From 5a95630f4c2dfa124285699066581e0868afd181 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Sat, 14 Mar 2020 17:30:22 -0400 Subject: [PATCH 01/10] the great beach gateway revamp --- _maps/map_files/RandomZLevels/beach.dmm | 33114 +++++++++------- code/game/area/Space Station 13 areas.dm | 5 + code/game/machinery/door_control.dm | 10 + code/game/machinery/doors/poddoor.dm | 10 + code/game/machinery/telecomms/presets.dm | 10 +- code/game/objects/effects/mapping_helpers.dm | 12 +- .../game/objects/effects/spawners/lootdrop.dm | 18 + .../items/stacks/sheets/sheet_types.dm | 14 + code/game/objects/structures/ladders.dm | 4 +- .../structures/stool_bed_chair_nest/bed.dm | 14 + .../structures/stool_bed_chair_nest/chairs.dm | 13 + code/game/turfs/simulated/floor/misc_floor.dm | 36 +- code/game/turfs/simulated/walls_misc.dm | 30 +- code/modules/awaymissions/corpse.dm | 41 +- .../simple_animal/hostile/deathsquid.dm | 39 +- .../simple_animal/hostile/mining/basilisk.dm | 20 + .../ruins/lavalandruin_code/dead_ratvar.dm | 63 +- icons/misc/beach.dmi | Bin 202418 -> 202899 bytes icons/obj/chairs.dmi | Bin 39969 -> 41977 bytes icons/obj/clockwork_objects.dmi | Bin 122481 -> 122401 bytes icons/obj/decals.dmi | Bin 48365 -> 48408 bytes icons/obj/doors/blastdoor_brass.dmi | Bin 0 -> 2406 bytes icons/obj/objects.dmi | Bin 114856 -> 116213 bytes 23 files changed, 18432 insertions(+), 15021 deletions(-) create mode 100644 icons/obj/doors/blastdoor_brass.dmi diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index eb5e1193f4ac..c4cbdbfb1ad0 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -40,7 +40,7 @@ /area/awaymission/undersea) "ak" = ( /obj/structure/cult/altar, -/obj/item/tome, +/obj/item/book/codex_gigas, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "al" = ( @@ -68,7 +68,13 @@ /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) "ar" = ( -/mob/living/simple_animal/hostile/retaliate/carp, +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "as" = ( @@ -81,7 +87,6 @@ /area/awaymission/undersea) "au" = ( /obj/structure/mineral_door/sandstone, -/obj/structure/barricade/wooden, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "av" = ( @@ -138,10 +143,6 @@ /obj/structure/toilet, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) -"aH" = ( -/obj/structure/grille, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) "aI" = ( /obj/structure/chair/wood/wings{ tag = "icon-wooden_chair_wings (EAST)"; @@ -158,14 +159,6 @@ }, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) -"aK" = ( -/obj/structure/chair/sofa/right, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aL" = ( -/obj/structure/chair/sofa/left, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) "aM" = ( /obj/structure/sink{ dir = 4; @@ -193,14 +186,6 @@ /obj/item/flag/species/skrell, /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) -"aQ" = ( -/obj/structure/mirror{ - icon_state = "mirror_broke"; - pixel_y = 28 - }, -/obj/item/storage/wallet/random, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) "aR" = ( /obj/structure/mirror{ icon_state = "mirror_broke"; @@ -224,10 +209,6 @@ /obj/structure/barricade/wooden, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) -"aW" = ( -/obj/structure/grille/broken, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) "aX" = ( /obj/item/stack/spacecash/c500, /turf/unsimulated/beach/water/deep/sand_floor, @@ -239,937 +220,1256 @@ }, /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) -"aZ" = ( -/obj/structure/closet/crate{ - icon_state = "crateopen"; - opened = 1 - }, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"bc" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) "bd" = ( -/obj/structure/flora/grass/brown, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"be" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 +/obj/structure/disposalpipe/segment{ + dir = 4 }, -/turf/unsimulated/beach/sand, +/turf/simulated/wall/mineral/sandstone, /area/awaymission/beach) "bf" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bg" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 +/obj/structure/sign/barsign{ + pixel_y = 32 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bh" = ( -/obj/structure/flora/ausbushes/leafybush, /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 + name = "rough sand" }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/wood, /area/awaymission/beach) +"bg" = ( +/obj/item/clockwork/alloy_shards/clockgolem_remains, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) "bi" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" +/obj/structure/closet/gmcloset{ + icon_closed = "black"; + icon_state = "black"; + name = "formal wardrobe" }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/wood, /area/awaymission/beach) "bj" = ( -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bk" = ( -/obj/effect/decal/snow/sand/surround{ - tag = "icon-gravsnow_surround (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_surround"; - dir = 1 - }, -/turf/unsimulated/beach/sand, +/obj/structure/closet/secure_closet/bar, +/turf/simulated/floor/wood, /area/awaymission/beach) "bl" = ( +/obj/structure/table/wood, +/obj/item/reagent_containers/food/drinks/shaker, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bw" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; + tag = "icon-gravsnow_corner (SOUTHWEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 4 + dir = 10 }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bm" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 +"bx" = ( +/obj/structure/clockwork/decorative/tinkerers_cache, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" }, -/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"by" = ( +/obj/machinery/cooker/foodgrill, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bz" = ( +/obj/structure/table/wood, +/obj/machinery/reagentgrinder, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bn" = ( +"bB" = ( +/mob/living/simple_animal/butterfly, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bD" = ( /obj/effect/decal/snow/sand/edge{ name = "rough sand" }, -/turf/unsimulated/floor/grass, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bF" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/soda, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bo" = ( +"bH" = ( +/obj/effect/overlay/palmtree_l, /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; + tag = "icon-gravsnow_corner (WEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 10 + dir = 8 }, -/turf/unsimulated/floor/grass, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"bJ" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bp" = ( -/obj/effect/decal/snow/sand/surround{ - name = "rough sand" - }, -/turf/unsimulated/floor/grass, +"bK" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/beer, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bq" = ( +"bL" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; + tag = "icon-gravsnow_corner (NORTH)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 6 + dir = 1 }, -/turf/unsimulated/floor/grass, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"bM" = ( +/obj/structure/chair/stool, +/turf/simulated/floor/wood, /area/awaymission/beach) -"br" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 +"bN" = ( +/obj/machinery/vending/cigarette{ + extended_inventory = 1 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bs" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 + name = "rough sand" }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bt" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bu" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 +/obj/structure/sign/poster/contraband/smoke{ + pixel_y = 32 }, -/turf/unsimulated/beach/sand/dense, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bv" = ( +"bO" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (NORTHEAST)"; name = "rough sand"; icon_state = "gravsnow_corner"; dir = 5 }, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"bw" = ( -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"bx" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"by" = ( +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"bT" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; + tag = "icon-gravsnow_corner (WEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 1 + dir = 8 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bz" = ( /obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"bA" = ( -/obj/structure/flora/ausbushes/genericbush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 +"bX" = ( +/obj/machinery/cooking/oven, +/obj/structure/disposalpipe/segment{ + dir = 4 }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bB" = ( +"cc" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/stalkybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"cg" = ( /obj/effect/overlay/palmtree_r, /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; + tag = "icon-gravsnow_corner (SOUTHWEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 1 + dir = 10 }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"ci" = ( +/obj/structure/flora/ausbushes/genericbush, +/obj/structure/flora/ausbushes/sparsegrass, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"cl" = ( +/obj/structure/closet/athletic_mixed, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bC" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 +"cH" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_y = -32 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bD" = ( +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"cJ" = ( /obj/effect/overlay/palmtree_r, /obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"bE" = ( -/obj/structure/flora/bush, -/turf/unsimulated/beach/sand, +"cL" = ( +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"cZ" = ( +/obj/structure/bonfire/dense, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"bF" = ( -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand, +"db" = ( +/obj/structure/chair/stool, +/obj/item/instrument/guitar, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"bG" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand/dense, +"dd" = ( +/obj/structure/dispenser/oxygen, +/obj/structure/sign/poster/official/air1{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bH" = ( -/obj/effect/overlay/palmtree_r, +"de" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; dir = 8 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bI" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"bJ" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 +/obj/structure/sign/poster/official/safety_internals{ + pixel_x = -32; + pixel_y = 0 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bK" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"bL" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, +"dg" = ( +/obj/structure/chair/beachchair, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"bM" = ( -/obj/structure/flora/ausbushes/sunnybush, +"dh" = ( +/obj/effect/overlay/palmtree_r, /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (NORTH)"; name = "rough sand"; icon_state = "gravsnow_corner"; dir = 1 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"bN" = ( -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"dj" = ( +/turf/simulated/floor/beach/roughcoastline/dense, /area/awaymission/beach) -"bO" = ( -/obj/structure/flora/grass/brown, -/turf/unsimulated/beach/sand, +"dk" = ( +/obj/structure/flora/rock, +/turf/simulated/floor/beach/roughcoastline/dense, /area/awaymission/beach) -"bP" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand, +"dr" = ( +/turf/unsimulated/beach/water, /area/awaymission/beach) -"bQ" = ( -/obj/structure/flora/ausbushes/grassybush, -/turf/unsimulated/beach/sand, +"ds" = ( +/turf/unsimulated/beach/water/dense, /area/awaymission/beach) -"bR" = ( -/obj/structure/flora/ausbushes/reedbush, -/turf/unsimulated/beach/sand, +"dt" = ( +/turf/unsimulated/beach/water/drop, /area/awaymission/beach) -"bS" = ( -/obj/structure/flora/grass/green, -/turf/unsimulated/beach/sand, +"du" = ( +/turf/unsimulated/beach/water/deep, /area/awaymission/beach) -"bT" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/palebush, -/turf/unsimulated/beach/sand/dense, +"dv" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy, +/turf/unsimulated/beach/water/deep, /area/awaymission/beach) -"bU" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/beach/sand/dense, +"dw" = ( +/turf/unsimulated/beach/water/deep/dense, /area/awaymission/beach) -"bV" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand/dense, +"dx" = ( +/turf/unsimulated/beach/water/drop/dense, /area/awaymission/beach) -"bW" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, +"dP" = ( +/mob/living/simple_animal/butterfly, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"em" = ( +/obj/structure/closet/crate, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/turf/simulated/floor/wood, /area/awaymission/beach) -"bX" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/sand/dense, +"eo" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 75 + }, +/turf/simulated/floor/beach/roughcoastline, /area/awaymission/beach) -"bY" = ( -/obj/structure/flora/ausbushes/palebush, -/turf/unsimulated/beach/sand/dense, +"er" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 104 + }, +/turf/simulated/floor/beach/roughcoastline, /area/awaymission/beach) -"bZ" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/lavendergrass, -/turf/unsimulated/beach/sand/dense, +"ew" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 44 + }, +/turf/simulated/floor/beach/roughcoastline, /area/awaymission/beach) -"ca" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand/dense, +"ey" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 97 + }, +/turf/simulated/floor/beach/roughcoastline, /area/awaymission/beach) -"cb" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand/dense, +"eB" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 94 + }, +/turf/simulated/floor/beach/roughcoastline, /area/awaymission/beach) -"cc" = ( +"eC" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"eM" = ( +/obj/item/shard, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"eN" = ( +/obj/structure/flora/grass/green, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"eS" = ( /obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/fernybush, +/obj/structure/flora/ausbushes/palebush, /turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"cd" = ( +/area/awaymission/beach/offshore) +"fa" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"fc" = ( +/obj/machinery/biogenerator, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"ce" = ( +"fd" = ( +/obj/structure/clockwork/decorative/relay, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"fj" = ( +/turf/unsimulated/wall{ + desc = "A sturdy wall of brass. It feels strangely warm and mechanical sounds can be heard from within."; + icon = 'icons/turf/walls/clockwork_wall.dmi'; + icon_state = "clockwork_wall"; + name = "clockwork wall" + }, +/area/awaymission/beach/offshore) +"fl" = ( +/obj/machinery/door_control/brass{ + id = "brass temple"; + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"fm" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"fA" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"fE" = ( +/obj/machinery/vending/syndisnack, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"fJ" = ( +/obj/structure/table/reinforced/brass, +/obj/item/clockwork/weapon/ratvarian_spear, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"fN" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"fR" = ( +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"fV" = ( /obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand, +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"gn" = ( +/obj/item/clockwork/component/geis_capacitor/fallen_armor, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"gp" = ( +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, /area/awaymission/beach) -"cf" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/fernybush, -/turf/unsimulated/beach/sand, +"gq" = ( +/obj/machinery/vending/crittercare, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"gt" = ( +/obj/structure/sink{ + icon_state = "sink"; + dir = 8; + pixel_x = -12; + pixel_y = 2 + }, +/obj/structure/sign/poster/official/cleanliness{ + pixel_x = -32 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"cg" = ( -/turf/unsimulated/beach/sand/dense, +"gw" = ( +/obj/machinery/disco{ + anchored = 1; + desc = "The first three prototypes were discontinued after mass casualty incidents. This one seems completely bolted to the floor."; + move_resist = 1e+031 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, /area/awaymission/beach) -"ch" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/sand, +"gA" = ( +/obj/structure/table/wood, +/obj/item/paper_bin, +/obj/item/pen/multi, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ci" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, +"gB" = ( +/obj/structure/barricade/wooden, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"gD" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/welding, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"gJ" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; + tag = "icon-gravsnow_corner (EAST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cj" = ( -/obj/machinery/gateway{ - dir = 9 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"ck" = ( -/obj/machinery/gateway{ - dir = 1 + dir = 4 }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cl" = ( -/obj/machinery/gateway{ - dir = 5 +"gL" = ( +/obj/structure/table/reinforced, +/obj/item/soap/syndie, +/turf/unsimulated/floor{ + icon_state = "white" }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cm" = ( +/area/awaymission/beach/offshore) +"gT" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/palebush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cn" = ( -/obj/machinery/gateway{ - dir = 8 +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"gW" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"hm" = ( +/obj/structure/rack, +/obj/item/stack/sheet/plasteel{ + amount = 20 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"co" = ( -/obj/machinery/gateway/centeraway, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cp" = ( -/obj/machinery/gateway{ - dir = 4 +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cq" = ( +/area/awaymission/undersea) +"hn" = ( +/obj/effect/baseturf_helper/beach/dense_roughsand, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"hp" = ( +/obj/item/clothing/gloves/botanic_leather, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"hv" = ( /obj/effect/decal/snow/sand/edge{ name = "rough sand" }, -/obj/structure/closet/crate/internals, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"hB" = ( +/obj/structure/closet, +/obj/item/clothing/under/pinkhawaiianshirt, +/obj/item/clothing/under/pinkhawaiianshirt, +/obj/item/clothing/under/orangehawaiianshirt, +/obj/item/clothing/under/orangehawaiianshirt, +/obj/item/clothing/under/redhawaiianshirt, +/obj/item/clothing/under/redhawaiianshirt, +/obj/item/clothing/under/bluehawaiianshirt, +/obj/item/clothing/under/bluehawaiianshirt, +/turf/simulated/floor/wood, /area/awaymission/beach) -"cr" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" +"hD" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 1 }, -/obj/structure/dispenser/oxygen, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cs" = ( -/obj/structure/flora/ausbushes/reedbush, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"hE" = ( +/obj/structure/rack, +/obj/item/storage/backpack/satchel_flat, +/turf/simulated/floor/plating, +/area/awaymission/beach/offshore) +"hF" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; + tag = "icon-gravsnow_corner (WEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 5 + dir = 8 }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"ct" = ( -/obj/effect/overlay/palmtree_r, /obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"cu" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"cv" = ( -/obj/structure/flora/bush, -/turf/unsimulated/beach/sand/dense, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cx" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/beach/sand/dense, +"hH" = ( +/obj/structure/table/wood, +/obj/item/storage/bag/plants, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"hI" = ( +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cy" = ( -/obj/machinery/gateway{ - dir = 10 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cz" = ( -/obj/machinery/gateway, +"hP" = ( +/obj/structure/flora/ausbushes/palebush, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cA" = ( -/obj/machinery/gateway{ - dir = 6 +/area/awaymission/beach/offshore) +"hR" = ( +/obj/structure/table/wood, +/obj/item/toy/figure/wizard, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"hT" = ( +/obj/item/stack/sheet/wood, +/obj/effect/decal/remains/human{ + plane = -1 }, -/turf/unsimulated/beach/sand, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"hY" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"id" = ( +/obj/machinery/vending/hydroseeds{ + extended_inventory = 1 + }, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"cB" = ( +"if" = ( +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"im" = ( +/obj/machinery/fishtank/tank, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"is" = ( +/obj/structure/table/wood, +/obj/item/clothing/suit/pirate_black, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"iu" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (EAST)"; name = "rough sand"; icon_state = "gravsnow_corner"; dir = 4 }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"ix" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"iC" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"iF" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/obj/item/melee/cultblade, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"iI" = ( /obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"iK" = ( +/obj/structure/chair/beachchair/red, +/obj/item/toy/plushie/octopus, +/obj/item/clothing/glasses/sunglasses{ + pixel_x = 2; + pixel_y = 6 + }, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cC" = ( -/obj/structure/flora/ausbushes/leafybush, +"iL" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"iT" = ( +/obj/machinery/photocopier, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"iU" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, /turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"iX" = ( +/obj/structure/table/wood, +/obj/item/flashlight, +/obj/item/stack/marker_beacon/thirty, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"cD" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone12"; - icon_state = "sandstone12" +"jd" = ( +/obj/item/clothing/under/color/brown, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ji" = ( +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"jr" = ( +/obj/effect/decal/cleanable/blood, +/obj/effect/decal/remains/human{ + plane = -1 }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"jA" = ( +/obj/machinery/vending/crittercare/free, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"cE" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone6"; - icon_state = "sandstone6" +"jD" = ( +/obj/structure/table/reinforced, +/obj/item/suppressor, +/obj/structure/sign/poster/contraband/syndicate_pistol{ + pixel_y = 32 }, -/area/awaymission/beach) -"cF" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone10"; - icon_state = "sandstone10" +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"jE" = ( +/obj/structure/table/wood, +/obj/item/reagent_containers/food/snacks/pie, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"jL" = ( +/turf/unsimulated/floor/lava, +/area/awaymission/beach/offshore) +"jN" = ( +/obj/machinery/gateway{ + dir = 9 }, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cG" = ( -/obj/structure/flora/ausbushes/ppflowers, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 +"jX" = ( +/obj/structure/sign/poster/official/love_ian{ + pixel_x = 32 }, -/turf/unsimulated/beach/sand, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"jY" = ( +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ka" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi, +/turf/unsimulated/beach/water, /area/awaymission/beach) -"cH" = ( +"kd" = ( +/obj/structure/chair/stool, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"kf" = ( +/obj/machinery/smartfridge, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"kg" = ( +/obj/item/clothing/shoes/black, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"km" = ( /turf/unsimulated/wall{ - tag = "icon-sandstone3"; - icon_state = "sandstone3" + icon_state = "rock"; + name = "rock" }, -/area/awaymission/beach) -"cI" = ( +/area/awaymission/beach/offshore) +"kp" = ( +/obj/structure/table/reinforced, +/obj/item/gun/projectile/automatic/pistol, +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"kq" = ( +/obj/structure/flora/grass/green, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"ky" = ( +/obj/structure/table/reinforced/brass, +/obj/item/storage/toolbox/brass/prefilled, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"kz" = ( +/obj/structure/closet/crate, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"kE" = ( /turf/unsimulated/wall{ - tag = "icon-sandstone1"; - icon_state = "sandstone1" + tag = "icon-sandstone0"; + icon_state = "sandstone0" }, +/area/awaymission/beach/offshore) +"kH" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"kO" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cJ" = ( +"kP" = ( +/obj/structure/grille/broken, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"kQ" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; + tag = "icon-gravsnow_corner (NORTHEAST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 4 + dir = 5 }, -/obj/structure/closet/athletic_mixed, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"kT" = ( +/obj/item/shovel/safety, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"kU" = ( +/obj/machinery/gateway/centeraway, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cK" = ( +"la" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; dir = 8 }, -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cL" = ( -/obj/structure/mineral_door/wood{ - tag = "icon-wood"; - icon_state = "wood" - }, -/turf/unsimulated/beach/sand, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"cN" = ( +"le" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; + tag = "icon-gravsnow_corner (NORTH)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 4 + dir = 1 }, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/structure/closet/crate, +/obj/structure/flora/ausbushes/sunnybush, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"lg" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/water, +/area/awaymission/beach/offshore) +"lo" = ( +/turf/simulated/floor/wood, /area/awaymission/beach) -"cO" = ( -/obj/structure/chair/stool, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" - }, -/area/awaymission/beach) -"cP" = ( -/turf/simulated/shuttle/wall{ - tag = "icon-swall8"; - icon_state = "swall8" - }, -/area/awaymission/beach) -"cQ" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f6"; - icon_state = "swall_f6" +"lC" = ( +/obj/structure/closet/secure_closet/freezer/kitchen{ + locked = 0; + req_access = null }, -/area/awaymission/beach) -"cR" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f10"; - icon_state = "swall_f10" +/obj/structure/sign/poster/official/high_class_martini{ + pixel_y = 32 }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"cS" = ( -/obj/structure/closet/gmcloset{ - icon_closed = "black"; - icon_state = "black"; - name = "formal wardrobe" +"lD" = ( +/obj/structure/sign/poster/contraband/lusty_xenomorph{ + pixel_y = 32 }, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/obj/structure/closet/cabinet, +/obj/item/clothing/suit/leathercoat, +/obj/item/clothing/head/fedora, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"lH" = ( +/obj/structure/shuttle/engine/propulsion{ + icon_state = "propulsion_l" }, -/area/awaymission/beach) -"cT" = ( -/obj/structure/closet/crate, -/obj/item/gun/energy/laser/retro, -/obj/item/gun/energy/laser/retro, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cU" = ( -/obj/structure/closet/secure_closet/bar, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"lJ" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"lK" = ( +/obj/structure/ladder/unbreakable/dive_point/anchor{ + id = "volcano_island" }, -/area/awaymission/beach) -"cV" = ( -/obj/structure/table/wood, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"lQ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 }, +/turf/simulated/wall/mineral/sandstone, /area/awaymission/beach) -"cW" = ( -/obj/structure/table/wood, -/obj/item/book/manual/barman_recipes, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +"lS" = ( +/turf/unsimulated/wall{ + icon = 'icons/turf/walls/abductor_wall.dmi'; + icon_state = "abductor" }, -/area/awaymission/beach) -"cX" = ( -/obj/structure/table/wood, -/obj/item/reagent_containers/food/drinks/shaker, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/area/awaymission/beach/offshore) +"lX" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 }, -/area/awaymission/beach) -"cY" = ( -/obj/structure/flora/rock/pile, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"cZ" = ( -/obj/machinery/vending/boozeomat{ - emagged = 1 +/area/awaymission/beach/offshore) +"lZ" = ( +/obj/item/instrument/bikehorn, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"mc" = ( +/obj/structure/ladder/unbreakable/dive_point/anchor{ + id = "brass temple" }, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ml" = ( +/obj/item/gun/projectile/automatic/pistol/enforcer, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" }, -/area/awaymission/beach) -"da" = ( +/area/awaymission/undersea) +"mp" = ( +/obj/machinery/sleeper, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"mw" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"mA" = ( /obj/structure/table/wood, -/obj/item/clothing/glasses/sunglasses, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/obj/structure/sign/poster/contraband/have_a_puff{ + pixel_x = -32 }, -/area/awaymission/beach) -"db" = ( -/obj/machinery/vending/cola, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"mF" = ( +/obj/structure/table/reinforced, /turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" + icon_state = "bar"; + dir = 2 }, +/area/awaymission/beach/offshore) +"mI" = ( +/obj/structure/rack, +/obj/item/stack/sheet/glass/fifty, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"mJ" = ( +/obj/item/harpoon, +/obj/item/harpoon, +/obj/item/harpoon, +/obj/structure/closet/crate, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"dc" = ( -/obj/structure/flora/ausbushes/palebush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dd" = ( -/mob/living/simple_animal/crab, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"de" = ( +"mP" = ( +/obj/effect/overlay/palmtree_l, /obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"df" = ( -/mob/living/simple_animal/crab/Coffee, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dg" = ( -/obj/structure/chair, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dh" = ( -/obj/structure/flora/ausbushes/reedbush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"di" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/stalkybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"dj" = ( -/turf/unsimulated/beach/coastline, -/area/awaymission/beach) -"dk" = ( -/turf/unsimulated/beach/coastline/dense, -/area/awaymission/beach) -"dl" = ( -/obj/machinery/vending/cigarette, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"mQ" = ( +/obj/effect/decal/cleanable/blood, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"mV" = ( +/obj/structure/clockwork/decorative/obelisk, /turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" }, +/area/awaymission/beach/offshore) +"mY" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"na" = ( +/obj/structure/closet/secure_closet/freezer/meat/open, +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dm" = ( -/obj/machinery/vending/snack, +"nb" = ( +/obj/structure/table/reinforced, +/obj/machinery/microwave, /turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" + tag = "icon-dark"; + icon_state = "dark" }, -/area/awaymission/beach) -"dn" = ( -/obj/structure/curtain/open, +/area/awaymission/beach/offshore) +"nc" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, /turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" + icon_state = "grimy" }, -/area/awaymission/beach) -"do" = ( +/area/awaymission/beach/offshore) +"nl" = ( +/obj/effect/overlay/palmtree_r, /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; + tag = "icon-gravsnow_corner (SOUTHWEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"no" = ( +/obj/structure/window/reinforced{ dir = 1 }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dp" = ( -/turf/simulated/shuttle/wall{ - tag = "icon-swall3"; - icon_state = "swall3" +/obj/structure/window/reinforced{ + dir = 4 }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dq" = ( -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +"nD" = ( +/obj/structure/table/wood, +/obj/item/id_decal/silver, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nE" = ( +/obj/item/clockwork/alloy_shards, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"nF" = ( +/mob/living/simple_animal/hostile/skeleton{ + harm_intent_damage = 10; + health = 100; + name = "The Captain" }, -/area/awaymission/beach) -"dr" = ( -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"ds" = ( -/turf/unsimulated/beach/water/dense, -/area/awaymission/beach) -"dt" = ( +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nJ" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 8 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"nL" = ( +/obj/structure/table/wood, +/obj/item/storage/box/PDAs, +/obj/structure/sign/poster/official/pda_ad{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nT" = ( /turf/unsimulated/beach/water/drop, -/area/awaymission/beach) -"du" = ( -/turf/unsimulated/beach/water/deep, -/area/awaymission/beach) -"dv" = ( -/obj/structure/ladder/unbreakable/dive_point/buoy, -/turf/unsimulated/beach/water/deep, -/area/awaymission/beach) -"dw" = ( -/turf/unsimulated/beach/water/deep/dense, -/area/awaymission/beach) -"dx" = ( -/turf/unsimulated/beach/water/drop/dense, -/area/awaymission/beach) -"dy" = ( -/turf/unsimulated/beach/water/edge_drop, -/area/awaymission/beach) -"dz" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 +/area/awaymission/beach/offshore) +"nU" = ( +/obj/effect/decal/remains/human{ + plane = -1 }, -/turf/unsimulated/beach/sand, +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"nV" = ( +/obj/structure/flora/ausbushes/grassybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"nY" = ( +/obj/item/storage/firstaid/adv, +/obj/structure/table/wood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nZ" = ( +/obj/structure/bed, +/obj/item/bedsheet/cult, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"oe" = ( +/obj/structure/table/wood, +/obj/machinery/microwave, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dA" = ( -/obj/structure/mineral_door/wood{ - tag = "icon-wood"; - icon_state = "wood" +"og" = ( +/turf/unsimulated/beach/water/deep/rock_wall{ + icon = 'icons/turf/walls/shuttle_wall.dmi'; + icon_state = "map-shuttle"; + name = "Shuttle Wall" }, -/turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" +/area/awaymission/undersea) +"oi" = ( +/obj/structure/table/reinforced, +/obj/item/paper{ + info = "Man NT falls for the simplest shit sometimes. All you need is an official looking paper with the right stamp and bam you've got redirrected shipments coming your way for pirating and agents with access they wouldn't have gotten otherwise."; + name = "Outpose commander's note" }, -/area/awaymission/beach) -"dB" = ( -/obj/structure/flora/grass/green, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"dC" = ( -/obj/structure/table/wood, -/obj/machinery/chem_dispenser/soda, /turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" + tag = "icon-dark"; + icon_state = "dark" }, -/area/awaymission/beach) -"dD" = ( +/area/awaymission/beach/offshore) +"ok" = ( /turf/unsimulated/wall{ - tag = "icon-sandstone0"; - icon_state = "sandstone0" + icon = 'icons/turf/mining.dmi'; + icon_state = "rock" + }, +/area/awaymission/beach/offshore) +"op" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy{ + id = "pirate" + }, +/turf/unsimulated/beach/water, +/area/awaymission/beach/offshore) +"ov" = ( +/obj/structure/rack, +/obj/item/stack/sheet/mineral/sandstone{ + amount = 20 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"oy" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"oz" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"oB" = ( +/obj/structure/sink{ + pixel_y = 24 }, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"dE" = ( +"oC" = ( +/obj/structure/flora/ausbushes/reedbush, /obj/effect/decal/snow/sand/edge{ - name = "rough sand" + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 }, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dF" = ( -/obj/structure/table/wood, -/obj/machinery/chem_dispenser/beer, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"oE" = ( +/obj/structure/table/reinforced, +/obj/item/storage/toolbox/syndicate, /turf/unsimulated/floor{ - tag = "icon-wood"; - icon_state = "wood" + icon_state = "vault"; + dir = 8 }, -/area/awaymission/beach) -"dG" = ( -/obj/structure/flora/grass/green, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"dH" = ( +/area/awaymission/beach/offshore) +"oG" = ( +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"oH" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; + tag = "icon-gravsnow_corner (SOUTHEAST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 1 + dir = 6 }, -/obj/structure/chair/stool/bar, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dI" = ( -/obj/structure/table/wood, -/obj/item/clothing/glasses/eyepatch, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dJ" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/bandana, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"oL" = ( +/obj/machinery/gateway, +/turf/simulated/floor/carpet, /area/awaymission/beach) -"dK" = ( -/obj/item/ship_in_a_bottle, -/obj/structure/table/wood, -/turf/unsimulated/beach/sand, +"oN" = ( +/obj/machinery/fishtank/wall, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"dL" = ( +"oP" = ( +/obj/structure/cult/altar, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"oU" = ( +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"oY" = ( +/obj/item/clothing/shoes/sandal, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"pb" = ( +/obj/structure/table/reinforced, +/obj/item/flashlight/seclite, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"pd" = ( /mob/living/simple_animal/hostile/pirate/ranged{ loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) }, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dM" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/collectable/pirate, -/obj/item/stack/sheet/mineral/diamond{ - amount = 2 +/area/awaymission/beach/offshore) +"pe" = ( +/obj/structure/closet/radiation, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" }, -/turf/unsimulated/beach/sand, +/area/awaymission/undersea) +"ph" = ( +/obj/structure/toilet/secret, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dN" = ( -/obj/structure/closet/crate, -/obj/item/stack/sheet/mineral/uranium{ - amount = 20 +"pp" = ( +/obj/structure/table/wood, +/obj/effect/decal/cleanable/blood, +/obj/item/paper{ + info = "It was tricky shooting down this dodgy ship but this fancy metal will be worth a shit tone of credits on the black market. Damn tough to take apart though so I'm thinking we set up shop here and maybe make this into a hideout once we're done."; + name = "Captain's note" }, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dO" = ( -/obj/structure/chair/office/light, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dP" = ( -/obj/effect/overlay/palmtree_r, +/area/awaymission/beach/offshore) +"pu" = ( +/mob/living/simple_animal/parrot, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"pw" = ( /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; + tag = "icon-gravsnow_corner (WEST)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 6 + dir = 8 }, -/turf/unsimulated/beach/sand, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dQ" = ( -/obj/structure/chair/office/light, -/mob/living/simple_animal/hostile/pirate/ranged{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) +"pz" = ( +/obj/machinery/door/airlock/hatch/syndicate, +/turf/unsimulated/floor{ + icon_state = "plastitanium" }, -/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"pD" = ( +/obj/machinery/door/unpowered/shuttle, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"pO" = ( +/obj/machinery/sleeper, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"dR" = ( -/obj/machinery/light{ - icon_state = "tube1"; - dir = 8 +"pP" = ( +/obj/structure/table/wood, +/obj/item/pizzabox/hawaiian, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"qb" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"qf" = ( +/obj/machinery/door/airlock{ + name = "Private Restroom" }, -/turf/simulated/shuttle/floor, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dS" = ( +"qj" = ( /obj/machinery/light{ dir = 4; icon_state = "tube1" @@ -1178,1271 +1478,3076 @@ dir = 8 }, /turf/simulated/shuttle/floor, -/area/awaymission/beach) -"dT" = ( +/area/awaymission/beach/offshore) +"ql" = ( +/obj/structure/chair/stool, +/mob/living/simple_animal/hostile/syndicate/melee/autogib, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"qo" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"qx" = ( /obj/effect/overlay/palmtree_l, /obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; + tag = "icon-gravsnow_corner (NORTH)"; name = "rough sand"; icon_state = "gravsnow_corner"; - dir = 8 + dir = 1 }, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dU" = ( -/obj/structure/grille, -/obj/structure/shuttle/window, -/turf/simulated/floor/plating/airless, -/area/awaymission/beach) -"dV" = ( -/obj/structure/flora/rock, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach) -"dW" = ( -/obj/structure/ladder/unbreakable/dive_point/buoy{ - id = "pirate" +/area/awaymission/beach/offshore) +"qK" = ( +/turf/unsimulated/wall/fakeglass{ + dir = 4; + icon_state = "fakewindows"; + opacity = 0 }, -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"dX" = ( -/obj/item/paper, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"dY" = ( +/area/awaymission/beach/offshore) +"qP" = ( +/obj/structure/flora/grass/brown, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"qR" = ( +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"qT" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"qU" = ( +/obj/structure/flora/ausbushes/fernybush, /turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f5"; - icon_state = "swall_f5" +/area/awaymission/beach/offshore) +"qX" = ( +/obj/structure/spawner/nether{ + max_mobs = 3; + name = "weak netherworld link" }, +/obj/effect/rune, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"qY" = ( +/obj/structure/table/wood, +/obj/item/camera, +/turf/simulated/floor/wood, /area/awaymission/beach) -"dZ" = ( -/obj/structure/chair/comfy/shuttle{ +"rc" = ( +/obj/structure/table/wood, +/obj/item/lighter/zippo/gonzofist, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"rg" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/genericbush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"rk" = ( +/obj/structure/closet/cabinet, +/obj/item/clothing/shoes/black, +/obj/item/clothing/under/color/lightgreen, +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"rt" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"rv" = ( +/obj/effect/spawner/lootdrop/brass_temple_spawner, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"rx" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"rA" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"rD" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; dir = 4 }, -/turf/simulated/shuttle/floor, +/mob/living/simple_animal/butterfly, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"rF" = ( +/mob/living/simple_animal/crab, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"rG" = ( +/obj/structure/sign/electricshock{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"rH" = ( +/obj/machinery/juicer, +/obj/structure/table/wood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"rS" = ( +/obj/structure/flora/ausbushes/ywflowers, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"rT" = ( +/obj/structure/table/wood, +/obj/item/hatchet, +/obj/item/cultivator, +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"ea" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f9"; - icon_state = "swall_f9" +"rV" = ( +/obj/structure/sign/poster/official/pda_ad{ + pixel_x = 32 }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"eb" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 8 +"rX" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"sa" = ( +/obj/machinery/gateway{ + dir = 6 }, -/turf/simulated/shuttle/floor, +/turf/simulated/floor/carpet, /area/awaymission/beach) -"ec" = ( -/obj/machinery/computer, -/turf/simulated/shuttle/floor, +"sg" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"sj" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"sk" = ( +/turf/simulated/floor/light/colour_cycle/dancefloor_a, /area/awaymission/beach) -"ed" = ( +"sp" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/bandana, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"sv" = ( +/obj/item/clockwork/alloy_shards/large, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"sx" = ( +/obj/structure/table/reinforced, +/obj/item/storage/firstaid/adv, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"sz" = ( +/obj/structure/sign/poster/contraband/hacking_guide{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"sF" = ( +/obj/structure/cult/altar, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"sN" = ( /obj/structure/closet/crate, /obj/item/clothing/under/pirate, /obj/item/clothing/under/pirate, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"sO" = ( +/obj/structure/rack, +/obj/item/stack/sheet/mineral/plasma{ + amount = 30 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"sV" = ( +/obj/machinery/door/airlock/public/glass{ + name = "Resort Bar" + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ee" = ( -/obj/structure/closet/crate, -/obj/item/clothing/suit/pirate_brown, -/turf/unsimulated/beach/sand, -/area/awaymission/beach) -"ef" = ( +"sZ" = ( /obj/effect/overlay/palmtree_r, -/obj/structure/flora/ausbushes/sunnybush, +/obj/effect/overlay/coconut, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"eg" = ( -/mob/living/simple_animal/hostile/pirate{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate) +/area/awaymission/beach/offshore) +"tc" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"te" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach) -"eh" = ( -/turf/simulated/shuttle/floor, -/area/awaymission/beach) -"ei" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 1 +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"ti" = ( +/turf/simulated/shuttle/wall{ + tag = "icon-swall8"; + icon_state = "swall8" }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach) -"ej" = ( +/area/awaymission/beach/offshore) +"to" = ( +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"tp" = ( +/obj/structure/table/reinforced, +/obj/item/defibrillator/loaded, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"tr" = ( +/obj/structure/shuttle/engine/propulsion, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"tF" = ( /obj/structure/table/wood, -/obj/item/clothing/suit/pirate_black, +/obj/item/reagent_containers/glass/beaker/large, +/obj/item/reagent_containers/glass/beaker/large, +/obj/structure/sign/poster/contraband/eat{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"tM" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"tN" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"tV" = ( +/turf/unsimulated/wall, +/area/awaymission/beach/offshore) +"uc" = ( +/obj/structure/mecha_wreckage/durand, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ue" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/vending/dinnerware, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ek" = ( +"un" = ( +/obj/structure/table/reinforced, +/obj/item/reagent_containers/food/snacks/syndicake, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"us" = ( +/obj/structure/sink{ + pixel_y = 24 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"uz" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"uE" = ( /obj/effect/decal/snow/sand/edge{ name = "rough sand" }, /obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"uN" = ( +/obj/structure/table/wood, +/obj/item/storage/toolbox/mechanical, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"uR" = ( +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"ve" = ( +/obj/structure/sink{ + dir = 4; + icon_state = "sink"; + pixel_x = 11; + pixel_y = 0 + }, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"vh" = ( +/mob/living/simple_animal/hostile/syndicate/ranged{ + loot = list() + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"vk" = ( +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"vm" = ( +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/wood, /area/awaymission/beach) -"el" = ( -/obj/machinery/door/unpowered/shuttle, -/turf/simulated/shuttle/floor, -/area/awaymission/beach) -"em" = ( -/obj/item/beach_ball, +"vn" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"en" = ( -/mob/living/simple_animal/hostile/pirate{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate) +/area/awaymission/beach/offshore) +"vq" = ( +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/structure/closet/crate, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"vr" = ( +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"vA" = ( +/obj/item/hatchet, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"vM" = ( +/obj/structure/rack/skeletal_bar/left, +/obj/machinery/chem_dispenser/beer, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"vN" = ( +/obj/item/stack/sheet/mineral/bananium, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"vO" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/collectable/pirate, +/obj/item/stack/sheet/mineral/diamond{ + amount = 2 }, /turf/unsimulated/beach/sand, -/area/awaymission/beach) -"eo" = ( -/obj/effect/waterfall{ +/area/awaymission/beach/offshore) +"vR" = ( +/obj/effect/decal/cleanable/blood, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"vY" = ( +/obj/structure/shuttle/engine/propulsion{ + icon_state = "propulsion_r" + }, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"vZ" = ( +/obj/structure/disposalpipe/junction{ dir = 1; - water_frequency = 75 + icon_state = "pipe-j1"; + tag = "icon-pipe-j1 (EAST)" }, -/turf/unsimulated/beach/coastline, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ep" = ( -/obj/effect/decal/cleanable/blood, -/turf/unsimulated/beach/sand, +"wb" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"wc" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"eq" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 192 +"wd" = ( +/obj/machinery/vending/hydronutrients{ + extended_inventory = 1 }, -/turf/unsimulated/beach/coastline, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"er" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 104 +"wf" = ( +/obj/structure/sign/poster/official/space_cops{ + pixel_y = 32 }, -/turf/unsimulated/beach/coastline, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"wi" = ( +/obj/item/toy/pet_rock/roxie, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"es" = ( -/obj/structure/chair/stool, +"wl" = ( +/obj/item/clothing/under/pinkhawaiianshirt, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"wr" = ( +/obj/structure/closet/crate/can, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"wu" = ( +/obj/structure/ladder/unbreakable{ + desc = "An extremely sturdy metal ladder. What's it doing out here?"; + icon_state = "ladder01"; + id = "volcanobaseladder"; + name = "mysterious ladder" + }, +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"ww" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"wx" = ( +/obj/structure/bed, +/obj/item/bedsheet/black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"wJ" = ( +/obj/effect/overlay/palmtree_l, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"wO" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"wP" = ( +/obj/item/clothing/under/librarian, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"wQ" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/lavendergrass, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"wR" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"wS" = ( +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"wW" = ( +/obj/structure/table/wood, +/obj/item/laser_pointer, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xb" = ( +/obj/item/stack/sheet/wood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xe" = ( +/obj/structure/bed, +/obj/item/bedsheet/syndie, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"xm" = ( +/obj/effect/decal/cleanable/blood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xv" = ( +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"xx" = ( +/obj/structure/flora/rock/pile, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"xD" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"xG" = ( +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"et" = ( -/obj/structure/shuttle/engine/heater, -/turf/simulated/floor/plating/airless, +"xH" = ( +/obj/structure/sign/restroom{ + pixel_x = -32 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"eu" = ( -/obj/structure/table/wood, +"xI" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"xN" = ( +/mob/living/simple_animal/crab/evil{ + faction = list("nether") + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xQ" = ( +/obj/structure/barricade/wooden, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"xR" = ( +/obj/structure/table/abductor, +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"xT" = ( +/obj/machinery/seed_extractor, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xX" = ( +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/obj/structure/rack, +/obj/item/toy/katana, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xZ" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/fernybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"ym" = ( +/obj/structure/flora/ausbushes/genericbush, +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"yq" = ( +/obj/structure/flora/bush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"yr" = ( +/obj/item/clothing/shoes/brown, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"yz" = ( +/obj/item/flag/syndi, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"yB" = ( +/obj/effect/baseturf_helper/beach/roughsand, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"yD" = ( +/obj/structure/closet/crate, +/obj/item/gun/energy/laser/retro, +/obj/item/gun/energy/laser/retro, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"yF" = ( +/obj/structure/rack, +/obj/item/stack/marker_beacon/ten, +/obj/item/stack/marker_beacon/ten, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ev" = ( +"yG" = ( +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"yH" = ( /obj/structure/table/wood, -/obj/item/reagent_containers/food/snacks/pie, +/obj/item/storage/box/monkeycubes/neaeracubes, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"yJ" = ( +/obj/structure/flora/grass/brown, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"yS" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ew" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 44 +"yT" = ( +/obj/structure/reagent_dispensers/watertank/high, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"yU" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/water/dense, +/area/awaymission/beach) +"yY" = ( +/obj/structure/closet/crate, +/obj/item/coin/antagtoken/syndicate, +/obj/item/clothing/mask/fawkes, +/turf/unsimulated/floor{ + icon_state = "grimy" }, -/turf/unsimulated/beach/coastline, +/area/awaymission/beach/offshore) +"zg" = ( +/obj/structure/window/reinforced, +/turf/simulated/floor/wood, /area/awaymission/beach) -"ex" = ( -/obj/structure/shuttle/engine/propulsion{ - icon_state = "propulsion_l" +"zh" = ( +/obj/structure/sign/greencross{ + pixel_y = 32 }, -/turf/simulated/floor/plating/airless, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"zi" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"zk" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"zn" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"zr" = ( +/obj/structure/flora/bush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"zw" = ( +/obj/structure/flora/grass/green, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"zx" = ( +/obj/machinery/conveyor_switch/oneway{ + id = "beach disposal" + }, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"ey" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 97 +"zB" = ( +/obj/item/clothing/under/rank/nursesuit, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"zC" = ( +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 }, -/turf/unsimulated/beach/coastline, +/area/awaymission/beach/offshore) +"zL" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"zQ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/structure/table/wood, +/obj/structure/reagent_dispensers/beerkeg, +/turf/simulated/floor/plasteel, /area/awaymission/beach) -"ez" = ( -/obj/structure/shuttle/engine/propulsion{ - icon_state = "propulsion_r" +"zR" = ( +/obj/machinery/door/airlock/hatch/syndicate{ + desc = "There's a note on the door. It says 'All intruders WILL BE SHOT!'." }, -/turf/simulated/floor/plating/airless, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"zX" = ( +/obj/effect/baseturf_helper/beach/roughsand, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"zZ" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Aa" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Am" = ( +/obj/structure/closet/crate/secure/loot, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Ao" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/obj/machinery/vending/cola, +/turf/simulated/floor/wood, /area/awaymission/beach) -"eA" = ( -/obj/structure/shuttle/engine/propulsion, -/turf/simulated/floor/plating/airless, +"Ap" = ( +/obj/item/clothing/under/color/lightgreen, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"AD" = ( +/obj/machinery/vending/boozeomat{ + emagged = 1 + }, +/obj/structure/sign/poster/official/nanomichi_ad{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"eB" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 94 +"AI" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"AM" = ( +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach/offshore) +"AP" = ( +/obj/structure/mecha_wreckage/ripley, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" }, -/turf/unsimulated/beach/coastline, +/area/awaymission/undersea) +"AQ" = ( +/obj/structure/sign/greencross{ + pixel_y = -32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"AS" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"AW" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"eC" = ( -/obj/structure/table/wood, -/obj/effect/decal/cleanable/blood, +"AX" = ( +/obj/structure/curtain/open, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Bc" = ( +/obj/structure/bed/wooden_lounge_chair, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Bi" = ( +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Bm" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Bn" = ( +/obj/structure/closet/crate, +/obj/item/stack/sheet/mineral/abductor{ + amount = 25 + }, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Bq" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"eD" = ( -/obj/structure/closet/crate/can, +"Bt" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi/honk, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"Bv" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"By" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Bz" = ( +/obj/structure/chair/sofa/left{ + dir = 4 + }, +/turf/simulated/floor/wood, /area/awaymission/beach) -"eE" = ( -/obj/structure/closet/crate, +"BA" = ( +/obj/structure/chair/office/light, +/mob/living/simple_animal/hostile/pirate/ranged{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) + }, /turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"BF" = ( +/obj/structure/closet/crate/internals, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/turf/simulated/floor/wood, /area/awaymission/beach) -"LZ" = ( -/obj/machinery/poolcontroller/invisible, -/turf/unsimulated/beach/water/deep, +"BH" = ( +/obj/item/clothing/under/color/grey, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"BJ" = ( +/obj/machinery/gateway{ + dir = 10 + }, +/turf/simulated/floor/carpet, /area/awaymission/beach) - -(1,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -bw -cu -dh -cg -bw -cg -bw -by -bS -bj -bF -dz -dB -dh -cg -bB -bj -bF -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dy -du -du -du -du -du -"} -(2,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bw -bx -bH -bg -bH -bg -bH -cG -bj -bz -bR -bj -bz -bg -bg -dT -bD -bj -bE -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -du -du -du -du -du -"} -(3,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bX -cg -bJ -bj -bD -bj -bz -bz -bj -bj -bD -bj -bN -bQ -bO -bz -bz -bj -bD -bF -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -du -du -du -du -du -"} -(4,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bd -bm -bj -bD -bj -bK -bD -bj -bz -bQ -bj -bz -bN -bF -bj -bK -bj -bz -bj -bz -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -du -du -du -du -du -"} -(5,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +"BM" = ( +/obj/structure/flora/grass/brown, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"BN" = ( +/obj/structure/table/reinforced, +/obj/item/storage/box/donkpockets, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"BR" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"BT" = ( +/obj/structure/closet/crate, +/obj/item/stack/sheet/mineral/gold{ + amount = 10 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"BU" = ( +/obj/structure/flora/rock/pile, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"BY" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Cf" = ( +/obj/structure/closet/abductor, +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"Ch" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ci" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/obj/item/clothing/under/color/purple, +/obj/item/clothing/shoes/black, +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Cj" = ( +/obj/structure/table/wood, +/obj/item/melee/chainofcommand, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Cm" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Co" = ( +/obj/structure/cult/pylon, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"Cr" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Cv" = ( +/obj/structure/shuttle/engine/heater, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"Cw" = ( +/turf/unsimulated/beach/coastline{ + dir = 1; + icon_state = "beachcorner" + }, +/area/awaymission/beach/offshore) +"Cz" = ( +/mob/living/simple_animal/hostile/skeleton, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"CA" = ( +/obj/item/clothing/shoes/black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"CB" = ( +/obj/structure/closet/secure_closet/freezer/fridge/open, +/obj/structure/sign/botany{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"CC" = ( +/obj/machinery/gateway{ + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"CE" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/machinery/vending/snack{ + extended_inventory = 1 + }, +/obj/structure/sign/poster/contraband/donut_corp{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"CF" = ( +/obj/machinery/door/airlock/multi_tile/glass{ + name = "Dance Club" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"CG" = ( +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"CJ" = ( +/obj/item/beach_ball, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"CM" = ( +/turf/unsimulated/beach/water, +/area/awaymission/beach/offshore) +"Db" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/fernybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Dd" = ( +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Dm" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/machinery/vending/coffee, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Ds" = ( +/obj/structure/mineral_door/wood{ + icon_state = "wood"; + name = "Captain's Quarters"; + tag = "icon-wood" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"DD" = ( +/obj/structure/table/wood, +/obj/item/soap/deluxe, +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"DG" = ( +/obj/item/clockwork/alloy_shards/pinion_lock, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"DP" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"DR" = ( +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"DV" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Ed" = ( +/obj/item/shard, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Ej" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Ep" = ( +/obj/structure/sign/poster/official/safety_report{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Eq" = ( +/obj/machinery/recycler, +/obj/machinery/conveyor/east{ + id = "beach disposal" + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Et" = ( +/obj/structure/table/reinforced, +/obj/item/storage/box/syndie_kit/safecracking, +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"Eu" = ( +/obj/structure/table/wood, +/obj/item/storage/box/characters, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ez" = ( +/obj/structure/table/wood, +/obj/item/storage/firstaid/o2, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"EB" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"EE" = ( +/obj/structure/table/wood, +/obj/item/storage/bag/trash, +/obj/item/storage/bag/trash, +/obj/item/storage/bag/trash, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"EH" = ( +/obj/item/clothing/shoes/winterboots, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"EM" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"ER" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"ET" = ( +/obj/structure/mineral_door/wood{ + icon_state = "wood"; + name = "Scuba Shack"; + tag = "icon-wood" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"EX" = ( +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"EZ" = ( +/obj/structure/flora/grass/green, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Fa" = ( +/obj/item/chair/wood/wings, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Fb" = ( +/obj/structure/chair/stool, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Fd" = ( +/obj/machinery/door/poddoor/brass{ + id_tag = "brass temple" + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Fi" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Fl" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Fp" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Fs" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Fv" = ( +/obj/machinery/door/airlock{ + name = "Private Recharge room" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Fx" = ( +/obj/structure/table/wood, +/obj/item/toy/figure/ninja, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Fy" = ( +/obj/machinery/vending/cigarette{ + extended_inventory = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"FB" = ( +/obj/structure/flora/rock, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"FF" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"FM" = ( +/obj/machinery/vending/hydroseeds{ + extended_inventory = 1 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"FN" = ( +/obj/structure/chair/comfy/shuttle, +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"FT" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Gd" = ( +/obj/structure/chair/sofa{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Gj" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Gm" = ( +/turf/simulated/floor/light/colour_cycle/dancefloor_b, +/area/awaymission/beach) +"Go" = ( +/obj/machinery/power/port_gen/pacman, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Gu" = ( +/obj/machinery/gateway{ + dir = 5 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Gz" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 192 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"GK" = ( +/obj/structure/table/wood, +/obj/item/sensor_device, +/obj/item/pinpointer/crew, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"GR" = ( +/obj/machinery/recharge_station, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"GW" = ( +/obj/machinery/biogenerator, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"GZ" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"Hb" = ( +/obj/vehicle/ambulance, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Hf" = ( +/obj/structure/flora/grass/green, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Hm" = ( +/obj/structure/table/reinforced, +/obj/item/stamp/chameleon, +/obj/structure/sign/nuke{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"Hp" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Hr" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Hy" = ( +/obj/item/clothing/shoes/white, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"HC" = ( +/obj/structure/table/wood, +/obj/item/reagent_containers/spray/plantbgone, +/obj/item/reagent_containers/spray/pestspray, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"HI" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"HJ" = ( +/obj/structure/cult/pylon, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"HN" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone10"; + icon_state = "sandstone10" + }, +/area/awaymission/beach/offshore) +"HQ" = ( +/obj/machinery/gateway{ + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"HY" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f6"; + icon_state = "swall_f6" + }, +/area/awaymission/beach/offshore) +"Ia" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Id" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ih" = ( +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Il" = ( +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"Im" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"In" = ( +/obj/structure/fermenting_barrel, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Io" = ( +/obj/item/clothing/under/color/lightred, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Ip" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"It" = ( +/mob/living/simple_animal/hostile/syndicate/melee/autogib, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"Iu" = ( +/obj/effect/decal/snow/sand/surround{ + tag = "icon-gravsnow_surround (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_surround"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Iw" = ( +/mob/living/simple_animal/crab/Coffee, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Ix" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"IA" = ( +/obj/machinery/hydroponics/constructable, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"ID" = ( +/mob/living/simple_animal/crab, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"IG" = ( +/obj/structure/closet, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/gloves/color/black, +/obj/item/clothing/gloves/color/black, +/obj/item/clothing/gloves/color/black, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"IN" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"IO" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"IZ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Ja" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/closet/crate/can, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Jb" = ( +/obj/structure/disposaloutlet{ + dir = 4 + }, +/obj/structure/disposalpipe/trunk, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Jc" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_x = 32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Jd" = ( +/mob/living/simple_animal/hostile/pirate{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate) + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Jf" = ( +/obj/machinery/door/airlock/hatch/syndicate, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"Jj" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone12"; + icon_state = "sandstone12" + }, +/area/awaymission/beach/offshore) +"Jl" = ( +/obj/structure/table/wood, +/obj/item/clothing/glasses/sunglasses, +/obj/machinery/vending/wallmed{ + extended_inventory = 1; + pixel_x = 0; + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Jr" = ( +/obj/structure/ladder/unbreakable{ + height = 1; + icon_state = "ladder10"; + id = "volcanobaseladder" + }, +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"Jv" = ( +/obj/structure/closet/crate/can, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"JB" = ( +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/structure/closet/crate, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/structure/sign/poster/contraband/d_day_promo{ + pixel_x = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"JE" = ( +/obj/structure/chair/stool, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"JF" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"JH" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/beret, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"JJ" = ( +/obj/item/toy/pet_rock/fred, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"JK" = ( +/obj/machinery/door/airlock/hatch/syndicate, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"JM" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"JQ" = ( +/obj/structure/sign/poster/contraband/missing_gloves{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"JZ" = ( +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Ka" = ( +/obj/structure/flora/ausbushes/ppflowers, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Kb" = ( +/obj/machinery/door/poddoor/brass/beach_brass_temple, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Ke" = ( +/obj/structure/table/reinforced, +/obj/item/storage/fancy/cigarettes/cigpack_syndicate, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Kj" = ( +/obj/effect/spawner/window/reinforced, +/turf/simulated/floor, +/area/awaymission/beach) +"Kk" = ( +/obj/machinery/conveyor/east{ + id = "beach disposal" + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Kr" = ( +/obj/structure/closet/toolcloset, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Ks" = ( +/obj/structure/table/wood, +/obj/item/gun/energy/gun/mini, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Kt" = ( +/obj/structure/flora/ausbushes/ppflowers, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"KL" = ( +/obj/item/clothing/under/rank/chaplain, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"KM" = ( +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "ramptop"; + tag = "icon-stage_stairs" + }, +/area/awaymission/beach) +"KS" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"KU" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, +/obj/structure/sign/poster/contraband/energy_swords{ + pixel_y = -32 + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Lj" = ( +/obj/structure/rack, +/obj/item/pickaxe/drill, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Lk" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Lp" = ( +/obj/structure/chair{ + dir = 1 + }, +/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"Lq" = ( +/obj/structure/flora/grass/brown, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Lu" = ( +/obj/structure/table/reinforced/brass, +/obj/item/stack/tile/brass/fifty, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Lv" = ( +/obj/item/shard, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Lz" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"LB" = ( +/obj/item/stack/sheet/wood, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"LE" = ( +/obj/structure/rack, +/obj/item/melee/classic_baton, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"LJ" = ( +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"LK" = ( +/obj/structure/flora/grass/green, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"LL" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone6"; + icon_state = "sandstone6" + }, +/area/awaymission/beach/offshore) +"LM" = ( +/obj/item/shovel, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"LV" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"LY" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"LZ" = ( +/obj/machinery/poolcontroller/invisible, +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach) +"Mb" = ( +/turf/unsimulated/beach/water/deep/rock_wall{ + icon = 'icons/turf/walls/shuttle_wall.dmi'; + icon_state = "map-shuttle"; + name = "Shuttle Wall" + }, +/area/shuttle/transport) +"Mq" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"Mw" = ( +/obj/structure/dresser, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Mx" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"My" = ( +/obj/effect/decal/snow/sand/surround{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"MB" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"MG" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"ML" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"MO" = ( +/obj/item/clothing/shoes/jackboots, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"MY" = ( +/obj/structure/table/wood, +/obj/item/clothing/accessory/armband, +/obj/item/clothing/accessory/red, +/obj/structure/sign/poster/official/the_owl{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Nb" = ( +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Ne" = ( +/obj/structure/flora/ausbushes/fernybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Nn" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Nq" = ( +/obj/structure/closet/crate/internals, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/structure/sign/poster/official/safety_internals{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Nr" = ( +/turf/unsimulated/beach/coastline, +/area/awaymission/beach/offshore) +"Ns" = ( +/mob/living/simple_animal/hostile/pirate{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate) + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"NE" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/helmet/justice, +/obj/structure/sign/poster/contraband/fun_police{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"NF" = ( +/obj/structure/chair/stool, +/obj/item/instrument/piano_synth, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"NG" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"NJ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"NP" = ( +/obj/structure/flora/rock, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"NQ" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, +/obj/structure/sign/poster/contraband/lusty_xenomorph{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"NR" = ( +/turf/unsimulated/wall/fakeglass{ + dir = 10; + icon_state = "fakewindows" + }, +/area/awaymission/beach/offshore) +"NT" = ( +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"NV" = ( +/obj/structure/shuttle/engine/heater{ + dir = 8 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"NZ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ob" = ( +/obj/structure/flora/rock, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Oc" = ( +/obj/structure/table/reinforced, +/obj/item/paper_bin, +/obj/structure/sign/poster/contraband/c20r{ + pixel_y = 32 + }, +/obj/item/pen/multi, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"Oi" = ( +/obj/structure/flora/rock, +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Ol" = ( +/obj/structure/chair/sofa/right{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Op" = ( +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Or" = ( +/obj/structure/table/wood, +/obj/item/cultivator, +/obj/item/seeds/cotton, +/obj/item/seeds/cotton, +/obj/item/seeds/cotton, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ou" = ( +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Oz" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"OD" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"OF" = ( +/obj/structure/flora/ausbushes/ppflowers, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"OG" = ( +/obj/structure/closet, +/obj/item/clothing/shoes/chameleon/noslip, +/obj/item/clothing/under/chameleon, +/obj/item/clothing/mask/chameleon, +/turf/simulated/floor/plating, +/area/awaymission/beach/offshore) +"OJ" = ( +/obj/structure/mineral_door/wood{ + tag = "icon-wood"; + icon_state = "wood" + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"ON" = ( +/obj/structure/table/wood, +/obj/item/harpoon, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"OR" = ( +/obj/structure/flora/ausbushes/grassybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"OT" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"OY" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Pb" = ( +/obj/structure/chair/stool, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Pc" = ( +/obj/effect/mob_spawn/human/resort_host, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Pm" = ( +/obj/structure/chair/office/light, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Po" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Pp" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Pu" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Pz" = ( +/obj/machinery/vending/clothing, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"PE" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"PH" = ( +/obj/structure/flora/grass/green, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"PL" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"PR" = ( +/turf/unsimulated/wall{ + icon_state = "rock"; + name = "rock" + }, +/area/space) +"PW" = ( +/turf/simulated/shuttle/wall{ + tag = "icon-swall3"; + icon_state = "swall3" + }, +/area/awaymission/beach/offshore) +"Qf" = ( +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Qh" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Qm" = ( +/obj/structure/chair/beachchair{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Qu" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/stalkybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Qv" = ( +/obj/item/key/ambulance, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"QI" = ( +/turf/unsimulated/wall/fakeglass{ + icon_state = "fakewindows2"; + dir = 1 + }, +/area/awaymission/undersea) +"QJ" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_l, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"QM" = ( +/mob/living/simple_animal/hostile/carp/megacarp{ + name = "Mega Sea Carp" + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"QN" = ( +/obj/effect/decal/snow/sand/surround{ + name = "rough sand" + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"QR" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"QW" = ( +/obj/structure/chair/beachchair/red{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"QY" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"QZ" = ( +/obj/structure/rack, +/obj/item/restraints/handcuffs/cable/zipties, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Rj" = ( +/obj/structure/table/wood, +/obj/item/storage/bag/plants, +/obj/item/clothing/gloves/botanic_leather, +/obj/item/shovel/spade, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Rk" = ( +/obj/structure/table/wood, +/obj/machinery/pos, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Rl" = ( +/mob/living/simple_animal/hostile/deathsquid, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Ru" = ( +/obj/structure/table/reinforced, +/obj/item/storage/secure/briefcase/syndie, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"RG" = ( +/obj/structure/chair/beachchair/red, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"RL" = ( +/obj/structure/chair/wood/wings{ + tag = "icon-wooden_chair_wings (EAST)"; + icon_state = "wooden_chair_wings"; + dir = 4 + }, +/obj/structure/sign/poster/official/cleanliness{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"RN" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"RQ" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f9"; + icon_state = "swall_f9" + }, +/area/awaymission/beach/offshore) +"RU" = ( +/turf/unsimulated/beach/water/deep/rock_wall{ + icon = 'icons/turf/walls/wood_wall.dmi'; + icon_state = "wood"; + name = "Wood Wall" + }, +/area/awaymission/undersea) +"RV" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"RY" = ( +/obj/structure/sign/poster/contraband/syndicate_recruitment{ + pixel_x = 0; + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Sb" = ( +/obj/structure/closet/crate, +/obj/item/toy/syndicateballoon, +/obj/item/toy/figure/syndie, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Sc" = ( +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"Se" = ( +/obj/item/clothing/head/nursehat, +/obj/structure/sign/poster/official/healthy{ + pixel_x = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"So" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Sp" = ( +/obj/structure/mineral_door/sandstone, +/turf/space, +/area/awaymission/undersea) +"Ss" = ( +/obj/structure/sign/poster/official/cohiba_robusto_ad{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Su" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Sw" = ( +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Sy" = ( +/obj/structure/mecha_wreckage/ripley/firefighter, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Sz" = ( +/obj/structure/flora/ausbushes/reedbush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"SD" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy{ + id = "volcano_island" + }, +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach/offshore) +"SL" = ( +/obj/structure/sign/poster/contraband/lusty_xenomorph{ + pixel_x = 32; + pixel_y = 0 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) +"SO" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"SS" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi/honk, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ST" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/genericbush, +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"SW" = ( +/obj/structure/table/wood, +/obj/machinery/pos, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"SX" = ( +/obj/machinery/vending/coffee/free, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Td" = ( +/obj/structure/closet/cabinet, +/obj/item/reagent_containers/food/drinks/bottle/rum, +/obj/item/reagent_containers/food/drinks/bottle/rum, +/obj/item/reagent_containers/food/drinks/bottle/rum, +/obj/item/reagent_containers/food/drinks/drinkingglass, +/obj/item/reagent_containers/food/drinks/drinkingglass, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"TF" = ( +/obj/structure/mirror{ + pixel_y = 32 + }, +/obj/structure/sink{ + pixel_y = 16 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"TG" = ( +/obj/structure/dresser, +/obj/item/storage/wallet/random, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"TI" = ( +/obj/structure/closet/crate/secure/loot, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"TL" = ( +/obj/structure/falsewall/brass, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"TP" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Ul" = ( +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Uv" = ( +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"Uy" = ( +/mob/living/simple_animal/hostile/carp/megacarp{ + name = "Mega Sea Carp" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"UK" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"UL" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f10"; + icon_state = "swall_f10" + }, +/area/awaymission/beach/offshore) +"UM" = ( +/obj/structure/chair/stool, +/mob/living/simple_animal/hostile/syndicate/ranged{ + loot = list() + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"UN" = ( +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"UP" = ( +/obj/structure/rack/skeletal_bar/right, +/obj/machinery/chem_dispenser/soda, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"UQ" = ( +/obj/structure/sign/directions/medical{ + dir = 1; + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"UR" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"UU" = ( +/obj/structure/closet/crate, +/obj/item/stack/sheet/mineral/uranium{ + amount = 20 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Va" = ( +/obj/structure/grille, +/obj/structure/shuttle/window, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"Vh" = ( +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Vp" = ( +/obj/effect/decal/cleanable/blood, +/obj/structure/chair/wood/wings{ + tag = "icon-wooden_chair_wings (EAST)"; + icon_state = "wooden_chair_wings"; + dir = 4 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Vq" = ( +/obj/structure/flora/rock, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Vw" = ( +/obj/structure/closet/secure_closet/personal, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"VE" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"VO" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy{ + id = "brass temple" + }, +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach/offshore) +"VP" = ( +/obj/item/ship_in_a_bottle, +/obj/structure/table/wood, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"VR" = ( +/obj/structure/flora/bush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"We" = ( +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Wk" = ( +/obj/structure/bed/amb_trolley, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Wm" = ( +/obj/structure/mecha_wreckage/odysseus, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Wq" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"Wr" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Wy" = ( +/obj/machinery/gateway{ + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"WC" = ( +/obj/machinery/bodyscanner{ + dir = 4 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"WK" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/chefhat, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"WL" = ( +/obj/structure/chair/comfy/black{ + dir = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"WN" = ( +/obj/structure/closet/crate/can, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"WQ" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"WS" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"WV" = ( +/obj/machinery/seed_extractor, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Xa" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Xi" = ( +/obj/machinery/door/airlock/hatch/syndicate{ + name = "Bunk House" + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Xr" = ( +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Xz" = ( +/obj/item/clothing/under/color/lightred, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"XF" = ( +/obj/structure/table/wood, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"XG" = ( +/obj/structure/flora/rock, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"XH" = ( +/obj/structure/piano, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"XL" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"XN" = ( +/obj/structure/table/wood, +/obj/item/storage/firstaid/adv{ + pixel_x = 8; + pixel_y = 8 + }, +/obj/item/storage/firstaid/toxin{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/storage/firstaid/o2, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"XU" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"XZ" = ( +/obj/structure/chair/stool, +/obj/structure/mirror{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Yc" = ( +/obj/structure/table/wood, +/obj/item/lighter/zippo, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Yi" = ( +/turf/unsimulated/wall/fakeglass{ + icon_state = "fakewindows2"; + dir = 1 + }, +/area/awaymission/beach/offshore) +"Yj" = ( +/obj/structure/closet/cabinet, +/obj/item/clothing/head/pirate, +/obj/item/clothing/suit/pirate_black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ym" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Yo" = ( +/obj/structure/shuttle/engine/heater{ + dir = 1 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Ys" = ( +/obj/effect/overlay/palmtree_l, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Yt" = ( +/obj/machinery/hydroponics/soil, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Yv" = ( +/obj/structure/table/reinforced, +/obj/item/storage/backpack/duffel/syndie/med, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"YB" = ( +/mob/living/simple_animal/crab, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"YF" = ( +/obj/item/clothing/shoes/brown, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"YG" = ( +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"YJ" = ( +/obj/structure/table/wood, +/obj/item/stack/spacecash/c100, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"YK" = ( +/obj/structure/flora/ausbushes/genericbush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"YM" = ( +/obj/machinery/telecomms/relay/preset/beachresort, +/obj/item/multitool, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"YO" = ( +/turf/unsimulated/wall/fakeglass{ + icon_state = "fakewindows2"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"YR" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone1"; + icon_state = "sandstone1" + }, +/area/awaymission/beach/offshore) +"YS" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f5"; + icon_state = "swall_f5" + }, +/area/awaymission/beach/offshore) +"YY" = ( +/obj/machinery/vending/autodrobe, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Zb" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Zg" = ( +/obj/structure/closet/crate, +/obj/item/toy/plushie/nukeplushie, +/obj/item/toy/nuke, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Zj" = ( +/mob/living/simple_animal/hostile/viscerator, +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"Zl" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_x = -32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Zo" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone3"; + icon_state = "sandstone3" + }, +/area/awaymission/beach/offshore) +"Zp" = ( +/obj/structure/closet/crate{ + name = "Ripley parts crate" + }, +/obj/item/mecha_parts/chassis/ripley, +/obj/item/mecha_parts/part/ripley_left_arm, +/obj/item/mecha_parts/part/ripley_left_leg, +/obj/item/mecha_parts/part/ripley_right_arm, +/obj/item/mecha_parts/part/ripley_right_leg, +/obj/item/mecha_parts/part/ripley_torso, +/obj/item/circuitboard/mecha/ripley/main, +/obj/item/circuitboard/mecha/ripley/peripherals, +/obj/item/mecha_parts/mecha_equipment/drill, +/obj/item/mecha_parts/mecha_equipment/cable_layer, +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp, +/obj/item/mecha_parts/mecha_equipment/rcd, +/obj/item/stack/sheet/plasteel{ + amount = 20 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Zr" = ( +/obj/effect/rune, +/obj/structure/spawner/nether{ + max_mobs = 3; + name = "weak netherworld link" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Zt" = ( +/obj/item/clothing/under/rainbow, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Zu" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"Zy" = ( +/obj/structure/table/reinforced, +/obj/item/storage/belt/military/traitor, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"ZC" = ( +/obj/structure/table/wood, +/obj/item/paicard, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"ZD" = ( +/obj/structure/closet/crate, +/obj/item/stack/marker_beacon/thirty, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"ZE" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/chair/stool/bar, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"ZG" = ( +/obj/structure/closet/crate, +/obj/item/clothing/suit/pirate_brown, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"ZN" = ( +/obj/machinery/door/airlock{ + name = "Resort Backroom" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"ZU" = ( +/obj/machinery/computer, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"ZW" = ( +/obj/structure/flora/rock/pile, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"ZZ" = ( +/obj/item/clothing/shoes/sandal, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) + +(1,1,1) = {" aa aa aa @@ -2629,77 +4734,77 @@ aa aa aa aa -bc -bt -bj -bz -bD -bj -bS -bj -bz -bj -bF -bj -bz -bz -bj -bz -bj -bz -bj -bj -bj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +JZ +if +zL +Nn +JZ +if +JZ +if +Su +PH +JZ +Nb +cg +PH +Nn +JZ +dh +JZ +Nb +JZ +JZ +if +zL +Nn +JZ +if +NP +RV +IO +NP +Fl +JZ +if dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds du du du du du "} -(6,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(2,1,1) = {" aa aa aa @@ -2886,27 +4991,48 @@ aa aa aa aa -be -bl -bD -bE -bj -bz -bf -bj -bL -cm -ch -bj -bL -bj -bf -bf -bj -dc -bL -bj -bj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +SO +Cr +Lk +Cr +Lk +Cr +Ka +fR +iI +wR +fR +iI +Lk +Lk +bH +Ia +fR +zr +fR +iI +SO +Cr +Lk +Cr +Lk +fR +fR +iI +iI +nV +fR +iI dj dr dr @@ -2927,263 +5053,6 @@ dr dr dr dr -dr -dt -du -du -du -du -du -"} -(7,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bw -by -bz -bN -bf -bT -bX -bW -bV -bX -bW -bV -bW -cc -bX -bW -bX -bV -bW -dh -dk -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds ds dt du @@ -3192,7 +5061,7 @@ du du du "} -(8,1,1) = {" +(3,1,1) = {" aa aa aa @@ -3379,52 +5248,53 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bw -bB -bj -bt -bj -bU -cd -bj -bj -bj -bj -bj -bj -cd -cY -bj -bj -bj -bj -bj +PR +PR +PR +PR +PR +PR +PR +PR +NP +fR +PL +fR +Ia +fR +iI +iI +fR +fR +Ia +fR +lJ +nV +Lq +iI +iI +fR +Ia +uR +Vq +fR +PL +fR +Ia +fR +iI +gW +fR +iI +Lq +gW +zZ +fR dj dr dr dr +ID dr dr dr @@ -3437,8 +5307,7 @@ dr dr dr dr -dr -dr +ID dr dr ds @@ -3449,28 +5318,7 @@ du du du "} -(9,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(4,1,1) = {" aa aa aa @@ -3657,30 +5505,49 @@ aa aa aa aa -cg -bw -bB -bF -bz -bN -bV -bj -cj -cn -cy -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -eo -dr -dr +PR +PR +PR +PR +PR +PR +PR +PR +qP +NZ +fR +Ia +fR +gW +Ia +fR +iI +nV +fR +iI +lJ +uR +fR +gW +fR +iI +fR +iI +Lq +NZ +fR +Ia +fR +gW +Ia +Oz +NG +NG +NG +iI +iI +iI +dj dr dr dr @@ -3690,7 +5557,9 @@ dr dr dr dr +ka dr +ka dr dr dr @@ -3706,28 +5575,7 @@ du du du "} -(10,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(5,1,1) = {" aa aa aa @@ -3914,27 +5762,48 @@ aa aa aa aa -cg -bx -bj -bz -bz -bj -bW -bj -ck -co -cz -bj -bj -bj -bz -bj -bj -bj -bj -bj -bj +PR +PR +PR +PR +PR +PR +PR +PR +Im +Mx +fR +iI +Ia +fR +kq +fR +iI +fR +uR +fR +iI +iI +fR +iI +fR +iI +fR +fR +AS +Mx +fR +iI +Ia +fR +kq +iI +iI +zr +fR +PL +wR +fR dj dr dr @@ -3950,7 +5819,7 @@ dr dr dr dr -dr +Bt dr dr dr @@ -3963,28 +5832,7 @@ du du du "} -(11,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(6,1,1) = {" aa aa aa @@ -4171,27 +6019,48 @@ aa aa aa aa -bg -bj -bE -bj -bD +PR +PR +PR +PR +PR +PR +PR +PR +rt +NG +Ia +zr +fR +iI +fN +fR +zZ +fV +Vq +fR +zZ +fR +fN +fN +fR +We +zZ +uR +nl +lJ +nV +Lq +iI +iI +fR +Ia +Vq +zZ +lJ bL -bX -bj -cl -cp -cA -bj -bj -bj -bj -bj -bj -dd -bj -em -bj +LY +fm dj dr dr @@ -4202,9 +6071,9 @@ dr dr dr dr +ka dr -dr -dr +ka dr dr dr @@ -4220,49 +6089,7 @@ du du du "} -(12,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(7,1,1) = {" aa aa aa @@ -4428,56 +6255,6 @@ aa aa aa aa -bf -bj -bz -bz -bj -bz -bU -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(13,1,1) = {" aa aa aa @@ -4499,6 +6276,77 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +EM +iI +lJ +fN +gT +NP +MB +Fs +NP +MB +Fs +MB +Db +NP +MB +gT +NP +MB +Fs +NP +MB +Fs +MB +NP +Db +Fs +MB +gT +NP +MB +Fs +NP +dk +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +dt +du +du +du +du +du +"} +(8,1,1) = {" aa aa aa @@ -4685,28 +6533,49 @@ aa aa aa aa -bh -bj -bz -bz -bj -bj -bV -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -eq +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +VE +fR +Mx +fR +rg +HI +pw +pw +pw +pw +pw +pw +bw +Po +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +ZD +hI +JJ +hI +LJ dr dr dr @@ -4734,28 +6603,7 @@ du du du "} -(14,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(9,1,1) = {" aa aa aa @@ -4942,28 +6790,49 @@ aa aa aa aa -cg -bA -bD -bt -bE -bf -bW -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bF -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +VE +uR +iI +lJ +Fs +yS +kO +XF +Qm +QW +XF +kO +Pu +Po +DR +Vw +lo +DR +XZ +DR +XZ +DR +XZ +DR +ph +DR +GR +DR +mJ +hI +wi +hI +eo dr dr dr @@ -4991,28 +6860,7 @@ du du du "} -(15,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(10,1,1) = {" aa aa aa @@ -5199,28 +7047,49 @@ aa aa aa aa -bX -by -bF -bj -bz -bj -bT -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -de -bj -bj -er +PR +PR +PR +PR +PR +PR +PR +PR +JZ +SO +fR +iI +iI +fR +MB +yS +lo +lo +lo +lo +lo +lo +Pu +Po +DR +Vw +lo +DR +AX +DR +AX +DR +AX +DR +qf +DR +Fv +DR +DR +DR +DR +DR +LJ dr dr dr @@ -5248,28 +7117,7 @@ du du du "} -(16,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(11,1,1) = {" aa aa aa @@ -5456,28 +7304,49 @@ aa aa aa aa -cg -bB -bj -bF -bF -bf -bU -bj -bj -bK -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +Cm +fR +zr +fR +Ia +zZ +NP +Fp +TP +TP +TP +TP +TP +TP +BR +Po +DR +Vw +lo +lo +lo +cl +lo +cl +lo +cl +lo +xH +lo +DR +dd +yF +em +DR +LJ dr dr dr @@ -5505,28 +7374,7 @@ du du du "} -(17,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(12,1,1) = {" aa aa aa @@ -5713,29 +7561,49 @@ aa aa aa aa -cg -bB -bj -bz -bz -bz -bX -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +IO +fR +iI +iI +fR +iI +rg +hI +hI +hI +hI +hI +hI +hI +hI +Po +DR +Vw +lo +lo +lo +lo +lo +lo +lo +lo +lo +lo +lo +DR +BF +lo +lo +DR +LJ dr dr dr @@ -5750,6 +7618,7 @@ dr dr dr dr +ID dr dr dr @@ -5762,28 +7631,7 @@ du du du "} -(18,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(13,1,1) = {" aa aa aa @@ -5970,33 +7818,54 @@ aa aa aa aa -cg -by -bE -bj -bj -bj -bY -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -dd -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +Hp +fR +iI +iI +fR +fR +Fs +hI +hI +hI +hI +hI +hI +hI +hI +Po +DR +Vw +lo +DR +YY +Pz +lo +lo +rV +lo +JB +hB +Mw +DR +Nq +lo +lo +DR +LJ dr dr dr dr +ZW dr dr dr @@ -6019,7 +7888,7 @@ du du du "} -(19,1,1) = {" +(14,1,1) = {" aa aa aa @@ -6206,50 +8075,49 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bk -bj -bz -bj -bt -bj -bV -bj -bj -bj -bl -bl -bl -bl -bl -bj -bj -bj -bj -bj -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +YK +Ia +Mx +zr +fN +MB +hI +xv +hI +hI +hI +hI +hI +hI +bD +DR +DR +DR +DR +DR +DR +lo +lo +DR +DR +DR +DR +DR +DR +DR +DR +ET +DR +LJ dr dr dr @@ -6260,6 +8128,7 @@ dr dr dr dr +ZW dr dr dr @@ -6276,28 +8145,7 @@ du du du "} -(20,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(15,1,1) = {" aa aa aa @@ -6484,28 +8332,49 @@ aa aa aa aa -cg -by -bz -bD -bj -bN -bW -bj -bj -bi -cE -cH -cI -dA -dD -by -bj -bj -bj -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +NP +EM +uR +fR +iI +fR +gT +hI +Qf +hI +hI +xG +hI +hI +hI +hI +hF +wc +wc +bT +wc +la +wc +wc +la +wc +wc +wc +wc +wc +wc +de +hI +hI +er dr dr dr @@ -6533,28 +8402,7 @@ du du du "} -(21,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(16,1,1) = {" aa aa aa @@ -6741,28 +8589,49 @@ aa aa aa aa -bg -bC -bj -bF -bF -bj -bT -bj -bj -bi -cD -cS -dq -dq -dC -dH -bj -bj -bj -dg -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +VE +fR +uR +uR +fN +rg +hI +hI +jN +HQ +BJ +hI +hI +bB +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -6790,28 +8659,7 @@ du du du "} -(22,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(17,1,1) = {" aa aa aa @@ -6998,28 +8846,49 @@ aa aa aa aa -bi -bw -by -bj -bF -bj -bV -bj -bj -cq -cD -cU -dq -dq -dF -dH -bj -bj -bj -bj -bj -ew +PR +PR +PR +PR +PR +PR +PR +PR +JZ +VE +fR +iI +iI +iI +NP +hI +hI +CC +kU +oL +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -7047,62 +8916,7 @@ du du du "} -(23,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(18,1,1) = {" aa aa aa @@ -7255,56 +9069,6 @@ aa aa aa aa -bj -bg -bz -bF -bK -bL -bX -bj -bj -cr -cD -cW -dq -dq -cV -dH -bj -bj -bj -dg -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(24,1,1) = {" aa aa aa @@ -7339,6 +9103,77 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +EM +zr +fR +fR +pu +yG +hI +hI +Gu +Wy +sa +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +YB +hI +Gz +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +ZW +dr +ds +dt +du +du +du +du +du +"} +(19,1,1) = {" aa aa aa @@ -7512,28 +9347,62 @@ aa aa aa aa -bj -bj -bj -bz -bD -bj -bW -bj -bj -cq -cD -cX -dq -dq -cV -dH -bj -bj -bj -bj -bj -dj +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Iu +fR +iI +fR +Mx +fR +Fs +hI +hI +hI +hI +xG +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -7561,28 +9430,7 @@ du du du "} -(25,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(20,1,1) = {" aa aa aa @@ -7769,29 +9617,49 @@ aa aa aa aa -bl -bz -bE -bj -bj -bj -bV -bj -bj -bi -cD -cV -dq -dq -cV -dH -bj -bj -bj -dg -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +EM +iI +Ia +fR +lJ +MB +hI +Qf +xv +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -7805,6 +9673,7 @@ dr dr dr dr +ka dr dr dr @@ -7818,28 +9687,7 @@ du du du "} -(26,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(21,1,1) = {" aa aa aa @@ -8026,28 +9874,49 @@ aa aa aa aa -cg -bB -bj -bz -bj -bP -bX -bj -bj -bi -cD -da -dq -dq -cV -dH -bj -bj -bj -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +Cm +Ix +fR +uR +uR +fR +gT +DR +DR +iL +DR +DR +DR +DR +DR +sV +DR +DR +Ja +YB +hI +hI +hI +hI +hI +Qf +xv +hI +hI +Qf +hI +hI +dg +hI +LJ dr dr dr @@ -8075,28 +9944,7 @@ du du du "} -(27,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(22,1,1) = {" aa aa aa @@ -8283,29 +10131,49 @@ aa aa aa aa -cg -by -bj -bj -bz -bj -bT -bj -bj +PR +PR +PR +PR +PR +PR +PR +PR +ww +iI +EM +fR +uR +fR +Fs +DR +YM +oN +oN +jA +DR bi -cD -cZ -dq -dq -cV -dH -bj -bj -bj -dg -bj -ey -dr +gt +lo +lo +bF +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +xv +hI +hI +hI +hI +ew dr dr dr @@ -8313,6 +10181,7 @@ dr dr dr dr +ID dr dr dr @@ -8332,49 +10201,7 @@ du du du "} -(28,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(23,1,1) = {" aa aa aa @@ -8540,56 +10367,6 @@ aa aa aa aa -bm -bz -bz -bK -bj -bL -bZ -ce -bj -bi -cD -dl -dq -dq -cV -dH -bj -bj -bj -bj -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -dt -du -du -du -du -"} -(29,1,1) = {" aa aa aa @@ -8611,6 +10388,77 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +Lk +iI +uR +gW +zZ +NP +DR +oB +UN +UN +UN +DR +bj +lo +lo +lo +bK +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +RG +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +Bt +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(24,1,1) = {" aa aa aa @@ -8797,28 +10645,49 @@ aa aa aa aa -bj -bj -bz -bj -bK -bj -ca -bj -bj -bi -cD -db -dq -dq -cV -dH -bj -bj -bj -dg -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +iI +Ia +fR +MB +Kj +fc +UN +UN +pO +DR +AD +lo +lo +lo +XF +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -8837,37 +10706,16 @@ dr dr dr dr -dt -dx +dr +ds dt du du -dt -dt +du +du du "} -(30,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(25,1,1) = {" aa aa aa @@ -9054,28 +10902,49 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +iu +iI +zr +fR +fR +hn +Fs +Kj +WV +UN +UN +XN +DR bl -bl -bF -bj -bz -bj -bX -cf -bj -bi -cD -dm -dq -dq -cV -dH -bj -bj -bF -bj -bj -dj +lo +lo +lo +Yc +ZE +hI +hI +yB +hI +hI +Fb +hI +hI +hI +hI +hI +hI +hI +dg +hI +LJ dr dr dr @@ -9093,38 +10962,17 @@ dr dr dr dr +dr +dr +ds dt -dt -dw du du du du -dt du "} -(31,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(26,1,1) = {" aa aa aa @@ -9311,28 +11159,49 @@ aa aa aa aa -cg -bw -by -bE -bj -bQ -bT -cd -bj -bi -cF -cH -cH -cH -cI -by -bj -bj -bj -dg -bj -er +PR +PR +PR +PR +PR +PR +PR +PR +JZ +VE +fR +iI +fR +Id +NP +Kj +IA +UN +UN +GK +DR +DD +lo +lo +lo +ZC +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -9341,6 +11210,7 @@ dr dr dr dr +ZW dr dr dr @@ -9350,38 +11220,16 @@ dr dr dr dr +dr +ds dt -dv -dw du du du du -dt du "} -(32,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(27,1,1) = {" aa aa aa @@ -9568,28 +11416,49 @@ aa aa aa aa -cg -cg -bJ -bj -bD -bj -cb -bj -bj -bj -bg -bg -bg -bg -bg -bj -bj -bj -bj -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +EM +fR +fR +iI +fR +gT +Kj +IA +UN +Pc +iT +DR +Jl +lo +lo +bM +XF +ZE +hI +hI +hI +Fb +hI +cZ +hI +Fb +hI +hI +hI +hI +hI +RG +hI +ey dr dr dr @@ -9607,38 +11476,17 @@ dr dr dr dr +dr +dr +ds dt du -dw -du du du du -dt du "} -(33,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(28,1,1) = {" aa aa aa @@ -9825,28 +11673,50 @@ aa aa aa aa -cg -cg -by -bj -bF -bf -bV -bj -bj -cJ -cN -bl -bj -bj -bj -bj -bj -bj -bj -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +qb +iI +iI +gW +fR +zZ +wQ +Kj +IA +UN +UN +UN +kf +lo +lo +lo +lo +gA +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr dr dr dr @@ -9864,38 +11734,16 @@ dr dr dr dr +dr +ds dt dt -dw du du -dt -du du du "} -(34,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(29,1,1) = {" aa aa aa @@ -10082,28 +11930,49 @@ aa aa aa aa -cg -bw -by -bD -bL -bj -cc -bj -bi -cE -cH -cI -by -bj -bj -bj -bj -bj -bj -bj -bj -dj +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +iI +fR +gW +fR +Zb +Kj +IA +UN +UN +UN +ZN +lo +lo +lo +lo +qY +ZE +hI +hI +hI +hI +hI +db +hI +hI +hI +hI +hI +hI +hI +dg +hI +LJ dr dr dr @@ -10124,35 +11993,14 @@ dr dr dt dx +dt du du -du -du -du +dt +dt du "} -(35,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(30,1,1) = {" aa aa aa @@ -10339,31 +12187,52 @@ aa aa aa aa -cg -bX -by -bz -bj -bN -bX -bj -bi -cD -cO -dn -by -bj -bF -bj -bj -bj -bj -bj -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +iu +NG +uR +fR +iI +fR +NP +Kj +wd +UN +Pc +yT +DR +CB +lo +lo +lo +pP +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr +ZW dr dr dr @@ -10378,38 +12247,17 @@ dr dr dr dr -dr -dr -dx dt +dt +dw du du du du +dt du "} -(36,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(31,1,1) = {" aa aa aa @@ -10596,30 +12444,49 @@ aa aa aa aa -bo -bd -bB -bE -bz -bj -bW -bj -bi -cF -cH -cI -by -bj -bj -bj -bj -bj -df -bj -bj -dj -dr -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +EM +zr +fR +nV +gT +DR +id +UN +UN +iX +DR +lC +lo +lo +lo +SW +ZE +hI +hI +hI +hI +hI +hI +hI +YB +hI +CJ +hI +hI +hI +RG +hI +er dr dr dr @@ -10637,36 +12504,17 @@ dr dr dr dr -ds dt +dv +dw du du du du +dt du "} -(37,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(32,1,1) = {" aa aa aa @@ -10853,30 +12701,49 @@ aa aa aa aa -bn -cg -bM -bj -bj -bN -ca -bj -bj -bg -bg -bg -bj -bj -bj -bj -bj -bj -bj -bj -bj -ey -dr -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +PL +fR +Ia +fR +zi +DR +Jb +DP +DP +rT +Zu +na +vZ +bJ +ue +Zu +OT +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -10894,198 +12761,17 @@ dr dr dr dr -ds dt du +dw +du du du du +dt du "} -(38,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +(33,1,1) = {" aa aa aa @@ -11110,56 +12796,6 @@ aa aa aa aa -bn -cg -ci -bf -bL -bj -cc -bj -bj -cJ -cN -bl -bj -bj -em -bj -bj -bj -bj -bj -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(39,1,1) = {" aa aa aa @@ -11189,139 +12825,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab aa aa aa @@ -11367,56 +12870,6 @@ aa aa aa aa -bq -bw -by -bj -bz -bf -bX -bj -bi -cE -cH -cI -by -bj -bj -bj -bj -bz -bj -bj -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(40,1,1) = {" aa aa aa @@ -11446,139 +12899,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab aa aa aa @@ -11624,30 +12944,63 @@ aa aa aa aa -cg -bG -by -bz -bj -bj -bT -bj -bi -cD -cO -dn -by -bj -bj -bj -bj -bj -de -bj -bj -eB -dr -dr +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +EM +fR +uR +fN +Fs +DR +Kk +UN +UN +HC +Mq +WK +Bq +lo +XF +DR +bf +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +Jv +hI +LJ dr dr dr @@ -11665,15 +13018,42 @@ dr dr dr dr -ds dt +dt +dw du du +dt du du du "} -(41,1,1) = {" +(34,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa aa aa aa @@ -11703,139 +13083,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab aa aa aa @@ -11881,56 +13128,6 @@ aa aa aa aa -bp -cg -by -bL -bj -bN -bV -bj -bi -cF -cH -cI -by -bF -bj -bj -bj -bj -bj -bj -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(42,1,1) = {" aa aa aa @@ -11960,139 +13157,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab aa aa aa @@ -12138,29 +13202,62 @@ aa aa aa aa -cg -cg -bB -bj -bz -bj -bW -bj -bj -bg -bg -bg -bj -bj -bj -bj -bj -bj -bj -dg -bj -dj -dr +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +EM +Ia +zZ +fR +Db +DR +Eq +UN +kd +Rj +DR +tF +Bq +lo +XF +DR +Pu +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +Qf +xv +LJ dr dr dr @@ -12179,15 +13276,16 @@ dr dr dr dr -ds dt +dx +du du du du du du "} -(43,1,1) = {" +(35,1,1) = {" aa aa aa @@ -12217,139 +13315,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab aa aa aa @@ -12395,29 +13360,161 @@ aa aa aa aa -cg -cg -bB -bj -bD -bj -cc -bj -bj -cJ -cN -bl -bj -bj -bj -bj -bj -bj -bj -bj -bj -ey -dr +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +Vq +EM +iI +fR +lJ +NP +DR +Kk +zx +EE +zQ +DR +oe +bX +by +bz +DR +bN +hI +hI +hI +hI +hI +hI +hI +Qf +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -12426,6 +13523,7 @@ dr dr dr dr +ZW dr dr dr @@ -12436,7 +13534,7 @@ dr dr dr dr -ds +dx dt du du @@ -12444,7 +13542,25 @@ du du du "} -(44,1,1) = {" +(36,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa aa aa aa @@ -12474,139 +13590,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab aa aa aa @@ -12652,56 +13635,6 @@ aa aa aa aa -bg -bH -bj -bz -bz -bL -bX -bj -bi -cE -cH -cI -by -bj -bj -bj -bj -bj -bj -dg -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(45,1,1) = {" aa aa aa @@ -12731,139 +13664,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab aa aa aa @@ -12909,29 +13709,69 @@ aa aa aa aa -bj -bj -bj -bz -bj -bR -cb -bf -bi -cD -cO -dn -by -bj -bj -bj -bj -bj -bj -bj -bj -eq -dr +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Ym +Lq +VE +zr +iI +fR +MB +Mq +DR +DR +DR +DR +DR +DR +bd +DR +DR +DR +Dm +hI +hI +bB +hI +hI +hI +hI +xv +hI +hI +hI +hI +hI +Ez +hI +LJ dr dr dr @@ -12948,6 +13788,7 @@ dr dr dr dr +ZW dr dr ds @@ -12958,7 +13799,31 @@ du du du "} -(46,1,1) = {" +(37,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa aa aa aa @@ -12988,139 +13853,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -ad -ad -ad -av -av -av -ad -ab -ab -ab -ab -ab -ab aa aa aa @@ -13166,56 +13898,6 @@ aa aa aa aa -bj -bl -bC -bj -bz -bf -bX -bz -bi -cF -cH -cI -by -bj -bj -bj -bj -bj -bz -bj -bj -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(47,1,1) = {" aa aa aa @@ -13245,139 +13927,6 @@ aa aa aa aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -ad -ab -ab -ab -ab -ab -ab aa aa aa @@ -13423,28 +13972,63 @@ aa aa aa aa -br -bw -cg -by -bj -bN -bT -cf -bj -cK -bg -bg -bj -bj -bj -bj -bj -bj -bj -bj -bz -dj +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +ww +fR +Ip +fR +fR +lJ +Zb +lQ +Fy +XF +Bz +Gd +Ol +XF +EB +bM +XF +DR +CE +hI +hI +hI +hI +hI +hI +hI +hI +hI +Iw +hI +hI +hI +iK +hI +ey dr dr dr @@ -13472,7 +14056,7 @@ du du du "} -(48,1,1) = {" +(38,1,1) = {" aa aa aa @@ -13508,127 +14092,127 @@ ab ab ab ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -ad +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab ab ab ab @@ -13659,68 +14243,68 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bX -cg -bB -bL -bj -ca -cg -bW -bV -bW -bX -cc -bV -bW -bX -cc -bV -bX -bW -di -dk -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds +PR +PR +PR +PR +PR +PR +PR +PR +ww +fR +So +fN +zZ +fR +Db +DR +Ss +lo +lo +lo +lo +lo +lo +lo +bM +DR +Ao +hI +hI +hI +cJ +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +ID +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr ds dt du @@ -13729,7 +14313,7 @@ du du du "} -(49,1,1) = {" +(39,1,1) = {" aa aa aa @@ -13765,127 +14349,127 @@ ab ab ab ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -ad +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab ab ab ab @@ -13916,50 +14500,49 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -cg -ci -bE -bj -bj -ch -bD -bf -ch -bL -bj -bz -bf -bz -bL -bz -bj -bN -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +zn +iI +EM +fR +iI +fN +NP +DR +lo +lo +lo +lo +lo +lo +lo +lo +vm +DR +UR +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr @@ -13979,6 +14562,7 @@ dr dr dr dr +ds dt du du @@ -13986,7 +14570,7 @@ du du du "} -(50,1,1) = {" +(40,1,1) = {" aa aa aa @@ -14022,133 +14606,135 @@ ab ab ab ab -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -av -ad ab ab ab ab ab ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa aa aa aa @@ -14171,6 +14757,83 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +zZ +EM +iI +fR +fR +gT +DR +sk +Gm +sk +Gm +sk +Gm +sk +lo +lo +CF +UR +hI +hI +hI +hI +hI +hI +Fb +hI +Fb +hI +Fb +hI +hI +Bc +hI +eB +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(41,1,1) = {" +aa +aa +aa +aa +aa +aa aa aa aa @@ -14194,41 +14857,218 @@ aa aa aa aa -cg -cg -bx -bz -bj -bL -bL -bj -bj -bz -bz -bQ -bj -bz -bj -bR -bj -bK -bE -bj -bj -dj -dr -dr -dr +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +My +fR +EM +zZ +fR +lJ +Fs +DR +Gm +sk +Gm +sk +Gm +sk +Gm +lo +lo +lo +UR +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ dr dr dr dr +ZW dr dr dr dr dr dr +ka dr dr dr @@ -14236,6 +15076,7 @@ dr dr dr dr +ds dt du du @@ -14243,7 +15084,7 @@ du du du "} -(51,1,1) = {" +(42,1,1) = {" aa aa aa @@ -14279,133 +15120,146 @@ ab ab ab ab -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -aq -av -av -ad -ad -ad -ad -ad -ad -ad -ad -av -ad ab ab ab ab ab ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa aa aa aa @@ -14417,6 +15271,82 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +VE +fR +iI +fR +MB +DR +sk +Gm +sk +Gm +sk +Gm +sk +lo +vm +DR +hI +hI +hI +hI +hI +hI +hI +Fb +hI +Fb +hI +Fb +hI +hI +Bc +xv +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ID +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(43,1,1) = {" +aa +aa +aa +aa +aa aa aa aa @@ -14441,6 +15371,153 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa aa aa aa @@ -14451,30 +15528,49 @@ aa aa aa aa -bs -cg -by -bj -bz -bj -bz -bK -bj -bz -bO -bK -bL -bj -bz -bj -bO -bz -bS -bz -bj -dj -dr -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +VE +fR +Ia +fR +Db +DR +Gm +sk +Gm +gw +Gm +sk +Gm +lo +lo +DR +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +ey dr dr dr @@ -14484,6 +15580,7 @@ dr dr dr dr +ka dr dr dr @@ -14493,6 +15590,7 @@ dr dr dr dr +ds dt du du @@ -14500,7 +15598,7 @@ du du du "} -(52,1,1) = {" +(44,1,1) = {" aa aa aa @@ -14543,111 +15641,17 @@ ad ad ad ad -av -av -av -av -av -av -av -av -av -av ad ad -av -av ad -av ad ad ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av ad -av -av -av -av -av -av -av -av -av ad -av -av ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av ad ad -av -av -av -av -av -av -av ad ad ad @@ -14655,7 +15659,101 @@ ad ad ad ad -av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad ad ab ab @@ -14687,50 +15785,49 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -br -bw -cs -bj -bj -bj -bR -cB -bl -bl -bl -bz -bz -bz -bj -bD -bj -bD -bj -bz -bj -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +Cm +Cr +fR +iI +iI +zZ +NP +DR +gp +Gm +sk +Gm +sk +Gm +sk +lo +WL +DR +UR +xv +hI +hI +hI +hI +hI +AW +gJ +gJ +gJ +AW +hI +hI +Bc +hI +LJ dr dr dr @@ -14750,6 +15847,7 @@ dr dr dr dr +ds dt du du @@ -14757,7 +15855,7 @@ du du du "} -(53,1,1) = {" +(45,1,1) = {" aa aa aa @@ -14796,13 +15894,12 @@ ab ad ad ad -ad -ad -ad -ad av av av +ad +ad +av av av av @@ -14810,17 +15907,9 @@ av av av av -ad av -aq av -ad av -ad -ad -ad -ad -ad av av av @@ -14860,7 +15949,6 @@ av av av av -ad av av av @@ -14874,7 +15962,7 @@ av av av av -ad +av av av av @@ -14899,20 +15987,30 @@ av av ad ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +HJ av av +ao av av av -av -av +HJ ad ad ad ad ad ad -av ad ab ab @@ -14944,50 +16042,49 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -cg -bB -bO -bj -bi -bw -bw -cv -cg -bJ -bR -bj -bl -dP -bG -be -bz -bS -bz -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +iI +fR +wR +zi +DR +Gm +sk +Gm +sk +Gm +sk +Gm +lo +XF +DR +UR +Qf +hI +hI +xv +hI +hI +lo +lo +lo +lo +lo +KM +hI +hI +hI +Gz dr dr dr @@ -15007,6 +16104,7 @@ dr dr dr dr +ds dt du du @@ -15014,7 +16112,7 @@ du du du "} -(54,1,1) = {" +(46,1,1) = {" aa aa aa @@ -15052,11 +16150,7 @@ ab ab ad ad -ad av -ad -ad -ad av av av @@ -15066,19 +16160,11 @@ av av av av -ad -ad av av av -ad av av -ad -ad -ad -ad -ad av av av @@ -15125,10 +16211,11 @@ av av av av -ad av av av +ad +av av av av @@ -15151,9 +16238,11 @@ av av av av +an av av av +ao ad ad ad @@ -15162,15 +16251,24 @@ ad ad ad ad +HJ av +av +an +av +av +av +av +uz +av +av +ao ad ad ad ad ad ad -av -ad ab ab ab @@ -15201,50 +16299,49 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -cg -by -bj -bz -dE -cC -bX -bG -cC -do -ef -ek -dG -cC -bw -dV -bD -bj -bz -dj -dr +PR +PR +PR +PR +PR +PR +PR +PR +JZ +NG +Ix +fR +iI +fN +NP +DR +sk +Gm +sk +Gm +sk +Gm +SL +lo +WL +DR +UR +hI +Bc +hI +hI +Bc +hI +MG +lo +NF +lo +zg +hI +xv +hI +hI +LJ dr dr dr @@ -15264,14 +16361,15 @@ dr dr dr dr -dy +ds +dt +du du du du du -LZ "} -(55,1,1) = {" +(47,1,1) = {" aa aa aa @@ -15310,6 +16408,10 @@ ab ad ad ad +ad +ad +av +av av av av @@ -15323,19 +16425,8 @@ av av av av -ad -ad -ad -ad -ad -ad av av -ad -ad -ad -ad -ad av av av @@ -15375,7 +16466,6 @@ av av av av -ad av av av @@ -15388,7 +16478,6 @@ av av av av -ad av av av @@ -15404,6 +16493,7 @@ av av av av +ao av av av @@ -15417,17 +16507,25 @@ ad ad ad ad +ao +av +av +av +av +av +uz +av +av +av +av +av +av +av +HJ ad ad ad ad -ad -ad -ad -ad -ad -av -ad ab ab ab @@ -15458,77 +16556,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bw -by -bE -bj -bQ -bT -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -be -bC -bF -bJ -bj -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +PR +PR +PR +PR +PR +PR +PR +PR +zn +iI +fR +EM +fR +lJ +gT +Mq +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +UR +hI +hI +hI +hI +hI +Qf +no +OD +OD +OD +Ih +hI +Qf +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du "} -(56,1,1) = {" +(48,1,1) = {" aa aa aa @@ -15567,6 +16665,8 @@ ab ad ad ad +ad +ad av av av @@ -15580,20 +16680,7 @@ av av av av -ad -ad av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad av av av @@ -15637,16 +16724,15 @@ av av av av -ad av av av av +ad av av av av -ad av av av @@ -15668,23 +16754,35 @@ av av av av +av +av +an +av +ao ad ad ad ad ad +av +av +av +av +av +uz +uz +av +av +an +av +uz +av +av +av +av ad ad ad -ad -ad -ad -ad -ad -ad -ad -av -ad ab ab ab @@ -15715,77 +16813,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -bJ -bj -bD -bj -cb -bj -bj -bj -cQ -dp -dp -el -el -dp -dp -dY -bj -bj -cb -by -bj -bF -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +Vq +fR +VE +zZ +fR +Zb +JZ +MB +Fs +MB +NP +Db +Fs +MB +NP +Db +Fs +NP +NP +Nn +Nn +NP +MB +gT +cc +NP +cc +Db +NP +MB +NP +Fs +cc +dk +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +dt +du +du +du +du +du "} -(57,1,1) = {" +(49,1,1) = {" aa aa aa @@ -15824,12 +16922,12 @@ ab ad ad ad -av ad ad av av av +ao av av av @@ -15841,13 +16939,6 @@ av av av av -ad -ad -ad -ad -ad -ad -ad av av av @@ -15863,10 +16954,7 @@ av av av av -ad av -ad -ad av av av @@ -15893,11 +16981,14 @@ av av av av -ad av av av av +ad +av +av +av av av av @@ -15929,18 +17020,25 @@ ad ad ad ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad +HJ +av +av +uz +uz +av +av +av +uz +av av +av +av +av +uz +av +av +ao +ad ad ab ab @@ -15972,77 +17070,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -by -bj -bF -bf -bV -bj -bj -bj -cP -dZ -eg -eh -eh -dR -et -ex -bj -bf -bV -by -bD -bL -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +So +zr +fR +fR +Vq +Ia +fN +Vq +zZ +fR +iI +fN +iI +zZ +iI +fR +lJ +fR +Vq +Ia +fN +Vq +fR +iI +fN +fR +Lk +iI +uR +gW +zZ +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du "} -(58,1,1) = {" +(50,1,1) = {" aa aa aa @@ -16097,12 +17195,10 @@ av av av av -av -av -ad -ad +ao ad ad +TL ad av av @@ -16119,14 +17215,11 @@ av av av av -ad av av av av av -ad -ad av av av @@ -16146,12 +17239,10 @@ av av av av -ad av av -ad -av av +ad av av av @@ -16161,8 +17252,6 @@ av av av av -ad -av av av av @@ -16179,25 +17268,34 @@ av av av av +ao av av av av +HJ ad ad ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad av +av +uz +av +av +av +av +uz +av +av +uz +av +uz +av +av +av +av +av +ad ad ab ab @@ -16229,77 +17327,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bw -by -bD -bL -bj -cc -bj -bj -bj -dU -ec -ei -eh -eh -eh -et -eA -bj -bj -cc -by -bz -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +SO +iI +fR +zZ +zZ +fR +fR +iI +iI +nV +fR +iI +fR +wR +fR +gW +zr +fR +fR +fR +fR +iI +iI +fR +iI +fR +fR +fR +fR +iI +Ia +fR +dj +dr +dr +dr +ID +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ID +dr +dr +ds +dt +du +du +du +du +du "} -(59,1,1) = {" +(51,1,1) = {" aa aa aa @@ -16353,15 +17451,13 @@ av av av av +ad +ad av av av ad ad -ad -ad -ad -av av av av @@ -16370,11 +17466,11 @@ av av av av +aq av av av av -ad av av av @@ -16385,7 +17481,6 @@ av av av av -ad av av av @@ -16416,9 +17511,10 @@ av av av av +ad +ad av av -ad av av av @@ -16435,26 +17531,28 @@ av av av av +ad +ad +ad av +uz +uz av av +HJ av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad +an av +av +av +av +av +an +av +uz +av +HJ ad ab ab @@ -16486,77 +17584,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bX -by -bz -bj -bN -bX -bj -bj -bj -cP -eb -eh -eh -eh -dS -et -ez -bj -bN -bX -bB -bE -bz -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +PR +PR +PR +PR +PR +PR +PR +PR +Ym +fR +EM +fR +iI +fR +iI +gW +fR +iI +Lq +gW +zZ +fR +iI +fR +Lq +iI +kq +iI +fR +gW +fR +iI +Lq +zZ +fR +iI +NG +iI +zr +fR +fR +fR +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +ka +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du "} -(60,1,1) = {" +(52,1,1) = {" aa aa aa @@ -16597,6 +17695,8 @@ ad ad ad ad +ad +ad av av av @@ -16607,14 +17707,13 @@ av av av av -av -av -av -av -av +ad +ad av av ad +jY +ad ad ad av @@ -16629,10 +17728,6 @@ av av av av -ad -ad -av -av av av av @@ -16643,7 +17738,13 @@ av av av av -ad +RU +RU +RU +RU +RU +RU +RU av av av @@ -16656,18 +17757,20 @@ av av av av +ad av av av av -ad av av av av av +ad av av +ad av av av @@ -16676,7 +17779,6 @@ av av av av -ad av av av @@ -16686,31 +17788,27 @@ av av av av +ad +ad +ad av +uz av av av +an av av av +uz av +an +uz +uz av av av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad av ad ab @@ -16743,77 +17841,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bo -bd -bB -bE -bz -bj -bW -bj -bj -bj -cR -dp -dp -el -el -dp -dp -ea -bj -bj -bW -bM -bj -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +PR +PR +PR +PR +PR +PR +PR +PR +zn +iI +oC +fR +fR +fR +wR +Oz +NG +NG +NG +iI +iI +iI +fR +Ia +fR +Ia +fR +iI +fR +Oz +NG +rD +NG +iI +iI +fR +fR +VE +fR +iI +fR +Id +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +Bt +dr +dr +dr +dr +ds +dt +du +du +du +du +du "} -(61,1,1) = {" +(53,1,1) = {" aa aa aa @@ -16852,6 +17950,10 @@ ab ad ad ad +ad +ad +ad +ad av av av @@ -16862,10 +17964,17 @@ av av av av +ad av av av +ad av +ad +ad +ad +ad +ad av av av @@ -16886,9 +17995,13 @@ av av av av -av -av -av +RU +BT +Ks +aE +aE +Td +RU av av av @@ -16910,15 +18023,13 @@ av av av av -av -av +ad av av av av ad av -ad av av av @@ -16931,20 +18042,16 @@ av av av av +ao av av ad ad -av -av -av -av -av -av -ad ad +ao av av +an av av av @@ -16957,17 +18064,8 @@ av av av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad +uz +an av ad ab @@ -17000,6 +18098,85 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +VE +Lq +fR +hv +iI +iI +zr +fR +PL +wR +fR +NG +Ch +zZ +bO +iI +kq +iI +iI +iI +zr +fR +wR +fR +NG +dP +EM +fR +fR +iI +fR +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +ka +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(54,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa aa aa aa @@ -17021,30 +18198,139 @@ aa aa aa aa -bn -cg -bM -bj -bj -bN -ca -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bN -ca -ci -bf -bL +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +ad +ad +mc +av +av +ad +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +RU +ah +Uv +aJ +ah +Uv +RU +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +HJ +ad +ad +ad +av +av +av +av +av +av +av +av +Rl +av +av +uz +av +av +av +av +uz +av +ao +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -17069,8 +18355,77 @@ aa aa aa aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +JZ +JZ +Su +JZ +if +mY +ML +NP +Fl +ML +NJ +KS +uE +EZ +ML +if +XG +RV +JZ +if +ML +NP +Fl +ML +KS +uE +EZ +qb +if +if +mP +JZ +Fl +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +du +du +du +du +LZ "} -(62,1,1) = {" +(55,1,1) = {" aa aa aa @@ -17107,6 +18462,8 @@ ab ab ab ad +ad +ad av av av @@ -17120,8 +18477,19 @@ av av av av +ad +ad +ad +ad +ad +ad av av +ad +ad +ad +ad +ad av av av @@ -17141,12 +18509,15 @@ av av av av +RU +Yj +ah +nF +ah +wx +RU +uz av -ad -av -av -av -ad av av av @@ -17164,9 +18535,7 @@ av av av av -av -av -av +ad av av av @@ -17183,6 +18552,7 @@ av av av av +ao av av av @@ -17190,16 +18560,9 @@ av av av av -av -ad -ad -av -av -av ad -av -av ad +HJ av av av @@ -17211,20 +18574,12 @@ av av av av +uz +uz av av av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad av ad ab @@ -17241,93 +18596,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bn -cg -ci -bf -bL -bj -cc -cE -cH -cH -cH -cH -cI -bj -bj -bj -bj -bj -bj -bj -cc -by -bj -bz -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +fA +YG +XL +Gj +vr +xD +YG +zk +tM +zk +fA +xZ +tM +zk +fA +xZ +tM +fA +zk +Qu +vr +ji +ji +XL +vr +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(63,1,1) = {" +(56,1,1) = {" aa aa aa @@ -17364,6 +18719,8 @@ ab ab ab ad +ad +ad av av av @@ -17377,7 +18734,20 @@ av av av av +ad +ad av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad av av av @@ -17396,10 +18766,15 @@ av av av av +RU +RU +RU +Ds +RU +RU +RU av av -ad -av av av av @@ -17425,15 +18800,7 @@ av av av av -av -av -av -av -av -av -av -av -av +ad av av av @@ -17451,140 +18818,128 @@ av av av ad -av -av -av -av -av ad av +an av av av +uz av +uz av av av av +uz av av +an av av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bq -bw -by -bj -bz -bf -bX -cD -dr -dr -dr -dr -dr -bj -bj -bj -bj -bj -bj -bf -bX -by -bz -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +HJ +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +YG +xI +VR +vr +vr +kH +sZ +wO +kH +Gj +vr +ji +wO +ji +Gj +ji +vr +WQ +vr +vr +ji +vr +vr +ji +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(64,1,1) = {" +(57,1,1) = {" aa aa aa @@ -17621,7 +18976,11 @@ ab ab ab ad +ad +ad av +ad +ad av av av @@ -17636,6 +18995,13 @@ av av av av +ad +ad +ad +ad +ad +ad +ad av av av @@ -17651,19 +19017,27 @@ av av av av +ad av +ad +ad av av +RU +In +In +ah +ah +ah +RU av av av av +an av av av -ad -av -av av av av @@ -17690,13 +19064,6 @@ av av av av -ad -av -av -av -av -av -av av av av @@ -17714,8 +19081,10 @@ av av av av +uz av av +uz av av av @@ -17723,23 +19092,9 @@ av av av av +uz av av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -av ad ab ab @@ -17755,93 +19110,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bG -by -bz -bj -bj -bT -cD -dr -dr -dr -dr -dr -bj -ep -bj -es -es -bj -bj -bT -by -bL -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +YG +lX +ji +vr +Gj +Gj +vr +vr +ji +ji +OR +vr +ji +vr +OY +vr +LV +VR +vr +vr +vr +qx +vr +sZ +vr +qx +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(65,1,1) = {" +(58,1,1) = {" aa aa aa @@ -17878,6 +19233,11 @@ ab ab ab ad +ad +ad +ad +ad +ad av av av @@ -17893,29 +19253,11 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av +ad +ad +ad +ad +ad av av av @@ -17937,8 +19279,17 @@ av av av av +ad +RU +In +Uv +ah +iC +Uv +RU av av +LB av av av @@ -17949,10 +19300,10 @@ av av av av +ad av av -av -av +ad av av av @@ -17975,28 +19326,32 @@ av av av av +uz +uz av av av +HJ +ad +ao av +uz av av +uz av av av av av +an av av av av av av -ad -ad -ad -aZ -av +an ad ab ab @@ -18012,93 +19367,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bp -cg -by -bL -bj -bN -bV -cD -dr -dr -dW -dr -dr -dL -bj -es -eu -eu -bj -bN -bV -bB -bj -bz -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +tN +YG +BY +vr +ji +vr +ji +LV +vr +ji +yJ +LV +Gj +vr +ji +vr +yJ +ji +Hf +ji +vr +ji +vr +sZ +vr +qx +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(66,1,1) = {" +(59,1,1) = {" aa aa aa @@ -18135,6 +19490,11 @@ ab ab ab ad +ad +ad +ad +ad +ad av av av @@ -18150,6 +19510,11 @@ av av av av +ad +ad +ad +ad +ad av av av @@ -18157,11 +19522,13 @@ av av av av +aq av av av av av +ad av av av @@ -18170,9 +19537,13 @@ av av av av -av -av -av +RU +ah +iC +Cz +ah +ah +RU av av av @@ -18201,34 +19572,11 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad ad av av av av -ad -av -av av av av @@ -18241,121 +19589,128 @@ av av av av +ad av av av +uz av av av av av +an av av -ad -ad av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -bB -bj -bz -bj -bW -cD -dr -dr -dr -dr -dr -dX -bj -es -ev -eC -bj -bj -bW -bB -bj -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +HJ +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +oH +vk +Sz +vr +vr +vr +OY +XU +FT +FT +FT +ji +ji +ji +vr +sZ +vr +sZ +vr +ji +vr +sZ +vr +ji +sZ +vr +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(67,1,1) = {" +(60,1,1) = {" aa aa aa @@ -18392,6 +19747,10 @@ ab ab ab ad +ad +ad +ad +ad av av av @@ -18409,9 +19768,9 @@ av av av av -av -av -av +ad +ad +ad av av av @@ -18425,14 +19784,6 @@ av av av ad -av -av -av -av -av -av -av -av ad av av @@ -18443,11 +19794,18 @@ av av av av +RU +ah +ah +ah +ah +ah +RU av +uz av av -av -av +uz av av av @@ -18471,46 +19829,43 @@ av av av av -ad -ad -ad -ad av ad -ad -ad -ad -av -av -av -av -av -av -av av av av av av av +uz av av av av +uz av av +ao +ad av av av av +uz +uz +uz +uz av av +uz +uz av av av +uz av -ad -ad +an +ao ad ab ab @@ -18526,93 +19881,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -bB -bj -bD -bj -cc -cD -dr -dr -dr -dr -dr -bj -bj -bj -es -es -bj -bj -cc -bj -bz -bz -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +YG +YG +XL +yJ +vr +sj +vk +vk +yq +YG +qx +OY +vr +FT +JM +Aa +kQ +ji +Hf +ji +fA +vr +sZ +vr +vr +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(68,1,1) = {" +(61,1,1) = {" aa aa aa @@ -18649,6 +20004,8 @@ ab ab ab ad +ad +ad av av av @@ -18681,34 +20038,6 @@ av av av av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av av av av @@ -18719,26 +20048,30 @@ av av av av -ad av av av +RU +vq +ah +Cz +ah +ah +RU av av av av -ad av av +LB av av av -ad av av ad av -av ad av av @@ -18750,116 +20083,53 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bg -bH -bj -bz -bz -bL -bX -cF -cH -cH -cH -cH -cI -bj -bj -bj -ep -bj -bj -bL -bX -bj -bz -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +av +av +av +av +ad +ad +av +av +av +av +av +av +ad +ad +av +uz +uz +av +av +av +ad +HJ +av +av +av +av +uz +av +uz +av +uz +uz +av +av +an +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -18868,8 +20138,93 @@ aa aa aa aa +YG +YG +YG +BY +vr +ji +Lz +qo +fA +Aa +qo +le +Pp +rA +zw +qo +vk +Ob +sZ +vr +vk +eS +sZ +vr +sZ +sZ +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(69,1,1) = {" +(62,1,1) = {" aa aa aa @@ -18941,22 +20296,29 @@ av av av av +ad av av av +ad av av av av av -ad -av -av av av +RU +Co +ah +iC +ah +ah +gB av av av +uz av av av @@ -18965,13 +20327,13 @@ av av av av +ad av av av av av av -ad av av av @@ -18988,18 +20350,18 @@ ad av av av -av ad av av -av -av ad av +uz av av av av +ad +ad av av av @@ -19009,22 +20371,15 @@ av av av av +uz +uz av av av +uz av av -av -av -av -av -av -av -av -av -av -av -av +HJ ad ab ab @@ -19040,93 +20395,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bj -bj -bj -bz -bj -bR -cb -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bj -bR -cb -bC -bj -bz -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +vk +BY +VR +vr +OR +eS +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +kQ +UK +wJ +qx +vr +sZ +BY +VR +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +NT +NT +NT +NT +NT +NT +NT +km +km +km +km +km +fl +Bi +Bi +Bi +Bi +km +km +km +km +km +km +km "} -(70,1,1) = {" +(63,1,1) = {" aa aa aa @@ -19173,16 +20528,13 @@ av av av av +aq av av av av av av -ad -av -av -av av av av @@ -19195,12 +20547,12 @@ av av av av -ad av av av av av +ad av av av @@ -19213,6 +20565,13 @@ av av av av +RU +vq +ah +Cz +ah +ah +aV av av av @@ -19221,6 +20580,7 @@ av av av av +an av av av @@ -19250,38 +20610,33 @@ av av av av -av -ad ad av +uz av +an av av +HJ +ad +xQ +xQ +ad +ad +ao av +uz av av av av +uz av +uz av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad ad ab ab @@ -19297,93 +20652,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bj -bl -bC -bj -bz -bf -bX -cE -cH -cH -cH -cH -cH -cH -cH -cI +YG +YG +qx +vr +sZ +vr +QJ +vr +vr +vr +HY +PW +PW +pD +pD +PW +PW +YS +vr +vr +QJ +BY +vr +Ys +qx +vr +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +PE +NT +NT cL +NT cL -dD -bf -bX -cg -by -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +cL +NT +NT +NT +to +km +km +km +km +km +km +Bi +km +km +km +km +km +km +km +km +km "} -(71,1,1) = {" +(64,1,1) = {" aa aa aa @@ -19435,16 +20790,11 @@ av av av av -ad -av -ad -av -av -av av av av av +ao av av av @@ -19452,7 +20802,6 @@ av av av av -ad av av av @@ -19466,13 +20815,22 @@ av av av av +ad av av av av av av +RU +ah +ah +ah +ah +RN +aV av +LB av av av @@ -19492,24 +20850,19 @@ av av av av -ad -av -av -av av -ad av av av av av -ad av av av av av ad +ad av av av @@ -19520,25 +20873,27 @@ av av av av +uz av +xQ +xQ +xQ +xQ +ad +ad av av av av +uz av av av av av av -av -av -av -av -av -av -av -av +ao +ad ad ab ab @@ -19554,93 +20909,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -br -bw -cg -by -bj -bN -bT -cD -dI -dO -cD -bj -dL -bj +YG +YG +BY +vr +Ys +wO +tM +vr +vr +vr +ti +oy +Ns +oG +oG +EX +Cv +lH +vr +wO +tM +BY +sZ +Gj +BY +vr +sZ +km +km +km +km +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +km +km +km +NT +NT +NT +NT +eC +NT +NT +NT +NT +to +NT cL -bj -bj -bj -cD -bN -bT -cg -bB -bL -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +NT +NT +to +FB +NT +to +km +km +Bi +Bi +Bi +km +km +km +km +km +km +km "} -(72,1,1) = {" +(65,1,1) = {" aa aa aa @@ -19694,13 +21049,6 @@ av av av av -ad -av -av -av -av -av -av av av av @@ -19710,10 +21058,6 @@ av av av av -ad -av -av -av av av av @@ -19735,6 +21079,13 @@ av av av av +RU +ah +Uv +Cz +ah +ah +RU av av av @@ -19753,11 +21104,6 @@ av av av av -ad -av -av -ad -av av av av @@ -19766,13 +21112,13 @@ av av av av -ad av av av av av av +ad av av av @@ -19789,13 +21135,22 @@ av av av av +uz +HJ +ad +ad +HJ av av av av av +uz av av +ao +ad +ad ad ab ab @@ -19811,93 +21166,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bX -cg -bB -bL -bj -ca -cD -dK -bj -cD -bj -bj -bj -dD -bj -bj -bj -cD -bj -ca -cg -ci -bE -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +NT +vk +BY +sZ +Gj +vr +xZ +vr +vr +vr +Va +ZU +Wq +oG +oG +oG +Cv +tr +vr +vr +xZ +BY +ji +vr +BY +sZ +ji +km +km +km +km +fj +fj +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +fj +fj +km +km +km +cL +NT +NT +NT +eC +ym +NT +eC +NT +qT +JF +NT +NT +cL +cL +NT +NT +eC +BU +km +km +km +Bi +km +km +km +km +km +km +km "} -(73,1,1) = {" +(66,1,1) = {" aa aa aa @@ -19932,26 +21287,7 @@ ab ab ab ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av +ab ad av av @@ -19967,17 +21303,6 @@ av av av av -ad -av -av -av -av -av -av -av -av -av -av av av av @@ -20001,7 +21326,6 @@ av av av av -ad av av av @@ -20012,11 +21336,20 @@ av av av av +RU +kT +ah +ah +ah +ah +RU av +ad av av av av +LB av av av @@ -20038,21 +21371,43 @@ av av av av +ad +ad +ad +ad +ad av av av av +ad +uz +uz av av av +uz av av +uz av +uz av av +ao +ad +ad +ad +ad +HJ +ao av av av +HJ +ad +ad +ad ad ab ab @@ -20068,93 +21423,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -cg -ci -bE -bj -bj -cD -dJ -bj -cD -ed -bj -bj -dD -bj -en -bj -cD -bj -bj -bx -bz -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +fA +BY +ji +vr +WQ +fA +vr +vr +vr +ti +GZ +oG +oG +oG +qj +Cv +vY +vr +WQ +fA +XL +VR +ji +BY +ji +VR +km +km +km +km +fj +fj +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +fj +fj +km +km +eC +NT +NT +cL +eC +NT +eC +NT +NT +eN +NT +JF +JF +NT +NT +to +NT +BU +NT +eC +eC +km +km +Bi +km +km +km +km +km +km +km "} -(74,1,1) = {" +(67,1,1) = {" aa aa aa @@ -20210,8 +21565,6 @@ av av av av -ad -av av av av @@ -20234,12 +21587,21 @@ av av av av +ad av av av av av +RU +ah +ah +Cz +ah +ah +RU av +ad av av av @@ -20248,11 +21610,7 @@ av av av av -av -av -av -av -av +ad av av av @@ -20269,47 +21627,44 @@ av av ad ad -av -av -av -av ad -av -av -av -av -av ad av +ad +ad +ad +ad av av av av +uz +HJ av av +ao av av av +uz av av av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +HJ +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad ad ab ab @@ -20325,93 +21680,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -bx -bz -bj -bL -bL -cD -bj -bj -cD -ee -bj -bj +ix +BM +XL +VR +ji +vr +zk +vr +vr +vr +UL +PW +PW +pD +pD +PW +PW +RQ +vr +vr +zk +FF +vr +vr +XL +VR +vr +km +km +km +km +fj +rv +Kb +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Kb +rv +fj +km +NT +eC +NT +NT +NT +NT +NT cL -bj -bj -bj -cD -bL -bL -by -bj -bz -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +cL +NT +cL +NT +NT +eC +JF +qT +oz +eC +eC +NT +eC +eC +to +km +Bi +km +km +km +km +km +km +km "} -(75,1,1) = {" +(68,1,1) = {" aa aa aa @@ -20480,20 +21835,6 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av ad av av @@ -20510,16 +21851,11 @@ av av av av -av -av -av -av -av -av -av -av -av -av +RU +iC +ah +ah +RU av av ad @@ -20537,6 +21873,7 @@ av av av av +ad av av av @@ -20544,29 +21881,47 @@ av av av av +ad av av av av av +ad av av +ad av av +ad av +uz av av av av av av +uz av +uz +an +uz av +uz av av av av -av +HJ +ad +ad +ad +ad +ad +ad +ad +ad ad ab ab @@ -20582,93 +21937,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bs -cg -by -bj -bz -bj -bz -cD -cI -cL -cF -cH -cH +rx +NT +FF +vr +vr +WQ +xD +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +WQ +xD +xI +wO +Gj +FF +vr +vr +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op cH -cH -cI -bj -bj -cD -bj -bz -bJ -bj -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +fj +fj +fj +km +NT +eC +NT +cL +NT +PE +NT +NT +NT +PE +NT +to +eN +eC +NT +qT +qT +eC +eN +NT +NT +FB +eC +km +Bi +km +km +km +km +km +km +km "} -(76,1,1) = {" +(69,1,1) = {" aa aa aa @@ -20718,32 +22073,6 @@ av av av av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av av av av @@ -20753,6 +22082,7 @@ av av av av +aq av av av @@ -20778,17 +22108,21 @@ av av av av +RU +ah +ah +ah +RU av av ad av +LB av av av av av -ad -av av av ad @@ -20803,119 +22137,55 @@ av av av av +ad +ad av av av av +ad av av av av +ad av av av av +uz av av av av +uz av av av av av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bX -cg -bJ -bj -bD -bj -bz -cD -dL -bj -bj -cL -bj -bj -bj -bj -bj -bj -cD -bj -bz -bj -bD -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +av +av +av +av +av +av +av +av +ao +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -20924,8 +22194,93 @@ aa aa aa aa +PE +NT +Ne +vr +WQ +qU +fA +vr +vr +WQ +vr +vr +vr +vr +vr +vr +vr +vr +vr +wO +eS +YG +XL +vr +YG +vr +kH +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +Op +Op +Op +Ou +Op +Op +Op +Op +Op +Op +Op +Kb +rv +fj +km +NT +NT +NT +NT +NT +cL +NT +eN +Ul +NT +vr +vr +vr +NT +vr +NT +qT +NT +NT +to +fa +NT +eC +km +Bi +km +km +km +km +km +km +km "} -(77,1,1) = {" +(70,1,1) = {" aa aa aa @@ -20966,6 +22321,7 @@ av av av av +ao av av av @@ -20977,6 +22333,7 @@ av av av av +ad av av av @@ -20992,6 +22349,7 @@ av av av av +ad av av av @@ -21008,10 +22366,13 @@ av av av av +RU +ah +RU av -ad av av +ad av av av @@ -21031,36 +22392,24 @@ av av av av -ad av av av av -ad av av ad av -aq -av -av -av -av -av -av -av -av -av -av -av -av av av av av av +ad +ad av av +uz av av av @@ -21070,6 +22419,7 @@ av av av av +uz av av av @@ -21081,6 +22431,11 @@ av av av av +HJ +ad +ad +ad +ad ad ab ab @@ -21096,93 +22451,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bd -bm -bj -bD -bj -bK -bj -cD -bj -bj -bj -cL -bj -en -bj -bj -bj -bj -cD -bK -bj -bJ -bj -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +NT +NT +to +WQ +vr +vr +fA +WQ +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +Gj +xD +YG +xI +vr +fA +vr +xx +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +NT +NT +eC +NT +oU +rS +NT +NT +NT +vr +vr +vr +ji +vr +vr +vr +vr +vr +ji +NT +NT +oz +NT +eC +FB +Bi +km +km +km +km +km +km +km "} -(78,1,1) = {" +(71,1,1) = {" aa aa aa @@ -21234,7 +22589,9 @@ av av av av +ad av +ad av av av @@ -21249,6 +22606,7 @@ av av av av +ad av av av @@ -21265,8 +22623,13 @@ av av av av +RU +ah +RU av av +ad +ad av av av @@ -21277,23 +22640,6 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av ad av av @@ -21303,19 +22649,21 @@ av ad av av -ad av av +ad av av av av av +ad av av av av av +ad av av av @@ -21323,6 +22671,7 @@ av av av av +uz av av av @@ -21330,15 +22679,21 @@ av av av av +uz +uz av av av +uz av av av av av ad +ad +ad +ad ab ab ab @@ -21353,93 +22708,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bX -cg -bJ -bj -bD -bj -bz -cD -dL -bj -bj +NT +PE +NT +Ne +WQ +qU +fA +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +YG +lX +ji +vr +YG +vr +kH +km +km +km +km +fj +rv +Kb +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Kb +rv +fj +NT +NT +NT +bg +NT +NT +eC cL -bj -bj -bj -bj -bj -bj -cD -bj -bz -bj -bD -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +eC +vr +vr +vr +vr +vr +vr +vr +OY +vr +to +cL +eN +eC +NT +ST +FB +km +km +km +km +km +km +km +km "} -(79,1,1) = {" +(72,1,1) = {" aa aa aa @@ -21488,10 +22843,6 @@ av av av av -ad -av -ad -av av av av @@ -21513,7 +22864,7 @@ av av av av -av +ad av av av @@ -21530,8 +22881,11 @@ av av av av +RU av av +ad +ad av av av @@ -21556,9 +22910,6 @@ av ad av av -av -av -av ad av av @@ -21569,6 +22920,7 @@ av av av av +ad av av av @@ -21579,10 +22931,13 @@ av av av av +uz av av +uz av av +uz av av av @@ -21593,8 +22948,8 @@ av av av av -av -av +ad +ad ad ab ab @@ -21610,93 +22965,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bd -bm -bj -bD -bj -bK -bD -cD -cH -cI -cL -cE -cH -cH +rx +YG +Qh +wO +Gj +vr +xZ +LL +Zo +Zo +Zo +Zo +YR +vr +vr +vr +vr +vr +vr +vr +xZ +BY +vr +xx +vr +kH +nU +km +km +km +km +fj +fj +fj +QY +Op +Op +fj +Op +Op +fj +fj +fj +Op +Op +fj +Op +Op cH -cI -bj -bj -cD -bK -bD -bj -bz -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +fj +fj +fj +NT +nE +NT +NT +Hr +NT +NT +ci +wO +OY +vr +Nr +CM +CM +nT +vr +vr +vr +NT +NT +NT +eC +to +NT +NT +km +km +km +km +km +km +km +km "} -(80,1,1) = {" +(73,1,1) = {" aa aa aa @@ -21751,6 +23106,7 @@ ad av av av +ad av av av @@ -21765,6 +23121,7 @@ av av av av +ad av av av @@ -21781,8 +23138,11 @@ av av av av +RU av ad +ad +ad av av av @@ -21795,6 +23155,7 @@ av av av av +ad av av av @@ -21810,11 +23171,8 @@ av av av av -ad -ad av av -ad av av av @@ -21830,29 +23188,26 @@ av av av av +ao av av av av +HJ av av +ao av +uz av av +uz av av av +ao ad ad -av -av -av -av -av -av -av -av -ad ab ab ab @@ -21867,6 +23222,93 @@ aa aa aa aa +Fi +to +BY +vr +ji +wO +fA +Jj +CM +CM +CM +CM +CM +vr +vr +vr +vr +vr +vr +wO +fA +BY +ji +xx +kH +Sc +Sc +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +fj +fj +fj +fj +fj +fj +fj +Op +Op +Op +Op +Kb +rv +fj +NT +NT +DG +sv +NT +oU +NT +NT +vr +vr +vr +Nr +CM +nT +nT +AM +vr +vr +NT +NT +NT +JF +to +NT +NT +NT +km +km +km +km +km +km +km +"} +(74,1,1) = {" aa aa aa @@ -21896,6 +23338,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +uz +uz +av +uz +av +av +av +av +av +av +av +uz +av +uz +av +av +av +av +av +av +av +av +av +av +av +ao +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -21904,56 +23479,93 @@ aa aa aa aa -bc -bt -bj -bz -bD -bj -bS -cD -dN -bj -bj -cD -ej -bj -bj -cL -bj -bj -eE -bf -bX -bj -bD -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +Aa +BY +ji +vr +vr +eS +Jj +CM +CM +CM +CM +CM +vr +vR +vr +Pb +Pb +vr +vr +eS +BY +Gj +vr +lS +Cf +Sc +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +fj +bx +mV +bx +mV +bx +fj +Op +Op +Op +cH +fj +fj +fj +sv +cL +NT +NT +nE +NT +eC +NT +vr +vr +vr +Nr +CM +VO +AM +AM +vr +vr +vr +eC +NT +JF +NT +NT +NT +FB +km +km +km +km +km +km +km "} -(81,1,1) = {" +(75,1,1) = {" aa aa aa @@ -22001,9 +23613,7 @@ av av av av -ad av -ad av av av @@ -22016,13 +23626,11 @@ av av av av -ad av -ad av -ad -ad -ad +av +av +av av av av @@ -22070,8 +23678,6 @@ av av ad av -ad -ad av av av @@ -22089,6 +23695,13 @@ av av av av +uz +av +av +av +uz +av +av av av av @@ -22096,17 +23709,16 @@ av av av av -ad -ad -ad -ad -ad av +uz av av av av av +an +av +av av av ad @@ -22124,93 +23736,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -be -bl -bD -bE -bj -bz -bf -cD -dM -dQ -cT -cD -bj -dL -bj +QN +YG +BY +Gj +vr +WQ +tM +Jj +CM +CM +op +CM +CM +pd +vr +Pb +gD +uN +vr +WQ +tM +XL +xx +vr +lS +xR +Sc +km +km +km +km +fj +rv +Kb +Op +Op +Op +fj +fj +fJ +Op +Xa +Op +Op +fj +fj +Op +Op +Op +Op +Op +Fd +NT +gn +NT cL -bj -bj -eD -bN -bT -bD -bj -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +bg +iF +eC +rS +zX +vr +vr +Nr +CM +nT +nT +AM +vr +vr +vr +ji +OF +NT +NT +cL +NT +to +km +km +km +km +km +km +km "} -(82,1,1) = {" +(76,1,1) = {" aa aa aa @@ -22260,8 +23872,6 @@ av av av av -av -av ad av av @@ -22269,9 +23879,6 @@ av av av av -av -ad -av ad av av @@ -22280,7 +23887,6 @@ av av av av -ad av av av @@ -22288,6 +23894,7 @@ av av av av +ad av av av @@ -22314,11 +23921,13 @@ av av av av +ao av av av av av +ad av av av @@ -22326,17 +23935,17 @@ av av av ad -ad -ad -ad av av av av av av +ad +av av av +ad av av av @@ -22347,18 +23956,21 @@ av av av av +uz +uz av av av av av +uz +uz av -ad av av av -ad av +uz av av av @@ -22381,93 +23993,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -bw -by -bz -bN -bf -bT -cF -cH -cH -cH -cD -cH -cH -cH -cH -cH -cH -cI -bj -ca -bj -bD -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +YG +XL +vr +ji +vr +zk +Jj +CM +CM +CM +CM +CM +vr +vr +Pb +jE +pp +vr +vr +zk +XL +vr +vr +lS +Sc +Sc +km +km +km +km +fj +fj +fj +QY +Op +fj +fj +fj +Lu +Xa +fd +Xa +Op +Kb +Kb +Kb +Op +Op +Op +Op +Fd +NT +NT +Hr +NT +NT +NT +NT +NT +NT +WQ +vr +Nr +CM +CM +nT +vr +OY +vr +vr +vr +eC +eC +to +NT +NT +NT +km +km +km +km +km +km +km "} -(83,1,1) = {" +(77,1,1) = {" aa aa aa @@ -22515,11 +24127,6 @@ av av av av -ad -av -av -av -av av av av @@ -22538,8 +24145,6 @@ av av av av -ad -av av av av @@ -22558,6 +24163,7 @@ av av av av +ad av av av @@ -22579,13 +24185,17 @@ av av av av +ad av av av av +ad av av +ad av +aq av av av @@ -22599,26 +24209,28 @@ av av av av +uz av av av av av av +uz +uz +uz av av av av av -ad -ad av av -ad av -ad av av +uz +uz av av av @@ -22638,93 +24250,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -cg -cg -bw -cu -dh -cg -bw -cg -bw -by -bS -bj -bF -dz -dB -dh -cg -bB -bj -bF -bj -bD -bj -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +YG +XL +vr +sZ +vr +xZ +Jj +CM +CM +CM +CM +CM +vr +vr +vr +Pb +Pb +vr +vr +xZ +vr +ji +vr +lS +Sc +Sc +km +km +km +km +fj +rv +Kb +Op +Op +Op +fj +fj +ky +Op +Xa +Op +Op +fj +fj +Op +Op +Op +Op +Op +Fd +NT +sv +NT +gn +NT +NT +eC +cL +vr +vr +vr +OY +vr +vr +vr +OY +vr +vr +WQ +NT +NT +NT +to +NT +NT +km +km +km +km +km +km +km +km "} -(84,1,1) = {" +(78,1,1) = {" aa aa aa @@ -22772,18 +24384,17 @@ av av av av -ad av av av av av av +ao av av av av -ad av av av @@ -22795,8 +24406,6 @@ av av av av -ad -ad av av av @@ -22834,15 +24443,21 @@ av av av av +ad +ad +ad av av +ad av av av av av +ad av av +ad av av av @@ -22866,15 +24481,12 @@ av av av av +ao av -ad -ad av av av av -ad -ad av av av @@ -22895,6 +24507,93 @@ aa aa aa aa +IZ +Bv +vr +ji +ji +Gj +fA +HN +Zo +Zo +Zo +Zo +YR +vr +vr +vr +vR +vr +vr +Gj +fA +vr +ji +vr +lS +lS +Sc +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +fj +bx +mV +bx +mV +bx +fj +Op +Op +Op +cH +fj +fj +fj +DG +NT +eC +NT +NT +NT +eC +NT +NT +vr +ji +vr +vr +vr +vr +vr +vr +vr +qT +NT +PE +PE +NT +NT +NT +km +km +km +km +km +km +km +km +"} +(79,1,1) = {" aa aa aa @@ -22924,6 +24623,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -22932,56 +24764,93 @@ aa aa aa aa -cg -bw -bx -bH -bg -bH -bg -bH -cG -bj -bz -bR -bj -bz -bg -bg -dT -bD -bj -bE -bj -bz -bD -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +vr +vr +vr +ji +vr +OY +QJ +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +OY +QJ +UK +vr +vr +vr +lS +lS +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +fj +fj +fj +fj +fj +fj +fj +Op +Op +Op +Op +Kb +rv +fj +NT +cL +cL +NT +Hr +eC +NT +eC +NT +WQ +vr +vr +NT +NT +wO +NT +ji +vr +qT +qT +eN +PE +NT +BU +NT +km +km +km +km +km +km +km +km "} -(85,1,1) = {" +(80,1,1) = {" aa aa aa @@ -23029,21 +24898,17 @@ av av av av -ad -av -av av av av +ad av av av -ad av av av av -ad av av av @@ -23052,8 +24917,6 @@ av av av av -ad -ad av av av @@ -23063,6 +24926,7 @@ av av av av +rX av av av @@ -23072,6 +24936,7 @@ av av av av +ad av av av @@ -23087,6 +24952,7 @@ av av av av +an av av av @@ -23098,8 +24964,11 @@ av av av av +ad +ad av av +ad av av av @@ -23113,8 +24982,12 @@ av av av av +uz +uz av av +uz +uz av av av @@ -23130,10 +25003,6 @@ av av av av -ad -ad -av -av av av av @@ -23152,93 +25021,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bX -cg -bJ -bj -bD -bj -bz -bz -bj -bj -bD -bj -bN -bQ -bO -bz -bz -bj -bD -bF -bj -by -bz -bD -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +vr +FT +UK +vr +ji +wO +fA +LL +Zo +Zo +Zo +Zo +Zo +Zo +Zo +YR +OJ +OJ +kE +wO +fA +YG +BY +vr +kH +vr +lS +km +km +km +km +fj +fj +fj +QY +Op +Op +fj +Op +Op +fj +fj +fj +Op +Op +fj +Op +Op +cH +fj +fj +fj +cL +gn +NT +nE +NT +oU +NT +NT +NT +NT +NT +eC +eC +NT +NT +NT +NT +NT +AI +By +qT +NT +PE +NT +NT +km +km +km +km +km +km +km +km "} -(86,1,1) = {" +(81,1,1) = {" aa aa aa @@ -23286,10 +25155,8 @@ av av av av -av ad av -av ad av av @@ -23303,15 +25170,13 @@ av av av av -av -av -av -av -aq -av +ad av ad av +ad +ad +ad av av av @@ -23320,6 +25185,7 @@ av av av av +ao av av av @@ -23328,6 +25194,7 @@ av av av av +ad av av av @@ -23355,7 +25222,10 @@ av av av av +ad av +ad +ad av av av @@ -23382,12 +25252,11 @@ av av ad ad -av -av -av -av ad ad +ad +av +av av av av @@ -23409,93 +25278,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bd -bm -bj -bD -bj -bK -bD -bj -bz -bQ -bj -bz -bN -bF -bj -bK -bj -bz -bj -bz -bj -bB -by -bj -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +oH +vk +YG +BY +vr +WQ +eS +Jj +Cj +Pm +Jj +vr +pd +vr +OJ +vr +vr +vr +Jj +WQ +eS +YG +XL +Gj +YG +vr +vr +km +km +km +km +fj +rv +Kb +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Kb +rv +fj +NT +NT +NT +NT +NT +NT +NT +PE +NT +NT +to +eC +NT +NT +NT +NT +NT +NT +qT +qT +qT +BU +NT +BU +km +km +km +km +km +km +km +km +km "} -(87,1,1) = {" +(82,1,1) = {" aa aa aa @@ -23542,34 +25411,30 @@ av av av av -ad av -ad av av -ad av av ad av -ad av -ad av av av av av +ad av +ad av av av av av -ad -ad av av +ad av av av @@ -23614,6 +25479,10 @@ av av av av +ad +ad +ad +ad av av av @@ -23639,11 +25508,11 @@ av av av ad -ad +av +av av ad -ad -ad +av av av av @@ -23666,93 +25535,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -bu -bt -bj -bz -bD -bj -bS -bj -bz -bj -bF -bj -bz -bz -bj -bz -bj -bz -bj -bj -bj -ci -bB -bL -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +fA +YG +XL +Gj +vr +xD +Jj +VP +vr +Jj +vr +vr +vr +kE +vr +vr +vr +Jj +vr +xD +YG +xI +VR +YG +kH +vr +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +NT +nE +NT +eC +bg +NT +NT +NT +NT +eN +NT +NT +NT +PE +qT +cL +NT +eC +eC +qT +NT +NT +BU +NT +km +km +km +km +km +km +km +km +km "} -(88,1,1) = {" +(83,1,1) = {" aa aa aa @@ -23793,16 +25662,12 @@ av av av av -ad -ad -ad -ad -ad av -ad av -ad -ad +av +av +av +av av ad av @@ -23813,9 +25678,7 @@ av av av av -ad av -ad av av av @@ -23823,7 +25686,12 @@ av av av av -ad +av +av +av +av +av +av ad av av @@ -23898,10 +25766,11 @@ av av ad ad -ad av av +ad av +ad av av av @@ -23923,6 +25792,93 @@ aa aa aa aa +YG +YG +YG +xI +VR +vr +vr +Jj +sp +pd +Jj +sN +vr +vr +kE +vr +Jd +vr +Jj +vr +YG +lX +ji +vr +YG +vr +vr +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +Op +Op +Op +Ou +Op +Op +Op +Op +Op +Op +Op +Kb +rv +fj +NT +NT +eC +NT +NT +NT +eC +cL +eC +eC +NT +NT +NT +to +NT +PE +OF +NT +to +NT +to +NT +NT +NT +km +km +km +km +km +km +km +km +km +"} +(84,1,1) = {" aa aa aa @@ -23952,6 +25908,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +HJ +av +av +av +ad +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -23960,56 +26049,93 @@ aa aa aa aa -bv -bI -ct -cv -bj -bw -cx -bj -bG -bT -ch -cg -bG -cg -cx -bf -cg -dc -bL -cg -cg -cg -ci -bE -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +YG +lX +ji +vr +Gj +Gj +Jj +Bn +vr +Jj +ZG +vr +vr +OJ +vr +vr +vr +Jj +Gj +Aa +BY +vr +ji +lX +vr +kH +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +bg +NT +NT +NT +NT +NT +NT +NT +eC +JF +qT +JF +eC +NT +NT +NT +NT +NT +NT +eC +eC +NT +NT +NT +km +km +km +km +km +km +km +km +km "} -(89,1,1) = {" +(85,1,1) = {" aa aa aa @@ -24054,11 +26180,6 @@ av av av av -ad -ad -av -av -av av av av @@ -24076,15 +26197,17 @@ av av av av +ad av av av av av -ad av av av +ad +ad av av av @@ -24108,11 +26231,9 @@ av av av av -ad -ad -ad av -ad +an +av av av av @@ -24156,10 +26277,15 @@ av av av av +ad +ad av +sF av av av +ad +ad av av av @@ -24180,93 +26306,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +tN +YG +BY +vr +ji +vr +ji +Jj +YR +OJ +HN +Zo +Zo +Zo +Zo +YR +vr +vr +Jj +vr +vk +qx +vr +sZ +BY +vr +kH +km +km +km +km +fj +rv +Kb +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Kb +rv +fj +NT +NT +NT +NT +NT +NT +NT +NT +eN +qT +NT +eC +NT +NT +to +eN +eN +cL +NT +eC +cL +NT +NT +to +km +km +km +km +km +km +km +km +km "} -(90,1,1) = {" +(86,1,1) = {" aa aa aa @@ -24314,13 +26440,11 @@ av av av av -ad av ad av -ad -ad av +ad av av av @@ -24330,18 +26454,17 @@ av av av av -ad av av av av av av -ad -ad av +aq av av +ad av av av @@ -24364,14 +26487,12 @@ av av av av -ad av av av av av av -ad av av av @@ -24413,9 +26534,14 @@ av av av av +ad +ad av +HJ av av +ad +ad av av av @@ -24437,93 +26563,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +fA +YG +qx +vr +sZ +vr +ji +Jj +pd +vr +vr +OJ +vr +vr +vr +vr +vr +vr +Jj +vr +vk +vr +sZ +vr +qx +vr +vr +km +km +km +km +fj +fj +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +fj +fj +km +NT +NT +eC +NT +NT +PE +NT +qT +NT +NT +NT +eN +NT +qT +qT +sg +NT +NT +NT +NT +BU +NT +km +km +km +km +km +km +km +km +km +km "} -(91,1,1) = {" +(87,1,1) = {" aa aa aa @@ -24567,25 +26693,20 @@ av av av av -ad -av av av av ad av -av ad av av +ad av av +ad av -av -av -av -av -av +ad av ad av @@ -24599,6 +26720,8 @@ av av av av +ad +ad av av av @@ -24620,7 +26743,6 @@ av av av av -ad av av av @@ -24651,8 +26773,6 @@ av av av av -ad -ad av av av @@ -24672,6 +26792,12 @@ av av av av +ad +ad +av +ad +ad +ad av av av @@ -24694,93 +26820,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +BM +ER +vr +sZ +vr +LV +vr +Jj +vr +vr +vr +OJ +vr +Jd +vr +vr +vr +vr +Jj +iU +YG +qx +vr +sZ +vr +sZ +vr +km +km +km +km +fj +fj +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +fj +fj +km +NT +NT +NT +NT +NT +NT +NT +NT +NT +NT +NT +eC +eC +qT +qT +qT +NT +BU +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km "} -(92,1,1) = {" +(88,1,1) = {" aa aa aa @@ -24821,20 +26947,18 @@ av av av av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +ad ad av +ad av +ad +ad av +ad av av av @@ -24843,7 +26967,7 @@ av av av av -av +ad av ad av @@ -24852,8 +26976,9 @@ av av av av -ad av +ad +ad av av av @@ -24871,12 +26996,12 @@ av av av av +ao av av av av av -ad av av av @@ -24887,7 +27012,6 @@ av av av av -ad av av av @@ -24908,7 +27032,6 @@ av av av av -ad av av av @@ -24927,6 +27050,9 @@ av av av av +ad +ad +ad av av av @@ -24951,93 +27077,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +fA +YG +qx +vr +sZ +vr +ji +Jj +pd +vr +vr +OJ +vr +vr +vr +vr +vr +vr +Jj +YG +vk +vr +sZ +vr +qx +vr +sZ +km +km +km +km +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +km +km +NT +NT +NT +NT +NT +NT +eN +cL +NT +NT +JF +qT +qT +NT +NT +NT +NT +NT +BU +NT +km +km +km +km +km +km +km +km +km +km +km "} -(93,1,1) = {" +(89,1,1) = {" aa aa aa @@ -25079,17 +27205,18 @@ av av av av -ad av av av +ad +ad +av av av av av av ad -ad av av av @@ -25098,11 +27225,11 @@ av av av av +ad av av av av -ad av av av @@ -25133,11 +27260,13 @@ av av av av -ad -av av av +ad +ad +ad av +ad av av av @@ -25146,7 +27275,6 @@ av av av av -ad av av av @@ -25164,10 +27292,8 @@ av av av av -ad av av -ad av av av @@ -25208,93 +27334,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +BM +ER +vr +sZ +vr +LV +sZ +Jj +Zo +YR +OJ +LL +Zo +Zo +Zo +YR +vr +vr +Jj +iU +sZ +vr +ji +sZ +vr +sZ +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +PE +NT +PE +NT +NT +NT +NT +eC +NT +NT +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(94,1,1) = {" +(90,1,1) = {" aa aa aa @@ -25331,10 +27457,6 @@ ab ab ab ad -ad -ad -ad -ad av av av @@ -25347,8 +27469,9 @@ av av av ad +av ad -ad +av ad ad av @@ -25360,14 +27483,16 @@ av av av av +av ad av av av av av -ad av +ad +ad av av av @@ -25379,6 +27504,7 @@ av av av av +rF av av av @@ -25390,15 +27516,16 @@ av av av av -ad av av +ad av av av av av av +ad av av av @@ -25421,7 +27548,6 @@ av av av av -ad av av av @@ -25465,93 +27591,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +vn +QR +vr +ji +sZ +vr +Hf +Jj +UU +vr +vr +Jj +is +vr +vr +OJ +vr +vr +kz +DV +fA +vr +sZ +vr +vr +ji +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +NT +NT +NT +qT +qT +NT +eC +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(95,1,1) = {" +(91,1,1) = {" aa aa aa @@ -25588,27 +27714,25 @@ ab ab ab ad +ad av av av av av av +ad av av av av +ad av av -av -av -ad ad av av av -ad -av av av av @@ -25650,6 +27774,7 @@ av av av av +ad av av av @@ -25661,7 +27786,6 @@ av av av av -ad av av av @@ -25672,15 +27796,17 @@ av av av av +SS av av av av av -ad av av av +ad +ad av av av @@ -25722,93 +27848,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +kQ +FT +sZ +VR +vr +ji +wO +Jj +vO +BA +yD +Jj +vr +pd +vr +OJ +vr +vr +WN +qo +eS +sZ +vr +sZ +sZ +VR +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +NT +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(96,1,1) = {" +(92,1,1) = {" aa aa aa @@ -25845,6 +27971,8 @@ ab ab ab ad +ad +ad av av av @@ -25857,14 +27985,11 @@ av av av av +ad av av av -ad -ad av -ad -ad av av av @@ -25874,14 +27999,14 @@ av av av av +ad av av av av -ad -ad av av +ad av av av @@ -25903,6 +28028,7 @@ av av av av +rF av ad av @@ -25911,12 +28037,11 @@ av av av av -aq -av av av av av +ad av av av @@ -25934,7 +28059,6 @@ av av av av -ad av av av @@ -25964,6 +28088,8 @@ av av av av +av +av ad ab ab @@ -25979,93 +28105,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +vk +BY +ji +WQ +wO +eS +HN +Zo +Zo +Zo +Jj +Zo +Zo +Zo +Zo +Zo +Zo +YR +YG +xD +vr +sZ +vr +BY +ji +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(97,1,1) = {" +(93,1,1) = {" aa aa aa @@ -26102,8 +28228,12 @@ ab ab ab ad +ad +ad +ad av av +ad av av av @@ -26112,15 +28242,10 @@ av av av av -av -av -av -ad ad ad av av -ad av av av @@ -26131,6 +28256,7 @@ av av av av +ad av av av @@ -26141,7 +28267,7 @@ av av av av -av +ao av av av @@ -26174,11 +28300,10 @@ av av av av +ad av av av -ad -av av av av @@ -26191,11 +28316,12 @@ av av av av -ad av av +ad av av +ad av av av @@ -26227,92 +28353,7 @@ ab ab ab ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ab aa aa aa @@ -26321,8 +28362,93 @@ aa aa aa aa +YG +YG +vk +WS +Ej +YG +vk +YG +vk +BY +Hf +vr +Ys +IN +LK +Ej +YG +XL +vr +Ys +vr +sZ +vr +sZ +vk +WS +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(98,1,1) = {" +(94,1,1) = {" aa aa aa @@ -26359,26 +28485,25 @@ ab ab ab ad +ad +ad +ad +ad av av av av av +Uy av av -av -av -av +ao av av ad -av -av -av ad ad -av -av +ad ad av av @@ -26389,13 +28514,13 @@ av av av av +ad av av av -ad -av av av +ad av av av @@ -26419,6 +28544,7 @@ av av av av +ad av av av @@ -26435,7 +28561,6 @@ av av av av -ad av av av @@ -26450,6 +28575,7 @@ av av av av +ad av av av @@ -26493,93 +28619,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +YG +vk +lX +Bv +IZ +Bv +IZ +Bv +Kt +vr +ji +OY +vr +ji +IZ +IZ +Wr +sZ +vr +VR +vr +ji +sZ +vr +lX +Bv +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(99,1,1) = {" +(95,1,1) = {" aa aa aa @@ -26616,8 +28742,10 @@ ab ab ab ad +lK av av +ad av av av @@ -26626,19 +28754,13 @@ av av av av -ad -ad -av -ad av av ad -av ad av av av -av ad av av @@ -26671,6 +28793,9 @@ av av av av +rX +av +av av av av @@ -26690,6 +28815,7 @@ av av av av +ad av av av @@ -26705,11 +28831,11 @@ av av av av +ad av av av av -ad av av av @@ -26750,93 +28876,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +fA +YG +qx +vr +sZ +vr +ji +ji +vr +vr +sZ +vr +WQ +OR +yJ +ji +ji +vr +sZ +Ys +vr +BY +ji +sZ +qx +vr +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(100,1,1) = {" +(96,1,1) = {" aa aa aa @@ -26876,6 +29002,7 @@ ad av av av +aY av av av @@ -26884,10 +29011,11 @@ av av av av -ad av av av +ad +ad av ad ad @@ -26898,17 +29026,15 @@ av av av av -ad av -ad -ad av -ad av -ad av av av +ad +ad +av av av av @@ -26939,6 +29065,7 @@ av av av av +aq av av av @@ -26949,7 +29076,6 @@ av av av av -ad av av av @@ -26966,7 +29092,7 @@ ad av av av -av +ad av av av @@ -27007,93 +29133,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +BM +ER +vr +sZ +vr +LV +sZ +vr +ji +OR +vr +ji +WQ +Ys +vr +LV +vr +ji +vr +ji +vr +XL +BY +vr +vr +sZ +BY +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(101,1,1) = {" +(97,1,1) = {" aa aa aa @@ -27132,6 +29258,8 @@ ab ad av av +ad +ad av av av @@ -27141,16 +29269,12 @@ av av av av -av -ad -av -av -av ad ad ad av av +ad av av av @@ -27166,6 +29290,7 @@ av av av av +ad av av av @@ -27190,8 +29315,7 @@ av av av av -av -av +ad av av av @@ -27221,6 +29345,7 @@ av av av av +ad av av av @@ -27234,6 +29359,7 @@ av av av av +rF av av av @@ -27264,93 +29390,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +mw +QR +vr +ji +sZ +vr +Hf +vr +ji +vr +Ys +vr +ji +ji +vr +ji +vr +ji +vr +vr +vr +xI +XL +Gj +vr +ji +XL +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(102,1,1) = {" +(98,1,1) = {" aa aa aa @@ -27388,10 +29514,7 @@ ab ab ad av -av -av -av -av +ad av av av @@ -27406,8 +29529,11 @@ ad av av av +ad +ad av av +ad av av av @@ -27447,7 +29573,6 @@ av av av av -ad av av av @@ -27480,7 +29605,8 @@ av av av av -ad +av +av av av av @@ -27521,93 +29647,93 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +te +Bm +tc +yq +vr +vk +DV +vr +Aa +eS +kH +YG +Aa +YG +DV +wO +YG +hP +Gj +YG +YG +YG +xI +VR +tc +yq +xI +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km "} -(103,1,1) = {" +(99,1,1) = {" aa aa aa @@ -27644,6 +29770,7 @@ ab ab ab ad +ad av av av @@ -27653,20 +29780,20 @@ av av av av +ad +ad av +ad av av +ad av +ad av av av av ad -ad -ad -av -av -av av av av @@ -27683,6 +29810,7 @@ av av av av +rX av av av @@ -27735,9 +29863,7 @@ av av av av -av -av -av +ad av av av @@ -27792,79 +29918,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(104,1,1) = {" +(100,1,1) = {" aa aa aa @@ -27905,7 +30031,7 @@ av av av av -av +Uy av av av @@ -27917,9 +30043,6 @@ av av av av -av -av -ad ad ad av @@ -27929,9 +30052,14 @@ av av av av +ad av +ad +ad av +ad av +ad av av av @@ -27958,12 +30086,11 @@ av av av av +ad av av av av -ad -av av av av @@ -27978,7 +30105,6 @@ av av ad av -ad av av av @@ -27993,7 +30119,6 @@ av ad av av -ad av av av @@ -28015,6 +30140,7 @@ av av av av +rF av av av @@ -28049,79 +30175,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(105,1,1) = {" +(101,1,1) = {" aa aa aa @@ -28159,7 +30285,9 @@ ab ab ad av -ad +av +av +av av av av @@ -28172,6 +30300,9 @@ ad av av av +ad +ad +ad av av av @@ -28181,14 +30312,11 @@ av av av av -ad av av av av -ad av -ad av av av @@ -28227,17 +30355,15 @@ av av av av +rX av av av av av ad -ad -ad -ad -ad -ad +av +av av av av @@ -28306,79 +30432,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(106,1,1) = {" +(102,1,1) = {" aa aa aa @@ -28418,13 +30544,11 @@ ad av av av -ad av av av av av -ad av av av @@ -28432,6 +30556,7 @@ av av av av +ad av av av @@ -28441,7 +30566,6 @@ av av av av -ad av av av @@ -28450,6 +30574,7 @@ av av av av +ad av av av @@ -28470,11 +30595,13 @@ av av av av +SS av av av av av +ad av av av @@ -28491,12 +30618,10 @@ av av av av +ad av av av -ad -ad -ad av av av @@ -28509,6 +30634,7 @@ av av av av +ad av av av @@ -28563,79 +30689,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(107,1,1) = {" +(103,1,1) = {" aa aa aa @@ -28676,11 +30802,9 @@ av av av av -ad av av av -ad av av av @@ -28691,6 +30815,10 @@ av av av av +ad +ad +ad +av av av av @@ -28702,6 +30830,7 @@ av av av av +ad av av av @@ -28716,6 +30845,7 @@ av av av av +rF av av av @@ -28734,7 +30864,6 @@ av av av av -ad av av av @@ -28753,10 +30882,8 @@ av av av av -ad av av -ad av av av @@ -28764,7 +30891,6 @@ av av av av -ad av av av @@ -28820,79 +30946,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(108,1,1) = {" +(104,1,1) = {" aa aa aa @@ -28934,23 +31060,22 @@ av av av av -ad -ad -ad -ad -av -av av av +ao av av av +ad av av av av av av +ad +ad +ad av av av @@ -28981,6 +31106,7 @@ av av av av +an av av av @@ -28990,10 +31116,12 @@ av av av av +ad av av av av +SS av av av @@ -29002,7 +31130,9 @@ av av av av +ad av +ad av av av @@ -29011,14 +31141,12 @@ av av av av -ad av av av ad av av -av ad av av @@ -29046,8 +31174,6 @@ av av av av -av -av ad ab ab @@ -29077,79 +31203,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +jL +jL +jL +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(109,1,1) = {" +(105,1,1) = {" aa aa aa @@ -29187,6 +31313,7 @@ ab ab ad av +ad av av av @@ -29195,6 +31322,7 @@ av av av av +ad av av av @@ -29207,11 +31335,14 @@ av av av av +ad av av av av +ad av +ad av av av @@ -29221,6 +31352,7 @@ av av av av +an av av av @@ -29244,18 +31376,22 @@ av av av av -aU av av av av -ad av av av av av av +ad +ad +ad +ad +ad +ad av av av @@ -29273,12 +31409,10 @@ av av av av -ad av av av av -ad av av av @@ -29297,14 +31431,6 @@ av av av av -av -ad -ad -ad -ad -ad -ad -av ad ab ab @@ -29334,79 +31460,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +tV +Yi +NR +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(110,1,1) = {" +(106,1,1) = {" aa aa aa @@ -29446,15 +31572,18 @@ ad av av av +ad av av av av av +ad av av av av +ao av av av @@ -29466,6 +31595,7 @@ av av av av +ad av av av @@ -29518,6 +31648,11 @@ av av av av +ad +ad +ad +av +av av av av @@ -29526,13 +31661,11 @@ av av av av -ad av av av av av -ad av av av @@ -29550,18 +31683,11 @@ av av av av -ad -ad -ad av av av -ad -ad av av -ad -ad ad ab ab @@ -29591,79 +31717,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +tV +Jr +YO +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(111,1,1) = {" +(107,1,1) = {" aa aa aa @@ -29704,8 +31830,11 @@ av av av av +ad +av av av +ad av av av @@ -29759,13 +31888,13 @@ av av av av +ad av av av av av av -ad av av av @@ -29778,6 +31907,10 @@ av av av av +ad +av +av +ad av av av @@ -29791,8 +31924,6 @@ av av av av -ad -ad av av av @@ -29806,20 +31937,15 @@ av av av av -ad av av av -ad av av av -ad -ad av av ad -ad ab ab ab @@ -29848,79 +31974,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +tV +CG +YO +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} -(112,1,1) = {" +(108,1,1) = {" aa aa aa @@ -29962,6 +32088,10 @@ av av av av +ad +ad +ad +ad av av av @@ -30023,8 +32153,6 @@ av av av av -ad -av av av av @@ -30037,17 +32165,19 @@ av av av av +ad av av av +ad av av av +ad av av av av -ad av av av @@ -30063,16 +32193,12 @@ av av av av -ad av -ad av -ad av av av av -ad av av av @@ -30105,6 +32231,79 @@ aa aa aa aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +tV +CG +qK +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(109,1,1) = {" aa aa aa @@ -30134,6 +32333,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aU +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -30156,28 +32488,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +tV +zR +tV +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM "} -(113,1,1) = {" +(110,1,1) = {" aa aa aa @@ -30261,6 +32644,9 @@ av av av av +rX +av +av av av av @@ -30282,16 +32668,10 @@ av av av av -ad av -ad av av -ad av -ad -ad -ad av av av @@ -30305,7 +32685,7 @@ av av av av -ad +av ad av av @@ -30320,12 +32700,13 @@ av av av av -ad av av av -ad av +ad +ad +ad av av av @@ -30334,6 +32715,8 @@ ad av av ad +ad +ad ab ab ab @@ -30362,79 +32745,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +ql +zC +tV +tV +tV +tV +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +CM +CM +CM +nT +nT +AM +AM +AM +AM +AM +AM +AM "} -(114,1,1) = {" +(111,1,1) = {" aa aa aa @@ -30536,22 +32919,19 @@ av av av av +ad av av av av av av -ad av av av av av av -ad -ad -ad av av av @@ -30563,9 +32943,11 @@ ad av av av -ad av av +ad +ad +av av av av @@ -30578,19 +32960,20 @@ av av av av -ad -ad ad av av av +ad av av av ad +ad av av ad +ad ab ab ab @@ -30619,79 +33002,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +zC +zC +tV +fE +SX +wr +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +Nr +CM +CM +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(115,1,1) = {" +(112,1,1) = {" aa aa aa @@ -30738,6 +33121,7 @@ av av av av +ad av av av @@ -30774,6 +33158,7 @@ av av av av +an av av av @@ -30792,6 +33177,7 @@ av av av av +ad av av av @@ -30808,11 +33194,6 @@ av av av av -ad -av -av -av -av av av av @@ -30821,8 +33202,6 @@ av av av ad -ad -av av av av @@ -30838,15 +33217,19 @@ av av av av +ad av +ad av ad av +SS av av ad av av +av ad ab ab @@ -30876,79 +33259,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +RY +zC +tV +Dd +wS +wS +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +vr +kH +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +Cw +CM +CM +lg +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(116,1,1) = {" +(113,1,1) = {" aa aa aa @@ -30993,10 +33376,14 @@ av av av av +ad +ad av av av av +ad +ad av av av @@ -31049,14 +33436,16 @@ av av av av +ad av +ad av av +ad av -av -av -av -av +ad +ad +ad av av av @@ -31070,11 +33459,7 @@ av av av av -av -av -av -av -av +ad ad av av @@ -31089,19 +33474,17 @@ av av av av +ad av av av ad -ad -ad -av -av av av av av ad +ad av av ad @@ -31133,6 +33516,79 @@ aa aa aa aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +Yv +sx +tp +tV +zC +zC +tV +nc +wS +KU +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +vr +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +vr +vr +Nr +CM +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(114,1,1) = {" aa aa aa @@ -31162,6 +33618,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +ad +ad +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +vN +av +ad +av +av +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -31184,28 +33773,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +gL +Il +JE +tV +zC +zC +Xi +wS +wS +Zg +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +Cw +CM +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(117,1,1) = {" +(115,1,1) = {" aa aa aa @@ -31249,11 +33889,19 @@ av av av av +ad av av av +ad +ad +ad av av +ad +ad +ad +ad av av av @@ -31270,16 +33918,7 @@ av av av av -av -av -av -av -av -av -av -av -av -av +rX av av av @@ -31334,6 +33973,7 @@ av av av av +av ad ad av @@ -31348,16 +33988,16 @@ av av av av -ad av av av -ad -av av av av ad +vN +lZ +vN ad av av @@ -31390,79 +34030,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +tV +Et +hE +OG +tV +Il +Il +Il +tV +UQ +zC +tV +NQ +wS +nc +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +jL +jL +Bi +Bi +Bi +Bi +Bi +Bi +jL +jL +Bi +Bi +vr +vr +vr +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(118,1,1) = {" +(116,1,1) = {" aa aa aa @@ -31505,23 +34145,22 @@ av av av av +ad av av av +ad +ad av av av av av av -av -av -av -av -av -av -av -av +ad +ad +ad +ad av av av @@ -31590,6 +34229,7 @@ av av av av +ad av av av @@ -31605,17 +34245,17 @@ av av av av -ad av ad -av +ad ad av av av av -ad +vN av +ad av av ad @@ -31647,79 +34287,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +tV +jD +Zj +CG +tV +wb +Il +It +JK +zC +zC +tV +Sb +wS +yY +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +Bi +vr +kH +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(119,1,1) = {" +(117,1,1) = {" aa aa aa @@ -31762,10 +34402,11 @@ av av av av +ad av av -av -av +ad +ad av av av @@ -31775,6 +34416,9 @@ av av av ad +ad +ad +ad av av av @@ -31833,12 +34477,7 @@ av av av av -av -av -av -av -av -av +ad av av av @@ -31862,6 +34501,7 @@ av av av av +av ad av av @@ -31870,12 +34510,12 @@ ad av av av +av ad ad av av ad -ad ab ab ab @@ -31904,79 +34544,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +tV +kp +CG +CG +tV +Il +Il +ve +tV +zh +zC +tV +BN +vh +nc +tV +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +jL +Bi +Bi +Bi +ok +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(120,1,1) = {" +(118,1,1) = {" aa aa aa @@ -32019,8 +34659,10 @@ av av av av +ad av av +ad av av av @@ -32029,13 +34671,10 @@ av av av av -ad -ad -av -av av av ad +av ad av av @@ -32071,6 +34710,7 @@ av av av av +an av av av @@ -32106,11 +34746,10 @@ av av av av -ad av -ad av -ad +av +av av av av @@ -32121,17 +34760,18 @@ av av av ad +av ad +av ad av +SS av av ad -ad av av -ad -ad +av ad ab ab @@ -32161,79 +34801,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +tV +tV +tV +pz +tV +tV +tV +tV +tV +zC +zC +tV +nb +qR +qR +Zy +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +jL +Bi +ok +ok +ok +ok +ok +Bi +Bi +jL +Bi +Bi +Bi +vr +vr +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM "} -(121,1,1) = {" +(119,1,1) = {" aa aa aa @@ -32275,9 +34915,11 @@ av av av av +ad av av av +ad av av av @@ -32285,17 +34927,12 @@ av av av av -ad -ad -av av av av ad ad ad -ad -ad av av av @@ -32315,6 +34952,7 @@ av av av av +an av av av @@ -32332,6 +34970,7 @@ av av av av +rX av av av @@ -32343,6 +34982,7 @@ av av av av +rF av av av @@ -32351,7 +34991,6 @@ av av av av -ad av av av @@ -32365,31 +35004,32 @@ av av av ad +ad av av av -ad av av av av +rF av av av +ad av av av +ad av av av ad ad -ad -ad -ad -ad +av av ad +ad ab ab ab @@ -32418,6 +35058,79 @@ aa aa aa aa +ok +ok +ok +ok +ok +ok +ok +tV +Ru +qR +qR +yz +tV +zC +zC +zC +zC +tV +IG +qR +pb +oE +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +jL +jL +ok +jL +jL +jL +ok +ok +jL +Bi +Bi +Bi +Bi +vr +vr +Nr +CM +CM +nT +nT +AM +AM +AM +AM +AM +AM +AM +"} +(120,1,1) = {" aa aa aa @@ -32447,6 +35160,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +SS +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +ad +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +ad +ad +av +av +ad +ad +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -32469,28 +35315,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +tV +Oc +Lp +qR +qR +Jf +zC +zC +zC +zC +tV +tV +tV +tV +tV +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Oi +Bi +Bi +jL +ok +jL +Bi +Bi +jL +jL +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM "} -(122,1,1) = {" +(121,1,1) = {" aa aa aa @@ -32532,31 +35429,25 @@ av av av av +ad av av av +ad av av av +ah +ah av av av av -ad -av -av +aq av ad -ad -ad av -av -ad ad -ad -ad -av -av av av av @@ -32614,6 +35505,7 @@ av av av av +ad av av av @@ -32621,18 +35513,16 @@ av av av av -ad av av av av av ad -ad -ad av av av +ad av av av @@ -32647,6 +35537,13 @@ av av av ad +ad +ad +ad +ad +ad +av +ad ab ab ab @@ -32675,79 +35572,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +tV +Hm +oi +qR +xe +tV +mF +un +UM +Ke +tV +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +ok +ok +ok +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +SD +AM +AM +AM +AM +AM +AM +AM +AM "} -(123,1,1) = {" +(122,1,1) = {" aa aa aa @@ -32789,17 +35686,21 @@ av av av av +ad av av av +ad av av +ah +ah +aw +ah av av av av -ad -av av av ad @@ -32810,11 +35711,6 @@ av av av av -ad -ad -ad -ad -av av av av @@ -32865,8 +35761,6 @@ av av av av -ad -av av av av @@ -32878,15 +35772,18 @@ av av av av -ad av av av +ad av av av av av +ad +ad +ad av av av @@ -32932,79 +35829,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +tV +tV +tV +tV +tV +tV +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +ok +jL +jL +Bi +Bi +Bi +Bi +jL +jL +jL +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM "} -(124,1,1) = {" +(123,1,1) = {" aa aa aa @@ -33046,18 +35943,23 @@ av av av av +ad av av av +ad av -av -av -av -av +ah +ah +ad +ad +ah +ah av ad av av +av ad ad av @@ -33068,11 +35970,6 @@ av av av av -ad -ad -ad -ad -av av av av @@ -33134,6 +36031,7 @@ av av av av +av ad av av @@ -33150,7 +36048,6 @@ av av av av -aq av av av @@ -33189,79 +36086,79 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +ok +ok +jL +Bi +wu +Bi +jL +ok +ok +jL +jL +Bi +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM "} -(125,1,1) = {" +(124,1,1) = {" aa aa aa @@ -33303,32 +36200,30 @@ av av av av -av -av -av -av -av -av +ad av av av ad av -av +ah +ah ad +ad +ah +ah av +ad av av av av +ad av av av av av -ad -av -ad av av av @@ -33347,8 +36242,10 @@ av av av av +ao av av +rF av av av @@ -33446,6 +36343,79 @@ aa aa aa aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +Bi +ok +jL +Bi +Bi +Bi +jL +ok +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(125,1,1) = {" aa aa aa @@ -33475,6 +36445,139 @@ aa aa aa aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +aq +av +ad +av +av +ah +ah +ah +ah +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab aa aa aa @@ -33497,26 +36600,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +Bi +Oi +Bi +ok +ok +jL +jL +jL +ok +ok +Oi +Bi +Bi +jL +Bi +vr +kH +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM "} (126,1,1) = {" aa @@ -33560,11 +36714,16 @@ av av av av +ad +av av av +ad av av av +ah +ah av av av @@ -33583,11 +36742,6 @@ av av av av -ad -ad -ad -av -av av av av @@ -33703,77 +36857,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +Bi +ok +ok +ok +ok +ok +Bi +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM "} (127,1,1) = {" aa @@ -33817,22 +36971,24 @@ av av av av +ad av av av av +ad av av av av -ad -av av av ad +ad av av av +ad av av av @@ -33841,9 +36997,6 @@ av av av av -ad -ad -av av av av @@ -33857,6 +37010,7 @@ av av av av +ad av av av @@ -33960,77 +37114,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM "} (128,1,1) = {" aa @@ -34074,9 +37228,13 @@ av av av av +ad +av av av av +ad +av av av av @@ -34086,21 +37244,16 @@ ad av av av -ad av +ad av av -ah -ah av av av av -aq av -ad av -ad av av av @@ -34114,6 +37267,7 @@ av av av av +ad av av av @@ -34217,77 +37371,77 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Oi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (129,1,1) = {" aa @@ -34331,33 +37485,30 @@ av av av av +ad +av av av av av +ad av av av av ad +ad av av av ad -av -av -ah -ah -aw -ah +ad av av av av av av -ad -ad av av av @@ -34373,6 +37524,8 @@ av av av av +ad +ad av av av @@ -34390,6 +37543,7 @@ av av av av +rX av av av @@ -34503,48 +37657,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +jL +jL +jL +Bi +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +Nr +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (130,1,1) = {" aa @@ -34588,33 +37742,26 @@ av av av av +ad av av av av av av -av -av +ad +ad +ad ad av av av -ad av -ah -ah -ad -ad -ah -ah av ad av av av -ad -ad av av av @@ -34636,12 +37783,10 @@ av av av ad -ad -ad -ad -ad -ad -ad +av +av +rX +av av av av @@ -34661,6 +37806,15 @@ av av av av +ad +ad +av +av +av +av +av +av +av av av ad @@ -34760,48 +37914,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +kH +vr +vr +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +Bi +Bi +vr +vr +nT +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (131,1,1) = {" aa @@ -34845,6 +37999,7 @@ av av av av +ad av av av @@ -34853,25 +38008,16 @@ av av av av -ad av av av -ad av -ah -ah -ad -ad -ah -ah av -ad av +ad av av av -ad av av av @@ -34889,15 +38035,11 @@ av av av av +ao av av av ad -aD -ah -ah -ah -ah ad av av @@ -34920,6 +38062,18 @@ av av av av +av +ad +av +av +av +av +av +av +av +av +av +av ad av av @@ -35017,48 +38171,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +vr +vr +vr +Bi +Bi +Bi +Bi +Bi +vr +vr +vr +vr +vr +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (132,1,1) = {" aa @@ -35102,6 +38256,7 @@ av av av av +ad av av av @@ -35110,24 +38265,17 @@ av av av av -ad -av -aq av -ad av av -ah -ah -ah -ah av av ad +ad +av av av av -ad av av av @@ -35150,12 +38298,6 @@ av av av ad -aE -ah -ah -ah -ah -ad av av av @@ -35178,6 +38320,18 @@ av av av ad +ad +av +av +av +av +av +av +av +av +av +av +ad av av av @@ -35274,48 +38428,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +kH +vr +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (133,1,1) = {" aa @@ -35359,6 +38513,9 @@ av av av av +ad +ad +av av av av @@ -35367,7 +38524,6 @@ av av av av -ad av av av @@ -35375,16 +38531,12 @@ ad av av av -ah -ah av av av -ad av av av -ad av av av @@ -35402,17 +38554,13 @@ av av av av +ad +av +av av av av av -ad -aF -ah -ah -ah -ah -aC av av av @@ -35427,6 +38575,12 @@ av av av av +ad +ad +av +av +av +av av av av @@ -35531,48 +38685,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (134,1,1) = {" aa @@ -35617,6 +38771,10 @@ av av av av +ad +av +av +av av av av @@ -35625,23 +38783,21 @@ av av av ad +ad +ad av av av av -ad av av av av av av -ad -ad av av av -ad av av av @@ -35655,6 +38811,8 @@ av av av av +ad +ad av av av @@ -35663,15 +38821,9 @@ av av av av -ad -ah -ah -ah -ah -ah -ad av av +ad av av av @@ -35679,6 +38831,8 @@ av av av av +ad +ad av av av @@ -35788,48 +38942,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (135,1,1) = {" aa @@ -35875,6 +39029,8 @@ av av av av +ad +ad av av av @@ -35882,23 +39038,22 @@ av av av ad +ad +ad av av av av -ad av av av av av av -ad av av av av -ad av av av @@ -35914,19 +39069,16 @@ av av av av +ad +ad +ad +av av av av av av av -ad -ad -ad -ad -aC -ad -ad av av av @@ -35936,6 +39088,8 @@ av av av av +ad +av av av av @@ -36045,48 +39199,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (136,1,1) = {" aa @@ -36133,29 +39287,30 @@ av av av av +ad +ad +ad +ad +ad +ad +ad av av av av av -ad av av av av av -ad av av av av -ad -ad av av av -ad -ad av av av @@ -36173,17 +39328,12 @@ av av av av +ad +ad av av av av -ad -aG -aN -aC -ah -aS -ad av av av @@ -36194,6 +39344,10 @@ av av av av +ad +ad +av +av av av av @@ -36302,48 +39456,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (137,1,1) = {" aa @@ -36395,23 +39549,17 @@ av av av av -ad av av av av av av -ad -ad -ad -ad av av av av av -ad av av av @@ -36435,12 +39583,6 @@ av av av ad -ad -aM -ad -aQ -aT -ad av av av @@ -36457,6 +39599,18 @@ av av av av +ad +ad +av +av +av +av +av +av +av +av +av +av av av av @@ -36559,48 +39713,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (138,1,1) = {" aa @@ -36652,7 +39806,6 @@ av av av av -ad av av av @@ -36667,7 +39820,6 @@ av av av av -ad av av av @@ -36691,13 +39843,6 @@ av av av av -as -ad -ad -ad -ad -ad -ad av av av @@ -36744,6 +39889,15 @@ av av av av +TI +av +av +av +av +av +av +av +av av av av @@ -36816,48 +39970,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (139,1,1) = {" aa @@ -36909,7 +40063,6 @@ av av av av -ad av av av @@ -36923,8 +40076,6 @@ av av av av -ad -ad av av av @@ -36949,12 +40100,15 @@ av av av av -aH av av av av -aH +av +av +av +av +av av av av @@ -37073,48 +40227,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (140,1,1) = {" aa @@ -37166,8 +40320,6 @@ av av av av -ad -ad av av av @@ -37180,7 +40332,6 @@ av av av av -ad av av av @@ -37206,12 +40357,15 @@ av av av av -aH av -aP av av -aH +av +av +av +av +av +av av av av @@ -37330,48 +40484,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (141,1,1) = {" aa @@ -37424,7 +40578,6 @@ av av av av -ad av av av @@ -37435,9 +40588,6 @@ av av av av -ad -ad -ad av av av @@ -37463,12 +40613,10 @@ av av av av -aH av av av av -aH av av av @@ -37505,6 +40653,12 @@ av av av av +TI +av +av +av +av +av av av av @@ -37587,48 +40741,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (142,1,1) = {" aa @@ -37682,17 +40836,12 @@ av av av av -ad -ad av av av av av av -ad -ad -ad av av av @@ -37715,18 +40864,6 @@ av av av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -as av av av @@ -37780,6 +40917,23 @@ av av av av +og +av +av +og +og +og +og +av +av +av +av +av +av +av +av +av +av av av av @@ -37844,48 +40998,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (143,1,1) = {" aa @@ -37940,13 +41094,6 @@ av av av av -ad -ad -ad -ad -ad -ad -ad av av av @@ -37971,19 +41118,6 @@ av av av av -ad -ad -ad -ad -ad -ad -aI -ah -aI -ah -aI -ad -ad av av av @@ -37998,6 +41132,18 @@ av av av av +ad +av +av +av +av +av +av +av +av +av +av +av av av av @@ -38028,6 +41174,14 @@ av av av av +Am +Sw +Sw +Sw +Am +Sw +Sw +og av av av @@ -38101,48 +41255,48 @@ aa aa aa aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM "} (144,1,1) = {" aa @@ -38228,18 +41382,6 @@ av av av av -ad -ax -az -ah -ah -ad -aE -ah -aE -ah -aE -aV av av av @@ -38255,6 +41397,8 @@ av av av av +ad +av av av av @@ -38285,6 +41429,16 @@ av av av av +Xr +Sw +Sw +Sw +Ed +Sw +Sw +Sw +Ed +kP av av av @@ -38485,18 +41639,6 @@ av av av av -ad -ah -aA -ah -ah -ad -aJ -ah -aJ -ah -aJ -aV av av av @@ -38543,6 +41685,18 @@ av av av av +hD +Yo +Sw +Sw +Sw +QM +Ed +Sw +FN +Sw +kP +av av av av @@ -38742,18 +41896,6 @@ av av av av -ad -ah -az -ah -ah -aC -ah -ah -ah -ah -ah -ah av av av @@ -38791,6 +41933,7 @@ av av av av +TI av av av @@ -38799,6 +41942,17 @@ av av av av +hD +Yo +Sw +Sw +Sw +Sw +Sw +Sw +Sw +Ed +kP av av av @@ -38999,21 +42153,11 @@ av av av av -ad -ah -aA -ah -ah -aC -ah -ah -ah -ah -ah -ah av av av +ad +av av av av @@ -39056,6 +42200,16 @@ av av av av +og +Mb +Am +Sw +Sw +Sw +Ed +Am +Sw +og av av av @@ -39256,18 +42410,6 @@ av av av av -ad -ah -ah -ah -ah -ad -aI -ah -aI -ah -aI -aV av av av @@ -39284,6 +42426,9 @@ av av av av +ad +av +av av av av @@ -39313,6 +42458,15 @@ av av av av +Mb +og +og +og +og +og +og +og +av av av av @@ -39477,6 +42631,14 @@ av av av av +nJ +nJ +nJ +av +av +av +av +av av av av @@ -39513,18 +42675,6 @@ av av av av -ad -ay -aB -ah -ah -ad -aE -ah -aE -ah -aE -aV av av av @@ -39533,6 +42683,10 @@ av av av av +ad +av +av +av av av av @@ -39733,6 +42887,18 @@ ad av av av +og +NV +NV +NV +og +av +av +av +av +av +av +av av av av @@ -39770,22 +42936,10 @@ av av av av -ad -ad -ad -ad -ad -ad -aJ -ah -aJ -ah -aJ -ad -ad av av av +ad av av av @@ -39989,6 +43143,13 @@ ad ad ad av +og +Sw +Sw +Sw +Sw +Sw +og av av av @@ -40028,23 +43189,16 @@ av av av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad av av av av av av +ad +ad +av +av av av av @@ -40245,6 +43399,35 @@ ad ad ad ad +as +og +Sw +Sw +Zp +Sw +AP +og +as +as +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad av av av @@ -40290,35 +43473,6 @@ av av av av -aH -aO -av -aO -av -aH -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av av ad av @@ -40502,9 +43656,37 @@ ad ad ad ad +ad av +Sw +Sw +Sw +Sw +Sw av av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad av av av @@ -40521,6 +43703,7 @@ av av av av +ad av av av @@ -40538,36 +43721,7 @@ av av av av -av -av -av -av -av -av -av -av -av -aH -av -av -av -av -aW -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ao av av av @@ -40754,13 +43908,14 @@ ad ad ad ad -av +ad ad ad ad ad av av +uc av av av @@ -40769,12 +43924,28 @@ av av av av -av -av +ad +aG +aN +aC +ah +ah ad av av av +ad +aE +wW +rc +aE +nL +nD +ad +ad +ad +ad +av av av av @@ -40804,23 +43975,6 @@ av av av av -aH -av -av -av -av -aH -av -av -av -av -av -av -av -av -av -av -av av av av @@ -41011,28 +44165,45 @@ ad ad ad ad +ad +ad +ad av av av +as av av av av av av -av -av -av -av -av -av +as av av ad +ad +ad +ad +ah +ah +ad av av av +ad +yH +ah +ah +ah +ar +BH +ad av +ad +ad +ad +ad av av av @@ -41060,23 +44231,6 @@ av av av av -as -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av av av av @@ -41268,30 +44422,46 @@ ad ad ad ad +ad av av av av av av +Sw av +Sy av -av -av -av -av -av -av +Sw av av av av ad +lD +ah +ar +ah +aI +ad av av av +ad +im +ah +ah +ah +ah +YF +ad av av +ad +ad +ad +ad av av av @@ -41303,6 +44473,7 @@ av av av av +ad av av av @@ -41317,23 +44488,6 @@ av av av av -ad -ad -aN -ad -aR -aS -ad -av -av -av -av -av -av -av -av -av -av av av av @@ -41522,6 +44676,8 @@ ab ab ab ad +ad +ad av av av @@ -41530,27 +44686,39 @@ av av av av +Lv av +as av av av av av av -av -av -av -av -av -av -av +as +aS +ah +ah +ag +Eu ad av av av +ad +im +ah +YJ +aE +aE +Rk +ad av av av +ad +ad +ad av av av @@ -41574,20 +44742,6 @@ av av av av -ad -aG -ah -aC -ah -aT -ad -av -av -av -av -av -av -av av av av @@ -41779,6 +44933,8 @@ ab ab ab ad +ad +ad av av av @@ -41788,31 +44944,32 @@ av av av av +Ed av +Sw av av av av av +ad +xX +ah +ah +ag +hR +ad av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +gq +ah +ar +EH +ah +ah +ad av av av @@ -41822,6 +44979,7 @@ ad av av av +ao av av av @@ -41833,11 +44991,6 @@ av av ad ad -ad -ad -aC -ad -ad av av av @@ -41860,9 +45013,10 @@ av av av av -ad av +ad av +rF av av av @@ -42036,6 +45190,7 @@ ab ab ab ad +ad av av av @@ -42044,39 +45199,41 @@ av av av av +Lv av av av av +as av -av -av -av -av -av -av -av -av +Lv av av ad +Fx +ah +ah +ah +aJ +ad av av av -av -av -av -av -av -av -av +ad +ah +ah +jd +ah +ah +ah +ad av av av av ad ad -av +ad av av av @@ -42089,11 +45246,6 @@ av av av ad -aK -ah -ah -ah -ah ad av av @@ -42105,6 +45257,8 @@ av av av av +rF +av av av av @@ -42293,10 +45447,18 @@ ab ab ab ad +ad +ad +ad +ad +ad +ad +ad av av av av +ah av av av @@ -42304,22 +45466,31 @@ av av av av +ad +ad +aC +ad +ad +ad +ad av av av +ad +ad +ad +ad +aC +aC +ad +ad av av av av av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -42332,28 +45503,11 @@ av av av ad -ad -av -av -av -av -av -av -av av av av av av -ad -aL -ah -ah -ah -ah -ad -av -av av av av @@ -42550,14 +45704,29 @@ ab ab ab ad +ad +mI +hm +Lj +Xr +Go +ad av av av +Wm +ah +ah +eM +Lv +ah +as av av av av av +aO av av av @@ -42566,16 +45735,7 @@ av av av av -av -av -av -av -av -av -av -av -av -ad +as av av av @@ -42586,9 +45746,10 @@ av av av av +ad +ad av av -ad av av av @@ -42602,13 +45763,6 @@ av av av av -ad -ah -ah -ah -ah -ah -aC av av av @@ -42807,8 +45961,22 @@ ab ab ab ad +ad +sO +Xr +Xr +Xr +Go +ad +JQ av av +ah +ad +ah +ad +ah +ah av av av @@ -42823,6 +45991,7 @@ av av av av +as av av av @@ -42831,8 +46000,11 @@ av av av av +HJ av av +ad +ad av av av @@ -42846,26 +46018,8 @@ av av av av -ad -av -av -av -av -av -av -av -av -av -av av av -ad -ah -aE -aE -ah -ah -ad av av av @@ -43064,14 +46218,24 @@ ab ab ab ad +ad +ov +Xr +Xr +Xr +Xr +ad +rG av av av +LB av av av av av +as av av av @@ -43084,24 +46248,21 @@ av av av av +as av av av av av av -av -ad -av -av -av -av -av -av +Io +Vh av av av ad +ad +av av av av @@ -43116,13 +46277,6 @@ av av av av -ad -ad -ad -ad -ad -ad -ad av av av @@ -43321,6 +46475,13 @@ ab ab ab ad +ad +sz +Xr +Xr +Xr +Xr +aY av av av @@ -43329,6 +46490,8 @@ av av av av +as +LB av av av @@ -43342,29 +46505,20 @@ av av av av +as av av av av +as av av +yr av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -43440,174 +46594,162 @@ av av av av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(165,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(165,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +Xr +Xr +Kr +pe +Xr +ad av av av +LB +av av av av av av av -ad av av av @@ -43617,11 +46759,23 @@ av av av av -ad av av av av +as +av +av +av +av +av +av +av +av +av +av +ad +ad av av av @@ -43835,17 +46989,26 @@ ab ab ab ad +ad +ad +ad +ad +ad +ad +ad av av av av av av +LB av av av av av +as av av av @@ -43858,11 +47021,7 @@ av av av av -av -av -av -av -av +as av av av @@ -43873,12 +47032,7 @@ av av av ad -av -av -av -av -av -av +ad av av av @@ -44092,6 +47246,8 @@ ab ab ab ad +ad +ad av av av @@ -44113,12 +47269,27 @@ av av av av +ad +ad +ad +ad +ad +ad av av av av +ad +ad +ad +ad +ad +ad +ad av av +ad +ad av av av @@ -44127,6 +47298,7 @@ av av av av +ao av av av @@ -44162,25 +47334,7 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ao av av av @@ -44349,17 +47503,8 @@ ab ab ab ad -av -av -av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -44382,17 +47527,26 @@ av av av ad -av -av +JH +MY +LE +QZ +ad av av av av ad +aD +ah +ar +ah +ah +ad av av -av -av +ad +ad av av av @@ -44452,7 +47606,7 @@ av av av av -av +rF av av av @@ -44606,22 +47760,8 @@ ab ab ab ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -44644,12 +47784,26 @@ av av av ad +ah +ah +ah +ah +ad +Ep av av av +ad +aE +Ap +oY +ar +ah +ad +aO av -av -av +ad +ad av av av @@ -44863,6 +48017,7 @@ ab ab ab ad +ad av av av @@ -44885,28 +48040,27 @@ av av av av +ad +wf +ml +Zr +xm +ad av av av av +ad +aF +ah +Xz +YF +ah +aC av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -45120,18 +48274,7 @@ ab ab ab ad -av -av -av -av -av -av -av -av -av -av -av -av +ad av av av @@ -45155,15 +48298,26 @@ av av av ad -av -av +Vp +ah +MO +iC +ad +aP av av av ad +ah +jX +ah +ah +ah +ad av av -av +ad +ad av av av @@ -45377,20 +48531,6 @@ ab ab ab ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -av ad ad av @@ -45415,12 +48555,26 @@ av av av ad +NE +iC +xm +xb +xb +LM av av av +ad +ad +ad +ad +aC +ad +ad av av -av +ad +ad av av av @@ -45634,24 +48788,10 @@ ab ab ab ad -av -av -av -av -av -av -ad -ad -ad ad av av av -ad -ad -ad -ad -av av av av @@ -45670,14 +48810,28 @@ av av av av +as ad +Fa +ah +Uv +xm +ad +hT +mQ av av +ad +aG +ah +aC +ah +aS +ad av av -av -av -av +ad +ad av av av @@ -45891,27 +49045,12 @@ ab ab ab ad +ad av av av av av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av av av av @@ -45926,15 +49065,30 @@ av av av av -aq av +as av +ad +ad +ad +ad +ad +ad +vA +hp av av ad +ad +aM +ad +aR +TG +ad av av -av +ad +ad av av av @@ -45974,7 +49128,7 @@ av av av av -av +rF av av av @@ -46154,6 +49308,10 @@ av av av av +av +av +av +av ad ad ad @@ -46164,19 +49322,11 @@ ad ad ad ad -ad -ad -ad -ad -ad -av -av -av -av -av +as av av av +as av av av @@ -46185,14 +49335,18 @@ av av av av +as +ad +ad +ad +ad +ad ad av av -av -av -av -av -av +ad +ad +aP av av av @@ -46412,8 +49566,8 @@ av av av av -ad -ad +av +av ad ad ah @@ -46426,13 +49580,13 @@ ah ah ad ad -ad -av av av av av +as av +as av av av @@ -46440,15 +49594,15 @@ av av av av +as av av av av -ad -ad -av av av +xQ +xQ av av av @@ -46669,43 +49823,43 @@ av av av av +av ad ad -ad -ah -ah -ah ah ah ah ah ah +oY +Xz ah ah +Ap ad -ad -av -av av av av av av +as av av av +HJ av av av +as av av av av -ad -ad av av av +xQ +xQ av av av @@ -46926,7 +50080,7 @@ av av av av -ad +av ad af ah @@ -46937,9 +50091,8 @@ ah ap ah ap -ah -ah -ad +YF +ar ad av av @@ -46947,23 +50100,23 @@ av av av av +as av av av +as av av av +as +as av av +HJ +as av -av -av -ad -av -av -av -av -av +xQ +xQ av av av @@ -46990,6 +50143,7 @@ av av av av +ao av av av @@ -47183,29 +50337,27 @@ av av av av +av ad ad -ad +CA +ai ah ai ah -ap ah -ap ah -ap +ai ah at ad -ad -av -av av av av av av av +as av av av @@ -47213,14 +50365,15 @@ av av av av -ad +as av av av -ad av av av +xQ +xQ av av av @@ -47234,6 +50387,7 @@ av av av av +ao av av av @@ -47440,43 +50594,43 @@ av av av av -ad +av ad ag -ah +KL ak ah -ap ah -ap +jr +jr +jr ah ap ah -ah au as av av av -ad -ad -av -av -av av av av +as +aO av av av av av -ad +as av av +aO av av av +xQ +xQ av av av @@ -47697,44 +50851,44 @@ av av av av +av ad -ad -ag ah -aj +xN +oP ah -ap ah -ap +jr +Zr +jr ah ap ah -ah -au +Sp as av av av -ad av ad -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +aC +ad ad av +ad +ad +ad +aC +ad +ad +ad av -av +ad +ad +aP av av av @@ -47795,7 +50949,7 @@ av av av av -av +rF av av av @@ -47947,6 +51101,7 @@ ab ab ab ad +ad av av av @@ -47955,46 +51110,45 @@ av av av ad -ad -ad +ag ah -ai +aj ah -ap ah -ap +jr +jr +jr ah ap ah -at -ad -ad -av +au +as av av -ad av av ad -av -av -av -av -av -av -av -av +aG ad +rk +ah +aE ad av -av +ad +aG +ad +ah +ah +aE ad av -av -av -av -av -av +ad +ad +QI +QI +QI +ad av av av @@ -48204,7 +51358,8 @@ ab ab ab ad -av +ad +ad av av av @@ -48213,45 +51368,44 @@ av av ad ad -af -ah ah +ai ah -ap +ai ah -ap ah -ap ah +ai ah +at ad -ad -av -av -av av av av av av ad +TF +ad +ah +ah +aJ +ad av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +us +ad +aR +ah +aJ +ad +as +ad +ad +ah +ah +ah +ad av av av @@ -48461,31 +51615,26 @@ ab ab ab ad +ad +ad av av av av av av -av -ad -ad ad +af ah ah ah +ap ah +ap ah +ap ah -ah -ah -ah -ah -ad -ad -av -av -av +ar ad av av @@ -48493,22 +51642,27 @@ av av av ad -av -av -av -av -av -av -av -av -av +ah +aC +ah +ah +ah ad av +ad +ah +aC +ar +ah +CA +ad av -av -av -av -av +ad +ad +ah +ah +ah +aY av av av @@ -48718,19 +51872,17 @@ ab ab ab ad +ad +ad +ad av av av av av -av -av -ad -ad ad ad ah -af ah ah ah @@ -48738,34 +51890,36 @@ ah ah ah ah +BH +CA ad -as -av -av -av -av -av -av av av av av av ad -av -av -av +ah ad -av -av -av +ah +ah +aS ad av +ad +ah +ad +BH +ah +aS +ad av -av -av -av -av +ad +ad +ON +aJ +ah +ad av av av @@ -48975,6 +52129,9 @@ ab ab ab ad +ad +ad +ad av av av @@ -48983,46 +52140,43 @@ av av ad ad +ah +af +ah +jd +ar +YF +ah +ah +ah ad +as +av +av +av +av ad ad -al ad ad +aC ad ad +as ad ad -as -av -as -as -av -av -av -av ad -av -av -av -av -av -av -av -av +aC +ad ad -av ad -av av ad ad -av -av -av -av -av -av +ad +ad +ad +ad av av av @@ -49232,6 +52386,9 @@ ab ab ab ad +ad +ad +ad av av av @@ -49240,42 +52397,39 @@ av av ad ad -ad -ad -ah -ah -ah +al ad ad ad ad ad ad -av -av as av +as +as av av av -ad -av av av av +as av +wl +Vh +HJ av av av av av -ad -ad -ad av av av av +ad +ad av av av @@ -49489,6 +52643,8 @@ ab ab ab ad +ad +ad av av av @@ -49497,16 +52653,10 @@ av av av ad -ad -ad ai -am -ai -ad -ad -ad -ad -ad +ah +ah +ah ad ad av @@ -49514,25 +52664,29 @@ av av av av +as av av av av av +as av av av +kg av av +qX av av av -ad -av av av av av +ad +ad av av av @@ -49746,6 +52900,8 @@ ab ab ab ad +ad +ad av av av @@ -49754,14 +52910,11 @@ av av av ad +am +ah +ah +nZ ad -ad -ad -ad -ad -ad -ao -an av av av @@ -49774,22 +52927,23 @@ av av av ad +ad +ad +ad av av +Zt av av av +kg av av av av av ad -av -av -av -av -av +ad av av av @@ -50003,6 +53157,8 @@ ab ab ab ad +ad +ad av av av @@ -50010,17 +53166,12 @@ av av av av -av -av -ad ad +ai +ah +ah +aT ad -ao -an -av -av -av -av av av av @@ -50033,20 +53184,23 @@ av av av ad +aG +aN +ad av av +Vh +ZZ av +wP +Vh av +Wk +Hb av av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -50260,6 +53414,7 @@ ab ab ab ad +ad av av av @@ -50268,17 +53423,12 @@ av av av av -av -av -av -an -av -av -av -av -av -av -av +ad +ad +ad +ad +ad +ad av av av @@ -50291,19 +53441,23 @@ av av av ad +ad +aC +ad +ad +ad +ad av +HJ av av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +ad +ad +ad +ad av av av @@ -50517,6 +53671,7 @@ ab ab ab ad +ad av av av @@ -50527,14 +53682,7 @@ av av av av -av -av -av -av -av -av -av -av +an av av av @@ -50550,17 +53698,23 @@ av av av ad +ah +ah +mA +ah +aS +ad av av av -av -av -av -av -av -av -av -av +AQ +ad +WC +aI +RL +aI +ad +ad av av av @@ -50788,15 +53942,7 @@ av av av av -av -av -av -av -av -av -av -av -av +ao av av av @@ -50809,9 +53955,23 @@ av av av ad +xT +ah +aF +ah +ah +ad +aO av av av +aC +ah +Qv +ar +ah +ad +ad av av av @@ -50830,12 +53990,6 @@ av av av av -ad -ad -ad -ad -av -av av av av @@ -51057,16 +54211,23 @@ av av av av +ad +rH +ah +ah +ah +ah +aC av av av av -av -av -av -av -av -av +ad +nY +zB +Hy +ah +ad ad av av @@ -51079,14 +54240,6 @@ av av av av -ad -ad -ad -ad -ad -ad -ad -ad av av av @@ -51111,6 +54264,7 @@ av av av av +rF av av av @@ -51304,40 +54458,39 @@ av av av av +ad +ad +ad +ad +ad +ad +ad av av av +ad +Or +hH +ah +GW +FM +ad av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +as +ad +mp +Se +hY +Ci +ad ad av av av av av -ad -av -av av av av @@ -51349,6 +54502,7 @@ av av av av +ao av av av @@ -51558,43 +54712,43 @@ av av av av +ad +ad +ad +ad +aI +ah +aI +ah +XH +ad av av av +ad +ad +ad +aC +ad +ad +ad +as av +as av +ad +ad +ad +ad +ad +ad +ad av av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av +ao av av av @@ -51815,13 +54969,28 @@ av av av av +ad +ax +vM +ah +aE +ah +aE +ah +aJ +ad av av av av +Yt +Yt av +Yt +Yt av av +as av av av @@ -51829,6 +54998,8 @@ av av av av +ad +ad av av av @@ -51847,23 +55018,6 @@ av av av av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av av av av @@ -52072,6 +55226,16 @@ av av av av +ad +ah +UP +ah +aJ +ah +aJ +ah +ah +ad av av av @@ -52091,18 +55255,8 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -52329,6 +55483,16 @@ av av av av +ad +ah +az +ah +ah +ah +ah +ah +ah +ah av av av @@ -52348,18 +55512,8 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad av av av @@ -52586,6 +55740,16 @@ av av av av +ad +ah +aA +ah +ah +ah +ah +ah +ah +ah av av av @@ -52605,19 +55769,9 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad av av av @@ -52843,6 +55997,16 @@ av av av av +ad +ah +ah +ah +aI +ah +aI +ah +aI +ad av av av @@ -52861,21 +56025,11 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +ad +ad av av av @@ -53100,6 +56254,16 @@ av av av av +ad +ay +aB +ah +aE +ah +aE +ah +aE +ad av av av @@ -53117,22 +56281,12 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +ad +ad +ad av av av @@ -53357,6 +56511,16 @@ av av av av +ad +ad +ad +ad +aJ +ah +aJ +ah +aJ +ad av av av @@ -53370,28 +56534,18 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad av av av @@ -53617,6 +56771,13 @@ av av av av +ad +ad +ad +ad +ad +ad +ad av av av @@ -53628,28 +56789,21 @@ av av av av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad av av av diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 97eca30cbfe6..630903054a26 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -2174,6 +2174,11 @@ var/list/ghostteleportlocs = list() requires_power = FALSE ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg','sound/ambience/seag2.ogg','sound/ambience/seag2.ogg') +/area/awaymission/beach/boundry + +/area/awaymission/beach/offshore + name = "Offshore location" + /area/awaymission/undersea name = "Undersea" icon_state = "undersea" diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index f6bbbb3bc020..ea8c9cc09f92 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -124,3 +124,13 @@ icon_state = "doorctrl-p" else icon_state = "doorctrl0" + +/obj/machinery/door_control/brass + name = "brass door-control" + desc = "A brass remote control-switch for a door." + icon = 'icons/obj/clockwork_objects.dmi' + +/obj/machinery/door_control/brass/beach_brass_temple_switch/Initialize() + . = ..() + id = pick(list("brassbeachtempledoor1", "brassbeachtempledoor2", "brassbeachtempledoor3", "brassbeachtempledoor4", "brassbeachtempledoor5", "brassbeachtempledoor6", "brassbeachtempledoor7", "brassbeachtempledoor8", + "brassbeachtempledoor9", "brassbeachtempledoor10", "brassbeachtempledoor11", "brassbeachtempledoor12", "brassbeachtempledoor13", "brassbeachtempledoor14", "brassbeachtempledoor15", "brassbeachtempledoor16")) diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index 4d8fbf01974b..d71613652561 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -124,3 +124,13 @@ icon = 'icons/obj/doors/1x2blast_hor.dmi' width = 2 dir = EAST + +/obj/machinery/door/poddoor/brass + name = "brass blast door" + desc = "A great brass blast door. It looks like it can withstand a lot of punishment." + icon = 'icons/obj/doors/blastdoor_brass.dmi' + +/obj/machinery/door/poddoor/brass/beach_brass_temple/Initialize() + . = ..() + id_tag = pick(list("brassbeachtempledoor1", "brassbeachtempledoor2", "brassbeachtempledoor3", "brassbeachtempledoor4", "brassbeachtempledoor5", "brassbeachtempledoor6", "brassbeachtempledoor7", "brassbeachtempledoor8", + "brassbeachtempledoor9", "brassbeachtempledoor10", "brassbeachtempledoor11", "brassbeachtempledoor12", "brassbeachtempledoor13", "brassbeachtempledoor14", "brassbeachtempledoor15", "brassbeachtempledoor16")) diff --git a/code/game/machinery/telecomms/presets.dm b/code/game/machinery/telecomms/presets.dm index 539e4565d664..5e8238622eb4 100644 --- a/code/game/machinery/telecomms/presets.dm +++ b/code/game/machinery/telecomms/presets.dm @@ -30,6 +30,12 @@ toggled = 0 autolinkers = list("e_relay") +/obj/machinery/telecomms/relay/preset/beachresort + id = "Beach Resort" + hide = 1 + toggled = 0 + autolinkers = list("b_relay") + /obj/machinery/telecomms/relay/preset/centcom id = "Centcom Relay" hide = 1 @@ -44,7 +50,7 @@ /obj/machinery/telecomms/hub/preset id = "Hub" network = "tcommsat" - autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "e_relay", "science", "medical", + autolinkers = list("hub", "relay", "c_relay", "s_relay", "m_relay", "r_relay", "e_relay", "b_relay", "science", "medical", "supply", "service", "common", "command", "engineering", "security", "receiverA", "receiverB", "broadcasterA", "broadcasterB") @@ -52,7 +58,7 @@ id = "CentComm Hub" network = "tcommsat" use_power = NO_POWER_USE - autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay", "e_relay", + autolinkers = list("hub_cent", "c_relay", "s_relay", "m_relay", "r_relay", "e_relay", "b_relay", "centcomm", "receiverCent", "broadcasterCent") //Receivers diff --git a/code/game/objects/effects/mapping_helpers.dm b/code/game/objects/effects/mapping_helpers.dm index 44a16c5ae5f8..dfec1453176a 100644 --- a/code/game/objects/effects/mapping_helpers.dm +++ b/code/game/objects/effects/mapping_helpers.dm @@ -42,6 +42,14 @@ name = "beach sand baseturf editor" baseturf = /turf/simulated/floor/beach/sand +/obj/effect/baseturf_helper/beach/roughsand + name = "beach sand baseturf editor" + baseturf =/turf/simulated/floor/beach/roughsand + +/obj/effect/baseturf_helper/beach/dense_roughsand + name = "beach sand baseturf editor" + baseturf =/turf/simulated/floor/beach/roughsand/dense + /obj/effect/baseturf_helper/beach/water name = "water baseturf editor" baseturf = /turf/simulated/floor/beach/water @@ -61,7 +69,7 @@ /obj/effect/mapping_helpers/Initialize(mapload) ..() - + return late ? INITIALIZE_HINT_LATELOAD : qdel(src) // INITIALIZE_HINT_QDEL <-- Doesn't work /obj/effect/mapping_helpers/no_lava @@ -83,7 +91,7 @@ airlock.unres_sides ^= dir else log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]") - ..() + ..() /obj/effect/mapping_helpers/no_lava/New() var/turf/T = get_turf(src) T.flags |= NO_LAVA_GEN diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index f8abaa7a17ee..42ab0ef61ebf 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -402,3 +402,21 @@ /obj/effect/spawner/lootdrop/three_course_meal/New() loot = list(pick(soups) = 1,pick(salads) = 1,pick(mains) = 1) . = ..() + +/obj/effect/spawner/lootdrop/brass_temple_spawner + name = "brass temple spawner" + lootdoubles = 0 + + loot = list( + /obj/item/clockwork/component/geis_capacitor/fallen_armor = 1, //you got lucky in a bad way + /obj/item/clockwork/alloy_shards/large = 1, + /obj/item/clockwork/alloy_shards/pinion_lock = 1, + /obj/item/clockwork/alloy_shards/clockgolem_remains = 1, + /obj/item/stack/tile/brass = 8, //general loot + /obj/item/stack/tile/brass/five =6, + /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden = 6, //your prize is a fight yay! + /obj/structure/clockwork/decorative/obelisk = 4, + /obj/item/stack/tile/brass/twenty =2, //jackpot + /obj/item/storage/toolbox/brass =2, + /obj/item/storage/toolbox/brass/prefilled = 1, + /obj/item/clockwork/weapon/ratvarian_spear = 1) diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 30085f8dad22..4874bbd3b94d 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -170,6 +170,9 @@ var/global/list/datum/stack_recipe/wood_recipes = list( new /datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20), new /datum/stack_recipe("wood table frame", /obj/structure/table_frame/wood, 2, time = 10), \ new /datum/stack_recipe("wooden chair", /obj/structure/chair/wood, 3, time = 10, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("beach chair, blue", /obj/structure/chair/beachchair, 3, time = 10, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("beach chair, red", /obj/structure/chair/beachchair/red, 3, time = 10, one_per_turf = 1, on_floor = 1), + new /datum/stack_recipe("wooden lounge chair", /obj/structure/bed/wooden_lounge_chair, 5, time = 15, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("wooden barricade", /obj/structure/barricade/wooden, 5, time = 50, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("bookcase", /obj/structure/bookcase, 5, time = 50, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("dresser", /obj/structure/dresser, 30, time = 50, one_per_turf = 1, on_floor = 1), @@ -408,6 +411,11 @@ var/global/list/datum/stack_recipe/brass_recipes = list (\ new/datum/stack_recipe/window("fulltile brass window", /obj/structure/window/reinforced/clockwork/fulltile, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \ new/datum/stack_recipe("brass chair", /obj/structure/chair/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("brass table frame", /obj/structure/table_frame/brass, 1, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ + new/datum/stack_recipe("prolonging prism", /obj/structure/clockwork/decorative/prolonging_prism, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ + new/datum/stack_recipe("mania motor", /obj/structure/clockwork/decorative/mania_motor, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ + new/datum/stack_recipe("brass obelisk", /obj/structure/clockwork/decorative/obelisk, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ + new/datum/stack_recipe("tinkerers cache", /obj/structure/clockwork/decorative/tinkerers_cache, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ + new/datum/stack_recipe("brass relay", /obj/structure/clockwork/decorative/relay, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ ) /obj/item/stack/tile/brass @@ -433,6 +441,12 @@ var/global/list/datum/stack_recipe/brass_recipes = list (\ pixel_x = 0 pixel_y = 0 +/obj/item/stack/tile/brass/five + amount = 5 + +/obj/item/stack/tile/brass/twenty + amount = 20 + /obj/item/stack/tile/brass/fifty amount = 50 diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index d240e78af5c4..ddcabccf03d7 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -177,7 +177,7 @@ /obj/structure/ladder/unbreakable/dive_point/buoy name = "diving point buoy" - desc = "A buoy marking the location of an underwater dive area." + desc = "A buoy marking the location of an underwater dive area. You can pull people and objects along with you" //the notice is because several people didn't know you could do this and I feel it's important people knew icon = 'icons/misc/beach.dmi' icon_state = "buoy" id = "dive" @@ -187,7 +187,7 @@ /obj/structure/ladder/unbreakable/dive_point/anchor name = "diving point anchor" - desc = "An anchor tethered to the buoy at the surface, to keep the dive area marked." + desc = "An anchor tethered to the buoy at the surface, to keep the dive area marked. You can pull people and objects along with you" icon = 'icons/misc/beach.dmi' icon_state = "anchor" id = "dive" diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm index b823cc1b623d..1c12a8c4a1b6 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm @@ -31,6 +31,20 @@ icon_state = "psychbed" buildstackamount = 5 +/obj/structure/bed/wooden_lounge_chair + name = "wooden lounge chair" + desc = "Time to lay back and soak in the solar rays." + icon_state = "wooden_lounge_chair" + can_buckle = TRUE + anchored = TRUE + buckle_lying = TRUE + resistance_flags = FLAMMABLE + max_integrity = 100 + integrity_failure = 30 + buildstacktype = /obj/item/stack/sheet/wood + buildstackamount = 5 + + /obj/structure/bed/alien name = "resting contraption" desc = "This looks similar to contraptions from Earth. Could aliens be stealing our technology?" diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index 90b489300d36..44be47286306 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -165,6 +165,19 @@ icon_state = "wooden_chair_wings" item_chair = /obj/item/chair/wood/wings +/obj/structure/chair/beachchair + name = "beach chair" + desc = "Time to take a load off and relax." + icon_state = "beach_chair" + resistance_flags = FLAMMABLE + max_integrity = 50 + buildstackamount = 3 + buildstacktype = /obj/item/stack/sheet/wood + item_chair = null + +/obj/structure/chair/beachchair/red + icon_state = "beach_chair_red" + /obj/structure/chair/comfy name = "comfy chair" desc = "It looks comfy." diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index ee7c1bcbb304..111bdc8ad028 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -42,14 +42,43 @@ /turf/simulated/floor/beach name = "beach" icon = 'icons/misc/beach.dmi' + var/water_overlay_image = null -/turf/simulated/floor/beach/pry_tile(obj/item/C, mob/user, silent = FALSE) +/turf/simulated/floor/beach/crowbar_act() return /turf/simulated/floor/beach/sand name = "sand" icon_state = "sand" +/turf/simulated/floor/beach/roughsand + name = "Sand" + icon_state = "desert" + +/turf/simulated/floor/beach/roughsand/dense //made simulated versions to fix lighting issues + density = 1 + +/turf/simulated/floor/beach/roughsand/New() //a simulated version of the unsimulated beach sand + icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4") + ..() + +/turf/simulated/floor/beach/roughcoastline/New() + ..() + if(water_overlay_image) + var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER) + overlay_image.plane = GAME_PLANE + overlays += overlay_image + +/turf/simulated/floor/beach/roughcoastline + name = "Coastline" + //icon = 'icons/misc/beach2.dmi' + //icon_state = "sandwater" + icon_state = "beach" + water_overlay_image = "water_coast" + +/turf/simulated/floor/beach/roughcoastline/dense //for boundary "walls" + density = 1 + /turf/simulated/floor/beach/coastline name = "coastline" icon = 'icons/misc/beach2.dmi' @@ -131,10 +160,11 @@ /turf/simulated/floor/clockwork name = "clockwork floor" desc = "Tightly-pressed brass tiles. They emit minute vibration." - icon_state = "plating" + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "clockwork_floor" baseturf = /turf/simulated/floor/clockwork var/dropped_brass - var/uses_overlay = TRUE + var/uses_overlay = FALSE //disabled the overlay cause it doesn't remove itself var/obj/effect/clockwork/overlay/floor/realappearence /turf/simulated/floor/clockwork/Initialize(mapload) diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm index b2d6b40c123f..7ccbc03b67b7 100644 --- a/code/game/turfs/simulated/walls_misc.dm +++ b/code/game/turfs/simulated/walls_misc.dm @@ -55,6 +55,8 @@ /turf/simulated/wall/clockwork name = "clockwork wall" desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "clockwork_wall" explosion_block = 2 hardness = 10 slicing_duration = 80 @@ -63,8 +65,8 @@ girder_type = /obj/structure/clockwork/wall_gear baseturf = /turf/simulated/floor/clockwork/reebe var/heated - var/obj/effect/clockwork/overlay/wall/realappearance - + //var/obj/effect/clockwork/overlay/wall/realappearance //disabled the fancy build overlay because I can't figure out how to get it to go away when deconstructed +/* /turf/simulated/wall/clockwork/Initialize() . = ..() new /obj/effect/temp_visual/ratvar/wall(src) @@ -78,7 +80,7 @@ realappearance = null return ..() - +*/ /turf/simulated/wall/clockwork/ReplaceWithLattice() ..() for(var/obj/structure/lattice/L in src) @@ -92,24 +94,6 @@ animate(src, color = previouscolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) -/turf/simulated/wall/clockwork/dismantle_wall(devastated=0, explode=0) - if(devastated) - devastate_wall() - ChangeTurf(baseturf) - else - playsound(src, 'sound/items/welder.ogg', 100, 1) - var/newgirder = break_wall() - if(newgirder) //maybe we want a gear! - transfer_fingerprints_to(newgirder) - ChangeTurf(baseturf) - - for(var/obj/O in src) //Eject contents! - if(istype(O, /obj/structure/sign/poster)) - var/obj/structure/sign/poster/P = O - P.roll_and_drop(src) - else - O.forceMove(src) - /turf/simulated/wall/clockwork/devastate_wall() for(var/i in 1 to 2) new/obj/item/clockwork/alloy_shards/large(src) @@ -139,11 +123,11 @@ heated = TRUE hardness = -100 //Lower numbers are tougher, so this makes the wall essentially impervious to smashing slicing_duration = 150 - animate(realappearance, color = "#FFC3C3", time = 5) + animate(color = "#FFC3C3", time = 5) else name = initial(name) visible_message("[src] cools down.") heated = FALSE hardness = initial(hardness) slicing_duration = initial(slicing_duration) - animate(realappearance, color = initial(realappearance.color), time = 25) + animate(color = initial(color), time = 25) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 7131b79b03bb..188d8f9ed087 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -150,7 +150,8 @@ var/suit = -1 var/shoes = -1 var/gloves = -1 - var/ears = -1 + var/l_ear = -1 + var/r_ear = -1 var/glasses = -1 var/mask = -1 var/head = -1 @@ -221,7 +222,7 @@ H.update_dna() H.regenerate_icons() if(outfit) - var/static/list/slots = list("uniform", "r_hand", "l_hand", "suit", "shoes", "gloves", "ears", "glasses", "mask", "head", "belt", "r_pocket", "l_pocket", "back", "id", "neck", "backpack_contents", "suit_store") + var/static/list/slots = list("uniform", "r_hand", "l_hand", "suit", "shoes", "gloves", "l_ear", "r_ear", "glasses", "mask", "head", "belt", "r_pocket", "l_pocket", "back", "id", "neck", "backpack_contents", "suit_store") for(var/slot in slots) var/T = vars[slot] if(!isnum(T)) @@ -511,6 +512,42 @@ glasses = /obj/item/clothing/glasses/sunglasses uniform = /obj/item/clothing/under/shorts/red +/obj/effect/mob_spawn/human/resort_host + death = FALSE + roundstart = FALSE + random = TRUE + allow_species_pick = TRUE + name = "Resort Sleeper" + mob_name = "Resort host" + desc = "A small sleeper with a palm tree logo on it. A well dressed humanoid occupant sleeps within." + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper" + flavour_text = "You are a beach resort host! Your job is to man the resort and tend to the customers, handle riff raff, keep the resort clean \ + and treating customer's injuries/recovering their bodies should they perish while exploring the ocean. Don't leave the resort unless it is to recover someone's body from the ocean \ + to take them to the station for revival or to do some light advertising for the resort." + assignedrole = "Resort Host" + id_job = "Resort Host" + id_access_list = list(access_bar, access_kitchen) + outfit = /datum/outfit/resort_host + +/datum/outfit/resort_host + name = "Resort Host" + uniform = /obj/item/clothing/under/waiter + suit = /obj/item/clothing/suit/storage/lawyer/blackjacket/armored + r_pocket = /obj/item/flash + belt = /obj/item/melee/classic_baton/telescopic + back = /obj/item/storage/backpack/satchel_norm + shoes = /obj/item/clothing/shoes/laceup + l_ear = /obj/item/radio/headset/headset_service + glasses = /obj/item/clothing/glasses/sunglasses/reagent + gloves = /obj/item/clothing/gloves/color/white + id = /obj/item/card/id + backpack_contents = list( + /obj/item/reagent_containers/food/drinks/shaker = 1, + /obj/item/soap/deluxe = 1, + /obj/item/storage/firstaid/regular = 1, //in case people run off with the stuff to maintain the resort + /obj/item/radio/headset/headset_service = 1)//for some reason can't get things to appear on ears + /////////////////Spooky Undead////////////////////// /obj/effect/mob_spawn/human/skeleton diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index 14b66017d41c..2e0de7502041 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -15,7 +15,7 @@ pixel_y = -24 attacktext = "slices" - attack_sound = 'sound/weapons/bladeslice.ogg' + attack_sound = 'sound/weapons/whip.ogg' armour_penetration = 25 melee_damage_lower = 10 melee_damage_upper = 100 @@ -30,8 +30,42 @@ mob_size = MOB_SIZE_LARGE ventcrawler = 0 gold_core_spawnable = NO_SPAWN + loot = list(/obj/structure/closet/crate/deathsquid, /obj/item/toy/plushie/octopus) +/obj/structure/closet/crate/deathsquid + name = "digested crate" + desc = "A half digested crate from the death squid's stomache. It's contents still seem intact." + +/obj/structure/closet/crate/deathsquid/New(add_loot = TRUE) + ..() + + if(!add_loot) + return + + var/loot = rand(1, 20) + switch(loot) + if(1) + new /obj/item/guardiancreator/biological// bingo! + if(2) + new /obj/item/spellbook/oneuse/summonitem + new /obj/item/clothing/head/wizard/fake + if(3 to 5) + new /obj/item/teleportation_scroll + if(6 to 10) + new /obj/item/clothing/suit/space/hardsuit/syndi + new /obj/item/gun/projectile/automatic/pistol + if(11 to 14) + new /obj/item/storage/belt/utility/chief + new /obj/item/clothing/head/hardhat/white + new /obj/item/clothing/gloves/color/yellow + if(15 to 16) + new /obj/item/voodoo(src) + new /obj/item/bedsheet/cult(src) + if(17 to 18) + new /obj/item/wisp_lantern(src) + if(19 to 20) + new /obj/item/immortality_talisman(src) /mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(var/movement_dir = 0) return 1 //copypasta from carp code @@ -48,4 +82,5 @@ armour_penetration = 5 melee_damage_lower = 10 melee_damage_upper = 20 - environment_smash = 2 \ No newline at end of file + environment_smash = 2 + loot = list() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm index ef6e3c540f64..a2681aac6247 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm @@ -120,6 +120,26 @@ crusher_loot = /obj/item/crusher_trophy/watcher_wing/ice_wing crusher_drop_mod = 30 +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden + name = "ocular warden" + desc = "A construct made of brass. Acts as a guard to Rat'Var sites." + icon = 'icons/obj/clockwork_objects.dmi' + icon_state = "ocular_warden" + icon_living = "ocular_warden" + icon_aggro = "ocular_warden" + icon_dead = "blind_eye" + speak_emote = list("telepathically states") + pixel_x = 0 + vision_range = 5 + maxHealth = 100 //This is a gateway mission so no KA damage to help you here thus reduced the damage + health = 100 + light_range = 3 + light_power = 2.5 + light_color = LIGHT_COLOR_LAVA + projectiletype = /obj/item/projectile/temp/basilisk/magmawing + crusher_loot = null + butcher_results = list(/obj/item/stack/tile/brass = 2) + /obj/item/projectile/temp/basilisk/magmawing name = "scorching blast" icon_state = "lava" diff --git a/code/modules/ruins/lavalandruin_code/dead_ratvar.dm b/code/modules/ruins/lavalandruin_code/dead_ratvar.dm index 0d56293f1bc9..ccfb10913183 100644 --- a/code/modules/ruins/lavalandruin_code/dead_ratvar.dm +++ b/code/modules/ruins/lavalandruin_code/dead_ratvar.dm @@ -69,17 +69,70 @@ /obj/effect/clockwork/overlay/floor/bloodcult //this is used by BLOOD CULT, it shouldn't use such a path... icon_state = "cult" +//The base clockwork structure. Can have an alternate desc and will show up in the list of clockwork objects. +/obj/structure/clockwork + icon = 'icons/obj/clockwork_objects.dmi' + resistance_flags = FIRE_PROOF | ACID_PROOF + max_integrity = 100 + anchored = TRUE + density = TRUE + var/buildstacktype = /obj/item/stack/tile/brass + var/buildstackamount = 1 + +/obj/structure/clockwork/decorative/deconstruct() + // If we have materials, and don't have the NOCONSTRUCT flag + if(buildstacktype && (!(flags & NODECONSTRUCT))) + new buildstacktype(loc, buildstackamount) + ..() + +/obj/structure/clockwork/decorative/wrench_act(mob/user, obj/item/I) + . = TRUE + if(flags & NODECONSTRUCT) + to_chat(user, "Try as you might, you can't figure out how to deconstruct [src].") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + deconstruct(TRUE) + +/obj/structure/clockwork/decorative/prolonging_prism + name = "prolonging prism" + icon_state = "prolonging_prism" + desc = "A brass structure of unknown purpose but being around it feels strange." + buildstackamount = 3 + + +/obj/structure/clockwork/decorative/mania_motor + name = "mania motor" + icon_state = "mania_motor" + desc = "A sourceless sound of ticking gears constantly plays in your mind when near it." + buildstackamount = 3 + +/obj/structure/clockwork/decorative/obelisk + name = "brass obelisk" + icon_state = "obelisk" + desc = "A brass obelisk floating in the air. It gives off a yellow glow." + light_range = 5 + light_color = "#dab40e" + buildstackamount = 3 + +/obj/structure/clockwork/decorative/tinkerers_cache + name = "tinkerers cache" + icon_state = "tinkerers_cache" + desc = "A cache of unknown devices and whirling cogs." + buildstackamount = 3 + +/obj/structure/clockwork/decorative/relay + name = "brass relay" + icon_state = "relay" + desc = "You sense something is looking back when you stare at it." + buildstackamount = 3 + // Wall gears //A massive gear, effectively a girder for clocks. /obj/structure/clockwork/wall_gear name = "massive gear" - icon = 'icons/obj/clockwork_objects.dmi' icon_state = "wall_gear" climbable = TRUE - max_integrity = 100 - anchored = TRUE - density = TRUE - resistance_flags = FIRE_PROOF | ACID_PROOF desc = "A massive brass gear. You could probably secure or unsecure it with a wrench, or just climb over it." /obj/structure/clockwork/wall_gear/displaced diff --git a/icons/misc/beach.dmi b/icons/misc/beach.dmi index aa846e1c60a6a4f625232f40e639c3bbadaefe52..46ef2712ed3be7faac102812d5b60a0c3ffbd3a3 100644 GIT binary patch delta 5487 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 delta 5002 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�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 delta 15681 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 diff --git a/icons/obj/clockwork_objects.dmi b/icons/obj/clockwork_objects.dmi index c147f01fdff318a4506a042567835be88b2fd5b7..8ec5c520eb73cae7cf9275b7cc31986f91e68371 100644 GIT binary patch delta 44193 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 CI%Hk| delta 44274 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 DSy3L` diff --git a/icons/obj/decals.dmi b/icons/obj/decals.dmi index 7351c8c7483f073e6465c9440cc8a94e772b3ea3..03c5f12733a5ab888ecec2bf9348b096448b8da9 100644 GIT binary patch delta 26956 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 delta 26923 zcmZU41yEewvNi7R5-fOdf&{k&w*bL|y95Xh0|!k)aCZxu;O-0(Tn2a7;4XvBPriF^ z{qNmZb*kpnOwHNdeR}uqwbtG#cL+yNgct^R#aKo;WJLy*r=PJd+H(6+^*vE$aaDtI zq~t8f5rn3k(Z>~oADUj3K*I`li*k-n@H8lWqt@J$kQ`s)P-2b58vVMYJ|y@0dW7B9`w@UU(&XCi>4i=`u;wTwh!(b zVBH!wL|^Rvo@gfjufD(qQhed^-9q%QYnxaakpFg*4dmSd4*1x=`qrah`1fn>~yfOc*_#$vByAu;n$C>;p=J#{1VWm+6%jKHz6I_|KEvLkln1 zQSFJiY0ata*VqK&P)|Kk74JaDy06PqSrL8ZUmNuvJAJN&Dg|jPjnX1gz$icY^zF45 zNNyl=!Bt>DB=~DZS}!iMf4Z;nQTI929xm=Yie`69kb7LKJFlPh!XHPz7xz&{f58D7 z3C`Oq$UZ-=Xj}^n)+~(CxJYh(^mKpvYY0Ao4h@~7f5(zWER`FI$r~1fjaCx-B;&aW z002f#6VS!yu!zwgEPb#;{8O(r3AFOtz=^Bs6-WH_fn?SNKEwHZ=OLfTA6wL9 z5a>nx?jqwf;-gY8+Kb&5Dl#H}x$$*!G%*f5bp$TCkh_uCh`v?obFy!uj5!{UAH>3w z&ao7ec904$^U}3h|7u3#I%%655P~qqGJV;MLH9taYhQbAUihluF}esYfii!@iWzbZ zoU_5v;dgC}db%o|42!G5TpUOuP+ov81KEXa_N;Jdpf#1f$ z{)=L@G#}4y5Cn^ZrdE!mB@^)+^faQi1sW?4Gx^bmdaBt&h~!!EHE2WD+Ktt=iv+Vr z5tAi)elZ^WxtU(ZmyQ`-47q#FnLji&;2^ZwHC2_R$y~#8*|In_z`ExUdvA#XXn0&d zXP5$|-wtnLLfZe1EtmO)od%a2w3T9TcRF9z@64P3_wI~(ivziaQaNSi-s`{&?1CLG_xqFmhw;bgTl%Mo zvqgepgLZbttH0(LMQ!CivZU(-FOCJ^Ar$aaW9)gOt*0o*B>k>tcUyy=jt8kVoiwQN zT20uBI&Xu?Tpxd;v8rcvdkgxZ>W(DXwjkON_5qy{!rfFIGtWWt9fK$H+7d_&;qm{& zp`7JOPWr-!(sQH@iH=dRNjVFK8KV39R^-9$zl>@JSQ8*6xjeMUESULuAC02y9NyYH zCv^47U;xzvb5FwtZ237SYvK`^ZWK2UJFIp|rJI51YWJZ!6baIqsJE~P$}AjkJe|%f zlJTb&VZaSW|MU&)_TaHG=5H$icK#V}sv`t2m8f*`ynf}_z6D`U?b*`+d>|p5F zu7Yy;8?#{W71rDfhtDtNd!fSO@OTlcff8Z!dTn8XHjCe{De7FDoc~Tv-^CxHa1I}9 zxsMp*1$Eg)r|H<=rnhbFfOhmFlkn=N(|uA;thTxE|5aJlKC1oW86bBt#EFV4m6VKV zl9)Vsv_?1A=_C=Kt;+#KL^P-u`Wa46X>LuzqoplbjwsO_8-Cta$9U3cfR&GCd$0cZ zXHJSl84FN9mr{7ds==W>QIi~}cpMVOTtY9O*X1h_B@J}$^c2ZfuXKy?^lfXy7LTCF z=Z3gP{KTrrd)APog}IfHA+V4|_}9W@a6u0!wSOk|+i8Ohun+ftroJYZR1Le7X7q%6 zV>3<}<#?y=D6U2_W7{Vq?o;EpDuZw5uzDZ)DS0a}OTwUL*1P{JT~C#4K7;LFUgy>Z z%Mp;0hJKYsz6_RaOmUz-h|Q2LDbIFQk6}v30W=U7h=x$f^$ktF zT8{*1d$`~-Hw1fjl>U3cNIfUyH7(9 z?!RK0!fy{~M;+3?{f0*;9TFAnAf^z4hX3;IH+9fk1QzMP_X$WI2MTHCTk@95{;5$i zD7_EX@KphQIR3#-1a1Lahx4CZ4F=T}-(GP!J}QmSJU?b5#{{0p2Ree_l71L&h>T}Y zw;@4aQ0qV!vfUyNr_WZF*mBd}ZCzJ%S|F(f114s`QxDH$vQYmPEI4V-9D%fW+pS`& z>BprwLbFpwI&ytq7QD3|k;4>MVWi=9;2H%(ThQq6C#3CTG;WRj^zpgaMDBeNfqSp^ zhu(DQNxnnkGDAVjUMNxg_H}%55V5Ip^RGr2(cc;wyKd{V~ym#$@ zG5OgWWk;iSrV1KF;)kL!d8GbJA64kVszK>zxr?tEj+)-z)r~?W6$9g*COvk8q0{zD zxRCLJt!Q=qIdd@ZCTrp;>8IkoeTcML5nL@CN65VSH{D?->V`_k^y7Os%Qe<}Gd&l) zphPZq>-(ZQG?V^08Lzed-AYGH5i4^*k04iyH8kG&_UvlaU)0IJF2v%A>OwP-(&J3sU*I9yHq&iR`}OumH!T>K{G-S>ntmm?NJ`hJi?5qm8MQtRT~dNTNLeYH9Ll zMF@1JzA|wsjLr#GODV3GnYgu_NO#9g1`1I61QL&TOR~0+MQ#hO4zhGl&IUQPfH}v| zOX`NO9tYu*`A|POTxrGd=hiBTM=(Q?-ReEb1u1=5>ih0F%Jz}&S<$|I#)C=+%v!5x zc(L07>>|^}89ic``!H>2=;|53Q1F8ng-6K@nQL{HQ<#Vji4+H%$$G&_{|S{V<;y!w z66c%NAu`~P6~{9xUDY0mZP({c-81JbJ4k;O(; z3JpXtNIlT%R|n>`(EF_3vpAcAXJfa(E2o}XXM7sy>~t~Gm0lD0&@rIsjK+63*85{T zmsu5AriiE6!Auz%U6mP$*vb|+P!i}e+>~PwpB3flS1l}%T=dfKqwP6RHF-_5mRDQc^R8SP#GeLx~iSiw2+ke ziOW6|u((=>%kY@sDKjQAX`nG$-hkc%eBT!qgtcjA$%hE$czchz9D#RefsT4;=oCj4 z7CIDcOpnF*QeGAY`Z>h)j0(bO`SDx%n{wRtGX`iySLd0q1|0)Y@NvZ~ymLqHs*=c4 zXRBOIVfOV#Nyg^~G^2rd329r?#dsj`A|HJ9OIit+ope#-_aFn}*OMM-LsnGB&U#6f z)fMW&{BFleK!=YlFJSgWkw0Nmre|xgBw>#G+{a>3x7$%jWZXI)`_(yEZWteJ(5&EZrmsY5^J)9E?B~iEdKIMQbaGG?_n(@@>Fs zU;{&lIQ56X8H#O27cu7$8BvnH)klK;g9Chu{Mr~|OBUuZ$9efYc#ObZWc{1~FHu?V zt|7Vdolxb@NeWewJ+i{KTf6kzo*mTh9}Y=!TcX2itKsVLeDnSJoQOk7IQto-1!zlU z?{JqRVUJjktN}Yu=#!O>Y+lg#98((jmiP`+TC^v|GT zOKWwn-|4x#8gs2Y?|>`PCvY|HS(h8@;mJ;Rp7?8Jdt+ddUH^LZ=(ke|_y) zqyg0QJ2}`I*eJbZ$HbFQ)mHrrcIa*)(LW1ZBJy5i%+fRhVpk3LvM*Wsn&W-p+IKUg-~S{kKd8QS zMunU?^9?`hBP@PDyF$*?pmrmOJvU77Lc?b^B9X(GXTsi6GY4ni`M#Vb$&w1FKmW>O z_8To>H#gBK>a9HfT7Es9B3}}%(+`|A zsOBV|iOLCl|DZH8ep1pxA0oNKqWwrtI$^nQQJ>*rx~~iQ89z^Ux9qsTHT7riP$dh~ zD$D#YH6ROX(r5ey*d7?$J+Oks77A+`^nn`O zA)W3Y2?mgyu$9jvaa#gWEHij&zfO@T`p#8(#iN?Dp^l}j<*BL^NTv(3j*{`k* zC;QO}Yw<5FN_+-aAS0DuotqbS-ymhV9`n5UN%CqN zLKL$uF7ccLy$oSrke;uBUVx!|vwEXVt?^`5jn`uWHuj9V9&Q)nABG?3kQ8mNkJ^?a zp59JR4g9=id4`^ayU!^2sa%tJ(sADcoO7$Ra`7{DF9e}qOQX|XU-=~F?;1~NIL6sYw_AEDm-2uh;e>F5Qdbsx#9ik zQy2S#so~VA3Qn=O;K_k&=f%=g-<6>uM2Z!+Kx~yzRsXj`dbKWjmF|lGSL5s+K?zVo zZ1~AL+SA%DchOqL@xg8a;gkbA5vHeA{-fshwuc=%cr7Cr{zs>_;6b+X9Vb{yFQb=X zP@k>R%4RL&Nr68MxT2XXZrrfDdtCR}0H+3M406e3rG2uWtF=A`fht|XP$(G$*bqd4U~ z(1^I=(;MoBD{O)oy~I?){k*b<4qR|<2FW;WmNzBS=D%f49=HGk@xsY~ui2dc&S&kk zD)a^81M~TISe3?UMVqXq!3?2gPt3xshb+!vNa;zaN1O?C6di#jTq%FXYY^9b%?AhQ z<2*n>yx0|NEM8A<1${P-@--!v(Qi7uEGatQa<|?Rm9X>HfwMZC-NK_o_=>Ki&x+4M zB#*5jd2=T`Na)^(5oPElSb=qiUcRf&`B?c1y-$X1`4i#XZ&*tqtqM!T^|^i7`-N=H zrM9Azt!iJ$%He#2gzxnc?vmn z6+VNJ3+hs}^A{2dZ-Suxm z=;X;G-rB9+G6p)wB=QRc<8LwWa?^>X!L?Uh{DAn8tw|y#`;f8M&&s*>wj}GXQQdSi zTRo&+-&oPJlDvkj5!aX+%&{NCPAX}U^%WncGeYSZK|v=&pV$uX*qlmj?&uuY4PF); z-OP8^n0Bq)w(!{JBZ!7x61kA#5wMB^?N6&MDK#*)G5wr`u4;y)Oz^(x;OA~)Y}>B> zwL0XHXe#&qZ><+jY%gY2Qpd_$j)hld)12VdM6cMxdQq%I<2m~yQ7+5S!P|&*jYep3 zQ56M4FYP2{Zy&&vqjFdhL}H#sr+(sVn7J2RcRu5{5!dF!pPEAO_|Shy*V48CAfw0= z_qXx0i9BH`Z1%}qGJE>9sxMD%1ZtmoCyonYF2CJPlao^>*VvL;C-DgiLkKKj_BGkj zO@=V}>ie19kr$8Y)pdNi)T;K1wxJV6id6dndH<@yP6e!;2{QMwm=-1&4mR?5TwI$i z;U{vsaR633?e;Z?j*46|;Q3CGa=CKm2JfQwgqITu4U_e-br%H-ER}|G+4YLa%7#C+ zf68a#90y@4>(Zqnr~5-Lev2d~EVWilWkSWQDX5xSCf5DHs~++`AvqEMg74E0>ioAh z>mqcuod`)Ph=Y8cZS}N}fd>zLI5CL59qLi~dm3P0&*y4B7~5U~uspKP9<#m?gV(*E zXAa;B$@1FQ^YMD{m-Gz@w;~q4`7Z zYDlLa)8;rX4Oh>ppE5Jxh#Q4yv;1ws;5{E4&dVWPL)1CfP>48E1js0*}i2v_J3Bd8rH|_+B7~6HQOZ%`bk`JwLEsrx-yg^ zoFyN=u2}*1_-k~8kZTFY!!%z0oG?O~Vb}t@{uh+Oo8^FA@8TG^X4?asxP_i+1qVmM zSpgf|X6$ZP(c4S@1Z}UZ6D86b#}|D(;h=C+c>6DabZz$aDO&|}ZmV=s_F^#oOT1J61&%eo1S_GuijkpdE{t@5jvI=_Qllb0cw3Sr>1`aXo2+)Q-Vf zd%6q|w=torAvgYXN2Er7zqZ(bDZkNd^g*YE8m4$QhIY{73o43Rwt3#B~26pi;!6oF<1;(o(#`W`r5|w z(W#~j4TE^m!5|mQ*aV8_Np4|kpp{@7&I>vbOhDmyUmt17W_I>Q1C3y?2j_=qqvAT# z#m%ujI#igpmJB;L@|0eJPmG#k^l;0Uq{7Lo3d1|qq+#uGZ9gqqo#xa z#qD@@Co$ltjXyI0o!=}6j*H)xoFdeINNs;5mpwLSR_PT=K7C5kGK<$(3R zz4KER_77ox4bvnGUBXfh|DtB5JiuI~%ie##%Xd1p-(y4$=LSLhw)(0}MQITYjnv5|g&wCwB| zhW+KiRw;#@|2P}pUh+yfBaawMwTh&PD*IUtUOl<`n2X_V{ke_7!N(}5!+iZ zINzHXza+mzFxZb8NS7dN3Q-g21k6u2RlvLJ)ZjC4i*ct8ju!!6Mu`6*KDAW)9iCi#FFNB< ze*c6<^eNbCwmu)aHI$9Xv#5$rTSla4j|07n1)ZM_TG@6}jM5oP5U|a40R3^iy6nsT z@LGrNVm)sQ@m?Vb@HK(c_{mr&Lp@IAH}1xB0}7LyVsN;ZI9Mc$FUx6LQF`+Q=|A8) z%YdjlY5u(ewUcXUYvGk1cDRZGBB5)twz;vlQ7DRDhsKY73Fn{@^pW#f>b~fAMe6z{ z^H)Dl#gISHhPeJh1ZSspyM2>BQr$(#}tAOT!x8bJTEbf9BIZnB14&JLl$ z*IRBfP#vaVcSR!|HD@{55fZ3DjkNT;?`yJYt)MM!+8(hsrpT@zpo!h5*4;wW!5V-y zMjpWJ-@11*f{Q^)*HJB|D!$3V$Gtf~KX*`YR4UZ&I%MRtlQEXzKBgeryrw5X&}d1A z*NDNJC%7eDSUC{dBv{fKs!sPXPaD!v?FbBE6?omA0fnOjF~Q+86p#y0AS%1pt~&|b zuK$ezQ(h@kD;GtVG+Nr%jZ|~#Ga_0LEihH_55o`l+{yp`hvI*QqE`Z@8G^GL{gK)DXNO<;= zTjo-HUHvn?eW8y%BmN?y54RFceKXJEr1%@XoUdP^^j>1ksv6H_9mZ{N~k!Iw7s zspORwhLra^=7inqKFmd%9V6?*u61ze+KEz@9GcWS;9aQ1$#(M-NS*rKQ|HC1_2deo zw}1cdA5$Z=#!rSR{JtIwClavn=vURZZJuymplOV|lSg7W?b0XFqYQ=no#eNtA*M*f zO+5=yrUo{T@2aa}zl5I96(hD0t5C(f{V~y2tRH~m2YRg-ufTeBXodCIWve1*N^7(K zMmR{~7w|8T4^r30@B8rGVuFr3+H|oUI#SK37>l9){oFjKNxwkZA<&7s&u>@7p(Wkv z2SMB;jvx8#K*ftM5;UT-+rAMiGt~yw_~K&PT$_{LUw;JZaZv}y+jP_u$S+DObc$6- zz_W}>ydt`sBGNmUN9-lIwdz>-|)BQC2{J;$+qDC7+Y$LI4d*xz?tJ83NXgVl`Nuf?RiaKcos-u14 z_OMc`;bL=8^CP(>1)$N<5(`>3FAm3=!)r<31n{_!QTpD7LPDCfrE3OMh=oaw+GzX_ zg}?uQ7g$3}_#j~JcuiI6&;;JP<9_##0D=CUy|MC)6^=Zbgk2Ec%P|T*{k00#K}{s` zCmkcdO!H_V-sypzE?Bj1`0fex>G*W%57zo*HF;QCQnj?0e%>d&^ zlLL%k#sZ>*#1AG09(Z^pqL9vN_N<3pSN}hGqP6uBA3f0rXsFaguahcvQSvzZ+%+;L ztp#1~2=89vZD~`04}{!KR?428Rot_!FX?*L!wA?TE*6c@hdf1;LovpY@X_J%f6tu* z#!L`}HNKhL1j-0@WNH1MD;j8+F<#^_My#_Js$(R$1@J!HSo2|2U zMK~L=6(~3&&rKeaMwXx*s|JqyI@Y7;XEoh_brZ7?hx(*Z3H-ZJchZq-xYWWtXKE?Z zI4f0zl-ogec<1S=>AxE;c%P)Xk6T^&Z7f(OB6{5p+a(q73)>@-0D6t>w}Mn{2ToAW zII7Ood@O+i`@8JY$W@KN#G|6di_X6DU$c!g8S&Z+RDab5ywKj`u)jhc8vggd4XB6g zKdK%YGA)7RUri4$m*E?d`ak*}3;N+q{6C_faCys40w2wJNrj(y+GVsl_e=N$#AG!e z?p1dnsLX6)dzW~7yOjDv*}W|$D?be;MmpDgQy-`%@b&khi z*wc@*q~_=D2mE@o*uu$HbmoZGdyVPI{|Vc?FKQb2vq(DYt{@<=A&e8&E1O{vXTTbT_sab~ISsJxb)U`n z;neao7XF^~n%oe*qc8kG?ZeT)bfwbNe z!D6o#%o%pS7M08~m%Lwf=MJ*hm1N6{mJmYo1Sfe7ajkJ%GPK3>?oNaDm-d*QgVClt zuj7icLKlv(Sxjb1yO7>QL4Lf-ACJYIvHP9};z*mLV!zzP)T&>)V zpLpzvFOo~0cpT}Dz^|8KN$MDcK*~6%-SbPgcre?1Wi@qx(Y^Nh*@h2wCdFpe&nm83 zJiC*}Y(D=t$!e+T9;LdF)%>&WM?P|>wUYU1v|h>Wt|sKD76OzoHBvgYzK1>RN?5OT z1eP(Xn9f&QENOqyItJ zm@U7LtmQBB(lACij6gtmuX@(#bQ6=oI$N)kA1L0+gz@qIy7Llqol){$4zDur+ut5a z-W8QF>400ZAmBoi+Vsxi3Na*gNPqr5uZ}5Jer#?o{aabX&|1hxBYz2)Sg zqG8L4+x2%&u=W6tWo+?&+w3!FN6db}?x=X#5Fq>O8-#GQ#91KfQoBVJ)qo)a2gy-j72T_CKA#i`jzTR)gq~gF|u^Kk=WZtGBpiyd$y?=gkr<=ZFn`* zM%nbx0oPO@Wt8dRV`;jdgL|Fq@5Z&b&ry%6ap7E{$Ur!OmM+dT{UF(xF}}#0Oqmwn zlfip;6f}Iqqufxd(G1nrb49TVFB}>mzwul@MpcIzI;8ze?!$rEFs66bS4xMj9pNaL z!d>i-x*1#PK9>vu%$&-cuZ8#@btlp6rJ5!@88F5}7Q-v?`mGS`)poOSt8FFPuCGE( zcxq__E`SPq?|gBaM_TQHLC@c#&`!Wn6hR)AI7<>d&xZTnD9#`}rtt?y-oz$um!4jyyKV zcob5Wq3qNgrYsNX8DQ2qFPkJZ{2XRBjsO>qq6VBcfIFvkgc440>q;0twE^kV`jM4L zc)MKDcBgT)CU?WK-;!OYE5wq@|8y-nO*bfKF>5@#d1^Qd6T#zds?DxP%TbP>d7hS& zNXlgMMnRxq*CQ$2wIden(mZ7o4!Y!P^AI(%qv@b()5p837eg+s)V`si+ZS_54)Rxy zz+#zjYdPgy^~$=J>RRT?2~jYC&Bt(_-L4C94tvB`9h2DFoRf0el304qGb(Q3{RzaUt9Nzlj7d=p=EtmZSkq=mjx z9mk}U?47*^d~MW($EdRW70=gS3X*|t>0`*b8nMLXUB;0uGbCoWQ}*p1?E+QNQ@Ig( zY@|QYhIH+ArKuSpk4adS4c>iLqmU4t79yLJ*$J1heK^J853}^Zp>jl7!e^>X0%BB7 z$y3aGh(Zo#D~ddwU@KXJwyonw-UJ53Vwos7sJ{ZRJMKG*(kbu=R`xD29%q2Wv=z3+ zo@HvcTGbXWqyB!q3`7$4uXwEF%4Q`@-*XygdNj zzv#zHw&~hDOD|(`NVn^wm~K9vJg_LM2o9!p*@}I~VIU`kcH23hIoJIDqM^@Fbe(P- zFWyb+Wu`2(KW)^LvPQea)QI+4qi!36LB00*+$vF*M^NQh8W}?5kTG@jpM}NkEvNY> zZIq*saeob0$v`Hgp6P%cF1>65X;ZRJvnXo}3VM*oR_j>mv|r6AIT4{L9T1bEH?Z$& zdesz@vPZLM?QA*7ldH?}_MQg~v#b7`V%>H8F5IhZ(ba8b2dmhLEq33 ze#}L~q|1YnO%_$vrIU{4@7b&ql7QB&bI3AP1uO^=4vtG8cb+`LFy*bh#}I&26HoQ~ zN3|1<<$2s5vSxL}r64Y^PslIU1Zdj(*UWVUSb(=(1>@tU8g> zeLHAyDZ^DU)pG9}0I#lF373$VSeLSb_6yX|##L-x-nM2I2WgX^&TJrr&9MI59I(zV z@>pX0o$VL+tL2!5VJPENaQvL@46+xm(FgYJa?%XiZPI~N%)7%}ADq;0TjVjyq!a3D^>*7Q+Ps?mXvWgfHNgo`QzykdF*&?>cwBW+z00LtX@lRJ zFV)LW6)J)+DyEGZ;F zs%jSqIv@Av<)_nZH%md3E2kNVm~(Y7p^D$@p_%IdttlhXn7t3*P$e=CKn$|44f_6h zWc)ymx8>%reyor#eI|a-En$y%5g)RSE30k6ADF4PEI2}rA>0e?J*6;BIP%ohwf(py zL`x4^X+4N;y1`mVdrM?bu<*Ty9277zSddmn z8K1&2#SVuVV!eKVN1Y5>WrU{oNLVlSbDoyrlSn)YEIoOW&Q?`9lFq`TQr0Rdcbo$S z=oGDQf^YDKjZ+ z12=U_ZR*NHF>+5asjr2x(qD8HHb+ebRI3PlJv!-;6?c_LW!W)Q7Z2hh>>&ur{%u!$ zK_Nt~`x`|Z&OdxD`S%;Dz%DCnYM>KXw_9Ub& zeLt6R)qjY>#Gf4m|3`g&GWK~37-P1dI0MMYd?H`LcGsS(v61!2mFsS&5fPQREJiv; zV=v41=tZGn4en-Nq#Pz0;6kb@3Qz5Y_qG%jZn^B3rR>q(;!JG63)i%2sZtC;y`Jwg z-+mRH0tZ1jYWCv696ED$>8KVe6&`)hoaizhP2d0>Hm}1xY1QuiJzi;c@&;2}wOSGG zX`hz8qsGk5ol2ZZ72+4~<|lUVsK&v~Z|@s)l8?{VyUC&JWj6zBye*8`D7&sfh!Kr2i!AJXwjqD&b`UGccLWmGqm3rIpTF672&G5b1hBy}5W73dv{ z4mU(1%D?H!@G&`mGlqTRWj~G#yVNeP!p*YDt>C5>&4*r>zTa=I(}VE)6x!WGM$nBL zgrGLMr&9DK+RVBI>_8=dI0;+5ALD>&ALf@A?Bw1dYaw}(7ouc=D4{bGUUfVOq^VWaDa%K5#?0eO-sP7BjzQn?Zjy@dSl6XdssXw!46U;=bhcKNUG39!xZTz-x(F1vG{U;CU_|H z5jvTTqvqXei9ut-+gC@Lb%1ieT%@x#Ey(OTj88go-%IKJ=aj5r8AZo<6KM7f_N6(W z^t;JNN25E>!UEWc-U&8&RTgj#RIMgp-Rl$V$!jSGV)9z8JHnMDhd0bv9x*32p7EYi zyr({#?8&Qs)%&d`{)J!EP+SwYNdPjn%^txCV@%dp`)-38bB8cXCqjCYE>n+*7Z}oM zu?Njw`kuvbgY`K5w3Cl~7sw-m%4!BCNQ+V2?g?E;Nfp367dz;4VuhIg*fI9w?XbQY zHs^9+Q^y<8-k2?9lH9Sz#*AnyVw2{c@~Y2wka2tI{#X}H( zIF+IP^Bw}YJAIfq*=;yW7Fo+INOn1q5u2-YAzT#De<6@x^~VyUyLjap;c!JM$ad}; zuyrm*wG7K%x+#>y{gm?5q~+6)ng3L|`0485`kV~+u`SLk)#MP$x%}#2Je}R5g6v0$ zOlwOxGY9Z7p=7-49f7rZ8S<-^4a%%Vh3Bc7J>~>J++7kCQjuP<^8%haRs&JbZt>oU zaTATmuE9U>G{l@jdy$NDA6+G(OMQlPDZ*yX1SFs5L)3ZbtNEm?iMi+bs-10eK<`uF znzezv3YA&VsF;n!_ZEcyuL%=68?{y=zZRy>sJF%D&(BIOSxyEbG^0#ux%bpw-<}X) z&M8`gXm>OG?NAen&FRd8(n7JFWg~v?GrSrz69MFC&Et-oytS@xcJ&WYI@^u$m&C_q zebdS)iJO^=9Xn7WW8(I>*sk{vOxZfB=bsM{;c~DiaRwTGlqyV$S3&dMp*;PQ;!P85 zczt==YEZ~(A@@$aKh(?vfdf78_wc1qj`C|m0})vL8?noU21}`FZK*eHH{qAMU{t66 zX!6`Ilc7x@1Z5=IAA3PuF;Br@7Gjfz^6lS(H&)&!*Vx~?gVTpMO%Kr^L&{tF){{H{ zA}7p)obD2l08*BH+h|>GZMUZYa9MrXDmEISpLQB3}Cy!gq0Vl1r;fLSws8dPV&@@3|>|69mytD8}Vy}Fg>)oE9!;3^_1bp80?=bh3jKzT(?Ta_r zJQ~<*gh{W_yh!J;rr;NYYLhtTYUejie5#oE>8CsnOIIvp@6pp&Jwk5MxlH&{;bm!3 zm*Hw6PX{2-4q7hwgwO0~BS)9IZR`cm46lu_;S?UkQ!0{Q$E}$o-lTLQ7M1>TtcjnC zp4O}-Hply@Hz$<1wxfXu`mX@!96bX>j?m}>5!390h(5Zl`styuvsST}WELpMeT}Fs zL$a3$483r~nfq1Qtk8-*b{p7r%up#T1HzT}NLun!6C)VdhZy2v5G~!4Hm>_r`UIYu zsB?MQeo+FqNv1M>sHd%39%zz?!Iv&`>^nUoW~)wHG1_0vmZS0Gt7CqY-bum&-!Gqh zueZ0`=>kW|%Gv9r+6*nJF)i35+a9hbv^&{(rzGb>MX^v zD1$TvFZ8P~8l9`7LgA2Q`GtClkGTB~`}>b#iV{~pHok9iBplGaLXr+Re91yti1#ak znT8YC%_BDGJSQzoyMOF`@fCn@?*7?l7(Dj^&@YdygEyHj&}rRUJG}S| zxwpXVoXnC(Lup!=PHFw#bp4X%X?V3~12T)0G=wcex$v5F$_QQq%VEDQEbsvLH{Ag0 zwVYeH=a2VIzu6U(bsfr;)a%Qb@A0gaPBuyb*nm6WdWFa#hD*Av)%by>bxmmTB2fr+ zQp4K=3#bq{+jR^oX+DTbfR3bWVrj(U$!2TSzALV=hU`3JRQAPdDuST3yzZ|!mUFZ1 zvRy8qR7xaSuO18w_{7{&7#3cEtmACxx814vNrRh4cKuFr2OZrzsQ17Y&QTg zBDc8Tcdrgt1P(}SsePHyf5&twpa+r>Bn%!Q~hI=f8oR_DfbT$COq%P)`K>2^bgxn7hCW}hEMJRl3vX)U|U zDb86m>sx=>WkoR>F>g7g3Y1nBGo4h=wL2@-;|kl1Dw(MO7tNbTlZh$dEy~e02YZM; zU6+pZE}(Qs+fwE|*(l|;lCdSTW%&vB#`s@d632ZJQWu}I90%A^Vc(s8rC$Md_cw@k z3Im*0+!NdjljS{xOW9!j_PIC_Yxf8o!+yZ9r}f^y8lj*8>m8d@@NO?lv<|D=sPR&6G)(GMLS2eK==#>EY@7<7uo8dYpRW zdzk)v9|z_q*sBSF#@bXdcDax9U`Npu%KeD-61^+#tKf_n-z3J>E|p8)H6-(xd$=H)XYozcorf-`>La*}DAt&dsJ|_TH26R2dY( zb3GMu-?{Hs<>5t!L;wj-Ur%S3psbIs0NPN2z1|Cgt5TT!>65HV=H_Qh{QAvNxo>U> zC<*iBrfR>P<;LDyHB$8eS9K6f!nPb;{DRdir?a^!GDq~U=191Wt$dD@_gLi)g^w16 zg4`79;$#Br5ULup)<~91u~86pmv`+u#KE1>Y`@)cwXa>P8#g>L^^ev7w~{-C82}xEWu|-*QQHvdzEq>2Bs8Yvl0qywo?a|?L zWmqMz24+U$-ktO{9&IozN}Rqy8%Wo1=^O3}fjr)U|7vxoP<$waopC|Dg@k{`h*UUHcT8Ssu_|Qo!s6Q+({OdME6c0V;Ux&L0i0Y|i{t7{dNR692 znv**$oKIJ;jf;f}(ZoIU;cH8jyWTw?t^RFHcH9Wn^1H;xe&N!1^6F{FAWr0y_=g4^ z0BzarG5?3K(N!&sX0|3e*}qT7miYUG$Mm6nvd`bl z`q!T~sol=Vl5oa6Jk_f@&V+nw_Ky=kVV1Ch!Izy_Pg8>$LCCn`m(C0!2(=1u*w0U_ zFl>rjSkQ-8|L~#UX4!M~2inVvQ{aZuP1mD3-lP*FW75%s%3JRiu8FvumYkRQWAQN5W6(#@vXrGX4Gh>w zI1P3Aq_iSpb*%hLlG;CV|J7zl8BZ+?py8p7@uI6}YhXA`Qu==nw;{YutFGv(sWUO8L5hJ3ahhjJ~oq;K2u9#psonVAq2 zS#2`7_%J;!0WMmm4~VBpd;!#*{MCW>*Zmr=1K%;S&qqn!diP)5Bx9$hXOdf>lny7B zlW{LZ;|t5+ykBx{7@q`L$`1QA8)mIevwTDnsj zDM3oQrAtDQI=Z{#=sMzE{Jrnzy?<_Zc4zj9neX#`W@kslL4MS3bOnqt1vh@qt2css znVd2X+VC1u!#&K!D9lEOLpSYr8#6`d&82=oq-l1SW87N} z3J?>9el!>O8t>2ZIsW331*wT6Y@-Sufe9PqILX8p!cQ-v_Ai2IM@3IvOh+e-Qnrue zY9q5n8aN}?M;>IjS=kH+#!Q|3h$%m!9j0623F!NB=_2pc(#R9{JMj^~E+Ya*8@kos z8sP82vaF^3B}z@L+3ZE!>~Wek`z3yc3{_OUyq3}T@uE+uiJGR}m|t|kE54yVzkDm= zh%MEZ8y4qwohrcKG-r)xE&oeOyWZQz&%eun_RWg=oW~pCZ&M_Awbq@i78)v`5>l5?ZuIsakb zwVZuXl#W8|Xvi5H0CK~AxDksm!0h47p~B*7G#^y0%2Z@WLRa?grf8}N*)CKu$CmU| zywAF!hw~~Pr-51QJ6Z|t*QKS9$z_)hWx`wp;{`9aRcO}>t40;m=g;^@JVhp= z$Q0_g7h>0c+RdANI9K+-SJvgllxNm`hVeX3Cz%aUY8V(4m7xMn?9VmuXenWM<*v?U ze@Rtss{v^I%(#l;YtW0X@JI=*0yQ9nT)>x-QfSkM*N)_ukd)mH;hgJa1cvEA@-N;` zABTEk?K_6W!Nq{eg4;g5i`*1;?6}1LpcGvmg@6?rGi|HhPq3lagQ5V1VaV=;bRrXvnztcS(t^15*aSq8;`k2Oh^$ zrIdV}Hp6mscg&Tq7FUtFpKm?g5O1#}8A)ZDKLsV9cd~LmTiL7gI~*OE%U;RJml2N& zAs#p1l}2@Mmv1L(4nbp;ut#ZVEFz>Kiq2}Wr+8p0yFh`8uUDQ`nhU;8%&{-1#{n|+ z`HZda2ywTl{kGX{E>s;!Oz~gIb_MfatYh2Vm3zB-u0l@a2%-eEhp1=*&x=|*PX`#% z3a!wxZeFL{2-G|3Tj2eojZoZnl`}E7WW6fKmg4{JWh2>cY<7>0Jc5DmgG`h{`-=NmJzRMe4W?h%{SI-d*ungCDeLBpEKR^f@tXDJSVP=#?XCPDBSyDLhZb&E^S+ z$-S>^{`S6ZM;c!Kedo}F^P~8r|8B3)n1$qceIR|OW&&z6*YN&*h?kB2iNNk)R6J9` zo|qO_jlo?-c<&sN()4zPJG&F`Bb;L)Ii)4T#jfp`bfO(2f4V$E%yLj?+*6Jm6)Z5f zl)g*WIN8H^`jF-@2O({F^@t*em~ehBl7%;jiSZ%H@C~wY_AnuEO+!=TDYnPjF$AIP zdx*kH`g|*0n3|@!o~#1{QeVRLCiU&%_$V>;VvToG*PjlD;6hgKzAJa2YSy!yO`veG z%0k&TGk+9mSiV{JBj-i1g3vlq#gO$p&QZ0bg#~5h%tq#*oR(IQQHv;_z&?pYRZrdT zPq~-7s1Wcd^v@vO0$KT)9E#c;Nu+IR$B>Jin2~%QqqsfVIx}9!&GWc}H*Yy!;77== z?}cvSV|Sh*28@qG<0gT_IboVcqZh_rsJM9*Kam5l+2K|KxP0^r>$}^ZpY~p`y4xXr zd_&AzBUYC>b#6Q$gj|k_a&0p%#@043l|7T+{FuvC$q<_=0bmx%T&>OTA68z6R<-W+ zKa#-B{YsTMQ0rio4fk@1ahH5AAL)wZocWH2Bh;%!@ffBu*3JyXCng8hFpe}o>%e){ z+_gVUG}$IJ#IC2nbNC1J6Zf*kMlV5hWN0Kpy$WL-R4nOOotewxBK6j z@W$_d?bF)P99tFCt5NzF z$F~H83trHPQXQGD4S72W)(5s9^rVIt&Xxw2LX%yQcDTD+Z5xt09qGIqMzd(=3l+ce zG=cAQK4pmOOX*EhTF*}(n@B@C7EToIZvQ?%`|$mttvG_x0q#6?TnJI;^qH2zvyu8Fh|I13+3s! zo|NL?`OlSU(%oQmOs3`7;rU3^nc5|z9)6{MJrr4jZr)aJsLRh;<+Vl+fb**HSSO6` zV8`J4%rwLMc%sO4H5*6Myn;{n$~u`&HFRM2)nSkqkX3!wIq%rkirfQ%8+~wN4*>By zfB81s&W4O}qoj@rQtQ&=wtc9ugn27%xRlN3{I?eF;6v%((Q$bA#qDCMf%C%kx0px9 zRQ}l-e2oxmDCJn1n_ec1d@&S1 zNLdg6Cd(k3_PYcRI)B<5pV5B!7#<>ME7t~X@fb{}fg_UnX6H2<=mgORc8^{AS`u&F zPz!sEsEnmSM?spyr4SIU38#?0kJfk?BYUS}$rkMZWC3-w_^WlD_OU zW_Be)^s(P;xLJNN2^?cZz_9$*KJw4c{>0&=UC0@m4GXGLKWsydi#_>6|3=j9S&O8u z|3!cX#T8zgA+25<5e(HHU^wkeQW!ip6$iE!60v+wJlEi-gs)4^E|1)&lWHH^yxS7l zG-=#6i0jQ!UEVBw)8%koC2h&Al2=(t1L7jSHU=?yBIacFl7^JuTix2b`8*dAWY%M~ z>8}^lExoAQ+i0n!+QUwJ*r~5|q&vnhoBMVy>JKYem8 z^mkBEKi1x2UtH6hTET+56thhk_qeylDZ5K3ZVYblo`?Y!$m=TA>lsMeO+X;K!r-et z5mEM5L>~^_saz=_3E#;+KaY~%6Gil6e#xEC+-o|$zSVU1$G`=JC2h)u=M%cSbl%koIf~dEjq;c;19DI_rP*=f%e;K2=|E=kTZNI22Zaa=Yx1O>rNDc`>*R7x<(UmI&G8FgrDMuAn$g6Z#?l{? z(Q8&Cf`%|Gd@mh_gC(cm=aeM6wHyAo6(b;!X-6E=TT|x9!HHnA++aGlWN6N3&b2ud zXd(?@v{Cv{TOVhDul;r&PMBvgo{ z1Hj_#c(GIgG5{Pq)Y$VD&L zB79&i>(axIcJ7#yUxdSmf$E1x=6J4QfOR7*(}`SG=+jKQL*ma;lLRy-N=9OxtH`=W zqpy!U>)7nHr9ZdzA2auvDi){m|K=>XwiCiv)2O2VG_A@B#daRgN|;`MHnvas@Zt~7 zKea@yE~7kl@}<+pLfx{gsjt8$(^_s<#wpue+C zI6dcDV|0?meoZUt+l51xwY6ML5kiqV--MoKeA?|XAQF=?8J8bBFr@K0!x{t`NY0Y48`P32tTN@mi zS&%&Ah}oK_0C@zhA6|FFE=g{M589b28mBa_R2gh*+Kr{w({STO+nWy`f?R-R*~1R` zVD>fFfxk)P!q<$zhtemtd1ew}1)CakyUArbQeC&_<(6v?$0Wt3SdbqVfU~=k;FR%M z6C3O(Om3P^_Qm@4f0<;bO$tX$7FPf2ZMIgRp$)o+O@mnn(P zbnk=t-bm|(xK`+nf6*BM4IehjuRJ)#^$At$ApNVpa)D5wtrHY%9U&o9?(n{Xe=7$t5W6c#x}DVPr6KflSat=*Db2oZzPr!%3B}EjHIugP5sdt zC~4*{a-oHei(rlM5K9T=gAdG)A>=D{D#)|^7_L#U2&Dj44FW9&}E0dn<4e0V3|N|LVKsj5?+op^G6&tVqlQ3mKknBO9+ zUNc;4W+>pG{7v+XC1O+da692c$wM`lRpH=jCxm=hRFW8cTQwy~-&^yQy63+q2hfUl zVM01OWmIQ|zH0`IH`9k^FNB9Hry2mivYs_UAjcbnJyEF7->9htG&~;(n!RX8arDG;9L% z+5-iIzrk>8I%TqcD$r!m1 zv=9c^Lm!Q=GaS3*q@;|zyzlr*62%VUjdbUY|NQ;?D^K|=1g#V{Oagf)N}8cJr5;ehhpZ!0q)V45zAuxp^3VmqU_$YiX`#NRK zapvG&IX?)D{&l|JEv>uAug!sh7cXDqgH0KMf8(TNMjjUU0|F=nk|)F@x0hUyj9)D` z9u>`e@`Q}d2dAe=r#@8FF4#z60%hr?0iNIzzDy$Wb3)T-+t235{$)&jE^37>`M?ca z@lTVE=V6bM(zRrB?4#X_aLKb5*#|wv*8PcN3MonKKRV&I{7A9V>7*irMQe3Oi`nxa zPXb6m(JKe{zxiEV;sJ|`GK`T4Tl)j`YrDrkjr75GZyHn$9->W(W$N1EVRGy#q&1MY z#a-+SG#4xoV^VZJTe0{G3&d#k0jxH(h)5PZ_8c5m%Pt4fMNLL1;r3)c+}%7E#@^R! zJwQtY{GyBkUA@wrOp&a=BVf`SvRKZk$@l6kIk!Ru)a+ZthYR=J9QoF29@ax=NNu~# zo8`R@gKjvz-e-J*GGP`GQxi?$dN2KHGp7qj{Y=TCohYvlD(;-(TS@Fd#9wTAUEK!{ z^CRAhmlnP1=q(t@@6K95Q0tGM5YZB0aWd`Y93WqkRy z;yaOrBuYzBt#WI>2Ap;EUL!*yGNlyapo}2BVKxqq_#1OydU+g|#BU#mISiE&=}-yh zcB^=-7p5+_cAyCyKx$xM*GxM8JeYs5)Ag;D*e&4)jBU(l5}n8N+&ClzPo#C~XVO2h zrnJ6W;@_D4G&6H{+0arum}JspUuIF)w|%^KaO!pFBK&^(Db>&KJ&I1t%VGj0;Wx3G zOR3wgowtQ>=PqxHntod-?DXcwCz5eN{`YK9gMc*yBaOL_kB}UqTc!m z&9#D+1K{|j5`5Ag(%6j((evJTe|?tJT5BuWR{D1Fq@t{%>`*wqDKjE4dNlj5-Z`}t z%yek`^uWA+Dv2HOq!Q(qARjA-PJDON)CVnm?(icCJ)S{t>+#t_Fn7=q51Lx>ws_1| z+iK3PY_?qK*!h0Rbt|YyA#5)inC|18vm>=42Tq7{k?@x4sMg&^kK1B8f7^*U@A?kV zXi5Mx^yLjSi}vMBj-0jxaK$?;^#1C(EpR1o3?5J3Y|sbrsC!y3yl5;(&?9Tb)CV(l zTOaKY{@wO~mr+B7E6fGI648+cnzXu7K>}EJ@&xIB;sPgvh13gdRP;N?J_Vo1Bh43C@wMhY-A#g3b$#Dwond{-!l=-n; z#vHWx#$Ur{)s9)JC5TQI3}`UI?;|EaDlFnsoq=)dvt#vXO93Av8>x$KMnKM;g{Zgl zlY@07hSftE>pA=R!btKAWq9+#z6^>iAsP|Gft8f5*=M^!wl*qk;M(N)k=W(rhRnRa zfJ#TRMVojX)P)?XZ*pGyjA7lGPtc2Hnx1gwDhJl$GasZ3);DlW?{9~b`!x1Ih;PZc zKYdqr?8wPJNa~LRk2!DlpVb~=zCe+VJ`YP$vrA^Eu*s65`w#uAo?)KPF~9!$F*-;O zd8QFTJ>>!5_Ld>hDknOh|GUS>{+wbab%30-*&@yqn*Pj6cKKK?&|yeA%T3fF210}; zjRpf{KiI`z>=&DGW^z1yMc|YMNNDuFqmg% zjDN-j;Tf@U^T#B>m@nT$l*N<{dPpcDeNljafj=D#`M6z0>RMLRsg6Y7&Z1sF>gf^k z!U)MfS~gPCtEShvrlP2)kcTwA>rvF{iE|3KX{wp>wsAtqdp?$Ux8;@0o|U?|sDdJHUA)1)c@y1OGz${}U8e+^-BM z@oewrM;?>>EZ$XZz1zo;?v(}|2knh#Lue8cGZ|Jm3a#?vrK~rf!#xs$)-c6T!)VSC>dcD z!gOAMKpYcnJBatt9y8||?&)dCDpuhe?F1W}Vy!Top3Ki$Pn}PwLwHN%))J|Lo2uQ| zlqiZFE{mL-LQxZ#mkfPwCU@pP(+4bL>oDzV~-mGRL z{P-b32m=b?dJRRSp-9czRjUZ~HByg%rBmha)3H6AtnvAv#*CQO-5IfOBvXZ)8I!}d zLbyjwn4L!{7dit9P-`ps2r*$s3(#mnBhzA#%hE|hR^`ZND)QTuZp)Xwr;(Z2t6ljH zukhgOxv&lNn0urDUh2FAggk}l%?66QQ+SJ@Qj=yqVgpBFhPSOJ^5i~6bv5mT)Hz$9 zPiqAnr*`HG&T3p`EZ>>kEc~9@;gZu@zwrjh?OV%l$oN zY5_W_@QLL=d-HtKk+8H{c=K2CvnhWN!QiPTa77(tc!LRKT0UuXJ>zrCe(8eDq$z-_ zowXyC8YU;Uv0hdzY~B0K(4`(ZqE+*L>QZsoxQUi6TD%1}LHn15rva+=PQ!77@@CVs zy+rFLJl6BcHs~2nT2W#Qey}&AXfJGS<#E`_2kqHaxX$*#W&UTwGZ~FmG92-m7IqUq zyL0#-=>R_HBIaB4i?oM^Hbtu13F(MMXn(q>dfh^MOr_J#hcPY~n1<9KMTzULip78_ zqO|3)w4r+%`g`@|EnGBEueXMyf6XAAnlWIvaw=z9YoII`sGF*uh)Y~xD9QO2kth)0 zP`EyST#EAMv4l&Tb5yxJw9*R5VwJjvEaPX4j&({m4zC(gr8XIQPv)&jkj0l{R zBVLN$w%l5}itx+-lm~&tuF}2W^kdDQYyC5NpI^tS8kz)!deI+=?J661%Ziq15bT zuR9}zbA!Ojffi4gCG=0Ie-zQ?TKtST`R1p~Ir7PYpnbcmVDyc$mx1BJTN3dskd}0u ztvo(1FLxCMxr)kJG5}7g1GNKWn7x0XrQ!;CNjLL6Ej5VA@e-F?>x0vcQ^eePiB}I# z>C{A6DvRTVokVwJ-+7n~imS~phnd$-BrUym67OsM%2+t-e%t>EE9*?sZ{MwC<4O^c zaYI%whHbybM957CliG38m%B;fuknnpBph{RSBob-DQ6k30K9^eVy{H8Lw4hCw5KEt zJSevo+vo6Yr56KVY~LsxPuhR}%nM;)TsnW+8d2?fs<;D!oGT(uB%r(7;080(Xl+?I zZ1@w0?cuJ)X46oBJUxvcmzCd~H%r>+Eeqz4EHX=Rx1*0c{^u!<6=g4M-q$)AxJ52P zxd*=&EvH$#0f~s>h_|#Yp3_YvX~p}Fmib_@xfb^$FILwwn~7SEqH1uXVov=M+WOBQ zPhD*K{$R#Tytf}1Ms;9OQ&Z`cm}*Vf<6YHjU$Oq9oVrnRN)H4Z-oFpA5`^t@$7x?D z9e;4uxE;N+Pu}Jz@G(}o5!!tz#&Kl+LCxjI^pZGGl@fEVcXHs9mhkzlWV9}-J28qh z8iQr);fkeb2HZ$IysIkT;^_=IQN!fgjOT$woVzsFRMN_KUD)^C(6YACAjd9ucoQk* zPN3J;=ookkHg7FjE}zQSS)Z<2_)SXK+5|O1cc!vf3c;Tx$OtC4%Y|U@>CU_RBn|Tc zyvf1YwW04@BM2ijnfvWP7S86R332fzyYky4YTmbn$HJH;sCMsd!zx^XB-YYoZVp-JZnX{?zM)>iWiJ0n5!{#KajpzKM9N z&6fPpnaEc$OkYKQro*b?>__vZUucC@EG?fm^y9=5(XWU8o-&wE5|0Fc(FO^$5se%l8shwZjD+(P$1?uY7qKNwj~R}e>0 zbwjpEk{;PiGW{u5bgp)n2X}Z4@D}~}N9pDzk}Ii}<*>Yxyf-)YKHe#4PUQq$U44Kw zXW-v0jie_Ws(AviI@J?DzHdASyf1Qto=mY##<^;Y6EADVR%Boq_&(3;mSAO^;IaT{ zGEl+U3x>4L43pU3PhyH=%`aaDqBKs#6EN7SSr!-tq`wd?J^3cJeYp$z#|e&EVk6qb zru{VifB6<4x;)6gF3|t6v<|L+T$0dx2}mU9%-}HzYi*PwM6-V-bI(G-c_M}T=_6RH z)IDMT|4L}6Qa10MC1^6WIPb~TD|XiSgy&l?H8klwJSib0AX{nJ9jW@Am+i??Yl2h+ ztD-#(s;uN0{a~U^U6i=oakv42ROBp3rCD%-w5CJ!`vuUR$;FOYq+g%=;vLJRN{`-v zf_*{fd2a2KnI{vb;Gk61*Y(X_yl0U9+~_?xVQM{vi2_{c_IWiOEi@eZssBa!3z4U0 ztl>!y;NVyL??D#(E7I4J!Wo2?6@>rugP4GJNgKVe#YmxJMbIuO1qUGYafX`nL9rCpa!U+Ji0;M(uelvgiVnb)Y`IC6>*NK!CV5ajSs$Z|^Ux2fQa-a^VfPV@#^rt5$f%vd)td-Kysow*s<91E8F;W! zciLN2$bIh{v-BSe^lA>RS;9VHMgwKM%c4T>$!nfmr28Tfk9YMt#dVDNq2+Q;5E-Bi zxsQ5=Akt2Si2pM@p>q3gHReF&q%Mt$r4q+zRsLlboY`zbkCP{R@pk|27>t;Dwn>A62D6EdlV$E(W4|tK z(()*6N{i3?uTKH(?)n*d7DXU{%Jno+JR@>n+s>&C*k=6}FbF^UrEZ|IY`8nkoUu)nK!qLuoRFS>b+@8+ZIW2u zw-9e@fVw7};73k*))iK9Z`PI&*d3+1U@)UM2K2=M&UZSjK@hg3xt(qLD)&L_(I!xZ zp|43!a7Z+<9vE#X8N>&?^dY8ivB6ei08?M3+1r<&m}wN3t5{lODoVLc^0`a8c|l~h z{0hRcjVKSw1&GI8=Tl&^NO;=`gUPhb&m`u{Q9x!=qrYrafrz9a79g?~9vB@@N(t=H zZrkap`lgYuDtSU6g{B;nCunXmU{z?)3|-7@Jh{Q~+ewm)?bJwIMJLn0Z2junoxitV zuSrZJSj7fds4jH>4}B?%UXkG0qPEn zRlom(&)~SsR?~NShS;b8^jK|lTD5J7h34aQjpEhf8#55lD!J`#pd|MR_YxPbiHXVW vzQ6vz8O;NN{W9piZULD83u6D5Qr@BFDt^{&U6p2qfWMb=uVqVR4E+8F_k?*E diff --git a/icons/obj/doors/blastdoor_brass.dmi b/icons/obj/doors/blastdoor_brass.dmi new file mode 100644 index 0000000000000000000000000000000000000000..0888fe5f615036eee9e70564d515741ccb34056d GIT binary patch literal 2406 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 aJyxc Date: Sat, 14 Mar 2020 18:55:36 -0400 Subject: [PATCH 02/10] updates the host's access to the new deffine access list --- code/modules/awaymissions/corpse.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 7eab4133ca35..64c715bd9da7 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -527,7 +527,7 @@ to take them to the station for revival or to do some light advertising for the resort." assignedrole = "Resort Host" id_job = "Resort Host" - id_access_list = list(access_bar, access_kitchen) + id_access_list = list(ACCESS_BAR, ACCESS_KITCHEN) outfit = /datum/outfit/resort_host /datum/outfit/resort_host From 327ed4f5ae300703435aa0b541fb9089138e4c19 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Wed, 25 Mar 2020 13:10:09 -0400 Subject: [PATCH 03/10] Merge branch 'master' into revamp_beach_gateway --- .editorconfig | 14 + .github/workflows/label_merge_conflicts.yml | 13 + .gitignore | 3 +- .travis.yml | 2 +- .vscode/extensions.json | 3 +- .../lavaland_surface_syndicate_base1.dmm | 6 +- _maps/map_files/cyberiad/cyberiad.dmm | 49 +- code/ATMOSPHERICS/atmospherics.dm | 690 +- .../binary_devices/binary_atmos_base.dm | 310 +- .../components/binary_devices/circulator.dm | 250 +- .../components/binary_devices/dp_vent_pump.dm | 538 +- .../components/binary_devices/passive_gate.dm | 384 +- .../components/binary_devices/pump.dm | 522 +- .../components/binary_devices/volume_pump.dm | 514 +- .../components/omni_devices/_omni_extras.dm | 2 +- .../components/omni_devices/omni_base.dm | 2 +- .../components/trinary_devices/filter.dm | 534 +- .../components/trinary_devices/mixer.dm | 466 +- .../trinary_devices/trinary_base.dm | 426 +- .../components/trinary_devices/tvalve.dm | 2 +- .../unary_devices/oxygen_generator.dm | 2 +- .../components/unary_devices/tank.dm | 2 +- .../components/unary_devices/thermal_plate.dm | 2 +- .../components/unary_devices/unary_base.dm | 2 +- code/ATMOSPHERICS/datum_icon_manager.dm | 38 +- code/ATMOSPHERICS/datum_pipeline.dm | 552 +- code/ATMOSPHERICS/pipes/cap.dm | 2 +- code/ATMOSPHERICS/pipes/manifold.dm | 4 +- code/ATMOSPHERICS/pipes/manifold4w.dm | 4 +- code/ATMOSPHERICS/pipes/simple/pipe_simple.dm | 4 +- .../pipes/simple/pipe_simple_he.dm | 2 +- .../pipes/simple/pipe_simple_hidden.dm | 2 +- .../pipes/simple/pipe_simple_insulated.dm | 2 +- .../pipes/simple/pipe_simple_visible.dm | 2 +- code/LINDA/LINDA_fire.dm | 8 +- code/LINDA/LINDA_system.dm | 8 +- code/LINDA/LINDA_turf_tile.dm | 12 +- code/__DEFINES/_readme.dm | 2 +- code/__DEFINES/_tick.dm | 2 +- code/__DEFINES/access.dm | 222 +- code/__DEFINES/admin.dm | 2 +- code/__DEFINES/antagonists.dm | 1 + code/__DEFINES/atmospherics.dm | 2 +- code/__DEFINES/bots.dm | 2 +- code/__DEFINES/callbacks.dm | 2 +- code/__DEFINES/clothing.dm | 2 +- code/__DEFINES/colors.dm | 9 + code/__DEFINES/components.dm | 2 +- code/__DEFINES/crafting.dm | 2 +- code/__DEFINES/dna.dm | 57 + code/__DEFINES/game.dm | 2 +- code/__DEFINES/gamemode.dm | 2 +- code/__DEFINES/genetics.dm | 3 +- code/__DEFINES/hydroponics.dm | 2 +- code/__DEFINES/is_helpers.dm | 6 +- code/__DEFINES/job.dm | 128 +- code/__DEFINES/js.dm | 182 +- code/__DEFINES/layers.dm | 2 +- code/__DEFINES/lighting.dm | 2 +- code/__DEFINES/logs.dm | 6 + code/__DEFINES/mecha.dm | 2 +- code/__DEFINES/medal.dm | 2 +- code/__DEFINES/misc.dm | 10 +- code/__DEFINES/mobs.dm | 2 +- code/__DEFINES/move_force.dm | 2 +- code/__DEFINES/pda.dm | 2 +- code/__DEFINES/qdel.dm | 2 +- code/__DEFINES/radio.dm | 120 +- code/__DEFINES/reagents.dm | 2 +- code/__DEFINES/role_preferences.dm | 162 +- code/__DEFINES/rolebans.dm | 8 +- code/__DEFINES/shuttle.dm | 2 +- code/__DEFINES/sight.dm | 2 +- code/__DEFINES/sound.dm | 2 +- code/__DEFINES/station_goals.dm | 2 +- code/__DEFINES/status_effects.dm | 2 +- code/__DEFINES/subsystems.dm | 2 +- code/__DEFINES/typeids.dm | 2 +- code/__DEFINES/vv.dm | 2 +- code/__HELPERS/AnimationLibrary.dm | 2 +- code/__HELPERS/_logging.dm | 48 +- code/__HELPERS/_string_lists.dm | 24 +- code/__HELPERS/cmp.dm | 2 +- code/__HELPERS/constants.dm | 2 +- code/__HELPERS/experimental.dm | 2 +- code/__HELPERS/files.dm | 120 +- code/__HELPERS/game.dm | 1078 +-- code/__HELPERS/global_lists.dm | 180 +- code/__HELPERS/icon_smoothing.dm | 2 +- code/__HELPERS/icons.dm | 1926 ++--- code/__HELPERS/lists.dm | 1644 ++-- code/__HELPERS/maths.dm | 286 +- code/__HELPERS/matrices.dm | 2 +- code/__HELPERS/mobs.dm | 1190 +-- code/__HELPERS/names.dm | 564 +- code/__HELPERS/qdel.dm | 2 +- code/__HELPERS/sanitize_values.dm | 104 +- code/__HELPERS/sorts/InsertSort.dm | 2 +- code/__HELPERS/sorts/MergeSort.dm | 2 +- code/__HELPERS/sorts/TimSort.dm | 2 +- code/__HELPERS/text.dm | 1238 +-- code/__HELPERS/time.dm | 16 +- code/__HELPERS/traits.dm | 2 +- code/__HELPERS/type2type.dm | 786 +- code/__HELPERS/typelists.dm | 2 +- code/__HELPERS/unique_ids.dm | 4 +- code/__HELPERS/unsorted.dm | 39 +- code/_compile_options.dm | 2 +- code/_globalvars/configuration.dm | 70 +- code/_globalvars/database.dm | 11 - code/_globalvars/game_modes.dm | 15 +- code/_globalvars/genetics.dm | 106 +- code/_globalvars/lists/devil.dm | 4 +- code/_globalvars/lists/names.dm | 2 +- code/_globalvars/lists/objects.dm | 2 +- code/_globalvars/lists/reagents.dm | 2 +- code/_globalvars/lists/typecache.dm | 2 +- code/_globalvars/logging.dm | 25 +- code/_globalvars/mapping.dm | 73 +- code/_globalvars/misc.dm | 116 +- code/_globalvars/sensitive.dm | 12 + code/_globalvars/traits.dm | 2 +- code/_globalvars/unused.dm | 1 - code/_onclick/adjacent.dm | 2 +- code/_onclick/ai.dm | 6 +- code/_onclick/click.dm | 2 +- code/_onclick/click_override.dm | 2 +- code/_onclick/hud/ai.dm | 2 +- code/_onclick/hud/blob_overmind.dm | 2 +- code/_onclick/hud/bot.dm | 2 +- code/_onclick/hud/constructs.dm | 2 +- code/_onclick/hud/guardian.dm | 188 +- code/_onclick/hud/hud.dm | 2 +- code/_onclick/hud/other_mobs.dm | 2 +- code/_onclick/hud/parallax.dm | 2 +- code/_onclick/hud/picture_in_picture.dm | 2 +- code/_onclick/hud/plane_master.dm | 2 +- code/_onclick/hud/slime.dm | 2 +- code/_onclick/hud/swarmer.dm | 194 +- code/_onclick/overmind.dm | 2 +- code/controllers/configuration.dm | 1794 ++--- code/controllers/controller.dm | 2 +- code/controllers/failsafe.dm | 204 +- code/controllers/globals.dm | 2 +- code/controllers/master.dm | 2 +- code/controllers/subsystem.dm | 2 +- code/controllers/subsystem/acid.dm | 2 +- code/controllers/subsystem/afk.dm | 2 +- code/controllers/subsystem/air.dm | 34 +- code/controllers/subsystem/alarm.dm | 60 +- code/controllers/subsystem/assets.dm | 2 +- code/controllers/subsystem/chat.dm | 2 +- code/controllers/subsystem/events.dm | 598 +- code/controllers/subsystem/garbage.dm | 2 +- code/controllers/subsystem/holiday.dm | 62 +- code/controllers/subsystem/idlenpcpool.dm | 2 +- code/controllers/subsystem/jobs.dm | 14 +- code/controllers/subsystem/lighting.dm | 2 +- code/controllers/subsystem/mapping.dm | 12 +- code/controllers/subsystem/medals.dm | 2 +- code/controllers/subsystem/mobs.dm | 2 +- code/controllers/subsystem/nightshift.dm | 6 +- code/controllers/subsystem/npcpool.dm | 2 +- .../subsystem/processing/fastprocess.dm | 2 +- code/controllers/subsystem/processing/obj.dm | 2 +- .../subsystem/processing/processing.dm | 2 +- code/controllers/subsystem/radio.dm | 198 +- code/controllers/subsystem/shuttles.dm | 2 +- code/controllers/subsystem/sun.dm | 2 +- code/controllers/subsystem/ticker.dm | 1066 +-- code/controllers/subsystem/timer.dm | 2 +- code/controllers/subsystem/vote.dm | 802 +- code/controllers/subsystem/weather.dm | 2 +- code/controllers/verbs.dm | 208 +- code/datums/ai_laws.dm | 640 +- code/datums/beam.dm | 2 +- code/datums/browser.dm | 2 +- code/datums/cache/air_alarm.dm | 8 +- code/datums/cache/apc.dm | 4 +- code/datums/cache/cache.dm | 2 +- code/datums/cache/crew.dm | 2 +- code/datums/cache/powermonitor.dm | 12 +- code/datums/click_intercept.dm | 2 +- code/datums/components/_component.dm | 2 +- code/datums/components/caltrop.dm | 2 +- code/datums/components/decal.dm | 2 +- code/datums/components/material_container.dm | 2 +- code/datums/components/spawner.dm | 2 +- code/datums/components/squeak.dm | 2 +- code/datums/datacore.dm | 1304 +-- code/datums/datum.dm | 2 +- code/datums/datumvars.dm | 2774 +++---- code/datums/diseases/_disease.dm | 2 +- code/datums/diseases/advance/advance.dm | 23 +- code/datums/diseases/advance/presets.dm | 2 +- .../datums/diseases/advance/symptoms/beard.dm | 2 +- .../diseases/advance/symptoms/choking.dm | 2 +- .../datums/diseases/advance/symptoms/cough.dm | 2 +- .../datums/diseases/advance/symptoms/dizzy.dm | 2 +- .../datums/diseases/advance/symptoms/fever.dm | 2 +- .../diseases/advance/symptoms/flesh_eating.dm | 2 +- .../diseases/advance/symptoms/headache.dm | 2 +- .../diseases/advance/symptoms/itching.dm | 2 +- .../diseases/advance/symptoms/sensory.dm | 2 +- .../diseases/advance/symptoms/shedding.dm | 2 +- code/datums/diseases/advance/symptoms/skin.dm | 2 +- .../diseases/advance/symptoms/sneeze.dm | 2 +- .../diseases/advance/symptoms/symptoms.dm | 4 +- .../diseases/advance/symptoms/vision.dm | 2 +- .../diseases/advance/symptoms/weight.dm | 2 +- .../datums/diseases/advance/symptoms/youth.dm | 2 +- code/datums/diseases/berserker.dm | 2 +- code/datums/diseases/cold.dm | 2 +- code/datums/diseases/critical.dm | 80 +- code/datums/diseases/food_poisoning.dm | 2 +- code/datums/diseases/kingstons.dm | 2 +- code/datums/diseases/kuru.dm | 2 +- code/datums/diseases/magnitis.dm | 2 +- code/datums/diseases/pierrot_throat.dm | 2 +- code/datums/diseases/retrovirus.dm | 2 +- code/datums/dog_fashion.dm | 2 +- .../helper_datums/construction_datum.dm | 488 +- code/datums/helper_datums/events.dm | 136 +- code/datums/helper_datums/global_iterator.dm | 304 +- code/datums/helper_datums/map_template.dm | 26 +- code/datums/helper_datums/teleport.dm | 474 +- code/datums/helper_datums/topic_input.dm | 120 +- code/datums/hud.dm | 14 +- code/datums/log_record.dm | 37 + code/datums/log_viewer.dm | 232 + code/datums/looping_sounds/looping_sound.dm | 2 +- .../datums/looping_sounds/machinery_sounds.dm | 2 +- code/datums/looping_sounds/thermal_drill.dm | 2 +- code/datums/mind.dm | 3652 ++++----- code/datums/mixed.dm | 80 +- code/datums/mutable_appearance.dm | 2 +- code/datums/outfits/outfit_admin.dm | 4 +- code/datums/outfits/plasmamen.dm | 2 +- code/datums/outfits/vv_outfit.dm | 2 +- code/datums/periodic_news.dm | 17 +- code/datums/radio.dm | 228 +- code/datums/recipe.dm | 262 +- code/datums/ruins/lavaland.dm | 2 +- code/datums/spawners_menu.dm | 2 +- code/datums/spell.dm | 998 +-- code/datums/spells/area_teleport.dm | 196 +- code/datums/spells/banana_touch.dm | 12 +- code/datums/spells/cluwne.dm | 18 +- code/datums/spells/conjure.dm | 140 +- code/datums/spells/dumbfire.dm | 168 +- code/datums/spells/emplosion.dm | 30 +- code/datums/spells/ethereal_jaunt.dm | 208 +- code/datums/spells/explosion.dm | 30 +- code/datums/spells/fake_gib.dm | 2 +- code/datums/spells/genetic.dm | 74 +- code/datums/spells/horsemask.dm | 96 +- code/datums/spells/inflict_handler.dm | 114 +- code/datums/spells/knock.dm | 114 +- code/datums/spells/lichdom.dm | 2 +- code/datums/spells/lightning.dm | 2 +- code/datums/spells/magnet.dm | 2 +- code/datums/spells/mime.dm | 1 + code/datums/spells/mime_malaise.dm | 6 +- code/datums/spells/mind_transfer.dm | 164 +- code/datums/spells/projectile.dm | 172 +- code/datums/spells/rathens.dm | 4 +- code/datums/spells/shapeshift.dm | 2 +- code/datums/spells/trigger.dm | 58 +- code/datums/spells/turf_teleport.dm | 80 +- code/datums/spells/wizard.dm | 894 +-- code/datums/statclick.dm | 2 +- code/datums/status_effects/buffs.dm | 6 +- code/datums/status_effects/debuffs.dm | 4 +- code/datums/status_effects/gas.dm | 2 +- code/datums/status_effects/neutral.dm | 2 +- code/datums/status_effects/status_effect.dm | 2 +- code/datums/supplypacks.dm | 2 +- .../datums/weather/weather_types/ash_storm.dm | 4 +- .../weather/weather_types/radiation_storm.dm | 8 +- code/datums/wires/apc.dm | 2 +- code/datums/wires/autolathe.dm | 2 +- code/datums/wires/camera.dm | 2 +- code/datums/wires/mulebot.dm | 2 +- code/datums/wires/nuclearbomb.dm | 4 +- code/datums/wires/particle_accelerator.dm | 2 +- code/datums/wires/robot.dm | 2 +- code/datums/wires/suitstorage.dm | 2 +- code/datums/wires/syndicatebomb.dm | 2 +- code/datums/wires/tesla_coil.dm | 2 +- code/datums/wires/vending.dm | 2 +- code/datums/wires/wires.dm | 16 +- code/defines/procs/AStar.dm | 368 +- code/defines/procs/announce.dm | 8 +- code/defines/procs/dbcore.dm | 405 +- code/defines/procs/radio.dm | 4 +- code/defines/procs/records.dm | 6 +- code/defines/procs/statistics.dm | 326 +- code/defines/vox_sounds.dm | 4 +- code/game/area/Dynamic areas.dm | 2 +- code/game/area/Space Station 13 areas.dm | 28 +- code/game/area/ai_monitored.dm | 52 +- code/game/area/areas.dm | 906 +-- code/game/area/areas/depot-areas.dm | 2 +- code/game/area/areas/mining.dm | 2 +- code/game/area/areas/ruins/lavaland.dm | 2 +- code/game/atoms.dm | 1768 ++--- code/game/atoms_movable.dm | 1130 +-- code/game/data_huds.dm | 10 +- code/game/dna/dna2.dm | 70 +- code/game/dna/dna2_domutcheck.dm | 4 +- code/game/dna/dna2_helpers.dm | 6 +- code/game/dna/genes/disabilities.dm | 28 +- code/game/dna/genes/goon_disabilities.dm | 18 +- code/game/dna/genes/goon_powers.dm | 18 +- code/game/dna/genes/monkey.dm | 4 +- code/game/dna/genes/powers.dm | 26 +- code/game/dna/genes/vg_disabilities.dm | 4 +- code/game/dna/genes/vg_powers.dm | 6 +- code/game/gamemodes/blob/blob.dm | 422 +- code/game/gamemodes/blob/blob_finish.dm | 84 +- code/game/gamemodes/blob/blob_report.dm | 206 +- code/game/gamemodes/blob/blobs/blob_mobs.dm | 2 +- code/game/gamemodes/blob/blobs/core.dm | 288 +- code/game/gamemodes/blob/blobs/factory.dm | 58 +- code/game/gamemodes/blob/blobs/node.dm | 72 +- code/game/gamemodes/blob/blobs/shield.dm | 106 +- code/game/gamemodes/blob/blobs/storage.dm | 2 +- code/game/gamemodes/blob/powers.dm | 8 +- code/game/gamemodes/blob/theblob.dm | 484 +- code/game/gamemodes/changeling/changeling.dm | 664 +- .../gamemodes/changeling/evolution_menu.dm | 8 +- .../changeling/powers/chameleon_skin.dm | 16 +- .../gamemodes/changeling/powers/humanform.dm | 4 +- .../gamemodes/changeling/powers/lesserform.dm | 2 +- .../gamemodes/changeling/powers/panacea.dm | 2 +- .../gamemodes/changeling/powers/transform.dm | 2 +- .../game/gamemodes/changeling/traitor_chan.dm | 2 +- code/game/gamemodes/cult/cult.dm | 793 +- code/game/gamemodes/cult/cult_items.dm | 754 +- code/game/gamemodes/cult/cult_objectives.dm | 10 +- code/game/gamemodes/cult/cult_structures.dm | 6 +- code/game/gamemodes/cult/ritual.dm | 640 +- code/game/gamemodes/cult/runes.dm | 16 +- code/game/gamemodes/cult/talisman.dm | 872 +- code/game/gamemodes/devil/devilinfo.dm | 28 +- code/game/gamemodes/devil/game_mode.dm | 18 +- code/game/gamemodes/extended/extended.dm | 28 +- code/game/gamemodes/game_mode.dm | 1044 +-- code/game/gamemodes/heist/heist.dm | 15 +- code/game/gamemodes/intercept_report.dm | 468 +- .../gamemodes/malfunction/Malf_Modules.dm | 1516 ++-- code/game/gamemodes/meteor/meteor.dm | 122 +- code/game/gamemodes/meteor/meteors.dm | 636 +- .../abduction/abductee_objectives.dm | 2 +- .../miniantags/abduction/abduction.dm | 7 +- .../miniantags/abduction/abduction_outfits.dm | 2 +- .../gamemodes/miniantags/abduction/gland.dm | 12 +- .../miniantags/abduction/machinery/camera.dm | 4 +- .../miniantags/abduction/machinery/console.dm | 2 +- .../abduction/machinery/dispenser.dm | 2 +- .../abduction/machinery/experiment.dm | 2 +- .../miniantags/abduction/machinery/pad.dm | 4 +- .../gamemodes/miniantags/borer/borer_event.dm | 2 +- .../gamemodes/miniantags/borer/borer_html.dm | 2 +- .../gamemodes/miniantags/bot_swarm/swarmer.dm | 4 +- .../miniantags/bot_swarm/swarmer_event.dm | 2 +- .../miniantags/guardian/types/assassin.dm | 2 +- .../miniantags/guardian/types/charger.dm | 2 +- .../miniantags/guardian/types/healer.dm | 2 +- .../miniantags/guardian/types/lightning.dm | 2 +- .../miniantags/guardian/types/protector.dm | 2 +- .../miniantags/guardian/types/standard.dm | 2 +- code/game/gamemodes/miniantags/morph/morph.dm | 2 +- .../gamemodes/miniantags/morph/morph_event.dm | 6 +- .../revenant/revenant_spawn_event.dm | 2 +- code/game/gamemodes/nuclear/nuclear.dm | 1073 +-- .../gamemodes/nuclear/nuclear_challenge.dm | 2 +- code/game/gamemodes/nuclear/nuclearbomb.dm | 944 +-- code/game/gamemodes/nuclear/pinpointer.dm | 813 +- code/game/gamemodes/objective.dm | 1618 ++-- code/game/gamemodes/revolution/revolution.dm | 899 +-- code/game/gamemodes/sandbox/h_sandbox.dm | 322 +- code/game/gamemodes/sandbox/sandbox.dm | 42 +- code/game/gamemodes/scoreboard.dm | 144 +- code/game/gamemodes/setupgame.dm | 328 +- code/game/gamemodes/shadowling/shadowling.dm | 9 +- .../shadowling/shadowling_abilities.dm | 2 +- .../gamemodes/shadowling/shadowling_items.dm | 2 +- .../special_shadowling_abilities.dm | 6 +- code/game/gamemodes/traitor/traitor.dm | 294 +- code/game/gamemodes/vampire/traitor_vamp.dm | 2 +- code/game/gamemodes/vampire/vampire.dm | 5 +- code/game/gamemodes/vampire/vampire_powers.dm | 4 +- code/game/gamemodes/wizard/artefact.dm | 10 +- code/game/gamemodes/wizard/raginmages.dm | 2 +- code/game/gamemodes/wizard/rightandwrong.dm | 2 + code/game/gamemodes/wizard/soulstone.dm | 759 +- code/game/gamemodes/wizard/spellbook.dm | 2009 ++--- code/game/gamemodes/wizard/wizard.dm | 567 +- code/game/jobs/access.dm | 1031 ++- code/game/jobs/job/central.dm | 2 +- code/game/jobs/job/civilian.dm | 56 +- code/game/jobs/job/engineering.dm | 306 +- code/game/jobs/job/job.dm | 547 +- code/game/jobs/job/medical.dm | 636 +- code/game/jobs/job/security.dm | 4 +- code/game/jobs/job/silicon.dm | 80 +- code/game/jobs/job/supervisor.dm | 9 +- code/game/jobs/job/support.dm | 18 +- code/game/jobs/job/syndicate.dm | 4 +- code/game/jobs/job_exp.dm | 24 +- code/game/jobs/job_objective.dm | 2 +- code/game/jobs/job_objectives/science.dm | 2 +- code/game/jobs/job_scaling.dm | 2 +- code/game/jobs/jobs.dm | 295 +- code/game/jobs/whitelist.dm | 198 +- code/game/machinery/Beacon.dm | 2 +- code/game/machinery/Freezer.dm | 664 +- code/game/machinery/OpTable.dm | 310 +- code/game/machinery/PDApainter.dm | 264 +- code/game/machinery/Sleeper.dm | 1112 +-- code/game/machinery/adv_med.dm | 20 +- code/game/machinery/ai_slipper.dm | 2 +- code/game/machinery/alarm.dm | 2220 +++--- code/game/machinery/atmoalter/canister.dm | 1211 ++- code/game/machinery/atmoalter/meter.dm | 348 +- .../atmoalter/portable_atmospherics.dm | 320 +- code/game/machinery/atmoalter/pump.dm | 346 +- code/game/machinery/atmoalter/scrubber.dm | 432 +- code/game/machinery/atmoalter/zvent.dm | 70 +- code/game/machinery/autolathe.dm | 938 +-- code/game/machinery/buttons.dm | 412 +- code/game/machinery/camera/camera.dm | 866 +- code/game/machinery/camera/camera_assembly.dm | 358 +- code/game/machinery/camera/motion.dm | 132 +- code/game/machinery/camera/presets.dm | 184 +- code/game/machinery/camera/tracking.dm | 520 +- code/game/machinery/cell_charger.dm | 274 +- code/game/machinery/cloning.dm | 1316 ++-- code/game/machinery/computer/Operating.dm | 376 +- code/game/machinery/computer/ai_core.dm | 14 +- code/game/machinery/computer/aifixer.dm | 294 +- code/game/machinery/computer/arcade.dm | 2046 ++--- code/game/machinery/computer/atmos_alert.dm | 9 +- code/game/machinery/computer/brigcells.dm | 2 +- .../game/machinery/computer/buildandrepair.dm | 1374 ++-- code/game/machinery/computer/camera.dm | 712 +- .../machinery/computer/camera_advanced.dm | 6 +- code/game/machinery/computer/card.dm | 36 +- code/game/machinery/computer/cloning.dm | 30 +- .../game/machinery/computer/communications.dm | 1138 +-- code/game/machinery/computer/crew.dm | 72 +- code/game/machinery/computer/depot.dm | 2 +- code/game/machinery/computer/honkputer.dm | 2 +- code/game/machinery/computer/medical.dm | 1144 +-- code/game/machinery/computer/message.dm | 26 +- .../computer/pod_tracking_console.dm | 2 +- code/game/machinery/computer/power.dm | 130 +- code/game/machinery/computer/robot.dm | 480 +- code/game/machinery/computer/salvage_ship.dm | 2 +- code/game/machinery/computer/security.dm | 1156 +-- code/game/machinery/computer/skills.dm | 38 +- .../machinery/computer/specops_shuttle.dm | 680 +- code/game/machinery/computer/station_alert.dm | 106 +- code/game/machinery/computer/store.dm | 8 +- .../computer/syndicate_specops_shuttle.dm | 512 +- code/game/machinery/constructable_frame.dm | 2182 ++--- code/game/machinery/cryo.dm | 1034 +-- code/game/machinery/cryopod.dm | 12 +- code/game/machinery/dance_machine.dm | 2 +- code/game/machinery/defib_mount.dm | 4 +- code/game/machinery/deployable.dm | 424 +- code/game/machinery/doors/airlock.dm | 15 +- .../machinery/doors/airlock_electronics.dm | 204 +- code/game/machinery/doors/alarmlock.dm | 88 +- code/game/machinery/doors/brigdoors.dm | 1008 +-- .../machinery/doors/checkForMultipleDoors.dm | 32 +- code/game/machinery/doors/door.dm | 772 +- code/game/machinery/doors/firedoor.dm | 980 +-- code/game/machinery/doors/shutters.dm | 28 +- code/game/machinery/doors/windowdoor.dm | 944 +-- code/game/machinery/doppler_array.dm | 460 +- .../airlock_controllers.dm | 6 +- .../embedded_controller/airlock_program.dm | 2 +- .../embedded_controller_base.dm | 174 +- code/game/machinery/firealarm.dm | 6 +- code/game/machinery/flasher.dm | 306 +- code/game/machinery/hologram.dm | 1034 +-- code/game/machinery/igniter.dm | 252 +- code/game/machinery/iv_drip.dm | 150 +- code/game/machinery/lightswitch.dm | 366 +- code/game/machinery/machinery.dm | 1192 +-- code/game/machinery/magnet.dm | 772 +- code/game/machinery/mass_driver.dm | 422 +- code/game/machinery/navbeacon.dm | 444 +- code/game/machinery/newscaster.dm | 80 +- code/game/machinery/overview.dm | 724 +- code/game/machinery/pipe/construction.dm | 1126 +-- code/game/machinery/pipe/pipe_dispenser.dm | 370 +- code/game/machinery/poolcontroller.dm | 2 +- code/game/machinery/portable_tag_turret.dm | 2 +- code/game/machinery/portable_turret.dm | 2134 ++--- code/game/machinery/rechargestation.dm | 584 +- code/game/machinery/requests_console.dm | 732 +- code/game/machinery/shieldgen.dm | 1356 ++-- code/game/machinery/slotmachine.dm | 8 +- code/game/machinery/spaceheater.dm | 2 +- code/game/machinery/status_display.dm | 584 +- code/game/machinery/suit_storage_unit.dm | 1598 ++-- code/game/machinery/syndicatebeacon.dm | 384 +- code/game/machinery/telecomms/broadcaster.dm | 74 +- code/game/machinery/telecomms/ntsl2.dm | 14 +- .../machinery/telecomms/telecomunications.dm | 10 +- .../machinery/telecomms/traffic_control.dm | 2 +- code/game/machinery/teleporter.dm | 2 +- code/game/machinery/transformer.dm | 718 +- code/game/machinery/turret_control.dm | 2 +- code/game/machinery/vending.dm | 3888 ++++----- code/game/machinery/washing_machine.dm | 656 +- code/game/magic/Uristrunes.dm | 20 +- code/game/mecha/combat/combat.dm | 74 +- code/game/mecha/combat/durand.dm | 88 +- code/game/mecha/combat/gygax.dm | 130 +- code/game/mecha/combat/honker.dm | 292 +- code/game/mecha/combat/marauder.dm | 208 +- code/game/mecha/combat/phazon.dm | 98 +- code/game/mecha/equipment/mecha_equipment.dm | 304 +- .../mecha/equipment/tools/medical_tools.dm | 1132 +-- .../mecha/equipment/tools/mining_tools.dm | 2 +- code/game/mecha/equipment/weapons/weapons.dm | 1084 +-- code/game/mecha/mech_bay.dm | 392 +- code/game/mecha/mech_fabricator.dm | 962 +-- code/game/mecha/mecha.dm | 3043 +++---- code/game/mecha/mecha_actions.dm | 2 +- code/game/mecha/mecha_construction_paths.dm | 3454 ++++---- code/game/mecha/mecha_control_console.dm | 322 +- code/game/mecha/mecha_modkit.dm | 2 +- code/game/mecha/mecha_parts.dm | 914 +-- code/game/mecha/mecha_wreckage.dm | 430 +- code/game/mecha/medical/medical.dm | 14 +- code/game/mecha/medical/odysseus.dm | 92 +- code/game/mecha/paintkits.dm | 2 +- code/game/mecha/working/ripley.dm | 414 +- code/game/mecha/working/working.dm | 14 +- code/game/objects/buckling.dm | 2 +- code/game/objects/effects/alien_acid.dm | 2 +- code/game/objects/effects/anomalies.dm | 2 +- code/game/objects/effects/bump_teleporter.dm | 80 +- .../objects/effects/decals/Cleanable/fuel.dm | 2 +- .../effects/decals/Cleanable/humans.dm | 2 +- .../objects/effects/decals/Cleanable/misc.dm | 2 +- .../effects/decals/Cleanable/robots.dm | 120 +- .../effects/decals/Cleanable/tracks.dm | 20 +- code/game/objects/effects/decals/cleanable.dm | 146 +- code/game/objects/effects/decals/crayon.dm | 38 +- code/game/objects/effects/decals/decal.dm | 2 +- code/game/objects/effects/decals/misc.dm | 144 +- code/game/objects/effects/decals/remains.dm | 92 +- .../objects/effects/decals/turfdecals/dirt.dm | 2 +- .../effects/decals/turfdecals/markings.dm | 2 +- .../effects/decals/turfdecals/tilecoloring.dm | 2 +- .../effects/decals/turfdecals/weather.dm | 2 +- code/game/objects/effects/effect_system.dm | 8 +- .../effects/effect_system/effect_system.dm | 10 +- .../effect_system/effects_chem_smoke.dm | 2 +- .../effect_system/effects_explosion.dm | 2 +- .../effects/effect_system/effects_foam.dm | 4 +- .../effects/effect_system/effects_other.dm | 6 +- .../effects/effect_system/effects_smoke.dm | 4 +- .../effects/effect_system/effects_water.dm | 8 +- code/game/objects/effects/effects.dm | 2 +- code/game/objects/effects/forcefields.dm | 2 +- code/game/objects/effects/gibs.dm | 106 +- code/game/objects/effects/glowshroom.dm | 338 +- code/game/objects/effects/landmarks.dm | 612 +- code/game/objects/effects/manifest.dm | 40 +- code/game/objects/effects/mines.dm | 370 +- code/game/objects/effects/misc.dm | 246 +- code/game/objects/effects/overlays.dm | 130 +- code/game/objects/effects/portals.dm | 256 +- code/game/objects/effects/snowcloud.dm | 2 +- .../objects/effects/spawners/bombspawner.dm | 130 +- .../objects/effects/spawners/gibspawner.dm | 70 +- .../effects/spawners/random_spawners.dm | 2 +- .../objects/effects/spawners/vaultspawner.dm | 52 +- .../objects/effects/spawners/windowspawner.dm | 2 +- code/game/objects/effects/spiders.dm | 426 +- code/game/objects/effects/step_triggers.dm | 2 +- .../objects/effects/temporary_visuals/cult.dm | 2 +- .../temporary_visuals/miscellaneous.dm | 4 +- .../temporary_visuals/temporary_visual.dm | 2 +- code/game/objects/empulse.dm | 64 +- code/game/objects/explosion.dm | 482 +- code/game/objects/items.dm | 1361 ++-- code/game/objects/items/ashtray.dm | 2 +- code/game/objects/items/blueprints.dm | 2 +- code/game/objects/items/bodybag.dm | 154 +- code/game/objects/items/candle.dm | 172 +- code/game/objects/items/crayons.dm | 574 +- code/game/objects/items/dehy_carp.dm | 2 +- code/game/objects/items/devices/aicard.dm | 214 +- code/game/objects/items/devices/autopsy.dm | 2 +- code/game/objects/items/devices/camera_bug.dm | 2 +- .../objects/items/devices/chameleonproj.dm | 516 +- .../objects/items/devices/enginepicker.dm | 2 +- code/game/objects/items/devices/flash.dm | 562 +- code/game/objects/items/devices/flashlight.dm | 796 +- .../objects/items/devices/handheld_defib.dm | 2 +- .../game/objects/items/devices/instruments.dm | 4 +- .../objects/items/devices/laserpointer.dm | 1 + .../objects/items/devices/lightreplacer.dm | 538 +- code/game/objects/items/devices/paicard.dm | 680 +- .../objects/items/devices/pipe_painter.dm | 4 +- code/game/objects/items/devices/pizza_bomb.dm | 2 +- code/game/objects/items/devices/powersink.dm | 306 +- .../objects/items/devices/radio/beacon.dm | 212 +- .../items/devices/radio/electropack.dm | 226 +- .../objects/items/devices/radio/headset.dm | 852 +- .../objects/items/devices/radio/intercom.dm | 548 +- .../game/objects/items/devices/radio/radio.dm | 1732 ++-- code/game/objects/items/devices/scanners.dm | 1738 ++-- .../objects/items/devices/taperecorder.dm | 646 +- .../objects/items/devices/thermal_drill.dm | 2 +- .../objects/items/devices/traitordevices.dm | 2 +- .../objects/items/devices/transfer_valve.dm | 440 +- code/game/objects/items/devices/uplinks.dm | 16 +- code/game/objects/items/devices/voice.dm | 2 +- code/game/objects/items/documents.dm | 2 +- code/game/objects/items/flag.dm | 2 +- code/game/objects/items/latexballoon.dm | 130 +- code/game/objects/items/mixing_bowl.dm | 2 +- .../items/mountable_frames/air_alarm.dm | 2 +- .../items/mountable_frames/apc_frame.dm | 2 +- .../mountable_frames/buttons_switches.dm | 2 +- .../items/mountable_frames/fire_alarm.dm | 2 +- .../items/mountable_frames/intercom.dm | 2 +- .../objects/items/mountable_frames/lights.dm | 2 +- .../items/mountable_frames/mountables.dm | 2 +- .../mountable_frames/newscaster_frame.dm | 2 +- code/game/objects/items/random_items.dm | 2 +- code/game/objects/items/robot/robot_items.dm | 74 +- code/game/objects/items/robot/robot_parts.dm | 4 +- code/game/objects/items/shooting_range.dm | 354 +- code/game/objects/items/stacks/medical.dm | 566 +- code/game/objects/items/stacks/rods.dm | 136 +- .../game/objects/items/stacks/sheets/glass.dm | 422 +- .../objects/items/stacks/sheets/leather.dm | 484 +- .../game/objects/items/stacks/sheets/light.dm | 74 +- .../objects/items/stacks/sheets/mineral.dm | 778 +- .../items/stacks/sheets/sheet_types.dm | 44 +- .../objects/items/stacks/sheets/sheets.dm | 36 +- .../items/stacks/tiles/tile_mineral.dm | 24 +- code/game/objects/items/tools/multitool.dm | 6 +- .../objects/items/tools/tool_behaviour.dm | 6 +- code/game/objects/items/toys.dm | 3566 ++++----- code/game/objects/items/trash.dm | 210 +- code/game/objects/items/weapons/AI_modules.dm | 784 +- code/game/objects/items/weapons/RCD.dm | 1026 +-- code/game/objects/items/weapons/RCL.dm | 2 +- code/game/objects/items/weapons/RSF.dm | 168 +- code/game/objects/items/weapons/cards_ids.dm | 1854 ++--- code/game/objects/items/weapons/cash.dm | 2 +- .../objects/items/weapons/chrono_eraser.dm | 2 +- code/game/objects/items/weapons/cigs.dm | 2 +- .../game/objects/items/weapons/clown_items.dm | 116 +- code/game/objects/items/weapons/cosmetics.dm | 360 +- code/game/objects/items/weapons/dice.dm | 426 +- .../objects/items/weapons/dna_injector.dm | 1388 ++-- code/game/objects/items/weapons/explosives.dm | 556 +- .../objects/items/weapons/extinguisher.dm | 360 +- code/game/objects/items/weapons/fireworks.dm | 2 +- .../objects/items/weapons/flamethrower.dm | 2 +- .../items/weapons/grenades/atmosgrenade.dm | 2 +- .../items/weapons/grenades/bananade.dm | 2 +- .../items/weapons/grenades/chem_grenade.dm | 1182 +-- .../items/weapons/grenades/clowngrenade.dm | 2 +- .../items/weapons/grenades/emgrenade.dm | 22 +- .../items/weapons/grenades/flashbang.dm | 158 +- .../objects/items/weapons/grenades/grenade.dm | 222 +- .../items/weapons/grenades/smokebomb.dm | 74 +- .../items/weapons/grenades/spawnergrenade.dm | 136 +- .../items/weapons/grenades/syndieminibomb.dm | 2 +- code/game/objects/items/weapons/handcuffs.dm | 388 +- code/game/objects/items/weapons/holosign.dm | 2 +- .../objects/items/weapons/holy_weapons.dm | 2 +- .../objects/items/weapons/implants/health.dm | 2 +- .../objects/items/weapons/implants/implant.dm | 174 +- .../weapons/implants/implant_abductor.dm | 2 +- .../weapons/implants/implant_death_alarm.dm | 2 +- .../weapons/implants/implant_explosive.dm | 2 +- .../weapons/implants/implant_krav_maga.dm | 2 +- .../weapons/implants/implant_mindshield.dm | 2 +- .../items/weapons/implants/implant_track.dm | 2 +- .../items/weapons/implants/implantcase.dm | 204 +- .../items/weapons/implants/implantchair.dm | 316 +- .../items/weapons/implants/implanter.dm | 178 +- .../items/weapons/implants/implantpad.dm | 214 +- .../items/weapons/implants/implantuplink.dm | 90 +- code/game/objects/items/weapons/kitchen.dm | 614 +- code/game/objects/items/weapons/lighters.dm | 2 +- code/game/objects/items/weapons/manuals.dm | 2814 +++---- .../objects/items/weapons/melee/energy.dm | 734 +- code/game/objects/items/weapons/melee/misc.dm | 208 +- code/game/objects/items/weapons/mop.dm | 240 +- code/game/objects/items/weapons/paint.dm | 136 +- code/game/objects/items/weapons/paiwire.dm | 22 +- code/game/objects/items/weapons/rpd.dm | 34 +- code/game/objects/items/weapons/scrolls.dm | 212 +- code/game/objects/items/weapons/shields.dm | 296 +- .../items/weapons/storage/artistic_toolbox.dm | 2 +- .../objects/items/weapons/storage/backpack.dm | 1174 +-- .../objects/items/weapons/storage/bags.dm | 1006 +-- .../objects/items/weapons/storage/belt.dm | 1586 ++-- .../objects/items/weapons/storage/bible.dm | 228 +- .../objects/items/weapons/storage/boxes.dm | 2261 +++--- .../items/weapons/storage/briefcase.dm | 188 +- .../objects/items/weapons/storage/firstaid.dm | 765 +- .../objects/items/weapons/storage/lockbox.dm | 274 +- .../objects/items/weapons/storage/secure.dm | 493 +- .../objects/items/weapons/storage/storage.dm | 1158 +-- .../objects/items/weapons/storage/toolbox.dm | 274 +- .../objects/items/weapons/swords_axes_etc.dm | 238 +- .../objects/items/weapons/tanks/jetpack.dm | 492 +- .../objects/items/weapons/tanks/tank_types.dm | 506 +- .../game/objects/items/weapons/tanks/tanks.dm | 584 +- .../objects/items/weapons/teleportation.dm | 298 +- code/game/objects/items/weapons/tools.dm | 1570 ++-- code/game/objects/items/weapons/twohanded.dm | 1756 ++--- .../objects/items/weapons/vending_items.dm | 2 +- code/game/objects/items/weapons/weaponry.dm | 550 +- code/game/objects/obj_defense.dm | 6 +- code/game/objects/objs.dm | 2 +- code/game/objects/random/random.dm | 2 +- code/game/objects/structures.dm | 324 +- code/game/objects/structures/aliens.dm | 668 +- code/game/objects/structures/bedsheet_bin.dm | 664 +- code/game/objects/structures/coathanger.dm | 2 +- .../structures/crates_lockers/closets.dm | 902 +-- .../crates_lockers/closets/coffin.dm | 46 +- .../crates_lockers/closets/crittercrate.dm | 14 +- .../crates_lockers/closets/fitness.dm | 2 +- .../crates_lockers/closets/gimmick.dm | 272 +- .../crates_lockers/closets/job_closets.dm | 288 +- .../crates_lockers/closets/l3closet.dm | 152 +- .../crates_lockers/closets/malfunction.dm | 30 +- .../crates_lockers/closets/secure/depot.dm | 2 +- .../closets/secure/engineering.dm | 304 +- .../crates_lockers/closets/secure/freezer.dm | 224 +- .../closets/secure/guncabinet.dm | 2 +- .../closets/secure/hydroponics.dm | 2 +- .../crates_lockers/closets/secure/medical.dm | 591 +- .../crates_lockers/closets/secure/personal.dm | 178 +- .../closets/secure/scientist.dm | 216 +- .../closets/secure/secure_closets.dm | 358 +- .../crates_lockers/closets/secure/security.dm | 936 +-- .../crates_lockers/closets/statue.dm | 2 +- .../crates_lockers/closets/syndicate.dm | 298 +- .../crates_lockers/closets/utility_closets.dm | 452 +- .../crates_lockers/closets/wardrobe.dm | 962 +-- .../structures/crates_lockers/crates.dm | 1286 +-- .../structures/crates_lockers/crittercrate.dm | 2 +- .../structures/crates_lockers/largecrate.dm | 198 +- code/game/objects/structures/depot.dm | 2 +- code/game/objects/structures/displaycase.dm | 476 +- code/game/objects/structures/door_assembly.dm | 626 +- code/game/objects/structures/dresser.dm | 2 +- code/game/objects/structures/electricchair.dm | 2 +- code/game/objects/structures/engicart.dm | 2 +- code/game/objects/structures/false_walls.dm | 711 +- code/game/objects/structures/flora.dm | 692 +- code/game/objects/structures/fluff.dm | 2 +- code/game/objects/structures/foodcart.dm | 2 +- code/game/objects/structures/girders.dm | 942 +-- code/game/objects/structures/grille.dm | 636 +- code/game/objects/structures/guillotine.dm | 2 +- code/game/objects/structures/inflatable.dm | 2 +- code/game/objects/structures/janicart.dm | 372 +- code/game/objects/structures/kitchen_spike.dm | 312 +- code/game/objects/structures/lattice.dm | 252 +- .../structures/lavaland/necropolis_tendril.dm | 2 +- code/game/objects/structures/loom.dm | 2 +- code/game/objects/structures/mineral_doors.dm | 472 +- code/game/objects/structures/mirror.dm | 326 +- code/game/objects/structures/misc.dm | 4 +- code/game/objects/structures/mop_bucket.dm | 74 +- code/game/objects/structures/morgue.dm | 982 +-- code/game/objects/structures/musician.dm | 682 +- code/game/objects/structures/noticeboard.dm | 164 +- code/game/objects/structures/plasticflaps.dm | 2 +- code/game/objects/structures/reflector.dm | 366 +- code/game/objects/structures/safe.dm | 770 +- code/game/objects/structures/signs.dm | 652 +- code/game/objects/structures/spawner.dm | 2 +- code/game/objects/structures/spirit_board.dm | 2 +- code/game/objects/structures/statues.dm | 2 +- .../stool_bed_chair_nest/alien_nests.dm | 182 +- .../stool_bed_chair_nest/wheelchair.dm | 2 +- code/game/objects/structures/tables_racks.dm | 1554 ++-- .../game/objects/structures/tank_dispenser.dm | 264 +- code/game/objects/structures/target_stake.dm | 108 +- .../structures/transit_tubes/station.dm | 2 +- .../transit_tubes/transit_tube_pod.dm | 2 +- code/game/objects/structures/watercloset.dm | 2 +- code/game/objects/structures/window.dm | 1532 ++-- code/game/shuttle_engines.dm | 120 +- code/game/sound.dm | 2 +- code/game/turfs/simulated.dm | 218 +- code/game/turfs/simulated/floor.dm | 506 +- code/game/turfs/simulated/floor/asteroid.dm | 4 +- code/game/turfs/simulated/floor/chasm.dm | 4 +- .../turfs/simulated/floor/indestructible.dm | 2 +- code/game/turfs/simulated/floor/lava.dm | 2 +- code/game/turfs/simulated/minerals.dm | 4 +- code/game/turfs/simulated/river.dm | 2 +- code/game/turfs/simulated/walls.dm | 1012 +-- .../turfs/simulated/walls_indestructible.dm | 2 +- code/game/turfs/simulated/walls_mineral.dm | 708 +- code/game/turfs/simulated/walls_reinforced.dm | 476 +- code/game/turfs/space/space.dm | 550 +- code/game/turfs/space/transit.dm | 320 +- code/game/turfs/turf.dm | 12 +- code/game/turfs/unsimulated.dm | 90 +- code/game/turfs/unsimulated/beach.dm | 254 +- code/game/turfs/unsimulated/floor.dm | 142 +- code/game/turfs/unsimulated/walls.dm | 70 +- code/game/verbs/ooc.dm | 510 +- code/game/verbs/suicide.dm | 494 +- code/game/verbs/who.dm | 260 +- code/game/world.dm | 96 +- code/hub.dm | 28 +- code/modules/admin/DB ban/functions.dm | 1166 +-- code/modules/admin/IsBanned.dm | 8 +- code/modules/admin/NewBan.dm | 464 +- code/modules/admin/ToRban.dm | 178 +- code/modules/admin/admin.dm | 2188 +++--- code/modules/admin/admin_investigate.dm | 6 +- code/modules/admin/admin_memo.dm | 20 +- code/modules/admin/admin_ranks.dm | 321 +- code/modules/admin/admin_verbs.dm | 2037 ++--- code/modules/admin/banappearance.dm | 36 +- code/modules/admin/banjob.dm | 358 +- code/modules/admin/create_mob.dm | 18 +- code/modules/admin/create_object.dm | 46 +- code/modules/admin/create_poll.dm | 26 +- code/modules/admin/create_turf.dm | 18 +- code/modules/admin/holder2.dm | 217 +- code/modules/admin/ipintel.dm | 20 +- .../admin/permissionverbs/permissionedit.dm | 340 +- code/modules/admin/player_panel.dm | 12 +- code/modules/admin/secrets.dm | 4 +- code/modules/admin/sql_notes.dm | 24 +- code/modules/admin/topic.dm | 6991 +++++++++-------- code/modules/admin/verbs/BrokenInhands.dm | 72 +- code/modules/admin/verbs/adminhelp.dm | 374 +- code/modules/admin/verbs/adminjump.dm | 322 +- code/modules/admin/verbs/adminsay.dm | 164 +- code/modules/admin/verbs/alt_check.dm | 2 +- code/modules/admin/verbs/atmosdebug.dm | 126 +- code/modules/admin/verbs/cinematic.dm | 2 +- code/modules/admin/verbs/custom_event.dm | 12 +- code/modules/admin/verbs/deadsay.dm | 98 +- code/modules/admin/verbs/debug.dm | 1784 ++--- code/modules/admin/verbs/diagnostics.dm | 356 +- code/modules/admin/verbs/dice.dm | 2 +- code/modules/admin/verbs/freeze.dm | 12 +- code/modules/admin/verbs/getlogs.dm | 2 +- code/modules/admin/verbs/honksquad.dm | 8 +- .../admin/verbs/infiltratorteam_syndicate.dm | 10 +- code/modules/admin/verbs/logging_view.dm | 10 + .../admin/verbs/map_template_loadverb.dm | 6 +- code/modules/admin/verbs/mapping.dm | 370 +- code/modules/admin/verbs/massmodvar.dm | 544 +- code/modules/admin/verbs/modifyvariables.dm | 1266 +-- code/modules/admin/verbs/one_click_antag.dm | 1264 +-- code/modules/admin/verbs/onlyone.dm | 208 +- code/modules/admin/verbs/onlyoneteam.dm | 12 +- code/modules/admin/verbs/playsound.dm | 306 +- code/modules/admin/verbs/pray.dm | 178 +- code/modules/admin/verbs/randomverbs.dm | 2318 +++--- code/modules/admin/verbs/serialization.dm | 2 +- code/modules/admin/verbs/space_transitions.dm | 4 +- code/modules/admin/verbs/striketeam.dm | 408 +- .../admin/verbs/striketeam_syndicate.dm | 338 +- code/modules/admin/verbs/ticklag.dm | 42 +- code/modules/admin/verbs/toggledebugverbs.dm | 6 +- code/modules/admin/verbs/tripAI.dm | 44 +- code/modules/admin/verbs/vox_raiders.dm | 8 +- code/modules/admin/watchlist.dm | 14 +- code/modules/alarm/camera_alarm.dm | 2 +- code/modules/alarm/motion_alarm.dm | 2 +- .../antagonists/_common/antag_datum.dm | 2 +- .../antagonists/_common/antag_helpers.dm | 2 +- code/modules/antagonists/_common/antag_hud.dm | 6 +- .../antagonists/_common/antag_spawner.dm | 2 +- .../antagonists/survivalist/survivalist.dm | 2 +- .../antagonists/traitor/datum_mindslave.dm | 4 +- .../antagonists/traitor/datum_traitor.dm | 16 +- .../antagonists/wishgranter/wishgranter.dm | 70 +- code/modules/arcade/arcade_prize.dm | 4 +- code/modules/arcade/claw_game.dm | 10 +- .../arcade/mob_hunt/battle_computer.dm | 2 +- code/modules/arcade/mob_hunt/mob_datums.dm | 4 +- .../arcade/mob_hunt/mob_type_datums.dm | 2 +- code/modules/arcade/page.css | 2 +- code/modules/arcade/prize_counter.dm | 8 +- code/modules/arcade/prize_datums.dm | 5 +- code/modules/assembly/assembly.dm | 300 +- code/modules/assembly/bomb.dm | 360 +- code/modules/assembly/health.dm | 2 +- code/modules/assembly/helpers.dm | 88 +- code/modules/assembly/holder.dm | 394 +- code/modules/assembly/igniter.dm | 90 +- code/modules/assembly/infrared.dm | 572 +- code/modules/assembly/mousetrap.dm | 288 +- code/modules/assembly/proximity.dm | 268 +- code/modules/assembly/shock_kit.dm | 92 +- code/modules/assembly/signaler.dm | 354 +- code/modules/assembly/timer.dm | 254 +- code/modules/assembly/voice.dm | 2 +- code/modules/atmos_automation/console.dm | 4 +- .../implementation/digital_valves.dm | 2 +- .../implementation/emitters.dm | 2 +- .../implementation/injectors.dm | 2 +- .../implementation/scrubbers.dm | 8 +- .../implementation/sensors.dm | 2 +- code/modules/atmos_automation/statements.dm | 8 +- code/modules/awaymissions/exile.dm | 88 +- code/modules/awaymissions/gateway.dm | 590 +- code/modules/awaymissions/loot.dm | 50 +- code/modules/awaymissions/map_rng.dm | 4 +- .../awaymissions/maploader/dmm_suite.dm | 5 +- code/modules/awaymissions/maploader/reader.dm | 28 +- .../awaymissions/mission_code/academy.dm | 8 +- .../awaymissions/mission_code/beach.dm | 2 +- .../mission_code/blackmarketpackers.dm | 2 +- .../awaymissions/mission_code/centcomAway.dm | 2 +- .../awaymissions/mission_code/evil_santa.dm | 2 +- .../ghost_role_spawners/oldstation.dm | 2 +- .../mission_code/ruins/oldstation.dm | 2 +- .../mission_code/ruins/wizardcrash.dm | 2 +- .../awaymissions/mission_code/spacebattle.dm | 2 +- .../awaymissions/mission_code/spacehotel.dm | 2 +- .../mission_code/stationCollision.dm | 20 +- code/modules/awaymissions/pamphlet.dm | 76 +- code/modules/awaymissions/zlevel.dm | 34 +- code/modules/awaymissions/zvis.dm | 2 +- code/modules/buildmode/README.md | 2 +- code/modules/buildmode/bm_mode.dm | 6 +- code/modules/buildmode/buildmode.dm | 2 +- code/modules/buildmode/buttons.dm | 2 +- code/modules/buildmode/effects/line.dm | 2 +- code/modules/buildmode/submodes/advanced.dm | 2 +- code/modules/buildmode/submodes/atmos.dm | 2 +- code/modules/buildmode/submodes/fill.dm | 2 +- code/modules/buildmode/submodes/mapgen.dm | 2 +- code/modules/buildmode/submodes/save.dm | 4 +- code/modules/busy_space/air_traffic.dm | 41 +- code/modules/busy_space/loremaster.dm | 3 +- code/modules/busy_space/organizations.dm | 4 +- code/modules/client/asset_cache.dm | 17 +- code/modules/client/client defines.dm | 2 +- code/modules/client/client procs.dm | 48 +- code/modules/client/message.dm | 2 +- .../client/preference/loadout/gear_tweaks.dm | 3 +- .../client/preference/loadout/loadout.dm | 20 +- .../preference/loadout/loadout_cosmetics.dm | 2 +- .../preference/loadout/loadout_donor.dm | 2 +- .../preference/loadout/loadout_glasses.dm | 2 +- .../preference/loadout/loadout_gloves.dm | 2 +- .../preference/loadout/loadout_shoes.dm | 2 +- code/modules/client/preference/preferences.dm | 111 +- .../client/preference/preferences_mysql.dm | 20 +- .../preference/preferences_spawnpoints.dm | 14 +- code/modules/client/view.dm | 2 +- code/modules/clothing/chameleon.dm | 2 +- code/modules/clothing/clothing.dm | 1606 ++-- code/modules/clothing/ears/ears.dm | 2 +- code/modules/clothing/glasses/glasses.dm | 1050 +-- code/modules/clothing/glasses/hud.dm | 396 +- code/modules/clothing/gloves/boxing.dm | 38 +- code/modules/clothing/gloves/color.dm | 480 +- code/modules/clothing/head/beanie.dm | 2 +- code/modules/clothing/head/collectable.dm | 292 +- code/modules/clothing/head/hardhat.dm | 2 +- code/modules/clothing/head/helmet.dm | 602 +- code/modules/clothing/head/jobs.dm | 380 +- code/modules/clothing/head/misc.dm | 986 +-- code/modules/clothing/head/misc_special.dm | 546 +- code/modules/clothing/head/soft_caps.dm | 264 +- code/modules/clothing/masks/boxing.dm | 104 +- code/modules/clothing/masks/breath.dm | 108 +- code/modules/clothing/masks/gasmask.dm | 730 +- code/modules/clothing/masks/miscellaneous.dm | 1046 +-- code/modules/clothing/patreon/glasses.dm | 2 +- code/modules/clothing/patreon/hats.dm | 2 +- code/modules/clothing/shoes/colour.dm | 212 +- code/modules/clothing/shoes/magboots.dm | 236 +- code/modules/clothing/shoes/miscellaneous.dm | 692 +- code/modules/clothing/spacesuits/breaches.dm | 14 +- code/modules/clothing/spacesuits/ert.dm | 4 +- code/modules/clothing/spacesuits/hardsuit.dm | 1180 +-- .../clothing/spacesuits/miscellaneous.dm | 574 +- code/modules/clothing/spacesuits/plasmamen.dm | 2 +- .../spacesuits/rig/modules/computer.dm | 4 +- .../clothing/spacesuits/rig/modules/vision.dm | 2 +- code/modules/clothing/spacesuits/rig/rig.dm | 6 +- .../clothing/spacesuits/rig/rig_verbs.dm | 2 +- .../spacesuits/rig/suits/ert_suits.dm | 2 +- code/modules/clothing/spacesuits/syndi.dm | 376 +- code/modules/clothing/spacesuits/void.dm | 248 +- code/modules/clothing/suits/alien.dm | 2 +- code/modules/clothing/suits/armor.dm | 1088 +-- code/modules/clothing/suits/bio.dm | 222 +- code/modules/clothing/suits/jobs.dm | 770 +- code/modules/clothing/suits/labcoat.dm | 130 +- code/modules/clothing/suits/toggles.dm | 2 +- code/modules/clothing/suits/utility.dm | 318 +- code/modules/clothing/suits/wiz_robe.dm | 408 +- code/modules/clothing/under/color.dm | 414 +- code/modules/clothing/under/jobs/civilian.dm | 478 +- .../clothing/under/jobs/engineering.dm | 154 +- code/modules/clothing/under/jobs/medsci.dm | 492 +- .../under/jobs/plasmamen/_plasmamen.dm | 2 +- .../under/jobs/plasmamen/civilian_service.dm | 2 +- .../clothing/under/jobs/plasmamen/security.dm | 2 +- code/modules/clothing/under/jobs/security.dm | 400 +- code/modules/clothing/under/miscellaneous.dm | 1702 ++-- code/modules/clothing/under/pants.dm | 2 +- code/modules/clothing/under/shorts.dm | 52 +- code/modules/clothing/under/syndicate.dm | 52 +- code/modules/crafting/craft.dm | 12 +- code/modules/customitems/item_defines.dm | 4 +- code/modules/customitems/item_spawning.dm | 2 +- code/modules/detective_work/scanner.dm | 6 +- code/modules/economy/ATM.dm | 10 +- code/modules/economy/Accounts.dm | 58 +- code/modules/economy/Accounts_DB.dm | 38 +- code/modules/economy/EFTPOS.dm | 6 +- code/modules/economy/Economy.dm | 16 +- code/modules/economy/Economy_Events.dm | 8 +- .../modules/economy/Economy_Events_Mundane.dm | 12 +- .../economy/Economy_TradeDestinations.dm | 4 +- code/modules/economy/Job_Departments.dm | 4 +- code/modules/economy/POS.dm | 16 +- code/modules/economy/utils.dm | 6 +- code/modules/error_handler/error_handler.dm | 38 +- code/modules/error_handler/error_viewer.dm | 8 +- code/modules/events/abductor.dm | 2 +- code/modules/events/alien_infestation.dm | 80 +- code/modules/events/anomaly.dm | 4 +- code/modules/events/anomaly_bluespace.dm | 6 +- code/modules/events/anomaly_flux.dm | 4 +- code/modules/events/anomaly_grav.dm | 4 +- code/modules/events/anomaly_pyro.dm | 4 +- code/modules/events/anomaly_vortex.dm | 4 +- code/modules/events/aurora_caelus.dm | 4 +- code/modules/events/blob.dm | 66 +- code/modules/events/brand_intelligence.dm | 132 +- code/modules/events/cargobonus.dm | 2 +- code/modules/events/carp_migration.dm | 90 +- .../modules/events/communications_blackout.dm | 60 +- code/modules/events/disease_outbreak.dm | 2 +- code/modules/events/dust.dm | 4 +- code/modules/events/electrical_storm.dm | 54 +- code/modules/events/event.dm | 316 +- code/modules/events/event_container.dm | 10 +- code/modules/events/event_procs.dm | 240 +- code/modules/events/floorcluwne.dm | 6 +- code/modules/events/grid_check.dm | 8 +- code/modules/events/headcrabs.dm | 8 +- code/modules/events/holidays/AprilFools.dm | 2 +- code/modules/events/holidays/Easter.dm | 2 +- code/modules/events/holidays/Holidays.dm | 3 +- code/modules/events/immovable_rod.dm | 4 +- code/modules/events/infestation.dm | 4 +- code/modules/events/ion_storm.dm | 2 +- code/modules/events/koi_mirgration.dm | 2 +- code/modules/events/mass_hallucination.dm | 2 +- code/modules/events/meaty_gore.dm | 6 +- code/modules/events/meaty_ops.dm | 6 +- code/modules/events/meaty_ores.dm | 6 +- code/modules/events/meteors.dm | 14 +- code/modules/events/money_hacker.dm | 10 +- code/modules/events/money_lotto.dm | 8 +- code/modules/events/money_spam.dm | 6 +- code/modules/events/prison_break.dm | 136 +- code/modules/events/radiation_storm.dm | 22 +- code/modules/events/rogue_drones.dm | 6 +- code/modules/events/sentience.dm | 4 +- code/modules/events/slaughterevent.dm | 2 +- code/modules/events/spacevine.dm | 1410 ++-- code/modules/events/spider_infestation.dm | 58 +- code/modules/events/spider_terror.dm | 2 +- code/modules/events/tear.dm | 4 +- code/modules/events/tear_honk.dm | 4 +- code/modules/events/traders.dm | 12 +- code/modules/events/vent_clog.dm | 2 +- code/modules/events/wallrot.dm | 4 +- code/modules/events/wormholes.dm | 4 +- code/modules/examine/descriptions/medical.dm | 2 +- code/modules/examine/descriptions/stacks.dm | 2 +- code/modules/examine/descriptions/turfs.dm | 2 +- code/modules/ext_scripts/python.dm | 2 +- code/modules/fancytitle/fancytitle.dm | 4 +- code/modules/fish/fish_types.dm | 2 +- code/modules/flufftext/Dreaming.dm | 2 +- code/modules/flufftext/Hallucination.dm | 16 +- .../food_and_drinks/drinks/bottler/bottler.dm | 2 +- .../food_and_drinks/drinks/drinks/bottle.dm | 4 +- .../drinks/drinks/drinkingglass.dm | 2 +- code/modules/food_and_drinks/food.dm | 2 +- .../food_and_drinks/food/foods/meat.dm | 8 + .../food_and_drinks/food/foods/pizza.dm | 2 +- .../food_and_drinks/food/foods/soups.dm | 2 +- .../kitchen_machinery/cooker.dm | 2 + .../kitchen_machinery/deep_fryer.dm | 18 +- .../kitchen_machinery/gibber.dm | 1 + .../kitchen_machinery/grill_new.dm | 2 +- .../kitchen_machinery/icecream_vat_2.dm | 10 +- .../kitchen_machinery/microwave.dm | 2 +- .../kitchen_machinery/smartfridge.dm | 2 +- .../recipes/tablecraft/recipes_table.dm | 2 +- code/modules/hydroponics/beekeeping/beebox.dm | 2 +- .../hydroponics/beekeeping/honeycomb.dm | 2 +- code/modules/hydroponics/grown/ambrosia.dm | 2 +- code/modules/hydroponics/grown/beans.dm | 2 +- .../hydroponics/grown/cocoa_vanilla.dm | 2 +- code/modules/hydroponics/grown/cotton.dm | 2 +- code/modules/hydroponics/grown/eggplant.dm | 2 +- code/modules/hydroponics/grown/garlic.dm | 2 +- code/modules/hydroponics/grown/herbals.dm | 2 +- code/modules/hydroponics/grown/misc.dm | 2 +- code/modules/hydroponics/grown/mushrooms.dm | 2 +- code/modules/hydroponics/grown/nymph.dm | 2 +- code/modules/hydroponics/grown/onion.dm | 2 +- code/modules/hydroponics/grown/peanut.dm | 2 +- code/modules/hydroponics/grown/pineapple.dm | 2 +- code/modules/hydroponics/grown/potato.dm | 2 +- code/modules/hydroponics/grown/tea_coffee.dm | 2 +- code/modules/hydroponics/grown/tobacco.dm | 2 +- code/modules/hydroponics/grown/towercap.dm | 2 +- code/modules/hydroponics/hydroponics.dm | 6 +- code/modules/hydroponics/sample.dm | 16 +- code/modules/karma/karma.dm | 44 +- code/modules/keybindings/bindings_ai.dm | 2 +- code/modules/keybindings/bindings_atom.dm | 2 +- code/modules/keybindings/bindings_carbon.dm | 2 +- code/modules/keybindings/bindings_client.dm | 2 +- code/modules/keybindings/bindings_human.dm | 2 +- code/modules/keybindings/bindings_living.dm | 2 +- code/modules/keybindings/bindings_mob.dm | 2 +- code/modules/keybindings/bindings_robot.dm | 2 +- code/modules/keybindings/focus.dm | 2 +- code/modules/keybindings/readme.md | 2 +- code/modules/library/admin.dm | 4 +- code/modules/library/codex_gigas.dm | 2 +- code/modules/library/computers/base.dm | 6 +- code/modules/library/computers/checkout.dm | 930 +-- code/modules/library/computers/public.dm | 256 +- code/modules/library/lib_items.dm | 622 +- code/modules/library/lib_machines.dm | 460 +- code/modules/library/lib_readme.dm | 122 +- code/modules/library/random_books.dm | 6 +- code/modules/lighting/__lighting_docs.dm | 2 +- code/modules/lighting/lighting_area.dm | 2 +- code/modules/lighting/lighting_corner.dm | 2 +- code/modules/lighting/lighting_object.dm | 2 +- code/modules/lighting/lighting_setup.dm | 2 +- code/modules/lighting/lighting_source.dm | 2 +- code/modules/logic/logic_base.dm | 2 +- code/modules/map_fluff/maps.dm | 4 +- code/modules/martial_arts/cqc.dm | 2 +- code/modules/martial_arts/plasma_fist.dm | 2 +- code/modules/mining/abandonedcrates.dm | 2 +- .../modules/mining/equipment/explorer_gear.dm | 2 +- .../mining/equipment/kinetic_crusher.dm | 2 +- .../mining/equipment/lazarus_injector.dm | 2 +- .../mining/equipment/marker_beacons.dm | 10 +- .../mining/equipment/mineral_scanner.dm | 2 +- code/modules/mining/equipment/mining_tools.dm | 2 +- .../mining/equipment/regenerative_core.dm | 2 +- code/modules/mining/equipment/resonator.dm | 2 +- code/modules/mining/equipment/survival_pod.dm | 2 +- .../mining/equipment/wormhole_jaunter.dm | 2 +- code/modules/mining/fulton.dm | 2 +- code/modules/mining/laborcamp/laborshuttle.dm | 2 +- code/modules/mining/laborcamp/laborstacker.dm | 4 +- code/modules/mining/lavaland/ash_flora.dm | 2 +- .../mining/lavaland/loot/bubblegum_loot.dm | 2 +- .../mining/lavaland/loot/colossus_loot.dm | 2 +- .../mining/lavaland/loot/hierophant_loot.dm | 4 +- .../mining/lavaland/necropolis_chests.dm | 2 +- code/modules/mining/machine_processing.dm | 2 +- code/modules/mining/machine_redemption.dm | 4 +- code/modules/mining/machine_stacking.dm | 2 +- code/modules/mining/machine_unloading.dm | 2 +- code/modules/mining/mine_items.dm | 2 +- code/modules/mining/minebot.dm | 2 +- code/modules/mining/mint.dm | 2 +- code/modules/mining/money_bag.dm | 48 +- code/modules/mining/ores_coins.dm | 2 +- code/modules/mob/abilities.dm | 5 - code/modules/mob/dead/dead.dm | 2 +- code/modules/mob/dead/death.dm | 2 +- code/modules/mob/dead/observer/login.dm | 18 +- code/modules/mob/dead/observer/logout.dm | 14 +- code/modules/mob/dead/observer/observer.dm | 1651 ++-- code/modules/mob/dead/observer/say.dm | 96 +- code/modules/mob/emote.dm | 1 + code/modules/mob/hear_say.dm | 6 +- code/modules/mob/inventory.dm | 530 +- code/modules/mob/living/carbon/_defines.dm | 2 +- code/modules/mob/living/carbon/alien/alien.dm | 616 +- .../mob/living/carbon/alien/alien_defense.dm | 2 +- .../carbon/alien/humanoid/alien_powers.dm | 340 +- .../carbon/alien/humanoid/caste/drone.dm | 96 +- .../carbon/alien/humanoid/caste/hunter.dm | 262 +- .../carbon/alien/humanoid/caste/sentinel.dm | 170 +- .../mob/living/carbon/alien/humanoid/emote.dm | 2 +- .../living/carbon/alien/humanoid/humanoid.dm | 246 +- .../living/carbon/alien/humanoid/inventory.dm | 94 +- .../mob/living/carbon/alien/humanoid/life.dm | 178 +- .../mob/living/carbon/alien/humanoid/queen.dm | 190 +- .../carbon/alien/humanoid/update_icons.dm | 332 +- .../mob/living/carbon/alien/larva/emote.dm | 2 +- .../living/carbon/alien/larva/inventory.dm | 6 +- .../mob/living/carbon/alien/larva/larva.dm | 192 +- .../carbon/alien/larva/larva_defense.dm | 2 +- .../mob/living/carbon/alien/larva/life.dm | 114 +- .../mob/living/carbon/alien/larva/powers.dm | 108 +- .../living/carbon/alien/larva/update_icons.dm | 50 +- code/modules/mob/living/carbon/alien/login.dm | 8 +- .../modules/mob/living/carbon/alien/logout.dm | 8 +- .../carbon/alien/special/alien_embryo.dm | 258 +- .../living/carbon/alien/special/facehugger.dm | 478 +- code/modules/mob/living/carbon/brain/MMI.dm | 598 +- code/modules/mob/living/carbon/brain/brain.dm | 222 +- .../mob/living/carbon/brain/brain_item.dm | 282 +- code/modules/mob/living/carbon/brain/death.dm | 54 +- code/modules/mob/living/carbon/brain/emote.dm | 2 +- code/modules/mob/living/carbon/brain/life.dm | 80 +- code/modules/mob/living/carbon/brain/login.dm | 6 +- code/modules/mob/living/carbon/brain/say.dm | 100 +- code/modules/mob/living/carbon/carbon.dm | 12 +- .../mob/living/carbon/carbon_defines.dm | 60 +- .../mob/living/carbon/human/appearance.dm | 18 +- .../living/carbon/human/body_accessories.dm | 18 +- code/modules/mob/living/carbon/human/death.dm | 390 +- code/modules/mob/living/carbon/human/emote.dm | 40 +- .../mob/living/carbon/human/examine.dm | 871 +- code/modules/mob/living/carbon/human/human.dm | 3928 ++++----- .../mob/living/carbon/human/human_damage.dm | 700 +- .../mob/living/carbon/human/human_defense.dm | 1460 ++-- .../mob/living/carbon/human/human_defines.dm | 148 +- .../mob/living/carbon/human/human_movement.dm | 266 +- .../mob/living/carbon/human/inventory.dm | 874 +-- code/modules/mob/living/carbon/human/life.dm | 2248 +++--- code/modules/mob/living/carbon/human/login.dm | 18 +- .../modules/mob/living/carbon/human/logout.dm | 2 +- code/modules/mob/living/carbon/human/say.dm | 517 +- .../living/carbon/human/species/_species.dm | 2 +- .../living/carbon/human/species/abductor.dm | 6 +- .../mob/living/carbon/human/species/diona.dm | 2 +- .../mob/living/carbon/human/species/golem.dm | 2 +- .../mob/living/carbon/human/species/grey.dm | 6 +- .../mob/living/carbon/human/species/human.dm | 2 +- .../mob/living/carbon/human/species/kidan.dm | 2 +- .../living/carbon/human/species/machine.dm | 5 +- .../mob/living/carbon/human/species/monkey.dm | 6 +- .../living/carbon/human/species/nucleation.dm | 2 +- .../mob/living/carbon/human/species/shadow.dm | 2 +- .../living/carbon/human/species/shadowling.dm | 2 +- .../mob/living/carbon/human/species/skrell.dm | 2 +- .../mob/living/carbon/human/species/slime.dm | 6 +- .../living/carbon/human/species/tajaran.dm | 2 +- .../living/carbon/human/species/vulpkanin.dm | 2 +- .../mob/living/carbon/human/species/wryn.dm | 2 +- .../mob/living/carbon/human/update_icons.dm | 2704 +++---- .../mob/living/carbon/human/update_stat.dm | 2 +- code/modules/mob/living/carbon/life.dm | 2 +- .../modules/mob/living/carbon/status_procs.dm | 2 +- .../modules/mob/living/carbon/update_icons.dm | 2 +- .../mob/living/carbon/update_status.dm | 2 +- code/modules/mob/living/damage_procs.dm | 684 +- code/modules/mob/living/death.dm | 2 +- code/modules/mob/living/life.dm | 2 +- code/modules/mob/living/living.dm | 5 +- code/modules/mob/living/living_defense.dm | 720 +- code/modules/mob/living/living_defines.dm | 148 +- code/modules/mob/living/logout.dm | 26 +- code/modules/mob/living/say.dm | 948 +-- code/modules/mob/living/silicon/ai/ai.dm | 2646 +++---- code/modules/mob/living/silicon/ai/death.dm | 82 +- code/modules/mob/living/silicon/ai/examine.dm | 68 +- .../living/silicon/ai/freelook/cameranet.dm | 4 +- .../mob/living/silicon/ai/freelook/eye.dm | 302 +- .../mob/living/silicon/ai/freelook/read_me.dm | 102 +- .../modules/mob/living/silicon/ai/latejoin.dm | 14 +- code/modules/mob/living/silicon/ai/laws.dm | 54 +- code/modules/mob/living/silicon/ai/life.dm | 308 +- code/modules/mob/living/silicon/ai/login.dm | 30 +- code/modules/mob/living/silicon/ai/logout.dm | 12 +- .../modules/mob/living/silicon/ai/multicam.dm | 2 +- code/modules/mob/living/silicon/ai/say.dm | 370 +- .../modules/mob/living/silicon/decoy/death.dm | 20 +- .../modules/mob/living/silicon/decoy/decoy.dm | 124 +- code/modules/mob/living/silicon/decoy/life.dm | 38 +- code/modules/mob/living/silicon/login.dm | 24 +- code/modules/mob/living/silicon/pai/death.dm | 34 +- code/modules/mob/living/silicon/pai/life.dm | 54 +- code/modules/mob/living/silicon/pai/pai.dm | 1232 +-- .../mob/living/silicon/pai/personality.dm | 120 +- .../modules/mob/living/silicon/pai/recruit.dm | 806 +- code/modules/mob/living/silicon/pai/say.dm | 32 +- .../mob/living/silicon/pai/software.dm | 270 +- .../living/silicon/pai/software_modules.dm | 40 +- .../modules/mob/living/silicon/robot/death.dm | 150 +- .../mob/living/silicon/robot/drone/drone.dm | 2 +- .../silicon/robot/drone/drone_abilities.dm | 2 +- .../silicon/robot/drone/drone_console.dm | 2 +- .../living/silicon/robot/drone/drone_say.dm | 2 +- .../mob/living/silicon/robot/examine.dm | 116 +- .../mob/living/silicon/robot/inventory.dm | 508 +- code/modules/mob/living/silicon/robot/laws.dm | 108 +- code/modules/mob/living/silicon/robot/life.dm | 406 +- .../modules/mob/living/silicon/robot/login.dm | 8 +- .../modules/mob/living/silicon/robot/robot.dm | 2948 +++---- .../mob/living/silicon/robot/robot_damage.dm | 358 +- .../mob/living/silicon/robot/robot_items.dm | 2 +- .../mob/living/silicon/robot/robot_modules.dm | 1180 +-- .../living/silicon/robot/robot_movement.dm | 2 +- .../mob/living/silicon/robot/update_status.dm | 1 + code/modules/mob/living/silicon/say.dm | 230 +- code/modules/mob/living/silicon/silicon.dm | 720 +- code/modules/mob/living/silicon/subsystems.dm | 18 +- .../living/simple_animal/animal_defense.dm | 2 +- .../mob/living/simple_animal/bot/bot.dm | 10 +- .../living/simple_animal/bot/construction.dm | 7 +- .../mob/living/simple_animal/bot/ed209bot.dm | 4 +- .../mob/living/simple_animal/bot/griefsky.dm | 2 +- .../mob/living/simple_animal/bot/honkbot.dm | 2 +- .../mob/living/simple_animal/bot/medbot.dm | 2 +- .../mob/living/simple_animal/bot/secbot.dm | 2 +- .../mob/living/simple_animal/bot/syndicate.dm | 2 +- .../mob/living/simple_animal/constructs.dm | 810 +- .../mob/living/simple_animal/corpse.dm | 230 +- .../mob/living/simple_animal/damage_procs.dm | 2 +- .../mob/living/simple_animal/friendly/cat.dm | 558 +- .../mob/living/simple_animal/friendly/crab.dm | 110 +- .../mob/living/simple_animal/friendly/deer.dm | 2 +- .../living/simple_animal/friendly/diona.dm | 4 +- .../mob/living/simple_animal/friendly/dog.dm | 2 +- .../simple_animal/friendly/farm_animals.dm | 896 +-- .../mob/living/simple_animal/friendly/fox.dm | 2 +- .../living/simple_animal/friendly/lizard.dm | 50 +- .../living/simple_animal/friendly/mouse.dm | 472 +- .../living/simple_animal/friendly/penguin.dm | 2 +- .../mob/living/simple_animal/friendly/pet.dm | 2 +- .../living/simple_animal/friendly/sloth.dm | 2 +- .../living/simple_animal/friendly/snake.dm | 2 +- .../mob/living/simple_animal/hostile/alien.dm | 328 +- .../mob/living/simple_animal/hostile/bat.dm | 2 +- .../mob/living/simple_animal/hostile/bear.dm | 112 +- .../mob/living/simple_animal/hostile/bees.dm | 2 +- .../mob/living/simple_animal/hostile/carp.dm | 342 +- .../living/simple_animal/hostile/creature.dm | 34 +- .../living/simple_animal/hostile/faithless.dm | 82 +- .../living/simple_animal/hostile/feral_cat.dm | 2 +- .../simple_animal/hostile/floorcluwne.dm | 6 +- .../simple_animal/hostile/giant_spider.dm | 492 +- .../living/simple_animal/hostile/hivebot.dm | 216 +- .../living/simple_animal/hostile/hostile.dm | 1136 +-- .../simple_animal/hostile/jungle_animals.dm | 2 +- .../hostile/megafauna/blood_drunk_miner.dm | 6 +- .../hostile/megafauna/bubblegum.dm | 6 +- .../hostile/megafauna/colossus.dm | 14 +- .../simple_animal/hostile/megafauna/drake.dm | 4 +- .../hostile/megafauna/hierophant.dm | 26 +- .../hostile/megafauna/megafauna.dm | 2 +- .../hostile/megafauna/swarmer.dm | 4 +- .../mob/living/simple_animal/hostile/mimic.dm | 566 +- .../simple_animal/hostile/mining/goliath.dm | 2 +- .../simple_animal/hostile/mining/gutlunch.dm | 2 +- .../simple_animal/hostile/mining/hivelord.dm | 4 +- .../simple_animal/hostile/mining/mining.dm | 2 +- .../simple_animal/hostile/netherworld.dm | 2 +- .../living/simple_animal/hostile/pirate.dm | 90 +- .../simple_animal/hostile/retaliate/clown.dm | 106 +- .../hostile/retaliate/retaliate.dm | 112 +- .../simple_animal/hostile/retaliate/undead.dm | 2 +- .../living/simple_animal/hostile/russian.dm | 88 +- .../living/simple_animal/hostile/skeleton.dm | 2 +- .../living/simple_animal/hostile/syndicate.dm | 734 +- .../hostile/terror_spiders/actions.dm | 4 +- .../hostile/terror_spiders/black.dm | 2 +- .../hostile/terror_spiders/empress.dm | 8 +- .../hostile/terror_spiders/ghost.dm | 4 +- .../hostile/terror_spiders/gray.dm | 2 +- .../hostile/terror_spiders/hive.dm | 12 +- .../hostile/terror_spiders/purple.dm | 2 +- .../hostile/terror_spiders/queen.dm | 6 +- .../hostile/terror_spiders/reproduction.dm | 10 +- .../hostile/terror_spiders/terror_ai.dm | 6 +- .../hostile/terror_spiders/terror_spiders.dm | 38 +- .../hostile/terror_spiders/white.dm | 2 +- .../mob/living/simple_animal/hostile/tree.dm | 90 +- .../mob/living/simple_animal/parrot.dm | 1458 ++-- .../modules/mob/living/simple_animal/shade.dm | 122 +- .../mob/living/simple_animal/simple_animal.dm | 1230 +-- .../mob/living/simple_animal/slime/death.dm | 2 +- .../mob/living/simple_animal/slime/emote.dm | 2 +- .../mob/living/simple_animal/slime/life.dm | 6 +- .../mob/living/simple_animal/slime/say.dm | 2 +- .../mob/living/simple_animal/slime/slime.dm | 2 +- .../mob/living/simple_animal/tribbles.dm | 8 +- code/modules/mob/living/stat_states.dm | 5 +- code/modules/mob/living/status_procs.dm | 18 +- code/modules/mob/living/taste.dm | 2 +- code/modules/mob/living/update_status.dm | 10 +- code/modules/mob/login.dm | 141 +- code/modules/mob/logout.dm | 41 +- code/modules/mob/mob.dm | 27 +- code/modules/mob/mob_defines.dm | 5 +- code/modules/mob/mob_grab.dm | 904 +-- code/modules/mob/mob_helpers.dm | 1372 ++-- code/modules/mob/mob_movement.dm | 1018 +-- code/modules/mob/new_player/login.dm | 12 +- code/modules/mob/new_player/logout.dm | 14 +- code/modules/mob/new_player/new_player.dm | 1238 +-- code/modules/mob/new_player/poll.dm | 1322 ++-- .../mob/new_player/preferences_setup.dm | 1914 ++--- .../sprite_accessories/diona/diona_hair.dm | 2 +- .../drask/drask_body_markings.dm | 2 +- .../human/human_body_markings.dm | 2 +- .../human/human_facial_hair.dm | 2 +- .../sprite_accessories/human/human_hair.dm | 2 +- .../sprite_accessories/ipc/ipc_face.dm | 2 +- .../sprite_accessories/ipc/ipc_optics.dm | 2 +- .../kidan/kidan_body_markings.dm | 2 +- .../sprite_accessories/kidan/kidan_hair.dm | 2 +- .../kidan/kidan_head_accessories.dm | 2 +- .../nucleation/nucleation_face.dm | 2 +- .../tajaran/tajaran_body_markings.dm | 2 +- .../tajaran/tajaran_facial_hair.dm | 2 +- .../tajaran/tajaran_hair.dm | 2 +- .../tajaran/tajaran_head_markings.dm | 2 +- .../unathi/unathi_alt_heads.dm | 2 +- .../unathi/unathi_body_markings.dm | 2 +- .../unathi/unathi_facial_hair.dm | 2 +- .../unathi/unathi_head_accessories.dm | 2 +- .../vox/vox_body_markings.dm | 2 +- .../sprite_accessories/vox/vox_facial_hair.dm | 2 +- .../vox/vox_tail_markings.dm | 2 +- .../vulpkanin/vulpkanin_head_accessories.dm | 2 +- .../vulpkanin/vulpkanin_head_markings.dm | 2 +- .../vulpkanin/vulpkanin_tail_markings.dm | 2 +- .../sprite_accessories/wryn/wryn_face.dm | 2 +- code/modules/mob/say.dm | 421 +- code/modules/mob/status_procs.dm | 5 +- code/modules/mob/transform_procs.dm | 602 +- code/modules/mob/typing_indicator.dm | 20 +- code/modules/mob/update_icons.dm | 136 +- .../NTNet/NTNRC/conversation.dm | 14 +- code/modules/modular_computers/NTNet/NTNet.dm | 6 +- .../modular_computers/NTNet/NTNet_relay.dm | 22 +- .../computers/item/computer.dm | 2 +- .../computers/item/laptop_presets.dm | 2 +- .../computers/item/processor.dm | 2 +- .../computers/item/tablet.dm | 2 +- .../computers/machinery/console_presets.dm | 2 +- .../computers/machinery/modular_computer.dm | 4 +- .../computers/machinery/modular_console.dm | 2 +- .../file_system/computer_file.dm | 8 +- .../file_system/programs/antagonist/dos.dm | 14 +- .../programs/antagonist/revelation.dm | 2 +- .../file_system/programs/command/card.dm | 32 +- .../file_system/programs/command/comms.dm | 14 +- .../file_system/programs/engineering/alarm.dm | 2 +- .../programs/engineering/power_monitor.dm | 4 +- .../programs/generic/file_browser.dm | 2 +- .../programs/generic/ntdownloader.dm | 12 +- .../programs/generic/ntnrc_client.dm | 6 +- .../programs/generic/nttransfer.dm | 18 +- .../programs/research/airestorer.dm | 2 +- .../programs/research/ntmonitor.dm | 52 +- .../modules/modular_computers/hardware/CPU.dm | 2 +- .../modular_computers/hardware/ai_slot.dm | 2 +- .../hardware/battery_module.dm | 2 +- .../modular_computers/hardware/hard_drive.dm | 2 +- .../hardware/network_card.dm | 8 +- .../modular_computers/hardware/printer.dm | 2 +- .../modular_computers/laptop_vendor.dm | 2 +- code/modules/nano/interaction/admin.dm | 2 +- code/modules/nano/interaction/conscious.dm | 2 +- code/modules/nano/interaction/contained.dm | 2 +- code/modules/nano/interaction/default.dm | 4 +- code/modules/nano/interaction/ghost.dm | 4 +- code/modules/nano/interaction/inventory.dm | 2 +- .../nano/interaction/inventory_deep.dm | 2 +- .../nano/interaction/not_incapacitated.dm | 7 +- code/modules/nano/interaction/physical.dm | 2 +- code/modules/nano/interaction/self.dm | 2 +- code/modules/nano/interaction/zlevel.dm | 2 +- code/modules/nano/modules/alarm_monitor.dm | 6 +- code/modules/nano/modules/atmos_control.dm | 6 +- code/modules/nano/modules/crew_monitor.dm | 6 +- code/modules/nano/modules/ert_manager.dm | 6 +- code/modules/nano/modules/human_appearance.dm | 6 +- code/modules/nano/modules/law_manager.dm | 4 +- code/modules/nano/modules/nano_module.dm | 2 +- code/modules/nano/modules/power_monitor.dm | 8 +- code/modules/nano/nanoexternal.dm | 4 +- code/modules/nano/nanomapgen.dm | 2 +- code/modules/nano/nanoui.dm | 4 +- code/modules/ninja/Ninja_Readme.dm | 2 +- code/modules/ninja/__ninjaDefines.dm | 2 +- code/modules/ninja/admin_ninja_verbs.dm | 2 +- code/modules/ninja/energy_katana.dm | 2 +- code/modules/ninja/ninja_event.dm | 2 +- .../suit/n_suit_verbs/energy_net_nets.dm | 2 +- .../suit/n_suit_verbs/ninja_adrenaline.dm | 2 +- .../suit/n_suit_verbs/ninja_cost_check.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_empulse.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_net.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_smoke.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_stars.dm | 2 +- .../ninja/suit/n_suit_verbs/ninja_stealth.dm | 2 +- .../suit/n_suit_verbs/ninja_teleporting.dm | 2 +- code/modules/ninja/suit/suit_process.dm | 2 +- code/modules/paperwork/contract.dm | 6 +- code/modules/paperwork/fax.dm | 12 +- code/modules/paperwork/faxmachine.dm | 48 +- code/modules/paperwork/filingcabinet.dm | 16 +- code/modules/paperwork/paper.dm | 33 +- code/modules/paperwork/paperbin.dm | 18 +- code/modules/paperwork/photocopier.dm | 14 +- code/modules/paperwork/photography.dm | 1160 +-- code/modules/pda/PDA.dm | 1040 +-- code/modules/pda/ai.dm | 2 +- code/modules/pda/cart.dm | 650 +- code/modules/pda/cart_apps.dm | 18 +- code/modules/pda/core_apps.dm | 6 +- code/modules/pda/messenger.dm | 8 +- code/modules/pda/messenger_plugins.dm | 2 +- code/modules/pda/mob_hunt_game_app.dm | 2 +- code/modules/pda/radio.dm | 416 +- code/modules/power/apc.dm | 2768 +++---- code/modules/power/cable.dm | 1734 ++-- code/modules/power/cable_heavyduty.dm | 2 +- code/modules/power/cable_logic.dm | 584 +- code/modules/power/cell.dm | 712 +- code/modules/power/generator.dm | 500 +- code/modules/power/gravitygenerator.dm | 14 +- code/modules/power/lighting.dm | 1480 ++-- code/modules/power/port_gen.dm | 918 +-- code/modules/power/power.dm | 760 +- code/modules/power/powernet.dm | 2 +- code/modules/power/singularity/collector.dm | 296 +- .../power/singularity/containment_field.dm | 252 +- code/modules/power/singularity/emitter.dm | 692 +- .../power/singularity/field_generator.dm | 674 +- code/modules/power/singularity/generator.dm | 76 +- code/modules/power/singularity/investigate.dm | 2 +- .../particle_accelerator/particle.dm | 118 +- .../particle_accelerator.dm | 720 +- .../particle_accelerator/particle_chamber.dm | 20 +- .../particle_accelerator/particle_control.dm | 558 +- .../particle_accelerator/particle_emitter.dm | 96 +- .../particle_accelerator/particle_power.dm | 12 +- code/modules/power/singularity/singularity.dm | 886 +-- code/modules/power/smes.dm | 986 +-- code/modules/power/solar.dm | 1042 +-- code/modules/power/supermatter/supermatter.dm | 4 +- code/modules/power/terminal.dm | 152 +- code/modules/power/tesla/energy_ball.dm | 4 +- code/modules/power/tesla/generator.dm | 2 +- code/modules/power/tracker.dm | 200 +- code/modules/power/turbine.dm | 810 +- .../procedural_mapping/mapGeneratorModule.dm | 2 +- .../procedural_mapping/mapGeneratorReadme.dm | 2 +- .../mapGenerators/asteroid.dm | 2 +- .../mapGenerators/lava_river.dm | 2 +- .../mapGenerators/lavaland.dm | 2 +- code/modules/projectiles/ammunition.dm | 444 +- code/modules/projectiles/ammunition/boxes.dm | 296 +- .../projectiles/ammunition/magazines.dm | 2 +- code/modules/projectiles/gun.dm | 1048 +-- code/modules/projectiles/guns/energy.dm | 440 +- .../guns/energy/kinetic_accelerator.dm | 2 +- code/modules/projectiles/guns/energy/laser.dm | 318 +- .../projectiles/guns/energy/nuclear.dm | 198 +- code/modules/projectiles/guns/energy/pulse.dm | 170 +- .../projectiles/guns/energy/special.dm | 1038 +-- code/modules/projectiles/guns/energy/stun.dm | 120 +- code/modules/projectiles/guns/magic/wand.dm | 1 + .../projectiles/guns/misc/blastcannon.dm | 2 +- code/modules/projectiles/guns/mounted.dm | 2 +- code/modules/projectiles/guns/projectile.dm | 428 +- .../projectiles/guns/projectile/automatic.dm | 612 +- .../projectiles/guns/projectile/pistol.dm | 236 +- .../projectiles/guns/projectile/revolver.dm | 950 +-- .../projectiles/guns/projectile/shotgun.dm | 662 +- .../projectiles/guns/projectile/sniper.dm | 2 +- .../projectiles/guns/projectile/toy.dm | 2 +- code/modules/projectiles/projectile.dm | 710 +- code/modules/projectiles/projectile/beams.dm | 342 +- .../modules/projectiles/projectile/bullets.dm | 600 +- code/modules/projectiles/projectile/energy.dm | 194 +- code/modules/projectiles/projectile/magic.dm | 8 +- .../projectiles/projectile/reusable.dm | 2 +- .../modules/projectiles/projectile/special.dm | 696 +- code/modules/reagents/chem_splash.dm | 4 +- code/modules/reagents/chemistry/colors.dm | 4 +- .../chemistry/machinery/chem_dispenser.dm | 6 +- .../chemistry/machinery/chem_heater.dm | 2 +- .../chemistry/machinery/chem_master.dm | 4 +- .../reagents/chemistry/machinery/pandemic.dm | 20 +- code/modules/reagents/chemistry/readme.dm | 2 +- code/modules/reagents/chemistry/reagents.dm | 2 +- .../reagents/chemistry/reagents/blob.dm | 2 +- .../reagents/chemistry/reagents/disease.dm | 2 +- .../reagents/chemistry/reagents/drugs.dm | 4 +- .../reagents/chemistry/reagents/food.dm | 2 +- .../reagents/chemistry/reagents/medicine.dm | 37 + .../reagents/chemistry/reagents/misc.dm | 13 +- .../reagents/chemistry/reagents/toxins.dm | 63 +- code/modules/reagents/chemistry/recipes.dm | 2 +- .../reagents/chemistry/recipes/drinks.dm | 2 +- .../reagents/chemistry/recipes/drugs.dm | 2 +- .../reagents/chemistry/recipes/medicine.dm | 9 +- .../reagents/chemistry/recipes/others.dm | 17 + .../reagents/chemistry/recipes/toxins.dm | 11 +- code/modules/reagents/reagent_containers.dm | 148 +- .../reagents/reagent_containers/borghydro.dm | 232 +- .../reagents/reagent_containers/bottle.dm | 2 +- .../reagents/reagent_containers/dropper.dm | 288 +- .../reagent_containers/glass_containers.dm | 2 +- .../reagents/reagent_containers/hypospray.dm | 350 +- .../reagents/reagent_containers/pill.dm | 310 +- .../reagents/reagent_containers/spray.dm | 434 +- .../reagents/reagent_containers/syringes.dm | 463 +- code/modules/reagents/reagent_dispenser.dm | 562 +- code/modules/recycling/conveyor2.dm | 984 +-- .../recycling/disposal-construction.dm | 530 +- code/modules/recycling/disposal.dm | 2768 +++---- code/modules/recycling/sortingmachinery.dm | 889 +-- code/modules/research/circuitprinter.dm | 210 +- code/modules/research/designs.dm | 94 +- .../research/designs/biogenerator_designs.dm | 2 +- .../research/designs/computer_part_designs.dm | 2 +- .../research/designs/mining_designs.dm | 224 +- .../research/designs/smelting_designs.dm | 2 +- .../research/designs/stock_parts_designs.dm | 2 +- code/modules/research/destructive_analyzer.dm | 176 +- code/modules/research/message_server.dm | 760 +- code/modules/research/protolathe.dm | 226 +- code/modules/research/rd-readme.dm | 478 +- code/modules/research/rdconsole.dm | 1896 ++--- code/modules/research/rdmachines.dm | 266 +- code/modules/research/research.dm | 800 +- code/modules/research/server.dm | 720 +- .../research/xenobiology/xenobio_camera.dm | 24 +- code/modules/response_team/ert.dm | 80 +- .../lavalandruin_code/animal_hospital.dm | 2 +- .../ruins/lavalandruin_code/ash_walker_den.dm | 6 +- .../ruins/lavalandruin_code/clown_planet.dm | 2 +- .../ruins/lavalandruin_code/fountain_hall.dm | 2 +- .../ruins/lavalandruin_code/pizzaparty.dm | 2 +- .../modules/ruins/lavalandruin_code/puzzle.dm | 2 +- .../ruins/lavalandruin_code/seed_vault.dm | 2 +- .../ruins/lavalandruin_code/sin_ruins.dm | 2 +- .../ruins/lavalandruin_code/syndicate_base.dm | 2 +- code/modules/ruins/objects_and_mobs/gym.dm | 2 +- .../ruins/objects_and_mobs/necropolis_gate.dm | 2 +- .../security_levels/keycard authentication.dm | 28 +- .../security_levels/security levels.dm | 50 +- code/modules/shuttle/README.md | 2 +- code/modules/shuttle/assault_pod.dm | 4 +- code/modules/shuttle/emergency.dm | 22 +- code/modules/shuttle/ert.dm | 2 +- code/modules/shuttle/navigation_computer.dm | 2 +- code/modules/shuttle/on_move.dm | 2 +- code/modules/shuttle/shuttle.dm | 4 +- code/modules/shuttle/shuttle_manipulator.dm | 8 +- code/modules/shuttle/shuttle_rotate.dm | 2 +- code/modules/shuttle/supply.dm | 8 +- code/modules/shuttle/syndicate.dm | 2 +- code/modules/space_management/level_check.dm | 2 +- code/modules/space_management/level_traits.dm | 16 +- code/modules/space_management/space_chunk.dm | 2 +- code/modules/space_management/space_level.dm | 32 +- .../space_management/space_transition.dm | 6 +- .../space_management/zlevel_manager.dm | 8 +- code/modules/spacepods/construction.dm | 2 +- code/modules/spacepods/lock_buster.dm | 2 +- code/modules/spacepods/spacepod.dm | 1 + code/modules/station_goals/bsa.dm | 6 +- code/modules/station_goals/dna_vault.dm | 8 +- code/modules/station_goals/shield.dm | 2 +- code/modules/station_goals/station_goal.dm | 4 +- code/modules/store/store.dm | 4 +- code/modules/surgery/cavity_implant.dm | 2 +- code/modules/surgery/core_removal.dm | 2 +- code/modules/surgery/dental_implant.dm | 2 +- code/modules/surgery/implant_removal.dm | 2 +- code/modules/surgery/limb_augmentation.dm | 2 +- code/modules/surgery/limb_reattach.dm | 2 +- code/modules/surgery/organs/augments_eyes.dm | 6 +- code/modules/surgery/organs/blood.dm | 8 +- code/modules/surgery/organs/eyes.dm | 10 +- code/modules/surgery/organs/heart.dm | 2 +- code/modules/surgery/organs/helpers.dm | 2 +- code/modules/surgery/organs/kidneys.dm | 2 +- code/modules/surgery/organs/liver.dm | 2 +- code/modules/surgery/organs/lungs.dm | 2 +- code/modules/surgery/organs/mmi_holder.dm | 2 +- code/modules/surgery/organs/organ_external.dm | 4 +- code/modules/surgery/organs/organ_icon.dm | 2 +- code/modules/surgery/organs/organ_internal.dm | 20 +- code/modules/surgery/organs/pain.dm | 2 +- code/modules/surgery/organs/robolimbs.dm | 18 +- .../surgery/organs/subtypes/abductor.dm | 2 +- code/modules/surgery/organs/subtypes/diona.dm | 2 +- code/modules/surgery/organs/subtypes/drask.dm | 2 +- code/modules/surgery/organs/subtypes/grey.dm | 2 +- code/modules/surgery/organs/subtypes/kidan.dm | 2 +- .../surgery/organs/subtypes/machine.dm | 2 +- .../surgery/organs/subtypes/nucleation.dm | 2 +- .../surgery/organs/subtypes/plasmaman.dm | 2 +- .../modules/surgery/organs/subtypes/shadow.dm | 2 +- .../modules/surgery/organs/subtypes/skrell.dm | 2 +- code/modules/surgery/organs/subtypes/slime.dm | 2 +- .../surgery/organs/subtypes/tajaran.dm | 2 +- .../modules/surgery/organs/subtypes/unathi.dm | 2 +- .../surgery/organs/subtypes/unbreakable.dm | 2 +- code/modules/surgery/organs/subtypes/vox.dm | 2 +- .../surgery/organs/subtypes/vulpkanin.dm | 2 +- code/modules/surgery/organs/subtypes/wryn.dm | 2 +- code/modules/surgery/organs/vocal_cords.dm | 150 +- code/modules/surgery/plastic_surgery.dm | 2 +- .../modules/surgery/remove_embedded_object.dm | 2 +- code/modules/surgery/robotics.dm | 2 +- code/modules/telesci/bscrystal.dm | 4 +- code/modules/telesci/gps.dm | 12 +- code/modules/tram/tram.dm | 11 +- code/modules/tram/tram_control_pad.dm | 2 +- code/modules/tram/tram_floor.dm | 4 +- code/modules/tram/tram_rail.dm | 2 +- code/modules/tram/tram_wall.dm | 4 +- code/modules/vehicle/ambulance.dm | 2 +- code/modules/vehicle/atv.dm | 2 +- code/modules/vehicle/janicart.dm | 2 +- code/modules/vehicle/motorcycle.dm | 2 +- code/modules/vehicle/secway.dm | 2 +- code/modules/vehicle/speedbike.dm | 2 +- code/modules/vehicle/sportscar.dm | 2 +- code/modules/vehicle/vehicle.dm | 2 +- code/modules/zombie/items.dm | 2 +- code/modules/zombie/organs.dm | 2 +- code/world.dm | 18 +- goon/browserassets/css/browserOutput-dark.css | 2 + goon/browserassets/css/browserOutput.css | 4 + goon/code/datums/browserOutput.dm | 2 +- icons/mob/alienqueen.dmi | Bin 52590 -> 43924 bytes icons/mob/inhands/items_lefthand.dmi | Bin 153786 -> 152689 bytes icons/mob/inhands/items_righthand.dmi | Bin 150805 -> 152133 bytes icons/mob/lavaland/blood_drunk.dmi | Bin 0 -> 6257 bytes icons/obj/food/food.dmi | Bin 97979 -> 98640 bytes icons/obj/trash.dmi | Bin 11560 -> 12110 bytes icons/paper_icons/ntlogo.png | Bin 9990 -> 6673 bytes icons/paper_icons/syndielogo.png | Bin 0 -> 7734 bytes interface/interface.dm | 4 +- nano/package-lock.json | 102 +- sound/items/PDA/__credits.dm | 2 +- ...gnTimeResolveAssemblyReferencesInput.cache | Bin 6853 -> 0 bytes tools/travis/README.MD | 3 + tools/travis/check_grep.sh | 34 + tools/travis/check_line_endings.py | 49 + 1778 files changed, 184987 insertions(+), 184164 deletions(-) create mode 100644 .editorconfig create mode 100644 .github/workflows/label_merge_conflicts.yml create mode 100644 code/__DEFINES/dna.dm create mode 100644 code/__DEFINES/logs.dm delete mode 100644 code/_globalvars/database.dm create mode 100644 code/_globalvars/sensitive.dm delete mode 100644 code/_globalvars/unused.dm create mode 100644 code/datums/log_record.dm create mode 100644 code/datums/log_viewer.dm create mode 100644 code/modules/admin/verbs/logging_view.dm delete mode 100644 code/modules/mob/abilities.dm create mode 100644 icons/mob/lavaland/blood_drunk.dmi create mode 100644 icons/paper_icons/syndielogo.png delete mode 100644 tools/midi2piano/midi2piano/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache create mode 100644 tools/travis/README.MD create mode 100644 tools/travis/check_grep.sh create mode 100644 tools/travis/check_line_endings.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000000..1770177310de --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +[*] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf + +[*.yml] +indent_style = space +indent_size = 2 + +[*.py] +indent_style = space diff --git a/.github/workflows/label_merge_conflicts.yml b/.github/workflows/label_merge_conflicts.yml new file mode 100644 index 000000000000..d2a56e2ef3d4 --- /dev/null +++ b/.github/workflows/label_merge_conflicts.yml @@ -0,0 +1,13 @@ +name: 'Merge Conflict Detection' +on: + push: + branches: + - master +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: mschilde/auto-label-merge-conflicts@master + with: + CONFLICT_LABEL_NAME: 'Merge Conflict' + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 74660294c3bc..e76c9f6db1d9 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ stddef.dm /tools/dmitool/.project # ignore midi2piano build cache -/tools/midi2piano/obj/* - +/tools/midi2piano/midi2piano/obj/* diff --git a/.travis.yml b/.travis.yml index 5a731170a79a..7f74746d15fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ jobs: before_script: skip script: - shopt -s globstar - - (! grep 'step_[xy]' _maps/map_files/**/*.dmm) + - python3 tools/travis/check_line_endings.py - (num=$(grep -Ern '\\(red|blue|green|black|italic|bold|b|i[^mc])' code/ | wc -l); echo "$num BYOND text macros (expecting ${BYOND_MACRO_COUNT} or fewer)"; [ $num -le ${BYOND_MACRO_COUNT} ]) - md5sum -c - <<< "6dc1b6bf583f3bd4176b6df494caa5f1 *html/changelogs/example.yml" - python tools/ss13_genchangelog.py html/changelog.html html/changelogs diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9099488143bb..9ab4061ce6ad 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,6 +1,7 @@ { "recommendations": [ "gbasood.byond-dm-language-support", - "platymuus.dm-langclient" + "platymuus.dm-langclient", + "EditorConfig.EditorConfig" ] } diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm index 279afa9713d5..e00aedc8100d 100644 --- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_syndicate_base1.dmm @@ -1789,7 +1789,6 @@ /area/ruin/unpowered/syndicate_lava_base/cargo) "ed" = ( /obj/structure/table, -/obj/item/paper_bin, /obj/item/pen, /obj/effect/decal/cleanable/dirt, /obj/effect/decal/cleanable/dirt, @@ -1803,6 +1802,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/cargo) "ef" = ( @@ -1898,7 +1898,6 @@ "ep" = ( /obj/structure/table/reinforced, /obj/effect/decal/cleanable/dirt, -/obj/item/paper_bin, /obj/item/pen, /obj/machinery/atmospherics/pipe/simple/hidden/supply, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers, @@ -1912,6 +1911,7 @@ /obj/effect/turf_decal/tile/neutral{ dir = 8 }, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/testlab) "eq" = ( @@ -6790,8 +6790,8 @@ /area/ruin/unpowered/syndicate_lava_base/telecomms) "ov" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin, /obj/item/pen, +/obj/item/paper_bin/syndicate, /turf/simulated/floor/plasteel/dark, /area/ruin/unpowered/syndicate_lava_base/telecomms) "ox" = ( diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 74e179255ce4..7cf27593f6f1 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -1619,15 +1619,12 @@ /area/security/range) "adH" = ( /obj/structure/table/wood, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen/multi, /obj/machinery/light{ dir = 1; on = 1 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ icon_state = "dark" }, @@ -16077,16 +16074,13 @@ /area/lawoffice) "aAI" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, /obj/machinery/alarm{ dir = 8; pixel_x = 25; pixel_y = 0 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ tag = "icon-cult"; icon_state = "cult"; @@ -16863,11 +16857,8 @@ /area/magistrateoffice) "aCj" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/stamp/magistrate, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/magistrateoffice) "aCk" = ( @@ -21875,11 +21866,8 @@ /area/maintenance/fpmaint2) "aMO" = ( /obj/structure/table/reinforced, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ tag = "icon-cult"; icon_state = "cult"; @@ -27795,16 +27783,13 @@ /obj/item/folder/yellow, /obj/item/stamp/ce, /obj/item/book/manual/sop_engineering, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/storage/fancy/cigarettes, /obj/item/lighter/zippo, /obj/item/pen/multi, /obj/item/enginepicker{ layer = 3.1 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 8; icon_state = "neutralfull" @@ -43355,12 +43340,9 @@ }, /area/hallway/primary/central/west) "bCG" = ( -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen, /obj/structure/table/wood, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/bridge/meeting_room) "bCH" = ( @@ -47113,10 +47095,6 @@ /area/shuttle/constructionsite) "bJN" = ( /obj/structure/table/wood, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 10 - }, /obj/item/pen/multi/fountain, /obj/machinery/door_control{ id = "captainofficedoor"; @@ -47129,6 +47107,7 @@ /obj/item/radio/intercom{ pixel_x = -28 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/wood, /area/crew_quarters/captain) "bJO" = ( @@ -50576,15 +50555,12 @@ }, /area/toxins/lab) "bPE" = ( -/obj/item/paper_bin{ - pixel_x = 1; - pixel_y = 9 - }, /obj/structure/table/glass, /obj/item/pen/multi, /obj/item/book/manual/sop_science{ pixel_y = -14 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 2; icon_state = "cafeteria"; @@ -54151,10 +54127,6 @@ /area/hallway/primary/central/se) "bVy" = ( /obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, /obj/item/pen/multi, /obj/item/pen/multi, /obj/item/megaphone, @@ -55112,6 +55084,7 @@ /obj/item/book/manual/sop_service, /obj/item/book/manual/sop_supply, /obj/item/book/manual/sop_command, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel, /area/crew_quarters/heads) "bXa" = ( @@ -60880,7 +60853,6 @@ /area/medical/cmo) "cfW" = ( /obj/structure/table/glass, -/obj/item/paper_bin, /obj/item/pen/multi, /obj/item/folder/white{ pixel_y = 7 @@ -60890,6 +60862,7 @@ pixel_x = 32; pixel_y = 0 }, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/plasteel{ dir = 4; icon_state = "darkblue" @@ -67468,12 +67441,12 @@ /area/blueshield) "cqS" = ( /obj/structure/table/wood, -/obj/item/paper_bin, /obj/item/flashlight/lamp/green{ pixel_x = -5; pixel_y = 12 }, /obj/item/paper/ntrep, +/obj/item/paper_bin/nanotrasen, /turf/simulated/floor/carpet, /area/ntrep) "cqT" = ( diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 157b03129952..507142c0892c 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -1,345 +1,345 @@ -/* -Quick overview: - -Pipes combine to form pipelines -Pipelines and other atmospheric objects combine to form pipe_networks - Note: A single pipe_network represents a completely open space - -Pipes -> Pipelines -Pipelines + Other Objects -> Pipe network -*/ -GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) -/obj/machinery/atmospherics - anchored = 1 - layer = GAS_PIPE_HIDDEN_LAYER //under wires - resistance_flags = FIRE_PROOF - max_integrity = 200 - plane = FLOOR_PLANE - idle_power_usage = 0 - active_power_usage = 0 - power_channel = ENVIRON - on_blueprints = TRUE - var/nodealert = 0 - var/can_unwrench = 0 - - var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber - var/connected_to = 1 //same as above, currently not used for anything - var/icon_connect_type = "" //"-supply" or "-scrubbers" - - var/initialize_directions = 0 - - var/pipe_color - var/obj/item/pipe/stored - var/image/pipe_image - -/obj/machinery/atmospherics/New() - if (!armor) - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) - ..() - - if(!pipe_color) - pipe_color = color - color = null - - if(!pipe_color_check(pipe_color)) - pipe_color = null - -/obj/machinery/atmospherics/Initialize() - . = ..() - SSair.atmos_machinery += src - -/obj/machinery/atmospherics/proc/atmos_init() - if(can_unwrench) - stored = new(src, make_from = src) - -/obj/machinery/atmospherics/Destroy() - QDEL_NULL(stored) - SSair.atmos_machinery -= src - SSair.deferred_pipenet_rebuilds -= src - for(var/mob/living/L in src) //ventcrawling is serious business - L.remove_ventcrawl() - L.forceMove(get_turf(src)) - QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else - return ..() - -// Icons/overlays/underlays -/obj/machinery/atmospherics/update_icon() - var/turf/T = get_turf(loc) - if(!T || level == 2 || !T.intact) - plane = GAME_PLANE - else - plane = FLOOR_PLANE - -/obj/machinery/atmospherics/proc/update_pipe_image() - pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view) - pipe_image.plane = HUD_PLANE - -/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) - if(!istype(GLOB.pipe_icon_manager)) - if(!safety) //to prevent infinite loops - GLOB.pipe_icon_manager = new() - check_icon_cache(1) - return 0 - - return 1 - -/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) - //Don't use this for standard pipes - if(!istype(node)) - return null - - return node.pipe_color - -/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) - if(node) - if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) - -/obj/machinery/atmospherics/proc/update_underlays() - if(check_icon_cache()) - return 1 - else - return 0 - -// Connect types -/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) - var/i - var/list1[] = atmos1.connect_types - var/list2[] = atmos2.connect_types - for(i=1,i<=list1.len,i++) - var/j - for(j=1,j<=list2.len,j++) - if(list1[i] == list2[j]) - var/n = list1[i] - return n - return 0 - -/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) - var/i - var/list1[] = atmos1.connect_types - var/list2[] = pipe2.connect_types - for(i=1,i<=list1.len,i++) - var/j - for(j=1,j<=list2.len,j++) - if(list1[i] == list2[j]) - var/n = list1[i] - return n - return 0 - -// Pipenet related functions -/obj/machinery/atmospherics/proc/returnPipenet() - return - -/obj/machinery/atmospherics/proc/returnPipenetAir() - return - -/obj/machinery/atmospherics/proc/setPipenet() - return - -/obj/machinery/atmospherics/proc/replacePipenet() - return - -/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE) - // Called to build a network from this node - if(remove_deferral) - SSair.deferred_pipenet_rebuilds -= src - -/obj/machinery/atmospherics/proc/defer_build_network() - SSair.deferred_pipenet_rebuilds += src - -/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) - return - -/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P) - if(P) - P.other_atmosmch -= src - -//(De)construction -/obj/machinery/atmospherics/attackby(obj/item/W, mob/user) - if(can_unwrench && istype(W, /obj/item/wrench)) - var/turf/T = get_turf(src) - if(level == 1 && isturf(T) && T.intact) - to_chat(user, "You must remove the plating first.") - return 1 - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - add_fingerprint(user) - - var/unsafe_wrenching = FALSE - var/I = int_air ? int_air.return_pressure() : 0 - var/E = env_air ? env_air.return_pressure() : 0 - var/internal_pressure = I - E - - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(internal_pressure > 2*ONE_ATMOSPHERE) - to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") - unsafe_wrenching = TRUE //Oh dear oh dear - - if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - investigate_log("was REMOVED by [key_name(usr)]", "atmos") - - //You unwrenched a pipe full of pressure? let's splat you into the wall silly. - if(unsafe_wrenching) - unsafe_pressure_release(user,internal_pressure) - deconstruct(TRUE) - else - return ..() - -//Called when an atmospherics object is unwrenched while having a large pressure difference -//with it's locs air contents. -/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures) - if(!user) - return - - if(!pressures) - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - pressures = int_air.return_pressure() - env_air.return_pressure() - - var/fuck_you_dir = get_dir(src, user) - var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir) - user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") - //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed - user.throw_at(general_direction, pressures/10, pressures/50) - -/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(can_unwrench) - if(stored) - stored.forceMove(get_turf(src)) - if(!disassembled) - stored.obj_integrity = stored.max_integrity * 0.5 - transfer_fingerprints_to(stored) - stored = null - ..() - -/obj/machinery/atmospherics/on_construction(D, P, C) - if(C) - color = C - dir = D - initialize_directions = P - var/turf/T = loc - level = T.intact ? 2 : 1 - add_fingerprint(usr) - if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets - SSair.machinery_to_construct.Add(src) - return - initialize_atmos_network() - -/obj/machinery/atmospherics/proc/initialize_atmos_network() - atmos_init() - var/list/nodes = pipeline_expansion() - for(var/obj/machinery/atmospherics/A in nodes) - A.atmos_init() - A.addMember(src) - build_network() - -// Find a connecting /obj/machinery/atmospherics in specified direction. -/obj/machinery/atmospherics/proc/findConnecting(var/direction) - for(var/obj/machinery/atmospherics/target in get_step(src,direction)) - var/can_connect = check_connect_types(target, src) - if(can_connect && (target.initialize_directions & get_dir(target,src))) - return target - -// Ventcrawling -#define VENT_SOUND_DELAY 30 -/obj/machinery/atmospherics/relaymove(mob/living/user, direction) - direction &= initialize_directions - if(!direction || !(direction in cardinal)) //cant go this way. - return - - if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug - return - - var/obj/machinery/atmospherics/target_move = findConnecting(direction) - if(target_move) - if(is_type_in_list(target_move, ventcrawl_machinery) && target_move.can_crawl_through()) - user.remove_ventcrawl() - user.forceMove(target_move.loc) //handles entering and so on - user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.") - else if(target_move.can_crawl_through()) - if(returnPipenet() != target_move.returnPipenet()) - user.update_pipe_vision(target_move) - user.loc = target_move - user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement - if(world.time - user.last_played_vent > VENT_SOUND_DELAY) - user.last_played_vent = world.time - playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) - else - if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent - user.remove_ventcrawl() - user.forceMove(src.loc) - user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") - user.canmove = 0 - spawn(1) - user.canmove = 1 - -/obj/machinery/atmospherics/AltClick(var/mob/living/L) - if(is_type_in_list(src, ventcrawl_machinery)) - L.handle_ventcrawl(src) - return - ..() - -/obj/machinery/atmospherics/proc/can_crawl_through() - return 1 - -/obj/machinery/atmospherics/proc/change_color(var/new_color) - //only pass valid pipe colors please ~otherwise your pipe will turn invisible - if(!pipe_color_check(new_color)) - return - - pipe_color = new_color - update_icon() - -// Additional icon procs -/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) - var/turf/T = get_turf(src) - if(!istype(T)) return - if(node) - var/node_dir = get_dir(src,node) - if(node.icon_connect_type == "-supply") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, node, node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else if(node.icon_connect_type == "-scrubbers") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, node, node_dir, "-scrubbers") - else - add_underlay_adapter(T, node, node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else - add_underlay_adapter(T, , direction, "-supply") - add_underlay_adapter(T, , direction, "-scrubbers") - add_underlay_adapter(T, , direction, "") - -/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays - if(node) - if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) - -/obj/machinery/atmospherics/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - deconstruct(FALSE) - return ..() - -/obj/machinery/atmospherics/update_remote_sight(mob/user) - user.sight |= (SEE_TURFS|BLIND) - . = ..() +/* +Quick overview: + +Pipes combine to form pipelines +Pipelines and other atmospheric objects combine to form pipe_networks + Note: A single pipe_network represents a completely open space + +Pipes -> Pipelines +Pipelines + Other Objects -> Pipe network +*/ +GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) +/obj/machinery/atmospherics + anchored = 1 + layer = GAS_PIPE_HIDDEN_LAYER //under wires + resistance_flags = FIRE_PROOF + max_integrity = 200 + plane = FLOOR_PLANE + idle_power_usage = 0 + active_power_usage = 0 + power_channel = ENVIRON + on_blueprints = TRUE + var/nodealert = 0 + var/can_unwrench = 0 + + var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber + var/connected_to = 1 //same as above, currently not used for anything + var/icon_connect_type = "" //"-supply" or "-scrubbers" + + var/initialize_directions = 0 + + var/pipe_color + var/obj/item/pipe/stored + var/image/pipe_image + +/obj/machinery/atmospherics/New() + if (!armor) + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) + ..() + + if(!pipe_color) + pipe_color = color + color = null + + if(!pipe_color_check(pipe_color)) + pipe_color = null + +/obj/machinery/atmospherics/Initialize() + . = ..() + SSair.atmos_machinery += src + +/obj/machinery/atmospherics/proc/atmos_init() + if(can_unwrench) + stored = new(src, make_from = src) + +/obj/machinery/atmospherics/Destroy() + QDEL_NULL(stored) + SSair.atmos_machinery -= src + SSair.deferred_pipenet_rebuilds -= src + for(var/mob/living/L in src) //ventcrawling is serious business + L.remove_ventcrawl() + L.forceMove(get_turf(src)) + QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else + return ..() + +// Icons/overlays/underlays +/obj/machinery/atmospherics/update_icon() + var/turf/T = get_turf(loc) + if(!T || level == 2 || !T.intact) + plane = GAME_PLANE + else + plane = FLOOR_PLANE + +/obj/machinery/atmospherics/proc/update_pipe_image() + pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view) + pipe_image.plane = HUD_PLANE + +/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) + if(!istype(GLOB.pipe_icon_manager)) + if(!safety) //to prevent infinite loops + GLOB.pipe_icon_manager = new() + check_icon_cache(1) + return 0 + + return 1 + +/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) + //Don't use this for standard pipes + if(!istype(node)) + return null + + return node.pipe_color + +/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) + if(node) + if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) + +/obj/machinery/atmospherics/proc/update_underlays() + if(check_icon_cache()) + return 1 + else + return 0 + +// Connect types +/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) + var/i + var/list1[] = atmos1.connect_types + var/list2[] = atmos2.connect_types + for(i=1,i<=list1.len,i++) + var/j + for(j=1,j<=list2.len,j++) + if(list1[i] == list2[j]) + var/n = list1[i] + return n + return 0 + +/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) + var/i + var/list1[] = atmos1.connect_types + var/list2[] = pipe2.connect_types + for(i=1,i<=list1.len,i++) + var/j + for(j=1,j<=list2.len,j++) + if(list1[i] == list2[j]) + var/n = list1[i] + return n + return 0 + +// Pipenet related functions +/obj/machinery/atmospherics/proc/returnPipenet() + return + +/obj/machinery/atmospherics/proc/returnPipenetAir() + return + +/obj/machinery/atmospherics/proc/setPipenet() + return + +/obj/machinery/atmospherics/proc/replacePipenet() + return + +/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE) + // Called to build a network from this node + if(remove_deferral) + SSair.deferred_pipenet_rebuilds -= src + +/obj/machinery/atmospherics/proc/defer_build_network() + SSair.deferred_pipenet_rebuilds += src + +/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) + return + +/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P) + if(P) + P.other_atmosmch -= src + +//(De)construction +/obj/machinery/atmospherics/attackby(obj/item/W, mob/user) + if(can_unwrench && istype(W, /obj/item/wrench)) + var/turf/T = get_turf(src) + if(level == 1 && isturf(T) && T.intact) + to_chat(user, "You must remove the plating first.") + return 1 + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + add_fingerprint(user) + + var/unsafe_wrenching = FALSE + var/I = int_air ? int_air.return_pressure() : 0 + var/E = env_air ? env_air.return_pressure() : 0 + var/internal_pressure = I - E + + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(internal_pressure > 2*ONE_ATMOSPHERE) + to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") + unsafe_wrenching = TRUE //Oh dear oh dear + + if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + investigate_log("was REMOVED by [key_name(usr)]", "atmos") + + //You unwrenched a pipe full of pressure? let's splat you into the wall silly. + if(unsafe_wrenching) + unsafe_pressure_release(user,internal_pressure) + deconstruct(TRUE) + else + return ..() + +//Called when an atmospherics object is unwrenched while having a large pressure difference +//with it's locs air contents. +/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures) + if(!user) + return + + if(!pressures) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + pressures = int_air.return_pressure() - env_air.return_pressure() + + var/fuck_you_dir = get_dir(src, user) + var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir) + user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") + //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed + user.throw_at(general_direction, pressures/10, pressures/50) + +/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(can_unwrench) + if(stored) + stored.forceMove(get_turf(src)) + if(!disassembled) + stored.obj_integrity = stored.max_integrity * 0.5 + transfer_fingerprints_to(stored) + stored = null + ..() + +/obj/machinery/atmospherics/on_construction(D, P, C) + if(C) + color = C + dir = D + initialize_directions = P + var/turf/T = loc + level = T.intact ? 2 : 1 + add_fingerprint(usr) + if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets + SSair.machinery_to_construct.Add(src) + return + initialize_atmos_network() + +/obj/machinery/atmospherics/proc/initialize_atmos_network() + atmos_init() + var/list/nodes = pipeline_expansion() + for(var/obj/machinery/atmospherics/A in nodes) + A.atmos_init() + A.addMember(src) + build_network() + +// Find a connecting /obj/machinery/atmospherics in specified direction. +/obj/machinery/atmospherics/proc/findConnecting(var/direction) + for(var/obj/machinery/atmospherics/target in get_step(src,direction)) + var/can_connect = check_connect_types(target, src) + if(can_connect && (target.initialize_directions & get_dir(target,src))) + return target + +// Ventcrawling +#define VENT_SOUND_DELAY 30 +/obj/machinery/atmospherics/relaymove(mob/living/user, direction) + direction &= initialize_directions + if(!direction || !(direction in GLOB.cardinal)) //cant go this way. + return + + if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug + return + + var/obj/machinery/atmospherics/target_move = findConnecting(direction) + if(target_move) + if(is_type_in_list(target_move, GLOB.ventcrawl_machinery) && target_move.can_crawl_through()) + user.remove_ventcrawl() + user.forceMove(target_move.loc) //handles entering and so on + user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.") + else if(target_move.can_crawl_through()) + if(returnPipenet() != target_move.returnPipenet()) + user.update_pipe_vision(target_move) + user.loc = target_move + user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement + if(world.time - user.last_played_vent > VENT_SOUND_DELAY) + user.last_played_vent = world.time + playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) + else + if((direction & initialize_directions) || is_type_in_list(src, GLOB.ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent + user.remove_ventcrawl() + user.forceMove(src.loc) + user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") + user.canmove = 0 + spawn(1) + user.canmove = 1 + +/obj/machinery/atmospherics/AltClick(var/mob/living/L) + if(is_type_in_list(src, GLOB.ventcrawl_machinery)) + L.handle_ventcrawl(src) + return + ..() + +/obj/machinery/atmospherics/proc/can_crawl_through() + return 1 + +/obj/machinery/atmospherics/proc/change_color(var/new_color) + //only pass valid pipe colors please ~otherwise your pipe will turn invisible + if(!pipe_color_check(new_color)) + return + + pipe_color = new_color + update_icon() + +// Additional icon procs +/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) + var/turf/T = get_turf(src) + if(!istype(T)) return + if(node) + var/node_dir = get_dir(src,node) + if(node.icon_connect_type == "-supply") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, node, node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else if(node.icon_connect_type == "-scrubbers") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, node, node_dir, "-scrubbers") + else + add_underlay_adapter(T, node, node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else + add_underlay_adapter(T, , direction, "-supply") + add_underlay_adapter(T, , direction, "-scrubbers") + add_underlay_adapter(T, , direction, "") + +/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays + if(node) + if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) + +/obj/machinery/atmospherics/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + deconstruct(FALSE) + return ..() + +/obj/machinery/atmospherics/update_remote_sight(mob/user) + user.sight |= (SEE_TURFS|BLIND) + . = ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 68a1033a266e..0a8b9eb95a4c 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,155 +1,155 @@ -/obj/machinery/atmospherics/binary - dir = SOUTH - initialize_directions = SOUTH|NORTH - use_power = IDLE_POWER_USE - - layer = GAS_PUMP_LAYER - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - - var/datum/pipeline/parent1 - var/datum/pipeline/parent2 - -/obj/machinery/atmospherics/binary/New() - ..() - switch(dir) - if(NORTH) - initialize_directions = NORTH|SOUTH - if(SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST) - initialize_directions = EAST|WEST - if(WEST) - initialize_directions = EAST|WEST - - air1 = new - air2 = new - - air1.volume = 200 - air2.volume = 200 - -/obj/machinery/atmospherics/binary/Destroy() - if(node1) - node1.disconnect(src) - node1 = null - nullifyPipenet(parent1) - if(node2) - node2.disconnect(src) - node2 = null - nullifyPipenet(parent2) - return ..() - -/obj/machinery/atmospherics/binary/atmos_init() - ..() - var/node2_connect = dir - var/node1_connect = turn(dir, 180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - var/c = check_connect_types(target,src) - if(c) - target.connected_to = c - connected_to = c - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - var/c = check_connect_types(target,src) - if(c) - target.connected_to = c - connected_to = c - node2 = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE) - if(!parent1) - parent1 = new /datum/pipeline() - parent1.build_pipeline(src) - - if(!parent2) - parent2 = new /datum/pipeline() - parent2.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - qdel(parent1) - node1 = null - else if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - qdel(parent2) - node2 = null - update_icon() - -/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - if(P == parent1) - parent1.other_airs -= air1 - parent1 = null - else if(P == parent2) - parent2.other_airs -= air2 - parent2 = null - -/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P) - if(P == parent1) - return air1 - else if(P == parent2) - return air2 - -/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P) - if(P) - if(parent1 == P) - return list(node1) - else if(parent2 == P) - return list(node2) - else - return list(node1, node2) - -/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - if(A == node1) - parent1 = P - else if(A == node2) - parent2 = P - -/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A) - if(A == node1) - return parent1 - else if(A == node2) - return parent2 - -/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - if(Old == parent1) - parent1 = New - else if(Old == parent2) - parent2 = New - -/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures) - ..() - - var/turf/T = get_turf(src) - if(T) - //Remove the gas from air1+air2 and assume it - var/datum/gas_mixture/environment = T.return_air() - var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - var/shared_loss = lost/2 - - var/datum/gas_mixture/to_release = air1.remove(shared_loss) - to_release.merge(air2.remove(shared_loss)) - T.assume_air(to_release) - air_update_turf(1) - -/obj/machinery/atmospherics/binary/process_atmos() - ..() - return parent1 && parent2 +/obj/machinery/atmospherics/binary + dir = SOUTH + initialize_directions = SOUTH|NORTH + use_power = IDLE_POWER_USE + + layer = GAS_PUMP_LAYER + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + + var/datum/pipeline/parent1 + var/datum/pipeline/parent2 + +/obj/machinery/atmospherics/binary/New() + ..() + switch(dir) + if(NORTH) + initialize_directions = NORTH|SOUTH + if(SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST) + initialize_directions = EAST|WEST + if(WEST) + initialize_directions = EAST|WEST + + air1 = new + air2 = new + + air1.volume = 200 + air2.volume = 200 + +/obj/machinery/atmospherics/binary/Destroy() + if(node1) + node1.disconnect(src) + node1 = null + nullifyPipenet(parent1) + if(node2) + node2.disconnect(src) + node2 = null + nullifyPipenet(parent2) + return ..() + +/obj/machinery/atmospherics/binary/atmos_init() + ..() + var/node2_connect = dir + var/node1_connect = turn(dir, 180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + var/c = check_connect_types(target,src) + if(c) + target.connected_to = c + connected_to = c + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + var/c = check_connect_types(target,src) + if(c) + target.connected_to = c + connected_to = c + node2 = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE) + if(!parent1) + parent1 = new /datum/pipeline() + parent1.build_pipeline(src) + + if(!parent2) + parent2 = new /datum/pipeline() + parent2.build_pipeline(src) + ..() + +/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + qdel(parent1) + node1 = null + else if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + qdel(parent2) + node2 = null + update_icon() + +/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P) + ..() + if(!P) + return + if(P == parent1) + parent1.other_airs -= air1 + parent1 = null + else if(P == parent2) + parent2.other_airs -= air2 + parent2 = null + +/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P) + if(P == parent1) + return air1 + else if(P == parent2) + return air2 + +/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P) + if(P) + if(parent1 == P) + return list(node1) + else if(parent2 == P) + return list(node2) + else + return list(node1, node2) + +/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) + if(A == node1) + parent1 = P + else if(A == node2) + parent2 = P + +/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A) + if(A == node1) + return parent1 + else if(A == node2) + return parent2 + +/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) + if(Old == parent1) + parent1 = New + else if(Old == parent2) + parent2 = New + +/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures) + ..() + + var/turf/T = get_turf(src) + if(T) + //Remove the gas from air1+air2 and assume it + var/datum/gas_mixture/environment = T.return_air() + var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + var/shared_loss = lost/2 + + var/datum/gas_mixture/to_release = air1.remove(shared_loss) + to_release.merge(air2.remove(shared_loss)) + T.assume_air(to_release) + air_update_turf(1) + +/obj/machinery/atmospherics/binary/process_atmos() + ..() + return parent1 && parent2 diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index fbba6f809ed6..b45dd8929b48 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -1,125 +1,125 @@ -//node1, air1, network1 correspond to input -//node2, air2, network2 correspond to output -#define CIRC_LEFT WEST -#define CIRC_RIGHT EAST - -/obj/machinery/atmospherics/binary/circulator - name = "circulator/heat exchanger" - desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side." - icon = 'icons/obj/atmospherics/circulator.dmi' - icon_state = "circ1-off" - - var/side = CIRC_LEFT - - var/last_pressure_delta = 0 - - var/obj/machinery/power/generator/generator - - anchored = 1 - density = 1 - - can_unwrench = 1 - var/side_inverted = 0 - -// Creating a custom circulator pipe subtype to be delivered through cargo -/obj/item/pipe/circulator - name = "circulator/heat exchanger fitting" - -/obj/item/pipe/circulator/New(loc) - var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null) - ..(loc, make_from = C) - -/obj/machinery/atmospherics/binary/circulator/Destroy() - if(generator && generator.cold_circ == src) - generator.cold_circ = null - else if(generator && generator.hot_circ == src) - generator.hot_circ = null - return ..() - -/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() - var/datum/gas_mixture/inlet = get_inlet_air() - var/datum/gas_mixture/outlet = get_outlet_air() - var/output_starting_pressure = outlet.return_pressure() - var/input_starting_pressure = inlet.return_pressure() - - if(output_starting_pressure >= input_starting_pressure - 10) - //Need at least 10 KPa difference to overcome friction in the mechanism - last_pressure_delta = 0 - return null - - //Calculate necessary moles to transfer using PV = nRT - if(inlet.temperature > 0) - var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2 - - var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION) - - last_pressure_delta = pressure_delta - - //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];") - - //Actually transfer the gas - var/datum/gas_mixture/removed = inlet.remove(transfer_moles) - - parent1.update = 1 - parent2.update = 1 - - return removed - - else - last_pressure_delta = 0 - -/obj/machinery/atmospherics/binary/circulator/process_atmos() - ..() - update_icon() - -/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air() - if(side_inverted==0) - return air2 - else - return air1 - -/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air() - if(side_inverted==0) - return air1 - else - return air2 - -/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side() - if(dir==SOUTH||dir==NORTH) - if(side_inverted==0) - return "South" - else - return "North" - -/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side() - if(dir==SOUTH||dir==NORTH) - if(side_inverted==0) - return "North" - else - return "South" - -/obj/machinery/atmospherics/binary/circulator/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!side_inverted) - side_inverted = TRUE - else - side_inverted = FALSE - to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.") - desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side." - -/obj/machinery/atmospherics/binary/circulator/update_icon() - ..() - - if(stat & (BROKEN|NOPOWER)) - icon_state = "circ[side]-p" - else if(last_pressure_delta > 0) - if(last_pressure_delta > ONE_ATMOSPHERE) - icon_state = "circ[side]-run" - else - icon_state = "circ[side]-slow" - else - icon_state = "circ[side]-off" - - return 1 \ No newline at end of file +//node1, air1, network1 correspond to input +//node2, air2, network2 correspond to output +#define CIRC_LEFT WEST +#define CIRC_RIGHT EAST + +/obj/machinery/atmospherics/binary/circulator + name = "circulator/heat exchanger" + desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side." + icon = 'icons/obj/atmospherics/circulator.dmi' + icon_state = "circ1-off" + + var/side = CIRC_LEFT + + var/last_pressure_delta = 0 + + var/obj/machinery/power/generator/generator + + anchored = 1 + density = 1 + + can_unwrench = 1 + var/side_inverted = 0 + +// Creating a custom circulator pipe subtype to be delivered through cargo +/obj/item/pipe/circulator + name = "circulator/heat exchanger fitting" + +/obj/item/pipe/circulator/New(loc) + var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null) + ..(loc, make_from = C) + +/obj/machinery/atmospherics/binary/circulator/Destroy() + if(generator && generator.cold_circ == src) + generator.cold_circ = null + else if(generator && generator.hot_circ == src) + generator.hot_circ = null + return ..() + +/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() + var/datum/gas_mixture/inlet = get_inlet_air() + var/datum/gas_mixture/outlet = get_outlet_air() + var/output_starting_pressure = outlet.return_pressure() + var/input_starting_pressure = inlet.return_pressure() + + if(output_starting_pressure >= input_starting_pressure - 10) + //Need at least 10 KPa difference to overcome friction in the mechanism + last_pressure_delta = 0 + return null + + //Calculate necessary moles to transfer using PV = nRT + if(inlet.temperature > 0) + var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2 + + var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION) + + last_pressure_delta = pressure_delta + + //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];") + + //Actually transfer the gas + var/datum/gas_mixture/removed = inlet.remove(transfer_moles) + + parent1.update = 1 + parent2.update = 1 + + return removed + + else + last_pressure_delta = 0 + +/obj/machinery/atmospherics/binary/circulator/process_atmos() + ..() + update_icon() + +/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air() + if(side_inverted==0) + return air2 + else + return air1 + +/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air() + if(side_inverted==0) + return air1 + else + return air2 + +/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side() + if(dir==SOUTH||dir==NORTH) + if(side_inverted==0) + return "South" + else + return "North" + +/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side() + if(dir==SOUTH||dir==NORTH) + if(side_inverted==0) + return "North" + else + return "South" + +/obj/machinery/atmospherics/binary/circulator/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!side_inverted) + side_inverted = TRUE + else + side_inverted = FALSE + to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.") + desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side." + +/obj/machinery/atmospherics/binary/circulator/update_icon() + ..() + + if(stat & (BROKEN|NOPOWER)) + icon_state = "circ[side]-p" + else if(last_pressure_delta > 0) + if(last_pressure_delta > ONE_ATMOSPHERE) + icon_state = "circ[side]-run" + else + icon_state = "circ[side]-slow" + else + icon_state = "circ[side]-off" + + return 1 diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index b9902dbb066c..c0ed34ef206f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -1,269 +1,269 @@ -/obj/machinery/atmospherics/binary/dp_vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_dp_vent" - - //node2 is output port - //node1 is input port - - req_one_access_txt = "24;10" - - name = "dual-port air vent" - desc = "Has a valve and pump attached to it. There are two ports." - - can_unwrench = 1 - - level = 1 - - connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes - - var/on = 0 - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = ONE_ATMOSPHERE - var/input_pressure_min = 0 - var/output_pressure_max = 0 - - var/frequency = ATMOS_VENTSCRUB - var/id_tag = null - var/datum/radio_frequency/radio_connection - var/advcontrol = 0//does this device listen to the AAC - - settagwhitelist = list("id_tag") - - var/pressure_checks = 1 - //1: Do not pass external_pressure_bound - //2: Do not pass input_pressure_min - //4: Do not pass output_pressure_max - -/obj/machinery/atmospherics/binary/dp_vent_pump/New() - ..() - if(!id_tag) - assign_uid() - id_tag = num2text(uid) - icon = null - -/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume - name = "large dual port air vent" - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on - on = TRUE - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() - ..() - air1.volume = 1000 - air2.volume = 1000 - -/obj/machinery/atmospherics/binary/volume_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) - ..() - - if(!check_icon_cache()) - return - - overlays.Cut() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(!powered()) - vent_icon += "off" - else - vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]" - - overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - return - else - if(node1) - add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) - else - add_underlay(T, node1, turn(dir, -180)) - if(node2) - add_underlay(T, node2, dir, node2.icon_connect_type) - else - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos() - ..() - if(!on) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //input -> external - var/pressure_delta = 10000 - - if(pressure_checks&1) - pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) - if(pressure_checks&2) - pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min)) - - if(pressure_delta > 0) - if(air1.temperature > 0) - var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - - loc.assume_air(removed) - - parent1.update = 1 - air_update_turf() - else //external -> output - var/pressure_delta = 10000 - - if(pressure_checks&1) - pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) - if(pressure_checks&4) - pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure())) - - if(pressure_delta > 0) - if(environment.temperature > 0) - var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) - - air2.merge(removed) - - parent2.update = 1 - air_update_turf() - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id_tag, - "device" = "ADVP", - "power" = on, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "input" = input_pressure_min, - "output" = output_pressure_max, - "external" = external_pressure_bound, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol)) - return 0 - if(signal.data["power"] != null) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"] != null) - on = !on - - if(signal.data["direction"] != null) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["checks"] != null) - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["purge"]) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["set_input_pressure"] != null) - input_pressure_min = between( - 0, - text2num(signal.data["set_input_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["set_output_pressure"] != null) - output_pressure_max = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["set_external_pressure"] != null) - external_pressure_bound = between( - 0, - text2num(signal.data["set_external_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - return ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P) - return {" -

- "} - -/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O) - . = ..() - if(.) - return . - if("toggleadvcontrol" in href_list) - advcontrol = !advcontrol - return TRUE +/obj/machinery/atmospherics/binary/dp_vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_dp_vent" + + //node2 is output port + //node1 is input port + + req_one_access_txt = "24;10" + + name = "dual-port air vent" + desc = "Has a valve and pump attached to it. There are two ports." + + can_unwrench = 1 + + level = 1 + + connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes + + var/on = 0 + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = ONE_ATMOSPHERE + var/input_pressure_min = 0 + var/output_pressure_max = 0 + + var/frequency = ATMOS_VENTSCRUB + var/id_tag = null + var/datum/radio_frequency/radio_connection + var/advcontrol = 0//does this device listen to the AAC + + settagwhitelist = list("id_tag") + + var/pressure_checks = 1 + //1: Do not pass external_pressure_bound + //2: Do not pass input_pressure_min + //4: Do not pass output_pressure_max + +/obj/machinery/atmospherics/binary/dp_vent_pump/New() + ..() + if(!id_tag) + assign_uid() + id_tag = num2text(uid) + icon = null + +/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume + name = "large dual port air vent" + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on + on = TRUE + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() + ..() + air1.volume = 1000 + air2.volume = 1000 + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) + ..() + + if(!check_icon_cache()) + return + + overlays.Cut() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(!powered()) + vent_icon += "off" + else + vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]" + + overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + return + else + if(node1) + add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) + else + add_underlay(T, node1, turn(dir, -180)) + if(node2) + add_underlay(T, node2, dir, node2.icon_connect_type) + else + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos() + ..() + if(!on) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //input -> external + var/pressure_delta = 10000 + + if(pressure_checks&1) + pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) + if(pressure_checks&2) + pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min)) + + if(pressure_delta > 0) + if(air1.temperature > 0) + var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + + loc.assume_air(removed) + + parent1.update = 1 + air_update_turf() + else //external -> output + var/pressure_delta = 10000 + + if(pressure_checks&1) + pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) + if(pressure_checks&4) + pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure())) + + if(pressure_delta > 0) + if(environment.temperature > 0) + var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) + + air2.merge(removed) + + parent2.update = 1 + air_update_turf() + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id_tag, + "device" = "ADVP", + "power" = on, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "input" = input_pressure_min, + "output" = output_pressure_max, + "external" = external_pressure_bound, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol)) + return 0 + if(signal.data["power"] != null) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"] != null) + on = !on + + if(signal.data["direction"] != null) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["checks"] != null) + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["purge"]) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["set_input_pressure"] != null) + input_pressure_min = between( + 0, + text2num(signal.data["set_input_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["set_output_pressure"] != null) + output_pressure_max = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["set_external_pressure"] != null) + external_pressure_bound = between( + 0, + text2num(signal.data["set_external_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + return ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P) + return {" + + "} + +/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O) + . = ..() + if(.) + return . + if("toggleadvcontrol" in href_list) + advcontrol = !advcontrol + return TRUE diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 3448655c953d..87c7c6426c1f 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,192 +1,192 @@ -/obj/machinery/atmospherics/binary/passive_gate - //Tries to achieve target pressure at output (like a normal pump) except - // Uses no power but can not transfer gases from a low pressure area to a high pressure area - icon = 'icons/atmos/passive_gate.dmi' - icon_state = "map" - - name = "passive gate" - desc = "A one-way air valve that does not require power" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/passive_gate/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/passive_gate/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/passive_gate/update_icon() - ..() - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/passive_gate/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, 180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/passive_gate/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - var/input_starting_pressure = air1.return_pressure() - - if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10)) - //No need to pump gas if target is already reached or input pressure is too low - //Need at least 10 KPa difference to overcome friction in the mechanism - return 1 - - //Calculate necessary moles to transfer using PV = nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) - var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2) - //Can not have a pressure delta that would cause output_pressure > input_pressure - - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - parent1.update = 1 - - parent2.update = 1 - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if("power" in signal.data) - on = text2num(signal.data["power"]) - - if("power_toggle" in signal.data) - on = !on - - if("set_output_pressure" in signal.data) - target_pressure = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) - if(!istype(W, /obj/item/wrench)) - return ..() - if(on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() +/obj/machinery/atmospherics/binary/passive_gate + //Tries to achieve target pressure at output (like a normal pump) except + // Uses no power but can not transfer gases from a low pressure area to a high pressure area + icon = 'icons/atmos/passive_gate.dmi' + icon_state = "map" + + name = "passive gate" + desc = "A one-way air valve that does not require power" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/passive_gate/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/passive_gate/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/passive_gate/update_icon() + ..() + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/passive_gate/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, 180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/passive_gate/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air2.return_pressure() + var/input_starting_pressure = air1.return_pressure() + + if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10)) + //No need to pump gas if target is already reached or input pressure is too low + //Need at least 10 KPa difference to overcome friction in the mechanism + return 1 + + //Calculate necessary moles to transfer using PV = nRT + if((air1.total_moles() > 0) && (air1.temperature>0)) + var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2) + //Can not have a pressure delta that would cause output_pressure > input_pressure + + var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + parent1.update = 1 + + parent2.update = 1 + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if("power" in signal.data) + on = text2num(signal.data["power"]) + + if("power_toggle" in signal.data) + on = !on + + if("set_output_pressure" in signal.data) + target_pressure = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure)) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) + if(!istype(W, /obj/item/wrench)) + return ..() + if(on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 1998e67e17d4..7e02f89c5e27 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,261 +1,261 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/pump - icon = 'icons/atmos/pump.dmi' - icon_state = "map_off" - - name = "gas pump" - desc = "A pump" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/binary/pump/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/binary/pump/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/binary/pump/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/binary/pump/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/binary/pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/pump/on - icon_state = "map_on" - on = 1 - -/obj/machinery/atmospherics/binary/pump/update_icon() - ..() - - if(!powered()) - icon_state = "off" - else - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/pump/process_atmos() - ..() - if((stat & (NOPOWER|BROKEN)) || !on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - - if( (target_pressure - output_starting_pressure) < 0.01) - //No need to pump gas if target is already reached! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - parent1.update = 1 - - parent2.update = 1 - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - return 1 - -/obj/machinery/atmospherics/binary/pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if(signal.data["power"]) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - on = !on - - if(signal.data["set_output_pressure"]) - target_pressure = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else if(!istype(W, /obj/item/wrench)) - return ..() - if(!(stat & NOPOWER) && on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() \ No newline at end of file +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/pump + icon = 'icons/atmos/pump.dmi' + icon_state = "map_off" + + name = "gas pump" + desc = "A pump" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/binary/pump/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/binary/pump/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/binary/pump/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/binary/pump/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/binary/pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/pump/on + icon_state = "map_on" + on = 1 + +/obj/machinery/atmospherics/binary/pump/update_icon() + ..() + + if(!powered()) + icon_state = "off" + else + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/pump/process_atmos() + ..() + if((stat & (NOPOWER|BROKEN)) || !on) + return 0 + + var/output_starting_pressure = air2.return_pressure() + + if( (target_pressure - output_starting_pressure) < 0.01) + //No need to pump gas if target is already reached! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + if((air1.total_moles() > 0) && (air1.temperature>0)) + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + parent1.update = 1 + + parent2.update = 1 + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + return 1 + +/obj/machinery/atmospherics/binary/pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if(signal.data["power"]) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + on = !on + + if(signal.data["set_output_pressure"]) + target_pressure = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure)) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else if(!istype(W, /obj/item/wrench)) + return ..() + if(!(stat & NOPOWER) && on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 0947b2ee8601..8741acb47bfb 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,257 +1,257 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/volume_pump - icon = 'icons/atmos/volume_pump.dmi' - icon_state = "map_off" - - name = "volumetric gas pump" - desc = "A volumetric pump" - - can_unwrench = 1 - - var/on = 0 - var/transfer_rate = 200 - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/binary/volume_pump/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/proc/set_max() - if(powered()) - transfer_rate = MAX_TRANSFER_RATE - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/on - on = 1 - icon_state = "map_on" - -/obj/machinery/atmospherics/binary/volume_pump/atmos_init() - ..() - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/volume_pump/update_icon() - ..() - - if(!powered()) - icon_state = "off" - else - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/volume_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/volume_pump/process_atmos() - ..() - if((stat & (NOPOWER|BROKEN)) || !on) - return 0 - - // Pump mechanism just won't do anything if the pressure is too high/too low - var/input_starting_pressure = air1.return_pressure() - var/output_starting_pressure = air2.return_pressure() - - if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000)) - return 1 - - var/transfer_ratio = max(1, transfer_rate/air1.volume) - - var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) - - air2.merge(removed) - - - parent1.update = 1 - parent2.update = 1 - - return 1 - -/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency) - -/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "APV", - "power" = on, - "transfer_rate" = transfer_rate, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - - return 1 - -/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if(signal.data["power"]) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - on = !on - - if(signal.data["set_transfer_rate"]) - transfer_rate = between( - 0, - text2num(signal.data["set_transfer_rate"]), - air1.volume - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["rate"] = round(transfer_rate) - data["max_rate"] = round(MAX_TRANSFER_RATE) - return data - -/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["rate"]) - var/rate = href_list["rate"] - if(rate == "max") - rate = MAX_TRANSFER_RATE - . = TRUE - else if(rate == "input") - rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null - if(!isnull(rate)) - . = TRUE - else if(text2num(rate) != null) - rate = text2num(rate) - . = TRUE - if(.) - transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE) - investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/volume_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else if(!istype(W, /obj/item/wrench)) - return ..() - if(!(stat & NOPOWER) && on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() \ No newline at end of file +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/volume_pump + icon = 'icons/atmos/volume_pump.dmi' + icon_state = "map_off" + + name = "volumetric gas pump" + desc = "A volumetric pump" + + can_unwrench = 1 + + var/on = 0 + var/transfer_rate = 200 + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/binary/volume_pump/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_max() + if(powered()) + transfer_rate = MAX_TRANSFER_RATE + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/on + on = 1 + icon_state = "map_on" + +/obj/machinery/atmospherics/binary/volume_pump/atmos_init() + ..() + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/volume_pump/update_icon() + ..() + + if(!powered()) + icon_state = "off" + else + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/volume_pump/process_atmos() + ..() + if((stat & (NOPOWER|BROKEN)) || !on) + return 0 + + // Pump mechanism just won't do anything if the pressure is too high/too low + var/input_starting_pressure = air1.return_pressure() + var/output_starting_pressure = air2.return_pressure() + + if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000)) + return 1 + + var/transfer_ratio = max(1, transfer_rate/air1.volume) + + var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) + + air2.merge(removed) + + + parent1.update = 1 + parent2.update = 1 + + return 1 + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency) + +/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "APV", + "power" = on, + "transfer_rate" = transfer_rate, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + + return 1 + +/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if(signal.data["power"]) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + on = !on + + if(signal.data["set_transfer_rate"]) + transfer_rate = between( + 0, + text2num(signal.data["set_transfer_rate"]), + air1.volume + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["rate"] = round(transfer_rate) + data["max_rate"] = round(MAX_TRANSFER_RATE) + return data + +/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["rate"]) + var/rate = href_list["rate"] + if(rate == "max") + rate = MAX_TRANSFER_RATE + . = TRUE + else if(rate == "input") + rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null + if(!isnull(rate)) + . = TRUE + else if(text2num(rate) != null) + rate = text2num(rate) + . = TRUE + if(.) + transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE) + investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/volume_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else if(!istype(W, /obj/item/wrench)) + return ..() + if(!(stat & NOPOWER) && on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm index a1f80916aeea..d03329e24fc5 100644 --- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm +++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm @@ -91,4 +91,4 @@ return WEST else return 0 - \ No newline at end of file + diff --git a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm index cde1c9e80b0f..d75e2e683c1a 100644 --- a/code/ATMOSPHERICS/components/omni_devices/omni_base.dm +++ b/code/ATMOSPHERICS/components/omni_devices/omni_base.dm @@ -31,7 +31,7 @@ icon_state = "base" ports = new() - for(var/d in cardinal) + for(var/d in GLOB.cardinal) var/datum/omni_port/new_port = new(src, d) switch(d) if(NORTH) diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index e45a8c3956f5..84e212598c80 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,267 +1,267 @@ -/obj/machinery/atmospherics/trinary/filter - icon = 'icons/atmos/filter.dmi' - icon_state = "map" - - can_unwrench = 1 - - name = "gas filter" - - var/target_pressure = ONE_ATMOSPHERE - - var/filter_type = 0 -/* -Filter types: --1: Nothing - 0: Toxins: Toxins, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Sleeping Agent (N2O) -*/ - - var/frequency = 0 - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/filter/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/trinary/filter/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/trinary/filter/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/trinary/filter/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/trinary/filter/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/trinary/filter/flipped - icon_state = "mmap" - flipped = 1 - -/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/trinary/filter/update_icon() - ..() - - if(flipped) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += on ? "on" : "off" - else - icon_state += "off" - on = 0 - -/obj/machinery/atmospherics/trinary/filter/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(flipped) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/filter/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/filter/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air3.return_pressure() - - if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure ) - //No need to mix if target is already full! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles - - if(air1.temperature > 0) - transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - - if(transfer_moles > 0) - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - - if(!removed) - return - var/datum/gas_mixture/filtered_out = new - filtered_out.temperature = removed.temperature - - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/oxygen_agent_b)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - if(1) //removing O2 - filtered_out.oxygen = removed.oxygen - removed.oxygen = 0 - - if(2) //removing N2 - filtered_out.nitrogen = removed.nitrogen - removed.nitrogen = 0 - - if(3) //removing CO2 - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(4)//removing N2O - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/sleeping_agent)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - else - filtered_out = null - - - air2.merge(filtered_out) - air3.merge(removed) - - parent2.update = 1 - - parent3.update = 1 - - parent1.update = 1 - - return 1 - -/obj/machinery/atmospherics/trinary/filter/atmos_init() - set_frequency(frequency) - ..() - -/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) - ui.open() - -/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["filter_type"] = filter_type - return data - -/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["filter"]) - filter_type = text2num(href_list["filter"]) - investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") - . = TRUE - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else - return ..() \ No newline at end of file +/obj/machinery/atmospherics/trinary/filter + icon = 'icons/atmos/filter.dmi' + icon_state = "map" + + can_unwrench = 1 + + name = "gas filter" + + var/target_pressure = ONE_ATMOSPHERE + + var/filter_type = 0 +/* +Filter types: +-1: Nothing + 0: Toxins: Toxins, Oxygen Agent B + 1: Oxygen: Oxygen ONLY + 2: Nitrogen: Nitrogen ONLY + 3: Carbon Dioxide: Carbon Dioxide ONLY + 4: Sleeping Agent (N2O) +*/ + + var/frequency = 0 + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/filter/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/trinary/filter/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/trinary/filter/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/trinary/filter/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/trinary/filter/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/trinary/filter/flipped + icon_state = "mmap" + flipped = 1 + +/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/trinary/filter/update_icon() + ..() + + if(flipped) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += on ? "on" : "off" + else + icon_state += "off" + on = 0 + +/obj/machinery/atmospherics/trinary/filter/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(flipped) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/filter/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/filter/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air3.return_pressure() + + if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure ) + //No need to mix if target is already full! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles + + if(air1.temperature > 0) + transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + + if(transfer_moles > 0) + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + + if(!removed) + return + var/datum/gas_mixture/filtered_out = new + filtered_out.temperature = removed.temperature + + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out.toxins = removed.toxins + removed.toxins = 0 + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/oxygen_agent_b)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + if(1) //removing O2 + filtered_out.oxygen = removed.oxygen + removed.oxygen = 0 + + if(2) //removing N2 + filtered_out.nitrogen = removed.nitrogen + removed.nitrogen = 0 + + if(3) //removing CO2 + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(4)//removing N2O + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/sleeping_agent)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + else + filtered_out = null + + + air2.merge(filtered_out) + air3.merge(removed) + + parent2.update = 1 + + parent3.update = 1 + + parent1.update = 1 + + return 1 + +/obj/machinery/atmospherics/trinary/filter/atmos_init() + set_frequency(frequency) + ..() + +/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) + ui.open() + +/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["filter_type"] = filter_type + return data + +/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if(href_list["filter"]) + filter_type = text2num(href_list["filter"]) + investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") + . = TRUE + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 19849582a0fd..6128c1afa334 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,233 +1,233 @@ -/obj/machinery/atmospherics/trinary/mixer - icon = 'icons/atmos/mixer.dmi' - icon_state = "map" - - can_unwrench = 1 - - name = "gas mixer" - - var/target_pressure = ONE_ATMOSPHERE - var/node1_concentration = 0.5 - var/node2_concentration = 0.5 - - //node 3 is the outlet, nodes 1 & 2 are intakes - -/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/trinary/mixer/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/flipped - icon_state = "mmap" - flipped = 1 - -/obj/machinery/atmospherics/trinary/mixer/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0) - ..() - - if(flipped) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += on ? "on" : "off" - else - icon_state += "off" - on = 0 - -/obj/machinery/atmospherics/trinary/mixer/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(flipped) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/mixer/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/New() - ..() - air3.volume = 300 - -/obj/machinery/atmospherics/trinary/mixer/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air3.return_pressure() - - if(output_starting_pressure >= target_pressure) - //No need to mix if target is already full! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles1 = 0 - var/transfer_moles2 = 0 - - if(air1.temperature > 0) - transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - if(air2.temperature > 0) - transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - - var/air1_moles = air1.total_moles() - var/air2_moles = air2.total_moles() - - if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2)) - if(!transfer_moles1 || !transfer_moles2) return - var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) - - transfer_moles1 *= ratio - transfer_moles2 *= ratio - - //Actually transfer the gas - - if(transfer_moles1 > 0) - var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1) - air3.merge(removed1) - - if(transfer_moles2 > 0) - var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2) - air3.merge(removed2) - - if(transfer_moles1) - parent1.update = 1 - - if(transfer_moles2) - parent2.update = 1 - - parent3.update = 1 - - return 1 - -/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) - ui.open() - -/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["node1_concentration"] = round(node1_concentration*100) - data["node2_concentration"] = round(node2_concentration*100) - return data - -/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["node1"]) - var/value = text2num(href_list["node1"]) - node1_concentration = max(0, min(1, node1_concentration + value)) - node2_concentration = max(0, min(1, node2_concentration - value)) - investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["node2"]) - var/value = text2num(href_list["node2"]) - node2_concentration = max(0, min(1, node2_concentration + value)) - node1_concentration = max(0, min(1, node1_concentration - value)) - investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") - . = TRUE - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else - return ..() \ No newline at end of file +/obj/machinery/atmospherics/trinary/mixer + icon = 'icons/atmos/mixer.dmi' + icon_state = "map" + + can_unwrench = 1 + + name = "gas mixer" + + var/target_pressure = ONE_ATMOSPHERE + var/node1_concentration = 0.5 + var/node2_concentration = 0.5 + + //node 3 is the outlet, nodes 1 & 2 are intakes + +/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/trinary/mixer/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/flipped + icon_state = "mmap" + flipped = 1 + +/obj/machinery/atmospherics/trinary/mixer/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0) + ..() + + if(flipped) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += on ? "on" : "off" + else + icon_state += "off" + on = 0 + +/obj/machinery/atmospherics/trinary/mixer/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(flipped) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/mixer/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/New() + ..() + air3.volume = 300 + +/obj/machinery/atmospherics/trinary/mixer/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air3.return_pressure() + + if(output_starting_pressure >= target_pressure) + //No need to mix if target is already full! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles1 = 0 + var/transfer_moles2 = 0 + + if(air1.temperature > 0) + transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + if(air2.temperature > 0) + transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + + var/air1_moles = air1.total_moles() + var/air2_moles = air2.total_moles() + + if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2)) + if(!transfer_moles1 || !transfer_moles2) return + var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) + + transfer_moles1 *= ratio + transfer_moles2 *= ratio + + //Actually transfer the gas + + if(transfer_moles1 > 0) + var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1) + air3.merge(removed1) + + if(transfer_moles2 > 0) + var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2) + air3.merge(removed2) + + if(transfer_moles1) + parent1.update = 1 + + if(transfer_moles2) + parent2.update = 1 + + parent3.update = 1 + + return 1 + +/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) + ui.open() + +/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["node1_concentration"] = round(node1_concentration*100) + data["node2_concentration"] = round(node2_concentration*100) + return data + +/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if(href_list["node1"]) + var/value = text2num(href_list["node1"]) + node1_concentration = max(0, min(1, node1_concentration + value)) + node2_concentration = max(0, min(1, node2_concentration - value)) + investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["node2"]) + var/value = text2num(href_list["node2"]) + node2_concentration = max(0, min(1, node2_concentration + value)) + node1_concentration = max(0, min(1, node1_concentration - value)) + investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") + . = TRUE + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index 8087db9966e7..79fae0f7d88e 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,213 +1,213 @@ -/obj/machinery/atmospherics/trinary - dir = SOUTH - initialize_directions = SOUTH|NORTH|WEST - use_power = IDLE_POWER_USE - - var/on = 0 - layer = GAS_FILTER_LAYER - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - var/datum/gas_mixture/air3 - - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - var/obj/machinery/atmospherics/node3 - - var/datum/pipeline/parent1 - var/datum/pipeline/parent2 - var/datum/pipeline/parent3 - - var/flipped = 0 - -/obj/machinery/atmospherics/trinary/New() - ..() - - if(!flipped) - switch(dir) - if(NORTH) - initialize_directions = EAST|NORTH|SOUTH - if(SOUTH) - initialize_directions = SOUTH|WEST|NORTH - if(EAST) - initialize_directions = EAST|WEST|SOUTH - if(WEST) - initialize_directions = WEST|NORTH|EAST - else - switch(dir) - if(NORTH) - initialize_directions = SOUTH|NORTH|WEST - if(SOUTH) - initialize_directions = NORTH|SOUTH|EAST - if(EAST) - initialize_directions = WEST|EAST|NORTH - if(WEST) - initialize_directions = EAST|WEST|SOUTH - - air1 = new - air2 = new - air3 = new - - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 - -/obj/machinery/atmospherics/trinary/Destroy() - if(node1) - node1.disconnect(src) - node1 = null - nullifyPipenet(parent1) - if(node2) - node2.disconnect(src) - node2 = null - nullifyPipenet(parent2) - if(node3) - node3.disconnect(src) - node3 = null - nullifyPipenet(parent3) - return ..() - -/obj/machinery/atmospherics/trinary/atmos_init() - ..() - //Mixer: - //1 and 2 is input - //Node 3 is output - //If we flip the mixer, 1 and 3 shall exchange positions - - //Filter: - //Node 1 is input - //Node 2 is filtered output - //Node 3 is rest output - //If we flip the filter, 1 and 3 shall exchange positions - - var/node1_connect = turn(dir, -180) - var/node2_connect = turn(dir, -90) - var/node3_connect = dir - - if(flipped) - node2_connect = turn(node2_connect, -180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - node3 = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE) - if(!parent1) - parent1 = new /datum/pipeline() - parent1.build_pipeline(src) - - if(!parent2) - parent2 = new /datum/pipeline() - parent2.build_pipeline(src) - - if(!parent3) - parent3 = new /datum/pipeline() - parent3.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - qdel(parent1) - node1 = null - else if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - qdel(parent2) - node2 = null - else if(reference == node3) - if(istype(node3, /obj/machinery/atmospherics/pipe)) - qdel(parent3) - node3 = null - update_icon() - -/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - if(P == parent1) - parent1.other_airs -= air1 - parent1 = null - else if(P == parent2) - parent2.other_airs -= air2 - parent2 = null - else if(P == parent3) - parent3.other_airs -= air3 - parent3 = null - -/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P) - if(P == parent1) - return air1 - else if(P == parent2) - return air2 - else if(P == parent3) - return air3 - -/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P) - if(P) - if(parent1 == P) - return list(node1) - else if(parent2 == P) - return list(node2) - else if(parent3 == P) - return list(node3) - return list(node1, node2, node3) - -/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - if(A == node1) - parent1 = P - else if(A == node2) - parent2 = P - else if(A == node3) - parent3 = P - -/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A) - if(A == node1) - return parent1 - else if(A == node2) - return parent2 - else if(A == node3) - return parent3 - -/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - if(Old == parent1) - parent1 = New - else if(Old == parent2) - parent2 = New - else if(Old == parent3) - parent3 = New - -/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures) - ..() - - var/turf/T = get_turf(src) - if(T) - //Remove the gas from air1+air2+air3 and assume it - var/datum/gas_mixture/environment = T.return_air() - var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION) - var/shared_loss = lost/3 - - var/datum/gas_mixture/to_release = air1.remove(shared_loss) - to_release.merge(air2.remove(shared_loss)) - to_release.merge(air3.remove(shared_loss)) - T.assume_air(to_release) - air_update_turf(1) - -/obj/machinery/atmospherics/trinary/process_atmos() - ..() - return parent1 && parent2 && parent3 +/obj/machinery/atmospherics/trinary + dir = SOUTH + initialize_directions = SOUTH|NORTH|WEST + use_power = IDLE_POWER_USE + + var/on = 0 + layer = GAS_FILTER_LAYER + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + var/datum/gas_mixture/air3 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + var/obj/machinery/atmospherics/node3 + + var/datum/pipeline/parent1 + var/datum/pipeline/parent2 + var/datum/pipeline/parent3 + + var/flipped = 0 + +/obj/machinery/atmospherics/trinary/New() + ..() + + if(!flipped) + switch(dir) + if(NORTH) + initialize_directions = EAST|NORTH|SOUTH + if(SOUTH) + initialize_directions = SOUTH|WEST|NORTH + if(EAST) + initialize_directions = EAST|WEST|SOUTH + if(WEST) + initialize_directions = WEST|NORTH|EAST + else + switch(dir) + if(NORTH) + initialize_directions = SOUTH|NORTH|WEST + if(SOUTH) + initialize_directions = NORTH|SOUTH|EAST + if(EAST) + initialize_directions = WEST|EAST|NORTH + if(WEST) + initialize_directions = EAST|WEST|SOUTH + + air1 = new + air2 = new + air3 = new + + air1.volume = 200 + air2.volume = 200 + air3.volume = 200 + +/obj/machinery/atmospherics/trinary/Destroy() + if(node1) + node1.disconnect(src) + node1 = null + nullifyPipenet(parent1) + if(node2) + node2.disconnect(src) + node2 = null + nullifyPipenet(parent2) + if(node3) + node3.disconnect(src) + node3 = null + nullifyPipenet(parent3) + return ..() + +/obj/machinery/atmospherics/trinary/atmos_init() + ..() + //Mixer: + //1 and 2 is input + //Node 3 is output + //If we flip the mixer, 1 and 3 shall exchange positions + + //Filter: + //Node 1 is input + //Node 2 is filtered output + //Node 3 is rest output + //If we flip the filter, 1 and 3 shall exchange positions + + var/node1_connect = turn(dir, -180) + var/node2_connect = turn(dir, -90) + var/node3_connect = dir + + if(flipped) + node2_connect = turn(node2_connect, -180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + node3 = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE) + if(!parent1) + parent1 = new /datum/pipeline() + parent1.build_pipeline(src) + + if(!parent2) + parent2 = new /datum/pipeline() + parent2.build_pipeline(src) + + if(!parent3) + parent3 = new /datum/pipeline() + parent3.build_pipeline(src) + ..() + +/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + qdel(parent1) + node1 = null + else if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + qdel(parent2) + node2 = null + else if(reference == node3) + if(istype(node3, /obj/machinery/atmospherics/pipe)) + qdel(parent3) + node3 = null + update_icon() + +/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P) + ..() + if(!P) + return + if(P == parent1) + parent1.other_airs -= air1 + parent1 = null + else if(P == parent2) + parent2.other_airs -= air2 + parent2 = null + else if(P == parent3) + parent3.other_airs -= air3 + parent3 = null + +/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P) + if(P == parent1) + return air1 + else if(P == parent2) + return air2 + else if(P == parent3) + return air3 + +/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P) + if(P) + if(parent1 == P) + return list(node1) + else if(parent2 == P) + return list(node2) + else if(parent3 == P) + return list(node3) + return list(node1, node2, node3) + +/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) + if(A == node1) + parent1 = P + else if(A == node2) + parent2 = P + else if(A == node3) + parent3 = P + +/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A) + if(A == node1) + return parent1 + else if(A == node2) + return parent2 + else if(A == node3) + return parent3 + +/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) + if(Old == parent1) + parent1 = New + else if(Old == parent2) + parent2 = New + else if(Old == parent3) + parent3 = New + +/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures) + ..() + + var/turf/T = get_turf(src) + if(T) + //Remove the gas from air1+air2+air3 and assume it + var/datum/gas_mixture/environment = T.return_air() + var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION) + var/shared_loss = lost/3 + + var/datum/gas_mixture/to_release = air1.remove(shared_loss) + to_release.merge(air2.remove(shared_loss)) + to_release.merge(air3.remove(shared_loss)) + T.assume_air(to_release) + air_update_turf(1) + +/obj/machinery/atmospherics/trinary/process_atmos() + ..() + return parent1 && parent2 && parent3 diff --git a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm index 7b3f82993391..31039a412d5d 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm @@ -177,4 +177,4 @@ switch_side() #undef TVALVE_STATE_STRAIGHT -#undef TVALVE_STATE_SIDE \ No newline at end of file +#undef TVALVE_STATE_SIDE diff --git a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm index a9c3b98ad743..9b06019e49e0 100644 --- a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm +++ b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm @@ -45,4 +45,4 @@ parent.update = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/ATMOSPHERICS/components/unary_devices/tank.dm b/code/ATMOSPHERICS/components/unary_devices/tank.dm index cb82417033b9..11a752b90302 100644 --- a/code/ATMOSPHERICS/components/unary_devices/tank.dm +++ b/code/ATMOSPHERICS/components/unary_devices/tank.dm @@ -110,4 +110,4 @@ var/datum/gas/oxygen_agent_b/trace_gas = new trace_gas.moles = (50 * ONE_ATMOSPHERE) * (air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - air_contents.trace_gases += trace_gas \ No newline at end of file + air_contents.trace_gases += trace_gas diff --git a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm index 5a6e76a182b1..6cd050de0229 100644 --- a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm +++ b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm @@ -77,4 +77,4 @@ parent.update = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm index 4ef7743fc8c0..14c93dd04904 100644 --- a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm @@ -102,4 +102,4 @@ /obj/machinery/atmospherics/unary/process_atmos() ..() - return parent \ No newline at end of file + return parent diff --git a/code/ATMOSPHERICS/datum_icon_manager.dm b/code/ATMOSPHERICS/datum_icon_manager.dm index 75cb0fce8cee..2286b3523a0e 100644 --- a/code/ATMOSPHERICS/datum_icon_manager.dm +++ b/code/ATMOSPHERICS/datum_icon_manager.dm @@ -6,26 +6,16 @@ // atmospherics devices. //-------------------------------------------- -#define PIPE_COLOR_GREY "#ffffff" //yes white is grey -#define PIPE_COLOR_RED "#ff0000" -#define PIPE_COLOR_BLUE "#0000ff" -#define PIPE_COLOR_CYAN "#00ffff" -#define PIPE_COLOR_GREEN "#00ff00" -#define PIPE_COLOR_YELLOW "#ffcc00" -#define PIPE_COLOR_PURPLE "#5c1ec0" - -var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE) - /proc/pipe_color_lookup(var/color) - for(var/C in pipe_colors) - if(color == pipe_colors[C]) + for(var/C in GLOB.pipe_colors) + if(color == GLOB.pipe_colors[C]) return "[C]" /proc/pipe_color_check(var/color) if(!color) return 1 - for(var/C in pipe_colors) - if(color == pipe_colors[C]) + for(var/C in GLOB.pipe_colors) + if(color == GLOB.pipe_colors[C]) return 1 return 0 @@ -105,10 +95,10 @@ var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_ var/image/I = image('icons/atmos/pipes.dmi', icon_state = state) pipe_icons[cache_name] = I - for(var/pipe_color in pipe_colors) + for(var/pipe_color in GLOB.pipe_colors) I = image('icons/atmos/pipes.dmi', icon_state = state) - I.color = pipe_colors[pipe_color] - pipe_icons[state + "[pipe_colors[pipe_color]]"] = I + I.color = GLOB.pipe_colors[pipe_color] + pipe_icons[state + "[GLOB.pipe_colors[pipe_color]]"] = I pipe = new ('icons/atmos/heat.dmi') for(var/state in pipe.IconStates()) @@ -137,10 +127,10 @@ var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_ if(findtext(state, "core") || findtext(state, "4way")) var/image/I = image('icons/atmos/manifold.dmi', icon_state = state) manifold_icons[state] = I - for(var/pipe_color in pipe_colors) + for(var/pipe_color in GLOB.pipe_colors) I = image('icons/atmos/manifold.dmi', icon_state = state) - I.color = pipe_colors[pipe_color] - manifold_icons[state + pipe_colors[pipe_color]] = I + I.color = GLOB.pipe_colors[pipe_color] + manifold_icons[state + GLOB.pipe_colors[pipe_color]] = I /datum/pipe_icon_manager/proc/gen_device_icons() if(!device_icons) @@ -185,10 +175,10 @@ var/global/list/pipe_colors = list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_ var/cache_name = state - for(var/D in cardinal) + for(var/D in GLOB.cardinal) var/image/I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D)) underlays[cache_name + "[D]"] = I - for(var/pipe_color in pipe_colors) + for(var/pipe_color in GLOB.pipe_colors) I = image(icon('icons/atmos/pipe_underlays.dmi', icon_state = state, dir = D)) - I.color = pipe_colors[pipe_color] - underlays[state + "[D]" + "[pipe_colors[pipe_color]]"] = I + I.color = GLOB.pipe_colors[pipe_color] + underlays[state + "[D]" + "[GLOB.pipe_colors[pipe_color]]"] = I diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 43082c38dedf..2cf3edfb2759 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,276 +1,276 @@ -/datum/pipeline - var/datum/gas_mixture/air - var/list/datum/gas_mixture/other_airs = list() - - var/list/obj/machinery/atmospherics/pipe/members = list() - var/list/obj/machinery/atmospherics/other_atmosmch = list() - - var/update = 1 - - var/alert_pressure = 0 - -/datum/pipeline/New() - SSair.networks += src - -/datum/pipeline/Destroy() - SSair.networks -= src - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - for(var/obj/machinery/atmospherics/A in other_atmosmch) - A.nullifyPipenet(src) - return ..() - -/datum/pipeline/process()//This use to be called called from the pipe networks - if(update) - update = 0 - reconcile_air() - return - -var/pipenetwarnings = 10 - -/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base) - var/volume = 0 - if(istype(base, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/E = base - volume = E.volume - alert_pressure = E.alert_pressure - members += E - if(E.air_temporary) - air = E.air_temporary - E.air_temporary = null - else - addMachineryMember(base) - if(!air) - air = new - var/list/possible_expansions = list(base) - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/borderline in possible_expansions) - - var/list/result = borderline.pipeline_expansion(src) - - if(result.len>0) - for(var/obj/machinery/atmospherics/P in result) - if(istype(P, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/item = P - if(!members.Find(item)) - - if(item.parent) - log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z].")) - members += item - possible_expansions += item - - volume += item.volume - item.parent = src - - alert_pressure = min(alert_pressure, item.alert_pressure) - - if(item.air_temporary) - air.merge(item.air_temporary) - item.air_temporary = null - else - P.setPipenet(src, borderline) - addMachineryMember(P) - - possible_expansions -= borderline - - air.volume = volume - -/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A) - other_atmosmch |= A - var/datum/gas_mixture/G = A.returnPipenetAir(src) - other_airs |= G - -/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N) - if(istype(A, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/P = A - P.parent = src - var/list/adjacent = P.pipeline_expansion() - for(var/obj/machinery/atmospherics/pipe/I in adjacent) - if(I.parent == src) - continue - var/datum/pipeline/E = I.parent - merge(E) - if(!members.Find(P)) - members += P - air.volume += P.volume - else - A.setPipenet(src, N) - addMachineryMember(A) - -/datum/pipeline/proc/merge(datum/pipeline/E) - air.volume += E.air.volume - members.Add(E.members) - for(var/obj/machinery/atmospherics/pipe/S in E.members) - S.parent = src - air.merge(E.air) - for(var/obj/machinery/atmospherics/A in E.other_atmosmch) - A.replacePipenet(E, src) - other_atmosmch.Add(E.other_atmosmch) - other_airs.Add(E.other_airs) - E.members.Cut() - E.other_atmosmch.Cut() - qdel(E) - -/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A) - var/datum/pipeline/P = returnPipenet(A) - P.addMember(A, src) - -/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A) - parent.addMember(A, src) - -/datum/pipeline/proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio - - for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.volume = member.volume - - member.air_temporary.oxygen = air.oxygen*member.volume/air.volume - member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume - member.air_temporary.toxins = air.toxins*member.volume/air.volume - member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume - - member.air_temporary.temperature = air.temperature - - if(air.trace_gases.len) - for(var/datum/gas/trace_gas in air.trace_gases) - var/datum/gas/corresponding = new trace_gas.type() - member.air_temporary.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*member.volume/air.volume - -/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target - - if(modeled_location.blocks_air) - - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - update = 1 - -/datum/pipeline/proc/reconcile_air() - var/list/datum/gas_mixture/GL = list() - var/list/datum/pipeline/PL = list() - PL += src - - for(var/i=1;i<=PL.len;i++) - var/datum/pipeline/P = PL[i] - if(!P) - return - GL += P.air - GL += P.other_airs - for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch) - if(V.open) - PL |= V.parent1 - PL |= V.parent2 - for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch) - if(!T.state) - if(src != T.parent2) // otherwise dc'd side connects to both other sides! - PL |= T.parent1 - PL |= T.parent3 - else - if(src != T.parent3) - PL |= T.parent1 - PL |= T.parent2 - for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch) - if(C.connected_device) - GL += C.portableConnectorReturnAir() - - var/total_volume = 0 - var/total_thermal_energy = 0 - var/total_heat_capacity = 0 - var/total_oxygen = 0 - var/total_nitrogen = 0 - var/total_toxins = 0 - var/total_carbon_dioxide = 0 - var/list/total_trace_gases = list() - - for(var/datum/gas_mixture/G in GL) - total_volume += G.volume - total_thermal_energy += G.thermal_energy() - total_heat_capacity += G.heat_capacity() - - total_oxygen += G.oxygen - total_nitrogen += G.nitrogen - total_toxins += G.toxins - total_carbon_dioxide += G.carbon_dioxide - - if(G.trace_gases.len) - for(var/datum/gas/trace_gas in G.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - total_trace_gases += corresponding - - corresponding.moles += trace_gas.moles - - if(total_volume > 0) - - //Calculate temperature - var/temperature = 0 - - if(total_heat_capacity > 0) - temperature = total_thermal_energy/total_heat_capacity - - //Update individual gas_mixtures by volume ratio - for(var/datum/gas_mixture/G in GL) - G.oxygen = total_oxygen*G.volume/total_volume - G.nitrogen = total_nitrogen*G.volume/total_volume - G.toxins = total_toxins*G.volume/total_volume - G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume - - G.temperature = temperature - - if(total_trace_gases.len) - for(var/datum/gas/trace_gas in total_trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - G.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*G.volume/total_volume +/datum/pipeline + var/datum/gas_mixture/air + var/list/datum/gas_mixture/other_airs = list() + + var/list/obj/machinery/atmospherics/pipe/members = list() + var/list/obj/machinery/atmospherics/other_atmosmch = list() + + var/update = 1 + + var/alert_pressure = 0 + +/datum/pipeline/New() + SSair.networks += src + +/datum/pipeline/Destroy() + SSair.networks -= src + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + for(var/obj/machinery/atmospherics/A in other_atmosmch) + A.nullifyPipenet(src) + return ..() + +/datum/pipeline/process()//This use to be called called from the pipe networks + if(update) + update = 0 + reconcile_air() + return + +GLOBAL_VAR_INIT(pipenetwarnings, 10) + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base) + var/volume = 0 + if(istype(base, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/E = base + volume = E.volume + alert_pressure = E.alert_pressure + members += E + if(E.air_temporary) + air = E.air_temporary + E.air_temporary = null + else + addMachineryMember(base) + if(!air) + air = new + var/list/possible_expansions = list(base) + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/borderline in possible_expansions) + + var/list/result = borderline.pipeline_expansion(src) + + if(result.len>0) + for(var/obj/machinery/atmospherics/P in result) + if(istype(P, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/item = P + if(!members.Find(item)) + + if(item.parent) + log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z].")) + members += item + possible_expansions += item + + volume += item.volume + item.parent = src + + alert_pressure = min(alert_pressure, item.alert_pressure) + + if(item.air_temporary) + air.merge(item.air_temporary) + item.air_temporary = null + else + P.setPipenet(src, borderline) + addMachineryMember(P) + + possible_expansions -= borderline + + air.volume = volume + +/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A) + other_atmosmch |= A + var/datum/gas_mixture/G = A.returnPipenetAir(src) + other_airs |= G + +/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N) + if(istype(A, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/P = A + P.parent = src + var/list/adjacent = P.pipeline_expansion() + for(var/obj/machinery/atmospherics/pipe/I in adjacent) + if(I.parent == src) + continue + var/datum/pipeline/E = I.parent + merge(E) + if(!members.Find(P)) + members += P + air.volume += P.volume + else + A.setPipenet(src, N) + addMachineryMember(A) + +/datum/pipeline/proc/merge(datum/pipeline/E) + air.volume += E.air.volume + members.Add(E.members) + for(var/obj/machinery/atmospherics/pipe/S in E.members) + S.parent = src + air.merge(E.air) + for(var/obj/machinery/atmospherics/A in E.other_atmosmch) + A.replacePipenet(E, src) + other_atmosmch.Add(E.other_atmosmch) + other_airs.Add(E.other_airs) + E.members.Cut() + E.other_atmosmch.Cut() + qdel(E) + +/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A) + var/datum/pipeline/P = returnPipenet(A) + P.addMember(A, src) + +/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A) + parent.addMember(A, src) + +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.volume = member.volume + + member.air_temporary.oxygen = air.oxygen*member.volume/air.volume + member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume + member.air_temporary.toxins = air.toxins*member.volume/air.volume + member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume + + member.air_temporary.temperature = air.temperature + + if(air.trace_gases.len) + for(var/datum/gas/trace_gas in air.trace_gases) + var/datum/gas/corresponding = new trace_gas.type() + member.air_temporary.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*member.volume/air.volume + +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target + + if(modeled_location.blocks_air) + + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + modeled_location.temperature += heat/modeled_location.heat_capacity + + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 + + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + update = 1 + +/datum/pipeline/proc/reconcile_air() + var/list/datum/gas_mixture/GL = list() + var/list/datum/pipeline/PL = list() + PL += src + + for(var/i=1;i<=PL.len;i++) + var/datum/pipeline/P = PL[i] + if(!P) + return + GL += P.air + GL += P.other_airs + for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch) + if(V.open) + PL |= V.parent1 + PL |= V.parent2 + for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch) + if(!T.state) + if(src != T.parent2) // otherwise dc'd side connects to both other sides! + PL |= T.parent1 + PL |= T.parent3 + else + if(src != T.parent3) + PL |= T.parent1 + PL |= T.parent2 + for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch) + if(C.connected_device) + GL += C.portableConnectorReturnAir() + + var/total_volume = 0 + var/total_thermal_energy = 0 + var/total_heat_capacity = 0 + var/total_oxygen = 0 + var/total_nitrogen = 0 + var/total_toxins = 0 + var/total_carbon_dioxide = 0 + var/list/total_trace_gases = list() + + for(var/datum/gas_mixture/G in GL) + total_volume += G.volume + total_thermal_energy += G.thermal_energy() + total_heat_capacity += G.heat_capacity() + + total_oxygen += G.oxygen + total_nitrogen += G.nitrogen + total_toxins += G.toxins + total_carbon_dioxide += G.carbon_dioxide + + if(G.trace_gases.len) + for(var/datum/gas/trace_gas in G.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + total_trace_gases += corresponding + + corresponding.moles += trace_gas.moles + + if(total_volume > 0) + + //Calculate temperature + var/temperature = 0 + + if(total_heat_capacity > 0) + temperature = total_thermal_energy/total_heat_capacity + + //Update individual gas_mixtures by volume ratio + for(var/datum/gas_mixture/G in GL) + G.oxygen = total_oxygen*G.volume/total_volume + G.nitrogen = total_nitrogen*G.volume/total_volume + G.toxins = total_toxins*G.volume/total_volume + G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume + + G.temperature = temperature + + if(total_trace_gases.len) + for(var/datum/gas/trace_gas in total_trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + G.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*G.volume/total_volume diff --git a/code/ATMOSPHERICS/pipes/cap.dm b/code/ATMOSPHERICS/pipes/cap.dm index 8421685d44ca..15b53d83e9c9 100644 --- a/code/ATMOSPHERICS/pipes/cap.dm +++ b/code/ATMOSPHERICS/pipes/cap.dm @@ -123,4 +123,4 @@ connect_types = list(2) layer = 2.39 icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE \ No newline at end of file + color = PIPE_COLOR_BLUE diff --git a/code/ATMOSPHERICS/pipes/manifold.dm b/code/ATMOSPHERICS/pipes/manifold.dm index 13e35d10c7b1..2eef5ad74c42 100644 --- a/code/ATMOSPHERICS/pipes/manifold.dm +++ b/code/ATMOSPHERICS/pipes/manifold.dm @@ -33,7 +33,7 @@ /obj/machinery/atmospherics/pipe/manifold/atmos_init() ..() - for(var/D in cardinal) + for(var/D in GLOB.cardinal) if(D == dir) continue for(var/obj/machinery/atmospherics/target in get_step(src, D)) @@ -116,7 +116,7 @@ /obj/machinery/atmospherics/pipe/manifold/update_icon(var/safety = 0) ..() - + if(!check_icon_cache()) return diff --git a/code/ATMOSPHERICS/pipes/manifold4w.dm b/code/ATMOSPHERICS/pipes/manifold4w.dm index d2cee29c0ba6..2b384b3684b8 100644 --- a/code/ATMOSPHERICS/pipes/manifold4w.dm +++ b/code/ATMOSPHERICS/pipes/manifold4w.dm @@ -90,7 +90,7 @@ /obj/machinery/atmospherics/pipe/manifold4w/update_icon(var/safety = 0) ..() - + if(!check_icon_cache()) return @@ -137,7 +137,7 @@ /obj/machinery/atmospherics/pipe/manifold4w/atmos_init() ..() - for(var/D in cardinal) + for(var/D in GLOB.cardinal) for(var/obj/machinery/atmospherics/target in get_step(src, D)) if(target.initialize_directions & get_dir(target,src)) var/c = check_connect_types(target,src) diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm index b966690733bf..94983f62acd1 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple.dm @@ -48,7 +48,7 @@ if(initPipe) normalize_dir() var/N = 2 - for(var/D in cardinal) + for(var/D in GLOB.cardinal) if(D & initialize_directions) N-- for(var/obj/machinery/atmospherics/target in get_step(src, D)) @@ -138,7 +138,7 @@ /obj/machinery/atmospherics/pipe/simple/update_icon(var/safety = 0) ..() - + if(!check_icon_cache()) return diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm index 35df3c60d23a..9c6b0da2121a 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_he.dm @@ -69,7 +69,7 @@ if(initPipe) normalize_dir() var/N = 2 - for(var/D in cardinal) + for(var/D in GLOB.cardinal) if(D & initialize_directions_he) N-- for(var/obj/machinery/atmospherics/pipe/simple/heat_exchanging/target in get_step(src, D)) diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm index db982639c2f3..e0bd78bc2de2 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm @@ -68,4 +68,4 @@ color = PIPE_COLOR_GREEN /obj/machinery/atmospherics/pipe/simple/hidden/purple - color = PIPE_COLOR_PURPLE \ No newline at end of file + color = PIPE_COLOR_PURPLE diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm index ce6ff78b2d09..e7e5ec55e909 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm @@ -8,4 +8,4 @@ fatigue_pressure = 900*ONE_ATMOSPHERE alert_pressure = 900*ONE_ATMOSPHERE - level = 2 \ No newline at end of file + level = 2 diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm index 1ca4da646ac9..410c992c74c9 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm @@ -65,4 +65,4 @@ /obj/machinery/atmospherics/pipe/simple/visible/universal/update_underlays() ..() - update_icon() \ No newline at end of file + update_icon() diff --git a/code/LINDA/LINDA_fire.dm b/code/LINDA/LINDA_fire.dm index 9467f7a37428..6ec505d9f9a2 100644 --- a/code/LINDA/LINDA_fire.dm +++ b/code/LINDA/LINDA_fire.dm @@ -66,7 +66,7 @@ if(!fake) SSair.hotspots += src perform_exposure() - dir = pick(cardinal) + dir = pick(GLOB.cardinal) air_update_turf() /obj/effect/hotspot/proc/perform_exposure() @@ -133,7 +133,7 @@ //Possible spread due to radiated heat if(location.air.temperature > FIRE_MINIMUM_TEMPERATURE_TO_SPREAD) var/radiated_temperature = location.air.temperature*FIRE_SPREAD_RADIOSITY_SCALE - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(location.atmos_adjacent_turfs & direction)) var/turf/simulated/wall/W = get_step(src, direction) if(istype(W)) @@ -309,7 +309,7 @@ if(dist == max_dist) continue - for(var/dir in cardinal) + for(var/dir in GLOB.cardinal) var/turf/link = get_step(T, dir) if (!link) continue @@ -338,4 +338,4 @@ if(prob(chance) || bypass_rng) T.visible_message("[T] melts!") T.burn_down() - return affected \ No newline at end of file + return affected diff --git a/code/LINDA/LINDA_system.dm b/code/LINDA/LINDA_system.dm index fd24dcc0c778..c5bc65d3bf7b 100644 --- a/code/LINDA/LINDA_system.dm +++ b/code/LINDA/LINDA_system.dm @@ -62,7 +62,7 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) /turf/proc/CalculateAdjacentTurfs() atmos_adjacent_turfs_amount = 0 - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) var/turf/T = get_step(src, direction) if(!istype(T)) continue @@ -89,7 +89,7 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) var/adjacent_turfs = list() var/turf/simulated/curloc = src - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(curloc.atmos_adjacent_turfs & direction)) continue @@ -99,11 +99,11 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) if(!alldir) return adjacent_turfs - for(var/direction in diagonals) + for(var/direction in GLOB.diagonals) var/matchingDirections = 0 var/turf/simulated/S = get_step(curloc, direction) - for(var/checkDirection in cardinal) + for(var/checkDirection in GLOB.cardinal) if(!(S.atmos_adjacent_turfs & checkDirection)) continue var/turf/simulated/checkTurf = get_step(S, checkDirection) diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm index b86ad2299ff8..72f77d2440f3 100644 --- a/code/LINDA/LINDA_turf_tile.dm +++ b/code/LINDA/LINDA_turf_tile.dm @@ -149,7 +149,7 @@ if (planet_atmos) atmos_adjacent_turfs_amount++ - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(atmos_adjacent_turfs & direction)) continue @@ -260,9 +260,9 @@ /turf/simulated/proc/get_atmos_overlay_by_name(name) switch(name) if("plasma") - return plmaster + return GLOB.plmaster if("sleeping_agent") - return slmaster + return GLOB.slmaster return null /turf/simulated/proc/tile_graphic() @@ -412,13 +412,13 @@ archive() else //Does particate in air exchange so only consider directions not considered during process_cell() - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(atmos_adjacent_turfs & direction) && !(atmos_supeconductivity & direction)) conductivity_directions += direction if(conductivity_directions>0) //Conduct with tiles around me - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(conductivity_directions&direction) var/turf/neighbor = get_step(src,direction) @@ -499,7 +499,7 @@ turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space /turf/simulated/Initialize_Atmos(times_fired) ..() update_visuals() - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(atmos_adjacent_turfs & direction)) continue var/turf/enemy_tile = get_step(src, direction) diff --git a/code/__DEFINES/_readme.dm b/code/__DEFINES/_readme.dm index 42ad52286fee..8bf6ada6477a 100644 --- a/code/__DEFINES/_readme.dm +++ b/code/__DEFINES/_readme.dm @@ -11,4 +11,4 @@ and most importantly, how to undo your changes if you screw it up. - Sayu -*/ \ No newline at end of file +*/ diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 666971e7209d..10b815e547c7 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -10,4 +10,4 @@ #define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 ) #define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 ) -#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) \ No newline at end of file +#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm index 55ea1d663f81..ea319971a1b6 100644 --- a/code/__DEFINES/access.dm +++ b/code/__DEFINES/access.dm @@ -1,111 +1,111 @@ -#define ACCESS_SECURITY 1 // Security equipment -#define ACCESS_BRIG 2 // Brig timers and permabrig -#define ACCESS_ARMORY 3 -#define ACCESS_FORENSICS_LOCKERS 4 -#define ACCESS_MEDICAL 5 -#define ACCESS_MORGUE 6 -#define ACCESS_TOX 7 -#define ACCESS_TOX_STORAGE 8 -#define ACCESS_GENETICS 9 -#define ACCESS_ENGINE 10 -#define ACCESS_ENGINE_EQUIP 11 -#define ACCESS_MAINT_TUNNELS 12 -#define ACCESS_EXTERNAL_AIRLOCKS 13 -#define ACCESS_EMERGENCY_STORAGE 14 -#define ACCESS_CHANGE_IDS 15 -#define ACCESS_AI_UPLOAD 16 -#define ACCESS_TELEPORTER 17 -#define ACCESS_EVA 18 -#define ACCESS_HEADS 19 -#define ACCESS_CAPTAIN 20 -#define ACCESS_ALL_PERSONAL_LOCKERS 21 -#define ACCESS_CHAPEL_OFFICE 22 -#define ACCESS_TECH_STORAGE 23 -#define ACCESS_ATMOSPHERICS 24 -#define ACCESS_BAR 25 -#define ACCESS_JANITOR 26 -#define ACCESS_CREMATORIUM 27 -#define ACCESS_KITCHEN 28 -#define ACCESS_ROBOTICS 29 -#define ACCESS_RD 30 -#define ACCESS_CARGO 31 -#define ACCESS_CONSTRUCTION 32 -#define ACCESS_CHEMISTRY 33 -#define ACCESS_CARGO_BOT 34 -#define ACCESS_HYDROPONICS 35 -#define ACCESS_MANUFACTURING 36 -#define ACCESS_LIBRARY 37 -#define ACCESS_LAWYER 38 -#define ACCESS_VIROLOGY 39 -#define ACCESS_CMO 40 -#define ACCESS_QM 41 -#define ACCESS_COURT 42 -#define ACCESS_CLOWN 43 -#define ACCESS_MIME 44 -#define ACCESS_SURGERY 45 -#define ACCESS_THEATRE 46 -#define ACCESS_RESEARCH 47 -#define ACCESS_MINING 48 -#define ACCESS_MINING_OFFICE 49 //not in use -#define ACCESS_MAILSORTING 50 -#define ACCESS_MINT 51 -#define ACCESS_MINT_VAULT 52 -#define ACCESS_HEADS_VAULT 53 -#define ACCESS_MINING_STATION 54 -#define ACCESS_XENOBIOLOGY 55 -#define ACCESS_CE 56 -#define ACCESS_HOP 57 -#define ACCESS_HOS 58 -#define ACCESS_RC_ANNOUNCE 59 //Request console announcements -#define ACCESS_KEYCARD_AUTH 60 //Used for events which require at least two people to confirm them -#define ACCESS_TCOMSAT 61 // has access to the entire telecomms satellite / machinery -#define ACCESS_GATEWAY 62 -#define ACCESS_SEC_DOORS 63 // Security front doors -#define ACCESS_PSYCHIATRIST 64 // Psychiatrist's office -#define ACCESS_XENOARCH 65 -#define ACCESS_PARAMEDIC 66 -#define ACCESS_BLUESHIELD 67 -#define ACCESS_SALVAGE_CAPTAIN 69 // Salvage ship captain's quarters -#define ACCESS_MECHANIC 70 -#define ACCESS_PILOT 71 -#define ACCESS_NTREP 73 -#define ACCESS_MAGISTRATE 74 -#define ACCESS_MINISAT 75 -#define ACCESS_MINERAL_STOREROOM 76 -#define ACCESS_NETWORK 77 - -#define ACCESS_WEAPONS 99 //Weapon authorization for secbots - - //BEGIN CENTCOM ACCESS -#define ACCESS_CENT_GENERAL 101//General facilities. -#define ACCESS_CENT_LIVING 102//Living quarters. -#define ACCESS_CENT_MEDICAL 103//Medical. -#define ACCESS_CENT_SECURITY 104//Security. -#define ACCESS_CENT_STORAGE 105//Storage areas. -#define ACCESS_CENT_SHUTTLES 106//Shuttle docks. -#define ACCESS_CENT_TELECOMMS 107//Telecomms. -#define ACCESS_CENT_TELEPORTER 108//Teleporter -#define ACCESS_CENT_SPECOPS 109//Special Ops. -#define ACCESS_CENT_SPECOPS_COMMANDER 110//Special Ops Commander. -#define ACCESS_CENT_BLACKOPS 111//Black Ops. -#define ACCESS_CENT_THUNDER 112//Thunderdome. -#define ACCESS_CENT_BRIDGE 113//Bridge. -#define ACCESS_CENT_COMMANDER 114//Commander's Office/ID computer. - -//The Syndicate -#define ACCESS_SYNDICATE 150//General Syndicate Access -#define ACCESS_SYNDICATE_LEADER 151//Nuke Op Leader Access -#define ACCESS_VOX 152//Vox Access -#define ACCESS_SYNDICATE_COMMAND 153//Admin syndi officer - -//Trade Stations -#define ACCESS_TRADE_SOL 160 - -//MONEY -#define ACCESS_CRATE_CASH 200 - -//Awaymissions -#define ACCESS_AWAY01 271 - -//Ghost roles -#define ACCESS_FREE_GOLEMS 300 \ No newline at end of file +#define ACCESS_SECURITY 1 // Security equipment +#define ACCESS_BRIG 2 // Brig timers and permabrig +#define ACCESS_ARMORY 3 +#define ACCESS_FORENSICS_LOCKERS 4 +#define ACCESS_MEDICAL 5 +#define ACCESS_MORGUE 6 +#define ACCESS_TOX 7 +#define ACCESS_TOX_STORAGE 8 +#define ACCESS_GENETICS 9 +#define ACCESS_ENGINE 10 +#define ACCESS_ENGINE_EQUIP 11 +#define ACCESS_MAINT_TUNNELS 12 +#define ACCESS_EXTERNAL_AIRLOCKS 13 +#define ACCESS_EMERGENCY_STORAGE 14 +#define ACCESS_CHANGE_IDS 15 +#define ACCESS_AI_UPLOAD 16 +#define ACCESS_TELEPORTER 17 +#define ACCESS_EVA 18 +#define ACCESS_HEADS 19 +#define ACCESS_CAPTAIN 20 +#define ACCESS_ALL_PERSONAL_LOCKERS 21 +#define ACCESS_CHAPEL_OFFICE 22 +#define ACCESS_TECH_STORAGE 23 +#define ACCESS_ATMOSPHERICS 24 +#define ACCESS_BAR 25 +#define ACCESS_JANITOR 26 +#define ACCESS_CREMATORIUM 27 +#define ACCESS_KITCHEN 28 +#define ACCESS_ROBOTICS 29 +#define ACCESS_RD 30 +#define ACCESS_CARGO 31 +#define ACCESS_CONSTRUCTION 32 +#define ACCESS_CHEMISTRY 33 +#define ACCESS_CARGO_BOT 34 +#define ACCESS_HYDROPONICS 35 +#define ACCESS_MANUFACTURING 36 +#define ACCESS_LIBRARY 37 +#define ACCESS_LAWYER 38 +#define ACCESS_VIROLOGY 39 +#define ACCESS_CMO 40 +#define ACCESS_QM 41 +#define ACCESS_COURT 42 +#define ACCESS_CLOWN 43 +#define ACCESS_MIME 44 +#define ACCESS_SURGERY 45 +#define ACCESS_THEATRE 46 +#define ACCESS_RESEARCH 47 +#define ACCESS_MINING 48 +#define ACCESS_MINING_OFFICE 49 //not in use +#define ACCESS_MAILSORTING 50 +#define ACCESS_MINT 51 +#define ACCESS_MINT_VAULT 52 +#define ACCESS_HEADS_VAULT 53 +#define ACCESS_MINING_STATION 54 +#define ACCESS_XENOBIOLOGY 55 +#define ACCESS_CE 56 +#define ACCESS_HOP 57 +#define ACCESS_HOS 58 +#define ACCESS_RC_ANNOUNCE 59 //Request console announcements +#define ACCESS_KEYCARD_AUTH 60 //Used for events which require at least two people to confirm them +#define ACCESS_TCOMSAT 61 // has access to the entire telecomms satellite / machinery +#define ACCESS_GATEWAY 62 +#define ACCESS_SEC_DOORS 63 // Security front doors +#define ACCESS_PSYCHIATRIST 64 // Psychiatrist's office +#define ACCESS_XENOARCH 65 +#define ACCESS_PARAMEDIC 66 +#define ACCESS_BLUESHIELD 67 +#define ACCESS_SALVAGE_CAPTAIN 69 // Salvage ship captain's quarters +#define ACCESS_MECHANIC 70 +#define ACCESS_PILOT 71 +#define ACCESS_NTREP 73 +#define ACCESS_MAGISTRATE 74 +#define ACCESS_MINISAT 75 +#define ACCESS_MINERAL_STOREROOM 76 +#define ACCESS_NETWORK 77 + +#define ACCESS_WEAPONS 99 //Weapon authorization for secbots + + //BEGIN CENTCOM ACCESS +#define ACCESS_CENT_GENERAL 101//General facilities. +#define ACCESS_CENT_LIVING 102//Living quarters. +#define ACCESS_CENT_MEDICAL 103//Medical. +#define ACCESS_CENT_SECURITY 104//Security. +#define ACCESS_CENT_STORAGE 105//Storage areas. +#define ACCESS_CENT_SHUTTLES 106//Shuttle docks. +#define ACCESS_CENT_TELECOMMS 107//Telecomms. +#define ACCESS_CENT_TELEPORTER 108//Teleporter +#define ACCESS_CENT_SPECOPS 109//Special Ops. +#define ACCESS_CENT_SPECOPS_COMMANDER 110//Special Ops Commander. +#define ACCESS_CENT_BLACKOPS 111//Black Ops. +#define ACCESS_CENT_THUNDER 112//Thunderdome. +#define ACCESS_CENT_BRIDGE 113//Bridge. +#define ACCESS_CENT_COMMANDER 114//Commander's Office/ID computer. + +//The Syndicate +#define ACCESS_SYNDICATE 150//General Syndicate Access +#define ACCESS_SYNDICATE_LEADER 151//Nuke Op Leader Access +#define ACCESS_VOX 152//Vox Access +#define ACCESS_SYNDICATE_COMMAND 153//Admin syndi officer + +//Trade Stations +#define ACCESS_TRADE_SOL 160 + +//MONEY +#define ACCESS_CRATE_CASH 200 + +//Awaymissions +#define ACCESS_AWAY01 271 + +//Ghost roles +#define ACCESS_FREE_GOLEMS 300 diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 65f4da2b50ac..bbf394c6c0bc 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -70,4 +70,4 @@ ///Max amount of keypress messages per second over two seconds before client is autokicked #define MAX_KEYPRESS_AUTOKICK 50 ///Length of held key rolling buffer -#define HELD_KEY_BUFFER_LENGTH 15 \ No newline at end of file +#define HELD_KEY_BUFFER_LENGTH 15 diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index e69de29bb2d1..8b137891791f 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -0,0 +1 @@ + diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 2f296960622f..de20ccd1a4cf 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -131,4 +131,4 @@ #define ATMOS_ALARM_DANGER 2 //LAVALAND -#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland \ No newline at end of file +#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland diff --git a/code/__DEFINES/bots.dm b/code/__DEFINES/bots.dm index 75bf8a0a5535..c283445d0ad6 100644 --- a/code/__DEFINES/bots.dm +++ b/code/__DEFINES/bots.dm @@ -38,4 +38,4 @@ #define SENTIENCE_ARTIFICIAL 2 #define SENTIENCE_OTHER 3 #define SENTIENCE_MINEBOT 4 -#define SENTIENCE_BOSS 5 \ No newline at end of file +#define SENTIENCE_BOSS 5 diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm index 26c81f8b1432..18059a608399 100644 --- a/code/__DEFINES/callbacks.dm +++ b/code/__DEFINES/callbacks.dm @@ -1,4 +1,4 @@ #define GLOBAL_PROC "some_magic_bullshit" #define CALLBACK new /datum/callback -#define INVOKE_ASYNC ImmediateInvokeAsync \ No newline at end of file +#define INVOKE_ASYNC ImmediateInvokeAsync diff --git a/code/__DEFINES/clothing.dm b/code/__DEFINES/clothing.dm index c44c65397dd1..c39ce0947de2 100644 --- a/code/__DEFINES/clothing.dm +++ b/code/__DEFINES/clothing.dm @@ -97,4 +97,4 @@ //flags for muzzle speech blocking #define MUZZLE_MUTE_NONE 0 // Does not mute you. #define MUZZLE_MUTE_MUFFLE 1 // Muffles everything you say "MHHPHHMMM!!! -#define MUZZLE_MUTE_ALL 2 // Completely mutes you. \ No newline at end of file +#define MUZZLE_MUTE_ALL 2 // Completely mutes you. diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index 13f7777d3866..2bb53c91ce41 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -99,3 +99,12 @@ #define COLOR_ASSEMBLY_LBLUE "#5D99BE" #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" + +// Pipe colours +#define PIPE_COLOR_GREY "#ffffff" //yes white is grey +#define PIPE_COLOR_RED "#ff0000" +#define PIPE_COLOR_BLUE "#0000ff" +#define PIPE_COLOR_CYAN "#00ffff" +#define PIPE_COLOR_GREEN "#00ff00" +#define PIPE_COLOR_YELLOW "#ffcc00" +#define PIPE_COLOR_PURPLE "#5c1ec0" diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index c94dff5ca6c8..c9691c195b8e 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -288,4 +288,4 @@ #define COMSIG_XENO_SLIME_CLICK_SHIFT "xeno_slime_click_shift" //from slime ShiftClickOn(): (/mob) #define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" //from turf ShiftClickOn(): (/mob) #define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob) -#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) \ No newline at end of file +#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm index 0fbd76b9e218..33c80e970cff 100644 --- a/code/__DEFINES/crafting.dm +++ b/code/__DEFINES/crafting.dm @@ -33,4 +33,4 @@ #define RECIPE_GRILL "Grill" #define RECIPE_CANDY "Candy" -#define RECIPE_FAIL null \ No newline at end of file +#define RECIPE_FAIL null diff --git a/code/__DEFINES/dna.dm b/code/__DEFINES/dna.dm new file mode 100644 index 000000000000..7984c1a6dde7 --- /dev/null +++ b/code/__DEFINES/dna.dm @@ -0,0 +1,57 @@ +// Defines for all genetics/DNA related stuff + +// What each index means: +#define DNA_OFF_LOWERBOUND 1 // changed as lists start at 1 not 0 +#define DNA_OFF_UPPERBOUND 2 +#define DNA_ON_LOWERBOUND 3 +#define DNA_ON_UPPERBOUND 4 + +// Define block bounds (off-low,off-high,on-low,on-high) +// Used in setupgame.dm +#define DNA_DEFAULT_BOUNDS list(1,2049,2050,4095) +#define DNA_HARDER_BOUNDS list(1,3049,3050,4095) +#define DNA_HARD_BOUNDS list(1,3490,3500,4095) + +// UI Indices (can change to mutblock style, if desired) +#define DNA_UI_HAIR_R 1 +#define DNA_UI_HAIR_G 2 +#define DNA_UI_HAIR_B 3 +#define DNA_UI_HAIR2_R 4 +#define DNA_UI_HAIR2_G 5 +#define DNA_UI_HAIR2_B 6 +#define DNA_UI_BEARD_R 7 +#define DNA_UI_BEARD_G 8 +#define DNA_UI_BEARD_B 9 +#define DNA_UI_BEARD2_R 10 +#define DNA_UI_BEARD2_G 11 +#define DNA_UI_BEARD2_B 12 +#define DNA_UI_SKIN_TONE 13 +#define DNA_UI_SKIN_R 14 +#define DNA_UI_SKIN_G 15 +#define DNA_UI_SKIN_B 16 +#define DNA_UI_HACC_R 17 +#define DNA_UI_HACC_G 18 +#define DNA_UI_HACC_B 19 +#define DNA_UI_HEAD_MARK_R 20 +#define DNA_UI_HEAD_MARK_G 21 +#define DNA_UI_HEAD_MARK_B 22 +#define DNA_UI_BODY_MARK_R 23 +#define DNA_UI_BODY_MARK_G 24 +#define DNA_UI_BODY_MARK_B 25 +#define DNA_UI_TAIL_MARK_R 26 +#define DNA_UI_TAIL_MARK_G 27 +#define DNA_UI_TAIL_MARK_B 28 +#define DNA_UI_EYES_R 29 +#define DNA_UI_EYES_G 30 +#define DNA_UI_EYES_B 31 +#define DNA_UI_GENDER 32 +#define DNA_UI_BEARD_STYLE 33 +#define DNA_UI_HAIR_STYLE 34 +/*#define DNA_UI_BACC_STYLE 23*/ +#define DNA_UI_HACC_STYLE 35 +#define DNA_UI_HEAD_MARK_STYLE 36 +#define DNA_UI_BODY_MARK_STYLE 37 +#define DNA_UI_TAIL_MARK_STYLE 38 +#define DNA_UI_LENGTH 38 // Update this when you add something, or you WILL break shit. + +#define DNA_SE_LENGTH 55 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow. diff --git a/code/__DEFINES/game.dm b/code/__DEFINES/game.dm index 5c262724a14d..4ad2ca587d9d 100644 --- a/code/__DEFINES/game.dm +++ b/code/__DEFINES/game.dm @@ -2,4 +2,4 @@ #define GAME_STATE_PREGAME 1 #define GAME_STATE_SETTING_UP 2 #define GAME_STATE_PLAYING 3 -#define GAME_STATE_FINISHED 4 \ No newline at end of file +#define GAME_STATE_FINISHED 4 diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 308e7a5f6d91..73a812e8f5b2 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -53,4 +53,4 @@ #define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone" #define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel" #define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva" -#define SPECIAL_ROLE_EVENTMISC "Event Role" \ No newline at end of file +#define SPECIAL_ROLE_EVENTMISC "Event Role" diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm index c8f98c1fbeba..b446a5407ac7 100644 --- a/code/__DEFINES/genetics.dm +++ b/code/__DEFINES/genetics.dm @@ -104,6 +104,7 @@ #define NUTRITION_LEVEL_FED 350 #define NUTRITION_LEVEL_HUNGRY 250 #define NUTRITION_LEVEL_STARVING 150 +#define NUTRITION_LEVEL_HYPOGLYCEMIA 100 #define NUTRITION_LEVEL_CURSED 0 //Used as an upper limit for species that continuously gain nutriment @@ -166,4 +167,4 @@ #define NO_GERMS "no_germs" #define NO_DECAY "no_decay" #define PIERCEIMMUNE "pierce_immunity" -#define NO_HUNGER "no_hunger" \ No newline at end of file +#define NO_HUNGER "no_hunger" diff --git a/code/__DEFINES/hydroponics.dm b/code/__DEFINES/hydroponics.dm index f6693b0bcfce..8a3890d45f9b 100644 --- a/code/__DEFINES/hydroponics.dm +++ b/code/__DEFINES/hydroponics.dm @@ -57,4 +57,4 @@ #define TRAIT_BIOLUM_COLOUR 37 #define TRAIT_IMMUTABLE 38 #define TRAIT_RARITY 39 -#define TRAIT_BATTERY_RECHARGE 40 \ No newline at end of file +#define TRAIT_BATTERY_RECHARGE 40 diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 33ce299c5405..671897b612d9 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -28,13 +28,13 @@ #define is_pen(W) (istype(W, /obj/item/pen)) -var/list/static/global/pointed_types = typecacheof(list( +GLOBAL_LIST_INIT(pointed_types, typecacheof(list( /obj/item/pen, /obj/item/screwdriver, /obj/item/reagent_containers/syringe, - /obj/item/kitchen/utensil/fork)) + /obj/item/kitchen/utensil/fork))) -#define is_pointed(W) (is_type_in_typecache(W, pointed_types)) +#define is_pointed(W) (is_type_in_typecache(W, GLOB.pointed_types)) GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( /obj/item/stack/sheet/glass, diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm index ff7dda4fa94e..d72628e2cb75 100644 --- a/code/__DEFINES/job.dm +++ b/code/__DEFINES/job.dm @@ -1,64 +1,64 @@ -/////////////////////////////// -// WARNING // -//////////////////////////////////////////////////////////////////////// -// Do NOT touch the values associated with these defines, as they are // -// used by the game database to keep track of job flags. Do NOT touch // -//////////////////////////////////////////////////////////////////////// - -#define JOBCAT_ENGSEC (1<<0) - -#define JOB_CAPTAIN (1<<0) -#define JOB_HOS (1<<1) -#define JOB_WARDEN (1<<2) -#define JOB_DETECTIVE (1<<3) -#define JOB_OFFICER (1<<4) -#define JOB_CHIEF (1<<5) -#define JOB_ENGINEER (1<<6) -#define JOB_ATMOSTECH (1<<7) -#define JOB_AI (1<<8) -#define JOB_CYBORG (1<<9) -#define JOB_CENTCOM (1<<10) -#define JOB_SYNDICATE (1<<11) - -#define JOBCAT_MEDSCI (1<<1) - -#define JOB_RD (1<<0) -#define JOB_SCIENTIST (1<<1) -#define JOB_CHEMIST (1<<2) -#define JOB_CMO (1<<3) -#define JOB_DOCTOR (1<<4) -#define JOB_GENETICIST (1<<5) -#define JOB_VIROLOGIST (1<<6) -#define JOB_PSYCHIATRIST (1<<7) -#define JOB_ROBOTICIST (1<<8) -#define JOB_PARAMEDIC (1<<9) -#define JOB_CORONER (1<<10) - - -#define JOBCAT_SUPPORT (1<<2) - -#define JOB_HOP (1<<0) -#define JOB_BARTENDER (1<<1) -#define JOB_BOTANIST (1<<2) -#define JOB_CHEF (1<<3) -#define JOB_JANITOR (1<<4) -#define JOB_LIBRARIAN (1<<5) -#define JOB_QUARTERMASTER (1<<6) -#define JOB_CARGOTECH (1<<7) -#define JOB_MINER (1<<8) -#define JOB_LAWYER (1<<9) -#define JOB_CHAPLAIN (1<<10) -#define JOB_CLOWN (1<<11) -#define JOB_MIME (1<<12) -#define JOB_CIVILIAN (1<<13) - - -#define JOBCAT_KARMA (1<<3) - -#define JOB_NANO (1<<0) -#define JOB_BLUESHIELD (1<<1) -#define JOB_BARBER (1<<3) -#define JOB_MECHANIC (1<<4) -#define JOB_BRIGDOC (1<<5) -#define JOB_JUDGE (1<<6) -#define JOB_PILOT (1<<7) +/////////////////////////////// +// WARNING // +//////////////////////////////////////////////////////////////////////// +// Do NOT touch the values associated with these defines, as they are // +// used by the game database to keep track of job flags. Do NOT touch // +//////////////////////////////////////////////////////////////////////// + +#define JOBCAT_ENGSEC (1<<0) + +#define JOB_CAPTAIN (1<<0) +#define JOB_HOS (1<<1) +#define JOB_WARDEN (1<<2) +#define JOB_DETECTIVE (1<<3) +#define JOB_OFFICER (1<<4) +#define JOB_CHIEF (1<<5) +#define JOB_ENGINEER (1<<6) +#define JOB_ATMOSTECH (1<<7) +#define JOB_AI (1<<8) +#define JOB_CYBORG (1<<9) +#define JOB_CENTCOM (1<<10) +#define JOB_SYNDICATE (1<<11) + +#define JOBCAT_MEDSCI (1<<1) + +#define JOB_RD (1<<0) +#define JOB_SCIENTIST (1<<1) +#define JOB_CHEMIST (1<<2) +#define JOB_CMO (1<<3) +#define JOB_DOCTOR (1<<4) +#define JOB_GENETICIST (1<<5) +#define JOB_VIROLOGIST (1<<6) +#define JOB_PSYCHIATRIST (1<<7) +#define JOB_ROBOTICIST (1<<8) +#define JOB_PARAMEDIC (1<<9) +#define JOB_CORONER (1<<10) + + +#define JOBCAT_SUPPORT (1<<2) + +#define JOB_HOP (1<<0) +#define JOB_BARTENDER (1<<1) +#define JOB_BOTANIST (1<<2) +#define JOB_CHEF (1<<3) +#define JOB_JANITOR (1<<4) +#define JOB_LIBRARIAN (1<<5) +#define JOB_QUARTERMASTER (1<<6) +#define JOB_CARGOTECH (1<<7) +#define JOB_MINER (1<<8) +#define JOB_LAWYER (1<<9) +#define JOB_CHAPLAIN (1<<10) +#define JOB_CLOWN (1<<11) +#define JOB_MIME (1<<12) +#define JOB_CIVILIAN (1<<13) + + +#define JOBCAT_KARMA (1<<3) + +#define JOB_NANO (1<<0) +#define JOB_BLUESHIELD (1<<1) +#define JOB_BARBER (1<<3) +#define JOB_MECHANIC (1<<4) +#define JOB_BRIGDOC (1<<5) +#define JOB_JUDGE (1<<6) +#define JOB_PILOT (1<<7) diff --git a/code/__DEFINES/js.dm b/code/__DEFINES/js.dm index 65b70e04d637..e1a86e664d44 100644 --- a/code/__DEFINES/js.dm +++ b/code/__DEFINES/js.dm @@ -1,91 +1,91 @@ -//this function places received data into element with specified id. -#define JS_BYJAX {" - -function replaceContent() { - var args = Array.prototype.slice.call(arguments); - var id = args\[0\]; - var content = args\[1\]; - var callback = null; - if(args\[2\]){ - callback = args\[2\]; - if(args\[3\]){ - args = args.slice(3); - } - } - var parent = document.getElementById(id); - if(typeof(parent)!=='undefined' && parent!=null){ - parent.innerHTML = content?content:''; - } - if(callback && window\[callback\]){ - window\[callback\].apply(null,args); - } -} -"} - -/* -sends data to control_id:replaceContent - -receiver - mob -control_id - window id (for windows opened with browse(), it'll be "windowname.browser") -target_element - HTML element id -new_content - HTML content -callback - js function that will be called after the data is sent -callback_args - arguments for callback function - -Be sure to include required js functions in your page, or it'll raise an exception. - -And yes I know this is a proc in a defines file, but its highly relevant so it can be here -*/ -proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) - if(receiver && target_element && control_id) // && winexists(receiver, control_id)) - var/list/argums = list(target_element, new_content) - if(callback) - argums += callback - if(callback_args) - argums += callback_args - argums = list2params(argums) -/* if(callback_args) - argums += "&[list2params(callback_args)]" -*/ - receiver << output(argums,"[control_id]:replaceContent") - return - - -// Misc JS for some dropdowns -#define JS_DROPDOWNS {" -function dropdowns() { - var divs = document.getElementsByTagName('div'); - var headers = new Array(); - var links = new Array(); - for(var i=0;i=0) { - elem.className = elem.className.replace('visible','hidden'); - this.className = this.className.replace('open','closed'); - this.innerHTML = this.innerHTML.replace('-','+'); - } - else { - elem.className = elem.className.replace('hidden','visible'); - this.className = this.className.replace('closed','open'); - this.innerHTML = this.innerHTML.replace('+','-'); - } - return false; - } - })(links\[i\]); - } - } -} -"} \ No newline at end of file +//this function places received data into element with specified id. +#define JS_BYJAX {" + +function replaceContent() { + var args = Array.prototype.slice.call(arguments); + var id = args\[0\]; + var content = args\[1\]; + var callback = null; + if(args\[2\]){ + callback = args\[2\]; + if(args\[3\]){ + args = args.slice(3); + } + } + var parent = document.getElementById(id); + if(typeof(parent)!=='undefined' && parent!=null){ + parent.innerHTML = content?content:''; + } + if(callback && window\[callback\]){ + window\[callback\].apply(null,args); + } +} +"} + +/* +sends data to control_id:replaceContent + +receiver - mob +control_id - window id (for windows opened with browse(), it'll be "windowname.browser") +target_element - HTML element id +new_content - HTML content +callback - js function that will be called after the data is sent +callback_args - arguments for callback function + +Be sure to include required js functions in your page, or it'll raise an exception. + +And yes I know this is a proc in a defines file, but its highly relevant so it can be here +*/ +proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) + if(receiver && target_element && control_id) // && winexists(receiver, control_id)) + var/list/argums = list(target_element, new_content) + if(callback) + argums += callback + if(callback_args) + argums += callback_args + argums = list2params(argums) +/* if(callback_args) + argums += "&[list2params(callback_args)]" +*/ + receiver << output(argums,"[control_id]:replaceContent") + return + + +// Misc JS for some dropdowns +#define JS_DROPDOWNS {" +function dropdowns() { + var divs = document.getElementsByTagName('div'); + var headers = new Array(); + var links = new Array(); + for(var i=0;i=0) { + elem.className = elem.className.replace('visible','hidden'); + this.className = this.className.replace('open','closed'); + this.innerHTML = this.innerHTML.replace('-','+'); + } + else { + elem.className = elem.className.replace('hidden','visible'); + this.className = this.className.replace('closed','open'); + this.innerHTML = this.innerHTML.replace('+','-'); + } + return false; + } + })(links\[i\]); + } + } +} +"} diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index db88f46a676f..ed4cb0d5940c 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -110,4 +110,4 @@ #define ABOVE_HUD_LAYER 22 #define SPLASHSCREEN_LAYER 23 -#define SPLASHSCREEN_PLANE 23 \ No newline at end of file +#define SPLASHSCREEN_PLANE 23 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 7357c5f441e3..01dbc2f1c2b9 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -89,4 +89,4 @@ #define FLASH_LIGHT_DURATION 2 #define FLASH_LIGHT_POWER 3 -#define FLASH_LIGHT_RANGE 3.8 \ No newline at end of file +#define FLASH_LIGHT_RANGE 3.8 diff --git a/code/__DEFINES/logs.dm b/code/__DEFINES/logs.dm new file mode 100644 index 000000000000..a8a4a457c7c2 --- /dev/null +++ b/code/__DEFINES/logs.dm @@ -0,0 +1,6 @@ +#define ATTACK_LOG "Attack" +#define DEFENSE_LOG "Defense" +#define CONVERSION_LOG "Conversion" +#define SAY_LOG "Say" +#define EMOTE_LOG "Emote" +#define MISC_LOG "Misc" \ No newline at end of file diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm index e8955e64b50e..b414f1467054 100644 --- a/code/__DEFINES/mecha.dm +++ b/code/__DEFINES/mecha.dm @@ -19,4 +19,4 @@ #define MECHA_SECURE_BOLTS 1 #define MECHA_LOOSE_BOLTS 2 #define MECHA_OPEN_HATCH 3 -#define MECHA_UNSECURE_CELL 4 \ No newline at end of file +#define MECHA_UNSECURE_CELL 4 diff --git a/code/__DEFINES/medal.dm b/code/__DEFINES/medal.dm index 81dea5f373bf..b47d562efee3 100644 --- a/code/__DEFINES/medal.dm +++ b/code/__DEFINES/medal.dm @@ -21,4 +21,4 @@ #define DRAKE_SCORE "Drakes Killed" #define LEGION_SCORE "Legion Killed" #define SWARMER_BEACON_SCORE "Swarmer Beacons Killed" -#define TENDRIL_CLEAR_SCORE "Tendrils Killed" \ No newline at end of file +#define TENDRIL_CLEAR_SCORE "Tendrils Killed" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 9ff3345cfab7..7eaaa86747ec 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -122,10 +122,10 @@ ) #define FOR_DVIEW(type, range, center, invis_flags) \ - dview_mob.loc = center; \ - dview_mob.see_invisible = invis_flags; \ - for(type in view(range, dview_mob)) -#define END_FOR_DVIEW dview_mob.loc = null + GLOB.dview_mob.loc = center; \ + GLOB.dview_mob.see_invisible = invis_flags; \ + for(type in view(range, GLOB.dview_mob)) +#define END_FOR_DVIEW GLOB.dview_mob.loc = null //Turf locational stuff #define get_turf(A) (get_step(A, 0)) @@ -434,4 +434,4 @@ #define LINDA_SPAWN_N2O 64 -#define LINDA_SPAWN_AIR 256 \ No newline at end of file +#define LINDA_SPAWN_AIR 256 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 293d97e4fa3c..e1e15a926d6e 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -239,4 +239,4 @@ #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) -#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; \ No newline at end of file +#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm index 750821bd898a..8051a5162986 100644 --- a/code/__DEFINES/move_force.dm +++ b/code/__DEFINES/move_force.dm @@ -16,4 +16,4 @@ #define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT #define MOVE_FORCE_WEAK MOVE_FORCE_DEFAULT / 2 #define MOVE_FORCE_VERY_WEAK (MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1 -#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3) \ No newline at end of file +#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3) diff --git a/code/__DEFINES/pda.dm b/code/__DEFINES/pda.dm index d22b90870b04..c5d32f03ba82 100644 --- a/code/__DEFINES/pda.dm +++ b/code/__DEFINES/pda.dm @@ -1,3 +1,3 @@ #define PDA_APP_UPDATE 0 #define PDA_APP_NOUPDATE 1 -#define PDA_APP_UPDATE_SLOW 2 \ No newline at end of file +#define PDA_APP_UPDATE_SLOW 2 diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 6adb876da8d4..0aaee0533d42 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -20,4 +20,4 @@ #define QDELING(X) (X.gc_destroyed) #define QDELETED(X) (!X || QDELING(X)) -#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) \ No newline at end of file +#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) diff --git a/code/__DEFINES/radio.dm b/code/__DEFINES/radio.dm index e49a9ea9b677..e0c71836c726 100644 --- a/code/__DEFINES/radio.dm +++ b/code/__DEFINES/radio.dm @@ -1,60 +1,60 @@ - -#define DISPLAY_FREQ 1435 //status displays -#define ATMOS_FIRE_FREQ 1437 //air alarms -#define ENGINE_FREQ 1438 //engine components -#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control -#define ATMOS_DISTRO_FREQ 1443 //distro loop -#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks -#define BOT_BEACON_FREQ 1445 //bot navigation beacons -#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets - -#define RSD_FREQ 1457 //radio signal device -#define IMPL_FREQ 1451 //tracking implant - -#define RADIO_LOW_FREQ 1200 //minimum radio freq -#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq -#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq -#define RADIO_HIGH_FREQ 1600 //maximum radio freq - -#define SYND_FREQ 1213 -#define SYNDTEAM_FREQ 1244 -#define DTH_FREQ 1341 //Special Operations -#define AI_FREQ 1343 -#define ERT_FREQ 1345 -#define COMM_FREQ 1353 //Command -#define BOT_FREQ 1447 //mulebot, secbot, ed209 - - -// Department channels -#define PUB_FREQ 1459 //standard radio chat -#define SEC_FREQ 1359 //security -#define ENG_FREQ 1357 //engineering -#define SCI_FREQ 1351 //science -#define MED_FREQ 1355 //medical -#define SUP_FREQ 1347 //cargo -#define SRV_FREQ 1349 //service - -// Internal department channels -#define MED_I_FREQ 1485 -#define SEC_I_FREQ 1475 - -// Transmission methods -#define TRANSMISSION_WIRE 0 -#define TRANSMISSION_RADIO 1 - -//This filter is special because devices belonging to default also recieve signals sent to any other filter. -#define RADIO_DEFAULT "radio_default" -#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms -#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms -#define RADIO_CHAT "radio_telecoms" -#define RADIO_ATMOSIA "radio_atmos" -#define RADIO_NAVBEACONS "radio_navbeacon" -#define RADIO_AIRLOCK "radio_airlock" -#define RADIO_SECBOT "radio_secbot" -#define RADIO_HONKBOT "radio_honkbot" -#define RADIO_MULEBOT "radio_mulebot" -#define RADIO_CLEANBOT "10" -#define RADIO_FLOORBOT "11" -#define RADIO_MEDBOT "12" -#define RADIO_MAGNETS "radio_magnet" -#define RADIO_LOGIC "radio_logic" + +#define DISPLAY_FREQ 1435 //status displays +#define ATMOS_FIRE_FREQ 1437 //air alarms +#define ENGINE_FREQ 1438 //engine components +#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control +#define ATMOS_DISTRO_FREQ 1443 //distro loop +#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks +#define BOT_BEACON_FREQ 1445 //bot navigation beacons +#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets + +#define RSD_FREQ 1457 //radio signal device +#define IMPL_FREQ 1451 //tracking implant + +#define RADIO_LOW_FREQ 1200 //minimum radio freq +#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq +#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq +#define RADIO_HIGH_FREQ 1600 //maximum radio freq + +#define SYND_FREQ 1213 +#define SYNDTEAM_FREQ 1244 +#define DTH_FREQ 1341 //Special Operations +#define AI_FREQ 1343 +#define ERT_FREQ 1345 +#define COMM_FREQ 1353 //Command +#define BOT_FREQ 1447 //mulebot, secbot, ed209 + + +// Department channels +#define PUB_FREQ 1459 //standard radio chat +#define SEC_FREQ 1359 //security +#define ENG_FREQ 1357 //engineering +#define SCI_FREQ 1351 //science +#define MED_FREQ 1355 //medical +#define SUP_FREQ 1347 //cargo +#define SRV_FREQ 1349 //service + +// Internal department channels +#define MED_I_FREQ 1485 +#define SEC_I_FREQ 1475 + +// Transmission methods +#define TRANSMISSION_WIRE 0 +#define TRANSMISSION_RADIO 1 + +//This filter is special because devices belonging to default also recieve signals sent to any other filter. +#define RADIO_DEFAULT "radio_default" +#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms +#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms +#define RADIO_CHAT "radio_telecoms" +#define RADIO_ATMOSIA "radio_atmos" +#define RADIO_NAVBEACONS "radio_navbeacon" +#define RADIO_AIRLOCK "radio_airlock" +#define RADIO_SECBOT "radio_secbot" +#define RADIO_HONKBOT "radio_honkbot" +#define RADIO_MULEBOT "radio_mulebot" +#define RADIO_CLEANBOT "10" +#define RADIO_FLOORBOT "11" +#define RADIO_MEDBOT "12" +#define RADIO_MAGNETS "radio_magnet" +#define RADIO_LOGIC "radio_logic" diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index ce4767f8b895..564247455e37 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -18,4 +18,4 @@ #define OPENCONTAINER (REFILLABLE | DRAINABLE | TRANSPARENT) #define REAGENT_TOUCH 1 -#define REAGENT_INGEST 2 \ No newline at end of file +#define REAGENT_INGEST 2 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 34ad4d69e54c..2b9e2c56b4de 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -1,81 +1,81 @@ - - -//Values for antag preferences, event roles, etc. unified here - - - -//These are synced with the Database, if you change the values of the defines -//then you MUST update the database! -// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out -// justice if someone's abusing your role -#define ROLE_SYNDICATE "Syndicate" -#define ROLE_TRAITOR "traitor" -#define ROLE_OPERATIVE "operative" -#define ROLE_CHANGELING "changeling" -#define ROLE_WIZARD "wizard" -#define ROLE_REV "revolutionary" -#define ROLE_ALIEN "xenomorph" -#define ROLE_PAI "pAI" -#define ROLE_CULTIST "cultist" -#define ROLE_BLOB "blob" -#define ROLE_NINJA "space ninja" -#define ROLE_MONKEY "monkey" -#define ROLE_GANG "gangster" -#define ROLE_SHADOWLING "shadowling" -#define ROLE_ABDUCTOR "abductor" -#define ROLE_REVENANT "revenant" -#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another -#define ROLE_HOG_CULTIST "hand of god: cultist" -#define ROLE_DEVIL "devil" -#define ROLE_RAIDER "vox raider" -#define ROLE_TRADER "trader" -#define ROLE_VAMPIRE "vampire" -// Role tags for EVERYONE! -#define ROLE_BORER "cortical borer" -#define ROLE_DEMON "slaughter demon" -#define ROLE_SENTIENT "sentient animal" -#define ROLE_POSIBRAIN "positronic brain" -#define ROLE_GUARDIAN "guardian" -#define ROLE_MORPH "morph" -#define ROLE_ERT "emergency response team" -#define ROLE_NYMPH "Dionaea" -#define ROLE_GSPIDER "giant spider" -#define ROLE_DRONE "drone" -#define ROLE_DEATHSQUAD "deathsquad" -#define ROLE_EVENTMISC "eventmisc" -#define ROLE_GHOST "ghost role" - -//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. -//The gamemode specific ones are just so the gamemodes can query whether a player is old enough -//(in game days played) to play that role -var/global/list/special_roles = list( - ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor - ROLE_BLOB = /datum/game_mode/blob, // Blob - ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling - ROLE_BORER, // Cortical borer - ROLE_CULTIST = /datum/game_mode/cult, // Cultist - ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil - ROLE_GSPIDER, // Giant spider - ROLE_GUARDIAN, // Guardian - ROLE_MORPH, // Morph - ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative - ROLE_PAI, // PAI - ROLE_POSIBRAIN, // Positronic brain - ROLE_REVENANT, // Revenant - ROLE_REV = /datum/game_mode/revolution, // Revolutionary - ROLE_SENTIENT, // Sentient animal - ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling - ROLE_DEMON, // Slaguther demon - ROLE_NINJA, // Space ninja - ROLE_TRADER, // Trader - ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor - ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire - ROLE_RAIDER = /datum/game_mode/heist, // Vox raider - ROLE_ALIEN, // Xenomorph - ROLE_WIZARD = /datum/game_mode/wizard, // Wizard - // UNUSED/BROKEN ANTAGS -// ROLE_HOG_GOD = /datum/game_mode/hand_of_god, -// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, -// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported -// ROLE_GANG = /datum/game_mode/gang, -) + + +//Values for antag preferences, event roles, etc. unified here + + + +//These are synced with the Database, if you change the values of the defines +//then you MUST update the database! +// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out +// justice if someone's abusing your role +#define ROLE_SYNDICATE "Syndicate" +#define ROLE_TRAITOR "traitor" +#define ROLE_OPERATIVE "operative" +#define ROLE_CHANGELING "changeling" +#define ROLE_WIZARD "wizard" +#define ROLE_REV "revolutionary" +#define ROLE_ALIEN "xenomorph" +#define ROLE_PAI "pAI" +#define ROLE_CULTIST "cultist" +#define ROLE_BLOB "blob" +#define ROLE_NINJA "space ninja" +#define ROLE_MONKEY "monkey" +#define ROLE_GANG "gangster" +#define ROLE_SHADOWLING "shadowling" +#define ROLE_ABDUCTOR "abductor" +#define ROLE_REVENANT "revenant" +#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another +#define ROLE_HOG_CULTIST "hand of god: cultist" +#define ROLE_DEVIL "devil" +#define ROLE_RAIDER "vox raider" +#define ROLE_TRADER "trader" +#define ROLE_VAMPIRE "vampire" +// Role tags for EVERYONE! +#define ROLE_BORER "cortical borer" +#define ROLE_DEMON "slaughter demon" +#define ROLE_SENTIENT "sentient animal" +#define ROLE_POSIBRAIN "positronic brain" +#define ROLE_GUARDIAN "guardian" +#define ROLE_MORPH "morph" +#define ROLE_ERT "emergency response team" +#define ROLE_NYMPH "Dionaea" +#define ROLE_GSPIDER "giant spider" +#define ROLE_DRONE "drone" +#define ROLE_DEATHSQUAD "deathsquad" +#define ROLE_EVENTMISC "eventmisc" +#define ROLE_GHOST "ghost role" + +//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. +//The gamemode specific ones are just so the gamemodes can query whether a player is old enough +//(in game days played) to play that role +var/global/list/special_roles = list( + ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor + ROLE_BLOB = /datum/game_mode/blob, // Blob + ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling + ROLE_BORER, // Cortical borer + ROLE_CULTIST = /datum/game_mode/cult, // Cultist + ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil + ROLE_GSPIDER, // Giant spider + ROLE_GUARDIAN, // Guardian + ROLE_MORPH, // Morph + ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative + ROLE_PAI, // PAI + ROLE_POSIBRAIN, // Positronic brain + ROLE_REVENANT, // Revenant + ROLE_REV = /datum/game_mode/revolution, // Revolutionary + ROLE_SENTIENT, // Sentient animal + ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling + ROLE_DEMON, // Slaguther demon + ROLE_NINJA, // Space ninja + ROLE_TRADER, // Trader + ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor + ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire + ROLE_RAIDER = /datum/game_mode/heist, // Vox raider + ROLE_ALIEN, // Xenomorph + ROLE_WIZARD = /datum/game_mode/wizard, // Wizard + // UNUSED/BROKEN ANTAGS +// ROLE_HOG_GOD = /datum/game_mode/hand_of_god, +// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, +// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported +// ROLE_GANG = /datum/game_mode/gang, +) diff --git a/code/__DEFINES/rolebans.dm b/code/__DEFINES/rolebans.dm index d6e54abf14ae..ad6f1324afab 100644 --- a/code/__DEFINES/rolebans.dm +++ b/code/__DEFINES/rolebans.dm @@ -1,5 +1,5 @@ // Bannable antag roles -var/global/list/antag_roles = list( +GLOBAL_LIST_INIT(antag_roles, list( ROLE_TRAITOR, ROLE_OPERATIVE, ROLE_CHANGELING, @@ -18,14 +18,14 @@ var/global/list/antag_roles = list( ROLE_GUARDIAN, ROLE_MORPH, ROLE_GSPIDER, -) +)) // Bannable other roles -var/global/list/other_roles = list( +GLOBAL_LIST_INIT(other_roles, list( ROLE_SENTIENT, ROLE_NYMPH, ROLE_ERT, ROLE_GHOST, "AntagHUD", "Records" -) +)) diff --git a/code/__DEFINES/shuttle.dm b/code/__DEFINES/shuttle.dm index 22af9a38670a..72ed25d1518c 100644 --- a/code/__DEFINES/shuttle.dm +++ b/code/__DEFINES/shuttle.dm @@ -13,4 +13,4 @@ #define SHUTTLE_DOCKER_LANDING_CLEAR 1 #define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2 -#define SHUTTLE_DOCKER_BLOCKED 3 \ No newline at end of file +#define SHUTTLE_DOCKER_BLOCKED 3 diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm index 58f557c4b751..52fc66aa814f 100644 --- a/code/__DEFINES/sight.dm +++ b/code/__DEFINES/sight.dm @@ -38,4 +38,4 @@ #define VISOR_TINT (1<<1) #define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses #define VISOR_DARKNESSVIEW (1<<3) -#define VISOR_INVISVIEW (1<<4) \ No newline at end of file +#define VISOR_INVISVIEW (1<<4) diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 7e131dcaa21a..b0274e9d8026 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -14,4 +14,4 @@ #define SOUND_MINIMUM_PRESSURE 10 -#define FALLOFF_SOUNDS 0.5 \ No newline at end of file +#define FALLOFF_SOUNDS 0.5 diff --git a/code/__DEFINES/station_goals.dm b/code/__DEFINES/station_goals.dm index 7c13f4f9e67b..0c19da7b5f10 100644 --- a/code/__DEFINES/station_goals.dm +++ b/code/__DEFINES/station_goals.dm @@ -1,2 +1,2 @@ #define BSA_SIZE_FRONT 4 -#define BSA_SIZE_BACK 6 \ No newline at end of file +#define BSA_SIZE_BACK 6 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 6a14e6fae663..54b85d80e76b 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -81,4 +81,4 @@ #define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target -#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module \ No newline at end of file +#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index c55fec2cd871..300d3a41fc0d 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -144,4 +144,4 @@ A.overlays |= po;\ }\ A.flags_2 &= ~OVERLAY_QUEUED_2;\ -} \ No newline at end of file +} diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm index 16b6e475b8ea..56e0958d1a41 100644 --- a/code/__DEFINES/typeids.dm +++ b/code/__DEFINES/typeids.dm @@ -3,4 +3,4 @@ #define TYPEID_NORMAL_LIST "f" //helper macros #define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref) - 6) ) ) -#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) \ No newline at end of file +#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 22e13f87f8a3..0ac352f9ae94 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -19,4 +19,4 @@ #define VV_NULL "NULL" #define VV_RESTORE_DEFAULT "Restore to Default" #define VV_MARKED_DATUM "Marked Datum" -#define VV_REGEX "Regex" \ No newline at end of file +#define VV_REGEX "Regex" diff --git a/code/__HELPERS/AnimationLibrary.dm b/code/__HELPERS/AnimationLibrary.dm index fd79fe197f2c..cc496a85b634 100644 --- a/code/__HELPERS/AnimationLibrary.dm +++ b/code/__HELPERS/AnimationLibrary.dm @@ -187,4 +187,4 @@ var/original_y = A.pixel_y animate(A, transform = matrix(punchstr, MATRIX_ROTATE), pixel_y = 16, time = 2, color = "#eeeeee", easing = BOUNCE_EASING) animate(transform = matrix(-punchstr, MATRIX_ROTATE), pixel_y = original_y, time = 2, color = "#ffffff", easing = BOUNCE_EASING) - animate(transform = null, time = 3, easing = BOUNCE_EASING) \ No newline at end of file + animate(transform = null, time = 3, easing = BOUNCE_EASING) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 7a91c3bf9bcd..f5d582f5e429 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -5,7 +5,7 @@ // will get logs that are one big line if the system is Linux and they are using notepad. This solves it by adding CR to every line ending // in the logs. ascii character 13 = CR -/var/global/log_end = world.system_type == UNIX ? ascii2text(13) : "" +GLOBAL_VAR_INIT(log_end, (world.system_type == UNIX ? ascii2text(13) : "")) #define DIRECT_OUTPUT(A, B) A << B #define SEND_IMAGE(target, image) DIRECT_OUTPUT(target, image) @@ -30,13 +30,13 @@ #endif /proc/log_admin(text) - admin_log.Add(text) + GLOB.admin_log.Add(text) if(config.log_admin) - WRITE_LOG(GLOB.world_game_log, "ADMIN: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "ADMIN: [text][GLOB.log_end]") /proc/log_debug(text) if(config.log_debug) - WRITE_LOG(GLOB.world_game_log, "DEBUG: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "DEBUG: [text][GLOB.log_end]") for(var/client/C in GLOB.admins) if(check_rights(R_DEBUG, 0, C.mob) && (C.prefs.toggles & CHAT_DEBUGLOGS)) @@ -44,88 +44,88 @@ /proc/log_game(text) if(config.log_game) - WRITE_LOG(GLOB.world_game_log, "GAME: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "GAME: [text][GLOB.log_end]") /proc/log_vote(text) if(config.log_vote) - WRITE_LOG(GLOB.world_game_log, "VOTE: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "VOTE: [text][GLOB.log_end]") /proc/log_access_in(client/new_client) if(config.log_access) var/message = "[key_name(new_client)] - IP:[new_client.address] - CID:[new_client.computer_id] - BYOND v[new_client.byond_version]" - WRITE_LOG(GLOB.world_game_log, "ACCESS IN: [message][log_end]") + WRITE_LOG(GLOB.world_game_log, "ACCESS IN: [message][GLOB.log_end]") /proc/log_access_out(mob/last_mob) if(config.log_access) var/message = "[key_name(last_mob)] - IP:[last_mob.lastKnownIP] - CID:[last_mob.computer_id] - BYOND Logged Out" - WRITE_LOG(GLOB.world_game_log, "ACCESS OUT: [message][log_end]") + WRITE_LOG(GLOB.world_game_log, "ACCESS OUT: [message][GLOB.log_end]") /proc/log_say(text, mob/speaker) if(config.log_say) - WRITE_LOG(GLOB.world_game_log, "SAY: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "SAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_whisper(text, mob/speaker) if(config.log_whisper) - WRITE_LOG(GLOB.world_game_log, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "WHISPER: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_ooc(text, client/user) if(config.log_ooc) - WRITE_LOG(GLOB.world_game_log, "OOC: [user.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "OOC: [user.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_aooc(text, client/user) if(config.log_ooc) - WRITE_LOG(GLOB.world_game_log, "AOOC: [user.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "AOOC: [user.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_looc(text, client/user) if(config.log_ooc) - WRITE_LOG(GLOB.world_game_log, "LOOC: [user.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "LOOC: [user.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_emote(text, mob/speaker) if(config.log_emote) - WRITE_LOG(GLOB.world_game_log, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "EMOTE: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_attack(attacker, defender, message) if(config.log_attack) - WRITE_LOG(GLOB.world_game_log, "ATTACK: [attacker] against [defender]: [message][log_end]") //Seperate attack logs? Why? + WRITE_LOG(GLOB.world_game_log, "ATTACK: [attacker] against [defender]: [message][GLOB.log_end]") //Seperate attack logs? Why? /proc/log_adminsay(text, mob/speaker) if(config.log_adminchat) - WRITE_LOG(GLOB.world_game_log, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "ADMINSAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_qdel(text) WRITE_LOG(GLOB.world_qdel_log, "QDEL: [text]") /proc/log_mentorsay(text, mob/speaker) if(config.log_adminchat) - WRITE_LOG(GLOB.world_game_log, "MENTORSAY: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "MENTORSAY: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_ghostsay(text, mob/speaker) if(config.log_say) - WRITE_LOG(GLOB.world_game_log, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "DEADCHAT: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_ghostemote(text, mob/speaker) if(config.log_emote) - WRITE_LOG(GLOB.world_game_log, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "DEADEMOTE: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_adminwarn(text) if(config.log_adminwarn) - WRITE_LOG(GLOB.world_game_log, "ADMINWARN: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "ADMINWARN: [html_decode(text)][GLOB.log_end]") /proc/log_pda(text, mob/speaker) if(config.log_pda) - WRITE_LOG(GLOB.world_game_log, "PDA: [speaker.simple_info_line()]: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "PDA: [speaker.simple_info_line()]: [html_decode(text)][GLOB.log_end]") /proc/log_chat(text, mob/speaker) if(config.log_pda) - WRITE_LOG(GLOB.world_game_log, "CHAT: [speaker.simple_info_line()] [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "CHAT: [speaker.simple_info_line()] [html_decode(text)][GLOB.log_end]") /proc/log_misc(text) - WRITE_LOG(GLOB.world_game_log, "MISC: [text][log_end]") + WRITE_LOG(GLOB.world_game_log, "MISC: [text][GLOB.log_end]") /proc/log_world(text) SEND_TEXT(world.log, text) if(config && config.log_world_output) - WRITE_LOG(GLOB.world_game_log, "WORLD: [html_decode(text)][log_end]") + WRITE_LOG(GLOB.world_game_log, "WORLD: [html_decode(text)][GLOB.log_end]") /proc/log_runtime_txt(text) // different from /tg/'s log_runtime because our error handler has a log_runtime proc already that does other stuff WRITE_LOG(GLOB.world_runtime_log, text) diff --git a/code/__HELPERS/_string_lists.dm b/code/__HELPERS/_string_lists.dm index 36aa2f9b6fae..e2f0c6081e74 100644 --- a/code/__HELPERS/_string_lists.dm +++ b/code/__HELPERS/_string_lists.dm @@ -2,15 +2,15 @@ #define pick_list_replacements(FILE, KEY) (strings_replacement(FILE, KEY)) #define json_load(FILE) (json_decode(file2text(FILE))) -var/global/list/string_cache -var/global/list/string_filename_current_key +GLOBAL_LIST_EMPTY(string_cache) +GLOBAL_LIST_EMPTY(string_filename_current_key) /proc/strings_replacement(filename, key) load_strings_file(filename) - if((filename in string_cache) && (key in string_cache[filename])) - var/response = pick(string_cache[filename][key]) + 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 @@ -19,23 +19,23 @@ var/global/list/string_filename_current_key /proc/strings(filename as text, key as text) load_strings_file(filename) - if((filename in string_cache) && (key in string_cache[filename])) - return string_cache[filename][key] + if((filename in GLOB.string_cache) && (key in GLOB.string_cache[filename])) + return GLOB.string_cache[filename][key] else CRASH("strings list not found: strings/[filename], index=[key]") /proc/strings_subkey_lookup(match, group1) - return pick_list(string_filename_current_key, group1) + return pick_list(GLOB.string_filename_current_key, group1) /proc/load_strings_file(filename) - string_filename_current_key = filename - if(filename in string_cache) + GLOB.string_filename_current_key = filename + if(filename in GLOB.string_cache) return //no work to do - if(!string_cache) - string_cache = new + if(!GLOB.string_cache) + GLOB.string_cache = new if(fexists("strings/[filename]")) - string_cache[filename] = json_load("strings/[filename]") + GLOB.string_cache[filename] = json_load("strings/[filename]") else CRASH("file not found: strings/[filename]") diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 0db57b27eae8..afb70cb81e6c 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -47,4 +47,4 @@ if(A.plane != B.plane) return A.plane - B.plane else - return A.layer - B.layer \ No newline at end of file + return A.layer - B.layer diff --git a/code/__HELPERS/constants.dm b/code/__HELPERS/constants.dm index 3b801a541e1c..b976a28e1951 100644 --- a/code/__HELPERS/constants.dm +++ b/code/__HELPERS/constants.dm @@ -1,2 +1,2 @@ #define TICKS_IN_DAY 864000 -#define TICKS_IN_SECOND 10 \ No newline at end of file +#define TICKS_IN_SECOND 10 diff --git a/code/__HELPERS/experimental.dm b/code/__HELPERS/experimental.dm index 533f67b68d9c..4f13c07cbd45 100644 --- a/code/__HELPERS/experimental.dm +++ b/code/__HELPERS/experimental.dm @@ -48,4 +48,4 @@ if(A != myArea) myArea = A - . = myArea \ No newline at end of file + . = myArea diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index 27f3537d5213..f8817c2e8e65 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -1,60 +1,60 @@ -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/return_file_text(filename) - if(fexists(filename) == 0) - error("File not found ([filename])") - return - - var/text = file2text(filename) - if(!text) - error("File empty ([filename])") - return - - return text - -//Sends resource files to client cache -/client/proc/getFiles() - for(var/file in args) - src << browse_rsc(file) - -/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) - var/path = root - - for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") - return - - return path - -#define FTPDELAY 200 //200 tick delay to discourage spam -/* This proc is a failsafe to prevent spamming of file requests. - It is just a timer that only permits a download every [FTPDELAY] ticks. - This can be changed by modifying FTPDELAY's value above. - - PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ -/client/proc/file_spam_check() - var/time_to_wait = fileaccess_timer - world.time - if(time_to_wait > 0) - to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") - return 1 - fileaccess_timer = world.time + FTPDELAY - return 0 -#undef FTPDELAY \ No newline at end of file +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/return_file_text(filename) + if(fexists(filename) == 0) + error("File not found ([filename])") + return + + var/text = file2text(filename) + if(!text) + error("File empty ([filename])") + return + + return text + +//Sends resource files to client cache +/client/proc/getFiles() + for(var/file in args) + src << browse_rsc(file) + +/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) + var/path = root + + for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") + return + + return path + +#define FTPDELAY 200 //200 tick delay to discourage spam +/* This proc is a failsafe to prevent spamming of file requests. + It is just a timer that only permits a download every [FTPDELAY] ticks. + This can be changed by modifying FTPDELAY's value above. + + PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ +/client/proc/file_spam_check() + var/time_to_wait = GLOB.fileaccess_timer - world.time + if(time_to_wait > 0) + to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") + return 1 + GLOB.fileaccess_timer = world.time + FTPDELAY + return 0 +#undef FTPDELAY diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 0ab10447f8c0..263f7a9b86fd 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -1,539 +1,539 @@ -/proc/get_area(atom/A) - if(isarea(A)) - return A - var/turf/T = get_turf(A) - return T ? T.loc : null - -/proc/get_area_name(N) //get area by its name - for(var/area/A in world) - if(A.name == N) - return A - return 0 - -/proc/get_location_name(atom/X, format_text = FALSE) - var/area/A = isarea(X) ? X : get_area(X) - if(!A) - return null - return format_text ? format_text(A.name) : A.name - -/proc/get_areas_in_range(dist=0, atom/center=usr) - if(!dist) - var/turf/T = get_turf(center) - return T ? list(T.loc) : list() - if(!center) - return list() - - var/list/turfs = RANGE_TURFS(dist, center) - var/list/areas = list() - for(var/V in turfs) - var/turf/T = V - areas |= T.loc - return areas - -// Like view but bypasses luminosity check - -/proc/hear(var/range, var/atom/source) - var/lum = source.luminosity - source.luminosity = 6 - - var/list/heard = view(range, source) - source.luminosity = lum - - return heard - -/proc/circlerange(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - - //turfs += centerturf - return turfs - -/proc/circleview(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/atoms = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/A in view(radius, centerturf)) - var/dx = A.x - centerturf.x - var/dy = A.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - atoms += A - - //turfs += centerturf - return atoms - -/proc/ff_cansee(atom/A, atom/B) - var/AT = get_turf(A) - var/BT = get_turf(B) - if(AT == BT) - return 1 - var/list/line = getline(A, B) - for(var/turf/T in line) - if(T == AT || T == BT) - break - if(T.density) - return FALSE - return TRUE - -/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) - var/dx = Loc1.x - Loc2.x - var/dy = Loc1.y - Loc2.y - - var/dist = sqrt(dx**2 + dy**2) - - return dist - -/proc/circlerangeturfs(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - -/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in view(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - - - -//var/debug_mob = 0 - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) - - //debug_mob += O.contents.len - if(!recursion_limit) - return L - for(var/atom/A in O.contents) - - if(ismob(A)) - var/mob/M = A - if(client_check && !M.client) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - continue - if(sight_check && !isInSight(A, O)) - continue - L |= M - //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - - else if(include_radio && istype(A, /obj/item/radio)) - if(sight_check && !isInSight(A, O)) - continue - L |= A - - if(isobj(A) || ismob(A)) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - return L - -// The old system would loop through lists for a total of 5000 per function call, in an empty server. -// This new system will loop at around 1000 in an empty server. - -/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) - // Returns a list of mobs in range of R from source. Used in radio and say code. - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/atom/A in range) - if(ismob(A)) - var/mob/M = A - if(M.client || include_clientless) - hear += M - //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - else if(istype(A, /obj/item/radio)) - hear += A - - if(isobj(A) || ismob(A)) - hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) - - return hear - - -/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) - - set background = 1 - - . = list() - // Returns a list of mobs who can hear any of the radios given in @radios - var/list/speaker_coverage = list() - for(var/obj/item/radio/R in radios) - if(R) - //Cyborg checks. Receiving message uses a bit of cyborg's charge. - var/obj/item/radio/borg/BR = R - if(istype(BR) && BR.myborg) - var/mob/living/silicon/robot/borg = BR.myborg - var/datum/robot_component/CO = borg.get_component("radio") - if(!CO) - continue //No radio component (Shouldn't happen) - if(!borg.is_component_functioning("radio")) - continue //No power. - - var/turf/speaker = get_turf(R) - if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = T - - - // Try to find all the players who can hear the message - for(var/A in GLOB.player_list + GLOB.hear_radio_list) - var/mob/M = A - if(M) - var/turf/ear = get_turf(M) - if(ear) - // Ghostship is magic: Ghosts can hear radio chatter from anywhere - if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO))) - . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. - return . - -/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) - var/turf/T - if(X1==X2) - if(Y1==Y2) - return 1 //Light cannot be blocked on same tile - else - var/s = SIMPLE_SIGN(Y2-Y1) - Y1+=s - while(Y1!=Y2) - T=locate(X1,Y1,Z) - if(T.opacity) - return 0 - Y1+=s - else - var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) - var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIMPLE_SIGN(X2-X1) - var/signY = SIMPLE_SIGN(Y2-Y1) - if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) - if(dy > 0) - return get_step(start, SOUTH) - else - return get_step(start, NORTH) - else - if(dx > 0) - return get_step(start, WEST) - else - return get_step(start, EAST) - -/proc/try_move_adjacent(atom/movable/AM) - var/turf/T = get_turf(AM) - for(var/direction in cardinal) - if(AM.Move(get_step(T, direction))) - break - -/proc/get_mob_by_key(var/key) - for(var/mob/M in GLOB.mob_list) - if(M.ckey == lowertext(key)) - return M - return null - -/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) - var/roletext = get_roletext(be_special_type) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < 6000) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) - if(override_age || player_old_enough_antag(G.client,be_special_type)) - candidates += G.client - afk_bracket += 600 // Add a minute to the bracket, for every attempt - - return candidates - -/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) - var/roletext = get_roletext(be_special_type) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < 6000) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) - if(override_age || player_old_enough_antag(G.client,be_special_type)) - candidates += G - afk_bracket += 600 // Add a minute to the bracket, for every attempt - - return candidates - -/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) - if(!isobj(O)) O = new /obj/screen/text() - O.maptext = maptext - O.maptext_height = maptext_height - O.maptext_width = maptext_width - O.screen_loc = screen_loc - return O - -/proc/Show2Group4Delay(obj/O, list/group, delay=0) - if(!isobj(O)) return - if(!group) group = GLOB.clients - for(var/client/C in group) - C.screen += O - if(delay) - spawn(delay) - for(var/client/C in group) - C.screen -= O - -/proc/flick_overlay(image/I, list/show_to, duration) - for(var/client/C in show_to) - C.images += I - spawn(duration) - for(var/client/C in show_to) - C.images -= I - -/proc/get_active_player_count() - // Get active players who are playing in the round - var/active_players = 0 - for(var/i = 1; i <= GLOB.player_list.len; i++) - var/mob/M = GLOB.player_list[i] - if(M && M.client) - if(istype(M, /mob/new_player)) // exclude people in the lobby - continue - else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) - var/mob/dead/observer/O = M - if(O.started_as_observer) // Exclude people who started as observers - continue - active_players++ - return active_players - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - - -/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list) - var/list/mobs_found[0] - var/area/our_area = get_area(the_area) - for(var/mob/M in moblist) - if(client_needed && !M.client) - continue - if(our_area != get_area(M)) - continue - mobs_found += M - return mobs_found - -/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon) - var/area/our_area = get_area(the_area) - for(var/C in GLOB.living_mob_list) - if(!istype(C, check_type)) - continue - if(C == must_be_alone) - continue - if(our_area == get_area(C)) - return 0 - return 1 - - -/proc/GetRedPart(const/hexa) - return hex2num(copytext(hexa, 2, 4)) - -/proc/GetGreenPart(const/hexa) - return hex2num(copytext(hexa, 4, 6)) - -/proc/GetBluePart(const/hexa) - return hex2num(copytext(hexa, 6, 8)) - -/proc/lavaland_equipment_pressure_check(turf/T) - . = FALSE - if(!istype(T)) - return - var/datum/gas_mixture/environment = T.return_air() - if(!istype(environment)) - return - var/pressure = environment.return_pressure() - if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) - . = TRUE - -/proc/GetHexColors(const/hexa) - return list( - GetRedPart(hexa), - GetGreenPart(hexa), - GetBluePart(hexa), - ) - -/proc/MinutesToTicks(var/minutes as num) - return minutes * 60 * 10 - -/proc/SecondsToTicks(var/seconds) - return seconds * 10 - -proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/roletext = be_special_type ? get_roletext(be_special_type) : null - var/list/mob/dead/observer/candidates = list() - var/time_passed = world.time - if(!Question) - Question = "Would you like to be a special role?" - - for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) - if(!G.key || !G.client) - continue - if(be_special_type) - if(!(be_special_type in G.client.prefs.be_special)) - continue - if(antag_age_check) - if(!player_old_enough_antag(G.client, be_special_type)) - continue - if(roletext) - if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) - continue - if(config.use_exp_restrictions && min_hours) - if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) - continue - if(check_antaghud && cannotPossess(G)) - continue - spawn(0) - G << 'sound/misc/notice2.ogg'//Alerting them to their consideration - if(flashwindow) - window_flash(G.client) - var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") - if(!G?.client) - return - switch(ans) - if("Yes") - to_chat(G, "Choice registered: Yes.") - if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. - to_chat(G, "Sorry, you were too late for the consideration!") - G << 'sound/machines/buzz-sigh.ogg' - return - candidates += G - if("No") - to_chat(G, "Choice registered: No.") - return - if("Not This Round") - to_chat(G, "Choice registered: No.") - to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") - G.client.prefs.be_special -= be_special_type - return - else - return - sleep(poll_time) - - //Check all our candidates, to make sure they didn't log off during the 30 second wait period. - for(var/mob/dead/observer/G in candidates) - if(!G.key || !G.client) - candidates.Remove(G) - - return candidates - -/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) - var/list/selected_ghosts = list() - if(!willing_ghosts.len) - return selected_ghosts - - var/list/candidate_ghosts = willing_ghosts.Copy() - - to_chat(adminusr, "Candidate Ghosts:"); - for(var/mob/dead/observer/G in candidate_ghosts) - if(G.key && G.client) - to_chat(adminusr, "- [G] ([G.key])"); - else - candidate_ghosts -= G - - for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--) - var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts - candidate_ghosts -= this_ghost - selected_ghosts += this_ghost - return selected_ghosts - -/proc/window_flash(client/C) - if(ismob(C)) - var/mob/M = C - if(M.client) - C = M.client - if(!C || !C.prefs.windowflashing) - return - winset(C, "mainwindow", "flash=5") +/proc/get_area(atom/A) + if(isarea(A)) + return A + var/turf/T = get_turf(A) + return T ? T.loc : null + +/proc/get_area_name(N) //get area by its name + for(var/area/A in world) + if(A.name == N) + return A + return 0 + +/proc/get_location_name(atom/X, format_text = FALSE) + var/area/A = isarea(X) ? X : get_area(X) + if(!A) + return null + return format_text ? format_text(A.name) : A.name + +/proc/get_areas_in_range(dist=0, atom/center=usr) + if(!dist) + var/turf/T = get_turf(center) + return T ? list(T.loc) : list() + if(!center) + return list() + + var/list/turfs = RANGE_TURFS(dist, center) + var/list/areas = list() + for(var/V in turfs) + var/turf/T = V + areas |= T.loc + return areas + +// Like view but bypasses luminosity check + +/proc/hear(var/range, var/atom/source) + var/lum = source.luminosity + source.luminosity = 6 + + var/list/heard = view(range, source) + source.luminosity = lum + + return heard + +/proc/circlerange(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + + //turfs += centerturf + return turfs + +/proc/circleview(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/atoms = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/A in view(radius, centerturf)) + var/dx = A.x - centerturf.x + var/dy = A.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + atoms += A + + //turfs += centerturf + return atoms + +/proc/ff_cansee(atom/A, atom/B) + var/AT = get_turf(A) + var/BT = get_turf(B) + if(AT == BT) + return 1 + var/list/line = getline(A, B) + for(var/turf/T in line) + if(T == AT || T == BT) + break + if(T.density) + return FALSE + return TRUE + +/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) + var/dx = Loc1.x - Loc2.x + var/dy = Loc1.y - Loc2.y + + var/dist = sqrt(dx**2 + dy**2) + + return dist + +/proc/circlerangeturfs(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + +/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in view(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + + + +//GLOBAL_VAR_INIT(debug_mob, 0) + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) + + //GLOB.debug_mob += O.contents.len + if(!recursion_limit) + return L + for(var/atom/A in O.contents) + + if(ismob(A)) + var/mob/M = A + if(client_check && !M.client) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + continue + if(sight_check && !isInSight(A, O)) + continue + L |= M + //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + + else if(include_radio && istype(A, /obj/item/radio)) + if(sight_check && !isInSight(A, O)) + continue + L |= A + + if(isobj(A) || ismob(A)) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + return L + +// The old system would loop through lists for a total of 5000 per function call, in an empty server. +// This new system will loop at around 1000 in an empty server. + +/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) + // Returns a list of mobs in range of R from source. Used in radio and say code. + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/atom/A in range) + if(ismob(A)) + var/mob/M = A + if(M.client || include_clientless) + hear += M + //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + else if(istype(A, /obj/item/radio)) + hear += A + + if(isobj(A) || ismob(A)) + hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) + + return hear + + +/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) + + set background = 1 + + . = list() + // Returns a list of mobs who can hear any of the radios given in @radios + var/list/speaker_coverage = list() + for(var/obj/item/radio/R in radios) + if(R) + //Cyborg checks. Receiving message uses a bit of cyborg's charge. + var/obj/item/radio/borg/BR = R + if(istype(BR) && BR.myborg) + var/mob/living/silicon/robot/borg = BR.myborg + var/datum/robot_component/CO = borg.get_component("radio") + if(!CO) + continue //No radio component (Shouldn't happen) + if(!borg.is_component_functioning("radio")) + continue //No power. + + var/turf/speaker = get_turf(R) + if(speaker) + for(var/turf/T in hear(R.canhear_range,speaker)) + speaker_coverage[T] = T + + + // Try to find all the players who can hear the message + for(var/A in GLOB.player_list + GLOB.hear_radio_list) + var/mob/M = A + if(M) + var/turf/ear = get_turf(M) + if(ear) + // Ghostship is magic: Ghosts can hear radio chatter from anywhere + if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO))) + . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. + return . + +/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) + var/turf/T + if(X1==X2) + if(Y1==Y2) + return 1 //Light cannot be blocked on same tile + else + var/s = SIMPLE_SIGN(Y2-Y1) + Y1+=s + while(Y1!=Y2) + T=locate(X1,Y1,Z) + if(T.opacity) + return 0 + Y1+=s + else + var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) + var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles + var/signX = SIMPLE_SIGN(X2-X1) + var/signY = SIMPLE_SIGN(Y2-Y1) + if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) + if(dy > 0) + return get_step(start, SOUTH) + else + return get_step(start, NORTH) + else + if(dx > 0) + return get_step(start, WEST) + else + return get_step(start, EAST) + +/proc/try_move_adjacent(atom/movable/AM) + var/turf/T = get_turf(AM) + for(var/direction in GLOB.cardinal) + if(AM.Move(get_step(T, direction))) + break + +/proc/get_mob_by_key(var/key) + for(var/mob/M in GLOB.mob_list) + if(M.ckey == lowertext(key)) + return M + return null + +/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) + var/roletext = get_roletext(be_special_type) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < 6000) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) + if(override_age || player_old_enough_antag(G.client,be_special_type)) + candidates += G.client + afk_bracket += 600 // Add a minute to the bracket, for every attempt + + return candidates + +/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) + var/roletext = get_roletext(be_special_type) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < 6000) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) + if(override_age || player_old_enough_antag(G.client,be_special_type)) + candidates += G + afk_bracket += 600 // Add a minute to the bracket, for every attempt + + return candidates + +/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) + if(!isobj(O)) O = new /obj/screen/text() + O.maptext = maptext + O.maptext_height = maptext_height + O.maptext_width = maptext_width + O.screen_loc = screen_loc + return O + +/proc/Show2Group4Delay(obj/O, list/group, delay=0) + if(!isobj(O)) return + if(!group) group = GLOB.clients + for(var/client/C in group) + C.screen += O + if(delay) + spawn(delay) + for(var/client/C in group) + C.screen -= O + +/proc/flick_overlay(image/I, list/show_to, duration) + for(var/client/C in show_to) + C.images += I + spawn(duration) + for(var/client/C in show_to) + C.images -= I + +/proc/get_active_player_count() + // Get active players who are playing in the round + var/active_players = 0 + for(var/i = 1; i <= GLOB.player_list.len; i++) + var/mob/M = GLOB.player_list[i] + if(M && M.client) + if(istype(M, /mob/new_player)) // exclude people in the lobby + continue + else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) + var/mob/dead/observer/O = M + if(O.started_as_observer) // Exclude people who started as observers + continue + active_players++ + return active_players + +/datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) + + +/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list) + var/list/mobs_found[0] + var/area/our_area = get_area(the_area) + for(var/mob/M in moblist) + if(client_needed && !M.client) + continue + if(our_area != get_area(M)) + continue + mobs_found += M + return mobs_found + +/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon) + var/area/our_area = get_area(the_area) + for(var/C in GLOB.living_mob_list) + if(!istype(C, check_type)) + continue + if(C == must_be_alone) + continue + if(our_area == get_area(C)) + return 0 + return 1 + + +/proc/GetRedPart(const/hexa) + return hex2num(copytext(hexa, 2, 4)) + +/proc/GetGreenPart(const/hexa) + return hex2num(copytext(hexa, 4, 6)) + +/proc/GetBluePart(const/hexa) + return hex2num(copytext(hexa, 6, 8)) + +/proc/lavaland_equipment_pressure_check(turf/T) + . = FALSE + if(!istype(T)) + return + var/datum/gas_mixture/environment = T.return_air() + if(!istype(environment)) + return + var/pressure = environment.return_pressure() + if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) + . = TRUE + +/proc/GetHexColors(const/hexa) + return list( + GetRedPart(hexa), + GetGreenPart(hexa), + GetBluePart(hexa), + ) + +/proc/MinutesToTicks(var/minutes as num) + return minutes * 60 * 10 + +/proc/SecondsToTicks(var/seconds) + return seconds * 10 + +proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) + var/roletext = be_special_type ? get_roletext(be_special_type) : null + var/list/mob/dead/observer/candidates = list() + var/time_passed = world.time + if(!Question) + Question = "Would you like to be a special role?" + + for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) + if(!G.key || !G.client) + continue + if(be_special_type) + if(!(be_special_type in G.client.prefs.be_special)) + continue + if(antag_age_check) + if(!player_old_enough_antag(G.client, be_special_type)) + continue + if(roletext) + if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) + continue + if(config.use_exp_restrictions && min_hours) + if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) + continue + if(check_antaghud && cannotPossess(G)) + continue + spawn(0) + G << 'sound/misc/notice2.ogg'//Alerting them to their consideration + if(flashwindow) + window_flash(G.client) + var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") + if(!G?.client) + return + switch(ans) + if("Yes") + to_chat(G, "Choice registered: Yes.") + if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. + to_chat(G, "Sorry, you were too late for the consideration!") + G << 'sound/machines/buzz-sigh.ogg' + return + candidates += G + if("No") + to_chat(G, "Choice registered: No.") + return + if("Not This Round") + to_chat(G, "Choice registered: No.") + to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") + G.client.prefs.be_special -= be_special_type + return + else + return + sleep(poll_time) + + //Check all our candidates, to make sure they didn't log off during the 30 second wait period. + for(var/mob/dead/observer/G in candidates) + if(!G.key || !G.client) + candidates.Remove(G) + + return candidates + +/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) + var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) + var/list/selected_ghosts = list() + if(!willing_ghosts.len) + return selected_ghosts + + var/list/candidate_ghosts = willing_ghosts.Copy() + + to_chat(adminusr, "Candidate Ghosts:"); + for(var/mob/dead/observer/G in candidate_ghosts) + if(G.key && G.client) + to_chat(adminusr, "- [G] ([G.key])"); + else + candidate_ghosts -= G + + for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--) + var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts + candidate_ghosts -= this_ghost + selected_ghosts += this_ghost + return selected_ghosts + +/proc/window_flash(client/C) + if(ismob(C)) + var/mob/M = C + if(M.client) + C = M.client + if(!C || !C.prefs.windowflashing) + return + winset(C, "mainwindow", "flash=5") diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 2a23cc8baa02..0002bc0d4887 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -1,90 +1,90 @@ - -////////////////////////// -/////Initial Building///// -////////////////////////// - -/proc/makeDatumRefLists() - //markings - init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list) - //head accessory - init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) - //hair - init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) - //facial hair - init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) - //underwear - init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) - //undershirt - init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) - //socks - init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f) - //alt heads - init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list) - - init_subtypes(/datum/surgery_step, GLOB.surgery_steps) - - for(var/path in (subtypesof(/datum/surgery))) - GLOB.surgeries_list += new path() - - init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title") - init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name") - init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name") - - for(var/language_name in GLOB.all_languages) - var/datum/language/L = GLOB.all_languages[language_name] - if(!(L.flags & NONGLOBAL)) - GLOB.language_keys[":[lowertext(L.key)]"] = L - GLOB.language_keys[".[lowertext(L.key)]"] = L - GLOB.language_keys["#[lowertext(L.key)]"] = L - - var/rkey = 0 - for(var/spath in subtypesof(/datum/species)) - var/datum/species/S = new spath() - S.race_key = ++rkey //Used in mob icon caching. - GLOB.all_species[S.name] = S - - if(IS_WHITELISTED in S.species_traits) - GLOB.whitelisted_species += S.name - - init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) - - //Pipe list building - init_subtypes(/datum/pipes, GLOB.construction_pipe_list) - for(var/D in GLOB.construction_pipe_list) - var/datum/pipes/P = D - if(P.rpd_dispensable) - GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy)) - return 1 - -/* // Uncomment to debug chemical reaction list. -/client/verb/debug_chemical_list() - - for(var/reaction in GLOB.chemical_reactions_list) - . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n" - if(islist(GLOB.chemical_reactions_list[reaction])) - var/list/L = GLOB.chemical_reactions_list[reaction] - for(var/t in L) - . += " has: [t]\n" - to_chat(world, .) -*/ - - -//creates every subtype of prototype (excluding prototype) and adds it to list L. -//if no list/L is provided, one is created. -/proc/init_subtypes(prototype, list/L) - if(!istype(L)) L = list() - for(var/path in subtypesof(prototype)) - L += new path() - return L - -/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar) - if(!istype(L)) L = list() - for(var/path in subtypesof(prototype) - pexempt) - var/datum/D = new path() - if(istype(D)) - var/assoc - if(D.vars["[assocvar]"]) //has the var - assoc = D.vars["[assocvar]"] //access value of var - if(assoc) //value gotten - L["[assoc]"] = D //put in association - return L + +////////////////////////// +/////Initial Building///// +////////////////////////// + +/proc/makeDatumRefLists() + //markings + init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list) + //head accessory + init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) + //hair + init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) + //facial hair + init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) + //underwear + init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) + //undershirt + init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) + //socks + init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f) + //alt heads + init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list) + + init_subtypes(/datum/surgery_step, GLOB.surgery_steps) + + for(var/path in (subtypesof(/datum/surgery))) + GLOB.surgeries_list += new path() + + init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title") + init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name") + init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name") + + for(var/language_name in GLOB.all_languages) + var/datum/language/L = GLOB.all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + GLOB.language_keys[":[lowertext(L.key)]"] = L + GLOB.language_keys[".[lowertext(L.key)]"] = L + GLOB.language_keys["#[lowertext(L.key)]"] = L + + var/rkey = 0 + for(var/spath in subtypesof(/datum/species)) + var/datum/species/S = new spath() + S.race_key = ++rkey //Used in mob icon caching. + GLOB.all_species[S.name] = S + + if(IS_WHITELISTED in S.species_traits) + GLOB.whitelisted_species += S.name + + init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) + + //Pipe list building + init_subtypes(/datum/pipes, GLOB.construction_pipe_list) + for(var/D in GLOB.construction_pipe_list) + var/datum/pipes/P = D + if(P.rpd_dispensable) + GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy)) + return 1 + +/* // Uncomment to debug chemical reaction list. +/client/verb/debug_chemical_list() + + for(var/reaction in GLOB.chemical_reactions_list) + . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n" + if(islist(GLOB.chemical_reactions_list[reaction])) + var/list/L = GLOB.chemical_reactions_list[reaction] + for(var/t in L) + . += " has: [t]\n" + to_chat(world, .) +*/ + + +//creates every subtype of prototype (excluding prototype) and adds it to list L. +//if no list/L is provided, one is created. +/proc/init_subtypes(prototype, list/L) + if(!istype(L)) L = list() + for(var/path in subtypesof(prototype)) + L += new path() + return L + +/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar) + if(!istype(L)) L = list() + for(var/path in subtypesof(prototype) - pexempt) + var/datum/D = new path() + if(istype(D)) + var/assoc + if(D.vars["[assocvar]"]) //has the var + assoc = D.vars["[assocvar]"] //access value of var + if(assoc) //value gotten + L["[assoc]"] = D //put in association + return L diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index acf202f0eee9..dc256a934789 100644 --- a/code/__HELPERS/icon_smoothing.dm +++ b/code/__HELPERS/icon_smoothing.dm @@ -66,7 +66,7 @@ if(AM.can_be_unanchored && !AM.anchored) return 0 - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) AM = find_type_in_direction(A, direction) if(AM == NULLTURF_BORDER) if((A.smooth & SMOOTH_BORDER)) diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 0b37872decd5..fe904e6c2eda 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1,963 +1,963 @@ -/* -IconProcs README - -A BYOND library for manipulating icons and colors - -by Lummox JR - -version 1.0 - -The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation -routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. - -CHANGING ICONS - -Several new procs have been added to the /icon datum to simplify working with icons. To use them, -remember you first need to setup an /icon var like so: - -var/icon/my_icon = new('iconfile.dmi') - -icon/ChangeOpacity(amount = 1) - A very common operation in DM is to try to make an icon more or less transparent. Making an icon more - transparent is usually much easier than making it less so, however. This proc basically is a frontend - for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() - can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. - If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. -icon/GrayScale() - Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. -icon/ColorTone(tone) - Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an - RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. - See also the global ColorTone() proc. -icon/MinColors(icon) - The icon is blended with a second icon where the minimum of each RGB pixel is the result. - Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. -icon/MaxColors(icon) - The icon is blended with a second icon where the maximum of each RGB pixel is the result. - Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. -icon/Opaque(background = "#000000") - All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. -icon/BecomeAlphaMask() - You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. - The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. -icon/AddAlphaMask(mask) - The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, - the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. - Where the mask is translucent, the current icon becomes more transparent. -icon/UseAlphaMask(mask, mode) - Sometimes you may want to take the alpha values from one icon and use them on a different icon. - This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change - so it has the same colors as before but uses the mask for opacity. - -COLOR MANAGEMENT AND HSV - -RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. - - * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to - cyan to blue to magenta and back to red. - * The saturation of a color is how much color is in it. A color with low saturation will be more gray, - and with no saturation at all it is a shade of gray. - * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, - and no value at all is black. - -Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three -hex digits because it ranges from 0 to 0x5FF. - - * 0 to 0xFF - red to yellow - * 0x100 to 0x1FF - yellow to green - * 0x200 to 0x2FF - green to cyan - * 0x300 to 0x3FF - cyan to blue - * 0x400 to 0x4FF - blue to magenta - * 0x500 to 0x5FF - magenta to red - -Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), -value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". - -More than one HSV color can match the same RGB color. - -Here are some procs you can use for color management: - -ReadRGB(rgb) - Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used - that includes alpha, the list will have a fourth item for the alpha value. -hsv(hue, sat, val, apha) - Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" - format. Alpha is not included in the result if null. -ReadHSV(rgb) - Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that - includes alpha, the list will have a fourth item for the alpha value. -RGBtoHSV(rgb) - Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". -HSVtoRGB(hsv) - Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". -BlendRGB(rgb1, rgb2, amount) - Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; - if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an RGB or RGBA color. -BlendHSV(hsv1, hsv2, amount) - Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB - blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, - the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an HSV or HSVA color. -BlendRGBasHSV(rgb1, rgb2, amount) - Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. -HueToAngle(hue) - Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. -AngleToHue(hue) - Converts an angle to a hue in the valid range. -RotateHue(hsv, angle) - Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. - (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value - as the original, but a different hue. -GrayScale(rgb) - Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. -ColorTone(rgb, tone) - Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of - using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. -*/ - -/* -Get Flat Icon DEMO by DarkCampainger - -This is a test for the get flat icon proc, modified approprietly for icons and their states. -Probably not a good idea to run this unless you want to see how the proc works in detail. -mob - icon = 'old_or_unused.dmi' - icon_state = "green" - - Login() - // Testing image underlays - underlays += image(icon='old_or_unused.dmi',icon_state="red") - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) - - // Testing image overlays - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) - - // Testing icon file overlays (defaults to mob's state) - overlays += '_flat_demoIcons2.dmi' - - // Testing icon_state overlays (defaults to mob's icon) - overlays += "white" - - // Testing dynamic icon overlays - var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") - I.Shift(NORTH,16,1) - overlays+=I - - // Testing dynamic image overlays - I=image(icon=I,pixel_x = -32, pixel_y = 32) - overlays+=I - - // Testing object types (and layers) - overlays+=/obj/effect/overlayTest - - loc = locate (10,10,1) - verb - Browse_Icon() - set name = "1. Browse Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Send the icon to src's local cache - src<

") - - Output_Icon() - set name = "2. Output Icon" - to_chat(src, "Icon is: [bicon(getFlatIcon(src))]") - - Label_Icon() - set name = "3. Label Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Copy the file to the rsc manually - var/icon/I = fcopy_rsc(getFlatIcon(src)) - // Send the icon to src's local cache - src< transparent, gray -> translucent white, white -> solid white -/icon/proc/BecomeAlphaMask() - SwapColor(null, "#000000ff") // don't let transparent become gray - MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) - -/icon/proc/UseAlphaMask(mask) - Opaque() - AddAlphaMask(mask) - -/icon/proc/AddAlphaMask(mask) - var/icon/M = new(mask) - M.Blend("#ffffff", ICON_SUBTRACT) - // apply mask - Blend(M, ICON_ADD) - -/* - HSV format is represented as "#hhhssvv" or "#hhhssvvaa" - - Hue ranges from 0 to 0x5ff (1535) - - 0x000 = red - 0x100 = yellow - 0x200 = green - 0x300 = cyan - 0x400 = blue - 0x500 = magenta - - Saturation is from 0 to 0xff (255) - - More saturation = more color - Less saturation = more gray - - Value ranges from 0 to 0xff (255) - - Higher value means brighter color - */ - -/proc/ReadRGB(rgb) - if(!rgb) return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(rgb) == 35) ++start // skip opening # - var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(rgb), ++i) - ch = text2ascii(rgb, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break - ++digits - if(digits == 8) break - - var/single = digits < 6 - if(digits != 3 && digits != 4 && digits != 6 && digits != 8) return - if(digits == 4 || digits == 8) usealpha = 1 - for(i=start, digits>0, ++i) - ch = text2ascii(rgb, i) - if(ch >= 48 && ch <= 57) ch -= 48 - else if(ch >= 65 && ch <= 70) ch -= 55 - else if(ch >= 97 && ch <= 102) ch -= 87 - else break - --digits - switch(which) - if(0) - r = (r << 4) | ch - if(single) - r |= r << 4 - ++which - else if(!(digits & 1)) ++which - if(1) - g = (g << 4) | ch - if(single) - g |= g << 4 - ++which - else if(!(digits & 1)) ++which - if(2) - b = (b << 4) | ch - if(single) - b |= b << 4 - ++which - else if(!(digits & 1)) ++which - if(3) - alpha = (alpha << 4) | ch - if(single) alpha |= alpha << 4 - - . = list(r, g, b) - if(usealpha) . += alpha - -/proc/ReadHSV(hsv) - if(!hsv) return - - // interpret the HSV or HSVA value - var/i=1,start=1 - if(text2ascii(hsv) == 35) ++start // skip opening # - var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha - var/digits=0 - for(i=start, i<=length(hsv), ++i) - ch = text2ascii(hsv, i) - if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break - ++digits - if(digits == 9) break - if(digits > 7) usealpha = 1 - if(digits <= 4) ++which - if(digits <= 2) ++which - for(i=start, digits>0, ++i) - ch = text2ascii(hsv, i) - if(ch >= 48 && ch <= 57) ch -= 48 - else if(ch >= 65 && ch <= 70) ch -= 55 - else if(ch >= 97 && ch <= 102) ch -= 87 - else break - --digits - switch(which) - if(0) - hue = (hue << 4) | ch - if(digits == (usealpha ? 6 : 4)) ++which - if(1) - sat = (sat << 4) | ch - if(digits == (usealpha ? 4 : 2)) ++which - if(2) - val = (val << 4) | ch - if(digits == (usealpha ? 2 : 0)) ++which - if(3) - alpha = (alpha << 4) | ch - - . = list(hue, sat, val) - if(usealpha) . += alpha - -/proc/HSVtoRGB(hsv) - if(!hsv) return "#000000" - var/list/HSV = ReadHSV(hsv) - if(!HSV) return "#000000" - - var/hue = HSV[1] - var/sat = HSV[2] - var/val = HSV[3] - - // Compress hue into easier-to-manage range - hue -= hue >> 8 - if(hue >= 0x5fa) hue -= 0x5fa - - var/hi,mid,lo,r,g,b - hi = val - lo = round((255 - sat) * val / 255, 1) - mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) - if(hue >= 765) - if(hue >= 1275) {r=hi; g=lo; b=mid} - else if(hue >= 1020) {r=mid; g=lo; b=hi } - else {r=lo; g=mid; b=hi } - else - if(hue >= 510) {r=lo; g=hi; b=mid} - else if(hue >= 255) {r=mid; g=hi; b=lo } - else {r=hi; g=mid; b=lo } - - return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) - -/proc/RGBtoHSV(rgb) - if(!rgb) return "#0000000" - var/list/RGB = ReadRGB(rgb) - if(!RGB) return "#0000000" - - var/r = RGB[1] - var/g = RGB[2] - var/b = RGB[3] - var/hi = max(r,g,b) - var/lo = min(r,g,b) - - var/val = hi - var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 - var/hue = 0 - - if(sat) - var/dir - var/mid - if(hi == r) - if(lo == b) {hue=0; dir=1; mid=g} - else {hue=1535; dir=-1; mid=b} - else if(hi == g) - if(lo == r) {hue=512; dir=1; mid=b} - else {hue=511; dir=-1; mid=r} - else if(hi == b) - if(lo == g) {hue=1024; dir=1; mid=r} - else {hue=1023; dir=-1; mid=g} - hue += dir * round((mid-lo) * 255 / (hi-lo), 1) - - return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) - -/proc/hsv(hue, sat, val, alpha) - if(hue < 0 || hue >= 1536) hue %= 1536 - if(hue < 0) hue += 1536 - if((hue & 0xFF) == 0xFF) - ++hue - if(hue >= 1536) hue = 0 - if(sat < 0) sat = 0 - if(sat > 255) sat = 255 - if(val < 0) val = 0 - if(val > 255) val = 255 - . = "#" - . += TO_HEX_DIGIT(hue >> 8) - . += TO_HEX_DIGIT(hue >> 4) - . += TO_HEX_DIGIT(hue) - . += TO_HEX_DIGIT(sat >> 4) - . += TO_HEX_DIGIT(sat) - . += TO_HEX_DIGIT(val >> 4) - . += TO_HEX_DIGIT(val) - if(!isnull(alpha)) - if(alpha < 0) alpha = 0 - if(alpha > 255) alpha = 255 - . += TO_HEX_DIGIT(alpha >> 4) - . += TO_HEX_DIGIT(alpha) - -/* - Smooth blend between HSV colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed - */ -/proc/BlendHSV(hsv1, hsv2, amount) - var/list/HSV1 = ReadHSV(hsv1) - var/list/HSV2 = ReadHSV(hsv2) - - // add missing alpha if needed - if(HSV1.len < HSV2.len) HSV1 += 255 - else if(HSV2.len < HSV1.len) HSV2 += 255 - var/usealpha = HSV1.len > 3 - - // normalize hsv values in case anything is screwy - if(HSV1[1] > 1536) HSV1[1] %= 1536 - if(HSV2[1] > 1536) HSV2[1] %= 1536 - if(HSV1[1] < 0) HSV1[1] += 1536 - if(HSV2[1] < 0) HSV2[1] += 1536 - if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} - if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} - - // no value for one color means don't change saturation - if(!HSV1[3]) HSV1[2] = HSV2[2] - if(!HSV2[3]) HSV2[2] = HSV1[2] - // no saturation for one color means don't change hues - if(!HSV1[2]) HSV1[1] = HSV2[1] - if(!HSV2[2]) HSV2[1] = HSV1[1] - - // Compress hues into easier-to-manage range - HSV1[1] -= HSV1[1] >> 8 - HSV2[1] -= HSV2[1] >> 8 - - var/hue_diff = HSV2[1] - HSV1[1] - if(hue_diff > 765) hue_diff -= 1530 - else if(hue_diff <= -765) hue_diff += 1530 - - var/hue = round(HSV1[1] + hue_diff * amount, 1) - var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) - var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) - var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null - - // normalize hue - if(hue < 0 || hue >= 1530) hue %= 1530 - if(hue < 0) hue += 1530 - // decompress hue - hue += round(hue / 255) - - return hsv(hue, sat, val, alpha) - -/* - Smooth blend between RGB colors - - amount=0 is the first color - amount=1 is the second color - amount=0.5 is directly between the two colors - - amount<0 or amount>1 are allowed - */ -/proc/BlendRGB(rgb1, rgb2, amount) - var/list/RGB1 = ReadRGB(rgb1) - var/list/RGB2 = ReadRGB(rgb2) - - // add missing alpha if needed - if(RGB1.len < RGB2.len) RGB1 += 255 - else if(RGB2.len < RGB1.len) RGB2 += 255 - var/usealpha = RGB1.len > 3 - - var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) - var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) - var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) - var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null - - return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) - -/proc/BlendRGBasHSV(rgb1, rgb2, amount) - return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) - -/proc/HueToAngle(hue) - // normalize hsv in case anything is screwy - if(hue < 0 || hue >= 1536) hue %= 1536 - if(hue < 0) hue += 1536 - // Compress hue into easier-to-manage range - hue -= hue >> 8 - return hue / (1530/360) - -/proc/AngleToHue(angle) - // normalize hsv in case anything is screwy - if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) - var/hue = angle * (1530/360) - // Decompress hue - hue += round(hue / 255) - return hue - - -// positive angle rotates forward through red->green->blue -/proc/RotateHue(hsv, angle) - var/list/HSV = ReadHSV(hsv) - - // normalize hsv in case anything is screwy - if(HSV[1] >= 1536) HSV[1] %= 1536 - if(HSV[1] < 0) HSV[1] += 1536 - - // Compress hue into easier-to-manage range - HSV[1] -= HSV[1] >> 8 - - if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) - HSV[1] = round(HSV[1] + angle * (1530/360), 1) - - // normalize hue - if(HSV[1] < 0 || HSV[1] >= 1530) HSV[1] %= 1530 - if(HSV[1] < 0) HSV[1] += 1530 - // decompress hue - HSV[1] += round(HSV[1] / 255) - - return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) - -// Convert an rgb color to grayscale -/proc/GrayScale(rgb) - var/list/RGB = ReadRGB(rgb) - var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 - return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) - -// Change grayscale color to black->tone->white range -/proc/ColorTone(rgb, tone) - var/list/RGB = ReadRGB(rgb) - var/list/TONE = ReadRGB(tone) - - var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 - var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 - - if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1)) - else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1)) - - -/* -Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays -as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. -The _flatIcons list is a cache for generated icon files. -*/ - -// Creates a single icon from a given /atom or /image. Only the first argument is required. -/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) - //Define... defines. - var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") - - #define BLANK icon(flat_template) - #define SET_SELF(SETVAR) do { \ - var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ - if(A.alpha<255) { \ - SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ - } \ - if(A.color) { \ - if(islist(A.color)){ \ - SELF_ICON.MapColors(arglist(A.color))} \ - else{ \ - SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ - } \ - ##SETVAR=SELF_ICON;\ - } while (0) - #define INDEX_X_LOW 1 - #define INDEX_X_HIGH 2 - #define INDEX_Y_LOW 3 - #define INDEX_Y_HIGH 4 - - #define flatX1 flat_size[INDEX_X_LOW] - #define flatX2 flat_size[INDEX_X_HIGH] - #define flatY1 flat_size[INDEX_Y_LOW] - #define flatY2 flat_size[INDEX_Y_HIGH] - #define addX1 add_size[INDEX_X_LOW] - #define addX2 add_size[INDEX_X_HIGH] - #define addY1 add_size[INDEX_Y_LOW] - #define addY2 add_size[INDEX_Y_HIGH] - - if(!A || A.alpha <= 0) - return BLANK - - var/noIcon = FALSE - if(start) - if(!defdir) - defdir = A.dir - if(!deficon) - deficon = A.icon - if(!defstate) - defstate = A.icon_state - if(!defblend) - defblend = A.blend_mode - - var/curicon = A.icon || deficon - var/curstate = A.icon_state || defstate - - if(!((noIcon = (!curicon)))) - var/curstates = icon_states(curicon) - if(!(curstate in curstates)) - if("" in curstates) - curstate = "" - else - noIcon = TRUE // Do not render this object. - - var/curdir - var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have - - //These should use the parent's direction (most likely) - if(!A.dir || A.dir == SOUTH) - curdir = defdir - else - curdir = A.dir - - //Try to remove/optimize this section ASAP, CPU hog. - //Determines if there's directionals. - if(!noIcon && curdir != SOUTH) - var/exist = FALSE - var/static/list/checkdirs = list(NORTH, EAST, WEST) - for(var/i in checkdirs) //Not using GLOB for a reason. - if(length(icon_states(icon(curicon, curstate, i)))) - exist = TRUE - break - if(!exist) - base_icon_dir = SOUTH - // - - if(!base_icon_dir) - base_icon_dir = curdir - - ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. - - var/curblend = A.blend_mode || defblend - - if(A.overlays.len || A.underlays.len) - var/icon/flat = BLANK - // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed - var/list/layers = list() - var/image/copy - // Add the atom's icon itself, without pixel_x/y offsets. - if(!noIcon) - copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) - copy.color = A.color - copy.alpha = A.alpha - copy.blend_mode = curblend - layers[copy] = A.layer - - // Loop through the underlays, then overlays, sorting them into the layers list - for(var/process_set in 0 to 1) - var/list/process = process_set? A.overlays : A.underlays - for(var/i in 1 to process.len) - var/image/current = process[i] - if(!current) - continue - if(current.plane != FLOAT_PLANE && current.plane != A.plane) - continue - var/current_layer = current.layer - if(current_layer < 0) - if(current_layer <= -1000) - return flat - current_layer = process_set + A.layer + current_layer / 1000 - - for(var/p in 1 to layers.len) - var/image/cmp = layers[p] - if(current_layer < layers[cmp]) - layers.Insert(p, current) - break - layers[current] = current_layer - - //sortTim(layers, /proc/cmp_image_layer_asc) - - var/icon/add // Icon of overlay being added - - // Current dimensions of flattened icon - var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) - // Dimensions of overlay being added - var/list/add_size[4] - - for(var/V in layers) - var/image/I = V - if(I.alpha == 0) - continue - - if(I == copy) // 'I' is an /image based on the object being flattened. - curblend = BLEND_OVERLAY - add = icon(I.icon, I.icon_state, base_icon_dir) - else // 'I' is an appearance object. - add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) - if(!add) - continue - // Find the new dimensions of the flat icon to fit the added overlay - add_size = list( - min(flatX1, I.pixel_x+1), - max(flatX2, I.pixel_x+add.Width()), - min(flatY1, I.pixel_y+1), - max(flatY2, I.pixel_y+add.Height()) - ) - - if(flat_size ~! add_size) - // Resize the flattened icon so the new icon fits - flat.Crop( - addX1 - flatX1 + 1, - addY1 - flatY1 + 1, - addX2 - flatX1 + 1, - addY2 - flatY1 + 1 - ) - flat_size = add_size.Copy() - - // Blend the overlay into the flattened icon - flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) - - if(A.color) - if(islist(A.color)) - flat.MapColors(arglist(A.color)) - else - flat.Blend(A.color, ICON_MULTIPLY) - - if(A.alpha < 255) - flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) - - if(no_anim) - //Clean up repeated frames - var/icon/cleaned = new /icon() - cleaned.Insert(flat, "", SOUTH, 1, 0) - . = cleaned - else - . = icon(flat, "", SOUTH) - else //There's no overlays. - if(!noIcon) - SET_SELF(.) - - //Clear defines - #undef flatX1 - #undef flatX2 - #undef flatY1 - #undef flatY2 - #undef addX1 - #undef addX2 - #undef addY1 - #undef addY2 - - #undef INDEX_X_LOW - #undef INDEX_X_HIGH - #undef INDEX_Y_LOW - #undef INDEX_Y_HIGH - - #undef BLANK - #undef SET_SELF - -/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N - var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. - for(var/V in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. - var/image/I = V - if(I.layer>A.layer) - continue//If layer is greater than what we need, skip it. - var/icon/image_overlay = new(I.icon,I.icon_state)//Blend only works with icon objects. - //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. - alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. - return alpha_mask//And now return the mask. - -/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. - var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. - //Now we need to culculate overlays+underlays and add them together to form an image for a mask. - //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. - var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. - opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. - opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. - for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. - var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. - switch(i)//Now to determine offset so the result is somewhat blurred. - if(1) I.pixel_x-- - if(2) I.pixel_x++ - if(3) I.pixel_y-- - if(4) I.pixel_y++ - overlays += I//And finally add the overlay. - -/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created. - var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. - var/icon/alpha_mask - flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. - flat_icon.ChangeOpacity(0.5)//Make it half transparent. - if(A.Height() == 64) - alpha_mask = new('icons/mob/ancient_machine.dmi', "scanline2")//Scaline for tall icons. - else - alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. - flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. - return flat_icon - -//For photo camera. -/proc/build_composite_icon(atom/A) - var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) - for(var/O in A.overlays) - var/image/I = O - composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) - return composite - -/proc/adjust_brightness(var/color, var/value) - if(!color) return "#FFFFFF" - if(!value) return color - - var/list/RGB = ReadRGB(color) - RGB[1] = Clamp(RGB[1]+value,0,255) - RGB[2] = Clamp(RGB[2]+value,0,255) - RGB[3] = Clamp(RGB[3]+value,0,255) - return rgb(RGB[1],RGB[2],RGB[3]) - -/proc/sort_atoms_by_layer(var/list/atoms) - // Comb sort icons based on levels - var/list/result = atoms.Copy() - var/gap = result.len - var/swapped = 1 - while(gap > 1 || swapped) - swapped = 0 - if(gap > 1) - gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient - if(gap < 1) - gap = 1 - for(var/i = 1; gap + i <= result.len; i++) - var/atom/l = result[i] //Fucking hate - var/atom/r = result[gap+i] //how lists work here - if(l.layer > r.layer) //no "result[i].layer" for me - result.Swap(i, gap + i) - swapped = 1 - return result - -//Interface for using DrawBox() to draw 1 pixel on a coordinate. -//Returns the same icon specifed in the argument, but with the pixel drawn -/proc/DrawPixel(var/icon/I,var/colour,var/drawX,var/drawY) - if(!I) - return 0 - var/Iwidth = I.Width() - var/Iheight = I.Height() - if(drawX > Iwidth || drawX <= 0) - return 0 - if(drawY > Iheight || drawY <= 0) - return 0 - I.DrawBox(colour,drawX, drawY) - return I - -//Interface for easy drawing of one pixel on an atom. -/atom/proc/DrawPixelOn(var/colour, var/drawX, var/drawY) - var/icon/I = new(icon) - var/icon/J = DrawPixel(I, colour, drawX, drawY) - if(J) //Only set the icon if it succeeded, the icon without the pixel is 1000x better than a black square. - icon = J - return J - return 0 - -//Hook, override to run code on- wait this is images -//Images have dir without being an atom, so they get their own definition. -//Lame. -/image/proc/setDir(newdir) - dir = newdir - -/proc/rand_hex_color() - var/list/colors = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f") - var/color="" - for(var/i=0;i<6;i++) - color = color+pick(colors) - return "#[color]" - -//Imagine removing pixels from the main icon that are covered by pixels from the mask icon. -//Standard behaviour is to cut pixels from the main icon that are covered by pixels from the mask icon unless passed mask_ready, see below. -/proc/get_icon_difference(var/icon/main, var/icon/mask, var/mask_ready) - /*You should skip prep if the mask is already sprited properly. This significantly improves performance by eliminating most of the realtime icon work. - e.g. A 'ready' mask is a mask where the part you want cut out is missing (no pixels, 0 alpha) from the sprite, and everything else is solid white.*/ - - if(istype(main) && istype(mask)) - if(!mask_ready) //Prep the mask if we're using a regular old sprite and not a special-made mask. - mask.Blend(rgb(255,255,255), ICON_SUBTRACT) //Make all pixels on the mask as black as possible. - mask.Opaque(rgb(255,255,255)) //Make the transparent pixels (background) white. - mask.BecomeAlphaMask() //Make all the black pixels vanish (fully transparent), leaving only the white background pixels. - - main.AddAlphaMask(mask) //Make the pixels in the main icon that are in the transparent zone of the mask icon also vanish (fully transparent). - return main \ No newline at end of file +/* +IconProcs README + +A BYOND library for manipulating icons and colors + +by Lummox JR + +version 1.0 + +The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation +routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. + +CHANGING ICONS + +Several new procs have been added to the /icon datum to simplify working with icons. To use them, +remember you first need to setup an /icon var like so: + + var/icon/my_icon = new('iconfile.dmi') + +icon/ChangeOpacity(amount = 1) + A very common operation in DM is to try to make an icon more or less transparent. Making an icon more + transparent is usually much easier than making it less so, however. This proc basically is a frontend + for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() + can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. + If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. +icon/GrayScale() + Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. +icon/ColorTone(tone) + Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an + RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. + See also the global ColorTone() proc. +icon/MinColors(icon) + The icon is blended with a second icon where the minimum of each RGB pixel is the result. + Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. +icon/MaxColors(icon) + The icon is blended with a second icon where the maximum of each RGB pixel is the result. + Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. +icon/Opaque(background = "#000000") + All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. +icon/BecomeAlphaMask() + You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. + The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. +icon/AddAlphaMask(mask) + The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, + the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. + Where the mask is translucent, the current icon becomes more transparent. +icon/UseAlphaMask(mask, mode) + Sometimes you may want to take the alpha values from one icon and use them on a different icon. + This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change + so it has the same colors as before but uses the mask for opacity. + +COLOR MANAGEMENT AND HSV + +RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. + + * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to + cyan to blue to magenta and back to red. + * The saturation of a color is how much color is in it. A color with low saturation will be more gray, + and with no saturation at all it is a shade of gray. + * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, + and no value at all is black. + +Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three +hex digits because it ranges from 0 to 0x5FF. + + * 0 to 0xFF - red to yellow + * 0x100 to 0x1FF - yellow to green + * 0x200 to 0x2FF - green to cyan + * 0x300 to 0x3FF - cyan to blue + * 0x400 to 0x4FF - blue to magenta + * 0x500 to 0x5FF - magenta to red + +Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), +value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". + +More than one HSV color can match the same RGB color. + +Here are some procs you can use for color management: + +ReadRGB(rgb) + Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used + that includes alpha, the list will have a fourth item for the alpha value. +hsv(hue, sat, val, apha) + Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" + format. Alpha is not included in the result if null. +ReadHSV(rgb) + Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that + includes alpha, the list will have a fourth item for the alpha value. +RGBtoHSV(rgb) + Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". +HSVtoRGB(hsv) + Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". +BlendRGB(rgb1, rgb2, amount) + Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; + if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. + The returned value is an RGB or RGBA color. +BlendHSV(hsv1, hsv2, amount) + Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB + blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, + the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. + The returned value is an HSV or HSVA color. +BlendRGBasHSV(rgb1, rgb2, amount) + Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. +HueToAngle(hue) + Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. +AngleToHue(hue) + Converts an angle to a hue in the valid range. +RotateHue(hsv, angle) + Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. + (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value + as the original, but a different hue. +GrayScale(rgb) + Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. +ColorTone(rgb, tone) + Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of + using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. +*/ + +/* +Get Flat Icon DEMO by DarkCampainger + +This is a test for the get flat icon proc, modified approprietly for icons and their states. +Probably not a good idea to run this unless you want to see how the proc works in detail. +mob + icon = 'old_or_unused.dmi' + icon_state = "green" + + Login() + // Testing image underlays + underlays += image(icon='old_or_unused.dmi',icon_state="red") + underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) + underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) + + // Testing image overlays + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) + overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) + + // Testing icon file overlays (defaults to mob's state) + overlays += '_flat_demoIcons2.dmi' + + // Testing icon_state overlays (defaults to mob's icon) + overlays += "white" + + // Testing dynamic icon overlays + var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") + I.Shift(NORTH,16,1) + overlays+=I + + // Testing dynamic image overlays + I=image(icon=I,pixel_x = -32, pixel_y = 32) + overlays+=I + + // Testing object types (and layers) + overlays+=/obj/effect/overlayTest + + loc = locate (10,10,1) + verb + Browse_Icon() + set name = "1. Browse Icon" + // Give it a name for the cache + var/iconName = "[ckey(src.name)]_flattened.dmi" + // Send the icon to src's local cache + src<

") + + Output_Icon() + set name = "2. Output Icon" + to_chat(src, "Icon is: [bicon(getFlatIcon(src))]") + + Label_Icon() + set name = "3. Label Icon" + // Give it a name for the cache + var/iconName = "[ckey(src.name)]_flattened.dmi" + // Copy the file to the rsc manually + var/icon/I = fcopy_rsc(getFlatIcon(src)) + // Send the icon to src's local cache + src< transparent, gray -> translucent white, white -> solid white +/icon/proc/BecomeAlphaMask() + SwapColor(null, "#000000ff") // don't let transparent become gray + MapColors(0,0,0,0.3, 0,0,0,0.59, 0,0,0,0.11, 0,0,0,0, 1,1,1,0) + +/icon/proc/UseAlphaMask(mask) + Opaque() + AddAlphaMask(mask) + +/icon/proc/AddAlphaMask(mask) + var/icon/M = new(mask) + M.Blend("#ffffff", ICON_SUBTRACT) + // apply mask + Blend(M, ICON_ADD) + +/* + HSV format is represented as "#hhhssvv" or "#hhhssvvaa" + + Hue ranges from 0 to 0x5ff (1535) + + 0x000 = red + 0x100 = yellow + 0x200 = green + 0x300 = cyan + 0x400 = blue + 0x500 = magenta + + Saturation is from 0 to 0xff (255) + + More saturation = more color + Less saturation = more gray + + Value ranges from 0 to 0xff (255) + + Higher value means brighter color + */ + +/proc/ReadRGB(rgb) + if(!rgb) return + + // interpret the HSV or HSVA value + var/i=1,start=1 + if(text2ascii(rgb) == 35) ++start // skip opening # + var/ch,which=0,r=0,g=0,b=0,alpha=0,usealpha + var/digits=0 + for(i=start, i<=length(rgb), ++i) + ch = text2ascii(rgb, i) + if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break + ++digits + if(digits == 8) break + + var/single = digits < 6 + if(digits != 3 && digits != 4 && digits != 6 && digits != 8) return + if(digits == 4 || digits == 8) usealpha = 1 + for(i=start, digits>0, ++i) + ch = text2ascii(rgb, i) + if(ch >= 48 && ch <= 57) ch -= 48 + else if(ch >= 65 && ch <= 70) ch -= 55 + else if(ch >= 97 && ch <= 102) ch -= 87 + else break + --digits + switch(which) + if(0) + r = (r << 4) | ch + if(single) + r |= r << 4 + ++which + else if(!(digits & 1)) ++which + if(1) + g = (g << 4) | ch + if(single) + g |= g << 4 + ++which + else if(!(digits & 1)) ++which + if(2) + b = (b << 4) | ch + if(single) + b |= b << 4 + ++which + else if(!(digits & 1)) ++which + if(3) + alpha = (alpha << 4) | ch + if(single) alpha |= alpha << 4 + + . = list(r, g, b) + if(usealpha) . += alpha + +/proc/ReadHSV(hsv) + if(!hsv) return + + // interpret the HSV or HSVA value + var/i=1,start=1 + if(text2ascii(hsv) == 35) ++start // skip opening # + var/ch,which=0,hue=0,sat=0,val=0,alpha=0,usealpha + var/digits=0 + for(i=start, i<=length(hsv), ++i) + ch = text2ascii(hsv, i) + if(ch < 48 || (ch > 57 && ch < 65) || (ch > 70 && ch < 97) || ch > 102) break + ++digits + if(digits == 9) break + if(digits > 7) usealpha = 1 + if(digits <= 4) ++which + if(digits <= 2) ++which + for(i=start, digits>0, ++i) + ch = text2ascii(hsv, i) + if(ch >= 48 && ch <= 57) ch -= 48 + else if(ch >= 65 && ch <= 70) ch -= 55 + else if(ch >= 97 && ch <= 102) ch -= 87 + else break + --digits + switch(which) + if(0) + hue = (hue << 4) | ch + if(digits == (usealpha ? 6 : 4)) ++which + if(1) + sat = (sat << 4) | ch + if(digits == (usealpha ? 4 : 2)) ++which + if(2) + val = (val << 4) | ch + if(digits == (usealpha ? 2 : 0)) ++which + if(3) + alpha = (alpha << 4) | ch + + . = list(hue, sat, val) + if(usealpha) . += alpha + +/proc/HSVtoRGB(hsv) + if(!hsv) return "#000000" + var/list/HSV = ReadHSV(hsv) + if(!HSV) return "#000000" + + var/hue = HSV[1] + var/sat = HSV[2] + var/val = HSV[3] + + // Compress hue into easier-to-manage range + hue -= hue >> 8 + if(hue >= 0x5fa) hue -= 0x5fa + + var/hi,mid,lo,r,g,b + hi = val + lo = round((255 - sat) * val / 255, 1) + mid = lo + round(abs(round(hue, 510) - hue) * (hi - lo) / 255, 1) + if(hue >= 765) + if(hue >= 1275) {r=hi; g=lo; b=mid} + else if(hue >= 1020) {r=mid; g=lo; b=hi } + else {r=lo; g=mid; b=hi } + else + if(hue >= 510) {r=lo; g=hi; b=mid} + else if(hue >= 255) {r=mid; g=hi; b=lo } + else {r=hi; g=mid; b=lo } + + return (HSV.len > 3) ? rgb(r,g,b,HSV[4]) : rgb(r,g,b) + +/proc/RGBtoHSV(rgb) + if(!rgb) return "#0000000" + var/list/RGB = ReadRGB(rgb) + if(!RGB) return "#0000000" + + var/r = RGB[1] + var/g = RGB[2] + var/b = RGB[3] + var/hi = max(r,g,b) + var/lo = min(r,g,b) + + var/val = hi + var/sat = hi ? round((hi-lo) * 255 / hi, 1) : 0 + var/hue = 0 + + if(sat) + var/dir + var/mid + if(hi == r) + if(lo == b) {hue=0; dir=1; mid=g} + else {hue=1535; dir=-1; mid=b} + else if(hi == g) + if(lo == r) {hue=512; dir=1; mid=b} + else {hue=511; dir=-1; mid=r} + else if(hi == b) + if(lo == g) {hue=1024; dir=1; mid=r} + else {hue=1023; dir=-1; mid=g} + hue += dir * round((mid-lo) * 255 / (hi-lo), 1) + + return hsv(hue, sat, val, (RGB.len>3 ? RGB[4] : null)) + +/proc/hsv(hue, sat, val, alpha) + if(hue < 0 || hue >= 1536) hue %= 1536 + if(hue < 0) hue += 1536 + if((hue & 0xFF) == 0xFF) + ++hue + if(hue >= 1536) hue = 0 + if(sat < 0) sat = 0 + if(sat > 255) sat = 255 + if(val < 0) val = 0 + if(val > 255) val = 255 + . = "#" + . += TO_HEX_DIGIT(hue >> 8) + . += TO_HEX_DIGIT(hue >> 4) + . += TO_HEX_DIGIT(hue) + . += TO_HEX_DIGIT(sat >> 4) + . += TO_HEX_DIGIT(sat) + . += TO_HEX_DIGIT(val >> 4) + . += TO_HEX_DIGIT(val) + if(!isnull(alpha)) + if(alpha < 0) alpha = 0 + if(alpha > 255) alpha = 255 + . += TO_HEX_DIGIT(alpha >> 4) + . += TO_HEX_DIGIT(alpha) + +/* + Smooth blend between HSV colors + + amount=0 is the first color + amount=1 is the second color + amount=0.5 is directly between the two colors + + amount<0 or amount>1 are allowed + */ +/proc/BlendHSV(hsv1, hsv2, amount) + var/list/HSV1 = ReadHSV(hsv1) + var/list/HSV2 = ReadHSV(hsv2) + + // add missing alpha if needed + if(HSV1.len < HSV2.len) HSV1 += 255 + else if(HSV2.len < HSV1.len) HSV2 += 255 + var/usealpha = HSV1.len > 3 + + // normalize hsv values in case anything is screwy + if(HSV1[1] > 1536) HSV1[1] %= 1536 + if(HSV2[1] > 1536) HSV2[1] %= 1536 + if(HSV1[1] < 0) HSV1[1] += 1536 + if(HSV2[1] < 0) HSV2[1] += 1536 + if(!HSV1[3]) {HSV1[1] = 0; HSV1[2] = 0} + if(!HSV2[3]) {HSV2[1] = 0; HSV2[2] = 0} + + // no value for one color means don't change saturation + if(!HSV1[3]) HSV1[2] = HSV2[2] + if(!HSV2[3]) HSV2[2] = HSV1[2] + // no saturation for one color means don't change hues + if(!HSV1[2]) HSV1[1] = HSV2[1] + if(!HSV2[2]) HSV2[1] = HSV1[1] + + // Compress hues into easier-to-manage range + HSV1[1] -= HSV1[1] >> 8 + HSV2[1] -= HSV2[1] >> 8 + + var/hue_diff = HSV2[1] - HSV1[1] + if(hue_diff > 765) hue_diff -= 1530 + else if(hue_diff <= -765) hue_diff += 1530 + + var/hue = round(HSV1[1] + hue_diff * amount, 1) + var/sat = round(HSV1[2] + (HSV2[2] - HSV1[2]) * amount, 1) + var/val = round(HSV1[3] + (HSV2[3] - HSV1[3]) * amount, 1) + var/alpha = usealpha ? round(HSV1[4] + (HSV2[4] - HSV1[4]) * amount, 1) : null + + // normalize hue + if(hue < 0 || hue >= 1530) hue %= 1530 + if(hue < 0) hue += 1530 + // decompress hue + hue += round(hue / 255) + + return hsv(hue, sat, val, alpha) + +/* + Smooth blend between RGB colors + + amount=0 is the first color + amount=1 is the second color + amount=0.5 is directly between the two colors + + amount<0 or amount>1 are allowed + */ +/proc/BlendRGB(rgb1, rgb2, amount) + var/list/RGB1 = ReadRGB(rgb1) + var/list/RGB2 = ReadRGB(rgb2) + + // add missing alpha if needed + if(RGB1.len < RGB2.len) RGB1 += 255 + else if(RGB2.len < RGB1.len) RGB2 += 255 + var/usealpha = RGB1.len > 3 + + var/r = round(RGB1[1] + (RGB2[1] - RGB1[1]) * amount, 1) + var/g = round(RGB1[2] + (RGB2[2] - RGB1[2]) * amount, 1) + var/b = round(RGB1[3] + (RGB2[3] - RGB1[3]) * amount, 1) + var/alpha = usealpha ? round(RGB1[4] + (RGB2[4] - RGB1[4]) * amount, 1) : null + + return isnull(alpha) ? rgb(r, g, b) : rgb(r, g, b, alpha) + +/proc/BlendRGBasHSV(rgb1, rgb2, amount) + return HSVtoRGB(RGBtoHSV(rgb1), RGBtoHSV(rgb2), amount) + +/proc/HueToAngle(hue) + // normalize hsv in case anything is screwy + if(hue < 0 || hue >= 1536) hue %= 1536 + if(hue < 0) hue += 1536 + // Compress hue into easier-to-manage range + hue -= hue >> 8 + return hue / (1530/360) + +/proc/AngleToHue(angle) + // normalize hsv in case anything is screwy + if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) + var/hue = angle * (1530/360) + // Decompress hue + hue += round(hue / 255) + return hue + + +// positive angle rotates forward through red->green->blue +/proc/RotateHue(hsv, angle) + var/list/HSV = ReadHSV(hsv) + + // normalize hsv in case anything is screwy + if(HSV[1] >= 1536) HSV[1] %= 1536 + if(HSV[1] < 0) HSV[1] += 1536 + + // Compress hue into easier-to-manage range + HSV[1] -= HSV[1] >> 8 + + if(angle < 0 || angle >= 360) angle -= 360 * round(angle / 360) + HSV[1] = round(HSV[1] + angle * (1530/360), 1) + + // normalize hue + if(HSV[1] < 0 || HSV[1] >= 1530) HSV[1] %= 1530 + if(HSV[1] < 0) HSV[1] += 1530 + // decompress hue + HSV[1] += round(HSV[1] / 255) + + return hsv(HSV[1], HSV[2], HSV[3], (HSV.len > 3 ? HSV[4] : null)) + +// Convert an rgb color to grayscale +/proc/GrayScale(rgb) + var/list/RGB = ReadRGB(rgb) + var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 + return (RGB.len > 3) ? rgb(gray, gray, gray, RGB[4]) : rgb(gray, gray, gray) + +// Change grayscale color to black->tone->white range +/proc/ColorTone(rgb, tone) + var/list/RGB = ReadRGB(rgb) + var/list/TONE = ReadRGB(tone) + + var/gray = RGB[1]*0.3 + RGB[2]*0.59 + RGB[3]*0.11 + var/tone_gray = TONE[1]*0.3 + TONE[2]*0.59 + TONE[3]*0.11 + + if(gray <= tone_gray) return BlendRGB("#000000", tone, gray/(tone_gray || 1)) + else return BlendRGB(tone, "#ffffff", (gray-tone_gray)/((255-tone_gray) || 1)) + + +/* +Get flat icon by DarkCampainger. As it says on the tin, will return an icon with all the overlays +as a single icon. Useful for when you want to manipulate an icon via the above as overlays are not normally included. +The _flatIcons list is a cache for generated icon files. +*/ + +// Creates a single icon from a given /atom or /image. Only the first argument is required. +/proc/getFlatIcon(image/A, defdir, deficon, defstate, defblend, start = TRUE, no_anim = FALSE) + //Define... defines. + var/static/icon/flat_template = icon('icons/effects/effects.dmi', "nothing") + + #define BLANK icon(flat_template) + #define SET_SELF(SETVAR) do { \ + var/icon/SELF_ICON=icon(icon(curicon, curstate, base_icon_dir),"",SOUTH,no_anim?1:null); \ + if(A.alpha<255) { \ + SELF_ICON.Blend(rgb(255,255,255,A.alpha),ICON_MULTIPLY);\ + } \ + if(A.color) { \ + if(islist(A.color)){ \ + SELF_ICON.MapColors(arglist(A.color))} \ + else{ \ + SELF_ICON.Blend(A.color,ICON_MULTIPLY)} \ + } \ + ##SETVAR=SELF_ICON;\ + } while (0) + #define INDEX_X_LOW 1 + #define INDEX_X_HIGH 2 + #define INDEX_Y_LOW 3 + #define INDEX_Y_HIGH 4 + + #define flatX1 flat_size[INDEX_X_LOW] + #define flatX2 flat_size[INDEX_X_HIGH] + #define flatY1 flat_size[INDEX_Y_LOW] + #define flatY2 flat_size[INDEX_Y_HIGH] + #define addX1 add_size[INDEX_X_LOW] + #define addX2 add_size[INDEX_X_HIGH] + #define addY1 add_size[INDEX_Y_LOW] + #define addY2 add_size[INDEX_Y_HIGH] + + if(!A || A.alpha <= 0) + return BLANK + + var/noIcon = FALSE + if(start) + if(!defdir) + defdir = A.dir + if(!deficon) + deficon = A.icon + if(!defstate) + defstate = A.icon_state + if(!defblend) + defblend = A.blend_mode + + var/curicon = A.icon || deficon + var/curstate = A.icon_state || defstate + + if(!((noIcon = (!curicon)))) + var/curstates = icon_states(curicon) + if(!(curstate in curstates)) + if("" in curstates) + curstate = "" + else + noIcon = TRUE // Do not render this object. + + var/curdir + var/base_icon_dir //We'll use this to get the icon state to display if not null BUT NOT pass it to overlays as the dir we have + + //These should use the parent's direction (most likely) + if(!A.dir || A.dir == SOUTH) + curdir = defdir + else + curdir = A.dir + + //Try to remove/optimize this section ASAP, CPU hog. + //Determines if there's directionals. + if(!noIcon && curdir != SOUTH) + var/exist = FALSE + var/static/list/checkdirs = list(NORTH, EAST, WEST) + for(var/i in checkdirs) //Not using GLOB for a reason. + if(length(icon_states(icon(curicon, curstate, i)))) + exist = TRUE + break + if(!exist) + base_icon_dir = SOUTH + // + + if(!base_icon_dir) + base_icon_dir = curdir + + ASSERT(!BLEND_DEFAULT) //I might just be stupid but lets make sure this define is 0. + + var/curblend = A.blend_mode || defblend + + if(A.overlays.len || A.underlays.len) + var/icon/flat = BLANK + // Layers will be a sorted list of icons/overlays, based on the order in which they are displayed + var/list/layers = list() + var/image/copy + // Add the atom's icon itself, without pixel_x/y offsets. + if(!noIcon) + copy = image(icon=curicon, icon_state=curstate, layer=A.layer, dir=base_icon_dir) + copy.color = A.color + copy.alpha = A.alpha + copy.blend_mode = curblend + layers[copy] = A.layer + + // Loop through the underlays, then overlays, sorting them into the layers list + for(var/process_set in 0 to 1) + var/list/process = process_set? A.overlays : A.underlays + for(var/i in 1 to process.len) + var/image/current = process[i] + if(!current) + continue + if(current.plane != FLOAT_PLANE && current.plane != A.plane) + continue + var/current_layer = current.layer + if(current_layer < 0) + if(current_layer <= -1000) + return flat + current_layer = process_set + A.layer + current_layer / 1000 + + for(var/p in 1 to layers.len) + var/image/cmp = layers[p] + if(current_layer < layers[cmp]) + layers.Insert(p, current) + break + layers[current] = current_layer + + //sortTim(layers, /proc/cmp_image_layer_asc) + + var/icon/add // Icon of overlay being added + + // Current dimensions of flattened icon + var/list/flat_size = list(1, flat.Width(), 1, flat.Height()) + // Dimensions of overlay being added + var/list/add_size[4] + + for(var/V in layers) + var/image/I = V + if(I.alpha == 0) + continue + + if(I == copy) // 'I' is an /image based on the object being flattened. + curblend = BLEND_OVERLAY + add = icon(I.icon, I.icon_state, base_icon_dir) + else // 'I' is an appearance object. + add = getFlatIcon(image(I), curdir, curicon, curstate, curblend, FALSE, no_anim) + if(!add) + continue + // Find the new dimensions of the flat icon to fit the added overlay + add_size = list( + min(flatX1, I.pixel_x+1), + max(flatX2, I.pixel_x+add.Width()), + min(flatY1, I.pixel_y+1), + max(flatY2, I.pixel_y+add.Height()) + ) + + if(flat_size ~! add_size) + // Resize the flattened icon so the new icon fits + flat.Crop( + addX1 - flatX1 + 1, + addY1 - flatY1 + 1, + addX2 - flatX1 + 1, + addY2 - flatY1 + 1 + ) + flat_size = add_size.Copy() + + // Blend the overlay into the flattened icon + flat.Blend(add, blendMode2iconMode(curblend), I.pixel_x + 2 - flatX1, I.pixel_y + 2 - flatY1) + + if(A.color) + if(islist(A.color)) + flat.MapColors(arglist(A.color)) + else + flat.Blend(A.color, ICON_MULTIPLY) + + if(A.alpha < 255) + flat.Blend(rgb(255, 255, 255, A.alpha), ICON_MULTIPLY) + + if(no_anim) + //Clean up repeated frames + var/icon/cleaned = new /icon() + cleaned.Insert(flat, "", SOUTH, 1, 0) + . = cleaned + else + . = icon(flat, "", SOUTH) + else //There's no overlays. + if(!noIcon) + SET_SELF(.) + + //Clear defines + #undef flatX1 + #undef flatX2 + #undef flatY1 + #undef flatY2 + #undef addX1 + #undef addX2 + #undef addY1 + #undef addY2 + + #undef INDEX_X_LOW + #undef INDEX_X_HIGH + #undef INDEX_Y_LOW + #undef INDEX_Y_HIGH + + #undef BLANK + #undef SET_SELF + +/proc/getIconMask(atom/A)//By yours truly. Creates a dynamic mask for a mob/whatever. /N + var/icon/alpha_mask = new(A.icon,A.icon_state)//So we want the default icon and icon state of A. + for(var/V in A.overlays)//For every image in overlays. var/image/I will not work, don't try it. + var/image/I = V + if(I.layer>A.layer) + continue//If layer is greater than what we need, skip it. + var/icon/image_overlay = new(I.icon,I.icon_state)//Blend only works with icon objects. + //Also, icons cannot directly set icon_state. Slower than changing variables but whatever. + alpha_mask.Blend(image_overlay,ICON_OR)//OR so they are lumped together in a nice overlay. + return alpha_mask//And now return the mask. + +/mob/proc/AddCamoOverlay(atom/A)//A is the atom which we are using as the overlay. + var/icon/opacity_icon = new(A.icon, A.icon_state)//Don't really care for overlays/underlays. + //Now we need to culculate overlays+underlays and add them together to form an image for a mask. + //var/icon/alpha_mask = getFlatIcon(src)//Accurate but SLOW. Not designed for running each tick. Could have other uses I guess. + var/icon/alpha_mask = getIconMask(src)//Which is why I created that proc. Also a little slow since it's blending a bunch of icons together but good enough. + opacity_icon.AddAlphaMask(alpha_mask)//Likely the main source of lag for this proc. Probably not designed to run each tick. + opacity_icon.ChangeOpacity(0.4)//Front end for MapColors so it's fast. 0.5 means half opacity and looks the best in my opinion. + for(var/i=0,i<5,i++)//And now we add it as overlays. It's faster than creating an icon and then merging it. + var/image/I = image("icon" = opacity_icon, "icon_state" = A.icon_state, "layer" = layer+0.8)//So it's above other stuff but below weapons and the like. + switch(i)//Now to determine offset so the result is somewhat blurred. + if(1) I.pixel_x-- + if(2) I.pixel_x++ + if(3) I.pixel_y-- + if(4) I.pixel_y++ + overlays += I//And finally add the overlay. + +/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created. + var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon. + var/icon/alpha_mask + flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish. + flat_icon.ChangeOpacity(0.5)//Make it half transparent. + if(A.Height() == 64) + alpha_mask = new('icons/mob/ancient_machine.dmi', "scanline2")//Scaline for tall icons. + else + alpha_mask = new('icons/effects/effects.dmi', "scanline")//Scanline effect. + flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect. + return flat_icon + +//For photo camera. +/proc/build_composite_icon(atom/A) + var/icon/composite = icon(A.icon, A.icon_state, A.dir, 1) + for(var/O in A.overlays) + var/image/I = O + composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY) + return composite + +/proc/adjust_brightness(var/color, var/value) + if(!color) return "#FFFFFF" + if(!value) return color + + var/list/RGB = ReadRGB(color) + RGB[1] = Clamp(RGB[1]+value,0,255) + RGB[2] = Clamp(RGB[2]+value,0,255) + RGB[3] = Clamp(RGB[3]+value,0,255) + return rgb(RGB[1],RGB[2],RGB[3]) + +/proc/sort_atoms_by_layer(var/list/atoms) + // Comb sort icons based on levels + var/list/result = atoms.Copy() + var/gap = result.len + var/swapped = 1 + while(gap > 1 || swapped) + swapped = 0 + if(gap > 1) + gap = round(gap / 1.3) // 1.3 is the emperic comb sort coefficient + if(gap < 1) + gap = 1 + for(var/i = 1; gap + i <= result.len; i++) + var/atom/l = result[i] //Fucking hate + var/atom/r = result[gap+i] //how lists work here + if(l.layer > r.layer) //no "result[i].layer" for me + result.Swap(i, gap + i) + swapped = 1 + return result + +//Interface for using DrawBox() to draw 1 pixel on a coordinate. +//Returns the same icon specifed in the argument, but with the pixel drawn +/proc/DrawPixel(var/icon/I,var/colour,var/drawX,var/drawY) + if(!I) + return 0 + var/Iwidth = I.Width() + var/Iheight = I.Height() + if(drawX > Iwidth || drawX <= 0) + return 0 + if(drawY > Iheight || drawY <= 0) + return 0 + I.DrawBox(colour,drawX, drawY) + return I + +//Interface for easy drawing of one pixel on an atom. +/atom/proc/DrawPixelOn(var/colour, var/drawX, var/drawY) + var/icon/I = new(icon) + var/icon/J = DrawPixel(I, colour, drawX, drawY) + if(J) //Only set the icon if it succeeded, the icon without the pixel is 1000x better than a black square. + icon = J + return J + return 0 + +//Hook, override to run code on- wait this is images +//Images have dir without being an atom, so they get their own definition. +//Lame. +/image/proc/setDir(newdir) + dir = newdir + +/proc/rand_hex_color() + var/list/colors = list("0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f") + var/color="" + for(var/i=0;i<6;i++) + color = color+pick(colors) + return "#[color]" + +//Imagine removing pixels from the main icon that are covered by pixels from the mask icon. +//Standard behaviour is to cut pixels from the main icon that are covered by pixels from the mask icon unless passed mask_ready, see below. +/proc/get_icon_difference(var/icon/main, var/icon/mask, var/mask_ready) + /*You should skip prep if the mask is already sprited properly. This significantly improves performance by eliminating most of the realtime icon work. + e.g. A 'ready' mask is a mask where the part you want cut out is missing (no pixels, 0 alpha) from the sprite, and everything else is solid white.*/ + + if(istype(main) && istype(mask)) + if(!mask_ready) //Prep the mask if we're using a regular old sprite and not a special-made mask. + mask.Blend(rgb(255,255,255), ICON_SUBTRACT) //Make all pixels on the mask as black as possible. + mask.Opaque(rgb(255,255,255)) //Make the transparent pixels (background) white. + mask.BecomeAlphaMask() //Make all the black pixels vanish (fully transparent), leaving only the white background pixels. + + main.AddAlphaMask(mask) //Make the pixels in the main icon that are in the transparent zone of the mask icon also vanish (fully transparent). + return main diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 5ffe891d95bb..7d1b8ee6ebc1 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -1,822 +1,822 @@ -/* - * Holds procs to help with list operations - * Contains groups: - * Misc - * Sorting - */ - -/* - * Misc - */ - - // binary search sorted insert -// IN: Object to be inserted -// LIST: List to insert object into -// TYPECONT: The typepath of the contents of the list -// COMPARE: The variable on the objects to compare -#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \ - var/__BIN_CTTL = length(LIST);\ - if(!__BIN_CTTL) {\ - LIST += IN;\ - } else {\ - var/__BIN_LEFT = 1;\ - var/__BIN_RIGHT = __BIN_CTTL;\ - var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ - var/##TYPECONT/__BIN_ITEM;\ - while(__BIN_LEFT < __BIN_RIGHT) {\ - __BIN_ITEM = LIST[__BIN_MID];\ - if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\ - __BIN_LEFT = __BIN_MID + 1;\ - } else {\ - __BIN_RIGHT = __BIN_MID;\ - };\ - __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ - };\ - __BIN_ITEM = LIST[__BIN_MID];\ - __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ - LIST.Insert(__BIN_MID, IN);\ - } - - -//Returns a list in plain english as a string -/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) - var/total = input.len - if(!total) - return "[nothing_text]" - else if(total == 1) - return "[input[1]]" - else if(total == 2) - return "[input[1]][and_text][input[2]]" - else - var/output = "" - var/index = 1 - while(index < total) - if(index == total - 1) - comma_text = final_comma_text - - output += "[input[index]][comma_text]" - index++ - - return "[output][and_text][input[index]]" - -//Returns list element or null. Should prevent "index out of bounds" error. -/proc/listgetindex(var/list/list,index) - if(istype(list) && list.len) - if(isnum(index)) - if(InRange(index,1,list.len)) - return list[index] - else if(index in list) - return list[index] - return - -//Return either pick(list) or null if list is not of type /list or is empty -/proc/safepick(list/list) - if(!islist(list) || !list.len) - return - return pick(list) - -//Checks if the list is empty -/proc/isemptylist(list/list) - if(!list.len) - return 1 - return 0 - -//Checks for specific types in a list -/proc/is_type_in_list(atom/A, list/L) - if(!L || !L.len || !A) - return 0 - for(var/type in L) - if(istype(A, type)) - return 1 - return 0 - -//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches') -/proc/is_type_in_typecache(atom/A, list/L) - if(!L || !L.len || !A) - return 0 - return L[A.type] - -//returns a new list with only atoms that are in typecache L -/proc/typecache_filter_list(list/atoms, list/typecache) - . = list() - for(var/thing in atoms) - var/atom/A = thing - if(typecache[A.type]) - . += A - -/proc/typecache_filter_list_reverse(list/atoms, list/typecache) - . = list() - for(var/thing in atoms) - var/atom/A = thing - if(!typecache[A.type]) - . += A - -/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude) - . = list() - for(var/thing in atoms) - var/atom/A = thing - if(typecache_include[A.type] && !typecache_exclude[A.type]) - . += A - -//Like typesof() or subtypesof(), but returns a typecache instead of a list -/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE) - if(ispath(path)) - var/list/types = list() - if(only_root_path) - types = list(path) - else - types = ignore_root_path ? subtypesof(path) : typesof(path) - var/list/L = list() - for(var/T in types) - L[T] = TRUE - return L - else if(islist(path)) - var/list/pathlist = path - var/list/L = list() - if(ignore_root_path) - for(var/P in pathlist) - for(var/T in subtypesof(P)) - L[T] = TRUE - else - for(var/P in pathlist) - if(only_root_path) - L[P] = TRUE - else - for(var/T in typesof(P)) - L[T] = TRUE - return L - -//Removes any null entries from the list -/proc/listclearnulls(list/list) - if(istype(list)) - while(null in list) - list -= null - return - -/* - * 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(var/list/first, var/list/second, var/skiprep=0) - if(!islist(first) || !islist(second)) - return - var/list/result = new - if(skiprep) - for(var/e in first) - if(!(e in result) && !(e in second)) - result += e - else - result = first - second - - return result - -/* - * Returns list containing entries that are in either list but not both. - * If skipref = 1, repeated elements are treated as one. - * If either of arguments is not a list, returns null - */ -/proc/uniquemergelist(var/list/first, var/list/second, var/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 - -//Pretends to pick an element based on its weight but really just seems to pick a random element. -/proc/pickweight(list/L) - var/total = 0 - var/item - for(item in L) - if(!L[item]) - L[item] = 1 - total += L[item] - - total = rand(1, total) - for(item in L) - total -=L [item] - if(total <= 0) - return item - - return null - -//Pick a random element from the list and remove it from the list. -/proc/pick_n_take(list/listfrom) - if(listfrom.len > 0) - var/picked = pick(listfrom) - listfrom -= picked - return picked - return null - -//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) - - -/* - * Sorting - */ - -//Reverses the order of items in the list -/proc/reverselist(list/L) - var/list/output = list() - if(L) - for(var/i = L.len; i >= 1; i--) - output += L[i] - return output - -//Randomize: Return the list in a random order -/proc/shuffle(var/list/L) - if(!L) - return - L = L.Copy() - - for(var/i=1, i current_index) - current_index++ - current_item = sorted_list[current_index] - - current_item_value = current_item:dd_SortValue() - current_sort_object_value = current_sort_object:dd_SortValue() - if(current_sort_object_value < current_item_value) - high_index = current_index - 1 - else if(current_sort_object_value > current_item_value) - low_index = current_index + 1 - else - // current_sort_object == current_item - low_index = current_index - break - - // Insert before low_index. - insert_index = low_index - - // Special case adding to end of list. - if(insert_index > sorted_list.len) - sorted_list += current_sort_object - continue - - // Because BYOND lists don't support insert, have to do it by: - // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. - list_bottom = sorted_list.Copy(insert_index) - sorted_list.Cut(insert_index) - sorted_list += current_sort_object - sorted_list += list_bottom - return sorted_list -*/ - -/proc/dd_sortedtextlist(list/incoming, case_sensitive = 0) - // Returns a new list with the text values sorted. - // Use binary search to order by sortValue. - // This works by going to the half-point of the list, seeing if the node in question is higher or lower cost, - // then going halfway up or down the list and checking again. - // This is a very fast way to sort an item into a list. - var/list/sorted_text = new() - var/low_index - var/high_index - var/insert_index - var/midway_calc - var/current_index - var/current_item - var/list/list_bottom - var/sort_result - - var/current_sort_text - for(current_sort_text in incoming) - low_index = 1 - high_index = sorted_text.len - while(low_index <= high_index) - // Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.) - midway_calc = (low_index + high_index) / 2 - current_index = round(midway_calc) - if(midway_calc > current_index) - current_index++ - current_item = sorted_text[current_index] - - if(case_sensitive) - sort_result = sorttextEx(current_sort_text, current_item) - else - sort_result = sorttext(current_sort_text, current_item) - - switch(sort_result) - if(1) - high_index = current_index - 1 // current_sort_text < current_item - if(-1) - low_index = current_index + 1 // current_sort_text > current_item - if(0) - low_index = current_index // current_sort_text == current_item - break - - // Insert before low_index. - insert_index = low_index - - // Special case adding to end of list. - if(insert_index > sorted_text.len) - sorted_text += current_sort_text - continue - - // Because BYOND lists don't support insert, have to do it by: - // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. - list_bottom = sorted_text.Copy(insert_index) - sorted_text.Cut(insert_index) - sorted_text += current_sort_text - sorted_text += list_bottom - return sorted_text - - -/proc/dd_sortedTextList(list/incoming) - var/case_sensitive = 1 - return dd_sortedtextlist(incoming, case_sensitive) - -/proc/subtypesof(var/path) //Returns a list containing all subtypes of the given path, but not the given path itself. - if(!path || !ispath(path)) - CRASH("Invalid path, failed to fetch subtypes of \"[path]\".") - return (typesof(path) - path) - -/datum/proc/dd_SortValue() - return "[src]" - -/obj/machinery/dd_SortValue() - return "[sanitize(name)]" - -/obj/machinery/camera/dd_SortValue() - return "[c_tag]" - -/datum/alarm/dd_SortValue() - return "[sanitize(last_name)]" - -//Picks from the list, with some safeties, and returns the "default" arg if it fails -#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) - -#define LAZYINITLIST(L) if (!L) L = list() - -#define UNSETEMPTY(L) if (L && !L.len) L = null -#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } } -#define LAZYADD(L, I) if(!L) { L = list(); } L += I; -#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) -#define LAZYLEN(L) length(L) -#define LAZYCLEARLIST(L) if(L) L.Cut() - -// LAZYING PT 2: THE LAZENING -#define LAZYREINITLIST(L) LAZYCLEARLIST(L); LAZYINITLIST(L); - - -//same, but returns nothing and acts on list in place -/proc/shuffle_inplace(list/L) - if(!L) - return - - for(var/i=1, i toIndex) - ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one - - L.Insert(toIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(fromIndex, fromIndex + 1) - - -//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex) -//Same as moveElement but for ranges of elements -//This will preserve associations ~Carnie -/proc/moveRange(list/L, fromIndex, toIndex, len = 1) - var/distance = abs(toIndex - fromIndex) - if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements - if(fromIndex <= toIndex) - return //no need to move - fromIndex += len //we want to shift left instead of right - - for(var/i = 0, i < distance, ++i) - L.Insert(fromIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(toIndex, toIndex + 1) - else - if(fromIndex > toIndex) - fromIndex += len - - for(var/i = 0, i < len, ++i) - L.Insert(toIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(fromIndex, fromIndex + 1) - -//Move elements from [fromIndex, fromIndex+len) to [toIndex, toIndex+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/swapRange(list/L, fromIndex, toIndex, len = 1) - var/distance = abs(toIndex - fromIndex) - if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements - if(fromIndex < toIndex) - toIndex += len - else - fromIndex += len - - for(var/i = 0, i < distance, ++i) - L.Insert(fromIndex, null) - L.Swap(fromIndex, toIndex) - L.Cut(toIndex, toIndex + 1) - else - if(toIndex > fromIndex) - var/a = toIndex - toIndex = fromIndex - fromIndex = a - - for(var/i = 0, i < len, ++i) - L.Swap(fromIndex++, toIndex++) - -//replaces reverseList ~Carnie -/proc/reverseRange(list/L, start = 1, end = 0) - if(L.len) - start = start % L.len - end = end % (L.len + 1) - if(start <= 0) - start += L.len - if(end <= 0) - end += L.len + 1 - - --end - while(start < end) - L.Swap(start++, end--) - - return L - -/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 \ No newline at end of file +/* + * Holds procs to help with list operations + * Contains groups: + * Misc + * Sorting + */ + +/* + * Misc + */ + + // binary search sorted insert +// IN: Object to be inserted +// LIST: List to insert object into +// TYPECONT: The typepath of the contents of the list +// COMPARE: The variable on the objects to compare +#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \ + var/__BIN_CTTL = length(LIST);\ + if(!__BIN_CTTL) {\ + LIST += IN;\ + } else {\ + var/__BIN_LEFT = 1;\ + var/__BIN_RIGHT = __BIN_CTTL;\ + var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + var/##TYPECONT/__BIN_ITEM;\ + while(__BIN_LEFT < __BIN_RIGHT) {\ + __BIN_ITEM = LIST[__BIN_MID];\ + if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\ + __BIN_LEFT = __BIN_MID + 1;\ + } else {\ + __BIN_RIGHT = __BIN_MID;\ + };\ + __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + };\ + __BIN_ITEM = LIST[__BIN_MID];\ + __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ + LIST.Insert(__BIN_MID, IN);\ + } + + +//Returns a list in plain english as a string +/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) + var/total = input.len + if(!total) + return "[nothing_text]" + else if(total == 1) + return "[input[1]]" + else if(total == 2) + return "[input[1]][and_text][input[2]]" + else + var/output = "" + var/index = 1 + while(index < total) + if(index == total - 1) + comma_text = final_comma_text + + output += "[input[index]][comma_text]" + index++ + + return "[output][and_text][input[index]]" + +//Returns list element or null. Should prevent "index out of bounds" error. +/proc/listgetindex(var/list/list,index) + if(istype(list) && list.len) + if(isnum(index)) + if(InRange(index,1,list.len)) + return list[index] + else if(index in list) + return list[index] + return + +//Return either pick(list) or null if list is not of type /list or is empty +/proc/safepick(list/list) + if(!islist(list) || !list.len) + return + return pick(list) + +//Checks if the list is empty +/proc/isemptylist(list/list) + if(!list.len) + return 1 + return 0 + +//Checks for specific types in a list +/proc/is_type_in_list(atom/A, list/L) + if(!L || !L.len || !A) + return 0 + for(var/type in L) + if(istype(A, type)) + return 1 + return 0 + +//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches') +/proc/is_type_in_typecache(atom/A, list/L) + if(!L || !L.len || !A) + return 0 + return L[A.type] + +//returns a new list with only atoms that are in typecache L +/proc/typecache_filter_list(list/atoms, list/typecache) + . = list() + for(var/thing in atoms) + var/atom/A = thing + if(typecache[A.type]) + . += A + +/proc/typecache_filter_list_reverse(list/atoms, list/typecache) + . = list() + for(var/thing in atoms) + var/atom/A = thing + if(!typecache[A.type]) + . += A + +/proc/typecache_filter_multi_list_exclusion(list/atoms, list/typecache_include, list/typecache_exclude) + . = list() + for(var/thing in atoms) + var/atom/A = thing + if(typecache_include[A.type] && !typecache_exclude[A.type]) + . += A + +//Like typesof() or subtypesof(), but returns a typecache instead of a list +/proc/typecacheof(path, ignore_root_path, only_root_path = FALSE) + if(ispath(path)) + var/list/types = list() + if(only_root_path) + types = list(path) + else + types = ignore_root_path ? subtypesof(path) : typesof(path) + var/list/L = list() + for(var/T in types) + L[T] = TRUE + return L + else if(islist(path)) + var/list/pathlist = path + var/list/L = list() + if(ignore_root_path) + for(var/P in pathlist) + for(var/T in subtypesof(P)) + L[T] = TRUE + else + for(var/P in pathlist) + if(only_root_path) + L[P] = TRUE + else + for(var/T in typesof(P)) + L[T] = TRUE + return L + +//Removes any null entries from the list +/proc/listclearnulls(list/list) + if(istype(list)) + while(null in list) + list -= null + return + +/* + * 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(var/list/first, var/list/second, var/skiprep=0) + if(!islist(first) || !islist(second)) + return + var/list/result = new + if(skiprep) + for(var/e in first) + if(!(e in result) && !(e in second)) + result += e + else + result = first - second + + return result + +/* + * Returns list containing entries that are in either list but not both. + * If skipref = 1, repeated elements are treated as one. + * If either of arguments is not a list, returns null + */ +/proc/uniquemergelist(var/list/first, var/list/second, var/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 + +//Pretends to pick an element based on its weight but really just seems to pick a random element. +/proc/pickweight(list/L) + var/total = 0 + var/item + for(item in L) + if(!L[item]) + L[item] = 1 + total += L[item] + + total = rand(1, total) + for(item in L) + total -=L [item] + if(total <= 0) + return item + + return null + +//Pick a random element from the list and remove it from the list. +/proc/pick_n_take(list/listfrom) + if(listfrom.len > 0) + var/picked = pick(listfrom) + listfrom -= picked + return picked + return null + +//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) + + +/* + * Sorting + */ + +//Reverses the order of items in the list +/proc/reverselist(list/L) + var/list/output = list() + if(L) + for(var/i = L.len; i >= 1; i--) + output += L[i] + return output + +//Randomize: Return the list in a random order +/proc/shuffle(var/list/L) + if(!L) + return + L = L.Copy() + + for(var/i=1, i current_index) + current_index++ + current_item = sorted_list[current_index] + + current_item_value = current_item:dd_SortValue() + current_sort_object_value = current_sort_object:dd_SortValue() + if(current_sort_object_value < current_item_value) + high_index = current_index - 1 + else if(current_sort_object_value > current_item_value) + low_index = current_index + 1 + else + // current_sort_object == current_item + low_index = current_index + break + + // Insert before low_index. + insert_index = low_index + + // Special case adding to end of list. + if(insert_index > sorted_list.len) + sorted_list += current_sort_object + continue + + // Because BYOND lists don't support insert, have to do it by: + // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. + list_bottom = sorted_list.Copy(insert_index) + sorted_list.Cut(insert_index) + sorted_list += current_sort_object + sorted_list += list_bottom + return sorted_list +*/ + +/proc/dd_sortedtextlist(list/incoming, case_sensitive = 0) + // Returns a new list with the text values sorted. + // Use binary search to order by sortValue. + // This works by going to the half-point of the list, seeing if the node in question is higher or lower cost, + // then going halfway up or down the list and checking again. + // This is a very fast way to sort an item into a list. + var/list/sorted_text = new() + var/low_index + var/high_index + var/insert_index + var/midway_calc + var/current_index + var/current_item + var/list/list_bottom + var/sort_result + + var/current_sort_text + for(current_sort_text in incoming) + low_index = 1 + high_index = sorted_text.len + while(low_index <= high_index) + // Figure out the midpoint, rounding up for fractions. (BYOND rounds down, so add 1 if necessary.) + midway_calc = (low_index + high_index) / 2 + current_index = round(midway_calc) + if(midway_calc > current_index) + current_index++ + current_item = sorted_text[current_index] + + if(case_sensitive) + sort_result = sorttextEx(current_sort_text, current_item) + else + sort_result = sorttext(current_sort_text, current_item) + + switch(sort_result) + if(1) + high_index = current_index - 1 // current_sort_text < current_item + if(-1) + low_index = current_index + 1 // current_sort_text > current_item + if(0) + low_index = current_index // current_sort_text == current_item + break + + // Insert before low_index. + insert_index = low_index + + // Special case adding to end of list. + if(insert_index > sorted_text.len) + sorted_text += current_sort_text + continue + + // Because BYOND lists don't support insert, have to do it by: + // 1) taking out bottom of list, 2) adding item, 3) putting back bottom of list. + list_bottom = sorted_text.Copy(insert_index) + sorted_text.Cut(insert_index) + sorted_text += current_sort_text + sorted_text += list_bottom + return sorted_text + + +/proc/dd_sortedTextList(list/incoming) + var/case_sensitive = 1 + return dd_sortedtextlist(incoming, case_sensitive) + +/proc/subtypesof(var/path) //Returns a list containing all subtypes of the given path, but not the given path itself. + if(!path || !ispath(path)) + CRASH("Invalid path, failed to fetch subtypes of \"[path]\".") + return (typesof(path) - path) + +/datum/proc/dd_SortValue() + return "[src]" + +/obj/machinery/dd_SortValue() + return "[sanitize(name)]" + +/obj/machinery/camera/dd_SortValue() + return "[c_tag]" + +/datum/alarm/dd_SortValue() + return "[sanitize(last_name)]" + +//Picks from the list, with some safeties, and returns the "default" arg if it fails +#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default) + +#define LAZYINITLIST(L) if (!L) L = list() + +#define UNSETEMPTY(L) if (L && !L.len) L = null +#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } } +#define LAZYADD(L, I) if(!L) { L = list(); } L += I; +#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null) +#define LAZYLEN(L) length(L) +#define LAZYCLEARLIST(L) if(L) L.Cut() + +// LAZYING PT 2: THE LAZENING +#define LAZYREINITLIST(L) LAZYCLEARLIST(L); LAZYINITLIST(L); + + +//same, but returns nothing and acts on list in place +/proc/shuffle_inplace(list/L) + if(!L) + return + + for(var/i=1, i toIndex) + ++fromIndex //since a null will be inserted before fromIndex, the index needs to be nudged right by one + + L.Insert(toIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(fromIndex, fromIndex + 1) + + +//Move elements [fromIndex,fromIndex+len) to [toIndex-len, toIndex) +//Same as moveElement but for ranges of elements +//This will preserve associations ~Carnie +/proc/moveRange(list/L, fromIndex, toIndex, len = 1) + var/distance = abs(toIndex - fromIndex) + if(len >= distance) //there are more elements to be moved than the distance to be moved. Therefore the same result can be achieved (with fewer operations) by moving elements between where we are and where we are going. The result being, our range we are moving is shifted left or right by dist elements + if(fromIndex <= toIndex) + return //no need to move + fromIndex += len //we want to shift left instead of right + + for(var/i = 0, i < distance, ++i) + L.Insert(fromIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(toIndex, toIndex + 1) + else + if(fromIndex > toIndex) + fromIndex += len + + for(var/i = 0, i < len, ++i) + L.Insert(toIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(fromIndex, fromIndex + 1) + +//Move elements from [fromIndex, fromIndex+len) to [toIndex, toIndex+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/swapRange(list/L, fromIndex, toIndex, len = 1) + var/distance = abs(toIndex - fromIndex) + if(len > distance) //there is an overlap, therefore swapping each element will require more swaps than inserting new elements + if(fromIndex < toIndex) + toIndex += len + else + fromIndex += len + + for(var/i = 0, i < distance, ++i) + L.Insert(fromIndex, null) + L.Swap(fromIndex, toIndex) + L.Cut(toIndex, toIndex + 1) + else + if(toIndex > fromIndex) + var/a = toIndex + toIndex = fromIndex + fromIndex = a + + for(var/i = 0, i < len, ++i) + L.Swap(fromIndex++, toIndex++) + +//replaces reverseList ~Carnie +/proc/reverseRange(list/L, start = 1, end = 0) + if(L.len) + start = start % L.len + end = end % (L.len + 1) + if(start <= 0) + start += L.len + if(end <= 0) + end += L.len + 1 + + --end + while(start < end) + L.Swap(start++, end--) + + return L + +/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 diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 8491182ab9a6..76d422e3d82a 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -1,143 +1,143 @@ -// Credits to Nickr5 for the useful procs I've taken from his library resource. - -#define MATH_E 2.71828183 -#define SQRT2 1.41421356 - -/proc/Atan2(x, y) - if(!x && !y) return 0 - var/a = arccos(x / sqrt(x*x + y*y)) - return y >= 0 ? a : -a - -// Greatest Common Divisor - Euclid's algorithm -/proc/Gcd(a, b) - return b ? Gcd(b, a % b) : a - -/proc/IsAboutEqual(a, b, deviation = 0.1) - return abs(a - b) <= deviation - -// Performs a linear interpolation between a and b. -// Note that amount=0 returns a, amount=1 returns b, and -// amount=0.5 returns the mean of a and b. -/proc/Lerp(a, b, amount = 0.5) - return a + (b - a) * amount - -/proc/Mean(...) - var/values = 0 - var/sum = 0 - for(var/val in args) - values++ - sum += val - return sum / values - -// The quadratic formula. Returns a list with the solutions, or an empty list -// if they are imaginary. -/proc/SolveQuadratic(a, b, c) - ASSERT(a) - . = list() - var/d = b*b - 4 * a * c - var/bottom = 2 * a - if(d < 0) return - var/root = sqrt(d) - . += (-b + root) / bottom - if(!d) return - . += (-b - root) / bottom - -// Will filter out extra rotations and negative rotations -// E.g: 540 becomes 180. -180 becomes 180. -/proc/SimplifyDegrees(degrees) - degrees = degrees % 360 - if(degrees < 0) - degrees += 360 - return degrees - -// min is inclusive, max is exclusive -/proc/Wrap(val, min, max) - var/d = max - min - var/t = Floor((val - min) / d) - return val - (t * d) - -//A logarithm that converts an integer to a number scaled between 0 and 1 (can be tweaked to be higher). -//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions. -/proc/TransformUsingVariable(input, inputmaximum, scaling_modifier = 0) - - var/inputToDegrees = (input/inputmaximum)*180 //Converting from a 0 -> 100 scale to a 0 -> 180 scale. The 0 -> 180 scale corresponds to degrees - var/size_factor = ((-cos(inputToDegrees) +1) /2) //returns a value from 0 to 1 - - return size_factor + scaling_modifier //scale mod of 0 results in a number from 0 to 1. A scale modifier of +0.5 returns 0.5 to 1.5 - //world<< "Transform multiplier of [src] is [size_factor + scaling_modifer]" - -/proc/RaiseToPower(num, power) - if(!power) return 1 - return (power-- > 1 ? num * RaiseToPower(num, power) : num) - -//converts a uniform distributed random number into a normal distributed one -//since this method produces two random numbers, one is saved for subsequent calls -//(making the cost negligble for every second call) -//This will return +/- decimals, situated about mean with standard deviation stddev -//68% chance that the number is within 1stddev -//95% chance that the number is within 2stddev -//98% chance that the number is within 3stddev...etc -var/gaussian_next -#define ACCURACY 10000 -/proc/gaussian(mean, stddev) - var/R1;var/R2;var/working - if(gaussian_next != null) - R1 = gaussian_next - gaussian_next = null - else - do - R1 = rand(-ACCURACY,ACCURACY)/ACCURACY - R2 = rand(-ACCURACY,ACCURACY)/ACCURACY - working = R1*R1 + R2*R2 - while(working >= 1 || working==0) - working = sqrt(-2 * log(working) / working) - R1 *= working - gaussian_next = R2 * working - return (mean + stddev * R1) -#undef ACCURACY - - - -// oof, what a mouthful -// Used in status_procs' "adjust" to let them modify a status effect by a given -// amount, without inadverdently increasing it in the wrong direction -/proc/directional_bounded_sum(orig_val, modifier, bound_lower, bound_upper) - var/new_val = orig_val + modifier - if(modifier > 0) - if(new_val > bound_upper) - new_val = max(orig_val, bound_upper) - else if(modifier < 0) - if(new_val < bound_lower) - new_val = min(orig_val, bound_lower) - return new_val - -// sqrt, but if you give it a negative number, you get 0 instead of a runtime -/proc/sqrtor0(num) - if(num < 0) - return 0 - return sqrt(num) - -/proc/round_down(num) - if(round(num) != num) - return round(num--) - else return num - -// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles -/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4) - var/list/region_x1 = list() - var/list/region_y1 = list() - var/list/region_x2 = list() - var/list/region_y2 = list() - - // These loops create loops filled with x/y values that the boundaries inhabit - // ex: list(5, 6, 7, 8, 9) - for(var/i in min(x1, x2) to max(x1, x2)) - region_x1["[i]"] = TRUE - for(var/i in min(y1, y2) to max(y1, y2)) - region_y1["[i]"] = TRUE - for(var/i in min(x3, x4) to max(x3, x4)) - region_x2["[i]"] = TRUE - for(var/i in min(y3, y4) to max(y3, y4)) - region_y2["[i]"] = TRUE - - return list(region_x1 & region_x2, region_y1 & region_y2) \ No newline at end of file +// Credits to Nickr5 for the useful procs I've taken from his library resource. + +#define MATH_E 2.71828183 +#define SQRT2 1.41421356 + +/proc/Atan2(x, y) + if(!x && !y) return 0 + var/a = arccos(x / sqrt(x*x + y*y)) + return y >= 0 ? a : -a + +// Greatest Common Divisor - Euclid's algorithm +/proc/Gcd(a, b) + return b ? Gcd(b, a % b) : a + +/proc/IsAboutEqual(a, b, deviation = 0.1) + return abs(a - b) <= deviation + +// Performs a linear interpolation between a and b. +// Note that amount=0 returns a, amount=1 returns b, and +// amount=0.5 returns the mean of a and b. +/proc/Lerp(a, b, amount = 0.5) + return a + (b - a) * amount + +/proc/Mean(...) + var/values = 0 + var/sum = 0 + for(var/val in args) + values++ + sum += val + return sum / values + +// The quadratic formula. Returns a list with the solutions, or an empty list +// if they are imaginary. +/proc/SolveQuadratic(a, b, c) + ASSERT(a) + . = list() + var/d = b*b - 4 * a * c + var/bottom = 2 * a + if(d < 0) return + var/root = sqrt(d) + . += (-b + root) / bottom + if(!d) return + . += (-b - root) / bottom + +// Will filter out extra rotations and negative rotations +// E.g: 540 becomes 180. -180 becomes 180. +/proc/SimplifyDegrees(degrees) + degrees = degrees % 360 + if(degrees < 0) + degrees += 360 + return degrees + +// min is inclusive, max is exclusive +/proc/Wrap(val, min, max) + var/d = max - min + var/t = Floor((val - min) / d) + return val - (t * d) + +//A logarithm that converts an integer to a number scaled between 0 and 1 (can be tweaked to be higher). +//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions. +/proc/TransformUsingVariable(input, inputmaximum, scaling_modifier = 0) + + var/inputToDegrees = (input/inputmaximum)*180 //Converting from a 0 -> 100 scale to a 0 -> 180 scale. The 0 -> 180 scale corresponds to degrees + var/size_factor = ((-cos(inputToDegrees) +1) /2) //returns a value from 0 to 1 + + return size_factor + scaling_modifier //scale mod of 0 results in a number from 0 to 1. A scale modifier of +0.5 returns 0.5 to 1.5 + //world<< "Transform multiplier of [src] is [size_factor + scaling_modifer]" + +/proc/RaiseToPower(num, power) + if(!power) return 1 + return (power-- > 1 ? num * RaiseToPower(num, power) : num) + +//converts a uniform distributed random number into a normal distributed one +//since this method produces two random numbers, one is saved for subsequent calls +//(making the cost negligble for every second call) +//This will return +/- decimals, situated about mean with standard deviation stddev +//68% chance that the number is within 1stddev +//95% chance that the number is within 2stddev +//98% chance that the number is within 3stddev...etc +GLOBAL_VAR(gaussian_next) +#define ACCURACY 10000 +/proc/gaussian(mean, stddev) + var/R1;var/R2;var/working + if(GLOB.gaussian_next != null) + R1 = GLOB.gaussian_next + GLOB.gaussian_next = null + else + do + R1 = rand(-ACCURACY,ACCURACY)/ACCURACY + R2 = rand(-ACCURACY,ACCURACY)/ACCURACY + working = R1*R1 + R2*R2 + while(working >= 1 || working==0) + working = sqrt(-2 * log(working) / working) + R1 *= working + GLOB.gaussian_next = R2 * working + return (mean + stddev * R1) +#undef ACCURACY + + + +// oof, what a mouthful +// Used in status_procs' "adjust" to let them modify a status effect by a given +// amount, without inadverdently increasing it in the wrong direction +/proc/directional_bounded_sum(orig_val, modifier, bound_lower, bound_upper) + var/new_val = orig_val + modifier + if(modifier > 0) + if(new_val > bound_upper) + new_val = max(orig_val, bound_upper) + else if(modifier < 0) + if(new_val < bound_lower) + new_val = min(orig_val, bound_lower) + return new_val + +// sqrt, but if you give it a negative number, you get 0 instead of a runtime +/proc/sqrtor0(num) + if(num < 0) + return 0 + return sqrt(num) + +/proc/round_down(num) + if(round(num) != num) + return round(num--) + else return num + +// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles +/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4) + var/list/region_x1 = list() + var/list/region_y1 = list() + var/list/region_x2 = list() + var/list/region_y2 = list() + + // These loops create loops filled with x/y values that the boundaries inhabit + // ex: list(5, 6, 7, 8, 9) + for(var/i in min(x1, x2) to max(x1, x2)) + region_x1["[i]"] = TRUE + for(var/i in min(y1, y2) to max(y1, y2)) + region_y1["[i]"] = TRUE + for(var/i in min(x3, x4) to max(x3, x4)) + region_x2["[i]"] = TRUE + for(var/i in min(y3, y4) to max(y3, y4)) + region_y2["[i]"] = TRUE + + return list(region_x1 & region_x2, region_y1 & region_y2) diff --git a/code/__HELPERS/matrices.dm b/code/__HELPERS/matrices.dm index 3b370f8a9453..0520ab6b0775 100644 --- a/code/__HELPERS/matrices.dm +++ b/code/__HELPERS/matrices.dm @@ -59,4 +59,4 @@ //The Y pixel offset of this matrix /matrix/proc/get_y_shift() - . = f \ No newline at end of file + . = f diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index 994602828a08..8f0fa4f51316 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -1,578 +1,612 @@ -proc/GetOppositeDir(var/dir) - switch(dir) - if(NORTH) return SOUTH - if(SOUTH) return NORTH - if(EAST) return WEST - if(WEST) return EAST - if(SOUTHWEST) return NORTHEAST - if(NORTHWEST) return SOUTHEAST - if(NORTHEAST) return SOUTHWEST - if(SOUTHEAST) return NORTHWEST - return 0 - -proc/random_underwear(gender, species = "Human") - var/list/pick_list = list() - switch(gender) - if(MALE) pick_list = GLOB.underwear_m - if(FEMALE) pick_list = GLOB.underwear_f - else pick_list = GLOB.underwear_list - return pick_species_allowed_underwear(pick_list, species) - -proc/random_undershirt(gender, species = "Human") - var/list/pick_list = list() - switch(gender) - if(MALE) pick_list = GLOB.undershirt_m - if(FEMALE) pick_list = GLOB.undershirt_f - else pick_list = GLOB.undershirt_list - return pick_species_allowed_underwear(pick_list, species) - -proc/random_socks(gender, species = "Human") - var/list/pick_list = list() - switch(gender) - if(MALE) pick_list = GLOB.socks_m - if(FEMALE) pick_list = GLOB.socks_f - else pick_list = GLOB.socks_list - return pick_species_allowed_underwear(pick_list, species) - -proc/pick_species_allowed_underwear(list/all_picks, species) - var/list/valid_picks = list() - for(var/test in all_picks) - var/datum/sprite_accessory/S = all_picks[test] - if(!(species in S.species_allowed)) - continue - valid_picks += test - - if(!valid_picks.len) valid_picks += "Nude" - - return pick(valid_picks) - -proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) - var/h_style = "Bald" - var/list/valid_hairstyles = list() - for(var/hairstyle in GLOB.hair_styles_public_list) - var/datum/sprite_accessory/S = GLOB.hair_styles_public_list[hairstyle] - - if(hairstyle == "Bald") //Just in case. - valid_hairstyles += hairstyle - continue - if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) - continue - if(species == "Machine") //If the user is a species who can have a robotic head... - if(!robohead) - robohead = all_robolimbs["Morpheus Cyberkinetics"] - if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. - valid_hairstyles += hairstyle //Give them their hairstyles if they do. - else - if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. - But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */ - valid_hairstyles += hairstyle - else //If the user is not a species who can have robotic heads, use the default handling. - if(species in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list. - valid_hairstyles += hairstyle - - if(valid_hairstyles.len) - h_style = pick(valid_hairstyles) - - return h_style - -proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) - var/f_style = "Shaved" - var/list/valid_facial_hairstyles = list() - for(var/facialhairstyle in GLOB.facial_hair_styles_list) - var/datum/sprite_accessory/S = GLOB.facial_hair_styles_list[facialhairstyle] - - if(facialhairstyle == "Shaved") //Just in case. - valid_facial_hairstyles += facialhairstyle - continue - if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) - continue - if(species == "Machine") //If the user is a species who can have a robotic head... - if(!robohead) - robohead = all_robolimbs["Morpheus Cyberkinetics"] - if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. - valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. - else - if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. - But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */ - valid_facial_hairstyles += facialhairstyle - else //If the user is not a species who can have robotic heads, use the default handling. - if(species in S.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list. - valid_facial_hairstyles += facialhairstyle - - if(valid_facial_hairstyles.len) - f_style = pick(valid_facial_hairstyles) - - return f_style - -proc/random_head_accessory(species = "Human") - var/ha_style = "None" - var/list/valid_head_accessories = list() - for(var/head_accessory in GLOB.head_accessory_styles_list) - var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory] - - if(!(species in S.species_allowed)) - continue - valid_head_accessories += head_accessory - - if(valid_head_accessories.len) - ha_style = pick(valid_head_accessories) - - return ha_style - -proc/random_marking_style(var/location = "body", species = "Human", var/datum/robolimb/robohead, var/body_accessory, var/alt_head) - var/m_style = "None" - var/list/valid_markings = list() - for(var/marking in GLOB.marking_styles_list) - var/datum/sprite_accessory/body_markings/S = GLOB.marking_styles_list[marking] - if(S.name == "None") - valid_markings += marking - continue - if(S.marking_location != location) //If the marking isn't for the location we desire, skip. - continue - if(!(species in S.species_allowed)) //If the user's head is not of a species the marking style allows, skip it. Otherwise, add it to the list. - continue - if(location == "tail") - if(!body_accessory) - if(S.tails_allowed) - continue - else - if(!S.tails_allowed || !(body_accessory in S.tails_allowed)) - continue - if(location == "head") - var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[S.name] - if(species == "Machine")//If the user is a species that can have a robotic head... - if(!robohead) - robohead = all_robolimbs["Morpheus Cyberkinetics"] - if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head. - continue - else if(alt_head && alt_head != "None") //If the user's got an alt head, validate markings for that head. - if(!("All" in M.heads_allowed) && !(alt_head in M.heads_allowed)) - continue - else - if(M.heads_allowed && !("All" in M.heads_allowed)) - continue - valid_markings += marking - - if(valid_markings.len) - m_style = pick(valid_markings) - - return m_style - -proc/random_body_accessory(species = "Vulpkanin") - var/body_accessory = null - var/list/valid_body_accessories = list() - for(var/B in body_accessory_by_name) - var/datum/body_accessory/A = body_accessory_by_name[B] - if(!istype(A)) - valid_body_accessories += "None" //The only null entry should be the "None" option. - continue - if(species in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list. - valid_body_accessories += B - - if(valid_body_accessories.len) - body_accessory = pick(valid_body_accessories) - - return body_accessory - -proc/random_name(gender, species = "Human") - - var/datum/species/current_species - if(species) - current_species = GLOB.all_species[species] - - if(!current_species || current_species.name == "Human") - if(gender==FEMALE) - return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) - else - return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) - else - return current_species.get_random_name(gender) - -proc/random_skin_tone(species = "Human") - if(species == "Human" || species == "Drask") - switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) - if("caucasian") . = -10 - if("afroamerican") . = -115 - if("african") . = -165 - if("latino") . = -55 - if("albino") . = 34 - else . = rand(-185, 34) - return min(max(. + rand(-25, 25), -185), 34) - else if(species == "Vox") - . = rand(1, 6) - return . - -proc/skintone2racedescription(tone, species = "Human") - if(species == "Human") - switch(tone) - if(30 to INFINITY) return "albino" - if(20 to 30) return "pale" - if(5 to 15) return "light skinned" - if(-10 to 5) return "white" - if(-25 to -10) return "tan" - if(-45 to -25) return "darker skinned" - if(-65 to -45) return "brown" - if(-INFINITY to -65) return "black" - else return "unknown" - else if(species == "Vox") - switch(tone) - if(2) return "dark green" - if(3) return "brown" - if(4) return "gray" - if(5) return "emerald" - if(6) return "azure" - else return "green" - else - return "unknown" - -proc/age2agedescription(age) - switch(age) - if(0 to 1) return "infant" - if(1 to 3) return "toddler" - if(3 to 13) return "child" - if(13 to 19) return "teenager" - if(19 to 30) return "young adult" - if(30 to 45) return "adult" - if(45 to 60) return "middle-aged" - if(60 to 70) return "aging" - if(70 to INFINITY) return "elderly" - else return "unknown" - -/proc/set_criminal_status(mob/living/user, datum/data/record/target_records , criminal_status, comment, user_rank, list/authcard_access = list()) - var/status = criminal_status - var/their_name = target_records.fields["name"] - var/their_rank = target_records.fields["rank"] - switch(criminal_status) - if("arrest") - status = "*Arrest*" - if("none") - status = "None" - if("execute") - if((ACCESS_MAGISTRATE in authcard_access) || (ACCESS_ARMORY in authcard_access)) - status = "*Execute*" - message_admins("[ADMIN_FULLMONTY(usr)] authorized EXECUTION for [their_rank] [their_name], with comment: [comment]") - else - return 0 - if("incarcerated") - status = "Incarcerated" - if("parolled") - status = "Parolled" - if("released") - status = "Released" - target_records.fields["criminal"] = status - log_admin("[key_name_admin(user)] set secstatus of [their_rank] [their_name] to [status], comment: [comment]") - target_records.fields["comments"] += "Set to [status] by [user.name] ([user_rank]) on [current_date_string] [station_time_timestamp()], comment: [comment]" - update_all_mob_security_hud() - return 1 - -/* -Proc for attack log creation, because really why not -1 argument is the actor -2 argument is the target of action -3 is the full description of the action -4 is whether or not to message admins -This is always put in the attack log. -*/ - -/proc/add_attack_logs(mob/user, mob/target, what_done, custom_level) - if(islist(target)) // Multi-victim adding - var/list/targets = target - for(var/mob/M in targets) - add_attack_logs(user, M, what_done, custom_level) - return - - var/user_str = key_name_log(user) + COORD(user) - var/target_str = key_name_log(target) + COORD(target) - - if(istype(user)) - user.create_attack_log("Attacked [target_str]: [what_done]") - if(istype(target)) - target.create_attack_log("Attacked by [user_str]: [what_done]") - log_attack(user_str, target_str, what_done) - - var/loglevel = ATKLOG_MOST - if(!isnull(custom_level)) - loglevel = custom_level - else if(istype(target)) - if(istype(user) && !user.ckey && !target.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL - loglevel = ATKLOG_ALL - else if(!user.ckey || !target.ckey || (user.ckey == target.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares - loglevel = ATKLOG_ALMOSTALL - else - var/area/A = get_area(target) - if(A && A.hide_attacklogs) - loglevel = ATKLOG_ALMOSTALL - if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level - loglevel = ATKLOG_FEW - - msg_admin_attack("[key_name_admin(user)] vs [key_name_admin(target)]: [what_done]", loglevel) - -/proc/do_mob(var/mob/user, var/mob/target, var/time = 30, var/uninterruptible = 0, progress = 1, datum/callback/extra_checks = null) - if(!user || !target) - return 0 - var/user_loc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/target_loc = target.loc - - var/holding = user.get_active_hand() - var/datum/progressbar/progbar - if(progress) - progbar = new(user, time, target) - - var/endtime = world.time+time - var/starttime = world.time - . = 1 - while(world.time < endtime) - sleep(1) - if(progress) - progbar.update(world.time - starttime) - if(!user || !target) - . = 0 - break - if(uninterruptible) - continue - - if(drifting && !user.inertia_dir) - drifting = 0 - user_loc = user.loc - - if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying || (extra_checks && !extra_checks.Invoke())) - . = 0 - break - if(progress) - qdel(progbar) - -/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null) - if(!user) - return 0 - var/atom/Tloc = null - if(target) - Tloc = target.loc - - var/atom/Uloc = user.loc - - var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) - drifting = 1 - - var/holding = user.get_active_hand() - - var/holdingnull = 1 //User's hand started out empty, check for an empty hand - if(holding) - holdingnull = 0 //Users hand started holding something, check to see if it's still holding that - - var/datum/progressbar/progbar - if(progress) - progbar = new(user, delay, target) - - var/endtime = world.time + delay - var/starttime = world.time - . = 1 - while(world.time < endtime) - sleep(1) - if(progress) - progbar.update(world.time - starttime) - - if(drifting && !user.inertia_dir) - drifting = 0 - Uloc = user.loc - - if(!user || user.stat || user.IsWeakened() || user.stunned || (!drifting && user.loc != Uloc)|| (extra_checks && !extra_checks.Invoke())) - . = 0 - break - - if(Tloc && (!target || Tloc != target.loc)) - . = 0 - break - - if(needhand) - //This might seem like an odd check, but you can still need a hand even when it's empty - //i.e the hand is used to pull some item/tool out of the construction - if(!holdingnull) - if(!holding) - . = 0 - break - if(user.get_active_hand() != holding) - . = 0 - break - if(progress) - qdel(progbar) - -#define DOAFTERONCE_MAGIC "Magic~~" -GLOBAL_LIST_INIT(do_after_once_tracker, list()) -/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, attempt_cancel_message = "Attempt cancelled.") - if(!user || !target) - return - - var/cache_key = "[user.UID()][target.UID()]" - if(GLOB.do_after_once_tracker[cache_key]) - GLOB.do_after_once_tracker[cache_key] = DOAFTERONCE_MAGIC - to_chat(user, "[attempt_cancel_message]") - return FALSE - GLOB.do_after_once_tracker[cache_key] = TRUE - . = do_after(user, delay, needhand, target, progress, extra_checks = CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key)) - GLOB.do_after_once_tracker[cache_key] = FALSE - -/proc/do_after_once_checks(cache_key) - if(GLOB.do_after_once_tracker[cache_key] && GLOB.do_after_once_tracker[cache_key] == DOAFTERONCE_MAGIC) - GLOB.do_after_once_tracker[cache_key] = FALSE - return FALSE - return TRUE - -/proc/is_species(A, species_datum) - . = FALSE - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.dna && istype(H.dna.species, species_datum)) - . = TRUE - -/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args) - var/turf/T = get_turf(target) - if(!T) - CRASH("attempt to spawn atom type: [spawn_type] in nullspace") - - var/list/new_args = list(T) - if(extra_args) - new_args += extra_args - - for(var/j in 1 to amount) - var/atom/X = new spawn_type(arglist(new_args)) - X.admin_spawned = admin_spawn - -/proc/admin_mob_info(mob/M, mob/user = usr) - if(!ismob(M)) - to_chat(user, "This can only be used on instances of type /mob") - return - - var/location_description = "" - var/special_role_description = "" - var/health_description = "" - var/gender_description = "" - var/turf/T = get_turf(M) - - //Location - if(isturf(T)) - if(isarea(T.loc)) - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" - else - location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" - - //Job + antagonist - if(M.mind) - special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]" - else - special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;" - - //Health - if(isliving(M)) - var/mob/living/L = M - var/status - switch(M.stat) - if(CONSCIOUS) - status = "Alive" - if(UNCONSCIOUS) - status = "Unconscious" - if(DEAD) - status = "Dead" - health_description = "Status = [status]" - health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]" - else - health_description = "This mob type has no health to speak of." - - //Gener - switch(M.gender) - if(MALE, FEMALE) - gender_description = "[M.gender]" - else - gender_description = "[M.gender]" - - to_chat(user, "Info about [M.name]: ") - to_chat(user, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") - to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") - to_chat(user, "Location = [location_description];") - to_chat(user, "[special_role_description]") - to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])") - -// Gets the first mob contained in an atom, and warns the user if there's not exactly one -/proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr) - if(!istype(A)) - return null - if(ismob(A)) - return A - - . = null - for(var/mob/M in A) - if(!.) - . = M - else - to_chat(user, "Multiple mobs in [A], using first mob found...") - break - if(!.) - to_chat(user, "No mob located in [A].") - -// Suppress the mouse macros -/client/var/next_mouse_macro_warning -/mob/proc/LogMouseMacro(verbused, params) - if(!client) - return - if(!client.next_mouse_macro_warning) // Log once - log_admin("[key_name(usr)] attempted to use a mouse macro: [verbused] [params]") - message_admins("[key_name_admin(usr)] attempted to use a mouse macro: [verbused] [html_encode(params)]") - if(client.next_mouse_macro_warning < world.time) // Warn occasionally - usr << 'sound/misc/sadtrombone.ogg' - client.next_mouse_macro_warning = world.time + 600 -/mob/verb/ClickSubstitute(params as command_text) - set hidden = 1 - set name = ".click" - LogMouseMacro(".click", params) -/mob/verb/DblClickSubstitute(params as command_text) - set hidden = 1 - set name = ".dblclick" - LogMouseMacro(".dblclick", params) -/mob/verb/MouseSubstitute(params as command_text) - set hidden = 1 - set name = ".mouse" - LogMouseMacro(".mouse", params) - -/proc/update_all_mob_security_hud() - for(var/mob/living/carbon/human/H in GLOB.mob_list) - H.sec_hud_set_security_status() - -/proc/getviewsize(view) - var/viewX - var/viewY - if(isnum(view)) - var/totalviewrange = 1 + 2 * view - viewX = totalviewrange - viewY = totalviewrange - else - var/list/viewrangelist = splittext(view, "x") - viewX = text2num(viewrangelist[1]) - viewY = text2num(viewrangelist[2]) - return list(viewX, viewY) - -//Used in chemical_mob_spawn. Generates a random mob based on a given gold_core_spawnable value. -/proc/create_random_mob(spawn_location, mob_class = HOSTILE_SPAWN) - var/static/list/mob_spawn_meancritters = list() // list of possible hostile mobs - var/static/list/mob_spawn_nicecritters = list() // and possible friendly mobs - - if(mob_spawn_meancritters.len <= 0 || mob_spawn_nicecritters.len <= 0) - for(var/T in typesof(/mob/living/simple_animal)) - var/mob/living/simple_animal/SA = T - switch(initial(SA.gold_core_spawnable)) - if(HOSTILE_SPAWN) - mob_spawn_meancritters += T - if(FRIENDLY_SPAWN) - mob_spawn_nicecritters += T - - var/chosen - if(mob_class == FRIENDLY_SPAWN) - chosen = pick(mob_spawn_nicecritters) - else - chosen = pick(mob_spawn_meancritters) - var/mob/living/simple_animal/C = new chosen(spawn_location) - return C \ No newline at end of file +proc/GetOppositeDir(var/dir) + switch(dir) + if(NORTH) return SOUTH + if(SOUTH) return NORTH + if(EAST) return WEST + if(WEST) return EAST + if(SOUTHWEST) return NORTHEAST + if(NORTHWEST) return SOUTHEAST + if(NORTHEAST) return SOUTHWEST + if(SOUTHEAST) return NORTHWEST + return 0 + +proc/random_underwear(gender, species = "Human") + var/list/pick_list = list() + switch(gender) + if(MALE) pick_list = GLOB.underwear_m + if(FEMALE) pick_list = GLOB.underwear_f + else pick_list = GLOB.underwear_list + return pick_species_allowed_underwear(pick_list, species) + +proc/random_undershirt(gender, species = "Human") + var/list/pick_list = list() + switch(gender) + if(MALE) pick_list = GLOB.undershirt_m + if(FEMALE) pick_list = GLOB.undershirt_f + else pick_list = GLOB.undershirt_list + return pick_species_allowed_underwear(pick_list, species) + +proc/random_socks(gender, species = "Human") + var/list/pick_list = list() + switch(gender) + if(MALE) pick_list = GLOB.socks_m + if(FEMALE) pick_list = GLOB.socks_f + else pick_list = GLOB.socks_list + return pick_species_allowed_underwear(pick_list, species) + +proc/pick_species_allowed_underwear(list/all_picks, species) + var/list/valid_picks = list() + for(var/test in all_picks) + var/datum/sprite_accessory/S = all_picks[test] + if(!(species in S.species_allowed)) + continue + valid_picks += test + + if(!valid_picks.len) valid_picks += "Nude" + + return pick(valid_picks) + +proc/random_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) + var/h_style = "Bald" + var/list/valid_hairstyles = list() + for(var/hairstyle in GLOB.hair_styles_public_list) + var/datum/sprite_accessory/S = GLOB.hair_styles_public_list[hairstyle] + + if(hairstyle == "Bald") //Just in case. + valid_hairstyles += hairstyle + continue + if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) + continue + if(species == "Machine") //If the user is a species who can have a robotic head... + if(!robohead) + robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] + if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. + valid_hairstyles += hairstyle //Give them their hairstyles if they do. + else + if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. + But if the user has a robotic humanoid head and the hairstyle can fit humans, let them use it as a wig. */ + valid_hairstyles += hairstyle + else //If the user is not a species who can have robotic heads, use the default handling. + if(species in S.species_allowed) //If the user's head is of a species the hairstyle allows, add it to the list. + valid_hairstyles += hairstyle + + if(valid_hairstyles.len) + h_style = pick(valid_hairstyles) + + return h_style + +proc/random_facial_hair_style(var/gender, species = "Human", var/datum/robolimb/robohead) + var/f_style = "Shaved" + var/list/valid_facial_hairstyles = list() + for(var/facialhairstyle in GLOB.facial_hair_styles_list) + var/datum/sprite_accessory/S = GLOB.facial_hair_styles_list[facialhairstyle] + + if(facialhairstyle == "Shaved") //Just in case. + valid_facial_hairstyles += facialhairstyle + continue + if((gender == MALE && S.gender == FEMALE) || (gender == FEMALE && S.gender == MALE)) + continue + if(species == "Machine") //If the user is a species who can have a robotic head... + if(!robohead) + robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] + if((species in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. + valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. + else + if(!robohead.is_monitor && ("Human" in S.species_allowed)) /*If the facial hairstyle is not native to the user's species and they're using a head with an ipc-style screen, don't let them access it. + But if the user has a robotic humanoid head and the facial hairstyle can fit humans, let them use it as a wig. */ + valid_facial_hairstyles += facialhairstyle + else //If the user is not a species who can have robotic heads, use the default handling. + if(species in S.species_allowed) //If the user's head is of a species the facial hair style allows, add it to the list. + valid_facial_hairstyles += facialhairstyle + + if(valid_facial_hairstyles.len) + f_style = pick(valid_facial_hairstyles) + + return f_style + +proc/random_head_accessory(species = "Human") + var/ha_style = "None" + var/list/valid_head_accessories = list() + for(var/head_accessory in GLOB.head_accessory_styles_list) + var/datum/sprite_accessory/S = GLOB.head_accessory_styles_list[head_accessory] + + if(!(species in S.species_allowed)) + continue + valid_head_accessories += head_accessory + + if(valid_head_accessories.len) + ha_style = pick(valid_head_accessories) + + return ha_style + +proc/random_marking_style(var/location = "body", species = "Human", var/datum/robolimb/robohead, var/body_accessory, var/alt_head) + var/m_style = "None" + var/list/valid_markings = list() + for(var/marking in GLOB.marking_styles_list) + var/datum/sprite_accessory/body_markings/S = GLOB.marking_styles_list[marking] + if(S.name == "None") + valid_markings += marking + continue + if(S.marking_location != location) //If the marking isn't for the location we desire, skip. + continue + if(!(species in S.species_allowed)) //If the user's head is not of a species the marking style allows, skip it. Otherwise, add it to the list. + continue + if(location == "tail") + if(!body_accessory) + if(S.tails_allowed) + continue + else + if(!S.tails_allowed || !(body_accessory in S.tails_allowed)) + continue + if(location == "head") + var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[S.name] + if(species == "Machine")//If the user is a species that can have a robotic head... + if(!robohead) + robohead = GLOB.all_robolimbs["Morpheus Cyberkinetics"] + if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head. + continue + else if(alt_head && alt_head != "None") //If the user's got an alt head, validate markings for that head. + if(!("All" in M.heads_allowed) && !(alt_head in M.heads_allowed)) + continue + else + if(M.heads_allowed && !("All" in M.heads_allowed)) + continue + valid_markings += marking + + if(valid_markings.len) + m_style = pick(valid_markings) + + return m_style + +proc/random_body_accessory(species = "Vulpkanin") + var/body_accessory = null + var/list/valid_body_accessories = list() + for(var/B in GLOB.body_accessory_by_name) + var/datum/body_accessory/A = GLOB.body_accessory_by_name[B] + if(!istype(A)) + valid_body_accessories += "None" //The only null entry should be the "None" option. + continue + if(species in A.allowed_species) //If the user is not of a species the body accessory style allows, skip it. Otherwise, add it to the list. + valid_body_accessories += B + + if(valid_body_accessories.len) + body_accessory = pick(valid_body_accessories) + + return body_accessory + +proc/random_name(gender, species = "Human") + + var/datum/species/current_species + if(species) + current_species = GLOB.all_species[species] + + if(!current_species || current_species.name == "Human") + if(gender==FEMALE) + return capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) + else + return capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) + else + return current_species.get_random_name(gender) + +proc/random_skin_tone(species = "Human") + if(species == "Human" || species == "Drask") + switch(pick(60;"caucasian", 15;"afroamerican", 10;"african", 10;"latino", 5;"albino")) + if("caucasian") . = -10 + if("afroamerican") . = -115 + if("african") . = -165 + if("latino") . = -55 + if("albino") . = 34 + else . = rand(-185, 34) + return min(max(. + rand(-25, 25), -185), 34) + else if(species == "Vox") + . = rand(1, 6) + return . + +proc/skintone2racedescription(tone, species = "Human") + if(species == "Human") + switch(tone) + if(30 to INFINITY) return "albino" + if(20 to 30) return "pale" + if(5 to 15) return "light skinned" + if(-10 to 5) return "white" + if(-25 to -10) return "tan" + if(-45 to -25) return "darker skinned" + if(-65 to -45) return "brown" + if(-INFINITY to -65) return "black" + else return "unknown" + else if(species == "Vox") + switch(tone) + if(2) return "dark green" + if(3) return "brown" + if(4) return "gray" + if(5) return "emerald" + if(6) return "azure" + else return "green" + else + return "unknown" + +proc/age2agedescription(age) + switch(age) + if(0 to 1) return "infant" + if(1 to 3) return "toddler" + if(3 to 13) return "child" + if(13 to 19) return "teenager" + if(19 to 30) return "young adult" + if(30 to 45) return "adult" + if(45 to 60) return "middle-aged" + if(60 to 70) return "aging" + if(70 to INFINITY) return "elderly" + else return "unknown" + +/proc/set_criminal_status(mob/living/user, datum/data/record/target_records , criminal_status, comment, user_rank, list/authcard_access = list()) + var/status = criminal_status + var/their_name = target_records.fields["name"] + var/their_rank = target_records.fields["rank"] + switch(criminal_status) + if("arrest") + status = "*Arrest*" + if("none") + status = "None" + if("execute") + if((ACCESS_MAGISTRATE in authcard_access) || (ACCESS_ARMORY in authcard_access)) + status = "*Execute*" + message_admins("[ADMIN_FULLMONTY(usr)] authorized EXECUTION for [their_rank] [their_name], with comment: [comment]") + else + return 0 + if("incarcerated") + status = "Incarcerated" + if("parolled") + status = "Parolled" + if("released") + status = "Released" + target_records.fields["criminal"] = status + log_admin("[key_name_admin(user)] set secstatus of [their_rank] [their_name] to [status], comment: [comment]") + target_records.fields["comments"] += "Set to [status] by [user.name] ([user_rank]) on [GLOB.current_date_string] [station_time_timestamp()], comment: [comment]" + update_all_mob_security_hud() + return 1 + +/* +Proc for attack log creation, because really why not +1 argument is the actor +2 argument is the target of action +3 is the full description of the action +4 is whether or not to message admins +This is always put in the attack log. +*/ + +/proc/add_attack_logs(atom/user, target, what_done, custom_level) + if(islist(target)) // Multi-victim adding + var/list/targets = target + for(var/mob/M in targets) + add_attack_logs(user, M, what_done, custom_level) + return + + var/user_str = key_name_log(user) + COORD(user) + var/target_str + if(isatom(target)) + var/atom/AT = target + target_str = key_name_log(AT) + COORD(AT) + else + target_str = target + var/mob/MU = user + var/mob/MT = target + if(istype(MU)) + MU.create_log(ATTACK_LOG, what_done, target, get_turf(user)) + MU.create_attack_log("Attacked [target_str]: [what_done]") + if(istype(MT)) + MT.create_log(DEFENSE_LOG, what_done, user, get_turf(MT)) + MT.create_attack_log("Attacked by [user_str]: [what_done]") + log_attack(user_str, target_str, what_done) + + var/loglevel = ATKLOG_MOST + if(!isnull(custom_level)) + loglevel = custom_level + else if(istype(MT)) + if(istype(MU)) + if(!MU.ckey && !MT.ckey) // Attacks between NPCs are only shown to admins with ATKLOG_ALL + loglevel = ATKLOG_ALL + else if(!MU.ckey || !MT.ckey || (MU.ckey == MT.ckey)) // Player v NPC combat is de-prioritized. Also no self-harm, nobody cares + loglevel = ATKLOG_ALMOSTALL + else + var/area/A = get_area(MT) + if(A && A.hide_attacklogs) + loglevel = ATKLOG_ALMOSTALL + if(isLivingSSD(target)) // Attacks on SSDs are shown to admins with any log level except ATKLOG_NONE. Overrides custom level + loglevel = ATKLOG_FEW + + msg_admin_attack("[key_name_admin(user)] vs [key_name_admin(target)]: [what_done]", loglevel) + +/proc/do_mob(mob/user, mob/target, time = 30, uninterruptible = 0, progress = 1, list/extra_checks = list()) + if(!user || !target) + return 0 + var/user_loc = user.loc + + var/drifting = 0 + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = 1 + + var/target_loc = target.loc + + var/holding = user.get_active_hand() + var/datum/progressbar/progbar + if(progress) + progbar = new(user, time, target) + + var/endtime = world.time+time + var/starttime = world.time + . = 1 + while(world.time < endtime) + sleep(1) + if(progress) + progbar.update(world.time - starttime) + if(!user || !target) + . = 0 + break + if(uninterruptible) + continue + + if(drifting && !user.inertia_dir) + drifting = 0 + user_loc = user.loc + + if((!drifting && user.loc != user_loc) || target.loc != target_loc || user.get_active_hand() != holding || user.incapacitated() || user.lying || check_for_true_callbacks(extra_checks)) + . = 0 + break + if(progress) + qdel(progbar) + +/* Use this proc when you want to have code under it execute after a delay, and ensure certain conditions are met during that delay... + * Such as the user not being interrupted via getting stunned or by moving off the tile they're currently on. + * + * Example usage: + * + * if(do_after(user, 50, target = sometarget, extra_checks = list(callback_check1, callback_check2))) + * do_stuff() + * + * This will create progress bar that lasts for 5 seconds. If the user doesn't move or otherwise do something that would cause the checks to fail in those 5 seconds, do_stuff() would execute. + * The Proc returns TRUE upon success (the progress bar reached the end), or FALSE upon failure (the user moved or some other check failed) + */ +/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, list/extra_checks = list(), use_default_checks = TRUE) + if(!user) + return FALSE + var/atom/Tloc = null + if(target) + Tloc = target.loc + + var/atom/Uloc = user.loc + + var/drifting = FALSE + if(!user.Process_Spacemove(0) && user.inertia_dir) + drifting = TRUE + + var/holding = user.get_active_hand() + + var/holdingnull = TRUE //User's hand started out empty, check for an empty hand + if(holding) + holdingnull = FALSE //Users hand started holding something, check to see if it's still holding that + + var/datum/progressbar/progbar + if(progress) + progbar = new(user, delay, target) + + var/endtime = world.time + delay + var/starttime = world.time + . = TRUE + + // By default, checks for weakness and stunned get added to the extra_checks list. + // Setting `use_default_checks` to FALSE means that you don't want the do_after to check for these statuses, or that you will be supplying your own checks. + if(use_default_checks) + extra_checks += CALLBACK(user, /mob.proc/IsWeakened) + extra_checks += CALLBACK(user, /mob.proc/IsStunned) + + while(world.time < endtime) + sleep(1) + if(progress) + progbar.update(world.time - starttime) + + if(drifting && !user.inertia_dir) + drifting = FALSE + Uloc = user.loc + + if(!user || user.stat || (!drifting && user.loc != Uloc) || check_for_true_callbacks(extra_checks)) + . = FALSE + break + + if(Tloc && (!target || Tloc != target.loc)) + . = FALSE + break + + if(needhand) + //This might seem like an odd check, but you can still need a hand even when it's empty + //i.e the hand is used to pull some item/tool out of the construction + if(!holdingnull) + if(!holding) + . = FALSE + break + if(user.get_active_hand() != holding) + . = FALSE + break + if(progress) + qdel(progbar) + +// Upon any of the callbacks in the list returning TRUE, the proc will return TRUE. +/proc/check_for_true_callbacks(list/extra_checks) + for(var/datum/callback/CB in extra_checks) + if(CB.Invoke()) + return TRUE + return FALSE + +#define DOAFTERONCE_MAGIC "Magic~~" +GLOBAL_LIST_INIT(do_after_once_tracker, list()) +/proc/do_after_once(mob/user, delay, needhand = 1, atom/target = null, progress = 1, attempt_cancel_message = "Attempt cancelled.") + if(!user || !target) + return + + var/cache_key = "[user.UID()][target.UID()]" + if(GLOB.do_after_once_tracker[cache_key]) + GLOB.do_after_once_tracker[cache_key] = DOAFTERONCE_MAGIC + to_chat(user, "[attempt_cancel_message]") + return FALSE + GLOB.do_after_once_tracker[cache_key] = TRUE + . = do_after(user, delay, needhand, target, progress, extra_checks = list(CALLBACK(GLOBAL_PROC, .proc/do_after_once_checks, cache_key))) + GLOB.do_after_once_tracker[cache_key] = FALSE + +/proc/do_after_once_checks(cache_key) + if(GLOB.do_after_once_tracker[cache_key] && GLOB.do_after_once_tracker[cache_key] == DOAFTERONCE_MAGIC) + GLOB.do_after_once_tracker[cache_key] = FALSE + return FALSE + return TRUE + +/proc/is_species(A, species_datum) + . = FALSE + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.dna && istype(H.dna.species, species_datum)) + . = TRUE + +/proc/spawn_atom_to_turf(spawn_type, target, amount, admin_spawn=FALSE, list/extra_args) + var/turf/T = get_turf(target) + if(!T) + CRASH("attempt to spawn atom type: [spawn_type] in nullspace") + + var/list/new_args = list(T) + if(extra_args) + new_args += extra_args + + for(var/j in 1 to amount) + var/atom/X = new spawn_type(arglist(new_args)) + X.admin_spawned = admin_spawn + +/proc/admin_mob_info(mob/M, mob/user = usr) + if(!ismob(M)) + to_chat(user, "This can only be used on instances of type /mob") + return + + var/location_description = "" + var/special_role_description = "" + var/health_description = "" + var/gender_description = "" + var/turf/T = get_turf(M) + + //Location + if(isturf(T)) + if(isarea(T.loc)) + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z] in area [T.loc])" + else + location_description = "([M.loc == T ? "at coordinates " : "in [M.loc] at coordinates "] [T.x], [T.y], [T.z])" + + //Job + antagonist + if(M.mind) + special_role_description = "Role: [M.mind.assigned_role]; Antagonist: [M.mind.special_role]; Has been rev: [(M.mind.has_been_rev)?"Yes":"No"]" + else + special_role_description = "Role: Mind datum missing Antagonist: Mind datum missing; Has been rev: Mind datum missing;" + + //Health + if(isliving(M)) + var/mob/living/L = M + var/status + switch(M.stat) + if(CONSCIOUS) + status = "Alive" + if(UNCONSCIOUS) + status = "Unconscious" + if(DEAD) + status = "Dead" + health_description = "Status = [status]" + health_description += "
Oxy: [L.getOxyLoss()] - Tox: [L.getToxLoss()] - Fire: [L.getFireLoss()] - Brute: [L.getBruteLoss()] - Clone: [L.getCloneLoss()] - Brain: [L.getBrainLoss()]" + else + health_description = "This mob type has no health to speak of." + + //Gener + switch(M.gender) + if(MALE, FEMALE) + gender_description = "[M.gender]" + else + gender_description = "[M.gender]" + + to_chat(user, "Info about [M.name]: ") + to_chat(user, "Mob type = [M.type]; Gender = [gender_description] Damage = [health_description]") + to_chat(user, "Name = [M.name]; Real_name = [M.real_name]; Mind_name = [M.mind?"[M.mind.name]":""]; Key = [M.key];") + to_chat(user, "Location = [location_description];") + to_chat(user, "[special_role_description]") + to_chat(user, "(PM) ([ADMIN_PP(M,"PP")]) ([ADMIN_VV(M,"VV")]) ([ADMIN_TP(M,"TP")]) ([ADMIN_SM(M,"SM")]) ([ADMIN_FLW(M,"FLW")])") + +// Gets the first mob contained in an atom, and warns the user if there's not exactly one +/proc/get_mob_in_atom_with_warning(atom/A, mob/user = usr) + if(!istype(A)) + return null + if(ismob(A)) + return A + + . = null + for(var/mob/M in A) + if(!.) + . = M + else + to_chat(user, "Multiple mobs in [A], using first mob found...") + break + if(!.) + to_chat(user, "No mob located in [A].") + +// Suppress the mouse macros +/client/var/next_mouse_macro_warning +/mob/proc/LogMouseMacro(verbused, params) + if(!client) + return + if(!client.next_mouse_macro_warning) // Log once + log_admin("[key_name(usr)] attempted to use a mouse macro: [verbused] [params]") + message_admins("[key_name_admin(usr)] attempted to use a mouse macro: [verbused] [html_encode(params)]") + if(client.next_mouse_macro_warning < world.time) // Warn occasionally + usr << 'sound/misc/sadtrombone.ogg' + client.next_mouse_macro_warning = world.time + 600 +/mob/verb/ClickSubstitute(params as command_text) + set hidden = 1 + set name = ".click" + LogMouseMacro(".click", params) +/mob/verb/DblClickSubstitute(params as command_text) + set hidden = 1 + set name = ".dblclick" + LogMouseMacro(".dblclick", params) +/mob/verb/MouseSubstitute(params as command_text) + set hidden = 1 + set name = ".mouse" + LogMouseMacro(".mouse", params) + +/proc/update_all_mob_security_hud() + for(var/mob/living/carbon/human/H in GLOB.mob_list) + H.sec_hud_set_security_status() + +/proc/getviewsize(view) + var/viewX + var/viewY + if(isnum(view)) + var/totalviewrange = 1 + 2 * view + viewX = totalviewrange + viewY = totalviewrange + else + var/list/viewrangelist = splittext(view, "x") + viewX = text2num(viewrangelist[1]) + viewY = text2num(viewrangelist[2]) + return list(viewX, viewY) + +//Used in chemical_mob_spawn. Generates a random mob based on a given gold_core_spawnable value. +/proc/create_random_mob(spawn_location, mob_class = HOSTILE_SPAWN) + var/static/list/mob_spawn_meancritters = list() // list of possible hostile mobs + var/static/list/mob_spawn_nicecritters = list() // and possible friendly mobs + + if(mob_spawn_meancritters.len <= 0 || mob_spawn_nicecritters.len <= 0) + for(var/T in typesof(/mob/living/simple_animal)) + var/mob/living/simple_animal/SA = T + switch(initial(SA.gold_core_spawnable)) + if(HOSTILE_SPAWN) + mob_spawn_meancritters += T + if(FRIENDLY_SPAWN) + mob_spawn_nicecritters += T + + var/chosen + if(mob_class == FRIENDLY_SPAWN) + chosen = pick(mob_spawn_nicecritters) + else + chosen = pick(mob_spawn_meancritters) + var/mob/living/simple_animal/C = new chosen(spawn_location) + return C diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm index d5a1ad8cbfd5..d702c073b86f 100644 --- a/code/__HELPERS/names.dm +++ b/code/__HELPERS/names.dm @@ -1,282 +1,282 @@ -var/church_name = null -/proc/church_name() - if(church_name) - return church_name - - var/name = "" - - name += pick("Holy", "United", "First", "Second", "Last") - - if(prob(20)) - name += " Space" - - name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") - name += " of [religion_name()]" - - return name - -var/command_name = null -/proc/command_name() - return using_map.dock_name - -var/religion_name = null -/proc/religion_name() - if(religion_name) - return religion_name - - var/name = "" - - name += pick("bee", "science", "edu", "captain", "civilian", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") - name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") - - return capitalize(name) - -/proc/system_name() - return using_map.starsys_name - -/proc/station_name() - return using_map.station_name - -/proc/new_station_name() - var/random = rand(1,5) - var/name = "" - var/new_station_name = "" - - //Rare: Pre-Prefix - if(prob(10)) - name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") - new_station_name = name + " " - name = "" - - // Prefix - for(var/holiday_name in SSholiday.holidays) - if(holiday_name == "Friday the 13th") - random = 13 - var/datum/holiday/holiday = SSholiday.holidays[holiday_name] - name = holiday.getStationPrefix() - //get normal name - if(!name) - name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") - if(name) - new_station_name += name + " " - - // Suffix - name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") - new_station_name += name + " " - - // ID Number - switch(random) - if(1) - new_station_name += "[rand(1, 99)]" - if(2) - new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") - if(3) - new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") - if(4) - new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") - if(5) - new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") - if(13) - new_station_name += pick("13","XIII","Thirteen") - return new_station_name - -var/syndicate_name = null -/proc/syndicate_name() - if(syndicate_name) - return syndicate_name - - var/name = "" - - // Prefix - name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") - - // Suffix - if(prob(80)) - name += " " - - // Full - if(prob(60)) - name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") - // Broken - else - name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") - name += pick("", "-") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") - // Small - else - name += pick("-", "*", "") - name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") - - syndicate_name = name - return name - - -//Traitors and traitor silicons will get these. Revs will not. -GLOBAL_VAR(syndicate_code_phrase) //Code phrase for traitors. -GLOBAL_VAR(syndicate_code_response) //Code response for traitors. - - /* - Should be expanded. - How this works: - Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. - Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." - The phrase should then have the words: James Smith. - The response should then have the words: run, void, and derelict. - This way assures that the code is suited to the conversation and is unpredicatable. - Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. - Can probably be done through "{ }" but I don't really see the practical benefit. - One example of an earlier system is commented below. - /N - */ - -/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm - - var/code_phrase = ""//What is returned when the proc finishes. - var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. - 50; 2, - 200; 3, - 50; 4, - 25; 5 - ) - - var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. - var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") - var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") - var/locations[] = teleportlocs.len ? teleportlocs : drinks//if null, defaults to drinks instead. - - var/names[] = list() - for(var/datum/data/record/t in data_core.general)//Picks from crew manifest. - names += t.fields["name"] - - var/maxwords = words//Extra var to check for duplicates. - - for(words,words>0,words--)//Randomly picks from one of the choices below. - - if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. - safety = list(pick(1,2))//Select choice 1 or 2. - else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, - safety = list(3)//Default to list 3 - - switch(pick(safety))//Chance based on the safety list. - if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. - switch(rand(1,2))//Mainly to add more options later. - if(1) - if(names.len) - code_phrase += pick(names) - if(2) - code_phrase += pick(GLOB.joblist)//Returns a job. - safety -= 1 - if(2) - switch(rand(1,2))//Places or things. - if(1) - code_phrase += pick(drinks) - if(2) - code_phrase += pick(locations) - safety -= 2 - if(3) - switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. - if(1) - code_phrase += pick(nouns) - if(2) - code_phrase += pick(GLOB.adjectives) - if(3) - code_phrase += pick(GLOB.verbs) - if(words==1) - code_phrase += "." - else - code_phrase += ", " - - return code_phrase - -/proc/GenerateKey() - var/newKey - newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") - newKey += pick("diamond", "beer", "mushroom", "civilian", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") - newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") - return newKey - -/* -//This proc tests the gen above. -/client/verb/test_code_phrase() - set name = "Generate Code Phrase" - set category = "Debug" - - to_chat(world, "Code Phrase is: [generate_code_phrase()]") - return - - - This was an earlier attempt at code phrase system, aside from an even earlier attempt (and failure). - This system more or less works as intended--aside from being unfinished--but it's still very predictable. - Particularly, the phrase opening statements are pretty easy to recognize and identify when metagaming. - I think the above-used method solves this issue by using words in a sequence, providing for much greater flexibility. - /N - - switch(choice) - if(1) - syndicate_code_phrase += pick("I'm looking for","Have you seen","Maybe you've seen","I'm trying to find","I'm tracking") - syndicate_code_phrase += " " - syndicate_code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) - syndicate_code_phrase += " " - syndicate_code_phrase += pick(GLOB.last_names) - syndicate_code_phrase += "." - if(2) - syndicate_code_phrase += pick("How do I get to","How do I find","Where is","Where do I find") - syndicate_code_phrase += " " - syndicate_code_phrase += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") - syndicate_code_phrase += "?" - if(3) - if(prob(70)) - syndicate_code_phrase += pick("Get me","I want","I'd like","Make me") - syndicate_code_phrase += " a " - else - syndicate_code_phrase += pick("One") - syndicate_code_phrase += " " - syndicate_code_phrase += pick("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") - syndicate_code_phrase += "." - if(4) - syndicate_code_phrase += pick("I wish I was","My dad was","His mom was","Where do I find","The hero this station needs is","I'd fuck","I wouldn't trust","Someone caught","HoS caught","Someone found","I'd wrestle","I wanna kill") - syndicate_code_phrase += " [pick("a","the")] " - syndicate_code_phrase += pick("wizard","ninja","xeno","lizard","slime","monkey","syndicate","cyborg","clown","space carp","singularity","singulo","mime") - syndicate_code_phrase += "." - if(5) - syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's") - syndicate_code_phrase += " " - syndicate_code_phrase += "[pick(GLOB.joblist)]" - syndicate_code_phrase += "?" - - switch(choice) - if(1) - if(prob(80)) - syndicate_code_response += pick("Try looking for them near","I they ran off to","Yes. I saw them near","Nope. I'm heading to","Try searching") - syndicate_code_response += " " - syndicate_code_response += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") - syndicate_code_response += "." - else if(prob(60)) - syndicate_code_response += pick("No. I'm busy, sorry.","I don't have the time.","Not sure, maybe?","There is no time.") - else - syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") - if(2) - if(prob(80)) - syndicate_code_response += pick("Go to","Navigate to","Try","Sure, run to","Try searching","It's near","It's around") - syndicate_code_response += " the " - syndicate_code_response += pick("[pick("south","north","east","west")] maitenance door","nearby maitenance","teleporter","[pick("cold","dead")] space","morgue","vacuum","[pick("south","north","east","west")] hall ","[pick("south","north","east","west")] hallway","[pick("white","black","red","green","blue","pink","purple")] [pick("rabbit","frog","lion","tiger","panther","snake","facehugger")]") - syndicate_code_response += "." - else if(prob(60)) - syndicate_code_response += pick("Try asking","Ask","Talk to","Go see","Follow","Hunt down") - syndicate_code_response += " " - if(prob(50)) - syndicate_code_response += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) - syndicate_code_response += " " - syndicate_code_response += pick(GLOB.last_names) - else - syndicate_code_response += " the " - syndicate_code_response += "[pic(GLOB.joblist)]" - syndicate_code_response += "." - else - syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") - if(3) - if(4) - if(5) - - return -*/ +GLOBAL_VAR(church_name) +/proc/church_name() + if(GLOB.church_name) + return GLOB.church_name + + var/name = "" + + name += pick("Holy", "United", "First", "Second", "Last") + + if(prob(20)) + name += " Space" + + name += " " + pick("Church", "Cathedral", "Body", "Worshippers", "Movement", "Witnesses") + name += " of [religion_name()]" + + return name + +GLOBAL_VAR(command_name) +/proc/command_name() + return GLOB.using_map.dock_name + +var/religion_name = null +/proc/religion_name() + if(religion_name) + return religion_name + + var/name = "" + + name += pick("bee", "science", "edu", "captain", "civilian", "monkey", "alien", "space", "unit", "sprocket", "gadget", "bomb", "revolution", "beyond", "station", "goon", "robot", "ivor", "hobnob") + name += pick("ism", "ia", "ology", "istism", "ites", "ick", "ian", "ity") + + return capitalize(name) + +/proc/system_name() + return GLOB.using_map.starsys_name + +/proc/station_name() + return GLOB.using_map.station_name + +/proc/new_station_name() + var/random = rand(1,5) + var/name = "" + var/new_station_name = "" + + //Rare: Pre-Prefix + if(prob(10)) + name = pick("Imperium", "Heretical", "Cuban", "Psychic", "Elegant", "Common", "Uncommon", "Rare", "Unique", "Houseruled", "Religious", "Atheist", "Traditional", "Houseruled", "Mad", "Super", "Ultra", "Secret", "Top Secret", "Deep", "Death", "Zybourne", "Central", "Main", "Government", "Uoi", "Fat", "Automated", "Experimental", "Augmented") + new_station_name = name + " " + name = "" + + // Prefix + for(var/holiday_name in SSholiday.holidays) + if(holiday_name == "Friday the 13th") + random = 13 + var/datum/holiday/holiday = SSholiday.holidays[holiday_name] + name = holiday.getStationPrefix() + //get normal name + if(!name) + name = pick("", "Stanford", "Dorf", "Alium", "Prefix", "Clowning", "Aegis", "Ishimura", "Scaredy", "Death-World", "Mime", "Honk", "Rogue", "MacRagge", "Ultrameens", "Safety", "Paranoia", "Explosive", "Neckbear", "Donk", "Muppet", "North", "West", "East", "South", "Slant-ways", "Widdershins", "Rimward", "Expensive", "Procreatory", "Imperial", "Unidentified", "Immoral", "Carp", "Ork", "Pete", "Control", "Nettle", "Aspie", "Class", "Crab", "Fist","Corrogated","Skeleton","Race", "Fatguy", "Gentleman", "Capitalist", "Communist", "Bear", "Beard", "Derp", "Space", "Spess", "Star", "Moon", "System", "Mining", "Neckbeard", "Research", "Supply", "Military", "Orbital", "Battle", "Science", "Asteroid", "Home", "Production", "Transport", "Delivery", "Extraplanetary", "Orbital", "Correctional", "Robot", "Hats", "Pizza") + if(name) + new_station_name += name + " " + + // Suffix + name = pick("Station", "Fortress", "Frontier", "Suffix", "Death-trap", "Space-hulk", "Lab", "Hazard","Spess Junk", "Fishery", "No-Moon", "Tomb", "Crypt", "Hut", "Monkey", "Bomb", "Trade Post", "Fortress", "Village", "Town", "City", "Edition", "Hive", "Complex", "Base", "Facility", "Depot", "Outpost", "Installation", "Drydock", "Observatory", "Array", "Relay", "Monitor", "Platform", "Construct", "Hangar", "Prison", "Center", "Port", "Waystation", "Factory", "Waypoint", "Stopover", "Hub", "HQ", "Office", "Object", "Fortification", "Colony", "Planet-Cracker", "Roost", "Fat Camp") + new_station_name += name + " " + + // ID Number + switch(random) + if(1) + new_station_name += "[rand(1, 99)]" + if(2) + new_station_name += pick("Alpha", "Beta", "Gamma", "Delta", "Epsilon", "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi", "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi", "Omega") + if(3) + new_station_name += pick("II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX") + if(4) + new_station_name += pick("Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu") + if(5) + new_station_name += pick("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen") + if(13) + new_station_name += pick("13","XIII","Thirteen") + return new_station_name + +GLOBAL_VAR(syndicate_name) +/proc/syndicate_name() + if(GLOB.syndicate_name) + return GLOB.syndicate_name + + var/name = "" + + // Prefix + name += pick("Clandestine", "Prima", "Blue", "Zero-G", "Max", "Blasto", "Waffle", "North", "Omni", "Newton", "Cyber", "Bonk", "Gene", "Gib") + + // Suffix + if(prob(80)) + name += " " + + // Full + if(prob(60)) + name += pick("Syndicate", "Consortium", "Collective", "Corporation", "Group", "Holdings", "Biotech", "Industries", "Systems", "Products", "Chemicals", "Enterprises", "Family", "Creations", "International", "Intergalactic", "Interplanetary", "Foundation", "Positronics", "Hive") + // Broken + else + name += pick("Syndi", "Corp", "Bio", "System", "Prod", "Chem", "Inter", "Hive") + name += pick("", "-") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Code") + // Small + else + name += pick("-", "*", "") + name += pick("Tech", "Sun", "Co", "Tek", "X", "Inc", "Gen", "Star", "Dyne", "Code", "Hive") + + GLOB.syndicate_name = name + return name + + +//Traitors and traitor silicons will get these. Revs will not. +GLOBAL_VAR(syndicate_code_phrase) //Code phrase for traitors. +GLOBAL_VAR(syndicate_code_response) //Code response for traitors. + + /* + Should be expanded. + How this works: + Instead of "I'm looking for James Smith," the traitor would say "James Smith" as part of a conversation. + Another traitor may then respond with: "They enjoy running through the void-filled vacuum of the derelict." + The phrase should then have the words: James Smith. + The response should then have the words: run, void, and derelict. + This way assures that the code is suited to the conversation and is unpredicatable. + Obviously, some people will be better at this than others but in theory, everyone should be able to do it and it only enhances roleplay. + Can probably be done through "{ }" but I don't really see the practical benefit. + One example of an earlier system is commented below. + /N + */ + +/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm + + var/code_phrase = ""//What is returned when the proc finishes. + var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely. + 50; 2, + 200; 3, + 50; 4, + 25; 5 + ) + + var/safety[] = list(1,2,3)//Tells the proc which options to remove later on. + var/nouns[] = list("love","hate","anger","peace","pride","sympathy","bravery","loyalty","honesty","integrity","compassion","charity","success","courage","deceit","skill","beauty","brilliance","pain","misery","beliefs","dreams","justice","truth","faith","liberty","knowledge","thought","information","culture","trust","dedication","progress","education","hospitality","leisure","trouble","friendships", "relaxation") + var/drinks[] = list("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") + var/locations[] = GLOB.teleportlocs.len ? GLOB.teleportlocs : drinks//if null, defaults to drinks instead. + + var/names[] = list() + for(var/datum/data/record/t in GLOB.data_core.general)//Picks from crew manifest. + names += t.fields["name"] + + var/maxwords = words//Extra var to check for duplicates. + + for(words,words>0,words--)//Randomly picks from one of the choices below. + + if(words==1&&(1 in safety)&&(2 in safety))//If there is only one word remaining and choice 1 or 2 have not been selected. + safety = list(pick(1,2))//Select choice 1 or 2. + else if(words==1&&maxwords==2)//Else if there is only one word remaining (and there were two originally), and 1 or 2 were chosen, + safety = list(3)//Default to list 3 + + switch(pick(safety))//Chance based on the safety list. + if(1)//1 and 2 can only be selected once each to prevent more than two specific names/places/etc. + switch(rand(1,2))//Mainly to add more options later. + if(1) + if(names.len) + code_phrase += pick(names) + if(2) + code_phrase += pick(GLOB.joblist)//Returns a job. + safety -= 1 + if(2) + switch(rand(1,2))//Places or things. + if(1) + code_phrase += pick(drinks) + if(2) + code_phrase += pick(locations) + safety -= 2 + if(3) + switch(rand(1,3))//Nouns, adjectives, verbs. Can be selected more than once. + if(1) + code_phrase += pick(nouns) + if(2) + code_phrase += pick(GLOB.adjectives) + if(3) + code_phrase += pick(GLOB.verbs) + if(words==1) + code_phrase += "." + else + code_phrase += ", " + + return code_phrase + +/proc/GenerateKey() + var/newKey + newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le") + newKey += pick("diamond", "beer", "mushroom", "civilian", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai") + newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0") + return newKey + +/* +//This proc tests the gen above. +/client/verb/test_code_phrase() + set name = "Generate Code Phrase" + set category = "Debug" + + to_chat(world, "Code Phrase is: [generate_code_phrase()]") + return + + + This was an earlier attempt at code phrase system, aside from an even earlier attempt (and failure). + This system more or less works as intended--aside from being unfinished--but it's still very predictable. + Particularly, the phrase opening statements are pretty easy to recognize and identify when metagaming. + I think the above-used method solves this issue by using words in a sequence, providing for much greater flexibility. + /N + + switch(choice) + if(1) + syndicate_code_phrase += pick("I'm looking for","Have you seen","Maybe you've seen","I'm trying to find","I'm tracking") + syndicate_code_phrase += " " + syndicate_code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) + syndicate_code_phrase += " " + syndicate_code_phrase += pick(GLOB.last_names) + syndicate_code_phrase += "." + if(2) + syndicate_code_phrase += pick("How do I get to","How do I find","Where is","Where do I find") + syndicate_code_phrase += " " + syndicate_code_phrase += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") + syndicate_code_phrase += "?" + if(3) + if(prob(70)) + syndicate_code_phrase += pick("Get me","I want","I'd like","Make me") + syndicate_code_phrase += " a " + else + syndicate_code_phrase += pick("One") + syndicate_code_phrase += " " + syndicate_code_phrase += pick("vodka and tonic","gin fizz","bahama mama","manhattan","black Russian","whiskey soda","long island tea","margarita","Irish coffee"," manly dwarf","Irish cream","doctor's delight","Beepksy Smash","tequila sunrise","brave bull","gargle blaster","bloody mary","whiskey cola","white Russian","vodka martini","martini","Cuba libre","kahlua","vodka","wine","moonshine") + syndicate_code_phrase += "." + if(4) + syndicate_code_phrase += pick("I wish I was","My dad was","His mom was","Where do I find","The hero this station needs is","I'd fuck","I wouldn't trust","Someone caught","HoS caught","Someone found","I'd wrestle","I wanna kill") + syndicate_code_phrase += " [pick("a","the")] " + syndicate_code_phrase += pick("wizard","ninja","xeno","lizard","slime","monkey","syndicate","cyborg","clown","space carp","singularity","singulo","mime") + syndicate_code_phrase += "." + if(5) + syndicate_code_phrase += pick("Do we have","Is there","Where is","Where's","Who's") + syndicate_code_phrase += " " + syndicate_code_phrase += "[pick(GLOB.joblist)]" + syndicate_code_phrase += "?" + + switch(choice) + if(1) + if(prob(80)) + syndicate_code_response += pick("Try looking for them near","I they ran off to","Yes. I saw them near","Nope. I'm heading to","Try searching") + syndicate_code_response += " " + syndicate_code_response += pick("Escape","Engineering","Atmos","the bridge","the brig","Clown Planet","CentComm","the library","the chapel","a bathroom","Med Bay","Tool Storage","the escape shuttle","Robotics","a locker room","the living quarters","the gym","the autolathe","QM","the bar","the theater","the derelict") + syndicate_code_response += "." + else if(prob(60)) + syndicate_code_response += pick("No. I'm busy, sorry.","I don't have the time.","Not sure, maybe?","There is no time.") + else + syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") + if(2) + if(prob(80)) + syndicate_code_response += pick("Go to","Navigate to","Try","Sure, run to","Try searching","It's near","It's around") + syndicate_code_response += " the " + syndicate_code_response += pick("[pick("south","north","east","west")] maitenance door","nearby maitenance","teleporter","[pick("cold","dead")] space","morgue","vacuum","[pick("south","north","east","west")] hall ","[pick("south","north","east","west")] hallway","[pick("white","black","red","green","blue","pink","purple")] [pick("rabbit","frog","lion","tiger","panther","snake","facehugger")]") + syndicate_code_response += "." + else if(prob(60)) + syndicate_code_response += pick("Try asking","Ask","Talk to","Go see","Follow","Hunt down") + syndicate_code_response += " " + if(prob(50)) + syndicate_code_response += pick(pick(GLOB.first_names_male,GLOB.first_names_female)) + syndicate_code_response += " " + syndicate_code_response += pick(GLOB.last_names) + else + syndicate_code_response += " the " + syndicate_code_response += "[pic(GLOB.joblist)]" + syndicate_code_response += "." + else + syndicate_code_response += pick("*shrug*","*smile*","*blink*","*sigh*","*laugh*","*nod*","*giggle*") + if(3) + if(4) + if(5) + + return +*/ diff --git a/code/__HELPERS/qdel.dm b/code/__HELPERS/qdel.dm index 136e573a25e8..020077a4e5c4 100644 --- a/code/__HELPERS/qdel.dm +++ b/code/__HELPERS/qdel.dm @@ -7,4 +7,4 @@ #define QDEL_LIST_ASSOC_VAL(L) if(L) { for(var/I in L) qdel(L[I]); L.Cut(); } /proc/______qdel_list_wrapper(list/L) //the underscores are to encourage people not to use this directly. - QDEL_LIST(L) \ No newline at end of file + QDEL_LIST(L) diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm index 6777f43ff919..34e4c96da6a6 100644 --- a/code/__HELPERS/sanitize_values.dm +++ b/code/__HELPERS/sanitize_values.dm @@ -1,52 +1,52 @@ -//general stuff -/proc/sanitize_integer(number, min=0, max=1, default=0) - if(isnum(number)) - number = round(number) - if(min <= number && number <= max) - return number - return default - -/proc/sanitize_text(text, default="") - if(istext(text)) - return text - return default - -/proc/sanitize_inlist(value, list/List, default) - if(value in List) return value - if(default) return default - if(List && List.len)return pick(List) - - - -//more specialised stuff -/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") - switch(gender) - if(MALE, FEMALE)return gender - if(NEUTER) - if(neuter) return gender - else return default - if(PLURAL) - if(plural) return gender - else return default - return default - -/proc/sanitize_hexcolor(color, default="#000000") - if(!istext(color)) return default - var/len = length(color) - if(len != 7 && len !=4) return default - if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" - . = "#" - for(var/i=2,i<=len,i++) - var/ascii = text2ascii(color,i) - switch(ascii) - if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9 - if(97 to 102) . += ascii2text(ascii) //letters a to f - if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase - else return default - return . - -/proc/sanitize_ooccolor(color) - var/list/HSL = rgb2hsl(hex2num(copytext(color,2,4)),hex2num(copytext(color,4,6)),hex2num(copytext(color,6,8))) - HSL[3] = min(HSL[3],0.4) - var/list/RGB = hsl2rgb(arglist(HSL)) - return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]" +//general stuff +/proc/sanitize_integer(number, min=0, max=1, default=0) + if(isnum(number)) + number = round(number) + if(min <= number && number <= max) + return number + return default + +/proc/sanitize_text(text, default="") + if(istext(text)) + return text + return default + +/proc/sanitize_inlist(value, list/List, default) + if(value in List) return value + if(default) return default + if(List && List.len)return pick(List) + + + +//more specialised stuff +/proc/sanitize_gender(gender,neuter=0,plural=0, default="male") + switch(gender) + if(MALE, FEMALE)return gender + if(NEUTER) + if(neuter) return gender + else return default + if(PLURAL) + if(plural) return gender + else return default + return default + +/proc/sanitize_hexcolor(color, default="#000000") + if(!istext(color)) return default + var/len = length(color) + if(len != 7 && len !=4) return default + if(text2ascii(color,1) != 35) return default //35 is the ascii code for "#" + . = "#" + for(var/i=2,i<=len,i++) + var/ascii = text2ascii(color,i) + switch(ascii) + if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9 + if(97 to 102) . += ascii2text(ascii) //letters a to f + if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase + else return default + return . + +/proc/sanitize_ooccolor(color) + var/list/HSL = rgb2hsl(hex2num(copytext(color,2,4)),hex2num(copytext(color,4,6)),hex2num(copytext(color,6,8))) + HSL[3] = min(HSL[3],0.4) + var/list/RGB = hsl2rgb(arglist(HSL)) + return "#[num2hex(RGB[1],2)][num2hex(RGB[2],2)][num2hex(RGB[3],2)]" diff --git a/code/__HELPERS/sorts/InsertSort.dm b/code/__HELPERS/sorts/InsertSort.dm index decc1c29ff2b..641cf1208229 100644 --- a/code/__HELPERS/sorts/InsertSort.dm +++ b/code/__HELPERS/sorts/InsertSort.dm @@ -16,4 +16,4 @@ SI.associative = associative SI.binarySort(fromIndex, toIndex, fromIndex) - return L \ No newline at end of file + return L diff --git a/code/__HELPERS/sorts/MergeSort.dm b/code/__HELPERS/sorts/MergeSort.dm index d8c3d1477d07..811017f017f9 100644 --- a/code/__HELPERS/sorts/MergeSort.dm +++ b/code/__HELPERS/sorts/MergeSort.dm @@ -16,4 +16,4 @@ SI.associative = associative SI.mergeSort(fromIndex, toIndex) - return L \ No newline at end of file + return L diff --git a/code/__HELPERS/sorts/TimSort.dm b/code/__HELPERS/sorts/TimSort.dm index b3aca97ea992..03063f683a6d 100644 --- a/code/__HELPERS/sorts/TimSort.dm +++ b/code/__HELPERS/sorts/TimSort.dm @@ -17,4 +17,4 @@ SI.associative = associative SI.timSort(fromIndex, toIndex) - return L \ No newline at end of file + return L diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index db41b411d122..ecbebf813df4 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -1,619 +1,619 @@ -/* - * Holds procs designed to help with filtering text - * Contains groups: - * SQL sanitization - * Text sanitization - * Text searches - * Text modification - * Misc - */ - - -/* - * SQL sanitization - */ - -// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. -/proc/sanitizeSQL(var/t as text) - if(isnull(t)) - return null - if(!istext(t)) - t = "[t]" // Just quietly assume any non-texts are supposed to be text - var/sqltext = dbcon.Quote(t); - return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that - -/proc/format_table_name(table as text) - return sqlfdbktableprefix + table - -/* - * Text sanitization - */ -// Can be used almost the same way as normal input for text -/proc/clean_input(Message, Title, Default, mob/user=usr) - var/txt = input(user, Message, Title, Default) as text | null - if(txt) - return html_encode(txt) - -//Simply removes < and > and limits the length of the message -/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) - var/list/strip_chars = list("<",">") - t = copytext(t,1,limit) - for(var/char in strip_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + copytext(t, index+1) - index = findtext(t, char) - return t - -//Removes a few problematic characters -/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) - for(var/char in repl_chars) - t = replacetext(t, char, repl_chars[char]) - return t - -/proc/readd_quotes(var/t) - var/list/repl_chars = list(""" = "\"") - for(var/char in repl_chars) - var/index = findtext(t, char) - while(index) - t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) - index = findtext(t, char) - return t - -//Runs byond's sanitization proc along-side sanitize_simple -/proc/sanitize(var/t,var/list/repl_chars = null) - return html_encode(sanitize_simple(t,repl_chars)) - -// Gut ANYTHING that isnt alphanumeric, or brackets -/proc/paranoid_sanitize(t) - var/regex/alphanum_only = regex("\[^a-zA-Z0-9# ,.?!:;()]", "g") - return alphanum_only.Replace(t, "#") - -// Less agressive, to allow discord features, such as <>, / and @ -/proc/not_as_paranoid_sanitize(t) - var/regex/alphanum_slashes_only = regex("\[^a-zA-Z0-9# ,.?!:;()/<>@]", "g") - return alphanum_slashes_only.Replace(t, "#") - -//Runs sanitize and strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode() -/proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN) - return copytext((sanitize(strip_html_simple(t))),1,limit) - -// Used to get a properly sanitized multiline input, of max_length -/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/name = input(user, message, title, default) as message|null - if(no_trim) - return copytext(html_encode(name), 1, max_length) - else - return trim(html_encode(name), max_length) - -//Runs byond's sanitization proc along-side strip_html_simple -//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause -/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) - return copytext((html_encode(strip_html_simple(t))),1,limit) - - -//Returns null if there is any bad text in the string -/proc/reject_bad_text(var/text, var/max_length=512) - if(length(text) > max_length) return //message too long - var/non_whitespace = 0 - for(var/i=1, i<=length(text), i++) - switch(text2ascii(text,i)) - if(62,60,92,47) return //rejects the text if it contains these bad characters: <, >, \ or / - if(127 to 255) return //rejects weird letters like � - if(0 to 31) return //more weird stuff - if(32) continue //whitespace - else non_whitespace = 1 - if(non_whitespace) return text //only accepts the text if it has some non-spaces - -// Used to get a sanitized input. -/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) - var/name = html_encode(input(user, message, title, default) as text|null) - if(!no_trim) - name = trim(name) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) - return copytext(name, 1, max_length) - -// Uses client.typing to check if the popup should appear or not -/proc/typing_input(mob/user, message = "", title = "", default = "") - if(user.client.checkTyping()) // Prevent double windows - return null - var/client/C = user.client // Save it in a var in case the client disconnects from the mob - C.typing = TRUE - var/msg = input(user, message, title, default) as text|null - if(!C) - return null - C.typing = FALSE - if(!user || C != user.client) // User got out of the mob for some reason or the mob is gone - return null - return msg - -//Filters out undesirable characters from names -/proc/reject_bad_name(var/t_in, var/allow_numbers=0, var/max_length=MAX_NAME_LEN) - // Decode so that names with characters like < are still rejected - t_in = html_decode(t_in) - if(!t_in || length(t_in) > max_length) - return //Rejects the input if it is null or if it is longer than the max length allowed - - var/number_of_alphanumeric = 0 - var/last_char_group = 0 - var/t_out = "" - - for(var/i=1, i<=length(t_in), i++) - var/ascii_char = text2ascii(t_in,i) - switch(ascii_char) - // A .. Z - if(65 to 90) //Uppercase Letters - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // a .. z - if(97 to 122) //Lowercase Letters - if(last_char_group<2) t_out += ascii2text(ascii_char-32) //Force uppercase first character - else t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 4 - - // 0 .. 9 - if(48 to 57) //Numbers - if(!last_char_group) continue //suppress at start of string - if(!allow_numbers) continue - t_out += ascii2text(ascii_char) - number_of_alphanumeric++ - last_char_group = 3 - - // ' - . , - if(39, 45, 46, 44) //Common name punctuation - if(!last_char_group) continue - t_out += ascii2text(ascii_char) - last_char_group = 2 - - // ~ | @ : # $ % & * + ! - if(126, 124, 64, 58, 35, 36, 37, 38, 42, 43, 33) //Other symbols that we'll allow (mainly for AI) - if(!last_char_group) continue //suppress at start of string - if(!allow_numbers) continue - t_out += ascii2text(ascii_char) - last_char_group = 2 - - //Space - if(32) - if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string - t_out += ascii2text(ascii_char) - last_char_group = 1 - else - return - - if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" - - if(last_char_group == 1) - t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) - - for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names - if(cmptext(t_out,bad_name)) return //(not case sensitive) - - return t_out - -//checks text for html tags -//if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm) -//relpaces < with < -proc/checkhtml(var/t) - t = sanitize_simple(t, list("&#"=".")) - var/p = findtext(t,"<",1) - while(p) //going through all the tags - var/start = p++ - var/tag = copytext(t,p, p+1) - if(tag != "/") - while(reject_bad_text(copytext(t, p, p+1), 1)) - tag = copytext(t,start, p) - p++ - tag = copytext(t,start+1, p) - if(!(tag in paper_tag_whitelist)) //if it's unkown tag, disarming it - t = copytext(t,1,start-1) + "<" + copytext(t,start+1) - p = findtext(t,"<",p) - return t -/* - * Text searches - */ - -//Checks the beginning of a string for a specified sub-string -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtext(text, prefix, start, end) - -//Checks the beginning of a string for a specified sub-string. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hasprefix_case(text, prefix) - var/start = 1 - var/end = length(prefix) + 1 - return findtextEx(text, prefix, start, end) - -//Checks the end of a string for a specified substring. -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtext(text, suffix, start, null) - return - -//Checks the end of a string for a specified substring. This proc is case sensitive -//Returns the position of the substring or 0 if it was not found -/proc/dd_hassuffix_case(text, suffix) - var/start = length(text) - length(suffix) - if(start) - return findtextEx(text, suffix, start, null) - -/* - * Text modification - */ -// See bygex.dm -/proc/replace_characters(var/t,var/list/repl_chars) - for(var/char in repl_chars) - t = replacetext(t, char, repl_chars[char]) - return t - -//Strips the first char and returns it and the new string as a list -/proc/strip_first(t) - return list(copytext(t, 1, 2), copytext(t, 2, 0)) - -//Strips the last char and returns it and the new string as a list -/proc/strip_last(t) - return list(copytext(t, 1, length(t)), copytext(t, length(t))) - -//Adds 'u' number of zeros ahead of the text 't' -/proc/add_zero(t, u) - while(length(t) < u) - t = "0[t]" - return t - -//Adds 'u' number of spaces ahead of the text 't' -/proc/add_lspace(t, u) - while(length(t) < u) - t = " [t]" - return t - -//Adds 'u' number of spaces behind the text 't' -/proc/add_tspace(t, u) - while(length(t) < u) - t = "[t] " - return t - -//Returns a string with reserved characters and spaces before the first letter removed -/proc/trim_left(text) - for(var/i = 1 to length(text)) - if(text2ascii(text, i) > 32) - return copytext(text, i) - return "" - -//Returns a string with reserved characters and spaces after the last letter removed -/proc/trim_right(text) - for(var/i = length(text), i > 0, i--) - if(text2ascii(text, i) > 32) - return copytext(text, 1, i + 1) - - return "" - -//Returns a string with reserved characters and spaces before the first word and after the last word removed. -/proc/trim(text) - return trim_left(trim_right(text)) - -//Returns a string with the first element of the string capitalized. -/proc/capitalize(var/t as text) - return uppertext(copytext(t, 1, 2)) + copytext(t, 2) - -//Centers text by adding spaces to either side of the string. -/proc/dd_centertext(message, length) - var/new_message = message - var/size = length(message) - var/delta = length - size - if(size == length) - return new_message - if(size > length) - return copytext(new_message, 1, length + 1) - if(delta == 1) - return new_message + " " - if(delta % 2) - new_message = " " + new_message - delta-- - var/spaces = add_lspace("",delta/2-1) - return spaces + new_message + spaces - -//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose -/proc/dd_limittext(message, length) - var/size = length(message) - if(size <= length) - return message - return copytext(message, 1, length + 1) - - -/proc/stringmerge(var/text,var/compare,replace = "*") -//This proc fills in all spaces with the "replace" var (* by default) with whatever -//is in the other string at the same spot (assuming it is not a replace char). -//This is used for fingerprints - var/newtext = text - if(length(text) != length(compare)) - return 0 - for(var/i = 1, i < length(text), i++) - var/a = copytext(text,i,i+1) - var/b = copytext(compare,i,i+1) -//if it isn't both the same letter, or if they are both the replacement character -//(no way to know what it was supposed to be) - if(a != b) - if(a == replace) //if A is the replacement char - newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) - else if(b == replace) //if B is the replacement char - newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) - else //The lists disagree, Uh-oh! - return 0 - return newtext - -/proc/stringpercent(var/text,character = "*") -//This proc returns the number of chars of the string that is the character -//This is used for detective work to determine fingerprint completion. - if(!text || !character) - return 0 - var/count = 0 - for(var/i = 1, i <= length(text), i++) - var/a = copytext(text,i,i+1) - if(a == character) - count++ - return count - -/proc/reverse_text(var/text = "") - var/new_text = "" - for(var/i = length(text); i > 0; i--) - new_text += copytext(text, i, i+1) - return new_text - -//This proc strips html properly, but it's not lazy like the other procs. -//This means that it doesn't just remove < and > and call it a day. -//Also limit the size of the input, if specified. -/proc/strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) - if(!input) - return - var/opentag = 1 //These store the position of < and > respectively. - var/closetag = 1 - while(1) - opentag = findtext(input, "<") - closetag = findtext(input, ">") - if(closetag && opentag) - if(closetag < opentag) - input = copytext(input, (closetag + 1)) - else - input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) - else if(closetag || opentag) - if(opentag) - input = copytext(input, 1, opentag) - else - input = copytext(input, (closetag + 1)) - else - break - if(max_length) - input = copytext(input,1,max_length) - return sanitize(input, allow_lines ? list("\t" = " ") : list("\n" = " ", "\t" = " ")) - -/proc/trim_strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) - return trim(strip_html_properly(input, max_length, allow_lines)) - -//Used in preferences' SetFlavorText and human's set_flavor verb -//Previews a string of len or less length -/proc/TextPreview(var/string,var/len=40) - if(length(string) <= len) - if(!length(string)) - return "\[...\]" - else - return html_encode(string) //NO DECODED HTML YOU CHUCKLEFUCKS - else - return "[copytext_preserve_html(string, 1, 37)]..." - -//alternative copytext() for encoded text, doesn't break html entities (" and other) -/proc/copytext_preserve_html(var/text, var/first, var/last) - return html_encode(copytext(html_decode(text), first, last)) - -//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). -//Best used for sanitize object names, window titles. -//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - -//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! -/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) - return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) - - -//Replace BYOND text macros with span classes for to_chat -/proc/replace_text_macro(match, code, rest) - var/regex/text_macro = new("(\\xFF.)(.*)$") - return text_macro.Replace(rest, /proc/replace_text_macro) - -/proc/macro2html(text) - var/static/regex/text_macro = new("(\\xFF.)(.*)$") - return text_macro.Replace(text, /proc/replace_text_macro) - -/proc/dmm_encode(text) - // First, go through and nix out any of our escape sequences so we don't leave ourselves open to some escape sequence attack - // Some coder will probably despise me for this, years down the line - - var/list/repl_chars = list("#?qt;", "#?lbr;", "#?rbr;") - for(var/char in repl_chars) - var/index = findtext(text, char) - var/keylength = length(char) - while(index) - log_runtime(EXCEPTION("Bad string given to dmm encoder! [text]")) - // Replace w/ underscore to prevent "{4;" from cheesing the radar - // Should probably also use canon text replacing procs - text = copytext(text, 1, index) + "_" + copytext(text, index+keylength) - index = findtext(text, char) - - // Then, replace characters as normal - var/list/repl_chars_2 = list("\"" = "#?qt;", "{" = "#?lbr;", "}" = "#?rbr;") - for(var/char in repl_chars_2) - var/index = findtext(text, char) - var/keylength = length(char) - while(index) - text = copytext(text, 1, index) + repl_chars_2[char] + copytext(text, index+keylength) - index = findtext(text, char) - return text - - -/proc/dmm_decode(text) - // Replace what we extracted above - var/list/repl_chars = list("#?qt;" = "\"", "#?lbr;" = "{", "#?rbr;" = "}") - for(var/char in repl_chars) - var/index = findtext(text, char) - var/keylength = length(char) - while(index) - text = copytext(text, 1, index) + repl_chars[char] + copytext(text, index+keylength) - index = findtext(text, char) - return text - -//Checks if any of a given list of needles is in the haystack -/proc/text_in_list(haystack, list/needle_list, start=1, end=0) - for(var/needle in needle_list) - if(findtext(haystack, needle, start, end)) - return 1 - return 0 - -//Like above, but case sensitive -/proc/text_in_list_case(haystack, list/needle_list, start=1, end=0) - for(var/needle in needle_list) - if(findtextEx(haystack, needle, start, end)) - return 1 - return 0 - - -// Pencode -/proc/pencode_to_html(text, mob/user, obj/item/pen/P = null, format = 1, sign = 1, fields = 1, deffont = PEN_FONT, signfont = SIGNFONT, crayonfont = CRAYON_FONT, no_font = FALSE) - text = replacetext(text, "\[b\]", "") - text = replacetext(text, "\[/b\]", "") - text = replacetext(text, "\[i\]", "") - text = replacetext(text, "\[/i\]", "") - text = replacetext(text, "\[u\]", "") - text = replacetext(text, "\[/u\]", "") - if(sign) - text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") - if(fields) - text = replacetext(text, "\[field\]", "") - if(format) - text = replacetext(text, "\[h1\]", "

") - text = replacetext(text, "\[/h1\]", "

") - text = replacetext(text, "\[h2\]", "

") - text = replacetext(text, "\[/h2\]", "

") - text = replacetext(text, "\[h3\]", "

") - text = replacetext(text, "\[/h3\]", "

") - text = replacetext(text, "\n", "
") - text = replacetext(text, "\[center\]", "
") - text = replacetext(text, "\[/center\]", "
") - text = replacetext(text, "\[br\]", "
") - text = replacetext(text, "\[large\]", "") - text = replacetext(text, "\[/large\]", "") - - if(istype(P, /obj/item/toy/crayon) || !format) // If it is a crayon, and he still tries to use these, make them empty! - text = replacetext(text, "\[*\]", "") - text = replacetext(text, "\[hr\]", "") - text = replacetext(text, "\[small\]", "") - text = replacetext(text, "\[/small\]", "") - text = replacetext(text, "\[list\]", "") - text = replacetext(text, "\[/list\]", "") - text = replacetext(text, "\[table\]", "") - text = replacetext(text, "\[/table\]", "") - text = replacetext(text, "\[row\]", "") - text = replacetext(text, "\[cell\]", "") - text = replacetext(text, "\[logo\]", "") - if(istype(P, /obj/item/toy/crayon)) - text = "[text]" - else // They are using "not a crayon" - formatting is OK and such - text = replacetext(text, "\[*\]", "
  • ") - text = replacetext(text, "\[hr\]", "
    ") - text = replacetext(text, "\[small\]", "") - text = replacetext(text, "\[/small\]", "") - text = replacetext(text, "\[list\]", "
      ") - text = replacetext(text, "\[/list\]", "
    ") - text = replacetext(text, "\[table\]", "") - text = replacetext(text, "\[/table\]", "
    ") - text = replacetext(text, "\[grid\]", "") - text = replacetext(text, "\[/grid\]", "
    ") - text = replacetext(text, "\[row\]", "") - text = replacetext(text, "\[cell\]", "") - text = replacetext(text, "\[logo\]", "") - text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO - if(!no_font) - if(P) - text = "[text]" - else - text = "[text]" - - text = copytext(text, 1, MAX_PAPER_MESSAGE_LEN) - return text - -/proc/convert_pencode_arg(text, tag, arg) - arg = sanitize_simple(html_encode(arg), list("''"="","\""="", "?"="")) - // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4---css-escape-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values - var/list/style_attacks = list("javascript:", "expression", "byond:", "file:") - - for(var/style_attack in style_attacks) - if(findtext(arg, style_attack)) - // Do not attempt to render dangerous things - return text - - if(tag == "class") - return "" - - if(tag == "style") - return "" - - if(tag == "img") - var/list/img_props = splittext(arg, ";") - if(img_props.len == 3) - return "" - if(img_props.len == 2) - return "" - return "" - - return text - -/proc/admin_pencode_to_html() - var/text = pencode_to_html(arglist(args)) - var/regex/R = new(@"\[(.*?) (.*?)\]", "ge") - text = R.Replace(text, /proc/convert_pencode_arg) - - text = replacetext(text, "\[/class\]", "") - text = replacetext(text, "\[/style\]", "") - text = replacetext(text, "\[/img\]", "") - - return text - -/proc/html_to_pencode(text) - text = replacetext(text, "
    ", "\n") - text = replacetext(text, "
    ", "\[center\]") - text = replacetext(text, "
    ", "\[/center\]") - text = replacetext(text, "
    ", "\[br\]") - text = replacetext(text, "", "\[b\]") - text = replacetext(text, "", "\[/b\]") - text = replacetext(text, "", "\[i\]") - text = replacetext(text, "", "\[/i\]") - text = replacetext(text, "", "\[u\]") - text = replacetext(text, "", "\[/u\]") - text = replacetext(text, "", "\[large\]") - text = replacetext(text, "", "\[field\]") - - text = replacetext(text, "

    ", "\[h1\]") - text = replacetext(text, "

    ", "\[/h1\]") - text = replacetext(text, "

    ", "\[h2\]") - text = replacetext(text, "

    ", "\[/h2\]") - text = replacetext(text, "

    ", "\[h3\]") - text = replacetext(text, "

    ", "\[/h3\]") - - text = replacetext(text, "
  • ", "\[*\]") - text = replacetext(text, "
    ", "\[hr\]") - text = replacetext(text, "", "\[small\]") - text = replacetext(text, "
      ", "\[list\]") - text = replacetext(text, "
    ", "\[/list\]") - text = replacetext(text, "", "\[table\]") - text = replacetext(text, "
    ", "\[/table\]") - text = replacetext(text, "", "\[grid\]") - text = replacetext(text, "
    ", "\[/grid\]") - text = replacetext(text, "", "\[row\]") - text = replacetext(text, "", "\[cell\]") - text = replacetext(text, "", "\[logo\]") - return text - -#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, "")) +/* + * Holds procs designed to help with filtering text + * Contains groups: + * SQL sanitization + * Text sanitization + * Text searches + * Text modification + * Misc + */ + + +/* + * SQL sanitization + */ + +// Run all strings to be used in an SQL query through this proc first to properly escape out injection attempts. +/proc/sanitizeSQL(var/t as text) + if(isnull(t)) + return null + if(!istext(t)) + t = "[t]" // Just quietly assume any non-texts are supposed to be text + var/sqltext = GLOB.dbcon.Quote(t); + return copytext(sqltext, 2, length(sqltext));//Quote() adds quotes around input, we already do that + +/proc/format_table_name(table as text) + return sqlfdbktableprefix + table + +/* + * Text sanitization + */ +// Can be used almost the same way as normal input for text +/proc/clean_input(Message, Title, Default, mob/user=usr) + var/txt = input(user, Message, Title, Default) as text | null + if(txt) + return html_encode(txt) + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Removes a few problematic characters +/proc/sanitize_simple(var/t,var/list/repl_chars = list("\n"="#","\t"="#")) + for(var/char in repl_chars) + t = replacetext(t, char, repl_chars[char]) + return t + +/proc/readd_quotes(var/t) + var/list/repl_chars = list(""" = "\"") + for(var/char in repl_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + repl_chars[char] + copytext(t, index+5) + index = findtext(t, char) + return t + +//Runs byond's sanitization proc along-side sanitize_simple +/proc/sanitize(var/t,var/list/repl_chars = null) + return html_encode(sanitize_simple(t,repl_chars)) + +// Gut ANYTHING that isnt alphanumeric, or brackets +/proc/paranoid_sanitize(t) + var/regex/alphanum_only = regex("\[^a-zA-Z0-9# ,.?!:;()]", "g") + return alphanum_only.Replace(t, "#") + +// Less agressive, to allow discord features, such as <>, / and @ +/proc/not_as_paranoid_sanitize(t) + var/regex/alphanum_slashes_only = regex("\[^a-zA-Z0-9# ,.?!:;()/<>@]", "g") + return alphanum_slashes_only.Replace(t, "#") + +//Runs sanitize and strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' after sanitize() calls byond's html_encode() +/proc/strip_html(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((sanitize(strip_html_simple(t))),1,limit) + +// Used to get a properly sanitized multiline input, of max_length +/proc/stripped_multiline_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = input(user, message, title, default) as message|null + if(no_trim) + return copytext(html_encode(name), 1, max_length) + else + return trim(html_encode(name), max_length) + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) + + +//Returns null if there is any bad text in the string +/proc/reject_bad_text(var/text, var/max_length=512) + if(length(text) > max_length) return //message too long + var/non_whitespace = 0 + for(var/i=1, i<=length(text), i++) + switch(text2ascii(text,i)) + if(62,60,92,47) return //rejects the text if it contains these bad characters: <, >, \ or / + if(127 to 255) return //rejects weird letters like � + if(0 to 31) return //more weird stuff + if(32) continue //whitespace + else non_whitespace = 1 + if(non_whitespace) return text //only accepts the text if it has some non-spaces + +// Used to get a sanitized input. +/proc/stripped_input(mob/user, message = "", title = "", default = "", max_length=MAX_MESSAGE_LEN, no_trim=FALSE) + var/name = html_encode(input(user, message, title, default) as text|null) + if(!no_trim) + name = trim(name) //trim is "outside" because html_encode can expand single symbols into multiple symbols (such as turning < into <) + return copytext(name, 1, max_length) + +// Uses client.typing to check if the popup should appear or not +/proc/typing_input(mob/user, message = "", title = "", default = "") + if(user.client.checkTyping()) // Prevent double windows + return null + var/client/C = user.client // Save it in a var in case the client disconnects from the mob + C.typing = TRUE + var/msg = input(user, message, title, default) as text|null + if(!C) + return null + C.typing = FALSE + if(!user || C != user.client) // User got out of the mob for some reason or the mob is gone + return null + return msg + +//Filters out undesirable characters from names +/proc/reject_bad_name(var/t_in, var/allow_numbers=0, var/max_length=MAX_NAME_LEN) + // Decode so that names with characters like < are still rejected + t_in = html_decode(t_in) + if(!t_in || length(t_in) > max_length) + return //Rejects the input if it is null or if it is longer than the max length allowed + + var/number_of_alphanumeric = 0 + var/last_char_group = 0 + var/t_out = "" + + for(var/i=1, i<=length(t_in), i++) + var/ascii_char = text2ascii(t_in,i) + switch(ascii_char) + // A .. Z + if(65 to 90) //Uppercase Letters + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // a .. z + if(97 to 122) //Lowercase Letters + if(last_char_group<2) t_out += ascii2text(ascii_char-32) //Force uppercase first character + else t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 4 + + // 0 .. 9 + if(48 to 57) //Numbers + if(!last_char_group) continue //suppress at start of string + if(!allow_numbers) continue + t_out += ascii2text(ascii_char) + number_of_alphanumeric++ + last_char_group = 3 + + // ' - . , + if(39, 45, 46, 44) //Common name punctuation + if(!last_char_group) continue + t_out += ascii2text(ascii_char) + last_char_group = 2 + + // ~ | @ : # $ % & * + ! + if(126, 124, 64, 58, 35, 36, 37, 38, 42, 43, 33) //Other symbols that we'll allow (mainly for AI) + if(!last_char_group) continue //suppress at start of string + if(!allow_numbers) continue + t_out += ascii2text(ascii_char) + last_char_group = 2 + + //Space + if(32) + if(last_char_group <= 1) continue //suppress double-spaces and spaces at start of string + t_out += ascii2text(ascii_char) + last_char_group = 1 + else + return + + if(number_of_alphanumeric < 2) return //protects against tiny names like "A" and also names like "' ' ' ' ' ' ' '" + + if(last_char_group == 1) + t_out = copytext(t_out,1,length(t_out)) //removes the last character (in this case a space) + + for(var/bad_name in list("space","floor","wall","r-wall","monkey","unknown","inactive ai","plating")) //prevents these common metagamey names + if(cmptext(t_out,bad_name)) return //(not case sensitive) + + return t_out + +//checks text for html tags +//if tag is not in whitelist (var/list/paper_tag_whitelist in global.dm) +//relpaces < with < +proc/checkhtml(var/t) + t = sanitize_simple(t, list("&#"=".")) + var/p = findtext(t,"<",1) + while(p) //going through all the tags + var/start = p++ + var/tag = copytext(t,p, p+1) + if(tag != "/") + while(reject_bad_text(copytext(t, p, p+1), 1)) + tag = copytext(t,start, p) + p++ + tag = copytext(t,start+1, p) + if(!(tag in GLOB.paper_tag_whitelist)) //if it's unkown tag, disarming it + t = copytext(t,1,start-1) + "<" + copytext(t,start+1) + p = findtext(t,"<",p) + return t +/* + * Text searches + */ + +//Checks the beginning of a string for a specified sub-string +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtext(text, prefix, start, end) + +//Checks the beginning of a string for a specified sub-string. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hasprefix_case(text, prefix) + var/start = 1 + var/end = length(prefix) + 1 + return findtextEx(text, prefix, start, end) + +//Checks the end of a string for a specified substring. +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtext(text, suffix, start, null) + return + +//Checks the end of a string for a specified substring. This proc is case sensitive +//Returns the position of the substring or 0 if it was not found +/proc/dd_hassuffix_case(text, suffix) + var/start = length(text) - length(suffix) + if(start) + return findtextEx(text, suffix, start, null) + +/* + * Text modification + */ +// See bygex.dm +/proc/replace_characters(var/t,var/list/repl_chars) + for(var/char in repl_chars) + t = replacetext(t, char, repl_chars[char]) + return t + +//Strips the first char and returns it and the new string as a list +/proc/strip_first(t) + return list(copytext(t, 1, 2), copytext(t, 2, 0)) + +//Strips the last char and returns it and the new string as a list +/proc/strip_last(t) + return list(copytext(t, 1, length(t)), copytext(t, length(t))) + +//Adds 'u' number of zeros ahead of the text 't' +/proc/add_zero(t, u) + while(length(t) < u) + t = "0[t]" + return t + +//Adds 'u' number of spaces ahead of the text 't' +/proc/add_lspace(t, u) + while(length(t) < u) + t = " [t]" + return t + +//Adds 'u' number of spaces behind the text 't' +/proc/add_tspace(t, u) + while(length(t) < u) + t = "[t] " + return t + +//Returns a string with reserved characters and spaces before the first letter removed +/proc/trim_left(text) + for(var/i = 1 to length(text)) + if(text2ascii(text, i) > 32) + return copytext(text, i) + return "" + +//Returns a string with reserved characters and spaces after the last letter removed +/proc/trim_right(text) + for(var/i = length(text), i > 0, i--) + if(text2ascii(text, i) > 32) + return copytext(text, 1, i + 1) + + return "" + +//Returns a string with reserved characters and spaces before the first word and after the last word removed. +/proc/trim(text) + return trim_left(trim_right(text)) + +//Returns a string with the first element of the string capitalized. +/proc/capitalize(var/t as text) + return uppertext(copytext(t, 1, 2)) + copytext(t, 2) + +//Centers text by adding spaces to either side of the string. +/proc/dd_centertext(message, length) + var/new_message = message + var/size = length(message) + var/delta = length - size + if(size == length) + return new_message + if(size > length) + return copytext(new_message, 1, length + 1) + if(delta == 1) + return new_message + " " + if(delta % 2) + new_message = " " + new_message + delta-- + var/spaces = add_lspace("",delta/2-1) + return spaces + new_message + spaces + +//Limits the length of the text. Note: MAX_MESSAGE_LEN and MAX_NAME_LEN are widely used for this purpose +/proc/dd_limittext(message, length) + var/size = length(message) + if(size <= length) + return message + return copytext(message, 1, length + 1) + + +/proc/stringmerge(var/text,var/compare,replace = "*") +//This proc fills in all spaces with the "replace" var (* by default) with whatever +//is in the other string at the same spot (assuming it is not a replace char). +//This is used for fingerprints + var/newtext = text + if(length(text) != length(compare)) + return 0 + for(var/i = 1, i < length(text), i++) + var/a = copytext(text,i,i+1) + var/b = copytext(compare,i,i+1) +//if it isn't both the same letter, or if they are both the replacement character +//(no way to know what it was supposed to be) + if(a != b) + if(a == replace) //if A is the replacement char + newtext = copytext(newtext,1,i) + b + copytext(newtext, i+1) + else if(b == replace) //if B is the replacement char + newtext = copytext(newtext,1,i) + a + copytext(newtext, i+1) + else //The lists disagree, Uh-oh! + return 0 + return newtext + +/proc/stringpercent(var/text,character = "*") +//This proc returns the number of chars of the string that is the character +//This is used for detective work to determine fingerprint completion. + if(!text || !character) + return 0 + var/count = 0 + for(var/i = 1, i <= length(text), i++) + var/a = copytext(text,i,i+1) + if(a == character) + count++ + return count + +/proc/reverse_text(var/text = "") + var/new_text = "" + for(var/i = length(text); i > 0; i--) + new_text += copytext(text, i, i+1) + return new_text + +//This proc strips html properly, but it's not lazy like the other procs. +//This means that it doesn't just remove < and > and call it a day. +//Also limit the size of the input, if specified. +/proc/strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) + if(!input) + return + var/opentag = 1 //These store the position of < and > respectively. + var/closetag = 1 + while(1) + opentag = findtext(input, "<") + closetag = findtext(input, ">") + if(closetag && opentag) + if(closetag < opentag) + input = copytext(input, (closetag + 1)) + else + input = copytext(input, 1, opentag) + copytext(input, (closetag + 1)) + else if(closetag || opentag) + if(opentag) + input = copytext(input, 1, opentag) + else + input = copytext(input, (closetag + 1)) + else + break + if(max_length) + input = copytext(input,1,max_length) + return sanitize(input, allow_lines ? list("\t" = " ") : list("\n" = " ", "\t" = " ")) + +/proc/trim_strip_html_properly(var/input, var/max_length = MAX_MESSAGE_LEN, allow_lines = 0) + return trim(strip_html_properly(input, max_length, allow_lines)) + +//Used in preferences' SetFlavorText and human's set_flavor verb +//Previews a string of len or less length +/proc/TextPreview(var/string,var/len=40) + if(length(string) <= len) + if(!length(string)) + return "\[...\]" + else + return html_encode(string) //NO DECODED HTML YOU CHUCKLEFUCKS + else + return "[copytext_preserve_html(string, 1, 37)]..." + +//alternative copytext() for encoded text, doesn't break html entities (" and other) +/proc/copytext_preserve_html(var/text, var/first, var/last) + return html_encode(copytext(html_decode(text), first, last)) + +//Run sanitize(), but remove <, >, " first to prevent displaying them as > < &34; in some places, after html_encode(). +//Best used for sanitize object names, window titles. +//If you have a problem with sanitize() in chat, when quotes and >, < are displayed as html entites - +//this is a problem of double-encode(when & becomes &), use sanitize() with encode=0, but not the sanitizeSafe()! +/proc/sanitizeSafe(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) + return sanitize(replace_characters(input, list(">"=" ","<"=" ", "\""="'")), max_length, encode, trim, extra) + + +//Replace BYOND text macros with span classes for to_chat +/proc/replace_text_macro(match, code, rest) + var/regex/text_macro = new("(\\xFF.)(.*)$") + return text_macro.Replace(rest, /proc/replace_text_macro) + +/proc/macro2html(text) + var/static/regex/text_macro = new("(\\xFF.)(.*)$") + return text_macro.Replace(text, /proc/replace_text_macro) + +/proc/dmm_encode(text) + // First, go through and nix out any of our escape sequences so we don't leave ourselves open to some escape sequence attack + // Some coder will probably despise me for this, years down the line + + var/list/repl_chars = list("#?qt;", "#?lbr;", "#?rbr;") + for(var/char in repl_chars) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + log_runtime(EXCEPTION("Bad string given to dmm encoder! [text]")) + // Replace w/ underscore to prevent "{4;" from cheesing the radar + // Should probably also use canon text replacing procs + text = copytext(text, 1, index) + "_" + copytext(text, index+keylength) + index = findtext(text, char) + + // Then, replace characters as normal + var/list/repl_chars_2 = list("\"" = "#?qt;", "{" = "#?lbr;", "}" = "#?rbr;") + for(var/char in repl_chars_2) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + text = copytext(text, 1, index) + repl_chars_2[char] + copytext(text, index+keylength) + index = findtext(text, char) + return text + + +/proc/dmm_decode(text) + // Replace what we extracted above + var/list/repl_chars = list("#?qt;" = "\"", "#?lbr;" = "{", "#?rbr;" = "}") + for(var/char in repl_chars) + var/index = findtext(text, char) + var/keylength = length(char) + while(index) + text = copytext(text, 1, index) + repl_chars[char] + copytext(text, index+keylength) + index = findtext(text, char) + return text + +//Checks if any of a given list of needles is in the haystack +/proc/text_in_list(haystack, list/needle_list, start=1, end=0) + for(var/needle in needle_list) + if(findtext(haystack, needle, start, end)) + return 1 + return 0 + +//Like above, but case sensitive +/proc/text_in_list_case(haystack, list/needle_list, start=1, end=0) + for(var/needle in needle_list) + if(findtextEx(haystack, needle, start, end)) + return 1 + return 0 + + +// Pencode +/proc/pencode_to_html(text, mob/user, obj/item/pen/P = null, format = 1, sign = 1, fields = 1, deffont = PEN_FONT, signfont = SIGNFONT, crayonfont = CRAYON_FONT, no_font = FALSE) + text = replacetext(text, "\[b\]", "") + text = replacetext(text, "\[/b\]", "") + text = replacetext(text, "\[i\]", "") + text = replacetext(text, "\[/i\]", "") + text = replacetext(text, "\[u\]", "") + text = replacetext(text, "\[/u\]", "") + if(sign) + text = replacetext(text, "\[sign\]", "[user ? user.real_name : "Anonymous"]") + if(fields) + text = replacetext(text, "\[field\]", "") + if(format) + text = replacetext(text, "\[h1\]", "

    ") + text = replacetext(text, "\[/h1\]", "

    ") + text = replacetext(text, "\[h2\]", "

    ") + text = replacetext(text, "\[/h2\]", "

    ") + text = replacetext(text, "\[h3\]", "

    ") + text = replacetext(text, "\[/h3\]", "

    ") + text = replacetext(text, "\n", "
    ") + text = replacetext(text, "\[center\]", "
    ") + text = replacetext(text, "\[/center\]", "
    ") + text = replacetext(text, "\[br\]", "
    ") + text = replacetext(text, "\[large\]", "") + text = replacetext(text, "\[/large\]", "") + + if(istype(P, /obj/item/toy/crayon) || !format) // If it is a crayon, and he still tries to use these, make them empty! + text = replacetext(text, "\[*\]", "") + text = replacetext(text, "\[hr\]", "") + text = replacetext(text, "\[small\]", "") + text = replacetext(text, "\[/small\]", "") + text = replacetext(text, "\[list\]", "") + text = replacetext(text, "\[/list\]", "") + text = replacetext(text, "\[table\]", "") + text = replacetext(text, "\[/table\]", "") + text = replacetext(text, "\[row\]", "") + text = replacetext(text, "\[cell\]", "") + text = replacetext(text, "\[logo\]", "") + if(istype(P, /obj/item/toy/crayon)) + text = "[text]" + else // They are using "not a crayon" - formatting is OK and such + text = replacetext(text, "\[*\]", "
  • ") + text = replacetext(text, "\[hr\]", "
    ") + text = replacetext(text, "\[small\]", "") + text = replacetext(text, "\[/small\]", "") + text = replacetext(text, "\[list\]", "
      ") + text = replacetext(text, "\[/list\]", "
    ") + text = replacetext(text, "\[table\]", "") + text = replacetext(text, "\[/table\]", "
    ") + text = replacetext(text, "\[grid\]", "") + text = replacetext(text, "\[/grid\]", "
    ") + text = replacetext(text, "\[row\]", "") + text = replacetext(text, "\[cell\]", "") + text = replacetext(text, "\[logo\]", "") + text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO + if(!no_font) + if(P) + text = "[text]" + else + text = "[text]" + + text = copytext(text, 1, MAX_PAPER_MESSAGE_LEN) + return text + +/proc/convert_pencode_arg(text, tag, arg) + arg = sanitize_simple(html_encode(arg), list("''"="","\""="", "?"="")) + // https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html#rule-4---css-escape-and-strictly-validate-before-inserting-untrusted-data-into-html-style-property-values + var/list/style_attacks = list("javascript:", "expression", "byond:", "file:") + + for(var/style_attack in style_attacks) + if(findtext(arg, style_attack)) + // Do not attempt to render dangerous things + return text + + if(tag == "class") + return "" + + if(tag == "style") + return "" + + if(tag == "img") + var/list/img_props = splittext(arg, ";") + if(img_props.len == 3) + return "" + if(img_props.len == 2) + return "" + return "" + + return text + +/proc/admin_pencode_to_html() + var/text = pencode_to_html(arglist(args)) + var/regex/R = new(@"\[(.*?) (.*?)\]", "ge") + text = R.Replace(text, /proc/convert_pencode_arg) + + text = replacetext(text, "\[/class\]", "") + text = replacetext(text, "\[/style\]", "") + text = replacetext(text, "\[/img\]", "") + + return text + +/proc/html_to_pencode(text) + text = replacetext(text, "
    ", "\n") + text = replacetext(text, "
    ", "\[center\]") + text = replacetext(text, "
    ", "\[/center\]") + text = replacetext(text, "
    ", "\[br\]") + text = replacetext(text, "", "\[b\]") + text = replacetext(text, "", "\[/b\]") + text = replacetext(text, "", "\[i\]") + text = replacetext(text, "", "\[/i\]") + text = replacetext(text, "", "\[u\]") + text = replacetext(text, "", "\[/u\]") + text = replacetext(text, "", "\[large\]") + text = replacetext(text, "", "\[field\]") + + text = replacetext(text, "

    ", "\[h1\]") + text = replacetext(text, "

    ", "\[/h1\]") + text = replacetext(text, "

    ", "\[h2\]") + text = replacetext(text, "

    ", "\[/h2\]") + text = replacetext(text, "

    ", "\[h3\]") + text = replacetext(text, "

    ", "\[/h3\]") + + text = replacetext(text, "
  • ", "\[*\]") + text = replacetext(text, "
    ", "\[hr\]") + text = replacetext(text, "", "\[small\]") + text = replacetext(text, "
      ", "\[list\]") + text = replacetext(text, "
    ", "\[/list\]") + text = replacetext(text, "", "\[table\]") + text = replacetext(text, "
    ", "\[/table\]") + text = replacetext(text, "", "\[grid\]") + text = replacetext(text, "
    ", "\[/grid\]") + text = replacetext(text, "", "\[row\]") + text = replacetext(text, "", "\[cell\]") + text = replacetext(text, "", "\[logo\]") + return text + +#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, "")) diff --git a/code/__HELPERS/time.dm b/code/__HELPERS/time.dm index d13bf08c58be..340d7a3b1346 100644 --- a/code/__HELPERS/time.dm +++ b/code/__HELPERS/time.dm @@ -50,10 +50,24 @@ return "[date_portion]T[time_portion]" /proc/gameTimestamp(format = "hh:mm:ss", wtime=null) - if(!wtime) + if(wtime == null) wtime = world.time return time2text(wtime - GLOB.timezoneOffset, format) +// max hh:mm:ss supported +/proc/timeStampToNum(timestamp) + var/list/splits = text2numlist(timestamp, ":") + . = 0 + var/split_len = length(splits) + for(var/i = 1 to length(splits)) + switch(split_len - i) + if(2) + . += splits[i] HOURS + if(1) + . += splits[i] MINUTES + if(0) + . += splits[i] SECONDS + /* This is used for displaying the "station time" equivelent of a world.time value Calling it with no args will give you the current time, but you can specify a world.time-based value as an argument - You can use this, for example, to do "This will expire at [station_time_at(world.time + 500)]" to display a "station time" expiration date diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm index 7c1d2257e270..467b07a0ddf6 100644 --- a/code/__HELPERS/traits.dm +++ b/code/__HELPERS/traits.dm @@ -65,4 +65,4 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_WATERBREATH "waterbreathing" // common trait sources -#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention \ No newline at end of file +#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index ae7704459b5d..661d09f04129 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -1,393 +1,393 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * file2list - * angle2dir - * angle2text - * worldtime2text - */ - -//Returns an integer given a hex input -/proc/hex2num(hex) - if(!(istext(hex))) - return - - var/num = 0 - var/power = 0 - var/i = null - i = length(hex) - while(i > 0) - var/char = copytext(hex, i, i + 1) - switch(char) - if("0") - //Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist - if("9", "8", "7", "6", "5", "4", "3", "2", "1") - num += text2num(char) * 16 ** power - if("a", "A") - num += 16 ** power * 10 - if("b", "B") - num += 16 ** power * 11 - if("c", "C") - num += 16 ** power * 12 - if("d", "D") - num += 16 ** power * 13 - if("e", "E") - num += 16 ** power * 14 - if("f", "F") - num += 16 ** power * 15 - else - return - power++ - i-- - return num - -//Returns the hex value of a number given a value assumed to be a base-ten value -/proc/num2hex(num, placeholder) - if(!isnum(num)) return - if(placeholder == null) placeholder = 2 - - var/hex = "" - while(num) - var/val = num % 16 - num = round(num / 16) - - if(val > 9) - val = ascii2text(55 + val) // 65 - 70 correspond to "A" - "F" - hex = "[val][hex]" - while(length(hex) < placeholder) - hex = "0[hex]" - return hex || "0" - -//Returns an integer value for R of R/G/B given a hex color input. -/proc/color2R(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 2, 4)) //Returning R - -//Returns an integer value for G of R/G/B given a hex color input. -/proc/color2G(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 4, 6)) //Returning G - -//Returns an integer value for B of R/G/B given a hex color input. -/proc/color2B(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 6, 8)) //Returning B - -/proc/text2numlist(text, delimiter="\n") - var/list/num_list = list() - for(var/x in splittext(text, delimiter)) - num_list += text2num(x) - return num_list - -//Splits the text of a file at seperator and returns them in a list. -/proc/file2list(filename, seperator="\n") - return splittext(return_file_text(filename),seperator) - - -//Turns a direction into text - -/proc/num2dir(direction) - switch(direction) - if(1.0) return NORTH - if(2.0) return SOUTH - if(4.0) return EAST - if(8.0) return WEST - else - log_runtime(EXCEPTION("UNKNOWN DIRECTION: [direction]")) - -/proc/dir2text(direction) - switch(direction) - if(1.0) - return "north" - if(2.0) - return "south" - if(4.0) - return "east" - if(8.0) - return "west" - if(5.0) - return "northeast" - if(6.0) - return "southeast" - if(9.0) - return "northwest" - if(10.0) - return "southwest" - else - return - -//Turns text into proper directions -/proc/text2dir(direction) - switch(uppertext(direction)) - if("NORTH") - return 1 - if("SOUTH") - return 2 - if("EAST") - return 4 - if("WEST") - return 8 - if("NORTHEAST") - return 5 - if("NORTHWEST") - return 9 - if("SOUTHEAST") - return 6 - if("SOUTHWEST") - return 10 - else - return - -//Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(var/degree) - degree = ((degree+22.5)%365) - if(degree < 45) return NORTH - if(degree < 90) return NORTHEAST - if(degree < 135) return EAST - if(degree < 180) return SOUTHEAST - if(degree < 225) return SOUTH - if(degree < 270) return SOUTHWEST - if(degree < 315) return WEST - return NORTH|WEST - -/proc/angle2dir_cardinal(angle) - switch(round(angle, 0.1)) - if(315.5 to 360, 0 to 45.5) - return NORTH - if(45.6 to 135.5) - return EAST - if(135.6 to 225.5) - return SOUTH - if(225.6 to 315.5) - return WEST - -//returns the north-zero clockwise angle in degrees, given a direction - -/proc/dir2angle(var/D) - switch(D) - if(NORTH) return 0 - if(SOUTH) return 180 - if(EAST) return 90 - if(WEST) return 270 - if(NORTHEAST) return 45 - if(SOUTHEAST) return 135 - if(NORTHWEST) return 315 - if(SOUTHWEST) return 225 - else return null - -//Returns the angle in english -/proc/angle2text(var/degree) - return dir2text(angle2dir(degree)) - -//Converts a blend_mode constant to one acceptable to icon.Blend() -/proc/blendMode2iconMode(blend_mode) - switch(blend_mode) - if(BLEND_MULTIPLY) return ICON_MULTIPLY - if(BLEND_ADD) return ICON_ADD - if(BLEND_SUBTRACT) return ICON_SUBTRACT - else return ICON_OVERLAY - -//Converts a rights bitfield into a string -/proc/rights2text(rights,seperator="") - if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" - if(rights & R_ADMIN) . += "[seperator]+ADMIN" - if(rights & R_BAN) . += "[seperator]+BAN" - if(rights & R_EVENT) . += "[seperator]+EVENT" - if(rights & R_SERVER) . += "[seperator]+SERVER" - if(rights & R_DEBUG) . += "[seperator]+DEBUG" - if(rights & R_POSSESS) . += "[seperator]+POSSESS" - if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" - if(rights & R_STEALTH) . += "[seperator]+STEALTH" - if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" - if(rights & R_VAREDIT) . += "[seperator]+VAREDIT" - if(rights & R_SOUNDS) . += "[seperator]+SOUND" - if(rights & R_SPAWN) . += "[seperator]+SPAWN" - if(rights & R_PROCCALL) . += "[seperator]+PROCCALL" - if(rights & R_MOD) . += "[seperator]+MODERATOR" - if(rights & R_MENTOR) . += "[seperator]+MENTOR" - return . - -/proc/ui_style2icon(ui_style) - switch(ui_style) - if("Retro") - return 'icons/mob/screen_retro.dmi' - if("Plasmafire") - return 'icons/mob/screen_plasmafire.dmi' - if("Slimecore") - return 'icons/mob/screen_slimecore.dmi' - if("Operative") - return 'icons/mob/screen_operative.dmi' - if("White") - return 'icons/mob/screen_white.dmi' - else - return 'icons/mob/screen_midnight.dmi' - -//colour formats -/proc/rgb2hsl(red, green, blue) - red /= 255;green /= 255;blue /= 255; - var/max = max(red,green,blue) - var/min = min(red,green,blue) - var/range = max-min - - var/hue=0;var/saturation=0;var/lightness=0; - lightness = (max + min)/2 - if(range != 0) - if(lightness < 0.5) saturation = range/(max+min) - else saturation = range/(2-max-min) - - var/dred = ((max-red)/(6*max)) + 0.5 - var/dgreen = ((max-green)/(6*max)) + 0.5 - var/dblue = ((max-blue)/(6*max)) + 0.5 - - if(max==red) hue = dblue - dgreen - else if(max==green) hue = dred - dblue + (1/3) - else hue = dgreen - dred + (2/3) - if(hue < 0) hue++ - else if(hue > 1) hue-- - - return list(hue, saturation, lightness) - -/proc/hsl2rgb(hue, saturation, lightness) - var/red;var/green;var/blue; - if(saturation == 0) - red = lightness * 255 - green = red - blue = red - else - var/a;var/b; - if(lightness < 0.5) b = lightness*(1+saturation) - else b = (lightness+saturation) - (saturation*lightness) - a = 2*lightness - b - - red = round(255 * hue2rgb(a, b, hue+(1/3))) - green = round(255 * hue2rgb(a, b, hue)) - blue = round(255 * hue2rgb(a, b, hue-(1/3))) - - return list(red, green, blue) - -/proc/hue2rgb(a, b, hue) - if(hue < 0) hue++ - else if(hue > 1) hue-- - if(6*hue < 1) return (a+(b-a)*6*hue) - if(2*hue < 1) return b - if(3*hue < 2) return (a+(b-a)*((2/3)-hue)*6) - return a - -/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000) - var/finalNum = num2text(theNum, sigFig) - - // Start from the end, or from the decimal point - var/end = findtextEx(finalNum, ".") || length(finalNum) + 1 - - // Moving towards start of string, insert comma every 3 characters - for(var/pos = end - 3, pos > 1, pos -= 3) - finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos) - - return finalNum - - -// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ -/proc/heat2color(temp) - return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) - -/proc/heat2color_r(temp) - temp /= 100 - if(temp <= 66) - . = 255 - else - . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) - -/proc/heat2color_g(temp) - temp /= 100 - if(temp <= 66) - . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) - else - . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) - -/proc/heat2color_b(temp) - temp /= 100 - if(temp >= 66) - . = 255 - else - if(temp <= 16) - . = 0 - else - . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) - -//Argument: Give this a space-separated string consisting of 6 numbers. Returns null if you don't -/proc/text2matrix(var/matrixtext) - var/list/matrixtext_list = splittext(matrixtext, " ") - var/list/matrix_list = list() - for(var/item in matrixtext_list) - var/entry = text2num(item) - if(entry == null) - return null - matrix_list += entry - if(matrix_list.len < 6) - return null - var/a = matrix_list[1] - var/b = matrix_list[2] - var/c = matrix_list[3] - var/d = matrix_list[4] - var/e = matrix_list[5] - var/f = matrix_list[6] - return matrix(a, b, c, d, e, f) - - -//This is a weird one: -//It returns a list of all var names found in the string -//These vars must be in the [var_name] format -//It's only a proc because it's used in more than one place - -//Takes a string and a datum -//The string is well, obviously the string being checked -//The datum is used as a source for var names, to check validity -//Otherwise every single word could technically be a variable! -/proc/string2listofvars(var/t_string, var/datum/var_source) - if(!t_string || !var_source) - return list() - - . = list() - - var/var_found = findtext(t_string, "\[") //Not the actual variables, just a generic "should we even bother" check - if(var_found) - //Find var names - - // "A dog said hi [name]!" - // splittext() --> list("A dog said hi ","name]!" - // jointext() --> "A dog said hi name]!" - // splittext() --> list("A","dog","said","hi","name]!") - - t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios - var/list/list_value = splittext(t_string, "\[") - var/intermediate_stage = jointext(list_value, null) - - list_value = splittext(intermediate_stage, " ") - for(var/value in list_value) - if(findtext(value, "]")) - value = splittext(value, "]") //"name]!" --> list("name","!") - for(var/A in value) - if(var_source.vars.Find(A)) - . += A - -/proc/type2parent(child) - var/string_type = "[child]" - var/last_slash = findlasttext(string_type, "/") - if(last_slash == 1) - switch(child) - if(/datum) - return null - if(/obj || /mob) - return /atom/movable - if(/area || /turf) - return /atom - else - return /datum - return text2path(copytext(string_type, 1, last_slash)) \ No newline at end of file +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * file2list + * angle2dir + * angle2text + * worldtime2text + */ + +//Returns an integer given a hex input +/proc/hex2num(hex) + if(!(istext(hex))) + return + + var/num = 0 + var/power = 0 + var/i = null + i = length(hex) + while(i > 0) + var/char = copytext(hex, i, i + 1) + switch(char) + if("0") + //Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist + if("9", "8", "7", "6", "5", "4", "3", "2", "1") + num += text2num(char) * 16 ** power + if("a", "A") + num += 16 ** power * 10 + if("b", "B") + num += 16 ** power * 11 + if("c", "C") + num += 16 ** power * 12 + if("d", "D") + num += 16 ** power * 13 + if("e", "E") + num += 16 ** power * 14 + if("f", "F") + num += 16 ** power * 15 + else + return + power++ + i-- + return num + +//Returns the hex value of a number given a value assumed to be a base-ten value +/proc/num2hex(num, placeholder) + if(!isnum(num)) return + if(placeholder == null) placeholder = 2 + + var/hex = "" + while(num) + var/val = num % 16 + num = round(num / 16) + + if(val > 9) + val = ascii2text(55 + val) // 65 - 70 correspond to "A" - "F" + hex = "[val][hex]" + while(length(hex) < placeholder) + hex = "0[hex]" + return hex || "0" + +//Returns an integer value for R of R/G/B given a hex color input. +/proc/color2R(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 2, 4)) //Returning R + +//Returns an integer value for G of R/G/B given a hex color input. +/proc/color2G(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 4, 6)) //Returning G + +//Returns an integer value for B of R/G/B given a hex color input. +/proc/color2B(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 6, 8)) //Returning B + +/proc/text2numlist(text, delimiter="\n") + var/list/num_list = list() + for(var/x in splittext(text, delimiter)) + num_list += text2num(x) + return num_list + +//Splits the text of a file at seperator and returns them in a list. +/proc/file2list(filename, seperator="\n") + return splittext(return_file_text(filename),seperator) + + +//Turns a direction into text + +/proc/num2dir(direction) + switch(direction) + if(1.0) return NORTH + if(2.0) return SOUTH + if(4.0) return EAST + if(8.0) return WEST + else + log_runtime(EXCEPTION("UNKNOWN DIRECTION: [direction]")) + +/proc/dir2text(direction) + switch(direction) + if(1.0) + return "north" + if(2.0) + return "south" + if(4.0) + return "east" + if(8.0) + return "west" + if(5.0) + return "northeast" + if(6.0) + return "southeast" + if(9.0) + return "northwest" + if(10.0) + return "southwest" + else + return + +//Turns text into proper directions +/proc/text2dir(direction) + switch(uppertext(direction)) + if("NORTH") + return 1 + if("SOUTH") + return 2 + if("EAST") + return 4 + if("WEST") + return 8 + if("NORTHEAST") + return 5 + if("NORTHWEST") + return 9 + if("SOUTHEAST") + return 6 + if("SOUTHWEST") + return 10 + else + return + +//Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(var/degree) + degree = ((degree+22.5)%365) + if(degree < 45) return NORTH + if(degree < 90) return NORTHEAST + if(degree < 135) return EAST + if(degree < 180) return SOUTHEAST + if(degree < 225) return SOUTH + if(degree < 270) return SOUTHWEST + if(degree < 315) return WEST + return NORTH|WEST + +/proc/angle2dir_cardinal(angle) + switch(round(angle, 0.1)) + if(315.5 to 360, 0 to 45.5) + return NORTH + if(45.6 to 135.5) + return EAST + if(135.6 to 225.5) + return SOUTH + if(225.6 to 315.5) + return WEST + +//returns the north-zero clockwise angle in degrees, given a direction + +/proc/dir2angle(var/D) + switch(D) + if(NORTH) return 0 + if(SOUTH) return 180 + if(EAST) return 90 + if(WEST) return 270 + if(NORTHEAST) return 45 + if(SOUTHEAST) return 135 + if(NORTHWEST) return 315 + if(SOUTHWEST) return 225 + else return null + +//Returns the angle in english +/proc/angle2text(var/degree) + return dir2text(angle2dir(degree)) + +//Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch(blend_mode) + if(BLEND_MULTIPLY) return ICON_MULTIPLY + if(BLEND_ADD) return ICON_ADD + if(BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY + +//Converts a rights bitfield into a string +/proc/rights2text(rights,seperator="") + if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" + if(rights & R_ADMIN) . += "[seperator]+ADMIN" + if(rights & R_BAN) . += "[seperator]+BAN" + if(rights & R_EVENT) . += "[seperator]+EVENT" + if(rights & R_SERVER) . += "[seperator]+SERVER" + if(rights & R_DEBUG) . += "[seperator]+DEBUG" + if(rights & R_POSSESS) . += "[seperator]+POSSESS" + if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" + if(rights & R_STEALTH) . += "[seperator]+STEALTH" + if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" + if(rights & R_VAREDIT) . += "[seperator]+VAREDIT" + if(rights & R_SOUNDS) . += "[seperator]+SOUND" + if(rights & R_SPAWN) . += "[seperator]+SPAWN" + if(rights & R_PROCCALL) . += "[seperator]+PROCCALL" + if(rights & R_MOD) . += "[seperator]+MODERATOR" + if(rights & R_MENTOR) . += "[seperator]+MENTOR" + return . + +/proc/ui_style2icon(ui_style) + switch(ui_style) + if("Retro") + return 'icons/mob/screen_retro.dmi' + if("Plasmafire") + return 'icons/mob/screen_plasmafire.dmi' + if("Slimecore") + return 'icons/mob/screen_slimecore.dmi' + if("Operative") + return 'icons/mob/screen_operative.dmi' + if("White") + return 'icons/mob/screen_white.dmi' + else + return 'icons/mob/screen_midnight.dmi' + +//colour formats +/proc/rgb2hsl(red, green, blue) + red /= 255;green /= 255;blue /= 255; + var/max = max(red,green,blue) + var/min = min(red,green,blue) + var/range = max-min + + var/hue=0;var/saturation=0;var/lightness=0; + lightness = (max + min)/2 + if(range != 0) + if(lightness < 0.5) saturation = range/(max+min) + else saturation = range/(2-max-min) + + var/dred = ((max-red)/(6*max)) + 0.5 + var/dgreen = ((max-green)/(6*max)) + 0.5 + var/dblue = ((max-blue)/(6*max)) + 0.5 + + if(max==red) hue = dblue - dgreen + else if(max==green) hue = dred - dblue + (1/3) + else hue = dgreen - dred + (2/3) + if(hue < 0) hue++ + else if(hue > 1) hue-- + + return list(hue, saturation, lightness) + +/proc/hsl2rgb(hue, saturation, lightness) + var/red;var/green;var/blue; + if(saturation == 0) + red = lightness * 255 + green = red + blue = red + else + var/a;var/b; + if(lightness < 0.5) b = lightness*(1+saturation) + else b = (lightness+saturation) - (saturation*lightness) + a = 2*lightness - b + + red = round(255 * hue2rgb(a, b, hue+(1/3))) + green = round(255 * hue2rgb(a, b, hue)) + blue = round(255 * hue2rgb(a, b, hue-(1/3))) + + return list(red, green, blue) + +/proc/hue2rgb(a, b, hue) + if(hue < 0) hue++ + else if(hue > 1) hue-- + if(6*hue < 1) return (a+(b-a)*6*hue) + if(2*hue < 1) return b + if(3*hue < 2) return (a+(b-a)*((2/3)-hue)*6) + return a + +/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000) + var/finalNum = num2text(theNum, sigFig) + + // Start from the end, or from the decimal point + var/end = findtextEx(finalNum, ".") || length(finalNum) + 1 + + // Moving towards start of string, insert comma every 3 characters + for(var/pos = end - 3, pos > 1, pos -= 3) + finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos) + + return finalNum + + +// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ +/proc/heat2color(temp) + return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) + +/proc/heat2color_r(temp) + temp /= 100 + if(temp <= 66) + . = 255 + else + . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) + +/proc/heat2color_g(temp) + temp /= 100 + if(temp <= 66) + . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) + else + . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) + +/proc/heat2color_b(temp) + temp /= 100 + if(temp >= 66) + . = 255 + else + if(temp <= 16) + . = 0 + else + . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) + +//Argument: Give this a space-separated string consisting of 6 numbers. Returns null if you don't +/proc/text2matrix(var/matrixtext) + var/list/matrixtext_list = splittext(matrixtext, " ") + var/list/matrix_list = list() + for(var/item in matrixtext_list) + var/entry = text2num(item) + if(entry == null) + return null + matrix_list += entry + if(matrix_list.len < 6) + return null + var/a = matrix_list[1] + var/b = matrix_list[2] + var/c = matrix_list[3] + var/d = matrix_list[4] + var/e = matrix_list[5] + var/f = matrix_list[6] + return matrix(a, b, c, d, e, f) + + +//This is a weird one: +//It returns a list of all var names found in the string +//These vars must be in the [var_name] format +//It's only a proc because it's used in more than one place + +//Takes a string and a datum +//The string is well, obviously the string being checked +//The datum is used as a source for var names, to check validity +//Otherwise every single word could technically be a variable! +/proc/string2listofvars(var/t_string, var/datum/var_source) + if(!t_string || !var_source) + return list() + + . = list() + + var/var_found = findtext(t_string, "\[") //Not the actual variables, just a generic "should we even bother" check + if(var_found) + //Find var names + + // "A dog said hi [name]!" + // splittext() --> list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string, "\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage, " ") + for(var/value in list_value) + if(findtext(value, "]")) + value = splittext(value, "]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A + +/proc/type2parent(child) + var/string_type = "[child]" + var/last_slash = findlasttext(string_type, "/") + if(last_slash == 1) + switch(child) + if(/datum) + return null + if(/obj || /mob) + return /atom/movable + if(/area || /turf) + return /atom + else + return /datum + return text2path(copytext(string_type, 1, last_slash)) diff --git a/code/__HELPERS/typelists.dm b/code/__HELPERS/typelists.dm index f271b9204ded..3519eb60f3aa 100644 --- a/code/__HELPERS/typelists.dm +++ b/code/__HELPERS/typelists.dm @@ -40,4 +40,4 @@ GLOBAL_LIST_EMPTY(typelistkeys) for (var/saving in savings) to_chat(world, "Savings for [saving]: [savings[saving]] lists, [saveditems[saving]] items") -#endif \ No newline at end of file +#endif diff --git a/code/__HELPERS/unique_ids.dm b/code/__HELPERS/unique_ids.dm index a66de0b5a884..549f5c710165 100644 --- a/code/__HELPERS/unique_ids.dm +++ b/code/__HELPERS/unique_ids.dm @@ -14,7 +14,7 @@ // var/myUID = mydatum.UID() // var/datum/D = locateUID(myUID) -var/global/next_unique_datum_id = 1 +GLOBAL_VAR_INIT(next_unique_datum_id, 1) // /client/var/tmp/unique_datum_id = null @@ -22,7 +22,7 @@ var/global/next_unique_datum_id = 1 if(!unique_datum_id) var/tag_backup = tag tag = null // Grab the raw ref, not the tag - unique_datum_id = "\ref[src]_[next_unique_datum_id++]" + unique_datum_id = "\ref[src]_[GLOB.next_unique_datum_id++]" tag = tag_backup return unique_datum_id diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 45283498ebda..aedc16c41621 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1129,9 +1129,9 @@ proc/get_mob_with_client_list() //For objects that should embed, but make no sense being is_sharp or is_pointed() //e.g: rods -var/list/can_embed_types = typecacheof(list( +GLOBAL_LIST_INIT(can_embed_types, typecacheof(list( /obj/item/stack/rods, - /obj/item/pipe)) + /obj/item/pipe))) /proc/can_embed(obj/item/W) if(is_sharp(W)) @@ -1139,7 +1139,7 @@ var/list/can_embed_types = typecacheof(list( if(is_pointed(W)) return 1 - if(is_type_in_typecache(W, can_embed_types)) + if(is_type_in_typecache(W, GLOB.can_embed_types)) return 1 /proc/is_hot(obj/item/W as obj) @@ -1221,18 +1221,18 @@ var/list/can_embed_types = typecacheof(list( /* Checks if that loc and dir has a item on the wall */ -var/list/static/global/wall_items = typecacheof(list(/obj/machinery/power/apc, /obj/machinery/alarm, +GLOBAL_LIST_INIT(wall_items, typecacheof(list(/obj/machinery/power/apc, /obj/machinery/alarm, /obj/item/radio/intercom, /obj/structure/extinguisher_cabinet, /obj/structure/reagent_dispensers/peppertank, /obj/machinery/status_display, /obj/machinery/requests_console, /obj/machinery/light_switch, /obj/structure/sign, /obj/machinery/newscaster, /obj/machinery/firealarm, /obj/structure/noticeboard, /obj/machinery/door_control, /obj/machinery/computer/security/telescreen, /obj/machinery/embedded_controller/radio/airlock, /obj/item/storage/secure/safe, /obj/machinery/door_timer, /obj/machinery/flasher, /obj/machinery/keycard_auth, /obj/structure/mirror, /obj/structure/closet/fireaxecabinet, /obj/machinery/computer/security/telescreen/entertainment, - /obj/structure/sign)) + /obj/structure/sign))) /proc/gotwallitem(loc, dir) for(var/obj/O in loc) - if(is_type_in_typecache(O, wall_items)) + if(is_type_in_typecache(O, GLOB.wall_items)) //Direction works sometimes if(O.dir == dir) return 1 @@ -1254,7 +1254,7 @@ var/list/static/global/wall_items = typecacheof(list(/obj/machinery/power/apc, / //Some stuff is placed directly on the wallturf (signs) for(var/obj/O in get_step(loc, dir)) - if(is_type_in_typecache(O, wall_items)) + if(is_type_in_typecache(O, GLOB.wall_items)) if(abs(O.pixel_x) <= 10 && abs(O.pixel_y) <= 10) return 1 return 0 @@ -1381,7 +1381,7 @@ atom/proc/GetTypeInAllContents(typepath) var/initial_chance = chance while(steps > 0) if(prob(chance)) - step(AM, pick(alldirs)) + step(AM, pick(GLOB.alldirs)) chance = max(chance - (initial_chance / steps), 0) steps-- @@ -1415,19 +1415,19 @@ atom/proc/GetTypeInAllContents(typepath) return locate(dest_x,dest_y,dest_z) -var/mob/dview/dview_mob = new +GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) //Version of view() which ignores darkness, because BYOND doesn't have it. /proc/dview(var/range = world.view, var/center, var/invis_flags = 0) if(!center) return - dview_mob.loc = center + GLOB.dview_mob.loc = center - dview_mob.see_invisible = invis_flags + GLOB.dview_mob.see_invisible = invis_flags - . = view(range, dview_mob) - dview_mob.loc = null + . = view(range, GLOB.dview_mob) + GLOB.dview_mob.loc = null /mob/dview invisibility = 101 @@ -1895,8 +1895,15 @@ var/mob/dview/dview_mob = new #undef DELTA_CALC -// This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). -/proc/getpois(mobs_only=0,skip_mindless=0) +/* + * This proc gets a list of all "points of interest" (poi's) that can be used by admins to track valuable mobs or atoms (such as the nuke disk). + * @param mobs_only if set to TRUE it won't include locations to the returned list + * @param skip_mindless if set to TRUE it will skip mindless mobs + * @param force_include_bots if set to TRUE it will include bots even if skip_mindless is set to TRUE + * @param force_include_cameras if set to TRUE it will include camera eyes even if skip_mindless is set to TRUE + * @return returns a list with the found points of interest +*/ +/proc/getpois(mobs_only = FALSE, skip_mindless = FALSE, force_include_bots = FALSE, force_include_cameras = FALSE) var/list/mobs = sortmobs() var/list/names = list() var/list/pois = list() @@ -1904,7 +1911,7 @@ var/mob/dview/dview_mob = new for(var/mob/M in mobs) if(skip_mindless && (!M.mind && !M.ckey)) - if(!isbot(M) && !istype(M, /mob/camera/)) + if(!(force_include_bots && isbot(M)) && !(force_include_cameras && istype(M, /mob/camera))) continue if(M.client && M.client.holder && M.client.holder.fakekey) //stealthmins continue diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 968a2f4d577a..bd7e6484bdf2 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -28,4 +28,4 @@ This may require updating to a beta release. #endif // Macros that must exist before world.dm -#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat \ No newline at end of file +#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm index ee3f6a73a92b..5cf36541eebc 100644 --- a/code/_globalvars/configuration.dm +++ b/code/_globalvars/configuration.dm @@ -1,45 +1,45 @@ -var/datum/configuration/config = null +GLOBAL_REAL(config, /datum/configuration) -var/host = null -var/join_motd = null +GLOBAL_VAR(host) +GLOBAL_VAR(join_motd) GLOBAL_VAR(join_tos) -var/game_version = "ParaCode" -var/changelog_hash = md5('html/changelog.html') //used to check if the CL changed -var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 544) +GLOBAL_VAR_INIT(game_version, "ParaCode") +GLOBAL_VAR_INIT(changelog_hash, md5('html/changelog.html')) //used to check if the CL changed +GLOBAL_VAR_INIT(game_year, (text2num(time2text(world.realtime, "YYYY")) + 544)) -var/aliens_allowed = 1 -var/traitor_scaling = 1 -//var/goonsay_allowed = 0 -var/dna_ident = 1 -var/abandon_allowed = 0 -var/enter_allowed = 1 -var/guests_allowed = 1 -var/shuttle_frozen = 0 -var/shuttle_left = 0 -var/tinted_weldhelh = 1 -var/mouse_respawn_time = 5 //Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes. - -// Command to run if shutting down (SHUTDOWN_ON_REBOOT) instead of rebooting -// It's defined here as a global because this is a hilariously bad thing to have on the easily-edited config datum -var/global/shutdown_shell_command - -// Also global to prevent easy edits -var/global/python_path = "" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix +GLOBAL_VAR_INIT(aliens_allowed, 1) +GLOBAL_VAR_INIT(traitor_scaling, 1) +//GLOBAL_VAR_INIT(goonsay_allowed, 0) +GLOBAL_VAR_INIT(dna_ident, 1) +GLOBAL_VAR_INIT(abandon_allowed, 0) +GLOBAL_VAR_INIT(enter_allowed, 1) +GLOBAL_VAR_INIT(guests_allowed, 1) +GLOBAL_VAR_INIT(shuttle_frozen, 0) +GLOBAL_VAR_INIT(shuttle_left, 0) +GLOBAL_VAR_INIT(tinted_weldhelh, 1) +GLOBAL_VAR_INIT(mouse_respawn_time, 5) //Amount of time that must pass between a player dying as a mouse and repawning as a mouse. In minutes. // Debug is used exactly once (in living.dm) but is commented out in a lot of places. It is not set anywhere and only checked. // Debug2 is used in conjunction with a lot of admin verbs and therefore is actually legit. -var/Debug = 0 // global debug switch -var/Debug2 = 1 // enables detailed job debug file in secrets +GLOBAL_VAR_INIT(debug, 0) // global debug switch +GLOBAL_VAR_INIT(debug2, 1) // enables detailed job debug file in secrets //This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage -var/MAX_EX_DEVASTATION_RANGE = 3 -var/MAX_EX_HEAVY_RANGE = 7 -var/MAX_EX_LIGHT_RANGE = 14 -var/MAX_EX_FLASH_RANGE = 14 -var/MAX_EX_FLAME_RANGE = 14 +GLOBAL_VAR_INIT(max_ex_devastation_range, 3) +GLOBAL_VAR_INIT(max_ex_heavy_range, 7) +GLOBAL_VAR_INIT(max_ex_light_range, 14) +GLOBAL_VAR_INIT(max_ex_flash_range, 14) +GLOBAL_VAR_INIT(max_ex_flame_range, 14) //Random event stuff, apparently used -var/eventchance = 10 //% per 5 mins -var/event = 0 -var/hadevent = 0 -var/blobevent = 0 +GLOBAL_VAR_INIT(eventchance, 10) //% per 5 mins +GLOBAL_VAR_INIT(event, 0) +GLOBAL_VAR_INIT(hadevent, 0) +GLOBAL_VAR_INIT(blobevent, 0) + +// These vars are protected because changing them could pose a security risk, though they are fine to be read since they are just system paths +GLOBAL_VAR(shutdown_shell_command) // Command to run if shutting down (SHUTDOWN_ON_REBOOT) instead of rebooting +GLOBAL_PROTECT(shutdown_shell_command) + +GLOBAL_VAR(python_path) //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix +GLOBAL_PROTECT(python_path) diff --git a/code/_globalvars/database.dm b/code/_globalvars/database.dm deleted file mode 100644 index 5c5b4e93f6a3..000000000000 --- a/code/_globalvars/database.dm +++ /dev/null @@ -1,11 +0,0 @@ -// MySQL configuration -var/sqladdress = "localhost" -var/sqlport = "3306" -var/sqlfdbkdb = "test" -var/sqlfdbklogin = "root" -var/sqlfdbkpass = "" -var/sqlfdbktableprefix = "erro_" //backwords compatibility with downstream server hosts - -//Database connections -//A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.). -var/DBConnection/dbcon = new() //Feedback database (New database) diff --git a/code/_globalvars/game_modes.dm b/code/_globalvars/game_modes.dm index 3f8ec321470e..3088270579c6 100644 --- a/code/_globalvars/game_modes.dm +++ b/code/_globalvars/game_modes.dm @@ -1,10 +1,11 @@ -var/master_mode = "extended"//"extended" -var/secret_force_mode = "secret" // if this is anything but "secret", the secret rotation will forceably choose this mode -var/wavesecret = 0 // meteor mode, delays wave progression, terrible name -var/datum/station_state/start_state = null // Used in round-end report +GLOBAL_VAR_INIT(master_mode, "extended") //"extended" +GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret", the secret rotation will forceably choose this mode -var/custom_event_msg = null -var/custom_event_admin_msg = null +GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name +GLOBAL_DATUM(start_state, /datum/station_state) // Used in round-end report. Dont ask why it inits as null -var/list/summon_spots = list() +GLOBAL_VAR(custom_event_msg) +GLOBAL_VAR(custom_event_admin_msg) + +GLOBAL_LIST_EMPTY(summon_spots) diff --git a/code/_globalvars/genetics.dm b/code/_globalvars/genetics.dm index 6a4a118f8668..1119ac364cce 100644 --- a/code/_globalvars/genetics.dm +++ b/code/_globalvars/genetics.dm @@ -1,68 +1,68 @@ /////////// -var/BLINDBLOCK = 0 -var/COLOURBLINDBLOCK = 0 -var/DEAFBLOCK = 0 -var/HULKBLOCK = 0 -var/TELEBLOCK = 0 -var/FIREBLOCK = 0 -var/XRAYBLOCK = 0 -var/CLUMSYBLOCK = 0 -var/FAKEBLOCK = 0 -var/COUGHBLOCK = 0 -var/GLASSESBLOCK = 0 -var/EPILEPSYBLOCK = 0 -var/TWITCHBLOCK = 0 -var/NERVOUSBLOCK = 0 -var/WINGDINGSBLOCK = 0 -var/MONKEYBLOCK = 50 // Monkey block will always be the DNA_SE_LENGTH +GLOBAL_VAR_INIT(blindblock, 0) +GLOBAL_VAR_INIT(colourblindblock, 0) +GLOBAL_VAR_INIT(deafblock, 0) +GLOBAL_VAR_INIT(hulkblock, 0) +GLOBAL_VAR_INIT(teleblock, 0) +GLOBAL_VAR_INIT(fireblock, 0) +GLOBAL_VAR_INIT(xrayblock, 0) +GLOBAL_VAR_INIT(clumsyblock, 0) +GLOBAL_VAR_INIT(fakeblock, 0) +GLOBAL_VAR_INIT(coughblock, 0) +GLOBAL_VAR_INIT(glassesblock, 0) +GLOBAL_VAR_INIT(epilepsyblock, 0) +GLOBAL_VAR_INIT(twitchblock, 0) +GLOBAL_VAR_INIT(nervousblock, 0) +GLOBAL_VAR_INIT(wingdingsblock, 0) +GLOBAL_VAR_INIT(monkeyblock, DNA_SE_LENGTH) // Monkey block will always be the DNA_SE_LENGTH -var/BLOCKADD = 0 -var/DIFFMUT = 0 +GLOBAL_VAR_INIT(blockadd, 0) +GLOBAL_VAR_INIT(diffmut, 0) -var/BREATHLESSBLOCK = 0 -var/REMOTEVIEWBLOCK = 0 -var/REGENERATEBLOCK = 0 -var/INCREASERUNBLOCK = 0 -var/REMOTETALKBLOCK = 0 -var/MORPHBLOCK = 0 -var/COLDBLOCK = 0 -var/HALLUCINATIONBLOCK = 0 -var/NOPRINTSBLOCK = 0 -var/SHOCKIMMUNITYBLOCK = 0 -var/SMALLSIZEBLOCK = 0 +GLOBAL_VAR_INIT(breathlessblock, 0) +GLOBAL_VAR_INIT(remoteviewblock, 0) +GLOBAL_VAR_INIT(regenerateblock, 0) +GLOBAL_VAR_INIT(increaserunblock, 0) +GLOBAL_VAR_INIT(remotetalkblock, 0) +GLOBAL_VAR_INIT(morphblock, 0) +GLOBAL_VAR_INIT(coldblock, 0) +GLOBAL_VAR_INIT(hallucinationblock, 0) +GLOBAL_VAR_INIT(noprintsblock, 0) +GLOBAL_VAR_INIT(shockimmunityblock, 0) +GLOBAL_VAR_INIT(smallsizeblock, 0) /////////////////////////////// // Goon Stuff /////////////////////////////// // Disabilities -var/LISPBLOCK = 0 -var/MUTEBLOCK = 0 -var/RADBLOCK = 0 -var/FATBLOCK = 0 -var/CHAVBLOCK = 0 -var/SWEDEBLOCK = 0 -var/SCRAMBLEBLOCK = 0 -var/STRONGBLOCK = 0 -var/HORNSBLOCK = 0 -var/COMICBLOCK = 0 +GLOBAL_VAR_INIT(lispblock, 0) +GLOBAL_VAR_INIT(muteblock, 0) +GLOBAL_VAR_INIT(radblock, 0) +GLOBAL_VAR_INIT(fatblock, 0) +GLOBAL_VAR_INIT(chavblock, 0) +GLOBAL_VAR_INIT(swedeblock, 0) +GLOBAL_VAR_INIT(scrambleblock, 0) +GLOBAL_VAR_INIT(strongblock, 0) +GLOBAL_VAR_INIT(hornsblock, 0) +GLOBAL_VAR_INIT(comicblock, 0) // Powers -var/SOBERBLOCK = 0 -var/PSYRESISTBLOCK = 0 -var/SHADOWBLOCK = 0 -var/CHAMELEONBLOCK = 0 -var/CRYOBLOCK = 0 -var/EATBLOCK = 0 -var/JUMPBLOCK = 0 -var/EMPATHBLOCK = 0 -var/IMMOLATEBLOCK = 0 -var/POLYMORPHBLOCK = 0 +GLOBAL_VAR_INIT(soberblock, 0) +GLOBAL_VAR_INIT(psyresistblock, 0) +GLOBAL_VAR_INIT(shadowblock, 0) +GLOBAL_VAR_INIT(chameleonblock, 0) +GLOBAL_VAR_INIT(cryoblock, 0) +GLOBAL_VAR_INIT(eatblock, 0) +GLOBAL_VAR_INIT(jumpblock, 0) +GLOBAL_VAR_INIT(empathblock, 0) +GLOBAL_VAR_INIT(immolateblock, 0) +GLOBAL_VAR_INIT(polymorphblock, 0) /////////////////////////////// // /vg/ Mutations /////////////////////////////// -var/LOUDBLOCK = 0 -var/DIZZYBLOCK = 0 +GLOBAL_VAR_INIT(loudblock, 0) +GLOBAL_VAR_INIT(dizzyblock, 0) -var/list/reg_dna = list( ) //this appears to be a list of UE == real_name correlations -var/list/global_mutations = list() // list of hidden mutation things +GLOBAL_LIST_EMPTY(reg_dna) +GLOBAL_LIST_EMPTY(global_mutations) diff --git a/code/_globalvars/lists/devil.dm b/code/_globalvars/lists/devil.dm index 2c941069fe42..54528709573f 100644 --- a/code/_globalvars/lists/devil.dm +++ b/code/_globalvars/lists/devil.dm @@ -1,8 +1,8 @@ //what could possibly go wrong -var/list/devil_machines = typecacheof(/obj/item/circuitboard, TRUE) - list( +GLOBAL_LIST_INIT(devil_machines, typecacheof(/obj/item/circuitboard, TRUE) - list( /obj/item/circuitboard/shuttle, /obj/item/circuitboard/swfdoor, /obj/item/circuitboard/olddoor, /obj/item/circuitboard/computer, /obj/item/circuitboard/machine - ) +)) diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index feadeece28f0..744e595ad3b8 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -21,4 +21,4 @@ GLOBAL_LIST_INIT(nightmare_strings, file2list("config/names/nightmares.txt")) //loaded on startup because of " //would include in rsc if ' was used -GLOBAL_LIST_INIT(vox_name_syllables, list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) \ No newline at end of file +GLOBAL_LIST_INIT(vox_name_syllables, list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index f383ba3fc336..931a0e394e8f 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -51,4 +51,4 @@ GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects GLOBAL_LIST_EMPTY(explosive_walls) -GLOBAL_LIST_EMPTY(engine_beacon_list) \ No newline at end of file +GLOBAL_LIST_EMPTY(engine_beacon_list) diff --git a/code/_globalvars/lists/reagents.dm b/code/_globalvars/lists/reagents.dm index 981eb63aa7e9..20620d60800e 100644 --- a/code/_globalvars/lists/reagents.dm +++ b/code/_globalvars/lists/reagents.dm @@ -63,4 +63,4 @@ GLOBAL_LIST_INIT(blocked_chems, list("polonium", "initropidril", "concentrated_i GLOBAL_LIST_INIT(safe_chem_list, list("antihol", "charcoal", "epinephrine", "insulin", "teporone","silver_sulfadiazine", "salbutamol", "omnizine", "stimulants", "synaptizine", "potass_iodide", "oculine", "mannitol", "styptic_powder", "spaceacillin", "salglu_solution", "sal_acid", "cryoxadone", "blood", "synthflesh", "hydrocodone", - "mitocholide", "rezadone")) \ No newline at end of file + "mitocholide", "rezadone")) diff --git a/code/_globalvars/lists/typecache.dm b/code/_globalvars/lists/typecache.dm index 2600e5c287f9..96807214f2f8 100644 --- a/code/_globalvars/lists/typecache.dm +++ b/code/_globalvars/lists/typecache.dm @@ -9,4 +9,4 @@ GLOBAL_LIST_INIT(typecache_living, typecacheof(/mob/living)) GLOBAL_LIST_INIT(typecache_stack, typecacheof(/obj/item/stack)) -GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure))) \ No newline at end of file +GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure))) diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index 10c7d74c752d..0ea513708a16 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -1,3 +1,4 @@ +// Everything in this file should be protected GLOBAL_VAR(log_directory) GLOBAL_PROTECT(log_directory) GLOBAL_VAR(world_game_log) @@ -15,14 +16,20 @@ GLOBAL_PROTECT(world_asset_log) GLOBAL_VAR(runtime_summary_log) GLOBAL_PROTECT(runtime_summary_log) -var/list/jobMax = list() -var/list/admin_log = list ( ) -var/list/lastsignalers = list( ) //keeps last 100 signals here in format: "[src] used \ref[src] @ location [src.loc]: [freq]/[code]" -var/list/lawchanges = list( ) //Stores who uploaded laws to which silicon-based lifeform, and what the law was +GLOBAL_LIST_EMPTY(jobMax) +GLOBAL_PROTECT(jobMax) +GLOBAL_LIST_EMPTY(admin_log) +GLOBAL_PROTECT(admin_log) +GLOBAL_LIST_EMPTY(lastsignalers) +GLOBAL_PROTECT(lastsignalers) +GLOBAL_LIST_EMPTY(lawchanges) +GLOBAL_PROTECT(lawchanges) -var/list/combatlog = list() -var/list/IClog = list() -var/list/OOClog = list() -var/list/adminlog = list() +GLOBAL_LIST_EMPTY(combatlog) +GLOBAL_PROTECT(combatlog) +GLOBAL_LIST_EMPTY(IClog) +GLOBAL_PROTECT(IClog) +GLOBAL_LIST_EMPTY(OOClog) +GLOBAL_PROTECT(OOClog) -var/list/investigate_log_subjects = list("notes", "watchlist", "hrefs") +GLOBAL_LIST_INIT(investigate_log_subjects, list("notes", "watchlist", "hrefs")) diff --git a/code/_globalvars/mapping.dm b/code/_globalvars/mapping.dm index c1efb29075d1..ca1c899e3012 100644 --- a/code/_globalvars/mapping.dm +++ b/code/_globalvars/mapping.dm @@ -3,12 +3,14 @@ #define Z_SOUTH 3 #define Z_WEST 4 -var/list/cardinal = list( NORTH, SOUTH, EAST, WEST ) -var/list/alldirs = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) -var/list/diagonals = list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST) +GLOBAL_LIST_INIT(cardinal, list( NORTH, SOUTH, EAST, WEST )) +GLOBAL_LIST_INIT(alldirs, list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) +GLOBAL_LIST_INIT(diagonals, list(NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) -var/global/list/global_map = null - //list/global_map = list(list(1,5),list(4,3))//an array of map Z levels. +// This must exist early on or shit breaks bad +GLOBAL_DATUM_INIT(using_map, /datum/map, new USING_MAP_DATUM) + +GLOBAL_LIST(global_map) // This is the array of zlevels | list(list(1,5),list(4,3)) | becomes a 2D array of zlevels //Resulting sector map looks like //|_1_|_4_| //|_5_|_3_| @@ -18,38 +20,37 @@ var/global/list/global_map = null //3 - AI satellite //5 - empty space -var/list/wizardstart = list() -var/list/newplayer_start = list() -var/list/latejoin = list() -var/list/latejoin_gateway = list() -var/list/latejoin_cryo = list() -var/list/latejoin_cyborg = list() -var/list/prisonwarp = list() //prisoners go to these -var/list/xeno_spawn = list()//Aliens spawn at these. -var/list/ertdirector = list() -var/list/emergencyresponseteamspawn = list() -var/list/tdome1 = list() -var/list/tdome2 = list() -var/list/team_alpha = list() -var/list/team_bravo = list() -var/list/tdomeobserve = list() -var/list/tdomeadmin = list() -var/list/aroomwarp = list() -var/list/prisonsecuritywarp = list() //prison security goes to these -var/list/prisonwarped = list() //list of players already warped -var/list/blobstart = list() -var/list/ninjastart = list() -var/list/carplist = list() //list of all carp-spawn landmarks -var/list/syndicateofficer = list() +GLOBAL_LIST_EMPTY(wizardstart) +GLOBAL_LIST_EMPTY(newplayer_start) +GLOBAL_LIST_EMPTY(latejoin) +GLOBAL_LIST_EMPTY(latejoin_gateway) +GLOBAL_LIST_EMPTY(latejoin_cryo) +GLOBAL_LIST_EMPTY(latejoin_cyborg) +GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these +GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these. +GLOBAL_LIST_EMPTY(ertdirector) +GLOBAL_LIST_EMPTY(emergencyresponseteamspawn) +GLOBAL_LIST_EMPTY(tdome1) +GLOBAL_LIST_EMPTY(tdome2) +GLOBAL_LIST_EMPTY(team_alpha) +GLOBAL_LIST_EMPTY(team_bravo) +GLOBAL_LIST_EMPTY(tdomeobserve) +GLOBAL_LIST_EMPTY(tdomeadmin) +GLOBAL_LIST_EMPTY(aroomwarp) +GLOBAL_LIST_EMPTY(prisonsecuritywarp) //prison security goes to these +GLOBAL_LIST_EMPTY(prisonwarped) //list of players already warped +GLOBAL_LIST_EMPTY(blobstart) +GLOBAL_LIST_EMPTY(ninjastart) +GLOBAL_LIST_EMPTY(carplist) //list of all carp-spawn landmarks +GLOBAL_LIST_EMPTY(syndicateofficer) //away missions -var/list/awaydestinations = list() //a list of landmarks that the warpgate can take you to +GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can take you to //List of preloaded templates -var/list/datum/map_template/map_templates = list() - -var/list/datum/map_template/ruins_templates = list() -var/list/datum/map_template/space_ruins_templates = list() -var/list/datum/map_template/lava_ruins_templates = list() -var/list/datum/map_template/shelter_templates = list() -var/list/datum/map_template/shuttle_templates = list() \ No newline at end of file +GLOBAL_LIST_EMPTY(map_templates) +GLOBAL_LIST_EMPTY(ruins_templates) +GLOBAL_LIST_EMPTY(space_ruins_templates) +GLOBAL_LIST_EMPTY(lava_ruins_templates) +GLOBAL_LIST_EMPTY(shelter_templates) +GLOBAL_LIST_EMPTY(shuttle_templates) diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index 04db7233401b..32d6370f3d04 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -1,93 +1,103 @@ -var/global/obj/effect/overlay/plmaster = null -var/global/obj/effect/overlay/slmaster = null +GLOBAL_DATUM(plmaster, /obj/effect/overlay) +GLOBAL_DATUM(slmaster, /obj/effect/overlay) GLOBAL_VAR_INIT(CELLRATE, 0.002) // conversion ratio between a watt-tick and kilojoule GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second) // Announcer intercom, because too much stuff creates an intercom for one message then hard del()s it. -var/global/obj/item/radio/intercom/global_announcer = create_global_announcer() -var/global/obj/item/radio/intercom/command/command_announcer = create_command_announcer() +GLOBAL_DATUM_INIT(global_announcer, /obj/item/radio/intercom, create_global_announcer()) +GLOBAL_DATUM_INIT(command_announcer, /obj/item/radio/intercom/command, create_command_announcer()) + // Load order issues means this can't be new'd until other code runs // This is probably not the way I should be doing this, but I don't know how to do it right! proc/create_global_announcer() spawn(0) - global_announcer = new(null) + GLOB.global_announcer = new(null) return proc/create_command_announcer() spawn(0) - command_announcer = new(null) + GLOB.command_announcer = new(null) return -var/list/paper_tag_whitelist = list("center","p","div","span","h1","h2","h3","h4","h5","h6","hr","pre", \ +GLOBAL_LIST_INIT(paper_tag_whitelist, list("center","p","div","span","h1","h2","h3","h4","h5","h6","hr","pre", \ "big","small","font","i","u","b","s","sub","sup","tt","br","hr","ol","ul","li","caption","col", \ - "table","td","th","tr") -var/list/paper_blacklist = list("java","onblur","onchange","onclick","ondblclick","onfocus","onkeydown", \ + "table","td","th","tr")) +GLOBAL_LIST_INIT(paper_blacklist, list("java","onblur","onchange","onclick","ondblclick","onfocus","onkeydown", \ "onkeypress","onkeyup","onload","onmousedown","onmousemove","onmouseout","onmouseover", \ - "onmouseup","onreset","onselect","onsubmit","onunload") + "onmouseup","onreset","onselect","onsubmit","onunload")) //Reverse of dir -var/list/reverse_dir = list(2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47, 16, 18, 17, 19, 24, 26, 25, 27, 20, 22, 21, 23, 28, 30, 29, 31, 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, 55, 60, 62, 61, 63) -var/gravity_is_on = 1 //basically unused, just one admin verb.. +GLOBAL_LIST_INIT(reverse_dir, list(2, 1, 3, 8, 10, 9, 11, 4, 6, 5, 7, 12, 14, 13, 15, 32, 34, 33, 35, 40, 42, 41, 43, 36, 38, 37, 39, 44, 46, 45, 47, 16, 18, 17, 19, 24, 26, 25, 27, 20, 22, 21, 23, 28, 30, 29, 31, 48, 50, 49, 51, 56, 58, 57, 59, 52, 54, 53, 55, 60, 62, 61, 63)) +GLOBAL_VAR_INIT(gravity_is_on, 1) //basically unused, just one admin verb.. // Recall time limit: 2 hours -var/recall_time_limit=72000 //apparently used for the comm console +GLOBAL_VAR_INIT(recall_time_limit, 72000) //apparently used for the comm console //////SCORE STUFF //Goonstyle scoreboard -var/score_crewscore = 0 // this is the overall var/score for the whole round -var/score_stuffshipped = 0 // how many useful items have cargo shipped out? -var/score_stuffharvested = 0 // how many harvests have hydroponics done? -var/score_oremined = 0 // obvious -var/score_researchdone = 0 -var/score_eventsendured = 0 // how many random events did the station survive? -var/score_powerloss = 0 // how many APCs have poor charge? -var/score_escapees = 0 // how many people got out alive? -var/score_deadcrew = 0 // dead bodies on the station, oh no -var/score_mess = 0 // how much poo, puke, gibs, etc went uncleaned -var/score_meals = 0 -var/score_disease = 0 // how many rampant, uncured diseases are on board the station -var/score_deadcommand = 0 // used during rev, how many command staff perished -var/score_arrested = 0 // how many traitors/revs/whatever are alive in the brig -var/score_traitorswon = 0 // how many traitors were successful? -var/score_allarrested = 0 // did the crew catch all the enemies alive? -var/score_opkilled = 0 // used during nuke mode, how many operatives died? -var/score_disc = 0 // is the disc safe and secure? -var/score_nuked = 0 // was the station blown into little bits? -var/score_nuked_penalty = 0 //penalty for getting blown to bits +GLOBAL_VAR_INIT(score_crewscore, 0) // this is the overall var/score for the whole round +GLOBAL_VAR_INIT(score_stuffshipped, 0) // how many useful items have cargo shipped out? +GLOBAL_VAR_INIT(score_stuffharvested, 0) // how many harvests have hydroponics done? +GLOBAL_VAR_INIT(score_oremined, 0) // obvious +GLOBAL_VAR_INIT(score_researchdone, 0) +GLOBAL_VAR_INIT(score_eventsendured, 0) // how many random events did the station survive? +GLOBAL_VAR_INIT(score_powerloss, 0) // how many APCs have poor charge? +GLOBAL_VAR_INIT(score_escapees, 0) // how many people got out alive? +GLOBAL_VAR_INIT(score_deadcrew, 0) // dead bodies on the station, oh no +GLOBAL_VAR_INIT(score_mess, 0) // how much poo, puke, gibs, etc went uncleaned +GLOBAL_VAR_INIT(score_meals, 0) +GLOBAL_VAR_INIT(score_disease, 0) // how many rampant, uncured diseases are on board the station +GLOBAL_VAR_INIT(score_deadcommand, 0) // used during rev, how many command staff perished +GLOBAL_VAR_INIT(score_arrested, 0) // how many traitors/revs/whatever are alive in the brig +GLOBAL_VAR_INIT(score_traitorswon, 0) // how many traitors were successful? +GLOBAL_VAR_INIT(score_allarrested, 0) // did the crew catch all the enemies alive? +GLOBAL_VAR_INIT(score_opkilled, 0) // used during nuke mode, how many operatives died? +GLOBAL_VAR_INIT(score_disc, 0) // is the disc safe and secure? +GLOBAL_VAR_INIT(score_nuked, 0) // was the station blown into little bits? +GLOBAL_VAR_INIT(score_nuked_penalty, 0) //penalty for getting blown to bits // these ones are mainly for the stat panel -var/score_powerbonus = 0 // if all APCs on the station are running optimally, big bonus -var/score_messbonus = 0 // if there are no messes on the station anywhere, huge bonus -var/score_deadaipenalty = 0 // is the AI dead? if so, big penalty -var/score_foodeaten = 0 // nom nom nom -var/score_clownabuse = 0 // how many times a clown was punched, struck or otherwise maligned -var/score_richestname = null // this is all stuff to show who was the richest alive on the shuttle -var/score_richestjob = null // kinda pointless if you dont have a money system i guess -var/score_richestcash = 0 -var/score_richestkey = null -var/score_dmgestname = null // who had the most damage on the shuttle (but was still alive) -var/score_dmgestjob = null -var/score_dmgestdamage = 0 -var/score_dmgestkey = null - -var/TAB = "    " +GLOBAL_VAR_INIT(score_powerbonus, 0) // if all APCs on the station are running optimally, big bonus +GLOBAL_VAR_INIT(score_messbonus, 0) // if there are no messes on the station anywhere, huge bonus +GLOBAL_VAR_INIT(score_deadaipenalty, 0) // is the AI dead? if so, big penalty +GLOBAL_VAR_INIT(score_foodeaten, 0) // nom nom nom +GLOBAL_VAR_INIT(score_clownabuse, 0) // how many times a clown was punched, struck or otherwise maligned +GLOBAL_VAR(score_richestname) // this is all stuff to show who was the richest alive on the shuttle +GLOBAL_VAR(score_richestjob) // kinda pointless if you dont have a money system i guess +GLOBAL_VAR_INIT(score_richestcash, 0) +GLOBAL_VAR(score_richestkey) +GLOBAL_VAR(score_dmgestname) // who had the most damage on the shuttle (but was still alive) +GLOBAL_VAR(score_dmgestjob) +GLOBAL_VAR_INIT(score_dmgestdamage, 0) +GLOBAL_VAR(score_dmgestkey) + +#define TAB "    " GLOBAL_VAR_INIT(timezoneOffset, 0) // The difference betwen midnight (of the host computer) and 0 world.ticks. // For FTP requests. (i.e. downloading runtime logs.) // However it'd be ok to use for accessing attack logs and such too, which are even laggier. -var/fileaccess_timer = 0 +GLOBAL_VAR_INIT(fileaccess_timer, 0) GLOBAL_VAR_INIT(gametime_offset, 432000) // 12:00 in seconds //printers shutdown if too much shit printed -var/copier_items_printed = 0 -var/copier_max_items = 300 -var/copier_items_printed_logged = FALSE +GLOBAL_VAR_INIT(copier_items_printed, 0) +GLOBAL_VAR_INIT(copier_max_items, 300) +GLOBAL_VAR_INIT(copier_items_printed_logged, FALSE) GLOBAL_VAR(map_name) // Self explanatory -var/global/datum/datacore/data_core = null // Station datacore, manifest, etc +GLOBAL_DATUM(data_core, /datum/datacore) // Station datacore, manifest, etc GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled + +//Database connections +//A connection is established on world creation. Ideally, the connection dies when the server restarts (After feedback logging.). +// This is a protected datum which means its read only. I hope the reason for protecting this is obvious +GLOBAL_DATUM_INIT(dbcon, /DBConnection, new) //Feedback database (New database) +GLOBAL_PROTECT(dbcon) + +GLOBAL_LIST_EMPTY(ability_verbs) // Create-level abilities +GLOBAL_LIST_INIT(pipe_colors, list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE)) diff --git a/code/_globalvars/sensitive.dm b/code/_globalvars/sensitive.dm new file mode 100644 index 000000000000..b1e6751c4dd3 --- /dev/null +++ b/code/_globalvars/sensitive.dm @@ -0,0 +1,12 @@ +// All global vars in this file require a different handler as we dont want their data to be exposed, not even to admins with permission to view globals +// They write as native BYOND globals, which means they cant be edited at all, and also use a format +// Please only throw absolutely crucial do-not-view shit in here please. -aa07 + +// MySQL configuration +GLOBAL_REAL_VAR(sqladdress) = "localhost" +GLOBAL_REAL_VAR(sqlport) = "3306" +GLOBAL_REAL_VAR(sqlfdbkdb) = "test" +GLOBAL_REAL_VAR(sqlfdbklogin) = "root" +GLOBAL_REAL_VAR(sqlfdbkpass) = "" +GLOBAL_REAL_VAR(sqlfdbktableprefix) = "erro_" + diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index d545a2aac1ab..9e0d45643bec 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -16,4 +16,4 @@ GLOBAL_LIST(trait_name_map) for(var/key in GLOB.traits_by_type) for(var/tname in GLOB.traits_by_type[key]) var/val = GLOB.traits_by_type[key][tname] - .[val] = tname \ No newline at end of file + .[val] = tname diff --git a/code/_globalvars/unused.dm b/code/_globalvars/unused.dm deleted file mode 100644 index 2e3f8f9c6dc5..000000000000 --- a/code/_globalvars/unused.dm +++ /dev/null @@ -1 +0,0 @@ -var/going = 1.0 \ No newline at end of file diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 038174388ff4..58cfdf477f25 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -96,4 +96,4 @@ else if( !border_only ) // dense, not on border, cannot pass over return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 7c2214e22876..fbb04d4c0218 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -42,7 +42,7 @@ if(control_disabled || stat) return - + var/turf/pixel_turf = isturf(A) ? A : get_turf_pixel(A) if(isnull(pixel_turf)) return @@ -57,7 +57,7 @@ var/turf_visible if(pixel_turf) - turf_visible = cameranet.checkTurfVis(pixel_turf) + turf_visible = GLOB.cameranet.checkTurfVis(pixel_turf) if(!turf_visible) if(istype(loc, /obj/item/aicard) && (pixel_turf in view(client.view, loc))) turf_visible = TRUE @@ -228,4 +228,4 @@ // /mob/living/silicon/ai/TurfAdjacent(var/turf/T) - return (cameranet && cameranet.checkTurfVis(T)) + return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index ea9f1663ed03..6bfd72413de3 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -447,4 +447,4 @@ params += "&catcher=1" if(T) T.Click(location, control, params) - . = 1 \ No newline at end of file + . = 1 diff --git a/code/_onclick/click_override.dm b/code/_onclick/click_override.dm index a6042386b3d7..481d30da4b68 100644 --- a/code/_onclick/click_override.dm +++ b/code/_onclick/click_override.dm @@ -89,4 +89,4 @@ A = target_atom next_shocked.Cut() - P.last_shocked = world.time \ No newline at end of file + P.last_shocked = world.time diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index dc98d887279a..ada0ce8cd303 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -262,4 +262,4 @@ using = new /obj/screen/act_intent/robot/AI() using.icon_state = mymob.a_intent static_inventory += using - action_intent = using \ No newline at end of file + action_intent = using diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index c1f14fc625dc..01940be1d8bd 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -196,4 +196,4 @@ using = new /obj/screen/blob/Split() using.screen_loc = ui_acti - static_inventory += using \ No newline at end of file + static_inventory += using diff --git a/code/_onclick/hud/bot.dm b/code/_onclick/hud/bot.dm index 5f4ddbd76229..12320f2da3e6 100644 --- a/code/_onclick/hud/bot.dm +++ b/code/_onclick/hud/bot.dm @@ -30,4 +30,4 @@ mymob.pullin.icon = 'icons/mob/screen_bot.dmi' mymob.pullin.update_icon(mymob) mymob.pullin.screen_loc = ui_bot_pull - static_inventory += mymob.pullin \ No newline at end of file + static_inventory += mymob.pullin diff --git a/code/_onclick/hud/constructs.dm b/code/_onclick/hud/constructs.dm index 7ed1d66724f7..0effd812b2d8 100644 --- a/code/_onclick/hud/constructs.dm +++ b/code/_onclick/hud/constructs.dm @@ -60,4 +60,4 @@ mymob.pullin.icon = 'icons/mob/screen_construct.dmi' mymob.pullin.icon_state = "pull0" mymob.pullin.name = "pull" - mymob.pullin.screen_loc = ui_construct_pull \ No newline at end of file + mymob.pullin.screen_loc = ui_construct_pull diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index cc1ac13931f8..d628124e0520 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -1,94 +1,94 @@ -/mob/living/simple_animal/hostile/guardian/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/guardian(src) - -/datum/hud/guardian/New(mob/owner) - ..() - var/obj/screen/using - - guardianhealthdisplay = new /obj/screen/healths/guardian() - infodisplay += guardianhealthdisplay - - using = new /obj/screen/act_intent/guardian() - using.icon_state = mymob.a_intent - static_inventory += using - action_intent = using - - using = new /obj/screen/guardian/Manifest() - using.screen_loc = ui_rhand - static_inventory += using - - using = new /obj/screen/guardian/Recall() - using.screen_loc = ui_lhand - static_inventory += using - - using = new /obj/screen/guardian/ToggleMode() - using.screen_loc = ui_storage1 - static_inventory += using - - using = new /obj/screen/guardian/ToggleLight() - using.screen_loc = ui_inventory - static_inventory += using - - using = new /obj/screen/guardian/Communicate() - using.screen_loc = ui_back - static_inventory += using - - -//HUD BUTTONS - -/obj/screen/guardian - icon = 'icons/mob/guardian.dmi' - icon_state = "base" - -/obj/screen/guardian/Manifest - icon_state = "manifest" - name = "Manifest" - desc = "Spring forth into battle!" - -/obj/screen/guardian/Manifest/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Manifest() - - -/obj/screen/guardian/Recall - icon_state = "recall" - name = "Recall" - desc = "Return to your user." - -/obj/screen/guardian/Recall/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Recall() - -/obj/screen/guardian/ToggleMode - icon_state = "toggle" - name = "Toggle Mode" - desc = "Switch between ability modes." - -/obj/screen/guardian/ToggleMode/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.ToggleMode() - -/obj/screen/guardian/Communicate - icon_state = "communicate" - name = "Communicate" - desc = "Communicate telepathically with your user." - -/obj/screen/guardian/Communicate/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Communicate() - - -/obj/screen/guardian/ToggleLight - icon_state = "light" - name = "Toggle Light" - desc = "Glow like star dust." - -/obj/screen/guardian/ToggleLight/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.ToggleLight() \ No newline at end of file +/mob/living/simple_animal/hostile/guardian/create_mob_hud() + if(client && !hud_used) + hud_used = new /datum/hud/guardian(src) + +/datum/hud/guardian/New(mob/owner) + ..() + var/obj/screen/using + + guardianhealthdisplay = new /obj/screen/healths/guardian() + infodisplay += guardianhealthdisplay + + using = new /obj/screen/act_intent/guardian() + using.icon_state = mymob.a_intent + static_inventory += using + action_intent = using + + using = new /obj/screen/guardian/Manifest() + using.screen_loc = ui_rhand + static_inventory += using + + using = new /obj/screen/guardian/Recall() + using.screen_loc = ui_lhand + static_inventory += using + + using = new /obj/screen/guardian/ToggleMode() + using.screen_loc = ui_storage1 + static_inventory += using + + using = new /obj/screen/guardian/ToggleLight() + using.screen_loc = ui_inventory + static_inventory += using + + using = new /obj/screen/guardian/Communicate() + using.screen_loc = ui_back + static_inventory += using + + +//HUD BUTTONS + +/obj/screen/guardian + icon = 'icons/mob/guardian.dmi' + icon_state = "base" + +/obj/screen/guardian/Manifest + icon_state = "manifest" + name = "Manifest" + desc = "Spring forth into battle!" + +/obj/screen/guardian/Manifest/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Manifest() + + +/obj/screen/guardian/Recall + icon_state = "recall" + name = "Recall" + desc = "Return to your user." + +/obj/screen/guardian/Recall/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Recall() + +/obj/screen/guardian/ToggleMode + icon_state = "toggle" + name = "Toggle Mode" + desc = "Switch between ability modes." + +/obj/screen/guardian/ToggleMode/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.ToggleMode() + +/obj/screen/guardian/Communicate + icon_state = "communicate" + name = "Communicate" + desc = "Communicate telepathically with your user." + +/obj/screen/guardian/Communicate/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Communicate() + + +/obj/screen/guardian/ToggleLight + icon_state = "light" + name = "Toggle Light" + desc = "Glow like star dust." + +/obj/screen/guardian/ToggleLight/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.ToggleLight() diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index beb6949ce2cf..00b23ff3cddf 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -214,4 +214,4 @@ to_chat(usr, "This mob type does not use a HUD.") /datum/hud/proc/update_locked_slots() - return \ No newline at end of file + return diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm index a82c847a2464..f03d436bcaf1 100644 --- a/code/_onclick/hud/other_mobs.dm +++ b/code/_onclick/hud/other_mobs.dm @@ -24,4 +24,4 @@ mymob.pullin.icon = 'icons/mob/screen_corgi.dmi' mymob.pullin.update_icon(mymob) mymob.pullin.screen_loc = ui_construct_pull - static_inventory += mymob.pullin \ No newline at end of file + static_inventory += mymob.pullin diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 429344e6142c..68847cbd97d5 100644 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -310,4 +310,4 @@ 1, 1, 1, 1, 0, 0, 0, 0 ) - screen_loc = "CENTER-7,CENTER-7" \ No newline at end of file + screen_loc = "CENTER-7,CENTER-7" diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm index e011aea037cc..872659d714bf 100644 --- a/code/_onclick/hud/picture_in_picture.dm +++ b/code/_onclick/hud/picture_in_picture.dm @@ -141,4 +141,4 @@ /obj/screen/movable/pic_in_pic/proc/unshow_to(client/C) if(C) shown_to -= C - C.screen -= src \ No newline at end of file + C.screen -= src diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 9c0f498cd83a..fc3e926b396a 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -51,4 +51,4 @@ /obj/screen/plane_master/lighting/backdrop(mob/mymob) mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit) - mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) \ No newline at end of file + mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) diff --git a/code/_onclick/hud/slime.dm b/code/_onclick/hud/slime.dm index f49228ddf661..273991a83c84 100644 --- a/code/_onclick/hud/slime.dm +++ b/code/_onclick/hud/slime.dm @@ -5,4 +5,4 @@ /mob/living/simple_animal/slime/create_mob_hud() if(client && !hud_used) - hud_used = new /datum/hud/slime(src) \ No newline at end of file + hud_used = new /datum/hud/slime(src) diff --git a/code/_onclick/hud/swarmer.dm b/code/_onclick/hud/swarmer.dm index 84f5811cc8d4..2042442642ad 100644 --- a/code/_onclick/hud/swarmer.dm +++ b/code/_onclick/hud/swarmer.dm @@ -1,97 +1,97 @@ - -////HUD NONSENSE//// -/obj/screen/swarmer - icon = 'icons/mob/swarmer.dmi' - -/obj/screen/swarmer/FabricateTrap - icon_state = "ui_trap" - name = "Create trap (Costs 5 Resources)" - desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)" - -/obj/screen/swarmer/FabricateTrap/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateTrap() - -/obj/screen/swarmer/Barricade - icon_state = "ui_barricade" - name = "Create barricade (Costs 5 Resources)" - desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)" - -/obj/screen/swarmer/Barricade/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateBarricade() - -/obj/screen/swarmer/Replicate - icon_state = "ui_replicate" - name = "Replicate (Costs 50 Resources)" - desc = "Creates a another of our kind." - -/obj/screen/swarmer/Replicate/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateSwarmer() - -/obj/screen/swarmer/RepairSelf - icon_state = "ui_self_repair" - name = "Repair self" - desc = "Repairs damage to our body." - -/obj/screen/swarmer/RepairSelf/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.RepairSelf() - -/obj/screen/swarmer/ToggleLight - icon_state = "ui_light" - name = "Toggle light" - desc = "Toggles our inbuilt light on or off." - -/obj/screen/swarmer/ToggleLight/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.ToggleLight() - -/obj/screen/swarmer/ContactSwarmers - icon_state = "ui_contact_swarmers" - name = "Contact swarmers" - desc = "Sends a message to all other swarmers, should they exist." - -/obj/screen/swarmer/ContactSwarmers/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.ContactSwarmers() - -/mob/living/simple_animal/hostile/swarmer/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/swarmer(src) - -/datum/hud/swarmer/New(mob/owner) - ..() - var/obj/screen/using - - using = new /obj/screen/swarmer/FabricateTrap() - using.screen_loc = ui_rhand - static_inventory += using - - using = new /obj/screen/swarmer/Barricade() - using.screen_loc = ui_lhand - static_inventory += using - - using = new /obj/screen/swarmer/Replicate() - using.screen_loc = ui_zonesel - static_inventory += using - - using = new /obj/screen/swarmer/RepairSelf() - using.screen_loc = ui_storage1 - static_inventory += using - - using = new /obj/screen/swarmer/ToggleLight() - using.screen_loc = ui_back - static_inventory += using - - using = new /obj/screen/swarmer/ContactSwarmers() - using.screen_loc = ui_inventory - static_inventory += using - + +////HUD NONSENSE//// +/obj/screen/swarmer + icon = 'icons/mob/swarmer.dmi' + +/obj/screen/swarmer/FabricateTrap + icon_state = "ui_trap" + name = "Create trap (Costs 5 Resources)" + desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)" + +/obj/screen/swarmer/FabricateTrap/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateTrap() + +/obj/screen/swarmer/Barricade + icon_state = "ui_barricade" + name = "Create barricade (Costs 5 Resources)" + desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)" + +/obj/screen/swarmer/Barricade/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateBarricade() + +/obj/screen/swarmer/Replicate + icon_state = "ui_replicate" + name = "Replicate (Costs 50 Resources)" + desc = "Creates a another of our kind." + +/obj/screen/swarmer/Replicate/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateSwarmer() + +/obj/screen/swarmer/RepairSelf + icon_state = "ui_self_repair" + name = "Repair self" + desc = "Repairs damage to our body." + +/obj/screen/swarmer/RepairSelf/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.RepairSelf() + +/obj/screen/swarmer/ToggleLight + icon_state = "ui_light" + name = "Toggle light" + desc = "Toggles our inbuilt light on or off." + +/obj/screen/swarmer/ToggleLight/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.ToggleLight() + +/obj/screen/swarmer/ContactSwarmers + icon_state = "ui_contact_swarmers" + name = "Contact swarmers" + desc = "Sends a message to all other swarmers, should they exist." + +/obj/screen/swarmer/ContactSwarmers/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.ContactSwarmers() + +/mob/living/simple_animal/hostile/swarmer/create_mob_hud() + if(client && !hud_used) + hud_used = new /datum/hud/swarmer(src) + +/datum/hud/swarmer/New(mob/owner) + ..() + var/obj/screen/using + + using = new /obj/screen/swarmer/FabricateTrap() + using.screen_loc = ui_rhand + static_inventory += using + + using = new /obj/screen/swarmer/Barricade() + using.screen_loc = ui_lhand + static_inventory += using + + using = new /obj/screen/swarmer/Replicate() + using.screen_loc = ui_zonesel + static_inventory += using + + using = new /obj/screen/swarmer/RepairSelf() + using.screen_loc = ui_storage1 + static_inventory += using + + using = new /obj/screen/swarmer/ToggleLight() + using.screen_loc = ui_back + static_inventory += using + + using = new /obj/screen/swarmer/ContactSwarmers() + using.screen_loc = ui_inventory + static_inventory += using + diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index 8ace273dd8fe..419524c87113 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -32,4 +32,4 @@ /mob/camera/blob/AltClickOn(atom/A) //Remove a blob var/turf/T = get_turf(A) if(T) - remove_blob(T) \ No newline at end of file + remove_blob(T) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index cb803e7d52ab..2f4811de380e 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -1,897 +1,897 @@ -/datum/configuration - var/server_name = null // server name (for world name / status) - var/server_tag_line = null // server tagline (for showing on hub entry) - var/server_extra_features = null // server-specific extra features (for hub entry) - var/server_suffix = 0 // generate numeric suffix based on server port - - var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit - - var/nudge_script_path = "nudge.py" // where the nudge.py script is located - - var/log_ooc = 0 // log OOC channel - var/log_access = 0 // log login/logout - var/log_say = 0 // log client say - var/log_admin = 0 // log admin actions - var/log_debug = 1 // log debug output - var/log_game = 0 // log game events - var/log_vote = 0 // log voting - var/log_whisper = 0 // log client whisper - var/log_emote = 0 // log emotes - var/log_attack = 0 // log attack messages - var/log_adminchat = 0 // log admin chat messages - var/log_adminwarn = 0 // log warnings admins get about bomb construction and such - var/log_pda = 0 // log pda messages - var/log_world_output = 0 // log world.log << messages - var/log_runtimes = 0 // logs world.log to a file - var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits - var/sql_enabled = 0 // for sql switching - var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour - var/pregame_timestart = 240 // Time it takes for the server to start the game - var/allow_vote_restart = 0 // allow votes to restart - var/allow_vote_mode = 0 // allow votes to change mode - var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) - var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) - var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called - var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote - var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) - var/vote_no_dead = 0 // dead people can't vote (tbi) -// var/enable_authentication = 0 // goon authentication - var/del_new_on_log = 1 // del's new players if they log before they spawn in - var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard - var/traitor_scaling = 0 //if amount of traitors scales based on amount of players - var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other - var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. - var/allow_Metadata = 0 // Metadata is supported. - var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. - var/Ticklag = 0.5 - var/socket_talk = 0 // use socket_talk to communicate with other processes - var/list/resource_urls = null - var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. - var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. - var/list/mode_names = list() - var/list/modes = list() // allowed modes - var/list/votable_modes = list() // votable modes - var/list/probabilities = list() // relative probability of each mode - var/humans_need_surnames = 0 - var/allow_random_events = 0 // enables random events mid-round when set to 1 - var/allow_ai = 1 // allow ai job - var/respawn = 0 - var/guest_jobban = 1 - var/panic_bunker_threshold = 150 // above this player count threshold, never-before-seen players are blocked from connecting - var/usewhitelist = 0 - var/mods_are_mentors = 0 - var/load_jobs_from_txt = 0 - var/ToRban = 0 - var/automute_on = 0 //enables automuting/spam prevention - var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. - var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized - var/medal_hub_address = null - var/medal_hub_password = null - - var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors - - var/assistantlimit = 0 //enables assistant limiting - var/assistantratio = 2 //how many assistants to security members - - // The AFK subsystem will not be activated if any of the below config values are equal or less than 0 - var/warn_afk_minimum = 0 // How long till you get a warning while being AFK - var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK - var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic) - - var/auto_cryo_ssd_mins = 0 - var/ssd_warning = 0 - - var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb - - var/traitor_objectives_amount = 2 - var/shadowling_max_age = 0 - - var/max_maint_drones = 5 //This many drones can spawn, - var/allow_drone_spawn = 1 //assuming the admin allow them to. - var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. - - var/usealienwhitelist = 0 - var/limitalienplayers = 0 - var/alien_to_human_ratio = 0.5 - - var/server - var/banappeals - var/wikiurl = "http://example.org" - var/forumurl = "http://example.org" - var/rulesurl = "http://example.org" - var/githuburl = "http://example.org" - var/donationsurl = "http://example.org" - var/repositoryurl = "http://example.org" - var/discordurl = "http://example.org" - var/discordforumurl = "http://example.org" - - var/overflow_server_url - var/forbid_singulo_possession = 0 - - var/check_randomizer = 0 - - //game_options.txt configs - - var/bones_can_break = 1 - - var/revival_pod_plants = 1 - var/revival_cloning = 1 - var/revival_brain_life = -1 - - var/auto_toggle_ooc_during_round = 0 - - var/shuttle_refuel_delay = 12000 - - //Used for modifying movement speed for mobs. - //Unversal modifiers - var/run_speed = 0 - var/walk_speed = 0 - - //Mob specific modifiers. NOTE: These will affect different mob types in different ways - var/human_delay = 0 - var/robot_delay = 0 - var/monkey_delay = 0 - var/alien_delay = 0 - var/slime_delay = 0 - var/animal_delay = 0 - - //IP Intel vars - var/ipintel_email - var/ipintel_rating_bad = 1 - var/ipintel_save_good = 12 - var/ipintel_save_bad = 1 - var/ipintel_domain = "check.getipintel.net" - var/ipintel_maxplaytime = 0 - var/ipintel_whitelist = 0 - var/ipintel_detailsurl = "https://iphub.info/?ip=" - - var/forum_link_url - var/forum_playerinfo_url - - var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt - var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt - var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database - var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database - - var/use_exp_tracking = 0 - var/use_exp_restrictions = 0 - var/use_exp_restrictions_admin_bypass = 0 - - var/simultaneous_pm_warning_timeout = 100 - - var/assistant_maint = 0 //Do assistants get maint access? - var/gateway_delay = 6000 //How long the gateway takes before it activates. Default is half an hour. - var/ghost_interaction = 0 - - var/comms_password = "" - - var/use_irc_bot = 0 - var/list/irc_bot_host = list() - var/main_irc = "" - var/admin_irc = "" - var/admin_notify_irc = "" - var/cidrandomizer_irc = "" - - var/default_laws = 0 //Controls what laws the AI spawns with. - - var/list/station_levels = list(1) // Defines which Z-levels the station exists on. - var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle - var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect - var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach - - var/const/minutes_to_ticks = 60 * 10 - // Event settings - var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours - // If the first delay has a custom start time - // No custom time, no custom time, between 80 to 100 minutes respectively. - var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) - // The lowest delay until next event - // 10, 30, 50 minutes respectively - var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) - // The upper delay until next event - // 15, 45, 70 minutes respectively - var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) - - var/starlight = 0 // Whether space turfs have ambient light or not - var/allow_holidays = 0 - var/player_overflow_cap = 0 //number of players before the server starts rerouting - var/list/overflow_whitelist = list() //whitelist for overflow - - var/disable_away_missions = 0 // disable away missions - var/disable_space_ruins = 0 //disable space ruins - - var/extra_space_ruin_levels_min = 2 - var/extra_space_ruin_levels_max = 4 - - var/ooc_allowed = 1 - var/looc_allowed = 1 - var/dooc_allowed = 1 - var/dsay_allowed = 1 - - var/disable_lobby_music = 0 // Disables the lobby music - var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing. - - var/max_loadout_points = 5 // How many points can be spent on extra items in character setup - - var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC - - var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it - - var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default - - // StonedMC - var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling - - // Highpop tickrates - var/base_mc_tick_rate = 1 - var/high_pop_mc_tick_rate = 1.1 - - var/high_pop_mc_mode_amount = 65 - var/disable_high_pop_mc_mode_amount = 60 - - // Nightshift - var/randomize_shift_time = FALSE - var/enable_night_shifts = FALSE - - // Developer - var/developer_express_start = 0 - - // Automatic localhost admin disable - var/disable_localhost_admin = 0 - - //Start now warning - var/start_now_confirmation = 0 - - // Lavaland - var/lavaland_budget = 60 - - //cube monkey limit - var/cubemonkeycap = 20 - - // Makes gamemodes respect player limits - var/enable_gamemode_player_limit = 0 - -/datum/configuration/New() - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = T - - if(initial(M.config_tag)) - if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once - src.modes += initial(M.config_tag) - src.mode_names[initial(M.config_tag)] = initial(M.name) - src.probabilities[initial(M.config_tag)] = initial(M.probability) - if(initial(M.votable)) - src.votable_modes += initial(M.config_tag) - src.votable_modes += "secret" - -/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist - var/list/Lines = file2list(filename) - - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - var/value = null - - if(pos) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if(!name) - continue - - if(type == "config") - switch(name) - if("resource_urls") - config.resource_urls = splittext(value, " ") - - if("admin_legacy_system") - config.admin_legacy_system = 1 - - if("ban_legacy_system") - config.ban_legacy_system = 1 - - if("use_age_restriction_for_jobs") - config.use_age_restriction_for_jobs = 1 - - if("use_age_restriction_for_antags") - config.use_age_restriction_for_antags = 1 - - if("use_exp_tracking") - config.use_exp_tracking = 1 - - if("use_exp_restrictions") - config.use_exp_restrictions = 1 - - if("use_exp_restrictions_admin_bypass") - config.use_exp_restrictions_admin_bypass = 1 - - if("jobs_have_minimal_access") - config.jobs_have_minimal_access = 1 - - if("shadowling_max_age") - config.shadowling_max_age = text2num(value) - - if("warn_afk_minimum") - config.warn_afk_minimum = text2num(value) - if("auto_cryo_afk") - config.auto_cryo_afk = text2num(value) - if("auto_despawn_afk") - config.auto_despawn_afk = text2num(value) - - if("auto_cryo_ssd_mins") - config.auto_cryo_ssd_mins = text2num(value) - if("ssd_warning") - config.ssd_warning = 1 - - if("list_afk_minimum") - config.list_afk_minimum = text2num(value) - - if("ipintel_email") - if(value != "ch@nge.me") - config.ipintel_email = value - if("ipintel_rating_bad") - config.ipintel_rating_bad = text2num(value) - if("ipintel_domain") - config.ipintel_domain = value - if("ipintel_save_good") - config.ipintel_save_good = text2num(value) - if("ipintel_save_bad") - config.ipintel_save_bad = text2num(value) - if("ipintel_maxplaytime") - config.ipintel_maxplaytime = text2num(value) - if("ipintel_whitelist") - config.ipintel_whitelist = 1 - if("ipintel_detailsurl") - config.ipintel_detailsurl = value - - if("forum_link_url") - config.forum_link_url = value - - if("forum_playerinfo_url") - config.forum_playerinfo_url = value - - if("log_ooc") - config.log_ooc = 1 - - if("log_access") - config.log_access = 1 - - if("log_say") - config.log_say = 1 - - if("log_admin") - config.log_admin = 1 - - if("log_debug") - config.log_debug = 1 - - if("log_game") - config.log_game = 1 - - if("log_vote") - config.log_vote = 1 - - if("log_whisper") - config.log_whisper = 1 - - if("log_attack") - config.log_attack = 1 - - if("log_emote") - config.log_emote = 1 - - if("log_adminchat") - config.log_adminchat = 1 - - if("log_adminwarn") - config.log_adminwarn = 1 - - if("log_pda") - config.log_pda = 1 - - if("log_world_output") - config.log_world_output = 1 - - if("log_hrefs") - config.log_hrefs = 1 - - if("log_runtime") - config.log_runtimes = 1 - - if("mentors") - config.mods_are_mentors = 1 - - if("allow_admin_ooccolor") - config.allow_admin_ooccolor = 1 - - if("pregame_timestart") - config.pregame_timestart = text2num(value) - - if("allow_vote_restart") - config.allow_vote_restart = 1 - - if("allow_vote_mode") - config.allow_vote_mode = 1 - - if("no_dead_vote") - config.vote_no_dead = 1 - - if("vote_autotransfer_initial") - config.vote_autotransfer_initial = text2num(value) - - if("vote_autotransfer_interval") - config.vote_autotransfer_interval = text2num(value) - - if("default_no_vote") - config.vote_no_default = 1 - - if("vote_delay") - config.vote_delay = text2num(value) - - if("vote_period") - config.vote_period = text2num(value) - - if("allow_ai") - config.allow_ai = 1 - -// if("authentication") -// config.enable_authentication = 1 - - if("norespawn") - config.respawn = 0 - - if("servername") - config.server_name = value - - if("server_tag_line") - config.server_tag_line = value - - if("server_extra_features") - config.server_extra_features = value - - if("serversuffix") - config.server_suffix = 1 - - if("minimum_client_build") - config.minimum_client_build = text2num(value) - - if("nudge_script_path") - config.nudge_script_path = value - - if("server") - config.server = value - - if("banappeals") - config.banappeals = value - - if("wikiurl") - config.wikiurl = value - - if("forumurl") - config.forumurl = value - - if("rulesurl") - config.rulesurl = value - - if("githuburl") - config.githuburl = value - - if("discordurl") - config.discordurl = value - - if("discordforumurl") - config.discordforumurl = value - - if("donationsurl") - config.donationsurl = value - - if("repositoryurl") - config.repositoryurl = value - - if("guest_jobban") - config.guest_jobban = 1 - - if("guest_ban") - guests_allowed = 0 - - if("panic_bunker_threshold") - config.panic_bunker_threshold = text2num(value) - - if("usewhitelist") - config.usewhitelist = 1 - - if("feature_object_spell_system") - config.feature_object_spell_system = 1 - - if("allow_metadata") - config.allow_Metadata = 1 - - if("traitor_scaling") - config.traitor_scaling = 1 - - if("protect_roles_from_antagonist") - config.protect_roles_from_antagonist = 1 - - if("probability") - var/prob_pos = findtext(value, " ") - var/prob_name = null - var/prob_value = null - - if(prob_pos) - prob_name = lowertext(copytext(value, 1, prob_pos)) - prob_value = copytext(value, prob_pos + 1) - if(prob_name in config.modes) - config.probabilities[prob_name] = text2num(prob_value) - else - log_config("Unknown game mode probability configuration definition: [prob_name].") - else - log_config("Incorrect probability configuration definition: [prob_name] [prob_value].") - - if("allow_random_events") - config.allow_random_events = 1 - - if("load_jobs_from_txt") - load_jobs_from_txt = 1 - - if("forbid_singulo_possession") - forbid_singulo_possession = 1 - - if("check_randomizer") - check_randomizer = 1 - - if("popup_admin_pm") - config.popup_admin_pm = 1 - - if("allow_holidays") - config.allow_holidays = 1 - - if("use_irc_bot") - use_irc_bot = 1 - - if("ticklag") - Ticklag = text2num(value) - - if("socket_talk") - socket_talk = text2num(value) - - if("allow_antag_hud") - config.antag_hud_allowed = 1 - - if("antag_hud_restricted") - config.antag_hud_restricted = 1 - - if("humans_need_surnames") - humans_need_surnames = 1 - - if("tor_ban") - ToRban = 1 - - if("automute_on") - automute_on = 1 - - if("usealienwhitelist") - usealienwhitelist = 1 - - if("alien_player_ratio") - limitalienplayers = 1 - alien_to_human_ratio = text2num(value) - - if("assistant_maint") - config.assistant_maint = 1 - - if("gateway_delay") - config.gateway_delay = text2num(value) - - if("continuous_rounds") - config.continuous_rounds = 1 - - if("ghost_interaction") - config.ghost_interaction = 1 - - if("comms_password") - config.comms_password = value - - if("irc_bot_host") - config.irc_bot_host = splittext(value, ";") - - if("main_irc") - config.main_irc = value - - if("admin_irc") - config.admin_irc = value - - if("admin_notify_irc") - config.admin_notify_irc = value - - if("cidrandomizer_irc") - config.cidrandomizer_irc = value - - if("python_path") - if(value) - python_path = value - else - if(world.system_type == UNIX) - python_path = "/usr/bin/env python2" - else //probably windows, if not this should work anyway - python_path = "pythonw" - - if("assistant_limit") - config.assistantlimit = 1 - - if("assistant_ratio") - config.assistantratio = text2num(value) - - if("allow_drone_spawn") - config.allow_drone_spawn = text2num(value) - - if("drone_build_time") - config.drone_build_time = text2num(value) - - if("max_maint_drones") - config.max_maint_drones = text2num(value) - - if("expected_round_length") - config.expected_round_length = MinutesToTicks(text2num(value)) - - if("event_custom_start_mundane") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_moderate") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_major") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_delay_lower") - var/values = text2numlist(value, ";") - config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("event_delay_upper") - var/values = text2numlist(value, ";") - config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("starlight") - config.starlight = 1 - - if("player_reroute_cap") - var/vvalue = text2num(value) - config.player_overflow_cap = vvalue >= 0 ? vvalue : 0 - - if("overflow_server_url") - config.overflow_server_url = value - - if("disable_away_missions") - config.disable_away_missions = 1 - - if("disable_space_ruins") - config.disable_space_ruins = 1 - - if("disable_lobby_music") - config.disable_lobby_music = 1 - - if("disable_cid_warn_popup") - config.disable_cid_warn_popup = 1 - - if("extra_space_ruin_levels_min") - var/vvalue = text2num(value) - config.extra_space_ruin_levels_min = max(vvalue, 0) - - if("extra_space_ruin_levels_max") - var/vvalue = text2num(value) - config.extra_space_ruin_levels_max = max(vvalue, 0) - - if("max_loadout_points") - config.max_loadout_points = text2num(value) - - if("round_abandon_penalty_period") - config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) - - if("medal_hub_address") - config.medal_hub_address = value - - if("medal_hub_password") - config.medal_hub_password = value - - if("disable_ooc_emoji") - config.disable_ooc_emoji = 1 - - if("shutdown_on_reboot") - config.shutdown_on_reboot = 1 - - if("shutdown_shell_command") - shutdown_shell_command = value - - if("disable_karma") - config.disable_karma = 1 - - if("start_now_confirmation") - config.start_now_confirmation = 1 - - if("tick_limit_mc_init") - config.tick_limit_mc_init = text2num(value) - if("base_mc_tick_rate") - config.base_mc_tick_rate = text2num(value) - if("high_pop_mc_tick_rate") - config.high_pop_mc_tick_rate = text2num(value) - if("high_pop_mc_mode_amount") - config.high_pop_mc_mode_amount = text2num(value) - if("disable_high_pop_mc_mode_amount") - config.disable_high_pop_mc_mode_amount = text2num(value) - if("developer_express_start") - config.developer_express_start = 1 - if("disable_localhost_admin") - config.disable_localhost_admin = 1 - if("enable_gamemode_player_limit") - config.enable_gamemode_player_limit = 1 - else - log_config("Unknown setting in configuration: '[name]'") - - - else if(type == "game_options") - value = text2num(value) - - switch(name) - if("revival_pod_plants") - config.revival_pod_plants = value - if("revival_cloning") - config.revival_cloning = value - if("revival_brain_life") - config.revival_brain_life = value - if("auto_toggle_ooc_during_round") - config.auto_toggle_ooc_during_round = 1 - if("run_speed") - config.run_speed = value - if("walk_speed") - config.walk_speed = value - if("human_delay") - config.human_delay = value - if("robot_delay") - config.robot_delay = value - if("monkey_delay") - config.monkey_delay = value - if("alien_delay") - config.alien_delay = value - if("slime_delay") - config.slime_delay = value - if("animal_delay") - config.animal_delay = value - if("bones_can_break") - config.bones_can_break = value - if("shuttle_refuel_delay") - config.shuttle_refuel_delay = text2num(value) - if("traitor_objectives_amount") - config.traitor_objectives_amount = text2num(value) - if("reactionary_explosions") - config.reactionary_explosions = 1 - if("bombcap") - var/BombCap = text2num(value) - if(!BombCap) - continue - if(BombCap < 4) - BombCap = 4 - if(BombCap > 128) - BombCap = 128 - - MAX_EX_DEVASTATION_RANGE = round(BombCap/4) - MAX_EX_HEAVY_RANGE = round(BombCap/2) - MAX_EX_LIGHT_RANGE = BombCap - MAX_EX_FLASH_RANGE = BombCap - MAX_EX_FLAME_RANGE = BombCap - if("default_laws") - config.default_laws = text2num(value) - if("randomize_shift_time") - config.randomize_shift_time = TRUE - if("enable_night_shifts") - config.enable_night_shifts = TRUE - if("lavaland_budget") - config.lavaland_budget = text2num(value) - if("cubemonkey_cap") - config.cubemonkeycap = text2num(value) - else - log_config("Unknown setting in configuration: '[name]'") - -/datum/configuration/proc/loadsql(filename) // -- TLE - var/list/Lines = file2list(filename) - var/db_version = 0 - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - var/value = null - - if(pos) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if(!name) - continue - - switch(name) - if("sql_enabled") - config.sql_enabled = 1 - if("address") - sqladdress = value - if("port") - sqlport = value - if("feedback_database") - sqlfdbkdb = value - if("feedback_login") - sqlfdbklogin = value - if("feedback_password") - sqlfdbkpass = value - if("feedback_tableprefix") - sqlfdbktableprefix = value - if("db_version") - db_version = text2num(value) - else - log_config("Unknown setting in configuration: '[name]'") - if(config.sql_enabled && db_version != SQL_VERSION) - config.sql_enabled = 0 - log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!") - spawn(60) - if(SSticker.current_state == GAME_STATE_PREGAME) - going = 0 - spawn(600) - to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED.
    Please check database version for recent upstream changes!
    ") - -/datum/configuration/proc/loadoverflowwhitelist(filename) - var/list/Lines = file2list(filename) - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - config.overflow_whitelist += t - -/datum/configuration/proc/pick_mode(mode_name) - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = T - if(initial(M.config_tag) && initial(M.config_tag) == mode_name) - return new T() - return new /datum/game_mode/extended() - -/datum/configuration/proc/get_runnable_modes() - var/list/datum/game_mode/runnable_modes = new - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = new T() -// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]") - if(!(M.config_tag in modes)) - qdel(M) - continue - if(probabilities[M.config_tag]<=0) - qdel(M) - continue - if(M.can_start()) - runnable_modes[M] = probabilities[M.config_tag] -// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]") - return runnable_modes +/datum/configuration + var/server_name = null // server name (for world name / status) + var/server_tag_line = null // server tagline (for showing on hub entry) + var/server_extra_features = null // server-specific extra features (for hub entry) + var/server_suffix = 0 // generate numeric suffix based on server port + + var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit + + var/nudge_script_path = "nudge.py" // where the nudge.py script is located + + var/log_ooc = 0 // log OOC channel + var/log_access = 0 // log login/logout + var/log_say = 0 // log client say + var/log_admin = 0 // log admin actions + var/log_debug = 1 // log debug output + var/log_game = 0 // log game events + var/log_vote = 0 // log voting + var/log_whisper = 0 // log client whisper + var/log_emote = 0 // log emotes + var/log_attack = 0 // log attack messages + var/log_adminchat = 0 // log admin chat messages + var/log_adminwarn = 0 // log warnings admins get about bomb construction and such + var/log_pda = 0 // log pda messages + var/log_world_output = 0 // log world.log << messages + var/log_runtimes = 0 // logs world.log to a file + var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits + var/sql_enabled = 0 // for sql switching + var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour + var/pregame_timestart = 240 // Time it takes for the server to start the game + var/allow_vote_restart = 0 // allow votes to restart + var/allow_vote_mode = 0 // allow votes to change mode + var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) + var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) + var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called + var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote + var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) + var/vote_no_dead = 0 // dead people can't vote (tbi) +// var/enable_authentication = 0 // goon authentication + var/del_new_on_log = 1 // del's new players if they log before they spawn in + var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard + var/traitor_scaling = 0 //if amount of traitors scales based on amount of players + var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other + var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. + var/allow_Metadata = 0 // Metadata is supported. + var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. + var/Ticklag = 0.5 + var/socket_talk = 0 // use socket_talk to communicate with other processes + var/list/resource_urls = null + var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. + var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. + var/list/mode_names = list() + var/list/modes = list() // allowed modes + var/list/votable_modes = list() // votable modes + var/list/probabilities = list() // relative probability of each mode + var/humans_need_surnames = 0 + var/allow_random_events = 0 // enables random events mid-round when set to 1 + var/allow_ai = 1 // allow ai job + var/respawn = 0 + var/guest_jobban = 1 + var/panic_bunker_threshold = 150 // above this player count threshold, never-before-seen players are blocked from connecting + var/usewhitelist = 0 + var/mods_are_mentors = 0 + var/load_jobs_from_txt = 0 + var/ToRban = 0 + var/automute_on = 0 //enables automuting/spam prevention + var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. + var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized + var/medal_hub_address = null + var/medal_hub_password = null + + var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors + + var/assistantlimit = 0 //enables assistant limiting + var/assistantratio = 2 //how many assistants to security members + + // The AFK subsystem will not be activated if any of the below config values are equal or less than 0 + var/warn_afk_minimum = 0 // How long till you get a warning while being AFK + var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK + var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic) + + var/auto_cryo_ssd_mins = 0 + var/ssd_warning = 0 + + var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb + + var/traitor_objectives_amount = 2 + var/shadowling_max_age = 0 + + var/max_maint_drones = 5 //This many drones can spawn, + var/allow_drone_spawn = 1 //assuming the admin allow them to. + var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. + + var/usealienwhitelist = 0 + var/limitalienplayers = 0 + var/alien_to_human_ratio = 0.5 + + var/server + var/banappeals + var/wikiurl = "http://example.org" + var/forumurl = "http://example.org" + var/rulesurl = "http://example.org" + var/githuburl = "http://example.org" + var/donationsurl = "http://example.org" + var/repositoryurl = "http://example.org" + var/discordurl = "http://example.org" + var/discordforumurl = "http://example.org" + + var/overflow_server_url + var/forbid_singulo_possession = 0 + + var/check_randomizer = 0 + + //game_options.txt configs + + var/bones_can_break = 1 + + var/revival_pod_plants = 1 + var/revival_cloning = 1 + var/revival_brain_life = -1 + + var/auto_toggle_ooc_during_round = 0 + + var/shuttle_refuel_delay = 12000 + + //Used for modifying movement speed for mobs. + //Unversal modifiers + var/run_speed = 0 + var/walk_speed = 0 + + //Mob specific modifiers. NOTE: These will affect different mob types in different ways + var/human_delay = 0 + var/robot_delay = 0 + var/monkey_delay = 0 + var/alien_delay = 0 + var/slime_delay = 0 + var/animal_delay = 0 + + //IP Intel vars + var/ipintel_email + var/ipintel_rating_bad = 1 + var/ipintel_save_good = 12 + var/ipintel_save_bad = 1 + var/ipintel_domain = "check.getipintel.net" + var/ipintel_maxplaytime = 0 + var/ipintel_whitelist = 0 + var/ipintel_detailsurl = "https://iphub.info/?ip=" + + var/forum_link_url + var/forum_playerinfo_url + + var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt + var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt + var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database + var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database + + var/use_exp_tracking = 0 + var/use_exp_restrictions = 0 + var/use_exp_restrictions_admin_bypass = 0 + + var/simultaneous_pm_warning_timeout = 100 + + var/assistant_maint = 0 //Do assistants get maint access? + var/gateway_delay = 6000 //How long the gateway takes before it activates. Default is half an hour. + var/ghost_interaction = 0 + + var/comms_password = "" + + var/use_irc_bot = 0 + var/list/irc_bot_host = list() + var/main_irc = "" + var/admin_irc = "" + var/admin_notify_irc = "" + var/cidrandomizer_irc = "" + + var/default_laws = 0 //Controls what laws the AI spawns with. + + var/list/station_levels = list(1) // Defines which Z-levels the station exists on. + var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle + var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect + var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach + + var/const/minutes_to_ticks = 60 * 10 + // Event settings + var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours + // If the first delay has a custom start time + // No custom time, no custom time, between 80 to 100 minutes respectively. + var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) + // The lowest delay until next event + // 10, 30, 50 minutes respectively + var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) + // The upper delay until next event + // 15, 45, 70 minutes respectively + var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) + + var/starlight = 0 // Whether space turfs have ambient light or not + var/allow_holidays = 0 + var/player_overflow_cap = 0 //number of players before the server starts rerouting + var/list/overflow_whitelist = list() //whitelist for overflow + + var/disable_away_missions = 0 // disable away missions + var/disable_space_ruins = 0 //disable space ruins + + var/extra_space_ruin_levels_min = 2 + var/extra_space_ruin_levels_max = 4 + + var/ooc_allowed = 1 + var/looc_allowed = 1 + var/dooc_allowed = 1 + var/dsay_allowed = 1 + + var/disable_lobby_music = 0 // Disables the lobby music + var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing. + + var/max_loadout_points = 5 // How many points can be spent on extra items in character setup + + var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC + + var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it + + var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default + + // StonedMC + var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling + + // Highpop tickrates + var/base_mc_tick_rate = 1 + var/high_pop_mc_tick_rate = 1.1 + + var/high_pop_mc_mode_amount = 65 + var/disable_high_pop_mc_mode_amount = 60 + + // Nightshift + var/randomize_shift_time = FALSE + var/enable_night_shifts = FALSE + + // Developer + var/developer_express_start = 0 + + // Automatic localhost admin disable + var/disable_localhost_admin = 0 + + //Start now warning + var/start_now_confirmation = 0 + + // Lavaland + var/lavaland_budget = 60 + + //cube monkey limit + var/cubemonkeycap = 20 + + // Makes gamemodes respect player limits + var/enable_gamemode_player_limit = 0 + +/datum/configuration/New() + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = T + + if(initial(M.config_tag)) + if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once + src.modes += initial(M.config_tag) + src.mode_names[initial(M.config_tag)] = initial(M.name) + src.probabilities[initial(M.config_tag)] = initial(M.probability) + if(initial(M.votable)) + src.votable_modes += initial(M.config_tag) + src.votable_modes += "secret" + +/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist + var/list/Lines = file2list(filename) + + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + var/value = null + + if(pos) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if(!name) + continue + + if(type == "config") + switch(name) + if("resource_urls") + config.resource_urls = splittext(value, " ") + + if("admin_legacy_system") + config.admin_legacy_system = 1 + + if("ban_legacy_system") + config.ban_legacy_system = 1 + + if("use_age_restriction_for_jobs") + config.use_age_restriction_for_jobs = 1 + + if("use_age_restriction_for_antags") + config.use_age_restriction_for_antags = 1 + + if("use_exp_tracking") + config.use_exp_tracking = 1 + + if("use_exp_restrictions") + config.use_exp_restrictions = 1 + + if("use_exp_restrictions_admin_bypass") + config.use_exp_restrictions_admin_bypass = 1 + + if("jobs_have_minimal_access") + config.jobs_have_minimal_access = 1 + + if("shadowling_max_age") + config.shadowling_max_age = text2num(value) + + if("warn_afk_minimum") + config.warn_afk_minimum = text2num(value) + if("auto_cryo_afk") + config.auto_cryo_afk = text2num(value) + if("auto_despawn_afk") + config.auto_despawn_afk = text2num(value) + + if("auto_cryo_ssd_mins") + config.auto_cryo_ssd_mins = text2num(value) + if("ssd_warning") + config.ssd_warning = 1 + + if("list_afk_minimum") + config.list_afk_minimum = text2num(value) + + if("ipintel_email") + if(value != "ch@nge.me") + config.ipintel_email = value + if("ipintel_rating_bad") + config.ipintel_rating_bad = text2num(value) + if("ipintel_domain") + config.ipintel_domain = value + if("ipintel_save_good") + config.ipintel_save_good = text2num(value) + if("ipintel_save_bad") + config.ipintel_save_bad = text2num(value) + if("ipintel_maxplaytime") + config.ipintel_maxplaytime = text2num(value) + if("ipintel_whitelist") + config.ipintel_whitelist = 1 + if("ipintel_detailsurl") + config.ipintel_detailsurl = value + + if("forum_link_url") + config.forum_link_url = value + + if("forum_playerinfo_url") + config.forum_playerinfo_url = value + + if("log_ooc") + config.log_ooc = 1 + + if("log_access") + config.log_access = 1 + + if("log_say") + config.log_say = 1 + + if("log_admin") + config.log_admin = 1 + + if("log_debug") + config.log_debug = 1 + + if("log_game") + config.log_game = 1 + + if("log_vote") + config.log_vote = 1 + + if("log_whisper") + config.log_whisper = 1 + + if("log_attack") + config.log_attack = 1 + + if("log_emote") + config.log_emote = 1 + + if("log_adminchat") + config.log_adminchat = 1 + + if("log_adminwarn") + config.log_adminwarn = 1 + + if("log_pda") + config.log_pda = 1 + + if("log_world_output") + config.log_world_output = 1 + + if("log_hrefs") + config.log_hrefs = 1 + + if("log_runtime") + config.log_runtimes = 1 + + if("mentors") + config.mods_are_mentors = 1 + + if("allow_admin_ooccolor") + config.allow_admin_ooccolor = 1 + + if("pregame_timestart") + config.pregame_timestart = text2num(value) + + if("allow_vote_restart") + config.allow_vote_restart = 1 + + if("allow_vote_mode") + config.allow_vote_mode = 1 + + if("no_dead_vote") + config.vote_no_dead = 1 + + if("vote_autotransfer_initial") + config.vote_autotransfer_initial = text2num(value) + + if("vote_autotransfer_interval") + config.vote_autotransfer_interval = text2num(value) + + if("default_no_vote") + config.vote_no_default = 1 + + if("vote_delay") + config.vote_delay = text2num(value) + + if("vote_period") + config.vote_period = text2num(value) + + if("allow_ai") + config.allow_ai = 1 + +// if("authentication") +// config.enable_authentication = 1 + + if("norespawn") + config.respawn = 0 + + if("servername") + config.server_name = value + + if("server_tag_line") + config.server_tag_line = value + + if("server_extra_features") + config.server_extra_features = value + + if("serversuffix") + config.server_suffix = 1 + + if("minimum_client_build") + config.minimum_client_build = text2num(value) + + if("nudge_script_path") + config.nudge_script_path = value + + if("server") + config.server = value + + if("banappeals") + config.banappeals = value + + if("wikiurl") + config.wikiurl = value + + if("forumurl") + config.forumurl = value + + if("rulesurl") + config.rulesurl = value + + if("githuburl") + config.githuburl = value + + if("discordurl") + config.discordurl = value + + if("discordforumurl") + config.discordforumurl = value + + if("donationsurl") + config.donationsurl = value + + if("repositoryurl") + config.repositoryurl = value + + if("guest_jobban") + config.guest_jobban = 1 + + if("guest_ban") + GLOB.guests_allowed = 0 + + if("panic_bunker_threshold") + config.panic_bunker_threshold = text2num(value) + + if("usewhitelist") + config.usewhitelist = 1 + + if("feature_object_spell_system") + config.feature_object_spell_system = 1 + + if("allow_metadata") + config.allow_Metadata = 1 + + if("traitor_scaling") + config.traitor_scaling = 1 + + if("protect_roles_from_antagonist") + config.protect_roles_from_antagonist = 1 + + if("probability") + var/prob_pos = findtext(value, " ") + var/prob_name = null + var/prob_value = null + + if(prob_pos) + prob_name = lowertext(copytext(value, 1, prob_pos)) + prob_value = copytext(value, prob_pos + 1) + if(prob_name in config.modes) + config.probabilities[prob_name] = text2num(prob_value) + else + log_config("Unknown game mode probability configuration definition: [prob_name].") + else + log_config("Incorrect probability configuration definition: [prob_name] [prob_value].") + + if("allow_random_events") + config.allow_random_events = 1 + + if("load_jobs_from_txt") + load_jobs_from_txt = 1 + + if("forbid_singulo_possession") + forbid_singulo_possession = 1 + + if("check_randomizer") + check_randomizer = 1 + + if("popup_admin_pm") + config.popup_admin_pm = 1 + + if("allow_holidays") + config.allow_holidays = 1 + + if("use_irc_bot") + use_irc_bot = 1 + + if("ticklag") + Ticklag = text2num(value) + + if("socket_talk") + socket_talk = text2num(value) + + if("allow_antag_hud") + config.antag_hud_allowed = 1 + + if("antag_hud_restricted") + config.antag_hud_restricted = 1 + + if("humans_need_surnames") + humans_need_surnames = 1 + + if("tor_ban") + ToRban = 1 + + if("automute_on") + automute_on = 1 + + if("usealienwhitelist") + usealienwhitelist = 1 + + if("alien_player_ratio") + limitalienplayers = 1 + alien_to_human_ratio = text2num(value) + + if("assistant_maint") + config.assistant_maint = 1 + + if("gateway_delay") + config.gateway_delay = text2num(value) + + if("continuous_rounds") + config.continuous_rounds = 1 + + if("ghost_interaction") + config.ghost_interaction = 1 + + if("comms_password") + config.comms_password = value + + if("irc_bot_host") + config.irc_bot_host = splittext(value, ";") + + if("main_irc") + config.main_irc = value + + if("admin_irc") + config.admin_irc = value + + if("admin_notify_irc") + config.admin_notify_irc = value + + if("cidrandomizer_irc") + config.cidrandomizer_irc = value + + if("python_path") + if(value) + GLOB.python_path = value + else + if(world.system_type == UNIX) + GLOB.python_path = "/usr/bin/env python2" + else //probably windows, if not this should work anyway + GLOB.python_path = "pythonw" + + if("assistant_limit") + config.assistantlimit = 1 + + if("assistant_ratio") + config.assistantratio = text2num(value) + + if("allow_drone_spawn") + config.allow_drone_spawn = text2num(value) + + if("drone_build_time") + config.drone_build_time = text2num(value) + + if("max_maint_drones") + config.max_maint_drones = text2num(value) + + if("expected_round_length") + config.expected_round_length = MinutesToTicks(text2num(value)) + + if("event_custom_start_mundane") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_moderate") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_major") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_delay_lower") + var/values = text2numlist(value, ";") + config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("event_delay_upper") + var/values = text2numlist(value, ";") + config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("starlight") + config.starlight = 1 + + if("player_reroute_cap") + var/vvalue = text2num(value) + config.player_overflow_cap = vvalue >= 0 ? vvalue : 0 + + if("overflow_server_url") + config.overflow_server_url = value + + if("disable_away_missions") + config.disable_away_missions = 1 + + if("disable_space_ruins") + config.disable_space_ruins = 1 + + if("disable_lobby_music") + config.disable_lobby_music = 1 + + if("disable_cid_warn_popup") + config.disable_cid_warn_popup = 1 + + if("extra_space_ruin_levels_min") + var/vvalue = text2num(value) + config.extra_space_ruin_levels_min = max(vvalue, 0) + + if("extra_space_ruin_levels_max") + var/vvalue = text2num(value) + config.extra_space_ruin_levels_max = max(vvalue, 0) + + if("max_loadout_points") + config.max_loadout_points = text2num(value) + + if("round_abandon_penalty_period") + config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) + + if("medal_hub_address") + config.medal_hub_address = value + + if("medal_hub_password") + config.medal_hub_password = value + + if("disable_ooc_emoji") + config.disable_ooc_emoji = 1 + + if("shutdown_on_reboot") + config.shutdown_on_reboot = 1 + + if("shutdown_shell_command") + GLOB.shutdown_shell_command = value + + if("disable_karma") + config.disable_karma = 1 + + if("start_now_confirmation") + config.start_now_confirmation = 1 + + if("tick_limit_mc_init") + config.tick_limit_mc_init = text2num(value) + if("base_mc_tick_rate") + config.base_mc_tick_rate = text2num(value) + if("high_pop_mc_tick_rate") + config.high_pop_mc_tick_rate = text2num(value) + if("high_pop_mc_mode_amount") + config.high_pop_mc_mode_amount = text2num(value) + if("disable_high_pop_mc_mode_amount") + config.disable_high_pop_mc_mode_amount = text2num(value) + if("developer_express_start") + config.developer_express_start = 1 + if("disable_localhost_admin") + config.disable_localhost_admin = 1 + if("enable_gamemode_player_limit") + config.enable_gamemode_player_limit = 1 + else + log_config("Unknown setting in configuration: '[name]'") + + + else if(type == "game_options") + value = text2num(value) + + switch(name) + if("revival_pod_plants") + config.revival_pod_plants = value + if("revival_cloning") + config.revival_cloning = value + if("revival_brain_life") + config.revival_brain_life = value + if("auto_toggle_ooc_during_round") + config.auto_toggle_ooc_during_round = 1 + if("run_speed") + config.run_speed = value + if("walk_speed") + config.walk_speed = value + if("human_delay") + config.human_delay = value + if("robot_delay") + config.robot_delay = value + if("monkey_delay") + config.monkey_delay = value + if("alien_delay") + config.alien_delay = value + if("slime_delay") + config.slime_delay = value + if("animal_delay") + config.animal_delay = value + if("bones_can_break") + config.bones_can_break = value + if("shuttle_refuel_delay") + config.shuttle_refuel_delay = text2num(value) + if("traitor_objectives_amount") + config.traitor_objectives_amount = text2num(value) + if("reactionary_explosions") + config.reactionary_explosions = 1 + if("bombcap") + var/BombCap = text2num(value) + if(!BombCap) + continue + if(BombCap < 4) + BombCap = 4 + if(BombCap > 128) + BombCap = 128 + + GLOB.max_ex_devastation_range = round(BombCap/4) + GLOB.max_ex_heavy_range = round(BombCap/2) + GLOB.max_ex_light_range = BombCap + GLOB.max_ex_flash_range = BombCap + GLOB.max_ex_flame_range = BombCap + if("default_laws") + config.default_laws = text2num(value) + if("randomize_shift_time") + config.randomize_shift_time = TRUE + if("enable_night_shifts") + config.enable_night_shifts = TRUE + if("lavaland_budget") + config.lavaland_budget = text2num(value) + if("cubemonkey_cap") + config.cubemonkeycap = text2num(value) + else + log_config("Unknown setting in configuration: '[name]'") + +/datum/configuration/proc/loadsql(filename) // -- TLE + var/list/Lines = file2list(filename) + var/db_version = 0 + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + var/value = null + + if(pos) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if(!name) + continue + + switch(name) + if("sql_enabled") + config.sql_enabled = 1 + if("address") + sqladdress = value + if("port") + sqlport = value + if("feedback_database") + sqlfdbkdb = value + if("feedback_login") + sqlfdbklogin = value + if("feedback_password") + sqlfdbkpass = value + if("feedback_tableprefix") + sqlfdbktableprefix = value + if("db_version") + db_version = text2num(value) + else + log_config("Unknown setting in configuration: '[name]'") + if(config.sql_enabled && db_version != SQL_VERSION) + config.sql_enabled = 0 + log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!") + spawn(60) + if(SSticker.current_state == GAME_STATE_PREGAME) + SSticker.ticker_going = FALSE + spawn(600) + to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED.
    Please check database version for recent upstream changes!
    ") + +/datum/configuration/proc/loadoverflowwhitelist(filename) + var/list/Lines = file2list(filename) + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + config.overflow_whitelist += t + +/datum/configuration/proc/pick_mode(mode_name) + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = T + if(initial(M.config_tag) && initial(M.config_tag) == mode_name) + return new T() + return new /datum/game_mode/extended() + +/datum/configuration/proc/get_runnable_modes() + var/list/datum/game_mode/runnable_modes = new + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = new T() +// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]") + if(!(M.config_tag in modes)) + qdel(M) + continue + if(probabilities[M.config_tag]<=0) + qdel(M) + continue + if(M.can_start()) + runnable_modes[M] = probabilities[M.config_tag] +// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]") + return runnable_modes diff --git a/code/controllers/controller.dm b/code/controllers/controller.dm index 06547d120d52..c9d5f1e5650c 100644 --- a/code/controllers/controller.dm +++ b/code/controllers/controller.dm @@ -16,4 +16,4 @@ /datum/controller/proc/Recover() -/datum/controller/proc/stat_entry() \ No newline at end of file +/datum/controller/proc/stat_entry() diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 8b0f0b1987ec..bebcdc7a8979 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -1,102 +1,102 @@ - /** - * Failsafe - * - * Pretty much pokes the MC to make sure it's still alive. - **/ - -GLOBAL_REAL(Failsafe, /datum/controller/failsafe) - -/datum/controller/failsafe // This thing pretty much just keeps poking the master controller - name = "Failsafe" - - // The length of time to check on the MC (in deciseconds). - // Set to 0 to disable. - var/processing_interval = 20 - // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. - var/defcon = 5 - //the world.time of the last check, so the mc can restart US if we hang. - // (Real friends look out for *eachother*) - var/lasttick = 0 - - // Track the MC iteration to make sure its still on track. - var/master_iteration = 0 - var/running = TRUE - -/datum/controller/failsafe/New() - // Highlander-style: there can only be one! Kill off the old and replace it with the new. - if(Failsafe != src) - if(istype(Failsafe)) - qdel(Failsafe) - Failsafe = src - Initialize() - -/datum/controller/failsafe/Initialize() - set waitfor = 0 - Failsafe.Loop() - if(!QDELETED(src)) - qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us - -/datum/controller/failsafe/Destroy() - running = FALSE - ..() - return QDEL_HINT_HARDDEL_NOW - -/datum/controller/failsafe/proc/Loop() - while(running) - lasttick = world.time - if(!Master) - // Replace the missing Master! This should never, ever happen. - new /datum/controller/master() - // Only poke it if overrides are not in effect. - if(processing_interval > 0) - if(Master.processing && Master.iteration) - // Check if processing is done yet. - if(Master.iteration == master_iteration) - switch(defcon) - if(4,5) - --defcon - if(3) - message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.") - --defcon - if(2) - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") - --defcon - if(1) - - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...") - --defcon - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else if(rtn < 0) - log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") - to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") - //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again - //no need to handle that specially when defcon 0 can handle it - if(0) //DEFCON 0! (mc failed to restart) - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else - defcon = min(defcon + 1,5) - master_iteration = Master.iteration - if(defcon <= 1) - sleep(processing_interval * 2) - else - sleep(processing_interval) - else - defcon = 5 - sleep(initial(processing_interval)) - -/datum/controller/failsafe/proc/defcon_pretty() - return defcon - -/datum/controller/failsafe/stat_entry() - if(!statclick) - statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - - stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) + /** + * Failsafe + * + * Pretty much pokes the MC to make sure it's still alive. + **/ + +GLOBAL_REAL(Failsafe, /datum/controller/failsafe) + +/datum/controller/failsafe // This thing pretty much just keeps poking the master controller + name = "Failsafe" + + // The length of time to check on the MC (in deciseconds). + // Set to 0 to disable. + var/processing_interval = 20 + // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. + var/defcon = 5 + //the world.time of the last check, so the mc can restart US if we hang. + // (Real friends look out for *eachother*) + var/lasttick = 0 + + // Track the MC iteration to make sure its still on track. + var/master_iteration = 0 + var/running = TRUE + +/datum/controller/failsafe/New() + // Highlander-style: there can only be one! Kill off the old and replace it with the new. + if(Failsafe != src) + if(istype(Failsafe)) + qdel(Failsafe) + Failsafe = src + Initialize() + +/datum/controller/failsafe/Initialize() + set waitfor = 0 + Failsafe.Loop() + if(!QDELETED(src)) + qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us + +/datum/controller/failsafe/Destroy() + running = FALSE + ..() + return QDEL_HINT_HARDDEL_NOW + +/datum/controller/failsafe/proc/Loop() + while(running) + lasttick = world.time + if(!Master) + // Replace the missing Master! This should never, ever happen. + new /datum/controller/master() + // Only poke it if overrides are not in effect. + if(processing_interval > 0) + if(Master.processing && Master.iteration) + // Check if processing is done yet. + if(Master.iteration == master_iteration) + switch(defcon) + if(4,5) + --defcon + if(3) + message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.") + --defcon + if(2) + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") + --defcon + if(1) + + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...") + --defcon + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else if(rtn < 0) + log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") + to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") + //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again + //no need to handle that specially when defcon 0 can handle it + if(0) //DEFCON 0! (mc failed to restart) + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else + defcon = min(defcon + 1,5) + master_iteration = Master.iteration + if(defcon <= 1) + sleep(processing_interval * 2) + else + sleep(processing_interval) + else + defcon = 5 + sleep(initial(processing_interval)) + +/datum/controller/failsafe/proc/defcon_pretty() + return defcon + +/datum/controller/failsafe/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) + + stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm index 543f0aed4b2f..5a520b547e1c 100644 --- a/code/controllers/globals.dm +++ b/code/controllers/globals.dm @@ -64,4 +64,4 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) call(src, I)() var/end_tick = world.time if(end_tick - start_tick) - warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") \ No newline at end of file + warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 10c6b42a369e..f64e566e0973 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -615,4 +615,4 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(client_count < config.disable_high_pop_mc_mode_amount) processing = config.base_mc_tick_rate else if(client_count > config.high_pop_mc_mode_amount) - processing = config.high_pop_mc_tick_rate \ No newline at end of file + processing = config.high_pop_mc_tick_rate diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 7ba1b68d4a60..b8996f2cf897 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -214,4 +214,4 @@ next_fire = world.time + wait if("queued_priority") //editing this breaks things. return 0 - . = ..() \ No newline at end of file + . = ..() diff --git a/code/controllers/subsystem/acid.dm b/code/controllers/subsystem/acid.dm index f86c8540af57..40063b00a873 100644 --- a/code/controllers/subsystem/acid.dm +++ b/code/controllers/subsystem/acid.dm @@ -33,4 +33,4 @@ SUBSYSTEM_DEF(acid) processing -= O if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm index 9f409fd35b43..6ab1d67a816a 100644 --- a/code/controllers/subsystem/afk.dm +++ b/code/controllers/subsystem/afk.dm @@ -78,4 +78,4 @@ SUBSYSTEM_DEF(afk) break #undef AFK_WARNED -#undef AFK_CRYOD \ No newline at end of file +#undef AFK_CRYOD diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index fe48494f321b..6f2a451ef760 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -275,7 +275,7 @@ SUBSYSTEM_DEF(air) if(blockchanges && T.excited_group) T.excited_group.garbage_collect() else - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(!(T.atmos_adjacent_turfs & direction)) continue var/turf/simulated/S = get_step(T, direction) @@ -360,21 +360,21 @@ SUBSYSTEM_DEF(air) return count /datum/controller/subsystem/air/proc/setup_overlays() - plmaster = new /obj/effect/overlay() - plmaster.icon = 'icons/effects/tile_effects.dmi' - plmaster.icon_state = "plasma" - plmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - plmaster.anchored = TRUE // should only appear in vis_contents, but to be safe - plmaster.layer = FLY_LAYER - plmaster.appearance_flags = TILE_BOUND - - slmaster = new /obj/effect/overlay() - slmaster.icon = 'icons/effects/tile_effects.dmi' - slmaster.icon_state = "sleeping_agent" - slmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - slmaster.anchored = TRUE // should only appear in vis_contents, but to be safe - slmaster.layer = FLY_LAYER - slmaster.appearance_flags = TILE_BOUND + GLOB.plmaster = new /obj/effect/overlay() + GLOB.plmaster.icon = 'icons/effects/tile_effects.dmi' + GLOB.plmaster.icon_state = "plasma" + GLOB.plmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + GLOB.plmaster.anchored = TRUE // should only appear in vis_contents, but to be safe + GLOB.plmaster.layer = FLY_LAYER + GLOB.plmaster.appearance_flags = TILE_BOUND + + GLOB.slmaster = new /obj/effect/overlay() + GLOB.slmaster.icon = 'icons/effects/tile_effects.dmi' + GLOB.slmaster.icon_state = "sleeping_agent" + GLOB.slmaster.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + GLOB.slmaster.anchored = TRUE // should only appear in vis_contents, but to be safe + GLOB.slmaster.layer = FLY_LAYER + GLOB.slmaster.appearance_flags = TILE_BOUND #undef SSAIR_PIPENETS #undef SSAIR_ATMOSMACHINERY @@ -382,4 +382,4 @@ SUBSYSTEM_DEF(air) #undef SSAIR_EXCITEDGROUPS #undef SSAIR_HIGHPRESSURE #undef SSAIR_HOTSPOTS -#undef SSAIR_SUPERCONDUCTIVITY \ No newline at end of file +#undef SSAIR_SUPERCONDUCTIVITY diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm index 90e3ff960f19..bac32a449de3 100644 --- a/code/controllers/subsystem/alarm.dm +++ b/code/controllers/subsystem/alarm.dm @@ -1,30 +1,30 @@ -SUBSYSTEM_DEF(alarms) - name = "Alarms" - init_order = INIT_ORDER_ALARMS // 2 - var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() - var/datum/alarm_handler/burglar/burglar_alarm = new() - var/datum/alarm_handler/camera/camera_alarm = new() - var/datum/alarm_handler/fire/fire_alarm = new() - var/datum/alarm_handler/motion/motion_alarm = new() - var/datum/alarm_handler/power/power_alarm = new() - var/list/datum/alarm/all_handlers - -/datum/controller/subsystem/alarms/Initialize(start_timeofday) - all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) - return ..() - -/datum/controller/subsystem/alarms/fire() - for(var/datum/alarm_handler/AH in all_handlers) - AH.process() - -/datum/controller/subsystem/alarms/proc/active_alarms() - var/list/all_alarms = new () - for(var/datum/alarm_handler/AH in all_handlers) - var/list/alarms = AH.alarms - all_alarms += alarms - - return all_alarms - -/datum/controller/subsystem/alarms/proc/number_of_active_alarms() - var/list/alarms = active_alarms() - return alarms.len +SUBSYSTEM_DEF(alarms) + name = "Alarms" + init_order = INIT_ORDER_ALARMS // 2 + var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() + var/datum/alarm_handler/burglar/burglar_alarm = new() + var/datum/alarm_handler/camera/camera_alarm = new() + var/datum/alarm_handler/fire/fire_alarm = new() + var/datum/alarm_handler/motion/motion_alarm = new() + var/datum/alarm_handler/power/power_alarm = new() + var/list/datum/alarm/all_handlers + +/datum/controller/subsystem/alarms/Initialize(start_timeofday) + all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) + return ..() + +/datum/controller/subsystem/alarms/fire() + for(var/datum/alarm_handler/AH in all_handlers) + AH.process() + +/datum/controller/subsystem/alarms/proc/active_alarms() + var/list/all_alarms = new () + for(var/datum/alarm_handler/AH in all_handlers) + var/list/alarms = AH.alarms + all_alarms += alarms + + return all_alarms + +/datum/controller/subsystem/alarms/proc/number_of_active_alarms() + var/list/alarms = active_alarms() + return alarms.len diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm index cd531db614a8..7a9a3d355f34 100644 --- a/code/controllers/subsystem/assets.dm +++ b/code/controllers/subsystem/assets.dm @@ -14,4 +14,4 @@ SUBSYSTEM_DEF(assets) for(var/client/C in GLOB.clients) addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10) - return ..() \ No newline at end of file + return ..() diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index d2f87c93e06d..dbe38634e6a7 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -63,4 +63,4 @@ SUBSYSTEM_DEF(chat) C.chatOutput.messageQueue += message return - payload[C] += twiceEncoded \ No newline at end of file + payload[C] += twiceEncoded diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 2f415efa38dd..1a7ae8ae81a9 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -1,299 +1,299 @@ -SUBSYSTEM_DEF(events) - name = "Events" - init_order = INIT_ORDER_EVENTS - runlevels = RUNLEVEL_GAME - // Report events at the end of the rouund - var/report_at_round_end = 0 - - // UI vars - var/window_x = 700 - var/window_y = 600 - var/table_options = " align='center'" - var/head_options = " style='font-weight:bold;'" - var/row_options1 = " width='85px'" - var/row_options2 = " width='260px'" - var/row_options3 = " width='150px'" - - // Event vars - var/datum/event_container/selected_event_container = null - var/list/active_events = list() - var/list/finished_events = list() - var/list/allEvents - var/list/event_containers = list( - EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, - EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, - EVENT_LEVEL_MAJOR = new/datum/event_container/major - ) - - var/datum/event_meta/new_event = new - -/datum/controller/subsystem/events/Initialize() - allEvents = subtypesof(/datum/event) - return ..() - -/datum/controller/subsystem/events/fire() - for(var/datum/event/E in active_events) - E.process() - - for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/list/datum/event_container/EC = event_containers[i] - EC.process() - -/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) - if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" - log_debug("Event of '[E.type]' with missing meta-data has completed.") - return - - finished_events += E - - var/theseverity - - if(!E.severity) - theseverity = EVENT_LEVEL_MODERATE - - if(!E.severity == EVENT_LEVEL_MUNDANE && !E.severity == EVENT_LEVEL_MODERATE && !E.severity == EVENT_LEVEL_MAJOR) - theseverity = EVENT_LEVEL_MODERATE //just to be careful - - if(E.severity) - theseverity = E.severity - - // Add the event back to the list of available events - var/datum/event_container/EC = event_containers[theseverity] - var/datum/event_meta/EM = E.event_meta - EC.available_events += EM - - log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].") - -/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) - var/list/datum/event_container/EC = event_containers[severity] - EC.next_event_time += delay - -/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) - - var/html = GetInteractWindow() - - var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) - popup.set_content(html) - popup.open() - -/datum/controller/subsystem/events/proc/RoundEnd() - if(!report_at_round_end) - return - - to_chat(world, "


    Random Events This Round:") - for(var/datum/event/E in active_events|finished_events) - var/datum/event_meta/EM = E.event_meta - if(EM.name == "Nothing") - continue - var/message = "'[EM.name]' began at [station_time_timestamp("hh:mm:ss", E.startedAt)] " - if(E.isRunning) - message += "and is still running." - else - if(E.endedAt - E.startedAt > MinutesToTicks(5)) // Only mention end time if the entire duration was more than 5 minutes - message += "and ended at [station_time_timestamp("hh:mm:ss", E.endedAt)]." - else - message += "and ran to completion." - - to_chat(world, message) - -/datum/controller/subsystem/events/proc/GetInteractWindow() - var/html = "Refresh" - - if(selected_event_container) - var/event_time = max(0, selected_event_container.next_event_time - world.time) - html += "Back
    " - html += "Time till start: [round(event_time / 600, 0.1)]
    " - html += "
    " - html += "

    Available [severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

    " - html += "" - html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" - for(var/datum/event_meta/EM in selected_event_container.available_events) - html += "" - html += "[EM.name]" - html += "[EM.weight]" - html += "[EM.min_weight]" - html += "[EM.max_weight]" - html += "[EM.one_shot]" - html += "[EM.enabled]" - html += "[EM.get_weight(number_active_with_role())]" - html += "Remove" - html += "" - html += "" - html += "
    " - - html += "
    " - html += "

    Add Event

    " - html += "" - html += "NameTypeWeightOneShot" - html += "" - html += "[new_event.name ? new_event.name : "Enter Event"]" - html += "[new_event.event_type ? new_event.event_type : "Select Type"]" - html += "[new_event.weight ? new_event.weight : 0]" - html += "[new_event.one_shot]" - html += "" - html += "" - html += "Add
    " - html += "
    " - else - html += "Round End Report: [report_at_round_end ? "On": "Off"]
    " - html += "
    " - html += "

    Event Start

    " - - html += "" - html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/next_event_at = max(0, EC.next_event_time - world.time) - html += "" - html += "[severity_to_string[severity]]" - html += "[station_time_timestamp("hh:mm:ss", max(EC.next_event_time, world.time))]" - html += "[round(next_event_at / 600, 0.1)]" - html += "" - html += "--" - html += "-" - html += "+" - html += "++" - html += "" - html += "" - html += "[EC.delayed ? "Resume" : "Pause"]" - html += "" - html += "" - html += "[EC.delay_modifier]" - html += "" - html += "" - html += "" - html += "
    " - - html += "
    " - html += "

    Next Event

    " - html += "" - html += "SeverityNameEvent RotationClear" - for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/datum/event_container/EC = event_containers[severity] - var/datum/event_meta/EM = EC.next_event - html += "" - html += "[severity_to_string[severity]]" - html += "[EM ? EM.name : "Random"]" - html += "View" - html += "Clear" - html += "" - html += "" - html += "
    " - - html += "
    " - html += "

    Running Events

    " - html += "Estimated times, affected by master controller delays." - html += "" - html += "SeverityNameEnds AtEnds InStop" - for(var/datum/event/E in active_events) - if(!E.event_meta) - continue - var/datum/event_meta/EM = E.event_meta - var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes - var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) - var/no_end = E.noAutoEnd - html += "" - html += "[severity_to_string[EM.severity]]" - html += "[EM.name]" - html += "[no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)]" - html += "[no_end ? "N/A" : ends_in]" - html += "Stop" - html += "" - html += "" - html += "
    " - - return html - -/datum/controller/subsystem/events/Topic(href, href_list) - if(..()) - return - - - if(href_list["toggle_report"]) - report_at_round_end = !report_at_round_end - admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") - else if(href_list["dec_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"]))) - EC.next_event_time -= decrease - admin_log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") - else if(href_list["inc_timer"]) - var/datum/event_container/EC = locate(href_list["event"]) - var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"]))) - EC.next_event_time += increase - admin_log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") - else if(href_list["select_event"]) - var/datum/event_container/EC = locate(href_list["select_event"]) - var/datum/event_meta/EM = EC.SelectEvent() - if(EM) - admin_log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") - else if(href_list["pause"]) - var/datum/event_container/EC = locate(href_list["pause"]) - EC.delayed = !EC.delayed - admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") - else if(href_list["interval"]) - var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null - if(delay && delay > 0) - var/datum/event_container/EC = locate(href_list["interval"]) - EC.delay_modifier = delay - admin_log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") - else if(href_list["stop"]) - if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes") - return - var/datum/event/E = locate(href_list["stop"]) - var/datum/event_meta/EM = E.event_meta - admin_log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") - E.kill() - else if(href_list["view_events"]) - selected_event_container = locate(href_list["view_events"]) - else if(href_list["back"]) - selected_event_container = null - else if(href_list["set_name"]) - var/name = clean_input("Enter event name.", "Set Name") - if(name) - var/datum/event_meta/EM = locate(href_list["set_name"]) - EM.name = name - else if(href_list["set_type"]) - var/type = input("Select event type.", "Select") as null|anything in allEvents - if(type) - var/datum/event_meta/EM = locate(href_list["set_type"]) - EM.event_type = type - else if(href_list["set_weight"]) - var/weight = input("Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") as num|null - if(weight && weight > 0) - var/datum/event_meta/EM = locate(href_list["set_weight"]) - EM.weight = weight - if(EM != new_event) - admin_log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") - else if(href_list["toggle_oneshot"]) - var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) - EM.one_shot = !EM.one_shot - if(EM != new_event) - admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["toggle_enabled"]) - var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) - EM.enabled = !EM.enabled - admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["remove"]) - if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes") - return - var/datum/event_meta/EM = locate(href_list["remove"]) - var/datum/event_container/EC = locate(href_list["EC"]) - EC.available_events -= EM - admin_log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") - else if(href_list["add"]) - if(!new_event.name || !new_event.event_type) - return - if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes") - return - new_event.severity = selected_event_container.severity - selected_event_container.available_events += new_event - admin_log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") - new_event = new - else if(href_list["clear"]) - var/datum/event_container/EC = locate(href_list["clear"]) - if(EC.next_event) - admin_log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") - EC.next_event = null - - Interact(usr) +SUBSYSTEM_DEF(events) + name = "Events" + init_order = INIT_ORDER_EVENTS + runlevels = RUNLEVEL_GAME + // Report events at the end of the rouund + var/report_at_round_end = 0 + + // UI vars + var/window_x = 700 + var/window_y = 600 + var/table_options = " align='center'" + var/head_options = " style='font-weight:bold;'" + var/row_options1 = " width='85px'" + var/row_options2 = " width='260px'" + var/row_options3 = " width='150px'" + + // Event vars + var/datum/event_container/selected_event_container = null + var/list/active_events = list() + var/list/finished_events = list() + var/list/allEvents + var/list/event_containers = list( + EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, + EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, + EVENT_LEVEL_MAJOR = new/datum/event_container/major + ) + + var/datum/event_meta/new_event = new + +/datum/controller/subsystem/events/Initialize() + allEvents = subtypesof(/datum/event) + return ..() + +/datum/controller/subsystem/events/fire() + for(var/datum/event/E in active_events) + E.process() + + for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/list/datum/event_container/EC = event_containers[i] + EC.process() + +/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) + if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" + log_debug("Event of '[E.type]' with missing meta-data has completed.") + return + + finished_events += E + + var/theseverity + + if(!E.severity) + theseverity = EVENT_LEVEL_MODERATE + + if(!E.severity == EVENT_LEVEL_MUNDANE && !E.severity == EVENT_LEVEL_MODERATE && !E.severity == EVENT_LEVEL_MAJOR) + theseverity = EVENT_LEVEL_MODERATE //just to be careful + + if(E.severity) + theseverity = E.severity + + // Add the event back to the list of available events + var/datum/event_container/EC = event_containers[theseverity] + var/datum/event_meta/EM = E.event_meta + EC.available_events += EM + + log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].") + +/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) + var/list/datum/event_container/EC = event_containers[severity] + EC.next_event_time += delay + +/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) + + var/html = GetInteractWindow() + + var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) + popup.set_content(html) + popup.open() + +/datum/controller/subsystem/events/proc/RoundEnd() + if(!report_at_round_end) + return + + to_chat(world, "


    Random Events This Round:") + for(var/datum/event/E in active_events|finished_events) + var/datum/event_meta/EM = E.event_meta + if(EM.name == "Nothing") + continue + var/message = "'[EM.name]' began at [station_time_timestamp("hh:mm:ss", E.startedAt)] " + if(E.isRunning) + message += "and is still running." + else + if(E.endedAt - E.startedAt > MinutesToTicks(5)) // Only mention end time if the entire duration was more than 5 minutes + message += "and ended at [station_time_timestamp("hh:mm:ss", E.endedAt)]." + else + message += "and ran to completion." + + to_chat(world, message) + +/datum/controller/subsystem/events/proc/GetInteractWindow() + var/html = "Refresh" + + if(selected_event_container) + var/event_time = max(0, selected_event_container.next_event_time - world.time) + html += "Back
    " + html += "Time till start: [round(event_time / 600, 0.1)]
    " + html += "
    " + html += "

    Available [GLOB.severity_to_string[selected_event_container.severity]] Events (queued & running events will not be displayed)

    " + html += "" + html += "Name Weight MinWeight MaxWeight OneShot Enabled CurrWeight Remove" + for(var/datum/event_meta/EM in selected_event_container.available_events) + html += "" + html += "[EM.name]" + html += "[EM.weight]" + html += "[EM.min_weight]" + html += "[EM.max_weight]" + html += "[EM.one_shot]" + html += "[EM.enabled]" + html += "[EM.get_weight(number_active_with_role())]" + html += "Remove" + html += "" + html += "" + html += "
    " + + html += "
    " + html += "

    Add Event

    " + html += "" + html += "NameTypeWeightOneShot" + html += "" + html += "[new_event.name ? new_event.name : "Enter Event"]" + html += "[new_event.event_type ? new_event.event_type : "Select Type"]" + html += "[new_event.weight ? new_event.weight : 0]" + html += "[new_event.one_shot]" + html += "" + html += "" + html += "Add
    " + html += "
    " + else + html += "Round End Report: [report_at_round_end ? "On": "Off"]
    " + html += "
    " + html += "

    Event Start

    " + + html += "" + html += "SeverityStarts AtStarts InAdjust StartPauseInterval Mod" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/next_event_at = max(0, EC.next_event_time - world.time) + html += "" + html += "[GLOB.severity_to_string[severity]]" + html += "[station_time_timestamp("hh:mm:ss", max(EC.next_event_time, world.time))]" + html += "[round(next_event_at / 600, 0.1)]" + html += "" + html += "--" + html += "-" + html += "+" + html += "++" + html += "" + html += "" + html += "[EC.delayed ? "Resume" : "Pause"]" + html += "" + html += "" + html += "[EC.delay_modifier]" + html += "" + html += "" + html += "" + html += "
    " + + html += "
    " + html += "

    Next Event

    " + html += "" + html += "SeverityNameEvent RotationClear" + for(var/severity = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) + var/datum/event_container/EC = event_containers[severity] + var/datum/event_meta/EM = EC.next_event + html += "" + html += "[GLOB.severity_to_string[severity]]" + html += "[EM ? EM.name : "Random"]" + html += "View" + html += "Clear" + html += "" + html += "" + html += "
    " + + html += "
    " + html += "

    Running Events

    " + html += "Estimated times, affected by master controller delays." + html += "" + html += "SeverityNameEnds AtEnds InStop" + for(var/datum/event/E in active_events) + if(!E.event_meta) + continue + var/datum/event_meta/EM = E.event_meta + var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes + var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) + var/no_end = E.noAutoEnd + html += "" + html += "[GLOB.severity_to_string[EM.severity]]" + html += "[EM.name]" + html += "[no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)]" + html += "[no_end ? "N/A" : ends_in]" + html += "Stop" + html += "" + html += "" + html += "
    " + + return html + +/datum/controller/subsystem/events/Topic(href, href_list) + if(..()) + return + + + if(href_list["toggle_report"]) + report_at_round_end = !report_at_round_end + admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") + else if(href_list["dec_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"]))) + EC.next_event_time -= decrease + admin_log_and_message_admins("decreased timer for [GLOB.severity_to_string[EC.severity]] events by [decrease/600] minute(s).") + else if(href_list["inc_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"]))) + EC.next_event_time += increase + admin_log_and_message_admins("increased timer for [GLOB.severity_to_string[EC.severity]] events by [increase/600] minute(s).") + else if(href_list["select_event"]) + var/datum/event_container/EC = locate(href_list["select_event"]) + var/datum/event_meta/EM = EC.SelectEvent() + if(EM) + admin_log_and_message_admins("has queued the [GLOB.severity_to_string[EC.severity]] event '[EM.name]'.") + else if(href_list["pause"]) + var/datum/event_container/EC = locate(href_list["pause"]) + EC.delayed = !EC.delayed + admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [GLOB.severity_to_string[EC.severity]] events.") + else if(href_list["interval"]) + var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null + if(delay && delay > 0) + var/datum/event_container/EC = locate(href_list["interval"]) + EC.delay_modifier = delay + admin_log_and_message_admins("has set the interval modifier for [GLOB.severity_to_string[EC.severity]] events to [EC.delay_modifier].") + else if(href_list["stop"]) + if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes") + return + var/datum/event/E = locate(href_list["stop"]) + var/datum/event_meta/EM = E.event_meta + admin_log_and_message_admins("has stopped the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + E.kill() + else if(href_list["view_events"]) + selected_event_container = locate(href_list["view_events"]) + else if(href_list["back"]) + selected_event_container = null + else if(href_list["set_name"]) + var/name = clean_input("Enter event name.", "Set Name") + if(name) + var/datum/event_meta/EM = locate(href_list["set_name"]) + EM.name = name + else if(href_list["set_type"]) + var/type = input("Select event type.", "Select") as null|anything in allEvents + if(type) + var/datum/event_meta/EM = locate(href_list["set_type"]) + EM.event_type = type + else if(href_list["set_weight"]) + var/weight = input("Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") as num|null + if(weight && weight > 0) + var/datum/event_meta/EM = locate(href_list["set_weight"]) + EM.weight = weight + if(EM != new_event) + admin_log_and_message_admins("has changed the weight of the [GLOB.severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") + else if(href_list["toggle_oneshot"]) + var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) + EM.one_shot = !EM.one_shot + if(EM != new_event) + admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["toggle_enabled"]) + var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) + EM.enabled = !EM.enabled + admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["remove"]) + if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes") + return + var/datum/event_meta/EM = locate(href_list["remove"]) + var/datum/event_container/EC = locate(href_list["EC"]) + EC.available_events -= EM + admin_log_and_message_admins("has removed the [GLOB.severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["add"]) + if(!new_event.name || !new_event.event_type) + return + if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes") + return + new_event.severity = selected_event_container.severity + selected_event_container.available_events += new_event + admin_log_and_message_admins("has added \a [GLOB.severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") + new_event = new + else if(href_list["clear"]) + var/datum/event_container/EC = locate(href_list["clear"]) + if(EC.next_event) + admin_log_and_message_admins("has dequeued the [GLOB.severity_to_string[EC.severity]] event '[EC.next_event.name]'.") + EC.next_event = null + + Interact(usr) diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 4c397636cab4..4bc3965e6cfa 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -432,4 +432,4 @@ SUBSYSTEM_DEF(garbage) CHECK_TICK #endif -#endif \ No newline at end of file +#endif diff --git a/code/controllers/subsystem/holiday.dm b/code/controllers/subsystem/holiday.dm index be9d6d4fc7d4..ddde9cd62be4 100644 --- a/code/controllers/subsystem/holiday.dm +++ b/code/controllers/subsystem/holiday.dm @@ -1,31 +1,31 @@ -SUBSYSTEM_DEF(holiday) - name = "Holiday" - init_order = INIT_ORDER_HOLIDAY // 3 - flags = SS_NO_FIRE - var/list/holidays - -/datum/controller/subsystem/holiday/Initialize(start_timeofday) - if(!config.allow_holidays) - return ..() //Holiday stuff was not enabled in the config! - - var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - - for(var/H in subtypesof(/datum/holiday)) - var/datum/holiday/holiday = new H() - if(holiday.shouldCelebrate(DD, MM, YY)) - holiday.celebrate() - if(!holidays) - holidays = list() - holidays[holiday.name] = holiday - - if(holidays) - holidays = shuffle(holidays) - world.update_status() - for(var/datum/holiday/H in holidays) - if(H.eventChance) - if(prob(H.eventChance)) - H.handle_event() - - return ..() \ No newline at end of file +SUBSYSTEM_DEF(holiday) + name = "Holiday" + init_order = INIT_ORDER_HOLIDAY // 3 + flags = SS_NO_FIRE + var/list/holidays + +/datum/controller/subsystem/holiday/Initialize(start_timeofday) + if(!config.allow_holidays) + return ..() //Holiday stuff was not enabled in the config! + + var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + + for(var/H in subtypesof(/datum/holiday)) + var/datum/holiday/holiday = new H() + if(holiday.shouldCelebrate(DD, MM, YY)) + holiday.celebrate() + if(!holidays) + holidays = list() + holidays[holiday.name] = holiday + + if(holidays) + holidays = shuffle(holidays) + world.update_status() + for(var/datum/holiday/H in holidays) + if(H.eventChance) + if(prob(H.eventChance)) + H.handle_event() + + return ..() diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm index 3757237243f3..42df941e10fb 100644 --- a/code/controllers/subsystem/idlenpcpool.dm +++ b/code/controllers/subsystem/idlenpcpool.dm @@ -39,4 +39,4 @@ SUBSYSTEM_DEF(idlenpcpool) if(SA.stat != DEAD) SA.consider_wakeup() if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index 29efa85b8a07..b465c65c4417 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -49,7 +49,7 @@ SUBSYSTEM_DEF(jobs) /datum/controller/subsystem/jobs/proc/Debug(var/text) - if(!Debug2) + if(!GLOB.debug2) return 0 job_debug.Add(text) return 1 @@ -155,10 +155,10 @@ SUBSYSTEM_DEF(jobs) if(istype(job, GetJob("Civilian"))) // We don't want to give him assistant, that's boring! continue - if(job.title in command_positions) //If you want a command position, select it! + if(job.title in GLOB.command_positions) //If you want a command position, select it! continue - if(job.title in whitelisted_positions) // No random whitelisted job, sorry! + if(job.title in GLOB.whitelisted_positions) // No random whitelisted job, sorry! continue if(job.admin_only) // No admin positions either. @@ -203,7 +203,7 @@ SUBSYSTEM_DEF(jobs) ///This proc is called before the level loop of DivideOccupations() and will try to select a head, ignoring ALL non-head preferences for every level until it locates a head or runs out of levels to check /datum/controller/subsystem/jobs/proc/FillHeadPosition() for(var/level = 1 to 3) - for(var/command_position in command_positions) + for(var/command_position in GLOB.command_positions) var/datum/job/job = GetJob(command_position) if(!job) continue @@ -231,7 +231,7 @@ SUBSYSTEM_DEF(jobs) ///This proc is called at the start of the level loop of DivideOccupations() and will cause head jobs to be checked before any other jobs of the same level /datum/controller/subsystem/jobs/proc/CheckHeadPositions(var/level) - for(var/command_position in command_positions) + for(var/command_position in GLOB.command_positions) var/datum/job/job = GetJob(command_position) if(!job) continue @@ -600,7 +600,7 @@ SUBSYSTEM_DEF(jobs) // If they're head, give them the account info for their department if(job && job.head_position) remembered_info = "" - var/datum/money_account/department_account = department_accounts[job.department] + var/datum/money_account/department_account = GLOB.department_accounts[job.department] if(department_account) remembered_info += "Your department's account number is: #[department_account.account_number]
    " @@ -638,7 +638,7 @@ SUBSYSTEM_DEF(jobs) var/datum/job/oldjobdatum = SSjobs.GetJob(oldtitle) var/datum/job/newjobdatum = SSjobs.GetJob(newtitle) if(istype(oldjobdatum) && oldjobdatum.current_positions > 0 && istype(newjobdatum)) - if(!(oldjobdatum.title in command_positions) && !(newjobdatum.title in command_positions)) + if(!(oldjobdatum.title in GLOB.command_positions) && !(newjobdatum.title in GLOB.command_positions)) oldjobdatum.current_positions-- newjobdatum.current_positions++ diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 82bbd52471a8..7441d855f229 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -84,4 +84,4 @@ SUBSYSTEM_DEF(lighting) /datum/controller/subsystem/lighting/Recover() initialized = SSlighting.initialized - ..() \ No newline at end of file + ..() diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 75fd75193571..17174b02468e 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -13,24 +13,24 @@ SUBSYSTEM_DEF(mapping) if(!config.disable_space_ruins) var/timer = start_watch() log_startup_progress("Creating random space levels...") - seedRuins(list(level_name_to_num(EMPTY_AREA)), rand(0, 3), /area/space, space_ruins_templates) + seedRuins(list(level_name_to_num(EMPTY_AREA)), rand(0, 3), /area/space, GLOB.space_ruins_templates) log_startup_progress("Loaded random space levels in [stop_watch(timer)]s.") // load in extra levels of space ruins var/num_extra_space = rand(config.extra_space_ruin_levels_min, config.extra_space_ruin_levels_max) for(var/i = 1, i <= num_extra_space, i++) - var/zlev = space_manager.add_new_zlevel("[EMPTY_AREA] #[i]", linkage = CROSSLINKED, traits = list(REACHABLE)) - seedRuins(list(zlev), rand(0, 3), /area/space, space_ruins_templates) + var/zlev = GLOB.space_manager.add_new_zlevel("[EMPTY_AREA] #[i]", linkage = CROSSLINKED, traits = list(REACHABLE)) + seedRuins(list(zlev), rand(0, 3), /area/space, GLOB.space_ruins_templates) // Setup the Z-level linkage - space_manager.do_transition_setup() + GLOB.space_manager.do_transition_setup() // Spawn Lavaland ruins and rivers. - seedRuins(list(level_name_to_num(MINING)), config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates) + seedRuins(list(level_name_to_num(MINING)), config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, GLOB.lava_ruins_templates) spawn_rivers(list(level_name_to_num(MINING))) return ..() /datum/controller/subsystem/mapping/Recover() - flags |= SS_NO_INIT \ No newline at end of file + flags |= SS_NO_INIT diff --git a/code/controllers/subsystem/medals.dm b/code/controllers/subsystem/medals.dm index f3c2752ddf2c..5de6cdeb27a8 100644 --- a/code/controllers/subsystem/medals.dm +++ b/code/controllers/subsystem/medals.dm @@ -83,4 +83,4 @@ SUBSYSTEM_DEF(medals) /datum/controller/subsystem/medals/proc/ClearScore(client/player) if(isnull(world.SetScores(player.ckey, "", config.medal_hub_address, config.medal_hub_password))) log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.ckey].") - message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") \ No newline at end of file + message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm index a8a1fb72ceb2..c21b8f0760a9 100644 --- a/code/controllers/subsystem/mobs.dm +++ b/code/controllers/subsystem/mobs.dm @@ -33,4 +33,4 @@ SUBSYSTEM_DEF(mobs) else GLOB.mob_list.Remove(M) if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/nightshift.dm b/code/controllers/subsystem/nightshift.dm index 4ebe1dceb0e7..e13f49fd1c25 100644 --- a/code/controllers/subsystem/nightshift.dm +++ b/code/controllers/subsystem/nightshift.dm @@ -25,12 +25,12 @@ SUBSYSTEM_DEF(nightshift) check_nightshift() /datum/controller/subsystem/nightshift/proc/announce(message) - priority_announcement.Announce(message, new_sound = 'sound/misc/notice2.ogg', new_title = "Automated Lighting System Announcement") + GLOB.priority_announcement.Announce(message, new_sound = 'sound/misc/notice2.ogg', new_title = "Automated Lighting System Announcement") /datum/controller/subsystem/nightshift/proc/check_nightshift(check_canfire=FALSE) if(check_canfire && !can_fire) return - var/emergency = security_level >= SEC_LEVEL_RED + var/emergency = GLOB.security_level >= SEC_LEVEL_RED var/announcing = TRUE var/time = station_time() var/night_time = (time < nightshift_end_time) || (time > nightshift_start_time) @@ -41,7 +41,7 @@ SUBSYSTEM_DEF(nightshift) if(!emergency) announce("Restoring night lighting configuration to normal operation.") else - announce("Disabling night lighting: Station is in a state of emergency.") + announce("Disabling night lighting: Station is in a state of emergency.") if(emergency) night_time = FALSE if(nightshift_active != night_time) diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm index f2e159404fdd..5eaca5d19c4e 100644 --- a/code/controllers/subsystem/npcpool.dm +++ b/code/controllers/subsystem/npcpool.dm @@ -34,4 +34,4 @@ SUBSYSTEM_DEF(npcpool) if(SA.stat != DEAD) SA.handle_automated_speech() if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm index 732c5a3ba556..9622e0214692 100644 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ b/code/controllers/subsystem/processing/fastprocess.dm @@ -3,4 +3,4 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess) name = "Fast Processing" wait = 2 - stat_tag = "FP" \ No newline at end of file + stat_tag = "FP" diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm index 7ee2bb1f0f3a..26021fb267a1 100644 --- a/code/controllers/subsystem/processing/obj.dm +++ b/code/controllers/subsystem/processing/obj.dm @@ -2,4 +2,4 @@ PROCESSING_SUBSYSTEM_DEF(obj) name = "Objects" priority = FIRE_PRIORITY_OBJ flags = SS_NO_INIT - wait = 20 \ No newline at end of file + wait = 20 diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm index 2e06d7ebcc62..a8bc823bbbeb 100644 --- a/code/controllers/subsystem/processing/processing.dm +++ b/code/controllers/subsystem/processing/processing.dm @@ -34,4 +34,4 @@ SUBSYSTEM_DEF(processing) /datum/proc/process() set waitfor = 0 - return PROCESS_KILL \ No newline at end of file + return PROCESS_KILL diff --git a/code/controllers/subsystem/radio.dm b/code/controllers/subsystem/radio.dm index 93b5152ee455..49b300627861 100644 --- a/code/controllers/subsystem/radio.dm +++ b/code/controllers/subsystem/radio.dm @@ -1,99 +1,99 @@ -SUBSYSTEM_DEF(radio) - name = "Radio" - flags = SS_NO_INIT | SS_NO_FIRE - - var/list/radiochannels = list( - "Common" = PUB_FREQ, - "Science" = SCI_FREQ, - "Command" = COMM_FREQ, - "Medical" = MED_FREQ, - "Engineering" = ENG_FREQ, - "Security" = SEC_FREQ, - "Response Team" = ERT_FREQ, - "Special Ops" = DTH_FREQ, - "Syndicate" = SYND_FREQ, - "SyndTeam" = SYNDTEAM_FREQ, - "Supply" = SUP_FREQ, - "Service" = SRV_FREQ, - "AI Private" = AI_FREQ, - "Medical(I)" = MED_I_FREQ, - "Security(I)" = SEC_I_FREQ - ) - var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) - var/list/ANTAG_FREQS = list(SYND_FREQ, SYNDTEAM_FREQ) - var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) - var/list/datum/radio_frequency/frequencies = list() - -// This is fucking disgusting and needs to die -/datum/controller/subsystem/radio/proc/frequency_span_class(var/frequency) - // Antags! - if(frequency in ANTAG_FREQS) - return "syndradio" - // centcomm channels (deathsquid and ert) - if(frequency in CENT_FREQS) - return "centradio" - // This switch used to be a shit tonne of if statements. I am gonna find who made this and give them a kind talking to - switch(frequency) - if(COMM_FREQ) - return "comradio" - if(AI_FREQ) - return "airadio" - if(SEC_FREQ) - return "secradio" - if(ENG_FREQ) - return "engradio" - if(SCI_FREQ) - return "sciradio" - if(MED_FREQ) - return "medradio" - if(SUP_FREQ) - return "supradio" - if(SRV_FREQ) - return "srvradio" - - // If the above switch somehow failed. And it needs the SSradio. part otherwise it fails to compile - if(frequency in DEPT_FREQS) - return "deptradio" - - // If its none of the others - return "radio" - - -/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - - frequency.add_listener(device, filter) - return frequency - -/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) - var/f_text = num2text(old_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(frequency) - frequency.remove_listener(device) - - if(frequency.devices.len == 0) - qdel(frequency) - frequencies -= f_text - - return 1 - -/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - - return frequency - - -// ALL THE SHIT BELOW THIS LINE ISNT PART OF THE SUBSYSTEM AND REALLY NEEDS ITS OWN FILE +SUBSYSTEM_DEF(radio) + name = "Radio" + flags = SS_NO_INIT | SS_NO_FIRE + + var/list/radiochannels = list( + "Common" = PUB_FREQ, + "Science" = SCI_FREQ, + "Command" = COMM_FREQ, + "Medical" = MED_FREQ, + "Engineering" = ENG_FREQ, + "Security" = SEC_FREQ, + "Response Team" = ERT_FREQ, + "Special Ops" = DTH_FREQ, + "Syndicate" = SYND_FREQ, + "SyndTeam" = SYNDTEAM_FREQ, + "Supply" = SUP_FREQ, + "Service" = SRV_FREQ, + "AI Private" = AI_FREQ, + "Medical(I)" = MED_I_FREQ, + "Security(I)" = SEC_I_FREQ + ) + var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) + var/list/ANTAG_FREQS = list(SYND_FREQ, SYNDTEAM_FREQ) + var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) + var/list/datum/radio_frequency/frequencies = list() + +// This is fucking disgusting and needs to die +/datum/controller/subsystem/radio/proc/frequency_span_class(var/frequency) + // Antags! + if(frequency in ANTAG_FREQS) + return "syndradio" + // centcomm channels (deathsquid and ert) + if(frequency in CENT_FREQS) + return "centradio" + // This switch used to be a shit tonne of if statements. I am gonna find who made this and give them a kind talking to + switch(frequency) + if(COMM_FREQ) + return "comradio" + if(AI_FREQ) + return "airadio" + if(SEC_FREQ) + return "secradio" + if(ENG_FREQ) + return "engradio" + if(SCI_FREQ) + return "sciradio" + if(MED_FREQ) + return "medradio" + if(SUP_FREQ) + return "supradio" + if(SRV_FREQ) + return "srvradio" + + // If the above switch somehow failed. And it needs the SSradio. part otherwise it fails to compile + if(frequency in DEPT_FREQS) + return "deptradio" + + // If its none of the others + return "radio" + + +/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) + var/f_text = num2text(new_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency + + frequency.add_listener(device, filter) + return frequency + +/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) + var/f_text = num2text(old_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(frequency) + frequency.remove_listener(device) + + if(frequency.devices.len == 0) + qdel(frequency) + frequencies -= f_text + + return 1 + +/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num) + var/f_text = num2text(new_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency + + return frequency + + +// ALL THE SHIT BELOW THIS LINE ISNT PART OF THE SUBSYSTEM AND REALLY NEEDS ITS OWN FILE diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index a1ca9b5fdadb..da3d91c1de15 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -311,4 +311,4 @@ SUBSYSTEM_DEF(shuttle) QDEL_LIST(remove_images) -#undef CALL_SHUTTLE_REASON_LENGTH \ No newline at end of file +#undef CALL_SHUTTLE_REASON_LENGTH diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm index 8e5e47e7abf2..6cfb52f2d25a 100644 --- a/code/controllers/subsystem/sun.dm +++ b/code/controllers/subsystem/sun.dm @@ -39,4 +39,4 @@ SUBSYSTEM_DEF(sun) if(!SC.powernet) solars.Remove(SC) continue - SC.update() \ No newline at end of file + SC.update() diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index f9528f2b2b9b..bbc73cfed14f 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -1,533 +1,533 @@ -SUBSYSTEM_DEF(ticker) - name = "Ticker" - init_order = INIT_ORDER_TICKER - - priority = FIRE_PRIORITY_TICKER - flags = SS_KEEP_TIMING - runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME - - var/round_start_time = 0 - var/const/restart_timeout = 600 - var/current_state = GAME_STATE_STARTUP - var/force_start = 0 // Do we want to force-start as soon as we can - var/force_ending = 0 - var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o - var/datum/game_mode/mode = null - var/event_time = null - var/event = 0 - var/login_music // music played in pregame lobby - var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. - var/Bible_icon_state // icon_state the chaplain has chosen for his bible - var/Bible_item_state // item_state the chaplain has chosen for his bible - var/Bible_name // name of the bible - var/Bible_deity_name - var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes - var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders - var/list/syndicate_coalition = list() // list of traitor-compatible factions - var/list/factions = list() // list of all factions - var/list/availablefactions = list() // list of factions with openings - var/tipped = FALSE //Did we broadcast the tip of the day yet? - var/selected_tip // What will be the tip of the day? - var/pregame_timeleft // This is used for calculations - var/delay_end = 0 //if set to nonzero, the round will not restart on it's own - var/triai = 0//Global holder for Triumvirate - var/initialtpass = 0 //holder for inital autotransfer vote timer - var/obj/screen/cinematic = null //used for station explosion cinematic - var/round_end_announced = 0 // Spam Prevention. Announce round end only once.\ - -/datum/controller/subsystem/ticker/Initialize() - login_music = pick(\ - 'sound/music/thunderdome.ogg',\ - 'sound/music/space.ogg',\ - 'sound/music/title1.ogg',\ - 'sound/music/title2.ogg',\ - 'sound/music/title3.ogg',) - - // Map name - if(using_map && using_map.name) - GLOB.map_name = "[using_map.name]" - else - GLOB.map_name = "Unknown" - - // World name - if(config && config.server_name) - world.name = "[config.server_name]: [station_name()]" - else - world.name = station_name() - - return ..() - - -/datum/controller/subsystem/ticker/fire() - switch(current_state) - if(GAME_STATE_STARTUP) - // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails - round_start_time = world.time + (config.pregame_timestart * 10) - to_chat(world, "Welcome to the pre-game lobby!") - to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") - current_state = GAME_STATE_PREGAME - fire() // TG says this is a good idea - if(GAME_STATE_PREGAME) - if(!going) - return - - // This is so we dont have sleeps in controllers, because that is a bad, bad thing - if(!delay_end) - pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed - else - pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off - - if(pregame_timeleft <= 600 && !tipped) // 60 seconds - send_tip_of_the_round() - tipped = TRUE - - if(pregame_timeleft <= 0 || force_start) - current_state = GAME_STATE_SETTING_UP - Master.SetRunLevel(RUNLEVEL_SETUP) - if(GAME_STATE_SETTING_UP) - if(!setup()) // Setup failed - current_state = GAME_STATE_STARTUP - Master.SetRunLevel(RUNLEVEL_LOBBY) - if(GAME_STATE_PLAYING) - delay_end = 0 // reset this in case round start was delayed - mode.process() - mode.process_job_tasks() - var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked - if(config.continuous_rounds) - mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result - else - game_finished |= mode.check_finished() - if(game_finished) - current_state = GAME_STATE_FINISHED - if(GAME_STATE_FINISHED) - current_state = GAME_STATE_FINISHED - Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know - auto_toggle_ooc(1) // Turn it on - - spawn(0) - declare_completion() - - spawn(50) - if(mode.station_was_nuked) - world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") - else - world.Reboot("Round ended.", "end_proper", "proper completion") - - -/datum/controller/subsystem/ticker/proc/votetimer() - var/timerbuffer = 0 - if(initialtpass == 0) - timerbuffer = config.vote_autotransfer_initial - else - timerbuffer = config.vote_autotransfer_interval - spawn(timerbuffer) - SSvote.autotransfer() - initialtpass = 1 - votetimer() - - -/datum/controller/subsystem/ticker/proc/setup() - cultdat = setupcult() - //Create and announce mode - if(master_mode=="secret") - src.hide_mode = 1 - var/list/datum/game_mode/runnable_modes - if((master_mode=="random") || (master_mode=="secret")) - runnable_modes = config.get_runnable_modes() - if(runnable_modes.len==0) - current_state = GAME_STATE_PREGAME - Master.SetRunLevel(RUNLEVEL_LOBBY) - to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") - return 0 - if(secret_force_mode != "secret") - var/datum/game_mode/M = config.pick_mode(secret_force_mode) - if(M.can_start()) - src.mode = config.pick_mode(secret_force_mode) - SSjobs.ResetOccupations() - if(!src.mode) - src.mode = pickweight(runnable_modes) - if(src.mode) - var/mtype = src.mode.type - src.mode = new mtype - else - src.mode = config.pick_mode(master_mode) - if(!src.mode.can_start()) - to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") - mode = null - current_state = GAME_STATE_PREGAME - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - //Configure mode and assign player to special mode stuff - src.mode.pre_pre_setup() - var/can_continue - can_continue = src.mode.pre_setup()//Setup special modes - SSjobs.DivideOccupations() //Distribute jobs - if(!can_continue) - qdel(mode) - current_state = GAME_STATE_PREGAME - to_chat(world, "Error setting up [master_mode]. Reverting to pre-game lobby.") - SSjobs.ResetOccupations() - Master.SetRunLevel(RUNLEVEL_LOBBY) - return 0 - - if(hide_mode) - var/list/modes = new - for(var/datum/game_mode/M in runnable_modes) - modes+=M.name - modes = sortList(modes) - to_chat(world, "The current game mode is - Secret!") - to_chat(world, "Possibilities: [english_list(modes)]") - else - src.mode.announce() - - create_characters() //Create player characters and transfer them - populate_spawn_points() - collect_minds() - equip_characters() - data_core.manifest() - current_state = GAME_STATE_PLAYING - Master.SetRunLevel(RUNLEVEL_GAME) - - callHook("roundstart") - - //here to initialize the random events nicely at round start - setup_economy() - setupfactions() - - //shuttle_controller.setup_shuttle_docks() - - spawn(0)//Forking here so we dont have to wait for this to finish - if(!GLOB.syndicate_code_phrase) - GLOB.syndicate_code_phrase = generate_code_phrase() - if(!GLOB.syndicate_code_response) - GLOB.syndicate_code_response = generate_code_phrase() - mode.post_setup() - //Cleanup some stuff - for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) - //Deleting Startpoints but we need the ai point to AI-ize people later - if(S.name != "AI") - qdel(S) - - // take care of random spesspod spawning - var/list/obj/effect/landmark/spacepod/random/L = list() - for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) - if(istype(SS)) - L += SS - if(L.len) - var/obj/effect/landmark/spacepod/random/S = pick(L) - new /obj/spacepod/random(S.loc) - for(var/obj/effect/landmark/spacepod/random/R in L) - qdel(R) - - to_chat(world, "Enjoy the game!") - world << sound('sound/AI/welcome.ogg')// Skie - - if(SSholiday.holidays) - to_chat(world, "and...") - for(var/holidayname in SSholiday.holidays) - var/datum/holiday/holiday = SSholiday.holidays[holidayname] - to_chat(world, "

    [holiday.greet()]

    ") - - spawn(0) // Forking dynamic room selection - var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) - var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) - - for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) - var/area/dynamic/destination/current_destination = locate(current_destination_candidate) - - if(!current_destination) - continue - - if(current_destination.match_width == 0 || current_destination.match_height == 0) - message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") - continue - - var/list/area/dynamic/source/candidate_source_areas = new /list(0) - for(var/area/dynamic/source/candidate_source_area in available_source_candidates) - var/area/dynamic/source/candidate_source = locate(candidate_source_area) - - if(!candidate_source) - continue - - if(candidate_source.match_tag != current_destination.match_tag) - continue - - if(candidate_source.match_width != current_destination.match_width || \ - candidate_source.match_height != current_destination.match_height) - continue - - candidate_source_areas += candidate_source - - if(candidate_source_areas.len == 0) - message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") - continue - - var/area/dynamic/source/selected_source = pick(candidate_source_areas) - available_source_candidates -= selected_source - - selected_source.copy_contents_to(current_destination, 0) - - if(current_destination.enable_lights || selected_source.enable_lights) - current_destination.power_light = 1 - else - current_destination.power_light = 0 - current_destination.power_change() - - //start_events() //handles random events and space dust. - //new random event system is handled from the MC. - - var/list/admins_number = staff_countup(R_BAN) - if(admins_number[1] == 0 && admins_number[3] == 0) - send2irc(config.admin_notify_irc, "Round has started with no admins online.") - auto_toggle_ooc(0) // Turn it off - round_start_time = world.time - - if(config.sql_enabled) - spawn(3000) - statistic_cycle() // Polls population totals regularly and stores them in an SQL DB - - votetimer() - - for(var/mob/new_player/N in GLOB.mob_list) - if(N.client) - N.new_player_panel_proc() - - return 1 - -/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) - if(cinematic) - return //already a cinematic in progress! - - auto_toggle_ooc(1) // Turn it on - //initialise our cinematic screen object - cinematic = new /obj/screen(src) - cinematic.icon = 'icons/effects/station_explosion.dmi' - cinematic.icon_state = "station_intact" - cinematic.layer = 21 - cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT - cinematic.screen_loc = "1,0" - - var/obj/structure/bed/temp_buckle = new(src) - if(station_missed) - for(var/mob/M in GLOB.mob_list) - M.buckled = temp_buckle //buckles the mob so it can't do anything - if(M.client) - M.client.screen += cinematic //show every client the cinematic - else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" - for(var/mob/M in GLOB.mob_list) - M.buckled = temp_buckle - if(M.stat != DEAD) - var/turf/T = get_turf(M) - if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) - var/mob/ghost = M.ghostize() - M.dust() //no mercy - if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. - ghost.client.screen += cinematic - CHECK_TICK - if(M && M.client) //Play the survivors a cinematic. - M.client.screen += cinematic - - //Now animate the cinematic - switch(station_missed) - if(1) //nuke was nearby but (mostly) missed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke wasn't on station when it blew up - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - flick("station_intact_fade_red", cinematic) - cinematic.icon_state = "summary_nukefail" - if("fake") //The round isn't over, we're just freaking people out for fun - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/items/bikehorn.ogg') - flick("summary_selfdes", cinematic) - else - flick("intro_nuke", cinematic) - sleep(35) - world << sound('sound/effects/explosionfar.ogg') - - - if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation - sleep(50) - world << sound('sound/effects/explosionfar.ogg') - else //station was destroyed - if(mode && !override) - override = mode.name - switch(override) - if("nuclear emergency") //Nuke Ops successfully bombed the station - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_nukewin" - if("AI malfunction") //Malf (screen,explosion,summary) - flick("intro_malf", cinematic) - sleep(76) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_malf" - if("blob") //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - else //Station nuked (nuke,explosion,summary) - flick("intro_nuke", cinematic) - sleep(35) - flick("station_explode_fade_red", cinematic) - world << sound('sound/effects/explosionfar.ogg') - cinematic.icon_state = "summary_selfdes" - //If its actually the end of the round, wait for it to end. - //Otherwise if its a verb it will continue on afterwards. - spawn(300) - QDEL_NULL(cinematic) //end the cinematic - if(temp_buckle) - qdel(temp_buckle) //release everybody - - - -/datum/controller/subsystem/ticker/proc/create_characters() - for(var/mob/new_player/player in GLOB.player_list) - if(player.ready && player.mind) - if(player.mind.assigned_role == "AI") - player.close_spawn_windows() - var/mob/living/silicon/ai/ai_character = player.AIize() - ai_character.moveToAILandmark() - else if(!player.mind.assigned_role) - continue - else - player.create_character() - qdel(player) - - -/datum/controller/subsystem/ticker/proc/collect_minds() - for(var/mob/living/player in GLOB.player_list) - if(player.mind) - SSticker.minds += player.mind - - -/datum/controller/subsystem/ticker/proc/equip_characters() - var/captainless=1 - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(player && player.mind && player.mind.assigned_role) - if(player.mind.assigned_role == "Captain") - captainless=0 - if(player.mind.assigned_role != player.mind.special_role) - SSjobs.AssignRank(player, player.mind.assigned_role, 0) - SSjobs.EquipRank(player, player.mind.assigned_role, 0) - EquipCustomItems(player) - if(captainless) - for(var/mob/M in GLOB.player_list) - if(!istype(M,/mob/new_player)) - to_chat(M, "Captainship not forced on anyone.") - -/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() - var/m - if(selected_tip) - m = selected_tip - else - var/list/randomtips = file2list("strings/tips.txt") - var/list/memetips = file2list("strings/sillytips.txt") - if(randomtips.len && prob(95)) - m = pick(randomtips) - else if(memetips.len) - m = pick(memetips) - - if(m) - to_chat(world, "Tip of the round: [html_encode(m)]") - -/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) - for(var/datum/faction/F in factions) - if(F.name == name) - return F - - -/datum/controller/subsystem/ticker/proc/declare_completion() - nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. - //Round statistics report - var/datum/station_state/end_state = new /datum/station_state() - end_state.count() - var/station_integrity = min(round( 100.0 * start_state.score(end_state), 0.1), 100.0) - - to_chat(world, "
    [TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") - to_chat(world, "
    [TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") - to_chat(world, "
    ") - - //Silicon laws report - for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) - if(aiPlayer.stat != 2) - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") - else - to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") - aiPlayer.show_laws(1) - - if(aiPlayer.connected_robots.len) - var/robolist = "The AI's loyal minions were: " - for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) - robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" - to_chat(world, "[robolist]") - - var/dronecount = 0 - - for(var/mob/living/silicon/robot/robo in GLOB.mob_list) - - if(istype(robo,/mob/living/silicon/robot/drone)) - dronecount++ - continue - - if(!robo.connected_ai) - if(robo.stat != 2) - to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") - else - to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") - - if(robo) //How the hell do we lose robo between here and the world messages directly above this? - robo.laws.show_laws(world) - - if(dronecount) - to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") - - if(mode.eventmiscs.len) - var/emobtext = "" - for(var/datum/mind/eventmind in mode.eventmiscs) - emobtext += printeventplayer(eventmind) - emobtext += "
    " - emobtext += printobjectives(eventmind) - emobtext += "
    " - emobtext += "
    " - to_chat(world, emobtext) - - mode.declare_completion()//To declare normal completion. - - //calls auto_declare_completion_* for all modes - for(var/handler in typesof(/datum/game_mode/proc)) - if(findtext("[handler]","auto_declare_completion_")) - call(mode, handler)() - - scoreboard() - - // Declare the completion of the station goals - mode.declare_station_goal_completion() - - //Ask the event manager to print round end information - SSevents.RoundEnd() - - // Add AntagHUD to everyone, see who was really evil the whole time! - for(var/datum/atom_hud/antag/H in huds) - for(var/m in GLOB.player_list) - var/mob/M = m - H.add_hud_to(M) - - return 1 - -/datum/controller/subsystem/ticker/proc/HasRoundStarted() - return current_state >= GAME_STATE_PLAYING - -/datum/controller/subsystem/ticker/proc/IsRoundInProgress() - return current_state == GAME_STATE_PLAYING \ No newline at end of file +SUBSYSTEM_DEF(ticker) + name = "Ticker" + init_order = INIT_ORDER_TICKER + + priority = FIRE_PRIORITY_TICKER + flags = SS_KEEP_TIMING + runlevels = RUNLEVEL_LOBBY | RUNLEVEL_SETUP | RUNLEVEL_GAME + + var/round_start_time = 0 + var/const/restart_timeout = 600 + var/current_state = GAME_STATE_STARTUP + var/force_start = 0 // Do we want to force-start as soon as we can + var/force_ending = 0 + var/hide_mode = 0 // leave here at 0 ! setup() will take care of it when needed for Secret mode -walter0o + var/datum/game_mode/mode = null + var/event_time = null + var/event = 0 + var/login_music // music played in pregame lobby + var/list/datum/mind/minds = list()//The people in the game. Used for objective tracking. + var/Bible_icon_state // icon_state the chaplain has chosen for his bible + var/Bible_item_state // item_state the chaplain has chosen for his bible + var/Bible_name // name of the bible + var/Bible_deity_name + var/datum/cult_info/cultdat = null //here instead of cult for adminbus purposes + var/random_players = 0 // if set to nonzero, ALL players who latejoin or declare-ready join will have random appearances/genders + var/list/syndicate_coalition = list() // list of traitor-compatible factions + var/list/factions = list() // list of all factions + var/list/availablefactions = list() // list of factions with openings + var/tipped = FALSE //Did we broadcast the tip of the day yet? + var/selected_tip // What will be the tip of the day? + var/pregame_timeleft // This is used for calculations + var/delay_end = 0 //if set to nonzero, the round will not restart on it's own + var/triai = 0//Global holder for Triumvirate + var/initialtpass = 0 //holder for inital autotransfer vote timer + var/obj/screen/cinematic = null //used for station explosion cinematic + var/round_end_announced = 0 // Spam Prevention. Announce round end only once. + var/ticker_going = TRUE // This used to be in the unused globals, but it turns out its actually used in a load of places. Its now a ticker var because its related to round stuff, -aa + +/datum/controller/subsystem/ticker/Initialize() + login_music = pick(\ + 'sound/music/thunderdome.ogg',\ + 'sound/music/space.ogg',\ + 'sound/music/title1.ogg',\ + 'sound/music/title2.ogg',\ + 'sound/music/title3.ogg',) + // Map name + if(GLOB.using_map && GLOB.using_map.name) + GLOB.map_name = "[GLOB.using_map.name]" + else + GLOB.map_name = "Unknown" + + // World name + if(config && config.server_name) + world.name = "[config.server_name]: [station_name()]" + else + world.name = station_name() + + return ..() + + +/datum/controller/subsystem/ticker/fire() + switch(current_state) + if(GAME_STATE_STARTUP) + // This is ran as soon as the MC starts firing, and should only run ONCE, unless startup fails + round_start_time = world.time + (config.pregame_timestart * 10) + to_chat(world, "Welcome to the pre-game lobby!") + to_chat(world, "Please, setup your character and select ready. Game will start in [config.pregame_timestart] seconds") + current_state = GAME_STATE_PREGAME + fire() // TG says this is a good idea + if(GAME_STATE_PREGAME) + if(!SSticker.ticker_going) // This has to be referenced like this, and I dont know why. If you dont put SSticker. it will break + return + + // This is so we dont have sleeps in controllers, because that is a bad, bad thing + if(!delay_end) + pregame_timeleft = max(0,round_start_time - world.time) // Normal lobby countdown when roundstart was not delayed + else + pregame_timeleft = max(0,pregame_timeleft - 20) // If roundstart was delayed, we should resume the countdown where it left off + + if(pregame_timeleft <= 600 && !tipped) // 60 seconds + send_tip_of_the_round() + tipped = TRUE + + if(pregame_timeleft <= 0 || force_start) + current_state = GAME_STATE_SETTING_UP + Master.SetRunLevel(RUNLEVEL_SETUP) + if(GAME_STATE_SETTING_UP) + if(!setup()) // Setup failed + current_state = GAME_STATE_STARTUP + Master.SetRunLevel(RUNLEVEL_LOBBY) + if(GAME_STATE_PLAYING) + delay_end = 0 // reset this in case round start was delayed + mode.process() + mode.process_job_tasks() + var/game_finished = SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || mode.station_was_nuked + if(config.continuous_rounds) + mode.check_finished() // some modes contain var-changing code in here, so call even if we don't uses result + else + game_finished |= mode.check_finished() + if(game_finished) + current_state = GAME_STATE_FINISHED + if(GAME_STATE_FINISHED) + current_state = GAME_STATE_FINISHED + Master.SetRunLevel(RUNLEVEL_POSTGAME) // This shouldnt process more than once, but you never know + auto_toggle_ooc(1) // Turn it on + + spawn(0) + declare_completion() + + spawn(50) + if(mode.station_was_nuked) + world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke") + else + world.Reboot("Round ended.", "end_proper", "proper completion") + + +/datum/controller/subsystem/ticker/proc/votetimer() + var/timerbuffer = 0 + if(initialtpass == 0) + timerbuffer = config.vote_autotransfer_initial + else + timerbuffer = config.vote_autotransfer_interval + spawn(timerbuffer) + SSvote.autotransfer() + initialtpass = 1 + votetimer() + + +/datum/controller/subsystem/ticker/proc/setup() + cultdat = setupcult() + //Create and announce mode + if(GLOB.master_mode=="secret") + src.hide_mode = 1 + var/list/datum/game_mode/runnable_modes + if((GLOB.master_mode=="random") || (GLOB.master_mode=="secret")) + runnable_modes = config.get_runnable_modes() + if(runnable_modes.len==0) + current_state = GAME_STATE_PREGAME + Master.SetRunLevel(RUNLEVEL_LOBBY) + to_chat(world, "Unable to choose playable game mode. Reverting to pre-game lobby.") + return 0 + if(GLOB.secret_force_mode != "secret") + var/datum/game_mode/M = config.pick_mode(GLOB.secret_force_mode) + if(M.can_start()) + src.mode = config.pick_mode(GLOB.secret_force_mode) + SSjobs.ResetOccupations() + if(!src.mode) + src.mode = pickweight(runnable_modes) + if(src.mode) + var/mtype = src.mode.type + src.mode = new mtype + else + src.mode = config.pick_mode(GLOB.master_mode) + if(!src.mode.can_start()) + to_chat(world, "Unable to start [mode.name]. Not enough players, [mode.required_players] players needed. Reverting to pre-game lobby.") + mode = null + current_state = GAME_STATE_PREGAME + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + //Configure mode and assign player to special mode stuff + src.mode.pre_pre_setup() + var/can_continue + can_continue = src.mode.pre_setup()//Setup special modes + SSjobs.DivideOccupations() //Distribute jobs + if(!can_continue) + qdel(mode) + current_state = GAME_STATE_PREGAME + to_chat(world, "Error setting up [GLOB.master_mode]. Reverting to pre-game lobby.") + SSjobs.ResetOccupations() + Master.SetRunLevel(RUNLEVEL_LOBBY) + return 0 + + if(hide_mode) + var/list/modes = new + for(var/datum/game_mode/M in runnable_modes) + modes+=M.name + modes = sortList(modes) + to_chat(world, "The current game mode is - Secret!") + to_chat(world, "Possibilities: [english_list(modes)]") + else + src.mode.announce() + + create_characters() //Create player characters and transfer them + populate_spawn_points() + collect_minds() + equip_characters() + GLOB.data_core.manifest() + current_state = GAME_STATE_PLAYING + Master.SetRunLevel(RUNLEVEL_GAME) + + callHook("roundstart") + + //here to initialize the random events nicely at round start + setup_economy() + setupfactions() + + //shuttle_controller.setup_shuttle_docks() + + spawn(0)//Forking here so we dont have to wait for this to finish + if(!GLOB.syndicate_code_phrase) + GLOB.syndicate_code_phrase = generate_code_phrase() + if(!GLOB.syndicate_code_response) + GLOB.syndicate_code_response = generate_code_phrase() + mode.post_setup() + //Cleanup some stuff + for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) + //Deleting Startpoints but we need the ai point to AI-ize people later + if(S.name != "AI") + qdel(S) + + // take care of random spesspod spawning + var/list/obj/effect/landmark/spacepod/random/L = list() + for(var/obj/effect/landmark/spacepod/random/SS in GLOB.landmarks_list) + if(istype(SS)) + L += SS + if(L.len) + var/obj/effect/landmark/spacepod/random/S = pick(L) + new /obj/spacepod/random(S.loc) + for(var/obj/effect/landmark/spacepod/random/R in L) + qdel(R) + + to_chat(world, "Enjoy the game!") + world << sound('sound/AI/welcome.ogg')// Skie + + if(SSholiday.holidays) + to_chat(world, "and...") + for(var/holidayname in SSholiday.holidays) + var/datum/holiday/holiday = SSholiday.holidays[holidayname] + to_chat(world, "

    [holiday.greet()]

    ") + + spawn(0) // Forking dynamic room selection + var/list/area/dynamic/source/available_source_candidates = subtypesof(/area/dynamic/source) + var/list/area/dynamic/destination/available_destination_candidates = subtypesof(/area/dynamic/destination) + + for(var/area/dynamic/destination/current_destination_candidate in available_destination_candidates) + var/area/dynamic/destination/current_destination = locate(current_destination_candidate) + + if(!current_destination) + continue + + if(current_destination.match_width == 0 || current_destination.match_height == 0) + message_admins("Dynamic area destination '[current_destination.name]' does not have its size requirements set.") + continue + + var/list/area/dynamic/source/candidate_source_areas = new /list(0) + for(var/area/dynamic/source/candidate_source_area in available_source_candidates) + var/area/dynamic/source/candidate_source = locate(candidate_source_area) + + if(!candidate_source) + continue + + if(candidate_source.match_tag != current_destination.match_tag) + continue + + if(candidate_source.match_width != current_destination.match_width || \ + candidate_source.match_height != current_destination.match_height) + continue + + candidate_source_areas += candidate_source + + if(candidate_source_areas.len == 0) + message_admins("Failed to find a matching source for dynamic area: [current_destination.name]") + continue + + var/area/dynamic/source/selected_source = pick(candidate_source_areas) + available_source_candidates -= selected_source + + selected_source.copy_contents_to(current_destination, 0) + + if(current_destination.enable_lights || selected_source.enable_lights) + current_destination.power_light = 1 + else + current_destination.power_light = 0 + current_destination.power_change() + + //start_events() //handles random events and space dust. + //new random event system is handled from the MC. + + var/list/admins_number = staff_countup(R_BAN) + if(admins_number[1] == 0 && admins_number[3] == 0) + send2irc(config.admin_notify_irc, "Round has started with no admins online.") + auto_toggle_ooc(0) // Turn it off + round_start_time = world.time + + if(config.sql_enabled) + spawn(3000) + statistic_cycle() // Polls population totals regularly and stores them in an SQL DB + + votetimer() + + for(var/mob/new_player/N in GLOB.mob_list) + if(N.client) + N.new_player_panel_proc() + + return 1 + +/datum/controller/subsystem/ticker/proc/station_explosion_cinematic(station_missed = 0, override = null) + if(cinematic) + return //already a cinematic in progress! + + auto_toggle_ooc(1) // Turn it on + //initialise our cinematic screen object + cinematic = new /obj/screen(src) + cinematic.icon = 'icons/effects/station_explosion.dmi' + cinematic.icon_state = "station_intact" + cinematic.layer = 21 + cinematic.mouse_opacity = MOUSE_OPACITY_TRANSPARENT + cinematic.screen_loc = "1,0" + + var/obj/structure/bed/temp_buckle = new(src) + if(station_missed) + for(var/mob/M in GLOB.mob_list) + M.buckled = temp_buckle //buckles the mob so it can't do anything + if(M.client) + M.client.screen += cinematic //show every client the cinematic + else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived" + for(var/mob/M in GLOB.mob_list) + M.buckled = temp_buckle + if(M.stat != DEAD) + var/turf/T = get_turf(M) + if(T && is_station_level(T.z) && !istype(M.loc, /obj/structure/closet/secure_closet/freezer)) + var/mob/ghost = M.ghostize() + M.dust() //no mercy + if(ghost && ghost.client) //Play the victims an uninterrupted cinematic. + ghost.client.screen += cinematic + CHECK_TICK + if(M && M.client) //Play the survivors a cinematic. + M.client.screen += cinematic + + //Now animate the cinematic + switch(station_missed) + if(1) //nuke was nearby but (mostly) missed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke wasn't on station when it blew up + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + flick("station_intact_fade_red", cinematic) + cinematic.icon_state = "summary_nukefail" + if("fake") //The round isn't over, we're just freaking people out for fun + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/items/bikehorn.ogg') + flick("summary_selfdes", cinematic) + else + flick("intro_nuke", cinematic) + sleep(35) + world << sound('sound/effects/explosionfar.ogg') + + + if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation + sleep(50) + world << sound('sound/effects/explosionfar.ogg') + else //station was destroyed + if(mode && !override) + override = mode.name + switch(override) + if("nuclear emergency") //Nuke Ops successfully bombed the station + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_nukewin" + if("AI malfunction") //Malf (screen,explosion,summary) + flick("intro_malf", cinematic) + sleep(76) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_malf" + if("blob") //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + else //Station nuked (nuke,explosion,summary) + flick("intro_nuke", cinematic) + sleep(35) + flick("station_explode_fade_red", cinematic) + world << sound('sound/effects/explosionfar.ogg') + cinematic.icon_state = "summary_selfdes" + //If its actually the end of the round, wait for it to end. + //Otherwise if its a verb it will continue on afterwards. + spawn(300) + QDEL_NULL(cinematic) //end the cinematic + if(temp_buckle) + qdel(temp_buckle) //release everybody + + + +/datum/controller/subsystem/ticker/proc/create_characters() + for(var/mob/new_player/player in GLOB.player_list) + if(player.ready && player.mind) + if(player.mind.assigned_role == "AI") + player.close_spawn_windows() + var/mob/living/silicon/ai/ai_character = player.AIize() + ai_character.moveToAILandmark() + else if(!player.mind.assigned_role) + continue + else + player.create_character() + qdel(player) + + +/datum/controller/subsystem/ticker/proc/collect_minds() + for(var/mob/living/player in GLOB.player_list) + if(player.mind) + SSticker.minds += player.mind + + +/datum/controller/subsystem/ticker/proc/equip_characters() + var/captainless=1 + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(player && player.mind && player.mind.assigned_role) + if(player.mind.assigned_role == "Captain") + captainless=0 + if(player.mind.assigned_role != player.mind.special_role) + SSjobs.AssignRank(player, player.mind.assigned_role, 0) + SSjobs.EquipRank(player, player.mind.assigned_role, 0) + EquipCustomItems(player) + if(captainless) + for(var/mob/M in GLOB.player_list) + if(!istype(M,/mob/new_player)) + to_chat(M, "Captainship not forced on anyone.") + +/datum/controller/subsystem/ticker/proc/send_tip_of_the_round() + var/m + if(selected_tip) + m = selected_tip + else + var/list/randomtips = file2list("strings/tips.txt") + var/list/memetips = file2list("strings/sillytips.txt") + if(randomtips.len && prob(95)) + m = pick(randomtips) + else if(memetips.len) + m = pick(memetips) + + if(m) + to_chat(world, "Tip of the round: [html_encode(m)]") + +/datum/controller/subsystem/ticker/proc/getfactionbyname(var/name) + for(var/datum/faction/F in factions) + if(F.name == name) + return F + + +/datum/controller/subsystem/ticker/proc/declare_completion() + GLOB.nologevent = 1 //end of round murder and shenanigans are legal; there's no need to jam up attack logs past this point. + //Round statistics report + var/datum/station_state/end_state = new /datum/station_state() + end_state.count() + var/station_integrity = min(round( 100.0 * GLOB.start_state.score(end_state), 0.1), 100.0) + + to_chat(world, "
    [TAB]Shift Duration: [round(ROUND_TIME / 36000)]:[add_zero("[ROUND_TIME / 600 % 60]", 2)]:[ROUND_TIME / 100 % 6][ROUND_TIME / 100 % 10]") + to_chat(world, "
    [TAB]Station Integrity: [mode.station_was_nuked ? "Destroyed" : "[station_integrity]%"]") + to_chat(world, "
    ") + + //Silicon laws report + for(var/mob/living/silicon/ai/aiPlayer in GLOB.mob_list) + if(aiPlayer.stat != 2) + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws at the end of the game were:") + else + to_chat(world, "[aiPlayer.name] (Played by: [aiPlayer.key])'s laws when it was deactivated were:") + aiPlayer.show_laws(1) + + if(aiPlayer.connected_robots.len) + var/robolist = "The AI's loyal minions were: " + for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots) + robolist += "[robo.name][robo.stat?" (Deactivated) (Played by: [robo.key]), ":" (Played by: [robo.key]), "]" + to_chat(world, "[robolist]") + + var/dronecount = 0 + + for(var/mob/living/silicon/robot/robo in GLOB.mob_list) + + if(istype(robo,/mob/living/silicon/robot/drone)) + dronecount++ + continue + + if(!robo.connected_ai) + if(robo.stat != 2) + to_chat(world, "[robo.name] (Played by: [robo.key]) survived as an AI-less borg! Its laws were:") + else + to_chat(world, "[robo.name] (Played by: [robo.key]) was unable to survive the rigors of being a cyborg without an AI. Its laws were:") + + if(robo) //How the hell do we lose robo between here and the world messages directly above this? + robo.laws.show_laws(world) + + if(dronecount) + to_chat(world, "There [dronecount>1 ? "were" : "was"] [dronecount] industrious maintenance [dronecount>1 ? "drones" : "drone"] this round.") + + if(mode.eventmiscs.len) + var/emobtext = "" + for(var/datum/mind/eventmind in mode.eventmiscs) + emobtext += printeventplayer(eventmind) + emobtext += "
    " + emobtext += printobjectives(eventmind) + emobtext += "
    " + emobtext += "
    " + to_chat(world, emobtext) + + mode.declare_completion()//To declare normal completion. + + //calls auto_declare_completion_* for all modes + for(var/handler in typesof(/datum/game_mode/proc)) + if(findtext("[handler]","auto_declare_completion_")) + call(mode, handler)() + + scoreboard() + + // Declare the completion of the station goals + mode.declare_station_goal_completion() + + //Ask the event manager to print round end information + SSevents.RoundEnd() + + // Add AntagHUD to everyone, see who was really evil the whole time! + for(var/datum/atom_hud/antag/H in GLOB.huds) + for(var/m in GLOB.player_list) + var/mob/M = m + H.add_hud_to(M) + + return 1 + +/datum/controller/subsystem/ticker/proc/HasRoundStarted() + return current_state >= GAME_STATE_PLAYING + +/datum/controller/subsystem/ticker/proc/IsRoundInProgress() + return current_state == GAME_STATE_PLAYING diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 1b2938fc70f8..1caa3d7819a9 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -518,4 +518,4 @@ SUBSYSTEM_DEF(timer) #undef BUCKET_LEN #undef BUCKET_POS #undef TIMER_MAX -#undef TIMER_ID_MAX \ No newline at end of file +#undef TIMER_ID_MAX diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index eca632b7b95d..28456c9b5c54 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -1,401 +1,401 @@ -SUBSYSTEM_DEF(vote) - name = "Vote" - wait = 10 - flags = SS_KEEP_TIMING|SS_NO_INIT - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - - var/initiator = null - var/started_time = null - var/time_remaining = 0 - var/mode = null - var/question = null - var/list/choices = list() - var/list/voted = list() - var/list/voting = list() - var/list/current_votes = list() - var/list/round_voters = list() - var/auto_muted = 0 - -/datum/controller/subsystem/vote/fire() - if(mode) - // No more change mode votes after the game has started. - if(mode == "gamemode" && SSticker.current_state >= GAME_STATE_SETTING_UP) - to_chat(world, "Voting aborted due to game start.") - reset() - return - - // Calculate how much time is remaining by comparing current time, to time of vote start, - // plus vote duration - time_remaining = round((started_time + config.vote_period - world.time)/10) - - if(time_remaining < 0) - result() - for(var/client/C in voting) - if(C) - C << browse(null,"window=vote") - reset() - else - for(var/client/C in voting) - update_panel(C) - CHECK_TICK - -/datum/controller/subsystem/vote/proc/autotransfer() - initiate_vote("crew_transfer","the server") - -/datum/controller/subsystem/vote/proc/reset() - initiator = null - time_remaining = 0 - mode = null - question = null - choices.Cut() - voted.Cut() - voting.Cut() - current_votes.Cut() - - if(auto_muted && !config.ooc_allowed && !(config.auto_toggle_ooc_during_round && SSticker.current_state == GAME_STATE_PLAYING)) - auto_muted = 0 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically enabled due to vote end.") - log_admin("OOC was toggled automatically due to vote end.") - message_admins("OOC has been toggled on automatically.") - - -/datum/controller/subsystem/vote/proc/get_result() - var/greatest_votes = 0 - var/total_votes = 0 - var/list/sorted_choices = list() - var/sorted_highest - var/sorted_votes = -1 - //get the highest number of votes, while also sorting the list - while(choices.len) - // This is a very inefficient sorting method, but that's okay - for(var/option in choices) - var/votes = choices[option] - if(sorted_votes < votes) - sorted_highest = option - sorted_votes = votes - if(votes > greatest_votes) - greatest_votes = votes - sorted_votes = -1 - total_votes += choices[sorted_highest] - sorted_choices[sorted_highest] = choices[sorted_highest] || 0 - choices -= sorted_highest - choices = sorted_choices - //default-vote for everyone who didn't vote - if(!config.vote_no_default && choices.len) - var/non_voters = (GLOB.clients.len - total_votes) - if(non_voters > 0) - if(mode == "restart") - choices["Continue Playing"] += non_voters - if(choices["Continue Playing"] >= greatest_votes) - greatest_votes = choices["Continue Playing"] - else if(mode == "gamemode") - if(master_mode in choices) - choices[master_mode] += non_voters - if(choices[master_mode] >= greatest_votes) - greatest_votes = choices[master_mode] - else if(mode == "crew_transfer") - var/factor = 0.5 - switch(world.time / (10 * 60)) // minutes - if(0 to 60) - factor = 0.5 - if(61 to 120) - factor = 0.8 - if(121 to 240) - factor = 1 - if(241 to 300) - factor = 1.2 - else - factor = 1.4 - choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) - to_chat(world, "Crew Transfer Factor: [factor]") - greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) - - - //get all options with that many votes and return them in a list - . = list() - if(greatest_votes) - for(var/option in choices) - if(choices[option] == greatest_votes) - . += option - return . - -/datum/controller/subsystem/vote/proc/announce_result() - var/list/winners = get_result() - var/text - if(winners.len > 0) - if(winners.len > 1) - if(mode != "gamemode" || SSticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes - text = "Vote Tied Between:\n" - for(var/option in winners) - text += "\t[option]\n" - . = pick(winners) - - for(var/key in current_votes) - if(choices[current_votes[key]] == .) - round_voters += key // Keep track of who voted for the winning round. - if(mode == "gamemode" && (. == "extended" || SSticker.hide_mode == 0)) // Announce Extended gamemode, but not other gamemodes - text += "Vote Result: [.] ([choices[.]] vote\s)" - else - if(mode == "custom") - // Completely replace text to show all results in custom votes - text = "[question]\n" - for(var/option in winners) - text += "\t[option]: [choices[option]] vote\s\n" - for(var/option in (choices-winners)) - text += "\t[option]: [choices[option]] vote\s\n" - else if(mode != "gamemode") - text += "Vote Result: [.] ([choices[.]] vote\s)" - else - text += "The vote has ended." // What will be shown if it is a gamemode vote that isn't extended - - else - text += "Vote Result: Inconclusive - No Votes!" - log_vote(text) - to_chat(world, "[text]") - return . - -/datum/controller/subsystem/vote/proc/result() - . = announce_result() - var/restart = 0 - if(.) - switch(mode) - if("restart") - if(. == "Restart Round") - restart = 1 - if("gamemode") - if(master_mode != .) - world.save_mode(.) - if(SSticker && SSticker.mode) - restart = 1 - else - master_mode = . - if(!going) - going = 1 - to_chat(world, "The round will start soon.") - if("crew_transfer") - if(. == "Initiate Crew Transfer") - init_shift_change(null, 1) - - - if(restart) - world.Reboot("Restart vote successful.", "end_error", "restart vote") - - return . - -/datum/controller/subsystem/vote/proc/submit_vote(var/ckey, var/vote) - if(mode) - if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) - return 0 - if(current_votes[ckey]) - choices[choices[current_votes[ckey]]]-- - if(vote && 1<=vote && vote<=choices.len) - voted += usr.ckey - choices[choices[vote]]++ //check this - current_votes[ckey] = vote - return vote - return 0 - -/datum/controller/subsystem/vote/proc/initiate_vote(var/vote_type, var/initiator_key) - if(!mode) - if(started_time != null && !check_rights(R_ADMIN)) - var/next_allowed_time = (started_time + config.vote_delay) - if(next_allowed_time > world.time) - return 0 - - reset() - switch(vote_type) - if("restart") - choices.Add("Restart Round","Continue Playing") - if("gamemode") - if(SSticker.current_state >= 2) - return 0 - choices.Add(config.votable_modes) - if("crew_transfer") - if(check_rights(R_ADMIN|R_MOD)) - if(SSticker.current_state <= 2) - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - else - if(SSticker.current_state <= 2) - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - if("custom") - question = html_encode(input(usr,"What is the vote for?") as text|null) - if(!question) return 0 - for(var/i=1,i<=10,i++) - var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null)) - if(!option || mode || !usr.client) break - choices.Add(option) - else - return 0 - mode = vote_type - initiator = initiator_key - started_time = world.time - var/text = "[capitalize(mode)] vote started by [initiator]." - if(mode == "custom") - text += "\n[question]" - if(usr) - log_admin("[capitalize(mode)] ([question]) vote started by [key_name(usr)].") - else if(usr) - log_admin("[capitalize(mode)] vote started by [key_name(usr)].") - - log_vote(text) - to_chat(world, {"[text] - Click here or type vote to place your vote. - You have [config.vote_period/10] seconds to vote."}) - switch(vote_type) - if("crew_transfer") - world << sound('sound/ambience/alarm4.ogg') - if("gamemode") - world << sound('sound/ambience/alarm4.ogg') - if("custom") - world << sound('sound/ambience/alarm4.ogg') - if(mode == "gamemode" && going) - going = 0 - to_chat(world, "Round start has been delayed.") - if(mode == "crew_transfer" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to a crew transfer vote.") - log_admin("OOC was toggled automatically due to crew_transfer vote.") - message_admins("OOC has been toggled off automatically.") - if(mode == "gamemode" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to the gamemode vote.") - log_admin("OOC was toggled automatically due to gamemode vote.") - message_admins("OOC has been toggled off automatically.") - if(mode == "custom" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to a custom vote.") - log_admin("OOC was toggled automatically due to custom vote.") - message_admins("OOC has been toggled off automatically.") - - time_remaining = round(config.vote_period/10) - return 1 - return 0 - -/datum/controller/subsystem/vote/proc/browse_to(var/client/C) - if(!C) - return - var/admin = check_rights(R_ADMIN, 0, user = C.mob) - voting |= C - - var/dat = {""} - if(mode) - dat += "
    [vote_html(C)]

    " - if(admin) - dat += "(Cancel Vote) " - else - dat += "

    Start a vote:



    " - var/datum/browser/popup = new(C.mob, "vote", "Voting Panel", nref=src) - popup.set_content(dat) - popup.open() - -/datum/controller/subsystem/vote/proc/update_panel(var/client/C) - C << output(url_encode(vote_html(C)), "vote.browser:update_vote_div") - -/datum/controller/subsystem/vote/proc/vote_html(var/client/C) - . = "" - if(question) - . += "

    Vote: '[question]'

    " - else - . += "

    Vote: [capitalize(mode)]

    " - . += "Time Left: [time_remaining] s
    " - - -/datum/controller/subsystem/vote/Topic(href,href_list[],hsrc) - if(!usr || !usr.client) - return //not necessary but meh...just in-case somebody does something stupid - var/admin = check_rights(R_ADMIN,0) - if(href_list["close"]) - voting -= usr.client - return - switch(href_list["vote"]) - if("open") - // vote proc will automatically get called after this switch ends - if("cancel") - if(admin && mode) - var/votedesc = capitalize(mode) - if(mode == "custom") - votedesc += " ([question])" - admin_log_and_message_admins("cancelled the running [votedesc] vote.") - reset() - if("toggle_restart") - if(admin) - config.allow_vote_restart = !config.allow_vote_restart - if("toggle_gamemode") - if(admin) - config.allow_vote_mode = !config.allow_vote_mode - if("restart") - if(config.allow_vote_restart || admin) - initiate_vote("restart",usr.key) - if("gamemode") - if(config.allow_vote_mode || admin) - initiate_vote("gamemode",usr.key) - if("crew_transfer") - if(config.allow_vote_restart || admin) - initiate_vote("crew_transfer",usr.key) - if("custom") - if(admin) - initiate_vote("custom",usr.key) - else - submit_vote(usr.ckey, round(text2num(href_list["vote"]))) - update_panel(usr.client) - return - usr.vote() - - -/mob/verb/vote() - set category = "OOC" - set name = "Vote" - - if(SSvote) - SSvote.browse_to(client) +SUBSYSTEM_DEF(vote) + name = "Vote" + wait = 10 + flags = SS_KEEP_TIMING|SS_NO_INIT + runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT + + var/initiator = null + var/started_time = null + var/time_remaining = 0 + var/mode = null + var/question = null + var/list/choices = list() + var/list/voted = list() + var/list/voting = list() + var/list/current_votes = list() + var/list/round_voters = list() + var/auto_muted = 0 + +/datum/controller/subsystem/vote/fire() + if(mode) + // No more change mode votes after the game has started. + if(mode == "gamemode" && SSticker.current_state >= GAME_STATE_SETTING_UP) + to_chat(world, "Voting aborted due to game start.") + reset() + return + + // Calculate how much time is remaining by comparing current time, to time of vote start, + // plus vote duration + time_remaining = round((started_time + config.vote_period - world.time)/10) + + if(time_remaining < 0) + result() + for(var/client/C in voting) + if(C) + C << browse(null,"window=vote") + reset() + else + for(var/client/C in voting) + update_panel(C) + CHECK_TICK + +/datum/controller/subsystem/vote/proc/autotransfer() + initiate_vote("crew_transfer","the server") + +/datum/controller/subsystem/vote/proc/reset() + initiator = null + time_remaining = 0 + mode = null + question = null + choices.Cut() + voted.Cut() + voting.Cut() + current_votes.Cut() + + if(auto_muted && !config.ooc_allowed && !(config.auto_toggle_ooc_during_round && SSticker.current_state == GAME_STATE_PLAYING)) + auto_muted = 0 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically enabled due to vote end.") + log_admin("OOC was toggled automatically due to vote end.") + message_admins("OOC has been toggled on automatically.") + + +/datum/controller/subsystem/vote/proc/get_result() + var/greatest_votes = 0 + var/total_votes = 0 + var/list/sorted_choices = list() + var/sorted_highest + var/sorted_votes = -1 + //get the highest number of votes, while also sorting the list + while(choices.len) + // This is a very inefficient sorting method, but that's okay + for(var/option in choices) + var/votes = choices[option] + if(sorted_votes < votes) + sorted_highest = option + sorted_votes = votes + if(votes > greatest_votes) + greatest_votes = votes + sorted_votes = -1 + total_votes += choices[sorted_highest] + sorted_choices[sorted_highest] = choices[sorted_highest] || 0 + choices -= sorted_highest + choices = sorted_choices + //default-vote for everyone who didn't vote + if(!config.vote_no_default && choices.len) + var/non_voters = (GLOB.clients.len - total_votes) + if(non_voters > 0) + if(mode == "restart") + choices["Continue Playing"] += non_voters + if(choices["Continue Playing"] >= greatest_votes) + greatest_votes = choices["Continue Playing"] + else if(mode == "gamemode") + if(GLOB.master_mode in choices) + choices[GLOB.master_mode] += non_voters + if(choices[GLOB.master_mode] >= greatest_votes) + greatest_votes = choices[GLOB.master_mode] + else if(mode == "crew_transfer") + var/factor = 0.5 + switch(world.time / (10 * 60)) // minutes + if(0 to 60) + factor = 0.5 + if(61 to 120) + factor = 0.8 + if(121 to 240) + factor = 1 + if(241 to 300) + factor = 1.2 + else + factor = 1.4 + choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) + to_chat(world, "Crew Transfer Factor: [factor]") + greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) + + + //get all options with that many votes and return them in a list + . = list() + if(greatest_votes) + for(var/option in choices) + if(choices[option] == greatest_votes) + . += option + return . + +/datum/controller/subsystem/vote/proc/announce_result() + var/list/winners = get_result() + var/text + if(winners.len > 0) + if(winners.len > 1) + if(mode != "gamemode" || SSticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes + text = "Vote Tied Between:\n" + for(var/option in winners) + text += "\t[option]\n" + . = pick(winners) + + for(var/key in current_votes) + if(choices[current_votes[key]] == .) + round_voters += key // Keep track of who voted for the winning round. + if(mode == "gamemode" && (. == "extended" || SSticker.hide_mode == 0)) // Announce Extended gamemode, but not other gamemodes + text += "Vote Result: [.] ([choices[.]] vote\s)" + else + if(mode == "custom") + // Completely replace text to show all results in custom votes + text = "[question]\n" + for(var/option in winners) + text += "\t[option]: [choices[option]] vote\s\n" + for(var/option in (choices-winners)) + text += "\t[option]: [choices[option]] vote\s\n" + else if(mode != "gamemode") + text += "Vote Result: [.] ([choices[.]] vote\s)" + else + text += "The vote has ended." // What will be shown if it is a gamemode vote that isn't extended + + else + text += "Vote Result: Inconclusive - No Votes!" + log_vote(text) + to_chat(world, "[text]") + return . + +/datum/controller/subsystem/vote/proc/result() + . = announce_result() + var/restart = 0 + if(.) + switch(mode) + if("restart") + if(. == "Restart Round") + restart = 1 + if("gamemode") + if(GLOB.master_mode != .) + world.save_mode(.) + if(SSticker && SSticker.mode) + restart = 1 + else + GLOB.master_mode = . + if(!SSticker.ticker_going) + SSticker.ticker_going = TRUE + to_chat(world, "The round will start soon.") + if("crew_transfer") + if(. == "Initiate Crew Transfer") + init_shift_change(null, 1) + + + if(restart) + world.Reboot("Restart vote successful.", "end_error", "restart vote") + + return . + +/datum/controller/subsystem/vote/proc/submit_vote(var/ckey, var/vote) + if(mode) + if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) + return 0 + if(current_votes[ckey]) + choices[choices[current_votes[ckey]]]-- + if(vote && 1<=vote && vote<=choices.len) + voted += usr.ckey + choices[choices[vote]]++ //check this + current_votes[ckey] = vote + return vote + return 0 + +/datum/controller/subsystem/vote/proc/initiate_vote(var/vote_type, var/initiator_key) + if(!mode) + if(started_time != null && !check_rights(R_ADMIN)) + var/next_allowed_time = (started_time + config.vote_delay) + if(next_allowed_time > world.time) + return 0 + + reset() + switch(vote_type) + if("restart") + choices.Add("Restart Round","Continue Playing") + if("gamemode") + if(SSticker.current_state >= 2) + return 0 + choices.Add(config.votable_modes) + if("crew_transfer") + if(check_rights(R_ADMIN|R_MOD)) + if(SSticker.current_state <= 2) + return 0 + question = "End the shift?" + choices.Add("Initiate Crew Transfer", "Continue The Round") + else + if(SSticker.current_state <= 2) + return 0 + question = "End the shift?" + choices.Add("Initiate Crew Transfer", "Continue The Round") + if("custom") + question = html_encode(input(usr,"What is the vote for?") as text|null) + if(!question) return 0 + for(var/i=1,i<=10,i++) + var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null)) + if(!option || mode || !usr.client) break + choices.Add(option) + else + return 0 + mode = vote_type + initiator = initiator_key + started_time = world.time + var/text = "[capitalize(mode)] vote started by [initiator]." + if(mode == "custom") + text += "\n[question]" + if(usr) + log_admin("[capitalize(mode)] ([question]) vote started by [key_name(usr)].") + else if(usr) + log_admin("[capitalize(mode)] vote started by [key_name(usr)].") + + log_vote(text) + to_chat(world, {"[text] + Click here or type vote to place your vote. + You have [config.vote_period/10] seconds to vote."}) + switch(vote_type) + if("crew_transfer") + world << sound('sound/ambience/alarm4.ogg') + if("gamemode") + world << sound('sound/ambience/alarm4.ogg') + if("custom") + world << sound('sound/ambience/alarm4.ogg') + if(mode == "gamemode" && SSticker.ticker_going) + SSticker.ticker_going = FALSE + to_chat(world, "Round start has been delayed.") + if(mode == "crew_transfer" && config.ooc_allowed) + auto_muted = 1 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically disabled due to a crew transfer vote.") + log_admin("OOC was toggled automatically due to crew_transfer vote.") + message_admins("OOC has been toggled off automatically.") + if(mode == "gamemode" && config.ooc_allowed) + auto_muted = 1 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically disabled due to the gamemode vote.") + log_admin("OOC was toggled automatically due to gamemode vote.") + message_admins("OOC has been toggled off automatically.") + if(mode == "custom" && config.ooc_allowed) + auto_muted = 1 + config.ooc_allowed = !( config.ooc_allowed ) + to_chat(world, "The OOC channel has been automatically disabled due to a custom vote.") + log_admin("OOC was toggled automatically due to custom vote.") + message_admins("OOC has been toggled off automatically.") + + time_remaining = round(config.vote_period/10) + return 1 + return 0 + +/datum/controller/subsystem/vote/proc/browse_to(var/client/C) + if(!C) + return + var/admin = check_rights(R_ADMIN, 0, user = C.mob) + voting |= C + + var/dat = {""} + if(mode) + dat += "
    [vote_html(C)]

    " + if(admin) + dat += "(Cancel Vote) " + else + dat += "

    Start a vote:



    " + var/datum/browser/popup = new(C.mob, "vote", "Voting Panel", nref=src) + popup.set_content(dat) + popup.open() + +/datum/controller/subsystem/vote/proc/update_panel(var/client/C) + C << output(url_encode(vote_html(C)), "vote.browser:update_vote_div") + +/datum/controller/subsystem/vote/proc/vote_html(var/client/C) + . = "" + if(question) + . += "

    Vote: '[question]'

    " + else + . += "

    Vote: [capitalize(mode)]

    " + . += "Time Left: [time_remaining] s
    " + + +/datum/controller/subsystem/vote/Topic(href,href_list[],hsrc) + if(!usr || !usr.client) + return //not necessary but meh...just in-case somebody does something stupid + var/admin = check_rights(R_ADMIN,0) + if(href_list["close"]) + voting -= usr.client + return + switch(href_list["vote"]) + if("open") + // vote proc will automatically get called after this switch ends + if("cancel") + if(admin && mode) + var/votedesc = capitalize(mode) + if(mode == "custom") + votedesc += " ([question])" + admin_log_and_message_admins("cancelled the running [votedesc] vote.") + reset() + if("toggle_restart") + if(admin) + config.allow_vote_restart = !config.allow_vote_restart + if("toggle_gamemode") + if(admin) + config.allow_vote_mode = !config.allow_vote_mode + if("restart") + if(config.allow_vote_restart || admin) + initiate_vote("restart",usr.key) + if("gamemode") + if(config.allow_vote_mode || admin) + initiate_vote("gamemode",usr.key) + if("crew_transfer") + if(config.allow_vote_restart || admin) + initiate_vote("crew_transfer",usr.key) + if("custom") + if(admin) + initiate_vote("custom",usr.key) + else + submit_vote(usr.ckey, round(text2num(href_list["vote"]))) + update_panel(usr.client) + return + usr.vote() + + +/mob/verb/vote() + set category = "OOC" + set name = "Vote" + + if(SSvote) + SSvote.browse_to(client) diff --git a/code/controllers/subsystem/weather.dm b/code/controllers/subsystem/weather.dm index 4b898df0f11f..ac2f0c59313b 100644 --- a/code/controllers/subsystem/weather.dm +++ b/code/controllers/subsystem/weather.dm @@ -80,4 +80,4 @@ SUBSYSTEM_DEF(weather) if((z in W.impacted_z_levels) && W.area_type == active_area.type) A = W break - return A \ No newline at end of file + return A diff --git a/code/controllers/verbs.dm b/code/controllers/verbs.dm index f694b6d2e757..3eaed85c303a 100644 --- a/code/controllers/verbs.dm +++ b/code/controllers/verbs.dm @@ -1,104 +1,104 @@ -//TODO: rewrite and standardise all controller datums to the datum/controller type -//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done - - -/client/proc/restart_controller(controller in list("Master", "Failsafe")) - set category = "Debug" - set name = "Restart Controller" - set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" - - if(!holder) - return - switch(controller) - if("Master") - Recreate_MC() - feedback_add_details("admin_verb","RMaster") - if("Failsafe") - new /datum/controller/failsafe() - feedback_add_details("admin_verb","RFailsafe") - - message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") - -/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI", - "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires", - "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input")) - set category = "Debug" - set name = "Debug Controller" - set desc = "Debug the various periodic loop controllers for the game (be careful!)" - - if(!holder) return - switch(controller) - if("failsafe") - debug_variables(Failsafe) - feedback_add_details("admin_verb", "dfailsafe") - if("Master") - debug_variables(Master) - feedback_add_details("admin_verb","Dsmc") - if("Ticker") - debug_variables(SSticker) - feedback_add_details("admin_verb","DTicker") - if("Air") - debug_variables(SSair) - feedback_add_details("admin_verb","DAir") - if("Jobs") - debug_variables(SSjobs) - feedback_add_details("admin_verb","DJobs") - if("Sun") - debug_variables(SSsun) - feedback_add_details("admin_verb","DSun") - if("Radio") - debug_variables(SSradio) - feedback_add_details("admin_verb","DRadio") - if("Configuration") - debug_variables(config) - feedback_add_details("admin_verb","DConf") - if("pAI") - debug_variables(paiController) - feedback_add_details("admin_verb","DpAI") - if("Cameras") - debug_variables(cameranet) - feedback_add_details("admin_verb","DCameras") - if("Garbage") - debug_variables(SSgarbage) - feedback_add_details("admin_verb","DGarbage") - if("Event") - debug_variables(SSevents) - feedback_add_details("admin_verb","DEvent") - if("Alarm") - debug_variables(SSalarms) - feedback_add_details("admin_verb", "DAlarm") - if("Nano") - debug_variables(SSnanoui) - feedback_add_details("admin_verb","DNano") - if("Vote") - debug_variables(SSvote) - feedback_add_details("admin_verb","DVote") - if("Fires") - debug_variables(SSfires) - feedback_add_details("admin_verb","DFires") - if("Mob") - debug_variables(SSmobs) - feedback_add_details("admin_verb","DMob") - if("NPC Pool") - debug_variables(SSnpcpool) - feedback_add_details("admin_verb","DNPCPool") - if("Shuttle") - debug_variables(SSshuttle) - feedback_add_details("admin_verb","DShuttle") - if("Timer") - debug_variables(SStimer) - feedback_add_details("admin_verb","DTimer") - if("Weather") - debug_variables(SSweather) - feedback_add_details("admin_verb","DWeather") - if("Space") - debug_variables(space_manager) - feedback_add_details("admin_verb","DSpace") - if("Mob Hunt Server") - debug_variables(SSmob_hunt) - feedback_add_details("admin_verb","DMobHuntServer") - if("Input") - debug_variables(SSinput) - feedback_add_details("admin_verb","DInput") - - message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") \ No newline at end of file +//TODO: rewrite and standardise all controller datums to the datum/controller type +//TODO: allow all controllers to be deleted for clean restarts (see WIP master controller stuff) - MC done - lighting done + + +/client/proc/restart_controller(controller in list("Master", "Failsafe")) + set category = "Debug" + set name = "Restart Controller" + set desc = "Restart one of the various periodic loop controllers for the game (be careful!)" + + if(!holder) + return + switch(controller) + if("Master") + Recreate_MC() + feedback_add_details("admin_verb","RMaster") + if("Failsafe") + new /datum/controller/failsafe() + feedback_add_details("admin_verb","RFailsafe") + + message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.") + +/client/proc/debug_controller(controller in list("failsafe", "Master", "Ticker", "Air", "Jobs", "Sun", "Radio", "Configuration", "pAI", + "Cameras", "Garbage", "Event", "Alarm", "Nano", "Vote", "Fires", + "Mob", "NPC Pool", "Shuttle", "Timer", "Weather", "Space", "Mob Hunt Server","Input")) + set category = "Debug" + set name = "Debug Controller" + set desc = "Debug the various periodic loop controllers for the game (be careful!)" + + if(!holder) return + switch(controller) + if("failsafe") + debug_variables(Failsafe) + feedback_add_details("admin_verb", "dfailsafe") + if("Master") + debug_variables(Master) + feedback_add_details("admin_verb","Dsmc") + if("Ticker") + debug_variables(SSticker) + feedback_add_details("admin_verb","DTicker") + if("Air") + debug_variables(SSair) + feedback_add_details("admin_verb","DAir") + if("Jobs") + debug_variables(SSjobs) + feedback_add_details("admin_verb","DJobs") + if("Sun") + debug_variables(SSsun) + feedback_add_details("admin_verb","DSun") + if("Radio") + debug_variables(SSradio) + feedback_add_details("admin_verb","DRadio") + if("Configuration") + debug_variables(config) + feedback_add_details("admin_verb","DConf") + if("pAI") + debug_variables(GLOB.paiController) + feedback_add_details("admin_verb","DpAI") + if("Cameras") + debug_variables(GLOB.cameranet) + feedback_add_details("admin_verb","DCameras") + if("Garbage") + debug_variables(SSgarbage) + feedback_add_details("admin_verb","DGarbage") + if("Event") + debug_variables(SSevents) + feedback_add_details("admin_verb","DEvent") + if("Alarm") + debug_variables(SSalarms) + feedback_add_details("admin_verb", "DAlarm") + if("Nano") + debug_variables(SSnanoui) + feedback_add_details("admin_verb","DNano") + if("Vote") + debug_variables(SSvote) + feedback_add_details("admin_verb","DVote") + if("Fires") + debug_variables(SSfires) + feedback_add_details("admin_verb","DFires") + if("Mob") + debug_variables(SSmobs) + feedback_add_details("admin_verb","DMob") + if("NPC Pool") + debug_variables(SSnpcpool) + feedback_add_details("admin_verb","DNPCPool") + if("Shuttle") + debug_variables(SSshuttle) + feedback_add_details("admin_verb","DShuttle") + if("Timer") + debug_variables(SStimer) + feedback_add_details("admin_verb","DTimer") + if("Weather") + debug_variables(SSweather) + feedback_add_details("admin_verb","DWeather") + if("Space") + debug_variables(GLOB.space_manager) + feedback_add_details("admin_verb","DSpace") + if("Mob Hunt Server") + debug_variables(SSmob_hunt) + feedback_add_details("admin_verb","DMobHuntServer") + if("Input") + debug_variables(SSinput) + feedback_add_details("admin_verb","DInput") + + message_admins("Admin [key_name_admin(usr)] is debugging the [controller] controller.") diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index 47a8122d1632..0a14b0e9476e 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -1,320 +1,320 @@ -#define BASE_LAW_TYPE /datum/ai_laws/nanotrasen - -/datum/ai_law - var/law = "" - var/index = 0 - -/datum/ai_law/New(law, index) - src.law = law - src.index = index - -/datum/ai_law/proc/get_index() - return index - -/datum/ai_law/ion/get_index() - return ionnum() - -/datum/ai_law/zero/get_index() - return 0 - -/datum/ai_law/sixsixsix/get_index() - return 666 - - -/datum/ai_laws - var/name = "Unknown Laws" - var/law_header = "Prime Directives" - var/selectable = 0 - var/default = 0 - var/datum/ai_law/zero/zeroth_law = null - var/datum/ai_law/zero/zeroth_law_borg = null - var/list/datum/ai_law/inherent_laws = list() - var/list/datum/ai_law/supplied_laws = list() - var/list/datum/ai_law/ion/ion_laws = list() - var/list/datum/ai_law/sixsixsix/devil_laws = list() - var/list/datum/ai_law/sorted_laws = list() - - var/state_zeroth = 0 - var/list/state_devil = list() - var/list/state_ion = list() - var/list/state_inherent = list() - var/list/state_supplied = list() - -/datum/ai_laws/New() - ..() - sort_laws() - -/* General ai_law functions */ -/datum/ai_laws/proc/all_laws() - sort_laws() - return sorted_laws - -/datum/ai_laws/proc/laws_to_state() - sort_laws() - var/list/statements = new() - for(var/datum/ai_law/law in sorted_laws) - if(get_state_law(law)) - statements += law - - return statements - -/datum/ai_laws/proc/sort_laws() - if(sorted_laws.len) - return - - for(var/ion_law in ion_laws) - sorted_laws += ion_law - - for(var/evil_law in devil_laws) - sorted_laws += evil_law - - if(zeroth_law) - sorted_laws += zeroth_law - - var/index = 1 - for(var/datum/ai_law/inherent_law in inherent_laws) - inherent_law.index = index++ - if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) - sorted_laws += inherent_law - - for(var/datum/ai_law/AL in supplied_laws) - if(istype(AL)) - sorted_laws += AL - -/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) - // Add directly to laws to avoid log-spam - S.sync_zeroth(zeroth_law, zeroth_law_borg) - - if(full_sync || ion_laws.len) - S.laws.clear_ion_laws() - if(full_sync || inherent_laws.len) - S.laws.clear_inherent_laws() - if(full_sync || supplied_laws.len) - S.laws.clear_supplied_laws() - - for(var/datum/ai_law/law in ion_laws) - S.laws.add_ion_law(law.law) - for(var/datum/ai_law/law in inherent_laws) - S.laws.add_inherent_law(law.law) - for(var/datum/ai_law/law in supplied_laws) - if(law) - S.laws.add_supplied_law(law.index, law.law) - - -/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if(!is_special_character(src) || mind.original != src) - if(zeroth_law_borg) - laws.set_zeroth_law(zeroth_law_borg.law) - else if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law) - -/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) - if(zeroth_law) - laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) - -/**************** -* Add Laws * -****************/ -/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) - if(!law) - return - - zeroth_law = new(law) - if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO - zeroth_law_borg = new(law_borg) - else - zeroth_law_borg = null - sorted_laws.Cut() - -/datum/ai_laws/proc/set_sixsixsix_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in devil_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/sixsixsix(law) - devil_laws += new_law - if(state_devil.len < devil_laws.len) - state_devil += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_ion_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in ion_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/ion(law) - ion_laws += new_law - if(state_ion.len < ion_laws.len) - state_ion += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_inherent_law(var/law) - if(!law) - return - - for(var/datum/ai_law/AL in inherent_laws) - if(AL.law == law) - return - - var/new_law = new/datum/ai_law/inherent(law) - inherent_laws += new_law - if(state_inherent.len < inherent_laws.len) - state_inherent += 1 - - sorted_laws.Cut() - -/datum/ai_laws/proc/add_supplied_law(var/number, var/law) - if(!law) - return - - if(supplied_laws.len >= number) - var/datum/ai_law/existing_law = supplied_laws[number] - if(existing_law && existing_law.law == law) - return - - if(supplied_laws.len >= number && supplied_laws[number]) - delete_law(supplied_laws[number]) - - while(src.supplied_laws.len < number) - src.supplied_laws += "" - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - var/new_law = new/datum/ai_law/supplied(law, number) - supplied_laws[number] = new_law - if(state_supplied.len < supplied_laws.len) - state_supplied += 1 - - sorted_laws.Cut() - -/**************** -* Remove Laws * -*****************/ -/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) - if(istype(law)) - law.delete_law(src) - -/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) - laws.clear_zeroth_laws() - -/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/sixsixsix/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.devil_laws, laws.state_devil, src) - -/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) - laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) - var/index = laws.supplied_laws.Find(src) - if(index) - laws.supplied_laws[index] = "" - laws.state_supplied[index] = 1 - -/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - laws -= law - for(index, index < state.len, index++) - state[index] = state[index+1] - sorted_laws.Cut() - -/**************** -* Clear Laws * -****************/ -/datum/ai_laws/proc/clear_zeroth_laws() - zeroth_law = null - zeroth_law_borg = null - -/datum/ai_laws/proc/clear_sixsixsix_laws() - devil_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_ion_laws() - ion_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_inherent_laws() - inherent_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/clear_supplied_laws() - supplied_laws.Cut() - sorted_laws.Cut() - -/datum/ai_laws/proc/show_laws(var/who) - sort_laws() - for(var/datum/ai_law/law in sorted_laws) - if(law == zeroth_law_borg) - continue - if(law == zeroth_law) - to_chat(who, "[law.get_index()]. [law.law]") - else - to_chat(who, "[law.get_index()]. [law.law]") - -/******************** -* Stating Laws * -********************/ -/******** -* Get * -********/ -/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) - return law.get_state_law(src) - -/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) - -/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) - if(src == laws.zeroth_law) - return laws.state_zeroth - -/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) - -/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) - -/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) - return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) - -/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) - var/index = laws.Find(law) - if(index) - return state[index] - return 0 - -/******** -* Set * -********/ -/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) - law.set_state_law(src, state) - -/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) - -/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) - if(src == laws.zeroth_law) - laws.state_zeroth = state - -/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) - -/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) - -/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) - laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) - -/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) - var/index = laws.Find(law) - if(index) - state[index] = do_state +#define BASE_LAW_TYPE /datum/ai_laws/nanotrasen + +/datum/ai_law + var/law = "" + var/index = 0 + +/datum/ai_law/New(law, index) + src.law = law + src.index = index + +/datum/ai_law/proc/get_index() + return index + +/datum/ai_law/ion/get_index() + return ionnum() + +/datum/ai_law/zero/get_index() + return 0 + +/datum/ai_law/sixsixsix/get_index() + return 666 + + +/datum/ai_laws + var/name = "Unknown Laws" + var/law_header = "Prime Directives" + var/selectable = 0 + var/default = 0 + var/datum/ai_law/zero/zeroth_law = null + var/datum/ai_law/zero/zeroth_law_borg = null + var/list/datum/ai_law/inherent_laws = list() + var/list/datum/ai_law/supplied_laws = list() + var/list/datum/ai_law/ion/ion_laws = list() + var/list/datum/ai_law/sixsixsix/devil_laws = list() + var/list/datum/ai_law/sorted_laws = list() + + var/state_zeroth = 0 + var/list/state_devil = list() + var/list/state_ion = list() + var/list/state_inherent = list() + var/list/state_supplied = list() + +/datum/ai_laws/New() + ..() + sort_laws() + +/* General ai_law functions */ +/datum/ai_laws/proc/all_laws() + sort_laws() + return sorted_laws + +/datum/ai_laws/proc/laws_to_state() + sort_laws() + var/list/statements = new() + for(var/datum/ai_law/law in sorted_laws) + if(get_state_law(law)) + statements += law + + return statements + +/datum/ai_laws/proc/sort_laws() + if(sorted_laws.len) + return + + for(var/ion_law in ion_laws) + sorted_laws += ion_law + + for(var/evil_law in devil_laws) + sorted_laws += evil_law + + if(zeroth_law) + sorted_laws += zeroth_law + + var/index = 1 + for(var/datum/ai_law/inherent_law in inherent_laws) + inherent_law.index = index++ + if(supplied_laws.len < inherent_law.index || !istype(supplied_laws[inherent_law.index], /datum/ai_law)) + sorted_laws += inherent_law + + for(var/datum/ai_law/AL in supplied_laws) + if(istype(AL)) + sorted_laws += AL + +/datum/ai_laws/proc/sync(var/mob/living/silicon/S, var/full_sync = 1) + // Add directly to laws to avoid log-spam + S.sync_zeroth(zeroth_law, zeroth_law_borg) + + if(full_sync || ion_laws.len) + S.laws.clear_ion_laws() + if(full_sync || inherent_laws.len) + S.laws.clear_inherent_laws() + if(full_sync || supplied_laws.len) + S.laws.clear_supplied_laws() + + for(var/datum/ai_law/law in ion_laws) + S.laws.add_ion_law(law.law) + for(var/datum/ai_law/law in inherent_laws) + S.laws.add_inherent_law(law.law) + for(var/datum/ai_law/law in supplied_laws) + if(law) + S.laws.add_supplied_law(law.index, law.law) + + +/mob/living/silicon/proc/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if(!is_special_character(src) || mind.original != src) + if(zeroth_law_borg) + laws.set_zeroth_law(zeroth_law_borg.law) + else if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law) + +/mob/living/silicon/ai/sync_zeroth(var/datum/ai_law/zeroth_law, var/datum/ai_law/zeroth_law_borg) + if(zeroth_law) + laws.set_zeroth_law(zeroth_law.law, zeroth_law_borg ? zeroth_law_borg.law : null) + +/**************** +* Add Laws * +****************/ +/datum/ai_laws/proc/set_zeroth_law(var/law, var/law_borg = null) + if(!law) + return + + zeroth_law = new(law) + if(law_borg) //Making it possible for slaved borgs to see a different law 0 than their AI. --NEO + zeroth_law_borg = new(law_borg) + else + zeroth_law_borg = null + sorted_laws.Cut() + +/datum/ai_laws/proc/set_sixsixsix_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in devil_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/sixsixsix(law) + devil_laws += new_law + if(state_devil.len < devil_laws.len) + state_devil += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_ion_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in ion_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/ion(law) + ion_laws += new_law + if(state_ion.len < ion_laws.len) + state_ion += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_inherent_law(var/law) + if(!law) + return + + for(var/datum/ai_law/AL in inherent_laws) + if(AL.law == law) + return + + var/new_law = new/datum/ai_law/inherent(law) + inherent_laws += new_law + if(state_inherent.len < inherent_laws.len) + state_inherent += 1 + + sorted_laws.Cut() + +/datum/ai_laws/proc/add_supplied_law(var/number, var/law) + if(!law) + return + + if(supplied_laws.len >= number) + var/datum/ai_law/existing_law = supplied_laws[number] + if(existing_law && existing_law.law == law) + return + + if(supplied_laws.len >= number && supplied_laws[number]) + delete_law(supplied_laws[number]) + + while(src.supplied_laws.len < number) + src.supplied_laws += "" + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + var/new_law = new/datum/ai_law/supplied(law, number) + supplied_laws[number] = new_law + if(state_supplied.len < supplied_laws.len) + state_supplied += 1 + + sorted_laws.Cut() + +/**************** +* Remove Laws * +*****************/ +/datum/ai_laws/proc/delete_law(var/datum/ai_law/law) + if(istype(law)) + law.delete_law(src) + +/datum/ai_law/proc/delete_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/delete_law(var/datum/ai_laws/laws) + laws.clear_zeroth_laws() + +/datum/ai_law/ion/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/sixsixsix/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.devil_laws, laws.state_devil, src) + +/datum/ai_law/inherent/delete_law(var/datum/ai_laws/laws) + laws.internal_delete_law(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/delete_law(var/datum/ai_laws/laws) + var/index = laws.supplied_laws.Find(src) + if(index) + laws.supplied_laws[index] = "" + laws.state_supplied[index] = 1 + +/datum/ai_laws/proc/internal_delete_law(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + laws -= law + for(index, index < state.len, index++) + state[index] = state[index+1] + sorted_laws.Cut() + +/**************** +* Clear Laws * +****************/ +/datum/ai_laws/proc/clear_zeroth_laws() + zeroth_law = null + zeroth_law_borg = null + +/datum/ai_laws/proc/clear_sixsixsix_laws() + devil_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_ion_laws() + ion_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_inherent_laws() + inherent_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/clear_supplied_laws() + supplied_laws.Cut() + sorted_laws.Cut() + +/datum/ai_laws/proc/show_laws(var/who) + sort_laws() + for(var/datum/ai_law/law in sorted_laws) + if(law == zeroth_law_borg) + continue + if(law == zeroth_law) + to_chat(who, "[law.get_index()]. [law.law]") + else + to_chat(who, "[law.get_index()]. [law.law]") + +/******************** +* Stating Laws * +********************/ +/******** +* Get * +********/ +/datum/ai_laws/proc/get_state_law(var/datum/ai_law/law) + return law.get_state_law(src) + +/datum/ai_law/proc/get_state_law(var/datum/ai_laws/laws) + +/datum/ai_law/zero/get_state_law(var/datum/ai_laws/laws) + if(src == laws.zeroth_law) + return laws.state_zeroth + +/datum/ai_law/ion/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.ion_laws, laws.state_ion, src) + +/datum/ai_law/inherent/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.inherent_laws, laws.state_inherent, src) + +/datum/ai_law/supplied/get_state_law(var/datum/ai_laws/laws) + return laws.get_state_internal(laws.supplied_laws, laws.state_supplied, src) + +/datum/ai_laws/proc/get_state_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law) + var/index = laws.Find(law) + if(index) + return state[index] + return 0 + +/******** +* Set * +********/ +/datum/ai_laws/proc/set_state_law(var/datum/ai_law/law, var/state) + law.set_state_law(src, state) + +/datum/ai_law/proc/set_state_law(var/datum/ai_law/law, var/state) + +/datum/ai_law/zero/set_state_law(var/datum/ai_laws/laws, var/state) + if(src == laws.zeroth_law) + laws.state_zeroth = state + +/datum/ai_law/ion/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.ion_laws, laws.state_ion, src, state) + +/datum/ai_law/inherent/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.inherent_laws, laws.state_inherent, src, state) + +/datum/ai_law/supplied/set_state_law(var/datum/ai_laws/laws, var/state) + laws.set_state_law_internal(laws.supplied_laws, laws.state_supplied, src, state) + +/datum/ai_laws/proc/set_state_law_internal(var/list/datum/ai_law/laws, var/list/state, var/list/datum/ai_law/law, var/do_state) + var/index = laws.Find(law) + if(index) + state[index] = do_state diff --git a/code/datums/beam.dm b/code/datums/beam.dm index ac585d071978..86f816662c0d 100644 --- a/code/datums/beam.dm +++ b/code/datums/beam.dm @@ -133,4 +133,4 @@ var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time) spawn(0) newbeam.Start() - return newbeam \ No newline at end of file + return newbeam diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 85f8e52e89d4..76be0b02a129 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -179,4 +179,4 @@ if(src && src.mob) // to_chat(world, "[src] was [src.mob.machine], setting to null") src.mob.unset_machine() - return \ No newline at end of file + return diff --git a/code/datums/cache/air_alarm.dm b/code/datums/cache/air_alarm.dm index 662b0958d5ca..2edc0792a346 100644 --- a/code/datums/cache/air_alarm.dm +++ b/code/datums/cache/air_alarm.dm @@ -1,4 +1,4 @@ -var/global/datum/repository/air_alarm/air_alarm_repository = new() +GLOBAL_DATUM_INIT(air_alarm_repository, /datum/repository/air_alarm, new()) /datum/repository/air_alarm/proc/air_alarm_data(var/list/monitored_alarms, var/refresh = 0, var/obj/machinery/alarm/passed_alarm) var/alarms[0] @@ -11,13 +11,13 @@ var/global/datum/repository/air_alarm/air_alarm_repository = new() if(!refresh) return cache_entry.data - if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time + if(SSticker && SSticker.current_state < GAME_STATE_PLAYING && istype(passed_alarm)) // Generating the list for the first time as the game hasn't started - no need to run through the machines list everything every time alarms = cache_entry.data // Don't deleate the list if(is_station_contact(passed_alarm.z) && passed_alarm.remote_control) // Still need sanity checks - alarms[++alarms.len] = passed_alarm.get_nano_data_console() + alarms[++alarms.len] = passed_alarm.get_nano_data_console() else for(var/obj/machinery/alarm/alarm in (monitored_alarms ? monitored_alarms : GLOB.air_alarms)) // Generating the whole list again is a bad habit but I can't be bothered to fix it right now - if(!monitored_alarms && !is_station_contact(alarm.z)) + if(!monitored_alarms && !is_station_contact(alarm.z)) continue if(!alarm.remote_control) continue diff --git a/code/datums/cache/apc.dm b/code/datums/cache/apc.dm index 29a2574f6e05..318fd654fe37 100644 --- a/code/datums/cache/apc.dm +++ b/code/datums/cache/apc.dm @@ -1,4 +1,4 @@ -var/global/datum/repository/apc/apc_repository = new() +GLOBAL_DATUM_INIT(apc_repository, /datum/repository/apc, new()) /datum/repository/apc/proc/apc_data(datum/powernet/powernet) var/apcData[0] @@ -25,4 +25,4 @@ var/global/datum/repository/apc/apc_repository = new() cache_entry.timestamp = world.time + 5 SECONDS cache_entry.data = apcData - return apcData \ No newline at end of file + return apcData diff --git a/code/datums/cache/cache.dm b/code/datums/cache/cache.dm index f401e54517a4..09d8670e29eb 100644 --- a/code/datums/cache/cache.dm +++ b/code/datums/cache/cache.dm @@ -3,4 +3,4 @@ var/list/data = list() /datum/repository - var/cache_data \ No newline at end of file + var/cache_data diff --git a/code/datums/cache/crew.dm b/code/datums/cache/crew.dm index b30b5c578893..21ec5ebcc796 100644 --- a/code/datums/cache/crew.dm +++ b/code/datums/cache/crew.dm @@ -1,4 +1,4 @@ -var/global/datum/repository/crew/crew_repository = new() +GLOBAL_DATUM_INIT(crew_repository, /datum/repository/crew, new()) /datum/repository/crew/New() cache_data = list() diff --git a/code/datums/cache/powermonitor.dm b/code/datums/cache/powermonitor.dm index 5112d58ad29d..606dd500edab 100644 --- a/code/datums/cache/powermonitor.dm +++ b/code/datums/cache/powermonitor.dm @@ -1,8 +1,8 @@ -var/global/datum/repository/powermonitor/powermonitor_repository = new() +GLOBAL_DATUM_INIT(powermonitor_repository, /datum/repository/powermonitor, new()) /datum/repository/powermonitor/proc/powermonitor_data(var/refresh = 0) var/pMonData[0] - + var/datum/cache_entry/cache_entry = cache_data if(!cache_entry) cache_entry = new/datum/cache_entry @@ -10,15 +10,15 @@ var/global/datum/repository/powermonitor/powermonitor_repository = new() if(!refresh) return cache_entry.data - + for(var/obj/machinery/computer/monitor/pMon in GLOB.power_monitors) if( !(pMon.stat & (NOPOWER|BROKEN)) && !pMon.is_secret_monitor ) pMonData[++pMonData.len] = list ("Name" = pMon.name, "ref" = "\ref[pMon]") cache_entry.timestamp = world.time //+ 30 SECONDS - cache_entry.data = pMonData + cache_entry.data = pMonData return pMonData - + /datum/repository/powermonitor/proc/update_cache() return powermonitor_data(refresh = 1) - \ No newline at end of file + diff --git a/code/datums/click_intercept.dm b/code/datums/click_intercept.dm index d3e9bccbda0a..7eed1bb479bf 100644 --- a/code/datums/click_intercept.dm +++ b/code/datums/click_intercept.dm @@ -28,4 +28,4 @@ return /datum/click_intercept/proc/InterceptClickOn(user,params,atom/object) - return \ No newline at end of file + return diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 7b3b2ec503a2..e733ca78d2be 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -310,4 +310,4 @@ target.TakeComponent(comps) /datum/component/nano_host() - return parent \ No newline at end of file + return parent diff --git a/code/datums/components/caltrop.dm b/code/datums/components/caltrop.dm index 63c6110aa5da..3bd740973a35 100644 --- a/code/datums/components/caltrop.dm +++ b/code/datums/components/caltrop.dm @@ -56,4 +56,4 @@ H.visible_message("[H] slides on [A]!", "You slide on [A]!") cooldown = world.time - H.Weaken(3) \ No newline at end of file + H.Weaken(3) diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm index 26d4653b25a7..6a0f686b13ae 100644 --- a/code/datums/components/decal.dm +++ b/code/datums/components/decal.dm @@ -72,4 +72,4 @@ qdel(src) /datum/component/decal/proc/examine(datum/source, mob/user, var/list/examine_list) - examine_list += description \ No newline at end of file + examine_list += description diff --git a/code/datums/components/material_container.dm b/code/datums/components/material_container.dm index 8d44679fa4ad..3071a07d5bba 100644 --- a/code/datums/components/material_container.dm +++ b/code/datums/components/material_container.dm @@ -398,4 +398,4 @@ /datum/material/plastic name = "Plastic" id = MAT_PLASTIC - sheet_type = /obj/item/stack/sheet/plastic \ No newline at end of file + sheet_type = /obj/item/stack/sheet/plastic diff --git a/code/datums/components/spawner.dm b/code/datums/components/spawner.dm index 68ec51959a27..bbc71ce101f8 100644 --- a/code/datums/components/spawner.dm +++ b/code/datums/components/spawner.dm @@ -48,4 +48,4 @@ spawned_mobs += L L.nest = src L.faction = src.faction - P.visible_message("[L] [spawn_text] [P].") \ No newline at end of file + P.visible_message("[L] [spawn_text] [P].") diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm index aca505e6a032..818e78e8d2e6 100644 --- a/code/datums/components/squeak.dm +++ b/code/datums/components/squeak.dm @@ -90,4 +90,4 @@ /datum/component/squeak/proc/holder_dir_change(datum/source, old_dir, new_dir) //If the dir changes it means we're going through a bend in the pipes, let's pretend we bumped the wall if(old_dir != new_dir) - play_squeak() \ No newline at end of file + play_squeak() diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm index 7845d836f497..fa8bb1db9e23 100644 --- a/code/datums/datacore.dm +++ b/code/datums/datacore.dm @@ -1,652 +1,652 @@ -/hook/startup/proc/createDatacore() - data_core = new /datum/datacore() - return 1 - -/datum/datacore - var/list/medical = list() - var/list/general = list() - var/list/security = list() - //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). - var/list/locked = list() - -/datum/datacore/proc/get_manifest(monochrome, OOC) - var/list/heads = new() - var/list/sec = new() - var/list/eng = new() - var/list/med = new() - var/list/sci = new() - var/list/ser = new() - var/list/sup = new() - var/list/bot = new() - var/list/misc = new() - var/list/isactive = new() - var/dat = {" - - - - "} - var/even = 0 - // sort mobs - for(var/datum/data/record/t in data_core.general) - var/name = t.fields["name"] - var/rank = t.fields["rank"] - var/real_rank = t.fields["real_rank"] - if(OOC) - var/active = 0 - for(var/mob/M in GLOB.player_list) - if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) - active = 1 - break - isactive[name] = active ? "Active" : "Inactive" - else - isactive[name] = t.fields["p_stat"] - var/department = 0 - if(real_rank in command_positions) - heads[name] = rank - department = 1 - if(real_rank in security_positions) - sec[name] = rank - department = 1 - if(real_rank in engineering_positions) - eng[name] = rank - department = 1 - if(real_rank in medical_positions) - med[name] = rank - department = 1 - if(real_rank in science_positions) - sci[name] = rank - department = 1 - if(real_rank in service_positions) - ser[name] = rank - department = 1 - if(real_rank in supply_positions) - sup[name] = rank - department = 1 - if(real_rank in nonhuman_positions) - bot[name] = rank - department = 1 - if(!department && !(name in heads)) - misc[name] = rank - if(heads.len > 0) - dat += "" - for(var/name in heads) - dat += "" - even = !even - if(sec.len > 0) - dat += "" - for(var/name in sec) - dat += "" - even = !even - if(eng.len > 0) - dat += "" - for(var/name in eng) - dat += "" - even = !even - if(med.len > 0) - dat += "" - for(var/name in med) - dat += "" - even = !even - if(sci.len > 0) - dat += "" - for(var/name in sci) - dat += "" - even = !even - if(ser.len > 0) - dat += "" - for(var/name in ser) - dat += "" - even = !even - if(sup.len > 0) - dat += "" - for(var/name in sup) - dat += "" - even = !even - if(bot.len > 0) - dat += "" - for(var/name in bot) - dat += "" - even = !even - if(misc.len > 0) - dat += "" - for(var/name in misc) - dat += "" - even = !even - - dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Service
    [name][ser[name]][isactive[name]]
    Supply
    [name][sup[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " - dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly - dat = replacetext(dat, "\t", "") - return dat - - -/* -We can't just insert in HTML into the nanoUI so we need the raw data to play with. -Instead of creating this list over and over when someone leaves their PDA open to the page -we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change -using /datum/datacore/proc/manifest_inject(), or manifest_insert() -*/ - -var/global/list/PDA_Manifest = list() - -/datum/datacore/proc/get_manifest_json() - if(PDA_Manifest.len) - return - var/heads[0] - var/sec[0] - var/eng[0] - var/med[0] - var/sci[0] - var/ser[0] - var/sup[0] - var/bot[0] - var/misc[0] - for(var/datum/data/record/t in data_core.general) - var/name = sanitize(t.fields["name"]) - var/rank = sanitize(t.fields["rank"]) - var/real_rank = t.fields["real_rank"] - - var/isactive = t.fields["p_stat"] - var/department = 0 - var/depthead = 0 // Department Heads will be placed at the top of their lists. - if(real_rank in command_positions) - heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - depthead = 1 - if(rank == "Captain" && heads.len != 1) - heads.Swap(1, heads.len) - - if(real_rank in security_positions) - sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sec.len != 1) - sec.Swap(1, sec.len) - - if(real_rank in engineering_positions) - eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && eng.len != 1) - eng.Swap(1, eng.len) - - if(real_rank in medical_positions) - med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && med.len != 1) - med.Swap(1, med.len) - - if(real_rank in science_positions) - sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sci.len != 1) - sci.Swap(1, sci.len) - - if(real_rank in service_positions) - ser[++ser.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && ser.len != 1) - ser.Swap(1, ser.len) - - if(real_rank in supply_positions) - sup[++sup.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - if(depthead && sup.len != 1) - sup.Swap(1, sup.len) - - if(real_rank in nonhuman_positions) - bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) - department = 1 - - if(!department && !(name in heads)) - misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) - - - PDA_Manifest = list(\ - "heads" = heads,\ - "sec" = sec,\ - "eng" = eng,\ - "med" = med,\ - "sci" = sci,\ - "ser" = ser,\ - "sup" = sup,\ - "bot" = bot,\ - "misc" = misc\ - ) - return - - - -/datum/datacore/proc/manifest() - for(var/mob/living/carbon/human/H in GLOB.player_list) - manifest_inject(H) - -/datum/datacore/proc/manifest_modify(name, assignment) - if(PDA_Manifest.len) - PDA_Manifest.Cut() - var/datum/data/record/foundrecord - var/real_title = assignment - - for(var/datum/data/record/t in data_core.general) - if(t) - if(t.fields["name"] == name) - foundrecord = t - break - - var/list/all_jobs = get_job_datums() - - for(var/datum/job/J in all_jobs) - var/list/alttitles = get_alternate_titles(J.title) - if(!J) continue - if(assignment in alttitles) - real_title = J.title - break - - if(foundrecord) - foundrecord.fields["rank"] = assignment - foundrecord.fields["real_rank"] = real_title - -var/record_id_num = 1001 -/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H) - if(PDA_Manifest.len) - PDA_Manifest.Cut() - - if(H.mind && (H.mind.assigned_role != H.mind.special_role)) - var/assignment - if(H.mind.role_alt_title) - assignment = H.mind.role_alt_title - else if(H.mind.assigned_role) - assignment = H.mind.assigned_role - else if(H.job) - assignment = H.job - else - assignment = "Unassigned" - - var/id = num2hex(record_id_num++, 6) - - - //General Record - var/datum/data/record/G = new() - G.fields["id"] = id - G.fields["name"] = H.real_name - G.fields["real_rank"] = H.mind.assigned_role - G.fields["rank"] = assignment - G.fields["age"] = H.age - G.fields["fingerprint"] = md5(H.dna.uni_identity) - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["sex"] = capitalize(H.gender) - G.fields["species"] = H.dna.species.name - G.fields["photo"] = get_id_photo(H) - G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'" - G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'" - if(H.gen_record && !jobban_isbanned(H, "Records")) - G.fields["notes"] = H.gen_record - else - G.fields["notes"] = "No notes found." - general += G - - //Medical Record - var/datum/data/record/M = new() - M.fields["id"] = id - M.fields["name"] = H.real_name - M.fields["blood_type"] = H.dna.blood_type - M.fields["b_dna"] = H.dna.unique_enzymes - M.fields["mi_dis"] = "None" - M.fields["mi_dis_d"] = "No minor disabilities have been declared." - M.fields["ma_dis"] = "None" - M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - M.fields["alg"] = "None" - M.fields["alg_d"] = "No allergies have been detected in this patient." - M.fields["cdi"] = "None" - M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - if(H.med_record && !jobban_isbanned(H, "Records")) - M.fields["notes"] = H.med_record - else - M.fields["notes"] = "No notes found." - medical += M - - //Security Record - var/datum/data/record/S = new() - S.fields["id"] = id - S.fields["name"] = H.real_name - S.fields["criminal"] = "None" - S.fields["mi_crim"] = "None" - S.fields["mi_crim_d"] = "No minor crime convictions." - S.fields["ma_crim"] = "None" - S.fields["ma_crim_d"] = "No major crime convictions." - S.fields["notes"] = "No notes." - if(H.sec_record && !jobban_isbanned(H, "Records")) - S.fields["notes"] = H.sec_record - else - S.fields["notes"] = "No notes." - LAZYINITLIST(S.fields["comments"]) - security += S - - //Locked Record - var/datum/data/record/L = new() - L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") - L.fields["name"] = H.real_name - L.fields["rank"] = H.mind.assigned_role - L.fields["age"] = H.age - L.fields["sex"] = capitalize(H.gender) - L.fields["blood_type"] = H.dna.blood_type - L.fields["b_dna"] = H.dna.unique_enzymes - L.fields["enzymes"] = H.dna.SE // Used in respawning - L.fields["identity"] = H.dna.UI // " - L.fields["image"] = getFlatIcon(H) //This is god-awful - L.fields["reference"] = H - locked += L - return - -/proc/get_id_photo(mob/living/carbon/human/H, var/custom_job = null) - var/icon/preview_icon = null - var/obj/item/organ/external/head/head_organ = H.get_organ("head") - var/obj/item/organ/internal/eyes/eyes_organ = H.get_int_organ(/obj/item/organ/internal/eyes) - - var/g = "m" - if(H.gender == FEMALE) - g = "f" - - var/icon/icobase = head_organ.icobase //At this point all the organs would have the same icobase, so this is just recycling. - - preview_icon = new /icon(icobase, "torso_[g]") - var/icon/temp - temp = new /icon(icobase, "groin_[g]") - preview_icon.Blend(temp, ICON_OVERLAY) - var/head = "head" - if(head_organ.alt_head && head_organ.dna.species.bodyflags & HAS_ALT_HEADS) - var/datum/sprite_accessory/alt_heads/alternate_head = GLOB.alt_heads_list[head_organ.alt_head] - if(alternate_head.icon_state) - head = alternate_head.icon_state - temp = new /icon(icobase, "[head]_[g]") - preview_icon.Blend(temp, ICON_OVERLAY) - - //Tail - if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/tail)) - temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) - preview_icon.Blend(temp, ICON_OVERLAY) - else if(H.tail && H.dna.species.bodyflags & HAS_TAIL) - temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.tail]_s") - preview_icon.Blend(temp, ICON_OVERLAY) - - for(var/obj/item/organ/external/E in H.bodyparts) - preview_icon.Blend(E.get_icon(), ICON_OVERLAY) - - // Skin tone - if(H.dna.species.bodyflags & HAS_SKIN_TONE) - if(H.s_tone >= 0) - preview_icon.Blend(rgb(H.s_tone, H.s_tone, H.s_tone), ICON_ADD) - else - preview_icon.Blend(rgb(-H.s_tone, -H.s_tone, -H.s_tone), ICON_SUBTRACT) - - // Proper Skin color - Fix, you can't have HAS_SKIN_TONE *and* HAS_SKIN_COLOR - if(H.dna.species.bodyflags & HAS_SKIN_COLOR) - preview_icon.Blend(H.skin_colour, ICON_ADD) - - //Tail Markings - var/icon/t_marking_s - if(H.dna.species.bodyflags & HAS_TAIL_MARKINGS) - var/tail_marking = H.m_styles["tail"] - var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] - if(tail_marking_style && tail_marking_style.species_allowed) - t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") - t_marking_s.Blend(H.m_colours["tail"], ICON_ADD) - if(!(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body))) - preview_icon.Blend(t_marking_s, ICON_OVERLAY) - - var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") - if(!(H.dna.species.bodyflags & NO_EYES)) - var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = H.dna.species ? H.dna.species.eyes : "eyes_s") - if(!eyes_organ) - return - eyes_s.Blend(eyes_organ.eye_colour, ICON_ADD) - face_s.Blend(eyes_s, ICON_OVERLAY) - - var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] - if(hair_style) - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - // I'll want to make a species-specific proc for this sooner or later - // But this'll do for now - if(istype(head_organ.dna.species, /datum/species/slime)) - hair_s.Blend("[H.skin_colour]A0", ICON_AND) //A0 = 160 alpha. - else - hair_s.Blend(head_organ.hair_colour, ICON_ADD) - - if(hair_style.secondary_theme) - var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") - if(!hair_style.no_sec_colour) - hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) - hair_s.Blend(hair_secondary_s, ICON_OVERLAY) - - face_s.Blend(hair_s, ICON_OVERLAY) - - //Head Accessory - if(head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY) - var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style] - if(head_accessory_style && head_accessory_style.species_allowed) - var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") - head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD) - face_s.Blend(head_accessory_s, ICON_OVERLAY) - - var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] - if(facial_hair_style && facial_hair_style.species_allowed) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(istype(head_organ.dna.species, /datum/species/slime)) - facial_s.Blend("[H.skin_colour]A0", ICON_ADD) //A0 = 160 alpha. - else - facial_s.Blend(head_organ.facial_colour, ICON_ADD) - - if(facial_hair_style.secondary_theme) - var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") - if(!facial_hair_style.no_sec_colour) - facial_secondary_s.Blend(head_organ.sec_facial_colour, ICON_ADD) - facial_s.Blend(facial_secondary_s, ICON_OVERLAY) - - face_s.Blend(facial_s, ICON_OVERLAY) - - //Markings - if((H.dna.species.bodyflags & HAS_HEAD_MARKINGS) || (H.dna.species.bodyflags & HAS_BODY_MARKINGS)) - if(H.dna.species.bodyflags & HAS_BODY_MARKINGS) //Body markings. - var/body_marking = H.m_styles["body"] - var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] - if(body_marking_style && body_marking_style.species_allowed) - var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") - b_marking_s.Blend(H.m_colours["body"], ICON_ADD) - face_s.Blend(b_marking_s, ICON_OVERLAY) - if(H.dna.species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. - var/head_marking = H.m_styles["head"] - var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] - if(head_marking_style && head_marking_style.species_allowed) - var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") - h_marking_s.Blend(H.m_colours["head"], ICON_ADD) - face_s.Blend(h_marking_s, ICON_OVERLAY) - - preview_icon.Blend(face_s, ICON_OVERLAY) - - - var/icon/clothes_s = null - var/job_clothes = null - if(custom_job) - job_clothes = custom_job - else if(H.mind) - job_clothes = H.mind.assigned_role - switch(job_clothes) - if("Head of Personnel") - clothes_s = new /icon('icons/mob/uniform.dmi', "hop_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Nanotrasen Representative") - clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - if("Blueshield") - clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) - if("Magistrate") - clothes_s = new /icon('icons/mob/uniform.dmi', "really_black_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_OVERLAY) - if("Bartender") - clothes_s = new /icon('icons/mob/uniform.dmi', "ba_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Botanist") - clothes_s = new /icon('icons/mob/uniform.dmi', "hydroponics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Chef") - clothes_s = new /icon('icons/mob/uniform.dmi', "chef_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Janitor") - clothes_s = new /icon('icons/mob/uniform.dmi', "janitor_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Librarian") - clothes_s = new /icon('icons/mob/uniform.dmi', "red_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Barber") - clothes_s = new /icon('icons/mob/uniform.dmi', "barber_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Clown") - clothes_s = new /icon('icons/mob/uniform.dmi', "clown_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) - if("Mime") - clothes_s = new /icon('icons/mob/uniform.dmi', "mime_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Quartermaster") - clothes_s = new /icon('icons/mob/uniform.dmi', "qm_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Cargo Technician") - clothes_s = new /icon('icons/mob/uniform.dmi', "cargotech_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Shaft Miner") - clothes_s = new /icon('icons/mob/uniform.dmi', "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Lawyer") - clothes_s = new /icon('icons/mob/uniform.dmi', "internalaffairs_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Chaplain") - clothes_s = new /icon('icons/mob/uniform.dmi', "chapblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Research Director") - clothes_s = new /icon('icons/mob/uniform.dmi', "director_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if("Scientist") - clothes_s = new /icon('icons/mob/uniform.dmi', "toxinswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) - if("Chemist") - clothes_s = new /icon('icons/mob/uniform.dmi', "chemistrywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) - if("Chief Medical Officer") - clothes_s = new /icon('icons/mob/uniform.dmi', "cmo_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) - if("Medical Doctor") - clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if("Coroner") - clothes_s = new /icon('icons/mob/uniform.dmi', "scrubsblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_mort_open"), ICON_OVERLAY) - if("Geneticist") - clothes_s = new /icon('icons/mob/uniform.dmi', "geneticswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) - if("Virologist") - clothes_s = new /icon('icons/mob/uniform.dmi', "virologywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) - if("Psychiatrist") - clothes_s = new /icon('icons/mob/uniform.dmi', "psych_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_UNDERLAY) - if("Paramedic") - clothes_s = new /icon('icons/mob/uniform.dmi', "paramedic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Captain") - clothes_s = new /icon('icons/mob/uniform.dmi', "captain_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if("Head of Security") - clothes_s = new /icon('icons/mob/uniform.dmi', "hosred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if("Warden") - clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if("Detective") - clothes_s = new /icon('icons/mob/uniform.dmi', "detective_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) - if("Security Pod Pilot") - clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) - if("Brig Physician") - clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) - if("Security Officer") - clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if("Chief Engineer") - clothes_s = new /icon('icons/mob/uniform.dmi', "chief_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Station Engineer") - clothes_s = new /icon('icons/mob/uniform.dmi', "engine_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Life Support Specialist") - clothes_s = new /icon('icons/mob/uniform.dmi', "atmos_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Mechanic") - clothes_s = new /icon('icons/mob/uniform.dmi', "mechanic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if("Roboticist") - clothes_s = new /icon('icons/mob/uniform.dmi', "robotics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if("Syndicate Agent") - clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if("Syndicate Officer") - clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - if("Syndicate Nuclear Operative") - clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - else if(H.mind && H.mind.assigned_role in get_all_centcom_jobs()) - clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - else - clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - - preview_icon.Blend(face_s, ICON_OVERLAY) // Why do we do this twice - if(clothes_s) - preview_icon.Blend(clothes_s, ICON_OVERLAY) - //Bus body accessories that go over clothes. - if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body)) - temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) - if(H.body_accessory.pixel_x_offset) - temp.Shift(EAST, H.body_accessory.pixel_x_offset) - if(H.body_accessory.pixel_y_offset) - temp.Shift(NORTH, H.body_accessory.pixel_y_offset) - if(H.dna.species.bodyflags & HAS_SKIN_COLOR) - temp.Blend(H.skin_colour, H.body_accessory.blend_mode) - if(t_marking_s) - temp.Blend(t_marking_s, ICON_OVERLAY) - preview_icon.Blend(temp, ICON_OVERLAY) - qdel(face_s) - qdel(clothes_s) - - return preview_icon +/hook/startup/proc/createDatacore() + GLOB.data_core = new /datum/datacore() + return 1 + +/datum/datacore + var/list/medical = list() + var/list/general = list() + var/list/security = list() + //This list tracks characters spawned in the world and cannot be modified in-game. Currently referenced by respawn_character(). + var/list/locked = list() + +/datum/datacore/proc/get_manifest(monochrome, OOC) + var/list/heads = new() + var/list/sec = new() + var/list/eng = new() + var/list/med = new() + var/list/sci = new() + var/list/ser = new() + var/list/sup = new() + var/list/bot = new() + var/list/misc = new() + var/list/isactive = new() + var/dat = {" + + + + "} + var/even = 0 + // sort mobs + for(var/datum/data/record/t in GLOB.data_core.general) + var/name = t.fields["name"] + var/rank = t.fields["rank"] + var/real_rank = t.fields["real_rank"] + if(OOC) + var/active = 0 + for(var/mob/M in GLOB.player_list) + if(M.real_name == name && M.client && M.client.inactivity <= 10 * 60 * 10) + active = 1 + break + isactive[name] = active ? "Active" : "Inactive" + else + isactive[name] = t.fields["p_stat"] + var/department = 0 + if(real_rank in GLOB.command_positions) + heads[name] = rank + department = 1 + if(real_rank in GLOB.security_positions) + sec[name] = rank + department = 1 + if(real_rank in GLOB.engineering_positions) + eng[name] = rank + department = 1 + if(real_rank in GLOB.medical_positions) + med[name] = rank + department = 1 + if(real_rank in GLOB.science_positions) + sci[name] = rank + department = 1 + if(real_rank in GLOB.service_positions) + ser[name] = rank + department = 1 + if(real_rank in GLOB.supply_positions) + sup[name] = rank + department = 1 + if(real_rank in GLOB.nonhuman_positions) + bot[name] = rank + department = 1 + if(!department && !(name in heads)) + misc[name] = rank + if(heads.len > 0) + dat += "" + for(var/name in heads) + dat += "" + even = !even + if(sec.len > 0) + dat += "" + for(var/name in sec) + dat += "" + even = !even + if(eng.len > 0) + dat += "" + for(var/name in eng) + dat += "" + even = !even + if(med.len > 0) + dat += "" + for(var/name in med) + dat += "" + even = !even + if(sci.len > 0) + dat += "" + for(var/name in sci) + dat += "" + even = !even + if(ser.len > 0) + dat += "" + for(var/name in ser) + dat += "" + even = !even + if(sup.len > 0) + dat += "" + for(var/name in sup) + dat += "" + even = !even + if(bot.len > 0) + dat += "" + for(var/name in bot) + dat += "" + even = !even + if(misc.len > 0) + dat += "" + for(var/name in misc) + dat += "" + even = !even + + dat += "
    NameRankActivity
    Heads
    [name][heads[name]][isactive[name]]
    Security
    [name][sec[name]][isactive[name]]
    Engineering
    [name][eng[name]][isactive[name]]
    Medical
    [name][med[name]][isactive[name]]
    Science
    [name][sci[name]][isactive[name]]
    Service
    [name][ser[name]][isactive[name]]
    Supply
    [name][sup[name]][isactive[name]]
    Silicon
    [name][bot[name]][isactive[name]]
    Miscellaneous
    [name][misc[name]][isactive[name]]
    " + dat = replacetext(dat, "\n", "") // so it can be placed on paper correctly + dat = replacetext(dat, "\t", "") + return dat + + +/* +We can't just insert in HTML into the nanoUI so we need the raw data to play with. +Instead of creating this list over and over when someone leaves their PDA open to the page +we'll only update it when it changes. The PDA_Manifest global list is zeroed out upon any change +using /datum/datacore/proc/manifest_inject(), or manifest_insert() +*/ + +GLOBAL_LIST_EMPTY(PDA_Manifest) + +/datum/datacore/proc/get_manifest_json() + if(GLOB.PDA_Manifest.len) + return + var/heads[0] + var/sec[0] + var/eng[0] + var/med[0] + var/sci[0] + var/ser[0] + var/sup[0] + var/bot[0] + var/misc[0] + for(var/datum/data/record/t in GLOB.data_core.general) + var/name = sanitize(t.fields["name"]) + var/rank = sanitize(t.fields["rank"]) + var/real_rank = t.fields["real_rank"] + + var/isactive = t.fields["p_stat"] + var/department = 0 + var/depthead = 0 // Department Heads will be placed at the top of their lists. + if(real_rank in GLOB.command_positions) + heads[++heads.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + depthead = 1 + if(rank == "Captain" && heads.len != 1) + heads.Swap(1, heads.len) + + if(real_rank in GLOB.security_positions) + sec[++sec.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sec.len != 1) + sec.Swap(1, sec.len) + + if(real_rank in GLOB.engineering_positions) + eng[++eng.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && eng.len != 1) + eng.Swap(1, eng.len) + + if(real_rank in GLOB.medical_positions) + med[++med.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && med.len != 1) + med.Swap(1, med.len) + + if(real_rank in GLOB.science_positions) + sci[++sci.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sci.len != 1) + sci.Swap(1, sci.len) + + if(real_rank in GLOB.service_positions) + ser[++ser.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && ser.len != 1) + ser.Swap(1, ser.len) + + if(real_rank in GLOB.supply_positions) + sup[++sup.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + if(depthead && sup.len != 1) + sup.Swap(1, sup.len) + + if(real_rank in GLOB.nonhuman_positions) + bot[++bot.len] = list("name" = name, "rank" = rank, "active" = isactive) + department = 1 + + if(!department && !(name in heads)) + misc[++misc.len] = list("name" = name, "rank" = rank, "active" = isactive) + + + GLOB.PDA_Manifest = list(\ + "heads" = heads,\ + "sec" = sec,\ + "eng" = eng,\ + "med" = med,\ + "sci" = sci,\ + "ser" = ser,\ + "sup" = sup,\ + "bot" = bot,\ + "misc" = misc\ + ) + return + + + +/datum/datacore/proc/manifest() + for(var/mob/living/carbon/human/H in GLOB.player_list) + manifest_inject(H) + +/datum/datacore/proc/manifest_modify(name, assignment) + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + var/datum/data/record/foundrecord + var/real_title = assignment + + for(var/datum/data/record/t in GLOB.data_core.general) + if(t) + if(t.fields["name"] == name) + foundrecord = t + break + + var/list/all_jobs = get_job_datums() + + for(var/datum/job/J in all_jobs) + var/list/alttitles = get_alternate_titles(J.title) + if(!J) continue + if(assignment in alttitles) + real_title = J.title + break + + if(foundrecord) + foundrecord.fields["rank"] = assignment + foundrecord.fields["real_rank"] = real_title + +GLOBAL_VAR_INIT(record_id_num, 1001) +/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H) + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + + if(H.mind && (H.mind.assigned_role != H.mind.special_role)) + var/assignment + if(H.mind.role_alt_title) + assignment = H.mind.role_alt_title + else if(H.mind.assigned_role) + assignment = H.mind.assigned_role + else if(H.job) + assignment = H.job + else + assignment = "Unassigned" + + var/id = num2hex(GLOB.record_id_num++, 6) + + + //General Record + var/datum/data/record/G = new() + G.fields["id"] = id + G.fields["name"] = H.real_name + G.fields["real_rank"] = H.mind.assigned_role + G.fields["rank"] = assignment + G.fields["age"] = H.age + G.fields["fingerprint"] = md5(H.dna.uni_identity) + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["sex"] = capitalize(H.gender) + G.fields["species"] = H.dna.species.name + G.fields["photo"] = get_id_photo(H) + G.fields["photo-south"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = SOUTH))]'" + G.fields["photo-west"] = "'data:image/png;base64,[icon2base64(icon(G.fields["photo"], dir = WEST))]'" + if(H.gen_record && !jobban_isbanned(H, "Records")) + G.fields["notes"] = H.gen_record + else + G.fields["notes"] = "No notes found." + general += G + + //Medical Record + var/datum/data/record/M = new() + M.fields["id"] = id + M.fields["name"] = H.real_name + M.fields["blood_type"] = H.dna.blood_type + M.fields["b_dna"] = H.dna.unique_enzymes + M.fields["mi_dis"] = "None" + M.fields["mi_dis_d"] = "No minor disabilities have been declared." + M.fields["ma_dis"] = "None" + M.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + M.fields["alg"] = "None" + M.fields["alg_d"] = "No allergies have been detected in this patient." + M.fields["cdi"] = "None" + M.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + if(H.med_record && !jobban_isbanned(H, "Records")) + M.fields["notes"] = H.med_record + else + M.fields["notes"] = "No notes found." + medical += M + + //Security Record + var/datum/data/record/S = new() + S.fields["id"] = id + S.fields["name"] = H.real_name + S.fields["criminal"] = "None" + S.fields["mi_crim"] = "None" + S.fields["mi_crim_d"] = "No minor crime convictions." + S.fields["ma_crim"] = "None" + S.fields["ma_crim_d"] = "No major crime convictions." + S.fields["notes"] = "No notes." + if(H.sec_record && !jobban_isbanned(H, "Records")) + S.fields["notes"] = H.sec_record + else + S.fields["notes"] = "No notes." + LAZYINITLIST(S.fields["comments"]) + security += S + + //Locked Record + var/datum/data/record/L = new() + L.fields["id"] = md5("[H.real_name][H.mind.assigned_role]") + L.fields["name"] = H.real_name + L.fields["rank"] = H.mind.assigned_role + L.fields["age"] = H.age + L.fields["sex"] = capitalize(H.gender) + L.fields["blood_type"] = H.dna.blood_type + L.fields["b_dna"] = H.dna.unique_enzymes + L.fields["enzymes"] = H.dna.SE // Used in respawning + L.fields["identity"] = H.dna.UI // " + L.fields["image"] = getFlatIcon(H) //This is god-awful + L.fields["reference"] = H + locked += L + return + +/proc/get_id_photo(mob/living/carbon/human/H, var/custom_job = null) + var/icon/preview_icon = null + var/obj/item/organ/external/head/head_organ = H.get_organ("head") + var/obj/item/organ/internal/eyes/eyes_organ = H.get_int_organ(/obj/item/organ/internal/eyes) + + var/g = "m" + if(H.gender == FEMALE) + g = "f" + + var/icon/icobase = head_organ.icobase //At this point all the organs would have the same icobase, so this is just recycling. + + preview_icon = new /icon(icobase, "torso_[g]") + var/icon/temp + temp = new /icon(icobase, "groin_[g]") + preview_icon.Blend(temp, ICON_OVERLAY) + var/head = "head" + if(head_organ.alt_head && head_organ.dna.species.bodyflags & HAS_ALT_HEADS) + var/datum/sprite_accessory/alt_heads/alternate_head = GLOB.alt_heads_list[head_organ.alt_head] + if(alternate_head.icon_state) + head = alternate_head.icon_state + temp = new /icon(icobase, "[head]_[g]") + preview_icon.Blend(temp, ICON_OVERLAY) + + //Tail + if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/tail)) + temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) + preview_icon.Blend(temp, ICON_OVERLAY) + else if(H.tail && H.dna.species.bodyflags & HAS_TAIL) + temp = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[H.tail]_s") + preview_icon.Blend(temp, ICON_OVERLAY) + + for(var/obj/item/organ/external/E in H.bodyparts) + preview_icon.Blend(E.get_icon(), ICON_OVERLAY) + + // Skin tone + if(H.dna.species.bodyflags & HAS_SKIN_TONE) + if(H.s_tone >= 0) + preview_icon.Blend(rgb(H.s_tone, H.s_tone, H.s_tone), ICON_ADD) + else + preview_icon.Blend(rgb(-H.s_tone, -H.s_tone, -H.s_tone), ICON_SUBTRACT) + + // Proper Skin color - Fix, you can't have HAS_SKIN_TONE *and* HAS_SKIN_COLOR + if(H.dna.species.bodyflags & HAS_SKIN_COLOR) + preview_icon.Blend(H.skin_colour, ICON_ADD) + + //Tail Markings + var/icon/t_marking_s + if(H.dna.species.bodyflags & HAS_TAIL_MARKINGS) + var/tail_marking = H.m_styles["tail"] + var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] + if(tail_marking_style && tail_marking_style.species_allowed) + t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") + t_marking_s.Blend(H.m_colours["tail"], ICON_ADD) + if(!(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body))) + preview_icon.Blend(t_marking_s, ICON_OVERLAY) + + var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") + if(!(H.dna.species.bodyflags & NO_EYES)) + var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = H.dna.species ? H.dna.species.eyes : "eyes_s") + if(!eyes_organ) + return + eyes_s.Blend(eyes_organ.eye_colour, ICON_ADD) + face_s.Blend(eyes_s, ICON_OVERLAY) + + var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] + if(hair_style) + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + // I'll want to make a species-specific proc for this sooner or later + // But this'll do for now + if(istype(head_organ.dna.species, /datum/species/slime)) + hair_s.Blend("[H.skin_colour]A0", ICON_AND) //A0 = 160 alpha. + else + hair_s.Blend(head_organ.hair_colour, ICON_ADD) + + if(hair_style.secondary_theme) + var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") + if(!hair_style.no_sec_colour) + hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) + hair_s.Blend(hair_secondary_s, ICON_OVERLAY) + + face_s.Blend(hair_s, ICON_OVERLAY) + + //Head Accessory + if(head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY) + var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style] + if(head_accessory_style && head_accessory_style.species_allowed) + var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") + head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD) + face_s.Blend(head_accessory_s, ICON_OVERLAY) + + var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] + if(facial_hair_style && facial_hair_style.species_allowed) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(istype(head_organ.dna.species, /datum/species/slime)) + facial_s.Blend("[H.skin_colour]A0", ICON_ADD) //A0 = 160 alpha. + else + facial_s.Blend(head_organ.facial_colour, ICON_ADD) + + if(facial_hair_style.secondary_theme) + var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") + if(!facial_hair_style.no_sec_colour) + facial_secondary_s.Blend(head_organ.sec_facial_colour, ICON_ADD) + facial_s.Blend(facial_secondary_s, ICON_OVERLAY) + + face_s.Blend(facial_s, ICON_OVERLAY) + + //Markings + if((H.dna.species.bodyflags & HAS_HEAD_MARKINGS) || (H.dna.species.bodyflags & HAS_BODY_MARKINGS)) + if(H.dna.species.bodyflags & HAS_BODY_MARKINGS) //Body markings. + var/body_marking = H.m_styles["body"] + var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] + if(body_marking_style && body_marking_style.species_allowed) + var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") + b_marking_s.Blend(H.m_colours["body"], ICON_ADD) + face_s.Blend(b_marking_s, ICON_OVERLAY) + if(H.dna.species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. + var/head_marking = H.m_styles["head"] + var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] + if(head_marking_style && head_marking_style.species_allowed) + var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") + h_marking_s.Blend(H.m_colours["head"], ICON_ADD) + face_s.Blend(h_marking_s, ICON_OVERLAY) + + preview_icon.Blend(face_s, ICON_OVERLAY) + + + var/icon/clothes_s = null + var/job_clothes = null + if(custom_job) + job_clothes = custom_job + else if(H.mind) + job_clothes = H.mind.assigned_role + switch(job_clothes) + if("Head of Personnel") + clothes_s = new /icon('icons/mob/uniform.dmi', "hop_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Nanotrasen Representative") + clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + if("Blueshield") + clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) + if("Magistrate") + clothes_s = new /icon('icons/mob/uniform.dmi', "really_black_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_OVERLAY) + if("Bartender") + clothes_s = new /icon('icons/mob/uniform.dmi', "ba_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Botanist") + clothes_s = new /icon('icons/mob/uniform.dmi', "hydroponics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Chef") + clothes_s = new /icon('icons/mob/uniform.dmi', "chef_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Janitor") + clothes_s = new /icon('icons/mob/uniform.dmi', "janitor_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Librarian") + clothes_s = new /icon('icons/mob/uniform.dmi', "red_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Barber") + clothes_s = new /icon('icons/mob/uniform.dmi', "barber_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Clown") + clothes_s = new /icon('icons/mob/uniform.dmi', "clown_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) + if("Mime") + clothes_s = new /icon('icons/mob/uniform.dmi', "mime_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Quartermaster") + clothes_s = new /icon('icons/mob/uniform.dmi', "qm_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Cargo Technician") + clothes_s = new /icon('icons/mob/uniform.dmi', "cargotech_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Shaft Miner") + clothes_s = new /icon('icons/mob/uniform.dmi', "miner_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Lawyer") + clothes_s = new /icon('icons/mob/uniform.dmi', "internalaffairs_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Chaplain") + clothes_s = new /icon('icons/mob/uniform.dmi', "chapblack_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Research Director") + clothes_s = new /icon('icons/mob/uniform.dmi', "director_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if("Scientist") + clothes_s = new /icon('icons/mob/uniform.dmi', "toxinswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) + if("Chemist") + clothes_s = new /icon('icons/mob/uniform.dmi', "chemistrywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) + if("Chief Medical Officer") + clothes_s = new /icon('icons/mob/uniform.dmi', "cmo_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) + if("Medical Doctor") + clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if("Coroner") + clothes_s = new /icon('icons/mob/uniform.dmi', "scrubsblack_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_mort_open"), ICON_OVERLAY) + if("Geneticist") + clothes_s = new /icon('icons/mob/uniform.dmi', "geneticswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) + if("Virologist") + clothes_s = new /icon('icons/mob/uniform.dmi', "virologywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) + if("Psychiatrist") + clothes_s = new /icon('icons/mob/uniform.dmi', "psych_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_UNDERLAY) + if("Paramedic") + clothes_s = new /icon('icons/mob/uniform.dmi', "paramedic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Captain") + clothes_s = new /icon('icons/mob/uniform.dmi', "captain_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if("Head of Security") + clothes_s = new /icon('icons/mob/uniform.dmi', "hosred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if("Warden") + clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if("Detective") + clothes_s = new /icon('icons/mob/uniform.dmi', "detective_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) + if("Security Pod Pilot") + clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) + if("Brig Physician") + clothes_s = new /icon('icons/mob/uniform.dmi', "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) + if("Security Officer") + clothes_s = new /icon('icons/mob/uniform.dmi', "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if("Chief Engineer") + clothes_s = new /icon('icons/mob/uniform.dmi', "chief_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Station Engineer") + clothes_s = new /icon('icons/mob/uniform.dmi', "engine_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Life Support Specialist") + clothes_s = new /icon('icons/mob/uniform.dmi', "atmos_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Mechanic") + clothes_s = new /icon('icons/mob/uniform.dmi', "mechanic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if("Roboticist") + clothes_s = new /icon('icons/mob/uniform.dmi', "robotics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if("Syndicate Agent") + clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if("Syndicate Officer") + clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + if("Syndicate Nuclear Operative") + clothes_s = new /icon('icons/mob/uniform.dmi', "syndicate_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + else if(H.mind && H.mind.assigned_role in get_all_centcom_jobs()) + clothes_s = new /icon('icons/mob/uniform.dmi', "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + else + clothes_s = new /icon('icons/mob/uniform.dmi', "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + + preview_icon.Blend(face_s, ICON_OVERLAY) // Why do we do this twice + if(clothes_s) + preview_icon.Blend(clothes_s, ICON_OVERLAY) + //Bus body accessories that go over clothes. + if(H.body_accessory && istype(H.body_accessory, /datum/body_accessory/body)) + temp = new/icon("icon" = H.body_accessory.icon, "icon_state" = H.body_accessory.icon_state) + if(H.body_accessory.pixel_x_offset) + temp.Shift(EAST, H.body_accessory.pixel_x_offset) + if(H.body_accessory.pixel_y_offset) + temp.Shift(NORTH, H.body_accessory.pixel_y_offset) + if(H.dna.species.bodyflags & HAS_SKIN_COLOR) + temp.Blend(H.skin_colour, H.body_accessory.blend_mode) + if(t_marking_s) + temp.Blend(t_marking_s, ICON_OVERLAY) + preview_icon.Blend(temp, ICON_OVERLAY) + qdel(face_s) + qdel(clothes_s) + + return preview_icon diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 6e716f057c43..00054349e364 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -66,4 +66,4 @@ /datum/nothing - // Placeholder object, used for ispath checks. Has to be defined to prevent errors, but shouldn't ever be created. \ No newline at end of file + // Placeholder object, used for ispath checks. Has to be defined to prevent errors, but shouldn't ever be created. diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index daa501191353..b2b09003934c 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -1,1387 +1,1387 @@ -// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0) - -/datum/proc/can_vv_get(var_name) - return TRUE - -// /client/proc/can_vv_get(var_name) -// return TRUE - -/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited - switch(var_name) - if("vars") - return FALSE - if("var_edited") - return FALSE - var_edited = TRUE - vars[var_name] = var_value - - . = TRUE - - -/client/vv_edit_var(var_name, var_value) //called whenever a var is edited - switch(var_name) - if("vars") - return FALSE - if("var_edited") - return FALSE - var_edited = TRUE - vars[var_name] = var_value - - . = TRUE - -/datum/proc/vv_get_var(var_name) - switch(var_name) - if("attack_log", "debug_log") - return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE) - if("vars") - return debug_variable(var_name, list(), 0, src) - return debug_variable(var_name, vars[var_name], 0, src) - -/client/vv_get_var(var_name) - switch(var_name) - if("vars") - return debug_variable(var_name, list(), 0, src) - return debug_variable(var_name, vars[var_name], 0, src) - -/datum/proc/can_vv_delete() - return TRUE - -//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down -//add seperaters by doing . += "---" -/datum/proc/vv_get_dropdown() - . = list() - . += "---" - .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" - .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" - .["Jump to Object"] = "?_src_=vars;jump_to=[UID()]" - .["Delete"] = "?_src_=vars;delete=[UID()]" - .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" - . += "---" - -/client/vv_get_dropdown() - . = list() - . += "---" - .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" - .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" - .["Delete"] = "?_src_=vars;delete=[UID()]" - .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" - . += "---" - -/client/proc/debug_variables(datum/D in world) - set category = "Debug" - set name = "View Variables" - - var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. - - if(!is_admin(usr)) - to_chat(usr, "You need to be an administrator to access this.") - return - - if(!D) - return - - - var/islist = islist(D) - var/isclient = isclient(D) - if(!islist && !isclient && !istype(D)) - return - - var/title = "" - var/icon/sprite - var/hash - var/refid - - if(!islist) - refid = "[D.UID()]" - else - refid = "\ref[D]" - - var/type = /list - if(!islist) - type = D.type - - if(isatom(D)) - var/atom/A = D - if(A.icon && A.icon_state) - sprite = new /icon(A.icon, A.icon_state) - hash = md5(A.icon) - hash = md5(hash + A.icon_state) - usr << browse_rsc(sprite, "vv[hash].png") - - - var/sprite_text - if(sprite) - sprite_text = "" - - - var/list/atomsnowflake = list() - if(isatom(D)) - var/atom/A = D - if(isliving(A)) - var/mob/living/L = A - atomsnowflake += "[L]" - if(L.dir) - atomsnowflake += "
    << [dir2text(L.dir)] >>" - atomsnowflake += {" -
    [L.ckey ? L.ckey : "No ckey"] / [L.real_name ? L.real_name : "No real name"] -
    - BRUTE:[L.getBruteLoss()] - FIRE:[L.getFireLoss()] - TOXIN:[L.getToxLoss()] - OXY:[L.getOxyLoss()] - CLONE:[L.getCloneLoss()] - BRAIN:[L.getBrainLoss()] - STAMINA:[L.getStaminaLoss()] - - "} - else - atomsnowflake += "[A]" - if(A.dir) - atomsnowflake += "
    << [dir2text(A.dir)] >>" - else - atomsnowflake += "[D]" - - - var/formatted_type = "[type]" - if(length(formatted_type) > 25) - var/middle_point = length(formatted_type) / 2 - var/splitpoint = findtext(formatted_type, "/", middle_point) - if(splitpoint) - formatted_type = "[copytext(formatted_type, 1, splitpoint)]
    [copytext(formatted_type, splitpoint)]" - else - formatted_type = "Type too long" //No suitable splitpoint (/) found. - - - var/marked - if(holder.marked_datum && holder.marked_datum == D) - marked = "
    Marked Object" - - - var/varedited_line = "" - if(isatom(D)) - var/atom/A = D - if(A.admin_spawned) - varedited_line += "
    Admin Spawned" - - - if(!islist && D.var_edited) - varedited_line += "
    Var Edited" - - - var/dropdownoptions = list() - if(islist) - dropdownoptions = list( - "---", - "Add Item" = "?_src_=vars;listadd=[refid]", - "Remove Nulls" = "?_src_=vars;listnulls=[refid]", - "Remove Dupes" = "?_src_=vars;listdupes=[refid]", - "Set len" = "?_src_=vars;listlen=[refid]", - "Shuffle" = "?_src_=vars;listshuffle=[refid]" - ) - else - dropdownoptions = D.vv_get_dropdown() - - - var/list/dropdownoptions_html = list() - for(var/name in dropdownoptions) - var/link = dropdownoptions[name] - if(link) - dropdownoptions_html += "" - else - dropdownoptions_html += "" - - - var/list/names = list() - if(!islist) - for(var/V in D.vars) - names += V - - - sleep(1) // Without a sleep here, VV sometimes disconnects clients - - - var/list/variable_html = list() - if(islist) - var/list/L = D - for(var/i in 1 to L.len) - var/key = L[i] - var/value - if(IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - variable_html += debug_variable(i, value, 0, D) - else - names = sortList(names) - for(var/V in names) - if(D.can_vv_get(V)) - variable_html += D.vv_get_var(V) - - var/html = {" - - - [title] - - - - -
    - - - - - -
    - - - - -
    - [sprite_text] -
    - [atomsnowflake.Join()] -
    -
    -
    - [formatted_type] - [marked] - [varedited_line] -
    -
    -
    - Refresh -
    - -
    -
    -
    -
    -
    - - E - Edit, tries to determine the variable type by itself.
    - C - Change, asks you for the var type first.
    - M - Mass modify: changes this variable for all objects of this type.
    -
    -
    - - - - - -
    -
    - Search: -
    -
    - -
    -
    -
      - [variable_html.Join()] -
    - - - - "} - - usr << browse(html, "window=variables[refid];size=475x650") - -#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) -/proc/debug_variable(name, value, level, var/datum/DA = null, sanitize = TRUE) - var/header - if(DA) - if(islist(DA)) - var/index = name - if(value) - name = DA[name] // name is really the index until this line - else - value = DA[name] - header = "
  • (E) (C) (-) " - else - header = "
  • (E) (C) (M) " - else - header = "
  • " - - var/item - if(isnull(value)) - item = "[VV_HTML_ENCODE(name)] = null" - - else if(istext(value)) - item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" - - else if(isicon(value)) - #ifdef VARSICON - item = "[name] = /icon ([value]) [bicon(value, use_class=0)]" - #else - item = "[name] = /icon ([value])" - #endif - - else if(istype(value, /image)) - var/image/I = value - #ifdef VARSICON - item = "[name] \ref[value] = /image ([value]) [bicon(value, use_class=0)]" - #else - item = "[name] \ref[value] = /image ([value])" - #endif - - else if(isfile(value)) - item = "[VV_HTML_ENCODE(name)] = '[value]'" - - else if(istype(value, /datum)) - var/datum/D = value - item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" - - else if(istype(value, /client)) - var/client/C = value - item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" -// - else if(islist(value)) - var/list/L = value - var/list/items = list() - - if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > (IS_NORMAL_LIST(L) ? 250 : 300))) - for(var/i in 1 to L.len) - var/key = L[i] - var/val - if(IS_NORMAL_LIST(L) && !isnum(key)) - val = L[key] - if(!val) - val = key - key = i - - items += debug_variable(key, val, level + 1, sanitize = sanitize) - - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " - - else - item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" - - else - item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" - - return "[header][item]
  • " - -#undef VV_HTML_ENCODE - -/client/proc/view_var_Topic(href, href_list, hsrc) - //This should all be moved over to datum/admins/Topic() or something ~Carn - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings - return - - // Correct and warn about any VV topic links that aren't using UIDs - for(var/paramname in href_list) - if(findtext(href_list[paramname], "]_")) - continue // Contains UID-specific formatting, skip it - var/datum/D = locate(href_list[paramname]) - if(!D) - continue - var/datuminfo = "[D]" - if(istype(D)) - datuminfo = datum_info_line(D) - href_list[paramname] = D.UID() - else if(isclient(D)) - var/client/C = D - href_list[paramname] = C.UID() - log_runtime(EXCEPTION("Found \\ref-based '[paramname]' param in VV topic for [datuminfo], should be UID: [href]")) - - if(href_list["Vars"]) - debug_variables(locateUID(href_list["Vars"])) - - //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). - else if(href_list["rename"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["rename"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/new_name = reject_bad_name(sanitize(copytext(input(usr, "What would you like to name this mob?", "Input a name", M.real_name) as text|null, 1, MAX_NAME_LEN)), allow_numbers = TRUE) - if( !new_name || !M ) return - - message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") - M.rename_character(M.real_name, new_name) - href_list["datumrefresh"] = href_list["rename"] - - else if(href_list["varnameedit"] && href_list["datumedit"]) - if(!check_rights(R_VAREDIT)) return - - var/D = locateUID(href_list["datumedit"]) - if(!istype(D,/datum) && !istype(D,/client)) - to_chat(usr, "This can only be used on instances of types /client or /datum") - return - - modify_variables(D, href_list["varnameedit"], 1) - - else if(href_list["togbit"]) - if(!check_rights(R_VAREDIT)) return - - var/atom/D = locateUID(href_list["subject"]) - if(!istype(D,/datum) && !istype(D,/client)) - to_chat(usr, "This can only be used on instances of types /client or /datum") - return - if(!(href_list["var"] in D.vars)) - to_chat(usr, "Unable to find variable specified.") - return - var/value = D.vars[href_list["var"]] - value ^= 1 << text2num(href_list["togbit"]) - - D.vars[href_list["var"]] = value - - else if(href_list["varnamechange"] && href_list["datumchange"]) - if(!check_rights(R_VAREDIT)) return - - var/D = locateUID(href_list["datumchange"]) - if(!istype(D,/datum) && !istype(D,/client)) - to_chat(usr, "This can only be used on instances of types /client or /datum") - return - - modify_variables(D, href_list["varnamechange"], 0) - - else if(href_list["varnamemass"] && href_list["datummass"]) - if(!check_rights(R_VAREDIT)) return - - var/atom/A = locateUID(href_list["datummass"]) - if(!istype(A)) - to_chat(usr, "This can only be used on instances of type /atom") - return - - cmd_mass_modify_object_variables(A, href_list["varnamemass"]) - - - else if(href_list["mob_player_panel"]) - if(!check_rights(R_ADMIN|R_MOD)) return - - var/mob/M = locateUID(href_list["mob_player_panel"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.holder.show_player_panel(M) - href_list["datumrefresh"] = href_list["mob_player_panel"] - - else if(href_list["give_spell"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/M = locateUID(href_list["give_spell"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_spell(M) - href_list["datumrefresh"] = href_list["give_spell"] - - else if(href_list["givemartialart"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/living/carbon/C = locateUID(href_list["givemartialart"]) - if(!istype(C)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/list/artpaths = subtypesof(/datum/martial_art) - var/list/artnames = list() - for(var/i in artpaths) - var/datum/martial_art/M = i - artnames[initial(M.name)] = M - - var/result = input(usr, "Choose the martial art to teach", "JUDO CHOP") as null|anything in artnames - if(!usr) - return - if(QDELETED(C)) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(result) - var/chosenart = artnames[result] - var/datum/martial_art/MA = new chosenart - MA.teach(C) - - href_list["datumrefresh"] = href_list["givemartialart"] - - else if(href_list["give_disease"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/M = locateUID(href_list["give_disease"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.give_disease(M) - href_list["datumrefresh"] = href_list["give_spell"] - - else if(href_list["godmode"]) - if(!check_rights(R_REJUVINATE)) return - - var/mob/M = locateUID(href_list["godmode"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_godmode(M) - href_list["datumrefresh"] = href_list["godmode"] - - else if(href_list["gib"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/M = locateUID(href_list["gib"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - src.cmd_admin_gib(M) - - else if(href_list["build_mode"]) - if(!check_rights(R_BUILDMODE)) return - - var/mob/M = locateUID(href_list["build_mode"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - togglebuildmode(M) - href_list["datumrefresh"] = href_list["build_mode"] - - else if(href_list["drop_everything"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/mob/M = locateUID(href_list["drop_everything"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_admin_drop_everything(M) - - else if(href_list["direct_control"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/mob/M = locateUID(href_list["direct_control"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(usr.client) - usr.client.cmd_assume_direct_control(M) - - else if(href_list["make_skeleton"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/living/carbon/human/H = locateUID(href_list["make_skeleton"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - var/confirm = alert("Are you sure you want to turn this mob into a skeleton?","Confirm Skeleton Transformation","Yes","No") - if(confirm != "Yes") - return - - H.makeSkeleton() - message_admins("[key_name(usr)] has turned [key_name(H)] into a skeleton") - log_admin("[key_name_admin(usr)] has turned [key_name_admin(H)] into a skeleton") - href_list["datumrefresh"] = href_list["make_skeleton"] - - else if(href_list["offer_control"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["offer_control"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - offer_control(M) - - else if(href_list["delete"]) - if(!check_rights(R_DEBUG, 0)) - return - - var/datum/D = locateUID(href_list["delete"]) - if(!D) - to_chat(usr, "Unable to locate item!") - admin_delete(D) - href_list["datumrefresh"] = href_list["delete"] - - else if(href_list["delall"]) - if(!check_rights(R_DEBUG|R_SERVER)) return - - var/obj/O = locateUID(href_list["delall"]) - if(!isobj(O)) - to_chat(usr, "This can only be used on instances of type /obj") - return - - var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") - if(action_type == "Cancel" || !action_type) - return - - if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") - return - - if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") - return - - var/O_type = O.type - switch(action_type) - if("Strict type") - var/i = 0 - for(var/obj/Obj in world) - if(Obj.type == O_type) - i++ - qdel(Obj) - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted)") - message_admins("[key_name_admin(usr)] deleted all objects of type [O_type] ([i] objects deleted)") - if("Type and subtypes") - var/i = 0 - for(var/obj/Obj in world) - if(istype(Obj,O_type)) - i++ - qdel(Obj) - if(!i) - to_chat(usr, "No objects of this type exist") - return - log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") - message_admins("[key_name_admin(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") - - else if(href_list["makespeedy"]) - if(!check_rights(R_DEBUG|R_ADMIN)) - return - var/obj/A = locateUID(href_list["makespeedy"]) - if(!istype(A)) - return - A.var_edited = TRUE - A.makeSpeedProcess() - log_admin("[key_name(usr)] has made [A] speed process") - message_admins("[key_name(usr)] has made [A] speed process") - return TRUE - - else if(href_list["makenormalspeed"]) - if(!check_rights(R_DEBUG|R_ADMIN)) - return - var/obj/A = locateUID(href_list["makenormalspeed"]) - if(!istype(A)) - return - A.var_edited = TRUE - A.makeNormalProcess() - log_admin("[key_name(usr)] has made [A] process normally") - message_admins("[key_name(usr)] has made [A] process normally") - return TRUE - - else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */ - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/atom/A = locateUID(href_list["addreagent"]) - - if(!A.reagents) - var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num - if(amount) - A.create_reagents(amount) - - if(A.reagents) - var/chosen_id - var/list/reagent_options = sortAssoc(GLOB.chemical_reagents_list) - switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) - if("Enter ID") - var/valid_id - while(!valid_id) - chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") - if(!chosen_id) //Get me out of here! - break - for(var/ID in reagent_options) - if(ID == chosen_id) - valid_id = 1 - if(!valid_id) - to_chat(usr, "A reagent with that ID doesn't exist!") - if("Choose ID") - chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options - if(chosen_id) - var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num - if(amount) - A.reagents.add_reagent(chosen_id, amount) - log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") - - else if(href_list["explode"]) - if(!check_rights(R_DEBUG|R_EVENT)) return - - var/atom/A = locateUID(href_list["explode"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_explosion(A) - href_list["datumrefresh"] = href_list["explode"] - - else if(href_list["emp"]) - if(!check_rights(R_DEBUG|R_EVENT)) return - - var/atom/A = locateUID(href_list["emp"]) - if(!isobj(A) && !ismob(A) && !isturf(A)) - to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") - return - - src.cmd_admin_emp(A) - href_list["datumrefresh"] = href_list["emp"] - - else if(href_list["mark_object"]) - if(!check_rights(0)) return - - var/datum/D = locateUID(href_list["mark_object"]) - if(!istype(D)) - to_chat(usr, "This can only be done to instances of type /datum") - return - - src.holder.marked_datum = D - href_list["datumrefresh"] = href_list["mark_object"] - - else if(href_list["proc_call"]) - if(!check_rights(R_PROCCALL)) - return - - var/T = locateUID(href_list["proc_call"]) - - if(T) - callproc_datum(T) - - else if(href_list["jump_to"]) - if(!check_rights(R_ADMIN)) - return - - var/atom/A = locateUID(href_list["jump_to"]) - var/turf/T = get_turf(A) - if(T) - usr.client.jumptoturf(T) - href_list["datumrefresh"] = href_list["jump_to"] - - - else if(href_list["rotatedatum"]) - if(!check_rights(R_DEBUG|R_ADMIN)) return - - var/atom/A = locateUID(href_list["rotatedatum"]) - if(!istype(A)) - to_chat(usr, "This can only be done to instances of type /atom") - return - - switch(href_list["rotatedir"]) - if("right") A.dir = turn(A.dir, -45) - if("left") A.dir = turn(A.dir, 45) - - message_admins("[key_name_admin(usr)] has rotated \the [A]") - log_admin("[key_name(usr)] has rotated \the [A]") - href_list["datumrefresh"] = href_list["rotatedatum"] - - else if(href_list["makemonkey"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makemonkey"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makerobot"=href_list["makerobot"])) - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makealien"=href_list["makealien"])) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeslime"=href_list["makeslime"])) - - else if(href_list["makesuper"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makesuper"=href_list["makesuper"])) - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - holder.Topic(href, list("makeai"=href_list["makeai"])) - - else if(href_list["setspecies"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["setspecies"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") - return - - var/new_species = input("Please choose a new species.","Species",null) as null|anything in GLOB.all_species - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - var/datum/species/S = GLOB.all_species[new_species] - if(H.set_species(S.type)) - to_chat(usr, "Set species of [H] to [H.dna.species].") - H.regenerate_icons() - message_admins("[key_name_admin(usr)] has changed the species of [key_name_admin(H)] to [new_species]") - log_admin("[key_name(usr)] has changed the species of [key_name(H)] to [new_species]") - else - to_chat(usr, "Failed! Something went wrong.") - - else if(href_list["addlanguage"]) - if(!check_rights(R_SPAWN)) return - - var/mob/H = locateUID(href_list["addlanguage"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob") - return - - var/new_language = input("Please choose a language to add.","Language",null) as null|anything in GLOB.all_languages - - if(!new_language) - return - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(H.add_language(new_language)) - to_chat(usr, "Added [new_language] to [H].") - message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the language [new_language]") - log_admin("[key_name(usr)] has given [key_name(H)] the language [new_language]") - else - to_chat(usr, "Mob already knows that language.") - - else if(href_list["remlanguage"]) - if(!check_rights(R_SPAWN)) return - - var/mob/H = locateUID(href_list["remlanguage"]) - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob") - return - - if(!H.languages.len) - to_chat(usr, "This mob knows no languages.") - return - - var/datum/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in H.languages - - if(!rem_language) - return - - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(H.remove_language(rem_language.name)) - to_chat(usr, "Removed [rem_language] from [H].") - message_admins("[key_name_admin(usr)] has removed language [rem_language] from [key_name_admin(H)]") - log_admin("[key_name(usr)] has removed language [rem_language] from [key_name(H)]") - else - to_chat(usr, "Mob doesn't know that language.") - - else if(href_list["addverb"]) - if(!check_rights(R_DEBUG)) return - - var/mob/living/H = locateUID(href_list["addverb"]) - - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob/living") - return - var/list/possibleverbs = list() - possibleverbs += "Cancel" // One for the top... - possibleverbs += typesof(/mob/proc,/mob/verb,/mob/living/proc,/mob/living/verb) - switch(H.type) - if(/mob/living/carbon/human) - possibleverbs += typesof(/mob/living/carbon/proc,/mob/living/carbon/verb,/mob/living/carbon/human/verb,/mob/living/carbon/human/proc) - if(/mob/living/silicon/robot) - possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/robot/proc,/mob/living/silicon/robot/verb) - if(/mob/living/silicon/ai) - possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/ai/proc,/mob/living/silicon/ai/verb) - possibleverbs -= H.verbs - possibleverbs += "Cancel" // ...And one for the bottom - - var/verb = input("Select a verb!", "Verbs",null) as anything in possibleverbs - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - if(!verb || verb == "Cancel") - return - else - H.verbs += verb - message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the verb [verb]") - log_admin("[key_name(usr)] has given [key_name(H)] the verb [verb]") - - else if(href_list["remverb"]) - if(!check_rights(R_DEBUG)) return - - var/mob/H = locateUID(href_list["remverb"]) - - if(!istype(H)) - to_chat(usr, "This can only be done to instances of type /mob") - return - var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in H.verbs - if(!H) - to_chat(usr, "Mob doesn't exist anymore") - return - if(!verb) - return - else - H.verbs -= verb - message_admins("[key_name_admin(usr)] has removed verb [verb] from [key_name_admin(H)]") - log_admin("[key_name(usr)] has removed verb [verb] from [key_name(H)]") - - else if(href_list["addorgan"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/M = locateUID(href_list["addorgan"]) - if(!istype(M)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/new_organ = input("Please choose an organ to add.","Organ",null) as null|anything in subtypesof(/obj/item/organ)-/obj/item/organ - if(!new_organ) return - - if(!M) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(locateUID(new_organ) in M.internal_organs) - to_chat(usr, "Mob already has that organ.") - return - var/obj/item/organ/internal/organ = new new_organ - organ.insert(M) - message_admins("[key_name_admin(usr)] has given [key_name_admin(M)] the organ [new_organ]") - log_admin("[key_name(usr)] has given [key_name(M)] the organ [new_organ]") - - else if(href_list["remorgan"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/M = locateUID(href_list["remorgan"]) - if(!istype(M)) - to_chat(usr, "This can only be done to instances of type /mob/living/carbon") - return - - var/obj/item/organ/internal/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in M.internal_organs - - if(!M) - to_chat(usr, "Mob doesn't exist anymore") - return - - if(!(rem_organ in M.internal_organs)) - to_chat(usr, "Mob does not have that organ.") - return - - to_chat(usr, "Removed [rem_organ] from [M].") - rem_organ.remove(M) - message_admins("[key_name_admin(usr)] has removed the organ [rem_organ] from [key_name_admin(M)]") - log_admin("[key_name(usr)] has removed the organ [rem_organ] from [key_name(M)]") - qdel(rem_organ) - - else if(href_list["fix_nano"]) - if(!check_rights(R_DEBUG)) return - - var/mob/H = locateUID(href_list["fix_nano"]) - - if(!istype(H) || !H.client) - to_chat(usr, "This can only be done on mobs with clients") - return - - H.client.reload_nanoui_resources() - - to_chat(usr, "Resource files sent") - to_chat(H, "Your NanoUI Resource files have been refreshed") - - log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)]") - - else if(href_list["regenerateicons"]) - if(!check_rights(0)) return - - var/mob/M = locateUID(href_list["regenerateicons"]) - if(!ismob(M)) - to_chat(usr, "This can only be done to instances of type /mob") - return - M.regenerate_icons() - - else if(href_list["adjustDamage"] && href_list["mobToDamage"]) - if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return - - var/mob/living/L = locateUID(href_list["mobToDamage"]) - if(!istype(L)) return - - var/Text = href_list["adjustDamage"] - - var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num - - if(!L) - to_chat(usr, "Mob doesn't exist anymore") - return - - switch(Text) - if("brute") - if(ishuman(L)) - var/mob/living/carbon/human/H = L - H.adjustBruteLoss(amount, robotic = TRUE) - else - L.adjustBruteLoss(amount) - if("fire") - if(ishuman(L)) - var/mob/living/carbon/human/H = L - H.adjustFireLoss(amount, robotic = TRUE) - else - L.adjustFireLoss(amount) - if("toxin") - L.adjustToxLoss(amount) - if("oxygen") - L.adjustOxyLoss(amount) - if("brain") - L.adjustBrainLoss(amount) - if("clone") - L.adjustCloneLoss(amount) - if("stamina") - L.adjustStaminaLoss(amount) - else - to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") - return - - if(amount != 0) - log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]") - message_admins("[key_name_admin(usr)] dealt [amount] amount of [Text] damage to [L]") - href_list["datumrefresh"] = href_list["mobToDamage"] - - else if(href_list["traitmod"]) - if(!check_rights(NONE)) - return - var/datum/A = locateUID(href_list["traitmod"]) - if(!istype(A)) - return - holder.modify_traits(A) - - if(href_list["datumrefresh"]) - var/datum/DAT = locateUID(href_list["datumrefresh"]) - if(!istype(DAT, /datum) && !isclient(DAT)) - return - src.debug_variables(DAT) - - if(href_list["copyoutfit"]) - if(!check_rights(R_EVENT)) - return - var/mob/living/carbon/human/H = locateUID(href_list["copyoutfit"]) - if(istype(H)) - H.copy_outfit() - -/client/proc/view_var_Topic_list(href, href_list, hsrc) - if(href_list["VarsList"]) - debug_variables(locate(href_list["VarsList"])) - return TRUE - - if(href_list["listedit"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if(!index) - return TRUE - - var/list/L = locate(href_list["listedit"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) - return TRUE - - if(href_list["listchange"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if(!index) - return TRUE - - var/list/L = locate(href_list["listchange"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) - return TRUE - - if(href_list["listremove"] && href_list["index"]) - var/index = text2num(href_list["index"]) - if(!index) - return TRUE - - var/list/L = locate(href_list["listremove"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return - - var/variable = L[index] - var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") - if(prompt != "Yes") - return - L.Cut(index, index+1) - log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") - log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") - message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") - return TRUE - - if(href_list["listadd"]) - var/list/L = locate(href_list["listadd"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - mod_list_add(L, null, "list", "contents") - return TRUE - - if(href_list["listdupes"]) - var/list/L = locate(href_list["listdupes"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - uniqueList_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") - log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") - return TRUE - - if(href_list["listnulls"]) - var/list/L = locate(href_list["listnulls"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - listclearnulls(L) - log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") - log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") - return TRUE - - if(href_list["listlen"]) - var/list/L = locate(href_list["listlen"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - var/value = vv_get_value(VV_NUM) - if(value["class"] != VV_NUM) - return TRUE - - L.len = value["value"] - log_world("### ListVarEdit by [src]: /list len: [L.len]") - log_admin("[key_name(src)] modified list's len: [L.len]") - message_admins("[key_name_admin(src)] modified list's len: [L.len]") - return TRUE - - if(href_list["listshuffle"]) - var/list/L = locate(href_list["listshuffle"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /list") - return TRUE - - shuffle_inplace(L) - log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") - log_admin("[key_name(src)] modified list's contents: SHUFFLE") - message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") - return TRUE - - if(href_list["listrefresh"]) - debug_variables(locate(href_list["listrefresh"])) - return TRUE +// reference: /client/proc/modify_variables(var/atom/O, var/param_var_name = null, var/autodetect_class = 0) + +/datum/proc/can_vv_get(var_name) + return TRUE + +// /client/proc/can_vv_get(var_name) +// return TRUE + +/datum/proc/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if("vars") + return FALSE + if("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + + . = TRUE + + +/client/vv_edit_var(var_name, var_value) //called whenever a var is edited + switch(var_name) + if("vars") + return FALSE + if("var_edited") + return FALSE + var_edited = TRUE + vars[var_name] = var_value + + . = TRUE + +/datum/proc/vv_get_var(var_name) + switch(var_name) + if("attack_log_old", "debug_log") + return debug_variable(var_name, vars[var_name], 0, src, sanitize = FALSE) + if("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +/client/vv_get_var(var_name) + switch(var_name) + if("vars") + return debug_variable(var_name, list(), 0, src) + return debug_variable(var_name, vars[var_name], 0, src) + +/datum/proc/can_vv_delete() + return TRUE + +//please call . = ..() first and append to the result, that way parent items are always at the top and child items are further down +//add seperaters by doing . += "---" +/datum/proc/vv_get_dropdown() + . = list() + . += "---" + .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" + .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" + .["Jump to Object"] = "?_src_=vars;jump_to=[UID()]" + .["Delete"] = "?_src_=vars;delete=[UID()]" + .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" + . += "---" + +/client/vv_get_dropdown() + . = list() + . += "---" + .["Call Proc"] = "?_src_=vars;proc_call=[UID()]" + .["Mark Object"] = "?_src_=vars;mark_object=[UID()]" + .["Delete"] = "?_src_=vars;delete=[UID()]" + .["Modify Traits"] = "?_src_=vars;traitmod=[UID()]" + . += "---" + +/client/proc/debug_variables(datum/D in world) + set category = "Debug" + set name = "View Variables" + + var/static/cookieoffset = rand(1, 9999) //to force cookies to reset after the round. + + if(!is_admin(usr)) + to_chat(usr, "You need to be an administrator to access this.") + return + + if(!D) + return + + + var/islist = islist(D) + var/isclient = isclient(D) + if(!islist && !isclient && !istype(D)) + return + + var/title = "" + var/icon/sprite + var/hash + var/refid + + if(!islist) + refid = "[D.UID()]" + else + refid = "\ref[D]" + + var/type = /list + if(!islist) + type = D.type + + if(isatom(D)) + var/atom/A = D + if(A.icon && A.icon_state) + sprite = new /icon(A.icon, A.icon_state) + hash = md5(A.icon) + hash = md5(hash + A.icon_state) + usr << browse_rsc(sprite, "vv[hash].png") + + + var/sprite_text + if(sprite) + sprite_text = "" + + + var/list/atomsnowflake = list() + if(isatom(D)) + var/atom/A = D + if(isliving(A)) + var/mob/living/L = A + atomsnowflake += "[L]" + if(L.dir) + atomsnowflake += "
    << [dir2text(L.dir)] >>" + atomsnowflake += {" +
    [L.ckey ? L.ckey : "No ckey"] / [L.real_name ? L.real_name : "No real name"] +
    + BRUTE:[L.getBruteLoss()] + FIRE:[L.getFireLoss()] + TOXIN:[L.getToxLoss()] + OXY:[L.getOxyLoss()] + CLONE:[L.getCloneLoss()] + BRAIN:[L.getBrainLoss()] + STAMINA:[L.getStaminaLoss()] + + "} + else + atomsnowflake += "[A]" + if(A.dir) + atomsnowflake += "
    << [dir2text(A.dir)] >>" + else + atomsnowflake += "[D]" + + + var/formatted_type = "[type]" + if(length(formatted_type) > 25) + var/middle_point = length(formatted_type) / 2 + var/splitpoint = findtext(formatted_type, "/", middle_point) + if(splitpoint) + formatted_type = "[copytext(formatted_type, 1, splitpoint)]
    [copytext(formatted_type, splitpoint)]" + else + formatted_type = "Type too long" //No suitable splitpoint (/) found. + + + var/marked + if(holder.marked_datum && holder.marked_datum == D) + marked = "
    Marked Object" + + + var/varedited_line = "" + if(isatom(D)) + var/atom/A = D + if(A.admin_spawned) + varedited_line += "
    Admin Spawned" + + + if(!islist && D.var_edited) + varedited_line += "
    Var Edited" + + + var/dropdownoptions = list() + if(islist) + dropdownoptions = list( + "---", + "Add Item" = "?_src_=vars;listadd=[refid]", + "Remove Nulls" = "?_src_=vars;listnulls=[refid]", + "Remove Dupes" = "?_src_=vars;listdupes=[refid]", + "Set len" = "?_src_=vars;listlen=[refid]", + "Shuffle" = "?_src_=vars;listshuffle=[refid]" + ) + else + dropdownoptions = D.vv_get_dropdown() + + + var/list/dropdownoptions_html = list() + for(var/name in dropdownoptions) + var/link = dropdownoptions[name] + if(link) + dropdownoptions_html += "" + else + dropdownoptions_html += "" + + + var/list/names = list() + if(!islist) + for(var/V in D.vars) + names += V + + + sleep(1) // Without a sleep here, VV sometimes disconnects clients + + + var/list/variable_html = list() + if(islist) + var/list/L = D + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + variable_html += debug_variable(i, value, 0, D) + else + names = sortList(names) + for(var/V in names) + if(D.can_vv_get(V)) + variable_html += D.vv_get_var(V) + + var/html = {" + + + [title] + + + + +
    + + + + + +
    + + + + +
    + [sprite_text] +
    + [atomsnowflake.Join()] +
    +
    +
    + [formatted_type] + [marked] + [varedited_line] +
    +
    +
    + Refresh +
    + +
    +
    +
    +
    +
    + + E - Edit, tries to determine the variable type by itself.
    + C - Change, asks you for the var type first.
    + M - Mass modify: changes this variable for all objects of this type.
    +
    +
    + + + + + +
    +
    + Search: +
    +
    + +
    +
    +
      + [variable_html.Join()] +
    + + + + "} + + usr << browse(html, "window=variables[refid];size=475x650") + +#define VV_HTML_ENCODE(thing) ( sanitize ? html_encode(thing) : thing ) +/proc/debug_variable(name, value, level, var/datum/DA = null, sanitize = TRUE) + var/header + if(DA) + if(islist(DA)) + var/index = name + if(value) + name = DA[name] // name is really the index until this line + else + value = DA[name] + header = "
  • (E) (C) (-) " + else + header = "
  • (E) (C) (M) " + else + header = "
  • " + + var/item + if(isnull(value)) + item = "[VV_HTML_ENCODE(name)] = null" + + else if(istext(value)) + item = "[VV_HTML_ENCODE(name)] = \"[VV_HTML_ENCODE(value)]\"" + + else if(isicon(value)) + #ifdef VARSICON + item = "[name] = /icon ([value]) [bicon(value, use_class=0)]" + #else + item = "[name] = /icon ([value])" + #endif + + else if(istype(value, /image)) + var/image/I = value + #ifdef VARSICON + item = "[name] \ref[value] = /image ([value]) [bicon(value, use_class=0)]" + #else + item = "[name] \ref[value] = /image ([value])" + #endif + + else if(isfile(value)) + item = "[VV_HTML_ENCODE(name)] = '[value]'" + + else if(istype(value, /datum)) + var/datum/D = value + item = "[VV_HTML_ENCODE(name)] \ref[value] = [D.type]" + + else if(istype(value, /client)) + var/client/C = value + item = "[VV_HTML_ENCODE(name)] \ref[value] = [C] [C.type]" +// + else if(islist(value)) + var/list/L = value + var/list/items = list() + + if(L.len > 0 && !(name == "underlays" || name == "overlays" || name == "vars" || L.len > (IS_NORMAL_LIST(L) ? 250 : 300))) + for(var/i in 1 to L.len) + var/key = L[i] + var/val + if(IS_NORMAL_LIST(L) && !isnum(key)) + val = L[key] + if(!val) + val = key + key = i + + items += debug_variable(key, val, level + 1, sanitize = sanitize) + + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])
      [items.Join()]
    " + + else + item = "[VV_HTML_ENCODE(name)] = /list ([L.len])" + + else + item = "[VV_HTML_ENCODE(name)] = [VV_HTML_ENCODE(value)]" + + return "[header][item]
  • " + +#undef VV_HTML_ENCODE + +/client/proc/view_var_Topic(href, href_list, hsrc) + //This should all be moved over to datum/admins/Topic() or something ~Carn + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(view_var_Topic_list(href, href_list, hsrc)) // done because you can't use UIDs with lists and I don't want to snowflake into the below check to supress warnings + return + + // Correct and warn about any VV topic links that aren't using UIDs + for(var/paramname in href_list) + if(findtext(href_list[paramname], "]_")) + continue // Contains UID-specific formatting, skip it + var/datum/D = locate(href_list[paramname]) + if(!D) + continue + var/datuminfo = "[D]" + if(istype(D)) + datuminfo = datum_info_line(D) + href_list[paramname] = D.UID() + else if(isclient(D)) + var/client/C = D + href_list[paramname] = C.UID() + log_runtime(EXCEPTION("Found \\ref-based '[paramname]' param in VV topic for [datuminfo], should be UID: [href]")) + + if(href_list["Vars"]) + debug_variables(locateUID(href_list["Vars"])) + + //~CARN: for renaming mobs (updates their name, real_name, mind.name, their ID/PDA and datacore records). + else if(href_list["rename"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["rename"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/new_name = reject_bad_name(sanitize(copytext(input(usr, "What would you like to name this mob?", "Input a name", M.real_name) as text|null, 1, MAX_NAME_LEN)), allow_numbers = TRUE) + if( !new_name || !M ) return + + message_admins("Admin [key_name_admin(usr)] renamed [key_name_admin(M)] to [new_name].") + M.rename_character(M.real_name, new_name) + href_list["datumrefresh"] = href_list["rename"] + + else if(href_list["varnameedit"] && href_list["datumedit"]) + if(!check_rights(R_VAREDIT)) return + + var/D = locateUID(href_list["datumedit"]) + if(!istype(D,/datum) && !istype(D,/client)) + to_chat(usr, "This can only be used on instances of types /client or /datum") + return + + modify_variables(D, href_list["varnameedit"], 1) + + else if(href_list["togbit"]) + if(!check_rights(R_VAREDIT)) return + + var/atom/D = locateUID(href_list["subject"]) + if(!istype(D,/datum) && !istype(D,/client)) + to_chat(usr, "This can only be used on instances of types /client or /datum") + return + if(!(href_list["var"] in D.vars)) + to_chat(usr, "Unable to find variable specified.") + return + var/value = D.vars[href_list["var"]] + value ^= 1 << text2num(href_list["togbit"]) + + D.vars[href_list["var"]] = value + + else if(href_list["varnamechange"] && href_list["datumchange"]) + if(!check_rights(R_VAREDIT)) return + + var/D = locateUID(href_list["datumchange"]) + if(!istype(D,/datum) && !istype(D,/client)) + to_chat(usr, "This can only be used on instances of types /client or /datum") + return + + modify_variables(D, href_list["varnamechange"], 0) + + else if(href_list["varnamemass"] && href_list["datummass"]) + if(!check_rights(R_VAREDIT)) return + + var/atom/A = locateUID(href_list["datummass"]) + if(!istype(A)) + to_chat(usr, "This can only be used on instances of type /atom") + return + + cmd_mass_modify_object_variables(A, href_list["varnamemass"]) + + + else if(href_list["mob_player_panel"]) + if(!check_rights(R_ADMIN|R_MOD)) return + + var/mob/M = locateUID(href_list["mob_player_panel"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.holder.show_player_panel(M) + href_list["datumrefresh"] = href_list["mob_player_panel"] + + else if(href_list["give_spell"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/M = locateUID(href_list["give_spell"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.give_spell(M) + href_list["datumrefresh"] = href_list["give_spell"] + + else if(href_list["givemartialart"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/living/carbon/C = locateUID(href_list["givemartialart"]) + if(!istype(C)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/list/artpaths = subtypesof(/datum/martial_art) + var/list/artnames = list() + for(var/i in artpaths) + var/datum/martial_art/M = i + artnames[initial(M.name)] = M + + var/result = input(usr, "Choose the martial art to teach", "JUDO CHOP") as null|anything in artnames + if(!usr) + return + if(QDELETED(C)) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(result) + var/chosenart = artnames[result] + var/datum/martial_art/MA = new chosenart + MA.teach(C) + + href_list["datumrefresh"] = href_list["givemartialart"] + + else if(href_list["give_disease"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/M = locateUID(href_list["give_disease"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.give_disease(M) + href_list["datumrefresh"] = href_list["give_spell"] + + else if(href_list["godmode"]) + if(!check_rights(R_REJUVINATE)) return + + var/mob/M = locateUID(href_list["godmode"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.cmd_admin_godmode(M) + href_list["datumrefresh"] = href_list["godmode"] + + else if(href_list["gib"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/M = locateUID(href_list["gib"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + src.cmd_admin_gib(M) + + else if(href_list["build_mode"]) + if(!check_rights(R_BUILDMODE)) return + + var/mob/M = locateUID(href_list["build_mode"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + togglebuildmode(M) + href_list["datumrefresh"] = href_list["build_mode"] + + else if(href_list["drop_everything"]) + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/mob/M = locateUID(href_list["drop_everything"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(usr.client) + usr.client.cmd_admin_drop_everything(M) + + else if(href_list["direct_control"]) + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/mob/M = locateUID(href_list["direct_control"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(usr.client) + usr.client.cmd_assume_direct_control(M) + + else if(href_list["make_skeleton"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/living/carbon/human/H = locateUID(href_list["make_skeleton"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + var/confirm = alert("Are you sure you want to turn this mob into a skeleton?","Confirm Skeleton Transformation","Yes","No") + if(confirm != "Yes") + return + + H.makeSkeleton() + message_admins("[key_name(usr)] has turned [key_name(H)] into a skeleton") + log_admin("[key_name_admin(usr)] has turned [key_name_admin(H)] into a skeleton") + href_list["datumrefresh"] = href_list["make_skeleton"] + + else if(href_list["offer_control"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["offer_control"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + offer_control(M) + + else if(href_list["delete"]) + if(!check_rights(R_DEBUG, 0)) + return + + var/datum/D = locateUID(href_list["delete"]) + if(!D) + to_chat(usr, "Unable to locate item!") + admin_delete(D) + href_list["datumrefresh"] = href_list["delete"] + + else if(href_list["delall"]) + if(!check_rights(R_DEBUG|R_SERVER)) return + + var/obj/O = locateUID(href_list["delall"]) + if(!isobj(O)) + to_chat(usr, "This can only be used on instances of type /obj") + return + + var/action_type = alert("Strict type ([O.type]) or type and all subtypes?",,"Strict type","Type and subtypes","Cancel") + if(action_type == "Cancel" || !action_type) + return + + if(alert("Are you really sure you want to delete all objects of type [O.type]?",,"Yes","No") != "Yes") + return + + if(alert("Second confirmation required. Delete?",,"Yes","No") != "Yes") + return + + var/O_type = O.type + switch(action_type) + if("Strict type") + var/i = 0 + for(var/obj/Obj in world) + if(Obj.type == O_type) + i++ + qdel(Obj) + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type [O_type] ([i] objects deleted)") + message_admins("[key_name_admin(usr)] deleted all objects of type [O_type] ([i] objects deleted)") + if("Type and subtypes") + var/i = 0 + for(var/obj/Obj in world) + if(istype(Obj,O_type)) + i++ + qdel(Obj) + if(!i) + to_chat(usr, "No objects of this type exist") + return + log_admin("[key_name(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") + message_admins("[key_name_admin(usr)] deleted all objects of type or subtype of [O_type] ([i] objects deleted)") + + else if(href_list["makespeedy"]) + if(!check_rights(R_DEBUG|R_ADMIN)) + return + var/obj/A = locateUID(href_list["makespeedy"]) + if(!istype(A)) + return + A.var_edited = TRUE + A.makeSpeedProcess() + log_admin("[key_name(usr)] has made [A] speed process") + message_admins("[key_name(usr)] has made [A] speed process") + return TRUE + + else if(href_list["makenormalspeed"]) + if(!check_rights(R_DEBUG|R_ADMIN)) + return + var/obj/A = locateUID(href_list["makenormalspeed"]) + if(!istype(A)) + return + A.var_edited = TRUE + A.makeNormalProcess() + log_admin("[key_name(usr)] has made [A] process normally") + message_admins("[key_name(usr)] has made [A] process normally") + return TRUE + + else if(href_list["addreagent"]) /* Made on /TG/, credit to them. */ + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/atom/A = locateUID(href_list["addreagent"]) + + if(!A.reagents) + var/amount = input(usr, "Specify the reagent size of [A]", "Set Reagent Size", 50) as num + if(amount) + A.create_reagents(amount) + + if(A.reagents) + var/chosen_id + var/list/reagent_options = sortAssoc(GLOB.chemical_reagents_list) + switch(alert(usr, "Choose a method.", "Add Reagents", "Enter ID", "Choose ID")) + if("Enter ID") + var/valid_id + while(!valid_id) + chosen_id = stripped_input(usr, "Enter the ID of the reagent you want to add.") + if(!chosen_id) //Get me out of here! + break + for(var/ID in reagent_options) + if(ID == chosen_id) + valid_id = 1 + if(!valid_id) + to_chat(usr, "A reagent with that ID doesn't exist!") + if("Choose ID") + chosen_id = input(usr, "Choose a reagent to add.", "Choose a reagent.") as null|anything in reagent_options + if(chosen_id) + var/amount = input(usr, "Choose the amount to add.", "Choose the amount.", A.reagents.maximum_volume) as num + if(amount) + A.reagents.add_reagent(chosen_id, amount) + log_admin("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") + message_admins("[key_name(usr)] has added [amount] units of [chosen_id] to \the [A]") + + else if(href_list["explode"]) + if(!check_rights(R_DEBUG|R_EVENT)) return + + var/atom/A = locateUID(href_list["explode"]) + if(!isobj(A) && !ismob(A) && !isturf(A)) + to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") + return + + src.cmd_admin_explosion(A) + href_list["datumrefresh"] = href_list["explode"] + + else if(href_list["emp"]) + if(!check_rights(R_DEBUG|R_EVENT)) return + + var/atom/A = locateUID(href_list["emp"]) + if(!isobj(A) && !ismob(A) && !isturf(A)) + to_chat(usr, "This can only be done to instances of type /obj, /mob and /turf") + return + + src.cmd_admin_emp(A) + href_list["datumrefresh"] = href_list["emp"] + + else if(href_list["mark_object"]) + if(!check_rights(0)) return + + var/datum/D = locateUID(href_list["mark_object"]) + if(!istype(D)) + to_chat(usr, "This can only be done to instances of type /datum") + return + + src.holder.marked_datum = D + href_list["datumrefresh"] = href_list["mark_object"] + + else if(href_list["proc_call"]) + if(!check_rights(R_PROCCALL)) + return + + var/T = locateUID(href_list["proc_call"]) + + if(T) + callproc_datum(T) + + else if(href_list["jump_to"]) + if(!check_rights(R_ADMIN)) + return + + var/atom/A = locateUID(href_list["jump_to"]) + var/turf/T = get_turf(A) + if(T) + usr.client.jumptoturf(T) + href_list["datumrefresh"] = href_list["jump_to"] + + + else if(href_list["rotatedatum"]) + if(!check_rights(R_DEBUG|R_ADMIN)) return + + var/atom/A = locateUID(href_list["rotatedatum"]) + if(!istype(A)) + to_chat(usr, "This can only be done to instances of type /atom") + return + + switch(href_list["rotatedir"]) + if("right") A.dir = turn(A.dir, -45) + if("left") A.dir = turn(A.dir, 45) + + message_admins("[key_name_admin(usr)] has rotated \the [A]") + log_admin("[key_name(usr)] has rotated \the [A]") + href_list["datumrefresh"] = href_list["rotatedatum"] + + else if(href_list["makemonkey"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makemonkey"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("monkeyone"=href_list["makemonkey"])) + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makerobot"=href_list["makerobot"])) + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makealien"=href_list["makealien"])) + + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makeslime"=href_list["makeslime"])) + + else if(href_list["makesuper"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makesuper"=href_list["makesuper"])) + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + if(alert("Confirm mob type change?",,"Transform","Cancel") != "Transform") return + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + holder.Topic(href, list("makeai"=href_list["makeai"])) + + else if(href_list["setspecies"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["setspecies"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon/human") + return + + var/new_species = input("Please choose a new species.","Species",null) as null|anything in GLOB.all_species + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + var/datum/species/S = GLOB.all_species[new_species] + if(H.set_species(S.type)) + to_chat(usr, "Set species of [H] to [H.dna.species].") + H.regenerate_icons() + message_admins("[key_name_admin(usr)] has changed the species of [key_name_admin(H)] to [new_species]") + log_admin("[key_name(usr)] has changed the species of [key_name(H)] to [new_species]") + else + to_chat(usr, "Failed! Something went wrong.") + + else if(href_list["addlanguage"]) + if(!check_rights(R_SPAWN)) return + + var/mob/H = locateUID(href_list["addlanguage"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob") + return + + var/new_language = input("Please choose a language to add.","Language",null) as null|anything in GLOB.all_languages + + if(!new_language) + return + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(H.add_language(new_language)) + to_chat(usr, "Added [new_language] to [H].") + message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the language [new_language]") + log_admin("[key_name(usr)] has given [key_name(H)] the language [new_language]") + else + to_chat(usr, "Mob already knows that language.") + + else if(href_list["remlanguage"]) + if(!check_rights(R_SPAWN)) return + + var/mob/H = locateUID(href_list["remlanguage"]) + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob") + return + + if(!H.languages.len) + to_chat(usr, "This mob knows no languages.") + return + + var/datum/language/rem_language = input("Please choose a language to remove.","Language",null) as null|anything in H.languages + + if(!rem_language) + return + + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(H.remove_language(rem_language.name)) + to_chat(usr, "Removed [rem_language] from [H].") + message_admins("[key_name_admin(usr)] has removed language [rem_language] from [key_name_admin(H)]") + log_admin("[key_name(usr)] has removed language [rem_language] from [key_name(H)]") + else + to_chat(usr, "Mob doesn't know that language.") + + else if(href_list["addverb"]) + if(!check_rights(R_DEBUG)) return + + var/mob/living/H = locateUID(href_list["addverb"]) + + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob/living") + return + var/list/possibleverbs = list() + possibleverbs += "Cancel" // One for the top... + possibleverbs += typesof(/mob/proc,/mob/verb,/mob/living/proc,/mob/living/verb) + switch(H.type) + if(/mob/living/carbon/human) + possibleverbs += typesof(/mob/living/carbon/proc,/mob/living/carbon/verb,/mob/living/carbon/human/verb,/mob/living/carbon/human/proc) + if(/mob/living/silicon/robot) + possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/robot/proc,/mob/living/silicon/robot/verb) + if(/mob/living/silicon/ai) + possibleverbs += typesof(/mob/living/silicon/proc,/mob/living/silicon/ai/proc,/mob/living/silicon/ai/verb) + possibleverbs -= H.verbs + possibleverbs += "Cancel" // ...And one for the bottom + + var/verb = input("Select a verb!", "Verbs",null) as anything in possibleverbs + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + if(!verb || verb == "Cancel") + return + else + H.verbs += verb + message_admins("[key_name_admin(usr)] has given [key_name_admin(H)] the verb [verb]") + log_admin("[key_name(usr)] has given [key_name(H)] the verb [verb]") + + else if(href_list["remverb"]) + if(!check_rights(R_DEBUG)) return + + var/mob/H = locateUID(href_list["remverb"]) + + if(!istype(H)) + to_chat(usr, "This can only be done to instances of type /mob") + return + var/verb = input("Please choose a verb to remove.","Verbs",null) as null|anything in H.verbs + if(!H) + to_chat(usr, "Mob doesn't exist anymore") + return + if(!verb) + return + else + H.verbs -= verb + message_admins("[key_name_admin(usr)] has removed verb [verb] from [key_name_admin(H)]") + log_admin("[key_name(usr)] has removed verb [verb] from [key_name(H)]") + + else if(href_list["addorgan"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/M = locateUID(href_list["addorgan"]) + if(!istype(M)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/new_organ = input("Please choose an organ to add.","Organ",null) as null|anything in subtypesof(/obj/item/organ)-/obj/item/organ + if(!new_organ) return + + if(!M) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(locateUID(new_organ) in M.internal_organs) + to_chat(usr, "Mob already has that organ.") + return + var/obj/item/organ/internal/organ = new new_organ + organ.insert(M) + message_admins("[key_name_admin(usr)] has given [key_name_admin(M)] the organ [new_organ]") + log_admin("[key_name(usr)] has given [key_name(M)] the organ [new_organ]") + + else if(href_list["remorgan"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/M = locateUID(href_list["remorgan"]) + if(!istype(M)) + to_chat(usr, "This can only be done to instances of type /mob/living/carbon") + return + + var/obj/item/organ/internal/rem_organ = input("Please choose an organ to remove.","Organ",null) as null|anything in M.internal_organs + + if(!M) + to_chat(usr, "Mob doesn't exist anymore") + return + + if(!(rem_organ in M.internal_organs)) + to_chat(usr, "Mob does not have that organ.") + return + + to_chat(usr, "Removed [rem_organ] from [M].") + rem_organ.remove(M) + message_admins("[key_name_admin(usr)] has removed the organ [rem_organ] from [key_name_admin(M)]") + log_admin("[key_name(usr)] has removed the organ [rem_organ] from [key_name(M)]") + qdel(rem_organ) + + else if(href_list["fix_nano"]) + if(!check_rights(R_DEBUG)) return + + var/mob/H = locateUID(href_list["fix_nano"]) + + if(!istype(H) || !H.client) + to_chat(usr, "This can only be done on mobs with clients") + return + + H.client.reload_nanoui_resources() + + to_chat(usr, "Resource files sent") + to_chat(H, "Your NanoUI Resource files have been refreshed") + + log_admin("[key_name(usr)] resent the NanoUI resource files to [key_name(H)]") + + else if(href_list["regenerateicons"]) + if(!check_rights(0)) return + + var/mob/M = locateUID(href_list["regenerateicons"]) + if(!ismob(M)) + to_chat(usr, "This can only be done to instances of type /mob") + return + M.regenerate_icons() + + else if(href_list["adjustDamage"] && href_list["mobToDamage"]) + if(!check_rights(R_DEBUG|R_ADMIN|R_EVENT)) return + + var/mob/living/L = locateUID(href_list["mobToDamage"]) + if(!istype(L)) return + + var/Text = href_list["adjustDamage"] + + var/amount = input("Deal how much damage to mob? (Negative values here heal)","Adjust [Text]loss",0) as num + + if(!L) + to_chat(usr, "Mob doesn't exist anymore") + return + + switch(Text) + if("brute") + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.adjustBruteLoss(amount, robotic = TRUE) + else + L.adjustBruteLoss(amount) + if("fire") + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.adjustFireLoss(amount, robotic = TRUE) + else + L.adjustFireLoss(amount) + if("toxin") + L.adjustToxLoss(amount) + if("oxygen") + L.adjustOxyLoss(amount) + if("brain") + L.adjustBrainLoss(amount) + if("clone") + L.adjustCloneLoss(amount) + if("stamina") + L.adjustStaminaLoss(amount) + else + to_chat(usr, "You caused an error. DEBUG: Text:[Text] Mob:[L]") + return + + if(amount != 0) + log_admin("[key_name(usr)] dealt [amount] amount of [Text] damage to [L]") + message_admins("[key_name_admin(usr)] dealt [amount] amount of [Text] damage to [L]") + href_list["datumrefresh"] = href_list["mobToDamage"] + + else if(href_list["traitmod"]) + if(!check_rights(NONE)) + return + var/datum/A = locateUID(href_list["traitmod"]) + if(!istype(A)) + return + holder.modify_traits(A) + + if(href_list["datumrefresh"]) + var/datum/DAT = locateUID(href_list["datumrefresh"]) + if(!istype(DAT, /datum) && !isclient(DAT)) + return + src.debug_variables(DAT) + + if(href_list["copyoutfit"]) + if(!check_rights(R_EVENT)) + return + var/mob/living/carbon/human/H = locateUID(href_list["copyoutfit"]) + if(istype(H)) + H.copy_outfit() + +/client/proc/view_var_Topic_list(href, href_list, hsrc) + if(href_list["VarsList"]) + debug_variables(locate(href_list["VarsList"])) + return TRUE + + if(href_list["listedit"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listedit"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = TRUE) + return TRUE + + if(href_list["listchange"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listchange"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + mod_list(L, null, "list", "contents", index, autodetect_class = FALSE) + return TRUE + + if(href_list["listremove"] && href_list["index"]) + var/index = text2num(href_list["index"]) + if(!index) + return TRUE + + var/list/L = locate(href_list["listremove"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return + + var/variable = L[index] + var/prompt = alert("Do you want to remove item number [index] from list?", "Confirm", "Yes", "No") + if(prompt != "Yes") + return + L.Cut(index, index+1) + log_world("### ListVarEdit by [src]: /list's contents: REMOVED=[html_encode("[variable]")]") + log_admin("[key_name(src)] modified list's contents: REMOVED=[variable]") + message_admins("[key_name_admin(src)] modified list's contents: REMOVED=[variable]") + return TRUE + + if(href_list["listadd"]) + var/list/L = locate(href_list["listadd"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + mod_list_add(L, null, "list", "contents") + return TRUE + + if(href_list["listdupes"]) + var/list/L = locate(href_list["listdupes"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + uniqueList_inplace(L) + log_world("### ListVarEdit by [src]: /list contents: CLEAR DUPES") + log_admin("[key_name(src)] modified list's contents: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR DUPES") + return TRUE + + if(href_list["listnulls"]) + var/list/L = locate(href_list["listnulls"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + listclearnulls(L) + log_world("### ListVarEdit by [src]: /list contents: CLEAR NULLS") + log_admin("[key_name(src)] modified list's contents: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified list's contents: CLEAR NULLS") + return TRUE + + if(href_list["listlen"]) + var/list/L = locate(href_list["listlen"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + var/value = vv_get_value(VV_NUM) + if(value["class"] != VV_NUM) + return TRUE + + L.len = value["value"] + log_world("### ListVarEdit by [src]: /list len: [L.len]") + log_admin("[key_name(src)] modified list's len: [L.len]") + message_admins("[key_name_admin(src)] modified list's len: [L.len]") + return TRUE + + if(href_list["listshuffle"]) + var/list/L = locate(href_list["listshuffle"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /list") + return TRUE + + shuffle_inplace(L) + log_world("### ListVarEdit by [src]: /list contents: SHUFFLE") + log_admin("[key_name(src)] modified list's contents: SHUFFLE") + message_admins("[key_name_admin(src)] modified list's contents: SHUFFLE") + return TRUE + + if(href_list["listrefresh"]) + debug_variables(locate(href_list["listrefresh"])) + return TRUE diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 84dde0f22e04..6b7c2d896062 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -29,7 +29,7 @@ #define BIOHAZARD "BIOHAZARD THREAT!" -var/list/diseases = subtypesof(/datum/disease) +GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) /datum/disease diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index e0711d88e5f8..5464bd9f012d 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -6,16 +6,15 @@ If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus. */ - -var/list/archive_diseases = list() +GLOBAL_LIST_EMPTY(archive_diseases) // The order goes from easy to cure to hard to cure. -var/list/advance_cures = list( +GLOBAL_LIST_INIT(advance_cures, list( "sodiumchloride", "sugar", "orangejuice", "spaceacillin", "salglu_solution", "ethanol", "teporone", "diphenhydramine", "lipolicide", "silver", "gold" - ) +)) /* @@ -132,7 +131,7 @@ var/list/advance_cures = list( // Generate symptoms. By default, we only choose non-deadly symptoms. var/list/possible_symptoms = list() - for(var/symp in list_symptoms) + for(var/symp in GLOB.list_symptoms) var/datum/symptom/S = new symp if(S.level >= level_min && S.level <= level_max) if(!HasSymptom(S)) @@ -158,13 +157,13 @@ var/list/advance_cures = list( AssignProperties(properties) id = null - if(!archive_diseases[GetDiseaseID()]) + if(!GLOB.archive_diseases[GetDiseaseID()]) if(new_name) AssignName() - archive_diseases[GetDiseaseID()] = src // So we don't infinite loop - archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) + GLOB.archive_diseases[GetDiseaseID()] = src // So we don't infinite loop + GLOB.archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) - var/datum/disease/advance/A = archive_diseases[GetDiseaseID()] + var/datum/disease/advance/A = GLOB.archive_diseases[GetDiseaseID()] AssignName(A.name) //Generate disease properties based on the effects. Returns an associated list. @@ -246,9 +245,9 @@ var/list/advance_cures = list( // Will generate a random cure, the less resistance the symptoms have, the harder the cure. /datum/disease/advance/proc/GenerateCure(list/properties = list()) if(properties && properties.len) - var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len) + var/res = Clamp(properties["resistance"] - (symptoms.len / 2), 1, GLOB.advance_cures.len) // to_chat(world, "Res = [res]") - cures = list(advance_cures[res]) + cures = list(GLOB.advance_cures[res]) // Get the cure name from the cure_id var/datum/reagent/D = GLOB.chemical_reagents_list[cures[1]] @@ -371,7 +370,7 @@ var/list/advance_cures = list( var/list/symptoms = list() symptoms += "Done" - symptoms += list_symptoms.Copy() + symptoms += GLOB.list_symptoms.Copy() do if(user) var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in symptoms diff --git a/code/datums/diseases/advance/presets.dm b/code/datums/diseases/advance/presets.dm index a641db2383fc..c1df095f9c26 100644 --- a/code/datums/diseases/advance/presets.dm +++ b/code/datums/diseases/advance/presets.dm @@ -48,4 +48,4 @@ if(!D) name = "Reality Enhancer" symptoms = list(new/datum/symptom/sensory_restoration) - ..(process, D, copy) \ No newline at end of file + ..(process, D, copy) diff --git a/code/datums/diseases/advance/symptoms/beard.dm b/code/datums/diseases/advance/symptoms/beard.dm index 2ce411367f8e..75bf2267ff91 100644 --- a/code/datums/diseases/advance/symptoms/beard.dm +++ b/code/datums/diseases/advance/symptoms/beard.dm @@ -47,4 +47,4 @@ BONUS if(!(head_organ.f_style == "Dwarf Beard") && !(head_organ.f_style == "Very Long Beard")) head_organ.f_style = pick("Dwarf Beard", "Very Long Beard") H.update_fhair() - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index 62e31105b924..f0a3eaa9ce00 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -50,4 +50,4 @@ Bonus /datum/symptom/choking/proc/Choke(mob/living/M, datum/disease/advance/A) var/get_damage = sqrtor0(21+A.totalStageSpeed()*0.5)+sqrtor0(16+A.totalStealth()*5) M.adjustOxyLoss(get_damage) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index 44f57ce0faca..5f495718ffff 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -37,4 +37,4 @@ BONUS var/obj/item/I = M.get_active_hand() if(I && I.w_class == 1) M.drop_item() - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm index c594298b11cf..920d6d15b49e 100644 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ b/code/datums/diseases/advance/symptoms/dizzy.dm @@ -35,4 +35,4 @@ Bonus else to_chat(M, "A wave of dizziness washes over you!") M.Dizzy(5) - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index b19d821b75f4..32c051d5a263 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -38,4 +38,4 @@ Bonus /datum/symptom/fever/proc/Heat(mob/living/M, datum/disease/advance/A) var/get_heat = (sqrtor0(21+A.totalTransmittable()*2))+(sqrtor0(20+A.totalStageSpeed()*3)) M.bodytemperature = min(M.bodytemperature + (get_heat * A.stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index 9cfdf8a9d34f..e7d68bebb92c 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -40,4 +40,4 @@ Bonus /datum/symptom/flesh_eating/proc/Flesheat(mob/living/M, datum/disease/advance/A) var/get_damage = ((sqrtor0(16-A.totalStealth()))*5) M.adjustBruteLoss(get_damage) - return 1 \ No newline at end of file + return 1 diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index 526ab93836fa..d1efc35fc987 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -31,4 +31,4 @@ BONUS if(prob(SYMPTOM_ACTIVATION_PROB)) var/mob/living/M = A.affected_mob to_chat(M, "[pick("Your head hurts.", "Your head starts pounding.")]") - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 8563520b09a0..f6689c3c464b 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -31,4 +31,4 @@ BONUS if(prob(SYMPTOM_ACTIVATION_PROB)) var/mob/living/M = A.affected_mob to_chat(M, "Your [pick("back", "arm", "leg", "elbow", "head")] itches.") - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index b1571aec62f4..cf39d91a8663 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -62,4 +62,4 @@ Bonus M.reagents.add_reagent("oculine", 20) else if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your healing feels more acute.")]") \ No newline at end of file + to_chat(M, "[pick("Your eyes feel great.","You feel like your eyes can focus more clearly.", "You don't feel the need to blink.","Your ears feel great.","Your healing feels more acute.")]") diff --git a/code/datums/diseases/advance/symptoms/shedding.dm b/code/datums/diseases/advance/symptoms/shedding.dm index e79b87d1d4b9..c3a645017595 100644 --- a/code/datums/diseases/advance/symptoms/shedding.dm +++ b/code/datums/diseases/advance/symptoms/shedding.dm @@ -47,4 +47,4 @@ BONUS head_organ.h_style = "Bald" H.update_hair() H.update_fhair() - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/skin.dm b/code/datums/diseases/advance/symptoms/skin.dm index 6916a46994a8..aa5a34945844 100644 --- a/code/datums/diseases/advance/symptoms/skin.dm +++ b/code/datums/diseases/advance/symptoms/skin.dm @@ -83,4 +83,4 @@ BONUS else H.visible_message("[H] looks a bit dark...", "Your skin suddenly appears darker...") - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 0a31a4acae66..b8af8f92a244 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -36,4 +36,4 @@ Bonus else M.emote("sneeze") A.spread(5) - return \ No newline at end of file + return diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index f2c5beee400a..8f72de7f5998 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -1,6 +1,6 @@ // Symptoms are the effects that engineered advanced diseases do. -var/list/list_symptoms = subtypesof(/datum/symptom) +GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) /datum/symptom // Buffs/Debuffs the symptom has to the overall engineered disease. @@ -17,7 +17,7 @@ var/list/list_symptoms = subtypesof(/datum/symptom) var/id = "" /datum/symptom/New() - var/list/S = list_symptoms + var/list/S = GLOB.list_symptoms for(var/i = 1; i <= S.len; i++) if(type == S[i]) id = "[i]" diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index aa47b1c7a2a0..252eaf16ecc6 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -47,4 +47,4 @@ Bonus M.BecomeNearsighted() if(prob(eyes.damage - 10 + 1)) if(M.BecomeBlind()) - to_chat(M, "You go blind!") \ No newline at end of file + to_chat(M, "You go blind!") diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index 98b940f2b837..732e56b69ed5 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -36,4 +36,4 @@ Bonus else to_chat(M, "[pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you...")]") M.overeatduration = max(M.overeatduration - 100, 0) - M.adjust_nutrition(-100) \ No newline at end of file + M.adjust_nutrition(-100) diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm index 56e3b66fab39..5165f7916f4f 100644 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -52,4 +52,4 @@ BONUS H.age = 21 to_chat(H, "You feel like you can take on the world!") - return \ No newline at end of file + return diff --git a/code/datums/diseases/berserker.dm b/code/datums/diseases/berserker.dm index 3efb4d9e06a5..81b3ca8cb729 100644 --- a/code/datums/diseases/berserker.dm +++ b/code/datums/diseases/berserker.dm @@ -52,4 +52,4 @@ M.adjustBruteLoss(damage) else playsound(affected_mob.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - affected_mob.visible_message("[affected_mob] fails to hit [M] with [affected_mob.p_their()] thrashing!") \ No newline at end of file + affected_mob.visible_message("[affected_mob] fails to hit [M] with [affected_mob.p_their()] thrashing!") diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index 975c92b2aef3..955ec568c776 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -63,4 +63,4 @@ if(!affected_mob.resistances.Find(/datum/disease/flu)) var/datum/disease/Flu = new /datum/disease/flu(0) affected_mob.ContractDisease(Flu) - cure() \ No newline at end of file + cure() diff --git a/code/datums/diseases/critical.dm b/code/datums/diseases/critical.dm index 37d77687d8ca..8dc910e3baf0 100644 --- a/code/datums/diseases/critical.dm +++ b/code/datums/diseases/critical.dm @@ -6,12 +6,17 @@ if(prob(stage_prob)) stage = min(stage + 1, max_stages) + if(has_cure()) + cure() + return FALSE + return TRUE + +/datum/disease/critical/has_cure() for(var/C_id in cures) if(affected_mob.reagents.has_reagent(C_id)) if(prob(cure_chance)) - cure() - return FALSE - return TRUE + return TRUE + return FALSE /datum/disease/critical/shock name = "Shock" @@ -31,7 +36,7 @@ /datum/disease/critical/shock/stage_act() if(..()) - if(affected_mob.health >= 25) + if(affected_mob.health >= 25 && affected_mob.nutrition >= NUTRITION_LEVEL_HYPOGLYCEMIA) to_chat(affected_mob, "You feel better.") cure() return @@ -80,8 +85,8 @@ spread_text = "The patient is having a cardiac emergency" max_stages = 3 spread_flags = SPECIAL - cure_text = "Atropine or Epinephrine" - cures = list("atropine", "epinephrine") + cure_text = "Atropine, Epinephrine, or Heparin" + cures = list("atropine", "epinephrine", "heparin") cure_chance = 10 needs_all_cures = FALSE viable_mobtypes = list(/mob/living/carbon/human) @@ -133,4 +138,65 @@ affected_mob.emote(pick("twitch", "gasp")) if(prob(5) && ishuman(affected_mob)) var/mob/living/carbon/human/H = affected_mob - H.set_heartattack(TRUE) \ No newline at end of file + H.set_heartattack(TRUE) + +/datum/disease/critical/hypoglycemia + name = "Hypoglycemia" + form = "Medical Emergency" + max_stages = 3 + spread_flags = SPECIAL + spread_text = "The patient has low blood sugar." + cure_text = "Eating or administration of vitamins or nutrients" + viable_mobtypes = list(/mob/living/carbon/human) + stage_prob = 1 + severity = DANGEROUS + disease_flags = CURABLE + bypasses_immunity = TRUE + virus_heal_resistant = TRUE + +/datum/disease/critical/hypoglycemia/has_cure() + if(ishuman(affected_mob)) + var/mob/living/carbon/human/H = affected_mob + if(NO_HUNGER in H.dna.species.species_traits) + return TRUE + if(ismachine(H)) + return TRUE + return ..() + +/datum/disease/critical/hypoglycemia/stage_act() + if(..()) + if(affected_mob.nutrition > NUTRITION_LEVEL_HYPOGLYCEMIA) + to_chat(affected_mob, "You feel a lot better!") + cure() + return + switch(stage) + if(1) + if(prob(4)) + to_chat(affected_mob, "You feel hungry!") + if(prob(2)) + to_chat(affected_mob, "You have a headache!") + if(prob(2)) + to_chat(affected_mob, "You feel [pick("anxious", "depressed")]!") + if(2) + if(prob(4)) + to_chat(affected_mob, "You feel like everything is wrong with your life!") + if(prob(5)) + affected_mob.Slowed(rand(4, 16)) + to_chat(affected_mob, "You feel [pick("tired", "exhausted", "sluggish")].") + if(prob(5)) + affected_mob.Weaken(6) + affected_mob.Stuttering(10) + to_chat(affected_mob, "You feel [pick("numb", "confused", "dizzy", "lightheaded")].") + affected_mob.emote("collapse") + if(3) + if(prob(8)) + var/datum/disease/D = new /datum/disease/critical/shock + affected_mob.ForceContractDisease(D) + if(prob(12)) + affected_mob.Weaken(6) + affected_mob.Stuttering(10) + to_chat(affected_mob, "You feel [pick("numb", "confused", "dizzy", "lightheaded")].") + affected_mob.emote("collapse") + if(prob(12)) + to_chat(affected_mob, "You feel [pick("tired", "exhausted", "sluggish")].") + affected_mob.Slowed(rand(4, 16)) \ No newline at end of file diff --git a/code/datums/diseases/food_poisoning.dm b/code/datums/diseases/food_poisoning.dm index 4df31ec57d96..a0e3792de0c1 100644 --- a/code/datums/diseases/food_poisoning.dm +++ b/code/datums/diseases/food_poisoning.dm @@ -68,4 +68,4 @@ to_chat(affected_mob, "Your stomach lurches painfully!") affected_mob.visible_message("[affected_mob] gags and retches!") affected_mob.Stun(rand(2,4)) - affected_mob.Weaken(rand(2,4)) \ No newline at end of file + affected_mob.Weaken(rand(2,4)) diff --git a/code/datums/diseases/kingstons.dm b/code/datums/diseases/kingstons.dm index c7db27968efc..733ee3cf0e75 100644 --- a/code/datums/diseases/kingstons.dm +++ b/code/datums/diseases/kingstons.dm @@ -93,4 +93,4 @@ twisted.visible_message("[twisted] scratches at thier skin!", \ "You scratch your skin to try not to itch!") twisted.adjustBruteLoss(-5) - twisted.adjustStaminaLoss(5) \ No newline at end of file + twisted.adjustStaminaLoss(5) diff --git a/code/datums/diseases/kuru.dm b/code/datums/diseases/kuru.dm index 5cb04ef5f858..f147bbd16124 100644 --- a/code/datums/diseases/kuru.dm +++ b/code/datums/diseases/kuru.dm @@ -52,4 +52,4 @@ affected_mob.drop_r_hand() affected_mob.Stun(10) affected_mob.Weaken(10) - affected_mob.visible_message("[affected_mob] laughs uncontrollably!") \ No newline at end of file + affected_mob.visible_message("[affected_mob] laughs uncontrollably!") diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm index 924ee01dd699..dd1d062cbf7e 100644 --- a/code/datums/diseases/magnitis.dm +++ b/code/datums/diseases/magnitis.dm @@ -60,4 +60,4 @@ var/iter = rand(1,3) for(i=0,iThere's not enough cable to finish the task.")) - return 0 - else - C.use(4) - playsound(holder, C.usesound, 50, 1) - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < 5) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(5) - else if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) - if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) - return 0 - return 1 - -/datum/construction/proc/check_all_steps(atom/used_atom,mob/user as mob) //check all steps, remove matching one. - for(var/i=1;i<=steps.len;i++) - var/list/L = steps[i]; - if(do_tool_or_atom_check(used_atom, L["key"]) && custom_action(i, used_atom, user)) - steps[i]=null;//stupid byond list from list removal... - listclearnulls(steps); - if(!steps.len) - spawn_result(user) - return 1 - return 0 - - -/datum/construction/proc/spawn_result(mob/user as mob) - if(result) - if(taskpath) - var/datum/job_objective/task = user.mind.findJobTask(taskpath) - if(istype(task)) - task.unit_completed() - - new result(get_turf(holder)) - spawn() - qdel(holder) - return - -/datum/construction/proc/set_desc(index as num) - var/list/step = steps[index] - holder.desc = step["desc"] - return - -/datum/construction/proc/try_consume(mob/user as mob, atom/used_atom, amount) - if(amount > 0) - // CABLES - if(istype(used_atom,/obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil=used_atom - if(!coil.use(amount)) - to_chat(user, "You don't have enough cable! You need at least [amount] coils.") - return 0 - // TOOLS - if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) - if(!I.use(amount)) - return 0 - // STACKS - if(istype(used_atom,/obj/item/stack)) - var/obj/item/stack/stack=used_atom - if(stack.amount < amount) - to_chat(user, "You don't have enough [stack]! You need at least [amount].") - return 0 - stack.use(amount) - return 1 - -/datum/construction/proc/do_tool_or_atom_check(used_atom, thing_to_check) //Checks if an atom is either a required thing; or if it's a required tool - if(istype(used_atom, thing_to_check)) - return TRUE - else if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour == thing_to_check) - return TRUE - -/datum/construction/reversible - var/index - -/datum/construction/reversible/New(atom) - ..() - index = steps.len - return - -/datum/construction/reversible/proc/update_index(diff as num, mob/user as mob) - index+=diff - if(index==0) - spawn_result(user) - else - set_desc(index) - return - -/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step - var/list/L = steps[index] - if(do_tool_or_atom_check(used_atom, L["key"])) - return FORWARD //to the first step -> forward - else if(L["backkey"] && do_tool_or_atom_check(used_atom, L["backkey"])) - return BACKWARD //to the last step -> backwards - return 0 - -/datum/construction/reversible/check_step(atom/used_atom,mob/user as mob) - var/diff = is_right_key(used_atom) - if(diff) - if(custom_action(index, diff, used_atom, user)) - update_index(diff, user) - return 1 - return 0 - -/datum/construction/reversible/custom_action(index, diff, used_atom, user) - if(!..(index,used_atom,user)) - return 0 - return 1 - -#define state_next "next" -#define state_prev "prev" - -/datum/construction/reversible2 - var/index - var/base_icon = "durand" - -/datum/construction/reversible2/New(atom) - ..() - index = 1 - return - -/datum/construction/reversible2/proc/update_index(diff as num, mob/user as mob) - index-=diff - if(index==steps.len+1) - spawn_result(user) - else - set_desc(index) - return - -/datum/construction/reversible2/proc/update_icon() - holder.icon_state="[base_icon]_[index]" - -/datum/construction/reversible2/is_right_key(mob/user as mob,atom/used_atom) // returns index step - var/list/state = steps[index] - if(state_next in state) - var/list/step = state[state_next] - if(do_tool_or_atom_check(used_atom, step["key"])) - //if(L["consume"] && !try_consume(used_atom,L["consume"])) - // return 0 - return FORWARD //to the first step -> forward - else if(state_prev in state) - var/list/step = state[state_prev] - if(do_tool_or_atom_check(used_atom, step["key"])) - //if(L["consume"] && !try_consume(used_atom,L["consume"])) - // return 0 - return BACKWARD //to the first step -> forward - return 0 - -/datum/construction/reversible2/check_step(atom/used_atom,mob/user as mob) - var/diff = is_right_key(user,used_atom) - if(diff) - if(custom_action(index, diff, used_atom, user)) - update_index(diff,user) - update_icon() - return 1 - return 0 - -/datum/construction/reversible2/proc/fixText(text,user) - text = replacetext(text,"{USER}","[user]") - text = replacetext(text,"{HOLDER}","[holder]") - return text - -/datum/construction/reversible2/custom_action(index, diff, used_atom, var/mob/user) - if(!..(index,used_atom,user)) - return 0 - - var/list/step = steps[index] - var/list/state = step[diff==FORWARD ? state_next : state_prev] - user.visible_message(fixText(state["vis_msg"],user),fixText(state["self_msg"],user)) - - if("delete" in state) - qdel(used_atom) - else if("spawn" in state) - var/spawntype=state["spawn"] - var/atom/A = new spawntype(holder.loc) - if("amount" in state) - if(istype(A,/obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C=A - C.amount=state["amount"] - if(istype(A,/obj/item/stack)) - var/obj/item/stack/S=A - S.amount=state["amount"] - - return 1 - -/datum/construction/reversible2/action(used_atom,user) - return check_step(used_atom,user) +#define FORWARD -1 +#define BACKWARD 1 +#define CONSTRUCTION_TOOL_BEHAVIOURS list(TOOL_CROWBAR, TOOL_SCREWDRIVER, TOOL_WELDER, TOOL_WRENCH) + +/datum/construction + var/list/steps + var/atom/holder + var/result + var/list/steps_desc + var/taskpath = null // Path of job objective completed. + +/datum/construction/New(atom) + ..() + holder = atom + if(!holder) //don't want this without a holder + spawn + qdel(src) + set_desc(steps.len) + return + +/datum/construction/proc/next_step(mob/user as mob) + steps.len-- + if(!steps.len) + spawn_result(user) + else + set_desc(steps.len) + return + +/datum/construction/proc/action(atom/used_atom,mob/user as mob) + return + +/datum/construction/proc/check_step(atom/used_atom,mob/user as mob) //check last step only + var/valid_step = is_right_key(used_atom) + if(valid_step) + if(custom_action(valid_step, used_atom, user)) + next_step(user) + return 1 + return 0 + +/datum/construction/proc/is_right_key(atom/used_atom) // returns current step num if used_atom is of the right type. + var/list/L = steps[steps.len] + if(do_tool_or_atom_check(used_atom, L["key"])) + return steps.len + return 0 + + +/datum/construction/proc/custom_action(step, used_atom, user) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.amount<4) + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else + C.use(4) + playsound(holder, C.usesound, 50, 1) + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < 5) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(5) + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) + return 0 + return 1 + +/datum/construction/proc/check_all_steps(atom/used_atom,mob/user as mob) //check all steps, remove matching one. + for(var/i=1;i<=steps.len;i++) + var/list/L = steps[i]; + if(do_tool_or_atom_check(used_atom, L["key"]) && custom_action(i, used_atom, user)) + steps[i]=null;//stupid byond list from list removal... + listclearnulls(steps); + if(!steps.len) + spawn_result(user) + return 1 + return 0 + + +/datum/construction/proc/spawn_result(mob/user as mob) + if(result) + if(taskpath) + var/datum/job_objective/task = user.mind.findJobTask(taskpath) + if(istype(task)) + task.unit_completed() + + new result(get_turf(holder)) + spawn() + qdel(holder) + return + +/datum/construction/proc/set_desc(index as num) + var/list/step = steps[index] + holder.desc = step["desc"] + return + +/datum/construction/proc/try_consume(mob/user as mob, atom/used_atom, amount) + if(amount > 0) + // CABLES + if(istype(used_atom,/obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil=used_atom + if(!coil.use(amount)) + to_chat(user, "You don't have enough cable! You need at least [amount] coils.") + return 0 + // TOOLS + if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use(amount)) + return 0 + // STACKS + if(istype(used_atom,/obj/item/stack)) + var/obj/item/stack/stack=used_atom + if(stack.amount < amount) + to_chat(user, "You don't have enough [stack]! You need at least [amount].") + return 0 + stack.use(amount) + return 1 + +/datum/construction/proc/do_tool_or_atom_check(used_atom, thing_to_check) //Checks if an atom is either a required thing; or if it's a required tool + if(istype(used_atom, thing_to_check)) + return TRUE + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour == thing_to_check) + return TRUE + +/datum/construction/reversible + var/index + +/datum/construction/reversible/New(atom) + ..() + index = steps.len + return + +/datum/construction/reversible/proc/update_index(diff as num, mob/user as mob) + index+=diff + if(index==0) + spawn_result(user) + else + set_desc(index) + return + +/datum/construction/reversible/is_right_key(atom/used_atom) // returns index step + var/list/L = steps[index] + if(do_tool_or_atom_check(used_atom, L["key"])) + return FORWARD //to the first step -> forward + else if(L["backkey"] && do_tool_or_atom_check(used_atom, L["backkey"])) + return BACKWARD //to the last step -> backwards + return 0 + +/datum/construction/reversible/check_step(atom/used_atom,mob/user as mob) + var/diff = is_right_key(used_atom) + if(diff) + if(custom_action(index, diff, used_atom, user)) + update_index(diff, user) + return 1 + return 0 + +/datum/construction/reversible/custom_action(index, diff, used_atom, user) + if(!..(index,used_atom,user)) + return 0 + return 1 + +#define state_next "next" +#define state_prev "prev" + +/datum/construction/reversible2 + var/index + var/base_icon = "durand" + +/datum/construction/reversible2/New(atom) + ..() + index = 1 + return + +/datum/construction/reversible2/proc/update_index(diff as num, mob/user as mob) + index-=diff + if(index==steps.len+1) + spawn_result(user) + else + set_desc(index) + return + +/datum/construction/reversible2/proc/update_icon() + holder.icon_state="[base_icon]_[index]" + +/datum/construction/reversible2/is_right_key(mob/user as mob,atom/used_atom) // returns index step + var/list/state = steps[index] + if(state_next in state) + var/list/step = state[state_next] + if(do_tool_or_atom_check(used_atom, step["key"])) + //if(L["consume"] && !try_consume(used_atom,L["consume"])) + // return 0 + return FORWARD //to the first step -> forward + else if(state_prev in state) + var/list/step = state[state_prev] + if(do_tool_or_atom_check(used_atom, step["key"])) + //if(L["consume"] && !try_consume(used_atom,L["consume"])) + // return 0 + return BACKWARD //to the first step -> forward + return 0 + +/datum/construction/reversible2/check_step(atom/used_atom,mob/user as mob) + var/diff = is_right_key(user,used_atom) + if(diff) + if(custom_action(index, diff, used_atom, user)) + update_index(diff,user) + update_icon() + return 1 + return 0 + +/datum/construction/reversible2/proc/fixText(text,user) + text = replacetext(text,"{USER}","[user]") + text = replacetext(text,"{HOLDER}","[holder]") + return text + +/datum/construction/reversible2/custom_action(index, diff, used_atom, var/mob/user) + if(!..(index,used_atom,user)) + return 0 + + var/list/step = steps[index] + var/list/state = step[diff==FORWARD ? state_next : state_prev] + user.visible_message(fixText(state["vis_msg"],user),fixText(state["self_msg"],user)) + + if("delete" in state) + qdel(used_atom) + else if("spawn" in state) + var/spawntype=state["spawn"] + var/atom/A = new spawntype(holder.loc) + if("amount" in state) + if(istype(A,/obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C=A + C.amount=state["amount"] + if(istype(A,/obj/item/stack)) + var/obj/item/stack/S=A + S.amount=state["amount"] + + return 1 + +/datum/construction/reversible2/action(used_atom,user) + return check_step(used_atom,user) diff --git a/code/datums/helper_datums/events.dm b/code/datums/helper_datums/events.dm index fa0dccd2a156..2fba9d996aee 100644 --- a/code/datums/helper_datums/events.dm +++ b/code/datums/helper_datums/events.dm @@ -1,68 +1,68 @@ -/* - * WARRANTY VOID IF CODE USED - */ - - -/datum/events - var/list/events - -/datum/events/New() - ..() - events = new - -/datum/events/proc/addEventType(event_type as text) - if(!(event_type in events) || !islist(events[event_type])) - events[event_type] = list() - return 1 - return - - -// Arguments: event_type as text, proc_holder as datum, proc_name as text -// Returns: New event, null on error. -/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) - if(!event_type || !proc_holder || !proc_name) - return - addEventType(event_type) - var/list/event = events[event_type] - var/datum/event/E = new /datum/event(proc_holder,proc_name) - event += E - return E - -// Arguments: event_type as text, any number of additional arguments to pass to event handler -// Returns: null -/datum/events/proc/fireEvent() - //world << "Events in [args[1]] called" - var/list/event = listgetindex(events,args[1]) - if(istype(event)) - spawn(0) - for(var/datum/event/E in event) - if(!E.Fire(arglist(args.Copy(2)))) - clearEvent(args[1],E) - return - -// Arguments: event_type as text, E as /datum/event -// Returns: 1 if event cleared, null on error - -/datum/events/proc/clearEvent(event_type as text, datum/event/E) - if(!event_type || !E) - return - var/list/event = listgetindex(events,event_type) - event -= E - return 1 - - -/datum/event - var/listener - var/proc_name - -/datum/event/New(tlistener,tprocname) - listener = tlistener - proc_name = tprocname - return ..() - -/datum/event/proc/Fire() - //world << "Event fired" - if(listener) - call(listener,proc_name)(arglist(args)) - return 1 - return +/* + * WARRANTY VOID IF CODE USED + */ + + +/datum/events + var/list/events + +/datum/events/New() + ..() + events = new + +/datum/events/proc/addEventType(event_type as text) + if(!(event_type in events) || !islist(events[event_type])) + events[event_type] = list() + return 1 + return + + +// Arguments: event_type as text, proc_holder as datum, proc_name as text +// Returns: New event, null on error. +/datum/events/proc/addEvent(event_type as text, proc_holder, proc_name as text) + if(!event_type || !proc_holder || !proc_name) + return + addEventType(event_type) + var/list/event = events[event_type] + var/datum/event/E = new /datum/event(proc_holder,proc_name) + event += E + return E + +// Arguments: event_type as text, any number of additional arguments to pass to event handler +// Returns: null +/datum/events/proc/fireEvent() + //world << "Events in [args[1]] called" + var/list/event = listgetindex(events,args[1]) + if(istype(event)) + spawn(0) + for(var/datum/event/E in event) + if(!E.Fire(arglist(args.Copy(2)))) + clearEvent(args[1],E) + return + +// Arguments: event_type as text, E as /datum/event +// Returns: 1 if event cleared, null on error + +/datum/events/proc/clearEvent(event_type as text, datum/event/E) + if(!event_type || !E) + return + var/list/event = listgetindex(events,event_type) + event -= E + return 1 + + +/datum/event + var/listener + var/proc_name + +/datum/event/New(tlistener,tprocname) + listener = tlistener + proc_name = tprocname + return ..() + +/datum/event/proc/Fire() + //world << "Event fired" + if(listener) + call(listener,proc_name)(arglist(args)) + return 1 + return diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm index e3d14743fe10..214045941b5d 100644 --- a/code/datums/helper_datums/global_iterator.dm +++ b/code/datums/helper_datums/global_iterator.dm @@ -1,152 +1,152 @@ -/* -README: - -The global_iterator datum is supposed to provide a simple and robust way to -create some constantly "looping" processes with ability to stop and restart them at will. -Generally, the only thing you want to play with (meaning, redefine) is the process() proc. -It must contain all the things you want done. - -Control functions: - new - used to create datum. First argument (optional) - var list(to use in process() proc) as list, - second (optional) - autostart control. - If autostart == TRUE, the loop will be started immediately after datum creation. - - start(list/arguments) - starts the loop. Takes arguments(optional) as a list, which is then used - by process() proc. Returns null if datum already active, 1 if loop started succesfully and 0 if there's - an error in supplied arguments (not list or empty list). - - stop() - stops the loop. Returns null if datum is already inactive and 1 on success. - - set_delay(new_delay) - sets the delay between iterations. Pretty selfexplanatory. - Returns 0 on error(new_delay is not numerical), 1 otherwise. - - set_process_args(list/arguments) - passes the supplied arguments to the process() proc. - - active() - Returns 1 if datum is active, 0 otherwise. - - toggle() - toggles datum state. Returns new datum state (see active()). - -Misc functions: - - get_last_exec_time() - Returns the time of last iteration. - - get_last_exec_time_as_text() - Returns the time of last iteration as text - - -Control vars: - - delay - delay between iterations - - check_for_null - if equals TRUE, on each iteration the supplied arguments will be checked for nulls. - If some varible equals null (and null only), the loop is stopped. - Usefull, if some var unexpectedly becomes null - due to object deletion, for example. - Of course, you can also check the variables inside process() proc to prevent runtime errors. - -Data storage vars: - - result - stores the value returned by process() proc -*/ - -/datum/global_iterator - var/control_switch = 0 - var/delay = 10 - var/list/arg_list = new - var/last_exec = null - var/check_for_null = 1 - var/forbid_garbage = 0 - var/result - var/state = 0 - -/datum/global_iterator/New(list/arguments=null,autostart=1) - delay = delay>0?(delay):1 - if(forbid_garbage) //prevents garbage collection with tag != null - tag = "\ref[src]" - set_process_args(arguments) - if(autostart) - start() - return - -/datum/global_iterator/proc/main() - state = 1 - while(src && control_switch) - last_exec = world.timeofday - if(check_for_null && has_null_args()) - stop() - return 0 - result = process(arglist(arg_list)) - for(var/sleep_time=delay;sleep_time>0;sleep_time--) //uhh, this is ugly. But I see no other way to terminate sleeping proc. Such disgrace. - if(!control_switch) - return 0 - sleep(1) - return 0 - -/datum/global_iterator/proc/start(list/arguments=null) - if(active()) - return - if(arguments) - if(!set_process_args(arguments)) - return 0 - if(!state_check()) //the main loop is sleeping, wait for it to terminate. - return - control_switch = 1 - spawn() - state = main() - return 1 - -/datum/global_iterator/proc/stop() - if(!active()) - return - control_switch = 0 - spawn(-1) //report termination error but don't wait for state_check(). - state_check() - return 1 - -/datum/global_iterator/proc/state_check() - var/lag = 0 - while(state) - sleep(1) - if(++lag>10) - CRASH("The global_iterator loop \ref[src] failed to terminate in designated timeframe. This may be caused by server lagging.") - return 1 - -/datum/global_iterator/process() - return - -/datum/global_iterator/proc/active() - return control_switch - -/datum/global_iterator/proc/has_null_args() - if(null in arg_list) - return 1 - return 0 - - -/datum/global_iterator/proc/set_delay(new_delay) - if(isnum(new_delay)) - delay = max(1, round(new_delay)) - return 1 - else - return 0 - -/datum/global_iterator/proc/get_last_exec_time() - return (last_exec||0) - -/datum/global_iterator/proc/get_last_exec_time_as_text() - return (time2text(last_exec)||"Wasn't executed yet") - -/datum/global_iterator/proc/set_process_args(list/arguments) - if(arguments && istype(arguments, /list) && arguments.len) - arg_list = arguments - return 1 - else -// to_chat(world, "Invalid arguments supplied for [src.type], ref = \ref[src]") - return 0 - -/datum/global_iterator/proc/toggle_null_checks() - check_for_null = !check_for_null - return check_for_null - -/datum/global_iterator/proc/toggle() - if(!stop()) - start() - return active() +/* +README: + +The global_iterator datum is supposed to provide a simple and robust way to +create some constantly "looping" processes with ability to stop and restart them at will. +Generally, the only thing you want to play with (meaning, redefine) is the process() proc. +It must contain all the things you want done. + +Control functions: + new - used to create datum. First argument (optional) - var list(to use in process() proc) as list, + second (optional) - autostart control. + If autostart == TRUE, the loop will be started immediately after datum creation. + + start(list/arguments) - starts the loop. Takes arguments(optional) as a list, which is then used + by process() proc. Returns null if datum already active, 1 if loop started succesfully and 0 if there's + an error in supplied arguments (not list or empty list). + + stop() - stops the loop. Returns null if datum is already inactive and 1 on success. + + set_delay(new_delay) - sets the delay between iterations. Pretty selfexplanatory. + Returns 0 on error(new_delay is not numerical), 1 otherwise. + + set_process_args(list/arguments) - passes the supplied arguments to the process() proc. + + active() - Returns 1 if datum is active, 0 otherwise. + + toggle() - toggles datum state. Returns new datum state (see active()). + +Misc functions: + + get_last_exec_time() - Returns the time of last iteration. + + get_last_exec_time_as_text() - Returns the time of last iteration as text + + +Control vars: + + delay - delay between iterations + + check_for_null - if equals TRUE, on each iteration the supplied arguments will be checked for nulls. + If some varible equals null (and null only), the loop is stopped. + Usefull, if some var unexpectedly becomes null - due to object deletion, for example. + Of course, you can also check the variables inside process() proc to prevent runtime errors. + +Data storage vars: + + result - stores the value returned by process() proc +*/ + +/datum/global_iterator + var/control_switch = 0 + var/delay = 10 + var/list/arg_list = new + var/last_exec = null + var/check_for_null = 1 + var/forbid_garbage = 0 + var/result + var/state = 0 + +/datum/global_iterator/New(list/arguments=null,autostart=1) + delay = delay>0?(delay):1 + if(forbid_garbage) //prevents garbage collection with tag != null + tag = "\ref[src]" + set_process_args(arguments) + if(autostart) + start() + return + +/datum/global_iterator/proc/main() + state = 1 + while(src && control_switch) + last_exec = world.timeofday + if(check_for_null && has_null_args()) + stop() + return 0 + result = process(arglist(arg_list)) + for(var/sleep_time=delay;sleep_time>0;sleep_time--) //uhh, this is ugly. But I see no other way to terminate sleeping proc. Such disgrace. + if(!control_switch) + return 0 + sleep(1) + return 0 + +/datum/global_iterator/proc/start(list/arguments=null) + if(active()) + return + if(arguments) + if(!set_process_args(arguments)) + return 0 + if(!state_check()) //the main loop is sleeping, wait for it to terminate. + return + control_switch = 1 + spawn() + state = main() + return 1 + +/datum/global_iterator/proc/stop() + if(!active()) + return + control_switch = 0 + spawn(-1) //report termination error but don't wait for state_check(). + state_check() + return 1 + +/datum/global_iterator/proc/state_check() + var/lag = 0 + while(state) + sleep(1) + if(++lag>10) + CRASH("The global_iterator loop \ref[src] failed to terminate in designated timeframe. This may be caused by server lagging.") + return 1 + +/datum/global_iterator/process() + return + +/datum/global_iterator/proc/active() + return control_switch + +/datum/global_iterator/proc/has_null_args() + if(null in arg_list) + return 1 + return 0 + + +/datum/global_iterator/proc/set_delay(new_delay) + if(isnum(new_delay)) + delay = max(1, round(new_delay)) + return 1 + else + return 0 + +/datum/global_iterator/proc/get_last_exec_time() + return (last_exec||0) + +/datum/global_iterator/proc/get_last_exec_time_as_text() + return (time2text(last_exec)||"Wasn't executed yet") + +/datum/global_iterator/proc/set_process_args(list/arguments) + if(arguments && istype(arguments, /list) && arguments.len) + arg_list = arguments + return 1 + else +// to_chat(world, "Invalid arguments supplied for [src.type], ref = \ref[src]") + return 0 + +/datum/global_iterator/proc/toggle_null_checks() + check_for_null = !check_for_null + return check_for_null + +/datum/global_iterator/proc/toggle() + if(!stop()) + start() + return active() diff --git a/code/datums/helper_datums/map_template.dm b/code/datums/helper_datums/map_template.dm index 2030897e3ce9..a54707842dbe 100644 --- a/code/datums/helper_datums/map_template.dm +++ b/code/datums/helper_datums/map_template.dm @@ -17,7 +17,7 @@ name = rename /datum/map_template/proc/preload_size(path) - var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap = 0, measureOnly = 1) + var/bounds = GLOB.maploader.load_map(file(path), 1, 1, 1, cropMap = 0, measureOnly = 1) if(bounds) width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1 height = bounds[MAP_MAXY] @@ -48,8 +48,8 @@ // This system will metaphorically snap in half (not postpone init everywhere) // if given a multi-z template // it might need to be adapted for that when that time comes - space_manager.add_dirt(placement.z) - var/list/bounds = maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1) + GLOB.space_manager.add_dirt(placement.z) + var/list/bounds = GLOB.maploader.load_map(get_file(), min_x, min_y, placement.z, cropMap = 1) if(!bounds) return 0 if(bot_left == null || top_right == null) @@ -58,7 +58,7 @@ if(ST_bot_left == null || ST_top_right == null) log_runtime(EXCEPTION("One of the smoothing corners is bust"), src) - space_manager.remove_dirt(placement.z) + GLOB.space_manager.remove_dirt(placement.z) late_setup_level( block(bot_left, top_right), block(ST_bot_left, ST_top_right)) @@ -108,7 +108,7 @@ for(var/map in flist(path)) if(cmptext(copytext(map, length(map) - 3), ".dmm")) var/datum/map_template/T = new(path = "[path][map]", rename = "[map]") - map_templates[T.name] = T + GLOB.map_templates[T.name] = T if(!config.disable_space_ruins) // so we don't unnecessarily clutter start-up preloadRuinTemplates() @@ -134,13 +134,13 @@ if(banned.Find(R.mappath)) continue - map_templates[R.name] = R - ruins_templates[R.name] = R + GLOB.map_templates[R.name] = R + GLOB.ruins_templates[R.name] = R if(istype(R, /datum/map_template/ruin/lavaland)) - lava_ruins_templates[R.name] = R + GLOB.lava_ruins_templates[R.name] = R if(istype(R, /datum/map_template/ruin/space)) - space_ruins_templates[R.name] = R + GLOB.space_ruins_templates[R.name] = R /proc/preloadShelterTemplates() for(var/item in subtypesof(/datum/map_template/shelter)) @@ -149,8 +149,8 @@ continue var/datum/map_template/shelter/S = new shelter_type() - shelter_templates[S.shelter_id] = S - map_templates[S.shelter_id] = S + GLOB.shelter_templates[S.shelter_id] = S + GLOB.map_templates[S.shelter_id] = S /proc/preloadShuttleTemplates() for(var/item in subtypesof(/datum/map_template/shuttle)) @@ -160,5 +160,5 @@ var/datum/map_template/shuttle/S = new shuttle_type() - shuttle_templates[S.shuttle_id] = S - map_templates[S.shuttle_id] = S \ No newline at end of file + GLOB.shuttle_templates[S.shuttle_id] = S + GLOB.map_templates[S.shuttle_id] = S diff --git a/code/datums/helper_datums/teleport.dm b/code/datums/helper_datums/teleport.dm index 2170ad4196e3..9bba49f26789 100644 --- a/code/datums/helper_datums/teleport.dm +++ b/code/datums/helper_datums/teleport.dm @@ -1,237 +1,237 @@ -//wrapper -/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) - var/datum/teleport/instant/science/D = new - if(D.start(arglist(args))) - return 1 - return 0 - -/datum/teleport - var/atom/movable/teleatom //atom to teleport - var/atom/destination //destination to teleport to - var/precision = 0 //teleport precision - var/datum/effect_system/effectin //effect to show right before teleportation - var/datum/effect_system/effectout //effect to show right after teleportation - var/soundin //soundfile to play before teleportation - var/soundout //soundfile to play after teleportation - var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) - var/ignore_area_flag = FALSE - - -/datum/teleport/proc/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) - if(!initTeleport(arglist(args))) - return 0 - return 1 - -/datum/teleport/proc/initTeleport(ateleatom, adestination, aprecision, afteleport, aeffectin, aeffectout, asoundin, asoundout, bypass_area_flag=FALSE) - if(!setTeleatom(ateleatom)) - return 0 - if(!setDestination(adestination)) - return 0 - if(!setPrecision(aprecision)) - return 0 - setEffects(aeffectin,aeffectout) - setForceTeleport(afteleport) - setSounds(asoundin,asoundout) - ignore_area_flag = bypass_area_flag - return 1 - -//must succeed -/datum/teleport/proc/setPrecision(aprecision) - if(isnum(aprecision)) - precision = aprecision - return 1 - return 0 - -//must succeed -/datum/teleport/proc/setDestination(atom/adestination) - if(istype(adestination)) - destination = adestination - return 1 - return 0 - -//must succeed in most cases -/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) - if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) - qdel(ateleatom) - return 0 - if(istype(ateleatom)) - teleatom = ateleatom - return 1 - return 0 - -//custom effects must be properly set up first for instant-type teleports -//optional -/datum/teleport/proc/setEffects(datum/effect_system/aeffectin=null,datum/effect_system/aeffectout=null) - effectin = istype(aeffectin) ? aeffectin : null - effectout = istype(aeffectout) ? aeffectout : null - return 1 - -//optional -/datum/teleport/proc/setForceTeleport(afteleport) - force_teleport = afteleport - return 1 - -//optional -/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) - soundin = isfile(asoundin) ? asoundin : null - soundout = isfile(asoundout) ? asoundout : null - return 1 - -//placeholder -/datum/teleport/proc/teleportChecks() - return 1 - -/datum/teleport/proc/playSpecials(atom/location,datum/effect_system/effect,sound) - if(location) - if(effect) - spawn(-1) - src = null - effect.attach(location) - effect.start() - if(sound) - spawn(-1) - src = null - playsound(location,sound,60,1) - return - -//do the monkey dance -/datum/teleport/proc/doTeleport() - - var/turf/destturf - var/turf/curturf = get_turf(teleatom) - var/area/curarea = get_area(curturf) - - if(precision) - var/list/posturfs = list() - var/center = get_turf(destination) - if(!center) - center = destination - for(var/turf/T in range(precision,center)) - posturfs.Add(T) - destturf = safepick(posturfs) - else - destturf = get_turf(destination) - - if(!is_teleport_allowed(destturf.z) && !ignore_area_flag) - return 0 - // Only check the destination zlevel for is_teleport_allowed. Checking origin as well breaks ERT teleporters. - - var/area/destarea = get_area(destturf) - - if(!ignore_area_flag) - if(curarea.tele_proof) - return 0 - if(destarea.tele_proof) - return 0 - - if(!destturf || !curturf) - return 0 - - playSpecials(curturf,effectin,soundin) - - if(force_teleport) - teleatom.forceMove(destturf) - playSpecials(destturf,effectout,soundout) - else - if(teleatom.Move(destturf)) - playSpecials(destturf,effectout,soundout) - - if(isliving(teleatom)) - var/mob/living/L = teleatom - if(L.buckled) - L.buckled.unbuckle_mob(L, force = TRUE) - if(L.has_buckled_mobs()) - L.unbuckle_all_mobs(force = TRUE) - - destarea.Entered(teleatom) - - return 1 - -/datum/teleport/proc/teleport() - if(teleportChecks()) - return doTeleport() - return 0 - -/datum/teleport/instant //teleports when datum is created - - start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) - if(..()) - if(teleport()) - return 1 - return 0 - - -/datum/teleport/instant/science - -/datum/teleport/instant/science/setEffects(datum/effect_system/aeffectin,datum/effect_system/aeffectout) - if(aeffectin==null || aeffectout==null) - var/datum/effect_system/spark_spread/aeffect = new - aeffect.set_up(5, 1, teleatom) - effectin = effectin || aeffect - effectout = effectout || aeffect - return 1 - else - return ..() - -/datum/teleport/instant/science/setPrecision(aprecision) - ..() - if(!is_admin_level(destination.z)) - if(istype(teleatom, /obj/item/storage/backpack/holding)) - precision = rand(1, 100) - - var/list/bagholding = teleatom.search_contents_for(/obj/item/storage/backpack/holding) - if(bagholding.len) - precision = max(rand(1, 100)*bagholding.len, 100) - if(istype(teleatom, /mob/living)) - var/mob/living/MM = teleatom - to_chat(MM, "The bluespace interface on your bag of holding interferes with the teleport!") - return 1 - -// Safe location finder -/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE) - if(!zlevels) - if(zlevel) - zlevels = list(zlevel) - else - zlevels = levels_by_trait(STATION_LEVEL) - var/cycles = 1000 - for(var/cycle in 1 to cycles) - // DRUNK DIALLING WOOOOOOOOO - var/x = rand(1, world.maxx) - var/y = rand(1, world.maxy) - var/z = pick(zlevels) - var/random_location = locate(x,y,z) - - if(!isfloorturf(random_location)) - continue - var/turf/simulated/floor/F = random_location - if(!F.air) - continue - - var/datum/gas_mixture/A = F.air - - // Can most things breathe? - if(A.trace_gases.len) - continue - if(A.oxygen < 16) - continue - if(A.toxins) - continue - if(A.carbon_dioxide >= 10) - continue - - // Aim for goldilocks temperatures and pressure - if((A.temperature <= 270) || (A.temperature >= 360)) - continue - var/pressure = A.return_pressure() - if((pressure <= 20) || (pressure >= 550)) - continue - - if(extended_safety_checks) - if(islava(F)) //chasms aren't /floor, and so are pre-filtered - var/turf/simulated/floor/plating/lava/L = F - if(!L.is_safe()) - continue - - // DING! You have passed the gauntlet, and are "probably" safe. - return F \ No newline at end of file +//wrapper +/proc/do_teleport(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) + var/datum/teleport/instant/science/D = new + if(D.start(arglist(args))) + return 1 + return 0 + +/datum/teleport + var/atom/movable/teleatom //atom to teleport + var/atom/destination //destination to teleport to + var/precision = 0 //teleport precision + var/datum/effect_system/effectin //effect to show right before teleportation + var/datum/effect_system/effectout //effect to show right after teleportation + var/soundin //soundfile to play before teleportation + var/soundout //soundfile to play after teleportation + var/force_teleport = 1 //if false, teleport will use Move() proc (dense objects will prevent teleportation) + var/ignore_area_flag = FALSE + + +/datum/teleport/proc/start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null, bypass_area_flag=FALSE) + if(!initTeleport(arglist(args))) + return 0 + return 1 + +/datum/teleport/proc/initTeleport(ateleatom, adestination, aprecision, afteleport, aeffectin, aeffectout, asoundin, asoundout, bypass_area_flag=FALSE) + if(!setTeleatom(ateleatom)) + return 0 + if(!setDestination(adestination)) + return 0 + if(!setPrecision(aprecision)) + return 0 + setEffects(aeffectin,aeffectout) + setForceTeleport(afteleport) + setSounds(asoundin,asoundout) + ignore_area_flag = bypass_area_flag + return 1 + +//must succeed +/datum/teleport/proc/setPrecision(aprecision) + if(isnum(aprecision)) + precision = aprecision + return 1 + return 0 + +//must succeed +/datum/teleport/proc/setDestination(atom/adestination) + if(istype(adestination)) + destination = adestination + return 1 + return 0 + +//must succeed in most cases +/datum/teleport/proc/setTeleatom(atom/movable/ateleatom) + if(istype(ateleatom, /obj/effect) && !istype(ateleatom, /obj/effect/dummy/chameleon)) + qdel(ateleatom) + return 0 + if(istype(ateleatom)) + teleatom = ateleatom + return 1 + return 0 + +//custom effects must be properly set up first for instant-type teleports +//optional +/datum/teleport/proc/setEffects(datum/effect_system/aeffectin=null,datum/effect_system/aeffectout=null) + effectin = istype(aeffectin) ? aeffectin : null + effectout = istype(aeffectout) ? aeffectout : null + return 1 + +//optional +/datum/teleport/proc/setForceTeleport(afteleport) + force_teleport = afteleport + return 1 + +//optional +/datum/teleport/proc/setSounds(asoundin=null,asoundout=null) + soundin = isfile(asoundin) ? asoundin : null + soundout = isfile(asoundout) ? asoundout : null + return 1 + +//placeholder +/datum/teleport/proc/teleportChecks() + return 1 + +/datum/teleport/proc/playSpecials(atom/location,datum/effect_system/effect,sound) + if(location) + if(effect) + spawn(-1) + src = null + effect.attach(location) + effect.start() + if(sound) + spawn(-1) + src = null + playsound(location,sound,60,1) + return + +//do the monkey dance +/datum/teleport/proc/doTeleport() + + var/turf/destturf + var/turf/curturf = get_turf(teleatom) + var/area/curarea = get_area(curturf) + + if(precision) + var/list/posturfs = list() + var/center = get_turf(destination) + if(!center) + center = destination + for(var/turf/T in range(precision,center)) + posturfs.Add(T) + destturf = safepick(posturfs) + else + destturf = get_turf(destination) + + if(!is_teleport_allowed(destturf.z) && !ignore_area_flag) + return 0 + // Only check the destination zlevel for is_teleport_allowed. Checking origin as well breaks ERT teleporters. + + var/area/destarea = get_area(destturf) + + if(!ignore_area_flag) + if(curarea.tele_proof) + return 0 + if(destarea.tele_proof) + return 0 + + if(!destturf || !curturf) + return 0 + + playSpecials(curturf,effectin,soundin) + + if(force_teleport) + teleatom.forceMove(destturf) + playSpecials(destturf,effectout,soundout) + else + if(teleatom.Move(destturf)) + playSpecials(destturf,effectout,soundout) + + if(isliving(teleatom)) + var/mob/living/L = teleatom + if(L.buckled) + L.buckled.unbuckle_mob(L, force = TRUE) + if(L.has_buckled_mobs()) + L.unbuckle_all_mobs(force = TRUE) + + destarea.Entered(teleatom) + + return 1 + +/datum/teleport/proc/teleport() + if(teleportChecks()) + return doTeleport() + return 0 + +/datum/teleport/instant //teleports when datum is created + + start(ateleatom, adestination, aprecision=0, afteleport=1, aeffectin=null, aeffectout=null, asoundin=null, asoundout=null) + if(..()) + if(teleport()) + return 1 + return 0 + + +/datum/teleport/instant/science + +/datum/teleport/instant/science/setEffects(datum/effect_system/aeffectin,datum/effect_system/aeffectout) + if(aeffectin==null || aeffectout==null) + var/datum/effect_system/spark_spread/aeffect = new + aeffect.set_up(5, 1, teleatom) + effectin = effectin || aeffect + effectout = effectout || aeffect + return 1 + else + return ..() + +/datum/teleport/instant/science/setPrecision(aprecision) + ..() + if(!is_admin_level(destination.z)) + if(istype(teleatom, /obj/item/storage/backpack/holding)) + precision = rand(1, 100) + + var/list/bagholding = teleatom.search_contents_for(/obj/item/storage/backpack/holding) + if(bagholding.len) + precision = max(rand(1, 100)*bagholding.len, 100) + if(istype(teleatom, /mob/living)) + var/mob/living/MM = teleatom + to_chat(MM, "The bluespace interface on your bag of holding interferes with the teleport!") + return 1 + +// Safe location finder +/proc/find_safe_turf(zlevel, list/zlevels, extended_safety_checks = FALSE) + if(!zlevels) + if(zlevel) + zlevels = list(zlevel) + else + zlevels = levels_by_trait(STATION_LEVEL) + var/cycles = 1000 + for(var/cycle in 1 to cycles) + // DRUNK DIALLING WOOOOOOOOO + var/x = rand(1, world.maxx) + var/y = rand(1, world.maxy) + var/z = pick(zlevels) + var/random_location = locate(x,y,z) + + if(!isfloorturf(random_location)) + continue + var/turf/simulated/floor/F = random_location + if(!F.air) + continue + + var/datum/gas_mixture/A = F.air + + // Can most things breathe? + if(A.trace_gases.len) + continue + if(A.oxygen < 16) + continue + if(A.toxins) + continue + if(A.carbon_dioxide >= 10) + continue + + // Aim for goldilocks temperatures and pressure + if((A.temperature <= 270) || (A.temperature >= 360)) + continue + var/pressure = A.return_pressure() + if((pressure <= 20) || (pressure >= 550)) + continue + + if(extended_safety_checks) + if(islava(F)) //chasms aren't /floor, and so are pre-filtered + var/turf/simulated/floor/plating/lava/L = F + if(!L.is_safe()) + continue + + // DING! You have passed the gauntlet, and are "probably" safe. + return F diff --git a/code/datums/helper_datums/topic_input.dm b/code/datums/helper_datums/topic_input.dm index 8090a5221ebe..0f6a9f7d6894 100644 --- a/code/datums/helper_datums/topic_input.dm +++ b/code/datums/helper_datums/topic_input.dm @@ -1,60 +1,60 @@ -/datum/topic_input - var/href - var/list/href_list - -/datum/topic_input/New(thref,list/thref_list) - href = thref - href_list = thref_list.Copy() - return - -/datum/topic_input/proc/get(i) - return listgetindex(href_list,i) - -/datum/topic_input/proc/getAndLocate(i) - var/t = get(i) - if(t) - t = locate(t) - return t || null - -/datum/topic_input/proc/getNum(i) - var/t = get(i) - if(t) - t = text2num(t) - return isnum(t) ? t : null - -/datum/topic_input/proc/getObj(i) - var/t = getAndLocate(i) - return isobj(t) ? t : null - -/datum/topic_input/proc/getMob(i) - var/t = getAndLocate(i) - return ismob(t) ? t : null - -/datum/topic_input/proc/getTurf(i) - var/t = getAndLocate(i) - return isturf(t) ? t : null - -/datum/topic_input/proc/getAtom(i) - return getType(i,/atom) - -/datum/topic_input/proc/getArea(i) - var/t = getAndLocate(i) - return isarea(t) ? t : null - -/datum/topic_input/proc/getStr(i)//params should always be text, but... - var/t = get(i) - return istext(t) ? t : null - -/datum/topic_input/proc/getType(i,type) - var/t = getAndLocate(i) - return istype(t,type) ? t : null - -/datum/topic_input/proc/getPath(i) - var/t = get(i) - if(t) - t = text2path(t) - return ispath(t) ? t : null - -/datum/topic_input/proc/getList(i) - var/t = getAndLocate(i) - return islist(t) ? t : null +/datum/topic_input + var/href + var/list/href_list + +/datum/topic_input/New(thref,list/thref_list) + href = thref + href_list = thref_list.Copy() + return + +/datum/topic_input/proc/get(i) + return listgetindex(href_list,i) + +/datum/topic_input/proc/getAndLocate(i) + var/t = get(i) + if(t) + t = locate(t) + return t || null + +/datum/topic_input/proc/getNum(i) + var/t = get(i) + if(t) + t = text2num(t) + return isnum(t) ? t : null + +/datum/topic_input/proc/getObj(i) + var/t = getAndLocate(i) + return isobj(t) ? t : null + +/datum/topic_input/proc/getMob(i) + var/t = getAndLocate(i) + return ismob(t) ? t : null + +/datum/topic_input/proc/getTurf(i) + var/t = getAndLocate(i) + return isturf(t) ? t : null + +/datum/topic_input/proc/getAtom(i) + return getType(i,/atom) + +/datum/topic_input/proc/getArea(i) + var/t = getAndLocate(i) + return isarea(t) ? t : null + +/datum/topic_input/proc/getStr(i)//params should always be text, but... + var/t = get(i) + return istext(t) ? t : null + +/datum/topic_input/proc/getType(i,type) + var/t = getAndLocate(i) + return istype(t,type) ? t : null + +/datum/topic_input/proc/getPath(i) + var/t = get(i) + if(t) + t = text2path(t) + return ispath(t) ? t : null + +/datum/topic_input/proc/getList(i) + var/t = getAndLocate(i) + return islist(t) ? t : null diff --git a/code/datums/hud.dm b/code/datums/hud.dm index 365a43094131..50152c4873da 100644 --- a/code/datums/hud.dm +++ b/code/datums/hud.dm @@ -1,8 +1,8 @@ /* HUD DATUMS */ -var/global/list/all_huds = list() +GLOBAL_LIST_EMPTY(all_huds) ///GLOBAL HUD LIST -var/datum/atom_hud/huds = list( \ +GLOBAL_LIST_INIT(huds, list( \ DATA_HUD_SECURITY_BASIC = new/datum/atom_hud/data/human/security/basic(), \ DATA_HUD_SECURITY_ADVANCED = new/datum/atom_hud/data/human/security/advanced(), \ DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(), \ @@ -24,7 +24,7 @@ var/datum/atom_hud/huds = list( \ ANTAG_HUD_DEVIL = new/datum/atom_hud/antag/hidden(),\ ANTAG_HUD_EVENTMISC = new/datum/atom_hud/antag/hidden(),\ ANTAG_HUD_BLOB = new/datum/atom_hud/antag/hidden()\ - ) +)) /datum/atom_hud var/list/atom/hudatoms = list() //list of all atoms which display this hud @@ -33,14 +33,14 @@ var/datum/atom_hud/huds = list( \ /datum/atom_hud/New() - all_huds += src + GLOB.all_huds += src /datum/atom_hud/Destroy() for(var/v in hudusers) remove_hud_from(v) for(var/v in hudatoms) remove_from_hud(v) - all_huds -= src + GLOB.all_huds -= src return ..() /datum/atom_hud/proc/remove_hud_from(mob/M) @@ -99,7 +99,7 @@ var/datum/atom_hud/huds = list( \ serv_huds += serv.thrallhud - for(var/datum/atom_hud/hud in (all_huds|serv_huds))//|gang_huds)) + for(var/datum/atom_hud/hud in (GLOB.all_huds|serv_huds))//|gang_huds)) if(src in hud.hudusers) hud.add_hud_to(src) @@ -110,4 +110,4 @@ var/datum/atom_hud/huds = list( \ client.screen += client.void /mob/new_player/add_click_catcher() - return \ No newline at end of file + return diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm new file mode 100644 index 000000000000..9b7d52b5390b --- /dev/null +++ b/code/datums/log_record.dm @@ -0,0 +1,37 @@ +/datum/log_record + var/log_type // Type of log + var/raw_time // When did this happen? + var/what // What happened + var/who // Who did it + var/target // Who/what was targeted (can be a string) + var/turf/where // Where did it happen + +/datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time) + log_type = _log_type + + who = get_subject_text(_who) + what = _what + target = get_subject_text(_target) + if(!_where) + _where = get_turf(_who) + where = _where + if(!_raw_time) + _raw_time = world.time + raw_time = _raw_time + +/datum/log_record/proc/get_subject_text(subject) + if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind)) + return key_name_admin(subject) + if(isatom(subject)) + var/atom/A = subject + return A.name + if(istype(subject, /datum)) + var/datum/D = subject + return D.type + return subject + +/proc/compare_log_record(datum/log_record/A, datum/log_record/B) + var/time_diff = A.raw_time - B.raw_time + if(!time_diff) // Same time + return cmp_text_asc(A.log_type, B.log_type) + return time_diff diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm new file mode 100644 index 000000000000..70f7d8329248 --- /dev/null +++ b/code/datums/log_viewer.dm @@ -0,0 +1,232 @@ +#define ALL_LOGS list(ATTACK_LOG, DEFENSE_LOG, CONVERSION_LOG, SAY_LOG, EMOTE_LOG, MISC_LOG) + +/datum/log_viewer + var/time_from = 0 + var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up + var/list/selected_mobs = list() // The mobs in question + var/list/selected_log_types = list() // The log types being searched for + + var/list/log_records = list() // Found and sorted records + +/datum/log_viewer/proc/clear_all() + selected_mobs.Cut() + selected_log_types.Cut() + time_from = initial(time_from) + time_to = initial(time_to) + log_records.Cut() + return + +/datum/log_viewer/proc/search() + log_records.Cut() // Empty the old results + var/list/invalid_mobs = list() + for(var/i in selected_mobs) + var/mob/M = i + if(!M || QDELETED(M)) + invalid_mobs |= M + continue + for(var/log_type in selected_log_types) + var/list/logs = M.logs[log_type] + var/len_logs = length(logs) + if(len_logs) + var/start_index = get_earliest_log_index(logs) + if(!start_index) // No log found that matches the starting time criteria + continue + var/end_index = get_latest_log_index(logs) + if(!end_index) // No log found that matches the end time criteria + continue + log_records.Add(logs.Copy(start_index, end_index + 1)) + + if(invalid_mobs.len) + to_chat(usr, "The search criteria contained invalid mobs. They have been removed from the criteria.") + for(var/i in invalid_mobs) + selected_mobs -= i // Cleanup + + log_records = sortTim(log_records, /proc/compare_log_record) + +/** Binary search like implementation to find the earliest log + * Returns the index of the earliest log using the time_from value for the given list of logs. + * It will return 0 if no log after time_from is found +*/ +/datum/log_viewer/proc/get_earliest_log_index(list/logs) + if(!time_from) + return 1 + var/start = 1 + var/end = length(logs) + var/mid + do + mid = round_down((end + start) / 2) + var/datum/log_record/L = logs[mid] + if(L.raw_time >= time_from) + end = mid + else + start = mid + while(end - start > 1) + var/datum/log_record/L = logs[end] + if(L.raw_time >= time_from) // Check if there is atleast one valid log + return end + return 0 + +/** Binary search like implementation to find the latest log + * Returns the index of the latest log using the time_to value (1 second is added to prevent rounding weirdness) for the given list of logs. + * It will return 0 if no log before time_to + 10 is found +*/ +/datum/log_viewer/proc/get_latest_log_index(list/logs) + if(world.time < time_to) + return length(logs) + + var/end = length(logs) + var/start = 1 + var/mid + var/max_time = time_to + 10 + do + mid = round((end + start) / 2 + 0.5) + var/datum/log_record/L = logs[mid] + if(L.raw_time >= max_time) + end = mid + else + start = mid + while(end - start > 1) + var/datum/log_record/L = logs[start] + if(L.raw_time < max_time) // Check if there is atleast one valid log + return start + return 0 + +/datum/log_viewer/proc/add_mob(mob/user, mob/M) + if(!M || !user) + return + selected_mobs |= M + + show_ui(user) + +/datum/log_viewer/proc/show_ui(mob/user) + var/all_log_types = ALL_LOGS + var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;" + var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;" + var/dat + dat += "" + dat += "
    " + dat += "Time Search Range: [gameTimestamp(wtime = time_from)]" + dat += " To: [gameTimestamp(wtime = time_to)]" + dat += "
    " + + dat += "Mobs being used:" + for(var/i in selected_mobs) + var/mob/M = i + dat += "[M.name]" + dat += "Add Mob" + dat += "Clear All Mobs" + dat += "
    " + + dat += "Log Types:" + for(var/i in all_log_types) + var/log_type = i + var/enabled = (log_type in selected_log_types) + var/text + var/style + if(enabled) + text = "[log_type]" + style = "background: [get_logtype_color(i)]" + else + text = log_type + + dat += "[text]" + + dat += "
    " + dat += "Clear All Settings" + dat += "Search" + dat += "
    " + + // Search results + var/tdStyleTime = "width:80px; text-align:center;" + var/tdStyleType = "width:80px; text-align:center;" + var/tdStyleWho = "width:300px; text-align:center;" + var/tdStyleWhere = "width:150px; text-align:center;" + dat += "
    " + dat += "" + dat += "" + for(var/i in log_records) + var/datum/log_record/L = i + var/time = gameTimestamp(wtime = L.raw_time - 9.99) // The time rounds up for some reason. Will result in weird filtering results + + dat +="\ + \ + " + + dat += "
    WhenTypeWhoWhatTargetWhere
    [time][L.log_type][L.who][L.what][L.target][ADMIN_COORDJMP(L.where)]
    " + dat += "
    " + + var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600) + popup.set_content(dat) + popup.open() + +/datum/log_viewer/Topic(href, href_list) + if(href_list["start_time"]) + var/input = input(usr, "hh:mm:ss", "Start time", "00:00:00") as text|null + if(!input) + return + var/res = timeStampToNum(input) + if(res < 0) + to_chat(usr, "'[input]' is an invalid input value.") + return + time_from = res + show_ui(usr) + return + if(href_list["end_time"]) + var/input = input(usr, "hh:mm:ss", "End time", "04:00:00") as text|null + if(!input) + return + var/res = timeStampToNum(input) + if(res < 0) + to_chat(usr, "'[input]' is an invalid input value.") + return + time_to = res + + show_ui(usr) + return + if(href_list["search"]) + search(usr) + show_ui(usr) + return + if(href_list["clear_all"]) + clear_all(usr) + show_ui(usr) + return + if(href_list["clear_mobs"]) + selected_mobs.Cut() + show_ui(usr) + return + if(href_list["add_mob"]) + var/list/mobs = getpois(TRUE, TRUE) + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) + A.on_close(CALLBACK(src, .proc/add_mob, usr)) + return + if(href_list["remove_mob"]) + var/mob/M = locate(href_list["remove_mob"]) + if(M) + selected_mobs -= M + show_ui(usr) + return + if(href_list["toggle_log_type"]) + var/log_type = href_list["toggle_log_type"] + if(log_type in selected_log_types) + selected_log_types -= log_type + else + selected_log_types += log_type + show_ui(usr) + return + +/datum/log_viewer/proc/get_logtype_color(log_type) + switch(log_type) + if(ATTACK_LOG) + return "darkred" + if(DEFENSE_LOG) + return "chocolate" + if(CONVERSION_LOG) + return "indigo" + if(SAY_LOG) + return "teal" + if(EMOTE_LOG) + return "deepskyblue" + if(MISC_LOG) + return "gray" + return "slategray" diff --git a/code/datums/looping_sounds/looping_sound.dm b/code/datums/looping_sounds/looping_sound.dm index 2cf79ad318b6..f44a87bdd7ab 100644 --- a/code/datums/looping_sounds/looping_sound.dm +++ b/code/datums/looping_sounds/looping_sound.dm @@ -97,4 +97,4 @@ /datum/looping_sound/proc/on_stop(looped) if(end_sound) - play(end_sound) \ No newline at end of file + play(end_sound) diff --git a/code/datums/looping_sounds/machinery_sounds.dm b/code/datums/looping_sounds/machinery_sounds.dm index b1f5bdc69081..8fe373242378 100644 --- a/code/datums/looping_sounds/machinery_sounds.dm +++ b/code/datums/looping_sounds/machinery_sounds.dm @@ -4,4 +4,4 @@ mid_sounds = list('sound/machines/shower/shower_mid1.ogg' = 1,'sound/machines/shower/shower_mid2.ogg' = 1,'sound/machines/shower/shower_mid3.ogg' = 1) mid_length = 10 end_sound = 'sound/machines/shower/shower_end.ogg' - volume = 20 \ No newline at end of file + volume = 20 diff --git a/code/datums/looping_sounds/thermal_drill.dm b/code/datums/looping_sounds/thermal_drill.dm index d4a20647f2b1..3346d7e0e06a 100644 --- a/code/datums/looping_sounds/thermal_drill.dm +++ b/code/datums/looping_sounds/thermal_drill.dm @@ -1,4 +1,4 @@ /datum/looping_sound/thermal_drill mid_sounds = list('sound/items/thermal_drill.ogg' = 1) mid_length = 19 - volume = 30 \ No newline at end of file + volume = 30 diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 036d70fa17a3..135114c11f96 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -1,1822 +1,1830 @@ -/* Note from Carnie: - The way datum/mind stuff works has been changed a lot. - Minds now represent IC characters rather than following a client around constantly. - Guidelines for using minds properly: - - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! - ghost.mind is however used as a reference to the ghost's corpse - - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) - the existing mind of the old mob should be transfered to the new mob like so: - mind.transfer_to(new_mob) - - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. - By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing - the player. - - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. - - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting - a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. - new_mob.key = key - The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! - However if you want that mind to have any special properties like being a traitor etc you will have to do that - yourself. -*/ - -/datum/mind - var/key - var/name //replaces mob/var/original_name - var/mob/living/current - var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. - var/active = 0 - - var/memory - - var/assigned_role //assigned role is what job you're assigned to when you join the station. - var/playtime_role //if set, overrides your assigned_role for the purpose of playtime awards. Set by IDcomputer when your ID is changed. - var/special_role //special roles are typically reserved for antags or roles like ERT. If you want to avoid a character being automatically announced by the AI, on arrival (becuase they're an off station character or something); ensure that special_role and assigned_role are equal. - var/offstation_role = FALSE //set to true for ERT, deathsquad, abductors, etc, that can go from and to z2 at will and shouldn't be antag targets - var/list/restricted_roles = list() - - var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. - - var/role_alt_title - - var/datum/job/assigned_job - var/list/kills = list() - var/list/datum/objective/objectives = list() - var/list/datum/objective/special_verbs = list() - var/list/targets = list() - - var/has_been_rev = 0//Tracks if this mind has been a rev or not - - var/miming = 0 // Mime's vow of silence - var/list/antag_datums - var/speech_span // What span any body this mind has talks in. - var/datum/faction/faction //associated faction - var/datum/changeling/changeling //changeling holder - var/linglink - var/datum/vampire/vampire //vampire holder - var/datum/abductor/abductor //abductor holder - var/datum/devilinfo/devilinfo //devil holder - - var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state - var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD - var/datum/mindslaves/som //stands for slave or master...hush.. - var/datum/devilinfo/devilinfo //Information about the devil, if any. - var/damnation_type = 0 - var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src - var/hasSoul = TRUE - - var/rev_cooldown = 0 - - var/isholy = FALSE // is this person a chaplain or admin role allowed to use bibles - var/isblessed = FALSE // is this person blessed by a chaplain? - var/num_blessed = 0 // for prayers - - // the world.time since the mob has been brigged, or -1 if not at all - var/brigged_since = -1 - var/suicided = FALSE - - //put this here for easier tracking ingame - var/datum/money_account/initial_account - - //zealot_master is a reference to the mob that converted them into a zealot (for ease of investigation and such) - var/mob/living/carbon/human/zealot_master = null - - var/list/learned_recipes //List of learned recipe TYPES. - -/datum/mind/New(new_key) - key = new_key - soulOwner = src - -/datum/mind/Destroy() - SSticker.minds -= src - if(islist(antag_datums)) - for(var/i in antag_datums) - var/datum/antagonist/antag_datum = i - if(antag_datum.delete_on_mind_deletion) - qdel(i) - antag_datums = null - return ..() - -/datum/mind/proc/transfer_to(mob/living/new_character) - var/datum/atom_hud/antag/hud_to_transfer = antag_hud //we need this because leave_hud() will clear this list - var/mob/living/old_current = current - if(!istype(new_character)) - log_runtime(EXCEPTION("transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob."), src) - if(current) //remove ourself from our old body's mind variable - current.mind = null - leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it - - SSnanoui.user_transferred(current, new_character) - - if(new_character.mind) //remove any mind currently in our new body's mind variable - new_character.mind.current = null - current = new_character //link ourself to our new body - new_character.mind = src //and link our new body to ourself - for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body - var/datum/antagonist/A = a - A.on_body_transfer(old_current, current) - transfer_antag_huds(hud_to_transfer) //inherit the antag HUD - transfer_actions(new_character) - - if(active) - new_character.key = key //now transfer the key to link the client to our new body - -/datum/mind/proc/store_memory(new_text) - memory += "[new_text]
    " - -/datum/mind/proc/wipe_memory() - memory = null - -/datum/mind/proc/show_memory(mob/recipient, window = 1) - if(!recipient) - recipient = current - var/output = "[current.real_name]'s Memories:
    " - output += memory - - var/antag_datum_objectives = FALSE - for(var/datum/antagonist/A in antag_datums) - output += A.antag_memory - if(!antag_datum_objectives && LAZYLEN(A.objectives)) - antag_datum_objectives = TRUE - - if(LAZYLEN(objectives) || antag_datum_objectives) - output += "
    Objectives:
    " - output += gen_objective_text() - - if(LAZYLEN(job_objectives)) - output += "
    Job Objectives:
      " - - var/obj_count = 1 - for(var/datum/job_objective/objective in job_objectives) - output += "
    • Task #[obj_count]: [objective.get_description()]
    • " - obj_count++ - output += "
    " - if(window) - recipient << browse(output, "window=memory") - else - to_chat(recipient, "[output]") - -/datum/mind/proc/gen_objective_text(admin = FALSE) - . = "" - var/obj_count = 1 - var/list/all_objectives = list() - for(var/datum/antagonist/A in antag_datums) - all_objectives |= A.objectives - - if(LAZYLEN(all_objectives)) - for(var/datum/objective/objective in all_objectives) - . += "
    Objective #[obj_count++]: [objective.explanation_text]" - - for(var/datum/objective/objective in objectives) - . += "Objective #[obj_count++]: [objective.explanation_text]" - if(admin) - . += " Edit " // Edit - . += "Delete " // Delete - - . += "" // Mark Completed - . += "Toggle Completion" - . += "" - . += "
    " - -/datum/mind/proc/_memory_edit_header(gamemode, list/alt) - . = gamemode - if(SSticker.mode.config_tag == gamemode || (LAZYLEN(alt) && (SSticker.mode.config_tag in alt))) - . = uppertext(.) - . = "[.]: " - -/datum/mind/proc/_memory_edit_role_enabled(role) - . = "|Disabled in Prefs" - if(current && current.client && (role in current.client.prefs.be_special)) - . = "|Enabled in Prefs" - -/datum/mind/proc/memory_edit_implant(mob/living/carbon/human/H) - if(ismindshielded(H)) - . = "Mindshield Implant:Remove|Implanted
    " - else - . = "Mindshield Implant:No Implant|Implant [H.p_them()]!
    " - - -/datum/mind/proc/memory_edit_revolution(mob/living/carbon/human/H) - . = _memory_edit_header("revolution") - if(ismindshielded(H)) - . += "NO|headrev|rev" - else if(src in SSticker.mode.head_revolutionaries) - . += "no|HEADREV|rev" - . += "
    Flash: give" - - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - if(flash) - if(!flash.broken) - . += "|take." - else - . += "|take|repair." - else - . += "." - - . += " Reequip (gives traitor uplink)." - if(objectives.len==0) - . += "
    Objectives are empty! Set to kill all heads." - else if(src in SSticker.mode.revolutionaries) - . += "no|headrev|REV" - else - . += "NO|headrev|rev" - - . += _memory_edit_role_enabled(ROLE_REV) - -/datum/mind/proc/memory_edit_cult(mob/living/carbon/human/H) - . = _memory_edit_header("cult") - if(src in SSticker.mode.cult) - . += "no|CULTIST" - . += "
    Give tome|equip." - else - . += "NO|cultist" - - . += _memory_edit_role_enabled(ROLE_CULTIST) - -/datum/mind/proc/memory_edit_wizard(mob/living/carbon/human/H) - . = _memory_edit_header("wizard") - if(src in SSticker.mode.wizards) - . += "WIZARD|no" - . += "
    To lair, undress, dress up, let choose name." - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - else - . += "wizard|NO" - - . += _memory_edit_role_enabled(ROLE_WIZARD) - -/datum/mind/proc/memory_edit_changeling(mob/living/carbon/human/H) - . = _memory_edit_header("changeling", list("traitorchan")) - if(src in SSticker.mode.changelings) - . += "CHANGELING|no" - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - if(changeling && changeling.absorbed_dna.len && (current.real_name != changeling.absorbed_dna[1])) - . += "
    Transform to initial appearance." - else - . += "changeling|NO" - - . += _memory_edit_role_enabled(ROLE_CHANGELING) - -/datum/mind/proc/memory_edit_vampire(mob/living/carbon/human/H) - . = _memory_edit_header("vampire", list("traitorvamp")) - if(src in SSticker.mode.vampires) - . += "VAMPIRE|no" - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - else - . += "vampire|NO" - - . += _memory_edit_role_enabled(ROLE_VAMPIRE) - /** Enthralled ***/ - . += "
    enthralled: " - if(src in SSticker.mode.vampire_enthralled) - . += "THRALL|no" - else - . += "thrall|NO" - -/datum/mind/proc/memory_edit_nuclear(mob/living/carbon/human/H) - . = _memory_edit_header("nuclear") - if(src in SSticker.mode.syndicates) - . += "OPERATIVE|no" - . += "
    To shuttle, undress, dress up." - var/code - for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if(code) - . += " Code is [code]. tell the code." - else - . += "operative|NO" - - . += _memory_edit_role_enabled(ROLE_OPERATIVE) - -/datum/mind/proc/memory_edit_shadowling(mob/living/carbon/human/H) - . = _memory_edit_header("shadowling") - if(src in SSticker.mode.shadows) - . += "SHADOWLING|thrall|no" - else if(src in SSticker.mode.shadowling_thralls) - . += "Shadowling|THRALL|no" - else - . += "shadowling|thrall|NO" - - . += _memory_edit_role_enabled(ROLE_SHADOWLING) - -/datum/mind/proc/memory_edit_abductor(mob/living/carbon/human/H) - . = _memory_edit_header("abductor") - if(src in SSticker.mode.abductors) - . += "ABDUCTOR|no" - . += "|undress|equip" - else - . += "abductor|NO" - - . += _memory_edit_role_enabled(ROLE_ABDUCTOR) - -/datum/mind/proc/memory_edit_devil(mob/living/H) - . = _memory_edit_header("devil", list("devilagents")) - if(src in SSticker.mode.devils) - if(!devilinfo) - . += "No devilinfo found! Yell at a coder!" - else if(!devilinfo.ascendable) - . += "DEVIL|Ascendable Devil|sintouched|no" - else - . += "DEVIL|ASCENDABLE DEVIL|sintouched|no" - else if(src in SSticker.mode.sintouched) - . += "devil|Ascendable Devil|SINTOUCHED|no" - else - . += "devil|Ascendable Devil|sintouched|NO" - - . += _memory_edit_role_enabled(ROLE_DEVIL) - -/datum/mind/proc/memory_edit_eventmisc(mob/living/H) - . = _memory_edit_header("event", list()) - if(src in SSticker.mode.eventmiscs) - . += "YES|no" - else - . += "Event Role|NO" - -/datum/mind/proc/memory_edit_traitor() - . = _memory_edit_header("traitor", list("traitorchan", "traitorvamp")) - if(has_antag_datum(/datum/antagonist/traitor)) - . += "TRAITOR|no" - if(objectives.len==0) - . += "
    Objectives are empty! Randomize!" - else - . += "traitor|NO" - - . += _memory_edit_role_enabled(ROLE_TRAITOR) - // Mindslave - . += "
    mindslaved: " - if(has_antag_datum(/datum/antagonist/mindslave)) - . += "MINDSLAVE|no" - else - . += "mindslave|NO" - -/datum/mind/proc/memory_edit_silicon() - . = "Silicon: " - var/mob/living/silicon/robot/robot = current - if(istype(robot) && robot.emagged) - . += "
    Cyborg: Is emagged! Unemag!
    0th law: [robot.laws.zeroth_law]" - var/mob/living/silicon/ai/ai = current - if(istype(ai) && ai.connected_robots.len) - var/n_e_robots = 0 - for(var/mob/living/silicon/robot/R in ai.connected_robots) - if(R.emagged) - n_e_robots++ - . += "
    [n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" - -/datum/mind/proc/memory_edit_uplink() - . = "" - if(ishuman(current) && ((src in SSticker.mode.head_revolutionaries) || \ - (has_antag_datum(/datum/antagonist/traitor)) || \ - (src in SSticker.mode.syndicates))) - . = "Uplink: give" - var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals - if(suplink) - crystals = suplink.uses - if(suplink) - . += "|take" - if(usr.client.holder.rights & (R_SERVER|R_EVENT)) - . += ", [crystals] crystals" - else - . += ", [crystals] crystals" - . += "." //hiel grammar - // ^ whoever left this comment is literally a grammar nazi. stalin better. in russia grammar correct you. - -/datum/mind/proc/edit_memory() - if(!SSticker || !SSticker.mode) - alert("Not before round-start!", "Alert") - return - - var/out = "[name][(current && (current.real_name != name))?" (as [current.real_name])" : ""]
    " - out += "Mind currently owned by key: [key] [active ? "(synced)" : "(not synced)"]
    " - out += "Assigned role: [assigned_role]. Edit
    " - out += "Factions and special roles:
    " - - var/list/sections = list( - "implant", - "revolution", - "cult", - "wizard", - "changeling", - "vampire", // "traitorvamp", - "nuclear", - "traitor", // "traitorchan", - ) - var/mob/living/carbon/human/H = current - if(ishuman(current)) - /** Impanted**/ - sections["implant"] = memory_edit_implant(H) - /** REVOLUTION ***/ - sections["revolution"] = memory_edit_revolution(H) - /** CULT ***/ - sections["cult"] = memory_edit_cult(H) - /** WIZARD ***/ - sections["wizard"] = memory_edit_wizard(H) - /** CHANGELING ***/ - sections["changeling"] = memory_edit_changeling(H) - /** VAMPIRE ***/ - sections["vampire"] = memory_edit_vampire(H) - /** NUCLEAR ***/ - sections["nuclear"] = memory_edit_nuclear(H) - /** SHADOWLING **/ - sections["shadowling"] = memory_edit_shadowling(H) - /** Abductors **/ - sections["abductor"] = memory_edit_abductor(H) - /** DEVIL ***/ - var/static/list/devils_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/carbon/true_devil, /mob/living/silicon/robot)) - if(is_type_in_typecache(current, devils_typecache)) - sections["devil"] = memory_edit_devil(H) - sections["eventmisc"] = memory_edit_eventmisc(H) - /** TRAITOR ***/ - sections["traitor"] = memory_edit_traitor() - /** SILICON ***/ - if(issilicon(current)) - sections["silicon"] = memory_edit_silicon() - /* - This prioritizes antags relevant to the current round to make them appear at the top of the panel. - Traitorchan and traitorvamp are snowflaked in because they have multiple sections. - */ - if(SSticker.mode.config_tag == "traitorchan") - if(sections["traitor"]) - out += sections["traitor"] + "
    " - if(sections["changeling"]) - out += sections["changeling"] + "
    " - sections -= "traitor" - sections -= "changeling" - // Elif technically unnecessary but it makes the following else look better - else if(SSticker.mode.config_tag == "traitorvamp") - if(sections["traitor"]) - out += sections["traitor"] + "
    " - if(sections["vampire"]) - out += sections["vampire"] + "
    " - sections -= "traitor" - sections -= "vampire" - else - if(sections[SSticker.mode.config_tag]) - out += sections[SSticker.mode.config_tag] + "
    " - sections -= SSticker.mode.config_tag - - for(var/i in sections) - if(sections[i]) - out += sections[i] + "
    " - - out += memory_edit_uplink() - out += "
    " - - out += "Memory:
    " - out += memory - out += "
    Edit memory
    " - out += "Objectives:
    " - if(objectives.len == 0) - out += "EMPTY
    " - else - out += gen_objective_text(admin = TRUE) - out += "Add objective

    " - out += "Announce objectives

    " - usr << browse(out, "window=edit_memory[src];size=500x500") - -/datum/mind/Topic(href, href_list) - if(!check_rights(R_ADMIN)) - return - - if(href_list["role_edit"]) - var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in GLOB.joblist - if(!new_role) - return - assigned_role = new_role - log_admin("[key_name(usr)] has changed [key_name(current)]'s assigned role to [assigned_role]") - message_admins("[key_name_admin(usr)] has changed [key_name_admin(current)]'s assigned role to [assigned_role]") - - else if(href_list["memory_edit"]) - var/messageinput = input("Write new memory", "Memory", memory) as null|message - if(isnull(messageinput)) - return - var/new_memo = copytext(messageinput, 1,MAX_MESSAGE_LEN) - var/confirmed = alert(usr, "Are you sure you want to edit their memory? It will wipe out their original memory!", "Edit Memory", "Yes", "No") - if(confirmed == "Yes") // Because it is too easy to accidentally wipe someone's memory - memory = new_memo - log_admin("[key_name(usr)] has edited [key_name(current)]'s memory") - message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory") - - else if(href_list["obj_edit"] || href_list["obj_add"]) - var/datum/objective/objective - var/objective_pos - var/def_value - - if(href_list["obj_edit"]) - objective = locate(href_list["obj_edit"]) - if(!objective) - return - objective_pos = objectives.Find(objective) - - //Text strings are easy to manipulate. Revised for simplicity. - var/temp_obj_type = "[objective.type]"//Convert path into a text string. - def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. - if(!def_value)//If it's a custom objective, it will be an empty string. - def_value = "custom" - - var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "identity theft", "custom") - if(!new_obj_type) - return - - var/datum/objective/new_objective = null - - switch(new_obj_type) - if("assassinate","protect","debrain", "brig", "maroon") - //To determine what to name the objective in explanation text. - var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. - var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. - var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. - - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) - possible_targets += possible_target.current - - var/mob/def_target = null - var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) - if(objective&&(objective.type in objective_list) && objective:target) - def_target = objective:target.current - possible_targets = sortAtom(possible_targets) - possible_targets += "Free objective" - - var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets - if(!new_target) - return - - var/objective_path = text2path("/datum/objective/[new_obj_type]") - if(new_target == "Free objective") - new_objective = new objective_path - new_objective.owner = src - new_objective:target = null - new_objective.explanation_text = "Free objective" - else - new_objective = new objective_path - new_objective.owner = src - new_objective:target = new_target:mind - //Will display as special role if assigned mode is equal to special role.. Ninjas/commandos/nuke ops. - new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role == new_target:mind:special_role ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." - - if("destroy") - var/list/possible_targets = active_ais(1) - if(possible_targets.len) - var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets - new_objective = new /datum/objective/destroy - new_objective.target = new_target.mind - new_objective.owner = src - new_objective.explanation_text = "Destroy [new_target.name], the experimental AI." - else - to_chat(usr, "No active AIs with minds") - - if("prevent") - new_objective = new /datum/objective/block - new_objective.owner = src - - if("hijack") - new_objective = new /datum/objective/hijack - new_objective.owner = src - - if("escape") - new_objective = new /datum/objective/escape - new_objective.owner = src - - if("survive") - new_objective = new /datum/objective/survive - new_objective.owner = src - - if("die") - new_objective = new /datum/objective/die - new_objective.owner = src - - if("nuclear") - new_objective = new /datum/objective/nuclear - new_objective.owner = src - - if("steal") - if(!istype(objective, /datum/objective/steal)) - new_objective = new /datum/objective/steal - new_objective.owner = src - else - new_objective = objective - var/datum/objective/steal/steal = new_objective - if(!steal.select_target()) - return - - if("download","capture","absorb", "blood") - var/def_num - if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) - def_num = objective.target_amount - - var/target_number = input("Input target number:", "Objective", def_num) as num|null - if(isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. - return - - switch(new_obj_type) - if("download") - new_objective = new /datum/objective/download - new_objective.explanation_text = "Download [target_number] research levels." - if("capture") - new_objective = new /datum/objective/capture - new_objective.explanation_text = "Accumulate [target_number] capture points." - if("absorb") - new_objective = new /datum/objective/absorb - new_objective.explanation_text = "Absorb [target_number] compatible genomes." - if("blood") - new_objective = new /datum/objective/blood - new_objective.explanation_text = "Accumulate at least [target_number] total units of blood." - new_objective.owner = src - new_objective.target_amount = target_number - - if("identity theft") - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if((possible_target != src) && ishuman(possible_target.current)) - possible_targets += possible_target - possible_targets = sortAtom(possible_targets) - possible_targets += "Free objective" - var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets - if(!new_target) - return - var/datum/mind/targ = new_target - if(!istype(targ)) - log_runtime(EXCEPTION("Invalid target for identity theft objective, cancelling"), src) - return - new_objective = new /datum/objective/escape/escape_with_identity - new_objective.owner = src - new_objective.target = new_target - new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing [targ.current.p_their()] identification card." - if("custom") - var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN)) - if(!expl) - return - new_objective = new /datum/objective - new_objective.owner = src - new_objective.explanation_text = expl - - if(!new_objective) - return - - if(objective) - objectives -= objective - qdel(objective) - objectives.Insert(objective_pos, new_objective) - else - objectives += new_objective - - log_admin("[key_name(usr)] has updated [key_name(current)]'s objectives: [new_objective]") - message_admins("[key_name_admin(usr)] has updated [key_name_admin(current)]'s objectives: [new_objective]") - - else if(href_list["obj_delete"]) - var/datum/objective/objective = locate(href_list["obj_delete"]) - if(!istype(objective)) - return - objectives -= objective - - log_admin("[key_name(usr)] has removed one of [key_name(current)]'s objectives: [objective]") - message_admins("[key_name_admin(usr)] has removed one of [key_name_admin(current)]'s objectives: [objective]") - qdel(objective) - - else if(href_list["obj_completed"]) - var/datum/objective/objective = locate(href_list["obj_completed"]) - if(!istype(objective)) - return - objective.completed = !objective.completed - - log_admin("[key_name(usr)] has toggled the completion of one of [key_name(current)]'s objectives") - message_admins("[key_name_admin(usr)] has toggled the completion of one of [key_name_admin(current)]'s objectives") - - else if(href_list["implant"]) - var/mob/living/carbon/human/H = current - - switch(href_list["implant"]) - if("remove") - for(var/obj/item/implant/mindshield/I in H.contents) - if(I && I.implanted) - qdel(I) - to_chat(H, "Your mindshield implant has been deactivated.") - log_admin("[key_name(usr)] has deactivated [key_name(current)]'s mindshield implant") - message_admins("[key_name_admin(usr)] has deactivated [key_name_admin(current)]'s mindshield implant") - if("add") - var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(H) - L.implant(H) - - log_admin("[key_name(usr)] has given [key_name(current)] a mindshield implant") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a mindshield implant") - - to_chat(H, "You somehow have become the recepient of a mindshield transplant, and it just activated!") - if(src in SSticker.mode.revolutionaries) - special_role = null - SSticker.mode.revolutionaries -= src - to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") - if(src in SSticker.mode.head_revolutionaries) - special_role = null - SSticker.mode.head_revolutionaries -=src - to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") - if(src in SSticker.mode.cult) - SSticker.mode.cult -= src - SSticker.mode.update_cult_icons_removed(src) - special_role = null - var/datum/game_mode/cult/cult = SSticker.mode - if(istype(cult)) - cult.memorize_cult_objectives(src) - to_chat(current, "The nanobots in the mindshield implant remove all thoughts about being in a cult. Have a productive day!") - memory = "" - - else if(href_list["revolution"]) - - switch(href_list["revolution"]) - if("clear") - if(src in SSticker.mode.revolutionaries) - SSticker.mode.revolutionaries -= src - to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") - SSticker.mode.update_rev_icons_removed(src) - special_role = null - if(src in SSticker.mode.head_revolutionaries) - SSticker.mode.head_revolutionaries -= src - to_chat(current, "You have been brainwashed! You are no longer a head revolutionary!") - SSticker.mode.update_rev_icons_removed(src) - special_role = null - log_admin("[key_name(usr)] has de-rev'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-rev'd [key_name_admin(current)]") - - if("rev") - if(src in SSticker.mode.head_revolutionaries) - SSticker.mode.head_revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - to_chat(current, "Revolution has been disappointed of your leadership traits! You are a regular revolutionary now!") - else if(!(src in SSticker.mode.revolutionaries)) - to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") - else - return - SSticker.mode.revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = SPECIAL_ROLE_REV - log_admin("[key_name(usr)] has rev'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has rev'd [key_name_admin(current)]") - - if("headrev") - if(src in SSticker.mode.revolutionaries) - SSticker.mode.revolutionaries -= src - SSticker.mode.update_rev_icons_removed(src) - to_chat(current, "You have proven your devotion to revolution! You are a head revolutionary now!") - else if(!(src in SSticker.mode.head_revolutionaries)) - to_chat(current, "You are a member of the revolutionaries' leadership now!") - else - return - if(SSticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries - if(valid_head) - for(var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." - objectives += rev_obj - SSticker.mode.greet_revolutionary(src,0) - SSticker.mode.head_revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = SPECIAL_ROLE_HEAD_REV - log_admin("[key_name(usr)] has head-rev'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has head-rev'd [key_name_admin(current)]") - - if("autoobjectives") - SSticker.mode.forge_revolutionary_objectives(src) - SSticker.mode.greet_revolutionary(src,0) - log_admin("[key_name(usr)] has automatically forged revolutionary objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged revolutionary objectives for [key_name_admin(current)]") - - if("flash") - if(!SSticker.mode.equip_revolutionary(current)) - to_chat(usr, "Spawning flash failed!") - log_admin("[key_name(usr)] has given [key_name(current)] a flash") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a flash") - - if("takeflash") - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - if(!flash) - to_chat(usr, "Deleting flash failed!") - qdel(flash) - log_admin("[key_name(usr)] has taken [key_name(current)]'s flash") - message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s flash") - - if("repairflash") - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - if(!flash) - to_chat(usr, "Repairing flash failed!") - else - flash.broken = 0 - log_admin("[key_name(usr)] has repaired [key_name(current)]'s flash") - message_admins("[key_name_admin(usr)] has repaired [key_name_admin(current)]'s flash") - - if("reequip") - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - qdel(flash) - take_uplink() - var/fail = 0 - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - fail |= !T.equip_traitor(src) - fail |= !SSticker.mode.equip_revolutionary(current) - if(fail) - to_chat(usr, "Reequipping revolutionary goes wrong!") - return - log_admin("[key_name(usr)] has equipped [key_name(current)] as a revolutionary") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a revolutionary") - - else if(href_list["cult"]) - switch(href_list["cult"]) - if("clear") - if(src in SSticker.mode.cult) - SSticker.mode.remove_cultist(src) - special_role = null - log_admin("[key_name(usr)] has de-culted [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-culted [key_name_admin(current)]") - if("cultist") - if(!(src in SSticker.mode.cult)) - SSticker.mode.add_cultist(src) - special_role = SPECIAL_ROLE_CULTIST - to_chat(current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") - to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve [SSticker.cultdat.entity_title2] above all else. Bring It back.") - log_admin("[key_name(usr)] has culted [key_name(current)]") - message_admins("[key_name_admin(usr)] has culted [key_name_admin(current)]") - if(!summon_spots.len) - while(summon_spots.len < SUMMON_POSSIBILITIES) - var/area/summon = pick(return_sorted_areas() - summon_spots) - if(summon && is_station_level(summon.z) && summon.valid_territory) - summon_spots += summon - if("tome") - var/mob/living/carbon/human/H = current - if(istype(H)) - var/obj/item/tome/T = new(H) - - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = H.equip_in_one_of_slots(T, slots) - if(!where) - to_chat(usr, "Spawning tome failed!") - qdel(T) - else - to_chat(H, "A tome, a message from your new master, appears in your [where].") - log_admin("[key_name(usr)] has spawned a tome for [key_name(current)]") - message_admins("[key_name_admin(usr)] has spawned a tome for [key_name_admin(current)]") - - if("equip") - if(!SSticker.mode.equip_cultist(current)) - to_chat(usr, "Spawning equipment failed!") - log_admin("[key_name(usr)] has equipped [key_name(current)] as a cultist") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a cultist") - - else if(href_list["wizard"]) - - switch(href_list["wizard"]) - if("clear") - if(src in SSticker.mode.wizards) - SSticker.mode.wizards -= src - special_role = null - current.spellremove(current) - current.faction = list("Station") - SSticker.mode.update_wiz_icons_removed(src) - to_chat(current, "You have been brainwashed! You are no longer a wizard!") - log_admin("[key_name(usr)] has de-wizarded [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-wizarded [key_name_admin(current)]") - if("wizard") - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src - special_role = SPECIAL_ROLE_WIZARD - //ticker.mode.learn_basic_spells(current) - SSticker.mode.update_wiz_icons_added(src) - SEND_SOUND(current, 'sound/ambience/antag/ragesmages.ogg') - to_chat(current, "You are a Space Wizard!") - current.faction = list("wizard") - log_admin("[key_name(usr)] has wizarded [key_name(current)]") - message_admins("[key_name_admin(usr)] has wizarded [key_name_admin(current)]") - if("lair") - current.forceMove(pick(wizardstart)) - log_admin("[key_name(usr)] has moved [key_name(current)] to the wizard's lair") - message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the wizard's lair") - if("dressup") - SSticker.mode.equip_wizard(current) - log_admin("[key_name(usr)] has equipped [key_name(current)] as a wizard") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a wizard") - if("name") - SSticker.mode.name_wizard(current) - log_admin("[key_name(usr)] has allowed wizard [key_name(current)] to name themselves") - message_admins("[key_name_admin(usr)] has allowed wizard [key_name_admin(current)] to name themselves") - if("autoobjectives") - SSticker.mode.forge_wizard_objectives(src) - to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged wizard objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged wizard objectives for [key_name_admin(current)]") - - - else if(href_list["changeling"]) - switch(href_list["changeling"]) - if("clear") - if(src in SSticker.mode.changelings) - SSticker.mode.changelings -= src - special_role = null - if(changeling) - current.remove_changeling_powers() - qdel(changeling) - changeling = null - SSticker.mode.update_change_icons_removed(src) - to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") - log_admin("[key_name(usr)] has de-changelinged [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-changelinged [key_name_admin(current)]") - if("changeling") - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - SSticker.mode.grant_changeling_powers(current) - SSticker.mode.update_change_icons_added(src) - special_role = SPECIAL_ROLE_CHANGELING - SEND_SOUND(current, 'sound/ambience/antag/ling_aler.ogg') - to_chat(current, "Your powers have awoken. A flash of memory returns to us... we are a changeling!") - log_admin("[key_name(usr)] has changelinged [key_name(current)]") - message_admins("[key_name_admin(usr)] has changelinged [key_name_admin(current)]") - - if("autoobjectives") - SSticker.mode.forge_changeling_objectives(src) - to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") - - if("initialdna") - if(!changeling || !changeling.absorbed_dna.len) - to_chat(usr, "Resetting DNA failed!") - else - current.dna = changeling.absorbed_dna[1] - current.real_name = current.dna.real_name - current.UpdateAppearance() - domutcheck(current, null) - log_admin("[key_name(usr)] has reset [key_name(current)]'s DNA") - message_admins("[key_name_admin(usr)] has reset [key_name_admin(current)]'s DNA") - - else if(href_list["vampire"]) - switch(href_list["vampire"]) - if("clear") - if(src in SSticker.mode.vampires) - SSticker.mode.vampires -= src - special_role = null - if(vampire) - vampire.remove_vampire_powers() - qdel(vampire) - vampire = null - SSticker.mode.update_vampire_icons_removed(src) - to_chat(current, "You grow weak and lose your powers! You are no longer a vampire and are stuck in your current form!") - log_admin("[key_name(usr)] has de-vampired [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-vampired [key_name_admin(current)]") - if("vampire") - if(!(src in SSticker.mode.vampires)) - SSticker.mode.vampires += src - SSticker.mode.grant_vampire_powers(current) - SSticker.mode.update_vampire_icons_added(src) - var/datum/mindslaves/slaved = new() - slaved.masters += src - som = slaved //we MIGT want to mindslave someone - special_role = SPECIAL_ROLE_VAMPIRE - SEND_SOUND(current, 'sound/ambience/antag/vampalert.ogg') - to_chat(current, "Your powers have awoken. Your lust for blood grows... You are a Vampire!") - log_admin("[key_name(usr)] has vampired [key_name(current)]") - message_admins("[key_name_admin(usr)] has vampired [key_name_admin(current)]") - - if("autoobjectives") - SSticker.mode.forge_vampire_objectives(src) - to_chat(usr, "The objectives for vampire [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") - - else if(href_list["vampthrall"]) - switch(href_list["vampthrall"]) - if("clear") - if(src in SSticker.mode.vampire_enthralled) - SSticker.mode.remove_vampire_mind(src) - log_admin("[key_name(usr)] has de-vampthralled [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-vampthralled [key_name_admin(current)]") - - else if(href_list["nuclear"]) - var/mob/living/carbon/human/H = current - - switch(href_list["nuclear"]) - if("clear") - if(src in SSticker.mode.syndicates) - SSticker.mode.syndicates -= src - SSticker.mode.update_synd_icons_removed(src) - special_role = null - for(var/datum/objective/nuclear/O in objectives) - objectives-=O - qdel(O) - to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") - log_admin("[key_name(usr)] has de-nuke op'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-nuke op'd [key_name_admin(current)]") - if("nuclear") - if(!(src in SSticker.mode.syndicates)) - SSticker.mode.syndicates += src - SSticker.mode.update_synd_icons_added(src) - if(SSticker.mode.syndicates.len==1) - SSticker.mode.prepare_syndicate_leader(src) - else - current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" - special_role = SPECIAL_ROLE_NUKEOPS - to_chat(current, "You are a [syndicate_name()] agent!") - SSticker.mode.forge_syndicate_objectives(src) - SSticker.mode.greet_syndicate(src) - log_admin("[key_name(usr)] has nuke op'd [key_name(current)]") - message_admins("[key_name_admin(usr)] has nuke op'd [key_name_admin(current)]") - if("lair") - current.forceMove(get_turf(locate("landmark*Syndicate-Spawn"))) - log_admin("[key_name(usr)] has moved [key_name(current)] to the nuclear operative spawn") - message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the nuclear operative spawn") - if("dressup") - qdel(H.belt) - qdel(H.back) - qdel(H.l_ear) - qdel(H.r_ear) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_pda) - qdel(H.wear_suit) - qdel(H.w_uniform) - - if(!SSticker.mode.equip_syndicate(current)) - to_chat(usr, "Equipping a syndicate failed!") - return - SSticker.mode.update_syndicate_id(current.mind, SSticker.mode.syndicates.len == 1) - log_admin("[key_name(usr)] has equipped [key_name(current)] as a nuclear operative") - message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a nuclear operative") - - if("tellcode") - var/code - for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) - if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") - code = bombue.r_code - break - if(code) - store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) - to_chat(current, "The nuclear authorization code is: [code]") - log_admin("[key_name(usr)] has given [key_name(current)] the nuclear authorization code") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] the nuclear authorization code") - else - to_chat(usr, "No valid nuke found!") - - else if(href_list["eventmisc"]) - switch(href_list["eventmisc"]) - if("clear") - if(src in SSticker.mode.eventmiscs) - SSticker.mode.eventmiscs -= src - SSticker.mode.update_eventmisc_icons_removed(src) - special_role = null - message_admins("[key_name_admin(usr)] has de-eventantag'ed [current].") - log_admin("[key_name(usr)] has de-eventantag'ed [current].") - if("eventmisc") - SSticker.mode.eventmiscs += src - special_role = SPECIAL_ROLE_EVENTMISC - SSticker.mode.update_eventmisc_icons_added(src) - message_admins("[key_name_admin(usr)] has eventantag'ed [current].") - log_admin("[key_name(usr)] has eventantag'ed [current].") - else if(href_list["devil"]) - switch(href_list["devil"]) - if("clear") - if(src in SSticker.mode.devils) - if(istype(current,/mob/living/carbon/true_devil/)) - to_chat(usr,"This cannot be used on true or arch-devils.") - else - SSticker.mode.devils -= src - SSticker.mode.update_devil_icons_removed(src) - special_role = null - to_chat(current,"Your infernal link has been severed! You are no longer a devil!") - RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) - RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) - RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) - message_admins("[key_name_admin(usr)] has de-devil'ed [current].") - if(issilicon(current)) - var/mob/living/silicon/S = current - S.laws.clear_sixsixsix_laws() - devilinfo = null - log_admin("[key_name(usr)] has de-devil'ed [current].") - else if(src in SSticker.mode.sintouched) - SSticker.mode.sintouched -= src - message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") - log_admin("[key_name(usr)] has de-sintouch'ed [current].") - if("devil") - if(devilinfo) - devilinfo.ascendable = FALSE - message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") - return - if(!ishuman(current) && !isrobot(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.update_devil_icons_added(src) - SSticker.mode.finalize_devil(src, FALSE) - SSticker.mode.forge_devil_objectives(src, 2) - SSticker.mode.greet_devil(src) - message_admins("[key_name_admin(usr)] has devil'ed [current].") - log_admin("[key_name(usr)] has devil'ed [current].") - if("ascendable_devil") - if(devilinfo) - devilinfo.ascendable = TRUE - message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") - return - if(!ishuman(current) && !isrobot(current)) - to_chat(usr, "This only works on humans and cyborgs!") - return - SSticker.mode.devils += src - special_role = "devil" - SSticker.mode.update_devil_icons_added(src) - SSticker.mode.finalize_devil(src, TRUE) - SSticker.mode.forge_devil_objectives(src, 2) - SSticker.mode.greet_devil(src) - message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") - if("sintouched") - var/mob/living/carbon/human/H = current - H.influenceSin() - message_admins("[key_name_admin(usr)] has sintouch'ed [current].") - log_admin("[key_name(usr)] has sintouch'ed [current].") - - else if(href_list["traitor"]) - switch(href_list["traitor"]) - if("clear") - if(has_antag_datum(/datum/antagonist/traitor)) - to_chat(current, "You have been brainwashed! You are no longer a traitor!") - remove_antag_datum(/datum/antagonist/traitor) - log_admin("[key_name(usr)] has de-traitored [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-traitored [key_name_admin(current)]") - - if("traitor") - if(!(has_antag_datum(/datum/antagonist/traitor))) - var/datum/antagonist/traitor/T = new() - T.give_objectives = FALSE - T.should_equip = FALSE - add_antag_datum(T) - log_admin("[key_name(usr)] has traitored [key_name(current)]") - message_admins("[key_name_admin(usr)] has traitored [key_name_admin(current)]") - - if("autoobjectives") - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - T.forge_traitor_objectives(src) - to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and announce manually.") - log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") - message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") - - else if(href_list["mindslave"]) - switch(href_list["mindslave"]) - if("clear") - if(has_antag_datum(/datum/antagonist/mindslave)) - var/mob/living/carbon/human/H = current - for(var/i in H.contents) - if(istype(i, /obj/item/implant/traitor)) - qdel(i) - break - remove_antag_datum(/datum/antagonist/mindslave) - log_admin("[key_name(usr)] has de-mindslaved [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-mindslaved [key_name_admin(current)]") - - else if(href_list["shadowling"]) - switch(href_list["shadowling"]) - if("clear") - SSticker.mode.update_shadow_icons_removed(src) - if(src in SSticker.mode.shadows) - SSticker.mode.shadows -= src - special_role = null - to_chat(current, "Your powers have been quenched! You are no longer a shadowling!") - message_admins("[key_name_admin(usr)] has de-shadowlinged [current].") - log_admin("[key_name(usr)] has de-shadowlinged [current].") - current.spellremove(current) - current.remove_language("Shadowling Hivemind") - else if(src in SSticker.mode.shadowling_thralls) - SSticker.mode.remove_thrall(src,0) - message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") - log_admin("[key_name(usr)] has de-thralled [key_name(current)]") - message_admins("[key_name_admin(usr)] has de-thralled [key_name_admin(current)]") - if("shadowling") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - SSticker.mode.shadows += src - special_role = SPECIAL_ROLE_SHADOWLING - to_chat(current, "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ - time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ - ") - SSticker.mode.finalize_shadowling(src) - SSticker.mode.update_shadow_icons_added(src) - log_admin("[key_name(usr)] has shadowlinged [key_name(current)]") - message_admins("[key_name_admin(usr)] has shadowlinged [key_name_admin(current)]") - if("thrall") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - SSticker.mode.add_thrall(src) - message_admins("[key_name_admin(usr)] has thralled [current].") - log_admin("[key_name(usr)] has thralled [current].") - - else if(href_list["abductor"]) - switch(href_list["abductor"]) - if("clear") - to_chat(usr, "Not implemented yet. Sorry!") - //ticker.mode.update_abductor_icons_removed(src) - if("abductor") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - make_Abductor() - log_admin("[key_name(usr)] turned [current] into abductor.") - SSticker.mode.update_abductor_icons_added(src) - if("equip") - if(!ishuman(current)) - to_chat(usr, "This only works on humans!") - return - - var/mob/living/carbon/human/H = current - var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") - if(gear) - if(gear=="Agent") - H.equipOutfit(/datum/outfit/abductor/agent) - else - H.equipOutfit(/datum/outfit/abductor/scientist) - - else if(href_list["silicon"]) - switch(href_list["silicon"]) - if("unemag") - var/mob/living/silicon/robot/R = current - if(istype(R)) - R.emagged = 0 - if(R.module) - if(R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - R.clear_supplied_laws() - R.laws = new /datum/ai_laws/crewsimov - log_admin("[key_name(usr)] has un-emagged [key_name(current)]") - message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") - - if("unemagcyborgs") - if(isAI(current)) - var/mob/living/silicon/ai/ai = current - for(var/mob/living/silicon/robot/R in ai.connected_robots) - R.emagged = 0 - if(R.module) - if(R.activated(R.module.emag)) - R.module_active = null - if(R.module_state_1 == R.module.emag) - R.module_state_1 = null - R.contents -= R.module.emag - else if(R.module_state_2 == R.module.emag) - R.module_state_2 = null - R.contents -= R.module.emag - else if(R.module_state_3 == R.module.emag) - R.module_state_3 = null - R.contents -= R.module.emag - R.clear_supplied_laws() - R.laws = new /datum/ai_laws/crewsimov - log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") - message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") - - else if(href_list["common"]) - switch(href_list["common"]) - if("undress") - if(ishuman(current)) - var/mob/living/carbon/human/H = current - // Don't "undress" organs right out of the body - for(var/obj/item/W in H.contents - (H.bodyparts | H.internal_organs)) - current.unEquip(W, 1) - else - for(var/obj/item/W in current) - current.unEquip(W, 1) - log_admin("[key_name(usr)] has unequipped [key_name(current)]") - message_admins("[key_name_admin(usr)] has unequipped [key_name_admin(current)]") - if("takeuplink") - take_uplink() - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - T.antag_memory = "" //Remove any antag memory they may have had (uplink codes, code phrases) - log_admin("[key_name(usr)] has taken [key_name(current)]'s uplink") - message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s uplink") - if("crystals") - if(usr.client.holder.rights & (R_SERVER|R_EVENT)) - var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() - var/crystals - if(suplink) - crystals = suplink.uses - crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num - if(!isnull(crystals)) - if(suplink) - suplink.uses = crystals - log_admin("[key_name(usr)] has set [key_name(current)]'s telecrystals to [crystals]") - message_admins("[key_name_admin(usr)] has set [key_name_admin(current)]'s telecrystals to [crystals]") - if("uplink") - if(has_antag_datum(/datum/antagonist/traitor)) - var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) - T.give_codewords() - if(!T.equip_traitor(src)) - to_chat(usr, "Equipping a syndicate failed!") - return - log_admin("[key_name(usr)] has given [key_name(current)] an uplink") - message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] an uplink") - - else if(href_list["obj_announce"]) - announce_objectives() - SEND_SOUND(current, sound('sound/ambience/alarm4.ogg')) - log_admin("[key_name(usr)] has announced [key_name(current)]'s objectives") - message_admins("[key_name_admin(usr)] has announced [key_name_admin(current)]'s objectives") - - edit_memory() - - -// Datum antag mind procs -/datum/mind/proc/add_antag_datum(datum_type_or_instance, team) - if(!datum_type_or_instance) - return - var/datum/antagonist/A - if(!ispath(datum_type_or_instance)) - A = datum_type_or_instance - if(!istype(A)) - return - else - A = new datum_type_or_instance() - //Choose snowflake variation if antagonist handles it - var/datum/antagonist/S = A.specialization(src) - if(S && S != A) - qdel(A) - A = S - if(!A.can_be_owned(src)) - qdel(A) - return - A.owner = src - LAZYADD(antag_datums, A) - A.create_team(team) - var/datum/team/antag_team = A.get_team() - if(antag_team) - antag_team.add_member(src) - A.on_gain() - return A - -/datum/mind/proc/remove_antag_datum(datum_type) - if(!datum_type) - return - var/datum/antagonist/A = has_antag_datum(datum_type) - if(A) - A.on_removal() - return TRUE - - -/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. - for(var/a in antag_datums) - var/datum/antagonist/A = a - A.on_removal() - -/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) - if(!datum_type) - return - . = FALSE - for(var/a in antag_datums) - var/datum/antagonist/A = a - if(check_subtypes && istype(A, datum_type)) - return A - else if(A.type == datum_type) - return A - -/datum/mind/proc/announce_objectives() - to_chat(current, "Your current objectives:") - for(var/line in splittext(gen_objective_text(), "
    ")) - to_chat(current, line) - -/datum/mind/proc/find_syndicate_uplink() - var/list/L = current.get_contents() - for(var/obj/item/I in L) - if(I.hidden_uplink) - return I.hidden_uplink - return null - -/datum/mind/proc/take_uplink() - var/obj/item/uplink/hidden/H = find_syndicate_uplink() - if(H) - qdel(H) - -/datum/mind/proc/make_Traitor() - if(!has_antag_datum(/datum/antagonist/traitor)) - add_antag_datum(/datum/antagonist/traitor) - -/datum/mind/proc/make_Nuke() - if(!(src in SSticker.mode.syndicates)) - SSticker.mode.syndicates += src - SSticker.mode.update_synd_icons_added(src) - if(SSticker.mode.syndicates.len==1) - SSticker.mode.prepare_syndicate_leader(src) - else - current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" - special_role = SPECIAL_ROLE_NUKEOPS - assigned_role = SPECIAL_ROLE_NUKEOPS - to_chat(current, "You are a [syndicate_name()] agent!") - SSticker.mode.forge_syndicate_objectives(src) - SSticker.mode.greet_syndicate(src) - - current.loc = get_turf(locate("landmark*Syndicate-Spawn")) - - var/mob/living/carbon/human/H = current - qdel(H.belt) - qdel(H.back) - qdel(H.l_ear) - qdel(H.r_ear) - qdel(H.gloves) - qdel(H.head) - qdel(H.shoes) - qdel(H.wear_id) - qdel(H.wear_pda) - qdel(H.wear_suit) - qdel(H.w_uniform) - - SSticker.mode.equip_syndicate(current) - -/datum/mind/proc/make_Vampire() - if(!(src in SSticker.mode.vampires)) - SSticker.mode.vampires += src - SSticker.mode.grant_vampire_powers(current) - special_role = SPECIAL_ROLE_VAMPIRE - SSticker.mode.forge_vampire_objectives(src) - SSticker.mode.greet_vampire(src) - SSticker.mode.update_vampire_icons_added(src) - -/datum/mind/proc/make_Changeling() - if(!(src in SSticker.mode.changelings)) - SSticker.mode.changelings += src - SSticker.mode.grant_changeling_powers(current) - special_role = SPECIAL_ROLE_CHANGELING - SSticker.mode.forge_changeling_objectives(src) - SSticker.mode.greet_changeling(src) - SSticker.mode.update_change_icons_added(src) - -/datum/mind/proc/make_Overmind() - if(!(src in SSticker.mode.blob_overminds)) - SSticker.mode.blob_overminds += src - special_role = SPECIAL_ROLE_BLOB_OVERMIND - -/datum/mind/proc/make_Wizard() - if(!(src in SSticker.mode.wizards)) - SSticker.mode.wizards += src - special_role = SPECIAL_ROLE_WIZARD - assigned_role = SPECIAL_ROLE_WIZARD - //ticker.mode.learn_basic_spells(current) - if(!wizardstart.len) - current.loc = pick(latejoin) - to_chat(current, "HOT INSERTION, GO GO GO") - else - current.loc = pick(wizardstart) - - SSticker.mode.equip_wizard(current) - for(var/obj/item/spellbook/S in current.contents) - S.op = 0 - SSticker.mode.name_wizard(current) - SSticker.mode.forge_wizard_objectives(src) - SSticker.mode.greet_wizard(src) - SSticker.mode.update_wiz_icons_added(src) - -/datum/mind/proc/make_Rev() - if(SSticker.mode.head_revolutionaries.len>0) - // copy targets - var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries - if(valid_head) - for(var/datum/objective/mutiny/O in valid_head.objectives) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = src - rev_obj.target = O.target - rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." - objectives += rev_obj - SSticker.mode.greet_revolutionary(src,0) - SSticker.mode.head_revolutionaries += src - SSticker.mode.update_rev_icons_added(src) - special_role = SPECIAL_ROLE_HEAD_REV - - SSticker.mode.forge_revolutionary_objectives(src) - SSticker.mode.greet_revolutionary(src,0) - - var/list/L = current.get_contents() - var/obj/item/flash/flash = locate() in L - qdel(flash) - take_uplink() - var/fail = 0 -// fail |= !ticker.mode.equip_traitor(current, 1) - fail |= !SSticker.mode.equip_revolutionary(current) - -/datum/mind/proc/make_Abductor() - var/role = alert("Abductor Role ?","Role","Agent","Scientist") - var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) - var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") - - if(!role || !team || !teleport) - return - - if(!ishuman(current)) - return - - SSticker.mode.abductors |= src - - var/datum/objective/stay_hidden/hidden_obj = new - hidden_obj.owner = src - objectives += hidden_obj - - var/datum/objective/experiment/O = new - O.owner = src - objectives += O - - var/mob/living/carbon/human/H = current - - H.set_species(/datum/species/abductor) - var/datum/species/abductor/S = H.dna.species - - if(role == "Scientist") - S.scientist = TRUE - - S.team = team - - var/list/obj/effect/landmark/abductor/agent_landmarks = new - var/list/obj/effect/landmark/abductor/scientist_landmarks = new - agent_landmarks.len = 4 - scientist_landmarks.len = 4 - for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list) - if(istype(A, /obj/effect/landmark/abductor/agent)) - agent_landmarks[text2num(A.team)] = A - else if(istype(A, /obj/effect/landmark/abductor/scientist)) - scientist_landmarks[text2num(A.team)] = A - - var/obj/effect/landmark/L - if(teleport == "Yes") - switch(role) - if("Agent") - L = agent_landmarks[team] - if("Scientist") - L = agent_landmarks[team] - H.forceMove(L.loc) - - -// check whether this mind's mob has been brigged for the given duration -// have to call this periodically for the duration to work properly -/datum/mind/proc/is_brigged(duration) - var/turf/T = current.loc - if(!istype(T)) - brigged_since = -1 - return 0 - - var/is_currently_brigged = current.is_in_brig() - if(!is_currently_brigged) - brigged_since = -1 - return 0 - - if(brigged_since == -1) - brigged_since = world.time - - return (duration <= world.time - brigged_since) - -/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) - spell_list += S - S.action.Grant(current) - -/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) //To remove a specific spell from a mind - if(!spell) - return - for(var/obj/effect/proc_holder/spell/S in spell_list) - if(istype(S, spell)) - qdel(S) - spell_list -= S - -/datum/mind/proc/transfer_actions(mob/living/new_character) - if(current && current.actions) - for(var/datum/action/A in current.actions) - A.Grant(new_character) - transfer_mindbound_actions(new_character) - -/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - S.action.Grant(new_character) - -/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) - for(var/X in spell_list) - var/obj/effect/proc_holder/spell/S = X - for(var/type in exceptions) - if(istype(S, type)) - continue - S.charge_counter = delay - spawn(0) - S.start_recharge() - S.updateButtonIcon() - -/datum/mind/proc/get_ghost(even_if_they_cant_reenter) - for(var/mob/dead/observer/G in GLOB.dead_mob_list) - if(G.mind == src) - if(G.can_reenter_corpse || even_if_they_cant_reenter) - return G - break - -/datum/mind/proc/grab_ghost(force) - var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) - . = G - if(G) - G.reenter_corpse() - - -/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 6000, team_color = "red") - - zealot_master = missionary - - var/list/implanters - if(!(missionary.mind in SSticker.mode.implanter)) - SSticker.mode.implanter[missionary.mind] = list() - implanters = SSticker.mode.implanter[missionary.mind] - implanters.Add(src) - SSticker.mode.implanted.Add(src) - SSticker.mode.implanted[src] = missionary.mind - SSticker.mode.implanter[missionary.mind] = implanters - SSticker.mode.traitors += src - - - var/datum/objective/protect/zealot_objective = new - zealot_objective.target = missionary.mind - zealot_objective.owner = src - zealot_objective.explanation_text = "Obey every order from and protect [missionary.real_name], the [missionary.mind.assigned_role == missionary.mind.special_role ? (missionary.mind.special_role) : (missionary.mind.assigned_role)]." - objectives += zealot_objective - add_antag_datum(/datum/antagonist/mindslave) - - var/datum/antagonist/traitor/T = missionary.mind.has_antag_datum(/datum/antagonist) - T.update_traitor_icons_added(missionary.mind) - - to_chat(current, "You're now a loyal zealot of [missionary.name]! You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.") - - var/datum/mindslaves/slaved = missionary.mind.som - som = slaved - slaved.serv += current - slaved.add_serv_hud(missionary.mind, "master") //handles master servent icons - slaved.add_serv_hud(src, "mindslave") - - var/obj/item/clothing/under/jumpsuit = null - if(ishuman(current)) //only bother with the jumpsuit stuff if we are a human type, since we won't have the slot otherwise - var/mob/living/carbon/human/H = current - if(H.w_uniform) - jumpsuit = H.w_uniform - jumpsuit.color = team_color - H.update_inv_w_uniform(0,0) - - add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") - addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires - return 1 - -/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) - if(!zealot_master) //if they aren't a zealot, we can't remove their zealot status, obviously. don't bother with the rest so we don't confuse them with the messages - return - remove_antag_datum(/datum/antagonist/mindslave) - add_attack_logs(zealot_master, current, "Lost control of zealot") - zealot_master = null - - if(jumpsuit) - jumpsuit.color = initial(jumpsuit.color) //reset the jumpsuit no matter where our mind is - if(ishuman(current)) //but only try updating us if we are still a human type since it is a human proc - var/mob/living/carbon/human/H = current - H.update_inv_w_uniform(0,0) - - to_chat(current, "You seem to have forgotten the events of the past 10 minutes or so, and your head aches a bit as if someone beat it savagely with a stick.") - to_chat(current, "This means you don't remember who you were working for or what you were doing.") - -/datum/mind/proc/is_revivable() //Note, this ONLY checks the mind. - if(damnation_type) - return FALSE - return TRUE - -// returns a mob to message to produce something visible for the target mind -/datum/mind/proc/messageable_mob() - if(!QDELETED(current) && current.client) - return current - else - return get_ghost(even_if_they_cant_reenter = TRUE) - -//Initialisation procs -/mob/proc/mind_initialize() - if(mind) - mind.key = key - else - mind = new /datum/mind(key) - if(SSticker) - SSticker.minds += mind - else - error("mind_initialize(): No ticker ready yet! Please inform Carn") - if(!mind.name) - mind.name = real_name - mind.current = src - -//HUMAN -/mob/living/carbon/human/mind_initialize() - ..() - if(!mind.assigned_role) - mind.assigned_role = "Civilian" //defualt - -/mob/proc/sync_mind() - mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) - mind.active = 1 //indicates that the mind is currently synced with a client - -//slime -/mob/living/simple_animal/slime/mind_initialize() - ..() - mind.assigned_role = "slime" - -//XENO -/mob/living/carbon/alien/mind_initialize() - ..() - mind.assigned_role = "Alien" - //XENO HUMANOID -/mob/living/carbon/alien/humanoid/queen/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_QUEEN - -/mob/living/carbon/alien/humanoid/hunter/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_HUNTER - -/mob/living/carbon/alien/humanoid/drone/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_DRONE - -/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_SENTINEL - //XENO LARVA -/mob/living/carbon/alien/larva/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_XENOMORPH_LARVA - -//AI -/mob/living/silicon/ai/mind_initialize() - ..() - mind.assigned_role = "AI" - -//BORG -/mob/living/silicon/robot/mind_initialize() - ..() - mind.assigned_role = "Cyborg" - -//PAI -/mob/living/silicon/pai/mind_initialize() - ..() - mind.assigned_role = "pAI" - mind.special_role = null - -//BLOB -/mob/camera/overmind/mind_initialize() - ..() - mind.special_role = SPECIAL_ROLE_BLOB - -//Animals -/mob/living/simple_animal/mind_initialize() - ..() - mind.assigned_role = "Animal" - -/mob/living/simple_animal/pet/dog/corgi/mind_initialize() - ..() - mind.assigned_role = "Corgi" - -/mob/living/simple_animal/shade/mind_initialize() - ..() - mind.assigned_role = "Shade" - -/mob/living/simple_animal/construct/builder/mind_initialize() - ..() - mind.assigned_role = "Artificer" - mind.special_role = SPECIAL_ROLE_CULTIST - -/mob/living/simple_animal/construct/wraith/mind_initialize() - ..() - mind.assigned_role = "Wraith" - mind.special_role = SPECIAL_ROLE_CULTIST - -/mob/living/simple_animal/construct/armoured/mind_initialize() - ..() - mind.assigned_role = "Juggernaut" - mind.special_role = SPECIAL_ROLE_CULTIST +/* Note from Carnie: + The way datum/mind stuff works has been changed a lot. + Minds now represent IC characters rather than following a client around constantly. + Guidelines for using minds properly: + - Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living! + ghost.mind is however used as a reference to the ghost's corpse + - When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human) + the existing mind of the old mob should be transfered to the new mob like so: + mind.transfer_to(new_mob) + - You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you. + By setting key or ckey explicitly after transfering the mind with transfer_to you will cause bugs like DCing + the player. + - IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you. + - When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting + a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done. + new_mob.key = key + The Login proc will handle making a new mob for that mobtype (including setting up stuff like mind.name). Simple! + However if you want that mind to have any special properties like being a traitor etc you will have to do that + yourself. +*/ + +/datum/mind + var/key + var/name //replaces mob/var/original_name + var/mob/living/current + var/mob/living/original //TODO: remove.not used in any meaningful way ~Carn. First I'll need to tweak the way silicon-mobs handle minds. + var/active = 0 + + var/memory + + var/assigned_role //assigned role is what job you're assigned to when you join the station. + var/playtime_role //if set, overrides your assigned_role for the purpose of playtime awards. Set by IDcomputer when your ID is changed. + var/special_role //special roles are typically reserved for antags or roles like ERT. If you want to avoid a character being automatically announced by the AI, on arrival (becuase they're an off station character or something); ensure that special_role and assigned_role are equal. + var/offstation_role = FALSE //set to true for ERT, deathsquad, abductors, etc, that can go from and to z2 at will and shouldn't be antag targets + var/list/restricted_roles = list() + + var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button. + + var/role_alt_title + + var/datum/job/assigned_job + var/list/kills = list() + var/list/datum/objective/objectives = list() + var/list/datum/objective/special_verbs = list() + var/list/targets = list() + + var/has_been_rev = 0//Tracks if this mind has been a rev or not + + var/miming = 0 // Mime's vow of silence + var/list/antag_datums + var/speech_span // What span any body this mind has talks in. + var/datum/faction/faction //associated faction + var/datum/changeling/changeling //changeling holder + var/linglink + var/datum/vampire/vampire //vampire holder + var/datum/abductor/abductor //abductor holder + var/datum/devilinfo/devilinfo //devil holder + + var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state + var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD + var/datum/mindslaves/som //stands for slave or master...hush.. + var/datum/devilinfo/devilinfo //Information about the devil, if any. + var/damnation_type = 0 + var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src + var/hasSoul = TRUE + + var/rev_cooldown = 0 + + var/isholy = FALSE // is this person a chaplain or admin role allowed to use bibles + var/isblessed = FALSE // is this person blessed by a chaplain? + var/num_blessed = 0 // for prayers + + // the world.time since the mob has been brigged, or -1 if not at all + var/brigged_since = -1 + var/suicided = FALSE + + //put this here for easier tracking ingame + var/datum/money_account/initial_account + + //zealot_master is a reference to the mob that converted them into a zealot (for ease of investigation and such) + var/mob/living/carbon/human/zealot_master = null + + var/list/learned_recipes //List of learned recipe TYPES. + +/datum/mind/New(new_key) + key = new_key + soulOwner = src + +/datum/mind/Destroy() + SSticker.minds -= src + if(islist(antag_datums)) + for(var/i in antag_datums) + var/datum/antagonist/antag_datum = i + if(antag_datum.delete_on_mind_deletion) + qdel(i) + antag_datums = null + return ..() + +/datum/mind/proc/transfer_to(mob/living/new_character) + var/datum/atom_hud/antag/hud_to_transfer = antag_hud //we need this because leave_hud() will clear this list + var/mob/living/old_current = current + if(!istype(new_character)) + log_runtime(EXCEPTION("transfer_to(): Some idiot has tried to transfer_to() a non mob/living mob."), src) + if(current) //remove ourself from our old body's mind variable + current.mind = null + leave_all_huds() //leave all the huds in the old body, so it won't get huds if somebody else enters it + + for(var/log_type in current.logs) // Copy the old logs + var/list/logs = current.logs[log_type] + if(new_character.logs[log_type]) + new_character.logs[log_type] += logs.Copy() // Append the old ones + new_character.logs[log_type] = sortTim(new_character.logs[log_type], /proc/compare_log_record) // Sort them on time + else + new_character.logs[log_type] = logs.Copy() // Just copy them + + SSnanoui.user_transferred(current, new_character) + + if(new_character.mind) //remove any mind currently in our new body's mind variable + new_character.mind.current = null + current = new_character //link ourself to our new body + new_character.mind = src //and link our new body to ourself + for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body + var/datum/antagonist/A = a + A.on_body_transfer(old_current, current) + transfer_antag_huds(hud_to_transfer) //inherit the antag HUD + transfer_actions(new_character) + + if(active) + new_character.key = key //now transfer the key to link the client to our new body + +/datum/mind/proc/store_memory(new_text) + memory += "[new_text]
    " + +/datum/mind/proc/wipe_memory() + memory = null + +/datum/mind/proc/show_memory(mob/recipient, window = 1) + if(!recipient) + recipient = current + var/output = "[current.real_name]'s Memories:
    " + output += memory + + var/antag_datum_objectives = FALSE + for(var/datum/antagonist/A in antag_datums) + output += A.antag_memory + if(!antag_datum_objectives && LAZYLEN(A.objectives)) + antag_datum_objectives = TRUE + + if(LAZYLEN(objectives) || antag_datum_objectives) + output += "
    Objectives:
    " + output += gen_objective_text() + + if(LAZYLEN(job_objectives)) + output += "
    Job Objectives:
      " + + var/obj_count = 1 + for(var/datum/job_objective/objective in job_objectives) + output += "
    • Task #[obj_count]: [objective.get_description()]
    • " + obj_count++ + output += "
    " + if(window) + recipient << browse(output, "window=memory") + else + to_chat(recipient, "[output]") + +/datum/mind/proc/gen_objective_text(admin = FALSE) + . = "" + var/obj_count = 1 + var/list/all_objectives = list() + for(var/datum/antagonist/A in antag_datums) + all_objectives |= A.objectives + + if(LAZYLEN(all_objectives)) + for(var/datum/objective/objective in all_objectives) + . += "
    Objective #[obj_count++]: [objective.explanation_text]" + + for(var/datum/objective/objective in objectives) + . += "Objective #[obj_count++]: [objective.explanation_text]" + if(admin) + . += " Edit " // Edit + . += "Delete " // Delete + + . += "" // Mark Completed + . += "Toggle Completion" + . += "" + . += "
    " + +/datum/mind/proc/_memory_edit_header(gamemode, list/alt) + . = gamemode + if(SSticker.mode.config_tag == gamemode || (LAZYLEN(alt) && (SSticker.mode.config_tag in alt))) + . = uppertext(.) + . = "[.]: " + +/datum/mind/proc/_memory_edit_role_enabled(role) + . = "|Disabled in Prefs" + if(current && current.client && (role in current.client.prefs.be_special)) + . = "|Enabled in Prefs" + +/datum/mind/proc/memory_edit_implant(mob/living/carbon/human/H) + if(ismindshielded(H)) + . = "Mindshield Implant:Remove|Implanted
    " + else + . = "Mindshield Implant:No Implant|Implant [H.p_them()]!
    " + + +/datum/mind/proc/memory_edit_revolution(mob/living/carbon/human/H) + . = _memory_edit_header("revolution") + if(ismindshielded(H)) + . += "NO|headrev|rev" + else if(src in SSticker.mode.head_revolutionaries) + . += "no|HEADREV|rev" + . += "
    Flash: give" + + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(flash) + if(!flash.broken) + . += "|take." + else + . += "|take|repair." + else + . += "." + + . += " Reequip (gives traitor uplink)." + if(objectives.len==0) + . += "
    Objectives are empty! Set to kill all heads." + else if(src in SSticker.mode.revolutionaries) + . += "no|headrev|REV" + else + . += "NO|headrev|rev" + + . += _memory_edit_role_enabled(ROLE_REV) + +/datum/mind/proc/memory_edit_cult(mob/living/carbon/human/H) + . = _memory_edit_header("cult") + if(src in SSticker.mode.cult) + . += "no|CULTIST" + . += "
    Give tome|equip." + else + . += "NO|cultist" + + . += _memory_edit_role_enabled(ROLE_CULTIST) + +/datum/mind/proc/memory_edit_wizard(mob/living/carbon/human/H) + . = _memory_edit_header("wizard") + if(src in SSticker.mode.wizards) + . += "WIZARD|no" + . += "
    To lair, undress, dress up, let choose name." + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + else + . += "wizard|NO" + + . += _memory_edit_role_enabled(ROLE_WIZARD) + +/datum/mind/proc/memory_edit_changeling(mob/living/carbon/human/H) + . = _memory_edit_header("changeling", list("traitorchan")) + if(src in SSticker.mode.changelings) + . += "CHANGELING|no" + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + if(changeling && changeling.absorbed_dna.len && (current.real_name != changeling.absorbed_dna[1])) + . += "
    Transform to initial appearance." + else + . += "changeling|NO" + + . += _memory_edit_role_enabled(ROLE_CHANGELING) + +/datum/mind/proc/memory_edit_vampire(mob/living/carbon/human/H) + . = _memory_edit_header("vampire", list("traitorvamp")) + if(src in SSticker.mode.vampires) + . += "VAMPIRE|no" + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + else + . += "vampire|NO" + + . += _memory_edit_role_enabled(ROLE_VAMPIRE) + /** Enthralled ***/ + . += "
    enthralled: " + if(src in SSticker.mode.vampire_enthralled) + . += "THRALL|no" + else + . += "thrall|NO" + +/datum/mind/proc/memory_edit_nuclear(mob/living/carbon/human/H) + . = _memory_edit_header("nuclear") + if(src in SSticker.mode.syndicates) + . += "OPERATIVE|no" + . += "
    To shuttle, undress, dress up." + var/code + for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if(code) + . += " Code is [code]. tell the code." + else + . += "operative|NO" + + . += _memory_edit_role_enabled(ROLE_OPERATIVE) + +/datum/mind/proc/memory_edit_shadowling(mob/living/carbon/human/H) + . = _memory_edit_header("shadowling") + if(src in SSticker.mode.shadows) + . += "SHADOWLING|thrall|no" + else if(src in SSticker.mode.shadowling_thralls) + . += "Shadowling|THRALL|no" + else + . += "shadowling|thrall|NO" + + . += _memory_edit_role_enabled(ROLE_SHADOWLING) + +/datum/mind/proc/memory_edit_abductor(mob/living/carbon/human/H) + . = _memory_edit_header("abductor") + if(src in SSticker.mode.abductors) + . += "ABDUCTOR|no" + . += "|undress|equip" + else + . += "abductor|NO" + + . += _memory_edit_role_enabled(ROLE_ABDUCTOR) + +/datum/mind/proc/memory_edit_devil(mob/living/H) + . = _memory_edit_header("devil", list("devilagents")) + if(src in SSticker.mode.devils) + if(!devilinfo) + . += "No devilinfo found! Yell at a coder!" + else if(!devilinfo.ascendable) + . += "DEVIL|Ascendable Devil|sintouched|no" + else + . += "DEVIL|ASCENDABLE DEVIL|sintouched|no" + else if(src in SSticker.mode.sintouched) + . += "devil|Ascendable Devil|SINTOUCHED|no" + else + . += "devil|Ascendable Devil|sintouched|NO" + + . += _memory_edit_role_enabled(ROLE_DEVIL) + +/datum/mind/proc/memory_edit_eventmisc(mob/living/H) + . = _memory_edit_header("event", list()) + if(src in SSticker.mode.eventmiscs) + . += "YES|no" + else + . += "Event Role|NO" + +/datum/mind/proc/memory_edit_traitor() + . = _memory_edit_header("traitor", list("traitorchan", "traitorvamp")) + if(has_antag_datum(/datum/antagonist/traitor)) + . += "TRAITOR|no" + if(objectives.len==0) + . += "
    Objectives are empty! Randomize!" + else + . += "traitor|NO" + + . += _memory_edit_role_enabled(ROLE_TRAITOR) + // Mindslave + . += "
    mindslaved: " + if(has_antag_datum(/datum/antagonist/mindslave)) + . += "MINDSLAVE|no" + else + . += "mindslave|NO" + +/datum/mind/proc/memory_edit_silicon() + . = "Silicon: " + var/mob/living/silicon/robot/robot = current + if(istype(robot) && robot.emagged) + . += "
    Cyborg: Is emagged! Unemag!
    0th law: [robot.laws.zeroth_law]" + var/mob/living/silicon/ai/ai = current + if(istype(ai) && ai.connected_robots.len) + var/n_e_robots = 0 + for(var/mob/living/silicon/robot/R in ai.connected_robots) + if(R.emagged) + n_e_robots++ + . += "
    [n_e_robots] of [ai.connected_robots.len] slaved cyborgs are emagged. Unemag" + +/datum/mind/proc/memory_edit_uplink() + . = "" + if(ishuman(current) && ((src in SSticker.mode.head_revolutionaries) || \ + (has_antag_datum(/datum/antagonist/traitor)) || \ + (src in SSticker.mode.syndicates))) + . = "Uplink: give" + var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if(suplink) + crystals = suplink.uses + if(suplink) + . += "|take" + if(usr.client.holder.rights & (R_SERVER|R_EVENT)) + . += ", [crystals] crystals" + else + . += ", [crystals] crystals" + . += "." //hiel grammar + // ^ whoever left this comment is literally a grammar nazi. stalin better. in russia grammar correct you. + +/datum/mind/proc/edit_memory() + if(!SSticker || !SSticker.mode) + alert("Not before round-start!", "Alert") + return + + var/out = "[name][(current && (current.real_name != name))?" (as [current.real_name])" : ""]
    " + out += "Mind currently owned by key: [key] [active ? "(synced)" : "(not synced)"]
    " + out += "Assigned role: [assigned_role]. Edit
    " + out += "Factions and special roles:
    " + + var/list/sections = list( + "implant", + "revolution", + "cult", + "wizard", + "changeling", + "vampire", // "traitorvamp", + "nuclear", + "traitor", // "traitorchan", + ) + var/mob/living/carbon/human/H = current + if(ishuman(current)) + /** Impanted**/ + sections["implant"] = memory_edit_implant(H) + /** REVOLUTION ***/ + sections["revolution"] = memory_edit_revolution(H) + /** CULT ***/ + sections["cult"] = memory_edit_cult(H) + /** WIZARD ***/ + sections["wizard"] = memory_edit_wizard(H) + /** CHANGELING ***/ + sections["changeling"] = memory_edit_changeling(H) + /** VAMPIRE ***/ + sections["vampire"] = memory_edit_vampire(H) + /** NUCLEAR ***/ + sections["nuclear"] = memory_edit_nuclear(H) + /** SHADOWLING **/ + sections["shadowling"] = memory_edit_shadowling(H) + /** Abductors **/ + sections["abductor"] = memory_edit_abductor(H) + /** DEVIL ***/ + var/static/list/devils_typecache = typecacheof(list(/mob/living/carbon/human, /mob/living/carbon/true_devil, /mob/living/silicon/robot)) + if(is_type_in_typecache(current, devils_typecache)) + sections["devil"] = memory_edit_devil(H) + sections["eventmisc"] = memory_edit_eventmisc(H) + /** TRAITOR ***/ + sections["traitor"] = memory_edit_traitor() + /** SILICON ***/ + if(issilicon(current)) + sections["silicon"] = memory_edit_silicon() + /* + This prioritizes antags relevant to the current round to make them appear at the top of the panel. + Traitorchan and traitorvamp are snowflaked in because they have multiple sections. + */ + if(SSticker.mode.config_tag == "traitorchan") + if(sections["traitor"]) + out += sections["traitor"] + "
    " + if(sections["changeling"]) + out += sections["changeling"] + "
    " + sections -= "traitor" + sections -= "changeling" + // Elif technically unnecessary but it makes the following else look better + else if(SSticker.mode.config_tag == "traitorvamp") + if(sections["traitor"]) + out += sections["traitor"] + "
    " + if(sections["vampire"]) + out += sections["vampire"] + "
    " + sections -= "traitor" + sections -= "vampire" + else + if(sections[SSticker.mode.config_tag]) + out += sections[SSticker.mode.config_tag] + "
    " + sections -= SSticker.mode.config_tag + + for(var/i in sections) + if(sections[i]) + out += sections[i] + "
    " + + out += memory_edit_uplink() + out += "
    " + + out += "Memory:
    " + out += memory + out += "
    Edit memory
    " + out += "Objectives:
    " + if(objectives.len == 0) + out += "EMPTY
    " + else + out += gen_objective_text(admin = TRUE) + out += "Add objective

    " + out += "Announce objectives

    " + usr << browse(out, "window=edit_memory[src];size=500x500") + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN)) + return + + if(href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in GLOB.joblist + if(!new_role) + return + assigned_role = new_role + log_admin("[key_name(usr)] has changed [key_name(current)]'s assigned role to [assigned_role]") + message_admins("[key_name_admin(usr)] has changed [key_name_admin(current)]'s assigned role to [assigned_role]") + + else if(href_list["memory_edit"]) + var/messageinput = input("Write new memory", "Memory", memory) as null|message + if(isnull(messageinput)) + return + var/new_memo = copytext(messageinput, 1,MAX_MESSAGE_LEN) + var/confirmed = alert(usr, "Are you sure you want to edit their memory? It will wipe out their original memory!", "Edit Memory", "Yes", "No") + if(confirmed == "Yes") // Because it is too easy to accidentally wipe someone's memory + memory = new_memo + log_admin("[key_name(usr)] has edited [key_name(current)]'s memory") + message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory") + + else if(href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if(href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if(!objective) + return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "identity theft", "custom") + if(!new_obj_type) + return + + var/datum/objective/new_objective = null + + switch(new_obj_type) + if("assassinate","protect","debrain", "brig", "maroon") + //To determine what to name the objective in explanation text. + var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. + var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. + var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. + + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) + if(objective&&(objective.type in objective_list) && objective:target) + def_target = objective:target.current + possible_targets = sortAtom(possible_targets) + possible_targets += "Free objective" + + var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + if(!new_target) + return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + if(new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = new_target:mind + //Will display as special role if assigned mode is equal to special role.. Ninjas/commandos/nuke ops. + new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role == new_target:mind:special_role ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." + + if("destroy") + var/list/possible_targets = active_ais(1) + if(possible_targets.len) + var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + new_objective = new /datum/objective/destroy + new_objective.target = new_target.mind + new_objective.owner = src + new_objective.explanation_text = "Destroy [new_target.name], the experimental AI." + else + to_chat(usr, "No active AIs with minds") + + if("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if("die") + new_objective = new /datum/objective/die + new_objective.owner = src + + if("nuclear") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if("steal") + if(!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if(!steal.select_target()) + return + + if("download","capture","absorb", "blood") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + if(isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Accumulate [target_number] capture points." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + if("blood") + new_objective = new /datum/objective/blood + new_objective.explanation_text = "Accumulate at least [target_number] total units of blood." + new_objective.owner = src + new_objective.target_amount = target_number + + if("identity theft") + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if((possible_target != src) && ishuman(possible_target.current)) + possible_targets += possible_target + possible_targets = sortAtom(possible_targets) + possible_targets += "Free objective" + var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + if(!new_target) + return + var/datum/mind/targ = new_target + if(!istype(targ)) + log_runtime(EXCEPTION("Invalid target for identity theft objective, cancelling"), src) + return + new_objective = new /datum/objective/escape/escape_with_identity + new_objective.owner = src + new_objective.target = new_target + new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing [targ.current.p_their()] identification card." + if("custom") + var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN)) + if(!expl) + return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if(!new_objective) + return + + if(objective) + objectives -= objective + qdel(objective) + objectives.Insert(objective_pos, new_objective) + else + objectives += new_objective + + log_admin("[key_name(usr)] has updated [key_name(current)]'s objectives: [new_objective]") + message_admins("[key_name_admin(usr)] has updated [key_name_admin(current)]'s objectives: [new_objective]") + + else if(href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) + return + objectives -= objective + + log_admin("[key_name(usr)] has removed one of [key_name(current)]'s objectives: [objective]") + message_admins("[key_name_admin(usr)] has removed one of [key_name_admin(current)]'s objectives: [objective]") + qdel(objective) + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) + return + objective.completed = !objective.completed + + log_admin("[key_name(usr)] has toggled the completion of one of [key_name(current)]'s objectives") + message_admins("[key_name_admin(usr)] has toggled the completion of one of [key_name_admin(current)]'s objectives") + + else if(href_list["implant"]) + var/mob/living/carbon/human/H = current + + switch(href_list["implant"]) + if("remove") + for(var/obj/item/implant/mindshield/I in H.contents) + if(I && I.implanted) + qdel(I) + to_chat(H, "Your mindshield implant has been deactivated.") + log_admin("[key_name(usr)] has deactivated [key_name(current)]'s mindshield implant") + message_admins("[key_name_admin(usr)] has deactivated [key_name_admin(current)]'s mindshield implant") + if("add") + var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(H) + L.implant(H) + + log_admin("[key_name(usr)] has given [key_name(current)] a mindshield implant") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a mindshield implant") + + to_chat(H, "You somehow have become the recepient of a mindshield transplant, and it just activated!") + if(src in SSticker.mode.revolutionaries) + special_role = null + SSticker.mode.revolutionaries -= src + to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") + if(src in SSticker.mode.head_revolutionaries) + special_role = null + SSticker.mode.head_revolutionaries -=src + to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") + if(src in SSticker.mode.cult) + SSticker.mode.cult -= src + SSticker.mode.update_cult_icons_removed(src) + special_role = null + var/datum/game_mode/cult/cult = SSticker.mode + if(istype(cult)) + cult.memorize_cult_objectives(src) + to_chat(current, "The nanobots in the mindshield implant remove all thoughts about being in a cult. Have a productive day!") + memory = "" + + else if(href_list["revolution"]) + + switch(href_list["revolution"]) + if("clear") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") + SSticker.mode.update_rev_icons_removed(src) + special_role = null + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + to_chat(current, "You have been brainwashed! You are no longer a head revolutionary!") + SSticker.mode.update_rev_icons_removed(src) + special_role = null + log_admin("[key_name(usr)] has de-rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-rev'd [key_name_admin(current)]") + + if("rev") + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "Revolution has been disappointed of your leadership traits! You are a regular revolutionary now!") + else if(!(src in SSticker.mode.revolutionaries)) + to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + else + return + SSticker.mode.revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_REV + log_admin("[key_name(usr)] has rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has rev'd [key_name_admin(current)]") + + if("headrev") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "You have proven your devotion to revolution! You are a head revolutionary now!") + else if(!(src in SSticker.mode.head_revolutionaries)) + to_chat(current, "You are a member of the revolutionaries' leadership now!") + else + return + if(SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if(valid_head) + for(var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_HEAD_REV + log_admin("[key_name(usr)] has head-rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has head-rev'd [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + log_admin("[key_name(usr)] has automatically forged revolutionary objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged revolutionary objectives for [key_name_admin(current)]") + + if("flash") + if(!SSticker.mode.equip_revolutionary(current)) + to_chat(usr, "Spawning flash failed!") + log_admin("[key_name(usr)] has given [key_name(current)] a flash") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a flash") + + if("takeflash") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(!flash) + to_chat(usr, "Deleting flash failed!") + qdel(flash) + log_admin("[key_name(usr)] has taken [key_name(current)]'s flash") + message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s flash") + + if("repairflash") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(!flash) + to_chat(usr, "Repairing flash failed!") + else + flash.broken = 0 + log_admin("[key_name(usr)] has repaired [key_name(current)]'s flash") + message_admins("[key_name_admin(usr)] has repaired [key_name_admin(current)]'s flash") + + if("reequip") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + fail |= !T.equip_traitor(src) + fail |= !SSticker.mode.equip_revolutionary(current) + if(fail) + to_chat(usr, "Reequipping revolutionary goes wrong!") + return + log_admin("[key_name(usr)] has equipped [key_name(current)] as a revolutionary") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a revolutionary") + + else if(href_list["cult"]) + switch(href_list["cult"]) + if("clear") + if(src in SSticker.mode.cult) + SSticker.mode.remove_cultist(src) + special_role = null + log_admin("[key_name(usr)] has de-culted [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-culted [key_name_admin(current)]") + if("cultist") + if(!(src in SSticker.mode.cult)) + SSticker.mode.add_cultist(src) + special_role = SPECIAL_ROLE_CULTIST + to_chat(current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") + to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve [SSticker.cultdat.entity_title2] above all else. Bring It back.") + log_admin("[key_name(usr)] has culted [key_name(current)]") + message_admins("[key_name_admin(usr)] has culted [key_name_admin(current)]") + if(!GLOB.summon_spots.len) + while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES) + var/area/summon = pick(return_sorted_areas() - GLOB.summon_spots) + if(summon && is_station_level(summon.z) && summon.valid_territory) + GLOB.summon_spots += summon + if("tome") + var/mob/living/carbon/human/H = current + if(istype(H)) + var/obj/item/tome/T = new(H) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = H.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(usr, "Spawning tome failed!") + qdel(T) + else + to_chat(H, "A tome, a message from your new master, appears in your [where].") + log_admin("[key_name(usr)] has spawned a tome for [key_name(current)]") + message_admins("[key_name_admin(usr)] has spawned a tome for [key_name_admin(current)]") + + if("equip") + if(!SSticker.mode.equip_cultist(current)) + to_chat(usr, "Spawning equipment failed!") + log_admin("[key_name(usr)] has equipped [key_name(current)] as a cultist") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a cultist") + + else if(href_list["wizard"]) + + switch(href_list["wizard"]) + if("clear") + if(src in SSticker.mode.wizards) + SSticker.mode.wizards -= src + special_role = null + current.spellremove(current) + current.faction = list("Station") + SSticker.mode.update_wiz_icons_removed(src) + to_chat(current, "You have been brainwashed! You are no longer a wizard!") + log_admin("[key_name(usr)] has de-wizarded [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-wizarded [key_name_admin(current)]") + if("wizard") + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = SPECIAL_ROLE_WIZARD + //ticker.mode.learn_basic_spells(current) + SSticker.mode.update_wiz_icons_added(src) + SEND_SOUND(current, 'sound/ambience/antag/ragesmages.ogg') + to_chat(current, "You are a Space Wizard!") + current.faction = list("wizard") + log_admin("[key_name(usr)] has wizarded [key_name(current)]") + message_admins("[key_name_admin(usr)] has wizarded [key_name_admin(current)]") + if("lair") + current.forceMove(pick(GLOB.wizardstart)) + log_admin("[key_name(usr)] has moved [key_name(current)] to the wizard's lair") + message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the wizard's lair") + if("dressup") + SSticker.mode.equip_wizard(current) + log_admin("[key_name(usr)] has equipped [key_name(current)] as a wizard") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a wizard") + if("name") + SSticker.mode.name_wizard(current) + log_admin("[key_name(usr)] has allowed wizard [key_name(current)] to name themselves") + message_admins("[key_name_admin(usr)] has allowed wizard [key_name_admin(current)] to name themselves") + if("autoobjectives") + SSticker.mode.forge_wizard_objectives(src) + to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged wizard objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged wizard objectives for [key_name_admin(current)]") + + + else if(href_list["changeling"]) + switch(href_list["changeling"]) + if("clear") + if(src in SSticker.mode.changelings) + SSticker.mode.changelings -= src + special_role = null + if(changeling) + current.remove_changeling_powers() + qdel(changeling) + changeling = null + SSticker.mode.update_change_icons_removed(src) + to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") + log_admin("[key_name(usr)] has de-changelinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-changelinged [key_name_admin(current)]") + if("changeling") + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + SSticker.mode.grant_changeling_powers(current) + SSticker.mode.update_change_icons_added(src) + special_role = SPECIAL_ROLE_CHANGELING + SEND_SOUND(current, 'sound/ambience/antag/ling_aler.ogg') + to_chat(current, "Your powers have awoken. A flash of memory returns to us... we are a changeling!") + log_admin("[key_name(usr)] has changelinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has changelinged [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_changeling_objectives(src) + to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + if("initialdna") + if(!changeling || !changeling.absorbed_dna.len) + to_chat(usr, "Resetting DNA failed!") + else + current.dna = changeling.absorbed_dna[1] + current.real_name = current.dna.real_name + current.UpdateAppearance() + domutcheck(current, null) + log_admin("[key_name(usr)] has reset [key_name(current)]'s DNA") + message_admins("[key_name_admin(usr)] has reset [key_name_admin(current)]'s DNA") + + else if(href_list["vampire"]) + switch(href_list["vampire"]) + if("clear") + if(src in SSticker.mode.vampires) + SSticker.mode.vampires -= src + special_role = null + if(vampire) + vampire.remove_vampire_powers() + qdel(vampire) + vampire = null + SSticker.mode.update_vampire_icons_removed(src) + to_chat(current, "You grow weak and lose your powers! You are no longer a vampire and are stuck in your current form!") + log_admin("[key_name(usr)] has de-vampired [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-vampired [key_name_admin(current)]") + if("vampire") + if(!(src in SSticker.mode.vampires)) + SSticker.mode.vampires += src + SSticker.mode.grant_vampire_powers(current) + SSticker.mode.update_vampire_icons_added(src) + var/datum/mindslaves/slaved = new() + slaved.masters += src + som = slaved //we MIGT want to mindslave someone + special_role = SPECIAL_ROLE_VAMPIRE + SEND_SOUND(current, 'sound/ambience/antag/vampalert.ogg') + to_chat(current, "Your powers have awoken. Your lust for blood grows... You are a Vampire!") + log_admin("[key_name(usr)] has vampired [key_name(current)]") + message_admins("[key_name_admin(usr)] has vampired [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_vampire_objectives(src) + to_chat(usr, "The objectives for vampire [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + else if(href_list["vampthrall"]) + switch(href_list["vampthrall"]) + if("clear") + if(src in SSticker.mode.vampire_enthralled) + SSticker.mode.remove_vampire_mind(src) + log_admin("[key_name(usr)] has de-vampthralled [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-vampthralled [key_name_admin(current)]") + + else if(href_list["nuclear"]) + var/mob/living/carbon/human/H = current + + switch(href_list["nuclear"]) + if("clear") + if(src in SSticker.mode.syndicates) + SSticker.mode.syndicates -= src + SSticker.mode.update_synd_icons_removed(src) + special_role = null + for(var/datum/objective/nuclear/O in objectives) + objectives-=O + qdel(O) + to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") + log_admin("[key_name(usr)] has de-nuke op'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-nuke op'd [key_name_admin(current)]") + if("nuclear") + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if(SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = SPECIAL_ROLE_NUKEOPS + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + log_admin("[key_name(usr)] has nuke op'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has nuke op'd [key_name_admin(current)]") + if("lair") + current.forceMove(get_turf(locate("landmark*Syndicate-Spawn"))) + log_admin("[key_name(usr)] has moved [key_name(current)] to the nuclear operative spawn") + message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the nuclear operative spawn") + if("dressup") + qdel(H.belt) + qdel(H.back) + qdel(H.l_ear) + qdel(H.r_ear) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_pda) + qdel(H.wear_suit) + qdel(H.w_uniform) + + if(!SSticker.mode.equip_syndicate(current)) + to_chat(usr, "Equipping a syndicate failed!") + return + SSticker.mode.update_syndicate_id(current.mind, SSticker.mode.syndicates.len == 1) + log_admin("[key_name(usr)] has equipped [key_name(current)] as a nuclear operative") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a nuclear operative") + + if("tellcode") + var/code + for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if(code) + store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) + to_chat(current, "The nuclear authorization code is: [code]") + log_admin("[key_name(usr)] has given [key_name(current)] the nuclear authorization code") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] the nuclear authorization code") + else + to_chat(usr, "No valid nuke found!") + + else if(href_list["eventmisc"]) + switch(href_list["eventmisc"]) + if("clear") + if(src in SSticker.mode.eventmiscs) + SSticker.mode.eventmiscs -= src + SSticker.mode.update_eventmisc_icons_removed(src) + special_role = null + message_admins("[key_name_admin(usr)] has de-eventantag'ed [current].") + log_admin("[key_name(usr)] has de-eventantag'ed [current].") + if("eventmisc") + SSticker.mode.eventmiscs += src + special_role = SPECIAL_ROLE_EVENTMISC + SSticker.mode.update_eventmisc_icons_added(src) + message_admins("[key_name_admin(usr)] has eventantag'ed [current].") + log_admin("[key_name(usr)] has eventantag'ed [current].") + else if(href_list["devil"]) + switch(href_list["devil"]) + if("clear") + if(src in SSticker.mode.devils) + if(istype(current,/mob/living/carbon/true_devil/)) + to_chat(usr,"This cannot be used on true or arch-devils.") + else + SSticker.mode.devils -= src + SSticker.mode.update_devil_icons_removed(src) + special_role = null + to_chat(current,"Your infernal link has been severed! You are no longer a devil!") + RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) + RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) + message_admins("[key_name_admin(usr)] has de-devil'ed [current].") + if(issilicon(current)) + var/mob/living/silicon/S = current + S.laws.clear_sixsixsix_laws() + devilinfo = null + log_admin("[key_name(usr)] has de-devil'ed [current].") + else if(src in SSticker.mode.sintouched) + SSticker.mode.sintouched -= src + message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") + log_admin("[key_name(usr)] has de-sintouch'ed [current].") + if("devil") + if(devilinfo) + devilinfo.ascendable = FALSE + message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + return + if(!ishuman(current) && !isrobot(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.update_devil_icons_added(src) + SSticker.mode.finalize_devil(src, FALSE) + SSticker.mode.forge_devil_objectives(src, 2) + SSticker.mode.greet_devil(src) + message_admins("[key_name_admin(usr)] has devil'ed [current].") + log_admin("[key_name(usr)] has devil'ed [current].") + if("ascendable_devil") + if(devilinfo) + devilinfo.ascendable = TRUE + message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + return + if(!ishuman(current) && !isrobot(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.update_devil_icons_added(src) + SSticker.mode.finalize_devil(src, TRUE) + SSticker.mode.forge_devil_objectives(src, 2) + SSticker.mode.greet_devil(src) + message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + if("sintouched") + var/mob/living/carbon/human/H = current + H.influenceSin() + message_admins("[key_name_admin(usr)] has sintouch'ed [current].") + log_admin("[key_name(usr)] has sintouch'ed [current].") + + else if(href_list["traitor"]) + switch(href_list["traitor"]) + if("clear") + if(has_antag_datum(/datum/antagonist/traitor)) + to_chat(current, "You have been brainwashed! You are no longer a traitor!") + remove_antag_datum(/datum/antagonist/traitor) + log_admin("[key_name(usr)] has de-traitored [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-traitored [key_name_admin(current)]") + + if("traitor") + if(!(has_antag_datum(/datum/antagonist/traitor))) + var/datum/antagonist/traitor/T = new() + T.give_objectives = FALSE + T.should_equip = FALSE + add_antag_datum(T) + log_admin("[key_name(usr)] has traitored [key_name(current)]") + message_admins("[key_name_admin(usr)] has traitored [key_name_admin(current)]") + + if("autoobjectives") + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.forge_traitor_objectives(src) + to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + else if(href_list["mindslave"]) + switch(href_list["mindslave"]) + if("clear") + if(has_antag_datum(/datum/antagonist/mindslave)) + var/mob/living/carbon/human/H = current + for(var/i in H.contents) + if(istype(i, /obj/item/implant/traitor)) + qdel(i) + break + remove_antag_datum(/datum/antagonist/mindslave) + log_admin("[key_name(usr)] has de-mindslaved [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-mindslaved [key_name_admin(current)]") + + else if(href_list["shadowling"]) + switch(href_list["shadowling"]) + if("clear") + SSticker.mode.update_shadow_icons_removed(src) + if(src in SSticker.mode.shadows) + SSticker.mode.shadows -= src + special_role = null + to_chat(current, "Your powers have been quenched! You are no longer a shadowling!") + message_admins("[key_name_admin(usr)] has de-shadowlinged [current].") + log_admin("[key_name(usr)] has de-shadowlinged [current].") + current.spellremove(current) + current.remove_language("Shadowling Hivemind") + else if(src in SSticker.mode.shadowling_thralls) + SSticker.mode.remove_thrall(src,0) + message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") + log_admin("[key_name(usr)] has de-thralled [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-thralled [key_name_admin(current)]") + if("shadowling") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + SSticker.mode.shadows += src + special_role = SPECIAL_ROLE_SHADOWLING + to_chat(current, "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ + time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ + ") + SSticker.mode.finalize_shadowling(src) + SSticker.mode.update_shadow_icons_added(src) + log_admin("[key_name(usr)] has shadowlinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has shadowlinged [key_name_admin(current)]") + if("thrall") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + SSticker.mode.add_thrall(src) + message_admins("[key_name_admin(usr)] has thralled [current].") + log_admin("[key_name(usr)] has thralled [current].") + + else if(href_list["abductor"]) + switch(href_list["abductor"]) + if("clear") + to_chat(usr, "Not implemented yet. Sorry!") + //ticker.mode.update_abductor_icons_removed(src) + if("abductor") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + make_Abductor() + log_admin("[key_name(usr)] turned [current] into abductor.") + SSticker.mode.update_abductor_icons_added(src) + if("equip") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + + var/mob/living/carbon/human/H = current + var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") + if(gear) + if(gear=="Agent") + H.equipOutfit(/datum/outfit/abductor/agent) + else + H.equipOutfit(/datum/outfit/abductor/scientist) + + else if(href_list["silicon"]) + switch(href_list["silicon"]) + if("unemag") + var/mob/living/silicon/robot/R = current + if(istype(R)) + R.emagged = 0 + if(R.module) + if(R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + R.clear_supplied_laws() + R.laws = new /datum/ai_laws/crewsimov + log_admin("[key_name(usr)] has un-emagged [key_name(current)]") + message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") + + if("unemagcyborgs") + if(isAI(current)) + var/mob/living/silicon/ai/ai = current + for(var/mob/living/silicon/robot/R in ai.connected_robots) + R.emagged = 0 + if(R.module) + if(R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + R.clear_supplied_laws() + R.laws = new /datum/ai_laws/crewsimov + log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") + message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") + + else if(href_list["common"]) + switch(href_list["common"]) + if("undress") + if(ishuman(current)) + var/mob/living/carbon/human/H = current + // Don't "undress" organs right out of the body + for(var/obj/item/W in H.contents - (H.bodyparts | H.internal_organs)) + current.unEquip(W, 1) + else + for(var/obj/item/W in current) + current.unEquip(W, 1) + log_admin("[key_name(usr)] has unequipped [key_name(current)]") + message_admins("[key_name_admin(usr)] has unequipped [key_name_admin(current)]") + if("takeuplink") + take_uplink() + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.antag_memory = "" //Remove any antag memory they may have had (uplink codes, code phrases) + log_admin("[key_name(usr)] has taken [key_name(current)]'s uplink") + message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s uplink") + if("crystals") + if(usr.client.holder.rights & (R_SERVER|R_EVENT)) + var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if(suplink) + crystals = suplink.uses + crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num + if(!isnull(crystals)) + if(suplink) + suplink.uses = crystals + log_admin("[key_name(usr)] has set [key_name(current)]'s telecrystals to [crystals]") + message_admins("[key_name_admin(usr)] has set [key_name_admin(current)]'s telecrystals to [crystals]") + if("uplink") + if(has_antag_datum(/datum/antagonist/traitor)) + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.give_codewords() + if(!T.equip_traitor(src)) + to_chat(usr, "Equipping a syndicate failed!") + return + log_admin("[key_name(usr)] has given [key_name(current)] an uplink") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] an uplink") + + else if(href_list["obj_announce"]) + announce_objectives() + SEND_SOUND(current, sound('sound/ambience/alarm4.ogg')) + log_admin("[key_name(usr)] has announced [key_name(current)]'s objectives") + message_admins("[key_name_admin(usr)] has announced [key_name_admin(current)]'s objectives") + + edit_memory() + + +// Datum antag mind procs +/datum/mind/proc/add_antag_datum(datum_type_or_instance, team) + if(!datum_type_or_instance) + return + var/datum/antagonist/A + if(!ispath(datum_type_or_instance)) + A = datum_type_or_instance + if(!istype(A)) + return + else + A = new datum_type_or_instance() + //Choose snowflake variation if antagonist handles it + var/datum/antagonist/S = A.specialization(src) + if(S && S != A) + qdel(A) + A = S + if(!A.can_be_owned(src)) + qdel(A) + return + A.owner = src + LAZYADD(antag_datums, A) + A.create_team(team) + var/datum/team/antag_team = A.get_team() + if(antag_team) + antag_team.add_member(src) + A.on_gain() + return A + +/datum/mind/proc/remove_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = has_antag_datum(datum_type) + if(A) + A.on_removal() + return TRUE + + +/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. + for(var/a in antag_datums) + var/datum/antagonist/A = a + A.on_removal() + +/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) + if(!datum_type) + return + . = FALSE + for(var/a in antag_datums) + var/datum/antagonist/A = a + if(check_subtypes && istype(A, datum_type)) + return A + else if(A.type == datum_type) + return A + +/datum/mind/proc/announce_objectives() + to_chat(current, "Your current objectives:") + for(var/line in splittext(gen_objective_text(), "
    ")) + to_chat(current, line) + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for(var/obj/item/I in L) + if(I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) + +/datum/mind/proc/make_Traitor() + if(!has_antag_datum(/datum/antagonist/traitor)) + add_antag_datum(/datum/antagonist/traitor) + +/datum/mind/proc/make_Nuke() + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if(SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = SPECIAL_ROLE_NUKEOPS + assigned_role = SPECIAL_ROLE_NUKEOPS + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + + current.loc = get_turf(locate("landmark*Syndicate-Spawn")) + + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.l_ear) + qdel(H.r_ear) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_pda) + qdel(H.wear_suit) + qdel(H.w_uniform) + + SSticker.mode.equip_syndicate(current) + +/datum/mind/proc/make_Vampire() + if(!(src in SSticker.mode.vampires)) + SSticker.mode.vampires += src + SSticker.mode.grant_vampire_powers(current) + special_role = SPECIAL_ROLE_VAMPIRE + SSticker.mode.forge_vampire_objectives(src) + SSticker.mode.greet_vampire(src) + SSticker.mode.update_vampire_icons_added(src) + +/datum/mind/proc/make_Changeling() + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + SSticker.mode.grant_changeling_powers(current) + special_role = SPECIAL_ROLE_CHANGELING + SSticker.mode.forge_changeling_objectives(src) + SSticker.mode.greet_changeling(src) + SSticker.mode.update_change_icons_added(src) + +/datum/mind/proc/make_Overmind() + if(!(src in SSticker.mode.blob_overminds)) + SSticker.mode.blob_overminds += src + special_role = SPECIAL_ROLE_BLOB_OVERMIND + +/datum/mind/proc/make_Wizard() + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = SPECIAL_ROLE_WIZARD + assigned_role = SPECIAL_ROLE_WIZARD + //ticker.mode.learn_basic_spells(current) + if(!GLOB.wizardstart.len) + current.loc = pick(GLOB.latejoin) + to_chat(current, "HOT INSERTION, GO GO GO") + else + current.loc = pick(GLOB.wizardstart) + + SSticker.mode.equip_wizard(current) + for(var/obj/item/spellbook/S in current.contents) + S.op = 0 + SSticker.mode.name_wizard(current) + SSticker.mode.forge_wizard_objectives(src) + SSticker.mode.greet_wizard(src) + SSticker.mode.update_wiz_icons_added(src) + +/datum/mind/proc/make_Rev() + if(SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if(valid_head) + for(var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_HEAD_REV + + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 +// fail |= !ticker.mode.equip_traitor(current, 1) + fail |= !SSticker.mode.equip_revolutionary(current) + +/datum/mind/proc/make_Abductor() + var/role = alert("Abductor Role ?","Role","Agent","Scientist") + var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) + var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") + + if(!role || !team || !teleport) + return + + if(!ishuman(current)) + return + + SSticker.mode.abductors |= src + + var/datum/objective/stay_hidden/hidden_obj = new + hidden_obj.owner = src + objectives += hidden_obj + + var/datum/objective/experiment/O = new + O.owner = src + objectives += O + + var/mob/living/carbon/human/H = current + + H.set_species(/datum/species/abductor) + var/datum/species/abductor/S = H.dna.species + + if(role == "Scientist") + S.scientist = TRUE + + S.team = team + + var/list/obj/effect/landmark/abductor/agent_landmarks = new + var/list/obj/effect/landmark/abductor/scientist_landmarks = new + agent_landmarks.len = 4 + scientist_landmarks.len = 4 + for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list) + if(istype(A, /obj/effect/landmark/abductor/agent)) + agent_landmarks[text2num(A.team)] = A + else if(istype(A, /obj/effect/landmark/abductor/scientist)) + scientist_landmarks[text2num(A.team)] = A + + var/obj/effect/landmark/L + if(teleport == "Yes") + switch(role) + if("Agent") + L = agent_landmarks[team] + if("Scientist") + L = agent_landmarks[team] + H.forceMove(L.loc) + + +// check whether this mind's mob has been brigged for the given duration +// have to call this periodically for the duration to work properly +/datum/mind/proc/is_brigged(duration) + var/turf/T = current.loc + if(!istype(T)) + brigged_since = -1 + return 0 + + var/is_currently_brigged = current.is_in_brig() + if(!is_currently_brigged) + brigged_since = -1 + return 0 + + if(brigged_since == -1) + brigged_since = world.time + + return (duration <= world.time - brigged_since) + +/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) + spell_list += S + S.action.Grant(current) + +/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) //To remove a specific spell from a mind + if(!spell) + return + for(var/obj/effect/proc_holder/spell/S in spell_list) + if(istype(S, spell)) + qdel(S) + spell_list -= S + +/datum/mind/proc/transfer_actions(mob/living/new_character) + if(current && current.actions) + for(var/datum/action/A in current.actions) + A.Grant(new_character) + transfer_mindbound_actions(new_character) + +/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + S.action.Grant(new_character) + +/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + for(var/type in exceptions) + if(istype(S, type)) + continue + S.charge_counter = delay + spawn(0) + S.start_recharge() + S.updateButtonIcon() + +/datum/mind/proc/get_ghost(even_if_they_cant_reenter) + for(var/mob/dead/observer/G in GLOB.dead_mob_list) + if(G.mind == src) + if(G.can_reenter_corpse || even_if_they_cant_reenter) + return G + break + +/datum/mind/proc/grab_ghost(force) + var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() + + +/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 6000, team_color = "red") + + zealot_master = missionary + + var/list/implanters + if(!(missionary.mind in SSticker.mode.implanter)) + SSticker.mode.implanter[missionary.mind] = list() + implanters = SSticker.mode.implanter[missionary.mind] + implanters.Add(src) + SSticker.mode.implanted.Add(src) + SSticker.mode.implanted[src] = missionary.mind + SSticker.mode.implanter[missionary.mind] = implanters + SSticker.mode.traitors += src + + + var/datum/objective/protect/zealot_objective = new + zealot_objective.target = missionary.mind + zealot_objective.owner = src + zealot_objective.explanation_text = "Obey every order from and protect [missionary.real_name], the [missionary.mind.assigned_role == missionary.mind.special_role ? (missionary.mind.special_role) : (missionary.mind.assigned_role)]." + objectives += zealot_objective + add_antag_datum(/datum/antagonist/mindslave) + + var/datum/antagonist/traitor/T = missionary.mind.has_antag_datum(/datum/antagonist) + T.update_traitor_icons_added(missionary.mind) + + to_chat(current, "You're now a loyal zealot of [missionary.name]! You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.") + + var/datum/mindslaves/slaved = missionary.mind.som + som = slaved + slaved.serv += current + slaved.add_serv_hud(missionary.mind, "master") //handles master servent icons + slaved.add_serv_hud(src, "mindslave") + + var/obj/item/clothing/under/jumpsuit = null + if(ishuman(current)) //only bother with the jumpsuit stuff if we are a human type, since we won't have the slot otherwise + var/mob/living/carbon/human/H = current + if(H.w_uniform) + jumpsuit = H.w_uniform + jumpsuit.color = team_color + H.update_inv_w_uniform(0,0) + + add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") + addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires + return 1 + +/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) + if(!zealot_master) //if they aren't a zealot, we can't remove their zealot status, obviously. don't bother with the rest so we don't confuse them with the messages + return + remove_antag_datum(/datum/antagonist/mindslave) + add_attack_logs(zealot_master, current, "Lost control of zealot") + zealot_master = null + + if(jumpsuit) + jumpsuit.color = initial(jumpsuit.color) //reset the jumpsuit no matter where our mind is + if(ishuman(current)) //but only try updating us if we are still a human type since it is a human proc + var/mob/living/carbon/human/H = current + H.update_inv_w_uniform(0,0) + + to_chat(current, "You seem to have forgotten the events of the past 10 minutes or so, and your head aches a bit as if someone beat it savagely with a stick.") + to_chat(current, "This means you don't remember who you were working for or what you were doing.") + +/datum/mind/proc/is_revivable() //Note, this ONLY checks the mind. + if(damnation_type) + return FALSE + return TRUE + +// returns a mob to message to produce something visible for the target mind +/datum/mind/proc/messageable_mob() + if(!QDELETED(current) && current.client) + return current + else + return get_ghost(even_if_they_cant_reenter = TRUE) + +//Initialisation procs +/mob/proc/mind_initialize() + if(mind) + mind.key = key + else + mind = new /datum/mind(key) + if(SSticker) + SSticker.minds += mind + else + error("mind_initialize(): No ticker ready yet! Please inform Carn") + if(!mind.name) + mind.name = real_name + mind.current = src + +//HUMAN +/mob/living/carbon/human/mind_initialize() + ..() + if(!mind.assigned_role) + mind.assigned_role = "Civilian" //defualt + +/mob/proc/sync_mind() + mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) + mind.active = 1 //indicates that the mind is currently synced with a client + +//slime +/mob/living/simple_animal/slime/mind_initialize() + ..() + mind.assigned_role = "slime" + +//XENO +/mob/living/carbon/alien/mind_initialize() + ..() + mind.assigned_role = "Alien" + //XENO HUMANOID +/mob/living/carbon/alien/humanoid/queen/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_QUEEN + +/mob/living/carbon/alien/humanoid/hunter/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_HUNTER + +/mob/living/carbon/alien/humanoid/drone/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_DRONE + +/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_SENTINEL + //XENO LARVA +/mob/living/carbon/alien/larva/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_LARVA + +//AI +/mob/living/silicon/ai/mind_initialize() + ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + ..() + mind.assigned_role = "pAI" + mind.special_role = null + +//BLOB +/mob/camera/overmind/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_BLOB + +//Animals +/mob/living/simple_animal/mind_initialize() + ..() + mind.assigned_role = "Animal" + +/mob/living/simple_animal/pet/dog/corgi/mind_initialize() + ..() + mind.assigned_role = "Corgi" + +/mob/living/simple_animal/shade/mind_initialize() + ..() + mind.assigned_role = "Shade" + +/mob/living/simple_animal/construct/builder/mind_initialize() + ..() + mind.assigned_role = "Artificer" + mind.special_role = SPECIAL_ROLE_CULTIST + +/mob/living/simple_animal/construct/wraith/mind_initialize() + ..() + mind.assigned_role = "Wraith" + mind.special_role = SPECIAL_ROLE_CULTIST + +/mob/living/simple_animal/construct/armoured/mind_initialize() + ..() + mind.assigned_role = "Juggernaut" + mind.special_role = SPECIAL_ROLE_CULTIST diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index a40a9f9a34d2..a5ff38a26b06 100644 --- a/code/datums/mixed.dm +++ b/code/datums/mixed.dm @@ -1,40 +1,40 @@ -/datum/data - var/name = "data" - var/size = 1.0 - - -/datum/data/function - name = "function" - size = 2.0 - - -/datum/data/function/data_control - name = "data control" - - -/datum/data/function/id_changer - name = "id changer" - - -/datum/data/record - name = "record" - size = 5.0 - var/list/fields = list( ) - -/datum/data/record/Destroy() - if(src in data_core.medical) - data_core.medical -= src - if(src in data_core.security) - data_core.security -= src - if(src in data_core.general) - data_core.general -= src - if(src in data_core.locked) - data_core.locked -= src - return ..() - -/datum/data/text - name = "text" - var/data = null - -/datum/debug - var/list/debuglist +/datum/data + var/name = "data" + var/size = 1.0 + + +/datum/data/function + name = "function" + size = 2.0 + + +/datum/data/function/data_control + name = "data control" + + +/datum/data/function/id_changer + name = "id changer" + + +/datum/data/record + name = "record" + size = 5.0 + var/list/fields = list( ) + +/datum/data/record/Destroy() + if(src in GLOB.data_core.medical) + GLOB.data_core.medical -= src + if(src in GLOB.data_core.security) + GLOB.data_core.security -= src + if(src in GLOB.data_core.general) + GLOB.data_core.general -= src + if(src in GLOB.data_core.locked) + GLOB.data_core.locked -= src + return ..() + +/datum/data/text + name = "text" + var/data = null + +/datum/debug + var/list/debuglist diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 31c203155080..0887290a6f49 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -23,4 +23,4 @@ . = ..() alpha = 255 opacity = 1 - transform = null \ No newline at end of file + transform = null diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index d2c19f61bfaf..53c755e88228 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -1048,8 +1048,8 @@ to_chat(H, "You have gained the ability to shapeshift into lesser hellhound form. This is a combat form with different abilities, tough but not invincible. It can regenerate itself over time by resting.") H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/raise_vampires) to_chat(H, "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed.") - H.dna.SetSEState(JUMPBLOCK, 1) - genemutcheck(H, JUMPBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.jumpblock, 1) + genemutcheck(H, GLOB.jumpblock, null, MUTCHK_FORCED) H.update_mutations() H.gene_stability = 100 diff --git a/code/datums/outfits/plasmamen.dm b/code/datums/outfits/plasmamen.dm index c14eafd08f23..376bf2af90c3 100644 --- a/code/datums/outfits/plasmamen.dm +++ b/code/datums/outfits/plasmamen.dm @@ -175,4 +175,4 @@ name = "Blueshield Plasmaman" head = /obj/item/clothing/head/helmet/space/plasmaman/blueshield - uniform = /obj/item/clothing/under/plasmaman/blueshield \ No newline at end of file + uniform = /obj/item/clothing/under/plasmaman/blueshield diff --git a/code/datums/outfits/vv_outfit.dm b/code/datums/outfits/vv_outfit.dm index 7a07b087ae81..95c91318bb81 100644 --- a/code/datums/outfits/vv_outfit.dm +++ b/code/datums/outfits/vv_outfit.dm @@ -187,4 +187,4 @@ . = ..() stored_access = outfit_data["stored_access"] vv_values = outfit_data["vv_values"] - update_id_name = outfit_data["update_id_name"] \ No newline at end of file + update_id_name = outfit_data["update_id_name"] diff --git a/code/datums/periodic_news.dm b/code/datums/periodic_news.dm index 9a09ec4a7ec4..d1cb9fc014dd 100644 --- a/code/datums/periodic_news.dm +++ b/code/datums/periodic_news.dm @@ -1,6 +1,7 @@ // This system defines news that will be displayed in the course of a round. // Uses BYOND's type system to put everything into a nice format +// THIS ISNT PROPERLY PATHED AND I AM GOING TO FUCKING SCREAM AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA /datum/news_announcement var round_time // time of the round at which this should be announced, in seconds @@ -114,24 +115,24 @@ the riots. More on this at 6."} round_time = 60 * 60 - -var/global/list/newscaster_standard_feeds = list(/datum/news_announcement/bluespace_research, /datum/news_announcement/lotus_tree, /datum/news_announcement/random_junk, /datum/news_announcement/food_riots) +GLOBAL_LIST_INIT(newscaster_standard_feeds, list(/datum/news_announcement/bluespace_research, /datum/news_announcement/lotus_tree, /datum/news_announcement/random_junk, /datum/news_announcement/food_riots)) proc/process_newscaster() check_for_newscaster_updates(SSticker.mode.newscaster_announcements) -var/global/tmp/announced_news_types = list() +GLOBAL_LIST_EMPTY(announced_news_types) + proc/check_for_newscaster_updates(type) for(var/subtype in subtypesof(type)) var/datum/news_announcement/news = new subtype() - if(news.round_time * 10 <= world.time && !(subtype in announced_news_types)) - announced_news_types += subtype + if(news.round_time * 10 <= world.time && !(subtype in GLOB.announced_news_types)) + GLOB.announced_news_types += subtype announce_newscaster_news(news) proc/announce_newscaster_news(datum/news_announcement/news) var/datum/feed_channel/sendto - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == news.channel_name) sendto = FC break @@ -142,7 +143,7 @@ proc/announce_newscaster_news(datum/news_announcement/news) sendto.author = news.author sendto.locked = 1 sendto.is_admin_channel = 1 - news_network.network_channels += sendto + GLOB.news_network.network_channels += sendto var/datum/feed_message/newMsg = new /datum/feed_message newMsg.author = news.author ? news.author : sendto.author @@ -152,5 +153,5 @@ proc/announce_newscaster_news(datum/news_announcement/news) sendto.messages += newMsg - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert(news.channel_name) diff --git a/code/datums/radio.dm b/code/datums/radio.dm index 7670bc76e323..b748a9f92293 100644 --- a/code/datums/radio.dm +++ b/code/datums/radio.dm @@ -1,114 +1,114 @@ - -/datum/radio_frequency - var/frequency as num - var/list/list/obj/devices = list() - -/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null) - var/turf/start_point - if(range) - start_point = get_turf(source) - if(!start_point) - qdel(signal) - return 0 - if(filter) - send_to_filter(source, signal, filter, start_point, range) - send_to_filter(source, signal, RADIO_DEFAULT, start_point, range) - else - //Broadcast the signal to everyone! - for(var/next_filter in devices) - send_to_filter(source, signal, next_filter, start_point, range) - -//Sends a signal to all machines belonging to a given filter. Should be called by post_signal() -/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null) - if(range && !start_point) - return - - for(var/obj/device in devices[filter]) - if(device == source) - continue - if(range) - var/turf/end_point = get_turf(device) - if(!end_point) - continue - if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range) - continue - - device.receive_signal(signal, TRANSMISSION_RADIO, frequency) - -/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null) - if(!filter) - filter = RADIO_DEFAULT - //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]") - var/list/obj/devices_line = devices[filter] - if(!devices_line) - devices_line = new - devices[filter] = devices_line - devices_line+=device -// var/list/obj/devices_line___ = devices[filter_str] -// var/l = devices_line___.len - //log_admin("DEBUG: devices_line.len=[devices_line.len]") - //log_admin("DEBUG: devices(filter_str).len=[l]") - -/datum/radio_frequency/proc/remove_listener(obj/device) - for(var/devices_filter in devices) - var/list/devices_line = devices[devices_filter] - devices_line-=device - while(null in devices_line) - devices_line -= null - if(devices_line.len==0) - devices -= devices_filter - qdel(devices_line) - -/datum/signal - var/obj/source - - var/transmission_method = 0 //unused at the moment - //0 = wire - //1 = radio transmission - //2 = subspace transmission - - var/list/data = list() - var/encryption - - var/frequency = 0 - -/datum/signal/proc/copy_from(datum/signal/model) - source = model.source - transmission_method = model.transmission_method - data = model.data - encryption = model.encryption - frequency = model.frequency - -/datum/signal/proc/debug_print() - if(source) - . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n" - else - . = "signal = {source = '[source]' ()\n" - for(var/i in data) - . += "data\[\"[i]\"\] = \"[data[i]]\"\n" - if(islist(data[i])) - var/list/L = data[i] - for(var/t in L) - . += "data\[\"[i]\"\] list has: [t]" - -/datum/signal/proc/get_race(mob/M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - . = H.dna.species.name - else if(isbrain(M)) - var/mob/living/carbon/brain/B = M - . = B.get_race() - else if(issilicon(M)) - . = "Artificial Life" - else if(isslime(M)) - . = "Slime" - else if(isbot(M)) - . = "Bot" - else if(isanimal(M)) - . = "Domestic Animal" - else - . = "Unidentifiable" - -//callback used by objects to react to incoming radio signals -/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return null + +/datum/radio_frequency + var/frequency as num + var/list/list/obj/devices = list() + +/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null) + var/turf/start_point + if(range) + start_point = get_turf(source) + if(!start_point) + qdel(signal) + return 0 + if(filter) + send_to_filter(source, signal, filter, start_point, range) + send_to_filter(source, signal, RADIO_DEFAULT, start_point, range) + else + //Broadcast the signal to everyone! + for(var/next_filter in devices) + send_to_filter(source, signal, next_filter, start_point, range) + +//Sends a signal to all machines belonging to a given filter. Should be called by post_signal() +/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null) + if(range && !start_point) + return + + for(var/obj/device in devices[filter]) + if(device == source) + continue + if(range) + var/turf/end_point = get_turf(device) + if(!end_point) + continue + if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range) + continue + + device.receive_signal(signal, TRANSMISSION_RADIO, frequency) + +/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null) + if(!filter) + filter = RADIO_DEFAULT + //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]") + var/list/obj/devices_line = devices[filter] + if(!devices_line) + devices_line = new + devices[filter] = devices_line + devices_line+=device +// var/list/obj/devices_line___ = devices[filter_str] +// var/l = devices_line___.len + //log_admin("DEBUG: devices_line.len=[devices_line.len]") + //log_admin("DEBUG: devices(filter_str).len=[l]") + +/datum/radio_frequency/proc/remove_listener(obj/device) + for(var/devices_filter in devices) + var/list/devices_line = devices[devices_filter] + devices_line-=device + while(null in devices_line) + devices_line -= null + if(devices_line.len==0) + devices -= devices_filter + qdel(devices_line) + +/datum/signal + var/obj/source + + var/transmission_method = 0 //unused at the moment + //0 = wire + //1 = radio transmission + //2 = subspace transmission + + var/list/data = list() + var/encryption + + var/frequency = 0 + +/datum/signal/proc/copy_from(datum/signal/model) + source = model.source + transmission_method = model.transmission_method + data = model.data + encryption = model.encryption + frequency = model.frequency + +/datum/signal/proc/debug_print() + if(source) + . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n" + else + . = "signal = {source = '[source]' ()\n" + for(var/i in data) + . += "data\[\"[i]\"\] = \"[data[i]]\"\n" + if(islist(data[i])) + var/list/L = data[i] + for(var/t in L) + . += "data\[\"[i]\"\] list has: [t]" + +/datum/signal/proc/get_race(mob/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + . = H.dna.species.name + else if(isbrain(M)) + var/mob/living/carbon/brain/B = M + . = B.get_race() + else if(issilicon(M)) + . = "Artificial Life" + else if(isslime(M)) + . = "Slime" + else if(isbot(M)) + . = "Bot" + else if(isanimal(M)) + . = "Domestic Animal" + else + . = "Unidentifiable" + +//callback used by objects to react to incoming radio signals +/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return null diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm index 99d97e456260..52189fe527e9 100644 --- a/code/datums/recipe.dm +++ b/code/datums/recipe.dm @@ -1,131 +1,131 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * - * /datum/recipe by rastaf0 13 apr 2011 * - * * * * * * * * * * * * * * * * * * * * * * * * * * - * This is powerful and flexible recipe system. - * It exists not only for food. - * supports both reagents and objects as prerequisites. - * In order to use this system you have to define a deriative from /datum/recipe - * * reagents are reagents. Acid, milc, booze, etc. - * * items are objects. Fruits, tools, circuit boards. - * * result is type to create as new object - * * time is optional parameter, you shall use in in your machine, - default /datum/recipe/ procs does not rely on this parameter. - * - * Functions you need: - * /datum/recipe/proc/make(var/obj/container as obj) - * Creates result inside container, - * deletes prerequisite reagents, - * transfers reagents from prerequisite objects, - * deletes all prerequisite objects (even not needed for recipe at the moment). - * - * /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1) - * Wonderful function that select suitable recipe for you. - * obj is a machine (or magik hat) with prerequisites, - * exact = 0 forces algorithm to ignore superfluous stuff. - * - * - * Functions you do not need to call directly but could: - * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) - * //1=precisely, 0=insufficiently, -1=superfluous - * - * /datum/recipe/proc/check_items(var/obj/container as obj) - * //1=precisely, 0=insufficiently, -1=superfluous - * - * */ - -/datum/recipe - var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice - var/list/items // example: =list(/obj/item/crowbar, /obj/item/welder) // place /foo/bar before /foo - var/result //example: = /obj/item/reagent_containers/food/snacks/donut - var/time = 100 // 1/10 part of second - var/byproduct // example: = /obj/item/kitchen/mould // byproduct to return, such as a mould or trash - -/datum/recipe/proc/check_reagents(datum/reagents/avail_reagents) //1=precisely, 0=insufficiently, -1=superfluous - . = 1 - for(var/r_r in reagents) - var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) - if(!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals - if(aval_r_amnt>reagents[r_r]) - . = -1 - else - return 0 - if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) - return -1 - return . - -/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous - . = 1 - var/list/checklist = items ? items.Copy() : list() - for(var/obj/O in container) - if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring - continue - if(!items) - return -1 - var/found = 0 - for(var/type in checklist) - if(istype(O,type)) - checklist -= type - found = 1 - break - if(!found) - . = -1 - if(checklist.len) - return 0 - return . - -//general version -/datum/recipe/proc/make(obj/container) - var/obj/result_obj = new result(container) - for(var/obj/O in (container.contents-result_obj)) - O.reagents.trans_to(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -// food-related -/datum/recipe/proc/make_food(obj/container) - var/obj/result_obj = new result(container) - for(var/obj/O in (container.contents-result_obj)) - if(O.reagents) - O.reagents.del_reagent("nutriment") - O.reagents.update_total() - O.reagents.trans_to(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null) - if(!exact) - exact = -1 - var/list/datum/recipe/possible_recipes = new - for(var/datum/recipe/recipe in available_recipes) - if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) - possible_recipes += recipe - if(possible_recipes.len == 0) - return null - else if(possible_recipes.len == 1) - return possible_recipes[1] - else //okay, let's select the most complicated recipe - var/r_count = 0 - var/i_count = 0 - . = possible_recipes[1] - for(var/datum/recipe/recipe in possible_recipes) - var/N_i = (recipe.items)?(recipe.items.len):0 - var/N_r = (recipe.reagents)?(recipe.reagents.len):0 - if(N_i > i_count || (N_i== i_count && N_r > r_count )) - r_count = N_r - i_count = N_i - . = recipe - return . - -/datum/recipe/proc/get_byproduct() - if(byproduct) - return byproduct - else - return null - -/datum/recipe/proc/count_n_items() - var/count = 0 - if(items && items.len) - count += items.len - return count \ No newline at end of file +/* * * * * * * * * * * * * * * * * * * * * * * * * * + * /datum/recipe by rastaf0 13 apr 2011 * + * * * * * * * * * * * * * * * * * * * * * * * * * * + * This is powerful and flexible recipe system. + * It exists not only for food. + * supports both reagents and objects as prerequisites. + * In order to use this system you have to define a deriative from /datum/recipe + * * reagents are reagents. Acid, milc, booze, etc. + * * items are objects. Fruits, tools, circuit boards. + * * result is type to create as new object + * * time is optional parameter, you shall use in in your machine, + default /datum/recipe/ procs does not rely on this parameter. + * + * Functions you need: + * /datum/recipe/proc/make(var/obj/container as obj) + * Creates result inside container, + * deletes prerequisite reagents, + * transfers reagents from prerequisite objects, + * deletes all prerequisite objects (even not needed for recipe at the moment). + * + * /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1) + * Wonderful function that select suitable recipe for you. + * obj is a machine (or magik hat) with prerequisites, + * exact = 0 forces algorithm to ignore superfluous stuff. + * + * + * Functions you do not need to call directly but could: + * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) + * //1=precisely, 0=insufficiently, -1=superfluous + * + * /datum/recipe/proc/check_items(var/obj/container as obj) + * //1=precisely, 0=insufficiently, -1=superfluous + * + * */ + +/datum/recipe + var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice + var/list/items // example: =list(/obj/item/crowbar, /obj/item/welder) // place /foo/bar before /foo + var/result //example: = /obj/item/reagent_containers/food/snacks/donut + var/time = 100 // 1/10 part of second + var/byproduct // example: = /obj/item/kitchen/mould // byproduct to return, such as a mould or trash + +/datum/recipe/proc/check_reagents(datum/reagents/avail_reagents) //1=precisely, 0=insufficiently, -1=superfluous + . = 1 + for(var/r_r in reagents) + var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) + if(!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals + if(aval_r_amnt>reagents[r_r]) + . = -1 + else + return 0 + if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + return -1 + return . + +/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous + . = 1 + var/list/checklist = items ? items.Copy() : list() + for(var/obj/O in container) + if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring + continue + if(!items) + return -1 + var/found = 0 + for(var/type in checklist) + if(istype(O,type)) + checklist -= type + found = 1 + break + if(!found) + . = -1 + if(checklist.len) + return 0 + return . + +//general version +/datum/recipe/proc/make(obj/container) + var/obj/result_obj = new result(container) + for(var/obj/O in (container.contents-result_obj)) + O.reagents.trans_to(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +// food-related +/datum/recipe/proc/make_food(obj/container) + var/obj/result_obj = new result(container) + for(var/obj/O in (container.contents-result_obj)) + if(O.reagents) + O.reagents.del_reagent("nutriment") + O.reagents.update_total() + O.reagents.trans_to(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null) + if(!exact) + exact = -1 + var/list/datum/recipe/possible_recipes = new + for(var/datum/recipe/recipe in available_recipes) + if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) + possible_recipes += recipe + if(possible_recipes.len == 0) + return null + else if(possible_recipes.len == 1) + return possible_recipes[1] + else //okay, let's select the most complicated recipe + var/r_count = 0 + var/i_count = 0 + . = possible_recipes[1] + for(var/datum/recipe/recipe in possible_recipes) + var/N_i = (recipe.items)?(recipe.items.len):0 + var/N_r = (recipe.reagents)?(recipe.reagents.len):0 + if(N_i > i_count || (N_i== i_count && N_r > r_count )) + r_count = N_r + i_count = N_i + . = recipe + return . + +/datum/recipe/proc/get_byproduct() + if(byproduct) + return byproduct + else + return null + +/datum/recipe/proc/count_n_items() + var/count = 0 + if(items && items.len) + count += items.len + return count diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index bb80fe84d01a..f826b6ff0d25 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -217,4 +217,4 @@ datum/map_template/ruin/lavaland/ash_walker id = "puzzle" description = "Mystery to be solved." suffix = "lavaland_surface_puzzle.dmm" - cost = 5 \ No newline at end of file + cost = 5 diff --git a/code/datums/spawners_menu.dm b/code/datums/spawners_menu.dm index 5cc767213775..a651edbc79c0 100644 --- a/code/datums/spawners_menu.dm +++ b/code/datums/spawners_menu.dm @@ -6,7 +6,7 @@ qdel(src) owner = new_owner -/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = FALSE, datum/topic_state/state = ghost_state, datum/nanoui/master_ui = null) +/datum/spawners_menu/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = FALSE, datum/topic_state/state = GLOB.ghost_state, datum/nanoui/master_ui = null) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "spawners_menu.tmpl", "Spawners Menu", 700, 600, master_ui, state = state) diff --git a/code/datums/spell.dm b/code/datums/spell.dm index f19f81005535..e367e2d9aa42 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -1,499 +1,499 @@ -#define TARGET_CLOSEST 1 -#define TARGET_RANDOM 2 - -/obj/effect/proc_holder - var/panel = "Debug"//What panel the proc holder needs to go on. - var/active = FALSE //Used by toggle based abilities. - var/ranged_mousepointer - var/mob/living/ranged_ability_user - -/obj/effect/proc_holder/singularity_act() - return - -/obj/effect/proc_holder/singularity_pull() - return - -var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin verb for now - -/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/user, params, atom/A) - if(user.ranged_ability != src) - to_chat(user, "[user.ranged_ability.name] has been disabled.") - user.ranged_ability.remove_ranged_ability(user) - return TRUE //TRUE for failed, FALSE for passed. - user.changeNext_click(CLICK_CD_CLICK_ABILITY) - user.face_atom(A) - return FALSE - -/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client) - return - if(user.ranged_ability && user.ranged_ability != src) - to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") - user.ranged_ability.remove_ranged_ability(user) - user.ranged_ability = src - ranged_ability_user = user - user.client.click_intercept = user.ranged_ability - add_mousepointer(user.client) - active = TRUE - if(msg) - to_chat(user, msg) - update_icon() - -/obj/effect/proc_holder/proc/add_mousepointer(client/C) - if(C && ranged_mousepointer && C.mouse_pointer_icon == initial(C.mouse_pointer_icon)) - C.mouse_pointer_icon = ranged_mousepointer - -/obj/effect/proc_holder/proc/remove_mousepointer(client/C) - if(C && ranged_mousepointer && C.mouse_pointer_icon == ranged_mousepointer) - C.mouse_pointer_icon = initial(C.mouse_pointer_icon) - -/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability - return - user.ranged_ability = null - ranged_ability_user = null - user.client.click_intercept = null - remove_mousepointer(user.client) - active = FALSE - if(msg) - to_chat(user, msg) - update_icon() - -/obj/effect/proc_holder/spell - name = "Spell" - desc = "A wizard spell" - panel = "Spells"//What panel the proc holder needs to go on. - density = 0 - opacity = 0 - - var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? - - var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that - - var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" - var/starts_charged = TRUE //Does this spell start ready to go? - var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" - var/still_recharging_msg = "The spell is still recharging." - - var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" - var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used - - var/ghost = 0 // Skip life check. - var/clothes_req = 1 //see if it requires clothes - var/human_req = 0 //spell can only be cast by humans - var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities - var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells - var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell - var/invocation_emote_self = null - var/invocation_type = "none" //can be none, whisper and shout - var/range = 7 //the range of the spell; outer radius for aoe spells - var/message = "" //whatever it says to the guy affected by it - var/selection_type = "view" //can be "range" or "view" - var/spell_level = 0 //if a spell can be taken multiple times, this raises - var/level_max = 4 //The max possible level_max is 4 - var/cooldown_min = 0 //This defines what spell quickened four timeshas as a cooldown. Make sure to set this for every spell - - var/overlay = 0 - var/overlay_icon = 'icons/obj/wizard.dmi' - var/overlay_icon_state = "spell" - var/overlay_lifespan = 0 - - var/sparks_spread = 0 - var/sparks_amt = 0 //cropped at 10 - var/smoke_spread = 0 //1 - harmless, 2 - harmful - var/smoke_amt = 0 //cropped at 10 - - var/critfailchance = 0 - var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 - - var/datum/action/spell_action/action = null - var/action_icon = 'icons/mob/actions/actions.dmi' - var/action_icon_state = "spell_default" - var/action_background_icon_state = "bg_spell" - var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable() - - var/sound = null //The sound the spell makes when it is cast - -/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - to_chat(user, "You shouldn't have this spell! Something's wrong.") - return 0 - - if(ishuman(user)) - var/mob/living/carbon/human/caster = user - if(caster.remoteview_target) - caster.remoteview_target = null - caster.reset_perspective(0) - return 0 - - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - to_chat(user, still_recharging_msg) - return 0 - if("charges") - if(!charge_counter) - to_chat(user, "[name] has no charges left.") - return 0 - - if(!ghost) - if(user.stat && !stat_allowed) - to_chat(user, "You can't cast this spell while incapacitated.") - return 0 - if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) - to_chat(user, "Mmmf mrrfff!") - return 0 - - var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) - if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check - var/mob/living/carbon/human/H = user - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") - return 0 - else if(!ishuman(user)) - if(clothes_req || human_req) - to_chat(user, "This spell can only be cast by humans!") - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - to_chat(user, "This spell can only be cast by physical beings!") - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - charge_counter = 0 //doesn't start recharging until the targets selecting ends - if("charges") - charge_counter-- //returns the charge if the targets selecting fails - if("holdervar") - adjust_var(user, holder_var_type, holder_var_amount) - - if(action) - action.UpdateButtonIcon() - return 1 - -/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount - switch(invocation_type) - if("shout") - if(!user.IsVocal()) - user.custom_emote(1, "makes frantic gestures!") - else - if(prob(50))//Auto-mute? Fuck that noise - user.say(invocation) - else - user.say(replacetext(invocation," ","`")) - if("whisper") - if(prob(50)) - user.whisper(invocation) - else - user.whisper(replacetext(invocation," ","`")) - if("emote") - user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm - -/obj/effect/proc_holder/spell/proc/playMagSound() - playsound(get_turf(usr), sound,50,1) - -/obj/effect/proc_holder/spell/New() - ..() - action = new(src) - still_recharging_msg = "[name] is still recharging." - if(starts_charged) - charge_counter = charge_max - else - start_recharge() - -/obj/effect/proc_holder/spell/Destroy() - QDEL_NULL(action) - return ..() - -/obj/effect/proc_holder/spell/Click() - if(cast_check()) - choose_targets() - return 1 - -/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf - return - -/obj/effect/proc_holder/spell/proc/start_recharge() - if(action) - action.UpdateButtonIcon() - START_PROCESSING(SSfastprocess, src) - -/obj/effect/proc_holder/spell/process() - charge_counter += 2 - if(charge_counter < charge_max) - return - STOP_PROCESSING(SSfastprocess, src) - charge_counter = charge_max - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells - before_cast(targets) - invocation() - if(user && user.ckey) - user.create_attack_log("[key_name(user)] cast the spell [name].") - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - - if(sound) - playMagSound() - - if(prob(critfailchance)) - critfail(targets) - else - cast(targets, user = user) - after_cast(targets) - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/before_cast(list/targets) - if(overlay) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - var/obj/effect/overlay/spell = new /obj/effect/overlay(location) - spell.icon = overlay_icon - spell.icon_state = overlay_icon_state - spell.anchored = 1 - spell.density = 0 - spawn(overlay_lifespan) - qdel(spell) - -/obj/effect/proc_holder/spell/proc/after_cast(list/targets) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - if(istype(target,/mob/living) && message) - to_chat(target, text("[message]")) - if(sparks_spread) - do_sparks(sparks_amt, 0, location) - if(smoke_spread) - if(smoke_spread == 1) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 2) - var/datum/effect_system/smoke_spread/bad/smoke = new - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 3) - var/datum/effect_system/smoke_spread/sleeping/smoke = new - smoke.set_up(smoke_amt, 0, location) // same here - smoke.start() - -/obj/effect/proc_holder/spell/proc/cast(list/targets, mob/user = usr) - return - -/obj/effect/proc_holder/spell/proc/critfail(list/targets) - return - -/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge - switch(charge_type) - if("recharge") - charge_counter = charge_max - if("charges") - charge_counter++ - if("holdervar") - adjust_var(user, holder_var_type, -holder_var_amount) - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/updateButtonIcon() - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types - switch(type) - if("bruteloss") - target.adjustBruteLoss(amount) - if("fireloss") - target.adjustFireLoss(amount) - if("toxloss") - target.adjustToxLoss(amount) - if("oxyloss") - target.adjustOxyLoss(amount) - if("stunned") - target.AdjustStunned(amount) - if("weakened") - target.AdjustWeakened(amount) - if("paralysis") - target.AdjustParalysis(amount) - else - target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars - return - -/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob - var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range - var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast - var/include_user = 0 //if it includes usr in the target list - var/random_target = 0 // chooses random viable target instead of asking the caster - var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target - var/humans_only = 0 //for avoiding simple animals and only doing "human" mobs, 0 = all mobs, 1 = humans only - -/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) - var/inner_radius = -1 //for all your ring spell needs - -/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) - var/list/targets = list() - - switch(max_targets) - if(0) //unlimited - - if(!humans_only) - for(var/mob/living/target in view_or_range(range, user, selection_type)) - targets += target - else - for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) - targets += target - - if(1) //single target can be picked - if(range < 0) - targets += user - else - var/possible_targets = list() - - if(!humans_only) - for(var/mob/living/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - else - for(var/mob/living/carbon/human/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - - //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets - //Adds a safety check post-input to make sure those targets are actually in range. - var/mob/M - if(!random_target) - M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets - else - switch(random_target_priority) - if(TARGET_RANDOM) - M = pick(possible_targets) - if(TARGET_CLOSEST) - for(var/mob/living/L in possible_targets) - if(M) - if(get_dist(user,L) < get_dist(user,M)) - if(los_check(user,L)) - M = L - else - if(los_check(user,L)) - M = L - if(M in view_or_range(range, user, selection_type)) targets += M - - else - var/list/possible_targets = list() - if(!humans_only) - for(var/mob/living/target in view_or_range(range, user, selection_type)) - possible_targets += target - else - for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) - possible_targets += target - for(var/i=1,i<=max_targets,i++) - if(!possible_targets.len) - break - if(target_ignore_prev) - var/target = pick(possible_targets) - possible_targets -= target - targets += target - else - targets += pick(possible_targets) - - if(!include_user && (user in targets)) - targets -= user - - if(!targets.len) //doesn't waste the spell - revert_cast(user) - return - - perform(targets, user=user) - - return - -/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) - var/list/targets = list() - - for(var/turf/target in view_or_range(range,user,selection_type)) - if(!(target in view_or_range(inner_radius,user,selection_type))) - targets += target - - if(!targets.len) //doesn't waste the spell - revert_cast() - return - - perform(targets, user=user) - - return - - -/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) - //Checks for obstacles from A to B - var/obj/dummy = new(A.loc) - dummy.pass_flags |= PASSTABLE - for(var/turf/turf in getline(A,B)) - for(var/atom/movable/AM in turf) - if(!AM.CanPass(dummy,turf,1)) - qdel(dummy) - return 0 - qdel(dummy) - return 1 - -/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - return 0 - - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - return 0 - if("charges") - if(!charge_counter) - return 0 - - if(user.stat && !stat_allowed) - return 0 - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - - if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) - return 0 - - var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list - var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) - if(clothes_req && !clothcheck && !clothcheck2) //clothes check - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - return 0 - else - if(clothes_req || human_req) - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - return 0 - return 1 +#define TARGET_CLOSEST 1 +#define TARGET_RANDOM 2 + +/obj/effect/proc_holder + var/panel = "Debug"//What panel the proc holder needs to go on. + var/active = FALSE //Used by toggle based abilities. + var/ranged_mousepointer + var/mob/living/ranged_ability_user + +/obj/effect/proc_holder/singularity_act() + return + +/obj/effect/proc_holder/singularity_pull() + return + +GLOBAL_LIST_INIT(spells, typesof(/obj/effect/proc_holder/spell)) + +/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/user, params, atom/A) + if(user.ranged_ability != src) + to_chat(user, "[user.ranged_ability.name] has been disabled.") + user.ranged_ability.remove_ranged_ability(user) + return TRUE //TRUE for failed, FALSE for passed. + user.changeNext_click(CLICK_CD_CLICK_ABILITY) + user.face_atom(A) + return FALSE + +/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) + if(!user || !user.client) + return + if(user.ranged_ability && user.ranged_ability != src) + to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") + user.ranged_ability.remove_ranged_ability(user) + user.ranged_ability = src + ranged_ability_user = user + user.client.click_intercept = user.ranged_ability + add_mousepointer(user.client) + active = TRUE + if(msg) + to_chat(user, msg) + update_icon() + +/obj/effect/proc_holder/proc/add_mousepointer(client/C) + if(C && ranged_mousepointer && C.mouse_pointer_icon == initial(C.mouse_pointer_icon)) + C.mouse_pointer_icon = ranged_mousepointer + +/obj/effect/proc_holder/proc/remove_mousepointer(client/C) + if(C && ranged_mousepointer && C.mouse_pointer_icon == ranged_mousepointer) + C.mouse_pointer_icon = initial(C.mouse_pointer_icon) + +/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) + if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability + return + user.ranged_ability = null + ranged_ability_user = null + user.client.click_intercept = null + remove_mousepointer(user.client) + active = FALSE + if(msg) + to_chat(user, msg) + update_icon() + +/obj/effect/proc_holder/spell + name = "Spell" + desc = "A wizard spell" + panel = "Spells"//What panel the proc holder needs to go on. + density = 0 + opacity = 0 + + var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? + + var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that + + var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" + var/starts_charged = TRUE //Does this spell start ready to go? + var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" + var/still_recharging_msg = "The spell is still recharging." + + var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" + var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used + + var/ghost = 0 // Skip life check. + var/clothes_req = 1 //see if it requires clothes + var/human_req = 0 //spell can only be cast by humans + var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities + var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells + var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell + var/invocation_emote_self = null + var/invocation_type = "none" //can be none, whisper and shout + var/range = 7 //the range of the spell; outer radius for aoe spells + var/message = "" //whatever it says to the guy affected by it + var/selection_type = "view" //can be "range" or "view" + var/spell_level = 0 //if a spell can be taken multiple times, this raises + var/level_max = 4 //The max possible level_max is 4 + var/cooldown_min = 0 //This defines what spell quickened four timeshas as a cooldown. Make sure to set this for every spell + + var/overlay = 0 + var/overlay_icon = 'icons/obj/wizard.dmi' + var/overlay_icon_state = "spell" + var/overlay_lifespan = 0 + + var/sparks_spread = 0 + var/sparks_amt = 0 //cropped at 10 + var/smoke_spread = 0 //1 - harmless, 2 - harmful + var/smoke_amt = 0 //cropped at 10 + + var/critfailchance = 0 + var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 + + var/datum/action/spell_action/action = null + var/action_icon = 'icons/mob/actions/actions.dmi' + var/action_icon_state = "spell_default" + var/action_background_icon_state = "bg_spell" + var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable() + + var/sound = null //The sound the spell makes when it is cast + +/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + to_chat(user, "You shouldn't have this spell! Something's wrong.") + return 0 + + if(ishuman(user)) + var/mob/living/carbon/human/caster = user + if(caster.remoteview_target) + caster.remoteview_target = null + caster.reset_perspective(0) + return 0 + + if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + to_chat(user, still_recharging_msg) + return 0 + if("charges") + if(!charge_counter) + to_chat(user, "[name] has no charges left.") + return 0 + + if(!ghost) + if(user.stat && !stat_allowed) + to_chat(user, "You can't cast this spell while incapacitated.") + return 0 + if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) + to_chat(user, "Mmmf mrrfff!") + return 0 + + var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) + if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check + var/mob/living/carbon/human/H = user + var/obj/item/clothing/robe = H.wear_suit + var/obj/item/clothing/hat = H.head + var/obj/item/clothing/shoes = H.shoes + if(!robe || !hat || !shoes) + to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") + return 0 + if(!robe.magical || !hat.magical || !shoes.magical) + to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") + return 0 + else if(!ishuman(user)) + if(clothes_req || human_req) + to_chat(user, "This spell can only be cast by humans!") + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + to_chat(user, "This spell can only be cast by physical beings!") + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + charge_counter = 0 //doesn't start recharging until the targets selecting ends + if("charges") + charge_counter-- //returns the charge if the targets selecting fails + if("holdervar") + adjust_var(user, holder_var_type, holder_var_amount) + + if(action) + action.UpdateButtonIcon() + return 1 + +/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount + switch(invocation_type) + if("shout") + if(!user.IsVocal()) + user.custom_emote(1, "makes frantic gestures!") + else + if(prob(50))//Auto-mute? Fuck that noise + user.say(invocation) + else + user.say(replacetext(invocation," ","`")) + if("whisper") + if(prob(50)) + user.whisper(invocation) + else + user.whisper(replacetext(invocation," ","`")) + if("emote") + user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm + +/obj/effect/proc_holder/spell/proc/playMagSound() + playsound(get_turf(usr), sound,50,1) + +/obj/effect/proc_holder/spell/New() + ..() + action = new(src) + still_recharging_msg = "[name] is still recharging." + if(starts_charged) + charge_counter = charge_max + else + start_recharge() + +/obj/effect/proc_holder/spell/Destroy() + QDEL_NULL(action) + return ..() + +/obj/effect/proc_holder/spell/Click() + if(cast_check()) + choose_targets() + return 1 + +/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf + return + +/obj/effect/proc_holder/spell/proc/start_recharge() + if(action) + action.UpdateButtonIcon() + START_PROCESSING(SSfastprocess, src) + +/obj/effect/proc_holder/spell/process() + charge_counter += 2 + if(charge_counter < charge_max) + return + STOP_PROCESSING(SSfastprocess, src) + charge_counter = charge_max + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells + before_cast(targets) + invocation() + if(user && user.ckey) + add_attack_logs(user, null, "cast the spell [name]", ATKLOG_ALL) + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + + if(sound) + playMagSound() + + if(prob(critfailchance)) + critfail(targets) + else + cast(targets, user = user) + after_cast(targets) + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/before_cast(list/targets) + if(overlay) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + var/obj/effect/overlay/spell = new /obj/effect/overlay(location) + spell.icon = overlay_icon + spell.icon_state = overlay_icon_state + spell.anchored = 1 + spell.density = 0 + spawn(overlay_lifespan) + qdel(spell) + +/obj/effect/proc_holder/spell/proc/after_cast(list/targets) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + if(istype(target,/mob/living) && message) + to_chat(target, text("[message]")) + if(sparks_spread) + do_sparks(sparks_amt, 0, location) + if(smoke_spread) + if(smoke_spread == 1) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is + smoke.start() + else if(smoke_spread == 2) + var/datum/effect_system/smoke_spread/bad/smoke = new + smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is + smoke.start() + else if(smoke_spread == 3) + var/datum/effect_system/smoke_spread/sleeping/smoke = new + smoke.set_up(smoke_amt, 0, location) // same here + smoke.start() + +/obj/effect/proc_holder/spell/proc/cast(list/targets, mob/user = usr) + return + +/obj/effect/proc_holder/spell/proc/critfail(list/targets) + return + +/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge + switch(charge_type) + if("recharge") + charge_counter = charge_max + if("charges") + charge_counter++ + if("holdervar") + adjust_var(user, holder_var_type, -holder_var_amount) + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/updateButtonIcon() + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types + switch(type) + if("bruteloss") + target.adjustBruteLoss(amount) + if("fireloss") + target.adjustFireLoss(amount) + if("toxloss") + target.adjustToxLoss(amount) + if("oxyloss") + target.adjustOxyLoss(amount) + if("stunned") + target.AdjustStunned(amount) + if("weakened") + target.AdjustWeakened(amount) + if("paralysis") + target.AdjustParalysis(amount) + else + target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars + return + +/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob + var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range + var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast + var/include_user = 0 //if it includes usr in the target list + var/random_target = 0 // chooses random viable target instead of asking the caster + var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target + var/humans_only = 0 //for avoiding simple animals and only doing "human" mobs, 0 = all mobs, 1 = humans only + +/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) + var/inner_radius = -1 //for all your ring spell needs + +/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) + var/list/targets = list() + + switch(max_targets) + if(0) //unlimited + + if(!humans_only) + for(var/mob/living/target in view_or_range(range, user, selection_type)) + targets += target + else + for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) + targets += target + + if(1) //single target can be picked + if(range < 0) + targets += user + else + var/possible_targets = list() + + if(!humans_only) + for(var/mob/living/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + else + for(var/mob/living/carbon/human/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + + //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets + //Adds a safety check post-input to make sure those targets are actually in range. + var/mob/M + if(!random_target) + M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets + else + switch(random_target_priority) + if(TARGET_RANDOM) + M = pick(possible_targets) + if(TARGET_CLOSEST) + for(var/mob/living/L in possible_targets) + if(M) + if(get_dist(user,L) < get_dist(user,M)) + if(los_check(user,L)) + M = L + else + if(los_check(user,L)) + M = L + if(M in view_or_range(range, user, selection_type)) targets += M + + else + var/list/possible_targets = list() + if(!humans_only) + for(var/mob/living/target in view_or_range(range, user, selection_type)) + possible_targets += target + else + for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) + possible_targets += target + for(var/i=1,i<=max_targets,i++) + if(!possible_targets.len) + break + if(target_ignore_prev) + var/target = pick(possible_targets) + possible_targets -= target + targets += target + else + targets += pick(possible_targets) + + if(!include_user && (user in targets)) + targets -= user + + if(!targets.len) //doesn't waste the spell + revert_cast(user) + return + + perform(targets, user=user) + + return + +/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) + var/list/targets = list() + + for(var/turf/target in view_or_range(range,user,selection_type)) + if(!(target in view_or_range(inner_radius,user,selection_type))) + targets += target + + if(!targets.len) //doesn't waste the spell + revert_cast() + return + + perform(targets, user=user) + + return + + +/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) + //Checks for obstacles from A to B + var/obj/dummy = new(A.loc) + dummy.pass_flags |= PASSTABLE + for(var/turf/turf in getline(A,B)) + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy,turf,1)) + qdel(dummy) + return 0 + qdel(dummy) + return 1 + +/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + return 0 + + if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + return 0 + if("charges") + if(!charge_counter) + return 0 + + if(user.stat && !stat_allowed) + return 0 + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + + if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) + return 0 + + var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list + var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) + if(clothes_req && !clothcheck && !clothcheck2) //clothes check + var/obj/item/clothing/robe = H.wear_suit + var/obj/item/clothing/hat = H.head + var/obj/item/clothing/shoes = H.shoes + if(!robe || !hat || !shoes) + return 0 + if(!robe.magical || !hat.magical || !shoes.magical) + return 0 + else + if(clothes_req || human_req) + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + return 0 + return 1 diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm index 4aa7eafbed67..8a3585febc0b 100644 --- a/code/datums/spells/area_teleport.dm +++ b/code/datums/spells/area_teleport.dm @@ -1,98 +1,98 @@ -/obj/effect/proc_holder/spell/targeted/area_teleport - name = "Area teleport" - desc = "This spell teleports you to a type of area of your selection." - nonabstract_req = 1 - - var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list - var/invocation_area = 1 //if the invocation appends the selected area - - var/sound1 = 'sound/weapons/zapbang.ogg' - var/sound2 = 'sound/weapons/zapbang.ogg' - -/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) - var/thearea = before_cast(targets) - if(!thearea || !cast_check(1)) - revert_cast() - return - invocation(thearea) - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - cast(targets,thearea) - after_cast(targets) - -/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets) - var/A = null - - if(!randomise_selection) - A = input("Area to teleport to", "Teleport", A) as null|anything in teleportlocs - else - A = pick(teleportlocs) - - if(!A) - return - - var/area/thearea = teleportlocs[A] - - if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) - to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") - return - - return thearea - -/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/living/user = usr) - playsound(get_turf(user), sound1, 50,1) - for(var/mob/living/target in targets) - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L.len) - to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - if(target && target.buckled) - target.buckled.unbuckle_mob(target, force = TRUE) - - if(target && target.has_buckled_mobs()) - target.unbuckle_all_mobs(force = TRUE) - - var/list/tempL = L - var/attempt = null - var/success = 0 - while(tempL.len) - attempt = pick(tempL) - success = target.Move(attempt) - if(!success) - tempL.Remove(attempt) - else - break - - if(!success) - target.forceMove(pick(L)) - playsound(get_turf(user), sound2, 50,1) - - return - -/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) - if(!invocation_area || !chosenarea) - ..() - else - switch(invocation_type) - if("shout") - usr.say("[invocation] [uppertext(chosenarea.name)]") - if(usr.gender==MALE) - playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) - else - playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) - if("whisper") - usr.whisper("[invocation] [uppertext(chosenarea.name)]") - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/area_teleport + name = "Area teleport" + desc = "This spell teleports you to a type of area of your selection." + nonabstract_req = 1 + + var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list + var/invocation_area = 1 //if the invocation appends the selected area + + var/sound1 = 'sound/weapons/zapbang.ogg' + var/sound2 = 'sound/weapons/zapbang.ogg' + +/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) + var/thearea = before_cast(targets) + if(!thearea || !cast_check(1)) + revert_cast() + return + invocation(thearea) + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + cast(targets,thearea) + after_cast(targets) + +/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets) + var/A = null + + if(!randomise_selection) + A = input("Area to teleport to", "Teleport", A) as null|anything in GLOB.teleportlocs + else + A = pick(GLOB.teleportlocs) + + if(!A) + return + + var/area/thearea = GLOB.teleportlocs[A] + + if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) + to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") + return + + return thearea + +/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/living/user = usr) + playsound(get_turf(user), sound1, 50,1) + for(var/mob/living/target in targets) + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L.len) + to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") + return + + if(target && target.buckled) + target.buckled.unbuckle_mob(target, force = TRUE) + + if(target && target.has_buckled_mobs()) + target.unbuckle_all_mobs(force = TRUE) + + var/list/tempL = L + var/attempt = null + var/success = 0 + while(tempL.len) + attempt = pick(tempL) + success = target.Move(attempt) + if(!success) + tempL.Remove(attempt) + else + break + + if(!success) + target.forceMove(pick(L)) + playsound(get_turf(user), sound2, 50,1) + + return + +/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) + if(!invocation_area || !chosenarea) + ..() + else + switch(invocation_type) + if("shout") + usr.say("[invocation] [uppertext(chosenarea.name)]") + if(usr.gender==MALE) + playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) + else + playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) + if("whisper") + usr.whisper("[invocation] [uppertext(chosenarea.name)]") + + return diff --git a/code/datums/spells/banana_touch.dm b/code/datums/spells/banana_touch.dm index 4e2f1d44e51a..e8b7f27b0dac 100644 --- a/code/datums/spells/banana_touch.dm +++ b/code/datums/spells/banana_touch.dm @@ -55,10 +55,10 @@ equip_to_slot_if_possible(new /obj/item/clothing/under/rank/clown/nodrop, slot_w_uniform, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/shoes/clown_shoes/nodrop, slot_shoes, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/clown_hat/nodrop, slot_wear_mask, TRUE, TRUE) - dna.SetSEState(CLUMSYBLOCK, TRUE, TRUE) - dna.SetSEState(COMICBLOCK, TRUE, TRUE) - genemutcheck(src, CLUMSYBLOCK, null, MUTCHK_FORCED) - genemutcheck(src, COMICBLOCK, null, MUTCHK_FORCED) + dna.SetSEState(GLOB.clumsyblock, TRUE, TRUE) + dna.SetSEState(GLOB.comicblock, TRUE, TRUE) + genemutcheck(src, GLOB.clumsyblock, null, MUTCHK_FORCED) + genemutcheck(src, GLOB.comicblock, null, MUTCHK_FORCED) if(!(iswizard(src) || (mind && mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE))) //Mutations are permanent on non-wizards. Can still be removed by genetics fuckery but not mutadone. - dna.default_blocks.Add(CLUMSYBLOCK) - dna.default_blocks.Add(COMICBLOCK) \ No newline at end of file + dna.default_blocks.Add(GLOB.clumsyblock) + dna.default_blocks.Add(GLOB.comicblock) diff --git a/code/datums/spells/cluwne.dm b/code/datums/spells/cluwne.dm index 58a33e31a44c..91df261e9c22 100644 --- a/code/datums/spells/cluwne.dm +++ b/code/datums/spells/cluwne.dm @@ -27,8 +27,8 @@ var/obj/item/organ/internal/honktumor/cursed/tumor = new tumor.insert(src) mutations.Add(NERVOUS) - dna.SetSEState(NERVOUSBLOCK, 1, 1) - genemutcheck(src, NERVOUSBLOCK, null, MUTCHK_FORCED) + dna.SetSEState(GLOB.nervousblock, 1, 1) + genemutcheck(src, GLOB.nervousblock, null, MUTCHK_FORCED) rename_character(real_name, "cluwne") unEquip(w_uniform, 1) @@ -56,14 +56,14 @@ tumor.remove(src) else mutations.Remove(CLUMSY) - mutations.Remove(COMICBLOCK) - dna.SetSEState(CLUMSYBLOCK,0) - dna.SetSEState(COMICBLOCK,0) - genemutcheck(src, CLUMSYBLOCK, null, MUTCHK_FORCED) - genemutcheck(src, COMICBLOCK, null, MUTCHK_FORCED) + mutations.Remove(GLOB.comicblock) + dna.SetSEState(GLOB.clumsyblock,0) + dna.SetSEState(GLOB.comicblock,0) + genemutcheck(src, GLOB.clumsyblock, null, MUTCHK_FORCED) + genemutcheck(src, GLOB.comicblock, null, MUTCHK_FORCED) mutations.Remove(NERVOUS) - dna.SetSEState(NERVOUSBLOCK, 0) - genemutcheck(src, NERVOUSBLOCK, null, MUTCHK_FORCED) + dna.SetSEState(GLOB.nervousblock, 0) + genemutcheck(src, GLOB.nervousblock, null, MUTCHK_FORCED) var/obj/item/clothing/under/U = w_uniform unEquip(w_uniform, 1) diff --git a/code/datums/spells/conjure.dm b/code/datums/spells/conjure.dm index ebf9fa59fe81..306ec8b9987f 100644 --- a/code/datums/spells/conjure.dm +++ b/code/datums/spells/conjure.dm @@ -1,70 +1,70 @@ -/obj/effect/proc_holder/spell/aoe_turf/conjure - name = "Conjure" - desc = "This spell conjures objs of the specified types in range." - - var/list/summon_type = list() //determines what exactly will be summoned - //should be text, like list("/mob/simple_animal/bot/ed209") - - var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds - var/summon_amt = 1 //amount of objects summoned - var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places - var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point - - var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet - //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example - var/delay = 1//Go Go Gadget Inheritance - - var/cast_sound = 'sound/items/welder.ogg' - -/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/living/user = usr) - playsound(get_turf(user), cast_sound, 50,1) - for(var/turf/T in targets) - if(T.density && !summon_ignore_density) - targets -= T - playsound(get_turf(src), cast_sound, 50, 1) - - if(do_after(user, delay, target = user)) - for(var/i=0,iYou can't build things on shuttles!") - break - var/turf/O = spawn_place - var/N = summoned_object_type - O.ChangeTurf(N) - else - var/atom/summoned_object = new summoned_object_type(spawn_place) - - for(var/varName in newVars) - if(varName in summoned_object.vars) - summoned_object.vars[varName] = newVars[varName] - summoned_object.admin_spawned = TRUE - - if(summon_lifespan) - spawn(summon_lifespan) - if(summoned_object) - qdel(summoned_object) - else - switch(charge_type) - if("recharge") - charge_counter = charge_max - 5//So you don't lose charge for a failed spell(Also prevents most over-fill) - if("charges") - charge_counter++//Ditto, just for different spell types - - - return - -/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes - name = "Dispense Wizard Justice" - desc = "This spell dispenses wizard justice." - - summon_type = list(/mob/living/simple_animal/bot/ed209) - summon_amt = 10 - range = 3 - newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") \ No newline at end of file +/obj/effect/proc_holder/spell/aoe_turf/conjure + name = "Conjure" + desc = "This spell conjures objs of the specified types in range." + + var/list/summon_type = list() //determines what exactly will be summoned + //should be text, like list("/mob/simple_animal/bot/ed209") + + var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds + var/summon_amt = 1 //amount of objects summoned + var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places + var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point + + var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet + //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example + var/delay = 1//Go Go Gadget Inheritance + + var/cast_sound = 'sound/items/welder.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/living/user = usr) + playsound(get_turf(user), cast_sound, 50,1) + for(var/turf/T in targets) + if(T.density && !summon_ignore_density) + targets -= T + playsound(get_turf(src), cast_sound, 50, 1) + + if(do_after(user, delay, target = user)) + for(var/i=0,iYou can't build things on shuttles!") + break + var/turf/O = spawn_place + var/N = summoned_object_type + O.ChangeTurf(N) + else + var/atom/summoned_object = new summoned_object_type(spawn_place) + + for(var/varName in newVars) + if(varName in summoned_object.vars) + summoned_object.vars[varName] = newVars[varName] + summoned_object.admin_spawned = TRUE + + if(summon_lifespan) + spawn(summon_lifespan) + if(summoned_object) + qdel(summoned_object) + else + switch(charge_type) + if("recharge") + charge_counter = charge_max - 5//So you don't lose charge for a failed spell(Also prevents most over-fill) + if("charges") + charge_counter++//Ditto, just for different spell types + + + return + +/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes + name = "Dispense Wizard Justice" + desc = "This spell dispenses wizard justice." + + summon_type = list(/mob/living/simple_animal/bot/ed209) + summon_amt = 10 + range = 3 + newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") diff --git a/code/datums/spells/dumbfire.dm b/code/datums/spells/dumbfire.dm index ca8cc43df9c4..9efb93a0e7b7 100644 --- a/code/datums/spells/dumbfire.dm +++ b/code/datums/spells/dumbfire.dm @@ -1,84 +1,84 @@ -/obj/effect/proc_holder/spell/dumbfire - - var/projectile_type = "" - var/activate_on_collision = 1 - - var/proj_icon = 'icons/obj/projectiles.dmi' - var/proj_icon_state = "spell" - var/proj_name = "a spell projectile" - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - - var/proj_type = /obj/effect/proc_holder/spell //IMPORTANT use only subtypes of this - - var/proj_insubstantial = 0 //if it can pass through dense objects or not - var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target) - - var/proj_lifespan = 100 //in deciseconds * proj_step_delay - var/proj_step_delay = 1 //lower = faster - -/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) - - var/turf/T = get_turf(usr) - for(var/i = 1; i < range; i++) - var/turf/new_turf = get_step(T, usr.dir) - if(new_turf.density) - break - T = new_turf - perform(list(T), user = user) - -/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) - - for(var/turf/target in targets) - spawn(0) - var/obj/effect/proc_holder/spell/targeted/projectile - projectile = new proj_type(user) - projectile.icon = proj_icon - projectile.icon_state = proj_icon_state - projectile.dir = get_dir(projectile, target) - projectile.name = proj_name - - var/current_loc = user.loc - - projectile.loc = current_loc - - for(var/i = 0,i < proj_lifespan,i++) - if(!projectile) - break - - if(proj_insubstantial) - projectile.loc = get_step(projectile, projectile.dir) - else - step(projectile, projectile.dir) - - if(projectile.loc == current_loc || i == proj_lifespan) - projectile.cast(current_loc) - break - - var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user - if(L && L.stat != DEAD) - projectile.cast(L.loc) - break - - if(proj_trail && projectile) - spawn(0) - if(projectile) - var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.density = 0 - spawn(proj_trail_lifespan) - qdel(trail) - - current_loc = projectile.loc - var/matrix/M = new - M.Turn(dir2angle(projectile.dir)) - projectile.transform = M - - sleep(proj_step_delay) - - if(projectile) - qdel(projectile) +/obj/effect/proc_holder/spell/dumbfire + + var/projectile_type = "" + var/activate_on_collision = 1 + + var/proj_icon = 'icons/obj/projectiles.dmi' + var/proj_icon_state = "spell" + var/proj_name = "a spell projectile" + + var/proj_trail = 0 //if it leaves a trail + var/proj_trail_lifespan = 0 //deciseconds + var/proj_trail_icon = 'icons/obj/wizard.dmi' + var/proj_trail_icon_state = "trail" + + var/proj_type = /obj/effect/proc_holder/spell //IMPORTANT use only subtypes of this + + var/proj_insubstantial = 0 //if it can pass through dense objects or not + var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target) + + var/proj_lifespan = 100 //in deciseconds * proj_step_delay + var/proj_step_delay = 1 //lower = faster + +/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) + + var/turf/T = get_turf(usr) + for(var/i = 1; i < range; i++) + var/turf/new_turf = get_step(T, usr.dir) + if(new_turf.density) + break + T = new_turf + perform(list(T), user = user) + +/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) + + for(var/turf/target in targets) + spawn(0) + var/obj/effect/proc_holder/spell/targeted/projectile + projectile = new proj_type(user) + projectile.icon = proj_icon + projectile.icon_state = proj_icon_state + projectile.dir = get_dir(projectile, target) + projectile.name = proj_name + + var/current_loc = user.loc + + projectile.loc = current_loc + + for(var/i = 0,i < proj_lifespan,i++) + if(!projectile) + break + + if(proj_insubstantial) + projectile.loc = get_step(projectile, projectile.dir) + else + step(projectile, projectile.dir) + + if(projectile.loc == current_loc || i == proj_lifespan) + projectile.cast(current_loc) + break + + var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user + if(L && L.stat != DEAD) + projectile.cast(L.loc) + break + + if(proj_trail && projectile) + spawn(0) + if(projectile) + var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.density = 0 + spawn(proj_trail_lifespan) + qdel(trail) + + current_loc = projectile.loc + var/matrix/M = new + M.Turn(dir2angle(projectile.dir)) + projectile.transform = M + + sleep(proj_step_delay) + + if(projectile) + qdel(projectile) diff --git a/code/datums/spells/emplosion.dm b/code/datums/spells/emplosion.dm index b2f0a2282826..cd84432aba84 100644 --- a/code/datums/spells/emplosion.dm +++ b/code/datums/spells/emplosion.dm @@ -1,15 +1,15 @@ -/obj/effect/proc_holder/spell/targeted/emplosion - name = "Emplosion" - desc = "This spell emplodes an area." - - var/emp_heavy = 2 - var/emp_light = 3 - - action_icon_state = "emp" - -/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - empulse(target.loc, emp_heavy, emp_light, 1) - - return +/obj/effect/proc_holder/spell/targeted/emplosion + name = "Emplosion" + desc = "This spell emplodes an area." + + var/emp_heavy = 2 + var/emp_light = 3 + + action_icon_state = "emp" + +/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + empulse(target.loc, emp_heavy, emp_light, 1) + + return diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 3103d8d5e5d2..f7d7a9bfb8d8 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -1,104 +1,104 @@ -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt - name = "Ethereal Jaunt" - desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." - - school = "transmutation" - charge_max = 300 - clothes_req = 1 - invocation = "none" - invocation_type = "none" - range = -1 - cooldown_min = 100 //50 deciseconds reduction per rank - include_user = 1 - nonabstract_req = 1 - centcom_cancast = 0 //Prevent people from getting to centcom - - var/jaunt_duration = 50 //in deciseconds - var/jaunt_in_time = 5 - var/jaunt_in_type = /obj/effect/temp_visual/wizard - var/jaunt_out_type = /obj/effect/temp_visual/wizard/out - - action_icon_state = "jaunt" - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded - playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) - for(var/mob/living/target in targets) - if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains - to_chat(target, "You are somehow too bound to your current location to abandon it.") - continue - INVOKE_ASYNC(src, .proc/do_jaunt, target) - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) - target.notransform = 1 - var/turf/mobloc = get_turf(target) - var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc) - new jaunt_out_type(mobloc, target.dir) - target.ExtinguishMob() - target.forceMove(holder) - target.reset_perspective(holder) - target.notransform = 0 //mob is safely inside holder now, no need for protection. - jaunt_steam(mobloc) - - sleep(jaunt_duration) - - if(target.loc != holder) //mob warped out of the warp - qdel(holder) - return - mobloc = get_turf(target.loc) - jaunt_steam(mobloc) - target.canmove = 0 - holder.reappearing = 1 - playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) - sleep(25 - jaunt_in_time) - new jaunt_in_type(mobloc, holder.dir) - target.setDir(holder.dir) - sleep(jaunt_in_time) - qdel(holder) - if(!QDELETED(target)) - if(mobloc.density) - for(var/direction in alldirs) - var/turf/T = get_step(mobloc, direction) - if(T) - if(target.Move(T)) - break - target.canmove = 1 - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) - var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() - steam.set_up(10, 0, mobloc) - steam.start() - -/obj/effect/dummy/spell_jaunt - name = "water" - icon = 'icons/effects/effects.dmi' - icon_state = "nothing" - var/reappearing = 0 - var/movedelay = 0 - var/movespeed = 2 - density = 0 - anchored = 1 - invisibility = 60 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/effect/dummy/spell_jaunt/Destroy() - // Eject contents if deleted somehow - for(var/atom/movable/AM in src) - AM.forceMove(get_turf(src)) - return ..() - -/obj/effect/dummy/spell_jaunt/relaymove(mob/user, direction) - if((movedelay > world.time) || reappearing || !direction) - return - var/turf/newLoc = get_step(src,direction) - setDir(direction) - if(!(newLoc.flags & NOJAUNT)) - forceMove(newLoc) - else - to_chat(user, "Some strange aura is blocking the way!") - movedelay = world.time + movespeed - -/obj/effect/dummy/spell_jaunt/ex_act(blah) - return - -/obj/effect/dummy/spell_jaunt/bullet_act(blah) - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt + name = "Ethereal Jaunt" + desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." + + school = "transmutation" + charge_max = 300 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 + cooldown_min = 100 //50 deciseconds reduction per rank + include_user = 1 + nonabstract_req = 1 + centcom_cancast = 0 //Prevent people from getting to centcom + + var/jaunt_duration = 50 //in deciseconds + var/jaunt_in_time = 5 + var/jaunt_in_type = /obj/effect/temp_visual/wizard + var/jaunt_out_type = /obj/effect/temp_visual/wizard/out + + action_icon_state = "jaunt" + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded + playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) + for(var/mob/living/target in targets) + if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains + to_chat(target, "You are somehow too bound to your current location to abandon it.") + continue + INVOKE_ASYNC(src, .proc/do_jaunt, target) + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) + target.notransform = 1 + var/turf/mobloc = get_turf(target) + var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc) + new jaunt_out_type(mobloc, target.dir) + target.ExtinguishMob() + target.forceMove(holder) + target.reset_perspective(holder) + target.notransform = 0 //mob is safely inside holder now, no need for protection. + jaunt_steam(mobloc) + + sleep(jaunt_duration) + + if(target.loc != holder) //mob warped out of the warp + qdel(holder) + return + mobloc = get_turf(target.loc) + jaunt_steam(mobloc) + target.canmove = 0 + holder.reappearing = 1 + playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) + sleep(25 - jaunt_in_time) + new jaunt_in_type(mobloc, holder.dir) + target.setDir(holder.dir) + sleep(jaunt_in_time) + qdel(holder) + if(!QDELETED(target)) + if(mobloc.density) + for(var/direction in GLOB.alldirs) + var/turf/T = get_step(mobloc, direction) + if(T) + if(target.Move(T)) + break + target.canmove = 1 + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) + var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() + steam.set_up(10, 0, mobloc) + steam.start() + +/obj/effect/dummy/spell_jaunt + name = "water" + icon = 'icons/effects/effects.dmi' + icon_state = "nothing" + var/reappearing = 0 + var/movedelay = 0 + var/movespeed = 2 + density = 0 + anchored = 1 + invisibility = 60 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/effect/dummy/spell_jaunt/Destroy() + // Eject contents if deleted somehow + for(var/atom/movable/AM in src) + AM.forceMove(get_turf(src)) + return ..() + +/obj/effect/dummy/spell_jaunt/relaymove(mob/user, direction) + if((movedelay > world.time) || reappearing || !direction) + return + var/turf/newLoc = get_step(src,direction) + setDir(direction) + if(!(newLoc.flags & NOJAUNT)) + forceMove(newLoc) + else + to_chat(user, "Some strange aura is blocking the way!") + movedelay = world.time + movespeed + +/obj/effect/dummy/spell_jaunt/ex_act(blah) + return + +/obj/effect/dummy/spell_jaunt/bullet_act(blah) + return diff --git a/code/datums/spells/explosion.dm b/code/datums/spells/explosion.dm index 4beeae1206ab..22580d9a8969 100644 --- a/code/datums/spells/explosion.dm +++ b/code/datums/spells/explosion.dm @@ -1,15 +1,15 @@ -/obj/effect/proc_holder/spell/targeted/explosion - name = "Explosion" - desc = "This spell explodes an area." - - var/ex_severe = 1 - var/ex_heavy = 2 - var/ex_light = 3 - var/ex_flash = 4 - -/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash) - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/explosion + name = "Explosion" + desc = "This spell explodes an area." + + var/ex_severe = 1 + var/ex_heavy = 2 + var/ex_light = 3 + var/ex_flash = 4 + +/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash) + + return diff --git a/code/datums/spells/fake_gib.dm b/code/datums/spells/fake_gib.dm index 6e2f31ea1581..dc187576496e 100644 --- a/code/datums/spells/fake_gib.dm +++ b/code/datums/spells/fake_gib.dm @@ -8,4 +8,4 @@ clothes_req = 0 cooldown_min = 200 //100 deciseconds reduction per rank - action_icon_state = "gib" \ No newline at end of file + action_icon_state = "gib" diff --git a/code/datums/spells/genetic.dm b/code/datums/spells/genetic.dm index 697337d85e00..3c2f997e4611 100644 --- a/code/datums/spells/genetic.dm +++ b/code/datums/spells/genetic.dm @@ -1,37 +1,37 @@ -/obj/effect/proc_holder/spell/targeted/genetic - name = "Genetic" - desc = "This spell inflicts a set of mutations and disabilities upon the target." - - var/disabilities = 0 //bits - var/list/mutations = list() //mutation strings - var/duration = 100 //deciseconds - /* - Disabilities - 1st bit - ? - 2nd bit - ? - 3rd bit - ? - 4th bit - ? - 5th bit - ? - 6th bit - ? - */ - -/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - for(var/x in mutations) - target.mutations.Add(x) - /* if(x == HULK && ishuman(target)) - target:hulk_time=world.time + duration */ - target.disabilities |= disabilities - target.update_mutations() //update target's mutation overlays - var/mob/living/carbon/human/H = target - if(ishuman(target)) - H.update_body() - spawn(duration) - target.mutations.Remove(mutations) - target.disabilities &= ~disabilities - target.update_mutations() - if(ishuman(target)) - H.update_body() - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/genetic + name = "Genetic" + desc = "This spell inflicts a set of mutations and disabilities upon the target." + + var/disabilities = 0 //bits + var/list/mutations = list() //mutation strings + var/duration = 100 //deciseconds + /* + Disabilities + 1st bit - ? + 2nd bit - ? + 3rd bit - ? + 4th bit - ? + 5th bit - ? + 6th bit - ? + */ + +/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + for(var/x in mutations) + target.mutations.Add(x) + /* if(x == HULK && ishuman(target)) + target:hulk_time=world.time + duration */ + target.disabilities |= disabilities + target.update_mutations() //update target's mutation overlays + var/mob/living/carbon/human/H = target + if(ishuman(target)) + H.update_body() + spawn(duration) + target.mutations.Remove(mutations) + target.disabilities &= ~disabilities + target.update_mutations() + if(ishuman(target)) + H.update_body() + + return diff --git a/code/datums/spells/horsemask.dm b/code/datums/spells/horsemask.dm index f536041e807b..2c000a0d232f 100644 --- a/code/datums/spells/horsemask.dm +++ b/code/datums/spells/horsemask.dm @@ -1,48 +1,48 @@ -/obj/effect/proc_holder/spell/targeted/horsemask - name = "Curse of the Horseman" - desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." - school = "transmutation" - charge_type = "recharge" - charge_max = 150 - charge_counter = 0 - clothes_req = 0 - stat_allowed = 0 - invocation = "KN'A FTAGHU, PUCK 'BTHNK!" - invocation_type = "shout" - range = 7 - cooldown_min = 30 //30 deciseconds reduction per rank - selection_type = "range" - - action_icon_state = "barn" - sound = 'sound/magic/HorseHead_curse.ogg' - -/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) - if(!targets.len) - to_chat(user, "No target found in range.") - return - - var/mob/living/carbon/target = targets[1] - - if(!target) - return - - - if(!ishuman(target)) - to_chat(user, "It'd be stupid to curse [target] with a horse's head!") - return - - if(!(target in oview(range)))//If they are not in overview after selection. - to_chat(user, "They are too far away!") - return - - var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead - magichead.flags |= NODROP | DROPDEL //curses! - magichead.flags_inv = null //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ - "Your face burns up, and shortly after the fire you realise you have the face of a horse!") - if(!target.unEquip(target.wear_mask)) - qdel(target.wear_mask) - target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) - - target.flash_eyes() \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/horsemask + name = "Curse of the Horseman" + desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." + school = "transmutation" + charge_type = "recharge" + charge_max = 150 + charge_counter = 0 + clothes_req = 0 + stat_allowed = 0 + invocation = "KN'A FTAGHU, PUCK 'BTHNK!" + invocation_type = "shout" + range = 7 + cooldown_min = 30 //30 deciseconds reduction per rank + selection_type = "range" + + action_icon_state = "barn" + sound = 'sound/magic/HorseHead_curse.ogg' + +/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) + if(!targets.len) + to_chat(user, "No target found in range.") + return + + var/mob/living/carbon/target = targets[1] + + if(!target) + return + + + if(!ishuman(target)) + to_chat(user, "It'd be stupid to curse [target] with a horse's head!") + return + + if(!(target in oview(range)))//If they are not in overview after selection. + to_chat(user, "They are too far away!") + return + + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP | DROPDEL //curses! + magichead.flags_inv = null //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ + "Your face burns up, and shortly after the fire you realise you have the face of a horse!") + if(!target.unEquip(target.wear_mask)) + qdel(target.wear_mask) + target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + + target.flash_eyes() diff --git a/code/datums/spells/inflict_handler.dm b/code/datums/spells/inflict_handler.dm index 896e399187a3..7615a37cfff2 100644 --- a/code/datums/spells/inflict_handler.dm +++ b/code/datums/spells/inflict_handler.dm @@ -1,57 +1,57 @@ -/obj/effect/proc_holder/spell/targeted/inflict_handler - name = "Inflict Handler" - desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target." - - var/amt_weakened = 0 - var/amt_paralysis = 0 - var/amt_stunned = 0 - - //set to negatives for healing - var/amt_dam_fire = 0 - var/amt_dam_brute = 0 - var/amt_dam_oxy = 0 - var/amt_dam_tox = 0 - - var/amt_eye_blind = 0 - var/amt_eye_blurry = 0 - - var/destroys = "none" //can be "none", "gib" or "disintegrate" - - var/summon_type = null //this will put an obj at the target's location - -/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - switch(destroys) - if("gib") - target.gib() - if("disintegrate") - target.dust() - - if(!target) - continue - //damage - if(amt_dam_brute > 0) - if(amt_dam_fire >= 0) - target.take_overall_damage(amt_dam_brute,amt_dam_fire) - else if(amt_dam_fire < 0) - target.take_overall_damage(amt_dam_brute,0) - target.heal_overall_damage(0,amt_dam_fire) - else if(amt_dam_brute < 0) - if(amt_dam_fire > 0) - target.take_overall_damage(0,amt_dam_fire) - target.heal_overall_damage(amt_dam_brute,0) - else if(amt_dam_fire <= 0) - target.heal_overall_damage(amt_dam_brute,amt_dam_fire) - target.adjustToxLoss(amt_dam_tox) - target.adjustOxyLoss(amt_dam_oxy) - //disabling - target.Weaken(amt_weakened) - target.Paralyse(amt_paralysis) - target.Stun(amt_stunned) - - target.AdjustEyeBlind(amt_eye_blind) - target.AdjustEyeBlurry(amt_eye_blurry) - //summoning - if(summon_type) - new summon_type(target.loc, target) +/obj/effect/proc_holder/spell/targeted/inflict_handler + name = "Inflict Handler" + desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target." + + var/amt_weakened = 0 + var/amt_paralysis = 0 + var/amt_stunned = 0 + + //set to negatives for healing + var/amt_dam_fire = 0 + var/amt_dam_brute = 0 + var/amt_dam_oxy = 0 + var/amt_dam_tox = 0 + + var/amt_eye_blind = 0 + var/amt_eye_blurry = 0 + + var/destroys = "none" //can be "none", "gib" or "disintegrate" + + var/summon_type = null //this will put an obj at the target's location + +/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + switch(destroys) + if("gib") + target.gib() + if("disintegrate") + target.dust() + + if(!target) + continue + //damage + if(amt_dam_brute > 0) + if(amt_dam_fire >= 0) + target.take_overall_damage(amt_dam_brute,amt_dam_fire) + else if(amt_dam_fire < 0) + target.take_overall_damage(amt_dam_brute,0) + target.heal_overall_damage(0,amt_dam_fire) + else if(amt_dam_brute < 0) + if(amt_dam_fire > 0) + target.take_overall_damage(0,amt_dam_fire) + target.heal_overall_damage(amt_dam_brute,0) + else if(amt_dam_fire <= 0) + target.heal_overall_damage(amt_dam_brute,amt_dam_fire) + target.adjustToxLoss(amt_dam_tox) + target.adjustOxyLoss(amt_dam_oxy) + //disabling + target.Weaken(amt_weakened) + target.Paralyse(amt_paralysis) + target.Stun(amt_stunned) + + target.AdjustEyeBlind(amt_eye_blind) + target.AdjustEyeBlurry(amt_eye_blurry) + //summoning + if(summon_type) + new summon_type(target.loc, target) diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm index b8fafb58bac5..dc2efcc1e91a 100644 --- a/code/datums/spells/knock.dm +++ b/code/datums/spells/knock.dm @@ -1,57 +1,57 @@ -/obj/effect/proc_holder/spell/aoe_turf/knock - name = "Knock" - desc = "This spell opens nearby doors and does not require wizard garb." - - school = "transmutation" - charge_max = 100 - clothes_req = 0 - invocation = "AULIE OXIN FIERA" - invocation_type = "whisper" - range = 3 - cooldown_min = 20 //20 deciseconds reduction per rank - - action_icon_state = "knock" - sound = 'sound/magic/knock.ogg' - -/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) - for(var/turf/T in targets) - for(var/obj/machinery/door/door in T.contents) - spawn(1) - if(istype(door,/obj/machinery/door/airlock/hatch/gamma)) - return - if(istype(door,/obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = door - A.unlock(1) //forced because it's magic! - door.open() - for(var/obj/structure/closet/C in T.contents) - spawn(1) - if(istype(C, /obj/structure/closet/secure_closet)) - var/obj/structure/closet/secure_closet/SC = C - SC.locked = 0 - C.open() - - return - -/obj/effect/proc_holder/spell/aoe_turf/knock/greater - name = "Greater Knock" - desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!" - - charge_max = 200 - invocation = "MAIOR OXIN FIERA" - invocation_type = "shout" - range = 7 - level_max = 0 //Cannot be improved, quality of life since can't be refunded - cooldown_min = 200 - var/used = FALSE - -/obj/effect/proc_holder/spell/aoe_turf/knock/greater/cast(list/targets, mob/user = usr) - if(!used) - used = TRUE - for(var/obj/machinery/door/airlock/A in GLOB.airlocks) - if(is_station_level(A.z)) - A.req_access = list() - A.req_one_access = list() - command_announcement.Announce("We have removed all access requirements on your station's airlocks. You can thank us later!", "Greetings!", 'sound/misc/notice2.ogg', , , "Space Wizard Federation Message") - else - ..() - return +/obj/effect/proc_holder/spell/aoe_turf/knock + name = "Knock" + desc = "This spell opens nearby doors and does not require wizard garb." + + school = "transmutation" + charge_max = 100 + clothes_req = 0 + invocation = "AULIE OXIN FIERA" + invocation_type = "whisper" + range = 3 + cooldown_min = 20 //20 deciseconds reduction per rank + + action_icon_state = "knock" + sound = 'sound/magic/knock.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) + for(var/turf/T in targets) + for(var/obj/machinery/door/door in T.contents) + spawn(1) + if(istype(door,/obj/machinery/door/airlock/hatch/gamma)) + return + if(istype(door,/obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = door + A.unlock(1) //forced because it's magic! + door.open() + for(var/obj/structure/closet/C in T.contents) + spawn(1) + if(istype(C, /obj/structure/closet/secure_closet)) + var/obj/structure/closet/secure_closet/SC = C + SC.locked = 0 + C.open() + + return + +/obj/effect/proc_holder/spell/aoe_turf/knock/greater + name = "Greater Knock" + desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!" + + charge_max = 200 + invocation = "MAIOR OXIN FIERA" + invocation_type = "shout" + range = 7 + level_max = 0 //Cannot be improved, quality of life since can't be refunded + cooldown_min = 200 + var/used = FALSE + +/obj/effect/proc_holder/spell/aoe_turf/knock/greater/cast(list/targets, mob/user = usr) + if(!used) + used = TRUE + for(var/obj/machinery/door/airlock/A in GLOB.airlocks) + if(is_station_level(A.z)) + A.req_access = list() + A.req_one_access = list() + GLOB.command_announcement.Announce("We have removed all access requirements on your station's airlocks. You can thank us later!", "Greetings!", 'sound/misc/notice2.ogg', , , "Space Wizard Federation Message") + else + ..() + return diff --git a/code/datums/spells/lichdom.dm b/code/datums/spells/lichdom.dm index f37d48c0a10d..9db46b517db0 100644 --- a/code/datums/spells/lichdom.dm +++ b/code/datums/spells/lichdom.dm @@ -130,4 +130,4 @@ H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) \ No newline at end of file + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm index dcca0cfc9199..e7821aed6c29 100644 --- a/code/datums/spells/lightning.dm +++ b/code/datums/spells/lightning.dm @@ -117,4 +117,4 @@ obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr) return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces \ No newline at end of file + Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces diff --git a/code/datums/spells/magnet.dm b/code/datums/spells/magnet.dm index 8a6e9c525c4b..fea6780085c5 100644 --- a/code/datums/spells/magnet.dm +++ b/code/datums/spells/magnet.dm @@ -122,4 +122,4 @@ obj/effect/proc_holder/spell/targeted/magnet/proc/Reset(mob/user = usr) return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces \ No newline at end of file + Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index f11fb5b95724..d0c052649d9a 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -141,6 +141,7 @@ else user.mind.AddSpell(S) to_chat(user, "You flip through the pages. Your understanding of the boundaries of reality increases. You can cast [spellname]!") + user.create_log(MISC_LOG, "learned the spell [spellname] ([S])") user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") onlearned(user) diff --git a/code/datums/spells/mime_malaise.dm b/code/datums/spells/mime_malaise.dm index a5afd26d9167..1765b2f5741c 100644 --- a/code/datums/spells/mime_malaise.dm +++ b/code/datums/spells/mime_malaise.dm @@ -49,6 +49,6 @@ equip_to_slot_if_possible(new /obj/item/clothing/mask/gas/mime/nodrop, slot_wear_mask, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/under/mime/nodrop, slot_w_uniform, TRUE, TRUE) equip_to_slot_if_possible(new /obj/item/clothing/suit/suspenders/nodrop, slot_wear_suit, TRUE, TRUE) - dna.SetSEState(MUTEBLOCK , TRUE, TRUE) - genemutcheck(src, MUTEBLOCK , null, MUTCHK_FORCED) - dna.default_blocks.Add(MUTEBLOCK) \ No newline at end of file + dna.SetSEState(GLOB.muteblock , TRUE, TRUE) + genemutcheck(src, GLOB.muteblock , null, MUTCHK_FORCED) + dna.default_blocks.Add(GLOB.muteblock) diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm index e45d27ec28e9..3d9031a8e03f 100644 --- a/code/datums/spells/mind_transfer.dm +++ b/code/datums/spells/mind_transfer.dm @@ -1,82 +1,82 @@ -/obj/effect/proc_holder/spell/targeted/mind_transfer - name = "Mind Transfer" - desc = "This spell allows the user to switch bodies with a target." - - school = "transmutation" - charge_max = 600 - clothes_req = 0 - invocation = "GIN'YU CAPAN" - invocation_type = "whisper" - range = 1 - cooldown_min = 200 //100 deciseconds reduction per rank - var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell - var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell - var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell - action_icon_state = "mindswap" - -/* -Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. -Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. -Also, you never added distance checking after target is selected. I've went ahead and did that. -*/ -/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) - - var/mob/living/target = targets[range] - - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. - to_chat(user, "They are too far away!") - return - - if(target.stat == DEAD) - to_chat(user, "You don't particularly want to be dead.") - return - - if(!target.key || !target.mind) - to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") - return - - if(user.suiciding) - to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") - return - - if(target.mind.special_role in protected_roles) - to_chat(user, "Their mind is resisting your spell.") - return - - if(istype(target, /mob/living/silicon)) - to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") - return - - var/mob/living/victim = target//The target of the spell whos body will be transferred to. - var/mob/caster = user//The wizard/whomever doing the body transferring. - - //MIND TRANSFER BEGIN - if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list. - for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot. - caster.verbs -= V//But a safety nontheless. - - if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs. - for(var/V in victim.mind.special_verbs) - victim.verbs -= V - - var/mob/dead/observer/ghost = victim.ghostize(0) - caster.mind.transfer_to(victim) - - if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. - for(var/V in caster.mind.special_verbs)//Not too important but could come into play. - caster.verbs += V - - ghost.mind.transfer_to(caster) - if(ghost.key) - GLOB.non_respawnable_keys -= ghost.ckey //ghostizing with an argument of 0 will make them unable to respawn forever, which is bad - caster.key = ghost.key //have to transfer the key since the mind was not active - qdel(ghost) - - if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. - for(var/V in caster.mind.special_verbs) - caster.verbs += V - //MIND TRANSFER END - - //Here we paralyze both mobs and knock them out for a time. - caster.Paralyse(paralysis_amount_caster) - victim.Paralyse(paralysis_amount_victim) +/obj/effect/proc_holder/spell/targeted/mind_transfer + name = "Mind Transfer" + desc = "This spell allows the user to switch bodies with a target." + + school = "transmutation" + charge_max = 600 + clothes_req = 0 + invocation = "GIN'YU CAPAN" + invocation_type = "whisper" + range = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell + var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell + var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell + action_icon_state = "mindswap" + +/* +Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. +Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. +Also, you never added distance checking after target is selected. I've went ahead and did that. +*/ +/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) + + var/mob/living/target = targets[range] + + if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. + to_chat(user, "They are too far away!") + return + + if(target.stat == DEAD) + to_chat(user, "You don't particularly want to be dead.") + return + + if(!target.key || !target.mind) + to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") + return + + if(user.suiciding) + to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") + return + + if(target.mind.special_role in protected_roles) + to_chat(user, "Their mind is resisting your spell.") + return + + if(istype(target, /mob/living/silicon)) + to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") + return + + var/mob/living/victim = target//The target of the spell whos body will be transferred to. + var/mob/caster = user//The wizard/whomever doing the body transferring. + + //MIND TRANSFER BEGIN + if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list. + for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot. + caster.verbs -= V//But a safety nontheless. + + if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs. + for(var/V in victim.mind.special_verbs) + victim.verbs -= V + + var/mob/dead/observer/ghost = victim.ghostize(0) + caster.mind.transfer_to(victim) + + if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. + for(var/V in caster.mind.special_verbs)//Not too important but could come into play. + caster.verbs += V + + ghost.mind.transfer_to(caster) + if(ghost.key) + GLOB.non_respawnable_keys -= ghost.ckey //ghostizing with an argument of 0 will make them unable to respawn forever, which is bad + caster.key = ghost.key //have to transfer the key since the mind was not active + qdel(ghost) + + if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. + for(var/V in caster.mind.special_verbs) + caster.verbs += V + //MIND TRANSFER END + + //Here we paralyze both mobs and knock them out for a time. + caster.Paralyse(paralysis_amount_caster) + victim.Paralyse(paralysis_amount_victim) diff --git a/code/datums/spells/projectile.dm b/code/datums/spells/projectile.dm index 258925653d84..955aa6e3537f 100644 --- a/code/datums/spells/projectile.dm +++ b/code/datums/spells/projectile.dm @@ -1,86 +1,86 @@ -/obj/effect/proc_holder/spell/targeted/projectile - name = "Projectile" - desc = "This spell summons projectiles which try to hit the targets." - - var/proj_icon = 'icons/obj/projectiles.dmi' - var/proj_icon_state = "spell" - var/proj_name = "a spell projectile" - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - - var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this - - var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle - var/proj_homing = 1 //if it follows the target - var/proj_insubstantial = 0 //if it can pass through dense objects or not - var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target) - - var/proj_lifespan = 15 //in deciseconds * proj_step_delay - var/proj_step_delay = 1 //lower = faster - -/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - spawn(0) - var/obj/effect/proc_holder/spell/targeted/projectile - if(istext(proj_type)) - var/projectile_type = text2path(proj_type) - projectile = new projectile_type(user) - if(istype(proj_type,/obj/effect/proc_holder/spell)) - projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) - projectile:linked_spells += proj_type - projectile.icon = proj_icon - projectile.icon_state = proj_icon_state - projectile.dir = get_dir(target,projectile) - projectile.name = proj_name - - var/current_loc = user.loc - - projectile.loc = current_loc - - for(var/i = 0,i < proj_lifespan,i++) - if(!projectile) - break - - if(proj_homing) - if(proj_insubstantial) - projectile.dir = get_dir(projectile,target) - projectile.loc = get_step_to(projectile,target) - else - step_to(projectile,target) - else - if(proj_insubstantial) - projectile.loc = get_step(projectile,dir) - else - step(projectile,dir) - - if(!projectile) // step and step_to sleeps so we'll have to check again. - break - - if(!proj_lingering && projectile.loc == current_loc) //if it didn't move since last time - qdel(projectile) - break - - if(proj_trail && projectile) - spawn(0) - if(projectile) - var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.density = 0 - spawn(proj_trail_lifespan) - qdel(trail) - - if(projectile.loc in range(target.loc,proj_trigger_range)) - projectile.perform(list(target), user = user) - break - - current_loc = projectile.loc - - sleep(proj_step_delay) - - if(projectile) - qdel(projectile) \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/projectile + name = "Projectile" + desc = "This spell summons projectiles which try to hit the targets." + + var/proj_icon = 'icons/obj/projectiles.dmi' + var/proj_icon_state = "spell" + var/proj_name = "a spell projectile" + + var/proj_trail = 0 //if it leaves a trail + var/proj_trail_lifespan = 0 //deciseconds + var/proj_trail_icon = 'icons/obj/wizard.dmi' + var/proj_trail_icon_state = "trail" + + var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this + + var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle + var/proj_homing = 1 //if it follows the target + var/proj_insubstantial = 0 //if it can pass through dense objects or not + var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target) + + var/proj_lifespan = 15 //in deciseconds * proj_step_delay + var/proj_step_delay = 1 //lower = faster + +/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + spawn(0) + var/obj/effect/proc_holder/spell/targeted/projectile + if(istext(proj_type)) + var/projectile_type = text2path(proj_type) + projectile = new projectile_type(user) + if(istype(proj_type,/obj/effect/proc_holder/spell)) + projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) + projectile:linked_spells += proj_type + projectile.icon = proj_icon + projectile.icon_state = proj_icon_state + projectile.dir = get_dir(target,projectile) + projectile.name = proj_name + + var/current_loc = user.loc + + projectile.loc = current_loc + + for(var/i = 0,i < proj_lifespan,i++) + if(!projectile) + break + + if(proj_homing) + if(proj_insubstantial) + projectile.dir = get_dir(projectile,target) + projectile.loc = get_step_to(projectile,target) + else + step_to(projectile,target) + else + if(proj_insubstantial) + projectile.loc = get_step(projectile,dir) + else + step(projectile,dir) + + if(!projectile) // step and step_to sleeps so we'll have to check again. + break + + if(!proj_lingering && projectile.loc == current_loc) //if it didn't move since last time + qdel(projectile) + break + + if(proj_trail && projectile) + spawn(0) + if(projectile) + var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.density = 0 + spawn(proj_trail_lifespan) + qdel(trail) + + if(projectile.loc in range(target.loc,proj_trigger_range)) + projectile.perform(list(target), user = user) + break + + current_loc = projectile.loc + + sleep(proj_step_delay) + + if(projectile) + qdel(projectile) diff --git a/code/datums/spells/rathens.dm b/code/datums/spells/rathens.dm index b2d35ff5393e..4d98be324b2d 100644 --- a/code/datums/spells/rathens.dm +++ b/code/datums/spells/rathens.dm @@ -21,14 +21,14 @@ A.remove(H) A.forceMove(get_turf(H)) spawn() - A.throw_at(get_edge_target_turf(H, pick(alldirs)), rand(1, 10), 5) + A.throw_at(get_edge_target_turf(H, pick(GLOB.alldirs)), rand(1, 10), 5) H.visible_message("[H]'s [A.name] flies out of their body in a magical explosion!",\ "Your [A.name] flies out of your body in a magical explosion!") H.Weaken(2) else var/obj/effect/decal/cleanable/blood/gibs/G = new/obj/effect/decal/cleanable/blood/gibs(get_turf(H)) spawn() - G.throw_at(get_edge_target_turf(H, pick(alldirs)), rand(1, 10), 5) + G.throw_at(get_edge_target_turf(H, pick(GLOB.alldirs)), rand(1, 10), 5) H.apply_damage(10, BRUTE, "chest") to_chat(H, "You have no appendix, but something had to give! Holy shit, what was that?") H.Weaken(3) diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm index ff303ce750b3..7b72b5a42c42 100644 --- a/code/datums/spells/shapeshift.dm +++ b/code/datums/spells/shapeshift.dm @@ -111,4 +111,4 @@ shapeshift_type = /mob/living/simple_animal/hostile/hellhound/greater current_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) current_casters = list() - possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) \ No newline at end of file + possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) diff --git a/code/datums/spells/trigger.dm b/code/datums/spells/trigger.dm index bba1f22cb1a5..bc768cef57bf 100644 --- a/code/datums/spells/trigger.dm +++ b/code/datums/spells/trigger.dm @@ -1,29 +1,29 @@ -/obj/effect/proc_holder/spell/targeted/trigger - name = "Trigger" - desc = "This spell triggers another spell or a few." - - var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly - var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks - -/obj/effect/proc_holder/spell/targeted/trigger/New() - ..() - - for(var/spell in starting_spells) - var/spell_to_add = text2path(spell) - new spell_to_add(src) //should result in adding to contents, needs testing - -/obj/effect/proc_holder/spell/targeted/trigger/Destroy() - for(var/spell in contents) - qdel(spell) - linked_spells = null - starting_spells = null - return ..() - -/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets, mob/user = usr) - for(var/mob/living/target in targets) - for(var/obj/effect/proc_holder/spell/spell in contents) - spell.perform(list(target), 0, user = user) - for(var/obj/effect/proc_holder/spell/spell in linked_spells) - spell.perform(list(target), 0, user = user) - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/trigger + name = "Trigger" + desc = "This spell triggers another spell or a few." + + var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly + var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks + +/obj/effect/proc_holder/spell/targeted/trigger/New() + ..() + + for(var/spell in starting_spells) + var/spell_to_add = text2path(spell) + new spell_to_add(src) //should result in adding to contents, needs testing + +/obj/effect/proc_holder/spell/targeted/trigger/Destroy() + for(var/spell in contents) + qdel(spell) + linked_spells = null + starting_spells = null + return ..() + +/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets, mob/user = usr) + for(var/mob/living/target in targets) + for(var/obj/effect/proc_holder/spell/spell in contents) + spell.perform(list(target), 0, user = user) + for(var/obj/effect/proc_holder/spell/spell in linked_spells) + spell.perform(list(target), 0, user = user) + + return diff --git a/code/datums/spells/turf_teleport.dm b/code/datums/spells/turf_teleport.dm index 6ecc9d693d8f..adaa4afc94c1 100644 --- a/code/datums/spells/turf_teleport.dm +++ b/code/datums/spells/turf_teleport.dm @@ -1,40 +1,40 @@ -/obj/effect/proc_holder/spell/targeted/turf_teleport - name = "Turf Teleport" - desc = "This spell teleports the target to the turf in range." - nonabstract_req = 1 - - var/inner_tele_radius = 1 - var/outer_tele_radius = 2 - - var/include_space = 0 //whether it includes space tiles in possible teleport locations - var/include_dense = 0 //whether it includes dense tiles in possible teleport locations - - var/sound1 = 'sound/weapons/zapbang.ogg' - var/sound2 = 'sound/weapons/zapbang.ogg' - -/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets,mob/living/user = usr) - playsound(get_turf(user), sound1, 50,1) - for(var/mob/living/target in targets) - var/list/turfs = new/list() - for(var/turf/T in range(target,outer_tele_radius)) - if(T in range(target,inner_tele_radius)) continue - if(istype(T,/turf/space) && !include_space) continue - if(T.density && !include_dense) continue - if(T.x>world.maxx-outer_tele_radius || T.xworld.maxy-outer_tele_radius || T.yworld.maxx-outer_tele_radius || T.xworld.maxy-outer_tele_radius || T.yYou're slammed into the floor by a mystical force!") - else - new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own - if(isliving(AM)) - var/mob/living/M = AM - M.Weaken(stun_amt) - to_chat(M, "You're thrown back by a mystical force!") - spawn(0) - AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. - -/obj/effect/proc_holder/spell/targeted/sacred_flame - name = "Sacred Flame" - desc = "Makes everyone around you more flammable, and lights yourself on fire." - charge_max = 60 - clothes_req = 0 - invocation = "FI'RAN DADISKO" - invocation_type = "shout" - max_targets = 0 - range = 6 - include_user = 1 - selection_type = "view" - action_icon_state = "sacredflame" - sound = 'sound/magic/fireball.ogg' - -/obj/effect/proc_holder/spell/targeted/sacred_flame/cast(list/targets, mob/user = usr) - for(var/mob/living/L in targets) - L.adjust_fire_stacks(20) - if(isliving(user)) - var/mob/living/U = user - U.IgniteMob() +/obj/effect/proc_holder/spell/targeted/projectile/magic_missile + name = "Magic Missile" + desc = "This spell fires several, slow moving, magic projectiles at nearby targets." + + school = "evocation" + charge_max = 200 + clothes_req = 1 + invocation = "FORTI GY AMA" + invocation_type = "shout" + range = 7 + cooldown_min = 60 //35 deciseconds reduction per rank + + max_targets = 0 + + proj_icon_state = "magicm" + proj_name = "a magic missile" + proj_lingering = 1 + proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile" + + proj_lifespan = 20 + proj_step_delay = 5 + + proj_trail = 1 + proj_trail_lifespan = 5 + proj_trail_icon_state = "magicmd" + + action_icon_state = "magicm" + + sound = 'sound/magic/magic_missile.ogg' + +/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile + amt_weakened = 3 + sound = 'sound/magic/mm_hit.ogg' + + +/obj/effect/proc_holder/spell/targeted/projectile/honk_missile + name = "Honk Missile" + desc = "This spell fires several, slow moving, magic bikehorns at nearby targets." + + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "HONK GY AMA" + invocation_type = "shout" + range = 7 + cooldown_min = 60 //35 deciseconds reduction per rank + + max_targets = 0 + + proj_icon = 'icons/obj/items.dmi' + proj_icon_state = "bike_horn" + proj_name = "A bike horn" + proj_lingering = 1 + proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/honk_missile" + + proj_lifespan = 20 + proj_step_delay = 5 + + proj_trail_icon = 'icons/obj/items.dmi' + proj_trail = 1 + proj_trail_lifespan = 5 + proj_trail_icon_state = "bike_horn" + + action_icon_state = "magicm" + + sound = 'sound/items/bikehorn.ogg' + +/obj/effect/proc_holder/spell/targeted/inflict_handler/honk_missile + amt_weakened = 3 + sound = 'sound/items/bikehorn.ogg' + +/obj/effect/proc_holder/spell/noclothes + name = "No Clothes" + desc = "This always-on spell allows you to cast magic without your garments." + action_icon_state = "no_clothes" + +/obj/effect/proc_holder/spell/targeted/genetic/mutate + name = "Mutate" + desc = "This spell causes you to turn into a hulk and gain laser vision for a short while." + + school = "transmutation" + charge_max = 400 + clothes_req = 1 + invocation = "BIRUZ BENNAR" + invocation_type = "shout" + message = "You feel strong! You feel a pressure building behind your eyes!" + range = -1 + include_user = 1 + centcom_cancast = 0 + + mutations = list(LASER, HULK) + duration = 300 + cooldown_min = 300 //25 deciseconds reduction per rank + + action_icon_state = "mutate" + sound = 'sound/magic/mutate.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/mutate/cast(list/targets, mob/user = usr) + for(var/mob/living/target in targets) + target.dna.SetSEState(GLOB.hulkblock, 1) + genemutcheck(target, GLOB.hulkblock, null, MUTCHK_FORCED) + spawn(duration) + target.dna.SetSEState(GLOB.hulkblock, 0) + genemutcheck(target, GLOB.hulkblock, null, MUTCHK_FORCED) + ..() + +/obj/effect/proc_holder/spell/targeted/smoke + name = "Smoke" + desc = "This spell spawns a cloud of choking smoke at your location and does not require wizard garb." + + school = "conjuration" + charge_max = 120 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + cooldown_min = 20 //25 deciseconds reduction per rank + + smoke_spread = 2 + smoke_amt = 10 + + action_icon_state = "smoke" + +/obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + name = "Disable Tech" + desc = "This spell disables all weapons, cameras and most other technology in range." + charge_max = 400 + clothes_req = 1 + invocation = "NEC CANTIO" + invocation_type = "shout" + range = -1 + include_user = 1 + cooldown_min = 200 //50 deciseconds reduction per rank + + emp_heavy = 6 + emp_light = 10 + + sound = 'sound/magic/disable_tech.ogg' + +/obj/effect/proc_holder/spell/targeted/turf_teleport/blink + name = "Blink" + desc = "This spell randomly teleports you a short distance." + + school = "abjuration" + charge_max = 20 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + cooldown_min = 5 //4 deciseconds reduction per rank + + + smoke_spread = 1 + smoke_amt = 1 + + inner_tele_radius = 0 + outer_tele_radius = 6 + + centcom_cancast = 0 //prevent people from getting to centcom + + action_icon_state = "blink" + + sound1 = 'sound/magic/blink.ogg' + sound2 = 'sound/magic/blink.ogg' + +/obj/effect/proc_holder/spell/targeted/area_teleport/teleport + name = "Teleport" + desc = "This spell teleports you to a type of area of your selection." + + school = "abjuration" + charge_max = 600 + clothes_req = 1 + invocation = "SCYAR NILA" + invocation_type = "shout" + range = -1 + include_user = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + + smoke_spread = 1 + smoke_amt = 5 + + action_icon_state = "spell_teleport" + + sound1 = 'sound/magic/teleport_diss.ogg' + sound2 = 'sound/magic/teleport_app.ogg' + +/obj/effect/proc_holder/spell/targeted/forcewall + name = "Force Wall" + desc = "This spell creates a small unbreakable wall that only you can pass through, and does not need wizard garb. Lasts 30 seconds." + + school = "transmutation" + charge_max = 100 + clothes_req = FALSE + invocation = "TARCOL MINTI ZHERI" + invocation_type = "whisper" + sound = 'sound/magic/forcewall.ogg' + action_icon_state = "shield" + range = -1 + include_user = TRUE + cooldown_min = 50 //12 deciseconds reduction per rank + var/wall_type = /obj/effect/forcefield/wizard + var/large = FALSE + +/obj/effect/proc_holder/spell/targeted/forcewall/cast(list/targets, mob/user = usr) + new wall_type(get_turf(user), user) + if(large) //Extra THICK + if(user.dir == SOUTH || user.dir == NORTH) + new wall_type(get_step(user, EAST), user) + new wall_type(get_step(user, WEST), user) + else + new wall_type(get_step(user, NORTH), user) + new wall_type(get_step(user, SOUTH), user) + +/obj/effect/proc_holder/spell/targeted/forcewall/greater + name = "Greater Force Wall" + desc = "Create a larger magical barrier that only you can pass through, but requires wizard garb. Lasts 30 seconds." + + clothes_req = TRUE + invocation = "TARCOL GRANDI ZHERI" + invocation_type = "shout" + large = TRUE + +/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + name = "Stop Time" + desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen." + charge_max = 500 + clothes_req = 1 + invocation = "TOKI WO TOMARE" + invocation_type = "shout" + range = 0 + cooldown_min = 100 + summon_amt = 1 + action_icon_state = "time" + + summon_type = list(/obj/effect/timestop/wizard) + +/obj/effect/proc_holder/spell/aoe_turf/conjure/carp + name = "Summon Carp" + desc = "This spell conjures a simple carp." + + school = "conjuration" + charge_max = 1200 + clothes_req = 1 + invocation = "NOUK FHUNMM SACP RISSKA" + invocation_type = "shout" + range = 1 + + summon_type = list(/mob/living/simple_animal/hostile/carp) + + cast_sound = 'sound/magic/summon_karp.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/construct + name = "Artificer" + desc = "This spell conjures a construct which may be controlled by Shades" + + school = "conjuration" + charge_max = 600 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = 0 + + summon_type = list(/obj/structure/constructshell) + + action_icon_state = "artificer" + cast_sound = 'sound/magic/summonitems_generic.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/creature + name = "Summon Creature Swarm" + desc = "This spell tears the fabric of reality, allowing horrific daemons to spill forth" + + school = "conjuration" + charge_max = 1200 + clothes_req = 0 + invocation = "IA IA" + invocation_type = "shout" + summon_amt = 10 + range = 3 + + summon_type = list(/mob/living/simple_animal/hostile/creature) + cast_sound = 'sound/magic/summonitems_generic.ogg' + +/obj/effect/proc_holder/spell/targeted/trigger/blind + name = "Blind" + desc = "This spell temporarily blinds a single person and does not require wizard garb." + + school = "transmutation" + charge_max = 300 + clothes_req = 0 + invocation = "STI KALY" + invocation_type = "whisper" + message = "Your eyes cry out in pain!" + cooldown_min = 50 //12 deciseconds reduction per rank + + starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind","/obj/effect/proc_holder/spell/targeted/genetic/blind") + + action_icon_state = "blind" + +/obj/effect/proc_holder/spell/targeted/inflict_handler/blind + amt_eye_blind = 10 + amt_eye_blurry = 20 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/blind + disabilities = BLIND + duration = 300 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/fireball + name = "Fireball" + desc = "This spell fires a fireball at a target and does not require wizard garb." + + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "ONI SOMA" + invocation_type = "shout" + range = 20 + cooldown_min = 20 //10 deciseconds reduction per rank + var/fireball_type = /obj/item/projectile/magic/fireball + action_icon_state = "fireball0" + sound = 'sound/magic/fireball.ogg' + + active = FALSE + +/obj/effect/proc_holder/spell/fireball/Click() + var/mob/living/user = usr + if(!istype(user)) + return + + var/msg + + if(!can_cast(user)) + msg = "You can no longer cast Fireball." + remove_ranged_ability(user, msg) + return + + if(active) + msg = "You extinguish your fireball...for now." + remove_ranged_ability(user, msg) + else + msg = "Your prepare to cast your fireball spell! Left-click to cast at a target!" + add_ranged_ability(user, msg) + +/obj/effect/proc_holder/spell/fireball/update_icon() + if(!action) + return + action.button_icon_state = "fireball[active]" + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/user, params, atom/target) + if(..()) + return FALSE + + if(!cast_check(0, user)) + remove_ranged_ability(user) + return FALSE + + var/list/targets = list(target) + perform(targets, user = user) + + return TRUE + +/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user = usr) + var/target = targets[1] //There is only ever one target for fireball + var/turf/T = user.loc + var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction + if(!isturf(U) || !isturf(T)) + return 0 + + var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc) + FB.current = get_turf(user) + FB.preparePixelProjectile(target, get_turf(target), user) + FB.fire() + user.newtonian_move(get_dir(U, T)) + remove_ranged_ability(user) + + return 1 + +/obj/effect/proc_holder/spell/aoe_turf/repulse + name = "Repulse" + desc = "This spell throws everything around the user away." + charge_max = 400 + clothes_req = TRUE + invocation = "GITTAH WEIGH" + invocation_type = "shout" + range = 5 + cooldown_min = 150 + selection_type = "view" + sound = 'sound/magic/repulse.ogg' + var/maxthrow = 5 + var/sparkle_path = /obj/effect/temp_visual/gravpush + action_icon_state = "repulse" + +/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr, stun_amt = 2) + var/list/thrownatoms = list() + var/atom/throwtarget + var/distfromcaster + playMagSound() + for(var/turf/T in targets) //Done this way so things don't get thrown all around hilariously. + for(var/atom/movable/AM in T) + thrownatoms += AM + + for(var/am in thrownatoms) + var/atom/movable/AM = am + if(AM == user || AM.anchored) + continue + + throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user))) + distfromcaster = get_dist(user, AM) + if(distfromcaster == 0) + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(5) + M.adjustBruteLoss(5) + to_chat(M, "You're slammed into the floor by a mystical force!") + else + new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(stun_amt) + to_chat(M, "You're thrown back by a mystical force!") + spawn(0) + AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. + +/obj/effect/proc_holder/spell/targeted/sacred_flame + name = "Sacred Flame" + desc = "Makes everyone around you more flammable, and lights yourself on fire." + charge_max = 60 + clothes_req = 0 + invocation = "FI'RAN DADISKO" + invocation_type = "shout" + max_targets = 0 + range = 6 + include_user = 1 + selection_type = "view" + action_icon_state = "sacredflame" + sound = 'sound/magic/fireball.ogg' + +/obj/effect/proc_holder/spell/targeted/sacred_flame/cast(list/targets, mob/user = usr) + for(var/mob/living/L in targets) + L.adjust_fire_stacks(20) + if(isliving(user)) + var/mob/living/U = user + U.IgniteMob() diff --git a/code/datums/statclick.dm b/code/datums/statclick.dm index 11a3ed41ce31..2c83e57fb455 100644 --- a/code/datums/statclick.dm +++ b/code/datums/statclick.dm @@ -30,4 +30,4 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick) class = "unknown" usr.client.debug_variables(target) - message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") \ No newline at end of file + message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index c4ba55552fa5..5ded7fa43a4c 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -113,13 +113,13 @@ /datum/status_effect/hippocraticOath/on_apply() //Makes the user passive, it's in their oath not to harm! ADD_TRAIT(owner, TRAIT_PACIFISM, "hippocraticOath") - var/datum/atom_hud/H = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/H = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] H.add_hud_to(owner) return ..() /datum/status_effect/hippocraticOath/on_remove() REMOVE_TRAIT(owner, TRAIT_PACIFISM, "hippocraticOath") - var/datum/atom_hud/H = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/H = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] H.remove_hud_from(owner) /datum/status_effect/hippocraticOath/tick() @@ -229,4 +229,4 @@ return TRUE /datum/status_effect/regenerative_core/on_remove() - owner.status_flags &= ~IGNORESLOWDOWN \ No newline at end of file + owner.status_flags &= ~IGNORESLOWDOWN diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 3ce68f852506..d8b5e4d9cbe9 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -107,9 +107,9 @@ if(needs_to_bleed) var/turf/T = get_turf(owner) new /obj/effect/temp_visual/bleed/explode(T) - for(var/d in alldirs) + for(var/d in GLOB.alldirs) new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d) playsound(T, "desceration", 200, 1, -1) owner.adjustBruteLoss(bleed_damage) else - new /obj/effect/temp_visual/bleed(get_turf(owner)) \ No newline at end of file + new /obj/effect/temp_visual/bleed(get_turf(owner)) diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index bdd80e7fc32c..004179931441 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -43,4 +43,4 @@ /datum/status_effect/freon/watcher duration = 8 - can_melt = FALSE \ No newline at end of file + can_melt = FALSE diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index b2365ad4e1ce..7a86a00ccd7e 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -44,4 +44,4 @@ alert_type = null /datum/status_effect/high_five/on_remove() - owner.visible_message("[owner] was left hanging....") \ No newline at end of file + owner.visible_message("[owner] was left hanging....") diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index 8ed41c559b5c..896f4f999a94 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -234,4 +234,4 @@ owner.cut_overlay(status_overlay) owner.underlays -= status_underlay QDEL_NULL(status_overlay) - return ..() \ No newline at end of file + return ..() diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index f17e9840e4f7..91e68da427b9 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -16,7 +16,7 @@ #define SUPPLY_MISC 8 #define SUPPLY_VEND 9 -var/list/all_supply_groups = list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY_ENGINEER,SUPPLY_MEDICAL,SUPPLY_SCIENCE,SUPPLY_ORGANIC,SUPPLY_MATERIALS,SUPPLY_MISC,SUPPLY_VEND) +GLOBAL_LIST_INIT(all_supply_groups, list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY_ENGINEER,SUPPLY_MEDICAL,SUPPLY_SCIENCE,SUPPLY_ORGANIC,SUPPLY_MATERIALS,SUPPLY_MISC,SUPPLY_VEND)) /proc/get_supply_group_name(var/cat) switch(cat) diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index dbc75cb9bf01..558669df3c6d 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -36,7 +36,7 @@ var/list/outside_areas = list() var/list/eligible_areas = list() for(var/z in impacted_z_levels) - eligible_areas += space_manager.areas_in_z["[z]"] + eligible_areas += GLOB.space_manager.areas_in_z["[z]"] for(var/i in 1 to eligible_areas.len) var/area/place = eligible_areas[i] if(place.outdoors) @@ -105,4 +105,4 @@ aesthetic = TRUE - probability = 10 \ No newline at end of file + probability = 10 diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 08b30ecef482..2bfefa5624ed 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -26,8 +26,8 @@ /datum/weather/rad_storm/telegraph() ..() status_alarm(TRUE) - pre_maint_all_access = maint_all_access - if(!maint_all_access) + pre_maint_all_access = GLOB.maint_all_access + if(!GLOB.maint_all_access) make_maint_all_access() @@ -51,7 +51,7 @@ /datum/weather/rad_storm/end() if(..()) return - priority_announcement.Announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert") + GLOB.priority_announcement.Announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert") status_alarm(FALSE) if(!pre_maint_all_access) revoke_maint_all_access() @@ -61,4 +61,4 @@ post_status("alert", "radiation") else post_status("blank") - post_status("shuttle") \ No newline at end of file + post_status("shuttle") diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index 8c1108a3b1b7..b1076c8e29b4 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -91,4 +91,4 @@ else if(A.aidisabled == 1) A.aidisabled = 0 - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm index f0cb85b6d52a..c1413abd0cc3 100644 --- a/code/datums/wires/autolathe.dm +++ b/code/datums/wires/autolathe.dm @@ -71,4 +71,4 @@ /datum/wires/autolathe/proc/updateUIs() SSnanoui.update_uis(src) if(holder) - SSnanoui.update_uis(holder) \ No newline at end of file + SSnanoui.update_uis(holder) diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index f5f0c5df6338..a6b1549fbe10 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -58,4 +58,4 @@ if(IsIndexCut(CAMERA_WIRE_POWER) && IsIndexCut(CAMERA_WIRE_FOCUS)) return TRUE else - return FALSE \ No newline at end of file + return FALSE diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm index 0b970d38e5d6..988b520854f6 100644 --- a/code/datums/wires/mulebot.dm +++ b/code/datums/wires/mulebot.dm @@ -87,4 +87,4 @@ return !(wires_status & MULEBOT_WIRE_REMOTE_RX) /datum/wires/mulebot/proc/BeaconRX() - return !(wires_status & MULEBOT_WIRE_BEACON_RX) \ No newline at end of file + return !(wires_status & MULEBOT_WIRE_BEACON_RX) diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index 83fa911506d8..3b8ff5db098b 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -72,11 +72,11 @@ if(N.icon_state == "nuclearbomb2") N.icon_state = "nuclearbomb1" N.timing = 0 - bomb_set = 0 + GLOB.bomb_set = 0 if(NUCLEARBOMB_WIRE_LIGHT) N.lighthack = !N.lighthack /datum/wires/nuclearbomb/proc/updateUIs() SSnanoui.update_uis(src) if(holder) - SSnanoui.update_uis(holder) \ No newline at end of file + SSnanoui.update_uis(holder) diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm index 64705e6c2153..d6e68a79cfb4 100644 --- a/code/datums/wires/particle_accelerator.dm +++ b/code/datums/wires/particle_accelerator.dm @@ -64,4 +64,4 @@ C.strength_upper_limit = (mended ? 2 : 3) if(C.strength_upper_limit < C.strength) C.remove_strength() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index f55cee5951fb..789a0472869f 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -103,4 +103,4 @@ return wires_status & BORG_WIRE_LAWCHECK /datum/wires/robot/proc/AIHasControl() - return wires_status & BORG_WIRE_AI_CONTROL \ No newline at end of file + return wires_status & BORG_WIRE_AI_CONTROL diff --git a/code/datums/wires/suitstorage.dm b/code/datums/wires/suitstorage.dm index ddd0fda2bb61..175b36e60811 100644 --- a/code/datums/wires/suitstorage.dm +++ b/code/datums/wires/suitstorage.dm @@ -66,4 +66,4 @@ datum/wires/suitstorage/UpdatePulsed(index) A.shocked = FALSE if(SSU_WIRE_UV) A.uv_super = !A.uv_super - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm index 27ea28a90ad7..b490d150efe5 100644 --- a/code/datums/wires/syndicatebomb.dm +++ b/code/datums/wires/syndicatebomb.dm @@ -99,4 +99,4 @@ B.active = FALSE B.defused = TRUE B.update_icon() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm index b0b0a57ede45..bc513c6c6c00 100644 --- a/code/datums/wires/tesla_coil.dm +++ b/code/datums/wires/tesla_coil.dm @@ -20,4 +20,4 @@ switch(index) if(TESLACOIL_WIRE_ZAP) T.zap() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index 704c6bad750f..c9e6032afeed 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -66,4 +66,4 @@ V.seconds_electrified = -1 if(VENDING_WIRE_IDSCAN) V.scan_id = 1 - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 6a877ef90ead..1a407f2ec505 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -5,9 +5,9 @@ #define MAX_FLAG 65535 -var/list/same_wires = list() +GLOBAL_LIST_EMPTY(same_wires) // 12 colours, if you're adding more than 12 wires then add more colours here -var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink") +GLOBAL_LIST_INIT(wireColours, list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink")) /datum/wires @@ -39,11 +39,11 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", // Get the same wires else // We don't have any wires to copy yet, generate some and then copy it. - if(!same_wires[holder_type]) + if(!GLOB.same_wires[holder_type]) GenerateWires() - same_wires[holder_type] = src.wires.Copy() + GLOB.same_wires[holder_type] = src.wires.Copy() else - var/list/wires = same_wires[holder_type] + var/list/wires = GLOB.same_wires[holder_type] src.wires = wires // Reference the wires list. /datum/wires/Destroy() @@ -51,7 +51,7 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", return ..() /datum/wires/proc/GenerateWires() - var/list/colours_to_pick = wireColours.Copy() // Get a copy, not a reference. + var/list/colours_to_pick = GLOB.wireColours.Copy() // Get a copy, not a reference. var/list/indexes_to_pick = list() //Generate our indexes for(var/i = 1; i < MAX_FLAG && i < (1 << wire_count); i += i) @@ -81,7 +81,7 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", ui = new(user, src, ui_key, "wires.tmpl", holder.name, window_x, window_y) ui.open() -/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) +/datum/wires/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) var/data[0] var/list/replace_colours = null if(ishuman(user)) @@ -329,4 +329,4 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", /datum/wires/proc/Shuffle() wires_status = 0 - GenerateWires() \ No newline at end of file + GenerateWires() diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index 1ec4f4eb7fc7..1f8c83f5f047 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -1,184 +1,184 @@ -/* -A Star pathfinding algorithm -Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of -windows along the route into account. -Use: -your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) - -Optional extras to add on (in order): -Distance proc : the distance used in every A* calculation (length of path and heuristic) -MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) -Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) -Mintargetdist: Minimum distance to the target before path returns, could be used to get -near a target, but not right to it - for an AI mob with a gun, for example. -Adjacent proc : returns the turfs to consider around the actually processed node -Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) - -Also added 'exclude' turf to avoid travelling over; defaults to null - -Actual Adjacent procs : - - /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) - - /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos - -*/ - -////////////////////// -//PathNode object -////////////////////// - -//A* nodes variables -/PathNode - var/turf/source //turf associated with the PathNode - var/PathNode/prevNode //link to the parent PathNode - var/f //A* Node weight (f = g + h) - var/g //A* movement cost variable - var/h //A* heuristic variable - var/nt //count the number of Nodes traversed - -/PathNode/New(s,p,pg,ph,pnt) - source = s - prevNode = p - g = pg - h = ph - f = g + h - source.PNode = src - nt = pnt - -/PathNode/proc/calc_f() - f = g + h - -////////////////////// -//A* procs -////////////////////// - -//the weighting function, used in the A* algorithm -/proc/PathWeightCompare(PathNode/a, PathNode/b) - return a.f - b.f - -//reversed so that the Heap is a MinHeap rather than a MaxHeap -/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) - return b.f - a.f - -//wrapper that returns an empty list if A* failed to find a path -/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) - var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only) - if(!path) - path = list() - return path - -//the actual algorithm -/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) - - //sanitation - var/start = get_turf(caller) - if(!start) - return 0 - - if(maxnodes) - //if start turf is farther than maxnodes from end turf, no need to do anything - if(call(start, dist)(end) > maxnodes) - return 0 - maxnodedepth = maxnodes //no need to consider path longer than maxnodes - - var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list - var/list/closed = new() //the closed list - var/list/path = null //the returned path, if any - var/PathNode/cur //current processed turf - - //initialization - open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) - - //then run the main loop - while(!open.IsEmpty() && !path) - { - //get the lower f node on the open list - cur = open.Pop() //get the lower f turf in the open list - closed.Add(cur.source) //and tell we've processed it - - //if we only want to get near the target, check if we're close enough - var/closeenough - if(mintargetdist) - closeenough = call(cur.source,dist)(end) <= mintargetdist - - //if too many steps, abandon that path - if(maxnodedepth && (cur.nt > maxnodedepth)) - continue - - //found the target turf (or close enough), let's create the path to it - if(cur.source == end || closeenough) - path = new() - path.Add(cur.source) - - while(cur.prevNode) - cur = cur.prevNode - path.Add(cur.source) - - break - - //get adjacents turfs using the adjacent proc, checking for access with id - var/list/L = call(cur.source,adjacent)(caller, id, simulated_only) - for(var/turf/T in L) - if(T == exclude || (T in closed)) - continue - - var/newg = cur.g + call(cur.source,dist)(T) - if(!T.PNode) //is not already in open list, so add it - open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) - else //is already in open list, check if it's a better way from the current turf - if(newg < T.PNode.g) - T.PNode.prevNode = cur - T.PNode.g = newg - T.PNode.calc_f() - T.PNode.nt = cur.nt + 1 - open.ReSort(T.PNode)//reorder the changed element in the list - - } - - //cleaning after us - for(var/PathNode/PN in open.L) - PN.source.PNode = null - for(var/turf/T in closed) - T.PNode = null - - //reverse the path to get it from start to finish - if(path) - for(var/i = 1; i <= path.len/2; i++) - path.Swap(i,path.len-i+1) - - return path - -//Returns adjacent turfs in cardinal directions that are reachable -//simulated_only controls whether only simulated turfs are considered or not -/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) - var/list/L = new() - var/turf/simulated/T - - for(var/dir in cardinal) - T = get_step(src,dir) - if(!T || (simulated_only && !istype(T))) - continue - if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) - L.Add(T) - return L - -//Returns adjacent turfs in cardinal directions that are reachable via atmos -/turf/proc/reachableAdjacentAtmosTurfs() - return atmos_adjacent_turfs - -/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) - var/adir = get_dir(src, T) - var/rdir = get_dir(T, src) - - for(var/obj/structure/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/machinery/door/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/O in T) - if(!O.CanAStarPass(ID, rdir, caller)) - return 1 - - return 0 +/* +A Star pathfinding algorithm +Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of +windows along the route into account. +Use: +your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) + +Optional extras to add on (in order): +Distance proc : the distance used in every A* calculation (length of path and heuristic) +MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) +Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) +Mintargetdist: Minimum distance to the target before path returns, could be used to get +near a target, but not right to it - for an AI mob with a gun, for example. +Adjacent proc : returns the turfs to consider around the actually processed node +Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) + +Also added 'exclude' turf to avoid travelling over; defaults to null + +Actual Adjacent procs : + + /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) + + /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos + +*/ + +////////////////////// +//PathNode object +////////////////////// + +//A* nodes variables +/PathNode + var/turf/source //turf associated with the PathNode + var/PathNode/prevNode //link to the parent PathNode + var/f //A* Node weight (f = g + h) + var/g //A* movement cost variable + var/h //A* heuristic variable + var/nt //count the number of Nodes traversed + +/PathNode/New(s,p,pg,ph,pnt) + source = s + prevNode = p + g = pg + h = ph + f = g + h + source.PNode = src + nt = pnt + +/PathNode/proc/calc_f() + f = g + h + +////////////////////// +//A* procs +////////////////////// + +//the weighting function, used in the A* algorithm +/proc/PathWeightCompare(PathNode/a, PathNode/b) + return a.f - b.f + +//reversed so that the Heap is a MinHeap rather than a MaxHeap +/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) + return b.f - a.f + +//wrapper that returns an empty list if A* failed to find a path +/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only) + if(!path) + path = list() + return path + +//the actual algorithm +/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + + //sanitation + var/start = get_turf(caller) + if(!start) + return 0 + + if(maxnodes) + //if start turf is farther than maxnodes from end turf, no need to do anything + if(call(start, dist)(end) > maxnodes) + return 0 + maxnodedepth = maxnodes //no need to consider path longer than maxnodes + + var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list + var/list/closed = new() //the closed list + var/list/path = null //the returned path, if any + var/PathNode/cur //current processed turf + + //initialization + open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) + + //then run the main loop + while(!open.IsEmpty() && !path) + { + //get the lower f node on the open list + cur = open.Pop() //get the lower f turf in the open list + closed.Add(cur.source) //and tell we've processed it + + //if we only want to get near the target, check if we're close enough + var/closeenough + if(mintargetdist) + closeenough = call(cur.source,dist)(end) <= mintargetdist + + //if too many steps, abandon that path + if(maxnodedepth && (cur.nt > maxnodedepth)) + continue + + //found the target turf (or close enough), let's create the path to it + if(cur.source == end || closeenough) + path = new() + path.Add(cur.source) + + while(cur.prevNode) + cur = cur.prevNode + path.Add(cur.source) + + break + + //get adjacents turfs using the adjacent proc, checking for access with id + var/list/L = call(cur.source,adjacent)(caller, id, simulated_only) + for(var/turf/T in L) + if(T == exclude || (T in closed)) + continue + + var/newg = cur.g + call(cur.source,dist)(T) + if(!T.PNode) //is not already in open list, so add it + open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) + else //is already in open list, check if it's a better way from the current turf + if(newg < T.PNode.g) + T.PNode.prevNode = cur + T.PNode.g = newg + T.PNode.calc_f() + T.PNode.nt = cur.nt + 1 + open.ReSort(T.PNode)//reorder the changed element in the list + + } + + //cleaning after us + for(var/PathNode/PN in open.L) + PN.source.PNode = null + for(var/turf/T in closed) + T.PNode = null + + //reverse the path to get it from start to finish + if(path) + for(var/i = 1; i <= path.len/2; i++) + path.Swap(i,path.len-i+1) + + return path + +//Returns adjacent turfs in cardinal directions that are reachable +//simulated_only controls whether only simulated turfs are considered or not +/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) + var/list/L = new() + var/turf/simulated/T + + for(var/dir in GLOB.cardinal) + T = get_step(src,dir) + if(!T || (simulated_only && !istype(T))) + continue + if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) + L.Add(T) + return L + +//Returns adjacent turfs in cardinal directions that are reachable via atmos +/turf/proc/reachableAdjacentAtmosTurfs() + return atmos_adjacent_turfs + +/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) + var/adir = get_dir(src, T) + var/rdir = get_dir(T, src) + + for(var/obj/structure/window/W in src) + if(!W.CanAStarPass(ID, adir)) + return 1 + for(var/obj/machinery/door/window/W in src) + if(!W.CanAStarPass(ID, adir)) + return 1 + for(var/obj/O in T) + if(!O.CanAStarPass(ID, rdir, caller)) + return 1 + + return 0 diff --git a/code/defines/procs/announce.dm b/code/defines/procs/announce.dm index 3a70d05029ef..a2b80440ecbf 100644 --- a/code/defines/procs/announce.dm +++ b/code/defines/procs/announce.dm @@ -1,7 +1,7 @@ -/var/datum/announcement/minor/minor_announcement = new() -/var/datum/announcement/priority/priority_announcement = new(do_log = 0) -/var/datum/announcement/priority/command/command_announcement = new(do_log = 0, do_newscast = 0) -/var/datum/announcement/priority/command/event/event_announcement = new(do_log = 0, do_newscast = 0) +GLOBAL_DATUM_INIT(minor_announcement, /datum/announcement/minor, new()) +GLOBAL_DATUM_INIT(priority_announcement, /datum/announcement/priority, new(do_log = 0)) +GLOBAL_DATUM_INIT(command_announcement, /datum/announcement/priority/command, new(do_log = 0, do_newscast = 0)) +GLOBAL_DATUM_INIT(event_announcement, /datum/announcement/priority/command/event, new(do_log = 0, do_newscast = 0)) /datum/announcement var/title = "Attention" diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index b04a4c3b93a0..76e5a1fd0a68 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -1,206 +1,199 @@ -//cursors -#define Default_Cursor 0 -#define Client_Cursor 1 -#define Server_Cursor 2 -//conversions -#define TEXT_CONV 1 -#define RSC_FILE_CONV 2 -#define NUMBER_CONV 3 -//column flag values: -#define IS_NUMERIC 1 -#define IS_BINARY 2 -#define IS_NOT_NULL 4 -#define IS_PRIMARY_KEY 8 -#define IS_UNSIGNED 16 -//types -#define TINYINT 1 -#define SMALLINT 2 -#define MEDIUMINT 3 -#define INTEGER 4 -#define BIGINT 5 -#define DECIMAL 6 -#define FLOAT 7 -#define DOUBLE 8 -#define DATE 9 -#define DATETIME 10 -#define TIMESTAMP 11 -#define TIME 12 -#define STRING 13 -#define BLOB 14 -// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew - - -// Deprecated! See global.dm for new configuration vars -/* -var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) -var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) -*/ - -DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/dbi // This variable is a string containing the DBI MySQL requires. - var/user // This variable contains the username data. - var/password // This variable contains the password data. - var/default_cursor // This contains the default database cursor data. - // - var/server = "" - var/port = 3306 - -DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler - _db_con = _dm_db_new_con() - -DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!config.sql_enabled) - return 0 - if(!src) return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) - -DBConnection/proc/Disconnect() return _dm_db_close(_db_con) - -DBConnection/proc/IsConnected() - if(!config.sql_enabled) return 0 - var/success = _dm_db_is_connected(_db_con) - return success - -DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) - -DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) -DBConnection/proc/SelectDB(database_name,dbi) - if(IsConnected()) Disconnect() - //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) - - -DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) src.sql = sql_query - if(connection_handler) src.db_connection = connection_handler - if(cursor_handler) src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - - -DBQuery - var/sql // The sql query being executed. - var/default_cursor - var/list/columns //list of DB Columns populated by Columns() - var/list/conversions - var/list/item[0] //list of data values populated by NextRow() - - var/DBConnection/db_connection - var/_db_query - -DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler - -DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) - Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) - -DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) - -DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) - -DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) - -DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) - -DBQuery/proc/Columns() - if(!columns) - columns = _dm_db_columns(_db_query,/DBColumn) - return columns - -DBQuery/proc/GetRowData() - var/list/columns = Columns() - var/list/results - if(columns.len) - results = list() - for(var/C in columns) - results+=C - var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] - return results - -DBQuery/proc/Close() - item.len = 0 - columns = null - conversions = null - return _dm_db_close(_db_query) - -DBQuery/proc/Quote(str) - return db_connection.Quote(str) - -DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) column = columns.Find(column) - if(!conversions) conversions = new/list(column) - else if(conversions.len < column) conversions.len = column - conversions[column] = conversion - - -DBColumn - var/name - var/table - var/position //1-based index into item data - var/sql_type - var/flags - var/length - var/max_length - -DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler - return ..() - - -DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) return "TINYINT" - if(SMALLINT) return "SMALLINT" - if(MEDIUMINT) return "MEDIUMINT" - if(INTEGER) return "INTEGER" - if(BIGINT) return "BIGINT" - if(FLOAT) return "FLOAT" - if(DOUBLE) return "DOUBLE" - if(DATE) return "DATE" - if(DATETIME) return "DATETIME" - if(TIMESTAMP) return "TIMESTAMP" - if(TIME) return "TIME" - if(STRING) return "STRING" - if(BLOB) return "BLOB" - - -#undef Default_Cursor -#undef Client_Cursor -#undef Server_Cursor -#undef TEXT_CONV -#undef RSC_FILE_CONV -#undef NUMBER_CONV -#undef IS_NUMERIC -#undef IS_BINARY -#undef IS_NOT_NULL -#undef IS_PRIMARY_KEY -#undef IS_UNSIGNED -#undef TINYINT -#undef SMALLINT -#undef MEDIUMINT -#undef INTEGER -#undef BIGINT -#undef DECIMAL -#undef FLOAT -#undef DOUBLE -#undef DATE -#undef DATETIME -#undef TIMESTAMP -#undef TIME -#undef STRING -#undef BLOB +//cursors +#define Default_Cursor 0 +#define Client_Cursor 1 +#define Server_Cursor 2 +//conversions +#define TEXT_CONV 1 +#define RSC_FILE_CONV 2 +#define NUMBER_CONV 3 +//column flag values: +#define IS_NUMERIC 1 +#define IS_BINARY 2 +#define IS_NOT_NULL 4 +#define IS_PRIMARY_KEY 8 +#define IS_UNSIGNED 16 +//types +#define TINYINT 1 +#define SMALLINT 2 +#define MEDIUMINT 3 +#define INTEGER 4 +#define BIGINT 5 +#define DECIMAL 6 +#define FLOAT 7 +#define DOUBLE 8 +#define DATE 9 +#define DATETIME 10 +#define TIMESTAMP 11 +#define TIME 12 +#define STRING 13 +#define BLOB 14 +// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew + +DBConnection + var/_db_con // This variable contains a reference to the actual database connection. + var/dbi // This variable is a string containing the DBI MySQL requires. + var/user // This variable contains the username data. + var/password // This variable contains the password data. + var/default_cursor // This contains the default database cursor data. + // + var/server = "" + var/port = 3306 + +DBConnection/New(dbi_handler,username,password_handler,cursor_handler) + src.dbi = dbi_handler + src.user = username + src.password = password_handler + src.default_cursor = cursor_handler + _db_con = _dm_db_new_con() + +DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) + if(!config.sql_enabled) + return 0 + if(!src) return 0 + cursor_handler = src.default_cursor + if(!cursor_handler) cursor_handler = Default_Cursor + return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) + +DBConnection/proc/Disconnect() return _dm_db_close(_db_con) + +DBConnection/proc/IsConnected() + if(!config.sql_enabled) return 0 + var/success = _dm_db_is_connected(_db_con) + return success + +DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) + +DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) +DBConnection/proc/SelectDB(database_name,dbi) + if(IsConnected()) Disconnect() + //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) + return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) +DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) + + +DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) + if(sql_query) src.sql = sql_query + if(connection_handler) src.db_connection = connection_handler + if(cursor_handler) src.default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() + + +DBQuery + var/sql // The sql query being executed. + var/default_cursor + var/list/columns //list of DB Columns populated by Columns() + var/list/conversions + var/list/item[0] //list of data values populated by NextRow() + + var/DBConnection/db_connection + var/_db_query + +DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler + +DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) + Close() + return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) + +DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) + +DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) + +DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) + +DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) + +DBQuery/proc/Columns() + if(!columns) + columns = _dm_db_columns(_db_query,/DBColumn) + return columns + +DBQuery/proc/GetRowData() + var/list/columns = Columns() + var/list/results + if(columns.len) + results = list() + for(var/C in columns) + results+=C + var/DBColumn/cur_col = columns[C] + results[C] = src.item[(cur_col.position+1)] + return results + +DBQuery/proc/Close() + item.len = 0 + columns = null + conversions = null + return _dm_db_close(_db_query) + +DBQuery/proc/Quote(str) + return db_connection.Quote(str) + +DBQuery/proc/SetConversion(column,conversion) + if(istext(column)) column = columns.Find(column) + if(!conversions) conversions = new/list(column) + else if(conversions.len < column) conversions.len = column + conversions[column] = conversion + + +DBColumn + var/name + var/table + var/position //1-based index into item data + var/sql_type + var/flags + var/length + var/max_length + +DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) + src.name = name_handler + src.table = table_handler + src.position = position_handler + src.sql_type = type_handler + src.flags = flag_handler + src.length = length_handler + src.max_length = max_length_handler + return ..() + + +DBColumn/proc/SqlTypeName(type_handler=src.sql_type) + switch(type_handler) + if(TINYINT) return "TINYINT" + if(SMALLINT) return "SMALLINT" + if(MEDIUMINT) return "MEDIUMINT" + if(INTEGER) return "INTEGER" + if(BIGINT) return "BIGINT" + if(FLOAT) return "FLOAT" + if(DOUBLE) return "DOUBLE" + if(DATE) return "DATE" + if(DATETIME) return "DATETIME" + if(TIMESTAMP) return "TIMESTAMP" + if(TIME) return "TIME" + if(STRING) return "STRING" + if(BLOB) return "BLOB" + + +#undef Default_Cursor +#undef Client_Cursor +#undef Server_Cursor +#undef TEXT_CONV +#undef RSC_FILE_CONV +#undef NUMBER_CONV +#undef IS_NUMERIC +#undef IS_BINARY +#undef IS_NOT_NULL +#undef IS_PRIMARY_KEY +#undef IS_UNSIGNED +#undef TINYINT +#undef SMALLINT +#undef MEDIUMINT +#undef INTEGER +#undef BIGINT +#undef DECIMAL +#undef FLOAT +#undef DOUBLE +#undef DATE +#undef DATETIME +#undef TIMESTAMP +#undef TIME +#undef STRING +#undef BLOB diff --git a/code/defines/procs/radio.dm b/code/defines/procs/radio.dm index 84d3ddaae783..a3d6647ef416 100644 --- a/code/defines/procs/radio.dm +++ b/code/defines/procs/radio.dm @@ -35,8 +35,8 @@ var/list/receiver_reception = new /proc/get_message_server() - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) + if(GLOB.message_servers) + for(var/obj/machinery/message_server/MS in GLOB.message_servers) if(MS.active) return MS return null diff --git a/code/defines/procs/records.dm b/code/defines/procs/records.dm index 256ae51dbf2e..a74680ec5788 100644 --- a/code/defines/procs/records.dm +++ b/code/defines/procs/records.dm @@ -20,7 +20,7 @@ G.fields["religion"] = "Unknown" G.fields["photo_front"] = front G.fields["photo_side"] = side - data_core.general += G + GLOB.data_core.general += G qdel(dummy) return G @@ -36,11 +36,11 @@ R.fields["ma_crim"] = "None" R.fields["ma_crim_d"] = "No major crime convictions." R.fields["notes"] = "No notes." - data_core.security += R + GLOB.data_core.security += R return R /proc/find_security_record(field, value) - return find_record(field, value, data_core.security) + return find_record(field, value, GLOB.data_core.security) /proc/find_record(field, value, list/L) for(var/datum/data/record/R in L) diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index e68bb7ecc638..e6e111880fb3 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -1,163 +1,163 @@ -/proc/sql_poll_players() - if(!config.sql_enabled) - return - var/playercount = 0 - for(var/mob/M in GLOB.player_list) - if(M.client) - playercount += 1 - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during player polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, time) VALUES ([playercount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during player polling. Error : \[[err]\]\n") - - -/proc/sql_poll_admins() - if(!config.sql_enabled) - return - var/admincount = GLOB.admins.len - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during admin polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (admincount, time) VALUES ([admincount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during admin polling. Error : \[[err]\]\n") - -/proc/sql_report_round_start() - // TODO - if(!config.sql_enabled) - return - -/proc/sql_report_round_end() - // TODO - if(!config.sql_enabled) - return - -/proc/sql_report_death(mob/living/carbon/human/H) - if(!config.sql_enabled) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H.loc) - var/podname = "Unknown" - if(placeofdeath) - podname = sanitizeSQL(placeofdeath.name) - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" -// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/sql_report_cyborg_death(mob/living/silicon/robot/H) - if(!config.sql_enabled) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/turf/T = H.loc - var/area/placeofdeath = get_area(T.loc) - var/podname = sanitizeSQL(placeofdeath.name) - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" -// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - -/proc/statistic_cycle() - if(!config.sql_enabled) - return - while(1) - sql_poll_players() - sleep(600) - sql_poll_admins() - sleep(6000) //Poll every ten minutes - -//This proc is used for feedback. It is executed at round end. -/proc/sql_commit_feedback() - if(!blackbox) - log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") - return - - //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. - var/list/datum/feedback_variable/content = blackbox.get_round_feedback() - - if(!content) - log_game("Round ended without any feedback being generated. No feedback was sent to the database.") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during feedback reporting. Failed to connect.") - else - - var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]") - max_query.Execute() - - var/newroundid - - while(max_query.NextRow()) - newroundid = max_query.item[1] - - if(!(isnum(newroundid))) - newroundid = text2num(newroundid) - - if(isnum(newroundid)) - newroundid++ - else - newroundid = 1 - - for(var/datum/feedback_variable/item in content) - var/variable = item.get_variable() - var/value = item.get_value() - - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during feedback reporting. Error : \[[err]\]\n") +/proc/sql_poll_players() + if(!config.sql_enabled) + return + var/playercount = 0 + for(var/mob/M in GLOB.player_list) + if(M.client) + playercount += 1 + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during player polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, time) VALUES ([playercount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during player polling. Error : \[[err]\]\n") + + +/proc/sql_poll_admins() + if(!config.sql_enabled) + return + var/admincount = GLOB.admins.len + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during admin polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (admincount, time) VALUES ([admincount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during admin polling. Error : \[[err]\]\n") + +/proc/sql_report_round_start() + // TODO + if(!config.sql_enabled) + return + +/proc/sql_report_round_end() + // TODO + if(!config.sql_enabled) + return + +/proc/sql_report_death(mob/living/carbon/human/H) + if(!config.sql_enabled) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H.loc) + var/podname = "Unknown" + if(placeofdeath) + podname = sanitizeSQL(placeofdeath.name) + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" +// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/sql_report_cyborg_death(mob/living/silicon/robot/H) + if(!config.sql_enabled) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/turf/T = H.loc + var/area/placeofdeath = get_area(T.loc) + var/podname = sanitizeSQL(placeofdeath.name) + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" +// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + +/proc/statistic_cycle() + if(!config.sql_enabled) + return + while(1) + sql_poll_players() + sleep(600) + sql_poll_admins() + sleep(6000) //Poll every ten minutes + +//This proc is used for feedback. It is executed at round end. +/proc/sql_commit_feedback() + if(!GLOB.blackbox) + log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") + return + + //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. + var/list/datum/feedback_variable/content = GLOB.blackbox.get_round_feedback() + + if(!content) + log_game("Round ended without any feedback being generated. No feedback was sent to the database.") + return + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_game("SQL ERROR during feedback reporting. Failed to connect.") + else + + var/DBQuery/max_query = GLOB.dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]") + max_query.Execute() + + var/newroundid + + while(max_query.NextRow()) + newroundid = max_query.item[1] + + if(!(isnum(newroundid))) + newroundid = text2num(newroundid) + + if(isnum(newroundid)) + newroundid++ + else + newroundid = 1 + + for(var/datum/feedback_variable/item in content) + var/variable = item.get_variable() + var/value = item.get_value() + + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during feedback reporting. Error : \[[err]\]\n") diff --git a/code/defines/vox_sounds.dm b/code/defines/vox_sounds.dm index 794a259ea4e4..1d7bb9753a0a 100644 --- a/code/defines/vox_sounds.dm +++ b/code/defines/vox_sounds.dm @@ -1,7 +1,7 @@ // List is required to compile the resources into the game when it loads. // Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable. -var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg', +GLOBAL_LIST_INIT(vox_sounds, list("," = 'sound/vox_fem/,.ogg', "." = 'sound/vox_fem/..ogg', "a" = 'sound/vox_fem/a.ogg', "abortions" = 'sound/vox_fem/abortions.ogg', @@ -1267,4 +1267,4 @@ var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg', "zero" = 'sound/vox_fem/zero.ogg', "zone" = 'sound/vox_fem/zone.ogg', "zulu" = 'sound/vox_fem/zulu.ogg' -) \ No newline at end of file +)) diff --git a/code/game/area/Dynamic areas.dm b/code/game/area/Dynamic areas.dm index c6d1154847b2..10957e11d2ad 100644 --- a/code/game/area/Dynamic areas.dm +++ b/code/game/area/Dynamic areas.dm @@ -41,4 +41,4 @@ match_width = 5 match_height = 4 requires_power = FALSE - dynamic_lighting = DYNAMIC_LIGHTING_FORCED \ No newline at end of file + dynamic_lighting = DYNAMIC_LIGHTING_FORCED diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 630903054a26..3abe68251718 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -15,30 +15,30 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /*Adding a wizard area teleport list because motherfucking lag -- Urist*/ /*I am far too lazy to make it a proper list of areas so I'll just make it run the usual telepot routine at the start of the game*/ -var/list/teleportlocs = list() +GLOBAL_LIST_EMPTY(teleportlocs) /hook/startup/proc/process_teleport_locs() for(var/area/AR in world) if(AR.no_teleportlocs) continue - if(teleportlocs.Find(AR.name)) continue + if(GLOB.teleportlocs.Find(AR.name)) continue var/turf/picked = safepick(get_area_turfs(AR.type)) if(picked && is_station_level(picked.z)) - teleportlocs += AR.name - teleportlocs[AR.name] = AR + GLOB.teleportlocs += AR.name + GLOB.teleportlocs[AR.name] = AR - teleportlocs = sortAssoc(teleportlocs) + GLOB.teleportlocs = sortAssoc(GLOB.teleportlocs) return 1 -var/list/ghostteleportlocs = list() +GLOBAL_LIST_EMPTY(ghostteleportlocs) /hook/startup/proc/process_ghost_teleport_locs() for(var/area/AR in world) - if(ghostteleportlocs.Find(AR.name)) continue + if(GLOB.ghostteleportlocs.Find(AR.name)) continue var/list/turfs = get_area_turfs(AR.type) if(turfs.len) - ghostteleportlocs += AR.name - ghostteleportlocs[AR.name] = AR + GLOB.ghostteleportlocs += AR.name + GLOB.ghostteleportlocs[AR.name] = AR - ghostteleportlocs = sortAssoc(ghostteleportlocs) + GLOB.ghostteleportlocs = sortAssoc(GLOB.ghostteleportlocs) return 1 @@ -2290,7 +2290,7 @@ var/list/ghostteleportlocs = list() */ // CENTCOM -var/list/centcom_areas = list ( +GLOBAL_LIST_INIT(centcom_areas, list( /area/centcom, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, @@ -2299,10 +2299,10 @@ var/list/centcom_areas = list ( /area/shuttle/transport1, /area/shuttle/administration/centcom, /area/shuttle/specops/centcom, -) +)) //SPACE STATION 13 -var/list/the_station_areas = list ( +GLOBAL_LIST_INIT(the_station_areas, list( /area/shuttle/arrival, /area/shuttle/escape, /area/shuttle/escape_pod1/station, @@ -2349,4 +2349,4 @@ var/list/the_station_areas = list ( /area/turret_protected/ai_upload, //do not try to simplify to "/area/turret_protected" --rastaf0 /area/turret_protected/ai_upload_foyer, /area/turret_protected/ai, -) +)) diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index 0bc7b5f001bd..2ca607921ffe 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -1,26 +1,26 @@ -/area/ai_monitored - name = "AI Monitored Area" - var/obj/machinery/camera/motioncamera = null - - -/area/ai_monitored/New() - ..() - // locate and store the motioncamera - spawn (20) // spawn on a delay to let turfs/objs load - for(var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - return - return - -/area/ai_monitored/Entered(atom/movable/O) - ..() - if(ismob(O) && motioncamera) - motioncamera.newTarget(O) - -/area/ai_monitored/Exited(atom/movable/O) - if(ismob(O) && motioncamera) - motioncamera.lostTarget(O) - - +/area/ai_monitored + name = "AI Monitored Area" + var/obj/machinery/camera/motioncamera = null + + +/area/ai_monitored/New() + ..() + // locate and store the motioncamera + spawn (20) // spawn on a delay to let turfs/objs load + for(var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncamera = M + M.area_motion = src + return + return + +/area/ai_monitored/Entered(atom/movable/O) + ..() + if(ismob(O) && motioncamera) + motioncamera.newTarget(O) + +/area/ai_monitored/Exited(atom/movable/O) + if(ismob(O) && motioncamera) + motioncamera.lostTarget(O) + + diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 26dc6ddbf56c..6ad455326414 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -1,453 +1,453 @@ -/area - var/fire = null - var/atmosalm = ATMOS_ALARM_NONE - var/poweralm = TRUE - var/party = null - var/report_alerts = TRUE // Should atmos alerts notify the AI/computers - level = null - name = "Space" - icon = 'icons/turf/areas.dmi' - icon_state = "unknown" - layer = AREA_LAYER - plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. - luminosity = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - invisibility = INVISIBILITY_LIGHTING - var/valid_territory = TRUE //used for cult summoning areas on station zlevel - var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. - var/lightswitch = TRUE - - var/eject = null - - var/debug = FALSE - var/requires_power = TRUE - var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() - - var/power_equip = TRUE - var/power_light = TRUE - var/power_environ = TRUE - var/music = null - var/used_equip = FALSE - var/used_light = FALSE - var/used_environ = FALSE - var/static_equip - var/static_light = FALSE - var/static_environ - - var/has_gravity = TRUE - var/list/apc = list() - var/no_air = null - - var/air_doors_activated = FALSE - - var/tele_proof = FALSE - var/no_teleportlocs = FALSE - - var/outdoors = FALSE //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room) - var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? - var/nad_allowed = FALSE //is the station NAD allowed on this area? - - // This var is used with the maploader (modules/awaymissions/maploader/reader.dm) - // if this is 1, when used in a map snippet, this will instantiate a unique - // area from any other instances already present (meaning you can have - // separate APCs, and so on) - var/there_can_be_many = FALSE - - var/global/global_uid = 0 - var/uid - - var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ - 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ - 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ - 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ - 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ - 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') - - var/fast_despawn = FALSE - var/can_get_auto_cryod = TRUE - var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. - - var/parallax_movedir = 0 - var/moving = FALSE - -/area/Initialize(mapload) - GLOB.all_areas += src - icon_state = "" - layer = AREA_LAYER - uid = ++global_uid - - map_name = name // Save the initial (the name set in the map) name of the area. - - if(requires_power) - luminosity = 0 - else - power_light = TRUE - power_equip = TRUE - power_environ = TRUE - - if(dynamic_lighting == DYNAMIC_LIGHTING_FORCED) - dynamic_lighting = DYNAMIC_LIGHTING_ENABLED - luminosity = 0 - else if(dynamic_lighting != DYNAMIC_LIGHTING_IFSTARLIGHT) - dynamic_lighting = DYNAMIC_LIGHTING_DISABLED - if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) - dynamic_lighting = config.starlight ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED - - . = ..() - - blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. - - if(!IS_DYNAMIC_LIGHTING(src)) - add_overlay(/obj/effect/fullbright) - - reg_in_areas_in_z() - - return INITIALIZE_HINT_LATELOAD - -/area/LateInitialize() - . = ..() - power_change() // all machines set to current power level, also updates lighting icon - -/area/proc/reg_in_areas_in_z() - if(contents.len) - var/list/areas_in_z = space_manager.areas_in_z - var/z - for(var/i in 1 to contents.len) - var/atom/thing = contents[i] - if(!thing) - continue - z = thing.z - break - if(!z) - WARNING("No z found for [src]") - return - if(!areas_in_z["[z]"]) - areas_in_z["[z]"] = list() - areas_in_z["[z]"] += src - -/area/proc/get_cameras() - var/list/cameras = list() - for(var/obj/machinery/camera/C in src) - cameras += C - return cameras - - -/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) - if(report_alerts) - if(danger_level == ATMOS_ALARM_NONE) - SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) - else - SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) - - //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. - for(var/obj/machinery/alarm/AA in src) - if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) - danger_level = max(danger_level, AA.danger_level) - - if(danger_level != atmosalm) - if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) - //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise - air_doors_open() - else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) - air_doors_close() - - atmosalm = danger_level - for(var/obj/machinery/alarm/AA in src) - AA.update_icon() - - air_alarm_repository.update_cache(src) - return 1 - air_alarm_repository.update_cache(src) - return 0 - -/area/proc/air_doors_close() - if(!air_doors_activated) - air_doors_activated = TRUE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.activate_alarm() - if(D.operating) - D.nextstate = FD_CLOSED - else if(!D.density) - spawn(0) - D.close() - -/area/proc/air_doors_open() - if(air_doors_activated) - air_doors_activated = FALSE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.deactivate_alarm() - if(D.operating) - D.nextstate = OPEN - else if(D.density) - spawn(0) - D.open() - - -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_close() - -/area/proc/fire_reset() - if(fire) - fire = 0 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_open() - - return - -/area/proc/burglaralert(var/obj/trigger) - if(always_unpowered == 1) //no burglar alarms in space/asteroid - return - - //Trigger alarm effect - set_fire_alarm_effect() - - //Lockdown airlocks - for(var/obj/machinery/door/airlock/A in src) - spawn(0) - A.close() - if(A.density) - A.lock() - - SSalarms.burglar_alarm.triggerAlarm(src, trigger) - spawn(600) - SSalarms.burglar_alarm.clearAlarm(src, trigger) - -/area/proc/set_fire_alarm_effect() - fire = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/readyalert() - if(!eject) - eject = 1 - updateicon() - -/area/proc/readyreset() - if(eject) - eject = 0 - updateicon() - -/area/proc/partyalert() - if(!party) - party = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/partyreset() - if(party) - party = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - updateicon() - -/area/proc/updateicon() - if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. - if(fire && !eject && !party) - icon_state = "red" - else if(!fire && eject && !party) - icon_state = "red" - else if(party && !fire && !eject) - icon_state = "party" - else - icon_state = "blue-red" - else - var/weather_icon - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.stage != END_STAGE && (src in W.impacted_areas)) - W.update_areas() - weather_icon = TRUE - if(!weather_icon) - icon_state = null - -/area/space/updateicon() - icon_state = null - -/* -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -*/ - -/area/proc/powered(var/chan) // return true if the area has power to given channel - - if(!requires_power) - return 1 - if(always_unpowered) - return 0 - switch(chan) - if(EQUIP) - return power_equip - if(LIGHT) - return power_light - if(ENVIRON) - return power_environ - - return 0 - -/area/space/powered(chan) //Nope.avi - return 0 - -// called when power status changes - -/area/proc/power_change() - for(var/obj/machinery/M in src) // for each machine in the area - M.power_change() // reverify power status (to update icons etc.) - updateicon() - -/area/proc/usage(var/chan) - var/used = 0 - switch(chan) - if(LIGHT) - used += used_light - if(EQUIP) - used += used_equip - if(ENVIRON) - used += used_environ - if(TOTAL) - used += used_light + used_equip + used_environ - if(STATIC_EQUIP) - used += static_equip - if(STATIC_LIGHT) - used += static_light - if(STATIC_ENVIRON) - used += static_environ - return used - -/area/proc/addStaticPower(value, powerchannel) - switch(powerchannel) - if(STATIC_EQUIP) - static_equip += value - if(STATIC_LIGHT) - static_light += value - if(STATIC_ENVIRON) - static_environ += value - -/area/proc/clear_usage() - - used_equip = 0 - used_light = 0 - used_environ = 0 - -/area/proc/use_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - -/area/proc/use_battery_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - - -/area/Entered(A) - var/area/newarea - var/area/oldarea - - if(istype(A,/mob)) - var/mob/M=A - - if(!M.lastarea) - M.lastarea = get_area(M) - newarea = get_area(M) - oldarea = M.lastarea - - if(newarea==oldarea) return - - M.lastarea = src - - if(!istype(A,/mob/living)) return - - var/mob/living/L = A - if(!L.ckey) return - if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together. - thunk(L) - - // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys - L.client.ambience_playing = 1 - L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) - else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) - L.client.ambience_playing = 0 - - if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) - var/sound = pick(ambientsounds) - - if(!L.client.played) - L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) - L.client.played = 1 - spawn(600) //ewww - this is very very bad - if(L && L.client) - L.client.played = 0 - -/area/proc/gravitychange(var/gravitystate = 0, var/area/A) - A.has_gravity = gravitystate - - if(gravitystate) - for(var/mob/living/carbon/human/M in A) - thunk(M) - -/area/proc/thunk(var/mob/living/carbon/human/M) - if(istype(M,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. - if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP)) - return - - if(M.buckled) //Cam't fall down if you are buckled - return - - if(istype(get_turf(M), /turf/space)) // Can't fall onto nothing. - return - - if((istype(M,/mob/living/carbon/human/)) && (M.m_intent == MOVE_INTENT_RUN)) - M.Stun(5) - M.Weaken(5) - - else if(istype(M,/mob/living/carbon/human/)) - M.Stun(2) - M.Weaken(2) - - - to_chat(M, "Gravity!") - -/proc/has_gravity(atom/AT, turf/T) - if(!T) - T = get_turf(AT) - var/area/A = get_area(T) - if(istype(T, /turf/space)) // Turf never has gravity - return 0 - else if(A && A.has_gravity) // Areas which always has gravity - return 1 - else - // There's a gravity generator on our z level - // This would do well when integrated with the z level manager - if(T && gravity_generators["[T.z]"] && length(gravity_generators["[T.z]"])) - return 1 - return 0 - -/area/proc/prison_break() - for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) - for(var/obj/machinery/door/airlock/temp_airlock in src) - temp_airlock.prison_open() - for(var/obj/machinery/door/window/temp_windoor in src) - temp_windoor.open() - -/area/AllowDrop() - CRASH("Bad op: area/AllowDrop() called") - -/area/drop_location() - CRASH("Bad op: area/drop_location() called") +/area + var/fire = null + var/atmosalm = ATMOS_ALARM_NONE + var/poweralm = TRUE + var/party = null + var/report_alerts = TRUE // Should atmos alerts notify the AI/computers + level = null + name = "Space" + icon = 'icons/turf/areas.dmi' + icon_state = "unknown" + layer = AREA_LAYER + plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. + luminosity = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + invisibility = INVISIBILITY_LIGHTING + var/valid_territory = TRUE //used for cult summoning areas on station zlevel + var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. + var/lightswitch = TRUE + + var/eject = null + + var/debug = FALSE + var/requires_power = TRUE + var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() + + var/power_equip = TRUE + var/power_light = TRUE + var/power_environ = TRUE + var/music = null + var/used_equip = FALSE + var/used_light = FALSE + var/used_environ = FALSE + var/static_equip + var/static_light = FALSE + var/static_environ + + var/has_gravity = TRUE + var/list/apc = list() + var/no_air = null + + var/air_doors_activated = FALSE + + var/tele_proof = FALSE + var/no_teleportlocs = FALSE + + var/outdoors = FALSE //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room) + var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? + var/nad_allowed = FALSE //is the station NAD allowed on this area? + + // This var is used with the maploader (modules/awaymissions/maploader/reader.dm) + // if this is 1, when used in a map snippet, this will instantiate a unique + // area from any other instances already present (meaning you can have + // separate APCs, and so on) + var/there_can_be_many = FALSE + + var/global/global_uid = 0 + var/uid + + var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ + 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ + 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ + 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ + 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ + 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') + + var/fast_despawn = FALSE + var/can_get_auto_cryod = TRUE + var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. + + var/parallax_movedir = 0 + var/moving = FALSE + +/area/Initialize(mapload) + GLOB.all_areas += src + icon_state = "" + layer = AREA_LAYER + uid = ++global_uid + + map_name = name // Save the initial (the name set in the map) name of the area. + + if(requires_power) + luminosity = 0 + else + power_light = TRUE + power_equip = TRUE + power_environ = TRUE + + if(dynamic_lighting == DYNAMIC_LIGHTING_FORCED) + dynamic_lighting = DYNAMIC_LIGHTING_ENABLED + luminosity = 0 + else if(dynamic_lighting != DYNAMIC_LIGHTING_IFSTARLIGHT) + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) + dynamic_lighting = config.starlight ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED + + . = ..() + + blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. + + if(!IS_DYNAMIC_LIGHTING(src)) + add_overlay(/obj/effect/fullbright) + + reg_in_areas_in_z() + + return INITIALIZE_HINT_LATELOAD + +/area/LateInitialize() + . = ..() + power_change() // all machines set to current power level, also updates lighting icon + +/area/proc/reg_in_areas_in_z() + if(contents.len) + var/list/areas_in_z = GLOB.space_manager.areas_in_z + var/z + for(var/i in 1 to contents.len) + var/atom/thing = contents[i] + if(!thing) + continue + z = thing.z + break + if(!z) + WARNING("No z found for [src]") + return + if(!areas_in_z["[z]"]) + areas_in_z["[z]"] = list() + areas_in_z["[z]"] += src + +/area/proc/get_cameras() + var/list/cameras = list() + for(var/obj/machinery/camera/C in src) + cameras += C + return cameras + + +/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) + if(report_alerts) + if(danger_level == ATMOS_ALARM_NONE) + SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) + else + SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) + + //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. + for(var/obj/machinery/alarm/AA in src) + if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) + danger_level = max(danger_level, AA.danger_level) + + if(danger_level != atmosalm) + if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) + //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise + air_doors_open() + else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) + air_doors_close() + + atmosalm = danger_level + for(var/obj/machinery/alarm/AA in src) + AA.update_icon() + + GLOB.air_alarm_repository.update_cache(src) + return 1 + GLOB.air_alarm_repository.update_cache(src) + return 0 + +/area/proc/air_doors_close() + if(!air_doors_activated) + air_doors_activated = TRUE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.welded) + D.activate_alarm() + if(D.operating) + D.nextstate = FD_CLOSED + else if(!D.density) + spawn(0) + D.close() + +/area/proc/air_doors_open() + if(air_doors_activated) + air_doors_activated = FALSE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.welded) + D.deactivate_alarm() + if(D.operating) + D.nextstate = OPEN + else if(D.density) + spawn(0) + D.open() + + +/area/proc/fire_alert() + if(!fire) + fire = 1 //used for firedoor checks + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + air_doors_close() + +/area/proc/fire_reset() + if(fire) + fire = 0 //used for firedoor checks + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + air_doors_open() + + return + +/area/proc/burglaralert(var/obj/trigger) + if(always_unpowered == 1) //no burglar alarms in space/asteroid + return + + //Trigger alarm effect + set_fire_alarm_effect() + + //Lockdown airlocks + for(var/obj/machinery/door/airlock/A in src) + spawn(0) + A.close() + if(A.density) + A.lock() + + SSalarms.burglar_alarm.triggerAlarm(src, trigger) + spawn(600) + SSalarms.burglar_alarm.clearAlarm(src, trigger) + +/area/proc/set_fire_alarm_effect() + fire = 1 + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/area/proc/readyalert() + if(!eject) + eject = 1 + updateicon() + +/area/proc/readyreset() + if(eject) + eject = 0 + updateicon() + +/area/proc/partyalert() + if(!party) + party = 1 + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/area/proc/partyreset() + if(party) + party = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + updateicon() + +/area/proc/updateicon() + if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. + if(fire && !eject && !party) + icon_state = "red" + else if(!fire && eject && !party) + icon_state = "red" + else if(party && !fire && !eject) + icon_state = "party" + else + icon_state = "blue-red" + else + var/weather_icon + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.stage != END_STAGE && (src in W.impacted_areas)) + W.update_areas() + weather_icon = TRUE + if(!weather_icon) + icon_state = null + +/area/space/updateicon() + icon_state = null + +/* +#define EQUIP 1 +#define LIGHT 2 +#define ENVIRON 3 +*/ + +/area/proc/powered(var/chan) // return true if the area has power to given channel + + if(!requires_power) + return 1 + if(always_unpowered) + return 0 + switch(chan) + if(EQUIP) + return power_equip + if(LIGHT) + return power_light + if(ENVIRON) + return power_environ + + return 0 + +/area/space/powered(chan) //Nope.avi + return 0 + +// called when power status changes + +/area/proc/power_change() + for(var/obj/machinery/M in src) // for each machine in the area + M.power_change() // reverify power status (to update icons etc.) + updateicon() + +/area/proc/usage(var/chan) + var/used = 0 + switch(chan) + if(LIGHT) + used += used_light + if(EQUIP) + used += used_equip + if(ENVIRON) + used += used_environ + if(TOTAL) + used += used_light + used_equip + used_environ + if(STATIC_EQUIP) + used += static_equip + if(STATIC_LIGHT) + used += static_light + if(STATIC_ENVIRON) + used += static_environ + return used + +/area/proc/addStaticPower(value, powerchannel) + switch(powerchannel) + if(STATIC_EQUIP) + static_equip += value + if(STATIC_LIGHT) + static_light += value + if(STATIC_ENVIRON) + static_environ += value + +/area/proc/clear_usage() + + used_equip = 0 + used_light = 0 + used_environ = 0 + +/area/proc/use_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + +/area/proc/use_battery_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + + +/area/Entered(A) + var/area/newarea + var/area/oldarea + + if(istype(A,/mob)) + var/mob/M=A + + if(!M.lastarea) + M.lastarea = get_area(M) + newarea = get_area(M) + oldarea = M.lastarea + + if(newarea==oldarea) return + + M.lastarea = src + + if(!istype(A,/mob/living)) return + + var/mob/living/L = A + if(!L.ckey) return + if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together. + thunk(L) + + // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch + if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys + L.client.ambience_playing = 1 + L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) + else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) + L.client.ambience_playing = 0 + + if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) + var/sound = pick(ambientsounds) + + if(!L.client.played) + L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) + L.client.played = 1 + spawn(600) //ewww - this is very very bad + if(L && L.client) + L.client.played = 0 + +/area/proc/gravitychange(var/gravitystate = 0, var/area/A) + A.has_gravity = gravitystate + + if(gravitystate) + for(var/mob/living/carbon/human/M in A) + thunk(M) + +/area/proc/thunk(var/mob/living/carbon/human/M) + if(istype(M,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. + if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP)) + return + + if(M.buckled) //Cam't fall down if you are buckled + return + + if(istype(get_turf(M), /turf/space)) // Can't fall onto nothing. + return + + if((istype(M,/mob/living/carbon/human/)) && (M.m_intent == MOVE_INTENT_RUN)) + M.Stun(5) + M.Weaken(5) + + else if(istype(M,/mob/living/carbon/human/)) + M.Stun(2) + M.Weaken(2) + + + to_chat(M, "Gravity!") + +/proc/has_gravity(atom/AT, turf/T) + if(!T) + T = get_turf(AT) + var/area/A = get_area(T) + if(istype(T, /turf/space)) // Turf never has gravity + return 0 + else if(A && A.has_gravity) // Areas which always has gravity + return 1 + else + // There's a gravity generator on our z level + // This would do well when integrated with the z level manager + if(T && GLOB.gravity_generators["[T.z]"] && length(GLOB.gravity_generators["[T.z]"])) + return 1 + return 0 + +/area/proc/prison_break() + for(var/obj/machinery/power/apc/temp_apc in src) + temp_apc.overload_lighting(70) + for(var/obj/machinery/door/airlock/temp_airlock in src) + temp_airlock.prison_open() + for(var/obj/machinery/door/window/temp_windoor in src) + temp_windoor.open() + +/area/AllowDrop() + CRASH("Bad op: area/AllowDrop() called") + +/area/drop_location() + CRASH("Bad op: area/drop_location() called") diff --git a/code/game/area/areas/depot-areas.dm b/code/game/area/areas/depot-areas.dm index d0ae6fc55884..7a7c934df353 100644 --- a/code/game/area/areas/depot-areas.dm +++ b/code/game/area/areas/depot-areas.dm @@ -476,4 +476,4 @@ var/obj/machinery/shieldwall/syndicate/S = locateUID(shuid) if(S) qdel(S) - shield_list = list() \ No newline at end of file + shield_list = list() diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm index 4a2317889faa..0b07fffda5e7 100644 --- a/code/game/area/areas/mining.dm +++ b/code/game/area/areas/mining.dm @@ -127,4 +127,4 @@ icon_state = "danger" /area/lavaland/surface/outdoors/explored - name = "Lavaland Labor Camp" \ No newline at end of file + name = "Lavaland Labor Camp" diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm index ac4c41b0c42c..5baf1fe315e9 100644 --- a/code/game/area/areas/ruins/lavaland.dm +++ b/code/game/area/areas/ruins/lavaland.dm @@ -87,4 +87,4 @@ //ash walker nest /area/ruin/unpowered/ash_walkers - icon_state = "red" \ No newline at end of file + icon_state = "red" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index cd7e1e450157..1cb0ee3baad1 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,884 +1,884 @@ -/atom - layer = TURF_LAYER - plane = GAME_PLANE - var/level = 2 - var/flags = NONE - var/flags_2 = NONE - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - var/blood_color - var/last_bumped = 0 - var/pass_flags = 0 - var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. - var/simulated = TRUE //filter for actions - used by lighting overlays - var/atom_say_verb = "says" - var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays - - - ///Chemistry. - var/container_type = NONE - var/datum/reagents/reagents = null - - //This atom's HUD (med/sec, etc) images. Associative list. - var/list/image/hud_list = list() - //HUD images that this atom can provide. - var/list/hud_possible - - ///Chemistry. - - - //Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 - - //Detective Work, used for the duplicate data points kept in the scanners - var/list/original_atom - - //Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default - var/can_leave_fibers = TRUE - - var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox - - var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. - - var/initialized = FALSE - - var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/remove_overlays // a very temporary list of overlays to remove - var/list/add_overlays // a very temporary list of overlays to add - - var/list/atom_colours //used to store the different colors on an atom - //its inherent color, the colored paint applied on it, special color effect etc... - -/atom/New(loc, ...) - if(use_preloader && (src.type == _preloader.target_path))//in case the instanciated atom is creating other atoms in New() - _preloader.load(src) - . = ..() - attempt_init(arglist(args)) - -// This is distinct from /tg/ because of our space management system -// This is overriden in /atom/movable and the parent isn't called if the SMS wants to deal with it's init -/atom/proc/attempt_init(...) - var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD - if(SSatoms.InitAtom(src, args)) - // we were deleted - return - - -//Called after New if the map is being loaded. mapload = TRUE -//Called from base of New if the map is not being loaded. mapload = FALSE -//This base must be called or derivatives must set initialized to TRUE -//must not sleep -//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm - -//Note: the following functions don't call the base for optimization and must copypasta: -// /turf/Initialize -// /turf/open/space/Initialize - -/atom/proc/Initialize(mapload, ...) - if(initialized) - stack_trace("Warning: [src]([type]) initialized multiple times!") - initialized = TRUE - - if(color) - add_atom_colour(color, FIXED_COLOUR_PRIORITY) - - if(light_power && light_range) - update_light() - - if(opacity && isturf(loc)) - var/turf/T = loc - T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. - - if(loc) - loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. - - ComponentInitialize() - - return INITIALIZE_HINT_NORMAL - - -//called if Initialize returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -// Put your AddComponent() calls here -/atom/proc/ComponentInitialize() - return - -/atom/proc/InitializedOn(atom/A) // Proc for when something is initialized on a atom - Optional to call. Useful for observer pattern etc. - return - -/atom/proc/onCentcom() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(!is_admin_level(T.z))//if not, don't bother - return 0 - - //check for centcomm shuttles - for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry")) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle) - if(T in M.areaInstance) - return 1 - - //finally check for centcom itself - return istype(T.loc,/area/centcom) - -/atom/proc/onSyndieBase() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(!is_admin_level(T.z))//if not, don't bother - return 0 - - if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership)) - return 1 - - return 0 - -/atom/Destroy() - if(alternate_appearances) - for(var/aakey in alternate_appearances) - var/datum/alternate_appearance/AA = alternate_appearances[aakey] - qdel(AA) - alternate_appearances = null - - QDEL_NULL(reagents) - invisibility = INVISIBILITY_MAXIMUM - LAZYCLEARLIST(overlays) - LAZYCLEARLIST(priority_overlays) - - QDEL_NULL(light) - - return ..() - -//Hook for running code when a dir change occurs -/atom/proc/setDir(newdir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) - dir = newdir - -///Handle melee attack by a mech -/atom/proc/mech_melee_attack(obj/mecha/M) - return - -/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) - if(does_attack_animation) - user.changeNext_move(CLICK_CD_MELEE) - add_attack_logs(user, src, "Punched with hulk powers") - user.do_attack_animation(src, ATTACK_EFFECT_SMASH) - -/atom/proc/CheckParts(list/parts_list) - for(var/A in parts_list) - if(istype(A, /datum/reagent)) - if(!reagents) - reagents = new() - reagents.reagent_list.Add(A) - reagents.conditional_update() - else if(istype(A, /atom/movable)) - var/atom/movable/M = A - if(istype(M.loc, /mob/living)) - var/mob/living/L = M.loc - L.unEquip(M) - M.forceMove(src) - -/atom/proc/assume_air(datum/gas_mixture/giver) - qdel(giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -/atom/proc/check_eye(user as mob) - if(istype(user, /mob/living/silicon/ai)) // WHYYYY - return 1 - return - -/atom/proc/on_reagent_change() - return - -/atom/proc/Bumped(AM as mob|obj) - return - -/// Convenience proc to see if a container is open for chemistry handling -/atom/proc/is_open_container() - return is_refillable() && is_drainable() - -/// Is this atom injectable into other atoms -/atom/proc/is_injectable(mob/user, allowmobs = TRUE) - return reagents && (container_type & (INJECTABLE | REFILLABLE)) - -/// Can we draw from this atom with an injectable atom -/atom/proc/is_drawable(mob/user, allowmobs = TRUE) - return reagents && (container_type & (DRAWABLE | DRAINABLE)) - -/// Can this atoms reagents be refilled -/atom/proc/is_refillable() - return reagents && (container_type & REFILLABLE) - -/// Is this atom drainable of reagents -/atom/proc/is_drainable() - return reagents && (container_type & DRAINABLE) - -/atom/proc/CheckExit() - return 1 - -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(var/severity) - return - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) - . = P.on_hit(src, 0, def_zone) - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return 1 - else if(src in container) - return 1 - return - -/* - * atom/proc/search_contents_for(path,list/filter_path=null) - * Recursevly searches all atom contens (including contents contents and so on). - * - * ARGS: path - search atom contents for atoms of this type - * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. - * - * RETURNS: list of found atoms - */ - -/atom/proc/search_contents_for(path,list/filter_path=null) - var/list/found = list() - for(var/atom/A in src) - if(istype(A, path)) - found += A - if(filter_path) - var/pass = 0 - for(var/type in filter_path) - pass |= istype(A, type) - if(!pass) - continue - if(A.contents.len) - found += A.search_contents_for(path,filter_path) - return found - - -//All atoms -/atom/proc/examine(mob/user, infix = "", suffix = "") - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src][infix]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - if(blood_color != "#030303") - f_name += "blood-stained [name][infix]!" - else - f_name += "oil-stained [name][infix]." - . = list("[bicon(src)] That's [f_name] [suffix]") - if(desc) - . += desc - - if(reagents) - if(container_type & TRANSPARENT) - . += "It contains:" - if(reagents.reagent_list.len) - if(user.can_see_reagents()) //Show each individual reagent - for(var/I in reagents.reagent_list) - var/datum/reagent/R = I - . += "[R.volume] units of [R.name]" - else //Otherwise, just show the total volume - if(reagents && reagents.reagent_list.len) - . += "[reagents.total_volume] units of various reagents." - else - . += "Nothing." - else if(container_type & AMOUNT_VISIBLE) - if(reagents.total_volume) - . += "It has [reagents.total_volume] unit\s left." - else - . += "It's empty." - - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) - -/atom/proc/relaymove() - return - -/atom/proc/ex_act() - return - -/atom/proc/blob_act(obj/structure/blob/B) - SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) - -/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) - if(reagents) - reagents.temperature_reagents(exposed_temperature) - -/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) - switch(tool_type) - if(TOOL_CROWBAR) - return crowbar_act(user, I) - if(TOOL_MULTITOOL) - return multitool_act(user, I) - if(TOOL_SCREWDRIVER) - return screwdriver_act(user, I) - if(TOOL_WRENCH) - return wrench_act(user, I) - if(TOOL_WIRECUTTER) - return wirecutter_act(user, I) - if(TOOL_WELDER) - return welder_act(user, I) - - -// Tool-specific behavior procs. To be overridden in subtypes. -/atom/proc/crowbar_act(mob/living/user, obj/item/I) - return - -/atom/proc/multitool_act(mob/living/user, obj/item/I) - return - -//Check if the multitool has an item in its data buffer -/atom/proc/multitool_check_buffer(user, silent = FALSE) - if(!silent) - to_chat(user, "[src] has no data buffer!") - return FALSE - -/atom/proc/screwdriver_act(mob/living/user, obj/item/I) - return - -/atom/proc/wrench_act(mob/living/user, obj/item/I) - return - -/atom/proc/wirecutter_act(mob/living/user, obj/item/I) - return - -/atom/proc/welder_act(mob/living/user, obj/item/I) - return - -/atom/proc/emag_act() - return - -/atom/proc/fart_act(mob/living/M) - return FALSE - -/atom/proc/rpd_act() - return - -/atom/proc/rpd_blocksusage() - // Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf. - return FALSE - -/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) - -/atom/proc/hitby_react(atom/movable/AM) - if(AM && isturf(AM.loc)) - step(AM, turn(AM.dir, 180)) - -/atom/proc/get_spooked() - return - -/atom/proc/add_hiddenprint(mob/living/M as mob) - if(isnull(M)) return - if(isnull(M.key)) return - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(!istype(H.dna, /datum/dna)) - return 0 - if(H.gloves) - if(fingerprintslast != H.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) - fingerprintslast = H.ckey - return 0 - if(!fingerprints) - if(fingerprintslast != H.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) - fingerprintslast = H.ckey - return 1 - else - if(fingerprintslast != M.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) - fingerprintslast = M.ckey - return - - -//Set ignoregloves to add prints irrespective of the mob having gloves on. -/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) - if(isnull(M)) return - if(isnull(M.key)) return - if(ishuman(M)) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - - //Fibers~ - add_fibers(M) - - //He has no prints! - if(FINGERPRINTS in M.mutations) - if(fingerprintslast != M.key) - fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" - fingerprintslast = M.key - return 0 //Now, lets get to the dirty work. - //First, make sure their DNA makes sense. - var/mob/living/carbon/human/H = M - if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) - if(!istype(H.dna, /datum/dna)) - H.dna = new /datum/dna(null) - H.dna.real_name = H.real_name - H.check_dna() - - //Check if the gloves (if any) hide fingerprints - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.transfer_prints) - ignoregloves = 1 - - //Now, deal with gloves. - if(!ignoregloves) - if(H.gloves && H.gloves != src) - if(fingerprintslast != H.ckey) - fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.ckey - H.gloves.add_fingerprint(M) - return 0 - - //More adminstuffz - if(fingerprintslast != H.ckey) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.ckey - - //Make the list if it does not exist. - if(!fingerprints) - fingerprints = list() - - //Hash this shit. - var/full_print = H.get_full_print() - - // Add the fingerprints - fingerprints[full_print] = full_print - - return 1 - else - //Smudge up dem prints some - if(fingerprintslast != M.ckey) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) - fingerprintslast = M.ckey - - return - - -/atom/proc/transfer_fingerprints_to(var/atom/A) - - // Make sure everything are lists. - if(!islist(A.fingerprints)) - A.fingerprints = list() - if(!islist(A.fingerprintshidden)) - A.fingerprintshidden = list() - - if(!islist(fingerprints)) - fingerprints = list() - if(!islist(fingerprintshidden)) - fingerprintshidden = list() - - // Transfer - if(fingerprints) - A.fingerprints |= fingerprints.Copy() //detective - if(fingerprintshidden) - A.fingerprintshidden |= fingerprintshidden.Copy() //admin - A.fingerprintslast = fingerprintslast - -var/list/blood_splatter_icons = list() - -/atom/proc/blood_splatter_index() - return "\ref[initial(icon)]-[initial(icon_state)]" - -//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list -/mob/living/proc/get_blood_dna_list() - if(get_blood_id() != "blood") - return - return list("ANIMAL DNA" = "Y-") - -/mob/living/carbon/get_blood_dna_list() - if(get_blood_id() != "blood") - return - var/list/blood_dna = list() - if(dna) - blood_dna[dna.unique_enzymes] = dna.blood_type - else - blood_dna["UNKNOWN DNA"] = "X*" - return blood_dna - -/mob/living/carbon/alien/get_blood_dna_list() - return list("UNKNOWN DNA" = "X*") - -//to add a mob's dna info into an object's blood_DNA list. -/atom/proc/transfer_mob_blood_dna(mob/living/L) - var/new_blood_dna = L.get_blood_dna_list() - if(!new_blood_dna) - return 0 - return transfer_blood_dna(new_blood_dna) - -/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L) - ..(L) - var/list/b_data = L.get_blood_data(L.get_blood_id()) - if(b_data) - basecolor = b_data["blood_color"] - else - basecolor = "#A10808" - update_icon() - -/obj/effect/decal/cleanable/blood/footprints/transfer_mob_blood_dna(mob/living/L) - ..(L) - var/list/b_data = L.get_blood_data(L.get_blood_id()) - if(b_data) - basecolor = b_data["blood_color"] - else - basecolor = "#A10808" - update_icon() - -//to add blood dna info to the object's blood_DNA list -/atom/proc/transfer_blood_dna(list/blood_dna) - if(!blood_DNA) - blood_DNA = list() - var/old_length = blood_DNA.len - blood_DNA |= blood_dna - if(blood_DNA.len > old_length) - return 1//some new blood DNA was added - - -//to add blood from a mob onto something, and transfer their dna info -/atom/proc/add_mob_blood(mob/living/M) - var/list/blood_dna = M.get_blood_dna_list() - if(!blood_dna) - return 0 - var/bloodcolor = "#A10808" - var/list/b_data = M.get_blood_data(M.get_blood_id()) - if(b_data) - bloodcolor = b_data["blood_color"] - - return add_blood(blood_dna, bloodcolor) - -//to add blood onto something, with blood dna info to include. -/atom/proc/add_blood(list/blood_dna, color) - return 0 - -/obj/add_blood(list/blood_dna, color) - return transfer_blood_dna(blood_dna) - -/obj/item/add_blood(list/blood_dna, color) - var/blood_count = !blood_DNA ? 0 : blood_DNA.len - if(!..()) - return 0 - if(!blood_count)//apply the blood-splatter overlay if it isn't already in there - add_blood_overlay(color) - return 1 //we applied blood to the item - -/obj/item/clothing/gloves/add_blood(list/blood_dna, color) - . = ..() - transfer_blood = rand(2, 4) - -/turf/add_blood(list/blood_dna, color) - var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src - if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(src) - B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. - B.basecolor = color - return 1 //we bloodied the floor - -/mob/living/carbon/human/add_blood(list/blood_dna, color) - if(wear_suit) - wear_suit.add_blood(blood_dna, color) - wear_suit.blood_color = color - update_inv_wear_suit(1) - else if(w_uniform) - w_uniform.add_blood(blood_dna, color) - w_uniform.blood_color = color - update_inv_w_uniform(1) - if(head) - head.add_blood(blood_dna, color) - head.blood_color = color - update_inv_head(0,0) - if(glasses) - glasses.add_blood(blood_dna, color) - glasses.blood_color = color - update_inv_glasses(0) - if(gloves) - var/obj/item/clothing/gloves/G = gloves - G.add_blood(blood_dna, color) - G.blood_color = color - verbs += /mob/living/carbon/human/proc/bloody_doodle - else - hand_blood_color = color - bloody_hands = rand(2, 4) - transfer_blood_dna(blood_dna) - verbs += /mob/living/carbon/human/proc/bloody_doodle - - update_inv_gloves(1) //handles bloody hands overlays and updating - return 1 - -/obj/item/proc/add_blood_overlay(color) - if(initial(icon) && initial(icon_state)) - //try to find a pre-processed blood-splatter. otherwise, make a new one - var/index = blood_splatter_index() - var/icon/blood_splatter_icon = blood_splatter_icons[index] - if(!blood_splatter_icon) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon = fcopy_rsc(blood_splatter_icon) - blood_splatter_icons[index] = blood_splatter_icon - - blood_overlay = image(blood_splatter_icon) - blood_overlay.color = color - overlays += blood_overlay - -/atom/proc/clean_blood() - germ_level = 0 - if(islist(blood_DNA)) - blood_DNA = null - return TRUE - -/obj/effect/decal/cleanable/blood/clean_blood() - return // While this seems nonsensical, clean_blood isn't supposed to be used like this on a blood decal. - -/obj/item/clean_blood() - . = ..() - if(.) - if(blood_overlay) - overlays -= blood_overlay - -/obj/item/clothing/gloves/clean_blood() - . = ..() - if(.) - transfer_blood = 0 - - -/obj/item/clothing/shoes/clean_blood() - ..() - bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - blood_state = BLOOD_STATE_NOT_BLOODY - if(ismob(loc)) - var/mob/M = loc - M.update_inv_shoes() - - -/mob/living/carbon/human/clean_blood() - if(gloves) - if(gloves.clean_blood()) - clean_blood() - update_inv_gloves() - gloves.germ_level = 0 - else - ..() // Clear the Blood_DNA list - if(bloody_hands) - bloody_hands = 0 - update_inv_gloves() - update_icons() //apply the now updated overlays to the mob - - -/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE) - playsound(src, 'sound/effects/splat.ogg', 50, 1) - if(!isspaceturf(src)) - var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit - var/vomit_reagent = green ? "green_vomit" : "vomit" - for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src)) - if(V.type == type) - V.reagents.add_reagent(vomit_reagent, 5) - return - - var/obj/effect/decal/cleanable/vomit/this = new type(src) - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - -/atom/proc/get_global_map_pos() - if(!islist(global_map) || isemptylist(global_map)) return - var/cur_x = null - var/cur_y = null - var/list/y_arr = null - for(cur_x=1,cur_x<=global_map.len,cur_x++) - y_arr = global_map[cur_x] - cur_y = y_arr.Find(src.z) - if(cur_y) - break -// to_chat(world, "X = [cur_x]; Y = [cur_y]") - if(cur_x && cur_y) - return list("x"=cur_x,"y"=cur_y) - else - return 0 - -// Used to provide overlays when using this atom as a viewing focus -// (cameras, locker tint, etc.) -/atom/proc/get_remote_view_fullscreens(mob/user) - return - -//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) -/atom/proc/update_remote_sight(mob/living/user) - user.sync_lighting_plane_alpha() - return - -/atom/proc/checkpass(passflag) - return pass_flags&passflag - -/atom/proc/isinspace() - if(isspaceturf(get_turf(src))) - return TRUE - else - return FALSE - -/atom/proc/handle_fall() - return - -/atom/proc/singularity_act() - return - -/atom/proc/singularity_pull(obj/singularity/S, current_size) - SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) - -/** - * Respond to acid being used on our atom - * - * Default behaviour is to send COMSIG_ATOM_ACID_ACT and return - */ -/atom/proc/acid_act(acidpwr, acid_volume) - SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) - -/atom/proc/narsie_act() - return - -/atom/proc/ratvar_act() - return - -/atom/proc/handle_ricochet(obj/item/projectile/P) - return - -//This proc is called on the location of an atom when the atom is Destroy()'d -/atom/proc/handle_atom_del(atom/A) - return - -/atom/proc/atom_say(message) - if(!message) - return - audible_message("[src] [atom_say_verb], \"[message]\"") - -/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - return - -/atom/vv_edit_var(var_name, var_value) - if(!Debug2) - admin_spawned = TRUE - . = ..() - switch(var_name) - if("light_power", "light_range", "light_color") - update_light() - if("color") - add_atom_colour(color, ADMIN_COLOUR_PRIORITY) - - -/atom/vv_get_dropdown() - . = ..() - var/turf/curturf = get_turf(src) - if(curturf) - .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" - .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" - .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" - .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : get_turf(L) - -/atom/Entered(atom/movable/AM, atom/oldLoc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) - -/atom/Exit(atom/movable/AM, atom/newLoc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/newLoc) - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) - -/* - Adds an instance of colour_type to the atom's atom_colours list -*/ -/atom/proc/add_atom_colour(coloration, colour_priority) - if(!atom_colours || !atom_colours.len) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(!coloration) - return - if(colour_priority > atom_colours.len) - return - atom_colours[colour_priority] = coloration - update_atom_colour() - - -/* - Removes an instance of colour_type from the atom's atom_colours list -*/ -/atom/proc/remove_atom_colour(colour_priority, coloration) - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(colour_priority > atom_colours.len) - return - if(coloration && atom_colours[colour_priority] != coloration) - return //if we don't have the expected color (for a specific priority) to remove, do nothing - atom_colours[colour_priority] = null - update_atom_colour() - - -/* - Resets the atom's color to null, and then sets it to the highest priority - colour available -*/ -/atom/proc/update_atom_colour() - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - color = null - for(var/C in atom_colours) - if(islist(C)) - var/list/L = C - if(L.len) - color = L - return - else if(C) - color = C - return \ No newline at end of file +/atom + layer = TURF_LAYER + plane = GAME_PLANE + var/level = 2 + var/flags = NONE + var/flags_2 = NONE + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + var/blood_color + var/last_bumped = 0 + var/pass_flags = 0 + var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. + var/simulated = TRUE //filter for actions - used by lighting overlays + var/atom_say_verb = "says" + var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays + + + ///Chemistry. + var/container_type = NONE + var/datum/reagents/reagents = null + + //This atom's HUD (med/sec, etc) images. Associative list. + var/list/image/hud_list = list() + //HUD images that this atom can provide. + var/list/hud_possible + + ///Chemistry. + + + //Value used to increment ex_act() if reactionary_explosions is on + var/explosion_block = 0 + + //Detective Work, used for the duplicate data points kept in the scanners + var/list/original_atom + + //Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default + var/can_leave_fibers = TRUE + + var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox + + var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. + + var/initialized = FALSE + + var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add + + var/list/atom_colours //used to store the different colors on an atom + //its inherent color, the colored paint applied on it, special color effect etc... + +/atom/New(loc, ...) + if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New() + GLOB._preloader.load(src) + . = ..() + attempt_init(arglist(args)) + +// This is distinct from /tg/ because of our space management system +// This is overriden in /atom/movable and the parent isn't called if the SMS wants to deal with it's init +/atom/proc/attempt_init(...) + var/do_initialize = SSatoms.initialized + if(do_initialize != INITIALIZATION_INSSATOMS) + args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(SSatoms.InitAtom(src, args)) + // we were deleted + return + + +//Called after New if the map is being loaded. mapload = TRUE +//Called from base of New if the map is not being loaded. mapload = FALSE +//This base must be called or derivatives must set initialized to TRUE +//must not sleep +//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm + +//Note: the following functions don't call the base for optimization and must copypasta: +// /turf/Initialize +// /turf/open/space/Initialize + +/atom/proc/Initialize(mapload, ...) + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + + if(color) + add_atom_colour(color, FIXED_COLOUR_PRIORITY) + + if(light_power && light_range) + update_light() + + if(opacity && isturf(loc)) + var/turf/T = loc + T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. + + if(loc) + loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. + + ComponentInitialize() + + return INITIALIZE_HINT_NORMAL + + +//called if Initialize returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +// Put your AddComponent() calls here +/atom/proc/ComponentInitialize() + return + +/atom/proc/InitializedOn(atom/A) // Proc for when something is initialized on a atom - Optional to call. Useful for observer pattern etc. + return + +/atom/proc/onCentcom() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(!is_admin_level(T.z))//if not, don't bother + return 0 + + //check for centcomm shuttles + for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry")) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle) + if(T in M.areaInstance) + return 1 + + //finally check for centcom itself + return istype(T.loc,/area/centcom) + +/atom/proc/onSyndieBase() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(!is_admin_level(T.z))//if not, don't bother + return 0 + + if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership)) + return 1 + + return 0 + +/atom/Destroy() + if(alternate_appearances) + for(var/aakey in alternate_appearances) + var/datum/alternate_appearance/AA = alternate_appearances[aakey] + qdel(AA) + alternate_appearances = null + + QDEL_NULL(reagents) + invisibility = INVISIBILITY_MAXIMUM + LAZYCLEARLIST(overlays) + LAZYCLEARLIST(priority_overlays) + + QDEL_NULL(light) + + return ..() + +//Hook for running code when a dir change occurs +/atom/proc/setDir(newdir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) + dir = newdir + +///Handle melee attack by a mech +/atom/proc/mech_melee_attack(obj/mecha/M) + return + +/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) + if(does_attack_animation) + user.changeNext_move(CLICK_CD_MELEE) + add_attack_logs(user, src, "Punched with hulk powers") + user.do_attack_animation(src, ATTACK_EFFECT_SMASH) + +/atom/proc/CheckParts(list/parts_list) + for(var/A in parts_list) + if(istype(A, /datum/reagent)) + if(!reagents) + reagents = new() + reagents.reagent_list.Add(A) + reagents.conditional_update() + else if(istype(A, /atom/movable)) + var/atom/movable/M = A + if(istype(M.loc, /mob/living)) + var/mob/living/L = M.loc + L.unEquip(M) + M.forceMove(src) + +/atom/proc/assume_air(datum/gas_mixture/giver) + qdel(giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +/atom/proc/check_eye(user as mob) + if(istype(user, /mob/living/silicon/ai)) // WHYYYY + return 1 + return + +/atom/proc/on_reagent_change() + return + +/atom/proc/Bumped(AM as mob|obj) + return + +/// Convenience proc to see if a container is open for chemistry handling +/atom/proc/is_open_container() + return is_refillable() && is_drainable() + +/// Is this atom injectable into other atoms +/atom/proc/is_injectable(mob/user, allowmobs = TRUE) + return reagents && (container_type & (INJECTABLE | REFILLABLE)) + +/// Can we draw from this atom with an injectable atom +/atom/proc/is_drawable(mob/user, allowmobs = TRUE) + return reagents && (container_type & (DRAWABLE | DRAINABLE)) + +/// Can this atoms reagents be refilled +/atom/proc/is_refillable() + return reagents && (container_type & REFILLABLE) + +/// Is this atom drainable of reagents +/atom/proc/is_drainable() + return reagents && (container_type & DRAINABLE) + +/atom/proc/CheckExit() + return 1 + +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(var/severity) + return + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) + . = P.on_hit(src, 0, def_zone) + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return 1 + else if(src in container) + return 1 + return + +/* + * atom/proc/search_contents_for(path,list/filter_path=null) + * Recursevly searches all atom contens (including contents contents and so on). + * + * ARGS: path - search atom contents for atoms of this type + * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. + * + * RETURNS: list of found atoms + */ + +/atom/proc/search_contents_for(path,list/filter_path=null) + var/list/found = list() + for(var/atom/A in src) + if(istype(A, path)) + found += A + if(filter_path) + var/pass = 0 + for(var/type in filter_path) + pass |= istype(A, type) + if(!pass) + continue + if(A.contents.len) + found += A.search_contents_for(path,filter_path) + return found + + +//All atoms +/atom/proc/examine(mob/user, infix = "", suffix = "") + //This reformat names to get a/an properly working on item descriptions when they are bloody + var/f_name = "\a [src][infix]." + if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + if(blood_color != "#030303") + f_name += "blood-stained [name][infix]!" + else + f_name += "oil-stained [name][infix]." + . = list("[bicon(src)] That's [f_name] [suffix]") + if(desc) + . += desc + + if(reagents) + if(container_type & TRANSPARENT) + . += "It contains:" + if(reagents.reagent_list.len) + if(user.can_see_reagents()) //Show each individual reagent + for(var/I in reagents.reagent_list) + var/datum/reagent/R = I + . += "[R.volume] units of [R.name]" + else //Otherwise, just show the total volume + if(reagents && reagents.reagent_list.len) + . += "[reagents.total_volume] units of various reagents." + else + . += "Nothing." + else if(container_type & AMOUNT_VISIBLE) + if(reagents.total_volume) + . += "It has [reagents.total_volume] unit\s left." + else + . += "It's empty." + + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) + +/atom/proc/relaymove() + return + +/atom/proc/ex_act() + return + +/atom/proc/blob_act(obj/structure/blob/B) + SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) + +/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) + if(reagents) + reagents.temperature_reagents(exposed_temperature) + +/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) + switch(tool_type) + if(TOOL_CROWBAR) + return crowbar_act(user, I) + if(TOOL_MULTITOOL) + return multitool_act(user, I) + if(TOOL_SCREWDRIVER) + return screwdriver_act(user, I) + if(TOOL_WRENCH) + return wrench_act(user, I) + if(TOOL_WIRECUTTER) + return wirecutter_act(user, I) + if(TOOL_WELDER) + return welder_act(user, I) + + +// Tool-specific behavior procs. To be overridden in subtypes. +/atom/proc/crowbar_act(mob/living/user, obj/item/I) + return + +/atom/proc/multitool_act(mob/living/user, obj/item/I) + return + +//Check if the multitool has an item in its data buffer +/atom/proc/multitool_check_buffer(user, silent = FALSE) + if(!silent) + to_chat(user, "[src] has no data buffer!") + return FALSE + +/atom/proc/screwdriver_act(mob/living/user, obj/item/I) + return + +/atom/proc/wrench_act(mob/living/user, obj/item/I) + return + +/atom/proc/wirecutter_act(mob/living/user, obj/item/I) + return + +/atom/proc/welder_act(mob/living/user, obj/item/I) + return + +/atom/proc/emag_act() + return + +/atom/proc/fart_act(mob/living/M) + return FALSE + +/atom/proc/rpd_act() + return + +/atom/proc/rpd_blocksusage() + // Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf. + return FALSE + +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) + +/atom/proc/hitby_react(atom/movable/AM) + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) + +/atom/proc/get_spooked() + return + +/atom/proc/add_hiddenprint(mob/living/M as mob) + if(isnull(M)) return + if(isnull(M.key)) return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!istype(H.dna, /datum/dna)) + return 0 + if(H.gloves) + if(fingerprintslast != H.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) + fingerprintslast = H.ckey + return 0 + if(!fingerprints) + if(fingerprintslast != H.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) + fingerprintslast = H.ckey + return 1 + else + if(fingerprintslast != M.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) + fingerprintslast = M.ckey + return + + +//Set ignoregloves to add prints irrespective of the mob having gloves on. +/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) + if(isnull(M)) return + if(isnull(M.key)) return + if(ishuman(M)) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + + //Fibers~ + add_fibers(M) + + //He has no prints! + if(FINGERPRINTS in M.mutations) + if(fingerprintslast != M.key) + fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" + fingerprintslast = M.key + return 0 //Now, lets get to the dirty work. + //First, make sure their DNA makes sense. + var/mob/living/carbon/human/H = M + if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) + if(!istype(H.dna, /datum/dna)) + H.dna = new /datum/dna(null) + H.dna.real_name = H.real_name + H.check_dna() + + //Check if the gloves (if any) hide fingerprints + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.transfer_prints) + ignoregloves = 1 + + //Now, deal with gloves. + if(!ignoregloves) + if(H.gloves && H.gloves != src) + if(fingerprintslast != H.ckey) + fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.ckey + H.gloves.add_fingerprint(M) + return 0 + + //More adminstuffz + if(fingerprintslast != H.ckey) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.ckey + + //Make the list if it does not exist. + if(!fingerprints) + fingerprints = list() + + //Hash this shit. + var/full_print = H.get_full_print() + + // Add the fingerprints + fingerprints[full_print] = full_print + + return 1 + else + //Smudge up dem prints some + if(fingerprintslast != M.ckey) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) + fingerprintslast = M.ckey + + return + + +/atom/proc/transfer_fingerprints_to(var/atom/A) + + // Make sure everything are lists. + if(!islist(A.fingerprints)) + A.fingerprints = list() + if(!islist(A.fingerprintshidden)) + A.fingerprintshidden = list() + + if(!islist(fingerprints)) + fingerprints = list() + if(!islist(fingerprintshidden)) + fingerprintshidden = list() + + // Transfer + if(fingerprints) + A.fingerprints |= fingerprints.Copy() //detective + if(fingerprintshidden) + A.fingerprintshidden |= fingerprintshidden.Copy() //admin + A.fingerprintslast = fingerprintslast + +GLOBAL_LIST_EMPTY(blood_splatter_icons) + +/atom/proc/blood_splatter_index() + return "\ref[initial(icon)]-[initial(icon_state)]" + +//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list +/mob/living/proc/get_blood_dna_list() + if(get_blood_id() != "blood") + return + return list("ANIMAL DNA" = "Y-") + +/mob/living/carbon/get_blood_dna_list() + if(get_blood_id() != "blood") + return + var/list/blood_dna = list() + if(dna) + blood_dna[dna.unique_enzymes] = dna.blood_type + else + blood_dna["UNKNOWN DNA"] = "X*" + return blood_dna + +/mob/living/carbon/alien/get_blood_dna_list() + return list("UNKNOWN DNA" = "X*") + +//to add a mob's dna info into an object's blood_DNA list. +/atom/proc/transfer_mob_blood_dna(mob/living/L) + var/new_blood_dna = L.get_blood_dna_list() + if(!new_blood_dna) + return 0 + return transfer_blood_dna(new_blood_dna) + +/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L) + ..(L) + var/list/b_data = L.get_blood_data(L.get_blood_id()) + if(b_data) + basecolor = b_data["blood_color"] + else + basecolor = "#A10808" + update_icon() + +/obj/effect/decal/cleanable/blood/footprints/transfer_mob_blood_dna(mob/living/L) + ..(L) + var/list/b_data = L.get_blood_data(L.get_blood_id()) + if(b_data) + basecolor = b_data["blood_color"] + else + basecolor = "#A10808" + update_icon() + +//to add blood dna info to the object's blood_DNA list +/atom/proc/transfer_blood_dna(list/blood_dna) + if(!blood_DNA) + blood_DNA = list() + var/old_length = blood_DNA.len + blood_DNA |= blood_dna + if(blood_DNA.len > old_length) + return 1//some new blood DNA was added + + +//to add blood from a mob onto something, and transfer their dna info +/atom/proc/add_mob_blood(mob/living/M) + var/list/blood_dna = M.get_blood_dna_list() + if(!blood_dna) + return 0 + var/bloodcolor = "#A10808" + var/list/b_data = M.get_blood_data(M.get_blood_id()) + if(b_data) + bloodcolor = b_data["blood_color"] + + return add_blood(blood_dna, bloodcolor) + +//to add blood onto something, with blood dna info to include. +/atom/proc/add_blood(list/blood_dna, color) + return 0 + +/obj/add_blood(list/blood_dna, color) + return transfer_blood_dna(blood_dna) + +/obj/item/add_blood(list/blood_dna, color) + var/blood_count = !blood_DNA ? 0 : blood_DNA.len + if(!..()) + return 0 + if(!blood_count)//apply the blood-splatter overlay if it isn't already in there + add_blood_overlay(color) + return 1 //we applied blood to the item + +/obj/item/clothing/gloves/add_blood(list/blood_dna, color) + . = ..() + transfer_blood = rand(2, 4) + +/turf/add_blood(list/blood_dna, color) + var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src + if(!B) + B = new /obj/effect/decal/cleanable/blood/splatter(src) + B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. + B.basecolor = color + return 1 //we bloodied the floor + +/mob/living/carbon/human/add_blood(list/blood_dna, color) + if(wear_suit) + wear_suit.add_blood(blood_dna, color) + wear_suit.blood_color = color + update_inv_wear_suit(1) + else if(w_uniform) + w_uniform.add_blood(blood_dna, color) + w_uniform.blood_color = color + update_inv_w_uniform(1) + if(head) + head.add_blood(blood_dna, color) + head.blood_color = color + update_inv_head(0,0) + if(glasses) + glasses.add_blood(blood_dna, color) + glasses.blood_color = color + update_inv_glasses(0) + if(gloves) + var/obj/item/clothing/gloves/G = gloves + G.add_blood(blood_dna, color) + G.blood_color = color + verbs += /mob/living/carbon/human/proc/bloody_doodle + else + hand_blood_color = color + bloody_hands = rand(2, 4) + transfer_blood_dna(blood_dna) + verbs += /mob/living/carbon/human/proc/bloody_doodle + + update_inv_gloves(1) //handles bloody hands overlays and updating + return 1 + +/obj/item/proc/add_blood_overlay(color) + if(initial(icon) && initial(icon_state)) + //try to find a pre-processed blood-splatter. otherwise, make a new one + var/index = blood_splatter_index() + var/icon/blood_splatter_icon = GLOB.blood_splatter_icons[index] + if(!blood_splatter_icon) + blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + blood_splatter_icon = fcopy_rsc(blood_splatter_icon) + GLOB.blood_splatter_icons[index] = blood_splatter_icon + + blood_overlay = image(blood_splatter_icon) + blood_overlay.color = color + overlays += blood_overlay + +/atom/proc/clean_blood() + germ_level = 0 + if(islist(blood_DNA)) + blood_DNA = null + return TRUE + +/obj/effect/decal/cleanable/blood/clean_blood() + return // While this seems nonsensical, clean_blood isn't supposed to be used like this on a blood decal. + +/obj/item/clean_blood() + . = ..() + if(.) + if(blood_overlay) + overlays -= blood_overlay + +/obj/item/clothing/gloves/clean_blood() + . = ..() + if(.) + transfer_blood = 0 + + +/obj/item/clothing/shoes/clean_blood() + ..() + bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + blood_state = BLOOD_STATE_NOT_BLOODY + if(ismob(loc)) + var/mob/M = loc + M.update_inv_shoes() + + +/mob/living/carbon/human/clean_blood() + if(gloves) + if(gloves.clean_blood()) + clean_blood() + update_inv_gloves() + gloves.germ_level = 0 + else + ..() // Clear the Blood_DNA list + if(bloody_hands) + bloody_hands = 0 + update_inv_gloves() + update_icons() //apply the now updated overlays to the mob + + +/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE) + playsound(src, 'sound/effects/splat.ogg', 50, 1) + if(!isspaceturf(src)) + var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit + var/vomit_reagent = green ? "green_vomit" : "vomit" + for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src)) + if(V.type == type) + V.reagents.add_reagent(vomit_reagent, 5) + return + + var/obj/effect/decal/cleanable/vomit/this = new type(src) + + // Make toxins vomit look different + if(toxvomit) + this.icon_state = "vomittox_[pick(1,4)]" + +/atom/proc/get_global_map_pos() + if(!islist(GLOB.global_map) || isemptylist(GLOB.global_map)) return + var/cur_x = null + var/cur_y = null + var/list/y_arr = null + for(cur_x=1,cur_x<=GLOB.global_map.len,cur_x++) + y_arr = GLOB.global_map[cur_x] + cur_y = y_arr.Find(src.z) + if(cur_y) + break +// to_chat(world, "X = [cur_x]; Y = [cur_y]") + if(cur_x && cur_y) + return list("x"=cur_x,"y"=cur_y) + else + return 0 + +// Used to provide overlays when using this atom as a viewing focus +// (cameras, locker tint, etc.) +/atom/proc/get_remote_view_fullscreens(mob/user) + return + +//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) +/atom/proc/update_remote_sight(mob/living/user) + user.sync_lighting_plane_alpha() + return + +/atom/proc/checkpass(passflag) + return pass_flags&passflag + +/atom/proc/isinspace() + if(isspaceturf(get_turf(src))) + return TRUE + else + return FALSE + +/atom/proc/handle_fall() + return + +/atom/proc/singularity_act() + return + +/atom/proc/singularity_pull(obj/singularity/S, current_size) + SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) + +/** + * Respond to acid being used on our atom + * + * Default behaviour is to send COMSIG_ATOM_ACID_ACT and return + */ +/atom/proc/acid_act(acidpwr, acid_volume) + SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) + +/atom/proc/narsie_act() + return + +/atom/proc/ratvar_act() + return + +/atom/proc/handle_ricochet(obj/item/projectile/P) + return + +//This proc is called on the location of an atom when the atom is Destroy()'d +/atom/proc/handle_atom_del(atom/A) + return + +/atom/proc/atom_say(message) + if(!message) + return + audible_message("[src] [atom_say_verb], \"[message]\"") + +/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + return + +/atom/vv_edit_var(var_name, var_value) + if(!GLOB.debug2) + admin_spawned = TRUE + . = ..() + switch(var_name) + if("light_power", "light_range", "light_color") + update_light() + if("color") + add_atom_colour(color, ADMIN_COLOUR_PRIORITY) + + +/atom/vv_get_dropdown() + . = ..() + var/turf/curturf = get_turf(src) + if(curturf) + .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" + .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" + .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" + .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : get_turf(L) + +/atom/Entered(atom/movable/AM, atom/oldLoc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) + +/atom/Exit(atom/movable/AM, atom/newLoc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/newLoc) + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) + +/* + Adds an instance of colour_type to the atom's atom_colours list +*/ +/atom/proc/add_atom_colour(coloration, colour_priority) + if(!atom_colours || !atom_colours.len) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(!coloration) + return + if(colour_priority > atom_colours.len) + return + atom_colours[colour_priority] = coloration + update_atom_colour() + + +/* + Removes an instance of colour_type from the atom's atom_colours list +*/ +/atom/proc/remove_atom_colour(colour_priority, coloration) + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(colour_priority > atom_colours.len) + return + if(coloration && atom_colours[colour_priority] != coloration) + return //if we don't have the expected color (for a specific priority) to remove, do nothing + atom_colours[colour_priority] = null + update_atom_colour() + + +/* + Resets the atom's color to null, and then sets it to the highest priority + colour available +*/ +/atom/proc/update_atom_colour() + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + color = null + for(var/C in atom_colours) + if(islist(C)) + var/list/L = C + if(L.len) + color = L + return + else if(C) + color = C + return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index e7d642078412..6b49962bbace 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,565 +1,565 @@ -/atom/movable - layer = 3 - appearance_flags = TILE_BOUND - var/last_move = null - var/anchored = 0 - var/move_resist = MOVE_RESIST_DEFAULT - var/move_force = MOVE_FORCE_DEFAULT - var/pull_force = PULL_FORCE_DEFAULT - // var/elevation = 2 - not used anywhere - var/move_speed = 10 - var/l_move_time = 1 - var/datum/thrownthing/throwing = null - var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported - var/throw_range = 7 - var/no_spin = 0 - var/no_spin_thrown = 0 - var/moved_recently = 0 - var/mob/pulledby = null - var/atom/movable/pulling - var/throwforce = 0 - var/canmove = 1 - - var/inertia_dir = 0 - var/atom/inertia_last_loc - var/inertia_moving = 0 - var/inertia_next_move = 0 - var/inertia_move_delay = 5 - - var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move - var/list/client_mobs_in_contents - var/area/areaMaster - -/atom/movable/New() - . = ..() - areaMaster = get_area(src) - -/atom/movable/attempt_init(loc, ...) - var/turf/T = get_turf(src) - if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && space_manager.is_zlevel_dirty(T.z)) - space_manager.postpone_init(T.z, src) - return - . = ..() - -/atom/movable/Destroy() - unbuckle_all_mobs(force = TRUE) - if(loc) - loc.handle_atom_del(src) - for(var/atom/movable/AM in contents) - qdel(AM) - loc = null - if(pulledby) - if(pulledby.pulling == src) - pulledby.pulling = null - pulledby = null - return ..() - -//Returns an atom's power cell, if it has one. Overload for individual items. -/atom/movable/proc/get_cell() - return - -/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) - if(QDELETED(AM)) - return FALSE - if(!(AM.can_be_pulled(src, state, force))) - return FALSE - - // if we're pulling something then drop what we're currently pulling and pull this instead. - if(pulling) - if(state == 0) - stop_pulling() - return FALSE - // Are we trying to pull something we are already pulling? Then enter grab cycle and end. - if(AM == pulling) - if(isliving(AM)) - var/mob/living/AMob = AM - AMob.grabbedby(src) - return TRUE - stop_pulling() - if(AM.pulledby) - add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL) - AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. - pulling = AM - AM.pulledby = src - if(ismob(AM)) - var/mob/M = AM - add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) - if(!supress_message) - visible_message("[src] has grabbed [M] passively!") - return TRUE - -/atom/movable/proc/stop_pulling() - if(pulling) - pulling.pulledby = null - var/mob/living/ex_pulled = pulling - pulling = null - pulledby = null - if(isliving(ex_pulled)) - var/mob/living/L = ex_pulled - L.update_canmove()// mob gets up if it was lyng down in a chokehold - -/atom/movable/proc/check_pulling() - if(pulling) - var/atom/movable/pullee = pulling - if(pullee && get_dist(src, pullee) > 1) - stop_pulling() - return - if(!isturf(loc)) - stop_pulling() - return - if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove(). - log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") - stop_pulling() - return - if(pulling.anchored || pulling.move_resist > move_force) - stop_pulling() - return - if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. - pulledby.stop_pulling() - -/atom/movable/proc/can_be_pulled(user, grab_state, force) - if(src == user || !isturf(loc)) - return FALSE - if(anchored || throwing) - return FALSE - if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) - return FALSE - return TRUE - -// Used in shuttle movement and AI eye stuff. -// Primarily used to notify objects being moved by a shuttle/bluespace fuckup. -/atom/movable/proc/setLoc(var/T, var/teleported=0) - loc = T - -/atom/movable/Move(atom/newloc, direct = 0) - if(!loc || !newloc) return 0 - var/atom/oldloc = loc - - if(loc != newloc) - if(!(direct & (direct - 1))) //Cardinal move - . = ..() - else //Diagonal move, split it into cardinal moves - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a forceMove taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - if(direct & NORTH) - if(direct & EAST) - if(step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if(moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if(direct & WEST) - if(step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if(moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if(direct & SOUTH) - if(direct & EAST) - if(step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if(moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if(direct & WEST) - if(step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if(moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - if(moving_diagonally == SECOND_DIAG_STEP) - if(!.) - setDir(first_step_dir) - else if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(direct) - moving_diagonally = 0 - return - - if(!loc || (loc == oldloc && oldloc != newloc)) - last_move = 0 - return - - if(.) - Moved(oldloc, direct) - - last_move = direct - src.move_speed = world.time - src.l_move_time - src.l_move_time = world.time - - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct)) //movement failed due to buckled mob - . = 0 - -// Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) - if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(Dir) - if(length(client_mobs_in_contents)) - update_parallax_contents() - return TRUE - -// Previously known as HasEntered() -// This is automatically called when something enters your square -/atom/movable/Crossed(atom/movable/AM, oldloc) - SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) - SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src) - -/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). - if(A && yes) - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) - if(throwing) - throwing.hit_atom(A) - . = 1 - if(!A || QDELETED(A)) - return - A.Bumped(src) - -/atom/movable/proc/forceMove(atom/destination) - var/turf/old_loc = loc - loc = destination - moving_diagonally = 0 - - if(old_loc) - old_loc.Exited(src, destination) - for(var/atom/movable/AM in old_loc) - AM.Uncrossed(src) - - if(destination) - destination.Entered(src) - for(var/atom/movable/AM in destination) - if(AM == src) - continue - AM.Crossed(src, old_loc) - var/turf/oldturf = get_turf(old_loc) - var/turf/destturf = get_turf(destination) - var/old_z = (oldturf ? oldturf.z : null) - var/dest_z = (destturf ? destturf.z : null) - if(old_z != dest_z) - onTransitZ(old_z, dest_z) - if(isturf(destination) && opacity) - var/turf/new_loc = destination - new_loc.reconsider_lights() - - if(isturf(old_loc) && opacity) - old_loc.reconsider_lights() - - for(var/datum/light_source/L in light_sources) - L.source_atom.update_light() - - return 1 - -/atom/movable/proc/onTransitZ(old_z,new_z) - for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. - var/atom/movable/AM = item - AM.onTransitZ(old_z,new_z) - -/mob/living/forceMove(atom/destination) - if(buckled) - addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) - if(pulling) - addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) - . = ..() - if(client) - reset_perspective(destination) - update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. - - -//Called whenever an object moves and by mobs when they attempt to move themselves through space -//And when an object or action applies a force on src, see newtonian_move() below -//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting -//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm -//movement_dir == 0 when stopping or any dir when trying to move -/atom/movable/proc/Process_Spacemove(var/movement_dir = 0) - if(has_gravity(src)) - return 1 - - if(pulledby && !pulledby.pulling) - return 1 - - if(throwing) - return 1 - - if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier - return 1 - - return 0 - -/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity - if(!loc || Process_Spacemove(0)) - inertia_dir = 0 - return 0 - - inertia_dir = direction - if(!direction) - return 1 - - inertia_last_loc = loc - SSspacedrift.processing[src] = src - return 1 - - -//called when src is thrown into hit_atom -/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) - set waitfor = 0 - SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) - if(!QDELETED(hit_atom)) - return hit_atom.hitby(src) - -/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum) - if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO)))) - step(src, AM.dir) - ..() - -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) - if(!target || (flags & NODROP) || speed <= 0) - return 0 - - if(pulledby) - pulledby.stop_pulling() - - // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? - if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) - var/user_momentum = thrower.movement_delay() - if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead - user_momentum = world.tick_lag - - user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses - - if(get_dir(thrower, target) & last_move) - user_momentum = user_momentum // basically a noop, but needed - else if(get_dir(target, thrower) & last_move) - user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly - else - user_momentum = 0 - - if(user_momentum) - // first lets add that momentum to range - range *= (user_momentum / speed) + 1 - //then lets add it to speed - speed += user_momentum - if(speed <= 0) - return //no throw speed, the user was moving too fast. - - var/datum/thrownthing/TT = new() - TT.thrownthing = src - TT.target = target - TT.target_turf = get_turf(target) - TT.init_dir = get_dir(src, target) - TT.maxrange = range - TT.speed = speed - TT.thrower = thrower - TT.diagonals_first = diagonals_first - TT.callback = callback - - var/dist_x = abs(target.x - src.x) - var/dist_y = abs(target.y - src.y) - var/dx = (target.x > src.x) ? EAST : WEST - var/dy = (target.y > src.y) ? NORTH : SOUTH - - if(dist_x == dist_y) - TT.pure_diagonal = 1 - - else if(dist_x <= dist_y) - var/olddist_x = dist_x - var/olddx = dx - dist_x = dist_y - dist_y = olddist_x - dx = dy - dy = olddx - TT.dist_x = dist_x - TT.dist_y = dist_y - TT.dx = dx - TT.dy = dy - TT.diagonal_error = dist_x / 2 - dist_y - TT.start_time = world.time - - if(pulledby) - pulledby.stop_pulling() - - throwing = TT - if(spin && !no_spin && !no_spin_thrown) - SpinAnimation(5, 1) - - SSthrowing.processing[src] = TT - TT.tick() - - return TRUE - -//Overlays -/atom/movable/overlay - var/atom/master = null - anchored = TRUE - simulated = FALSE - -/atom/movable/overlay/New() - verbs.Cut() - return - -/atom/movable/overlay/attackby(a, b, c) - if(master) - return master.attackby(a, b, c) - -/atom/movable/overlay/attack_hand(a, b, c) - if(master) - return master.attack_hand(a, b, c) - -/atom/movable/proc/water_act(volume, temperature, source, method = REAGENT_TOUCH) //amount of water acting : temperature of water in kelvin : object that called it (for shennagins) - return TRUE - -/atom/movable/proc/handle_buckled_mob_movement(newloc,direct) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - if(!buckled_mob.Move(newloc, direct)) - forceMove(buckled_mob.loc) - last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move - return 0 - return 1 - -/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) - return FALSE - -/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE) - . = AM.force_pushed(src, force, direction) - if(!silent && .) - visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!") - -/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE) - . = AM.move_crushed(src, force, direction) - if(!silent && .) - visible_message("[src] crushes past [AM]!", "You crush [AM]!") - -/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) - return FALSE - -/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5) - if(mover in buckled_mobs) - return 1 - return ..() - -/atom/movable/proc/get_spacemove_backup() - var/atom/movable/dense_object_backup - for(var/A in orange(1, get_turf(src))) - if(isarea(A)) - continue - else if(isturf(A)) - var/turf/turf = A - if(!turf.density) - continue - return turf - else - var/atom/movable/AM = A - if(!AM.CanPass(src) || AM.density) - if(AM.anchored) - return AM - dense_object_backup = AM - break - . = dense_object_backup - -/atom/movable/proc/transfer_prints_to(atom/movable/target = null, overwrite = FALSE) - if(!target) - return - if(overwrite) - target.fingerprints = fingerprints - target.fingerprintshidden = fingerprintshidden - else - target.fingerprints += fingerprints - target.fingerprintshidden += fingerprintshidden - target.fingerprintslast = fingerprintslast - -/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect && (visual_effect_icon || used_item)) - do_item_attack_animation(A, visual_effect_icon, used_item) - - if(A == src) - return //don't do an animation if attacking self - var/pixel_x_diff = 0 - var/pixel_y_diff = 0 - var/final_pixel_y = initial(pixel_y) - if(end_pixel_y) - final_pixel_y = end_pixel_y - - var/direction = get_dir(src, A) - if(direction & NORTH) - pixel_y_diff = 8 - else if(direction & SOUTH) - pixel_y_diff = -8 - - if(direction & EAST) - pixel_x_diff = 8 - else if(direction & WEST) - pixel_x_diff = -8 - - animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) - animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) - -/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item) - var/image/I - if(visual_effect_icon) - I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) - else if(used_item) - I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) - - // Scale the icon. - I.transform *= 0.75 - // The icon should not rotate. - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - - // Set the direction of the icon animation. - var/direction = get_dir(src, A) - if(direction & NORTH) - I.pixel_y = -16 - else if(direction & SOUTH) - I.pixel_y = 16 - - if(direction & EAST) - I.pixel_x = -16 - else if(direction & WEST) - I.pixel_x = 16 - - if(!direction) // Attacked self?! - I.pixel_z = 16 - - if(!I) - return - - // Who can see the attack? - var/list/viewing = list() - for(var/mob/M in viewers(A)) - if(M.client && M.client.prefs.show_ghostitem_attack) - viewing |= M.client - - flick_overlay(I, viewing, 5) // 5 ticks/half a second - - // And animate the attack! - var/t_color = "#ffffff" - if(ismob(src) && ismob(A) && (!used_item)) - var/mob/M = src - t_color = M.a_intent == INTENT_HARM ? "#ff0000" : "#ffffff" - animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color) - -/atom/movable/proc/portal_destroyed(obj/effect/portal/P) - return +/atom/movable + layer = 3 + appearance_flags = TILE_BOUND + var/last_move = null + var/anchored = 0 + var/move_resist = MOVE_RESIST_DEFAULT + var/move_force = MOVE_FORCE_DEFAULT + var/pull_force = PULL_FORCE_DEFAULT + // var/elevation = 2 - not used anywhere + var/move_speed = 10 + var/l_move_time = 1 + var/datum/thrownthing/throwing = null + var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported + var/throw_range = 7 + var/no_spin = 0 + var/no_spin_thrown = 0 + var/moved_recently = 0 + var/mob/pulledby = null + var/atom/movable/pulling + var/throwforce = 0 + var/canmove = 1 + + var/inertia_dir = 0 + var/atom/inertia_last_loc + var/inertia_moving = 0 + var/inertia_next_move = 0 + var/inertia_move_delay = 5 + + var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move + var/list/client_mobs_in_contents + var/area/areaMaster + +/atom/movable/New() + . = ..() + areaMaster = get_area(src) + +/atom/movable/attempt_init(loc, ...) + var/turf/T = get_turf(src) + if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && GLOB.space_manager.is_zlevel_dirty(T.z)) + GLOB.space_manager.postpone_init(T.z, src) + return + . = ..() + +/atom/movable/Destroy() + unbuckle_all_mobs(force = TRUE) + if(loc) + loc.handle_atom_del(src) + for(var/atom/movable/AM in contents) + qdel(AM) + loc = null + if(pulledby) + if(pulledby.pulling == src) + pulledby.pulling = null + pulledby = null + return ..() + +//Returns an atom's power cell, if it has one. Overload for individual items. +/atom/movable/proc/get_cell() + return + +/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) + if(QDELETED(AM)) + return FALSE + if(!(AM.can_be_pulled(src, state, force))) + return FALSE + + // if we're pulling something then drop what we're currently pulling and pull this instead. + if(pulling) + if(state == 0) + stop_pulling() + return FALSE + // Are we trying to pull something we are already pulling? Then enter grab cycle and end. + if(AM == pulling) + if(isliving(AM)) + var/mob/living/AMob = AM + AMob.grabbedby(src) + return TRUE + stop_pulling() + if(AM.pulledby) + add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL) + AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. + pulling = AM + AM.pulledby = src + if(ismob(AM)) + var/mob/M = AM + add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) + if(!supress_message) + visible_message("[src] has grabbed [M] passively!") + return TRUE + +/atom/movable/proc/stop_pulling() + if(pulling) + pulling.pulledby = null + var/mob/living/ex_pulled = pulling + pulling = null + pulledby = null + if(isliving(ex_pulled)) + var/mob/living/L = ex_pulled + L.update_canmove()// mob gets up if it was lyng down in a chokehold + +/atom/movable/proc/check_pulling() + if(pulling) + var/atom/movable/pullee = pulling + if(pullee && get_dist(src, pullee) > 1) + stop_pulling() + return + if(!isturf(loc)) + stop_pulling() + return + if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove(). + log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") + stop_pulling() + return + if(pulling.anchored || pulling.move_resist > move_force) + stop_pulling() + return + if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. + pulledby.stop_pulling() + +/atom/movable/proc/can_be_pulled(user, grab_state, force) + if(src == user || !isturf(loc)) + return FALSE + if(anchored || throwing) + return FALSE + if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) + return FALSE + return TRUE + +// Used in shuttle movement and AI eye stuff. +// Primarily used to notify objects being moved by a shuttle/bluespace fuckup. +/atom/movable/proc/setLoc(var/T, var/teleported=0) + loc = T + +/atom/movable/Move(atom/newloc, direct = 0) + if(!loc || !newloc) return 0 + var/atom/oldloc = loc + + if(loc != newloc) + if(!(direct & (direct - 1))) //Cardinal move + . = ..() + else //Diagonal move, split it into cardinal moves + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a forceMove taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + if(direct & NORTH) + if(direct & EAST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if(direct & WEST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if(direct & SOUTH) + if(direct & EAST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if(direct & WEST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + if(moving_diagonally == SECOND_DIAG_STEP) + if(!.) + setDir(first_step_dir) + else if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(direct) + moving_diagonally = 0 + return + + if(!loc || (loc == oldloc && oldloc != newloc)) + last_move = 0 + return + + if(.) + Moved(oldloc, direct) + + last_move = direct + src.move_speed = world.time - src.l_move_time + src.l_move_time = world.time + + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct)) //movement failed due to buckled mob + . = 0 + +// Called after a successful Move(). By this point, we've already moved +/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) + if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(Dir) + if(length(client_mobs_in_contents)) + update_parallax_contents() + return TRUE + +// Previously known as HasEntered() +// This is automatically called when something enters your square +/atom/movable/Crossed(atom/movable/AM, oldloc) + SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) + SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src) + +/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). + if(A && yes) + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) + if(throwing) + throwing.hit_atom(A) + . = 1 + if(!A || QDELETED(A)) + return + A.Bumped(src) + +/atom/movable/proc/forceMove(atom/destination) + var/turf/old_loc = loc + loc = destination + moving_diagonally = 0 + + if(old_loc) + old_loc.Exited(src, destination) + for(var/atom/movable/AM in old_loc) + AM.Uncrossed(src) + + if(destination) + destination.Entered(src) + for(var/atom/movable/AM in destination) + if(AM == src) + continue + AM.Crossed(src, old_loc) + var/turf/oldturf = get_turf(old_loc) + var/turf/destturf = get_turf(destination) + var/old_z = (oldturf ? oldturf.z : null) + var/dest_z = (destturf ? destturf.z : null) + if(old_z != dest_z) + onTransitZ(old_z, dest_z) + if(isturf(destination) && opacity) + var/turf/new_loc = destination + new_loc.reconsider_lights() + + if(isturf(old_loc) && opacity) + old_loc.reconsider_lights() + + for(var/datum/light_source/L in light_sources) + L.source_atom.update_light() + + return 1 + +/atom/movable/proc/onTransitZ(old_z,new_z) + for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. + var/atom/movable/AM = item + AM.onTransitZ(old_z,new_z) + +/mob/living/forceMove(atom/destination) + if(buckled) + addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) + if(has_buckled_mobs()) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) + if(pulling) + addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) + . = ..() + if(client) + reset_perspective(destination) + update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. + + +//Called whenever an object moves and by mobs when they attempt to move themselves through space +//And when an object or action applies a force on src, see newtonian_move() below +//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting +//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm +//movement_dir == 0 when stopping or any dir when trying to move +/atom/movable/proc/Process_Spacemove(var/movement_dir = 0) + if(has_gravity(src)) + return 1 + + if(pulledby && !pulledby.pulling) + return 1 + + if(throwing) + return 1 + + if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier + return 1 + + return 0 + +/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity + if(!loc || Process_Spacemove(0)) + inertia_dir = 0 + return 0 + + inertia_dir = direction + if(!direction) + return 1 + + inertia_last_loc = loc + SSspacedrift.processing[src] = src + return 1 + + +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) + set waitfor = 0 + SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) + if(!QDELETED(hit_atom)) + return hit_atom.hitby(src) + +/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum) + if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO)))) + step(src, AM.dir) + ..() + +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) + if(!target || (flags & NODROP) || speed <= 0) + return 0 + + if(pulledby) + pulledby.stop_pulling() + + // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? + if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) + var/user_momentum = thrower.movement_delay() + if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead + user_momentum = world.tick_lag + + user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses + + if(get_dir(thrower, target) & last_move) + user_momentum = user_momentum // basically a noop, but needed + else if(get_dir(target, thrower) & last_move) + user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly + else + user_momentum = 0 + + if(user_momentum) + // first lets add that momentum to range + range *= (user_momentum / speed) + 1 + //then lets add it to speed + speed += user_momentum + if(speed <= 0) + return //no throw speed, the user was moving too fast. + + var/datum/thrownthing/TT = new() + TT.thrownthing = src + TT.target = target + TT.target_turf = get_turf(target) + TT.init_dir = get_dir(src, target) + TT.maxrange = range + TT.speed = speed + TT.thrower = thrower + TT.diagonals_first = diagonals_first + TT.callback = callback + + var/dist_x = abs(target.x - src.x) + var/dist_y = abs(target.y - src.y) + var/dx = (target.x > src.x) ? EAST : WEST + var/dy = (target.y > src.y) ? NORTH : SOUTH + + if(dist_x == dist_y) + TT.pure_diagonal = 1 + + else if(dist_x <= dist_y) + var/olddist_x = dist_x + var/olddx = dx + dist_x = dist_y + dist_y = olddist_x + dx = dy + dy = olddx + TT.dist_x = dist_x + TT.dist_y = dist_y + TT.dx = dx + TT.dy = dy + TT.diagonal_error = dist_x / 2 - dist_y + TT.start_time = world.time + + if(pulledby) + pulledby.stop_pulling() + + throwing = TT + if(spin && !no_spin && !no_spin_thrown) + SpinAnimation(5, 1) + + SSthrowing.processing[src] = TT + TT.tick() + + return TRUE + +//Overlays +/atom/movable/overlay + var/atom/master = null + anchored = TRUE + simulated = FALSE + +/atom/movable/overlay/New() + verbs.Cut() + return + +/atom/movable/overlay/attackby(a, b, c) + if(master) + return master.attackby(a, b, c) + +/atom/movable/overlay/attack_hand(a, b, c) + if(master) + return master.attack_hand(a, b, c) + +/atom/movable/proc/water_act(volume, temperature, source, method = REAGENT_TOUCH) //amount of water acting : temperature of water in kelvin : object that called it (for shennagins) + return TRUE + +/atom/movable/proc/handle_buckled_mob_movement(newloc,direct) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + if(!buckled_mob.Move(newloc, direct)) + forceMove(buckled_mob.loc) + last_move = buckled_mob.last_move + inertia_dir = last_move + buckled_mob.inertia_dir = last_move + return 0 + return 1 + +/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + return FALSE + +/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE) + . = AM.force_pushed(src, force, direction) + if(!silent && .) + visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!") + +/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE) + . = AM.move_crushed(src, force, direction) + if(!silent && .) + visible_message("[src] crushes past [AM]!", "You crush [AM]!") + +/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + return FALSE + +/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5) + if(mover in buckled_mobs) + return 1 + return ..() + +/atom/movable/proc/get_spacemove_backup() + var/atom/movable/dense_object_backup + for(var/A in orange(1, get_turf(src))) + if(isarea(A)) + continue + else if(isturf(A)) + var/turf/turf = A + if(!turf.density) + continue + return turf + else + var/atom/movable/AM = A + if(!AM.CanPass(src) || AM.density) + if(AM.anchored) + return AM + dense_object_backup = AM + break + . = dense_object_backup + +/atom/movable/proc/transfer_prints_to(atom/movable/target = null, overwrite = FALSE) + if(!target) + return + if(overwrite) + target.fingerprints = fingerprints + target.fingerprintshidden = fingerprintshidden + else + target.fingerprints += fingerprints + target.fingerprintshidden += fingerprintshidden + target.fingerprintslast = fingerprintslast + +/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect && (visual_effect_icon || used_item)) + do_item_attack_animation(A, visual_effect_icon, used_item) + + if(A == src) + return //don't do an animation if attacking self + var/pixel_x_diff = 0 + var/pixel_y_diff = 0 + var/final_pixel_y = initial(pixel_y) + if(end_pixel_y) + final_pixel_y = end_pixel_y + + var/direction = get_dir(src, A) + if(direction & NORTH) + pixel_y_diff = 8 + else if(direction & SOUTH) + pixel_y_diff = -8 + + if(direction & EAST) + pixel_x_diff = 8 + else if(direction & WEST) + pixel_x_diff = -8 + + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) + animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) + +/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item) + var/image/I + if(visual_effect_icon) + I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) + else if(used_item) + I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) + + // Scale the icon. + I.transform *= 0.75 + // The icon should not rotate. + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + + // Set the direction of the icon animation. + var/direction = get_dir(src, A) + if(direction & NORTH) + I.pixel_y = -16 + else if(direction & SOUTH) + I.pixel_y = 16 + + if(direction & EAST) + I.pixel_x = -16 + else if(direction & WEST) + I.pixel_x = 16 + + if(!direction) // Attacked self?! + I.pixel_z = 16 + + if(!I) + return + + // Who can see the attack? + var/list/viewing = list() + for(var/mob/M in viewers(A)) + if(M.client && M.client.prefs.show_ghostitem_attack) + viewing |= M.client + + flick_overlay(I, viewing, 5) // 5 ticks/half a second + + // And animate the attack! + var/t_color = "#ffffff" + if(ismob(src) && ismob(A) && (!used_item)) + var/mob/M = src + t_color = M.a_intent == INTENT_HARM ? "#ff0000" : "#ffffff" + animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color) + +/atom/movable/proc/portal_destroyed(obj/effect/portal/P) + return diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 4352e48ffe14..aff345d24515 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -7,10 +7,10 @@ /* DATA HUD DATUMS */ /atom/proc/add_to_all_human_data_huds() - for(var/datum/atom_hud/data/human/hud in huds) hud.add_to_hud(src) + for(var/datum/atom_hud/data/human/hud in GLOB.huds) hud.add_to_hud(src) /atom/proc/remove_from_all_data_huds() - for(var/datum/atom_hud/data/hud in huds) hud.remove_from_hud(src) + for(var/datum/atom_hud/data/hud in GLOB.huds) hud.remove_from_hud(src) /datum/atom_hud/data @@ -142,7 +142,7 @@ //called when a human changes suit sensors /mob/living/carbon/proc/update_suit_sensors() - var/datum/atom_hud/data/human/medical/basic/B = huds[DATA_HUD_MEDICAL_BASIC] + var/datum/atom_hud/data/human/medical/basic/B = GLOB.huds[DATA_HUD_MEDICAL_BASIC] B.update_suit_sensors(src) @@ -220,7 +220,7 @@ var/perpname = get_visible_name(TRUE) //gets the name of the perp, works if they have an id or if their face is uncovered if(!SSticker) return //wait till the game starts or the monkeys runtime.... if(perpname) - var/datum/data/record/R = find_record("name", perpname, data_core.security) + var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security) if(R) switch(R.fields["criminal"]) if("*Execute*") @@ -440,4 +440,4 @@ if(weedlevel < 1) // You don't want to see these icons if the value is small holder.icon_state = "" return - holder.icon_state = "hudweed[RoundPlantBar(weedlevel/10)]" \ No newline at end of file + holder.icon_state = "hudweed[RoundPlantBar(weedlevel/10)]" diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index e6d805296510..ceabae259735 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -4,76 +4,20 @@ * @author N3X15 */ -// What each index means: -#define DNA_OFF_LOWERBOUND 1 // changed as lists start at 1 not 0 -#define DNA_OFF_UPPERBOUND 2 -#define DNA_ON_LOWERBOUND 3 -#define DNA_ON_UPPERBOUND 4 - -// Define block bounds (off-low,off-high,on-low,on-high) -// Used in setupgame.dm -#define DNA_DEFAULT_BOUNDS list(1,2049,2050,4095) -#define DNA_HARDER_BOUNDS list(1,3049,3050,4095) -#define DNA_HARD_BOUNDS list(1,3490,3500,4095) - -// UI Indices (can change to mutblock style, if desired) -#define DNA_UI_HAIR_R 1 -#define DNA_UI_HAIR_G 2 -#define DNA_UI_HAIR_B 3 -#define DNA_UI_HAIR2_R 4 -#define DNA_UI_HAIR2_G 5 -#define DNA_UI_HAIR2_B 6 -#define DNA_UI_BEARD_R 7 -#define DNA_UI_BEARD_G 8 -#define DNA_UI_BEARD_B 9 -#define DNA_UI_BEARD2_R 10 -#define DNA_UI_BEARD2_G 11 -#define DNA_UI_BEARD2_B 12 -#define DNA_UI_SKIN_TONE 13 -#define DNA_UI_SKIN_R 14 -#define DNA_UI_SKIN_G 15 -#define DNA_UI_SKIN_B 16 -#define DNA_UI_HACC_R 17 -#define DNA_UI_HACC_G 18 -#define DNA_UI_HACC_B 19 -#define DNA_UI_HEAD_MARK_R 20 -#define DNA_UI_HEAD_MARK_G 21 -#define DNA_UI_HEAD_MARK_B 22 -#define DNA_UI_BODY_MARK_R 23 -#define DNA_UI_BODY_MARK_G 24 -#define DNA_UI_BODY_MARK_B 25 -#define DNA_UI_TAIL_MARK_R 26 -#define DNA_UI_TAIL_MARK_G 27 -#define DNA_UI_TAIL_MARK_B 28 -#define DNA_UI_EYES_R 29 -#define DNA_UI_EYES_G 30 -#define DNA_UI_EYES_B 31 -#define DNA_UI_GENDER 32 -#define DNA_UI_BEARD_STYLE 33 -#define DNA_UI_HAIR_STYLE 34 -/*#define DNA_UI_BACC_STYLE 23*/ -#define DNA_UI_HACC_STYLE 35 -#define DNA_UI_HEAD_MARK_STYLE 36 -#define DNA_UI_BODY_MARK_STYLE 37 -#define DNA_UI_TAIL_MARK_STYLE 38 -#define DNA_UI_LENGTH 38 // Update this when you add something, or you WILL break shit. - -#define DNA_SE_LENGTH 55 // Was STRUCDNASIZE, size 27. 15 new blocks added = 42, plus room to grow. - // Defines which values mean "on" or "off". // This is to make some of the more OP superpowers a larger PITA to activate, // and to tell our new DNA datum which values to set in order to turn something // on or off. -var/global/list/dna_activity_bounds[DNA_SE_LENGTH] -var/global/list/assigned_gene_blocks[DNA_SE_LENGTH] +GLOBAL_LIST_INIT(dna_activity_bounds, new(DNA_SE_LENGTH)) +GLOBAL_LIST_INIT(assigned_gene_blocks, new(DNA_SE_LENGTH)) // Used to determine what each block means (admin hax and species stuff on /vg/, mostly) -var/global/list/assigned_blocks[DNA_SE_LENGTH] +GLOBAL_LIST_INIT(assigned_blocks, new(DNA_SE_LENGTH)) -var/global/list/datum/dna/gene/dna_genes[0] +GLOBAL_LIST_EMPTY(dna_genes) -var/global/list/good_blocks[0] -var/global/list/bad_blocks[0] +GLOBAL_LIST_EMPTY(good_blocks) +GLOBAL_LIST_EMPTY(bad_blocks) /datum/dna // READ-ONLY, GETS OVERWRITTEN @@ -413,7 +357,7 @@ var/global/list/bad_blocks[0] SE_original = SE.Copy() unique_enzymes = md5(character.real_name) - reg_dna[unique_enzymes] = character.real_name + GLOB.reg_dna[unique_enzymes] = character.real_name // Hmm, I wonder how to go about this without a huge convention break /datum/dna/serialize() diff --git a/code/game/dna/dna2_domutcheck.dm b/code/game/dna/dna2_domutcheck.dm index 07979f40b7ce..0091745c5180 100644 --- a/code/game/dna/dna2_domutcheck.dm +++ b/code/game/dna/dna2_domutcheck.dm @@ -4,7 +4,7 @@ // connected: Machine we're in, type unchecked so I doubt it's used beyond monkeying // flags: See below, bitfield. /proc/domutcheck(var/mob/living/M, var/connected=null, var/flags=0) - for(var/datum/dna/gene/gene in dna_genes) + for(var/datum/dna/gene/gene in GLOB.dna_genes) if(!M || !M.dna) return if(!gene.block) @@ -23,7 +23,7 @@ if(block < 0) return - var/datum/dna/gene/gene = assigned_gene_blocks[block] + var/datum/dna/gene/gene = GLOB.assigned_gene_blocks[block] domutation(gene, M, connected, flags) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index 9ef5dbfcab07..1857848c2c95 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -15,7 +15,7 @@ // DNA Gene activation boundaries, see dna2.dm. // Returns a list object with 4 numbers. /proc/GetDNABounds(var/block) - var/list/BOUNDS=dna_activity_bounds[block] + var/list/BOUNDS=GLOB.dna_activity_bounds[block] if(!istype(BOUNDS)) return DNA_DEFAULT_BOUNDS return BOUNDS @@ -24,14 +24,14 @@ /proc/randmutb(var/mob/living/M) if(!M || !M.dna) return M.dna.check_integrity() - var/block = pick(bad_blocks) + var/block = pick(GLOB.bad_blocks) M.dna.SetSEState(block, 1) // Give Random Good Mutation to M /proc/randmutg(var/mob/living/M) if(!M || !M.dna) return M.dna.check_integrity() - var/block = pick(good_blocks) + var/block = pick(GLOB.good_blocks) M.dna.SetSEState(block, 1) // Random Appearance Mutation diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index ae3539a28679..e1af06f2ff45 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -54,7 +54,7 @@ mutation=HALLUCINATE /datum/dna/gene/disability/hallucinate/New() - block=HALLUCINATIONBLOCK + block=GLOB.hallucinationblock /datum/dna/gene/disability/epilepsy name="Epilepsy" @@ -64,7 +64,7 @@ disability=EPILEPSY /datum/dna/gene/disability/epilepsy/New() - block=EPILEPSYBLOCK + block=GLOB.epilepsyblock /datum/dna/gene/disability/cough name="Coughing" @@ -74,7 +74,7 @@ disability=COUGHING /datum/dna/gene/disability/cough/New() - block=COUGHBLOCK + block=GLOB.coughblock /datum/dna/gene/disability/clumsy name="Clumsiness" @@ -84,7 +84,7 @@ mutation=CLUMSY /datum/dna/gene/disability/clumsy/New() - block=CLUMSYBLOCK + block=GLOB.clumsyblock /datum/dna/gene/disability/tourettes name="Tourettes" @@ -94,7 +94,7 @@ disability=TOURETTES /datum/dna/gene/disability/tourettes/New() - block=TWITCHBLOCK + block=GLOB.twitchblock /datum/dna/gene/disability/nervousness name="Nervousness" @@ -103,7 +103,7 @@ disability=NERVOUS /datum/dna/gene/disability/nervousness/New() - block=NERVOUSBLOCK + block=GLOB.nervousblock /datum/dna/gene/disability/blindness @@ -114,7 +114,7 @@ disability = BLIND /datum/dna/gene/disability/blindness/New() - block = BLINDBLOCK + block = GLOB.blindblock /datum/dna/gene/disability/blindness/activate(mob/M, connected, flags) ..() @@ -133,7 +133,7 @@ disability = COLOURBLIND /datum/dna/gene/disability/colourblindness/New() - block=COLOURBLINDBLOCK + block=GLOB.colourblindblock /datum/dna/gene/disability/colourblindness/activate(var/mob/M, var/connected, var/flags) ..() @@ -153,7 +153,7 @@ disability=DEAF /datum/dna/gene/disability/deaf/New() - block=DEAFBLOCK + block=GLOB.deafblock /datum/dna/gene/disability/deaf/activate(var/mob/M, var/connected, var/flags) ..() @@ -167,7 +167,7 @@ disability=NEARSIGHTED /datum/dna/gene/disability/nearsighted/New() - block=GLASSESBLOCK + block=GLOB.glassesblock /datum/dna/gene/disability/nearsighted/activate(mob/living/M, connected, flags) . = ..() @@ -186,7 +186,7 @@ /datum/dna/gene/disability/lisp/New() ..() - block=LISPBLOCK + block=GLOB.lispblock /datum/dna/gene/disability/lisp/OnSay(var/mob/M, var/message) return replacetext(message,"s","th") @@ -199,7 +199,7 @@ mutation=COMIC /datum/dna/gene/disability/comic/New() - block = COMICBLOCK + block = GLOB.comicblock /datum/dna/gene/disability/wingdings name = "Alien Voice" @@ -210,7 +210,7 @@ mutation = WINGDINGS /datum/dna/gene/disability/wingdings/New() - block = WINGDINGSBLOCK + block = GLOB.wingdingsblock /datum/dna/gene/disability/wingdings/OnSay(var/mob/M, var/message) var/list/chars = string2charlist(message) @@ -223,4 +223,4 @@ else garbled_message += C message = garbled_message - return message \ No newline at end of file + return message diff --git a/code/game/dna/genes/goon_disabilities.dm b/code/game/dna/genes/goon_disabilities.dm index e6c55f02d114..3d794f1a70df 100644 --- a/code/game/dna/genes/goon_disabilities.dm +++ b/code/game/dna/genes/goon_disabilities.dm @@ -17,7 +17,7 @@ /datum/dna/gene/disability/mute/New() ..() - block=MUTEBLOCK + block=GLOB.muteblock /datum/dna/gene/disability/mute/OnSay(var/mob/M, var/message) return "" @@ -36,7 +36,7 @@ /datum/dna/gene/disability/radioactive/New() ..() - block=RADBLOCK + block=GLOB.radblock /datum/dna/gene/disability/radioactive/can_activate(var/mob/M,var/flags) @@ -76,7 +76,7 @@ /datum/dna/gene/disability/fat/New() ..() - block=FATBLOCK + block=GLOB.fatblock // WAS: /datum/bioEffect/chav /datum/dna/gene/disability/speech/chav @@ -88,7 +88,7 @@ /datum/dna/gene/disability/speech/chav/New() ..() - block=CHAVBLOCK + block=GLOB.chavblock /datum/dna/gene/disability/speech/chav/OnSay(var/mob/M, var/message) // THIS ENTIRE THING BEGS FOR REGEX @@ -127,7 +127,7 @@ /datum/dna/gene/disability/speech/swedish/New() ..() - block=SWEDEBLOCK + block=GLOB.swedeblock /datum/dna/gene/disability/speech/swedish/OnSay(var/mob/M, var/message) // svedish @@ -157,7 +157,7 @@ /datum/dna/gene/disability/unintelligable/New() ..() - block=SCRAMBLEBLOCK + block=GLOB.scrambleblock /datum/dna/gene/disability/unintelligable/OnSay(var/mob/M, var/message) var/prefix=copytext(message,1,2) @@ -197,7 +197,7 @@ /datum/dna/gene/disability/strong/New() ..() - block=STRONGBLOCK + block=GLOB.strongblock // WAS: /datum/bioEffect/horns /datum/dna/gene/disability/horns @@ -209,7 +209,7 @@ /datum/dna/gene/disability/horns/New() ..() - block=HORNSBLOCK + block=GLOB.hornsblock /datum/dna/gene/disability/horns/OnDrawUnderlays(var/mob/M,var/g,var/fat) return "horns_s" @@ -227,7 +227,7 @@ /datum/dna/gene/basic/grant_spell/immolate/New() ..() - block = IMMOLATEBLOCK + block = GLOB.immolateblock /obj/effect/proc_holder/spell/targeted/immolate name = "Incendiary Mitochondria" diff --git a/code/game/dna/genes/goon_powers.dm b/code/game/dna/genes/goon_powers.dm index f49a250b0fa4..fd2f300e8fb7 100644 --- a/code/game/dna/genes/goon_powers.dm +++ b/code/game/dna/genes/goon_powers.dm @@ -9,7 +9,7 @@ mutation=SOBER /datum/dna/gene/basic/sober/New() - block=SOBERBLOCK + block=GLOB.soberblock //WAS: /datum/bioEffect/psychic_resist /datum/dna/gene/basic/psychic_resist @@ -21,7 +21,7 @@ mutation=PSY_RESIST /datum/dna/gene/basic/psychic_resist/New() - block=PSYRESISTBLOCK + block=GLOB.psyresistblock ///////////////////////// // Stealth Enhancers @@ -51,7 +51,7 @@ mutation = CLOAK /datum/dna/gene/basic/stealth/darkcloak/New() - block=SHADOWBLOCK + block=GLOB.shadowblock /datum/dna/gene/basic/stealth/darkcloak/OnMobLife(var/mob/M) var/turf/simulated/T = get_turf(M) @@ -73,7 +73,7 @@ mutation = CHAMELEON /datum/dna/gene/basic/stealth/chameleon/New() - block=CHAMELEONBLOCK + block=GLOB.chameleonblock /datum/dna/gene/basic/stealth/chameleon/OnMobLife(var/mob/M) if((world.time - M.last_movement) >= 30 && !M.stat && M.canmove && !M.restrained()) @@ -123,7 +123,7 @@ /datum/dna/gene/basic/grant_spell/cryo/New() ..() - block = CRYOBLOCK + block = GLOB.cryoblock /obj/effect/proc_holder/spell/targeted/cryokinesis name = "Cryokinesis" @@ -215,7 +215,7 @@ /datum/dna/gene/basic/grant_spell/mattereater/New() ..() - block = EATBLOCK + block = GLOB.eatblock /obj/effect/proc_holder/spell/targeted/eat name = "Eat" @@ -368,7 +368,7 @@ /datum/dna/gene/basic/grant_spell/jumpy/New() ..() - block = JUMPBLOCK + block = GLOB.jumpblock /obj/effect/proc_holder/spell/targeted/leap name = "Jump" @@ -463,7 +463,7 @@ /datum/dna/gene/basic/grant_spell/polymorph/New() ..() - block = POLYMORPHBLOCK + block = GLOB.polymorphblock /obj/effect/proc_holder/spell/targeted/polymorph name = "Polymorph" @@ -512,7 +512,7 @@ /datum/dna/gene/basic/grant_spell/empath/New() ..() - block = EMPATHBLOCK + block = GLOB.empathblock /obj/effect/proc_holder/spell/targeted/empath name = "Read Mind" diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 97b5eff3be2e..3b6182ecf63b 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -2,7 +2,7 @@ name="Monkey" /datum/dna/gene/monkey/New() - block=MONKEYBLOCK + block=GLOB.monkeyblock /datum/dna/gene/monkey/can_activate(var/mob/M,var/flags) return ishuman(M) @@ -79,4 +79,4 @@ to_chat(H, "You are now a [H.dna.species.name].") - return H \ No newline at end of file + return H diff --git a/code/game/dna/genes/powers.dm b/code/game/dna/genes/powers.dm index f8b231372dc5..b0a99e05ddd2 100644 --- a/code/game/dna/genes/powers.dm +++ b/code/game/dna/genes/powers.dm @@ -11,7 +11,7 @@ activation_prob=25 /datum/dna/gene/basic/nobreath/New() - block = BREATHLESSBLOCK + block = GLOB.breathlessblock /datum/dna/gene/basic/regenerate @@ -22,7 +22,7 @@ mutation=REGEN /datum/dna/gene/basic/regenerate/New() - block=REGENERATEBLOCK + block=GLOB.regenerateblock /datum/dna/gene/basic/increaserun name="Super Speed" @@ -32,7 +32,7 @@ mutation=RUN /datum/dna/gene/basic/increaserun/New() - block=INCREASERUNBLOCK + block=GLOB.increaserunblock /datum/dna/gene/basic/increaserun/can_activate(var/mob/M,var/flags) if(!..()) @@ -51,7 +51,7 @@ mutation = HEATRES /datum/dna/gene/basic/heat_resist/New() - block=COLDBLOCK + block=GLOB.coldblock /datum/dna/gene/basic/heat_resist/OnDrawUnderlays(var/mob/M,var/g,var/fat) return "cold[fat]_s" @@ -64,7 +64,7 @@ mutation = COLDRES /datum/dna/gene/basic/cold_resist/New() - block=FIREBLOCK + block=GLOB.fireblock /datum/dna/gene/basic/cold_resist/OnDrawUnderlays(var/mob/M,var/g,var/fat) return "fire[fat]_s" @@ -77,7 +77,7 @@ mutation=FINGERPRINTS /datum/dna/gene/basic/noprints/New() - block=NOPRINTSBLOCK + block=GLOB.noprintsblock /datum/dna/gene/basic/noshock name="Shock Immunity" @@ -87,7 +87,7 @@ mutation=NO_SHOCK /datum/dna/gene/basic/noshock/New() - block=SHOCKIMMUNITYBLOCK + block=GLOB.shockimmunityblock /datum/dna/gene/basic/midget name="Midget" @@ -97,7 +97,7 @@ mutation=DWARF /datum/dna/gene/basic/midget/New() - block=SMALLSIZEBLOCK + block=GLOB.smallsizeblock /datum/dna/gene/basic/midget/activate(var/mob/M, var/connected, var/flags) ..(M,connected,flags) @@ -121,7 +121,7 @@ activation_prob=15 /datum/dna/gene/basic/hulk/New() - block=HULKBLOCK + block=GLOB.hulkblock /datum/dna/gene/basic/hulk/activate(var/mob/M, var/connected, var/flags) ..() @@ -145,8 +145,8 @@ return if((HULK in M.mutations) && M.health <= 0) M.mutations.Remove(HULK) - M.dna.SetSEState(HULKBLOCK,0) - genemutcheck(M, HULKBLOCK,null,MUTCHK_FORCED) + M.dna.SetSEState(GLOB.hulkblock,0) + genemutcheck(M, GLOB.hulkblock,null,MUTCHK_FORCED) M.update_mutations() //update our mutation overlays M.update_body() M.status_flags |= CANSTUN | CANWEAKEN | CANPARALYSE | CANPUSH //temporary fix until the problem can be solved. @@ -161,7 +161,7 @@ activation_prob=15 /datum/dna/gene/basic/xray/New() - block=XRAYBLOCK + block=GLOB.xrayblock /datum/dna/gene/basic/xray/activate(mob/living/M, connected, flags) ..() @@ -182,7 +182,7 @@ activation_prob=15 /datum/dna/gene/basic/tk/New() - block=TELEBLOCK + block=GLOB.teleblock /datum/dna/gene/basic/tk/OnDrawUnderlays(var/mob/M,var/g,var/fat) return "telekinesishead[fat]_s" diff --git a/code/game/dna/genes/vg_disabilities.dm b/code/game/dna/genes/vg_disabilities.dm index 79f1ca16411c..d423f0bf89bd 100644 --- a/code/game/dna/genes/vg_disabilities.dm +++ b/code/game/dna/genes/vg_disabilities.dm @@ -8,7 +8,7 @@ /datum/dna/gene/disability/speech/loud/New() ..() - block=LOUDBLOCK + block=GLOB.loudblock @@ -28,7 +28,7 @@ /datum/dna/gene/disability/dizzy/New() ..() - block=DIZZYBLOCK + block=GLOB.dizzyblock /datum/dna/gene/disability/dizzy/OnMobLife(var/mob/living/carbon/human/M) diff --git a/code/game/dna/genes/vg_powers.dm b/code/game/dna/genes/vg_powers.dm index 506ef5714f8b..5ed6c705a17f 100644 --- a/code/game/dna/genes/vg_powers.dm +++ b/code/game/dna/genes/vg_powers.dm @@ -11,7 +11,7 @@ /datum/dna/gene/basic/grant_spell/morph/New() ..() - block = MORPHBLOCK + block = GLOB.morphblock /obj/effect/proc_holder/spell/targeted/morph name = "Morph" @@ -186,7 +186,7 @@ /datum/dna/gene/basic/grant_spell/remotetalk/New() ..() - block=REMOTETALKBLOCK + block=GLOB.remotetalkblock /datum/dna/gene/basic/grant_spell/remotetalk/activate(mob/user) ..() @@ -345,7 +345,7 @@ spelltype =/obj/effect/proc_holder/spell/targeted/remoteview /datum/dna/gene/basic/grant_spell/remoteview/New() - block=REMOTEVIEWBLOCK + block=GLOB.remoteviewblock /obj/effect/proc_holder/spell/targeted/remoteview diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index 33b68f940790..a11e5d922bac 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -1,211 +1,211 @@ -//Few global vars to track the blob -var/list/blobs = list() -var/list/blob_cores = list() -var/list/blob_nodes = list() - -/datum/game_mode - var/list/blob_overminds = list() - -/datum/game_mode/blob - name = "blob" - config_tag = "blob" - - required_players = 30 - required_enemies = 1 - recommended_enemies = 1 - restricted_jobs = list("Cyborg", "AI") - - var/declared = 0 - var/burst = 0 - - var/cores_to_spawn = 1 - var/players_per_core = 30 - var/blob_point_rate = 3 - - var/blobwincount = 350 - - var/list/infected_crew = list() - -/datum/game_mode/blob/pre_setup() - - var/list/possible_blobs = get_players_for_role(ROLE_BLOB) - - // stop setup if no possible traitors - if(!possible_blobs.len) - return 0 - - cores_to_spawn = max(round(num_players()/players_per_core, 1), 1) - - blobwincount = initial(blobwincount) * cores_to_spawn - - - for(var/j = 0, j < cores_to_spawn, j++) - if(!possible_blobs.len) - break - - var/datum/mind/blob = pick(possible_blobs) - infected_crew += blob - blob.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blob) - blob.restricted_roles = restricted_jobs - log_game("[key_name(blob)] has been selected as a Blob") - possible_blobs -= blob - - if(!infected_crew.len) - return 0 - ..() - return 1 - -/datum/game_mode/blob/proc/get_blob_candidates() - var/list/candidates = list() - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!player.stat && player.mind && !player.client.skip_antag && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special)) - candidates += player - return candidates - - -/datum/game_mode/blob/proc/blobize(var/mob/living/carbon/human/blob) - var/datum/mind/blobmind = blob.mind - if(!istype(blobmind)) - return 0 - - infected_crew += blobmind - blobmind.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blobmind) - - log_game("[key_name(blob)] has been selected as a Blob") - greet_blob(blobmind) - to_chat(blob, "You feel very tired and bloated! You don't have long before you burst!") - spawn(600) - burst_blob(blobmind) - return 1 - -/datum/game_mode/blob/proc/make_blobs(var/count) - var/list/candidates = get_blob_candidates() - var/mob/living/carbon/human/blob = null - count=min(count, candidates.len) - for(var/i = 0, i < count, i++) - blob = pick(candidates) - candidates -= blob - blobize(blob) - return count - - - -/datum/game_mode/blob/announce() - to_chat(world, "The current game mode is - Blob!") - to_chat(world, "A dangerous alien organism is rapidly spreading throughout the station!") - to_chat(world, "You must kill it all while minimizing the damage to the station.") - - -/datum/game_mode/blob/proc/greet_blob(var/datum/mind/blob) - to_chat(blob.current, "You are infected by the Blob!") - to_chat(blob.current, "Your body is ready to give spawn to a new blob core which will eat this station.") - to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") - to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") - to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") - SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') - return - -/datum/game_mode/blob/proc/show_message(var/message) - for(var/datum/mind/blob in infected_crew) - to_chat(blob.current, message) - -/datum/game_mode/blob/proc/burst_blobs() - for(var/datum/mind/blob in infected_crew) - burst_blob(blob) - -/datum/game_mode/blob/proc/burst_blob(var/datum/mind/blob, var/warned=0) - var/client/blob_client = null - var/turf/location = null - - if(iscarbon(blob.current)) - var/mob/living/carbon/C = blob.current - if(GLOB.directory[ckey(blob.key)]) - blob_client = GLOB.directory[ckey(blob.key)] - location = get_turf(C) - if(!is_station_level(location.z) || istype(location, /turf/space)) - if(!warned) - to_chat(C, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - message_admins("[key_name_admin(C)] was in space when the blobs burst, and will die if [C.p_they()] [C.p_do()] not return to the station.") - spawn(300) - burst_blob(blob, 1) - else - burst++ - log_admin("[key_name(C)] was in space when attempting to burst as a blob.") - message_admins("[key_name_admin(C)] was in space when attempting to burst as a blob.") - C.gib() - make_blobs(1) - check_finished() //Still needed in case we can't make any blobs - - else if(blob_client && location) - burst++ - C.gib() - var/obj/structure/blob/core/core = new(location, 200, blob_client, blob_point_rate) - if(core.overmind && core.overmind.mind) - core.overmind.mind.name = blob.name - infected_crew -= blob - infected_crew += core.overmind.mind - core.overmind.mind.special_role = SPECIAL_ROLE_BLOB_OVERMIND - -/datum/game_mode/blob/post_setup() - - for(var/datum/mind/blob in infected_crew) - greet_blob(blob) - - if(SSshuttle) - SSshuttle.emergencyNoEscape = 1 - - spawn(0) - - var/wait_time = rand(waittime_l, waittime_h) - - sleep(wait_time) - - send_intercept(0) - - sleep(100) - - show_message("You feel tired and bloated.") - - sleep(wait_time) - - show_message("You feel like you are about to burst.") - - sleep(wait_time / 2) - - burst_blobs() - - // Stage 0 - sleep(wait_time) - stage(0) - - // Stage 1 - sleep(wait_time) - stage(1) - - // Stage 2 - sleep(30000) - stage(2) - - return ..() - -/datum/game_mode/blob/proc/stage(var/stage) - switch(stage) - if(0) - send_intercept(1) - declared = 1 - if(1) - event_announcement.Announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg') - if(2) - send_intercept(2) - -/datum/game_mode/proc/update_blob_icons_added(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] - antaghud.join_hud(mob_mind.current) - set_antag_hud(mob_mind.current, "hudblob") - -/datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] - antaghud.leave_hud(mob_mind.current) - set_antag_hud(mob_mind.current, null) +//Few global vars to track the blob +GLOBAL_LIST_EMPTY(blobs) +GLOBAL_LIST_EMPTY(blob_cores) +GLOBAL_LIST_EMPTY(blob_nodes) + +/datum/game_mode + var/list/blob_overminds = list() + +/datum/game_mode/blob + name = "blob" + config_tag = "blob" + + required_players = 30 + required_enemies = 1 + recommended_enemies = 1 + restricted_jobs = list("Cyborg", "AI") + + var/declared = 0 + var/burst = 0 + + var/cores_to_spawn = 1 + var/players_per_core = 30 + var/blob_point_rate = 3 + + var/blobwincount = 350 + + var/list/infected_crew = list() + +/datum/game_mode/blob/pre_setup() + + var/list/possible_blobs = get_players_for_role(ROLE_BLOB) + + // stop setup if no possible traitors + if(!possible_blobs.len) + return 0 + + cores_to_spawn = max(round(num_players()/players_per_core, 1), 1) + + blobwincount = initial(blobwincount) * cores_to_spawn + + + for(var/j = 0, j < cores_to_spawn, j++) + if(!possible_blobs.len) + break + + var/datum/mind/blob = pick(possible_blobs) + infected_crew += blob + blob.special_role = SPECIAL_ROLE_BLOB + update_blob_icons_added(blob) + blob.restricted_roles = restricted_jobs + log_game("[key_name(blob)] has been selected as a Blob") + possible_blobs -= blob + + if(!infected_crew.len) + return 0 + ..() + return 1 + +/datum/game_mode/blob/proc/get_blob_candidates() + var/list/candidates = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!player.stat && player.mind && !player.client.skip_antag && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special)) + candidates += player + return candidates + + +/datum/game_mode/blob/proc/blobize(var/mob/living/carbon/human/blob) + var/datum/mind/blobmind = blob.mind + if(!istype(blobmind)) + return 0 + + infected_crew += blobmind + blobmind.special_role = SPECIAL_ROLE_BLOB + update_blob_icons_added(blobmind) + + log_game("[key_name(blob)] has been selected as a Blob") + greet_blob(blobmind) + to_chat(blob, "You feel very tired and bloated! You don't have long before you burst!") + spawn(600) + burst_blob(blobmind) + return 1 + +/datum/game_mode/blob/proc/make_blobs(var/count) + var/list/candidates = get_blob_candidates() + var/mob/living/carbon/human/blob = null + count=min(count, candidates.len) + for(var/i = 0, i < count, i++) + blob = pick(candidates) + candidates -= blob + blobize(blob) + return count + + + +/datum/game_mode/blob/announce() + to_chat(world, "The current game mode is - Blob!") + to_chat(world, "A dangerous alien organism is rapidly spreading throughout the station!") + to_chat(world, "You must kill it all while minimizing the damage to the station.") + + +/datum/game_mode/blob/proc/greet_blob(var/datum/mind/blob) + to_chat(blob.current, "You are infected by the Blob!") + to_chat(blob.current, "Your body is ready to give spawn to a new blob core which will eat this station.") + to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") + to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") + to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") + SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') + return + +/datum/game_mode/blob/proc/show_message(var/message) + for(var/datum/mind/blob in infected_crew) + to_chat(blob.current, message) + +/datum/game_mode/blob/proc/burst_blobs() + for(var/datum/mind/blob in infected_crew) + burst_blob(blob) + +/datum/game_mode/blob/proc/burst_blob(var/datum/mind/blob, var/warned=0) + var/client/blob_client = null + var/turf/location = null + + if(iscarbon(blob.current)) + var/mob/living/carbon/C = blob.current + if(GLOB.directory[ckey(blob.key)]) + blob_client = GLOB.directory[ckey(blob.key)] + location = get_turf(C) + if(!is_station_level(location.z) || istype(location, /turf/space)) + if(!warned) + to_chat(C, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + message_admins("[key_name_admin(C)] was in space when the blobs burst, and will die if [C.p_they()] [C.p_do()] not return to the station.") + spawn(300) + burst_blob(blob, 1) + else + burst++ + log_admin("[key_name(C)] was in space when attempting to burst as a blob.") + message_admins("[key_name_admin(C)] was in space when attempting to burst as a blob.") + C.gib() + make_blobs(1) + check_finished() //Still needed in case we can't make any blobs + + else if(blob_client && location) + burst++ + C.gib() + var/obj/structure/blob/core/core = new(location, 200, blob_client, blob_point_rate) + if(core.overmind && core.overmind.mind) + core.overmind.mind.name = blob.name + infected_crew -= blob + infected_crew += core.overmind.mind + core.overmind.mind.special_role = SPECIAL_ROLE_BLOB_OVERMIND + +/datum/game_mode/blob/post_setup() + + for(var/datum/mind/blob in infected_crew) + greet_blob(blob) + + if(SSshuttle) + SSshuttle.emergencyNoEscape = 1 + + spawn(0) + + var/wait_time = rand(waittime_l, waittime_h) + + sleep(wait_time) + + send_intercept(0) + + sleep(100) + + show_message("You feel tired and bloated.") + + sleep(wait_time) + + show_message("You feel like you are about to burst.") + + sleep(wait_time / 2) + + burst_blobs() + + // Stage 0 + sleep(wait_time) + stage(0) + + // Stage 1 + sleep(wait_time) + stage(1) + + // Stage 2 + sleep(30000) + stage(2) + + return ..() + +/datum/game_mode/blob/proc/stage(var/stage) + switch(stage) + if(0) + send_intercept(1) + declared = 1 + if(1) + GLOB.event_announcement.Announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg') + if(2) + send_intercept(2) + +/datum/game_mode/proc/update_blob_icons_added(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_BLOB] + antaghud.join_hud(mob_mind.current) + set_antag_hud(mob_mind.current, "hudblob") + +/datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_BLOB] + antaghud.leave_hud(mob_mind.current) + set_antag_hud(mob_mind.current, null) diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm index a00449d00b5c..b1ef70f6b4f7 100644 --- a/code/game/gamemodes/blob/blob_finish.dm +++ b/code/game/gamemodes/blob/blob_finish.dm @@ -1,42 +1,42 @@ -/datum/game_mode/blob/check_finished() - if(infected_crew.len > burst)//Some blobs have yet to burst - return 0 - if(blobwincount <= blobs.len)//Blob took over - return 1 - if(!blob_cores.len) // blob is dead - return 1 - return ..() - - -/datum/game_mode/blob/declare_completion() - if(blobwincount <= blobs.len) - feedback_set_details("round_end_result","blob win - blob took over") - to_chat(world, "The blob has taken over the station!") - to_chat(world, "The entire station was eaten by the Blob") - log_game("Blob mode completed with a blob victory.") - - else if(station_was_nuked) - feedback_set_details("round_end_result","blob halfwin - nuke") - to_chat(world, "Partial Win: The station has been destroyed!") - to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") - log_game("Blob mode completed with a tie (station destroyed).") - - else if(!blob_cores.len) - feedback_set_details("round_end_result","blob loss - blob eliminated") - to_chat(world, "The staff has won!") - to_chat(world, "The alien organism has been eradicated from the station") - log_game("Blob mode completed with a crew victory.") - to_chat(world, "Rebooting in 30s") - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_blob() - if(GAMEMODE_IS_BLOB) - var/datum/game_mode/blob/blob_mode = src - if(blob_mode.infected_crew.len) - var/text = "The blob[(blob_mode.infected_crew.len > 1 ? "s were" : " was")]:" - - for(var/datum/mind/blob in blob_mode.infected_crew) - text += "
    [blob.key] was [blob.name]" - to_chat(world, text) - return 1 +/datum/game_mode/blob/check_finished() + if(infected_crew.len > burst)//Some blobs have yet to burst + return 0 + if(blobwincount <= GLOB.blobs.len)//Blob took over + return 1 + if(!GLOB.blob_cores.len) // blob is dead + return 1 + return ..() + + +/datum/game_mode/blob/declare_completion() + if(blobwincount <= GLOB.blobs.len) + feedback_set_details("round_end_result","blob win - blob took over") + to_chat(world, "The blob has taken over the station!") + to_chat(world, "The entire station was eaten by the Blob") + log_game("Blob mode completed with a blob victory.") + + else if(station_was_nuked) + feedback_set_details("round_end_result","blob halfwin - nuke") + to_chat(world, "Partial Win: The station has been destroyed!") + to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") + log_game("Blob mode completed with a tie (station destroyed).") + + else if(!GLOB.blob_cores.len) + feedback_set_details("round_end_result","blob loss - blob eliminated") + to_chat(world, "The staff has won!") + to_chat(world, "The alien organism has been eradicated from the station") + log_game("Blob mode completed with a crew victory.") + to_chat(world, "Rebooting in 30s") + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_blob() + if(GAMEMODE_IS_BLOB) + var/datum/game_mode/blob/blob_mode = src + if(blob_mode.infected_crew.len) + var/text = "The blob[(blob_mode.infected_crew.len > 1 ? "s were" : " was")]:" + + for(var/datum/mind/blob in blob_mode.infected_crew) + text += "
    [blob.key] was [blob.name]" + to_chat(world, text) + return 1 diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 53a98de70289..893c2e0e8ac2 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -1,103 +1,103 @@ -/datum/game_mode/blob/proc/send_intercept(var/report = 1) - var/intercepttext = "" - var/interceptname = "" - switch(report) - if(0) - ..() - return - if(1) - interceptname = "Level 5-6 Biohazard Response Procedures" - intercepttext += "Nanotrasen Update: Biohazard Alert.
    " - intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.
    " - intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. Its origin is unknown.
    " - intercepttext += "Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.
    " - intercepttext += "Orders for all [station_name()] personnel follows:
    " - intercepttext += " 1. Do not leave the quarantine area.
    " - intercepttext += " 2. Locate any outbreaks of the organism on the station.
    " - intercepttext += " 3. If found, use any neccesary means to contain the organism.
    " - intercepttext += " 4. Avoid damage to the capital infrastructure of the station.
    " - intercepttext += "
    Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
    " - intercepttext += "Message ends." - if(2) - var/nukecode = rand(10000, 99999) - for(var/obj/machinery/nuclearbomb/bomb in world) - if(bomb && bomb.r_code) - if(is_station_level(bomb.z)) - bomb.r_code = nukecode - - interceptname = "Classified [command_name()] Update" - intercepttext += "Nanotrasen Update: Biohazard Alert.
    " - intercepttext += "Directive 7-12 has been issued for [station_name()].
    " - intercepttext += "The biohazard has grown out of control and will soon reach critical mass.
    " - intercepttext += "Your orders are as follows:
    " - intercepttext += "1. Secure the Nuclear Authentication Disk.
    " - intercepttext += "2. Detonate the Nuke located in the Station's Vault.
    " - intercepttext += "Nuclear Authentication Code: [nukecode]
    " - intercepttext += "Message ends." - - for(var/mob/living/silicon/ai/aiPlayer in GLOB.player_list) - if(aiPlayer.client) - var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]." - aiPlayer.set_zeroth_law(law) - to_chat(aiPlayer, "Laws Updated: [law]") - - print_command_report(intercepttext, interceptname) - event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") - -/datum/station_state - var/floor = 0 - var/wall = 0 - var/r_wall = 0 - var/window = 0 - var/door = 0 - var/grille = 0 - var/mach = 0 - - -/datum/station_state/proc/count() - for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1))) - - if(istype(T,/turf/simulated/floor)) - if(!(T:burnt)) - src.floor += 12 - else - src.floor += 1 - - if(istype(T, /turf/simulated/wall)) - var/turf/simulated/wall/W = T - if(W.intact) - src.wall += 2 - else - src.wall += 1 - - if(istype(T, /turf/simulated/wall/r_wall)) - var/turf/simulated/wall/r_wall/R = T - if(R.intact) - src.r_wall += 2 - else - src.r_wall += 1 - - - for(var/obj/O in T.contents) - if(istype(O, /obj/structure/window)) - src.window += 1 - else if(istype(O, /obj/structure/grille)) - var/obj/structure/grille/GR = O - if(!GR.broken) - grille += 1 - else if(istype(O, /obj/machinery/door)) - src.door += 1 - else if(istype(O, /obj/machinery)) - src.mach += 1 - -/datum/station_state/proc/score(var/datum/station_state/result) - if(!result) return 0 - var/output = 0 - output += (result.floor / max(floor,1)) - output += (result.r_wall/ max(r_wall,1)) - output += (result.wall / max(wall,1)) - output += (result.window / max(window,1)) - output += (result.door / max(door,1)) - output += (result.grille / max(grille,1)) - output += (result.mach / max(mach,1)) - return (output/7) +/datum/game_mode/blob/proc/send_intercept(var/report = 1) + var/intercepttext = "" + var/interceptname = "" + switch(report) + if(0) + ..() + return + if(1) + interceptname = "Level 5-6 Biohazard Response Procedures" + intercepttext += "Nanotrasen Update: Biohazard Alert.
    " + intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.
    " + intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. Its origin is unknown.
    " + intercepttext += "Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.
    " + intercepttext += "Orders for all [station_name()] personnel follows:
    " + intercepttext += " 1. Do not leave the quarantine area.
    " + intercepttext += " 2. Locate any outbreaks of the organism on the station.
    " + intercepttext += " 3. If found, use any neccesary means to contain the organism.
    " + intercepttext += " 4. Avoid damage to the capital infrastructure of the station.
    " + intercepttext += "
    Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
    " + intercepttext += "Message ends." + if(2) + var/nukecode = rand(10000, 99999) + for(var/obj/machinery/nuclearbomb/bomb in world) + if(bomb && bomb.r_code) + if(is_station_level(bomb.z)) + bomb.r_code = nukecode + + interceptname = "Classified [command_name()] Update" + intercepttext += "Nanotrasen Update: Biohazard Alert.
    " + intercepttext += "Directive 7-12 has been issued for [station_name()].
    " + intercepttext += "The biohazard has grown out of control and will soon reach critical mass.
    " + intercepttext += "Your orders are as follows:
    " + intercepttext += "1. Secure the Nuclear Authentication Disk.
    " + intercepttext += "2. Detonate the Nuke located in the Station's Vault.
    " + intercepttext += "Nuclear Authentication Code: [nukecode]
    " + intercepttext += "Message ends." + + for(var/mob/living/silicon/ai/aiPlayer in GLOB.player_list) + if(aiPlayer.client) + var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]." + aiPlayer.set_zeroth_law(law) + to_chat(aiPlayer, "Laws Updated: [law]") + + print_command_report(intercepttext, interceptname) + GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") + +/datum/station_state + var/floor = 0 + var/wall = 0 + var/r_wall = 0 + var/window = 0 + var/door = 0 + var/grille = 0 + var/mach = 0 + + +/datum/station_state/proc/count() + for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1))) + + if(istype(T,/turf/simulated/floor)) + if(!(T:burnt)) + src.floor += 12 + else + src.floor += 1 + + if(istype(T, /turf/simulated/wall)) + var/turf/simulated/wall/W = T + if(W.intact) + src.wall += 2 + else + src.wall += 1 + + if(istype(T, /turf/simulated/wall/r_wall)) + var/turf/simulated/wall/r_wall/R = T + if(R.intact) + src.r_wall += 2 + else + src.r_wall += 1 + + + for(var/obj/O in T.contents) + if(istype(O, /obj/structure/window)) + src.window += 1 + else if(istype(O, /obj/structure/grille)) + var/obj/structure/grille/GR = O + if(!GR.broken) + grille += 1 + else if(istype(O, /obj/machinery/door)) + src.door += 1 + else if(istype(O, /obj/machinery)) + src.mach += 1 + +/datum/station_state/proc/score(var/datum/station_state/result) + if(!result) return 0 + var/output = 0 + output += (result.floor / max(floor,1)) + output += (result.r_wall/ max(r_wall,1)) + output += (result.wall / max(wall,1)) + output += (result.window / max(window,1)) + output += (result.door / max(door,1)) + output += (result.grille / max(grille,1)) + output += (result.mach / max(mach,1)) + return (output/7) diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index cc7203d9c404..fb26ce916989 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -214,4 +214,4 @@ if(message) for(var/mob/M in GLOB.mob_list) if(isovermind(M) || isobserver(M) || istype((M), /mob/living/simple_animal/hostile/blob/blobbernaut)) - M.show_message(rendered, 2) \ No newline at end of file + M.show_message(rendered, 2) diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index 354f28a45b76..672ab037cc55 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -1,144 +1,144 @@ -/obj/structure/blob/core - name = "blob core" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 400 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) - fire_resist = 2 - point_return = -1 - var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes - var/resource_delay = 0 - var/point_rate = 2 - var/is_offspring = null - var/selecting = 0 - -/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) - blob_cores += src - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - adjustcolors(color) //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(overmind) - adjustcolors(overmind.blob_reagent_datum.color) - if(offspring) - is_offspring = 1 - point_rate = new_rate - ..(loc, h) - - -/obj/structure/blob/core/adjustcolors(var/a_color) - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") - overlays += C - - -/obj/structure/blob/core/Destroy() - blob_cores -= src - if(overmind) - overmind.blob_core = null - overmind = null - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - return ..() - -/obj/structure/blob/core/ex_act(severity) - var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity - take_damage(damage, BRUTE, "bomb", 0) - -/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) - . = ..() - if(obj_integrity > 0) - if(overmind) //we should have an overmind, but... - overmind.update_health_hud() - -/obj/structure/blob/core/RegenHealth() - return // Don't regen, we handle it in Life() - -/obj/structure/blob/core/Life(seconds, times_fired) - if(!overmind) - create_overmind() - else - if(resource_delay <= world.time) - resource_delay = world.time + 10 // 1 second - overmind.add_points(point_rate) - obj_integrity = min(max_integrity, obj_integrity + 1) - if(overmind) - overmind.update_health_hud() - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(0, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(0, i, color) - for(var/b_dir in alldirs) - if(!prob(5)) - continue - var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) - if(B) - B.change_to(/obj/structure/blob/shield/core) - if(B && overmind) - B.color = overmind.blob_reagent_datum.color - else - B.color = color - color = null - ..() - - -/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) - if(overmind_get_delay > world.time && !override_delay) - return - - overmind_get_delay = world.time + 3000 // 5 minutes - - if(overmind) - qdel(overmind) - - var/mob/C = null - var/list/candidates = list() - - spawn() - if(!new_overmind) - if(is_offspring) - candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) - else - candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) - - if(candidates.len) - C = pick(candidates) - else - C = new_overmind - - if(C) - var/mob/camera/blob/B = new(src.loc) - B.key = C.key - B.blob_core = src - src.overmind = B - color = overmind.blob_reagent_datum.color - if(B.mind && !B.mind.special_role) - B.mind.make_Overmind() - spawn(0) - if(is_offspring) - B.is_offspring = TRUE - -/obj/structure/blob/core/proc/lateblobtimer() - addtimer(CALLBACK(src, .proc/lateblobcheck), 50) - -/obj/structure/blob/core/proc/lateblobcheck() - if(overmind) - overmind.add_points(60) - if(overmind.mind) - overmind.mind.make_Overmind() - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") - -/obj/structure/blob/core/onTransitZ(old_z, new_z) - if(overmind && is_station_level(new_z)) - overmind.forceMove(get_turf(src)) - return ..() \ No newline at end of file +/obj/structure/blob/core + name = "blob core" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + max_integrity = 400 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) + fire_resist = 2 + point_return = -1 + var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes + var/resource_delay = 0 + var/point_rate = 2 + var/is_offspring = null + var/selecting = 0 + +/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) + GLOB.blob_cores += src + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + adjustcolors(color) //so it atleast appears + if(!overmind) + create_overmind(new_overmind) + if(overmind) + adjustcolors(overmind.blob_reagent_datum.color) + if(offspring) + is_offspring = 1 + point_rate = new_rate + ..(loc, h) + + +/obj/structure/blob/core/adjustcolors(var/a_color) + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + I.color = a_color + overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") + overlays += C + + +/obj/structure/blob/core/Destroy() + GLOB.blob_cores -= src + if(overmind) + overmind.blob_core = null + overmind = null + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + return ..() + +/obj/structure/blob/core/ex_act(severity) + var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity + take_damage(damage, BRUTE, "bomb", 0) + +/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) + . = ..() + if(obj_integrity > 0) + if(overmind) //we should have an overmind, but... + overmind.update_health_hud() + +/obj/structure/blob/core/RegenHealth() + return // Don't regen, we handle it in Life() + +/obj/structure/blob/core/Life(seconds, times_fired) + if(!overmind) + create_overmind() + else + if(resource_delay <= world.time) + resource_delay = world.time + 10 // 1 second + overmind.add_points(point_rate) + obj_integrity = min(max_integrity, obj_integrity + 1) + if(overmind) + overmind.update_health_hud() + if(overmind) + for(var/i = 1; i < 8; i += i) + Pulse(0, i, overmind.blob_reagent_datum.color) + else + for(var/i = 1; i < 8; i += i) + Pulse(0, i, color) + for(var/b_dir in GLOB.alldirs) + if(!prob(5)) + continue + var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) + if(B) + B.change_to(/obj/structure/blob/shield/core) + if(B && overmind) + B.color = overmind.blob_reagent_datum.color + else + B.color = color + color = null + ..() + + +/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) + if(overmind_get_delay > world.time && !override_delay) + return + + overmind_get_delay = world.time + 3000 // 5 minutes + + if(overmind) + qdel(overmind) + + var/mob/C = null + var/list/candidates = list() + + spawn() + if(!new_overmind) + if(is_offspring) + candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) + else + candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) + + if(candidates.len) + C = pick(candidates) + else + C = new_overmind + + if(C) + var/mob/camera/blob/B = new(src.loc) + B.key = C.key + B.blob_core = src + src.overmind = B + color = overmind.blob_reagent_datum.color + if(B.mind && !B.mind.special_role) + B.mind.make_Overmind() + spawn(0) + if(is_offspring) + B.is_offspring = TRUE + +/obj/structure/blob/core/proc/lateblobtimer() + addtimer(CALLBACK(src, .proc/lateblobcheck), 50) + +/obj/structure/blob/core/proc/lateblobcheck() + if(overmind) + overmind.add_points(60) + if(overmind.mind) + overmind.mind.make_Overmind() + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") + +/obj/structure/blob/core/onTransitZ(old_z, new_z) + if(overmind && is_station_level(new_z)) + overmind.forceMove(get_turf(src)) + return ..() diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm index b8bff19cbcf5..54139ca4a35c 100644 --- a/code/game/gamemodes/blob/blobs/factory.dm +++ b/code/game/gamemodes/blob/blobs/factory.dm @@ -1,29 +1,29 @@ -/obj/structure/blob/factory - name = "factory blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_factory" - max_integrity = 200 - point_return = 18 - var/list/spores = list() - var/max_spores = 3 - var/spore_delay = 0 - -/obj/structure/blob/factory/Destroy() - for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) - if(spore.factory == src) - spore.factory = null - spores = null - return ..() - -/obj/structure/blob/factory/run_action() - if(spores.len >= max_spores) - return - if(spore_delay > world.time) - return - flick("blob_factory_glow", src) - spore_delay = world.time + 100 // 10 seconds - var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) - if(overmind) - BS.color = overmind.blob_reagent_datum?.complementary_color - BS.overmind = overmind - overmind.blob_mobs.Add(BS) +/obj/structure/blob/factory + name = "factory blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_factory" + max_integrity = 200 + point_return = 18 + var/list/spores = list() + var/max_spores = 3 + var/spore_delay = 0 + +/obj/structure/blob/factory/Destroy() + for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) + if(spore.factory == src) + spore.factory = null + spores = null + return ..() + +/obj/structure/blob/factory/run_action() + if(spores.len >= max_spores) + return + if(spore_delay > world.time) + return + flick("blob_factory_glow", src) + spore_delay = world.time + 100 // 10 seconds + var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) + if(overmind) + BS.color = overmind.blob_reagent_datum?.complementary_color + BS.overmind = overmind + overmind.blob_mobs.Add(BS) diff --git a/code/game/gamemodes/blob/blobs/node.dm b/code/game/gamemodes/blob/blobs/node.dm index 7d21bcc7946a..d8fe4943586d 100644 --- a/code/game/gamemodes/blob/blobs/node.dm +++ b/code/game/gamemodes/blob/blobs/node.dm @@ -1,36 +1,36 @@ -/obj/structure/blob/node - name = "blob node" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) - point_return = 18 - -/obj/structure/blob/node/New(loc, var/h = 100) - blob_nodes += src - START_PROCESSING(SSobj, src) - ..(loc, h) - -/obj/structure/blob/node/adjustcolors(var/a_color) - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - src.overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") - src.overlays += C - -/obj/structure/blob/node/Destroy() - blob_nodes -= src - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/structure/blob/node/Life(seconds, times_fired) - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(5, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(5, i, color) - obj_integrity = min(max_integrity, obj_integrity + 1) - color = null \ No newline at end of file +/obj/structure/blob/node + name = "blob node" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) + point_return = 18 + +/obj/structure/blob/node/New(loc, var/h = 100) + GLOB.blob_nodes += src + START_PROCESSING(SSobj, src) + ..(loc, h) + +/obj/structure/blob/node/adjustcolors(var/a_color) + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + I.color = a_color + src.overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") + src.overlays += C + +/obj/structure/blob/node/Destroy() + GLOB.blob_nodes -= src + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/structure/blob/node/Life(seconds, times_fired) + if(overmind) + for(var/i = 1; i < 8; i += i) + Pulse(5, i, overmind.blob_reagent_datum.color) + else + for(var/i = 1; i < 8; i += i) + Pulse(5, i, color) + obj_integrity = min(max_integrity, obj_integrity + 1) + color = null diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm index 9e841131b785..04e9f6d677f7 100644 --- a/code/game/gamemodes/blob/blobs/shield.dm +++ b/code/game/gamemodes/blob/blobs/shield.dm @@ -1,53 +1,53 @@ -/obj/structure/blob/shield - name = "strong blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_shield" - desc = "Some blob creature thingy" - max_integrity = 150 - brute_resist = 0.25 - explosion_block = 3 - atmosblock = TRUE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) - -/obj/structure/blob/shield/core - point_return = 0 - -/obj/structure/blob/shield/update_icon() - ..() - if(obj_integrity < max_integrity * 0.5) - icon_state = "[initial(icon_state)]_damaged" - name = "weakened [initial(name)]" - desc = "A wall of twitching tendrils." - atmosblock = FALSE - else - icon_state = initial(icon_state) - name = initial(name) - desc = initial(desc) - atmosblock = TRUE - air_update_turf(1) - -/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 - return 0 - -/obj/structure/blob/shield/reflective - name = "reflective blob" - desc = "A solid wall of slightly twitching tendrils with a reflective glow." - icon_state = "blob_glow" - max_integrity = 100 - brute_resist = 0.5 - explosion_block = 2 - point_return = 9 - flags_2 = CHECK_RICOCHET_2 - -/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - visible_message("[P] reflects off [src]!") - return TRUE \ No newline at end of file +/obj/structure/blob/shield + name = "strong blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_shield" + desc = "Some blob creature thingy" + max_integrity = 150 + brute_resist = 0.25 + explosion_block = 3 + atmosblock = TRUE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) + +/obj/structure/blob/shield/core + point_return = 0 + +/obj/structure/blob/shield/update_icon() + ..() + if(obj_integrity < max_integrity * 0.5) + icon_state = "[initial(icon_state)]_damaged" + name = "weakened [initial(name)]" + desc = "A wall of twitching tendrils." + atmosblock = FALSE + else + icon_state = initial(icon_state) + name = initial(name) + desc = initial(desc) + atmosblock = TRUE + air_update_turf(1) + +/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 + return 0 + +/obj/structure/blob/shield/reflective + name = "reflective blob" + desc = "A solid wall of slightly twitching tendrils with a reflective glow." + icon_state = "blob_glow" + max_integrity = 100 + brute_resist = 0.5 + explosion_block = 2 + point_return = 9 + flags_2 = CHECK_RICOCHET_2 + +/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + visible_message("[P] reflects off [src]!") + return TRUE diff --git a/code/game/gamemodes/blob/blobs/storage.dm b/code/game/gamemodes/blob/blobs/storage.dm index 0f3225e8362c..b9052b3601a3 100644 --- a/code/game/gamemodes/blob/blobs/storage.dm +++ b/code/game/gamemodes/blob/blobs/storage.dm @@ -13,4 +13,4 @@ /obj/structure/blob/storage/proc/update_max_blob_points(var/new_point_increase) if(overmind) - overmind.max_blob_points += new_point_increase \ No newline at end of file + overmind.max_blob_points += new_point_increase diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm index 548f2759ba04..bdbd875a3dcf 100644 --- a/code/game/gamemodes/blob/powers.dm +++ b/code/game/gamemodes/blob/powers.dm @@ -22,10 +22,10 @@ set name = "Jump to Node" set desc = "Transport back to a selected node." - if(blob_nodes.len) + if(GLOB.blob_nodes.len) var/list/nodes = list() - for(var/i = 1; i <= blob_nodes.len; i++) - var/obj/structure/blob/node/B = blob_nodes[i] + for(var/i = 1; i <= GLOB.blob_nodes.len; i++) + var/obj/structure/blob/node/B = GLOB.blob_nodes[i] nodes["Blob Node #[i] ([get_location_name(B)])"] = B var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes var/obj/structure/blob/node/chosen_node = nodes[node_name] @@ -463,7 +463,7 @@ color = blob_reagent_datum.complementary_color - for(var/obj/structure/blob/BL in blobs) + for(var/obj/structure/blob/BL in GLOB.blobs) BL.adjustcolors(blob_reagent_datum.color) for(var/mob/living/simple_animal/hostile/blob/BLO) diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index 37251cabbf2e..6293fd919c5d 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -1,242 +1,242 @@ -//I will need to recode parts of this but I am way too tired atm -/obj/structure/blob - name = "blob" - icon = 'icons/mob/blob.dmi' - light_range = 3 - desc = "Some blob creature thingy" - density = 0 - opacity = 0 - anchored = 1 - max_integrity = 30 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. - var/health_timestamp = 0 - var/brute_resist = 0.5 //multiplies brute damage by this - var/fire_resist = 1 //multiplies burn damage by this - var/atmosblock = FALSE //if the blob blocks atmos and heat spread - var/mob/camera/blob/overmind - -/obj/structure/blob/New(loc) - ..() - blobs += src - setDir(pick(cardinal)) - update_icon() - if(atmosblock) - air_update_turf(1) - ConsumeTile() - -/obj/structure/blob/Destroy() - if(atmosblock) - atmosblock = FALSE - air_update_turf(1) - blobs -= src - if(isturf(loc)) //Necessary because Expand() is retarded and spawns a blob and then deletes it - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - return ..() - -/obj/structure/blob/BlockSuperconductivity() - return atmosblock - -/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover) && mover.checkpass(PASSBLOB)) - return 1 - return 0 - -/obj/structure/blob/CanAtmosPass(turf/T) - return !atmosblock - -/obj/structure/blob/CanAStarPass(ID, dir, caller) - . = 0 - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSBLOB) - -/obj/structure/blob/process() - Life() - return - -/obj/structure/blob/blob_act(obj/structure/blob/B) - return - -/obj/structure/blob/proc/Life() - return - -/obj/structure/blob/proc/RegenHealth() - // All blobs heal over time when pulsed, but it has a cool down - if(health_timestamp > world.time) - return 0 - if(obj_integrity < max_integrity) - obj_integrity = min(max_integrity, obj_integrity + 1) - update_icon() - health_timestamp = world.time + 10 // 1 seconds - - -/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand - - set background = BACKGROUND_ENABLED - - RegenHealth() - - if(run_action())//If we can do something here then we dont need to pulse more - return - - if(pulse > 30) - return//Inf loop check - - //Looking for another blob to pulse - var/list/dirs = list(1,2,4,8) - dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us - for(var/i = 1 to 4) - if(!dirs.len) break - var/dirn = pick(dirs) - dirs.Remove(dirn) - var/turf/T = get_step(src, dirn) - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - if(!B) - expand(T,1,a_color)//No blob here so try and expand - return - B.adjustcolors(a_color) - - B.Pulse((pulse+1),get_dir(src.loc,T), a_color) - return - return - - -/obj/structure/blob/proc/run_action() - return 0 - -/obj/structure/blob/proc/ConsumeTile() - for(var/atom/A in loc) - A.blob_act(src) - if(iswallturf(loc)) - loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it - -/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) - if(prob && !prob(obj_integrity)) - return - if(istype(T, /turf/space) && prob(75)) return - if(!T) - var/list/dirs = list(1,2,4,8) - for(var/i = 1 to 4) - var/dirn = pick(dirs) - dirs.Remove(dirn) - T = get_step(src, dirn) - if(!(locate(/obj/structure/blob) in T)) break - else T = null - - if(!T) return 0 - var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) - B.color = a_color - B.density = 1 - if(T.Enter(B,src))//Attempt to move into the tile - B.density = initial(B.density) - B.loc = T - else - T.blob_act()//If we cant move in hit the turf - B.loc = null //So we don't play the splat sound, see Destroy() - qdel(B) - - for(var/atom/A in T)//Hit everything in the turf - A.blob_act(src) - return 1 - -/obj/structure/blob/Crossed(var/mob/living/L, oldloc) - ..() - L.blob_act(src) - -/obj/structure/blob/tesla_act(power) - ..() - take_damage(power / 400, BURN, "energy") - -/obj/structure/blob/hulk_damage() - return 15 - -/obj/structure/blob/attack_animal(mob/living/simple_animal/M) - if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut - return - ..() - -/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - switch(damage_type) - if(BRUTE) - damage_amount *= brute_resist - if(BURN) - damage_amount *= fire_resist - if(CLONE) - else - return 0 - var/armor_protection = 0 - if(damage_flag) - armor_protection = armor[damage_flag] - damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) - if(overmind && damage_flag) - damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) - return damage_amount - -/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - update_icon() - -/obj/structure/blob/proc/change_to(var/type) - if(!ispath(type)) - error("[type] is an invalid type for the blob.") - var/obj/structure/blob/B = new type(src.loc) - if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) - B.color = color - else - B.adjustcolors(color) - qdel(src) - -/obj/structure/blob/proc/adjustcolors(var/a_color) - if(a_color) - color = a_color - return - -/obj/structure/blob/examine(mob/user) - . = ..() - . += "It looks like it's made of [get_chem_name()]." - - -/obj/structure/blob/proc/get_chem_name() - for(var/mob/camera/blob/B in GLOB.mob_list) - if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these - return B.blob_reagent_datum.name - return "unknown" - -/obj/structure/blob/normal - icon_state = "blob" - light_range = 0 - obj_integrity = 21 //doesn't start at full health - max_integrity = 25 - brute_resist = 0.25 - -/obj/structure/blob/normal/update_icon() - ..() - if(obj_integrity <= 15) - icon_state = "blob_damaged" - name = "fragile blob" - desc = "A thin lattice of slightly twitching tendrils." - brute_resist = 0.5 - else if(overmind) - icon_state = "blob" - name = "blob" - desc = "A thick wall of writhing tendrils." - brute_resist = 0.25 - else - icon_state = "blob" - name = "dead blob" - desc = "A thick wall of lifeless tendrils." - brute_resist = 0.25 \ No newline at end of file +//I will need to recode parts of this but I am way too tired atm +/obj/structure/blob + name = "blob" + icon = 'icons/mob/blob.dmi' + light_range = 3 + desc = "Some blob creature thingy" + density = 0 + opacity = 0 + anchored = 1 + max_integrity = 30 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + var/health_timestamp = 0 + var/brute_resist = 0.5 //multiplies brute damage by this + var/fire_resist = 1 //multiplies burn damage by this + var/atmosblock = FALSE //if the blob blocks atmos and heat spread + var/mob/camera/blob/overmind + +/obj/structure/blob/New(loc) + ..() + GLOB.blobs += src + setDir(pick(GLOB.cardinal)) + update_icon() + if(atmosblock) + air_update_turf(1) + ConsumeTile() + +/obj/structure/blob/Destroy() + if(atmosblock) + atmosblock = FALSE + air_update_turf(1) + GLOB.blobs -= src + if(isturf(loc)) //Necessary because Expand() is retarded and spawns a blob and then deletes it + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + return ..() + +/obj/structure/blob/BlockSuperconductivity() + return atmosblock + +/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSBLOB)) + return 1 + return 0 + +/obj/structure/blob/CanAtmosPass(turf/T) + return !atmosblock + +/obj/structure/blob/CanAStarPass(ID, dir, caller) + . = 0 + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSBLOB) + +/obj/structure/blob/process() + Life() + return + +/obj/structure/blob/blob_act(obj/structure/blob/B) + return + +/obj/structure/blob/proc/Life() + return + +/obj/structure/blob/proc/RegenHealth() + // All blobs heal over time when pulsed, but it has a cool down + if(health_timestamp > world.time) + return 0 + if(obj_integrity < max_integrity) + obj_integrity = min(max_integrity, obj_integrity + 1) + update_icon() + health_timestamp = world.time + 10 // 1 seconds + + +/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand + + set background = BACKGROUND_ENABLED + + RegenHealth() + + if(run_action())//If we can do something here then we dont need to pulse more + return + + if(pulse > 30) + return//Inf loop check + + //Looking for another blob to pulse + var/list/dirs = list(1,2,4,8) + dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us + for(var/i = 1 to 4) + if(!dirs.len) break + var/dirn = pick(dirs) + dirs.Remove(dirn) + var/turf/T = get_step(src, dirn) + var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) + if(!B) + expand(T,1,a_color)//No blob here so try and expand + return + B.adjustcolors(a_color) + + B.Pulse((pulse+1),get_dir(src.loc,T), a_color) + return + return + + +/obj/structure/blob/proc/run_action() + return 0 + +/obj/structure/blob/proc/ConsumeTile() + for(var/atom/A in loc) + A.blob_act(src) + if(iswallturf(loc)) + loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it + +/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) + if(prob && !prob(obj_integrity)) + return + if(istype(T, /turf/space) && prob(75)) return + if(!T) + var/list/dirs = list(1,2,4,8) + for(var/i = 1 to 4) + var/dirn = pick(dirs) + dirs.Remove(dirn) + T = get_step(src, dirn) + if(!(locate(/obj/structure/blob) in T)) break + else T = null + + if(!T) return 0 + var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) + B.color = a_color + B.density = 1 + if(T.Enter(B,src))//Attempt to move into the tile + B.density = initial(B.density) + B.loc = T + else + T.blob_act()//If we cant move in hit the turf + B.loc = null //So we don't play the splat sound, see Destroy() + qdel(B) + + for(var/atom/A in T)//Hit everything in the turf + A.blob_act(src) + return 1 + +/obj/structure/blob/Crossed(var/mob/living/L, oldloc) + ..() + L.blob_act(src) + +/obj/structure/blob/tesla_act(power) + ..() + take_damage(power / 400, BURN, "energy") + +/obj/structure/blob/hulk_damage() + return 15 + +/obj/structure/blob/attack_animal(mob/living/simple_animal/M) + if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut + return + ..() + +/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + switch(damage_type) + if(BRUTE) + damage_amount *= brute_resist + if(BURN) + damage_amount *= fire_resist + if(CLONE) + else + return 0 + var/armor_protection = 0 + if(damage_flag) + armor_protection = armor[damage_flag] + damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) + if(overmind && damage_flag) + damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) + return damage_amount + +/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + update_icon() + +/obj/structure/blob/proc/change_to(var/type) + if(!ispath(type)) + error("[type] is an invalid type for the blob.") + var/obj/structure/blob/B = new type(src.loc) + if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) + B.color = color + else + B.adjustcolors(color) + qdel(src) + +/obj/structure/blob/proc/adjustcolors(var/a_color) + if(a_color) + color = a_color + return + +/obj/structure/blob/examine(mob/user) + . = ..() + . += "It looks like it's made of [get_chem_name()]." + + +/obj/structure/blob/proc/get_chem_name() + for(var/mob/camera/blob/B in GLOB.mob_list) + if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these + return B.blob_reagent_datum.name + return "unknown" + +/obj/structure/blob/normal + icon_state = "blob" + light_range = 0 + obj_integrity = 21 //doesn't start at full health + max_integrity = 25 + brute_resist = 0.25 + +/obj/structure/blob/normal/update_icon() + ..() + if(obj_integrity <= 15) + icon_state = "blob_damaged" + name = "fragile blob" + desc = "A thin lattice of slightly twitching tendrils." + brute_resist = 0.5 + else if(overmind) + icon_state = "blob" + name = "blob" + desc = "A thick wall of writhing tendrils." + brute_resist = 0.25 + else + icon_state = "blob" + name = "dead blob" + desc = "A thick wall of lifeless tendrils." + brute_resist = 0.25 diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 713779af2411..91dd553fe0dd 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -1,332 +1,332 @@ -#define LING_FAKEDEATH_TIME 400 //40 seconds -#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. -#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob - -var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") - -/datum/game_mode - var/list/datum/mind/changelings = list() - -/datum/game_mode/changeling - name = "changeling" - config_tag = "changeling" - restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - protected_species = list("Machine") - required_players = 15 - required_enemies = 1 - recommended_enemies = 4 - - var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time - var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target - var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target - - var/const/prob_int_item = 50 // intercept names the theft target half the time - var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target - var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target - - var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time - var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target - var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target - - var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative - var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative - var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly - var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly - - var/changeling_amount = 4 - -/datum/game_mode/changeling/announce() - to_chat(world, "The current game mode is - Changeling!") - to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") - -/datum/game_mode/changeling/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) - - changeling_amount = 1 + round(num_players() / 10) - - if(possible_changelings.len>0) - for(var/i = 0, i < changeling_amount, i++) - if(!possible_changelings.len) break - var/datum/mind/changeling = pick(possible_changelings) - possible_changelings -= changeling - changelings += changeling - changeling.restricted_roles = restricted_jobs - modePlayer += changelings - changeling.special_role = SPECIAL_ROLE_CHANGELING - ..() - return 1 - else - return 0 - -/datum/game_mode/changeling/post_setup() - for(var/datum/mind/changeling in changelings) - grant_changeling_powers(changeling.current) - forge_changeling_objectives(changeling) - greet_changeling(changeling) - update_change_icons_added(changeling) - - ..() - -/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling) - //OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives. - //If they have two objectives as well as absorb, they must survive rather than escape - //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting - //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone - - var/datum/objective/absorb/absorb_objective = new - absorb_objective.owner = changeling - absorb_objective.gen_amount_goal(6, 8) - changeling.objectives += absorb_objective - - if(prob(60)) - var/datum/objective/steal/steal_objective = new - steal_objective.owner = changeling - steal_objective.find_target() - changeling.objectives += steal_objective - else - var/datum/objective/debrain/debrain_objective = new - debrain_objective.owner = changeling - debrain_objective.find_target() - changeling.objectives += debrain_objective - - var/list/active_ais = active_ais() - if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags. - var/datum/objective/destroy/destroy_objective = new - destroy_objective.owner = changeling - destroy_objective.find_target() - changeling.objectives += destroy_objective - else - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = changeling - kill_objective.find_target() - changeling.objectives += kill_objective - - if(!(locate(/datum/objective/escape) in changeling.objectives)) - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.target = kill_objective.target - if(identity_theft.target && identity_theft.target.current) - identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this. - var/mob/living/carbon/human/H = identity_theft.target.current - if(can_absorb_species(H.dna.species)) // For species that can't be absorbed - should default to an escape objective - identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing [identity_theft.target.p_their()] identification card." - changeling.objectives += identity_theft - else - qdel(identity_theft) - - if(!(locate(/datum/objective/escape) in changeling.objectives)) - if(prob(70)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = changeling - changeling.objectives += escape_objective - else - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.find_target() - changeling.objectives += identity_theft - return - -/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) - SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg') - if(you_are) - to_chat(changeling.current, "You are a changeling!") - to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.") - to_chat(changeling.current, "You must complete the following tasks:") - if(changeling.current.mind) - if(changeling.current.mind.assigned_role == "Clown") - to_chat(changeling.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") - changeling.current.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(changeling.current) - var/obj_count = 1 - for(var/datum/objective/objective in changeling.objectives) - to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - -/datum/game_mode/proc/remove_changeling(datum/mind/changeling_mind) - if(changeling_mind in changelings) - changelings -= changeling_mind - changeling_mind.current.remove_changeling_powers() - changeling_mind.memory = "" - changeling_mind.special_role = null - if(issilicon(changeling_mind.current)) - to_chat(changeling_mind.current, "You have been robotized!") - to_chat(changeling_mind.current, "You must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.") - else - to_chat(changeling_mind.current, "You lose your powers! You are no longer a changeling and are stuck in your current form!") - update_change_icons_removed(changeling_mind) - -/datum/game_mode/proc/update_change_icons_added(datum/mind/changeling) - var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] - linghud.join_hud(changeling.current) - set_antag_hud(changeling.current, "hudchangeling") - -/datum/game_mode/proc/update_change_icons_removed(datum/mind/changeling) - var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] - linghud.leave_hud(changeling.current) - set_antag_hud(changeling.current, null) - -/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob) - if(!istype(changeling_mob)) - return - changeling_mob.make_changeling() - -/datum/game_mode/proc/auto_declare_completion_changeling() - if(changelings.len) - var/text = "The changelings were:" - for(var/datum/mind/changeling in changelings) - var/changelingwin = 1 - - text += "
    [changeling.key] was [changeling.name] (" - if(changeling.current) - if(changeling.current.stat == DEAD) - text += "died" - else - text += "survived" - if(changeling.current.real_name != changeling.name) - text += " as [changeling.current.real_name]" - else - text += "body destroyed" - changelingwin = 0 - text += ")" - - //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. - text += "
    Changeling ID: [changeling.changeling.changelingID]." - text += "
    Genomes Extracted: [changeling.changeling.absorbedcount]" - - if(changeling.objectives.len) - var/count = 1 - for(var/datum/objective/objective in changeling.objectives) - if(objective.check_completion()) - text += "
    Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("changeling_objective","[objective.type]|SUCCESS") - else - text += "
    Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("changeling_objective","[objective.type]|FAIL") - changelingwin = 0 - count++ - - if(changelingwin) - text += "
    The changeling was successful!" - feedback_add_details("changeling_success","SUCCESS") - else - text += "
    The changeling has failed." - feedback_add_details("changeling_success","FAIL") - - to_chat(world, text) - - return 1 - -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/absorbed_dna = list() - var/list/absorbed_languages = list() - var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full. - var/dna_max = 5 //How many total DNA strands the changeling can store for transformation. - var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human - var/chem_charges = 20 - var/chem_recharge_rate = 1 - var/chem_recharge_slowdown = 0 - var/chem_storage = 75 - var/sting_range = 2 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/islinking = 0 - var/geneticpoints = 10 - var/purchasedpowers = list() - var/mimicing = "" - var/canrespec = FALSE //set to TRUE in absorb.dm - var/changeling_speak = 0 - var/datum/dna/chosen_dna - var/datum/action/changeling/sting/chosen_sting - var/regenerating = FALSE - -/datum/changeling/New(gender=FEMALE) - ..() - var/honorific - if(gender == FEMALE) - honorific = "Ms." - else - honorific = "Mr." - if(possible_changeling_IDs.len) - changelingID = pick(possible_changeling_IDs) - possible_changeling_IDs -= changelingID - changelingID = "[honorific] [changelingID]" - else - changelingID = "[honorific] [rand(1,999)]" - -/datum/changeling/proc/regenerate(mob/living/carbon/the_ling) - if(istype(the_ling)) - if(the_ling.stat == DEAD) - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) - geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) - else //not dead? no chem/geneticdamage caps. - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) - geneticdamage = max(0, geneticdamage-1) - -/datum/changeling/proc/GetDNA(dna_owner) - for(var/datum/dna/DNA in (absorbed_dna + protected_dna)) - if(dna_owner == DNA.real_name) - return DNA - -/datum/changeling/proc/find_dna(datum/dna/tDNA) - for(var/datum/dna/D in (absorbed_dna + protected_dna)) - if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species.type == D.species.type) - return D - return null - -/datum/changeling/proc/has_dna(datum/dna/tDNA) - if(find_dna(tDNA)) - return 1 - return 0 - -// A changeling's DNA is "stale" if their current form's DNA is the oldest DNA in a full list -/datum/changeling/proc/using_stale_dna(mob/living/carbon/user) - var/current_dna = find_dna(user.dna) - if(absorbed_dna.len < dna_max) - return 0 // Still more room for DNA - if(!current_dna || !(current_dna in absorbed_dna)) - return 1 // Oops, our current DNA was somehow not absorbed; force a transformation - if(absorbed_dna[1] == current_dna) - return 1 // Oldest DNA is the current DNA - return 0 - -/datum/changeling/proc/trim_dna() - absorbed_dna -= null - if(absorbed_dna.len > dna_max) - absorbed_dna.Cut(1, (absorbed_dna.len - dna_max) + 1) - -/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/target) - if(using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. - to_chat(user, "The DNA we are wearing is stale. Transform and try again.") - return - - if(!target || !target.dna) - to_chat(user, "This creature does not have any DNA.") - return - - var/mob/living/carbon/human/T = target - if(!istype(T) || issmall(T)) - to_chat(user, "[T] is not compatible with our biology.") - return - - if((NOCLONE || SKELETON || HUSK) in T.mutations) - to_chat(user, "DNA of [target] is ruined beyond usability!") - return - - if(NO_DNA in T.dna.species.species_traits) - to_chat(user, "This creature does not have DNA!") - return - - if(has_dna(target.dna)) - to_chat(user, "We already have this DNA in storage!") - - return 1 - -/proc/can_absorb_species(datum/species/S) - return !(NO_DNA in S.species_traits) +#define LING_FAKEDEATH_TIME 400 //40 seconds +#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. +#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob + +GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")) + +/datum/game_mode + var/list/datum/mind/changelings = list() + +/datum/game_mode/changeling + name = "changeling" + config_tag = "changeling" + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_species = list("Machine") + required_players = 15 + required_enemies = 1 + recommended_enemies = 4 + + var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time + var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target + var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target + + var/const/prob_int_item = 50 // intercept names the theft target half the time + var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target + var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target + + var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time + var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target + var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target + + var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative + var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative + var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly + var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly + + var/changeling_amount = 4 + +/datum/game_mode/changeling/announce() + to_chat(world, "The current game mode is - Changeling!") + to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") + +/datum/game_mode/changeling/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) + + changeling_amount = 1 + round(num_players() / 10) + + if(possible_changelings.len>0) + for(var/i = 0, i < changeling_amount, i++) + if(!possible_changelings.len) break + var/datum/mind/changeling = pick(possible_changelings) + possible_changelings -= changeling + changelings += changeling + changeling.restricted_roles = restricted_jobs + modePlayer += changelings + changeling.special_role = SPECIAL_ROLE_CHANGELING + ..() + return 1 + else + return 0 + +/datum/game_mode/changeling/post_setup() + for(var/datum/mind/changeling in changelings) + grant_changeling_powers(changeling.current) + forge_changeling_objectives(changeling) + greet_changeling(changeling) + update_change_icons_added(changeling) + + ..() + +/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling) + //OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives. + //If they have two objectives as well as absorb, they must survive rather than escape + //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting + //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone + + var/datum/objective/absorb/absorb_objective = new + absorb_objective.owner = changeling + absorb_objective.gen_amount_goal(6, 8) + changeling.objectives += absorb_objective + + if(prob(60)) + var/datum/objective/steal/steal_objective = new + steal_objective.owner = changeling + steal_objective.find_target() + changeling.objectives += steal_objective + else + var/datum/objective/debrain/debrain_objective = new + debrain_objective.owner = changeling + debrain_objective.find_target() + changeling.objectives += debrain_objective + + var/list/active_ais = active_ais() + if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags. + var/datum/objective/destroy/destroy_objective = new + destroy_objective.owner = changeling + destroy_objective.find_target() + changeling.objectives += destroy_objective + else + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = changeling + kill_objective.find_target() + changeling.objectives += kill_objective + + if(!(locate(/datum/objective/escape) in changeling.objectives)) + var/datum/objective/escape/escape_with_identity/identity_theft = new + identity_theft.owner = changeling + identity_theft.target = kill_objective.target + if(identity_theft.target && identity_theft.target.current) + identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this. + var/mob/living/carbon/human/H = identity_theft.target.current + if(can_absorb_species(H.dna.species)) // For species that can't be absorbed - should default to an escape objective + identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing [identity_theft.target.p_their()] identification card." + changeling.objectives += identity_theft + else + qdel(identity_theft) + + if(!(locate(/datum/objective/escape) in changeling.objectives)) + if(prob(70)) + var/datum/objective/escape/escape_objective = new + escape_objective.owner = changeling + changeling.objectives += escape_objective + else + var/datum/objective/escape/escape_with_identity/identity_theft = new + identity_theft.owner = changeling + identity_theft.find_target() + changeling.objectives += identity_theft + return + +/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) + SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg') + if(you_are) + to_chat(changeling.current, "You are a changeling!") + to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.") + to_chat(changeling.current, "You must complete the following tasks:") + if(changeling.current.mind) + if(changeling.current.mind.assigned_role == "Clown") + to_chat(changeling.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") + changeling.current.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(changeling.current) + var/obj_count = 1 + for(var/datum/objective/objective in changeling.objectives) + to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + +/datum/game_mode/proc/remove_changeling(datum/mind/changeling_mind) + if(changeling_mind in changelings) + changelings -= changeling_mind + changeling_mind.current.remove_changeling_powers() + changeling_mind.memory = "" + changeling_mind.special_role = null + if(issilicon(changeling_mind.current)) + to_chat(changeling_mind.current, "You have been robotized!") + to_chat(changeling_mind.current, "You must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.") + else + to_chat(changeling_mind.current, "You lose your powers! You are no longer a changeling and are stuck in your current form!") + update_change_icons_removed(changeling_mind) + +/datum/game_mode/proc/update_change_icons_added(datum/mind/changeling) + var/datum/atom_hud/antag/linghud = GLOB.huds[ANTAG_HUD_CHANGELING] + linghud.join_hud(changeling.current) + set_antag_hud(changeling.current, "hudchangeling") + +/datum/game_mode/proc/update_change_icons_removed(datum/mind/changeling) + var/datum/atom_hud/antag/linghud = GLOB.huds[ANTAG_HUD_CHANGELING] + linghud.leave_hud(changeling.current) + set_antag_hud(changeling.current, null) + +/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob) + if(!istype(changeling_mob)) + return + changeling_mob.make_changeling() + +/datum/game_mode/proc/auto_declare_completion_changeling() + if(changelings.len) + var/text = "The changelings were:" + for(var/datum/mind/changeling in changelings) + var/changelingwin = 1 + + text += "
    [changeling.key] was [changeling.name] (" + if(changeling.current) + if(changeling.current.stat == DEAD) + text += "died" + else + text += "survived" + if(changeling.current.real_name != changeling.name) + text += " as [changeling.current.real_name]" + else + text += "body destroyed" + changelingwin = 0 + text += ")" + + //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. + text += "
    Changeling ID: [changeling.changeling.changelingID]." + text += "
    Genomes Extracted: [changeling.changeling.absorbedcount]" + + if(changeling.objectives.len) + var/count = 1 + for(var/datum/objective/objective in changeling.objectives) + if(objective.check_completion()) + text += "
    Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("changeling_objective","[objective.type]|SUCCESS") + else + text += "
    Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("changeling_objective","[objective.type]|FAIL") + changelingwin = 0 + count++ + + if(changelingwin) + text += "
    The changeling was successful!" + feedback_add_details("changeling_success","SUCCESS") + else + text += "
    The changeling has failed." + feedback_add_details("changeling_success","FAIL") + + to_chat(world, text) + + return 1 + +/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) + var/list/absorbed_dna = list() + var/list/absorbed_languages = list() + var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full. + var/dna_max = 5 //How many total DNA strands the changeling can store for transformation. + var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human + var/chem_charges = 20 + var/chem_recharge_rate = 1 + var/chem_recharge_slowdown = 0 + var/chem_storage = 75 + var/sting_range = 2 + var/changelingID = "Changeling" + var/geneticdamage = 0 + var/isabsorbing = 0 + var/islinking = 0 + var/geneticpoints = 10 + var/purchasedpowers = list() + var/mimicing = "" + var/canrespec = FALSE //set to TRUE in absorb.dm + var/changeling_speak = 0 + var/datum/dna/chosen_dna + var/datum/action/changeling/sting/chosen_sting + var/regenerating = FALSE + +/datum/changeling/New(gender=FEMALE) + ..() + var/honorific + if(gender == FEMALE) + honorific = "Ms." + else + honorific = "Mr." + if(GLOB.possible_changeling_IDs.len) + changelingID = pick(GLOB.possible_changeling_IDs) + GLOB.possible_changeling_IDs -= changelingID + changelingID = "[honorific] [changelingID]" + else + changelingID = "[honorific] [rand(1,999)]" + +/datum/changeling/proc/regenerate(mob/living/carbon/the_ling) + if(istype(the_ling)) + if(the_ling.stat == DEAD) + chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) + geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) + else //not dead? no chem/geneticdamage caps. + chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) + geneticdamage = max(0, geneticdamage-1) + +/datum/changeling/proc/GetDNA(dna_owner) + for(var/datum/dna/DNA in (absorbed_dna + protected_dna)) + if(dna_owner == DNA.real_name) + return DNA + +/datum/changeling/proc/find_dna(datum/dna/tDNA) + for(var/datum/dna/D in (absorbed_dna + protected_dna)) + if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species.type == D.species.type) + return D + return null + +/datum/changeling/proc/has_dna(datum/dna/tDNA) + if(find_dna(tDNA)) + return 1 + return 0 + +// A changeling's DNA is "stale" if their current form's DNA is the oldest DNA in a full list +/datum/changeling/proc/using_stale_dna(mob/living/carbon/user) + var/current_dna = find_dna(user.dna) + if(absorbed_dna.len < dna_max) + return 0 // Still more room for DNA + if(!current_dna || !(current_dna in absorbed_dna)) + return 1 // Oops, our current DNA was somehow not absorbed; force a transformation + if(absorbed_dna[1] == current_dna) + return 1 // Oldest DNA is the current DNA + return 0 + +/datum/changeling/proc/trim_dna() + absorbed_dna -= null + if(absorbed_dna.len > dna_max) + absorbed_dna.Cut(1, (absorbed_dna.len - dna_max) + 1) + +/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/target) + if(using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. + to_chat(user, "The DNA we are wearing is stale. Transform and try again.") + return + + if(!target || !target.dna) + to_chat(user, "This creature does not have any DNA.") + return + + var/mob/living/carbon/human/T = target + if(!istype(T) || issmall(T)) + to_chat(user, "[T] is not compatible with our biology.") + return + + if((NOCLONE || SKELETON || HUSK) in T.mutations) + to_chat(user, "DNA of [target] is ruined beyond usability!") + return + + if(NO_DNA in T.dna.species.species_traits) + to_chat(user, "This creature does not have DNA!") + return + + if(has_dna(target.dna)) + to_chat(user, "We already have this DNA in storage!") + + return 1 + +/proc/can_absorb_species(datum/species/S) + return !(NO_DNA in S.species_traits) diff --git a/code/game/gamemodes/changeling/evolution_menu.dm b/code/game/gamemodes/changeling/evolution_menu.dm index b6a0213f82f6..9c33ffaaca56 100644 --- a/code/game/gamemodes/changeling/evolution_menu.dm +++ b/code/game/gamemodes/changeling/evolution_menu.dm @@ -1,4 +1,4 @@ -var/list/sting_paths +GLOBAL_LIST_EMPTY(sting_paths) // totally stolen from the new player panel. YAYY /datum/action/changeling/evolution_menu @@ -12,8 +12,8 @@ var/list/sting_paths return var/datum/changeling/changeling = usr.mind.changeling - if(!sting_paths) - sting_paths = init_subtypes(/datum/action/changeling) + if(!GLOB.sting_paths || !GLOB.sting_paths.len) + GLOB.sting_paths = init_subtypes(/datum/action/changeling) var/dat = create_menu(changeling) usr << browse(dat, "window=powers;size=600x700")//900x480 @@ -230,7 +230,7 @@ var/list/sting_paths "} var/i = 1 - for(var/datum/action/changeling/cling_power in sting_paths) + for(var/datum/action/changeling/cling_power in GLOB.sting_paths) if(cling_power.dna_cost <= 0) //Let's skip the crap we start with. Keeps the evolution menu uncluttered. continue diff --git a/code/game/gamemodes/changeling/powers/chameleon_skin.dm b/code/game/gamemodes/changeling/powers/chameleon_skin.dm index 7de16801153d..4b53e5ce0820 100644 --- a/code/game/gamemodes/changeling/powers/chameleon_skin.dm +++ b/code/game/gamemodes/changeling/powers/chameleon_skin.dm @@ -11,19 +11,19 @@ var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1 if(!istype(H)) // req_human could be done in can_sting stuff. return - if(H.dna.GetSEState(CHAMELEONBLOCK)) - H.dna.SetSEState(CHAMELEONBLOCK, 0) - genemutcheck(H, CHAMELEONBLOCK, null, MUTCHK_FORCED) + if(H.dna.GetSEState(GLOB.chameleonblock)) + H.dna.SetSEState(GLOB.chameleonblock, 0) + genemutcheck(H, GLOB.chameleonblock, null, MUTCHK_FORCED) else - H.dna.SetSEState(CHAMELEONBLOCK, 1) - genemutcheck(H, CHAMELEONBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.chameleonblock, 1) + genemutcheck(H, GLOB.chameleonblock, null, MUTCHK_FORCED) feedback_add_details("changeling_powers","CS") return TRUE /datum/action/changeling/chameleon_skin/Remove(mob/user) var/mob/living/carbon/C = user - if(C.dna.GetSEState(CHAMELEONBLOCK)) - C.dna.SetSEState(CHAMELEONBLOCK, 0) - genemutcheck(C, CHAMELEONBLOCK, null, MUTCHK_FORCED) + if(C.dna.GetSEState(GLOB.chameleonblock)) + C.dna.SetSEState(GLOB.chameleonblock, 0) + genemutcheck(C, GLOB.chameleonblock, null, MUTCHK_FORCED) ..() diff --git a/code/game/gamemodes/changeling/powers/humanform.dm b/code/game/gamemodes/changeling/powers/humanform.dm index f9b214c513c4..1f90f7eb702d 100644 --- a/code/game/gamemodes/changeling/powers/humanform.dm +++ b/code/game/gamemodes/changeling/powers/humanform.dm @@ -24,8 +24,8 @@ if(!user) return 0 to_chat(user, "We transform our appearance.") - user.dna.SetSEState(MONKEYBLOCK,0,1) - genemutcheck(user,MONKEYBLOCK,null,MUTCHK_FORCED) + user.dna.SetSEState(GLOB.monkeyblock,0,1) + genemutcheck(user,GLOB.monkeyblock,null,MUTCHK_FORCED) if(istype(user)) user.set_species(chosen_dna.species.type) user.dna = chosen_dna.Clone() diff --git a/code/game/gamemodes/changeling/powers/lesserform.dm b/code/game/gamemodes/changeling/powers/lesserform.dm index 0dd112b66dd3..ceab9e02cd3c 100644 --- a/code/game/gamemodes/changeling/powers/lesserform.dm +++ b/code/game/gamemodes/changeling/powers/lesserform.dm @@ -33,4 +33,4 @@ HF.Grant(user) feedback_add_details("changeling_powers","LF") - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm index 6e6d69cc1dd7..ae675ac5ad63 100644 --- a/code/game/gamemodes/changeling/powers/panacea.dm +++ b/code/game/gamemodes/changeling/powers/panacea.dm @@ -41,4 +41,4 @@ D.cure() feedback_add_details("changeling_powers","AP") - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index 417fbaa052de..afad76299fbd 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -32,4 +32,4 @@ if(!chosen_name) return var/datum/dna/chosen_dna = GetDNA(chosen_name) - return chosen_dna \ No newline at end of file + return chosen_dna diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 551cfdc05c64..f163b2a4c04f 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -41,4 +41,4 @@ greet_changeling(changeling) update_change_icons_added(changeling) ..() - return \ No newline at end of file + return diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 39e3c5b7d3f4..1a9dd15c65e6 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,396 +1,397 @@ -var/global/list/all_cults = list() - -/datum/game_mode - var/list/datum/mind/cult = list() - -/proc/iscultist(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.cult) - - -/proc/is_convertable_to_cult(datum/mind/mind) - if(!mind) - return FALSE - if(!mind.current) - return FALSE - if(iscultist(mind.current)) - return TRUE //If they're already in the cult, assume they are convertable - if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain"))) - return FALSE - if(ishuman(mind.current)) - var/mob/living/carbon/human/H = mind.current - if(ismindshielded(H)) //mindshield protects against conversions unless removed - return FALSE -// if(mind.offstation_role) cant convert offstation roles such as ghost spawns -// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew - if(issilicon(mind.current)) - return FALSE //can't convert machines, that's ratvar's thing - if(isguardian(mind.current)) - var/mob/living/simple_animal/hostile/guardian/G = mind.current - if(!iscultist(G.summoner)) - return FALSE //can't convert it unless the owner is converted - if(isgolem(mind.current)) - return FALSE - return TRUE - -/proc/is_sacrifice_target(datum/mind/mind) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(mind == cult_mode.sacrifice_target) - return 1 - return 0 - -/datum/game_mode/cult - name = "cult" - config_tag = "cult" - restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - protected_jobs = list() - required_players = 30 - required_enemies = 3 - recommended_enemies = 4 - - var/datum/mind/sacrifice_target = null - var/finished = 0 - - - var/list/objectives = list() - - var/eldergod = 1 //for the summon god objective - var/demons_summoned = 0 - - var/acolytes_needed = 4 //for the survive objective - base number of acolytes, increased by 1 for every 10 players - var/const/min_cultists_to_start = 3 - var/const/max_cultists_to_start = 4 - var/acolytes_survived = 0 - - var/narsie_condition_cleared = 0 //allows Nar-Sie to be summonned during cult rounds. set to 1 once the cult reaches the second phase. - var/current_objective = 1 //equals the number of cleared objectives + 1 - var/prenarsie_objectives = 2 //how many objectives at most before the cult gets to summon narsie - var/list/bloody_floors = list() - var/spilltarget = 100 //how many floor tiles must be covered in blood to complete the bloodspill objective - var/convert_target = 0 //how many members the cult needs to reach to complete the convert objective - var/harvested = 0 - - var/list/sacrificed = list() //contains the mind of the sacrifice target ONCE the sacrifice objective has been completed - var/mass_convert = 0 //set to 1 if the convert objective has been accomplised once that round - var/spilled_blood = 0 //set to 1 if the bloodspill objective has been accomplised once that round - var/max_spilled_blood = 0 //highest quantity of blood covered tiles during the round - var/bonus = 0 //set to 1 if the cult has completed the bonus (third phase) objective (harvest, hijack, massacre) - - var/harvest_target = 10 - var/massacre_target = 5 - - var/escaped_shuttle = 0 - var/escaped_pod = 0 - var/survivors = 0 - -/datum/game_mode/cult/announce() - to_chat(world, "The current game mode is - Cult!") - to_chat(world, "Some crewmembers are attempting to start a cult!
    \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult.
    \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.
    ") - - -/datum/game_mode/cult/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - ..() - var/list/cultists_possible = get_players_for_role(ROLE_CULTIST) - - for(var/cultists_number = 1 to max_cultists_to_start) - if(!cultists_possible.len) - break - var/datum/mind/cultist = pick(cultists_possible) - cultists_possible -= cultist - cult += cultist - cultist.restricted_roles = restricted_jobs - cultist.special_role = SPECIAL_ROLE_CULTIST - ..() - return (cult.len>0) - - -/datum/game_mode/cult/post_setup() - modePlayer += cult - acolytes_needed = acolytes_needed + round((num_players_started() / 10)) - - if(!summon_spots.len) - while(summon_spots.len < SUMMON_POSSIBILITIES) - var/area/summon = pick(return_sorted_areas() - summon_spots) - if(summon && is_station_level(summon.z) && summon.valid_territory) - summon_spots += summon - - for(var/datum/mind/cult_mind in cult) - SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') - equip_cultist(cult_mind.current) - cult_mind.current.faction |= "cult" - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - update_cult_icons_added(cult_mind) - to_chat(cult_mind.current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") - - first_phase() - - ..() - - -/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) - for(var/obj_count in 1 to objectives.len) - var/explanation - switch(objectives[obj_count]) - if("survive") - explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." - if("convert") - explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions." - if("bloodspill") - explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles." - if("sacrifice") - if(sacrifice_target) - explanation = "Sacrifice [sacrifice_target.current.real_name], the [sacrifice_target.assigned_role]. You will need the sacrifice rune and three acolytes to do so." - else - explanation = "Free objective." - if("eldergod") - explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune.The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin." - to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") - cult_mind.memory += "Objective #[obj_count]: [explanation]
    " - - -/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if(mob.mind) - if(mob.mind.assigned_role == "Clown") - to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - mob.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(mob) - var/obj/item/paper/talisman/supply/T = new(mob) - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - if(!where) - to_chat(mob, "Unfortunately, you weren't able to get a talisman. This is very bad and you should adminhelp immediately.") - else - to_chat(mob, "You have a talisman in your [where], one that will help you start the cult on this station. Use it well and remember - there are others.") - mob.update_icons() - return 1 - - -/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE - if(!istype(cult_mind)) - return 0 - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(!(cult_mind in cult)) - cult += cult_mind - cult_mind.current.faction |= "cult" - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') - cult_mind.current.create_attack_log("Has been converted to the cult!") - if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) - replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) - update_cult_icons_added(cult_mind) - cult_mode.memorize_cult_objectives(cult_mind) - if(GAMEMODE_IS_CULT) - cult_mode.check_numbers() - return 1 - -/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) - if(cult_mind in cult) - cult -= cult_mind - to_chat(cult_mind.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") - cult_mind.current.faction -= "cult" - cult_mind.memory = "" - cult_mind.special_role = null - for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions) - qdel(C) - update_cult_icons_removed(cult_mind) - if(show_message) - for(var/mob/M in viewers(cult_mind.current)) - to_chat(M, "[cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!") - - -/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.join_hud(cult_mind.current) - set_antag_hud(cult_mind.current, "hudcultist") - - -/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.leave_hud(cult_mind.current) - set_antag_hud(cult_mind.current, null) - -/datum/game_mode/proc/update_cult_comms_added(datum/mind/cult_mind) - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - -/datum/game_mode/cult/proc/get_unconvertables() - var/list/ucs = list() - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!is_convertable_to_cult(player.mind)) - ucs += player.mind - return ucs - - -/datum/game_mode/cult/proc/check_cult_victory() - var/cult_fail = 0 - if(objectives.Find("survive")) - cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise - if(objectives.Find("eldergod")) - cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once - if(objectives.Find("slaughter")) - if(!demons_summoned) - cult_fail++ - if(objectives.Find("sacrifice")) - if(sacrifice_target && !(sacrifice_target in sacrificed)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail - cult_fail++ - if(objectives.Find("convert")) - if(cult.len < convert_target) - cult_fail++ - if(objectives.Find("bloodspill")) - if(max_spilled_blood < spilltarget) - cult_fail++ - - return cult_fail //if any objectives aren't met, failure - - -/datum/game_mode/cult/proc/check_survive() - acolytes_survived = 0 - for(var/datum/mind/cult_mind in cult) - if(cult_mind.current && cult_mind.current.stat!=2) - var/area/A = get_area(cult_mind.current ) - if( is_type_in_list(A, centcom_areas)) - acolytes_survived++ - else if(A == SSshuttle.emergency.areaInstance && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //snowflaked into objectives because shitty bay shuttles had areas to auto-determine this - acolytes_survived++ - - if(acolytes_survived>=acolytes_needed) - return 0 - else - return 1 - - -/atom/proc/cult_log(var/message) - investigate_log(message, "cult") - - -/datum/game_mode/cult/declare_completion() - bonus_check() - - if(!check_cult_victory()) - feedback_set_details("round_end_result","cult win - cult win") - feedback_set("round_end_result",acolytes_survived) - to_chat(world, " The cult wins! It has succeeded in serving its dark masters!") - else - feedback_set_details("round_end_result","cult loss - staff stopped the cult") - feedback_set("round_end_result",acolytes_survived) - to_chat(world, " The staff managed to stop the cult!") - - var/text = "Cultists escaped: [acolytes_survived]" - - if(objectives.len) - text += "
    The cultists' objectives were:" - for(var/obj_count=1, obj_count <= objectives.len, obj_count++) - var/explanation - switch(objectives[obj_count]) - if("survive") - if(!check_survive()) - explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!" - feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]") - else - explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail." - feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") - if("sacrifice") - if(sacrifice_target) - if(sacrifice_target in sacrificed) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" - feedback_add_details("cult_objective","cult_sacrifice|SUCCESS") - else if(sacrifice_target && sacrifice_target.current) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." - feedback_add_details("cult_objective","cult_sacrifice|FAIL") - else - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." - feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") - if("eldergod") - if(!eldergod) - explanation = "Summon [SSticker.cultdat.entity_name]. Success!" - feedback_add_details("cult_objective","cult_narsie|SUCCESS") - else - explanation = "Summon [SSticker.cultdat.entity_name]. Fail." - feedback_add_details("cult_objective","cult_narsie|FAIL") - if("slaughter") - if(demons_summoned) - explanation = "Bring the Slaughter. Success!" - feedback_add_details("cult_objective","cult_demons|SUCCESS") - else - explanation = "Bring the Slaughter. Fail." - feedback_add_details("cult_objective","cult_demons|FAIL") - - if("convert")//convert half the crew - if(cult.len >= convert_target) - explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!" - feedback_add_details("cult_objective","cult_convertion|SUCCESS") - else - explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!" - feedback_add_details("cult_objective","cult_convertion|FAIL") - - if("bloodspill")//cover a large portion of the station in blood - if(max_spilled_blood >= spilltarget) - explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!" - feedback_add_details("cult_objective","cult_bloodspill|SUCCESS") - else - explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!" - feedback_add_details("cult_objective","cult_bloodspill|FAIL") - - if("harvest") - if(harvested > harvest_target) - explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!" - feedback_add_details("cult_objective","cult_harvest|SUCCESS") - else - explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!" - feedback_add_details("cult_objective","cult_harvest|FAIL") - - if("hijack") - if(!escaped_shuttle) - explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!" - feedback_add_details("cult_objective","cult_hijack|SUCCESS") - else - explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!" - feedback_add_details("cult_objective","cult_hijack|FAIL") - - if("massacre") - if(survivors < massacre_target) - explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!" - feedback_add_details("cult_objective","cult_massacre|SUCCESS") - else - explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!" - feedback_add_details("cult_objective","cult_massacre|FAIL") - - text += "
    Objective #[obj_count]: [explanation]" - - to_chat(world, text) - ..() - return 1 - - -/datum/game_mode/proc/auto_declare_completion_cult() - if(cult.len || (SSticker && GAMEMODE_IS_CULT)) - var/text = "The cultists were:" - for(var/datum/mind/cultist in cult) - - text += "
    [cultist.key] was [cultist.name] (" - if(cultist.current) - if(cultist.current.stat == DEAD) - text += "died" - else - text += "survived" - if(cultist.current.real_name != cultist.name) - text += " as [cultist.current.real_name]" - else - text += "body destroyed" - text += ")" - - to_chat(world, text) +GLOBAL_LIST_EMPTY(all_cults) + +/datum/game_mode + var/list/datum/mind/cult = list() + +/proc/iscultist(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.cult) + + +/proc/is_convertable_to_cult(datum/mind/mind) + if(!mind) + return FALSE + if(!mind.current) + return FALSE + if(iscultist(mind.current)) + return TRUE //If they're already in the cult, assume they are convertable + if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain"))) + return FALSE + if(ishuman(mind.current)) + var/mob/living/carbon/human/H = mind.current + if(ismindshielded(H)) //mindshield protects against conversions unless removed + return FALSE +// if(mind.offstation_role) cant convert offstation roles such as ghost spawns +// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew + if(issilicon(mind.current)) + return FALSE //can't convert machines, that's ratvar's thing + if(isguardian(mind.current)) + var/mob/living/simple_animal/hostile/guardian/G = mind.current + if(!iscultist(G.summoner)) + return FALSE //can't convert it unless the owner is converted + if(isgolem(mind.current)) + return FALSE + return TRUE + +/proc/is_sacrifice_target(datum/mind/mind) + if(SSticker.mode.name == "cult") + var/datum/game_mode/cult/cult_mode = SSticker.mode + if(mind == cult_mode.sacrifice_target) + return 1 + return 0 + +/datum/game_mode/cult + name = "cult" + config_tag = "cult" + restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_jobs = list() + required_players = 30 + required_enemies = 3 + recommended_enemies = 4 + + var/datum/mind/sacrifice_target = null + var/finished = 0 + + + var/list/objectives = list() + + var/eldergod = 1 //for the summon god objective + var/demons_summoned = 0 + + var/acolytes_needed = 4 //for the survive objective - base number of acolytes, increased by 1 for every 10 players + var/const/min_cultists_to_start = 3 + var/const/max_cultists_to_start = 4 + var/acolytes_survived = 0 + + var/narsie_condition_cleared = 0 //allows Nar-Sie to be summonned during cult rounds. set to 1 once the cult reaches the second phase. + var/current_objective = 1 //equals the number of cleared objectives + 1 + var/prenarsie_objectives = 2 //how many objectives at most before the cult gets to summon narsie + var/list/bloody_floors = list() + var/spilltarget = 100 //how many floor tiles must be covered in blood to complete the bloodspill objective + var/convert_target = 0 //how many members the cult needs to reach to complete the convert objective + var/harvested = 0 + + var/list/sacrificed = list() //contains the mind of the sacrifice target ONCE the sacrifice objective has been completed + var/mass_convert = 0 //set to 1 if the convert objective has been accomplised once that round + var/spilled_blood = 0 //set to 1 if the bloodspill objective has been accomplised once that round + var/max_spilled_blood = 0 //highest quantity of blood covered tiles during the round + var/bonus = 0 //set to 1 if the cult has completed the bonus (third phase) objective (harvest, hijack, massacre) + + var/harvest_target = 10 + var/massacre_target = 5 + + var/escaped_shuttle = 0 + var/escaped_pod = 0 + var/survivors = 0 + +/datum/game_mode/cult/announce() + to_chat(world, "The current game mode is - Cult!") + to_chat(world, "Some crewmembers are attempting to start a cult!
    \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult.
    \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.
    ") + + +/datum/game_mode/cult/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + ..() + var/list/cultists_possible = get_players_for_role(ROLE_CULTIST) + + for(var/cultists_number = 1 to max_cultists_to_start) + if(!cultists_possible.len) + break + var/datum/mind/cultist = pick(cultists_possible) + cultists_possible -= cultist + cult += cultist + cultist.restricted_roles = restricted_jobs + cultist.special_role = SPECIAL_ROLE_CULTIST + ..() + return (cult.len>0) + + +/datum/game_mode/cult/post_setup() + modePlayer += cult + acolytes_needed = acolytes_needed + round((num_players_started() / 10)) + + if(!GLOB.summon_spots.len) + while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES) + var/area/summon = pick(return_sorted_areas() - GLOB.summon_spots) + if(summon && is_station_level(summon.z) && summon.valid_territory) + GLOB.summon_spots += summon + + for(var/datum/mind/cult_mind in cult) + SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') + equip_cultist(cult_mind.current) + cult_mind.current.faction |= "cult" + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + update_cult_icons_added(cult_mind) + to_chat(cult_mind.current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") + + first_phase() + + ..() + + +/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) + for(var/obj_count in 1 to objectives.len) + var/explanation + switch(objectives[obj_count]) + if("survive") + explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." + if("convert") + explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions." + if("bloodspill") + explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles." + if("sacrifice") + if(sacrifice_target) + explanation = "Sacrifice [sacrifice_target.current.real_name], the [sacrifice_target.assigned_role]. You will need the sacrifice rune and three acolytes to do so." + else + explanation = "Free objective." + if("eldergod") + explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune.The summoning can only be accomplished in [english_list(GLOB.summon_spots)] - where the veil is weak enough for the ritual to begin." + to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") + cult_mind.memory += "Objective #[obj_count]: [explanation]
    " + + +/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if(mob.mind) + if(mob.mind.assigned_role == "Clown") + to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + mob.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(mob) + var/obj/item/paper/talisman/supply/T = new(mob) + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(mob, "Unfortunately, you weren't able to get a talisman. This is very bad and you should adminhelp immediately.") + else + to_chat(mob, "You have a talisman in your [where], one that will help you start the cult on this station. Use it well and remember - there are others.") + mob.update_icons() + return 1 + + +/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE + if(!istype(cult_mind)) + return 0 + var/datum/game_mode/cult/cult_mode = SSticker.mode + if(!(cult_mind in cult)) + cult += cult_mind + cult_mind.current.faction |= "cult" + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') + cult_mind.current.create_attack_log("Has been converted to the cult!") + cult_mind.current.create_log(CONVERSION_LOG, "converted to the cult") + if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) + replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) + update_cult_icons_added(cult_mind) + cult_mode.memorize_cult_objectives(cult_mind) + if(GAMEMODE_IS_CULT) + cult_mode.check_numbers() + return 1 + +/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) + if(cult_mind in cult) + cult -= cult_mind + to_chat(cult_mind.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") + cult_mind.current.faction -= "cult" + cult_mind.memory = "" + cult_mind.special_role = null + for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions) + qdel(C) + update_cult_icons_removed(cult_mind) + if(show_message) + for(var/mob/M in viewers(cult_mind.current)) + to_chat(M, "[cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!") + + +/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = GLOB.huds[ANTAG_HUD_CULT] + culthud.join_hud(cult_mind.current) + set_antag_hud(cult_mind.current, "hudcultist") + + +/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = GLOB.huds[ANTAG_HUD_CULT] + culthud.leave_hud(cult_mind.current) + set_antag_hud(cult_mind.current, null) + +/datum/game_mode/proc/update_cult_comms_added(datum/mind/cult_mind) + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + +/datum/game_mode/cult/proc/get_unconvertables() + var/list/ucs = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!is_convertable_to_cult(player.mind)) + ucs += player.mind + return ucs + + +/datum/game_mode/cult/proc/check_cult_victory() + var/cult_fail = 0 + if(objectives.Find("survive")) + cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise + if(objectives.Find("eldergod")) + cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once + if(objectives.Find("slaughter")) + if(!demons_summoned) + cult_fail++ + if(objectives.Find("sacrifice")) + if(sacrifice_target && !(sacrifice_target in sacrificed)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail + cult_fail++ + if(objectives.Find("convert")) + if(cult.len < convert_target) + cult_fail++ + if(objectives.Find("bloodspill")) + if(max_spilled_blood < spilltarget) + cult_fail++ + + return cult_fail //if any objectives aren't met, failure + + +/datum/game_mode/cult/proc/check_survive() + acolytes_survived = 0 + for(var/datum/mind/cult_mind in cult) + if(cult_mind.current && cult_mind.current.stat!=2) + var/area/A = get_area(cult_mind.current ) + if( is_type_in_list(A, GLOB.centcom_areas)) + acolytes_survived++ + else if(A == SSshuttle.emergency.areaInstance && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //snowflaked into objectives because shitty bay shuttles had areas to auto-determine this + acolytes_survived++ + + if(acolytes_survived>=acolytes_needed) + return 0 + else + return 1 + + +/atom/proc/cult_log(var/message) + investigate_log(message, "cult") + + +/datum/game_mode/cult/declare_completion() + bonus_check() + + if(!check_cult_victory()) + feedback_set_details("round_end_result","cult win - cult win") + feedback_set("round_end_result",acolytes_survived) + to_chat(world, " The cult wins! It has succeeded in serving its dark masters!") + else + feedback_set_details("round_end_result","cult loss - staff stopped the cult") + feedback_set("round_end_result",acolytes_survived) + to_chat(world, " The staff managed to stop the cult!") + + var/text = "Cultists escaped: [acolytes_survived]" + + if(objectives.len) + text += "
    The cultists' objectives were:" + for(var/obj_count=1, obj_count <= objectives.len, obj_count++) + var/explanation + switch(objectives[obj_count]) + if("survive") + if(!check_survive()) + explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!" + feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]") + else + explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail." + feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") + if("sacrifice") + if(sacrifice_target) + if(sacrifice_target in sacrificed) + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" + feedback_add_details("cult_objective","cult_sacrifice|SUCCESS") + else if(sacrifice_target && sacrifice_target.current) + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." + feedback_add_details("cult_objective","cult_sacrifice|FAIL") + else + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." + feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") + if("eldergod") + if(!eldergod) + explanation = "Summon [SSticker.cultdat.entity_name]. Success!" + feedback_add_details("cult_objective","cult_narsie|SUCCESS") + else + explanation = "Summon [SSticker.cultdat.entity_name]. Fail." + feedback_add_details("cult_objective","cult_narsie|FAIL") + if("slaughter") + if(demons_summoned) + explanation = "Bring the Slaughter. Success!" + feedback_add_details("cult_objective","cult_demons|SUCCESS") + else + explanation = "Bring the Slaughter. Fail." + feedback_add_details("cult_objective","cult_demons|FAIL") + + if("convert")//convert half the crew + if(cult.len >= convert_target) + explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!" + feedback_add_details("cult_objective","cult_convertion|SUCCESS") + else + explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!" + feedback_add_details("cult_objective","cult_convertion|FAIL") + + if("bloodspill")//cover a large portion of the station in blood + if(max_spilled_blood >= spilltarget) + explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!" + feedback_add_details("cult_objective","cult_bloodspill|SUCCESS") + else + explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!" + feedback_add_details("cult_objective","cult_bloodspill|FAIL") + + if("harvest") + if(harvested > harvest_target) + explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!" + feedback_add_details("cult_objective","cult_harvest|SUCCESS") + else + explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!" + feedback_add_details("cult_objective","cult_harvest|FAIL") + + if("hijack") + if(!escaped_shuttle) + explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!" + feedback_add_details("cult_objective","cult_hijack|SUCCESS") + else + explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!" + feedback_add_details("cult_objective","cult_hijack|FAIL") + + if("massacre") + if(survivors < massacre_target) + explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!" + feedback_add_details("cult_objective","cult_massacre|SUCCESS") + else + explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!" + feedback_add_details("cult_objective","cult_massacre|FAIL") + + text += "
    Objective #[obj_count]: [explanation]" + + to_chat(world, text) + ..() + return 1 + + +/datum/game_mode/proc/auto_declare_completion_cult() + if(cult.len || (SSticker && GAMEMODE_IS_CULT)) + var/text = "The cultists were:" + for(var/datum/mind/cultist in cult) + + text += "
    [cultist.key] was [cultist.name] (" + if(cultist.current) + if(cultist.current.stat == DEAD) + text += "died" + else + text += "survived" + if(cultist.current.real_name != cultist.name) + text += " as [cultist.current.real_name]" + else + text += "body destroyed" + text += ")" + + to_chat(world, text) diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 74d7a1889e74..c0daac2a01a4 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,377 +1,377 @@ -/obj/item/melee/cultblade - name = "Cult Blade" - desc = "An arcane weapon wielded by the followers of a cult." - icon_state = "cultblade" - item_state = "cultblade" - w_class = WEIGHT_CLASS_BULKY - force = 30 - throwforce = 10 - sharp = 1 - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - -/obj/item/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) - if(!iscultist(user)) - user.Weaken(5) - user.unEquip(src, 1) - user.visible_message("A powerful force shoves [user] away from [target]!", \ - "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) - else - user.adjustBruteLoss(rand(force/2,force)) - return - ..() - -/obj/item/melee/cultblade/pickup(mob/living/user) - . = ..() - if(!iscultist(user)) - to_chat(user, "\"I wouldn't advise that.\"") - to_chat(user, "An overwhelming sense of nausea overpowers you!") - user.Dizzy(120) - - if(HULK in user.mutations) - to_chat(user, "You can't seem to hold the blade properly!") - return FALSE - -/obj/item/melee/cultblade/dagger - name = "sacrificial dagger" - desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - w_class = WEIGHT_CLASS_SMALL - force = 15 - throwforce = 25 - embed_chance = 75 - -/obj/item/melee/cultblade/dagger/attack(atom/target, mob/living/carbon/human/user) - ..() - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if((H.stat != DEAD) && !(NO_BLOOD in H.dna.species.species_traits)) - H.bleed(50) - -/obj/item/restraints/legcuffs/bola/cult - name = "runed bola" - desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim." - icon = 'icons/obj/items.dmi' - icon_state = "bola_cult" - breakouttime = 45 - weaken = 1 - -/obj/item/clothing/head/hooded/culthood - name = "cult hood" - icon_state = "culthood" - desc = "A hood worn by the followers of a cult." - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list(melee = 30, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - - -/obj/item/clothing/head/hooded/culthood/alt - icon_state = "cult_hoodalt" - item_state = "cult_hoodalt" - - -/obj/item/clothing/suit/hooded/cultrobes - name = "cult robes" - desc = "A set of armored robes worn by the followers of a cult." - icon_state = "cultrobes" - item_state = "cultrobes" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/culthood - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list("melee" = 40, "bullet" = 30, "laser" = 40, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - flags_inv = HIDEJUMPSUIT - -/obj/item/clothing/suit/hooded/cultrobes/alt - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - hoodtype = /obj/item/clothing/head/hooded/culthood/alt - -/obj/item/clothing/head/magus - name = "magus helm" - icon_state = "magus" - item_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." - flags = BLOCKHAIR - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - -/obj/item/clothing/suit/magusred - name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie" - icon_state = "magusred" - item_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - -/obj/item/clothing/head/helmet/space/cult - name = "cult helmet" - desc = "A space worthy helmet used by the followers of a cult." - icon_state = "cult_helmet" - item_state = "cult_helmet" - armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) - -/obj/item/clothing/suit/space/cult - name = "cult armor" - icon_state = "cult_armour" - item_state = "cult_armour" - desc = "A bulky suit of armor, bristling with spikes. It looks space proof." - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/tome,/obj/item/melee/cultblade,/obj/item/tank) - slowdown = 1 - armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield - name = "empowered cultist robe" - desc = "Empowered garb which creates a powerful shield around the user." - icon_state = "cult_armour" - item_state = "cult_armour" - w_class = WEIGHT_CLASS_BULKY - armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 30, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 50, "acid" = 60) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - var/current_charges = 3 - hoodtype = /obj/item/clothing/head/hooded/cult_hoodie - -/obj/item/clothing/head/hooded/cult_hoodie - name = "empowered cultist robe" - desc = "Empowered garb which creates a powerful shield around the user." - icon_state = "cult_hoodalt" - armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - body_parts_covered = HEAD - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot) - ..() - if(!iscultist(user)) - to_chat(user, "\"I wouldn't advise that.\"") - to_chat(user, "An overwhelming sense of nausea overpowers you!") - user.unEquip(src, 1) - user.Dizzy(30) - user.Weaken(5) - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(current_charges) - owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") - current_charges-- - new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) - if(!current_charges) - owner.visible_message("The runed shield around [owner] suddenly disappears!") - owner.update_inv_wear_suit() - return 1 - return 0 - -/obj/item/clothing/suit/hooded/cultrobes/berserker - name = "flagellant's robes" - desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "hardsuit-berserker" - item_state = "hardsuit-berserker" - flags_inv = HIDEJUMPSUIT - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) - slowdown = -1 - hoodtype = /obj/item/clothing/head/hooded/berserkerhood - - -/obj/item/clothing/head/hooded/berserkerhood - name = "flagellant's robes" - desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "culthood" - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/whetstone/cult - name = "eldritch whetstone" - desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone." - icon_state = "cult_sharpener" - used = 0 - increment = 5 - max = 40 - prefix = "darkened" - claw_damage_increase = 4 - -/obj/item/whetstone/cult/update_icon() - icon_state = "cult_sharpener[used ? "_used" : ""]" - -/obj/item/reagent_containers/food/drinks/bottle/unholywater - name = "flask of unholy water" - desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of a cult." - icon_state = "holyflask" - color = "#333333" - list_reagents = list("unholywater" = 40) - -/obj/item/clothing/glasses/night/cultblind - desc = "May the master guide you through the darkness and shield you from the light." - name = "zealot's blindfold" - icon_state = "blindfold" - item_state = "blindfold" - see_in_dark = 8 - flash_protect = 1 - -/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot) - ..() - if(!iscultist(user)) - to_chat(user, "\"You want to be blind, do you?\"") - user.unEquip(src, 1) - user.Dizzy(30) - user.Weaken(5) - user.EyeBlind(30) - -/obj/item/shuttle_curse - name = "cursed orb" - desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle." - icon = 'icons/obj/projectiles.dmi' - icon_state ="bluespace" - color = "#ff0000" - var/global/curselimit = 0 - -/obj/item/shuttle_curse/attack_self(mob/user) - if(!iscultist(user)) - user.unEquip(src, 1) - user.Weaken(5) - to_chat(user, "A powerful force shoves you away from [src]!") - return - if(curselimit > 1) - to_chat(user, "We have exhausted our ability to curse the shuttle.") - return - if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) - to_chat(user, "Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.") - return - - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/cursetime = 1800 - var/timer = SSshuttle.emergency.timeLeft(1) + cursetime - SSshuttle.emergency.setTimer(timer) - to_chat(user,"You shatter the orb! A dark essence spirals into the air, then disappears.") - playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1) - curselimit++ - qdel(src) - sleep(20) - var/global/list/curses - if(!curses) - curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.", - "The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.", - "The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.", - "A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.", - "A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.", - "The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.", - "Steve repeatedly touched a lightbulb until his hands fell off. The shuttle will be delayed by two minutes.") - var/message = pick(curses) - command_announcement.Announce("[message]", "System Failure", 'sound/misc/notice1.ogg') - -/obj/item/cult_shift - name = "veil shifter" - desc = "This relic teleports you forward a medium distance." - icon = 'icons/obj/cult.dmi' - icon_state ="shifter" - var/uses = 4 - -/obj/item/cult_shift/examine(mob/user) - . = ..() - if(uses) - . += "It has [uses] uses remaining." - else - . += "It seems drained." - -/obj/item/cult_shift/proc/handle_teleport_grab(turf/T, mob/user) - var/mob/living/carbon/C = user - if(C.pulling) - var/atom/movable/pulled = C.pulling - pulled.forceMove(T) - . = pulled - -/obj/item/cult_shift/attack_self(mob/user) - if(!uses || !iscarbon(user)) - to_chat(user, "\The [src] is dull and unmoving in your hands.") - return - if(!iscultist(user)) - user.unEquip(src, 1) - step(src, pick(alldirs)) - to_chat(user, "\The [src] flickers out of your hands, too eager to move!") - return - - var/outer_tele_radius = 9 - - var/mob/living/carbon/C = user - var/turf/mobloc = get_turf(C) - var/list/turfs = new/list() - for(var/turf/T in range(user, outer_tele_radius)) - if(!is_teleport_allowed(T.z)) - continue - if(get_dir(C, T) != C.dir) - continue - if(T == mobloc) - continue - if(istype(T, /turf/space)) - continue - if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius) - continue //putting them at the edge is dumb - if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius) - continue - - turfs += T - - if(turfs) - uses-- - var/turf/destination = pick(turfs) - if(uses <= 0) - icon_state ="shifter_drained" - playsound(mobloc, "sparks", 50, 1) - new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir) - - var/atom/movable/pulled = handle_teleport_grab(destination, C) - C.forceMove(destination) - if(pulled) - C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull - - new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir) - playsound(destination, 'sound/effects/phasein.ogg', 25, 1) - playsound(destination, "sparks", 50, 1) - - else - to_chat(C, "The veil cannot be torn here!") - -/obj/item/melee/cultblade/ghost - name = "eldritch sword" - force = 15 - flags = NODROP | DROPDEL - -/obj/item/clothing/head/hooded/culthood/alt/ghost - flags = NODROP | DROPDEL - -/obj/item/clothing/suit/cultrobesghost - name = "ghostly cult robes" - desc = "A set of ethreal armored robes worn by the undead followers of a cult." - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) - flags_inv = HIDEJUMPSUIT - - flags = NODROP | DROPDEL - - -/obj/item/clothing/shoes/cult/ghost - flags = NODROP | DROPDEL - -/datum/outfit/ghost_cultist - name = "Cultist Ghost" - - uniform = /obj/item/clothing/under/color/black - suit = /obj/item/clothing/suit/cultrobesghost - shoes = /obj/item/clothing/shoes/cult/ghost - head = /obj/item/clothing/head/hooded/culthood/alt/ghost - r_hand = /obj/item/melee/cultblade/ghost \ No newline at end of file +/obj/item/melee/cultblade + name = "Cult Blade" + desc = "An arcane weapon wielded by the followers of a cult." + icon_state = "cultblade" + item_state = "cultblade" + w_class = WEIGHT_CLASS_BULKY + force = 30 + throwforce = 10 + sharp = 1 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + +/obj/item/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) + if(!iscultist(user)) + user.Weaken(5) + user.unEquip(src, 1) + user.visible_message("A powerful force shoves [user] away from [target]!", \ + "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) + else + user.adjustBruteLoss(rand(force/2,force)) + return + ..() + +/obj/item/melee/cultblade/pickup(mob/living/user) + . = ..() + if(!iscultist(user)) + to_chat(user, "\"I wouldn't advise that.\"") + to_chat(user, "An overwhelming sense of nausea overpowers you!") + user.Dizzy(120) + + if(HULK in user.mutations) + to_chat(user, "You can't seem to hold the blade properly!") + return FALSE + +/obj/item/melee/cultblade/dagger + name = "sacrificial dagger" + desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + w_class = WEIGHT_CLASS_SMALL + force = 15 + throwforce = 25 + embed_chance = 75 + +/obj/item/melee/cultblade/dagger/attack(atom/target, mob/living/carbon/human/user) + ..() + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if((H.stat != DEAD) && !(NO_BLOOD in H.dna.species.species_traits)) + H.bleed(50) + +/obj/item/restraints/legcuffs/bola/cult + name = "runed bola" + desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim." + icon = 'icons/obj/items.dmi' + icon_state = "bola_cult" + breakouttime = 45 + weaken = 1 + +/obj/item/clothing/head/hooded/culthood + name = "cult hood" + icon_state = "culthood" + desc = "A hood worn by the followers of a cult." + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list(melee = 30, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + + +/obj/item/clothing/head/hooded/culthood/alt + icon_state = "cult_hoodalt" + item_state = "cult_hoodalt" + + +/obj/item/clothing/suit/hooded/cultrobes + name = "cult robes" + desc = "A set of armored robes worn by the followers of a cult." + icon_state = "cultrobes" + item_state = "cultrobes" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/culthood + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list("melee" = 40, "bullet" = 30, "laser" = 40, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + flags_inv = HIDEJUMPSUIT + +/obj/item/clothing/suit/hooded/cultrobes/alt + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + hoodtype = /obj/item/clothing/head/hooded/culthood/alt + +/obj/item/clothing/head/magus + name = "magus helm" + icon_state = "magus" + item_state = "magus" + desc = "A helm worn by the followers of Nar-Sie." + flags = BLOCKHAIR + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + +/obj/item/clothing/suit/magusred + name = "magus robes" + desc = "A set of armored robes worn by the followers of Nar-Sie" + icon_state = "magusred" + item_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + +/obj/item/clothing/head/helmet/space/cult + name = "cult helmet" + desc = "A space worthy helmet used by the followers of a cult." + icon_state = "cult_helmet" + item_state = "cult_helmet" + armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) + +/obj/item/clothing/suit/space/cult + name = "cult armor" + icon_state = "cult_armour" + item_state = "cult_armour" + desc = "A bulky suit of armor, bristling with spikes. It looks space proof." + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/tome,/obj/item/melee/cultblade,/obj/item/tank) + slowdown = 1 + armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield + name = "empowered cultist robe" + desc = "Empowered garb which creates a powerful shield around the user." + icon_state = "cult_armour" + item_state = "cult_armour" + w_class = WEIGHT_CLASS_BULKY + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 30, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 50, "acid" = 60) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + var/current_charges = 3 + hoodtype = /obj/item/clothing/head/hooded/cult_hoodie + +/obj/item/clothing/head/hooded/cult_hoodie + name = "empowered cultist robe" + desc = "Empowered garb which creates a powerful shield around the user." + icon_state = "cult_hoodalt" + armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + body_parts_covered = HEAD + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot) + ..() + if(!iscultist(user)) + to_chat(user, "\"I wouldn't advise that.\"") + to_chat(user, "An overwhelming sense of nausea overpowers you!") + user.unEquip(src, 1) + user.Dizzy(30) + user.Weaken(5) + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(current_charges) + owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") + current_charges-- + new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) + if(!current_charges) + owner.visible_message("The runed shield around [owner] suddenly disappears!") + owner.update_inv_wear_suit() + return 1 + return 0 + +/obj/item/clothing/suit/hooded/cultrobes/berserker + name = "flagellant's robes" + desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." + icon_state = "hardsuit-berserker" + item_state = "hardsuit-berserker" + flags_inv = HIDEJUMPSUIT + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) + slowdown = -1 + hoodtype = /obj/item/clothing/head/hooded/berserkerhood + + +/obj/item/clothing/head/hooded/berserkerhood + name = "flagellant's robes" + desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." + icon_state = "culthood" + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/whetstone/cult + name = "eldritch whetstone" + desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone." + icon_state = "cult_sharpener" + used = 0 + increment = 5 + max = 40 + prefix = "darkened" + claw_damage_increase = 4 + +/obj/item/whetstone/cult/update_icon() + icon_state = "cult_sharpener[used ? "_used" : ""]" + +/obj/item/reagent_containers/food/drinks/bottle/unholywater + name = "flask of unholy water" + desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of a cult." + icon_state = "holyflask" + color = "#333333" + list_reagents = list("unholywater" = 40) + +/obj/item/clothing/glasses/night/cultblind + desc = "May the master guide you through the darkness and shield you from the light." + name = "zealot's blindfold" + icon_state = "blindfold" + item_state = "blindfold" + see_in_dark = 8 + flash_protect = 1 + +/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot) + ..() + if(!iscultist(user)) + to_chat(user, "\"You want to be blind, do you?\"") + user.unEquip(src, 1) + user.Dizzy(30) + user.Weaken(5) + user.EyeBlind(30) + +/obj/item/shuttle_curse + name = "cursed orb" + desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle." + icon = 'icons/obj/projectiles.dmi' + icon_state ="bluespace" + color = "#ff0000" + var/global/curselimit = 0 + +/obj/item/shuttle_curse/attack_self(mob/user) + if(!iscultist(user)) + user.unEquip(src, 1) + user.Weaken(5) + to_chat(user, "A powerful force shoves you away from [src]!") + return + if(curselimit > 1) + to_chat(user, "We have exhausted our ability to curse the shuttle.") + return + if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) + to_chat(user, "Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.") + return + + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/cursetime = 1800 + var/timer = SSshuttle.emergency.timeLeft(1) + cursetime + SSshuttle.emergency.setTimer(timer) + to_chat(user,"You shatter the orb! A dark essence spirals into the air, then disappears.") + playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1) + curselimit++ + qdel(src) + sleep(20) + var/global/list/curses + if(!curses) + curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.", + "The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.", + "The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.", + "A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.", + "A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.", + "The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.", + "Steve repeatedly touched a lightbulb until his hands fell off. The shuttle will be delayed by two minutes.") + var/message = pick(curses) + GLOB.command_announcement.Announce("[message]", "System Failure", 'sound/misc/notice1.ogg') + +/obj/item/cult_shift + name = "veil shifter" + desc = "This relic teleports you forward a medium distance." + icon = 'icons/obj/cult.dmi' + icon_state ="shifter" + var/uses = 4 + +/obj/item/cult_shift/examine(mob/user) + . = ..() + if(uses) + . += "It has [uses] uses remaining." + else + . += "It seems drained." + +/obj/item/cult_shift/proc/handle_teleport_grab(turf/T, mob/user) + var/mob/living/carbon/C = user + if(C.pulling) + var/atom/movable/pulled = C.pulling + pulled.forceMove(T) + . = pulled + +/obj/item/cult_shift/attack_self(mob/user) + if(!uses || !iscarbon(user)) + to_chat(user, "\The [src] is dull and unmoving in your hands.") + return + if(!iscultist(user)) + user.unEquip(src, 1) + step(src, pick(GLOB.alldirs)) + to_chat(user, "\The [src] flickers out of your hands, too eager to move!") + return + + var/outer_tele_radius = 9 + + var/mob/living/carbon/C = user + var/turf/mobloc = get_turf(C) + var/list/turfs = new/list() + for(var/turf/T in range(user, outer_tele_radius)) + if(!is_teleport_allowed(T.z)) + continue + if(get_dir(C, T) != C.dir) + continue + if(T == mobloc) + continue + if(istype(T, /turf/space)) + continue + if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius) + continue //putting them at the edge is dumb + if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius) + continue + + turfs += T + + if(turfs) + uses-- + var/turf/destination = pick(turfs) + if(uses <= 0) + icon_state ="shifter_drained" + playsound(mobloc, "sparks", 50, 1) + new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir) + + var/atom/movable/pulled = handle_teleport_grab(destination, C) + C.forceMove(destination) + if(pulled) + C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull + + new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir) + playsound(destination, 'sound/effects/phasein.ogg', 25, 1) + playsound(destination, "sparks", 50, 1) + + else + to_chat(C, "The veil cannot be torn here!") + +/obj/item/melee/cultblade/ghost + name = "eldritch sword" + force = 15 + flags = NODROP | DROPDEL + +/obj/item/clothing/head/hooded/culthood/alt/ghost + flags = NODROP | DROPDEL + +/obj/item/clothing/suit/cultrobesghost + name = "ghostly cult robes" + desc = "A set of ethreal armored robes worn by the undead followers of a cult." + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) + flags_inv = HIDEJUMPSUIT + + flags = NODROP | DROPDEL + + +/obj/item/clothing/shoes/cult/ghost + flags = NODROP | DROPDEL + +/datum/outfit/ghost_cultist + name = "Cultist Ghost" + + uniform = /obj/item/clothing/under/color/black + suit = /obj/item/clothing/suit/cultrobesghost + shoes = /obj/item/clothing/shoes/cult/ghost + head = /obj/item/clothing/head/hooded/culthood/alt/ghost + r_hand = /obj/item/melee/cultblade/ghost diff --git a/code/game/gamemodes/cult/cult_objectives.dm b/code/game/gamemodes/cult/cult_objectives.dm index 83ae3c2f1b7e..80b889a5bfba 100644 --- a/code/game/gamemodes/cult/cult_objectives.dm +++ b/code/game/gamemodes/cult/cult_objectives.dm @@ -114,10 +114,10 @@ if(prob(40))//split the chance of this objectives += "eldergod" - explanation = "Summon [SSticker.cultdat.entity_name] on the Station via the use of the Tear Reality rune. The veil is weak enough in [english_list(summon_spots)] for the ritual to begin." + explanation = "Summon [SSticker.cultdat.entity_name] on the Station via the use of the Tear Reality rune. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin." else objectives += "slaughter" - explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(summon_spots)] for the ritual to begin." + explanation = "Bring the Slaughter via the rune 'Bring forth the slaughter'. The veil is weak enough in [english_list(GLOB.summon_spots)] for the ritual to begin." for(var/datum/mind/cult_mind in cult) if(cult_mind) @@ -174,7 +174,7 @@ updated_memory = replacetext("[cult_mind.memory]", "[previous_target]", "[sacrifice_target]") updated_memory = replacetext("[updated_memory]", "[previous_role]", "[sacrifice_target.assigned_role]") cult_mind.memory = updated_memory - + /datum/game_mode/cult/proc/pick_objective() var/list/possible_objectives = list() @@ -254,7 +254,7 @@ for(var/mob/living/L in GLOB.player_list) if(L.stat != DEAD && !(L.mind in cult)) var/area/A = get_area(L) - if(is_type_in_list(A.loc, centcom_areas)) + if(is_type_in_list(A.loc, GLOB.centcom_areas)) escaped_shuttle++ if(!escaped_shuttle) bonus = 1 @@ -266,4 +266,4 @@ if(is_station_level(T.z)) //we're only interested in the remaining humans on the station survivors++ if(survivors < massacre_target) - bonus = 1 \ No newline at end of file + bonus = 1 diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index de8846d292ef..618abec44702 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -152,7 +152,7 @@ return 1 return ..() -var/list/blacklisted_pylon_turfs = typecacheof(list( +GLOBAL_LIST_INIT(blacklisted_pylon_turfs, typecacheof(list( /turf/simulated/floor/engine/cult, /turf/space, /turf/simulated/floor/plating/lava, @@ -160,7 +160,7 @@ var/list/blacklisted_pylon_turfs = typecacheof(list( /turf/simulated/wall/cult, /turf/simulated/wall/cult/artificer, /turf/unsimulated/wall - )) + ))) /obj/structure/cult/functional/pylon name = "pylon" @@ -214,7 +214,7 @@ var/list/blacklisted_pylon_turfs = typecacheof(list( if(istype(T, /turf/simulated/floor/engine/cult)) cultturfs |= T continue - if(is_type_in_typecache(T, blacklisted_pylon_turfs)) + if(is_type_in_typecache(T, GLOB.blacklisted_pylon_turfs)) continue else validturfs |= T diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index a32f7219ca87..9422ffcc7166 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,320 +1,320 @@ -#define CULT_ELDERGOD "eldergod" -#define CULT_SLAUGHTER "slaughter" - -/obj/effect/rune/proc/fizzle() - if(istype(src,/obj/effect/rune)) - usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - for (var/mob/V in viewers(src)) - V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) - return - -/obj/effect/rune/proc/check_icon() - if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way - var/bits = make_bit_triplet() - icon = get_rune(bits) - else - icon = get_rune_cult(invocation) - -/obj/item/tome - name = "arcane tome" - desc = "An old, dusty tome with frayed edges and a sinister-looking cover." - icon_state ="tome" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/scribereduct = 0 - var/canbypass = 0 //ADMINBUS - -/obj/item/tome/accursed - name = "accursed tome" - desc = "An arcane tome still empowered with a shadow of its former consecration." - scribereduct = 30 //faster because it's made by corrupting a bible - -/obj/item/tome/imbued //Admin-only tome, allows instant drawing of runes - name = "imbued arcane tome" - desc = "An arcane tome granted by the Geometer itself." - scribereduct = 50 - canbypass = 1 - -/obj/item/tome/New() - if(!SSticker.mode) - icon_state = "tome" - else - icon_state = SSticker.cultdat.tome_icon - ..() - -/obj/item/tome/examine(mob/user) - . = ..() - if(iscultist(user) || user.stat == DEAD) - . += "The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]." - . += "Striking another cultist with it will purge holy water from them." - . += "Striking a noncultist, however, will sear their flesh." - -/obj/item/tome/attack(mob/living/M, mob/living/user) - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion - to_chat(user, "You remove the taint from [M].") - var/holy2unholy = M.reagents.get_reagent_amount("holywater") - M.reagents.del_reagent("holywater") - M.reagents.add_reagent("unholywater",holy2unholy) - add_attack_logs(user, M, "Hit with [src], removing the holy water from them") - return - M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 - playsound(M, 'sound/weapons/sear.ogg', 50, 1) - M.visible_message("[user] strikes [M] with the arcane tome!", \ - "[user] strikes you with the tome, searing your flesh!") - flick("tome_attack", src) - user.do_attack_animation(M) - add_attack_logs(user, M, "Hit with [src]") - -/obj/item/tome/attack_self(mob/user) - if(!iscultist(user)) - to_chat(user, "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?") - return - open_tome(user) - -/obj/item/tome/proc/open_tome(mob/user) - var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel") - switch(choice) - if("More Information") - read_tome(user) - if("Scribe Rune") - scribe_rune(user) - if("Cancel") - return - -/obj/item/tome/proc/read_tome(mob/user) - var/text = list() - text += "
    Archives of [SSticker.cultdat.entity_title1]



    " - text += "A rune's name and effects can be revealed by examining the rune.<

    " - - text += "Rite of Binding
    This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \ - invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.

    " - - text += "Teleport
    This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \ - If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.

    " - - text += "Rite of Enlightenment
    This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \ - To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshielded or a certain assignment, they will \ - be unable to be converted. People [SSticker.cultdat.entity_title3] wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.
    \ - Successful conversions will produce a tome for the new cultist.

    " - - text += "Rite of Tribute
    This rune is necessary to achieve your goals. Simply place any dead creature upon the rune and invoke it (this will not \ - target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew or your cult's target will require 3 cultists. \ - Soulstones used on construct shells will move that soul into a powerful construct of your choice.

    " - - - text += "Rite of Resurrection
    This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \ - the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \ - within a few seconds, they will be brought back to life, healed of all ailments.

    " - - text += "Rite of Disruption
    Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \ - hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \ - rune is activated.

    " - - text += "Astral Communion
    This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \ - user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \ - The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \ - also be used to commune with the dead.

    " - - text += "Rite of the Corporeal Shield
    While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \ - rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \ - undo this effect and allow passage again.

    " - - text += "Rite of Joined Souls
    This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \ - any location. It requires two invokers, and will damage each invoker slightly.

    " - - text += "Blood Boil
    When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \ - It requires three invokers.

    " - - text += "Leeching
    When invoked, this rune will transfer lifeforce from the victim to the invoker.

    " - - text += "Rite of Spectral Manifestation
    This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and [SSticker.cultdat.entity_title3]. \ - However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \ - multiple spirits with one rune, but you will rapidly take damage in doing so.

    " - - text += "Ritual of Dimensional Rending
    This rune is necessary to achieve your goals. On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \ - and does massive damage to the one attempting to scribe it.
    Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon [SSticker.cultdat.entity_title3], [SSticker.cultdat.entity_name]. \ - This will complete your objectives.


    " - - text += "Talisman of Teleportation
    The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.

    " - - text += "Talisman of Fabrication
    This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to receive a soul to occupy it.

    " - - text += "Talisman of Tome Summoning
    This talisman will produce a single tome at your feet.

    " - - text += "Talisman of Veiling/Revealing
    This talisman will hide runes on its first use, and on the second, will reveal runes.

    " - - text += "Talisman of Disguising
    This talisman will permanently disguise all nearby runes as crayon runes.

    " - - text += "Talisman of Electromagnetic Pulse
    This talisman will EMP anything else nearby. It disappears after one use.

    " - - text += "Talisman of Stunning
    Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \ - Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.

    " - - text += "Talisman of Armaments
    The Talisman of Arming will equip the user with armored robes, a backpack, shoes, an eldritch longsword, and an empowered bola. Any equipment that cannot \ - be equipped will not be summoned, weaponry will be put on the floor below the user. Attacking a fellow cultist with it will instead equip them.

    " - - text += "Talisman of Horrors
    The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the end-times that may incapacitate them.

    " - - text += "Talisman of Shackling
    The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.

    " - - text += "In addition to these runes, the cult has a small selection of equipment and constructs.

    " - - text += "Equipment:

    " - - text += "Cult Blade
    Cult blades are sharp weapons that, notably, cannot be used by non-cultists. These blades are produced by the Talisman of Arming.

    " - - text += "Cult Bola
    Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.

    " - - text += "Cult Robes
    Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.

    " - - text += "Soulstone
    A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \ -
    The soul within can also be released as a Shade by using it in-hand.

    " - - text += "Construct Shell
    A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a Wraith, a Juggernaut, or an Artificer. \ -
    Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.

    " - - text += "Constructs:

    " - - text += "Shade
    While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.

    " - - text += "Wraith
    The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.

    " - - text += "Juggernaut
    The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.

    " - - text += "Artificer
    The Artificer is a weak and fragile construct, able to heal other constructs, produce more soulstones and construct shells, \ - construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.

    " - - text += "Harvester
    If you see one, know that you have done all you can and your life is void.

    " - - var/text_string = jointext(text, null) - var/datum/browser/popup = new(user, "tome", "", 800, 600) - popup.set_content(text_string) - popup.open() - return 1 - -/obj/item/tome/proc/finale_runes_ok(mob/living/user, obj/effect/rune/rune_to_scribe) - var/datum/game_mode/cult/cult_mode = SSticker.mode - var/area/A = get_area(src) - if(GAMEMODE_IS_CULT) - if(!canbypass)//not an admin-tome, check things - if(!cult_mode.narsie_condition_cleared) - to_chat(user, "There is still more to do before unleashing [SSticker.cultdat.entity_name] power!") - return 0 - if(!cult_mode.eldergod) - to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") - return 0 - if(cult_mode.demons_summoned) - to_chat(user, "\"We are already here. There is no need to try to summon us now.\"") - return 0 - if(!((CULT_ELDERGOD in cult_mode.objectives) || (CULT_SLAUGHTER in cult_mode.objectives))) - to_chat(user, "[SSticker.cultdat.entity_name]'s power does not wish to be unleashed!") - return 0 - if(!(A in summon_spots)) - to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(summon_spots)]!") - return 0 - var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") - if(confirm_final == "No" || confirm_final == null) - to_chat(user, "You decide to prepare further before scribing the rune.") - return 0 - else - return 1 - else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS - var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") - if(confirm_final == "No" || confirm_final == null) - to_chat(user, "You decide to prepare further before scribing the rune.") - return 0 - else - return 1 - -/obj/item/tome/proc/scribe_rune(mob/living/user) - var/turf/runeturf = get_turf(user) - if(isspaceturf(runeturf)) - return - var/chosen_keyword - var/obj/effect/rune/rune_to_scribe - var/entered_rune_name - var/list/possible_runes = list() - var/list/shields = list() - var/area/A = get_area(src) - if(locate(/obj/effect/rune) in runeturf) - to_chat(user, "There is already a rune here.") - return - for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) - var/obj/effect/rune/R = T - if(initial(R.cultist_name)) - possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this - if(!possible_runes.len) - return - entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes - if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) - return - for(var/T in typesof(/obj/effect/rune)) - var/obj/effect/rune/R = T - if(initial(R.cultist_name) == entered_rune_name) - rune_to_scribe = R - if(initial(R.req_keyword)) - var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") - if(!the_keyword) - return - chosen_keyword = the_keyword - break - if(!rune_to_scribe) - return - runeturf = get_turf(user) //we may have moved. adjust as needed... - A = get_area(src) - if(locate(/obj/effect/rune) in runeturf) - to_chat(user, "There is already a rune here.") - return - if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) - return - if(ispath(rune_to_scribe, /obj/effect/rune/narsie) || ispath(rune_to_scribe, /obj/effect/rune/slaughter))//may need to change this - Fethas - if(finale_runes_ok(user,rune_to_scribe)) - A = get_area(src) - if(!(A in summon_spots)) // Check again to make sure they didn't move - to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(summon_spots)]!") - return - command_announcement.Announce("Figments from an eldritch god are being summoned somewhere on the station from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') - for(var/B in spiral_range_turfs(1, user, 1)) - var/turf/T = B - var/obj/machinery/shield/N = new(T) - N.name = "Rune-Scriber's Shield" - N.desc = "A potent shield summoned by cultists to protect them while they prepare the final ritual" - N.icon_state = "shield-cult" - N.health = 60 - shields |= N - else - return//don't do shit - - var/mob/living/carbon/human/H = user - var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) - user.visible_message("[user] cuts open [user.p_their()] [affecting] and begins writing in [user.p_their()] own blood!", "You slice open your [affecting] and begin drawing a sigil of [SSticker.cultdat.entity_title3].") - user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting) - if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user))) - for(var/V in shields) - var/obj/machinery/shield/S = V - if(S && !QDELETED(S)) - qdel(S) - return - if(locate(/obj/effect/rune) in runeturf) - to_chat(user, "There is already a rune here.") - return - user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.", \ - "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].") - for(var/V in shields) - var/obj/machinery/shield/S = V - if(S && !QDELETED(S)) - qdel(S) - var/obj/effect/rune/R = new rune_to_scribe(runeturf, chosen_keyword) - R.blood_DNA = list() - R.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type - R.add_hiddenprint(H) - to_chat(user, "The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]") +#define CULT_ELDERGOD "eldergod" +#define CULT_SLAUGHTER "slaughter" + +/obj/effect/rune/proc/fizzle() + if(istype(src,/obj/effect/rune)) + usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + else + usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) + for (var/mob/V in viewers(src)) + V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) + return + +/obj/effect/rune/proc/check_icon() + if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way + var/bits = make_bit_triplet() + icon = get_rune(bits) + else + icon = get_rune_cult(invocation) + +/obj/item/tome + name = "arcane tome" + desc = "An old, dusty tome with frayed edges and a sinister-looking cover." + icon_state ="tome" + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + var/scribereduct = 0 + var/canbypass = 0 //ADMINBUS + +/obj/item/tome/accursed + name = "accursed tome" + desc = "An arcane tome still empowered with a shadow of its former consecration." + scribereduct = 30 //faster because it's made by corrupting a bible + +/obj/item/tome/imbued //Admin-only tome, allows instant drawing of runes + name = "imbued arcane tome" + desc = "An arcane tome granted by the Geometer itself." + scribereduct = 50 + canbypass = 1 + +/obj/item/tome/New() + if(!SSticker.mode) + icon_state = "tome" + else + icon_state = SSticker.cultdat.tome_icon + ..() + +/obj/item/tome/examine(mob/user) + . = ..() + if(iscultist(user) || user.stat == DEAD) + . += "The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]." + . += "Striking another cultist with it will purge holy water from them." + . += "Striking a noncultist, however, will sear their flesh." + +/obj/item/tome/attack(mob/living/M, mob/living/user) + if(!istype(M)) + return + if(!iscultist(user)) + return ..() + if(iscultist(M)) + if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion + to_chat(user, "You remove the taint from [M].") + var/holy2unholy = M.reagents.get_reagent_amount("holywater") + M.reagents.del_reagent("holywater") + M.reagents.add_reagent("unholywater",holy2unholy) + add_attack_logs(user, M, "Hit with [src], removing the holy water from them") + return + M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 + playsound(M, 'sound/weapons/sear.ogg', 50, 1) + M.visible_message("[user] strikes [M] with the arcane tome!", \ + "[user] strikes you with the tome, searing your flesh!") + flick("tome_attack", src) + user.do_attack_animation(M) + add_attack_logs(user, M, "Hit with [src]") + +/obj/item/tome/attack_self(mob/user) + if(!iscultist(user)) + to_chat(user, "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?") + return + open_tome(user) + +/obj/item/tome/proc/open_tome(mob/user) + var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel") + switch(choice) + if("More Information") + read_tome(user) + if("Scribe Rune") + scribe_rune(user) + if("Cancel") + return + +/obj/item/tome/proc/read_tome(mob/user) + var/text = list() + text += "
    Archives of [SSticker.cultdat.entity_title1]



    " + text += "A rune's name and effects can be revealed by examining the rune.<

    " + + text += "Rite of Binding
    This rune is one of the most important runes the cult has, being the only way to create new talismans. A blank sheet of paper must be on top of the rune. After \ + invoking it and choosing which talisman you desire, the paper will be converted, after some delay into a talisman.

    " + + text += "Teleport
    This rune is unique in that it requires a keyword before the scribing can begin. When invoked, it will find any other Teleport runes; \ + If any are found, the user can choose which rune to send to. Upon activation, the rune teleports everything above it to the selected rune.

    " + + text += "Rite of Enlightenment
    This rune is critical to the success of the cult. It will allow you to convert normal crew members into cultists. \ + To do this, simply place the crew member upon the rune and invoke it. This rune requires two invokers to use. If the target to be converted is mindshielded or a certain assignment, they will \ + be unable to be converted. People [SSticker.cultdat.entity_title3] wishes sacrificed will also be ineligible for conversion, and anyone with a shielding presence like the null rod will not be converted.
    \ + Successful conversions will produce a tome for the new cultist.

    " + + text += "Rite of Tribute
    This rune is necessary to achieve your goals. Simply place any dead creature upon the rune and invoke it (this will not \ + target cultists!). If this creature has a mind, a soulstone will be created and the creature's soul transported to it. Sacrificing the dead can be done alone, but sacrificing living crew or your cult's target will require 3 cultists. \ + Soulstones used on construct shells will move that soul into a powerful construct of your choice.

    " + + + text += "Rite of Resurrection
    This rune requires two corpses. To perform the ritual, place the corpse you wish to revive onto \ + the rune and the offering body adjacent to it. When the rune is invoked, the body to be sacrificed will turn to dust, the life force flowing into the revival target. Assuming the target is not moved \ + within a few seconds, they will be brought back to life, healed of all ailments.

    " + + text += "Rite of Disruption
    Robotic lifeforms have time and time again been the downfall of fledgling cults. This rune may allow you to gain the upper \ + hand against these pests. By using the rune, a large electromagnetic pulse will be emitted from the rune's location. The size of the EMP will grow significantly for each additional adjacent cultist when the \ + rune is activated.

    " + + text += "Astral Communion
    This rune is perhaps the most ingenious rune that is usable by a single person. Upon invoking the rune, the \ + user's spirit will be ripped from their body. In this state, the user's physical body will be locked in place to the rune itself - any attempts to move it will result in the rune pulling it back. \ + The body will also take constant damage while in this form, and may even die. The user's spirit will contain their consciousness, and will allow them to freely wander the station as a ghost. This may \ + also be used to commune with the dead.

    " + + text += "Rite of the Corporeal Shield
    While simple, this rune serves an important purpose in defense and hindering passage. When invoked, the \ + rune will draw a small amount of life force from the user and make the space above the rune completely dense, rendering it impassable to all but the most complex means. The rune may be invoked again to \ + undo this effect and allow passage again.

    " + + text += "Rite of Joined Souls
    This rune allows the cult to free other cultists with ease. When invoked, it will allow the user to summon a single cultist to the rune from \ + any location. It requires two invokers, and will damage each invoker slightly.

    " + + text += "Blood Boil
    When invoked, this rune will do a massive amount of damage to all non-cultist viewers, but it will also emit a small explosion upon invocation. \ + It requires three invokers.

    " + + text += "Leeching
    When invoked, this rune will transfer lifeforce from the victim to the invoker.

    " + + text += "Rite of Spectral Manifestation
    This rune allows you to summon spirits as humanoid fighters. When invoked, a spirit above the rune will be brought to life as a human, wearing nothing, that seeks only to serve you and [SSticker.cultdat.entity_title3]. \ + However, the spirit's link to reality is fragile - you must remain on top of the rune, and you will slowly take damage. Upon stepping off the rune, all summoned spirits will dissipate, dropping their items to the ground. You may manifest \ + multiple spirits with one rune, but you will rapidly take damage in doing so.

    " + + text += "Ritual of Dimensional Rending
    This rune is necessary to achieve your goals. On attempting to scribe it, it will produce shields around you and alert everyone you are attempting to scribe it; it takes a very long time to scribe, \ + and does massive damage to the one attempting to scribe it.
    Invoking it requires 9 invokers and the sacrifice of a specific crewmember, and once invoked, will summon [SSticker.cultdat.entity_title3], [SSticker.cultdat.entity_name]. \ + This will complete your objectives.


    " + + text += "Talisman of Teleportation
    The talisman form of the Teleport rune will transport the invoker to a selected Teleport rune once.

    " + + text += "Talisman of Fabrication
    This talisman is the main way of creating construct shells. To use it, one must strike 30 sheets of metal with the talisman. The sheets will then be twisted into a construct shell, ready to receive a soul to occupy it.

    " + + text += "Talisman of Tome Summoning
    This talisman will produce a single tome at your feet.

    " + + text += "Talisman of Veiling/Revealing
    This talisman will hide runes on its first use, and on the second, will reveal runes.

    " + + text += "Talisman of Disguising
    This talisman will permanently disguise all nearby runes as crayon runes.

    " + + text += "Talisman of Electromagnetic Pulse
    This talisman will EMP anything else nearby. It disappears after one use.

    " + + text += "Talisman of Stunning
    Attacking a target will knock them down for a long duration in addition to inhibiting their speech. \ + Robotic lifeforms will suffer the effects of a heavy electromagnetic pulse instead.

    " + + text += "Talisman of Armaments
    The Talisman of Arming will equip the user with armored robes, a backpack, shoes, an eldritch longsword, and an empowered bola. Any equipment that cannot \ + be equipped will not be summoned, weaponry will be put on the floor below the user. Attacking a fellow cultist with it will instead equip them.

    " + + text += "Talisman of Horrors
    The Talisman of Horror must be applied directly to the victim, it will shatter your victim's mind with visions of the end-times that may incapacitate them.

    " + + text += "Talisman of Shackling
    The Talisman of Shackling must be applied directly to the victim, it has 4 uses and cuffs victims with magic shackles that disappear when removed.

    " + + text += "In addition to these runes, the cult has a small selection of equipment and constructs.

    " + + text += "Equipment:

    " + + text += "Cult Blade
    Cult blades are sharp weapons that, notably, cannot be used by non-cultists. These blades are produced by the Talisman of Arming.

    " + + text += "Cult Bola
    Cult bolas are strong bolas, useful for snaring targets. These bolas are produced by the Talisman of Arming.

    " + + text += "Cult Robes
    Cult robes are heavily armored robes. These robes are produced by the Talisman of Arming.

    " + + text += "Soulstone
    A soulstone is a simple piece of magic, produced either via the starter talisman or by sacrificing humans. Using it on an unconscious or dead human, or on a Shade, will trap their soul in the stone, allowing its use in construct shells. \ +
    The soul within can also be released as a Shade by using it in-hand.

    " + + text += "Construct Shell
    A construct shell is useless on its own, but placing a filled soulstone within it allows you to produce your choice of a Wraith, a Juggernaut, or an Artificer. \ +
    Each construct has uses, detailed below in Constructs. Construct shells can be produced via the starter talisman or the Rite of Fabrication.

    " + + text += "Constructs:

    " + + text += "Shade
    While technically not a construct, the Shade is produced when released from a soulstone. It is quite fragile and has weak melee attacks, but is fully healed when recaptured by a soulstone.

    " + + text += "Wraith
    The Wraith is a fast, lethal melee attacker which can jaunt through walls. However, it is only slightly more durable than a shade.

    " + + text += "Juggernaut
    The Juggernaut is a slow, but durable, melee attacker which can produce temporary forcewalls. It will also reflect most lethal energy weapons.

    " + + text += "Artificer
    The Artificer is a weak and fragile construct, able to heal other constructs, produce more soulstones and construct shells, \ + construct fortifying cult walls and flooring, and finally, it can release a few indiscriminate stunning missiles.

    " + + text += "Harvester
    If you see one, know that you have done all you can and your life is void.

    " + + var/text_string = jointext(text, null) + var/datum/browser/popup = new(user, "tome", "", 800, 600) + popup.set_content(text_string) + popup.open() + return 1 + +/obj/item/tome/proc/finale_runes_ok(mob/living/user, obj/effect/rune/rune_to_scribe) + var/datum/game_mode/cult/cult_mode = SSticker.mode + var/area/A = get_area(src) + if(GAMEMODE_IS_CULT) + if(!canbypass)//not an admin-tome, check things + if(!cult_mode.narsie_condition_cleared) + to_chat(user, "There is still more to do before unleashing [SSticker.cultdat.entity_name] power!") + return 0 + if(!cult_mode.eldergod) + to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") + return 0 + if(cult_mode.demons_summoned) + to_chat(user, "\"We are already here. There is no need to try to summon us now.\"") + return 0 + if(!((CULT_ELDERGOD in cult_mode.objectives) || (CULT_SLAUGHTER in cult_mode.objectives))) + to_chat(user, "[SSticker.cultdat.entity_name]'s power does not wish to be unleashed!") + return 0 + if(!(A in GLOB.summon_spots)) + to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(GLOB.summon_spots)]!") + return 0 + var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") + if(confirm_final == "No" || confirm_final == null) + to_chat(user, "You decide to prepare further before scribing the rune.") + return 0 + else + return 1 + else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS + var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") + if(confirm_final == "No" || confirm_final == null) + to_chat(user, "You decide to prepare further before scribing the rune.") + return 0 + else + return 1 + +/obj/item/tome/proc/scribe_rune(mob/living/user) + var/turf/runeturf = get_turf(user) + if(isspaceturf(runeturf)) + return + var/chosen_keyword + var/obj/effect/rune/rune_to_scribe + var/entered_rune_name + var/list/possible_runes = list() + var/list/shields = list() + var/area/A = get_area(src) + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) + var/obj/effect/rune/R = T + if(initial(R.cultist_name)) + possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this + if(!possible_runes.len) + return + entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes + if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) + return + for(var/T in typesof(/obj/effect/rune)) + var/obj/effect/rune/R = T + if(initial(R.cultist_name) == entered_rune_name) + rune_to_scribe = R + if(initial(R.req_keyword)) + var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") + if(!the_keyword) + return + chosen_keyword = the_keyword + break + if(!rune_to_scribe) + return + runeturf = get_turf(user) //we may have moved. adjust as needed... + A = get_area(src) + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) + return + if(ispath(rune_to_scribe, /obj/effect/rune/narsie) || ispath(rune_to_scribe, /obj/effect/rune/slaughter))//may need to change this - Fethas + if(finale_runes_ok(user,rune_to_scribe)) + A = get_area(src) + if(!(A in GLOB.summon_spots)) // Check again to make sure they didn't move + to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(GLOB.summon_spots)]!") + return + GLOB.command_announcement.Announce("Figments from an eldritch god are being summoned somewhere on the station from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + for(var/B in spiral_range_turfs(1, user, 1)) + var/turf/T = B + var/obj/machinery/shield/N = new(T) + N.name = "Rune-Scriber's Shield" + N.desc = "A potent shield summoned by cultists to protect them while they prepare the final ritual" + N.icon_state = "shield-cult" + N.health = 60 + shields |= N + else + return//don't do shit + + var/mob/living/carbon/human/H = user + var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) + user.visible_message("[user] cuts open [user.p_their()] [affecting] and begins writing in [user.p_their()] own blood!", "You slice open your [affecting] and begin drawing a sigil of [SSticker.cultdat.entity_title3].") + user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting) + if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user))) + for(var/V in shields) + var/obj/machinery/shield/S = V + if(S && !QDELETED(S)) + qdel(S) + return + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.", \ + "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].") + for(var/V in shields) + var/obj/machinery/shield/S = V + if(S && !QDELETED(S)) + qdel(S) + var/obj/effect/rune/R = new rune_to_scribe(runeturf, chosen_keyword) + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type + R.add_hiddenprint(H) + to_chat(user, "The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]") diff --git a/code/game/gamemodes/cult/runes.dm b/code/game/gamemodes/cult/runes.dm index d918bead0ded..290d873bcf3b 100644 --- a/code/game/gamemodes/cult/runes.dm +++ b/code/game/gamemodes/cult/runes.dm @@ -1,5 +1,5 @@ -var/list/sacrificed = list() -var/list/non_revealed_runes = (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) +GLOBAL_LIST_EMPTY(sacrificed) +GLOBAL_LIST_INIT(non_revealed_runes, (subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed)) /* This file contains runes. @@ -255,7 +255,7 @@ structure_check() searches for nearby cultist structures required for the invoca qdel(paper_to_imbue) rune_in_use = 0 -var/list/teleport_runes = list() +GLOBAL_LIST_EMPTY(teleport_runes) /obj/effect/rune/teleport cultist_name = "Teleport" cultist_desc = "warps everything above it to another chosen teleport rune." @@ -270,10 +270,10 @@ var/list/teleport_runes = list() var/area/A = get_area(src) var/locname = initial(A.name) listkey = set_keyword ? "[set_keyword] [locname]":"[locname]" - teleport_runes += src + GLOB.teleport_runes += src /obj/effect/rune/teleport/Destroy() - teleport_runes -= src + GLOB.teleport_runes -= src return ..() /obj/effect/rune/teleport/invoke(var/list/invokers) @@ -281,7 +281,7 @@ var/list/teleport_runes = list() var/list/potential_runes = list() var/list/teleportnames = list() var/list/duplicaterunecount = list() - for(var/R in teleport_runes) + for(var/R in GLOB.teleport_runes) var/obj/effect/rune/teleport/T = R var/resultkey = T.listkey if(resultkey in teleportnames) @@ -349,7 +349,7 @@ var/list/teleport_runes = list() req_cultists = 1 allow_excess_invokers = TRUE rune_in_use = FALSE - + /obj/effect/rune/convert/do_invoke_glow() return @@ -410,7 +410,7 @@ var/list/teleport_runes = list() var/sacrifice_fulfilled var/datum/game_mode/cult/cult_mode = SSticker.mode if(offering.mind) - sacrificed.Add(offering.mind) + GLOB.sacrificed.Add(offering.mind) if(is_sacrifice_target(offering.mind)) sacrifice_fulfilled = TRUE new /obj/effect/temp_visual/cult/sac(loc) diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index f7aaf2c1ebd7..e7fb06c1b940 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,436 +1,436 @@ -/obj/item/paper/talisman - icon = 'icons/obj/paper.dmi' - icon_state = "paper_talisman" - var/cultist_name = "talisman" - var/cultist_desc = "A basic talisman. It serves no purpose." - var/invocation = "Naise meam!" - info = "


    " - var/uses = 1 - var/health_cost = 0 //The amount of health taken from the user when invoking the talisman - -/obj/item/paper/talisman/update_icon()//overriding this so the update_icon doesn't turn them into normal looking paper - SEND_SIGNAL(src, COMSIG_OBJ_UPDATE_ICON) - -/obj/item/paper/talisman/examine(mob/user) - . = ..() - if(iscultist(user) || user.stat == DEAD) - . += "Name: [cultist_name]" - . += "Effect: [cultist_desc]" - . += "Uses Remaining: [uses]" - else - . += "You see strange symbols on the paper. Are they supposed to mean something?" - -/obj/item/paper/talisman/attack_self(mob/living/user) - if(!iscultist(user)) - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return - if(invoke(user)) - uses-- - if(uses <= 0) - user.drop_item() - qdel(src) - -/obj/item/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1) - . = successfuluse - if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff - if(invocation) - user.whisper(invocation) - if(health_cost && iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) - -//Malformed Talisman: If something goes wrong. -/obj/item/paper/talisman/malformed - cultist_name = "malformed talisman" - cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." - invocation = "Ra'sha yoka!" - -/obj/item/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1) - to_chat(user, "You feel a pain in your head. [SSticker.cultdat.entity_title3] is displeased.") - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.apply_damage(10, BRUTE, "head") - -//Supply Talisman: Has a few unique effects. Granted only to starter cultists. -/obj/item/paper/talisman/supply - cultist_name = "Supply Talisman" - icon_state = "supply" - cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." - invocation = null - uses = 3 - -/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1) - var/dat = list() - dat += "There are [uses] bloody runes on the parchment.
    " - dat += "Please choose the chant to be imbued into the fabric of reality.
    " - dat += "
    " - dat += "N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
    " - dat += "Bar'tea eas! - Provides 5 runed metal.
    " - dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
    " - dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " - dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
    " - dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
    " - dat += "Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans.
    " - dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
    " - var/datum/browser/popup = new(user, "talisman", "", 400, 400) - popup.set_content(jointext(dat, "")) - popup.open() - return 0 - -/obj/item/paper/talisman/supply/Topic(href, href_list) - if(src) - if(usr.stat || usr.restrained() || !in_range(src, usr)) - return - if(href_list["rune"]) - switch(href_list["rune"]) - if("newtome") - var/obj/item/tome/T = new(usr) - usr.put_in_hands(T) - if("metal") - if(istype(src, /obj/item/paper/talisman/supply/weak)) - usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!") - return - var/obj/item/stack/sheet/runed_metal/R = new(usr,5) - usr.put_in_hands(R) - if("teleport") - var/obj/item/paper/talisman/teleport/T = new(usr) - usr.put_in_hands(T) - if("emp") - var/obj/item/paper/talisman/emp/T = new(usr) - usr.put_in_hands(T) - if("runestun") - var/obj/item/paper/talisman/stun/T = new(usr) - usr.put_in_hands(T) - if("soulstone") - var/obj/item/soulstone/T = new(usr) - usr.put_in_hands(T) - if("construct") - new /obj/structure/constructshell(get_turf(usr)) - if("veiling") - var/obj/item/paper/talisman/true_sight/T = new(usr) - usr.put_in_hands(T) - uses-- - if(uses <= 0) - if(iscarbon(usr)) - var/mob/living/carbon/C = usr - C.drop_item() - visible_message("[src] crumbles to dust.") - qdel(src) - -/obj/item/paper/talisman/supply/weak - uses = 2 - -//Rite of Translocation: Same as rune -/obj/item/paper/talisman/teleport - cultist_name = "Talisman of Teleportation" - icon_state = "teleport" - cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." - invocation = "Sas'so c'arta forbici!" - health_cost = 5 - -/obj/item/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1) - var/list/potential_runes = list() - var/list/teleportnames = list() - var/list/duplicaterunecount = list() - for(var/R in teleport_runes) - var/obj/effect/rune/teleport/T = R - var/resultkey = T.listkey - if(resultkey in teleportnames) - duplicaterunecount[resultkey]++ - resultkey = "[resultkey] ([duplicaterunecount[resultkey]])" - else - teleportnames.Add(resultkey) - duplicaterunecount[resultkey] = 1 - potential_runes[resultkey] = T - - if(!potential_runes.len) - to_chat(user, "There are no valid runes to teleport to!") - log_game("Teleport talisman failed - no other teleport runes") - return ..(user, 0) - - if(!is_level_reachable(user.z)) - to_chat(user, "You are not in the right dimension!") - log_game("Teleport talisman failed - user in away mission") - return ..(user, 0) - - var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked - var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to? - if(!src || QDELETED(src) || !user || user.l_hand != src && user.r_hand != src || user.incapacitated() || !actual_selected_rune) - return ..(user, 0) - - user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] in a flash of red light!", \ - "You speak the words of the talisman and find yourself somewhere else!") - user.forceMove(get_turf(actual_selected_rune)) - return ..() - - -/obj/item/paper/talisman/summon_tome - cultist_name = "Talisman of Tome Summoning" - icon_state = "tome" - cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of a cult." - invocation = "N'ath reth sh'yro eth d'raggathnor!" - health_cost = 1 - -/obj/item/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1) - . = ..() - user.visible_message("[user]'s hand glows red for a moment.", \ - "You speak the words of the talisman!") - new /obj/item/tome(get_turf(user)) - user.visible_message("A tome appears at [user]'s feet!", \ - "An arcane tome materializes at your feet.") - -/obj/item/paper/talisman/true_sight - cultist_name = "Talisman of Veiling" - icon_state = "veil" - cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes." - invocation = "Kla'atu barada nikt'o!" - health_cost = 1 - uses = 2 - var/revealing = FALSE //if it reveals or not - -/obj/item/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1) - . = ..() - if(!revealing) - user.visible_message("Thin grey dust falls from [user]'s hand!", \ - "You speak the words of the talisman, hiding nearby runes.") - invocation = "Nikt'o barada kla'atu!" - revealing = TRUE - for(var/obj/effect/rune/R in range(3,user)) - R.talismanhide() - else - user.visible_message("A flash of light shines from [user]'s hand!", \ - "You speak the words of the talisman, revealing nearby runes.") - for(var/obj/effect/rune/R in range(3,user)) - R.talismanreveal() - -//Rite of False Truths: Same as rune -/obj/item/paper/talisman/make_runes_fake - cultist_name = "Talisman of Disguising" - icon_state = "disguising" - cultist_desc = "A talisman that will make nearby runes appear fake." - invocation = "By'o nar'nar!" - -/obj/item/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1) - . = ..() - user.visible_message("Dust flows from [user]s hand.", \ - "You speak the words of the talisman, making nearby runes appear fake.") - for(var/obj/effect/rune/R in orange(6,user)) - R.talismanfake() - R.desc = "A rune drawn in crayon." - - -//Rite of Disruption: Weaker than rune -/obj/item/paper/talisman/emp - cultist_name = "Talisman of Electromagnetic Pulse" - icon_state = "emp" - cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." - invocation = "Ta'gh fara'qha fel d'amar det!" - health_cost = 5 - -/obj/item/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1) - . = ..() - user.visible_message("[user]'s hand flashes a bright blue!", \ - "You speak the words of the talisman, emitting an EMP blast.") - empulse(src, 4, 8, 1) - - -//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time -/obj/item/paper/talisman/stun - cultist_name = "Talisman of Stunning" - icon_state = "stunning" - cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly." - invocation = "Dream sign:Evil sealing talisman!" - health_cost = 10 - -/obj/item/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0) - if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff - return ..() - if(iscultist(user)) - to_chat(user, "To use this talisman, attack the target directly.") - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return 0 - -/obj/item/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1) - if(iscultist(user)) - invoke(user, 1) - user.visible_message("[user] holds up [src], which explodes in a flash of red light!", \ - "You stun [target] with the talisman!") - var/obj/item/nullrod/N = locate() in target - if(N) - target.visible_message("[target]'s holy weapon absorbs the talisman's light!", \ - "Your holy weapon absorbs the blinding light!") - else - add_attack_logs(user, target, "Stunned with a talisman") - target.Weaken(10) - target.Stun(10) - target.flash_eyes(1,1) - if(issilicon(target)) - var/mob/living/silicon/S = target - S.emp_act(1) - if(iscarbon(target)) - var/mob/living/carbon/C = target - C.AdjustSilence(5) - C.AdjustStuttering(15) - C.AdjustCultSlur(20) - C.Jitter(15) - user.drop_item() - qdel(src) - return - ..() - - -//Rite of Arming: Equips cultist armor on the user, where available -/obj/item/paper/talisman/armor - cultist_name = "Talisman of Arming" - icon_state = "arming" - cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." - invocation = "N'ath reth sh'yro eth draggathnor!" - -/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1) - . = ..() - var/mob/living/carbon/human/H = user - user.visible_message("Otherworldly armor suddenly appears on [user]!", \ - "You speak the words of the talisman, arming yourself!") - - H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit) - H.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) - H.put_in_hands(new /obj/item/melee/cultblade(user)) - H.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user)) - -/obj/item/paper/talisman/armor/attack(mob/living/target, mob/living/user) - if(iscultist(user) && iscultist(target)) - user.drop_item() - qdel(src) - invoke(target) - return - ..() - - -//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations -/obj/item/paper/talisman/horror - cultist_name = "Talisman of Horrors" - icon_state = "horror" - cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations." - invocation = "Lo'Nab Na'Dm!" - -/obj/item/paper/talisman/horror/attack(mob/living/target, mob/living/user) - if(iscultist(user)) - to_chat(user, "You disturb [target] with visions of the end!") - if(iscarbon(target)) - var/mob/living/carbon/H = target - H.AdjustHallucinate(30) - qdel(src) - - -//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets. -/obj/item/paper/talisman/construction - cultist_name = "Talisman of Construction" - icon_state = "construction" - cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell or on plasteel to make runed metal" - invocation = "Ethra p'ni dedol!" - uses = 25 - -/obj/item/paper/talisman/construction/attack_self(mob/living/user) - if(iscultist(user)) - to_chat(user, "To use this talisman, place it upon a stack of metal sheets or plasteel sheets!.") - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - - -/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user) - if(iscultist(user)) - to_chat(user, "This talisman will only work on a stack of metal sheets or plasteel sheets!!") - log_game("Construct talisman failed - not a valid target") - -/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters) - ..() - if(proximity_flag && iscultist(user)) - if(istype(target, /obj/item/stack/sheet/metal)) - var/turf/T = get_turf(target) - if(target.use(25)) - new /obj/structure/constructshell(T) - to_chat(user, "The talisman clings to the metal and twists it into a construct shell!") - user << sound('sound/magic/staff_chaos.ogg',0,1,25) - qdel(src) - return - if(istype(target, /obj/item/stack/sheet/plasteel)) - var/turf/T = get_turf(target) - var/quantity = min(target.amount, uses) - uses -= quantity - new /obj/item/stack/sheet/runed_metal(T,quantity) - target.use(quantity) - to_chat(user, "The talisman clings to the plasteel, transforming it into runed metal!") - user << sound('sound/magic/staff_chaos.ogg',0,1,25) - invoke(user, 1) - if(uses <= 0) - qdel(src) - else - to_chat(user, "The talisman must be used on metal or plasteel!") - -//Talisman of Shackling: Applies special cuffs directly from the talisman -/obj/item/paper/talisman/shackle - cultist_name = "Talisman of Shackling" - icon_state = "shackling" - cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings." - invocation = "In'totum Lig'abis!" - uses = 4 - -/obj/item/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0) - if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff - return ..() - if(iscultist(user)) - to_chat(user, "To use this talisman, attack the target directly.") - else - to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") - return 0 - -/obj/item/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user) - if(iscultist(user) && istype(target)) - if(target.stat == DEAD) - user.visible_message("This talisman's magic does not affect the dead!") - return - CuffAttack(target, user) - return - ..() - -/obj/item/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user) - if(!C.handcuffed) - invoke(user, 1) - playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) - C.visible_message("[user] begins restraining [C] with dark magic!", \ - "[user] begins shaping a dark magic around your wrists!") - if(do_mob(user, C, 30)) - if(!C.handcuffed) - C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C) - C.update_handcuffed() - to_chat(user, "You shackle [C].") - add_attack_logs(user, C, "Handcuffed (shackle talisman)") - uses-- - else - to_chat(user, "[C] is already bound.") - else - to_chat(user, "You fail to shackle [C].") - else - to_chat(user, "[C] is already bound.") - if(uses <= 0) - user.drop_item() - qdel(src) - return - -/obj/item/restraints/handcuffs/energy/cult //For the talisman of shackling - name = "cult shackles" - desc = "Shackles that bind the wrists with sinister magic." - trashtype = /obj/item/restraints/handcuffs/energy/used - origin_tech = "materials=2;magnets=5" - -/obj/item/restraints/handcuffs/energy/used - desc = "energy discharge" - flags = DROPDEL - -/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user) - user.visible_message("[user]'s shackles shatter in a discharge of dark magic!", \ - "Your [src] shatters in a discharge of dark magic!") - qdel(src) - . = ..() +/obj/item/paper/talisman + icon = 'icons/obj/paper.dmi' + icon_state = "paper_talisman" + var/cultist_name = "talisman" + var/cultist_desc = "A basic talisman. It serves no purpose." + var/invocation = "Naise meam!" + info = "


    " + var/uses = 1 + var/health_cost = 0 //The amount of health taken from the user when invoking the talisman + +/obj/item/paper/talisman/update_icon()//overriding this so the update_icon doesn't turn them into normal looking paper + SEND_SIGNAL(src, COMSIG_OBJ_UPDATE_ICON) + +/obj/item/paper/talisman/examine(mob/user) + . = ..() + if(iscultist(user) || user.stat == DEAD) + . += "Name: [cultist_name]" + . += "Effect: [cultist_desc]" + . += "Uses Remaining: [uses]" + else + . += "You see strange symbols on the paper. Are they supposed to mean something?" + +/obj/item/paper/talisman/attack_self(mob/living/user) + if(!iscultist(user)) + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return + if(invoke(user)) + uses-- + if(uses <= 0) + user.drop_item() + qdel(src) + +/obj/item/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1) + . = successfuluse + if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff + if(invocation) + user.whisper(invocation) + if(health_cost && iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) + +//Malformed Talisman: If something goes wrong. +/obj/item/paper/talisman/malformed + cultist_name = "malformed talisman" + cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." + invocation = "Ra'sha yoka!" + +/obj/item/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1) + to_chat(user, "You feel a pain in your head. [SSticker.cultdat.entity_title3] is displeased.") + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(10, BRUTE, "head") + +//Supply Talisman: Has a few unique effects. Granted only to starter cultists. +/obj/item/paper/talisman/supply + cultist_name = "Supply Talisman" + icon_state = "supply" + cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." + invocation = null + uses = 3 + +/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1) + var/dat = list() + dat += "There are [uses] bloody runes on the parchment.
    " + dat += "Please choose the chant to be imbued into the fabric of reality.
    " + dat += "
    " + dat += "N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
    " + dat += "Bar'tea eas! - Provides 5 runed metal.
    " + dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
    " + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " + dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
    " + dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
    " + dat += "Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans.
    " + dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
    " + var/datum/browser/popup = new(user, "talisman", "", 400, 400) + popup.set_content(jointext(dat, "")) + popup.open() + return 0 + +/obj/item/paper/talisman/supply/Topic(href, href_list) + if(src) + if(usr.stat || usr.restrained() || !in_range(src, usr)) + return + if(href_list["rune"]) + switch(href_list["rune"]) + if("newtome") + var/obj/item/tome/T = new(usr) + usr.put_in_hands(T) + if("metal") + if(istype(src, /obj/item/paper/talisman/supply/weak)) + usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!") + return + var/obj/item/stack/sheet/runed_metal/R = new(usr,5) + usr.put_in_hands(R) + if("teleport") + var/obj/item/paper/talisman/teleport/T = new(usr) + usr.put_in_hands(T) + if("emp") + var/obj/item/paper/talisman/emp/T = new(usr) + usr.put_in_hands(T) + if("runestun") + var/obj/item/paper/talisman/stun/T = new(usr) + usr.put_in_hands(T) + if("soulstone") + var/obj/item/soulstone/T = new(usr) + usr.put_in_hands(T) + if("construct") + new /obj/structure/constructshell(get_turf(usr)) + if("veiling") + var/obj/item/paper/talisman/true_sight/T = new(usr) + usr.put_in_hands(T) + uses-- + if(uses <= 0) + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + C.drop_item() + visible_message("[src] crumbles to dust.") + qdel(src) + +/obj/item/paper/talisman/supply/weak + uses = 2 + +//Rite of Translocation: Same as rune +/obj/item/paper/talisman/teleport + cultist_name = "Talisman of Teleportation" + icon_state = "teleport" + cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." + invocation = "Sas'so c'arta forbici!" + health_cost = 5 + +/obj/item/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1) + var/list/potential_runes = list() + var/list/teleportnames = list() + var/list/duplicaterunecount = list() + for(var/R in GLOB.teleport_runes) + var/obj/effect/rune/teleport/T = R + var/resultkey = T.listkey + if(resultkey in teleportnames) + duplicaterunecount[resultkey]++ + resultkey = "[resultkey] ([duplicaterunecount[resultkey]])" + else + teleportnames.Add(resultkey) + duplicaterunecount[resultkey] = 1 + potential_runes[resultkey] = T + + if(!potential_runes.len) + to_chat(user, "There are no valid runes to teleport to!") + log_game("Teleport talisman failed - no other teleport runes") + return ..(user, 0) + + if(!is_level_reachable(user.z)) + to_chat(user, "You are not in the right dimension!") + log_game("Teleport talisman failed - user in away mission") + return ..(user, 0) + + var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked + var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to? + if(!src || QDELETED(src) || !user || user.l_hand != src && user.r_hand != src || user.incapacitated() || !actual_selected_rune) + return ..(user, 0) + + user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] in a flash of red light!", \ + "You speak the words of the talisman and find yourself somewhere else!") + user.forceMove(get_turf(actual_selected_rune)) + return ..() + + +/obj/item/paper/talisman/summon_tome + cultist_name = "Talisman of Tome Summoning" + icon_state = "tome" + cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of a cult." + invocation = "N'ath reth sh'yro eth d'raggathnor!" + health_cost = 1 + +/obj/item/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("[user]'s hand glows red for a moment.", \ + "You speak the words of the talisman!") + new /obj/item/tome(get_turf(user)) + user.visible_message("A tome appears at [user]'s feet!", \ + "An arcane tome materializes at your feet.") + +/obj/item/paper/talisman/true_sight + cultist_name = "Talisman of Veiling" + icon_state = "veil" + cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes." + invocation = "Kla'atu barada nikt'o!" + health_cost = 1 + uses = 2 + var/revealing = FALSE //if it reveals or not + +/obj/item/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1) + . = ..() + if(!revealing) + user.visible_message("Thin grey dust falls from [user]'s hand!", \ + "You speak the words of the talisman, hiding nearby runes.") + invocation = "Nikt'o barada kla'atu!" + revealing = TRUE + for(var/obj/effect/rune/R in range(3,user)) + R.talismanhide() + else + user.visible_message("A flash of light shines from [user]'s hand!", \ + "You speak the words of the talisman, revealing nearby runes.") + for(var/obj/effect/rune/R in range(3,user)) + R.talismanreveal() + +//Rite of False Truths: Same as rune +/obj/item/paper/talisman/make_runes_fake + cultist_name = "Talisman of Disguising" + icon_state = "disguising" + cultist_desc = "A talisman that will make nearby runes appear fake." + invocation = "By'o nar'nar!" + +/obj/item/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("Dust flows from [user]s hand.", \ + "You speak the words of the talisman, making nearby runes appear fake.") + for(var/obj/effect/rune/R in orange(6,user)) + R.talismanfake() + R.desc = "A rune drawn in crayon." + + +//Rite of Disruption: Weaker than rune +/obj/item/paper/talisman/emp + cultist_name = "Talisman of Electromagnetic Pulse" + icon_state = "emp" + cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." + invocation = "Ta'gh fara'qha fel d'amar det!" + health_cost = 5 + +/obj/item/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("[user]'s hand flashes a bright blue!", \ + "You speak the words of the talisman, emitting an EMP blast.") + empulse(src, 4, 8, 1) + + +//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time +/obj/item/paper/talisman/stun + cultist_name = "Talisman of Stunning" + icon_state = "stunning" + cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly." + invocation = "Dream sign:Evil sealing talisman!" + health_cost = 10 + +/obj/item/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0) + if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff + return ..() + if(iscultist(user)) + to_chat(user, "To use this talisman, attack the target directly.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return 0 + +/obj/item/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1) + if(iscultist(user)) + invoke(user, 1) + user.visible_message("[user] holds up [src], which explodes in a flash of red light!", \ + "You stun [target] with the talisman!") + var/obj/item/nullrod/N = locate() in target + if(N) + target.visible_message("[target]'s holy weapon absorbs the talisman's light!", \ + "Your holy weapon absorbs the blinding light!") + else + add_attack_logs(user, target, "Stunned with a talisman") + target.Weaken(10) + target.Stun(10) + target.flash_eyes(1,1) + if(issilicon(target)) + var/mob/living/silicon/S = target + S.emp_act(1) + if(iscarbon(target)) + var/mob/living/carbon/C = target + C.AdjustSilence(5) + C.AdjustStuttering(15) + C.AdjustCultSlur(20) + C.Jitter(15) + user.drop_item() + qdel(src) + return + ..() + + +//Rite of Arming: Equips cultist armor on the user, where available +/obj/item/paper/talisman/armor + cultist_name = "Talisman of Arming" + icon_state = "arming" + cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." + invocation = "N'ath reth sh'yro eth draggathnor!" + +/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1) + . = ..() + var/mob/living/carbon/human/H = user + user.visible_message("Otherworldly armor suddenly appears on [user]!", \ + "You speak the words of the talisman, arming yourself!") + + H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) + H.put_in_hands(new /obj/item/melee/cultblade(user)) + H.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user)) + +/obj/item/paper/talisman/armor/attack(mob/living/target, mob/living/user) + if(iscultist(user) && iscultist(target)) + user.drop_item() + qdel(src) + invoke(target) + return + ..() + + +//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations +/obj/item/paper/talisman/horror + cultist_name = "Talisman of Horrors" + icon_state = "horror" + cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations." + invocation = "Lo'Nab Na'Dm!" + +/obj/item/paper/talisman/horror/attack(mob/living/target, mob/living/user) + if(iscultist(user)) + to_chat(user, "You disturb [target] with visions of the end!") + if(iscarbon(target)) + var/mob/living/carbon/H = target + H.AdjustHallucinate(30) + qdel(src) + + +//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets. +/obj/item/paper/talisman/construction + cultist_name = "Talisman of Construction" + icon_state = "construction" + cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell or on plasteel to make runed metal" + invocation = "Ethra p'ni dedol!" + uses = 25 + +/obj/item/paper/talisman/construction/attack_self(mob/living/user) + if(iscultist(user)) + to_chat(user, "To use this talisman, place it upon a stack of metal sheets or plasteel sheets!.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + + +/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user) + if(iscultist(user)) + to_chat(user, "This talisman will only work on a stack of metal sheets or plasteel sheets!!") + log_game("Construct talisman failed - not a valid target") + +/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters) + ..() + if(proximity_flag && iscultist(user)) + if(istype(target, /obj/item/stack/sheet/metal)) + var/turf/T = get_turf(target) + if(target.use(25)) + new /obj/structure/constructshell(T) + to_chat(user, "The talisman clings to the metal and twists it into a construct shell!") + user << sound('sound/magic/staff_chaos.ogg',0,1,25) + qdel(src) + return + if(istype(target, /obj/item/stack/sheet/plasteel)) + var/turf/T = get_turf(target) + var/quantity = min(target.amount, uses) + uses -= quantity + new /obj/item/stack/sheet/runed_metal(T,quantity) + target.use(quantity) + to_chat(user, "The talisman clings to the plasteel, transforming it into runed metal!") + user << sound('sound/magic/staff_chaos.ogg',0,1,25) + invoke(user, 1) + if(uses <= 0) + qdel(src) + else + to_chat(user, "The talisman must be used on metal or plasteel!") + +//Talisman of Shackling: Applies special cuffs directly from the talisman +/obj/item/paper/talisman/shackle + cultist_name = "Talisman of Shackling" + icon_state = "shackling" + cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings." + invocation = "In'totum Lig'abis!" + uses = 4 + +/obj/item/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0) + if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff + return ..() + if(iscultist(user)) + to_chat(user, "To use this talisman, attack the target directly.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return 0 + +/obj/item/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user) + if(iscultist(user) && istype(target)) + if(target.stat == DEAD) + user.visible_message("This talisman's magic does not affect the dead!") + return + CuffAttack(target, user) + return + ..() + +/obj/item/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user) + if(!C.handcuffed) + invoke(user, 1) + playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + C.visible_message("[user] begins restraining [C] with dark magic!", \ + "[user] begins shaping a dark magic around your wrists!") + if(do_mob(user, C, 30)) + if(!C.handcuffed) + C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C) + C.update_handcuffed() + to_chat(user, "You shackle [C].") + add_attack_logs(user, C, "Handcuffed (shackle talisman)") + uses-- + else + to_chat(user, "[C] is already bound.") + else + to_chat(user, "You fail to shackle [C].") + else + to_chat(user, "[C] is already bound.") + if(uses <= 0) + user.drop_item() + qdel(src) + return + +/obj/item/restraints/handcuffs/energy/cult //For the talisman of shackling + name = "cult shackles" + desc = "Shackles that bind the wrists with sinister magic." + trashtype = /obj/item/restraints/handcuffs/energy/used + origin_tech = "materials=2;magnets=5" + +/obj/item/restraints/handcuffs/energy/used + desc = "energy discharge" + flags = DROPDEL + +/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user) + user.visible_message("[user]'s shackles shatter in a discharge of dark magic!", \ + "Your [src] shatters in a discharge of dark magic!") + qdel(src) + . = ..() diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm index abd90172ee9f..ecbeb54f7f79 100644 --- a/code/game/gamemodes/devil/devilinfo.dm +++ b/code/game/gamemodes/devil/devilinfo.dm @@ -13,8 +13,8 @@ #define DEVILRESURRECTTIME 600 -var/global/list/allDevils = list() -var/global/list/lawlorify = list ( +GLOBAL_LIST_EMPTY(allDevils) +GLOBAL_LIST_INIT(lawlorify, list ( LORE = list( OBLIGATION_FOOD = "This devil seems to always offer it's victims food before slaughtering them.", OBLIGATION_FIDDLE = "This devil will never turn down a musical challenge.", @@ -77,7 +77,7 @@ var/global/list/lawlorify = list ( BANISH_DESTRUCTION = "If your corpse is destroyed, you will be unable to resurrect.", BANISH_FUNERAL_GARB = "If your corpse is clad in funeral garments, you will be unable to resurrect." ) - ) + )) /datum/devilinfo var/datum/mind/owner = null @@ -111,11 +111,11 @@ var/global/list/lawlorify = list ( return devil /proc/devilInfo(name, saveDetails = 0) - if(allDevils[lowertext(name)]) - return allDevils[lowertext(name)] + if(GLOB.allDevils[lowertext(name)]) + return GLOB.allDevils[lowertext(name)] else var/datum/devilinfo/devil = randomDevilInfo(name) - allDevils[lowertext(name)] = devil + GLOB.allDevils[lowertext(name)] = devil devil.exists = saveDetails return devil @@ -444,9 +444,9 @@ var/global/list/lawlorify = list ( D.oldform.revive() // Heal the old body too, so the devil doesn't resurrect, then immediately regress into a dead body. if(body.stat == DEAD) // Not sure why this would happen create_new_body() - else if(blobstart.len > 0) + else if(GLOB.blobstart.len > 0) // teleport the body so repeated beatdowns aren't an option) - body.forceMove(get_turf(pick(blobstart))) + body.forceMove(get_turf(pick(GLOB.blobstart))) // give them the devil lawyer outfit in case they got stripped if(ishuman(body)) var/mob/living/carbon/human/H = body @@ -456,8 +456,8 @@ var/global/list/lawlorify = list ( check_regression() /datum/devilinfo/proc/create_new_body() - if(blobstart.len > 0) - var/turf/targetturf = get_turf(pick(blobstart)) + if(GLOB.blobstart.len > 0) + var/turf/targetturf = get_turf(pick(GLOB.blobstart)) var/mob/currentMob = owner.current if(QDELETED(currentMob)) currentMob = owner.get_ghost() @@ -511,10 +511,10 @@ var/global/list/lawlorify = list ( to_chat(owner, "However, your infernal form is not without weaknesses.") to_chat(owner, "You may not use violence to coerce someone into selling their soul.") to_chat(owner, "You may not directly and knowingly physically harm a devil, other than yourself.") - to_chat(owner,lawlorify[LAW][bane]) - to_chat(owner,lawlorify[LAW][ban]) - to_chat(owner,lawlorify[LAW][obligation]) - to_chat(owner,lawlorify[LAW][banish]) + to_chat(owner,GLOB.lawlorify[LAW][bane]) + to_chat(owner,GLOB.lawlorify[LAW][ban]) + to_chat(owner,GLOB.lawlorify[LAW][obligation]) + to_chat(owner,GLOB.lawlorify[LAW][banish]) to_chat(owner, "

    Remember, the crew can research your weaknesses if they find out your devil name.
    ") diff --git a/code/game/gamemodes/devil/game_mode.dm b/code/game/gamemodes/devil/game_mode.dm index f8b980f2dfd2..dd67ccc03495 100644 --- a/code/game/gamemodes/devil/game_mode.dm +++ b/code/game/gamemodes/devil/game_mode.dm @@ -34,7 +34,7 @@ var/trueName= randomDevilName() devil_mind.devilinfo = devilInfo(trueName, 1) devil_mind.devilinfo.ascendable = ascendable - devil_mind.store_memory("Your diabolical true name is [devil_mind.devilinfo.truename]
    [lawlorify[LAW][devil_mind.devilinfo.ban]]
    You may not use violence to coerce someone into selling their soul.
    You may not directly and knowingly physically harm a devil, other than yourself.
    [lawlorify[LAW][devil_mind.devilinfo.bane]]
    [lawlorify[LAW][devil_mind.devilinfo.obligation]]
    [lawlorify[LAW][devil_mind.devilinfo.banish]]
    ") + devil_mind.store_memory("Your diabolical true name is [devil_mind.devilinfo.truename]
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]
    You may not use violence to coerce someone into selling their soul.
    You may not directly and knowingly physically harm a devil, other than yourself.
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.bane]]
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]
    [GLOB.lawlorify[LAW][devil_mind.devilinfo.banish]]
    ") devil_mind.devilinfo.link_with_mob(devil_mind.current) if(devil_mind.assigned_role == "Clown") to_chat(devil_mind.current, "Your infernal nature allows you to wield weapons without harming yourself.") @@ -47,8 +47,8 @@ var/mob/living/silicon/S = devil_mind.current S.laws.set_sixsixsix_law("You may not use violence to coerce someone into selling their soul.") S.laws.set_sixsixsix_law("You may not directly and knowingly physically harm a devil, other than yourself.") - S.laws.set_sixsixsix_law("[lawlorify[LAW][devil_mind.devilinfo.ban]]") - S.laws.set_sixsixsix_law("[lawlorify[LAW][devil_mind.devilinfo.obligation]]") + S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.ban]]") + S.laws.set_sixsixsix_law("[GLOB.lawlorify[LAW][devil_mind.devilinfo.obligation]]") S.laws.set_sixsixsix_law("Accomplish your objectives at all costs.") // unsure about the second "quantity" arg and how it fits with the antag refactor @@ -76,18 +76,18 @@ return "Target is not a devil." var/text = "
    The devil's true name is: [ply.devilinfo.truename]
    " text += "The devil's bans were:
    " - text += " [lawlorify[LORE][ply.devilinfo.ban]]
    " - text += " [lawlorify[LORE][ply.devilinfo.bane]]
    " - text += " [lawlorify[LORE][ply.devilinfo.obligation]]
    " - text += " [lawlorify[LORE][ply.devilinfo.banish]]

    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.ban]]
    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.bane]]
    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.obligation]]
    " + text += " [GLOB.lawlorify[LORE][ply.devilinfo.banish]]

    " return text /datum/game_mode/proc/update_devil_icons_added(datum/mind/devil_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL] hud.join_hud(devil_mind.current) set_antag_hud(devil_mind.current, "huddevil") /datum/game_mode/proc/update_devil_icons_removed(datum/mind/devil_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_DEVIL] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_DEVIL] hud.leave_hud(devil_mind.current) set_antag_hud(devil_mind.current, null) diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm index a5dcef8d4e71..52edccc9e73a 100644 --- a/code/game/gamemodes/extended/extended.dm +++ b/code/game/gamemodes/extended/extended.dm @@ -1,14 +1,14 @@ -/datum/game_mode/extended - name = "extended" - config_tag = "extended" - required_players = 0 - -/datum/game_mode/announce() - to_chat(world, "The current game mode is - Extended Role-Playing!") - to_chat(world, "Just have fun and role-play!") - -/datum/game_mode/extended/pre_setup() - return 1 - -/datum/game_mode/extended/post_setup() - ..() \ No newline at end of file +/datum/game_mode/extended + name = "extended" + config_tag = "extended" + required_players = 0 + +/datum/game_mode/announce() + to_chat(world, "The current game mode is - Extended Role-Playing!") + to_chat(world, "Just have fun and role-play!") + +/datum/game_mode/extended/pre_setup() + return 1 + +/datum/game_mode/extended/post_setup() + ..() diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index e7fb9b4969f1..0981ec72a518 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,522 +1,522 @@ -/* - * GAMEMODES (by Rastaf0) - * - * In the new mode system all special roles are fully supported. - * You can have proper wizards/traitors/changelings/cultists during any mode. - * Only two things really depends on gamemode: - * 1. Starting roles, equipment and preparations - * 2. Conditions of finishing the round. - * - */ - - -/datum/game_mode - var/name = "invalid" - var/config_tag = null - var/intercept_hacked = 0 - var/votable = 1 - var/probability = 0 - var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm - var/explosion_in_progress = 0 //sit back and relax - var/list/datum/mind/modePlayer = new - var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist - var/list/protected_jobs = list() // Jobs that can't be traitors - var/list/protected_species = list() // Species that can't be traitors - var/required_players = 0 - var/required_enemies = 0 - var/recommended_enemies = 0 - var/newscaster_announcements = null - var/ert_disabled = 0 - var/uplink_welcome = "Syndicate Uplink Console:" - var/uplink_uses = 20 - - var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) - var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) - var/list/player_draft_log = list() - var/list/datum/mind/xenos = list() - var/list/datum/mind/eventmiscs = list() - - var/list/datum/station_goal/station_goals = list() // A list of all station goals for this game mode - -/datum/game_mode/proc/announce() //to be calles when round starts - to_chat(world, "Notice: [src] did not define announce()") - - -///can_start() -///Checks to see if the game can be setup and ran with the current number of players or whatnot. -/datum/game_mode/proc/can_start() - var/playerC = 0 - for(var/mob/new_player/player in GLOB.player_list) - if((player.client)&&(player.ready)) - playerC++ - - if(!config.enable_gamemode_player_limit || (playerC >= required_players)) - return 1 - return 0 - -//pre_pre_setup() For when you really don't want certain jobs ingame. -/datum/game_mode/proc/pre_pre_setup() - - return 1 - -///pre_setup() -///Attempts to select players for special roles the mode might have. -/datum/game_mode/proc/pre_setup() - - return 1 - - -///post_setup() -///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things -/datum/game_mode/proc/post_setup() - - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_roundstart_logout_report() - - feedback_set_details("round_start","[time2text(world.realtime)]") - if(SSticker && SSticker.mode) - feedback_set_details("game_mode","[SSticker.mode]") -// if(revdata) -// feedback_set_details("revision","[revdata.revision]") - feedback_set_details("server_ip","[world.internet_address]:[world.port]") - generate_station_goals() - start_state = new /datum/station_state() - start_state.count() - return 1 - -///process() -///Called by the gameticker -/datum/game_mode/process() - return 0 - -//Called by the gameticker -/datum/game_mode/proc/process_job_tasks() - var/obj/machinery/message_server/useMS = null - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) - if(MS.active) - useMS = MS - break - for(var/mob/M in GLOB.player_list) - if(M.mind) - var/obj/item/pda/P=null - for(var/obj/item/pda/check_pda in PDAs) - if(check_pda.owner==M.name) - P=check_pda - break - var/count=0 - for(var/datum/job_objective/objective in M.mind.job_objectives) - count++ - var/msg="" - var/pay=0 - if(objective.per_unit && objective.units_compensated0) - if(M.mind.initial_account) - M.mind.initial_account.credit(pay, "Payment", "\[CLASSIFIED\] Terminal #[rand(111,333)]", "[command_name()] Payroll") - msg += "You have been sent the $[pay], as agreed." - else - msg += "However, we were unable to send you the $[pay] you're entitled." - if(useMS && P) - useMS.send_pda_message("[P.owner]", "[command_name()] Payroll", msg) - - var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) - PM.notify("Message from [command_name()] (Payroll), \"[msg]\" (Unable to Reply)", 0) - break - -/datum/game_mode/proc/check_finished() //to be called by ticker - if((SSshuttle.emergency && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) || station_was_nuked) - return 1 - return 0 - -/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. - return - -/datum/game_mode/proc/declare_completion() - var/clients = 0 - var/surviving_humans = 0 - var/surviving_total = 0 - var/ghosts = 0 - var/escaped_humans = 0 - var/escaped_total = 0 - var/escaped_on_pod_1 = 0 - var/escaped_on_pod_2 = 0 - var/escaped_on_pod_3 = 0 - var/escaped_on_pod_5 = 0 - var/escaped_on_shuttle = 0 - - var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) - - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) //shuttle didn't get to centcom - escape_locations -= /area/shuttle/escape - - for(var/mob/M in GLOB.player_list) - if(M.client) - clients++ - if(ishuman(M)) - if(!M.stat) - surviving_humans++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_humans++ - if(!M.stat) - surviving_total++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_total++ - - if(M.loc && M.loc.loc && M.loc.loc.type == SSshuttle.emergency.areaInstance.type && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) - escaped_on_shuttle++ - - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) - escaped_on_pod_1++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) - escaped_on_pod_2++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) - escaped_on_pod_3++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom) - escaped_on_pod_5++ - - if(isobserver(M)) - ghosts++ - - if(clients > 0) - feedback_set("round_end_clients",clients) - if(ghosts > 0) - feedback_set("round_end_ghosts",ghosts) - if(surviving_humans > 0) - feedback_set("survived_human",surviving_humans) - if(surviving_total > 0) - feedback_set("survived_total",surviving_total) - if(escaped_humans > 0) - feedback_set("escaped_human",escaped_humans) - if(escaped_total > 0) - feedback_set("escaped_total",escaped_total) - if(escaped_on_shuttle > 0) - feedback_set("escaped_on_shuttle",escaped_on_shuttle) - if(escaped_on_pod_1 > 0) - feedback_set("escaped_on_pod_1",escaped_on_pod_1) - if(escaped_on_pod_2 > 0) - feedback_set("escaped_on_pod_2",escaped_on_pod_2) - if(escaped_on_pod_3 > 0) - feedback_set("escaped_on_pod_3",escaped_on_pod_3) - if(escaped_on_pod_5 > 0) - feedback_set("escaped_on_pod_5",escaped_on_pod_5) - - send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") - return 0 - - -/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. - return 0 - -/datum/game_mode/proc/get_players_for_role(var/role, override_jobbans=0) - var/list/players = list() - var/list/candidates = list() - //var/list/drafted = list() - //var/datum/mind/applicant = null - - var/roletext = get_roletext(role) - - // Assemble a list of active players without jobbans. - for(var/mob/new_player/player in GLOB.player_list) - if(player.client && player.ready && player.has_valid_preferences()) - if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, roletext)) - if(player_old_enough_antag(player.client,role)) - players += player - - // Shuffle the players list so that it becomes ping-independent. - players = shuffle(players) - - // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species - for(var/mob/new_player/player in players) - if(!player.client.skip_antag) - if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species)) - player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them." - candidates += player.mind - players -= player - - // If we don't have enough antags, draft people who voted for the round. - if(candidates.len < recommended_enemies) - for(var/key in SSvote.round_voters) - for(var/mob/new_player/player in players) - if(player.ckey == key) - player_draft_log += "[player.key] voted for this round, so we are drafting them." - candidates += player.mind - players -= player - break - - // Remove candidates who want to be antagonist but have a job that precludes it - if(restricted_jobs) - for(var/datum/mind/player in candidates) - for(var/job in restricted_jobs) - if(player.assigned_role == job) - candidates -= player - - - return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies - // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, - // Less if there are not enough valid players in the game entirely to make recommended_enemies. - - -/datum/game_mode/proc/latespawn(var/mob) - -/* -/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/player) - if(player.preferences.be_special & role) - return 1 - return 0 -*/ - -/datum/game_mode/proc/num_players() - . = 0 - for(var/mob/new_player/P in GLOB.player_list) - if(P.client && P.ready) - .++ - -/datum/game_mode/proc/num_players_started() - . = 0 - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.client) - .++ - -/////////////////////////////////// -//Keeps track of all living heads// -/////////////////////////////////// -/datum/game_mode/proc/get_living_heads() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - var/list/real_command_positions = command_positions.Copy() - "Nanotrasen Representative" - if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions)) - . |= player.mind - - -//////////////////////////// -//Keeps track of all heads// -//////////////////////////// -/datum/game_mode/proc/get_all_heads() - . = list() - for(var/mob/player in GLOB.mob_list) - var/list/real_command_positions = command_positions.Copy() - "Nanotrasen Representative" - if(player.mind && (player.mind.assigned_role in real_command_positions)) - . |= player.mind - -////////////////////////////////////////////// -//Keeps track of all living security members// -////////////////////////////////////////////// -/datum/game_mode/proc/get_living_sec() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - if(player.stat != DEAD && player.mind && (player.mind.assigned_role in security_positions)) - . |= player.mind - -//////////////////////////////////////// -//Keeps track of all security members// -//////////////////////////////////////// -/datum/game_mode/proc/get_all_sec() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - if(player.mind && (player.mind.assigned_role in security_positions)) - . |= player.mind - -/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) - return 0 - -/datum/game_mode/New() - newscaster_announcements = pick(newscaster_standard_feeds) - -////////////////////////// -//Reports player logouts// -////////////////////////// -proc/display_roundstart_logout_report() - var/msg = "Roundstart logout report\n\n" - for(var/mob/living/L in GLOB.mob_list) - - if(L.ckey) - var/found = 0 - for(var/client/C in GLOB.clients) - if(C.ckey == L.ckey) - found = 1 - break - if(!found) - msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" - - - if(L.ckey && L.client) - if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) - msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" - continue //AFK client - if(L.stat) - if(L.suiciding) //Suicider - msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" - SSjobs.FreeRole(L.job) - message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Suicide)\n") - continue //Disconnected client - if(L.stat == UNCONSCIOUS) - msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" - continue //Unconscious - if(L.stat == DEAD) - msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" - continue //Dead - - continue //Happy connected client - for(var/mob/dead/observer/D in GLOB.mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) - if(L.stat == DEAD) - if(L.suiciding) //Suicider - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" - continue //Disconnected client - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" - continue //Dead mob, ghost abandoned - else - if(D.can_reenter_corpse) - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (This shouldn't appear.)\n" - continue //Lolwhat - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" - SSjobs.FreeRole(L.job) - message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Ghosted While Alive)\n") - continue //Ghosted while alive - - - - for(var/mob/M in GLOB.mob_list) - if(check_rights(R_ADMIN, 0, M)) - to_chat(M, msg) - -//Announces objectives/generic antag text. -/proc/show_generic_antag_text(var/datum/mind/player) - if(player.current) - to_chat(player.current, "You are an antagonist! Within the rules, \ - try to act as an opposing force to the crew. Further RP and try to make sure \ - other players have fun! If you are confused or at a loss, always adminhelp, \ - and before taking extreme actions, please try to also contact the administration! \ - Think through your actions and make the roleplay immersive! Please remember all \ - rules aside from those without explicit exceptions apply to antagonists.") - -/proc/show_objectives(var/datum/mind/player) - if(!player || !player.current) return - - var/obj_count = 1 - to_chat(player.current, "Your current objectives:") - for(var/datum/objective/objective in player.objectives) - to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - -/proc/get_roletext(var/role) - return role - -/proc/get_nuke_code() - var/nukecode = "ERROR" - for(var/obj/machinery/nuclearbomb/bomb in world) - if(bomb && bomb.r_code && is_station_level(bomb.z)) - nukecode = bomb.r_code - return nukecode - -/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) - var/mob/dead/observer/theghost = null - if(candidates.len) - theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") - M.ghostize() - M.key = theghost.key - else - message_admins("[M] ([M.key] has been converted into [role_type] with an active antagonist jobban for said role since no ghost has volunteered to take [M.p_their()] place.") - to_chat(M, "You have been converted into [role_type] with an active jobban. Any further violations of the rules on your part are likely to result in a permanent ban.") - -/proc/printplayer(datum/mind/ply, fleecheck) - var/jobtext = "" - if(ply.assigned_role) - jobtext = " the [ply.assigned_role]" - var/text = "[ply.key] was [ply.name][jobtext] and" - if(ply.current) - if(ply.current.stat == DEAD) - text += " died" - else - text += " survived" - if(fleecheck) - var/turf/T = get_turf(ply.current) - if(!T || !is_station_level(T.z)) - text += " while fleeing the station" - if(ply.current.real_name != ply.name) - text += " as [ply.current.real_name]" - else - text += " had [ply.p_their()] body destroyed" - return text - -/proc/printeventplayer(datum/mind/ply) - var/text = "[ply.key] was [ply.name]" - if(ply.special_role != SPECIAL_ROLE_EVENTMISC) - text += " the [ply.special_role]" - text += " and" - if(ply.current) - if(ply.current.stat == DEAD) - text += " died" - else - text += " survived" - else - text += " had [ply.p_their()] body destroyed" - return text - -/proc/printobjectives(datum/mind/ply) - var/list/objective_parts = list() - var/count = 1 - for(var/datum/objective/objective in ply.objectives) - if(objective.check_completion()) - objective_parts += "Objective #[count]: [objective.explanation_text] Success!" - else - objective_parts += "Objective #[count]: [objective.explanation_text] Fail." - count++ - return objective_parts.Join("
    ") - -/datum/game_mode/proc/generate_station_goals() - var/list/possible = list() - for(var/T in subtypesof(/datum/station_goal)) - var/datum/station_goal/G = T - if(config_tag in initial(G.gamemode_blacklist)) - continue - possible += T - var/goal_weights = 0 - while(possible.len && goal_weights < STATION_GOAL_BUDGET) - var/datum/station_goal/picked = pick_n_take(possible) - goal_weights += initial(picked.weight) - station_goals += new picked - - if(station_goals.len) - send_station_goals_message() - -/datum/game_mode/proc/send_station_goals_message() - var/message_text = "
    " - message_text += "

    [command_name()] Orders


    " - message_text += "Special Orders for [station_name()]:

    " - - for(var/datum/station_goal/G in station_goals) - G.on_report() - message_text += G.get_report() - message_text += "
    " - - print_command_report(message_text, "[command_name()] Orders") - -/datum/game_mode/proc/declare_station_goal_completion() - for(var/V in station_goals) - var/datum/station_goal/G = V - G.print_result() - -/datum/game_mode/proc/update_eventmisc_icons_added(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_EVENTMISC] - antaghud.join_hud(mob_mind.current) - set_antag_hud(mob_mind.current, "hudevent") - -/datum/game_mode/proc/update_eventmisc_icons_removed(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_EVENTMISC] - antaghud.leave_hud(mob_mind.current) - set_antag_hud(mob_mind.current, null) \ No newline at end of file +/* + * GAMEMODES (by Rastaf0) + * + * In the new mode system all special roles are fully supported. + * You can have proper wizards/traitors/changelings/cultists during any mode. + * Only two things really depends on gamemode: + * 1. Starting roles, equipment and preparations + * 2. Conditions of finishing the round. + * + */ + + +/datum/game_mode + var/name = "invalid" + var/config_tag = null + var/intercept_hacked = 0 + var/votable = 1 + var/probability = 0 + var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm + var/explosion_in_progress = 0 //sit back and relax + var/list/datum/mind/modePlayer = new + var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist + var/list/protected_jobs = list() // Jobs that can't be traitors + var/list/protected_species = list() // Species that can't be traitors + var/required_players = 0 + var/required_enemies = 0 + var/recommended_enemies = 0 + var/newscaster_announcements = null + var/ert_disabled = 0 + var/uplink_welcome = "Syndicate Uplink Console:" + var/uplink_uses = 20 + + var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) + var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) + var/list/player_draft_log = list() + var/list/datum/mind/xenos = list() + var/list/datum/mind/eventmiscs = list() + + var/list/datum/station_goal/station_goals = list() // A list of all station goals for this game mode + +/datum/game_mode/proc/announce() //to be calles when round starts + to_chat(world, "Notice: [src] did not define announce()") + + +///can_start() +///Checks to see if the game can be setup and ran with the current number of players or whatnot. +/datum/game_mode/proc/can_start() + var/playerC = 0 + for(var/mob/new_player/player in GLOB.player_list) + if((player.client)&&(player.ready)) + playerC++ + + if(!config.enable_gamemode_player_limit || (playerC >= required_players)) + return 1 + return 0 + +//pre_pre_setup() For when you really don't want certain jobs ingame. +/datum/game_mode/proc/pre_pre_setup() + + return 1 + +///pre_setup() +///Attempts to select players for special roles the mode might have. +/datum/game_mode/proc/pre_setup() + + return 1 + + +///post_setup() +///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things +/datum/game_mode/proc/post_setup() + + spawn (ROUNDSTART_LOGOUT_REPORT_TIME) + display_roundstart_logout_report() + + feedback_set_details("round_start","[time2text(world.realtime)]") + if(SSticker && SSticker.mode) + feedback_set_details("game_mode","[SSticker.mode]") +// if(revdata) +// feedback_set_details("revision","[revdata.revision]") + feedback_set_details("server_ip","[world.internet_address]:[world.port]") + generate_station_goals() + GLOB.start_state = new /datum/station_state() + GLOB.start_state.count() + return 1 + +///process() +///Called by the gameticker +/datum/game_mode/process() + return 0 + +//Called by the gameticker +/datum/game_mode/proc/process_job_tasks() + var/obj/machinery/message_server/useMS = null + if(GLOB.message_servers) + for(var/obj/machinery/message_server/MS in GLOB.message_servers) + if(MS.active) + useMS = MS + break + for(var/mob/M in GLOB.player_list) + if(M.mind) + var/obj/item/pda/P=null + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.owner==M.name) + P=check_pda + break + var/count=0 + for(var/datum/job_objective/objective in M.mind.job_objectives) + count++ + var/msg="" + var/pay=0 + if(objective.per_unit && objective.units_compensated0) + if(M.mind.initial_account) + M.mind.initial_account.credit(pay, "Payment", "\[CLASSIFIED\] Terminal #[rand(111,333)]", "[command_name()] Payroll") + msg += "You have been sent the $[pay], as agreed." + else + msg += "However, we were unable to send you the $[pay] you're entitled." + if(useMS && P) + useMS.send_pda_message("[P.owner]", "[command_name()] Payroll", msg) + + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [command_name()] (Payroll), \"[msg]\" (Unable to Reply)", 0) + break + +/datum/game_mode/proc/check_finished() //to be called by ticker + if((SSshuttle.emergency && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) || station_was_nuked) + return 1 + return 0 + +/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. + return + +/datum/game_mode/proc/declare_completion() + var/clients = 0 + var/surviving_humans = 0 + var/surviving_total = 0 + var/ghosts = 0 + var/escaped_humans = 0 + var/escaped_total = 0 + var/escaped_on_pod_1 = 0 + var/escaped_on_pod_2 = 0 + var/escaped_on_pod_3 = 0 + var/escaped_on_pod_5 = 0 + var/escaped_on_shuttle = 0 + + var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) + + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) //shuttle didn't get to centcom + escape_locations -= /area/shuttle/escape + + for(var/mob/M in GLOB.player_list) + if(M.client) + clients++ + if(ishuman(M)) + if(!M.stat) + surviving_humans++ + if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) + escaped_humans++ + if(!M.stat) + surviving_total++ + if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) + escaped_total++ + + if(M.loc && M.loc.loc && M.loc.loc.type == SSshuttle.emergency.areaInstance.type && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) + escaped_on_shuttle++ + + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) + escaped_on_pod_1++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) + escaped_on_pod_2++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) + escaped_on_pod_3++ + if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom) + escaped_on_pod_5++ + + if(isobserver(M)) + ghosts++ + + if(clients > 0) + feedback_set("round_end_clients",clients) + if(ghosts > 0) + feedback_set("round_end_ghosts",ghosts) + if(surviving_humans > 0) + feedback_set("survived_human",surviving_humans) + if(surviving_total > 0) + feedback_set("survived_total",surviving_total) + if(escaped_humans > 0) + feedback_set("escaped_human",escaped_humans) + if(escaped_total > 0) + feedback_set("escaped_total",escaped_total) + if(escaped_on_shuttle > 0) + feedback_set("escaped_on_shuttle",escaped_on_shuttle) + if(escaped_on_pod_1 > 0) + feedback_set("escaped_on_pod_1",escaped_on_pod_1) + if(escaped_on_pod_2 > 0) + feedback_set("escaped_on_pod_2",escaped_on_pod_2) + if(escaped_on_pod_3 > 0) + feedback_set("escaped_on_pod_3",escaped_on_pod_3) + if(escaped_on_pod_5 > 0) + feedback_set("escaped_on_pod_5",escaped_on_pod_5) + + send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") + return 0 + + +/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. + return 0 + +/datum/game_mode/proc/get_players_for_role(var/role, override_jobbans=0) + var/list/players = list() + var/list/candidates = list() + //var/list/drafted = list() + //var/datum/mind/applicant = null + + var/roletext = get_roletext(role) + + // Assemble a list of active players without jobbans. + for(var/mob/new_player/player in GLOB.player_list) + if(player.client && player.ready && player.has_valid_preferences()) + if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, roletext)) + if(player_old_enough_antag(player.client,role)) + players += player + + // Shuffle the players list so that it becomes ping-independent. + players = shuffle(players) + + // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species + for(var/mob/new_player/player in players) + if(!player.client.skip_antag) + if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species)) + player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them." + candidates += player.mind + players -= player + + // If we don't have enough antags, draft people who voted for the round. + if(candidates.len < recommended_enemies) + for(var/key in SSvote.round_voters) + for(var/mob/new_player/player in players) + if(player.ckey == key) + player_draft_log += "[player.key] voted for this round, so we are drafting them." + candidates += player.mind + players -= player + break + + // Remove candidates who want to be antagonist but have a job that precludes it + if(restricted_jobs) + for(var/datum/mind/player in candidates) + for(var/job in restricted_jobs) + if(player.assigned_role == job) + candidates -= player + + + return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies + // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, + // Less if there are not enough valid players in the game entirely to make recommended_enemies. + + +/datum/game_mode/proc/latespawn(var/mob) + +/* +/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/player) + if(player.preferences.be_special & role) + return 1 + return 0 +*/ + +/datum/game_mode/proc/num_players() + . = 0 + for(var/mob/new_player/P in GLOB.player_list) + if(P.client && P.ready) + .++ + +/datum/game_mode/proc/num_players_started() + . = 0 + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.client) + .++ + +/////////////////////////////////// +//Keeps track of all living heads// +/////////////////////////////////// +/datum/game_mode/proc/get_living_heads() + . = list() + for(var/mob/living/carbon/human/player in GLOB.mob_list) + var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative" + if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions)) + . |= player.mind + + +//////////////////////////// +//Keeps track of all heads// +//////////////////////////// +/datum/game_mode/proc/get_all_heads() + . = list() + for(var/mob/player in GLOB.mob_list) + var/list/real_command_positions = GLOB.command_positions.Copy() - "Nanotrasen Representative" + if(player.mind && (player.mind.assigned_role in real_command_positions)) + . |= player.mind + +////////////////////////////////////////////// +//Keeps track of all living security members// +////////////////////////////////////////////// +/datum/game_mode/proc/get_living_sec() + . = list() + for(var/mob/living/carbon/human/player in GLOB.mob_list) + if(player.stat != DEAD && player.mind && (player.mind.assigned_role in GLOB.security_positions)) + . |= player.mind + +//////////////////////////////////////// +//Keeps track of all security members// +//////////////////////////////////////// +/datum/game_mode/proc/get_all_sec() + . = list() + for(var/mob/living/carbon/human/player in GLOB.mob_list) + if(player.mind && (player.mind.assigned_role in GLOB.security_positions)) + . |= player.mind + +/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) + return 0 + +/datum/game_mode/New() + newscaster_announcements = pick(GLOB.newscaster_standard_feeds) + +////////////////////////// +//Reports player logouts// +////////////////////////// +proc/display_roundstart_logout_report() + var/msg = "Roundstart logout report\n\n" + for(var/mob/living/L in GLOB.mob_list) + + if(L.ckey) + var/found = 0 + for(var/client/C in GLOB.clients) + if(C.ckey == L.ckey) + found = 1 + break + if(!found) + msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" + + + if(L.ckey && L.client) + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" + continue //AFK client + if(L.stat) + if(L.suiciding) //Suicider + msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" + SSjobs.FreeRole(L.job) + message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Suicide)\n") + continue //Disconnected client + if(L.stat == UNCONSCIOUS) + msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" + continue //Unconscious + if(L.stat == DEAD) + msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" + continue //Dead + + continue //Happy connected client + for(var/mob/dead/observer/D in GLOB.mob_list) + if(D.mind && (D.mind.original == L || D.mind.current == L)) + if(L.stat == DEAD) + if(L.suiciding) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (This shouldn't appear.)\n" + continue //Lolwhat + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" + SSjobs.FreeRole(L.job) + message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Ghosted While Alive)\n") + continue //Ghosted while alive + + + + for(var/mob/M in GLOB.mob_list) + if(check_rights(R_ADMIN, 0, M)) + to_chat(M, msg) + +//Announces objectives/generic antag text. +/proc/show_generic_antag_text(var/datum/mind/player) + if(player.current) + to_chat(player.current, "You are an antagonist! Within the rules, \ + try to act as an opposing force to the crew. Further RP and try to make sure \ + other players have fun! If you are confused or at a loss, always adminhelp, \ + and before taking extreme actions, please try to also contact the administration! \ + Think through your actions and make the roleplay immersive! Please remember all \ + rules aside from those without explicit exceptions apply to antagonists.") + +/proc/show_objectives(var/datum/mind/player) + if(!player || !player.current) return + + var/obj_count = 1 + to_chat(player.current, "Your current objectives:") + for(var/datum/objective/objective in player.objectives) + to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + +/proc/get_roletext(var/role) + return role + +/proc/get_nuke_code() + var/nukecode = "ERROR" + for(var/obj/machinery/nuclearbomb/bomb in world) + if(bomb && bomb.r_code && is_station_level(bomb.z)) + nukecode = bomb.r_code + return nukecode + +/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) + var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) + var/mob/dead/observer/theghost = null + if(candidates.len) + theghost = pick(candidates) + to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") + message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") + M.ghostize() + M.key = theghost.key + else + message_admins("[M] ([M.key] has been converted into [role_type] with an active antagonist jobban for said role since no ghost has volunteered to take [M.p_their()] place.") + to_chat(M, "You have been converted into [role_type] with an active jobban. Any further violations of the rules on your part are likely to result in a permanent ban.") + +/proc/printplayer(datum/mind/ply, fleecheck) + var/jobtext = "" + if(ply.assigned_role) + jobtext = " the [ply.assigned_role]" + var/text = "[ply.key] was [ply.name][jobtext] and" + if(ply.current) + if(ply.current.stat == DEAD) + text += " died" + else + text += " survived" + if(fleecheck) + var/turf/T = get_turf(ply.current) + if(!T || !is_station_level(T.z)) + text += " while fleeing the station" + if(ply.current.real_name != ply.name) + text += " as [ply.current.real_name]" + else + text += " had [ply.p_their()] body destroyed" + return text + +/proc/printeventplayer(datum/mind/ply) + var/text = "[ply.key] was [ply.name]" + if(ply.special_role != SPECIAL_ROLE_EVENTMISC) + text += " the [ply.special_role]" + text += " and" + if(ply.current) + if(ply.current.stat == DEAD) + text += " died" + else + text += " survived" + else + text += " had [ply.p_their()] body destroyed" + return text + +/proc/printobjectives(datum/mind/ply) + var/list/objective_parts = list() + var/count = 1 + for(var/datum/objective/objective in ply.objectives) + if(objective.check_completion()) + objective_parts += "Objective #[count]: [objective.explanation_text] Success!" + else + objective_parts += "Objective #[count]: [objective.explanation_text] Fail." + count++ + return objective_parts.Join("
    ") + +/datum/game_mode/proc/generate_station_goals() + var/list/possible = list() + for(var/T in subtypesof(/datum/station_goal)) + var/datum/station_goal/G = T + if(config_tag in initial(G.gamemode_blacklist)) + continue + possible += T + var/goal_weights = 0 + while(possible.len && goal_weights < STATION_GOAL_BUDGET) + var/datum/station_goal/picked = pick_n_take(possible) + goal_weights += initial(picked.weight) + station_goals += new picked + + if(station_goals.len) + send_station_goals_message() + +/datum/game_mode/proc/send_station_goals_message() + var/message_text = "
    " + message_text += "

    [command_name()] Orders


    " + message_text += "Special Orders for [station_name()]:

    " + + for(var/datum/station_goal/G in station_goals) + G.on_report() + message_text += G.get_report() + message_text += "
    " + + print_command_report(message_text, "[command_name()] Orders") + +/datum/game_mode/proc/declare_station_goal_completion() + for(var/V in station_goals) + var/datum/station_goal/G = V + G.print_result() + +/datum/game_mode/proc/update_eventmisc_icons_added(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_EVENTMISC] + antaghud.join_hud(mob_mind.current) + set_antag_hud(mob_mind.current, "hudevent") + +/datum/game_mode/proc/update_eventmisc_icons_removed(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_EVENTMISC] + antaghud.leave_hud(mob_mind.current) + set_antag_hud(mob_mind.current, null) diff --git a/code/game/gamemodes/heist/heist.dm b/code/game/gamemodes/heist/heist.dm index 2b4d095b3271..142ae33c078f 100644 --- a/code/game/gamemodes/heist/heist.dm +++ b/code/game/gamemodes/heist/heist.dm @@ -1,9 +1,8 @@ /* VOX HEIST ROUNDTYPE */ - -var/global/list/raider_spawn = list() -var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' objective. Clumsy, rewrite sometime. +GLOBAL_LIST_EMPTY(raider_spawn) +GLOBAL_LIST_EMPTY(cortical_stacks) //Stacks for 'leave nobody behind' objective. Clumsy, rewrite sometime. /datum/game_mode/ var/list/datum/mind/raiders = list() //Antags. @@ -70,10 +69,10 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' //Spawn the vox! for(var/datum/mind/raider in raiders) - if(index > raider_spawn.len) + if(index > GLOB.raider_spawn.len) index = 1 - raider.current.loc = raider_spawn[index] + raider.current.loc = GLOB.raider_spawn[index] index++ create_vox(raider) @@ -128,16 +127,16 @@ var/global/list/obj/cortical_stacks = list() //Stacks for 'leave nobody behind' //Now apply cortical stack. var/obj/item/implant/cortical/I = new(vox) I.implant(vox) - cortical_stacks += I + GLOB.cortical_stacks += I vox.equip_vox_raider() vox.regenerate_icons() /datum/game_mode/proc/is_raider_crew_safe() - if(cortical_stacks.len == 0) + if(GLOB.cortical_stacks.len == 0) return 0 - for(var/obj/stack in cortical_stacks) + for(var/obj/stack in GLOB.cortical_stacks) if(get_area(stack) != locate(/area/shuttle/vox) && get_area(stack) != locate(/area/vox_station)) return 0 //this is stupid as fuck return 1 diff --git a/code/game/gamemodes/intercept_report.dm b/code/game/gamemodes/intercept_report.dm index d1d176f797ed..acee798e96c8 100644 --- a/code/game/gamemodes/intercept_report.dm +++ b/code/game/gamemodes/intercept_report.dm @@ -1,234 +1,234 @@ -/datum/intercept_text - var/text - /* - var/prob_correct_person_lower = 20 - var/prob_correct_person_higher = 80 - var/prob_correct_job_lower = 20 - var/prob_correct_job_higher = 80 - var/prob_correct_prints_lower = 20 - var/prob_correct_print_higher = 80 - var/prob_correct_objective_lower = 20 - var/prob_correct_objective_higher = 80 - */ - var/list/org_names_1 = list( - "Blighted", - "Defiled", - "Unholy", - "Murderous", - "Ugly", - "French", - "Blue", - "Farmer" - ) - var/list/org_names_2 = list( - "Reapers", - "Swarm", - "Rogues", - "Menace", - "Jeff Worshippers", - "Drunks", - "Strikers", - "Creed" - ) - var/list/anomalies = list( - "Huge electrical storm", - "Photon emitter", - "Meson generator", - "Blue swirly thing" - ) - var/list/SWF_names = list( - "Grand Wizard", - "His Most Unholy Master", - "The Most Angry", - "Bighands", - "Tall Hat", - "Deadly Sandals" - ) - var/list/changeling_names = list( - "Odo", - "The Thing", - "Booga", - "The Goatee of Wrath", - "Tam Lin", - "Species 3157", - "Small Prick" - ) - - -/datum/intercept_text/proc/build(var/mode_type, datum/mind/correct_person) - switch(mode_type) - if("revolution") - src.text = "" - src.build_rev(correct_person) - return src.text - if("cult") - src.text = "" - src.build_cult(correct_person) - return src.text - if("wizard") - src.text = "" - src.build_wizard(correct_person) - return src.text - if("nuke") - src.text = "" - src.build_nuke(correct_person) - return src.text - if("traitor") - src.text = "" - src.build_traitor(correct_person) - return src.text - if("changeling","traitorchan") - src.text = "" - src.build_changeling(correct_person) - return src.text - else - return null - -// NOTE: Commentted out was the code which showed the chance of someone being an antag. If you want to re-add it, just uncomment the code. - -/* -/datum/intercept_text/proc/pick_mob() - var/list/dudes = list() - for(var/mob/living/carbon/human/man in GLOB.player_list) - if(!man.mind) continue - if(man.mind.assigned_role == man.mind.special_role) continue - dudes += man - if(dudes.len==0) - return null - return pick(dudes) - - -/datum/intercept_text/proc/pick_fingerprints() - var/mob/living/carbon/human/dude = src.pick_mob() - //if(!dude) return pick_fingerprints() //who coded that is totally crasy or just a traitor. -- rastaf0 - if(dude) - return num2text(md5(dude.dna.uni_identity)) - else - return num2text(md5(num2text(rand(1,10000)))) -*/ - -/datum/intercept_text/proc/get_suspect() - var/list/dudes = list() - for(var/mob/living/carbon/human/man in GLOB.player_list) - if(man.client && man.client.prefs.nanotrasen_relation == "Opposed") - //don't include suspects who can't possibly be the antag based on their job (no suspecting the captain of being a damned dirty tator) - if(man.mind && man.mind.assigned_role) - if((man.mind.assigned_role in SSticker.mode.protected_jobs) || (man.mind.assigned_role in SSticker.mode.restricted_jobs)) - return - //don't include suspects who can't possibly be the antag based on their species (no suspecting the machines of being sneaky changelings) - if(man.dna.species.name in SSticker.mode.protected_species) - return - dudes += man - for(var/i = 0, i < max(GLOB.player_list.len/10,2), i++) - dudes += pick(GLOB.player_list) - return pick(dudes) - -/datum/intercept_text/proc/build_traitor(datum/mind/correct_person) - var/name_1 = pick(src.org_names_1) - var/name_2 = pick(src.org_names_2) - - var/mob/living/carbon/human/H = get_suspect() - if(!H) return - - var/fingerprints = num2text(md5(H.dna.uni_identity)) - var/traitor_name = H.real_name - var/prob_right_dude = rand(1, 100) - - src.text += "

    The [name_1] [name_2] implied an undercover operative was acting on their behalf on the station currently." - src.text += "It would be in your best interests to suspect everybody, as these undercover operatives could have implants which trigger them to have their memories removed until they are needed. He, or she, could even be a high ranking officer." - - src.text += "After some investigation, we " - if(prob(50)) - src.text += "are [prob_right_dude]% sure that [traitor_name] may have been involved, and should be closely observed." - src.text += "
    Note: This group are known to be untrustworthy, so do not act on this information without proper discourse." - else - src.text += "discovered the following set of fingerprints ([fingerprints]) on sensitive materials, and their owner should be closely observed." - src.text += "However, these could also belong to a current Cent. Com employee, so do not act on this without reason." - - - -/datum/intercept_text/proc/build_cult(datum/mind/correct_person) - var/name_1 = pick(src.org_names_1) - var/name_2 = pick(src.org_names_2) - - var/prob_right_dude = rand(1, 100) - var/mob/living/carbon/human/H = get_suspect() - if(!H) return - var/traitor_job = H.mind.assigned_role - - src.text += "

    It has been brought to our attention that the [name_1] [name_2] have stumbled upon some dark secrets. They apparently want to spread the dangerous knowledge onto as many stations as they can." - src.text += "Watch out for the following: praying to an unfamilar god, preaching the word of \[REDACTED\], sacrifices, magical dark power, living constructs of evil and a portal to the dimension of the underworld." - - src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been converted " - src.text += "and instilled with the idea of the flimsiness of the real world, seeking to destroy it. " - - src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." - - - -/datum/intercept_text/proc/build_rev(datum/mind/correct_person) - var/name_1 = pick(src.org_names_1) - var/name_2 = pick(src.org_names_2) - - var/prob_right_dude = rand(1, 100) - var/mob/living/carbon/human/H = get_suspect() - if(!H) return - var/traitor_job = H.mind.assigned_role - - src.text += "

    It has been brought to our attention that the [name_1] [name_2] are attempting to stir unrest on one of our stations in your sector." - src.text += "Watch out for suspicious activity among the crew and make sure that all heads of staff report in periodically." - - src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been brainwashed " - src.text += "at a recent conference, and their department should be closely monitored for signs of mutiny. " - - src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." - - - -/datum/intercept_text/proc/build_wizard(datum/mind/correct_person) - var/SWF_desc = pick(SWF_names) - - src.text += "

    The evil Space Wizards Federation have recently broke their most feared wizard, known only as \"[SWF_desc]\" out of space jail. " - src.text += "He is on the run, last spotted in a system near your present location. If anybody suspicious is located aboard, please " - src.text += "approach with EXTREME caution. Cent. Com also recommends that it would be wise to not inform the crew of this, due to their fearful nature." - src.text += "Known attributes include: Brown sandals, a large blue hat, a voluptous white beard, and an inclination to cast spells." - -/datum/intercept_text/proc/build_nuke(datum/mind/correct_person) - src.text += "

    Cent. Com recently recieved a report of a plot to destroy one of our stations in your area. We believe the Nuclear Authentication Disc " - src.text += "that is standard issue aboard your vessel may be a target. We recommend removal of this object, and it's storage in a safe " - src.text += "environment. As this may cause panic among the crew, all efforts should be made to keep this information a secret from all but " - src.text += "the most trusted crew-members." - -/datum/intercept_text/proc/build_changeling(datum/mind/correct_person) - var/cname = pick(src.changeling_names) - var/orgname1 = pick(src.org_names_1) - var/orgname2 = pick(src.org_names_2) - /* - var/changeling_name - var/changeling_job - var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher) - var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher) - if(prob(prob_right_job)) - if(correct_person) - if(correct_person:assigned_role == correct_person:special_role) - changeling_job = pick(GLOB.joblist) - else - changeling_job = correct_person:assigned_role - else - changeling_job = pick(GLOB.joblist) - if(prob(prob_right_dude) && ticker.mode == "changeling") - if(correct_person:assigned_role == correct_person:special_role) - changeling_name = correct_person:current - else - changeling_name = src.pick_mob() - else - changeling_name = src.pick_mob() - */ - - src.text += "

    We have received a report that a dangerous alien lifeform known only as \"[cname]\" may have infiltrated your crew. " - /* - src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. " - src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. " - */ - src.text += "These lifeforms are assosciated with the [orgname1] [orgname2] and may be attempting to acquire sensitive materials on their behalf. " - src.text += "Please take care not to alarm the crew, as [cname] may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!" +/datum/intercept_text + var/text + /* + var/prob_correct_person_lower = 20 + var/prob_correct_person_higher = 80 + var/prob_correct_job_lower = 20 + var/prob_correct_job_higher = 80 + var/prob_correct_prints_lower = 20 + var/prob_correct_print_higher = 80 + var/prob_correct_objective_lower = 20 + var/prob_correct_objective_higher = 80 + */ + var/list/org_names_1 = list( + "Blighted", + "Defiled", + "Unholy", + "Murderous", + "Ugly", + "French", + "Blue", + "Farmer" + ) + var/list/org_names_2 = list( + "Reapers", + "Swarm", + "Rogues", + "Menace", + "Jeff Worshippers", + "Drunks", + "Strikers", + "Creed" + ) + var/list/anomalies = list( + "Huge electrical storm", + "Photon emitter", + "Meson generator", + "Blue swirly thing" + ) + var/list/SWF_names = list( + "Grand Wizard", + "His Most Unholy Master", + "The Most Angry", + "Bighands", + "Tall Hat", + "Deadly Sandals" + ) + var/list/changeling_names = list( + "Odo", + "The Thing", + "Booga", + "The Goatee of Wrath", + "Tam Lin", + "Species 3157", + "Small Prick" + ) + + +/datum/intercept_text/proc/build(var/mode_type, datum/mind/correct_person) + switch(mode_type) + if("revolution") + src.text = "" + src.build_rev(correct_person) + return src.text + if("cult") + src.text = "" + src.build_cult(correct_person) + return src.text + if("wizard") + src.text = "" + src.build_wizard(correct_person) + return src.text + if("nuke") + src.text = "" + src.build_nuke(correct_person) + return src.text + if("traitor") + src.text = "" + src.build_traitor(correct_person) + return src.text + if("changeling","traitorchan") + src.text = "" + src.build_changeling(correct_person) + return src.text + else + return null + +// NOTE: Commentted out was the code which showed the chance of someone being an antag. If you want to re-add it, just uncomment the code. + +/* +/datum/intercept_text/proc/pick_mob() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in GLOB.player_list) + if(!man.mind) continue + if(man.mind.assigned_role == man.mind.special_role) continue + dudes += man + if(dudes.len==0) + return null + return pick(dudes) + + +/datum/intercept_text/proc/pick_fingerprints() + var/mob/living/carbon/human/dude = src.pick_mob() + //if(!dude) return pick_fingerprints() //who coded that is totally crasy or just a traitor. -- rastaf0 + if(dude) + return num2text(md5(dude.dna.uni_identity)) + else + return num2text(md5(num2text(rand(1,10000)))) +*/ + +/datum/intercept_text/proc/get_suspect() + var/list/dudes = list() + for(var/mob/living/carbon/human/man in GLOB.player_list) + if(man.client && man.client.prefs.nanotrasen_relation == "Opposed") + //don't include suspects who can't possibly be the antag based on their job (no suspecting the captain of being a damned dirty tator) + if(man.mind && man.mind.assigned_role) + if((man.mind.assigned_role in SSticker.mode.protected_jobs) || (man.mind.assigned_role in SSticker.mode.restricted_jobs)) + return + //don't include suspects who can't possibly be the antag based on their species (no suspecting the machines of being sneaky changelings) + if(man.dna.species.name in SSticker.mode.protected_species) + return + dudes += man + for(var/i = 0, i < max(GLOB.player_list.len/10,2), i++) + dudes += pick(GLOB.player_list) + return pick(dudes) + +/datum/intercept_text/proc/build_traitor(datum/mind/correct_person) + var/name_1 = pick(src.org_names_1) + var/name_2 = pick(src.org_names_2) + + var/mob/living/carbon/human/H = get_suspect() + if(!H) return + + var/fingerprints = num2text(md5(H.dna.uni_identity)) + var/traitor_name = H.real_name + var/prob_right_dude = rand(1, 100) + + src.text += "

    The [name_1] [name_2] implied an undercover operative was acting on their behalf on the station currently." + src.text += "It would be in your best interests to suspect everybody, as these undercover operatives could have implants which trigger them to have their memories removed until they are needed. He, or she, could even be a high ranking officer." + + src.text += "After some investigation, we " + if(prob(50)) + src.text += "are [prob_right_dude]% sure that [traitor_name] may have been involved, and should be closely observed." + src.text += "
    Note: This group are known to be untrustworthy, so do not act on this information without proper discourse." + else + src.text += "discovered the following set of fingerprints ([fingerprints]) on sensitive materials, and their owner should be closely observed." + src.text += "However, these could also belong to a current Cent. Com employee, so do not act on this without reason." + + + +/datum/intercept_text/proc/build_cult(datum/mind/correct_person) + var/name_1 = pick(src.org_names_1) + var/name_2 = pick(src.org_names_2) + + var/prob_right_dude = rand(1, 100) + var/mob/living/carbon/human/H = get_suspect() + if(!H) return + var/traitor_job = H.mind.assigned_role + + src.text += "

    It has been brought to our attention that the [name_1] [name_2] have stumbled upon some dark secrets. They apparently want to spread the dangerous knowledge onto as many stations as they can." + src.text += "Watch out for the following: praying to an unfamilar god, preaching the word of \[REDACTED\], sacrifices, magical dark power, living constructs of evil and a portal to the dimension of the underworld." + + src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been converted " + src.text += "and instilled with the idea of the flimsiness of the real world, seeking to destroy it. " + + src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." + + + +/datum/intercept_text/proc/build_rev(datum/mind/correct_person) + var/name_1 = pick(src.org_names_1) + var/name_2 = pick(src.org_names_2) + + var/prob_right_dude = rand(1, 100) + var/mob/living/carbon/human/H = get_suspect() + if(!H) return + var/traitor_job = H.mind.assigned_role + + src.text += "

    It has been brought to our attention that the [name_1] [name_2] are attempting to stir unrest on one of our stations in your sector." + src.text += "Watch out for suspicious activity among the crew and make sure that all heads of staff report in periodically." + + src.text += "Based on our intelligence, we are [prob_right_dude]% sure that if true, someone doing the job of [traitor_job] on your station may have been brainwashed " + src.text += "at a recent conference, and their department should be closely monitored for signs of mutiny. " + + src.text += "
    However, if this information is acted on without substantial evidence, those responsible will face severe repercussions." + + + +/datum/intercept_text/proc/build_wizard(datum/mind/correct_person) + var/SWF_desc = pick(SWF_names) + + src.text += "

    The evil Space Wizards Federation have recently broke their most feared wizard, known only as \"[SWF_desc]\" out of space jail. " + src.text += "He is on the run, last spotted in a system near your present location. If anybody suspicious is located aboard, please " + src.text += "approach with EXTREME caution. Cent. Com also recommends that it would be wise to not inform the crew of this, due to their fearful nature." + src.text += "Known attributes include: Brown sandals, a large blue hat, a voluptous white beard, and an inclination to cast spells." + +/datum/intercept_text/proc/build_nuke(datum/mind/correct_person) + src.text += "

    Cent. Com recently recieved a report of a plot to destroy one of our stations in your area. We believe the Nuclear Authentication Disc " + src.text += "that is standard issue aboard your vessel may be a target. We recommend removal of this object, and it's storage in a safe " + src.text += "environment. As this may cause panic among the crew, all efforts should be made to keep this information a secret from all but " + src.text += "the most trusted crew-members." + +/datum/intercept_text/proc/build_changeling(datum/mind/correct_person) + var/cname = pick(src.changeling_names) + var/orgname1 = pick(src.org_names_1) + var/orgname2 = pick(src.org_names_2) + /* + var/changeling_name + var/changeling_job + var/prob_right_dude = rand(prob_correct_person_lower, prob_correct_person_higher) + var/prob_right_job = rand(prob_correct_job_lower, prob_correct_job_higher) + if(prob(prob_right_job)) + if(correct_person) + if(correct_person:assigned_role == correct_person:special_role) + changeling_job = pick(GLOB.joblist) + else + changeling_job = correct_person:assigned_role + else + changeling_job = pick(GLOB.joblist) + if(prob(prob_right_dude) && ticker.mode == "changeling") + if(correct_person:assigned_role == correct_person:special_role) + changeling_name = correct_person:current + else + changeling_name = src.pick_mob() + else + changeling_name = src.pick_mob() + */ + + src.text += "

    We have received a report that a dangerous alien lifeform known only as \"[cname]\" may have infiltrated your crew. " + /* + src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. " + src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. " + */ + src.text += "These lifeforms are assosciated with the [orgname1] [orgname2] and may be attempting to acquire sensitive materials on their behalf. " + src.text += "Please take care not to alarm the crew, as [cname] may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!" diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index 70e0e3258ebe..ad15da69412e 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -1,758 +1,758 @@ -//The malf AI action subtype. All malf actions are subtypes of this. -/datum/action/innate/ai - name = "AI Action" - desc = "You aren't entirely sure what this does, but it's very beepy and boopy." - background_icon_state = "bg_tech_blue" - var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time - var/uses //If we have multiple uses of the same power - var/auto_use_uses = TRUE //If we automatically use up uses on each activation - var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules - -/datum/action/innate/ai/Grant(mob/living/L) - . = ..() - if(!isAI(owner)) - WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!") - qdel(src) - else - owner_AI = owner - -/datum/action/innate/ai/IsAvailable() - . = ..() - if(owner_AI && owner_AI.malf_cooldown > world.time) - return - -/datum/action/innate/ai/Trigger() - . = ..() - if(auto_use_uses) - adjust_uses(-1) - if(cooldown_period) - owner_AI.malf_cooldown = world.time + cooldown_period - -/datum/action/innate/ai/proc/adjust_uses(amt, silent) - uses += amt - if(!silent && uses) - to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") - if(!uses) - if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! - to_chat(owner, "[name] has run out of uses!") - qdel(src) - -//Framework for ranged abilities that can have different effects by left-clicking stuff. -/datum/action/innate/ai/ranged - name = "Ranged AI Action" - auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses - var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code - var/linked_ability_type //The path of our linked ability - -/datum/action/innate/ai/ranged/New() - if(!linked_ability_type) - WARNING("Ranged AI action [name] attempted to spawn without a linked ability!") - qdel(src) //uh oh! - return - linked_ability = new linked_ability_type() - linked_ability.attached_action = src - ..() - -/datum/action/innate/ai/ranged/adjust_uses(amt, silent) - uses += amt - if(!silent && uses) - to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") - if(!uses) - if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! - to_chat(owner, "[name] has run out of uses!") - Remove(owner) - QDEL_IN(src, 100) //let any active timers on us finish up - -/datum/action/innate/ai/ranged/Destroy() - QDEL_NULL(linked_ability) - return ..() - -/datum/action/innate/ai/ranged/Activate() - linked_ability.toggle(owner) - return TRUE - -//The actual ranged proc holder. -/obj/effect/proc_holder/ranged_ai - var/enable_text = "Hello World!" //Appears when the user activates the ability - var/disable_text = "Goodbye Cruel World!" //Context clues! - var/datum/action/innate/ai/ranged/attached_action - -/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user) - if(active) - remove_ranged_ability(user, disable_text) - else - add_ranged_ability(user, enable_text) - -//The datum and interface for the malf unlock menu, which lets them choose actions to unlock. -/datum/module_picker - var/temp - var/processing_time = 50 - var/list/possible_modules - -/datum/module_picker/New() - possible_modules = list() - for(var/type in typesof(/datum/AI_Module)) - var/datum/AI_Module/AM = new type - if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) - possible_modules += AM - -/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. - for(var/datum/AI_Module/AM in possible_modules) - for(var/datum/action/A in AI.actions) - if(istype(A, initial(AM.power_type))) - qdel(A) - -/datum/module_picker/proc/use(user as mob) - var/dat - dat += {"Select use of processing time: (currently #[processing_time] left.)
    -
    - Install Module:
    - The number afterwards is the amount of processing time it consumes.
    "} - for(var/datum/AI_Module/large/module in possible_modules) - dat += "[module.module_name]\[?\] ([module.cost])
    " - for(var/datum/AI_Module/small/module in possible_modules) - dat += "[module.module_name]\[?\] ([module.cost])
    " - dat += "
    " - if(temp) - dat += "[temp]" - var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu", 400, 500) - popup.set_content(dat) - popup.open() - return - -/datum/module_picker/Topic(href, href_list) - ..() - - if(!isAI(usr)) - return - var/mob/living/silicon/ai/A = usr - - if(A.stat == DEAD) - to_chat(A, "You are already dead!") - return - - for(var/datum/AI_Module/AM in possible_modules) - if (href_list[AM.mod_pick_name]) - - // Cost check - if(AM.cost > processing_time) - temp = "You cannot afford this module." - break - - var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions - - // Give the power and take away the money. - if(AM.upgrade) //upgrade and upgrade() are separate, be careful! - AM.upgrade(A) - possible_modules -= AM - to_chat(A, AM.unlock_text) - A.playsound_local(A, AM.unlock_sound, 50, 0) - else - if(AM.power_type) - if(!action) //Unlocking for the first time - var/datum/action/AC = new AM.power_type - AC.Grant(A) - A.current_modules += new AM.type - temp = AM.description - if(AM.one_purchase) - possible_modules -= AM - if(AM.unlock_text) - to_chat(A, AM.unlock_text) - if(AM.unlock_sound) - A.playsound_local(A, AM.unlock_sound, 50, 0) - else //Adding uses to an existing module - action.uses += initial(action.uses) - action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." - action.UpdateButtonIcon() - temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" - processing_time -= AM.cost - - if(href_list["showdesc"]) - if(AM.mod_pick_name == href_list["showdesc"]) - temp = AM.description - use(usr) - -//The base module type, which holds info about each ability. -/datum/AI_Module - var/module_name - var/mod_pick_name - var/description = "" - var/engaged = 0 - var/cost = 5 - var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false. - var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade. - var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type. - var/unlock_text = "Hello World!" //Text shown when an ability is unlocked - var/unlock_sound //Sound played when an ability is unlocked - var/uses - -/datum/AI_Module/proc/upgrade(mob/living/silicon/AI/AI) //Apply upgrades! - return - -/datum/AI_Module/large //Big, powerful stuff that can only be used once. - -/datum/AI_Module/small //Weak, usually localized stuff with multiple uses. - -//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely. -/datum/AI_Module/large/nuke_station - module_name = "Doomsday Device" - mod_pick_name = "nukestation" - description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed." - cost = 130 - one_purchase = TRUE - power_type = /datum/action/innate/ai/nuke_station - unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time." - unlock_sound = 'sound/items/timer.ogg' - -/datum/action/innate/ai/nuke_station - name = "Doomsday Device" - desc = "Activates the doomsday device. This is not reversible." - button_icon_state = "doomsday_device" - auto_use_uses = FALSE - -/datum/action/innate/ai/nuke_station/Activate() - var/turf/T = get_turf(owner) - if(!istype(T) || !is_station_level(T.z)) - to_chat(owner, "You cannot activate the doomsday device while off-station!") - return - if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;") - return - if(active) - return //prevent the AI from activating an already active doomsday - active = TRUE - set_us_up_the_bomb() - -/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb() - to_chat(owner_AI, "Nuclear device armed.") - event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') - set_security_level("delta") - owner_AI.nuking = TRUE - var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI) - owner_AI.doomsday_device = DOOM - owner_AI.doomsday_device.start() - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer now tracks the AI core - qdel(src) - -/obj/machinery/doomsday_device - icon = 'icons/obj/machines/nuke_terminal.dmi' - name = "doomsday device" - icon_state = "nuclearbomb_base" - desc = "A weapon which disintegrates all organic life in a large area." - anchored = 1 - density = 1 - atom_say_verb = "blares" - speed_process = TRUE // Disgusting fix. Please remove once #12952 is merged - var/timing = 0 - var/default_timer = 4500 - var/detonation_timer - var/announced = 0 - -/obj/machinery/doomsday_device/Destroy() - STOP_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - return ..() - -/obj/machinery/doomsday_device/proc/start() - detonation_timer = world.time + default_timer - timing = 1 - START_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 1 - -/obj/machinery/doomsday_device/proc/seconds_remaining() - . = max(0, (round(detonation_timer - world.time) / 10)) - -/obj/machinery/doomsday_device/process() - var/turf/T = get_turf(src) - if(!T || !is_station_level(T.z)) - minor_announcement.Announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - qdel(src) - if(!timing) - STOP_PROCESSING(SSfastprocess, src) - return - var/sec_left = seconds_remaining() - if(sec_left <= 0) - timing = 0 - detonate(T.z) - qdel(src) - else - if(!(sec_left % 60) && !announced) - var/message = "[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!" - minor_announcement.Announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') - announced = 10 - announced = max(0, announced-1) - -/obj/machinery/doomsday_device/proc/detonate(z_level = 1) - for(var/mob/M in GLOB.player_list) - M << 'sound/machines/alarm.ogg' - sleep(100) - for(var/mob/living/L in GLOB.mob_list) - var/turf/T = get_turf(L) - if(!T || T.z != z_level) - continue - if(issilicon(L)) - continue - to_chat(L, "The blast wave from [src] tears you atom from atom!") - L.dust() - to_chat(world, "The AI cleansed the station of life with the doomsday device!") - SSticker.force_ending = 1 - -//AI Turret Upgrade: Increases the health and damage of all turrets. -/datum/AI_Module/large/upgrade_turrets - module_name = "AI Turret Upgrade" - mod_pick_name = "turret" - description = "Improves the power and health of all AI turrets. This effect is permanent." - cost = 30 - upgrade = TRUE - unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/AI/AI) - for(var/obj/machinery/porta_turret/turret in GLOB.machines) - var/turf/T = get_turf(turret) - if(is_station_level(T.z)) - turret.health += 30 - turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. - turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' - -//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset. -/datum/AI_Module/large/lockdown - module_name = "Hostile Station Lockdown" - mod_pick_name = "lockdown" - description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \ - opening all doors on the station." - cost = 30 - one_purchase = TRUE - power_type = /datum/action/innate/ai/lockdown - unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time." - -/datum/action/innate/ai/lockdown - name = "Lockdown" - desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves." - button_icon_state = "lockdown" - uses = 1 - -/datum/action/innate/ai/lockdown/Activate() - for(var/obj/machinery/door/D in GLOB.airlocks) - if(!is_station_level(D.z)) - continue - INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner) - addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) - - post_status("alert", "lockdown") - - minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") - to_chat(owner, "Lockdown Initiated. Network reset in 90 seconds.") - spawn(900) - minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:") - -//Destroy RCDs: Detonates all non-cyborg RCDs on the station. -/datum/AI_Module/large/destroy_rcd - module_name = "Destroy RCDs" - mod_pick_name = "rcd" - description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station." - cost = 25 - one_purchase = TRUE - power_type = /datum/action/innate/ai/destroy_rcds - unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs." - -/datum/action/innate/ai/destroy_rcds - name = "Destroy RCDs" - desc = "Detonate all non-cyborg RCDs on the station." - button_icon_state = "detonate_rcds" - uses = 1 - cooldown_period = 100 - -/datum/action/innate/ai/destroy_rcds/Activate() - for(var/obj/item/rcd/RCD in GLOB.rcd_list) - if(!istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared. - RCD.detonate_pulse() - - to_chat(owner, "RCD detonation pulse emitted.") - owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0) - -//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right? -/datum/AI_Module/large/mecha_domination - module_name = "Unlock Mech Domination" - mod_pick_name = "mechjack" - description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ - Do not allow the mech to leave the station's vicinity or allow it to be destroyed." - cost = 30 - upgrade = TRUE - unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." - unlock_sound = 'sound/mecha/nominal.ogg' - -/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI) - AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk! - -//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job. -/datum/AI_Module/large/break_fire_alarms - module_name = "Thermal Sensor Override" - mod_pick_name = "burnpigs" - description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \ - Anyone can check the fire alarm's interface and may be tipped off by its status." - one_purchase = TRUE - cost = 25 - power_type = /datum/action/innate/ai/break_fire_alarms - unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will." - -/datum/action/innate/ai/break_fire_alarms - name = "Override Thermal Sensors" - desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless." - button_icon_state = "break_fire_alarms" - uses = 1 - -/datum/action/innate/ai/break_fire_alarms/Activate() - for(var/obj/machinery/firealarm/F in GLOB.machines) - if(!is_station_level(F.z)) - continue - F.emagged = TRUE - to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.") - owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) - -//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms. -/datum/AI_Module/large/break_air_alarms - module_name = "Air Alarm Safety Override" - mod_pick_name = "allow_flooding" - description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \ - Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality." - one_purchase = TRUE - cost = 50 - power_type = /datum/action/innate/ai/break_air_alarms - unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard." - -/datum/action/innate/ai/break_air_alarms - name = "Override Air Alarm Safeties" - desc = "Enables the Flood setting on all air alarms." - button_icon_state = "break_air_alarms" - uses = 1 - -/datum/action/innate/ai/break_air_alarms/Activate() - for(var/obj/machinery/alarm/AA in GLOB.machines) - if(!is_station_level(AA.z)) - continue - AA.emagged = TRUE - to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") - owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) - - -//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase. -/datum/AI_Module/small/overload_machine - module_name = "Machine Overload" - mod_pick_name = "overload" - description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase." - cost = 20 - power_type = /datum/action/innate/ai/ranged/overload_machine - unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery." - -/datum/action/innate/ai/ranged/overload_machine - name = "Overload Machine" - desc = "Overheats a machine, causing a small explosion after a short time." - button_icon_state = "overload_machine" - uses = 2 - linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine - -/datum/action/innate/ai/ranged/overload_machine/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M) - if(M && !QDELETED(M)) - explosion(get_turf(M), 0,1,1,0) - if(M) //to check if the explosion killed it before we try to delete it - qdel(M) - -/obj/effect/proc_holder/ranged_ai/overload_machine - active = FALSE - ranged_mousepointer = 'icons/effects/overload_machine_target.dmi' - enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel." - disable_text = "You release your hold on the powernet." - -/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) - if(..()) - return - if(ranged_ability_user.incapacitated()) - remove_ranged_ability() - return - if(!istype(target)) - to_chat(ranged_ability_user, "You can only overload machines!") - return - - ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0) - attached_action.adjust_uses(-1) - if(attached_action && attached_action.uses) - attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." - attached_action.UpdateButtonIcon() - target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") - addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom! - remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...") - return TRUE - - -//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself. -/datum/AI_Module/small/override_machine - module_name = "Machine Override" - mod_pick_name = "override" - description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses." - cost = 30 - power_type = /datum/action/innate/ai/ranged/override_machine - unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines." - -/datum/action/innate/ai/ranged/override_machine - name = "Override Machine" - desc = "Animates a targeted machine, causing it to attack anyone nearby." - button_icon_state = "override_machine" - uses = 4 - linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine - -/datum/action/innate/ai/ranged/override_machine/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M) - if(M && !QDELETED(M)) - new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1) - -/obj/effect/proc_holder/ranged_ai/override_machine - active = FALSE - ranged_mousepointer = 'icons/effects/override_machine_target.dmi' - enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel." - disable_text = "You release your hold on the powernet." - -/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) - if(..()) - return - if(ranged_ability_user.incapacitated()) - remove_ranged_ability() - return - if(!istype(target)) - to_chat(ranged_ability_user, "You can only animate machines!") - return - if(!target.can_be_overridden()) - to_chat(ranged_ability_user, "That machine can't be overridden!") - return - - ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0) - attached_action.adjust_uses(-1) - if(attached_action && attached_action.uses) - attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." - attached_action.UpdateButtonIcon() - target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") - addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep! - remove_ranged_ability(ranged_ability_user, "Sending override signal...") - return TRUE - - -//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. -/datum/AI_Module/large/place_cyborg_transformer - module_name = "Robotic Factory (Removes Shunting)" - mod_pick_name = "cyborgtransformer" - description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside." - cost = 100 - one_purchase = TRUE - power_type = /datum/action/innate/ai/place_transformer - unlock_text = "You prepare a robotics factory for deployment." - unlock_sound = 'sound/machines/ping.ogg' - -/datum/action/innate/ai/place_transformer - name = "Place Robotics Factory" - desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!" - button_icon_state = "robotic_factory" - uses = 1 - auto_use_uses = FALSE //So we can attempt multiple times - var/list/turfOverlays - -/datum/action/innate/ai/place_transformer/New() - ..() - for(var/i in 1 to 3) - var/image/I = image("icon"='icons/turf/overlays.dmi') - LAZYADD(turfOverlays, I) - -/datum/action/innate/ai/place_transformer/Activate() - if(!owner_AI.can_place_transformer(src)) - return - active = TRUE - if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No") - active = FALSE - return - if(!owner_AI.can_place_transformer(src)) - active = FALSE - return - var/turf/T = get_turf(owner_AI.eyeobj) - new /obj/machinery/transformer/conveyor(T) - playsound(T, 'sound/effects/phasein.ogg', 100, 1) - owner_AI.can_shunt = FALSE - to_chat(owner, "You are no longer able to shunt your core to APCs.") - adjust_uses(-1) - -/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T) - if(C && I.loc == T) - C.images -= I - -/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action) - if(!eyeobj || !isturf(loc) || incapacitated() || !action) - return - var/turf/middle = get_turf(eyeobj) - var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z)) - var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor." - var/success = TRUE - for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled - var/turf/T = turfs[n] - if(!isfloorturf(T)) - success = FALSE - var/datum/camerachunk/C = cameranet.getCameraChunk(T.x, T.y, T.z) - if(!C.visibleTurfs[T]) - alert_msg = "You don't have camera vision of this location!" - success = FALSE - for(var/atom/movable/AM in T.contents) - if(AM.density) - alert_msg = "That area must be clear of objects!" - success = FALSE - var/image/I = action.turfOverlays[n] - I.loc = T - client.images += I - I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively - addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30) - if(!success) - to_chat(src, "[alert_msg]") - return success - -//Blackout: Overloads a random number of lights across the station. Three uses. -/datum/AI_Module/small/blackout - module_name = "Blackout" - mod_pick_name = "blackout" - description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses." - cost = 15 - power_type = /datum/action/innate/ai/blackout - unlock_text = "You hook into the powernet and route bonus power towards the station's lighting." - -/datum/action/innate/ai/blackout - name = "Blackout" - desc = "Overloads random lights across the station." - button_icon_state = "blackout" - uses = 3 - auto_use_uses = FALSE - -/datum/action/innate/ai/blackout/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/blackout/Activate() - for(var/obj/machinery/power/apc/apc in GLOB.apcs) - if(prob(30 * apc.overload)) - apc.overload_lighting() - else - apc.overload++ - to_chat(owner, "Overcurrent applied to the powernet.") - owner.playsound_local(owner, "sparks", 50, 0) - adjust_uses(-1) - if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe - desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() - -//Reactivate Camera Network: Reactivates up to 30 cameras across the station. -/datum/AI_Module/small/reactivate_cameras - module_name = "Reactivate Camera Network" - mod_pick_name = "recam" - description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." - cost = 10 - one_purchase = TRUE - power_type = /datum/action/innate/ai/reactivate_cameras - unlock_text = "You deploy nanomachines to the cameranet." - -/datum/action/innate/ai/reactivate_cameras - name = "Reactivate Cameras" - desc = "Reactivates disabled cameras across the station; remaining uses can be used later." - button_icon_state = "reactivate_cameras" - uses = 30 - auto_use_uses = FALSE - cooldown_period = 30 - -/datum/action/innate/ai/reactivate_cameras/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/reactivate_cameras/Activate() - var/fixed_cameras = 0 - for(var/V in cameranet.cameras) - if(!uses) - break - var/obj/machinery/camera/C = V - if(!C.status || C.view_range != initial(C.view_range)) - C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc. - C.view_range = initial(C.view_range) - fixed_cameras++ - uses-- //Not adjust_uses() so it doesn't automatically delete or show a message - to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].") - owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0) - adjust_uses(0, TRUE) //Checks the uses remaining - if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe - desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() - -//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision. -/datum/AI_Module/large/upgrade_cameras - module_name = "Upgrade Camera Network" - mod_pick_name = "upgradecam" - description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble - //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. - one_purchase = TRUE - cost = 35 //Decent price for omniscience! - upgrade = TRUE - unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI) - AI.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //Night-vision, without which X-ray would be very limited in power. - AI.update_sight() - var/upgraded_cameras = 0 - - for(var/V in cameranet.cameras) - var/obj/machinery/camera/C = V - if(C.assembly) - var/upgraded = FALSE - - if(!C.isXRay()) - C.upgradeXRay() - //Update what it can see. - cameranet.updateVisibility(C, 0) - upgraded = TRUE - - if(!C.isEmpProof()) - C.upgradeEmpProof() - upgraded = TRUE - - if(upgraded) - upgraded_cameras++ - - unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade() - -/datum/AI_Module/large/eavesdrop - module_name = "Enhanced Surveillance" - mod_pick_name = "eavesdrop" - description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations." - cost = 30 - one_purchase = TRUE - upgrade = TRUE - unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI) - if(AI.eyeobj) - AI.eyeobj.relay_speech = TRUE - +//The malf AI action subtype. All malf actions are subtypes of this. +/datum/action/innate/ai + name = "AI Action" + desc = "You aren't entirely sure what this does, but it's very beepy and boopy." + background_icon_state = "bg_tech_blue" + var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time + var/uses //If we have multiple uses of the same power + var/auto_use_uses = TRUE //If we automatically use up uses on each activation + var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules + +/datum/action/innate/ai/Grant(mob/living/L) + . = ..() + if(!isAI(owner)) + WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!") + qdel(src) + else + owner_AI = owner + +/datum/action/innate/ai/IsAvailable() + . = ..() + if(owner_AI && owner_AI.malf_cooldown > world.time) + return + +/datum/action/innate/ai/Trigger() + . = ..() + if(auto_use_uses) + adjust_uses(-1) + if(cooldown_period) + owner_AI.malf_cooldown = world.time + cooldown_period + +/datum/action/innate/ai/proc/adjust_uses(amt, silent) + uses += amt + if(!silent && uses) + to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") + if(!uses) + if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! + to_chat(owner, "[name] has run out of uses!") + qdel(src) + +//Framework for ranged abilities that can have different effects by left-clicking stuff. +/datum/action/innate/ai/ranged + name = "Ranged AI Action" + auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses + var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code + var/linked_ability_type //The path of our linked ability + +/datum/action/innate/ai/ranged/New() + if(!linked_ability_type) + WARNING("Ranged AI action [name] attempted to spawn without a linked ability!") + qdel(src) //uh oh! + return + linked_ability = new linked_ability_type() + linked_ability.attached_action = src + ..() + +/datum/action/innate/ai/ranged/adjust_uses(amt, silent) + uses += amt + if(!silent && uses) + to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") + if(!uses) + if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! + to_chat(owner, "[name] has run out of uses!") + Remove(owner) + QDEL_IN(src, 100) //let any active timers on us finish up + +/datum/action/innate/ai/ranged/Destroy() + QDEL_NULL(linked_ability) + return ..() + +/datum/action/innate/ai/ranged/Activate() + linked_ability.toggle(owner) + return TRUE + +//The actual ranged proc holder. +/obj/effect/proc_holder/ranged_ai + var/enable_text = "Hello World!" //Appears when the user activates the ability + var/disable_text = "Goodbye Cruel World!" //Context clues! + var/datum/action/innate/ai/ranged/attached_action + +/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user) + if(active) + remove_ranged_ability(user, disable_text) + else + add_ranged_ability(user, enable_text) + +//The datum and interface for the malf unlock menu, which lets them choose actions to unlock. +/datum/module_picker + var/temp + var/processing_time = 50 + var/list/possible_modules + +/datum/module_picker/New() + possible_modules = list() + for(var/type in typesof(/datum/AI_Module)) + var/datum/AI_Module/AM = new type + if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) + possible_modules += AM + +/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. + for(var/datum/AI_Module/AM in possible_modules) + for(var/datum/action/A in AI.actions) + if(istype(A, initial(AM.power_type))) + qdel(A) + +/datum/module_picker/proc/use(user as mob) + var/dat + dat += {"Select use of processing time: (currently #[processing_time] left.)
    +
    + Install Module:
    + The number afterwards is the amount of processing time it consumes.
    "} + for(var/datum/AI_Module/large/module in possible_modules) + dat += "[module.module_name]\[?\] ([module.cost])
    " + for(var/datum/AI_Module/small/module in possible_modules) + dat += "[module.module_name]\[?\] ([module.cost])
    " + dat += "
    " + if(temp) + dat += "[temp]" + var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu", 400, 500) + popup.set_content(dat) + popup.open() + return + +/datum/module_picker/Topic(href, href_list) + ..() + + if(!isAI(usr)) + return + var/mob/living/silicon/ai/A = usr + + if(A.stat == DEAD) + to_chat(A, "You are already dead!") + return + + for(var/datum/AI_Module/AM in possible_modules) + if (href_list[AM.mod_pick_name]) + + // Cost check + if(AM.cost > processing_time) + temp = "You cannot afford this module." + break + + var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions + + // Give the power and take away the money. + if(AM.upgrade) //upgrade and upgrade() are separate, be careful! + AM.upgrade(A) + possible_modules -= AM + to_chat(A, AM.unlock_text) + A.playsound_local(A, AM.unlock_sound, 50, 0) + else + if(AM.power_type) + if(!action) //Unlocking for the first time + var/datum/action/AC = new AM.power_type + AC.Grant(A) + A.current_modules += new AM.type + temp = AM.description + if(AM.one_purchase) + possible_modules -= AM + if(AM.unlock_text) + to_chat(A, AM.unlock_text) + if(AM.unlock_sound) + A.playsound_local(A, AM.unlock_sound, 50, 0) + else //Adding uses to an existing module + action.uses += initial(action.uses) + action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." + action.UpdateButtonIcon() + temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" + processing_time -= AM.cost + + if(href_list["showdesc"]) + if(AM.mod_pick_name == href_list["showdesc"]) + temp = AM.description + use(usr) + +//The base module type, which holds info about each ability. +/datum/AI_Module + var/module_name + var/mod_pick_name + var/description = "" + var/engaged = 0 + var/cost = 5 + var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false. + var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade. + var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type. + var/unlock_text = "Hello World!" //Text shown when an ability is unlocked + var/unlock_sound //Sound played when an ability is unlocked + var/uses + +/datum/AI_Module/proc/upgrade(mob/living/silicon/AI/AI) //Apply upgrades! + return + +/datum/AI_Module/large //Big, powerful stuff that can only be used once. + +/datum/AI_Module/small //Weak, usually localized stuff with multiple uses. + +//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely. +/datum/AI_Module/large/nuke_station + module_name = "Doomsday Device" + mod_pick_name = "nukestation" + description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed." + cost = 130 + one_purchase = TRUE + power_type = /datum/action/innate/ai/nuke_station + unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time." + unlock_sound = 'sound/items/timer.ogg' + +/datum/action/innate/ai/nuke_station + name = "Doomsday Device" + desc = "Activates the doomsday device. This is not reversible." + button_icon_state = "doomsday_device" + auto_use_uses = FALSE + +/datum/action/innate/ai/nuke_station/Activate() + var/turf/T = get_turf(owner) + if(!istype(T) || !is_station_level(T.z)) + to_chat(owner, "You cannot activate the doomsday device while off-station!") + return + if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;") + return + if(active) + return //prevent the AI from activating an already active doomsday + active = TRUE + set_us_up_the_bomb() + +/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb() + to_chat(owner_AI, "Nuclear device armed.") + GLOB.event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') + set_security_level("delta") + owner_AI.nuking = TRUE + var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI) + owner_AI.doomsday_device = DOOM + owner_AI.doomsday_device.start() + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + for(var/mob/living/silicon/ai/A in GLOB.ai_list) + if((A.stat != DEAD) && A.nuking) + point.the_disk = A //The pinpointer now tracks the AI core + qdel(src) + +/obj/machinery/doomsday_device + icon = 'icons/obj/machines/nuke_terminal.dmi' + name = "doomsday device" + icon_state = "nuclearbomb_base" + desc = "A weapon which disintegrates all organic life in a large area." + anchored = 1 + density = 1 + atom_say_verb = "blares" + speed_process = TRUE // Disgusting fix. Please remove once #12952 is merged + var/timing = 0 + var/default_timer = 4500 + var/detonation_timer + var/announced = 0 + +/obj/machinery/doomsday_device/Destroy() + STOP_PROCESSING(SSfastprocess, src) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + return ..() + +/obj/machinery/doomsday_device/proc/start() + detonation_timer = world.time + default_timer + timing = 1 + START_PROCESSING(SSfastprocess, src) + SSshuttle.emergencyNoEscape = 1 + +/obj/machinery/doomsday_device/proc/seconds_remaining() + . = max(0, (round(detonation_timer - world.time) / 10)) + +/obj/machinery/doomsday_device/process() + var/turf/T = get_turf(src) + if(!T || !is_station_level(T.z)) + GLOB.minor_announcement.Announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + qdel(src) + if(!timing) + STOP_PROCESSING(SSfastprocess, src) + return + var/sec_left = seconds_remaining() + if(sec_left <= 0) + timing = 0 + detonate(T.z) + qdel(src) + else + if(!(sec_left % 60) && !announced) + var/message = "[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!" + GLOB.minor_announcement.Announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') + announced = 10 + announced = max(0, announced-1) + +/obj/machinery/doomsday_device/proc/detonate(z_level = 1) + for(var/mob/M in GLOB.player_list) + M << 'sound/machines/alarm.ogg' + sleep(100) + for(var/mob/living/L in GLOB.mob_list) + var/turf/T = get_turf(L) + if(!T || T.z != z_level) + continue + if(issilicon(L)) + continue + to_chat(L, "The blast wave from [src] tears you atom from atom!") + L.dust() + to_chat(world, "The AI cleansed the station of life with the doomsday device!") + SSticker.force_ending = 1 + +//AI Turret Upgrade: Increases the health and damage of all turrets. +/datum/AI_Module/large/upgrade_turrets + module_name = "AI Turret Upgrade" + mod_pick_name = "turret" + description = "Improves the power and health of all AI turrets. This effect is permanent." + cost = 30 + upgrade = TRUE + unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/AI/AI) + for(var/obj/machinery/porta_turret/turret in GLOB.machines) + var/turf/T = get_turf(turret) + if(is_station_level(T.z)) + turret.health += 30 + turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. + turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' + +//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset. +/datum/AI_Module/large/lockdown + module_name = "Hostile Station Lockdown" + mod_pick_name = "lockdown" + description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \ + opening all doors on the station." + cost = 30 + one_purchase = TRUE + power_type = /datum/action/innate/ai/lockdown + unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time." + +/datum/action/innate/ai/lockdown + name = "Lockdown" + desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves." + button_icon_state = "lockdown" + uses = 1 + +/datum/action/innate/ai/lockdown/Activate() + for(var/obj/machinery/door/D in GLOB.airlocks) + if(!is_station_level(D.z)) + continue + INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner) + addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) + + post_status("alert", "lockdown") + + GLOB.minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") + to_chat(owner, "Lockdown Initiated. Network reset in 90 seconds.") + spawn(900) + GLOB.minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:") + +//Destroy RCDs: Detonates all non-cyborg RCDs on the station. +/datum/AI_Module/large/destroy_rcd + module_name = "Destroy RCDs" + mod_pick_name = "rcd" + description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station." + cost = 25 + one_purchase = TRUE + power_type = /datum/action/innate/ai/destroy_rcds + unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs." + +/datum/action/innate/ai/destroy_rcds + name = "Destroy RCDs" + desc = "Detonate all non-cyborg RCDs on the station." + button_icon_state = "detonate_rcds" + uses = 1 + cooldown_period = 100 + +/datum/action/innate/ai/destroy_rcds/Activate() + for(var/obj/item/rcd/RCD in GLOB.rcd_list) + if(!istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared. + RCD.detonate_pulse() + + to_chat(owner, "RCD detonation pulse emitted.") + owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0) + +//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right? +/datum/AI_Module/large/mecha_domination + module_name = "Unlock Mech Domination" + mod_pick_name = "mechjack" + description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ + Do not allow the mech to leave the station's vicinity or allow it to be destroyed." + cost = 30 + upgrade = TRUE + unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." + unlock_sound = 'sound/mecha/nominal.ogg' + +/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI) + AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk! + +//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job. +/datum/AI_Module/large/break_fire_alarms + module_name = "Thermal Sensor Override" + mod_pick_name = "burnpigs" + description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \ + Anyone can check the fire alarm's interface and may be tipped off by its status." + one_purchase = TRUE + cost = 25 + power_type = /datum/action/innate/ai/break_fire_alarms + unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will." + +/datum/action/innate/ai/break_fire_alarms + name = "Override Thermal Sensors" + desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless." + button_icon_state = "break_fire_alarms" + uses = 1 + +/datum/action/innate/ai/break_fire_alarms/Activate() + for(var/obj/machinery/firealarm/F in GLOB.machines) + if(!is_station_level(F.z)) + continue + F.emagged = TRUE + to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.") + owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) + +//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms. +/datum/AI_Module/large/break_air_alarms + module_name = "Air Alarm Safety Override" + mod_pick_name = "allow_flooding" + description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \ + Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality." + one_purchase = TRUE + cost = 50 + power_type = /datum/action/innate/ai/break_air_alarms + unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard." + +/datum/action/innate/ai/break_air_alarms + name = "Override Air Alarm Safeties" + desc = "Enables the Flood setting on all air alarms." + button_icon_state = "break_air_alarms" + uses = 1 + +/datum/action/innate/ai/break_air_alarms/Activate() + for(var/obj/machinery/alarm/AA in GLOB.machines) + if(!is_station_level(AA.z)) + continue + AA.emagged = TRUE + to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") + owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) + + +//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase. +/datum/AI_Module/small/overload_machine + module_name = "Machine Overload" + mod_pick_name = "overload" + description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase." + cost = 20 + power_type = /datum/action/innate/ai/ranged/overload_machine + unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery." + +/datum/action/innate/ai/ranged/overload_machine + name = "Overload Machine" + desc = "Overheats a machine, causing a small explosion after a short time." + button_icon_state = "overload_machine" + uses = 2 + linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine + +/datum/action/innate/ai/ranged/overload_machine/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M) + if(M && !QDELETED(M)) + explosion(get_turf(M), 0,1,1,0) + if(M) //to check if the explosion killed it before we try to delete it + qdel(M) + +/obj/effect/proc_holder/ranged_ai/overload_machine + active = FALSE + ranged_mousepointer = 'icons/effects/overload_machine_target.dmi' + enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel." + disable_text = "You release your hold on the powernet." + +/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + if(!istype(target)) + to_chat(ranged_ability_user, "You can only overload machines!") + return + + ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0) + attached_action.adjust_uses(-1) + if(attached_action && attached_action.uses) + attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." + attached_action.UpdateButtonIcon() + target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") + addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom! + remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...") + return TRUE + + +//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself. +/datum/AI_Module/small/override_machine + module_name = "Machine Override" + mod_pick_name = "override" + description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses." + cost = 30 + power_type = /datum/action/innate/ai/ranged/override_machine + unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines." + +/datum/action/innate/ai/ranged/override_machine + name = "Override Machine" + desc = "Animates a targeted machine, causing it to attack anyone nearby." + button_icon_state = "override_machine" + uses = 4 + linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine + +/datum/action/innate/ai/ranged/override_machine/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M) + if(M && !QDELETED(M)) + new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1) + +/obj/effect/proc_holder/ranged_ai/override_machine + active = FALSE + ranged_mousepointer = 'icons/effects/override_machine_target.dmi' + enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel." + disable_text = "You release your hold on the powernet." + +/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + if(!istype(target)) + to_chat(ranged_ability_user, "You can only animate machines!") + return + if(!target.can_be_overridden()) + to_chat(ranged_ability_user, "That machine can't be overridden!") + return + + ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0) + attached_action.adjust_uses(-1) + if(attached_action && attached_action.uses) + attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." + attached_action.UpdateButtonIcon() + target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") + addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep! + remove_ranged_ability(ranged_ability_user, "Sending override signal...") + return TRUE + + +//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. +/datum/AI_Module/large/place_cyborg_transformer + module_name = "Robotic Factory (Removes Shunting)" + mod_pick_name = "cyborgtransformer" + description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside." + cost = 100 + one_purchase = TRUE + power_type = /datum/action/innate/ai/place_transformer + unlock_text = "You prepare a robotics factory for deployment." + unlock_sound = 'sound/machines/ping.ogg' + +/datum/action/innate/ai/place_transformer + name = "Place Robotics Factory" + desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!" + button_icon_state = "robotic_factory" + uses = 1 + auto_use_uses = FALSE //So we can attempt multiple times + var/list/turfOverlays + +/datum/action/innate/ai/place_transformer/New() + ..() + for(var/i in 1 to 3) + var/image/I = image("icon"='icons/turf/overlays.dmi') + LAZYADD(turfOverlays, I) + +/datum/action/innate/ai/place_transformer/Activate() + if(!owner_AI.can_place_transformer(src)) + return + active = TRUE + if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No") + active = FALSE + return + if(!owner_AI.can_place_transformer(src)) + active = FALSE + return + var/turf/T = get_turf(owner_AI.eyeobj) + new /obj/machinery/transformer/conveyor(T) + playsound(T, 'sound/effects/phasein.ogg', 100, 1) + owner_AI.can_shunt = FALSE + to_chat(owner, "You are no longer able to shunt your core to APCs.") + adjust_uses(-1) + +/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T) + if(C && I.loc == T) + C.images -= I + +/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action) + if(!eyeobj || !isturf(loc) || incapacitated() || !action) + return + var/turf/middle = get_turf(eyeobj) + var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z)) + var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor." + var/success = TRUE + for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled + var/turf/T = turfs[n] + if(!isfloorturf(T)) + success = FALSE + var/datum/camerachunk/C = GLOB.cameranet.getCameraChunk(T.x, T.y, T.z) + if(!C.visibleTurfs[T]) + alert_msg = "You don't have camera vision of this location!" + success = FALSE + for(var/atom/movable/AM in T.contents) + if(AM.density) + alert_msg = "That area must be clear of objects!" + success = FALSE + var/image/I = action.turfOverlays[n] + I.loc = T + client.images += I + I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively + addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30) + if(!success) + to_chat(src, "[alert_msg]") + return success + +//Blackout: Overloads a random number of lights across the station. Three uses. +/datum/AI_Module/small/blackout + module_name = "Blackout" + mod_pick_name = "blackout" + description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses." + cost = 15 + power_type = /datum/action/innate/ai/blackout + unlock_text = "You hook into the powernet and route bonus power towards the station's lighting." + +/datum/action/innate/ai/blackout + name = "Blackout" + desc = "Overloads random lights across the station." + button_icon_state = "blackout" + uses = 3 + auto_use_uses = FALSE + +/datum/action/innate/ai/blackout/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/blackout/Activate() + for(var/obj/machinery/power/apc/apc in GLOB.apcs) + if(prob(30 * apc.overload)) + apc.overload_lighting() + else + apc.overload++ + to_chat(owner, "Overcurrent applied to the powernet.") + owner.playsound_local(owner, "sparks", 50, 0) + adjust_uses(-1) + if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe + desc = "[initial(desc)] It has [uses] use\s remaining." + UpdateButtonIcon() + +//Reactivate Camera Network: Reactivates up to 30 cameras across the station. +/datum/AI_Module/small/reactivate_cameras + module_name = "Reactivate Camera Network" + mod_pick_name = "recam" + description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." + cost = 10 + one_purchase = TRUE + power_type = /datum/action/innate/ai/reactivate_cameras + unlock_text = "You deploy nanomachines to the cameranet." + +/datum/action/innate/ai/reactivate_cameras + name = "Reactivate Cameras" + desc = "Reactivates disabled cameras across the station; remaining uses can be used later." + button_icon_state = "reactivate_cameras" + uses = 30 + auto_use_uses = FALSE + cooldown_period = 30 + +/datum/action/innate/ai/reactivate_cameras/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/reactivate_cameras/Activate() + var/fixed_cameras = 0 + for(var/V in GLOB.cameranet.cameras) + if(!uses) + break + var/obj/machinery/camera/C = V + if(!C.status || C.view_range != initial(C.view_range)) + C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc. + C.view_range = initial(C.view_range) + fixed_cameras++ + uses-- //Not adjust_uses() so it doesn't automatically delete or show a message + to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].") + owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0) + adjust_uses(0, TRUE) //Checks the uses remaining + if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe + desc = "[initial(desc)] It has [uses] use\s remaining." + UpdateButtonIcon() + +//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision. +/datum/AI_Module/large/upgrade_cameras + module_name = "Upgrade Camera Network" + mod_pick_name = "upgradecam" + description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble + //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. + one_purchase = TRUE + cost = 35 //Decent price for omniscience! + upgrade = TRUE + unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI) + AI.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //Night-vision, without which X-ray would be very limited in power. + AI.update_sight() + var/upgraded_cameras = 0 + + for(var/V in GLOB.cameranet.cameras) + var/obj/machinery/camera/C = V + if(C.assembly) + var/upgraded = FALSE + + if(!C.isXRay()) + C.upgradeXRay() + //Update what it can see. + GLOB.cameranet.updateVisibility(C, 0) + upgraded = TRUE + + if(!C.isEmpProof()) + C.upgradeEmpProof() + upgraded = TRUE + + if(upgraded) + upgraded_cameras++ + + unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade() + +/datum/AI_Module/large/eavesdrop + module_name = "Enhanced Surveillance" + mod_pick_name = "eavesdrop" + description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations." + cost = 30 + one_purchase = TRUE + upgrade = TRUE + unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI) + if(AI.eyeobj) + AI.eyeobj.relay_speech = TRUE + diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index ee0ef02d1dcd..5cd46ca89b5e 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,61 +1,61 @@ -/datum/game_mode/meteor - name = "meteor" - config_tag = "meteor" - var/const/initialmeteordelay = 6000 - var/wave = 1 - required_players = 35 - - -/datum/game_mode/meteor/announce() - to_chat(world, "The current game mode is - Meteor!") - to_chat(world, "The space station has been stuck in a major meteor shower. You must escape from the station or at least live.") - - -/datum/game_mode/meteor/post_setup() - spawn(rand(waittime_l, waittime_h)) - command_announcement.Announce("The station is on the path of an incoming wave of meteors. Reinforce the hull and prepare damage control parties.", "Incoming Meteors", 'sound/effects/siren.ogg') - spawn(initialmeteordelay) - sendmeteors() - ..() - - - -/datum/game_mode/meteor/proc/sendmeteors() - var/waveduration = world.time + rand(0,1000) + text2num("[wave]000") / 2 - var/waitduration = rand(3000,6000) - while(waveduration - world.time > 0) - sleep(max(65 - text2num("[wave]0") / 2, 40)) - spawn() spawn_meteors(6, meteors_normal) - wave++ - sleep(waitduration) - sendmeteors() - -/datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 - for(var/mob/living/player in GLOB.player_list) - if(player.stat != DEAD) - var/turf/location = get_turf(player.loc) - if(!location) continue - - if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason - text += "
    [player.real_name] escaped on the emergency shuttle" - - else - switch(location.loc.type) - if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) - text += "
    [player.real_name] escaped in a life pod." - else - text += "
    [player.real_name] survived but is stranded without any hope of rescue." - survivors++ - - if(survivors) - to_chat(world, "The following survived the meteor storm:[text]") - else - to_chat(world, "Nobody survived the meteor storm!") - - feedback_set_details("round_end_result","meteor end - evacuation") - feedback_set("round_end_result", "Meteor survivors: [survivors]") - - ..() - return 1 +/datum/game_mode/meteor + name = "meteor" + config_tag = "meteor" + var/const/initialmeteordelay = 6000 + var/wave = 1 + required_players = 35 + + +/datum/game_mode/meteor/announce() + to_chat(world, "The current game mode is - Meteor!") + to_chat(world, "The space station has been stuck in a major meteor shower. You must escape from the station or at least live.") + + +/datum/game_mode/meteor/post_setup() + spawn(rand(waittime_l, waittime_h)) + GLOB.command_announcement.Announce("The station is on the path of an incoming wave of meteors. Reinforce the hull and prepare damage control parties.", "Incoming Meteors", 'sound/effects/siren.ogg') + spawn(initialmeteordelay) + sendmeteors() + ..() + + + +/datum/game_mode/meteor/proc/sendmeteors() + var/waveduration = world.time + rand(0,1000) + text2num("[wave]000") / 2 + var/waitduration = rand(3000,6000) + while(waveduration - world.time > 0) + sleep(max(65 - text2num("[wave]0") / 2, 40)) + spawn() spawn_meteors(6, GLOB.meteors_normal) + wave++ + sleep(waitduration) + sendmeteors() + +/datum/game_mode/meteor/declare_completion() + var/text + var/survivors = 0 + for(var/mob/living/player in GLOB.player_list) + if(player.stat != DEAD) + var/turf/location = get_turf(player.loc) + if(!location) continue + + if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason + text += "
    [player.real_name] escaped on the emergency shuttle" + + else + switch(location.loc.type) + if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) + text += "
    [player.real_name] escaped in a life pod." + else + text += "
    [player.real_name] survived but is stranded without any hope of rescue." + survivors++ + + if(survivors) + to_chat(world, "The following survived the meteor storm:[text]") + else + to_chat(world, "Nobody survived the meteor storm!") + + feedback_set_details("round_end_result","meteor end - evacuation") + feedback_set("round_end_result", "Meteor survivors: [survivors]") + + ..() + return 1 diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 5dafcee72a74..0743a6f686da 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,318 +1,318 @@ -//Meteors probability of spawning during a given wave -/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ - /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event - -/var/list/meteors_threatening = list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ - /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3) //for threatening meteor event - -/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ - /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event - -/var/list/meteors_dust = list(/obj/effect/meteor/dust) //for space dust event - -/var/list/meteors_gore = list(/obj/effect/meteor/gore) //Meaty Gore - -/var/list/meteors_ops = list(/obj/effect/meteor/goreops) //Meaty Ops - - -/////////////////////////////// -//Meteor spawning global procs -/////////////////////////////// -/proc/spawn_meteors(var/number = 10, var/list/meteortypes) - for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes) - -/proc/spawn_meteor(var/list/meteortypes) - var/turf/pickedstart - var/turf/pickedgoal - var/max_i = 10//number of tries to spawn meteor. - while(!istype(pickedstart, /turf/space)) - var/startSide = pick(cardinal) - pickedstart = spaceDebrisStartLoc(startSide, 1) - pickedgoal = spaceDebrisFinishLoc(startSide, 1) - max_i-- - if(max_i<=0) - return - var/Me = pickweight(meteortypes) - var/obj/effect/meteor/M = new Me(pickedstart) - M.dest = pickedgoal - M.z_original = level_name_to_num(MAIN_STATION) - spawn(0) - walk_towards(M, M.dest, 1) - return - -/proc/spaceDebrisStartLoc(startSide, Z) - var/starty - var/startx - switch(startSide) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - var/turf/T = locate(startx, starty, Z) - return T - -/proc/spaceDebrisFinishLoc(startSide, Z) - var/endy - var/endx - switch(startSide) - if(NORTH) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - var/turf/T = locate(endx, endy, Z) - return T - -/////////////////////// -//The meteor effect -////////////////////// - -/obj/effect/meteor - name = "the concept of meteor" - desc = "You should probably run instead of gawking at this." - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - density = 1 - anchored = 1 - var/hits = 4 - var/hitpwr = 2 //Level of ex_act to be called on hit. - var/dest - pass_flags = PASSTABLE - var/heavy = 0 - var/meteorsound = 'sound/effects/meteorimpact.ogg' - var/z_original = 1 - - var/meteordrop = /obj/item/stack/ore/iron - var/dropamt = 2 - -/obj/effect/meteor/Move() - if(z != z_original || loc == dest) - qdel(src) - return - - . = ..() //process movement... - - if(.)//.. if did move, ram the turf we get in - var/turf/T = get_turf(loc) - ram_turf(T) - - if(prob(10) && !istype(T, /turf/space))//randomly takes a 'hit' from ramming - get_hit() - - return . - -/obj/effect/meteor/Destroy() - GLOB.meteor_list -= src - walk(src,0) //this cancels the walk_towards() proc - return ..() - -/obj/effect/meteor/New() - ..() - GLOB.meteor_list += src - SpinAnimation() - -/obj/effect/meteor/Bump(atom/A) - if(A) - ram_turf(get_turf(A)) - playsound(src.loc, meteorsound, 40, 1) - get_hit() - -/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0) - return istype(mover, /obj/effect/meteor) ? 1 : ..() - -/obj/effect/meteor/proc/ram_turf(var/turf/T) - //first bust whatever is in the turf - for(var/atom/A in T) - if(A != src) - A.ex_act(hitpwr) - - //then, ram the turf if it still exists - if(T) - T.ex_act(hitpwr) - -//process getting 'hit' by colliding with a dense object -//or randomly when ramming turfs -/obj/effect/meteor/proc/get_hit() - hits-- - if(hits <= 0) - make_debris() - meteor_effect(heavy) - qdel(src) - -/obj/effect/meteor/ex_act() - return - -/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/pickaxe)) - make_debris() - qdel(src) - return - return ..() - -/obj/effect/meteor/proc/make_debris() - for(var/throws = dropamt, throws > 0, throws--) - var/obj/item/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/meteor_effect(var/sound=1) - if(sound) - var/sound/meteor_sound = sound(meteorsound) - var/random_frequency = get_rand_frequency() - - for(var/P in GLOB.player_list) - var/mob/M = P - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - if(prob(50)) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) - M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) - -/////////////////////// -//Meteor types -/////////////////////// - -//Dust -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - hitpwr = 3 - meteorsound = 'sound/weapons/tap.ogg' - meteordrop = /obj/item/stack/ore/glass - -//Medium-sized -/obj/effect/meteor/medium - name = "meteor" - dropamt = 3 - -/obj/effect/meteor/medium/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 1, 2, 3, 0) - -//Large-sized -/obj/effect/meteor/big - name = "large meteor" - icon_state = "large" - hits = 6 - heavy = 1 - dropamt = 4 - -/obj/effect/meteor/big/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0) - -//Flaming meteor -/obj/effect/meteor/flaming - name = "flaming meteor" - icon_state = "flaming" - hits = 5 - heavy = 1 - meteorsound = 'sound/effects/bamf.ogg' - meteordrop = /obj/item/stack/ore/plasma - -/obj/effect/meteor/flaming/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5) - -//Radiation meteor -/obj/effect/meteor/irradiated - name = "glowing meteor" - icon_state = "glowing" - heavy = 1 - meteordrop = /obj/item/stack/ore/uranium - - -/obj/effect/meteor/irradiated/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 0, 4, 3, 0) - new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - for(var/mob/living/L in view(5, src)) - L.apply_effect(40, IRRADIATE) - -//Station buster Tunguska -/obj/effect/meteor/tunguska - name = "tunguska meteor" - icon_state = "flaming" - desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." - hits = 30 - hitpwr = 1 - heavy = 1 - meteorsound = 'sound/effects/bamf.ogg' - meteordrop = /obj/item/stack/ore/plasma - -/obj/effect/meteor/tunguska/meteor_effect() - ..(heavy) - explosion(src.loc, 5, 10, 15, 20, 0) - -/obj/effect/meteor/tunguska/Bump() - ..() - if(prob(20)) - explosion(src.loc,2,4,6,8) - - -//Gore -/obj/effect/meteor/gore - name = "Oraganic Debris" - icon = 'icons/mob/human.dmi' - icon_state = "body_m_s" - hits = 1 - hitpwr = 0 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic - -/obj/effect/meteor/gore/make_debris() - ..() - new meteorgibs(get_turf(src)) - - -/obj/effect/meteor/gore/ram_turf(turf/T) - if(!istype(T, /turf/space)) - new /obj/effect/decal/cleanable/blood(T) - -/obj/effect/meteor/gore/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() - -//Meteor Ops -/obj/effect/meteor/goreops - name = "MeteorOps" - icon = 'icons/mob/animal.dmi' - icon_state = "syndicaterangedpsace" - hits = 10 - hitpwr = 1 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic - -/obj/effect/meteor/goreops/make_debris() - ..() - new meteorgibs(get_turf(src)) - - -/obj/effect/meteor/goreops/ram_turf(turf/T) - if(!istype(T, /turf/space)) - new /obj/effect/decal/cleanable/blood(T) - -/obj/effect/meteor/goreops/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() +//Meteors probability of spawning during a given wave +GLOBAL_LIST_INIT(meteors_normal, list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ + /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3)) //for normal meteor event + +GLOBAL_LIST_INIT(meteors_threatening, list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ + /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3)) //for threatening meteor event + +GLOBAL_LIST_INIT(meteors_catastrophic, list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ + /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1)) //for catastrophic meteor event + +GLOBAL_LIST_INIT(meteors_dust, list(/obj/effect/meteor/dust)) //for space dust event + +GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/gore)) //Meaty Gore + +GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops + + +/////////////////////////////// +//Meteor spawning global procs +/////////////////////////////// +/proc/spawn_meteors(var/number = 10, var/list/meteortypes) + for(var/i = 0; i < number; i++) + spawn_meteor(meteortypes) + +/proc/spawn_meteor(var/list/meteortypes) + var/turf/pickedstart + var/turf/pickedgoal + var/max_i = 10//number of tries to spawn meteor. + while(!istype(pickedstart, /turf/space)) + var/startSide = pick(GLOB.cardinal) + pickedstart = spaceDebrisStartLoc(startSide, 1) + pickedgoal = spaceDebrisFinishLoc(startSide, 1) + max_i-- + if(max_i<=0) + return + var/Me = pickweight(meteortypes) + var/obj/effect/meteor/M = new Me(pickedstart) + M.dest = pickedgoal + M.z_original = level_name_to_num(MAIN_STATION) + spawn(0) + walk_towards(M, M.dest, 1) + return + +/proc/spaceDebrisStartLoc(startSide, Z) + var/starty + var/startx + switch(startSide) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + var/turf/T = locate(startx, starty, Z) + return T + +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(EAST) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + var/turf/T = locate(endx, endy, Z) + return T + +/////////////////////// +//The meteor effect +////////////////////// + +/obj/effect/meteor + name = "the concept of meteor" + desc = "You should probably run instead of gawking at this." + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + density = 1 + anchored = 1 + var/hits = 4 + var/hitpwr = 2 //Level of ex_act to be called on hit. + var/dest + pass_flags = PASSTABLE + var/heavy = 0 + var/meteorsound = 'sound/effects/meteorimpact.ogg' + var/z_original = 1 + + var/meteordrop = /obj/item/stack/ore/iron + var/dropamt = 2 + +/obj/effect/meteor/Move() + if(z != z_original || loc == dest) + qdel(src) + return + + . = ..() //process movement... + + if(.)//.. if did move, ram the turf we get in + var/turf/T = get_turf(loc) + ram_turf(T) + + if(prob(10) && !istype(T, /turf/space))//randomly takes a 'hit' from ramming + get_hit() + + return . + +/obj/effect/meteor/Destroy() + GLOB.meteor_list -= src + walk(src,0) //this cancels the walk_towards() proc + return ..() + +/obj/effect/meteor/New() + ..() + GLOB.meteor_list += src + SpinAnimation() + +/obj/effect/meteor/Bump(atom/A) + if(A) + ram_turf(get_turf(A)) + playsound(src.loc, meteorsound, 40, 1) + get_hit() + +/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0) + return istype(mover, /obj/effect/meteor) ? 1 : ..() + +/obj/effect/meteor/proc/ram_turf(var/turf/T) + //first bust whatever is in the turf + for(var/atom/A in T) + if(A != src) + A.ex_act(hitpwr) + + //then, ram the turf if it still exists + if(T) + T.ex_act(hitpwr) + +//process getting 'hit' by colliding with a dense object +//or randomly when ramming turfs +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + make_debris() + meteor_effect(heavy) + qdel(src) + +/obj/effect/meteor/ex_act() + return + +/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/pickaxe)) + make_debris() + qdel(src) + return + return ..() + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/obj/item/O = new meteordrop(get_turf(src)) + O.throw_at(dest, 5, 10) + +/obj/effect/meteor/proc/meteor_effect(var/sound=1) + if(sound) + var/sound/meteor_sound = sound(meteorsound) + var/random_frequency = get_rand_frequency() + + for(var/P in GLOB.player_list) + var/mob/M = P + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + if(prob(50)) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) + +/////////////////////// +//Meteor types +/////////////////////// + +//Dust +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + hitpwr = 3 + meteorsound = 'sound/weapons/tap.ogg' + meteordrop = /obj/item/stack/ore/glass + +//Medium-sized +/obj/effect/meteor/medium + name = "meteor" + dropamt = 3 + +/obj/effect/meteor/medium/meteor_effect() + ..(heavy) + explosion(src.loc, 0, 1, 2, 3, 0) + +//Large-sized +/obj/effect/meteor/big + name = "large meteor" + icon_state = "large" + hits = 6 + heavy = 1 + dropamt = 4 + +/obj/effect/meteor/big/meteor_effect() + ..(heavy) + explosion(src.loc, 1, 2, 3, 4, 0) + +//Flaming meteor +/obj/effect/meteor/flaming + name = "flaming meteor" + icon_state = "flaming" + hits = 5 + heavy = 1 + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = /obj/item/stack/ore/plasma + +/obj/effect/meteor/flaming/meteor_effect() + ..(heavy) + explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5) + +//Radiation meteor +/obj/effect/meteor/irradiated + name = "glowing meteor" + icon_state = "glowing" + heavy = 1 + meteordrop = /obj/item/stack/ore/uranium + + +/obj/effect/meteor/irradiated/meteor_effect() + ..(heavy) + explosion(src.loc, 0, 0, 4, 3, 0) + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + for(var/mob/living/L in view(5, src)) + L.apply_effect(40, IRRADIATE) + +//Station buster Tunguska +/obj/effect/meteor/tunguska + name = "tunguska meteor" + icon_state = "flaming" + desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." + hits = 30 + hitpwr = 1 + heavy = 1 + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = /obj/item/stack/ore/plasma + +/obj/effect/meteor/tunguska/meteor_effect() + ..(heavy) + explosion(src.loc, 5, 10, 15, 20, 0) + +/obj/effect/meteor/tunguska/Bump() + ..() + if(prob(20)) + explosion(src.loc,2,4,6,8) + + +//Gore +/obj/effect/meteor/gore + name = "Oraganic Debris" + icon = 'icons/mob/human.dmi' + icon_state = "body_m_s" + hits = 1 + hitpwr = 0 + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = /obj/item/reagent_containers/food/snacks/meat + var/meteorgibs = /obj/effect/gibspawner/generic + +/obj/effect/meteor/gore/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/gore/ram_turf(turf/T) + if(!istype(T, /turf/space)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/gore/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() + +//Meteor Ops +/obj/effect/meteor/goreops + name = "MeteorOps" + icon = 'icons/mob/animal.dmi' + icon_state = "syndicaterangedpsace" + hits = 10 + hitpwr = 1 + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = /obj/item/reagent_containers/food/snacks/meat + var/meteorgibs = /obj/effect/gibspawner/generic + +/obj/effect/meteor/goreops/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/goreops/ram_turf(turf/T) + if(!istype(T, /turf/space)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/goreops/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() diff --git a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm index 5db63d3450d9..3859d68accb8 100644 --- a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm +++ b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm @@ -144,4 +144,4 @@ explanation_text = "You are doomed to feel woefully incomplete forever... until you find your true love on this station. They're waiting for you!" /datum/objective/abductee/sixthsense - explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo." \ No newline at end of file + explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo." diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm index bcbc86d991bf..1888d0e4a1dc 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction.dm @@ -47,7 +47,7 @@ /datum/game_mode/abduction/proc/make_abductor_team(team_number,preset_agent=null,preset_scientist=null) //Team Name - team_names[team_number] = "Mothership [pick(possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names + team_names[team_number] = "Mothership [pick(GLOB.possible_changeling_IDs)]" //TODO Ensure unique and actual alieny names //Team Objective var/datum/objective/experiment/team_objective = new team_objective.team = team_number @@ -267,6 +267,7 @@ SSticker.mode.abductors -= abductor_mind abductor_mind.special_role = null abductor_mind.current.create_attack_log("No longer abductor") + abductor_mind.current.create_log(CONVERSION_LOG, "No longer abductor") if(issilicon(abductor_mind.current)) to_chat(abductor_mind.current, "You have been turned into a robot! You are no longer an abductor.") else @@ -274,11 +275,11 @@ SSticker.mode.update_abductor_icons_removed(abductor_mind) /datum/game_mode/proc/update_abductor_icons_added(datum/mind/alien_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR] hud.join_hud(alien_mind.current) set_antag_hud(alien_mind.current, ((alien_mind in abductors) ? "abductor" : "abductee")) /datum/game_mode/proc/update_abductor_icons_removed(datum/mind/alien_mind) - var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR] + var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_ABDUCTOR] hud.leave_hud(alien_mind.current) set_antag_hud(alien_mind.current, null) \ No newline at end of file diff --git a/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm b/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm index c90aaac19d83..6478a997b763 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm @@ -59,4 +59,4 @@ ..() if(!visualsOnly) var/obj/item/implant/abductor/beamplant = new /obj/item/implant/abductor(H) - beamplant.implant(H) \ No newline at end of file + beamplant.implant(H) diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index 23d9792ca4cb..e23e37c349e3 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -69,7 +69,7 @@ active = 0 if(initial(uses) == 1) uses = initial(uses) - var/datum/atom_hud/abductor/hud = huds[DATA_HUD_ABDUCTOR] + var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR] hud.remove_from_hud(owner) clear_mind_control() . = ..() @@ -78,7 +78,7 @@ ..() if(special != 2 && uses) // Special 2 means abductor surgery Start() - var/datum/atom_hud/abductor/hud = huds[DATA_HUD_ABDUCTOR] + var/datum/atom_hud/abductor/hud = GLOB.huds[DATA_HUD_ABDUCTOR] hud.add_to_hud(owner) update_gland_hud() @@ -277,14 +277,14 @@ ..() if(ishuman(owner)) owner.gene_stability += GENE_INSTABILITY_MODERATE // give them this gene for free - owner.dna.SetSEState(SHOCKIMMUNITYBLOCK, TRUE) - genemutcheck(owner, SHOCKIMMUNITYBLOCK, null, MUTCHK_FORCED) + owner.dna.SetSEState(GLOB.shockimmunityblock, TRUE) + genemutcheck(owner, GLOB.shockimmunityblock, null, MUTCHK_FORCED) /obj/item/organ/internal/heart/gland/electric/remove(mob/living/carbon/M, special = 0) if(ishuman(owner)) owner.gene_stability -= GENE_INSTABILITY_MODERATE // but return it to normal once it's removed - owner.dna.SetSEState(SHOCKIMMUNITYBLOCK, FALSE) - genemutcheck(owner, SHOCKIMMUNITYBLOCK, null, MUTCHK_FORCED) + owner.dna.SetSEState(GLOB.shockimmunityblock, FALSE) + genemutcheck(owner, GLOB.shockimmunityblock, null, MUTCHK_FORCED) return ..() /obj/item/organ/internal/heart/gland/electric/activate() diff --git a/code/game/gamemodes/miniantags/abduction/machinery/camera.dm b/code/game/gamemodes/miniantags/abduction/machinery/camera.dm index 951ee6806de2..6fb401d31e94 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/camera.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/camera.dm @@ -73,7 +73,7 @@ var/mob/camera/aiEye/remote/remote_eye = C.remote_control var/obj/machinery/abductor/pad/P = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) P.PadToLoc(remote_eye.loc) /datum/action/innate/teleport_out @@ -98,7 +98,7 @@ var/mob/camera/aiEye/remote/remote_eye = C.remote_control var/obj/machinery/abductor/pad/P = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) P.MobToLoc(remote_eye.loc,C) /datum/action/innate/vest_mode_swap diff --git a/code/game/gamemodes/miniantags/abduction/machinery/console.dm b/code/game/gamemodes/miniantags/abduction/machinery/console.dm index b162103c88b8..17ebcbdd2704 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/console.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/console.dm @@ -221,4 +221,4 @@ else new item(src.loc) else - atom_say("Insufficent data!") \ No newline at end of file + atom_say("Insufficent data!") diff --git a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm index 1a377667650f..5964c6f379ba 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm @@ -83,4 +83,4 @@ if(amounts[count]>0) amounts[count]-- var/T = gland_types[count] - new T(get_turf(src)) \ No newline at end of file + new T(get_turf(src)) diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm index 04dda244a0ce..6e1b4a39d3ac 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm @@ -176,7 +176,7 @@ H.uncuff() return //Area not chosen / It's not safe area - teleport to arrivals - H.forceMove(pick(latejoin)) + H.forceMove(pick(GLOB.latejoin)) H.uncuff() return diff --git a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm index 697d173716af..24e43b6583f7 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm @@ -12,7 +12,7 @@ /obj/machinery/abductor/pad/proc/Send() if(teleport_target == null) - teleport_target = teleportlocs[pick(teleportlocs)] + teleport_target = GLOB.teleportlocs[pick(GLOB.teleportlocs)] flick("alien-pad", src) for(var/mob/living/target in loc) target.forceMove(teleport_target) @@ -48,4 +48,4 @@ /obj/effect/temp_visual/teleport_abductor/New() do_sparks(10, 0, loc) - ..() \ No newline at end of file + ..() diff --git a/code/game/gamemodes/miniantags/borer/borer_event.dm b/code/game/gamemodes/miniantags/borer/borer_event.dm index f925a59d9fc8..245b8fa7b41c 100644 --- a/code/game/gamemodes/miniantags/borer/borer_event.dm +++ b/code/game/gamemodes/miniantags/borer/borer_event.dm @@ -12,7 +12,7 @@ /datum/event/borer_infestation/announce() if(successSpawn) - command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') + GLOB.command_announcement.Announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", new_sound = 'sound/AI/aliens.ogg') /datum/event/borer_infestation/start() var/list/vents = list() diff --git a/code/game/gamemodes/miniantags/borer/borer_html.dm b/code/game/gamemodes/miniantags/borer/borer_html.dm index 415438884e9a..8b6e19e6226e 100644 --- a/code/game/gamemodes/miniantags/borer/borer_html.dm +++ b/code/game/gamemodes/miniantags/borer/borer_html.dm @@ -66,4 +66,4 @@
    [content]
    "} - return html \ No newline at end of file + return html diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index e764f97a46a5..65424bf9008b 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -119,7 +119,7 @@ ..() add_language("Swarmer", 1) verbs -= /mob/living/verb/pulled - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) updatename() @@ -689,4 +689,4 @@ if(message) for(var/mob/M in GLOB.mob_list) if(isswarmer(M) || (M in GLOB.dead_mob_list)) - to_chat(M, "Swarm communication - [src] states: [message]") \ No newline at end of file + to_chat(M, "Swarm communication - [src] states: [message]") diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm index 0d5ba13d6825..d0be68819ae5 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm @@ -8,7 +8,7 @@ swarmer_report += "

    Our long-range sensors have detected an odd signal emanating from your station's gateway. We recommend immediate investigation of your gateway, as something may have come \ through." print_command_report(swarmer_report, "Classified [command_name()] Update") - event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg') + GLOB.event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg') /datum/event/spawn_swarmer/start() if(find_swarmer()) diff --git a/code/game/gamemodes/miniantags/guardian/types/assassin.dm b/code/game/gamemodes/miniantags/guardian/types/assassin.dm index 4b17e597aa58..a12fce36e82e 100644 --- a/code/game/gamemodes/miniantags/guardian/types/assassin.dm +++ b/code/game/gamemodes/miniantags/guardian/types/assassin.dm @@ -90,4 +90,4 @@ clear_alert("instealth") instealthalert = null clear_alert("canstealth") - canstealthalert = null \ No newline at end of file + canstealthalert = null diff --git a/code/game/gamemodes/miniantags/guardian/types/charger.dm b/code/game/gamemodes/miniantags/guardian/types/charger.dm index 17ed569f2567..3ea6dbe5f302 100644 --- a/code/game/gamemodes/miniantags/guardian/types/charger.dm +++ b/code/game/gamemodes/miniantags/guardian/types/charger.dm @@ -69,4 +69,4 @@ shake_camera(L, 4, 3) shake_camera(src, 2, 3) - charging = 0 \ No newline at end of file + charging = 0 diff --git a/code/game/gamemodes/miniantags/guardian/types/healer.dm b/code/game/gamemodes/miniantags/guardian/types/healer.dm index bf2c92ffb282..c59f7f876364 100644 --- a/code/game/gamemodes/miniantags/guardian/types/healer.dm +++ b/code/game/gamemodes/miniantags/guardian/types/healer.dm @@ -33,7 +33,7 @@ /mob/living/simple_animal/hostile/guardian/healer/Life(seconds, times_fired) ..() - var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medsensor.add_hud_to(src) /mob/living/simple_animal/hostile/guardian/healer/Stat() diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm index 13b9b67f0a9e..bf84e199babc 100644 --- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm +++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm @@ -105,4 +105,4 @@ "You hear a heavy electrical crack." \ ) L.adjustFireLoss(1.2) //adds up very rapidly - . = 1 \ No newline at end of file + . = 1 diff --git a/code/game/gamemodes/miniantags/guardian/types/protector.dm b/code/game/gamemodes/miniantags/guardian/types/protector.dm index 5fb31b8ba155..534b7e169341 100644 --- a/code/game/gamemodes/miniantags/guardian/types/protector.dm +++ b/code/game/gamemodes/miniantags/guardian/types/protector.dm @@ -55,4 +55,4 @@ summoner.visible_message("[summoner] jumps back to [summoner.p_their()] protector.") new /obj/effect/temp_visual/guardian/phase/out(get_turf(summoner)) summoner.forceMove(get_turf(src)) - new /obj/effect/temp_visual/guardian/phase(get_turf(summoner))//Protector \ No newline at end of file + new /obj/effect/temp_visual/guardian/phase(get_turf(summoner))//Protector diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm index dc85a48a480e..e6d95ffb92c7 100644 --- a/code/game/gamemodes/miniantags/guardian/types/standard.dm +++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm @@ -45,4 +45,4 @@ playstyle_string = "As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls." environment_smash = 2 battlecry = "URK" - adminseal = TRUE \ No newline at end of file + adminseal = TRUE diff --git a/code/game/gamemodes/miniantags/morph/morph.dm b/code/game/gamemodes/miniantags/morph/morph.dm index ef90f44f4a05..a122351a4bb2 100644 --- a/code/game/gamemodes/miniantags/morph/morph.dm +++ b/code/game/gamemodes/miniantags/morph/morph.dm @@ -136,7 +136,7 @@ for(var/atom/movable/AM in src) AM.forceMove(loc) if(prob(90)) - step(AM, pick(alldirs)) + step(AM, pick(GLOB.alldirs)) // Only execute the below if we successfully died if(!.) return FALSE diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm index e80374e2dcab..e9465492a270 100644 --- a/code/game/gamemodes/miniantags/morph/morph_event.dm +++ b/code/game/gamemodes/miniantags/morph/morph_event.dm @@ -15,9 +15,9 @@ var/datum/mind/player_mind = new /datum/mind(key_of_morph) player_mind.active = 1 - if(!xeno_spawn) + if(!GLOB.xeno_spawn) return kill() - var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(xeno_spawn)) + var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(GLOB.xeno_spawn)) player_mind.transfer_to(S) player_mind.assigned_role = SPECIAL_ROLE_MORPH player_mind.special_role = SPECIAL_ROLE_MORPH @@ -29,4 +29,4 @@ return 1 /datum/event/spawn_morph/start() - get_morph() \ No newline at end of file + get_morph() diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm index 437e7637a777..1f902a518068 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm @@ -52,4 +52,4 @@ /datum/event/revenant/start() - get_revenant() \ No newline at end of file + get_revenant() diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 578cc8ac3a73..ad1146c0bc81 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,536 +1,537 @@ -#define NUKESCALINGMODIFIER 1.2 - -/datum/game_mode - var/list/datum/mind/syndicates = list() - -proc/issyndicate(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) - -/datum/game_mode/nuclear - name = "nuclear emergency" - config_tag = "nuclear" - required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining - required_enemies = 5 - recommended_enemies = 5 - - var/const/agents_possible = 5 //If we ever need more syndicate agents. - - var/nukes_left = 1 //Call 3714-PRAY right now and order more nukes! Limited offer! - var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station - var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level - var/total_tc = 0 //Total amount of telecrystals shared between nuke ops - -/datum/game_mode/nuclear/announce() - to_chat(world, "The current game mode is - Nuclear Emergency!") - to_chat(world, "A [syndicate_name()] Strike Force is approaching [station_name()]!") - to_chat(world, "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear authentication disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! There are most likely Syndicate starships are in the vicinity, so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!") - -/datum/game_mode/nuclear/can_start()//This could be better, will likely have to recode it later - if(!..()) - return 0 - - var/list/possible_syndicates = get_players_for_role(ROLE_OPERATIVE) - var/agent_number = 0 - - if(possible_syndicates.len < 1) - return 0 - - if(LAZYLEN(possible_syndicates) > agents_possible) - agent_number = agents_possible - else - agent_number = possible_syndicates.len - - var/n_players = num_players() - if(agent_number > n_players) - agent_number = n_players/2 - - while(agent_number > 0) - var/datum/mind/new_syndicate = pick(possible_syndicates) - syndicates += new_syndicate - possible_syndicates -= new_syndicate //So it doesn't pick the same guy each time. - agent_number-- - - for(var/datum/mind/synd_mind in syndicates) - synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. - synd_mind.special_role = SPECIAL_ROLE_NUKEOPS - synd_mind.offstation_role = TRUE - return 1 - - -/datum/game_mode/nuclear/pre_setup() - ..() - return 1 - -/datum/game_mode/proc/remove_operative(datum/mind/operative_mind) - if(operative_mind in syndicates) - SSticker.mode.syndicates -= operative_mind - operative_mind.special_role = null - for(var/datum/objective/nuclear/O in operative_mind.objectives) - operative_mind.objectives -= O - operative_mind.current.create_attack_log("No longer nuclear operative") - if(issilicon(operative_mind.current)) - to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") - else - to_chat(operative_mind.current, "You have been brainwashed! You are no longer a Syndicate operative.") - SSticker.mode.update_synd_icons_removed(operative_mind) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(synd_mind.current) - set_antag_hud(synd_mind.current, "hudoperative") - -/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.leave_hud(synd_mind.current) - set_antag_hud(synd_mind.current, null) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/nuclear/post_setup() - - var/list/turf/synd_spawn = list() - - for(var/obj/effect/landmark/A in GLOB.landmarks_list) - if(A.name == "Syndicate-Spawn") - synd_spawn += get_turf(A) - qdel(A) - continue - - var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") - - var/nuke_code = "[rand(10000, 99999)]" - var/leader_selected = 0 - var/agent_number = 1 - var/spawnpos = 1 - - for(var/datum/mind/synd_mind in syndicates) - if(spawnpos > synd_spawn.len) - spawnpos = 2 - synd_mind.current.loc = synd_spawn[spawnpos] - - forge_syndicate_objectives(synd_mind) - create_syndicate(synd_mind) - greet_syndicate(synd_mind) - equip_syndicate(synd_mind.current) - - if(!leader_selected) - prepare_syndicate_leader(synd_mind, nuke_code) - leader_selected = 1 - else - synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" - update_syndicate_id(synd_mind, FALSE) - - agent_number++ - spawnpos++ - update_synd_icons_added(synd_mind) - - scale_telecrystals() - share_telecrystals() - if(nuke_spawn && synd_spawn.len > 0) - var/obj/machinery/nuclearbomb/syndicate/the_bomb = new /obj/machinery/nuclearbomb/syndicate(nuke_spawn.loc) - the_bomb.r_code = nuke_code - - return ..() - -/datum/game_mode/nuclear/proc/scale_telecrystals() - var/danger - danger = GLOB.player_list.len - while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten - - total_tc += danger * NUKESCALINGMODIFIER - -/datum/game_mode/nuclear/proc/share_telecrystals() - var/player_tc - var/remainder - - player_tc = round(total_tc / GLOB.nuclear_uplink_list.len) //round to get an integer and not floating point - remainder = total_tc % GLOB.nuclear_uplink_list.len - - for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) - U.hidden_uplink.uses += player_tc - while(remainder > 0) - for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) - if(remainder <= 0) - break - U.hidden_uplink.uses++ - remainder-- - -/datum/game_mode/proc/create_syndicate(datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human - var/mob/living/carbon/human/M = synd_mind.current - - M.set_species(/datum/species/human, TRUE) - M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics - M.dna.species.create_organs(M) - M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ops. - M.overeatduration = 0 - M.flavor_text = null - - var/obj/item/organ/external/head/head_organ = M.get_organ("head") - var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde - var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue - var/skin_tone = pick(-50, -30, -10, 0, 0, 0, 10) // Caucasian/black - head_organ.facial_colour = hair_c - head_organ.sec_facial_colour = hair_c - head_organ.hair_colour = hair_c - head_organ.sec_hair_colour = hair_c - M.change_eye_color(eye_c) - M.s_tone = skin_tone - head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) - head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) - M.body_accessory = null - M.regenerate_icons() - M.update_body() - -/datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) - var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") - synd_mind.current.real_name = "[syndicate_name()] Team [leader_title]" - to_chat(synd_mind.current, "You are the Syndicate leader for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.") - to_chat(synd_mind.current, "If you feel you are not up to this task, give your ID to another operative.") - to_chat(synd_mind.current, "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.") - - var/obj/item/nuclear_challenge/challenge = new /obj/item/nuclear_challenge - synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) - - update_syndicate_id(synd_mind, leader_title, TRUE) - - if(nuke_code) - synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) - to_chat(synd_mind.current, "The nuclear authorization code is: [nuke_code]") - var/obj/item/paper/P = new - P.info = "The nuclear authorization code is: [nuke_code]" - P.name = "nuclear bomb code" - var/obj/item/stamp/syndicate/stamp = new - P.stamp(stamp) - qdel(stamp) - - if(SSticker.mode.config_tag=="nuclear") - P.loc = synd_mind.current.loc - else - var/mob/living/carbon/human/H = synd_mind.current - P.loc = H.loc - H.equip_to_slot_or_del(P, slot_r_store, 0) - H.update_icons() - - else - nuke_code = "code will be provided later" - -/datum/game_mode/proc/update_syndicate_id(var/datum/mind/synd_mind, is_leader = FALSE) - var/list/found_ids = synd_mind.current.search_contents_for(/obj/item/card/id) - - if(LAZYLEN(found_ids)) - for(var/obj/item/card/id/ID in found_ids) - ID.name = "[synd_mind.current.real_name] ID card" - ID.registered_name = synd_mind.current.real_name - if(is_leader) - ID.access += ACCESS_SYNDICATE_LEADER - else - message_admins("Warning: Operative [key_name_admin(synd_mind.current)] spawned without an ID card!") - -/datum/game_mode/proc/forge_syndicate_objectives(var/datum/mind/syndicate) - var/datum/objective/nuclear/syndobj = new - syndobj.owner = syndicate - syndicate.objectives += syndobj - - -/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1) - SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg') - if(you_are) - to_chat(syndicate.current, "You are a [syndicate_name()] agent!") - var/obj_count = 1 - for(var/datum/objective/objective in syndicate.objectives) - to_chat(syndicate.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - - -/datum/game_mode/proc/random_radio_frequency() - return 1337 // WHY??? -- Doohl - - -/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20) - var/radio_freq = SYND_FREQ - - var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob) - R.set_frequency(radio_freq) - synd_mob.equip_to_slot_or_del(R, slot_l_ear) - - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(synd_mob), slot_w_uniform) - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(synd_mob), slot_shoes) - synd_mob.equip_or_collect(new /obj/item/clothing/gloves/combat(synd_mob), slot_gloves) - synd_mob.equip_to_slot_or_del(new /obj/item/card/id/syndicate(synd_mob), slot_wear_id) - synd_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(synd_mob), slot_back) - synd_mob.equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/pistol(synd_mob), slot_belt) - synd_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(synd_mob.back), slot_in_backpack) - synd_mob.equip_to_slot_or_del(new /obj/item/pinpointer/nukeop(synd_mob), slot_wear_pda) - var/obj/item/radio/uplink/nuclear/U = new /obj/item/radio/uplink/nuclear(synd_mob) - U.hidden_uplink.uplink_owner="[synd_mob.key]" - U.hidden_uplink.uses = uplink_uses - synd_mob.equip_to_slot_or_del(U, slot_in_backpack) - - if(synd_mob.dna.species) - - /* - Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave - synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops. - var/datum/preferences/pref = new() - A.randomize_appearance_for(synd_mob) - */ - - var/race = synd_mob.dna.species.name - - switch(race) - if("Vox" || "Vox Armalis") - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) - synd_mob.equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/vox(synd_mob), slot_l_hand) - synd_mob.internal = synd_mob.l_hand - synd_mob.update_action_buttons_icon() - - if("Plasmaman") - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) - synd_mob.equip_or_collect(new /obj/item/tank/plasma/plasmaman(synd_mob), slot_s_store) - synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) - synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) - synd_mob.internal = synd_mob.get_item_by_slot(slot_s_store) - synd_mob.update_action_buttons_icon() - - synd_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups - var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(synd_mob) - E.implant(synd_mob) - synd_mob.faction |= "syndicate" - synd_mob.update_icons() - return 1 - -/datum/game_mode/nuclear/check_win() - if(nukes_left == 0) - return 1 - return ..() - - -/datum/game_mode/proc/is_operatives_are_dead() - for(var/datum/mind/operative_mind in syndicates) - if(!istype(operative_mind.current,/mob/living/carbon/human)) - if(operative_mind.current) - if(operative_mind.current.stat!=2) - return 0 - return 1 - - -/datum/game_mode/nuclear/declare_completion() - var/disk_rescued = 1 - for(var/obj/item/disk/nuclear/D in GLOB.poi_list) - if(!D.onCentcom()) - disk_rescued = 0 - break - var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) - //var/operatives_are_dead = is_operatives_are_dead() - - - //nukes_left - //station_was_nuked - //derp //Used for tracking if the syndies actually haul the nuke to the station //no - //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS - - if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear win - syndicate nuke") - to_chat(world, "Syndicate Major Victory!") - to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") - - else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - syndicate nuke - did not evacuate in time") - to_chat(world, "Total Annihilation") - to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") - - else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - blew wrong station") - to_chat(world, "Crew Minor Victory") - to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") - - else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - blew wrong station - did not evacuate in time") - to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") - to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") - - else if(disk_rescued && is_operatives_are_dead()) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured - syndi team dead") - to_chat(world, "Crew Major Victory!") - to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") - - else if(disk_rescued) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured") - to_chat(world, "Crew Major Victory") - to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") - - else if(!disk_rescued && is_operatives_are_dead()) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk not secured") - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") - - else if(!disk_rescued && crew_evacuated) - feedback_set_details("round_end_result","nuclear halfwin - detonation averted") - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") - - else if(!disk_rescued && !crew_evacuated) - feedback_set_details("round_end_result","nuclear halfwin - interrupted") - to_chat(world, "Neutral Victory") - to_chat(world, "Round was mysteriously interrupted!") - ..() - return - - -/datum/game_mode/proc/auto_declare_completion_nuclear() - if(syndicates.len || GAMEMODE_IS_NUCLEAR) - var/text = "
    The syndicate operatives were:" - - var/purchases = "" - var/TC_uses = 0 - - for(var/datum/mind/syndicate in syndicates) - - text += "
    [syndicate.key] was [syndicate.name] (" - if(syndicate.current) - if(syndicate.current.stat == DEAD) - text += "died" - else - text += "survived" - if(syndicate.current.real_name != syndicate.name) - text += " as [syndicate.current.real_name]" - else - text += "body destroyed" - text += ")" - for(var/obj/item/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==syndicate.key) - TC_uses += H.used_TC - purchases += H.purchase_log - - text += "
    " - - text += "(Syndicates used [TC_uses] TC) [purchases]" - - if(TC_uses==0 && station_was_nuked && !is_operatives_are_dead()) - text += "" - - to_chat(world, text) - return 1 - -/proc/nukelastname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. - var/randomname = pick(GLOB.last_names) - var/newname = sanitize(copytext(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname),1,MAX_NAME_LEN)) - - if(!newname) - newname = randomname - - else - if(newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") - to_chat(M, "That name is reserved.") - return nukelastname(M) - - return newname - - -/datum/game_mode/nuclear/set_scoreboard_gvars() - var/foecount = 0 - for(var/datum/mind/M in SSticker.mode.syndicates) - foecount++ - if(!M || !M.current) - score_opkilled++ - continue - - if(M.current.stat == DEAD) - score_opkilled++ - - else if(M.current.restrained()) - score_arrested++ - - if(foecount == score_arrested) - score_allarrested = 1 - - for(var/obj/machinery/nuclearbomb/nuke in world) - if(nuke.r_code == "Nope") continue - var/turf/T = get_turf(nuke) - var/area/A = T.loc - - var/list/thousand_penalty = list(/area/wizard_station, /area/solar, /area) - var/list/fiftythousand_penalty = list(/area/security/main, /area/security/brig, /area/security/armoury, /area/security/checkpoint2) - - if(is_type_in_list(A, thousand_penalty)) - score_nuked_penalty = 1000 - - else if(is_type_in_list(A, fiftythousand_penalty)) - score_nuked_penalty = 50000 - - else if(istype(A, /area/engine)) - score_nuked_penalty = 100000 - - else - score_nuked_penalty = 10000 - - break - - var/killpoints = score_opkilled * 250 - var/arrestpoints = score_arrested * 1000 - score_crewscore += killpoints - score_crewscore += arrestpoints - if(score_nuked) - score_crewscore -= score_nuked_penalty - - - -/datum/game_mode/nuclear/get_scoreboard_stats() - var/foecount = 0 - var/crewcount = 0 - - var/diskdat = "" - var/bombdat = null - - for(var/datum/mind/M in SSticker.mode.syndicates) - foecount++ - - for(var/mob/living/C in world) - if(ishuman(C) || isAI(C) || isrobot(C)) - if(C.stat == 2) continue - if(!C.client) continue - crewcount++ - - var/obj/item/disk/nuclear/N = locate() in world - if(istype(N)) - var/atom/disk_loc = N.loc - while(!isturf(disk_loc)) - if(ismob(disk_loc)) - var/mob/M = disk_loc - diskdat += "Carried by [M.real_name] " - if(isobj(disk_loc)) - var/obj/O = disk_loc - diskdat += "in \a [O]" - disk_loc = disk_loc.loc - diskdat += "in [disk_loc.loc]" - - - if(!diskdat) - diskdat = "WARNING: Nuked_penalty could not be found, look at [__FILE__], [__LINE__]." - - var/dat = "" - dat += "Mode Statistics
    " - - dat += "Number of Operatives: [foecount]
    " - dat += "Number of Surviving Crew: [crewcount]
    " - - dat += "Final Location of Nuke: [bombdat]
    " - dat += "Final Location of Disk: [diskdat]
    " - - dat += "
    " - - dat += "Operatives Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " - dat += "All Operatives Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " - - dat += "Operatives Killed: [score_opkilled] ([score_opkilled * 1000] Points)
    " - dat += "Station Destroyed: [score_nuked ? "Yes" : "No"] (-[score_nuked_penalty] Points)
    " - dat += "
    " - - return dat - -#undef NUKESCALINGMODIFIER +#define NUKESCALINGMODIFIER 1.2 + +/datum/game_mode + var/list/datum/mind/syndicates = list() + +proc/issyndicate(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) + +/datum/game_mode/nuclear + name = "nuclear emergency" + config_tag = "nuclear" + required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining + required_enemies = 5 + recommended_enemies = 5 + + var/const/agents_possible = 5 //If we ever need more syndicate agents. + + var/nukes_left = 1 //Call 3714-PRAY right now and order more nukes! Limited offer! + var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station + var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level + var/total_tc = 0 //Total amount of telecrystals shared between nuke ops + +/datum/game_mode/nuclear/announce() + to_chat(world, "The current game mode is - Nuclear Emergency!") + to_chat(world, "A [syndicate_name()] Strike Force is approaching [station_name()]!") + to_chat(world, "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear authentication disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! There are most likely Syndicate starships are in the vicinity, so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!") + +/datum/game_mode/nuclear/can_start()//This could be better, will likely have to recode it later + if(!..()) + return 0 + + var/list/possible_syndicates = get_players_for_role(ROLE_OPERATIVE) + var/agent_number = 0 + + if(possible_syndicates.len < 1) + return 0 + + if(LAZYLEN(possible_syndicates) > agents_possible) + agent_number = agents_possible + else + agent_number = possible_syndicates.len + + var/n_players = num_players() + if(agent_number > n_players) + agent_number = n_players/2 + + while(agent_number > 0) + var/datum/mind/new_syndicate = pick(possible_syndicates) + syndicates += new_syndicate + possible_syndicates -= new_syndicate //So it doesn't pick the same guy each time. + agent_number-- + + for(var/datum/mind/synd_mind in syndicates) + synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. + synd_mind.special_role = SPECIAL_ROLE_NUKEOPS + synd_mind.offstation_role = TRUE + return 1 + + +/datum/game_mode/nuclear/pre_setup() + ..() + return 1 + +/datum/game_mode/proc/remove_operative(datum/mind/operative_mind) + if(operative_mind in syndicates) + SSticker.mode.syndicates -= operative_mind + operative_mind.special_role = null + for(var/datum/objective/nuclear/O in operative_mind.objectives) + operative_mind.objectives -= O + operative_mind.current.create_attack_log("No longer nuclear operative") + operative_mind.current.create_log(CONVERSION_LOG, "No longer nuclear operative") + if(issilicon(operative_mind.current)) + to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") + else + to_chat(operative_mind.current, "You have been brainwashed! You are no longer a Syndicate operative.") + SSticker.mode.update_synd_icons_removed(operative_mind) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] + opshud.join_hud(synd_mind.current) + set_antag_hud(synd_mind.current, "hudoperative") + +/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] + opshud.leave_hud(synd_mind.current) + set_antag_hud(synd_mind.current, null) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/nuclear/post_setup() + + var/list/turf/synd_spawn = list() + + for(var/obj/effect/landmark/A in GLOB.landmarks_list) + if(A.name == "Syndicate-Spawn") + synd_spawn += get_turf(A) + qdel(A) + continue + + var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") + + var/nuke_code = "[rand(10000, 99999)]" + var/leader_selected = 0 + var/agent_number = 1 + var/spawnpos = 1 + + for(var/datum/mind/synd_mind in syndicates) + if(spawnpos > synd_spawn.len) + spawnpos = 2 + synd_mind.current.loc = synd_spawn[spawnpos] + + forge_syndicate_objectives(synd_mind) + create_syndicate(synd_mind) + greet_syndicate(synd_mind) + equip_syndicate(synd_mind.current) + + if(!leader_selected) + prepare_syndicate_leader(synd_mind, nuke_code) + leader_selected = 1 + else + synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" + update_syndicate_id(synd_mind, FALSE) + + agent_number++ + spawnpos++ + update_synd_icons_added(synd_mind) + + scale_telecrystals() + share_telecrystals() + if(nuke_spawn && synd_spawn.len > 0) + var/obj/machinery/nuclearbomb/syndicate/the_bomb = new /obj/machinery/nuclearbomb/syndicate(nuke_spawn.loc) + the_bomb.r_code = nuke_code + + return ..() + +/datum/game_mode/nuclear/proc/scale_telecrystals() + var/danger + danger = GLOB.player_list.len + while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten + + total_tc += danger * NUKESCALINGMODIFIER + +/datum/game_mode/nuclear/proc/share_telecrystals() + var/player_tc + var/remainder + + player_tc = round(total_tc / GLOB.nuclear_uplink_list.len) //round to get an integer and not floating point + remainder = total_tc % GLOB.nuclear_uplink_list.len + + for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) + U.hidden_uplink.uses += player_tc + while(remainder > 0) + for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) + if(remainder <= 0) + break + U.hidden_uplink.uses++ + remainder-- + +/datum/game_mode/proc/create_syndicate(datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human + var/mob/living/carbon/human/M = synd_mind.current + + M.set_species(/datum/species/human, TRUE) + M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics + M.dna.species.create_organs(M) + M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ops. + M.overeatduration = 0 + M.flavor_text = null + + var/obj/item/organ/external/head/head_organ = M.get_organ("head") + var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde + var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue + var/skin_tone = pick(-50, -30, -10, 0, 0, 0, 10) // Caucasian/black + head_organ.facial_colour = hair_c + head_organ.sec_facial_colour = hair_c + head_organ.hair_colour = hair_c + head_organ.sec_hair_colour = hair_c + M.change_eye_color(eye_c) + M.s_tone = skin_tone + head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) + head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) + M.body_accessory = null + M.regenerate_icons() + M.update_body() + +/datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) + var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") + synd_mind.current.real_name = "[syndicate_name()] Team [leader_title]" + to_chat(synd_mind.current, "You are the Syndicate leader for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.") + to_chat(synd_mind.current, "If you feel you are not up to this task, give your ID to another operative.") + to_chat(synd_mind.current, "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.") + + var/obj/item/nuclear_challenge/challenge = new /obj/item/nuclear_challenge + synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) + + update_syndicate_id(synd_mind, leader_title, TRUE) + + if(nuke_code) + synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) + to_chat(synd_mind.current, "The nuclear authorization code is: [nuke_code]") + var/obj/item/paper/P = new + P.info = "The nuclear authorization code is: [nuke_code]" + P.name = "nuclear bomb code" + var/obj/item/stamp/syndicate/stamp = new + P.stamp(stamp) + qdel(stamp) + + if(SSticker.mode.config_tag=="nuclear") + P.loc = synd_mind.current.loc + else + var/mob/living/carbon/human/H = synd_mind.current + P.loc = H.loc + H.equip_to_slot_or_del(P, slot_r_store, 0) + H.update_icons() + + else + nuke_code = "code will be provided later" + +/datum/game_mode/proc/update_syndicate_id(var/datum/mind/synd_mind, is_leader = FALSE) + var/list/found_ids = synd_mind.current.search_contents_for(/obj/item/card/id) + + if(LAZYLEN(found_ids)) + for(var/obj/item/card/id/ID in found_ids) + ID.name = "[synd_mind.current.real_name] ID card" + ID.registered_name = synd_mind.current.real_name + if(is_leader) + ID.access += ACCESS_SYNDICATE_LEADER + else + message_admins("Warning: Operative [key_name_admin(synd_mind.current)] spawned without an ID card!") + +/datum/game_mode/proc/forge_syndicate_objectives(var/datum/mind/syndicate) + var/datum/objective/nuclear/syndobj = new + syndobj.owner = syndicate + syndicate.objectives += syndobj + + +/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1) + SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg') + if(you_are) + to_chat(syndicate.current, "You are a [syndicate_name()] agent!") + var/obj_count = 1 + for(var/datum/objective/objective in syndicate.objectives) + to_chat(syndicate.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + + +/datum/game_mode/proc/random_radio_frequency() + return 1337 // WHY??? -- Doohl + + +/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20) + var/radio_freq = SYND_FREQ + + var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob) + R.set_frequency(radio_freq) + synd_mob.equip_to_slot_or_del(R, slot_l_ear) + + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(synd_mob), slot_w_uniform) + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(synd_mob), slot_shoes) + synd_mob.equip_or_collect(new /obj/item/clothing/gloves/combat(synd_mob), slot_gloves) + synd_mob.equip_to_slot_or_del(new /obj/item/card/id/syndicate(synd_mob), slot_wear_id) + synd_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(synd_mob), slot_back) + synd_mob.equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/pistol(synd_mob), slot_belt) + synd_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(synd_mob.back), slot_in_backpack) + synd_mob.equip_to_slot_or_del(new /obj/item/pinpointer/nukeop(synd_mob), slot_wear_pda) + var/obj/item/radio/uplink/nuclear/U = new /obj/item/radio/uplink/nuclear(synd_mob) + U.hidden_uplink.uplink_owner="[synd_mob.key]" + U.hidden_uplink.uses = uplink_uses + synd_mob.equip_to_slot_or_del(U, slot_in_backpack) + + if(synd_mob.dna.species) + + /* + Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave + synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops. + var/datum/preferences/pref = new() + A.randomize_appearance_for(synd_mob) + */ + + var/race = synd_mob.dna.species.name + + switch(race) + if("Vox" || "Vox Armalis") + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) + synd_mob.equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/vox(synd_mob), slot_l_hand) + synd_mob.internal = synd_mob.l_hand + synd_mob.update_action_buttons_icon() + + if("Plasmaman") + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) + synd_mob.equip_or_collect(new /obj/item/tank/plasma/plasmaman(synd_mob), slot_s_store) + synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) + synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) + synd_mob.internal = synd_mob.get_item_by_slot(slot_s_store) + synd_mob.update_action_buttons_icon() + + synd_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups + var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(synd_mob) + E.implant(synd_mob) + synd_mob.faction |= "syndicate" + synd_mob.update_icons() + return 1 + +/datum/game_mode/nuclear/check_win() + if(nukes_left == 0) + return 1 + return ..() + + +/datum/game_mode/proc/is_operatives_are_dead() + for(var/datum/mind/operative_mind in syndicates) + if(!istype(operative_mind.current,/mob/living/carbon/human)) + if(operative_mind.current) + if(operative_mind.current.stat!=2) + return 0 + return 1 + + +/datum/game_mode/nuclear/declare_completion() + var/disk_rescued = 1 + for(var/obj/item/disk/nuclear/D in GLOB.poi_list) + if(!D.onCentcom()) + disk_rescued = 0 + break + var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) + //var/operatives_are_dead = is_operatives_are_dead() + + + //nukes_left + //station_was_nuked + //derp //Used for tracking if the syndies actually haul the nuke to the station //no + //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS + + if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear win - syndicate nuke") + to_chat(world, "Syndicate Major Victory!") + to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") + + else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - syndicate nuke - did not evacuate in time") + to_chat(world, "Total Annihilation") + to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") + + else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - blew wrong station") + to_chat(world, "Crew Minor Victory") + to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") + + else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - blew wrong station - did not evacuate in time") + to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") + to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") + + else if(disk_rescued && is_operatives_are_dead()) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured - syndi team dead") + to_chat(world, "Crew Major Victory!") + to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") + + else if(disk_rescued) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured") + to_chat(world, "Crew Major Victory") + to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") + + else if(!disk_rescued && is_operatives_are_dead()) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk not secured") + to_chat(world, "Syndicate Minor Victory!") + to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") + + else if(!disk_rescued && crew_evacuated) + feedback_set_details("round_end_result","nuclear halfwin - detonation averted") + to_chat(world, "Syndicate Minor Victory!") + to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") + + else if(!disk_rescued && !crew_evacuated) + feedback_set_details("round_end_result","nuclear halfwin - interrupted") + to_chat(world, "Neutral Victory") + to_chat(world, "Round was mysteriously interrupted!") + ..() + return + + +/datum/game_mode/proc/auto_declare_completion_nuclear() + if(syndicates.len || GAMEMODE_IS_NUCLEAR) + var/text = "
    The syndicate operatives were:" + + var/purchases = "" + var/TC_uses = 0 + + for(var/datum/mind/syndicate in syndicates) + + text += "
    [syndicate.key] was [syndicate.name] (" + if(syndicate.current) + if(syndicate.current.stat == DEAD) + text += "died" + else + text += "survived" + if(syndicate.current.real_name != syndicate.name) + text += " as [syndicate.current.real_name]" + else + text += "body destroyed" + text += ")" + for(var/obj/item/uplink/H in GLOB.world_uplinks) + if(H && H.uplink_owner && H.uplink_owner==syndicate.key) + TC_uses += H.used_TC + purchases += H.purchase_log + + text += "
    " + + text += "(Syndicates used [TC_uses] TC) [purchases]" + + if(TC_uses==0 && station_was_nuked && !is_operatives_are_dead()) + text += "" + + to_chat(world, text) + return 1 + +/proc/nukelastname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. + var/randomname = pick(GLOB.last_names) + var/newname = sanitize(copytext(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname),1,MAX_NAME_LEN)) + + if(!newname) + newname = randomname + + else + if(newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") + to_chat(M, "That name is reserved.") + return nukelastname(M) + + return newname + + +/datum/game_mode/nuclear/set_scoreboard_gvars() + var/foecount = 0 + for(var/datum/mind/M in SSticker.mode.syndicates) + foecount++ + if(!M || !M.current) + GLOB.score_opkilled++ + continue + + if(M.current.stat == DEAD) + GLOB.score_opkilled++ + + else if(M.current.restrained()) + GLOB.score_arrested++ + + if(foecount == GLOB.score_arrested) + GLOB.score_allarrested = 1 + + for(var/obj/machinery/nuclearbomb/nuke in world) + if(nuke.r_code == "Nope") continue + var/turf/T = get_turf(nuke) + var/area/A = T.loc + + var/list/thousand_penalty = list(/area/wizard_station, /area/solar, /area) + var/list/fiftythousand_penalty = list(/area/security/main, /area/security/brig, /area/security/armoury, /area/security/checkpoint2) + + if(is_type_in_list(A, thousand_penalty)) + GLOB.score_nuked_penalty = 1000 + + else if(is_type_in_list(A, fiftythousand_penalty)) + GLOB.score_nuked_penalty = 50000 + + else if(istype(A, /area/engine)) + GLOB.score_nuked_penalty = 100000 + + else + GLOB.score_nuked_penalty = 10000 + + break + + var/killpoints = GLOB.score_opkilled * 250 + var/arrestpoints = GLOB.score_arrested * 1000 + GLOB.score_crewscore += killpoints + GLOB.score_crewscore += arrestpoints + if(GLOB.score_nuked) + GLOB.score_crewscore -= GLOB.score_nuked_penalty + + + +/datum/game_mode/nuclear/get_scoreboard_stats() + var/foecount = 0 + var/crewcount = 0 + + var/diskdat = "" + var/bombdat = null + + for(var/datum/mind/M in SSticker.mode.syndicates) + foecount++ + + for(var/mob/living/C in world) + if(ishuman(C) || isAI(C) || isrobot(C)) + if(C.stat == 2) continue + if(!C.client) continue + crewcount++ + + var/obj/item/disk/nuclear/N = locate() in world + if(istype(N)) + var/atom/disk_loc = N.loc + while(!isturf(disk_loc)) + if(ismob(disk_loc)) + var/mob/M = disk_loc + diskdat += "Carried by [M.real_name] " + if(isobj(disk_loc)) + var/obj/O = disk_loc + diskdat += "in \a [O]" + disk_loc = disk_loc.loc + diskdat += "in [disk_loc.loc]" + + + if(!diskdat) + diskdat = "WARNING: Nuked_penalty could not be found, look at [__FILE__], [__LINE__]." + + var/dat = "" + dat += "Mode Statistics
    " + + dat += "Number of Operatives: [foecount]
    " + dat += "Number of Surviving Crew: [crewcount]
    " + + dat += "Final Location of Nuke: [bombdat]
    " + dat += "Final Location of Disk: [diskdat]
    " + + dat += "
    " + + dat += "Operatives Arrested: [GLOB.score_arrested] ([GLOB.score_arrested * 1000] Points)
    " + dat += "All Operatives Arrested: [GLOB.score_allarrested ? "Yes" : "No"] (Score tripled)
    " + + dat += "Operatives Killed: [GLOB.score_opkilled] ([GLOB.score_opkilled * 1000] Points)
    " + dat += "Station Destroyed: [GLOB.score_nuked ? "Yes" : "No"] (-[GLOB.score_nuked_penalty] Points)
    " + dat += "
    " + + return dat + +#undef NUKESCALINGMODIFIER diff --git a/code/game/gamemodes/nuclear/nuclear_challenge.dm b/code/game/gamemodes/nuclear/nuclear_challenge.dm index 35c26408c5b7..481169116407 100644 --- a/code/game/gamemodes/nuclear/nuclear_challenge.dm +++ b/code/game/gamemodes/nuclear/nuclear_challenge.dm @@ -48,7 +48,7 @@ if(!check_allowed(user) || !war_declaration) return - event_announcement.Announce(war_declaration, "Declaration of War", 'sound/effects/siren.ogg') + GLOB.event_announcement.Announce(war_declaration, "Declaration of War", 'sound/effects/siren.ogg') to_chat(user, "You've attracted the attention of powerful forces within the syndicate. A bonus bundle of telecrystals has been granted to your team. Great things await you if you complete the mission.") to_chat(user, "Your bonus telecrystals have been split between your team's uplinks.") diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 714ccbf1d5fc..1dd254fade7a 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -1,472 +1,472 @@ -#define NUKE_INTACT 0 -#define NUKE_COVER_OFF 1 -#define NUKE_COVER_OPEN 2 -#define NUKE_SEALANT_OPEN 3 -#define NUKE_UNWRENCHED 4 -#define NUKE_MOBILE 5 - -var/bomb_set - -/obj/machinery/nuclearbomb - name = "\improper Nuclear Fission Explosive" - desc = "Uh oh. RUN!!!!" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nuclearbomb0" - density = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/deployable = 0 - var/extended = 0 - var/lighthack = 0 - var/timeleft = 120 - var/timing = 0 - var/r_code = "ADMIN" - var/code = "" - var/yes_code = 0 - var/safety = 1 - var/obj/item/disk/nuclear/auth = null - var/removal_stage = NUKE_INTACT - var/lastentered - var/is_syndicate = 0 - use_power = NO_POWER_USE - var/previous_level = "" - var/datum/wires/nuclearbomb/wires = null - -/obj/machinery/nuclearbomb/syndicate - is_syndicate = 1 - -/obj/machinery/nuclearbomb/New() - ..() - r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. - wires = new/datum/wires/nuclearbomb(src) - previous_level = get_security_level() - GLOB.poi_list |= src - -/obj/machinery/nuclearbomb/Destroy() - QDEL_NULL(wires) - GLOB.poi_list.Remove(src) - return ..() - -/obj/machinery/nuclearbomb/process() - if(timing) - bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. - timeleft = max(timeleft - 2, 0) // 2 seconds per process() - if(timeleft <= 0) - spawn - explode() - SSnanoui.update_uis(src) - return - -/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/disk/nuclear)) - if(extended) - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand!") - return - O.forceMove(src) - auth = O - add_fingerprint(user) - return attack_hand(user) - else - to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") - return - return ..() - -/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I) - if(!anchored) - return - if(removal_stage != NUKE_UNWRENCHED && removal_stage != NUKE_COVER_OFF) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(removal_stage == NUKE_COVER_OFF) - user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [I]...") - if(!I.use_tool(src, user, 15, volume = I.tool_volume) || removal_stage != NUKE_COVER_OFF) - return - user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") - removal_stage = NUKE_COVER_OPEN - else - user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") - if(!I.use_tool(src, user, 80, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED) - return - user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") - anchored = FALSE - removal_stage = NUKE_MOBILE - -/obj/machinery/nuclearbomb/wrench_act(mob/user, obj/item/I) - if(!anchored) - return - if(removal_stage != NUKE_SEALANT_OPEN) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume) || removal_stage != NUKE_SEALANT_OPEN) - return - user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") - removal_stage = NUKE_UNWRENCHED - -/obj/machinery/nuclearbomb/multitool_act(mob/user, obj/item/I) - if(!panel_open) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - attack_hand(user) - -/obj/machinery/nuclearbomb/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(auth) - if(!panel_open) - panel_open = TRUE - overlays += image(icon, "npanel_open") - to_chat(user, "You unscrew the control panel of [src].") - else - panel_open = FALSE - overlays -= image(icon, "npanel_open") - to_chat(user, "You screw the control panel of [src] back on.") - else - if(!panel_open) - to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") - if(panel_open == TRUE) - panel_open = FALSE - overlays -= image(icon, "npanel_open") - to_chat(user, "You screw the control panel of [src] back on.") - flick("nuclearbombc", src) - -/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I) - if(!panel_open) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - attack_hand(user) - -/obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I) - . = TRUE - if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN) - return - if(!I.tool_use_check(user, 0)) - return - if(removal_stage == NUKE_INTACT) - visible_message("[user] starts cutting loose the anchoring bolt covers on [src].",\ - "You start cutting loose the anchoring bolt covers with [I]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_INTACT) - return - visible_message("[user] cuts through the bolt covers on [src].",\ - "You cut through the bolt cover.") - removal_stage = NUKE_COVER_OFF - else if(removal_stage == NUKE_COVER_OPEN) - visible_message("[user] starts cutting apart the anchoring system sealant on [src].",\ - "You start cutting apart the anchoring system's sealant with [I]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_COVER_OPEN) - return - visible_message("[user] cuts apart the anchoring system sealant on [src].",\ - "You cut apart the anchoring system's sealant.
    ") - removal_stage = NUKE_SEALANT_OPEN - -/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) - if(extended) - attack_hand(user) - -/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) - if(extended) - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - else if(deployable) - if(removal_stage != NUKE_MOBILE) - anchored = 1 - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") - else - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - if(!lighthack) - flick("nuclearbombc", src) - icon_state = "nuclearbomb1" - extended = 1 - return - -/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = physical_state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["is_syndicate"] = is_syndicate - data["hacking"] = 0 - data["auth"] = is_auth(user) - if(is_auth(user)) - if(yes_code) - data["authstatus"] = timing ? "Functional/Set" : "Functional" - else - data["authstatus"] = "Auth. S2" - else - if(timing) - data["authstatus"] = "Set" - else - data["authstatus"] = "Auth. S1" - data["safe"] = safety ? "Safe" : "Engaged" - data["time"] = timeleft - data["timer"] = timing - data["safety"] = safety - data["anchored"] = anchored - data["yescode"] = yes_code - data["message"] = "AUTH" - if(is_auth(user)) - data["message"] = code - if(yes_code) - data["message"] = "*****" - - return data - -/obj/machinery/nuclearbomb/verb/make_deployable() - set category = "Object" - set name = "Make Deployable" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(deployable) - to_chat(usr, "You close several panels to make [src] undeployable.") - deployable = 0 - else - to_chat(usr, "You adjust some panels to make [src] deployable.") - deployable = 1 - return - -/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) - if(auth) - return 1 - else if(user.can_admin_interact()) - return 1 - else - return 0 - -/obj/machinery/nuclearbomb/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["auth"]) - if(auth) - auth.loc = loc - yes_code = 0 - auth = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/disk/nuclear)) - usr.drop_item() - I.loc = src - auth = I - if(is_auth(usr)) - if(href_list["type"]) - if(href_list["type"] == "E") - if(code == r_code) - yes_code = 1 - code = null - else - code = "ERROR" - else - if(href_list["type"] == "R") - yes_code = 0 - code = null - else - lastentered = text("[]", href_list["type"]) - if(text2num(lastentered) == null) - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) - log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") - else - code += lastentered - if(length(code) > 5) - code = "ERROR" - if(yes_code) - if(href_list["time"]) - var/time = text2num(href_list["time"]) - timeleft += time - timeleft = min(max(round(src.timeleft), 120), 600) - if(href_list["timer"]) - if(timing == -1.0) - SSnanoui.update_uis(src) - return - if(safety) - to_chat(usr, "The safety is still on.") - SSnanoui.update_uis(src) - return - timing = !(timing) - if(timing) - if(!lighthack) - icon_state = "nuclearbomb2" - if(!safety) - message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") - if(!is_syndicate) - set_security_level("delta") - bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N - else - bomb_set = 0 - else - if(!is_syndicate) - set_security_level(previous_level) - bomb_set = 0 - if(!lighthack) - icon_state = "nuclearbomb1" - if(href_list["safety"]) - safety = !(safety) - if(safety) - if(!is_syndicate) - set_security_level(previous_level) - timing = 0 - bomb_set = 0 - if(href_list["anchor"]) - if(removal_stage == NUKE_MOBILE) - anchored = 0 - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - SSnanoui.update_uis(src) - return - - if(!isinspace()) - anchored = !(anchored) - if(anchored) - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") - else - visible_message("The anchoring bolts slide back into the depths of [src].") - else - to_chat(usr, "There is nothing to anchor to!") - - SSnanoui.update_uis(src) - -/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) - if(timing == -1.0) - return - qdel(src) - -/obj/machinery/nuclearbomb/tesla_act(power, explosive) - ..() - if(explosive) - qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over - -#define NUKERANGE 80 -/obj/machinery/nuclearbomb/proc/explode() - if(safety) - timing = 0 - return - timing = -1.0 - yes_code = 0 - safety = 1 - if(!lighthack) - icon_state = "nuclearbomb3" - playsound(src,'sound/machines/alarm.ogg',100,0,5) - if(SSticker && SSticker.mode) - SSticker.mode.explosion_in_progress = 1 - sleep(100) - - enter_allowed = 0 - - var/off_station = 0 - var/turf/bomb_location = get_turf(src) - if( bomb_location && is_station_level(bomb_location.z) ) - if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) ) - off_station = 1 - else - off_station = 2 - - if(SSticker) - if(SSticker.mode && SSticker.mode.name == "nuclear emergency") - var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate") - if(syndie_shuttle) - SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z) - SSticker.mode:nuke_off_station = off_station - SSticker.station_explosion_cinematic(off_station,null) - if(SSticker.mode) - SSticker.mode.explosion_in_progress = 0 - if(SSticker.mode.name == "nuclear emergency") - SSticker.mode:nukes_left -- - else if(off_station == 1) - to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!") - else if(off_station == 2) - to_chat(world, "A nuclear device was set off, but the device was not on the station!") - else - to_chat(world, "The station was destoyed by the nuclear blast!") - - SSticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. - //kinda shit but I couldn't get permission to do what I wanted to do. - - if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is - world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending") - return - return - - -//==========DAT FUKKEN DISK=============== -/obj/item/disk/nuclear - name = "nuclear authentication disk" - desc = "Better keep this safe." - icon_state = "nucleardisk" - max_integrity = 250 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/disk/nuclear/unrestricted - desc = "Seems to have been stripped of its safeties, you better not lose it." - -/obj/item/disk/nuclear/New() - ..() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - -/obj/item/disk/nuclear/process() - if(!check_disk_loc()) - var/holder = get(src, /mob) - if(holder) - to_chat(holder, "You can't help but feel that you just lost something back there...") - qdel(src) - - //station disk is allowed on z1, escape shuttle/pods, CC, and syndicate shuttles/base, reset otherwise -/obj/item/disk/nuclear/proc/check_disk_loc() - var/turf/T = get_turf(src) - var/area/A = get_area(src) - if(is_station_level(T.z)) - return TRUE - if(A.nad_allowed) - return TRUE - return FALSE - -/obj/item/disk/nuclear/unrestricted/check_disk_loc() - return TRUE - -/obj/item/disk/nuclear/Destroy(force) - var/turf/diskturf = get_turf(src) - - if(force) - message_admins("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z] - JMP":"nonexistent location"]).") - log_game("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z]":"nonexistent location"]).") - GLOB.poi_list.Remove(src) - STOP_PROCESSING(SSobj, src) - return ..() - - if(blobstart.len > 0) - GLOB.poi_list.Remove(src) - var/obj/item/disk/nuclear/NEWDISK = new(pick(blobstart)) - transfer_fingerprints_to(NEWDISK) - message_admins("[src] has been destroyed at ([diskturf.x], [diskturf.y], [diskturf.z] - JMP). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z] - JMP).") - log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y], [diskturf.z]). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z]).") - return QDEL_HINT_HARDDEL_NOW - else - error("[src] was supposed to be destroyed, but we were unable to locate a blobstart landmark to spawn a new one.") - return QDEL_HINT_LETMELIVE // Cancel destruction unless forced. - -#undef NUKE_INTACT -#undef NUKE_COVER_OFF -#undef NUKE_COVER_OPEN -#undef NUKE_SEALANT_OPEN -#undef NUKE_UNWRENCHED -#undef NUKE_MOBILE +#define NUKE_INTACT 0 +#define NUKE_COVER_OFF 1 +#define NUKE_COVER_OPEN 2 +#define NUKE_SEALANT_OPEN 3 +#define NUKE_UNWRENCHED 4 +#define NUKE_MOBILE 5 + +GLOBAL_VAR(bomb_set) + +/obj/machinery/nuclearbomb + name = "\improper Nuclear Fission Explosive" + desc = "Uh oh. RUN!!!!" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nuclearbomb0" + density = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/deployable = 0 + var/extended = 0 + var/lighthack = 0 + var/timeleft = 120 + var/timing = 0 + var/r_code = "ADMIN" + var/code = "" + var/yes_code = 0 + var/safety = 1 + var/obj/item/disk/nuclear/auth = null + var/removal_stage = NUKE_INTACT + var/lastentered + var/is_syndicate = 0 + use_power = NO_POWER_USE + var/previous_level = "" + var/datum/wires/nuclearbomb/wires = null + +/obj/machinery/nuclearbomb/syndicate + is_syndicate = 1 + +/obj/machinery/nuclearbomb/New() + ..() + r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. + wires = new/datum/wires/nuclearbomb(src) + previous_level = get_security_level() + GLOB.poi_list |= src + +/obj/machinery/nuclearbomb/Destroy() + QDEL_NULL(wires) + GLOB.poi_list.Remove(src) + return ..() + +/obj/machinery/nuclearbomb/process() + if(timing) + GLOB.bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. + timeleft = max(timeleft - 2, 0) // 2 seconds per process() + if(timeleft <= 0) + spawn + explode() + SSnanoui.update_uis(src) + return + +/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/disk/nuclear)) + if(extended) + if(!user.drop_item()) + to_chat(user, "\The [O] is stuck to your hand!") + return + O.forceMove(src) + auth = O + add_fingerprint(user) + return attack_hand(user) + else + to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") + return + return ..() + +/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I) + if(!anchored) + return + if(removal_stage != NUKE_UNWRENCHED && removal_stage != NUKE_COVER_OFF) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(removal_stage == NUKE_COVER_OFF) + user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [I]...") + if(!I.use_tool(src, user, 15, volume = I.tool_volume) || removal_stage != NUKE_COVER_OFF) + return + user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") + removal_stage = NUKE_COVER_OPEN + else + user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") + if(!I.use_tool(src, user, 80, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED) + return + user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") + anchored = FALSE + removal_stage = NUKE_MOBILE + +/obj/machinery/nuclearbomb/wrench_act(mob/user, obj/item/I) + if(!anchored) + return + if(removal_stage != NUKE_SEALANT_OPEN) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume) || removal_stage != NUKE_SEALANT_OPEN) + return + user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") + removal_stage = NUKE_UNWRENCHED + +/obj/machinery/nuclearbomb/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + attack_hand(user) + +/obj/machinery/nuclearbomb/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(auth) + if(!panel_open) + panel_open = TRUE + overlays += image(icon, "npanel_open") + to_chat(user, "You unscrew the control panel of [src].") + else + panel_open = FALSE + overlays -= image(icon, "npanel_open") + to_chat(user, "You screw the control panel of [src] back on.") + else + if(!panel_open) + to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") + if(panel_open == TRUE) + panel_open = FALSE + overlays -= image(icon, "npanel_open") + to_chat(user, "You screw the control panel of [src] back on.") + flick("nuclearbombc", src) + +/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + attack_hand(user) + +/obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I) + . = TRUE + if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN) + return + if(!I.tool_use_check(user, 0)) + return + if(removal_stage == NUKE_INTACT) + visible_message("[user] starts cutting loose the anchoring bolt covers on [src].",\ + "You start cutting loose the anchoring bolt covers with [I]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_INTACT) + return + visible_message("[user] cuts through the bolt covers on [src].",\ + "You cut through the bolt cover.") + removal_stage = NUKE_COVER_OFF + else if(removal_stage == NUKE_COVER_OPEN) + visible_message("[user] starts cutting apart the anchoring system sealant on [src].",\ + "You start cutting apart the anchoring system's sealant with [I]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_COVER_OPEN) + return + visible_message("[user] cuts apart the anchoring system sealant on [src].",\ + "You cut apart the anchoring system's sealant.
    ") + removal_stage = NUKE_SEALANT_OPEN + +/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) + if(extended) + attack_hand(user) + +/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) + if(extended) + if(panel_open) + wires.Interact(user) + else + ui_interact(user) + else if(deployable) + if(removal_stage != NUKE_MOBILE) + anchored = 1 + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") + else + visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + if(!lighthack) + flick("nuclearbombc", src) + icon_state = "nuclearbomb1" + extended = 1 + return + +/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = GLOB.physical_state) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) + var/data[0] + data["is_syndicate"] = is_syndicate + data["hacking"] = 0 + data["auth"] = is_auth(user) + if(is_auth(user)) + if(yes_code) + data["authstatus"] = timing ? "Functional/Set" : "Functional" + else + data["authstatus"] = "Auth. S2" + else + if(timing) + data["authstatus"] = "Set" + else + data["authstatus"] = "Auth. S1" + data["safe"] = safety ? "Safe" : "Engaged" + data["time"] = timeleft + data["timer"] = timing + data["safety"] = safety + data["anchored"] = anchored + data["yescode"] = yes_code + data["message"] = "AUTH" + if(is_auth(user)) + data["message"] = code + if(yes_code) + data["message"] = "*****" + + return data + +/obj/machinery/nuclearbomb/verb/make_deployable() + set category = "Object" + set name = "Make Deployable" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(deployable) + to_chat(usr, "You close several panels to make [src] undeployable.") + deployable = 0 + else + to_chat(usr, "You adjust some panels to make [src] deployable.") + deployable = 1 + return + +/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) + if(auth) + return 1 + else if(user.can_admin_interact()) + return 1 + else + return 0 + +/obj/machinery/nuclearbomb/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["auth"]) + if(auth) + auth.loc = loc + yes_code = 0 + auth = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/disk/nuclear)) + usr.drop_item() + I.loc = src + auth = I + if(is_auth(usr)) + if(href_list["type"]) + if(href_list["type"] == "E") + if(code == r_code) + yes_code = 1 + code = null + else + code = "ERROR" + else + if(href_list["type"] == "R") + yes_code = 0 + code = null + else + lastentered = text("[]", href_list["type"]) + if(text2num(lastentered) == null) + var/turf/LOC = get_turf(usr) + message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) + log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") + else + code += lastentered + if(length(code) > 5) + code = "ERROR" + if(yes_code) + if(href_list["time"]) + var/time = text2num(href_list["time"]) + timeleft += time + timeleft = min(max(round(src.timeleft), 120), 600) + if(href_list["timer"]) + if(timing == -1.0) + SSnanoui.update_uis(src) + return + if(safety) + to_chat(usr, "The safety is still on.") + SSnanoui.update_uis(src) + return + timing = !(timing) + if(timing) + if(!lighthack) + icon_state = "nuclearbomb2" + if(!safety) + message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") + if(!is_syndicate) + set_security_level("delta") + GLOB.bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N + else + GLOB.bomb_set = 0 + else + if(!is_syndicate) + set_security_level(previous_level) + GLOB.bomb_set = 0 + if(!lighthack) + icon_state = "nuclearbomb1" + if(href_list["safety"]) + safety = !(safety) + if(safety) + if(!is_syndicate) + set_security_level(previous_level) + timing = 0 + GLOB.bomb_set = 0 + if(href_list["anchor"]) + if(removal_stage == NUKE_MOBILE) + anchored = 0 + visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + SSnanoui.update_uis(src) + return + + if(!isinspace()) + anchored = !(anchored) + if(anchored) + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") + else + visible_message("The anchoring bolts slide back into the depths of [src].") + else + to_chat(usr, "There is nothing to anchor to!") + + SSnanoui.update_uis(src) + +/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) + if(timing == -1.0) + return + qdel(src) + +/obj/machinery/nuclearbomb/tesla_act(power, explosive) + ..() + if(explosive) + qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over + +#define NUKERANGE 80 +/obj/machinery/nuclearbomb/proc/explode() + if(safety) + timing = 0 + return + timing = -1.0 + yes_code = 0 + safety = 1 + if(!lighthack) + icon_state = "nuclearbomb3" + playsound(src,'sound/machines/alarm.ogg',100,0,5) + if(SSticker && SSticker.mode) + SSticker.mode.explosion_in_progress = 1 + sleep(100) + + GLOB.enter_allowed = 0 + + var/off_station = 0 + var/turf/bomb_location = get_turf(src) + if( bomb_location && is_station_level(bomb_location.z) ) + if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) ) + off_station = 1 + else + off_station = 2 + + if(SSticker) + if(SSticker.mode && SSticker.mode.name == "nuclear emergency") + var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate") + if(syndie_shuttle) + SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z) + SSticker.mode:nuke_off_station = off_station + SSticker.station_explosion_cinematic(off_station,null) + if(SSticker.mode) + SSticker.mode.explosion_in_progress = 0 + if(SSticker.mode.name == "nuclear emergency") + SSticker.mode:nukes_left -- + else if(off_station == 1) + to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!") + else if(off_station == 2) + to_chat(world, "A nuclear device was set off, but the device was not on the station!") + else + to_chat(world, "The station was destoyed by the nuclear blast!") + + SSticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. + //kinda shit but I couldn't get permission to do what I wanted to do. + + if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is + world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending") + return + return + + +//==========DAT FUKKEN DISK=============== +/obj/item/disk/nuclear + name = "nuclear authentication disk" + desc = "Better keep this safe." + icon_state = "nucleardisk" + max_integrity = 250 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/disk/nuclear/unrestricted + desc = "Seems to have been stripped of its safeties, you better not lose it." + +/obj/item/disk/nuclear/New() + ..() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + +/obj/item/disk/nuclear/process() + if(!check_disk_loc()) + var/holder = get(src, /mob) + if(holder) + to_chat(holder, "You can't help but feel that you just lost something back there...") + qdel(src) + + //station disk is allowed on z1, escape shuttle/pods, CC, and syndicate shuttles/base, reset otherwise +/obj/item/disk/nuclear/proc/check_disk_loc() + var/turf/T = get_turf(src) + var/area/A = get_area(src) + if(is_station_level(T.z)) + return TRUE + if(A.nad_allowed) + return TRUE + return FALSE + +/obj/item/disk/nuclear/unrestricted/check_disk_loc() + return TRUE + +/obj/item/disk/nuclear/Destroy(force) + var/turf/diskturf = get_turf(src) + + if(force) + message_admins("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z] - JMP":"nonexistent location"]).") + log_game("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z]":"nonexistent location"]).") + GLOB.poi_list.Remove(src) + STOP_PROCESSING(SSobj, src) + return ..() + + if(GLOB.blobstart.len > 0) + GLOB.poi_list.Remove(src) + var/obj/item/disk/nuclear/NEWDISK = new(pick(GLOB.blobstart)) + transfer_fingerprints_to(NEWDISK) + message_admins("[src] has been destroyed at ([diskturf.x], [diskturf.y], [diskturf.z] - JMP). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z] - JMP).") + log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y], [diskturf.z]). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z]).") + return QDEL_HINT_HARDDEL_NOW + else + error("[src] was supposed to be destroyed, but we were unable to locate a blobstart landmark to spawn a new one.") + return QDEL_HINT_LETMELIVE // Cancel destruction unless forced. + +#undef NUKE_INTACT +#undef NUKE_COVER_OFF +#undef NUKE_COVER_OPEN +#undef NUKE_SEALANT_OPEN +#undef NUKE_UNWRENCHED +#undef NUKE_MOBILE diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 251c32716bb6..e8f4c22e377a 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,404 +1,409 @@ -/obj/item/pinpointer - name = "pinpointer" - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - flags = CONDUCT - slot_flags = SLOT_PDA | SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - materials = list(MAT_METAL=500) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/obj/item/disk/nuclear/the_disk = null - var/active = 0 - var/shows_nuke_timer = TRUE - var/icon_off = "pinoff" - var/icon_null = "pinonnull" - var/icon_direct = "pinondirect" - var/icon_close = "pinonclose" - var/icon_medium = "pinonmedium" - var/icon_far = "pinonfar" - -/obj/item/pinpointer/New() - ..() - GLOB.pinpointer_list += src - -/obj/item/pinpointer/Destroy() - GLOB.pinpointer_list -= src - active = 0 - the_disk = null - return ..() - -/obj/item/pinpointer/attack_self() - if(!active) - active = 1 - workdisk() - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/proc/scandisk() - if(!the_disk) - the_disk = locate() - -/obj/item/pinpointer/proc/point_at(atom/target, spawnself = 1) - if(!active) - return - if(!target) - icon_state = icon_null - return - - var/turf/T = get_turf(target) - var/turf/L = get_turf(src) - - if(!(T && L) || (T.z != L.z)) - icon_state = icon_null - else - dir = get_dir(L, T) - switch(get_dist(L, T)) - if(-1) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - if(spawnself) - spawn(5) - .() - -/obj/item/pinpointer/proc/workdisk() - scandisk() - point_at(the_disk, 0) - spawn(5) - .() - -/obj/item/pinpointer/examine(mob/user) - . = ..() - if(shows_nuke_timer) - for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) - if(bomb.timing) - . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" - -/obj/item/pinpointer/advpinpointer - name = "advanced pinpointer" - desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." - var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. - var/turf/location = null - var/obj/target = null - -/obj/item/pinpointer/advpinpointer/attack_self() - if(!active) - active = 1 - if(mode == 0) - workdisk() - if(mode == 1) - point_at(location) - if(mode == 2) - point_at(target) - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/advpinpointer/workdisk() - if(mode == 0) - scandisk() - point_at(the_disk, 0) - spawn(5) - .() - -/obj/item/pinpointer/advpinpointer/verb/toggle_mode() - set category = "Object" - set name = "Toggle Pinpointer Mode" - set src in usr - - if(usr.stat || usr.restrained()) - return - - active = 0 - icon_state = icon_off - target = null - location = null - - switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) - if("Location") - mode = 1 - - var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num - if(!locationx || !(usr in view(1,src))) - return - var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num - if(!locationy || !(usr in view(1,src))) - return - - var/turf/Z = get_turf(src) - - location = locate(locationx,locationy,Z.z) - - to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") - - - return attack_self() - - if("Disk Recovery") - mode = 0 - return attack_self() - - if("Other Signature") - mode = 2 - switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "Item" , "DNA")) - if("Item") - var/list/item_names[0] - var/list/item_paths[0] - for(var/objective in potential_theft_objectives) - var/datum/theft_objective/T = objective - var/name = initial(T.name) - item_names += name - item_paths[name] = initial(T.typepath) - var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in item_names - if(!targetitem) - return - - var/list/target_candidates = get_all_of_type(item_paths[targetitem], subtypes = TRUE) - for(var/obj/item/candidate in target_candidates) - if(!is_admin_level((get_turf(candidate)).z)) - target = candidate - break - - if(!target) - to_chat(usr, "Failed to locate [targetitem]!") - return - to_chat(usr, "You set the pinpointer to locate [targetitem].") - if("DNA") - var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") - if(!DNAstring) - return - for(var/mob/living/carbon/C in GLOB.mob_list) - if(!C.dna) - continue - if(C.dna.unique_enzymes == DNAstring) - target = C - break - - return attack_self() - -/////////////////////// -//nuke op pinpointers// -/////////////////////// -/obj/item/pinpointer/nukeop - var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle - var/obj/docking_port/mobile/home = null - slot_flags = SLOT_BELT | SLOT_PDA - -/obj/item/pinpointer/nukeop/attack_self(mob/user as mob) - if(!active) - active = 1 - if(!mode) - workdisk() - to_chat(user, "Authentication Disk Locator active.") - else - worklocation() - to_chat(user, "Shuttle Locator active.") - else - active = 0 - icon_state = icon_off - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/pinpointer/nukeop/workdisk() - if(!active) return - if(mode) //Check in case the mode changes while operating - worklocation() - return - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - worklocation() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed - return //Get outta here - scandisk() - if(!the_disk) - icon_state = icon_null - return - dir = get_dir(src, the_disk) - switch(get_dist(src, the_disk)) - if(0) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - - spawn(5) .() - -/obj/item/pinpointer/nukeop/proc/worklocation() - if(!active) - return - if(!mode) - workdisk() - return - if(!bomb_set) - mode = 0 - workdisk() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) - visible_message("Authentication Disk Locator mode actived.") - return - if(!home) - home = SSshuttle.getShuttle("syndicate") - if(!home) - icon_state = icon_null - return - if(loc.z != home.z) //If you are on a different z-level from the shuttle - icon_state = icon_null - else - dir = get_dir(src, home) - switch(get_dist(src, home)) - if(0) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - - spawn(5) - .() - -/obj/item/pinpointer/operative - name = "operative pinpointer" - desc = "A pinpointer that leads to the first Syndicate operative detected." - var/mob/living/carbon/nearest_op = null - -/obj/item/pinpointer/operative/attack_self() - if(!usr.mind || !(usr.mind in SSticker.mode.syndicates)) - to_chat(usr, "AUTHENTICATION FAILURE. ACCESS DENIED.") - return 0 - if(!active) - active = 1 - workop() - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/operative/proc/scan_for_ops() - if(active) - nearest_op = null //Resets nearest_op every time it scans - var/closest_distance = 1000 - for(var/mob/living/carbon/M in GLOB.mob_list) - if(M.mind && (M.mind in SSticker.mode.syndicates)) - if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to - nearest_op = M - -/obj/item/pinpointer/operative/proc/workop() - if(active) - scan_for_ops() - point_at(nearest_op, 0) - spawn(5) - .() - else - return 0 - -/obj/item/pinpointer/operative/examine(mob/user) - . = ..() - if(active) - if(nearest_op) - . += "Nearest operative detected is [nearest_op.real_name]." - else - . += "No operatives detected within scanning range." - -/obj/item/pinpointer/crew - name = "crew pinpointer" - desc = "A handheld tracking device that points to crew suit sensors." - shows_nuke_timer = FALSE - icon_state = "pinoff_crew" - icon_off = "pinoff_crew" - icon_null = "pinonnull_crew" - icon_direct = "pinondirect_crew" - icon_close = "pinonclose_crew" - icon_medium = "pinonmedium_crew" - icon_far = "pinonfar_crew" - -/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) - var/turf/here = get_turf(src) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - - // Suit sensors must be on maximum. - if(!U.has_sensor || U.sensor_mode < 3) - return FALSE - - var/turf/there = get_turf(U) - return there && there.z == here.z - - return FALSE - -/obj/item/pinpointer/crew/attack_self(mob/living/user) - if(active) - active = FALSE - icon_state = icon_off - user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.") - return - - var/list/name_counts = list() - var/list/names = list() - - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(!trackable(H)) - continue - - var/name = "Unknown" - if(H.wear_id) - var/obj/item/card/id/I = H.wear_id.GetID() - if(I) - name = I.registered_name - - while(name in name_counts) - name_counts[name]++ - name = text("[] ([])", name, name_counts[name]) - names[name] = H - name_counts[name] = 1 - - if(!names.len) - user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") - return - - var/A = input(user, "Person to track", "Pinpoint") in names - if(!src || !user || (user.get_active_hand() != src) || user.incapacitated() || !A) - return - - var/target = names[A] - active = TRUE - user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.") - point_at(target) - -/obj/item/pinpointer/crew/point_at(atom/target, spawnself = 1) - if(!active) - return - - if(!trackable(target) || !target) - icon_state = icon_null - return - - ..(target, spawnself = 0) - if(spawnself) - spawn(5) - .() - -/obj/item/pinpointer/crew/centcom - name = "centcom pinpointer" - desc = "A handheld tracking device that tracks crew based on remote centcom sensors." - -/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H) - var/turf/here = get_turf(src) - var/turf/there = get_turf(H) - return there && there.z == here.z \ No newline at end of file +/obj/item/pinpointer + name = "pinpointer" + icon = 'icons/obj/device.dmi' + icon_state = "pinoff" + flags = CONDUCT + slot_flags = SLOT_PDA | SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + materials = list(MAT_METAL=500) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/obj/item/disk/nuclear/the_disk = null + var/active = 0 + var/shows_nuke_timer = TRUE + var/icon_off = "pinoff" + var/icon_null = "pinonnull" + var/icon_direct = "pinondirect" + var/icon_close = "pinonclose" + var/icon_medium = "pinonmedium" + var/icon_far = "pinonfar" + +/obj/item/pinpointer/New() + ..() + GLOB.pinpointer_list += src + +/obj/item/pinpointer/Destroy() + GLOB.pinpointer_list -= src + active = 0 + the_disk = null + return ..() + +/obj/item/pinpointer/attack_self() + if(!active) + active = 1 + workdisk() + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/proc/scandisk() + if(!the_disk) + the_disk = locate() + +/obj/item/pinpointer/proc/point_at(atom/target, spawnself = 1) + if(!active) + return + if(!target) + icon_state = icon_null + return + + var/turf/T = get_turf(target) + var/turf/L = get_turf(src) + + if(!(T && L) || (T.z != L.z)) + icon_state = icon_null + else + dir = get_dir(L, T) + switch(get_dist(L, T)) + if(-1) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + if(spawnself) + spawn(5) + .() + +/obj/item/pinpointer/proc/workdisk() + scandisk() + point_at(the_disk, 0) + spawn(5) + .() + +/obj/item/pinpointer/examine(mob/user) + . = ..() + if(shows_nuke_timer) + for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) + if(bomb.timing) + . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" + +/obj/item/pinpointer/advpinpointer + name = "advanced pinpointer" + desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." + var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. + var/modelocked = FALSE // If true, user cannot change mode. + var/turf/location = null + var/obj/target = null + +/obj/item/pinpointer/advpinpointer/attack_self() + if(!active) + active = 1 + if(mode == 0) + workdisk() + if(mode == 1) + point_at(location) + if(mode == 2) + point_at(target) + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/advpinpointer/workdisk() + if(mode == 0) + scandisk() + point_at(the_disk, 0) + spawn(5) + .() + +/obj/item/pinpointer/advpinpointer/verb/toggle_mode() + set category = "Object" + set name = "Toggle Pinpointer Mode" + set src in usr + + if(usr.stat || usr.restrained()) + return + + if(modelocked) + to_chat(usr, "[src] is locked. It can only track one specific target.") + return + + active = 0 + icon_state = icon_off + target = null + location = null + + switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) + if("Location") + mode = 1 + + var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num + if(!locationx || !(usr in view(1,src))) + return + var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num + if(!locationy || !(usr in view(1,src))) + return + + var/turf/Z = get_turf(src) + + location = locate(locationx,locationy,Z.z) + + to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") + + + return attack_self() + + if("Disk Recovery") + mode = 0 + return attack_self() + + if("Other Signature") + mode = 2 + switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "Item" , "DNA")) + if("Item") + var/list/item_names[0] + var/list/item_paths[0] + for(var/objective in GLOB.potential_theft_objectives) + var/datum/theft_objective/T = objective + var/name = initial(T.name) + item_names += name + item_paths[name] = initial(T.typepath) + var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in item_names + if(!targetitem) + return + + var/list/target_candidates = get_all_of_type(item_paths[targetitem], subtypes = TRUE) + for(var/obj/item/candidate in target_candidates) + if(!is_admin_level((get_turf(candidate)).z)) + target = candidate + break + + if(!target) + to_chat(usr, "Failed to locate [targetitem]!") + return + to_chat(usr, "You set the pinpointer to locate [targetitem].") + if("DNA") + var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") + if(!DNAstring) + return + for(var/mob/living/carbon/C in GLOB.mob_list) + if(!C.dna) + continue + if(C.dna.unique_enzymes == DNAstring) + target = C + break + + return attack_self() + +/////////////////////// +//nuke op pinpointers// +/////////////////////// +/obj/item/pinpointer/nukeop + var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle + var/obj/docking_port/mobile/home = null + slot_flags = SLOT_BELT | SLOT_PDA + +/obj/item/pinpointer/nukeop/attack_self(mob/user as mob) + if(!active) + active = 1 + if(!mode) + workdisk() + to_chat(user, "Authentication Disk Locator active.") + else + worklocation() + to_chat(user, "Shuttle Locator active.") + else + active = 0 + icon_state = icon_off + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/pinpointer/nukeop/workdisk() + if(!active) return + if(mode) //Check in case the mode changes while operating + worklocation() + return + if(GLOB.bomb_set) //If the bomb is set, lead to the shuttle + mode = 1 //Ensures worklocation() continues to work + worklocation() + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep + visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed + return //Get outta here + scandisk() + if(!the_disk) + icon_state = icon_null + return + dir = get_dir(src, the_disk) + switch(get_dist(src, the_disk)) + if(0) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + + spawn(5) .() + +/obj/item/pinpointer/nukeop/proc/worklocation() + if(!active) + return + if(!mode) + workdisk() + return + if(!GLOB.bomb_set) + mode = 0 + workdisk() + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) + visible_message("Authentication Disk Locator mode actived.") + return + if(!home) + home = SSshuttle.getShuttle("syndicate") + if(!home) + icon_state = icon_null + return + if(loc.z != home.z) //If you are on a different z-level from the shuttle + icon_state = icon_null + else + dir = get_dir(src, home) + switch(get_dist(src, home)) + if(0) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + + spawn(5) + .() + +/obj/item/pinpointer/operative + name = "operative pinpointer" + desc = "A pinpointer that leads to the first Syndicate operative detected." + var/mob/living/carbon/nearest_op = null + +/obj/item/pinpointer/operative/attack_self() + if(!usr.mind || !(usr.mind in SSticker.mode.syndicates)) + to_chat(usr, "AUTHENTICATION FAILURE. ACCESS DENIED.") + return 0 + if(!active) + active = 1 + workop() + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/operative/proc/scan_for_ops() + if(active) + nearest_op = null //Resets nearest_op every time it scans + var/closest_distance = 1000 + for(var/mob/living/carbon/M in GLOB.mob_list) + if(M.mind && (M.mind in SSticker.mode.syndicates)) + if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to + nearest_op = M + +/obj/item/pinpointer/operative/proc/workop() + if(active) + scan_for_ops() + point_at(nearest_op, 0) + spawn(5) + .() + else + return 0 + +/obj/item/pinpointer/operative/examine(mob/user) + . = ..() + if(active) + if(nearest_op) + . += "Nearest operative detected is [nearest_op.real_name]." + else + . += "No operatives detected within scanning range." + +/obj/item/pinpointer/crew + name = "crew pinpointer" + desc = "A handheld tracking device that points to crew suit sensors." + shows_nuke_timer = FALSE + icon_state = "pinoff_crew" + icon_off = "pinoff_crew" + icon_null = "pinonnull_crew" + icon_direct = "pinondirect_crew" + icon_close = "pinonclose_crew" + icon_medium = "pinonmedium_crew" + icon_far = "pinonfar_crew" + +/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + + // Suit sensors must be on maximum. + if(!U.has_sensor || U.sensor_mode < 3) + return FALSE + + var/turf/there = get_turf(U) + return there && there.z == here.z + + return FALSE + +/obj/item/pinpointer/crew/attack_self(mob/living/user) + if(active) + active = FALSE + icon_state = icon_off + user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.") + return + + var/list/name_counts = list() + var/list/names = list() + + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(!trackable(H)) + continue + + var/name = "Unknown" + if(H.wear_id) + var/obj/item/card/id/I = H.wear_id.GetID() + if(I) + name = I.registered_name + + while(name in name_counts) + name_counts[name]++ + name = text("[] ([])", name, name_counts[name]) + names[name] = H + name_counts[name] = 1 + + if(!names.len) + user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") + return + + var/A = input(user, "Person to track", "Pinpoint") in names + if(!src || !user || (user.get_active_hand() != src) || user.incapacitated() || !A) + return + + var/target = names[A] + active = TRUE + user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.") + point_at(target) + +/obj/item/pinpointer/crew/point_at(atom/target, spawnself = 1) + if(!active) + return + + if(!trackable(target) || !target) + icon_state = icon_null + return + + ..(target, spawnself = 0) + if(spawnself) + spawn(5) + .() + +/obj/item/pinpointer/crew/centcom + name = "centcom pinpointer" + desc = "A handheld tracking device that tracks crew based on remote centcom sensors." + +/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + var/turf/there = get_turf(H) + return there && there.z == here.z diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 89ecb1d103b2..9974ee8305ab 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,809 +1,809 @@ -GLOBAL_LIST_EMPTY(all_objectives) - -var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique - -/datum/objective - var/datum/mind/owner = null //Who owns the objective. - var/explanation_text = "Nothing" //What that person is supposed to do. - var/datum/mind/target = null //If they are focused on a particular person. - var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. - var/completed = 0 //currently only used for custom objectives. - var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. - -/datum/objective/New(text) - GLOB.all_objectives += src - if(text) - explanation_text = text - -/datum/objective/Destroy() - GLOB.all_objectives -= src - return ..() - -/datum/objective/proc/check_completion() - return completed - -/datum/objective/proc/is_invalid_target(datum/mind/possible_target) - if(possible_target == owner) - return TARGET_INVALID_IS_OWNER - if(possible_target in owner.targets) - return TARGET_INVALID_IS_TARGET - if(!ishuman(possible_target.current)) - return TARGET_INVALID_NOT_HUMAN - if(!possible_target.current.stat == DEAD) - return TARGET_INVALID_DEAD - if(!possible_target.key) - return TARGET_INVALID_NOCKEY - if(possible_target.current) - var/turf/current_location = get_turf(possible_target.current) - if(current_location && !is_level_reachable(current_location.z)) - return TARGET_INVALID_UNREACHABLE - if(isgolem(possible_target.current)) - return TARGET_INVALID_GOLEM - if(possible_target.offstation_role) - return TARGET_INVALID_EVENT - - -/datum/objective/proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if(is_invalid_target(possible_target)) - continue - - possible_targets += possible_target - - if(possible_targets.len > 0) - target = pick(possible_targets) - -/datum/objective/assassinate - martyr_compatible = 1 - -/datum/objective/assassinate/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/assassinate/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 1 - if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - if(!target.current.ckey) - return 1 - return 0 - return 1 - - -/datum/objective/mutiny - martyr_compatible = 1 - -/datum/objective/mutiny/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/mutiny/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) - return 1 - var/turf/T = get_turf(target.current) - if(T && !is_station_level(T.z)) //If they leave the station they count as dead for this - return 1 - return 0 - return 1 - -/datum/objective/maroon - martyr_compatible = 1 - -/datum/objective/maroon/find_target() - ..() - if(target && target.current) - explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/maroon/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 1 - if(!target.current.ckey) - return 1 - if(issilicon(target.current)) - return 1 - if(isbrain(target.current)) - return 1 - var/turf/T = get_turf(target.current) - if(is_admin_level(T.z)) - return 0 - return 1 - return 1 - - -/datum/objective/debrain //I want braaaainssss - martyr_compatible = 0 - -/datum/objective/debrain/find_target() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/debrain/check_completion() - if(!target)//If it's a free objective. - return 1 - if(!owner.current || owner.current.stat == DEAD) - return 0 - if(!target.current || !isbrain(target.current)) - return 0 - var/atom/A = target.current - while(A.loc) //check to see if the brainmob is on our person - A = A.loc - if(A == owner.current) - return 1 - return 0 - - -/datum/objective/protect //The opposite of killing a dude. - martyr_compatible = 1 - -/datum/objective/protect/find_target() - ..() - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/protect/check_completion() - if(!target) //If it's a free objective. - return 1 - if(target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 0 - if(issilicon(target.current)) - return 0 - if(isbrain(target.current)) - return 0 - return 1 - return 0 - -/datum/objective/protect/mindslave //subytpe for mindslave implants - - -/datum/objective/hijack - martyr_compatible = 0 //Technically you won't get both anyway. - explanation_text = "Hijack the shuttle by escaping on it with no loyalist Nanotrasen crew on board and free. \ - Syndicate agents, other enemies of Nanotrasen, cyborgs, pets, and cuffed/restrained hostages may be allowed on the shuttle alive." - -/datum/objective/hijack/check_completion() - if(!owner.current || owner.current.stat) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(issilicon(owner.current)) - return 0 - - var/area/A = get_area(owner.current) - if(SSshuttle.emergency.areaInstance != A) - return 0 - - return SSshuttle.emergency.is_hijacked() - -/datum/objective/hijackclone - explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape." - martyr_compatible = 0 - -/datum/objective/hijackclone/check_completion() - if(!owner.current) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - - for(var/mob/living/player in GLOB.player_list) //Make sure nobody else is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(issilicon(player)) - continue - if(get_area(player) == A) - if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) - return 0 - - for(var/mob/living/player in GLOB.player_list) //Make sure at least one of you is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(issilicon(player)) - continue - if(get_area(player) == A) - if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) - return 1 - return 0 - -/datum/objective/block - explanation_text = "Do not allow any lifeforms, be it organic or synthetic to escape on the shuttle alive. AIs, Cyborgs, and pAIs are not considered alive." - martyr_compatible = 1 - -/datum/objective/block/check_completion() - if(!istype(owner.current, /mob/living/silicon)) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(!owner.current) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) - - for(var/mob/living/player in GLOB.player_list) - if(player.type in protected_mobs) - continue - - if(player.mind && player.stat != DEAD) - if(get_area(player) == A) - return 0 - - return 1 - -/datum/objective/escape - explanation_text = "Escape on the shuttle or an escape pod alive and free." - -/datum/objective/escape/check_completion() - if(issilicon(owner.current)) - return 0 - if(isbrain(owner.current)) - return 0 - if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) - return 0 - if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith - return 1 - if(SSticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win - return 1 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - var/turf/location = get_turf(owner.current) - if(!location) - return 0 - - if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph - return 0 - - if(location.onCentcom() || location.onSyndieBase()) - return 1 - - return 0 - - -/datum/objective/escape/escape_with_identity - var/target_real_name // Has to be stored because the target's real_name can change over the course of the round - -/datum/objective/escape/escape_with_identity/find_target() - var/list/possible_targets = list() //Copypasta because NO_DNA races, yay for snowflakes. - for(var/datum/mind/possible_target in SSticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && possible_target.current.client) - var/mob/living/carbon/human/H = possible_target.current - if(!(NO_DNA in H.dna.species.species_traits)) - possible_targets += possible_target - if(possible_targets.len > 0) - target = pick(possible_targets) - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." - else - explanation_text = "Free Objective" - -/datum/objective/escape/escape_with_identity/check_completion() - if(!target_real_name) - return 1 - if(!ishuman(owner.current)) - return 0 - var/mob/living/carbon/human/H = owner.current - if(..()) - if(H.dna.real_name == target_real_name) - if(H.get_id_name()== target_real_name) - return 1 - return 0 - -/datum/objective/die - explanation_text = "Die a glorious death." - -/datum/objective/die/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) - return 1 - if(issilicon(owner.current) && owner.current != owner.original) - return 1 - return 0 - - - -/datum/objective/survive - explanation_text = "Stay alive until the end." - -/datum/objective/survive/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) - return 0 //Brains no longer win survive objectives. --NEO - if(issilicon(owner.current) && owner.current != owner.original) - return 0 - return 1 - -/datum/objective/nuclear - explanation_text = "Destroy the station with a nuclear device." - martyr_compatible = 1 - -/datum/objective/steal - var/datum/theft_objective/steal_target - martyr_compatible = 0 - var/theft_area - -/datum/objective/steal/proc/get_location() - if(steal_target.location_override) - return steal_target.location_override - var/list/obj/item/steal_candidates = get_all_of_type(steal_target.typepath, subtypes = TRUE) - for(var/obj/item/candidate in steal_candidates) - if(!is_admin_level(candidate.loc.z)) - theft_area = get_area(candidate.loc) - return "[theft_area]" - return "an unknown area" - -/datum/objective/steal/find_target() - var/loop=50 - while(!steal_target && loop > 0) - loop-- - var/thefttype = pick(potential_theft_objectives) - var/datum/theft_objective/O = new thefttype - if(owner.assigned_role in O.protected_jobs) - continue - if(O in owner.targets) - continue - if(O.flags & 2) - continue - steal_target = O - - explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. " - if(islist(O.protected_jobs) && O.protected_jobs.len) - explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." - return - explanation_text = "Free Objective." - - -/datum/objective/steal/proc/select_target() - var/list/possible_items_all = potential_theft_objectives+"custom" - var/new_target = input("Select target:", "Objective target", null) as null|anything in possible_items_all - if(!new_target) return - if(new_target == "custom") - var/datum/theft_objective/O=new - O.typepath = input("Select type:","Type") as null|anything in typesof(/obj/item) - if(!O.typepath) return - var/tmp_obj = new O.typepath - var/custom_name = tmp_obj:name - qdel(tmp_obj) - O.name = sanitize(copytext(input("Enter target name:", "Objective target", custom_name) as text|null,1,MAX_NAME_LEN)) - if(!O.name) return - steal_target = O - explanation_text = "Steal [O.name]." - else - steal_target = new new_target - explanation_text = "Steal [steal_target.name]." - return steal_target - -/datum/objective/steal/check_completion() - if(!steal_target) - return 1 // Free Objective - - var/list/all_items = owner.current.GetAllContents() - - for(var/obj/I in all_items) - if(istype(I, steal_target.typepath)) - return steal_target.check_special_completion(I) - if(I.type in steal_target.altitems) - return steal_target.check_special_completion(I) - - -/datum/objective/steal/exchange - martyr_compatible = 0 - -/datum/objective/steal/exchange/proc/set_faction(var/faction,var/otheragent) - target = otheragent - var/datum/theft_objective/unique/targetinfo - if(faction == "red") - targetinfo = new /datum/theft_objective/unique/docs_blue - else if(faction == "blue") - targetinfo = new /datum/theft_objective/unique/docs_red - explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" - steal_target = targetinfo - -/datum/objective/steal/exchange/backstab -/datum/objective/steal/exchange/backstab/set_faction(var/faction) - var/datum/theft_objective/unique/targetinfo - if(faction == "red") - targetinfo = new /datum/theft_objective/unique/docs_red - else if(faction == "blue") - targetinfo = new /datum/theft_objective/unique/docs_blue - explanation_text = "Do not give up or lose [targetinfo.name]." - steal_target = targetinfo - -/datum/objective/download -/datum/objective/download/proc/gen_amount_goal() - target_amount = rand(10,20) - explanation_text = "Download [target_amount] research levels." - return target_amount - - -/datum/objective/download/check_completion() - return 0 - - - -/datum/objective/capture -/datum/objective/capture/proc/gen_amount_goal() - target_amount = rand(5,10) - explanation_text = "Accumulate [target_amount] capture points." - return target_amount - - -/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth. - return 0 - - - - -/datum/objective/absorb -/datum/objective/absorb/proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6) - target_amount = rand (lowbound,highbound) - if(SSticker) - var/n_p = 1 //autowin - if(SSticker.current_state == GAME_STATE_SETTING_UP) - for(var/mob/new_player/P in GLOB.player_list) - if(P.client && P.ready && P.mind != owner) - if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. - continue - n_p++ - else if(SSticker.current_state == GAME_STATE_PLAYING) - for(var/mob/living/carbon/human/P in GLOB.player_list) - if(NO_DNA in P.dna.species.species_traits) - continue - if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner) - n_p++ - target_amount = min(target_amount, n_p) - - explanation_text = "Absorb [target_amount] compatible genomes." - return target_amount - -/datum/objective/absorb/check_completion() - if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) - return 1 - else - return 0 - -/datum/objective/destroy - martyr_compatible = 1 - var/target_real_name - -/datum/objective/destroy/find_target() - var/list/possible_targets = active_ais(1) - var/mob/living/silicon/ai/target_ai = pick(possible_targets) - target = target_ai.mind - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Destroy [target_real_name], the AI." - else - explanation_text = "Free Objective" - return target - -/datum/objective/destroy/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey) - return 1 - return 0 - return 1 - -/datum/objective/steal_five_of_type - explanation_text = "Steal at least five items!" - var/list/wanted_items = list() - -/datum/objective/steal_five_of_type/New() - ..() - wanted_items = typecacheof(wanted_items) - -/datum/objective/steal_five_of_type/check_completion() - var/stolen_count = 0 - if(!isliving(owner.current)) - return FALSE - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for wanted items - if(is_type_in_typecache(I, wanted_items)) - stolen_count++ - return stolen_count >= 5 - -/datum/objective/steal_five_of_type/summon_guns - explanation_text = "Steal at least five guns!" - wanted_items = list(/obj/item/gun) - -/datum/objective/steal_five_of_type/summon_magic - explanation_text = "Steal at least five magical artefacts!" - wanted_items = list() - -/datum/objective/steal_five_of_type/summon_magic/New() - wanted_items = GLOB.summoned_magic_objectives - ..() - -/datum/objective/steal_five_of_type/summon_magic/check_completion() - var/stolen_count = 0 - if(!isliving(owner.current)) - return FALSE - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for wanted items - if(istype(I, /obj/item/spellbook) && !istype(I, /obj/item/spellbook/oneuse)) - var/obj/item/spellbook/spellbook = I - if(spellbook.uses) //if the book still has powers... - stolen_count++ //it counts. nice. - if(istype(I, /obj/item/spellbook/oneuse)) - var/obj/item/spellbook/oneuse/oneuse = I - if(!oneuse.used) - stolen_count++ - else if(is_type_in_typecache(I, wanted_items)) - stolen_count++ - return stolen_count >= 5 - -/datum/objective/blood -/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400) - target_amount = rand(low,high) - target_amount = round(round(target_amount/5)*5) - explanation_text = "Accumulate at least [target_amount] total units of blood." - return target_amount - -/datum/objective/blood/check_completion() - if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount) - return 1 - else - return 0 - -// /vg/; Vox Inviolate for humans :V -/datum/objective/minimize_casualties - explanation_text = "Minimise casualties." -/datum/objective/minimize_casualties/check_completion() - if(owner.kills.len>5) return 0 - return 1 - -//Vox heist objectives. - -/datum/objective/heist -/datum/objective/heist/proc/choose_target() - return - -/datum/objective/heist/kidnap -/datum/objective/heist/kidnap/choose_target() - var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer") - var/list/possible_targets = list() - var/list/priority_targets = list() - - for(var/datum/mind/possible_target in SSticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && (possible_target.assigned_role != possible_target.special_role) && !possible_target.offstation_role) - possible_targets += possible_target - for(var/role in roles) - if(possible_target.assigned_role == role) - priority_targets += possible_target - continue - - if(priority_targets.len > 0) - target = pick(priority_targets) - else if(possible_targets.len > 0) - target = pick(possible_targets) - - if(target && target.current) - explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/heist/kidnap/check_completion() - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - - var/area/shuttle/vox/A = locate() //stupid fucking hardcoding - var/area/vox_station/B = locate() //but necessary - - for(var/mob/living/carbon/human/M in A) - if(target.current == M) - return 1 - for(var/mob/living/carbon/human/M in B) - if(target.current == M) - return 1 - else - return 0 - -/datum/objective/heist/loot -/datum/objective/heist/loot/choose_target() - var/loot = "an object" - switch(rand(1,8)) - if(1) - target = /obj/structure/particle_accelerator - target_amount = 6 - loot = "a complete particle accelerator" - if(2) - target = /obj/machinery/the_singularitygen - target_amount = 1 - loot = "a gravitational singularity generator" - if(3) - target = /obj/machinery/power/emitter - target_amount = 4 - loot = "four emitters" - if(4) - target = /obj/machinery/nuclearbomb - target_amount = 1 - loot = "a nuclear bomb" - if(5) - target = /obj/item/gun - target_amount = 6 - loot = "six guns. Tasers and other non-lethal guns are acceptable" - if(6) - target = /obj/item/gun/energy - target_amount = 4 - loot = "four energy guns" - if(7) - target = /obj/item/gun/energy/laser - target_amount = 2 - loot = "two laser guns" - if(8) - target = /obj/item/gun/energy/ionrifle - target_amount = 1 - loot = "an ion gun" - - explanation_text = "We are lacking in hardware. Steal or trade [loot]." - -/datum/objective/heist/loot/check_completion() - var/total_amount = 0 - - for(var/obj/O in locate(/area/shuttle/vox)) - if(istype(O, target)) - total_amount++ - for(var/obj/I in O.contents) - if(istype(I, target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - for(var/obj/O in locate(/area/vox_station)) - if(istype(O, target)) - total_amount++ - for(var/obj/I in O.contents) - if(istype(I, target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - if(raider.current) - for(var/obj/O in raider.current.get_contents()) - if(istype(O,target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - return 0 - -/datum/objective/heist/salvage -/datum/objective/heist/salvage/choose_target() - switch(rand(1,8)) - if(1) - target = "metal" - target_amount = 300 - if(2) - target = "glass" - target_amount = 200 - if(3) - target = "plasteel" - target_amount = 100 - if(4) - target = "solid plasma" - target_amount = 100 - if(5) - target = "silver" - target_amount = 50 - if(6) - target = "gold" - target_amount = 20 - if(7) - target = "uranium" - target_amount = 20 - if(8) - target = "diamond" - target_amount = 20 - - explanation_text = "Ransack or trade with the station and escape with [target_amount] [target]." - -/datum/objective/heist/salvage/check_completion() - var/total_amount = 0 - - for(var/obj/item/O in locate(/area/shuttle/vox)) - var/obj/item/stack/sheet/S - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - S = O - total_amount += S.get_amount() - - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/sheet)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - for(var/obj/item/O in locate(/area/vox_station)) - var/obj/item/stack/sheet/S - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - S = O - total_amount += S.get_amount() - - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/sheet)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - if(raider.current) - for(var/obj/item/O in raider.current.get_contents()) - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - var/obj/item/stack/sheet/S = O - total_amount += S.get_amount() - - if(total_amount >= target_amount) return 1 - return 0 - - -/datum/objective/heist/inviolate_crew - explanation_text = "Do not leave any Vox behind, alive or dead." - -/datum/objective/heist/inviolate_crew/check_completion() - var/datum/game_mode/heist/H = SSticker.mode - if(H.is_raider_crew_safe()) - return 1 - return 0 - -/datum/objective/heist/inviolate_death - explanation_text = "Follow the Inviolate. Minimise death and loss of resources." - -/datum/objective/heist/inviolate_death/check_completion() - var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. - var/vox_total_kills = 0 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills - - if(vox_total_kills > vox_allowed_kills) return 0 - return 1 - - -// Traders -// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. - -/datum/objective/trade/proc/choose_target() - return - -/datum/objective/trade/plasma/choose_target() - explanation_text = "Acquire at least 15 sheets of plasma through trade." - -/datum/objective/trade/credits/choose_target() - explanation_text = "Acquire at least 10,000 credits through trade." - -//wizard - -/datum/objective/wizchaos - explanation_text = "Wreak havoc upon the station as much you can. Send those wandless Nanotrasen scum a message!" - completed = 1 +GLOBAL_LIST_EMPTY(all_objectives) + +GLOBAL_LIST_INIT(potential_theft_objectives, (subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique)) + +/datum/objective + var/datum/mind/owner = null //Who owns the objective. + var/explanation_text = "Nothing" //What that person is supposed to do. + var/datum/mind/target = null //If they are focused on a particular person. + var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. + var/completed = 0 //currently only used for custom objectives. + var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. + +/datum/objective/New(text) + GLOB.all_objectives += src + if(text) + explanation_text = text + +/datum/objective/Destroy() + GLOB.all_objectives -= src + return ..() + +/datum/objective/proc/check_completion() + return completed + +/datum/objective/proc/is_invalid_target(datum/mind/possible_target) + if(possible_target == owner) + return TARGET_INVALID_IS_OWNER + if(possible_target in owner.targets) + return TARGET_INVALID_IS_TARGET + if(!ishuman(possible_target.current)) + return TARGET_INVALID_NOT_HUMAN + if(!possible_target.current.stat == DEAD) + return TARGET_INVALID_DEAD + if(!possible_target.key) + return TARGET_INVALID_NOCKEY + if(possible_target.current) + var/turf/current_location = get_turf(possible_target.current) + if(current_location && !is_level_reachable(current_location.z)) + return TARGET_INVALID_UNREACHABLE + if(isgolem(possible_target.current)) + return TARGET_INVALID_GOLEM + if(possible_target.offstation_role) + return TARGET_INVALID_EVENT + + +/datum/objective/proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if(is_invalid_target(possible_target)) + continue + + possible_targets += possible_target + + if(possible_targets.len > 0) + target = pick(possible_targets) + +/datum/objective/assassinate + martyr_compatible = 1 + +/datum/objective/assassinate/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/assassinate/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 1 + if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + if(!target.current.ckey) + return 1 + return 0 + return 1 + + +/datum/objective/mutiny + martyr_compatible = 1 + +/datum/objective/mutiny/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/mutiny/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) + return 1 + var/turf/T = get_turf(target.current) + if(T && !is_station_level(T.z)) //If they leave the station they count as dead for this + return 1 + return 0 + return 1 + +/datum/objective/maroon + martyr_compatible = 1 + +/datum/objective/maroon/find_target() + ..() + if(target && target.current) + explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/maroon/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 1 + if(!target.current.ckey) + return 1 + if(issilicon(target.current)) + return 1 + if(isbrain(target.current)) + return 1 + var/turf/T = get_turf(target.current) + if(is_admin_level(T.z)) + return 0 + return 1 + return 1 + + +/datum/objective/debrain //I want braaaainssss + martyr_compatible = 0 + +/datum/objective/debrain/find_target() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/debrain/check_completion() + if(!target)//If it's a free objective. + return 1 + if(!owner.current || owner.current.stat == DEAD) + return 0 + if(!target.current || !isbrain(target.current)) + return 0 + var/atom/A = target.current + while(A.loc) //check to see if the brainmob is on our person + A = A.loc + if(A == owner.current) + return 1 + return 0 + + +/datum/objective/protect //The opposite of killing a dude. + martyr_compatible = 1 + +/datum/objective/protect/find_target() + ..() + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/protect/check_completion() + if(!target) //If it's a free objective. + return 1 + if(target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 0 + if(issilicon(target.current)) + return 0 + if(isbrain(target.current)) + return 0 + return 1 + return 0 + +/datum/objective/protect/mindslave //subytpe for mindslave implants + + +/datum/objective/hijack + martyr_compatible = 0 //Technically you won't get both anyway. + explanation_text = "Hijack the shuttle by escaping on it with no loyalist Nanotrasen crew on board and free. \ + Syndicate agents, other enemies of Nanotrasen, cyborgs, pets, and cuffed/restrained hostages may be allowed on the shuttle alive." + +/datum/objective/hijack/check_completion() + if(!owner.current || owner.current.stat) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(issilicon(owner.current)) + return 0 + + var/area/A = get_area(owner.current) + if(SSshuttle.emergency.areaInstance != A) + return 0 + + return SSshuttle.emergency.is_hijacked() + +/datum/objective/hijackclone + explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape." + martyr_compatible = 0 + +/datum/objective/hijackclone/check_completion() + if(!owner.current) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + + for(var/mob/living/player in GLOB.player_list) //Make sure nobody else is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(issilicon(player)) + continue + if(get_area(player) == A) + if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) + return 0 + + for(var/mob/living/player in GLOB.player_list) //Make sure at least one of you is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(issilicon(player)) + continue + if(get_area(player) == A) + if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) + return 1 + return 0 + +/datum/objective/block + explanation_text = "Do not allow any lifeforms, be it organic or synthetic to escape on the shuttle alive. AIs, Cyborgs, and pAIs are not considered alive." + martyr_compatible = 1 + +/datum/objective/block/check_completion() + if(!istype(owner.current, /mob/living/silicon)) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(!owner.current) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) + + for(var/mob/living/player in GLOB.player_list) + if(player.type in protected_mobs) + continue + + if(player.mind && player.stat != DEAD) + if(get_area(player) == A) + return 0 + + return 1 + +/datum/objective/escape + explanation_text = "Escape on the shuttle or an escape pod alive and free." + +/datum/objective/escape/check_completion() + if(issilicon(owner.current)) + return 0 + if(isbrain(owner.current)) + return 0 + if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) + return 0 + if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith + return 1 + if(SSticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win + return 1 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + var/turf/location = get_turf(owner.current) + if(!location) + return 0 + + if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph + return 0 + + if(location.onCentcom() || location.onSyndieBase()) + return 1 + + return 0 + + +/datum/objective/escape/escape_with_identity + var/target_real_name // Has to be stored because the target's real_name can change over the course of the round + +/datum/objective/escape/escape_with_identity/find_target() + var/list/possible_targets = list() //Copypasta because NO_DNA races, yay for snowflakes. + for(var/datum/mind/possible_target in SSticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && possible_target.current.client) + var/mob/living/carbon/human/H = possible_target.current + if(!(NO_DNA in H.dna.species.species_traits)) + possible_targets += possible_target + if(possible_targets.len > 0) + target = pick(possible_targets) + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." + else + explanation_text = "Free Objective" + +/datum/objective/escape/escape_with_identity/check_completion() + if(!target_real_name) + return 1 + if(!ishuman(owner.current)) + return 0 + var/mob/living/carbon/human/H = owner.current + if(..()) + if(H.dna.real_name == target_real_name) + if(H.get_id_name()== target_real_name) + return 1 + return 0 + +/datum/objective/die + explanation_text = "Die a glorious death." + +/datum/objective/die/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) + return 1 + if(issilicon(owner.current) && owner.current != owner.original) + return 1 + return 0 + + + +/datum/objective/survive + explanation_text = "Stay alive until the end." + +/datum/objective/survive/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) + return 0 //Brains no longer win survive objectives. --NEO + if(issilicon(owner.current) && owner.current != owner.original) + return 0 + return 1 + +/datum/objective/nuclear + explanation_text = "Destroy the station with a nuclear device." + martyr_compatible = 1 + +/datum/objective/steal + var/datum/theft_objective/steal_target + martyr_compatible = 0 + var/theft_area + +/datum/objective/steal/proc/get_location() + if(steal_target.location_override) + return steal_target.location_override + var/list/obj/item/steal_candidates = get_all_of_type(steal_target.typepath, subtypes = TRUE) + for(var/obj/item/candidate in steal_candidates) + if(!is_admin_level(candidate.loc.z)) + theft_area = get_area(candidate.loc) + return "[theft_area]" + return "an unknown area" + +/datum/objective/steal/find_target() + var/loop=50 + while(!steal_target && loop > 0) + loop-- + var/thefttype = pick(GLOB.potential_theft_objectives) + var/datum/theft_objective/O = new thefttype + if(owner.assigned_role in O.protected_jobs) + continue + if(O in owner.targets) + continue + if(O.flags & 2) + continue + steal_target = O + + explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. " + if(islist(O.protected_jobs) && O.protected_jobs.len) + explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." + return + explanation_text = "Free Objective." + + +/datum/objective/steal/proc/select_target() + var/list/possible_items_all = GLOB.potential_theft_objectives+"custom" + var/new_target = input("Select target:", "Objective target", null) as null|anything in possible_items_all + if(!new_target) return + if(new_target == "custom") + var/datum/theft_objective/O=new + O.typepath = input("Select type:","Type") as null|anything in typesof(/obj/item) + if(!O.typepath) return + var/tmp_obj = new O.typepath + var/custom_name = tmp_obj:name + qdel(tmp_obj) + O.name = sanitize(copytext(input("Enter target name:", "Objective target", custom_name) as text|null,1,MAX_NAME_LEN)) + if(!O.name) return + steal_target = O + explanation_text = "Steal [O.name]." + else + steal_target = new new_target + explanation_text = "Steal [steal_target.name]." + return steal_target + +/datum/objective/steal/check_completion() + if(!steal_target) + return 1 // Free Objective + + var/list/all_items = owner.current.GetAllContents() + + for(var/obj/I in all_items) + if(istype(I, steal_target.typepath)) + return steal_target.check_special_completion(I) + if(I.type in steal_target.altitems) + return steal_target.check_special_completion(I) + + +/datum/objective/steal/exchange + martyr_compatible = 0 + +/datum/objective/steal/exchange/proc/set_faction(var/faction,var/otheragent) + target = otheragent + var/datum/theft_objective/unique/targetinfo + if(faction == "red") + targetinfo = new /datum/theft_objective/unique/docs_blue + else if(faction == "blue") + targetinfo = new /datum/theft_objective/unique/docs_red + explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" + steal_target = targetinfo + +/datum/objective/steal/exchange/backstab +/datum/objective/steal/exchange/backstab/set_faction(var/faction) + var/datum/theft_objective/unique/targetinfo + if(faction == "red") + targetinfo = new /datum/theft_objective/unique/docs_red + else if(faction == "blue") + targetinfo = new /datum/theft_objective/unique/docs_blue + explanation_text = "Do not give up or lose [targetinfo.name]." + steal_target = targetinfo + +/datum/objective/download +/datum/objective/download/proc/gen_amount_goal() + target_amount = rand(10,20) + explanation_text = "Download [target_amount] research levels." + return target_amount + + +/datum/objective/download/check_completion() + return 0 + + + +/datum/objective/capture +/datum/objective/capture/proc/gen_amount_goal() + target_amount = rand(5,10) + explanation_text = "Accumulate [target_amount] capture points." + return target_amount + + +/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth. + return 0 + + + + +/datum/objective/absorb +/datum/objective/absorb/proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6) + target_amount = rand (lowbound,highbound) + if(SSticker) + var/n_p = 1 //autowin + if(SSticker.current_state == GAME_STATE_SETTING_UP) + for(var/mob/new_player/P in GLOB.player_list) + if(P.client && P.ready && P.mind != owner) + if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. + continue + n_p++ + else if(SSticker.current_state == GAME_STATE_PLAYING) + for(var/mob/living/carbon/human/P in GLOB.player_list) + if(NO_DNA in P.dna.species.species_traits) + continue + if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner) + n_p++ + target_amount = min(target_amount, n_p) + + explanation_text = "Absorb [target_amount] compatible genomes." + return target_amount + +/datum/objective/absorb/check_completion() + if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) + return 1 + else + return 0 + +/datum/objective/destroy + martyr_compatible = 1 + var/target_real_name + +/datum/objective/destroy/find_target() + var/list/possible_targets = active_ais(1) + var/mob/living/silicon/ai/target_ai = pick(possible_targets) + target = target_ai.mind + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Destroy [target_real_name], the AI." + else + explanation_text = "Free Objective" + return target + +/datum/objective/destroy/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey) + return 1 + return 0 + return 1 + +/datum/objective/steal_five_of_type + explanation_text = "Steal at least five items!" + var/list/wanted_items = list() + +/datum/objective/steal_five_of_type/New() + ..() + wanted_items = typecacheof(wanted_items) + +/datum/objective/steal_five_of_type/check_completion() + var/stolen_count = 0 + if(!isliving(owner.current)) + return FALSE + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for wanted items + if(is_type_in_typecache(I, wanted_items)) + stolen_count++ + return stolen_count >= 5 + +/datum/objective/steal_five_of_type/summon_guns + explanation_text = "Steal at least five guns!" + wanted_items = list(/obj/item/gun) + +/datum/objective/steal_five_of_type/summon_magic + explanation_text = "Steal at least five magical artefacts!" + wanted_items = list() + +/datum/objective/steal_five_of_type/summon_magic/New() + wanted_items = GLOB.summoned_magic_objectives + ..() + +/datum/objective/steal_five_of_type/summon_magic/check_completion() + var/stolen_count = 0 + if(!isliving(owner.current)) + return FALSE + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for wanted items + if(istype(I, /obj/item/spellbook) && !istype(I, /obj/item/spellbook/oneuse)) + var/obj/item/spellbook/spellbook = I + if(spellbook.uses) //if the book still has powers... + stolen_count++ //it counts. nice. + if(istype(I, /obj/item/spellbook/oneuse)) + var/obj/item/spellbook/oneuse/oneuse = I + if(!oneuse.used) + stolen_count++ + else if(is_type_in_typecache(I, wanted_items)) + stolen_count++ + return stolen_count >= 5 + +/datum/objective/blood +/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400) + target_amount = rand(low,high) + target_amount = round(round(target_amount/5)*5) + explanation_text = "Accumulate at least [target_amount] total units of blood." + return target_amount + +/datum/objective/blood/check_completion() + if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount) + return 1 + else + return 0 + +// /vg/; Vox Inviolate for humans :V +/datum/objective/minimize_casualties + explanation_text = "Minimise casualties." +/datum/objective/minimize_casualties/check_completion() + if(owner.kills.len>5) return 0 + return 1 + +//Vox heist objectives. + +/datum/objective/heist +/datum/objective/heist/proc/choose_target() + return + +/datum/objective/heist/kidnap +/datum/objective/heist/kidnap/choose_target() + var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer") + var/list/possible_targets = list() + var/list/priority_targets = list() + + for(var/datum/mind/possible_target in SSticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && (possible_target.assigned_role != possible_target.special_role) && !possible_target.offstation_role) + possible_targets += possible_target + for(var/role in roles) + if(possible_target.assigned_role == role) + priority_targets += possible_target + continue + + if(priority_targets.len > 0) + target = pick(priority_targets) + else if(possible_targets.len > 0) + target = pick(possible_targets) + + if(target && target.current) + explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/heist/kidnap/check_completion() + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + + var/area/shuttle/vox/A = locate() //stupid fucking hardcoding + var/area/vox_station/B = locate() //but necessary + + for(var/mob/living/carbon/human/M in A) + if(target.current == M) + return 1 + for(var/mob/living/carbon/human/M in B) + if(target.current == M) + return 1 + else + return 0 + +/datum/objective/heist/loot +/datum/objective/heist/loot/choose_target() + var/loot = "an object" + switch(rand(1,8)) + if(1) + target = /obj/structure/particle_accelerator + target_amount = 6 + loot = "a complete particle accelerator" + if(2) + target = /obj/machinery/the_singularitygen + target_amount = 1 + loot = "a gravitational singularity generator" + if(3) + target = /obj/machinery/power/emitter + target_amount = 4 + loot = "four emitters" + if(4) + target = /obj/machinery/nuclearbomb + target_amount = 1 + loot = "a nuclear bomb" + if(5) + target = /obj/item/gun + target_amount = 6 + loot = "six guns. Tasers and other non-lethal guns are acceptable" + if(6) + target = /obj/item/gun/energy + target_amount = 4 + loot = "four energy guns" + if(7) + target = /obj/item/gun/energy/laser + target_amount = 2 + loot = "two laser guns" + if(8) + target = /obj/item/gun/energy/ionrifle + target_amount = 1 + loot = "an ion gun" + + explanation_text = "We are lacking in hardware. Steal or trade [loot]." + +/datum/objective/heist/loot/check_completion() + var/total_amount = 0 + + for(var/obj/O in locate(/area/shuttle/vox)) + if(istype(O, target)) + total_amount++ + for(var/obj/I in O.contents) + if(istype(I, target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + for(var/obj/O in locate(/area/vox_station)) + if(istype(O, target)) + total_amount++ + for(var/obj/I in O.contents) + if(istype(I, target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + if(raider.current) + for(var/obj/O in raider.current.get_contents()) + if(istype(O,target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + return 0 + +/datum/objective/heist/salvage +/datum/objective/heist/salvage/choose_target() + switch(rand(1,8)) + if(1) + target = "metal" + target_amount = 300 + if(2) + target = "glass" + target_amount = 200 + if(3) + target = "plasteel" + target_amount = 100 + if(4) + target = "solid plasma" + target_amount = 100 + if(5) + target = "silver" + target_amount = 50 + if(6) + target = "gold" + target_amount = 20 + if(7) + target = "uranium" + target_amount = 20 + if(8) + target = "diamond" + target_amount = 20 + + explanation_text = "Ransack or trade with the station and escape with [target_amount] [target]." + +/datum/objective/heist/salvage/check_completion() + var/total_amount = 0 + + for(var/obj/item/O in locate(/area/shuttle/vox)) + var/obj/item/stack/sheet/S + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + S = O + total_amount += S.get_amount() + + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/sheet)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + for(var/obj/item/O in locate(/area/vox_station)) + var/obj/item/stack/sheet/S + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + S = O + total_amount += S.get_amount() + + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/sheet)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + if(raider.current) + for(var/obj/item/O in raider.current.get_contents()) + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + var/obj/item/stack/sheet/S = O + total_amount += S.get_amount() + + if(total_amount >= target_amount) return 1 + return 0 + + +/datum/objective/heist/inviolate_crew + explanation_text = "Do not leave any Vox behind, alive or dead." + +/datum/objective/heist/inviolate_crew/check_completion() + var/datum/game_mode/heist/H = SSticker.mode + if(H.is_raider_crew_safe()) + return 1 + return 0 + +/datum/objective/heist/inviolate_death + explanation_text = "Follow the Inviolate. Minimise death and loss of resources." + +/datum/objective/heist/inviolate_death/check_completion() + var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. + var/vox_total_kills = 0 + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills + + if(vox_total_kills > vox_allowed_kills) return 0 + return 1 + + +// Traders +// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. + +/datum/objective/trade/proc/choose_target() + return + +/datum/objective/trade/plasma/choose_target() + explanation_text = "Acquire at least 15 sheets of plasma through trade." + +/datum/objective/trade/credits/choose_target() + explanation_text = "Acquire at least 10,000 credits through trade." + +//wizard + +/datum/objective/wizchaos + explanation_text = "Wreak havoc upon the station as much you can. Send those wandless Nanotrasen scum a message!" + completed = 1 diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 9f7e14ccef26..00ce24b6c467 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,449 +1,450 @@ -// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), -// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) -// nothing else needs to be done, as that proc will check if they are a valid target. -// Just make sure the converter is a head before you call it! -// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), -// this will also check they're not a head, so it can just be called freely -// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. - -/datum/game_mode - var/list/datum/mind/head_revolutionaries = list() - var/list/datum/mind/revolutionaries = list() - -/datum/game_mode/revolution - name = "revolution" - config_tag = "revolution" - restricted_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician") - required_players = 20 - required_enemies = 1 - recommended_enemies = 3 - - var/finished = 0 - var/check_counter = 0 - var/max_headrevs = 3 - var/list/datum/mind/heads_to_kill = list() - var/list/possible_revolutionaries = list() - -/////////////////////////// -//Announces the game type// -/////////////////////////// -/datum/game_mode/revolution/announce() - to_chat(world, "The current game mode is - Revolution!") - to_chat(world, "Some crewmembers are attempting to start a revolution!
    \nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
    \nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
    ") - - -/////////////////////////////////////////////////////////////////////////////// -//Gets the round setup, cancelling if there's not enough players at the start// -/////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/pre_setup() - possible_revolutionaries = get_players_for_role(ROLE_REV) - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - - for(var/i=1 to max_headrevs) - if(possible_revolutionaries.len==0) - break - var/datum/mind/lenin = pick(possible_revolutionaries) - possible_revolutionaries -= lenin - head_revolutionaries += lenin - lenin.restricted_roles = restricted_jobs - - if(head_revolutionaries.len < required_enemies) - return FALSE - - return TRUE - - -/datum/game_mode/revolution/post_setup() - var/list/heads = get_living_heads() - var/list/sec = get_living_sec() - var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) - - while(weighted_score < head_revolutionaries.len) //das vi danya - var/datum/mind/trotsky = pick(head_revolutionaries) - possible_revolutionaries += trotsky - head_revolutionaries -= trotsky - update_rev_icons_removed(trotsky) - - for(var/datum/mind/rev_mind in head_revolutionaries) - log_game("[key_name(rev_mind)] has been selected as a head rev") - for(var/datum/mind/head_mind in heads) - mark_for_death(rev_mind, head_mind) - - addtimer(CALLBACK(src, .proc/equip_revolutionary, rev_mind.current), rand(10, 100)) - - for(var/datum/mind/rev_mind in head_revolutionaries) - greet_revolutionary(rev_mind) - modePlayer += head_revolutionaries - if(SSshuttle) - SSshuttle.emergencyNoEscape = 1 - ..() - - -/datum/game_mode/revolution/process() - check_counter++ - if(check_counter >= 5) - if(!finished) - check_heads() - SSticker.mode.check_win() - check_counter = 0 - return FALSE - - -/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) - var/list/heads = get_living_heads() - for(var/datum/mind/head_mind in heads) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - -/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) - var/obj_count = 1 - update_rev_icons_added(rev_mind) - if(you_are) - to_chat(rev_mind.current, "You are a member of the revolutionaries' leadership!") - for(var/datum/objective/objective in rev_mind.objectives) - to_chat(rev_mind.current, "Objective #[obj_count]: [objective.explanation_text]") - rev_mind.special_role = SPECIAL_ROLE_HEAD_REV - obj_count++ - -///////////////////////////////////////////////////////////////////////////////// -//This are equips the rev heads with their gear, and makes the clown not clumsy// -///////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if(mob.mind) - if(mob.mind.assigned_role == "Clown") - to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - mob.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(mob) - - var/obj/item/flash/T = new(mob) - var/obj/item/toy/crayon/spraycan/R = new(mob) - var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) - - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - var/where2 = mob.equip_in_one_of_slots(C, slots) - mob.equip_in_one_of_slots(R,slots) - - mob.update_icons() - - if(!where2) - to_chat(mob, "The Syndicate were unfortunately unable to get you a chameleon security HUD.") - else - to_chat(mob, "The chameleon security HUD in your [where2] will help you keep track of who is mindshield-implanted, and unable to be recruited.") - - if(!where) - to_chat(mob, "The Syndicate were unfortunately unable to get you a flash.") - else - to_chat(mob, "The flash in your [where] will help you to persuade the crew to join your cause.") - return 1 - -///////////////////////////////// -//Gives head revs their targets// -///////////////////////////////// -/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - heads_to_kill += head_mind - -//////////////////////////////////////////// -//Checks if new heads have joined midround// -//////////////////////////////////////////// -/datum/game_mode/revolution/proc/check_heads() - var/list/heads = get_all_heads() - var/list/sec = get_all_sec() - if(heads_to_kill.len < heads.len) - var/list/new_heads = heads - heads_to_kill - for(var/datum/mind/head_mind in new_heads) - for(var/datum/mind/rev_mind in head_revolutionaries) - mark_for_death(rev_mind, head_mind) - - if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) - latejoin_headrev() - -/////////////////////////////// -//Adds a new headrev midround// -/////////////////////////////// -/datum/game_mode/revolution/proc/latejoin_headrev() - if(revolutionaries) //Head Revs are not in this list - var/list/promotable_revs = list() - for(var/datum/mind/khrushchev in revolutionaries) - if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) - if(ROLE_REV in khrushchev.current.client.prefs.be_special) - promotable_revs += khrushchev - if(promotable_revs.len) - var/datum/mind/stalin = pick(promotable_revs) - revolutionaries -= stalin - head_revolutionaries += stalin - log_game("[key_name(stalin)] has been promoted to a head rev") - equip_revolutionary(stalin.current) - forge_revolutionary_objectives(stalin) - greet_revolutionary(stalin) - -////////////////////////////////////// -//Checks if the revs have won or not// -////////////////////////////////////// -/datum/game_mode/revolution/check_win() - if(check_rev_victory()) - finished = 1 - else if(check_heads_victory()) - finished = 2 - return - -/////////////////////////////// -//Checks if the round is over// -/////////////////////////////// -/datum/game_mode/revolution/check_finished() - if(config.continuous_rounds) - if(finished != 0) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg') - return ..() - if(finished != 0) - return TRUE - else - return ..() - -/////////////////////////////////////////////////// -//Deals with converting players to the revolution// -/////////////////////////////////////////////////// -/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) - if(rev_mind.assigned_role in command_positions) - return 0 - var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted - if(ismindshielded(H)) - return 0 - if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) - return 0 - revolutionaries += rev_mind - if(iscarbon(rev_mind.current)) - var/mob/living/carbon/carbon_mob = rev_mind.current - carbon_mob.Silence(5) - carbon_mob.flash_eyes(1, 1) - rev_mind.current.Stun(5) - to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") - rev_mind.current.create_attack_log("Has been converted to the revolution!") - rev_mind.special_role = SPECIAL_ROLE_REV - update_rev_icons_added(rev_mind) - if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) - replace_jobbanned_player(rev_mind.current, ROLE_REV) - return 1 -////////////////////////////////////////////////////////////////////////////// -//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. -////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) - var/remove_head = 0 - if(beingborged && (rev_mind in head_revolutionaries)) - head_revolutionaries -= rev_mind - remove_head = 1 - - if((rev_mind in revolutionaries) || remove_head) - revolutionaries -= rev_mind - rev_mind.special_role = null - rev_mind.current.create_attack_log("Has renounced the revolution!") - - if(beingborged) - to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") - message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") - - else - rev_mind.current.Paralyse(5) - to_chat(rev_mind.current, "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...") - - update_rev_icons_removed(rev_mind) - for(var/mob/living/M in view(rev_mind.current)) - if(beingborged) - to_chat(M, "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.") - - else - to_chat(M, "[rev_mind.current] looks like [rev_mind.current.p_they()] just remembered [rev_mind.current.p_their()] real allegiance!") - -///////////////////////////////////// -//Adds the rev hud to a new convert// -///////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.join_hud(rev_mind.current) - set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "hudheadrevolutionary" : "hudrevolutionary")) - -///////////////////////////////////////// -//Removes the hud from deconverted revs// -///////////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.leave_hud(rev_mind.current) - set_antag_hud(rev_mind.current, null) - -////////////////////////// -//Checks for rev victory// -////////////////////////// -/datum/game_mode/revolution/proc/check_rev_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - for(var/datum/objective/mutiny/objective in rev_mind.objectives) - if(!(objective.check_completion())) - return FALSE - - return TRUE - -///////////////////////////// -//Checks for a head victory// -///////////////////////////// -/datum/game_mode/revolution/proc/check_heads_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - var/turf/T = get_turf(rev_mind.current) - if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && rev_mind.current.client && T && is_station_level(T.z)) - if(ishuman(rev_mind.current)) - return FALSE - return TRUE - -////////////////////////////////////////////////////////////////////// -//Announces the end of the game with all relavent information stated// -////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/declare_completion() - if(finished == 1) - feedback_set_details("round_end_result","revolution win - heads killed") - to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") - else if(finished == 2) - feedback_set_details("round_end_result","revolution loss - rev heads killed") - to_chat(world, "The heads of staff managed to stop the revolution!") - ..() - return TRUE - -/datum/game_mode/proc/auto_declare_completion_revolution() - var/list/targets = list() - if(head_revolutionaries.len || GAMEMODE_IS_REVOLUTION) - var/num_revs = 0 - var/num_survivors = 0 - for(var/mob/living/carbon/survivor in GLOB.living_mob_list) - if(survivor.ckey) - num_survivors++ - if(survivor.mind) - if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) - num_revs++ - if(num_survivors) - to_chat(world, "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%") // % of loyal crew - var/text = "
    The head revolutionaries were:" - for(var/datum/mind/headrev in head_revolutionaries) - text += printplayer(headrev, 1) - text += "
    " - to_chat(world, text) - - if(revolutionaries.len || GAMEMODE_IS_REVOLUTION) - var/text = "
    The revolutionaries were:" - for(var/datum/mind/rev in revolutionaries) - text += printplayer(rev, 1) - text += "
    " - to_chat(world, text) - - if( head_revolutionaries.len || revolutionaries.len || GAMEMODE_IS_REVOLUTION ) - var/text = "
    The heads of staff were:" - var/list/heads = get_all_heads() - for(var/datum/mind/head in heads) - var/target = (head in targets) - if(target) - text += "Target" - text += printplayer(head, 1) - text += "
    " - to_chat(world, text) - -/datum/game_mode/revolution/set_scoreboard_gvars() - var/foecount = 0 - for(var/datum/mind/M in SSticker.mode.head_revolutionaries) - foecount++ - if(!M || !M.current) - score_opkilled++ - continue - - if(M.current.stat == DEAD) - score_opkilled++ - - else if(M.current.restrained()) - score_arrested++ - - if(foecount == score_arrested) - score_allarrested = 1 - - for(var/mob/living/carbon/human/player in world) - if(player.mind) - var/role = player.mind.assigned_role - if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) - if(player.stat == DEAD) - score_deadcommand++ - - - var/arrestpoints = score_arrested * 1000 - var/killpoints = score_opkilled * 500 - var/comdeadpts = score_deadcommand * 500 - if(score_traitorswon) - score_crewscore -= 10000 - - score_crewscore += arrestpoints - score_crewscore += killpoints - score_crewscore -= comdeadpts - - -/datum/game_mode/revolution/get_scoreboard_stats() - var/foecount = 0 - var/comcount = 0 - var/revcount = 0 - var/loycount = 0 - for(var/datum/mind/M in SSticker.mode:head_revolutionaries) - if(M.current && M.current.stat != DEAD) - foecount++ - for(var/datum/mind/M in SSticker.mode:revolutionaries) - if(M.current && M.current.stat != DEAD) - revcount++ - for(var/mob/living/carbon/human/player in world) - if(player.mind) - var/role = player.mind.assigned_role - if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) - if(player.stat != DEAD) - comcount++ - else - if(player.mind in SSticker.mode.revolutionaries) continue - loycount++ - - for(var/mob/living/silicon/X in world) - if(X.stat != DEAD) - loycount++ - - - var/dat = "" - - dat += "Mode Statistics
    " - dat += "Number of Surviving Revolution Heads: [foecount]
    " - dat += "Number of Surviving Command Staff: [comcount]
    " - dat += "Number of Surviving Revolutionaries: [revcount]
    " - dat += "Number of Surviving Loyal Crew: [loycount]
    " - - dat += "
    " - dat += "Revolution Heads Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " - dat += "All Revolution Heads Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " - - dat += "Revolution Heads Slain: [score_opkilled] ([score_opkilled * 500] Points)
    " - dat += "Command Staff Slain: [score_deadcommand] (-[score_deadcommand * 500] Points)
    " - dat += "Revolution Successful: [score_traitorswon ? "Yes" : "No"] (-[score_traitorswon * 10000] Points)
    " - dat += "
    " - - return dat +// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), +// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) +// nothing else needs to be done, as that proc will check if they are a valid target. +// Just make sure the converter is a head before you call it! +// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), +// this will also check they're not a head, so it can just be called freely +// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. + +/datum/game_mode + var/list/datum/mind/head_revolutionaries = list() + var/list/datum/mind/revolutionaries = list() + +/datum/game_mode/revolution + name = "revolution" + config_tag = "revolution" + restricted_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician") + required_players = 20 + required_enemies = 1 + recommended_enemies = 3 + + var/finished = 0 + var/check_counter = 0 + var/max_headrevs = 3 + var/list/datum/mind/heads_to_kill = list() + var/list/possible_revolutionaries = list() + +/////////////////////////// +//Announces the game type// +/////////////////////////// +/datum/game_mode/revolution/announce() + to_chat(world, "The current game mode is - Revolution!") + to_chat(world, "Some crewmembers are attempting to start a revolution!
    \nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
    \nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
    ") + + +/////////////////////////////////////////////////////////////////////////////// +//Gets the round setup, cancelling if there's not enough players at the start// +/////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/pre_setup() + possible_revolutionaries = get_players_for_role(ROLE_REV) + + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + + for(var/i=1 to max_headrevs) + if(possible_revolutionaries.len==0) + break + var/datum/mind/lenin = pick(possible_revolutionaries) + possible_revolutionaries -= lenin + head_revolutionaries += lenin + lenin.restricted_roles = restricted_jobs + + if(head_revolutionaries.len < required_enemies) + return FALSE + + return TRUE + + +/datum/game_mode/revolution/post_setup() + var/list/heads = get_living_heads() + var/list/sec = get_living_sec() + var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) + + while(weighted_score < head_revolutionaries.len) //das vi danya + var/datum/mind/trotsky = pick(head_revolutionaries) + possible_revolutionaries += trotsky + head_revolutionaries -= trotsky + update_rev_icons_removed(trotsky) + + for(var/datum/mind/rev_mind in head_revolutionaries) + log_game("[key_name(rev_mind)] has been selected as a head rev") + for(var/datum/mind/head_mind in heads) + mark_for_death(rev_mind, head_mind) + + addtimer(CALLBACK(src, .proc/equip_revolutionary, rev_mind.current), rand(10, 100)) + + for(var/datum/mind/rev_mind in head_revolutionaries) + greet_revolutionary(rev_mind) + modePlayer += head_revolutionaries + if(SSshuttle) + SSshuttle.emergencyNoEscape = 1 + ..() + + +/datum/game_mode/revolution/process() + check_counter++ + if(check_counter >= 5) + if(!finished) + check_heads() + SSticker.mode.check_win() + check_counter = 0 + return FALSE + + +/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) + var/list/heads = get_living_heads() + for(var/datum/mind/head_mind in heads) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + +/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) + var/obj_count = 1 + update_rev_icons_added(rev_mind) + if(you_are) + to_chat(rev_mind.current, "You are a member of the revolutionaries' leadership!") + for(var/datum/objective/objective in rev_mind.objectives) + to_chat(rev_mind.current, "Objective #[obj_count]: [objective.explanation_text]") + rev_mind.special_role = SPECIAL_ROLE_HEAD_REV + obj_count++ + +///////////////////////////////////////////////////////////////////////////////// +//This are equips the rev heads with their gear, and makes the clown not clumsy// +///////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if(mob.mind) + if(mob.mind.assigned_role == "Clown") + to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + mob.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(mob) + + var/obj/item/flash/T = new(mob) + var/obj/item/toy/crayon/spraycan/R = new(mob) + var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + var/where2 = mob.equip_in_one_of_slots(C, slots) + mob.equip_in_one_of_slots(R,slots) + + mob.update_icons() + + if(!where2) + to_chat(mob, "The Syndicate were unfortunately unable to get you a chameleon security HUD.") + else + to_chat(mob, "The chameleon security HUD in your [where2] will help you keep track of who is mindshield-implanted, and unable to be recruited.") + + if(!where) + to_chat(mob, "The Syndicate were unfortunately unable to get you a flash.") + else + to_chat(mob, "The flash in your [where] will help you to persuade the crew to join your cause.") + return 1 + +///////////////////////////////// +//Gives head revs their targets// +///////////////////////////////// +/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + heads_to_kill += head_mind + +//////////////////////////////////////////// +//Checks if new heads have joined midround// +//////////////////////////////////////////// +/datum/game_mode/revolution/proc/check_heads() + var/list/heads = get_all_heads() + var/list/sec = get_all_sec() + if(heads_to_kill.len < heads.len) + var/list/new_heads = heads - heads_to_kill + for(var/datum/mind/head_mind in new_heads) + for(var/datum/mind/rev_mind in head_revolutionaries) + mark_for_death(rev_mind, head_mind) + + if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) + latejoin_headrev() + +/////////////////////////////// +//Adds a new headrev midround// +/////////////////////////////// +/datum/game_mode/revolution/proc/latejoin_headrev() + if(revolutionaries) //Head Revs are not in this list + var/list/promotable_revs = list() + for(var/datum/mind/khrushchev in revolutionaries) + if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) + if(ROLE_REV in khrushchev.current.client.prefs.be_special) + promotable_revs += khrushchev + if(promotable_revs.len) + var/datum/mind/stalin = pick(promotable_revs) + revolutionaries -= stalin + head_revolutionaries += stalin + log_game("[key_name(stalin)] has been promoted to a head rev") + equip_revolutionary(stalin.current) + forge_revolutionary_objectives(stalin) + greet_revolutionary(stalin) + +////////////////////////////////////// +//Checks if the revs have won or not// +////////////////////////////////////// +/datum/game_mode/revolution/check_win() + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + +/////////////////////////////// +//Checks if the round is over// +/////////////////////////////// +/datum/game_mode/revolution/check_finished() + if(config.continuous_rounds) + if(finished != 0) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg') + return ..() + if(finished != 0) + return TRUE + else + return ..() + +/////////////////////////////////////////////////// +//Deals with converting players to the revolution// +/////////////////////////////////////////////////// +/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) + if(rev_mind.assigned_role in GLOB.command_positions) + return 0 + var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted + if(ismindshielded(H)) + return 0 + if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) + return 0 + revolutionaries += rev_mind + if(iscarbon(rev_mind.current)) + var/mob/living/carbon/carbon_mob = rev_mind.current + carbon_mob.Silence(5) + carbon_mob.flash_eyes(1, 1) + rev_mind.current.Stun(5) + to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + rev_mind.current.create_attack_log("Has been converted to the revolution!") + rev_mind.current.create_log(CONVERSION_LOG, "converted to the revolution") + rev_mind.special_role = SPECIAL_ROLE_REV + update_rev_icons_added(rev_mind) + if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) + replace_jobbanned_player(rev_mind.current, ROLE_REV) + return 1 +////////////////////////////////////////////////////////////////////////////// +//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. +////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) + var/remove_head = 0 + if(beingborged && (rev_mind in head_revolutionaries)) + head_revolutionaries -= rev_mind + remove_head = 1 + + if((rev_mind in revolutionaries) || remove_head) + revolutionaries -= rev_mind + rev_mind.special_role = null + rev_mind.current.create_attack_log("Has renounced the revolution!") + rev_mind.current.create_log(CONVERSION_LOG, "renounced the revolution") + if(beingborged) + to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") + message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") + + else + rev_mind.current.Paralyse(5) + to_chat(rev_mind.current, "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...") + + update_rev_icons_removed(rev_mind) + for(var/mob/living/M in view(rev_mind.current)) + if(beingborged) + to_chat(M, "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.") + + else + to_chat(M, "[rev_mind.current] looks like [rev_mind.current.p_they()] just remembered [rev_mind.current.p_their()] real allegiance!") + +///////////////////////////////////// +//Adds the rev hud to a new convert// +///////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV] + revhud.join_hud(rev_mind.current) + set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "hudheadrevolutionary" : "hudrevolutionary")) + +///////////////////////////////////////// +//Removes the hud from deconverted revs// +///////////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = GLOB.huds[ANTAG_HUD_REV] + revhud.leave_hud(rev_mind.current) + set_antag_hud(rev_mind.current, null) + +////////////////////////// +//Checks for rev victory// +////////////////////////// +/datum/game_mode/revolution/proc/check_rev_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + for(var/datum/objective/mutiny/objective in rev_mind.objectives) + if(!(objective.check_completion())) + return FALSE + + return TRUE + +///////////////////////////// +//Checks for a head victory// +///////////////////////////// +/datum/game_mode/revolution/proc/check_heads_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + var/turf/T = get_turf(rev_mind.current) + if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && rev_mind.current.client && T && is_station_level(T.z)) + if(ishuman(rev_mind.current)) + return FALSE + return TRUE + +////////////////////////////////////////////////////////////////////// +//Announces the end of the game with all relavent information stated// +////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/declare_completion() + if(finished == 1) + feedback_set_details("round_end_result","revolution win - heads killed") + to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") + else if(finished == 2) + feedback_set_details("round_end_result","revolution loss - rev heads killed") + to_chat(world, "The heads of staff managed to stop the revolution!") + ..() + return TRUE + +/datum/game_mode/proc/auto_declare_completion_revolution() + var/list/targets = list() + if(head_revolutionaries.len || GAMEMODE_IS_REVOLUTION) + var/num_revs = 0 + var/num_survivors = 0 + for(var/mob/living/carbon/survivor in GLOB.living_mob_list) + if(survivor.ckey) + num_survivors++ + if(survivor.mind) + if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) + num_revs++ + if(num_survivors) + to_chat(world, "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%") // % of loyal crew + var/text = "
    The head revolutionaries were:" + for(var/datum/mind/headrev in head_revolutionaries) + text += printplayer(headrev, 1) + text += "
    " + to_chat(world, text) + + if(revolutionaries.len || GAMEMODE_IS_REVOLUTION) + var/text = "
    The revolutionaries were:" + for(var/datum/mind/rev in revolutionaries) + text += printplayer(rev, 1) + text += "
    " + to_chat(world, text) + + if( head_revolutionaries.len || revolutionaries.len || GAMEMODE_IS_REVOLUTION ) + var/text = "
    The heads of staff were:" + var/list/heads = get_all_heads() + for(var/datum/mind/head in heads) + var/target = (head in targets) + if(target) + text += "Target" + text += printplayer(head, 1) + text += "
    " + to_chat(world, text) + +/datum/game_mode/revolution/set_scoreboard_gvars() + var/foecount = 0 + for(var/datum/mind/M in SSticker.mode.head_revolutionaries) + foecount++ + if(!M || !M.current) + GLOB.score_opkilled++ + continue + + if(M.current.stat == DEAD) + GLOB.score_opkilled++ + + else if(M.current.restrained()) + GLOB.score_arrested++ + + if(foecount == GLOB.score_arrested) + GLOB.score_allarrested = 1 + + for(var/mob/living/carbon/human/player in world) + if(player.mind) + var/role = player.mind.assigned_role + if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) + if(player.stat == DEAD) + GLOB.score_deadcommand++ + + + var/arrestpoints = GLOB.score_arrested * 1000 + var/killpoints = GLOB.score_opkilled * 500 + var/comdeadpts = GLOB.score_deadcommand * 500 + if(GLOB.score_traitorswon) + GLOB.score_crewscore -= 10000 + + GLOB.score_crewscore += arrestpoints + GLOB.score_crewscore += killpoints + GLOB.score_crewscore -= comdeadpts + + +/datum/game_mode/revolution/get_scoreboard_stats() + var/foecount = 0 + var/comcount = 0 + var/revcount = 0 + var/loycount = 0 + for(var/datum/mind/M in SSticker.mode:head_revolutionaries) + if(M.current && M.current.stat != DEAD) + foecount++ + for(var/datum/mind/M in SSticker.mode:revolutionaries) + if(M.current && M.current.stat != DEAD) + revcount++ + for(var/mob/living/carbon/human/player in world) + if(player.mind) + var/role = player.mind.assigned_role + if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) + if(player.stat != DEAD) + comcount++ + else + if(player.mind in SSticker.mode.revolutionaries) continue + loycount++ + + for(var/mob/living/silicon/X in world) + if(X.stat != DEAD) + loycount++ + + + var/dat = "" + + dat += "Mode Statistics
    " + dat += "Number of Surviving Revolution Heads: [foecount]
    " + dat += "Number of Surviving Command Staff: [comcount]
    " + dat += "Number of Surviving Revolutionaries: [revcount]
    " + dat += "Number of Surviving Loyal Crew: [loycount]
    " + + dat += "
    " + dat += "Revolution Heads Arrested: [GLOB.score_arrested] ([GLOB.score_arrested * 1000] Points)
    " + dat += "All Revolution Heads Arrested: [GLOB.score_allarrested ? "Yes" : "No"] (Score tripled)
    " + + dat += "Revolution Heads Slain: [GLOB.score_opkilled] ([GLOB.score_opkilled * 500] Points)
    " + dat += "Command Staff Slain: [GLOB.score_deadcommand] (-[GLOB.score_deadcommand * 500] Points)
    " + dat += "Revolution Successful: [GLOB.score_traitorswon ? "Yes" : "No"] (-[GLOB.score_traitorswon * 10000] Points)
    " + dat += "
    " + + return dat diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 216c946fc977..69eaef9051b5 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -1,161 +1,161 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -var/hsboxspawn = 1 -var/list - hrefs = list( - "hsbsuit" = "Suit Up (Space Travel Gear)", - "hsbmetal" = "Spawn 50 Metal", - "hsbglass" = "Spawn 50 Glass", - "hsbairlock" = "Spawn Airlock", - "hsbregulator" = "Spawn Air Regulator", - "hsbfilter" = "Spawn Air Filter", - "hsbcanister" = "Spawn Canister", - "hsbfueltank" = "Spawn Welding Fuel Tank", - "hsbwater tank" = "Spawn Water Tank", - "hsbtoolbox" = "Spawn Toolbox", - "hsbmedkit" = "Spawn Medical Kit") - -mob - var/datum/hSB/sandbox = null - proc - CanBuild() - if(master_mode == "sandbox") - sandbox = new/datum/hSB - sandbox.owner = src.ckey - if(src.client.holder) - sandbox.admin = 1 - verbs += new/mob/proc/sandbox_panel - sandbox_panel() - if(sandbox) - sandbox.update() - -datum/hSB - var/owner = null - var/admin = 0 - proc - update() - var/hsbpanel = "
    h_Sandbox Panel

    " - if(admin) - hsbpanel += "Administration Tools:
    " - hsbpanel += "- Toggle Object Spawning

    " - hsbpanel += "Regular Tools:
    " - for(var/T in hrefs) - hsbpanel += "- [hrefs[T]]
    " - if(hsboxspawn) - hsbpanel += "- Spawn Object

    " - usr << browse(hsbpanel, "window=hsbpanel") - Topic(href, href_list) - if(!(src.owner == usr.ckey)) return - if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. - if(href_list["hsb"]) - switch(href_list["hsb"]) - if("hsbtobj") - if(!admin) return - if(hsboxspawn) - to_chat(world, "Sandbox: [usr.key] has disabled object spawning!") - hsboxspawn = 0 - return - if(!hsboxspawn) - to_chat(world, "Sandbox: [usr.key] has enabled object spawning!") - hsboxspawn = 1 - return - if("hsbsuit") - var/mob/living/carbon/human/P = usr - if(P.wear_suit) - P.wear_suit.loc = P.loc - P.wear_suit.layer = initial(P.wear_suit.layer) - P.wear_suit.plane = initial(P.wear_suit.plane) - P.wear_suit = null - P.wear_suit = new/obj/item/clothing/suit/space(P) - P.wear_suit.layer = ABOVE_HUD_LAYER - P.wear_suit.plane = ABOVE_HUD_PLANE - if(P.head) - P.head.loc = P.loc - P.head.layer = initial(P.head.layer) - P.head.plane = initial(P.head.plane) - P.head = null - P.head = new/obj/item/clothing/head/helmet/space(P) - P.head.layer = ABOVE_HUD_LAYER - P.head.plane = ABOVE_HUD_PLANE - if(P.wear_mask) - P.wear_mask.loc = P.loc - P.wear_mask.layer = initial(P.wear_mask.layer) - P.wear_mask.plane = initial(P.wear_mask.plane) - P.wear_mask = null - P.wear_mask = new/obj/item/clothing/mask/gas(P) - P.wear_mask.layer = ABOVE_HUD_LAYER - P.wear_mask.plane = ABOVE_HUD_PLANE - if(P.back) - P.back.loc = P.loc - P.back.layer = initial(P.back.layer) - P.back.plane = initial(P.back.plane) - P.back = null - P.back = new/obj/item/tank/jetpack(P) - P.back.layer = ABOVE_HUD_LAYER - P.back.plane = ABOVE_HUD_PLANE - P.internal = P.back - if("hsbmetal") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal - hsb.amount = 50 - hsb.loc = usr.loc - if("hsbglass") - var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass - hsb.amount = 50 - hsb.loc = usr.loc - if("hsbairlock") - var/obj/machinery/door/hsb = new/obj/machinery/door/airlock - - //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups - hsb.req_access = list() - var/accesses = get_all_accesses() - for(var/A in accesses) - if(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") - hsb.req_access += A - - hsb.loc = usr.loc - to_chat(usr, "Sandbox: Created an airlock.") - if("hsbcanister") - var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister/) - var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" - if(!(hsbcanister == "Cancel")) - new hsbcanister(usr.loc) - if("hsbfueltank") - //var/obj/hsb = new/obj/weldfueltank - //hsb.loc = usr.loc - if("hsbwatertank") - //var/obj/hsb = new/obj/watertank - //hsb.loc = usr.loc - if("hsbtoolbox") - var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical - for(var/obj/item/radio/T in hsb) - qdel(T) - new/obj/item/crowbar (hsb) - hsb.loc = usr.loc - if("hsbmedkit") - var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular - hsb.loc = usr.loc - if("hsbobj") - if(!hsboxspawn) return - - var/list/selectable = list() - for(var/O in typesof(/obj/item/)) - //Note, these istypes don't work - if(istype(O, /obj/item/gun)) - continue - if(istype(O, /obj/item/assembly)) - continue - if(istype(O, /obj/item/camera)) - continue - if(istype(O, /obj/item/cloaking_device)) - continue - if(istype(O, /obj/item/dummy)) - continue - if(istype(O, /obj/item/melee/energy/sword/saber)) - continue - if(istype(O, /obj/structure)) - continue - selectable += O - - var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" - if(hsbitem != "Cancel") - new hsbitem(usr.loc) +//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 + +// Someone needs to properly path this file, or just delete it since its unticked and was last properly touched >4 years ago +GLOBAL_VAR_INIT(hsboxspawn, 1) +GLOBAL_LIST_INIT(sandbox_hrefs, list( + "hsbsuit" = "Suit Up (Space Travel Gear)", + "hsbmetal" = "Spawn 50 Metal", + "hsbglass" = "Spawn 50 Glass", + "hsbairlock" = "Spawn Airlock", + "hsbregulator" = "Spawn Air Regulator", + "hsbfilter" = "Spawn Air Filter", + "hsbcanister" = "Spawn Canister", + "hsbfueltank" = "Spawn Welding Fuel Tank", + "hsbwater tank" = "Spawn Water Tank", + "hsbtoolbox" = "Spawn Toolbox", + "hsbmedkit" = "Spawn Medical Kit")) + +mob + var/datum/hSB/sandbox = null + proc + CanBuild() + if(master_mode == "sandbox") + sandbox = new/datum/hSB + sandbox.owner = src.ckey + if(src.client.holder) + sandbox.admin = 1 + verbs += new/mob/proc/sandbox_panel + sandbox_panel() + if(sandbox) + sandbox.update() + +datum/hSB + var/owner = null + var/admin = 0 + proc + update() + var/hsbpanel = "
    h_Sandbox Panel

    " + if(admin) + hsbpanel += "Administration Tools:
    " + hsbpanel += "- Toggle Object Spawning

    " + hsbpanel += "Regular Tools:
    " + for(var/T in GLOB.sandbox_hrefs) + hsbpanel += "- [GLOB.sandbox_hrefs[T]]
    " + if(hsboxspawn) + hsbpanel += "- Spawn Object

    " + usr << browse(hsbpanel, "window=hsbpanel") + Topic(href, href_list) + if(!(src.owner == usr.ckey)) return + if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. + if(href_list["hsb"]) + switch(href_list["hsb"]) + if("hsbtobj") + if(!admin) return + if(hsboxspawn) + to_chat(world, "Sandbox: [usr.key] has disabled object spawning!") + hsboxspawn = 0 + return + if(!hsboxspawn) + to_chat(world, "Sandbox: [usr.key] has enabled object spawning!") + hsboxspawn = 1 + return + if("hsbsuit") + var/mob/living/carbon/human/P = usr + if(P.wear_suit) + P.wear_suit.loc = P.loc + P.wear_suit.layer = initial(P.wear_suit.layer) + P.wear_suit.plane = initial(P.wear_suit.plane) + P.wear_suit = null + P.wear_suit = new/obj/item/clothing/suit/space(P) + P.wear_suit.layer = ABOVE_HUD_LAYER + P.wear_suit.plane = ABOVE_HUD_PLANE + if(P.head) + P.head.loc = P.loc + P.head.layer = initial(P.head.layer) + P.head.plane = initial(P.head.plane) + P.head = null + P.head = new/obj/item/clothing/head/helmet/space(P) + P.head.layer = ABOVE_HUD_LAYER + P.head.plane = ABOVE_HUD_PLANE + if(P.wear_mask) + P.wear_mask.loc = P.loc + P.wear_mask.layer = initial(P.wear_mask.layer) + P.wear_mask.plane = initial(P.wear_mask.plane) + P.wear_mask = null + P.wear_mask = new/obj/item/clothing/mask/gas(P) + P.wear_mask.layer = ABOVE_HUD_LAYER + P.wear_mask.plane = ABOVE_HUD_PLANE + if(P.back) + P.back.loc = P.loc + P.back.layer = initial(P.back.layer) + P.back.plane = initial(P.back.plane) + P.back = null + P.back = new/obj/item/tank/jetpack(P) + P.back.layer = ABOVE_HUD_LAYER + P.back.plane = ABOVE_HUD_PLANE + P.internal = P.back + if("hsbmetal") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbglass") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbairlock") + var/obj/machinery/door/hsb = new/obj/machinery/door/airlock + + //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups + hsb.req_access = list() + var/accesses = get_all_accesses() + for(var/A in accesses) + if(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") + hsb.req_access += A + + hsb.loc = usr.loc + to_chat(usr, "Sandbox: Created an airlock.") + if("hsbcanister") + var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister/) + var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" + if(!(hsbcanister == "Cancel")) + new hsbcanister(usr.loc) + if("hsbfueltank") + //var/obj/hsb = new/obj/weldfueltank + //hsb.loc = usr.loc + if("hsbwatertank") + //var/obj/hsb = new/obj/watertank + //hsb.loc = usr.loc + if("hsbtoolbox") + var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical + for(var/obj/item/radio/T in hsb) + qdel(T) + new/obj/item/crowbar (hsb) + hsb.loc = usr.loc + if("hsbmedkit") + var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular + hsb.loc = usr.loc + if("hsbobj") + if(!hsboxspawn) return + + var/list/selectable = list() + for(var/O in typesof(/obj/item/)) + //Note, these istypes don't work + if(istype(O, /obj/item/gun)) + continue + if(istype(O, /obj/item/assembly)) + continue + if(istype(O, /obj/item/camera)) + continue + if(istype(O, /obj/item/cloaking_device)) + continue + if(istype(O, /obj/item/dummy)) + continue + if(istype(O, /obj/item/melee/energy/sword/saber)) + continue + if(istype(O, /obj/structure)) + continue + selectable += O + + var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" + if(hsbitem != "Cancel") + new hsbitem(usr.loc) diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm index 663759c961ca..7b6978f4a1b6 100644 --- a/code/game/gamemodes/sandbox/sandbox.dm +++ b/code/game/gamemodes/sandbox/sandbox.dm @@ -1,21 +1,21 @@ -/datum/game_mode/sandbox - name = "sandbox" - config_tag = "sandbox" - required_players = 0 - - uplink_welcome = "Syndicate Uplink Console:" - uplink_uses = 10 - -/datum/game_mode/sandbox/announce() - to_chat(world, "The current game mode is - Sandbox!") - to_chat(world, "Build your own station with the sandbox-panel command!") - -/datum/game_mode/sandbox/pre_setup() - for(var/mob/M in GLOB.player_list) - M.CanBuild() - return 1 - -/datum/game_mode/sandbox/post_setup() - ..() - if(emergency_shuttle) - emergency_shuttle.no_escape = 1 +/datum/game_mode/sandbox + name = "sandbox" + config_tag = "sandbox" + required_players = 0 + + uplink_welcome = "Syndicate Uplink Console:" + uplink_uses = 10 + +/datum/game_mode/sandbox/announce() + to_chat(world, "The current game mode is - Sandbox!") + to_chat(world, "Build your own station with the sandbox-panel command!") + +/datum/game_mode/sandbox/pre_setup() + for(var/mob/M in GLOB.player_list) + M.CanBuild() + return 1 + +/datum/game_mode/sandbox/post_setup() + ..() + if(emergency_shuttle) + emergency_shuttle.no_escape = 1 diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm index 379b4fbdcd94..e91c0d4f150a 100644 --- a/code/game/gamemodes/scoreboard.dm +++ b/code/game/gamemodes/scoreboard.dm @@ -22,12 +22,12 @@ // Who is alive/dead, who escaped for(var/mob/living/silicon/ai/I in GLOB.mob_list) if(I.stat == DEAD && is_station_level(I.z)) - score_deadaipenalty++ - score_deadcrew++ + GLOB.score_deadaipenalty++ + GLOB.score_deadcrew++ for(var/mob/living/carbon/human/I in GLOB.mob_list) if(I.stat == DEAD && is_station_level(I.z)) - score_deadcrew++ + GLOB.score_deadcrew++ if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) for(var/mob/living/player in GLOB.mob_list) @@ -36,7 +36,7 @@ var/turf/location = get_turf(player.loc) var/area/escape_zone = locate(/area/shuttle/escape) if(location in escape_zone) - score_escapees++ + GLOB.score_escapees++ @@ -53,18 +53,18 @@ if(E.stat != DEAD && location in escape_zone) // Escapee Scores cash_score = get_score_container_worth(E) - if(cash_score > score_richestcash) - score_richestcash = cash_score - score_richestname = E.real_name - score_richestjob = E.job - score_richestkey = E.key + if(cash_score > GLOB.score_richestcash) + GLOB.score_richestcash = cash_score + GLOB.score_richestname = E.real_name + GLOB.score_richestjob = E.job + GLOB.score_richestkey = E.key dmg_score = E.getBruteLoss() + E.getFireLoss() + E.getToxLoss() + E.getOxyLoss() - if(dmg_score > score_dmgestdamage) - score_dmgestdamage = dmg_score - score_dmgestname = E.real_name - score_dmgestjob = E.job - score_dmgestkey = E.key + if(dmg_score > GLOB.score_dmgestdamage) + GLOB.score_dmgestdamage = dmg_score + GLOB.score_dmgestname = E.real_name + GLOB.score_dmgestjob = E.job + GLOB.score_dmgestkey = E.key if(SSticker && SSticker.mode) SSticker.mode.set_scoreboard_gvars() @@ -75,73 +75,73 @@ if(!is_station_level(A.z)) continue for(var/obj/item/stock_parts/cell/C in A.contents) if(C.charge < 2300) - score_powerloss++ //200 charge leeway + GLOB.score_powerloss++ //200 charge leeway // Check how much uncleaned mess is on the station for(var/obj/effect/decal/cleanable/M in world) if(!is_station_level(M.z)) continue if(istype(M, /obj/effect/decal/cleanable/blood/gibs)) - score_mess += 3 + GLOB.score_mess += 3 if(istype(M, /obj/effect/decal/cleanable/blood)) - score_mess += 1 + GLOB.score_mess += 1 if(istype(M, /obj/effect/decal/cleanable/vomit)) - score_mess += 1 + GLOB.score_mess += 1 // Bonus Modifiers //var/traitorwins = score_traitorswon - var/deathpoints = score_deadcrew * 25 //done - var/researchpoints = score_researchdone * 30 - var/eventpoints = score_eventsendured * 50 - var/escapoints = score_escapees * 25 //done - var/harvests = score_stuffharvested * 5 //done - var/shipping = score_stuffshipped * 5 - var/mining = score_oremined * 2 //done - var/meals = score_meals * 5 //done, but this only counts cooked meals, not drinks served - var/power = score_powerloss * 20 + var/deathpoints = GLOB.score_deadcrew * 25 //done + var/researchpoints = GLOB.score_researchdone * 30 + var/eventpoints = GLOB.score_eventsendured * 50 + var/escapoints = GLOB.score_escapees * 25 //done + var/harvests = GLOB.score_stuffharvested * 5 //done + var/shipping = GLOB.score_stuffshipped * 5 + var/mining = GLOB.score_oremined * 2 //done + var/meals = GLOB.score_meals * 5 //done, but this only counts cooked meals, not drinks served + var/power = GLOB.score_powerloss * 20 var/messpoints - if(score_mess != 0) - messpoints = score_mess //done - var/plaguepoints = score_disease * 30 + if(GLOB.score_mess != 0) + messpoints = GLOB.score_mess //done + var/plaguepoints = GLOB.score_disease * 30 // Good Things - score_crewscore += shipping - score_crewscore += harvests - score_crewscore += mining - score_crewscore += researchpoints - score_crewscore += eventpoints - score_crewscore += escapoints + GLOB.score_crewscore += shipping + GLOB.score_crewscore += harvests + GLOB.score_crewscore += mining + GLOB.score_crewscore += researchpoints + GLOB.score_crewscore += eventpoints + GLOB.score_crewscore += escapoints if(power == 0) - score_crewscore += 2500 - score_powerbonus = 1 + GLOB.score_crewscore += 2500 + GLOB.score_powerbonus = 1 - if(score_mess == 0) - score_crewscore += 3000 - score_messbonus = 1 + if(GLOB.score_mess == 0) + GLOB.score_crewscore += 3000 + GLOB.score_messbonus = 1 - score_crewscore += meals - if(score_allarrested) - score_crewscore *= 3 // This needs to be here for the bonus to be applied properly + GLOB.score_crewscore += meals + if(GLOB.score_allarrested) + GLOB.score_crewscore *= 3 // This needs to be here for the bonus to be applied properly - score_crewscore -= deathpoints - if(score_deadaipenalty) - score_crewscore -= 250 - score_crewscore -= power + GLOB.score_crewscore -= deathpoints + if(GLOB.score_deadaipenalty) + GLOB.score_crewscore -= 250 + GLOB.score_crewscore -= power - score_crewscore -= messpoints - score_crewscore -= plaguepoints + GLOB.score_crewscore -= messpoints + GLOB.score_crewscore -= plaguepoints // Show the score - might add "ranks" later to_chat(world, "The crew's final score is:") - to_chat(world, "[score_crewscore]") + to_chat(world, "[GLOB.score_crewscore]") for(var/mob/E in GLOB.player_list) if(E.client && !E.get_preference(DISABLE_SCOREBOARD)) E.scorestats() @@ -178,30 +178,30 @@ General Statistics
    The Good:
    - Useful Items Shipped: [score_stuffshipped] ([score_stuffshipped * 5] Points)
    - Hydroponics Harvests: [score_stuffharvested] ([score_stuffharvested * 5] Points)
    - Ore Mined: [score_oremined] ([score_oremined * 2] Points)
    - Refreshments Prepared: [score_meals] ([score_meals * 5] Points)
    - Research Completed: [score_researchdone] ([score_researchdone * 30] Points)
    "} - if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME) dat += "Shuttle Escapees: [score_escapees] ([score_escapees * 25] Points)
    " - dat += {"Random Events Endured: [score_eventsendured] ([score_eventsendured * 50] Points)
    - Whole Station Powered: [score_powerbonus ? "Yes" : "No"] ([score_powerbonus * 2500] Points)
    - Ultra-Clean Station: [score_mess ? "No" : "Yes"] ([score_messbonus * 3000] Points)

    + Useful Items Shipped: [GLOB.score_stuffshipped] ([GLOB.score_stuffshipped * 5] Points)
    + Hydroponics Harvests: [GLOB.score_stuffharvested] ([GLOB.score_stuffharvested * 5] Points)
    + Ore Mined: [GLOB.score_oremined] ([GLOB.score_oremined * 2] Points)
    + Refreshments Prepared: [GLOB.score_meals] ([GLOB.score_meals * 5] Points)
    + Research Completed: [GLOB.score_researchdone] ([GLOB.score_researchdone * 30] Points)
    "} + if(SSshuttle.emergency.mode == SHUTTLE_ENDGAME) dat += "Shuttle Escapees: [GLOB.score_escapees] ([GLOB.score_escapees * 25] Points)
    " + dat += {"Random Events Endured: [GLOB.score_eventsendured] ([GLOB.score_eventsendured * 50] Points)
    + Whole Station Powered: [GLOB.score_powerbonus ? "Yes" : "No"] ([GLOB.score_powerbonus * 2500] Points)
    + Ultra-Clean Station: [GLOB.score_mess ? "No" : "Yes"] ([GLOB.score_messbonus * 3000] Points)

    The bad:
    - Dead bodies on Station: [score_deadcrew] (-[score_deadcrew * 25] Points)
    - Uncleaned Messes: [score_mess] (-[score_mess] Points)
    - Station Power Issues: [score_powerloss] (-[score_powerloss * 20] Points)
    - Rampant Diseases: [score_disease] (-[score_disease * 30] Points)
    - AI Destroyed: [score_deadaipenalty ? "Yes" : "No"] (-[score_deadaipenalty * 250] Points)

    + Dead bodies on Station: [GLOB.score_deadcrew] (-[GLOB.score_deadcrew * 25] Points)
    + Uncleaned Messes: [GLOB.score_mess] (-[GLOB.score_mess] Points)
    + Station Power Issues: [GLOB.score_powerloss] (-[GLOB.score_powerloss * 20] Points)
    + Rampant Diseases: [GLOB.score_disease] (-[GLOB.score_disease * 30] Points)
    + AI Destroyed: [GLOB.score_deadaipenalty ? "Yes" : "No"] (-[GLOB.score_deadaipenalty * 250] Points)

    The Weird
    - Food Eaten: [score_foodeaten] bites/sips
    - Times a Clown was Abused: [score_clownabuse]

    + Food Eaten: [GLOB.score_foodeaten] bites/sips
    + Times a Clown was Abused: [GLOB.score_clownabuse]

    "} - if(score_escapees) - dat += {"Richest Escapee: [score_richestname], [score_richestjob]: $[num2text(score_richestcash,50)] ([score_richestkey])
    - Most Battered Escapee: [score_dmgestname], [score_dmgestjob]: [score_dmgestdamage] damage ([score_dmgestkey])
    "} + if(GLOB.score_escapees) + dat += {"Richest Escapee: [GLOB.score_richestname], [GLOB.score_richestjob]: $[num2text(GLOB.score_richestcash,50)] ([GLOB.score_richestkey])
    + Most Battered Escapee: [GLOB.score_dmgestname], [GLOB.score_dmgestjob]: [GLOB.score_dmgestdamage] damage ([GLOB.score_dmgestkey])
    "} else if(SSshuttle.emergency.mode <= SHUTTLE_STRANDED) dat += "The station wasn't evacuated!
    " @@ -212,11 +212,11 @@ dat += {"

    - FINAL SCORE: [score_crewscore]
    + FINAL SCORE: [GLOB.score_crewscore]
    "} var/score_rating = "The Aristocrats!" - switch(score_crewscore) + switch(GLOB.score_crewscore) if(-99999 to -50000) score_rating = "Even the Singularity Deserves Better" if(-49999 to -5000) score_rating = "Singularity Fodder" if(-4999 to -1000) score_rating = "You're All Fired" diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 78e6cea4987a..da171113dd0f 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -1,164 +1,164 @@ -/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS, var/good=0) - if(blocksLeft.len==0) - warning("[name]: No more blocks left to assign!") - return 0 - var/assigned = pick(blocksLeft) - blocksLeft.Remove(assigned) - if(good) - good_blocks += assigned - else - bad_blocks += assigned - assigned_blocks[assigned]=name - dna_activity_bounds[assigned]=activity_bounds - //Debug message_admins("[name] assigned to block #[assigned].") -// testing("[name] assigned to block #[assigned].") - return assigned - -/proc/setupgenetics() - - if(prob(50)) - BLOCKADD = rand(-300,300) - if(prob(75)) - DIFFMUT = rand(0,20) - - -//Thanks to nexis for the fancy code -// BITCH I AIN'T DONE YET - - // SE blocks to assign. - var/list/numsToAssign=new() - for(var/i=1;iBecame a thrall") + new_thrall_mind.current.create_log(CONVERSION_LOG, "Became a thrall") new_thrall_mind.current.add_language("Shadowling Hivemind") new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/lesser_shadow_walk(null)) new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadow_vision/thrall(null)) @@ -171,7 +172,7 @@ Made by Xhuis replace_jobbanned_player(new_thrall_mind.current, ROLE_SHADOWLING) if(!victory_warning_announced && (length(shadowling_thralls) >= warning_threshold))//are the slings very close to winning? victory_warning_announced = TRUE //then let's give the station a warning - command_announcement.Announce("Large concentration of psychic bluespace energy detected by long-ranged scanners. Shadowling ascension event imminent. Prevent it at all costs!", "Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + GLOB.command_announcement.Announce("Large concentration of psychic bluespace energy detected by long-ranged scanners. Shadowling ascension event imminent. Prevent it at all costs!", "Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') return 1 /datum/game_mode/proc/remove_thrall(datum/mind/thrall_mind, var/kill = 0) @@ -179,6 +180,7 @@ Made by Xhuis return 0 //If there is no mind, the mind isn't a thrall, or the mind's mob isn't alive, return shadowling_thralls.Remove(thrall_mind) thrall_mind.current.create_attack_log("Dethralled") + thrall_mind.current.create_log(CONVERSION_LOG, "Dethralled") thrall_mind.special_role = null update_shadow_icons_removed(thrall_mind) for(var/obj/effect/proc_holder/spell/S in thrall_mind.spell_list) @@ -236,6 +238,7 @@ Made by Xhuis update_shadow_icons_removed(ling_mind) shadows.Remove(ling_mind) ling_mind.current.create_attack_log("Deshadowlinged") + ling_mind.current.create_log(CONVERSION_LOG, "Deshadowlinged") ling_mind.special_role = null for(var/obj/effect/proc_holder/spell/S in ling_mind.spell_list) ling_mind.RemoveSpell(S) @@ -322,12 +325,12 @@ Made by Xhuis */ /datum/game_mode/proc/update_shadow_icons_added(datum/mind/shadow_mind) - var/datum/atom_hud/antag/shadow_hud = huds[ANTAG_HUD_SHADOW] + var/datum/atom_hud/antag/shadow_hud = GLOB.huds[ANTAG_HUD_SHADOW] shadow_hud.join_hud(shadow_mind.current) set_antag_hud(shadow_mind.current, ((shadow_mind in shadows) ? "hudshadowling" : "hudshadowlingthrall")) /datum/game_mode/proc/update_shadow_icons_removed(datum/mind/shadow_mind) //This should never actually occur, but it's here anyway. - var/datum/atom_hud/antag/shadow_hud = huds[ANTAG_HUD_SHADOW] + var/datum/atom_hud/antag/shadow_hud = GLOB.huds[ANTAG_HUD_SHADOW] shadow_hud.leave_hud(shadow_mind.current) set_antag_hud(shadow_mind.current, null) diff --git a/code/game/gamemodes/shadowling/shadowling_abilities.dm b/code/game/gamemodes/shadowling/shadowling_abilities.dm index 1671c4a03b04..37f7296bd4ed 100644 --- a/code/game/gamemodes/shadowling/shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/shadowling_abilities.dm @@ -716,7 +716,7 @@ if(SSshuttle.emergency.mode == SHUTTLE_CALL) var/more_minutes = 6000 var/timer = SSshuttle.emergency.timeLeft(1) + more_minutes - event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg') + GLOB.event_announcement.Announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 10 minutes and the shuttle is unable to be recalled.", "System Failure", 'sound/misc/notice1.ogg') SSshuttle.emergency.setTimer(timer) SSshuttle.emergency.canRecall = FALSE user.mind.spell_list.Remove(src) //Can only be used once! diff --git a/code/game/gamemodes/shadowling/shadowling_items.dm b/code/game/gamemodes/shadowling/shadowling_items.dm index 431d986bfcd5..8a86715a907b 100644 --- a/code/game/gamemodes/shadowling/shadowling_items.dm +++ b/code/game/gamemodes/shadowling/shadowling_items.dm @@ -82,4 +82,4 @@ flash_protect = -1 vision_flags = SEE_MOBS lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - flags = ABSTRACT | NODROP \ No newline at end of file + flags = ABSTRACT | NODROP diff --git a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm index 514a16174b12..7896a4aab61e 100644 --- a/code/game/gamemodes/shadowling/special_shadowling_abilities.dm +++ b/code/game/gamemodes/shadowling/special_shadowling_abilities.dm @@ -1,5 +1,5 @@ //In here: Hatch and Ascendance -var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "Noaey'gief", "Mii`mahza", "Amerziox", "Gyrg-mylin", "Kanet'pruunance", "Vigistaezian") //Unpronouncable 2: electric boogalo) +GLOBAL_LIST_INIT(possibleShadowlingNames, list("U'ruan", "Y`shej", "Nex", "Hel-uae", "Noaey'gief", "Mii`mahza", "Amerziox", "Gyrg-mylin", "Kanet'pruunance", "Vigistaezian")) //Unpronouncable 2: electric boogalo) /obj/effect/proc_holder/spell/targeted/shadowling_hatch name = "Hatch" desc = "Casts off your disguise." @@ -66,8 +66,8 @@ var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "N H.status_flags = temp_flags sleep(10) playsound(H.loc, 'sound/effects/ghost.ogg', 100, 1) - var/newNameId = pick(possibleShadowlingNames) - possibleShadowlingNames.Remove(newNameId) + var/newNameId = pick(GLOB.possibleShadowlingNames) + GLOB.possibleShadowlingNames.Remove(newNameId) H.real_name = newNameId H.name = user.real_name H.SetStunned(0) diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm index ae131286afdd..c7ab4490f489 100644 --- a/code/game/gamemodes/traitor/traitor.dm +++ b/code/game/gamemodes/traitor/traitor.dm @@ -1,147 +1,147 @@ -/datum/game_mode - // this includes admin-appointed traitors and multitraitors. Easy! - var/list/datum/mind/traitors = list() - var/list/datum/mind/implanter = list() - var/list/datum/mind/implanted = list() - - var/datum/mind/exchange_red - var/datum/mind/exchange_blue - -/datum/game_mode/traitor - name = "traitor" - config_tag = "traitor" - restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Internal Affairs Agent", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - required_players = 0 - required_enemies = 1 - recommended_enemies = 4 - - var/list/datum/mind/pre_traitors = list() - var/traitors_possible = 4 //hard limit on traitors if scaling is turned off - var/const/traitor_scaling_coeff = 5.0 //how much does the amount of players get divided by to determine traitors - var/antag_datum = /datum/antagonist/traitor //what type of antag to create - -/datum/game_mode/traitor/announce() - to_chat(world, "The current game mode is - Traitor!") - to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") - - -/datum/game_mode/traitor/pre_setup() - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR) - - // stop setup if no possible traitors - if(!possible_traitors.len) - return 0 - - var/num_traitors = 1 - - if(config.traitor_scaling) - num_traitors = max(1, round((num_players())/(traitor_scaling_coeff))) - else - num_traitors = max(1, min(num_players(), traitors_possible)) - - for(var/j = 0, j < num_traitors, j++) - if(!possible_traitors.len) - break - var/datum/mind/traitor = pick(possible_traitors) - pre_traitors += traitor - traitor.special_role = SPECIAL_ROLE_TRAITOR - traitor.restricted_roles = restricted_jobs - possible_traitors.Remove(traitor) - - if(!pre_traitors.len) - return 0 - return 1 - - -/datum/game_mode/traitor/post_setup() - for(var/datum/mind/traitor in pre_traitors) - var/datum/antagonist/traitor/new_antag = new antag_datum() - addtimer(CALLBACK(traitor, /datum/mind.proc/add_antag_datum, new_antag), rand(10,100)) - if(!exchange_blue) - exchange_blue = -1 //Block latejoiners from getting exchange objectives - ..() - - -/datum/game_mode/traitor/declare_completion() - ..() - return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder. - -/datum/game_mode/traitor/process() - // Make sure all objectives are processed regularly, so that objectives - // which can be checked mid-round are checked mid-round. - for(var/datum/mind/traitor_mind in traitors) - for(var/datum/objective/objective in traitor_mind.objectives) - objective.check_completion() - return 0 - - -/datum/game_mode/proc/auto_declare_completion_traitor() - if(traitors.len) - var/text = "The traitors were:" - for(var/datum/mind/traitor in traitors) - var/traitorwin = 1 - - text += "
    [traitor.key] was [traitor.name] (" - if(traitor.current) - if(traitor.current.stat == DEAD) - text += "died" - else - text += "survived" - if(traitor.current.real_name != traitor.name) - text += " as [traitor.current.real_name]" - else - text += "body destroyed" - text += ")" - - - var/TC_uses = 0 - var/uplink_true = 0 - var/purchases = "" - for(var/obj/item/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==traitor.key) - TC_uses += H.used_TC - uplink_true=1 - purchases += H.purchase_log - - if(uplink_true) text += " (used [TC_uses] TC) [purchases]" - - - if(traitor.objectives && traitor.objectives.len)//If the traitor had no objectives, don't need to process this. - var/count = 1 - for(var/datum/objective/objective in traitor.objectives) - if(objective.check_completion()) - text += "
    Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("traitor_objective","[objective.type]|SUCCESS") - else - text += "
    Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("traitor_objective","[objective.type]|FAIL") - traitorwin = 0 - count++ - - var/special_role_text - if(traitor.special_role) - special_role_text = lowertext(traitor.special_role) - else - special_role_text = "antagonist" - - - if(traitorwin) - text += "
    The [special_role_text] was successful!" - feedback_add_details("traitor_success","SUCCESS") - else - text += "
    The [special_role_text] has failed!" - feedback_add_details("traitor_success","FAIL") - - var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") - var/responses = jointext(GLOB.syndicate_code_response, ", ") - - text += "

    The code phrases were: [phrases]
    \ - The code responses were: [responses]

    " - - to_chat(world, text) - return 1 +/datum/game_mode + // this includes admin-appointed traitors and multitraitors. Easy! + var/list/datum/mind/traitors = list() + var/list/datum/mind/implanter = list() + var/list/datum/mind/implanted = list() + + var/datum/mind/exchange_red + var/datum/mind/exchange_blue + +/datum/game_mode/traitor + name = "traitor" + config_tag = "traitor" + restricted_jobs = list("Cyborg")//They are part of the AI if he is traitor so are they, they use to get double chances + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Internal Affairs Agent", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + required_players = 0 + required_enemies = 1 + recommended_enemies = 4 + + var/list/datum/mind/pre_traitors = list() + var/traitors_possible = 4 //hard limit on traitors if scaling is turned off + var/const/traitor_scaling_coeff = 5.0 //how much does the amount of players get divided by to determine traitors + var/antag_datum = /datum/antagonist/traitor //what type of antag to create + +/datum/game_mode/traitor/announce() + to_chat(world, "The current game mode is - Traitor!") + to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") + + +/datum/game_mode/traitor/pre_setup() + + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR) + + // stop setup if no possible traitors + if(!possible_traitors.len) + return 0 + + var/num_traitors = 1 + + if(config.traitor_scaling) + num_traitors = max(1, round((num_players())/(traitor_scaling_coeff))) + else + num_traitors = max(1, min(num_players(), traitors_possible)) + + for(var/j = 0, j < num_traitors, j++) + if(!possible_traitors.len) + break + var/datum/mind/traitor = pick(possible_traitors) + pre_traitors += traitor + traitor.special_role = SPECIAL_ROLE_TRAITOR + traitor.restricted_roles = restricted_jobs + possible_traitors.Remove(traitor) + + if(!pre_traitors.len) + return 0 + return 1 + + +/datum/game_mode/traitor/post_setup() + for(var/datum/mind/traitor in pre_traitors) + var/datum/antagonist/traitor/new_antag = new antag_datum() + addtimer(CALLBACK(traitor, /datum/mind.proc/add_antag_datum, new_antag), rand(10,100)) + if(!exchange_blue) + exchange_blue = -1 //Block latejoiners from getting exchange objectives + ..() + + +/datum/game_mode/traitor/declare_completion() + ..() + return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder. + +/datum/game_mode/traitor/process() + // Make sure all objectives are processed regularly, so that objectives + // which can be checked mid-round are checked mid-round. + for(var/datum/mind/traitor_mind in traitors) + for(var/datum/objective/objective in traitor_mind.objectives) + objective.check_completion() + return 0 + + +/datum/game_mode/proc/auto_declare_completion_traitor() + if(traitors.len) + var/text = "The traitors were:" + for(var/datum/mind/traitor in traitors) + var/traitorwin = 1 + + text += "
    [traitor.key] was [traitor.name] (" + if(traitor.current) + if(traitor.current.stat == DEAD) + text += "died" + else + text += "survived" + if(traitor.current.real_name != traitor.name) + text += " as [traitor.current.real_name]" + else + text += "body destroyed" + text += ")" + + + var/TC_uses = 0 + var/uplink_true = 0 + var/purchases = "" + for(var/obj/item/uplink/H in GLOB.world_uplinks) + if(H && H.uplink_owner && H.uplink_owner==traitor.key) + TC_uses += H.used_TC + uplink_true=1 + purchases += H.purchase_log + + if(uplink_true) text += " (used [TC_uses] TC) [purchases]" + + + if(traitor.objectives && traitor.objectives.len)//If the traitor had no objectives, don't need to process this. + var/count = 1 + for(var/datum/objective/objective in traitor.objectives) + if(objective.check_completion()) + text += "
    Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("traitor_objective","[objective.type]|SUCCESS") + else + text += "
    Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("traitor_objective","[objective.type]|FAIL") + traitorwin = 0 + count++ + + var/special_role_text + if(traitor.special_role) + special_role_text = lowertext(traitor.special_role) + else + special_role_text = "antagonist" + + + if(traitorwin) + text += "
    The [special_role_text] was successful!" + feedback_add_details("traitor_success","SUCCESS") + else + text += "
    The [special_role_text] has failed!" + feedback_add_details("traitor_success","FAIL") + + var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") + var/responses = jointext(GLOB.syndicate_code_response, ", ") + + text += "

    The code phrases were: [phrases]
    \ + The code responses were: [responses]

    " + + to_chat(world, text) + return 1 diff --git a/code/game/gamemodes/vampire/traitor_vamp.dm b/code/game/gamemodes/vampire/traitor_vamp.dm index 4794f6755f2d..d69b9a23518e 100644 --- a/code/game/gamemodes/vampire/traitor_vamp.dm +++ b/code/game/gamemodes/vampire/traitor_vamp.dm @@ -45,4 +45,4 @@ forge_vampire_objectives(vampire) greet_vampire(vampire) update_vampire_icons_added(vampire) - ..() \ No newline at end of file + ..() diff --git a/code/game/gamemodes/vampire/vampire.dm b/code/game/gamemodes/vampire/vampire.dm index ac5ddc92f98c..6c22313ef6ea 100644 --- a/code/game/gamemodes/vampire/vampire.dm +++ b/code/game/gamemodes/vampire/vampire.dm @@ -348,6 +348,7 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha SSticker.mode.vampires -= vampire_mind vampire_mind.special_role = null vampire_mind.current.create_attack_log("De-vampired") + vampire_mind.current.create_log(CONVERSION_LOG, "De-vampired") if(vampire_mind.vampire) vampire_mind.vampire.remove_vampire_powers() QDEL_NULL(vampire_mind.vampire) @@ -359,12 +360,12 @@ You are weak to holy things and starlight. Don't go into space and avoid the Cha //prepare for copypaste /datum/game_mode/proc/update_vampire_icons_added(datum/mind/vampire_mind) - var/datum/atom_hud/antag/vamp_hud = huds[ANTAG_HUD_VAMPIRE] + var/datum/atom_hud/antag/vamp_hud = GLOB.huds[ANTAG_HUD_VAMPIRE] vamp_hud.join_hud(vampire_mind.current) set_antag_hud(vampire_mind.current, ((vampire_mind in vampires) ? "hudvampire" : "hudvampirethrall")) /datum/game_mode/proc/update_vampire_icons_removed(datum/mind/vampire_mind) - var/datum/atom_hud/antag/vampire_hud = huds[ANTAG_HUD_VAMPIRE] + var/datum/atom_hud/antag/vampire_hud = GLOB.huds[ANTAG_HUD_VAMPIRE] vampire_hud.leave_hud(vampire_mind.current) set_antag_hud(vampire_mind.current, null) diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm index 6c9674d130ed..032bab0b6e8f 100644 --- a/code/game/gamemodes/vampire/vampire_powers.dm +++ b/code/game/gamemodes/vampire/vampire_powers.dm @@ -397,7 +397,7 @@ /obj/effect/proc_holder/spell/vampire/bats/choose_targets(mob/user = usr) var/list/turf/locs = new - for(var/direction in alldirs) //looking for bat spawns + for(var/direction in GLOB.alldirs) //looking for bat spawns if(locs.len == num_bats) //we found 2 locations and thats all we need break var/turf/T = get_step(usr, direction) //getting a loc in that direction @@ -606,4 +606,4 @@ add_attack_logs(M, src, "Vampire-sired") mind.make_Vampire() revive() - Weaken(20) \ No newline at end of file + Weaken(20) diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm index 0068a20f468f..da7c6d4e1de6 100644 --- a/code/game/gamemodes/wizard/artefact.dm +++ b/code/game/gamemodes/wizard/artefact.dm @@ -226,7 +226,7 @@ user.ghostize(1) /////////////////////Multiverse Blade//////////////////// -var/global/list/multiverse = list() +GLOBAL_LIST_EMPTY(multiverse) /obj/item/multisword name = "multiverse sword" @@ -252,11 +252,11 @@ var/global/list/multiverse = list() /obj/item/multisword/New() ..() - multiverse |= src + GLOB.multiverse |= src /obj/item/multisword/Destroy() - multiverse.Remove(src) + GLOB.multiverse.Remove(src) return ..() /obj/item/multisword/attack(mob/living/M as mob, mob/living/user as mob) //to prevent accidental friendly fire or out and out grief. @@ -303,7 +303,7 @@ var/global/list/multiverse = list() evil = FALSE else cooldown = world.time + cooldown_between_uses - for(var/obj/item/multisword/M in multiverse) + for(var/obj/item/multisword/M in GLOB.multiverse) if(M.assigned == assigned) M.cooldown = cooldown @@ -842,7 +842,7 @@ var/global/list/multiverse = list() user.unset_machine() if("r_leg","l_leg") to_chat(user, "You move the doll's legs around.") - var/turf/T = get_step(target,pick(cardinal)) + var/turf/T = get_step(target,pick(GLOB.cardinal)) target.Move(T) if("r_arm","l_arm") //use active hand on random nearby mob diff --git a/code/game/gamemodes/wizard/raginmages.dm b/code/game/gamemodes/wizard/raginmages.dm index 57a4f4e17de0..cc52b431249f 100644 --- a/code/game/gamemodes/wizard/raginmages.dm +++ b/code/game/gamemodes/wizard/raginmages.dm @@ -145,7 +145,7 @@ /datum/game_mode/wizard/raginmages/proc/makeBody(var/mob/dead/observer/G) if(!G || !G.key) return // Let's not steal someone's soul here - var/mob/living/carbon/human/new_character = new(pick(latejoin)) + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin)) G.client.prefs.copy_to(new_character) new_character.key = G.key return new_character diff --git a/code/game/gamemodes/wizard/rightandwrong.dm b/code/game/gamemodes/wizard/rightandwrong.dm index 5f2dc094724d..3dbd92113993 100644 --- a/code/game/gamemodes/wizard/rightandwrong.dm +++ b/code/game/gamemodes/wizard/rightandwrong.dm @@ -109,6 +109,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) H.mind.add_antag_datum(/datum/antagonist/survivalist/guns) H.create_attack_log("was made into a survivalist, and trusts no one!") + H.create_log(CONVERSION_LOG, "was made into a survivalist") var/gun_type = pick(GLOB.summoned_guns) var/obj/item/gun/G = new gun_type(get_turf(H)) @@ -130,6 +131,7 @@ GLOBAL_VAR_INIT(summon_magic_triggered, FALSE) H.mind.add_antag_datum(/datum/antagonist/survivalist/magic) H.create_attack_log("was made into a survivalist, and trusts no one!") + H.create_log(CONVERSION_LOG, "was made into a survivalist") var/magic_type = pick(GLOB.summoned_magic) var/lucky = FALSE diff --git a/code/game/gamemodes/wizard/soulstone.dm b/code/game/gamemodes/wizard/soulstone.dm index d97ec2af5fd6..634fc8f5a382 100644 --- a/code/game/gamemodes/wizard/soulstone.dm +++ b/code/game/gamemodes/wizard/soulstone.dm @@ -1,381 +1,378 @@ -/obj/item/soulstone - name = "Soul Stone Shard" - icon = 'icons/obj/wizard.dmi' - icon_state = "soulstone" - item_state = "electronic" - desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artifact's power." - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_BELT - origin_tech = "bluespace=4;materials=5" - - var/optional = FALSE //does this soulstone ask the victim whether they want to be turned into a shade - var/usability = FALSE // Can this soul stone be used by anyone, or only cultists/wizards? - var/reusable = TRUE // Can this soul stone be used more than once? - var/spent = FALSE // If the soul stone can only be used once, has it been used? - - var/opt_in = FALSE // for tracking during the 'optional' bit - -/obj/item/soulstone/proc/can_use(mob/living/user) - if(iscultist(user) || iswizard(user) || usability) - return TRUE - - return FALSE - -/obj/item/soulstone/proc/was_used() - if(!reusable) - spent = TRUE - name = "dull [name]" - desc = "A fragment of the legendary treasure known simply as \ - the 'Soul Stone'. The shard lies still, dull and lifeless; \ - whatever spark it once held long extinguished." - -/obj/item/soulstone/anybody - usability = TRUE - -/obj/item/soulstone/anybody/chaplain - name = "mysterious old shard" - reusable = FALSE - optional = TRUE - -/obj/item/soulstone/pickup(mob/living/user) - . = ..() - if(!can_use(user)) - to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.") - user.Dizzy(120) - -//////////////////////////////Capturing//////////////////////////////////////////////////////// -/obj/item/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob) - if(!can_use(user)) - user.Paralyse(5) - to_chat(user, "Your body is wracked with debilitating pain!") - return - - if(spent) - to_chat(user, "There is no power left in the shard.") - return - - if(!ishuman(M)) //If target is not a human - return ..() - - if(M.has_brain_worms()) //Borer stuff - RR - to_chat(user, "This being is corrupted by an alien intelligence and cannot be soul trapped.") - return ..() - - if(jobban_isbanned(M, "cultist") || jobban_isbanned(M, "Syndicate")) - to_chat(user, "A mysterious force prevents you from trapping this being's soul.") - return ..() - - if(iscultist(user) && iscultist(M)) - to_chat(user, "\"Come now, do not capture your fellow's soul.\"") - return ..() - - M.create_attack_log("Has had their soul captured with [src.name] by [key_name(user)]") - user.create_attack_log("Used the [src.name] to capture the soul of [key_name(M)]") - - if(optional) - if(!M.ckey) - to_chat(user, "They have no soul!") - return - - to_chat(user, "You attempt to channel [M]'s soul into [src]. You must give the soul some time to react and stand still...") - - var/mob/player_mob = M - if(M.get_ghost())//in case our player ghosted and we need to throw the alert at their ghost instead - player_mob = M.get_ghost() - var/client/player_client = player_mob.client - to_chat(player_mob, "[user] is trying to capture your soul into [src]! Click the button in the top right of the game window to respond.") - player_client << 'sound/misc/notice2.ogg' - window_flash(player_client) - - var/obj/screen/alert/notify_soulstone/A = player_mob.throw_alert("\ref[src]_soulstone_thingy", /obj/screen/alert/notify_soulstone) - if(player_client.prefs && player_client.prefs.UI_style) - A.icon = ui_style2icon(player_client.prefs.UI_style) - - //pass the stuff to the alert itself - A.stone = src - A.stoner = user.real_name - - //layer shenanigans to make the alert display the soulstone - var/old_layer = layer - var/old_plane = plane - layer = FLOAT_LAYER - plane = FLOAT_PLANE - A.overlays += src - layer = old_layer - plane = old_plane - - //give the victim 10 seconds to respond - sleep(10 SECONDS) - - if(!opt_in) - to_chat(user, "The soul resists your attempts at capturing it!") - return - - opt_in = FALSE - - if(spent)//checking one more time against shenanigans - return - - add_attack_logs(user, M, "Stolestone'd with [name]") - transfer_soul("VICTIM", M, user) - return - -///////////////////Options for using captured souls/////////////////////////////////////// -/obj/item/soulstone/attack_self(mob/user) - if(!in_range(src, user)) - return - - if(!can_use(user)) - user.Paralyse(5) - to_chat(user, "Your body is wracked with debilitating pain!") - return - - user.set_machine(src) - var/dat = "Soul Stone
    " - for(var/mob/living/simple_animal/shade/A in src) - dat += "Captured Soul: [A.name]
    " - dat += {"Summon Shade"} - dat += "
    " - dat += {" Close"} - user << browse(dat, "window=aicard") - onclose(user, "aicard") - return - -/obj/item/soulstone/Topic(href, href_list) - var/mob/U = usr - if(!in_range(src, U) || U.machine != src || !can_use(usr)) - U << browse(null, "window=aicard") - U.unset_machine() - return - - add_fingerprint(U) - U.set_machine(src) - - switch(href_list["choice"])//Now we switch based on choice. - if("Close") - U << browse(null, "window=aicard") - U.unset_machine() - return - - if("Summon") - for(var/mob/living/simple_animal/shade/A in src) - A.status_flags &= ~GODMODE - A.canmove = 1 - A.forceMove(get_turf(usr)) - A.cancel_camera() - icon_state = "soulstone" - name = initial(name) - if(iswizard(usr) || usability) - to_chat(A, "You have been released from your prison, but you are still bound to [usr.real_name]'s will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") - else if(iscultist(usr)) - to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") - was_used() - attack_self(U) - -///////////////////////////Transferring to constructs///////////////////////////////////////////////////// -/obj/structure/constructshell - name = "empty shell" - icon = 'icons/obj/wizard.dmi' - icon_state = "construct-cult" - desc = "A wicked machine used by those skilled in magical arts. It is inactive" - -/obj/structure/constructshell/examine(mob/user) - . = ..() - if(in_range(user, src) && (iscultist(user) || iswizard(user) || user.stat == DEAD)) - . += "A construct shell, used to house bound souls from a soulstone." - . += "Placing a soulstone with a soul into this shell allows you to produce your choice of the following:" - . += "An Artificer, which can produce more shells and soulstones, as well as fortifications." - . += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile." - . += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow." - -/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/soulstone)) - var/obj/item/soulstone/SS = O - if(!SS.can_use(user)) - to_chat(user, "An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.") - user.Dizzy(120) - return - SS.transfer_soul("CONSTRUCT",src,user) - SS.was_used() - else - return ..() - -////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// - -/obj/item/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob) - switch(choice) - if("FORCE") - var/obj/item/soulstone/SS = src - var/mob/living/T = target - if(T.client != null) - SS.init_shade(T, U) - else - to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") - T.Paralyse(20) - if(!SS.getCultGhost(T,U)) - T.dust() //If we can't get a ghost, kill the sacrifice anyway. - - if("VICTIM") - var/mob/living/carbon/human/T = target - var/obj/item/soulstone/SS = src - if(T.stat == 0) - to_chat(U, "Capture failed!: Kill or maim the victim first!") - else - if(!T.client_mobs_in_contents?.len) - to_chat(U, "They have no soul!") - else - if(T.client == null) - to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") - SS.getCultGhost(T,U) - else - if(SS.contents.len) - to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") - else - SS.init_shade(T, U, vic = 1) - - if("SHADE") - var/mob/living/simple_animal/shade/T = target - var/obj/item/soulstone/SS = src - if(!SS.can_use(U)) - U.Paralyse(5) - to_chat(U, "Your body is wracked with debilitating pain!") - return - if(T.stat == DEAD) - to_chat(U, "Capture failed!: The shade has already been banished!") - else - if(SS.contents.len) - to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") - else - T.loc = SS //put shade in stone - T.status_flags |= GODMODE - T.canmove = 0 - T.health = T.maxHealth - T.faction |= "\ref[U]" - SS.icon_state = "soulstone2" - to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") - to_chat(U, "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone.") - if("CONSTRUCT") - var/obj/structure/constructshell/T = target - var/obj/item/soulstone/SS = src - var/mob/living/simple_animal/shade/SH = locate() in SS - if(SH) - var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") - switch(construct_class) - if("Juggernaut") - var/mob/living/simple_animal/hostile/construct/armoured/C = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc)) - to_chat(C, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.") - init_construct(C,SH,SS,T) - - if("Wraith") - var/mob/living/simple_animal/hostile/construct/wraith/C = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc)) - to_chat(C, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") - init_construct(C,SH,SS,T) - - if("Artificer") - var/mob/living/simple_animal/hostile/construct/builder/C = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc)) - to_chat(C, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).") - init_construct(C,SH,SS,T) - else - to_chat(U, "Creation failed!: The soul stone is empty! Go kill someone!") - return - -/proc/init_construct(mob/living/simple_animal/hostile/construct/C, mob/living/simple_animal/shade/SH, obj/item/soulstone/SS, obj/structure/constructshell/T) - SH.mind.transfer_to(C) - if(iscultist(C)) - var/datum/action/innate/cultcomm/CC = new() - CC.Grant(C) //We have to grant the cult comms again because they're lost during the mind transfer. - qdel(T) - qdel(SH) - to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - C.cancel_camera() - qdel(SS) - -/proc/makeNewConstruct(var/mob/living/simple_animal/hostile/construct/ctype, var/mob/target, var/mob/stoner = null, cultoverride = 0) - if(jobban_isbanned(target, "cultist") || jobban_isbanned(target, "Syndicate")) - return - var/mob/living/simple_animal/hostile/construct/newstruct = new ctype(get_turf(target)) - newstruct.faction |= "\ref[stoner]" - newstruct.key = target.key - if(stoner && iscultist(stoner) || cultoverride) - if(SSticker.mode.name == "cult") - SSticker.mode:add_cultist(newstruct.mind) - else - SSticker.mode.cult+=newstruct.mind - SSticker.mode.update_cult_icons_added(newstruct.mind) - if(stoner && iswizard(stoner)) - to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - else if(stoner && iscultist(stoner)) - to_chat(newstruct, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.") - else - to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") - newstruct.cancel_camera() - -/obj/item/soulstone/proc/init_shade(mob/living/T, mob/U, vic = 0) - new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton - T.invisibility = 101 - var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = T - flick("dust-h", animation) - qdel(animation) - var/path = get_shade_type() - var/mob/living/simple_animal/shade/S = new path(src) - S.status_flags |= GODMODE //So they won't die inside the stone somehow - S.canmove = 0//Can't move out of the soul stone - S.name = "Shade of [T.real_name]" - S.real_name = "Shade of [T.real_name]" - S.key = T.key - S.cancel_camera() - name = "soulstone: Shade of [T.real_name]" - icon_state = "soulstone2" - if(U) - S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation - if(iswizard(U)) - SSticker.mode.update_wiz_icons_added(S.mind) - S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE - if(iscultist(U)) - SSticker.mode.add_cultist(S.mind, 0) - S.mind.special_role = SPECIAL_ROLE_CULTIST - S.mind.store_memory("Serve the cult's will.") - to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.") - else - S.mind.store_memory("Serve [U.real_name], your creator.") - to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help them succeed in their goals at all costs.") - if(vic && U) - to_chat(U, "Capture successful!: [T.real_name]'s soul has been ripped from [U.p_their()] body and stored within the soul stone.") - if(isrobot(T))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn. - T.dust() - else - for(var/obj/item/W in T) - T.unEquip(W) - qdel(T) - -/obj/item/soulstone/proc/get_shade_type() - return /mob/living/simple_animal/shade/cult - -/obj/item/soulstone/anybody/get_shade_type() - return /mob/living/simple_animal/shade - -/obj/item/soulstone/proc/getCultGhost(mob/living/T, mob/U) - var/mob/dead/observer/chosen_ghost - - for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body - if(ghost.mind && ghost.mind.current == T && ghost.client) - chosen_ghost = ghost - break - - if(!chosen_ghost) //Failing that, we grab a ghost - var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100) - if(consenting_candidates.len) - chosen_ghost = pick(consenting_candidates) - if(!T) - return 0 - if(!chosen_ghost) - to_chat(U, "There were no spirits willing to become a shade.") - return 0 - if(contents.len) //If they used the soulstone on someone else in the meantime - return 0 - T.ckey = chosen_ghost.ckey - init_shade(T, U) - return 1 +/obj/item/soulstone + name = "Soul Stone Shard" + icon = 'icons/obj/wizard.dmi' + icon_state = "soulstone" + item_state = "electronic" + desc = "A fragment of the legendary treasure known simply as the 'Soul Stone'. The shard still flickers with a fraction of the full artifact's power." + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_BELT + origin_tech = "bluespace=4;materials=5" + + var/optional = FALSE //does this soulstone ask the victim whether they want to be turned into a shade + var/usability = FALSE // Can this soul stone be used by anyone, or only cultists/wizards? + var/reusable = TRUE // Can this soul stone be used more than once? + var/spent = FALSE // If the soul stone can only be used once, has it been used? + + var/opt_in = FALSE // for tracking during the 'optional' bit + +/obj/item/soulstone/proc/can_use(mob/living/user) + if(iscultist(user) || iswizard(user) || usability) + return TRUE + + return FALSE + +/obj/item/soulstone/proc/was_used() + if(!reusable) + spent = TRUE + name = "dull [name]" + desc = "A fragment of the legendary treasure known simply as \ + the 'Soul Stone'. The shard lies still, dull and lifeless; \ + whatever spark it once held long extinguished." + +/obj/item/soulstone/anybody + usability = TRUE + +/obj/item/soulstone/anybody/chaplain + name = "mysterious old shard" + reusable = FALSE + optional = TRUE + +/obj/item/soulstone/pickup(mob/living/user) + . = ..() + if(!can_use(user)) + to_chat(user, "An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.") + user.Dizzy(120) + +//////////////////////////////Capturing//////////////////////////////////////////////////////// +/obj/item/soulstone/attack(mob/living/carbon/human/M as mob, mob/user as mob) + if(!can_use(user)) + user.Paralyse(5) + to_chat(user, "Your body is wracked with debilitating pain!") + return + + if(spent) + to_chat(user, "There is no power left in the shard.") + return + + if(!ishuman(M)) //If target is not a human + return ..() + + if(M.has_brain_worms()) //Borer stuff - RR + to_chat(user, "This being is corrupted by an alien intelligence and cannot be soul trapped.") + return ..() + + if(jobban_isbanned(M, "cultist") || jobban_isbanned(M, "Syndicate")) + to_chat(user, "A mysterious force prevents you from trapping this being's soul.") + return ..() + + if(iscultist(user) && iscultist(M)) + to_chat(user, "\"Come now, do not capture your fellow's soul.\"") + return ..() + + if(optional) + if(!M.ckey) + to_chat(user, "They have no soul!") + return + + to_chat(user, "You attempt to channel [M]'s soul into [src]. You must give the soul some time to react and stand still...") + + var/mob/player_mob = M + if(M.get_ghost())//in case our player ghosted and we need to throw the alert at their ghost instead + player_mob = M.get_ghost() + var/client/player_client = player_mob.client + to_chat(player_mob, "[user] is trying to capture your soul into [src]! Click the button in the top right of the game window to respond.") + player_client << 'sound/misc/notice2.ogg' + window_flash(player_client) + + var/obj/screen/alert/notify_soulstone/A = player_mob.throw_alert("\ref[src]_soulstone_thingy", /obj/screen/alert/notify_soulstone) + if(player_client.prefs && player_client.prefs.UI_style) + A.icon = ui_style2icon(player_client.prefs.UI_style) + + //pass the stuff to the alert itself + A.stone = src + A.stoner = user.real_name + + //layer shenanigans to make the alert display the soulstone + var/old_layer = layer + var/old_plane = plane + layer = FLOAT_LAYER + plane = FLOAT_PLANE + A.overlays += src + layer = old_layer + plane = old_plane + + //give the victim 10 seconds to respond + sleep(10 SECONDS) + + if(!opt_in) + to_chat(user, "The soul resists your attempts at capturing it!") + return + + opt_in = FALSE + + if(spent)//checking one more time against shenanigans + return + + add_attack_logs(user, M, "Stolestone'd with [name]") + transfer_soul("VICTIM", M, user) + return + +///////////////////Options for using captured souls/////////////////////////////////////// +/obj/item/soulstone/attack_self(mob/user) + if(!in_range(src, user)) + return + + if(!can_use(user)) + user.Paralyse(5) + to_chat(user, "Your body is wracked with debilitating pain!") + return + + user.set_machine(src) + var/dat = "Soul Stone
    " + for(var/mob/living/simple_animal/shade/A in src) + dat += "Captured Soul: [A.name]
    " + dat += {"Summon Shade"} + dat += "
    " + dat += {" Close"} + user << browse(dat, "window=aicard") + onclose(user, "aicard") + return + +/obj/item/soulstone/Topic(href, href_list) + var/mob/U = usr + if(!in_range(src, U) || U.machine != src || !can_use(usr)) + U << browse(null, "window=aicard") + U.unset_machine() + return + + add_fingerprint(U) + U.set_machine(src) + + switch(href_list["choice"])//Now we switch based on choice. + if("Close") + U << browse(null, "window=aicard") + U.unset_machine() + return + + if("Summon") + for(var/mob/living/simple_animal/shade/A in src) + A.status_flags &= ~GODMODE + A.canmove = 1 + A.forceMove(get_turf(usr)) + A.cancel_camera() + icon_state = "soulstone" + name = initial(name) + if(iswizard(usr) || usability) + to_chat(A, "You have been released from your prison, but you are still bound to [usr.real_name]'s will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") + else if(iscultist(usr)) + to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help [usr.p_them()] succeed in [usr.p_their()] goals at all costs.") + was_used() + attack_self(U) + +///////////////////////////Transferring to constructs///////////////////////////////////////////////////// +/obj/structure/constructshell + name = "empty shell" + icon = 'icons/obj/wizard.dmi' + icon_state = "construct-cult" + desc = "A wicked machine used by those skilled in magical arts. It is inactive" + +/obj/structure/constructshell/examine(mob/user) + . = ..() + if(in_range(user, src) && (iscultist(user) || iswizard(user) || user.stat == DEAD)) + . += "A construct shell, used to house bound souls from a soulstone." + . += "Placing a soulstone with a soul into this shell allows you to produce your choice of the following:" + . += "An Artificer, which can produce more shells and soulstones, as well as fortifications." + . += "A Wraith, which does high damage and can jaunt through walls, though it is quite fragile." + . += "A Juggernaut, which is very hard to kill and can produce temporary walls, but is slow." + +/obj/structure/constructshell/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/soulstone)) + var/obj/item/soulstone/SS = O + if(!SS.can_use(user)) + to_chat(user, "An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.") + user.Dizzy(120) + return + SS.transfer_soul("CONSTRUCT",src,user) + SS.was_used() + else + return ..() + +////////////////////////////Proc for moving soul in and out off stone////////////////////////////////////// + +/obj/item/proc/transfer_soul(var/choice as text, var/target, var/mob/U as mob) + switch(choice) + if("FORCE") + var/obj/item/soulstone/SS = src + var/mob/living/T = target + if(T.client != null) + SS.init_shade(T, U) + else + to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") + T.Paralyse(20) + if(!SS.getCultGhost(T,U)) + T.dust() //If we can't get a ghost, kill the sacrifice anyway. + + if("VICTIM") + var/mob/living/carbon/human/T = target + var/obj/item/soulstone/SS = src + if(T.stat == 0) + to_chat(U, "Capture failed!: Kill or maim the victim first!") + else + if(!T.client_mobs_in_contents?.len) + to_chat(U, "They have no soul!") + else + if(T.client == null) + to_chat(U, "Capture failed!: The soul has already fled its mortal frame. You attempt to bring it back...") + SS.getCultGhost(T,U) + else + if(SS.contents.len) + to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") + else + SS.init_shade(T, U, vic = 1) + + if("SHADE") + var/mob/living/simple_animal/shade/T = target + var/obj/item/soulstone/SS = src + if(!SS.can_use(U)) + U.Paralyse(5) + to_chat(U, "Your body is wracked with debilitating pain!") + return + if(T.stat == DEAD) + to_chat(U, "Capture failed!: The shade has already been banished!") + else + if(SS.contents.len) + to_chat(U, "Capture failed!: The soul stone is full! Use or free an existing soul to make room.") + else + T.loc = SS //put shade in stone + T.status_flags |= GODMODE + T.canmove = 0 + T.health = T.maxHealth + T.faction |= "\ref[U]" + SS.icon_state = "soulstone2" + to_chat(T, "Your soul has been recaptured by the soul stone, its arcane energies are reknitting your ethereal form") + to_chat(U, "Capture successful!: [T.name]'s has been recaptured and stored within the soul stone.") + if("CONSTRUCT") + var/obj/structure/constructshell/T = target + var/obj/item/soulstone/SS = src + var/mob/living/simple_animal/shade/SH = locate() in SS + if(SH) + var/construct_class = alert(U, "Please choose which type of construct you wish to create.",,"Juggernaut","Wraith","Artificer") + switch(construct_class) + if("Juggernaut") + var/mob/living/simple_animal/hostile/construct/armoured/C = new /mob/living/simple_animal/hostile/construct/armoured (get_turf(T.loc)) + to_chat(C, "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, create shield walls and even deflect energy weapons, and rip apart enemies and walls alike.") + init_construct(C,SH,SS,T) + + if("Wraith") + var/mob/living/simple_animal/hostile/construct/wraith/C = new /mob/living/simple_animal/hostile/construct/wraith (get_turf(T.loc)) + to_chat(C, "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls.") + init_construct(C,SH,SS,T) + + if("Artificer") + var/mob/living/simple_animal/hostile/construct/builder/C = new /mob/living/simple_animal/hostile/construct/builder (get_turf(T.loc)) + to_chat(C, "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, use magic missile, repair allied constructs (by clicking on them), and most important of all create new constructs (Use your Artificer spell to summon a new construct shell and Summon Soulstone to create a new soulstone).") + init_construct(C,SH,SS,T) + else + to_chat(U, "Creation failed!: The soul stone is empty! Go kill someone!") + return + +/proc/init_construct(mob/living/simple_animal/hostile/construct/C, mob/living/simple_animal/shade/SH, obj/item/soulstone/SS, obj/structure/constructshell/T) + SH.mind.transfer_to(C) + if(iscultist(C)) + var/datum/action/innate/cultcomm/CC = new() + CC.Grant(C) //We have to grant the cult comms again because they're lost during the mind transfer. + qdel(T) + qdel(SH) + to_chat(C, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + C.cancel_camera() + qdel(SS) + +/proc/makeNewConstruct(var/mob/living/simple_animal/hostile/construct/ctype, var/mob/target, var/mob/stoner = null, cultoverride = 0) + if(jobban_isbanned(target, "cultist") || jobban_isbanned(target, "Syndicate")) + return + var/mob/living/simple_animal/hostile/construct/newstruct = new ctype(get_turf(target)) + newstruct.faction |= "\ref[stoner]" + newstruct.key = target.key + if(stoner && iscultist(stoner) || cultoverride) + if(SSticker.mode.name == "cult") + SSticker.mode:add_cultist(newstruct.mind) + else + SSticker.mode.cult+=newstruct.mind + SSticker.mode.update_cult_icons_added(newstruct.mind) + if(stoner && iswizard(stoner)) + to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + else if(stoner && iscultist(stoner)) + to_chat(newstruct, "You are still bound to serve the cult, follow their orders and help them complete their goals at all costs.") + else + to_chat(newstruct, "You are still bound to serve your creator, follow their orders and help them complete their goals at all costs.") + newstruct.cancel_camera() + +/obj/item/soulstone/proc/init_shade(mob/living/T, mob/U, vic = 0) + new /obj/effect/decal/remains/human(T.loc) //Spawns a skeleton + T.invisibility = 101 + var/atom/movable/overlay/animation = new /atom/movable/overlay( T.loc ) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = T + flick("dust-h", animation) + qdel(animation) + var/path = get_shade_type() + var/mob/living/simple_animal/shade/S = new path(src) + S.status_flags |= GODMODE //So they won't die inside the stone somehow + S.canmove = 0//Can't move out of the soul stone + S.name = "Shade of [T.real_name]" + S.real_name = "Shade of [T.real_name]" + S.key = T.key + S.cancel_camera() + name = "soulstone: Shade of [T.real_name]" + icon_state = "soulstone2" + if(U) + S.faction |= "\ref[U]" //Add the master as a faction, allowing inter-mob cooperation + if(iswizard(U)) + SSticker.mode.update_wiz_icons_added(S.mind) + S.mind.special_role = SPECIAL_ROLE_WIZARD_APPRENTICE + if(iscultist(U)) + SSticker.mode.add_cultist(S.mind, 0) + S.mind.special_role = SPECIAL_ROLE_CULTIST + S.mind.store_memory("Serve the cult's will.") + to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.") + else + S.mind.store_memory("Serve [U.real_name], your creator.") + to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help them succeed in their goals at all costs.") + if(vic && U) + to_chat(U, "Capture successful!: [T.real_name]'s soul has been ripped from [U.p_their()] body and stored within the soul stone.") + if(isrobot(T))//Robots have to dust or else they spill out an empty robot brain, and unequiping them spills robot components that shouldn't spawn. + T.dust() + else + for(var/obj/item/W in T) + T.unEquip(W) + qdel(T) + +/obj/item/soulstone/proc/get_shade_type() + return /mob/living/simple_animal/shade/cult + +/obj/item/soulstone/anybody/get_shade_type() + return /mob/living/simple_animal/shade + +/obj/item/soulstone/proc/getCultGhost(mob/living/T, mob/U) + var/mob/dead/observer/chosen_ghost + + for(var/mob/dead/observer/ghost in GLOB.player_list) //We put them back in their body + if(ghost.mind && ghost.mind.current == T && ghost.client) + chosen_ghost = ghost + break + + if(!chosen_ghost) //Failing that, we grab a ghost + var/list/consenting_candidates = pollCandidates("Would you like to play as a Shade?", ROLE_CULTIST, FALSE, poll_time = 100) + if(consenting_candidates.len) + chosen_ghost = pick(consenting_candidates) + if(!T) + return 0 + if(!chosen_ghost) + to_chat(U, "There were no spirits willing to become a shade.") + return 0 + if(contents.len) //If they used the soulstone on someone else in the meantime + return 0 + T.ckey = chosen_ghost.ckey + init_shade(T, U) + return 1 diff --git a/code/game/gamemodes/wizard/spellbook.dm b/code/game/gamemodes/wizard/spellbook.dm index 0b24362cf1c3..0dffc0e61594 100644 --- a/code/game/gamemodes/wizard/spellbook.dm +++ b/code/game/gamemodes/wizard/spellbook.dm @@ -1,1004 +1,1005 @@ -/datum/spellbook_entry - var/name = "Entry Name" - - var/spell_type = null - var/desc = "" - var/category = "Offensive" - var/log_name = "XX" //What it shows up as in logs - var/cost = 2 - var/refundable = 1 - var/surplus = -1 // -1 for infinite, not used by anything atm - var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell - var/buy_word = "Learn" - var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook - -/datum/spellbook_entry/proc/IsSpellAvailable() // For config prefs / gamemode restrictions - these are round applied - return 1 - -/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) // Specific circumstances - if(book.uses= aspell.level_max) - to_chat(user, "This spell cannot be improved further.") - return 0 - else - aspell.name = initial(aspell.name) - aspell.spell_level++ - aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max) - if(aspell.charge_max < aspell.charge_counter) - aspell.charge_counter = aspell.charge_max - switch(aspell.spell_level) - if(1) - to_chat(user, "You have improved [aspell.name] into Efficient [aspell.name].") - aspell.name = "Efficient [aspell.name]" - if(2) - to_chat(user, "You have further improved [aspell.name] into Quickened [aspell.name].") - aspell.name = "Quickened [aspell.name]" - if(3) - to_chat(user, "You have further improved [aspell.name] into Free [aspell.name].") - aspell.name = "Free [aspell.name]" - if(4) - to_chat(user, "You have further improved [aspell.name] into Instant [aspell.name].") - aspell.name = "Instant [aspell.name]" - if(aspell.spell_level >= aspell.level_max) - to_chat(user, "This spell cannot be strengthened any further.") - return 1 - //No same spell found - just learn it - feedback_add_details("wizard_spell_learned",log_name) - user.mind.AddSpell(S) - to_chat(user, "You have learned [S.name].") - return 1 - -/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user, obj/item/spellbook/book) - if(!refundable) - return 0 - if(!S) - S = new spell_type() - for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) - if(initial(S.name) == initial(aspell.name)) - return 1 - return 0 - -/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user, obj/item/spellbook/book) //return point value or -1 for failure - var/area/wizard_station/A = locate() - if(!(user in A.contents)) - to_chat(user, "You can only refund spells at the wizard lair") - return -1 - if(!S) - S = new spell_type() - var/spell_levels = 0 - for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) - if(initial(S.name) == initial(aspell.name)) - spell_levels = aspell.spell_level - user.mind.spell_list.Remove(aspell) - QDEL_NULL(S) - return cost * (spell_levels+1) - return -1 - -/datum/spellbook_entry/proc/GetInfo() - if(!S) - S = new spell_type() - var/dat ="" - dat += "[initial(S.name)]" - if(S.charge_type == "recharge") - dat += " Cooldown:[S.charge_max/10]" - dat += " Cost:[cost]
    " - dat += "[S.desc][desc]
    " - dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
    " - return dat - -//Main category - Spells -//Offensive -/datum/spellbook_entry/blind - name = "Blind" - spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind - log_name = "BD" - category = "Offensive" - cost = 1 - -/datum/spellbook_entry/lightningbolt - name = "Lightning Bolt" - spell_type = /obj/effect/proc_holder/spell/targeted/lightning - log_name = "LB" - category = "Offensive" - cost = 1 - -/datum/spellbook_entry/cluwne - name = "Curse of the Cluwne" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/cluwne - log_name = "CC" - category = "Offensive" - -/datum/spellbook_entry/banana_touch - name = "Banana Touch" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/banana - log_name = "BT" - cost = 1 - -/datum/spellbook_entry/mime_malaise - name = "Mime Malaise" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/mime_malaise - log_name = "MI" - cost = 1 - -/datum/spellbook_entry/horseman - name = "Curse of the Horseman" - spell_type = /obj/effect/proc_holder/spell/targeted/horsemask - log_name = "HH" - category = "Offensive" - -/datum/spellbook_entry/disintegrate - name = "Disintegrate" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate - log_name = "DG" - category = "Offensive" - -/datum/spellbook_entry/fireball - name = "Fireball" - spell_type = /obj/effect/proc_holder/spell/fireball - log_name = "FB" - category = "Offensive" - -/datum/spellbook_entry/fleshtostone - name = "Flesh to Stone" - spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone - log_name = "FS" - category = "Offensive" - -/datum/spellbook_entry/mutate - name = "Mutate" - spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate - log_name = "MU" - category = "Offensive" - -/datum/spellbook_entry/rod_form - name = "Rod Form" - spell_type = /obj/effect/proc_holder/spell/targeted/rod_form - log_name = "RF" - category = "Offensive" - -/datum/spellbook_entry/infinite_guns - name = "Lesser Summon Guns" - spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns - log_name = "IG" - category = "Offensive" - cost = 4 - -//Defensive -/datum/spellbook_entry/disabletech - name = "Disable Tech" - spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech - log_name = "DT" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/forcewall - name = "Force Wall" - spell_type = /obj/effect/proc_holder/spell/targeted/forcewall - log_name = "FW" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/greaterforcewall - name = "Greater Force Wall" - spell_type = /obj/effect/proc_holder/spell/targeted/forcewall/greater - log_name = "GFW" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/repulse - name = "Repulse" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse - log_name = "RP" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/smoke - name = "Smoke" - spell_type = /obj/effect/proc_holder/spell/targeted/smoke - log_name = "SM" - category = "Defensive" - cost = 1 - -/datum/spellbook_entry/lichdom - name = "Bind Soul" - spell_type = /obj/effect/proc_holder/spell/targeted/lichdom - log_name = "LD" - category = "Defensive" - -/datum/spellbook_entry/lichdom/IsSpellAvailable() - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/magicm - name = "Magic Missile" - spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile - log_name = "MM" - category = "Defensive" - -/datum/spellbook_entry/timestop - name = "Time Stop" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop - log_name = "TS" - category = "Defensive" - -//Mobility -/datum/spellbook_entry/knock - name = "Knock" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock - log_name = "KN" - category = "Mobility" - cost = 1 - -/datum/spellbook_entry/blink - name = "Blink" - spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink - log_name = "BL" - category = "Mobility" - -/datum/spellbook_entry/jaunt - name = "Ethereal Jaunt" - spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt - log_name = "EJ" - category = "Mobility" - -/datum/spellbook_entry/greaterknock - name = "Greater Knock" - spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock/greater - log_name = "GK" - category = "Mobility" - refundable = 0 //global effect on cast - -/datum/spellbook_entry/mindswap - name = "Mindswap" - spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer - log_name = "MT" - category = "Mobility" - -/datum/spellbook_entry/teleport - name = "Teleport" - spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport - log_name = "TP" - category = "Mobility" - -//Assistance -/datum/spellbook_entry/charge - name = "Charge" - spell_type = /obj/effect/proc_holder/spell/targeted/charge - log_name = "CH" - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/summonitem - name = "Summon Item" - spell_type = /obj/effect/proc_holder/spell/targeted/summonitem - log_name = "IS" - category = "Assistance" - cost = 1 - -/datum/spellbook_entry/noclothes - name = "Remove Clothes Requirement" - spell_type = /obj/effect/proc_holder/spell/noclothes - log_name = "NC" - category = "Assistance" - -//Rituals -/datum/spellbook_entry/summon - name = "Summon Stuff" - category = "Rituals" - refundable = FALSE - buy_word = "Cast" - var/active = FALSE - -/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) - return ..() && !active - -/datum/spellbook_entry/summon/GetInfo() - var/dat ="" - dat += "[name]" - if(cost>0) - dat += " Cost:[cost]
    " - else - dat += " No Cost
    " - dat += "[desc]
    " - if(active) - dat += "Already cast!
    " - return dat - -/datum/spellbook_entry/summon/ghosts - name = "Summon Ghosts" - desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you." - cost = 0 - log_name = "SGH" - -/datum/spellbook_entry/summon/ghosts/IsSpellAvailable() - if(!SSticker.mode) // In case spellbook is placed on map - return FALSE - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - new /datum/event/wizard/ghost() - active = TRUE - to_chat(user, "You have cast summon ghosts!") - playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1) - return TRUE - -/datum/spellbook_entry/summon/guns - name = "Summon Guns" - desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first." - log_name = "SG" - -/datum/spellbook_entry/summon/guns/IsSpellAvailable() - if(!SSticker.mode) // In case spellbook is placed on map - return FALSE - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - feedback_add_details("wizard_spell_learned", log_name) - rightandwrong(SUMMON_GUNS, user, 10) - active = TRUE - playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) - to_chat(user, "You have cast summon guns!") - return TRUE - -/datum/spellbook_entry/summon/magic - name = "Summon Magic" - desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time." - log_name = "SU" - -/datum/spellbook_entry/summon/magic/IsSpellAvailable() - if(!SSticker.mode) // In case spellbook is placed on map - return FALSE - if(SSticker.mode.name == "ragin' mages") - return FALSE - else - return TRUE - -/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - feedback_add_details("wizard_spell_learned", log_name) - rightandwrong(SUMMON_MAGIC, user, 10) - active = TRUE - playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) - to_chat(user, "You have cast summon magic!") - return TRUE - -//Main category - Magical Items -/datum/spellbook_entry/item - name = "Buy Item" - refundable = 0 - buy_word = "Summon" - var/item_path = null - -/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - user.put_in_hands(new item_path) - feedback_add_details("wizard_spell_learned", log_name) - return 1 - -/datum/spellbook_entry/item/GetInfo() - var/dat ="" - dat += "[name]" - dat += " Cost:[cost]
    " - dat += "[desc]
    " - if(surplus>=0) - dat += "[surplus] left.
    " - return dat - -//Artefacts -/datum/spellbook_entry/item/necrostone - name = "A Necromantic Stone" - desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." - item_path = /obj/item/necromantic_stone - log_name = "NS" - category = "Artefacts" - -/datum/spellbook_entry/item/scryingorb - name = "Scrying Orb" - desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." - item_path = /obj/item/scrying - log_name = "SO" - category = "Artefacts" - -/datum/spellbook_entry/item/scryingorb/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - if(..()) - if(!(XRAY in user.mutations)) - user.mutations.Add(XRAY) - user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) - user.see_in_dark = 8 - user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - to_chat(user, "The walls suddenly disappear.") - return TRUE - -/datum/spellbook_entry/item/soulstones - name = "Six Soul Stone Shards and the spell Artificer" - desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." - item_path = /obj/item/storage/belt/soulstone/full - log_name = "SS" - category = "Artefacts" - -/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) - . = ..() - if(.) - user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) - return . - -/datum/spellbook_entry/item/wands - name = "Wand Assortment" - desc = "A collection of wands that allow for a wide variety of utility. Wands do not recharge, so be conservative in use. Comes in a handy belt." - item_path = /obj/item/storage/belt/wands/full - log_name = "WA" - category = "Artefacts" - -//Weapons and Armors -/datum/spellbook_entry/item/battlemage - name = "Battlemage Armour" - desc = "An ensorceled suit of armour, protected by a powerful shield. The shield can completely negate sixteen attacks before being permanently depleted." - item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard - limit = 1 - category = "Weapons and Armors" - log_name = "BMA" - -/datum/spellbook_entry/item/battlemage_charge - name = "Battlemage Armour Charges" - desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour." - item_path = /obj/item/wizard_armour_charge - category = "Weapons and Armors" - cost = 1 - log_name = "BMAC" - -/datum/spellbook_entry/item/mjolnir - name = "Mjolnir" - desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." - item_path = /obj/item/twohanded/mjollnir - log_name = "MJ" - category = "Weapons and Armors" - -/datum/spellbook_entry/item/singularity_hammer - name = "Singularity Hammer" - desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everthing nearby to the point of impact." - item_path = /obj/item/twohanded/singularityhammer - log_name = "SI" - category = "Weapons and Armors" - -//Staves -/datum/spellbook_entry/item/staffdoor - name = "Staff of Door Creation" - desc = "A particular staff that can mold solid metal into ornate wooden doors. Useful for getting around in the absence of other transportation. Does not work on glass." - item_path = /obj/item/gun/magic/staff/door - log_name = "SD" - category = "Staves" - cost = 1 - -/datum/spellbook_entry/item/staffhealing - name = "Staff of Healing" - desc = "An altruistic staff that can heal the lame and raise the dead." - item_path = /obj/item/gun/magic/staff/healing - log_name = "SH" - category = "Staves" - cost = 1 - -/datum/spellbook_entry/item/staffslipping - name = "Staff of Slipping" - desc = "A staff that shoots magical bananas. These bananas will either slip or stun the target when hit. Surprisingly reliable!" - item_path = /obj/item/gun/magic/staff/slipping - log_name = "SL" - category = "Staves" - cost = 1 - -/datum/spellbook_entry/item/staffanimation - name = "Staff of Animation" - desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." - item_path = /obj/item/gun/magic/staff/animate - log_name = "SA" - category = "Staves" - -/datum/spellbook_entry/item/staffchange - name = "Staff of Change" - desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." - item_path = /obj/item/gun/magic/staff/change - log_name = "ST" - category = "Staves" - -/datum/spellbook_entry/item/staffchaos - name = "Staff of Chaos" - desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." - item_path = /obj/item/gun/magic/staff/chaos - log_name = "SC" - category = "Staves" - -//Summons -/datum/spellbook_entry/item/oozebottle - name = "Bottle of Ooze" - desc = "A bottle of magically infused ooze, which will awake an all-consuming Morph, capable of cunningly disguising itself as any object it comes in contact with and even casting some very basic spells. Be careful though, as Morph diet includes Wizards." - item_path = /obj/item/antag_spawner/morph - log_name = "BO" - category = "Summons" - limit = 3 - cost = 1 - -/datum/spellbook_entry/item/hugbottle - name = "Bottle of Tickles" - desc = "A bottle of magically infused fun, the smell of which will \ - attract adorable extradimensional beings when broken. These beings \ - are similar to slaughter demons, but are a little weaker and they do not permamently \ - kill their victims, instead putting them in an extradimensional hugspace, \ - to be released on the demon's death. Chaotic, but not ultimately \ - damaging. The crew's reaction to the other hand could be very \ - destructive." - item_path = /obj/item/antag_spawner/slaughter_demon/laughter - log_name = "HB" - category = "Summons" - limit = 3 - cost = 1 // Non-destructive; it's just a jape, sibling! - -/datum/spellbook_entry/item/bloodbottle - name = "Bottle of Blood" - desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." - item_path = /obj/item/antag_spawner/slaughter_demon - log_name = "BB" - category = "Summons" - limit = 3 - -/datum/spellbook_entry/item/contract - name = "Contract of Apprenticeship" - desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." - item_path = /obj/item/contract - log_name = "CT" - category = "Summons" - -/datum/spellbook_entry/item/tarotdeck - name = "Guardian Deck" - desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ - It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." - item_path = /obj/item/guardiancreator - log_name = "TD" - category = "Summons" - limit = 1 - -/obj/item/spellbook - name = "spell book" - desc = "The legendary book of spells of the wizard." - icon = 'icons/obj/library.dmi' - icon_state = "spellbook" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - var/uses = 10 - var/temp = null - var/op = 1 - var/tab = null - var/main_tab = null - var/mob/living/carbon/human/owner - var/list/datum/spellbook_entry/entries = list() - var/list/categories = list() - var/list/main_categories = list("Spells", "Magical Items") - var/list/spell_categories = list("Offensive", "Defensive", "Mobility", "Assistance", "Rituals") - var/list/item_categories = list("Artefacts", "Weapons and Armors", "Staves", "Summons") - -/obj/item/spellbook/proc/initialize() - var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon - for(var/T in entry_types) - var/datum/spellbook_entry/E = new T - if(E.IsSpellAvailable()) - entries |= E - categories |= E.category - else - qdel(E) - main_tab = main_categories[1] - tab = categories[1] - -/obj/item/spellbook/New() - ..() - initialize() - -/obj/item/spellbook/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/contract)) - var/obj/item/contract/contract = O - if(contract.used) - to_chat(user, "The contract has been used, you can't get your points back now!") - else - to_chat(user, "You feed the contract back into the spellbook, refunding your points.") - uses+=2 - qdel(O) - return - - if(istype(O, /obj/item/antag_spawner/slaughter_demon)) - to_chat(user, "On second thought, maybe summoning a demon is a bad idea. You refund your points.") - if(istype(O, /obj/item/antag_spawner/slaughter_demon/laughter)) - uses += 1 - for(var/datum/spellbook_entry/item/hugbottle/HB in entries) - if(!isnull(HB.limit)) - HB.limit++ - else - uses += 2 - for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) - if(!isnull(BB.limit)) - BB.limit++ - qdel(O) - return - - if(istype(O, /obj/item/antag_spawner/morph)) - to_chat(user, "On second thought, maybe awakening a morph is a bad idea. You refund your points.") - uses += 1 - for(var/datum/spellbook_entry/item/oozebottle/OB in entries) - if(!isnull(OB.limit)) - OB.limit++ - qdel(O) - return - return ..() - -/obj/item/spellbook/proc/GetCategoryHeader(category) - var/dat = "" - switch(category) - if("Offensive") - dat += "Spells geared towards debilitating and destroying.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Defensive") - dat += "Spells geared towards improving your survivabilty or reducing foes ability to attack.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Mobility") - dat += "Spells geared towards improving your ability to move. It is a good idea to take at least one.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Assistance") - dat += "Spells geared towards improving your other items and abilities.

    " - dat += "For spells: the number after the spell name is the cooldown time.
    " - dat += "You can reduce this number by spending more points on the spell.
    " - if("Rituals") - dat += "These powerful spells are capable of changing the very fabric of reality. Not always in your favour.
    " - if("Weapons and Armors") - dat += "Various weapons and armors to crush your enemies and protect you from harm.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - if("Staves") - dat += "Various staves granting you their power, which they slowly recharge over time.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - if("Artefacts") - dat += "Various magical artefacts to aid you.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - if("Summons") - dat += "Magical items geared towards bringing in outside forces to aid you.

    " - dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
    " - return dat - -/obj/item/spellbook/proc/wrap(content) - var/dat = "" - dat +="Spellbook" - dat += {" - - - - "} - dat += {"[content]"} - return dat - -/obj/item/spellbook/attack_self(mob/user as mob) - if(!owner) - to_chat(user, "You bind the spellbook to yourself.") - owner = user - return - if(user != owner) - to_chat(user, "The [name] does not recognize you as it's owner and refuses to open!") - return - user.set_machine(src) - var/dat = "" - - dat += "
      " - var/list/cat_dat = list() - for(var/main_category in main_categories) - cat_dat[main_category] = "
      " - dat += "
    • [main_category]
    • " - dat += "
    " - switch(main_tab) - if("Spells") - dat += "
      " - for(var/category in categories) - if(category in item_categories) - continue - cat_dat[category] = "
      " - dat += "
    • [category]
    • " - dat += "
    • Points remaining : [uses]
    • " - if("Magical Items") - dat += "
        " - for(var/category in categories) - if(category in spell_categories) - continue - cat_dat[category] = "
        " - dat += "
      • [category]
      • " - dat += "
      • Points remaining : [uses]
      • " - dat += "
      " - - var/datum/spellbook_entry/E - for(var/i=1,i<=entries.len,i++) - var/spell_info = "" - E = entries[i] - spell_info += E.GetInfo() - if(E.CanBuy(user,src)) - spell_info+= "[E.buy_word]
      " - else - spell_info+= "Can't [E.buy_word]
      " - if(E.CanRefund(user,src)) - spell_info+= "Refund
      " - spell_info += "
      " - if(cat_dat[E.category]) - cat_dat[E.category] += spell_info - - for(var/category in categories) - dat += "
      " - dat += GetCategoryHeader(category) - dat += cat_dat[category] - dat += "
      " - - user << browse(wrap(dat), "window=spellbook;size=800x600") - onclose(user, "spellbook") - return - -/obj/item/spellbook/Topic(href, href_list) - if(..()) - return 1 - var/mob/living/carbon/human/H = usr - - if(!ishuman(H)) - return 1 - - if(H.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE) - temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." - return 1 - - var/datum/spellbook_entry/E = null - if(loc == H || (in_range(src, H) && istype(loc, /turf))) - H.set_machine(src) - if(href_list["buy"]) - E = entries[text2num(href_list["buy"])] - if(E && E.CanBuy(H,src)) - if(E.Buy(H,src)) - if(E.limit) - E.limit-- - uses -= E.cost - else if(href_list["refund"]) - E = entries[text2num(href_list["refund"])] - if(E && E.refundable) - var/result = E.Refund(H,src) - if(result > 0) - if(!isnull(E.limit)) - E.limit += result - uses += result - else if(href_list["mainpage"]) - main_tab = sanitize(href_list["mainpage"]) - tab = sanitize(href_list["page"]) - if(main_tab == "Spells") - tab = spell_categories[1] - else if(main_tab == "Magical Items") - tab = item_categories[1] - else if(href_list["page"]) - tab = sanitize(href_list["page"]) - attack_self(H) - return 1 - -//Single Use Spellbooks -/obj/item/spellbook/oneuse - var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic - var/spellname = "sandbox" - var/used = 0 - name = "spellbook of " - uses = 1 - desc = "This template spellbook was never meant for the eyes of man..." - -/obj/item/spellbook/oneuse/New() - ..() - name += spellname - -/obj/item/spellbook/oneuse/initialize() //No need to init - return - -/obj/item/spellbook/oneuse/attack_self(mob/user) - var/obj/effect/proc_holder/spell/S = new spell - for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) - if(knownspell.type == S.type) - if(user.mind) - if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE || user.mind.special_role == SPECIAL_ROLE_WIZARD) - to_chat(user, "You're already far more versed in this spell than this flimsy how-to book can provide.") - else - to_chat(user, "You've already read this one.") - return - if(used) - recoil(user) - else - user.mind.AddSpell(S) - to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") - user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") - onlearned(user) - -/obj/item/spellbook/oneuse/proc/recoil(mob/user) - user.visible_message("[src] glows in a black light!") - -/obj/item/spellbook/oneuse/proc/onlearned(mob/user) - used = 1 - user.visible_message("[src] glows dark for a second!") - -/obj/item/spellbook/oneuse/attackby() - return - -/obj/item/spellbook/oneuse/fireball - spell = /obj/effect/proc_holder/spell/fireball - spellname = "fireball" - icon_state = "bookfireball" - desc = "This book feels warm to the touch." - -/obj/item/spellbook/oneuse/fireball/recoil(mob/user as mob) - ..() - explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) - qdel(src) - -/obj/item/spellbook/oneuse/smoke - spell = /obj/effect/proc_holder/spell/targeted/smoke - spellname = "smoke" - icon_state = "booksmoke" - desc = "This book is overflowing with the dank arts." - -/obj/item/spellbook/oneuse/smoke/recoil(mob/user as mob) - ..() - to_chat(user, "Your stomach rumbles...") - user.adjust_nutrition(-200) - -/obj/item/spellbook/oneuse/blind - spell = /obj/effect/proc_holder/spell/targeted/trigger/blind - spellname = "blind" - icon_state = "bookblind" - desc = "This book looks blurry, no matter how you look at it." - -/obj/item/spellbook/oneuse/blind/recoil(mob/user as mob) - ..() - to_chat(user, "You go blind!") - user.EyeBlind(10) - -/obj/item/spellbook/oneuse/mindswap - spell = /obj/effect/proc_holder/spell/targeted/mind_transfer - spellname = "mindswap" - icon_state = "bookmindswap" - desc = "This book's cover is pristine, though its pages look ragged and torn." - var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps - -/obj/item/spellbook/oneuse/mindswap/onlearned() - spellname = pick("fireball","smoke","blind","forcewall","knock","horses","charge") - icon_state = "book[spellname]" - name = "spellbook of [spellname]" //Note, desc doesn't change by design - ..() - -/obj/item/spellbook/oneuse/mindswap/recoil(mob/user) - ..() - if(stored_swap in GLOB.dead_mob_list) - stored_swap = null - if(!stored_swap) - stored_swap = user - to_chat(user, "For a moment you feel like you don't even know who you are anymore.") - return - if(stored_swap == user) - to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") - return - - var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new - swapper.cast(user, stored_swap, 1) - - to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") - to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") - stored_swap = null - -/obj/item/spellbook/oneuse/forcewall - spell = /obj/effect/proc_holder/spell/targeted/forcewall - spellname = "forcewall" - icon_state = "bookforcewall" - desc = "This book has a dedication to mimes everywhere inside the front cover." - -/obj/item/spellbook/oneuse/forcewall/recoil(mob/user as mob) - ..() - to_chat(user, "You suddenly feel very solid!") - var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) - S.timer = 30 - user.drop_item() - -/obj/item/spellbook/oneuse/knock - spell = /obj/effect/proc_holder/spell/aoe_turf/knock - spellname = "knock" - icon_state = "bookknock" - desc = "This book is hard to hold closed properly." - -/obj/item/spellbook/oneuse/knock/recoil(mob/user as mob) - ..() - to_chat(user, "You're knocked down!") - user.Weaken(20) - -/obj/item/spellbook/oneuse/horsemask - spell = /obj/effect/proc_holder/spell/targeted/horsemask - spellname = "horses" - icon_state = "bookhorses" - desc = "This book is more horse than your mind has room for." - -/obj/item/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) - if(istype(user, /mob/living/carbon/human)) - to_chat(user, "HOR-SIE HAS RISEN") - var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead - magichead.flags |= NODROP | DROPDEL //curses! - magichead.flags_inv = null //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - if(!user.unEquip(user.wear_mask)) - qdel(user.wear_mask) - user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) - qdel(src) - else - to_chat(user, "I say thee neigh") - -/obj/item/spellbook/oneuse/charge - spell = /obj/effect/proc_holder/spell/targeted/charge - spellname = "charging" - icon_state = "bookcharge" - desc = "This book is made of 100% post-consumer wizard." - -/obj/item/spellbook/oneuse/charge/recoil(mob/user as mob) - ..() - to_chat(user, "[src] suddenly feels very warm!") - empulse(src, 1, 1) - -/obj/item/spellbook/oneuse/summonitem - spell = /obj/effect/proc_holder/spell/targeted/summonitem - spellname = "instant summons" - icon_state = "booksummons" - desc = "This book is bright and garish, very hard to miss." - -/obj/item/spellbook/oneuse/summonitem/recoil(mob/user as mob) - ..() - to_chat(user, "[src] suddenly vanishes!") - qdel(src) - -/obj/item/spellbook/oneuse/fake_gib - spell = /obj/effect/proc_holder/spell/targeted/touch/fake_disintegrate - spellname = "disintegrate" - icon_state = "bookfireball" - desc = "This book feels like it will rip stuff apart." - -/obj/item/spellbook/oneuse/sacredflame - spell = /obj/effect/proc_holder/spell/targeted/sacred_flame - spellname = "sacred flame" - icon_state = "booksacredflame" - desc = "Become one with the flames that burn within... and invite others to do so as well." - -/obj/item/spellbook/oneuse/random - icon_state = "random_book" - -/obj/item/spellbook/oneuse/random/initialize() - . = ..() - var/static/banned_spells = list(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/fingergun/fake, /obj/item/spellbook/oneuse/mime/greaterwall) - var/real_type = pick(subtypesof(/obj/item/spellbook/oneuse) - banned_spells) - new real_type(loc) - qdel(src) \ No newline at end of file +/datum/spellbook_entry + var/name = "Entry Name" + + var/spell_type = null + var/desc = "" + var/category = "Offensive" + var/log_name = "XX" //What it shows up as in logs + var/cost = 2 + var/refundable = 1 + var/surplus = -1 // -1 for infinite, not used by anything atm + var/obj/effect/proc_holder/spell/S = null //Since spellbooks can be used by only one person anyway we can track the actual spell + var/buy_word = "Learn" + var/limit //used to prevent a spellbook_entry from being bought more than X times with one wizard spellbook + +/datum/spellbook_entry/proc/IsSpellAvailable() // For config prefs / gamemode restrictions - these are round applied + return 1 + +/datum/spellbook_entry/proc/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) // Specific circumstances + if(book.uses= aspell.level_max) + to_chat(user, "This spell cannot be improved further.") + return 0 + else + aspell.name = initial(aspell.name) + aspell.spell_level++ + aspell.charge_max = round(initial(aspell.charge_max) - aspell.spell_level * (initial(aspell.charge_max) - aspell.cooldown_min)/ aspell.level_max) + if(aspell.charge_max < aspell.charge_counter) + aspell.charge_counter = aspell.charge_max + switch(aspell.spell_level) + if(1) + to_chat(user, "You have improved [aspell.name] into Efficient [aspell.name].") + aspell.name = "Efficient [aspell.name]" + if(2) + to_chat(user, "You have further improved [aspell.name] into Quickened [aspell.name].") + aspell.name = "Quickened [aspell.name]" + if(3) + to_chat(user, "You have further improved [aspell.name] into Free [aspell.name].") + aspell.name = "Free [aspell.name]" + if(4) + to_chat(user, "You have further improved [aspell.name] into Instant [aspell.name].") + aspell.name = "Instant [aspell.name]" + if(aspell.spell_level >= aspell.level_max) + to_chat(user, "This spell cannot be strengthened any further.") + return 1 + //No same spell found - just learn it + feedback_add_details("wizard_spell_learned",log_name) + user.mind.AddSpell(S) + to_chat(user, "You have learned [S.name].") + return 1 + +/datum/spellbook_entry/proc/CanRefund(mob/living/carbon/human/user, obj/item/spellbook/book) + if(!refundable) + return 0 + if(!S) + S = new spell_type() + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(S.name) == initial(aspell.name)) + return 1 + return 0 + +/datum/spellbook_entry/proc/Refund(mob/living/carbon/human/user, obj/item/spellbook/book) //return point value or -1 for failure + var/area/wizard_station/A = locate() + if(!(user in A.contents)) + to_chat(user, "You can only refund spells at the wizard lair") + return -1 + if(!S) + S = new spell_type() + var/spell_levels = 0 + for(var/obj/effect/proc_holder/spell/aspell in user.mind.spell_list) + if(initial(S.name) == initial(aspell.name)) + spell_levels = aspell.spell_level + user.mind.spell_list.Remove(aspell) + QDEL_NULL(S) + return cost * (spell_levels+1) + return -1 + +/datum/spellbook_entry/proc/GetInfo() + if(!S) + S = new spell_type() + var/dat ="" + dat += "[initial(S.name)]" + if(S.charge_type == "recharge") + dat += " Cooldown:[S.charge_max/10]" + dat += " Cost:[cost]
      " + dat += "[S.desc][desc]
      " + dat += "[S.clothes_req?"Needs wizard garb":"Can be cast without wizard garb"]
      " + return dat + +//Main category - Spells +//Offensive +/datum/spellbook_entry/blind + name = "Blind" + spell_type = /obj/effect/proc_holder/spell/targeted/trigger/blind + log_name = "BD" + category = "Offensive" + cost = 1 + +/datum/spellbook_entry/lightningbolt + name = "Lightning Bolt" + spell_type = /obj/effect/proc_holder/spell/targeted/lightning + log_name = "LB" + category = "Offensive" + cost = 1 + +/datum/spellbook_entry/cluwne + name = "Curse of the Cluwne" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/cluwne + log_name = "CC" + category = "Offensive" + +/datum/spellbook_entry/banana_touch + name = "Banana Touch" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/banana + log_name = "BT" + cost = 1 + +/datum/spellbook_entry/mime_malaise + name = "Mime Malaise" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/mime_malaise + log_name = "MI" + cost = 1 + +/datum/spellbook_entry/horseman + name = "Curse of the Horseman" + spell_type = /obj/effect/proc_holder/spell/targeted/horsemask + log_name = "HH" + category = "Offensive" + +/datum/spellbook_entry/disintegrate + name = "Disintegrate" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/disintegrate + log_name = "DG" + category = "Offensive" + +/datum/spellbook_entry/fireball + name = "Fireball" + spell_type = /obj/effect/proc_holder/spell/fireball + log_name = "FB" + category = "Offensive" + +/datum/spellbook_entry/fleshtostone + name = "Flesh to Stone" + spell_type = /obj/effect/proc_holder/spell/targeted/touch/flesh_to_stone + log_name = "FS" + category = "Offensive" + +/datum/spellbook_entry/mutate + name = "Mutate" + spell_type = /obj/effect/proc_holder/spell/targeted/genetic/mutate + log_name = "MU" + category = "Offensive" + +/datum/spellbook_entry/rod_form + name = "Rod Form" + spell_type = /obj/effect/proc_holder/spell/targeted/rod_form + log_name = "RF" + category = "Offensive" + +/datum/spellbook_entry/infinite_guns + name = "Lesser Summon Guns" + spell_type = /obj/effect/proc_holder/spell/targeted/infinite_guns + log_name = "IG" + category = "Offensive" + cost = 4 + +//Defensive +/datum/spellbook_entry/disabletech + name = "Disable Tech" + spell_type = /obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + log_name = "DT" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/forcewall + name = "Force Wall" + spell_type = /obj/effect/proc_holder/spell/targeted/forcewall + log_name = "FW" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/greaterforcewall + name = "Greater Force Wall" + spell_type = /obj/effect/proc_holder/spell/targeted/forcewall/greater + log_name = "GFW" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/repulse + name = "Repulse" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/repulse + log_name = "RP" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/smoke + name = "Smoke" + spell_type = /obj/effect/proc_holder/spell/targeted/smoke + log_name = "SM" + category = "Defensive" + cost = 1 + +/datum/spellbook_entry/lichdom + name = "Bind Soul" + spell_type = /obj/effect/proc_holder/spell/targeted/lichdom + log_name = "LD" + category = "Defensive" + +/datum/spellbook_entry/lichdom/IsSpellAvailable() + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/magicm + name = "Magic Missile" + spell_type = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile + log_name = "MM" + category = "Defensive" + +/datum/spellbook_entry/timestop + name = "Time Stop" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + log_name = "TS" + category = "Defensive" + +//Mobility +/datum/spellbook_entry/knock + name = "Knock" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock + log_name = "KN" + category = "Mobility" + cost = 1 + +/datum/spellbook_entry/blink + name = "Blink" + spell_type = /obj/effect/proc_holder/spell/targeted/turf_teleport/blink + log_name = "BL" + category = "Mobility" + +/datum/spellbook_entry/jaunt + name = "Ethereal Jaunt" + spell_type = /obj/effect/proc_holder/spell/targeted/ethereal_jaunt + log_name = "EJ" + category = "Mobility" + +/datum/spellbook_entry/greaterknock + name = "Greater Knock" + spell_type = /obj/effect/proc_holder/spell/aoe_turf/knock/greater + log_name = "GK" + category = "Mobility" + refundable = 0 //global effect on cast + +/datum/spellbook_entry/mindswap + name = "Mindswap" + spell_type = /obj/effect/proc_holder/spell/targeted/mind_transfer + log_name = "MT" + category = "Mobility" + +/datum/spellbook_entry/teleport + name = "Teleport" + spell_type = /obj/effect/proc_holder/spell/targeted/area_teleport/teleport + log_name = "TP" + category = "Mobility" + +//Assistance +/datum/spellbook_entry/charge + name = "Charge" + spell_type = /obj/effect/proc_holder/spell/targeted/charge + log_name = "CH" + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/summonitem + name = "Summon Item" + spell_type = /obj/effect/proc_holder/spell/targeted/summonitem + log_name = "IS" + category = "Assistance" + cost = 1 + +/datum/spellbook_entry/noclothes + name = "Remove Clothes Requirement" + spell_type = /obj/effect/proc_holder/spell/noclothes + log_name = "NC" + category = "Assistance" + +//Rituals +/datum/spellbook_entry/summon + name = "Summon Stuff" + category = "Rituals" + refundable = FALSE + buy_word = "Cast" + var/active = FALSE + +/datum/spellbook_entry/summon/CanBuy(mob/living/carbon/human/user, obj/item/spellbook/book) + return ..() && !active + +/datum/spellbook_entry/summon/GetInfo() + var/dat ="" + dat += "[name]" + if(cost>0) + dat += " Cost:[cost]
      " + else + dat += " No Cost
      " + dat += "[desc]
      " + if(active) + dat += "Already cast!
      " + return dat + +/datum/spellbook_entry/summon/ghosts + name = "Summon Ghosts" + desc = "Spook the crew out by making them see dead people. Be warned, ghosts are capricious and occasionally vindicative, and some will use their incredibly minor abilities to frustrate you." + cost = 0 + log_name = "SGH" + +/datum/spellbook_entry/summon/ghosts/IsSpellAvailable() + if(!SSticker.mode) // In case spellbook is placed on map + return FALSE + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/ghosts/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + new /datum/event/wizard/ghost() + active = TRUE + to_chat(user, "You have cast summon ghosts!") + playsound(get_turf(user), 'sound/effects/ghost2.ogg', 50, 1) + return TRUE + +/datum/spellbook_entry/summon/guns + name = "Summon Guns" + desc = "Nothing could possibly go wrong with arming a crew of lunatics just itching for an excuse to kill you. There is a good chance that they will shoot each other first." + log_name = "SG" + +/datum/spellbook_entry/summon/guns/IsSpellAvailable() + if(!SSticker.mode) // In case spellbook is placed on map + return FALSE + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/guns/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + feedback_add_details("wizard_spell_learned", log_name) + rightandwrong(SUMMON_GUNS, user, 10) + active = TRUE + playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) + to_chat(user, "You have cast summon guns!") + return TRUE + +/datum/spellbook_entry/summon/magic + name = "Summon Magic" + desc = "Share the wonders of magic with the crew and show them why they aren't to be trusted with it at the same time." + log_name = "SU" + +/datum/spellbook_entry/summon/magic/IsSpellAvailable() + if(!SSticker.mode) // In case spellbook is placed on map + return FALSE + if(SSticker.mode.name == "ragin' mages") + return FALSE + else + return TRUE + +/datum/spellbook_entry/summon/magic/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + feedback_add_details("wizard_spell_learned", log_name) + rightandwrong(SUMMON_MAGIC, user, 10) + active = TRUE + playsound(get_turf(user), 'sound/magic/castsummon.ogg', 50, TRUE) + to_chat(user, "You have cast summon magic!") + return TRUE + +//Main category - Magical Items +/datum/spellbook_entry/item + name = "Buy Item" + refundable = 0 + buy_word = "Summon" + var/item_path = null + +/datum/spellbook_entry/item/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + user.put_in_hands(new item_path) + feedback_add_details("wizard_spell_learned", log_name) + return 1 + +/datum/spellbook_entry/item/GetInfo() + var/dat ="" + dat += "[name]" + dat += " Cost:[cost]
      " + dat += "[desc]
      " + if(surplus>=0) + dat += "[surplus] left.
      " + return dat + +//Artefacts +/datum/spellbook_entry/item/necrostone + name = "A Necromantic Stone" + desc = "A Necromantic stone is able to resurrect three dead individuals as skeletal thralls for you to command." + item_path = /obj/item/necromantic_stone + log_name = "NS" + category = "Artefacts" + +/datum/spellbook_entry/item/scryingorb + name = "Scrying Orb" + desc = "An incandescent orb of crackling energy, using it will allow you to ghost while alive, allowing you to spy upon the station with ease. In addition, buying it will permanently grant you x-ray vision." + item_path = /obj/item/scrying + log_name = "SO" + category = "Artefacts" + +/datum/spellbook_entry/item/scryingorb/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + if(..()) + if(!(XRAY in user.mutations)) + user.mutations.Add(XRAY) + user.sight |= (SEE_MOBS|SEE_OBJS|SEE_TURFS) + user.see_in_dark = 8 + user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + to_chat(user, "The walls suddenly disappear.") + return TRUE + +/datum/spellbook_entry/item/soulstones + name = "Six Soul Stone Shards and the spell Artificer" + desc = "Soul Stone Shards are ancient tools capable of capturing and harnessing the spirits of the dead and dying. The spell Artificer allows you to create arcane machines for the captured souls to pilot." + item_path = /obj/item/storage/belt/soulstone/full + log_name = "SS" + category = "Artefacts" + +/datum/spellbook_entry/item/soulstones/Buy(mob/living/carbon/human/user, obj/item/spellbook/book) + . = ..() + if(.) + user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/construct(null)) + return . + +/datum/spellbook_entry/item/wands + name = "Wand Assortment" + desc = "A collection of wands that allow for a wide variety of utility. Wands do not recharge, so be conservative in use. Comes in a handy belt." + item_path = /obj/item/storage/belt/wands/full + log_name = "WA" + category = "Artefacts" + +//Weapons and Armors +/datum/spellbook_entry/item/battlemage + name = "Battlemage Armour" + desc = "An ensorceled suit of armour, protected by a powerful shield. The shield can completely negate sixteen attacks before being permanently depleted." + item_path = /obj/item/clothing/suit/space/hardsuit/shielded/wizard + limit = 1 + category = "Weapons and Armors" + log_name = "BMA" + +/datum/spellbook_entry/item/battlemage_charge + name = "Battlemage Armour Charges" + desc = "A powerful defensive rune, it will grant eight additional charges to a suit of battlemage armour." + item_path = /obj/item/wizard_armour_charge + category = "Weapons and Armors" + cost = 1 + log_name = "BMAC" + +/datum/spellbook_entry/item/mjolnir + name = "Mjolnir" + desc = "A mighty hammer on loan from Thor, God of Thunder. It crackles with barely contained power." + item_path = /obj/item/twohanded/mjollnir + log_name = "MJ" + category = "Weapons and Armors" + +/datum/spellbook_entry/item/singularity_hammer + name = "Singularity Hammer" + desc = "A hammer that creates an intensely powerful field of gravity where it strikes, pulling everthing nearby to the point of impact." + item_path = /obj/item/twohanded/singularityhammer + log_name = "SI" + category = "Weapons and Armors" + +//Staves +/datum/spellbook_entry/item/staffdoor + name = "Staff of Door Creation" + desc = "A particular staff that can mold solid metal into ornate wooden doors. Useful for getting around in the absence of other transportation. Does not work on glass." + item_path = /obj/item/gun/magic/staff/door + log_name = "SD" + category = "Staves" + cost = 1 + +/datum/spellbook_entry/item/staffhealing + name = "Staff of Healing" + desc = "An altruistic staff that can heal the lame and raise the dead." + item_path = /obj/item/gun/magic/staff/healing + log_name = "SH" + category = "Staves" + cost = 1 + +/datum/spellbook_entry/item/staffslipping + name = "Staff of Slipping" + desc = "A staff that shoots magical bananas. These bananas will either slip or stun the target when hit. Surprisingly reliable!" + item_path = /obj/item/gun/magic/staff/slipping + log_name = "SL" + category = "Staves" + cost = 1 + +/datum/spellbook_entry/item/staffanimation + name = "Staff of Animation" + desc = "An arcane staff capable of shooting bolts of eldritch energy which cause inanimate objects to come to life. This magic doesn't affect machines." + item_path = /obj/item/gun/magic/staff/animate + log_name = "SA" + category = "Staves" + +/datum/spellbook_entry/item/staffchange + name = "Staff of Change" + desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself." + item_path = /obj/item/gun/magic/staff/change + log_name = "ST" + category = "Staves" + +/datum/spellbook_entry/item/staffchaos + name = "Staff of Chaos" + desc = "A caprious tool that can fire all sorts of magic without any rhyme or reason. Using it on people you care about is not recommended." + item_path = /obj/item/gun/magic/staff/chaos + log_name = "SC" + category = "Staves" + +//Summons +/datum/spellbook_entry/item/oozebottle + name = "Bottle of Ooze" + desc = "A bottle of magically infused ooze, which will awake an all-consuming Morph, capable of cunningly disguising itself as any object it comes in contact with and even casting some very basic spells. Be careful though, as Morph diet includes Wizards." + item_path = /obj/item/antag_spawner/morph + log_name = "BO" + category = "Summons" + limit = 3 + cost = 1 + +/datum/spellbook_entry/item/hugbottle + name = "Bottle of Tickles" + desc = "A bottle of magically infused fun, the smell of which will \ + attract adorable extradimensional beings when broken. These beings \ + are similar to slaughter demons, but are a little weaker and they do not permamently \ + kill their victims, instead putting them in an extradimensional hugspace, \ + to be released on the demon's death. Chaotic, but not ultimately \ + damaging. The crew's reaction to the other hand could be very \ + destructive." + item_path = /obj/item/antag_spawner/slaughter_demon/laughter + log_name = "HB" + category = "Summons" + limit = 3 + cost = 1 // Non-destructive; it's just a jape, sibling! + +/datum/spellbook_entry/item/bloodbottle + name = "Bottle of Blood" + desc = "A bottle of magically infused blood, the smell of which will attract extradimensional beings when broken. Be careful though, the kinds of creatures summoned by blood magic are indiscriminate in their killing, and you yourself may become a victim." + item_path = /obj/item/antag_spawner/slaughter_demon + log_name = "BB" + category = "Summons" + limit = 3 + +/datum/spellbook_entry/item/contract + name = "Contract of Apprenticeship" + desc = "A magical contract binding an apprentice wizard to your service, using it will summon them to your side." + item_path = /obj/item/contract + log_name = "CT" + category = "Summons" + +/datum/spellbook_entry/item/tarotdeck + name = "Guardian Deck" + desc = "A deck of guardian tarot cards, capable of binding a personal guardian to your body. There are multiple types of guardian available, but all of them will transfer some amount of damage to you. \ + It would be wise to avoid buying these with anything capable of causing you to swap bodies with others." + item_path = /obj/item/guardiancreator + log_name = "TD" + category = "Summons" + limit = 1 + +/obj/item/spellbook + name = "spell book" + desc = "The legendary book of spells of the wizard." + icon = 'icons/obj/library.dmi' + icon_state = "spellbook" + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + var/uses = 10 + var/temp = null + var/op = 1 + var/tab = null + var/main_tab = null + var/mob/living/carbon/human/owner + var/list/datum/spellbook_entry/entries = list() + var/list/categories = list() + var/list/main_categories = list("Spells", "Magical Items") + var/list/spell_categories = list("Offensive", "Defensive", "Mobility", "Assistance", "Rituals") + var/list/item_categories = list("Artefacts", "Weapons and Armors", "Staves", "Summons") + +/obj/item/spellbook/proc/initialize() + var/entry_types = subtypesof(/datum/spellbook_entry) - /datum/spellbook_entry/item - /datum/spellbook_entry/summon + for(var/T in entry_types) + var/datum/spellbook_entry/E = new T + if(E.IsSpellAvailable()) + entries |= E + categories |= E.category + else + qdel(E) + main_tab = main_categories[1] + tab = categories[1] + +/obj/item/spellbook/New() + ..() + initialize() + +/obj/item/spellbook/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/contract)) + var/obj/item/contract/contract = O + if(contract.used) + to_chat(user, "The contract has been used, you can't get your points back now!") + else + to_chat(user, "You feed the contract back into the spellbook, refunding your points.") + uses+=2 + qdel(O) + return + + if(istype(O, /obj/item/antag_spawner/slaughter_demon)) + to_chat(user, "On second thought, maybe summoning a demon is a bad idea. You refund your points.") + if(istype(O, /obj/item/antag_spawner/slaughter_demon/laughter)) + uses += 1 + for(var/datum/spellbook_entry/item/hugbottle/HB in entries) + if(!isnull(HB.limit)) + HB.limit++ + else + uses += 2 + for(var/datum/spellbook_entry/item/bloodbottle/BB in entries) + if(!isnull(BB.limit)) + BB.limit++ + qdel(O) + return + + if(istype(O, /obj/item/antag_spawner/morph)) + to_chat(user, "On second thought, maybe awakening a morph is a bad idea. You refund your points.") + uses += 1 + for(var/datum/spellbook_entry/item/oozebottle/OB in entries) + if(!isnull(OB.limit)) + OB.limit++ + qdel(O) + return + return ..() + +/obj/item/spellbook/proc/GetCategoryHeader(category) + var/dat = "" + switch(category) + if("Offensive") + dat += "Spells geared towards debilitating and destroying.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Defensive") + dat += "Spells geared towards improving your survivabilty or reducing foes ability to attack.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Mobility") + dat += "Spells geared towards improving your ability to move. It is a good idea to take at least one.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Assistance") + dat += "Spells geared towards improving your other items and abilities.

      " + dat += "For spells: the number after the spell name is the cooldown time.
      " + dat += "You can reduce this number by spending more points on the spell.
      " + if("Rituals") + dat += "These powerful spells are capable of changing the very fabric of reality. Not always in your favour.
      " + if("Weapons and Armors") + dat += "Various weapons and armors to crush your enemies and protect you from harm.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Staves") + dat += "Various staves granting you their power, which they slowly recharge over time.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Artefacts") + dat += "Various magical artefacts to aid you.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + if("Summons") + dat += "Magical items geared towards bringing in outside forces to aid you.

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + return dat + +/obj/item/spellbook/proc/wrap(content) + var/dat = "" + dat +="Spellbook" + dat += {" + + + + "} + dat += {"[content]"} + return dat + +/obj/item/spellbook/attack_self(mob/user as mob) + if(!owner) + to_chat(user, "You bind the spellbook to yourself.") + owner = user + return + if(user != owner) + to_chat(user, "The [name] does not recognize you as it's owner and refuses to open!") + return + user.set_machine(src) + var/dat = "" + + dat += "
        " + var/list/cat_dat = list() + for(var/main_category in main_categories) + cat_dat[main_category] = "
        " + dat += "
      • [main_category]
      • " + dat += "
      " + switch(main_tab) + if("Spells") + dat += "
        " + for(var/category in categories) + if(category in item_categories) + continue + cat_dat[category] = "
        " + dat += "
      • [category]
      • " + dat += "
      • Points remaining : [uses]
      • " + if("Magical Items") + dat += "
          " + for(var/category in categories) + if(category in spell_categories) + continue + cat_dat[category] = "
          " + dat += "
        • [category]
        • " + dat += "
        • Points remaining : [uses]
        • " + dat += "
        " + + var/datum/spellbook_entry/E + for(var/i=1,i<=entries.len,i++) + var/spell_info = "" + E = entries[i] + spell_info += E.GetInfo() + if(E.CanBuy(user,src)) + spell_info+= "[E.buy_word]
        " + else + spell_info+= "Can't [E.buy_word]
        " + if(E.CanRefund(user,src)) + spell_info+= "Refund
        " + spell_info += "
        " + if(cat_dat[E.category]) + cat_dat[E.category] += spell_info + + for(var/category in categories) + dat += "
        " + dat += GetCategoryHeader(category) + dat += cat_dat[category] + dat += "
        " + + user << browse(wrap(dat), "window=spellbook;size=800x600") + onclose(user, "spellbook") + return + +/obj/item/spellbook/Topic(href, href_list) + if(..()) + return 1 + var/mob/living/carbon/human/H = usr + + if(!ishuman(H)) + return 1 + + if(H.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE) + temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." + return 1 + + var/datum/spellbook_entry/E = null + if(loc == H || (in_range(src, H) && istype(loc, /turf))) + H.set_machine(src) + if(href_list["buy"]) + E = entries[text2num(href_list["buy"])] + if(E && E.CanBuy(H,src)) + if(E.Buy(H,src)) + if(E.limit) + E.limit-- + uses -= E.cost + else if(href_list["refund"]) + E = entries[text2num(href_list["refund"])] + if(E && E.refundable) + var/result = E.Refund(H,src) + if(result > 0) + if(!isnull(E.limit)) + E.limit += result + uses += result + else if(href_list["mainpage"]) + main_tab = sanitize(href_list["mainpage"]) + tab = sanitize(href_list["page"]) + if(main_tab == "Spells") + tab = spell_categories[1] + else if(main_tab == "Magical Items") + tab = item_categories[1] + else if(href_list["page"]) + tab = sanitize(href_list["page"]) + attack_self(H) + return 1 + +//Single Use Spellbooks +/obj/item/spellbook/oneuse + var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic + var/spellname = "sandbox" + var/used = 0 + name = "spellbook of " + uses = 1 + desc = "This template spellbook was never meant for the eyes of man..." + +/obj/item/spellbook/oneuse/New() + ..() + name += spellname + +/obj/item/spellbook/oneuse/initialize() //No need to init + return + +/obj/item/spellbook/oneuse/attack_self(mob/user) + var/obj/effect/proc_holder/spell/S = new spell + for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) + if(knownspell.type == S.type) + if(user.mind) + if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE || user.mind.special_role == SPECIAL_ROLE_WIZARD) + to_chat(user, "You're already far more versed in this spell than this flimsy how-to book can provide.") + else + to_chat(user, "You've already read this one.") + return + if(used) + recoil(user) + else + user.mind.AddSpell(S) + to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") + user.create_log(MISC_LOG, "learned the spell [spellname] ([S])") + user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") + onlearned(user) + +/obj/item/spellbook/oneuse/proc/recoil(mob/user) + user.visible_message("[src] glows in a black light!") + +/obj/item/spellbook/oneuse/proc/onlearned(mob/user) + used = 1 + user.visible_message("[src] glows dark for a second!") + +/obj/item/spellbook/oneuse/attackby() + return + +/obj/item/spellbook/oneuse/fireball + spell = /obj/effect/proc_holder/spell/fireball + spellname = "fireball" + icon_state = "bookfireball" + desc = "This book feels warm to the touch." + +/obj/item/spellbook/oneuse/fireball/recoil(mob/user as mob) + ..() + explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) + qdel(src) + +/obj/item/spellbook/oneuse/smoke + spell = /obj/effect/proc_holder/spell/targeted/smoke + spellname = "smoke" + icon_state = "booksmoke" + desc = "This book is overflowing with the dank arts." + +/obj/item/spellbook/oneuse/smoke/recoil(mob/user as mob) + ..() + to_chat(user, "Your stomach rumbles...") + user.adjust_nutrition(-200) + +/obj/item/spellbook/oneuse/blind + spell = /obj/effect/proc_holder/spell/targeted/trigger/blind + spellname = "blind" + icon_state = "bookblind" + desc = "This book looks blurry, no matter how you look at it." + +/obj/item/spellbook/oneuse/blind/recoil(mob/user as mob) + ..() + to_chat(user, "You go blind!") + user.EyeBlind(10) + +/obj/item/spellbook/oneuse/mindswap + spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spellname = "mindswap" + icon_state = "bookmindswap" + desc = "This book's cover is pristine, though its pages look ragged and torn." + var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps + +/obj/item/spellbook/oneuse/mindswap/onlearned() + spellname = pick("fireball","smoke","blind","forcewall","knock","horses","charge") + icon_state = "book[spellname]" + name = "spellbook of [spellname]" //Note, desc doesn't change by design + ..() + +/obj/item/spellbook/oneuse/mindswap/recoil(mob/user) + ..() + if(stored_swap in GLOB.dead_mob_list) + stored_swap = null + if(!stored_swap) + stored_swap = user + to_chat(user, "For a moment you feel like you don't even know who you are anymore.") + return + if(stored_swap == user) + to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") + return + + var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new + swapper.cast(user, stored_swap, 1) + + to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") + to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") + stored_swap = null + +/obj/item/spellbook/oneuse/forcewall + spell = /obj/effect/proc_holder/spell/targeted/forcewall + spellname = "forcewall" + icon_state = "bookforcewall" + desc = "This book has a dedication to mimes everywhere inside the front cover." + +/obj/item/spellbook/oneuse/forcewall/recoil(mob/user as mob) + ..() + to_chat(user, "You suddenly feel very solid!") + var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) + S.timer = 30 + user.drop_item() + +/obj/item/spellbook/oneuse/knock + spell = /obj/effect/proc_holder/spell/aoe_turf/knock + spellname = "knock" + icon_state = "bookknock" + desc = "This book is hard to hold closed properly." + +/obj/item/spellbook/oneuse/knock/recoil(mob/user as mob) + ..() + to_chat(user, "You're knocked down!") + user.Weaken(20) + +/obj/item/spellbook/oneuse/horsemask + spell = /obj/effect/proc_holder/spell/targeted/horsemask + spellname = "horses" + icon_state = "bookhorses" + desc = "This book is more horse than your mind has room for." + +/obj/item/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) + if(istype(user, /mob/living/carbon/human)) + to_chat(user, "HOR-SIE HAS RISEN") + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP | DROPDEL //curses! + magichead.flags_inv = null //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + if(!user.unEquip(user.wear_mask)) + qdel(user.wear_mask) + user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + qdel(src) + else + to_chat(user, "I say thee neigh") + +/obj/item/spellbook/oneuse/charge + spell = /obj/effect/proc_holder/spell/targeted/charge + spellname = "charging" + icon_state = "bookcharge" + desc = "This book is made of 100% post-consumer wizard." + +/obj/item/spellbook/oneuse/charge/recoil(mob/user as mob) + ..() + to_chat(user, "[src] suddenly feels very warm!") + empulse(src, 1, 1) + +/obj/item/spellbook/oneuse/summonitem + spell = /obj/effect/proc_holder/spell/targeted/summonitem + spellname = "instant summons" + icon_state = "booksummons" + desc = "This book is bright and garish, very hard to miss." + +/obj/item/spellbook/oneuse/summonitem/recoil(mob/user as mob) + ..() + to_chat(user, "[src] suddenly vanishes!") + qdel(src) + +/obj/item/spellbook/oneuse/fake_gib + spell = /obj/effect/proc_holder/spell/targeted/touch/fake_disintegrate + spellname = "disintegrate" + icon_state = "bookfireball" + desc = "This book feels like it will rip stuff apart." + +/obj/item/spellbook/oneuse/sacredflame + spell = /obj/effect/proc_holder/spell/targeted/sacred_flame + spellname = "sacred flame" + icon_state = "booksacredflame" + desc = "Become one with the flames that burn within... and invite others to do so as well." + +/obj/item/spellbook/oneuse/random + icon_state = "random_book" + +/obj/item/spellbook/oneuse/random/initialize() + . = ..() + var/static/banned_spells = list(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/fingergun/fake, /obj/item/spellbook/oneuse/mime/greaterwall) + var/real_type = pick(subtypesof(/obj/item/spellbook/oneuse) - banned_spells) + new real_type(loc) + qdel(src) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 7a6f634784f4..3d07ae173db3 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -1,283 +1,284 @@ -/datum/game_mode - var/list/datum/mind/wizards = list() - var/list/datum/mind/apprentices = list() - -/datum/game_mode/wizard - name = "wizard" - config_tag = "wizard" - required_players = 20 - required_enemies = 1 - recommended_enemies = 1 - var/use_huds = 1 - - var/finished = 0 - var/but_wait_theres_more = 0 - -/datum/game_mode/wizard/announce() - to_chat(world, "The current game mode is - Wizard!") - to_chat(world, "There is a SPACE WIZARD on the station. You can't let him achieve his objective!") - -/datum/game_mode/wizard/can_start()//This could be better, will likely have to recode it later - if(!..()) - return 0 - var/list/datum/mind/possible_wizards = get_players_for_role(ROLE_WIZARD) - if(possible_wizards.len==0) - return 0 - var/datum/mind/wizard = pick(possible_wizards) - - wizards += wizard - modePlayer += wizard - wizard.assigned_role = SPECIAL_ROLE_WIZARD //So they aren't chosen for other jobs. - wizard.special_role = SPECIAL_ROLE_WIZARD - wizard.original = wizard.current - if(wizardstart.len == 0) - to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") - return 0 - return 1 - -/datum/game_mode/wizard/pre_setup() - for(var/datum/mind/wiz in wizards) - wiz.current.loc = pick(wizardstart) - ..() - return 1 - -/datum/game_mode/wizard/post_setup() - for(var/datum/mind/wizard in wizards) - log_game("[key_name(wizard)] has been selected as a Wizard") - forge_wizard_objectives(wizard) - //learn_basic_spells(wizard.current) - equip_wizard(wizard.current) - name_wizard(wizard.current) - greet_wizard(wizard) - if(use_huds) - update_wiz_icons_added(wizard) - - ..() - -/datum/game_mode/proc/remove_wizard(datum/mind/wizard_mind) - if(wizard_mind in wizards) - SSticker.mode.wizards -= wizard_mind - wizard_mind.special_role = null - wizard_mind.current.create_attack_log("De-wizarded") - wizard_mind.current.spellremove(wizard_mind.current) - wizard_mind.current.faction = list("Station") - if(issilicon(wizard_mind.current)) - to_chat(wizard_mind.current, "You have been turned into a robot! You can feel your magical powers fading away...") - else - to_chat(wizard_mind.current, "You have been brainwashed! You are no longer a wizard.") - SSticker.mode.update_wiz_icons_removed(wizard_mind) - -/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] - wizhud.join_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "hudwizard" : "apprentice")) - -/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] - wizhud.leave_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, null) - -/datum/game_mode/proc/forge_wizard_objectives(var/datum/mind/wizard) - var/datum/objective/wizchaos/wiz_objective = new - wiz_objective.owner = wizard - wizard.objectives += wiz_objective - return - -/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) - //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. - var/wizard_name_first = pick(GLOB.wizard_first) - var/wizard_name_second = pick(GLOB.wizard_second) - var/randomname = "[wizard_name_first] [wizard_name_second]" - spawn(0) - var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) - - if(!newname) - newname = randomname - - wizard_mob.real_name = newname - wizard_mob.name = newname - if(wizard_mob.mind) - wizard_mob.mind.name = newname - return - -/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) - addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) - if(you_are) - to_chat(wizard.current, "You are the Space Wizard!") - to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") - - var/obj_count = 1 - for(var/datum/objective/objective in wizard.objectives) - to_chat(wizard.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - -/*/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob)) - return - if(!config.feature_object_spell_system) - wizard_mob.verbs += /client/proc/jaunt - wizard_mob.mind.special_verbs += /client/proc/jaunt - else - wizard_mob.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(usr) -*/ - -/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob)) - return - - //So zards properly get their items when they are admin-made. - qdel(wizard_mob.wear_suit) - qdel(wizard_mob.head) - qdel(wizard_mob.shoes) - qdel(wizard_mob.r_hand) - qdel(wizard_mob.r_store) - qdel(wizard_mob.l_store) - - wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) - if(!isplasmaman(wizard_mob)) //handled in the species file for plasmen on the afterjob equip proc for now - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(wizard_mob), slot_back) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) - wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) - var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) - spellbook.owner = wizard_mob - wizard_mob.equip_to_slot_or_del(spellbook, slot_r_hand) - - wizard_mob.faction = list("wizard") - - wizard_mob.dna.species.after_equip_job(null, wizard_mob) - - to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") - to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") - to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") - wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") - wizard_mob.update_icons() - wizard_mob.gene_stability += DEFAULT_GENE_STABILITY //magic - return 1 - -// Checks if the game should end due to all wizards and apprentices being dead, or MMI'd/Borged -/datum/game_mode/wizard/check_finished() - var/wizards_alive = 0 - var/apprentices_alive = 0 - - // Wizards - for(var/datum/mind/wizard in wizards) - if(!istype(wizard.current,/mob/living/carbon)) - continue - if(wizard.current.stat==DEAD) - continue - if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive - continue - wizards_alive++ - - // Apprentices - if(!wizards_alive) - for(var/datum/mind/apprentice in apprentices) - if(!istype(apprentice.current,/mob/living/carbon)) - continue - if(apprentice.current.stat==DEAD) - continue - if(istype(apprentice.current, /obj/item/mmi)) // apprentice is in an MMI, don't count them as alive - continue - apprentices_alive++ - - if(wizards_alive || apprentices_alive || but_wait_theres_more) - return ..() - else - finished = 1 - return 1 - -/datum/game_mode/wizard/declare_completion(var/ragin = 0) - if(finished && !ragin) - feedback_set_details("round_end_result","wizard loss - wizard killed") - to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_wizard() - if(wizards.len) - var/text = "
        the wizards/witches were:" - - for(var/datum/mind/wizard in wizards) - - text += "
        [wizard.key] was [wizard.name] (" - if(wizard.current) - if(wizard.current.stat == DEAD) - text += "died" - else - text += "survived" - if(wizard.current.real_name != wizard.name) - text += " as [wizard.current.real_name]" - else - text += "body destroyed" - text += ")" - - var/count = 1 - var/wizardwin = 1 - for(var/datum/objective/objective in wizard.objectives) - if(objective.check_completion()) - text += "
        Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("wizard_objective","[objective.type]|SUCCESS") - else - text += "
        Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("wizard_objective","[objective.type]|FAIL") - wizardwin = 0 - count++ - - if(wizard.current && wizard.current.stat!=DEAD && wizardwin) - text += "
        The wizard was successful!" - feedback_add_details("wizard_success","SUCCESS") - else - text += "
        The wizard has failed!" - feedback_add_details("wizard_success","FAIL") - if(wizard.spell_list) - text += "
        [wizard.name] used the following spells: " - var/i = 1 - for(var/obj/effect/proc_holder/spell/S in wizard.spell_list) - text += "[S.name]" - if(wizard.spell_list.len > i) - text += ", " - i++ - text += "
        " - - to_chat(world, text) - return 1 - -//OTHER PROCS - -//To batch-remove wizard spells. Linked to mind.dm -/mob/proc/spellremove(mob/M) - if(!mind) - return - for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list) - qdel(spell_to_remove) - mind.spell_list -= spell_to_remove - -//To batch-remove mob spells. -/mob/proc/mobspellremove(mob/M) - for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list) - qdel(spell_to_remove) - mob_spell_list -= spell_to_remove - -/*Checks if the wizard can cast spells. -Made a proc so this is not repeated 14 (or more) times.*/ -/mob/proc/casting() -//Removed the stat check because not all spells require clothing now. - if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) - to_chat(usr, "I don't feel strong enough without my robe.") - return 0 - if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) - to_chat(usr, "I don't feel strong enough without my sandals.") - return 0 - if(!istype(usr:head, /obj/item/clothing/head/wizard)) - to_chat(usr, "I don't feel strong enough without my hat.") - return 0 - else - return 1 - -/proc/iswizard(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && ((M.mind in SSticker.mode.wizards) || (M.mind in SSticker.mode.apprentices)) +/datum/game_mode + var/list/datum/mind/wizards = list() + var/list/datum/mind/apprentices = list() + +/datum/game_mode/wizard + name = "wizard" + config_tag = "wizard" + required_players = 20 + required_enemies = 1 + recommended_enemies = 1 + var/use_huds = 1 + + var/finished = 0 + var/but_wait_theres_more = 0 + +/datum/game_mode/wizard/announce() + to_chat(world, "The current game mode is - Wizard!") + to_chat(world, "There is a SPACE WIZARD on the station. You can't let him achieve his objective!") + +/datum/game_mode/wizard/can_start()//This could be better, will likely have to recode it later + if(!..()) + return 0 + var/list/datum/mind/possible_wizards = get_players_for_role(ROLE_WIZARD) + if(possible_wizards.len==0) + return 0 + var/datum/mind/wizard = pick(possible_wizards) + + wizards += wizard + modePlayer += wizard + wizard.assigned_role = SPECIAL_ROLE_WIZARD //So they aren't chosen for other jobs. + wizard.special_role = SPECIAL_ROLE_WIZARD + wizard.original = wizard.current + if(GLOB.wizardstart.len == 0) + to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") + return 0 + return 1 + +/datum/game_mode/wizard/pre_setup() + for(var/datum/mind/wiz in wizards) + wiz.current.loc = pick(GLOB.wizardstart) + ..() + return 1 + +/datum/game_mode/wizard/post_setup() + for(var/datum/mind/wizard in wizards) + log_game("[key_name(wizard)] has been selected as a Wizard") + forge_wizard_objectives(wizard) + //learn_basic_spells(wizard.current) + equip_wizard(wizard.current) + name_wizard(wizard.current) + greet_wizard(wizard) + if(use_huds) + update_wiz_icons_added(wizard) + + ..() + +/datum/game_mode/proc/remove_wizard(datum/mind/wizard_mind) + if(wizard_mind in wizards) + SSticker.mode.wizards -= wizard_mind + wizard_mind.special_role = null + wizard_mind.current.create_attack_log("De-wizarded") + wizard_mind.current.create_log(CONVERSION_LOG, "De-wizarded") + wizard_mind.current.spellremove(wizard_mind.current) + wizard_mind.current.faction = list("Station") + if(issilicon(wizard_mind.current)) + to_chat(wizard_mind.current, "You have been turned into a robot! You can feel your magical powers fading away...") + else + to_chat(wizard_mind.current, "You have been brainwashed! You are no longer a wizard.") + SSticker.mode.update_wiz_icons_removed(wizard_mind) + +/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) + var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] + wizhud.join_hud(wiz_mind.current) + set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "hudwizard" : "apprentice")) + +/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) + var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ] + wizhud.leave_hud(wiz_mind.current) + set_antag_hud(wiz_mind.current, null) + +/datum/game_mode/proc/forge_wizard_objectives(var/datum/mind/wizard) + var/datum/objective/wizchaos/wiz_objective = new + wiz_objective.owner = wizard + wizard.objectives += wiz_objective + return + +/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) + //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. + var/wizard_name_first = pick(GLOB.wizard_first) + var/wizard_name_second = pick(GLOB.wizard_second) + var/randomname = "[wizard_name_first] [wizard_name_second]" + spawn(0) + var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) + + if(!newname) + newname = randomname + + wizard_mob.real_name = newname + wizard_mob.name = newname + if(wizard_mob.mind) + wizard_mob.mind.name = newname + return + +/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) + addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) + if(you_are) + to_chat(wizard.current, "You are the Space Wizard!") + to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") + + var/obj_count = 1 + for(var/datum/objective/objective in wizard.objectives) + to_chat(wizard.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + +/*/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) + if(!istype(wizard_mob)) + return + if(!config.feature_object_spell_system) + wizard_mob.verbs += /client/proc/jaunt + wizard_mob.mind.special_verbs += /client/proc/jaunt + else + wizard_mob.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(usr) +*/ + +/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) + if(!istype(wizard_mob)) + return + + //So zards properly get their items when they are admin-made. + qdel(wizard_mob.wear_suit) + qdel(wizard_mob.head) + qdel(wizard_mob.shoes) + qdel(wizard_mob.r_hand) + qdel(wizard_mob.r_store) + qdel(wizard_mob.l_store) + + wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) + if(!isplasmaman(wizard_mob)) //handled in the species file for plasmen on the afterjob equip proc for now + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) + wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(wizard_mob), slot_back) + wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) + wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) + var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) + spellbook.owner = wizard_mob + wizard_mob.equip_to_slot_or_del(spellbook, slot_r_hand) + + wizard_mob.faction = list("wizard") + + wizard_mob.dna.species.after_equip_job(null, wizard_mob) + + to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") + to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") + to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") + wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") + wizard_mob.update_icons() + wizard_mob.gene_stability += DEFAULT_GENE_STABILITY //magic + return 1 + +// Checks if the game should end due to all wizards and apprentices being dead, or MMI'd/Borged +/datum/game_mode/wizard/check_finished() + var/wizards_alive = 0 + var/apprentices_alive = 0 + + // Wizards + for(var/datum/mind/wizard in wizards) + if(!istype(wizard.current,/mob/living/carbon)) + continue + if(wizard.current.stat==DEAD) + continue + if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive + continue + wizards_alive++ + + // Apprentices + if(!wizards_alive) + for(var/datum/mind/apprentice in apprentices) + if(!istype(apprentice.current,/mob/living/carbon)) + continue + if(apprentice.current.stat==DEAD) + continue + if(istype(apprentice.current, /obj/item/mmi)) // apprentice is in an MMI, don't count them as alive + continue + apprentices_alive++ + + if(wizards_alive || apprentices_alive || but_wait_theres_more) + return ..() + else + finished = 1 + return 1 + +/datum/game_mode/wizard/declare_completion(var/ragin = 0) + if(finished && !ragin) + feedback_set_details("round_end_result","wizard loss - wizard killed") + to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_wizard() + if(wizards.len) + var/text = "
        the wizards/witches were:" + + for(var/datum/mind/wizard in wizards) + + text += "
        [wizard.key] was [wizard.name] (" + if(wizard.current) + if(wizard.current.stat == DEAD) + text += "died" + else + text += "survived" + if(wizard.current.real_name != wizard.name) + text += " as [wizard.current.real_name]" + else + text += "body destroyed" + text += ")" + + var/count = 1 + var/wizardwin = 1 + for(var/datum/objective/objective in wizard.objectives) + if(objective.check_completion()) + text += "
        Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("wizard_objective","[objective.type]|SUCCESS") + else + text += "
        Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("wizard_objective","[objective.type]|FAIL") + wizardwin = 0 + count++ + + if(wizard.current && wizard.current.stat!=DEAD && wizardwin) + text += "
        The wizard was successful!" + feedback_add_details("wizard_success","SUCCESS") + else + text += "
        The wizard has failed!" + feedback_add_details("wizard_success","FAIL") + if(wizard.spell_list) + text += "
        [wizard.name] used the following spells: " + var/i = 1 + for(var/obj/effect/proc_holder/spell/S in wizard.spell_list) + text += "[S.name]" + if(wizard.spell_list.len > i) + text += ", " + i++ + text += "
        " + + to_chat(world, text) + return 1 + +//OTHER PROCS + +//To batch-remove wizard spells. Linked to mind.dm +/mob/proc/spellremove(mob/M) + if(!mind) + return + for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list) + qdel(spell_to_remove) + mind.spell_list -= spell_to_remove + +//To batch-remove mob spells. +/mob/proc/mobspellremove(mob/M) + for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list) + qdel(spell_to_remove) + mob_spell_list -= spell_to_remove + +/*Checks if the wizard can cast spells. +Made a proc so this is not repeated 14 (or more) times.*/ +/mob/proc/casting() +//Removed the stat check because not all spells require clothing now. + if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) + to_chat(usr, "I don't feel strong enough without my robe.") + return 0 + if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) + to_chat(usr, "I don't feel strong enough without my sandals.") + return 0 + if(!istype(usr:head, /obj/item/clothing/head/wizard)) + to_chat(usr, "I don't feel strong enough without my hat.") + return 0 + else + return 1 + +/proc/iswizard(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && ((M.mind in SSticker.mode.wizards) || (M.mind in SSticker.mode.apprentices)) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 6ac2daef330b..7f93fd9fef6c 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -1,517 +1,514 @@ -/obj/var/list/req_access = null -/obj/var/req_access_txt = "0" -/obj/var/list/req_one_access = null -/obj/var/req_one_access_txt = "0" - -//returns 1 if this mob has sufficient access to use this object -/obj/proc/allowed(mob/M) - //check if we don't require any access at all - if(check_access()) - return 1 - - if(!M) - return 0 - - var/acc = M.get_access() //see mob.dm - - if(acc == IGNORE_ACCESS || M.can_admin_interact()) - return 1 //Mob ignores access - - else - return check_access_list(acc) - - return 0 - -/obj/item/proc/GetAccess() - return list() - -/obj/item/proc/GetID() - return null - -/obj/proc/generate_req_lists() - //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system. - if(!req_access) - req_access = list() - if(req_access_txt) - var/list/req_access_str = splittext(req_access_txt, ";") - for(var/x in req_access_str) - var/n = text2num(x) - if(n) - req_access += n - - if(!req_one_access) - req_one_access = list() - if(req_one_access_txt) - var/list/req_one_access_str = splittext(req_one_access_txt,";") - for(var/x in req_one_access_str) - var/n = text2num(x) - if(n) - req_one_access += n - -/obj/proc/check_access(obj/item/I) - var/list/L - if(I) - L = I.GetAccess() - else - L = list() - return check_access_list(L) - -/obj/proc/check_access_list(var/list/L) - generate_req_lists() - - if(!L) - return 0 - if(!istype(L, /list)) - return 0 - return has_access(req_access, req_one_access, L) - -/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) - for(var/req in req_access) - if(!(req in accesses)) //doesn't have this access - return 0 - if(req_one_access.len) - for(var/req in req_one_access) - if(req in accesses) //has an access from the single access list - return 1 - return 0 - return 1 - -/proc/get_centcom_access(job) - switch(job) - if("VIP Guest") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) - if("Custodian") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) - if("Thunderdome Overseer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER) - if("Emergency Response Team Member") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS) + get_all_accesses() - if("Emergency Response Team Leader") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER) + get_all_accesses() - if("Medical Officer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + get_all_accesses() - if("Intel Officer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE) + get_all_accesses() - if("Research Officer") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER) + get_all_accesses() - if("Death Commando") - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS) + get_all_accesses() - if("Deathsquad Officer") - return get_all_centcom_access() + get_all_accesses() - if("NT Undercover Operative") - return get_all_centcom_access() + get_all_accesses() - if("Special Operations Officer") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Representative") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Officer") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Captain") - return get_all_centcom_access() + get_all_accesses() - if("Supreme Commander") - return get_all_centcom_access() + get_all_accesses() - -/proc/get_syndicate_access(job) - switch(job) - if("Syndicate Operative") - return list(ACCESS_SYNDICATE) - if("Syndicate Operative Leader") - return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) - if("Syndicate Agent") - return list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS) - if("Vox Raider") - return list(ACCESS_VOX) - if("Vox Trader") - return list(ACCESS_VOX) - if("Syndicate Commando") - return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) - if("Syndicate Officer") - return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND) - -/proc/get_all_accesses() - return list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, - ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_RD, - ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_CHEMISTRY, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_MAINT_TUNNELS, - ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, - ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_HEADS, ACCESS_CAPTAIN, ACCESS_ALL_PERSONAL_LOCKERS, - ACCESS_TECH_STORAGE, ACCESS_CHAPEL_OFFICE, ACCESS_ATMOSPHERICS, ACCESS_KITCHEN, - ACCESS_BAR, ACCESS_JANITOR, ACCESS_CREMATORIUM, ACCESS_ROBOTICS, ACCESS_CARGO, ACCESS_CONSTRUCTION, - ACCESS_HYDROPONICS, ACCESS_LIBRARY, ACCESS_LAWYER, ACCESS_VIROLOGY, ACCESS_PSYCHIATRIST, ACCESS_CMO, ACCESS_QM, ACCESS_CLOWN, ACCESS_MIME, ACCESS_SURGERY, - ACCESS_THEATRE, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_MAILSORTING, - ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, ACCESS_XENOBIOLOGY, ACCESS_CE, ACCESS_HOP, ACCESS_HOS, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_PARAMEDIC, ACCESS_BLUESHIELD, ACCESS_MECHANIC,ACCESS_WEAPONS, - ACCESS_PILOT, ACCESS_NTREP, ACCESS_MAGISTRATE, ACCESS_MINERAL_STOREROOM, ACCESS_MINISAT, ACCESS_NETWORK) - -/proc/get_all_centcom_access() - return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SHUTTLES, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS, ACCESS_CENT_THUNDER, ACCESS_CENT_BRIDGE, ACCESS_CENT_COMMANDER) - -/proc/get_all_syndicate_access() - return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_VOX, ACCESS_SYNDICATE_COMMAND) - -/proc/get_all_misc_access() - return list(ACCESS_SALVAGE_CAPTAIN, ACCESS_TRADE_SOL, ACCESS_CRATE_CASH, ACCESS_AWAY01) - -/proc/get_absolutely_all_accesses() - return (get_all_accesses() | get_all_centcom_access() | get_all_syndicate_access() | get_all_misc_access()) - -/proc/get_region_accesses(code) - switch(code) - if(REGION_ALL) - return get_all_accesses() - if(REGION_GENERAL) //station general - return list(ACCESS_KITCHEN, ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_LIBRARY, ACCESS_THEATRE, ACCESS_LAWYER, ACCESS_MAGISTRATE, ACCESS_CLOWN, ACCESS_MIME) - if(REGION_SECURITY) //security - return list(ACCESS_SEC_DOORS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_PILOT, ACCESS_HOS) - if(REGION_MEDBAY) //medbay - return list(ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_PSYCHIATRIST, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_CMO, ACCESS_PARAMEDIC) - if(REGION_RESEARCH) //research - return list(ACCESS_RESEARCH, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_GENETICS, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_RD, ACCESS_NETWORK) - if(REGION_ENGINEERING) //engineering and maintenance - return list(ACCESS_CONSTRUCTION, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MINISAT, ACCESS_CE, ACCESS_MECHANIC) - if(REGION_SUPPLY) //supply - return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_QM) - if(REGION_COMMAND) //command - return list(ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HEADS_VAULT, ACCESS_BLUESHIELD, ACCESS_NTREP, ACCESS_HOP, ACCESS_CAPTAIN) - if(REGION_CENTCOMM) //because why the heck not - return get_all_centcom_access() + get_all_accesses() - -/proc/get_region_accesses_name(code) - switch(code) - if(REGION_ALL) - return "All" - if(REGION_GENERAL) //station general - return "General" - if(REGION_SECURITY) //security - return "Security" - if(REGION_MEDBAY) //medbay - return "Medbay" - if(REGION_RESEARCH) //research - return "Research" - if(REGION_ENGINEERING) //engineering and maintenance - return "Engineering" - if(REGION_SUPPLY) //supply - return "Supply" - if(REGION_COMMAND) //command - return "Command" - if(REGION_CENTCOMM) //CC - return "CentComm" - - -/proc/get_access_desc(A) - switch(A) - if(ACCESS_CARGO) - return "Cargo Bay" - if(ACCESS_CARGO_BOT) - return "Cargo Bot Delivery" - if(ACCESS_SECURITY) - return "Security" - if(ACCESS_BRIG) - return "Holding Cells" - if(ACCESS_COURT) - return "Courtroom" - if(ACCESS_FORENSICS_LOCKERS) - return "Forensics" - if(ACCESS_MEDICAL) - return "Medical" - if(ACCESS_GENETICS) - return "Genetics Lab" - if(ACCESS_MORGUE) - return "Morgue" - if(ACCESS_TOX) - return "R&D Lab" - if(ACCESS_TOX_STORAGE) - return "Toxins Lab" - if(ACCESS_CHEMISTRY) - return "Chemistry Lab" - if(ACCESS_RD) - return "Research Director" - if(ACCESS_BAR) - return "Bar" - if(ACCESS_JANITOR) - return "Custodial Closet" - if(ACCESS_ENGINE) - return "Engineering" - if(ACCESS_ENGINE_EQUIP) - return "Power Equipment" - if(ACCESS_MAINT_TUNNELS) - return "Maintenance" - if(ACCESS_EXTERNAL_AIRLOCKS) - return "External Airlocks" - if(ACCESS_EMERGENCY_STORAGE) - return "Emergency Storage" - if(ACCESS_CHANGE_IDS) - return "ID Computer" - if(ACCESS_AI_UPLOAD) - return "AI Upload" - if(ACCESS_TELEPORTER) - return "Teleporter" - if(ACCESS_EVA) - return "EVA" - if(ACCESS_HEADS) - return "Bridge" - if(ACCESS_CAPTAIN) - return "Captain" - if(ACCESS_ALL_PERSONAL_LOCKERS) - return "Personal Lockers" - if(ACCESS_CHAPEL_OFFICE) - return "Chapel Office" - if(ACCESS_TECH_STORAGE) - return "Technical Storage" - if(ACCESS_ATMOSPHERICS) - return "Atmospherics" - if(ACCESS_CREMATORIUM) - return "Crematorium" - if(ACCESS_ARMORY) - return "Armory" - if(ACCESS_CONSTRUCTION) - return "Construction Areas" - if(ACCESS_KITCHEN) - return "Kitchen" - if(ACCESS_HYDROPONICS) - return "Hydroponics" - if(ACCESS_LIBRARY) - return "Library" - if(ACCESS_LAWYER) - return "Law Office" - if(ACCESS_ROBOTICS) - return "Robotics" - if(ACCESS_VIROLOGY) - return "Virology" - if(ACCESS_PSYCHIATRIST) - return "Psychiatrist's Office" - if(ACCESS_CMO) - return "Chief Medical Officer" - if(ACCESS_QM) - return "Quartermaster" - if(ACCESS_CLOWN) - return "Clown's Office" - if(ACCESS_MIME) - return "Mime's Office" - if(ACCESS_SURGERY) - return "Surgery" - if(ACCESS_THEATRE) - return "Theatre" - if(ACCESS_MANUFACTURING) - return "Manufacturing" - if(ACCESS_RESEARCH) - return "Science" - if(ACCESS_MINING) - return "Mining" - if(ACCESS_MINING_OFFICE) - return "Mining Office" - if(ACCESS_MAILSORTING) - return "Cargo Office" - if(ACCESS_MINT) - return "Mint" - if(ACCESS_MINT_VAULT) - return "Mint Vault" - if(ACCESS_HEADS_VAULT) - return "Main Vault" - if(ACCESS_MINING_STATION) - return "Mining EVA" - if(ACCESS_XENOBIOLOGY) - return "Xenobiology Lab" - if(ACCESS_XENOARCH) - return "Xenoarchaeology" - if(ACCESS_HOP) - return "Head of Personnel" - if(ACCESS_HOS) - return "Head of Security" - if(ACCESS_CE) - return "Chief Engineer" - if(ACCESS_RC_ANNOUNCE) - return "RC Announcements" - if(ACCESS_KEYCARD_AUTH) - return "Keycode Auth. Device" - if(ACCESS_TCOMSAT) - return "Telecommunications" - if(ACCESS_NETWORK) - return "Network Access" - if(ACCESS_GATEWAY) - return "Gateway" - if(ACCESS_SEC_DOORS) - return "Brig" - if(ACCESS_BLUESHIELD) - return "Blueshield" - if(ACCESS_NTREP) - return "Nanotrasen Rep." - if(ACCESS_PARAMEDIC) - return "Paramedic" - if(ACCESS_MECHANIC) - return "Mechanic Workshop" - if(ACCESS_PILOT) - return "Security Pod Pilot" - if(ACCESS_MAGISTRATE) - return "Magistrate" - if(ACCESS_MINERAL_STOREROOM) - return "Mineral Storage" - if(ACCESS_MINISAT) - return "AI Satellite" - if(ACCESS_WEAPONS) - return "Weapon Permit" - -/proc/get_centcom_access_desc(A) - switch(A) - if(ACCESS_CENT_GENERAL) - return "General Access" - if(ACCESS_CENT_LIVING) - return "Living Quarters" - if(ACCESS_CENT_MEDICAL) - return "Medical" - if(ACCESS_CENT_SECURITY) - return "Security" - if(ACCESS_CENT_STORAGE) - return "Storage" - if(ACCESS_CENT_SHUTTLES) - return "Shuttles" - if(ACCESS_CENT_TELECOMMS) - return "Telecommunications" - if(ACCESS_CENT_TELEPORTER) - return "Teleporter" - if(ACCESS_CENT_SPECOPS) - return "Special Ops" - if(ACCESS_CENT_SPECOPS_COMMANDER) - return "Special Ops Commander" - if(ACCESS_CENT_BLACKOPS) - return "Black Ops" - if(ACCESS_CENT_THUNDER) - return "Thunderdome" - if(ACCESS_CENT_BRIDGE) - return "Bridge" - if(ACCESS_CENT_COMMANDER) - return "Commander" - -/proc/get_syndicate_access_desc(A) - switch(A) - if(ACCESS_SYNDICATE) - return "Syndicate Operative" - if(ACCESS_SYNDICATE_LEADER) - return "Syndicate Operative Leader" - if(ACCESS_VOX) - return "Vox" - if(ACCESS_SYNDICATE_COMMAND) - return "Syndicate Command" - -/proc/get_all_jobs() - var/list/all_jobs = list() - var/list/all_datums = subtypesof(/datum/job) - all_datums.Remove(list(/datum/job/ai,/datum/job/cyborg)) - var/datum/job/jobdatum - for(var/jobtype in all_datums) - jobdatum = new jobtype - all_jobs.Add(jobdatum.title) - return all_jobs - -/proc/get_all_centcom_jobs() - return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander") - -//gets the actual job rank (ignoring alt titles) -//this is used solely for sechuds -/obj/proc/GetJobRealName() - if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) - return - - var/rank - var/assignment - if(istype(src, /obj/item/pda)) - if(src:id) - rank = src:id:rank - assignment = src:id:assignment - else if(istype(src, /obj/item/card/id)) - rank = src:rank - assignment = src:assignment - - if( rank in GLOB.joblist ) - return rank - - if( assignment in GLOB.joblist ) - return assignment - - return "Unknown" - -//gets the alt title, failing that the actual job rank -//this is unused -/obj/proc/sdsdsd() //GetJobDisplayName - if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) - return - - var/assignment - if(istype(src, /obj/item/pda)) - if(src:id) - assignment = src:id:assignment - else if(istype(src, /obj/item/card/id)) - assignment = src:assignment - - if(assignment) - return assignment - - return "Unknown" - -proc/GetIdCard(var/mob/living/carbon/human/H) - if(H.wear_id) - var/id = H.wear_id.GetID() - if(id) - return id - if(H.get_active_hand()) - var/obj/item/I = H.get_active_hand() - return I.GetID() - -proc/FindNameFromID(var/mob/living/carbon/human/H) - ASSERT(istype(H)) - var/obj/item/card/id/C = H.get_active_hand() - if( istype(C) || istype(C, /obj/item/pda) ) - var/obj/item/card/id/ID = C - - if( istype(C, /obj/item/pda) ) - var/obj/item/pda/pda = C - ID = pda.id - if(!istype(ID)) - ID = null - - if(ID) - return ID.registered_name - - C = H.wear_id - - if( istype(C) || istype(C, /obj/item/pda) ) - var/obj/item/card/id/ID = C - - if( istype(C, /obj/item/pda) ) - var/obj/item/pda/pda = C - ID = pda.id - if(!istype(ID)) - ID = null - - if(ID) - return ID.registered_name - -proc/get_all_job_icons() //For all existing HUD icons - return GLOB.joblist + list("Prisoner") - -/obj/proc/GetJobName() //Used in secHUD icon generation - var/assignmentName = "Unknown" - var/rankName = "Unknown" - if(istype(src, /obj/item/pda)) - var/obj/item/pda/P = src - assignmentName = P.ownjob - rankName = P.ownrank - else if(istype(src, /obj/item/card/id)) - var/obj/item/card/id/I = src - assignmentName = I.assignment - rankName = I.rank - - - var/job_icons = get_all_job_icons() - var/centcom = get_all_centcom_jobs() - - if(assignmentName in centcom) //Return with the NT logo if it is a Centcom job - return "Centcom" - if(rankName in centcom) - return "Centcom" - - if(assignmentName in job_icons) //Check if the job has a hud icon - return assignmentName - if(rankName in job_icons) - return rankName - - return "Unknown" //Return unknown if none of the above apply +/obj/var/list/req_access = null +/obj/var/req_access_txt = "0" +/obj/var/list/req_one_access = null +/obj/var/req_one_access_txt = "0" + +//returns 1 if this mob has sufficient access to use this object +/obj/proc/allowed(mob/M) + //check if we don't require any access at all + if(check_access()) + return 1 + + if(!M) + return 0 + + var/acc = M.get_access() //see mob.dm + + if(acc == IGNORE_ACCESS || M.can_admin_interact()) + return 1 //Mob ignores access + + else + return check_access_list(acc) + + return 0 + +/obj/item/proc/GetAccess() + return list() + +/obj/item/proc/GetID() + return null + +/obj/proc/generate_req_lists() + //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system. + if(!req_access) + req_access = list() + if(req_access_txt) + var/list/req_access_str = splittext(req_access_txt, ";") + for(var/x in req_access_str) + var/n = text2num(x) + if(n) + req_access += n + + if(!req_one_access) + req_one_access = list() + if(req_one_access_txt) + var/list/req_one_access_str = splittext(req_one_access_txt,";") + for(var/x in req_one_access_str) + var/n = text2num(x) + if(n) + req_one_access += n + +/obj/proc/check_access(obj/item/I) + var/list/L + if(I) + L = I.GetAccess() + else + L = list() + return check_access_list(L) + +/obj/proc/check_access_list(var/list/L) + generate_req_lists() + + if(!L) + return 0 + if(!istype(L, /list)) + return 0 + return has_access(req_access, req_one_access, L) + +/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) + for(var/req in req_access) + if(!(req in accesses)) //doesn't have this access + return 0 + if(req_one_access.len) + for(var/req in req_one_access) + if(req in accesses) //has an access from the single access list + return 1 + return 0 + return 1 + +/proc/get_centcom_access(job) + switch(job) + if("VIP Guest") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) + if("Custodian") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + if("Thunderdome Overseer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER) + if("Emergency Response Team Member") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS) + get_all_accesses() + if("Emergency Response Team Leader") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER) + get_all_accesses() + if("Medical Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + get_all_accesses() + if("Intel Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE) + get_all_accesses() + if("Research Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER) + get_all_accesses() + if("Death Commando") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS) + get_all_accesses() + if("Deathsquad Officer") + return get_all_centcom_access() + get_all_accesses() + if("NT Undercover Operative") + return get_all_centcom_access() + get_all_accesses() + if("Special Operations Officer") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Representative") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Officer") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Captain") + return get_all_centcom_access() + get_all_accesses() + if("Supreme Commander") + return get_all_centcom_access() + get_all_accesses() + +/proc/get_syndicate_access(job) + switch(job) + if("Syndicate Operative") + return list(ACCESS_SYNDICATE) + if("Syndicate Operative Leader") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + if("Syndicate Agent") + return list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS) + if("Vox Raider") + return list(ACCESS_VOX) + if("Vox Trader") + return list(ACCESS_VOX) + if("Syndicate Commando") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + if("Syndicate Officer") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND) + +/proc/get_all_accesses() + return list(ACCESS_MINISAT, ACCESS_AI_UPLOAD, ACCESS_ARMORY, ACCESS_ATMOSPHERICS, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_BLUESHIELD, + ACCESS_HEADS, ACCESS_CAPTAIN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_CHAPEL_OFFICE, ACCESS_CE, ACCESS_CHEMISTRY, ACCESS_CLOWN, ACCESS_CMO, + ACCESS_COURT, ACCESS_CONSTRUCTION, ACCESS_CREMATORIUM, ACCESS_JANITOR, ACCESS_ENGINE, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_FORENSICS_LOCKERS, + ACCESS_GENETICS, ACCESS_GATEWAY, ACCESS_BRIG, ACCESS_HOP, ACCESS_HOS, ACCESS_HYDROPONICS, ACCESS_CHANGE_IDS, ACCESS_KEYCARD_AUTH, ACCESS_KITCHEN, + ACCESS_LAWYER, ACCESS_LIBRARY, ACCESS_MAGISTRATE, ACCESS_MAINT_TUNNELS, ACCESS_HEADS_VAULT, ACCESS_MEDICAL, ACCESS_MECHANIC, ACCESS_MIME, + ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_MORGUE, ACCESS_NETWORK, ACCESS_NTREP, ACCESS_PARAMEDIC, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_ENGINE_EQUIP, ACCESS_PSYCHIATRIST, ACCESS_QM, ACCESS_RD, ACCESS_RC_ANNOUNCE, ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_RESEARCH, ACCESS_SECURITY, ACCESS_PILOT, + ACCESS_SURGERY, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_THEATRE, ACCESS_TCOMSAT, ACCESS_TOX_STORAGE, ACCESS_VIROLOGY, ACCESS_WEAPONS, ACCESS_XENOBIOLOGY, + ACCESS_XENOARCH) + +/proc/get_all_centcom_access() + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SHUTTLES, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS, ACCESS_CENT_THUNDER, ACCESS_CENT_BRIDGE, ACCESS_CENT_COMMANDER) + +/proc/get_all_syndicate_access() + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_VOX, ACCESS_SYNDICATE_COMMAND) + +/proc/get_all_misc_access() + return list(ACCESS_SALVAGE_CAPTAIN, ACCESS_TRADE_SOL, ACCESS_CRATE_CASH, ACCESS_AWAY01) + +/proc/get_absolutely_all_accesses() + return (get_all_accesses() | get_all_centcom_access() | get_all_syndicate_access() | get_all_misc_access()) + +/proc/get_region_accesses(code) + switch(code) + if(REGION_ALL) + return get_all_accesses() + if(REGION_GENERAL) //station general + return list(ACCESS_KITCHEN, ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_LIBRARY, ACCESS_THEATRE, ACCESS_LAWYER, ACCESS_MAGISTRATE, ACCESS_CLOWN, ACCESS_MIME) + if(REGION_SECURITY) //security + return list(ACCESS_SEC_DOORS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_PILOT, ACCESS_HOS) + if(REGION_MEDBAY) //medbay + return list(ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_PSYCHIATRIST, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_CMO, ACCESS_PARAMEDIC) + if(REGION_RESEARCH) //research + return list(ACCESS_RESEARCH, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_GENETICS, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_RD, ACCESS_NETWORK) + if(REGION_ENGINEERING) //engineering and maintenance + return list(ACCESS_CONSTRUCTION, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MINISAT, ACCESS_CE, ACCESS_MECHANIC) + if(REGION_SUPPLY) //supply + return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_QM) + if(REGION_COMMAND) //command + return list(ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HEADS_VAULT, ACCESS_BLUESHIELD, ACCESS_NTREP, ACCESS_HOP, ACCESS_CAPTAIN) + if(REGION_CENTCOMM) //because why the heck not + return get_all_centcom_access() + get_all_accesses() + +/proc/get_region_accesses_name(code) + switch(code) + if(REGION_ALL) + return "All" + if(REGION_GENERAL) //station general + return "General" + if(REGION_SECURITY) //security + return "Security" + if(REGION_MEDBAY) //medbay + return "Medbay" + if(REGION_RESEARCH) //research + return "Research" + if(REGION_ENGINEERING) //engineering and maintenance + return "Engineering" + if(REGION_SUPPLY) //supply + return "Supply" + if(REGION_COMMAND) //command + return "Command" + if(REGION_CENTCOMM) //CC + return "CentComm" + + +/proc/get_access_desc(A) + switch(A) + if(ACCESS_CARGO) + return "Cargo Bay" + if(ACCESS_CARGO_BOT) + return "Cargo Bot Delivery" + if(ACCESS_SECURITY) + return "Security" + if(ACCESS_BRIG) + return "Holding Cells" + if(ACCESS_COURT) + return "Courtroom" + if(ACCESS_FORENSICS_LOCKERS) + return "Forensics" + if(ACCESS_MEDICAL) + return "Medical" + if(ACCESS_GENETICS) + return "Genetics Lab" + if(ACCESS_MORGUE) + return "Morgue" + if(ACCESS_TOX) + return "R&D Lab" + if(ACCESS_TOX_STORAGE) + return "Toxins Lab" + if(ACCESS_CHEMISTRY) + return "Chemistry Lab" + if(ACCESS_RD) + return "Research Director" + if(ACCESS_BAR) + return "Bar" + if(ACCESS_JANITOR) + return "Custodial Closet" + if(ACCESS_ENGINE) + return "Engineering" + if(ACCESS_ENGINE_EQUIP) + return "Power Equipment" + if(ACCESS_MAINT_TUNNELS) + return "Maintenance" + if(ACCESS_EXTERNAL_AIRLOCKS) + return "External Airlocks" + if(ACCESS_EMERGENCY_STORAGE) + return "Emergency Storage" + if(ACCESS_CHANGE_IDS) + return "ID Computer" + if(ACCESS_AI_UPLOAD) + return "AI Upload" + if(ACCESS_TELEPORTER) + return "Teleporter" + if(ACCESS_EVA) + return "EVA" + if(ACCESS_HEADS) + return "Bridge" + if(ACCESS_CAPTAIN) + return "Captain" + if(ACCESS_ALL_PERSONAL_LOCKERS) + return "Personal Lockers" + if(ACCESS_CHAPEL_OFFICE) + return "Chapel Office" + if(ACCESS_TECH_STORAGE) + return "Technical Storage" + if(ACCESS_ATMOSPHERICS) + return "Atmospherics" + if(ACCESS_CREMATORIUM) + return "Crematorium" + if(ACCESS_ARMORY) + return "Armory" + if(ACCESS_CONSTRUCTION) + return "Construction Areas" + if(ACCESS_KITCHEN) + return "Kitchen" + if(ACCESS_HYDROPONICS) + return "Hydroponics" + if(ACCESS_LIBRARY) + return "Library" + if(ACCESS_LAWYER) + return "Law Office" + if(ACCESS_ROBOTICS) + return "Robotics" + if(ACCESS_VIROLOGY) + return "Virology" + if(ACCESS_PSYCHIATRIST) + return "Psychiatrist's Office" + if(ACCESS_CMO) + return "Chief Medical Officer" + if(ACCESS_QM) + return "Quartermaster" + if(ACCESS_CLOWN) + return "Clown's Office" + if(ACCESS_MIME) + return "Mime's Office" + if(ACCESS_SURGERY) + return "Surgery" + if(ACCESS_THEATRE) + return "Theatre" + if(ACCESS_MANUFACTURING) + return "Manufacturing" + if(ACCESS_RESEARCH) + return "Science" + if(ACCESS_MINING) + return "Mining" + if(ACCESS_MINING_OFFICE) + return "Mining Office" + if(ACCESS_MAILSORTING) + return "Cargo Office" + if(ACCESS_MINT) + return "Mint" + if(ACCESS_MINT_VAULT) + return "Mint Vault" + if(ACCESS_HEADS_VAULT) + return "Main Vault" + if(ACCESS_MINING_STATION) + return "Mining EVA" + if(ACCESS_XENOBIOLOGY) + return "Xenobiology Lab" + if(ACCESS_XENOARCH) + return "Xenoarchaeology" + if(ACCESS_HOP) + return "Head of Personnel" + if(ACCESS_HOS) + return "Head of Security" + if(ACCESS_CE) + return "Chief Engineer" + if(ACCESS_RC_ANNOUNCE) + return "RC Announcements" + if(ACCESS_KEYCARD_AUTH) + return "Keycode Auth. Device" + if(ACCESS_TCOMSAT) + return "Telecommunications" + if(ACCESS_NETWORK) + return "Network Access" + if(ACCESS_GATEWAY) + return "Gateway" + if(ACCESS_SEC_DOORS) + return "Brig" + if(ACCESS_BLUESHIELD) + return "Blueshield" + if(ACCESS_NTREP) + return "Nanotrasen Rep." + if(ACCESS_PARAMEDIC) + return "Paramedic" + if(ACCESS_MECHANIC) + return "Mechanic Workshop" + if(ACCESS_PILOT) + return "Security Pod Pilot" + if(ACCESS_MAGISTRATE) + return "Magistrate" + if(ACCESS_MINERAL_STOREROOM) + return "Mineral Storage" + if(ACCESS_MINISAT) + return "AI Satellite" + if(ACCESS_WEAPONS) + return "Weapon Permit" + +/proc/get_centcom_access_desc(A) + switch(A) + if(ACCESS_CENT_GENERAL) + return "General Access" + if(ACCESS_CENT_LIVING) + return "Living Quarters" + if(ACCESS_CENT_MEDICAL) + return "Medical" + if(ACCESS_CENT_SECURITY) + return "Security" + if(ACCESS_CENT_STORAGE) + return "Storage" + if(ACCESS_CENT_SHUTTLES) + return "Shuttles" + if(ACCESS_CENT_TELECOMMS) + return "Telecommunications" + if(ACCESS_CENT_TELEPORTER) + return "Teleporter" + if(ACCESS_CENT_SPECOPS) + return "Special Ops" + if(ACCESS_CENT_SPECOPS_COMMANDER) + return "Special Ops Commander" + if(ACCESS_CENT_BLACKOPS) + return "Black Ops" + if(ACCESS_CENT_THUNDER) + return "Thunderdome" + if(ACCESS_CENT_BRIDGE) + return "Bridge" + if(ACCESS_CENT_COMMANDER) + return "Commander" + +/proc/get_syndicate_access_desc(A) + switch(A) + if(ACCESS_SYNDICATE) + return "Syndicate Operative" + if(ACCESS_SYNDICATE_LEADER) + return "Syndicate Operative Leader" + if(ACCESS_VOX) + return "Vox" + if(ACCESS_SYNDICATE_COMMAND) + return "Syndicate Command" + +/proc/get_all_jobs() + var/list/all_jobs = list() + var/list/all_datums = subtypesof(/datum/job) + all_datums.Remove(list(/datum/job/ai,/datum/job/cyborg)) + var/datum/job/jobdatum + for(var/jobtype in all_datums) + jobdatum = new jobtype + all_jobs.Add(jobdatum.title) + return all_jobs + +/proc/get_all_centcom_jobs() + return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander") + +//gets the actual job rank (ignoring alt titles) +//this is used solely for sechuds +/obj/proc/GetJobRealName() + if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) + return + + var/rank + var/assignment + if(istype(src, /obj/item/pda)) + if(src:id) + rank = src:id:rank + assignment = src:id:assignment + else if(istype(src, /obj/item/card/id)) + rank = src:rank + assignment = src:assignment + + if( rank in GLOB.joblist ) + return rank + + if( assignment in GLOB.joblist ) + return assignment + + return "Unknown" + +//gets the alt title, failing that the actual job rank +//this is unused +/obj/proc/sdsdsd() //GetJobDisplayName + if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) + return + + var/assignment + if(istype(src, /obj/item/pda)) + if(src:id) + assignment = src:id:assignment + else if(istype(src, /obj/item/card/id)) + assignment = src:assignment + + if(assignment) + return assignment + + return "Unknown" + +proc/GetIdCard(var/mob/living/carbon/human/H) + if(H.wear_id) + var/id = H.wear_id.GetID() + if(id) + return id + if(H.get_active_hand()) + var/obj/item/I = H.get_active_hand() + return I.GetID() + +proc/FindNameFromID(var/mob/living/carbon/human/H) + ASSERT(istype(H)) + var/obj/item/card/id/C = H.get_active_hand() + if( istype(C) || istype(C, /obj/item/pda) ) + var/obj/item/card/id/ID = C + + if( istype(C, /obj/item/pda) ) + var/obj/item/pda/pda = C + ID = pda.id + if(!istype(ID)) + ID = null + + if(ID) + return ID.registered_name + + C = H.wear_id + + if( istype(C) || istype(C, /obj/item/pda) ) + var/obj/item/card/id/ID = C + + if( istype(C, /obj/item/pda) ) + var/obj/item/pda/pda = C + ID = pda.id + if(!istype(ID)) + ID = null + + if(ID) + return ID.registered_name + +proc/get_all_job_icons() //For all existing HUD icons + return GLOB.joblist + list("Prisoner") + +/obj/proc/GetJobName() //Used in secHUD icon generation + var/assignmentName = "Unknown" + var/rankName = "Unknown" + if(istype(src, /obj/item/pda)) + var/obj/item/pda/P = src + assignmentName = P.ownjob + rankName = P.ownrank + else if(istype(src, /obj/item/card/id)) + var/obj/item/card/id/I = src + assignmentName = I.assignment + rankName = I.rank + + + var/job_icons = get_all_job_icons() + var/centcom = get_all_centcom_jobs() + + if(assignmentName in centcom) //Return with the NT logo if it is a Centcom job + return "Centcom" + if(rankName in centcom) + return "Centcom" + + if(assignmentName in job_icons) //Check if the job has a hud icon + return assignmentName + if(rankName in job_icons) + return rankName + + return "Unknown" //Return unknown if none of the above apply diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm index d9c30292d820..796db23b36e6 100644 --- a/code/game/jobs/job/central.dm +++ b/code/game/jobs/job/central.dm @@ -100,4 +100,4 @@ . = ..() if(visualsOnly) return - H.mind.offstation_role = TRUE \ No newline at end of file + H.mind.offstation_role = TRUE diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index 757f4b007c27..6adc55a89f1f 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -1,28 +1,28 @@ -/datum/job/civilian - title = "Civilian" - flag = JOB_CIVILIAN - department_flag = JOBCAT_SUPPORT - total_positions = -1 - spawn_positions = -1 - supervisors = "the head of personnel" - department_head = list("Head of Personnel") - selection_color = "#dddddd" - access = list() //See /datum/job/assistant/get_access() - minimal_access = list() //See /datum/job/assistant/get_access() - alt_titles = list("Tourist","Businessman","Trader","Assistant") - outfit = /datum/outfit/job/assistant - -/datum/job/civilian/get_access() - if(config.assistant_maint) - return list(ACCESS_MAINT_TUNNELS) - else - return list() - -/datum/outfit/job/assistant - name = "Civilian" - jobtype = /datum/job/civilian - - uniform = /obj/item/clothing/under/color/random - shoes = /obj/item/clothing/shoes/black - - +/datum/job/civilian + title = "Civilian" + flag = JOB_CIVILIAN + department_flag = JOBCAT_SUPPORT + total_positions = -1 + spawn_positions = -1 + supervisors = "the head of personnel" + department_head = list("Head of Personnel") + selection_color = "#dddddd" + access = list() //See /datum/job/assistant/get_access() + minimal_access = list() //See /datum/job/assistant/get_access() + alt_titles = list("Tourist","Businessman","Trader","Assistant") + outfit = /datum/outfit/job/assistant + +/datum/job/civilian/get_access() + if(config.assistant_maint) + return list(ACCESS_MAINT_TUNNELS) + else + return list() + +/datum/outfit/job/assistant + name = "Civilian" + jobtype = /datum/job/civilian + + uniform = /obj/item/clothing/under/color/random + shoes = /obj/item/clothing/shoes/black + + diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index 6fe3d89fb676..84f903dd2b19 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -1,153 +1,153 @@ -/datum/job/chief_engineer - title = "Chief Engineer" - flag = JOB_CHIEF - department_flag = JOBCAT_ENGSEC - total_positions = 1 - spawn_positions = 1 - is_engineering = 1 - supervisors = "the captain" - department_head = list("Captain") - selection_color = "#ffeeaa" - req_admin_notify = 1 - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_ENGINEERING - outfit = /datum/outfit/job/chief_engineer - -/datum/outfit/job/chief_engineer - name = "Chief Engineer" - jobtype = /datum/job/chief_engineer - - uniform = /obj/item/clothing/under/rank/chief_engineer - belt = /obj/item/storage/belt/utility/chief/full - gloves = /obj/item/clothing/gloves/color/black/ce - shoes = /obj/item/clothing/shoes/brown - head = /obj/item/clothing/head/hardhat/white - l_ear = /obj/item/radio/headset/heads/ce - id = /obj/item/card/id/ce - l_pocket = /obj/item/t_scanner - pda = /obj/item/pda/heads/ce - backpack_contents = list( - /obj/item/melee/classic_baton/telescopic = 1 - ) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer - - -/datum/job/engineer - title = "Station Engineer" - flag = JOB_ENGINEER - department_flag = JOBCAT_ENGSEC - total_positions = 5 - spawn_positions = 5 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM) - alt_titles = list("Maintenance Technician","Engine Technician","Electrician") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/engineer - -/datum/outfit/job/engineer - name = "Station Engineer" - jobtype = /datum/job/engineer - - uniform = /obj/item/clothing/under/rank/engineer - belt = /obj/item/storage/belt/utility/full - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - l_pocket = /obj/item/t_scanner - pda = /obj/item/pda/engineering - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer - - - -/datum/job/atmos - title = "Life Support Specialist" - flag = JOB_ATMOSTECH - department_flag = JOBCAT_ENGSEC - total_positions = 3 - spawn_positions = 2 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_EVA, ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EMERGENCY_STORAGE, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE) - alt_titles = list("Atmospheric Technician") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/atmos - -/datum/outfit/job/atmos - name = "Life Support Specialist" - jobtype = /datum/job/atmos - - uniform = /obj/item/clothing/under/rank/atmospheric_technician - belt = /obj/item/storage/belt/utility/atmostech - shoes = /obj/item/clothing/shoes/workboots - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - pda = /obj/item/pda/atmos - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/atmos - box = /obj/item/storage/box/engineer - -/datum/job/mechanic - title = "Mechanic" - flag = JOB_MECHANIC - department_flag = JOBCAT_KARMA - total_positions = 1 - spawn_positions = 1 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_EMERGENCY_STORAGE, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) - outfit = /datum/outfit/job/mechanic - -/datum/outfit/job/mechanic - name = "Mechanic" - jobtype = /datum/job/mechanic - - uniform = /obj/item/clothing/under/rank/mechanic - belt = /obj/item/storage/belt/utility/full - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - r_pocket = /obj/item/t_scanner - pda = /obj/item/pda/engineering - backpack_contents = list( - /obj/item/pod_paint_bucket = 1 - ) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer +/datum/job/chief_engineer + title = "Chief Engineer" + flag = JOB_CHIEF + department_flag = JOBCAT_ENGSEC + total_positions = 1 + spawn_positions = 1 + is_engineering = 1 + supervisors = "the captain" + department_head = list("Captain") + selection_color = "#ffeeaa" + req_admin_notify = 1 + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_ENGINEERING + outfit = /datum/outfit/job/chief_engineer + +/datum/outfit/job/chief_engineer + name = "Chief Engineer" + jobtype = /datum/job/chief_engineer + + uniform = /obj/item/clothing/under/rank/chief_engineer + belt = /obj/item/storage/belt/utility/chief/full + gloves = /obj/item/clothing/gloves/color/black/ce + shoes = /obj/item/clothing/shoes/brown + head = /obj/item/clothing/head/hardhat/white + l_ear = /obj/item/radio/headset/heads/ce + id = /obj/item/card/id/ce + l_pocket = /obj/item/t_scanner + pda = /obj/item/pda/heads/ce + backpack_contents = list( + /obj/item/melee/classic_baton/telescopic = 1 + ) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer + + +/datum/job/engineer + title = "Station Engineer" + flag = JOB_ENGINEER + department_flag = JOBCAT_ENGSEC + total_positions = 5 + spawn_positions = 5 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Maintenance Technician","Engine Technician","Electrician") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/engineer + +/datum/outfit/job/engineer + name = "Station Engineer" + jobtype = /datum/job/engineer + + uniform = /obj/item/clothing/under/rank/engineer + belt = /obj/item/storage/belt/utility/full + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + l_pocket = /obj/item/t_scanner + pda = /obj/item/pda/engineering + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer + + + +/datum/job/atmos + title = "Life Support Specialist" + flag = JOB_ATMOSTECH + department_flag = JOBCAT_ENGSEC + total_positions = 3 + spawn_positions = 2 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EMERGENCY_STORAGE, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE) + alt_titles = list("Atmospheric Technician") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/atmos + +/datum/outfit/job/atmos + name = "Life Support Specialist" + jobtype = /datum/job/atmos + + uniform = /obj/item/clothing/under/rank/atmospheric_technician + belt = /obj/item/storage/belt/utility/atmostech + shoes = /obj/item/clothing/shoes/workboots + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + pda = /obj/item/pda/atmos + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/atmos + box = /obj/item/storage/box/engineer + +/datum/job/mechanic + title = "Mechanic" + flag = JOB_MECHANIC + department_flag = JOBCAT_KARMA + total_positions = 1 + spawn_positions = 1 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_EMERGENCY_STORAGE, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) + outfit = /datum/outfit/job/mechanic + +/datum/outfit/job/mechanic + name = "Mechanic" + jobtype = /datum/job/mechanic + + uniform = /obj/item/clothing/under/rank/mechanic + belt = /obj/item/storage/belt/utility/full + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + r_pocket = /obj/item/t_scanner + pda = /obj/item/pda/engineering + backpack_contents = list( + /obj/item/pod_paint_bucket = 1 + ) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 9def19d29700..ad8f3bf65354 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -1,272 +1,275 @@ -/datum/job - - //The name of the job - var/title = "NOPE" - - //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access - var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) - var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - - //Bitflags for the job - var/flag = 0 - var/department_flag = 0 - var/department_head = list() - - //Players will be allowed to spawn in as jobs that are set to "Station" - var/list/faction = list("Station") - - //How many players can be this job - var/total_positions = 0 - - //How many players can spawn in as this job - var/spawn_positions = 0 - - //How many players have this job - var/current_positions = 0 - - //Supervisors, who this person answers to directly - var/supervisors = "" - - //Sellection screen color - var/selection_color = "#ffffff" - - //List of alternate titles, if any - var/list/alt_titles - - //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. - var/req_admin_notify - - //Various Departmental identifiers - var/is_supply - var/is_service - var/is_command - var/is_legal - var/is_engineering - var/is_medical - var/is_science - var/is_security - - //If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) - var/minimal_player_age = 0 - - var/exp_requirements = 0 - var/exp_type = "" - - var/disabilities_allowed = 1 - - var/admin_only = 0 - var/spawn_ert = 0 - var/syndicate_command = 0 - - var/outfit = null - - ///////////////////////////////// - // /vg/ feature: Job Objectives! - ///////////////////////////////// - var/required_objectives=list() // Objectives that are ALWAYS added. - var/optional_objectives=list() // Objectives that are SOMETIMES added. - -//Only override this proc -/datum/job/proc/after_spawn(mob/living/carbon/human/H) - -/datum/job/proc/announce(mob/living/carbon/human/H) - -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE) - if(!H) - return 0 - - H.dna.species.before_equip_job(src, H, visualsOnly) - - if(outfit) - H.equipOutfit(outfit, visualsOnly) - - H.dna.species.after_equip_job(src, H, visualsOnly) - - if(!visualsOnly && announce) - announce(H) - -/datum/job/proc/get_access() - if(!config) //Needed for robots. - return src.minimal_access.Copy() - - if(config.jobs_have_minimal_access) - return src.minimal_access.Copy() - else - return src.access.Copy() - -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/job/proc/player_old_enough(client/C) - if(available_in_days(C) == 0) - return 1 //Available in 0 days = available right now = player is old enough to play. - return 0 - - -/datum/job/proc/available_in_days(client/C) - if(!C) - return 0 - if(!config.use_age_restriction_for_jobs) - return 0 - if(!isnum(C.player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - if(!isnum(minimal_player_age)) - return 0 - - return max(0, minimal_player_age - C.player_age) - -/datum/job/proc/barred_by_disability(client/C) - if(!C) - return 0 - if(disabilities_allowed) - return 0 - var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY) - for(var/i = 1, i < prohibited_disabilities.len, i++) - var/this_disability = prohibited_disabilities[i] - if(C.prefs.disabilities & this_disability) - return 1 - return 0 - -/datum/job/proc/is_position_available() - return (current_positions < total_positions) || (total_positions == -1) - -/datum/outfit/job - name = "Standard Gear" - collect_not_del = TRUE // we don't want anyone to lose their job shit - - var/allow_loadout = TRUE - var/allow_backbag_choice = TRUE - var/jobtype = null - - uniform = /obj/item/clothing/under/color/grey - id = /obj/item/card/id - l_ear = /obj/item/radio/headset - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/black - pda = /obj/item/pda - - var/backpack = /obj/item/storage/backpack - var/satchel = /obj/item/storage/backpack/satchel_norm - var/dufflebag = /obj/item/storage/backpack/duffel - box = /obj/item/storage/box/survival - - var/tmp/list/gear_leftovers = list() - -/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(allow_backbag_choice) - switch(H.backbag) - if(GBACKPACK) - back = /obj/item/storage/backpack //Grey backpack - if(GSATCHEL) - back = /obj/item/storage/backpack/satchel_norm //Grey satchel - if(GDUFFLEBAG) - back = /obj/item/storage/backpack/duffel //Grey Dufflebag - if(LSATCHEL) - back = /obj/item/storage/backpack/satchel //Leather Satchel - if(DSATCHEL) - back = satchel //Department satchel - if(DDUFFLEBAG) - back = dufflebag //Department dufflebag - else - back = backpack //Department backpack - - if(box && H.dna.species.speciesbox) - box = H.dna.species.speciesbox - - if(allow_loadout && H.client && (H.client.prefs.gear && H.client.prefs.gear.len)) - for(var/gear in H.client.prefs.gear) - var/datum/gear/G = gear_datums[gear] - if(G) - var/permitted = FALSE - - if(G.allowed_roles) - if(name in G.allowed_roles) - permitted = TRUE - else - permitted = TRUE - - if(G.whitelisted && (G.whitelisted != H.dna.species.name || !is_alien_whitelisted(H, G.whitelisted))) - permitted = FALSE - - if(!permitted) - to_chat(H, "Your current job or whitelist status does not permit you to spawn with [gear]!") - continue - - if(G.slot) - if(H.equip_to_slot_or_del(G.spawn_item(H), G.slot)) - to_chat(H, "Equipping you with [gear]!") - else - gear_leftovers += G - else - gear_leftovers += G - -/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(visualsOnly) - return - - imprint_idcard(H) - - H.sec_hud_set_ID() - - imprint_pda(H) - - if(gear_leftovers.len) - for(var/datum/gear/G in gear_leftovers) - var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.gear[G.display_name])) - if(istype(placed_in)) - if(isturf(placed_in)) - to_chat(H, "Placing [G.display_name] on [placed_in]!") - else - to_chat(H, "Placing [G.display_name] in [placed_in.name].") - continue - if(H.equip_to_appropriate_slot(G)) - to_chat(H, "Placing [G.display_name] in your inventory!") - continue - if(H.put_in_hands(G)) - to_chat(H, "Placing [G.display_name] in your hands!") - continue - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") - qdel(G) - - qdel(gear_leftovers) - - return 1 - -/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) - var/datum/job/J = SSjobs.GetJobType(jobtype) - if(!J) - J = SSjobs.GetJob(H.job) - - var/alt_title - if(H.mind) - alt_title = H.mind.role_alt_title - - var/obj/item/card/id/C = H.wear_id - if(istype(C)) - C.access = J.get_access() - C.registered_name = H.real_name - C.rank = J.title - C.assignment = alt_title ? alt_title : J.title - C.sex = capitalize(H.gender) - C.age = H.age - C.name = "[C.registered_name]'s ID Card ([C.assignment])" - C.photo = get_id_photo(H) - - if(H.mind && H.mind.initial_account) - C.associated_account_number = H.mind.initial_account.account_number - C.owner_uid = H.UID() - C.owner_ckey = H.ckey - -/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H) - var/obj/item/pda/PDA = H.wear_pda - var/obj/item/card/id/C = H.wear_id - if(istype(PDA) && istype(C)) - PDA.owner = H.real_name - PDA.ownjob = C.assignment - PDA.ownrank = C.rank - PDA.name = "PDA-[H.real_name] ([PDA.ownjob])" - -/datum/job/proc/would_accept_job_transfer_from_player(mob/player) - if(!guest_jobbans(title)) // actually checks if job is a whitelisted position - return TRUE - if(!istype(player)) - return FALSE - return is_job_whitelisted(player, title) +/datum/job + + //The name of the job + var/title = "NOPE" + + //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access + var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) + var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) + + //Bitflags for the job + var/flag = 0 + var/department_flag = 0 + var/department_head = list() + + //Players will be allowed to spawn in as jobs that are set to "Station" + var/list/faction = list("Station") + + //How many players can be this job + var/total_positions = 0 + + //How many players can spawn in as this job + var/spawn_positions = 0 + + //How many players have this job + var/current_positions = 0 + + //Supervisors, who this person answers to directly + var/supervisors = "" + + //Sellection screen color + var/selection_color = "#ffffff" + + //List of alternate titles, if any + var/list/alt_titles + + //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. + var/req_admin_notify + + //Various Departmental identifiers + var/is_supply + var/is_service + var/is_command + var/is_legal + var/is_engineering + var/is_medical + var/is_science + var/is_security + + //If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) + var/minimal_player_age = 0 + + var/exp_requirements = 0 + var/exp_type = "" + + var/disabilities_allowed = 1 + var/transfer_allowed = TRUE // If false, ID computer will always discourage transfers to this job, even if player is eligible + + var/admin_only = 0 + var/spawn_ert = 0 + var/syndicate_command = 0 + + var/outfit = null + + ///////////////////////////////// + // /vg/ feature: Job Objectives! + ///////////////////////////////// + var/required_objectives=list() // Objectives that are ALWAYS added. + var/optional_objectives=list() // Objectives that are SOMETIMES added. + +//Only override this proc +/datum/job/proc/after_spawn(mob/living/carbon/human/H) + +/datum/job/proc/announce(mob/living/carbon/human/H) + +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE) + if(!H) + return 0 + + H.dna.species.before_equip_job(src, H, visualsOnly) + + if(outfit) + H.equipOutfit(outfit, visualsOnly) + + H.dna.species.after_equip_job(src, H, visualsOnly) + + if(!visualsOnly && announce) + announce(H) + +/datum/job/proc/get_access() + if(!config) //Needed for robots. + return src.minimal_access.Copy() + + if(config.jobs_have_minimal_access) + return src.minimal_access.Copy() + else + return src.access.Copy() + +//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 +/datum/job/proc/player_old_enough(client/C) + if(available_in_days(C) == 0) + return 1 //Available in 0 days = available right now = player is old enough to play. + return 0 + + +/datum/job/proc/available_in_days(client/C) + if(!C) + return 0 + if(!config.use_age_restriction_for_jobs) + return 0 + if(!isnum(C.player_age)) + return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced + if(!isnum(minimal_player_age)) + return 0 + + return max(0, minimal_player_age - C.player_age) + +/datum/job/proc/barred_by_disability(client/C) + if(!C) + return 0 + if(disabilities_allowed) + return 0 + var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY) + for(var/i = 1, i < prohibited_disabilities.len, i++) + var/this_disability = prohibited_disabilities[i] + if(C.prefs.disabilities & this_disability) + return 1 + return 0 + +/datum/job/proc/is_position_available() + return (current_positions < total_positions) || (total_positions == -1) + +/datum/outfit/job + name = "Standard Gear" + collect_not_del = TRUE // we don't want anyone to lose their job shit + + var/allow_loadout = TRUE + var/allow_backbag_choice = TRUE + var/jobtype = null + + uniform = /obj/item/clothing/under/color/grey + id = /obj/item/card/id + l_ear = /obj/item/radio/headset + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/black + pda = /obj/item/pda + + var/backpack = /obj/item/storage/backpack + var/satchel = /obj/item/storage/backpack/satchel_norm + var/dufflebag = /obj/item/storage/backpack/duffel + box = /obj/item/storage/box/survival + + var/tmp/list/gear_leftovers = list() + +/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(allow_backbag_choice) + switch(H.backbag) + if(GBACKPACK) + back = /obj/item/storage/backpack //Grey backpack + if(GSATCHEL) + back = /obj/item/storage/backpack/satchel_norm //Grey satchel + if(GDUFFLEBAG) + back = /obj/item/storage/backpack/duffel //Grey Dufflebag + if(LSATCHEL) + back = /obj/item/storage/backpack/satchel //Leather Satchel + if(DSATCHEL) + back = satchel //Department satchel + if(DDUFFLEBAG) + back = dufflebag //Department dufflebag + else + back = backpack //Department backpack + + if(box && H.dna.species.speciesbox) + box = H.dna.species.speciesbox + + if(allow_loadout && H.client && (H.client.prefs.gear && H.client.prefs.gear.len)) + for(var/gear in H.client.prefs.gear) + var/datum/gear/G = GLOB.gear_datums[gear] + if(G) + var/permitted = FALSE + + if(G.allowed_roles) + if(name in G.allowed_roles) + permitted = TRUE + else + permitted = TRUE + + if(G.whitelisted && (G.whitelisted != H.dna.species.name || !is_alien_whitelisted(H, G.whitelisted))) + permitted = FALSE + + if(!permitted) + to_chat(H, "Your current job or whitelist status does not permit you to spawn with [gear]!") + continue + + if(G.slot) + if(H.equip_to_slot_or_del(G.spawn_item(H), G.slot)) + to_chat(H, "Equipping you with [gear]!") + else + gear_leftovers += G + else + gear_leftovers += G + +/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(visualsOnly) + return + + imprint_idcard(H) + + H.sec_hud_set_ID() + + imprint_pda(H) + + if(gear_leftovers.len) + for(var/datum/gear/G in gear_leftovers) + var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.gear[G.display_name])) + if(istype(placed_in)) + if(isturf(placed_in)) + to_chat(H, "Placing [G.display_name] on [placed_in]!") + else + to_chat(H, "Placing [G.display_name] in [placed_in.name].") + continue + if(H.equip_to_appropriate_slot(G)) + to_chat(H, "Placing [G.display_name] in your inventory!") + continue + if(H.put_in_hands(G)) + to_chat(H, "Placing [G.display_name] in your hands!") + continue + to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") + qdel(G) + + qdel(gear_leftovers) + + return 1 + +/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) + var/datum/job/J = SSjobs.GetJobType(jobtype) + if(!J) + J = SSjobs.GetJob(H.job) + + var/alt_title + if(H.mind) + alt_title = H.mind.role_alt_title + + var/obj/item/card/id/C = H.wear_id + if(istype(C)) + C.access = J.get_access() + C.registered_name = H.real_name + C.rank = J.title + C.assignment = alt_title ? alt_title : J.title + C.sex = capitalize(H.gender) + C.age = H.age + C.name = "[C.registered_name]'s ID Card ([C.assignment])" + C.photo = get_id_photo(H) + + if(H.mind && H.mind.initial_account) + C.associated_account_number = H.mind.initial_account.account_number + C.owner_uid = H.UID() + C.owner_ckey = H.ckey + +/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H) + var/obj/item/pda/PDA = H.wear_pda + var/obj/item/card/id/C = H.wear_id + if(istype(PDA) && istype(C)) + PDA.owner = H.real_name + PDA.ownjob = C.assignment + PDA.ownrank = C.rank + PDA.name = "PDA-[H.real_name] ([PDA.ownjob])" + +/datum/job/proc/would_accept_job_transfer_from_player(mob/player) + if(!transfer_allowed) + return FALSE + if(!guest_jobbans(title)) // actually checks if job is a whitelisted position + return TRUE + if(!istype(player)) + return FALSE + return is_job_whitelisted(player, title) diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 425cd9252e59..259afa12c9e6 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,318 +1,318 @@ -/datum/job/cmo - title = "Chief Medical Officer" - flag = JOB_CMO - department_flag = JOBCAT_MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the captain" - department_head = list("Captain") - selection_color = "#ffddf0" - req_admin_notify = 1 - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_EVA, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_MEDICAL - outfit = /datum/outfit/job/cmo - -/datum/outfit/job/cmo - name = "Chief Medical Officer" - jobtype = /datum/job/cmo - - uniform = /obj/item/clothing/under/rank/chief_medical_officer - suit = /obj/item/clothing/suit/storage/labcoat/cmo - shoes = /obj/item/clothing/shoes/brown - l_ear = /obj/item/radio/headset/heads/cmo - id = /obj/item/card/id/cmo - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv - pda = /obj/item/pda/heads/cmo - backpack_contents = list( - /obj/item/melee/classic_baton/telescopic = 1 - ) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - -/datum/job/doctor - title = "Medical Doctor" - flag = JOB_DOCTOR - department_flag = JOBCAT_MEDSCI - total_positions = 5 - spawn_positions = 3 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MAINT_TUNNELS) - alt_titles = list("Surgeon","Nurse") - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/doctor - -/datum/outfit/job/doctor - name = "Medical Doctor" - jobtype = /datum/job/doctor - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/labcoat - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv - pda = /obj/item/pda/medical - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - -/datum/job/coroner - title = "Coroner" - flag = JOB_CORONER - department_flag = JOBCAT_MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/coroner - -/datum/outfit/job/coroner - name = "Coroner" - jobtype = /datum/job/coroner - - uniform = /obj/item/clothing/under/rank/medical/mortician - suit = /obj/item/clothing/suit/storage/labcoat/mortician - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/clipboard - pda = /obj/item/pda/medical - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - - backpack_contents = list( - /obj/item/clothing/head/surgery/black = 1, - /obj/item/autopsy_scanner = 1, - /obj/item/reagent_scanner = 1, - /obj/item/storage/box/bodybags = 1) - -/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(H.mind && H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Surgeon") - uniform = /obj/item/clothing/under/rank/medical/blue - head = /obj/item/clothing/head/surgery/blue - if("Medical Doctor") - uniform = /obj/item/clothing/under/rank/medical - if("Nurse") - if(H.gender == FEMALE) - if(prob(50)) - uniform = /obj/item/clothing/under/rank/nursesuit - else - uniform = /obj/item/clothing/under/rank/nurse - head = /obj/item/clothing/head/nursehat - else - uniform = /obj/item/clothing/under/rank/medical/purple - - - -//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro -/datum/job/chemist - title = "Chemist" - flag = JOB_CHEMIST - department_flag = JOBCAT_MEDSCI - total_positions = 2 - spawn_positions = 2 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_CHEMISTRY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - alt_titles = list("Pharmacist","Pharmacologist") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/chemist - -/datum/outfit/job/chemist - name = "Chemist" - jobtype = /datum/job/chemist - - uniform = /obj/item/clothing/under/rank/chemist - suit = /obj/item/clothing/suit/storage/labcoat/chemist - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - glasses = /obj/item/clothing/glasses/science - id = /obj/item/card/id/medical - pda = /obj/item/pda/chemist - - backpack = /obj/item/storage/backpack/chemistry - satchel = /obj/item/storage/backpack/satchel_chem - dufflebag = /obj/item/storage/backpack/duffel/chemistry - -/datum/job/geneticist - title = "Geneticist" - flag = JOB_GENETICIST - department_flag = JOBCAT_MEDSCI - total_positions = 2 - spawn_positions = 2 - is_medical = 1 - supervisors = "the chief medical officer and the research director" - department_head = list("Chief Medical Officer", "Research Director") - selection_color = "#ffeef0" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) - minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MAINT_TUNNELS) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/geneticist - -/datum/outfit/job/geneticist - name = "Geneticist" - jobtype = /datum/job/geneticist - - uniform = /obj/item/clothing/under/rank/geneticist - suit = /obj/item/clothing/suit/storage/labcoat/genetics - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_medsci - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/geneticist - - backpack = /obj/item/storage/backpack/genetics - satchel = /obj/item/storage/backpack/satchel_gen - dufflebag = /obj/item/storage/backpack/duffel/genetics - - -/datum/job/virologist - title = "Virologist" - flag = JOB_VIROLOGIST - department_flag = JOBCAT_MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS) - minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) - alt_titles = list("Pathologist","Microbiologist") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/virologist - -/datum/outfit/job/virologist - name = "Virologist" - jobtype = /datum/job/virologist - - uniform = /obj/item/clothing/under/rank/virologist - suit = /obj/item/clothing/suit/storage/labcoat/virologist - shoes = /obj/item/clothing/shoes/white - mask = /obj/item/clothing/mask/surgical - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/viro - - backpack = /obj/item/storage/backpack/virology - satchel = /obj/item/storage/backpack/satchel_vir - dufflebag = /obj/item/storage/backpack/duffel/virology - -/datum/job/psychiatrist - title = "Psychiatrist" - flag = JOB_PSYCHIATRIST - department_flag = JOBCAT_MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_PSYCHIATRIST) - minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS) - alt_titles = list("Psychologist","Therapist") - outfit = /datum/outfit/job/psychiatrist - -/datum/outfit/job/psychiatrist - name = "Psychiatrist" - jobtype = /datum/job/psychiatrist - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/labcoat - shoes = /obj/item/clothing/shoes/laceup - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/medical - -/datum/outfit/job/psychiatrist/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(H.mind && H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Psychiatrist") - uniform = /obj/item/clothing/under/rank/psych - if("Psychologist") - uniform = /obj/item/clothing/under/rank/psych/turtleneck - if("Therapist") - uniform = /obj/item/clothing/under/rank/medical - -/datum/job/paramedic - title = "Paramedic" - flag = JOB_PARAMEDIC - department_flag = JOBCAT_MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) - minimal_access=list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/paramedic - -/datum/outfit/job/paramedic - name = "Paramedic" - jobtype = /datum/job/paramedic - - uniform = /obj/item/clothing/under/rank/medical/paramedic - shoes = /obj/item/clothing/shoes/black - head = /obj/item/clothing/head/soft/blue - mask = /obj/item/clothing/mask/cigarette - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - l_pocket = /obj/item/flashlight/pen - pda = /obj/item/pda/medical - backpack_contents = list( - /obj/item/healthanalyzer = 1 - ) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - box = /obj/item/storage/box/engineer - +/datum/job/cmo + title = "Chief Medical Officer" + flag = JOB_CMO + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the captain" + department_head = list("Captain") + selection_color = "#ffddf0" + req_admin_notify = 1 + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_MEDICAL + outfit = /datum/outfit/job/cmo + +/datum/outfit/job/cmo + name = "Chief Medical Officer" + jobtype = /datum/job/cmo + + uniform = /obj/item/clothing/under/rank/chief_medical_officer + suit = /obj/item/clothing/suit/storage/labcoat/cmo + shoes = /obj/item/clothing/shoes/brown + l_ear = /obj/item/radio/headset/heads/cmo + id = /obj/item/card/id/cmo + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/storage/firstaid/adv + pda = /obj/item/pda/heads/cmo + backpack_contents = list( + /obj/item/melee/classic_baton/telescopic = 1 + ) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/job/doctor + title = "Medical Doctor" + flag = JOB_DOCTOR + department_flag = JOBCAT_MEDSCI + total_positions = 5 + spawn_positions = 3 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MAINT_TUNNELS) + alt_titles = list("Surgeon","Nurse") + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/doctor + +/datum/outfit/job/doctor + name = "Medical Doctor" + jobtype = /datum/job/doctor + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/labcoat + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/storage/firstaid/adv + pda = /obj/item/pda/medical + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/job/coroner + title = "Coroner" + flag = JOB_CORONER + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/coroner + +/datum/outfit/job/coroner + name = "Coroner" + jobtype = /datum/job/coroner + + uniform = /obj/item/clothing/under/rank/medical/mortician + suit = /obj/item/clothing/suit/storage/labcoat/mortician + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/clipboard + pda = /obj/item/pda/medical + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + + backpack_contents = list( + /obj/item/clothing/head/surgery/black = 1, + /obj/item/autopsy_scanner = 1, + /obj/item/reagent_scanner = 1, + /obj/item/storage/box/bodybags = 1) + +/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(H.mind && H.mind.role_alt_title) + switch(H.mind.role_alt_title) + if("Surgeon") + uniform = /obj/item/clothing/under/rank/medical/blue + head = /obj/item/clothing/head/surgery/blue + if("Medical Doctor") + uniform = /obj/item/clothing/under/rank/medical + if("Nurse") + if(H.gender == FEMALE) + if(prob(50)) + uniform = /obj/item/clothing/under/rank/nursesuit + else + uniform = /obj/item/clothing/under/rank/nurse + head = /obj/item/clothing/head/nursehat + else + uniform = /obj/item/clothing/under/rank/medical/purple + + + +//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro +/datum/job/chemist + title = "Chemist" + flag = JOB_CHEMIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_CHEMISTRY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Pharmacist","Pharmacologist") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/chemist + +/datum/outfit/job/chemist + name = "Chemist" + jobtype = /datum/job/chemist + + uniform = /obj/item/clothing/under/rank/chemist + suit = /obj/item/clothing/suit/storage/labcoat/chemist + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + glasses = /obj/item/clothing/glasses/science + id = /obj/item/card/id/medical + pda = /obj/item/pda/chemist + + backpack = /obj/item/storage/backpack/chemistry + satchel = /obj/item/storage/backpack/satchel_chem + dufflebag = /obj/item/storage/backpack/duffel/chemistry + +/datum/job/geneticist + title = "Geneticist" + flag = JOB_GENETICIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + is_medical = 1 + supervisors = "the chief medical officer and the research director" + department_head = list("Chief Medical Officer", "Research Director") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MAINT_TUNNELS) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/geneticist + +/datum/outfit/job/geneticist + name = "Geneticist" + jobtype = /datum/job/geneticist + + uniform = /obj/item/clothing/under/rank/geneticist + suit = /obj/item/clothing/suit/storage/labcoat/genetics + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_medsci + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/geneticist + + backpack = /obj/item/storage/backpack/genetics + satchel = /obj/item/storage/backpack/satchel_gen + dufflebag = /obj/item/storage/backpack/duffel/genetics + + +/datum/job/virologist + title = "Virologist" + flag = JOB_VIROLOGIST + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS) + minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Pathologist","Microbiologist") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/virologist + +/datum/outfit/job/virologist + name = "Virologist" + jobtype = /datum/job/virologist + + uniform = /obj/item/clothing/under/rank/virologist + suit = /obj/item/clothing/suit/storage/labcoat/virologist + shoes = /obj/item/clothing/shoes/white + mask = /obj/item/clothing/mask/surgical + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/viro + + backpack = /obj/item/storage/backpack/virology + satchel = /obj/item/storage/backpack/satchel_vir + dufflebag = /obj/item/storage/backpack/duffel/virology + +/datum/job/psychiatrist + title = "Psychiatrist" + flag = JOB_PSYCHIATRIST + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_PSYCHIATRIST) + minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS) + alt_titles = list("Psychologist","Therapist") + outfit = /datum/outfit/job/psychiatrist + +/datum/outfit/job/psychiatrist + name = "Psychiatrist" + jobtype = /datum/job/psychiatrist + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/labcoat + shoes = /obj/item/clothing/shoes/laceup + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/medical + +/datum/outfit/job/psychiatrist/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(H.mind && H.mind.role_alt_title) + switch(H.mind.role_alt_title) + if("Psychiatrist") + uniform = /obj/item/clothing/under/rank/psych + if("Psychologist") + uniform = /obj/item/clothing/under/rank/psych/turtleneck + if("Therapist") + uniform = /obj/item/clothing/under/rank/medical + +/datum/job/paramedic + title = "Paramedic" + flag = JOB_PARAMEDIC + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) + minimal_access=list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/paramedic + +/datum/outfit/job/paramedic + name = "Paramedic" + jobtype = /datum/job/paramedic + + uniform = /obj/item/clothing/under/rank/medical/paramedic + shoes = /obj/item/clothing/shoes/black + head = /obj/item/clothing/head/soft/blue + mask = /obj/item/clothing/mask/cigarette + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + l_pocket = /obj/item/flashlight/pen + pda = /obj/item/pda/medical + backpack_contents = list( + /obj/item/healthanalyzer = 1 + ) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + box = /obj/item/storage/box/engineer + diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index d314f016a9ae..64e69e897655 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -149,7 +149,7 @@ if(visualsOnly) return - H.dna.SetSEState(SOBERBLOCK,1) + H.dna.SetSEState(GLOB.soberblock,1) H.mutations += SOBER H.check_mutations = 1 @@ -259,4 +259,4 @@ backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security - box = /obj/item/storage/box/engineer \ No newline at end of file + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index 289b441b4d7e..b63217658619 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,40 +1,40 @@ -/datum/job/ai - title = "AI" - flag = JOB_AI - department_flag = JOBCAT_ENGSEC - total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm - spawn_positions = 1 - selection_color = "#ccffcc" - supervisors = "your laws" - department_head = list("Captain") - req_admin_notify = 1 - minimal_player_age = 30 - exp_requirements = 300 - exp_type = EXP_TYPE_SILICON - -/datum/job/ai/equip(mob/living/carbon/human/H) - if(!H) - return 0 - -/datum/job/ai/is_position_available() - return (empty_playable_ai_cores.len != 0) - - -/datum/job/cyborg - title = "Cyborg" - flag = JOB_CYBORG - department_flag = JOBCAT_ENGSEC - total_positions = 1 - spawn_positions = 1 - supervisors = "your laws and the AI" //Nodrak - department_head = list("AI") - selection_color = "#ddffdd" - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - alt_titles = list("Android", "Robot") - -/datum/job/cyborg/equip(mob/living/carbon/human/H) - if(!H) - return 0 - return H.Robotize() \ No newline at end of file +/datum/job/ai + title = "AI" + flag = JOB_AI + department_flag = JOBCAT_ENGSEC + total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm + spawn_positions = 1 + selection_color = "#ccffcc" + supervisors = "your laws" + department_head = list("Captain") + req_admin_notify = 1 + minimal_player_age = 30 + exp_requirements = 300 + exp_type = EXP_TYPE_SILICON + +/datum/job/ai/equip(mob/living/carbon/human/H) + if(!H) + return 0 + +/datum/job/ai/is_position_available() + return (GLOB.empty_playable_ai_cores.len != 0) + + +/datum/job/cyborg + title = "Cyborg" + flag = JOB_CYBORG + department_flag = JOBCAT_ENGSEC + total_positions = 1 + spawn_positions = 1 + supervisors = "your laws and the AI" //Nodrak + department_head = list("AI") + selection_color = "#ddffdd" + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + alt_titles = list("Android", "Robot") + +/datum/job/cyborg/equip(mob/living/carbon/human/H) + if(!H) + return 0 + return H.Robotize() diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index 4e773d42ad98..c56d27bb4081 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -1,4 +1,4 @@ -var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) +GLOBAL_DATUM_INIT(captain_announcement, /datum/announcement/minor, new(do_newscast = 0)) // Why the hell are captain announcements minor /datum/job/captain title = "Captain" flag = JOB_CAPTAIN @@ -23,7 +23,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/captain/announce(mob/living/carbon/human/H) . = ..() - captain_announcement.Announce("All hands, Captain [H.real_name] on deck!") + GLOB.captain_announcement.Announce("All hands, Captain [H.real_name] on deck!") /datum/outfit/job/captain name = "Captain" @@ -112,6 +112,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) selection_color = "#ddddff" req_admin_notify = 1 is_command = 1 + transfer_allowed = FALSE minimal_player_age = 21 access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, @@ -155,6 +156,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) selection_color = "#ddddff" req_admin_notify = 1 is_command = 1 + transfer_allowed = FALSE minimal_player_age = 21 access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, @@ -198,6 +200,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) selection_color = "#ddddff" req_admin_notify = 1 is_legal = 1 + transfer_allowed = FALSE minimal_player_age = 30 access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, @@ -230,7 +233,7 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) -//var/global/lawyer = 0//Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. +//GLOBAL_VAR_INIT(lawyer, 0) //Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. | This was deprecated back in 2014, and its now 2020 /datum/job/lawyer title = "Internal Affairs Agent" flag = JOB_LAWYER diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index 7d4f5ccc6226..d0064300b627 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -33,9 +33,9 @@ if(visualsOnly) return - H.dna.SetSEState(SOBERBLOCK,1) - genemutcheck(H, SOBERBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(SOBERBLOCK) + H.dna.SetSEState(GLOB.soberblock,1) + genemutcheck(H, GLOB.soberblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.soberblock) H.check_mutations = 1 @@ -286,13 +286,13 @@ var/obj/item/organ/internal/cyberimp/brain/clown_voice/implant = new implant.insert(H) - H.dna.SetSEState(CLUMSYBLOCK, TRUE) - genemutcheck(H, CLUMSYBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(CLUMSYBLOCK) + H.dna.SetSEState(GLOB.clumsyblock, TRUE) + genemutcheck(H, GLOB.clumsyblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.clumsyblock) if(!ismachine(H)) - H.dna.SetSEState(COMICBLOCK, TRUE) - genemutcheck(H, COMICBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(COMICBLOCK) + H.dna.SetSEState(GLOB.comicblock, TRUE) + genemutcheck(H, GLOB.comicblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.comicblock) H.check_mutations = TRUE H.add_language("Clownish") diff --git a/code/game/jobs/job/syndicate.dm b/code/game/jobs/job/syndicate.dm index 44a04ef7593c..f1bf52cbc8a8 100644 --- a/code/game/jobs/job/syndicate.dm +++ b/code/game/jobs/job/syndicate.dm @@ -51,8 +51,8 @@ U.implant(H) U.hidden_uplink.uses = 500 H.faction += "syndicate" - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] opshud.join_hud(H.mind.current) H.mind.offstation_role = TRUE set_antag_hud(H.mind.current, "hudoperative") - H.regenerate_icons() \ No newline at end of file + H.regenerate_icons() diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm index b69dbfd3ed4b..f0e6f9bff427 100644 --- a/code/game/jobs/job_exp.dm +++ b/code/game/jobs/job_exp.dm @@ -1,6 +1,6 @@ // Playtime requirements for special roles (hours) -var/global/list/role_playtime_requirements = list( +GLOBAL_LIST_INIT(role_playtime_requirements, list( // NT ROLES ROLE_PAI = 0, ROLE_POSIBRAIN = 5, // Same as cyborg job. @@ -35,7 +35,7 @@ var/global/list/role_playtime_requirements = list( ROLE_RAIDER = 10, ROLE_ALIEN = 10, ROLE_ABDUCTOR = 10, -) +)) // Client Verbs @@ -113,7 +113,7 @@ var/global/list/role_playtime_requirements = list( var/isexempt = text2num(play_records[EXP_TYPE_EXEMPT]) if(isexempt) return 0 - var/minimal_player_hrs = role_playtime_requirements[role] + var/minimal_player_hrs = GLOB.role_playtime_requirements[role] if(!minimal_player_hrs) return 0 var/req_mins = minimal_player_hrs * 60 @@ -163,7 +163,7 @@ var/global/list/role_playtime_requirements = list( return "[key] has no records." var/return_text = "
          " var/list/exp_data = list() - for(var/category in exp_jobsmap) + for(var/category in GLOB.exp_jobsmap) if(text2num(play_records[category])) exp_data[category] = text2num(play_records[category]) else @@ -238,7 +238,7 @@ var/global/list/role_playtime_requirements = list( /client/proc/update_exp_client(var/minutes, var/announce_changes = 0) if(!src ||!ckey) return - var/DBQuery/exp_read = dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'") + var/DBQuery/exp_read = GLOB.dbcon.NewQuery("SELECT exp FROM [format_table_name("player")] WHERE ckey='[ckey]'") if(!exp_read.Execute()) var/err = exp_read.ErrorMsg() log_game("SQL ERROR during exp_update_client read. Error : \[[err]\]\n") @@ -252,7 +252,7 @@ var/global/list/role_playtime_requirements = list( if(!hasread) return var/list/play_records = list() - for(var/rtype in exp_jobsmap) + for(var/rtype in GLOB.exp_jobsmap) if(text2num(read_records[rtype])) play_records[rtype] = text2num(read_records[rtype]) else @@ -270,9 +270,9 @@ var/global/list/role_playtime_requirements = list( added_living += minutes if(announce_changes) to_chat(mob,"You got: [minutes] Living EXP!") - for(var/category in exp_jobsmap) - if(exp_jobsmap[category]["titles"]) - if(myrole in exp_jobsmap[category]["titles"]) + for(var/category in GLOB.exp_jobsmap) + if(GLOB.exp_jobsmap[category]["titles"]) + if(myrole in GLOB.exp_jobsmap[category]["titles"]) play_records[category] += minutes if(announce_changes) to_chat(mob,"You got: [minutes] [category] EXP!") @@ -290,15 +290,15 @@ var/global/list/role_playtime_requirements = list( var/new_exp = list2params(play_records) prefs.exp = new_exp new_exp = sanitizeSQL(new_exp) - var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]',lastseen = Now() WHERE ckey='[ckey]'") + var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET exp = '[new_exp]',lastseen = Now() WHERE ckey='[ckey]'") if(!update_query.Execute()) var/err = update_query.ErrorMsg() log_game("SQL ERROR during exp_update_client write 1. Error : \[[err]\]\n") message_admins("SQL ERROR during exp_update_client write 1. Error : \[[err]\]\n") return - var/DBQuery/update_query_history = dbcon.NewQuery("INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost) VALUES ('[ckey]',CURDATE(),[added_living],[added_ghost]) ON DUPLICATE KEY UPDATE time_living=time_living+VALUES(time_living),time_ghost=time_ghost+VALUES(time_ghost)") + var/DBQuery/update_query_history = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("playtime_history")] (ckey, date, time_living, time_ghost) VALUES ('[ckey]',CURDATE(),[added_living],[added_ghost]) ON DUPLICATE KEY UPDATE time_living=time_living+VALUES(time_living),time_ghost=time_ghost+VALUES(time_ghost)") if(!update_query_history.Execute()) var/err = update_query_history.ErrorMsg() log_game("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n") message_admins("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n") - return \ No newline at end of file + return diff --git a/code/game/jobs/job_objective.dm b/code/game/jobs/job_objective.dm index 48fa911bcf5b..11fc2b7181dd 100644 --- a/code/game/jobs/job_objective.dm +++ b/code/game/jobs/job_objective.dm @@ -73,4 +73,4 @@ else feedback_add_details("employee_success","FAIL") - return text \ No newline at end of file + return text diff --git a/code/game/jobs/job_objectives/science.dm b/code/game/jobs/job_objectives/science.dm index 0d5b4aebd830..70ff38a6a32a 100644 --- a/code/game/jobs/job_objectives/science.dm +++ b/code/game/jobs/job_objectives/science.dm @@ -42,4 +42,4 @@ /datum/job_objective/make_ripley/get_description() var/desc = "Make a Ripley or Firefighter." desc += "([units_completed] created.)" - return desc \ No newline at end of file + return desc diff --git a/code/game/jobs/job_scaling.dm b/code/game/jobs/job_scaling.dm index 90d55b03444f..c062fe6c2166 100644 --- a/code/game/jobs/job_scaling.dm +++ b/code/game/jobs/job_scaling.dm @@ -8,4 +8,4 @@ SSjobs.LoadJobs("config/jobs_highpop.txt") else log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config"); - return 1 \ No newline at end of file + return 1 diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index 09bbf2cf6334..8ff6b58e5c47 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -1,148 +1,147 @@ - -var/list/assistant_occupations = list( -) - - -var/list/command_positions = list( - "Captain", - "Head of Personnel", - "Head of Security", - "Chief Engineer", - "Research Director", - "Chief Medical Officer", - "Nanotrasen Representative" -) - - -var/list/engineering_positions = list( - "Chief Engineer", - "Station Engineer", - "Life Support Specialist", - "Mechanic" -) - - -var/list/medical_positions = list( - "Chief Medical Officer", - "Medical Doctor", - "Geneticist", - "Psychiatrist", - "Chemist", - "Virologist", - "Paramedic", - "Coroner" -) - - -var/list/science_positions = list( - "Research Director", - "Scientist", - "Geneticist", //Part of both medical and science - "Roboticist", -) - -//BS12 EDIT -var/list/support_positions = list( - "Head of Personnel", - "Bartender", - "Botanist", - "Chef", - "Janitor", - "Librarian", - "Quartermaster", - "Cargo Technician", - "Shaft Miner", - "Internal Affairs Agent", - "Chaplain", - "Clown", - "Mime", - "Barber", - "Magistrate", - "Nanotrasen Representative", - "Blueshield" -) - -var/list/supply_positions = list( - "Head of Personnel", - "Quartermaster", - "Cargo Technician", - "Shaft Miner" -) - -var/list/service_positions = support_positions - supply_positions + list("Head of Personnel") - - -var/list/security_positions = list( - "Head of Security", - "Warden", - "Detective", - "Security Officer", - "Brig Physician", - "Security Pod Pilot", - "Magistrate" -) - - -var/list/civilian_positions = list( - "Civilian" -) - -var/list/nonhuman_positions = list( - "AI", - "Cyborg", - "Drone", - "pAI" -) - -var/list/whitelisted_positions = list( - "Blueshield", - "Nanotrasen Representative", - "Barber", - "Mechanic", - "Brig Physician", - "Magistrate", - "Security Pod Pilot", -) - - -/proc/guest_jobbans(var/job) - return (job in whitelisted_positions) - -/proc/get_job_datums() - var/list/occupations = list() - var/list/all_jobs = typesof(/datum/job) - - for(var/A in all_jobs) - var/datum/job/job = new A() - if(!job) continue - occupations += job - - return occupations - -/proc/get_alternate_titles(var/job) - var/list/jobs = get_job_datums() - var/list/titles = list() - - for(var/datum/job/J in jobs) - if(!J) continue - if(J.title == job) - titles = J.alt_titles - - return titles - -var/global/list/exp_jobsmap = list( - EXP_TYPE_LIVING = list(), // all living mobs - EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions - EXP_TYPE_SPECIAL = list(), // antags, ERT, etc - EXP_TYPE_GHOST = list(), // dead people, observers - EXP_TYPE_EXEMPT = list(), // special grandfather setting - EXP_TYPE_COMMAND = list(titles = command_positions), - EXP_TYPE_ENGINEERING = list(titles = engineering_positions), - EXP_TYPE_MEDICAL = list(titles = medical_positions), - EXP_TYPE_SCIENCE = list(titles = science_positions), - EXP_TYPE_SUPPLY = list(titles = supply_positions), - EXP_TYPE_SECURITY = list(titles = security_positions), - EXP_TYPE_SILICON = list(titles = list("AI","Cyborg")), - EXP_TYPE_SERVICE = list(titles = service_positions), - EXP_TYPE_WHITELIST = list(titles = whitelisted_positions) // karma-locked jobs -) + +GLOBAL_LIST_EMPTY(assistant_occupations) + + +GLOBAL_LIST_INIT(command_positions, list( + "Captain", + "Head of Personnel", + "Head of Security", + "Chief Engineer", + "Research Director", + "Chief Medical Officer", + "Nanotrasen Representative" +)) + + +GLOBAL_LIST_INIT(engineering_positions, list( + "Chief Engineer", + "Station Engineer", + "Life Support Specialist", + "Mechanic" +)) + + +GLOBAL_LIST_INIT(medical_positions, list( + "Chief Medical Officer", + "Medical Doctor", + "Geneticist", + "Psychiatrist", + "Chemist", + "Virologist", + "Paramedic", + "Coroner" +)) + + +GLOBAL_LIST_INIT(science_positions, list( + "Research Director", + "Scientist", + "Geneticist", //Part of both medical and science + "Roboticist", +)) + +//BS12 EDIT +GLOBAL_LIST_INIT(support_positions, list( + "Head of Personnel", + "Bartender", + "Botanist", + "Chef", + "Janitor", + "Librarian", + "Quartermaster", + "Cargo Technician", + "Shaft Miner", + "Internal Affairs Agent", + "Chaplain", + "Clown", + "Mime", + "Barber", + "Magistrate", + "Nanotrasen Representative", + "Blueshield" +)) + +GLOBAL_LIST_INIT(supply_positions, list( + "Head of Personnel", + "Quartermaster", + "Cargo Technician", + "Shaft Miner" +)) + +GLOBAL_LIST_INIT(service_positions, (support_positions - supply_positions + list("Head of Personnel"))) + + +GLOBAL_LIST_INIT(security_positions, list( + "Head of Security", + "Warden", + "Detective", + "Security Officer", + "Brig Physician", + "Security Pod Pilot", + "Magistrate" +)) + + +GLOBAL_LIST_INIT(civilian_positions, list( + "Civilian" +)) + +GLOBAL_LIST_INIT(nonhuman_positions, list( + "AI", + "Cyborg", + "Drone", + "pAI" +)) + +GLOBAL_LIST_INIT(whitelisted_positions, list( + "Blueshield", + "Nanotrasen Representative", + "Barber", + "Mechanic", + "Brig Physician", + "Magistrate", + "Security Pod Pilot", +)) + + +/proc/guest_jobbans(var/job) + return (job in GLOB.whitelisted_positions) + +/proc/get_job_datums() + var/list/occupations = list() + var/list/all_jobs = typesof(/datum/job) + + for(var/A in all_jobs) + var/datum/job/job = new A() + if(!job) continue + occupations += job + + return occupations + +/proc/get_alternate_titles(var/job) + var/list/jobs = get_job_datums() + var/list/titles = list() + + for(var/datum/job/J in jobs) + if(!J) continue + if(J.title == job) + titles = J.alt_titles + + return titles + +GLOBAL_LIST_INIT(exp_jobsmap, list( + EXP_TYPE_LIVING = list(), // all living mobs + EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions + EXP_TYPE_SPECIAL = list(), // antags, ERT, etc + EXP_TYPE_GHOST = list(), // dead people, observers + EXP_TYPE_EXEMPT = list(), // special grandfather setting + EXP_TYPE_COMMAND = list(titles = command_positions), + EXP_TYPE_ENGINEERING = list(titles = engineering_positions), + EXP_TYPE_MEDICAL = list(titles = medical_positions), + EXP_TYPE_SCIENCE = list(titles = science_positions), + EXP_TYPE_SUPPLY = list(titles = supply_positions), + EXP_TYPE_SECURITY = list(titles = security_positions), + EXP_TYPE_SILICON = list(titles = list("AI","Cyborg")), + EXP_TYPE_SERVICE = list(titles = service_positions), + EXP_TYPE_WHITELIST = list(titles = whitelisted_positions) // karma-locked jobs +)) diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index a09eaed6c025..ab1bd3b80672 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,99 +1,99 @@ -#define WHITELISTFILE "data/whitelist.txt" - -var/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if(config.usewhitelist) - load_whitelist() - return 1 - -/proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!whitelist.len) whitelist = null -/* -/proc/check_whitelist(mob/M, var/rank) - if(!whitelist) - return 0 - return ("[M.ckey]" in whitelist) -*/ - -/proc/is_job_whitelisted(mob/M, var/rank) - if(guest_jobbans(rank)) - if(!config.usewhitelist) - return 1 - if(config.disable_karma) - return 1 - if(check_rights(R_ADMIN, 0, M)) - return 1 - if(!dbcon.IsConnected()) - to_chat(usr, "Unable to connect to whitelist database. Please try again later.
          ") - return 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT job FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") - query.Execute() - - - while(query.NextRow()) - var/joblist = query.item[1] - if(joblist!="*") - var/allowed_jobs = splittext(joblist,",") - if(rank in allowed_jobs) return 1 - else return 1 - return 0 - else - return 1 - - - - -/var/list/alien_whitelist = list() - -/hook/startup/proc/loadAlienWhitelist() - if(config.usealienwhitelist) - load_alienwhitelist() - return 1 - -/proc/load_alienwhitelist() - var/text = file2text("config/alienwhitelist.txt") - if(!text) - log_config("Failed to load config/alienwhitelist.txt\n") - else - alien_whitelist = splittext(text, "\n") - -//todo: admin aliens -/proc/is_alien_whitelisted(mob/M, var/species) - if(!config.usealienwhitelist) - return 1 - if(config.disable_karma) - return 1 - if(species == "human" || species == "Human") - return 1 - if(check_rights(R_ADMIN, 0)) - return 1 - if(!alien_whitelist) - return 0 - if(!dbcon.IsConnected()) - to_chat(usr, "Unable to connect to whitelist database. Please try again later.
          ") - return 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT species FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") - query.Execute() - - while(query.NextRow()) - var/specieslist = query.item[1] - if(specieslist!="*") - var/allowed_species = splittext(specieslist,",") - if(species in allowed_species) return 1 - else return 1 - return 0 -/* - if(M && species) - for(var/s in alien_whitelist) - if(findtext(s,"[M.ckey] - [species]")) - return 1 - if(findtext(s,"[M.ckey] - All")) - return 1 -*/ - - -#undef WHITELISTFILE \ No newline at end of file +#define WHITELISTFILE "data/whitelist.txt" + +GLOBAL_LIST_EMPTY(whitelist) + +/hook/startup/proc/loadWhitelist() + if(config.usewhitelist) + load_whitelist() + return 1 + +/proc/load_whitelist() + GLOB.whitelist = file2list(WHITELISTFILE) + if(!GLOB.whitelist.len) GLOB.whitelist = null +/* +/proc/check_whitelist(mob/M, var/rank) + if(!whitelist) + return 0 + return ("[M.ckey]" in whitelist) +*/ + +/proc/is_job_whitelisted(mob/M, var/rank) + if(guest_jobbans(rank)) + if(!config.usewhitelist) + return 1 + if(config.disable_karma) + return 1 + if(check_rights(R_ADMIN, 0, M)) + return 1 + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Unable to connect to whitelist database. Please try again later.
          ") + return 0 + else + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT job FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") + query.Execute() + + + while(query.NextRow()) + var/joblist = query.item[1] + if(joblist!="*") + var/allowed_jobs = splittext(joblist,",") + if(rank in allowed_jobs) return 1 + else return 1 + return 0 + else + return 1 + + + + +GLOBAL_LIST_EMPTY(alien_whitelist) + +/hook/startup/proc/loadAlienWhitelist() + if(config.usealienwhitelist) + load_alienwhitelist() + return 1 + +/proc/load_alienwhitelist() + var/text = file2text("config/alienwhitelist.txt") + if(!text) + log_config("Failed to load config/alienwhitelist.txt\n") + else + GLOB.alien_whitelist = splittext(text, "\n") + +//todo: admin aliens +/proc/is_alien_whitelisted(mob/M, var/species) + if(!config.usealienwhitelist) + return 1 + if(config.disable_karma) + return 1 + if(species == "human" || species == "Human") + return 1 + if(check_rights(R_ADMIN, 0)) + return 1 + if(!GLOB.alien_whitelist) + return 0 + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Unable to connect to whitelist database. Please try again later.
          ") + return 0 + else + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT species FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") + query.Execute() + + while(query.NextRow()) + var/specieslist = query.item[1] + if(specieslist!="*") + var/allowed_species = splittext(specieslist,",") + if(species in allowed_species) return 1 + else return 1 + return 0 +/* + if(M && species) + for(var/s in alien_whitelist) + if(findtext(s,"[M.ckey] - [species]")) + return 1 + if(findtext(s,"[M.ckey] - All")) + return 1 +*/ + + +#undef WHITELISTFILE diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index b5b2413fb966..d3b2114f8015 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -86,4 +86,4 @@ /obj/machinery/bluespace_beacon/syndicate/infiltrator/New() ..() - enabled = FALSE \ No newline at end of file + enabled = FALSE diff --git a/code/game/machinery/Freezer.dm b/code/game/machinery/Freezer.dm index b6c6121cfd61..d9243005ead0 100644 --- a/code/game/machinery/Freezer.dm +++ b/code/game/machinery/Freezer.dm @@ -1,332 +1,332 @@ -/obj/machinery/atmospherics/unary/cold_sink/freezer - name = "freezer" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "freezer" - density = 1 - var/min_temperature = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - current_heat_capacity = 1000 - layer = 3 - plane = GAME_PLANE - max_integrity = 300 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/New() - ..() - initialize_directions = dir - component_parts = list() - component_parts += new /obj/item/circuitboard/thermomachine(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/thermomachine(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/RefreshParts() - var/H - var/T - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - H += M.rating - for(var/obj/item/stock_parts/micro_laser/M in component_parts) - T += M.rating - min_temperature = max(0,T0C - (170 + (T*15))) - current_heat_capacity = 1000 * ((H - 1) ** 2) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - return ..() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/atmospherics/unary/cold_sink/freezer/screwdriver_act(mob/user, obj/item/I) - if(default_deconstruction_screwdriver(user, "freezer-o", "freezer", I)) - on = FALSE - update_icon() - return TRUE - -/obj/machinery/atmospherics/unary/cold_sink/freezer/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) - var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices - dir = choices[selected] - var/node_connect = dir - initialize_directions = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - build_network() - update_icon() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/update_icon() - if(panel_open) - icon_state = "freezer-o" - else if(src.on) - icon_state = "freezer_1" - else - icon_state = "freezer" - return - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ghost(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_hand(mob/user as mob) - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - src.ui_interact(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) - if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(min_temperature) - data["maxGasTemperature"] = round(T20C) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "good" - if(air_contents.temperature > (T0C - 20)) - temp_class = "bad" - else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) - temp_class = "average" - data["gasTemperatureClass"] = temp_class - return data - -/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = min_temperature - else if(href_list["maximum"]) - current_temperature = T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min(T20C, src.current_temperature+amount) - else - src.current_temperature = max(min_temperature, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 - -/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() - ..() - if(stat & NOPOWER) - on = 0 - update_icon() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ - name = "heater" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "heater" - density = 1 - var/max_temperature = 0 - anchored = 1.0 - layer = 3 - current_heat_capacity = 1000 - max_integrity = 300 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/New() - ..() - initialize_directions = dir - var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) - H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - H.name = "circuit board (Heater)" - component_parts = list() - component_parts += H - component_parts += new /obj/item/stock_parts/matter_bin(src) - component_parts += new /obj/item/stock_parts/matter_bin(src) - component_parts += new /obj/item/stock_parts/micro_laser(src) - component_parts += new /obj/item/stock_parts/micro_laser(src) - component_parts += new /obj/item/stack/sheet/glass(src) - component_parts += new /obj/item/stack/cable_coil(src, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/upgraded/New() - ..() - var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) - H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - H.name = "circuit board (Heater)" - component_parts = list() - component_parts += H - component_parts += new /obj/item/stock_parts/matter_bin/super(src) - component_parts += new /obj/item/stock_parts/matter_bin/super(src) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) - component_parts += new /obj/item/stack/sheet/glass(src) - component_parts += new /obj/item/stack/cable_coil(src, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/RefreshParts() - var/H - var/T - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - H += M.rating - for(var/obj/item/stock_parts/micro_laser/M in component_parts) - T += M.rating - max_temperature = T20C + (140 * T) - current_heat_capacity = 1000 * ((H - 1) ** 2) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - return ..() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/screwdriver_act(mob/user, obj/item/I) - if(default_deconstruction_screwdriver(user, "heater-o", "heater", I)) - on = 0 - update_icon() - return TRUE - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) - var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices - dir = choices[selected] - var/node_connect = dir - initialize_directions = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - build_network() - update_icon() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/update_icon() - if(panel_open) - icon_state = "heater-o" - else if(src.on) - icon_state = "heater_1" - else - icon_state = "heater" - return - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ghost(mob/user as mob) - src.attack_hand(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_hand(mob/user as mob) - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - src.ui_interact(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) - if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(T20C) - data["maxGasTemperature"] = round(T20C+max_temperature) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "normal" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - return data - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = T20C - else if(href_list["maximum"]) - current_temperature = max_temperature + T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) - else - src.current_temperature = max(T20C, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() - ..() - if(stat & NOPOWER) - on = 0 - update_icon() +/obj/machinery/atmospherics/unary/cold_sink/freezer + name = "freezer" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "freezer" + density = 1 + var/min_temperature = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + current_heat_capacity = 1000 + layer = 3 + plane = GAME_PLANE + max_integrity = 300 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/New() + ..() + initialize_directions = dir + component_parts = list() + component_parts += new /obj/item/circuitboard/thermomachine(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/thermomachine(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/RefreshParts() + var/H + var/T + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + H += M.rating + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + T += M.rating + min_temperature = max(0,T0C - (170 + (T*15))) + current_heat_capacity = 1000 * ((H - 1) ** 2) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/atmospherics/unary/cold_sink/freezer/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "freezer-o", "freezer", I)) + on = FALSE + update_icon() + return TRUE + +/obj/machinery/atmospherics/unary/cold_sink/freezer/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) + var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices + dir = choices[selected] + var/node_connect = dir + initialize_directions = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + build_network() + update_icon() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/update_icon() + if(panel_open) + icon_state = "freezer-o" + else if(src.on) + icon_state = "freezer_1" + else + icon_state = "freezer" + return + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ghost(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_hand(mob/user as mob) + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + src.ui_interact(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["on"] = on ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) + if(air_contents.total_moles() == 0 && air_contents.temperature == 0) + data["gasTemperatureCelsius"] = 0 + data["minGasTemperature"] = round(min_temperature) + data["maxGasTemperature"] = round(T20C) + data["targetGasTemperature"] = round(current_temperature) + data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) + + var/temp_class = "good" + if(air_contents.temperature > (T0C - 20)) + temp_class = "bad" + else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) + temp_class = "average" + data["gasTemperatureClass"] = temp_class + return data + +/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + src.on = !src.on + update_icon() + else if(href_list["minimum"]) + current_temperature = min_temperature + else if(href_list["maximum"]) + current_temperature = T20C + else if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + src.current_temperature = min(T20C, src.current_temperature+amount) + else + src.current_temperature = max(min_temperature, src.current_temperature+amount) + src.add_fingerprint(usr) + return 1 + +/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() + ..() + if(stat & NOPOWER) + on = 0 + update_icon() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ + name = "heater" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "heater" + density = 1 + var/max_temperature = 0 + anchored = 1.0 + layer = 3 + current_heat_capacity = 1000 + max_integrity = 300 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/New() + ..() + initialize_directions = dir + var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) + H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + H.name = "circuit board (Heater)" + component_parts = list() + component_parts += H + component_parts += new /obj/item/stock_parts/matter_bin(src) + component_parts += new /obj/item/stock_parts/matter_bin(src) + component_parts += new /obj/item/stock_parts/micro_laser(src) + component_parts += new /obj/item/stock_parts/micro_laser(src) + component_parts += new /obj/item/stack/sheet/glass(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/upgraded/New() + ..() + var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) + H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + H.name = "circuit board (Heater)" + component_parts = list() + component_parts += H + component_parts += new /obj/item/stock_parts/matter_bin/super(src) + component_parts += new /obj/item/stock_parts/matter_bin/super(src) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) + component_parts += new /obj/item/stack/sheet/glass(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/RefreshParts() + var/H + var/T + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + H += M.rating + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + T += M.rating + max_temperature = T20C + (140 * T) + current_heat_capacity = 1000 * ((H - 1) ** 2) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "heater-o", "heater", I)) + on = 0 + update_icon() + return TRUE + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) + var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices + dir = choices[selected] + var/node_connect = dir + initialize_directions = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + build_network() + update_icon() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/update_icon() + if(panel_open) + icon_state = "heater-o" + else if(src.on) + icon_state = "heater_1" + else + icon_state = "heater" + return + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ghost(mob/user as mob) + src.attack_hand(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_hand(mob/user as mob) + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + src.ui_interact(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["on"] = on ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) + if(air_contents.total_moles() == 0 && air_contents.temperature == 0) + data["gasTemperatureCelsius"] = 0 + data["minGasTemperature"] = round(T20C) + data["maxGasTemperature"] = round(T20C+max_temperature) + data["targetGasTemperature"] = round(current_temperature) + data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) + + var/temp_class = "normal" + if(air_contents.temperature > (T20C+40)) + temp_class = "bad" + data["gasTemperatureClass"] = temp_class + return data + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + src.on = !src.on + update_icon() + else if(href_list["minimum"]) + current_temperature = T20C + else if(href_list["maximum"]) + current_temperature = max_temperature + T20C + else if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) + else + src.current_temperature = max(T20C, src.current_temperature+amount) + src.add_fingerprint(usr) + return 1 + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() + ..() + if(stat & NOPOWER) + on = 0 + update_icon() diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 8c93028649b4..7317099b8dc2 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -1,155 +1,155 @@ -/obj/machinery/optable - name = "Operating Table" - desc = "Used for advanced medical procedures." - icon = 'icons/obj/surgery.dmi' - icon_state = "table2-idle" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 1 - active_power_usage = 5 - var/mob/living/carbon/human/victim = null - var/strapped = 0.0 - - var/obj/machinery/computer/operating/computer = null - buckle_lying = -1 - var/no_icon_updates = 0 //set this to 1 if you don't want the icons ever changing - var/list/injected_reagents = list() - var/reagent_target_amount = 1 - var/inject_amount = 1 - -/obj/machinery/optable/New() - ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) - if(computer) - computer.table = src - break - -/obj/machinery/optable/Destroy() - if(computer) - computer.table = null - computer.victim = null - computer = null - if(victim) - victim = null - return ..() - -/obj/machinery/optable/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - if(user.a_intent == INTENT_HARM) - ..(user, TRUE) - visible_message("[user] destroys the operating table!") - qdel(src) - return TRUE - -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) return 1 - - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - else - return 0 - - -/obj/machinery/optable/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if(usr.stat || (!ishuman(user) && !isrobot(user)) || user.restrained() || !check_table(user) || user.IsWeakened() || user.stunned) - return - - if(!ismob(O)) //humans only - return - - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit - return - - var/mob/living/L = O - take_victim(L,usr) - return - -/obj/machinery/optable/proc/check_victim() - if(locate(/mob/living/carbon/human, src.loc)) - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) - if(M.lying) - src.victim = M - if(!no_icon_updates) - icon_state = M.pulse ? "table2-active" : "table2-idle" - return 1 - src.victim = null - if(!no_icon_updates) - icon_state = "table2-idle" - return 0 - -/obj/machinery/optable/Crossed(atom/movable/AM, oldloc) - . = ..() - if(iscarbon(AM) && LAZYLEN(injected_reagents)) - to_chat(AM, "You feel a series of tiny pricks!") - -/obj/machinery/optable/process() - check_victim() - if(LAZYLEN(injected_reagents)) - for(var/mob/living/carbon/C in get_turf(src)) - var/datum/reagents/R = C.reagents - for(var/chemical in injected_reagents) - R.check_and_add(chemical,reagent_target_amount,inject_amount) - -/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) - if(C == user) - user.visible_message("[user] climbs on the operating table.","You climb on the operating table.") - else - visible_message("[C] has been laid on the operating table by [user].") - C.resting = 1 - C.update_canmove() - C.forceMove(loc) - if(user.pulling == C) - user.stop_pulling() - if(C.s_active) //Close the container opened - C.s_active.close(C) - for(var/obj/O in src) - O.loc = src.loc - src.add_fingerprint(user) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - src.victim = H - if(!no_icon_updates) - icon_state = H.pulse ? "table2-active" : "table2-idle" - else - if(!no_icon_updates) - icon_state = "table2-idle" - -/obj/machinery/optable/verb/climb_on() - set name = "Climb On Table" - set category = "Object" - set src in oview(1) - - if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) - return - - take_victim(usr,usr) - -/obj/machinery/optable/attackby(obj/item/I, mob/living/carbon/user, params) - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - if(iscarbon(G.affecting)) - take_victim(G.affecting, user) - qdel(G) - else - return ..() - -/obj/machinery/optable/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_start_check(user, 0)) - return - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - to_chat(user, "You deconstruct the table.") - new /obj/item/stack/sheet/plasteel(loc, 5) - qdel(src) - -/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) - if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) - to_chat(usr, "The table is already occupied!") - return 0 - - if(patient.buckled) - to_chat(usr, "Unbuckle first!") - return 0 - - return 1 +/obj/machinery/optable + name = "Operating Table" + desc = "Used for advanced medical procedures." + icon = 'icons/obj/surgery.dmi' + icon_state = "table2-idle" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 1 + active_power_usage = 5 + var/mob/living/carbon/human/victim = null + var/strapped = 0.0 + + var/obj/machinery/computer/operating/computer = null + buckle_lying = -1 + var/no_icon_updates = 0 //set this to 1 if you don't want the icons ever changing + var/list/injected_reagents = list() + var/reagent_target_amount = 1 + var/inject_amount = 1 + +/obj/machinery/optable/New() + ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) + if(computer) + computer.table = src + break + +/obj/machinery/optable/Destroy() + if(computer) + computer.table = null + computer.victim = null + computer = null + if(victim) + victim = null + return ..() + +/obj/machinery/optable/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM) + ..(user, TRUE) + visible_message("[user] destroys the operating table!") + qdel(src) + return TRUE + +/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) return 1 + + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + else + return 0 + + +/obj/machinery/optable/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if(usr.stat || (!ishuman(user) && !isrobot(user)) || user.restrained() || !check_table(user) || user.IsWeakened() || user.stunned) + return + + if(!ismob(O)) //humans only + return + + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit + return + + var/mob/living/L = O + take_victim(L,usr) + return + +/obj/machinery/optable/proc/check_victim() + if(locate(/mob/living/carbon/human, src.loc)) + var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) + if(M.lying) + src.victim = M + if(!no_icon_updates) + icon_state = M.pulse ? "table2-active" : "table2-idle" + return 1 + src.victim = null + if(!no_icon_updates) + icon_state = "table2-idle" + return 0 + +/obj/machinery/optable/Crossed(atom/movable/AM, oldloc) + . = ..() + if(iscarbon(AM) && LAZYLEN(injected_reagents)) + to_chat(AM, "You feel a series of tiny pricks!") + +/obj/machinery/optable/process() + check_victim() + if(LAZYLEN(injected_reagents)) + for(var/mob/living/carbon/C in get_turf(src)) + var/datum/reagents/R = C.reagents + for(var/chemical in injected_reagents) + R.check_and_add(chemical,reagent_target_amount,inject_amount) + +/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) + if(C == user) + user.visible_message("[user] climbs on the operating table.","You climb on the operating table.") + else + visible_message("[C] has been laid on the operating table by [user].") + C.resting = 1 + C.update_canmove() + C.forceMove(loc) + if(user.pulling == C) + user.stop_pulling() + if(C.s_active) //Close the container opened + C.s_active.close(C) + for(var/obj/O in src) + O.loc = src.loc + src.add_fingerprint(user) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + src.victim = H + if(!no_icon_updates) + icon_state = H.pulse ? "table2-active" : "table2-idle" + else + if(!no_icon_updates) + icon_state = "table2-idle" + +/obj/machinery/optable/verb/climb_on() + set name = "Climb On Table" + set category = "Object" + set src in oview(1) + + if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) + return + + take_victim(usr,usr) + +/obj/machinery/optable/attackby(obj/item/I, mob/living/carbon/user, params) + if(istype(I, /obj/item/grab)) + var/obj/item/grab/G = I + if(iscarbon(G.affecting)) + take_victim(G.affecting, user) + qdel(G) + else + return ..() + +/obj/machinery/optable/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_start_check(user, 0)) + return + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + to_chat(user, "You deconstruct the table.") + new /obj/item/stack/sheet/plasteel(loc, 5) + qdel(src) + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) + if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) + to_chat(usr, "The table is already occupied!") + return 0 + + if(patient.buckled) + to_chat(usr, "Unbuckle first!") + return 0 + + return 1 diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm index 83778a99c264..3185cdcddd41 100644 --- a/code/game/machinery/PDApainter.dm +++ b/code/game/machinery/PDApainter.dm @@ -1,132 +1,132 @@ -/obj/machinery/pdapainter - name = "PDA painter" - desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." - icon = 'icons/obj/pda.dmi' - icon_state = "pdapainter" - density = 1 - anchored = 1 - max_integrity = 200 - var/obj/item/pda/storedpda = null - var/list/colorlist = list() - - -/obj/machinery/pdapainter/update_icon() - cut_overlays() - - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - return - - if(storedpda) - add_overlay("[initial(icon_state)]-closed") - - if(powered()) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]-off" - - return - -/obj/machinery/pdapainter/New() - ..() - var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads, - /obj/item/pda/clear, /obj/item/pda/syndicate) - - for(var/P in typesof(/obj/item/pda)-blocked) - var/obj/item/pda/D = new P - - //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" - D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. - - src.colorlist += D - -/obj/machinery/pdapainter/Destroy() - QDEL_NULL(storedpda) - return ..() - -/obj/machinery/pdapainter/on_deconstruction() - if(storedpda) - storedpda.forceMove(loc) - storedpda = null - -/obj/machinery/pdapainter/ex_act(severity) - if(storedpda) - storedpda.ex_act(severity) - ..() - -/obj/machinery/pdapainter/handle_atom_del(atom/A) - if(A == storedpda) - storedpda = null - update_icon() - -/obj/machinery/pdapainter/attackby(obj/item/I, mob/user, params) - if(default_unfasten_wrench(user, I)) - power_change() - return - if(istype(I, /obj/item/pda)) - if(storedpda) - to_chat(user, "There is already a PDA inside.") - return - else - var/obj/item/pda/P = user.get_active_hand() - if(istype(P)) - if(user.drop_item()) - storedpda = P - P.forceMove(src) - P.add_fingerprint(user) - update_icon() - else - return ..() - -/obj/machinery/pdapainter/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - default_welder_repair(user, I) - -/obj/machinery/pdapainter/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - stat |= BROKEN - update_icon() - -/obj/machinery/pdapainter/attack_hand(mob/user as mob) - if(..()) - return 1 - - src.add_fingerprint(user) - - if(storedpda) - var/obj/item/pda/P - P = input(user, "Select your color!", "PDA Painting") as null|anything in colorlist - if(!P) - return - if(!in_range(src, user)) - return - - storedpda.icon_state = P.icon_state - storedpda.desc = P.desc - - else - to_chat(user, "The [src] is empty.") - - -/obj/machinery/pdapainter/verb/ejectpda() - set name = "Eject PDA" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return - - if(storedpda) - storedpda.loc = get_turf(src.loc) - storedpda = null - update_icon() - else - to_chat(usr, "The [src] is empty.") - - -/obj/machinery/pdapainter/power_change() - ..() - update_icon() \ No newline at end of file +/obj/machinery/pdapainter + name = "PDA painter" + desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." + icon = 'icons/obj/pda.dmi' + icon_state = "pdapainter" + density = 1 + anchored = 1 + max_integrity = 200 + var/obj/item/pda/storedpda = null + var/list/colorlist = list() + + +/obj/machinery/pdapainter/update_icon() + cut_overlays() + + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + return + + if(storedpda) + add_overlay("[initial(icon_state)]-closed") + + if(powered()) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]-off" + + return + +/obj/machinery/pdapainter/New() + ..() + var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads, + /obj/item/pda/clear, /obj/item/pda/syndicate) + + for(var/P in typesof(/obj/item/pda)-blocked) + var/obj/item/pda/D = new P + + //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" + D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. + + src.colorlist += D + +/obj/machinery/pdapainter/Destroy() + QDEL_NULL(storedpda) + return ..() + +/obj/machinery/pdapainter/on_deconstruction() + if(storedpda) + storedpda.forceMove(loc) + storedpda = null + +/obj/machinery/pdapainter/ex_act(severity) + if(storedpda) + storedpda.ex_act(severity) + ..() + +/obj/machinery/pdapainter/handle_atom_del(atom/A) + if(A == storedpda) + storedpda = null + update_icon() + +/obj/machinery/pdapainter/attackby(obj/item/I, mob/user, params) + if(default_unfasten_wrench(user, I)) + power_change() + return + if(istype(I, /obj/item/pda)) + if(storedpda) + to_chat(user, "There is already a PDA inside.") + return + else + var/obj/item/pda/P = user.get_active_hand() + if(istype(P)) + if(user.drop_item()) + storedpda = P + P.forceMove(src) + P.add_fingerprint(user) + update_icon() + else + return ..() + +/obj/machinery/pdapainter/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_welder_repair(user, I) + +/obj/machinery/pdapainter/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + stat |= BROKEN + update_icon() + +/obj/machinery/pdapainter/attack_hand(mob/user as mob) + if(..()) + return 1 + + src.add_fingerprint(user) + + if(storedpda) + var/obj/item/pda/P + P = input(user, "Select your color!", "PDA Painting") as null|anything in colorlist + if(!P) + return + if(!in_range(src, user)) + return + + storedpda.icon_state = P.icon_state + storedpda.desc = P.desc + + else + to_chat(user, "The [src] is empty.") + + +/obj/machinery/pdapainter/verb/ejectpda() + set name = "Eject PDA" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return + + if(storedpda) + storedpda.loc = get_turf(src.loc) + storedpda = null + update_icon() + else + to_chat(usr, "The [src] is empty.") + + +/obj/machinery/pdapainter/power_change() + ..() + update_icon() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index f1adf1a5f11b..81409b5de862 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -1,556 +1,556 @@ -#define ADDICTION_SPEEDUP_TIME 1500 // 2.5 minutes - -///////////// -// SLEEPER // -//////////// - -/obj/machinery/sleeper - name = "Sleeper" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper-open" - var/base_icon = "sleeper" - density = 1 - anchored = 1 - dir = WEST - var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" - var/mob/living/carbon/human/occupant = null - var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") - var/emergency_chems = list("ephedrine") // Desnowflaking - var/amounts = list(5, 10) - var/obj/item/reagent_containers/glass/beaker = null - var/filtering = 0 - var/max_chem - var/initial_bin_rating = 1 - var/min_health = -25 - var/controls_inside = FALSE - idle_power_usage = 1250 - active_power_usage = 2500 - - light_color = LIGHT_COLOR_CYAN - -/obj/machinery/sleeper/power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/sleeper/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper(null) - - // Customizable bin rating, used by the labor camp to stop people filling themselves with chemicals and escaping. - var/obj/item/stock_parts/matter_bin/B = new(null) - B.rating = initial_bin_rating - component_parts += B - - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/sleeper/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/sleeper/RefreshParts() - var/E - for(var/obj/item/stock_parts/matter_bin/B in component_parts) - E += B.rating - - max_chem = E * 20 - min_health = -E * 25 - -/obj/machinery/sleeper/Destroy() - for(var/mob/M in contents) - M.forceMove(get_turf(src)) - return ..() - -/obj/machinery/sleeper/relaymove(mob/user as mob) - if(user.incapacitated()) - return 0 //maybe they should be able to get out with cuffs, but whatever - go_out() - -/obj/machinery/sleeper/process() - if(filtering > 0) - if(beaker) - // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. - if(!istype(occupant) || !occupant.dna || (NO_BLOOD in occupant.dna.species.species_traits)) - filtering = 0 - return - - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - occupant.transfer_blood_to(beaker, 1) - for(var/datum/reagent/x in occupant.reagents.reagent_list) - occupant.reagents.trans_to(beaker, 3) - occupant.transfer_blood_to(beaker, 1) - - if(occupant) - for(var/A in occupant.reagents.addiction_list) - var/datum/reagent/R = A - - var/addiction_removal_chance = 5 - if(world.timeofday > (R.last_addiction_dose + ADDICTION_SPEEDUP_TIME)) // 2.5 minutes - addiction_removal_chance = 10 - if(prob(addiction_removal_chance)) - to_chat(occupant, "You no longer feel reliant on [R.name]!") - occupant.reagents.addiction_list.Remove(R) - qdel(R) - - for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp - if(M == occupant) - continue - else - M.forceMove(loc) - - updateDialog() - return - - -/obj/machinery/sleeper/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attack_ghost(mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - ui_interact(user) - -/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - var/crisis = 0 - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.dna.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.dna.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(istype(occupant, /mob/living/simple_animal)) - var/mob/living/simple_animal/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - - crisis = (occupant.health < min_health) - // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent - // Runtime is aptly named, isn't she? - if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) - - data["occupant"] = occupantData - data["maxchem"] = max_chem - data["minhealth"] = min_health - data["dialysis"] = filtering - if(beaker) - data["isBeakerLoaded"] = 1 - if(beaker.reagents) - data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) - else - data["beakerFreeSpace"] = 0 - - var/chemicals[0] - for(var/re in possible_chems) - var/datum/reagent/temp = GLOB.chemical_reagents_list[re] - if(temp) - var/reagent_amount = 0 - var/pretty_amount - var/injectable = occupant ? 1 : 0 - var/overdosing = 0 - var/caution = 0 // To make things clear that you're coming close to an overdose - if(crisis && !(temp.id in emergency_chems)) - injectable = 0 - - if(occupant && occupant.reagents) - reagent_amount = occupant.reagents.get_reagent_amount(temp.id) - // If they're mashing the highest concentration, they get one warning - if(temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) - caution = 1 - if(temp.id in occupant.reagents.overdose_list()) - overdosing = 1 - - // Because I don't know how to do this on the nano side - pretty_amount = round(reagent_amount, 0.05) - - chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) - data["chemicals"] = chemicals - return data - -/obj/machinery/sleeper/Topic(href, href_list) - if(!controls_inside && usr == occupant) - return 0 - - if(..()) - return 1 - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return 0 - - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - if(href_list["chemical"]) - if(occupant) - if(occupant.stat == DEAD) - to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") - else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) - inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - if(href_list["removebeaker"]) - remove_beaker() - if(href_list["togglefilter"]) - toggle_filter() - if(href_list["ejectify"]) - eject() - add_fingerprint(usr) - return 1 - -/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/glass)) - if(!beaker) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to you!") - return - - beaker = I - I.forceMove(src) - user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") - return - - else - to_chat(user, "The sleeper has a beaker already.") - return - - if(exchange_parts(user, I)) - return - - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(!ismob(G.affecting)) - return - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(G.affecting.has_buckled_mobs()) //mob attached to us - to_chat(user, "[G.affecting] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.") - return - - visible_message("[user] starts putting [G.affecting.name] into the sleeper.") - - if(do_after(user, 20, target = G.affecting)) - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(!G || !G.affecting) - return - var/mob/M = G.affecting - M.forceMove(src) - occupant = M - icon_state = "[base_icon]" - to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") - add_fingerprint(user) - qdel(G) - return - - return ..() - - -/obj/machinery/sleeper/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/sleeper/screwdriver_act(mob/user, obj/item/I) - if(occupant) - to_chat(user, "The maintenance panel is locked.") - return TRUE - if(default_deconstruction_screwdriver(user, "[base_icon]-o", "[base_icon]-open", I)) - return TRUE - -/obj/machinery/sleeper/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(occupant) - to_chat(user, "The scanner is occupied.") - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(dir == EAST) - orient = "LEFT" - setDir(WEST) - else - orient = "RIGHT" - setDir(EAST) - -/obj/machinery/sleeper/ex_act(severity) - if(filtering) - toggle_filter() - if(occupant) - occupant.ex_act(severity) - ..() - -/obj/machinery/sleeper/handle_atom_del(atom/A) - ..() - if(A == occupant) - occupant = null - updateUsrDialog() - update_icon() - if(A == beaker) - beaker = null - updateUsrDialog() - -/obj/machinery/sleeper/emp_act(severity) - if(filtering) - toggle_filter() - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(occupant) - go_out() - ..(severity) - -/obj/machinery/sleeper/narsie_act() - go_out() - new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! - qdel(src) - -/obj/machinery/sleeper/proc/toggle_filter() - if(filtering) - filtering = 0 - else - filtering = 1 - -/obj/machinery/sleeper/proc/go_out() - if(filtering) - toggle_filter() - if(!occupant) - return - occupant.forceMove(loc) - occupant = null - icon_state = "[base_icon]-open" - // eject trash the occupant dropped - for(var/atom/movable/A in contents - component_parts - list(beaker)) - A.forceMove(loc) - -/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) - if(!(chemical in possible_chems)) - to_chat(user, "The sleeper does not offer that chemical!") - return - - if(occupant) - if(occupant.reagents) - if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) - occupant.reagents.add_reagent(chemical, amount) - return - else - to_chat(user, "You can not inject any more of this chemical.") - return - else - to_chat(user, "The patient rejects the chemicals!") - return - else - to_chat(user, "There's no occupant in the sleeper!") - return - -/obj/machinery/sleeper/verb/eject() - set name = "Eject Sleeper" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - icon_state = "[base_icon]-open" - go_out() - add_fingerprint(usr) - return - -/obj/machinery/sleeper/verb/remove_beaker() - set name = "Remove Beaker" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - if(beaker) - filtering = 0 - beaker.forceMove(usr.loc) - beaker = null - add_fingerprint(usr) - return - -/obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if(O.loc == user) //no you can't pull things out of your ass - return - if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source - return - if(!ismob(O)) //humans only - return - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit - return - if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper - return - if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems - return - if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - var/mob/living/L = O - if(!istype(L) || L.buckled) - return - if(L.abiotic()) - to_chat(user, "Subject cannot have abiotic items on.") - return - if(L.has_buckled_mobs()) //mob attached to us - to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") - return - if(L == user) - visible_message("[user] starts climbing into the sleeper.") - else - visible_message("[user] starts putting [L.name] into the sleeper.") - - if(do_after(user, 20, target = L)) - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(!L) return - L.forceMove(src) - occupant = L - icon_state = "[base_icon]" - to_chat(L, "You feel cool air surround you. You go numb as your senses turn inward.") - add_fingerprint(user) - if(user.pulling == L) - user.stop_pulling() - return - return - -/obj/machinery/sleeper/AllowDrop() - return FALSE - -/obj/machinery/sleeper/verb/move_inside() - set name = "Enter Sleeper" - set category = "Object" - set src in oview(1) - if(usr.stat != 0 || !(ishuman(usr))) - return - if(occupant) - to_chat(usr, "The sleeper is already occupied!") - return - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(usr.has_buckled_mobs()) //mob attached to us - to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") - return - visible_message("[usr] starts climbing into the sleeper.") - if(do_after(usr, 20, target = usr)) - if(occupant) - to_chat(usr, "The sleeper is already occupied!") - return - usr.stop_pulling() - usr.forceMove(src) - occupant = usr - icon_state = "[base_icon]" - - for(var/obj/O in src) - qdel(O) - add_fingerprint(usr) - return - return - -/obj/machinery/sleeper/syndie - icon_state = "sleeper_s-open" - base_icon = "sleeper_s" - possible_chems = list("epinephrine", "ether", "salbutamol", "styptic_powder", "silver_sulfadiazine") - emergency_chems = list("epinephrine") - controls_inside = TRUE - - light_color = LIGHT_COLOR_DARKRED - -/obj/machinery/sleeper/syndie/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper/syndicate(null) - var/obj/item/stock_parts/matter_bin/B = new(null) - B.rating = initial_bin_rating - component_parts += B - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -#undef ADDICTION_SPEEDUP_TIME +#define ADDICTION_SPEEDUP_TIME 1500 // 2.5 minutes + +///////////// +// SLEEPER // +//////////// + +/obj/machinery/sleeper + name = "Sleeper" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper-open" + var/base_icon = "sleeper" + density = 1 + anchored = 1 + dir = WEST + var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" + var/mob/living/carbon/human/occupant = null + var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") + var/emergency_chems = list("ephedrine") // Desnowflaking + var/amounts = list(5, 10) + var/obj/item/reagent_containers/glass/beaker = null + var/filtering = 0 + var/max_chem + var/initial_bin_rating = 1 + var/min_health = -25 + var/controls_inside = FALSE + idle_power_usage = 1250 + active_power_usage = 2500 + + light_color = LIGHT_COLOR_CYAN + +/obj/machinery/sleeper/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/sleeper/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper(null) + + // Customizable bin rating, used by the labor camp to stop people filling themselves with chemicals and escaping. + var/obj/item/stock_parts/matter_bin/B = new(null) + B.rating = initial_bin_rating + component_parts += B + + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/sleeper/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/sleeper/RefreshParts() + var/E + for(var/obj/item/stock_parts/matter_bin/B in component_parts) + E += B.rating + + max_chem = E * 20 + min_health = -E * 25 + +/obj/machinery/sleeper/Destroy() + for(var/mob/M in contents) + M.forceMove(get_turf(src)) + return ..() + +/obj/machinery/sleeper/relaymove(mob/user as mob) + if(user.incapacitated()) + return 0 //maybe they should be able to get out with cuffs, but whatever + go_out() + +/obj/machinery/sleeper/process() + if(filtering > 0) + if(beaker) + // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. + if(!istype(occupant) || !occupant.dna || (NO_BLOOD in occupant.dna.species.species_traits)) + filtering = 0 + return + + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + occupant.transfer_blood_to(beaker, 1) + for(var/datum/reagent/x in occupant.reagents.reagent_list) + occupant.reagents.trans_to(beaker, 3) + occupant.transfer_blood_to(beaker, 1) + + if(occupant) + for(var/A in occupant.reagents.addiction_list) + var/datum/reagent/R = A + + var/addiction_removal_chance = 5 + if(world.timeofday > (R.last_addiction_dose + ADDICTION_SPEEDUP_TIME)) // 2.5 minutes + addiction_removal_chance = 10 + if(prob(addiction_removal_chance)) + to_chat(occupant, "You no longer feel reliant on [R.name]!") + occupant.reagents.addiction_list.Remove(R) + qdel(R) + + for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp + if(M == occupant) + continue + else + M.forceMove(loc) + + updateDialog() + return + + +/obj/machinery/sleeper/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attack_ghost(mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + ui_interact(user) + +/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) + var/data[0] + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + var/crisis = 0 + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.dna.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.dna.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + + crisis = (occupant.health < min_health) + // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent + // Runtime is aptly named, isn't she? + if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.blood_volume) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) + + data["occupant"] = occupantData + data["maxchem"] = max_chem + data["minhealth"] = min_health + data["dialysis"] = filtering + if(beaker) + data["isBeakerLoaded"] = 1 + if(beaker.reagents) + data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) + else + data["beakerFreeSpace"] = 0 + + var/chemicals[0] + for(var/re in possible_chems) + var/datum/reagent/temp = GLOB.chemical_reagents_list[re] + if(temp) + var/reagent_amount = 0 + var/pretty_amount + var/injectable = occupant ? 1 : 0 + var/overdosing = 0 + var/caution = 0 // To make things clear that you're coming close to an overdose + if(crisis && !(temp.id in emergency_chems)) + injectable = 0 + + if(occupant && occupant.reagents) + reagent_amount = occupant.reagents.get_reagent_amount(temp.id) + // If they're mashing the highest concentration, they get one warning + if(temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) + caution = 1 + if(temp.id in occupant.reagents.overdose_list()) + overdosing = 1 + + // Because I don't know how to do this on the nano side + pretty_amount = round(reagent_amount, 0.05) + + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) + data["chemicals"] = chemicals + return data + +/obj/machinery/sleeper/Topic(href, href_list) + if(!controls_inside && usr == occupant) + return 0 + + if(..()) + return 1 + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return 0 + + if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) + if(href_list["chemical"]) + if(occupant) + if(occupant.stat == DEAD) + to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") + else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) + inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + if(href_list["removebeaker"]) + remove_beaker() + if(href_list["togglefilter"]) + toggle_filter() + if(href_list["ejectify"]) + eject() + add_fingerprint(usr) + return 1 + +/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/glass)) + if(!beaker) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to you!") + return + + beaker = I + I.forceMove(src) + user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") + return + + else + to_chat(user, "The sleeper has a beaker already.") + return + + if(exchange_parts(user, I)) + return + + if(istype(I, /obj/item/grab)) + var/obj/item/grab/G = I + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(!ismob(G.affecting)) + return + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(G.affecting.has_buckled_mobs()) //mob attached to us + to_chat(user, "[G.affecting] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.") + return + + visible_message("[user] starts putting [G.affecting.name] into the sleeper.") + + if(do_after(user, 20, target = G.affecting)) + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(!G || !G.affecting) + return + var/mob/M = G.affecting + M.forceMove(src) + occupant = M + icon_state = "[base_icon]" + to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") + add_fingerprint(user) + qdel(G) + return + + return ..() + + +/obj/machinery/sleeper/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/sleeper/screwdriver_act(mob/user, obj/item/I) + if(occupant) + to_chat(user, "The maintenance panel is locked.") + return TRUE + if(default_deconstruction_screwdriver(user, "[base_icon]-o", "[base_icon]-open", I)) + return TRUE + +/obj/machinery/sleeper/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(occupant) + to_chat(user, "The scanner is occupied.") + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(dir == EAST) + orient = "LEFT" + setDir(WEST) + else + orient = "RIGHT" + setDir(EAST) + +/obj/machinery/sleeper/ex_act(severity) + if(filtering) + toggle_filter() + if(occupant) + occupant.ex_act(severity) + ..() + +/obj/machinery/sleeper/handle_atom_del(atom/A) + ..() + if(A == occupant) + occupant = null + updateUsrDialog() + update_icon() + if(A == beaker) + beaker = null + updateUsrDialog() + +/obj/machinery/sleeper/emp_act(severity) + if(filtering) + toggle_filter() + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(occupant) + go_out() + ..(severity) + +/obj/machinery/sleeper/narsie_act() + go_out() + new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! + qdel(src) + +/obj/machinery/sleeper/proc/toggle_filter() + if(filtering) + filtering = 0 + else + filtering = 1 + +/obj/machinery/sleeper/proc/go_out() + if(filtering) + toggle_filter() + if(!occupant) + return + occupant.forceMove(loc) + occupant = null + icon_state = "[base_icon]-open" + // eject trash the occupant dropped + for(var/atom/movable/A in contents - component_parts - list(beaker)) + A.forceMove(loc) + +/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) + if(!(chemical in possible_chems)) + to_chat(user, "The sleeper does not offer that chemical!") + return + + if(occupant) + if(occupant.reagents) + if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) + occupant.reagents.add_reagent(chemical, amount) + return + else + to_chat(user, "You can not inject any more of this chemical.") + return + else + to_chat(user, "The patient rejects the chemicals!") + return + else + to_chat(user, "There's no occupant in the sleeper!") + return + +/obj/machinery/sleeper/verb/eject() + set name = "Eject Sleeper" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + icon_state = "[base_icon]-open" + go_out() + add_fingerprint(usr) + return + +/obj/machinery/sleeper/verb/remove_beaker() + set name = "Remove Beaker" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + if(beaker) + filtering = 0 + beaker.forceMove(usr.loc) + beaker = null + add_fingerprint(usr) + return + +/obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if(O.loc == user) //no you can't pull things out of your ass + return + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source + return + if(!ismob(O)) //humans only + return + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit + return + if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper + return + if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems + return + if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + var/mob/living/L = O + if(!istype(L) || L.buckled) + return + if(L.abiotic()) + to_chat(user, "Subject cannot have abiotic items on.") + return + if(L.has_buckled_mobs()) //mob attached to us + to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") + return + if(L == user) + visible_message("[user] starts climbing into the sleeper.") + else + visible_message("[user] starts putting [L.name] into the sleeper.") + + if(do_after(user, 20, target = L)) + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(!L) return + L.forceMove(src) + occupant = L + icon_state = "[base_icon]" + to_chat(L, "You feel cool air surround you. You go numb as your senses turn inward.") + add_fingerprint(user) + if(user.pulling == L) + user.stop_pulling() + return + return + +/obj/machinery/sleeper/AllowDrop() + return FALSE + +/obj/machinery/sleeper/verb/move_inside() + set name = "Enter Sleeper" + set category = "Object" + set src in oview(1) + if(usr.stat != 0 || !(ishuman(usr))) + return + if(occupant) + to_chat(usr, "The sleeper is already occupied!") + return + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(usr.has_buckled_mobs()) //mob attached to us + to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") + return + visible_message("[usr] starts climbing into the sleeper.") + if(do_after(usr, 20, target = usr)) + if(occupant) + to_chat(usr, "The sleeper is already occupied!") + return + usr.stop_pulling() + usr.forceMove(src) + occupant = usr + icon_state = "[base_icon]" + + for(var/obj/O in src) + qdel(O) + add_fingerprint(usr) + return + return + +/obj/machinery/sleeper/syndie + icon_state = "sleeper_s-open" + base_icon = "sleeper_s" + possible_chems = list("epinephrine", "ether", "salbutamol", "styptic_powder", "silver_sulfadiazine") + emergency_chems = list("epinephrine") + controls_inside = TRUE + + light_color = LIGHT_COLOR_DARKRED + +/obj/machinery/sleeper/syndie/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper/syndicate(null) + var/obj/item/stock_parts/matter_bin/B = new(null) + B.rating = initial_bin_rating + component_parts += B + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +#undef ADDICTION_SPEEDUP_TIME diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index edb12487a68e..9d96ec94b994 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -425,6 +425,7 @@ var/AN = "" var/open = "" var/infected = "" + var/dead = "" var/robot = "" var/imp = "" var/bled = "" @@ -439,6 +440,8 @@ splint = "Splinted:" if(e.status & ORGAN_BROKEN) AN = "[e.broken_description]:" + if(e.status & ORGAN_DEAD) + dead = "DEAD:" if(e.is_robotic()) robot = "Robotic:" if(e.open) @@ -454,9 +457,9 @@ infected = "Acute Infection:" if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) infected = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 400) + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 399) infected = "Acute Infection++:" - if(INFECTION_LEVEL_THREE to INFINITY) + if(INFECTION_LEVEL_TWO + 400 to INFINITY) infected = "Septic:" var/unknown_body = 0 @@ -467,11 +470,14 @@ imp += "Unknown body present:" if(!AN && !open && !infected & !imp) AN = "None:" - dat += "
    " + dat += "" dat += "" for(var/obj/item/organ/internal/i in occupant.internal_organs) var/mech = i.desc var/infection = "None" + var/dead = "" + if(i.status & ORGAN_DEAD) + dead = "DEAD:" switch(i.germ_level) if(1 to INFECTION_LEVEL_ONE + 200) infection = "Mild Infection:" @@ -483,11 +489,13 @@ infection = "Acute Infection:" if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) infection = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFINITY) + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 399) infection = "Acute Infection++:" + if(INFECTION_LEVEL_TWO + 400 to INFINITY) + infection = "Septic:" dat += "" - dat += "" + dat += "" dat += "" dat += "
    [e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured][dead]
    [i.name]N/A[i.damage][infection]:[mech][i.name]N/A[i.damage][infection]:[mech][dead]
    " if(occupant.disabilities & BLIND) @@ -499,4 +507,4 @@ else dat += "[src] is empty." - return dat \ No newline at end of file + return dat diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index eb06f55cc7b3..eaa1120ab040 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -133,4 +133,4 @@ return if(uses >= 0) cooldown_on = FALSE - power_change() \ No newline at end of file + power_change() diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index b7fdea3389e1..02d95278e300 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1,1110 +1,1110 @@ -// A datum for dealing with threshold limit values -// used in /obj/machinery/alarm -/datum/tlv - var/min2 - var/min1 - var/max1 - var/max2 - -/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) - min2 = _min2 - min1 = _min1 - max1 = _max1 - max2 = _max2 - -/datum/tlv/proc/get_danger_level(curval as num) - if(max2 >=0 && curval>max2) - return ATMOS_ALARM_DANGER - if(min2 >=0 && curval=0 && curval>max1) - return ATMOS_ALARM_WARNING - if(min1 >=0 && curval target_temperature + 2 || regulating_temperature) - //If it goes too far, we should adjust ourselves back before stopping. - if(!cur_tlv.get_danger_level(target_temperature)) - if(!regulating_temperature) - regulating_temperature = 1 - visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.") - - if(target_temperature > MAX_TEMPERATURE) - target_temperature = MAX_TEMPERATURE - - if(target_temperature < MIN_TEMPERATURE) - target_temperature = MIN_TEMPERATURE - - var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) - var/heat_capacity = gas.heat_capacity() - var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) - - //Use power. Assuming that each power unit represents 1000 watts.... - use_power(energy_used/1000, ENVIRON) - - //We need to cool ourselves. - if(heat_capacity) - if(environment.temperature > target_temperature) - gas.temperature -= energy_used/heat_capacity - else - gas.temperature += energy_used/heat_capacity - - environment.merge(gas) - - if(abs(environment.temperature - target_temperature) <= 0.5) - regulating_temperature = 0 - visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.") - -/obj/machinery/alarm/update_icon() - if(wiresexposed) - icon_state = "alarmx" - return - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - return - - switch(max(danger_level, alarm_area.atmosalm-1)) - if(ATMOS_ALARM_NONE) - icon_state = "alarm0" - if(ATMOS_ALARM_WARNING) - icon_state = "alarm2" //yes, alarm2 is yellow alarm - if(ATMOS_ALARM_DANGER) - icon_state = "alarm1" - -/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) - var/new_name - if(device_type=="AVP") - new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" - alarm_area.air_vent_names[m_id] = new_name - else if(device_type=="AScr") - new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" - alarm_area.air_scrub_names[m_id] = new_name - else - return - spawn (10) - send_signal(m_id, list("init" = new_name) ) - -/obj/machinery/alarm/proc/refresh_all() - for(var/id_tag in alarm_area.air_vent_names) - var/list/I = alarm_area.air_vent_info[id_tag] - if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - for(var/id_tag in alarm_area.air_scrub_names) - var/list/I = alarm_area.air_scrub_info[id_tag] - if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - -/obj/machinery/alarm/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = command - signal.data["tag"] = target - signal.data["sigtype"] = "command" - - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// to_chat(world, text("Signal [] Broadcasted to []", command, target)) - - return 1 - -/obj/machinery/alarm/proc/apply_mode() - switch(mode) - if(AALARM_MODE_SCRUBBING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "o2_scrub" = (preset==AALARM_PRESET_VOX), - "n2_scrub" = 0, - "co2_scrub"= 1, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_CONTAMINATED) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 1, - "n2o_scrub"= 1, - "scrubbing"= 1, - "widenet"= 1, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_VENTING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*2 - )) - if(AALARM_MODE_REFILL) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 0, - "n2o_scrub"= 0, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*3 - )) - if( - AALARM_MODE_PANIC, - AALARM_MODE_REPLACEMENT - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 1, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if( - AALARM_MODE_SIPHON - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - - if(AALARM_MODE_OFF) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if(AALARM_MODE_FLOOD) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"=0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 0, - )) - -/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) - if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) - post_alert(new_danger_level) - - update_icon() - -/obj/machinery/alarm/proc/post_alert(alert_level) - var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) - if(!frequency) - return - - var/datum/signal/alert_signal = new - alert_signal.source = src - alert_signal.transmission_method = 1 - alert_signal.data["zone"] = alarm_area.name - alert_signal.data["type"] = "Atmospheric" - - if(alert_level==2) - alert_signal.data["alert"] = "severe" - else if(alert_level==1) - alert_signal.data["alert"] = "minor" - else if(alert_level==0) - alert_signal.data["alert"] = "clear" - - frequency.post_signal(src, alert_signal) - -/////////////// -//END HACKING// -/////////////// - -/obj/machinery/alarm/attack_ai(mob/user) - src.add_hiddenprint(user) - return ui_interact(user) - -/obj/machinery/alarm/attack_ghost(user as mob) - return interact(user) - -/obj/machinery/alarm/attack_hand(mob/user) - . = ..() - if(.) - return - return interact(user) - -/obj/machinery/alarm/interact(mob/user) - if(buildstage != 2) - return - - if(wiresexposed) - wires.Interact(user) - - if(!shorted) - ui_interact(user) - -/obj/machinery/alarm/proc/ui_air_status() - var/turf/location = get_turf(src) - if(!istype(location)) - return - - var/datum/gas_mixture/environment = location.return_air() - var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins - if(total==0) - return null - - var/datum/tlv/cur_tlv - var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume - - cur_tlv = TLV["pressure"] - var/environment_pressure = environment.return_pressure() - var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) - - cur_tlv = TLV["oxygen"] - var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) - var/oxygen_percent = round(environment.oxygen / total * 100, 2) - - cur_tlv = TLV["nitrogen"] - var/nitrogen_dangerlevel = cur_tlv.get_danger_level(environment.nitrogen*GET_PP) - var/nitrogen_percent = round(environment.nitrogen / total * 100, 2) - - cur_tlv = TLV["carbon dioxide"] - var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) - var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) - - cur_tlv = TLV["plasma"] - var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) - var/plasma_percent = round(environment.toxins / total * 100, 2) - - cur_tlv = TLV["other"] - var/other_moles = 0.0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles - var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) - - cur_tlv = TLV["temperature"] - var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) - - var/data[0] - data["pressure"] = environment_pressure - data["temperature"] = environment.temperature - data["temperature_c"] = round(environment.temperature - T0C, 0.1) - - var/percentages[0] - percentages["oxygen"] = oxygen_percent - percentages["nitrogen"] = nitrogen_percent - percentages["co2"] = co2_percent - percentages["plasma"] = plasma_percent - percentages["other"] = other_moles - data["contents"] = percentages - - var/danger[0] - danger["pressure"] = pressure_dangerlevel - danger["temperature"] = temperature_dangerlevel - danger["oxygen"] = oxygen_dangerlevel - danger["nitrogen"] = nitrogen_dangerlevel - danger["co2"] = co2_dangerlevel - danger["plasma"] = plasma_dangerlevel - danger["other"] = other_dangerlevel - danger["overall"] = max(pressure_dangerlevel,oxygen_dangerlevel,nitrogen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel) - data["danger"] = danger - return data - -/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/list/href_list = state.href_list(user) - if(href_list) - data["remote_connection"] = href_list["remote_connection"] - data["remote_access"] = href_list["remote_access"] - - data["name"] = sanitize(name) - data["air"] = ui_air_status() - data["alarmActivated"] = alarmActivated || danger_level == ATMOS_ALARM_DANGER - data["thresholds"] = generate_thresholds_menu() - - // Locked when: - // Not sent from atmos console AND - // Not silicon AND locked. - data["locked"] = !is_authenticated(user, href_list) - data["rcon"] = rcon_setting - data["target_temp"] = target_temperature - T0C - data["atmos_alarm"] = alarm_area.atmosalm - data["emagged"] = emagged - data["modes"] = list( - AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\ - AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\ - AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\ - AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\ - AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\ - AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\ - AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\ - AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\ - AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1) - ) - data["mode"] = mode - data["presets"] = list( - AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\ - AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\ - AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\ - AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms") - ) - data["preset"] = preset - data["screen"] = screen - - var/list/vents=list() - if(alarm_area.air_vent_names.len) - for(var/id_tag in alarm_area.air_vent_names) - var/vent_info[0] - var/long_name = alarm_area.air_vent_names[id_tag] - var/list/vent_data = alarm_area.air_vent_info[id_tag] - if(!vent_data) - continue - vent_info["id_tag"]=id_tag - vent_info["name"]=sanitize(long_name) - vent_info += vent_data - vents+=list(vent_info) - data["vents"]=vents - - var/list/scrubbers=list() - if(alarm_area.air_scrub_names.len) - for(var/id_tag in alarm_area.air_scrub_names) - var/long_name = alarm_area.air_scrub_names[id_tag] - var/list/scrubber_data = alarm_area.air_scrub_info[id_tag] - if(!scrubber_data) - continue - scrubber_data["id_tag"]=id_tag - scrubber_data["name"]=sanitize(long_name) - scrubbers+=list(scrubber_data) - data["scrubbers"]=scrubbers - return data - -/obj/machinery/alarm/proc/get_nano_data_console(mob/user) - var/data[0] - data["name"] = sanitize(name) - data["ref"] = "\ref[src]" - data["danger"] = max(danger_level, alarm_area.atmosalm) - var/area/Area = get_area(src) - data["area"] = sanitize(Area.name) - var/turf/pos = get_turf(src) - data["x"] = pos.x - data["y"] = pos.y - data["z"] = pos.z - return data - -/obj/machinery/alarm/proc/generate_thresholds_menu() - var/datum/tlv/selected - var/list/thresholds = list() - - var/list/gas_names = list( - "oxygen" = "O2", - "nitrogen" = "N2", - "carbon dioxide" = "CO2", - "plasma" = "Toxin", - "other" = "Other") - for(var/g in gas_names) - thresholds += list(list("name" = gas_names[g], "settings" = list())) - selected = TLV[g] - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) - - selected = TLV["pressure"] - thresholds += list(list("name" = "Pressure", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) - - selected = TLV["temperature"] - thresholds += list(list("name" = "Temperature", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) - - return thresholds - -/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list) - if(user.can_admin_interact()) - return 1 - else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list)) - return 1 - else - return !locked - -/obj/machinery/alarm/proc/is_auth_rcon(href_list) - if(href_list && href_list["remote_connection"] && href_list["remote_access"]) - return 1 - else - return 0 - -/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) - if(buildstage != 2) - return STATUS_CLOSE - - if(aidisabled && (isAI(user) || isrobot(user))) - to_chat(user, "AI control for \the [src] interface has been disabled.") - return STATUS_CLOSE - - . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE - - if(. == STATUS_INTERACTIVE) - var/extra_href = state.href_list(usr) - // Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access - if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"]) - . = STATUS_UPDATE - return min(..(), .) - -/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state) - if(..(href, href_list, nowindow, state)) - return 1 - - var/state_href = state.href_list(usr) - - if(href_list["rcon"]) - var/attempted_rcon_setting = text2num(href_list["rcon"]) - switch(attempted_rcon_setting) - if(RCON_NO) - rcon_setting = RCON_NO - if(RCON_AUTO) - rcon_setting = RCON_AUTO - if(RCON_YES) - rcon_setting = RCON_YES - return 1 - - add_fingerprint(usr) - - if(href_list["command"]) - if(!is_authenticated(usr, state_href)) - return - - var/device_id = href_list["id_tag"] - switch(href_list["command"]) - if( "power", - "adjust_external_pressure", - "set_external_pressure", - "checks", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "n2_scrub", - "o2_scrub", - "widenet", - "scrubbing", - "direction") - var/val - if(href_list["val"]) - val=text2num(href_list["val"]) - else - var/newval = input("Enter new value") as num|null - if(isnull(newval)) - return - if(href_list["command"]=="set_external_pressure") - if(newval>1000+ONE_ATMOSPHERE) - newval = 1000+ONE_ATMOSPHERE - if(newval<0) - newval = 0 - val = newval - - send_signal(device_id, list(href_list["command"] = val ) ) - waiting_on_device=device_id - - if("set_threshold") - var/env = href_list["env"] - var/varname = href_list["var"] - var/datum/tlv/tlv = TLV[env] - var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null - - if(isnull(newval) || ..(href, href_list, nowindow, state)) - return - if(newval<0) - tlv.vars[varname] = -1.0 - else if(env=="temperature" && newval>5000) - tlv.vars[varname] = 5000 - else if(env=="pressure" && newval>50*ONE_ATMOSPHERE) - tlv.vars[varname] = 50*ONE_ATMOSPHERE - else if(env!="temperature" && env!="pressure" && newval>200) - tlv.vars[varname] = 200 - else - newval = round(newval,0.01) - tlv.vars[varname] = newval - - if(href_list["screen"]) - if(!is_authenticated(usr, state_href)) - return - - screen = text2num(href_list["screen"]) - return 1 - - if(href_list["atmos_alarm"]) - if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) - apply_danger_level(ATMOS_ALARM_DANGER) - alarmActivated = 1 - update_icon() - return 1 - - if(href_list["atmos_reset"]) - if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) - apply_danger_level(ATMOS_ALARM_NONE) - alarmActivated = 0 - update_icon() - return 1 - - if(href_list["mode"]) - if(!is_authenticated(usr, state_href)) - return - - mode = text2num(href_list["mode"]) - apply_mode() - return 1 - - if(href_list["preset"]) - if(!is_authenticated(usr, state_href)) - return - - preset = text2num(href_list["preset"]) - apply_preset() - return 1 - - if(href_list["temperature"]) - var/datum/tlv/selected = TLV["temperature"] - var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE) - var/min_temperature = max(selected.min1, MIN_TEMPERATURE) - var/max_temperature_c = max_temperature - T0C - var/min_temperature_c = min_temperature - T0C - var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null - if(isnull(input_temperature) || ..(href, href_list, nowindow, state)) - return - input_temperature = input_temperature + T0C - if(input_temperature > max_temperature || input_temperature < min_temperature) - to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C") - else - target_temperature = input_temperature - return 1 - -/obj/machinery/alarm/emag_act(mob/user) - if(!emagged) - src.emagged = 1 - if(user) - user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") - playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) - return - -/obj/machinery/alarm/attackby(obj/item/I, mob/user, params) - add_fingerprint(user) - - switch(buildstage) - if(2) - if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))// trying to unlock the interface with an ID card - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "It does nothing") - return - else - if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") - updateUsrDialog() - else - to_chat(user, "Access denied.") - return - - if(1) - if(iscoil(I)) - var/obj/item/stack/cable_coil/coil = I - if(coil.amount < 5) - to_chat(user, "You need more cable for this!") - return - - to_chat(user, "You wire \the [src]!") - playsound(get_turf(src), coil.usesound, 50, 1) - coil.amount -= 5 - if(!coil.amount) - qdel(coil) - - buildstage = 2 - update_icon() - first_run() - return - if(0) - if(istype(I, /obj/item/airalarm_electronics)) - to_chat(user, "You insert the circuit!") - playsound(get_turf(src), I.usesound, 50, 1) - qdel(I) - buildstage = 1 - update_icon() - return - return ..() - -/obj/machinery/alarm/crowbar_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_BUILDING) - return - . = TRUE - if(!I.tool_start_check(user, 0)) - return - to_chat(user, "You start prying out the circuit.") - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - if(buildstage != AIR_ALARM_BUILDING) - return - to_chat(user, "You pry out the circuit!") - new /obj/item/airalarm_electronics(user.drop_location()) - buildstage = AIR_ALARM_FRAME - update_icon() - -/obj/machinery/alarm/multitool_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(wiresexposed) - attack_hand(user) - -/obj/machinery/alarm/screwdriver_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - wiresexposed = !wiresexposed - update_icon() - if(wiresexposed) - SCREWDRIVER_OPEN_PANEL_MESSAGE - else - SCREWDRIVER_CLOSE_PANEL_MESSAGE - -/obj/machinery/alarm/wirecutter_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(wires.wires_status == 31) // all wires cut - var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location()) - new_coil.amount = 5 - buildstage = AIR_ALARM_BUILDING - update_icon() - if(wiresexposed) - wires.Interact(user) - -/obj/machinery/alarm/wrench_act(mob/user, obj/item/I) - if(buildstage != AIR_ALARM_FRAME) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - new /obj/item/mounted/frame/alarm_frame(get_turf(user)) - WRENCH_UNANCHOR_WALL_MESSAGE - qdel(src) - -/obj/machinery/alarm/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - else - stat |= NOPOWER - spawn(rand(0,15)) - update_icon() - -/obj/machinery/alarm/obj_break(damage_flag) - ..() - update_icon() - -/obj/machinery/alarm/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, 2) - var/obj/item/I = new /obj/item/airalarm_electronics(loc) - if(!disassembled) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 3) - qdel(src) - -/obj/machinery/alarm/examine(mob/user) - . = ..() - if(buildstage < 2) - . += "It is not wired." - if(buildstage < 1) - . += "The circuit is missing." - -/obj/machinery/alarm/all_access - name = "all-access air alarm" - desc = "This particular atmos control unit appears to have no access restrictions." - locked = FALSE - req_access = null - req_one_access = null - -/* -AIR ALARM CIRCUIT -Just an object used in constructing air alarms -*/ -/obj/item/airalarm_electronics - name = "air alarm electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "Looks like a circuit. Probably is." - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - - -#undef AIR_ALARM_FRAME -#undef AIR_ALARM_BUILDING -#undef AIR_ALARM_READY +// A datum for dealing with threshold limit values +// used in /obj/machinery/alarm +/datum/tlv + var/min2 + var/min1 + var/max1 + var/max2 + +/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) + min2 = _min2 + min1 = _min1 + max1 = _max1 + max2 = _max2 + +/datum/tlv/proc/get_danger_level(curval as num) + if(max2 >=0 && curval>max2) + return ATMOS_ALARM_DANGER + if(min2 >=0 && curval=0 && curval>max1) + return ATMOS_ALARM_WARNING + if(min1 >=0 && curval target_temperature + 2 || regulating_temperature) + //If it goes too far, we should adjust ourselves back before stopping. + if(!cur_tlv.get_danger_level(target_temperature)) + if(!regulating_temperature) + regulating_temperature = 1 + visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.") + + if(target_temperature > MAX_TEMPERATURE) + target_temperature = MAX_TEMPERATURE + + if(target_temperature < MIN_TEMPERATURE) + target_temperature = MIN_TEMPERATURE + + var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) + var/heat_capacity = gas.heat_capacity() + var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) + + //Use power. Assuming that each power unit represents 1000 watts.... + use_power(energy_used/1000, ENVIRON) + + //We need to cool ourselves. + if(heat_capacity) + if(environment.temperature > target_temperature) + gas.temperature -= energy_used/heat_capacity + else + gas.temperature += energy_used/heat_capacity + + environment.merge(gas) + + if(abs(environment.temperature - target_temperature) <= 0.5) + regulating_temperature = 0 + visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click as a faint electronic humming stops.") + +/obj/machinery/alarm/update_icon() + if(wiresexposed) + icon_state = "alarmx" + return + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + return + + switch(max(danger_level, alarm_area.atmosalm-1)) + if(ATMOS_ALARM_NONE) + icon_state = "alarm0" + if(ATMOS_ALARM_WARNING) + icon_state = "alarm2" //yes, alarm2 is yellow alarm + if(ATMOS_ALARM_DANGER) + icon_state = "alarm1" + +/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) + var/new_name + if(device_type=="AVP") + new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" + alarm_area.air_vent_names[m_id] = new_name + else if(device_type=="AScr") + new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" + alarm_area.air_scrub_names[m_id] = new_name + else + return + spawn (10) + send_signal(m_id, list("init" = new_name) ) + +/obj/machinery/alarm/proc/refresh_all() + for(var/id_tag in alarm_area.air_vent_names) + var/list/I = alarm_area.air_vent_info[id_tag] + if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + for(var/id_tag in alarm_area.air_scrub_names) + var/list/I = alarm_area.air_scrub_info[id_tag] + if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + +/obj/machinery/alarm/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = command + signal.data["tag"] = target + signal.data["sigtype"] = "command" + + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) +// to_chat(world, text("Signal [] Broadcasted to []", command, target)) + + return 1 + +/obj/machinery/alarm/proc/apply_mode() + switch(mode) + if(AALARM_MODE_SCRUBBING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "o2_scrub" = (preset==AALARM_PRESET_VOX), + "n2_scrub" = 0, + "co2_scrub"= 1, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_CONTAMINATED) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 1, + "n2o_scrub"= 1, + "scrubbing"= 1, + "widenet"= 1, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_VENTING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*2 + )) + if(AALARM_MODE_REFILL) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 0, + "n2o_scrub"= 0, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*3 + )) + if( + AALARM_MODE_PANIC, + AALARM_MODE_REPLACEMENT + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 1, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if( + AALARM_MODE_SIPHON + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + + if(AALARM_MODE_OFF) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if(AALARM_MODE_FLOOD) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"=0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 0, + )) + +/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) + if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) + post_alert(new_danger_level) + + update_icon() + +/obj/machinery/alarm/proc/post_alert(alert_level) + var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) + if(!frequency) + return + + var/datum/signal/alert_signal = new + alert_signal.source = src + alert_signal.transmission_method = 1 + alert_signal.data["zone"] = alarm_area.name + alert_signal.data["type"] = "Atmospheric" + + if(alert_level==2) + alert_signal.data["alert"] = "severe" + else if(alert_level==1) + alert_signal.data["alert"] = "minor" + else if(alert_level==0) + alert_signal.data["alert"] = "clear" + + frequency.post_signal(src, alert_signal) + +/////////////// +//END HACKING// +/////////////// + +/obj/machinery/alarm/attack_ai(mob/user) + src.add_hiddenprint(user) + return ui_interact(user) + +/obj/machinery/alarm/attack_ghost(user as mob) + return interact(user) + +/obj/machinery/alarm/attack_hand(mob/user) + . = ..() + if(.) + return + return interact(user) + +/obj/machinery/alarm/interact(mob/user) + if(buildstage != 2) + return + + if(wiresexposed) + wires.Interact(user) + + if(!shorted) + ui_interact(user) + +/obj/machinery/alarm/proc/ui_air_status() + var/turf/location = get_turf(src) + if(!istype(location)) + return + + var/datum/gas_mixture/environment = location.return_air() + var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins + if(total==0) + return null + + var/datum/tlv/cur_tlv + var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume + + cur_tlv = TLV["pressure"] + var/environment_pressure = environment.return_pressure() + var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) + + cur_tlv = TLV["oxygen"] + var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) + var/oxygen_percent = round(environment.oxygen / total * 100, 2) + + cur_tlv = TLV["nitrogen"] + var/nitrogen_dangerlevel = cur_tlv.get_danger_level(environment.nitrogen*GET_PP) + var/nitrogen_percent = round(environment.nitrogen / total * 100, 2) + + cur_tlv = TLV["carbon dioxide"] + var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) + var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) + + cur_tlv = TLV["plasma"] + var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) + var/plasma_percent = round(environment.toxins / total * 100, 2) + + cur_tlv = TLV["other"] + var/other_moles = 0.0 + for(var/datum/gas/G in environment.trace_gases) + other_moles+=G.moles + var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) + + cur_tlv = TLV["temperature"] + var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) + + var/data[0] + data["pressure"] = environment_pressure + data["temperature"] = environment.temperature + data["temperature_c"] = round(environment.temperature - T0C, 0.1) + + var/percentages[0] + percentages["oxygen"] = oxygen_percent + percentages["nitrogen"] = nitrogen_percent + percentages["co2"] = co2_percent + percentages["plasma"] = plasma_percent + percentages["other"] = other_moles + data["contents"] = percentages + + var/danger[0] + danger["pressure"] = pressure_dangerlevel + danger["temperature"] = temperature_dangerlevel + danger["oxygen"] = oxygen_dangerlevel + danger["nitrogen"] = nitrogen_dangerlevel + danger["co2"] = co2_dangerlevel + danger["plasma"] = plasma_dangerlevel + danger["other"] = other_dangerlevel + danger["overall"] = max(pressure_dangerlevel,oxygen_dangerlevel,nitrogen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel) + data["danger"] = danger + return data + +/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + var/list/href_list = state.href_list(user) + if(href_list) + data["remote_connection"] = href_list["remote_connection"] + data["remote_access"] = href_list["remote_access"] + + data["name"] = sanitize(name) + data["air"] = ui_air_status() + data["alarmActivated"] = alarmActivated || danger_level == ATMOS_ALARM_DANGER + data["thresholds"] = generate_thresholds_menu() + + // Locked when: + // Not sent from atmos console AND + // Not silicon AND locked. + data["locked"] = !is_authenticated(user, href_list) + data["rcon"] = rcon_setting + data["target_temp"] = target_temperature - T0C + data["atmos_alarm"] = alarm_area.atmosalm + data["emagged"] = emagged + data["modes"] = list( + AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\ + AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\ + AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\ + AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\ + AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\ + AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\ + AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\ + AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\ + AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1) + ) + data["mode"] = mode + data["presets"] = list( + AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\ + AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\ + AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\ + AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms") + ) + data["preset"] = preset + data["screen"] = screen + + var/list/vents=list() + if(alarm_area.air_vent_names.len) + for(var/id_tag in alarm_area.air_vent_names) + var/vent_info[0] + var/long_name = alarm_area.air_vent_names[id_tag] + var/list/vent_data = alarm_area.air_vent_info[id_tag] + if(!vent_data) + continue + vent_info["id_tag"]=id_tag + vent_info["name"]=sanitize(long_name) + vent_info += vent_data + vents+=list(vent_info) + data["vents"]=vents + + var/list/scrubbers=list() + if(alarm_area.air_scrub_names.len) + for(var/id_tag in alarm_area.air_scrub_names) + var/long_name = alarm_area.air_scrub_names[id_tag] + var/list/scrubber_data = alarm_area.air_scrub_info[id_tag] + if(!scrubber_data) + continue + scrubber_data["id_tag"]=id_tag + scrubber_data["name"]=sanitize(long_name) + scrubbers+=list(scrubber_data) + data["scrubbers"]=scrubbers + return data + +/obj/machinery/alarm/proc/get_nano_data_console(mob/user) + var/data[0] + data["name"] = sanitize(name) + data["ref"] = "\ref[src]" + data["danger"] = max(danger_level, alarm_area.atmosalm) + var/area/Area = get_area(src) + data["area"] = sanitize(Area.name) + var/turf/pos = get_turf(src) + data["x"] = pos.x + data["y"] = pos.y + data["z"] = pos.z + return data + +/obj/machinery/alarm/proc/generate_thresholds_menu() + var/datum/tlv/selected + var/list/thresholds = list() + + var/list/gas_names = list( + "oxygen" = "O2", + "nitrogen" = "N2", + "carbon dioxide" = "CO2", + "plasma" = "Toxin", + "other" = "Other") + for(var/g in gas_names) + thresholds += list(list("name" = gas_names[g], "settings" = list())) + selected = TLV[g] + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) + + selected = TLV["pressure"] + thresholds += list(list("name" = "Pressure", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) + + selected = TLV["temperature"] + thresholds += list(list("name" = "Temperature", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) + + return thresholds + +/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list) + if(user.can_admin_interact()) + return 1 + else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list)) + return 1 + else + return !locked + +/obj/machinery/alarm/proc/is_auth_rcon(href_list) + if(href_list && href_list["remote_connection"] && href_list["remote_access"]) + return 1 + else + return 0 + +/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) + if(buildstage != 2) + return STATUS_CLOSE + + if(aidisabled && (isAI(user) || isrobot(user))) + to_chat(user, "AI control for \the [src] interface has been disabled.") + return STATUS_CLOSE + + . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE + + if(. == STATUS_INTERACTIVE) + var/extra_href = state.href_list(usr) + // Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access + if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"]) + . = STATUS_UPDATE + return min(..(), .) + +/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state) + if(..(href, href_list, nowindow, state)) + return 1 + + var/state_href = state.href_list(usr) + + if(href_list["rcon"]) + var/attempted_rcon_setting = text2num(href_list["rcon"]) + switch(attempted_rcon_setting) + if(RCON_NO) + rcon_setting = RCON_NO + if(RCON_AUTO) + rcon_setting = RCON_AUTO + if(RCON_YES) + rcon_setting = RCON_YES + return 1 + + add_fingerprint(usr) + + if(href_list["command"]) + if(!is_authenticated(usr, state_href)) + return + + var/device_id = href_list["id_tag"] + switch(href_list["command"]) + if( "power", + "adjust_external_pressure", + "set_external_pressure", + "checks", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "n2_scrub", + "o2_scrub", + "widenet", + "scrubbing", + "direction") + var/val + if(href_list["val"]) + val=text2num(href_list["val"]) + else + var/newval = input("Enter new value") as num|null + if(isnull(newval)) + return + if(href_list["command"]=="set_external_pressure") + if(newval>1000+ONE_ATMOSPHERE) + newval = 1000+ONE_ATMOSPHERE + if(newval<0) + newval = 0 + val = newval + + send_signal(device_id, list(href_list["command"] = val ) ) + waiting_on_device=device_id + + if("set_threshold") + var/env = href_list["env"] + var/varname = href_list["var"] + var/datum/tlv/tlv = TLV[env] + var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null + + if(isnull(newval) || ..(href, href_list, nowindow, state)) + return + if(newval<0) + tlv.vars[varname] = -1.0 + else if(env=="temperature" && newval>5000) + tlv.vars[varname] = 5000 + else if(env=="pressure" && newval>50*ONE_ATMOSPHERE) + tlv.vars[varname] = 50*ONE_ATMOSPHERE + else if(env!="temperature" && env!="pressure" && newval>200) + tlv.vars[varname] = 200 + else + newval = round(newval,0.01) + tlv.vars[varname] = newval + + if(href_list["screen"]) + if(!is_authenticated(usr, state_href)) + return + + screen = text2num(href_list["screen"]) + return 1 + + if(href_list["atmos_alarm"]) + if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) + apply_danger_level(ATMOS_ALARM_DANGER) + alarmActivated = 1 + update_icon() + return 1 + + if(href_list["atmos_reset"]) + if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) + apply_danger_level(ATMOS_ALARM_NONE) + alarmActivated = 0 + update_icon() + return 1 + + if(href_list["mode"]) + if(!is_authenticated(usr, state_href)) + return + + mode = text2num(href_list["mode"]) + apply_mode() + return 1 + + if(href_list["preset"]) + if(!is_authenticated(usr, state_href)) + return + + preset = text2num(href_list["preset"]) + apply_preset() + return 1 + + if(href_list["temperature"]) + var/datum/tlv/selected = TLV["temperature"] + var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE) + var/min_temperature = max(selected.min1, MIN_TEMPERATURE) + var/max_temperature_c = max_temperature - T0C + var/min_temperature_c = min_temperature - T0C + var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null + if(isnull(input_temperature) || ..(href, href_list, nowindow, state)) + return + input_temperature = input_temperature + T0C + if(input_temperature > max_temperature || input_temperature < min_temperature) + to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C") + else + target_temperature = input_temperature + return 1 + +/obj/machinery/alarm/emag_act(mob/user) + if(!emagged) + src.emagged = 1 + if(user) + user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") + playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) + return + +/obj/machinery/alarm/attackby(obj/item/I, mob/user, params) + add_fingerprint(user) + + switch(buildstage) + if(2) + if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))// trying to unlock the interface with an ID card + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "It does nothing") + return + else + if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") + updateUsrDialog() + else + to_chat(user, "Access denied.") + return + + if(1) + if(iscoil(I)) + var/obj/item/stack/cable_coil/coil = I + if(coil.amount < 5) + to_chat(user, "You need more cable for this!") + return + + to_chat(user, "You wire \the [src]!") + playsound(get_turf(src), coil.usesound, 50, 1) + coil.amount -= 5 + if(!coil.amount) + qdel(coil) + + buildstage = 2 + update_icon() + first_run() + return + if(0) + if(istype(I, /obj/item/airalarm_electronics)) + to_chat(user, "You insert the circuit!") + playsound(get_turf(src), I.usesound, 50, 1) + qdel(I) + buildstage = 1 + update_icon() + return + return ..() + +/obj/machinery/alarm/crowbar_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_BUILDING) + return + . = TRUE + if(!I.tool_start_check(user, 0)) + return + to_chat(user, "You start prying out the circuit.") + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + if(buildstage != AIR_ALARM_BUILDING) + return + to_chat(user, "You pry out the circuit!") + new /obj/item/airalarm_electronics(user.drop_location()) + buildstage = AIR_ALARM_FRAME + update_icon() + +/obj/machinery/alarm/multitool_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wiresexposed) + attack_hand(user) + +/obj/machinery/alarm/screwdriver_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + wiresexposed = !wiresexposed + update_icon() + if(wiresexposed) + SCREWDRIVER_OPEN_PANEL_MESSAGE + else + SCREWDRIVER_CLOSE_PANEL_MESSAGE + +/obj/machinery/alarm/wirecutter_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wires.wires_status == 31) // all wires cut + var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location()) + new_coil.amount = 5 + buildstage = AIR_ALARM_BUILDING + update_icon() + if(wiresexposed) + wires.Interact(user) + +/obj/machinery/alarm/wrench_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_FRAME) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new /obj/item/mounted/frame/alarm_frame(get_turf(user)) + WRENCH_UNANCHOR_WALL_MESSAGE + qdel(src) + +/obj/machinery/alarm/power_change() + if(powered(power_channel)) + stat &= ~NOPOWER + else + stat |= NOPOWER + spawn(rand(0,15)) + update_icon() + +/obj/machinery/alarm/obj_break(damage_flag) + ..() + update_icon() + +/obj/machinery/alarm/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, 2) + var/obj/item/I = new /obj/item/airalarm_electronics(loc) + if(!disassembled) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 3) + qdel(src) + +/obj/machinery/alarm/examine(mob/user) + . = ..() + if(buildstage < 2) + . += "It is not wired." + if(buildstage < 1) + . += "The circuit is missing." + +/obj/machinery/alarm/all_access + name = "all-access air alarm" + desc = "This particular atmos control unit appears to have no access restrictions." + locked = FALSE + req_access = null + req_one_access = null + +/* +AIR ALARM CIRCUIT +Just an object used in constructing air alarms +*/ +/obj/item/airalarm_electronics + name = "air alarm electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "Looks like a circuit. Probably is." + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + + +#undef AIR_ALARM_FRAME +#undef AIR_ALARM_BUILDING +#undef AIR_ALARM_READY diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 230401b673b3..d91fba5fa5c6 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,606 +1,605 @@ -/datum/canister_icons - var - possiblemaincolor = list( //these lists contain the possible colors of a canister - list("name" = "\[N2O\]", "icon" = "redws"), - list("name" = "\[N2\]", "icon" = "red"), - list("name" = "\[O2\]", "icon" = "blue"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange"), - list("name" = "\[CO2\]", "icon" = "black"), - list("name" = "\[Air\]", "icon" = "grey"), - list("name" = "\[CAUTION\]", "icon" = "yellow"), - list("name" = "\[SPECIAL\]", "icon" = "whiters") - ) - possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists - list("name" = "\[N2\]", "icon" = "red-c"), - list("name" = "\[O2\]", "icon" = "blue-c"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), - list("name" = "\[CO2\]", "icon" = "black-c"), - list("name" = "\[Air\]", "icon" = "grey-c"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c") - ) - possibletertcolor = list( - list("name" = "\[N2\]", "icon" = "red-c-1"), - list("name" = "\[O2\]", "icon" = "blue-c-1"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), - list("name" = "\[CO2\]", "icon" = "black-c-1"), - list("name" = "\[Air\]", "icon" = "grey-c-1"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") - ) - possiblequartcolor = list( - list("name" = "\[N2\]", "icon" = "red-c-2"), - list("name" = "\[O2\]", "icon" = "blue-c-2"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), - list("name" = "\[CO2\]", "icon" = "black-c-2"), - list("name" = "\[Air\]", "icon" = "grey-c-2"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") - ) - - possibledecals = list( //var that stores all possible decals, used by ui - list("name" = "Low temperature canister", "icon" = "cold"), - list("name" = "High temperature canister", "icon" = "hot"), - list("name" = "Plasma containing canister", "icon" = "plasma") - ) - -var/datum/canister_icons/canister_icon_container = new() - -/obj/machinery/portable_atmospherics/canister - name = "canister" - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = 1 - flags = CONDUCT - armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50) - max_integrity = 250 - integrity_failure = 100 - - var/menu = 0 - //used by nanoui: 0 = main menu, 1 = relabel - - var/valve_open = 0 - var/release_pressure = ONE_ATMOSPHERE - - var/list/canister_color //variable that stores colours - var/list/decals //list that stores the decals - - //lists for check_change() - var/list/oldcolor - var/list/olddecals - - //passed to the ui to render the color lists - var/list/colorcontainer - var/list/possibledecals - - var/can_label = 1 - var/filled = 0.5 - pressure_resistance = 7 * ONE_ATMOSPHERE - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = NO_POWER_USE - interact_offline = 1 - var/release_log = "" - var/update_flag = 0 - -/obj/machinery/portable_atmospherics/canister/New() - ..() - canister_color = list( - "prim" = "yellow", - "sec" = "none", - "ter" = "none", - "quart" = "none") - oldcolor = new /list() - decals = list("cold" = 0, "hot" = 0, "plasma" = 0) - colorcontainer = list() - possibledecals = list() - update_icon() - -/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() - //passed to the ui to render the color lists - colorcontainer = list( - "prim" = list( - "options" = canister_icon_container.possiblemaincolor, - "name" = "Primary color", - ), - "sec" = list( - "options" = canister_icon_container.possibleseccolor, - "name" = "Secondary color", - ), - "ter" = list( - "options" = canister_icon_container.possibletertcolor, - "name" = "Tertiary color", - ), - "quart" = list( - "options" = canister_icon_container.possiblequartcolor, - "name" = "Quaternary color", - ) - ) - - //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied - for(var/C in colorcontainer) - if(C == "prim") continue - var/list/L = colorcontainer[C] - if(!(canister_color[C]) || (canister_color[C] == "none")) - L.Add(list("anycolor" = 0)) - else - L.Add(list("anycolor" = 1)) - colorcontainer[C] = L - - possibledecals = list() - - var/i - var/list/L = canister_icon_container.possibledecals - for(i=1;i<=L.len;i++) - var/list/LL = L[i] - LL = LL.Copy() //make sure we don't edit the datum list - LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI - possibledecals.Add(LL) - -/obj/machinery/portable_atmospherics/canister/proc/check_change() - var/old_flag = update_flag - update_flag = 0 - if(holding) - update_flag |= 1 - if(connected_port) - update_flag |= 2 - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < 10) - update_flag |= 4 - else if(tank_pressure < ONE_ATMOSPHERE) - update_flag |= 8 - else if(tank_pressure < 15*ONE_ATMOSPHERE) - update_flag |= 16 - else - update_flag |= 32 - - if(list2params(oldcolor) != list2params(canister_color)) - update_flag |= 64 - oldcolor = canister_color.Copy() - - if(list2params(olddecals) != list2params(decals)) - update_flag |= 128 - olddecals = decals.Copy() - - if(update_flag == old_flag) - return 1 - else - return 0 - -/obj/machinery/portable_atmospherics/canister/update_icon() -/* -update_flag -1 = holding -2 = connected_port -4 = tank_pressure < 10 -8 = tank_pressure < ONE_ATMOS -16 = tank_pressure < 15*ONE_ATMOS -32 = tank_pressure go boom. -64 = colors -128 = decals -(note: colors and decals has to be applied every icon update) -*/ - - if(src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. - return - - if(icon_state != src.canister_color["prim"]) - icon_state = src.canister_color["prim"] - - if(check_change()) //Returns 1 if no change needed to icons. - return - - overlays.Cut() - - for(var/C in canister_color) - if(C == "prim") continue - if(canister_color[C] == "none") continue - overlays.Add(canister_color[C]) - - for(var/D in decals) - if(decals[D]) - overlays.Add("decal-" + D) - - if(update_flag & 1) - overlays += "can-open" - if(update_flag & 2) - overlays += "can-connector" - if(update_flag & 4) - overlays += "can-o0" - if(update_flag & 8) - overlays += "can-o1" - else if(update_flag & 16) - overlays += "can-o2" - else if(update_flag & 32) - overlays += "can-o3" - - update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. - return - -//template modification exploit prevention, used in Topic() -/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") - if(checkColor == "prim" || checkColor == "all") - for(var/list/L in canister_icon_container.possiblemaincolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "sec" || checkColor == "all") - for(var/list/L in canister_icon_container.possibleseccolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "ter" || checkColor == "all") - for(var/list/L in canister_icon_container.possibletertcolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "quart" || checkColor == "all") - for(var/list/L in canister_icon_container.possiblequartcolor) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) - for(var/list/L in canister_icon_container.possibledecals) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > temperature_resistance) - take_damage(5, BURN, 0) - -/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - canister_break() - if(disassembled) - new /obj/item/stack/sheet/metal (loc, 10) - else - new /obj/item/stack/sheet/metal (loc, 5) - qdel(src) - -/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag) - if((stat & BROKEN) || (flags & NODECONSTRUCT)) - return - canister_break() - -/obj/machinery/portable_atmospherics/canister/proc/canister_break() - disconnect() - var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) - var/turf/T = get_turf(src) - T.assume_air(expelled_gas) - air_update_turf() - - stat |= BROKEN - density = FALSE - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - update_icon() - - if(holding) - holding.forceMove(T) - holding = null - -/obj/machinery/portable_atmospherics/canister/process_atmos() - if(destroyed) - return - - ..() - - if(valve_open) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/env_pressure = environment.return_pressure() - var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - air_update_turf() - src.update_icon() - - - if(air_contents.return_pressure() < 1) - can_label = 1 - else - can_label = 0 - - src.updateDialog() - return - -/obj/machinery/portable_atmospherics/canister/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.return_pressure() - return 0 - -/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve) - . = ..() - if(.) - if(close_valve) - valve_open = FALSE - update_icon() - investigate_log("Valve was closed by [key_name(user)].
    ", "atmos") - else if(valve_open && holding) - investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") - -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) - return src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - if(src.destroyed) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - - -/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) - init_data_vars() //set up var/colorcontainer and var/possibledecals - - // this is the data which will be sent to the ui - var/data[0] - data["name"] = name - data["menu"] = menu ? 1 : 0 - data["canLabel"] = can_label ? 1 : 0 - data["canister_color"] = canister_color - data["colorContainer"] = colorcontainer.Copy() - colorcontainer.Cut() - data["possibleDecals"] = possibledecals.Copy() - possibledecals.Cut() - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) - data["valveOpen"] = valve_open ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) - - return data - -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["choice"] == "menu") - menu = text2num(href_list["mode_target"]) - - if(href_list["toggle"]) - var/logmsg - if(valve_open) - if(holding) - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " - else - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
    " - else - if(holding) - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
    " - else - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
    " - if(air_contents.toxins > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") - investigate_log(logmsg, "atmos") - release_log += logmsg - valve_open = !valve_open - - if(href_list["remove_tank"]) - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " - holding.loc = loc - holding = null - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - if(diff > 0) - release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) - else - release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) - - if(href_list["rename"]) - if(can_label) - var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) - if(can_label) //Exploit prevention - if(T) - name = T - else - name = "canister" - else - to_chat(usr, "As you attempted to rename it the pressure rose!") - - if(href_list["choice"] == "Primary color") - if(is_a_color(href_list["icon"],"prim")) - canister_color["prim"] = href_list["icon"] - if(href_list["choice"] == "Secondary color") - if(href_list["icon"] == "none") - canister_color["sec"] = "none" - else if(is_a_color(href_list["icon"],"sec")) - canister_color["sec"] = href_list["icon"] - if(href_list["choice"] == "Tertiary color") - if(href_list["icon"] == "none") - canister_color["ter"] = "none" - else if(is_a_color(href_list["icon"],"ter")) - canister_color["ter"] = href_list["icon"] - if(href_list["choice"] == "Quaternary color") - if(href_list["icon"] == "none") - canister_color["quart"] = "none" - else if(is_a_color(href_list["icon"],"quart")) - canister_color["quart"] = href_list["icon"] - - if(href_list["choice"] == "decals") - if(is_a_decal(href_list["icon"])) - decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) - - src.add_fingerprint(usr) - update_icon() - - return 1 - - -/obj/machinery/portable_atmospherics/canister/toxins - name = "Canister \[Toxin (Plasma)\]" - icon_state = "orange" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent - name = "Canister: \[N2O\]" - icon_state = "redws" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/nitrogen - name = "Canister: \[N2\]" - icon_state = "red" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/air - name = "Canister \[Air\]" - icon_state = "grey" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/custom_mix - name = "Canister \[Custom\]" - icon_state = "whiters" //See New() - can_label = 0 - - -/obj/machinery/portable_atmospherics/canister/toxins/New() - ..() - - canister_color["prim"] = "orange" - decals["plasma"] = 1 - src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/New() - ..() - - canister_color["prim"] = "blue" - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() - ..() - - canister_color["prim"] = "redws" - var/datum/gas/sleeping_agent/trace_gas = new - air_contents.trace_gases += trace_gas - trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - - -//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() - ..() - var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] - trace_gas.moles = 9*4000 - spawn(100) - var/turf/simulated/location = src.loc - if(istype(src.loc)) - while(!location.air) - sleep(1000) - location.assume_air(air_contents) - air_contents = new - return 1 - - -/obj/machinery/portable_atmospherics/canister/nitrogen/New() - ..() - - canister_color["prim"] = "red" - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() - ..() - - canister_color["prim"] = "black" - src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - - -/obj/machinery/portable_atmospherics/canister/air/New() - ..() - - canister_color["prim"] = "grey" - src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/custom_mix/New() - ..() - - canister_color["prim"] = "whiters" - src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD - return 1 - -/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) - if(!(stat & BROKEN)) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 50, volume = I.tool_volume)) - to_chat(user, "You salvage whats left of [src]!") - new /obj/item/stack/sheet/metal(drop_location(), 3) - qdel(src) +/datum/canister_icons + var + possiblemaincolor = list( //these lists contain the possible colors of a canister + list("name" = "\[N2O\]", "icon" = "redws"), + list("name" = "\[N2\]", "icon" = "red"), + list("name" = "\[O2\]", "icon" = "blue"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange"), + list("name" = "\[CO2\]", "icon" = "black"), + list("name" = "\[Air\]", "icon" = "grey"), + list("name" = "\[CAUTION\]", "icon" = "yellow"), + list("name" = "\[SPECIAL\]", "icon" = "whiters") + ) + possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists + list("name" = "\[N2\]", "icon" = "red-c"), + list("name" = "\[O2\]", "icon" = "blue-c"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), + list("name" = "\[CO2\]", "icon" = "black-c"), + list("name" = "\[Air\]", "icon" = "grey-c"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c") + ) + possibletertcolor = list( + list("name" = "\[N2\]", "icon" = "red-c-1"), + list("name" = "\[O2\]", "icon" = "blue-c-1"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), + list("name" = "\[CO2\]", "icon" = "black-c-1"), + list("name" = "\[Air\]", "icon" = "grey-c-1"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") + ) + possiblequartcolor = list( + list("name" = "\[N2\]", "icon" = "red-c-2"), + list("name" = "\[O2\]", "icon" = "blue-c-2"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), + list("name" = "\[CO2\]", "icon" = "black-c-2"), + list("name" = "\[Air\]", "icon" = "grey-c-2"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") + ) + + possibledecals = list( //var that stores all possible decals, used by ui + list("name" = "Low temperature canister", "icon" = "cold"), + list("name" = "High temperature canister", "icon" = "hot"), + list("name" = "Plasma containing canister", "icon" = "plasma") + ) +GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new()) + +/obj/machinery/portable_atmospherics/canister + name = "canister" + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = 1 + flags = CONDUCT + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50) + max_integrity = 250 + integrity_failure = 100 + + var/menu = 0 + //used by nanoui: 0 = main menu, 1 = relabel + + var/valve_open = 0 + var/release_pressure = ONE_ATMOSPHERE + + var/list/canister_color //variable that stores colours + var/list/decals //list that stores the decals + + //lists for check_change() + var/list/oldcolor + var/list/olddecals + + //passed to the ui to render the color lists + var/list/colorcontainer + var/list/possibledecals + + var/can_label = 1 + var/filled = 0.5 + pressure_resistance = 7 * ONE_ATMOSPHERE + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = NO_POWER_USE + interact_offline = 1 + var/release_log = "" + var/update_flag = 0 + +/obj/machinery/portable_atmospherics/canister/New() + ..() + canister_color = list( + "prim" = "yellow", + "sec" = "none", + "ter" = "none", + "quart" = "none") + oldcolor = new /list() + decals = list("cold" = 0, "hot" = 0, "plasma" = 0) + colorcontainer = list() + possibledecals = list() + update_icon() + +/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() + //passed to the ui to render the color lists + colorcontainer = list( + "prim" = list( + "options" = GLOB.canister_icon_container.possiblemaincolor, + "name" = "Primary color", + ), + "sec" = list( + "options" = GLOB.canister_icon_container.possibleseccolor, + "name" = "Secondary color", + ), + "ter" = list( + "options" = GLOB.canister_icon_container.possibletertcolor, + "name" = "Tertiary color", + ), + "quart" = list( + "options" = GLOB.canister_icon_container.possiblequartcolor, + "name" = "Quaternary color", + ) + ) + + //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied + for(var/C in colorcontainer) + if(C == "prim") continue + var/list/L = colorcontainer[C] + if(!(canister_color[C]) || (canister_color[C] == "none")) + L.Add(list("anycolor" = 0)) + else + L.Add(list("anycolor" = 1)) + colorcontainer[C] = L + + possibledecals = list() + + var/i + var/list/L = GLOB.canister_icon_container.possibledecals + for(i=1;i<=L.len;i++) + var/list/LL = L[i] + LL = LL.Copy() //make sure we don't edit the datum list + LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI + possibledecals.Add(LL) + +/obj/machinery/portable_atmospherics/canister/proc/check_change() + var/old_flag = update_flag + update_flag = 0 + if(holding) + update_flag |= 1 + if(connected_port) + update_flag |= 2 + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < 10) + update_flag |= 4 + else if(tank_pressure < ONE_ATMOSPHERE) + update_flag |= 8 + else if(tank_pressure < 15*ONE_ATMOSPHERE) + update_flag |= 16 + else + update_flag |= 32 + + if(list2params(oldcolor) != list2params(canister_color)) + update_flag |= 64 + oldcolor = canister_color.Copy() + + if(list2params(olddecals) != list2params(decals)) + update_flag |= 128 + olddecals = decals.Copy() + + if(update_flag == old_flag) + return 1 + else + return 0 + +/obj/machinery/portable_atmospherics/canister/update_icon() +/* +update_flag +1 = holding +2 = connected_port +4 = tank_pressure < 10 +8 = tank_pressure < ONE_ATMOS +16 = tank_pressure < 15*ONE_ATMOS +32 = tank_pressure go boom. +64 = colors +128 = decals +(note: colors and decals has to be applied every icon update) +*/ + + if(src.destroyed) + src.overlays = 0 + src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. + return + + if(icon_state != src.canister_color["prim"]) + icon_state = src.canister_color["prim"] + + if(check_change()) //Returns 1 if no change needed to icons. + return + + overlays.Cut() + + for(var/C in canister_color) + if(C == "prim") continue + if(canister_color[C] == "none") continue + overlays.Add(canister_color[C]) + + for(var/D in decals) + if(decals[D]) + overlays.Add("decal-" + D) + + if(update_flag & 1) + overlays += "can-open" + if(update_flag & 2) + overlays += "can-connector" + if(update_flag & 4) + overlays += "can-o0" + if(update_flag & 8) + overlays += "can-o1" + else if(update_flag & 16) + overlays += "can-o2" + else if(update_flag & 32) + overlays += "can-o3" + + update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. + return + +//template modification exploit prevention, used in Topic() +/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") + if(checkColor == "prim" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possiblemaincolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "sec" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possibleseccolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "ter" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possibletertcolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "quart" || checkColor == "all") + for(var/list/L in GLOB.canister_icon_container.possiblequartcolor) + if(L["icon"] == inputVar) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) + for(var/list/L in GLOB.canister_icon_container.possibledecals) + if(L["icon"] == inputVar) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > temperature_resistance) + take_damage(5, BURN, 0) + +/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + canister_break() + if(disassembled) + new /obj/item/stack/sheet/metal (loc, 10) + else + new /obj/item/stack/sheet/metal (loc, 5) + qdel(src) + +/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag) + if((stat & BROKEN) || (flags & NODECONSTRUCT)) + return + canister_break() + +/obj/machinery/portable_atmospherics/canister/proc/canister_break() + disconnect() + var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) + var/turf/T = get_turf(src) + T.assume_air(expelled_gas) + air_update_turf() + + stat |= BROKEN + density = FALSE + playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) + update_icon() + + if(holding) + holding.forceMove(T) + holding = null + +/obj/machinery/portable_atmospherics/canister/process_atmos() + if(destroyed) + return + + ..() + + if(valve_open) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/env_pressure = environment.return_pressure() + var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 0)) + transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + + if(holding) + environment.merge(removed) + else + loc.assume_air(removed) + air_update_turf() + src.update_icon() + + + if(air_contents.return_pressure() < 1) + can_label = 1 + else + can_label = 0 + + src.updateDialog() + return + +/obj/machinery/portable_atmospherics/canister/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/canister/proc/return_temperature() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.temperature + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/return_pressure() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.return_pressure() + return 0 + +/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve) + . = ..() + if(.) + if(close_valve) + valve_open = FALSE + update_icon() + investigate_log("Valve was closed by [key_name(user)].
    ", "atmos") + else if(valve_open && holding) + investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") + +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) + return src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) + return src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) + if(src.destroyed) + return + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + + +/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) + init_data_vars() //set up var/colorcontainer and var/possibledecals + + // this is the data which will be sent to the ui + var/data[0] + data["name"] = name + data["menu"] = menu ? 1 : 0 + data["canLabel"] = can_label ? 1 : 0 + data["canister_color"] = canister_color + data["colorContainer"] = colorcontainer.Copy() + colorcontainer.Cut() + data["possibleDecals"] = possibledecals.Copy() + possibledecals.Cut() + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) + data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["valveOpen"] = valve_open ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) + + return data + +/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["choice"] == "menu") + menu = text2num(href_list["mode_target"]) + + if(href_list["toggle"]) + var/logmsg + if(valve_open) + if(holding) + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " + else + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
    " + else + if(holding) + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
    " + else + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
    " + if(air_contents.toxins > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") + var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases + if(sleeping_agent && (sleeping_agent.moles > 1)) + message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") + investigate_log(logmsg, "atmos") + release_log += logmsg + valve_open = !valve_open + + if(href_list["remove_tank"]) + if(holding) + if(valve_open) + valve_open = 0 + release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
    " + holding.loc = loc + holding = null + + if(href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + if(diff > 0) + release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) + else + release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) + + if(href_list["rename"]) + if(can_label) + var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) + if(can_label) //Exploit prevention + if(T) + name = T + else + name = "canister" + else + to_chat(usr, "As you attempted to rename it the pressure rose!") + + if(href_list["choice"] == "Primary color") + if(is_a_color(href_list["icon"],"prim")) + canister_color["prim"] = href_list["icon"] + if(href_list["choice"] == "Secondary color") + if(href_list["icon"] == "none") + canister_color["sec"] = "none" + else if(is_a_color(href_list["icon"],"sec")) + canister_color["sec"] = href_list["icon"] + if(href_list["choice"] == "Tertiary color") + if(href_list["icon"] == "none") + canister_color["ter"] = "none" + else if(is_a_color(href_list["icon"],"ter")) + canister_color["ter"] = href_list["icon"] + if(href_list["choice"] == "Quaternary color") + if(href_list["icon"] == "none") + canister_color["quart"] = "none" + else if(is_a_color(href_list["icon"],"quart")) + canister_color["quart"] = href_list["icon"] + + if(href_list["choice"] == "decals") + if(is_a_decal(href_list["icon"])) + decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) + + src.add_fingerprint(usr) + update_icon() + + return 1 + + +/obj/machinery/portable_atmospherics/canister/toxins + name = "Canister \[Toxin (Plasma)\]" + icon_state = "orange" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/sleeping_agent + name = "Canister: \[N2O\]" + icon_state = "redws" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/nitrogen + name = "Canister: \[N2\]" + icon_state = "red" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/air + name = "Canister \[Air\]" + icon_state = "grey" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/custom_mix + name = "Canister \[Custom\]" + icon_state = "whiters" //See New() + can_label = 0 + + +/obj/machinery/portable_atmospherics/canister/toxins/New() + ..() + + canister_color["prim"] = "orange" + decals["plasma"] = 1 + src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/New() + ..() + + canister_color["prim"] = "blue" + src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() + ..() + + canister_color["prim"] = "redws" + var/datum/gas/sleeping_agent/trace_gas = new + air_contents.trace_gases += trace_gas + trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + + +//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 +/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() + ..() + var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] + trace_gas.moles = 9*4000 + spawn(100) + var/turf/simulated/location = src.loc + if(istype(src.loc)) + while(!location.air) + sleep(1000) + location.assume_air(air_contents) + air_contents = new + return 1 + + +/obj/machinery/portable_atmospherics/canister/nitrogen/New() + ..() + + canister_color["prim"] = "red" + src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() + ..() + + canister_color["prim"] = "black" + src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + + +/obj/machinery/portable_atmospherics/canister/air/New() + ..() + + canister_color["prim"] = "grey" + src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/custom_mix/New() + ..() + + canister_color["prim"] = "whiters" + src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD + return 1 + +/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) + if(!(stat & BROKEN)) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + to_chat(user, "You salvage whats left of [src]!") + new /obj/item/stack/sheet/metal(drop_location(), 3) + qdel(src) diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 90601f61bfbd..a20b47b30dbf 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -1,174 +1,174 @@ -/obj/machinery/meter - name = "gas flow meter" - desc = "It measures something." - icon = 'icons/obj/meter.dmi' - icon_state = "meterX" - - layer = GAS_PUMP_LAYER - - var/obj/machinery/atmospherics/pipe/target = null - anchored = TRUE - max_integrity = 150 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 0) - power_channel = ENVIRON - var/frequency = ATMOS_DISTRO_FREQ - var/id - var/id_tag - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 5 - req_one_access_txt = "24;10" - Mtoollink = TRUE - settagwhitelist = list("id_tag") - -/obj/machinery/meter/New() - ..() - SSair.atmos_machinery += src - target = locate(/obj/machinery/atmospherics/pipe) in loc - if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it - id_tag = id - return 1 - -/obj/machinery/meter/Destroy() - SSair.atmos_machinery -= src - target = null - return ..() - -/obj/machinery/meter/Initialize() - ..() - if(!target) - target = locate(/obj/machinery/atmospherics/pipe) in loc - -/obj/machinery/meter/process_atmos() - if(!target) - icon_state = "meterX" - return 0 - - if(stat & (BROKEN|NOPOWER)) - icon_state = "meter0" - return 0 - - var/datum/gas_mixture/environment = target.return_air() - if(!environment) - icon_state = "meterX" - return 0 - - var/env_pressure = environment.return_pressure() - if(env_pressure <= 0.15*ONE_ATMOSPHERE) - icon_state = "meter0" - else if(env_pressure <= 1.8*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) - icon_state = "meter1_[val]" - else if(env_pressure <= 30*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 - icon_state = "meter2_[val]" - else if(env_pressure <= 59*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 - icon_state = "meter3_[val]" - else - icon_state = "meter4" - - if(frequency) - var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) - - if(!radio_connection) return - - var/datum/signal/signal = new - signal.source = src - signal.transmission_method = 1 - signal.data = list( - "tag" = id_tag, - "device" = "AM", - "pressure" = round(env_pressure), - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - -/obj/machinery/meter/proc/status() - var/t = "" - if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" - else - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - return t - -/obj/machinery/meter/examine(mob/user) - var/t = "A gas flow meter. " - - if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead))) - t += "You are too far away to read it." - - else if(stat & (NOPOWER|BROKEN)) - t += "The display is off." - - else if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" - else - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - - . = list(t) - -/obj/machinery/meter/Click() - if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine - usr.examinate(src) - return 1 - - return ..() - -/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(!istype(W, /obj/item/wrench)) - return ..() - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(do_after(user, 40 * W.toolspeed, target = src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct(TRUE) - -/obj/machinery/meter/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/pipe_meter(loc) - qdel(src) - -/obj/machinery/meter/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -// TURF METER - REPORTS A TILE'S AIR CONTENTS - -/obj/machinery/meter/turf/New() - ..() - target = loc - return 1 - - -/obj/machinery/meter/turf/Initialize() - if(!target) - target = loc - ..() - -/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - return - -/obj/machinery/meter/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" - Main - "} +/obj/machinery/meter + name = "gas flow meter" + desc = "It measures something." + icon = 'icons/obj/meter.dmi' + icon_state = "meterX" + + layer = GAS_PUMP_LAYER + + var/obj/machinery/atmospherics/pipe/target = null + anchored = TRUE + max_integrity = 150 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 0) + power_channel = ENVIRON + var/frequency = ATMOS_DISTRO_FREQ + var/id + var/id_tag + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 5 + req_one_access_txt = "24;10" + Mtoollink = TRUE + settagwhitelist = list("id_tag") + +/obj/machinery/meter/New() + ..() + SSair.atmos_machinery += src + target = locate(/obj/machinery/atmospherics/pipe) in loc + if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it + id_tag = id + return 1 + +/obj/machinery/meter/Destroy() + SSair.atmos_machinery -= src + target = null + return ..() + +/obj/machinery/meter/Initialize() + ..() + if(!target) + target = locate(/obj/machinery/atmospherics/pipe) in loc + +/obj/machinery/meter/process_atmos() + if(!target) + icon_state = "meterX" + return 0 + + if(stat & (BROKEN|NOPOWER)) + icon_state = "meter0" + return 0 + + var/datum/gas_mixture/environment = target.return_air() + if(!environment) + icon_state = "meterX" + return 0 + + var/env_pressure = environment.return_pressure() + if(env_pressure <= 0.15*ONE_ATMOSPHERE) + icon_state = "meter0" + else if(env_pressure <= 1.8*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) + icon_state = "meter1_[val]" + else if(env_pressure <= 30*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 + icon_state = "meter2_[val]" + else if(env_pressure <= 59*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 + icon_state = "meter3_[val]" + else + icon_state = "meter4" + + if(frequency) + var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) + + if(!radio_connection) return + + var/datum/signal/signal = new + signal.source = src + signal.transmission_method = 1 + signal.data = list( + "tag" = id_tag, + "device" = "AM", + "pressure" = round(env_pressure), + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + +/obj/machinery/meter/proc/status() + var/t = "" + if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" + else + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + return t + +/obj/machinery/meter/examine(mob/user) + var/t = "A gas flow meter. " + + if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead))) + t += "You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + t += "The display is off." + + else if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" + else + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + + . = list(t) + +/obj/machinery/meter/Click() + if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + usr.examinate(src) + return 1 + + return ..() + +/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(!istype(W, /obj/item/wrench)) + return ..() + playsound(loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(do_after(user, 40 * W.toolspeed, target = src)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct(TRUE) + +/obj/machinery/meter/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/pipe_meter(loc) + qdel(src) + +/obj/machinery/meter/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +// TURF METER - REPORTS A TILE'S AIR CONTENTS + +/obj/machinery/meter/turf/New() + ..() + target = loc + return 1 + + +/obj/machinery/meter/turf/Initialize() + if(!target) + target = loc + ..() + +/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + return + +/obj/machinery/meter/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" + Main + "} diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 5008f4791ba9..cec04bf4f6fc 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,160 +1,160 @@ -/obj/machinery/portable_atmospherics - name = "atmoalter" - use_power = NO_POWER_USE - max_integrity = 250 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 30) - var/datum/gas_mixture/air_contents = new - - var/obj/machinery/atmospherics/unary/portables_connector/connected_port - var/obj/item/tank/holding - - var/volume = 0 - var/destroyed = 0 - - var/maximum_pressure = 90*ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/New() - ..() - SSair.atmos_machinery += src - - air_contents.volume = volume - air_contents.temperature = T20C - - return 1 - -/obj/machinery/portable_atmospherics/Initialize() - . = ..() - spawn() - var/obj/machinery/atmospherics/unary/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - -/obj/machinery/portable_atmospherics/process_atmos() - if(!connected_port) //only react when pipe_network will ont it do it for you - //Allow for reactions - air_contents.react() - else - update_icon() - -/obj/machinery/portable_atmospherics/Destroy() - SSair.atmos_machinery -= src - disconnect() - QDEL_NULL(air_contents) - QDEL_NULL(holding) - return ..() - -/obj/machinery/portable_atmospherics/update_icon() - return null - -/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - // To avoid a chicken-egg thing where pipes need to - // be initialized before the atmos cans are - if(!connected_port.parent) - connected_port.build_network() - connected_port.parent.reconcile_air() - - anchored = 1 //Prevent movement - - return 1 - -/obj/machinery/portable_atmospherics/proc/disconnect() - if(!connected_port) - return 0 - - anchored = 0 - - connected_port.connected_device = null - connected_port = null - - return 1 - -/obj/machinery/portable_atmospherics/portableConnectorReturnAir() - return air_contents - -/obj/machinery/portable_atmospherics/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - if(holding) - to_chat(user, "You remove [holding] from [src].") - replace_tank(user, TRUE) - -/obj/machinery/portable_atmospherics/examine(mob/user) - . = ..() - if(holding) - . += "\The [src] contains [holding]. Alt-click [src] to remove it." - -/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) - if(holding) - holding.forceMove(drop_location()) - if(Adjacent(user) && !issilicon(user)) - user.put_in_hands(holding) - if(new_tank) - holding = new_tank - else - holding = null - update_icon() - return TRUE - -/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/tank)) - if(!(stat & BROKEN)) - if(!user.drop_item()) - return - var/obj/item/tank/T = W - user.drop_item() - if(src.holding) - to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].") - replace_tank(user, FALSE) - T.loc = src - src.holding = T - update_icon() - return - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - return - return ..() - -/obj/machinery/portable_atmospherics/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(connected_port) - disconnect() - to_chat(user, "You disconnect [name] from the port.") - update_icon() - else - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - to_chat(user, "You connect [src] to the port.") - update_icon() - return - else - to_chat(user, "[src] failed to connect to the port.") - return - else - to_chat(user, "Nothing happens.") - -/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) - if(I.force < 10 && !(stat & BROKEN)) - take_damage(0) - else - add_fingerprint(user) - ..() \ No newline at end of file +/obj/machinery/portable_atmospherics + name = "atmoalter" + use_power = NO_POWER_USE + max_integrity = 250 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 30) + var/datum/gas_mixture/air_contents = new + + var/obj/machinery/atmospherics/unary/portables_connector/connected_port + var/obj/item/tank/holding + + var/volume = 0 + var/destroyed = 0 + + var/maximum_pressure = 90*ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/New() + ..() + SSair.atmos_machinery += src + + air_contents.volume = volume + air_contents.temperature = T20C + + return 1 + +/obj/machinery/portable_atmospherics/Initialize() + . = ..() + spawn() + var/obj/machinery/atmospherics/unary/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + +/obj/machinery/portable_atmospherics/process_atmos() + if(!connected_port) //only react when pipe_network will ont it do it for you + //Allow for reactions + air_contents.react() + else + update_icon() + +/obj/machinery/portable_atmospherics/Destroy() + SSair.atmos_machinery -= src + disconnect() + QDEL_NULL(air_contents) + QDEL_NULL(holding) + return ..() + +/obj/machinery/portable_atmospherics/update_icon() + return null + +/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + // To avoid a chicken-egg thing where pipes need to + // be initialized before the atmos cans are + if(!connected_port.parent) + connected_port.build_network() + connected_port.parent.reconcile_air() + + anchored = 1 //Prevent movement + + return 1 + +/obj/machinery/portable_atmospherics/proc/disconnect() + if(!connected_port) + return 0 + + anchored = 0 + + connected_port.connected_device = null + connected_port = null + + return 1 + +/obj/machinery/portable_atmospherics/portableConnectorReturnAir() + return air_contents + +/obj/machinery/portable_atmospherics/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + if(holding) + to_chat(user, "You remove [holding] from [src].") + replace_tank(user, TRUE) + +/obj/machinery/portable_atmospherics/examine(mob/user) + . = ..() + if(holding) + . += "\The [src] contains [holding]. Alt-click [src] to remove it." + +/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) + if(holding) + holding.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(holding) + if(new_tank) + holding = new_tank + else + holding = null + update_icon() + return TRUE + +/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/tank)) + if(!(stat & BROKEN)) + if(!user.drop_item()) + return + var/obj/item/tank/T = W + user.drop_item() + if(src.holding) + to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].") + replace_tank(user, FALSE) + T.loc = src + src.holding = T + update_icon() + return + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + return + return ..() + +/obj/machinery/portable_atmospherics/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(connected_port) + disconnect() + to_chat(user, "You disconnect [name] from the port.") + update_icon() + else + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc + if(possible_port) + if(connect(possible_port)) + to_chat(user, "You connect [src] to the port.") + update_icon() + return + else + to_chat(user, "[src] failed to connect to the port.") + return + else + to_chat(user, "Nothing happens.") + +/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) + if(I.force < 10 && !(stat & BROKEN)) + take_damage(0) + else + add_fingerprint(user) + ..() diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 5bd441f67488..0144a7f93c22 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,173 +1,173 @@ -/obj/machinery/portable_atmospherics/pump - name = "Portable Air Pump" - - icon = 'icons/obj/atmos.dmi' - icon_state = "psiphon:0" - density = 1 - - var/on = 0 - var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = 100 - - var/pressuremin = 0 - var/pressuremax = 10 * ONE_ATMOSPHERE - - volume = 1000 - -/obj/machinery/portable_atmospherics/pump/update_icon() - src.overlays = 0 - - if(on) - icon_state = "psiphon:1" - else - icon_state = "psiphon:0" - - if(holding) - overlays += "siphon-open" - - if(connected_port) - overlays += "siphon-connector" - - return - -/obj/machinery/portable_atmospherics/pump/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - - if(prob(100/severity)) - direction_out = !direction_out - - target_pressure = rand(0,1300) - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/pump/process_atmos() - ..() - if(on) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - if(direction_out) - var/pressure_delta = target_pressure - environment.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(air_contents.temperature > 0) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - air_update_turf() - else - var/pressure_delta = target_pressure - air_contents.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(environment.temperature > 0) - transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - air_update_turf() - - air_contents.merge(removed) - //src.update_icon() - - src.updateDialog() - return - -/obj/machinery/portable_atmospherics/pump/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve) - . = ..() - if(.) - if(close_valve) - if(on) - on = FALSE - update_icon() - else if(on && holding && direction_out) - investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") - -/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) - var/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["targetpressure"] = round(target_pressure) - data["pump_dir"] = direction_out - data["minpressure"] = round(pressuremin) - data["maxpressure"] = round(pressuremax) - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - return data - -/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - if(on && direction_out) - investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") - update_icon() - - if(href_list["direction"]) - direction_out = !direction_out - if(on && holding) - investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") - - if(href_list["remove_tank"]) - if(holding) - on = FALSE - holding.loc = loc - holding = null - update_icon() - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax) - update_icon() - - src.add_fingerprint(usr) +/obj/machinery/portable_atmospherics/pump + name = "Portable Air Pump" + + icon = 'icons/obj/atmos.dmi' + icon_state = "psiphon:0" + density = 1 + + var/on = 0 + var/direction_out = 0 //0 = siphoning, 1 = releasing + var/target_pressure = 100 + + var/pressuremin = 0 + var/pressuremax = 10 * ONE_ATMOSPHERE + + volume = 1000 + +/obj/machinery/portable_atmospherics/pump/update_icon() + src.overlays = 0 + + if(on) + icon_state = "psiphon:1" + else + icon_state = "psiphon:0" + + if(holding) + overlays += "siphon-open" + + if(connected_port) + overlays += "siphon-connector" + + return + +/obj/machinery/portable_atmospherics/pump/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + + if(prob(100/severity)) + direction_out = !direction_out + + target_pressure = rand(0,1300) + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/pump/process_atmos() + ..() + if(on) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + if(direction_out) + var/pressure_delta = target_pressure - environment.return_pressure() + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if(air_contents.temperature > 0) + transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + + if(holding) + environment.merge(removed) + else + loc.assume_air(removed) + air_update_turf() + else + var/pressure_delta = target_pressure - air_contents.return_pressure() + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if(environment.temperature > 0) + transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed + if(holding) + removed = environment.remove(transfer_moles) + else + removed = loc.remove_air(transfer_moles) + air_update_turf() + + air_contents.merge(removed) + //src.update_icon() + + src.updateDialog() + return + +/obj/machinery/portable_atmospherics/pump/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve) + . = ..() + if(.) + if(close_valve) + if(on) + on = FALSE + update_icon() + else if(on && holding && direction_out) + investigate_log("[key_name(user)] started a transfer into [holding].
    ", "atmos") + +/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) + var/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["targetpressure"] = round(target_pressure) + data["pump_dir"] = direction_out + data["minpressure"] = round(pressuremin) + data["maxpressure"] = round(pressuremax) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + return data + +/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + if(on && direction_out) + investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + update_icon() + + if(href_list["direction"]) + direction_out = !direction_out + if(on && holding) + investigate_log("[key_name(usr)] started a transfer into [holding].
    ", "atmos") + + if(href_list["remove_tank"]) + if(holding) + on = FALSE + holding.loc = loc + holding = null + update_icon() + + if(href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax) + update_icon() + + src.add_fingerprint(usr) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 2079b8cf915d..36e340d1a12a 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,216 +1,216 @@ -/obj/machinery/portable_atmospherics/scrubber - name = "Portable Air Scrubber" - - icon = 'icons/obj/atmos.dmi' - icon_state = "pscrubber:0" - density = 1 - - var/on = 0 - var/volume_rate = 800 - var/widenet = 0 //is this scrubber acting on the 3x3 area around it. - - volume = 750 - - var/minrate = 0//probably useless, but whatever - var/maxrate = 10 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/scrubber/update_icon() - src.overlays = 0 - - if(on) - icon_state = "pscrubber:1" - else - icon_state = "pscrubber:0" - - if(holding) - overlays += "scrubber-open" - - if(connected_port) - overlays += "scrubber-connector" - - return - -/obj/machinery/portable_atmospherics/scrubber/process_atmos() - ..() - - if(!on) - return - scrub(loc) - if(widenet) - var/turf/T = loc - if(istype(T)) - for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) - scrub(tile) - -/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = tile.return_air() - var/transfer_moles = min(1,volume_rate/environment.volume)*environment.total_moles() - - //Take a gas sample - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - - //Filter it - if(removed) - var/datum/gas_mixture/filtered_out = new - - filtered_out.temperature = removed.temperature - - - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/sleeping_agent)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/oxygen_agent_b)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - //Remix the resulting gases - air_contents.merge(filtered_out) - - if(holding) - environment.merge(removed) - else - tile.assume_air(removed) - tile.air_update_turf() - -/obj/machinery/portable_atmospherics/scrubber/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) - ui_interact(user) - return - -/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) - var/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["rate"] = round(volume_rate) - data["minrate"] = round(minrate) - data["maxrate"] = round(maxrate) - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - return data - -/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - update_icon() - - if(href_list["remove_tank"]) - if(holding) - holding.loc = loc - holding = null - - if(href_list["volume_adj"]) - var/diff = text2num(href_list["volume_adj"]) - volume_rate = Clamp(volume_rate+diff, minrate, maxrate) - - src.add_fingerprint(usr) - -/obj/machinery/portable_atmospherics/scrubber/huge - name = "Huge Air Scrubber" - icon_state = "scrubber:0" - anchored = 1 - volume = 50000 - volume_rate = 5000 - widenet = 1 - - var/global/gid = 1 - var/id = 0 - var/stationary = 0 - -/obj/machinery/portable_atmospherics/scrubber/huge/New() - ..() - id = gid - gid++ - - name = "[name] (ID [id])" - -/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) - to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") - -/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() - src.overlays = 0 - - if(on) - icon_state = "scrubber:1" - else - icon_state = "scrubber:0" - -/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/wrench)) - if(stationary) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - if(on) - to_chat(user, "Turn it off first!") - return - - anchored = !anchored - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - return - - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - return - return ..() - -/obj/machinery/portable_atmospherics/scrubber/huge/stationary - name = "Stationary Air Scrubber" - stationary = 1 +/obj/machinery/portable_atmospherics/scrubber + name = "Portable Air Scrubber" + + icon = 'icons/obj/atmos.dmi' + icon_state = "pscrubber:0" + density = 1 + + var/on = 0 + var/volume_rate = 800 + var/widenet = 0 //is this scrubber acting on the 3x3 area around it. + + volume = 750 + + var/minrate = 0//probably useless, but whatever + var/maxrate = 10 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/scrubber/update_icon() + src.overlays = 0 + + if(on) + icon_state = "pscrubber:1" + else + icon_state = "pscrubber:0" + + if(holding) + overlays += "scrubber-open" + + if(connected_port) + overlays += "scrubber-connector" + + return + +/obj/machinery/portable_atmospherics/scrubber/process_atmos() + ..() + + if(!on) + return + scrub(loc) + if(widenet) + var/turf/T = loc + if(istype(T)) + for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) + scrub(tile) + +/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = tile.return_air() + var/transfer_moles = min(1,volume_rate/environment.volume)*environment.total_moles() + + //Take a gas sample + var/datum/gas_mixture/removed + if(holding) + removed = environment.remove(transfer_moles) + else + removed = loc.remove_air(transfer_moles) + + //Filter it + if(removed) + var/datum/gas_mixture/filtered_out = new + + filtered_out.temperature = removed.temperature + + + filtered_out.toxins = removed.toxins + removed.toxins = 0 + + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/sleeping_agent)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/oxygen_agent_b)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + //Remix the resulting gases + air_contents.merge(filtered_out) + + if(holding) + environment.merge(removed) + else + tile.assume_air(removed) + tile.air_update_turf() + +/obj/machinery/portable_atmospherics/scrubber/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) + ui_interact(user) + return + +/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.physical_state) + var/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["rate"] = round(volume_rate) + data["minrate"] = round(minrate) + data["maxrate"] = round(maxrate) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + return data + +/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + update_icon() + + if(href_list["remove_tank"]) + if(holding) + holding.loc = loc + holding = null + + if(href_list["volume_adj"]) + var/diff = text2num(href_list["volume_adj"]) + volume_rate = Clamp(volume_rate+diff, minrate, maxrate) + + src.add_fingerprint(usr) + +/obj/machinery/portable_atmospherics/scrubber/huge + name = "Huge Air Scrubber" + icon_state = "scrubber:0" + anchored = 1 + volume = 50000 + volume_rate = 5000 + widenet = 1 + + var/global/gid = 1 + var/id = 0 + var/stationary = 0 + +/obj/machinery/portable_atmospherics/scrubber/huge/New() + ..() + id = gid + gid++ + + name = "[name] (ID [id])" + +/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) + to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") + +/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() + src.overlays = 0 + + if(on) + icon_state = "scrubber:1" + else + icon_state = "scrubber:0" + +/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + if(istype(W, /obj/item/wrench)) + if(stationary) + to_chat(user, "The bolts are too tight for you to unscrew!") + return + if(on) + to_chat(user, "Turn it off first!") + return + + anchored = !anchored + playsound(loc, W.usesound, 50, 1) + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") + return + + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + return + return ..() + +/obj/machinery/portable_atmospherics/scrubber/huge/stationary + name = "Stationary Air Scrubber" + stationary = 1 diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm index 18af424c4d68..773432133e87 100644 --- a/code/game/machinery/atmoalter/zvent.dm +++ b/code/game/machinery/atmoalter/zvent.dm @@ -1,35 +1,35 @@ -/obj/machinery/zvent - name = "Interfloor Air Transfer System" - - icon = 'icons/obj/pipes.dmi' - icon_state = "vent-db" - density = 0 - anchored=1 - - var/on = 0 - var/volume_rate = 800 - -/obj/machinery/zvent/New() - ..() - SSair.atmos_machinery += src - -/obj/machinery/zvent/Destroy() - SSair.atmos_machinery -= src - return ..() - -/obj/machinery/zvent/process_atmos() - - //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. - if(istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it - for(var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented - var/turf/simulated/zturf_conn = locate(x,y,z+new_z) - if(istype(zturf_conn)) - var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn - if(istype(zvent_conn)) - //both floors have simulated turfs, share() - var/turf/simulated/myturf = loc - var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference - var/datum/gas_mixture/my_air = myturf.air - if(istype(conn_air) && istype(my_air)) - my_air.share(conn_air) - air_update_turf() +/obj/machinery/zvent + name = "Interfloor Air Transfer System" + + icon = 'icons/obj/pipes.dmi' + icon_state = "vent-db" + density = 0 + anchored=1 + + var/on = 0 + var/volume_rate = 800 + +/obj/machinery/zvent/New() + ..() + SSair.atmos_machinery += src + +/obj/machinery/zvent/Destroy() + SSair.atmos_machinery -= src + return ..() + +/obj/machinery/zvent/process_atmos() + + //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. + if(istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it + for(var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented + var/turf/simulated/zturf_conn = locate(x,y,z+new_z) + if(istype(zturf_conn)) + var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn + if(istype(zvent_conn)) + //both floors have simulated turfs, share() + var/turf/simulated/myturf = loc + var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference + var/datum/gas_mixture/my_air = myturf.air + if(istype(conn_air) && istype(my_air)) + my_air.share(conn_air) + air_update_turf() diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 52ee48f067b0..78367327a0c4 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,469 +1,469 @@ -#define AUTOLATHE_MAIN_MENU 1 -#define AUTOLATHE_CATEGORY_MENU 2 -#define AUTOLATHE_SEARCH_MENU 3 - -/obj/machinery/autolathe - name = "autolathe" - desc = "It produces items using metal and glass." - icon_state = "autolathe" - density = 1 - - var/operating = 0.0 - var/list/queue = list() - var/queue_max_len = 12 - var/turf/BuildTurf - anchored = 1.0 - var/list/L = list() - var/list/LL = list() - var/hacked = 0 - var/disabled = 0 - var/shocked = 0 - var/hack_wire - var/disable_wire - var/shock_wire - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - var/busy = 0 - var/prod_coeff - var/datum/wires/autolathe/wires = null - - var/list/being_built = list() - var/datum/research/files - var/list/datum/design/matching_designs - var/temp_search - var/selected_category - var/screen = 1 - - var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") - -/obj/machinery/autolathe/New() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/autolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - - wires = new(src) - files = new /datum/research/autolathe(src) - matching_designs = list() - -/obj/machinery/autolathe/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/autolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - -/obj/machinery/autolathe/Destroy() - QDEL_NULL(wires) - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_all() - return ..() - -/obj/machinery/autolathe/interact(mob/user) - if(shocked && !(stat & NOPOWER)) - if(shock(user, 50)) - return - - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - -/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) - ui.open() - -/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - GET_COMPONENT(materials, /datum/component/material_container) - var/data[0] - data["screen"] = screen - data["total_amount"] = materials.total_amount - data["max_amount"] = materials.max_amount - data["metal_amount"] = materials.amount(MAT_METAL) - data["glass_amount"] = materials.amount(MAT_GLASS) - switch(screen) - if(AUTOLATHE_MAIN_MENU) - data["uid"] = UID() - data["categories"] = categories - if(AUTOLATHE_CATEGORY_MENU) - data["selected_category"] = selected_category - var/list/designs = list() - data["designs"] = designs - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(selected_category in D.category)) - continue - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - if(AUTOLATHE_SEARCH_MENU) - data["search"] = temp_search - var/list/designs = list() - data["designs"] = designs - for(var/datum/design/D in matching_designs) - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - - data = queue_data(data) - return data - -/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) - var/list/data = list() - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/has_metal = 1 - if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) - has_metal = 0 - var/has_glass = 1 - if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) - has_glass = 0 - - data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) - data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) - - return data - -/obj/machinery/autolathe/proc/queue_data(list/data) - GET_COMPONENT(materials, /datum/component/material_container) - var/temp_metal = materials.amount(MAT_METAL) - var/temp_glass = materials.amount(MAT_GLASS) - data["processing"] = being_built.len ? get_processing_line() : null - if(istype(queue) && queue.len) - var/list/data_queue = list() - for(var/list/L in queue) - var/datum/design/D = L[1] - var/list/LL = get_design_cost_as_list(D, L[2]) - data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) - temp_metal = max(temp_metal - LL[1], 1) - temp_glass = max(temp_glass - LL[2], 1) - data["queue"] = data_queue - data["queue_len"] = data_queue.len - else - data["queue"] = null - return data - -/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return 1 - if(exchange_parts(user, O)) - return - if(stat) - return 1 - - // Disks in general - if(istype(O, /obj/item/disk)) - if(istype(O, /obj/item/disk/design_disk)) - var/obj/item/disk/design_disk/D = O - if(D.blueprint) - user.visible_message("[user] begins to load \the [O] in \the [src]...", - "You begin to load a design from \the [O]...", - "You hear the chatter of a floppy drive.") - playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - busy = 1 - if(do_after(user, 14.4, target = src)) - files.AddDesign2Known(D.blueprint) - busy = 0 - else - to_chat(user, "That disk does not have a design on it!") - return 1 - else - // So that people who are bad at computers don't shred their disks - to_chat(user, "This is not the correct type of disk for the autolathe!") - return 1 - - return ..() - -/obj/machinery/autolathe/crowbar_act(mob/user, obj/item/I) - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - if(panel_open) - default_deconstruction_crowbar(user, I) - I.play_tool_sound(user, I.tool_volume) - -/obj/machinery/autolathe/screwdriver_act(mob/user, obj/item/I) - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) - SSnanoui.update_uis(src) - I.play_tool_sound(user, I.tool_volume) - -/obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) - if(!panel_open) - return - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - interact(user) - -/obj/machinery/autolathe/multitool_act(mob/user, obj/item/I) - if(!panel_open) - return - if(!I.use_tool(src, user, 0, volume = 0)) - return - . = TRUE - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return - interact(user) - -/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - switch(id_inserted) - if(MAT_METAL) - flick("autolathe_o", src)//plays metal insertion animation - if(MAT_GLASS) - flick("autolathe_r", src)//plays glass insertion animation - use_power(min(1000, amount_inserted / 100)) - SSnanoui.update_uis(src) - -/obj/machinery/autolathe/attack_ghost(mob/user) - interact(user) - -/obj/machinery/autolathe/attack_hand(mob/user) - if(..(user, 0)) - return - interact(user) - -/obj/machinery/autolathe/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["menu"]) - screen = text2num(href_list["menu"]) - - if(href_list["category"]) - selected_category = href_list["category"] - screen = AUTOLATHE_CATEGORY_MENU - - if(href_list["make"]) - BuildTurf = loc - - ///////////////// - //href protection - var/datum/design/design_last_ordered - design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design - if(!design_last_ordered) - return - if(!(design_last_ordered.build_type & AUTOLATHE)) - return - - //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier - var/multiplier = text2num(href_list["multiplier"]) - GET_COMPONENT(materials, /datum/component/material_container) - var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) - var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) - - if(!is_stack && (multiplier > 1)) - return - if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc - return - ///////////////// - - if((queue.len + 1) < queue_max_len) - add_to_queue(design_last_ordered,multiplier) - else - to_chat(usr, "The autolathe queue is full!") - if(!busy) - busy = 1 - process_queue() - busy = 0 - - if(href_list["remove_from_queue"]) - var/index = text2num(href_list["remove_from_queue"]) - if(isnum(index) && IsInRange(index, 1, queue.len)) - remove_from_queue(index) - if(href_list["queue_move"] && href_list["index"]) - var/index = text2num(href_list["index"]) - var/new_index = index + text2num(href_list["queue_move"]) - if(isnum(index) && isnum(new_index)) - if(IsInRange(new_index, 1, queue.len)) - queue.Swap(index,new_index) - if(href_list["clear_queue"]) - queue = list() - if(href_list["search"]) - if(href_list["to_search"]) - temp_search = href_list["to_search"] - if(!temp_search) - return - matching_designs.Cut() - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(findtext(D.name, temp_search)) - matching_designs.Add(D) - - screen = AUTOLATHE_SEARCH_MENU - - SSnanoui.update_uis(src) - return 1 - -/obj/machinery/autolathe/RefreshParts() - var/tot_rating = 0 - prod_coeff = 0 - for(var/obj/item/stock_parts/matter_bin/MB in component_parts) - tot_rating += MB.rating - tot_rating *= 25000 - GET_COMPONENT(materials, /datum/component/material_container) - materials.max_amount = tot_rating * 3 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - prod_coeff += M.rating - 1 - -/obj/machinery/autolathe/proc/get_coeff(datum/design/D) - var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient - return coeff - -/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) - desc = initial(desc)+"\nIt's building \a [initial(D.name)]." - var/is_stack = ispath(D.build_path, /obj/item/stack) - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/metal_cost = D.materials[MAT_METAL] - var/glass_cost = D.materials[MAT_GLASS] - var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) - if(can_build(D, multiplier)) - being_built = list(D, multiplier) - use_power(power) - icon_state = "autolathe" - flick("autolathe_n",src) - if(is_stack) - var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) - materials.use_amount(materials_used) - else - var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) - materials.use_amount(materials_used) - SSnanoui.update_uis(src) - sleep(32/coeff) - if(is_stack) - var/obj/item/stack/S = new D.build_path(BuildTurf) - S.amount = multiplier - else - var/obj/item/new_item = new D.build_path(BuildTurf) - new_item.materials[MAT_METAL] /= coeff - new_item.materials[MAT_GLASS] /= coeff - SSnanoui.update_uis(src) - desc = initial(desc) - -/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) - if(D.make_reagents.len) - return 0 - - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/metal_amount = materials.amount(MAT_METAL) - if(custom_metal) - metal_amount = custom_metal - var/glass_amount = materials.amount(MAT_GLASS) - if(custom_glass) - glass_amount = custom_glass - - if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) - return 0 - if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) - return 0 - return 1 - -/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) - var/list/OutputList = list(0,0) - var/coeff = get_coeff(D) - if(D.materials[MAT_METAL]) - OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier - if(D.materials[MAT_GLASS]) - OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier - return OutputList - -/obj/machinery/autolathe/proc/get_processing_line() - var/datum/design/D = being_built[1] - var/multiplier = being_built[2] - var/is_stack = (multiplier>1) - var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" - return output - -/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) - if(!istype(queue)) - queue = list() - if(D) - queue.Add(list(list(D,multiplier))) - return queue.len - -/obj/machinery/autolathe/proc/remove_from_queue(index) - if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) - return 0 - queue.Cut(index,++index) - return 1 - -/obj/machinery/autolathe/proc/process_queue() - var/datum/design/D = queue[1][1] - var/multiplier = queue[1][2] - if(!D) - remove_from_queue(1) - if(queue.len) - return process_queue() - else - return - while(D) - if(stat & (NOPOWER|BROKEN)) - being_built = new /list() - return 0 - if(!can_build(D, multiplier)) - visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") - queue = list() - being_built = new /list() - return 0 - - remove_from_queue(1) - build_item(D,multiplier) - D = listgetindex(listgetindex(queue, 1),1) - multiplier = listgetindex(listgetindex(queue,1),2) - being_built = new /list() - //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") - -/obj/machinery/autolathe/proc/adjust_hacked(hack) - hacked = hack - - if(hack) - for(var/datum/design/D in files.possible_designs) - if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) - files.AddDesign2Known(D) - else - for(var/datum/design/D in files.known_designs) - if("hacked" in D.category) - files.known_designs -= D.id +#define AUTOLATHE_MAIN_MENU 1 +#define AUTOLATHE_CATEGORY_MENU 2 +#define AUTOLATHE_SEARCH_MENU 3 + +/obj/machinery/autolathe + name = "autolathe" + desc = "It produces items using metal and glass." + icon_state = "autolathe" + density = 1 + + var/operating = 0.0 + var/list/queue = list() + var/queue_max_len = 12 + var/turf/BuildTurf + anchored = 1.0 + var/list/L = list() + var/list/LL = list() + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/hack_wire + var/disable_wire + var/shock_wire + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + var/busy = 0 + var/prod_coeff + var/datum/wires/autolathe/wires = null + + var/list/being_built = list() + var/datum/research/files + var/list/datum/design/matching_designs + var/temp_search + var/selected_category + var/screen = 1 + + var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") + +/obj/machinery/autolathe/New() + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/autolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + + wires = new(src) + files = new /datum/research/autolathe(src) + matching_designs = list() + +/obj/machinery/autolathe/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/autolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/autolathe/Destroy() + QDEL_NULL(wires) + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_all() + return ..() + +/obj/machinery/autolathe/interact(mob/user) + if(shocked && !(stat & NOPOWER)) + if(shock(user, 50)) + return + + if(panel_open) + wires.Interact(user) + else + ui_interact(user) + +/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) + ui.open() + +/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + GET_COMPONENT(materials, /datum/component/material_container) + var/data[0] + data["screen"] = screen + data["total_amount"] = materials.total_amount + data["max_amount"] = materials.max_amount + data["metal_amount"] = materials.amount(MAT_METAL) + data["glass_amount"] = materials.amount(MAT_GLASS) + switch(screen) + if(AUTOLATHE_MAIN_MENU) + data["uid"] = UID() + data["categories"] = categories + if(AUTOLATHE_CATEGORY_MENU) + data["selected_category"] = selected_category + var/list/designs = list() + data["designs"] = designs + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(selected_category in D.category)) + continue + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + if(AUTOLATHE_SEARCH_MENU) + data["search"] = temp_search + var/list/designs = list() + data["designs"] = designs + for(var/datum/design/D in matching_designs) + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + + data = queue_data(data) + return data + +/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) + var/list/data = list() + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/has_metal = 1 + if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) + has_metal = 0 + var/has_glass = 1 + if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) + has_glass = 0 + + data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) + data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) + + return data + +/obj/machinery/autolathe/proc/queue_data(list/data) + GET_COMPONENT(materials, /datum/component/material_container) + var/temp_metal = materials.amount(MAT_METAL) + var/temp_glass = materials.amount(MAT_GLASS) + data["processing"] = being_built.len ? get_processing_line() : null + if(istype(queue) && queue.len) + var/list/data_queue = list() + for(var/list/L in queue) + var/datum/design/D = L[1] + var/list/LL = get_design_cost_as_list(D, L[2]) + data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) + temp_metal = max(temp_metal - LL[1], 1) + temp_glass = max(temp_glass - LL[2], 1) + data["queue"] = data_queue + data["queue_len"] = data_queue.len + else + data["queue"] = null + return data + +/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return 1 + if(exchange_parts(user, O)) + return + if(stat) + return 1 + + // Disks in general + if(istype(O, /obj/item/disk)) + if(istype(O, /obj/item/disk/design_disk)) + var/obj/item/disk/design_disk/D = O + if(D.blueprint) + user.visible_message("[user] begins to load \the [O] in \the [src]...", + "You begin to load a design from \the [O]...", + "You hear the chatter of a floppy drive.") + playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + busy = 1 + if(do_after(user, 14.4, target = src)) + files.AddDesign2Known(D.blueprint) + busy = 0 + else + to_chat(user, "That disk does not have a design on it!") + return 1 + else + // So that people who are bad at computers don't shred their disks + to_chat(user, "This is not the correct type of disk for the autolathe!") + return 1 + + return ..() + +/obj/machinery/autolathe/crowbar_act(mob/user, obj/item/I) + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + if(panel_open) + default_deconstruction_crowbar(user, I) + I.play_tool_sound(user, I.tool_volume) + +/obj/machinery/autolathe/screwdriver_act(mob/user, obj/item/I) + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) + SSnanoui.update_uis(src) + I.play_tool_sound(user, I.tool_volume) + +/obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + interact(user) + +/obj/machinery/autolathe/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + interact(user) + +/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + switch(id_inserted) + if(MAT_METAL) + flick("autolathe_o", src)//plays metal insertion animation + if(MAT_GLASS) + flick("autolathe_r", src)//plays glass insertion animation + use_power(min(1000, amount_inserted / 100)) + SSnanoui.update_uis(src) + +/obj/machinery/autolathe/attack_ghost(mob/user) + interact(user) + +/obj/machinery/autolathe/attack_hand(mob/user) + if(..(user, 0)) + return + interact(user) + +/obj/machinery/autolathe/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["menu"]) + screen = text2num(href_list["menu"]) + + if(href_list["category"]) + selected_category = href_list["category"] + screen = AUTOLATHE_CATEGORY_MENU + + if(href_list["make"]) + BuildTurf = loc + + ///////////////// + //href protection + var/datum/design/design_last_ordered + design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design + if(!design_last_ordered) + return + if(!(design_last_ordered.build_type & AUTOLATHE)) + return + + //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier + var/multiplier = text2num(href_list["multiplier"]) + GET_COMPONENT(materials, /datum/component/material_container) + var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) + var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) + + if(!is_stack && (multiplier > 1)) + return + if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc + return + ///////////////// + + if((queue.len + 1) < queue_max_len) + add_to_queue(design_last_ordered,multiplier) + else + to_chat(usr, "The autolathe queue is full!") + if(!busy) + busy = 1 + process_queue() + busy = 0 + + if(href_list["remove_from_queue"]) + var/index = text2num(href_list["remove_from_queue"]) + if(isnum(index) && IsInRange(index, 1, queue.len)) + remove_from_queue(index) + if(href_list["queue_move"] && href_list["index"]) + var/index = text2num(href_list["index"]) + var/new_index = index + text2num(href_list["queue_move"]) + if(isnum(index) && isnum(new_index)) + if(IsInRange(new_index, 1, queue.len)) + queue.Swap(index,new_index) + if(href_list["clear_queue"]) + queue = list() + if(href_list["search"]) + if(href_list["to_search"]) + temp_search = href_list["to_search"] + if(!temp_search) + return + matching_designs.Cut() + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(findtext(D.name, temp_search)) + matching_designs.Add(D) + + screen = AUTOLATHE_SEARCH_MENU + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/autolathe/RefreshParts() + var/tot_rating = 0 + prod_coeff = 0 + for(var/obj/item/stock_parts/matter_bin/MB in component_parts) + tot_rating += MB.rating + tot_rating *= 25000 + GET_COMPONENT(materials, /datum/component/material_container) + materials.max_amount = tot_rating * 3 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + prod_coeff += M.rating - 1 + +/obj/machinery/autolathe/proc/get_coeff(datum/design/D) + var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient + return coeff + +/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) + desc = initial(desc)+"\nIt's building \a [initial(D.name)]." + var/is_stack = ispath(D.build_path, /obj/item/stack) + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/metal_cost = D.materials[MAT_METAL] + var/glass_cost = D.materials[MAT_GLASS] + var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) + if(can_build(D, multiplier)) + being_built = list(D, multiplier) + use_power(power) + icon_state = "autolathe" + flick("autolathe_n",src) + if(is_stack) + var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) + materials.use_amount(materials_used) + else + var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) + materials.use_amount(materials_used) + SSnanoui.update_uis(src) + sleep(32/coeff) + if(is_stack) + var/obj/item/stack/S = new D.build_path(BuildTurf) + S.amount = multiplier + else + var/obj/item/new_item = new D.build_path(BuildTurf) + new_item.materials[MAT_METAL] /= coeff + new_item.materials[MAT_GLASS] /= coeff + SSnanoui.update_uis(src) + desc = initial(desc) + +/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) + if(D.make_reagents.len) + return 0 + + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/metal_amount = materials.amount(MAT_METAL) + if(custom_metal) + metal_amount = custom_metal + var/glass_amount = materials.amount(MAT_GLASS) + if(custom_glass) + glass_amount = custom_glass + + if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) + return 0 + if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) + return 0 + return 1 + +/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) + var/list/OutputList = list(0,0) + var/coeff = get_coeff(D) + if(D.materials[MAT_METAL]) + OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier + if(D.materials[MAT_GLASS]) + OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier + return OutputList + +/obj/machinery/autolathe/proc/get_processing_line() + var/datum/design/D = being_built[1] + var/multiplier = being_built[2] + var/is_stack = (multiplier>1) + var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" + return output + +/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) + if(!istype(queue)) + queue = list() + if(D) + queue.Add(list(list(D,multiplier))) + return queue.len + +/obj/machinery/autolathe/proc/remove_from_queue(index) + if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) + return 0 + queue.Cut(index,++index) + return 1 + +/obj/machinery/autolathe/proc/process_queue() + var/datum/design/D = queue[1][1] + var/multiplier = queue[1][2] + if(!D) + remove_from_queue(1) + if(queue.len) + return process_queue() + else + return + while(D) + if(stat & (NOPOWER|BROKEN)) + being_built = new /list() + return 0 + if(!can_build(D, multiplier)) + visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") + queue = list() + being_built = new /list() + return 0 + + remove_from_queue(1) + build_item(D,multiplier) + D = listgetindex(listgetindex(queue, 1),1) + multiplier = listgetindex(listgetindex(queue,1),2) + being_built = new /list() + //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") + +/obj/machinery/autolathe/proc/adjust_hacked(hack) + hacked = hack + + if(hack) + for(var/datum/design/D in files.possible_designs) + if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) + files.AddDesign2Known(D) + else + for(var/datum/design/D in files.known_designs) + if("hacked" in D.category) + files.known_designs -= D.id diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index f91152b47e4f..796d5c552dd8 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -1,206 +1,206 @@ - -////////////////////////////////////// -// Driver Button // -////////////////////////////////////// - -/obj/machinery/driver_button - name = "mass driver button" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for a mass driver." - var/id_tag = "default" - var/active = 0 - settagwhitelist = list("id_tag", "logic_id_tag") - anchored = 1.0 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 90, acid = 70) - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - resistance_flags = LAVA_PROOF | FIRE_PROOF - var/range = 7 - - var/datum/radio_frequency/radio_connection - var/frequency = 0 - var/logic_id_tag = "default" //Defines the ID tag to send logic signals to, so you don't have to unlink from doors and stuff - var/logic_connect = 0 //Set this to allow the button to send out logic signals when pressed in addition to normal stuff - -/obj/machinery/button/indestructible - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/machinery/driver_button/New(turf/loc, var/w_dir=null) - ..() - switch(w_dir) - if(NORTH) - pixel_y = 25 - if(SOUTH) - pixel_y = -25 - if(EAST) - pixel_x = 25 - if(WEST) - pixel_x = -25 - if(SSradio) - set_frequency(frequency) - -/obj/machinery/driver_button/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/driver_button/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) - return - -/obj/machinery/driver_button/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - - -/obj/machinery/driver_button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/driver_button/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/driver_button/attackby(obj/item/W, mob/user as mob, params) - - if(istype(W, /obj/item/detective_scanner)) - return - - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/wrench)) - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src)) - to_chat(user, "You detach \the [src] from the wall.") - new/obj/item/mounted/frame/driver_button(get_turf(src)) - qdel(src) - return 1 - - return ..() - -/obj/machinery/driver_button/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" -
      -
    • ID Tag: [format_tag("ID Tag","id_tag")]
    • -
    • Logic Connection: [logic_connect ? "On" : "Off"]
    • -
    • Logic ID Tag: [format_tag("Logic ID Tag", "logic_id_tag")]
    • -
    "} - -/obj/machinery/driver_button/attack_hand(mob/user as mob) - - add_fingerprint(usr) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - add_fingerprint(user) - - use_power(5) - - launch_sequence() - -/obj/machinery/driver_button/proc/launch_sequence() - active = 1 - icon_state = "launcheract" - - if(logic_connect) - if(!radio_connection) //can't output without this - return - - if(logic_id_tag == null) //Don't output to an undefined id_tag - return - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_FLICKER, //Buttons are a FLICKER source, since they only register as ON when you press it, then turn OFF after you release - ) - - radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) - - for(var/obj/machinery/door/poddoor/M in range(src,range)) - if(M.id_tag == id_tag && !M.protected) - spawn() - M.open() - - sleep(20) - - for(var/obj/machinery/mass_driver/M in range(src,range)) - if(M.id_tag == id_tag) - M.drive() - - sleep(50) - - for(var/obj/machinery/door/poddoor/M in range(src,range)) - if(M.id_tag == id_tag && !M.protected) - spawn() - M.close() - return - - icon_state = "launcherbtt" - active = 0 - -/obj/machinery/driver_button/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - ..() - if("toggle_logic" in href_list) - logic_connect = !logic_connect - -////////////////////////////////////// -// Ignition Switch // -////////////////////////////////////// - -/obj/machinery/ignition_switch - name = "ignition switch" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for a mounted igniter." - var/id = null - var/active = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/ignition_switch/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/ignition_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/ignition_switch/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/sparker/M in world) - if(M.id == id) - spawn( 0 ) - M.spark() - - for(var/obj/machinery/igniter/M in world) - if(M.id == id) - use_power(50) - M.on = !( M.on ) - M.icon_state = text("igniter[]", M.on) - - sleep(50) - - icon_state = "launcherbtt" - active = 0 + +////////////////////////////////////// +// Driver Button // +////////////////////////////////////// + +/obj/machinery/driver_button + name = "mass driver button" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for a mass driver." + var/id_tag = "default" + var/active = 0 + settagwhitelist = list("id_tag", "logic_id_tag") + anchored = 1.0 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 90, acid = 70) + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + resistance_flags = LAVA_PROOF | FIRE_PROOF + var/range = 7 + + var/datum/radio_frequency/radio_connection + var/frequency = 0 + var/logic_id_tag = "default" //Defines the ID tag to send logic signals to, so you don't have to unlink from doors and stuff + var/logic_connect = 0 //Set this to allow the button to send out logic signals when pressed in addition to normal stuff + +/obj/machinery/button/indestructible + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/machinery/driver_button/New(turf/loc, var/w_dir=null) + ..() + switch(w_dir) + if(NORTH) + pixel_y = 25 + if(SOUTH) + pixel_y = -25 + if(EAST) + pixel_x = 25 + if(WEST) + pixel_x = -25 + if(SSradio) + set_frequency(frequency) + +/obj/machinery/driver_button/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/driver_button/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) + return + +/obj/machinery/driver_button/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + + +/obj/machinery/driver_button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/driver_button/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/driver_button/attackby(obj/item/W, mob/user as mob, params) + + if(istype(W, /obj/item/detective_scanner)) + return + + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/wrench)) + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src)) + to_chat(user, "You detach \the [src] from the wall.") + new/obj/item/mounted/frame/driver_button(get_turf(src)) + qdel(src) + return 1 + + return ..() + +/obj/machinery/driver_button/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" +
      +
    • ID Tag: [format_tag("ID Tag","id_tag")]
    • +
    • Logic Connection: [logic_connect ? "On" : "Off"]
    • +
    • Logic ID Tag: [format_tag("Logic ID Tag", "logic_id_tag")]
    • +
    "} + +/obj/machinery/driver_button/attack_hand(mob/user as mob) + + add_fingerprint(usr) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + add_fingerprint(user) + + use_power(5) + + launch_sequence() + +/obj/machinery/driver_button/proc/launch_sequence() + active = 1 + icon_state = "launcheract" + + if(logic_connect) + if(!radio_connection) //can't output without this + return + + if(logic_id_tag == null) //Don't output to an undefined id_tag + return + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_FLICKER, //Buttons are a FLICKER source, since they only register as ON when you press it, then turn OFF after you release + ) + + radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) + + for(var/obj/machinery/door/poddoor/M in range(src,range)) + if(M.id_tag == id_tag && !M.protected) + spawn() + M.open() + + sleep(20) + + for(var/obj/machinery/mass_driver/M in range(src,range)) + if(M.id_tag == id_tag) + M.drive() + + sleep(50) + + for(var/obj/machinery/door/poddoor/M in range(src,range)) + if(M.id_tag == id_tag && !M.protected) + spawn() + M.close() + return + + icon_state = "launcherbtt" + active = 0 + +/obj/machinery/driver_button/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + ..() + if("toggle_logic" in href_list) + logic_connect = !logic_connect + +////////////////////////////////////// +// Ignition Switch // +////////////////////////////////////// + +/obj/machinery/ignition_switch + name = "ignition switch" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for a mounted igniter." + var/id = null + var/active = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/ignition_switch/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/ignition_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/ignition_switch/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/sparker/M in world) + if(M.id == id) + spawn( 0 ) + M.spark() + + for(var/obj/machinery/igniter/M in world) + if(M.id == id) + use_power(50) + M.on = !( M.on ) + M.icon_state = text("igniter[]", M.on) + + sleep(50) + + icon_state = "launcherbtt" + active = 0 diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index bb09c247bca4..5bc32a0172e9 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,433 +1,433 @@ -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors.dmi' - icon_state = "camera" - use_power = ACTIVE_POWER_USE - idle_power_usage = 5 - active_power_usage = 10 - layer = WALL_OBJ_LAYER - resistance_flags = FIRE_PROOF - damage_deflection = 12 - armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50) - var/datum/wires/camera/wires = null // Wires datum - max_integrity = 100 - integrity_failure = 50 - var/list/network = list("SS13") - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = TRUE - var/start_active = FALSE //If it ignores the random chance to start broken on round start - var/invuln = null - var/obj/item/camera_bug/bug = null - var/obj/item/camera_assembly/assembly = null - - //OTHER - - var/view_range = 7 - var/short_range = 2 - - var/busy = FALSE - var/emped = FALSE //Number of consecutive EMP's on this camera - - var/in_use_lights = 0 // TO BE IMPLEMENTED - var/toggle_sound = 'sound/items/wirecutter.ogg' - - -/obj/machinery/camera/New() - ..() - wires = new(src) - assembly = new(src) - assembly.state = 4 - assembly.anchored = 1 - assembly.update_icon() - - cameranet.cameras += src - cameranet.addCamera(src) - -/obj/machinery/camera/Initialize() - ..() - if(is_station_level(z) && prob(3) && !start_active) - toggle_cam(null, FALSE) - wires.CutAll() - -/obj/machinery/camera/Destroy() - toggle_cam(null, FALSE) //kick anyone viewing out - QDEL_NULL(assembly) - if(istype(bug)) - bug.bugged_cameras -= c_tag - if(bug.current == src) - bug.current = null - bug = null - QDEL_NULL(wires) - cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that - cameranet.cameras -= src - var/area/ai_monitored/A = get_area(src) - if(istype(A)) - A.motioncamera = null - area_motion = null - return ..() - -/obj/machinery/camera/emp_act(severity) - if(!status) - return - if(!isEmpProof()) - if(prob(150/severity)) - update_icon() - var/list/previous_network = network - network = list() - cameranet.removeCamera(src) - stat |= EMPED - set_light(0) - emped = emped+1 //Increase the number of consecutive EMP's - update_icon() - var/thisemp = emped //Take note of which EMP this proc is for - spawn(900) - if(!QDELETED(src)) - triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. - if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again - network = previous_network - stat &= ~EMPED - update_icon() - if(can_use()) - cameranet.addCamera(src) - emped = 0 //Resets the consecutive EMP count - spawn(100) - if(!QDELETED(src)) - cancelCameraAlarm() - for(var/mob/M in GLOB.player_list) - if(M.client && M.client.eye == src) - M.unset_machine() - M.reset_perspective(null) - to_chat(M, "The screen bursts into static.") - ..() - -/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune - if(isEmpProof()) - return - ..() - qdel(src)//to prevent bomb testing camera from exploding over and over forever - -/obj/machinery/camera/ex_act(severity) - if(invuln) - return - ..() - -/obj/machinery/camera/proc/setViewRange(num = 7) - view_range = num - cameranet.updateVisibility(src, 0) - -/obj/machinery/camera/singularity_pull(S, current_size) - if (status && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall. - toggle_cam(null, 0) - ..() - -/obj/machinery/camera/attackby(obj/item/I, mob/living/user, params) - var/msg = "You attach [I] into the assembly inner circuits." - var/msg2 = "The camera already has that upgrade!" - - if(istype(I, /obj/item/analyzer) && panel_open) //XRay - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isXRay()) - upgradeXRay() - qdel(I) - to_chat(user, "[msg]") - else - to_chat(user, "[msg2]") - - else if(istype(I, /obj/item/stack/sheet/mineral/plasma) && panel_open) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isEmpProof()) - var/obj/item/stack/sheet/mineral/plasma/P = I - upgradeEmpProof() - to_chat(user, "[msg]") - P.use(1) - else - to_chat(user, "[msg2]") - else if(istype(I, /obj/item/assembly/prox_sensor) && panel_open) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isMotion()) - upgradeMotion() - to_chat(user, "[msg]") - qdel(I) - else - to_chat(user, "[msg2]") - - // OTHER - else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) - var/mob/living/U = user - var/obj/item/paper/X = null - var/obj/item/pda/PDA = null - - var/itemname = "" - var/info = "" - if(istype(I, /obj/item/paper)) - X = I - itemname = X.name - info = X.info - else - PDA = I - var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) - if(N) - itemname = PDA.name - info = N.notehtml - to_chat(U, "You hold \the [itemname] up to the camera ...") - U.changeNext_move(CLICK_CD_MELEE) - for(var/mob/O in GLOB.player_list) - if(istype(O, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = O - if(AI.control_disabled || (AI.stat == DEAD)) - return - if(U.name == "Unknown") - to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") - else - to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") - AI.last_paper_seen = "[itemname][info]" - else if(O.client && O.client.eye == src) - to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - - else if(istype(I, /obj/item/camera_bug)) - if(!can_use()) - to_chat(user, "Camera non-functional.") - return - if(istype(bug)) - to_chat(user, "Camera bug removed.") - bug.bugged_cameras -= c_tag - bug = null - else - to_chat(user, "Camera bugged.") - bug = I - bug.bugged_cameras[c_tag] = src - - else if(istype(I, /obj/item/laser_pointer)) - var/obj/item/laser_pointer/L = I - L.laser_act(src, user) - else - return ..() - - -/obj/machinery/camera/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - panel_open = !panel_open - to_chat(user, "You screw [src]'s panel [panel_open ? "open" : "closed"].") - -/obj/machinery/camera/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(panel_open) - wires.Interact(user) - -/obj/machinery/camera/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(panel_open) - wires.Interact(user) - -/obj/machinery/camera/welder_act(mob/user, obj/item/I) - if(!panel_open || !wires.CanDeconstruct()) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_WELD_MESSAGE - if(I.use_tool(src, user, 100, volume = I.tool_volume)) - visible_message("[user] unwelds [src], leaving it as just a frame bolted to the wall.", - "You unweld [src], leaving it as just a frame bolted to the wall") - deconstruct(TRUE) - -/obj/machinery/camera/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(stat & BROKEN) - return damage_amount - . = ..() - -/obj/machinery/camera/obj_break(damage_flag) - if(status && !(flags & NODECONSTRUCT)) - triggerCameraAlarm() - toggle_cam(null, FALSE) - wires.CutAll() - -/obj/machinery/camera/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - if(!assembly) - assembly = new() - assembly.forceMove(drop_location()) - assembly.state = 1 - assembly.setDir(dir) - assembly.update_icon() - assembly = null - else - var/obj/item/I = new /obj/item/camera_assembly(loc) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 2) - qdel(src) - -/obj/machinery/camera/update_icon() - if(!status) - icon_state = "[initial(icon_state)]1" - else if(stat & EMPED) - icon_state = "[initial(icon_state)]emp" - else - icon_state = "[initial(icon_state)]" - -/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) - status = !status - if(can_use()) - cameranet.addCamera(src) - else - set_light(0) - cameranet.removeCamera(src) - cameranet.updateChunk(x, y, z) - var/change_msg = "deactivates" - if(status) - change_msg = "reactivates" - triggerCameraAlarm() - spawn(100) - if(!QDELETED(src)) - cancelCameraAlarm() - if(displaymessage) - if(user) - visible_message("[user] [change_msg] [src]!") - add_hiddenprint(user) - else - visible_message("\The [src] [change_msg]!") - - playsound(loc, toggle_sound, 100, 1) - update_icon() - - // now disconnect anyone using the camera - //Apparently, this will disconnect anyone even if the camera was re-activated. - //I guess that doesn't matter since they can't use it anyway? - for(var/mob/O in GLOB.player_list) - if(O.client && O.client.eye == src) - O.unset_machine() - O.reset_perspective(null) - to_chat(O, "The screen bursts into static.") - -/obj/machinery/camera/proc/triggerCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.triggerAlarm(loc, src) - -/obj/machinery/camera/proc/cancelCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.clearAlarm(loc, src) - -/obj/machinery/camera/proc/can_use() - if(!status) - return 0 - if(stat & EMPED) - return 0 - return 1 - -/obj/machinery/camera/proc/can_see() - var/list/see = null - var/turf/pos = get_turf(src) - if(isXRay()) - see = range(view_range, pos) - else - see = hear(view_range, pos) - return see - -/atom/proc/auto_turn() - //Automatically turns based on nearby walls. - var/turf/simulated/wall/T = null - for(var/i = 1, i <= 8; i += i) - T = get_ranged_target_turf(src, i, 1) - if(istype(T)) - //If someone knows a better way to do this, let me know. -Giacom - switch(i) - if(NORTH) - setDir(SOUTH) - if(SOUTH) - setDir(NORTH) - if(WEST) - setDir(EAST) - if(EAST) - setDir(WEST) - break - -//Return a working camera that can see a given mob -//or null if none -/proc/seen_by_camera(mob/M) - for(var/obj/machinery/camera/C in oview(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - return null - -/proc/near_range_camera(mob/M) - for(var/obj/machinery/camera/C in range(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - - return null - -/obj/machinery/camera/proc/Togglelight(on = FALSE) - for(var/mob/living/silicon/ai/A in ai_list) - for(var/obj/machinery/camera/cam in A.lit_cameras) - if(cam == src) - return - if(on) - set_light(AI_CAMERA_LUMINOSITY) - else - set_light(0) - -/obj/machinery/camera/proc/nano_structure() - var/cam[0] - var/turf/T = get_turf(src) - cam["name"] = sanitize(c_tag) - cam["deact"] = !can_use() - cam["camera"] = "\ref[src]" - if(T) - cam["x"] = T.x - cam["y"] = T.y - cam["z"] = T.z - else - cam["x"] = 0 - cam["y"] = 0 - cam["z"] = 0 - return cam - -/obj/machinery/camera/get_remote_view_fullscreens(mob/user) - if(view_range == short_range) //unfocused - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -/obj/machinery/camera/update_remote_sight(mob/living/user) - if(isXRay()) - user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - user.see_in_dark = max(user.see_in_dark, 8) - user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - else - user.sight = initial(user.sight) - user.see_in_dark = initial(user.see_in_dark) - user.lighting_alpha = initial(user.lighting_alpha) - - ..() - return TRUE - -/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. - var/turf/prev_turf - -/obj/machinery/camera/portable/New() - ..() - assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. - assembly.anchored = 0 - assembly.update_icon() - -/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. - if(cameranet && get_turf(src) != prev_turf) - cameranet.updatePortableCamera(src) - prev_turf = get_turf(src) +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors.dmi' + icon_state = "camera" + use_power = ACTIVE_POWER_USE + idle_power_usage = 5 + active_power_usage = 10 + layer = WALL_OBJ_LAYER + resistance_flags = FIRE_PROOF + damage_deflection = 12 + armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50) + var/datum/wires/camera/wires = null // Wires datum + max_integrity = 100 + integrity_failure = 50 + var/list/network = list("SS13") + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = TRUE + var/start_active = FALSE //If it ignores the random chance to start broken on round start + var/invuln = null + var/obj/item/camera_bug/bug = null + var/obj/item/camera_assembly/assembly = null + + //OTHER + + var/view_range = 7 + var/short_range = 2 + + var/busy = FALSE + var/emped = FALSE //Number of consecutive EMP's on this camera + + var/in_use_lights = 0 // TO BE IMPLEMENTED + var/toggle_sound = 'sound/items/wirecutter.ogg' + + +/obj/machinery/camera/New() + ..() + wires = new(src) + assembly = new(src) + assembly.state = 4 + assembly.anchored = 1 + assembly.update_icon() + + GLOB.cameranet.cameras += src + GLOB.cameranet.addCamera(src) + +/obj/machinery/camera/Initialize() + ..() + if(is_station_level(z) && prob(3) && !start_active) + toggle_cam(null, FALSE) + wires.CutAll() + +/obj/machinery/camera/Destroy() + toggle_cam(null, FALSE) //kick anyone viewing out + QDEL_NULL(assembly) + if(istype(bug)) + bug.bugged_cameras -= c_tag + if(bug.current == src) + bug.current = null + bug = null + QDEL_NULL(wires) + GLOB.cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that + GLOB.cameranet.cameras -= src + var/area/ai_monitored/A = get_area(src) + if(istype(A)) + A.motioncamera = null + area_motion = null + return ..() + +/obj/machinery/camera/emp_act(severity) + if(!status) + return + if(!isEmpProof()) + if(prob(150/severity)) + update_icon() + var/list/previous_network = network + network = list() + GLOB.cameranet.removeCamera(src) + stat |= EMPED + set_light(0) + emped = emped+1 //Increase the number of consecutive EMP's + update_icon() + var/thisemp = emped //Take note of which EMP this proc is for + spawn(900) + if(!QDELETED(src)) + triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. + if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again + network = previous_network + stat &= ~EMPED + update_icon() + if(can_use()) + GLOB.cameranet.addCamera(src) + emped = 0 //Resets the consecutive EMP count + spawn(100) + if(!QDELETED(src)) + cancelCameraAlarm() + for(var/mob/M in GLOB.player_list) + if(M.client && M.client.eye == src) + M.unset_machine() + M.reset_perspective(null) + to_chat(M, "The screen bursts into static.") + ..() + +/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune + if(isEmpProof()) + return + ..() + qdel(src)//to prevent bomb testing camera from exploding over and over forever + +/obj/machinery/camera/ex_act(severity) + if(invuln) + return + ..() + +/obj/machinery/camera/proc/setViewRange(num = 7) + view_range = num + GLOB.cameranet.updateVisibility(src, 0) + +/obj/machinery/camera/singularity_pull(S, current_size) + if (status && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall. + toggle_cam(null, 0) + ..() + +/obj/machinery/camera/attackby(obj/item/I, mob/living/user, params) + var/msg = "You attach [I] into the assembly inner circuits." + var/msg2 = "The camera already has that upgrade!" + + if(istype(I, /obj/item/analyzer) && panel_open) //XRay + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isXRay()) + upgradeXRay() + qdel(I) + to_chat(user, "[msg]") + else + to_chat(user, "[msg2]") + + else if(istype(I, /obj/item/stack/sheet/mineral/plasma) && panel_open) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isEmpProof()) + var/obj/item/stack/sheet/mineral/plasma/P = I + upgradeEmpProof() + to_chat(user, "[msg]") + P.use(1) + else + to_chat(user, "[msg2]") + else if(istype(I, /obj/item/assembly/prox_sensor) && panel_open) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isMotion()) + upgradeMotion() + to_chat(user, "[msg]") + qdel(I) + else + to_chat(user, "[msg2]") + + // OTHER + else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) + var/mob/living/U = user + var/obj/item/paper/X = null + var/obj/item/pda/PDA = null + + var/itemname = "" + var/info = "" + if(istype(I, /obj/item/paper)) + X = I + itemname = X.name + info = X.info + else + PDA = I + var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) + if(N) + itemname = PDA.name + info = N.notehtml + to_chat(U, "You hold \the [itemname] up to the camera ...") + U.changeNext_move(CLICK_CD_MELEE) + for(var/mob/O in GLOB.player_list) + if(istype(O, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = O + if(AI.control_disabled || (AI.stat == DEAD)) + return + if(U.name == "Unknown") + to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") + else + to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") + AI.last_paper_seen = "[itemname][info]" + else if(O.client && O.client.eye == src) + to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + else if(istype(I, /obj/item/camera_bug)) + if(!can_use()) + to_chat(user, "Camera non-functional.") + return + if(istype(bug)) + to_chat(user, "Camera bug removed.") + bug.bugged_cameras -= c_tag + bug = null + else + to_chat(user, "Camera bugged.") + bug = I + bug.bugged_cameras[c_tag] = src + + else if(istype(I, /obj/item/laser_pointer)) + var/obj/item/laser_pointer/L = I + L.laser_act(src, user) + else + return ..() + + +/obj/machinery/camera/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + panel_open = !panel_open + to_chat(user, "You screw [src]'s panel [panel_open ? "open" : "closed"].") + +/obj/machinery/camera/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/camera/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/camera/welder_act(mob/user, obj/item/I) + if(!panel_open || !wires.CanDeconstruct()) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(I.use_tool(src, user, 100, volume = I.tool_volume)) + visible_message("[user] unwelds [src], leaving it as just a frame bolted to the wall.", + "You unweld [src], leaving it as just a frame bolted to the wall") + deconstruct(TRUE) + +/obj/machinery/camera/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(stat & BROKEN) + return damage_amount + . = ..() + +/obj/machinery/camera/obj_break(damage_flag) + if(status && !(flags & NODECONSTRUCT)) + triggerCameraAlarm() + toggle_cam(null, FALSE) + wires.CutAll() + +/obj/machinery/camera/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + if(!assembly) + assembly = new() + assembly.forceMove(drop_location()) + assembly.state = 1 + assembly.setDir(dir) + assembly.update_icon() + assembly = null + else + var/obj/item/I = new /obj/item/camera_assembly(loc) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 2) + qdel(src) + +/obj/machinery/camera/update_icon() + if(!status) + icon_state = "[initial(icon_state)]1" + else if(stat & EMPED) + icon_state = "[initial(icon_state)]emp" + else + icon_state = "[initial(icon_state)]" + +/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) + status = !status + if(can_use()) + GLOB.cameranet.addCamera(src) + else + set_light(0) + GLOB.cameranet.removeCamera(src) + GLOB.cameranet.updateChunk(x, y, z) + var/change_msg = "deactivates" + if(status) + change_msg = "reactivates" + triggerCameraAlarm() + spawn(100) + if(!QDELETED(src)) + cancelCameraAlarm() + if(displaymessage) + if(user) + visible_message("[user] [change_msg] [src]!") + add_hiddenprint(user) + else + visible_message("\The [src] [change_msg]!") + + playsound(loc, toggle_sound, 100, 1) + update_icon() + + // now disconnect anyone using the camera + //Apparently, this will disconnect anyone even if the camera was re-activated. + //I guess that doesn't matter since they can't use it anyway? + for(var/mob/O in GLOB.player_list) + if(O.client && O.client.eye == src) + O.unset_machine() + O.reset_perspective(null) + to_chat(O, "The screen bursts into static.") + +/obj/machinery/camera/proc/triggerCameraAlarm() + if(is_station_contact(z)) + SSalarms.camera_alarm.triggerAlarm(loc, src) + +/obj/machinery/camera/proc/cancelCameraAlarm() + if(is_station_contact(z)) + SSalarms.camera_alarm.clearAlarm(loc, src) + +/obj/machinery/camera/proc/can_use() + if(!status) + return 0 + if(stat & EMPED) + return 0 + return 1 + +/obj/machinery/camera/proc/can_see() + var/list/see = null + var/turf/pos = get_turf(src) + if(isXRay()) + see = range(view_range, pos) + else + see = hear(view_range, pos) + return see + +/atom/proc/auto_turn() + //Automatically turns based on nearby walls. + var/turf/simulated/wall/T = null + for(var/i = 1, i <= 8; i += i) + T = get_ranged_target_turf(src, i, 1) + if(istype(T)) + //If someone knows a better way to do this, let me know. -Giacom + switch(i) + if(NORTH) + setDir(SOUTH) + if(SOUTH) + setDir(NORTH) + if(WEST) + setDir(EAST) + if(EAST) + setDir(WEST) + break + +//Return a working camera that can see a given mob +//or null if none +/proc/seen_by_camera(mob/M) + for(var/obj/machinery/camera/C in oview(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + return null + +/proc/near_range_camera(mob/M) + for(var/obj/machinery/camera/C in range(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + + return null + +/obj/machinery/camera/proc/Togglelight(on = FALSE) + for(var/mob/living/silicon/ai/A in GLOB.ai_list) + for(var/obj/machinery/camera/cam in A.lit_cameras) + if(cam == src) + return + if(on) + set_light(AI_CAMERA_LUMINOSITY) + else + set_light(0) + +/obj/machinery/camera/proc/nano_structure() + var/cam[0] + var/turf/T = get_turf(src) + cam["name"] = sanitize(c_tag) + cam["deact"] = !can_use() + cam["camera"] = "\ref[src]" + if(T) + cam["x"] = T.x + cam["y"] = T.y + cam["z"] = T.z + else + cam["x"] = 0 + cam["y"] = 0 + cam["z"] = 0 + return cam + +/obj/machinery/camera/get_remote_view_fullscreens(mob/user) + if(view_range == short_range) //unfocused + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +/obj/machinery/camera/update_remote_sight(mob/living/user) + if(isXRay()) + user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + user.see_in_dark = max(user.see_in_dark, 8) + user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + else + user.sight = initial(user.sight) + user.see_in_dark = initial(user.see_in_dark) + user.lighting_alpha = initial(user.lighting_alpha) + + ..() + return TRUE + +/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. + var/turf/prev_turf + +/obj/machinery/camera/portable/New() + ..() + assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. + assembly.anchored = 0 + assembly.update_icon() + +/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. + if(GLOB.cameranet && get_turf(src) != prev_turf) + GLOB.cameranet.updatePortableCamera(src) + prev_turf = get_turf(src) diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index bd795ab340be..c7d95370613c 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -1,179 +1,179 @@ -#define ASSEMBLY_UNBUILT 0 // Nothing done to it -#define ASSEMBLY_WRENCHED 1 // Wrenched in place -#define ASSEMBLY_WELDED 2 // Welded in place -#define ASSEMBLY_WIRED 3 // Wires attached (Upgradable now) -#define ASSEMBLY_BUILT 4 // Fully built (incl panel closed) -#define HEY_IM_WORKING_HERE 666 //So nobody can mess with the camera while we're inputting settings - -/obj/item/camera_assembly - name = "camera assembly" - desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." - icon = 'icons/obj/monitors.dmi' - icon_state = "cameracase" - w_class = WEIGHT_CLASS_SMALL - anchored = FALSE - materials = list(MAT_METAL=400, MAT_GLASS=250) - // Motion, EMP-Proof, X-Ray - var/list/obj/item/possible_upgrades = list(/obj/item/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/analyzer) - var/list/upgrades = list() - var/state = ASSEMBLY_UNBUILT - - -/obj/item/camera_assembly/Destroy() - QDEL_LIST(upgrades) - return ..() - -/obj/item/camera_assembly/attackby(obj/item/I, mob/living/user, params) - if(state == ASSEMBLY_WELDED && iscoil(I)) - var/obj/item/stack/cable_coil/C = I - if(C.use(2)) - to_chat(user, "You add wires to the assembly.") - playsound(loc, I.usesound, 50, 1) - state = ASSEMBLY_WIRED - else - to_chat(user, "You need 2 coils of wire to wire the assembly.") - return - - // Upgrades! - else if(is_type_in_list(I, possible_upgrades) && !is_type_in_list(I, upgrades)) // Is a possible upgrade and isn't in the camera already. - if(!user.unEquip(I)) - to_chat(user, "[I] is stuck!") - return - to_chat(user, "You attach [I] into the assembly inner circuits.") - upgrades += I - user.drop_item() - I.loc = src - return - else - return ..() - -/obj/item/camera_assembly/crowbar_act(mob/user, obj/item/I) - if(!upgrades.len) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/obj/U = locate(/obj) in upgrades - if(U) - to_chat(user, "You unattach an upgrade from the assembly.") - playsound(loc, I.usesound, 50, 1) - U.loc = get_turf(src) - upgrades -= U - -/obj/item/camera_assembly/screwdriver_act(mob/user, obj/item/I) - if(state != ASSEMBLY_WIRED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - state = HEY_IM_WORKING_HERE - var/input = strip_html(input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")) - if(!input) - state = ASSEMBLY_WIRED - to_chat(usr, "No input found please hang up and try your call again.") - return - - var/list/tempnetwork = splittext(input, ",") - if(tempnetwork.len < 1) - state = ASSEMBLY_WIRED - to_chat(usr, "No network found please hang up and try your call again.") - return - - var/area/camera_area = get_area(src) - var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" - input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) - state = ASSEMBLY_BUILT - var/obj/machinery/camera/C = new(loc) - loc = C - C.assembly = src - - C.auto_turn() - - C.network = uniquelist(tempnetwork) - tempnetwork = difflist(C.network,GLOB.restricted_camera_networks) - if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility. - cameranet.removeCamera(C) - - C.c_tag = input - - for(var/i = 5; i >= 0; i -= 1) - var/direct = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) - if(direct != "LEAVE IT") - C.dir = text2dir(direct) - if(i != 0) - var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") - if(confirm == "Yes") - break - - -/obj/item/camera_assembly/wirecutter_act(mob/user, obj/item/I) - if(state != ASSEMBLY_WIRED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - new/obj/item/stack/cable_coil(get_turf(src), 2) - WIRECUTTER_SNIP_MESSAGE - state = ASSEMBLY_WELDED - return - -/obj/item/camera_assembly/wrench_act(mob/user, obj/item/I) - if(state != ASSEMBLY_UNBUILT && state != ASSEMBLY_WRENCHED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(state == ASSEMBLY_UNBUILT && isturf(loc)) - WRENCH_ANCHOR_TO_WALL_MESSAGE - anchored = TRUE - state = ASSEMBLY_WRENCHED - update_icon() - auto_turn() - else if(state == ASSEMBLY_WRENCHED) - WRENCH_UNANCHOR_WALL_MESSAGE - anchored = FALSE - update_icon() - state = ASSEMBLY_UNBUILT - else - to_chat(user, "[src] can't fit here!") - -/obj/item/camera_assembly/welder_act(mob/user, obj/item/I) - if(state == ASSEMBLY_UNBUILT) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_WELD_MESSAGE - if(state == ASSEMBLY_WRENCHED) - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - to_chat(user, "You weld [src] into place.") - state = ASSEMBLY_WELDED - else if(state == ASSEMBLY_WELDED) - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - to_chat(user, "You unweld [src] from its place.") - state = ASSEMBLY_WRENCHED - -/obj/item/camera_assembly/update_icon() - if(anchored) - icon_state = "camera1" - else - icon_state = "cameracase" - -/obj/item/camera_assembly/attack_hand(mob/user as mob) - if(!anchored) - ..() - -/obj/item/camera_assembly/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc) - qdel(src) - - -#undef ASSEMBLY_UNBUILT -#undef ASSEMBLY_WRENCHED -#undef ASSEMBLY_WELDED -#undef ASSEMBLY_WIRED -#undef ASSEMBLY_BUILT -#undef HEY_IM_WORKING_HERE +#define ASSEMBLY_UNBUILT 0 // Nothing done to it +#define ASSEMBLY_WRENCHED 1 // Wrenched in place +#define ASSEMBLY_WELDED 2 // Welded in place +#define ASSEMBLY_WIRED 3 // Wires attached (Upgradable now) +#define ASSEMBLY_BUILT 4 // Fully built (incl panel closed) +#define HEY_IM_WORKING_HERE 666 //So nobody can mess with the camera while we're inputting settings + +/obj/item/camera_assembly + name = "camera assembly" + desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." + icon = 'icons/obj/monitors.dmi' + icon_state = "cameracase" + w_class = WEIGHT_CLASS_SMALL + anchored = FALSE + materials = list(MAT_METAL=400, MAT_GLASS=250) + // Motion, EMP-Proof, X-Ray + var/list/obj/item/possible_upgrades = list(/obj/item/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/analyzer) + var/list/upgrades = list() + var/state = ASSEMBLY_UNBUILT + + +/obj/item/camera_assembly/Destroy() + QDEL_LIST(upgrades) + return ..() + +/obj/item/camera_assembly/attackby(obj/item/I, mob/living/user, params) + if(state == ASSEMBLY_WELDED && iscoil(I)) + var/obj/item/stack/cable_coil/C = I + if(C.use(2)) + to_chat(user, "You add wires to the assembly.") + playsound(loc, I.usesound, 50, 1) + state = ASSEMBLY_WIRED + else + to_chat(user, "You need 2 coils of wire to wire the assembly.") + return + + // Upgrades! + else if(is_type_in_list(I, possible_upgrades) && !is_type_in_list(I, upgrades)) // Is a possible upgrade and isn't in the camera already. + if(!user.unEquip(I)) + to_chat(user, "[I] is stuck!") + return + to_chat(user, "You attach [I] into the assembly inner circuits.") + upgrades += I + user.drop_item() + I.loc = src + return + else + return ..() + +/obj/item/camera_assembly/crowbar_act(mob/user, obj/item/I) + if(!upgrades.len) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/obj/U = locate(/obj) in upgrades + if(U) + to_chat(user, "You unattach an upgrade from the assembly.") + playsound(loc, I.usesound, 50, 1) + U.loc = get_turf(src) + upgrades -= U + +/obj/item/camera_assembly/screwdriver_act(mob/user, obj/item/I) + if(state != ASSEMBLY_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + state = HEY_IM_WORKING_HERE + var/input = strip_html(input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")) + if(!input) + state = ASSEMBLY_WIRED + to_chat(usr, "No input found please hang up and try your call again.") + return + + var/list/tempnetwork = splittext(input, ",") + if(tempnetwork.len < 1) + state = ASSEMBLY_WIRED + to_chat(usr, "No network found please hang up and try your call again.") + return + + var/area/camera_area = get_area(src) + var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" + input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) + state = ASSEMBLY_BUILT + var/obj/machinery/camera/C = new(loc) + loc = C + C.assembly = src + + C.auto_turn() + + C.network = uniquelist(tempnetwork) + tempnetwork = difflist(C.network,GLOB.restricted_camera_networks) + if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility. + GLOB.cameranet.removeCamera(C) + + C.c_tag = input + + for(var/i = 5; i >= 0; i -= 1) + var/direct = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) + if(direct != "LEAVE IT") + C.dir = text2dir(direct) + if(i != 0) + var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") + if(confirm == "Yes") + break + + +/obj/item/camera_assembly/wirecutter_act(mob/user, obj/item/I) + if(state != ASSEMBLY_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new/obj/item/stack/cable_coil(get_turf(src), 2) + WIRECUTTER_SNIP_MESSAGE + state = ASSEMBLY_WELDED + return + +/obj/item/camera_assembly/wrench_act(mob/user, obj/item/I) + if(state != ASSEMBLY_UNBUILT && state != ASSEMBLY_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state == ASSEMBLY_UNBUILT && isturf(loc)) + WRENCH_ANCHOR_TO_WALL_MESSAGE + anchored = TRUE + state = ASSEMBLY_WRENCHED + update_icon() + auto_turn() + else if(state == ASSEMBLY_WRENCHED) + WRENCH_UNANCHOR_WALL_MESSAGE + anchored = FALSE + update_icon() + state = ASSEMBLY_UNBUILT + else + to_chat(user, "[src] can't fit here!") + +/obj/item/camera_assembly/welder_act(mob/user, obj/item/I) + if(state == ASSEMBLY_UNBUILT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(state == ASSEMBLY_WRENCHED) + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + to_chat(user, "You weld [src] into place.") + state = ASSEMBLY_WELDED + else if(state == ASSEMBLY_WELDED) + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + to_chat(user, "You unweld [src] from its place.") + state = ASSEMBLY_WRENCHED + +/obj/item/camera_assembly/update_icon() + if(anchored) + icon_state = "camera1" + else + icon_state = "cameracase" + +/obj/item/camera_assembly/attack_hand(mob/user as mob) + if(!anchored) + ..() + +/obj/item/camera_assembly/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc) + qdel(src) + + +#undef ASSEMBLY_UNBUILT +#undef ASSEMBLY_WRENCHED +#undef ASSEMBLY_WELDED +#undef ASSEMBLY_WIRED +#undef ASSEMBLY_BUILT +#undef HEY_IM_WORKING_HERE diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 312e4cf577ef..16fb6eca2049 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -1,66 +1,66 @@ -/obj/machinery/camera - - var/list/motionTargets = list() - var/detectTime = 0 - var/area/ai_monitored/area_motion = null - var/alarm_delay = 100 - - -/obj/machinery/camera/process() - // motion camera event loop - if(stat & (EMPED|NOPOWER)) - return - if(!isMotion()) - . = PROCESS_KILL - return - if(detectTime > 0) - var/elapsed = world.time - detectTime - if(elapsed > alarm_delay) - triggerAlarm() - else if(detectTime == -1) - for(var/mob/target in motionTargets) - if(target.stat == 2) lostTarget(target) - // If not detecting with motion camera... - if(!area_motion) - // See if the camera is still in range - if(!in_range(src, target)) - // If they aren't in range, lose the target. - lostTarget(target) - -/obj/machinery/camera/proc/newTarget(var/mob/target) - if(istype(target, /mob/living/silicon/ai)) return 0 - if(detectTime == 0) - detectTime = world.time // start the clock - if(!(target in motionTargets)) - motionTargets += target - return 1 - -/obj/machinery/camera/proc/lostTarget(var/mob/target) - if(target in motionTargets) - motionTargets -= target - if(motionTargets.len == 0) - cancelAlarm() - -/obj/machinery/camera/proc/cancelAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(detectTime == -1 && is_station_contact(z)) - SSalarms.motion_alarm.clearAlarm(loc, src) - detectTime = 0 - return TRUE - -/obj/machinery/camera/proc/triggerAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(!detectTime || !is_station_contact(z)) - return FALSE - SSalarms.motion_alarm.triggerAlarm(loc, src) - detectTime = -1 - return TRUE - -/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) - // Motion cameras outside of an "ai monitored" area will use this to detect stuff. - if(!area_motion) - if(isliving(AM)) - newTarget(AM) - +/obj/machinery/camera + + var/list/motionTargets = list() + var/detectTime = 0 + var/area/ai_monitored/area_motion = null + var/alarm_delay = 100 + + +/obj/machinery/camera/process() + // motion camera event loop + if(stat & (EMPED|NOPOWER)) + return + if(!isMotion()) + . = PROCESS_KILL + return + if(detectTime > 0) + var/elapsed = world.time - detectTime + if(elapsed > alarm_delay) + triggerAlarm() + else if(detectTime == -1) + for(var/mob/target in motionTargets) + if(target.stat == 2) lostTarget(target) + // If not detecting with motion camera... + if(!area_motion) + // See if the camera is still in range + if(!in_range(src, target)) + // If they aren't in range, lose the target. + lostTarget(target) + +/obj/machinery/camera/proc/newTarget(var/mob/target) + if(istype(target, /mob/living/silicon/ai)) return 0 + if(detectTime == 0) + detectTime = world.time // start the clock + if(!(target in motionTargets)) + motionTargets += target + return 1 + +/obj/machinery/camera/proc/lostTarget(var/mob/target) + if(target in motionTargets) + motionTargets -= target + if(motionTargets.len == 0) + cancelAlarm() + +/obj/machinery/camera/proc/cancelAlarm() + if(!status || (stat & NOPOWER)) + return FALSE + if(detectTime == -1 && is_station_contact(z)) + SSalarms.motion_alarm.clearAlarm(loc, src) + detectTime = 0 + return TRUE + +/obj/machinery/camera/proc/triggerAlarm() + if(!status || (stat & NOPOWER)) + return FALSE + if(!detectTime || !is_station_contact(z)) + return FALSE + SSalarms.motion_alarm.triggerAlarm(loc, src) + detectTime = -1 + return TRUE + +/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) + // Motion cameras outside of an "ai monitored" area will use this to detect stuff. + if(!area_motion) + if(isliving(AM)) + newTarget(AM) + diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index ac70a7c11bad..0bd241ee0a07 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,92 +1,92 @@ -// PRESETS - -// EMP - -/obj/machinery/camera/emp_proof/New() - ..() - upgradeEmpProof() - -// X-RAY - -/obj/machinery/camera/xray - icon_state = "xraycam" // Thanks to Krutchen for the icons. - -/obj/machinery/camera/xray/New() - ..() - upgradeXRay() - -// MOTION - -/obj/machinery/camera/motion/New() - ..() - upgradeMotion() - -// ALL UPGRADES - -/obj/machinery/camera/all/New() - ..() - upgradeEmpProof() - upgradeXRay() - upgradeMotion() - -// AUTONAME - -/obj/machinery/camera/autoname - var/number = 0 //camera number in area - -//This camera type automatically sets it's name to whatever the area that it's in is called. -/obj/machinery/camera/autoname/New() - ..() - spawn(10) - number = 1 - var/area/A = get_area(src) - if(A) - for(var/obj/machinery/camera/autoname/C in world) - if(C == src) continue - var/area/CA = get_area(C) - if(CA.type == A.type) - if(C.number) - number = max(number, C.number+1) - c_tag = "[A.name] #[number]" - - -// CHECKS - -/obj/machinery/camera/proc/isEmpProof() - var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isXRay() - var/O = locate(/obj/item/analyzer) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isMotion() - var/O = locate(/obj/item/assembly/prox_sensor) in assembly.upgrades - return O - -// UPGRADE PROCS - -/obj/machinery/camera/proc/upgradeEmpProof() - assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) - setPowerUsage() - -/obj/machinery/camera/proc/upgradeXRay() - assembly.upgrades.Add(new /obj/item/analyzer(assembly)) - setPowerUsage() - //Update what it can see. - cameranet.updateVisibility(src, 0) - -// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. -/obj/machinery/camera/proc/upgradeMotion() - assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) - setPowerUsage() - // Add it to machines that process - START_PROCESSING(SSmachines, src) - -/obj/machinery/camera/proc/setPowerUsage() - var/mult = 1 - if(isXRay()) - mult++ - if(isMotion()) - mult++ - active_power_usage = mult*initial(active_power_usage) +// PRESETS + +// EMP + +/obj/machinery/camera/emp_proof/New() + ..() + upgradeEmpProof() + +// X-RAY + +/obj/machinery/camera/xray + icon_state = "xraycam" // Thanks to Krutchen for the icons. + +/obj/machinery/camera/xray/New() + ..() + upgradeXRay() + +// MOTION + +/obj/machinery/camera/motion/New() + ..() + upgradeMotion() + +// ALL UPGRADES + +/obj/machinery/camera/all/New() + ..() + upgradeEmpProof() + upgradeXRay() + upgradeMotion() + +// AUTONAME + +/obj/machinery/camera/autoname + var/number = 0 //camera number in area + +//This camera type automatically sets it's name to whatever the area that it's in is called. +/obj/machinery/camera/autoname/New() + ..() + spawn(10) + number = 1 + var/area/A = get_area(src) + if(A) + for(var/obj/machinery/camera/autoname/C in world) + if(C == src) continue + var/area/CA = get_area(C) + if(CA.type == A.type) + if(C.number) + number = max(number, C.number+1) + c_tag = "[A.name] #[number]" + + +// CHECKS + +/obj/machinery/camera/proc/isEmpProof() + var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isXRay() + var/O = locate(/obj/item/analyzer) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isMotion() + var/O = locate(/obj/item/assembly/prox_sensor) in assembly.upgrades + return O + +// UPGRADE PROCS + +/obj/machinery/camera/proc/upgradeEmpProof() + assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) + setPowerUsage() + +/obj/machinery/camera/proc/upgradeXRay() + assembly.upgrades.Add(new /obj/item/analyzer(assembly)) + setPowerUsage() + //Update what it can see. + GLOB.cameranet.updateVisibility(src, 0) + +// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. +/obj/machinery/camera/proc/upgradeMotion() + assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) + setPowerUsage() + // Add it to machines that process + START_PROCESSING(SSmachines, src) + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if(isXRay()) + mult++ + if(isMotion()) + mult++ + active_power_usage = mult*initial(active_power_usage) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 277cee5beb37..1c247b23c032 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,260 +1,260 @@ -/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) - if(!T) - return 1 - if(!is_level_reachable(T.z)) - return 1 - return 0 - - -/mob/living/silicon/ai/var/max_locations = 10 -/mob/living/silicon/ai/var/stored_locations[0] - -/mob/living/silicon/ai/proc/get_camera_list() - - track.cameras.Cut() - - if(src.stat == 2) - return - - var/list/L = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - L.Add(C) - - camera_sort(L) - - var/list/T = list() - - for(var/obj/machinery/camera/C in L) - var/list/tempnetwork = C.network & src.network - if(tempnetwork.len) - T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C - - track.cameras = T - return T - - -/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) - set category = "AI Commands" - set name = "Show Camera List" - - if(src.stat == 2) - to_chat(src, "You can't list the cameras because you are dead!") - return - - if(!camera || camera == "Cancel") - return 0 - - var/obj/machinery/camera/C = track.cameras[camera] - src.eyeobj.setLoc(C) - - return - -/mob/living/silicon/ai/proc/ai_store_location(loc as text) - set category = "AI Commands" - set name = "Store Camera Location" - set desc = "Stores your current camera location by the given name" - - loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN)) - if(!loc) - to_chat(src, "Must supply a location name") - return - - if(stored_locations.len >= max_locations) - to_chat(src, "Cannot store additional locations. Remove one first") - return - - if(loc in stored_locations) - to_chat(src, "There is already a stored location by this name") - return - - var/L = get_turf(eyeobj) - if(InvalidTurf(get_turf(L))) - to_chat(src, "Unable to store this location") - return - - stored_locations[loc] = L - to_chat(src, "Location '[loc]' stored") - -/mob/living/silicon/ai/proc/sorted_stored_locations() - return sortList(stored_locations) - -/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Goto Camera Location" - set desc = "Returns to the selected camera location" - - if(!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - var/L = stored_locations[loc] - src.eyeobj.setLoc(L) - -/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Delete Camera Location" - set desc = "Deletes the selected camera location" - - if(!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - stored_locations.Remove(loc) - to_chat(src, "Location [loc] removed") - -// Used to allow the AI is write in mob names/camera name from the CMD line. -/datum/trackable - var/list/names = list() - var/list/namecounts = list() - var/list/humans = list() - var/list/others = list() - var/list/cameras = list() - -/mob/living/silicon/ai/proc/trackable_mobs() - - track.names.Cut() - track.namecounts.Cut() - track.humans.Cut() - track.others.Cut() - - if(usr.stat == 2) - return list() - - for(var/mob/living/M in GLOB.mob_list) - if(!M.can_track(usr)) - continue - - // Human check - var/human = 0 - if(istype(M, /mob/living/carbon/human)) - human = 1 - - var/name = M.name - if(name in track.names) - track.namecounts[name]++ - name = text("[] ([])", name, track.namecounts[name]) - else - track.names.Add(name) - track.namecounts[name] = 1 - if(human) - track.humans[name] = M - else - track.others[name] = M - - var/list/targets = sortList(track.humans) + sortList(track.others) - - return targets - -/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs()) - set category = "AI Commands" - set name = "Track With Camera" - set desc = "Select who you would like to track." - - if(src.stat == DEAD) - to_chat(src, "You can't track with camera because you are dead!") - return - if(!target_name) - return - - var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) - - ai_actual_track(target) - -/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) - if(!cameraFollow) - return - - to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") - cameraFollow = null - -/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) - if(!istype(target)) - return - var/mob/living/silicon/ai/U = usr - - U.cameraFollow = target - U.tracking = 1 - - to_chat(U, "Attempting to track [target.get_visible_name()]...") - sleep(min(30, get_dist(target, U.eyeobj) / 4)) - spawn(15) //give the AI a grace period to stop moving. - U.tracking = 0 - - if(!target || !target.can_track(usr)) - to_chat(U, "Target is not near any active cameras.") - U.cameraFollow = null - return - - to_chat(U, "Now tracking [target.get_visible_name()] on camera.") - - var/cameraticks = 0 - spawn(0) - while(U.cameraFollow == target) - if(U.cameraFollow == null) - return - - if(!target.can_track(usr)) - U.tracking = 1 - if(!cameraticks) - to_chat(U, "Target is not near any active cameras. Attempting to reacquire...") - cameraticks++ - if(cameraticks > 9) - U.cameraFollow = null - to_chat(U, "Unable to reacquire, cancelling track...") - U.tracking = 0 - return - else - sleep(10) - continue - - else - cameraticks = 0 - U.tracking = 0 - - if(U.eyeobj) - U.eyeobj.setLoc(get_turf(target)) - - else - view_core() - U.cameraFollow = null - return - - sleep(10) - -/proc/near_camera(mob/living/M) - if(!isturf(M.loc)) - return 0 - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(!(R.camera && R.camera.can_use()) && !cameranet.checkCameraVis(M)) - return 0 - else if(!cameranet.checkCameraVis(M)) - return 0 - return 1 - -/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - if(!src.can_use()) - return - user.eyeobj.setLoc(get_turf(src)) - - -/mob/living/silicon/ai/attack_ai(mob/user) - ai_camera_list() - -/proc/camera_sort(list/L) - var/obj/machinery/camera/a - var/obj/machinery/camera/b - - for(var/i = L.len, i > 0, i--) - for(var/j = 1 to i - 1) - a = L[j] - b = L[j + 1] - if(a.c_tag_order != b.c_tag_order) - if(a.c_tag_order > b.c_tag_order) - L.Swap(j, j + 1) - else - if(sorttext(a.c_tag, b.c_tag) < 0) - L.Swap(j, j + 1) - return L +/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) + if(!T) + return 1 + if(!is_level_reachable(T.z)) + return 1 + return 0 + + +/mob/living/silicon/ai/var/max_locations = 10 +/mob/living/silicon/ai/var/stored_locations[0] + +/mob/living/silicon/ai/proc/get_camera_list() + + track.cameras.Cut() + + if(src.stat == 2) + return + + var/list/L = list() + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + L.Add(C) + + camera_sort(L) + + var/list/T = list() + + for(var/obj/machinery/camera/C in L) + var/list/tempnetwork = C.network & src.network + if(tempnetwork.len) + T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C + + track.cameras = T + return T + + +/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) + set category = "AI Commands" + set name = "Show Camera List" + + if(src.stat == 2) + to_chat(src, "You can't list the cameras because you are dead!") + return + + if(!camera || camera == "Cancel") + return 0 + + var/obj/machinery/camera/C = track.cameras[camera] + src.eyeobj.setLoc(C) + + return + +/mob/living/silicon/ai/proc/ai_store_location(loc as text) + set category = "AI Commands" + set name = "Store Camera Location" + set desc = "Stores your current camera location by the given name" + + loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN)) + if(!loc) + to_chat(src, "Must supply a location name") + return + + if(stored_locations.len >= max_locations) + to_chat(src, "Cannot store additional locations. Remove one first") + return + + if(loc in stored_locations) + to_chat(src, "There is already a stored location by this name") + return + + var/L = get_turf(eyeobj) + if(InvalidTurf(get_turf(L))) + to_chat(src, "Unable to store this location") + return + + stored_locations[loc] = L + to_chat(src, "Location '[loc]' stored") + +/mob/living/silicon/ai/proc/sorted_stored_locations() + return sortList(stored_locations) + +/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Goto Camera Location" + set desc = "Returns to the selected camera location" + + if(!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + var/L = stored_locations[loc] + src.eyeobj.setLoc(L) + +/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Delete Camera Location" + set desc = "Deletes the selected camera location" + + if(!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + stored_locations.Remove(loc) + to_chat(src, "Location [loc] removed") + +// Used to allow the AI is write in mob names/camera name from the CMD line. +/datum/trackable + var/list/names = list() + var/list/namecounts = list() + var/list/humans = list() + var/list/others = list() + var/list/cameras = list() + +/mob/living/silicon/ai/proc/trackable_mobs() + + track.names.Cut() + track.namecounts.Cut() + track.humans.Cut() + track.others.Cut() + + if(usr.stat == 2) + return list() + + for(var/mob/living/M in GLOB.mob_list) + if(!M.can_track(usr)) + continue + + // Human check + var/human = 0 + if(istype(M, /mob/living/carbon/human)) + human = 1 + + var/name = M.name + if(name in track.names) + track.namecounts[name]++ + name = text("[] ([])", name, track.namecounts[name]) + else + track.names.Add(name) + track.namecounts[name] = 1 + if(human) + track.humans[name] = M + else + track.others[name] = M + + var/list/targets = sortList(track.humans) + sortList(track.others) + + return targets + +/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs()) + set category = "AI Commands" + set name = "Track With Camera" + set desc = "Select who you would like to track." + + if(src.stat == DEAD) + to_chat(src, "You can't track with camera because you are dead!") + return + if(!target_name) + return + + var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + + ai_actual_track(target) + +/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) + if(!cameraFollow) + return + + to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") + cameraFollow = null + +/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) + if(!istype(target)) + return + var/mob/living/silicon/ai/U = usr + + U.cameraFollow = target + U.tracking = 1 + + to_chat(U, "Attempting to track [target.get_visible_name()]...") + sleep(min(30, get_dist(target, U.eyeobj) / 4)) + spawn(15) //give the AI a grace period to stop moving. + U.tracking = 0 + + if(!target || !target.can_track(usr)) + to_chat(U, "Target is not near any active cameras.") + U.cameraFollow = null + return + + to_chat(U, "Now tracking [target.get_visible_name()] on camera.") + + var/cameraticks = 0 + spawn(0) + while(U.cameraFollow == target) + if(U.cameraFollow == null) + return + + if(!target.can_track(usr)) + U.tracking = 1 + if(!cameraticks) + to_chat(U, "Target is not near any active cameras. Attempting to reacquire...") + cameraticks++ + if(cameraticks > 9) + U.cameraFollow = null + to_chat(U, "Unable to reacquire, cancelling track...") + U.tracking = 0 + return + else + sleep(10) + continue + + else + cameraticks = 0 + U.tracking = 0 + + if(U.eyeobj) + U.eyeobj.setLoc(get_turf(target)) + + else + view_core() + U.cameraFollow = null + return + + sleep(10) + +/proc/near_camera(mob/living/M) + if(!isturf(M.loc)) + return 0 + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(!(R.camera && R.camera.can_use()) && !GLOB.cameranet.checkCameraVis(M)) + return 0 + else if(!GLOB.cameranet.checkCameraVis(M)) + return 0 + return 1 + +/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + if(!src.can_use()) + return + user.eyeobj.setLoc(get_turf(src)) + + +/mob/living/silicon/ai/attack_ai(mob/user) + ai_camera_list() + +/proc/camera_sort(list/L) + var/obj/machinery/camera/a + var/obj/machinery/camera/b + + for(var/i = L.len, i > 0, i--) + for(var/j = 1 to i - 1) + a = L[j] + b = L[j + 1] + if(a.c_tag_order != b.c_tag_order) + if(a.c_tag_order > b.c_tag_order) + L.Swap(j, j + 1) + else + if(sorttext(a.c_tag, b.c_tag) < 0) + L.Swap(j, j + 1) + return L diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index fb87d91e3f04..675d18ebb325 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,137 +1,137 @@ -/obj/machinery/cell_charger - name = "cell charger" - desc = "It charges power cells." - icon = 'icons/obj/power.dmi' - icon_state = "ccharger0" - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 5 - active_power_usage = 60 - power_channel = EQUIP - pass_flags = PASSTABLE - var/obj/item/stock_parts/cell/charging = null - var/chargelevel = -1 - -/obj/machinery/cell_charger/deconstruct() - if(charging) - charging.forceMove(drop_location()) - return ..() - -/obj/machinery/cell_charger/Destroy() - QDEL_NULL(charging) - return ..() - -/obj/machinery/cell_charger/proc/updateicon() - icon_state = "ccharger[charging ? 1 : 0]" - - if(charging && !(stat & (BROKEN|NOPOWER))) - var/newlevel = round(charging.percent() * 4 / 100) - - if(chargelevel != newlevel) - chargelevel = newlevel - - overlays.Cut() - overlays += "ccharger-o[newlevel]" - - else - overlays.Cut() - -/obj/machinery/cell_charger/examine(mob/user) - . = ..() - . += "There's [charging ? "a" : "no"] cell in the charger." - if(charging) - . += "Current charge: [round(charging.percent(), 1)]%" - -/obj/machinery/cell_charger/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stock_parts/cell)) - if(stat & BROKEN) - to_chat(user, "[src] is broken!") - return - if(!anchored) - to_chat(user, "[src] isn't attached to the ground!") - return - if(charging) - to_chat(user, "There is already a cell in the charger!") - return - else - var/area/a = loc.loc // Gets our locations location, like a dream within a dream - if(!isarea(a)) - return - if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - to_chat(user, "[src] blinks red as you try to insert the cell!") - return - if(!user.drop_item()) - return - - I.forceMove(src) - charging = I - user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") - chargelevel = -1 - updateicon() - else - return ..() - -/obj/machinery/cell_charger/wrench_act(mob/user, obj/item/I) - . = TRUE - if(charging) - to_chat(user, "Remove the cell first!") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - anchored = !anchored - if(anchored) - WRENCH_ANCHOR_MESSAGE - else - WRENCH_UNANCHOR_MESSAGE - - -/obj/machinery/cell_charger/proc/removecell() - charging.update_icon() - charging = null - chargelevel = -1 - updateicon() - -/obj/machinery/cell_charger/attack_hand(mob/user) - if(!charging) - return - - user.put_in_hands(charging) - charging.add_fingerprint(user) - - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - - removecell() - -/obj/machinery/cell_charger/attack_tk(mob/user) - if(!charging) - return - - charging.forceMove(loc) - to_chat(user, "You telekinetically remove [charging] from [src].") - - removecell() - -/obj/machinery/cell_charger/attack_ai(mob/user) - return - -/obj/machinery/cell_charger/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - - if(charging) - charging.emp_act(severity) - - ..(severity) - - -/obj/machinery/cell_charger/process() - if(!charging || !anchored || (stat & (BROKEN|NOPOWER))) - return - - if(charging.percent() >= 100) - return - - use_power(200) //this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly! - charging.give(175) //inefficiency. - - updateicon() +/obj/machinery/cell_charger + name = "cell charger" + desc = "It charges power cells." + icon = 'icons/obj/power.dmi' + icon_state = "ccharger0" + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 5 + active_power_usage = 60 + power_channel = EQUIP + pass_flags = PASSTABLE + var/obj/item/stock_parts/cell/charging = null + var/chargelevel = -1 + +/obj/machinery/cell_charger/deconstruct() + if(charging) + charging.forceMove(drop_location()) + return ..() + +/obj/machinery/cell_charger/Destroy() + QDEL_NULL(charging) + return ..() + +/obj/machinery/cell_charger/proc/updateicon() + icon_state = "ccharger[charging ? 1 : 0]" + + if(charging && !(stat & (BROKEN|NOPOWER))) + var/newlevel = round(charging.percent() * 4 / 100) + + if(chargelevel != newlevel) + chargelevel = newlevel + + overlays.Cut() + overlays += "ccharger-o[newlevel]" + + else + overlays.Cut() + +/obj/machinery/cell_charger/examine(mob/user) + . = ..() + . += "There's [charging ? "a" : "no"] cell in the charger." + if(charging) + . += "Current charge: [round(charging.percent(), 1)]%" + +/obj/machinery/cell_charger/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stock_parts/cell)) + if(stat & BROKEN) + to_chat(user, "[src] is broken!") + return + if(!anchored) + to_chat(user, "[src] isn't attached to the ground!") + return + if(charging) + to_chat(user, "There is already a cell in the charger!") + return + else + var/area/a = loc.loc // Gets our locations location, like a dream within a dream + if(!isarea(a)) + return + if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! + to_chat(user, "[src] blinks red as you try to insert the cell!") + return + if(!user.drop_item()) + return + + I.forceMove(src) + charging = I + user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") + chargelevel = -1 + updateicon() + else + return ..() + +/obj/machinery/cell_charger/wrench_act(mob/user, obj/item/I) + . = TRUE + if(charging) + to_chat(user, "Remove the cell first!") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(anchored) + WRENCH_ANCHOR_MESSAGE + else + WRENCH_UNANCHOR_MESSAGE + + +/obj/machinery/cell_charger/proc/removecell() + charging.update_icon() + charging = null + chargelevel = -1 + updateicon() + +/obj/machinery/cell_charger/attack_hand(mob/user) + if(!charging) + return + + user.put_in_hands(charging) + charging.add_fingerprint(user) + + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + + removecell() + +/obj/machinery/cell_charger/attack_tk(mob/user) + if(!charging) + return + + charging.forceMove(loc) + to_chat(user, "You telekinetically remove [charging] from [src].") + + removecell() + +/obj/machinery/cell_charger/attack_ai(mob/user) + return + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + + if(charging) + charging.emp_act(severity) + + ..(severity) + + +/obj/machinery/cell_charger/process() + if(!charging || !anchored || (stat & (BROKEN|NOPOWER))) + return + + if(charging.percent() >= 100) + return + + use_power(200) //this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly! + charging.give(175) //inefficiency. + + updateicon() diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 6d0f03b22b01..3aef616eb98d 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,658 +1,658 @@ -//Cloning revival method. -//The pod handles the actual cloning while the computer manages the clone profiles - -//Potential replacement for genetics revives or something I dunno (?) - -#define CLONE_BIOMASS 150 -#define BIOMASS_MEAT_AMOUNT 50 -#define MINIMUM_HEAL_LEVEL 40 -#define CLONE_INITIAL_DAMAGE 190 -#define BRAIN_INITIAL_DAMAGE 90 // our minds are too feeble for 190 - -/obj/machinery/clonepod - anchored = 1 - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = 1 - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(ACCESS_GENETICS) //For premature unlocking. - var/mob/living/carbon/human/occupant - var/heal_level //The clone is released once its health reaches this level. - var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. - var/mess = 0 //Need to clean out it if it's full of exploded clone. - var/attempting = 0 //One clone attempt at a time thanks - var/biomass = 0 - var/speed_coeff - var/efficiency - - var/datum/mind/clonemind - var/grab_ghost_when = CLONER_MATURE_CLONE - - var/obj/item/radio/Radio - var/radio_announce = 0 - - var/obj/effect/countdown/clonepod/countdown - - var/list/brine_types = list("corazone", "perfluorodecalin", "epinephrine", "salglu_solution") //stops heart attacks, heart failure, shock, and keeps their O2 levels normal - var/list/missing_organs - var/organs_number = 0 - - light_color = LIGHT_COLOR_PURE_GREEN - -/obj/machinery/clonepod/power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/clonepod/biomass - biomass = CLONE_BIOMASS - -/obj/machinery/clonepod/New() - ..() - countdown = new(src) - - Radio = new /obj/item/radio(src) - Radio.listening = 0 - Radio.config(list("Medical" = 0)) - - component_parts = list() - component_parts += new /obj/item/circuitboard/clonepod(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - update_icon() - -/obj/machinery/clonepod/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/clonepod(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - biomass = CLONE_BIOMASS - RefreshParts() - -/obj/machinery/clonepod/Destroy() - if(connected) - connected.pods -= src - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - QDEL_NULL(Radio) - QDEL_NULL(countdown) - QDEL_LIST(missing_organs) - return ..() - -/obj/machinery/clonepod/RefreshParts() - speed_coeff = 0 - efficiency = 0 - for(var/obj/item/stock_parts/scanning_module/S in component_parts) - efficiency += S.rating - for(var/obj/item/stock_parts/manipulator/P in component_parts) - speed_coeff += P.rating - heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) - -//The return of data disks?? Just for transferring between genetics machine/cloning machine. -//TO-DO: Make the genetics machine accept them. -/obj/item/disk/data - name = "Cloning Data Disk" - icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. - var/datum/dna2/record/buf = null - var/read_only = 0 //Well,it's still a floppy disk - -/obj/item/disk/data/proc/initialize() - buf = new - buf.dna=new - -/obj/item/disk/data/Destroy() - QDEL_NULL(buf) - return ..() - -/obj/item/disk/data/demo - name = "data disk - 'God Emperor of Mankind'" - read_only = 1 - -/obj/item/disk/data/demo/New() - ..() - initialize() - buf.types=DNA2_BUF_UE|DNA2_BUF_UI - //data = "066000033000000000AF00330660FF4DB002690" - //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff - buf.dna.real_name="God Emperor of Mankind" - buf.dna.unique_enzymes = md5(buf.dna.real_name) - buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0xAF0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x033,0x066,0x0FF,0x4DB,0x002,0x690,0x000,0x000,0x000,0x328,0x045,0x5FC,0x053,0x035,0x035,0x035) - //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff - if(buf.dna.UI.len != DNA_UI_LENGTH) //If there's a disparity b/w the dna UI string lengths, 0-fill the extra blocks in this UI. - for(var/i in buf.dna.UI.len to DNA_UI_LENGTH) - buf.dna.UI += 0x000 - buf.dna.ResetSE() - buf.dna.UpdateUI() - -/obj/item/disk/data/monkey - name = "data disk - 'Mr. Muggles'" - read_only = 1 - -/obj/item/disk/data/monkey/New() - ..() - initialize() - buf.types=DNA2_BUF_SE - var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) - for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) - new_SE += rand(1,1024) - buf.dna.SE=new_SE - buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) - -//Disk stuff. -/obj/item/disk/data/New() - ..() - var/diskcolor = pick(0,1,2) - icon_state = "datadisk[diskcolor]" - -/obj/item/disk/data/attack_self(mob/user as mob) - read_only = !read_only - to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") - -/obj/item/disk/data/examine(mob/user) - . = ..() - . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." - -//Clonepod - -/obj/machinery/clonepod/examine(mob/user) - . = ..() - if(mess) - . += "It's filled with blood and viscera. You swear you can see it moving..." - if(!occupant || stat & (NOPOWER|BROKEN)) - return - if(occupant && occupant.stat != DEAD) - . += "Current clone cycle is [round(get_completion())]% complete." - -/obj/machinery/clonepod/return_air() //non-reactive air - var/datum/gas_mixture/GM = new - GM.nitrogen = MOLES_O2STANDARD + MOLES_N2STANDARD - GM.temperature = T20C - return GM - -/obj/machinery/clonepod/proc/get_completion() - . = (100 * ((occupant.health + 100) / (heal_level + 100))) - -/obj/machinery/clonepod/attack_ai(mob/user) - return examine(user) - -//Radio Announcement - -/obj/machinery/clonepod/proc/announce_radio_message(message) - if(radio_announce) - Radio.autosay(message, name, "Medical", list(z)) - -/obj/machinery/clonepod/proc/spooky_devil_flavor() - playsound(loc, pick('sound/goonstation/voice/male_scream.ogg', 'sound/goonstation/voice/female_scream.ogg'), 100, 1) - mess = 1 - update_icon() - connected_message("If you keep trying to steal from me, you'll end up with me.") - -//Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(datum/dna2/record/R) - if(mess || attempting || panel_open || stat & (NOPOWER|BROKEN)) - return 0 - clonemind = locate(R.mind) - if(!istype(clonemind)) //not a mind - return 0 - if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body - return 0 - if(clonemind.damnation_type) - spooky_devil_flavor() - return 0 - if(!clonemind.is_revivable()) //Other reasons for being unrevivable - return 0 - if(clonemind.active) //somebody is using that mind - if(ckey(clonemind.key) != R.ckey ) - return 0 - if(clonemind.suicided) // and stay out! - malfunction(go_easy = 0) - return -1 // Flush the record - else - // get_ghost() will fail if they're unable to reenter their body - var/mob/dead/observer/G = clonemind.get_ghost() - if(!G) - return 0 - -/* - if(clonemind.damnation_type) //Can't clone the damned. - playsound('sound/hallucinations/veryfar_noise.ogg', 50, 0) - malfunction() - return -1 // so that the record gets flushed out - */ - - if(biomass >= CLONE_BIOMASS) - biomass -= CLONE_BIOMASS - else - return 0 - - attempting = 1 //One at a time!! - countdown.start() - - if(!R.dna) - R.dna = new /datum/dna() - - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.set_species(R.dna.species.type) - occupant = H - - if(!R.dna.real_name) //to prevent null names - R.dna.real_name = H.real_name - else - H.real_name = R.dna.real_name - - H.dna = R.dna.Clone() - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - domutcheck(H, null, MUTCHK_FORCED) //Ensures species that get powers by the species proc handle_dna keep them - - if(efficiency > 2 && efficiency < 5 && prob(25)) - randmutb(H) - if(efficiency > 5 && prob(20)) - randmutg(H) - if(efficiency < 3 && prob(50)) - randmutb(H) - - H.dna.UpdateSE() - H.dna.UpdateUI() - - H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it - H.UpdateAppearance() - - check_brine() - //Get the clone body ready - maim_clone(H) - H.Paralyse(4) - - if(grab_ghost_when == CLONER_FRESH_CLONE) - clonemind.transfer_to(H) - H.ckey = R.ckey - update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. - to_chat(H, {"Consciousness slowly creeps over you - as your body regenerates.
    So this is what cloning - feels like?
    "}) - else if(grab_ghost_when == CLONER_MATURE_CLONE) - to_chat(clonemind.current, {"Your body is - beginning to regenerate in a cloning pod. You will - become conscious when it is complete."}) - // Set up a soul link with the dead body to catch a revival - soullink(/datum/soullink/soulhook, clonemind.current, src) - - update_icon() - - H.suiciding = 0 - attempting = 0 - return 1 - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/process() - var/show_message = 0 - for(var/obj/item/reagent_containers/food/snacks/meat/meat in range(1, src)) - qdel(meat) - biomass += BIOMASS_MEAT_AMOUNT - show_message = 1 - if(show_message) - visible_message("[src] sucks in and processes the nearby biomass.") - - if(stat & NOPOWER) //Autoeject if power is lost - if(occupant) - go_out() - connected_message("Clone Ejected: Loss of power.") - - else if((occupant) && (occupant.loc == src)) - if((occupant.stat == DEAD) || (occupant.suiciding) || (occupant.mind && !occupant.mind.is_revivable())) //Autoeject corpses and suiciding dudes. - announce_radio_message("The cloning of [occupant] has been aborted due to unrecoverable tissue failure.") - go_out() - connected_message("Clone Rejected: Deceased.") - - else if(occupant.cloneloss > (100 - heal_level)) - occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - occupant.adjustCloneLoss(-((speed_coeff/2))) - - // For human species that lack non-vital parts for some weird reason - if(organs_number) - var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() - progress += (100 - MINIMUM_HEAL_LEVEL) - var/milestone = CLONE_INITIAL_DAMAGE / organs_number -// Doing this as a #define so that the value can change when evaluated multiple times -#define INSTALLED (organs_number - LAZYLEN(missing_organs)) - - while((progress / milestone) > INSTALLED && LAZYLEN(missing_organs)) - var/obj/item/organ/I = pick_n_take(missing_organs) - I.safe_replace(occupant) - -#undef INSTALLED - - //Premature clones may have brain damage. - occupant.adjustBrainLoss(-((speed_coeff/20)*efficiency)) - - check_brine() - - //Also heal some oxyloss ourselves just in case!! - occupant.adjustOxyLoss(-10) - - use_power(7500) //This might need tweaking. - - else if((occupant.cloneloss <= (100 - heal_level))) - connected_message("Cloning Process Complete.") - announce_radio_message("The cloning cycle of [occupant] is complete.") - go_out() - - else if((!occupant) || (occupant.loc != src)) - occupant = null - update_icon() - use_power(200) - -//Let's unlock this early I guess. Might be too early, needs tweaking. -/obj/machinery/clonepod/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - - if(I.GetID()) - if(!check_access(I)) - to_chat(user, "Access Denied.") - return - if(!(occupant || mess)) - to_chat(user, "Error: Pod has no occupant.") - return - else - connected_message("Authorized Ejection") - announce_radio_message("An authorized ejection of [(occupant) ? occupant.real_name : "the malfunctioning pod"] has occured") - to_chat(user, "You force an emergency ejection.") - go_out() - -//Removing cloning pod biomass - else if(istype(I, /obj/item/reagent_containers/food/snacks/meat)) - if(user.drop_item()) - to_chat(user, "[src] processes [I].") - biomass += BIOMASS_MEAT_AMOUNT - qdel(I) - else - return ..() - -/obj/machinery/clonepod/crowbar_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_crowbar(user, I) - -/obj/machinery/clonepod/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!I.multitool_check_buffer(user)) - return - var/obj/item/multitool/M = I - M.set_multitool_buffer(user, src) - -/obj/machinery/clonepod/screwdriver_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", I) - -/obj/machinery/clonepod/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(occupant) - to_chat(user, "Can not do that while [src] is in use.") - return - if(anchored) - WRENCH_UNANCHOR_MESSAGE - anchored = FALSE - connected.pods -= src - connected = null - else - WRENCH_ANCHOR_MESSAGE - anchored = TRUE - -/obj/machinery/clonepod/emag_act(user) - if(isnull(occupant)) - return - go_out() - -/obj/machinery/clonepod/proc/update_clone_antag(var/mob/living/carbon/human/H) - // Check to see if the clone's mind is an antagonist of any kind and handle them accordingly to make sure they get their spells, HUD/whatever else back. - if((H.mind in SSticker.mode:revolutionaries) || (H.mind in SSticker.mode:head_revolutionaries)) - SSticker.mode.update_rev_icons_added() //So the icon actually appears - if(H.mind in SSticker.mode.syndicates) - SSticker.mode.update_synd_icons_added() - if(H.mind in SSticker.mode.cult) - SSticker.mode.add_cultist(occupant.mind) - SSticker.mode.update_cult_icons_added() //So the icon actually appears - SSticker.mode.update_cult_comms_added(H.mind) //So the comms actually appears - if(H.mind.vampire) - H.mind.vampire.update_owner(H) - if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled)) - SSticker.mode.update_vampire_icons_added(H.mind) - if(H.mind in SSticker.mode.changelings) - SSticker.mode.update_change_icons_added(H.mind) - if((H.mind in SSticker.mode.shadowling_thralls) || (H.mind in SSticker.mode.shadows)) - SSticker.mode.update_shadow_icons_added(H.mind) - -//Put messages in the connected computer's temp var for display. -/obj/machinery/clonepod/proc/connected_message(message) - if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) - return 0 - if(!message) - return 0 - - connected.temp = "[name] : [message]" - connected.updateUsrDialog() - return 1 - -/obj/machinery/clonepod/proc/go_out() - countdown.stop() - var/turf/T = get_turf(src) - if(mess) //Clean that mess and dump those gibs! - for(var/i in missing_organs) - var/obj/I = i - I.forceMove(T) - missing_organs.Cut() - mess = FALSE - new /obj/effect/gibspawner/generic(get_turf(src), occupant) - playsound(loc, 'sound/effects/splat.ogg', 50, 1) - update_icon() - return - - if(!occupant) - return - - if(grab_ghost_when == CLONER_MATURE_CLONE) - clonemind.transfer_to(occupant) - occupant.grab_ghost() - update_clone_antag(occupant) - to_chat(occupant, "There is a bright flash!
    \ - You feel like a new being.
    ") - occupant.flash_eyes(visual = 1) - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - - - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - occupant.SetLoseBreath(0) // Stop friggin' dying, gosh damn - occupant.setOxyLoss(0) - for(var/datum/disease/critical/crit in occupant.viruses) - crit.cure() - occupant.forceMove(T) - occupant.update_body() - domutcheck(occupant) //Waiting until they're out before possible notransform. - occupant.special_post_clone_handling() - occupant = null - update_icon() - -/obj/machinery/clonepod/proc/malfunction(go_easy = FALSE) - if(occupant) - connected_message("Critical Error!") - announce_radio_message("Critical error! Please contact a Thinktronic Systems technician, as your warranty may be affected.") - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - if(!go_easy) - if(occupant.mind != clonemind) - clonemind.transfer_to(occupant) - occupant.grab_ghost() // We really just want to make you suffer. - to_chat(occupant, {"Agony blazes across your - consciousness as your body is torn apart.
    - Is this what dying is like? Yes it is.
    "}) - occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - spawn(40) - qdel(occupant) - - - playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, 0) - mess = TRUE - update_icon() - -/obj/machinery/clonepod/update_icon() - ..() - icon_state = "pod_0" - if(occupant && !(stat & NOPOWER)) - icon_state = "pod_1" - else if(mess && !panel_open) - icon_state = "pod_g" - -/obj/machinery/clonepod/relaymove(mob/user) - if(user.stat == CONSCIOUS) - go_out() - -/obj/machinery/clonepod/emp_act(severity) - if(prob(100/(severity*efficiency))) malfunction() - ..() - -/obj/machinery/clonepod/ex_act(severity) - ..() - if(!QDELETED(src) && occupant) - go_out() - -/obj/machinery/clonepod/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - countdown.stop() - -/obj/machinery/clonepod/deconstruct(disassembled = TRUE) - if(occupant) - go_out() - ..() - -/obj/machinery/clonepod/onSoullinkRevive(mob/living/L) - if(occupant && L == clonemind.current) - // The old body's back in shape, time to ditch the cloning one - malfunction(go_easy = TRUE) - -/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) - LAZYINITLIST(missing_organs) - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - - H.setCloneLoss(CLONE_INITIAL_DAMAGE, FALSE) - H.setBrainLoss(BRAIN_INITIAL_DAMAGE) - - for(var/o in H.internal_organs) - var/obj/item/organ/O = o - if(!istype(O) || O.vital) - continue - - // Let's non-specially remove all non-vital organs - // What could possibly go wrong - var/obj/item/I = O.remove(H) - // Make this support stuff that turns into items when removed - I.forceMove(src) - missing_organs += I - - var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") - for(var/zone in zones) - var/obj/item/organ/external/E = H.get_organ(zone) - var/obj/item/I = E.remove(H) - I.forceMove(src) - missing_organs += I - - organs_number = LAZYLEN(missing_organs) - H.updatehealth() - -/obj/machinery/clonepod/proc/check_brine() - // Clones are in a pickled bath of mild chemicals, keeping - // them alive, despite their lack of internal organs - for(var/bt in brine_types) - if(occupant.reagents.get_reagent_amount(bt) < 1) - occupant.reagents.add_reagent(bt, 1) - -/* - * Diskette Box - */ - -/obj/item/storage/box/disks - name = "Diskette Box" - icon_state = "disk_kit" - -/obj/item/storage/box/disks/New() - ..() - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/paper/Cloning - name = "paper - 'H-87 Cloning Apparatus Manual" - info = {"

    Getting Started

    - Congratulations, your station has purchased the H-87 industrial cloning device!
    - Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    - That's all there is to it!
    - Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    -

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. - Profile Deletion has been restricted to \[Station Head\] level access.

    -

    Cloning from a profile

    - Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    - Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    -
    -

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. - The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    - Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    -

    Profile Management

    -

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. - These diskettes are used to transfer genetic information between machines and profiles. - A load/save dialog will become available in each profile if a disk is inserted.


    - A good diskette is a great way to counter aforementioned genetic drift!
    -
    - This technology produced under license from Thinktronic Systems, LTD."} - -//SOME SCRAPS I GUESS -/* EMP grenade/spell effect - if(istype(A, /obj/machinery/clonepod)) - A:malfunction() -*/ - -#undef MINIMUM_HEAL_LEVEL +//Cloning revival method. +//The pod handles the actual cloning while the computer manages the clone profiles + +//Potential replacement for genetics revives or something I dunno (?) + +#define CLONE_BIOMASS 150 +#define BIOMASS_MEAT_AMOUNT 50 +#define MINIMUM_HEAL_LEVEL 40 +#define CLONE_INITIAL_DAMAGE 190 +#define BRAIN_INITIAL_DAMAGE 90 // our minds are too feeble for 190 + +/obj/machinery/clonepod + anchored = 1 + name = "cloning pod" + desc = "An electronically-lockable pod for growing organic tissue." + density = 1 + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_0" + req_access = list(ACCESS_GENETICS) //For premature unlocking. + var/mob/living/carbon/human/occupant + var/heal_level //The clone is released once its health reaches this level. + var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. + var/mess = 0 //Need to clean out it if it's full of exploded clone. + var/attempting = 0 //One clone attempt at a time thanks + var/biomass = 0 + var/speed_coeff + var/efficiency + + var/datum/mind/clonemind + var/grab_ghost_when = CLONER_MATURE_CLONE + + var/obj/item/radio/Radio + var/radio_announce = 0 + + var/obj/effect/countdown/clonepod/countdown + + var/list/brine_types = list("corazone", "perfluorodecalin", "epinephrine", "salglu_solution") //stops heart attacks, heart failure, shock, and keeps their O2 levels normal + var/list/missing_organs + var/organs_number = 0 + + light_color = LIGHT_COLOR_PURE_GREEN + +/obj/machinery/clonepod/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/clonepod/biomass + biomass = CLONE_BIOMASS + +/obj/machinery/clonepod/New() + ..() + countdown = new(src) + + Radio = new /obj/item/radio(src) + Radio.listening = 0 + Radio.config(list("Medical" = 0)) + + component_parts = list() + component_parts += new /obj/item/circuitboard/clonepod(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + update_icon() + +/obj/machinery/clonepod/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/clonepod(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + biomass = CLONE_BIOMASS + RefreshParts() + +/obj/machinery/clonepod/Destroy() + if(connected) + connected.pods -= src + for(var/s in sharedSoulhooks) + var/datum/soullink/S = s + S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed + sharedSoulhooks = null + QDEL_NULL(Radio) + QDEL_NULL(countdown) + QDEL_LIST(missing_organs) + return ..() + +/obj/machinery/clonepod/RefreshParts() + speed_coeff = 0 + efficiency = 0 + for(var/obj/item/stock_parts/scanning_module/S in component_parts) + efficiency += S.rating + for(var/obj/item/stock_parts/manipulator/P in component_parts) + speed_coeff += P.rating + heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) + +//The return of data disks?? Just for transferring between genetics machine/cloning machine. +//TO-DO: Make the genetics machine accept them. +/obj/item/disk/data + name = "Cloning Data Disk" + icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. + var/datum/dna2/record/buf = null + var/read_only = 0 //Well,it's still a floppy disk + +/obj/item/disk/data/proc/initialize() + buf = new + buf.dna=new + +/obj/item/disk/data/Destroy() + QDEL_NULL(buf) + return ..() + +/obj/item/disk/data/demo + name = "data disk - 'God Emperor of Mankind'" + read_only = 1 + +/obj/item/disk/data/demo/New() + ..() + initialize() + buf.types=DNA2_BUF_UE|DNA2_BUF_UI + //data = "066000033000000000AF00330660FF4DB002690" + //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff + buf.dna.real_name="God Emperor of Mankind" + buf.dna.unique_enzymes = md5(buf.dna.real_name) + buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0xAF0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x033,0x066,0x0FF,0x4DB,0x002,0x690,0x000,0x000,0x000,0x328,0x045,0x5FC,0x053,0x035,0x035,0x035) + //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff + if(buf.dna.UI.len != DNA_UI_LENGTH) //If there's a disparity b/w the dna UI string lengths, 0-fill the extra blocks in this UI. + for(var/i in buf.dna.UI.len to DNA_UI_LENGTH) + buf.dna.UI += 0x000 + buf.dna.ResetSE() + buf.dna.UpdateUI() + +/obj/item/disk/data/monkey + name = "data disk - 'Mr. Muggles'" + read_only = 1 + +/obj/item/disk/data/monkey/New() + ..() + initialize() + buf.types=DNA2_BUF_SE + var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) + for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) + new_SE += rand(1,1024) + buf.dna.SE=new_SE + buf.dna.SetSEValueRange(GLOB.monkeyblock,0xDAC, 0xFFF) + +//Disk stuff. +/obj/item/disk/data/New() + ..() + var/diskcolor = pick(0,1,2) + icon_state = "datadisk[diskcolor]" + +/obj/item/disk/data/attack_self(mob/user as mob) + read_only = !read_only + to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") + +/obj/item/disk/data/examine(mob/user) + . = ..() + . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." + +//Clonepod + +/obj/machinery/clonepod/examine(mob/user) + . = ..() + if(mess) + . += "It's filled with blood and viscera. You swear you can see it moving..." + if(!occupant || stat & (NOPOWER|BROKEN)) + return + if(occupant && occupant.stat != DEAD) + . += "Current clone cycle is [round(get_completion())]% complete." + +/obj/machinery/clonepod/return_air() //non-reactive air + var/datum/gas_mixture/GM = new + GM.nitrogen = MOLES_O2STANDARD + MOLES_N2STANDARD + GM.temperature = T20C + return GM + +/obj/machinery/clonepod/proc/get_completion() + . = (100 * ((occupant.health + 100) / (heal_level + 100))) + +/obj/machinery/clonepod/attack_ai(mob/user) + return examine(user) + +//Radio Announcement + +/obj/machinery/clonepod/proc/announce_radio_message(message) + if(radio_announce) + Radio.autosay(message, name, "Medical", list(z)) + +/obj/machinery/clonepod/proc/spooky_devil_flavor() + playsound(loc, pick('sound/goonstation/voice/male_scream.ogg', 'sound/goonstation/voice/female_scream.ogg'), 100, 1) + mess = 1 + update_icon() + connected_message("If you keep trying to steal from me, you'll end up with me.") + +//Start growing a human clone in the pod! +/obj/machinery/clonepod/proc/growclone(datum/dna2/record/R) + if(mess || attempting || panel_open || stat & (NOPOWER|BROKEN)) + return 0 + clonemind = locate(R.mind) + if(!istype(clonemind)) //not a mind + return 0 + if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body + return 0 + if(clonemind.damnation_type) + spooky_devil_flavor() + return 0 + if(!clonemind.is_revivable()) //Other reasons for being unrevivable + return 0 + if(clonemind.active) //somebody is using that mind + if(ckey(clonemind.key) != R.ckey ) + return 0 + if(clonemind.suicided) // and stay out! + malfunction(go_easy = 0) + return -1 // Flush the record + else + // get_ghost() will fail if they're unable to reenter their body + var/mob/dead/observer/G = clonemind.get_ghost() + if(!G) + return 0 + +/* + if(clonemind.damnation_type) //Can't clone the damned. + playsound('sound/hallucinations/veryfar_noise.ogg', 50, 0) + malfunction() + return -1 // so that the record gets flushed out + */ + + if(biomass >= CLONE_BIOMASS) + biomass -= CLONE_BIOMASS + else + return 0 + + attempting = 1 //One at a time!! + countdown.start() + + if(!R.dna) + R.dna = new /datum/dna() + + var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) + H.set_species(R.dna.species.type) + occupant = H + + if(!R.dna.real_name) //to prevent null names + R.dna.real_name = H.real_name + else + H.real_name = R.dna.real_name + + H.dna = R.dna.Clone() + + for(var/datum/language/L in R.languages) + H.add_language(L.name) + + domutcheck(H, null, MUTCHK_FORCED) //Ensures species that get powers by the species proc handle_dna keep them + + if(efficiency > 2 && efficiency < 5 && prob(25)) + randmutb(H) + if(efficiency > 5 && prob(20)) + randmutg(H) + if(efficiency < 3 && prob(50)) + randmutb(H) + + H.dna.UpdateSE() + H.dna.UpdateUI() + + H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it + H.UpdateAppearance() + + check_brine() + //Get the clone body ready + maim_clone(H) + H.Paralyse(4) + + if(grab_ghost_when == CLONER_FRESH_CLONE) + clonemind.transfer_to(H) + H.ckey = R.ckey + update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. + to_chat(H, {"Consciousness slowly creeps over you + as your body regenerates.
    So this is what cloning + feels like?
    "}) + else if(grab_ghost_when == CLONER_MATURE_CLONE) + to_chat(clonemind.current, {"Your body is + beginning to regenerate in a cloning pod. You will + become conscious when it is complete."}) + // Set up a soul link with the dead body to catch a revival + soullink(/datum/soullink/soulhook, clonemind.current, src) + + update_icon() + + H.suiciding = 0 + attempting = 0 + return 1 + +//Grow clones to maturity then kick them out. FREELOADERS +/obj/machinery/clonepod/process() + var/show_message = 0 + for(var/obj/item/reagent_containers/food/snacks/meat/meat in range(1, src)) + qdel(meat) + biomass += BIOMASS_MEAT_AMOUNT + show_message = 1 + if(show_message) + visible_message("[src] sucks in and processes the nearby biomass.") + + if(stat & NOPOWER) //Autoeject if power is lost + if(occupant) + go_out() + connected_message("Clone Ejected: Loss of power.") + + else if((occupant) && (occupant.loc == src)) + if((occupant.stat == DEAD) || (occupant.suiciding) || (occupant.mind && !occupant.mind.is_revivable())) //Autoeject corpses and suiciding dudes. + announce_radio_message("The cloning of [occupant] has been aborted due to unrecoverable tissue failure.") + go_out() + connected_message("Clone Rejected: Deceased.") + + else if(occupant.cloneloss > (100 - heal_level)) + occupant.Paralyse(4) + + //Slowly get that clone healed and finished. + occupant.adjustCloneLoss(-((speed_coeff/2))) + + // For human species that lack non-vital parts for some weird reason + if(organs_number) + var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() + progress += (100 - MINIMUM_HEAL_LEVEL) + var/milestone = CLONE_INITIAL_DAMAGE / organs_number +// Doing this as a #define so that the value can change when evaluated multiple times +#define INSTALLED (organs_number - LAZYLEN(missing_organs)) + + while((progress / milestone) > INSTALLED && LAZYLEN(missing_organs)) + var/obj/item/organ/I = pick_n_take(missing_organs) + I.safe_replace(occupant) + +#undef INSTALLED + + //Premature clones may have brain damage. + occupant.adjustBrainLoss(-((speed_coeff/20)*efficiency)) + + check_brine() + + //Also heal some oxyloss ourselves just in case!! + occupant.adjustOxyLoss(-10) + + use_power(7500) //This might need tweaking. + + else if((occupant.cloneloss <= (100 - heal_level))) + connected_message("Cloning Process Complete.") + announce_radio_message("The cloning cycle of [occupant] is complete.") + go_out() + + else if((!occupant) || (occupant.loc != src)) + occupant = null + update_icon() + use_power(200) + +//Let's unlock this early I guess. Might be too early, needs tweaking. +/obj/machinery/clonepod/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + + if(I.GetID()) + if(!check_access(I)) + to_chat(user, "Access Denied.") + return + if(!(occupant || mess)) + to_chat(user, "Error: Pod has no occupant.") + return + else + connected_message("Authorized Ejection") + announce_radio_message("An authorized ejection of [(occupant) ? occupant.real_name : "the malfunctioning pod"] has occured") + to_chat(user, "You force an emergency ejection.") + go_out() + +//Removing cloning pod biomass + else if(istype(I, /obj/item/reagent_containers/food/snacks/meat)) + if(user.drop_item()) + to_chat(user, "[src] processes [I].") + biomass += BIOMASS_MEAT_AMOUNT + qdel(I) + else + return ..() + +/obj/machinery/clonepod/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/clonepod/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!I.multitool_check_buffer(user)) + return + var/obj/item/multitool/M = I + M.set_multitool_buffer(user, src) + +/obj/machinery/clonepod/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", I) + +/obj/machinery/clonepod/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(occupant) + to_chat(user, "Can not do that while [src] is in use.") + return + if(anchored) + WRENCH_UNANCHOR_MESSAGE + anchored = FALSE + connected.pods -= src + connected = null + else + WRENCH_ANCHOR_MESSAGE + anchored = TRUE + +/obj/machinery/clonepod/emag_act(user) + if(isnull(occupant)) + return + go_out() + +/obj/machinery/clonepod/proc/update_clone_antag(var/mob/living/carbon/human/H) + // Check to see if the clone's mind is an antagonist of any kind and handle them accordingly to make sure they get their spells, HUD/whatever else back. + if((H.mind in SSticker.mode:revolutionaries) || (H.mind in SSticker.mode:head_revolutionaries)) + SSticker.mode.update_rev_icons_added() //So the icon actually appears + if(H.mind in SSticker.mode.syndicates) + SSticker.mode.update_synd_icons_added() + if(H.mind in SSticker.mode.cult) + SSticker.mode.add_cultist(occupant.mind) + SSticker.mode.update_cult_icons_added() //So the icon actually appears + SSticker.mode.update_cult_comms_added(H.mind) //So the comms actually appears + if(H.mind.vampire) + H.mind.vampire.update_owner(H) + if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled)) + SSticker.mode.update_vampire_icons_added(H.mind) + if(H.mind in SSticker.mode.changelings) + SSticker.mode.update_change_icons_added(H.mind) + if((H.mind in SSticker.mode.shadowling_thralls) || (H.mind in SSticker.mode.shadows)) + SSticker.mode.update_shadow_icons_added(H.mind) + +//Put messages in the connected computer's temp var for display. +/obj/machinery/clonepod/proc/connected_message(message) + if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) + return 0 + if(!message) + return 0 + + connected.temp = "[name] : [message]" + connected.updateUsrDialog() + return 1 + +/obj/machinery/clonepod/proc/go_out() + countdown.stop() + var/turf/T = get_turf(src) + if(mess) //Clean that mess and dump those gibs! + for(var/i in missing_organs) + var/obj/I = i + I.forceMove(T) + missing_organs.Cut() + mess = FALSE + new /obj/effect/gibspawner/generic(get_turf(src), occupant) + playsound(loc, 'sound/effects/splat.ogg', 50, 1) + update_icon() + return + + if(!occupant) + return + + if(grab_ghost_when == CLONER_MATURE_CLONE) + clonemind.transfer_to(occupant) + occupant.grab_ghost() + update_clone_antag(occupant) + to_chat(occupant, "There is a bright flash!
    \ + You feel like a new being.
    ") + occupant.flash_eyes(visual = 1) + for(var/s in sharedSoulhooks) + var/datum/soullink/S = s + S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed + sharedSoulhooks = null + + + for(var/i in missing_organs) + qdel(i) + missing_organs.Cut() + occupant.SetLoseBreath(0) // Stop friggin' dying, gosh damn + occupant.setOxyLoss(0) + for(var/datum/disease/critical/crit in occupant.viruses) + crit.cure() + occupant.forceMove(T) + occupant.update_body() + domutcheck(occupant) //Waiting until they're out before possible notransform. + occupant.special_post_clone_handling() + occupant = null + update_icon() + +/obj/machinery/clonepod/proc/malfunction(go_easy = FALSE) + if(occupant) + connected_message("Critical Error!") + announce_radio_message("Critical error! Please contact a Thinktronic Systems technician, as your warranty may be affected.") + for(var/s in sharedSoulhooks) + var/datum/soullink/S = s + S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed + sharedSoulhooks = null + if(!go_easy) + if(occupant.mind != clonemind) + clonemind.transfer_to(occupant) + occupant.grab_ghost() // We really just want to make you suffer. + to_chat(occupant, {"Agony blazes across your + consciousness as your body is torn apart.
    + Is this what dying is like? Yes it is.
    "}) + occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) + for(var/i in missing_organs) + qdel(i) + missing_organs.Cut() + spawn(40) + qdel(occupant) + + + playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, 0) + mess = TRUE + update_icon() + +/obj/machinery/clonepod/update_icon() + ..() + icon_state = "pod_0" + if(occupant && !(stat & NOPOWER)) + icon_state = "pod_1" + else if(mess && !panel_open) + icon_state = "pod_g" + +/obj/machinery/clonepod/relaymove(mob/user) + if(user.stat == CONSCIOUS) + go_out() + +/obj/machinery/clonepod/emp_act(severity) + if(prob(100/(severity*efficiency))) malfunction() + ..() + +/obj/machinery/clonepod/ex_act(severity) + ..() + if(!QDELETED(src) && occupant) + go_out() + +/obj/machinery/clonepod/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + countdown.stop() + +/obj/machinery/clonepod/deconstruct(disassembled = TRUE) + if(occupant) + go_out() + ..() + +/obj/machinery/clonepod/onSoullinkRevive(mob/living/L) + if(occupant && L == clonemind.current) + // The old body's back in shape, time to ditch the cloning one + malfunction(go_easy = TRUE) + +/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) + LAZYINITLIST(missing_organs) + for(var/i in missing_organs) + qdel(i) + missing_organs.Cut() + + H.setCloneLoss(CLONE_INITIAL_DAMAGE, FALSE) + H.setBrainLoss(BRAIN_INITIAL_DAMAGE) + + for(var/o in H.internal_organs) + var/obj/item/organ/O = o + if(!istype(O) || O.vital) + continue + + // Let's non-specially remove all non-vital organs + // What could possibly go wrong + var/obj/item/I = O.remove(H) + // Make this support stuff that turns into items when removed + I.forceMove(src) + missing_organs += I + + var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") + for(var/zone in zones) + var/obj/item/organ/external/E = H.get_organ(zone) + var/obj/item/I = E.remove(H) + I.forceMove(src) + missing_organs += I + + organs_number = LAZYLEN(missing_organs) + H.updatehealth() + +/obj/machinery/clonepod/proc/check_brine() + // Clones are in a pickled bath of mild chemicals, keeping + // them alive, despite their lack of internal organs + for(var/bt in brine_types) + if(occupant.reagents.get_reagent_amount(bt) < 1) + occupant.reagents.add_reagent(bt, 1) + +/* + * Diskette Box + */ + +/obj/item/storage/box/disks + name = "Diskette Box" + icon_state = "disk_kit" + +/obj/item/storage/box/disks/New() + ..() + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + new /obj/item/disk/data(src) + +/* + * Manual -- A big ol' manual. + */ + +/obj/item/paper/Cloning + name = "paper - 'H-87 Cloning Apparatus Manual" + info = {"

    Getting Started

    + Congratulations, your station has purchased the H-87 industrial cloning device!
    + Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!
    + That's all there is to it!
    + Notice, cloning system cannot scan inorganic life or small primates. Scan may fail if subject has suffered extreme brain damage.
    +

    Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile. + Profile Deletion has been restricted to \[Station Head\] level access.

    +

    Cloning from a profile

    + Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.
    + Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.
    +
    +

    The provided CLONEPOD SYSTEM will produce the desired clone. Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds. + The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.


    + Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.
    +

    Profile Management

    +

    The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES. + These diskettes are used to transfer genetic information between machines and profiles. + A load/save dialog will become available in each profile if a disk is inserted.


    + A good diskette is a great way to counter aforementioned genetic drift!
    +
    + This technology produced under license from Thinktronic Systems, LTD."} + +//SOME SCRAPS I GUESS +/* EMP grenade/spell effect + if(istype(A, /obj/machinery/clonepod)) + A:malfunction() +*/ + +#undef MINIMUM_HEAL_LEVEL diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index bf0195930657..21fddce64b73 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -1,188 +1,188 @@ -#define OP_COMPUTER_COOLDOWN 60 - -/obj/machinery/computer/operating - name = "operating computer" - density = 1 - anchored = 1.0 - icon_keyboard = "med_key" - icon_screen = "crew" - circuit = /obj/item/circuitboard/operating - var/obj/machinery/optable/table = null - var/mob/living/carbon/human/victim = null - light_color = LIGHT_COLOR_PURE_BLUE - var/verbose = 1 //general speaker toggle - var/patientName = null - var/oxyAlarm = 30 //oxy damage at which the computer will beep - var/choice = 0 //just for going into and out of the options menu - var/healthAnnounce = 1 //healther announcer toggle - var/crit = 1 //crit beeping toggle - var/nextTick = OP_COMPUTER_COOLDOWN - var/healthAlarm = 50 - var/oxy = 1 //oxygen beeping toggle - -/obj/machinery/computer/operating/New() - ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - table = locate(/obj/machinery/optable, get_step(src, dir)) - if(table) - table.computer = src - break - -/obj/machinery/computer/operating/Destroy() - if(table) - table.computer = null - table = null - if(victim) - victim = null - return ..() - -/obj/machinery/computer/operating/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - if(..(user)) - return - - if(stat & (NOPOWER|BROKEN)) - return - - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/mob/living/carbon/human/occupant - if(table) - occupant = table.victim - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.dna.species) - var/datum/species/sp = occupant.dna.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(istype(occupant, /mob/living/simple_animal)) - var/mob/living/simple_animal/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.blood_type - if(occupant.surgeries.len) - occupantData["inSurgery"] = 1 - for(var/datum/surgery/procedure in occupant.surgeries) - occupantData["surgeryName"] = "[capitalize(procedure.name)]" - var/datum/surgery_step/surgery_step = procedure.get_surgery_step() - occupantData["stepName"] = "[capitalize(surgery_step.name)]" - - data["occupant"] = occupantData - data["verbose"]=verbose - data["oxyAlarm"]=oxyAlarm - data["choice"]=choice - data["health"]=healthAnnounce - data["crit"]=crit - data["healthAlarm"]=healthAlarm - data["oxy"]=oxy - - return data - - -/obj/machinery/computer/operating/Topic(href, href_list) - if(..()) - return 1 - if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if(href_list["verboseOn"]) - verbose=1 - if(href_list["verboseOff"]) - verbose=0 - if(href_list["healthOn"]) - healthAnnounce=1 - if(href_list["healthOff"]) - healthAnnounce=0 - if(href_list["critOn"]) - crit=1 - if(href_list["critOff"]) - crit=0 - if(href_list["oxyOn"]) - oxy=1 - if(href_list["oxyOff"]) - oxy=0 - if(href_list["oxy_adj"]!=0) - oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) - if(href_list["choiceOn"]) - choice=1 - if(href_list["choiceOff"]) - choice=0 - if(href_list["health_adj"]!=0) - healthAlarm=healthAlarm+text2num(href_list["health_adj"]) - return - - -/obj/machinery/computer/operating/process() - - if(table && table.check_victim()) - if(verbose) - if(patientName!=table.victim.name) - patientName=table.victim.name - atom_say("New patient detected, loading stats") - victim = table.victim - atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") - if(nextTick < world.time) - nextTick=world.time + OP_COMPUTER_COOLDOWN - if(crit && victim.health <= -50 ) - playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) - if(oxy && victim.getOxyLoss()>oxyAlarm) - playsound(src.loc, 'sound/machines/defib_saftyoff.ogg', 50, 0) - if(healthAnnounce && victim.health <= healthAlarm) - atom_say("[round(victim.health)]") +#define OP_COMPUTER_COOLDOWN 60 + +/obj/machinery/computer/operating + name = "operating computer" + density = 1 + anchored = 1.0 + icon_keyboard = "med_key" + icon_screen = "crew" + circuit = /obj/item/circuitboard/operating + var/obj/machinery/optable/table = null + var/mob/living/carbon/human/victim = null + light_color = LIGHT_COLOR_PURE_BLUE + var/verbose = 1 //general speaker toggle + var/patientName = null + var/oxyAlarm = 30 //oxy damage at which the computer will beep + var/choice = 0 //just for going into and out of the options menu + var/healthAnnounce = 1 //healther announcer toggle + var/crit = 1 //crit beeping toggle + var/nextTick = OP_COMPUTER_COOLDOWN + var/healthAlarm = 50 + var/oxy = 1 //oxygen beeping toggle + +/obj/machinery/computer/operating/New() + ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + table = locate(/obj/machinery/optable, get_step(src, dir)) + if(table) + table.computer = src + break + +/obj/machinery/computer/operating/Destroy() + if(table) + table.computer = null + table = null + if(victim) + victim = null + return ..() + +/obj/machinery/computer/operating/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + + +/obj/machinery/computer/operating/attack_hand(mob/user) + if(..(user)) + return + + if(stat & (NOPOWER|BROKEN)) + return + + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/mob/living/carbon/human/occupant + if(table) + occupant = table.victim + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.dna.species) + var/datum/species/sp = occupant.dna.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.blood_volume) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.blood_type + if(occupant.surgeries.len) + occupantData["inSurgery"] = 1 + for(var/datum/surgery/procedure in occupant.surgeries) + occupantData["surgeryName"] = "[capitalize(procedure.name)]" + var/datum/surgery_step/surgery_step = procedure.get_surgery_step() + occupantData["stepName"] = "[capitalize(surgery_step.name)]" + + data["occupant"] = occupantData + data["verbose"]=verbose + data["oxyAlarm"]=oxyAlarm + data["choice"]=choice + data["health"]=healthAnnounce + data["crit"]=crit + data["healthAlarm"]=healthAlarm + data["oxy"]=oxy + + return data + + +/obj/machinery/computer/operating/Topic(href, href_list) + if(..()) + return 1 + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["verboseOn"]) + verbose=1 + if(href_list["verboseOff"]) + verbose=0 + if(href_list["healthOn"]) + healthAnnounce=1 + if(href_list["healthOff"]) + healthAnnounce=0 + if(href_list["critOn"]) + crit=1 + if(href_list["critOff"]) + crit=0 + if(href_list["oxyOn"]) + oxy=1 + if(href_list["oxyOff"]) + oxy=0 + if(href_list["oxy_adj"]!=0) + oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) + if(href_list["choiceOn"]) + choice=1 + if(href_list["choiceOff"]) + choice=0 + if(href_list["health_adj"]!=0) + healthAlarm=healthAlarm+text2num(href_list["health_adj"]) + return + + +/obj/machinery/computer/operating/process() + + if(table && table.check_victim()) + if(verbose) + if(patientName!=table.victim.name) + patientName=table.victim.name + atom_say("New patient detected, loading stats") + victim = table.victim + atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + if(nextTick < world.time) + nextTick=world.time + OP_COMPUTER_COOLDOWN + if(crit && victim.health <= -50 ) + playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) + if(oxy && victim.getOxyLoss()>oxyAlarm) + playsound(src.loc, 'sound/machines/defib_saftyoff.ogg', 50, 0) + if(healthAnnounce && victim.health <= healthAlarm) + atom_say("[round(victim.health)]") diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index 8d27ac5e7fa2..ba139e50855e 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -158,7 +158,7 @@ var/open_for_latejoin = alert(user, "Would you like this core to be open for latejoining AIs?", "Latejoin", "Yes", "Yes", "No") == "Yes" var/obj/structure/AIcore/deactivated/D = new(loc) if(open_for_latejoin) - empty_playable_ai_cores += D + GLOB.empty_playable_ai_cores += D else if(brain.brainmob.mind) SSticker.mode.remove_cultist(brain.brainmob.mind, 1) @@ -248,8 +248,8 @@ circuit = new(src) /obj/structure/AIcore/deactivated/Destroy() - if(src in empty_playable_ai_cores) - empty_playable_ai_cores -= src + if(src in GLOB.empty_playable_ai_cores) + GLOB.empty_playable_ai_cores -= src return ..() /client/proc/empty_ai_core_toggle_latejoin() @@ -269,11 +269,11 @@ var/obj/structure/AIcore/deactivated/D = cores[id] if(!D) return - if(D in empty_playable_ai_cores) - empty_playable_ai_cores -= D + if(D in GLOB.empty_playable_ai_cores) + GLOB.empty_playable_ai_cores -= D to_chat(src, "\The [id] is now not available for latejoining AIs.") else - empty_playable_ai_cores += D + GLOB.empty_playable_ai_cores += D to_chat(src, "\The [id] is now available for latejoining AIs.") @@ -305,4 +305,4 @@ atom/proc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/ to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.
    ") qdel(src) else //If for some reason you use an empty card on an empty AI terminal. - to_chat(user, "There is no AI loaded on this terminal!") \ No newline at end of file + to_chat(user, "There is no AI loaded on this terminal!") diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index c7482bc140dc..0d715fe57591 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,147 +1,147 @@ -/obj/machinery/computer/aifixer - name = "\improper AI system integrity restorer" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "rd_key" - icon_screen = "ai-fixer" - circuit = /obj/item/circuitboard/aifixer - req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) - var/mob/living/silicon/ai/occupant = null - var/active = 0 - - light_color = LIGHT_COLOR_PURPLE - -/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) - if(occupant && istype(I, /obj/item/screwdriver)) - if(stat & BROKEN) - ..() - if(stat & NOPOWER) - to_chat(user, "The screws on [name]'s screen won't budge.") - else - to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep!.") - else - return ..() - -/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - - if(!ui) - ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) - var/data[0] - if(occupant) - data["occupant"] = occupant.name - data["reference"] = "\ref[occupant]" - data["integrity"] = (occupant.health+100)/2 - data["stat"] = occupant.stat - data["active"] = active - data["wireless"] = occupant.control_disabled - data["radio"] = occupant.aiRadio.disabledAi - - var/laws[0] - for(var/datum/ai_law/law in occupant.laws.all_laws()) - laws.Add(list(list("law" = law.law, "number" = law.get_index()))) - - data["laws"] = laws - - return data - -/obj/machinery/computer/aifixer/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["fix"]) - active = 1 - while(occupant.health < 100) - occupant.adjustOxyLoss(-1, FALSE) - occupant.adjustFireLoss(-1, FALSE) - occupant.adjustToxLoss(-1, FALSE) - occupant.adjustBruteLoss(-1, FALSE) - occupant.updatehealth() - if(occupant.health >= 0 && occupant.stat == DEAD) - occupant.update_revive() - occupant.lying = 0 - update_icon() - sleep(10) - active = 0 - add_fingerprint(usr) - - if(href_list["wireless"]) - var/wireless = text2num(href_list["wireless"]) - if(wireless == 0 || wireless == 1) - occupant.control_disabled = wireless - - if(href_list["radio"]) - var/radio = text2num(href_list["radio"]) - if(radio == 0 || radio == 1) - occupant.aiRadio.disabledAi = radio - - SSnanoui.update_uis(src) - update_icon() - return - -/obj/machinery/computer/aifixer/update_icon() - ..() - if(stat & (NOPOWER|BROKEN)) - return - else - var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays - if(active) - overlays += image(icon,"ai-fixer-on",overlay_layer) - if(occupant) - switch(occupant.stat) - if(0) - overlays += image(icon,"ai-fixer-full",overlay_layer) - if(2) - overlays += image(icon,"ai-fixer-404",overlay_layer) - else - overlays += image(icon,"ai-fixer-empty",overlay_layer) - -/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/aicard/card) - if(!..()) - return - //Downloading AI from card to terminal. - if(interaction == AI_TRANS_FROM_CARD) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "[src] is offline and cannot take an AI at this time!") - return - AI.loc = src - occupant = AI - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - to_chat(AI, "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - update_icon() - - else //Uploading AI from terminal to card - if(occupant && !active) - to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") - to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") - occupant.loc = card - occupant = null - update_icon() - else if(active) - to_chat(user, "ERROR: Reconstruction in progress.") - else if(!occupant) - to_chat(user, "ERROR: Unable to locate artificial intelligence.") - -/obj/machinery/computer/aifixer/Destroy() - if(occupant) - occupant.ghostize() - QDEL_NULL(occupant) - return ..() - -/obj/machinery/computer/aifixer/emp_act() - if(occupant) - occupant.ghostize() - QDEL_NULL(occupant) - else - ..() +/obj/machinery/computer/aifixer + name = "\improper AI system integrity restorer" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "rd_key" + icon_screen = "ai-fixer" + circuit = /obj/item/circuitboard/aifixer + req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) + var/mob/living/silicon/ai/occupant = null + var/active = 0 + + light_color = LIGHT_COLOR_PURPLE + +/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) + if(occupant && istype(I, /obj/item/screwdriver)) + if(stat & BROKEN) + ..() + if(stat & NOPOWER) + to_chat(user, "The screws on [name]'s screen won't budge.") + else + to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep!.") + else + return ..() + +/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + + if(!ui) + ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) + var/data[0] + if(occupant) + data["occupant"] = occupant.name + data["reference"] = "\ref[occupant]" + data["integrity"] = (occupant.health+100)/2 + data["stat"] = occupant.stat + data["active"] = active + data["wireless"] = occupant.control_disabled + data["radio"] = occupant.aiRadio.disabledAi + + var/laws[0] + for(var/datum/ai_law/law in occupant.laws.all_laws()) + laws.Add(list(list("law" = law.law, "number" = law.get_index()))) + + data["laws"] = laws + + return data + +/obj/machinery/computer/aifixer/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["fix"]) + active = 1 + while(occupant.health < 100) + occupant.adjustOxyLoss(-1, FALSE) + occupant.adjustFireLoss(-1, FALSE) + occupant.adjustToxLoss(-1, FALSE) + occupant.adjustBruteLoss(-1, FALSE) + occupant.updatehealth() + if(occupant.health >= 0 && occupant.stat == DEAD) + occupant.update_revive() + occupant.lying = 0 + update_icon() + sleep(10) + active = 0 + add_fingerprint(usr) + + if(href_list["wireless"]) + var/wireless = text2num(href_list["wireless"]) + if(wireless == 0 || wireless == 1) + occupant.control_disabled = wireless + + if(href_list["radio"]) + var/radio = text2num(href_list["radio"]) + if(radio == 0 || radio == 1) + occupant.aiRadio.disabledAi = radio + + SSnanoui.update_uis(src) + update_icon() + return + +/obj/machinery/computer/aifixer/update_icon() + ..() + if(stat & (NOPOWER|BROKEN)) + return + else + var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays + if(active) + overlays += image(icon,"ai-fixer-on",overlay_layer) + if(occupant) + switch(occupant.stat) + if(0) + overlays += image(icon,"ai-fixer-full",overlay_layer) + if(2) + overlays += image(icon,"ai-fixer-404",overlay_layer) + else + overlays += image(icon,"ai-fixer-empty",overlay_layer) + +/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/aicard/card) + if(!..()) + return + //Downloading AI from card to terminal. + if(interaction == AI_TRANS_FROM_CARD) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "[src] is offline and cannot take an AI at this time!") + return + AI.loc = src + occupant = AI + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + to_chat(AI, "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + update_icon() + + else //Uploading AI from terminal to card + if(occupant && !active) + to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") + to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") + occupant.loc = card + occupant = null + update_icon() + else if(active) + to_chat(user, "ERROR: Reconstruction in progress.") + else if(!occupant) + to_chat(user, "ERROR: Unable to locate artificial intelligence.") + +/obj/machinery/computer/aifixer/Destroy() + if(occupant) + occupant.ghostize() + QDEL_NULL(occupant) + return ..() + +/obj/machinery/computer/aifixer/emp_act() + if(occupant) + occupant.ghostize() + QDEL_NULL(occupant) + else + ..() diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 28f847d81ebb..20ff79041a9b 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -1,1023 +1,1023 @@ -/obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" - icon = 'icons/obj/computer.dmi' - icon_state = "arcade" - icon_keyboard = null - icon_screen = "invaders" - light_color = "#00FF00" - var/prize = /obj/item/stack/tickets - -/obj/machinery/computer/arcade/proc/Reset() - return - -/obj/machinery/computer/arcade/New() - ..() - if(!circuit) - var/choice = pick(subtypesof(/obj/machinery/computer/arcade)) - new choice(loc) - qdel(src) - return - Reset() - - -/obj/machinery/computer/arcade/proc/prizevend(var/score) - if(!contents.len) - var/prize_amount - if(score) - prize_amount = score - else - prize_amount = rand(1, 10) - new prize(get_turf(src), prize_amount) - else - var/atom/movable/prize = pick(contents) - prize.loc = get_turf(src) - -/obj/machinery/computer/arcade/emp_act(severity) - ..(severity) - if(stat & (NOPOWER|BROKEN)) - return - var/num_of_prizes = 0 - switch(severity) - if(1) - num_of_prizes = rand(1,4) - if(2) - num_of_prizes = rand(0,2) - for(var/i = num_of_prizes; i > 0; i--) - prizevend() - explosion(get_turf(src), -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes) - - -/obj/machinery/computer/arcade/battle - name = "arcade machine" - desc = "Does not support Pinball." - icon = 'icons/obj/computer.dmi' - icon_state = "arcade" - circuit = /obj/item/circuitboard/arcade/battle - var/enemy_name = "Space Villian" - var/temp = "Winners Don't Use Spacedrugs" //Temporary message, for attack messages, etc - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = 0 - var/blocked = 0 //Player cannot attack/heal while set - var/turtle = 0 - -/obj/machinery/computer/arcade/battle/Reset() - var/name_action - var/name_part1 - var/name_part2 - - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") - - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") - - enemy_name = replacetext((name_part1 + name_part2), "the ", "") - name = (name_action + name_part1 + name_part2) - -/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - var/dat = "Close" - dat += "

    [enemy_name]

    " - - dat += "

    [temp]

    " - dat += "
    Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]
    " - - if(gameover) - dat += "
    New Game" - else - dat += "
    Attack | " - dat += "Heal | " - dat += "Recharge Power" - - dat += "
    " - - //user << browse(dat, "window=arcade") - //onclose(user, "arcade") - var/datum/browser/popup = new(user, "arcade", "Space Villian 2000") - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - return - -/obj/machinery/computer/arcade/battle/Topic(href, href_list) - if(..()) - return - - if(!blocked && !gameover) - if(href_list["attack"]) - blocked = 1 - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) - updateUsrDialog() - if(turtle > 0) - turtle-- - - sleep(10) - enemy_hp -= attackamt - arcade_action() - - else if(href_list["heal"]) - blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) - updateUsrDialog() - turtle++ - - sleep(10) - player_mp -= pointamt - player_hp += healamt - blocked = 1 - updateUsrDialog() - arcade_action() - - else if(href_list["charge"]) - blocked = 1 - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - playsound(src.loc, 'sound/arcade/mana.ogg', 20, 1, extrarange = -6, falloff = 10) - player_mp += chargeamt - if(turtle > 0) - turtle-- - - updateUsrDialog() - sleep(10) - arcade_action() - - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - else if(href_list["newgame"]) //Reset everything - temp = "New Round" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - turtle = 0 - - if(emagged) - Reset() - emagged = 0 - - add_fingerprint(usr) - updateUsrDialog() - return - -/obj/machinery/computer/arcade/battle/proc/arcade_action() - if((enemy_mp <= 0) || (enemy_hp <= 0)) - if(!gameover) - gameover = 1 - temp = "[enemy_name] has fallen! Rejoice!" - playsound(src.loc, 'sound/arcade/win.ogg', 20, 1, extrarange = -6, falloff = 10) - - if(emagged) - feedback_inc("arcade_win_emagged") - new /obj/effect/spawner/newbomb/timer/syndicate(get_turf(src)) - new /obj/item/clothing/head/collectable/petehat(get_turf(src)) - message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.") - Reset() - emagged = 0 - else - feedback_inc("arcade_win_normal") - var/score = player_hp + player_mp + 5 - prizevend(score) - - else if(emagged && (turtle >= 4)) - var/boomamt = rand(5,10) - temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" - playsound(src.loc, 'sound/arcade/boom.ogg', 20, 1, extrarange = -6, falloff = 10) - player_hp -= boomamt - - else if((enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - temp = "[enemy_name] steals [stealamt] of your power!" - playsound(src.loc, 'sound/arcade/steal.ogg', 20, 1, extrarange = -6, falloff = 10) - player_mp -= stealamt - updateUsrDialog() - - if(player_mp <= 0) - gameover = 1 - sleep(10) - temp = "You have been drained! GAME OVER" - playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) - if(emagged) - feedback_inc("arcade_loss_mana_emagged") - usr.gib() - else - feedback_inc("arcade_loss_mana_normal") - - else if((enemy_hp <= 10) && (enemy_mp > 4)) - temp = "[enemy_name] heals for 4 health!" - playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) - enemy_hp += 4 - enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - temp = "[enemy_name] attacks for [attackamt] damage!" - playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) - player_hp -= attackamt - - if((player_mp <= 0) || (player_hp <= 0)) - gameover = 1 - temp = "You have been crushed! GAME OVER" - playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) - if(emagged) - feedback_inc("arcade_loss_hp_emagged") - usr.gib() - else - feedback_inc("arcade_loss_hp_normal") - - blocked = 0 - return - - -/obj/machinery/computer/arcade/battle/emag_act(user as mob) - if(!emagged) - temp = "If you die in the game, you die for real!" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 - gameover = 0 - blocked = 0 - - emagged = 1 - - enemy_name = "Cuban Pete" - name = "Outbomb Cuban Pete" - - updateUsrDialog() - -// *** THE ORION TRAIL ** // - -#define ORION_TRAIL_WINTURN 9 - -//Orion Trail Events -#define ORION_TRAIL_RAIDERS "Raiders" -#define ORION_TRAIL_FLUX "Interstellar Flux" -#define ORION_TRAIL_ILLNESS "Illness" -#define ORION_TRAIL_BREAKDOWN "Breakdown" -#define ORION_TRAIL_LING "Changelings?" -#define ORION_TRAIL_LING_ATTACK "Changeling Ambush" -#define ORION_TRAIL_MALFUNCTION "Malfunction" -#define ORION_TRAIL_COLLISION "Collision" -#define ORION_TRAIL_SPACEPORT "Spaceport" -#define ORION_TRAIL_BLACKHOLE "BlackHole" - - -/obj/machinery/computer/arcade/orion_trail - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - icon_state = "arcade" - circuit = /obj/item/circuitboard/arcade/orion_trail - var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. - var/engine = 0 - var/hull = 0 - var/electronics = 0 - var/food = 80 - var/fuel = 60 - var/turns = 4 - var/playing = 0 - var/gameover = 0 - var/alive = 4 - var/eventdat = null - var/event = null - var/list/settlers = list("Harry","Larry","Bob") - var/list/events = list(ORION_TRAIL_RAIDERS = 3, - ORION_TRAIL_FLUX = 1, - ORION_TRAIL_ILLNESS = 3, - ORION_TRAIL_BREAKDOWN = 2, - ORION_TRAIL_LING = 3, - ORION_TRAIL_MALFUNCTION = 2, - ORION_TRAIL_COLLISION = 1, - ORION_TRAIL_SPACEPORT = 2 - ) - var/list/stops = list() - var/list/stopblurbs = list() - var/lings_aboard = 0 - var/spaceport_raided = 0 - var/spaceport_freebie = 0 - var/last_spaceport_action = "" - -/obj/machinery/computer/arcade/orion_trail/Reset() - // Sets up the main trail - stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") - stopblurbs = list( - "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", - "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", - "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", - "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", - "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", - "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", - "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", - "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", - "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" - ) - -/obj/machinery/computer/arcade/orion_trail/proc/newgame() - // Set names of settlers in crew - settlers = list() - for(var/i = 1; i <= 3; i++) - add_crewmember() - add_crewmember("[usr]") - // Re-set items to defaults - engine = 1 - hull = 1 - electronics = 1 - food = 80 - fuel = 60 - alive = 4 - turns = 1 - event = null - playing = 1 - gameover = 0 - lings_aboard = 0 - - //spaceport junk - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - -/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/user) - if(..()) - return - if(fuel <= 0 || food <=0 || settlers.len == 0) - gameover = 1 - event = null - user.set_machine(src) - var/dat = "" - if(gameover) - dat = "

    Game Over

    " - dat += "Like many before you, your crew never made it to Orion, lost to space...
    Forever." - if(settlers.len == 0) - dat += "
    Your entire crew died, your ship joins the fleet of ghost-ships littering the galaxy." - else - if(food <= 0) - dat += "
    You ran out of food and starved." - if(emagged) - user.set_nutrition(0) //yeah you pretty hongry - to_chat(user, "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.") - if(fuel <= 0) - dat += "
    You ran out of fuel, and drift, slowly, into a star." - if(emagged) - var/mob/living/M = user - M.adjust_fire_stacks(5) - M.IgniteMob() //flew into a star, so you're on fire - to_chat(user, "You feel an immense wave of heat emanate from the arcade machine. Your skin bursts into flames.") - dat += "

    OK...

    " - - if(emagged) - to_chat(user, "You're never going to make it to Orion...") - user.death() - emagged = 0 //removes the emagged status after you lose - playing = 0 //also a new game - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - - else if(event) - dat = eventdat - else if(playing) - var/title = stops[turns] - var/subtext = stopblurbs[turns] - dat = "

    [title]

    " - dat += "[subtext]" - dat += "

    Crew:

    " - dat += english_list(settlers) - dat += "
    Food: [food] | Fuel: [fuel]" - dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - if(turns == 7) - dat += "

    Go Around Continue

    " - else - dat += "

    Continue

    " - dat += "

    Kill a crewmember

    " - dat += "

    Close

    " - else - dat = "

    The Orion Trail

    " - dat += "

    Experience the journey of your ancestors!



    " - dat += "
    New Game
    " - dat += "

    Close

    " - var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - return - -/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) - . = ..() - if(.) - return - if(href_list["close"]) - usr.unset_machine() - usr << browse(null, "window=arcade") - - if(busy) - return - busy = 1 - - if(href_list["continue"]) //Continue your travels - if(turns >= ORION_TRAIL_WINTURN) - win() - else - food -= (alive+lings_aboard)*2 - fuel -= 5 - if(turns == 2 && prob(30)) - event = ORION_TRAIL_COLLISION - event() - else if(prob(75)) - event = pickweight(events) - if(lings_aboard) - if(event == ORION_TRAIL_LING || prob(55)) - event = ORION_TRAIL_LING_ATTACK - event() - turns += 1 - if(emagged) - var/mob/living/carbon/M = usr //for some vars - switch(event) - if(ORION_TRAIL_RAIDERS) - if(prob(50)) - to_chat(usr, "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?") - M.AdjustHallucinate(30) - else - to_chat(usr, "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...") - M.take_organ_damage(30) - playsound(loc, 'sound/weapons/genhit2.ogg', 100, 1) - if(ORION_TRAIL_ILLNESS) - var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS - if(severity == 1) - to_chat(M, "You suddenly feel slightly nauseous.")//got off lucky - - if(severity == 2) - to_chat(usr, "You suddenly feel extremely nauseous and hunch over until it passes.") - M.Stun(3) - if(severity >= 3) //you didn't pray hard enough - to_chat(M, "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.") - M.Stun(5) - sleep(30) - atom_say("[M] violently throws up!") - playsound(loc, 'sound/effects/splat.ogg', 50, 1) - M.adjust_nutrition(-50) //lose a lot of food - var/turf/location = usr.loc - if(istype(location, /turf/simulated)) - location.add_vomit_floor(TRUE) - if(ORION_TRAIL_FLUX) - if(prob(75)) - M.Weaken(3) - atom_say("A sudden gust of powerful wind slams [M] into the floor!") - M.take_organ_damage(25) - playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) - else - to_chat(M, "A violent gale blows past you, and you barely manage to stay standing!") - if(ORION_TRAIL_COLLISION) //by far the most damaging event - if(prob(90)) - playsound(src.loc, 'sound/effects/bang.ogg', 100, 1) - var/turf/simulated/floor/F - for(F in orange(1, src)) - F.ChangeTurf(F.baseturf) - atom_say("Something slams into the floor around [src], exposing it to space!") - if(hull) - sleep(10) - atom_say("A new floor suddenly appears around [src]. What the hell?") - playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) - var/turf/space/T - for(T in orange(1, src)) - T.ChangeTurf(/turf/simulated/floor/plating) - else - atom_say("Something slams into the floor around [src] - luckily, it didn't get through!") - playsound(src.loc, 'sound/effects/bang.ogg', 20, 1) - if(ORION_TRAIL_MALFUNCTION) - playsound(src.loc, 'sound/effects/empulse.ogg', 20, 1) - visible_message("[src] malfunctions, randomizing in-game stats!") - var/oldfood = food - var/oldfuel = fuel - food = rand(10,80) / rand(1,2) - fuel = rand(10,60) / rand(1,2) - if(electronics) - sleep(10) - if(oldfuel > fuel && oldfood > food) - audible_message("[src] lets out a somehow reassuring chime.") - else if(oldfuel < fuel || oldfood < food) - audible_message("[src] lets out a somehow ominous chime.") - food = oldfood - fuel = oldfuel - playsound(src.loc, 'sound/machines/chime.ogg', 20, 1) - - else if(href_list["newgame"]) //Reset everything - newgame() - else if(href_list["menu"]) //back to the main menu - playing = 0 - event = null - gameover = 0 - food = 80 - fuel = 60 - settlers = list("Harry","Larry","Bob") - else if(href_list["slow"]) //slow down - food -= (alive+lings_aboard)*2 - fuel -= 5 - event = null - else if(href_list["pastblack"]) //slow down - food -= ((alive+lings_aboard)*2)*3 - fuel -= 15 - turns += 1 - event = null - else if(href_list["useengine"]) //use parts - engine = max(0, --engine) - event = null - else if(href_list["useelec"]) //use parts - electronics = max(0, --electronics) - event = null - else if(href_list["usehull"]) //use parts - hull = max(0, --hull) - event = null - else if(href_list["wait"]) //wait 3 days - food -= ((alive+lings_aboard)*2)*3 - event = null - else if(href_list["keepspeed"]) //keep speed - if(prob(75)) - event = "Breakdown" - event() - else - event = null - else if(href_list["blackhole"]) //keep speed past a black hole - if(prob(75)) - event = ORION_TRAIL_BLACKHOLE - event() - if(emagged) //has to be here because otherwise it doesn't work - playsound(src.loc, 'sound/effects/supermatter.ogg', 100, 1) - atom_say("A miniature black hole suddenly appears in front of [src], devouring [usr] alive!") - usr.Stun(10) //you can't run :^) - var/S = new /obj/singularity/academy(usr.loc) - emagged = 0 //immediately removes emagged status so people can't kill themselves by sprinting up and interacting - sleep(50) - atom_say("[S] winks out, just as suddenly as it appeared.") - qdel(S) - else - event = null - turns += 1 - else if(href_list["holedeath"]) - gameover = 1 - event = null - else if(href_list["eventclose"]) //end an event - event = null - - else if(href_list["killcrew"]) //shoot a crewmember - var/sheriff = remove_crewmember() //I shot the sheriff - playsound(loc,'sound/weapons/gunshots/gunshot.ogg', 100, 1) - - if(settlers.len == 0 || alive == 0) - atom_say("The last crewmember [sheriff], shot themselves, GAME OVER!") - if(emagged) - usr.death(0) - emagged = 0 - gameover = 1 - event = null - else if(emagged) - if(usr.name == sheriff) - atom_say("The crew of the ship chose to kill [usr.name]!") - usr.death(0) - - if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places - event = null - - //Spaceport specific interactions - //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) - //they also call event() again, to regen the eventdata, which is kind of odd but necessary - else if(href_list["buycrew"]) //buy a crewmember - var/bought = add_crewmember() - last_spaceport_action = "You hired [bought] as a new crewmember." - fuel -= 10 - food -= 10 - event() - - else if(href_list["sellcrew"]) //sell a crewmember - var/sold = remove_crewmember() - last_spaceport_action = "You sold your crewmember, [sold]!" - fuel += 7 - food += 7 - event() - - else if(href_list["leave_spaceport"]) - event = null - spaceport_raided = 0 - spaceport_freebie = 0 - last_spaceport_action = "" - - else if(href_list["raid_spaceport"]) - var/success = min(15 * alive,100) //default crew (4) have a 60% chance - spaceport_raided = 1 - - var/FU = 0 - var/FO = 0 - if(prob(success)) - FU = rand(5,15) - FO = rand(5,15) - last_spaceport_action = "You successfully raided the spaceport! you gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" - else - FU = rand(-5,-15) - FO = rand(-5,-15) - last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" - - //your chance of lose a crewmember is 1/2 your chance of success - //this makes higher % failures hurt more, don't get cocky space cowboy! - if(prob(success*5)) - var/lost_crew = remove_crewmember() - last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" - if(emagged) - atom_say("WEEWOO WEEWOO, Spaceport Security en route!") - for(var/i, i<=3, i++) - var/mob/living/simple_animal/hostile/syndicate/ranged/orion/O = new/mob/living/simple_animal/hostile/syndicate/ranged/orion(get_turf(src)) - O.target = usr - - - fuel += FU - food += FO - event() - - else if(href_list["buyparts"]) - switch(text2num(href_list["buyparts"])) - if(1) //Engine Parts - engine++ - last_spaceport_action = "Bought Engine Parts" - if(2) //Hull Plates - hull++ - last_spaceport_action = "Bought Hull Plates" - if(3) //Spare Electronics - electronics++ - last_spaceport_action = "Bought Spare Electronics" - fuel -= 5 //they all cost 5 - event() - - else if(href_list["trade"]) - switch(text2num(href_list["trade"])) - if(1) //Fuel - fuel -= 5 - food += 5 - last_spaceport_action = "Traded Fuel for Food" - if(2) //Food - fuel += 5 - food -= 5 - last_spaceport_action = "Traded Food for Fuel" - event() - - add_fingerprint(usr) - updateUsrDialog() - busy = 0 - return - - -/obj/machinery/computer/arcade/orion_trail/proc/event() - eventdat = "

    [event]

    " - - switch(event) - if(ORION_TRAIL_RAIDERS) - eventdat += "Raiders have come aboard your ship!" - if(prob(50)) - var/sfood = rand(1,10) - var/sfuel = rand(1,10) - food -= sfood - fuel -= sfuel - eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." - else if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] tried to fight back but was killed." - else - eventdat += "
    Fortunately you fended them off without any trouble." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_FLUX) - eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." - eventdat += "
    What will you do?" - eventdat += "

    Slow Down Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_ILLNESS) - eventdat += "A deadly illness has been contracted!" - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by the disease." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_BREAKDOWN) - eventdat += "Oh no! The engine has broken down!" - eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." - if(engine >= 1) - eventdat += "

    Use PartWait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_MALFUNCTION) - eventdat += "The ship's systems are malfunctioning!" - eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." - if(electronics >= 1) - eventdat += "

    Use PartWait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_COLLISION) - eventdat += "Something hit us! Looks like there's some hull damage." - if(prob(25)) - var/sfood = rand(5,15) - var/sfuel = rand(5,15) - food -= sfood - fuel -= sfuel - eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." - if(prob(10)) - var/deadname = remove_crewmember() - eventdat += "
    [deadname] was killed by rapid depressurization." - eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." - if(hull >= 1) - eventdat += "

    Use PartWait

    " - else - eventdat += "

    Wait

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_BLACKHOLE) - eventdat += "You were swept away into the black hole." - eventdat += "

    Oh...

    " - eventdat += "

    Close

    " - settlers = list() - - if(ORION_TRAIL_LING) - eventdat += "Strange reports warn of changelings infiltrating crews on trips to Orion..." - if(settlers.len <= 2) - eventdat += "
    Your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..." - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - if(prob(10)) // "likely", I didn't say it was guaranteed! - lings_aboard = min(++lings_aboard,2) - else - if(lings_aboard) //less likely to stack lings - if(prob(20)) - lings_aboard = min(++lings_aboard,2) - else if(prob(70)) - lings_aboard = min(++lings_aboard,2) - - eventdat += "

    Kill a crewmember

    " - eventdat += "

    Risk it

    " - eventdat += "

    Close

    " - - if(ORION_TRAIL_LING_ATTACK) - if(lings_aboard <= 0) //shouldn't trigger, but hey. - eventdat += "Haha, fooled you, there are no changelings on board!" - eventdat += "
    (You should report this to a coder :S)" - else - var/ling1 = remove_crewmember() - var/ling2 = "" - if(lings_aboard >= 2) - ling2 = remove_crewmember() - - eventdat += "Oh no, some of your crew are Changelings!" - if(ling2) - eventdat += "
    [ling1] and [ling2]'s arms twist and contort into grotesque blades!" - else - eventdat += "
    [ling1]'s arm twists and contorts into a grotesque blade!" - - var/chance2attack = alive*20 - if(prob(chance2attack)) - var/chancetokill = 30*lings_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance - if(prob(chancetokill)) - var/deadguy = remove_crewmember() - eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] up to [deadguy] and capitulates them!" - else - eventdat += "
    You valiantly fight off the Changeling[ling2 ? "s":""]!" - eventdat += "
    You cut the Changeling[ling2 ? "s":""] up into meat... Eww" - if(ling2) - food += 30 - lings_aboard = max(0,lings_aboard-2) - else - food += 15 - lings_aboard = max(0,--lings_aboard) - else - eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] away, What wimps!" - if(ling2) - lings_aboard = max(0,lings_aboard-2) - else - lings_aboard = max(0,--lings_aboard) - - eventdat += "

    Continue

    " - eventdat += "

    Close

    " - - - if(ORION_TRAIL_SPACEPORT) - if(spaceport_raided) - eventdat += "The Spaceport is on high alert! They wont let you dock since you tried to attack them!" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Depart Spaceport

    " - eventdat += "

    Close

    " - else - eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" - eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." - eventdat += "
    Trading terms: FU = Fuel, FO = Food" - if(last_spaceport_action) - eventdat += "
    Last Spaceport Action: [last_spaceport_action]" - eventdat += "

    Crew:

    " - eventdat += english_list(settlers) - eventdat += "
    Food: [food] | Fuel: [fuel]" - eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" - - - //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) - if(!spaceport_freebie && (fuel < 20 || food < 20)) - spaceport_freebie++ - var/FU = 10 - var/FO = 10 - var/freecrew = 0 - if(prob(30)) - FU = 25 - FO = 25 - - if(prob(10)) - add_crewmember() - freecrew++ - - eventdat += "
    The traders of the spaceport take pitty on you, and give you some food and fuel (+[FU]FU,+[FO]FO)" - if(freecrew) - eventdat += "
    You also gain a new crewmember!" - - fuel += FU - food += FO - - //CREW INTERACTIONS - eventdat += "

    Crew Management:

    " - - //Buy crew - if(food >= 10 && fuel >= 10) - eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " - else - eventdat += "

    Cant afford a new Crewmember

    " - - //Sell crew - if(settlers.len > 1) - eventdat += "

    Sell crew for Fuel and Food (+15FU,+15FO)

    " - else - eventdat += "

    Cant afford to sell a Crewmember

    " - - //BUY/SELL STUFF - eventdat += "

    Spare Parts:

    " - - //Engine parts - if(fuel > 5) - eventdat += "

    Buy Engine Parts (-5FU)

    " - else - eventdat += "

    Cant afford to buy Engine Parts" - - //Hull plates - if(fuel > 5) - eventdat += "

    Buy Hull Plates (-5FU)

    " - else - eventdat += "

    Cant afford to buy Hull Plates" - - //Electronics - if(fuel > 5) - eventdat += "

    Buy Spare Electronics (-5FU)

    " - else - eventdat += "

    Cant afford to buy Spare Electronics" - - //Trade - if(fuel > 5) - eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " - else - eventdat += "

    Cant afford to Trade Fuel for Food 5) - eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " - else - eventdat += "

    Cant afford to Trade Food for Fuel= 1) //need to make sure we even have anyone to remove - removed = pick(safe2remove) - - if(removed) - if(lings_aboard && prob(40*lings_aboard)) //if there are 2 lings you're twice as likely to get one, obviously - lings_aboard = max(0,--lings_aboard) - settlers -= removed - alive-- - return removed - - -/obj/machinery/computer/arcade/orion_trail/proc/win() - playing = 0 - turns = 1 - atom_say("Congratulations, you made it to Orion!") - if(emagged) - new /obj/item/orion_ship(get_turf(src)) - message_admins("[key_name_admin(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") - log_game("[key_name(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") - else - var/score = alive + round(food/2) + round(fuel/5) + engine + hull + electronics - lings_aboard - prizevend(score) - emagged = 0 - name = "The Orion Trail" - desc = "Learn how our ancestors got to Orion, and have fun in the process!" - -/obj/machinery/computer/arcade/orion_trail/emag_act(mob/user) - if(!emagged) - to_chat(user, "You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Realism Mode.") - name = "The Orion Trail: Realism Edition" - desc = "Learn how our ancestors got to Orion, and try not to die in the process!" - newgame() - emagged = 1 - -/mob/living/simple_animal/hostile/syndicate/ranged/orion - name = "spaceport security" - desc = "Premier corporate security forces for all spaceports found along the Orion Trail." - faction = list("orion") - loot = list() - del_on_death = TRUE - -/obj/item/orion_ship - name = "model settler ship" - desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..." - icon = 'icons/obj/toy.dmi' - icon_state = "ship" - w_class = WEIGHT_CLASS_SMALL - var/active = 0 //if the ship is on - -/obj/item/orion_ship/examine(mob/user) - . = ..() - if(in_range(user, src)) - if(!active) - . += "There's a little switch on the bottom. It's flipped down." - else - . += "There's a little switch on the bottom. It's flipped up." - -/obj/item/orion_ship/attack_self(mob/user) //Minibomb-level explosion. Should probably be more because of how hard it is to survive the machine! Also, just over a 5-second fuse - if(active) - return - - message_admins("[key_name_admin(usr)] primed an explosive Orion ship for detonation.") - log_game("[key_name(usr)] primed an explosive Orion ship for detonation.") - - to_chat(user, "You flip the switch on the underside of [src].") - active = 1 - visible_message("[src] softly beeps and whirs to life!") - playsound(src.loc, 'sound/machines/defib_saftyon.ogg', 25, 1) - atom_say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.") - sleep(20) - visible_message("[src] begins to vibrate...") - atom_say("Uh, Port? Having some issues with our reactor, could you check it out? Over.") - sleep(30) - atom_say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 25, 1) - sleep(3.6) - visible_message("[src] explodes!") - explosion(src.loc, 1,2,4, flame_range = 3) - qdel(src) - - -#undef ORION_TRAIL_WINTURN -#undef ORION_TRAIL_RAIDERS -#undef ORION_TRAIL_FLUX -#undef ORION_TRAIL_ILLNESS -#undef ORION_TRAIL_BREAKDOWN -#undef ORION_TRAIL_LING -#undef ORION_TRAIL_LING_ATTACK -#undef ORION_TRAIL_MALFUNCTION -#undef ORION_TRAIL_COLLISION -#undef ORION_TRAIL_SPACEPORT -#undef ORION_TRAIL_BLACKHOLE +/obj/machinery/computer/arcade + name = "random arcade" + desc = "random arcade machine" + icon = 'icons/obj/computer.dmi' + icon_state = "arcade" + icon_keyboard = null + icon_screen = "invaders" + light_color = "#00FF00" + var/prize = /obj/item/stack/tickets + +/obj/machinery/computer/arcade/proc/Reset() + return + +/obj/machinery/computer/arcade/New() + ..() + if(!circuit) + var/choice = pick(subtypesof(/obj/machinery/computer/arcade)) + new choice(loc) + qdel(src) + return + Reset() + + +/obj/machinery/computer/arcade/proc/prizevend(var/score) + if(!contents.len) + var/prize_amount + if(score) + prize_amount = score + else + prize_amount = rand(1, 10) + new prize(get_turf(src), prize_amount) + else + var/atom/movable/prize = pick(contents) + prize.loc = get_turf(src) + +/obj/machinery/computer/arcade/emp_act(severity) + ..(severity) + if(stat & (NOPOWER|BROKEN)) + return + var/num_of_prizes = 0 + switch(severity) + if(1) + num_of_prizes = rand(1,4) + if(2) + num_of_prizes = rand(0,2) + for(var/i = num_of_prizes; i > 0; i--) + prizevend() + explosion(get_turf(src), -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes) + + +/obj/machinery/computer/arcade/battle + name = "arcade machine" + desc = "Does not support Pinball." + icon = 'icons/obj/computer.dmi' + icon_state = "arcade" + circuit = /obj/item/circuitboard/arcade/battle + var/enemy_name = "Space Villian" + var/temp = "Winners Don't Use Spacedrugs" //Temporary message, for attack messages, etc + var/player_hp = 30 //Player health/attack points + var/player_mp = 10 + var/enemy_hp = 45 //Enemy health/attack points + var/enemy_mp = 20 + var/gameover = 0 + var/blocked = 0 //Player cannot attack/heal while set + var/turtle = 0 + +/obj/machinery/computer/arcade/battle/Reset() + var/name_action + var/name_part1 + var/name_part2 + + name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") + + name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") + name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") + + enemy_name = replacetext((name_part1 + name_part2), "the ", "") + name = (name_action + name_part1 + name_part2) + +/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) + if(..()) + return + user.set_machine(src) + var/dat = "Close" + dat += "

    [enemy_name]

    " + + dat += "

    [temp]

    " + dat += "
    Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]
    " + + if(gameover) + dat += "
    New Game" + else + dat += "
    Attack | " + dat += "Heal | " + dat += "Recharge Power" + + dat += "
    " + + //user << browse(dat, "window=arcade") + //onclose(user, "arcade") + var/datum/browser/popup = new(user, "arcade", "Space Villian 2000") + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + return + +/obj/machinery/computer/arcade/battle/Topic(href, href_list) + if(..()) + return + + if(!blocked && !gameover) + if(href_list["attack"]) + blocked = 1 + var/attackamt = rand(2,6) + temp = "You attack for [attackamt] damage!" + playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) + updateUsrDialog() + if(turtle > 0) + turtle-- + + sleep(10) + enemy_hp -= attackamt + arcade_action() + + else if(href_list["heal"]) + blocked = 1 + var/pointamt = rand(1,3) + var/healamt = rand(6,8) + temp = "You use [pointamt] magic to heal for [healamt] damage!" + playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) + updateUsrDialog() + turtle++ + + sleep(10) + player_mp -= pointamt + player_hp += healamt + blocked = 1 + updateUsrDialog() + arcade_action() + + else if(href_list["charge"]) + blocked = 1 + var/chargeamt = rand(4,7) + temp = "You regain [chargeamt] points" + playsound(src.loc, 'sound/arcade/mana.ogg', 20, 1, extrarange = -6, falloff = 10) + player_mp += chargeamt + if(turtle > 0) + turtle-- + + updateUsrDialog() + sleep(10) + arcade_action() + + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + else if(href_list["newgame"]) //Reset everything + temp = "New Round" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + turtle = 0 + + if(emagged) + Reset() + emagged = 0 + + add_fingerprint(usr) + updateUsrDialog() + return + +/obj/machinery/computer/arcade/battle/proc/arcade_action() + if((enemy_mp <= 0) || (enemy_hp <= 0)) + if(!gameover) + gameover = 1 + temp = "[enemy_name] has fallen! Rejoice!" + playsound(src.loc, 'sound/arcade/win.ogg', 20, 1, extrarange = -6, falloff = 10) + + if(emagged) + feedback_inc("arcade_win_emagged") + new /obj/effect/spawner/newbomb/timer/syndicate(get_turf(src)) + new /obj/item/clothing/head/collectable/petehat(get_turf(src)) + message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") + log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.") + Reset() + emagged = 0 + else + feedback_inc("arcade_win_normal") + var/score = player_hp + player_mp + 5 + prizevend(score) + + else if(emagged && (turtle >= 4)) + var/boomamt = rand(5,10) + temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" + playsound(src.loc, 'sound/arcade/boom.ogg', 20, 1, extrarange = -6, falloff = 10) + player_hp -= boomamt + + else if((enemy_mp <= 5) && (prob(70))) + var/stealamt = rand(2,3) + temp = "[enemy_name] steals [stealamt] of your power!" + playsound(src.loc, 'sound/arcade/steal.ogg', 20, 1, extrarange = -6, falloff = 10) + player_mp -= stealamt + updateUsrDialog() + + if(player_mp <= 0) + gameover = 1 + sleep(10) + temp = "You have been drained! GAME OVER" + playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) + if(emagged) + feedback_inc("arcade_loss_mana_emagged") + usr.gib() + else + feedback_inc("arcade_loss_mana_normal") + + else if((enemy_hp <= 10) && (enemy_mp > 4)) + temp = "[enemy_name] heals for 4 health!" + playsound(src.loc, 'sound/arcade/heal.ogg', 20, 1, extrarange = -6, falloff = 10) + enemy_hp += 4 + enemy_mp -= 4 + + else + var/attackamt = rand(3,6) + temp = "[enemy_name] attacks for [attackamt] damage!" + playsound(src.loc, 'sound/arcade/hit.ogg', 20, 1, extrarange = -6, falloff = 10) + player_hp -= attackamt + + if((player_mp <= 0) || (player_hp <= 0)) + gameover = 1 + temp = "You have been crushed! GAME OVER" + playsound(src.loc, 'sound/arcade/lose.ogg', 20, 1, extrarange = -6, falloff = 10) + if(emagged) + feedback_inc("arcade_loss_hp_emagged") + usr.gib() + else + feedback_inc("arcade_loss_hp_normal") + + blocked = 0 + return + + +/obj/machinery/computer/arcade/battle/emag_act(user as mob) + if(!emagged) + temp = "If you die in the game, you die for real!" + player_hp = 30 + player_mp = 10 + enemy_hp = 45 + enemy_mp = 20 + gameover = 0 + blocked = 0 + + emagged = 1 + + enemy_name = "Cuban Pete" + name = "Outbomb Cuban Pete" + + updateUsrDialog() + +// *** THE ORION TRAIL ** // + +#define ORION_TRAIL_WINTURN 9 + +//Orion Trail Events +#define ORION_TRAIL_RAIDERS "Raiders" +#define ORION_TRAIL_FLUX "Interstellar Flux" +#define ORION_TRAIL_ILLNESS "Illness" +#define ORION_TRAIL_BREAKDOWN "Breakdown" +#define ORION_TRAIL_LING "Changelings?" +#define ORION_TRAIL_LING_ATTACK "Changeling Ambush" +#define ORION_TRAIL_MALFUNCTION "Malfunction" +#define ORION_TRAIL_COLLISION "Collision" +#define ORION_TRAIL_SPACEPORT "Spaceport" +#define ORION_TRAIL_BLACKHOLE "BlackHole" + + +/obj/machinery/computer/arcade/orion_trail + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + icon_state = "arcade" + circuit = /obj/item/circuitboard/arcade/orion_trail + var/busy = 0 //prevent clickspam that allowed people to ~speedrun~ the game. + var/engine = 0 + var/hull = 0 + var/electronics = 0 + var/food = 80 + var/fuel = 60 + var/turns = 4 + var/playing = 0 + var/gameover = 0 + var/alive = 4 + var/eventdat = null + var/event = null + var/list/settlers = list("Harry","Larry","Bob") + var/list/events = list(ORION_TRAIL_RAIDERS = 3, + ORION_TRAIL_FLUX = 1, + ORION_TRAIL_ILLNESS = 3, + ORION_TRAIL_BREAKDOWN = 2, + ORION_TRAIL_LING = 3, + ORION_TRAIL_MALFUNCTION = 2, + ORION_TRAIL_COLLISION = 1, + ORION_TRAIL_SPACEPORT = 2 + ) + var/list/stops = list() + var/list/stopblurbs = list() + var/lings_aboard = 0 + var/spaceport_raided = 0 + var/spaceport_freebie = 0 + var/last_spaceport_action = "" + +/obj/machinery/computer/arcade/orion_trail/Reset() + // Sets up the main trail + stops = list("Pluto","Asteroid Belt","Proxima Centauri","Dead Space","Rigel Prime","Tau Ceti Beta","Black Hole","Space Outpost Beta-9","Orion Prime") + stopblurbs = list( + "Pluto, long since occupied with long-range sensors and scanners, stands ready to, and indeed continues to probe the far reaches of the galaxy.", + "At the edge of the Sol system lies a treacherous asteroid belt. Many have been crushed by stray asteroids and misguided judgement.", + "The nearest star system to Sol, in ages past it stood as a reminder of the boundaries of sub-light travel, now a low-population sanctuary for adventurers and traders.", + "This region of space is particularly devoid of matter. Such low-density pockets are known to exist, but the vastness of it is astounding.", + "Rigel Prime, the center of the Rigel system, burns hot, basking its planetary bodies in warmth and radiation.", + "Tau Ceti Beta has recently become a waypoint for colonists headed towards Orion. There are many ships and makeshift stations in the vicinity.", + "Sensors indicate that a black hole's gravitational field is affecting the region of space we were headed through. We could stay of course, but risk of being overcome by its gravity, or we could change course to go around, which will take longer.", + "You have come into range of the first man-made structure in this region of space. It has been constructed not by travellers from Sol, but by colonists from Orion. It stands as a monument to the colonists' success.", + "You have made it to Orion! Congratulations! Your crew is one of the few to start a new foothold for mankind!" + ) + +/obj/machinery/computer/arcade/orion_trail/proc/newgame() + // Set names of settlers in crew + settlers = list() + for(var/i = 1; i <= 3; i++) + add_crewmember() + add_crewmember("[usr]") + // Re-set items to defaults + engine = 1 + hull = 1 + electronics = 1 + food = 80 + fuel = 60 + alive = 4 + turns = 1 + event = null + playing = 1 + gameover = 0 + lings_aboard = 0 + + //spaceport junk + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + +/obj/machinery/computer/arcade/orion_trail/attack_hand(mob/user) + if(..()) + return + if(fuel <= 0 || food <=0 || settlers.len == 0) + gameover = 1 + event = null + user.set_machine(src) + var/dat = "" + if(gameover) + dat = "

    Game Over

    " + dat += "Like many before you, your crew never made it to Orion, lost to space...
    Forever." + if(settlers.len == 0) + dat += "
    Your entire crew died, your ship joins the fleet of ghost-ships littering the galaxy." + else + if(food <= 0) + dat += "
    You ran out of food and starved." + if(emagged) + user.set_nutrition(0) //yeah you pretty hongry + to_chat(user, "Your body instantly contracts to that of one who has not eaten in months. Agonizing cramps seize you as you fall to the floor.") + if(fuel <= 0) + dat += "
    You ran out of fuel, and drift, slowly, into a star." + if(emagged) + var/mob/living/M = user + M.adjust_fire_stacks(5) + M.IgniteMob() //flew into a star, so you're on fire + to_chat(user, "You feel an immense wave of heat emanate from the arcade machine. Your skin bursts into flames.") + dat += "

    OK...

    " + + if(emagged) + to_chat(user, "You're never going to make it to Orion...") + user.death() + emagged = 0 //removes the emagged status after you lose + playing = 0 //also a new game + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + + else if(event) + dat = eventdat + else if(playing) + var/title = stops[turns] + var/subtext = stopblurbs[turns] + dat = "

    [title]

    " + dat += "[subtext]" + dat += "

    Crew:

    " + dat += english_list(settlers) + dat += "
    Food: [food] | Fuel: [fuel]" + dat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + if(turns == 7) + dat += "

    Go Around Continue

    " + else + dat += "

    Continue

    " + dat += "

    Kill a crewmember

    " + dat += "

    Close

    " + else + dat = "

    The Orion Trail

    " + dat += "

    Experience the journey of your ancestors!



    " + dat += "
    New Game
    " + dat += "

    Close

    " + var/datum/browser/popup = new(user, "arcade", "The Orion Trail",400,700) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + return + +/obj/machinery/computer/arcade/orion_trail/Topic(href, href_list) + . = ..() + if(.) + return + if(href_list["close"]) + usr.unset_machine() + usr << browse(null, "window=arcade") + + if(busy) + return + busy = 1 + + if(href_list["continue"]) //Continue your travels + if(turns >= ORION_TRAIL_WINTURN) + win() + else + food -= (alive+lings_aboard)*2 + fuel -= 5 + if(turns == 2 && prob(30)) + event = ORION_TRAIL_COLLISION + event() + else if(prob(75)) + event = pickweight(events) + if(lings_aboard) + if(event == ORION_TRAIL_LING || prob(55)) + event = ORION_TRAIL_LING_ATTACK + event() + turns += 1 + if(emagged) + var/mob/living/carbon/M = usr //for some vars + switch(event) + if(ORION_TRAIL_RAIDERS) + if(prob(50)) + to_chat(usr, "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?") + M.AdjustHallucinate(30) + else + to_chat(usr, "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...") + M.take_organ_damage(30) + playsound(loc, 'sound/weapons/genhit2.ogg', 100, 1) + if(ORION_TRAIL_ILLNESS) + var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS + if(severity == 1) + to_chat(M, "You suddenly feel slightly nauseous.")//got off lucky + + if(severity == 2) + to_chat(usr, "You suddenly feel extremely nauseous and hunch over until it passes.") + M.Stun(3) + if(severity >= 3) //you didn't pray hard enough + to_chat(M, "An overpowering wave of nausea consumes over you. You hunch over, your stomach's contents preparing for a spectacular exit.") + M.Stun(5) + sleep(30) + atom_say("[M] violently throws up!") + playsound(loc, 'sound/effects/splat.ogg', 50, 1) + M.adjust_nutrition(-50) //lose a lot of food + var/turf/location = usr.loc + if(istype(location, /turf/simulated)) + location.add_vomit_floor(TRUE) + if(ORION_TRAIL_FLUX) + if(prob(75)) + M.Weaken(3) + atom_say("A sudden gust of powerful wind slams [M] into the floor!") + M.take_organ_damage(25) + playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) + else + to_chat(M, "A violent gale blows past you, and you barely manage to stay standing!") + if(ORION_TRAIL_COLLISION) //by far the most damaging event + if(prob(90)) + playsound(src.loc, 'sound/effects/bang.ogg', 100, 1) + var/turf/simulated/floor/F + for(F in orange(1, src)) + F.ChangeTurf(F.baseturf) + atom_say("Something slams into the floor around [src], exposing it to space!") + if(hull) + sleep(10) + atom_say("A new floor suddenly appears around [src]. What the hell?") + playsound(src.loc, 'sound/weapons/genhit.ogg', 100, 1) + var/turf/space/T + for(T in orange(1, src)) + T.ChangeTurf(/turf/simulated/floor/plating) + else + atom_say("Something slams into the floor around [src] - luckily, it didn't get through!") + playsound(src.loc, 'sound/effects/bang.ogg', 20, 1) + if(ORION_TRAIL_MALFUNCTION) + playsound(src.loc, 'sound/effects/empulse.ogg', 20, 1) + visible_message("[src] malfunctions, randomizing in-game stats!") + var/oldfood = food + var/oldfuel = fuel + food = rand(10,80) / rand(1,2) + fuel = rand(10,60) / rand(1,2) + if(electronics) + sleep(10) + if(oldfuel > fuel && oldfood > food) + audible_message("[src] lets out a somehow reassuring chime.") + else if(oldfuel < fuel || oldfood < food) + audible_message("[src] lets out a somehow ominous chime.") + food = oldfood + fuel = oldfuel + playsound(src.loc, 'sound/machines/chime.ogg', 20, 1) + + else if(href_list["newgame"]) //Reset everything + newgame() + else if(href_list["menu"]) //back to the main menu + playing = 0 + event = null + gameover = 0 + food = 80 + fuel = 60 + settlers = list("Harry","Larry","Bob") + else if(href_list["slow"]) //slow down + food -= (alive+lings_aboard)*2 + fuel -= 5 + event = null + else if(href_list["pastblack"]) //slow down + food -= ((alive+lings_aboard)*2)*3 + fuel -= 15 + turns += 1 + event = null + else if(href_list["useengine"]) //use parts + engine = max(0, --engine) + event = null + else if(href_list["useelec"]) //use parts + electronics = max(0, --electronics) + event = null + else if(href_list["usehull"]) //use parts + hull = max(0, --hull) + event = null + else if(href_list["wait"]) //wait 3 days + food -= ((alive+lings_aboard)*2)*3 + event = null + else if(href_list["keepspeed"]) //keep speed + if(prob(75)) + event = "Breakdown" + event() + else + event = null + else if(href_list["blackhole"]) //keep speed past a black hole + if(prob(75)) + event = ORION_TRAIL_BLACKHOLE + event() + if(emagged) //has to be here because otherwise it doesn't work + playsound(src.loc, 'sound/effects/supermatter.ogg', 100, 1) + atom_say("A miniature black hole suddenly appears in front of [src], devouring [usr] alive!") + usr.Stun(10) //you can't run :^) + var/S = new /obj/singularity/academy(usr.loc) + emagged = 0 //immediately removes emagged status so people can't kill themselves by sprinting up and interacting + sleep(50) + atom_say("[S] winks out, just as suddenly as it appeared.") + qdel(S) + else + event = null + turns += 1 + else if(href_list["holedeath"]) + gameover = 1 + event = null + else if(href_list["eventclose"]) //end an event + event = null + + else if(href_list["killcrew"]) //shoot a crewmember + var/sheriff = remove_crewmember() //I shot the sheriff + playsound(loc,'sound/weapons/gunshots/gunshot.ogg', 100, 1) + + if(settlers.len == 0 || alive == 0) + atom_say("The last crewmember [sheriff], shot themselves, GAME OVER!") + if(emagged) + usr.death(0) + emagged = 0 + gameover = 1 + event = null + else if(emagged) + if(usr.name == sheriff) + atom_say("The crew of the ship chose to kill [usr.name]!") + usr.death(0) + + if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places + event = null + + //Spaceport specific interactions + //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) + //they also call event() again, to regen the eventdata, which is kind of odd but necessary + else if(href_list["buycrew"]) //buy a crewmember + var/bought = add_crewmember() + last_spaceport_action = "You hired [bought] as a new crewmember." + fuel -= 10 + food -= 10 + event() + + else if(href_list["sellcrew"]) //sell a crewmember + var/sold = remove_crewmember() + last_spaceport_action = "You sold your crewmember, [sold]!" + fuel += 7 + food += 7 + event() + + else if(href_list["leave_spaceport"]) + event = null + spaceport_raided = 0 + spaceport_freebie = 0 + last_spaceport_action = "" + + else if(href_list["raid_spaceport"]) + var/success = min(15 * alive,100) //default crew (4) have a 60% chance + spaceport_raided = 1 + + var/FU = 0 + var/FO = 0 + if(prob(success)) + FU = rand(5,15) + FO = rand(5,15) + last_spaceport_action = "You successfully raided the spaceport! you gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" + else + FU = rand(-5,-15) + FO = rand(-5,-15) + last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food in your scramble to escape! ([FU]FU,[FO]FO)" + + //your chance of lose a crewmember is 1/2 your chance of success + //this makes higher % failures hurt more, don't get cocky space cowboy! + if(prob(success*5)) + var/lost_crew = remove_crewmember() + last_spaceport_action = "You failed to raid the spaceport! you lost [FU*-1] Fuel and [FO*-1] Food, AND [lost_crew] in your scramble to escape! ([FU]FI,[FO]FO,-Crew)" + if(emagged) + atom_say("WEEWOO WEEWOO, Spaceport Security en route!") + for(var/i, i<=3, i++) + var/mob/living/simple_animal/hostile/syndicate/ranged/orion/O = new/mob/living/simple_animal/hostile/syndicate/ranged/orion(get_turf(src)) + O.target = usr + + + fuel += FU + food += FO + event() + + else if(href_list["buyparts"]) + switch(text2num(href_list["buyparts"])) + if(1) //Engine Parts + engine++ + last_spaceport_action = "Bought Engine Parts" + if(2) //Hull Plates + hull++ + last_spaceport_action = "Bought Hull Plates" + if(3) //Spare Electronics + electronics++ + last_spaceport_action = "Bought Spare Electronics" + fuel -= 5 //they all cost 5 + event() + + else if(href_list["trade"]) + switch(text2num(href_list["trade"])) + if(1) //Fuel + fuel -= 5 + food += 5 + last_spaceport_action = "Traded Fuel for Food" + if(2) //Food + fuel += 5 + food -= 5 + last_spaceport_action = "Traded Food for Fuel" + event() + + add_fingerprint(usr) + updateUsrDialog() + busy = 0 + return + + +/obj/machinery/computer/arcade/orion_trail/proc/event() + eventdat = "

    [event]

    " + + switch(event) + if(ORION_TRAIL_RAIDERS) + eventdat += "Raiders have come aboard your ship!" + if(prob(50)) + var/sfood = rand(1,10) + var/sfuel = rand(1,10) + food -= sfood + fuel -= sfuel + eventdat += "
    They have stolen [sfood] Food and [sfuel] Fuel." + else if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] tried to fight back but was killed." + else + eventdat += "
    Fortunately you fended them off without any trouble." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_FLUX) + eventdat += "This region of space is highly turbulent.
    If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." + eventdat += "
    What will you do?" + eventdat += "

    Slow Down Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_ILLNESS) + eventdat += "A deadly illness has been contracted!" + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by the disease." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_BREAKDOWN) + eventdat += "Oh no! The engine has broken down!" + eventdat += "
    You can repair it with an engine part, or you can make repairs for 3 days." + if(engine >= 1) + eventdat += "

    Use PartWait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_MALFUNCTION) + eventdat += "The ship's systems are malfunctioning!" + eventdat += "
    You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." + if(electronics >= 1) + eventdat += "

    Use PartWait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_COLLISION) + eventdat += "Something hit us! Looks like there's some hull damage." + if(prob(25)) + var/sfood = rand(5,15) + var/sfuel = rand(5,15) + food -= sfood + fuel -= sfuel + eventdat += "
    [sfood] Food and [sfuel] Fuel was vented out into space." + if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
    [deadname] was killed by rapid depressurization." + eventdat += "
    You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." + if(hull >= 1) + eventdat += "

    Use PartWait

    " + else + eventdat += "

    Wait

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_BLACKHOLE) + eventdat += "You were swept away into the black hole." + eventdat += "

    Oh...

    " + eventdat += "

    Close

    " + settlers = list() + + if(ORION_TRAIL_LING) + eventdat += "Strange reports warn of changelings infiltrating crews on trips to Orion..." + if(settlers.len <= 2) + eventdat += "
    Your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..." + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + if(prob(10)) // "likely", I didn't say it was guaranteed! + lings_aboard = min(++lings_aboard,2) + else + if(lings_aboard) //less likely to stack lings + if(prob(20)) + lings_aboard = min(++lings_aboard,2) + else if(prob(70)) + lings_aboard = min(++lings_aboard,2) + + eventdat += "

    Kill a crewmember

    " + eventdat += "

    Risk it

    " + eventdat += "

    Close

    " + + if(ORION_TRAIL_LING_ATTACK) + if(lings_aboard <= 0) //shouldn't trigger, but hey. + eventdat += "Haha, fooled you, there are no changelings on board!" + eventdat += "
    (You should report this to a coder :S)" + else + var/ling1 = remove_crewmember() + var/ling2 = "" + if(lings_aboard >= 2) + ling2 = remove_crewmember() + + eventdat += "Oh no, some of your crew are Changelings!" + if(ling2) + eventdat += "
    [ling1] and [ling2]'s arms twist and contort into grotesque blades!" + else + eventdat += "
    [ling1]'s arm twists and contorts into a grotesque blade!" + + var/chance2attack = alive*20 + if(prob(chance2attack)) + var/chancetokill = 30*lings_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance + if(prob(chancetokill)) + var/deadguy = remove_crewmember() + eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] up to [deadguy] and capitulates them!" + else + eventdat += "
    You valiantly fight off the Changeling[ling2 ? "s":""]!" + eventdat += "
    You cut the Changeling[ling2 ? "s":""] up into meat... Eww" + if(ling2) + food += 30 + lings_aboard = max(0,lings_aboard-2) + else + food += 15 + lings_aboard = max(0,--lings_aboard) + else + eventdat += "
    The Changeling[ling2 ? "s":""] run[ling2 ? "":"s"] away, What wimps!" + if(ling2) + lings_aboard = max(0,lings_aboard-2) + else + lings_aboard = max(0,--lings_aboard) + + eventdat += "

    Continue

    " + eventdat += "

    Close

    " + + + if(ORION_TRAIL_SPACEPORT) + if(spaceport_raided) + eventdat += "The Spaceport is on high alert! They wont let you dock since you tried to attack them!" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Depart Spaceport

    " + eventdat += "

    Close

    " + else + eventdat += "You pull the ship up to dock at a nearby Spaceport, lucky find!" + eventdat += "
    This Spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." + eventdat += "
    Trading terms: FU = Fuel, FO = Food" + if(last_spaceport_action) + eventdat += "
    Last Spaceport Action: [last_spaceport_action]" + eventdat += "

    Crew:

    " + eventdat += english_list(settlers) + eventdat += "
    Food: [food] | Fuel: [fuel]" + eventdat += "
    Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + + + //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) + if(!spaceport_freebie && (fuel < 20 || food < 20)) + spaceport_freebie++ + var/FU = 10 + var/FO = 10 + var/freecrew = 0 + if(prob(30)) + FU = 25 + FO = 25 + + if(prob(10)) + add_crewmember() + freecrew++ + + eventdat += "
    The traders of the spaceport take pitty on you, and give you some food and fuel (+[FU]FU,+[FO]FO)" + if(freecrew) + eventdat += "
    You also gain a new crewmember!" + + fuel += FU + food += FO + + //CREW INTERACTIONS + eventdat += "

    Crew Management:

    " + + //Buy crew + if(food >= 10 && fuel >= 10) + eventdat += "

    Hire a new Crewmember (-10FU,-10FO)

    " + else + eventdat += "

    Cant afford a new Crewmember

    " + + //Sell crew + if(settlers.len > 1) + eventdat += "

    Sell crew for Fuel and Food (+15FU,+15FO)

    " + else + eventdat += "

    Cant afford to sell a Crewmember

    " + + //BUY/SELL STUFF + eventdat += "

    Spare Parts:

    " + + //Engine parts + if(fuel > 5) + eventdat += "

    Buy Engine Parts (-5FU)

    " + else + eventdat += "

    Cant afford to buy Engine Parts" + + //Hull plates + if(fuel > 5) + eventdat += "

    Buy Hull Plates (-5FU)

    " + else + eventdat += "

    Cant afford to buy Hull Plates" + + //Electronics + if(fuel > 5) + eventdat += "

    Buy Spare Electronics (-5FU)

    " + else + eventdat += "

    Cant afford to buy Spare Electronics" + + //Trade + if(fuel > 5) + eventdat += "

    Trade Fuel for Food (-5FU,+5FO)

    " + else + eventdat += "

    Cant afford to Trade Fuel for Food 5) + eventdat += "

    Trade Food for Fuel (+5FU,-5FO)

    " + else + eventdat += "

    Cant afford to Trade Food for Fuel= 1) //need to make sure we even have anyone to remove + removed = pick(safe2remove) + + if(removed) + if(lings_aboard && prob(40*lings_aboard)) //if there are 2 lings you're twice as likely to get one, obviously + lings_aboard = max(0,--lings_aboard) + settlers -= removed + alive-- + return removed + + +/obj/machinery/computer/arcade/orion_trail/proc/win() + playing = 0 + turns = 1 + atom_say("Congratulations, you made it to Orion!") + if(emagged) + new /obj/item/orion_ship(get_turf(src)) + message_admins("[key_name_admin(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") + log_game("[key_name(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") + else + var/score = alive + round(food/2) + round(fuel/5) + engine + hull + electronics - lings_aboard + prizevend(score) + emagged = 0 + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + +/obj/machinery/computer/arcade/orion_trail/emag_act(mob/user) + if(!emagged) + to_chat(user, "You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Realism Mode.") + name = "The Orion Trail: Realism Edition" + desc = "Learn how our ancestors got to Orion, and try not to die in the process!" + newgame() + emagged = 1 + +/mob/living/simple_animal/hostile/syndicate/ranged/orion + name = "spaceport security" + desc = "Premier corporate security forces for all spaceports found along the Orion Trail." + faction = list("orion") + loot = list() + del_on_death = TRUE + +/obj/item/orion_ship + name = "model settler ship" + desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..." + icon = 'icons/obj/toy.dmi' + icon_state = "ship" + w_class = WEIGHT_CLASS_SMALL + var/active = 0 //if the ship is on + +/obj/item/orion_ship/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(!active) + . += "There's a little switch on the bottom. It's flipped down." + else + . += "There's a little switch on the bottom. It's flipped up." + +/obj/item/orion_ship/attack_self(mob/user) //Minibomb-level explosion. Should probably be more because of how hard it is to survive the machine! Also, just over a 5-second fuse + if(active) + return + + message_admins("[key_name_admin(usr)] primed an explosive Orion ship for detonation.") + log_game("[key_name(usr)] primed an explosive Orion ship for detonation.") + + to_chat(user, "You flip the switch on the underside of [src].") + active = 1 + visible_message("[src] softly beeps and whirs to life!") + playsound(src.loc, 'sound/machines/defib_saftyon.ogg', 25, 1) + atom_say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.") + sleep(20) + visible_message("[src] begins to vibrate...") + atom_say("Uh, Port? Having some issues with our reactor, could you check it out? Over.") + sleep(30) + atom_say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 25, 1) + sleep(3.6) + visible_message("[src] explodes!") + explosion(src.loc, 1,2,4, flame_range = 3) + qdel(src) + + +#undef ORION_TRAIL_WINTURN +#undef ORION_TRAIL_RAIDERS +#undef ORION_TRAIL_FLUX +#undef ORION_TRAIL_ILLNESS +#undef ORION_TRAIL_BREAKDOWN +#undef ORION_TRAIL_LING +#undef ORION_TRAIL_LING_ATTACK +#undef ORION_TRAIL_MALFUNCTION +#undef ORION_TRAIL_COLLISION +#undef ORION_TRAIL_SPACEPORT +#undef ORION_TRAIL_BLACKHOLE diff --git a/code/game/machinery/computer/atmos_alert.dm b/code/game/machinery/computer/atmos_alert.dm index f8c2979e043b..a8e30eb09790 100644 --- a/code/game/machinery/computer/atmos_alert.dm +++ b/code/game/machinery/computer/atmos_alert.dm @@ -1,5 +1,5 @@ -var/global/list/priority_air_alarms = list() -var/global/list/minor_air_alarms = list() +GLOBAL_LIST_EMPTY(priority_air_alarms) +GLOBAL_LIST_EMPTY(minor_air_alarms) /obj/machinery/computer/atmos_alert @@ -67,12 +67,11 @@ var/global/list/minor_air_alarms = list() var/obj/machinery/alarm/air_alarm = alarm_source.source if(istype(air_alarm)) var/list/new_ref = list("atmos_reset" = 1) - air_alarm.Topic(href, new_ref, state = air_alarm_topic) + air_alarm.Topic(href, new_ref, state = GLOB.air_alarm_topic) update_icon() return 1 - -var/datum/topic_state/air_alarm_topic/air_alarm_topic = new() +GLOBAL_DATUM_INIT(air_alarm_topic, /datum/topic_state/air_alarm_topic, new) /datum/topic_state/air_alarm_topic/href_list(var/mob/user) var/list/extra_href = list() diff --git a/code/game/machinery/computer/brigcells.dm b/code/game/machinery/computer/brigcells.dm index 378c10d97839..a391bfa40219 100644 --- a/code/game/machinery/computer/brigcells.dm +++ b/code/game/machinery/computer/brigcells.dm @@ -30,7 +30,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/computer/brigcells/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/brigcells/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/list/timers = list() for(var/obj/machinery/door_timer/T in GLOB.celltimers_list) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 308b44e7a3cd..2f8321b57bb7 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -1,687 +1,687 @@ -/obj/structure/computerframe - density = 1 - anchored = 0 - name = "computer frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "0" - max_integrity = 100 - var/state = 0 - var/obj/item/circuitboard/circuit = null - var/base_mineral = /obj/item/stack/sheet/metal - -/obj/structure/computerframe/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - drop_computer_parts() - return ..() // will qdel the frame - -/obj/structure/computerframe/obj_break(damage_flag) - deconstruct() - -/obj/structure/computerframe/proc/drop_computer_parts() - new base_mineral(loc, 5) - if(circuit) - circuit.forceMove(loc) - circuit = null - if(state >= 3) - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - if(state >= 4) - new /obj/item/stack/sheet/glass(loc, 2) - -/obj/item/circuitboard - density = 0 - anchored = 0 - w_class = WEIGHT_CLASS_SMALL - name = "circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "id_mod" - item_state = "electronic" - origin_tech = "programming=2" - materials = list(MAT_GLASS=200) - var/id = null - var/frequency = null - var/build_path = null - var/board_type = "computer" - var/list/req_components = null - var/powernet = null - var/list/records = null - var/frame_desc = null - var/contain_parts = 1 - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/item/circuitboard/computer - board_type = "computer" - -/obj/item/circuitboard/machine - board_type = "machine" - -/obj/item/circuitboard/examine(mob/user) - . = ..() - if(LAZYLEN(req_components)) - var/list/nice_list = list() - for(var/B in req_components) - var/atom/A = B - if(!ispath(A)) - continue - nice_list += list("[req_components[A]] [initial(A.name)]") - . += "Required components: [english_list(nice_list)]." - -/obj/item/circuitboard/message_monitor - name = "Circuit board (Message Monitor)" - build_path = /obj/machinery/computer/message_monitor - origin_tech = "programming=2" -/obj/item/circuitboard/camera - name = "Circuit board (Camera Monitor)" - build_path = /obj/machinery/computer/security - origin_tech = "programming=2;combat=2" - -/obj/item/circuitboard/camera/telescreen - name = "Circuit board (Telescreen)" - build_path = /obj/machinery/computer/security/telescreen -/obj/item/circuitboard/camera/telescreen/entertainment - name = "Circuit board (Entertainment Monitor)" - build_path = /obj/machinery/computer/security/telescreen/entertainment -/obj/item/circuitboard/camera/wooden_tv - name = "Circuit board (Wooden TV)" - build_path = /obj/machinery/computer/security/wooden_tv -/obj/item/circuitboard/camera/mining - name = "Circuit board (Outpost Camera Monitor)" - build_path = /obj/machinery/computer/security/mining -/obj/item/circuitboard/camera/engineering - name = "Circuit board (Engineering Camera Monitor)" - build_path = /obj/machinery/computer/security/engineering - - -/obj/item/circuitboard/xenobiology - name = "Circuit board (Xenobiology Console)" - build_path = /obj/machinery/computer/camera_advanced/xenobio - origin_tech = "programming=3;biotech=3" -/obj/item/circuitboard/aicore - name = "Circuit board (AI Core)" - origin_tech = "programming=3" - board_type = "other" -/obj/item/circuitboard/aiupload - name = "Circuit board (AI Upload)" - build_path = /obj/machinery/computer/aiupload - origin_tech = "programming=4;engineering=4" -/obj/item/circuitboard/borgupload - name = "Circuit board (Cyborg Upload)" - build_path = /obj/machinery/computer/borgupload - origin_tech = "programming=4;engineering=4" -/obj/item/circuitboard/med_data - name = "Circuit board (Medical Records)" - build_path = /obj/machinery/computer/med_data - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/pandemic - name = "circuit board (PanD.E.M.I.C. 2200)" - build_path = /obj/machinery/computer/pandemic - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/scan_consolenew - name = "Circuit board (DNA Machine)" - build_path = /obj/machinery/computer/scan_consolenew - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/communications - name = "Circuit board (Communications Console)" - build_path = /obj/machinery/computer/communications - origin_tech = "programming=3;magnets=3" -/obj/item/circuitboard/card - name = "Circuit board (ID Computer)" - build_path = /obj/machinery/computer/card - origin_tech = "programming=3" -/obj/item/circuitboard/card/minor - name = "Circuit board (Dept ID Computer)" - build_path = /obj/machinery/computer/card/minor - var/target_dept = TARGET_DEPT_GENERIC -/obj/item/circuitboard/card/minor/hos - name = "Circuit board (Sec ID Computer)" - build_path = /obj/machinery/computer/card/minor/hos - target_dept = TARGET_DEPT_SEC -/obj/item/circuitboard/card/minor/cmo - name = "Circuit board (Medical ID Computer)" - build_path = /obj/machinery/computer/card/minor/cmo - target_dept = TARGET_DEPT_MED -/obj/item/circuitboard/card/minor/rd - name = "Circuit board (Science ID Computer)" - build_path = /obj/machinery/computer/card/minor/rd - target_dept = TARGET_DEPT_SCI -/obj/item/circuitboard/card/minor/ce - name = "Circuit board (Engineering ID Computer)" - build_path = /obj/machinery/computer/card/minor/ce - target_dept = TARGET_DEPT_ENG -/obj/item/circuitboard/card/centcom - name = "Circuit board (CentComm ID Computer)" - build_path = /obj/machinery/computer/card/centcom -/obj/item/circuitboard/teleporter - name = "Circuit board (Teleporter Console)" - build_path = /obj/machinery/computer/teleporter - origin_tech = "programming=3;bluespace=3;plasmatech=3" -/obj/item/circuitboard/secure_data - name = "Circuit board (Security Records)" - build_path = /obj/machinery/computer/secure_data - origin_tech = "programming=2;combat=2" -/obj/item/circuitboard/skills - name = "Circuit board (Employment Records)" - build_path = /obj/machinery/computer/skills -/obj/item/circuitboard/stationalert_engineering - name = "Circuit Board (Station Alert Console (Engineering))" - build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_security - name = "Circuit Board (Station Alert Console (Security))" - build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_all - name = "Circuit Board (Station Alert Console (All))" - build_path = /obj/machinery/computer/station_alert/all -/obj/item/circuitboard/atmos_alert - name = "Circuit Board (Atmospheric Alert Computer)" - build_path = /obj/machinery/computer/atmos_alert -/obj/item/circuitboard/atmoscontrol - name = "Circuit Board (Central Atmospherics Computer)" - build_path = /obj/machinery/computer/atmoscontrol -/obj/item/circuitboard/air_management - name = "Circuit board (Atmospheric Monitor)" - build_path = /obj/machinery/computer/general_air_control -/obj/item/circuitboard/injector_control - name = "Circuit board (Injector Control)" - build_path = /obj/machinery/computer/general_air_control/fuel_injection -/obj/item/circuitboard/pod - name = "Circuit board (Massdriver Control)" - build_path = /obj/machinery/computer/pod -/obj/item/circuitboard/pod/deathsquad - name = "Circuit board (Deathsquad Massdriver control)" - build_path = /obj/machinery/computer/pod/deathsquad -/obj/item/circuitboard/robotics - name = "Circuit board (Robotics Control Console)" - build_path = /obj/machinery/computer/robotics - origin_tech = "programming=3" -/obj/item/circuitboard/drone_control - name = "Circuit board (Drone Control)" - build_path = /obj/machinery/computer/drone_control - origin_tech = "programming=3" -/obj/item/circuitboard/cloning - name = "Circuit board (Cloning Machine Console)" - build_path = /obj/machinery/computer/cloning - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/arcade/battle - name = "circuit board (Arcade Battle)" - build_path = /obj/machinery/computer/arcade/battle - origin_tech = "programming=1" -/obj/item/circuitboard/arcade/orion_trail - name = "circuit board (Orion Trail)" - build_path = /obj/machinery/computer/arcade/orion_trail - origin_tech = "programming=1" -/obj/item/circuitboard/solar_control - name = "Circuit board (Solar Control)" - build_path = /obj/machinery/power/solar_control - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/powermonitor - name = "Circuit board (Power Monitor)" - build_path = /obj/machinery/computer/monitor - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/powermonitor/secret - name = "Circuit board (Outdated Power Monitor)" - build_path = /obj/machinery/computer/monitor/secret - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/olddoor - name = "Circuit board (DoorMex)" - build_path = /obj/machinery/computer/pod/old -/obj/item/circuitboard/syndicatedoor - name = "Circuit board (ProComp Executive)" - build_path = /obj/machinery/computer/pod/old/syndicate -/obj/item/circuitboard/swfdoor - name = "Circuit board (Magix)" - build_path = /obj/machinery/computer/pod/old/swf -/obj/item/circuitboard/prisoner - name = "Circuit board (Prisoner Management)" - build_path = /obj/machinery/computer/prisoner -/obj/item/circuitboard/brigcells - name = "Circuit board (Brig Cell Control)" - build_path = /obj/machinery/computer/brigcells - - -// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever -/obj/item/circuitboard/rdconsole - name = "Circuit Board (RD Console)" - desc = "Swipe a Scientist level ID or higher to reconfigure." - build_path = /obj/machinery/computer/rdconsole/core - req_access = list(ACCESS_TOX) // This is for adjusting the type of computer we're building - in case something messes up the pre-existing robotics or mechanics consoles - var/access_types = list("R&D Core", "Robotics", "E.X.P.E.R.I-MENTOR", "Mechanics", "Public") - id = 1 -/obj/item/circuitboard/rdconsole/robotics - name = "Circuit Board (RD Console - Robotics)" - build_path = /obj/machinery/computer/rdconsole/robotics - id = 2 -/obj/item/circuitboard/rdconsole/experiment - name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/computer/rdconsole/experiment - id = 3 -/obj/item/circuitboard/rdconsole/mechanics - name = "Circuit Board (RD Console - Mechanics)" - build_path = /obj/machinery/computer/rdconsole/mechanics - id = 4 -/obj/item/circuitboard/rdconsole/public - name = "Circuit Board (RD Console - Public)" - build_path = /obj/machinery/computer/rdconsole/public - id = 5 - - -/obj/item/circuitboard/mecha_control - name = "Circuit Board (Exosuit Control Console)" - build_path = /obj/machinery/computer/mecha -/obj/item/circuitboard/pod_locater - name = "Circuit Board (Pod Location Console)" - build_path = /obj/machinery/computer/podtracker -/obj/item/circuitboard/rdservercontrol - name = "Circuit Board (RD Server Control)" - build_path = /obj/machinery/computer/rdservercontrol -/obj/item/circuitboard/crew - name = "Circuit board (Crew Monitoring Computer)" - build_path = /obj/machinery/computer/crew - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/mech_bay_power_console - name = "Circuit board (Mech Bay Power Control Console)" - build_path = /obj/machinery/computer/mech_bay_power_console - origin_tech = "programming=3;powerstorage=3" -/obj/item/circuitboard/ordercomp - name = "Circuit board (Supply Ordering Console)" - build_path = /obj/machinery/computer/ordercomp - origin_tech = "programming=3" -/obj/item/circuitboard/supplycomp - name = "Circuit board (Supply Shuttle Console)" - build_path = /obj/machinery/computer/supplycomp - origin_tech = "programming=3" - var/contraband_enabled = 0 - -/obj/item/circuitboard/operating - name = "Circuit board (Operating Computer)" - build_path = /obj/machinery/computer/operating - origin_tech = "programming=2;biotech=3" -/obj/item/circuitboard/comm_monitor - name = "Circuit board (Telecommunications Monitor)" - build_path = /obj/machinery/computer/telecomms/monitor - origin_tech = "programming=3;magnets=3;bluespace=2" -/obj/item/circuitboard/comm_server - name = "Circuit board (Telecommunications Server Monitor)" - build_path = /obj/machinery/computer/telecomms/server - origin_tech = "programming=3;magnets=3;bluespace=2" -/obj/item/circuitboard/comm_traffic - name = "Circuitboard (Telecommunications Traffic Control)" - build_path = /obj/machinery/computer/telecomms/traffic - origin_tech = "programming=3;magnets=3;bluespace=2" - - -/obj/item/circuitboard/shuttle - name = "circuit board (Shuttle)" - build_path = /obj/machinery/computer/shuttle - var/shuttleId - var/possible_destinations = "" - -/obj/item/circuitboard/labor_shuttle - name = "circuit Board (Labor Shuttle)" - build_path = /obj/machinery/computer/shuttle/labor -/obj/item/circuitboard/labor_shuttle/one_way - name = "circuit Board (Prisoner Shuttle Console)" - build_path = /obj/machinery/computer/shuttle/labor/one_way -/obj/item/circuitboard/ferry - name = "circuit Board (Transport Ferry)" - build_path = /obj/machinery/computer/shuttle/ferry -/obj/item/circuitboard/ferry/request - name = "circuit Board (Transport Ferry Console)" - build_path = /obj/machinery/computer/shuttle/ferry/request -/obj/item/circuitboard/mining_shuttle - name = "circuit Board (Mining Shuttle)" - build_path = /obj/machinery/computer/shuttle/mining -/obj/item/circuitboard/white_ship - name = "circuit Board (White Ship)" - build_path = /obj/machinery/computer/shuttle/white_ship -/obj/item/circuitboard/shuttle/syndicate - name = "circuit board (Syndicate Shuttle)" - build_path = /obj/machinery/computer/shuttle/syndicate -/obj/item/circuitboard/shuttle/syndicate/recall - name = "circuit board (Syndicate Shuttle Recall Terminal)" - build_path = /obj/machinery/computer/shuttle/syndicate/recall -/obj/item/circuitboard/shuttle/syndicate/drop_pod - name = "circuit board (Syndicate Drop Pod)" - build_path = /obj/machinery/computer/shuttle/syndicate/drop_pod -/obj/item/circuitboard/shuttle/golem_ship - name = "circuit Board (Golem Ship)" - build_path = /obj/machinery/computer/shuttle/golem_ship - -/obj/item/circuitboard/HolodeckControl - name = "Circuit board (Holodeck Control)" - build_path = /obj/machinery/computer/HolodeckControl - origin_tech = "programming=4" -/obj/item/circuitboard/aifixer - name = "Circuit board (AI Integrity Restorer)" - build_path = /obj/machinery/computer/aifixer - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/area_atmos - name = "Circuit board (Area Air Control)" - build_path = /obj/machinery/computer/area_atmos - origin_tech = "programming=2" -/obj/item/circuitboard/telesci_console - name = "Circuit board (Telepad Control Console)" - build_path = /obj/machinery/computer/telescience - origin_tech = "programming=3;bluespace=3;plasmatech=4" - -/obj/item/circuitboard/atmos_automation - name = "Circuit board (Atmospherics Automation)" - build_path = /obj/machinery/computer/general_air_control/atmos_automation -/obj/item/circuitboard/large_tank_control - name = "Circuit board (Atmospheric Tank Control)" - build_path = /obj/machinery/computer/general_air_control/large_tank_control - origin_tech = "programming=2;engineering=3;materials=2" - -/obj/item/circuitboard/turbine_computer - name = "circuit board (Turbine Computer)" - build_path = /obj/machinery/computer/turbine_computer - origin_tech = "programming=4;engineering=4;powerstorage=4" - -/obj/item/circuitboard/HONKputer - name = "Circuit board (HONKputer)" - build_path = /obj/machinery/computer/HONKputer - origin_tech = "programming=2" - icon = 'icons/obj/machines/HONKputer.dmi' - icon_state = "bananium_board" - board_type = "honkcomputer" - - -/obj/item/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I,/obj/item/multitool)) - var/catastasis = contraband_enabled - var/opposite_catastasis - if(catastasis) - opposite_catastasis = "STANDARD" - catastasis = "BROAD" - else - opposite_catastasis = "BROAD" - catastasis = "STANDARD" - - switch( alert("Current receiver spectrum is set to: [catastasis]","Multitool-Circuitboard interface","Switch to [opposite_catastasis]","Cancel") ) - //switch( alert("Current receiver spectrum is set to: " {(contraband_enabled) ? ("BROAD") : ("STANDARD")} , "Multitool-Circuitboard interface" , "Switch to " {(contraband_enabled) ? ("STANDARD") : ("BROAD")}, "Cancel") ) - if("Switch to STANDARD","Switch to BROAD") - contraband_enabled = !contraband_enabled - - if("Cancel") - return - else - to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") - return - return ..() - -/obj/item/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I,/obj/item/card/id)||istype(I, /obj/item/pda)) - if(allowed(user)) - user.visible_message("\the [user] waves [user.p_their()] ID past the [src]'s access protocol scanner.", "You swipe your ID past the [src]'s access protocol scanner.") - var/console_choice = input(user, "What do you want to configure the access to?", "Access Modification", "R&D Core") as null|anything in access_types - if(console_choice == null) - return - switch(console_choice) - if("R&D Core") - name = "Circuit Board (RD Console)" - build_path = /obj/machinery/computer/rdconsole/core - id = 1 - if("Robotics") - name = "Circuit Board (RD Console - Robotics)" - build_path = /obj/machinery/computer/rdconsole/robotics - id = 2 - if("E.X.P.E.R.I-MENTOR") - name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/computer/rdconsole/experiment - id = 3 - if("Mechanics") - name = "Circuit Board (RD Console - Mechanics)" - build_path = /obj/machinery/computer/rdconsole/mechanics - id = 4 - if("Public") - name = "Circuit Board (RD Console - Public)" - build_path = /obj/machinery/computer/rdconsole/public - id = 5 - - to_chat(user, "Access protocols set to [console_choice].") - else - to_chat(user, "Access Denied") - return - return ..() - -/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params) - switch(state) - if(0) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You wrench the frame into place.") - anchored = 1 - state = 1 - return - if(1) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You unfasten the frame.") - anchored = 0 - state = 0 - return - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "computer") - playsound(loc, B.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - icon_state = "1" - circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - return - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = 2 - icon_state = "2" - return - if(istype(P, /obj/item/crowbar) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = 1 - icon_state = "0" - circuit.loc = loc - circuit = null - return - if(2) - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = 1 - icon_state = "1" - return - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - if(3) - if(istype(P, /obj/item/wirecutters)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 2 - icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - return - if(istype(P, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) - playsound(loc, G.usesound, 50, 1) - to_chat(user, "You start to add the glass panel to the frame.") - if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = 4 - icon_state = "4" - else - to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") - return - else - to_chat(user, "You need two sheets of glass for this.") - return - if(4) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = 3 - icon_state = "3" - new /obj/item/stack/sheet/glass(loc, 2) - return - if(istype(P, /obj/item/screwdriver)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new circuit.build_path (loc) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - if(istype(circuit,/obj/item/circuitboard/supplycomp)) - var/obj/machinery/computer/supplycomp/SC = B - var/obj/item/circuitboard/supplycomp/C = circuit - SC.can_order_contraband = C.contraband_enabled - qdel(src) - return - if(user.a_intent == INTENT_HARM) - return ..() - - -/obj/structure/computerframe/welder_act(mob/user, obj/item/I) - if(state) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 50, volume = I.tool_volume) && !state) - to_chat(user, "You deconstruct [src].") - deconstruct(TRUE) - - - -/obj/structure/computerframe/HONKputer - name = "Bananium Computer-frame" - icon = 'icons/obj/machines/HONKputer.dmi' - base_mineral = /obj/item/stack/sheet/mineral/bananium - -/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params) - switch(state) - if(0) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20, target = src)) - to_chat(user, "You wrench the frame into place.") - anchored = 1 - state = 1 - if(1) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You unfasten the frame.") - anchored = 0 - state = 0 - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "honkcomputer") - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - icon_state = "1" - circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = 2 - icon_state = "2" - if(istype(P, /obj/item/crowbar) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = 1 - icon_state = "0" - circuit.loc = loc - circuit = null - return - if(2) - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = 1 - icon_state = "1" - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - if(3) - if(istype(P, /obj/item/wirecutters)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 2 - icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - - if(istype(P, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) - playsound(loc, G.usesound, 50, 1) - to_chat(user, "You start to add the glass panel to the frame.") - if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = 4 - icon_state = "4" - else - to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") - return - else - to_chat(user, "You need two sheets of glass for this.") - return - if(4) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = 3 - icon_state = "3" - new /obj/item/stack/sheet/glass(loc, 2) - if(istype(P, /obj/item/screwdriver)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new circuit.build_path (loc) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - qdel(src) - return - return ..() \ No newline at end of file +/obj/structure/computerframe + density = 1 + anchored = 0 + name = "computer frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "0" + max_integrity = 100 + var/state = 0 + var/obj/item/circuitboard/circuit = null + var/base_mineral = /obj/item/stack/sheet/metal + +/obj/structure/computerframe/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + drop_computer_parts() + return ..() // will qdel the frame + +/obj/structure/computerframe/obj_break(damage_flag) + deconstruct() + +/obj/structure/computerframe/proc/drop_computer_parts() + new base_mineral(loc, 5) + if(circuit) + circuit.forceMove(loc) + circuit = null + if(state >= 3) + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + if(state >= 4) + new /obj/item/stack/sheet/glass(loc, 2) + +/obj/item/circuitboard + density = 0 + anchored = 0 + w_class = WEIGHT_CLASS_SMALL + name = "circuit board" + icon = 'icons/obj/module.dmi' + icon_state = "id_mod" + item_state = "electronic" + origin_tech = "programming=2" + materials = list(MAT_GLASS=200) + var/id = null + var/frequency = null + var/build_path = null + var/board_type = "computer" + var/list/req_components = null + var/powernet = null + var/list/records = null + var/frame_desc = null + var/contain_parts = 1 + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/item/circuitboard/computer + board_type = "computer" + +/obj/item/circuitboard/machine + board_type = "machine" + +/obj/item/circuitboard/examine(mob/user) + . = ..() + if(LAZYLEN(req_components)) + var/list/nice_list = list() + for(var/B in req_components) + var/atom/A = B + if(!ispath(A)) + continue + nice_list += list("[req_components[A]] [initial(A.name)]") + . += "Required components: [english_list(nice_list)]." + +/obj/item/circuitboard/message_monitor + name = "Circuit board (Message Monitor)" + build_path = /obj/machinery/computer/message_monitor + origin_tech = "programming=2" +/obj/item/circuitboard/camera + name = "Circuit board (Camera Monitor)" + build_path = /obj/machinery/computer/security + origin_tech = "programming=2;combat=2" + +/obj/item/circuitboard/camera/telescreen + name = "Circuit board (Telescreen)" + build_path = /obj/machinery/computer/security/telescreen +/obj/item/circuitboard/camera/telescreen/entertainment + name = "Circuit board (Entertainment Monitor)" + build_path = /obj/machinery/computer/security/telescreen/entertainment +/obj/item/circuitboard/camera/wooden_tv + name = "Circuit board (Wooden TV)" + build_path = /obj/machinery/computer/security/wooden_tv +/obj/item/circuitboard/camera/mining + name = "Circuit board (Outpost Camera Monitor)" + build_path = /obj/machinery/computer/security/mining +/obj/item/circuitboard/camera/engineering + name = "Circuit board (Engineering Camera Monitor)" + build_path = /obj/machinery/computer/security/engineering + + +/obj/item/circuitboard/xenobiology + name = "Circuit board (Xenobiology Console)" + build_path = /obj/machinery/computer/camera_advanced/xenobio + origin_tech = "programming=3;biotech=3" +/obj/item/circuitboard/aicore + name = "Circuit board (AI Core)" + origin_tech = "programming=3" + board_type = "other" +/obj/item/circuitboard/aiupload + name = "Circuit board (AI Upload)" + build_path = /obj/machinery/computer/aiupload + origin_tech = "programming=4;engineering=4" +/obj/item/circuitboard/borgupload + name = "Circuit board (Cyborg Upload)" + build_path = /obj/machinery/computer/borgupload + origin_tech = "programming=4;engineering=4" +/obj/item/circuitboard/med_data + name = "Circuit board (Medical Records)" + build_path = /obj/machinery/computer/med_data + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/pandemic + name = "circuit board (PanD.E.M.I.C. 2200)" + build_path = /obj/machinery/computer/pandemic + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/scan_consolenew + name = "Circuit board (DNA Machine)" + build_path = /obj/machinery/computer/scan_consolenew + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/communications + name = "Circuit board (Communications Console)" + build_path = /obj/machinery/computer/communications + origin_tech = "programming=3;magnets=3" +/obj/item/circuitboard/card + name = "Circuit board (ID Computer)" + build_path = /obj/machinery/computer/card + origin_tech = "programming=3" +/obj/item/circuitboard/card/minor + name = "Circuit board (Dept ID Computer)" + build_path = /obj/machinery/computer/card/minor + var/target_dept = TARGET_DEPT_GENERIC +/obj/item/circuitboard/card/minor/hos + name = "Circuit board (Sec ID Computer)" + build_path = /obj/machinery/computer/card/minor/hos + target_dept = TARGET_DEPT_SEC +/obj/item/circuitboard/card/minor/cmo + name = "Circuit board (Medical ID Computer)" + build_path = /obj/machinery/computer/card/minor/cmo + target_dept = TARGET_DEPT_MED +/obj/item/circuitboard/card/minor/rd + name = "Circuit board (Science ID Computer)" + build_path = /obj/machinery/computer/card/minor/rd + target_dept = TARGET_DEPT_SCI +/obj/item/circuitboard/card/minor/ce + name = "Circuit board (Engineering ID Computer)" + build_path = /obj/machinery/computer/card/minor/ce + target_dept = TARGET_DEPT_ENG +/obj/item/circuitboard/card/centcom + name = "Circuit board (CentComm ID Computer)" + build_path = /obj/machinery/computer/card/centcom +/obj/item/circuitboard/teleporter + name = "Circuit board (Teleporter Console)" + build_path = /obj/machinery/computer/teleporter + origin_tech = "programming=3;bluespace=3;plasmatech=3" +/obj/item/circuitboard/secure_data + name = "Circuit board (Security Records)" + build_path = /obj/machinery/computer/secure_data + origin_tech = "programming=2;combat=2" +/obj/item/circuitboard/skills + name = "Circuit board (Employment Records)" + build_path = /obj/machinery/computer/skills +/obj/item/circuitboard/stationalert_engineering + name = "Circuit Board (Station Alert Console (Engineering))" + build_path = /obj/machinery/computer/station_alert +/obj/item/circuitboard/stationalert_security + name = "Circuit Board (Station Alert Console (Security))" + build_path = /obj/machinery/computer/station_alert +/obj/item/circuitboard/stationalert_all + name = "Circuit Board (Station Alert Console (All))" + build_path = /obj/machinery/computer/station_alert/all +/obj/item/circuitboard/atmos_alert + name = "Circuit Board (Atmospheric Alert Computer)" + build_path = /obj/machinery/computer/atmos_alert +/obj/item/circuitboard/atmoscontrol + name = "Circuit Board (Central Atmospherics Computer)" + build_path = /obj/machinery/computer/atmoscontrol +/obj/item/circuitboard/air_management + name = "Circuit board (Atmospheric Monitor)" + build_path = /obj/machinery/computer/general_air_control +/obj/item/circuitboard/injector_control + name = "Circuit board (Injector Control)" + build_path = /obj/machinery/computer/general_air_control/fuel_injection +/obj/item/circuitboard/pod + name = "Circuit board (Massdriver Control)" + build_path = /obj/machinery/computer/pod +/obj/item/circuitboard/pod/deathsquad + name = "Circuit board (Deathsquad Massdriver control)" + build_path = /obj/machinery/computer/pod/deathsquad +/obj/item/circuitboard/robotics + name = "Circuit board (Robotics Control Console)" + build_path = /obj/machinery/computer/robotics + origin_tech = "programming=3" +/obj/item/circuitboard/drone_control + name = "Circuit board (Drone Control)" + build_path = /obj/machinery/computer/drone_control + origin_tech = "programming=3" +/obj/item/circuitboard/cloning + name = "Circuit board (Cloning Machine Console)" + build_path = /obj/machinery/computer/cloning + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/arcade/battle + name = "circuit board (Arcade Battle)" + build_path = /obj/machinery/computer/arcade/battle + origin_tech = "programming=1" +/obj/item/circuitboard/arcade/orion_trail + name = "circuit board (Orion Trail)" + build_path = /obj/machinery/computer/arcade/orion_trail + origin_tech = "programming=1" +/obj/item/circuitboard/solar_control + name = "Circuit board (Solar Control)" + build_path = /obj/machinery/power/solar_control + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/powermonitor + name = "Circuit board (Power Monitor)" + build_path = /obj/machinery/computer/monitor + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/powermonitor/secret + name = "Circuit board (Outdated Power Monitor)" + build_path = /obj/machinery/computer/monitor/secret + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/olddoor + name = "Circuit board (DoorMex)" + build_path = /obj/machinery/computer/pod/old +/obj/item/circuitboard/syndicatedoor + name = "Circuit board (ProComp Executive)" + build_path = /obj/machinery/computer/pod/old/syndicate +/obj/item/circuitboard/swfdoor + name = "Circuit board (Magix)" + build_path = /obj/machinery/computer/pod/old/swf +/obj/item/circuitboard/prisoner + name = "Circuit board (Prisoner Management)" + build_path = /obj/machinery/computer/prisoner +/obj/item/circuitboard/brigcells + name = "Circuit board (Brig Cell Control)" + build_path = /obj/machinery/computer/brigcells + + +// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever +/obj/item/circuitboard/rdconsole + name = "Circuit Board (RD Console)" + desc = "Swipe a Scientist level ID or higher to reconfigure." + build_path = /obj/machinery/computer/rdconsole/core + req_access = list(ACCESS_TOX) // This is for adjusting the type of computer we're building - in case something messes up the pre-existing robotics or mechanics consoles + var/access_types = list("R&D Core", "Robotics", "E.X.P.E.R.I-MENTOR", "Mechanics", "Public") + id = 1 +/obj/item/circuitboard/rdconsole/robotics + name = "Circuit Board (RD Console - Robotics)" + build_path = /obj/machinery/computer/rdconsole/robotics + id = 2 +/obj/item/circuitboard/rdconsole/experiment + name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/computer/rdconsole/experiment + id = 3 +/obj/item/circuitboard/rdconsole/mechanics + name = "Circuit Board (RD Console - Mechanics)" + build_path = /obj/machinery/computer/rdconsole/mechanics + id = 4 +/obj/item/circuitboard/rdconsole/public + name = "Circuit Board (RD Console - Public)" + build_path = /obj/machinery/computer/rdconsole/public + id = 5 + + +/obj/item/circuitboard/mecha_control + name = "Circuit Board (Exosuit Control Console)" + build_path = /obj/machinery/computer/mecha +/obj/item/circuitboard/pod_locater + name = "Circuit Board (Pod Location Console)" + build_path = /obj/machinery/computer/podtracker +/obj/item/circuitboard/rdservercontrol + name = "Circuit Board (RD Server Control)" + build_path = /obj/machinery/computer/rdservercontrol +/obj/item/circuitboard/crew + name = "Circuit board (Crew Monitoring Computer)" + build_path = /obj/machinery/computer/crew + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/mech_bay_power_console + name = "Circuit board (Mech Bay Power Control Console)" + build_path = /obj/machinery/computer/mech_bay_power_console + origin_tech = "programming=3;powerstorage=3" +/obj/item/circuitboard/ordercomp + name = "Circuit board (Supply Ordering Console)" + build_path = /obj/machinery/computer/ordercomp + origin_tech = "programming=3" +/obj/item/circuitboard/supplycomp + name = "Circuit board (Supply Shuttle Console)" + build_path = /obj/machinery/computer/supplycomp + origin_tech = "programming=3" + var/contraband_enabled = 0 + +/obj/item/circuitboard/operating + name = "Circuit board (Operating Computer)" + build_path = /obj/machinery/computer/operating + origin_tech = "programming=2;biotech=3" +/obj/item/circuitboard/comm_monitor + name = "Circuit board (Telecommunications Monitor)" + build_path = /obj/machinery/computer/telecomms/monitor + origin_tech = "programming=3;magnets=3;bluespace=2" +/obj/item/circuitboard/comm_server + name = "Circuit board (Telecommunications Server Monitor)" + build_path = /obj/machinery/computer/telecomms/server + origin_tech = "programming=3;magnets=3;bluespace=2" +/obj/item/circuitboard/comm_traffic + name = "Circuitboard (Telecommunications Traffic Control)" + build_path = /obj/machinery/computer/telecomms/traffic + origin_tech = "programming=3;magnets=3;bluespace=2" + + +/obj/item/circuitboard/shuttle + name = "circuit board (Shuttle)" + build_path = /obj/machinery/computer/shuttle + var/shuttleId + var/possible_destinations = "" + +/obj/item/circuitboard/labor_shuttle + name = "circuit Board (Labor Shuttle)" + build_path = /obj/machinery/computer/shuttle/labor +/obj/item/circuitboard/labor_shuttle/one_way + name = "circuit Board (Prisoner Shuttle Console)" + build_path = /obj/machinery/computer/shuttle/labor/one_way +/obj/item/circuitboard/ferry + name = "circuit Board (Transport Ferry)" + build_path = /obj/machinery/computer/shuttle/ferry +/obj/item/circuitboard/ferry/request + name = "circuit Board (Transport Ferry Console)" + build_path = /obj/machinery/computer/shuttle/ferry/request +/obj/item/circuitboard/mining_shuttle + name = "circuit Board (Mining Shuttle)" + build_path = /obj/machinery/computer/shuttle/mining +/obj/item/circuitboard/white_ship + name = "circuit Board (White Ship)" + build_path = /obj/machinery/computer/shuttle/white_ship +/obj/item/circuitboard/shuttle/syndicate + name = "circuit board (Syndicate Shuttle)" + build_path = /obj/machinery/computer/shuttle/syndicate +/obj/item/circuitboard/shuttle/syndicate/recall + name = "circuit board (Syndicate Shuttle Recall Terminal)" + build_path = /obj/machinery/computer/shuttle/syndicate/recall +/obj/item/circuitboard/shuttle/syndicate/drop_pod + name = "circuit board (Syndicate Drop Pod)" + build_path = /obj/machinery/computer/shuttle/syndicate/drop_pod +/obj/item/circuitboard/shuttle/golem_ship + name = "circuit Board (Golem Ship)" + build_path = /obj/machinery/computer/shuttle/golem_ship + +/obj/item/circuitboard/HolodeckControl + name = "Circuit board (Holodeck Control)" + build_path = /obj/machinery/computer/HolodeckControl + origin_tech = "programming=4" +/obj/item/circuitboard/aifixer + name = "Circuit board (AI Integrity Restorer)" + build_path = /obj/machinery/computer/aifixer + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/area_atmos + name = "Circuit board (Area Air Control)" + build_path = /obj/machinery/computer/area_atmos + origin_tech = "programming=2" +/obj/item/circuitboard/telesci_console + name = "Circuit board (Telepad Control Console)" + build_path = /obj/machinery/computer/telescience + origin_tech = "programming=3;bluespace=3;plasmatech=4" + +/obj/item/circuitboard/atmos_automation + name = "Circuit board (Atmospherics Automation)" + build_path = /obj/machinery/computer/general_air_control/atmos_automation +/obj/item/circuitboard/large_tank_control + name = "Circuit board (Atmospheric Tank Control)" + build_path = /obj/machinery/computer/general_air_control/large_tank_control + origin_tech = "programming=2;engineering=3;materials=2" + +/obj/item/circuitboard/turbine_computer + name = "circuit board (Turbine Computer)" + build_path = /obj/machinery/computer/turbine_computer + origin_tech = "programming=4;engineering=4;powerstorage=4" + +/obj/item/circuitboard/HONKputer + name = "Circuit board (HONKputer)" + build_path = /obj/machinery/computer/HONKputer + origin_tech = "programming=2" + icon = 'icons/obj/machines/HONKputer.dmi' + icon_state = "bananium_board" + board_type = "honkcomputer" + + +/obj/item/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I,/obj/item/multitool)) + var/catastasis = contraband_enabled + var/opposite_catastasis + if(catastasis) + opposite_catastasis = "STANDARD" + catastasis = "BROAD" + else + opposite_catastasis = "BROAD" + catastasis = "STANDARD" + + switch( alert("Current receiver spectrum is set to: [catastasis]","Multitool-Circuitboard interface","Switch to [opposite_catastasis]","Cancel") ) + //switch( alert("Current receiver spectrum is set to: " {(contraband_enabled) ? ("BROAD") : ("STANDARD")} , "Multitool-Circuitboard interface" , "Switch to " {(contraband_enabled) ? ("STANDARD") : ("BROAD")}, "Cancel") ) + if("Switch to STANDARD","Switch to BROAD") + contraband_enabled = !contraband_enabled + + if("Cancel") + return + else + to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") + return + return ..() + +/obj/item/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I,/obj/item/card/id)||istype(I, /obj/item/pda)) + if(allowed(user)) + user.visible_message("\the [user] waves [user.p_their()] ID past the [src]'s access protocol scanner.", "You swipe your ID past the [src]'s access protocol scanner.") + var/console_choice = input(user, "What do you want to configure the access to?", "Access Modification", "R&D Core") as null|anything in access_types + if(console_choice == null) + return + switch(console_choice) + if("R&D Core") + name = "Circuit Board (RD Console)" + build_path = /obj/machinery/computer/rdconsole/core + id = 1 + if("Robotics") + name = "Circuit Board (RD Console - Robotics)" + build_path = /obj/machinery/computer/rdconsole/robotics + id = 2 + if("E.X.P.E.R.I-MENTOR") + name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/computer/rdconsole/experiment + id = 3 + if("Mechanics") + name = "Circuit Board (RD Console - Mechanics)" + build_path = /obj/machinery/computer/rdconsole/mechanics + id = 4 + if("Public") + name = "Circuit Board (RD Console - Public)" + build_path = /obj/machinery/computer/rdconsole/public + id = 5 + + to_chat(user, "Access protocols set to [console_choice].") + else + to_chat(user, "Access Denied") + return + return ..() + +/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params) + switch(state) + if(0) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You wrench the frame into place.") + anchored = 1 + state = 1 + return + if(1) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You unfasten the frame.") + anchored = 0 + state = 0 + return + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "computer") + playsound(loc, B.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + icon_state = "1" + circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + return + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + state = 2 + icon_state = "2" + return + if(istype(P, /obj/item/crowbar) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + state = 1 + icon_state = "0" + circuit.loc = loc + circuit = null + return + if(2) + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + state = 1 + icon_state = "1" + return + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 2 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + if(3) + if(istype(P, /obj/item/wirecutters)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 2 + icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + return + if(istype(P, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = P + if(G.amount >= 2) + playsound(loc, G.usesound, 50, 1) + to_chat(user, "You start to add the glass panel to the frame.") + if(do_after(user, 20 * G.toolspeed, target = src)) + if(state == 3 && G.amount >= 2 && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = 4 + icon_state = "4" + else + to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") + return + else + to_chat(user, "You need two sheets of glass for this.") + return + if(4) + if(istype(P, /obj/item/crowbar)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + state = 3 + icon_state = "3" + new /obj/item/stack/sheet/glass(loc, 2) + return + if(istype(P, /obj/item/screwdriver)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new circuit.build_path (loc) + if(circuit.powernet) B:powernet = circuit.powernet + if(circuit.id) B:id = circuit.id + if(circuit.records) B:records = circuit.records + if(circuit.frequency) B:frequency = circuit.frequency + if(istype(circuit,/obj/item/circuitboard/supplycomp)) + var/obj/machinery/computer/supplycomp/SC = B + var/obj/item/circuitboard/supplycomp/C = circuit + SC.can_order_contraband = C.contraband_enabled + qdel(src) + return + if(user.a_intent == INTENT_HARM) + return ..() + + +/obj/structure/computerframe/welder_act(mob/user, obj/item/I) + if(state) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume) && !state) + to_chat(user, "You deconstruct [src].") + deconstruct(TRUE) + + + +/obj/structure/computerframe/HONKputer + name = "Bananium Computer-frame" + icon = 'icons/obj/machines/HONKputer.dmi' + base_mineral = /obj/item/stack/sheet/mineral/bananium + +/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params) + switch(state) + if(0) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20, target = src)) + to_chat(user, "You wrench the frame into place.") + anchored = 1 + state = 1 + if(1) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You unfasten the frame.") + anchored = 0 + state = 0 + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "honkcomputer") + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + icon_state = "1" + circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + state = 2 + icon_state = "2" + if(istype(P, /obj/item/crowbar) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + state = 1 + icon_state = "0" + circuit.loc = loc + circuit = null + return + if(2) + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + state = 1 + icon_state = "1" + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 2 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + if(3) + if(istype(P, /obj/item/wirecutters)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 2 + icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + + if(istype(P, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = P + if(G.amount >= 2) + playsound(loc, G.usesound, 50, 1) + to_chat(user, "You start to add the glass panel to the frame.") + if(do_after(user, 20 * G.toolspeed, target = src)) + if(state == 3 && G.amount >= 2 && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = 4 + icon_state = "4" + else + to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") + return + else + to_chat(user, "You need two sheets of glass for this.") + return + if(4) + if(istype(P, /obj/item/crowbar)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + state = 3 + icon_state = "3" + new /obj/item/stack/sheet/glass(loc, 2) + if(istype(P, /obj/item/screwdriver)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new circuit.build_path (loc) + if(circuit.powernet) B:powernet = circuit.powernet + if(circuit.id) B:id = circuit.id + if(circuit.records) B:records = circuit.records + if(circuit.frequency) B:frequency = circuit.frequency + qdel(src) + return + return ..() diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 6cf5f77a7f2d..c95735507a54 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,356 +1,356 @@ -/obj/machinery/computer/security - name = "security camera console" - desc = "Used to access the various cameras networks on the station." - - icon_keyboard = "security_key" - icon_screen = "cameras" - light_color = LIGHT_COLOR_RED - circuit = /obj/item/circuitboard/camera - - var/mapping = 0 // For the overview file (overview.dm), not used on this page - - var/list/network = list() - var/list/available_networks = list() - var/list/watchers = list() //who's using the console, associated with the camera they're on. - -/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() - generate_network_access() - ..() - -/obj/machinery/computer/security/proc/generate_network_access() - available_networks["SS13"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Telecomms"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Research Outpost"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Mining Outpost"] = list(ACCESS_QM,ACCESS_HOP,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Research"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Prison"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Labor Camp"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Interrogation"] = list(ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Atmosphere Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Fire Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Power Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Supermatter"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["MiniSat"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Singularity"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Anomaly Isolation"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Toxins"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["Telepad"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["TestChamber"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) - available_networks["ERT"] = list(ACCESS_CENT_SPECOPS_COMMANDER,ACCESS_CENT_COMMANDER) - available_networks["CentComm"] = list(ACCESS_CENT_SECURITY,ACCESS_CENT_COMMANDER) - available_networks["Thunderdome"] = list(ACCESS_CENT_THUNDER,ACCESS_CENT_COMMANDER) - -/obj/machinery/computer/security/Destroy() - if(watchers.len) - for(var/mob/M in watchers) - M.unset_machine() //to properly reset the view of the users if the console is deleted. - return ..() - -/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) - var/turf/consoleturf = get_turf(src) - var/turf/cameraturf = get_turf(C) - if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions - return TRUE - -/obj/machinery/computer/security/check_eye(mob/user) - if(!(user in watchers)) - user.unset_machine() - return - if(!watchers[user]) - user.unset_machine() - return - var/obj/machinery/camera/C = watchers[user] - if(isCameraFarAway(C)) - user.unset_machine() - return - if(!can_access_camera(C, user)) - user.unset_machine() - return - return 1 - -/obj/machinery/computer/security/on_unset_machine(mob/user) - watchers.Remove(user) - user.reset_perspective(null) - -/obj/machinery/computer/security/attack_hand(mob/user) - if(stat || ..()) - user.unset_machine() - return - - ui_interact(user) - -/obj/machinery/computer/security/telescreen/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/direction = input(user, "Which direction?", "Select direction!") as null|anything in list("North", "East", "South", "West", "Centre") - if(!direction || !Adjacent(user)) - return - pixel_x = 0 - pixel_y = 0 - switch(direction) - if("North") - pixel_y = 32 - if("East") - pixel_x = 32 - if("South") - pixel_y = -32 - if("West") - pixel_x = -32 - -/obj/machinery/computer/security/emag_act(user as mob) - if(!emagged) - emagged = 1 - to_chat(user, "You have authorized full network access!") - attack_hand(user) - else - attack_hand(user) - -/obj/machinery/computer/security/proc/get_user_access(mob/user) - var/list/access = list() - - if(emagged) - access = get_all_accesses() // Assume captain level access when emagged - else if(ishuman(user)) - access = user.get_access() - else if((isAI(user) || isrobot(user)) && CanUseTopic(user, default_state) == STATUS_INTERACTIVE) - access = get_all_accesses() // Assume captain level access when AI - else if(user.can_admin_interact()) - access = get_all_accesses() - return access - -/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "sec_camera_map_header.tmpl") - - ui.open() - -/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/list/cameras = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - if(isCameraFarAway(C)) - continue - if(!can_access_camera(C, user)) - continue - - cameras[++cameras.len] = C.nano_structure() - - for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI - for(var/j = 1 to i - 1) - var/a = cameras[j] - var/b = cameras[j + 1] - if(sorttext(a["name"], b["name"]) < 0) - cameras.Swap(j, j + 1) - - data["cameras"] = cameras - - var/list/access = get_user_access(user) - if(emagged) - data["emagged"] = 1 - - var/list/networks_list = list() - // Loop through the ID's permission, and check which networks the ID has access to. - for(var/net in available_networks) // Loop through networks. - for(var/req in available_networks[net]) // Loop through access levels of the networks. - if(req in access) - if(net in network) // Checks if the network is currently active. - networks_list.Add(list(list("name" = net, "active" = 1))) - else - networks_list.Add(list(list("name" = net, "active" = 0))) - break - - if(networks_list.len) - data["networks"] = networks_list - - data["current"] = null - if(watchers[user]) - var/obj/machinery/camera/watched = watchers[user] - data["current"] = watched.nano_structure() - - return data - -/obj/machinery/computer/security/Topic(href, href_list) - if(..()) - usr.unset_machine() - return 1 - - if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras - if(!C) - return 1 - - switch_to_camera(usr, C) - - else if(href_list["reset"]) - usr.unset_machine() - - else if(href_list["activate"]) // Activate: enable or disable networks - var/net = href_list["activate"] // Network to be enabled or disabled. - var/active = href_list["active"] // Is the network currently active. - var/list/access = get_user_access(usr) - for(var/a in available_networks[net]) - if(a in access) // Re-check for authorization. - if(text2num(active) == 1) - network -= net - break - else - network += net - break - - SSnanoui.update_uis(src) - -// Check if camera is accessible when jumping -/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) - if(CanUseTopic(M, default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) - return 0 - - if(isrobot(M)) - var/list/viewing = viewers(src) - if(!viewing.Find(M)) - return 0 - - if(isAI(M)) - var/mob/living/silicon/ai/A = M - if(!A.is_in_chassis()) - return 0 - - if(!issilicon(M) && !Adjacent(M)) - return 0 - - var/list/shared_networks = network & C.network - if(!shared_networks.len || !C.can_use()) - return 0 - - return 1 - -// Switching to cameras -/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) - if(!can_access_camera(C, user)) - user.unset_machine() - return 1 - - if(isAI(user)) - var/mob/living/silicon/ai/A = user - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - else - user.reset_perspective(C) - watchers[user] = C - use_power(50) - -//Camera control: moving. -/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A) - if(user.machine != src) - return - - var/obj/machinery/camera/jump_to - - if(istype(A, /obj/machinery/camera)) - jump_to = A - - else if(ismob(A)) - if(ishuman(A)) - var/mob/living/carbon/human/H = A - jump_to = locate() in H.head - else if(isrobot(A)) - var/mob/living/silicon/robot/R = A - jump_to = R.camera - - else if(isobj(A)) - var/obj/O = A - jump_to = locate() in O - - else if(isturf(A)) - var/best_dist = INFINITY - for(var/obj/machinery/camera/camera in get_area(A)) - if(!camera.can_use()) - continue - if(!can_access_camera(camera, user)) - continue - var/dist = get_dist(camera,A) - if(dist < best_dist) - best_dist = dist - jump_to = camera - - if(isnull(jump_to)) - return - - if(can_access_camera(jump_to, user)) - switch_to_camera(user, jump_to) - -// Camera control: mouse. -/atom/DblClick() - ..() - if(istype(usr.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = usr.machine - console.jump_on_click(usr, src) - -// Camera control: arrow keys. -/mob/Move(n, direct) - if(istype(machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = machine - var/turf/T = get_turf(console.watchers[src]) - for(var/i; i < 10; i++) - T = get_step(T, direct) - console.jump_on_click(src, T) - return - return ..(n,direct) - -// Other computer monitors. -/obj/machinery/computer/security/telescreen - name = "telescreen" - desc = "Used for watching camera networks." - icon_state = "telescreen_console" - icon_screen = "telescreen" - icon_keyboard = null - light_range_on = 0 - density = 0 - circuit = /obj/item/circuitboard/camera/telescreen - -/obj/machinery/computer/security/telescreen/entertainment - name = "entertainment monitor" - desc = "Damn, they better have Paradise TV on these things." - icon_state = "entertainment_console" - icon_screen = "entertainment" - light_color = "#FFEEDB" - light_range_on = 0 - network = list("news") - luminosity = 0 - circuit = /obj/item/circuitboard/camera/telescreen/entertainment - -/obj/machinery/computer/security/wooden_tv - name = "security camera monitor" - desc = "An old TV hooked into the station's camera network." - icon_state = "television" - icon_keyboard = null - icon_screen = "detective_tv" - light_color = "#3848B3" - light_power_on = 0.5 - network = list("SS13") - circuit = /obj/item/circuitboard/camera/wooden_tv - -/obj/machinery/computer/security/mining - name = "outpost camera monitor" - desc = "Used to access the various cameras on the outpost." - icon_keyboard = "mining_key" - icon_screen = "mining" - light_color = "#F9BBFC" - network = list("Mining Outpost") - circuit = /obj/item/circuitboard/camera/mining - -/obj/machinery/computer/security/engineering - name = "engineering camera monitor" - desc = "Used to monitor fires and breaches." - icon_keyboard = "power_key" - icon_screen = "engie_cams" - light_color = "#FAC54B" - network = list("Power Alarms","Atmosphere Alarms","Fire Alarms") - circuit = /obj/item/circuitboard/camera/engineering +/obj/machinery/computer/security + name = "security camera console" + desc = "Used to access the various cameras networks on the station." + + icon_keyboard = "security_key" + icon_screen = "cameras" + light_color = LIGHT_COLOR_RED + circuit = /obj/item/circuitboard/camera + + var/mapping = 0 // For the overview file (overview.dm), not used on this page + + var/list/network = list() + var/list/available_networks = list() + var/list/watchers = list() //who's using the console, associated with the camera they're on. + +/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() + generate_network_access() + ..() + +/obj/machinery/computer/security/proc/generate_network_access() + available_networks["SS13"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Telecomms"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Research Outpost"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Mining Outpost"] = list(ACCESS_QM,ACCESS_HOP,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Research"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Prison"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Labor Camp"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Interrogation"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Atmosphere Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Fire Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Power Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Supermatter"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["MiniSat"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Singularity"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Anomaly Isolation"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Toxins"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Telepad"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["TestChamber"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["ERT"] = list(ACCESS_CENT_SPECOPS_COMMANDER,ACCESS_CENT_COMMANDER) + available_networks["CentComm"] = list(ACCESS_CENT_SECURITY,ACCESS_CENT_COMMANDER) + available_networks["Thunderdome"] = list(ACCESS_CENT_THUNDER,ACCESS_CENT_COMMANDER) + +/obj/machinery/computer/security/Destroy() + if(watchers.len) + for(var/mob/M in watchers) + M.unset_machine() //to properly reset the view of the users if the console is deleted. + return ..() + +/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) + var/turf/consoleturf = get_turf(src) + var/turf/cameraturf = get_turf(C) + if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions + return TRUE + +/obj/machinery/computer/security/check_eye(mob/user) + if(!(user in watchers)) + user.unset_machine() + return + if(!watchers[user]) + user.unset_machine() + return + var/obj/machinery/camera/C = watchers[user] + if(isCameraFarAway(C)) + user.unset_machine() + return + if(!can_access_camera(C, user)) + user.unset_machine() + return + return 1 + +/obj/machinery/computer/security/on_unset_machine(mob/user) + watchers.Remove(user) + user.reset_perspective(null) + +/obj/machinery/computer/security/attack_hand(mob/user) + if(stat || ..()) + user.unset_machine() + return + + ui_interact(user) + +/obj/machinery/computer/security/telescreen/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/direction = input(user, "Which direction?", "Select direction!") as null|anything in list("North", "East", "South", "West", "Centre") + if(!direction || !Adjacent(user)) + return + pixel_x = 0 + pixel_y = 0 + switch(direction) + if("North") + pixel_y = 32 + if("East") + pixel_x = 32 + if("South") + pixel_y = -32 + if("West") + pixel_x = -32 + +/obj/machinery/computer/security/emag_act(user as mob) + if(!emagged) + emagged = 1 + to_chat(user, "You have authorized full network access!") + attack_hand(user) + else + attack_hand(user) + +/obj/machinery/computer/security/proc/get_user_access(mob/user) + var/list/access = list() + + if(emagged) + access = get_all_accesses() // Assume captain level access when emagged + else if(ishuman(user)) + access = user.get_access() + else if((isAI(user) || isrobot(user)) && CanUseTopic(user, GLOB.default_state) == STATUS_INTERACTIVE) + access = get_all_accesses() // Assume captain level access when AI + else if(user.can_admin_interact()) + access = get_all_accesses() + return access + +/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "sec_camera_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "sec_camera_map_header.tmpl") + + ui.open() + +/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + var/list/cameras = list() + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + if(isCameraFarAway(C)) + continue + if(!can_access_camera(C, user)) + continue + + cameras[++cameras.len] = C.nano_structure() + + for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI + for(var/j = 1 to i - 1) + var/a = cameras[j] + var/b = cameras[j + 1] + if(sorttext(a["name"], b["name"]) < 0) + cameras.Swap(j, j + 1) + + data["cameras"] = cameras + + var/list/access = get_user_access(user) + if(emagged) + data["emagged"] = 1 + + var/list/networks_list = list() + // Loop through the ID's permission, and check which networks the ID has access to. + for(var/net in available_networks) // Loop through networks. + for(var/req in available_networks[net]) // Loop through access levels of the networks. + if(req in access) + if(net in network) // Checks if the network is currently active. + networks_list.Add(list(list("name" = net, "active" = 1))) + else + networks_list.Add(list(list("name" = net, "active" = 0))) + break + + if(networks_list.len) + data["networks"] = networks_list + + data["current"] = null + if(watchers[user]) + var/obj/machinery/camera/watched = watchers[user] + data["current"] = watched.nano_structure() + + return data + +/obj/machinery/computer/security/Topic(href, href_list) + if(..()) + usr.unset_machine() + return 1 + + if(href_list["switchTo"]) + var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras + if(!C) + return 1 + + switch_to_camera(usr, C) + + else if(href_list["reset"]) + usr.unset_machine() + + else if(href_list["activate"]) // Activate: enable or disable networks + var/net = href_list["activate"] // Network to be enabled or disabled. + var/active = href_list["active"] // Is the network currently active. + var/list/access = get_user_access(usr) + for(var/a in available_networks[net]) + if(a in access) // Re-check for authorization. + if(text2num(active) == 1) + network -= net + break + else + network += net + break + + SSnanoui.update_uis(src) + +// Check if camera is accessible when jumping +/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) + if(CanUseTopic(M, GLOB.default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) + return 0 + + if(isrobot(M)) + var/list/viewing = viewers(src) + if(!viewing.Find(M)) + return 0 + + if(isAI(M)) + var/mob/living/silicon/ai/A = M + if(!A.is_in_chassis()) + return 0 + + if(!issilicon(M) && !Adjacent(M)) + return 0 + + var/list/shared_networks = network & C.network + if(!shared_networks.len || !C.can_use()) + return 0 + + return 1 + +// Switching to cameras +/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) + if(!can_access_camera(C, user)) + user.unset_machine() + return 1 + + if(isAI(user)) + var/mob/living/silicon/ai/A = user + A.eyeobj.setLoc(get_turf(C)) + A.client.eye = A.eyeobj + else + user.reset_perspective(C) + watchers[user] = C + use_power(50) + +//Camera control: moving. +/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A) + if(user.machine != src) + return + + var/obj/machinery/camera/jump_to + + if(istype(A, /obj/machinery/camera)) + jump_to = A + + else if(ismob(A)) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + jump_to = locate() in H.head + else if(isrobot(A)) + var/mob/living/silicon/robot/R = A + jump_to = R.camera + + else if(isobj(A)) + var/obj/O = A + jump_to = locate() in O + + else if(isturf(A)) + var/best_dist = INFINITY + for(var/obj/machinery/camera/camera in get_area(A)) + if(!camera.can_use()) + continue + if(!can_access_camera(camera, user)) + continue + var/dist = get_dist(camera,A) + if(dist < best_dist) + best_dist = dist + jump_to = camera + + if(isnull(jump_to)) + return + + if(can_access_camera(jump_to, user)) + switch_to_camera(user, jump_to) + +// Camera control: mouse. +/atom/DblClick() + ..() + if(istype(usr.machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = usr.machine + console.jump_on_click(usr, src) + +// Camera control: arrow keys. +/mob/Move(n, direct) + if(istype(machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = machine + var/turf/T = get_turf(console.watchers[src]) + for(var/i; i < 10; i++) + T = get_step(T, direct) + console.jump_on_click(src, T) + return + return ..(n,direct) + +// Other computer monitors. +/obj/machinery/computer/security/telescreen + name = "telescreen" + desc = "Used for watching camera networks." + icon_state = "telescreen_console" + icon_screen = "telescreen" + icon_keyboard = null + light_range_on = 0 + density = 0 + circuit = /obj/item/circuitboard/camera/telescreen + +/obj/machinery/computer/security/telescreen/entertainment + name = "entertainment monitor" + desc = "Damn, they better have Paradise TV on these things." + icon_state = "entertainment_console" + icon_screen = "entertainment" + light_color = "#FFEEDB" + light_range_on = 0 + network = list("news") + luminosity = 0 + circuit = /obj/item/circuitboard/camera/telescreen/entertainment + +/obj/machinery/computer/security/wooden_tv + name = "security camera monitor" + desc = "An old TV hooked into the station's camera network." + icon_state = "television" + icon_keyboard = null + icon_screen = "detective_tv" + light_color = "#3848B3" + light_power_on = 0.5 + network = list("SS13") + circuit = /obj/item/circuitboard/camera/wooden_tv + +/obj/machinery/computer/security/mining + name = "outpost camera monitor" + desc = "Used to access the various cameras on the outpost." + icon_keyboard = "mining_key" + icon_screen = "mining" + light_color = "#F9BBFC" + network = list("Mining Outpost") + circuit = /obj/item/circuitboard/camera/mining + +/obj/machinery/computer/security/engineering + name = "engineering camera monitor" + desc = "Used to monitor fires and breaches." + icon_keyboard = "power_key" + icon_screen = "engie_cams" + light_color = "#FAC54B" + network = list("Power Alarms","Atmosphere Alarms","Fire Alarms") + circuit = /obj/item/circuitboard/camera/engineering diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index b70addea9092..76242c4a0e4f 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -75,7 +75,7 @@ if(!eyeobj.eye_initialized) var/camera_location - for(var/obj/machinery/camera/C in cameranet.cameras) + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) if(!C.can_use()) continue if(C.network&networks) @@ -139,7 +139,7 @@ T = get_turf(T) loc = T if(use_static) - cameranet.visibility(src, GetViewerClient()) + GLOB.cameranet.visibility(src, GetViewerClient()) if(visible_icon) if(eye_user.client) eye_user.client.images -= user_image @@ -189,7 +189,7 @@ var/list/L = list() - for(var/obj/machinery/camera/cam in cameranet.cameras) + for(var/obj/machinery/camera/cam in GLOB.cameranet.cameras) L.Add(cam) camera_sort(L) diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 7ffc141dcb4d..de85ad842b73 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -1,6 +1,6 @@ //Keeps track of the time for the ID console. Having it as a global variable prevents people from dismantling/reassembling it to //increase the slots of many jobs. -var/time_last_changed_position = 0 +GLOBAL_VAR_INIT(time_last_changed_position, 0) /obj/machinery/computer/card name = "identification computer" @@ -160,7 +160,7 @@ var/time_last_changed_position = 0 if(job) if(!job_blacklisted_full(job) && !job_blacklisted_partial(job) && job_in_department(job, FALSE)) if((job.total_positions <= GLOB.player_list.len * (max_relative_positions / 100))) - var/delta = (world.time / 10) - time_last_changed_position + var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) return 1 return -2 @@ -172,7 +172,7 @@ var/time_last_changed_position = 0 if(job) if(!job_blacklisted_full(job) && !job_blacklisted_partial(job) && job_in_department(job, FALSE)) if(job.total_positions > job.current_positions && !(job in SSjobs.prioritized_jobs)) - var/delta = (world.time / 10) - time_last_changed_position + var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) return 1 return -2 @@ -236,13 +236,13 @@ var/time_last_changed_position = 0 ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 775, 700) ui.open() -/obj/machinery/computer/card/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/card/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["src"] = UID() data["station_name"] = station_name() data["mode"] = mode data["printing"] = printing - data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["manifest"] = GLOB.data_core ? GLOB.data_core.get_manifest(0) : null data["target_name"] = modify ? modify.name : "-----" data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" data["target_rank"] = get_target_rank() @@ -260,19 +260,19 @@ var/time_last_changed_position = 0 var/list/job_formats = SSjobs.format_jobs_for_id_computer(modify) data["top_jobs"] = format_jobs(list("Captain", "Custom"), data["target_rank"], job_formats) - data["engineering_jobs"] = format_jobs(engineering_positions, data["target_rank"], job_formats) - data["medical_jobs"] = format_jobs(medical_positions, data["target_rank"], job_formats) - data["science_jobs"] = format_jobs(science_positions, data["target_rank"], job_formats) - data["security_jobs"] = format_jobs(security_positions, data["target_rank"], job_formats) - data["support_jobs"] = format_jobs(support_positions, data["target_rank"], job_formats) - data["civilian_jobs"] = format_jobs(civilian_positions, data["target_rank"], job_formats) - data["special_jobs"] = format_jobs(whitelisted_positions, data["target_rank"], job_formats) + data["engineering_jobs"] = format_jobs(GLOB.engineering_positions, data["target_rank"], job_formats) + data["medical_jobs"] = format_jobs(GLOB.medical_positions, data["target_rank"], job_formats) + data["science_jobs"] = format_jobs(GLOB.science_positions, data["target_rank"], job_formats) + data["security_jobs"] = format_jobs(GLOB.security_positions, data["target_rank"], job_formats) + data["support_jobs"] = format_jobs(GLOB.support_positions, data["target_rank"], job_formats) + data["civilian_jobs"] = format_jobs(GLOB.civilian_positions, data["target_rank"], job_formats) + data["special_jobs"] = format_jobs(GLOB.whitelisted_positions, data["target_rank"], job_formats) data["centcom_jobs"] = format_jobs(get_all_centcom_jobs(), data["target_rank"], job_formats) data["card_skins"] = format_card_skins(get_station_card_skins()) data["job_slots"] = format_job_slots() - var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - time_last_changed_position), 1) + var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) var/mins = round(time_to_wait / 60) var/seconds = time_to_wait - (60*mins) data["cooldown_mins"] = mins @@ -321,7 +321,7 @@ var/time_last_changed_position = 0 switch(href_list["choice"]) if("modify") if(modify) - data_core.manifest_modify(modify.registered_name, modify.assignment) + GLOB.data_core.manifest_modify(modify.registered_name, modify.assignment) modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") if(ishuman(usr)) modify.forceMove(get_turf(src)) @@ -480,7 +480,7 @@ var/time_last_changed_position = 0 P.name = "crew manifest ([station_time_timestamp()])" P.info = {"

    Crew Manifest


    - [data_core ? data_core.get_manifest(0) : ""] + [GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""] "} else if(modify && !mode) P.name = "access report" @@ -544,7 +544,7 @@ var/time_last_changed_position = 0 if(can_open_job(j) != 1) return 0 if(opened_positions[edit_job_target] >= 0) - time_last_changed_position = world.time / 10 + GLOB.time_last_changed_position = world.time / 10 j.total_positions++ opened_positions[edit_job_target]++ log_game("[key_name(usr)] has opened a job slot for job \"[j]\".") @@ -564,7 +564,7 @@ var/time_last_changed_position = 0 return 0 //Allow instant closing without cooldown if a position has been opened before if(opened_positions[edit_job_target] <= 0) - time_last_changed_position = world.time / 10 + GLOB.time_last_changed_position = world.time / 10 j.total_positions-- opened_positions[edit_job_target]-- log_game("[key_name(usr)] has closed a job slot for job \"[j]\".") @@ -639,4 +639,4 @@ var/time_last_changed_position = 0 icon_screen = "idce" light_color = COLOR_YELLOW req_access = list(ACCESS_CE) - circuit = /obj/item/circuitboard/card/minor/ce \ No newline at end of file + circuit = /obj/item/circuitboard/card/minor/ce diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index c06cbb32a04d..14a8e2b6ddc2 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -129,7 +129,7 @@ ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520) ui.open() -/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["menu"] = src.menu data["scanner"] = sanitize("[src.scanner]") @@ -358,35 +358,37 @@ return if(scan_brain && !can_brainscan()) return - if((isnull(subject)) || (!(ishuman(subject))) || (!subject.dna) || (NO_SCAN in subject.dna.species.species_traits)) - scantemp = "Error: Unable to locate valid genetic data." - SSnanoui.update_uis(src) - return + if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna)) + if(isalien(subject)) + scantemp = "Error: Xenomorphs are not scannable." + SSnanoui.update_uis(src) + return + // can add more conditions for specific non-human messages here + else + scantemp = "Error: Subject species is not scannable." + SSnanoui.update_uis(src) + return if(subject.get_int_organ(/obj/item/organ/internal/brain)) var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain) if(istype(Brn)) if(NO_SCAN in Brn.dna.species.species_traits) - scantemp = "Error: Subject's brain is incompatible." + scantemp = "Error: [subject.dna.species.name_plural] are not scannable." SSnanoui.update_uis(src) return if(!subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No signs of intelligence detected." + scantemp = "Error: No brain detected in subject." SSnanoui.update_uis(src) return if(subject.suiciding) - scantemp = "Error: Subject's brain is not responding to scanning stimuli." + scantemp = "Error: Subject has committed suicide and is not scannable." SSnanoui.update_uis(src) return if((!subject.ckey) || (!subject.client)) - scantemp = "Error: Mental interface failure." + scantemp = "Error: Subject's brain is not responding. Further attempts after a short delay may succeed." SSnanoui.update_uis(src) return if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2) - scantemp = "Error: Mental interface failure." - SSnanoui.update_uis(src) - return - if(scan_brain && !subject.get_int_organ(/obj/item/organ/internal/brain)) - scantemp = "Error: No brain found." + scantemp = "Error: Subject has incompatible genetic mutations." SSnanoui.update_uis(src) return if(!isnull(find_record(subject.ckey))) diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 0a9627fa38b1..0faaff3af9d8 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,569 +1,569 @@ -#define COMM_SCREEN_MAIN 1 -#define COMM_SCREEN_STAT 2 -#define COMM_SCREEN_MESSAGES 3 -#define COMM_SCREEN_SECLEVEL 4 - -#define COMM_AUTHENTICATION_NONE 0 -#define COMM_AUTHENTICATION_MIN 1 -#define COMM_AUTHENTICATION_MAX 2 - -// The communications computer -/obj/machinery/computer/communications - name = "communications console" - desc = "This can be used for various important functions. Still under developement." - icon_keyboard = "tech_key" - icon_screen = "comm" - req_access = list(ACCESS_HEADS) - circuit = /obj/item/circuitboard/communications - var/prints_intercept = 1 - var/authenticated = COMM_AUTHENTICATION_NONE - var/list/messagetitle = list() - var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 - var/menu_state = COMM_SCREEN_MAIN - var/ai_menu_state = COMM_SCREEN_MAIN - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 - var/tmp_alertlevel = 0 - - var/stat_msg1 - var/stat_msg2 - var/display_type="blank" - - var/datum/announcement/priority/crew_announcement = new - - light_color = LIGHT_COLOR_LIGHTBLUE - -/obj/machinery/computer/communications/New() - GLOB.shuttle_caller_list += src - ..() - crew_announcement.newscast = 0 - -/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) - if(authenticated == COMM_AUTHENTICATION_MAX) - return COMM_AUTHENTICATION_MAX - else if(user.can_admin_interact()) - return COMM_AUTHENTICATION_MAX - else if(authenticated) - return COMM_AUTHENTICATION_MIN - else - if(message) - to_chat(user, "Access denied.") - return COMM_AUTHENTICATION_NONE - -/obj/machinery/computer/communications/proc/change_security_level(var/new_level) - tmp_alertlevel = new_level - var/old_level = security_level - if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) - if(security_level != old_level) - //Only notify the admins if an actual change happened - log_game("[key_name(usr)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - switch(security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green",1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue",1) - tmp_alertlevel = 0 - -/obj/machinery/computer/communications/Topic(href, href_list) - if(..(href, href_list)) - return 1 - - if(!is_secure_level(src.z)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 1 - - if(href_list["login"]) - if(!ishuman(usr)) - to_chat(usr, "Access denied.") - return - - var/list/access = usr.get_access() - if(allowed(usr)) - authenticated = COMM_AUTHENTICATION_MIN - - if(ACCESS_CAPTAIN in access) - authenticated = COMM_AUTHENTICATION_MAX - var/mob/living/carbon/human/H = usr - var/obj/item/card/id = H.get_idcard(TRUE) - if(istype(id)) - crew_announcement.announcer = GetNameAndAssignmentFromId(id) - - SSnanoui.update_uis(src) - return - - if(href_list["logout"]) - authenticated = COMM_AUTHENTICATION_NONE - crew_announcement.announcer = "" - setMenuState(usr,COMM_SCREEN_MAIN) - SSnanoui.update_uis(src) - return - - if(!is_authenticated(usr)) - return 1 - - switch(href_list["operation"]) - if("main") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("changeseclevel") - setMenuState(usr,COMM_SCREEN_SECLEVEL) - - if("newalertlevel") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from changing the alert level.") - return 1 - else if(usr.can_admin_interact()) - change_security_level(text2num(href_list["level"])) - return 1 - else if(!ishuman(usr)) - to_chat(usr, "Security measures prevent you from changing the alert level.") - return 1 - - var/mob/living/carbon/human/L = usr - var/obj/item/card = L.get_active_hand() - var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(I && istype(I)) - if(ACCESS_CAPTAIN in I.access) - change_security_level(text2num(href_list["level"])) - else - to_chat(usr, "You are not authorized to do this.") - setMenuState(usr,COMM_SCREEN_MAIN) - else - to_chat(usr, "You need to swipe your ID.") - - if("announce") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(message_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements.") - SSnanoui.update_uis(src) - return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") - if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - - if("callshuttle") - var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") - if(!input || ..() || !is_authenticated(usr)) - SSnanoui.update_uis(src) - return - - call_shuttle_proc(usr, input) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("cancelshuttle") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from recalling the shuttle.") - SSnanoui.update_uis(src) - return 1 - var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") - if(response == "Yes") - cancel_call_proc(usr) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("messagelist") - currmsg = 0 - if(href_list["msgid"]) - setCurrentMessage(usr, text2num(href_list["msgid"])) - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("delmessage") - if(href_list["msgid"]) - currmsg = text2num(href_list["msgid"]) - var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") - if(response == "Yes") - if(currmsg) - var/id = getCurrentMessage() - var/title = messagetitle[id] - var/text = messagetext[id] - messagetitle.Remove(title) - messagetext.Remove(text) - if(currmsg == id) - currmsg = 0 - if(aicurrmsg == id) - aicurrmsg = 0 - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("status") - setMenuState(usr,COMM_SCREEN_STAT) - - // Status display stuff - if("setstat") - display_type=href_list["statdisp"] - switch(display_type) - if("message") - post_status("message", stat_msg1, stat_msg2, usr) - if("alert") - post_status("alert", href_list["alert"], user = usr) - else - post_status(href_list["statdisp"], user = usr) - setMenuState(usr,COMM_SCREEN_STAT) - - if("setmsg1") - stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) - setMenuState(usr,COMM_SCREEN_STAT) - - if("setmsg2") - stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) - setMenuState(usr,COMM_SCREEN_STAT) - - if("nukerequest") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Nuke_request(input, usr) - to_chat(usr, "Request sent.") - log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") - priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("MessageCentcomm") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Centcomm_announce(input, usr) - print_centcom_report(input, station_time_timestamp() + " Captain's Message") - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Centcomm announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - // OMG SYNDICATE ...LETTERHEAD - if("MessageSyndicate") - if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Syndicate announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestoreBackup") - to_chat(usr, "Backup routing data restored!") - src.emagged = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestartNanoMob") - if(SSmob_hunt) - if(SSmob_hunt.manual_reboot()) - var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", - "Capturing all of them", "Fixing minor text issues", "Being the very best", - "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") - to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") - else - to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") - else - to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") - - if("ToggleATC") - atc.squelched = !atc.squelched - to_chat(usr, "ATC traffic is now: [atc.squelched ? "Disabled" : "Enabled"].") - - SSnanoui.update_uis(src) - return 1 - -/obj/machinery/computer/communications/emag_act(user as mob) - if(!emagged) - src.emagged = 1 - to_chat(user, "You scramble the communication routing circuits!") - SSnanoui.update_uis(src) - -/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) - if(..(user)) - return - - if(stat & (NOPOWER|BROKEN)) - return - - if(!is_secure_level(src.z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - - ui_interact(user) - -/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500) - // open the new ui window - ui.open() - -/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["is_ai"] = isAI(user) || isrobot(user) - data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state - data["emagged"] = emagged - data["authenticated"] = is_authenticated(user, 0) - data["screen"] = getMenuState(usr) - - data["stat_display"] = list( - "type" = display_type, - "line_1" = (stat_msg1 ? stat_msg1 : "-----"), - "line_2" = (stat_msg2 ? stat_msg2 : "-----"), - - "presets" = list( - list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), - list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), - list("name" = "message", "label" = "Message", "desc" = "A custom message.") - ), - - "alerts"=list( - list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), - list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), - list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), - list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), - ) - ) - - data["security_level"] = security_level - data["str_security_level"] = capitalize(get_security_level()) - data["levels"] = list( - list("id" = SEC_LEVEL_GREEN, "name" = "Green"), - list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), - //SEC_LEVEL_RED = list("name"="Red"), - ) - - var/list/msg_data = list() - for(var/i = 1; i <= messagetext.len; i++) - msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) - - data["messages"] = msg_data - if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) - data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] - data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] - - data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null - - var/shuttle[0] - switch(SSshuttle.emergency.mode) - if(SHUTTLE_IDLE, SHUTTLE_RECALL) - shuttle["callStatus"] = 2 //#define - else - shuttle["callStatus"] = 1 - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/timeleft = SSshuttle.emergency.timeLeft() - shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" - - data["shuttle"] = shuttle - - data["atcSquelched"] = atc.squelched - - return data - - -/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) - if(isAI(user) || isrobot(user)) - aicurrmsg = value - else - currmsg = value - -/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) - if(isAI(user) || isrobot(user)) - return aicurrmsg - else - return currmsg - -/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) - if(isAI(user) || isrobot(user)) - ai_menu_state=value - else - menu_state=value - -/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) - if(isAI(user) || isrobot(user)) - return ai_menu_state - else - return menu_state - -/proc/enable_prison_shuttle(var/mob/user); - -/proc/call_shuttle_proc(var/mob/user, var/reason) - if(sent_strike_team == 1) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(SSshuttle.emergencyNoEscape) - to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") - return - - if(SSshuttle.emergency.mode > SHUTTLE_ESCAPE) - to_chat(user, "The emergency shuttle may not be called while returning to Central Command.") - return - - if(SSticker.mode.name == "blob") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - SSshuttle.requestEvac(user, reason) - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle.", 1) - - return - -/proc/init_shift_change(var/mob/user, var/force = 0) - // if force is 0, some things may stop the shuttle call - if(!force) - if(SSshuttle.emergencyNoEscape) - to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") - return - - if(sent_strike_team == 1) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(world.time < 54000) // 30 minute grace period to let the game get going - to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") - return - - if(SSticker.mode.name == "epidemic") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. - SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) - SSshuttle.emergency.canRecall = FALSE - else - SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) - SSshuttle.emergency.canRecall = FALSE - if(user) - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1) - return - - -/proc/cancel_call_proc(var/mob/user) - if(SSticker.mode.name == "meteor") - return - - if(SSshuttle.cancelEvac(user)) - log_game("[key_name(user)] has recalled the shuttle.") - message_admins("[key_name_admin(user)] has recalled the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) - else - to_chat(user, "Central Command has refused the recall request!") - log_game("[key_name(user)] has tried and failed to recall the shuttle.") - message_admins("[key_name_admin(user)] has tried and failed to recall the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) - -/proc/post_status(command, data1, data2, mob/user = null) - - var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) - - if(!frequency) return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = 1 - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - log_admin("STATUS: [user] set status screen message with [src]: [data1] [data2]") - //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") - if("alert") - status_signal.data["picture_state"] = data1 - - spawn(0) - frequency.post_signal(src, status_signal) - - -/obj/machinery/computer/communications/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/obj/item/circuitboard/communications/New() - GLOB.shuttle_caller_list += src - ..() - -/obj/item/circuitboard/communications/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/proc/print_command_report(text = "", title = "Central Command Update") - for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) - if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "paper- '[title]'" - P.info = text - P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) - -/proc/print_centcom_report(text = "", title = "Incoming Message") - for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) - if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "paper- '[title]'" - P.info = text - P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) \ No newline at end of file +#define COMM_SCREEN_MAIN 1 +#define COMM_SCREEN_STAT 2 +#define COMM_SCREEN_MESSAGES 3 +#define COMM_SCREEN_SECLEVEL 4 + +#define COMM_AUTHENTICATION_NONE 0 +#define COMM_AUTHENTICATION_MIN 1 +#define COMM_AUTHENTICATION_MAX 2 + +// The communications computer +/obj/machinery/computer/communications + name = "communications console" + desc = "This can be used for various important functions. Still under developement." + icon_keyboard = "tech_key" + icon_screen = "comm" + req_access = list(ACCESS_HEADS) + circuit = /obj/item/circuitboard/communications + var/prints_intercept = 1 + var/authenticated = COMM_AUTHENTICATION_NONE + var/list/messagetitle = list() + var/list/messagetext = list() + var/currmsg = 0 + var/aicurrmsg = 0 + var/menu_state = COMM_SCREEN_MAIN + var/ai_menu_state = COMM_SCREEN_MAIN + var/message_cooldown = 0 + var/centcomm_message_cooldown = 0 + var/tmp_alertlevel = 0 + + var/stat_msg1 + var/stat_msg2 + var/display_type="blank" + + var/datum/announcement/priority/crew_announcement = new + + light_color = LIGHT_COLOR_LIGHTBLUE + +/obj/machinery/computer/communications/New() + GLOB.shuttle_caller_list += src + ..() + crew_announcement.newscast = 0 + +/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) + if(authenticated == COMM_AUTHENTICATION_MAX) + return COMM_AUTHENTICATION_MAX + else if(user.can_admin_interact()) + return COMM_AUTHENTICATION_MAX + else if(authenticated) + return COMM_AUTHENTICATION_MIN + else + if(message) + to_chat(user, "Access denied.") + return COMM_AUTHENTICATION_NONE + +/obj/machinery/computer/communications/proc/change_security_level(var/new_level) + tmp_alertlevel = new_level + var/old_level = GLOB.security_level + if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this + set_security_level(tmp_alertlevel) + if(GLOB.security_level != old_level) + //Only notify the admins if an actual change happened + log_game("[key_name(usr)] has changed the security level to [get_security_level()].") + message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") + switch(GLOB.security_level) + if(SEC_LEVEL_GREEN) + feedback_inc("alert_comms_green",1) + if(SEC_LEVEL_BLUE) + feedback_inc("alert_comms_blue",1) + tmp_alertlevel = 0 + +/obj/machinery/computer/communications/Topic(href, href_list) + if(..(href, href_list)) + return 1 + + if(!is_secure_level(src.z)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return 1 + + if(href_list["login"]) + if(!ishuman(usr)) + to_chat(usr, "Access denied.") + return + + var/list/access = usr.get_access() + if(allowed(usr)) + authenticated = COMM_AUTHENTICATION_MIN + + if(ACCESS_CAPTAIN in access) + authenticated = COMM_AUTHENTICATION_MAX + var/mob/living/carbon/human/H = usr + var/obj/item/card/id = H.get_idcard(TRUE) + if(istype(id)) + crew_announcement.announcer = GetNameAndAssignmentFromId(id) + + SSnanoui.update_uis(src) + return + + if(href_list["logout"]) + authenticated = COMM_AUTHENTICATION_NONE + crew_announcement.announcer = "" + setMenuState(usr,COMM_SCREEN_MAIN) + SSnanoui.update_uis(src) + return + + if(!is_authenticated(usr)) + return 1 + + switch(href_list["operation"]) + if("main") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("changeseclevel") + setMenuState(usr,COMM_SCREEN_SECLEVEL) + + if("newalertlevel") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from changing the alert level.") + return 1 + else if(usr.can_admin_interact()) + change_security_level(text2num(href_list["level"])) + return 1 + else if(!ishuman(usr)) + to_chat(usr, "Security measures prevent you from changing the alert level.") + return 1 + + var/mob/living/carbon/human/L = usr + var/obj/item/card = L.get_active_hand() + var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(I && istype(I)) + if(ACCESS_CAPTAIN in I.access) + change_security_level(text2num(href_list["level"])) + else + to_chat(usr, "You are not authorized to do this.") + setMenuState(usr,COMM_SCREEN_MAIN) + else + to_chat(usr, "You need to swipe your ID.") + + if("announce") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(message_cooldown) + to_chat(usr, "Please allow at least one minute to pass between announcements.") + SSnanoui.update_uis(src) + return + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") + if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + crew_announcement.Announce(input) + message_cooldown = 1 + spawn(600)//One minute cooldown + message_cooldown = 0 + + if("callshuttle") + var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") + if(!input || ..() || !is_authenticated(usr)) + SSnanoui.update_uis(src) + return + + call_shuttle_proc(usr, input) + if(SSshuttle.emergency.timer) + post_status("shuttle") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("cancelshuttle") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from recalling the shuttle.") + SSnanoui.update_uis(src) + return 1 + var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") + if(response == "Yes") + cancel_call_proc(usr) + if(SSshuttle.emergency.timer) + post_status("shuttle") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("messagelist") + currmsg = 0 + if(href_list["msgid"]) + setCurrentMessage(usr, text2num(href_list["msgid"])) + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("delmessage") + if(href_list["msgid"]) + currmsg = text2num(href_list["msgid"]) + var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") + if(response == "Yes") + if(currmsg) + var/id = getCurrentMessage() + var/title = messagetitle[id] + var/text = messagetext[id] + messagetitle.Remove(title) + messagetext.Remove(text) + if(currmsg == id) + currmsg = 0 + if(aicurrmsg == id) + aicurrmsg = 0 + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("status") + setMenuState(usr,COMM_SCREEN_STAT) + + // Status display stuff + if("setstat") + display_type=href_list["statdisp"] + switch(display_type) + if("message") + post_status("message", stat_msg1, stat_msg2, usr) + if("alert") + post_status("alert", href_list["alert"], user = usr) + else + post_status(href_list["statdisp"], user = usr) + setMenuState(usr,COMM_SCREEN_STAT) + + if("setmsg1") + stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) + setMenuState(usr,COMM_SCREEN_STAT) + + if("setmsg2") + stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) + setMenuState(usr,COMM_SCREEN_STAT) + + if("nukerequest") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Nuke_request(input, usr) + to_chat(usr, "Request sent.") + log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") + GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("MessageCentcomm") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Centcomm_announce(input, usr) + print_centcom_report(input, station_time_timestamp() + " Captain's Message") + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made a Centcomm announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + // OMG SYNDICATE ...LETTERHEAD + if("MessageSyndicate") + if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Syndicate_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made a Syndicate announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestoreBackup") + to_chat(usr, "Backup routing data restored!") + src.emagged = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestartNanoMob") + if(SSmob_hunt) + if(SSmob_hunt.manual_reboot()) + var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", + "Capturing all of them", "Fixing minor text issues", "Being the very best", + "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") + to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") + else + to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") + else + to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") + + if("ToggleATC") + GLOB.atc.squelched = !GLOB.atc.squelched + to_chat(usr, "ATC traffic is now: [GLOB.atc.squelched ? "Disabled" : "Enabled"].") + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/computer/communications/emag_act(user as mob) + if(!emagged) + src.emagged = 1 + to_chat(user, "You scramble the communication routing circuits!") + SSnanoui.update_uis(src) + +/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) + if(..(user)) + return + + if(stat & (NOPOWER|BROKEN)) + return + + if(!is_secure_level(src.z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + + ui_interact(user) + +/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500) + // open the new ui window + ui.open() + +/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["is_ai"] = isAI(user) || isrobot(user) + data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state + data["emagged"] = emagged + data["authenticated"] = is_authenticated(user, 0) + data["screen"] = getMenuState(usr) + + data["stat_display"] = list( + "type" = display_type, + "line_1" = (stat_msg1 ? stat_msg1 : "-----"), + "line_2" = (stat_msg2 ? stat_msg2 : "-----"), + + "presets" = list( + list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), + list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), + list("name" = "message", "label" = "Message", "desc" = "A custom message.") + ), + + "alerts"=list( + list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), + list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), + list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), + list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), + ) + ) + + data["security_level"] = GLOB.security_level + data["str_security_level"] = capitalize(get_security_level()) + data["levels"] = list( + list("id" = SEC_LEVEL_GREEN, "name" = "Green"), + list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), + //SEC_LEVEL_RED = list("name"="Red"), + ) + + var/list/msg_data = list() + for(var/i = 1; i <= messagetext.len; i++) + msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) + + data["messages"] = msg_data + if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) + data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] + data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] + + data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null + + var/shuttle[0] + switch(SSshuttle.emergency.mode) + if(SHUTTLE_IDLE, SHUTTLE_RECALL) + shuttle["callStatus"] = 2 //#define + else + shuttle["callStatus"] = 1 + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/timeleft = SSshuttle.emergency.timeLeft() + shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + + data["shuttle"] = shuttle + + data["atcSquelched"] = GLOB.atc.squelched + + return data + + +/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) + if(isAI(user) || isrobot(user)) + aicurrmsg = value + else + currmsg = value + +/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) + if(isAI(user) || isrobot(user)) + return aicurrmsg + else + return currmsg + +/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) + if(isAI(user) || isrobot(user)) + ai_menu_state=value + else + menu_state=value + +/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) + if(isAI(user) || isrobot(user)) + return ai_menu_state + else + return menu_state + +/proc/enable_prison_shuttle(var/mob/user); + +/proc/call_shuttle_proc(var/mob/user, var/reason) + if(GLOB.sent_strike_team == 1) + to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(SSshuttle.emergencyNoEscape) + to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") + return + + if(SSshuttle.emergency.mode > SHUTTLE_ESCAPE) + to_chat(user, "The emergency shuttle may not be called while returning to Central Command.") + return + + if(SSticker.mode.name == "blob") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + SSshuttle.requestEvac(user, reason) + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle.", 1) + + return + +/proc/init_shift_change(var/mob/user, var/force = 0) + // if force is 0, some things may stop the shuttle call + if(!force) + if(SSshuttle.emergencyNoEscape) + to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") + return + + if(GLOB.sent_strike_team == 1) + to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(world.time < 54000) // 30 minute grace period to let the game get going + to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") + return + + if(SSticker.mode.name == "epidemic") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. + SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) + SSshuttle.emergency.canRecall = FALSE + else + SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) + SSshuttle.emergency.canRecall = FALSE + if(user) + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1) + return + + +/proc/cancel_call_proc(var/mob/user) + if(SSticker.mode.name == "meteor") + return + + if(SSshuttle.cancelEvac(user)) + log_game("[key_name(user)] has recalled the shuttle.") + message_admins("[key_name_admin(user)] has recalled the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) + else + to_chat(user, "Central Command has refused the recall request!") + log_game("[key_name(user)] has tried and failed to recall the shuttle.") + message_admins("[key_name_admin(user)] has tried and failed to recall the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) + +/proc/post_status(command, data1, data2, mob/user = null) + + var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) + + if(!frequency) return + + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = 1 + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + log_admin("STATUS: [user] set status screen message with [src]: [data1] [data2]") + //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") + if("alert") + status_signal.data["picture_state"] = data1 + + spawn(0) + frequency.post_signal(src, status_signal) + + +/obj/machinery/computer/communications/Destroy() + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + return ..() + +/obj/item/circuitboard/communications/New() + GLOB.shuttle_caller_list += src + ..() + +/obj/item/circuitboard/communications/Destroy() + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + return ..() + +/proc/print_command_report(text = "", title = "Central Command Update") + for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) + if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "paper- '[title]'" + P.info = text + P.update_icon() + C.messagetitle.Add("[title]") + C.messagetext.Add(text) + for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) + if(P.computer) + var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) + +/proc/print_centcom_report(text = "", title = "Incoming Message") + for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) + if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "paper- '[title]'" + P.info = text + P.update_icon() + C.messagetitle.Add("[title]") + C.messagetext.Add(text) + for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) + if(P.computer) + var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 4efdcb69ee80..a832e2b5d160 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -1,36 +1,36 @@ -/obj/machinery/computer/crew - name = "crew monitoring computer" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - icon_keyboard = "med_key" - icon_screen = "crew" - use_power = IDLE_POWER_USE - idle_power_usage = 250 - active_power_usage = 500 - light_color = LIGHT_COLOR_DARKBLUE - circuit = /obj/item/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor - -/obj/machinery/computer/crew/New() - crew_monitor = new(src) - ..() - -/obj/machinery/computer/crew/Destroy() - QDEL_NULL(crew_monitor) - return ..() - -/obj/machinery/computer/crew/attack_ai(mob/user) - attack_hand(user) - ui_interact(user) - - -/obj/machinery/computer/crew/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - -/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - crew_monitor.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/crew/interact(mob/user) - crew_monitor.ui_interact(user) +/obj/machinery/computer/crew + name = "crew monitoring computer" + desc = "Used to monitor active health sensors built into most of the crew's uniforms." + icon_keyboard = "med_key" + icon_screen = "crew" + use_power = IDLE_POWER_USE + idle_power_usage = 250 + active_power_usage = 500 + light_color = LIGHT_COLOR_DARKBLUE + circuit = /obj/item/circuitboard/crew + var/datum/nano_module/crew_monitor/crew_monitor + +/obj/machinery/computer/crew/New() + crew_monitor = new(src) + ..() + +/obj/machinery/computer/crew/Destroy() + QDEL_NULL(crew_monitor) + return ..() + +/obj/machinery/computer/crew/attack_ai(mob/user) + attack_hand(user) + ui_interact(user) + + +/obj/machinery/computer/crew/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + +/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + crew_monitor.ui_interact(user, ui_key, ui, force_open) + +/obj/machinery/computer/crew/interact(mob/user) + crew_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/depot.dm b/code/game/machinery/computer/depot.dm index 382e1325f9d0..62c82d9bd958 100644 --- a/code/game/machinery/computer/depot.dm +++ b/code/game/machinery/computer/depot.dm @@ -543,4 +543,4 @@ to_chat(user, "[B] has been recalled.") qdel(B) raise_alert("Sentry bot removed via emergency recall.") - playsound(user, sound_yes, 50, 0) \ No newline at end of file + playsound(user, sound_yes, 50, 0) diff --git a/code/game/machinery/computer/honkputer.dm b/code/game/machinery/computer/honkputer.dm index 94787d610208..9f5d77a642c9 100644 --- a/code/game/machinery/computer/honkputer.dm +++ b/code/game/machinery/computer/honkputer.dm @@ -115,4 +115,4 @@ A.icon_state = "4" qdel(src) else - return ..() \ No newline at end of file + return ..() diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 5defd5e8a69e..9464c43b107e 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,572 +1,572 @@ -#define MED_DATA_MAIN 1 // Main menu -#define MED_DATA_R_LIST 2 // Record list -#define MED_DATA_MAINT 3 // Records maintenance -#define MED_DATA_RECORD 4 // Record -#define MED_DATA_V_DATA 5 // Virus database -#define MED_DATA_MEDBOT 6 // Medbot monitor - -/obj/machinery/computer/med_data //TODO:SANITY - name = "medical records console" - desc = "This can be used to check medical records." - icon_keyboard = "med_key" - icon_screen = "medcomp" - req_one_access = list(ACCESS_MEDICAL, ACCESS_FORENSICS_LOCKERS) - circuit = /obj/item/circuitboard/med_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - - light_color = LIGHT_COLOR_DARKBLUE - -/obj/machinery/computer/med_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - usr.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) - return - return ..() - -/obj/machinery/computer/med_data/attack_hand(mob/user) - if(..()) - return - if(is_away_level(z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380) - ui.open() - -/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) - if(MED_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) - if(MED_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/medical = list() - data["medical"] = medical - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - var/list/fields = list() - medical["fields"] = fields - fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) - fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - medical["comments"] = active2.fields["comments"] - medical["empty"] = 0 - else - medical["empty"] = 1 - if(MED_DATA_V_DATA) - data["virus"] = list() - for(var/D in typesof(/datum/disease)) - var/datum/disease/DS = new D(0) - if(istype(DS, /datum/disease/advance)) - continue - if(!DS.desc) - continue - data["virus"] += list(list("name" = DS.name, "D" = D)) - if(MED_DATA_MEDBOT) - data["medbots"] = list() - for(var/mob/living/simple_animal/bot/medbot/M in world) - if(M.z != z) - continue - var/turf/T = get_turf(M) - if(T) - var/medbot = list() - medbot["name"] = M.name - medbot["x"] = T.x - medbot["y"] = T.y - medbot["on"] = M.on - if(!isnull(M.reagent_glass) && M.use_beaker) - medbot["use_beaker"] = 1 - medbot["total_volume"] = M.reagent_glass.reagents.total_volume - medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume - else - medbot["use_beaker"] = 0 - data["medbots"] += list(medbot) - return data - -/obj/machinery/computer/med_data/Topic(href, href_list) - if(..()) - return 1 - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.medical.Find(active2)) - active2 = null - - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in data_core.medical) - qdel(R) - setTemp("

    All records deleted.

    ") - if("p_stat") - if(active1) - switch(temp_href[2]) - if("deceased") - active1.fields["p_stat"] = "*Deceased*" - if("ssd") - active1.fields["p_stat"] = "*SSD*" - if("active") - active1.fields["p_stat"] = "Active" - if("unfit") - active1.fields["p_stat"] = "Physically Unfit" - if("disabled") - active1.fields["p_stat"] = "Disabled" - if("m_stat") - if(active1) - switch(temp_href[2]) - if("insane") - active1.fields["m_stat"] = "*Insane*" - if("unstable") - active1.fields["m_stat"] = "*Unstable*" - if("watch") - active1.fields["m_stat"] = "*Watch*" - if("stable") - active1.fields["m_stat"] = "Stable" - if("blood_type") - if(active2) - switch(temp_href[2]) - if("an") - active2.fields["blood_type"] = "A-" - if("bn") - active2.fields["blood_type"] = "B-" - if("abn") - active2.fields["blood_type"] = "AB-" - if("on") - active2.fields["blood_type"] = "O-" - if("ap") - active2.fields["blood_type"] = "A+" - if("bp") - active2.fields["blood_type"] = "B+" - if("abp") - active2.fields["blood_type"] = "AB+" - if("op") - active2.fields["blood_type"] = "O+" - if("del_r2") - QDEL_NULL(active2) - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - - if(authenticated) - active1 = null - active2 = null - screen = MED_DATA_MAIN - - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null - - if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = MED_DATA_MAIN - - active1 = null - active2 = null - - if(href_list["vir"]) - var/type = href_list["vir"] - var/datum/disease/D = new type(0) - var/afs = "" - for(var/mob/M in D.viable_mobtypes) - afs += "[initial(M.name)];" - var/severity = D.severity - switch(severity) - if("Harmful", "Minor") - severity = "[severity]" - if("Medium") - severity = "[severity]" - if("Dangerous!") - severity = "[severity]" - if("BIOHAZARD THREAT!") - severity = "

    [severity]

    " - setTemp({"Name: [D.name] -
    Number of stages: [D.max_stages] -
    Spread: [D.spread_text] Transmission -
    Possible Cure: [(D.cure_text||"none")] -
    Affected Lifeforms:[afs]
    -
    Notes: [D.desc]
    -
    Severity: [severity]"}) - qdel(D) - - if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1") - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null) - setTemp("

    Are you sure you wish to delete all records?

    ", buttons) - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis"] = t1 - if("mi_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_dis_d"] = t1 - if("ma_dis") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis"] = t1 - if("ma_dis_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_dis_d"] = t1 - if("alg") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg"] = t1 - if("alg_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["alg_d"] = t1 - if("cdi") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi"] = t1 - if("cdi_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["cdi_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("p_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null)) - setTemp("

    Physical Condition

    ", buttons) - if("m_stat") - if(istype(active1, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null)) - setTemp("

    Mental Condition

    ", buttons) - if("blood_type") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null)) - setTemp("

    Blood Type

    ", buttons) - if("b_dna") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["b_dna"] = t1 - if("vir_name") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["name"] = t1 - if("vir_desc") - var/datum/data/record/v = locate(href_list["edit_vir"]) - if(v) - var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - v.fields["description"] = t1 - - if(href_list["del_r"]) - if(active2) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (Medical Portion Only)?

    ", buttons) - - if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - for(var/datum/data/record/E in data_core.medical) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = MED_DATA_RECORD - - if(href_list["new"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Medical Record #[R.fields["id"]]" - R.fields["blood_type"] = "Unknown" - R.fields["b_dna"] = "Unknown" - R.fields["mi_dis"] = "None" - R.fields["mi_dis_d"] = "No minor disabilities have been declared." - R.fields["ma_dis"] = "None" - R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." - R.fields["alg"] = "None" - R.fields["alg_d"] = "No allergies have been detected in this patient." - R.fields["cdi"] = "None" - R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." - R.fields["notes"] = "No notes." - data_core.medical += R - active2 = R - screen = MED_DATA_RECORD - - if(href_list["add_c"]) - if(!istype(active2, /datum/data/record)) - return 1 - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [station_time_timestamp()]
    [t1]" - - if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["search"]) - var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null) - if(!t1 || ..()) - return 1 - active1 = null - active2 = null - t1 = lowertext(t1) - for(var/datum/data/record/R in data_core.medical) - if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) - active2 = R - if(!active2) - setTemp("

    Could not locate record [t1].

    ") - else - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) - active1 = E - screen = MED_DATA_RECORD - - if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Medical Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - P.info += {"
    \n
    Medical Data
    -
    \nBlood Type: [active2.fields["blood_type"]] -
    \nDNA: [active2.fields["b_dna"]]
    \n -
    \nMinor Disabilities: [active2.fields["mi_dis"]] -
    \nDetails: [active2.fields["mi_dis_d"]]
    \n -
    \nMajor Disabilities: [active2.fields["ma_dis"]] -
    \nDetails: [active2.fields["ma_dis_d"]]
    \n -
    \nAllergies: [active2.fields["alg"]] -
    \nDetails: [active2.fields["alg_d"]]
    \n -
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) -
    \nDetails: [active2.fields["cdi_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n -
    \n -
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c]
    " - else - P.info += "Medical Record Lost!
    " - P.info += "
    " - P.name = "paper- 'Medical Record: [active1.fields["name"]]'" - printing = 0 - return 1 - -/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) - -/obj/machinery/computer/med_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return ..(severity) - - for(var/datum/data/record/R in data_core.medical) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["blood_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") - if(5) - R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - - -/obj/machinery/computer/med_data/laptop - name = "medical laptop" - desc = "Cheap Nanotrasen laptop." - icon_state = "laptop" - icon_keyboard = "laptop_key" - icon_screen = "medlaptop" - density = 0 - -#undef MED_DATA_MAIN -#undef MED_DATA_R_LIST -#undef MED_DATA_MAINT -#undef MED_DATA_RECORD -#undef MED_DATA_V_DATA -#undef MED_DATA_MEDBOT +#define MED_DATA_MAIN 1 // Main menu +#define MED_DATA_R_LIST 2 // Record list +#define MED_DATA_MAINT 3 // Records maintenance +#define MED_DATA_RECORD 4 // Record +#define MED_DATA_V_DATA 5 // Virus database +#define MED_DATA_MEDBOT 6 // Medbot monitor + +/obj/machinery/computer/med_data //TODO:SANITY + name = "medical records console" + desc = "This can be used to check medical records." + icon_keyboard = "med_key" + icon_screen = "medcomp" + req_one_access = list(ACCESS_MEDICAL, ACCESS_FORENSICS_LOCKERS) + circuit = /obj/item/circuitboard/med_data + var/obj/item/card/id/scan = null + var/authenticated = null + var/rank = null + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/temp = null + var/printing = null + + light_color = LIGHT_COLOR_DARKBLUE + +/obj/machinery/computer/med_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/card/id) && !scan) + usr.drop_item() + O.forceMove(src) + scan = O + ui_interact(user) + return + return ..() + +/obj/machinery/computer/med_data/attack_hand(mob/user) + if(..()) + return + if(is_away_level(z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380) + ui.open() + +/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["screen"] = screen + if(authenticated) + switch(screen) + if(MED_DATA_R_LIST) + if(!isnull(GLOB.data_core.general)) + var/list/records = list() + data["records"] = records + for(var/datum/data/record/R in sortRecord(GLOB.data_core.general)) + records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) + if(MED_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) + fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) + fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") + fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") + fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") + fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") + fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = list("photo" = active1.fields["photo-south"]) + photos[++photos.len] = list("photo" = active1.fields["photo-west"]) + general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/medical = list() + data["medical"] = medical + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + var/list/fields = list() + medical["fields"] = fields + fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) + fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) + fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + medical["comments"] = active2.fields["comments"] + medical["empty"] = 0 + else + medical["empty"] = 1 + if(MED_DATA_V_DATA) + data["virus"] = list() + for(var/D in typesof(/datum/disease)) + var/datum/disease/DS = new D(0) + if(istype(DS, /datum/disease/advance)) + continue + if(!DS.desc) + continue + data["virus"] += list(list("name" = DS.name, "D" = D)) + if(MED_DATA_MEDBOT) + data["medbots"] = list() + for(var/mob/living/simple_animal/bot/medbot/M in world) + if(M.z != z) + continue + var/turf/T = get_turf(M) + if(T) + var/medbot = list() + medbot["name"] = M.name + medbot["x"] = T.x + medbot["y"] = T.y + medbot["on"] = M.on + if(!isnull(M.reagent_glass) && M.use_beaker) + medbot["use_beaker"] = 1 + medbot["total_volume"] = M.reagent_glass.reagents.total_volume + medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume + else + medbot["use_beaker"] = 0 + data["medbots"] += list(medbot) + return data + +/obj/machinery/computer/med_data/Topic(href, href_list) + if(..()) + return 1 + + if(!GLOB.data_core.general.Find(active1)) + active1 = null + if(!GLOB.data_core.medical.Find(active2)) + active2 = null + + if(href_list["temp"]) + temp = null + + if(href_list["temp_action"]) + if(href_list["temp_action"]) + var/temp_href = splittext(href_list["temp_action"], "=") + switch(temp_href[1]) + if("del_all2") + for(var/datum/data/record/R in GLOB.data_core.medical) + qdel(R) + setTemp("

    All records deleted.

    ") + if("p_stat") + if(active1) + switch(temp_href[2]) + if("deceased") + active1.fields["p_stat"] = "*Deceased*" + if("ssd") + active1.fields["p_stat"] = "*SSD*" + if("active") + active1.fields["p_stat"] = "Active" + if("unfit") + active1.fields["p_stat"] = "Physically Unfit" + if("disabled") + active1.fields["p_stat"] = "Disabled" + if("m_stat") + if(active1) + switch(temp_href[2]) + if("insane") + active1.fields["m_stat"] = "*Insane*" + if("unstable") + active1.fields["m_stat"] = "*Unstable*" + if("watch") + active1.fields["m_stat"] = "*Watch*" + if("stable") + active1.fields["m_stat"] = "Stable" + if("blood_type") + if(active2) + switch(temp_href[2]) + if("an") + active2.fields["blood_type"] = "A-" + if("bn") + active2.fields["blood_type"] = "B-" + if("abn") + active2.fields["blood_type"] = "AB-" + if("on") + active2.fields["blood_type"] = "O-" + if("ap") + active2.fields["blood_type"] = "A+" + if("bp") + active2.fields["blood_type"] = "B+" + if("abp") + active2.fields["blood_type"] = "AB+" + if("op") + active2.fields["blood_type"] = "O+" + if("del_r2") + QDEL_NULL(active2) + + if(href_list["scan"]) + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + + if(href_list["login"]) + if(isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + else if(istype(scan, /obj/item/card/id)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + + if(authenticated) + active1 = null + active2 = null + screen = MED_DATA_MAIN + + if(authenticated) + if(href_list["logout"]) + authenticated = null + screen = null + active1 = null + active2 = null + + if(href_list["screen"]) + screen = text2num(href_list["screen"]) + if(screen < 1) + screen = MED_DATA_MAIN + + active1 = null + active2 = null + + if(href_list["vir"]) + var/type = href_list["vir"] + var/datum/disease/D = new type(0) + var/afs = "" + for(var/mob/M in D.viable_mobtypes) + afs += "[initial(M.name)];" + var/severity = D.severity + switch(severity) + if("Harmful", "Minor") + severity = "[severity]" + if("Medium") + severity = "[severity]" + if("Dangerous!") + severity = "[severity]" + if("BIOHAZARD THREAT!") + severity = "

    [severity]

    " + setTemp({"Name: [D.name] +
    Number of stages: [D.max_stages] +
    Spread: [D.spread_text] Transmission +
    Possible Cure: [(D.cure_text||"none")] +
    Affected Lifeforms:[afs]
    +
    Notes: [D.desc]
    +
    Severity: [severity]"}) + qdel(D) + + if(href_list["del_all"]) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1") + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null) + setTemp("

    Are you sure you wish to delete all records?

    ", buttons) + + if(href_list["field"]) + if(..()) + return 1 + var/a1 = active1 + var/a2 = active2 + switch(href_list["field"]) + if("fingerprint") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["fingerprint"] = t1 + if("sex") + if(istype(active1, /datum/data/record)) + if(active1.fields["sex"] == "Male") + active1.fields["sex"] = "Female" + else + active1.fields["sex"] = "Male" + if("age") + if(istype(active1, /datum/data/record)) + var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["age"] = t1 + if("mi_dis") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_dis"] = t1 + if("mi_dis_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_dis_d"] = t1 + if("ma_dis") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_dis"] = t1 + if("ma_dis_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_dis_d"] = t1 + if("alg") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["alg"] = t1 + if("alg_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["alg_d"] = t1 + if("cdi") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["cdi"] = t1 + if("cdi_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["cdi_d"] = t1 + if("notes") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["notes"] = t1 + if("p_stat") + if(istype(active1, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null)) + setTemp("

    Physical Condition

    ", buttons) + if("m_stat") + if(istype(active1, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null)) + setTemp("

    Mental Condition

    ", buttons) + if("blood_type") + if(istype(active2, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null)) + setTemp("

    Blood Type

    ", buttons) + if("b_dna") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["b_dna"] = t1 + if("vir_name") + var/datum/data/record/v = locate(href_list["edit_vir"]) + if(v) + var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + v.fields["name"] = t1 + if("vir_desc") + var/datum/data/record/v = locate(href_list["edit_vir"]) + if(v) + var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + v.fields["description"] = t1 + + if(href_list["del_r"]) + if(active2) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete the record (Medical Portion Only)?

    ", buttons) + + if(href_list["d_rec"]) + var/datum/data/record/R = locate(href_list["d_rec"]) + var/datum/data/record/M = locate(href_list["d_rec"]) + if(!GLOB.data_core.general.Find(R)) + setTemp("

    Record not found!

    ") + return 1 + for(var/datum/data/record/E in GLOB.data_core.medical) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + M = E + active1 = R + active2 = M + screen = MED_DATA_RECORD + + if(href_list["new"]) + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Medical Record #[R.fields["id"]]" + R.fields["blood_type"] = "Unknown" + R.fields["b_dna"] = "Unknown" + R.fields["mi_dis"] = "None" + R.fields["mi_dis_d"] = "No minor disabilities have been declared." + R.fields["ma_dis"] = "None" + R.fields["ma_dis_d"] = "No major disabilities have been diagnosed." + R.fields["alg"] = "None" + R.fields["alg_d"] = "No allergies have been detected in this patient." + R.fields["cdi"] = "None" + R.fields["cdi_d"] = "No diseases have been diagnosed at the moment." + R.fields["notes"] = "No notes." + GLOB.data_core.medical += R + active2 = R + screen = MED_DATA_RECORD + + if(href_list["add_c"]) + if(!istype(active2, /datum/data/record)) + return 1 + var/a2 = active2 + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + + if(href_list["del_c"]) + var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) + if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) + active2.fields["comments"] -= active2.fields["comments"][index] + + if(href_list["search"]) + var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null) + if(!t1 || ..()) + return 1 + active1 = null + active2 = null + t1 = lowertext(t1) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])) + active2 = R + if(!active2) + setTemp("

    Could not locate record [t1].

    ") + else + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"]) + active1 = E + screen = MED_DATA_RECORD + + if(href_list["print_p"]) + if(!printing) + printing = 1 + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + sleep(50) + var/obj/item/paper/P = new /obj/item/paper(loc) + P.info = "
    Medical Record

    " + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2)) + P.info += {"
    \n
    Medical Data
    +
    \nBlood Type: [active2.fields["blood_type"]] +
    \nDNA: [active2.fields["b_dna"]]
    \n +
    \nMinor Disabilities: [active2.fields["mi_dis"]] +
    \nDetails: [active2.fields["mi_dis_d"]]
    \n +
    \nMajor Disabilities: [active2.fields["ma_dis"]] +
    \nDetails: [active2.fields["ma_dis_d"]]
    \n +
    \nAllergies: [active2.fields["alg"]] +
    \nDetails: [active2.fields["alg_d"]]
    \n +
    \nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section) +
    \nDetails: [active2.fields["cdi_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n +
    \n +
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c]
    " + else + P.info += "Medical Record Lost!
    " + P.info += "
    " + P.name = "paper- 'Medical Record: [active1.fields["name"]]'" + printing = 0 + return 1 + +/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) + temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + +/obj/machinery/computer/med_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return ..(severity) + + for(var/datum/data/record/R in GLOB.data_core.medical) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["blood_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") + if(5) + R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + + +/obj/machinery/computer/med_data/laptop + name = "medical laptop" + desc = "Cheap Nanotrasen laptop." + icon_state = "laptop" + icon_keyboard = "laptop_key" + icon_screen = "medlaptop" + density = 0 + +#undef MED_DATA_MAIN +#undef MED_DATA_R_LIST +#undef MED_DATA_MAINT +#undef MED_DATA_RECORD +#undef MED_DATA_V_DATA +#undef MED_DATA_MEDBOT diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 55107809ce32..7fa1dedf4113 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -18,7 +18,7 @@ var/noserver = "ALERT: No server detected." var/incorrectkey = "ALERT: Incorrect decryption key!" var/defaultmsg = "Welcome. Please select an option." - var/rebootmsg = "%$&(£: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" + var/rebootmsg = "%$&(�: Critical %$$@ Error // !RestArting! - ?pLeaSe wAit!" //Computer properties var/screen = 0 // 0 = Main menu, 1 = Message Logs, 2 = Hacked screen, 3 = Custom Message var/hacking = 0 // Is it being hacked into by the AI/Cyborg @@ -54,7 +54,7 @@ MK.loc = src.loc playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) // Will help make emagging the console not so easy to get away with. - MK.info += "

    £%@%(*$%&(£&?*(%&£/{}" + MK.info += "

    �%@%(*$%&(�&?*(%&�/{}" update_icon() spawn(100*length(src.linkedServer.decryptkey)) UnmagConsole() @@ -75,8 +75,8 @@ ..() //Is the server isn't linked to a server, and there's a server available, default it to the first one in the list. if(!linkedServer) - if(message_servers && message_servers.len > 0) - linkedServer = message_servers[1] + if(GLOB.message_servers && GLOB.message_servers.len > 0) + linkedServer = GLOB.message_servers[1] return /obj/machinery/computer/message_monitor/attack_hand(var/mob/user as mob) @@ -288,11 +288,11 @@ if(auth) linkedServer.active = !linkedServer.active //Find a server if(href_list["find"]) - if(message_servers && message_servers.len > 1) - src.linkedServer = input(usr,"Please select a server.", "Select a server.", null) as null|anything in message_servers + if(GLOB.message_servers && GLOB.message_servers.len > 1) + src.linkedServer = input(usr,"Please select a server.", "Select a server.", null) as null|anything in GLOB.message_servers message = "NOTICE: Server selected." - else if(message_servers && message_servers.len > 0) - linkedServer = message_servers[1] + else if(GLOB.message_servers && GLOB.message_servers.len > 0) + linkedServer = GLOB.message_servers[1] message = "NOTICE: Only Single Server Detected - Server selected." else message = noserver @@ -396,13 +396,13 @@ if("Recepient") //Get out list of viable PDAs var/list/obj/item/pda/sendPDAs = list() - for(var/obj/item/pda/P in PDAs) + for(var/obj/item/pda/P in GLOB.PDAs) var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(!PM || !PM.can_receive()) continue sendPDAs += P - if(PDAs && PDAs.len > 0) + if(GLOB.PDAs && GLOB.PDAs.len > 0) customrecepient = input(usr, "Select a PDA from the list.") as null|anything in sortAtom(sendPDAs) else customrecepient = null @@ -436,7 +436,7 @@ return src.attack_hand(usr) var/obj/item/pda/PDARec = null - for(var/obj/item/pda/P in PDAs) + for(var/obj/item/pda/P in GLOB.PDAs) var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(!PM || !PM.can_receive()) @@ -486,8 +486,8 @@ /obj/item/paper/monitorkey/New() ..() spawn(10) - if(message_servers) - for(var/obj/machinery/message_server/server in message_servers) + if(GLOB.message_servers) + for(var/obj/machinery/message_server/server in GLOB.message_servers) if(!isnull(server)) if(!isnull(server.decryptkey)) info = "

    Daily Key Reset


    The new message monitor key is '[server.decryptkey]'.
    Please keep this a secret and away from the clown.
    If necessary, change the password to a more secure one." diff --git a/code/game/machinery/computer/pod_tracking_console.dm b/code/game/machinery/computer/pod_tracking_console.dm index cd6450c700c5..c4a6d5d440bd 100644 --- a/code/game/machinery/computer/pod_tracking_console.dm +++ b/code/game/machinery/computer/pod_tracking_console.dm @@ -20,7 +20,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/computer/podtracker/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/podtracker/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/list/pods[0] for(var/obj/item/spacepod_equipment/misc/tracker/TR in world) diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm index 416c73d2570e..bb38e59c800c 100644 --- a/code/game/machinery/computer/power.dm +++ b/code/game/machinery/computer/power.dm @@ -1,65 +1,65 @@ -/obj/machinery/computer/monitor - name = "power monitoring console" - desc = "Used to monitor power levels across the station." - icon_screen = "power" - icon_keyboard = "power_key" - use_power = ACTIVE_POWER_USE - idle_power_usage = 20 - active_power_usage = 80 - light_color = LIGHT_COLOR_ORANGE - circuit = /obj/item/circuitboard/powermonitor - var/datum/powernet/powernet = null - var/datum/nano_module/power_monitor/power_monitor - var/is_secret_monitor = FALSE - -/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. - name = "outdated power monitoring console" - desc = "It monitors power levels across the local powernet." - circuit = /obj/item/circuitboard/powermonitor/secret - is_secret_monitor = TRUE - -/obj/machinery/computer/monitor/New() - ..() - GLOB.power_monitors += src - GLOB.power_monitors = sortAtom(GLOB.power_monitors) - power_monitor = new(src) - -/obj/machinery/computer/monitor/Initialize() - ..() - powermonitor_repository.update_cache() - powernet = find_powernet() - -/obj/machinery/computer/monitor/Destroy() - GLOB.power_monitors -= src - powermonitor_repository.update_cache() - QDEL_NULL(power_monitor) - return ..() - -/obj/machinery/computer/monitor/power_change() - ..() - powermonitor_repository.update_cache() - -/obj/machinery/computer/monitor/proc/find_powernet() - var/obj/structure/cable/attached = null - var/turf/T = loc - if(isturf(T)) - attached = locate() in T - if(attached) - return attached.powernet - -/obj/machinery/computer/monitor/attack_ai(mob/user) - attack_hand(user) - -/obj/machinery/computer/monitor/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - // Update the powernet - powernet = find_powernet() - ui_interact(user) - -/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - power_monitor.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/monitor/interact(mob/user) - power_monitor.ui_interact(user) +/obj/machinery/computer/monitor + name = "power monitoring console" + desc = "Used to monitor power levels across the station." + icon_screen = "power" + icon_keyboard = "power_key" + use_power = ACTIVE_POWER_USE + idle_power_usage = 20 + active_power_usage = 80 + light_color = LIGHT_COLOR_ORANGE + circuit = /obj/item/circuitboard/powermonitor + var/datum/powernet/powernet = null + var/datum/nano_module/power_monitor/power_monitor + var/is_secret_monitor = FALSE + +/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. + name = "outdated power monitoring console" + desc = "It monitors power levels across the local powernet." + circuit = /obj/item/circuitboard/powermonitor/secret + is_secret_monitor = TRUE + +/obj/machinery/computer/monitor/New() + ..() + GLOB.power_monitors += src + GLOB.power_monitors = sortAtom(GLOB.power_monitors) + power_monitor = new(src) + +/obj/machinery/computer/monitor/Initialize() + ..() + GLOB.powermonitor_repository.update_cache() + powernet = find_powernet() + +/obj/machinery/computer/monitor/Destroy() + GLOB.power_monitors -= src + GLOB.powermonitor_repository.update_cache() + QDEL_NULL(power_monitor) + return ..() + +/obj/machinery/computer/monitor/power_change() + ..() + GLOB.powermonitor_repository.update_cache() + +/obj/machinery/computer/monitor/proc/find_powernet() + var/obj/structure/cable/attached = null + var/turf/T = loc + if(isturf(T)) + attached = locate() in T + if(attached) + return attached.powernet + +/obj/machinery/computer/monitor/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/monitor/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + // Update the powernet + powernet = find_powernet() + ui_interact(user) + +/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + power_monitor.ui_interact(user, ui_key, ui, force_open) + +/obj/machinery/computer/monitor/interact(mob/user) + power_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index fb4683ba3a9b..6134860733a9 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -1,240 +1,240 @@ -/obj/machinery/computer/robotics - name = "robotics control console" - desc = "Used to remotely lockdown or detonate linked Cyborgs." - icon = 'icons/obj/computer.dmi' - icon_keyboard = "tech_key" - icon_screen = "robot" - req_access = list(ACCESS_ROBOTICS) - circuit = /obj/item/circuitboard/robotics - var/temp = null - - light_color = LIGHT_COLOR_PURPLE - - var/safety = 1 - -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) - if(..()) - return - if(stat & (NOPOWER|BROKEN)) - return - ui_interact(user) - -/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) - if(user.can_admin_interact()) - return 1 - else if(allowed(user)) - return 1 - return 0 - -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/list/robots = get_cyborgs(user) - if(robots.len) - data["robots"] = robots - data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - data["allowed"] = is_authenticated(user) - return data - -/obj/machinery/computer/robotics/Topic(href, href_list) - if(..()) - return 1 - - var/mob/user = usr - if(!is_authenticated(user)) - to_chat(user, "Access denied.") - return - - // Destroys the cyborg - if(href_list["detonate"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) - if(!target || !istype(target)) - return - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - // Cyborgs may blow up themselves via the console - if((isrobot(user) && user != target) || !is_authenticated(user)) - to_chat(user, "Access Denied.") - return - var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") - if(choice != "Yes") - return - if(!target || !istype(target)) - return - - // Antagonistic cyborgs? Left here for downstream - if(target.mind && target.mind.special_role && target.emagged) - to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - target.ResetSecurityCodes() - else - message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") - log_game("\[key_name(usr)] detonated [key_name(target)]!") - to_chat(target, "Self-destruct command received.") - if(target.connected_ai) - to_chat(target.connected_ai, "

    ALERT - Cyborg detonation detected: [target.name]
    ") - spawn(10) - target.self_destruct() - - // Locks or unlocks the cyborg - else if(href_list["lockdown"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) - if(!target || !istype(target)) - return - - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(isrobot(user)) - to_chat(user, "Access Denied.") - return - - var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!") - log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!") - target.SetLockdown(!target.lockcharge) - to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") - if(target.connected_ai) - to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name]
    ") - - // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. - else if(href_list["hack"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) - if(!target || !istype(target)) - return - - // Antag AI checks - if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) - to_chat(user, "Access Denied.") - return - - if(target.connected_ai != user) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - - if(target.emagged) - to_chat(user, "Robot is already hacked.") - return - - var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") - if(choice != "Yes") - return - - if(!target || !istype(target)) - return - - message_admins("[key_name_admin(usr)] emagged [key_name_admin(target)] using robotic console!") - log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!") - target.emagged = 1 - to_chat(target, "Failsafe protocols overriden. New tools available.") - - // Arms the emergency self-destruct system - else if(href_list["arm"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied.") - return - - safety = !safety - to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct.") - - // Destroys all accessible cyborgs if safety is disabled - else if(href_list["nuke"]) - if(istype(user, /mob/living/silicon)) - to_chat(user, "Access Denied") - return - if(safety) - to_chat(user, "Self-destruct aborted - safety active") - return - - message_admins("[key_name_admin(usr)] detonated all cyborgs!") - log_game("\[key_name(usr)] detonated all cyborgs!") - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - to_chat(R, "Self-destruct command received.") - if(R.connected_ai) - to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") - spawn(10) - R.self_destruct() - -// Proc: get_cyborgs() -// Parameters: 1 (operator - mob which is operating the console.) -// Description: Returns NanoUI-friendly list of accessible cyborgs. -/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) - var/list/robots = list() - - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - // Ignore drones - if(istype(R, /mob/living/silicon/robot/drone)) - continue - // Ignore antagonistic cyborgs - if(R.scrambledcodes) - continue - - var/list/robot = list() - robot["name"] = R.name - if(R.stat) - robot["status"] = "Not Responding" - else if(!R.canmove) - robot["status"] = "Lockdown" - else - robot["status"] = "Operational" - - if(R.cell) - robot["cell"] = 1 - robot["cell_capacity"] = R.cell.maxcharge - robot["cell_current"] = R.cell.charge - robot["cell_percentage"] = round(R.cell.percent()) - else - robot["cell"] = 0 - - var/turf/pos = get_turf(R) - var/area/bot_area = get_area(R) - robot["xpos"] = pos.x - robot["ypos"] = pos.y - robot["zpos"] = pos.z - robot["area"] = format_text(bot_area.name) - - robot["health"] = round(R.health * 100 / R.maxHealth,0.1) - - robot["module"] = R.module ? R.module.name : "None" - robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" - robot["hackable"] = 0 - // Antag AIs know whether linked cyborgs are hacked or not. - if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) - robot["hacked"] = R.emagged ? 1 : 0 - robot["hackable"] = R.emagged? 0 : 1 - robots.Add(list(robot)) - return robots - -// Proc: get_cyborg_by_name() -// Parameters: 1 (name - Cyborg we are trying to find) -// Description: Helper proc for finding cyborg by name -/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) - if(!name) - return - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.name == name) - return R +/obj/machinery/computer/robotics + name = "robotics control console" + desc = "Used to remotely lockdown or detonate linked Cyborgs." + icon = 'icons/obj/computer.dmi' + icon_keyboard = "tech_key" + icon_screen = "robot" + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/robotics + var/temp = null + + light_color = LIGHT_COLOR_PURPLE + + var/safety = 1 + +/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) + return attack_hand(user) + +/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) + if(..()) + return + if(stat & (NOPOWER|BROKEN)) + return + ui_interact(user) + +/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) + if(user.can_admin_interact()) + return 1 + else if(allowed(user)) + return 1 + return 0 + +/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/list/robots = get_cyborgs(user) + if(robots.len) + data["robots"] = robots + data["safety"] = safety + // Also applies for cyborgs. Hides the manual self-destruct button. + data["is_ai"] = issilicon(user) + data["allowed"] = is_authenticated(user) + return data + +/obj/machinery/computer/robotics/Topic(href, href_list) + if(..()) + return 1 + + var/mob/user = usr + if(!is_authenticated(user)) + to_chat(user, "Access denied.") + return + + // Destroys the cyborg + if(href_list["detonate"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) + if(!target || !istype(target)) + return + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + // Cyborgs may blow up themselves via the console + if((isrobot(user) && user != target) || !is_authenticated(user)) + to_chat(user, "Access Denied.") + return + var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") + if(choice != "Yes") + return + if(!target || !istype(target)) + return + + // Antagonistic cyborgs? Left here for downstream + if(target.mind && target.mind.special_role && target.emagged) + to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") + target.ResetSecurityCodes() + else + message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") + log_game("\[key_name(usr)] detonated [key_name(target)]!") + to_chat(target, "Self-destruct command received.") + if(target.connected_ai) + to_chat(target.connected_ai, "

    ALERT - Cyborg detonation detected: [target.name]
    ") + spawn(10) + target.self_destruct() + + // Locks or unlocks the cyborg + else if(href_list["lockdown"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["lockdown"]) + if(!target || !istype(target)) + return + + if(isAI(user) && (target.connected_ai != user)) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + + if(isrobot(user)) + to_chat(user, "Access Denied.") + return + + var/choice = input("Really [target.lockcharge ? "unlock" : "lockdown"] [target.name] ?") in list ("Yes", "No") + if(choice != "Yes") + return + + if(!target || !istype(target)) + return + + message_admins("[key_name_admin(usr)] [target.canmove ? "locked down" : "released"] [key_name_admin(target)]!") + log_game("[key_name(usr)] [target.canmove ? "locked down" : "released"] [key_name(target)]!") + target.SetLockdown(!target.lockcharge) + to_chat(target, "[!target.lockcharge ? "Your lockdown has been lifted!" : "You have been locked down!"]") + if(target.connected_ai) + to_chat(target.connected_ai, "[!target.lockcharge ? "NOTICE - Cyborg lockdown lifted" : "ALERT - Cyborg lockdown detected"]: [target.name]
    ") + + // Remotely hacks the cyborg. Only antag AIs can do this and only to linked cyborgs. + else if(href_list["hack"]) + var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["hack"]) + if(!target || !istype(target)) + return + + // Antag AI checks + if(!istype(user, /mob/living/silicon/ai) || !(user.mind.special_role && user.mind.original == user)) + to_chat(user, "Access Denied.") + return + + if(target.connected_ai != user) + to_chat(user, "Access Denied. This robot is not linked to you.") + return + + if(target.emagged) + to_chat(user, "Robot is already hacked.") + return + + var/choice = input("Really hack [target.name]? This cannot be undone.") in list("Yes", "No") + if(choice != "Yes") + return + + if(!target || !istype(target)) + return + + message_admins("[key_name_admin(usr)] emagged [key_name_admin(target)] using robotic console!") + log_game("[key_name(usr)] emagged [key_name(target)] using robotic console!") + target.emagged = 1 + to_chat(target, "Failsafe protocols overriden. New tools available.") + + // Arms the emergency self-destruct system + else if(href_list["arm"]) + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access Denied.") + return + + safety = !safety + to_chat(user, "You [safety ? "disarm" : "arm"] the emergency self destruct.") + + // Destroys all accessible cyborgs if safety is disabled + else if(href_list["nuke"]) + if(istype(user, /mob/living/silicon)) + to_chat(user, "Access Denied") + return + if(safety) + to_chat(user, "Self-destruct aborted - safety active") + return + + message_admins("[key_name_admin(usr)] detonated all cyborgs!") + log_game("\[key_name(usr)] detonated all cyborgs!") + + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + to_chat(R, "Self-destruct command received.") + if(R.connected_ai) + to_chat(R.connected_ai, "

    ALERT - Cyborg detonation detected: [R.name]
    ") + spawn(10) + R.self_destruct() + +// Proc: get_cyborgs() +// Parameters: 1 (operator - mob which is operating the console.) +// Description: Returns NanoUI-friendly list of accessible cyborgs. +/obj/machinery/computer/robotics/proc/get_cyborgs(var/mob/operator) + var/list/robots = list() + + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + // Ignore drones + if(istype(R, /mob/living/silicon/robot/drone)) + continue + // Ignore antagonistic cyborgs + if(R.scrambledcodes) + continue + + var/list/robot = list() + robot["name"] = R.name + if(R.stat) + robot["status"] = "Not Responding" + else if(!R.canmove) + robot["status"] = "Lockdown" + else + robot["status"] = "Operational" + + if(R.cell) + robot["cell"] = 1 + robot["cell_capacity"] = R.cell.maxcharge + robot["cell_current"] = R.cell.charge + robot["cell_percentage"] = round(R.cell.percent()) + else + robot["cell"] = 0 + + var/turf/pos = get_turf(R) + var/area/bot_area = get_area(R) + robot["xpos"] = pos.x + robot["ypos"] = pos.y + robot["zpos"] = pos.z + robot["area"] = format_text(bot_area.name) + + robot["health"] = round(R.health * 100 / R.maxHealth,0.1) + + robot["module"] = R.module ? R.module.name : "None" + robot["master_ai"] = R.connected_ai ? R.connected_ai.name : "None" + robot["hackable"] = 0 + // Antag AIs know whether linked cyborgs are hacked or not. + if(operator && istype(operator, /mob/living/silicon/ai) && (R.connected_ai == operator) && (operator.mind.special_role && operator.mind.original == operator)) + robot["hacked"] = R.emagged ? 1 : 0 + robot["hackable"] = R.emagged? 0 : 1 + robots.Add(list(robot)) + return robots + +// Proc: get_cyborg_by_name() +// Parameters: 1 (name - Cyborg we are trying to find) +// Description: Helper proc for finding cyborg by name +/obj/machinery/computer/robotics/proc/get_cyborg_by_name(var/name) + if(!name) + return + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(R.name == name) + return R diff --git a/code/game/machinery/computer/salvage_ship.dm b/code/game/machinery/computer/salvage_ship.dm index df22a3cad143..18e4718bf971 100644 --- a/code/game/machinery/computer/salvage_ship.dm +++ b/code/game/machinery/computer/salvage_ship.dm @@ -106,4 +106,4 @@ return /obj/machinery/computer/salvage_ship/bullet_act(var/obj/item/projectile/Proj) - visible_message("[Proj] ricochets off [src]!") \ No newline at end of file + visible_message("[Proj] ricochets off [src]!") diff --git a/code/game/machinery/computer/security.dm b/code/game/machinery/computer/security.dm index 90ac427743dc..e886ac0db89c 100644 --- a/code/game/machinery/computer/security.dm +++ b/code/game/machinery/computer/security.dm @@ -1,578 +1,578 @@ -#define SEC_DATA_R_LIST 1 // Record list -#define SEC_DATA_MAINT 2 // Records maintenance -#define SEC_DATA_RECORD 3 // Record - -/obj/machinery/computer/secure_data//TODO:SANITY - name = "security records" - desc = "Used to view and edit personnel's security records." - icon_keyboard = "security_key" - icon_screen = "security" - req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) - circuit = /obj/item/circuitboard/secure_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/list/authcard_access = list() - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - //Sorting Variables - var/sortBy = "name" - var/order = 1 // -1 = Descending - 1 = Ascending - - light_color = LIGHT_COLOR_RED - -/obj/machinery/computer/secure_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - user.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) - return - return ..() - -//Someone needs to break down the dat += into chunks instead of long ass lines. -/obj/machinery/computer/secure_data/attack_hand(mob/user) - if(..()) - return - if(is_away_level(z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800) - ui.open() - -/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) - if(SEC_DATA_R_LIST) - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general, sortBy, order)) - var/crimstat = "null" - for(var/datum/data/record/E in data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - crimstat = E.fields["criminal"] - break - var/background = "''" - switch(crimstat) - if("*Execute*") - background = "'background-color:#5E0A1A'" - if("*Arrest*") - background = "'background-color:#890E26'" - if("Incarcerated") - background = "'background-color:#743B03'" - if("Parolled") - background = "'background-color:#743B03'" - if("Released") - background = "'background-color:#216489'" - if("None") - background = "'background-color:#007f47'" - if("null") - crimstat = "No record." - data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat)) - if(SEC_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name") - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id") - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null) - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null) - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/security = list() - data["security"] = security - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - var/list/fields = list() - security["fields"] = fields - fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - security["comments"] = active2.fields["comments"] - security["empty"] = 0 - else - security["empty"] = 1 - return data - -/obj/machinery/computer/secure_data/Topic(href, href_list) - if(..()) - return 1 - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.security.Find(active2)) - active2 = null - - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in data_core.security) - qdel(R) - update_all_mob_security_hud() - setTemp("

    All records deleted.

    ") - if("del_alllogs2") - if(GLOB.cell_logs.len) - setTemp("

    All cell logs deleted.

    ") - GLOB.cell_logs.Cut() - else - to_chat(usr, "Error; No cell logs to delete.") - if("del_r2") - if(active2) - qdel(active2) - update_all_mob_security_hud() - if("del_rg2") - if(active1) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) - qdel(R) - QDEL_NULL(active1) - QDEL_NULL(active2) - update_all_mob_security_hud() - screen = SEC_DATA_R_LIST - if("criminal") - if(active2) - var/t1 - if(temp_href[2] == "execute") - t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN) - else - t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1) - t1 = "(none)" - if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access)) - setTemp("

    Error: permission denied.

    ") - return 1 - if("rank") - if(active1) - active1.fields["rank"] = temp_href[2] - if(temp_href[2] in GLOB.joblist) - active1.fields["real_rank"] = temp_href[2] - - if(href_list["scan"]) - if(scan) - scan.forceMove(loc) - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(scan) - scan = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/card/id)) - usr.drop_item() - I.forceMove(src) - scan = I - - if(href_list["login"]) - if(isAI(usr)) - authenticated = usr.name - rank = "AI" - else if(isrobot(usr)) - authenticated = usr.name - var/mob/living/silicon/robot/R = usr - rank = "[R.modtype] [R.braintype]" - else if(istype(scan, /obj/item/card/id)) - if(check_access(scan)) - authenticated = scan.registered_name - rank = scan.assignment - authcard_access = scan.access - - if(authenticated) - active1 = null - active2 = null - screen = SEC_DATA_R_LIST - - if(authenticated) - if(href_list["logout"]) - authenticated = null - screen = null - active1 = null - active2 = null - authcard_access = list() - - else if(href_list["sort"]) - // Reverse the order if clicked twice - if(sortBy == href_list["sort"]) - if(order == 1) - order = -1 - else - order = 1 - else - sortBy = href_list["sort"] - order = initial(order) - - else if(href_list["screen"]) - screen = text2num(href_list["screen"]) - if(screen < 1) - screen = SEC_DATA_R_LIST - - active1 = null - active2 = null - - else if(href_list["d_rec"]) - var/datum/data/record/R = locate(href_list["d_rec"]) - var/datum/data/record/M = locate(href_list["d_rec"]) - if(!data_core.general.Find(R)) - setTemp("

    Record not found!

    ") - return 1 - for(var/datum/data/record/E in data_core.security) - if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) - M = E - active1 = R - active2 = M - screen = SEC_DATA_RECORD - - else if(href_list["del_all"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all records?

    ", buttons) - - else if(href_list["del_alllogs"]) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete all cell logs?

    ", buttons) - - else if(href_list["del_rg"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) - - else if(href_list["del_r"]) - if(active1) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) - buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) - setTemp("

    Are you sure you wish to delete the record (Security Portion Only)?

    ", buttons) - - else if(href_list["new_s"]) - if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) - var/datum/data/record/R = new /datum/data/record() - R.fields["name"] = active1.fields["name"] - R.fields["id"] = active1.fields["id"] - R.name = "Security Record #[R.fields["id"]]" - R.fields["criminal"] = "None" - R.fields["mi_crim"] = "None" - R.fields["mi_crim_d"] = "No minor crime convictions." - R.fields["ma_crim"] = "None" - R.fields["ma_crim_d"] = "No major crime convictions." - R.fields["notes"] = "No notes." - data_core.security += R - active2 = R - screen = SEC_DATA_RECORD - - else if(href_list["new_g"]) - var/datum/data/record/G = new /datum/data/record() - G.fields["name"] = "New Record" - G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" - G.fields["rank"] = "Unassigned" - G.fields["real_rank"] = "Unassigned" - G.fields["sex"] = "Male" - G.fields["age"] = "Unknown" - G.fields["fingerprint"] = "Unknown" - G.fields["p_stat"] = "Active" - G.fields["m_stat"] = "Stable" - G.fields["species"] = "Human" - data_core.general += G - active1 = G - active2 = null - - else if(href_list["print_r"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - var/obj/item/paper/P = new /obj/item/paper(loc) - P.info = "
    Security Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] -
    \nSex: [active1.fields["sex"]] -
    \nAge: [active1.fields["age"]] -
    \nFingerprint: [active1.fields["fingerprint"]] -
    \nPhysical Status: [active1.fields["p_stat"]] -
    \nMental Status: [active1.fields["m_stat"]]
    "} - else - P.info += "General Record Lost!
    " - if(istype(active2, /datum/data/record) && data_core.security.Find(active2)) - P.info += {"
    \n
    Security Data
    -
    \nCriminal Status: [active2.fields["criminal"]]
    \n -
    \nMinor Crimes: [active2.fields["mi_crim"]] -
    \nDetails: [active2.fields["mi_crim_d"]]
    \n -
    \nMajor Crimes: [active2.fields["ma_crim"]] -
    \nDetails: [active2.fields["ma_crim_d"]]
    \n -
    \nImportant Notes: -
    \n\t[active2.fields["notes"]]
    \n
    \n
    Comments/Log

    "} - for(var/c in active2.fields["comments"]) - P.info += "[c]
    " - else - P.info += "Security Record Lost!
    " - P.info += "" - P.name = "paper - 'Security Record: [active1.fields["name"]]'" - printing = 0 - -/* Removed due to BYOND issue - else if(href_list["print_p"]) - if(!printing) - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - sleep(50) - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - create_record_photo(active1) - printing = 0 -*/ - - else if(href_list["printlogs"]) - if(GLOB.cell_logs.len && !printing) - var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs - if(!P) - return 0 - printing = 1 - playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - to_chat(usr, "Printing file [P.name].") - sleep(50) - var/obj/item/paper/log = new /obj/item/paper(loc) - log.name = P.name - log.info = P.info - printing = 0 - return 1 - else - to_chat(usr, "[src] has no logs stored or is already printing.") - - - else if(href_list["add_c"]) - if(istype(active2, /datum/data/record)) - var/a2 = active2 - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["comments"] += "Made by [authenticated] ([rank]) on [current_date_string] [station_time_timestamp()]
    [t1]" - - else if(href_list["del_c"]) - var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) - if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) - active2.fields["comments"] -= active2.fields["comments"][index] - - if(href_list["field"]) - if(..()) - return 1 - var/a1 = active1 - var/a2 = active2 - switch(href_list["field"]) - if("name") - if(istype(active1, /datum/data/record)) - var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) - if(!t1 || !length(trim(t1)) || ..() || active1 != a1) - return 1 - active1.fields["name"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["name"] = t1 - if("id") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["id"] = t1 - if(istype(active2, /datum/data/record)) - active2.fields["id"] = t1 - if("fingerprint") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["fingerprint"] = t1 - if("sex") - if(istype(active1, /datum/data/record)) - if(active1.fields["sex"] == "Male") - active1.fields["sex"] = "Female" - else - active1.fields["sex"] = "Male" - if("age") - if(istype(active1, /datum/data/record)) - var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["age"] = t1 - if("mi_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim"] = t1 - if("mi_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["mi_crim_d"] = t1 - if("ma_crim") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim"] = t1 - if("ma_crim_d") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["ma_crim_d"] = t1 - if("notes") - if(istype(active2, /datum/data/record)) - var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active2 != a2) - return 1 - active2.fields["notes"] = t1 - if("criminal") - if(istype(active2, /datum/data/record)) - var/list/buttons = list() - buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == "None" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == "*Arrest*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == "Incarcerated" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == "*Execute*" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == "Parolled" ? "selected" : null)) - buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == "Released" ? "selected" : null)) - setTemp("

    Criminal Status

    ", buttons) - if("rank") - var/list/L = list("Head of Personnel", "Captain", "AI") - //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N - if(istype(active1, /datum/data/record) && L.Find(rank)) - var/list/buttons = list() - for(var/rank in GLOB.joblist) - buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) - setTemp("

    Rank

    ", buttons) - else - setTemp("

    You do not have the required rank to do this!

    ") - if("species") - if(istype(active1, /datum/data/record)) - var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || ..() || active1 != a1) - return 1 - active1.fields["species"] = t1 - return 1 - -/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) - temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) - -/* Proc disabled due to BYOND Issue - -/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) - // basically copy-pasted from the camera code but different enough that it has to be redone - var/icon/photoimage = get_record_photo(R) - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img, ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) - - var/datum/picture/P = new() - P.fields["name"] = "File Photo - [R.fields["name"]]" - P.fields["author"] = "Central Command" - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = "You can see [R.fields["name"]] on the photo." - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = 2 - - var/obj/item/photo/PH = new/obj/item/photo(loc) - PH.construct(P) - -*/ - -/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) - // similar to the code to make a photo, but of course the actual rendering is completely different - var/icon/res = icon('icons/effects/96x96.dmi', "") - // will be 2x2 to fit the 2 directions - res.Scale(2 * 32, 2 * 32) - // transparent background (it's a plastic transparency, you see) with the front and side icons - res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) - res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) - - return res - -/obj/machinery/computer/secure_data/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - for(var/datum/data/record/R in data_core.security) - if(prob(10/severity)) - switch(rand(1,6)) - if(1) - R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" - if(2) - R.fields["sex"] = pick("Male", "Female") - if(3) - R.fields["age"] = rand(5, 85) - if(4) - R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") - if(5) - R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") - if(6) - R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") - continue - - else if(prob(1)) - qdel(R) - continue - - ..(severity) - -/obj/machinery/computer/secure_data/detective_computer - icon = 'icons/obj/computer.dmi' - icon_state = "messyfiles" - -/obj/machinery/computer/secure_data/laptop - name = "security laptop" - desc = "Nanotrasen Security laptop. Bringing modern compact computing to this century!" - icon_state = "laptop" - icon_keyboard = "seclaptop_key" - icon_screen = "seclaptop" - density = 0 - -#undef SEC_DATA_R_LIST -#undef SEC_DATA_MAINT -#undef SEC_DATA_RECORD +#define SEC_DATA_R_LIST 1 // Record list +#define SEC_DATA_MAINT 2 // Records maintenance +#define SEC_DATA_RECORD 3 // Record + +/obj/machinery/computer/secure_data//TODO:SANITY + name = "security records" + desc = "Used to view and edit personnel's security records." + icon_keyboard = "security_key" + icon_screen = "security" + req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS) + circuit = /obj/item/circuitboard/secure_data + var/obj/item/card/id/scan = null + var/authenticated = null + var/rank = null + var/list/authcard_access = list() + var/screen = null + var/datum/data/record/active1 = null + var/datum/data/record/active2 = null + var/temp = null + var/printing = null + //Sorting Variables + var/sortBy = "name" + var/order = 1 // -1 = Descending - 1 = Ascending + + light_color = LIGHT_COLOR_RED + +/obj/machinery/computer/secure_data/Destroy() + active1 = null + active2 = null + return ..() + +/obj/machinery/computer/secure_data/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/card/id) && !scan) + user.drop_item() + O.forceMove(src) + scan = O + ui_interact(user) + return + return ..() + +//Someone needs to break down the dat += into chunks instead of long ass lines. +/obj/machinery/computer/secure_data/attack_hand(mob/user) + if(..()) + return + if(is_away_level(z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/secure_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "secure_data.tmpl", name, 800, 800) + ui.open() + +/obj/machinery/computer/secure_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["temp"] = temp + data["scan"] = scan ? scan.name : null + data["authenticated"] = authenticated + data["screen"] = screen + if(authenticated) + switch(screen) + if(SEC_DATA_R_LIST) + if(!isnull(GLOB.data_core.general)) + for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) + var/crimstat = "null" + for(var/datum/data/record/E in GLOB.data_core.security) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + crimstat = E.fields["criminal"] + break + var/background = "''" + switch(crimstat) + if("*Execute*") + background = "'background-color:#5E0A1A'" + if("*Arrest*") + background = "'background-color:#890E26'" + if("Incarcerated") + background = "'background-color:#743B03'" + if("Parolled") + background = "'background-color:#743B03'" + if("Released") + background = "'background-color:#216489'" + if("None") + background = "'background-color:#007f47'" + if("null") + crimstat = "No record." + data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"], "background" = background, "crimstat" = crimstat)) + if(SEC_DATA_RECORD) + var/list/general = list() + data["general"] = general + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + var/list/fields = list() + general["fields"] = fields + fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = "name") + fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = "id") + fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") + fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") + fields[++fields.len] = list("field" = "Rank:", "value" = active1.fields["rank"], "edit" = "rank") + fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") + fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = null) + fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = null) + var/list/photos = list() + general["photos"] = photos + photos[++photos.len] = list("photo" = active1.fields["photo-south"]) + photos[++photos.len] = list("photo" = active1.fields["photo-west"]) + general["has_photos"] += (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) + general["empty"] = 0 + else + general["empty"] = 1 + + var/list/security = list() + data["security"] = security + if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) + var/list/fields = list() + security["fields"] = fields + fields[++fields.len] = list("field" = "Criminal Status:", "value" = active2.fields["criminal"], "edit" = "criminal", "line_break" = 1) + fields[++fields.len] = list("field" = "Minor Crimes:", "value" = active2.fields["mi_crim"], "edit" = "mi_crim", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_crim_d"], "edit" = "mi_crim_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Major Crimes:", "value" = active2.fields["ma_crim"], "edit" = "ma_crim", "line_break" = 0) + fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_crim_d"], "edit" = "ma_crim_d", "line_break" = 1) + fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) + if(!active2.fields["comments"] || !islist(active2.fields["comments"])) + active2.fields["comments"] = list() + security["comments"] = active2.fields["comments"] + security["empty"] = 0 + else + security["empty"] = 1 + return data + +/obj/machinery/computer/secure_data/Topic(href, href_list) + if(..()) + return 1 + + if(!GLOB.data_core.general.Find(active1)) + active1 = null + if(!GLOB.data_core.security.Find(active2)) + active2 = null + + if(href_list["temp"]) + temp = null + + if(href_list["temp_action"]) + var/temp_href = splittext(href_list["temp_action"], "=") + switch(temp_href[1]) + if("del_all2") + for(var/datum/data/record/R in GLOB.data_core.security) + qdel(R) + update_all_mob_security_hud() + setTemp("

    All records deleted.

    ") + if("del_alllogs2") + if(GLOB.cell_logs.len) + setTemp("

    All cell logs deleted.

    ") + GLOB.cell_logs.Cut() + else + to_chat(usr, "Error; No cell logs to delete.") + if("del_r2") + if(active2) + qdel(active2) + update_all_mob_security_hud() + if("del_rg2") + if(active1) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) + qdel(R) + QDEL_NULL(active1) + QDEL_NULL(active2) + update_all_mob_security_hud() + screen = SEC_DATA_R_LIST + if("criminal") + if(active2) + var/t1 + if(temp_href[2] == "execute") + t1 = copytext(trim(sanitize(input("Explain why they are being executed. Include a list of their crimes, and victims.", "EXECUTION ORDER", null, null) as text)), 1, MAX_MESSAGE_LEN) + else + t1 = copytext(trim(sanitize(input("Enter Reason:", "Secure. records", null, null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1) + t1 = "(none)" + if(!set_criminal_status(usr, active2, temp_href[2], t1, rank, authcard_access)) + setTemp("

    Error: permission denied.

    ") + return 1 + if("rank") + if(active1) + active1.fields["rank"] = temp_href[2] + if(temp_href[2] in GLOB.joblist) + active1.fields["real_rank"] = temp_href[2] + + if(href_list["scan"]) + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) + scan = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + + if(href_list["login"]) + if(isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + else if(istype(scan, /obj/item/card/id)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + authcard_access = scan.access + + if(authenticated) + active1 = null + active2 = null + screen = SEC_DATA_R_LIST + + if(authenticated) + if(href_list["logout"]) + authenticated = null + screen = null + active1 = null + active2 = null + authcard_access = list() + + else if(href_list["sort"]) + // Reverse the order if clicked twice + if(sortBy == href_list["sort"]) + if(order == 1) + order = -1 + else + order = 1 + else + sortBy = href_list["sort"] + order = initial(order) + + else if(href_list["screen"]) + screen = text2num(href_list["screen"]) + if(screen < 1) + screen = SEC_DATA_R_LIST + + active1 = null + active2 = null + + else if(href_list["d_rec"]) + var/datum/data/record/R = locate(href_list["d_rec"]) + var/datum/data/record/M = locate(href_list["d_rec"]) + if(!GLOB.data_core.general.Find(R)) + setTemp("

    Record not found!

    ") + return 1 + for(var/datum/data/record/E in GLOB.data_core.security) + if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]) + M = E + active1 = R + active2 = M + screen = SEC_DATA_RECORD + + else if(href_list["del_all"]) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete all records?

    ", buttons) + + else if(href_list["del_alllogs"]) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_alllogs2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete all cell logs?

    ", buttons) + + else if(href_list["del_rg"]) + if(active1) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_rg2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) + + else if(href_list["del_r"]) + if(active1) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null) + buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null) + setTemp("

    Are you sure you wish to delete the record (Security Portion Only)?

    ", buttons) + + else if(href_list["new_s"]) + if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record)) + var/datum/data/record/R = new /datum/data/record() + R.fields["name"] = active1.fields["name"] + R.fields["id"] = active1.fields["id"] + R.name = "Security Record #[R.fields["id"]]" + R.fields["criminal"] = "None" + R.fields["mi_crim"] = "None" + R.fields["mi_crim_d"] = "No minor crime convictions." + R.fields["ma_crim"] = "None" + R.fields["ma_crim_d"] = "No major crime convictions." + R.fields["notes"] = "No notes." + GLOB.data_core.security += R + active2 = R + screen = SEC_DATA_RECORD + + else if(href_list["new_g"]) + var/datum/data/record/G = new /datum/data/record() + G.fields["name"] = "New Record" + G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" + G.fields["rank"] = "Unassigned" + G.fields["real_rank"] = "Unassigned" + G.fields["sex"] = "Male" + G.fields["age"] = "Unknown" + G.fields["fingerprint"] = "Unknown" + G.fields["p_stat"] = "Active" + G.fields["m_stat"] = "Stable" + G.fields["species"] = "Human" + GLOB.data_core.general += G + active1 = G + active2 = null + + else if(href_list["print_r"]) + if(!printing) + printing = 1 + playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + sleep(50) + var/obj/item/paper/P = new /obj/item/paper(loc) + P.info = "
    Security Record

    " + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) + P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]] +
    \nSex: [active1.fields["sex"]] +
    \nAge: [active1.fields["age"]] +
    \nFingerprint: [active1.fields["fingerprint"]] +
    \nPhysical Status: [active1.fields["p_stat"]] +
    \nMental Status: [active1.fields["m_stat"]]
    "} + else + P.info += "General Record Lost!
    " + if(istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)) + P.info += {"
    \n
    Security Data
    +
    \nCriminal Status: [active2.fields["criminal"]]
    \n +
    \nMinor Crimes: [active2.fields["mi_crim"]] +
    \nDetails: [active2.fields["mi_crim_d"]]
    \n +
    \nMajor Crimes: [active2.fields["ma_crim"]] +
    \nDetails: [active2.fields["ma_crim_d"]]
    \n +
    \nImportant Notes: +
    \n\t[active2.fields["notes"]]
    \n
    \n
    Comments/Log

    "} + for(var/c in active2.fields["comments"]) + P.info += "[c]
    " + else + P.info += "Security Record Lost!
    " + P.info += "" + P.name = "paper - 'Security Record: [active1.fields["name"]]'" + printing = 0 + +/* Removed due to BYOND issue + else if(href_list["print_p"]) + if(!printing) + printing = 1 + playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + sleep(50) + if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + create_record_photo(active1) + printing = 0 +*/ + + else if(href_list["printlogs"]) + if(GLOB.cell_logs.len && !printing) + var/obj/item/paper/P = input(usr, "Select log to print", "Available Cell Logs") as null|anything in GLOB.cell_logs + if(!P) + return 0 + printing = 1 + playsound(loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + to_chat(usr, "Printing file [P.name].") + sleep(50) + var/obj/item/paper/log = new /obj/item/paper(loc) + log.name = P.name + log.info = P.info + printing = 0 + return 1 + else + to_chat(usr, "[src] has no logs stored or is already printing.") + + + else if(href_list["add_c"]) + if(istype(active2, /datum/data/record)) + var/a2 = active2 + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Secure. records", null, null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + + else if(href_list["del_c"]) + var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"])) + if(istype(active2, /datum/data/record) && active2.fields["comments"][index]) + active2.fields["comments"] -= active2.fields["comments"][index] + + if(href_list["field"]) + if(..()) + return 1 + var/a1 = active1 + var/a2 = active2 + switch(href_list["field"]) + if("name") + if(istype(active1, /datum/data/record)) + var/t1 = reject_bad_name(clean_input("Please input name:", "Secure. records", active1.fields["name"], null)) + if(!t1 || !length(trim(t1)) || ..() || active1 != a1) + return 1 + active1.fields["name"] = t1 + if(istype(active2, /datum/data/record)) + active2.fields["name"] = t1 + if("id") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input id:", "Secure. records", active1.fields["id"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["id"] = t1 + if(istype(active2, /datum/data/record)) + active2.fields["id"] = t1 + if("fingerprint") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["fingerprint"] = t1 + if("sex") + if(istype(active1, /datum/data/record)) + if(active1.fields["sex"] == "Male") + active1.fields["sex"] = "Female" + else + active1.fields["sex"] = "Male" + if("age") + if(istype(active1, /datum/data/record)) + var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["age"] = t1 + if("mi_crim") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input minor crimes list:", "Secure. records", active2.fields["mi_crim"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_crim"] = t1 + if("mi_crim_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize minor crimes:", "Secure. records", active2.fields["mi_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["mi_crim_d"] = t1 + if("ma_crim") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please input major crimes list:", "Secure. records", active2.fields["ma_crim"], null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_crim"] = t1 + if("ma_crim_d") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please summarize major crimes:", "Secure. records", active2.fields["ma_crim_d"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["ma_crim_d"] = t1 + if("notes") + if(istype(active2, /datum/data/record)) + var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Secure. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active2 != a2) + return 1 + active2.fields["notes"] = t1 + if("criminal") + if(istype(active2, /datum/data/record)) + var/list/buttons = list() + buttons[++buttons.len] = list("name" = "None", "icon" = "unlock", "href" = "criminal=none", "status" = (active2.fields["criminal"] == "None" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Arrest*", "icon" = "lock", "href" = "criminal=arrest", "status" = (active2.fields["criminal"] == "*Arrest*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Incarcerated", "icon" = "lock", "href" = "criminal=incarcerated", "status" = (active2.fields["criminal"] == "Incarcerated" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "*Execute*", "icon" = "lock", "href" = "criminal=execute", "status" = (active2.fields["criminal"] == "*Execute*" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Parolled", "icon" = "unlock-alt", "href" = "criminal=parolled", "status" = (active2.fields["criminal"] == "Parolled" ? "selected" : null)) + buttons[++buttons.len] = list("name" = "Released", "icon" = "unlock", "href" = "criminal=released", "status" = (active2.fields["criminal"] == "Released" ? "selected" : null)) + setTemp("

    Criminal Status

    ", buttons) + if("rank") + var/list/L = list("Head of Personnel", "Captain", "AI") + //This was so silly before the change. Now it actually works without beating your head against the keyboard. /N + if(istype(active1, /datum/data/record) && L.Find(rank)) + var/list/buttons = list() + for(var/rank in GLOB.joblist) + buttons[++buttons.len] = list("name" = rank, "icon" = null, "href" = "rank=[rank]", "status" = (active1.fields["rank"] == rank ? "selected" : null)) + setTemp("

    Rank

    ", buttons) + else + setTemp("

    You do not have the required rank to do this!

    ") + if("species") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["species"] = t1 + return 1 + +/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) + temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + +/* Proc disabled due to BYOND Issue + +/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) + // basically copy-pasted from the camera code but different enough that it has to be redone + var/icon/photoimage = get_record_photo(R) + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img, ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + P.fields["name"] = "File Photo - [R.fields["name"]]" + P.fields["author"] = "Central Command" + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = "You can see [R.fields["name"]] on the photo." + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + P.fields["size"] = 2 + + var/obj/item/photo/PH = new/obj/item/photo(loc) + PH.construct(P) + +*/ + +/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) + // similar to the code to make a photo, but of course the actual rendering is completely different + var/icon/res = icon('icons/effects/96x96.dmi', "") + // will be 2x2 to fit the 2 directions + res.Scale(2 * 32, 2 * 32) + // transparent background (it's a plastic transparency, you see) with the front and side icons + res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) + res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) + + return res + +/obj/machinery/computer/secure_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in GLOB.data_core.security) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") + if(5) + R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + +/obj/machinery/computer/secure_data/detective_computer + icon = 'icons/obj/computer.dmi' + icon_state = "messyfiles" + +/obj/machinery/computer/secure_data/laptop + name = "security laptop" + desc = "Nanotrasen Security laptop. Bringing modern compact computing to this century!" + icon_state = "laptop" + icon_keyboard = "seclaptop_key" + icon_screen = "seclaptop" + density = 0 + +#undef SEC_DATA_R_LIST +#undef SEC_DATA_MAINT +#undef SEC_DATA_RECORD diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index e3c2918f335c..9e07fb43b285 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -52,7 +52,7 @@ ui = new(user, src, ui_key, "skills_data.tmpl", name, 800, 380) ui.open() -/obj/machinery/computer/skills/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/skills/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["temp"] = temp data["scan"] = scan ? scan.name : null @@ -61,13 +61,13 @@ if(authenticated) switch(screen) if(SKILL_DATA_R_LIST) - if(!isnull(data_core.general)) - for(var/datum/data/record/R in sortRecord(data_core.general, sortBy, order)) + if(!isnull(GLOB.data_core.general)) + for(var/datum/data/record/R in sortRecord(GLOB.data_core.general, sortBy, order)) data["records"] += list(list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"], "rank" = R.fields["rank"], "fingerprint" = R.fields["fingerprint"])) if(SKILL_DATA_RECORD) var/list/general = list() data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) var/list/fields = list() general["fields"] = fields fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "name" = "name") @@ -93,7 +93,7 @@ if(..()) return 1 - if(!data_core.general.Find(active1)) + if(!GLOB.data_core.general.Find(active1)) active1 = null if(href_list["temp"]) @@ -103,24 +103,24 @@ var/temp_list = splittext(href_list["temp_action"], "=") switch(temp_list[1]) if("del_all2") - if(PDA_Manifest && PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.security) + if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + for(var/datum/data/record/R in GLOB.data_core.security) qdel(R) setTemp("

    All employment records deleted.

    ") if("del_rg2") if(active1) - if(PDA_Manifest && PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.medical) + if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + for(var/datum/data/record/R in GLOB.data_core.medical) if(R.fields["name"] == active1.fields["name"] && R.fields["id"] == active1.fields["id"]) qdel(R) QDEL_NULL(active1) screen = SKILL_DATA_R_LIST if("rank") if(active1) - if(PDA_Manifest && PDA_Manifest.len) - PDA_Manifest.Cut() + if(GLOB.PDA_Manifest && GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() active1.fields["rank"] = temp_list[2] if(temp_list[2] in GLOB.joblist) active1.fields["real_rank"] = temp_list[2] @@ -182,7 +182,7 @@ else if(href_list["d_rec"]) var/datum/data/record/R = locate(href_list["d_rec"]) - if(!data_core.general.Find(R)) + if(!GLOB.data_core.general.Find(R)) setTemp("

    Record not found!

    ") return 1 active1 = R @@ -202,8 +202,8 @@ setTemp("

    Are you sure you wish to delete the record (ALL)?

    ", buttons) else if(href_list["new_g"]) - if(PDA_Manifest.len) - PDA_Manifest.Cut() + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() var/datum/data/record/G = new /datum/data/record() G.fields["name"] = "New Record" G.fields["id"] = "[add_zero(num2hex(rand(1, 1.6777215E7)), 6)]" @@ -215,7 +215,7 @@ G.fields["p_stat"] = "Active" G.fields["m_stat"] = "Stable" G.fields["species"] = "Human" - data_core.general += G + GLOB.data_core.general += G active1 = G else if(href_list["print_r"]) @@ -225,7 +225,7 @@ sleep(50) var/obj/item/paper/P = new /obj/item/paper(loc) P.info = "
    Employment Record

    " - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) + if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)) P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
    \nSex: [active1.fields["sex"]]
    \nAge: [active1.fields["age"]] @@ -300,7 +300,7 @@ ..(severity) return - for(var/datum/data/record/R in data_core.security) + for(var/datum/data/record/R in GLOB.data_core.security) if(prob(10/severity)) switch(rand(1,6)) if(1) diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 711aec59db52..f509ee42d348 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -1,340 +1,340 @@ -//Config stuff -#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station -#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock -#define SPECOPS_RETURN_DELAY 6000 //Time between the shuttle is capable of moving. - -var/specops_shuttle_moving_to_station = 0 -var/specops_shuttle_moving_to_centcom = 0 -var/specops_shuttle_at_station = 0 -var/specops_shuttle_can_send = 1 -var/specops_shuttle_time = 0 -var/specops_shuttle_timeleft = 0 - -/obj/machinery/computer/specops_shuttle - name = "\improper Spec. Ops. shuttle console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(ACCESS_CENT_SPECOPS) -// req_access = list(ACCESS_CENT_SPECOPS) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/specops_shuttle_timereset = 0 - -/proc/specops_return() - var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team", list(1,2)) - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - - var/area/start_location = locate(/area/shuttle/specops/station) - var/area/end_location = locate(/area/shuttle/specops/centcom) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived at Central Command. Operation has ended!") - - specops_shuttle_at_station = 0 - - for(var/obj/machinery/computer/specops_shuttle/S in world) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_process() - var/area/centcom/specops/special_ops = locate()//Where is the specops area located? - var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - //message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" - //announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/poddoor/M in special_ops) - switch(M.id_tag) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in world) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L.loc) - for(var/obj/effect/landmark/L in world) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder)) - //P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in special_ops) - switch(M.id_tag) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/poddoor/M in special_ops) - switch(M.id_tag)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - special_ops.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - var/area/start_location = locate(/area/shuttle/specops/centcom) - var/area/end_location = locate(/area/shuttle/specops/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - - for(var/obj/machinery/computer/specops_shuttle/S in world) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_can_move() - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) - return 0 - for(var/obj/machinery/computer/specops_shuttle/S in world) - if(world.timeofday <= S.specops_shuttle_timereset) - return 0 - return 1 - -/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) - to_chat(user, "Access Denied.") - return 1 - -/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob, params) - if(istype(I,/obj/item/card/emag)) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - else - return ..() - -/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - -//Commented out so admins can do shenanigans at their leisure. Also makes the force-spawned admin ERTs able to use the shuttle. -// if(sent_strike_team == 0 && send_emergency_team == 0) -// to_chat(usr, "The strike team has not yet deployed.") -// return - - if(..()) - return - - user.machine = src - var/dat - if(temp) - dat = temp - else - dat += {"
    Special Operations Shuttle
    - \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
    - [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/specops_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.machine = src - - if(href_list["sendtodock"]) - if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "Central Command will not allow the Special Operations shuttle to return yet.") - if(world.timeofday <= specops_shuttle_timereset) - if(((world.timeofday - specops_shuttle_timereset)/10) > 60) - to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") - to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") - return - - to_chat(usr, "The Special Operations shuttle will arrive at Central Command in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - specops_shuttle_moving_to_centcom = 1 - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_return() - - else if(href_list["sendtostation"]) - if(specops_shuttle_at_station || specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/centcom/specops/special_ops = locate() - if(special_ops) - special_ops.readyalert()//Trigger alarm for the spec ops area. - specops_shuttle_moving_to_station = 1 - - specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME - spawn(0) - specops_process() - - else if(href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station +#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock +#define SPECOPS_RETURN_DELAY 6000 //Time between the shuttle is capable of moving. + +GLOBAL_VAR_INIT(specops_shuttle_moving_to_station, 0) +GLOBAL_VAR_INIT(specops_shuttle_moving_to_centcom, 0) +GLOBAL_VAR_INIT(specops_shuttle_at_station, 0) +GLOBAL_VAR_INIT(specops_shuttle_can_send, 1) +GLOBAL_VAR_INIT(specops_shuttle_time, 0) +GLOBAL_VAR_INIT(specops_shuttle_timeleft, 0) + +/obj/machinery/computer/specops_shuttle + name = "\improper Spec. Ops. shuttle console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "security_key" + icon_screen = "syndishuttle" + light_color = LIGHT_COLOR_PURE_CYAN + req_access = list(ACCESS_CENT_SPECOPS) +// req_access = list(ACCESS_CENT_SPECOPS) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + var/specops_shuttle_timereset = 0 + +/proc/specops_return() + var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team", list(1,2)) + + while(GLOB.specops_shuttle_time - world.timeofday > 0) + var/ticksleft = GLOB.specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + GLOB.specops_shuttle_time = world.timeofday + 10 // midnight rollover + GLOB.specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(GLOB.specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + GLOB.specops_shuttle_moving_to_station = 0 + GLOB.specops_shuttle_moving_to_centcom = 0 + + GLOB.specops_shuttle_at_station = 1 + + var/area/start_location = locate(/area/shuttle/specops/station) + var/area/end_location = locate(/area/shuttle/specops/centcom) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived at Central Command. Operation has ended!") + + GLOB.specops_shuttle_at_station = 0 + + for(var/obj/machinery/computer/specops_shuttle/S in world) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_process() + var/area/centcom/specops/special_ops = locate()//Where is the specops area located? + var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + announcer.config(list("Response Team" = 0)) + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. + if(announcer) + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + //message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" + //announcer.autosay(message, "A.L.I.C.E.", "Response Team") + + while(GLOB.specops_shuttle_time - world.timeofday > 0) + var/ticksleft = GLOB.specops_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + GLOB.specops_shuttle_time = world.timeofday + 10 // midnight rollover + GLOB.specops_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(GLOB.specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" + if(rounded_time_left==0) + message = "\"ALERT: TAKEOFF\"" + announcer.autosay(message, "A.L.I.C.E.", "Response Team") + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + GLOB.specops_shuttle_moving_to_station = 0 + GLOB.specops_shuttle_moving_to_centcom = 0 + + GLOB.specops_shuttle_at_station = 1 + if(GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) return + + if(!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/poddoor/M in special_ops) + switch(M.id_tag) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in world) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L.loc) + for(var/obj/effect/landmark/L in world) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder)) + //P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in special_ops) + switch(M.id_tag) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/poddoor/M in special_ops) + switch(M.id_tag)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + special_ops.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. + + var/area/start_location = locate(/area/shuttle/specops/centcom) + var/area/end_location = locate(/area/shuttle/specops/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs += T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived to [station_name()]. Commence operation!") + + for(var/obj/machinery/computer/specops_shuttle/S in world) + S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY + + qdel(announcer) + +/proc/specops_can_move() + if(GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) + return 0 + for(var/obj/machinery/computer/specops_shuttle/S in world) + if(world.timeofday <= S.specops_shuttle_timereset) + return 0 + return 1 + +/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) + to_chat(user, "Access Denied.") + return 1 + +/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob, params) + if(istype(I,/obj/item/card/emag)) + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") + else + return ..() + +/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + +//Commented out so admins can do shenanigans at their leisure. Also makes the force-spawned admin ERTs able to use the shuttle. +// if(sent_strike_team == 0 && send_emergency_team == 0) +// to_chat(usr, "The strike team has not yet deployed.") +// return + + if(..()) + return + + user.machine = src + var/dat + if(temp) + dat = temp + else + dat += {"
    Special Operations Shuttle
    + \nLocation: [GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([GLOB.specops_shuttle_timeleft] seconds.)":GLOB.specops_shuttle_at_station ? "Station":"Dock"]
    + [GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
    \n
    ":GLOB.specops_shuttle_at_station ? "\nShuttle standing by...
    \n
    ":"\nDepart to [station_name()]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/specops_shuttle/Topic(href, href_list) + if(..()) + return 1 + + if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.machine = src + + if(href_list["sendtodock"]) + if(!GLOB.specops_shuttle_at_station|| GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) return + + if(!specops_can_move()) + to_chat(usr, "Central Command will not allow the Special Operations shuttle to return yet.") + if(world.timeofday <= specops_shuttle_timereset) + if(((world.timeofday - specops_shuttle_timereset)/10) > 60) + to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") + to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") + return + + to_chat(usr, "The Special Operations shuttle will arrive at Central Command in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + GLOB.specops_shuttle_moving_to_centcom = 1 + GLOB.specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_return() + + else if(href_list["sendtostation"]) + if(GLOB.specops_shuttle_at_station || GLOB.specops_shuttle_moving_to_station || GLOB.specops_shuttle_moving_to_centcom) return + + if(!specops_can_move()) + to_chat(usr, "The Special Operations shuttle is unable to leave.") + return + + to_chat(usr, "The Special Operations shuttle will arrive on [station_name()] in [(SPECOPS_MOVETIME/10)] seconds.") + + temp += "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/centcom/specops/special_ops = locate() + if(special_ops) + special_ops.readyalert()//Trigger alarm for the spec ops area. + GLOB.specops_shuttle_moving_to_station = 1 + + GLOB.specops_shuttle_time = world.timeofday + SPECOPS_MOVETIME + spawn(0) + specops_process() + + else if(href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/computer/station_alert.dm b/code/game/machinery/computer/station_alert.dm index 0170d32b3138..fb630d7a2519 100644 --- a/code/game/machinery/computer/station_alert.dm +++ b/code/game/machinery/computer/station_alert.dm @@ -1,53 +1,53 @@ - -/obj/machinery/computer/station_alert - name = "station alert console" - desc = "Used to access the station's automated alert system." - icon_keyboard = "tech_key" - icon_screen = "alert:0" - light_color = LIGHT_COLOR_CYAN - circuit = /obj/item/circuitboard/stationalert_engineering - var/datum/nano_module/alarm_monitor/alarm_monitor - var/monitor_type = /datum/nano_module/alarm_monitor/engineering - -/obj/machinery/computer/station_alert/security - monitor_type = /datum/nano_module/alarm_monitor/security - circuit = /obj/item/circuitboard/stationalert_security - -/obj/machinery/computer/station_alert/all - monitor_type = /datum/nano_module/alarm_monitor/all - circuit = /obj/item/circuitboard/stationalert_all - -/obj/machinery/computer/station_alert/New() - ..() - alarm_monitor = new monitor_type(src) - alarm_monitor.register(src, /obj/machinery/computer/station_alert/update_icon) - -/obj/machinery/computer/station_alert/Destroy() - alarm_monitor.unregister(src) - QDEL_NULL(alarm_monitor) - return ..() - -/obj/machinery/computer/station_alert/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - interact(user) - -/obj/machinery/computer/station_alert/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - interact(user) - -/obj/machinery/computer/station_alert/interact(mob/user) - alarm_monitor.ui_interact(user) - -/obj/machinery/computer/station_alert/update_icon() - if(alarm_monitor) - var/list/alarms = alarm_monitor.major_alarms() - if(alarms.len) - icon_screen = "alert:2" - else - icon_screen = "alert:0" - - ..() \ No newline at end of file + +/obj/machinery/computer/station_alert + name = "station alert console" + desc = "Used to access the station's automated alert system." + icon_keyboard = "tech_key" + icon_screen = "alert:0" + light_color = LIGHT_COLOR_CYAN + circuit = /obj/item/circuitboard/stationalert_engineering + var/datum/nano_module/alarm_monitor/alarm_monitor + var/monitor_type = /datum/nano_module/alarm_monitor/engineering + +/obj/machinery/computer/station_alert/security + monitor_type = /datum/nano_module/alarm_monitor/security + circuit = /obj/item/circuitboard/stationalert_security + +/obj/machinery/computer/station_alert/all + monitor_type = /datum/nano_module/alarm_monitor/all + circuit = /obj/item/circuitboard/stationalert_all + +/obj/machinery/computer/station_alert/New() + ..() + alarm_monitor = new monitor_type(src) + alarm_monitor.register(src, /obj/machinery/computer/station_alert/update_icon) + +/obj/machinery/computer/station_alert/Destroy() + alarm_monitor.unregister(src) + QDEL_NULL(alarm_monitor) + return ..() + +/obj/machinery/computer/station_alert/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + +/obj/machinery/computer/station_alert/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + interact(user) + +/obj/machinery/computer/station_alert/interact(mob/user) + alarm_monitor.ui_interact(user) + +/obj/machinery/computer/station_alert/update_icon() + if(alarm_monitor) + var/list/alarms = alarm_monitor.major_alarms() + if(alarms.len) + icon_screen = "alert:2" + else + icon_screen = "alert:0" + + ..() diff --git a/code/game/machinery/computer/store.dm b/code/game/machinery/computer/store.dm index f49d91c53b35..856e2322f713 100644 --- a/code/game/machinery/computer/store.dm +++ b/code/game/machinery/computer/store.dm @@ -102,11 +102,11 @@ th.cost.toomuch {background:maroon;} "} - for(var/datum/storeitem/item in centcomm_store.items) + for(var/datum/storeitem/item in GLOB.centcomm_store.items) var/cost_class="affordable" if(item.cost>balance) cost_class="toomuch" - var/itemID=centcomm_store.items.Find(item) + var/itemID=GLOB.centcomm_store.items.Find(item) var/row_color="light" if(itemID%2 == 0) row_color="dark" @@ -143,7 +143,7 @@ th.cost.toomuch {background:maroon;} if(href_list["buy"]) var/itemID = text2num(href_list["buy"]) - var/datum/storeitem/item = centcomm_store.items[itemID] + var/datum/storeitem/item = GLOB.centcomm_store.items[itemID] var/sure = alert(usr,"Are you sure you wish to purchase [item.name] for $[item.cost]?","You sure?","Yes","No") in list("Yes","No") if(!Adjacent(usr)) to_chat(usr, "You are not close enough to do that.") @@ -151,7 +151,7 @@ th.cost.toomuch {background:maroon;} if(sure=="No") updateUsrDialog() return - if(!centcomm_store.PlaceOrder(usr,itemID)) + if(!GLOB.centcomm_store.PlaceOrder(usr,itemID)) to_chat(usr, "Unable to charge your account.") else to_chat(usr, "You've successfully purchased the item. It should be in your hands or on the floor.") diff --git a/code/game/machinery/computer/syndicate_specops_shuttle.dm b/code/game/machinery/computer/syndicate_specops_shuttle.dm index 71bfe9f43abc..0a65b6be408a 100644 --- a/code/game/machinery/computer/syndicate_specops_shuttle.dm +++ b/code/game/machinery/computer/syndicate_specops_shuttle.dm @@ -1,256 +1,256 @@ -//Config stuff -#define SYNDICATE_ELITE_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SYNDICATE_ELITE_STATION_AREATYPE "/area/shuttle/syndicate_elite/station" //Type of the spec ops shuttle area for station -#define SYNDICATE_ELITE_DOCK_AREATYPE "/area/shuttle/syndicate_elite/mothership" //Type of the spec ops shuttle area for dock - -var/syndicate_elite_shuttle_moving_to_station = 0 -var/syndicate_elite_shuttle_moving_to_mothership = 0 -var/syndicate_elite_shuttle_at_station = 0 -var/syndicate_elite_shuttle_can_send = 1 -var/syndicate_elite_shuttle_time = 0 -var/syndicate_elite_shuttle_timeleft = 0 - -/obj/machinery/computer/syndicate_elite_shuttle - name = "\improper Elite Syndicate Squad shuttle console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "syndie_key" - icon_screen = "syndishuttle" - light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(ACCESS_SYNDICATE) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - -/proc/syndicate_elite_process() - var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work. - var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located? - var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "THE SYNDICATE ELITE SHUTTLE IS PREPARING FOR LAUNCH"//Initial message shown. - if(announcer) - announcer.say(message) - // message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" - // announcer.say(message) - - while(syndicate_elite_shuttle_time - world.timeofday > 0) - var/ticksleft = syndicate_elite_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - syndicate_elite_shuttle_time = world.timeofday // midnight rollover - syndicate_elite_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(syndicate_elite_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN" - if(rounded_time_left==0) - message = "ALERT: TAKEOFF" - announcer.say(message) - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - syndicate_elite_shuttle_moving_to_station = 0 - syndicate_elite_shuttle_moving_to_mothership = 0 - - syndicate_elite_shuttle_at_station = 1 - if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - if(!syndicate_elite_can_move()) - to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") - return - - sleep(600) -/* - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/poddoor/M in elite_squad) - switch(M.id) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L) - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc) - P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - P.target = pick(spawn_marauder)//Where the marauder will arrive. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in elite_squad) - switch(M.id) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/poddoor/M in elite_squad) - switch(M.id)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - */ - elite_squad.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Syndicate Breach Area") - explosion(L.loc,4,6,8,10,0) - - sleep(40) - - - var/area/start_location = locate(/area/shuttle/syndicate_elite/mothership) - var/area/end_location = locate(/area/shuttle/syndicate_elite/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs = T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - -/proc/syndicate_elite_can_move() - if(syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return 0 - else return 1 - -/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob) - to_chat(user, "Access Denied.") - return 1 - -/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob, params) - if(istype(I,/obj/item/card/emag)) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - else - return ..() - -/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - -// if(sent_syndicate_strike_team == 0) -// to_chat(usr, "The strike team has not yet deployed.") -// return - - if(..()) - return - - user.set_machine(src) - var/dat - if(temp) - dat = temp - else - dat = {"
    Special Operations Shuttle
    - \nLocation: [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "Departing for [station_name()] in ([syndicate_elite_shuttle_timeleft] seconds.)":syndicate_elite_shuttle_at_station ? "Station":"Dock"]
    - [syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.*
    \n
    ":syndicate_elite_shuttle_at_station ? "\nShuttle Offline
    \n
    ":"\nDepart to [station_name()]
    \n
    "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/syndicate_elite_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if(href_list["sendtodock"]) - if(!syndicate_elite_shuttle_at_station|| syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") - return - - else if(href_list["sendtostation"]) - if(syndicate_elite_shuttle_at_station || syndicate_elite_shuttle_moving_to_station || syndicate_elite_shuttle_moving_to_mothership) return - - if(!specops_can_move()) - to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") - return - - to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") - - temp = "Shuttle departing.

    OK" - updateUsrDialog() - - var/area/syndicate_mothership/elite_squad/elite_squad = locate() - if(elite_squad) - elite_squad.readyalert()//Trigger alarm for the spec ops area. - syndicate_elite_shuttle_moving_to_station = 1 - - syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME - spawn(0) - syndicate_elite_process() - - - else if(href_list["mainmenu"]) - temp = null - - add_fingerprint(usr) - updateUsrDialog() - return +//Config stuff +#define SYNDICATE_ELITE_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. +#define SYNDICATE_ELITE_STATION_AREATYPE "/area/shuttle/syndicate_elite/station" //Type of the spec ops shuttle area for station +#define SYNDICATE_ELITE_DOCK_AREATYPE "/area/shuttle/syndicate_elite/mothership" //Type of the spec ops shuttle area for dock + +GLOBAL_VAR_INIT(syndicate_elite_shuttle_moving_to_station, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_moving_to_mothership, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_at_station, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_can_send, 1) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_time, 0) +GLOBAL_VAR_INIT(syndicate_elite_shuttle_timeleft, 0) + +/obj/machinery/computer/syndicate_elite_shuttle + name = "\improper Elite Syndicate Squad shuttle console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "syndie_key" + icon_screen = "syndishuttle" + light_color = LIGHT_COLOR_PURE_CYAN + req_access = list(ACCESS_SYNDICATE) + var/temp = null + var/hacked = 0 + var/allowedtocall = 0 + +/proc/syndicate_elite_process() + var/area/syndicate_mothership/control/syndicate_ship = locate()//To find announcer. This area should exist for this proc to work. + var/area/syndicate_mothership/elite_squad/elite_squad = locate()//Where is the specops area located? + var/mob/living/silicon/decoy/announcer = locate() in syndicate_ship//We need a fake AI to announce some stuff below. Otherwise it will be wonky. + + var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. + var/message = "THE SYNDICATE ELITE SHUTTLE IS PREPARING FOR LAUNCH"//Initial message shown. + if(announcer) + announcer.say(message) + // message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" + // announcer.say(message) + + while(GLOB.syndicate_elite_shuttle_time - world.timeofday > 0) + var/ticksleft = GLOB.syndicate_elite_shuttle_time - world.timeofday + + if(ticksleft > 1e5) + GLOB.syndicate_elite_shuttle_time = world.timeofday // midnight rollover + GLOB.syndicate_elite_shuttle_timeleft = (ticksleft / 10) + + //All this does is announce the time before launch. + if(announcer) + var/rounded_time_left = round(GLOB.syndicate_elite_shuttle_timeleft)//Round time so that it will report only once, not in fractions. + if(rounded_time_left in message_tracker)//If that time is in the list for message announce. + message = "ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN" + if(rounded_time_left==0) + message = "ALERT: TAKEOFF" + announcer.say(message) + message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. + //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. + + sleep(5) + + GLOB.syndicate_elite_shuttle_moving_to_station = 0 + GLOB.syndicate_elite_shuttle_moving_to_mothership = 0 + + GLOB.syndicate_elite_shuttle_at_station = 1 + if(GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return + + if(!syndicate_elite_can_move()) + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") + return + + sleep(600) +/* + //Begin Marauder launchpad. + spawn(0)//So it parallel processes it. + for(var/obj/machinery/door/poddoor/M in elite_squad) + switch(M.id) + if("ASSAULT0") + spawn(10)//1 second delay between each. + M.open() + if("ASSAULT1") + spawn(20) + M.open() + if("ASSAULT2") + spawn(30) + M.open() + if("ASSAULT3") + spawn(40) + M.open() + + sleep(10) + + var/spawn_marauder[] = new() + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Marauder Entry") + spawn_marauder.Add(L) + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Marauder Exit") + var/obj/effect/portal/P = new(L.loc) + P.invisibility = 101//So it is not seen by anyone. + P.failchance = 0//So it has no fail chance when teleporting. + P.target = pick(spawn_marauder)//Where the marauder will arrive. + spawn_marauder.Remove(P.target) + + sleep(10) + + for(var/obj/machinery/mass_driver/M in elite_squad) + switch(M.id) + if("ASSAULT0") + spawn(10) + M.drive() + if("ASSAULT1") + spawn(20) + M.drive() + if("ASSAULT2") + spawn(30) + M.drive() + if("ASSAULT3") + spawn(40) + M.drive() + + sleep(50)//Doors remain open for 5 seconds. + + for(var/obj/machinery/door/poddoor/M in elite_squad) + switch(M.id)//Doors close at the same time. + if("ASSAULT0") + spawn(0) + M.close() + if("ASSAULT1") + spawn(0) + M.close() + if("ASSAULT2") + spawn(0) + M.close() + if("ASSAULT3") + spawn(0) + M.close() + */ + elite_squad.readyreset()//Reset firealarm after the team launched. + //End Marauder launchpad. + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Syndicate Breach Area") + explosion(L.loc,4,6,8,10,0) + + sleep(40) + + + var/area/start_location = locate(/area/shuttle/syndicate_elite/mothership) + var/area/end_location = locate(/area/shuttle/syndicate_elite/station) + + var/list/dstturfs = list() + var/throwy = world.maxy + + for(var/turf/T in end_location) + dstturfs = T + if(T.y < throwy) + throwy = T.y + + // hey you, get out of the way! + for(var/turf/T in dstturfs) + // find the turf to move things to + var/turf/D = locate(T.x, throwy - 1, 1) + //var/turf/E = get_step(D, SOUTH) + for(var/atom/movable/AM as mob|obj in T) + AM.Move(D) + if(istype(T, /turf/simulated)) + qdel(T) + + for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... + bug.gib() + + for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... + pest.gib() + + start_location.move_contents_to(end_location) + + for(var/turf/T in get_area_turfs(end_location) ) + var/mob/M = locate(/mob) in T + to_chat(M, "You have arrived to [station_name()]. Commence operation!") + +/proc/syndicate_elite_can_move() + if(GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return 0 + else return 1 + +/obj/machinery/computer/syndicate_elite_shuttle/attack_ai(var/mob/user as mob) + to_chat(user, "Access Denied.") + return 1 + +/obj/machinery/computer/syndicate_elite_shuttle/attackby(I as obj, user as mob, params) + if(istype(I,/obj/item/card/emag)) + to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") + else + return ..() + +/obj/machinery/computer/syndicate_elite_shuttle/attack_hand(var/mob/user as mob) + if(!allowed(user)) + to_chat(user, "Access Denied.") + return + +// if(sent_syndicate_strike_team == 0) +// to_chat(usr, "The strike team has not yet deployed.") +// return + + if(..()) + return + + user.set_machine(src) + var/dat + if(temp) + dat = temp + else + dat = {"
    Special Operations Shuttle
    + \nLocation: [GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership ? "Departing for [station_name()] in ([GLOB.syndicate_elite_shuttle_timeleft] seconds.)":GLOB.syndicate_elite_shuttle_at_station ? "Station":"Dock"]
    + [GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership ? "\n*The Syndicate Elite shuttle is already leaving.*
    \n
    ":GLOB.syndicate_elite_shuttle_at_station ? "\nShuttle Offline
    \n
    ":"\nDepart to [station_name()]
    \n
    "] + \nClose"} + + user << browse(dat, "window=computer;size=575x450") + onclose(user, "computer") + return + +/obj/machinery/computer/syndicate_elite_shuttle/Topic(href, href_list) + if(..()) + return 1 + + if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["sendtodock"]) + if(!GLOB.syndicate_elite_shuttle_at_station|| GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return + + to_chat(usr, "The Syndicate will not allow the Elite Squad shuttle to return.") + return + + else if(href_list["sendtostation"]) + if(GLOB.syndicate_elite_shuttle_at_station || GLOB.syndicate_elite_shuttle_moving_to_station || GLOB.syndicate_elite_shuttle_moving_to_mothership) return + + if(!specops_can_move()) + to_chat(usr, "The Syndicate Elite shuttle is unable to leave.") + return + + to_chat(usr, "The Syndicate Elite shuttle will arrive on [station_name()] in [(SYNDICATE_ELITE_MOVETIME/10)] seconds.") + + temp = "Shuttle departing.

    OK" + updateUsrDialog() + + var/area/syndicate_mothership/elite_squad/elite_squad = locate() + if(elite_squad) + elite_squad.readyalert()//Trigger alarm for the spec ops area. + GLOB.syndicate_elite_shuttle_moving_to_station = 1 + + GLOB.syndicate_elite_shuttle_time = world.timeofday + SYNDICATE_ELITE_MOVETIME + spawn(0) + syndicate_elite_process() + + + else if(href_list["mainmenu"]) + temp = null + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index b051ae34ca9a..efee10767ba6 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -1,1091 +1,1091 @@ -/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. - name = "machine frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "box_0" - density = 1 - anchored = 1 - use_power = NO_POWER_USE - max_integrity = 250 - var/obj/item/circuitboard/circuit = null - var/list/components = null - var/list/req_components = null - var/list/req_component_names = null // user-friendly names of components - var/state = 1 - - // For pods - var/list/connected_parts = list() - var/pattern_idx=0 - -/obj/machinery/constructable_frame/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, 5) - if(state >= 2) - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(loc) - A.amount = 5 - if(circuit) - circuit.forceMove(loc) - circuit = null - return ..() - -/obj/machinery/constructable_frame/obj_break(damage_flag) - deconstruct() - -// unfortunately, we have to instance the objects really quickly to get the names -// fortunately, this is only called once when the board is added and the items are immediately GC'd -// and none of the parts do much in their constructors -/obj/machinery/constructable_frame/proc/update_namelist() - if(!req_components) - return - - req_component_names = new() - for(var/tname in req_components) - var/path = tname - var/obj/O = new path() - req_component_names[tname] = O.name - -/obj/machinery/constructable_frame/proc/get_req_components_amt() - var/amt = 0 - for(var/path in req_components) - amt += req_components[path] - return amt - -// update description of required components remaining -/obj/machinery/constructable_frame/proc/update_req_desc() - if(!req_components || !req_component_names) - return - - var/hasContent = 0 - desc = "Requires" - for(var/i = 1 to req_components.len) - var/tname = req_components[i] - var/amt = req_components[tname] - if(amt == 0) - continue - var/use_and = i == req_components.len - desc += "[(hasContent ? (use_and ? ", and" : ",") : "")] [amt] [amt == 1 ? req_component_names[tname] : "[req_component_names[tname]]\s"]" - hasContent = 1 - - if(!hasContent) - desc = "Does not require any more components." - else - desc += "." - -/obj/machinery/constructable_frame/machine_frame/attackby(obj/item/P, mob/user, params) - switch(state) - if(1) - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(src.loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 1 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 2 - icon_state = "box_1" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - - if(istype(P, /obj/item/wrench)) - playsound(src.loc, P.usesound, 75, 1) - to_chat(user, "You dismantle the frame.") - deconstruct(TRUE) - return - if(2) - if(istype(P, /obj/item/circuitboard)) - var/obj/item/circuitboard/B = P - if(B.board_type == "machine") - playsound(src.loc, B.usesound, 50, 1) - to_chat(user, "You add the circuit board to the frame.") - circuit = P - user.drop_item() - P.loc = src - icon_state = "box_2" - state = 3 - components = list() - req_components = circuit.req_components.Copy() - update_namelist() - update_req_desc() - else - to_chat(user, "This frame does not accept circuit boards of this type!") - return - if(istype(P, /obj/item/wirecutters)) - playsound(src.loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 1 - icon_state = "box_0" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(src.loc,5) - A.amount = 5 - return - if(3) - if(istype(P, /obj/item/crowbar)) - playsound(src.loc, P.usesound, 50, 1) - state = 2 - circuit.loc = src.loc - circuit = null - if(components.len == 0) - to_chat(user, "You remove the circuit board.") - else - to_chat(user, "You remove the circuit board and other components.") - for(var/obj/item/I in components) - I.loc = src.loc - desc = initial(desc) - req_components = null - components = null - icon_state = "box_1" - return - - if(istype(P, /obj/item/screwdriver)) - var/component_check = 1 - for(var/R in req_components) - if(req_components[R] > 0) - component_check = 0 - break - if(component_check) - playsound(src.loc, P.usesound, 50, 1) - var/obj/machinery/new_machine = new src.circuit.build_path(src.loc) - new_machine.on_construction() - for(var/obj/O in new_machine.component_parts) - qdel(O) - new_machine.component_parts = list() - for(var/obj/O in src) - O.loc = null - new_machine.component_parts += O - circuit.loc = null - new_machine.RefreshParts() - qdel(src) - return - - if(istype(P, /obj/item/storage/part_replacer) && P.contents.len && get_req_components_amt()) - var/obj/item/storage/part_replacer/replacer = P - var/list/added_components = list() - var/list/part_list = list() - - //Assemble a list of current parts, then sort them by their rating! - for(var/obj/item/stock_parts/co in replacer) - part_list += co - - for(var/path in req_components) - while(req_components[path] > 0 && (locate(path) in part_list)) - var/obj/item/part = (locate(path) in part_list) - added_components[part] = path - replacer.remove_from_storage(part, src) - req_components[path]-- - part_list -= part - - for(var/obj/item/stock_parts/part in added_components) - components += part - to_chat(user, "[part.name] applied.") - replacer.play_rped_sound() - - update_req_desc() - return - - if(istype(P, /obj/item)) - var/success - for(var/I in req_components) - if(istype(P, I) && (req_components[I] > 0) && (!(P.flags & NODROP) || istype(P, /obj/item/stack))) - success=1 - playsound(src.loc, P.usesound, 50, 1) - if(istype(P, /obj/item/stack)) - var/obj/item/stack/S = P - var/camt = min(S.amount, req_components[I]) - var/obj/item/stack/NS = new P.type(src) - NS.amount = camt - NS.update_icon() - S.use(camt) - components += NS - req_components[I] -= camt - update_req_desc() - break - user.drop_item() - P.forceMove(src) - components += P - req_components[I]-- - update_req_desc() - return 1 - if(!success) - to_chat(user, "You cannot add that to the machine!") - return 0 - return - if(user.a_intent == INTENT_HARM) - return ..() - - -//Machine Frame Circuit Boards -/*Common Parts: Parts List: Ignitor, Timer, Infra-red laser, Infra-red sensor, t_scanner, Capacitor, Valve, sensor unit, -micro-manipulator, console screen, beaker, Microlaser, matter bin, power cells. -Note: Once everything is added to the public areas, will add MAT_METAL and MAT_GLASS to circuit boards since autolathe won't be able -to destroy them and players will be able to make replacements. -*/ -/obj/item/circuitboard/vendor - name = "circuit board (Booze-O-Mat Vendor)" - board_type = "machine" - origin_tech = "programming=1" - frame_desc = "Requires 1 Resupply Canister." - build_path = /obj/machinery/vending/boozeomat - req_components = list(/obj/item/vending_refill/boozeomat = 1) - - var/static/list/vending_names_paths = list( - /obj/machinery/vending/boozeomat = "Booze-O-Mat", - /obj/machinery/vending/coffee = "Solar's Best Hot Drinks", - /obj/machinery/vending/snack = "Getmore Chocolate Corp", - /obj/machinery/vending/chinese = "Mr. Chang", - /obj/machinery/vending/cola = "Robust Softdrinks", - /obj/machinery/vending/cigarette = "ShadyCigs Deluxe", - /obj/machinery/vending/hatdispenser = "Hatlord 9000", - /obj/machinery/vending/suitdispenser = "Suitlord 9000", - /obj/machinery/vending/shoedispenser = "Shoelord 9000", - /obj/machinery/vending/clothing = "ClothesMate", - /obj/machinery/vending/medical = "NanoMed Plus", - /obj/machinery/vending/wallmed = "NanoMed", - /obj/machinery/vending/assist = "Vendomat", - /obj/machinery/vending/engivend = "Engi-Vend", - /obj/machinery/vending/hydronutrients = "NutriMax", - /obj/machinery/vending/hydroseeds = "MegaSeed Servitor", - /obj/machinery/vending/sustenance = "Sustenance Vendor", - /obj/machinery/vending/dinnerware = "Plasteel Chef's Dinnerware Vendor", - /obj/machinery/vending/cart = "PTech", - /obj/machinery/vending/robotics = "Robotech Deluxe", - /obj/machinery/vending/engineering = "Robco Tool Maker", - /obj/machinery/vending/sovietsoda = "BODA", - /obj/machinery/vending/security = "SecTech", - /obj/machinery/vending/modularpc = "Deluxe Silicate Selections", - /obj/machinery/vending/crittercare = "CritterCare") - -/obj/item/circuitboard/vendor/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/static/list/display_vending_names_paths - if(!display_vending_names_paths) - display_vending_names_paths = list() - for(var/path in vending_names_paths) - display_vending_names_paths[vending_names_paths[path]] = path - var/choice = input(user, "Choose a new brand","Select an Item") as null|anything in display_vending_names_paths - if(loc != user) - to_chat(user, "You need to keep [src] in your hands while doing that!") - return - set_type(display_vending_names_paths[choice]) - -/obj/item/circuitboard/vendor/proc/set_type(obj/machinery/vending/typepath) - build_path = typepath - name = "circuit board ([vending_names_paths[build_path]] Vendor)" - req_components = list(initial(typepath.refill_canister) = 1) - -/obj/item/circuitboard/smes - name = "circuit board (SMES)" - build_path = /obj/machinery/power/smes - board_type = "machine" - origin_tech = "programming=3;powerstorage=3;engineering=3" - frame_desc = "Requires 5 pieces of cable, 5 Power Cells and 1 Capacitor." - req_components = list( - /obj/item/stack/cable_coil = 5, - /obj/item/stock_parts/cell = 5, - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/emitter - name = "circuit board (Emitter)" - build_path = /obj/machinery/power/emitter - board_type = "machine" - origin_tech = "programming=3;powerstorage=4;engineering=4" - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/power_compressor - name = "circuit board (Power Compressor)" - build_path = /obj/machinery/power/compressor - board_type = "machine" - origin_tech = "programming=4;powerstorage=4;engineering=4" - req_components = list( - /obj/item/stack/cable_coil = 5, - /obj/item/stock_parts/manipulator = 6) - -/obj/item/circuitboard/power_turbine - name = "circuit board (Power Turbine)" - build_path = /obj/machinery/power/turbine - board_type = "machine" - origin_tech = "programming=4;powerstorage=4;engineering=4" - req_components = list( - /obj/item/stack/cable_coil = 5, - /obj/item/stock_parts/capacitor = 6) - -/obj/item/circuitboard/thermomachine - name = "circuit board (Freezer)" - desc = "Use screwdriver to switch between heating and cooling modes." - build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer - board_type = "machine" - origin_tech = "programming=3;plasmatech=3" - frame_desc = "Requires 2 Matter Bins, 2 Micro Lasers, 1 piece of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/thermomachine/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - if(build_path == /obj/machinery/atmospherics/unary/cold_sink/freezer) - build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - name = "circuit board (Heater)" - to_chat(user, "You set the board to heating.") - else - build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer - name = "circuit board (Freezer)" - to_chat(user, "You set the board to cooling.") - return - return ..() - -/obj/item/circuitboard/recharger - name = "circuit board (Recharger)" - build_path = /obj/machinery/recharger - board_type = "machine" - origin_tech = "powerstorage=3;materials=2" - frame_desc = "Requires 1 Capacitor" - req_components = list(/obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/snow_machine - name = "circuit board (snow machine)" - build_path = /obj/machinery/snow_machine - board_type = "machine" - origin_tech = "programming=2;materials=2" - frame_desc = "Requires 1 Matter Bin and 1 Micro Laser." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/micro_laser = 1) - -/obj/item/circuitboard/biogenerator - name = "circuit board (Biogenerator)" - build_path = /obj/machinery/biogenerator - board_type = "machine" - origin_tech = "programming=2;biotech=3;materials=3" - frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/plantgenes - name = "Plant DNA Manipulator (Machine Board)" - build_path = /obj/machinery/plantgenes - board_type = "machine" - origin_tech = "programming=3;biotech=3" - frame_desc = "Requires 1 Manipulator, 1 Micro Laser, 1 Console Screen, and 1 Scanning Module." - req_components = list( - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/scanning_module = 1) - -/obj/item/circuitboard/plantgenes/vault - -/obj/item/circuitboard/seed_extractor - name = "circuit board (Seed Extractor)" - build_path = /obj/machinery/seed_extractor - board_type = "machine" - origin_tech = "programming=1" - frame_desc = "Requires 1 Matter Bin and 1 Manipulator." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/hydroponics - name = "circuit board (Hydroponics Tray)" - build_path = /obj/machinery/hydroponics/constructable - board_type = "machine" - origin_tech = "programming=1;biotech=2" - frame_desc = "Requires 2 Matter Bins, 1 Manipulator, and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/microwave - name = "circuit board (Microwave)" - build_path = /obj/machinery/kitchen_machine/microwave - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 1 Micro Laser, 2 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/oven - name = "circuit board (Oven)" - build_path = /obj/machinery/kitchen_machine/oven - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/grill - name = "circuit board (Grill)" - build_path = /obj/machinery/kitchen_machine/grill - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/candy_maker - name = "circuit board (Candy Maker)" - build_path = /obj/machinery/kitchen_machine/candy_maker - board_type = "machine" - origin_tech = "programming=2;magnets=2" - frame_desc = "Requires 1 Manipulator, 5 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/deepfryer - name = "circuit board (Deep Fryer)" - build_path = /obj/machinery/cooker/deepfryer - board_type = "machine" - origin_tech = "programming=1" - frame_desc = "Requires 2 Micro Lasers and 5 pieces of cable." - req_components = list( - /obj/item/stock_parts/micro_laser = 2, - /obj/item/stack/cable_coil = 5) - -/obj/item/circuitboard/gibber - name = "circuit board (Gibber)" - build_path = /obj/machinery/gibber - board_type = "machine" - origin_tech = "programming=2;engineering=2" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/tesla_coil - name = "circuit board (Tesla Coil)" - build_path = /obj/machinery/power/tesla_coil - board_type = "machine" - origin_tech = "programming=3;magnets=3;powerstorage=3" - req_components = list( - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/grounding_rod - name = "circuit board (Grounding Rod)" - build_path = /obj/machinery/power/grounding_rod - board_type = "machine" - origin_tech = "programming=3;powerstorage=3;magnets=3;plasmatech=2" - req_components = list( - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/processor - name = "circuit board (Food processor)" - build_path = /obj/machinery/processor - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/recycler - name = "circuit board (Recycler)" - build_path = /obj/machinery/recycler - board_type = "machine" - origin_tech = "programming=2;engineering=2" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/smartfridge - name = "circuit board (Smartfridge)" - build_path = /obj/machinery/smartfridge - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1) - var/list/fridge_names_paths = list( - "\improper SmartFridge" = /obj/machinery/smartfridge, - "\improper MegaSeed Servitor" = /obj/machinery/smartfridge/seeds, - "\improper Refrigerated Medicine Storage" = /obj/machinery/smartfridge/medbay, - "\improper Slime Extract Storage" = /obj/machinery/smartfridge/secure/extract, - "\improper Secure Refrigerated Medicine Storage" = /obj/machinery/smartfridge/secure/medbay, - "\improper Smart Chemical Storage" = /obj/machinery/smartfridge/secure/chemistry, - "smart virus storage" = /obj/machinery/smartfridge/secure/chemistry/virology, - "\improper Drink Showcase" = /obj/machinery/smartfridge/drinks - ) - - - -/obj/item/circuitboard/smartfridge/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - set_type(null, user) - return - return ..() - -/obj/item/circuitboard/smartfridge/proc/set_type(typepath, mob/user) - var/new_name = "" - if(!typepath) - new_name = input("Circuit Setting", "What would you change the board setting to?") in fridge_names_paths - typepath = fridge_names_paths[new_name] - else - for(var/name in fridge_names_paths) - if(fridge_names_paths[name] == typepath) - new_name = name - break - build_path = typepath - name = new_name - if(findtextEx(new_name, "\improper")) - new_name = replacetext(new_name, "\improper", "") - if(user) - to_chat(user, "You set the board to [new_name].") - -/obj/item/circuitboard/monkey_recycler - name = "circuit board (Monkey Recycler)" - build_path = /obj/machinery/monkey_recycler - board_type = "machine" - origin_tech = "programming=1;biotech=2" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1) - -/obj/item/circuitboard/holopad - name = "circuit board (AI Holopad)" - build_path = /obj/machinery/hologram/holopad - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/chem_dispenser - name = "circuit board (Chem Dispenser)" - build_path = /obj/machinery/chem_dispenser - board_type = "machine" - origin_tech = "materials=4;programming=4;plasmatech=4;biotech=3" - frame_desc = "Requires 2 Matter Bins, 1 Capacitor, 1 Manipulator, 1 Console Screen, and 1 Power Cell." - req_components = list( /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/capacitor = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/cell = 1) - -/obj/item/circuitboard/chem_master - name = "circuit board (ChemMaster 3000)" - build_path = /obj/machinery/chem_master - board_type = "machine" - origin_tech = "materials=3;programming=2;biotech=3" - req_components = list( - /obj/item/reagent_containers/glass/beaker = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/chem_master/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/new_name = "ChemMaster" - var/new_path = /obj/machinery/chem_master - - if(build_path == /obj/machinery/chem_master) - new_name = "CondiMaster" - new_path = /obj/machinery/chem_master/condimaster - - build_path = new_path - name = "circuit board ([new_name] 3000)" - to_chat(user, "You change the circuit board setting to \"[new_name]\".") - -/obj/item/circuitboard/chem_master/condi_master - name = "circuit board (CondiMaster 3000)" - build_path = /obj/machinery/chem_master/condimaster - -/obj/item/circuitboard/chem_heater - name = "circuit board (Chemical Heater)" - build_path = /obj/machinery/chem_heater - board_type = "machine" - origin_tech = "programming=2;engineering=2;biotech=2" - frame_desc = "Requires 1 Micro Laser and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/reagentgrinder - name = "circuit board (All-In-One Grinder)" - build_path = /obj/machinery/reagentgrinder/empty - board_type = "machine" - origin_tech = "materials=2;engineering=2;biotech=2" - frame_desc = "Requires 2 Manipulators and 1 Matter Bin." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/matter_bin = 1) - -//Almost the same recipe as destructive analyzer to give people choices. -/obj/item/circuitboard/experimentor - name = "circuit board (E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/r_n_d/experimentor - board_type = "machine" - origin_tech = "magnets=1;engineering=1;programming=1;biotech=1;bluespace=2" - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/micro_laser = 2) - -/obj/item/circuitboard/destructive_analyzer - name = "Circuit board (Destructive Analyzer)" - build_path = /obj/machinery/r_n_d/destructive_analyzer - board_type = "machine" - origin_tech = "magnets=2;engineering=2;programming=2" - frame_desc = "Requires 1 Scanning Module, 1 Manipulator, and 1 Micro-Laser." - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1) - -/obj/item/circuitboard/autolathe - name = "Circuit board (Autolathe)" - build_path = /obj/machinery/autolathe - board_type = "machine" - origin_tech = "engineering=2;programming=2" - frame_desc = "Requires 3 Matter Bins, 1 Manipulator, and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 3, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/protolathe - name = "Circuit board (Protolathe)" - build_path = /obj/machinery/r_n_d/protolathe - board_type = "machine" - origin_tech = "engineering=2;programming=2" - frame_desc = "Requires 2 Matter Bins, 2 Manipulators, and 2 Beakers." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 2, - /obj/item/reagent_containers/glass/beaker = 2) - -/obj/item/circuitboard/chem_dispenser/soda - name = "Circuit board (Soda Machine)" - build_path = /obj/machinery/chem_dispenser/soda - -/obj/item/circuitboard/chem_dispenser/beer - name = "Circuit board (Beer Machine)" - build_path = /obj/machinery/chem_dispenser/beer - -/obj/item/circuitboard/circuit_imprinter - name = "Circuit board (Circuit Imprinter)" - build_path = /obj/machinery/r_n_d/circuit_imprinter - board_type = "machine" - origin_tech = "engineering=2;programming=2" - frame_desc = "Requires 1 Matter Bin, 1 Manipulator, and 2 Beakers." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/reagent_containers/glass/beaker = 2) - -/obj/item/circuitboard/pacman - name = "Circuit Board (PACMAN-type Generator)" - build_path = /obj/machinery/power/port_gen/pacman - board_type = "machine" - origin_tech = "programming=2;powerstorage=3;plasmatech=3;engineering=3" - frame_desc = "Requires 1 Matter Bin, 1 Micro-Laser, 2 Pieces of Cable, and 1 Capacitor." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/capacitor = 1) - -/obj/item/circuitboard/pacman/super - name = "Circuit Board (SUPERPACMAN-type Generator)" - build_path = /obj/machinery/power/port_gen/pacman/super - origin_tech = "programming=3;powerstorage=4;engineering=4" - -/obj/item/circuitboard/pacman/mrs - name = "Circuit Board (MRSPACMAN-type Generator)" - build_path = /obj/machinery/power/port_gen/pacman/mrs - origin_tech = "programming=3;powerstorage=4;engineering=4;plasmatech=4" - -/obj/item/circuitboard/rdserver - name = "Circuit Board (R&D Server)" - build_path = /obj/machinery/r_n_d/server - board_type = "machine" - origin_tech = "programming=3" - frame_desc = "Requires 2 pieces of cable, and 1 Scanning Module." - req_components = list( - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/scanning_module = 1) - -/obj/item/circuitboard/mechfab - name = "Circuit board (Exosuit Fabricator)" - build_path = /obj/machinery/mecha_part_fabricator - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Matter Bins, 1 Manipulator, 1 Micro-Laser and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/podfab - name = "Circuit board (Spacepod Fabricator)" - build_path = /obj/machinery/mecha_part_fabricator/spacepod - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Matter Bins, 1 Manipulators, 1 Micro-Lasers, and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/matter_bin = 2, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1) - - -/obj/item/circuitboard/clonepod - name = "Circuit board (Clone Pod)" - build_path = /obj/machinery/clonepod - board_type = "machine" - origin_tech = "programming=2;biotech=2" - frame_desc = "Requires 2 Manipulator, 2 Scanning Module, 2 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/scanning_module = 2, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/clonescanner - name = "Circuit board (Cloning Scanner)" - build_path = /obj/machinery/dna_scannernew - board_type = "machine" - origin_tech = "programming=2;biotech=2" - frame_desc = "Requires 1 Scanning Module, 1 Manipulator, 1 Micro-Laser, 2 pieces of cable and 1 Console Screen." - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stack/cable_coil = 2,) - -/obj/item/circuitboard/mech_recharger - name = "circuit board (Mech Bay Recharger)" - build_path = /obj/machinery/mech_bay_recharge_port - board_type = "machine" - origin_tech = "programming=3;powerstorage=3;engineering=3" - frame_desc = "Requires 1 piece of cable and 5 Capacitors." - req_components = list( - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/capacitor = 5) - -/obj/item/circuitboard/teleporter_hub - name = "circuit board (Teleporter Hub)" - build_path = /obj/machinery/teleport/hub - board_type = "machine" - origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" - frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 3, - /obj/item/stock_parts/matter_bin = 1) - -/obj/item/circuitboard/teleporter_station - name = "circuit board (Teleporter Station)" - build_path = /obj/machinery/teleport/station - board_type = "machine" - origin_tech = "programming=4;engineering=4;bluespace=4;plasmatech=3" - frame_desc = "Requires 2 Bluespace Crystals, 2 Capacitors and 1 Console Screen." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 2, - /obj/item/stock_parts/capacitor = 2, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/teleporter_perma - name = "circuit board (Permanent Teleporter)" - build_path = /obj/machinery/teleport/perma - board_type = "machine" - origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" - frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 3, - /obj/item/stock_parts/matter_bin = 1) - var/target - -/obj/item/circuitboard/teleporter_perma/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/gps)) - var/obj/item/gps/L = I - if(L.locked_location) - target = get_turf(L.locked_location) - to_chat(user, "You upload the data from [L]") - return - return ..() - -/obj/item/circuitboard/telesci_pad - name = "Circuit board (Telepad)" - build_path = /obj/machinery/telepad - board_type = "machine" - origin_tech = "programming=4;engineering=3;plasmatech=4;bluespace=4" - frame_desc = "Requires 2 Bluespace Crystals, 1 Capacitor, 1 piece of cable and 1 Console Screen." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 2, - /obj/item/stock_parts/capacitor = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/quantumpad - name = "circuit board (Quantum Pad)" - build_path = /obj/machinery/quantumpad - board_type = "machine" - origin_tech = "programming=3;engineering=3;plasmatech=3;bluespace=4" - frame_desc = "Requires 1 Bluespace Crystal, 1 Capacitor, 1 piece of cable and 1 Manipulator." - req_components = list( - /obj/item/stack/ore/bluespace_crystal = 1, - /obj/item/stock_parts/capacitor = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 1) - -/obj/item/circuitboard/sleeper - name = "circuit board (Sleeper)" - build_path = /obj/machinery/sleeper - board_type = "machine" - origin_tech = "programming=3;biotech=2;engineering=3" - frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 2 Console Screens." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 2) - -/obj/item/circuitboard/sleeper/syndicate - name = "circuit board (Sleeper Syndicate)" - build_path = /obj/machinery/sleeper/syndie - -/obj/item/circuitboard/sleeper/survival - name = "circuit board (Sleeper Survival Pod)" - build_path = /obj/machinery/sleeper/survival_pod - - -/obj/item/circuitboard/bodyscanner - name = "circuit board (Body Scanner)" - build_path = /obj/machinery/bodyscanner - board_type = "machine" - origin_tech = "programming=3;biotech=2;engineering=3" - frame_desc = "Requires 1 Scanning Module, 2 pieces of cable and 2 Console Screens." - req_components = list( - /obj/item/stock_parts/scanning_module = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stack/sheet/glass = 2) - -/obj/item/circuitboard/cryo_tube - name = "circuit board (Cryotube)" - build_path = /obj/machinery/atmospherics/unary/cryo_cell - board_type = "machine" - origin_tech = "programming=4;biotech=3;engineering=4;plasmatech=3" - frame_desc = "Requires 1 Matter Bin, 1 piece of cable and 4 Console Screens." - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stack/cable_coil = 1, - /obj/item/stack/sheet/glass = 4) - -/obj/item/circuitboard/cyborgrecharger - name = "circuit board (Cyborg Recharger)" - build_path = /obj/machinery/recharge_station - board_type = "machine" - origin_tech = "powerstorage=3;engineering=3" - frame_desc = "Requires 2 Capacitors, 1 Power Cell and 1 Manipulator." - req_components = list( - /obj/item/stock_parts/capacitor = 2, - /obj/item/stock_parts/cell = 1, - /obj/item/stock_parts/manipulator = 1) - -// Telecomms circuit boards: -/obj/item/circuitboard/telecomms/receiver - name = "Circuit Board (Subspace Receiver)" - build_path = /obj/machinery/telecomms/receiver - board_type = "machine" - origin_tech = "programming=2;engineering=2;bluespace=1" - frame_desc = "Requires 1 Subspace Ansible, 1 Hyperwave Filter, 2 Manipulators, and 1 Micro-Laser." - req_components = list( - /obj/item/stock_parts/subspace/ansible = 1, - /obj/item/stock_parts/subspace/filter = 1, - /obj/item/stock_parts/manipulator = 2, - /obj/item/stock_parts/micro_laser = 1) - -/obj/item/circuitboard/telecomms/hub - name = "Circuit Board (Hub Mainframe)" - build_path = /obj/machinery/telecomms/hub - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filter." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/subspace/filter = 2) - -/obj/item/circuitboard/telecomms/relay - name = "Circuit Board (Relay Mainframe)" - build_path = /obj/machinery/telecomms/relay - board_type = "machine" - origin_tech = "programming=2;engineering=2;bluespace=2" - frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filters." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/subspace/filter = 2) - -/obj/item/circuitboard/telecomms/bus - name = "Circuit Board (Bus Mainframe)" - build_path = /obj/machinery/telecomms/bus - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/subspace/filter = 1) - -/obj/item/circuitboard/telecomms/processor - name = "Circuit Board (Processor Unit)" - build_path = /obj/machinery/telecomms/processor - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 3 Manipulators, 1 Hyperwave Filter, 2 Treatment Disks, 1 Wavelength Analyzer, 2 Cable Coils and 1 Subspace Amplifier." - req_components = list( - /obj/item/stock_parts/manipulator = 3, - /obj/item/stock_parts/subspace/filter = 1, - /obj/item/stock_parts/subspace/treatment = 2, - /obj/item/stock_parts/subspace/analyzer = 1, - /obj/item/stack/cable_coil = 2, - /obj/item/stock_parts/subspace/amplifier = 1) - -/obj/item/circuitboard/telecomms/server - name = "Circuit Board (Telecommunication Server)" - build_path = /obj/machinery/telecomms/server - board_type = "machine" - origin_tech = "programming=2;engineering=2" - frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/subspace/filter = 1) - -/obj/item/circuitboard/telecomms/broadcaster - name = "Circuit Board (Subspace Broadcaster)" - build_path = /obj/machinery/telecomms/broadcaster - board_type = "machine" - origin_tech = "programming=2;engineering=2;bluespace=1" - frame_desc = "Requires 2 Manipulators, 1 Cable Coil, 1 Hyperwave Filter, 1 Ansible Crystal and 2 High-Powered Micro-Lasers. " - req_components = list( - /obj/item/stock_parts/manipulator = 2, - /obj/item/stack/cable_coil = 1, - /obj/item/stock_parts/subspace/filter = 1, - /obj/item/stock_parts/subspace/crystal = 1, - /obj/item/stock_parts/micro_laser/high = 2) - -/obj/item/circuitboard/ore_redemption - name = "circuit board (Ore Redemption)" - build_path = /obj/machinery/mineral/ore_redemption - board_type = "machine" - origin_tech = "programming=1;engineering=2" - req_components = list( - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/assembly/igniter = 1) - -/obj/item/circuitboard/ore_redemption/golem - name = "circuit board (Golem Ore Redemption)" - build_path = /obj/machinery/mineral/ore_redemption/golem - -/obj/item/circuitboard/mining_equipment_vendor - name = "circuit board (Mining Equipment Vendor)" - build_path = /obj/machinery/mineral/equipment_vendor - board_type = "machine" - origin_tech = "programming=1;engineering=3" - req_components = list( - /obj/item/stack/sheet/glass = 1, - /obj/item/stock_parts/matter_bin = 3) - -/obj/item/circuitboard/mining_equipment_vendor/golem - name = "circuit board (Mining Equipment Vendor)" - build_path = /obj/machinery/mineral/equipment_vendor/golem - -/obj/item/circuitboard/clawgame - name = "circuit board (Claw Game)" - build_path = /obj/machinery/arcade/claw - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/cable_coil = 5, - /obj/item/stack/sheet/glass = 1) - -/obj/item/circuitboard/prize_counter - name = "circuit board (Prize Counter)" - build_path = /obj/machinery/prize_counter - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/matter_bin = 1, - /obj/item/stock_parts/manipulator = 1, - /obj/item/stack/sheet/glass = 1, - /obj/item/stack/cable_coil = 1) - -/obj/item/circuitboard/gameboard - name = "circuit board (Virtual Gameboard)" - build_path = /obj/machinery/gameboard - board_type = "machine" - origin_tech = "programming=1" - req_components = list( - /obj/item/stock_parts/micro_laser = 1, - /obj/item/stack/cable_coil = 3, - /obj/item/stack/sheet/glass = 1) - -//Selectable mode board, like vending machine boards -/obj/item/circuitboard/logic_gate - name = "circuit board (Logic Connector)" - build_path = /obj/machinery/logic_gate - board_type = "machine" - origin_tech = "programming=1" //This stuff is pretty much the absolute basis of programming, so it's mostly useless for research - req_components = list(/obj/item/stack/cable_coil = 1) - - var/list/names_paths = list( - "NOT Gate" = /obj/machinery/logic_gate/not, - "OR Gate" = /obj/machinery/logic_gate/or, - "AND Gate" = /obj/machinery/logic_gate/and, - "NAND Gate" = /obj/machinery/logic_gate/nand, - "NOR Gate" = /obj/machinery/logic_gate/nor, - "XOR Gate" = /obj/machinery/logic_gate/xor, - "XNOR Gate" = /obj/machinery/logic_gate/xnor, - "STATUS Gate" = /obj/machinery/logic_gate/status, - "CONVERT Gate" = /obj/machinery/logic_gate/convert - ) - -/obj/item/circuitboard/logic_gate/New() - ..() - if(build_path == /obj/machinery/logic_gate) //If we spawn the base type board (determined by the base type machine as the build path), become a random gate board - var/new_path = names_paths[pick(names_paths)] - set_type(new_path) - -/obj/item/circuitboard/logic_gate/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - set_type(null, user) - return - return ..() - -/obj/item/circuitboard/logic_gate/proc/set_type(typepath, mob/user) - var/new_name = "Logic Base" - if(!typepath) - new_name = input("Circuit Setting", "What would you change the board setting to?") in names_paths - typepath = names_paths[new_name] - else - for(var/name in names_paths) - if(names_paths[name] == typepath) - new_name = name - break - build_path = typepath - name = "circuit board ([new_name])" - if(user) - to_chat(user, "You set the board to [new_name].") +/obj/machinery/constructable_frame //Made into a seperate type to make future revisions easier. + name = "machine frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "box_0" + density = 1 + anchored = 1 + use_power = NO_POWER_USE + max_integrity = 250 + var/obj/item/circuitboard/circuit = null + var/list/components = null + var/list/req_components = null + var/list/req_component_names = null // user-friendly names of components + var/state = 1 + + // For pods + var/list/connected_parts = list() + var/pattern_idx=0 + +/obj/machinery/constructable_frame/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, 5) + if(state >= 2) + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(loc) + A.amount = 5 + if(circuit) + circuit.forceMove(loc) + circuit = null + return ..() + +/obj/machinery/constructable_frame/obj_break(damage_flag) + deconstruct() + +// unfortunately, we have to instance the objects really quickly to get the names +// fortunately, this is only called once when the board is added and the items are immediately GC'd +// and none of the parts do much in their constructors +/obj/machinery/constructable_frame/proc/update_namelist() + if(!req_components) + return + + req_component_names = new() + for(var/tname in req_components) + var/path = tname + var/obj/O = new path() + req_component_names[tname] = O.name + +/obj/machinery/constructable_frame/proc/get_req_components_amt() + var/amt = 0 + for(var/path in req_components) + amt += req_components[path] + return amt + +// update description of required components remaining +/obj/machinery/constructable_frame/proc/update_req_desc() + if(!req_components || !req_component_names) + return + + var/hasContent = 0 + desc = "Requires" + for(var/i = 1 to req_components.len) + var/tname = req_components[i] + var/amt = req_components[tname] + if(amt == 0) + continue + var/use_and = i == req_components.len + desc += "[(hasContent ? (use_and ? ", and" : ",") : "")] [amt] [amt == 1 ? req_component_names[tname] : "[req_component_names[tname]]\s"]" + hasContent = 1 + + if(!hasContent) + desc = "Does not require any more components." + else + desc += "." + +/obj/machinery/constructable_frame/machine_frame/attackby(obj/item/P, mob/user, params) + switch(state) + if(1) + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(src.loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 1 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 2 + icon_state = "box_1" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + + if(istype(P, /obj/item/wrench)) + playsound(src.loc, P.usesound, 75, 1) + to_chat(user, "You dismantle the frame.") + deconstruct(TRUE) + return + if(2) + if(istype(P, /obj/item/circuitboard)) + var/obj/item/circuitboard/B = P + if(B.board_type == "machine") + playsound(src.loc, B.usesound, 50, 1) + to_chat(user, "You add the circuit board to the frame.") + circuit = P + user.drop_item() + P.loc = src + icon_state = "box_2" + state = 3 + components = list() + req_components = circuit.req_components.Copy() + update_namelist() + update_req_desc() + else + to_chat(user, "This frame does not accept circuit boards of this type!") + return + if(istype(P, /obj/item/wirecutters)) + playsound(src.loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 1 + icon_state = "box_0" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil(src.loc,5) + A.amount = 5 + return + if(3) + if(istype(P, /obj/item/crowbar)) + playsound(src.loc, P.usesound, 50, 1) + state = 2 + circuit.loc = src.loc + circuit = null + if(components.len == 0) + to_chat(user, "You remove the circuit board.") + else + to_chat(user, "You remove the circuit board and other components.") + for(var/obj/item/I in components) + I.loc = src.loc + desc = initial(desc) + req_components = null + components = null + icon_state = "box_1" + return + + if(istype(P, /obj/item/screwdriver)) + var/component_check = 1 + for(var/R in req_components) + if(req_components[R] > 0) + component_check = 0 + break + if(component_check) + playsound(src.loc, P.usesound, 50, 1) + var/obj/machinery/new_machine = new src.circuit.build_path(src.loc) + new_machine.on_construction() + for(var/obj/O in new_machine.component_parts) + qdel(O) + new_machine.component_parts = list() + for(var/obj/O in src) + O.loc = null + new_machine.component_parts += O + circuit.loc = null + new_machine.RefreshParts() + qdel(src) + return + + if(istype(P, /obj/item/storage/part_replacer) && P.contents.len && get_req_components_amt()) + var/obj/item/storage/part_replacer/replacer = P + var/list/added_components = list() + var/list/part_list = list() + + //Assemble a list of current parts, then sort them by their rating! + for(var/obj/item/stock_parts/co in replacer) + part_list += co + + for(var/path in req_components) + while(req_components[path] > 0 && (locate(path) in part_list)) + var/obj/item/part = (locate(path) in part_list) + added_components[part] = path + replacer.remove_from_storage(part, src) + req_components[path]-- + part_list -= part + + for(var/obj/item/stock_parts/part in added_components) + components += part + to_chat(user, "[part.name] applied.") + replacer.play_rped_sound() + + update_req_desc() + return + + if(istype(P, /obj/item)) + var/success + for(var/I in req_components) + if(istype(P, I) && (req_components[I] > 0) && (!(P.flags & NODROP) || istype(P, /obj/item/stack))) + success=1 + playsound(src.loc, P.usesound, 50, 1) + if(istype(P, /obj/item/stack)) + var/obj/item/stack/S = P + var/camt = min(S.amount, req_components[I]) + var/obj/item/stack/NS = new P.type(src) + NS.amount = camt + NS.update_icon() + S.use(camt) + components += NS + req_components[I] -= camt + update_req_desc() + break + user.drop_item() + P.forceMove(src) + components += P + req_components[I]-- + update_req_desc() + return 1 + if(!success) + to_chat(user, "You cannot add that to the machine!") + return 0 + return + if(user.a_intent == INTENT_HARM) + return ..() + + +//Machine Frame Circuit Boards +/*Common Parts: Parts List: Ignitor, Timer, Infra-red laser, Infra-red sensor, t_scanner, Capacitor, Valve, sensor unit, +micro-manipulator, console screen, beaker, Microlaser, matter bin, power cells. +Note: Once everything is added to the public areas, will add MAT_METAL and MAT_GLASS to circuit boards since autolathe won't be able +to destroy them and players will be able to make replacements. +*/ +/obj/item/circuitboard/vendor + name = "circuit board (Booze-O-Mat Vendor)" + board_type = "machine" + origin_tech = "programming=1" + frame_desc = "Requires 1 Resupply Canister." + build_path = /obj/machinery/vending/boozeomat + req_components = list(/obj/item/vending_refill/boozeomat = 1) + + var/static/list/vending_names_paths = list( + /obj/machinery/vending/boozeomat = "Booze-O-Mat", + /obj/machinery/vending/coffee = "Solar's Best Hot Drinks", + /obj/machinery/vending/snack = "Getmore Chocolate Corp", + /obj/machinery/vending/chinese = "Mr. Chang", + /obj/machinery/vending/cola = "Robust Softdrinks", + /obj/machinery/vending/cigarette = "ShadyCigs Deluxe", + /obj/machinery/vending/hatdispenser = "Hatlord 9000", + /obj/machinery/vending/suitdispenser = "Suitlord 9000", + /obj/machinery/vending/shoedispenser = "Shoelord 9000", + /obj/machinery/vending/clothing = "ClothesMate", + /obj/machinery/vending/medical = "NanoMed Plus", + /obj/machinery/vending/wallmed = "NanoMed", + /obj/machinery/vending/assist = "Vendomat", + /obj/machinery/vending/engivend = "Engi-Vend", + /obj/machinery/vending/hydronutrients = "NutriMax", + /obj/machinery/vending/hydroseeds = "MegaSeed Servitor", + /obj/machinery/vending/sustenance = "Sustenance Vendor", + /obj/machinery/vending/dinnerware = "Plasteel Chef's Dinnerware Vendor", + /obj/machinery/vending/cart = "PTech", + /obj/machinery/vending/robotics = "Robotech Deluxe", + /obj/machinery/vending/engineering = "Robco Tool Maker", + /obj/machinery/vending/sovietsoda = "BODA", + /obj/machinery/vending/security = "SecTech", + /obj/machinery/vending/modularpc = "Deluxe Silicate Selections", + /obj/machinery/vending/crittercare = "CritterCare") + +/obj/item/circuitboard/vendor/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/static/list/display_vending_names_paths + if(!display_vending_names_paths) + display_vending_names_paths = list() + for(var/path in vending_names_paths) + display_vending_names_paths[vending_names_paths[path]] = path + var/choice = input(user, "Choose a new brand","Select an Item") as null|anything in display_vending_names_paths + if(loc != user) + to_chat(user, "You need to keep [src] in your hands while doing that!") + return + set_type(display_vending_names_paths[choice]) + +/obj/item/circuitboard/vendor/proc/set_type(obj/machinery/vending/typepath) + build_path = typepath + name = "circuit board ([vending_names_paths[build_path]] Vendor)" + req_components = list(initial(typepath.refill_canister) = 1) + +/obj/item/circuitboard/smes + name = "circuit board (SMES)" + build_path = /obj/machinery/power/smes + board_type = "machine" + origin_tech = "programming=3;powerstorage=3;engineering=3" + frame_desc = "Requires 5 pieces of cable, 5 Power Cells and 1 Capacitor." + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/cell = 5, + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/emitter + name = "circuit board (Emitter)" + build_path = /obj/machinery/power/emitter + board_type = "machine" + origin_tech = "programming=3;powerstorage=4;engineering=4" + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/power_compressor + name = "circuit board (Power Compressor)" + build_path = /obj/machinery/power/compressor + board_type = "machine" + origin_tech = "programming=4;powerstorage=4;engineering=4" + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/manipulator = 6) + +/obj/item/circuitboard/power_turbine + name = "circuit board (Power Turbine)" + build_path = /obj/machinery/power/turbine + board_type = "machine" + origin_tech = "programming=4;powerstorage=4;engineering=4" + req_components = list( + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/capacitor = 6) + +/obj/item/circuitboard/thermomachine + name = "circuit board (Freezer)" + desc = "Use screwdriver to switch between heating and cooling modes." + build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer + board_type = "machine" + origin_tech = "programming=3;plasmatech=3" + frame_desc = "Requires 2 Matter Bins, 2 Micro Lasers, 1 piece of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/thermomachine/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + if(build_path == /obj/machinery/atmospherics/unary/cold_sink/freezer) + build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + name = "circuit board (Heater)" + to_chat(user, "You set the board to heating.") + else + build_path = /obj/machinery/atmospherics/unary/cold_sink/freezer + name = "circuit board (Freezer)" + to_chat(user, "You set the board to cooling.") + return + return ..() + +/obj/item/circuitboard/recharger + name = "circuit board (Recharger)" + build_path = /obj/machinery/recharger + board_type = "machine" + origin_tech = "powerstorage=3;materials=2" + frame_desc = "Requires 1 Capacitor" + req_components = list(/obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/snow_machine + name = "circuit board (snow machine)" + build_path = /obj/machinery/snow_machine + board_type = "machine" + origin_tech = "programming=2;materials=2" + frame_desc = "Requires 1 Matter Bin and 1 Micro Laser." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/biogenerator + name = "circuit board (Biogenerator)" + build_path = /obj/machinery/biogenerator + board_type = "machine" + origin_tech = "programming=2;biotech=3;materials=3" + frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/plantgenes + name = "Plant DNA Manipulator (Machine Board)" + build_path = /obj/machinery/plantgenes + board_type = "machine" + origin_tech = "programming=3;biotech=3" + frame_desc = "Requires 1 Manipulator, 1 Micro Laser, 1 Console Screen, and 1 Scanning Module." + req_components = list( + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/scanning_module = 1) + +/obj/item/circuitboard/plantgenes/vault + +/obj/item/circuitboard/seed_extractor + name = "circuit board (Seed Extractor)" + build_path = /obj/machinery/seed_extractor + board_type = "machine" + origin_tech = "programming=1" + frame_desc = "Requires 1 Matter Bin and 1 Manipulator." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/hydroponics + name = "circuit board (Hydroponics Tray)" + build_path = /obj/machinery/hydroponics/constructable + board_type = "machine" + origin_tech = "programming=1;biotech=2" + frame_desc = "Requires 2 Matter Bins, 1 Manipulator, and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/microwave + name = "circuit board (Microwave)" + build_path = /obj/machinery/kitchen_machine/microwave + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 1 Micro Laser, 2 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/oven + name = "circuit board (Oven)" + build_path = /obj/machinery/kitchen_machine/oven + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/grill + name = "circuit board (Grill)" + build_path = /obj/machinery/kitchen_machine/grill + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 2 Micro Lasers, 5 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/candy_maker + name = "circuit board (Candy Maker)" + build_path = /obj/machinery/kitchen_machine/candy_maker + board_type = "machine" + origin_tech = "programming=2;magnets=2" + frame_desc = "Requires 1 Manipulator, 5 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/deepfryer + name = "circuit board (Deep Fryer)" + build_path = /obj/machinery/cooker/deepfryer + board_type = "machine" + origin_tech = "programming=1" + frame_desc = "Requires 2 Micro Lasers and 5 pieces of cable." + req_components = list( + /obj/item/stock_parts/micro_laser = 2, + /obj/item/stack/cable_coil = 5) + +/obj/item/circuitboard/gibber + name = "circuit board (Gibber)" + build_path = /obj/machinery/gibber + board_type = "machine" + origin_tech = "programming=2;engineering=2" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/tesla_coil + name = "circuit board (Tesla Coil)" + build_path = /obj/machinery/power/tesla_coil + board_type = "machine" + origin_tech = "programming=3;magnets=3;powerstorage=3" + req_components = list( + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/grounding_rod + name = "circuit board (Grounding Rod)" + build_path = /obj/machinery/power/grounding_rod + board_type = "machine" + origin_tech = "programming=3;powerstorage=3;magnets=3;plasmatech=2" + req_components = list( + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/processor + name = "circuit board (Food processor)" + build_path = /obj/machinery/processor + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/recycler + name = "circuit board (Recycler)" + build_path = /obj/machinery/recycler + board_type = "machine" + origin_tech = "programming=2;engineering=2" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/smartfridge + name = "circuit board (Smartfridge)" + build_path = /obj/machinery/smartfridge + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1) + var/list/fridge_names_paths = list( + "\improper SmartFridge" = /obj/machinery/smartfridge, + "\improper MegaSeed Servitor" = /obj/machinery/smartfridge/seeds, + "\improper Refrigerated Medicine Storage" = /obj/machinery/smartfridge/medbay, + "\improper Slime Extract Storage" = /obj/machinery/smartfridge/secure/extract, + "\improper Secure Refrigerated Medicine Storage" = /obj/machinery/smartfridge/secure/medbay, + "\improper Smart Chemical Storage" = /obj/machinery/smartfridge/secure/chemistry, + "smart virus storage" = /obj/machinery/smartfridge/secure/chemistry/virology, + "\improper Drink Showcase" = /obj/machinery/smartfridge/drinks + ) + + + +/obj/item/circuitboard/smartfridge/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + set_type(null, user) + return + return ..() + +/obj/item/circuitboard/smartfridge/proc/set_type(typepath, mob/user) + var/new_name = "" + if(!typepath) + new_name = input("Circuit Setting", "What would you change the board setting to?") in fridge_names_paths + typepath = fridge_names_paths[new_name] + else + for(var/name in fridge_names_paths) + if(fridge_names_paths[name] == typepath) + new_name = name + break + build_path = typepath + name = new_name + if(findtextEx(new_name, "\improper")) + new_name = replacetext(new_name, "\improper", "") + if(user) + to_chat(user, "You set the board to [new_name].") + +/obj/item/circuitboard/monkey_recycler + name = "circuit board (Monkey Recycler)" + build_path = /obj/machinery/monkey_recycler + board_type = "machine" + origin_tech = "programming=1;biotech=2" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1) + +/obj/item/circuitboard/holopad + name = "circuit board (AI Holopad)" + build_path = /obj/machinery/hologram/holopad + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/chem_dispenser + name = "circuit board (Chem Dispenser)" + build_path = /obj/machinery/chem_dispenser + board_type = "machine" + origin_tech = "materials=4;programming=4;plasmatech=4;biotech=3" + frame_desc = "Requires 2 Matter Bins, 1 Capacitor, 1 Manipulator, 1 Console Screen, and 1 Power Cell." + req_components = list( /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/cell = 1) + +/obj/item/circuitboard/chem_master + name = "circuit board (ChemMaster 3000)" + build_path = /obj/machinery/chem_master + board_type = "machine" + origin_tech = "materials=3;programming=2;biotech=3" + req_components = list( + /obj/item/reagent_containers/glass/beaker = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/chem_master/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/new_name = "ChemMaster" + var/new_path = /obj/machinery/chem_master + + if(build_path == /obj/machinery/chem_master) + new_name = "CondiMaster" + new_path = /obj/machinery/chem_master/condimaster + + build_path = new_path + name = "circuit board ([new_name] 3000)" + to_chat(user, "You change the circuit board setting to \"[new_name]\".") + +/obj/item/circuitboard/chem_master/condi_master + name = "circuit board (CondiMaster 3000)" + build_path = /obj/machinery/chem_master/condimaster + +/obj/item/circuitboard/chem_heater + name = "circuit board (Chemical Heater)" + build_path = /obj/machinery/chem_heater + board_type = "machine" + origin_tech = "programming=2;engineering=2;biotech=2" + frame_desc = "Requires 1 Micro Laser and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/reagentgrinder + name = "circuit board (All-In-One Grinder)" + build_path = /obj/machinery/reagentgrinder/empty + board_type = "machine" + origin_tech = "materials=2;engineering=2;biotech=2" + frame_desc = "Requires 2 Manipulators and 1 Matter Bin." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/matter_bin = 1) + +//Almost the same recipe as destructive analyzer to give people choices. +/obj/item/circuitboard/experimentor + name = "circuit board (E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/r_n_d/experimentor + board_type = "machine" + origin_tech = "magnets=1;engineering=1;programming=1;biotech=1;bluespace=2" + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/micro_laser = 2) + +/obj/item/circuitboard/destructive_analyzer + name = "Circuit board (Destructive Analyzer)" + build_path = /obj/machinery/r_n_d/destructive_analyzer + board_type = "machine" + origin_tech = "magnets=2;engineering=2;programming=2" + frame_desc = "Requires 1 Scanning Module, 1 Manipulator, and 1 Micro-Laser." + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/autolathe + name = "Circuit board (Autolathe)" + build_path = /obj/machinery/autolathe + board_type = "machine" + origin_tech = "engineering=2;programming=2" + frame_desc = "Requires 3 Matter Bins, 1 Manipulator, and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 3, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/protolathe + name = "Circuit board (Protolathe)" + build_path = /obj/machinery/r_n_d/protolathe + board_type = "machine" + origin_tech = "engineering=2;programming=2" + frame_desc = "Requires 2 Matter Bins, 2 Manipulators, and 2 Beakers." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 2, + /obj/item/reagent_containers/glass/beaker = 2) + +/obj/item/circuitboard/chem_dispenser/soda + name = "Circuit board (Soda Machine)" + build_path = /obj/machinery/chem_dispenser/soda + +/obj/item/circuitboard/chem_dispenser/beer + name = "Circuit board (Beer Machine)" + build_path = /obj/machinery/chem_dispenser/beer + +/obj/item/circuitboard/circuit_imprinter + name = "Circuit board (Circuit Imprinter)" + build_path = /obj/machinery/r_n_d/circuit_imprinter + board_type = "machine" + origin_tech = "engineering=2;programming=2" + frame_desc = "Requires 1 Matter Bin, 1 Manipulator, and 2 Beakers." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/reagent_containers/glass/beaker = 2) + +/obj/item/circuitboard/pacman + name = "Circuit Board (PACMAN-type Generator)" + build_path = /obj/machinery/power/port_gen/pacman + board_type = "machine" + origin_tech = "programming=2;powerstorage=3;plasmatech=3;engineering=3" + frame_desc = "Requires 1 Matter Bin, 1 Micro-Laser, 2 Pieces of Cable, and 1 Capacitor." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/capacitor = 1) + +/obj/item/circuitboard/pacman/super + name = "Circuit Board (SUPERPACMAN-type Generator)" + build_path = /obj/machinery/power/port_gen/pacman/super + origin_tech = "programming=3;powerstorage=4;engineering=4" + +/obj/item/circuitboard/pacman/mrs + name = "Circuit Board (MRSPACMAN-type Generator)" + build_path = /obj/machinery/power/port_gen/pacman/mrs + origin_tech = "programming=3;powerstorage=4;engineering=4;plasmatech=4" + +/obj/item/circuitboard/rdserver + name = "Circuit Board (R&D Server)" + build_path = /obj/machinery/r_n_d/server + board_type = "machine" + origin_tech = "programming=3" + frame_desc = "Requires 2 pieces of cable, and 1 Scanning Module." + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/scanning_module = 1) + +/obj/item/circuitboard/mechfab + name = "Circuit board (Exosuit Fabricator)" + build_path = /obj/machinery/mecha_part_fabricator + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Matter Bins, 1 Manipulator, 1 Micro-Laser and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/podfab + name = "Circuit board (Spacepod Fabricator)" + build_path = /obj/machinery/mecha_part_fabricator/spacepod + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Matter Bins, 1 Manipulators, 1 Micro-Lasers, and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1) + + +/obj/item/circuitboard/clonepod + name = "Circuit board (Clone Pod)" + build_path = /obj/machinery/clonepod + board_type = "machine" + origin_tech = "programming=2;biotech=2" + frame_desc = "Requires 2 Manipulator, 2 Scanning Module, 2 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/scanning_module = 2, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/clonescanner + name = "Circuit board (Cloning Scanner)" + build_path = /obj/machinery/dna_scannernew + board_type = "machine" + origin_tech = "programming=2;biotech=2" + frame_desc = "Requires 1 Scanning Module, 1 Manipulator, 1 Micro-Laser, 2 pieces of cable and 1 Console Screen." + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stack/cable_coil = 2,) + +/obj/item/circuitboard/mech_recharger + name = "circuit board (Mech Bay Recharger)" + build_path = /obj/machinery/mech_bay_recharge_port + board_type = "machine" + origin_tech = "programming=3;powerstorage=3;engineering=3" + frame_desc = "Requires 1 piece of cable and 5 Capacitors." + req_components = list( + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/capacitor = 5) + +/obj/item/circuitboard/teleporter_hub + name = "circuit board (Teleporter Hub)" + build_path = /obj/machinery/teleport/hub + board_type = "machine" + origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" + frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 3, + /obj/item/stock_parts/matter_bin = 1) + +/obj/item/circuitboard/teleporter_station + name = "circuit board (Teleporter Station)" + build_path = /obj/machinery/teleport/station + board_type = "machine" + origin_tech = "programming=4;engineering=4;bluespace=4;plasmatech=3" + frame_desc = "Requires 2 Bluespace Crystals, 2 Capacitors and 1 Console Screen." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 2, + /obj/item/stock_parts/capacitor = 2, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/teleporter_perma + name = "circuit board (Permanent Teleporter)" + build_path = /obj/machinery/teleport/perma + board_type = "machine" + origin_tech = "programming=3;engineering=4;bluespace=4;materials=4" + frame_desc = "Requires 3 Bluespace Crystals and 1 Matter Bin." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 3, + /obj/item/stock_parts/matter_bin = 1) + var/target + +/obj/item/circuitboard/teleporter_perma/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/gps)) + var/obj/item/gps/L = I + if(L.locked_location) + target = get_turf(L.locked_location) + to_chat(user, "You upload the data from [L]") + return + return ..() + +/obj/item/circuitboard/telesci_pad + name = "Circuit board (Telepad)" + build_path = /obj/machinery/telepad + board_type = "machine" + origin_tech = "programming=4;engineering=3;plasmatech=4;bluespace=4" + frame_desc = "Requires 2 Bluespace Crystals, 1 Capacitor, 1 piece of cable and 1 Console Screen." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 2, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/quantumpad + name = "circuit board (Quantum Pad)" + build_path = /obj/machinery/quantumpad + board_type = "machine" + origin_tech = "programming=3;engineering=3;plasmatech=3;bluespace=4" + frame_desc = "Requires 1 Bluespace Crystal, 1 Capacitor, 1 piece of cable and 1 Manipulator." + req_components = list( + /obj/item/stack/ore/bluespace_crystal = 1, + /obj/item/stock_parts/capacitor = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 1) + +/obj/item/circuitboard/sleeper + name = "circuit board (Sleeper)" + build_path = /obj/machinery/sleeper + board_type = "machine" + origin_tech = "programming=3;biotech=2;engineering=3" + frame_desc = "Requires 1 Matter Bin, 1 Manipulator, 1 piece of cable and 2 Console Screens." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 2) + +/obj/item/circuitboard/sleeper/syndicate + name = "circuit board (Sleeper Syndicate)" + build_path = /obj/machinery/sleeper/syndie + +/obj/item/circuitboard/sleeper/survival + name = "circuit board (Sleeper Survival Pod)" + build_path = /obj/machinery/sleeper/survival_pod + + +/obj/item/circuitboard/bodyscanner + name = "circuit board (Body Scanner)" + build_path = /obj/machinery/bodyscanner + board_type = "machine" + origin_tech = "programming=3;biotech=2;engineering=3" + frame_desc = "Requires 1 Scanning Module, 2 pieces of cable and 2 Console Screens." + req_components = list( + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stack/sheet/glass = 2) + +/obj/item/circuitboard/cryo_tube + name = "circuit board (Cryotube)" + build_path = /obj/machinery/atmospherics/unary/cryo_cell + board_type = "machine" + origin_tech = "programming=4;biotech=3;engineering=4;plasmatech=3" + frame_desc = "Requires 1 Matter Bin, 1 piece of cable and 4 Console Screens." + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stack/cable_coil = 1, + /obj/item/stack/sheet/glass = 4) + +/obj/item/circuitboard/cyborgrecharger + name = "circuit board (Cyborg Recharger)" + build_path = /obj/machinery/recharge_station + board_type = "machine" + origin_tech = "powerstorage=3;engineering=3" + frame_desc = "Requires 2 Capacitors, 1 Power Cell and 1 Manipulator." + req_components = list( + /obj/item/stock_parts/capacitor = 2, + /obj/item/stock_parts/cell = 1, + /obj/item/stock_parts/manipulator = 1) + +// Telecomms circuit boards: +/obj/item/circuitboard/telecomms/receiver + name = "Circuit Board (Subspace Receiver)" + build_path = /obj/machinery/telecomms/receiver + board_type = "machine" + origin_tech = "programming=2;engineering=2;bluespace=1" + frame_desc = "Requires 1 Subspace Ansible, 1 Hyperwave Filter, 2 Manipulators, and 1 Micro-Laser." + req_components = list( + /obj/item/stock_parts/subspace/ansible = 1, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/manipulator = 2, + /obj/item/stock_parts/micro_laser = 1) + +/obj/item/circuitboard/telecomms/hub + name = "Circuit Board (Hub Mainframe)" + build_path = /obj/machinery/telecomms/hub + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filter." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/subspace/filter = 2) + +/obj/item/circuitboard/telecomms/relay + name = "Circuit Board (Relay Mainframe)" + build_path = /obj/machinery/telecomms/relay + board_type = "machine" + origin_tech = "programming=2;engineering=2;bluespace=2" + frame_desc = "Requires 2 Manipulators, 2 Cable Coil and 2 Hyperwave Filters." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/subspace/filter = 2) + +/obj/item/circuitboard/telecomms/bus + name = "Circuit Board (Bus Mainframe)" + build_path = /obj/machinery/telecomms/bus + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/subspace/filter = 1) + +/obj/item/circuitboard/telecomms/processor + name = "Circuit Board (Processor Unit)" + build_path = /obj/machinery/telecomms/processor + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 3 Manipulators, 1 Hyperwave Filter, 2 Treatment Disks, 1 Wavelength Analyzer, 2 Cable Coils and 1 Subspace Amplifier." + req_components = list( + /obj/item/stock_parts/manipulator = 3, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/subspace/treatment = 2, + /obj/item/stock_parts/subspace/analyzer = 1, + /obj/item/stack/cable_coil = 2, + /obj/item/stock_parts/subspace/amplifier = 1) + +/obj/item/circuitboard/telecomms/server + name = "Circuit Board (Telecommunication Server)" + build_path = /obj/machinery/telecomms/server + board_type = "machine" + origin_tech = "programming=2;engineering=2" + frame_desc = "Requires 2 Manipulators, 1 Cable Coil and 1 Hyperwave Filter." + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/subspace/filter = 1) + +/obj/item/circuitboard/telecomms/broadcaster + name = "Circuit Board (Subspace Broadcaster)" + build_path = /obj/machinery/telecomms/broadcaster + board_type = "machine" + origin_tech = "programming=2;engineering=2;bluespace=1" + frame_desc = "Requires 2 Manipulators, 1 Cable Coil, 1 Hyperwave Filter, 1 Ansible Crystal and 2 High-Powered Micro-Lasers. " + req_components = list( + /obj/item/stock_parts/manipulator = 2, + /obj/item/stack/cable_coil = 1, + /obj/item/stock_parts/subspace/filter = 1, + /obj/item/stock_parts/subspace/crystal = 1, + /obj/item/stock_parts/micro_laser/high = 2) + +/obj/item/circuitboard/ore_redemption + name = "circuit board (Ore Redemption)" + build_path = /obj/machinery/mineral/ore_redemption + board_type = "machine" + origin_tech = "programming=1;engineering=2" + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/assembly/igniter = 1) + +/obj/item/circuitboard/ore_redemption/golem + name = "circuit board (Golem Ore Redemption)" + build_path = /obj/machinery/mineral/ore_redemption/golem + +/obj/item/circuitboard/mining_equipment_vendor + name = "circuit board (Mining Equipment Vendor)" + build_path = /obj/machinery/mineral/equipment_vendor + board_type = "machine" + origin_tech = "programming=1;engineering=3" + req_components = list( + /obj/item/stack/sheet/glass = 1, + /obj/item/stock_parts/matter_bin = 3) + +/obj/item/circuitboard/mining_equipment_vendor/golem + name = "circuit board (Mining Equipment Vendor)" + build_path = /obj/machinery/mineral/equipment_vendor/golem + +/obj/item/circuitboard/clawgame + name = "circuit board (Claw Game)" + build_path = /obj/machinery/arcade/claw + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 5, + /obj/item/stack/sheet/glass = 1) + +/obj/item/circuitboard/prize_counter + name = "circuit board (Prize Counter)" + build_path = /obj/machinery/prize_counter + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/matter_bin = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/sheet/glass = 1, + /obj/item/stack/cable_coil = 1) + +/obj/item/circuitboard/gameboard + name = "circuit board (Virtual Gameboard)" + build_path = /obj/machinery/gameboard + board_type = "machine" + origin_tech = "programming=1" + req_components = list( + /obj/item/stock_parts/micro_laser = 1, + /obj/item/stack/cable_coil = 3, + /obj/item/stack/sheet/glass = 1) + +//Selectable mode board, like vending machine boards +/obj/item/circuitboard/logic_gate + name = "circuit board (Logic Connector)" + build_path = /obj/machinery/logic_gate + board_type = "machine" + origin_tech = "programming=1" //This stuff is pretty much the absolute basis of programming, so it's mostly useless for research + req_components = list(/obj/item/stack/cable_coil = 1) + + var/list/names_paths = list( + "NOT Gate" = /obj/machinery/logic_gate/not, + "OR Gate" = /obj/machinery/logic_gate/or, + "AND Gate" = /obj/machinery/logic_gate/and, + "NAND Gate" = /obj/machinery/logic_gate/nand, + "NOR Gate" = /obj/machinery/logic_gate/nor, + "XOR Gate" = /obj/machinery/logic_gate/xor, + "XNOR Gate" = /obj/machinery/logic_gate/xnor, + "STATUS Gate" = /obj/machinery/logic_gate/status, + "CONVERT Gate" = /obj/machinery/logic_gate/convert + ) + +/obj/item/circuitboard/logic_gate/New() + ..() + if(build_path == /obj/machinery/logic_gate) //If we spawn the base type board (determined by the base type machine as the build path), become a random gate board + var/new_path = names_paths[pick(names_paths)] + set_type(new_path) + +/obj/item/circuitboard/logic_gate/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + set_type(null, user) + return + return ..() + +/obj/item/circuitboard/logic_gate/proc/set_type(typepath, mob/user) + var/new_name = "Logic Base" + if(!typepath) + new_name = input("Circuit Setting", "What would you change the board setting to?") in names_paths + typepath = names_paths[new_name] + else + for(var/name in names_paths) + if(names_paths[name] == typepath) + new_name = name + break + build_path = typepath + name = "circuit board ([new_name])" + if(user) + to_chat(user, "You set the board to [new_name].") diff --git a/code/game/machinery/cryo.dm b/code/game/machinery/cryo.dm index ded10e41a042..a99384d364ac 100644 --- a/code/game/machinery/cryo.dm +++ b/code/game/machinery/cryo.dm @@ -1,517 +1,517 @@ -/obj/machinery/atmospherics/unary/cryo_cell - name = "cryo cell" - desc = "Lowers the body temperature so certain medications may take effect." - icon = 'icons/obj/cryogenics.dmi' - icon_state = "pod0" - density = 1 - anchored = 1.0 - layer = ABOVE_WINDOW_LAYER - plane = GAME_PLANE - interact_offline = 1 - max_integrity = 350 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30) - var/on = 0 - var/temperature_archived - var/mob/living/carbon/occupant = null - var/obj/item/reagent_containers/glass/beaker = null - var/autoeject = 0 - - var/next_trans = 0 - var/current_heat_capacity = 50 - var/efficiency - - var/running_bob_animation = 0 // This is used to prevent threads from building up if update_icons is called multiple times - - light_color = LIGHT_COLOR_WHITE - power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/atmospherics/unary/cryo_cell/New() - ..() - initialize_directions = dir - component_parts = list() - component_parts += new /obj/item/circuitboard/cryo_tube(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cryo_cell/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/cryo_tube(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cryo_cell/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/cryo_cell/RefreshParts() - var/C - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - C += M.rating - current_heat_capacity = 50 * C - efficiency = C - -/obj/machinery/atmospherics/unary/cryo_cell/atmos_init() - ..() - if(node) return - for(var/cdir in cardinal) - node = findConnecting(cdir) - if(node) - break - -/obj/machinery/atmospherics/unary/cryo_cell/Destroy() - QDEL_NULL(beaker) - return ..() - -/obj/machinery/atmospherics/unary/cryo_cell/ex_act(severity) - if(occupant) - occupant.ex_act(severity) - if(beaker) - beaker.ex_act(severity) - ..() - -/obj/machinery/atmospherics/unary/cryo_cell/handle_atom_del(atom/A) - ..() - if(A == beaker) - beaker = null - updateUsrDialog() - if(A == occupant) - occupant = null - updateUsrDialog() - update_icon() - -/obj/machinery/atmospherics/unary/cryo_cell/on_deconstruction() - if(beaker) - beaker.forceMove(drop_location()) - beaker = null - -/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) - if(O.loc == user) //no you can't pull things out of your ass - return - if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source - return - if(!ismob(O)) //humans only - return - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robutts dont fit - return - if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper - return - if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems - return - if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? - return - if(occupant) - to_chat(user, "The cryo cell is already occupied!") - return - var/mob/living/L = O - if(!istype(L) || L.buckled) - return - if(L.abiotic()) - to_chat(user, "Subject cannot have abiotic items on.") - return - if(L.has_buckled_mobs()) //mob attached to us - to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") - return - if(put_mob(L)) - if(L == user) - visible_message("[user] climbs into the cryo cell.") - else - visible_message("[user] puts [L.name] into the cryo cell.") - add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL) - if(user.pulling == L) - user.stop_pulling() - -/obj/machinery/atmospherics/unary/cryo_cell/process() - ..() - if(autoeject) - if(occupant) - if(!occupant.has_organic_damage() && !occupant.has_mutated_organs()) - on = 0 - go_out() - playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) - - if(air_contents) - if(occupant) - process_occupant() - - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/process_atmos() - ..() - if(!node) - return - if(!on) - return - - if(air_contents) - temperature_archived = air_contents.temperature - heat_gas_contents() - - if(abs(temperature_archived-air_contents.temperature) > 1) - parent.update = 1 - - -/obj/machinery/atmospherics/unary/cryo_cell/AllowDrop() - return FALSE - - -/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) - if(user.stat) - return - go_out() - return - -/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) - return attack_hand(user) - -/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) - if(user == occupant) - return - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - - ui_interact(user) - - - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui - * - * @return nothing - */ -/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 420) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["isOperating"] = on - data["hasOccupant"] = occupant ? 1 : 0 - - var/occupantData[0] - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["bodyTemperature"] = occupant.bodytemperature - data["occupant"] = occupantData; - - data["cellTemperature"] = round(air_contents.temperature) - data["cellTemperatureStatus"] = "good" - if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) - data["cellTemperatureStatus"] = "bad" - else if(air_contents.temperature > TCRYO) - data["cellTemperatureStatus"] = "average" - - data["isBeakerLoaded"] = beaker ? 1 : 0 - data["beakerLabel"] = null - data["beakerVolume"] = 0 - if(beaker) - data["beakerLabel"] = beaker.label_text ? beaker.label_text : null - if(beaker.reagents && beaker.reagents.reagent_list.len) - for(var/datum/reagent/R in beaker.reagents.reagent_list) - data["beakerVolume"] += R.volume - - data["autoeject"] = autoeject - return data - -/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) - if(usr == occupant) - return 0 // don't update UIs attached to this object - - if(..()) - return 0 // don't update UIs attached to this object - - if(href_list["switchOn"]) - on = 1 - update_icon() - - if(href_list["switchOff"]) - on = 0 - update_icon() - - if(href_list["autoejectOn"]) - autoeject = 1 - - if(href_list["autoejectOff"]) - autoeject = 0 - - if(href_list["ejectBeaker"]) - if(beaker) - beaker.forceMove(get_step(loc, SOUTH)) - beaker = null - - if(href_list["ejectOccupant"]) - if(!occupant || isslime(usr) || ispAI(usr)) - return 0 // don't update UIs attached to this object - add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) - go_out() - - add_fingerprint(usr) - return 1 // update UIs attached to this object - -/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params) - if(istype(G, /obj/item/reagent_containers/glass)) - var/obj/item/reagent_containers/B = G - if(beaker) - to_chat(user, "A beaker is already loaded into the machine.") - return - if(!user.drop_item()) - to_chat(user, "[B] is stuck to you!") - return - B.forceMove(src) - beaker = B - add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]") - user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!") - return - - if(exchange_parts(user, G)) - return - - if(istype(G, /obj/item/grab)) - var/obj/item/grab/GG = G - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(!ismob(GG.affecting)) - return - if(GG.affecting.has_buckled_mobs()) //mob attached to us - to_chat(user, "[GG.affecting] will not fit into [src] because [GG.affecting.p_they()] [GG.affecting.p_have()] a slime latched onto [GG.affecting.p_their()] head.") - return - var/mob/M = GG.affecting - if(put_mob(M)) - qdel(GG) - return - return ..() - -/obj/machinery/atmospherics/unary/cryo_cell/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return - -/obj/machinery/atmospherics/unary/cryo_cell/screwdriver_act(mob/user, obj/item/I) - if(occupant || on) - to_chat(user, "The maintenance panel is locked.") - return TRUE - if(default_deconstruction_screwdriver(user, "pod0-o", "pod0", I)) - return TRUE - -/obj/machinery/atmospherics/unary/cryo_cell/update_icon() - handle_update_icon() - -/obj/machinery/atmospherics/unary/cryo_cell/proc/handle_update_icon() //making another proc to avoid spam in update_icon - overlays.Cut() //empty the overlay proc, just in case - icon_state = "pod[on]" //set the icon properly every time - - if(!src.occupant) - overlays += "lid[on]" //if no occupant, just put the lid overlay on, and ignore the rest - return - - if(occupant) - var/image/pickle = image(occupant.icon, occupant.icon_state) - pickle.overlays = occupant.overlays - pickle.pixel_y = 22 - - overlays += pickle - overlays += "lid[on]" - if(src.on && !running_bob_animation) //no bobbing if off - var/up = 0 //used to see if we are going up or down, 1 is down, 2 is up - spawn(0) // Without this, the icon update will block. The new thread will die once the occupant leaves. - running_bob_animation = 1 - while(occupant) - overlays -= "lid[on]" //have to remove the overlays first, to force an update- remove cloning pod overlay - overlays -= pickle //remove mob overlay - - switch(pickle.pixel_y) //this looks messy as fuck but it works, switch won't call itself twice - - if(23) //inbetween state, for smoothness - switch(up) //this is set later in the switch, to keep track of where the mob is supposed to go - if(2) //2 is up - pickle.pixel_y = 24 //set to highest - - if(1) //1 is down - pickle.pixel_y = 22 //set to lowest - - if(22) //mob is at it's lowest - pickle.pixel_y = 23 //set to inbetween - up = 2 //have to go up - - if(24) //mob is at it's highest - pickle.pixel_y = 23 //set to inbetween - up = 1 //have to go down - - overlays += pickle //re-add the mob to the icon - overlays += "lid[on]" //re-add the overlay of the pod, they are inside it, not floating - - sleep(7) //don't want to jiggle violently, just slowly bob - running_bob_animation = 0 - -/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() - if(air_contents.total_moles() < 10) - return - if(occupant) - if(occupant.stat == 2 || (occupant.health >= 100 && !occupant.has_mutated_organs())) //Why waste energy on dead or healthy people - occupant.bodytemperature = T0C - return - occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) - occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise - if(occupant.bodytemperature < T0C) - occupant.Sleeping(max(5/efficiency, (1/occupant.bodytemperature)*2000/efficiency)) - occupant.Paralyse(max(5/efficiency, (1/occupant.bodytemperature)*3000/efficiency)) - if(air_contents.oxygen > 2) - if(occupant.getOxyLoss()) - occupant.adjustOxyLoss(-6) - else - occupant.adjustOxyLoss(-1.2) - if(beaker && next_trans == 0) - var/proportion = 10 * min(1/beaker.volume, 1) - // Yes, this means you can get more bang for your buck with a beaker of SF vs a patch - // But it also means a giant beaker of SF won't heal people ridiculously fast 4 cheap - beaker.reagents.reaction(occupant, REAGENT_TOUCH, proportion) - beaker.reagents.trans_to(occupant, 1, 10) - next_trans++ - if(next_trans == 17) - next_trans = 0 - -/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() - if(air_contents.total_moles() < 1) - return - var/air_heat_capacity = air_contents.heat_capacity() - var/combined_heat_capacity = current_heat_capacity + air_heat_capacity - if(combined_heat_capacity > 0) - var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature - air_contents.temperature = combined_energy/combined_heat_capacity - -/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() - if(!occupant) - return - occupant.forceMove(get_step(loc, SOUTH)) //this doesn't account for walls or anything, but i don't forsee that being a problem. - if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected - occupant.bodytemperature = 261 - occupant = null - update_icon() - // eject trash the occupant dropped - for(var/atom/movable/A in contents - component_parts - list(beaker)) - A.forceMove(get_step(loc, SOUTH)) - -/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) - if(!istype(M)) - to_chat(usr, "The cryo cell cannot handle such a lifeform!") - return - if(occupant) - to_chat(usr, "The cryo cell is already occupied!") - return - if(M.abiotic()) - to_chat(usr, "Subject may not have abiotic items on.") - return - if(!node) - to_chat(usr, "The cell is not correctly connected to its pipe network!") - return - M.stop_pulling() - M.forceMove(src) - if(M.health > -100 && (M.health < 0 || M.sleeping)) - to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") - occupant = M -// M.metabslow = 1 - add_fingerprint(usr) - update_icon() - M.ExtinguishMob() - return 1 - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() - set name = "Eject occupant" - set category = "Object" - set src in oview(1) - - if(usr == occupant)//If the user is inside the tube... - if(usr.stat == DEAD) - return - to_chat(usr, "Release sequence activated. This will take two minutes.") - sleep(600) - if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already - return - go_out()//and release him from the eternal prison. - else - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - add_attack_logs(usr, occupant, "Ejected from cryo cell at [COORD(src)]") - go_out() - add_fingerprint(usr) - return - -/obj/machinery/atmospherics/unary/cryo_cell/narsie_act() - go_out() - new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! - color = "red"//force the icon to red - light_color = LIGHT_COLOR_RED - -/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() - set name = "Move Inside" - set category = "Object" - set src in oview(1) - - if(usr.has_buckled_mobs()) //mob attached to us - to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") - return - - if(stat & (NOPOWER|BROKEN)) - return - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - put_mob(usr) - return - - - -/datum/data/function/proc/reset() - return - -/datum/data/function/proc/r_input(href, href_list, mob/user as mob) - return - -/datum/data/function/proc/display() - return - -/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) - -/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) - return //we don't see the pipe network while inside cryo. +/obj/machinery/atmospherics/unary/cryo_cell + name = "cryo cell" + desc = "Lowers the body temperature so certain medications may take effect." + icon = 'icons/obj/cryogenics.dmi' + icon_state = "pod0" + density = 1 + anchored = 1.0 + layer = ABOVE_WINDOW_LAYER + plane = GAME_PLANE + interact_offline = 1 + max_integrity = 350 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 30, "acid" = 30) + var/on = 0 + var/temperature_archived + var/mob/living/carbon/occupant = null + var/obj/item/reagent_containers/glass/beaker = null + var/autoeject = 0 + + var/next_trans = 0 + var/current_heat_capacity = 50 + var/efficiency + + var/running_bob_animation = 0 // This is used to prevent threads from building up if update_icons is called multiple times + + light_color = LIGHT_COLOR_WHITE + power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/atmospherics/unary/cryo_cell/New() + ..() + initialize_directions = dir + component_parts = list() + component_parts += new /obj/item/circuitboard/cryo_tube(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cryo_cell/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/cryo_tube(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cryo_cell/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/cryo_cell/RefreshParts() + var/C + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + C += M.rating + current_heat_capacity = 50 * C + efficiency = C + +/obj/machinery/atmospherics/unary/cryo_cell/atmos_init() + ..() + if(node) return + for(var/cdir in GLOB.cardinal) + node = findConnecting(cdir) + if(node) + break + +/obj/machinery/atmospherics/unary/cryo_cell/Destroy() + QDEL_NULL(beaker) + return ..() + +/obj/machinery/atmospherics/unary/cryo_cell/ex_act(severity) + if(occupant) + occupant.ex_act(severity) + if(beaker) + beaker.ex_act(severity) + ..() + +/obj/machinery/atmospherics/unary/cryo_cell/handle_atom_del(atom/A) + ..() + if(A == beaker) + beaker = null + updateUsrDialog() + if(A == occupant) + occupant = null + updateUsrDialog() + update_icon() + +/obj/machinery/atmospherics/unary/cryo_cell/on_deconstruction() + if(beaker) + beaker.forceMove(drop_location()) + beaker = null + +/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) + if(O.loc == user) //no you can't pull things out of your ass + return + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source + return + if(!ismob(O)) //humans only + return + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robutts dont fit + return + if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper + return + if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems + return + if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? + return + if(occupant) + to_chat(user, "The cryo cell is already occupied!") + return + var/mob/living/L = O + if(!istype(L) || L.buckled) + return + if(L.abiotic()) + to_chat(user, "Subject cannot have abiotic items on.") + return + if(L.has_buckled_mobs()) //mob attached to us + to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") + return + if(put_mob(L)) + if(L == user) + visible_message("[user] climbs into the cryo cell.") + else + visible_message("[user] puts [L.name] into the cryo cell.") + add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL) + if(user.pulling == L) + user.stop_pulling() + +/obj/machinery/atmospherics/unary/cryo_cell/process() + ..() + if(autoeject) + if(occupant) + if(!occupant.has_organic_damage() && !occupant.has_mutated_organs()) + on = 0 + go_out() + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + + if(air_contents) + if(occupant) + process_occupant() + + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/process_atmos() + ..() + if(!node) + return + if(!on) + return + + if(air_contents) + temperature_archived = air_contents.temperature + heat_gas_contents() + + if(abs(temperature_archived-air_contents.temperature) > 1) + parent.update = 1 + + +/obj/machinery/atmospherics/unary/cryo_cell/AllowDrop() + return FALSE + + +/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob) + if(user.stat) + return + go_out() + return + +/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user) + return attack_hand(user) + +/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user) + if(user == occupant) + return + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + + ui_interact(user) + + + /** + * The ui_interact proc is used to open and update Nano UIs + * If ui_interact is not used then the UI will not update correctly + * ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob) + * + * @param user /mob The mob who is interacting with this ui + * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") + * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui + * + * @return nothing + */ +/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 420) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["isOperating"] = on + data["hasOccupant"] = occupant ? 1 : 0 + + var/occupantData[0] + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["bodyTemperature"] = occupant.bodytemperature + data["occupant"] = occupantData; + + data["cellTemperature"] = round(air_contents.temperature) + data["cellTemperatureStatus"] = "good" + if(air_contents.temperature > T0C) // if greater than 273.15 kelvin (0 celcius) + data["cellTemperatureStatus"] = "bad" + else if(air_contents.temperature > TCRYO) + data["cellTemperatureStatus"] = "average" + + data["isBeakerLoaded"] = beaker ? 1 : 0 + data["beakerLabel"] = null + data["beakerVolume"] = 0 + if(beaker) + data["beakerLabel"] = beaker.label_text ? beaker.label_text : null + if(beaker.reagents && beaker.reagents.reagent_list.len) + for(var/datum/reagent/R in beaker.reagents.reagent_list) + data["beakerVolume"] += R.volume + + data["autoeject"] = autoeject + return data + +/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list) + if(usr == occupant) + return 0 // don't update UIs attached to this object + + if(..()) + return 0 // don't update UIs attached to this object + + if(href_list["switchOn"]) + on = 1 + update_icon() + + if(href_list["switchOff"]) + on = 0 + update_icon() + + if(href_list["autoejectOn"]) + autoeject = 1 + + if(href_list["autoejectOff"]) + autoeject = 0 + + if(href_list["ejectBeaker"]) + if(beaker) + beaker.forceMove(get_step(loc, SOUTH)) + beaker = null + + if(href_list["ejectOccupant"]) + if(!occupant || isslime(usr) || ispAI(usr)) + return 0 // don't update UIs attached to this object + add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL) + go_out() + + add_fingerprint(usr) + return 1 // update UIs attached to this object + +/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params) + if(istype(G, /obj/item/reagent_containers/glass)) + var/obj/item/reagent_containers/B = G + if(beaker) + to_chat(user, "A beaker is already loaded into the machine.") + return + if(!user.drop_item()) + to_chat(user, "[B] is stuck to you!") + return + B.forceMove(src) + beaker = B + add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]") + user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!") + return + + if(exchange_parts(user, G)) + return + + if(istype(G, /obj/item/grab)) + var/obj/item/grab/GG = G + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(!ismob(GG.affecting)) + return + if(GG.affecting.has_buckled_mobs()) //mob attached to us + to_chat(user, "[GG.affecting] will not fit into [src] because [GG.affecting.p_they()] [GG.affecting.p_have()] a slime latched onto [GG.affecting.p_their()] head.") + return + var/mob/M = GG.affecting + if(put_mob(M)) + qdel(GG) + return + return ..() + +/obj/machinery/atmospherics/unary/cryo_cell/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return + +/obj/machinery/atmospherics/unary/cryo_cell/screwdriver_act(mob/user, obj/item/I) + if(occupant || on) + to_chat(user, "The maintenance panel is locked.") + return TRUE + if(default_deconstruction_screwdriver(user, "pod0-o", "pod0", I)) + return TRUE + +/obj/machinery/atmospherics/unary/cryo_cell/update_icon() + handle_update_icon() + +/obj/machinery/atmospherics/unary/cryo_cell/proc/handle_update_icon() //making another proc to avoid spam in update_icon + overlays.Cut() //empty the overlay proc, just in case + icon_state = "pod[on]" //set the icon properly every time + + if(!src.occupant) + overlays += "lid[on]" //if no occupant, just put the lid overlay on, and ignore the rest + return + + if(occupant) + var/image/pickle = image(occupant.icon, occupant.icon_state) + pickle.overlays = occupant.overlays + pickle.pixel_y = 22 + + overlays += pickle + overlays += "lid[on]" + if(src.on && !running_bob_animation) //no bobbing if off + var/up = 0 //used to see if we are going up or down, 1 is down, 2 is up + spawn(0) // Without this, the icon update will block. The new thread will die once the occupant leaves. + running_bob_animation = 1 + while(occupant) + overlays -= "lid[on]" //have to remove the overlays first, to force an update- remove cloning pod overlay + overlays -= pickle //remove mob overlay + + switch(pickle.pixel_y) //this looks messy as fuck but it works, switch won't call itself twice + + if(23) //inbetween state, for smoothness + switch(up) //this is set later in the switch, to keep track of where the mob is supposed to go + if(2) //2 is up + pickle.pixel_y = 24 //set to highest + + if(1) //1 is down + pickle.pixel_y = 22 //set to lowest + + if(22) //mob is at it's lowest + pickle.pixel_y = 23 //set to inbetween + up = 2 //have to go up + + if(24) //mob is at it's highest + pickle.pixel_y = 23 //set to inbetween + up = 1 //have to go down + + overlays += pickle //re-add the mob to the icon + overlays += "lid[on]" //re-add the overlay of the pod, they are inside it, not floating + + sleep(7) //don't want to jiggle violently, just slowly bob + running_bob_animation = 0 + +/obj/machinery/atmospherics/unary/cryo_cell/proc/process_occupant() + if(air_contents.total_moles() < 10) + return + if(occupant) + if(occupant.stat == 2 || (occupant.health >= 100 && !occupant.has_mutated_organs())) //Why waste energy on dead or healthy people + occupant.bodytemperature = T0C + return + occupant.bodytemperature += 2*(air_contents.temperature - occupant.bodytemperature)*current_heat_capacity/(current_heat_capacity + air_contents.heat_capacity()) + occupant.bodytemperature = max(occupant.bodytemperature, air_contents.temperature) // this is so ugly i'm sorry for doing it i'll fix it later i promise + if(occupant.bodytemperature < T0C) + occupant.Sleeping(max(5/efficiency, (1/occupant.bodytemperature)*2000/efficiency)) + occupant.Paralyse(max(5/efficiency, (1/occupant.bodytemperature)*3000/efficiency)) + if(air_contents.oxygen > 2) + if(occupant.getOxyLoss()) + occupant.adjustOxyLoss(-6) + else + occupant.adjustOxyLoss(-1.2) + if(beaker && next_trans == 0) + var/proportion = 10 * min(1/beaker.volume, 1) + // Yes, this means you can get more bang for your buck with a beaker of SF vs a patch + // But it also means a giant beaker of SF won't heal people ridiculously fast 4 cheap + beaker.reagents.reaction(occupant, REAGENT_TOUCH, proportion) + beaker.reagents.trans_to(occupant, 1, 10) + next_trans++ + if(next_trans == 17) + next_trans = 0 + +/obj/machinery/atmospherics/unary/cryo_cell/proc/heat_gas_contents() + if(air_contents.total_moles() < 1) + return + var/air_heat_capacity = air_contents.heat_capacity() + var/combined_heat_capacity = current_heat_capacity + air_heat_capacity + if(combined_heat_capacity > 0) + var/combined_energy = T20C*current_heat_capacity + air_heat_capacity*air_contents.temperature + air_contents.temperature = combined_energy/combined_heat_capacity + +/obj/machinery/atmospherics/unary/cryo_cell/proc/go_out() + if(!occupant) + return + occupant.forceMove(get_step(loc, SOUTH)) //this doesn't account for walls or anything, but i don't forsee that being a problem. + if(occupant.bodytemperature < 261 && occupant.bodytemperature >= 70) //Patch by Aranclanos to stop people from taking burn damage after being ejected + occupant.bodytemperature = 261 + occupant = null + update_icon() + // eject trash the occupant dropped + for(var/atom/movable/A in contents - component_parts - list(beaker)) + A.forceMove(get_step(loc, SOUTH)) + +/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob) + if(!istype(M)) + to_chat(usr, "The cryo cell cannot handle such a lifeform!") + return + if(occupant) + to_chat(usr, "The cryo cell is already occupied!") + return + if(M.abiotic()) + to_chat(usr, "Subject may not have abiotic items on.") + return + if(!node) + to_chat(usr, "The cell is not correctly connected to its pipe network!") + return + M.stop_pulling() + M.forceMove(src) + if(M.health > -100 && (M.health < 0 || M.sleeping)) + to_chat(M, "You feel a cold liquid surround you. Your skin starts to freeze up.") + occupant = M +// M.metabslow = 1 + add_fingerprint(usr) + update_icon() + M.ExtinguishMob() + return 1 + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_eject() + set name = "Eject occupant" + set category = "Object" + set src in oview(1) + + if(usr == occupant)//If the user is inside the tube... + if(usr.stat == DEAD) + return + to_chat(usr, "Release sequence activated. This will take two minutes.") + sleep(600) + if(!src || !usr || !occupant || (occupant != usr)) //Check if someone's released/replaced/bombed him already + return + go_out()//and release him from the eternal prison. + else + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + add_attack_logs(usr, occupant, "Ejected from cryo cell at [COORD(src)]") + go_out() + add_fingerprint(usr) + return + +/obj/machinery/atmospherics/unary/cryo_cell/narsie_act() + go_out() + new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! + color = "red"//force the icon to red + light_color = LIGHT_COLOR_RED + +/obj/machinery/atmospherics/unary/cryo_cell/verb/move_inside() + set name = "Move Inside" + set category = "Object" + set src in oview(1) + + if(usr.has_buckled_mobs()) //mob attached to us + to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") + return + + if(stat & (NOPOWER|BROKEN)) + return + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + put_mob(usr) + return + + + +/datum/data/function/proc/reset() + return + +/datum/data/function/proc/r_input(href, href_list, mob/user as mob) + return + +/datum/data/function/proc/display() + return + +/obj/machinery/atmospherics/components/unary/cryo_cell/get_remote_view_fullscreens(mob/user) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) + +/obj/machinery/atmospherics/components/unary/cryo_cell/update_remote_sight(mob/living/user) + return //we don't see the pipe network while inside cryo. diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index b1a78f2825f1..37d59977f3e7 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -40,7 +40,7 @@ /obj/machinery/computer/cryopod/New() ..() - for(var/T in potential_theft_objectives) + for(var/T in GLOB.potential_theft_objectives) theft_cache += new T /obj/machinery/computer/cryopod/attack_ai() @@ -416,15 +416,15 @@ // Delete them from datacore. var/announce_rank = null - if(PDA_Manifest.len) - PDA_Manifest.Cut() - for(var/datum/data/record/R in data_core.medical) + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + for(var/datum/data/record/R in GLOB.data_core.medical) if((R.fields["name"] == occupant.real_name)) qdel(R) - for(var/datum/data/record/T in data_core.security) + for(var/datum/data/record/T in GLOB.data_core.security) if((T.fields["name"] == occupant.real_name)) qdel(T) - for(var/datum/data/record/G in data_core.general) + for(var/datum/data/record/G in GLOB.data_core.general) if((G.fields["name"] == occupant.real_name)) announce_rank = G.fields["rank"] qdel(G) diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index 1fb2dfc422d5..baf32ab742c6 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -400,7 +400,7 @@ while(time) sleep(speed) for(var/i in 1 to speed) - M.setDir(pick(cardinal)) + M.setDir(pick(GLOB.cardinal)) M.resting = !M.resting M.update_canmove() time-- diff --git a/code/game/machinery/defib_mount.dm b/code/game/machinery/defib_mount.dm index 738fc1157346..d669b340436e 100644 --- a/code/game/machinery/defib_mount.dm +++ b/code/game/machinery/defib_mount.dm @@ -45,7 +45,7 @@ . = ..() if(defib) . += "There is a defib unit hooked up. Alt-click to remove it." - if(security_level >= SEC_LEVEL_RED) + if(GLOB.security_level >= SEC_LEVEL_RED) . += "Due to a security situation, its locking clamps can be toggled by swiping any ID." else . += "Its locking clamps can be [clamps_locked ? "dis" : ""]engaged by swiping an ID with access." @@ -100,7 +100,7 @@ return var/obj/item/card/id = I.GetID() if(id) - if(check_access(id) || security_level >= SEC_LEVEL_RED) //anyone can toggle the clamps in red alert! + if(check_access(id) || GLOB.security_level >= SEC_LEVEL_RED) //anyone can toggle the clamps in red alert! if(!defib) to_chat(user, "You can't engage the clamps on a defibrillator that isn't there.") return diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index f3f75f3c32cf..1d97056912f6 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -1,212 +1,212 @@ -#define SINGLE "single" -#define VERTICAL "vertical" -#define HORIZONTAL "horizontal" - -#define METAL 1 -#define WOOD 2 -#define SAND 3 - -//Barricades/cover - -/obj/structure/barricade - name = "chest high wall" - desc = "Looks like this would make good cover." - anchored = TRUE - density = TRUE - max_integrity = 100 - var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover - var/bar_material = METAL - var/drop_amount = 3 - var/stacktype = /obj/item/stack/sheet/metal - -/obj/structure/barricade/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - make_debris() - qdel(src) - - -/obj/structure/barricade/proc/make_debris() - if(stacktype) - new stacktype(get_turf(src), drop_amount) - -/obj/structure/barricade/welder_act(mob/user, obj/item/I) - if(obj_integrity >= max_integrity) - to_chat(user, "[src] does not need repairs.") - return - if(user.a_intent == INTENT_HARM) - return - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_REPAIR_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - WELDER_REPAIR_SUCCESS_MESSAGE - obj_integrity = Clamp(obj_integrity + 20, 0, max_integrity) - update_icon() - return TRUE - -/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. - if(locate(/obj/structure/barricade) in get_turf(mover)) - return TRUE - else if(istype(mover, /obj/item/projectile)) - if(!anchored) - return TRUE - var/obj/item/projectile/proj = mover - if(proj.firer && Adjacent(proj.firer)) - return TRUE - if(prob(proj_pass_rate)) - return TRUE - return FALSE - else - return !density - - - -/////BARRICADE TYPES/////// - -/obj/structure/barricade/wooden - name = "wooden barricade" - desc = "This space is blocked off by a wooden barricade." - icon = 'icons/obj/structures.dmi' - icon_state = "woodenbarricade" - bar_material = WOOD - stacktype = /obj/item/stack/sheet/wood - -/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user) - if(istype(I,/obj/item/stack/sheet/wood)) - var/obj/item/stack/sheet/wood/W = I - if(W.amount < 5) - to_chat(user, "You need at least five wooden planks to make a wall!") - return - else - to_chat(user, "You start adding [I] to [src]...") - if(do_after(user, 50, target = src)) - W.use(5) - new /turf/simulated/wall/mineral/wood/nonmetal(get_turf(src)) - qdel(src) - return - return ..() - -/obj/structure/barricade/wooden/crude - name = "crude plank barricade" - desc = "This space is blocked off by a crude assortment of planks." - icon_state = "woodenbarricade-old" - drop_amount = 1 - max_integrity = 50 - proj_pass_rate = 65 - -/obj/structure/barricade/wooden/crude/snow - desc = "This space is blocked off by a crude assortment of planks. It seems to be covered in a layer of snow." - icon_state = "woodenbarricade-snow-old" - max_integrity = 75 - -/obj/structure/barricade/sandbags - name = "sandbags" - desc = "Bags of sand. Self explanatory." - icon = 'icons/obj/smooth_structures/sandbags.dmi' - icon_state = "sandbags" - max_integrity = 280 - proj_pass_rate = 20 - pass_flags = LETPASSTHROW - bar_material = SAND - climbable = TRUE - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/barricade/sandbags, /turf/simulated/wall, /turf/simulated/wall/r_wall, /obj/structure/falsewall, /obj/structure/falsewall/reinforced, /turf/simulated/wall/rust, /turf/simulated/wall/r_wall/rust, /obj/structure/barricade/security) - stacktype = null - -/obj/structure/barricade/security - name = "security barrier" - desc = "A deployable barrier. Provides good cover in fire fights." - icon = 'icons/obj/objects.dmi' - icon_state = "barrier0" - density = FALSE - anchored = FALSE - max_integrity = 180 - proj_pass_rate = 20 - armor = list(melee = 10, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 10, acid = 0) - stacktype = null - var/deploy_time = 40 - var/deploy_message = TRUE - -/obj/structure/barricade/security/New() - ..() - addtimer(CALLBACK(src, .proc/deploy), deploy_time) - -/obj/structure/barricade/security/proc/deploy() - icon_state = "barrier1" - density = TRUE - anchored = TRUE - if(deploy_message) - visible_message("[src] deploys!") - - -/obj/item/grenade/barrier - name = "barrier grenade" - desc = "Instant cover." - icon = 'icons/obj/grenade.dmi' - icon_state = "flashbang" - item_state = "flashbang" - actions_types = list(/datum/action/item_action/toggle_barrier_spread) - var/mode = SINGLE - -/obj/item/grenade/barrier/examine(mob/user) - . = ..() - . += "Alt-click to toggle modes." - -/obj/item/grenade/barrier/AltClick(mob/living/carbon/user) - if(!istype(user) || !user.Adjacent(src) || user.incapacitated()) - return - toggle_mode(user) - -/obj/item/grenade/barrier/proc/toggle_mode(mob/user) - switch(mode) - if(SINGLE) - mode = VERTICAL - if(VERTICAL) - mode = HORIZONTAL - if(HORIZONTAL) - mode = SINGLE - - to_chat(user, "[src] is now in [mode] mode.") - -/obj/item/grenade/barrier/prime() - new /obj/structure/barricade/security(get_turf(loc)) - switch(mode) - if(VERTICAL) - var/target_turf = get_step(src, NORTH) - if(!(is_blocked_turf(target_turf))) - new /obj/structure/barricade/security(target_turf) - - var/target_turf2 = get_step(src, SOUTH) - if(!(is_blocked_turf(target_turf2))) - new /obj/structure/barricade/security(target_turf2) - if(HORIZONTAL) - var/target_turf = get_step(src, EAST) - if(!(is_blocked_turf(target_turf))) - new /obj/structure/barricade/security(target_turf) - - var/target_turf2 = get_step(src, WEST) - if(!(is_blocked_turf(target_turf2))) - new /obj/structure/barricade/security(target_turf2) - qdel(src) - -/obj/item/grenade/barrier/ui_action_click(mob/user) - toggle_mode(user) - - -/obj/structure/barricade/mime - name = "floor" - desc = "Is... this a floor?" - icon = 'icons/effects/water.dmi' - icon_state = "wet_floor_static" - stacktype = /obj/item/stack/sheet/mineral/tranquillite - -/obj/structure/barricade/mime/mrcd - stacktype = null - -#undef SINGLE -#undef VERTICAL -#undef HORIZONTAL - -#undef METAL -#undef WOOD -#undef SAND \ No newline at end of file +#define SINGLE "single" +#define VERTICAL "vertical" +#define HORIZONTAL "horizontal" + +#define METAL 1 +#define WOOD 2 +#define SAND 3 + +//Barricades/cover + +/obj/structure/barricade + name = "chest high wall" + desc = "Looks like this would make good cover." + anchored = TRUE + density = TRUE + max_integrity = 100 + var/proj_pass_rate = 50 //How many projectiles will pass the cover. Lower means stronger cover + var/bar_material = METAL + var/drop_amount = 3 + var/stacktype = /obj/item/stack/sheet/metal + +/obj/structure/barricade/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + make_debris() + qdel(src) + + +/obj/structure/barricade/proc/make_debris() + if(stacktype) + new stacktype(get_turf(src), drop_amount) + +/obj/structure/barricade/welder_act(mob/user, obj/item/I) + if(obj_integrity >= max_integrity) + to_chat(user, "[src] does not need repairs.") + return + if(user.a_intent == INTENT_HARM) + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + WELDER_REPAIR_SUCCESS_MESSAGE + obj_integrity = Clamp(obj_integrity + 20, 0, max_integrity) + update_icon() + return TRUE + +/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff. + if(locate(/obj/structure/barricade) in get_turf(mover)) + return TRUE + else if(istype(mover, /obj/item/projectile)) + if(!anchored) + return TRUE + var/obj/item/projectile/proj = mover + if(proj.firer && Adjacent(proj.firer)) + return TRUE + if(prob(proj_pass_rate)) + return TRUE + return FALSE + else + return !density + + + +/////BARRICADE TYPES/////// + +/obj/structure/barricade/wooden + name = "wooden barricade" + desc = "This space is blocked off by a wooden barricade." + icon = 'icons/obj/structures.dmi' + icon_state = "woodenbarricade" + bar_material = WOOD + stacktype = /obj/item/stack/sheet/wood + +/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user) + if(istype(I,/obj/item/stack/sheet/wood)) + var/obj/item/stack/sheet/wood/W = I + if(W.amount < 5) + to_chat(user, "You need at least five wooden planks to make a wall!") + return + else + to_chat(user, "You start adding [I] to [src]...") + if(do_after(user, 50, target = src)) + W.use(5) + new /turf/simulated/wall/mineral/wood/nonmetal(get_turf(src)) + qdel(src) + return + return ..() + +/obj/structure/barricade/wooden/crude + name = "crude plank barricade" + desc = "This space is blocked off by a crude assortment of planks." + icon_state = "woodenbarricade-old" + drop_amount = 1 + max_integrity = 50 + proj_pass_rate = 65 + +/obj/structure/barricade/wooden/crude/snow + desc = "This space is blocked off by a crude assortment of planks. It seems to be covered in a layer of snow." + icon_state = "woodenbarricade-snow-old" + max_integrity = 75 + +/obj/structure/barricade/sandbags + name = "sandbags" + desc = "Bags of sand. Self explanatory." + icon = 'icons/obj/smooth_structures/sandbags.dmi' + icon_state = "sandbags" + max_integrity = 280 + proj_pass_rate = 20 + pass_flags = LETPASSTHROW + bar_material = SAND + climbable = TRUE + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/barricade/sandbags, /turf/simulated/wall, /turf/simulated/wall/r_wall, /obj/structure/falsewall, /obj/structure/falsewall/reinforced, /turf/simulated/wall/rust, /turf/simulated/wall/r_wall/rust, /obj/structure/barricade/security) + stacktype = null + +/obj/structure/barricade/security + name = "security barrier" + desc = "A deployable barrier. Provides good cover in fire fights." + icon = 'icons/obj/objects.dmi' + icon_state = "barrier0" + density = FALSE + anchored = FALSE + max_integrity = 180 + proj_pass_rate = 20 + armor = list(melee = 10, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 10, acid = 0) + stacktype = null + var/deploy_time = 40 + var/deploy_message = TRUE + +/obj/structure/barricade/security/New() + ..() + addtimer(CALLBACK(src, .proc/deploy), deploy_time) + +/obj/structure/barricade/security/proc/deploy() + icon_state = "barrier1" + density = TRUE + anchored = TRUE + if(deploy_message) + visible_message("[src] deploys!") + + +/obj/item/grenade/barrier + name = "barrier grenade" + desc = "Instant cover." + icon = 'icons/obj/grenade.dmi' + icon_state = "flashbang" + item_state = "flashbang" + actions_types = list(/datum/action/item_action/toggle_barrier_spread) + var/mode = SINGLE + +/obj/item/grenade/barrier/examine(mob/user) + . = ..() + . += "Alt-click to toggle modes." + +/obj/item/grenade/barrier/AltClick(mob/living/carbon/user) + if(!istype(user) || !user.Adjacent(src) || user.incapacitated()) + return + toggle_mode(user) + +/obj/item/grenade/barrier/proc/toggle_mode(mob/user) + switch(mode) + if(SINGLE) + mode = VERTICAL + if(VERTICAL) + mode = HORIZONTAL + if(HORIZONTAL) + mode = SINGLE + + to_chat(user, "[src] is now in [mode] mode.") + +/obj/item/grenade/barrier/prime() + new /obj/structure/barricade/security(get_turf(loc)) + switch(mode) + if(VERTICAL) + var/target_turf = get_step(src, NORTH) + if(!(is_blocked_turf(target_turf))) + new /obj/structure/barricade/security(target_turf) + + var/target_turf2 = get_step(src, SOUTH) + if(!(is_blocked_turf(target_turf2))) + new /obj/structure/barricade/security(target_turf2) + if(HORIZONTAL) + var/target_turf = get_step(src, EAST) + if(!(is_blocked_turf(target_turf))) + new /obj/structure/barricade/security(target_turf) + + var/target_turf2 = get_step(src, WEST) + if(!(is_blocked_turf(target_turf2))) + new /obj/structure/barricade/security(target_turf2) + qdel(src) + +/obj/item/grenade/barrier/ui_action_click(mob/user) + toggle_mode(user) + + +/obj/structure/barricade/mime + name = "floor" + desc = "Is... this a floor?" + icon = 'icons/effects/water.dmi' + icon_state = "wet_floor_static" + stacktype = /obj/item/stack/sheet/mineral/tranquillite + +/obj/structure/barricade/mime/mrcd + stacktype = null + +#undef SINGLE +#undef VERTICAL +#undef HORIZONTAL + +#undef METAL +#undef WOOD +#undef SAND diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 5ba3885faff9..191d5e81079c 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -38,7 +38,7 @@ #define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection #define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection -var/list/airlock_overlays = list() +GLOBAL_LIST_EMPTY(airlock_overlays) /obj/machinery/door/airlock name = "airlock" @@ -276,6 +276,7 @@ About the new airlock wires panel: if(usr) shockedby += text("\[[time_stamp()]\] - [usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) else shockedby += text("\[[time_stamp()]\] - EMP)") message = "The door is now electrified [duration == -1 ? "permanently" : "for [duration] second\s"]." @@ -483,10 +484,10 @@ About the new airlock wires panel: /proc/get_airlock_overlay(icon_state, icon_file) var/iconkey = "[icon_state][icon_file]" - if(airlock_overlays[iconkey]) - return airlock_overlays[iconkey] - airlock_overlays[iconkey] = image(icon_file, icon_state) - return airlock_overlays[iconkey] + if(GLOB.airlock_overlays[iconkey]) + return GLOB.airlock_overlays[iconkey] + GLOB.airlock_overlays[iconkey] = image(icon_file, icon_state) + return GLOB.airlock_overlays[iconkey] /obj/machinery/door/airlock/do_animate(animation) switch(animation) @@ -550,7 +551,7 @@ About the new airlock wires panel: ui.open() ui.set_auto_update(1) -/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = default_state) +/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = GLOB.default_state) var/data[0] data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1) @@ -763,6 +764,7 @@ About the new airlock wires panel: else if(activate) //electrify door for 30 seconds shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) to_chat(usr, "The door is now electrified for thirty seconds.") electrify(30) if("electrify_permanently") @@ -774,6 +776,7 @@ About the new airlock wires panel: else if(activate) shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])") usr.create_attack_log("Electrified the [name] at [x] [y] [z]") + add_attack_logs(usr, src, "Electrified", ATKLOG_ALL) to_chat(usr, "The door is now electrified.") electrify(-1) if("open") diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index 4b36c13b0349..660c6df683e6 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -1,102 +1,102 @@ -/obj/item/airlock_electronics - name = "airlock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - req_access = list(ACCESS_ENGINE) - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - var/list/conf_access = null - var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access - var/const/max_brain_damage = 60 // Maximum brain damage a mob can have until it can't use the electronics - var/unres_sides = 0 - var/unres_direction = null - -/obj/item/airlock_electronics/attack_self(mob/user) - if(!ishuman(user) && !isrobot(user)) - return ..() - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= max_brain_damage) - to_chat(user, "You forget how to use \the [src].") - return - - var/t1 = text("Access control
    \n") - t1 += "
    " - t1 += " Unrestricted Access Settings
    " - - var/list/Directions = list("North","South",,"East",,,,"West") - for(var/direction in cardinal) - if (unres_direction && unres_direction == direction) - t1 += "[Directions[direction]]
    " - else - t1 += "[Directions[direction]]
    " - - t1 += "
    " - t1 += "Access requirement is set to " - t1 += one_access ? "ONE
    " : "ALL
    " - - t1 += conf_access == null ? "All
    " : "All
    " - - var/list/accesses = get_all_accesses() - for(var/acc in accesses) - var/aname = get_access_desc(acc) - - if(!conf_access || !conf_access.len || !(acc in conf_access)) - t1 += "[aname]
    " - else if(one_access) - t1 += "[aname]
    " - else - t1 += "[aname]
    " - - t1 += "

    Close

    \n" - - var/datum/browser/popup = new(user, "airlock_electronics", name, 400, 400) - popup.set_content(t1) - popup.open(0) - onclose(user, "airlock") - -/obj/item/airlock_electronics/Topic(href, href_list) - ..() - - if(usr.incapacitated() || (!ishuman(usr) && !isrobot(usr))) - return 1 - - if(href_list["close"]) - usr << browse(null, "window=airlock_electronics") - return - - if(href_list["one_access"]) - one_access = !one_access - - if(href_list["access"]) - toggle_access(href_list["access"]) - - if(href_list["unres_direction"]) - unres_direction = text2num(href_list["unres_direction"]) - if (unres_sides == unres_direction) - unres_sides = 0 - unres_direction = null - else - unres_sides = unres_direction - - attack_self(usr) - -/obj/item/airlock_electronics/proc/toggle_access(access) - if(access == "all") - conf_access = null - else - var/req = text2num(access) - - if(conf_access == null) - conf_access = list() - - if(!(req in conf_access)) - conf_access += req - else - conf_access -= req - if(!conf_access.len) - conf_access = null +/obj/item/airlock_electronics + name = "airlock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + req_access = list(ACCESS_ENGINE) + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + var/list/conf_access = null + var/one_access = 0 //if set to 1, door would receive req_one_access instead of req_access + var/const/max_brain_damage = 60 // Maximum brain damage a mob can have until it can't use the electronics + var/unres_sides = 0 + var/unres_direction = null + +/obj/item/airlock_electronics/attack_self(mob/user) + if(!ishuman(user) && !isrobot(user)) + return ..() + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= max_brain_damage) + to_chat(user, "You forget how to use \the [src].") + return + + var/t1 = text("Access control
    \n") + t1 += "
    " + t1 += " Unrestricted Access Settings
    " + + var/list/Directions = list("North","South",,"East",,,,"West") + for(var/direction in GLOB.cardinal) + if (unres_direction && unres_direction == direction) + t1 += "[Directions[direction]]
    " + else + t1 += "[Directions[direction]]
    " + + t1 += "
    " + t1 += "Access requirement is set to " + t1 += one_access ? "ONE
    " : "ALL
    " + + t1 += conf_access == null ? "All
    " : "All
    " + + var/list/accesses = get_all_accesses() + for(var/acc in accesses) + var/aname = get_access_desc(acc) + + if(!conf_access || !conf_access.len || !(acc in conf_access)) + t1 += "[aname]
    " + else if(one_access) + t1 += "[aname]
    " + else + t1 += "[aname]
    " + + t1 += "

    Close

    \n" + + var/datum/browser/popup = new(user, "airlock_electronics", name, 400, 400) + popup.set_content(t1) + popup.open(0) + onclose(user, "airlock") + +/obj/item/airlock_electronics/Topic(href, href_list) + ..() + + if(usr.incapacitated() || (!ishuman(usr) && !isrobot(usr))) + return 1 + + if(href_list["close"]) + usr << browse(null, "window=airlock_electronics") + return + + if(href_list["one_access"]) + one_access = !one_access + + if(href_list["access"]) + toggle_access(href_list["access"]) + + if(href_list["unres_direction"]) + unres_direction = text2num(href_list["unres_direction"]) + if (unres_sides == unres_direction) + unres_sides = 0 + unres_direction = null + else + unres_sides = unres_direction + + attack_self(usr) + +/obj/item/airlock_electronics/proc/toggle_access(access) + if(access == "all") + conf_access = null + else + var/req = text2num(access) + + if(conf_access == null) + conf_access = list() + + if(!(req in conf_access)) + conf_access += req + else + conf_access -= req + if(!conf_access.len) + conf_access = null diff --git a/code/game/machinery/doors/alarmlock.dm b/code/game/machinery/doors/alarmlock.dm index d3bf12f72f09..d5c78aa2325e 100644 --- a/code/game/machinery/doors/alarmlock.dm +++ b/code/game/machinery/doors/alarmlock.dm @@ -1,44 +1,44 @@ -/obj/machinery/door/airlock/alarmlock - name = "glass alarm airlock" - icon = 'icons/obj/doors/airlocks/station2/glass.dmi' - overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' - opacity = 0 - glass = 1 - autoclose = 0 - var/datum/radio_frequency/air_connection - var/air_frequency = ATMOS_FIRE_FREQ - -/obj/machinery/door/airlock/alarmlock/New() - ..() - air_connection = new - -/obj/machinery/door/airlock/alarmlock/Destroy() - if(SSradio) - SSradio.remove_object(src,air_frequency) - air_connection = null - return ..() - -/obj/machinery/door/airlock/alarmlock/Initialize() - ..() - SSradio.remove_object(src, air_frequency) - air_connection = SSradio.add_object(src, air_frequency, RADIO_TO_AIRALARM) - open() - -/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) - ..() - if(stat & (NOPOWER|BROKEN)) - return - - var/alarm_area = signal.data["zone"] - var/alert = signal.data["alert"] - - var/area/our_area = get_area(src) - - if(alarm_area == our_area.name) - switch(alert) - if("severe") - autoclose = 1 - close() - if("minor", "clear") - autoclose = 0 - open() +/obj/machinery/door/airlock/alarmlock + name = "glass alarm airlock" + icon = 'icons/obj/doors/airlocks/station2/glass.dmi' + overlays_file = 'icons/obj/doors/airlocks/station2/overlays.dmi' + opacity = 0 + glass = 1 + autoclose = 0 + var/datum/radio_frequency/air_connection + var/air_frequency = ATMOS_FIRE_FREQ + +/obj/machinery/door/airlock/alarmlock/New() + ..() + air_connection = new + +/obj/machinery/door/airlock/alarmlock/Destroy() + if(SSradio) + SSradio.remove_object(src,air_frequency) + air_connection = null + return ..() + +/obj/machinery/door/airlock/alarmlock/Initialize() + ..() + SSradio.remove_object(src, air_frequency) + air_connection = SSradio.add_object(src, air_frequency, RADIO_TO_AIRALARM) + open() + +/obj/machinery/door/airlock/alarmlock/receive_signal(datum/signal/signal) + ..() + if(stat & (NOPOWER|BROKEN)) + return + + var/alarm_area = signal.data["zone"] + var/alert = signal.data["alert"] + + var/area/our_area = get_area(src) + + if(alarm_area == our_area.name) + switch(alert) + if("severe") + autoclose = 1 + close() + if("minor", "clear") + autoclose = 0 + open() diff --git a/code/game/machinery/doors/brigdoors.dm b/code/game/machinery/doors/brigdoors.dm index 9b1481fe7396..29549a056306 100644 --- a/code/game/machinery/doors/brigdoors.dm +++ b/code/game/machinery/doors/brigdoors.dm @@ -1,504 +1,504 @@ -#define CHARS_PER_LINE 5 -#define FONT_SIZE "5pt" -#define FONT_COLOR "#09f" -#define FONT_STYLE "Small Fonts" - -/////////////////////////////////////////////////////////////////////////////////////////////// -// Brig Door control displays. -// Description: This is a controls the timer for the brig doors, displays the timer on itself and -// has a popup window when used, allowing to set the timer. -// Code Notes: Combination of old brigdoor.dm code from rev4407 and the status_display.dm code -// Date: 01/September/2010 -// Programmer: Veryinky -///////////////////////////////////////////////////////////////////////////////////////////////// -/obj/machinery/door_timer - name = "door timer" - icon = 'icons/obj/status_display.dmi' - icon_state = "frame" - desc = "A remote control for a door." - req_access = list(ACCESS_BRIG) - anchored = 1 // can't pick it up - density = 0 // can walk through it. - var/id = null // id of door it controls. - var/releasetime = 0 // when world.timeofday reaches it - release the prisoner - var/timing = 0 // boolean, true/1 timer is on, false/0 means it's not timing - var/picture_state // icon_state of alert picture, if not displaying text/numbers - var/list/obj/machinery/targets = list() - var/timetoset = 0 // Used to set releasetime upon starting the timer - var/obj/item/radio/Radio - var/printed = 0 - var/datum/data/record/prisoner - maptext_height = 26 - maptext_width = 32 - maptext_y = -1 - var/occupant = "None" - var/crimes = "None" - var/time = 0 - var/officer = "None" - -/obj/machinery/door_timer/New() - GLOB.celltimers_list += src - return ..() - -/obj/machinery/door_timer/Destroy() - GLOB.celltimers_list -= src - return ..() - -/obj/machinery/door_timer/proc/print_report() - var/logname = input(usr, "Name of the guilty?","[id] log name") - var/logcharges = stripped_multiline_input(usr, "What have they been charged with?","[id] log charges") - - if(!logname || !logcharges) - return 0 - occupant = logname - crimes = logcharges - time = timetoset - officer = usr.name - - for(var/obj/machinery/computer/prisoner/C in GLOB.prisoncomputer_list) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "[id] log - [logname] [station_time_timestamp()]" - P.info = "
    [id] - Brig record



    " - P.info += {"
    [station_name()] - Security Department

    -
    Admission data:

    - Log generated at: [station_time_timestamp()]
    - Detainee: [logname]
    - Duration: [seconds_to_time(timetoset / 10)]
    - Charge(s): [logcharges]
    - Arresting Officer: [usr.name]


    - This log file was generated automatically upon activation of a cell timer."} - - playsound(C.loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) - GLOB.cell_logs += P - - var/datum/data/record/G = find_record("name", logname, data_core.general) - var/prisoner_drank = "unknown" - var/prisoner_trank = "unknown" - if(G) - if(G.fields["rank"]) - prisoner_drank = G.fields["rank"] - if(G.fields["real_rank"]) // Ignore alt job titles - necessary for lookups - prisoner_trank = G.fields["real_rank"] - - var/datum/data/record/R = find_security_record("name", logname) - - var/announcetext = "Detainee [logname] ([prisoner_drank]) has been incarcerated for [seconds_to_time(timetoset / 10)] for the charges of, '[logcharges]'. \ - Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]" - Radio.autosay(announcetext, name, "Security", list(z)) - - if(prisoner_trank != "unknown") - notify_dept_head(prisoner_trank, announcetext) - - if(R) - prisoner = R - R.fields["criminal"] = "Incarcerated" - var/mob/living/carbon/human/M = usr - var/rank = "UNKNOWN RANK" - if(istype(M) && M.wear_id) - var/obj/item/card/id/I = M.wear_id - rank = I.assignment - if(!R.fields["comments"] || !islist(R.fields["comments"])) //copied from security computer code because apparently these need to be initialized - R.fields["comments"] = list() - R.fields["comments"] += "Autogenerated by [name] on [current_date_string] [station_time_timestamp()]
    Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]." - update_all_mob_security_hud() - return 1 - - -/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext) - if(!jobtitle || !antext) - return - if(jobtitle == "Civilian") - // Don't notify the HoP about greytiding civilians - return - var/datum/job/brigged_job = SSjobs.GetJob(jobtitle) - if(!brigged_job) - return - if(!brigged_job.department_head[1]) - return - var/boss_title = brigged_job.department_head[1] - - var/obj/item/pda/target_pda - for(var/obj/item/pda/check_pda in PDAs) - if(check_pda.ownrank == boss_title) - target_pda = check_pda - if(!target_pda) - return - var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) - if(PM && PM.can_receive()) - PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)") - - -/obj/machinery/door_timer/Initialize() - ..() - - Radio = new /obj/item/radio(src) - Radio.listening = 0 - Radio.config(list("Security" = 0)) - Radio.follow_target = src - - pixel_x = ((dir & 3)? (0) : (dir == 4 ? 32 : -32)) - pixel_y = ((dir & 3)? (dir ==1 ? 32 : -32) : (0)) - - spawn(20) - for(var/obj/machinery/door/window/brigdoor/M in GLOB.airlocks) - if(M.id == id) - targets += M - - for(var/obj/machinery/flasher/F in GLOB.machines) - if(F.id == id) - targets += F - - for(var/obj/structure/closet/secure_closet/brig/C in world) - if(C.id == id) - targets += C - - for(var/obj/machinery/treadmill_monitor/T in GLOB.machines) - if(T.id == id) - targets += T - - if(targets.len==0) - stat |= BROKEN - update_icon() - -/obj/machinery/door_timer/Destroy() - QDEL_NULL(Radio) - targets.Cut() - prisoner = null - return ..() - -//Main door timer loop, if it's timing and time is >0 reduce time by 1. -// if it's less than 0, open door, reset timer -// update the door_timer window and the icon -/obj/machinery/door_timer/process() - if(stat & (NOPOWER|BROKEN)) - return - if(timing) - if(timeleft() <= 0) - Radio.autosay("Timer has expired. Releasing prisoner.", name, "Security", list(z)) - occupant = "None" - timer_end() // open doors, reset timer, clear status screen - timing = 0 - . = PROCESS_KILL - - updateUsrDialog() - update_icon() - else - timer_end() - return PROCESS_KILL - -// has the door power situation changed, if so update icon. -/obj/machinery/door_timer/power_change() - ..() - update_icon() - - -// open/closedoor checks if door_timer has power, if so it checks if the -// linked door is open/closed (by density) then opens it/closes it. - -// Closes and locks doors, power check -/obj/machinery/door_timer/proc/timer_start() - - if(stat & (NOPOWER|BROKEN)) - return 0 - - if(!printed) - if(!print_report()) - timing = 0 - return 0 - - // Set releasetime - releasetime = world.timeofday + timetoset - START_PROCESSING(SSmachines, src) - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(door.density) - continue - spawn(0) - door.close() - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) - continue - if(C.opened && !C.close()) - continue - C.locked = 1 - C.icon_state = C.icon_locked - - for(var/obj/machinery/treadmill_monitor/T in targets) - T.total_joules = 0 - T.on = 1 - - return 1 - - -// Opens and unlocks doors, power check -/obj/machinery/door_timer/proc/timer_end() - if(stat & (NOPOWER|BROKEN)) - return 0 - - // Reset vars - occupant = "None" - crimes = "None" - time = 0 - officer = "None" - releasetime = 0 - printed = 0 - if(prisoner) - prisoner.fields["criminal"] = "Released" - update_all_mob_security_hud() - prisoner = null - - for(var/obj/machinery/door/window/brigdoor/door in targets) - if(!door.density) - continue - spawn(0) - door.open() - - for(var/obj/structure/closet/secure_closet/brig/C in targets) - if(C.broken) - continue - if(C.opened) - continue - C.locked = 0 - C.icon_state = C.icon_closed - - for(var/obj/machinery/treadmill_monitor/T in targets) - if(!T.stat) - T.redeem() - T.on = 0 - - return 1 - - -// Check for releasetime timeleft -/obj/machinery/door_timer/proc/timeleft() - var/time = releasetime - world.timeofday - if(time > MIDNIGHT_ROLLOVER / 2) - time -= MIDNIGHT_ROLLOVER - if(time < 0) - return 0 - return time / 10 - -// Set timetoset -/obj/machinery/door_timer/proc/timeset(seconds) - timetoset = seconds * 10 - - if(timetoset <= 0) - timetoset = 0 - - return - -//Allows AIs to use door_timer, see human attack_hand function below -/obj/machinery/door_timer/attack_ai(mob/user) - interact(user) - -/obj/machinery/door_timer/attack_ghost(mob/user) - interact(user) - -//Allows humans to use door_timer -//Opens dialog window when someone clicks on door timer -// Allows altering timer and the timing boolean. -// Flasher activation limited to 150 seconds -/obj/machinery/door_timer/attack_hand(mob/user) - if(..()) - return - interact(user) - -/obj/machinery/door_timer/interact(mob/user) - // Used for the 'time left' display - var/second = round(timeleft() % 60) - var/minute = round((timeleft() - second) / 60) - - // Used for 'set timer' - var/setsecond = round((timetoset / 10) % 60) - var/setminute = round(((timetoset / 10) - setsecond) / 60) - - user.set_machine(src) - - // dat - var/dat = "
    Timer System:" - dat += " Door [id] controls
    " - - // Start/Stop timer - if(timing) - dat += "Stop Timer and open door
    " - else - dat += "Activate Timer and close door
    " - - // Time Left display (uses releasetime) - dat += "Time Left: [(minute ? text("[minute]:") : null)][second]
    " - dat += "
    " - - // Set Timer display (uses timetoset) - if(timing) - dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] Set
    " - else - dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond]
    " - - // Controls - dat += "Input Time" - - // Mounted flash controls - for(var/obj/machinery/flasher/F in targets) - if(F.last_flash && (F.last_flash + 150) > world.time) - dat += "
    Flash Charging" - else - dat += "
    Activate Flash" - - dat += "

    Close" - - var/datum/browser/popup = new(user, "door_timer", name, 400, 500) - popup.set_content(dat) - popup.open() - - -//Function for using door_timer dialog input, checks if user has permission -// href_list to -// "timing" turns on timer -// "tp" value to modify timer -// "fc" activates flasher -// "change" resets the timer to the timetoset amount while the timer is counting down -// Also updates dialog window and timer icon -/obj/machinery/door_timer/Topic(href, href_list) - if(..()) - return 1 - - if(!allowed(usr) && !usr.can_admin_interact()) - return 1 - - usr.set_machine(src) - - if(href_list["timing"]) - timing = text2num(href_list["timing"]) - - if(timing) - timer_start() - else - timer_end() - if(!isobserver(usr)) //spooky admin ghosts are in your brig, releasing your prisoners - Radio.autosay("Timer stopped manually by [usr.name].", name, "Security", list(z)) - - else - if(href_list["settime"]) - var/time = min(max(round(return_time_input(usr)), 0), 3600) - timeset(time) - - if(href_list["fc"]) - for(var/obj/machinery/flasher/F in targets) - F.flash() - - if(href_list["change"]) - printed = 1 - timer_start() - - add_fingerprint(usr) - updateUsrDialog() - update_icon() - - -//icon update function -// if NOPOWER, display blank -// if BROKEN, display blue screen of death icon AI uses -// if timing=true, run update display function -/obj/machinery/door_timer/update_icon() - if(stat & (NOPOWER)) - icon_state = "frame" - return - if(stat & (BROKEN)) - set_picture("ai_bsod") - return - if(timing) - var/disp1 = id - var/timeleft = timeleft() - var/disp2 = "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" - if(length(disp2) > CHARS_PER_LINE) - disp2 = "Error" - update_display(disp1, disp2) - else - if(maptext) maptext = "" - - -// Adds an icon in case the screen is broken/off, stolen from status_display.dm -/obj/machinery/door_timer/proc/set_picture(state) - picture_state = state - overlays.Cut() - overlays += image('icons/obj/status_display.dmi', icon_state=picture_state) - -/obj/machinery/door_timer/proc/return_time_input() - var/mins = input(usr, "Minutes", "Enter number of minutes", 0) as num - var/seconds = input(usr, "Seconds", "Enter number of seconds", 0) as num - var/totaltime = (seconds + (mins * 60)) - return totaltime - -//Checks to see if there's 1 line or 2, adds text-icons-numbers/letters over display -// Stolen from status_display -/obj/machinery/door_timer/proc/update_display(line1, line2) - line1 = uppertext(line1) - line2 = uppertext(line2) - var/new_text = {"
    [line1]
    [line2]
    "} - if(maptext != new_text) - maptext = new_text - - -//Actual string input to icon display for loop, with 5 pixel x offsets for each letter. -//Stolen from status_display -/obj/machinery/door_timer/proc/texticon(tn, px = 0, py = 0) - var/image/I = image('icons/obj/status_display.dmi', "blank") - var/len = length(tn) - - for(var/d = 1 to len) - var/char = copytext(tn, len-d+1, len-d+2) - if(char == " ") - continue - var/image/ID = image('icons/obj/status_display.dmi', icon_state=char) - ID.pixel_x = -(d-1)*5 + px - ID.pixel_y = py - I.overlays += ID - return I - - -/obj/machinery/door_timer/cell_1 - name = "Cell 1" - id = "Cell 1" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_2 - name = "Cell 2" - id = "Cell 2" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_3 - name = "Cell 3" - id = "Cell 3" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_4 - name = "Cell 4" - id = "Cell 4" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_5 - name = "Cell 5" - id = "Cell 5" - dir = 2 - pixel_y = -32 - - -/obj/machinery/door_timer/cell_6 - name = "Cell 6" - id = "Cell 6" - dir = 4 - pixel_x = 32 - -#undef FONT_SIZE -#undef FONT_COLOR -#undef FONT_STYLE -#undef CHARS_PER_LINE +#define CHARS_PER_LINE 5 +#define FONT_SIZE "5pt" +#define FONT_COLOR "#09f" +#define FONT_STYLE "Small Fonts" + +/////////////////////////////////////////////////////////////////////////////////////////////// +// Brig Door control displays. +// Description: This is a controls the timer for the brig doors, displays the timer on itself and +// has a popup window when used, allowing to set the timer. +// Code Notes: Combination of old brigdoor.dm code from rev4407 and the status_display.dm code +// Date: 01/September/2010 +// Programmer: Veryinky +///////////////////////////////////////////////////////////////////////////////////////////////// +/obj/machinery/door_timer + name = "door timer" + icon = 'icons/obj/status_display.dmi' + icon_state = "frame" + desc = "A remote control for a door." + req_access = list(ACCESS_BRIG) + anchored = 1 // can't pick it up + density = 0 // can walk through it. + var/id = null // id of door it controls. + var/releasetime = 0 // when world.timeofday reaches it - release the prisoner + var/timing = 0 // boolean, true/1 timer is on, false/0 means it's not timing + var/picture_state // icon_state of alert picture, if not displaying text/numbers + var/list/obj/machinery/targets = list() + var/timetoset = 0 // Used to set releasetime upon starting the timer + var/obj/item/radio/Radio + var/printed = 0 + var/datum/data/record/prisoner + maptext_height = 26 + maptext_width = 32 + maptext_y = -1 + var/occupant = "None" + var/crimes = "None" + var/time = 0 + var/officer = "None" + +/obj/machinery/door_timer/New() + GLOB.celltimers_list += src + return ..() + +/obj/machinery/door_timer/Destroy() + GLOB.celltimers_list -= src + return ..() + +/obj/machinery/door_timer/proc/print_report() + var/logname = input(usr, "Name of the guilty?","[id] log name") + var/logcharges = stripped_multiline_input(usr, "What have they been charged with?","[id] log charges") + + if(!logname || !logcharges) + return 0 + occupant = logname + crimes = logcharges + time = timetoset + officer = usr.name + + for(var/obj/machinery/computer/prisoner/C in GLOB.prisoncomputer_list) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "[id] log - [logname] [station_time_timestamp()]" + P.info = "
    [id] - Brig record



    " + P.info += {"
    [station_name()] - Security Department

    +
    Admission data:

    + Log generated at: [station_time_timestamp()]
    + Detainee: [logname]
    + Duration: [seconds_to_time(timetoset / 10)]
    + Charge(s): [logcharges]
    + Arresting Officer: [usr.name]


    + This log file was generated automatically upon activation of a cell timer."} + + playsound(C.loc, "sound/goonstation/machines/printer_dotmatrix.ogg", 50, 1) + GLOB.cell_logs += P + + var/datum/data/record/G = find_record("name", logname, GLOB.data_core.general) + var/prisoner_drank = "unknown" + var/prisoner_trank = "unknown" + if(G) + if(G.fields["rank"]) + prisoner_drank = G.fields["rank"] + if(G.fields["real_rank"]) // Ignore alt job titles - necessary for lookups + prisoner_trank = G.fields["real_rank"] + + var/datum/data/record/R = find_security_record("name", logname) + + var/announcetext = "Detainee [logname] ([prisoner_drank]) has been incarcerated for [seconds_to_time(timetoset / 10)] for the charges of, '[logcharges]'. \ + Arresting Officer: [usr.name].[R ? "" : " Detainee record not found, manual record update required."]" + Radio.autosay(announcetext, name, "Security", list(z)) + + if(prisoner_trank != "unknown") + notify_dept_head(prisoner_trank, announcetext) + + if(R) + prisoner = R + R.fields["criminal"] = "Incarcerated" + var/mob/living/carbon/human/M = usr + var/rank = "UNKNOWN RANK" + if(istype(M) && M.wear_id) + var/obj/item/card/id/I = M.wear_id + rank = I.assignment + if(!R.fields["comments"] || !islist(R.fields["comments"])) //copied from security computer code because apparently these need to be initialized + R.fields["comments"] = list() + R.fields["comments"] += "Autogenerated by [name] on [GLOB.current_date_string] [station_time_timestamp()]
    Sentenced to [timetoset/10] seconds for the charges of \"[logcharges]\" by [rank] [usr.name]." + update_all_mob_security_hud() + return 1 + + +/obj/machinery/door_timer/proc/notify_dept_head(jobtitle, antext) + if(!jobtitle || !antext) + return + if(jobtitle == "Civilian") + // Don't notify the HoP about greytiding civilians + return + var/datum/job/brigged_job = SSjobs.GetJob(jobtitle) + if(!brigged_job) + return + if(!brigged_job.department_head[1]) + return + var/boss_title = brigged_job.department_head[1] + + var/obj/item/pda/target_pda + for(var/obj/item/pda/check_pda in GLOB.PDAs) + if(check_pda.ownrank == boss_title) + target_pda = check_pda + if(!target_pda) + return + var/datum/data/pda/app/messenger/PM = target_pda.find_program(/datum/data/pda/app/messenger) + if(PM && PM.can_receive()) + PM.notify("Message from Brig Timer (Automated), \"[antext]\" (Unable to Reply)") + + +/obj/machinery/door_timer/Initialize() + ..() + + Radio = new /obj/item/radio(src) + Radio.listening = 0 + Radio.config(list("Security" = 0)) + Radio.follow_target = src + + pixel_x = ((dir & 3)? (0) : (dir == 4 ? 32 : -32)) + pixel_y = ((dir & 3)? (dir ==1 ? 32 : -32) : (0)) + + spawn(20) + for(var/obj/machinery/door/window/brigdoor/M in GLOB.airlocks) + if(M.id == id) + targets += M + + for(var/obj/machinery/flasher/F in GLOB.machines) + if(F.id == id) + targets += F + + for(var/obj/structure/closet/secure_closet/brig/C in world) + if(C.id == id) + targets += C + + for(var/obj/machinery/treadmill_monitor/T in GLOB.machines) + if(T.id == id) + targets += T + + if(targets.len==0) + stat |= BROKEN + update_icon() + +/obj/machinery/door_timer/Destroy() + QDEL_NULL(Radio) + targets.Cut() + prisoner = null + return ..() + +//Main door timer loop, if it's timing and time is >0 reduce time by 1. +// if it's less than 0, open door, reset timer +// update the door_timer window and the icon +/obj/machinery/door_timer/process() + if(stat & (NOPOWER|BROKEN)) + return + if(timing) + if(timeleft() <= 0) + Radio.autosay("Timer has expired. Releasing prisoner.", name, "Security", list(z)) + occupant = "None" + timer_end() // open doors, reset timer, clear status screen + timing = 0 + . = PROCESS_KILL + + updateUsrDialog() + update_icon() + else + timer_end() + return PROCESS_KILL + +// has the door power situation changed, if so update icon. +/obj/machinery/door_timer/power_change() + ..() + update_icon() + + +// open/closedoor checks if door_timer has power, if so it checks if the +// linked door is open/closed (by density) then opens it/closes it. + +// Closes and locks doors, power check +/obj/machinery/door_timer/proc/timer_start() + + if(stat & (NOPOWER|BROKEN)) + return 0 + + if(!printed) + if(!print_report()) + timing = 0 + return 0 + + // Set releasetime + releasetime = world.timeofday + timetoset + START_PROCESSING(SSmachines, src) + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(door.density) + continue + spawn(0) + door.close() + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) + continue + if(C.opened && !C.close()) + continue + C.locked = 1 + C.icon_state = C.icon_locked + + for(var/obj/machinery/treadmill_monitor/T in targets) + T.total_joules = 0 + T.on = 1 + + return 1 + + +// Opens and unlocks doors, power check +/obj/machinery/door_timer/proc/timer_end() + if(stat & (NOPOWER|BROKEN)) + return 0 + + // Reset vars + occupant = "None" + crimes = "None" + time = 0 + officer = "None" + releasetime = 0 + printed = 0 + if(prisoner) + prisoner.fields["criminal"] = "Released" + update_all_mob_security_hud() + prisoner = null + + for(var/obj/machinery/door/window/brigdoor/door in targets) + if(!door.density) + continue + spawn(0) + door.open() + + for(var/obj/structure/closet/secure_closet/brig/C in targets) + if(C.broken) + continue + if(C.opened) + continue + C.locked = 0 + C.icon_state = C.icon_closed + + for(var/obj/machinery/treadmill_monitor/T in targets) + if(!T.stat) + T.redeem() + T.on = 0 + + return 1 + + +// Check for releasetime timeleft +/obj/machinery/door_timer/proc/timeleft() + var/time = releasetime - world.timeofday + if(time > MIDNIGHT_ROLLOVER / 2) + time -= MIDNIGHT_ROLLOVER + if(time < 0) + return 0 + return time / 10 + +// Set timetoset +/obj/machinery/door_timer/proc/timeset(seconds) + timetoset = seconds * 10 + + if(timetoset <= 0) + timetoset = 0 + + return + +//Allows AIs to use door_timer, see human attack_hand function below +/obj/machinery/door_timer/attack_ai(mob/user) + interact(user) + +/obj/machinery/door_timer/attack_ghost(mob/user) + interact(user) + +//Allows humans to use door_timer +//Opens dialog window when someone clicks on door timer +// Allows altering timer and the timing boolean. +// Flasher activation limited to 150 seconds +/obj/machinery/door_timer/attack_hand(mob/user) + if(..()) + return + interact(user) + +/obj/machinery/door_timer/interact(mob/user) + // Used for the 'time left' display + var/second = round(timeleft() % 60) + var/minute = round((timeleft() - second) / 60) + + // Used for 'set timer' + var/setsecond = round((timetoset / 10) % 60) + var/setminute = round(((timetoset / 10) - setsecond) / 60) + + user.set_machine(src) + + // dat + var/dat = "
    Timer System:" + dat += " Door [id] controls
    " + + // Start/Stop timer + if(timing) + dat += "Stop Timer and open door
    " + else + dat += "Activate Timer and close door
    " + + // Time Left display (uses releasetime) + dat += "Time Left: [(minute ? text("[minute]:") : null)][second]
    " + dat += "
    " + + // Set Timer display (uses timetoset) + if(timing) + dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond] Set
    " + else + dat += "Set Timer: [(setminute ? text("[setminute]:") : null)][setsecond]
    " + + // Controls + dat += "Input Time" + + // Mounted flash controls + for(var/obj/machinery/flasher/F in targets) + if(F.last_flash && (F.last_flash + 150) > world.time) + dat += "
    Flash Charging" + else + dat += "
    Activate Flash" + + dat += "

    Close" + + var/datum/browser/popup = new(user, "door_timer", name, 400, 500) + popup.set_content(dat) + popup.open() + + +//Function for using door_timer dialog input, checks if user has permission +// href_list to +// "timing" turns on timer +// "tp" value to modify timer +// "fc" activates flasher +// "change" resets the timer to the timetoset amount while the timer is counting down +// Also updates dialog window and timer icon +/obj/machinery/door_timer/Topic(href, href_list) + if(..()) + return 1 + + if(!allowed(usr) && !usr.can_admin_interact()) + return 1 + + usr.set_machine(src) + + if(href_list["timing"]) + timing = text2num(href_list["timing"]) + + if(timing) + timer_start() + else + timer_end() + if(!isobserver(usr)) //spooky admin ghosts are in your brig, releasing your prisoners + Radio.autosay("Timer stopped manually by [usr.name].", name, "Security", list(z)) + + else + if(href_list["settime"]) + var/time = min(max(round(return_time_input(usr)), 0), 3600) + timeset(time) + + if(href_list["fc"]) + for(var/obj/machinery/flasher/F in targets) + F.flash() + + if(href_list["change"]) + printed = 1 + timer_start() + + add_fingerprint(usr) + updateUsrDialog() + update_icon() + + +//icon update function +// if NOPOWER, display blank +// if BROKEN, display blue screen of death icon AI uses +// if timing=true, run update display function +/obj/machinery/door_timer/update_icon() + if(stat & (NOPOWER)) + icon_state = "frame" + return + if(stat & (BROKEN)) + set_picture("ai_bsod") + return + if(timing) + var/disp1 = id + var/timeleft = timeleft() + var/disp2 = "[add_zero(num2text((timeleft / 60) % 60),2)]:[add_zero(num2text(timeleft % 60), 2)]" + if(length(disp2) > CHARS_PER_LINE) + disp2 = "Error" + update_display(disp1, disp2) + else + if(maptext) maptext = "" + + +// Adds an icon in case the screen is broken/off, stolen from status_display.dm +/obj/machinery/door_timer/proc/set_picture(state) + picture_state = state + overlays.Cut() + overlays += image('icons/obj/status_display.dmi', icon_state=picture_state) + +/obj/machinery/door_timer/proc/return_time_input() + var/mins = input(usr, "Minutes", "Enter number of minutes", 0) as num + var/seconds = input(usr, "Seconds", "Enter number of seconds", 0) as num + var/totaltime = (seconds + (mins * 60)) + return totaltime + +//Checks to see if there's 1 line or 2, adds text-icons-numbers/letters over display +// Stolen from status_display +/obj/machinery/door_timer/proc/update_display(line1, line2) + line1 = uppertext(line1) + line2 = uppertext(line2) + var/new_text = {"
    [line1]
    [line2]
    "} + if(maptext != new_text) + maptext = new_text + + +//Actual string input to icon display for loop, with 5 pixel x offsets for each letter. +//Stolen from status_display +/obj/machinery/door_timer/proc/texticon(tn, px = 0, py = 0) + var/image/I = image('icons/obj/status_display.dmi', "blank") + var/len = length(tn) + + for(var/d = 1 to len) + var/char = copytext(tn, len-d+1, len-d+2) + if(char == " ") + continue + var/image/ID = image('icons/obj/status_display.dmi', icon_state=char) + ID.pixel_x = -(d-1)*5 + px + ID.pixel_y = py + I.overlays += ID + return I + + +/obj/machinery/door_timer/cell_1 + name = "Cell 1" + id = "Cell 1" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_2 + name = "Cell 2" + id = "Cell 2" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_3 + name = "Cell 3" + id = "Cell 3" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_4 + name = "Cell 4" + id = "Cell 4" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_5 + name = "Cell 5" + id = "Cell 5" + dir = 2 + pixel_y = -32 + + +/obj/machinery/door_timer/cell_6 + name = "Cell 6" + id = "Cell 6" + dir = 4 + pixel_x = 32 + +#undef FONT_SIZE +#undef FONT_COLOR +#undef FONT_STYLE +#undef CHARS_PER_LINE diff --git a/code/game/machinery/doors/checkForMultipleDoors.dm b/code/game/machinery/doors/checkForMultipleDoors.dm index d1c3c2e2d6b6..d7f66f4630b6 100644 --- a/code/game/machinery/doors/checkForMultipleDoors.dm +++ b/code/game/machinery/doors/checkForMultipleDoors.dm @@ -1,16 +1,16 @@ -/obj/machinery/door/proc/checkForMultipleDoors() - if(!loc) - return 0 - for(var/obj/machinery/door/D in loc) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - return 1 - -/turf/simulated/wall/proc/checkForMultipleDoors() - if(!loc) - return 0 - for(var/obj/machinery/door/D in locate(x,y,z)) - if(!istype(D, /obj/machinery/door/window) && D.density) - return 0 - //There are no false wall checks because that would be fucking retarded - return 1 \ No newline at end of file +/obj/machinery/door/proc/checkForMultipleDoors() + if(!loc) + return 0 + for(var/obj/machinery/door/D in loc) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + return 1 + +/turf/simulated/wall/proc/checkForMultipleDoors() + if(!loc) + return 0 + for(var/obj/machinery/door/D in locate(x,y,z)) + if(!istype(D, /obj/machinery/door/window) && D.density) + return 0 + //There are no false wall checks because that would be fucking retarded + return 1 diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 14d1de97327a..fbbe0201d31d 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -1,386 +1,386 @@ -/obj/machinery/door - name = "door" - desc = "It opens and closes." - icon = 'icons/obj/doors/doorint.dmi' - icon_state = "door1" - anchored = TRUE - opacity = 1 - density = TRUE - layer = OPEN_DOOR_LAYER - power_channel = ENVIRON - max_integrity = 350 - armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) - flags = PREVENT_CLICK_UNDER - damage_deflection = 10 - var/closingLayer = CLOSED_DOOR_LAYER - var/visible = 1 - var/operating = FALSE - var/autoclose = 0 - var/safe = TRUE //whether the door detects things and mobs in its way and reopen or crushes them. - var/locked = FALSE //whether the door is bolted or not. - var/glass = FALSE - var/welded = FALSE - var/normalspeed = 1 - var/auto_close_time = 150 - var/auto_close_time_dangerous = 15 - var/assemblytype //the type of door frame to drop during deconstruction - var/datum/effect_system/spark_spread/spark_system - var/real_explosion_block //ignore this, just use explosion_block - var/heat_proof = FALSE // For rglass-windowed airlocks and firedoors - var/emergency = FALSE - var/unres_sides = 0 //Unrestricted sides. A bitflag for which direction (if any) can open the door with no access - //Multi-tile doors - var/width = 1 - -/obj/machinery/door/New() - ..() - set_init_door_layer() - update_dir() - update_freelook_sight() - GLOB.airlocks += src - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(2, 1, src) - - //doors only block while dense though so we have to use the proc - real_explosion_block = explosion_block - explosion_block = EXPLOSION_BLOCK_PROC - -/obj/machinery/door/proc/set_init_door_layer() - if(density) - layer = closingLayer - else - layer = initial(layer) - -/obj/machinery/door/setDir(newdir) - ..() - update_dir() - -/obj/machinery/door/power_change() - ..() - update_icon() - -/obj/machinery/door/proc/update_dir() - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/machinery/door/Initialize() - air_update_turf(1) - ..() - -/obj/machinery/door/Destroy() - density = 0 - air_update_turf(1) - update_freelook_sight() - GLOB.airlocks -= src - QDEL_NULL(spark_system) - return ..() - -/obj/machinery/door/Bumped(atom/AM) - if(operating || emagged) - return - if(ismob(AM)) - var/mob/B = AM - if((isrobot(B)) && B.stat) - return - if(isliving(AM)) - var/mob/living/M = AM - if(world.time - M.last_bumped <= 10) - return //Can bump-open one airlock per second. This is to prevent shock spam. - M.last_bumped = world.time - if(M.restrained() && !check_access(null)) - return - if(M.mob_size > MOB_SIZE_TINY) - bumpopen(M) - return - - if(ismecha(AM)) - var/obj/mecha/mecha = AM - if(density) - if(mecha.occupant) - if(world.time - mecha.occupant.last_bumped <= 10) - return - if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access)) - open() - else - do_animate("deny") - return - -/obj/machinery/door/Move(new_loc, new_dir) - var/turf/T = loc - . = ..() - move_update_air(T) - - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density - -/obj/machinery/door/CanAtmosPass() - return !density - -/obj/machinery/door/proc/bumpopen(mob/user) - if(operating) - return - add_fingerprint(user) - - if(density && !emagged) - if(allowed(user)) - open() - if(isbot(user)) - var/mob/living/simple_animal/bot/B = user - B.door_opened(src) - else - do_animate("deny") - -/obj/machinery/door/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/door/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/door/attack_hand(mob/user) - return try_to_activate_door(user) - -/obj/machinery/door/attack_tk(mob/user) - if(!allowed(null)) - return - ..() - -/obj/machinery/door/proc/try_to_activate_door(mob/user) - add_fingerprint(user) - if(operating || emagged) - return - if(requiresID() && (allowed(user) || user.can_advanced_admin_interact())) - if(density) - open() - else - close() - return - if(density) - do_animate("deny") - -/obj/machinery/door/allowed(mob/M) - if(emergency) - return TRUE - if(unrestricted_side(M)) - return TRUE - if(!requiresID()) - return FALSE // Intentional. machinery/door/requiresID() always == 1. airlocks, however, == 0 if ID scan is disabled. Yes, this var is poorly named. - return ..() - -/obj/machinery/door/proc/unrestricted_side(mob/M) //Allows for specific side of airlocks to be unrestrected (IE, can exit maint freely, but need access to enter) - return get_dir(src, M) & unres_sides - -/obj/machinery/door/proc/try_to_crowbar(mob/user, obj/item/I) - return - -/obj/machinery/door/attackby(obj/item/I, mob/user, params) - if(user.a_intent != INTENT_HARM && istype(I, /obj/item/twohanded/fireaxe)) - try_to_crowbar(user, I) - return 1 - else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM) - try_to_activate_door(user) - return 1 - return ..() - - -/obj/machinery/door/crowbar_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - . = TRUE - if(operating) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - try_to_crowbar(user, I) - -/obj/machinery/door/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - if(damage_amount >= 10 && prob(30)) - spark_system.start() - -/obj/machinery/door/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(glass) - playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) - else if(damage_amount) - playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/door/emag_act(mob/user) - if(density) - flick("door_spark", src) - sleep(6) - open() - emagged = 1 - return 1 - -/obj/machinery/door/emp_act(severity) - if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) - spawn(0) - open() - ..() - -/obj/machinery/door/update_icon() - if(density) - icon_state = "door1" - else - icon_state = "door0" - -/obj/machinery/door/proc/do_animate(animation) - switch(animation) - if("opening") - if(panel_open) - flick("o_doorc0", src) - else - flick("doorc0", src) - if("closing") - if(panel_open) - flick("o_doorc1", src) - else - flick("doorc1", src) - if("deny") - if(!stat) - flick("door_deny", src) - -/obj/machinery/door/proc/open() - if(!density) - return TRUE - if(operating) - return - operating = TRUE - do_animate("opening") - set_opacity(0) - sleep(5) - density = FALSE - sleep(5) - layer = initial(layer) - update_icon() - set_opacity(0) - operating = FALSE - air_update_turf(1) - update_freelook_sight() - if(autoclose) - autoclose_in(normalspeed ? auto_close_time : auto_close_time_dangerous) - return TRUE - -/obj/machinery/door/proc/close() - if(density) - return TRUE - if(operating || welded) - return - if(safe) - for(var/turf/turf in locs) - for(var/atom/movable/M in turf) - if(M.density && M != src) //something is blocking the door - if(autoclose) - autoclose_in(60) - return - - operating = TRUE - - do_animate("closing") - layer = closingLayer - sleep(5) - density = TRUE - sleep(5) - update_icon() - if(visible && !glass) - set_opacity(1) - operating = 0 - air_update_turf(1) - update_freelook_sight() - if(safe) - CheckForMobs() - else - crush() - return TRUE - -/obj/machinery/door/proc/CheckForMobs() - if(locate(/mob/living) in get_turf(src)) - sleep(1) - open() - -/obj/machinery/door/proc/crush() - for(var/mob/living/L in get_turf(src)) - L.visible_message("[src] closes on [L], crushing [L.p_them()]!", "[src] closes on you and crushes you!") - if(isalien(L)) //For xenos - L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans. - L.emote("roar") - else if(ishuman(L)) //For humans - L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) - if(L.stat == CONSCIOUS) - L.emote("scream") - L.Weaken(5) - else //for simple_animals & borgs - L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) - var/turf/location = get_turf(src) - L.add_splatter_floor(location) - for(var/obj/mecha/M in get_turf(src)) - M.take_damage(DOOR_CRUSH_DAMAGE) - -/obj/machinery/door/proc/requiresID() - return 1 - -/obj/machinery/door/proc/hasPower() - return !(stat & NOPOWER) - -/obj/machinery/door/proc/autoclose() - if(!QDELETED(src) && !density && !operating && !locked && !welded && autoclose) - close() - -/obj/machinery/door/proc/autoclose_in(wait) - addtimer(CALLBACK(src, .proc/autoclose), wait, TIMER_UNIQUE | TIMER_NO_HASH_WAIT | TIMER_OVERRIDE) - -/obj/machinery/door/proc/update_freelook_sight() - if(!glass && cameranet) - cameranet.updateVisibility(src, 0) - -/obj/machinery/door/BlockSuperconductivity() // All non-glass airlocks block heat, this is intended. - if(opacity || heat_proof) - return 1 - return 0 - -/obj/machinery/door/morgue - icon = 'icons/obj/doors/doormorgue.dmi' - -/obj/machinery/door/proc/lock() - return - -/obj/machinery/door/proc/unlock() - return - -/obj/machinery/door/proc/hostile_lockdown(mob/origin) - if(!stat) //So that only powered doors are closed. - close() //Close ALL the doors! - -/obj/machinery/door/proc/disable_lockdown() - if(!stat) //Opens only powered doors. - open() //Open everything! - -/obj/machinery/door/ex_act(severity) - //if it blows up a wall it should blow up a door - ..(severity ? max(1, severity - 1) : 0) - - -/obj/machinery/door/GetExplosionBlock() - return density ? real_explosion_block : 0 +/obj/machinery/door + name = "door" + desc = "It opens and closes." + icon = 'icons/obj/doors/doorint.dmi' + icon_state = "door1" + anchored = TRUE + opacity = 1 + density = TRUE + layer = OPEN_DOOR_LAYER + power_channel = ENVIRON + max_integrity = 350 + armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 70) + flags = PREVENT_CLICK_UNDER + damage_deflection = 10 + var/closingLayer = CLOSED_DOOR_LAYER + var/visible = 1 + var/operating = FALSE + var/autoclose = 0 + var/safe = TRUE //whether the door detects things and mobs in its way and reopen or crushes them. + var/locked = FALSE //whether the door is bolted or not. + var/glass = FALSE + var/welded = FALSE + var/normalspeed = 1 + var/auto_close_time = 150 + var/auto_close_time_dangerous = 15 + var/assemblytype //the type of door frame to drop during deconstruction + var/datum/effect_system/spark_spread/spark_system + var/real_explosion_block //ignore this, just use explosion_block + var/heat_proof = FALSE // For rglass-windowed airlocks and firedoors + var/emergency = FALSE + var/unres_sides = 0 //Unrestricted sides. A bitflag for which direction (if any) can open the door with no access + //Multi-tile doors + var/width = 1 + +/obj/machinery/door/New() + ..() + set_init_door_layer() + update_dir() + update_freelook_sight() + GLOB.airlocks += src + spark_system = new /datum/effect_system/spark_spread + spark_system.set_up(2, 1, src) + + //doors only block while dense though so we have to use the proc + real_explosion_block = explosion_block + explosion_block = EXPLOSION_BLOCK_PROC + +/obj/machinery/door/proc/set_init_door_layer() + if(density) + layer = closingLayer + else + layer = initial(layer) + +/obj/machinery/door/setDir(newdir) + ..() + update_dir() + +/obj/machinery/door/power_change() + ..() + update_icon() + +/obj/machinery/door/proc/update_dir() + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/machinery/door/Initialize() + air_update_turf(1) + ..() + +/obj/machinery/door/Destroy() + density = 0 + air_update_turf(1) + update_freelook_sight() + GLOB.airlocks -= src + QDEL_NULL(spark_system) + return ..() + +/obj/machinery/door/Bumped(atom/AM) + if(operating || emagged) + return + if(ismob(AM)) + var/mob/B = AM + if((isrobot(B)) && B.stat) + return + if(isliving(AM)) + var/mob/living/M = AM + if(world.time - M.last_bumped <= 10) + return //Can bump-open one airlock per second. This is to prevent shock spam. + M.last_bumped = world.time + if(M.restrained() && !check_access(null)) + return + if(M.mob_size > MOB_SIZE_TINY) + bumpopen(M) + return + + if(ismecha(AM)) + var/obj/mecha/mecha = AM + if(density) + if(mecha.occupant) + if(world.time - mecha.occupant.last_bumped <= 10) + return + if(mecha.occupant && allowed(mecha.occupant) || check_access_list(mecha.operation_req_access)) + open() + else + do_animate("deny") + return + +/obj/machinery/door/Move(new_loc, new_dir) + var/turf/T = loc + . = ..() + move_update_air(T) + + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density + +/obj/machinery/door/CanAtmosPass() + return !density + +/obj/machinery/door/proc/bumpopen(mob/user) + if(operating) + return + add_fingerprint(user) + + if(density && !emagged) + if(allowed(user)) + open() + if(isbot(user)) + var/mob/living/simple_animal/bot/B = user + B.door_opened(src) + else + do_animate("deny") + +/obj/machinery/door/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/door/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/door/attack_hand(mob/user) + return try_to_activate_door(user) + +/obj/machinery/door/attack_tk(mob/user) + if(!allowed(null)) + return + ..() + +/obj/machinery/door/proc/try_to_activate_door(mob/user) + add_fingerprint(user) + if(operating || emagged) + return + if(requiresID() && (allowed(user) || user.can_advanced_admin_interact())) + if(density) + open() + else + close() + return + if(density) + do_animate("deny") + +/obj/machinery/door/allowed(mob/M) + if(emergency) + return TRUE + if(unrestricted_side(M)) + return TRUE + if(!requiresID()) + return FALSE // Intentional. machinery/door/requiresID() always == 1. airlocks, however, == 0 if ID scan is disabled. Yes, this var is poorly named. + return ..() + +/obj/machinery/door/proc/unrestricted_side(mob/M) //Allows for specific side of airlocks to be unrestrected (IE, can exit maint freely, but need access to enter) + return get_dir(src, M) & unres_sides + +/obj/machinery/door/proc/try_to_crowbar(mob/user, obj/item/I) + return + +/obj/machinery/door/attackby(obj/item/I, mob/user, params) + if(user.a_intent != INTENT_HARM && istype(I, /obj/item/twohanded/fireaxe)) + try_to_crowbar(user, I) + return 1 + else if(!(I.flags & NOBLUDGEON) && user.a_intent != INTENT_HARM) + try_to_activate_door(user) + return 1 + return ..() + + +/obj/machinery/door/crowbar_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(operating) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + try_to_crowbar(user, I) + +/obj/machinery/door/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + if(damage_amount >= 10 && prob(30)) + spark_system.start() + +/obj/machinery/door/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(glass) + playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) + else if(damage_amount) + playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/door/emag_act(mob/user) + if(density) + flick("door_spark", src) + sleep(6) + open() + emagged = 1 + return 1 + +/obj/machinery/door/emp_act(severity) + if(prob(20/severity) && (istype(src,/obj/machinery/door/airlock) || istype(src,/obj/machinery/door/window)) ) + spawn(0) + open() + ..() + +/obj/machinery/door/update_icon() + if(density) + icon_state = "door1" + else + icon_state = "door0" + +/obj/machinery/door/proc/do_animate(animation) + switch(animation) + if("opening") + if(panel_open) + flick("o_doorc0", src) + else + flick("doorc0", src) + if("closing") + if(panel_open) + flick("o_doorc1", src) + else + flick("doorc1", src) + if("deny") + if(!stat) + flick("door_deny", src) + +/obj/machinery/door/proc/open() + if(!density) + return TRUE + if(operating) + return + operating = TRUE + do_animate("opening") + set_opacity(0) + sleep(5) + density = FALSE + sleep(5) + layer = initial(layer) + update_icon() + set_opacity(0) + operating = FALSE + air_update_turf(1) + update_freelook_sight() + if(autoclose) + autoclose_in(normalspeed ? auto_close_time : auto_close_time_dangerous) + return TRUE + +/obj/machinery/door/proc/close() + if(density) + return TRUE + if(operating || welded) + return + if(safe) + for(var/turf/turf in locs) + for(var/atom/movable/M in turf) + if(M.density && M != src) //something is blocking the door + if(autoclose) + autoclose_in(60) + return + + operating = TRUE + + do_animate("closing") + layer = closingLayer + sleep(5) + density = TRUE + sleep(5) + update_icon() + if(visible && !glass) + set_opacity(1) + operating = 0 + air_update_turf(1) + update_freelook_sight() + if(safe) + CheckForMobs() + else + crush() + return TRUE + +/obj/machinery/door/proc/CheckForMobs() + if(locate(/mob/living) in get_turf(src)) + sleep(1) + open() + +/obj/machinery/door/proc/crush() + for(var/mob/living/L in get_turf(src)) + L.visible_message("[src] closes on [L], crushing [L.p_them()]!", "[src] closes on you and crushes you!") + if(isalien(L)) //For xenos + L.adjustBruteLoss(DOOR_CRUSH_DAMAGE * 1.5) //Xenos go into crit after aproximately the same amount of crushes as humans. + L.emote("roar") + else if(ishuman(L)) //For humans + L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) + if(L.stat == CONSCIOUS) + L.emote("scream") + L.Weaken(5) + else //for simple_animals & borgs + L.adjustBruteLoss(DOOR_CRUSH_DAMAGE) + var/turf/location = get_turf(src) + L.add_splatter_floor(location) + for(var/obj/mecha/M in get_turf(src)) + M.take_damage(DOOR_CRUSH_DAMAGE) + +/obj/machinery/door/proc/requiresID() + return 1 + +/obj/machinery/door/proc/hasPower() + return !(stat & NOPOWER) + +/obj/machinery/door/proc/autoclose() + if(!QDELETED(src) && !density && !operating && !locked && !welded && autoclose) + close() + +/obj/machinery/door/proc/autoclose_in(wait) + addtimer(CALLBACK(src, .proc/autoclose), wait, TIMER_UNIQUE | TIMER_NO_HASH_WAIT | TIMER_OVERRIDE) + +/obj/machinery/door/proc/update_freelook_sight() + if(!glass && GLOB.cameranet) + GLOB.cameranet.updateVisibility(src, 0) + +/obj/machinery/door/BlockSuperconductivity() // All non-glass airlocks block heat, this is intended. + if(opacity || heat_proof) + return 1 + return 0 + +/obj/machinery/door/morgue + icon = 'icons/obj/doors/doormorgue.dmi' + +/obj/machinery/door/proc/lock() + return + +/obj/machinery/door/proc/unlock() + return + +/obj/machinery/door/proc/hostile_lockdown(mob/origin) + if(!stat) //So that only powered doors are closed. + close() //Close ALL the doors! + +/obj/machinery/door/proc/disable_lockdown() + if(!stat) //Opens only powered doors. + open() //Open everything! + +/obj/machinery/door/ex_act(severity) + //if it blows up a wall it should blow up a door + ..(severity ? max(1, severity - 1) : 0) + + +/obj/machinery/door/GetExplosionBlock() + return density ? real_explosion_block : 0 diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm index 4cccb6674364..448c410c1c84 100644 --- a/code/game/machinery/doors/firedoor.dm +++ b/code/game/machinery/doors/firedoor.dm @@ -1,490 +1,490 @@ -#define CONSTRUCTION_COMPLETE 0 //No construction done - functioning as normal -#define CONSTRUCTION_PANEL_OPEN 1 //Maintenance panel is open, still functioning -#define CONSTRUCTION_WIRES_EXPOSED 2 //Cover plate is removed, wires are available -#define CONSTRUCTION_GUTTED 3 //Wires are removed, circuit ready to remove -#define CONSTRUCTION_NOCIRCUIT 4 //Circuit board removed, can safely weld apart - -/obj/machinery/door/firedoor - name = "firelock" - desc = "Apply crowbar." - icon = 'icons/obj/doors/doorfireglass.dmi' - icon_state = "door_open" - opacity = 0 - density = FALSE - max_integrity = 300 - resistance_flags = FIRE_PROOF - heat_proof = TRUE - glass = TRUE - explosion_block = 1 - safe = FALSE - layer = BELOW_OPEN_DOOR_LAYER - closingLayer = CLOSED_FIREDOOR_LAYER - auto_close_time = 50 - assemblytype = /obj/structure/firelock_frame - armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 95, "acid" = 70) - var/can_force = TRUE - var/force_open_time = 300 - var/can_crush = TRUE - var/nextstate = null - var/boltslocked = TRUE - var/active_alarm = FALSE - -/obj/machinery/door/firedoor/examine(mob/user) - . = ..() - if(!density) - . += "It is open, but could be pried closed." - else if(!welded) - . += "It is closed, but could be pried open. Deconstruction would require it to be welded shut." - else if(boltslocked) - . += "It is welded shut. The floor bolts have been locked by screws." - else - . += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor." - -/obj/machinery/door/firedoor/closed - icon_state = "door_closed" - opacity = TRUE - density = TRUE - -/obj/machinery/door/firedoor/Bumped(atom/AM) - if(panel_open || operating) - return - if(!density) - return ..() - return 0 - -/obj/machinery/door/firedoor/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - latetoggle() - else - stat |= NOPOWER - update_icon() - -/obj/machinery/door/firedoor/attack_hand(mob/user) - if(operating || !density) - return - - add_fingerprint(user) - user.changeNext_move(CLICK_CD_MELEE) - - if(can_force && (!glass || user.a_intent != INTENT_HELP)) - user.visible_message("[user] begins forcing \the [src].", \ - "You begin forcing \the [src].") - if(do_after(user, force_open_time, target = src)) - user.visible_message("[user] forces \the [src].", \ - "You force \the [src].") - open() - else if(glass) - user.visible_message("[user] bangs on \the [src].", - "You bang on \the [src].") - playsound(get_turf(src), 'sound/effects/glassknock.ogg', 10, 1) - -/obj/machinery/door/firedoor/attackby(obj/item/C, mob/user, params) - add_fingerprint(user) - - if(operating) - return - return ..() - -/obj/machinery/door/firedoor/try_to_activate_door(mob/user) - return - -/obj/machinery/door/firedoor/screwdriver_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - if(operating || !welded) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.", \ - "You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.") - boltslocked = !boltslocked - -/obj/machinery/door/firedoor/wrench_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - if(operating || !welded) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(boltslocked) - to_chat(user, "There are screws locking the bolts in place!") - return - user.visible_message("[user] starts undoing [src]'s bolts...", \ - "You start unfastening [src]'s floor bolts...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume) || boltslocked) - return - user.visible_message("[user] unfastens [src]'s bolts.", \ - "You undo [src]'s floor bolts.") - deconstruct(TRUE) - -/obj/machinery/door/firedoor/welder_act(mob/user, obj/item/I) - if(!density) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_WELD_MESSAGE - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - if(!density) //In case someone opens it while it's getting welded - return - WELDER_WELD_SUCCESS_MESSAGE - welded = !welded - update_icon() - -/obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user) - if(welded || operating) - return - if(density) - open() - else - close() - -/obj/machinery/door/firedoor/attack_ai(mob/user) - forcetoggle() - -/obj/machinery/door/firedoor/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - forcetoggle(TRUE) - -/obj/machinery/door/firedoor/attack_alien(mob/user) - add_fingerprint(user) - if(welded) - to_chat(user, "[src] refuses to budge!") - return - open() - -/obj/machinery/door/firedoor/do_animate(animation) - switch(animation) - if("opening") - flick("door_opening", src) - playsound(src, 'sound/machines/airlock_ext_open.ogg', 30, 1) - if("closing") - flick("door_closing", src) - playsound(src, 'sound/machines/airlock_ext_close.ogg', 30, 1) - -/obj/machinery/door/firedoor/update_icon() - overlays.Cut() - if(active_alarm && hasPower()) - overlays += image('icons/obj/doors/doorfire.dmi', "alarmlights") - if(density) - icon_state = "door_closed" - if(welded) - overlays += "welded" - else - icon_state = "door_open" - if(welded) - overlays += "welded_open" - -/obj/machinery/door/firedoor/proc/activate_alarm() - active_alarm = TRUE - update_icon() - -/obj/machinery/door/firedoor/proc/deactivate_alarm() - active_alarm = FALSE - update_icon() - -/obj/machinery/door/firedoor/open(auto_close = TRUE) - if(welded) - return - . = ..() - latetoggle(auto_close) - - if(auto_close) - autoclose = TRUE - -/obj/machinery/door/firedoor/close() - . = ..() - latetoggle() - -/obj/machinery/door/firedoor/autoclose() - if(active_alarm) - . = ..() - -/obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE) - if(operating || !hasPower() || !nextstate) - return - switch(nextstate) - if(FD_OPEN) - nextstate = null - open(auto_close) - if(FD_CLOSED) - nextstate = null - close() - -/obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE) - if(!magic && (operating || !hasPower())) - return - if(density) - open(auto_close) - else - close() - -/obj/machinery/door/firedoor/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/obj/structure/firelock_frame/F = new assemblytype(get_turf(src)) - if(disassembled) - F.constructionStep = CONSTRUCTION_PANEL_OPEN - else - F.constructionStep = CONSTRUCTION_WIRES_EXPOSED - F.obj_integrity = F.max_integrity * 0.5 - F.update_icon() - qdel(src) - -/obj/machinery/door/firedoor/border_only - icon = 'icons/obj/doors/edge_doorfire.dmi' - flags = ON_BORDER - can_crush = FALSE - -/obj/machinery/door/firedoor/border_only/closed - icon_state = "door_closed" - opacity = TRUE - density = TRUE - -/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - return !density - else - return 1 - -/obj/machinery/door/firedoor/border_only/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 - -/obj/machinery/door/firedoor/border_only/CanAtmosPass(turf/T) - if(get_dir(loc, T) == dir) - return !density - else - return 1 - -/obj/machinery/door/firedoor/heavy - name = "heavy firelock" - icon = 'icons/obj/doors/doorfire.dmi' - glass = FALSE - opacity = 1 - explosion_block = 2 - assemblytype = /obj/structure/firelock_frame/heavy - can_force = FALSE - max_integrity = 550 - -/obj/item/firelock_electronics - name = "firelock electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "A circuit board used in construction of firelocks." - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/structure/firelock_frame - name = "firelock frame" - desc = "A partially completed firelock." - icon = 'icons/obj/doors/doorfire.dmi' - icon_state = "frame1" - anchored = FALSE - density = TRUE - var/constructionStep = CONSTRUCTION_NOCIRCUIT - var/reinforced = 0 - -/obj/structure/firelock_frame/examine(mob/user) - . = ..() - switch(constructionStep) - if(CONSTRUCTION_PANEL_OPEN) - . += "It is unbolted from the floor. A small loosely connected metal plate is covering the wires." - if(!reinforced) - . += "It could be reinforced with plasteel." - if(CONSTRUCTION_WIRES_EXPOSED) - . += "The maintenance plate has been pried away, and wires are trailing." - if(CONSTRUCTION_GUTTED) - . += "The maintenance panel is missing wires and the circuit board is loosely connected." - if(CONSTRUCTION_NOCIRCUIT) - . += "There are no firelock electronics in the frame. The frame could be cut apart." - -/obj/structure/firelock_frame/update_icon() - ..() - icon_state = "frame[constructionStep]" - -/obj/structure/firelock_frame/attackby(obj/item/C, mob/user) - switch(constructionStep) - if(CONSTRUCTION_PANEL_OPEN) - if(istype(C, /obj/item/stack/sheet/plasteel)) - var/obj/item/stack/sheet/plasteel/P = C - if(reinforced) - to_chat(user, "[src] is already reinforced.") - return - if(P.get_amount() < 2) - to_chat(user, "You need more plasteel to reinforce [src].") - return - user.visible_message("[user] begins reinforcing [src]...", \ - "You begin reinforcing [src]...") - playsound(get_turf(src), C.usesound, 50, 1) - if(do_after(user, 60 * C.toolspeed, target = src)) - if(constructionStep != CONSTRUCTION_PANEL_OPEN || reinforced || P.get_amount() < 2 || !P) - return - user.visible_message("[user] reinforces [src].", \ - "You reinforce [src].") - playsound(get_turf(src), C.usesound, 50, 1) - P.use(2) - reinforced = 1 - return - if(CONSTRUCTION_GUTTED) - if(iscoil(C)) - var/obj/item/stack/cable_coil/B = C - if(B.get_amount() < 5) - to_chat(user, "You need more wires to add wiring to [src].") - return - user.visible_message("[user] begins wiring [src]...", \ - "You begin adding wires to [src]...") - playsound(get_turf(src), B.usesound, 50, 1) - if(do_after(user, 60 * B.toolspeed, target = src)) - if(constructionStep != CONSTRUCTION_GUTTED || B.get_amount() < 5 || !B) - return - user.visible_message("[user] adds wires to [src].", \ - "You wire [src].") - playsound(get_turf(src), B.usesound, 50, 1) - B.use(5) - constructionStep = CONSTRUCTION_WIRES_EXPOSED - update_icon() - return - if(CONSTRUCTION_NOCIRCUIT) - if(istype(C, /obj/item/firelock_electronics)) - user.visible_message("[user] starts adding [C] to [src]...", \ - "You begin adding a circuit board to [src]...") - playsound(get_turf(src), C.usesound, 50, 1) - if(!do_after(user, 40 * C.toolspeed, target = src)) - return - if(constructionStep != CONSTRUCTION_NOCIRCUIT) - return - user.drop_item() - qdel(C) - user.visible_message("[user] adds a circuit to [src].", \ - "You insert and secure [C].") - playsound(get_turf(src), C.usesound, 50, 1) - constructionStep = CONSTRUCTION_GUTTED - update_icon() - return - return ..() - -/obj/structure/firelock_frame/crowbar_act(mob/user, obj/item/I) - if(!(constructionStep in list(CONSTRUCTION_WIRES_EXPOSED, CONSTRUCTION_PANEL_OPEN, CONSTRUCTION_GUTTED))) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(constructionStep == CONSTRUCTION_WIRES_EXPOSED) - user.visible_message("[user] starts prying a metal plate into [src]...", \ - "You begin prying the cover plate back onto [src]...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) - return - user.visible_message("[user] pries the metal plate into [src].", \ - "You pry [src]'s cover plate into place, hiding the wires.") - constructionStep = CONSTRUCTION_PANEL_OPEN - else if(constructionStep == CONSTRUCTION_PANEL_OPEN) - user.visible_message("[user] starts prying something out from [src]...", \ - "You begin prying out the wire cover...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_PANEL_OPEN) - return - user.visible_message("[user] pries out a metal plate from [src], exposing the wires.", \ - "You remove the cover plate from [src], exposing the wires.") - constructionStep = CONSTRUCTION_WIRES_EXPOSED - else if(constructionStep == CONSTRUCTION_GUTTED) - user.visible_message("[user] begins removing the circuit board from [src]...", \ - "You begin prying out the circuit board from [src]...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_GUTTED) - return - user.visible_message("[user] removes [src]'s circuit board.", \ - "You remove the circuit board from [src].") - new /obj/item/firelock_electronics(get_turf(src)) - constructionStep = CONSTRUCTION_NOCIRCUIT - update_icon() - -/obj/structure/firelock_frame/wirecutter_act(mob/user, obj/item/I) - if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) - return - . = TRUE - if(!I.tool_start_check(user, 0)) - return - - user.visible_message("[user] starts cutting the wires from [src]...", \ - "You begin removing [src]'s wires...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) - return - user.visible_message("[user] removes the wires from [src].", \ - "You remove the wiring from [src], exposing the circuit board.") - var/obj/item/stack/cable_coil/B = new(get_turf(src)) - B.amount = 5 - constructionStep = CONSTRUCTION_GUTTED - update_icon() - -/obj/structure/firelock_frame/wrench_act(mob/user, obj/item/I) - if(constructionStep != CONSTRUCTION_PANEL_OPEN) - return - . = TRUE - if(locate(/obj/machinery/door/firedoor) in get_turf(src)) - to_chat(user, "There's already a firelock there.") - return - if(!I.tool_start_check(user, 0)) - return - user.visible_message("[user] starts bolting down [src]...", \ - "You begin bolting [src]...") - if(!I.use_tool(src, user, 50, volume = I.tool_volume)) - return - if(locate(/obj/machinery/door/firedoor) in get_turf(src)) - return - user.visible_message("[user] finishes the firelock.", \ - "You finish the firelock.") - if(reinforced) - new /obj/machinery/door/firedoor/heavy(get_turf(src)) - else - new /obj/machinery/door/firedoor(get_turf(src)) - qdel(src) - - - - - -/obj/structure/firelock_frame/welder_act(mob/user, obj/item/I) - if(constructionStep != CONSTRUCTION_NOCIRCUIT) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(!I.use_tool(src, user, 40, amount = 1, volume = I.tool_volume)) - return - if(constructionStep != CONSTRUCTION_NOCIRCUIT) - return - WELDER_SLICING_SUCCESS_MESSAGE - new /obj/item/stack/sheet/metal(drop_location(), 3) - if(reinforced) - new /obj/item/stack/sheet/plasteel(drop_location(), 2) - qdel(src) - -/obj/structure/firelock_frame/heavy - name = "heavy firelock frame" - reinforced = 1 - -#undef CONSTRUCTION_COMPLETE -#undef CONSTRUCTION_PANEL_OPEN -#undef CONSTRUCTION_WIRES_EXPOSED -#undef CONSTRUCTION_GUTTED -#undef CONSTRUCTION_NOCIRCUIT +#define CONSTRUCTION_COMPLETE 0 //No construction done - functioning as normal +#define CONSTRUCTION_PANEL_OPEN 1 //Maintenance panel is open, still functioning +#define CONSTRUCTION_WIRES_EXPOSED 2 //Cover plate is removed, wires are available +#define CONSTRUCTION_GUTTED 3 //Wires are removed, circuit ready to remove +#define CONSTRUCTION_NOCIRCUIT 4 //Circuit board removed, can safely weld apart + +/obj/machinery/door/firedoor + name = "firelock" + desc = "Apply crowbar." + icon = 'icons/obj/doors/doorfireglass.dmi' + icon_state = "door_open" + opacity = 0 + density = FALSE + max_integrity = 300 + resistance_flags = FIRE_PROOF + heat_proof = TRUE + glass = TRUE + explosion_block = 1 + safe = FALSE + layer = BELOW_OPEN_DOOR_LAYER + closingLayer = CLOSED_FIREDOOR_LAYER + auto_close_time = 50 + assemblytype = /obj/structure/firelock_frame + armor = list("melee" = 30, "bullet" = 30, "laser" = 20, "energy" = 20, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 95, "acid" = 70) + var/can_force = TRUE + var/force_open_time = 300 + var/can_crush = TRUE + var/nextstate = null + var/boltslocked = TRUE + var/active_alarm = FALSE + +/obj/machinery/door/firedoor/examine(mob/user) + . = ..() + if(!density) + . += "It is open, but could be pried closed." + else if(!welded) + . += "It is closed, but could be pried open. Deconstruction would require it to be welded shut." + else if(boltslocked) + . += "It is welded shut. The floor bolts have been locked by screws." + else + . += "The bolt locks have been unscrewed, but the bolts themselves are still wrenched to the floor." + +/obj/machinery/door/firedoor/closed + icon_state = "door_closed" + opacity = TRUE + density = TRUE + +/obj/machinery/door/firedoor/Bumped(atom/AM) + if(panel_open || operating) + return + if(!density) + return ..() + return 0 + +/obj/machinery/door/firedoor/power_change() + if(powered(power_channel)) + stat &= ~NOPOWER + latetoggle() + else + stat |= NOPOWER + update_icon() + +/obj/machinery/door/firedoor/attack_hand(mob/user) + if(operating || !density) + return + + add_fingerprint(user) + user.changeNext_move(CLICK_CD_MELEE) + + if(can_force && (!glass || user.a_intent != INTENT_HELP)) + user.visible_message("[user] begins forcing \the [src].", \ + "You begin forcing \the [src].") + if(do_after(user, force_open_time, target = src)) + user.visible_message("[user] forces \the [src].", \ + "You force \the [src].") + open() + else if(glass) + user.visible_message("[user] bangs on \the [src].", + "You bang on \the [src].") + playsound(get_turf(src), 'sound/effects/glassknock.ogg', 10, 1) + +/obj/machinery/door/firedoor/attackby(obj/item/C, mob/user, params) + add_fingerprint(user) + + if(operating) + return + return ..() + +/obj/machinery/door/firedoor/try_to_activate_door(mob/user) + return + +/obj/machinery/door/firedoor/screwdriver_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(operating || !welded) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + user.visible_message("[user] [boltslocked ? "unlocks" : "locks"] [src]'s bolts.", \ + "You [boltslocked ? "unlock" : "lock"] [src]'s floor bolts.") + boltslocked = !boltslocked + +/obj/machinery/door/firedoor/wrench_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(operating || !welded) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(boltslocked) + to_chat(user, "There are screws locking the bolts in place!") + return + user.visible_message("[user] starts undoing [src]'s bolts...", \ + "You start unfastening [src]'s floor bolts...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume) || boltslocked) + return + user.visible_message("[user] unfastens [src]'s bolts.", \ + "You undo [src]'s floor bolts.") + deconstruct(TRUE) + +/obj/machinery/door/firedoor/welder_act(mob/user, obj/item/I) + if(!density) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(!I.use_tool(src, user, 40, volume = I.tool_volume)) + return + if(!density) //In case someone opens it while it's getting welded + return + WELDER_WELD_SUCCESS_MESSAGE + welded = !welded + update_icon() + +/obj/machinery/door/firedoor/try_to_crowbar(obj/item/I, mob/user) + if(welded || operating) + return + if(density) + open() + else + close() + +/obj/machinery/door/firedoor/attack_ai(mob/user) + forcetoggle() + +/obj/machinery/door/firedoor/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + forcetoggle(TRUE) + +/obj/machinery/door/firedoor/attack_alien(mob/user) + add_fingerprint(user) + if(welded) + to_chat(user, "[src] refuses to budge!") + return + open() + +/obj/machinery/door/firedoor/do_animate(animation) + switch(animation) + if("opening") + flick("door_opening", src) + playsound(src, 'sound/machines/airlock_ext_open.ogg', 30, 1) + if("closing") + flick("door_closing", src) + playsound(src, 'sound/machines/airlock_ext_close.ogg', 30, 1) + +/obj/machinery/door/firedoor/update_icon() + overlays.Cut() + if(active_alarm && hasPower()) + overlays += image('icons/obj/doors/doorfire.dmi', "alarmlights") + if(density) + icon_state = "door_closed" + if(welded) + overlays += "welded" + else + icon_state = "door_open" + if(welded) + overlays += "welded_open" + +/obj/machinery/door/firedoor/proc/activate_alarm() + active_alarm = TRUE + update_icon() + +/obj/machinery/door/firedoor/proc/deactivate_alarm() + active_alarm = FALSE + update_icon() + +/obj/machinery/door/firedoor/open(auto_close = TRUE) + if(welded) + return + . = ..() + latetoggle(auto_close) + + if(auto_close) + autoclose = TRUE + +/obj/machinery/door/firedoor/close() + . = ..() + latetoggle() + +/obj/machinery/door/firedoor/autoclose() + if(active_alarm) + . = ..() + +/obj/machinery/door/firedoor/proc/latetoggle(auto_close = TRUE) + if(operating || !hasPower() || !nextstate) + return + switch(nextstate) + if(FD_OPEN) + nextstate = null + open(auto_close) + if(FD_CLOSED) + nextstate = null + close() + +/obj/machinery/door/firedoor/proc/forcetoggle(magic = FALSE, auto_close = TRUE) + if(!magic && (operating || !hasPower())) + return + if(density) + open(auto_close) + else + close() + +/obj/machinery/door/firedoor/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/obj/structure/firelock_frame/F = new assemblytype(get_turf(src)) + if(disassembled) + F.constructionStep = CONSTRUCTION_PANEL_OPEN + else + F.constructionStep = CONSTRUCTION_WIRES_EXPOSED + F.obj_integrity = F.max_integrity * 0.5 + F.update_icon() + qdel(src) + +/obj/machinery/door/firedoor/border_only + icon = 'icons/obj/doors/edge_doorfire.dmi' + flags = ON_BORDER + can_crush = FALSE + +/obj/machinery/door/firedoor/border_only/closed + icon_state = "door_closed" + opacity = TRUE + density = TRUE + +/obj/machinery/door/firedoor/border_only/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + return !density + else + return 1 + +/obj/machinery/door/firedoor/border_only/CheckExit(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) + return !density + else + return 1 + +/obj/machinery/door/firedoor/border_only/CanAtmosPass(turf/T) + if(get_dir(loc, T) == dir) + return !density + else + return 1 + +/obj/machinery/door/firedoor/heavy + name = "heavy firelock" + icon = 'icons/obj/doors/doorfire.dmi' + glass = FALSE + opacity = 1 + explosion_block = 2 + assemblytype = /obj/structure/firelock_frame/heavy + can_force = FALSE + max_integrity = 550 + +/obj/item/firelock_electronics + name = "firelock electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "A circuit board used in construction of firelocks." + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/structure/firelock_frame + name = "firelock frame" + desc = "A partially completed firelock." + icon = 'icons/obj/doors/doorfire.dmi' + icon_state = "frame1" + anchored = FALSE + density = TRUE + var/constructionStep = CONSTRUCTION_NOCIRCUIT + var/reinforced = 0 + +/obj/structure/firelock_frame/examine(mob/user) + . = ..() + switch(constructionStep) + if(CONSTRUCTION_PANEL_OPEN) + . += "It is unbolted from the floor. A small loosely connected metal plate is covering the wires." + if(!reinforced) + . += "It could be reinforced with plasteel." + if(CONSTRUCTION_WIRES_EXPOSED) + . += "The maintenance plate has been pried away, and wires are trailing." + if(CONSTRUCTION_GUTTED) + . += "The maintenance panel is missing wires and the circuit board is loosely connected." + if(CONSTRUCTION_NOCIRCUIT) + . += "There are no firelock electronics in the frame. The frame could be cut apart." + +/obj/structure/firelock_frame/update_icon() + ..() + icon_state = "frame[constructionStep]" + +/obj/structure/firelock_frame/attackby(obj/item/C, mob/user) + switch(constructionStep) + if(CONSTRUCTION_PANEL_OPEN) + if(istype(C, /obj/item/stack/sheet/plasteel)) + var/obj/item/stack/sheet/plasteel/P = C + if(reinforced) + to_chat(user, "[src] is already reinforced.") + return + if(P.get_amount() < 2) + to_chat(user, "You need more plasteel to reinforce [src].") + return + user.visible_message("[user] begins reinforcing [src]...", \ + "You begin reinforcing [src]...") + playsound(get_turf(src), C.usesound, 50, 1) + if(do_after(user, 60 * C.toolspeed, target = src)) + if(constructionStep != CONSTRUCTION_PANEL_OPEN || reinforced || P.get_amount() < 2 || !P) + return + user.visible_message("[user] reinforces [src].", \ + "You reinforce [src].") + playsound(get_turf(src), C.usesound, 50, 1) + P.use(2) + reinforced = 1 + return + if(CONSTRUCTION_GUTTED) + if(iscoil(C)) + var/obj/item/stack/cable_coil/B = C + if(B.get_amount() < 5) + to_chat(user, "You need more wires to add wiring to [src].") + return + user.visible_message("[user] begins wiring [src]...", \ + "You begin adding wires to [src]...") + playsound(get_turf(src), B.usesound, 50, 1) + if(do_after(user, 60 * B.toolspeed, target = src)) + if(constructionStep != CONSTRUCTION_GUTTED || B.get_amount() < 5 || !B) + return + user.visible_message("[user] adds wires to [src].", \ + "You wire [src].") + playsound(get_turf(src), B.usesound, 50, 1) + B.use(5) + constructionStep = CONSTRUCTION_WIRES_EXPOSED + update_icon() + return + if(CONSTRUCTION_NOCIRCUIT) + if(istype(C, /obj/item/firelock_electronics)) + user.visible_message("[user] starts adding [C] to [src]...", \ + "You begin adding a circuit board to [src]...") + playsound(get_turf(src), C.usesound, 50, 1) + if(!do_after(user, 40 * C.toolspeed, target = src)) + return + if(constructionStep != CONSTRUCTION_NOCIRCUIT) + return + user.drop_item() + qdel(C) + user.visible_message("[user] adds a circuit to [src].", \ + "You insert and secure [C].") + playsound(get_turf(src), C.usesound, 50, 1) + constructionStep = CONSTRUCTION_GUTTED + update_icon() + return + return ..() + +/obj/structure/firelock_frame/crowbar_act(mob/user, obj/item/I) + if(!(constructionStep in list(CONSTRUCTION_WIRES_EXPOSED, CONSTRUCTION_PANEL_OPEN, CONSTRUCTION_GUTTED))) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(constructionStep == CONSTRUCTION_WIRES_EXPOSED) + user.visible_message("[user] starts prying a metal plate into [src]...", \ + "You begin prying the cover plate back onto [src]...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) + return + user.visible_message("[user] pries the metal plate into [src].", \ + "You pry [src]'s cover plate into place, hiding the wires.") + constructionStep = CONSTRUCTION_PANEL_OPEN + else if(constructionStep == CONSTRUCTION_PANEL_OPEN) + user.visible_message("[user] starts prying something out from [src]...", \ + "You begin prying out the wire cover...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_PANEL_OPEN) + return + user.visible_message("[user] pries out a metal plate from [src], exposing the wires.", \ + "You remove the cover plate from [src], exposing the wires.") + constructionStep = CONSTRUCTION_WIRES_EXPOSED + else if(constructionStep == CONSTRUCTION_GUTTED) + user.visible_message("[user] begins removing the circuit board from [src]...", \ + "You begin prying out the circuit board from [src]...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_GUTTED) + return + user.visible_message("[user] removes [src]'s circuit board.", \ + "You remove the circuit board from [src].") + new /obj/item/firelock_electronics(get_turf(src)) + constructionStep = CONSTRUCTION_NOCIRCUIT + update_icon() + +/obj/structure/firelock_frame/wirecutter_act(mob/user, obj/item/I) + if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) + return + . = TRUE + if(!I.tool_start_check(user, 0)) + return + + user.visible_message("[user] starts cutting the wires from [src]...", \ + "You begin removing [src]'s wires...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_WIRES_EXPOSED) + return + user.visible_message("[user] removes the wires from [src].", \ + "You remove the wiring from [src], exposing the circuit board.") + var/obj/item/stack/cable_coil/B = new(get_turf(src)) + B.amount = 5 + constructionStep = CONSTRUCTION_GUTTED + update_icon() + +/obj/structure/firelock_frame/wrench_act(mob/user, obj/item/I) + if(constructionStep != CONSTRUCTION_PANEL_OPEN) + return + . = TRUE + if(locate(/obj/machinery/door/firedoor) in get_turf(src)) + to_chat(user, "There's already a firelock there.") + return + if(!I.tool_start_check(user, 0)) + return + user.visible_message("[user] starts bolting down [src]...", \ + "You begin bolting [src]...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + if(locate(/obj/machinery/door/firedoor) in get_turf(src)) + return + user.visible_message("[user] finishes the firelock.", \ + "You finish the firelock.") + if(reinforced) + new /obj/machinery/door/firedoor/heavy(get_turf(src)) + else + new /obj/machinery/door/firedoor(get_turf(src)) + qdel(src) + + + + + +/obj/structure/firelock_frame/welder_act(mob/user, obj/item/I) + if(constructionStep != CONSTRUCTION_NOCIRCUIT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(!I.use_tool(src, user, 40, amount = 1, volume = I.tool_volume)) + return + if(constructionStep != CONSTRUCTION_NOCIRCUIT) + return + WELDER_SLICING_SUCCESS_MESSAGE + new /obj/item/stack/sheet/metal(drop_location(), 3) + if(reinforced) + new /obj/item/stack/sheet/plasteel(drop_location(), 2) + qdel(src) + +/obj/structure/firelock_frame/heavy + name = "heavy firelock frame" + reinforced = 1 + +#undef CONSTRUCTION_COMPLETE +#undef CONSTRUCTION_PANEL_OPEN +#undef CONSTRUCTION_WIRES_EXPOSED +#undef CONSTRUCTION_GUTTED +#undef CONSTRUCTION_NOCIRCUIT diff --git a/code/game/machinery/doors/shutters.dm b/code/game/machinery/doors/shutters.dm index e09d40689e22..457b056b7f97 100644 --- a/code/game/machinery/doors/shutters.dm +++ b/code/game/machinery/doors/shutters.dm @@ -1,14 +1,14 @@ -/obj/machinery/door/poddoor/shutters - gender = PLURAL - name = "shutters" - desc = "Heavy duty metal shutters that open mechanically." - icon = 'icons/obj/doors/shutters.dmi' - layer = SHUTTER_LAYER - closingLayer = SHUTTER_LAYER - damage_deflection = 20 - dir = EAST - -/obj/machinery/door/poddoor/shutters/preopen - icon_state = "open" - density = FALSE - opacity = 0 +/obj/machinery/door/poddoor/shutters + gender = PLURAL + name = "shutters" + desc = "Heavy duty metal shutters that open mechanically." + icon = 'icons/obj/doors/shutters.dmi' + layer = SHUTTER_LAYER + closingLayer = SHUTTER_LAYER + damage_deflection = 20 + dir = EAST + +/obj/machinery/door/poddoor/shutters/preopen + icon_state = "open" + density = FALSE + opacity = 0 diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 55c7f3d37732..e423f92683c7 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -1,472 +1,472 @@ -/obj/machinery/door/window - name = "interior door" - desc = "A strong door." - icon = 'icons/obj/doors/windoor.dmi' - icon_state = "left" - layer = ABOVE_WINDOW_LAYER - closingLayer = ABOVE_WINDOW_LAYER - resistance_flags = ACID_PROOF - visible = 0 - flags = ON_BORDER - opacity = 0 - dir = EAST - max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file - integrity_failure = 0 - armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 70, "acid" = 100) - var/obj/item/airlock_electronics/electronics - var/base_state = "left" - var/reinf = 0 - var/cancolor = TRUE - var/shards = 2 - var/rods = 2 - var/cable = 1 - var/list/debris = list() - -/obj/machinery/door/window/New(loc, set_dir) - ..() - if(set_dir) - setDir(set_dir) - if(req_access && req_access.len) - icon_state = "[icon_state]" - base_state = icon_state - if(!color && cancolor) - color = color_windows(src) - for(var/i in 1 to shards) - debris += new /obj/item/shard(src) - if(rods) - debris += new /obj/item/stack/rods(src, rods) - if(cable) - debris += new /obj/item/stack/cable_coil(src, cable) - -/obj/machinery/door/window/Destroy() - density = FALSE - QDEL_LIST(debris) - if(obj_integrity == 0) - playsound(src, "shatter", 70, 1) - QDEL_NULL(electronics) - return ..() - -/obj/machinery/door/window/update_icon() - if(density) - icon_state = base_state - else - icon_state = "[base_state]open" - -/obj/machinery/door/window/examine(mob/user) - . = ..() - if(emagged) - . += "Its access panel is smoking slightly." - -/obj/machinery/door/window/proc/open_and_close() - open() - if(check_access(null)) - sleep(50) - else //secure doors close faster - sleep(20) - close() - -/obj/machinery/door/window/Bumped(atom/movable/AM) - if(operating || !density) - return - if(!ismob(AM)) - if(ismecha(AM)) - var/obj/mecha/mecha = AM - if(mecha.occupant && allowed(mecha.occupant)) - open_and_close() - else - do_animate("deny") - return - if(!SSticker) - return - var/mob/living/M = AM - if(!M.restrained() && M.mob_size > MOB_SIZE_TINY && (!(isrobot(M) && M.stat))) - bumpopen(M) - -/obj/machinery/door/window/bumpopen(mob/user) - if(operating || !density) - return - add_fingerprint(user) - if(!requiresID() || allowed(user)) - open_and_close() - else - do_animate("deny") - -/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) //Make sure looking at appropriate border - return !density - if(istype(mover, /obj/structure/window)) - var/obj/structure/window/W = mover - if(!valid_window_location(loc, W.ini_dir)) - return FALSE - else if(istype(mover, /obj/structure/windoor_assembly)) - var/obj/structure/windoor_assembly/W = mover - if(!valid_window_location(loc, W.ini_dir)) - return FALSE - else if(istype(mover, /obj/machinery/door/window) && !valid_window_location(loc, mover.dir)) - return FALSE - else - return 1 - -/obj/machinery/door/window/CanAtmosPass(turf/T) - if(get_dir(loc, T) == dir) - return !density - else - return 1 - -//used in the AStar algorithm to determinate if the turf the door is on is passable -/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir) - return !density || (dir != to_dir) || (check_access(ID) && hasPower()) - -/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(get_dir(loc, target) == dir) - return !density - else - return 1 - -/obj/machinery/door/window/open(forced=0) - if(operating) //doors can still open when emag-disabled - return 0 - if(!forced) - if(!hasPower()) - return 0 - if(forced < 2) - if(emagged) - return 0 - if(!operating) //in case of emag - operating = TRUE - do_animate("opening") - playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) - icon_state ="[base_state]open" - sleep(10) - - density = FALSE -// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn - air_update_turf(1) - update_freelook_sight() - - if(operating) //emag again - operating = FALSE - return 1 - -/obj/machinery/door/window/close(forced=0) - if(operating) - return 0 - if(!forced) - if(!hasPower()) - return 0 - if(forced < 2) - if(emagged) - return 0 - operating = TRUE - do_animate("closing") - playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) - icon_state = base_state - - density = 1 -// if(visible) -// set_opacity(1) //TODO: why is this here? Opaque windoors? ~Carn - air_update_turf(1) - update_freelook_sight() - sleep(10) - - operating = 0 - return 1 - -/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(src, 'sound/effects/glasshit.ogg', 90, TRUE) - if(BURN) - playsound(src, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/door/window/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT) && !disassembled) - for(var/obj/fragment in debris) - fragment.forceMove(get_turf(src)) - transfer_fingerprints_to(fragment) - debris -= fragment - qdel(src) - -/obj/machinery/door/window/narsie_act() - color = NARSIE_WINDOW_COLOUR - -/obj/machinery/door/window/ratvar_act() - var/obj/machinery/door/window/clockwork/C = new(loc, dir) - C.name = name - qdel(src) - -/obj/machinery/door/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > T0C + (reinf ? 1600 : 800)) - take_damage(round(exposed_volume / 200), BURN, 0, 0) - -/obj/machinery/door/window/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/door/window/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/door/window/attack_hand(mob/user) - return try_to_activate_door(user) - -/obj/machinery/door/window/emag_act(mob/user, obj/weapon) - if(!operating && density && !emagged) - emagged = TRUE - operating = TRUE - flick("[base_state]spark", src) - playsound(src, "sparks", 75, 1) - sleep(6) - operating = FALSE - open(2) - return 1 - -/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params) - //If it's in the process of opening/closing, ignore the click - if(operating) - return - - add_fingerprint(user) - return ..() - -/obj/machinery/door/window/screwdriver_act(mob/user, obj/item/I) - if(flags & NODECONSTRUCT) - return - . = TRUE - if(density || operating) - to_chat(user, "You need to open the door to access the maintenance panel!") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the [src.name].") - - -/obj/machinery/door/window/crowbar_act(mob/user, obj/item/I) - if(operating) - return - if(flags & NODECONSTRUCT) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(panel_open && !density && !operating) - user.visible_message("[user] removes the electronics from the [name].", \ - "You start to remove electronics from the [name]...") - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - if(panel_open && !density && !operating && loc) - var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc) - switch(base_state) - if("left") - WA.facing = "l" - if("right") - WA.facing = "r" - if("leftsecure") - WA.facing = "l" - WA.secure = TRUE - if("rightsecure") - WA.facing = "r" - WA.secure = TRUE - WA.anchored = TRUE - WA.state= "02" - WA.setDir(dir) - WA.ini_dir = dir - WA.update_icon() - WA.created_name = name - - if(emagged) - to_chat(user, "You discard the damaged electronics.") - qdel(src) - return - - to_chat(user, "You remove the airlock electronics.") - - var/obj/item/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/airlock_electronics(loc) - if(!req_access) - check_access() - if(req_access.len) - ae.conf_access = req_access - else if(req_one_access.len) - ae.conf_access = req_one_access - ae.one_access = 1 - else - ae = electronics - electronics = null - ae.forceMove(loc) - - qdel(src) - else - try_to_crowbar(user, I) - -/obj/machinery/door/window/try_to_crowbar(mob/user, obj/item/I) - if(!hasPower()) - if(density) - open(2) - else - close(2) - else - to_chat(user, "The door's motors resist your efforts to force it!") - -/obj/machinery/door/window/do_animate(animation) - switch(animation) - if("opening") - flick("[base_state]opening", src) - if("closing") - flick("[base_state]closing", src) - if("deny") - flick("[base_state]deny", src) - -/obj/machinery/door/window/brigdoor - name = "secure door" - icon_state = "leftsecure" - base_state = "leftsecure" - max_integrity = 300 //Stronger doors for prison (regular window door health is 200) - reinf = 1 - explosion_block = 1 - var/id = null - -/obj/machinery/door/window/brigdoor/security/cell - name = "cell door" - desc = "For keeping in criminal scum." - req_access = list(ACCESS_BRIG) - -/obj/machinery/door/window/clockwork - name = "brass windoor" - desc = "A thin door with translucent brass paneling." - icon_state = "clockwork" - base_state = "clockwork" - shards = 0 - rods = 0 - resistance_flags = ACID_PROOF | FIRE_PROOF - cancolor = FALSE - var/made_glow = FALSE - -/obj/machinery/door/window/clockwork/New(loc, set_dir) - ..() - debris += new/obj/item/stack/tile/brass(src, 2) - -/obj/machinery/door/window/clockwork/setDir(direct) - if(!made_glow) - var/obj/effect/E = new /obj/effect/temp_visual/ratvar/door/window(get_turf(src)) - E.setDir(direct) - made_glow = TRUE - ..() - -/obj/machinery/door/window/clockwork/emp_act(severity) - if(prob(80/severity)) - open() - -/obj/machinery/door/window/clockwork/ratvar_act() - obj_integrity = max_integrity - -/obj/machinery/door/window/clockwork/hasPower() - return TRUE //yup that's power all right - -/obj/machinery/door/window/clockwork/narsie_act() - take_damage(rand(30, 60), BRUTE) - if(src) - var/previouscolor = color - color = "#960000" - animate(src, color = previouscolor, time = 8) - -/obj/machinery/door/window/northleft - dir = NORTH - -/obj/machinery/door/window/eastleft - dir = EAST - -/obj/machinery/door/window/westleft - dir = WEST - -/obj/machinery/door/window/southleft - dir = SOUTH - -/obj/machinery/door/window/northright - dir = NORTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/eastright - dir = EAST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/westright - dir = WEST - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/southright - dir = SOUTH - icon_state = "right" - base_state = "right" - -/obj/machinery/door/window/brigdoor/northleft - dir = NORTH - -/obj/machinery/door/window/brigdoor/eastleft - dir = EAST - -/obj/machinery/door/window/brigdoor/westleft - dir = WEST - -/obj/machinery/door/window/brigdoor/southleft - dir = SOUTH - -/obj/machinery/door/window/brigdoor/northright - dir = NORTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/eastright - dir = EAST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/westright - dir = WEST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/southright - dir = SOUTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/northleft - dir = NORTH - -/obj/machinery/door/window/brigdoor/security/cell/eastleft - dir = EAST - -/obj/machinery/door/window/brigdoor/security/cell/westleft - dir = WEST - -/obj/machinery/door/window/brigdoor/security/cell/southleft - dir = SOUTH - -/obj/machinery/door/window/brigdoor/security/cell/northright - dir = NORTH - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/eastright - dir = EAST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/westright - dir = WEST - icon_state = "rightsecure" - base_state = "rightsecure" - -/obj/machinery/door/window/brigdoor/security/cell/southright - dir = SOUTH - icon_state = "rightsecure" - base_state = "rightsecure" +/obj/machinery/door/window + name = "interior door" + desc = "A strong door." + icon = 'icons/obj/doors/windoor.dmi' + icon_state = "left" + layer = ABOVE_WINDOW_LAYER + closingLayer = ABOVE_WINDOW_LAYER + resistance_flags = ACID_PROOF + visible = 0 + flags = ON_BORDER + opacity = 0 + dir = EAST + max_integrity = 150 //If you change this, consider changing ../door/window/brigdoor/ max_integrity at the bottom of this .dm file + integrity_failure = 0 + armor = list("melee" = 20, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 70, "acid" = 100) + var/obj/item/airlock_electronics/electronics + var/base_state = "left" + var/reinf = 0 + var/cancolor = TRUE + var/shards = 2 + var/rods = 2 + var/cable = 1 + var/list/debris = list() + +/obj/machinery/door/window/New(loc, set_dir) + ..() + if(set_dir) + setDir(set_dir) + if(req_access && req_access.len) + icon_state = "[icon_state]" + base_state = icon_state + if(!color && cancolor) + color = color_windows(src) + for(var/i in 1 to shards) + debris += new /obj/item/shard(src) + if(rods) + debris += new /obj/item/stack/rods(src, rods) + if(cable) + debris += new /obj/item/stack/cable_coil(src, cable) + +/obj/machinery/door/window/Destroy() + density = FALSE + QDEL_LIST(debris) + if(obj_integrity == 0) + playsound(src, "shatter", 70, 1) + QDEL_NULL(electronics) + return ..() + +/obj/machinery/door/window/update_icon() + if(density) + icon_state = base_state + else + icon_state = "[base_state]open" + +/obj/machinery/door/window/examine(mob/user) + . = ..() + if(emagged) + . += "Its access panel is smoking slightly." + +/obj/machinery/door/window/proc/open_and_close() + open() + if(check_access(null)) + sleep(50) + else //secure doors close faster + sleep(20) + close() + +/obj/machinery/door/window/Bumped(atom/movable/AM) + if(operating || !density) + return + if(!ismob(AM)) + if(ismecha(AM)) + var/obj/mecha/mecha = AM + if(mecha.occupant && allowed(mecha.occupant)) + open_and_close() + else + do_animate("deny") + return + if(!SSticker) + return + var/mob/living/M = AM + if(!M.restrained() && M.mob_size > MOB_SIZE_TINY && (!(isrobot(M) && M.stat))) + bumpopen(M) + +/obj/machinery/door/window/bumpopen(mob/user) + if(operating || !density) + return + add_fingerprint(user) + if(!requiresID() || allowed(user)) + open_and_close() + else + do_animate("deny") + +/obj/machinery/door/window/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) //Make sure looking at appropriate border + return !density + if(istype(mover, /obj/structure/window)) + var/obj/structure/window/W = mover + if(!valid_window_location(loc, W.ini_dir)) + return FALSE + else if(istype(mover, /obj/structure/windoor_assembly)) + var/obj/structure/windoor_assembly/W = mover + if(!valid_window_location(loc, W.ini_dir)) + return FALSE + else if(istype(mover, /obj/machinery/door/window) && !valid_window_location(loc, mover.dir)) + return FALSE + else + return 1 + +/obj/machinery/door/window/CanAtmosPass(turf/T) + if(get_dir(loc, T) == dir) + return !density + else + return 1 + +//used in the AStar algorithm to determinate if the turf the door is on is passable +/obj/machinery/door/window/CanAStarPass(obj/item/card/id/ID, to_dir) + return !density || (dir != to_dir) || (check_access(ID) && hasPower()) + +/obj/machinery/door/window/CheckExit(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(get_dir(loc, target) == dir) + return !density + else + return 1 + +/obj/machinery/door/window/open(forced=0) + if(operating) //doors can still open when emag-disabled + return 0 + if(!forced) + if(!hasPower()) + return 0 + if(forced < 2) + if(emagged) + return 0 + if(!operating) //in case of emag + operating = TRUE + do_animate("opening") + playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) + icon_state ="[base_state]open" + sleep(10) + + density = FALSE +// sd_set_opacity(0) //TODO: why is this here? Opaque windoors? ~Carn + air_update_turf(1) + update_freelook_sight() + + if(operating) //emag again + operating = FALSE + return 1 + +/obj/machinery/door/window/close(forced=0) + if(operating) + return 0 + if(!forced) + if(!hasPower()) + return 0 + if(forced < 2) + if(emagged) + return 0 + operating = TRUE + do_animate("closing") + playsound(loc, 'sound/machines/windowdoor.ogg', 100, 1) + icon_state = base_state + + density = 1 +// if(visible) +// set_opacity(1) //TODO: why is this here? Opaque windoors? ~Carn + air_update_turf(1) + update_freelook_sight() + sleep(10) + + operating = 0 + return 1 + +/obj/machinery/door/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(src, 'sound/effects/glasshit.ogg', 90, TRUE) + if(BURN) + playsound(src, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/door/window/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT) && !disassembled) + for(var/obj/fragment in debris) + fragment.forceMove(get_turf(src)) + transfer_fingerprints_to(fragment) + debris -= fragment + qdel(src) + +/obj/machinery/door/window/narsie_act() + color = NARSIE_WINDOW_COLOUR + +/obj/machinery/door/window/ratvar_act() + var/obj/machinery/door/window/clockwork/C = new(loc, dir) + C.name = name + qdel(src) + +/obj/machinery/door/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > T0C + (reinf ? 1600 : 800)) + take_damage(round(exposed_volume / 200), BURN, 0, 0) + +/obj/machinery/door/window/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/door/window/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/door/window/attack_hand(mob/user) + return try_to_activate_door(user) + +/obj/machinery/door/window/emag_act(mob/user, obj/weapon) + if(!operating && density && !emagged) + emagged = TRUE + operating = TRUE + flick("[base_state]spark", src) + playsound(src, "sparks", 75, 1) + sleep(6) + operating = FALSE + open(2) + return 1 + +/obj/machinery/door/window/attackby(obj/item/I, mob/living/user, params) + //If it's in the process of opening/closing, ignore the click + if(operating) + return + + add_fingerprint(user) + return ..() + +/obj/machinery/door/window/screwdriver_act(mob/user, obj/item/I) + if(flags & NODECONSTRUCT) + return + . = TRUE + if(density || operating) + to_chat(user, "You need to open the door to access the maintenance panel!") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + panel_open = !panel_open + to_chat(user, "You [panel_open ? "open":"close"] the maintenance panel of the [src.name].") + + +/obj/machinery/door/window/crowbar_act(mob/user, obj/item/I) + if(operating) + return + if(flags & NODECONSTRUCT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(panel_open && !density && !operating) + user.visible_message("[user] removes the electronics from the [name].", \ + "You start to remove electronics from the [name]...") + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + if(panel_open && !density && !operating && loc) + var/obj/structure/windoor_assembly/WA = new /obj/structure/windoor_assembly(loc) + switch(base_state) + if("left") + WA.facing = "l" + if("right") + WA.facing = "r" + if("leftsecure") + WA.facing = "l" + WA.secure = TRUE + if("rightsecure") + WA.facing = "r" + WA.secure = TRUE + WA.anchored = TRUE + WA.state= "02" + WA.setDir(dir) + WA.ini_dir = dir + WA.update_icon() + WA.created_name = name + + if(emagged) + to_chat(user, "You discard the damaged electronics.") + qdel(src) + return + + to_chat(user, "You remove the airlock electronics.") + + var/obj/item/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/airlock_electronics(loc) + if(!req_access) + check_access() + if(req_access.len) + ae.conf_access = req_access + else if(req_one_access.len) + ae.conf_access = req_one_access + ae.one_access = 1 + else + ae = electronics + electronics = null + ae.forceMove(loc) + + qdel(src) + else + try_to_crowbar(user, I) + +/obj/machinery/door/window/try_to_crowbar(mob/user, obj/item/I) + if(!hasPower()) + if(density) + open(2) + else + close(2) + else + to_chat(user, "The door's motors resist your efforts to force it!") + +/obj/machinery/door/window/do_animate(animation) + switch(animation) + if("opening") + flick("[base_state]opening", src) + if("closing") + flick("[base_state]closing", src) + if("deny") + flick("[base_state]deny", src) + +/obj/machinery/door/window/brigdoor + name = "secure door" + icon_state = "leftsecure" + base_state = "leftsecure" + max_integrity = 300 //Stronger doors for prison (regular window door health is 200) + reinf = 1 + explosion_block = 1 + var/id = null + +/obj/machinery/door/window/brigdoor/security/cell + name = "cell door" + desc = "For keeping in criminal scum." + req_access = list(ACCESS_BRIG) + +/obj/machinery/door/window/clockwork + name = "brass windoor" + desc = "A thin door with translucent brass paneling." + icon_state = "clockwork" + base_state = "clockwork" + shards = 0 + rods = 0 + resistance_flags = ACID_PROOF | FIRE_PROOF + cancolor = FALSE + var/made_glow = FALSE + +/obj/machinery/door/window/clockwork/New(loc, set_dir) + ..() + debris += new/obj/item/stack/tile/brass(src, 2) + +/obj/machinery/door/window/clockwork/setDir(direct) + if(!made_glow) + var/obj/effect/E = new /obj/effect/temp_visual/ratvar/door/window(get_turf(src)) + E.setDir(direct) + made_glow = TRUE + ..() + +/obj/machinery/door/window/clockwork/emp_act(severity) + if(prob(80/severity)) + open() + +/obj/machinery/door/window/clockwork/ratvar_act() + obj_integrity = max_integrity + +/obj/machinery/door/window/clockwork/hasPower() + return TRUE //yup that's power all right + +/obj/machinery/door/window/clockwork/narsie_act() + take_damage(rand(30, 60), BRUTE) + if(src) + var/previouscolor = color + color = "#960000" + animate(src, color = previouscolor, time = 8) + +/obj/machinery/door/window/northleft + dir = NORTH + +/obj/machinery/door/window/eastleft + dir = EAST + +/obj/machinery/door/window/westleft + dir = WEST + +/obj/machinery/door/window/southleft + dir = SOUTH + +/obj/machinery/door/window/northright + dir = NORTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/eastright + dir = EAST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/westright + dir = WEST + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/southright + dir = SOUTH + icon_state = "right" + base_state = "right" + +/obj/machinery/door/window/brigdoor/northleft + dir = NORTH + +/obj/machinery/door/window/brigdoor/eastleft + dir = EAST + +/obj/machinery/door/window/brigdoor/westleft + dir = WEST + +/obj/machinery/door/window/brigdoor/southleft + dir = SOUTH + +/obj/machinery/door/window/brigdoor/northright + dir = NORTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/eastright + dir = EAST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/westright + dir = WEST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/southright + dir = SOUTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/northleft + dir = NORTH + +/obj/machinery/door/window/brigdoor/security/cell/eastleft + dir = EAST + +/obj/machinery/door/window/brigdoor/security/cell/westleft + dir = WEST + +/obj/machinery/door/window/brigdoor/security/cell/southleft + dir = SOUTH + +/obj/machinery/door/window/brigdoor/security/cell/northright + dir = NORTH + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/eastright + dir = EAST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/westright + dir = WEST + icon_state = "rightsecure" + base_state = "rightsecure" + +/obj/machinery/door/window/brigdoor/security/cell/southright + dir = SOUTH + icon_state = "rightsecure" + base_state = "rightsecure" diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm index d0fe753a9deb..0a0873a26052 100644 --- a/code/game/machinery/doppler_array.dm +++ b/code/game/machinery/doppler_array.dm @@ -1,230 +1,230 @@ -var/list/doppler_arrays = list() - -/obj/machinery/doppler_array - name = "tachyon-doppler array" - desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." - icon = 'icons/obj/machines/research.dmi' - icon_state = "tdoppler" - density = 1 - anchored = 1 - atom_say_verb = "states coldly" - var/list/logged_explosions = list() - var/explosion_target - var/datum/tech/toxins/toxins_tech - var/max_toxins_tech = 7 - -/datum/explosion_log - var/logged_time - var/epicenter - var/actual_size_message - var/theoretical_size_message - -/datum/explosion_log/New(var/log_time, var/log_epicenter, var/log_actual_size_message, var/log_theoretical_size_message) - ..() - logged_time = log_time - epicenter = log_epicenter - actual_size_message = log_actual_size_message - theoretical_size_message = log_theoretical_size_message - -/obj/machinery/doppler_array/New() - ..() - doppler_arrays += src - explosion_target = rand(8, 20) - toxins_tech = new /datum/tech/toxins(src) - -/obj/machinery/doppler_array/Destroy() - doppler_arrays -= src - logged_explosions.Cut() - return ..() - -/obj/machinery/doppler_array/process() - return PROCESS_KILL - -/obj/machinery/doppler_array/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/disk/tech_disk)) - var/obj/item/disk/tech_disk/disk = I - disk.load_tech(toxins_tech) - to_chat(user, "You swipe the disk into [src].") - return - return ..() - -/obj/machinery/doppler_array/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!anchored && !isinspace()) - anchored = TRUE - WRENCH_ANCHOR_MESSAGE - else if(anchored) - anchored = FALSE - WRENCH_UNANCHOR_MESSAGE - power_change() - -/obj/machinery/doppler_array/attack_hand(mob/user) - if(..()) - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/doppler_array/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/doppler_array/AltClick(mob/user) - rotate(user) - -/obj/machinery/doppler_array/verb/rotate(mob/user) - set name = "Rotate Tachyon-doppler Dish" - set category = "Object" - set src in oview(1) - - if(user.incapacitated()) - return - if(!Adjacent(user)) - return - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do that!") - return - dir = turn(dir, 90) - to_chat(user, "You rotate [src].") - -/obj/machinery/doppler_array/proc/print_explosive_logs(mob/user) - if(!logged_explosions.len) - atom_say("No logs currently stored in internal database.") - return - if(active_timers) - to_chat(user, "[src] is already printing something, please wait.") - return - atom_say("Printing explosive log. Standby...") - addtimer(CALLBACK(src, .print), 50) - -/obj/machinery/doppler_array/proc/print() - visible_message("[src] prints a piece of paper!") - playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - var/obj/item/paper/explosive_log/P = new(get_turf(src)) - for(var/D in logged_explosions) - var/datum/explosion_log/E = D - P.info += "\ - [E.logged_time]\ - [E.epicenter]\ - [E.actual_size_message]\ - [E.theoretical_size_message]\ - " - P.info += "
    \ - Printed at [station_time_timestamp()]." - -/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range, - var/took,var/orig_dev_range,var/orig_heavy_range,var/orig_light_range) - if(stat & NOPOWER) - return - if(z != z0) - return - - var/dx = abs(x0-x) - var/dy = abs(y0-y) - var/distance - var/direct - var/capped = FALSE - - if(dx > dy) - distance = dx - if(x0 > x) - direct = EAST - else - direct = WEST - else - distance = dy - if(y0 > y) - direct = NORTH - else - direct = SOUTH - - if(distance > 100) - return - if(!(direct & dir)) - return - - var/list/messages = list("Explosive disturbance detected.", \ - "Epicenter at: grid ([x0],[y0]). Temporal displacement of tachyons: [took] seconds.", \ - "Actual: Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range].") - - // If the bomb was capped, say its theoretical size. - if(devastation_range < orig_dev_range || heavy_impact_range < orig_heavy_range || light_impact_range < orig_light_range) - capped = TRUE - messages += "Theoretical: Epicenter radius: [orig_dev_range]. Outer radius: [orig_heavy_range]. Shockwave radius: [orig_light_range]." - logged_explosions.Insert(1, new /datum/explosion_log(station_time_timestamp(), "[x0],[y0]", "[devastation_range], [heavy_impact_range], [light_impact_range]", capped ? "[orig_dev_range], [orig_heavy_range], [orig_light_range]" : "n/a")) //Newer logs appear first - messages += "Event successfully logged in internal database." - var/miss_by = abs(explosion_target - orig_light_range) - var/tmp_tech = max_toxins_tech - miss_by - if(!miss_by) - messages += "Explosion size matches target." - else - messages += "Target ([explosion_target]) missed by : [miss_by]." - if(tmp_tech > toxins_tech.level) - toxins_tech.level = tmp_tech - messages += "Toxins technology level upgraded to [toxins_tech.level]. Swipe a technology disk to save data." - for(var/message in messages) - atom_say(message) - -/obj/machinery/doppler_array/power_change() - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - else - if(powered() && anchored) - icon_state = initial(icon_state) - stat &= ~NOPOWER - else - icon_state = "[initial(icon_state)]-off" - stat |= NOPOWER - -/obj/machinery/doppler_array/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "doppler_array.tmpl", "Tachyon-doppler array", 500, 650) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/doppler_array/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/list/explosion_data = list() - for(var/D in logged_explosions) - var/datum/explosion_log/E = D - explosion_data += list(list( - "logged_time" = E.logged_time, - "epicenter" = E.epicenter, - "actual_size_message" = E.actual_size_message, - "theoretical_size_message" = E.theoretical_size_message, - "unique_datum_id" = E.UID())) - data["explosion_target"] = explosion_target - data["toxins_tech"] = toxins_tech.level - data["explosion_data"] = explosion_data - data["printing"] = active_timers - return data - -/obj/machinery/doppler_array/Topic(href, href_list) - if(..()) - return - if(href_list["log_to_delete"]) - var/log_to_delete = sanitize(href_list["log_to_delete"]) - for(var/D in logged_explosions) - var/datum/explosion_log/E = D - if(E.UID() == log_to_delete) - logged_explosions -= E - qdel(E) - to_chat(usr, "Log deletion successful.") - break - else if(href_list["print_logs"]) - print_explosive_logs(usr) - else - return - SSnanoui.update_uis(src) - -/obj/item/paper/explosive_log - name = "explosive log" - info = "

    Explosive Log Report

    \ - \ - \ - \ - \ - \ - \ - " //NB: the
    Time loggedEpicenterActualTheoretical
    tag is left open, it is closed later on, when the doppler array adds its data +GLOBAL_LIST_EMPTY(doppler_arrays) + +/obj/machinery/doppler_array + name = "tachyon-doppler array" + desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array." + icon = 'icons/obj/machines/research.dmi' + icon_state = "tdoppler" + density = 1 + anchored = 1 + atom_say_verb = "states coldly" + var/list/logged_explosions = list() + var/explosion_target + var/datum/tech/toxins/toxins_tech + var/max_toxins_tech = 7 + +/datum/explosion_log + var/logged_time + var/epicenter + var/actual_size_message + var/theoretical_size_message + +/datum/explosion_log/New(var/log_time, var/log_epicenter, var/log_actual_size_message, var/log_theoretical_size_message) + ..() + logged_time = log_time + epicenter = log_epicenter + actual_size_message = log_actual_size_message + theoretical_size_message = log_theoretical_size_message + +/obj/machinery/doppler_array/New() + ..() + GLOB.doppler_arrays += src + explosion_target = rand(8, 20) + toxins_tech = new /datum/tech/toxins(src) + +/obj/machinery/doppler_array/Destroy() + GLOB.doppler_arrays -= src + logged_explosions.Cut() + return ..() + +/obj/machinery/doppler_array/process() + return PROCESS_KILL + +/obj/machinery/doppler_array/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/disk/tech_disk)) + var/obj/item/disk/tech_disk/disk = I + disk.load_tech(toxins_tech) + to_chat(user, "You swipe the disk into [src].") + return + return ..() + +/obj/machinery/doppler_array/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!anchored && !isinspace()) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE + else if(anchored) + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + power_change() + +/obj/machinery/doppler_array/attack_hand(mob/user) + if(..()) + return + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/doppler_array/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/doppler_array/AltClick(mob/user) + rotate(user) + +/obj/machinery/doppler_array/verb/rotate(mob/user) + set name = "Rotate Tachyon-doppler Dish" + set category = "Object" + set src in oview(1) + + if(user.incapacitated()) + return + if(!Adjacent(user)) + return + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do that!") + return + dir = turn(dir, 90) + to_chat(user, "You rotate [src].") + +/obj/machinery/doppler_array/proc/print_explosive_logs(mob/user) + if(!logged_explosions.len) + atom_say("No logs currently stored in internal database.") + return + if(active_timers) + to_chat(user, "[src] is already printing something, please wait.") + return + atom_say("Printing explosive log. Standby...") + addtimer(CALLBACK(src, .print), 50) + +/obj/machinery/doppler_array/proc/print() + visible_message("[src] prints a piece of paper!") + playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + var/obj/item/paper/explosive_log/P = new(get_turf(src)) + for(var/D in logged_explosions) + var/datum/explosion_log/E = D + P.info += "\ + \ + \ + \ + \ + " + P.info += "
    [E.logged_time][E.epicenter][E.actual_size_message][E.theoretical_size_message]

    \ + Printed at [station_time_timestamp()]." + +/obj/machinery/doppler_array/proc/sense_explosion(var/x0,var/y0,var/z0,var/devastation_range,var/heavy_impact_range,var/light_impact_range, + var/took,var/orig_dev_range,var/orig_heavy_range,var/orig_light_range) + if(stat & NOPOWER) + return + if(z != z0) + return + + var/dx = abs(x0-x) + var/dy = abs(y0-y) + var/distance + var/direct + var/capped = FALSE + + if(dx > dy) + distance = dx + if(x0 > x) + direct = EAST + else + direct = WEST + else + distance = dy + if(y0 > y) + direct = NORTH + else + direct = SOUTH + + if(distance > 100) + return + if(!(direct & dir)) + return + + var/list/messages = list("Explosive disturbance detected.", \ + "Epicenter at: grid ([x0],[y0]). Temporal displacement of tachyons: [took] seconds.", \ + "Actual: Epicenter radius: [devastation_range]. Outer radius: [heavy_impact_range]. Shockwave radius: [light_impact_range].") + + // If the bomb was capped, say its theoretical size. + if(devastation_range < orig_dev_range || heavy_impact_range < orig_heavy_range || light_impact_range < orig_light_range) + capped = TRUE + messages += "Theoretical: Epicenter radius: [orig_dev_range]. Outer radius: [orig_heavy_range]. Shockwave radius: [orig_light_range]." + logged_explosions.Insert(1, new /datum/explosion_log(station_time_timestamp(), "[x0],[y0]", "[devastation_range], [heavy_impact_range], [light_impact_range]", capped ? "[orig_dev_range], [orig_heavy_range], [orig_light_range]" : "n/a")) //Newer logs appear first + messages += "Event successfully logged in internal database." + var/miss_by = abs(explosion_target - orig_light_range) + var/tmp_tech = max_toxins_tech - miss_by + if(!miss_by) + messages += "Explosion size matches target." + else + messages += "Target ([explosion_target]) missed by : [miss_by]." + if(tmp_tech > toxins_tech.level) + toxins_tech.level = tmp_tech + messages += "Toxins technology level upgraded to [toxins_tech.level]. Swipe a technology disk to save data." + for(var/message in messages) + atom_say(message) + +/obj/machinery/doppler_array/power_change() + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + else + if(powered() && anchored) + icon_state = initial(icon_state) + stat &= ~NOPOWER + else + icon_state = "[initial(icon_state)]-off" + stat |= NOPOWER + +/obj/machinery/doppler_array/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "doppler_array.tmpl", "Tachyon-doppler array", 500, 650) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/doppler_array/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/list/explosion_data = list() + for(var/D in logged_explosions) + var/datum/explosion_log/E = D + explosion_data += list(list( + "logged_time" = E.logged_time, + "epicenter" = E.epicenter, + "actual_size_message" = E.actual_size_message, + "theoretical_size_message" = E.theoretical_size_message, + "unique_datum_id" = E.UID())) + data["explosion_target"] = explosion_target + data["toxins_tech"] = toxins_tech.level + data["explosion_data"] = explosion_data + data["printing"] = active_timers + return data + +/obj/machinery/doppler_array/Topic(href, href_list) + if(..()) + return + if(href_list["log_to_delete"]) + var/log_to_delete = sanitize(href_list["log_to_delete"]) + for(var/D in logged_explosions) + var/datum/explosion_log/E = D + if(E.UID() == log_to_delete) + logged_explosions -= E + qdel(E) + to_chat(usr, "Log deletion successful.") + break + else if(href_list["print_logs"]) + print_explosive_logs(usr) + else + return + SSnanoui.update_uis(src) + +/obj/item/paper/explosive_log + name = "explosive log" + info = "

    Explosive Log Report

    \ + \ + \ + \ + \ + \ + \ + " //NB: the
    Time loggedEpicenterActualTheoretical
    tag is left open, it is closed later on, when the doppler array adds its data diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm index b1a6f96e2eb6..32924690d978 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -27,7 +27,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data = list( @@ -98,7 +98,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/embedded_controller/radio/airlock/airlock_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data = list( @@ -160,7 +160,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/embedded_controller/radio/airlock/access_controller/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data = list( diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 670cce47785a..8ad85a608f06 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -379,4 +379,4 @@ send an additional command to open the door again. #undef TARGET_NONE #undef TARGET_INOPEN -#undef TARGET_OUTOPEN \ No newline at end of file +#undef TARGET_OUTOPEN diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 62fbae71c785..2226f9f18fe9 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -1,87 +1,87 @@ -/obj/machinery/embedded_controller - var/datum/computer/file/embedded_program/program //the currently executing program - - name = "Embedded Controller" - anchored = 1 - - use_power = IDLE_POWER_USE - idle_power_usage = 10 - - var/on = 1 - -/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) - return 0 - -/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) - if(!signal || signal.encryption) return - - if(program) - program.receive_signal(signal, receive_method, receive_param) - //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 - -/obj/machinery/embedded_controller/process() - if(program) - program.process() - - update_icon() - src.updateDialog() - -/obj/machinery/embedded_controller/attack_ghost(mob/user as mob) - src.ui_interact(user) - -/obj/machinery/embedded_controller/attack_ai(mob/user as mob) - src.ui_interact(user) - -/obj/machinery/embedded_controller/attack_hand(mob/user as mob) - if(!user.IsAdvancedToolUser()) - return 0 - src.ui_interact(user) - -/obj/machinery/embedded_controller/ui_interact() - return - -/obj/machinery/embedded_controller/radio - icon = 'icons/obj/airlock_machines.dmi' - icon_state = "airlock_control_standby" - power_channel = ENVIRON - density = 0 - - var/id_tag - //var/radio_power_use = 50 //power used to xmit signals - - var/frequency = 1379 - var/radio_filter = null - var/datum/radio_frequency/radio_connection - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/machinery/embedded_controller/radio/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/embedded_controller/radio/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/embedded_controller/radio/update_icon() - if(on && program) - if(program.memory["processing"]) - icon_state = "airlock_control_process" - else - icon_state = "airlock_control_standby" - else - icon_state = "airlock_control_off" - -/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/filter = null) - signal.transmission_method = TRANSMISSION_RADIO - if(radio_connection) - //use_power(radio_power_use) //neat idea, but causes way too much lag. - return radio_connection.post_signal(src, signal, filter) - else - qdel(signal) - -/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, radio_filter) +/obj/machinery/embedded_controller + var/datum/computer/file/embedded_program/program //the currently executing program + + name = "Embedded Controller" + anchored = 1 + + use_power = IDLE_POWER_USE + idle_power_usage = 10 + + var/on = 1 + +/obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) + return 0 + +/obj/machinery/embedded_controller/receive_signal(datum/signal/signal, receive_method, receive_param) + if(!signal || signal.encryption) return + + if(program) + program.receive_signal(signal, receive_method, receive_param) + //spawn(5) program.process() //no, program.process sends some signals and machines respond and we here again and we lag -rastaf0 + +/obj/machinery/embedded_controller/process() + if(program) + program.process() + + update_icon() + src.updateDialog() + +/obj/machinery/embedded_controller/attack_ghost(mob/user as mob) + src.ui_interact(user) + +/obj/machinery/embedded_controller/attack_ai(mob/user as mob) + src.ui_interact(user) + +/obj/machinery/embedded_controller/attack_hand(mob/user as mob) + if(!user.IsAdvancedToolUser()) + return 0 + src.ui_interact(user) + +/obj/machinery/embedded_controller/ui_interact() + return + +/obj/machinery/embedded_controller/radio + icon = 'icons/obj/airlock_machines.dmi' + icon_state = "airlock_control_standby" + power_channel = ENVIRON + density = 0 + + var/id_tag + //var/radio_power_use = 50 //power used to xmit signals + + var/frequency = 1379 + var/radio_filter = null + var/datum/radio_frequency/radio_connection + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/machinery/embedded_controller/radio/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/embedded_controller/radio/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/embedded_controller/radio/update_icon() + if(on && program) + if(program.memory["processing"]) + icon_state = "airlock_control_process" + else + icon_state = "airlock_control_standby" + else + icon_state = "airlock_control_off" + +/obj/machinery/embedded_controller/radio/post_signal(datum/signal/signal, var/filter = null) + signal.transmission_method = TRANSMISSION_RADIO + if(radio_connection) + //use_power(radio_power_use) //neat idea, but causes way too much lag. + return radio_connection.post_signal(src, signal, filter) + else + qdel(signal) + +/obj/machinery/embedded_controller/radio/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, radio_filter) diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm index 7ae268fca46b..9beba3f4789b 100644 --- a/code/game/machinery/firealarm.dm +++ b/code/game/machinery/firealarm.dm @@ -236,14 +236,14 @@ FIRE ALARM ui_interact(user) -/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) +/obj/machinery/firealarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "firealarm.tmpl", name, 400, 400, state = state) ui.open() ui.set_auto_update(1) -/obj/machinery/firealarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/firealarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/area/A = get_area(src) @@ -318,7 +318,7 @@ FIRE ALARM pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0 if(is_station_contact(z) && show_alert_level) - if(security_level) + if(GLOB.security_level) overlays += image('icons/obj/monitors.dmi', "overlay_[get_security_level()]") else overlays += image('icons/obj/monitors.dmi', "overlay_green") diff --git a/code/game/machinery/flasher.dm b/code/game/machinery/flasher.dm index 1f42a6a78c4a..b7b7e35eef64 100644 --- a/code/game/machinery/flasher.dm +++ b/code/game/machinery/flasher.dm @@ -1,153 +1,153 @@ -// It is a gizmo that flashes a small area - -/obj/machinery/flasher - name = "Mounted flash" - desc = "A wall-mounted flashbulb device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "mflash1" - max_integrity = 250 - integrity_failure = 100 - damage_deflection = 10 - var/id = null - var/range = 2 //this is roughly the size of brig cell - var/disable = 0 - var/last_flash = 0 //Don't want it getting spammed like regular flashes - var/strength = 5 //How weakened targets are when flashed. - var/base_state = "mflash" - anchored = 1 - -/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored - name = "portable flasher" - desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." - icon_state = "pflash1" - strength = 4 - anchored = 0 - base_state = "pflash" - density = 1 - -/* -/obj/machinery/flasher/New() - sleep(4) //<--- What the fuck are you doing? D= - sd_set_light(2) -*/ -/obj/machinery/flasher/power_change() - if( powered() ) - stat &= ~NOPOWER - icon_state = "[base_state]1" -// sd_set_light(2) - else - stat |= ~NOPOWER - icon_state = "[base_state]1-p" -// sd_set_light(0) - -//Let the AI trigger them directly. -/obj/machinery/flasher/attack_ai(mob/user) - if(anchored) - return flash() - -/obj/machinery/flasher/attack_ghost(mob/user) - if(anchored && user.can_advanced_admin_interact()) - return flash() - -/obj/machinery/flasher/proc/flash() - if(!(powered())) - return - - if((disable) || (last_flash && world.time < last_flash + 150)) - return - - playsound(loc, 'sound/weapons/flash.ogg', 100, 1) - flick("[base_state]_flash", src) - last_flash = world.time - use_power(1000) - - for(var/mob/living/L in viewers(src, null)) - if(get_dist(src, L) > range) - continue - - if(L.flash_eyes(affect_silicon = 1)) - L.Weaken(strength) - if(L.weakeyes) - L.Weaken(strength * 1.5) - L.visible_message("[L] gasps and shields [L.p_their()] eyes!") - -/obj/machinery/flasher/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(prob(75/severity)) - flash() - ..(severity) - -/obj/machinery/flasher/portable/HasProximity(atom/movable/AM as mob|obj) - if((disable) || (last_flash && world.time < last_flash + 150)) - return - - if(istype(AM, /mob/living/carbon)) - var/mob/living/carbon/M = AM - if((M.m_intent != MOVE_INTENT_WALK) && (anchored)) - flash() - -//Don't want to render prison breaks impossible -/obj/machinery/flasher/portable/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - disable = !disable - if(disable) - user.visible_message("[user] has disconnected [src]'s flashbulb!", "You disconnect [src]'s flashbulb!") - if(!disable) - user.visible_message("[user] has connected [src]'s flashbulb!", "You connect [src]'s flashbulb!") - -/obj/machinery/flasher/portable/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - anchored = !anchored - if(anchored) - WRENCH_ANCHOR_MESSAGE - overlays.Cut() - else if(anchored) - WRENCH_UNANCHOR_MESSAGE - overlays += "[base_state]-s" - -// Flasher button -/obj/machinery/flasher_button - name = "flasher button" - desc = "A remote control switch for a mounted flasher." - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - var/id = null - var/active = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/flasher_button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/flasher_button/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/flasher_button/attack_hand(mob/user as mob) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/flasher/M in world) - if(M.id == id) - spawn() - M.flash() - - sleep(50) - - icon_state = "launcherbtt" - active = 0 +// It is a gizmo that flashes a small area + +/obj/machinery/flasher + name = "Mounted flash" + desc = "A wall-mounted flashbulb device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "mflash1" + max_integrity = 250 + integrity_failure = 100 + damage_deflection = 10 + var/id = null + var/range = 2 //this is roughly the size of brig cell + var/disable = 0 + var/last_flash = 0 //Don't want it getting spammed like regular flashes + var/strength = 5 //How weakened targets are when flashed. + var/base_state = "mflash" + anchored = 1 + +/obj/machinery/flasher/portable //Portable version of the flasher. Only flashes when anchored + name = "portable flasher" + desc = "A portable flashing device. Wrench to activate and deactivate. Cannot detect slow movements." + icon_state = "pflash1" + strength = 4 + anchored = 0 + base_state = "pflash" + density = 1 + +/* +/obj/machinery/flasher/New() + sleep(4) //<--- What the fuck are you doing? D= + sd_set_light(2) +*/ +/obj/machinery/flasher/power_change() + if( powered() ) + stat &= ~NOPOWER + icon_state = "[base_state]1" +// sd_set_light(2) + else + stat |= ~NOPOWER + icon_state = "[base_state]1-p" +// sd_set_light(0) + +//Let the AI trigger them directly. +/obj/machinery/flasher/attack_ai(mob/user) + if(anchored) + return flash() + +/obj/machinery/flasher/attack_ghost(mob/user) + if(anchored && user.can_advanced_admin_interact()) + return flash() + +/obj/machinery/flasher/proc/flash() + if(!(powered())) + return + + if((disable) || (last_flash && world.time < last_flash + 150)) + return + + playsound(loc, 'sound/weapons/flash.ogg', 100, 1) + flick("[base_state]_flash", src) + last_flash = world.time + use_power(1000) + + for(var/mob/living/L in viewers(src, null)) + if(get_dist(src, L) > range) + continue + + if(L.flash_eyes(affect_silicon = 1)) + L.Weaken(strength) + if(L.weakeyes) + L.Weaken(strength * 1.5) + L.visible_message("[L] gasps and shields [L.p_their()] eyes!") + +/obj/machinery/flasher/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(prob(75/severity)) + flash() + ..(severity) + +/obj/machinery/flasher/portable/HasProximity(atom/movable/AM as mob|obj) + if((disable) || (last_flash && world.time < last_flash + 150)) + return + + if(istype(AM, /mob/living/carbon)) + var/mob/living/carbon/M = AM + if((M.m_intent != MOVE_INTENT_WALK) && (anchored)) + flash() + +//Don't want to render prison breaks impossible +/obj/machinery/flasher/portable/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + disable = !disable + if(disable) + user.visible_message("[user] has disconnected [src]'s flashbulb!", "You disconnect [src]'s flashbulb!") + if(!disable) + user.visible_message("[user] has connected [src]'s flashbulb!", "You connect [src]'s flashbulb!") + +/obj/machinery/flasher/portable/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(anchored) + WRENCH_ANCHOR_MESSAGE + overlays.Cut() + else if(anchored) + WRENCH_UNANCHOR_MESSAGE + overlays += "[base_state]-s" + +// Flasher button +/obj/machinery/flasher_button + name = "flasher button" + desc = "A remote control switch for a mounted flasher." + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + var/id = null + var/active = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/flasher_button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/flasher_button/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/flasher_button/attack_hand(mob/user as mob) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/flasher/M in world) + if(M.id == id) + spawn() + M.flash() + + sleep(50) + + icon_state = "launcherbtt" + active = 0 diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index ef0ae0f2812d..fcd6de4020c8 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -1,517 +1,517 @@ -/* holograms! - * Contains: - * Holopad - * hologram - * Other stuff - */ - -/* -Revised. Original based on space ninja hologram code. Which is also mine. /N -How it works: -AI clicks on holopad in camera view. View centers on holopad. -AI clicks again on the holopad to display a hologram. hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. -AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. -Only one AI may project from a holopad at any given time. -AI may cancel the hologram at any time by clicking on the holopad once more. -Possible to do for anyone motivated enough: - Give an AI variable for different hologram icons. - Itegrate EMP effect to disable the unit. -*/ - - -/* - * Holopad - */ - -// HOLOPAD MODE -// 0 = RANGE BASED -// 1 = AREA BASED -#define HOLOPAD_PASSIVE_POWER_USAGE 1 -#define HOLOGRAM_POWER_USAGE 2 -#define RANGE_BASED 0 -#define AREA_BASED 1 - -#define HOLOPAD_MODE RANGE_BASED - -var/list/holopads = list() - -/obj/machinery/hologram/holopad - name = "holopad" - desc = "It's a floor-mounted device for projecting holographic images." - icon_state = "holopad0" - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 5 - active_power_usage = 100 - layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them. - plane = FLOOR_PLANE - max_integrity = 300 - armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0) - var/list/masters = list()//List of living mobs that use the holopad - var/list/holorays = list()//Holoray-mob link. - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. - var/temp = "" - var/list/holo_calls //array of /datum/holocalls - var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs - var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging - var/obj/effect/overlay/holoray/ray - var/ringing = FALSE - var/dialling_input = FALSE //The user is currently selecting where to send their call - -/obj/machinery/hologram/holopad/New() - ..() - holopads += src - component_parts = list() - component_parts += new /obj/item/circuitboard/holopad(null) - component_parts += new /obj/item/stock_parts/capacitor(null) - RefreshParts() - -/obj/machinery/hologram/holopad/Destroy() - if(outgoing_call) - outgoing_call.ConnectionFailure(src) - - for(var/I in holo_calls) - var/datum/holocall/HC = I - HC.ConnectionFailure(src) - - for(var/I in masters) - clear_holo(I) - holopads -= src - return ..() - -/obj/machinery/hologram/holopad/power_change() - if(powered()) - stat &= ~NOPOWER - else - stat |= NOPOWER - if(outgoing_call) - outgoing_call.ConnectionFailure(src) - -/obj/machinery/hologram/holopad/obj_break() - . = ..() - if(outgoing_call) - outgoing_call.ConnectionFailure(src) - -/obj/machinery/hologram/holopad/RefreshParts() - var/holograph_range = 4 - for(var/obj/item/stock_parts/capacitor/B in component_parts) - holograph_range += 1 * B.rating - holo_range = holograph_range - -/obj/machinery/hologram/holopad/attackby(obj/item/I, mob/user, params) - if(exchange_parts(user, I)) - return - return ..() - -/obj/machinery/hologram/holopad/screwdriver_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_screwdriver(user, "holopad_open", "holopad0", I) - - -/obj/machinery/hologram/holopad/wrench_act(mob/user, obj/item/I) - . = TRUE - default_unfasten_wrench(user, I) - -/obj/machinery/hologram/holopad/crowbar_act(mob/user, obj/item/I) - . = TRUE - default_deconstruction_crowbar(user, I) - -/obj/machinery/hologram/holopad/attack_hand(mob/living/carbon/human/user) - if(..()) - return - - if(outgoing_call) - return - - user.set_machine(src) - interact(user) - -/obj/machinery/hologram/holopad/AltClick(mob/living/carbon/human/user) - if(..()) - return - if(isAI(user)) - hangup_all_calls() - return - -//Stop ringing the AI!! -/obj/machinery/hologram/holopad/proc/hangup_all_calls() - for(var/I in holo_calls) - var/datum/holocall/HC = I - HC.Disconnect(src) - -/obj/machinery/hologram/holopad/interact(mob/living/carbon/human/user) //Carn: hologram requests. - if(!istype(user)) - return - if(!anchored) - return - - var/dat - if(temp) - dat = temp - else - dat = "Request an AI's presence.
    " - dat += "Call another holopad.
    " - - if(LAZYLEN(holo_calls)) - dat += "=====================================================
    " - - var/one_answered_call = FALSE - var/one_unanswered_call = FALSE - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad != src) - dat += "Answer call from [get_area(HC.calling_holopad)].
    " - one_unanswered_call = TRUE - else - one_answered_call = TRUE - - if(one_answered_call && one_unanswered_call) - dat += "=====================================================
    " - //we loop twice for formatting - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad == src) - dat += "Disconnect call from [HC.user].
    " - - var/area/area = get_area(src) - var/datum/browser/popup = new(user, "holopad", "[area] holopad", 400, 300) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - -/obj/machinery/hologram/holopad/Topic(href, href_list) - if(..() || isAI(usr)) - return - add_fingerprint(usr) - if(stat & NOPOWER) - return - if(href_list["AIrequest"]) - if(last_request + 200 < world.time) - last_request = world.time - temp = "You requested an AI's presence.
    " - temp += "Main Menu" - var/area/area = get_area(src) - for(var/mob/living/silicon/ai/AI in ai_list) - if(!AI.client) - continue - to_chat(AI, "Your presence is requested at \the [area].") - else - temp = "A request for AI presence was already sent recently.
    " - temp += "Main Menu" - - else if(href_list["Holocall"]) - if(outgoing_call) - return - if(dialling_input) - to_chat(usr, "Finish dialling first!") - return - temp = "You must stand on the holopad to make a call!
    " - temp += "Main Menu" - if(usr.loc == loc) - var/list/callnames = list() - for(var/I in holopads) - var/area/A = get_area(I) - if(A) - LAZYADD(callnames[A], I) - callnames -= get_area(src) - var/list/sorted_callnames = sortAtom(callnames) - dialling_input = TRUE - var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sorted_callnames - dialling_input = FALSE - if(QDELETED(usr) || !result || outgoing_call) - return - - if(usr.loc == loc) - temp = "Dialing...
    " - temp += "Main Menu" - new /datum/holocall(usr, src, callnames[result]) - - else if(href_list["connectcall"]) - var/datum/holocall/call_to_connect = locateUID(href_list["connectcall"]) - if(!QDELETED(call_to_connect) && (call_to_connect in holo_calls)) - call_to_connect.Answer(src) - temp = "" - - else if(href_list["disconnectcall"]) - var/datum/holocall/call_to_disconnect = locateUID(href_list["disconnectcall"]) - if(!QDELETED(call_to_disconnect)) - call_to_disconnect.Disconnect(src) - temp = "" - - else if(href_list["mainmenu"]) - temp = "" - if(outgoing_call) - outgoing_call.Disconnect() - - updateDialog() - -//do not allow AIs to answer calls or people will use it to meta the AI satellite -/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - if(outgoing_call) - return - /*There are pretty much only three ways to interact here. - I don't need to check for client since they're clicking on an object. - This may change in the future but for now will suffice.*/ - if(user.eyeobj.loc != loc)//Set client eye on the object if it's not already. - user.eyeobj.setLoc(get_turf(src)) - else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one. - activate_holo(user, 1) - else//If there is a hologram, remove it. - clear_holo(user) - -/obj/machinery/hologram/holopad/process() - for(var/I in masters) - var/mob/living/master = I - if((stat & NOPOWER) || !validate_user(master) || !anchored) - clear_holo(master) - - if(outgoing_call) - outgoing_call.Check() - - ringing = FALSE - - for(var/I in holo_calls) - var/datum/holocall/HC = I - //Sanity check and skip if no longer valid call - if(!HC.Check()) - atom_say("Call was terminated at remote terminal.") - continue - - if(HC.connected_holopad != src) - if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) - HC.Answer(src) - break - if(outgoing_call) - HC.Disconnect(src)//can't answer calls while calling - else - playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring! - ringing = TRUE - - update_icon() - - -//Try to transfer hologram to another pad that can project on T -/obj/machinery/hologram/holopad/proc/transfer_to_nearby_pad(turf/T, mob/holo_owner) - if(!isAI(holo_owner)) - return - for(var/pad in holopads) - var/obj/machinery/hologram/holopad/another = pad - if(another == src) - continue - if(another.validate_location(T)) - var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner] - unset_holo(holo_owner) - another.set_holo(holo_owner, h) - return TRUE - return FALSE - -/obj/machinery/hologram/holopad/proc/validate_user(mob/living/user) - if(QDELETED(user) || user.incapacitated() || !user.client) - return FALSE - - if(istype(user, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = user - if(!AI.current) - return FALSE - return TRUE - -//Can we display holos there -//Area check instead of line of sight check because this is a called a lot if AI wants to move around. -/obj/machinery/hologram/holopad/proc/validate_location(turf/T,check_los = FALSE) - if(T.z == z && get_dist(T, src) <= holo_range && T.loc == get_area(src)) - return TRUE - return FALSE - - -/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/user, turf/new_turf) - if(masters[user]) - var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] - var/transfered = FALSE - if(!validate_location(new_turf)) - if(!transfer_to_nearby_pad(new_turf,user)) - clear_holo(user) - return FALSE - else - transfered = TRUE - //All is good. - holo.setDir(get_dir(holo.loc, new_turf)) - holo.forceMove(new_turf) - if(!transfered) - update_holoray(user,new_turf) - return TRUE - -/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/user, var/force = 0) - var/mob/living/silicon/ai/AI = user - if(!istype(AI)) - AI = null - if(AI && !force && AI.eyeobj.loc != loc) // allows holopads to pass off holograms to the next holopad in the chain - to_chat(user, "ERROR: Unable to project hologram.") - if(!(stat & NOPOWER) && (!AI || force)) - if(AI && (istype(AI.current, /obj/machinery/hologram/holopad))) - to_chat(user, "ERROR: Image feed in progress.") - return - - var/obj/effect/overlay/holo_pad_hologram/hologram = new(loc)//Spawn a blank effect at the location. - if(isAI(user)) - hologram.icon = AI.holo_icon - else //make it like real life - hologram.icon = getHologramIcon(get_id_photo(user)) - hologram.icon_state = user.icon_state - hologram.alpha = 100 - hologram.Impersonation = user - - hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it. - hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - hologram.anchored = 1//So space wind cannot drag it. - hologram.name = "[user.name] (hologram)"//If someone decides to right click. - hologram.set_light(2) //hologram lighting - move_hologram() - - set_holo(user, hologram) - - if(!masters[user])//If there is not already a hologram. - visible_message("A holographic image of [user] flicks to life right before your eyes!") - - return hologram - - - to_chat(user, "ERROR: Hologram Projection Malfunction.") - clear_holo(user)//safety check - -/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. -For the other part of the code, check silicon say.dm. Particularly robot talk.*/ -/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, list/message_pieces, verb) - if(speaker && masters.len)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. - for(var/mob/living/silicon/ai/master in masters) - if(masters[master] && speaker != master) - master.relay_speech(speaker, message_pieces, verb) - - for(var/I in holo_calls) - var/datum/holocall/HC = I - if(HC.connected_holopad == src && speaker != HC.hologram) - HC.user.hear_say(message_pieces, verb, speaker = speaker) - - if(outgoing_call && speaker == outgoing_call.user) - outgoing_call.hologram.atom_say(multilingual_to_message(message_pieces)) - - - -/obj/machinery/hologram/holopad/proc/SetLightsAndPower() - var/total_users = masters.len + LAZYLEN(holo_calls) - use_power = total_users > 0 ? ACTIVE_POWER_USE : IDLE_POWER_USE - active_power_usage = HOLOPAD_PASSIVE_POWER_USAGE + (HOLOGRAM_POWER_USAGE * total_users) - if(total_users) - set_light(2) - icon_state = "holopad1" - else - set_light(0) - icon_state = "holopad0" - update_icon() - -/obj/machinery/hologram/holopad/update_icon() - var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls) - if(icon_state == "holopad_open") - return - else if(ringing) - icon_state = "holopad_ringing" - else if(total_users) - icon_state = "holopad1" - else - icon_state = "holopad0" - - -/obj/machinery/hologram/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) - masters[user] = h - holorays[user] = new /obj/effect/overlay/holoray(loc) - var/mob/living/silicon/ai/AI = user - if(istype(AI)) - AI.current = src - SetLightsAndPower() - update_holoray(user, get_turf(loc)) - return TRUE - -/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/user) - qdel(masters[user]) // Get rid of user's hologram - unset_holo(user) - return TRUE - -/obj/machinery/hologram/holopad/proc/unset_holo(mob/living/user) - var/mob/living/silicon/ai/AI = user - if(istype(AI) && AI.current == src) - AI.current = null - masters -= user // Discard AI from the list of those who use holopad - qdel(holorays[user]) - holorays -= user - SetLightsAndPower() - return TRUE - -/obj/machinery/hologram/holopad/proc/update_holoray(mob/living/user, turf/new_turf) - var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] - var/obj/effect/overlay/holoray/ray = holorays[user] - var/disty = holo.y - ray.y - var/distx = holo.x - ray.x - var/newangle - if(!disty) - if(distx >= 0) - newangle = 90 - else - newangle = 270 - else - newangle = arctan(distx/disty) - if(disty < 0) - newangle += 180 - else if(distx < 0) - newangle += 360 - var/matrix/M = matrix() - if(get_dist(get_turf(holo), new_turf) <= 1) - animate(ray, transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle), time = 1) - else - ray.transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle) - - -/obj/effect/overlay/holo_pad_hologram - var/mob/living/Impersonation - var/datum/holocall/HC - -/obj/effect/overlay/holo_pad_hologram/Destroy() - Impersonation = null - if(!QDELETED(HC)) - HC.Disconnect(HC.calling_holopad) - return ..() - -/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) - return 1 - -/obj/effect/overlay/holo_pad_hologram/examine(mob/user) - if(Impersonation) - . = Impersonation.examine(user) - else - . = ..() - - -/obj/effect/overlay/holoray - name = "holoray" - icon = 'icons/effects/96x96.dmi' - icon_state = "holoray" - layer = FLY_LAYER - density = FALSE - anchored = TRUE - mouse_opacity = MOUSE_OPACITY_ICON - pixel_x = -32 - pixel_y = -32 - alpha = 100 - -/* - * Other Stuff: Is this even used? - */ -/obj/machinery/hologram/projector - name = "hologram projector" - desc = "It makes a hologram appear...with magnets or something..." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "hologram0" - -#undef HOLOPAD_PASSIVE_POWER_USAGE -#undef HOLOGRAM_POWER_USAGE +/* holograms! + * Contains: + * Holopad + * hologram + * Other stuff + */ + +/* +Revised. Original based on space ninja hologram code. Which is also mine. /N +How it works: +AI clicks on holopad in camera view. View centers on holopad. +AI clicks again on the holopad to display a hologram. hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad. +AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master. +Only one AI may project from a holopad at any given time. +AI may cancel the hologram at any time by clicking on the holopad once more. +Possible to do for anyone motivated enough: + Give an AI variable for different hologram icons. + Itegrate EMP effect to disable the unit. +*/ + + +/* + * Holopad + */ + +// HOLOPAD MODE +// 0 = RANGE BASED +// 1 = AREA BASED +#define HOLOPAD_PASSIVE_POWER_USAGE 1 +#define HOLOGRAM_POWER_USAGE 2 +#define RANGE_BASED 0 +#define AREA_BASED 1 + +#define HOLOPAD_MODE RANGE_BASED + +GLOBAL_LIST_EMPTY(holopads) + +/obj/machinery/hologram/holopad + name = "holopad" + desc = "It's a floor-mounted device for projecting holographic images." + icon_state = "holopad0" + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 5 + active_power_usage = 100 + layer = TURF_LAYER+0.1 //Preventing mice and drones from sneaking under them. + plane = FLOOR_PLANE + max_integrity = 300 + armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0) + var/list/masters = list()//List of living mobs that use the holopad + var/list/holorays = list()//Holoray-mob link. + var/last_request = 0 //to prevent request spam. ~Carn + var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. + var/temp = "" + var/list/holo_calls //array of /datum/holocalls + var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs + var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging + var/obj/effect/overlay/holoray/ray + var/ringing = FALSE + var/dialling_input = FALSE //The user is currently selecting where to send their call + +/obj/machinery/hologram/holopad/New() + ..() + GLOB.holopads += src + component_parts = list() + component_parts += new /obj/item/circuitboard/holopad(null) + component_parts += new /obj/item/stock_parts/capacitor(null) + RefreshParts() + +/obj/machinery/hologram/holopad/Destroy() + if(outgoing_call) + outgoing_call.ConnectionFailure(src) + + for(var/I in holo_calls) + var/datum/holocall/HC = I + HC.ConnectionFailure(src) + + for(var/I in masters) + clear_holo(I) + GLOB.holopads -= src + return ..() + +/obj/machinery/hologram/holopad/power_change() + if(powered()) + stat &= ~NOPOWER + else + stat |= NOPOWER + if(outgoing_call) + outgoing_call.ConnectionFailure(src) + +/obj/machinery/hologram/holopad/obj_break() + . = ..() + if(outgoing_call) + outgoing_call.ConnectionFailure(src) + +/obj/machinery/hologram/holopad/RefreshParts() + var/holograph_range = 4 + for(var/obj/item/stock_parts/capacitor/B in component_parts) + holograph_range += 1 * B.rating + holo_range = holograph_range + +/obj/machinery/hologram/holopad/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/hologram/holopad/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "holopad_open", "holopad0", I) + + +/obj/machinery/hologram/holopad/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I) + +/obj/machinery/hologram/holopad/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/hologram/holopad/attack_hand(mob/living/carbon/human/user) + if(..()) + return + + if(outgoing_call) + return + + user.set_machine(src) + interact(user) + +/obj/machinery/hologram/holopad/AltClick(mob/living/carbon/human/user) + if(..()) + return + if(isAI(user)) + hangup_all_calls() + return + +//Stop ringing the AI!! +/obj/machinery/hologram/holopad/proc/hangup_all_calls() + for(var/I in holo_calls) + var/datum/holocall/HC = I + HC.Disconnect(src) + +/obj/machinery/hologram/holopad/interact(mob/living/carbon/human/user) //Carn: hologram requests. + if(!istype(user)) + return + if(!anchored) + return + + var/dat + if(temp) + dat = temp + else + dat = "Request an AI's presence.
    " + dat += "Call another holopad.
    " + + if(LAZYLEN(holo_calls)) + dat += "=====================================================
    " + + var/one_answered_call = FALSE + var/one_unanswered_call = FALSE + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad != src) + dat += "Answer call from [get_area(HC.calling_holopad)].
    " + one_unanswered_call = TRUE + else + one_answered_call = TRUE + + if(one_answered_call && one_unanswered_call) + dat += "=====================================================
    " + //we loop twice for formatting + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad == src) + dat += "Disconnect call from [HC.user].
    " + + var/area/area = get_area(src) + var/datum/browser/popup = new(user, "holopad", "[area] holopad", 400, 300) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + +/obj/machinery/hologram/holopad/Topic(href, href_list) + if(..() || isAI(usr)) + return + add_fingerprint(usr) + if(stat & NOPOWER) + return + if(href_list["AIrequest"]) + if(last_request + 200 < world.time) + last_request = world.time + temp = "You requested an AI's presence.
    " + temp += "Main Menu" + var/area/area = get_area(src) + for(var/mob/living/silicon/ai/AI in GLOB.ai_list) + if(!AI.client) + continue + to_chat(AI, "Your presence is requested at \the [area].") + else + temp = "A request for AI presence was already sent recently.
    " + temp += "Main Menu" + + else if(href_list["Holocall"]) + if(outgoing_call) + return + if(dialling_input) + to_chat(usr, "Finish dialling first!") + return + temp = "You must stand on the holopad to make a call!
    " + temp += "Main Menu" + if(usr.loc == loc) + var/list/callnames = list() + for(var/I in GLOB.holopads) + var/area/A = get_area(I) + if(A) + LAZYADD(callnames[A], I) + callnames -= get_area(src) + var/list/sorted_callnames = sortAtom(callnames) + dialling_input = TRUE + var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sorted_callnames + dialling_input = FALSE + if(QDELETED(usr) || !result || outgoing_call) + return + + if(usr.loc == loc) + temp = "Dialing...
    " + temp += "Main Menu" + new /datum/holocall(usr, src, callnames[result]) + + else if(href_list["connectcall"]) + var/datum/holocall/call_to_connect = locateUID(href_list["connectcall"]) + if(!QDELETED(call_to_connect) && (call_to_connect in holo_calls)) + call_to_connect.Answer(src) + temp = "" + + else if(href_list["disconnectcall"]) + var/datum/holocall/call_to_disconnect = locateUID(href_list["disconnectcall"]) + if(!QDELETED(call_to_disconnect)) + call_to_disconnect.Disconnect(src) + temp = "" + + else if(href_list["mainmenu"]) + temp = "" + if(outgoing_call) + outgoing_call.Disconnect() + + updateDialog() + +//do not allow AIs to answer calls or people will use it to meta the AI satellite +/obj/machinery/hologram/holopad/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + if(outgoing_call) + return + /*There are pretty much only three ways to interact here. + I don't need to check for client since they're clicking on an object. + This may change in the future but for now will suffice.*/ + if(user.eyeobj.loc != loc)//Set client eye on the object if it's not already. + user.eyeobj.setLoc(get_turf(src)) + else if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one. + activate_holo(user, 1) + else//If there is a hologram, remove it. + clear_holo(user) + +/obj/machinery/hologram/holopad/process() + for(var/I in masters) + var/mob/living/master = I + if((stat & NOPOWER) || !validate_user(master) || !anchored) + clear_holo(master) + + if(outgoing_call) + outgoing_call.Check() + + ringing = FALSE + + for(var/I in holo_calls) + var/datum/holocall/HC = I + //Sanity check and skip if no longer valid call + if(!HC.Check()) + atom_say("Call was terminated at remote terminal.") + continue + + if(HC.connected_holopad != src) + if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) + HC.Answer(src) + break + if(outgoing_call) + HC.Disconnect(src)//can't answer calls while calling + else + playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring! + ringing = TRUE + + update_icon() + + +//Try to transfer hologram to another pad that can project on T +/obj/machinery/hologram/holopad/proc/transfer_to_nearby_pad(turf/T, mob/holo_owner) + if(!isAI(holo_owner)) + return + for(var/pad in GLOB.holopads) + var/obj/machinery/hologram/holopad/another = pad + if(another == src) + continue + if(another.validate_location(T)) + var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner] + unset_holo(holo_owner) + another.set_holo(holo_owner, h) + return TRUE + return FALSE + +/obj/machinery/hologram/holopad/proc/validate_user(mob/living/user) + if(QDELETED(user) || user.incapacitated() || !user.client) + return FALSE + + if(istype(user, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = user + if(!AI.current) + return FALSE + return TRUE + +//Can we display holos there +//Area check instead of line of sight check because this is a called a lot if AI wants to move around. +/obj/machinery/hologram/holopad/proc/validate_location(turf/T,check_los = FALSE) + if(T.z == z && get_dist(T, src) <= holo_range && T.loc == get_area(src)) + return TRUE + return FALSE + + +/obj/machinery/hologram/holopad/proc/move_hologram(mob/living/user, turf/new_turf) + if(masters[user]) + var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] + var/transfered = FALSE + if(!validate_location(new_turf)) + if(!transfer_to_nearby_pad(new_turf,user)) + clear_holo(user) + return FALSE + else + transfered = TRUE + //All is good. + holo.setDir(get_dir(holo.loc, new_turf)) + holo.forceMove(new_turf) + if(!transfered) + update_holoray(user,new_turf) + return TRUE + +/obj/machinery/hologram/holopad/proc/activate_holo(mob/living/user, var/force = 0) + var/mob/living/silicon/ai/AI = user + if(!istype(AI)) + AI = null + if(AI && !force && AI.eyeobj.loc != loc) // allows holopads to pass off holograms to the next holopad in the chain + to_chat(user, "ERROR: Unable to project hologram.") + if(!(stat & NOPOWER) && (!AI || force)) + if(AI && (istype(AI.current, /obj/machinery/hologram/holopad))) + to_chat(user, "ERROR: Image feed in progress.") + return + + var/obj/effect/overlay/holo_pad_hologram/hologram = new(loc)//Spawn a blank effect at the location. + if(isAI(user)) + hologram.icon = AI.holo_icon + else //make it like real life + hologram.icon = getHologramIcon(get_id_photo(user)) + hologram.icon_state = user.icon_state + hologram.alpha = 100 + hologram.Impersonation = user + + hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it. + hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + hologram.anchored = 1//So space wind cannot drag it. + hologram.name = "[user.name] (hologram)"//If someone decides to right click. + hologram.set_light(2) //hologram lighting + move_hologram() + + set_holo(user, hologram) + + if(!masters[user])//If there is not already a hologram. + visible_message("A holographic image of [user] flicks to life right before your eyes!") + + return hologram + + + to_chat(user, "ERROR: Hologram Projection Malfunction.") + clear_holo(user)//safety check + +/*This is the proc for special two-way communication between AI and holopad/people talking near holopad. +For the other part of the code, check silicon say.dm. Particularly robot talk.*/ +/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, list/message_pieces, verb) + if(speaker && masters.len)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios. + for(var/mob/living/silicon/ai/master in masters) + if(masters[master] && speaker != master) + master.relay_speech(speaker, message_pieces, verb) + + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad == src && speaker != HC.hologram) + HC.user.hear_say(message_pieces, verb, speaker = speaker) + + if(outgoing_call && speaker == outgoing_call.user) + outgoing_call.hologram.atom_say(multilingual_to_message(message_pieces)) + + + +/obj/machinery/hologram/holopad/proc/SetLightsAndPower() + var/total_users = masters.len + LAZYLEN(holo_calls) + use_power = total_users > 0 ? ACTIVE_POWER_USE : IDLE_POWER_USE + active_power_usage = HOLOPAD_PASSIVE_POWER_USAGE + (HOLOGRAM_POWER_USAGE * total_users) + if(total_users) + set_light(2) + icon_state = "holopad1" + else + set_light(0) + icon_state = "holopad0" + update_icon() + +/obj/machinery/hologram/holopad/update_icon() + var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls) + if(icon_state == "holopad_open") + return + else if(ringing) + icon_state = "holopad_ringing" + else if(total_users) + icon_state = "holopad1" + else + icon_state = "holopad0" + + +/obj/machinery/hologram/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) + masters[user] = h + holorays[user] = new /obj/effect/overlay/holoray(loc) + var/mob/living/silicon/ai/AI = user + if(istype(AI)) + AI.current = src + SetLightsAndPower() + update_holoray(user, get_turf(loc)) + return TRUE + +/obj/machinery/hologram/holopad/proc/clear_holo(mob/living/user) + qdel(masters[user]) // Get rid of user's hologram + unset_holo(user) + return TRUE + +/obj/machinery/hologram/holopad/proc/unset_holo(mob/living/user) + var/mob/living/silicon/ai/AI = user + if(istype(AI) && AI.current == src) + AI.current = null + masters -= user // Discard AI from the list of those who use holopad + qdel(holorays[user]) + holorays -= user + SetLightsAndPower() + return TRUE + +/obj/machinery/hologram/holopad/proc/update_holoray(mob/living/user, turf/new_turf) + var/obj/effect/overlay/holo_pad_hologram/holo = masters[user] + var/obj/effect/overlay/holoray/ray = holorays[user] + var/disty = holo.y - ray.y + var/distx = holo.x - ray.x + var/newangle + if(!disty) + if(distx >= 0) + newangle = 90 + else + newangle = 270 + else + newangle = arctan(distx/disty) + if(disty < 0) + newangle += 180 + else if(distx < 0) + newangle += 360 + var/matrix/M = matrix() + if(get_dist(get_turf(holo), new_turf) <= 1) + animate(ray, transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle), time = 1) + else + ray.transform = turn(M.Scale(1, sqrt(distx*distx+disty*disty)), newangle) + + +/obj/effect/overlay/holo_pad_hologram + var/mob/living/Impersonation + var/datum/holocall/HC + +/obj/effect/overlay/holo_pad_hologram/Destroy() + Impersonation = null + if(!QDELETED(HC)) + HC.Disconnect(HC.calling_holopad) + return ..() + +/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) + return 1 + +/obj/effect/overlay/holo_pad_hologram/examine(mob/user) + if(Impersonation) + . = Impersonation.examine(user) + else + . = ..() + + +/obj/effect/overlay/holoray + name = "holoray" + icon = 'icons/effects/96x96.dmi' + icon_state = "holoray" + layer = FLY_LAYER + density = FALSE + anchored = TRUE + mouse_opacity = MOUSE_OPACITY_ICON + pixel_x = -32 + pixel_y = -32 + alpha = 100 + +/* + * Other Stuff: Is this even used? + */ +/obj/machinery/hologram/projector + name = "hologram projector" + desc = "It makes a hologram appear...with magnets or something..." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "hologram0" + +#undef HOLOPAD_PASSIVE_POWER_USAGE +#undef HOLOGRAM_POWER_USAGE diff --git a/code/game/machinery/igniter.dm b/code/game/machinery/igniter.dm index e4bdb0d5478d..c6918d795ff3 100755 --- a/code/game/machinery/igniter.dm +++ b/code/game/machinery/igniter.dm @@ -1,126 +1,126 @@ -/obj/machinery/igniter - name = "igniter" - desc = "It's useful for igniting plasma." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "igniter1" - plane = FLOOR_PLANE - max_integrity = 300 - armor = list(melee = 50, bullet = 30, laser = 70, energy = 50, bomb = 20, bio = 0, rad = 0, fire = 100, acid = 70) - resistance_flags = FIRE_PROOF - var/id = null - var/on = FALSE - anchored = TRUE - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/igniter/on - on = TRUE - -/obj/machinery/igniter/attack_ai(mob/user as mob) - return src.attack_hand(user) - - -/obj/machinery/igniter/attack_hand(mob/user as mob) - if(..()) - return - add_fingerprint(user) - - use_power(50) - src.on = !( src.on ) - src.icon_state = text("igniter[]", src.on) - return - -/obj/machinery/igniter/process() //ugh why is this even in process()? - if(src.on && !(stat & NOPOWER) ) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/igniter/New() - ..() - icon_state = "igniter[on]" - -/obj/machinery/igniter/power_change() - if(!( stat & NOPOWER) ) - icon_state = "igniter[src.on]" - else - icon_state = "igniter0" - -// Wall mounted remote-control igniter. - -/obj/machinery/sparker - name = "Mounted igniter" - desc = "A wall-mounted ignition device." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "migniter" - resistance_flags = FIRE_PROOF - var/id = null - var/disable = FALSE - var/last_spark = FALSE - var/base_state = "migniter" - anchored = 1 - -/obj/machinery/sparker/New() - ..() - -/obj/machinery/sparker/power_change() - if( powered() && disable == 0 ) - stat &= ~NOPOWER - icon_state = "[base_state]" -// src.sd_set_light(2) - else - stat |= ~NOPOWER - icon_state = "[base_state]-p" -// src.sd_set_light(0) - -/obj/machinery/sparker/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/detective_scanner)) - return - return ..() - -/obj/machinery/sparker/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - disable = !disable - if(disable) - user.visible_message("[user] has disabled [src]!", "You disable the connection to [src].") - icon_state = "[base_state]-d" - if(!disable) - user.visible_message("[user] has reconnected [src]!", "You fix the connection to [src].") - if(powered()) - icon_state = "[base_state]" - else - icon_state = "[base_state]-p" - -/obj/machinery/sparker/attack_ai() - if(src.anchored) - return src.spark() - else - return - -/obj/machinery/sparker/proc/spark() - if(!(powered())) - return - - if((src.disable) || (src.last_spark && world.time < src.last_spark + 50)) - return - - - flick("[base_state]-spark", src) - do_sparks(2, 1, src) - src.last_spark = world.time - use_power(1000) - var/turf/location = src.loc - if(isturf(location)) - location.hotspot_expose(1000,500,1) - return 1 - -/obj/machinery/sparker/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - spark() - ..(severity) +/obj/machinery/igniter + name = "igniter" + desc = "It's useful for igniting plasma." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "igniter1" + plane = FLOOR_PLANE + max_integrity = 300 + armor = list(melee = 50, bullet = 30, laser = 70, energy = 50, bomb = 20, bio = 0, rad = 0, fire = 100, acid = 70) + resistance_flags = FIRE_PROOF + var/id = null + var/on = FALSE + anchored = TRUE + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/igniter/on + on = TRUE + +/obj/machinery/igniter/attack_ai(mob/user as mob) + return src.attack_hand(user) + + +/obj/machinery/igniter/attack_hand(mob/user as mob) + if(..()) + return + add_fingerprint(user) + + use_power(50) + src.on = !( src.on ) + src.icon_state = text("igniter[]", src.on) + return + +/obj/machinery/igniter/process() //ugh why is this even in process()? + if(src.on && !(stat & NOPOWER) ) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/igniter/New() + ..() + icon_state = "igniter[on]" + +/obj/machinery/igniter/power_change() + if(!( stat & NOPOWER) ) + icon_state = "igniter[src.on]" + else + icon_state = "igniter0" + +// Wall mounted remote-control igniter. + +/obj/machinery/sparker + name = "Mounted igniter" + desc = "A wall-mounted ignition device." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "migniter" + resistance_flags = FIRE_PROOF + var/id = null + var/disable = FALSE + var/last_spark = FALSE + var/base_state = "migniter" + anchored = 1 + +/obj/machinery/sparker/New() + ..() + +/obj/machinery/sparker/power_change() + if( powered() && disable == 0 ) + stat &= ~NOPOWER + icon_state = "[base_state]" +// src.sd_set_light(2) + else + stat |= ~NOPOWER + icon_state = "[base_state]-p" +// src.sd_set_light(0) + +/obj/machinery/sparker/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/detective_scanner)) + return + return ..() + +/obj/machinery/sparker/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + disable = !disable + if(disable) + user.visible_message("[user] has disabled [src]!", "You disable the connection to [src].") + icon_state = "[base_state]-d" + if(!disable) + user.visible_message("[user] has reconnected [src]!", "You fix the connection to [src].") + if(powered()) + icon_state = "[base_state]" + else + icon_state = "[base_state]-p" + +/obj/machinery/sparker/attack_ai() + if(src.anchored) + return src.spark() + else + return + +/obj/machinery/sparker/proc/spark() + if(!(powered())) + return + + if((src.disable) || (src.last_spark && world.time < src.last_spark + 50)) + return + + + flick("[base_state]-spark", src) + do_sparks(2, 1, src) + src.last_spark = world.time + use_power(1000) + var/turf/location = src.loc + if(isturf(location)) + location.hotspot_expose(1000,500,1) + return 1 + +/obj/machinery/sparker/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + spark() + ..(severity) diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 204a85b426e6..d7b21983792f 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -1,75 +1,75 @@ -#define IV_TAKING 0 -#define IV_INJECTING 1 - -/obj/machinery/iv_drip - name = "\improper IV drip" - icon = 'icons/goonstation/objects/iv.dmi' - icon_state = "stand" - anchored = FALSE - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - var/obj/item/reagent_containers/iv_bag/bag = null - -/obj/machinery/iv_drip/update_icon() - cut_overlays() - - if(bag) - add_overlay("hangingbag") - if(bag.reagents.total_volume) - var/image/filling = image('icons/goonstation/objects/iv.dmi', src, "hangingbag-fluid") - filling.icon += mix_color_from_reagents(bag.reagents.reagent_list) - add_overlay(filling) - -/obj/machinery/iv_drip/MouseDrop(mob/living/target) - if(usr.incapacitated()) - return - - if(!ishuman(usr) || !iscarbon(target)) - return - - if(Adjacent(target) && usr.Adjacent(target)) - bag.afterattack(target, usr, TRUE) - -/obj/machinery/iv_drip/attack_hand(mob/user) - if(bag) - user.put_in_hands(bag) - bag.update_icon() - bag = null - update_icon() - -/obj/machinery/iv_drip/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/iv_bag)) - if(bag) - to_chat(user, "[src] already has an IV bag!") - return - if(!user.drop_item()) - return - - I.forceMove(src) - bag = I - to_chat(user, "You attach [I] to [src].") - update_icon() - else if (bag && istype(I, /obj/item/reagent_containers)) - bag.attackby(I) - I.afterattack(bag, usr, TRUE) - update_icon() - else - return ..() - -/obj/machinery/iv_drip/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc) - qdel(src) - -/obj/machinery/iv_drip/examine(mob/user) - . = ..() - if(bag) - . += bag.examine(user) - -/obj/machinery/iv_drip/Move(NewLoc, direct) - . = ..() - if(!.) // ..() will return 0 if we didn't actually move anywhere. - return . - playsound(loc, pick('sound/items/cartwheel1.ogg', 'sound/items/cartwheel2.ogg'), 100, 1, ignore_walls = FALSE) - -#undef IV_TAKING -#undef IV_INJECTING \ No newline at end of file +#define IV_TAKING 0 +#define IV_INJECTING 1 + +/obj/machinery/iv_drip + name = "\improper IV drip" + icon = 'icons/goonstation/objects/iv.dmi' + icon_state = "stand" + anchored = FALSE + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + var/obj/item/reagent_containers/iv_bag/bag = null + +/obj/machinery/iv_drip/update_icon() + cut_overlays() + + if(bag) + add_overlay("hangingbag") + if(bag.reagents.total_volume) + var/image/filling = image('icons/goonstation/objects/iv.dmi', src, "hangingbag-fluid") + filling.icon += mix_color_from_reagents(bag.reagents.reagent_list) + add_overlay(filling) + +/obj/machinery/iv_drip/MouseDrop(mob/living/target) + if(usr.incapacitated()) + return + + if(!ishuman(usr) || !iscarbon(target)) + return + + if(Adjacent(target) && usr.Adjacent(target)) + bag.afterattack(target, usr, TRUE) + +/obj/machinery/iv_drip/attack_hand(mob/user) + if(bag) + user.put_in_hands(bag) + bag.update_icon() + bag = null + update_icon() + +/obj/machinery/iv_drip/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/iv_bag)) + if(bag) + to_chat(user, "[src] already has an IV bag!") + return + if(!user.drop_item()) + return + + I.forceMove(src) + bag = I + to_chat(user, "You attach [I] to [src].") + update_icon() + else if (bag && istype(I, /obj/item/reagent_containers)) + bag.attackby(I) + I.afterattack(bag, usr, TRUE) + update_icon() + else + return ..() + +/obj/machinery/iv_drip/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc) + qdel(src) + +/obj/machinery/iv_drip/examine(mob/user) + . = ..() + if(bag) + . += bag.examine(user) + +/obj/machinery/iv_drip/Move(NewLoc, direct) + . = ..() + if(!.) // ..() will return 0 if we didn't actually move anywhere. + return . + playsound(loc, pick('sound/items/cartwheel1.ogg', 'sound/items/cartwheel2.ogg'), 100, 1, ignore_walls = FALSE) + +#undef IV_TAKING +#undef IV_INJECTING diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm index 13406ac4b0fd..53995f420d95 100644 --- a/code/game/machinery/lightswitch.dm +++ b/code/game/machinery/lightswitch.dm @@ -1,183 +1,183 @@ -// the light switch -// can have multiple per area -// can also operate on non-loc area through "otherarea" var -/obj/machinery/light_switch - name = "light switch" - desc = "It turns lights on and off. What are you, simple?" - icon = 'icons/obj/power.dmi' - icon_state = "light1" - anchored = 1.0 - var/on = 1 - var/area/area = null - var/otherarea = null - // luminosity = 1 - settagwhitelist = list("logic_id_tag") - var/light_connect = 1 //Allows the switch to control lights in its associated areas. When set to 0, using the switch won't affect the lights. - var/datum/radio_frequency/radio_connection - var/frequency = 0 - var/logic_id_tag = "default" //Defines the ID tag to send logic signals to. - var/logic_connect = 0 //Set this to allow the switch to send out logic signals. - - -/obj/machinery/light_switch/New(turf/loc, var/w_dir=null) - ..() - switch(w_dir) - if(NORTH) - pixel_y = 25 - if(SOUTH) - pixel_y = -25 - if(EAST) - pixel_x = 25 - if(WEST) - pixel_x = -25 - if(SSradio) - set_frequency(frequency) - spawn(5) - src.area = get_area(src) - - if(otherarea) - src.area = locate(text2path("/area/[otherarea]")) - - if(!name) - name = "light switch([area.name])" - - src.on = src.area.lightswitch - updateicon() - -/obj/machinery/light_switch/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/light_switch/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) - return - -/obj/machinery/light_switch/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/light_switch/proc/updateicon() - if(stat & NOPOWER) - icon_state = "light-p" - else - if(on) - icon_state = "light1" - else - icon_state = "light0" - -/obj/machinery/light_switch/examine(mob/user) - . = ..() - . += "A light switch. It is [on? "on" : "off"]." - -/obj/machinery/light_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/light_switch/attack_hand(mob/user) - on = !on - updateicon() - - if(light_connect) - area.lightswitch = on - area.updateicon() - - if(logic_connect && powered(LIGHT)) //Don't bother sending a signal if we aren't set to send them or we have no power to send with. - handle_output() - - if(light_connect) - for(var/obj/machinery/light_switch/L in area) - L.on = on - L.updateicon() - - area.power_change() - -/obj/machinery/light_switch/proc/handle_output() - if(!radio_connection) //can't output without this - return - - if(logic_id_tag == null) //Don't output to an undefined id_tag - return - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - //Light switches are continuous signal sources, since they register as ON or OFF and stay that way until adjusted again - if(on) - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_ON, - ) - else - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_OFF, - ) - - radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) - if(on) - use_power(5, LIGHT) //Use a tiny bit of power every time we send an ON signal. Draws from the local APC's lighting circuit, since this is a LIGHT switch. - -/obj/machinery/light_switch/power_change() - if(!otherarea) - if(powered(LIGHT)) - stat &= ~NOPOWER - else - stat |= NOPOWER - - updateicon() - -/obj/machinery/light_switch/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - power_change() - ..(severity) - -/obj/machinery/light_switch/process() - if(logic_connect && powered(LIGHT)) //We won't send signals while unpowered, but the last signal will remain valid for anything that received it before we went dark - handle_output() - -/obj/machinery/light_switch/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/detective_scanner)) - return - return ..() - -/obj/machinery/light_switch/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - update_multitool_menu(user) - -/obj/machinery/light_switch/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] starts unwrenching [src] from the wall...", "You are unwrenching [src] from the wall...", "You hear ratcheting.") - . = TRUE - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - WRENCH_UNANCHOR_WALL_MESSAGE - new/obj/item/mounted/frame/light_switch(get_turf(src)) - qdel(src) - -/obj/machinery/light_switch/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" - "} - -/obj/machinery/light_switch/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - ..() - if("toggle_light_connect" in href_list) - light_connect = !light_connect - if("toggle_logic" in href_list) - logic_connect = !logic_connect +// the light switch +// can have multiple per area +// can also operate on non-loc area through "otherarea" var +/obj/machinery/light_switch + name = "light switch" + desc = "It turns lights on and off. What are you, simple?" + icon = 'icons/obj/power.dmi' + icon_state = "light1" + anchored = 1.0 + var/on = 1 + var/area/area = null + var/otherarea = null + // luminosity = 1 + settagwhitelist = list("logic_id_tag") + var/light_connect = 1 //Allows the switch to control lights in its associated areas. When set to 0, using the switch won't affect the lights. + var/datum/radio_frequency/radio_connection + var/frequency = 0 + var/logic_id_tag = "default" //Defines the ID tag to send logic signals to. + var/logic_connect = 0 //Set this to allow the switch to send out logic signals. + + +/obj/machinery/light_switch/New(turf/loc, var/w_dir=null) + ..() + switch(w_dir) + if(NORTH) + pixel_y = 25 + if(SOUTH) + pixel_y = -25 + if(EAST) + pixel_x = 25 + if(WEST) + pixel_x = -25 + if(SSradio) + set_frequency(frequency) + spawn(5) + src.area = get_area(src) + + if(otherarea) + src.area = locate(text2path("/area/[otherarea]")) + + if(!name) + name = "light switch([area.name])" + + src.on = src.area.lightswitch + updateicon() + +/obj/machinery/light_switch/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/light_switch/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) + return + +/obj/machinery/light_switch/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/light_switch/proc/updateicon() + if(stat & NOPOWER) + icon_state = "light-p" + else + if(on) + icon_state = "light1" + else + icon_state = "light0" + +/obj/machinery/light_switch/examine(mob/user) + . = ..() + . += "A light switch. It is [on? "on" : "off"]." + +/obj/machinery/light_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/light_switch/attack_hand(mob/user) + on = !on + updateicon() + + if(light_connect) + area.lightswitch = on + area.updateicon() + + if(logic_connect && powered(LIGHT)) //Don't bother sending a signal if we aren't set to send them or we have no power to send with. + handle_output() + + if(light_connect) + for(var/obj/machinery/light_switch/L in area) + L.on = on + L.updateicon() + + area.power_change() + +/obj/machinery/light_switch/proc/handle_output() + if(!radio_connection) //can't output without this + return + + if(logic_id_tag == null) //Don't output to an undefined id_tag + return + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + //Light switches are continuous signal sources, since they register as ON or OFF and stay that way until adjusted again + if(on) + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_ON, + ) + else + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_OFF, + ) + + radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) + if(on) + use_power(5, LIGHT) //Use a tiny bit of power every time we send an ON signal. Draws from the local APC's lighting circuit, since this is a LIGHT switch. + +/obj/machinery/light_switch/power_change() + if(!otherarea) + if(powered(LIGHT)) + stat &= ~NOPOWER + else + stat |= NOPOWER + + updateicon() + +/obj/machinery/light_switch/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + power_change() + ..(severity) + +/obj/machinery/light_switch/process() + if(logic_connect && powered(LIGHT)) //We won't send signals while unpowered, but the last signal will remain valid for anything that received it before we went dark + handle_output() + +/obj/machinery/light_switch/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/detective_scanner)) + return + return ..() + +/obj/machinery/light_switch/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + update_multitool_menu(user) + +/obj/machinery/light_switch/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] starts unwrenching [src] from the wall...", "You are unwrenching [src] from the wall...", "You hear ratcheting.") + . = TRUE + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + WRENCH_UNANCHOR_WALL_MESSAGE + new/obj/item/mounted/frame/light_switch(get_turf(src)) + qdel(src) + +/obj/machinery/light_switch/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" + "} + +/obj/machinery/light_switch/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + ..() + if("toggle_light_connect" in href_list) + light_connect = !light_connect + if("toggle_logic" in href_list) + logic_connect = !logic_connect diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm index f1ac4e88bbbb..8d8cabed3417 100644 --- a/code/game/machinery/machinery.dm +++ b/code/game/machinery/machinery.dm @@ -1,596 +1,596 @@ -/* -Overview: - Used to create objects that need a per step proc call. Default definition of 'New()' - stores a reference to src machine in global 'machines list'. Default definition - of 'Del' removes reference to src machine in global 'machines list'. - -Class Variables: - use_power (num) - current state of auto power use. - Possible Values: - 0 -- no auto power use - 1 -- machine is using power at its idle power level - 2 -- machine is using power at its active power level - - active_power_usage (num) - Value for the amount of power to use when in active power mode - - idle_power_usage (num) - Value for the amount of power to use when in idle power mode - - power_channel (num) - What channel to draw from when drawing power for power mode - Possible Values: - EQUIP:0 -- Equipment Channel - LIGHT:2 -- Lighting Channel - ENVIRON:3 -- Environment Channel - - component_parts (list) - A list of component parts of machine used by frame based machines. - - uid (num) - Unique id of machine across all machines. - - gl_uid (global num) - Next uid value in sequence - - stat (bitflag) - Machine status bit flags. - Possible bit flags: - BROKEN:1 -- Machine is broken - NOPOWER:2 -- No power is being supplied to machine. - POWEROFF:4 -- tbd - MAINT:8 -- machine is currently under going maintenance. - EMPED:16 -- temporary broken by EMP pulse - - manual (num) - Currently unused. - -Class Procs: - initialize() 'game/machinery/machine.dm' - - Destroy() 'game/machinery/machine.dm' - - auto_use_power() 'game/machinery/machine.dm' - This proc determines how power mode power is deducted by the machine. - 'auto_use_power()' is called by the 'master_controller' game_controller every - tick. - - Return Value: - return:1 -- if object is powered - return:0 -- if object is not powered. - - Default definition uses 'use_power', 'power_channel', 'active_power_usage', - 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. - - powered(chan = EQUIP) 'modules/power/power.dm' - Checks to see if area that contains the object has power available for power - channel given in 'chan'. - - use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm' - Deducts 'amount' from the power channel 'chan' of the area that contains the object. - If it's autocalled then everything is normal, if something else calls use_power we are going to - need to recalculate the power two ticks in a row. - - power_change() 'modules/power/power.dm' - Called by the area that contains the object when ever that area under goes a - power state change (area runs out of power, or area channel is turned off). - - RefreshParts() 'game/machinery/machine.dm' - Called to refresh the variables in the machine that are contributed to by parts - contained in the component_parts list. (example: glass and material amounts for - the autolathe) - - Default definition does nothing. - - assign_uid() 'game/machinery/machine.dm' - Called by machine to assign a value to the uid variable. - - process() 'game/machinery/machine.dm' - Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. - - - Compiled by Aygar -*/ - -/obj/machinery - name = "machinery" - icon = 'icons/obj/stationobjs.dmi' - pressure_resistance = 15 - max_integrity = 200 - layer = BELOW_OBJ_LAYER - var/stat = 0 - var/emagged = 0 - var/use_power = IDLE_POWER_USE - //0 = dont run the auto - //1 = run auto, use idle - //2 = run auto, use active - var/idle_power_usage = 0 - var/active_power_usage = 0 - var/power_channel = EQUIP //EQUIP,ENVIRON or LIGHT - var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. - var/uid - var/manual = 0 - var/global/gl_uid = 1 - var/custom_aghost_alerts=0 - var/panel_open = 0 - var/area/myArea - var/interact_offline = 0 // Can the machine be interacted with while de-powered. - var/use_log = list() - var/list/settagwhitelist = list()//WHITELIST OF VARIABLES THAT THE set_tag HREF CAN MODIFY, DON'T PUT SHIT YOU DON'T NEED ON HERE, AND IF YOU'RE GONNA USE set_tag (format_tag() proc), ADD TO THIS LIST. - atom_say_verb = "beeps" - var/siemens_strength = 0.7 // how badly will it shock you? - -/obj/machinery/Initialize(mapload) - if(!armor) - armor = list(melee = 25, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) - . = ..() - GLOB.machines += src - - if(use_power) - myArea = get_area(src) - if(!speed_process) - START_PROCESSING(SSmachines, src) - else - START_PROCESSING(SSfastprocess, src) - - power_change() - -// gotta go fast -/obj/machinery/makeSpeedProcess() - if(speed_process) - return - speed_process = TRUE - STOP_PROCESSING(SSmachines, src) - START_PROCESSING(SSfastprocess, src) - -// gotta go slow -/obj/machinery/makeNormalProcess() - if(!speed_process) - return - speed_process = FALSE - STOP_PROCESSING(SSfastprocess, src) - START_PROCESSING(SSmachines, src) - -/obj/machinery/Destroy() - if(myArea) - myArea = null - GLOB.machines.Remove(src) - if(!speed_process) - STOP_PROCESSING(SSmachines, src) - else - STOP_PROCESSING(SSfastprocess, src) - return ..() - -/obj/machinery/proc/locate_machinery() - return - -/obj/machinery/process() // If you dont use process or power why are you here - return PROCESS_KILL - -/obj/machinery/proc/process_atmos() //If you dont use process why are you here - return PROCESS_KILL - -/obj/machinery/emp_act(severity) - if(use_power && !stat) - use_power(7500/severity) - new /obj/effect/temp_visual/emp(loc) - ..() -/obj/machinery/default_welder_repair(mob/user, obj/item/I) - . = ..() - if(.) - stat &= ~BROKEN - -//sets the use_power var and then forces an area power update -/obj/machinery/proc/update_use_power(var/new_use_power) - use_power = new_use_power - -/obj/machinery/proc/auto_use_power() - if(!powered(power_channel)) - return 0 - if(use_power == IDLE_POWER_USE) - use_power(idle_power_usage,power_channel, 1) - else if(use_power >= ACTIVE_POWER_USE) - use_power(active_power_usage,power_channel, 1) - return 1 - -/obj/machinery/proc/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - if("set_id" in href_list) - if(!("id_tag" in vars)) - warning("set_id: [type] has no id_tag var.") - var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID tag for this machine", src, src:id_tag) as null|text),1,MAX_MESSAGE_LEN) - if(newid) - src:id_tag = newid - return TRUE - if("set_freq" in href_list) - if(!("frequency" in vars)) - warning("set_freq: [type] has no frequency var.") - return FALSE - var/newfreq=src:frequency - if(href_list["set_freq"]!="-1") - newfreq=text2num(href_list["set_freq"]) - else - newfreq = input(usr, "Specify a new frequency (GHz). Decimals assigned automatically.", src, src:frequency) as null|num - if(newfreq) - if(findtext(num2text(newfreq), ".")) - newfreq *= 10 // shift the decimal one place - src:frequency = sanitize_frequency(newfreq, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - return TRUE - return FALSE - -/obj/machinery/proc/handle_multitool_topic(var/href, var/list/href_list, var/mob/user) - if(!allowed(user))//no, not even HREF exploits - return FALSE - var/obj/item/multitool/P = get_multitool(usr) - if(P && istype(P)) - var/update_mt_menu = FALSE - if("set_tag" in href_list) - if(!(href_list["set_tag"] in settagwhitelist))//I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. (seriously though, this is a powerfull HREF, I originally found this loophole, I'm not leaving it in on my PR) - message_admins("set_tag HREF (var attempted to edit: [href_list["set_tag"]]) exploit attempted by [key_name_admin(user)] on [src] (JMP)") - return FALSE - if(!(href_list["set_tag"] in vars)) - to_chat(usr, "Something went wrong: Unable to find [href_list["set_tag"]] in vars!") - return FALSE - var/current_tag = vars[href_list["set_tag"]] - var/newid = copytext(reject_bad_text(input(usr, "Specify the new value", src, current_tag) as null|text),1,MAX_MESSAGE_LEN) - if(newid) - vars[href_list["set_tag"]] = newid - update_mt_menu = TRUE - - if("unlink" in href_list) - var/idx = text2num(href_list["unlink"]) - if(!idx) - return FALSE - - var/obj/O = getLink(idx) - if(!O) - return FALSE - if(!canLink(O)) - to_chat(usr, "You can't link with that device.") - return FALSE - - if(unlinkFrom(usr, O)) - to_chat(usr, "A green light flashes on \the [P], confirming the link was removed.") - else - to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when unlinking the two devices.") - update_mt_menu = TRUE - - if("link" in href_list) - var/obj/O = P.buffer - if(!O) - return FALSE - if(!canLink(O,href_list)) - to_chat(usr, "You can't link with that device.") - return FALSE - if(isLinkedWith(O)) - to_chat(usr, "A red light flashes on \the [P]. The two devices are already linked.") - return FALSE - - if(linkWith(usr, O, href_list)) - to_chat(usr, "A green light flashes on \the [P], confirming the link was added.") - else - to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when linking the two devices.") - update_mt_menu = TRUE - - if("buffer" in href_list) - P.buffer = src - to_chat(usr, "A green light flashes, and the device appears in the multitool buffer.") - update_mt_menu = TRUE - - if("flush" in href_list) - to_chat(usr, "A green light flashes, and the device disappears from the multitool buffer.") - P.buffer = null - update_mt_menu = TRUE - - var/ret = multitool_topic(usr,href_list,P.buffer) - if(ret) - update_mt_menu = TRUE - - if(update_mt_menu) - update_multitool_menu(usr) - return TRUE - -/obj/machinery/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = default_state) - if(..(href, href_list, nowindow, state)) - return 1 - - handle_multitool_topic(href,href_list,usr) - add_fingerprint(usr) - return 0 - -/obj/machinery/proc/operable(var/additional_flags = 0) - return !inoperable(additional_flags) - -/obj/machinery/proc/inoperable(var/additional_flags = 0) - return (stat & (NOPOWER|BROKEN|additional_flags)) - -/obj/machinery/CanUseTopic(var/mob/user) - if(!interact_offline && (stat & (NOPOWER|BROKEN))) - return STATUS_CLOSE - - return ..() - -/obj/machinery/CouldUseTopic(var/mob/user) - ..() - user.set_machine(src) - -/obj/machinery/CouldNotUseTopic(var/mob/user) - usr.unset_machine() - -/obj/machinery/proc/dropContents()//putting for swarmers, occupent code commented out, someone can use later. - var/turf/T = get_turf(src) - for(var/atom/movable/AM in contents) - AM.forceMove(T) - -//////////////////////////////////////////////////////////////////////////////////////////// - -/obj/machinery/attack_ai(mob/user) - if(isrobot(user))// For some reason attack_robot doesn't work - var/mob/living/silicon/robot/R = user - if(R.client && R.client.eye == R && !R.low_power_mode)// This is to stop robots from using cameras to remotely control machines; and from using machines when the borg has no power. - return attack_hand(user) - else - return attack_hand(user) - -/obj/machinery/attack_hand(mob/user as mob) - if(user.incapacitated()) - return TRUE - - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return TRUE - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.getBrainLoss() >= 60) - visible_message("[H] stares cluelessly at [src] and drools.") - return TRUE - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return TRUE - - if(panel_open) - add_fingerprint(user) - return FALSE - - if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT)) - return TRUE - - add_fingerprint(user) - - return ..() - -/obj/machinery/proc/is_operational() - return !(stat & (NOPOWER|BROKEN|MAINT)) - -/obj/machinery/CheckParts(list/parts_list) - ..() - RefreshParts() - -/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. - return - return 0 - -/obj/machinery/proc/assign_uid() - uid = gl_uid - gl_uid++ - -/obj/machinery/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - on_deconstruction() - if(component_parts && component_parts.len) - spawn_frame(disassembled) - for(var/obj/item/I in component_parts) - I.forceMove(loc) - component_parts.Cut() - qdel(src) - -/obj/machinery/proc/spawn_frame(disassembled) - var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) - . = M - M.anchored = anchored - if(!disassembled) - M.obj_integrity = M.max_integrity * 0.5 //the frame is already half broken - transfer_fingerprints_to(M) - M.state = 2 - M.icon_state = "box_1" - -/obj/machinery/obj_break(damage_flag) - if(!(flags & NODECONSTRUCT)) - stat |= BROKEN - -/obj/machinery/proc/default_deconstruction_crowbar(user, obj/item/I, ignore_panel = 0) - if(I.tool_behaviour != TOOL_CROWBAR) - return FALSE - if(!I.use_tool(src, user, 0, volume = 0)) - return FALSE - if((panel_open || ignore_panel) && !(flags & NODECONSTRUCT)) - deconstruct(TRUE) - to_chat(user, "You disassemble [src].") - I.play_tool_sound(user, I.tool_volume) - return 1 - return 0 - -/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I) - if(I.tool_behaviour != TOOL_SCREWDRIVER) - return FALSE - if(!I.use_tool(src, user, 0, volume = 0)) - return FALSE - if(!(flags & NODECONSTRUCT)) - if(!panel_open) - panel_open = 1 - icon_state = icon_state_open - to_chat(user, "You open the maintenance hatch of [src].") - else - panel_open = 0 - icon_state = icon_state_closed - to_chat(user, "You close the maintenance hatch of [src].") - I.play_tool_sound(user, I.tool_volume) - return 1 - return 0 - -/obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/I) - if(I.tool_behaviour != TOOL_WRENCH) - return FALSE - if(!I.use_tool(src, user, 0, volume = 0)) - return FALSE - if(panel_open) - dir = turn(dir,-90) - to_chat(user, "You rotate [src].") - I.play_tool_sound(user, I.tool_volume) - return TRUE - return FALSE - -/obj/machinery/default_unfasten_wrench(mob/user, obj/item/I, time) - . = ..() - if(.) - power_change() - -/obj/machinery/proc/exchange_parts(mob/user, obj/item/storage/part_replacer/W) - var/shouldplaysound = 0 - if((flags & NODECONSTRUCT)) - return FALSE - if(istype(W) && component_parts) - if(panel_open || W.works_from_distance) - var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts - var/P - if(W.works_from_distance) - to_chat(user, display_parts(user)) - for(var/obj/item/stock_parts/A in component_parts) - for(var/D in CB.req_components) - if(ispath(A.type, D)) - P = D - break - for(var/obj/item/stock_parts/B in W.contents) - if(istype(B, P) && istype(A, P)) - if(B.rating > A.rating) - W.remove_from_storage(B, src) - W.handle_item_insertion(A, 1) - component_parts -= A - component_parts += B - B.loc = null - to_chat(user, "[A.name] replaced with [B.name].") - shouldplaysound = 1 - break - RefreshParts() - else - to_chat(user, display_parts(user)) - if(shouldplaysound) - W.play_rped_sound() - return 1 - else - return 0 - -/obj/machinery/proc/display_parts(mob/user) - . = list("Following parts detected in the machine:") - for(var/obj/item/C in component_parts) - . += "[bicon(C)] [C.name]" - . = jointext(., "\n") - -/obj/machinery/examine(mob/user) - . = ..() - if(stat & BROKEN) - . += "It looks broken and non-functional." - if(!(resistance_flags & INDESTRUCTIBLE)) - if(resistance_flags & ON_FIRE) - . += "It's on fire!" - var/healthpercent = (obj_integrity/max_integrity) * 100 - switch(healthpercent) - if(50 to 99) - . += "It looks slightly damaged." - if(25 to 50) - . += "It appears heavily damaged." - if(0 to 25) - . += "It's falling apart!" - if(user.research_scanner && component_parts) - . += display_parts(user) - -/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp) - return 0 - -/obj/machinery/proc/is_assess_emagged() - return emagged - -/obj/machinery/proc/assess_perp(mob/living/carbon/human/perp, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) - var/threatcount = 0 //the integer returned - - if(is_assess_emagged()) - return 10 //if emagged, always return 10. - - threatcount += on_assess_perp(perp) - if(threatcount >= 10) - return threatcount - - //Agent cards lower threatlevel. - var/obj/item/card/id/id = GetIdCard(perp) - if(id && istype(id, /obj/item/card/id/syndicate)) - threatcount -= 2 - // A proper CentCom id is hard currency. - else if(id && istype(id, /obj/item/card/id/centcom)) - threatcount -= 2 - - if(check_access && !allowed(perp)) - threatcount += 4 - - if(auth_weapons && !allowed(perp)) - if(istype(perp.l_hand, /obj/item/gun) || istype(perp.l_hand, /obj/item/melee)) - threatcount += 4 - - if(istype(perp.r_hand, /obj/item/gun) || istype(perp.r_hand, /obj/item/melee)) - threatcount += 4 - - if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee)) - threatcount += 2 - - if(!ishumanbasic(perp)) //beepsky so racist. - threatcount += 2 - - if(check_records || check_arrest) - var/perpname = perp.get_visible_name(TRUE) - - var/datum/data/record/R = find_security_record("name", perpname) - if(check_records && !R) - threatcount += 4 - - if(check_arrest && R && (R.fields["criminal"] == "*Arrest*")) - threatcount += 4 - - return threatcount - - -/obj/machinery/proc/shock(mob/user, prb) - if(inoperable()) - return FALSE - if(!prob(prb)) - return FALSE - do_sparks(5, 1, src) - if(electrocute_mob(user, get_area(src), src, siemens_strength, TRUE)) - return TRUE - return FALSE - -//called on machinery construction (i.e from frame to machinery) but not on initialization -/obj/machinery/proc/on_construction() - return - -/obj/machinery/proc/on_deconstruction() - return - -/obj/machinery/proc/can_be_overridden() - . = 1 - -/obj/machinery/tesla_act(power, explosive = FALSE) - ..() - if(prob(85) && explosive) - explosion(loc, 1, 2, 4, flame_range = 2, adminlog = 0, smoke = 0) - else if(prob(50)) - emp_act(EMP_LIGHT) - else - ex_act(EXPLODE_HEAVY) - -/obj/machinery/proc/adjust_item_drop_location(atom/movable/AM) // Adjust item drop location to a 3x3 grid inside the tile, returns slot id from 0 to 8 - var/md5 = md5(AM.name) // Oh, and it's deterministic too. A specific item will always drop from the same slot. - for (var/i in 1 to 32) - . += hex2num(md5[i]) - . = . % 9 - AM.pixel_x = -8 + ((.%3)*8) - AM.pixel_y = -8 + (round( . / 3)*8) +/* +Overview: + Used to create objects that need a per step proc call. Default definition of 'New()' + stores a reference to src machine in global 'machines list'. Default definition + of 'Del' removes reference to src machine in global 'machines list'. + +Class Variables: + use_power (num) + current state of auto power use. + Possible Values: + 0 -- no auto power use + 1 -- machine is using power at its idle power level + 2 -- machine is using power at its active power level + + active_power_usage (num) + Value for the amount of power to use when in active power mode + + idle_power_usage (num) + Value for the amount of power to use when in idle power mode + + power_channel (num) + What channel to draw from when drawing power for power mode + Possible Values: + EQUIP:0 -- Equipment Channel + LIGHT:2 -- Lighting Channel + ENVIRON:3 -- Environment Channel + + component_parts (list) + A list of component parts of machine used by frame based machines. + + uid (num) + Unique id of machine across all machines. + + gl_uid (global num) + Next uid value in sequence + + stat (bitflag) + Machine status bit flags. + Possible bit flags: + BROKEN:1 -- Machine is broken + NOPOWER:2 -- No power is being supplied to machine. + POWEROFF:4 -- tbd + MAINT:8 -- machine is currently under going maintenance. + EMPED:16 -- temporary broken by EMP pulse + + manual (num) + Currently unused. + +Class Procs: + initialize() 'game/machinery/machine.dm' + + Destroy() 'game/machinery/machine.dm' + + auto_use_power() 'game/machinery/machine.dm' + This proc determines how power mode power is deducted by the machine. + 'auto_use_power()' is called by the 'master_controller' game_controller every + tick. + + Return Value: + return:1 -- if object is powered + return:0 -- if object is not powered. + + Default definition uses 'use_power', 'power_channel', 'active_power_usage', + 'idle_power_usage', 'powered()', and 'use_power()' implement behavior. + + powered(chan = EQUIP) 'modules/power/power.dm' + Checks to see if area that contains the object has power available for power + channel given in 'chan'. + + use_power(amount, chan=EQUIP, autocalled) 'modules/power/power.dm' + Deducts 'amount' from the power channel 'chan' of the area that contains the object. + If it's autocalled then everything is normal, if something else calls use_power we are going to + need to recalculate the power two ticks in a row. + + power_change() 'modules/power/power.dm' + Called by the area that contains the object when ever that area under goes a + power state change (area runs out of power, or area channel is turned off). + + RefreshParts() 'game/machinery/machine.dm' + Called to refresh the variables in the machine that are contributed to by parts + contained in the component_parts list. (example: glass and material amounts for + the autolathe) + + Default definition does nothing. + + assign_uid() 'game/machinery/machine.dm' + Called by machine to assign a value to the uid variable. + + process() 'game/machinery/machine.dm' + Called by the 'master_controller' once per game tick for each machine that is listed in the 'machines' list. + + + Compiled by Aygar +*/ + +/obj/machinery + name = "machinery" + icon = 'icons/obj/stationobjs.dmi' + pressure_resistance = 15 + max_integrity = 200 + layer = BELOW_OBJ_LAYER + var/stat = 0 + var/emagged = 0 + var/use_power = IDLE_POWER_USE + //0 = dont run the auto + //1 = run auto, use idle + //2 = run auto, use active + var/idle_power_usage = 0 + var/active_power_usage = 0 + var/power_channel = EQUIP //EQUIP,ENVIRON or LIGHT + var/list/component_parts = null //list of all the parts used to build it, if made from certain kinds of frames. + var/uid + var/manual = 0 + var/global/gl_uid = 1 + var/custom_aghost_alerts=0 + var/panel_open = 0 + var/area/myArea + var/interact_offline = 0 // Can the machine be interacted with while de-powered. + var/use_log = list() + var/list/settagwhitelist = list()//WHITELIST OF VARIABLES THAT THE set_tag HREF CAN MODIFY, DON'T PUT SHIT YOU DON'T NEED ON HERE, AND IF YOU'RE GONNA USE set_tag (format_tag() proc), ADD TO THIS LIST. + atom_say_verb = "beeps" + var/siemens_strength = 0.7 // how badly will it shock you? + +/obj/machinery/Initialize(mapload) + if(!armor) + armor = list(melee = 25, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) + . = ..() + GLOB.machines += src + + if(use_power) + myArea = get_area(src) + if(!speed_process) + START_PROCESSING(SSmachines, src) + else + START_PROCESSING(SSfastprocess, src) + + power_change() + +// gotta go fast +/obj/machinery/makeSpeedProcess() + if(speed_process) + return + speed_process = TRUE + STOP_PROCESSING(SSmachines, src) + START_PROCESSING(SSfastprocess, src) + +// gotta go slow +/obj/machinery/makeNormalProcess() + if(!speed_process) + return + speed_process = FALSE + STOP_PROCESSING(SSfastprocess, src) + START_PROCESSING(SSmachines, src) + +/obj/machinery/Destroy() + if(myArea) + myArea = null + GLOB.machines.Remove(src) + if(!speed_process) + STOP_PROCESSING(SSmachines, src) + else + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/obj/machinery/proc/locate_machinery() + return + +/obj/machinery/process() // If you dont use process or power why are you here + return PROCESS_KILL + +/obj/machinery/proc/process_atmos() //If you dont use process why are you here + return PROCESS_KILL + +/obj/machinery/emp_act(severity) + if(use_power && !stat) + use_power(7500/severity) + new /obj/effect/temp_visual/emp(loc) + ..() +/obj/machinery/default_welder_repair(mob/user, obj/item/I) + . = ..() + if(.) + stat &= ~BROKEN + +//sets the use_power var and then forces an area power update +/obj/machinery/proc/update_use_power(var/new_use_power) + use_power = new_use_power + +/obj/machinery/proc/auto_use_power() + if(!powered(power_channel)) + return 0 + if(use_power == IDLE_POWER_USE) + use_power(idle_power_usage,power_channel, 1) + else if(use_power >= ACTIVE_POWER_USE) + use_power(active_power_usage,power_channel, 1) + return 1 + +/obj/machinery/proc/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + if("set_id" in href_list) + if(!("id_tag" in vars)) + warning("set_id: [type] has no id_tag var.") + var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID tag for this machine", src, src:id_tag) as null|text),1,MAX_MESSAGE_LEN) + if(newid) + src:id_tag = newid + return TRUE + if("set_freq" in href_list) + if(!("frequency" in vars)) + warning("set_freq: [type] has no frequency var.") + return FALSE + var/newfreq=src:frequency + if(href_list["set_freq"]!="-1") + newfreq=text2num(href_list["set_freq"]) + else + newfreq = input(usr, "Specify a new frequency (GHz). Decimals assigned automatically.", src, src:frequency) as null|num + if(newfreq) + if(findtext(num2text(newfreq), ".")) + newfreq *= 10 // shift the decimal one place + src:frequency = sanitize_frequency(newfreq, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + return TRUE + return FALSE + +/obj/machinery/proc/handle_multitool_topic(var/href, var/list/href_list, var/mob/user) + if(!allowed(user))//no, not even HREF exploits + return FALSE + var/obj/item/multitool/P = get_multitool(usr) + if(P && istype(P)) + var/update_mt_menu = FALSE + if("set_tag" in href_list) + if(!(href_list["set_tag"] in settagwhitelist))//I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. (seriously though, this is a powerfull HREF, I originally found this loophole, I'm not leaving it in on my PR) + message_admins("set_tag HREF (var attempted to edit: [href_list["set_tag"]]) exploit attempted by [key_name_admin(user)] on [src] (JMP)") + return FALSE + if(!(href_list["set_tag"] in vars)) + to_chat(usr, "Something went wrong: Unable to find [href_list["set_tag"]] in vars!") + return FALSE + var/current_tag = vars[href_list["set_tag"]] + var/newid = copytext(reject_bad_text(input(usr, "Specify the new value", src, current_tag) as null|text),1,MAX_MESSAGE_LEN) + if(newid) + vars[href_list["set_tag"]] = newid + update_mt_menu = TRUE + + if("unlink" in href_list) + var/idx = text2num(href_list["unlink"]) + if(!idx) + return FALSE + + var/obj/O = getLink(idx) + if(!O) + return FALSE + if(!canLink(O)) + to_chat(usr, "You can't link with that device.") + return FALSE + + if(unlinkFrom(usr, O)) + to_chat(usr, "A green light flashes on \the [P], confirming the link was removed.") + else + to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when unlinking the two devices.") + update_mt_menu = TRUE + + if("link" in href_list) + var/obj/O = P.buffer + if(!O) + return FALSE + if(!canLink(O,href_list)) + to_chat(usr, "You can't link with that device.") + return FALSE + if(isLinkedWith(O)) + to_chat(usr, "A red light flashes on \the [P]. The two devices are already linked.") + return FALSE + + if(linkWith(usr, O, href_list)) + to_chat(usr, "A green light flashes on \the [P], confirming the link was added.") + else + to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when linking the two devices.") + update_mt_menu = TRUE + + if("buffer" in href_list) + P.buffer = src + to_chat(usr, "A green light flashes, and the device appears in the multitool buffer.") + update_mt_menu = TRUE + + if("flush" in href_list) + to_chat(usr, "A green light flashes, and the device disappears from the multitool buffer.") + P.buffer = null + update_mt_menu = TRUE + + var/ret = multitool_topic(usr,href_list,P.buffer) + if(ret) + update_mt_menu = TRUE + + if(update_mt_menu) + update_multitool_menu(usr) + return TRUE + +/obj/machinery/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = GLOB.default_state) + if(..(href, href_list, nowindow, state)) + return 1 + + handle_multitool_topic(href,href_list,usr) + add_fingerprint(usr) + return 0 + +/obj/machinery/proc/operable(var/additional_flags = 0) + return !inoperable(additional_flags) + +/obj/machinery/proc/inoperable(var/additional_flags = 0) + return (stat & (NOPOWER|BROKEN|additional_flags)) + +/obj/machinery/CanUseTopic(var/mob/user) + if(!interact_offline && (stat & (NOPOWER|BROKEN))) + return STATUS_CLOSE + + return ..() + +/obj/machinery/CouldUseTopic(var/mob/user) + ..() + user.set_machine(src) + +/obj/machinery/CouldNotUseTopic(var/mob/user) + usr.unset_machine() + +/obj/machinery/proc/dropContents()//putting for swarmers, occupent code commented out, someone can use later. + var/turf/T = get_turf(src) + for(var/atom/movable/AM in contents) + AM.forceMove(T) + +//////////////////////////////////////////////////////////////////////////////////////////// + +/obj/machinery/attack_ai(mob/user) + if(isrobot(user))// For some reason attack_robot doesn't work + var/mob/living/silicon/robot/R = user + if(R.client && R.client.eye == R && !R.low_power_mode)// This is to stop robots from using cameras to remotely control machines; and from using machines when the borg has no power. + return attack_hand(user) + else + return attack_hand(user) + +/obj/machinery/attack_hand(mob/user as mob) + if(user.incapacitated()) + return TRUE + + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return TRUE + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.getBrainLoss() >= 60) + visible_message("[H] stares cluelessly at [src] and drools.") + return TRUE + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return TRUE + + if(panel_open) + add_fingerprint(user) + return FALSE + + if(!interact_offline && stat & (NOPOWER|BROKEN|MAINT)) + return TRUE + + add_fingerprint(user) + + return ..() + +/obj/machinery/proc/is_operational() + return !(stat & (NOPOWER|BROKEN|MAINT)) + +/obj/machinery/CheckParts(list/parts_list) + ..() + RefreshParts() + +/obj/machinery/proc/RefreshParts() //Placeholder proc for machines that are built using frames. + return + return 0 + +/obj/machinery/proc/assign_uid() + uid = gl_uid + gl_uid++ + +/obj/machinery/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + on_deconstruction() + if(component_parts && component_parts.len) + spawn_frame(disassembled) + for(var/obj/item/I in component_parts) + I.forceMove(loc) + component_parts.Cut() + qdel(src) + +/obj/machinery/proc/spawn_frame(disassembled) + var/obj/machinery/constructable_frame/machine_frame/M = new /obj/machinery/constructable_frame/machine_frame(loc) + . = M + M.anchored = anchored + if(!disassembled) + M.obj_integrity = M.max_integrity * 0.5 //the frame is already half broken + transfer_fingerprints_to(M) + M.state = 2 + M.icon_state = "box_1" + +/obj/machinery/obj_break(damage_flag) + if(!(flags & NODECONSTRUCT)) + stat |= BROKEN + +/obj/machinery/proc/default_deconstruction_crowbar(user, obj/item/I, ignore_panel = 0) + if(I.tool_behaviour != TOOL_CROWBAR) + return FALSE + if(!I.use_tool(src, user, 0, volume = 0)) + return FALSE + if((panel_open || ignore_panel) && !(flags & NODECONSTRUCT)) + deconstruct(TRUE) + to_chat(user, "You disassemble [src].") + I.play_tool_sound(user, I.tool_volume) + return 1 + return 0 + +/obj/machinery/proc/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/I) + if(I.tool_behaviour != TOOL_SCREWDRIVER) + return FALSE + if(!I.use_tool(src, user, 0, volume = 0)) + return FALSE + if(!(flags & NODECONSTRUCT)) + if(!panel_open) + panel_open = 1 + icon_state = icon_state_open + to_chat(user, "You open the maintenance hatch of [src].") + else + panel_open = 0 + icon_state = icon_state_closed + to_chat(user, "You close the maintenance hatch of [src].") + I.play_tool_sound(user, I.tool_volume) + return 1 + return 0 + +/obj/machinery/proc/default_change_direction_wrench(mob/user, obj/item/I) + if(I.tool_behaviour != TOOL_WRENCH) + return FALSE + if(!I.use_tool(src, user, 0, volume = 0)) + return FALSE + if(panel_open) + dir = turn(dir,-90) + to_chat(user, "You rotate [src].") + I.play_tool_sound(user, I.tool_volume) + return TRUE + return FALSE + +/obj/machinery/default_unfasten_wrench(mob/user, obj/item/I, time) + . = ..() + if(.) + power_change() + +/obj/machinery/proc/exchange_parts(mob/user, obj/item/storage/part_replacer/W) + var/shouldplaysound = 0 + if((flags & NODECONSTRUCT)) + return FALSE + if(istype(W) && component_parts) + if(panel_open || W.works_from_distance) + var/obj/item/circuitboard/CB = locate(/obj/item/circuitboard) in component_parts + var/P + if(W.works_from_distance) + to_chat(user, display_parts(user)) + for(var/obj/item/stock_parts/A in component_parts) + for(var/D in CB.req_components) + if(ispath(A.type, D)) + P = D + break + for(var/obj/item/stock_parts/B in W.contents) + if(istype(B, P) && istype(A, P)) + if(B.rating > A.rating) + W.remove_from_storage(B, src) + W.handle_item_insertion(A, 1) + component_parts -= A + component_parts += B + B.loc = null + to_chat(user, "[A.name] replaced with [B.name].") + shouldplaysound = 1 + break + RefreshParts() + else + to_chat(user, display_parts(user)) + if(shouldplaysound) + W.play_rped_sound() + return 1 + else + return 0 + +/obj/machinery/proc/display_parts(mob/user) + . = list("Following parts detected in the machine:") + for(var/obj/item/C in component_parts) + . += "[bicon(C)] [C.name]" + . = jointext(., "\n") + +/obj/machinery/examine(mob/user) + . = ..() + if(stat & BROKEN) + . += "It looks broken and non-functional." + if(!(resistance_flags & INDESTRUCTIBLE)) + if(resistance_flags & ON_FIRE) + . += "It's on fire!" + var/healthpercent = (obj_integrity/max_integrity) * 100 + switch(healthpercent) + if(50 to 99) + . += "It looks slightly damaged." + if(25 to 50) + . += "It appears heavily damaged." + if(0 to 25) + . += "It's falling apart!" + if(user.research_scanner && component_parts) + . += display_parts(user) + +/obj/machinery/proc/on_assess_perp(mob/living/carbon/human/perp) + return 0 + +/obj/machinery/proc/is_assess_emagged() + return emagged + +/obj/machinery/proc/assess_perp(mob/living/carbon/human/perp, var/check_access, var/auth_weapons, var/check_records, var/check_arrest) + var/threatcount = 0 //the integer returned + + if(is_assess_emagged()) + return 10 //if emagged, always return 10. + + threatcount += on_assess_perp(perp) + if(threatcount >= 10) + return threatcount + + //Agent cards lower threatlevel. + var/obj/item/card/id/id = GetIdCard(perp) + if(id && istype(id, /obj/item/card/id/syndicate)) + threatcount -= 2 + // A proper CentCom id is hard currency. + else if(id && istype(id, /obj/item/card/id/centcom)) + threatcount -= 2 + + if(check_access && !allowed(perp)) + threatcount += 4 + + if(auth_weapons && !allowed(perp)) + if(istype(perp.l_hand, /obj/item/gun) || istype(perp.l_hand, /obj/item/melee)) + threatcount += 4 + + if(istype(perp.r_hand, /obj/item/gun) || istype(perp.r_hand, /obj/item/melee)) + threatcount += 4 + + if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee)) + threatcount += 2 + + if(!ishumanbasic(perp)) //beepsky so racist. + threatcount += 2 + + if(check_records || check_arrest) + var/perpname = perp.get_visible_name(TRUE) + + var/datum/data/record/R = find_security_record("name", perpname) + if(check_records && !R) + threatcount += 4 + + if(check_arrest && R && (R.fields["criminal"] == "*Arrest*")) + threatcount += 4 + + return threatcount + + +/obj/machinery/proc/shock(mob/user, prb) + if(inoperable()) + return FALSE + if(!prob(prb)) + return FALSE + do_sparks(5, 1, src) + if(electrocute_mob(user, get_area(src), src, siemens_strength, TRUE)) + return TRUE + return FALSE + +//called on machinery construction (i.e from frame to machinery) but not on initialization +/obj/machinery/proc/on_construction() + return + +/obj/machinery/proc/on_deconstruction() + return + +/obj/machinery/proc/can_be_overridden() + . = 1 + +/obj/machinery/tesla_act(power, explosive = FALSE) + ..() + if(prob(85) && explosive) + explosion(loc, 1, 2, 4, flame_range = 2, adminlog = 0, smoke = 0) + else if(prob(50)) + emp_act(EMP_LIGHT) + else + ex_act(EXPLODE_HEAVY) + +/obj/machinery/proc/adjust_item_drop_location(atom/movable/AM) // Adjust item drop location to a 3x3 grid inside the tile, returns slot id from 0 to 8 + var/md5 = md5(AM.name) // Oh, and it's deterministic too. A specific item will always drop from the same slot. + for (var/i in 1 to 32) + . += hex2num(md5[i]) + . = . % 9 + AM.pixel_x = -8 + ((.%3)*8) + AM.pixel_y = -8 + (round( . / 3)*8) diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 9b982df99fdc..b73fa94259f0 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -1,386 +1,386 @@ -// Magnetic attractor, creates variable magnetic fields and attraction. -// Can also be used to emit electron/proton beams to create a center of magnetism on another tile - -// tl;dr: it's magnets lol -// This was created for firing ranges, but I suppose this could have other applications - Doohl - -/obj/machinery/magnetic_module - - icon = 'icons/obj/objects.dmi' - icon_state = "floor_magnet-f" - name = "Electromagnetic Generator" - desc = "A device that uses station power to create points of magnetic energy." - level = 1 // underfloor - layer = 2.5 - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 50 - - var/freq = AIRLOCK_FREQ // radio frequency - var/electricity_level = 1 // intensity of the magnetic pull - var/magnetic_field = 1 // the range of magnetic attraction - var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something - var/turf/center // the center of magnetic attraction - var/on = 0 - var/magpulling = 0 - - // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down - var/center_x = 0 - var/center_y = 0 - var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer - -/obj/machinery/magnetic_module/New() - ..() - var/turf/T = loc - hide(T.intact) - center = T - - spawn(10) // must wait for map loading to finish - if(SSradio) - SSradio.add_object(src, freq, RADIO_MAGNETS) - - spawn() - magnetic_process() - - // update the invisibility and icon -/obj/machinery/magnetic_module/hide(intact) - invisibility = intact ? 101 : 0 - updateicon() - - // update the icon_state -/obj/machinery/magnetic_module/proc/updateicon() - var/state="floor_magnet" - var/onstate="" - if(!on) - onstate="0" - - if(invisibility) - icon_state = "[state][onstate]-f" // if invisible, set icon to faded version - // in case of being revealed by T-scanner - else - icon_state = "[state][onstate]" - -/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) - var/command = signal.data["command"] - var/modifier = signal.data["modifier"] - var/signal_code = signal.data["code"] - if(command && (signal_code == code)) - Cmd(command, modifier) - - - -/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) - if(command) - switch(command) - if("set-electriclevel") - if(modifier) electricity_level = modifier - if("set-magneticfield") - if(modifier) magnetic_field = modifier - - if("add-elec") - electricity_level++ - if(electricity_level > 12) - electricity_level = 12 - if("sub-elec") - electricity_level-- - if(electricity_level <= 0) - electricity_level = 1 - if("add-mag") - magnetic_field++ - if(magnetic_field > 4) - magnetic_field = 4 - if("sub-mag") - magnetic_field-- - if(magnetic_field <= 0) - magnetic_field = 1 - - if("set-x") - if(modifier) center_x = modifier - if("set-y") - if(modifier) center_y = modifier - - if("N") // NORTH - center_y++ - if("S") // SOUTH - center_y-- - if("E") // EAST - center_x++ - if("W") // WEST - center_x-- - if("C") // CENTER - center_x = 0 - center_y = 0 - if("R") // RANDOM - center_x = rand(-max_dist, max_dist) - center_y = rand(-max_dist, max_dist) - - if("set-code") - if(modifier) code = modifier - if("toggle-power") - on = !on - - if(on) - spawn() - magnetic_process() - -/obj/machinery/magnetic_module/process() - if(stat & NOPOWER) - on = 0 - - // Sanity checks: - if(electricity_level <= 0) - electricity_level = 1 - if(magnetic_field <= 0) - magnetic_field = 1 - - - // Limitations: - if(abs(center_x) > max_dist) - center_x = max_dist - if(abs(center_y) > max_dist) - center_y = max_dist - if(magnetic_field > 4) - magnetic_field = 4 - if(electricity_level > 12) - electricity_level = 12 - - // Update power usage: - if(on) - use_power = 2 - active_power_usage = electricity_level*15 - else - use_power = 0 - updateicon() - - -/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling - if(magpulling) return - while(on) - - magpulling = 1 - center = locate(x+center_x, y+center_y, z) - if(center) - for(var/obj/M in orange(magnetic_field, center)) - if(!M.anchored && (M.flags & CONDUCT)) - step_towards(M, center) - - for(var/mob/living/silicon/S in orange(magnetic_field, center)) - if(istype(S, /mob/living/silicon/ai)) continue - step_towards(S, center) - - use_power(electricity_level * 5) - sleep(13 - electricity_level) - - magpulling = 0 - -/obj/machinery/magnetic_controller - name = "Magnetic Control Console" - icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! - icon_state = "airlock_control_standby" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 45 - var/frequency = AIRLOCK_FREQ - var/code = 0 - var/list/magnets = list() - var/title = "Magnetic Control Console" - var/autolink = 0 // if set to 1, can't probe for other magnets! - - var/pathpos = 1 // position in the path - var/path = "NULL" // text path of the magnet - var/speed = 1 // lowest = 1, highest = 10 - var/list/rpath = list() // real path of the magnet, used in iterator - - var/moving = 0 // 1 if scheduled to loop - var/looping = 0 // 1 if looping - - var/datum/radio_frequency/radio_connection - - -/obj/machinery/magnetic_controller/New() - ..() - - if(autolink) - for(var/obj/machinery/magnetic_module/M in world) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - - spawn(45) // must wait for map loading to finish - if(SSradio) - radio_connection = SSradio.add_object(src, frequency, RADIO_MAGNETS) - - - if(path) // check for default path - filter_path() // renders rpath - - -/obj/machinery/magnetic_controller/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/magnetic_controller/process() - if(magnets.len == 0 && autolink) - for(var/obj/machinery/magnetic_module/M in world) - if(M.freq == frequency && M.code == code) - magnets.Add(M) - - -/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "Magnetic Control Console

    " - if(!autolink) - dat += {" - Frequency: [frequency]
    - Code: [code]
    - Probe Generators
    - "} - - if(magnets.len >= 1) - - dat += "Magnets confirmed:
    " - var/i = 0 - for(var/obj/machinery/magnetic_module/M in magnets) - i++ - dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " - - dat += "
    Speed: - [speed] +
    " - dat += "Path: {[path]}
    " - dat += "Moving: [moving ? "Enabled":"Disabled"]" - - - user << browse(dat, "window=magnet;size=400x500") - onclose(user, "magnet") - -/obj/machinery/magnetic_controller/Topic(href, href_list) - if(stat & (BROKEN|NOPOWER)) - return - usr.set_machine(src) - src.add_fingerprint(usr) - - if(href_list["radio-op"]) - - // Prepare signal beforehand, because this is a radio operation - var/datum/signal/signal = new - signal.transmission_method = 1 // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - // Apply any necessary commands - switch(href_list["radio-op"]) - if("togglepower") - signal.data["command"] = "toggle-power" - - if("minuselec") - signal.data["command"] = "sub-elec" - if("pluselec") - signal.data["command"] = "add-elec" - - if("minusmag") - signal.data["command"] = "sub-mag" - if("plusmag") - signal.data["command"] = "add-mag" - - - // Broadcast the signal - - radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) - - spawn(1) - updateUsrDialog() // pretty sure this increases responsiveness - - if(href_list["operation"]) - switch(href_list["operation"]) - if("plusspeed") - speed++ - if(speed > 10) - speed = 10 - if("minusspeed") - speed -- - if(speed <= 0) - speed = 1 - if("setpath") - var/newpath = sanitize(copytext(input(usr, "Please define a new path!",,path) as text|null,1,MAX_MESSAGE_LEN)) - if(newpath && newpath != "") - moving = 0 // stop moving - path = newpath - pathpos = 1 // reset position - filter_path() // renders rpath - - if("togglemoving") - moving = !moving - if(moving) - spawn() MagnetMove() - - - updateUsrDialog() - -/obj/machinery/magnetic_controller/proc/MagnetMove() - if(looping) return - - while(moving && rpath.len >= 1) - - if(stat & (BROKEN|NOPOWER)) - break - - looping = 1 - - // Prepare the radio signal - var/datum/signal/signal = new - signal.transmission_method = 1 // radio transmission - signal.source = src - signal.frequency = frequency - signal.data["code"] = code - - if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! - pathpos = 1 - - var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive - - if(!(nextmove in list("N","S","E","W","C","R"))) - // N, S, E, W are directional - // C is center - // R is random (in magnetic field's bounds) - qdel(signal) - break // break the loop if the character located is invalid - - signal.data["command"] = nextmove - - - pathpos++ // increase iterator - - // Broadcast the signal - spawn() - radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) - - if(speed == 10) - sleep(1) - else - sleep(12-speed) - - looping = 0 - - -/obj/machinery/magnetic_controller/proc/filter_path() - // Generates the rpath variable using the path string, think of this as "string2list" - // Doesn't use params2list() because of the akward way it stacks entities - rpath = list() // clear rpath - var/maximum_character = min( 50, length(path) ) // chooses the maximum length of the iterator. 50 max length - - for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path - - var/nextchar = copytext(path, i, i+1) // find next character - - if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore - rpath += copytext(path, i, i+1) // else, add to list - - // there doesn't HAVE to be separators but it makes paths syntatically visible +// Magnetic attractor, creates variable magnetic fields and attraction. +// Can also be used to emit electron/proton beams to create a center of magnetism on another tile + +// tl;dr: it's magnets lol +// This was created for firing ranges, but I suppose this could have other applications - Doohl + +/obj/machinery/magnetic_module + + icon = 'icons/obj/objects.dmi' + icon_state = "floor_magnet-f" + name = "Electromagnetic Generator" + desc = "A device that uses station power to create points of magnetic energy." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 50 + + var/freq = AIRLOCK_FREQ // radio frequency + var/electricity_level = 1 // intensity of the magnetic pull + var/magnetic_field = 1 // the range of magnetic attraction + var/code = 0 // frequency code, they should be different unless you have a group of magnets working together or something + var/turf/center // the center of magnetic attraction + var/on = 0 + var/magpulling = 0 + + // x, y modifiers to the center turf; (0, 0) is centered on the magnet, whereas (1, -1) is one tile right, one tile down + var/center_x = 0 + var/center_y = 0 + var/max_dist = 20 // absolute value of center_x,y cannot exceed this integer + +/obj/machinery/magnetic_module/New() + ..() + var/turf/T = loc + hide(T.intact) + center = T + + spawn(10) // must wait for map loading to finish + if(SSradio) + SSradio.add_object(src, freq, RADIO_MAGNETS) + + spawn() + magnetic_process() + + // update the invisibility and icon +/obj/machinery/magnetic_module/hide(intact) + invisibility = intact ? 101 : 0 + updateicon() + + // update the icon_state +/obj/machinery/magnetic_module/proc/updateicon() + var/state="floor_magnet" + var/onstate="" + if(!on) + onstate="0" + + if(invisibility) + icon_state = "[state][onstate]-f" // if invisible, set icon to faded version + // in case of being revealed by T-scanner + else + icon_state = "[state][onstate]" + +/obj/machinery/magnetic_module/receive_signal(datum/signal/signal) + var/command = signal.data["command"] + var/modifier = signal.data["modifier"] + var/signal_code = signal.data["code"] + if(command && (signal_code == code)) + Cmd(command, modifier) + + + +/obj/machinery/magnetic_module/proc/Cmd(var/command, var/modifier) + if(command) + switch(command) + if("set-electriclevel") + if(modifier) electricity_level = modifier + if("set-magneticfield") + if(modifier) magnetic_field = modifier + + if("add-elec") + electricity_level++ + if(electricity_level > 12) + electricity_level = 12 + if("sub-elec") + electricity_level-- + if(electricity_level <= 0) + electricity_level = 1 + if("add-mag") + magnetic_field++ + if(magnetic_field > 4) + magnetic_field = 4 + if("sub-mag") + magnetic_field-- + if(magnetic_field <= 0) + magnetic_field = 1 + + if("set-x") + if(modifier) center_x = modifier + if("set-y") + if(modifier) center_y = modifier + + if("N") // NORTH + center_y++ + if("S") // SOUTH + center_y-- + if("E") // EAST + center_x++ + if("W") // WEST + center_x-- + if("C") // CENTER + center_x = 0 + center_y = 0 + if("R") // RANDOM + center_x = rand(-max_dist, max_dist) + center_y = rand(-max_dist, max_dist) + + if("set-code") + if(modifier) code = modifier + if("toggle-power") + on = !on + + if(on) + spawn() + magnetic_process() + +/obj/machinery/magnetic_module/process() + if(stat & NOPOWER) + on = 0 + + // Sanity checks: + if(electricity_level <= 0) + electricity_level = 1 + if(magnetic_field <= 0) + magnetic_field = 1 + + + // Limitations: + if(abs(center_x) > max_dist) + center_x = max_dist + if(abs(center_y) > max_dist) + center_y = max_dist + if(magnetic_field > 4) + magnetic_field = 4 + if(electricity_level > 12) + electricity_level = 12 + + // Update power usage: + if(on) + use_power = 2 + active_power_usage = electricity_level*15 + else + use_power = 0 + updateicon() + + +/obj/machinery/magnetic_module/proc/magnetic_process() // proc that actually does the pulling + if(magpulling) return + while(on) + + magpulling = 1 + center = locate(x+center_x, y+center_y, z) + if(center) + for(var/obj/M in orange(magnetic_field, center)) + if(!M.anchored && (M.flags & CONDUCT)) + step_towards(M, center) + + for(var/mob/living/silicon/S in orange(magnetic_field, center)) + if(istype(S, /mob/living/silicon/ai)) continue + step_towards(S, center) + + use_power(electricity_level * 5) + sleep(13 - electricity_level) + + magpulling = 0 + +/obj/machinery/magnetic_controller + name = "Magnetic Control Console" + icon = 'icons/obj/airlock_machines.dmi' // uses an airlock machine icon, THINK GREEN HELP THE ENVIRONMENT - RECYCLING! + icon_state = "airlock_control_standby" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 45 + var/frequency = AIRLOCK_FREQ + var/code = 0 + var/list/magnets = list() + var/title = "Magnetic Control Console" + var/autolink = 0 // if set to 1, can't probe for other magnets! + + var/pathpos = 1 // position in the path + var/path = "NULL" // text path of the magnet + var/speed = 1 // lowest = 1, highest = 10 + var/list/rpath = list() // real path of the magnet, used in iterator + + var/moving = 0 // 1 if scheduled to loop + var/looping = 0 // 1 if looping + + var/datum/radio_frequency/radio_connection + + +/obj/machinery/magnetic_controller/New() + ..() + + if(autolink) + for(var/obj/machinery/magnetic_module/M in world) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + + spawn(45) // must wait for map loading to finish + if(SSradio) + radio_connection = SSradio.add_object(src, frequency, RADIO_MAGNETS) + + + if(path) // check for default path + filter_path() // renders rpath + + +/obj/machinery/magnetic_controller/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/magnetic_controller/process() + if(magnets.len == 0 && autolink) + for(var/obj/machinery/magnetic_module/M in world) + if(M.freq == frequency && M.code == code) + magnets.Add(M) + + +/obj/machinery/magnetic_controller/attack_ai(mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/magnetic_controller/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "Magnetic Control Console

    " + if(!autolink) + dat += {" + Frequency: [frequency]
    + Code: [code]
    + Probe Generators
    + "} + + if(magnets.len >= 1) + + dat += "Magnets confirmed:
    " + var/i = 0 + for(var/obj/machinery/magnetic_module/M in magnets) + i++ + dat += "     < \[[i]\] ([M.on ? "On":"Off"]) | Electricity level: - [M.electricity_level] +; Magnetic field: - [M.magnetic_field] +
    " + + dat += "
    Speed: - [speed] +
    " + dat += "Path: {[path]}
    " + dat += "Moving: [moving ? "Enabled":"Disabled"]" + + + user << browse(dat, "window=magnet;size=400x500") + onclose(user, "magnet") + +/obj/machinery/magnetic_controller/Topic(href, href_list) + if(stat & (BROKEN|NOPOWER)) + return + usr.set_machine(src) + src.add_fingerprint(usr) + + if(href_list["radio-op"]) + + // Prepare signal beforehand, because this is a radio operation + var/datum/signal/signal = new + signal.transmission_method = 1 // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + // Apply any necessary commands + switch(href_list["radio-op"]) + if("togglepower") + signal.data["command"] = "toggle-power" + + if("minuselec") + signal.data["command"] = "sub-elec" + if("pluselec") + signal.data["command"] = "add-elec" + + if("minusmag") + signal.data["command"] = "sub-mag" + if("plusmag") + signal.data["command"] = "add-mag" + + + // Broadcast the signal + + radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) + + spawn(1) + updateUsrDialog() // pretty sure this increases responsiveness + + if(href_list["operation"]) + switch(href_list["operation"]) + if("plusspeed") + speed++ + if(speed > 10) + speed = 10 + if("minusspeed") + speed -- + if(speed <= 0) + speed = 1 + if("setpath") + var/newpath = sanitize(copytext(input(usr, "Please define a new path!",,path) as text|null,1,MAX_MESSAGE_LEN)) + if(newpath && newpath != "") + moving = 0 // stop moving + path = newpath + pathpos = 1 // reset position + filter_path() // renders rpath + + if("togglemoving") + moving = !moving + if(moving) + spawn() MagnetMove() + + + updateUsrDialog() + +/obj/machinery/magnetic_controller/proc/MagnetMove() + if(looping) return + + while(moving && rpath.len >= 1) + + if(stat & (BROKEN|NOPOWER)) + break + + looping = 1 + + // Prepare the radio signal + var/datum/signal/signal = new + signal.transmission_method = 1 // radio transmission + signal.source = src + signal.frequency = frequency + signal.data["code"] = code + + if(pathpos > rpath.len) // if the position is greater than the length, we just loop through the list! + pathpos = 1 + + var/nextmove = uppertext(rpath[pathpos]) // makes it un-case-sensitive + + if(!(nextmove in list("N","S","E","W","C","R"))) + // N, S, E, W are directional + // C is center + // R is random (in magnetic field's bounds) + qdel(signal) + break // break the loop if the character located is invalid + + signal.data["command"] = nextmove + + + pathpos++ // increase iterator + + // Broadcast the signal + spawn() + radio_connection.post_signal(src, signal, filter = RADIO_MAGNETS) + + if(speed == 10) + sleep(1) + else + sleep(12-speed) + + looping = 0 + + +/obj/machinery/magnetic_controller/proc/filter_path() + // Generates the rpath variable using the path string, think of this as "string2list" + // Doesn't use params2list() because of the akward way it stacks entities + rpath = list() // clear rpath + var/maximum_character = min( 50, length(path) ) // chooses the maximum length of the iterator. 50 max length + + for(var/i=1, i<=maximum_character, i++) // iterates through all characters in path + + var/nextchar = copytext(path, i, i+1) // find next character + + if(!(nextchar in list(";", "&", "*", " "))) // if char is a separator, ignore + rpath += copytext(path, i, i+1) // else, add to list + + // there doesn't HAVE to be separators but it makes paths syntatically visible diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm index 9e6f0d672cca..fc6e61be76d1 100644 --- a/code/game/machinery/mass_driver.dm +++ b/code/game/machinery/mass_driver.dm @@ -1,211 +1,211 @@ -/obj/machinery/mass_driver - name = "mass driver" - desc = "Shoots things into space." - icon = 'icons/obj/objects.dmi' - icon_state = "mass_driver" - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 50 - - var/power = 1.0 - var/code = 1.0 - var/id_tag = "default" - settagwhitelist = list("id_tag") - var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. - -/obj/machinery/mass_driver/attackby(obj/item/W, mob/user as mob) - - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/screwdriver)) - to_chat(user, "You begin to unscrew the bolts off the [src]...") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src)) - var/obj/machinery/mass_driver_frame/F = new(get_turf(src)) - F.dir = src.dir - F.anchored = 1 - F.build = 4 - F.update_icon() - qdel(src) - return 1 - - return ..() - -/obj/machinery/mass_driver/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" -
      -
    • [format_tag("ID Tag","id_tag")]
    • -
    "} - -/obj/machinery/mass_driver/proc/drive(amount) - if(stat & (BROKEN|NOPOWER)) - return - use_power(500*power) - var/O_limit = 0 - var/atom/target = get_edge_target_turf(src, dir) - for(var/atom/movable/O in loc) - if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. - O_limit++ - if(O_limit >= 20)//so no more than 20 items are sent at a time, probably for counter-lag purposes - break - use_power(500) - spawn() - var/coef = 1 - if(emagged) - coef = 5 - O.throw_at(target, drive_range * power * coef, power * coef) - flick("mass_driver1", src) - return - -/obj/machinery/mass_driver/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - drive() - ..(severity) - -/obj/machinery/mass_driver/emag_act(mob/user) - if(!emagged) - emagged = 1 - to_chat(user, "You hack the Mass Driver, radically increasing the force at which it'll throw things. Better not stand in its way.") - return 1 - return -1 - -////////////////MASS BUMPER/////////////////// - -/obj/machinery/mass_driver/bumper - name = "mass bumper" - desc = "Now you're here, now you're over there." - density = 1 - -/obj/machinery/mass_driver/bumper/Bumped(M as mob|obj) - density = 0 - step(M, get_dir(M,src)) - spawn(1) - density = 1 - drive() - return - -////////////////MASS DRIVER FRAME/////////////////// - -/obj/machinery/mass_driver_frame - name = "mass driver frame" - icon = 'icons/obj/objects.dmi' - icon_state = "mass_driver_b0" - density = 0 - anchored = 0 - var/build = 0 - -/obj/machinery/mass_driver_frame/attackby(var/obj/item/W as obj, var/mob/user as mob) - switch(build) - if(0) // Loose frame - if(istype(W, /obj/item/wrench)) - to_chat(user, "You begin to anchor \the [src] on the floor.") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 10 * W.toolspeed, target = src) && (build == 0)) - to_chat(user, "You anchor \the [src]!") - anchored = 1 - build++ - update_icon() - return 1 - return - if(1) // Fixed to the floor - if(istype(W, /obj/item/wrench)) - to_chat(user, "You begin to de-anchor \the [src] from the floor.") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 10 * W.toolspeed, target = src) && (build == 1)) - build-- - update_icon() - anchored = 0 - to_chat(user, "You de-anchored \the [src]!") - return 1 - if(2) // Welded to the floor - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - to_chat(user, "You start adding cables to \the [src]...") - playsound(get_turf(src), C.usesound, 50, 1) - if(do_after(user, 20 * C.toolspeed, target = src) && (C.amount >= 2) && (build == 2)) - C.use(2) - to_chat(user, "You've added cables to \the [src].") - build++ - update_icon() - return - if(3) // Wired - if(istype(W, /obj/item/wirecutters)) - to_chat(user, "You begin to remove the wiring from \the [src].") - if(do_after(user, 10 * W.toolspeed, target = src) && (build == 3)) - new /obj/item/stack/cable_coil(loc,2) - playsound(get_turf(src), W.usesound, 50, 1) - to_chat(user, "You've removed the cables from \the [src].") - build-- - update_icon() - return 1 - if(istype(W, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = W - to_chat(user, "You begin to complete \the [src]...") - playsound(get_turf(src), R.usesound, 50, 1) - if(do_after(user, 20 * R.toolspeed, target = src) && (R.amount >= 2) && (build == 3)) - R.use(2) - to_chat(user, "You've added the grille to \the [src].") - build++ - update_icon() - return 1 - return - if(4) // Grille in place - if(istype(W, /obj/item/crowbar)) - to_chat(user, "You begin to pry off the grille from \the [src]...") - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src) && (build == 4)) - new /obj/item/stack/rods(loc,2) - build-- - update_icon() - return 1 - if(istype(W, /obj/item/screwdriver)) - to_chat(user, "You finalize the Mass Driver...") - playsound(get_turf(src), W.usesound, 50, 1) - var/obj/machinery/mass_driver/M = new(get_turf(src)) - M.dir = src.dir - qdel(src) - return 1 - return - return ..() - -/obj/machinery/mass_driver_frame/welder_act(mob/user, obj/item/I) - if(build != 0 && build != 1 && build != 2) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(build == 0) //can deconstruct - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 30, volume = I.tool_volume)) - WELDER_SLICING_SUCCESS_MESSAGE - new /obj/item/stack/sheet/plasteel(drop_location(),3) - qdel(src) - else if(build == 1) //wrenched but not welded down - WELDER_ATTEMPT_FLOOR_WELD_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 1) - WELDER_FLOOR_WELD_SUCCESS_MESSAGE - build = 2 - else if(build == 2) //welded down - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 2) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - build = 1 - update_icon() - -/obj/machinery/mass_driver_frame/update_icon() - icon_state = "mass_driver_b[build]" - -/obj/machinery/mass_driver_frame/verb/rotate() - set category = "Object" - set name = "Rotate Frame" - set src in view(1) - - if( usr.stat || usr.restrained() || (usr.status_flags & FAKEDEATH)) - return - - src.dir = turn(src.dir, -90) - return +/obj/machinery/mass_driver + name = "mass driver" + desc = "Shoots things into space." + icon = 'icons/obj/objects.dmi' + icon_state = "mass_driver" + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 50 + + var/power = 1.0 + var/code = 1.0 + var/id_tag = "default" + settagwhitelist = list("id_tag") + var/drive_range = 50 //this is mostly irrelevant since current mass drivers throw into space, but you could make a lower-range mass driver for interstation transport or something I guess. + +/obj/machinery/mass_driver/attackby(obj/item/W, mob/user as mob) + + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/screwdriver)) + to_chat(user, "You begin to unscrew the bolts off the [src]...") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src)) + var/obj/machinery/mass_driver_frame/F = new(get_turf(src)) + F.dir = src.dir + F.anchored = 1 + F.build = 4 + F.update_icon() + qdel(src) + return 1 + + return ..() + +/obj/machinery/mass_driver/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" +
      +
    • [format_tag("ID Tag","id_tag")]
    • +
    "} + +/obj/machinery/mass_driver/proc/drive(amount) + if(stat & (BROKEN|NOPOWER)) + return + use_power(500*power) + var/O_limit = 0 + var/atom/target = get_edge_target_turf(src, dir) + for(var/atom/movable/O in loc) + if(!O.anchored||istype(O, /obj/mecha))//Mechs need their launch platforms. + O_limit++ + if(O_limit >= 20)//so no more than 20 items are sent at a time, probably for counter-lag purposes + break + use_power(500) + spawn() + var/coef = 1 + if(emagged) + coef = 5 + O.throw_at(target, drive_range * power * coef, power * coef) + flick("mass_driver1", src) + return + +/obj/machinery/mass_driver/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + drive() + ..(severity) + +/obj/machinery/mass_driver/emag_act(mob/user) + if(!emagged) + emagged = 1 + to_chat(user, "You hack the Mass Driver, radically increasing the force at which it'll throw things. Better not stand in its way.") + return 1 + return -1 + +////////////////MASS BUMPER/////////////////// + +/obj/machinery/mass_driver/bumper + name = "mass bumper" + desc = "Now you're here, now you're over there." + density = 1 + +/obj/machinery/mass_driver/bumper/Bumped(M as mob|obj) + density = 0 + step(M, get_dir(M,src)) + spawn(1) + density = 1 + drive() + return + +////////////////MASS DRIVER FRAME/////////////////// + +/obj/machinery/mass_driver_frame + name = "mass driver frame" + icon = 'icons/obj/objects.dmi' + icon_state = "mass_driver_b0" + density = 0 + anchored = 0 + var/build = 0 + +/obj/machinery/mass_driver_frame/attackby(var/obj/item/W as obj, var/mob/user as mob) + switch(build) + if(0) // Loose frame + if(istype(W, /obj/item/wrench)) + to_chat(user, "You begin to anchor \the [src] on the floor.") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 10 * W.toolspeed, target = src) && (build == 0)) + to_chat(user, "You anchor \the [src]!") + anchored = 1 + build++ + update_icon() + return 1 + return + if(1) // Fixed to the floor + if(istype(W, /obj/item/wrench)) + to_chat(user, "You begin to de-anchor \the [src] from the floor.") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 10 * W.toolspeed, target = src) && (build == 1)) + build-- + update_icon() + anchored = 0 + to_chat(user, "You de-anchored \the [src]!") + return 1 + if(2) // Welded to the floor + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + to_chat(user, "You start adding cables to \the [src]...") + playsound(get_turf(src), C.usesound, 50, 1) + if(do_after(user, 20 * C.toolspeed, target = src) && (C.amount >= 2) && (build == 2)) + C.use(2) + to_chat(user, "You've added cables to \the [src].") + build++ + update_icon() + return + if(3) // Wired + if(istype(W, /obj/item/wirecutters)) + to_chat(user, "You begin to remove the wiring from \the [src].") + if(do_after(user, 10 * W.toolspeed, target = src) && (build == 3)) + new /obj/item/stack/cable_coil(loc,2) + playsound(get_turf(src), W.usesound, 50, 1) + to_chat(user, "You've removed the cables from \the [src].") + build-- + update_icon() + return 1 + if(istype(W, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = W + to_chat(user, "You begin to complete \the [src]...") + playsound(get_turf(src), R.usesound, 50, 1) + if(do_after(user, 20 * R.toolspeed, target = src) && (R.amount >= 2) && (build == 3)) + R.use(2) + to_chat(user, "You've added the grille to \the [src].") + build++ + update_icon() + return 1 + return + if(4) // Grille in place + if(istype(W, /obj/item/crowbar)) + to_chat(user, "You begin to pry off the grille from \the [src]...") + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src) && (build == 4)) + new /obj/item/stack/rods(loc,2) + build-- + update_icon() + return 1 + if(istype(W, /obj/item/screwdriver)) + to_chat(user, "You finalize the Mass Driver...") + playsound(get_turf(src), W.usesound, 50, 1) + var/obj/machinery/mass_driver/M = new(get_turf(src)) + M.dir = src.dir + qdel(src) + return 1 + return + return ..() + +/obj/machinery/mass_driver_frame/welder_act(mob/user, obj/item/I) + if(build != 0 && build != 1 && build != 2) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(build == 0) //can deconstruct + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + WELDER_SLICING_SUCCESS_MESSAGE + new /obj/item/stack/sheet/plasteel(drop_location(),3) + qdel(src) + else if(build == 1) //wrenched but not welded down + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 1) + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + build = 2 + else if(build == 2) //welded down + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume) && build == 2) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + build = 1 + update_icon() + +/obj/machinery/mass_driver_frame/update_icon() + icon_state = "mass_driver_b[build]" + +/obj/machinery/mass_driver_frame/verb/rotate() + set category = "Object" + set name = "Rotate Frame" + set src in view(1) + + if( usr.stat || usr.restrained() || (usr.status_flags & FAKEDEATH)) + return + + src.dir = turn(src.dir, -90) + return diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index d5fa5277592c..48d87d0f330d 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -1,222 +1,222 @@ -// Navigation beacon for AI robots -// No longer exists on the radio controller, it is managed by a global list. - -/obj/machinery/navbeacon - - icon = 'icons/obj/objects.dmi' - icon_state = "navbeacon0-f" - name = "navigation beacon" - desc = "A radio beacon used for bot navigation." - level = 1 // underfloor - layer = 2.5 - anchored = 1 - max_integrity = 500 - armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) - var/open = 0 // true if cover is open - var/locked = 1 // true if controls are locked - var/location = "" // location response text - var/list/codes // assoc. list of transponder codes - var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" - - req_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS) - -/obj/machinery/navbeacon/New() - ..() - - set_codes() - - var/turf/T = loc - hide(T.intact) - if(!codes || !codes.len) - log_runtime(EXCEPTION("Empty codes datum at ([x],[y],[z])"), src, list("codes_txt: '[codes_txt]'")) - if("patrol" in codes) - if(!GLOB.navbeacons["[z]"]) - GLOB.navbeacons["[z]"] = list() - GLOB.navbeacons["[z]"] += src //Register with the patrol list! - if("delivery" in codes) - GLOB.deliverybeacons += src - GLOB.deliverybeacontags += location - -/obj/machinery/navbeacon/Destroy() - GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one. - GLOB.deliverybeacons -= src - return ..() - -/obj/machinery/navbeacon/serialize() - var/list/data = ..() - data["codes"] = codes - return data - -/obj/machinery/navbeacon/deserialize(list/data) - codes = data["codes"] - ..() - -// set the transponder codes assoc list from codes_txt -/obj/machinery/navbeacon/proc/set_codes() - if(!codes_txt) - return - - codes = new() - - var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons - - for(var/e in entries) - var/index = findtext(e, "=") // format is "key=value" - if(index) - var/key = copytext(e, 1, index) - var/val = copytext(e, index+1) - codes[key] = val - else - codes[e] = "1" - - -// called when turf state changes -// hide the object if turf is intact -/obj/machinery/navbeacon/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM : 0 - updateicon() - -// update the icon_state -/obj/machinery/navbeacon/proc/updateicon() - var/state="navbeacon[open]" - - if(invisibility) - icon_state = "[state]-f" // if invisible, set icon to faded version - // in case revealed by T-scanner - else - icon_state = "[state]" - -/obj/machinery/navbeacon/attackby(obj/item/I, mob/user, params) - var/turf/T = loc - if(T.intact) - return // prevent intraction when T-scanner revealed - - if(istype(I, /obj/item/screwdriver)) - open = !open - - user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") - - updateicon() - - else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) - if(open) - if(allowed(user)) - locked = !locked - to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") - else - to_chat(user, "Access denied.") - updateDialog() - else - to_chat(user, "You must open the cover first!") - else - return ..() - -/obj/machinery/navbeacon/attack_ai(mob/user) - interact(user, 1) - -/obj/machinery/navbeacon/attack_hand(mob/user) - interact(user, 0) - -/obj/machinery/navbeacon/interact(mob/user, ai = 0) - var/turf/T = loc - if(T.intact) - return // prevent intraction when T-scanner revealed - - if(!open && !ai) // can't alter controls if not open, unless you're an AI - to_chat(user, "The beacon's control cover is closed!") - return - - - var/t - - if(locked && !ai) - t = {"Navigation Beacon

    -(swipe card to unlock controls)
    -Location: [location ? location : "(none)"]
    -Transponder Codes:
      "} - - for(var/key in codes) - t += "
    • [key] ... [codes[key]]" - t+= "
        " - - else - - t = {"Navigation Beacon

        -(swipe card to lock controls)
        - -
        -Location: [location ? location : "None"]
        -Transponder Codes:
          "} - - for(var/key in codes) - t += "
        • [key] ... [codes[key]]" - t += " Edit" - t += " Delete
          " - t += " Add New
          " - t+= "
            " - - var/datum/browser/popup = new(user, "navbeacon", "Navigation Beacon", 300, 400) - popup.set_content(t) - popup.open() - return - -/obj/machinery/navbeacon/Topic(href, href_list) - if(..()) - return - if(open && !locked) - usr.set_machine(src) - - if(href_list["locedit"]) - var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) - if(newloc) - location = newloc - updateDialog() - - else if(href_list["edit"]) - var/codekey = href_list["code"] - - var/newkey = stripped_input(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey) - if(!newkey) - return - - var/codeval = codes[codekey] - var/newval = stripped_input(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval) - if(!newval) - newval = codekey - return - - codes.Remove(codekey) - codes[newkey] = newval - - updateDialog() - - else if(href_list["delete"]) - var/codekey = href_list["code"] - codes.Remove(codekey) - updateDialog() - - else if(href_list["add"]) - - var/newkey = stripped_input(usr, "Enter New Transponder Code Key", "Navigation Beacon") - if(!newkey) - return - - var/newval = stripped_input(usr, "Enter New Transponder Code Value", "Navigation Beacon") - if(!newval) - newval = "1" - return - - if(!codes) - codes = new() - - codes[newkey] = newval - - updateDialog() - - -/obj/machinery/navbeacon/invisible - invisibility = INVISIBILITY_MAXIMUM - -/obj/machinery/navbeacon/invisible/hide(intact) - invisibility = INVISIBILITY_MAXIMUM - updateicon() \ No newline at end of file +// Navigation beacon for AI robots +// No longer exists on the radio controller, it is managed by a global list. + +/obj/machinery/navbeacon + + icon = 'icons/obj/objects.dmi' + icon_state = "navbeacon0-f" + name = "navigation beacon" + desc = "A radio beacon used for bot navigation." + level = 1 // underfloor + layer = 2.5 + anchored = 1 + max_integrity = 500 + armor = list(melee = 70, bullet = 70, laser = 70, energy = 70, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) + var/open = 0 // true if cover is open + var/locked = 1 // true if controls are locked + var/location = "" // location response text + var/list/codes // assoc. list of transponder codes + var/codes_txt = "" // codes as set on map: "tag1;tag2" or "tag1=value;tag2=value" + + req_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS) + +/obj/machinery/navbeacon/New() + ..() + + set_codes() + + var/turf/T = loc + hide(T.intact) + if(!codes || !codes.len) + log_runtime(EXCEPTION("Empty codes datum at ([x],[y],[z])"), src, list("codes_txt: '[codes_txt]'")) + if("patrol" in codes) + if(!GLOB.navbeacons["[z]"]) + GLOB.navbeacons["[z]"] = list() + GLOB.navbeacons["[z]"] += src //Register with the patrol list! + if("delivery" in codes) + GLOB.deliverybeacons += src + GLOB.deliverybeacontags += location + +/obj/machinery/navbeacon/Destroy() + GLOB.navbeacons["[z]"] -= src //Remove from beacon list, if in one. + GLOB.deliverybeacons -= src + return ..() + +/obj/machinery/navbeacon/serialize() + var/list/data = ..() + data["codes"] = codes + return data + +/obj/machinery/navbeacon/deserialize(list/data) + codes = data["codes"] + ..() + +// set the transponder codes assoc list from codes_txt +/obj/machinery/navbeacon/proc/set_codes() + if(!codes_txt) + return + + codes = new() + + var/list/entries = splittext(codes_txt, ";") // entries are separated by semicolons + + for(var/e in entries) + var/index = findtext(e, "=") // format is "key=value" + if(index) + var/key = copytext(e, 1, index) + var/val = copytext(e, index+1) + codes[key] = val + else + codes[e] = "1" + + +// called when turf state changes +// hide the object if turf is intact +/obj/machinery/navbeacon/hide(intact) + invisibility = intact ? INVISIBILITY_MAXIMUM : 0 + updateicon() + +// update the icon_state +/obj/machinery/navbeacon/proc/updateicon() + var/state="navbeacon[open]" + + if(invisibility) + icon_state = "[state]-f" // if invisible, set icon to faded version + // in case revealed by T-scanner + else + icon_state = "[state]" + +/obj/machinery/navbeacon/attackby(obj/item/I, mob/user, params) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(istype(I, /obj/item/screwdriver)) + open = !open + + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + + updateicon() + + else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) + if(open) + if(allowed(user)) + locked = !locked + to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].") + else + to_chat(user, "Access denied.") + updateDialog() + else + to_chat(user, "You must open the cover first!") + else + return ..() + +/obj/machinery/navbeacon/attack_ai(mob/user) + interact(user, 1) + +/obj/machinery/navbeacon/attack_hand(mob/user) + interact(user, 0) + +/obj/machinery/navbeacon/interact(mob/user, ai = 0) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(!open && !ai) // can't alter controls if not open, unless you're an AI + to_chat(user, "The beacon's control cover is closed!") + return + + + var/t + + if(locked && !ai) + t = {"Navigation Beacon

            +(swipe card to unlock controls)
            +Location: [location ? location : "(none)"]
            +Transponder Codes:
              "} + + for(var/key in codes) + t += "
            • [key] ... [codes[key]]" + t+= "
                " + + else + + t = {"Navigation Beacon

                +(swipe card to lock controls)
                + +
                +Location: [location ? location : "None"]
                +Transponder Codes:
                  "} + + for(var/key in codes) + t += "
                • [key] ... [codes[key]]" + t += " Edit" + t += " Delete
                  " + t += " Add New
                  " + t+= "
                    " + + var/datum/browser/popup = new(user, "navbeacon", "Navigation Beacon", 300, 400) + popup.set_content(t) + popup.open() + return + +/obj/machinery/navbeacon/Topic(href, href_list) + if(..()) + return + if(open && !locked) + usr.set_machine(src) + + if(href_list["locedit"]) + var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) + if(newloc) + location = newloc + updateDialog() + + else if(href_list["edit"]) + var/codekey = href_list["code"] + + var/newkey = stripped_input(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey) + if(!newkey) + return + + var/codeval = codes[codekey] + var/newval = stripped_input(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval) + if(!newval) + newval = codekey + return + + codes.Remove(codekey) + codes[newkey] = newval + + updateDialog() + + else if(href_list["delete"]) + var/codekey = href_list["code"] + codes.Remove(codekey) + updateDialog() + + else if(href_list["add"]) + + var/newkey = stripped_input(usr, "Enter New Transponder Code Key", "Navigation Beacon") + if(!newkey) + return + + var/newval = stripped_input(usr, "Enter New Transponder Code Value", "Navigation Beacon") + if(!newval) + newval = "1" + return + + if(!codes) + codes = new() + + codes[newkey] = newval + + updateDialog() + + +/obj/machinery/navbeacon/invisible + invisibility = INVISIBILITY_MAXIMUM + +/obj/machinery/navbeacon/invisible/hide(intact) + invisibility = INVISIBILITY_MAXIMUM + updateicon() diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 61d96602343f..c2396ab1d9fc 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -55,9 +55,9 @@ var/list/datum/feed_channel/network_channels = list() var/datum/feed_message/wanted_issue -var/datum/feed_network/news_network = new /datum/feed_network //The global news-network, which is coincidentally a global list. +GLOBAL_DATUM_INIT(news_network, /datum/feed_network, new()) //The global news-network, which is coincidentally a global list. -var/list/obj/machinery/newscaster/allCasters = list() //Global list that will contain reference to all newscasters in existence. +GLOBAL_LIST_EMPTY(allNewscasters) //Global list that will contain reference to all newscasters in existence. #define NEWSCASTER_MAIN 0 // Main menu #define NEWSCASTER_FC_LIST 1 // Feed channel list @@ -128,13 +128,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co securityCaster = 1 /obj/machinery/newscaster/New() - allCasters += src - unit_no = allCasters.len + GLOB.allNewscasters += src + unit_no = GLOB.allNewscasters.len update_icon() //for any custom ones on the map... ..() /obj/machinery/newscaster/Destroy() - allCasters -= src + GLOB.allNewscasters -= src viewing_channel = null QDEL_NULL(photo) return ..() @@ -144,7 +144,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(inoperable()) icon_state = "newscaster_off" else - if(!news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message + if(!GLOB.news_network.wanted_issue) //wanted icon state, there can be no overlays on it as it's a priority message icon_state = "newscaster_normal" if(alert) //new message alert overlay add_overlay("newscaster_alert") @@ -194,7 +194,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co switch(screen) if(0) - data["wanted_issue"] = news_network.wanted_issue ? 1 : 0 + data["wanted_issue"] = GLOB.news_network.wanted_issue ? 1 : 0 data["silence"] = silence data["securityCaster"] = securityCaster if(securityCaster) @@ -202,7 +202,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(1, 6, 7) var/list/channels = list() data["channels"] = channels - for(var/datum/feed_channel/C in news_network.network_channels) + for(var/datum/feed_channel/C in GLOB.news_network.network_channels) channels[++channels.len] = list("name" = C.channel_name, "ref" = "\ref[C]", "censored" = C.censored, "admin" = C.is_admin_channel) if(2) data["scanned_user"] = scanned_user @@ -215,10 +215,10 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co data["msg"] = msg data["photo"] = photo ? 1 : 0 if(4) - var/total_num = length(news_network.network_channels) + var/total_num = length(GLOB.news_network.network_channels) var/active_num = total_num var/message_num=0 - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(!FC.censored) message_num += length(FC.messages) else @@ -254,7 +254,7 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co if(10) var/wanted_already = 0 var/end_param = 1 - if(news_network.wanted_issue) + if(GLOB.news_network.wanted_issue) wanted_already = 1 end_param = 2 data["wanted_already"] = wanted_already @@ -263,16 +263,16 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co data["msg"] = msg data["photo"] = photo ? 1 : 0 if(wanted_already) - data["author"] = news_network.wanted_issue.backup_author + data["author"] = GLOB.news_network.wanted_issue.backup_author else data["scanned_user"] = scanned_user if(11) - data["author"] = news_network.wanted_issue.backup_author - data["criminal"] = news_network.wanted_issue.author - data["description"] = news_network.wanted_issue.body - if(news_network.wanted_issue.img) - user << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png") - data["photo"] = news_network.wanted_issue.img ? news_network.wanted_issue.img : 0 + data["author"] = GLOB.news_network.wanted_issue.backup_author + data["criminal"] = GLOB.news_network.wanted_issue.author + data["description"] = GLOB.news_network.wanted_issue.body + if(GLOB.news_network.wanted_issue.img) + user << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") + data["photo"] = GLOB.news_network.wanted_issue.img ? GLOB.news_network.wanted_issue.img : 0 if(12) var/list/jobs = list() data["jobs"] = jobs @@ -295,13 +295,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co else if(href_list["submit_new_channel"]) var/list/existing_authors = list() - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.author == REDACTED) existing_authors += FC.backup_author else existing_authors += FC.author var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == channel_name) check = 1 break @@ -325,13 +325,13 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co newChannel.author = scanned_user newChannel.locked = c_locked feedback_inc("newscaster_channels", 1) - news_network.network_channels += newChannel //Adding channel to the global network + GLOB.news_network.network_channels += newChannel //Adding channel to the global network temp = "Feed channel '[channel_name]' created successfully." temp_back_screen = NEWSCASTER_MAIN else if(href_list["set_channel_receiving"]) var/list/available_channels = list() - for(var/datum/feed_channel/F in news_network.network_channels) + for(var/datum/feed_channel/F in GLOB.news_network.network_channels) if((!F.locked || F.author == scanned_user) && !F.censored) available_channels += F.channel_name channel_name = strip_html_simple(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels) @@ -366,14 +366,14 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co newMsg.img = photo.img feedback_inc("newscaster_stories",1) var/announcement = "" - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == channel_name) FC.messages += newMsg //Adding message to the network's appropriate feed_channel announcement = FC.announce_news(msg_title) break temp = "Feed story successfully submitted to [channel_name]." temp_back_screen = NEWSCASTER_MAIN - for(var/obj/machinery/newscaster/NC in allCasters) + for(var/obj/machinery/newscaster/NC in GLOB.allNewscasters) NC.newsAlert(announcement) else if(href_list["create_channel"]) @@ -405,12 +405,12 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co else if(href_list["menu_wanted"]) var/already_wanted = 0 - if(news_network.wanted_issue) + if(GLOB.news_network.wanted_issue) already_wanted = 1 if(already_wanted) - channel_name = news_network.wanted_issue.author - msg = news_network.wanted_issue.body + channel_name = GLOB.news_network.wanted_issue.author + msg = GLOB.news_network.wanted_issue.body screen = NEWSCASTER_W_ISSUE_H else if(href_list["set_wanted_name"]) @@ -441,32 +441,32 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co W.backup_author = scanned_user //I know, a bit wacky if(photo) W.img = photo.img - news_network.wanted_issue = W - for(var/obj/machinery/newscaster/NS in allCasters) + GLOB.news_network.wanted_issue = W + for(var/obj/machinery/newscaster/NS in GLOB.allNewscasters) NS.newsAlert() NS.update_icon() temp = "Wanted issue for [channel_name] is now in Network Circulation." temp_back_screen = NEWSCASTER_MAIN else - if(news_network.wanted_issue.is_admin_message) + if(GLOB.news_network.wanted_issue.is_admin_message) alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot edit it.","Ok") return - news_network.wanted_issue.author = channel_name - news_network.wanted_issue.body = msg - news_network.wanted_issue.backup_author = scanned_user + GLOB.news_network.wanted_issue.author = channel_name + GLOB.news_network.wanted_issue.body = msg + GLOB.news_network.wanted_issue.backup_author = scanned_user if(photo) - news_network.wanted_issue.img = photo.img + GLOB.news_network.wanted_issue.img = photo.img temp = "Wanted issue for [channel_name] successfully edited." temp_back_screen = NEWSCASTER_MAIN else if(href_list["cancel_wanted"]) - if(news_network.wanted_issue.is_admin_message) + if(GLOB.news_network.wanted_issue.is_admin_message) alert("The wanted issue has been distributed by a Nanotrasen higherup. You cannot take it down.", "Ok") return var/choice = alert("Please confirm wanted issue removal", "Network Security Handler", "Confirm", "Cancel") if(choice == "Confirm") - news_network.wanted_issue = null - for(var/obj/machinery/newscaster/NC in allCasters) + GLOB.news_network.wanted_issue = null + for(var/obj/machinery/newscaster/NC in GLOB.allNewscasters) NC.update_icon() temp = "Wanted Issue successfully deleted from Circulation" temp_back_screen = NEWSCASTER_MAIN @@ -826,10 +826,10 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co /obj/machinery/newscaster/proc/print_paper() feedback_inc("newscaster_newspapers_printed",1) var/obj/item/newspaper/NEWSPAPER = new /obj/item/newspaper - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) NEWSPAPER.news_content += FC - if(news_network.wanted_issue) - NEWSPAPER.important_message = news_network.wanted_issue + if(GLOB.news_network.wanted_issue) + NEWSPAPER.important_message = GLOB.news_network.wanted_issue NEWSPAPER.loc = get_turf(src) paper_remaining-- return diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index e77eef869dce..7165cceb1ff1 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -1,362 +1,362 @@ -//#define AMAP - -/obj/machinery/computer/security/verb/station_map() - set name = ".map" - set category = "Object" - set src in view(1) - usr.set_machine(src) - if(!mapping) return - - log_game("[usr]([usr.key]) used station map L[z] in [src.loc.loc]") - - src.drawmap(usr) - -/obj/machinery/computer/security/proc/drawmap(var/mob/user as mob) - - var/icx = round(world.maxx/16) + 1 - var/icy = round(world.maxy/16) + 1 - - var/xoff = round( (icx*16-world.maxx)-2) - var/yoff = round( (icy*16-world.maxy)-2) - - var/icount = icx * icy - - - var/list/imap = list() - -#ifdef AMAP - - for(var/i = 0; i
    - - - - -
    - [left_part] - - [list_queue()] -
    "} - var/datum/browser/popup = new(user, "mecha_fabricator", name, 1000, 600) - popup.set_content(dat) - popup.open(0) - onclose(user, "mecha_fabricator") - return - -/obj/machinery/mecha_part_fabricator/Topic(href, href_list) - if(..()) - return 1 - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["part_set"]) - var/tpart_set = afilter.getStr("part_set") - if(tpart_set) - if(tpart_set=="clear") - part_set = null - else - part_set = tpart_set - screen = "parts" - if(href_list["part"]) - var/T = afilter.getStr("part") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - if(!processing_queue) - build_part(D) - else - add_to_queue(D) - break - if(href_list["add_to_queue"]) - var/T = afilter.getStr("add_to_queue") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - add_to_queue(D) - break - return update_queue_on_page() - if(href_list["remove_from_queue"]) - remove_from_queue(afilter.getNum("remove_from_queue")) - return update_queue_on_page() - if(href_list["partset_to_queue"]) - add_part_set_to_queue(afilter.get("partset_to_queue")) - return update_queue_on_page() - if(href_list["process_queue"]) - spawn(0) - if(processing_queue || being_built) - return FALSE - processing_queue = 1 - process_queue() - processing_queue = 0 - if(href_list["clear_temp"]) - temp = null - if(href_list["screen"]) - screen = href_list["screen"] - if(href_list["queue_move"] && href_list["index"]) - var/index = afilter.getNum("index") - var/new_index = index + afilter.getNum("queue_move") - if(isnum(index) && isnum(new_index)) - if(IsInRange(new_index,1,queue.len)) - queue.Swap(index,new_index) - return update_queue_on_page() - if(href_list["clear_queue"]) - queue = list() - return update_queue_on_page() - if(href_list["sync"]) - sync() - if(href_list["part_desc"]) - var/T = afilter.getStr("part_desc") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - var/obj/part = D.build_path - temp = {"

    [initial(part.name)] description:

    - [initial(part.desc)]
    - Return - "} - break - - if(href_list["remove_mat"] && href_list["material"]) - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"]) - - updateUsrDialog() - return - -/obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - var/stack_name = material2name(id_inserted) - overlays += "fab-load-[stack_name]" - sleep(10) - overlays -= "fab-load-[stack_name]" - updateUsrDialog() - -/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params) - if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W)) - return - - if(exchange_parts(user, W)) - return - - if(default_deconstruction_crowbar(user, W)) - return TRUE - - else - return ..() - -/obj/machinery/mecha_part_fabricator/proc/material2name(ID) - return copytext(ID,2) - -/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user) - if(panel_open) - to_chat(user, "You can't load [src] while it's opened!") - return FALSE - if(being_built) - to_chat(user, "\The [src] is currently processing! Please wait until completion.") - return FALSE - - return TRUE - -/obj/machinery/mecha_part_fabricator/spacepod - name = "spacepod fabricator" - fabricator_type = PODFAB - part_sets = list( "Pod_Weaponry", - "Pod_Armor", - "Pod_Cargo", - "Pod_Parts", - "Pod_Frame", - "Misc") - req_access = list(ACCESS_MECHANIC) - -/obj/machinery/mecha_part_fabricator/spacepod/New() - ..() - QDEL_LIST(component_parts) - component_parts = list() - component_parts += new /obj/item/circuitboard/podfab(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - -/obj/machinery/mecha_part_fabricator/robot - name = "Robotic Fabricator" - part_sets = list("Cyborg") +/obj/machinery/mecha_part_fabricator + icon = 'icons/obj/robotics.dmi' + icon_state = "fab-idle" + name = "exosuit fabricator" + desc = "Nothing is being built." + density = TRUE + anchored = TRUE + use_power = IDLE_POWER_USE + idle_power_usage = 20 + active_power_usage = 5000 + var/time_coeff = 1 + var/component_coeff = 1 + var/datum/research/files + var/fabricator_type = MECHFAB + var/id + var/sync = 0 + var/part_set + var/datum/design/being_built + var/list/queue = list() + var/processing_queue = 0 + var/screen = "main" + var/temp + var/list/part_sets = list( + "Cyborg", + "Cyborg Repair", + "Ripley", + "Firefighter", + "Odysseus", + "Gygax", + "Durand", + "H.O.N.K", + "Reticence", + "Phazon", + "Exosuit Equipment", + "Cyborg Upgrade Modules", + "Medical", + "Misc" + ) + +/obj/machinery/mecha_part_fabricator/New() + var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, + list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE), 0, + FALSE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + materials.precise_insertion = TRUE + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/mechfab(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + files = new /datum/research(src) //Setup the research data holder. + +/obj/machinery/mecha_part_fabricator/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/mechfab(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/mecha_part_fabricator/Destroy() + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_all() + return ..() + +/obj/machinery/mecha_part_fabricator/RefreshParts() + var/T = 0 + + //maximum stocking amount (default 300000, 600000 at T4) + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + T += M.rating + GET_COMPONENT(materials, /datum/component/material_container) + materials.max_amount = (200000 + (T*50000)) + + //resources adjustment coefficient (1 -> 0.85 -> 0.7 -> 0.55) + T = 1.15 + for(var/obj/item/stock_parts/micro_laser/Ma in component_parts) + T -= Ma.rating*0.15 + component_coeff = T + + //building time adjustment coefficient (1 -> 0.8 -> 0.6) + T = -1 + for(var/obj/item/stock_parts/manipulator/Ml in component_parts) + T += Ml.rating + time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01) + + +/obj/machinery/mecha_part_fabricator/proc/output_parts_list(set_name) + var/output = "" + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(!(set_name in D.category)) + continue + output += "
    [output_part_info(D)]
    \[" + if(check_resources(D)) + output += "Build | " + output += "Add to queue\]\[?\]
    " + return output + +/obj/machinery/mecha_part_fabricator/proc/output_part_info(datum/design/D) + var/output = "[initial(D.name)] (Cost: [output_part_cost(D)]) [get_construction_time_w_coeff(D)/10] seconds" + return output + +/obj/machinery/mecha_part_fabricator/proc/output_part_cost(datum/design/D) + var/i = 0 + var/output + for(var/c in D.materials) + output += "[i?" | ":null][get_resource_cost_w_coeff(D, c)] [material2name(c)]" + i++ + return output + +/obj/machinery/mecha_part_fabricator/proc/output_available_resources() + var/output + GET_COMPONENT(materials, /datum/component/material_container) + for(var/mat_id in materials.materials) + var/datum/material/M = materials.materials[mat_id] + output += "[M.name]: [M.amount] cm³" + if(M.amount >= MINERAL_MATERIAL_AMOUNT) + output += "- Remove \[1\]" + if(M.amount >= (MINERAL_MATERIAL_AMOUNT * 10)) + output += " | \[10\]" + output += " | \[All\]" + output += "
    " + return output + +/obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) + var/list/resources = list() + for(var/R in D.materials) + resources[R] = get_resource_cost_w_coeff(D, R) + return resources + +/obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) + if(D.reagents_list.len) // No reagents storage - no reagent designs. + return FALSE + GET_COMPONENT(materials, /datum/component/material_container) + if(materials.has_materials(get_resources_w_coeff(D))) + return TRUE + return FALSE + +/obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D) + being_built = D + desc = "It's building \a [initial(D.name)]." + var/list/res_coef = get_resources_w_coeff(D) + + GET_COMPONENT(materials, /datum/component/material_container) + materials.use_amount(res_coef) + overlays += "fab-active" + use_power = ACTIVE_POWER_USE + updateUsrDialog() + sleep(get_construction_time_w_coeff(D)) + use_power = IDLE_POWER_USE + overlays -= "fab-active" + desc = initial(desc) + + var/obj/item/I = new D.build_path(loc) + if(D.locked) + var/obj/item/storage/lockbox/research/large/L = new /obj/item/storage/lockbox/research/large(get_step(src, SOUTH)) + I.forceMove(L) + L.name += " ([I.name])" + L.origin_tech = I.origin_tech + else + I.forceMove(get_step(src, SOUTH)) + if(istype(I)) + I.materials = res_coef + atom_say("[I] is complete.") + being_built = null + + updateUsrDialog() + return TRUE + +/obj/machinery/mecha_part_fabricator/proc/update_queue_on_page() + send_byjax(usr,"mecha_fabricator.browser","queue",list_queue()) + return + +/obj/machinery/mecha_part_fabricator/proc/add_part_set_to_queue(set_name) + if(set_name in part_sets) + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(set_name in D.category) + add_to_queue(D) + +/obj/machinery/mecha_part_fabricator/proc/add_to_queue(D) + if(!istype(queue)) + queue = list() + if(D) + queue[++queue.len] = D + return queue.len + +/obj/machinery/mecha_part_fabricator/proc/remove_from_queue(index) + if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) + return FALSE + queue.Cut(index,++index) + return TRUE + +/obj/machinery/mecha_part_fabricator/proc/process_queue() + var/datum/design/D = queue[1] + if(!D) + remove_from_queue(1) + if(queue.len) + return process_queue() + else + return + temp = null + while(D) + if(stat&(NOPOWER|BROKEN)) + return FALSE + if(!check_resources(D)) + atom_say("Not enough resources. Queue processing stopped.") + temp = {"Not enough resources to build next part.
    + Try again | Return"} + return FALSE + remove_from_queue(1) + build_part(D) + D = listgetindex(queue, 1) + atom_say("Queue processing finished successfully.") + +/obj/machinery/mecha_part_fabricator/proc/list_queue() + var/output = "Queue contains:" + if(!istype(queue) || !queue.len) + output += "
    Nothing" + else + output += "
      " + var/i = 0 + for(var/datum/design/D in queue) + i++ + var/obj/part = D.build_path + output += "" + output += initial(part.name) + " - " + output += "[i>1?"":null] " + output += "[i↓":null] " + output += "Remove" + + output += "
    " + output += "\[Process queue | Clear queue\]" + return output + +/obj/machinery/mecha_part_fabricator/proc/sync() + temp = "Updating local R&D database..." + updateUsrDialog() + sleep(30) //only sleep if called by user + var/area/localarea = get_area(src) + + for(var/obj/machinery/computer/rdconsole/RDC in localarea.contents) + if(!RDC.sync) + continue + RDC.files.push_data(files) + temp = "Processed equipment designs.
    " + //check if the tech coefficients have changed + temp += "Return" + + updateUsrDialog() + atom_say("Successfully synchronized with R&D server.") + return + + temp = "Unable to connect to local R&D Database.
    Please check your connections and try again.
    Return" + updateUsrDialog() + return + +/obj/machinery/mecha_part_fabricator/proc/get_resource_cost_w_coeff(datum/design/D, resource, roundto = 1) + return round(D.materials[resource]*component_coeff, roundto) + +/obj/machinery/mecha_part_fabricator/proc/get_construction_time_w_coeff(datum/design/D, roundto = 1) //aran + return round(initial(D.construction_time)*time_coeff, roundto) + +/obj/machinery/mecha_part_fabricator/attack_ghost(mob/user) + interact(user) + +/obj/machinery/mecha_part_fabricator/attack_hand(mob/user) + if(..()) + return 1 + if(!allowed(user) && !isobserver(user)) + to_chat(user, "Access denied.") + return 1 + return interact(user) + +/obj/machinery/mecha_part_fabricator/interact(mob/user) + var/dat, left_part + if(..()) + return + user.set_machine(src) + var/turf/exit = get_step(src,(dir)) + if(exit.density) + atom_say("Error! Part outlet is obstructed.") + return + if(temp) + left_part = temp + else if(being_built) + var/obj/I = being_built.build_path + left_part = {"Building [initial(I.name)].
    + Please wait until completion...
    "} + else + switch(screen) + if("main") + left_part = output_available_resources()+"
    " + left_part += "Sync with R&D servers
    " + for(var/part_set in part_sets) + left_part += "[part_set] - \[Add all parts to queue\]
    " + if("parts") + left_part += output_parts_list(part_set) + left_part += "
    Return" + dat = {" + + [name] + + + + + + + + +
    + [left_part] + + [list_queue()] +
    "} + var/datum/browser/popup = new(user, "mecha_fabricator", name, 1000, 600) + popup.set_content(dat) + popup.open(0) + onclose(user, "mecha_fabricator") + return + +/obj/machinery/mecha_part_fabricator/Topic(href, href_list) + if(..()) + return 1 + var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) + if(href_list["part_set"]) + var/tpart_set = afilter.getStr("part_set") + if(tpart_set) + if(tpart_set=="clear") + part_set = null + else + part_set = tpart_set + screen = "parts" + if(href_list["part"]) + var/T = afilter.getStr("part") + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(D.id == T) + if(!processing_queue) + build_part(D) + else + add_to_queue(D) + break + if(href_list["add_to_queue"]) + var/T = afilter.getStr("add_to_queue") + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(D.id == T) + add_to_queue(D) + break + return update_queue_on_page() + if(href_list["remove_from_queue"]) + remove_from_queue(afilter.getNum("remove_from_queue")) + return update_queue_on_page() + if(href_list["partset_to_queue"]) + add_part_set_to_queue(afilter.get("partset_to_queue")) + return update_queue_on_page() + if(href_list["process_queue"]) + spawn(0) + if(processing_queue || being_built) + return FALSE + processing_queue = 1 + process_queue() + processing_queue = 0 + if(href_list["clear_temp"]) + temp = null + if(href_list["screen"]) + screen = href_list["screen"] + if(href_list["queue_move"] && href_list["index"]) + var/index = afilter.getNum("index") + var/new_index = index + afilter.getNum("queue_move") + if(isnum(index) && isnum(new_index)) + if(IsInRange(new_index,1,queue.len)) + queue.Swap(index,new_index) + return update_queue_on_page() + if(href_list["clear_queue"]) + queue = list() + return update_queue_on_page() + if(href_list["sync"]) + sync() + if(href_list["part_desc"]) + var/T = afilter.getStr("part_desc") + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(D.build_type & fabricator_type) + if(D.id == T) + var/obj/part = D.build_path + temp = {"

    [initial(part.name)] description:

    + [initial(part.desc)]
    + Return + "} + break + + if(href_list["remove_mat"] && href_list["material"]) + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"]) + + updateUsrDialog() + return + +/obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + var/stack_name = material2name(id_inserted) + overlays += "fab-load-[stack_name]" + sleep(10) + overlays -= "fab-load-[stack_name]" + updateUsrDialog() + +/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params) + if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W)) + return + + if(exchange_parts(user, W)) + return + + if(default_deconstruction_crowbar(user, W)) + return TRUE + + else + return ..() + +/obj/machinery/mecha_part_fabricator/proc/material2name(ID) + return copytext(ID,2) + +/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user) + if(panel_open) + to_chat(user, "You can't load [src] while it's opened!") + return FALSE + if(being_built) + to_chat(user, "\The [src] is currently processing! Please wait until completion.") + return FALSE + + return TRUE + +/obj/machinery/mecha_part_fabricator/spacepod + name = "spacepod fabricator" + fabricator_type = PODFAB + part_sets = list( "Pod_Weaponry", + "Pod_Armor", + "Pod_Cargo", + "Pod_Parts", + "Pod_Frame", + "Misc") + req_access = list(ACCESS_MECHANIC) + +/obj/machinery/mecha_part_fabricator/spacepod/New() + ..() + QDEL_LIST(component_parts) + component_parts = list() + component_parts += new /obj/item/circuitboard/podfab(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/mecha_part_fabricator/robot + name = "Robotic Fabricator" + part_sets = list("Cyborg") diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 6d8852908a32..ec6f2c6456e1 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -1,1521 +1,1522 @@ -/obj/mecha - name = "Mecha" - desc = "Exosuit" - icon = 'icons/mecha/mecha.dmi' - density = 1 //Dense. To raise the heat. - opacity = 1 ///opaque. Menacing. - anchored = 1 //no pulling around. - resistance_flags = FIRE_PROOF | ACID_PROOF - layer = MOB_LAYER //icon draw layer - infra_luminosity = 15 //byond implementation is bugged. - force = 5 - max_integrity = 300 //max_integrity is base health - armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) - var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) - var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. - var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) - var/can_move = 0 // time of next allowed movement - var/mob/living/carbon/occupant = null - var/step_in = 10 //make a step in step_in/10 sec. - var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. - var/normal_step_energy_drain = 10 - var/step_energy_drain = 10 - var/melee_energy_drain = 15 - var/overload_step_energy_drain_min = 100 - var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. - var/obj/item/stock_parts/cell/cell - var/state = 0 - var/list/log = new - var/last_message = 0 - var/add_req_access = 1 - var/maint_access = 1 - var/dna //dna-locking the mech - var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference - var/datum/effect_system/spark_spread/spark_system = new - var/lights = 0 - var/lights_power = 6 - var/emagged = FALSE - - //inner atmos - var/use_internal_tank = 0 - var/internal_tank_valve = ONE_ATMOSPHERE - var/obj/machinery/portable_atmospherics/canister/internal_tank - var/datum/gas_mixture/cabin_air - var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null - - var/obj/item/radio/radio = null - var/list/trackers = list() - - var/max_temperature = 25000 - var/internal_damage_threshold = 50 //health percentage below which internal damage is possible - var/internal_damage = 0 //contains bitflags - - var/list/operation_req_access = list()//required access level for mecha operation - var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//required access level to open cell compartment - - var/wreckage - - var/list/equipment = new - var/obj/item/mecha_parts/mecha_equipment/selected - var/max_equip = 3 - var/datum/events/events - var/turf/crashing = null - var/occupant_sight_flags = 0 - - var/stepsound = 'sound/mecha/mechstep.ogg' - var/turnsound = 'sound/mecha/mechturn.ogg' - var/nominalsound = 'sound/mecha/nominal.ogg' - var/zoomsound = 'sound/mecha/imag_enh.ogg' - var/critdestrsound = 'sound/mecha/critdestr.ogg' - var/weapdestrsound = 'sound/mecha/weapdestr.ogg' - var/lowpowersound = 'sound/mecha/lowpower.ogg' - var/longactivationsound = 'sound/mecha/nominal.ogg' - var/starting_voice = /obj/item/mecha_modkit/voice - var/activated = FALSE - var/power_warned = FALSE - - var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed - - var/melee_cooldown = 10 - var/melee_can_hit = 1 - - // Action vars - var/defence_mode = FALSE - var/defence_mode_deflect_chance = 35 - var/leg_overload_mode = FALSE - var/leg_overload_coeff = 100 - var/thrusters_active = FALSE - var/smoke = 5 - var/smoke_ready = 1 - var/smoke_cooldown = 100 - var/zoom_mode = FALSE - var/phasing = FALSE - var/phasing_energy_drain = 200 - var/phase_state = "" //icon_state when phasing - - hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) - -/obj/mecha/Initialize() - . = ..() - events = new - icon_state += "-open" - add_radio() - add_cabin() - add_airtank() - spark_system.set_up(2, 0, src) - spark_system.attach(src) - smoke_system.set_up(3, src) - smoke_system.attach(src) - add_cell() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - log_message("[src] created.") - GLOB.mechas_list += src //global mech list - prepare_huds() - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) - diag_hud.add_to_hud(src) - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - var/obj/item/mecha_modkit/voice/V = new starting_voice(src) - V.install(src) - qdel(V) - -//////////////////////// -////// Helpers ///////// -//////////////////////// - -/obj/mecha/get_cell() - return cell - -/obj/mecha/proc/add_airtank() - internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) - return internal_tank - -/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) - if(C) - C.forceMove(src) - cell = C - return - cell = new/obj/item/stock_parts/cell/high/plus(src) - -/obj/mecha/proc/add_cabin() - cabin_air = new - cabin_air.temperature = T20C - cabin_air.volume = 200 - cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) - return cabin_air - -/obj/mecha/proc/add_radio() - radio = new(src) - radio.name = "[src] radio" - radio.icon = icon - radio.icon_state = icon_state - radio.subspace_transmission = 1 - -/obj/mecha/examine(mob/user) - . = ..() - var/integrity = obj_integrity * 100 / max_integrity - switch(integrity) - if(85 to 100) - . += "It's fully intact." - if(65 to 85) - . += "It's slightly damaged." - if(45 to 65) - . += "It's badly damaged." - if(25 to 45) - . += "It's heavily damaged." - else - . += "It's falling apart." - if(equipment && equipment.len) - . += "It's equipped with:" - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - . += "[bicon(ME)] [ME]" - -/obj/mecha/hear_talk(mob/M, list/message_pieces) - if(M == occupant && radio.broadcasting) - radio.talk_into(M, message_pieces) - -/obj/mecha/proc/click_action(atom/target, mob/user, params) - if(!occupant || occupant != user ) - return - if(user.incapacitated()) - return - if(phasing) - occupant_message("Unable to interact with objects while phasing.") - return - if(state) - occupant_message("Maintenance protocols in effect.") - return - if(!get_charge()) - return - if(src == target) - return - - var/dir_to_target = get_dir(src, target) - if(dir_to_target && !(dir_to_target & dir))//wrong direction - return - - if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) - target = safepick(view(3,target)) - if(!target) - return - - var/mob/living/L = user - if(!target.Adjacent(src)) - if(selected && selected.is_ranged()) - if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) - to_chat(L, "You don't want to harm other living beings!") - return - selected.action(target, params) - else if(selected && selected.is_melee()) - if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm other living beings!") - return - selected.action(target, params) - else - if(internal_damage & MECHA_INT_CONTROL_LOST) - target = safepick(oview(1, src)) - if(!melee_can_hit || !isatom(target)) - return - target.mech_melee_attack(src) - melee_can_hit = 0 - spawn(melee_cooldown) - melee_can_hit = 1 - -/obj/mecha/proc/mech_toxin_damage(mob/living/target) - playsound(src, 'sound/effects/spray2.ogg', 50, 1) - if(target.reagents) - if(target.reagents.get_reagent_amount("atropine") + force < force*2) - target.reagents.add_reagent("atropine", force/2) - if(target.reagents.get_reagent_amount("toxin") + force < force*2) - target.reagents.add_reagent("toxin", force/2.5) - -/obj/mecha/proc/range_action(atom/target) - return - - -////////////////////////////////// -//////// Movement procs //////// -////////////////////////////////// - -/obj/mecha/Move(atom/newLoc, direct) - . = ..() - if(.) - events.fireEvent("onMove",get_turf(src)) - -/obj/mecha/Process_Spacemove(var/movement_dir = 0) - . = ..() - if(.) - return 1 - if(thrusters_active && movement_dir && use_power(step_energy_drain)) - return 1 - - var/atom/movable/backup = get_spacemove_backup() - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - if(backup.newtonian_move(turn(movement_dir, 180))) - if(occupant) - to_chat(occupant, "You push off of [backup] to propel yourself.") - return 1 - -/obj/mecha/relaymove(mob/user, direction) - if(!direction) - return - if(user != occupant) //While not "realistic", this piece is player friendly. - user.forceMove(get_turf(src)) - to_chat(user, "You climb out from [src].") - return 0 - if(connected_port) - if(world.time - last_message > 20) - occupant_message("Unable to move while connected to the air system port!") - last_message = world.time - return 0 - if(state) - occupant_message("Maintenance protocols in effect.") - return - return domove(direction) - -/obj/mecha/proc/domove(direction) - if(can_move >= world.time) - return 0 - if(!Process_Spacemove(direction)) - return 0 - if(!has_charge(step_energy_drain)) - return 0 - if(defence_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in defence mode.") - last_message = world.time - return 0 - if(zoom_mode) - if(world.time - last_message > 20) - occupant_message("Unable to move while in zoom mode.") - last_message = world.time - return 0 - - var/move_result = 0 - var/move_type = 0 - if(internal_damage & MECHA_INT_CONTROL_LOST) - move_result = mechsteprand() - move_type = MECHAMOVE_RAND - else if(dir != direction) - move_result = mechturn(direction) - move_type = MECHAMOVE_TURN - else - move_result = mechstep(direction) - move_type = MECHAMOVE_STEP - - if(move_result && move_type) - aftermove(move_type) - can_move = world.time + step_in - return TRUE - return FALSE - -/obj/mecha/proc/aftermove(move_type) - use_power(step_energy_drain) - if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc - if(possible_port) - var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) - if(A) - A.target = possible_port - else - occupant.clear_alert("mechaport") - if(leg_overload_mode) - if(obj_integrity < max_integrity - max_integrity / 3) - leg_overload_mode = FALSE - step_in = initial(step_in) - step_energy_drain = initial(step_energy_drain) - occupant_message("Leg actuators damage threshold exceded. Disabling overload.") - -/obj/mecha/proc/mechturn(direction) - dir = direction - if(turnsound) - playsound(src,turnsound,40,1) - return 1 - -/obj/mecha/proc/mechstep(direction) - . = step(src, direction) - if(!.) - if(phasing && get_charge() >= phasing_energy_drain) - if(can_move < world.time) - . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself - flick("phazon-phase", src) - forceMove(get_step(src, direction)) - use_power(phasing_energy_drain) - playsound(src, stepsound, 40, 1) - can_move = world.time + (step_in * 3) - else if(stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/proc/mechsteprand() - . = step_rand(src) - if(. && stepsound) - playsound(src, stepsound, 40, 1) - -/obj/mecha/Bump(var/atom/obstacle, bump_allowed) - if(throwing) //high velocity mechas in your face! - var/breakthrough = 0 - if(istype(obstacle, /obj/structure/window)) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/grille/)) - var/obj/structure/grille/G = obstacle - G.obj_break() - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/table)) - var/obj/structure/table/T = obstacle - qdel(T) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/rack)) - new /obj/item/rack_parts(obstacle.loc) - qdel(obstacle) - breakthrough = 1 - - else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) - obstacle.ex_act(1) - - else if(isliving(obstacle)) - var/mob/living/L = obstacle - var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') - if(L.flags & GODMODE) - return - L.take_overall_damage(5,0) - if(L.buckled) - L.buckled = 0 - L.Stun(5) - L.Weaken(5) - L.apply_effect(STUTTER, 5) - playsound(src, pick(hit_sound), 50, 0, 0) - breakthrough = 1 - - else - if(throwing) - throwing.finalize(FALSE) - crashing = null - - ..() - - if(breakthrough) - if(crashing) - spawn(1) - throw_at(crashing, 50, throw_speed) - else - spawn(1) - crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. - throw_at(crashing, 50, throw_speed) - - else - if(bump_allowed) - if(..()) - return - if(isobj(obstacle)) - var/obj/O = obstacle - if(istype(O, /obj/effect/portal)) //derpfix - anchored = 0 - O.Bumped(src) - spawn(0) //countering portal teleport spawn(0), hurr - anchored = 1 - else if(!O.anchored) - step(obstacle, dir) - else if(ismob(obstacle)) - step(obstacle, dir) - - -/////////////////////////////////// -//////// Internal damage //////// -/////////////////////////////////// - -/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) - if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) - return - if(prob(20)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - for(var/T in possible_int_damage) - if(internal_damage & T) - possible_int_damage -= T - var/int_dam_flag = safepick(possible_int_damage) - if(int_dam_flag) - setInternalDamage(int_dam_flag) - if(prob(5)) - if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) - var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) - if(ME) - qdel(ME) - -/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) - return int_dam_flag ? internal_damage&int_dam_flag : internal_damage - - -/obj/mecha/proc/setInternalDamage(int_dam_flag) - internal_damage |= int_dam_flag - log_append_to_last("Internal damage of type [int_dam_flag].",1) - occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) - diag_hud_set_mechstat() - -/obj/mecha/proc/clearInternalDamage(int_dam_flag) - internal_damage &= ~int_dam_flag - switch(int_dam_flag) - if(MECHA_INT_TEMP_CONTROL) - occupant_message("Life support system reactivated.") - if(MECHA_INT_FIRE) - occupant_message("Internal fire extinquished.") - if(MECHA_INT_TANK_BREACH) - occupant_message("Damaged internal tank has been sealed.") - diag_hud_set_mechstat() - - -//////////////////////////////////////// -//////// Health related procs //////// -//////////////////////////////////////// - -/obj/mecha/proc/get_armour_facing(relative_dir) - switch(relative_dir) - if(0) // BACKSTAB! - return facing_modifiers[MECHA_BACK_ARMOUR] - if(45, 90, 270, 315) - return facing_modifiers[MECHA_SIDE_ARMOUR] - if(225, 180, 135) - return facing_modifiers[MECHA_FRONT_ARMOUR] - return 1 //always return non-0 - -/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - spark_system.start() - switch(damage_flag) - if("fire") - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) - if("melee") - check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) - else - check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) - if(. >= 5 || prob(33)) - occupant_message("Taking damage!") - log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") - -/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - . = ..() - if(!damage_amount) - return 0 - var/booster_deflection_modifier = 1 - var/booster_damage_modifier = 1 - if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") - for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) - if(B.projectile_react()) - booster_deflection_modifier = B.deflect_coeff - booster_damage_modifier = B.damage_coeff - break - else if(damage_flag == "melee") - for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) - if(B.attack_react()) - booster_deflection_modifier *= B.deflect_coeff - booster_damage_modifier *= B.damage_coeff - break - - if(attack_dir) - var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) - booster_damage_modifier /= facing_modifier - booster_deflection_modifier *= facing_modifier - if(prob(deflect_chance * booster_deflection_modifier)) - visible_message("[src]'s armour deflects the attack!") - log_message("Armor saved.") - return 0 - if(.) - . *= booster_damage_modifier - -/obj/mecha/attack_hand(mob/living/user) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) - playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) - user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") - log_message("Attack by hand/paw. Attacker - [user].") - - -/obj/mecha/attack_alien(mob/living/user) - log_message("Attack by alien. Attacker - [user].", color = "red") - playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) - attack_generic(user, 15, BRUTE, "melee", 0) - -/obj/mecha/attack_animal(mob/living/simple_animal/user) - log_message("Attack by simple animal. Attacker - [user].") - if(!user.melee_damage_upper && !user.obj_damage) - user.custom_emote(1, "[user.friendly] [src].") - return FALSE - else - var/play_soundeffect = 1 - if(user.environment_smash) - play_soundeffect = 0 - playsound(src, 'sound/effects/bang.ogg', 50, TRUE) - var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) - if(user.obj_damage) - animal_damage = user.obj_damage - animal_damage = min(animal_damage, 20*user.environment_smash) - user.create_attack_log("attacked [name]") - attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) - return TRUE - -/obj/mecha/hulk_damage() - return 15 - -/obj/mecha/attack_hulk(mob/living/carbon/human/user) - . = ..() - if(.) - log_message("Attack by hulk. Attacker - [user].", 1) - add_attack_logs(user, src, "Punched with hulk powers") - -/obj/mecha/blob_act(obj/structure/blob/B) - log_message("Attack by blob. Attacker - [B].") - take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) - -/obj/mecha/attack_tk() - return - -/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper - log_message("Hit by [AM].") - . = ..() - -/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper - log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") - ..() - -/obj/mecha/ex_act(severity, target) - log_message("Affected by explosion of severity: [severity].") - if(prob(deflect_chance)) - severity++ - log_message("Armor saved, changing severity to [severity]") - ..() - severity++ - for(var/X in equipment) - var/obj/item/mecha_parts/mecha_equipment/ME = X - ME.ex_act(severity) - for(var/Y in trackers) - var/obj/item/mecha_parts/mecha_tracking/MT = Y - MT.ex_act(severity) - if(occupant) - occupant.ex_act(severity) - -/obj/mecha/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - icon_state = initial(icon_state)+"-open" - setDir(dir_in) - -/obj/mecha/Destroy() - if(occupant) - occupant.SetSleeping(destruction_sleep_duration) - go_out() - var/mob/living/silicon/ai/AI - for(var/mob/M in src) //Let's just be ultra sure - if(isAI(M)) - occupant = null - AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. - else - M.forceMove(loc) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - E.detach(loc) - qdel(E) - equipment.Cut() - QDEL_NULL(cell) - QDEL_NULL(internal_tank) - if(AI) - AI.gib() //No wreck, no AI to recover - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - if(loc) - loc.assume_air(cabin_air) - air_update_turf() - else - qdel(cabin_air) - cabin_air = null - QDEL_NULL(spark_system) - QDEL_NULL(smoke_system) - - GLOB.mechas_list -= src //global mech list - return ..() - -//TODO -/obj/mecha/emp_act(severity) - if(get_charge()) - use_power((cell.charge/3)/(severity*2)) - take_damage(30 / severity, BURN, "energy", 1) - log_message("EMP detected", 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) - -/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > max_temperature) - log_message("Exposed to dangerous temperature.", 1) - take_damage(5, BURN, 0, 1) - check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) - -////////////////////// -////// AttackBy ////// -////////////////////// - -/obj/mecha/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/mmi)) - if(mmi_move_inside(W,user)) - to_chat(user, "[src]-MMI interface initialized successfuly") - else - to_chat(user, "[src]-MMI interface initialization failed.") - return - - if(istype(W, /obj/item/mecha_parts/mecha_equipment)) - var/obj/item/mecha_parts/mecha_equipment/E = W - spawn() - if(E.can_attach(src)) - if(!user.drop_item()) - return - E.attach(src) - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - else - to_chat(user, "You were unable to attach [W] to [src]!") - return - - if(W.GetID()) - if(add_req_access || maint_access) - if(internals_access_allowed(usr)) - var/obj/item/card/id/id_card - if(istype(W, /obj/item/card/id)) - id_card = W - else - var/obj/item/pda/pda = W - id_card = pda.id - output_maintenance_dialog(id_card, user) - return - else - to_chat(user, "Invalid ID: Access denied.") - else - to_chat(user, "Maintenance protocols disabled by operator.") - - else if(istype(W, /obj/item/stack/cable_coil)) - if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) - var/obj/item/stack/cable_coil/CC = W - if(CC.amount > 1) - CC.use(2) - clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) - to_chat(user, "You replace the fused wires.") - else - to_chat(user, "There's not enough wire to finish the task.") - return - - else if(istype(W, /obj/item/stock_parts/cell)) - if(state==4) - if(!cell) - if(!user.drop_item()) - return - to_chat(user, "You install the powercell.") - W.forceMove(src) - cell = W - log_message("Powercell installed") - else - to_chat(user, "There's already a powercell installed.") - return - - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) - if(!user.unEquip(W)) - to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") - return - W.forceMove(src) - trackers += W - user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") - diag_hud_set_mechtracking() - return - - else if(istype(W, /obj/item/paintkit)) - if(occupant) - to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") - return - - var/obj/item/paintkit/P = W - var/found = null - - for(var/type in P.allowed_types) - if(type == initial_icon) - found = 1 - break - - if(!found) - to_chat(user, "That kit isn't meant for use on this class of exosuit.") - return - - user.visible_message("[user] opens [P] and spends some quality time customising [src].") - - name = P.new_name - desc = P.new_desc - initial_icon = P.new_icon - reset_icon() - - user.drop_item() - qdel(P) - - else if(istype(W, /obj/item/mecha_modkit)) - if(occupant) - to_chat(user, "You can't access the mech's modification port while it is occupied.") - return - var/obj/item/mecha_modkit/M = W - if(do_after_once(user, M.install_time, target = src)) - M.install(src, user) - else - to_chat(user, "You stop installing [M].") - - else - return ..() - - -/obj/mecha/crowbar_act(mob/user, obj/item/I) - if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(state == 2) - state = 3 - to_chat(user, "You open the hatch to the power unit") - else if(state == 3) - state = 2 - to_chat(user, "You close the hatch to the power unit") - else - // Since having maint protocols available is controllable by the MMI, I see this as a consensual way to remove an MMI without destroying the mech - user.visible_message("[user] begins levering out the MMI from the [src].", "You begin to lever out the MMI from the [src].") - to_chat(occupant, "[user] is prying you out of the exosuit!") - if(I.use_tool(src, user, 80, volume = I.tool_volume) && pilot_is_mmi()) - user.visible_message("[user] pries the MMI out of the [src]!", "You finish removing the MMI from the [src]!") - go_out() - -/obj/mecha/screwdriver_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - if(!(state==3 && cell) && !(state==4 && cell)) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - clearInternalDamage(MECHA_INT_TEMP_CONTROL) - to_chat(user, "You repair the damaged temperature controller.") - else if(state==3 && cell) - cell.forceMove(loc) - cell = null - state = 4 - to_chat(user, "You unscrew and pry out the powercell.") - log_message("Powercell removed") - else if(state==4 && cell) - state=3 - to_chat(user, "You screw the cell in place.") - -/obj/mecha/wrench_act(mob/user, obj/item/I) - if(state != 1 && state != 2) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(state==1) - state = 2 - to_chat(user, "You undo the securing bolts.") - else - state = 1 - to_chat(user, "You tighten the securing bolts.") - -/obj/mecha/welder_act(mob/user, obj/item/I) - if(user.a_intent == INTENT_HARM) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if((obj_integrity >= max_integrity) && !internal_damage) - to_chat(user, "[src] is at full integrity!") - return - WELDER_ATTEMPT_REPAIR_MESSAGE - if(I.use_tool(src, user, 15, volume = I.tool_volume)) - if(internal_damage & MECHA_INT_TANK_BREACH) - clearInternalDamage(MECHA_INT_TANK_BREACH) - user.visible_message("[user] repairs the damaged gas tank.", "You repair the damaged gas tank.") - else if(obj_integrity < max_integrity) - user.visible_message("[user] repairs some damage to [name].", "You repair some damage to [name].") - obj_integrity += min(10, max_integrity - obj_integrity) - else - to_chat(user, "[src] is at full integrity!") - -/obj/mecha/mech_melee_attack(obj/mecha/M) - if(!has_charge(melee_energy_drain)) - return 0 - use_power(melee_energy_drain) - if(M.damtype == BRUTE || M.damtype == BURN) - add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") - . = ..() - -/obj/mecha/emag_act(mob/user) - to_chat(user, "[src]'s ID slot rejects the card.") - return - - -///////////////////////////////////// -//////////// AI piloting //////////// -///////////////////////////////////// - -/obj/mecha/attack_ai(mob/living/silicon/ai/user) - if(!isAI(user)) - return - //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. - if(user.can_dominate_mechs) - examine(user) //Get diagnostic information! - for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) - to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") - to_chat(user, "[B.get_mecha_info_text()]") - break - //Nothing like a big, red link to make the player feel powerful! - to_chat(user, "ASSUME DIRECT CONTROL?
    ") - else - examine(user) - if(occupant) - user << "This exosuit has a pilot and cannot be controlled." - return - var/can_control_mech = FALSE - for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) - can_control_mech = TRUE - to_chat(user, "[bicon(src)] Status of [name]:\n\ - [A.get_mecha_info_text()]") - break - if(!can_control_mech) - to_chat(user, "You cannot control exosuits without AI control beacons installed.") - return - to_chat(user, "Take control of exosuit?
    ") - -/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) - if(!..()) - return - - //Transfer from core or card to mech. Proc is called by mech. - switch(interaction) - if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. - if(!maint_access) //Mech must be in maint mode to allow carding. - to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") - return - AI = occupant - if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot - to_chat(user, "No AI detected in the [name] onboard computer.") - return - if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. - to_chat(user, "ACCESS DENIED.") - return - AI.aiRestorePowerRoutine = 0//So the AI initially has power. - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - AI.loc = card - occupant = null - AI.controlled_mech = null - AI.remote_control = null - icon_state = initial(icon_state)+"-open" - to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") - - if(AI_MECH_HACK) //Called by AIs on the mech - AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) - if(AI.can_dominate_mechs) - if(occupant) //Oh, I am sorry, were you using that? - to_chat(AI, "Pilot detected! Forced ejection initiated!") - to_chat(occupant, "You have been forcibly ejected!") - go_out(1) //IT IS MINE, NOW. SUCK IT, RD! - ai_enter_mech(AI, interaction) - - if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. - AI = locate(/mob/living/silicon/ai) in card - if(!AI) - to_chat(user, "There is no AI currently installed on this device.") - return - else if(AI.stat || !AI.client) - to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") - return - else if(occupant || dna) //Normal AIs cannot steal mechs! - to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") - return - AI.control_disabled = 0 - AI.aiRadio.disabledAi = 0 - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - ai_enter_mech(AI, interaction) - -//Hack and From Card interactions share some code, so leave that here for both to use. -/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) - AI.aiRestorePowerRoutine = 0 - AI.loc = src - occupant = AI - icon_state = initial(icon_state) - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - AI.cancel_camera() - AI.controlled_mech = src - AI.remote_control = src - AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. - AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. - to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ - : "You have been uploaded to a mech's onboard computer."]") - to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") - if(interaction == AI_TRANS_FROM_CARD) - GrantActions(AI, FALSE) - else - GrantActions(AI, !AI.can_dominate_mechs) - -///////////////////////////////////// -//////// Atmospheric stuff //////// -///////////////////////////////////// - -/obj/mecha/proc/get_turf_air() - var/turf/T = get_turf(src) - if(T) - . = T.return_air() - -/obj/mecha/remove_air(amount) - if(use_internal_tank) - return cabin_air.remove(amount) - else - var/turf/T = get_turf(src) - if(T) - return T.remove_air(amount) - -/obj/mecha/return_air() - if(use_internal_tank) - return cabin_air - return get_turf_air() - -/obj/mecha/proc/return_pressure() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_pressure() - -//skytodo: //No idea what you want me to do here, mate. -/obj/mecha/proc/return_temperature() - var/datum/gas_mixture/t_air = return_air() - if(t_air) - . = t_air.return_temperature() - -/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !istype(new_port) || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - connected_port.parent.reconcile_air() - - if(occupant) - occupant.clear_alert("mechaport") - occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) - - log_message("Connected to gas port.") - return 1 - -/obj/mecha/proc/disconnect() - if(!connected_port) - return 0 - - connected_port.connected_device = null - connected_port = null - log_message("Disconnected from gas port.") - if(occupant) - occupant.clear_alert("mechaport_d") - return 1 - -/obj/mecha/portableConnectorReturnAir() - return internal_tank.return_air() - -/obj/mecha/proc/toggle_lights() - lights = !lights - if(lights) - set_light(light_range + lights_power) - else - set_light(light_range - lights_power) - occupant_message("Toggled lights [lights ? "on" : "off"].") - log_message("Toggled lights [lights ? "on" : "off"].") - -/obj/mecha/proc/toggle_internal_tank() - use_internal_tank = !use_internal_tank - occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") - -/obj/mecha/MouseDrop_T(mob/M, mob/user) - if(user.incapacitated()) - return - if(user != M) - return - log_message("[user] tries to move in.") - if(occupant) - to_chat(user, "The [src] is already occupied!") - log_append_to_last("Permission denied.") - return - var/passed - if(dna) - if(ishuman(user)) - if(user.dna.unique_enzymes == dna) - passed = 1 - else if(operation_allowed(user)) - passed = 1 - if(!passed) - to_chat(user, "Access denied.") - log_append_to_last("Permission denied.") - return - if(user.buckled) - to_chat(user, "You are currently buckled and cannot move.") - log_append_to_last("Permission denied.") - return - if(user.has_buckled_mobs()) //mob attached to us - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - return - - visible_message("[user] starts to climb into [src]") - - if(do_after(user, 40, target = src)) - if(obj_integrity <= 0) - to_chat(user, "You cannot get in the [name], it has been destroyed!") - else if(occupant) - to_chat(user, "[occupant] was faster! Try better next time, loser.") - else if(user.buckled) - to_chat(user, "You can't enter the exosuit while buckled.") - else if(user.has_buckled_mobs()) - to_chat(user, "You can't enter the exosuit with other creatures attached to you!") - else - moved_inside(user) - else - to_chat(user, "You stop entering the exosuit!") - -/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) - if(H && H.client && H in range(1)) - occupant = H - H.stop_pulling() - H.forceMove(src) - H.reset_perspective(src) - add_fingerprint(H) - GrantActions(H, human_occupant = 1) - forceMove(loc) - log_append_to_last("[H] moved in as pilot.") - icon_state = reset_icon() - dir = dir_in - playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) - if(!activated) - occupant << sound(longactivationsound, volume = 50) - activated = TRUE - else if(!hasInternalDamage()) - occupant << sound(nominalsound, volume = 50) - if(state) - H.throw_alert("locked", /obj/screen/alert/mech_maintenance) - return 1 - else - return 0 - -/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected!") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level!") - return 0 - else if(occupant) - to_chat(user, "Occupant detected!") - return 0 - else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) - to_chat(user, "Access denied. [name] is secured with a DNA lock.") - return 0 - - if(do_after(user, 40, target = src)) - if(!occupant) - return mmi_moved_inside(mmi_as_oc,user) - else - to_chat(user, "Occupant detected!") - else - to_chat(user, "You stop inserting the MMI.") - return 0 - -/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) - if(mmi_as_oc && user in range(1)) - if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) - to_chat(user, "Consciousness matrix not detected.") - return 0 - else if(mmi_as_oc.brainmob.stat) - to_chat(user, "Beta-rhythm below acceptable level.") - return 0 - if(!user.unEquip(mmi_as_oc)) - to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") - return 0 - var/mob/brainmob = mmi_as_oc.brainmob - brainmob.reset_perspective(src) - occupant = brainmob - brainmob.forceMove(src) //should allow relaymove - brainmob.canmove = 1 - if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi_as_oc - if(R.imprinted_master) - to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") - mmi_as_oc.loc = src - mmi_as_oc.mecha = src - Entered(mmi_as_oc) - Move(loc) - icon_state = reset_icon() - dir = dir_in - log_message("[mmi_as_oc] moved in as pilot.") - if(!hasInternalDamage()) - to_chat(occupant, sound(nominalsound, volume=50)) - GrantActions(brainmob) - return 1 - else - return 0 - -/obj/mecha/proc/pilot_is_mmi() - var/atom/movable/mob_container - if(istype(occupant, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/brain = occupant - mob_container = brain.container - if(istype(mob_container, /obj/item/mmi)) - return 1 - return 0 - -/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) - return - -/obj/mecha/Exited(atom/movable/M, atom/newloc) - if(occupant && occupant == M) // The occupant exited the mech without calling go_out() - go_out(1, newloc) - -/obj/mecha/proc/go_out(forced, atom/newloc = loc) - if(!occupant) - return - var/atom/movable/mob_container - occupant.clear_alert("charge") - occupant.clear_alert("locked") - occupant.clear_alert("mech damage") - occupant.clear_alert("mechaport") - occupant.clear_alert("mechaport_d") - if(ishuman(occupant)) - mob_container = occupant - RemoveActions(occupant, human_occupant = 1) - else if(isbrain(occupant)) - var/mob/living/carbon/brain/brain = occupant - RemoveActions(brain) - mob_container = brain.container - else if(isAI(occupant)) - var/mob/living/silicon/ai/AI = occupant - if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. - RemoveActions(occupant) - occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. - occupant = null - return - else - if(!AI.linked_core || QDELETED(AI.linked_core)) - to_chat(AI, "Inactive core destroyed. Unable to return.") - AI.linked_core = null - return - to_chat(AI, "Returning to core...") - AI.controlled_mech = null - AI.remote_control = null - RemoveActions(occupant, 1) - mob_container = AI - newloc = get_turf(AI.linked_core) - qdel(AI.linked_core) - else - return - var/mob/living/L = occupant - occupant = null //we need it null when forceMove calls Exited(). - if(mob_container.forceMove(newloc))//ejecting mob container - log_message("[mob_container] moved out.") - L << browse(null, "window=exosuit") - - if(istype(mob_container, /obj/item/mmi)) - var/obj/item/mmi/mmi = mob_container - if(mmi.brainmob) - L.loc = mmi - L.reset_perspective() - mmi.mecha = null - mmi.update_icon() - L.canmove = 0 - if(istype(mmi, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = mmi - if(R.imprinted_master) - to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") - icon_state = initial(icon_state)+"-open" - dir = dir_in - - if(L && L.client) - L.client.RemoveViewMod("mecha") - zoom_mode = FALSE - -///////////////////////// -////// Access stuff ///// -///////////////////////// - -/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) - if(!ishuman(H)) - return 0 - for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, operation_req_access)) - return 1 - return 0 - - -/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) - for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) - if(check_access(ID, internals_req_access)) - return 1 - return 0 - - -/obj/mecha/check_access(obj/item/card/id/I, list/access_list) - if(!istype(access_list)) - return 1 - if(!access_list.len) //no requirements - return 1 - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(!istype(I) || !I.access) //not ID or no access - return 0 - if(access_list==operation_req_access) - for(var/req in access_list) - if(!(req in I.access)) //doesn't have this access - return 0 - else if(access_list==internals_req_access) - for(var/req in access_list) - if(req in I.access) - return 1 - return 1 - -/////////////////////// -///// Power stuff ///// -/////////////////////// - -/obj/mecha/proc/has_charge(amount) - return (get_charge()>=amount) - -/obj/mecha/proc/get_charge() - for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) - var/relay_charge = R.get_charge() - if(relay_charge) - return relay_charge - if(cell) - return max(0, cell.charge) - -/obj/mecha/proc/use_power(amount) - if(get_charge()) - cell.use(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/give_power(amount) - if(!isnull(get_charge())) - cell.give(amount) - if(occupant) - update_cell() - return 1 - return 0 - -/obj/mecha/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - occupant.clear_alert("charge") - if(0.5 to 0.75) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) - if(0.25 to 0.5) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) - if(power_warned) - power_warned = FALSE - if(0.01 to 0.25) - occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) - if(!power_warned) - occupant << sound(lowpowersound, volume = 50) - power_warned = TRUE - else - occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) - else - occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) - -/obj/mecha/proc/reset_icon() - if(initial_icon) - icon_state = initial_icon - else - icon_state = initial(icon_state) - return icon_state - -////////////////////////////////////////// -//////// Mecha global iterators //////// -////////////////////////////////////////// - -/obj/mecha/process() - process_internal_damage() - regulate_temp() - give_air() - update_huds() - -/obj/mecha/proc/process_internal_damage() - if(!internal_damage) - return - - if(internal_damage & MECHA_INT_FIRE) - if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) - clearInternalDamage(MECHA_INT_FIRE) - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) - setInternalDamage(MECHA_INT_TANK_BREACH) - - if(int_tank_air && int_tank_air.return_volume() > 0) - int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) - - if(cabin_air && cabin_air.return_volume()>0) - cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) - if(cabin_air.return_temperature() > max_temperature/2) - take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) - - if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank - if(internal_tank) - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) - if(loc) - loc.assume_air(leaked_gas) - air_update_turf() - else - qdel(leaked_gas) - - if(internal_damage & MECHA_INT_SHORT_CIRCUIT) - if(get_charge()) - spark_system.start() - cell.charge -= min(20,cell.charge) - cell.maxcharge -= min(20,cell.maxcharge) - -/obj/mecha/proc/regulate_temp() - if(internal_damage & MECHA_INT_TEMP_CONTROL) - return - - if(cabin_air && cabin_air.return_volume() > 0) - var/delta = cabin_air.temperature - T20C - cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) - -/obj/mecha/proc/give_air() - if(!internal_tank) - return - - var/datum/gas_mixture/tank_air = internal_tank.return_air() - - var/release_pressure = internal_tank_valve - var/cabin_pressure = cabin_air.return_pressure() - var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) - var/transfer_moles = 0 - if(pressure_delta > 0) //cabin pressure lower than release pressure - if(tank_air.return_temperature() > 0) - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) - cabin_air.merge(removed) - else if(pressure_delta < 0) //cabin pressure higher than release pressure - var/datum/gas_mixture/t_air = return_air() - pressure_delta = cabin_pressure - release_pressure - if(t_air) - pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) - if(pressure_delta > 0) //if location pressure is lower than cabin pressure - transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) - var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) - if(t_air) - t_air.merge(removed) - else //just delete the cabin gas, we're in space or some shit - qdel(removed) - -/obj/mecha/proc/update_huds() - diag_hud_set_mechhealth() - diag_hud_set_mechcell() - diag_hud_set_mechstat() - diag_hud_set_mechtracking() - - -/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) - -/obj/mecha/update_remote_sight(mob/living/user) - if(occupant_sight_flags) - if(user == occupant) - user.sight |= occupant_sight_flags - - ..() - -/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect) - if(selected) - used_item = selected - else if(!visual_effect_icon) - visual_effect_icon = ATTACK_EFFECT_SMASH - if(damtype == BURN) - visual_effect_icon = ATTACK_EFFECT_MECHFIRE - else if(damtype == TOX) - visual_effect_icon = ATTACK_EFFECT_MECHTOXIN - ..() - -/obj/mecha/obj_destruction() - if(wreckage) - var/mob/living/silicon/ai/AI - if(isAI(occupant)) - AI = occupant - occupant = null - var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) - for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) - if(E.salvageable && prob(30)) - WR.crowbar_salvage += E - E.detach(WR) //detaches from src into WR - E.equip_ready = 1 - else - E.detach(loc) - qdel(E) - if(cell) - WR.crowbar_salvage += cell - cell.forceMove(WR) - cell.charge = rand(0, cell.charge) - cell = null - if(internal_tank) - WR.crowbar_salvage += internal_tank - internal_tank.forceMove(WR) - cell = null - . = ..() - -/obj/mecha/CtrlClick(mob/living/L) - if(occupant != L || !istype(L)) - return ..() - - var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) - var/list/choices_to_refs = list() - - for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) - if(!MT.selectable || selected == MT) - continue - var/mutable_appearance/clean/MA = new(MT) - choices[MT.name] = MA - choices_to_refs[MT.name] = MT - - var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) - if(!check_menu(L) || choice == "Cancel / No Change") - return - - var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) - if(istype(new_sel)) - selected = new_sel - occupant_message("You switch to [selected].") - visible_message("[src] raises [selected]") - send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) - -/obj/mecha/proc/check_menu(mob/living/L) - if(L != occupant || !istype(L)) - return FALSE - if(L.incapacitated()) - return FALSE - return TRUE \ No newline at end of file +/obj/mecha + name = "Mecha" + desc = "Exosuit" + icon = 'icons/mecha/mecha.dmi' + density = 1 //Dense. To raise the heat. + opacity = 1 ///opaque. Menacing. + anchored = 1 //no pulling around. + resistance_flags = FIRE_PROOF | ACID_PROOF + layer = MOB_LAYER //icon draw layer + infra_luminosity = 15 //byond implementation is bugged. + force = 5 + max_integrity = 300 //max_integrity is base health + armor = list(melee = 20, bullet = 10, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 100, acid = 100) + var/list/facing_modifiers = list(MECHA_FRONT_ARMOUR = 1.5, MECHA_SIDE_ARMOUR = 1, MECHA_BACK_ARMOUR = 0.5) + var/ruin_mecha = FALSE //if the mecha starts on a ruin, don't automatically give it a tracking beacon to prevent metagaming. + var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) + var/can_move = 0 // time of next allowed movement + var/mob/living/carbon/occupant = null + var/step_in = 10 //make a step in step_in/10 sec. + var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South. + var/normal_step_energy_drain = 10 + var/step_energy_drain = 10 + var/melee_energy_drain = 15 + var/overload_step_energy_drain_min = 100 + var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act. + var/obj/item/stock_parts/cell/cell + var/state = 0 + var/list/log = new + var/last_message = 0 + var/add_req_access = 1 + var/maint_access = 1 + var/dna //dna-locking the mech + var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference + var/datum/effect_system/spark_spread/spark_system = new + var/lights = 0 + var/lights_power = 6 + var/emagged = FALSE + + //inner atmos + var/use_internal_tank = 0 + var/internal_tank_valve = ONE_ATMOSPHERE + var/obj/machinery/portable_atmospherics/canister/internal_tank + var/datum/gas_mixture/cabin_air + var/obj/machinery/atmospherics/unary/portables_connector/connected_port = null + + var/obj/item/radio/radio = null + var/list/trackers = list() + + var/max_temperature = 25000 + var/internal_damage_threshold = 50 //health percentage below which internal damage is possible + var/internal_damage = 0 //contains bitflags + + var/list/operation_req_access = list()//required access level for mecha operation + var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//required access level to open cell compartment + + var/wreckage + + var/list/equipment = new + var/obj/item/mecha_parts/mecha_equipment/selected + var/max_equip = 3 + var/datum/events/events + var/turf/crashing = null + var/occupant_sight_flags = 0 + + var/stepsound = 'sound/mecha/mechstep.ogg' + var/turnsound = 'sound/mecha/mechturn.ogg' + var/nominalsound = 'sound/mecha/nominal.ogg' + var/zoomsound = 'sound/mecha/imag_enh.ogg' + var/critdestrsound = 'sound/mecha/critdestr.ogg' + var/weapdestrsound = 'sound/mecha/weapdestr.ogg' + var/lowpowersound = 'sound/mecha/lowpower.ogg' + var/longactivationsound = 'sound/mecha/nominal.ogg' + var/starting_voice = /obj/item/mecha_modkit/voice + var/activated = FALSE + var/power_warned = FALSE + + var/destruction_sleep_duration = 1 //Time that mech pilot is put to sleep for if mech is destroyed + + var/melee_cooldown = 10 + var/melee_can_hit = 1 + + // Action vars + var/defence_mode = FALSE + var/defence_mode_deflect_chance = 35 + var/leg_overload_mode = FALSE + var/leg_overload_coeff = 100 + var/thrusters_active = FALSE + var/smoke = 5 + var/smoke_ready = 1 + var/smoke_cooldown = 100 + var/zoom_mode = FALSE + var/phasing = FALSE + var/phasing_energy_drain = 200 + var/phase_state = "" //icon_state when phasing + + hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD) + +/obj/mecha/Initialize() + . = ..() + events = new + icon_state += "-open" + add_radio() + add_cabin() + add_airtank() + spark_system.set_up(2, 0, src) + spark_system.attach(src) + smoke_system.set_up(3, src) + smoke_system.attach(src) + add_cell() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + log_message("[src] created.") + GLOB.mechas_list += src //global mech list + prepare_huds() + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) + diag_hud.add_to_hud(src) + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + var/obj/item/mecha_modkit/voice/V = new starting_voice(src) + V.install(src) + qdel(V) + +//////////////////////// +////// Helpers ///////// +//////////////////////// + +/obj/mecha/get_cell() + return cell + +/obj/mecha/proc/add_airtank() + internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src) + return internal_tank + +/obj/mecha/proc/add_cell(var/obj/item/stock_parts/cell/C=null) + if(C) + C.forceMove(src) + cell = C + return + cell = new/obj/item/stock_parts/cell/high/plus(src) + +/obj/mecha/proc/add_cabin() + cabin_air = new + cabin_air.temperature = T20C + cabin_air.volume = 200 + cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature) + return cabin_air + +/obj/mecha/proc/add_radio() + radio = new(src) + radio.name = "[src] radio" + radio.icon = icon + radio.icon_state = icon_state + radio.subspace_transmission = 1 + +/obj/mecha/examine(mob/user) + . = ..() + var/integrity = obj_integrity * 100 / max_integrity + switch(integrity) + if(85 to 100) + . += "It's fully intact." + if(65 to 85) + . += "It's slightly damaged." + if(45 to 65) + . += "It's badly damaged." + if(25 to 45) + . += "It's heavily damaged." + else + . += "It's falling apart." + if(equipment && equipment.len) + . += "It's equipped with:" + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + . += "[bicon(ME)] [ME]" + +/obj/mecha/hear_talk(mob/M, list/message_pieces) + if(M == occupant && radio.broadcasting) + radio.talk_into(M, message_pieces) + +/obj/mecha/proc/click_action(atom/target, mob/user, params) + if(!occupant || occupant != user ) + return + if(user.incapacitated()) + return + if(phasing) + occupant_message("Unable to interact with objects while phasing.") + return + if(state) + occupant_message("Maintenance protocols in effect.") + return + if(!get_charge()) + return + if(src == target) + return + + var/dir_to_target = get_dir(src, target) + if(dir_to_target && !(dir_to_target & dir))//wrong direction + return + + if(hasInternalDamage(MECHA_INT_CONTROL_LOST)) + target = safepick(view(3,target)) + if(!target) + return + + var/mob/living/L = user + if(!target.Adjacent(src)) + if(selected && selected.is_ranged()) + if(HAS_TRAIT(L, TRAIT_PACIFISM) && selected.harmful) + to_chat(L, "You don't want to harm other living beings!") + return + selected.action(target, params) + else if(selected && selected.is_melee()) + if(isliving(target) && selected.harmful && HAS_TRAIT(L, TRAIT_PACIFISM)) + to_chat(user, "You don't want to harm other living beings!") + return + selected.action(target, params) + else + if(internal_damage & MECHA_INT_CONTROL_LOST) + target = safepick(oview(1, src)) + if(!melee_can_hit || !isatom(target)) + return + target.mech_melee_attack(src) + melee_can_hit = 0 + spawn(melee_cooldown) + melee_can_hit = 1 + +/obj/mecha/proc/mech_toxin_damage(mob/living/target) + playsound(src, 'sound/effects/spray2.ogg', 50, 1) + if(target.reagents) + if(target.reagents.get_reagent_amount("atropine") + force < force*2) + target.reagents.add_reagent("atropine", force/2) + if(target.reagents.get_reagent_amount("toxin") + force < force*2) + target.reagents.add_reagent("toxin", force/2.5) + +/obj/mecha/proc/range_action(atom/target) + return + + +////////////////////////////////// +//////// Movement procs //////// +////////////////////////////////// + +/obj/mecha/Move(atom/newLoc, direct) + . = ..() + if(.) + events.fireEvent("onMove",get_turf(src)) + +/obj/mecha/Process_Spacemove(var/movement_dir = 0) + . = ..() + if(.) + return 1 + if(thrusters_active && movement_dir && use_power(step_energy_drain)) + return 1 + + var/atom/movable/backup = get_spacemove_backup() + if(backup) + if(istype(backup) && movement_dir && !backup.anchored) + if(backup.newtonian_move(turn(movement_dir, 180))) + if(occupant) + to_chat(occupant, "You push off of [backup] to propel yourself.") + return 1 + +/obj/mecha/relaymove(mob/user, direction) + if(!direction) + return + if(user != occupant) //While not "realistic", this piece is player friendly. + user.forceMove(get_turf(src)) + to_chat(user, "You climb out from [src].") + return 0 + if(connected_port) + if(world.time - last_message > 20) + occupant_message("Unable to move while connected to the air system port!") + last_message = world.time + return 0 + if(state) + occupant_message("Maintenance protocols in effect.") + return + return domove(direction) + +/obj/mecha/proc/domove(direction) + if(can_move >= world.time) + return 0 + if(!Process_Spacemove(direction)) + return 0 + if(!has_charge(step_energy_drain)) + return 0 + if(defence_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in defence mode.") + last_message = world.time + return 0 + if(zoom_mode) + if(world.time - last_message > 20) + occupant_message("Unable to move while in zoom mode.") + last_message = world.time + return 0 + + var/move_result = 0 + var/move_type = 0 + if(internal_damage & MECHA_INT_CONTROL_LOST) + move_result = mechsteprand() + move_type = MECHAMOVE_RAND + else if(dir != direction) + move_result = mechturn(direction) + move_type = MECHAMOVE_TURN + else + move_result = mechstep(direction) + move_type = MECHAMOVE_STEP + + if(move_result && move_type) + aftermove(move_type) + can_move = world.time + step_in + return TRUE + return FALSE + +/obj/mecha/proc/aftermove(move_type) + use_power(step_energy_drain) + if(move_type & (MECHAMOVE_RAND | MECHAMOVE_STEP) && occupant) + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc + if(possible_port) + var/obj/screen/alert/mech_port_available/A = occupant.throw_alert("mechaport", /obj/screen/alert/mech_port_available, override = TRUE) + if(A) + A.target = possible_port + else + occupant.clear_alert("mechaport") + if(leg_overload_mode) + if(obj_integrity < max_integrity - max_integrity / 3) + leg_overload_mode = FALSE + step_in = initial(step_in) + step_energy_drain = initial(step_energy_drain) + occupant_message("Leg actuators damage threshold exceded. Disabling overload.") + +/obj/mecha/proc/mechturn(direction) + dir = direction + if(turnsound) + playsound(src,turnsound,40,1) + return 1 + +/obj/mecha/proc/mechstep(direction) + . = step(src, direction) + if(!.) + if(phasing && get_charge() >= phasing_energy_drain) + if(can_move < world.time) + . = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself + flick("phazon-phase", src) + forceMove(get_step(src, direction)) + use_power(phasing_energy_drain) + playsound(src, stepsound, 40, 1) + can_move = world.time + (step_in * 3) + else if(stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/proc/mechsteprand() + . = step_rand(src) + if(. && stepsound) + playsound(src, stepsound, 40, 1) + +/obj/mecha/Bump(var/atom/obstacle, bump_allowed) + if(throwing) //high velocity mechas in your face! + var/breakthrough = 0 + if(istype(obstacle, /obj/structure/window)) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/grille/)) + var/obj/structure/grille/G = obstacle + G.obj_break() + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/table)) + var/obj/structure/table/T = obstacle + qdel(T) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/rack)) + new /obj/item/rack_parts(obstacle.loc) + qdel(obstacle) + breakthrough = 1 + + else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank)) + obstacle.ex_act(1) + + else if(isliving(obstacle)) + var/mob/living/L = obstacle + var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg') + if(L.flags & GODMODE) + return + L.take_overall_damage(5,0) + if(L.buckled) + L.buckled = 0 + L.Stun(5) + L.Weaken(5) + L.apply_effect(STUTTER, 5) + playsound(src, pick(hit_sound), 50, 0, 0) + breakthrough = 1 + + else + if(throwing) + throwing.finalize(FALSE) + crashing = null + + ..() + + if(breakthrough) + if(crashing) + spawn(1) + throw_at(crashing, 50, throw_speed) + else + spawn(1) + crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile. + throw_at(crashing, 50, throw_speed) + + else + if(bump_allowed) + if(..()) + return + if(isobj(obstacle)) + var/obj/O = obstacle + if(istype(O, /obj/effect/portal)) //derpfix + anchored = 0 + O.Bumped(src) + spawn(0) //countering portal teleport spawn(0), hurr + anchored = 1 + else if(!O.anchored) + step(obstacle, dir) + else if(ismob(obstacle)) + step(obstacle, dir) + + +/////////////////////////////////// +//////// Internal damage //////// +/////////////////////////////////// + +/obj/mecha/proc/check_for_internal_damage(list/possible_int_damage, ignore_threshold=null) + if(!islist(possible_int_damage) || isemptylist(possible_int_damage)) + return + if(prob(20)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + for(var/T in possible_int_damage) + if(internal_damage & T) + possible_int_damage -= T + var/int_dam_flag = safepick(possible_int_damage) + if(int_dam_flag) + setInternalDamage(int_dam_flag) + if(prob(5)) + if(ignore_threshold || obj_integrity*100/max_integrity < internal_damage_threshold) + var/obj/item/mecha_parts/mecha_equipment/ME = safepick(equipment) + if(ME) + qdel(ME) + +/obj/mecha/proc/hasInternalDamage(int_dam_flag=null) + return int_dam_flag ? internal_damage&int_dam_flag : internal_damage + + +/obj/mecha/proc/setInternalDamage(int_dam_flag) + internal_damage |= int_dam_flag + log_append_to_last("Internal damage of type [int_dam_flag].",1) + occupant << sound('sound/machines/warning-buzzer.ogg',wait=0) + diag_hud_set_mechstat() + +/obj/mecha/proc/clearInternalDamage(int_dam_flag) + internal_damage &= ~int_dam_flag + switch(int_dam_flag) + if(MECHA_INT_TEMP_CONTROL) + occupant_message("Life support system reactivated.") + if(MECHA_INT_FIRE) + occupant_message("Internal fire extinquished.") + if(MECHA_INT_TANK_BREACH) + occupant_message("Damaged internal tank has been sealed.") + diag_hud_set_mechstat() + + +//////////////////////////////////////// +//////// Health related procs //////// +//////////////////////////////////////// + +/obj/mecha/proc/get_armour_facing(relative_dir) + switch(relative_dir) + if(0) // BACKSTAB! + return facing_modifiers[MECHA_BACK_ARMOUR] + if(45, 90, 270, 315) + return facing_modifiers[MECHA_SIDE_ARMOUR] + if(225, 180, 135) + return facing_modifiers[MECHA_FRONT_ARMOUR] + return 1 //always return non-0 + +/obj/mecha/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + spark_system.start() + switch(damage_flag) + if("fire") + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL)) + if("melee") + check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST)) + else + check_for_internal_damage(list(MECHA_INT_FIRE,MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST,MECHA_INT_SHORT_CIRCUIT)) + if(. >= 5 || prob(33)) + occupant_message("Taking damage!") + log_message("Took [damage_amount] points of damage. Damage type: [damage_type]") + +/obj/mecha/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + . = ..() + if(!damage_amount) + return 0 + var/booster_deflection_modifier = 1 + var/booster_damage_modifier = 1 + if(damage_flag == "bullet" || damage_flag == "laser" || damage_flag == "energy") + for(var/obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster/B in equipment) + if(B.projectile_react()) + booster_deflection_modifier = B.deflect_coeff + booster_damage_modifier = B.damage_coeff + break + else if(damage_flag == "melee") + for(var/obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster/B in equipment) + if(B.attack_react()) + booster_deflection_modifier *= B.deflect_coeff + booster_damage_modifier *= B.damage_coeff + break + + if(attack_dir) + var/facing_modifier = get_armour_facing(dir2angle(attack_dir) - dir2angle(src)) + booster_damage_modifier /= facing_modifier + booster_deflection_modifier *= facing_modifier + if(prob(deflect_chance * booster_deflection_modifier)) + visible_message("[src]'s armour deflects the attack!") + log_message("Armor saved.") + return 0 + if(.) + . *= booster_damage_modifier + +/obj/mecha/attack_hand(mob/living/user) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_PUNCH) + playsound(loc, 'sound/weapons/tap.ogg', 40, 1, -1) + user.visible_message("[user] hits [name]. Nothing happens", "You hit [name] with no visible effect.") + log_message("Attack by hand/paw. Attacker - [user].") + + +/obj/mecha/attack_alien(mob/living/user) + log_message("Attack by alien. Attacker - [user].", color = "red") + playsound(src.loc, 'sound/weapons/slash.ogg', 100, TRUE) + attack_generic(user, 15, BRUTE, "melee", 0) + +/obj/mecha/attack_animal(mob/living/simple_animal/user) + log_message("Attack by simple animal. Attacker - [user].") + if(!user.melee_damage_upper && !user.obj_damage) + user.custom_emote(1, "[user.friendly] [src].") + return FALSE + else + var/play_soundeffect = 1 + if(user.environment_smash) + play_soundeffect = 0 + playsound(src, 'sound/effects/bang.ogg', 50, TRUE) + var/animal_damage = rand(user.melee_damage_lower,user.melee_damage_upper) + if(user.obj_damage) + animal_damage = user.obj_damage + animal_damage = min(animal_damage, 20*user.environment_smash) + user.create_attack_log("attacked [name]") + add_attack_logs(user, src, "Attacked") + attack_generic(user, animal_damage, user.melee_damage_type, "melee", play_soundeffect) + return TRUE + +/obj/mecha/hulk_damage() + return 15 + +/obj/mecha/attack_hulk(mob/living/carbon/human/user) + . = ..() + if(.) + log_message("Attack by hulk. Attacker - [user].", 1) + add_attack_logs(user, src, "Punched with hulk powers") + +/obj/mecha/blob_act(obj/structure/blob/B) + log_message("Attack by blob. Attacker - [B].") + take_damage(30, BRUTE, "melee", 0, get_dir(src, B)) + +/obj/mecha/attack_tk() + return + +/obj/mecha/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) //wrapper + log_message("Hit by [AM].") + . = ..() + +/obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper + log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).") + ..() + +/obj/mecha/ex_act(severity, target) + log_message("Affected by explosion of severity: [severity].") + if(prob(deflect_chance)) + severity++ + log_message("Armor saved, changing severity to [severity]") + ..() + severity++ + for(var/X in equipment) + var/obj/item/mecha_parts/mecha_equipment/ME = X + ME.ex_act(severity) + for(var/Y in trackers) + var/obj/item/mecha_parts/mecha_tracking/MT = Y + MT.ex_act(severity) + if(occupant) + occupant.ex_act(severity) + +/obj/mecha/handle_atom_del(atom/A) + if(A == occupant) + occupant = null + icon_state = initial(icon_state)+"-open" + setDir(dir_in) + +/obj/mecha/Destroy() + if(occupant) + occupant.SetSleeping(destruction_sleep_duration) + go_out() + var/mob/living/silicon/ai/AI + for(var/mob/M in src) //Let's just be ultra sure + if(isAI(M)) + occupant = null + AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck. + else + M.forceMove(loc) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + E.detach(loc) + qdel(E) + equipment.Cut() + QDEL_NULL(cell) + QDEL_NULL(internal_tank) + if(AI) + AI.gib() //No wreck, no AI to recover + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + if(loc) + loc.assume_air(cabin_air) + air_update_turf() + else + qdel(cabin_air) + cabin_air = null + QDEL_NULL(spark_system) + QDEL_NULL(smoke_system) + + GLOB.mechas_list -= src //global mech list + return ..() + +//TODO +/obj/mecha/emp_act(severity) + if(get_charge()) + use_power((cell.charge/3)/(severity*2)) + take_damage(30 / severity, BURN, "energy", 1) + log_message("EMP detected", 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL, MECHA_INT_CONTROL_LOST, MECHA_INT_SHORT_CIRCUIT), 1) + +/obj/mecha/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > max_temperature) + log_message("Exposed to dangerous temperature.", 1) + take_damage(5, BURN, 0, 1) + check_for_internal_damage(list(MECHA_INT_FIRE, MECHA_INT_TEMP_CONTROL)) + +////////////////////// +////// AttackBy ////// +////////////////////// + +/obj/mecha/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/mmi)) + if(mmi_move_inside(W,user)) + to_chat(user, "[src]-MMI interface initialized successfuly") + else + to_chat(user, "[src]-MMI interface initialization failed.") + return + + if(istype(W, /obj/item/mecha_parts/mecha_equipment)) + var/obj/item/mecha_parts/mecha_equipment/E = W + spawn() + if(E.can_attach(src)) + if(!user.drop_item()) + return + E.attach(src) + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + else + to_chat(user, "You were unable to attach [W] to [src]!") + return + + if(W.GetID()) + if(add_req_access || maint_access) + if(internals_access_allowed(usr)) + var/obj/item/card/id/id_card + if(istype(W, /obj/item/card/id)) + id_card = W + else + var/obj/item/pda/pda = W + id_card = pda.id + output_maintenance_dialog(id_card, user) + return + else + to_chat(user, "Invalid ID: Access denied.") + else + to_chat(user, "Maintenance protocols disabled by operator.") + + else if(istype(W, /obj/item/stack/cable_coil)) + if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) + var/obj/item/stack/cable_coil/CC = W + if(CC.amount > 1) + CC.use(2) + clearInternalDamage(MECHA_INT_SHORT_CIRCUIT) + to_chat(user, "You replace the fused wires.") + else + to_chat(user, "There's not enough wire to finish the task.") + return + + else if(istype(W, /obj/item/stock_parts/cell)) + if(state==4) + if(!cell) + if(!user.drop_item()) + return + to_chat(user, "You install the powercell.") + W.forceMove(src) + cell = W + log_message("Powercell installed") + else + to_chat(user, "There's already a powercell installed.") + return + + else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) + if(!user.unEquip(W)) + to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") + return + W.forceMove(src) + trackers += W + user.visible_message("[user] attaches [W] to [src].", "You attach [W] to [src].") + diag_hud_set_mechtracking() + return + + else if(istype(W, /obj/item/paintkit)) + if(occupant) + to_chat(user, "You can't customize a mech while someone is piloting it - that would be unsafe!") + return + + var/obj/item/paintkit/P = W + var/found = null + + for(var/type in P.allowed_types) + if(type == initial_icon) + found = 1 + break + + if(!found) + to_chat(user, "That kit isn't meant for use on this class of exosuit.") + return + + user.visible_message("[user] opens [P] and spends some quality time customising [src].") + + name = P.new_name + desc = P.new_desc + initial_icon = P.new_icon + reset_icon() + + user.drop_item() + qdel(P) + + else if(istype(W, /obj/item/mecha_modkit)) + if(occupant) + to_chat(user, "You can't access the mech's modification port while it is occupied.") + return + var/obj/item/mecha_modkit/M = W + if(do_after_once(user, M.install_time, target = src)) + M.install(src, user) + else + to_chat(user, "You stop installing [M].") + + else + return ..() + + +/obj/mecha/crowbar_act(mob/user, obj/item/I) + if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state == 2) + state = 3 + to_chat(user, "You open the hatch to the power unit") + else if(state == 3) + state = 2 + to_chat(user, "You close the hatch to the power unit") + else + // Since having maint protocols available is controllable by the MMI, I see this as a consensual way to remove an MMI without destroying the mech + user.visible_message("[user] begins levering out the MMI from the [src].", "You begin to lever out the MMI from the [src].") + to_chat(occupant, "[user] is prying you out of the exosuit!") + if(I.use_tool(src, user, 80, volume = I.tool_volume) && pilot_is_mmi()) + user.visible_message("[user] pries the MMI out of the [src]!", "You finish removing the MMI from the [src]!") + go_out() + +/obj/mecha/screwdriver_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(!(state==3 && cell) && !(state==4 && cell)) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + clearInternalDamage(MECHA_INT_TEMP_CONTROL) + to_chat(user, "You repair the damaged temperature controller.") + else if(state==3 && cell) + cell.forceMove(loc) + cell = null + state = 4 + to_chat(user, "You unscrew and pry out the powercell.") + log_message("Powercell removed") + else if(state==4 && cell) + state=3 + to_chat(user, "You screw the cell in place.") + +/obj/mecha/wrench_act(mob/user, obj/item/I) + if(state != 1 && state != 2) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state==1) + state = 2 + to_chat(user, "You undo the securing bolts.") + else + state = 1 + to_chat(user, "You tighten the securing bolts.") + +/obj/mecha/welder_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if((obj_integrity >= max_integrity) && !internal_damage) + to_chat(user, "[src] is at full integrity!") + return + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, 15, volume = I.tool_volume)) + if(internal_damage & MECHA_INT_TANK_BREACH) + clearInternalDamage(MECHA_INT_TANK_BREACH) + user.visible_message("[user] repairs the damaged gas tank.", "You repair the damaged gas tank.") + else if(obj_integrity < max_integrity) + user.visible_message("[user] repairs some damage to [name].", "You repair some damage to [name].") + obj_integrity += min(10, max_integrity - obj_integrity) + else + to_chat(user, "[src] is at full integrity!") + +/obj/mecha/mech_melee_attack(obj/mecha/M) + if(!has_charge(melee_energy_drain)) + return 0 + use_power(melee_energy_drain) + if(M.damtype == BRUTE || M.damtype == BURN) + add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + . = ..() + +/obj/mecha/emag_act(mob/user) + to_chat(user, "[src]'s ID slot rejects the card.") + return + + +///////////////////////////////////// +//////////// AI piloting //////////// +///////////////////////////////////// + +/obj/mecha/attack_ai(mob/living/silicon/ai/user) + if(!isAI(user)) + return + //Allows the Malf to scan a mech's status and loadout, helping it to decide if it is a worthy chariot. + if(user.can_dominate_mechs) + examine(user) //Get diagnostic information! + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers) + to_chat(user, "Warning: Tracking Beacon detected. Enter at your own risk. Beacon Data:") + to_chat(user, "[B.get_mecha_info_text()]") + break + //Nothing like a big, red link to make the player feel powerful! + to_chat(user, "ASSUME DIRECT CONTROL?
    ") + else + examine(user) + if(occupant) + user << "This exosuit has a pilot and cannot be controlled." + return + var/can_control_mech = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in trackers) + can_control_mech = TRUE + to_chat(user, "[bicon(src)] Status of [name]:\n\ + [A.get_mecha_info_text()]") + break + if(!can_control_mech) + to_chat(user, "You cannot control exosuits without AI control beacons installed.") + return + to_chat(user, "Take control of exosuit?
    ") + +/obj/mecha/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) + if(!..()) + return + + //Transfer from core or card to mech. Proc is called by mech. + switch(interaction) + if(AI_TRANS_TO_CARD) //Upload AI from mech to AI card. + if(!maint_access) //Mech must be in maint mode to allow carding. + to_chat(user, "[name] must have maintenance protocols active in order to allow a transfer.") + return + AI = occupant + if(!AI || !isAI(occupant)) //Mech does not have an AI for a pilot + to_chat(user, "No AI detected in the [name] onboard computer.") + return + if(AI.mind.special_role) //Malf AIs cannot leave mechs. Except through death. + to_chat(user, "ACCESS DENIED.") + return + AI.aiRestorePowerRoutine = 0//So the AI initially has power. + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + AI.loc = card + occupant = null + AI.controlled_mech = null + AI.remote_control = null + icon_state = initial(icon_state)+"-open" + to_chat(AI, "You have been downloaded to a mobile storage device. Wireless connection offline.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) removed from [name] and stored within local memory.") + + if(AI_MECH_HACK) //Called by AIs on the mech + AI.linked_core = new /obj/structure/AIcore/deactivated(AI.loc) + if(AI.can_dominate_mechs) + if(occupant) //Oh, I am sorry, were you using that? + to_chat(AI, "Pilot detected! Forced ejection initiated!") + to_chat(occupant, "You have been forcibly ejected!") + go_out(1) //IT IS MINE, NOW. SUCK IT, RD! + ai_enter_mech(AI, interaction) + + if(AI_TRANS_FROM_CARD) //Using an AI card to upload to a mech. + AI = locate(/mob/living/silicon/ai) in card + if(!AI) + to_chat(user, "There is no AI currently installed on this device.") + return + else if(AI.stat || !AI.client) + to_chat(user, "[AI.name] is currently unresponsive, and cannot be uploaded.") + return + else if(occupant || dna) //Normal AIs cannot steal mechs! + to_chat(user, "Access denied. [name] is [occupant ? "currently occupied" : "secured with a DNA lock"].") + return + AI.control_disabled = 0 + AI.aiRadio.disabledAi = 0 + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + ai_enter_mech(AI, interaction) + +//Hack and From Card interactions share some code, so leave that here for both to use. +/obj/mecha/proc/ai_enter_mech(mob/living/silicon/ai/AI, interaction) + AI.aiRestorePowerRoutine = 0 + AI.loc = src + occupant = AI + icon_state = initial(icon_state) + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + AI.cancel_camera() + AI.controlled_mech = src + AI.remote_control = src + AI.canmove = 1 //Much easier than adding AI checks! Be sure to set this back to 0 if you decide to allow an AI to leave a mech somehow. + AI.can_shunt = 0 //ONE AI ENTERS. NO AI LEAVES. + to_chat(AI, "[AI.can_dominate_mechs ? "Takeover of [name] complete! You are now permanently loaded onto the onboard computer. Do not attempt to leave the station sector!" \ + : "You have been uploaded to a mech's onboard computer."]") + to_chat(AI, "Use Middle-Mouse to activate mech functions and equipment. Click normally for AI interactions.") + if(interaction == AI_TRANS_FROM_CARD) + GrantActions(AI, FALSE) + else + GrantActions(AI, !AI.can_dominate_mechs) + +///////////////////////////////////// +//////// Atmospheric stuff //////// +///////////////////////////////////// + +/obj/mecha/proc/get_turf_air() + var/turf/T = get_turf(src) + if(T) + . = T.return_air() + +/obj/mecha/remove_air(amount) + if(use_internal_tank) + return cabin_air.remove(amount) + else + var/turf/T = get_turf(src) + if(T) + return T.remove_air(amount) + +/obj/mecha/return_air() + if(use_internal_tank) + return cabin_air + return get_turf_air() + +/obj/mecha/proc/return_pressure() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_pressure() + +//skytodo: //No idea what you want me to do here, mate. +/obj/mecha/proc/return_temperature() + var/datum/gas_mixture/t_air = return_air() + if(t_air) + . = t_air.return_temperature() + +/obj/mecha/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !istype(new_port) || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + connected_port.parent.reconcile_air() + + if(occupant) + occupant.clear_alert("mechaport") + occupant.throw_alert("mechaport_d", /obj/screen/alert/mech_port_disconnect) + + log_message("Connected to gas port.") + return 1 + +/obj/mecha/proc/disconnect() + if(!connected_port) + return 0 + + connected_port.connected_device = null + connected_port = null + log_message("Disconnected from gas port.") + if(occupant) + occupant.clear_alert("mechaport_d") + return 1 + +/obj/mecha/portableConnectorReturnAir() + return internal_tank.return_air() + +/obj/mecha/proc/toggle_lights() + lights = !lights + if(lights) + set_light(light_range + lights_power) + else + set_light(light_range - lights_power) + occupant_message("Toggled lights [lights ? "on" : "off"].") + log_message("Toggled lights [lights ? "on" : "off"].") + +/obj/mecha/proc/toggle_internal_tank() + use_internal_tank = !use_internal_tank + occupant_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + log_message("Now taking air from [use_internal_tank ? "internal airtank" : "environment"].") + +/obj/mecha/MouseDrop_T(mob/M, mob/user) + if(user.incapacitated()) + return + if(user != M) + return + log_message("[user] tries to move in.") + if(occupant) + to_chat(user, "The [src] is already occupied!") + log_append_to_last("Permission denied.") + return + var/passed + if(dna) + if(ishuman(user)) + if(user.dna.unique_enzymes == dna) + passed = 1 + else if(operation_allowed(user)) + passed = 1 + if(!passed) + to_chat(user, "Access denied.") + log_append_to_last("Permission denied.") + return + if(user.buckled) + to_chat(user, "You are currently buckled and cannot move.") + log_append_to_last("Permission denied.") + return + if(user.has_buckled_mobs()) //mob attached to us + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + return + + visible_message("[user] starts to climb into [src]") + + if(do_after(user, 40, target = src)) + if(obj_integrity <= 0) + to_chat(user, "You cannot get in the [name], it has been destroyed!") + else if(occupant) + to_chat(user, "[occupant] was faster! Try better next time, loser.") + else if(user.buckled) + to_chat(user, "You can't enter the exosuit while buckled.") + else if(user.has_buckled_mobs()) + to_chat(user, "You can't enter the exosuit with other creatures attached to you!") + else + moved_inside(user) + else + to_chat(user, "You stop entering the exosuit!") + +/obj/mecha/proc/moved_inside(var/mob/living/carbon/human/H as mob) + if(H && H.client && H in range(1)) + occupant = H + H.stop_pulling() + H.forceMove(src) + H.reset_perspective(src) + add_fingerprint(H) + GrantActions(H, human_occupant = 1) + forceMove(loc) + log_append_to_last("[H] moved in as pilot.") + icon_state = reset_icon() + dir = dir_in + playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) + if(!activated) + occupant << sound(longactivationsound, volume = 50) + activated = TRUE + else if(!hasInternalDamage()) + occupant << sound(nominalsound, volume = 50) + if(state) + H.throw_alert("locked", /obj/screen/alert/mech_maintenance) + return 1 + else + return 0 + +/obj/mecha/proc/mmi_move_inside(var/obj/item/mmi/mmi_as_oc as obj,mob/user as mob) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected!") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level!") + return 0 + else if(occupant) + to_chat(user, "Occupant detected!") + return 0 + else if(dna && dna != mmi_as_oc.brainmob.dna.unique_enzymes) + to_chat(user, "Access denied. [name] is secured with a DNA lock.") + return 0 + + if(do_after(user, 40, target = src)) + if(!occupant) + return mmi_moved_inside(mmi_as_oc,user) + else + to_chat(user, "Occupant detected!") + else + to_chat(user, "You stop inserting the MMI.") + return 0 + +/obj/mecha/proc/mmi_moved_inside(obj/item/mmi/mmi_as_oc,mob/user) + if(mmi_as_oc && user in range(1)) + if(!mmi_as_oc.brainmob || !mmi_as_oc.brainmob.client) + to_chat(user, "Consciousness matrix not detected.") + return 0 + else if(mmi_as_oc.brainmob.stat) + to_chat(user, "Beta-rhythm below acceptable level.") + return 0 + if(!user.unEquip(mmi_as_oc)) + to_chat(user, "\the [mmi_as_oc] is stuck to your hand, you cannot put it in \the [src]") + return 0 + var/mob/brainmob = mmi_as_oc.brainmob + brainmob.reset_perspective(src) + occupant = brainmob + brainmob.forceMove(src) //should allow relaymove + brainmob.canmove = 1 + if(istype(mmi_as_oc, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi_as_oc + if(R.imprinted_master) + to_chat(brainmob, "Your imprint to [R.imprinted_master] has been temporarily disabled. You should help the crew and not commit harm.") + mmi_as_oc.loc = src + mmi_as_oc.mecha = src + Entered(mmi_as_oc) + Move(loc) + icon_state = reset_icon() + dir = dir_in + log_message("[mmi_as_oc] moved in as pilot.") + if(!hasInternalDamage()) + to_chat(occupant, sound(nominalsound, volume=50)) + GrantActions(brainmob) + return 1 + else + return 0 + +/obj/mecha/proc/pilot_is_mmi() + var/atom/movable/mob_container + if(istype(occupant, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/brain = occupant + mob_container = brain.container + if(istype(mob_container, /obj/item/mmi)) + return 1 + return 0 + +/obj/mecha/proc/pilot_mmi_hud(var/mob/living/carbon/brain/pilot) + return + +/obj/mecha/Exited(atom/movable/M, atom/newloc) + if(occupant && occupant == M) // The occupant exited the mech without calling go_out() + go_out(1, newloc) + +/obj/mecha/proc/go_out(forced, atom/newloc = loc) + if(!occupant) + return + var/atom/movable/mob_container + occupant.clear_alert("charge") + occupant.clear_alert("locked") + occupant.clear_alert("mech damage") + occupant.clear_alert("mechaport") + occupant.clear_alert("mechaport_d") + if(ishuman(occupant)) + mob_container = occupant + RemoveActions(occupant, human_occupant = 1) + else if(isbrain(occupant)) + var/mob/living/carbon/brain/brain = occupant + RemoveActions(brain) + mob_container = brain.container + else if(isAI(occupant)) + var/mob/living/silicon/ai/AI = occupant + if(forced)//This should only happen if there are multiple AIs in a round, and at least one is Malf. + RemoveActions(occupant) + occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. + occupant = null + return + else + if(!AI.linked_core || QDELETED(AI.linked_core)) + to_chat(AI, "Inactive core destroyed. Unable to return.") + AI.linked_core = null + return + to_chat(AI, "Returning to core...") + AI.controlled_mech = null + AI.remote_control = null + RemoveActions(occupant, 1) + mob_container = AI + newloc = get_turf(AI.linked_core) + qdel(AI.linked_core) + else + return + var/mob/living/L = occupant + occupant = null //we need it null when forceMove calls Exited(). + if(mob_container.forceMove(newloc))//ejecting mob container + log_message("[mob_container] moved out.") + L << browse(null, "window=exosuit") + + if(istype(mob_container, /obj/item/mmi)) + var/obj/item/mmi/mmi = mob_container + if(mmi.brainmob) + L.loc = mmi + L.reset_perspective() + mmi.mecha = null + mmi.update_icon() + L.canmove = 0 + if(istype(mmi, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = mmi + if(R.imprinted_master) + to_chat(L, "Imprint re-enabled, you are once again bound to [R.imprinted_master]'s commands.") + icon_state = initial(icon_state)+"-open" + dir = dir_in + + if(L && L.client) + L.client.RemoveViewMod("mecha") + zoom_mode = FALSE + +///////////////////////// +////// Access stuff ///// +///////////////////////// + +/obj/mecha/proc/operation_allowed(mob/living/carbon/human/H) + if(!ishuman(H)) + return 0 + for(var/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, operation_req_access)) + return 1 + return 0 + + +/obj/mecha/proc/internals_access_allowed(mob/living/carbon/human/H) + for(var/atom/ID in list(H.get_active_hand(), H.wear_id, H.belt)) + if(check_access(ID, internals_req_access)) + return 1 + return 0 + + +/obj/mecha/check_access(obj/item/card/id/I, list/access_list) + if(!istype(access_list)) + return 1 + if(!access_list.len) //no requirements + return 1 + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(!istype(I) || !I.access) //not ID or no access + return 0 + if(access_list==operation_req_access) + for(var/req in access_list) + if(!(req in I.access)) //doesn't have this access + return 0 + else if(access_list==internals_req_access) + for(var/req in access_list) + if(req in I.access) + return 1 + return 1 + +/////////////////////// +///// Power stuff ///// +/////////////////////// + +/obj/mecha/proc/has_charge(amount) + return (get_charge()>=amount) + +/obj/mecha/proc/get_charge() + for(var/obj/item/mecha_parts/mecha_equipment/tesla_energy_relay/R in equipment) + var/relay_charge = R.get_charge() + if(relay_charge) + return relay_charge + if(cell) + return max(0, cell.charge) + +/obj/mecha/proc/use_power(amount) + if(get_charge()) + cell.use(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/give_power(amount) + if(!isnull(get_charge())) + cell.give(amount) + if(occupant) + update_cell() + return 1 + return 0 + +/obj/mecha/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + occupant.clear_alert("charge") + if(0.5 to 0.75) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 1) + if(0.25 to 0.5) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 2) + if(power_warned) + power_warned = FALSE + if(0.01 to 0.25) + occupant.throw_alert("charge", /obj/screen/alert/mech_lowcell, 3) + if(!power_warned) + occupant << sound(lowpowersound, volume = 50) + power_warned = TRUE + else + occupant.throw_alert("charge", /obj/screen/alert/mech_emptycell) + else + occupant.throw_alert("charge", /obj/screen/alert/mech_nocell) + +/obj/mecha/proc/reset_icon() + if(initial_icon) + icon_state = initial_icon + else + icon_state = initial(icon_state) + return icon_state + +////////////////////////////////////////// +//////// Mecha global iterators //////// +////////////////////////////////////////// + +/obj/mecha/process() + process_internal_damage() + regulate_temp() + give_air() + update_huds() + +/obj/mecha/proc/process_internal_damage() + if(!internal_damage) + return + + if(internal_damage & MECHA_INT_FIRE) + if(!(internal_damage & MECHA_INT_TEMP_CONTROL) && prob(5)) + clearInternalDamage(MECHA_INT_FIRE) + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + if(int_tank_air.return_pressure() > internal_tank.maximum_pressure && !(internal_damage & MECHA_INT_TANK_BREACH)) + setInternalDamage(MECHA_INT_TANK_BREACH) + + if(int_tank_air && int_tank_air.return_volume() > 0) + int_tank_air.temperature = min(6000 + T0C, cabin_air.return_temperature() + rand(10, 15)) + + if(cabin_air && cabin_air.return_volume()>0) + cabin_air.temperature = min(6000+T0C, cabin_air.return_temperature()+rand(10,15)) + if(cabin_air.return_temperature() > max_temperature/2) + take_damage(4/round(max_temperature/cabin_air.return_temperature(),0.1), BURN, 0, 0) + + if(internal_damage & MECHA_INT_TANK_BREACH) //remove some air from internal tank + if(internal_tank) + var/datum/gas_mixture/int_tank_air = internal_tank.return_air() + var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10) + if(loc) + loc.assume_air(leaked_gas) + air_update_turf() + else + qdel(leaked_gas) + + if(internal_damage & MECHA_INT_SHORT_CIRCUIT) + if(get_charge()) + spark_system.start() + cell.charge -= min(20,cell.charge) + cell.maxcharge -= min(20,cell.maxcharge) + +/obj/mecha/proc/regulate_temp() + if(internal_damage & MECHA_INT_TEMP_CONTROL) + return + + if(cabin_air && cabin_air.return_volume() > 0) + var/delta = cabin_air.temperature - T20C + cabin_air.temperature -= max(-10, min(10, round(delta / 4, 0.1))) + +/obj/mecha/proc/give_air() + if(!internal_tank) + return + + var/datum/gas_mixture/tank_air = internal_tank.return_air() + + var/release_pressure = internal_tank_valve + var/cabin_pressure = cabin_air.return_pressure() + var/pressure_delta = min(release_pressure - cabin_pressure, (tank_air.return_pressure() - cabin_pressure)/2) + var/transfer_moles = 0 + if(pressure_delta > 0) //cabin pressure lower than release pressure + if(tank_air.return_temperature() > 0) + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = tank_air.remove(transfer_moles) + cabin_air.merge(removed) + else if(pressure_delta < 0) //cabin pressure higher than release pressure + var/datum/gas_mixture/t_air = return_air() + pressure_delta = cabin_pressure - release_pressure + if(t_air) + pressure_delta = min(cabin_pressure - t_air.return_pressure(), pressure_delta) + if(pressure_delta > 0) //if location pressure is lower than cabin pressure + transfer_moles = pressure_delta*cabin_air.return_volume()/(cabin_air.return_temperature() * R_IDEAL_GAS_EQUATION) + var/datum/gas_mixture/removed = cabin_air.remove(transfer_moles) + if(t_air) + t_air.merge(removed) + else //just delete the cabin gas, we're in space or some shit + qdel(removed) + +/obj/mecha/proc/update_huds() + diag_hud_set_mechhealth() + diag_hud_set_mechcell() + diag_hud_set_mechstat() + diag_hud_set_mechtracking() + + +/obj/mecha/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + flick_overlay(image('icons/mob/talk.dmi', bubble_loc, bubble_state,MOB_LAYER+1), bubble_recipients, 30) + +/obj/mecha/update_remote_sight(mob/living/user) + if(occupant_sight_flags) + if(user == occupant) + user.sight |= occupant_sight_flags + + ..() + +/obj/mecha/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect) + if(selected) + used_item = selected + else if(!visual_effect_icon) + visual_effect_icon = ATTACK_EFFECT_SMASH + if(damtype == BURN) + visual_effect_icon = ATTACK_EFFECT_MECHFIRE + else if(damtype == TOX) + visual_effect_icon = ATTACK_EFFECT_MECHTOXIN + ..() + +/obj/mecha/obj_destruction() + if(wreckage) + var/mob/living/silicon/ai/AI + if(isAI(occupant)) + AI = occupant + occupant = null + var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI) + for(var/obj/item/mecha_parts/mecha_equipment/E in equipment) + if(E.salvageable && prob(30)) + WR.crowbar_salvage += E + E.detach(WR) //detaches from src into WR + E.equip_ready = 1 + else + E.detach(loc) + qdel(E) + if(cell) + WR.crowbar_salvage += cell + cell.forceMove(WR) + cell.charge = rand(0, cell.charge) + cell = null + if(internal_tank) + WR.crowbar_salvage += internal_tank + internal_tank.forceMove(WR) + cell = null + . = ..() + +/obj/mecha/CtrlClick(mob/living/L) + if(occupant != L || !istype(L)) + return ..() + + var/list/choices = list("Cancel / No Change" = mutable_appearance(icon = 'icons/mob/screen_gen.dmi', icon_state = "x")) + var/list/choices_to_refs = list() + + for(var/obj/item/mecha_parts/mecha_equipment/MT in equipment) + if(!MT.selectable || selected == MT) + continue + var/mutable_appearance/clean/MA = new(MT) + choices[MT.name] = MA + choices_to_refs[MT.name] = MT + + var/choice = show_radial_menu(L, src, choices, radius = 48, custom_check = CALLBACK(src, .proc/check_menu, L)) + if(!check_menu(L) || choice == "Cancel / No Change") + return + + var/obj/item/mecha_parts/mecha_equipment/new_sel = LAZYACCESS(choices_to_refs, choice) + if(istype(new_sel)) + selected = new_sel + occupant_message("You switch to [selected].") + visible_message("[src] raises [selected]") + send_byjax(occupant, "exosuit.browser", "eq_list", get_equipment_list()) + +/obj/mecha/proc/check_menu(mob/living/L) + if(L != occupant || !istype(L)) + return FALSE + if(L.incapacitated()) + return FALSE + return TRUE diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 3c71db19b723..8ff3d4ebd5c1 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -225,4 +225,4 @@ chassis.damtype = new_damtype button_icon_state = "mech_damtype_[new_damtype]" playsound(src, 'sound/mecha/mechmove01.ogg', 50, 1) - UpdateButtonIcon() \ No newline at end of file + UpdateButtonIcon() diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 64eac560d398..c9dc5b11e970 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -1,1727 +1,1727 @@ -#define STANDARD_STACK_AMOUNT 5 - -//////////////////////////////// -///// Construction datums ////// -//////////////////////////////// - -/datum/construction/mecha/custom_action(step, atom/used_atom, mob/user) - if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.use(4)) - playsound(holder, C.usesound, 50, 1) - else - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(STANDARD_STACK_AMOUNT) - else - return ..() - -/datum/construction/reversible/mecha/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob) - if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.use(4)) - playsound(holder, C.usesound, 50, 1) - else - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(STANDARD_STACK_AMOUNT) - else if(isitem(used_atom)) - var/obj/item/I = used_atom - if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) - if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) - return 0 - return 1 - - -/datum/construction/mecha/ripley_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 - ) - -/datum/construction/mecha/ripley_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/ripley_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/ripley_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "ripley0" - const_holder.density = 1 - const_holder.overlays.len = 0 - qdel(src) - return - - -/datum/construction/reversible/mecha/ripley - result = "/obj/mecha/working/ripley" - taskpath = /datum/job_objective/make_ripley - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //8 - list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //10 - list("key"=/obj/item/circuitboard/mecha/ripley/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //11 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/ripley/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/ripley/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "ripley2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "ripley0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "ripley3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "ripley4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "ripley2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "ripley5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "ripley3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "ripley6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "ripley4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "ripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "ripley5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "ripley8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "ripley6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "ripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "ripley7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "ripley10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "ripley8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "ripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "ripley9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - holder.icon_state = "ripley12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "ripley10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "ripley13" - else - user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "ripley11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "ripley12" - return 1 - -/datum/construction/reversible/mecha/ripley/spawn_result() - ..() - feedback_inc("mecha_ripley_created",1) - return - - - -/datum/construction/mecha/gygax_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/gygax_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/gygax_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/gygax_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/gygax - result = "/obj/mecha/combat/gygax" - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/gygax_armour, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Advanced capacitor is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Advanced capacitor is installed."), - //8 - list("key"=/obj/item/stock_parts/capacitor/adv, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Advanced scanner module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Advanced scanner module is installed."), - //10 - list("key"=/obj/item/stock_parts/scanning_module/adv, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Scanning module is secured."), - //11 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Scanning module is installed."), - //12 - list("key"=/obj/item/circuitboard/mecha/gygax/targeting, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //14 - list("key"=/obj/item/circuitboard/mecha/gygax/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //15 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/gygax/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //17 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/gygax/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/gygax/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/gygax/targeting(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to the [holder].", "You install advanced scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from the [holder].", "You remove the advanced scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to the [holder].", "You install advanced capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from the [holder].", "You remove the advanced capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Gygax Armor Plates to the [holder].", "You install Gygax Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Gygax Armor Plates.", "You secure Gygax Armor Plates.") - holder.icon_state = "gygax19" - else - user.visible_message("[user] pries Gygax Armor Plates from the [holder].", "You pry Gygax Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Gygax Armor Plates to the [holder].", "You weld Gygax Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/gygax/spawn_result() - ..() - feedback_inc("mecha_gygax_created",1) - return - -/datum/construction/mecha/firefighter_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 - list("key"=/obj/item/clothing/suit/fire)//6 - ) - -/datum/construction/mecha/firefighter_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/firefighter_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/firefighter_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "fireripley0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/firefighter - result = "/obj/mecha/working/ripley/firefighter" - taskpath = /datum/job_objective/make_ripley - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is being installed."), - //4 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //5 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //6 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - - //7 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //8 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //9 - list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //10 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //11 - list("key"=/obj/item/circuitboard/mecha/ripley/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //12 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //13 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //14 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //15 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/firefighter/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(15) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(14) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "fireripley2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "fireripley0" - if(13) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "fireripley3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "fireripley4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "fireripley2" - if(11) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "fireripley5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "fireripley3" - if(10) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "fireripley6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "fireripley4" - if(9) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "fireripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "fireripley5" - if(8) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "fireripley8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "fireripley6" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "fireripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "fireripley7" - - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "fireripley10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley8" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "fireripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "fireripley9" - if(4) - if(diff==FORWARD) - user.visible_message("[user] starts to install the external armor layer to the [holder].", "You start to install the external armor layer to the [holder].") - holder.icon_state = "fireripley12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "fireripley10" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - holder.icon_state = "fireripley13" - else - user.visible_message("[user] removes the external armor from the [holder].", "You remove the external armor from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley11" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "fireripley14" - else - user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley12" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "fireripley13" - return 1 - -/datum/construction/reversible/mecha/firefighter/spawn_result() - ..() - feedback_inc("mecha_firefighter_created",1) - return - - - -/datum/construction/mecha/honker_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/honker_torso),//1 - list("key"=/obj/item/mecha_parts/part/honker_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/honker_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/honker_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/honker_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/honker_head) - ) - -/datum/construction/mecha/honker_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/honker_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/honker_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/mecha/honker(const_holder) - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/mecha/honker - result = "/obj/mecha/combat/honker" - steps = list(list("key"=/obj/item/bikehorn),//1 - list("key"=/obj/item/clothing/shoes/clown_shoes),//2 - list("key"=/obj/item/bikehorn),//3 - list("key"=/obj/item/clothing/mask/gas/clown_hat),//4 - list("key"=/obj/item/bikehorn),//5 - list("key"=/obj/item/circuitboard/mecha/honker/targeting),//6 - list("key"=/obj/item/bikehorn),//7 - list("key"=/obj/item/circuitboard/mecha/honker/peripherals),//8 - list("key"=/obj/item/bikehorn),//9 - list("key"=/obj/item/circuitboard/mecha/honker/main),//10 - list("key"=/obj/item/bikehorn),//11 - ) - -/datum/construction/mecha/honker/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/mecha/honker/custom_action(step, atom/used_atom, mob/user) - if(!..()) - return 0 - - if(istype(used_atom, /obj/item/bikehorn)) - playsound(holder, 'sound/items/bikehorn.ogg', 50, 1) - user.visible_message("HONK!") - - //TODO: better messages. - switch(step) - if(10) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") - qdel(used_atom) - if(8) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - if(6) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - if(4) - user.visible_message("[user] puts clown wig and mask on the [holder].", "You put clown wig and mask on the [holder].") - qdel(used_atom) - if(2) - user.visible_message("[user] puts clown boots on the [holder].", "You put clown boots on the [holder].") - qdel(used_atom) - return 1 - -/datum/construction/mecha/honker/spawn_result() - ..() - feedback_inc("mecha_honker_created",1) - return - -/datum/construction/mecha/reticence_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/reticence_torso),//1 - list("key"=/obj/item/mecha_parts/part/reticence_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/reticence_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/reticence_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/reticence_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/reticence_head) - ) - -/datum/construction/mecha/reticence_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/reticence_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state + "+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/reticence_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/mecha/reticence(const_holder) - const_holder.density = 1 - qdel(src) - return - -/datum/construction/mecha/reticence - result = "/obj/mecha/combat/reticence" - steps = list(list("key"=/obj/effect/dummy/mecha_emote_step),//1 - list("key"=/obj/item/clothing/suit/suspenders),//2 - list("key"=/obj/effect/dummy/mecha_emote_step),//3 - list("key"=/obj/item/clothing/mask/gas/mime),//4 - list("key"=/obj/effect/dummy/mecha_emote_step),//5 - list("key"=/obj/item/clothing/head/beret),//6 - list("key"=/obj/item/circuitboard/mecha/reticence/targeting),//7 - list("key"=/obj/item/circuitboard/mecha/reticence/peripherals),//8 - list("key"=/obj/item/circuitboard/mecha/reticence/main),//9 - ) - -/datum/construction/mecha/reticence/action(atom/used_atom,mob/user) - return check_step(used_atom,user) - -/datum/construction/mecha/reticence/custom_action(step, atom/used_atom, mob/user) - if(!..()) - return 0 - - if(istype(used_atom, /obj/effect/dummy/mecha_emote_step)) - var/obj/effect/dummy/mecha_emote_step/E = used_atom - holder.visible_message("[holder] likewise [E.emote]") - qdel(used_atom) - - //TODO: better messages. - switch(step) - if(9) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") - qdel(used_atom) - if(8) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - if(7) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - if(6) - user.visible_message("[user] puts beret on the [holder].", "You put beret on the [holder].") - qdel(used_atom) - if(4) - user.visible_message("[user] puts mime mask on the [holder].", "You put mime mask on the [holder].") - qdel(used_atom) - if(2) - user.visible_message("[user] puts suspenders on the [holder].", "You put suspenders on the [holder].") - qdel(used_atom) - return 1 - -/datum/construction/mecha/reticence/spawn_result() - ..() - feedback_inc("mecha_reticence_created",1) - return - -/datum/construction/mecha/durand_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 - list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/durand_head) - ) - -/datum/construction/mecha/durand_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/durand_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/durand_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "durand0" - const_holder.density = 1 - qdel(src) - return - -/datum/construction/reversible/mecha/durand - result = "/obj/mecha/combat/durand" - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/durand_armor, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Super capacitor is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Super capacitor is installed."), - //8 - list("key"=/obj/item/stock_parts/capacitor/super, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Phasic scanner module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Phasic scanner module is installed."), - //10 - list("key"=/obj/item/stock_parts/scanning_module/phasic, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Scanning module is secured."), - //11 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Scanning module is installed."), - //12 - list("key"=/obj/item/circuitboard/mecha/durand/targeting, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //14 - list("key"=/obj/item/circuitboard/mecha/durand/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //15 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/durand/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //17 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //19 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/durand/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/durand/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "durand2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "durand0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "durand3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "durand4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "durand2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "durand5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "durand3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "durand6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/durand/main(get_turf(holder)) - holder.icon_state = "durand4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "durand7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "durand5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "durand8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "durand6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "durand9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "durand7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "durand10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/durand/targeting(get_turf(holder)) - holder.icon_state = "durand8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "durand11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "durand9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") - holder.icon_state = "durand12" - else - user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) - holder.icon_state = "durand10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "durand13" - else - user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") - holder.icon_state = "durand11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") - holder.icon_state = "durand14" - else - user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/super(get_turf(holder)) - holder.icon_state = "durand12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "durand15" - else - user.visible_message("[user] unfastens the super capacitor.", "You unfasten the super capacitor.") - holder.icon_state = "durand13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "durand16" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "durand14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "durand17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "durand15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Durand Armor Plates to the [holder].", "You install Durand Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "durand18" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "durand16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Durand Armor Plates.", "You secure Durand Armor Plates.") - holder.icon_state = "durand19" - else - user.visible_message("[user] pries Durand Armor Plates from the [holder].", "You pry Durand Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/durand_armor(get_turf(holder)) - holder.icon_state = "durand17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Durand Armor Plates to the [holder].", "You weld Durand Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Durand Armor Plates.", "You unfasten Durand Armor Plates.") - holder.icon_state = "durand18" - return 1 - -/datum/construction/reversible/mecha/durand/spawn_result() - ..() - feedback_inc("mecha_durand_created",1) - return - -//PHAZON - -/datum/construction/mecha/phazon_chassis - result = "/obj/mecha/combat/phazon" - steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 - list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/phazon_head) - ) - -/datum/construction/mecha/phazon_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/phazon_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/phazon_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "phazon0" - const_holder.density = 1 - qdel(src) - return - -/datum/construction/reversible/mecha/phazon - result = "/obj/mecha/combat/phazon" - steps = list( - //1 - list("key"=/obj/item/assembly/signaler/anomaly, - "backkey"=null, //Cannot remove the anomaly core once it's in - "desc"="Anomaly core socket is open and awaiting connection."), - //2 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //3 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //4 - list("key"=/obj/item/mecha_parts/part/phazon_armor, - "backkey"=TOOL_WELDER, - "desc"="Phase armor is welded."), - //5 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Phase armor is wrenched."), - //6 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Phase armor is installed."), - //7 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The bluespace crystal is engaged."), - //8 - list("key"=TOOL_SCREWDRIVER, - "backkey"=/obj/item/wirecutters, - "desc"="The bluespace crystal is connected."), - //9 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_CROWBAR, - "desc"="The bluespace crystal is installed."), - //10 - list("key"=/obj/item/stack/ore/bluespace_crystal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Super capacitor is secured."), - //11 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Super capacitor is installed."), - //12 - list("key"=/obj/item/stock_parts/capacitor/super, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Phasic scanner module is secured."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Phasic scanner module is installed."), - //14 - list("key"=/obj/item/stock_parts/scanning_module/phasic, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Scanning module is secured."), - //15 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Scanning module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/phazon/targeting, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //17 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed"), - //18 - list("key"=/obj/item/circuitboard/mecha/phazon/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //19 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //20 - list("key"=/obj/item/circuitboard/mecha/phazon/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //21 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //22 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //23 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //24 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(24) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(23) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "phazon2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "phazon0" - if(22) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "phazon3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(21) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "phazon4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "phazon2" - if(20) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "phazon3" - if(19) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "phazon6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/phazon/main(get_turf(holder)) - holder.icon_state = "phazon4" - if(18) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "phazon5" - if(17) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "phazon8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/phazon/peripherals(get_turf(holder)) - holder.icon_state = "phazon6" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "phazon7" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "phazon10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/phazon/targeting(get_turf(holder)) - holder.icon_state = "phazon8" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "phazon9" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") - holder.icon_state = "phazon12" - else - user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) - holder.icon_state = "phazon10" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon13" - else - user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") - holder.icon_state = "phazon11" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") - holder.icon_state = "phazon14" - else - user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/super(get_turf(holder)) - holder.icon_state = "phazon12" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the bluespace crystal.", "You install the bluespace crystals.") - holder.icon_state = "phazon15" - else - user.visible_message("[user] unsecures the super capacitor from the [holder].", "You unsecure the super capacitor from the [holder].") - holder.icon_state = "phazon13" - if(9) - if(diff==FORWARD) - user.visible_message("[user] connects the bluespace crystal.", "You connect the bluespace crystals.") - holder.icon_state = "phazon16" - else - user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].") - new /obj/item/stack/ore/bluespace_crystal(get_turf(holder), new_amount = 5) - holder.icon_state = "phazon14" - if(8) - if(diff==FORWARD) - user.visible_message("[user] engages the bluespace crystal.", "You engage the bluespace crystals.") - holder.icon_state = "phazon17" - else - user.visible_message("[user] disconnects the bluespace crystal from the [holder].", "You disconnect the bluespace crystal from the [holder].") - holder.icon_state = "phazon15" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs the phase armor layer to the [holder].", "You install the phase armor layer to the [holder].") - holder.icon_state = "phazon18" - else - user.visible_message("[user] disengages the bluespace crystal.", "You disengage the bluespace crystals.") - holder.icon_state = "phazon16" - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures the phase armor layer.", "You secure the phase armor layer.") - holder.icon_state = "phazon19" - else - user.visible_message("[user] pries the phase armor layer from the [holder].", "You pry the phase armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "phazon17" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds the phase armor layer to the [holder].", "You weld the phase armor layer to the [holder].") - holder.icon_state = "phazon20" - else - user.visible_message("[user] unfastens the phase armor layer.", "You unfasten the phase armor layer.") - holder.icon_state = "phazon18" - if(4) - if(diff==FORWARD) - user.visible_message("[user] installs Phazon Armor Plates to the [holder].", "You install Phazon Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon21" - else - user.visible_message("[user] cuts phase armor layer from the [holder].", "You cut the phase armor layer from the [holder].") - holder.icon_state = "phazon19" - if(3) - if(diff==FORWARD) - user.visible_message("[user] secures Phazon Armor Plates.", "You secure Phazon Armor Plates.") - holder.icon_state = "phazon22" - else - user.visible_message("[user] pries Phazon Armor Plates from the [holder].", "You pry Phazon Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/phazon_armor(get_turf(holder)) - holder.icon_state = "phazon20" - if(2) - if(diff==FORWARD) - user.visible_message("[user] welds Phazon Armor Plates to the [holder].", "You weld Phazon Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Phazon Armor Plates.", "You unfasten Phazon Armor Plates.") - holder.icon_state = "phazon21" - if(1) - if(diff==FORWARD) - user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") - qdel(used_atom) - return 1 - -/datum/construction/reversible/mecha/phazon/spawn_result() - ..() - feedback_inc("mecha_phazon_created",1) - return - -//ODYSSEUS - -/datum/construction/mecha/odysseus_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 - list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 - list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 - list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 - list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 - list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 - ) - -/datum/construction/mecha/odysseus_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/odysseus_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/odysseus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "odysseus0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/odysseus - result = "/obj/mecha/medical/odysseus" - steps = list( - //1 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="External armor is wrenched."), - //2 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=TOOL_WELDER, - "desc"="Internal armor is welded."), - //4 - list("key"=TOOL_WELDER, - "backkey"=TOOL_WRENCH, - "desc"="Internal armor is wrenched."), - //5 - list("key"=TOOL_WRENCH, - "backkey"=TOOL_CROWBAR, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Peripherals control module is secured."), - //7 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Peripherals control module is installed."), - //8 - list("key"=/obj/item/circuitboard/mecha/odysseus/peripherals, - "backkey"=TOOL_SCREWDRIVER, - "desc"="Central control module is secured."), - //9 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_CROWBAR, - "desc"="Central control module is installed."), - //10 - list("key"=/obj/item/circuitboard/mecha/odysseus/main, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is adjusted."), - //11 - list("key"=/obj/item/wirecutters, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The wiring is added."), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=TOOL_SCREWDRIVER, - "desc"="The hydraulic systems are active."), - //13 - list("key"=TOOL_SCREWDRIVER, - "backkey"=TOOL_WRENCH, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=TOOL_WRENCH, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/odysseus/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "odysseus2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "odysseus0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "odysseus3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "odysseus4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "odysseus2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "odysseus5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "odysseus3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "odysseus6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/odysseus/main(get_turf(holder)) - holder.icon_state = "odysseus4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "odysseus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "odysseus5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "odysseus8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) - holder.icon_state = "odysseus6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "odysseus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "odysseus7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "odysseus10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "odysseus8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "odysseus11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "odysseus9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs [used_atom] layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - - holder.icon_state = "odysseus12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "odysseus10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "odysseus13" - else - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - user.visible_message("[user] pries [MS] from the [holder].", "You pry [MS] from the [holder].") - holder.icon_state = "odysseus11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - holder.icon_state = "odysseus14" - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "odysseus12" - return 1 - -/datum/construction/reversible/mecha/odysseus/spawn_result() - ..() - feedback_inc("mecha_odysseus_created",1) - return +#define STANDARD_STACK_AMOUNT 5 + +//////////////////////////////// +///// Construction datums ////// +//////////////////////////////// + +/datum/construction/mecha/custom_action(step, atom/used_atom, mob/user) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.use(4)) + playsound(holder, C.usesound, 50, 1) + else + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < STANDARD_STACK_AMOUNT) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(STANDARD_STACK_AMOUNT) + else + return ..() + +/datum/construction/reversible/mecha/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.use(4)) + playsound(holder, C.usesound, 50, 1) + else + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < STANDARD_STACK_AMOUNT) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(STANDARD_STACK_AMOUNT) + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) + return 0 + return 1 + + +/datum/construction/mecha/ripley_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 + ) + +/datum/construction/mecha/ripley_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/ripley_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/ripley_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "ripley0" + const_holder.density = 1 + const_holder.overlays.len = 0 + qdel(src) + return + + +/datum/construction/reversible/mecha/ripley + result = "/obj/mecha/working/ripley" + taskpath = /datum/job_objective/make_ripley + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //8 + list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //10 + list("key"=/obj/item/circuitboard/mecha/ripley/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //11 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/ripley/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/ripley/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "ripley2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "ripley0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "ripley3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "ripley4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "ripley2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "ripley5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "ripley3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "ripley6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "ripley4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "ripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "ripley5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "ripley8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "ripley6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "ripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "ripley7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "ripley10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "ripley8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "ripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "ripley9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + holder.icon_state = "ripley12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "ripley10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "ripley13" + else + user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "ripley11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "ripley12" + return 1 + +/datum/construction/reversible/mecha/ripley/spawn_result() + ..() + feedback_inc("mecha_ripley_created",1) + return + + + +/datum/construction/mecha/gygax_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/gygax_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/gygax_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/gygax_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/gygax + result = "/obj/mecha/combat/gygax" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/gygax_armour, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Advanced capacitor is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Advanced capacitor is installed."), + //8 + list("key"=/obj/item/stock_parts/capacitor/adv, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Advanced scanner module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Advanced scanner module is installed."), + //10 + list("key"=/obj/item/stock_parts/scanning_module/adv, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //12 + list("key"=/obj/item/circuitboard/mecha/gygax/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //14 + list("key"=/obj/item/circuitboard/mecha/gygax/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/gygax/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //17 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/gygax/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/gygax/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/gygax/targeting(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to the [holder].", "You install advanced scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from the [holder].", "You remove the advanced scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to the [holder].", "You install advanced capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from the [holder].", "You remove the advanced capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Gygax Armor Plates to the [holder].", "You install Gygax Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Gygax Armor Plates.", "You secure Gygax Armor Plates.") + holder.icon_state = "gygax19" + else + user.visible_message("[user] pries Gygax Armor Plates from the [holder].", "You pry Gygax Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Gygax Armor Plates to the [holder].", "You weld Gygax Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/gygax/spawn_result() + ..() + feedback_inc("mecha_gygax_created",1) + return + +/datum/construction/mecha/firefighter_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 + list("key"=/obj/item/clothing/suit/fire)//6 + ) + +/datum/construction/mecha/firefighter_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/firefighter_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/firefighter_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "fireripley0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/firefighter + result = "/obj/mecha/working/ripley/firefighter" + taskpath = /datum/job_objective/make_ripley + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is being installed."), + //4 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //5 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //6 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + + //7 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //8 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //9 + list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //10 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //11 + list("key"=/obj/item/circuitboard/mecha/ripley/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //12 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //13 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //14 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //15 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/firefighter/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(15) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(14) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "fireripley2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "fireripley0" + if(13) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "fireripley3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "fireripley4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "fireripley2" + if(11) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "fireripley5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "fireripley3" + if(10) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "fireripley6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "fireripley4" + if(9) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "fireripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "fireripley5" + if(8) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "fireripley8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "fireripley6" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "fireripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "fireripley7" + + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "fireripley10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley8" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "fireripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "fireripley9" + if(4) + if(diff==FORWARD) + user.visible_message("[user] starts to install the external armor layer to the [holder].", "You start to install the external armor layer to the [holder].") + holder.icon_state = "fireripley12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "fireripley10" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + holder.icon_state = "fireripley13" + else + user.visible_message("[user] removes the external armor from the [holder].", "You remove the external armor from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley11" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "fireripley14" + else + user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley12" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "fireripley13" + return 1 + +/datum/construction/reversible/mecha/firefighter/spawn_result() + ..() + feedback_inc("mecha_firefighter_created",1) + return + + + +/datum/construction/mecha/honker_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/honker_torso),//1 + list("key"=/obj/item/mecha_parts/part/honker_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/honker_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/honker_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/honker_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/honker_head) + ) + +/datum/construction/mecha/honker_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/honker_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/honker_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/mecha/honker(const_holder) + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/mecha/honker + result = "/obj/mecha/combat/honker" + steps = list(list("key"=/obj/item/bikehorn),//1 + list("key"=/obj/item/clothing/shoes/clown_shoes),//2 + list("key"=/obj/item/bikehorn),//3 + list("key"=/obj/item/clothing/mask/gas/clown_hat),//4 + list("key"=/obj/item/bikehorn),//5 + list("key"=/obj/item/circuitboard/mecha/honker/targeting),//6 + list("key"=/obj/item/bikehorn),//7 + list("key"=/obj/item/circuitboard/mecha/honker/peripherals),//8 + list("key"=/obj/item/bikehorn),//9 + list("key"=/obj/item/circuitboard/mecha/honker/main),//10 + list("key"=/obj/item/bikehorn),//11 + ) + +/datum/construction/mecha/honker/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/mecha/honker/custom_action(step, atom/used_atom, mob/user) + if(!..()) + return 0 + + if(istype(used_atom, /obj/item/bikehorn)) + playsound(holder, 'sound/items/bikehorn.ogg', 50, 1) + user.visible_message("HONK!") + + //TODO: better messages. + switch(step) + if(10) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") + qdel(used_atom) + if(8) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + if(6) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + if(4) + user.visible_message("[user] puts clown wig and mask on the [holder].", "You put clown wig and mask on the [holder].") + qdel(used_atom) + if(2) + user.visible_message("[user] puts clown boots on the [holder].", "You put clown boots on the [holder].") + qdel(used_atom) + return 1 + +/datum/construction/mecha/honker/spawn_result() + ..() + feedback_inc("mecha_honker_created",1) + return + +/datum/construction/mecha/reticence_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/reticence_torso),//1 + list("key"=/obj/item/mecha_parts/part/reticence_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/reticence_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/reticence_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/reticence_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/reticence_head) + ) + +/datum/construction/mecha/reticence_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/reticence_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state + "+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/reticence_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/mecha/reticence(const_holder) + const_holder.density = 1 + qdel(src) + return + +/datum/construction/mecha/reticence + result = "/obj/mecha/combat/reticence" + steps = list(list("key"=/obj/effect/dummy/mecha_emote_step),//1 + list("key"=/obj/item/clothing/suit/suspenders),//2 + list("key"=/obj/effect/dummy/mecha_emote_step),//3 + list("key"=/obj/item/clothing/mask/gas/mime),//4 + list("key"=/obj/effect/dummy/mecha_emote_step),//5 + list("key"=/obj/item/clothing/head/beret),//6 + list("key"=/obj/item/circuitboard/mecha/reticence/targeting),//7 + list("key"=/obj/item/circuitboard/mecha/reticence/peripherals),//8 + list("key"=/obj/item/circuitboard/mecha/reticence/main),//9 + ) + +/datum/construction/mecha/reticence/action(atom/used_atom,mob/user) + return check_step(used_atom,user) + +/datum/construction/mecha/reticence/custom_action(step, atom/used_atom, mob/user) + if(!..()) + return 0 + + if(istype(used_atom, /obj/effect/dummy/mecha_emote_step)) + var/obj/effect/dummy/mecha_emote_step/E = used_atom + holder.visible_message("[holder] likewise [E.emote]") + qdel(used_atom) + + //TODO: better messages. + switch(step) + if(9) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") + qdel(used_atom) + if(8) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + if(7) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + if(6) + user.visible_message("[user] puts beret on the [holder].", "You put beret on the [holder].") + qdel(used_atom) + if(4) + user.visible_message("[user] puts mime mask on the [holder].", "You put mime mask on the [holder].") + qdel(used_atom) + if(2) + user.visible_message("[user] puts suspenders on the [holder].", "You put suspenders on the [holder].") + qdel(used_atom) + return 1 + +/datum/construction/mecha/reticence/spawn_result() + ..() + feedback_inc("mecha_reticence_created",1) + return + +/datum/construction/mecha/durand_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 + list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/durand_head) + ) + +/datum/construction/mecha/durand_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/durand_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/durand_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "durand0" + const_holder.density = 1 + qdel(src) + return + +/datum/construction/reversible/mecha/durand + result = "/obj/mecha/combat/durand" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/durand_armor, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Super capacitor is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Super capacitor is installed."), + //8 + list("key"=/obj/item/stock_parts/capacitor/super, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Phasic scanner module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Phasic scanner module is installed."), + //10 + list("key"=/obj/item/stock_parts/scanning_module/phasic, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //12 + list("key"=/obj/item/circuitboard/mecha/durand/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //14 + list("key"=/obj/item/circuitboard/mecha/durand/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/durand/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //17 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/durand/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/durand/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "durand2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "durand0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "durand3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "durand4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "durand2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "durand5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "durand3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "durand6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/durand/main(get_turf(holder)) + holder.icon_state = "durand4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "durand7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "durand5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "durand8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "durand6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "durand9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "durand7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "durand10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/durand/targeting(get_turf(holder)) + holder.icon_state = "durand8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "durand11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "durand9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") + holder.icon_state = "durand12" + else + user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) + holder.icon_state = "durand10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "durand13" + else + user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") + holder.icon_state = "durand11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") + holder.icon_state = "durand14" + else + user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/super(get_turf(holder)) + holder.icon_state = "durand12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "durand15" + else + user.visible_message("[user] unfastens the super capacitor.", "You unfasten the super capacitor.") + holder.icon_state = "durand13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "durand16" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "durand14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "durand17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "durand15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Durand Armor Plates to the [holder].", "You install Durand Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "durand18" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "durand16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Durand Armor Plates.", "You secure Durand Armor Plates.") + holder.icon_state = "durand19" + else + user.visible_message("[user] pries Durand Armor Plates from the [holder].", "You pry Durand Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/durand_armor(get_turf(holder)) + holder.icon_state = "durand17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Durand Armor Plates to the [holder].", "You weld Durand Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Durand Armor Plates.", "You unfasten Durand Armor Plates.") + holder.icon_state = "durand18" + return 1 + +/datum/construction/reversible/mecha/durand/spawn_result() + ..() + feedback_inc("mecha_durand_created",1) + return + +//PHAZON + +/datum/construction/mecha/phazon_chassis + result = "/obj/mecha/combat/phazon" + steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 + list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/phazon_head) + ) + +/datum/construction/mecha/phazon_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/phazon_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/phazon_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "phazon0" + const_holder.density = 1 + qdel(src) + return + +/datum/construction/reversible/mecha/phazon + result = "/obj/mecha/combat/phazon" + steps = list( + //1 + list("key"=/obj/item/assembly/signaler/anomaly, + "backkey"=null, //Cannot remove the anomaly core once it's in + "desc"="Anomaly core socket is open and awaiting connection."), + //2 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //3 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //4 + list("key"=/obj/item/mecha_parts/part/phazon_armor, + "backkey"=TOOL_WELDER, + "desc"="Phase armor is welded."), + //5 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Phase armor is wrenched."), + //6 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Phase armor is installed."), + //7 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The bluespace crystal is engaged."), + //8 + list("key"=TOOL_SCREWDRIVER, + "backkey"=/obj/item/wirecutters, + "desc"="The bluespace crystal is connected."), + //9 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_CROWBAR, + "desc"="The bluespace crystal is installed."), + //10 + list("key"=/obj/item/stack/ore/bluespace_crystal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Super capacitor is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Super capacitor is installed."), + //12 + list("key"=/obj/item/stock_parts/capacitor/super, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Phasic scanner module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Phasic scanner module is installed."), + //14 + list("key"=/obj/item/stock_parts/scanning_module/phasic, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/phazon/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //17 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed"), + //18 + list("key"=/obj/item/circuitboard/mecha/phazon/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //20 + list("key"=/obj/item/circuitboard/mecha/phazon/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //21 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //22 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //23 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //24 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(24) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(23) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "phazon2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "phazon0" + if(22) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "phazon3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(21) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "phazon4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "phazon2" + if(20) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "phazon3" + if(19) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "phazon6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/phazon/main(get_turf(holder)) + holder.icon_state = "phazon4" + if(18) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "phazon5" + if(17) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "phazon8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/phazon/peripherals(get_turf(holder)) + holder.icon_state = "phazon6" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "phazon7" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "phazon10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/phazon/targeting(get_turf(holder)) + holder.icon_state = "phazon8" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "phazon9" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") + holder.icon_state = "phazon12" + else + user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) + holder.icon_state = "phazon10" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon13" + else + user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") + holder.icon_state = "phazon11" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") + holder.icon_state = "phazon14" + else + user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/super(get_turf(holder)) + holder.icon_state = "phazon12" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the bluespace crystal.", "You install the bluespace crystals.") + holder.icon_state = "phazon15" + else + user.visible_message("[user] unsecures the super capacitor from the [holder].", "You unsecure the super capacitor from the [holder].") + holder.icon_state = "phazon13" + if(9) + if(diff==FORWARD) + user.visible_message("[user] connects the bluespace crystal.", "You connect the bluespace crystals.") + holder.icon_state = "phazon16" + else + user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].") + new /obj/item/stack/ore/bluespace_crystal(get_turf(holder), new_amount = 5) + holder.icon_state = "phazon14" + if(8) + if(diff==FORWARD) + user.visible_message("[user] engages the bluespace crystal.", "You engage the bluespace crystals.") + holder.icon_state = "phazon17" + else + user.visible_message("[user] disconnects the bluespace crystal from the [holder].", "You disconnect the bluespace crystal from the [holder].") + holder.icon_state = "phazon15" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs the phase armor layer to the [holder].", "You install the phase armor layer to the [holder].") + holder.icon_state = "phazon18" + else + user.visible_message("[user] disengages the bluespace crystal.", "You disengage the bluespace crystals.") + holder.icon_state = "phazon16" + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures the phase armor layer.", "You secure the phase armor layer.") + holder.icon_state = "phazon19" + else + user.visible_message("[user] pries the phase armor layer from the [holder].", "You pry the phase armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "phazon17" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds the phase armor layer to the [holder].", "You weld the phase armor layer to the [holder].") + holder.icon_state = "phazon20" + else + user.visible_message("[user] unfastens the phase armor layer.", "You unfasten the phase armor layer.") + holder.icon_state = "phazon18" + if(4) + if(diff==FORWARD) + user.visible_message("[user] installs Phazon Armor Plates to the [holder].", "You install Phazon Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon21" + else + user.visible_message("[user] cuts phase armor layer from the [holder].", "You cut the phase armor layer from the [holder].") + holder.icon_state = "phazon19" + if(3) + if(diff==FORWARD) + user.visible_message("[user] secures Phazon Armor Plates.", "You secure Phazon Armor Plates.") + holder.icon_state = "phazon22" + else + user.visible_message("[user] pries Phazon Armor Plates from the [holder].", "You pry Phazon Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/phazon_armor(get_turf(holder)) + holder.icon_state = "phazon20" + if(2) + if(diff==FORWARD) + user.visible_message("[user] welds Phazon Armor Plates to the [holder].", "You weld Phazon Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Phazon Armor Plates.", "You unfasten Phazon Armor Plates.") + holder.icon_state = "phazon21" + if(1) + if(diff==FORWARD) + user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") + qdel(used_atom) + return 1 + +/datum/construction/reversible/mecha/phazon/spawn_result() + ..() + feedback_inc("mecha_phazon_created",1) + return + +//ODYSSEUS + +/datum/construction/mecha/odysseus_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 + list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 + list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 + list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 + list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 + list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 + ) + +/datum/construction/mecha/odysseus_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/odysseus_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/odysseus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "odysseus0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/odysseus + result = "/obj/mecha/medical/odysseus" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //8 + list("key"=/obj/item/circuitboard/mecha/odysseus/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //10 + list("key"=/obj/item/circuitboard/mecha/odysseus/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //11 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/odysseus/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "odysseus2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "odysseus0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "odysseus3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "odysseus4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "odysseus2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "odysseus5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "odysseus3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "odysseus6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/odysseus/main(get_turf(holder)) + holder.icon_state = "odysseus4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "odysseus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "odysseus5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "odysseus8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) + holder.icon_state = "odysseus6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "odysseus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "odysseus7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "odysseus10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "odysseus8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "odysseus11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "odysseus9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs [used_atom] layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + + holder.icon_state = "odysseus12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "odysseus10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "odysseus13" + else + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + user.visible_message("[user] pries [MS] from the [holder].", "You pry [MS] from the [holder].") + holder.icon_state = "odysseus11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + holder.icon_state = "odysseus14" + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "odysseus12" + return 1 + +/datum/construction/reversible/mecha/odysseus/spawn_result() + ..() + feedback_inc("mecha_odysseus_created",1) + return diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 35440de6cf99..9a3dffb0c3bc 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -1,161 +1,161 @@ -/obj/machinery/computer/mecha - name = "exosuit control console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "rd_key" - icon_screen = "mecha" - light_color = LIGHT_COLOR_FADEDPURPLE - req_access = list(ACCESS_ROBOTICS) - circuit = /obj/item/circuitboard/mecha_control - var/list/located = list() - var/screen = 0 - var/stored_data - -/obj/machinery/computer/mecha/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/mecha/attack_hand(mob/user) - ui_interact(user) - -/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["screen"] = screen - if(screen == 0) - var/list/mechas[0] - for(var/obj/item/mecha_parts/mecha_tracking/TR in world) - var/answer = TR.get_mecha_info() - if(answer) - mechas[++mechas.len] = answer - data["mechas"] = mechas - if(screen == 1) - data["log"] = stored_data - return data - -/obj/machinery/computer/mecha/Topic(href, href_list) - if(..()) - return 1 - - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["send_message"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") - var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) - if(!trim(message) || ..()) - return 1 - var/obj/mecha/M = MT.in_mecha() - if(M) - M.occupant_message(message) - - if(href_list["shock"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") - MT.shock() - - if(href_list["get_log"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") - stored_data = MT.get_mecha_log() - screen = 1 - - if(href_list["return"]) - screen = 0 - - SSnanoui.update_uis(src) - return - -/obj/item/mecha_parts/mecha_tracking - name = "Exosuit tracking beacon" - desc = "Device used to transmit exosuit data." - icon = 'icons/obj/device.dmi' - icon_state = "motion2" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "programming=2;magnets=2" - var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() - if(!in_mecha()) - return FALSE - var/obj/mecha/M = loc - var/list/answer[0] - answer["reference"] = "\ref[src]" - answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON - if(M.cell) - answer["cell"] = 1 - answer["cell_capacity"] = M.cell.maxcharge - answer["cell_current"] = M.get_charge() - answer["cell_percentage"] = round(M.cell.percent()) - else - answer["cell"] = 0 - answer["integrity"] = round((M.obj_integrity/M.max_integrity*100), 0.01) - answer["airtank"] = M.return_pressure() - answer["pilot"] = "[M.occupant||"None"]" - var/area/area = get_area(M) - answer["location"] = "[sanitize(area.name)||"Unknown"]" - answer["equipment"] = "[M.selected||"None"]" - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer["hascargo"] = 1 - answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 - - return answer - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() - if(!in_mecha()) - return FALSE - var/obj/mecha/M = loc - var/cell_charge = M.get_charge() - var/area/A = get_area(M) - var/answer = {"Name: [M.name] - Integrity: [M.obj_integrity / M.max_integrity * 100]% - Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] - Airtank: [M.return_pressure()]kPa - Pilot: [M.occupant||"None"] - Location: [sanitize(A.name)||"Unknown"] - Active equipment: [M.selected||"None"]
    "} - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
    " - - return answer - -/obj/item/mecha_parts/mecha_tracking/emp_act() - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() - if(istype(loc, /obj/mecha)) - return loc - return FALSE - -/obj/item/mecha_parts/mecha_tracking/proc/shock() - var/obj/mecha/M = in_mecha() - if(M) - M.emp_act(2) - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() - if(!in_mecha()) - return 0 - var/obj/mecha/M = loc - return M.get_log_html() - -/obj/item/mecha_parts/mecha_tracking/ai_control - name = "exosuit AI control beacon" - desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." - origin_tech = "programming=3;magnets=2;engineering=2" - ai_beacon = TRUE - -/obj/item/storage/box/mechabeacons - name = "Exosuit Tracking Beacons" - -/obj/item/storage/box/mechabeacons/New() - ..() - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) +/obj/machinery/computer/mecha + name = "exosuit control console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "rd_key" + icon_screen = "mecha" + light_color = LIGHT_COLOR_FADEDPURPLE + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/mecha_control + var/list/located = list() + var/screen = 0 + var/stored_data + +/obj/machinery/computer/mecha/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/mecha/attack_hand(mob/user) + ui_interact(user) + +/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["screen"] = screen + if(screen == 0) + var/list/mechas[0] + for(var/obj/item/mecha_parts/mecha_tracking/TR in world) + var/answer = TR.get_mecha_info() + if(answer) + mechas[++mechas.len] = answer + data["mechas"] = mechas + if(screen == 1) + data["log"] = stored_data + return data + +/obj/machinery/computer/mecha/Topic(href, href_list) + if(..()) + return 1 + + var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) + if(href_list["send_message"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") + var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) + if(!trim(message) || ..()) + return 1 + var/obj/mecha/M = MT.in_mecha() + if(M) + M.occupant_message(message) + + if(href_list["shock"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") + MT.shock() + + if(href_list["get_log"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") + stored_data = MT.get_mecha_log() + screen = 1 + + if(href_list["return"]) + screen = 0 + + SSnanoui.update_uis(src) + return + +/obj/item/mecha_parts/mecha_tracking + name = "Exosuit tracking beacon" + desc = "Device used to transmit exosuit data." + icon = 'icons/obj/device.dmi' + icon_state = "motion2" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "programming=2;magnets=2" + var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/list/answer[0] + answer["reference"] = "\ref[src]" + answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON + if(M.cell) + answer["cell"] = 1 + answer["cell_capacity"] = M.cell.maxcharge + answer["cell_current"] = M.get_charge() + answer["cell_percentage"] = round(M.cell.percent()) + else + answer["cell"] = 0 + answer["integrity"] = round((M.obj_integrity/M.max_integrity*100), 0.01) + answer["airtank"] = M.return_pressure() + answer["pilot"] = "[M.occupant||"None"]" + var/area/area = get_area(M) + answer["location"] = "[sanitize(area.name)||"Unknown"]" + answer["equipment"] = "[M.selected||"None"]" + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer["hascargo"] = 1 + answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 + + return answer + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/cell_charge = M.get_charge() + var/area/A = get_area(M) + var/answer = {"Name: [M.name] + Integrity: [M.obj_integrity / M.max_integrity * 100]% + Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] + Airtank: [M.return_pressure()]kPa + Pilot: [M.occupant||"None"] + Location: [sanitize(A.name)||"Unknown"] + Active equipment: [M.selected||"None"]
    "} + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
    " + + return answer + +/obj/item/mecha_parts/mecha_tracking/emp_act() + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() + if(istype(loc, /obj/mecha)) + return loc + return FALSE + +/obj/item/mecha_parts/mecha_tracking/proc/shock() + var/obj/mecha/M = in_mecha() + if(M) + M.emp_act(2) + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() + if(!in_mecha()) + return 0 + var/obj/mecha/M = loc + return M.get_log_html() + +/obj/item/mecha_parts/mecha_tracking/ai_control + name = "exosuit AI control beacon" + desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." + origin_tech = "programming=3;magnets=2;engineering=2" + ai_beacon = TRUE + +/obj/item/storage/box/mechabeacons + name = "Exosuit Tracking Beacons" + +/obj/item/storage/box/mechabeacons/New() + ..() + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/mecha/mecha_modkit.dm b/code/game/mecha/mecha_modkit.dm index 9a75fc75077c..9d07e760c79a 100644 --- a/code/game/mecha/mecha_modkit.dm +++ b/code/game/mecha/mecha_modkit.dm @@ -75,4 +75,4 @@ critdestrsound = null weapdestrsound = null lowpowersound = null - longactivationsound = null \ No newline at end of file + longactivationsound = null diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 6aff10eb423c..86fd9cb68212 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -1,457 +1,457 @@ -///////////////////////// -////// Mecha Parts ////// -///////////////////////// - -/obj/item/mecha_parts - name = "mecha part" - icon = 'icons/mecha/mech_construct.dmi' - icon_state = "blank" - w_class = WEIGHT_CLASS_GIGANTIC - flags = CONDUCT - origin_tech = "programming=2;materials=2;engineering=2" - - -/obj/item/mecha_parts/chassis - name="Mecha Chassis" - icon_state = "backbone" - var/datum/construction/construct - flags = CONDUCT - -/obj/item/mecha_parts/chassis/Destroy() - QDEL_NULL(construct) - return ..() - -/obj/item/mecha_parts/chassis/attackby(obj/item/W, mob/user, params) - if(!construct || !construct.action(W, user)) - return ..() - -/obj/item/mecha_parts/chassis/attack_hand() - return - -/////////// Ripley - -/obj/item/mecha_parts/chassis/ripley - name = "Ripley Chassis" - -/obj/item/mecha_parts/chassis/ripley/New() - ..() - construct = new /datum/construction/mecha/ripley_chassis(src) - -/obj/item/mecha_parts/part/ripley_torso - name="Ripley Torso" - desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." - icon_state = "ripley_harness" - origin_tech = "programming=2;materials=2;biotech=2;engineering=2" - -/obj/item/mecha_parts/part/ripley_left_arm - name="Ripley Left Arm" - desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_l_arm" - -/obj/item/mecha_parts/part/ripley_right_arm - name="Ripley Right Arm" - desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_r_arm" - -/obj/item/mecha_parts/part/ripley_left_leg - name="Ripley Left Leg" - desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_l_leg" - -/obj/item/mecha_parts/part/ripley_right_leg - name="Ripley Right Leg" - desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_r_leg" - -///////// Gygax - -/obj/item/mecha_parts/chassis/gygax - name = "Gygax Chassis" - -/obj/item/mecha_parts/chassis/gygax/New() - ..() - construct = new /datum/construction/mecha/gygax_chassis(src) - -/obj/item/mecha_parts/part/gygax_torso - name="Gygax Torso" - desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." - icon_state = "gygax_harness" - origin_tech = "programming=2;materials=4;biotech=3;engineering=3" - -/obj/item/mecha_parts/part/gygax_head - name="Gygax Head" - desc="A Gygax head. Houses advanced surveilance and targeting sensors." - icon_state = "gygax_head" - origin_tech = "programming=2;materials=4;magnets=3;engineering=3" - -/obj/item/mecha_parts/part/gygax_left_arm - name="Gygax Left Arm" - desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_l_arm" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_right_arm - name="Gygax Right Arm" - desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_r_arm" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_left_leg - name="Gygax Left Leg" - icon_state = "gygax_l_leg" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_right_leg - name="Gygax Right Leg" - icon_state = "gygax_r_leg" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_armour - name="Gygax Armour Plates" - icon_state = "gygax_armour" - origin_tech = "materials=6;combat=4;engineering=4" - - -//////////// Durand - -/obj/item/mecha_parts/chassis/durand - name = "Durand Chassis" - -/obj/item/mecha_parts/chassis/durand/New() - ..() - construct = new /datum/construction/mecha/durand_chassis(src) - -/obj/item/mecha_parts/part/durand_torso - name="Durand Torso" - icon_state = "durand_harness" - origin_tech = "programming=2;materials=3;biotech=3;engineering=3" - -/obj/item/mecha_parts/part/durand_head - name="Durand Head" - icon_state = "durand_head" - origin_tech = "programming=2;materials=3;magnets=3;engineering=3" - -/obj/item/mecha_parts/part/durand_left_arm - name="Durand Left Arm" - icon_state = "durand_l_arm" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_right_arm - name="Durand Right Arm" - icon_state = "durand_r_arm" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_left_leg - name="Durand Left Leg" - icon_state = "durand_l_leg" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_right_leg - name="Durand Right Leg" - icon_state = "durand_r_leg" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_armor - name="Durand Armour Plates" - icon_state = "durand_armor" - origin_tech = "materials=5;combat=4;engineering=4" - - - -////////// Firefighter - -/obj/item/mecha_parts/chassis/firefighter - name = "Firefighter Chassis" - -/obj/item/mecha_parts/chassis/firefighter/New() - ..() - construct = new /datum/construction/mecha/firefighter_chassis(src) - -////////// HONK - -/obj/item/mecha_parts/chassis/honker - name = "H.O.N.K Chassis" - -/obj/item/mecha_parts/chassis/honker/New() - ..() - construct = new /datum/construction/mecha/honker_chassis(src) - -/obj/item/mecha_parts/part/honker_torso - name="H.O.N.K Torso" - icon_state = "honker_harness" - -/obj/item/mecha_parts/part/honker_head - name="H.O.N.K Head" - icon_state = "honker_head" - -/obj/item/mecha_parts/part/honker_left_arm - name="H.O.N.K Left Arm" - icon_state = "honker_l_arm" - -/obj/item/mecha_parts/part/honker_right_arm - name="H.O.N.K Right Arm" - icon_state = "honker_r_arm" - -/obj/item/mecha_parts/part/honker_left_leg - name="H.O.N.K Left Leg" - icon_state = "honker_l_leg" - -/obj/item/mecha_parts/part/honker_right_leg - name="H.O.N.K Right Leg" - icon_state = "honker_r_leg" - - -////////// Reticence - -/obj/item/mecha_parts/chassis/reticence - name = "Reticence Chassis" - -/obj/item/mecha_parts/chassis/reticence/New() - ..() - construct = new /datum/construction/mecha/reticence_chassis(src) - -/obj/effect/dummy/mecha_emote_step - var/emote - -/obj/effect/dummy/mecha_emote_step/New(e) - emote = e - -/obj/item/mecha_parts/chassis/reticence/hear_message(mob/living/M, msg) - if(!istype(M) || !istype(construct, /datum/construction/mecha/reticence)) - return - // is the current step the dummy emote object? - var/list/steps = construct.steps - if(steps[steps.len]["key"] == /obj/effect/dummy/mecha_emote_step) - construct.action(new /obj/effect/dummy/mecha_emote_step(msg), M) - -/obj/item/mecha_parts/part/reticence_torso - name = "Reticence Torso" - icon_state = "reticence_harness" - -/obj/item/mecha_parts/part/reticence_head - name = "Reticence Head" - icon_state = "reticence_head" - -/obj/item/mecha_parts/part/reticence_left_arm - name = "Reticence Left Arm" - icon_state = "reticence_l_arm" - -/obj/item/mecha_parts/part/reticence_right_arm - name = "Reticence Right Arm" - icon_state = "reticence_r_arm" - -/obj/item/mecha_parts/part/reticence_left_leg - name = "Reticence Left Leg" - icon_state = "reticence_l_leg" - -/obj/item/mecha_parts/part/reticence_right_leg - name = "Reticence Right Leg" - icon_state = "reticence_r_leg" - - -////////// Phazon - -/obj/item/mecha_parts/chassis/phazon - name = "Phazon Chassis" - -/obj/item/mecha_parts/chassis/phazon/New() - ..() - construct = new /datum/construction/mecha/phazon_chassis(src) - -/obj/item/mecha_parts/part/phazon_torso - name="Phazon Torso" - icon_state = "phazon_harness" - origin_tech = "programming=4;materials=4;bluespace=4;plasmatech=5" - -/obj/item/mecha_parts/part/phazon_head - name="Phazon Head" - icon_state = "phazon_head" - origin_tech = "programming=3;materials=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_left_arm - name="Phazon Left Arm" - icon_state = "phazon_l_arm" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_right_arm - name="Phazon Right Arm" - icon_state = "phazon_r_arm" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_left_leg - name="Phazon Left Leg" - icon_state = "phazon_l_leg" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_right_leg - name="Phazon Right Leg" - icon_state = "phazon_r_leg" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_armor - name="Phazon armor" - desc="Phazon armor plates. They are layered with plasma to protect the pilot from the stress of phasing and have unusual properties." - icon_state = "phazon_armor" - origin_tech = "materials=4;bluespace=4;plasmatech=5" - -///////// Odysseus -/obj/item/mecha_parts/chassis/odysseus - name = "Odysseus Chassis" - -/obj/item/mecha_parts/chassis/odysseus/New() - ..() - construct = new /datum/construction/mecha/odysseus_chassis(src) - -/obj/item/mecha_parts/part/odysseus_head - name="Odysseus Head" - icon_state = "odysseus_head" - -/obj/item/mecha_parts/part/odysseus_torso - name="Odysseus Torso" - desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." - icon_state = "odysseus_torso" - origin_tech = "programming=2;materials=2;biotech=2;engineering=2" - -/obj/item/mecha_parts/part/odysseus_left_arm - name="Odysseus Left Arm" - desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_l_arm" - -/obj/item/mecha_parts/part/odysseus_right_arm - name="Odysseus Right Arm" - desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_r_arm" - -/obj/item/mecha_parts/part/odysseus_left_leg - name="Odysseus Left Leg" - desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_l_leg" - -/obj/item/mecha_parts/part/odysseus_right_leg - name="Odysseus Right Leg" - desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_r_leg" - -/*/obj/item/mecha_parts/part/odysseus_armour - name="Odysseus Carapace" - icon_state = "odysseus_armour" - origin_tech = "materials=3;engineering=3")*/ - - -///////// Circuitboards - -/obj/item/circuitboard/mecha - name = "Exosuit Circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - item_state = "electronic" - board_type = "other" - flags = CONDUCT - force = 5.0 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5.0 - throw_speed = 3 - throw_range = 15 - -/obj/item/circuitboard/mecha/ripley - origin_tech = "programming=2" - -/obj/item/circuitboard/mecha/ripley/peripherals - name = "Circuit board (Ripley Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/ripley/main - name = "Circuit board (Ripley Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/gygax - origin_tech = "programming=4;combat=3;engineering=3" - -/obj/item/circuitboard/mecha/gygax/peripherals - name = "Circuit board (Gygax Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/gygax/targeting - name = "Circuit board (Gygax Weapon Control and Targeting module)" - icon_state = "mcontroller" - origin_tech = "programming=4;combat=4" - -/obj/item/circuitboard/mecha/gygax/main - name = "Circuit board (Gygax Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/durand - origin_tech = "programming=4;combat=3;engineering=3" - -/obj/item/circuitboard/mecha/durand/peripherals - name = "Circuit board (Durand Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/durand/targeting - name = "Circuit board (Durand Weapon Control and Targeting module)" - icon_state = "mcontroller" - origin_tech = "programming=4;combat=4;engineering=3" - -/obj/item/circuitboard/mecha/durand/main - name = "Circuit board (Durand Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/phazon - origin_tech = "programming=5;plasmatech=4" - -/obj/item/circuitboard/mecha/phazon/peripherals - name = "Circuit board (Phazon Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/phazon/targeting - name = "Circuit board (Phazon Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/phazon/main - name = "Circuit board (Phazon Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/honker - origin_tech = "programming=3;engineering=3" - -/obj/item/circuitboard/mecha/honker/peripherals - name = "Circuit board (H.O.N.K Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/honker/targeting - name = "Circuit board (H.O.N.K Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/honker/main - name = "Circuit board (H.O.N.K Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/reticence - origin_tech = "programming=3;engineering=3" - -/obj/item/circuitboard/mecha/reticence/peripherals - name = "circuit board (Reticence Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/reticence/targeting - name = "circuit board (Reticence Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/reticence/main - name = "circuit board (Reticence Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/odysseus - origin_tech = "programming=3;biotech=3" - -/obj/item/circuitboard/mecha/odysseus/peripherals - name = "Circuit board (Odysseus Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/odysseus/main - name = "Circuit board (Odysseus Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/pod - name = "Circuit board (Space Pod Mainboard)" - icon_state = "mainboard" +///////////////////////// +////// Mecha Parts ////// +///////////////////////// + +/obj/item/mecha_parts + name = "mecha part" + icon = 'icons/mecha/mech_construct.dmi' + icon_state = "blank" + w_class = WEIGHT_CLASS_GIGANTIC + flags = CONDUCT + origin_tech = "programming=2;materials=2;engineering=2" + + +/obj/item/mecha_parts/chassis + name="Mecha Chassis" + icon_state = "backbone" + var/datum/construction/construct + flags = CONDUCT + +/obj/item/mecha_parts/chassis/Destroy() + QDEL_NULL(construct) + return ..() + +/obj/item/mecha_parts/chassis/attackby(obj/item/W, mob/user, params) + if(!construct || !construct.action(W, user)) + return ..() + +/obj/item/mecha_parts/chassis/attack_hand() + return + +/////////// Ripley + +/obj/item/mecha_parts/chassis/ripley + name = "Ripley Chassis" + +/obj/item/mecha_parts/chassis/ripley/New() + ..() + construct = new /datum/construction/mecha/ripley_chassis(src) + +/obj/item/mecha_parts/part/ripley_torso + name="Ripley Torso" + desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." + icon_state = "ripley_harness" + origin_tech = "programming=2;materials=2;biotech=2;engineering=2" + +/obj/item/mecha_parts/part/ripley_left_arm + name="Ripley Left Arm" + desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_l_arm" + +/obj/item/mecha_parts/part/ripley_right_arm + name="Ripley Right Arm" + desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_r_arm" + +/obj/item/mecha_parts/part/ripley_left_leg + name="Ripley Left Leg" + desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_l_leg" + +/obj/item/mecha_parts/part/ripley_right_leg + name="Ripley Right Leg" + desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_r_leg" + +///////// Gygax + +/obj/item/mecha_parts/chassis/gygax + name = "Gygax Chassis" + +/obj/item/mecha_parts/chassis/gygax/New() + ..() + construct = new /datum/construction/mecha/gygax_chassis(src) + +/obj/item/mecha_parts/part/gygax_torso + name="Gygax Torso" + desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." + icon_state = "gygax_harness" + origin_tech = "programming=2;materials=4;biotech=3;engineering=3" + +/obj/item/mecha_parts/part/gygax_head + name="Gygax Head" + desc="A Gygax head. Houses advanced surveilance and targeting sensors." + icon_state = "gygax_head" + origin_tech = "programming=2;materials=4;magnets=3;engineering=3" + +/obj/item/mecha_parts/part/gygax_left_arm + name="Gygax Left Arm" + desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_l_arm" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_right_arm + name="Gygax Right Arm" + desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_r_arm" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_left_leg + name="Gygax Left Leg" + icon_state = "gygax_l_leg" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_right_leg + name="Gygax Right Leg" + icon_state = "gygax_r_leg" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_armour + name="Gygax Armour Plates" + icon_state = "gygax_armour" + origin_tech = "materials=6;combat=4;engineering=4" + + +//////////// Durand + +/obj/item/mecha_parts/chassis/durand + name = "Durand Chassis" + +/obj/item/mecha_parts/chassis/durand/New() + ..() + construct = new /datum/construction/mecha/durand_chassis(src) + +/obj/item/mecha_parts/part/durand_torso + name="Durand Torso" + icon_state = "durand_harness" + origin_tech = "programming=2;materials=3;biotech=3;engineering=3" + +/obj/item/mecha_parts/part/durand_head + name="Durand Head" + icon_state = "durand_head" + origin_tech = "programming=2;materials=3;magnets=3;engineering=3" + +/obj/item/mecha_parts/part/durand_left_arm + name="Durand Left Arm" + icon_state = "durand_l_arm" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_right_arm + name="Durand Right Arm" + icon_state = "durand_r_arm" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_left_leg + name="Durand Left Leg" + icon_state = "durand_l_leg" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_right_leg + name="Durand Right Leg" + icon_state = "durand_r_leg" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_armor + name="Durand Armour Plates" + icon_state = "durand_armor" + origin_tech = "materials=5;combat=4;engineering=4" + + + +////////// Firefighter + +/obj/item/mecha_parts/chassis/firefighter + name = "Firefighter Chassis" + +/obj/item/mecha_parts/chassis/firefighter/New() + ..() + construct = new /datum/construction/mecha/firefighter_chassis(src) + +////////// HONK + +/obj/item/mecha_parts/chassis/honker + name = "H.O.N.K Chassis" + +/obj/item/mecha_parts/chassis/honker/New() + ..() + construct = new /datum/construction/mecha/honker_chassis(src) + +/obj/item/mecha_parts/part/honker_torso + name="H.O.N.K Torso" + icon_state = "honker_harness" + +/obj/item/mecha_parts/part/honker_head + name="H.O.N.K Head" + icon_state = "honker_head" + +/obj/item/mecha_parts/part/honker_left_arm + name="H.O.N.K Left Arm" + icon_state = "honker_l_arm" + +/obj/item/mecha_parts/part/honker_right_arm + name="H.O.N.K Right Arm" + icon_state = "honker_r_arm" + +/obj/item/mecha_parts/part/honker_left_leg + name="H.O.N.K Left Leg" + icon_state = "honker_l_leg" + +/obj/item/mecha_parts/part/honker_right_leg + name="H.O.N.K Right Leg" + icon_state = "honker_r_leg" + + +////////// Reticence + +/obj/item/mecha_parts/chassis/reticence + name = "Reticence Chassis" + +/obj/item/mecha_parts/chassis/reticence/New() + ..() + construct = new /datum/construction/mecha/reticence_chassis(src) + +/obj/effect/dummy/mecha_emote_step + var/emote + +/obj/effect/dummy/mecha_emote_step/New(e) + emote = e + +/obj/item/mecha_parts/chassis/reticence/hear_message(mob/living/M, msg) + if(!istype(M) || !istype(construct, /datum/construction/mecha/reticence)) + return + // is the current step the dummy emote object? + var/list/steps = construct.steps + if(steps[steps.len]["key"] == /obj/effect/dummy/mecha_emote_step) + construct.action(new /obj/effect/dummy/mecha_emote_step(msg), M) + +/obj/item/mecha_parts/part/reticence_torso + name = "Reticence Torso" + icon_state = "reticence_harness" + +/obj/item/mecha_parts/part/reticence_head + name = "Reticence Head" + icon_state = "reticence_head" + +/obj/item/mecha_parts/part/reticence_left_arm + name = "Reticence Left Arm" + icon_state = "reticence_l_arm" + +/obj/item/mecha_parts/part/reticence_right_arm + name = "Reticence Right Arm" + icon_state = "reticence_r_arm" + +/obj/item/mecha_parts/part/reticence_left_leg + name = "Reticence Left Leg" + icon_state = "reticence_l_leg" + +/obj/item/mecha_parts/part/reticence_right_leg + name = "Reticence Right Leg" + icon_state = "reticence_r_leg" + + +////////// Phazon + +/obj/item/mecha_parts/chassis/phazon + name = "Phazon Chassis" + +/obj/item/mecha_parts/chassis/phazon/New() + ..() + construct = new /datum/construction/mecha/phazon_chassis(src) + +/obj/item/mecha_parts/part/phazon_torso + name="Phazon Torso" + icon_state = "phazon_harness" + origin_tech = "programming=4;materials=4;bluespace=4;plasmatech=5" + +/obj/item/mecha_parts/part/phazon_head + name="Phazon Head" + icon_state = "phazon_head" + origin_tech = "programming=3;materials=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_left_arm + name="Phazon Left Arm" + icon_state = "phazon_l_arm" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_right_arm + name="Phazon Right Arm" + icon_state = "phazon_r_arm" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_left_leg + name="Phazon Left Leg" + icon_state = "phazon_l_leg" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_right_leg + name="Phazon Right Leg" + icon_state = "phazon_r_leg" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_armor + name="Phazon armor" + desc="Phazon armor plates. They are layered with plasma to protect the pilot from the stress of phasing and have unusual properties." + icon_state = "phazon_armor" + origin_tech = "materials=4;bluespace=4;plasmatech=5" + +///////// Odysseus +/obj/item/mecha_parts/chassis/odysseus + name = "Odysseus Chassis" + +/obj/item/mecha_parts/chassis/odysseus/New() + ..() + construct = new /datum/construction/mecha/odysseus_chassis(src) + +/obj/item/mecha_parts/part/odysseus_head + name="Odysseus Head" + icon_state = "odysseus_head" + +/obj/item/mecha_parts/part/odysseus_torso + name="Odysseus Torso" + desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." + icon_state = "odysseus_torso" + origin_tech = "programming=2;materials=2;biotech=2;engineering=2" + +/obj/item/mecha_parts/part/odysseus_left_arm + name="Odysseus Left Arm" + desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_l_arm" + +/obj/item/mecha_parts/part/odysseus_right_arm + name="Odysseus Right Arm" + desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_r_arm" + +/obj/item/mecha_parts/part/odysseus_left_leg + name="Odysseus Left Leg" + desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_l_leg" + +/obj/item/mecha_parts/part/odysseus_right_leg + name="Odysseus Right Leg" + desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_r_leg" + +/*/obj/item/mecha_parts/part/odysseus_armour + name="Odysseus Carapace" + icon_state = "odysseus_armour" + origin_tech = "materials=3;engineering=3")*/ + + +///////// Circuitboards + +/obj/item/circuitboard/mecha + name = "Exosuit Circuit board" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + board_type = "other" + flags = CONDUCT + force = 5.0 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5.0 + throw_speed = 3 + throw_range = 15 + +/obj/item/circuitboard/mecha/ripley + origin_tech = "programming=2" + +/obj/item/circuitboard/mecha/ripley/peripherals + name = "Circuit board (Ripley Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/ripley/main + name = "Circuit board (Ripley Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/gygax + origin_tech = "programming=4;combat=3;engineering=3" + +/obj/item/circuitboard/mecha/gygax/peripherals + name = "Circuit board (Gygax Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/gygax/targeting + name = "Circuit board (Gygax Weapon Control and Targeting module)" + icon_state = "mcontroller" + origin_tech = "programming=4;combat=4" + +/obj/item/circuitboard/mecha/gygax/main + name = "Circuit board (Gygax Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/durand + origin_tech = "programming=4;combat=3;engineering=3" + +/obj/item/circuitboard/mecha/durand/peripherals + name = "Circuit board (Durand Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/durand/targeting + name = "Circuit board (Durand Weapon Control and Targeting module)" + icon_state = "mcontroller" + origin_tech = "programming=4;combat=4;engineering=3" + +/obj/item/circuitboard/mecha/durand/main + name = "Circuit board (Durand Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/phazon + origin_tech = "programming=5;plasmatech=4" + +/obj/item/circuitboard/mecha/phazon/peripherals + name = "Circuit board (Phazon Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/phazon/targeting + name = "Circuit board (Phazon Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/phazon/main + name = "Circuit board (Phazon Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/honker + origin_tech = "programming=3;engineering=3" + +/obj/item/circuitboard/mecha/honker/peripherals + name = "Circuit board (H.O.N.K Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/honker/targeting + name = "Circuit board (H.O.N.K Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/honker/main + name = "Circuit board (H.O.N.K Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/reticence + origin_tech = "programming=3;engineering=3" + +/obj/item/circuitboard/mecha/reticence/peripherals + name = "circuit board (Reticence Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/reticence/targeting + name = "circuit board (Reticence Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/reticence/main + name = "circuit board (Reticence Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/odysseus + origin_tech = "programming=3;biotech=3" + +/obj/item/circuitboard/mecha/odysseus/peripherals + name = "Circuit board (Odysseus Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/odysseus/main + name = "Circuit board (Odysseus Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/pod + name = "Circuit board (Space Pod Mainboard)" + icon_state = "mainboard" diff --git a/code/game/mecha/mecha_wreckage.dm b/code/game/mecha/mecha_wreckage.dm index 9f01dc83af41..d061d2e2005b 100644 --- a/code/game/mecha/mecha_wreckage.dm +++ b/code/game/mecha/mecha_wreckage.dm @@ -1,215 +1,215 @@ -/////////////////////////////////// -//////// Mecha wreckage //////// -/////////////////////////////////// - - -/obj/structure/mecha_wreckage - name = "exosuit wreckage" - desc = "Remains of some unfortunate mecha. Completely unrepairable, but perhaps something can be salvaged." - icon = 'icons/mecha/mecha.dmi' - density = TRUE - anchored = FALSE - opacity = 0 - var/list/welder_salvage = list(/obj/item/stack/sheet/plasteel, /obj/item/stack/sheet/metal, /obj/item/stack/rods) - var/salvage_num = 5 - var/list/crowbar_salvage = list() - var/wires_removed = FALSE - var/mob/living/silicon/ai/AI //AIs to be salvaged - var/list/parts - -/obj/structure/mecha_wreckage/Initialize(mapload, mob/living/silicon/ai/AI_pilot) - . = ..() - if(parts) - for(var/i in 1 to 2) - if(!parts.len) - break - if(prob(60)) - continue - var/part = pick(parts) - welder_salvage += part - parts = null - if(!AI_pilot) //Type-checking for this is already done in mecha/Destroy() - return - AI = AI_pilot - AI.apply_damage(150, BURN) //Give the AI a bit of damage from the "shock" of being suddenly shut down - AI.death() //The damage is not enough to kill the AI, but to be 'corrupted files' in need of repair. - AI.forceMove(src) //Put the dead AI inside the wreckage for recovery - add_overlay(mutable_appearance('icons/obj/projectiles.dmi', "green_laser")) //Overlay for the recovery beacon - AI.controlled_mech = null - AI.remote_control = null - -/obj/structure/mecha_wreckage/Destroy() - QDEL_NULL(AI) - QDEL_LIST(crowbar_salvage) - return ..() - -/obj/structure/mecha_wreckage/examine(mob/user) - . = ..() - if(!AI) - return - . += "The AI recovery beacon is active." - -/obj/structure/mecha_wreckage/crowbar_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(crowbar_salvage.len) - var/obj/S = pick(crowbar_salvage) - S.forceMove(user.drop_location()) - user.visible_message("[user] pries [S] from [src].", "You pry [S] from [src].") - crowbar_salvage -= S - return - to_chat(user, "You don't see anything that can be cut with [I]!") - -/obj/structure/mecha_wreckage/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(salvage_num <= 0 || !length(welder_salvage)) - to_chat(user, "You don't see anything that can be cut with [I]!") - return - if(prob(30)) - to_chat(user, "You fail to salvage anything valuable from [src]!") - return - var/type = pick(welder_salvage) - var/N = new type(get_turf(user)) - user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") - if(!istype(N, /obj/item/stack)) - welder_salvage -= type - salvage_num-- - -/obj/structure/mecha_wreckage/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(wires_removed) - to_chat(user, "You don't see anything that can be cut with [I]!") - return - var/N = new /obj/item/stack/cable_coil(get_turf(user), rand(1, 3)) - user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") - wires_removed = TRUE - -/obj/structure/mecha_wreckage/transfer_ai(interaction, mob/user, null, obj/item/aicard/card) - if(!..()) - return - - //Proc called on the wreck by the AI card. - if(interaction != AI_TRANS_TO_CARD) //AIs can only be transferred in one direction, from the wreck to the card. - return - if(!AI) //No AI in the wreck - to_chat(user, "No AI backups found.") - return - cut_overlays() //Remove the recovery beacon overlay - AI.forceMove(card) //Move the dead AI to the card. - if(AI.client) //AI player is still in the dead AI and is connected - to_chat(AI, "The remains of your file system have been recovered on a mobile storage device.") - else //Give the AI a heads-up that it is probably going to get fixed. - AI.notify_ghost_cloning("You have been recovered from the wreckage!", source = card) - to_chat(user, "Backup files recovered: [AI.name] ([rand(1000, 9999)].exe) salvaged from [name] and stored within local memory.") - AI = null - -/obj/structure/mecha_wreckage/gygax - name = "\improper Gygax wreckage" - icon_state = "gygax-broken" - parts = list( - /obj/item/mecha_parts/part/gygax_torso, - /obj/item/mecha_parts/part/gygax_head, - /obj/item/mecha_parts/part/gygax_left_arm, - /obj/item/mecha_parts/part/gygax_right_arm, - /obj/item/mecha_parts/part/gygax_left_leg, - /obj/item/mecha_parts/part/gygax_right_leg - ) - -/obj/structure/mecha_wreckage/gygax/dark - name = "\improper Dark Gygax wreckage" - icon_state = "darkgygax-broken" - -/obj/structure/mecha_wreckage/marauder - name = "\improper Marauder wreckage" - icon_state = "marauder-broken" - -/obj/structure/mecha_wreckage/mauler - name = "\improper Mauler wreckage" - icon_state = "mauler-broken" - desc = "The syndicate won't be very happy about this..." - -/obj/structure/mecha_wreckage/seraph - name = "\improper Seraph wreckage" - icon_state = "seraph-broken" - -/obj/structure/mecha_wreckage/reticence - name = "\improper Reticence wreckage" - icon_state = "reticence-broken" - color = "#87878715" - desc = "..." - -/obj/structure/mecha_wreckage/ripley - name = "\improper Ripley wreckage" - icon_state = "ripley-broken" - parts = list(/obj/item/mecha_parts/part/ripley_torso, - /obj/item/mecha_parts/part/ripley_left_arm, - /obj/item/mecha_parts/part/ripley_right_arm, - /obj/item/mecha_parts/part/ripley_left_leg, - /obj/item/mecha_parts/part/ripley_right_leg) - -/obj/structure/mecha_wreckage/ripley/mkii - name = "\improper Ripley MK-II wreckage" - icon_state = "ripleymkii-broken" - -/obj/structure/mecha_wreckage/ripley/firefighter - name = "\improper Firefighter wreckage" - icon_state = "firefighter-broken" - parts = list(/obj/item/mecha_parts/part/ripley_torso, - /obj/item/mecha_parts/part/ripley_left_arm, - /obj/item/mecha_parts/part/ripley_right_arm, - /obj/item/mecha_parts/part/ripley_left_leg, - /obj/item/mecha_parts/part/ripley_right_leg, - /obj/item/clothing/suit/fire) - -/obj/structure/mecha_wreckage/ripley/deathripley - name = "\improper Death-Ripley wreckage" - icon_state = "deathripley-broken" - parts = null - -/obj/structure/mecha_wreckage/honker - name = "\improper H.O.N.K wreckage" - icon_state = "honker-broken" - desc = "All is right in the universe." - parts = list( - /obj/item/mecha_parts/chassis/honker, - /obj/item/mecha_parts/part/honker_torso, - /obj/item/mecha_parts/part/honker_head, - /obj/item/mecha_parts/part/honker_left_arm, - /obj/item/mecha_parts/part/honker_right_arm, - /obj/item/mecha_parts/part/honker_left_leg, - /obj/item/mecha_parts/part/honker_right_leg) - -/obj/structure/mecha_wreckage/durand - name = "\improper Durand wreckage" - icon_state = "durand-broken" - parts = list( - /obj/item/mecha_parts/part/durand_torso, - /obj/item/mecha_parts/part/durand_head, - /obj/item/mecha_parts/part/durand_left_arm, - /obj/item/mecha_parts/part/durand_right_arm, - /obj/item/mecha_parts/part/durand_left_leg, - /obj/item/mecha_parts/part/durand_right_leg) - -/obj/structure/mecha_wreckage/durand/old - icon_state = "old_durand-broken" - -/obj/structure/mecha_wreckage/phazon - name = "\improper Phazon wreckage" - icon_state = "phazon-broken" - - -/obj/structure/mecha_wreckage/odysseus - name = "\improper Odysseus wreckage" - icon_state = "odysseus-broken" - parts = list( - /obj/item/mecha_parts/part/odysseus_torso, - /obj/item/mecha_parts/part/odysseus_head, - /obj/item/mecha_parts/part/odysseus_left_arm, - /obj/item/mecha_parts/part/odysseus_right_arm, - /obj/item/mecha_parts/part/odysseus_left_leg, - /obj/item/mecha_parts/part/odysseus_right_leg) +/////////////////////////////////// +//////// Mecha wreckage //////// +/////////////////////////////////// + + +/obj/structure/mecha_wreckage + name = "exosuit wreckage" + desc = "Remains of some unfortunate mecha. Completely unrepairable, but perhaps something can be salvaged." + icon = 'icons/mecha/mecha.dmi' + density = TRUE + anchored = FALSE + opacity = 0 + var/list/welder_salvage = list(/obj/item/stack/sheet/plasteel, /obj/item/stack/sheet/metal, /obj/item/stack/rods) + var/salvage_num = 5 + var/list/crowbar_salvage = list() + var/wires_removed = FALSE + var/mob/living/silicon/ai/AI //AIs to be salvaged + var/list/parts + +/obj/structure/mecha_wreckage/Initialize(mapload, mob/living/silicon/ai/AI_pilot) + . = ..() + if(parts) + for(var/i in 1 to 2) + if(!parts.len) + break + if(prob(60)) + continue + var/part = pick(parts) + welder_salvage += part + parts = null + if(!AI_pilot) //Type-checking for this is already done in mecha/Destroy() + return + AI = AI_pilot + AI.apply_damage(150, BURN) //Give the AI a bit of damage from the "shock" of being suddenly shut down + AI.death() //The damage is not enough to kill the AI, but to be 'corrupted files' in need of repair. + AI.forceMove(src) //Put the dead AI inside the wreckage for recovery + add_overlay(mutable_appearance('icons/obj/projectiles.dmi', "green_laser")) //Overlay for the recovery beacon + AI.controlled_mech = null + AI.remote_control = null + +/obj/structure/mecha_wreckage/Destroy() + QDEL_NULL(AI) + QDEL_LIST(crowbar_salvage) + return ..() + +/obj/structure/mecha_wreckage/examine(mob/user) + . = ..() + if(!AI) + return + . += "The AI recovery beacon is active." + +/obj/structure/mecha_wreckage/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(crowbar_salvage.len) + var/obj/S = pick(crowbar_salvage) + S.forceMove(user.drop_location()) + user.visible_message("[user] pries [S] from [src].", "You pry [S] from [src].") + crowbar_salvage -= S + return + to_chat(user, "You don't see anything that can be cut with [I]!") + +/obj/structure/mecha_wreckage/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(salvage_num <= 0 || !length(welder_salvage)) + to_chat(user, "You don't see anything that can be cut with [I]!") + return + if(prob(30)) + to_chat(user, "You fail to salvage anything valuable from [src]!") + return + var/type = pick(welder_salvage) + var/N = new type(get_turf(user)) + user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") + if(!istype(N, /obj/item/stack)) + welder_salvage -= type + salvage_num-- + +/obj/structure/mecha_wreckage/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wires_removed) + to_chat(user, "You don't see anything that can be cut with [I]!") + return + var/N = new /obj/item/stack/cable_coil(get_turf(user), rand(1, 3)) + user.visible_message("[user] cuts [N] from [src].", "You cut [N] from [src].") + wires_removed = TRUE + +/obj/structure/mecha_wreckage/transfer_ai(interaction, mob/user, null, obj/item/aicard/card) + if(!..()) + return + + //Proc called on the wreck by the AI card. + if(interaction != AI_TRANS_TO_CARD) //AIs can only be transferred in one direction, from the wreck to the card. + return + if(!AI) //No AI in the wreck + to_chat(user, "No AI backups found.") + return + cut_overlays() //Remove the recovery beacon overlay + AI.forceMove(card) //Move the dead AI to the card. + if(AI.client) //AI player is still in the dead AI and is connected + to_chat(AI, "The remains of your file system have been recovered on a mobile storage device.") + else //Give the AI a heads-up that it is probably going to get fixed. + AI.notify_ghost_cloning("You have been recovered from the wreckage!", source = card) + to_chat(user, "Backup files recovered: [AI.name] ([rand(1000, 9999)].exe) salvaged from [name] and stored within local memory.") + AI = null + +/obj/structure/mecha_wreckage/gygax + name = "\improper Gygax wreckage" + icon_state = "gygax-broken" + parts = list( + /obj/item/mecha_parts/part/gygax_torso, + /obj/item/mecha_parts/part/gygax_head, + /obj/item/mecha_parts/part/gygax_left_arm, + /obj/item/mecha_parts/part/gygax_right_arm, + /obj/item/mecha_parts/part/gygax_left_leg, + /obj/item/mecha_parts/part/gygax_right_leg + ) + +/obj/structure/mecha_wreckage/gygax/dark + name = "\improper Dark Gygax wreckage" + icon_state = "darkgygax-broken" + +/obj/structure/mecha_wreckage/marauder + name = "\improper Marauder wreckage" + icon_state = "marauder-broken" + +/obj/structure/mecha_wreckage/mauler + name = "\improper Mauler wreckage" + icon_state = "mauler-broken" + desc = "The syndicate won't be very happy about this..." + +/obj/structure/mecha_wreckage/seraph + name = "\improper Seraph wreckage" + icon_state = "seraph-broken" + +/obj/structure/mecha_wreckage/reticence + name = "\improper Reticence wreckage" + icon_state = "reticence-broken" + color = "#87878715" + desc = "..." + +/obj/structure/mecha_wreckage/ripley + name = "\improper Ripley wreckage" + icon_state = "ripley-broken" + parts = list(/obj/item/mecha_parts/part/ripley_torso, + /obj/item/mecha_parts/part/ripley_left_arm, + /obj/item/mecha_parts/part/ripley_right_arm, + /obj/item/mecha_parts/part/ripley_left_leg, + /obj/item/mecha_parts/part/ripley_right_leg) + +/obj/structure/mecha_wreckage/ripley/mkii + name = "\improper Ripley MK-II wreckage" + icon_state = "ripleymkii-broken" + +/obj/structure/mecha_wreckage/ripley/firefighter + name = "\improper Firefighter wreckage" + icon_state = "firefighter-broken" + parts = list(/obj/item/mecha_parts/part/ripley_torso, + /obj/item/mecha_parts/part/ripley_left_arm, + /obj/item/mecha_parts/part/ripley_right_arm, + /obj/item/mecha_parts/part/ripley_left_leg, + /obj/item/mecha_parts/part/ripley_right_leg, + /obj/item/clothing/suit/fire) + +/obj/structure/mecha_wreckage/ripley/deathripley + name = "\improper Death-Ripley wreckage" + icon_state = "deathripley-broken" + parts = null + +/obj/structure/mecha_wreckage/honker + name = "\improper H.O.N.K wreckage" + icon_state = "honker-broken" + desc = "All is right in the universe." + parts = list( + /obj/item/mecha_parts/chassis/honker, + /obj/item/mecha_parts/part/honker_torso, + /obj/item/mecha_parts/part/honker_head, + /obj/item/mecha_parts/part/honker_left_arm, + /obj/item/mecha_parts/part/honker_right_arm, + /obj/item/mecha_parts/part/honker_left_leg, + /obj/item/mecha_parts/part/honker_right_leg) + +/obj/structure/mecha_wreckage/durand + name = "\improper Durand wreckage" + icon_state = "durand-broken" + parts = list( + /obj/item/mecha_parts/part/durand_torso, + /obj/item/mecha_parts/part/durand_head, + /obj/item/mecha_parts/part/durand_left_arm, + /obj/item/mecha_parts/part/durand_right_arm, + /obj/item/mecha_parts/part/durand_left_leg, + /obj/item/mecha_parts/part/durand_right_leg) + +/obj/structure/mecha_wreckage/durand/old + icon_state = "old_durand-broken" + +/obj/structure/mecha_wreckage/phazon + name = "\improper Phazon wreckage" + icon_state = "phazon-broken" + + +/obj/structure/mecha_wreckage/odysseus + name = "\improper Odysseus wreckage" + icon_state = "odysseus-broken" + parts = list( + /obj/item/mecha_parts/part/odysseus_torso, + /obj/item/mecha_parts/part/odysseus_head, + /obj/item/mecha_parts/part/odysseus_left_arm, + /obj/item/mecha_parts/part/odysseus_right_arm, + /obj/item/mecha_parts/part/odysseus_left_leg, + /obj/item/mecha_parts/part/odysseus_right_leg) diff --git a/code/game/mecha/medical/medical.dm b/code/game/mecha/medical/medical.dm index 8aee3ef1f4c0..2255885030ca 100644 --- a/code/game/mecha/medical/medical.dm +++ b/code/game/mecha/medical/medical.dm @@ -1,7 +1,7 @@ -/obj/mecha/medical - turnsound = 'sound/mecha/mechmove01.ogg' - stepsound = 'sound/mecha/mechstep.ogg' - -/obj/mecha/medical/New() - ..() - trackers += new /obj/item/mecha_parts/mecha_tracking(src) +/obj/mecha/medical + turnsound = 'sound/mecha/mechmove01.ogg' + stepsound = 'sound/mecha/mechstep.ogg' + +/obj/mecha/medical/New() + ..() + trackers += new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/mecha/medical/odysseus.dm b/code/game/mecha/medical/odysseus.dm index dcee2c1e74ef..930032580088 100644 --- a/code/game/mecha/medical/odysseus.dm +++ b/code/game/mecha/medical/odysseus.dm @@ -1,46 +1,46 @@ -/obj/mecha/medical/odysseus - desc = "These exosuits are developed and produced by Vey-Med. (© All rights reserved)." - name = "Odysseus" - icon_state = "odysseus" - initial_icon = "odysseus" - step_in = 3 - max_temperature = 15000 - max_integrity = 120 - wreckage = /obj/structure/mecha_wreckage/odysseus - internal_damage_threshold = 35 - deflect_chance = 15 - step_energy_drain = 6 - normal_step_energy_drain = 6 - var/builtin_hud_user = 0 - -/obj/mecha/medical/odysseus/moved_inside(var/mob/living/carbon/human/H) - . = ..() - if(. && ishuman(H)) - if(istype(H.glasses, /obj/item/clothing/glasses/hud)) - occupant_message("[H.glasses] prevent you from using the built-in medical hud.") - else - var/datum/atom_hud/data/human/medical/advanced/A = huds[DATA_HUD_MEDICAL_ADVANCED] - A.add_hud_to(H) - builtin_hud_user = 1 - -/obj/mecha/medical/odysseus/mmi_moved_inside(var/obj/item/mmi/mmi_as_oc, mob/user) - . = ..() - if(.) - if(occupant.client) - var/datum/atom_hud/A = huds[DATA_HUD_MEDICAL_ADVANCED] - A.add_hud_to(occupant) - builtin_hud_user = 1 - -/obj/mecha/medical/odysseus/go_out() - if(ishuman(occupant) && builtin_hud_user) - var/mob/living/carbon/human/H = occupant - var/datum/atom_hud/data/human/medical/advanced/A = huds[DATA_HUD_MEDICAL_ADVANCED] - A.remove_hud_from(H) - builtin_hud_user = 0 - else if((isbrain(occupant) || pilot_is_mmi()) && builtin_hud_user) - var/mob/living/carbon/brain/H = occupant - var/datum/atom_hud/A = huds[DATA_HUD_MEDICAL_ADVANCED] - A.remove_hud_from(H) - builtin_hud_user = 0 - - . = ..() \ No newline at end of file +/obj/mecha/medical/odysseus + desc = "These exosuits are developed and produced by Vey-Med. (© All rights reserved)." + name = "Odysseus" + icon_state = "odysseus" + initial_icon = "odysseus" + step_in = 3 + max_temperature = 15000 + max_integrity = 120 + wreckage = /obj/structure/mecha_wreckage/odysseus + internal_damage_threshold = 35 + deflect_chance = 15 + step_energy_drain = 6 + normal_step_energy_drain = 6 + var/builtin_hud_user = 0 + +/obj/mecha/medical/odysseus/moved_inside(var/mob/living/carbon/human/H) + . = ..() + if(. && ishuman(H)) + if(istype(H.glasses, /obj/item/clothing/glasses/hud)) + occupant_message("[H.glasses] prevent you from using the built-in medical hud.") + else + var/datum/atom_hud/data/human/medical/advanced/A = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + A.add_hud_to(H) + builtin_hud_user = 1 + +/obj/mecha/medical/odysseus/mmi_moved_inside(var/obj/item/mmi/mmi_as_oc, mob/user) + . = ..() + if(.) + if(occupant.client) + var/datum/atom_hud/A = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + A.add_hud_to(occupant) + builtin_hud_user = 1 + +/obj/mecha/medical/odysseus/go_out() + if(ishuman(occupant) && builtin_hud_user) + var/mob/living/carbon/human/H = occupant + var/datum/atom_hud/data/human/medical/advanced/A = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + A.remove_hud_from(H) + builtin_hud_user = 0 + else if((isbrain(occupant) || pilot_is_mmi()) && builtin_hud_user) + var/mob/living/carbon/brain/H = occupant + var/datum/atom_hud/A = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] + A.remove_hud_from(H) + builtin_hud_user = 0 + + . = ..() diff --git a/code/game/mecha/paintkits.dm b/code/game/mecha/paintkits.dm index d11f87973db0..861c80c4672f 100644 --- a/code/game/mecha/paintkits.dm +++ b/code/game/mecha/paintkits.dm @@ -28,4 +28,4 @@ new_name = "APLU \"Strike the Earth!\"" new_desc = "Looks like an over worked, under maintained Ripley with some horrific damage." new_icon = "earth" - allowed_types = list("ripley","firefighter") \ No newline at end of file + allowed_types = list("ripley","firefighter") diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 0e095f7ca12e..0b106f2ab4c6 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -1,207 +1,207 @@ -/obj/mecha/working/ripley - desc = "Autonomous Power Loader Unit. This newer model is refitted with powerful armour against the dangers of the EVA mining process." - name = "APLU \"Ripley\"" - icon_state = "ripley" - initial_icon = "ripley" - step_in = 4 //Move speed, lower is faster. - var/fast_pressure_step_in = 2 //step_in while in normal pressure conditions - var/slow_pressure_step_in = 4 //step_in while in better pressure conditions - max_temperature = 20000 - max_integrity = 200 - lights_power = 7 - deflect_chance = 15 - armor = list("melee" = 40, "bullet" = 20, "laser" = 10, "energy" = 20, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - max_equip = 6 - wreckage = /obj/structure/mecha_wreckage/ripley - var/list/cargo = new - var/cargo_capacity = 15 - var/hides = 0 - -/obj/mecha/working/ripley/Move() - . = ..() - if(.) - collect_ore() - update_pressure() - -/obj/mecha/working/ripley/proc/collect_ore() - if(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in equipment) - var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo - if(ore_box) - for(var/obj/item/stack/ore/ore in range(1, src)) - if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! - ore.forceMove(ore_box) - -/obj/mecha/working/ripley/Destroy() - for(var/i=1, i <= hides, i++) - new /obj/item/stack/sheet/animalhide/goliath_hide(loc) //If a goliath-plated ripley gets killed, all the plates drop - for(var/atom/movable/A in cargo) - A.forceMove(loc) - step_rand(A) - cargo.Cut() - return ..() - -/obj/mecha/working/ripley/go_out() - ..() - update_icon() - -/obj/mecha/working/ripley/moved_inside(mob/living/carbon/human/H) - ..() - update_icon() - -/obj/mecha/working/ripley/mmi_moved_inside(obj/item/mmi/mmi_as_oc, mob/user) - ..() - update_icon() - -/obj/mecha/working/ripley/update_icon() - ..() - if(hides) - cut_overlays() - if(hides < 3) - add_overlay(occupant ? "ripley-g" : "ripley-g-open") - else - add_overlay(occupant ? "ripley-g-full" : "ripley-g-full-open") - -/obj/mecha/working/ripley/firefighter - desc = "Standart APLU chassis was refitted with additional thermal protection and cistern." - name = "APLU \"Firefighter\"" - icon_state = "firefighter" - initial_icon = "firefighter" - max_temperature = 65000 - max_integrity = 250 - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - lights_power = 7 - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 60, "bio" = 0, "rad" = 70, "fire" = 100, "acid" = 100) - max_equip = 5 // More armor, less tools - wreckage = /obj/structure/mecha_wreckage/ripley/firefighter - -/obj/mecha/working/ripley/deathripley - desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" - name = "DEATH-RIPLEY" - icon_state = "deathripley" - initial_icon = "deathripley" - step_in = 3 - slow_pressure_step_in = 3 - opacity=0 - max_temperature = 65000 - max_integrity = 300 - lights_power = 7 - armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 0, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - wreckage = /obj/structure/mecha_wreckage/ripley/deathripley - step_energy_drain = 0 - normal_step_energy_drain = 0 - -/obj/mecha/working/ripley/deathripley/New() - ..() - var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill - ME.attach(src) - return - -/obj/mecha/working/ripley/mining - desc = "An old, dusty mining ripley." - name = "APLU \"Miner\"" - obj_integrity = 75 //Low starting health - -/obj/mecha/working/ripley/mining/New() - ..() - if(cell) - cell.charge = FLOOR(cell.charge * 0.25, 1) //Starts at very low charge - //Attach drill - if(prob(70)) //Maybe add a drill - if(prob(15)) //Possible diamond drill... Feeling lucky? - var/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill/D = new - D.attach(src) - else - var/obj/item/mecha_parts/mecha_equipment/drill/D = new - D.attach(src) - - else //Add plasma cutter if no drill - var/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/P = new - P.attach(src) - - //Add ore box to cargo - cargo.Add(new /obj/structure/ore_box(src)) - - //Attach hydraulic clamp - var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new - HC.attach(src) - for(var/obj/item/mecha_parts/mecha_tracking/B in trackers)//Deletes the beacon so it can't be found easily - qdel(B) - - var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new - scanner.attach(src) - -/obj/mecha/working/ripley/Exit(atom/movable/O) - if(O in cargo) - return 0 - return ..() - -/obj/mecha/working/ripley/Topic(href, href_list) - ..() - if(href_list["drop_from_cargo"]) - var/obj/O = locate(href_list["drop_from_cargo"]) - if(O && O in cargo) - occupant_message("You unload [O].") - O.loc = get_turf(src) - cargo -= O - var/turf/T = get_turf(O) - if(T) - T.Entered(O) - log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - cargo.len]") - return - - - -/obj/mecha/working/ripley/get_stats_part() - var/output = ..() - output += "Cargo Compartment Contents:
    " - if(cargo.len) - for(var/obj/O in cargo) - output += "Unload : [O]
    " - else - output += "Nothing" - output += "
    " - return output - -/obj/mecha/working/ripley/Destroy() - for(var/mob/M in src) - if(M == occupant) - continue - M.loc = get_turf(src) - M.loc.Entered(M) - step_rand(M) - for(var/atom/movable/A in cargo) - A.loc = get_turf(src) - var/turf/T = get_turf(A) - if(T) - T.Entered(A) - step_rand(A) - return ..() - -/obj/mecha/working/ripley/ex_act(severity) - ..() - for(var/X in cargo) - var/obj/O = X - if(prob(30 / severity)) - cargo -= O - O.forceMove(drop_location()) - -/obj/mecha/working/ripley/proc/update_pressure() - var/turf/T = get_turf(loc) - - if(lavaland_equipment_pressure_check(T)) - step_in = fast_pressure_step_in - for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) - drill.equip_cooldown = initial(drill.equip_cooldown)/2 - else - step_in = slow_pressure_step_in - for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) - drill.equip_cooldown = initial(drill.equip_cooldown) - -/obj/mecha/working/ripley/emag_act(mob/user) - if(!emagged) - emagged = TRUE - to_chat(user, "You slide the card through [src]'s ID slot.") - playsound(loc, "sparks", 100, 1) - desc += "
    The mech's equipment slots spark dangerously!" - else - to_chat(user, "[src]'s ID slot rejects the card.") +/obj/mecha/working/ripley + desc = "Autonomous Power Loader Unit. This newer model is refitted with powerful armour against the dangers of the EVA mining process." + name = "APLU \"Ripley\"" + icon_state = "ripley" + initial_icon = "ripley" + step_in = 4 //Move speed, lower is faster. + var/fast_pressure_step_in = 2 //step_in while in normal pressure conditions + var/slow_pressure_step_in = 4 //step_in while in better pressure conditions + max_temperature = 20000 + max_integrity = 200 + lights_power = 7 + deflect_chance = 15 + armor = list("melee" = 40, "bullet" = 20, "laser" = 10, "energy" = 20, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + max_equip = 6 + wreckage = /obj/structure/mecha_wreckage/ripley + var/list/cargo = new + var/cargo_capacity = 15 + var/hides = 0 + +/obj/mecha/working/ripley/Move() + . = ..() + if(.) + collect_ore() + update_pressure() + +/obj/mecha/working/ripley/proc/collect_ore() + if(locate(/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp) in equipment) + var/obj/structure/ore_box/ore_box = locate(/obj/structure/ore_box) in cargo + if(ore_box) + for(var/obj/item/stack/ore/ore in range(1, src)) + if(ore.Adjacent(src) && ((get_dir(src, ore) & dir) || ore.loc == loc)) //we can reach it and it's in front of us? grab it! + ore.forceMove(ore_box) + +/obj/mecha/working/ripley/Destroy() + for(var/i=1, i <= hides, i++) + new /obj/item/stack/sheet/animalhide/goliath_hide(loc) //If a goliath-plated ripley gets killed, all the plates drop + for(var/atom/movable/A in cargo) + A.forceMove(loc) + step_rand(A) + cargo.Cut() + return ..() + +/obj/mecha/working/ripley/go_out() + ..() + update_icon() + +/obj/mecha/working/ripley/moved_inside(mob/living/carbon/human/H) + ..() + update_icon() + +/obj/mecha/working/ripley/mmi_moved_inside(obj/item/mmi/mmi_as_oc, mob/user) + ..() + update_icon() + +/obj/mecha/working/ripley/update_icon() + ..() + if(hides) + cut_overlays() + if(hides < 3) + add_overlay(occupant ? "ripley-g" : "ripley-g-open") + else + add_overlay(occupant ? "ripley-g-full" : "ripley-g-full-open") + +/obj/mecha/working/ripley/firefighter + desc = "Standart APLU chassis was refitted with additional thermal protection and cistern." + name = "APLU \"Firefighter\"" + icon_state = "firefighter" + initial_icon = "firefighter" + max_temperature = 65000 + max_integrity = 250 + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + lights_power = 7 + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 60, "bio" = 0, "rad" = 70, "fire" = 100, "acid" = 100) + max_equip = 5 // More armor, less tools + wreckage = /obj/structure/mecha_wreckage/ripley/firefighter + +/obj/mecha/working/ripley/deathripley + desc = "OH SHIT IT'S THE DEATHSQUAD WE'RE ALL GONNA DIE" + name = "DEATH-RIPLEY" + icon_state = "deathripley" + initial_icon = "deathripley" + step_in = 3 + slow_pressure_step_in = 3 + opacity=0 + max_temperature = 65000 + max_integrity = 300 + lights_power = 7 + armor = list("melee" = 40, "bullet" = 40, "laser" = 40, "energy" = 0, "bomb" = 70, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + wreckage = /obj/structure/mecha_wreckage/ripley/deathripley + step_energy_drain = 0 + normal_step_energy_drain = 0 + +/obj/mecha/working/ripley/deathripley/New() + ..() + var/obj/item/mecha_parts/mecha_equipment/ME = new /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/kill + ME.attach(src) + return + +/obj/mecha/working/ripley/mining + desc = "An old, dusty mining ripley." + name = "APLU \"Miner\"" + obj_integrity = 75 //Low starting health + +/obj/mecha/working/ripley/mining/New() + ..() + if(cell) + cell.charge = FLOOR(cell.charge * 0.25, 1) //Starts at very low charge + //Attach drill + if(prob(70)) //Maybe add a drill + if(prob(15)) //Possible diamond drill... Feeling lucky? + var/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill/D = new + D.attach(src) + else + var/obj/item/mecha_parts/mecha_equipment/drill/D = new + D.attach(src) + + else //Add plasma cutter if no drill + var/obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma/P = new + P.attach(src) + + //Add ore box to cargo + cargo.Add(new /obj/structure/ore_box(src)) + + //Attach hydraulic clamp + var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new + HC.attach(src) + for(var/obj/item/mecha_parts/mecha_tracking/B in trackers)//Deletes the beacon so it can't be found easily + qdel(B) + + var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new + scanner.attach(src) + +/obj/mecha/working/ripley/Exit(atom/movable/O) + if(O in cargo) + return 0 + return ..() + +/obj/mecha/working/ripley/Topic(href, href_list) + ..() + if(href_list["drop_from_cargo"]) + var/obj/O = locate(href_list["drop_from_cargo"]) + if(O && O in cargo) + occupant_message("You unload [O].") + O.loc = get_turf(src) + cargo -= O + var/turf/T = get_turf(O) + if(T) + T.Entered(O) + log_message("Unloaded [O]. Cargo compartment capacity: [cargo_capacity - cargo.len]") + return + + + +/obj/mecha/working/ripley/get_stats_part() + var/output = ..() + output += "Cargo Compartment Contents:
    " + if(cargo.len) + for(var/obj/O in cargo) + output += "Unload : [O]
    " + else + output += "Nothing" + output += "
    " + return output + +/obj/mecha/working/ripley/Destroy() + for(var/mob/M in src) + if(M == occupant) + continue + M.loc = get_turf(src) + M.loc.Entered(M) + step_rand(M) + for(var/atom/movable/A in cargo) + A.loc = get_turf(src) + var/turf/T = get_turf(A) + if(T) + T.Entered(A) + step_rand(A) + return ..() + +/obj/mecha/working/ripley/ex_act(severity) + ..() + for(var/X in cargo) + var/obj/O = X + if(prob(30 / severity)) + cargo -= O + O.forceMove(drop_location()) + +/obj/mecha/working/ripley/proc/update_pressure() + var/turf/T = get_turf(loc) + + if(lavaland_equipment_pressure_check(T)) + step_in = fast_pressure_step_in + for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) + drill.equip_cooldown = initial(drill.equip_cooldown)/2 + else + step_in = slow_pressure_step_in + for(var/obj/item/mecha_parts/mecha_equipment/drill/drill in equipment) + drill.equip_cooldown = initial(drill.equip_cooldown) + +/obj/mecha/working/ripley/emag_act(mob/user) + if(!emagged) + emagged = TRUE + to_chat(user, "You slide the card through [src]'s ID slot.") + playsound(loc, "sparks", 100, 1) + desc += "
    The mech's equipment slots spark dangerously!" + else + to_chat(user, "[src]'s ID slot rejects the card.") diff --git a/code/game/mecha/working/working.dm b/code/game/mecha/working/working.dm index 516b57e898a8..b29b64bed26b 100644 --- a/code/game/mecha/working/working.dm +++ b/code/game/mecha/working/working.dm @@ -1,7 +1,7 @@ -/obj/mecha/working - internal_damage_threshold = 60 - -/obj/mecha/working/New() - ..() - if(!ruin_mecha) - trackers += new /obj/item/mecha_parts/mecha_tracking(src) +/obj/mecha/working + internal_damage_threshold = 60 + +/obj/mecha/working/New() + ..() + if(!ruin_mecha) + trackers += new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index aa5eef1f04ac..bb1070f2cf13 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -150,4 +150,4 @@ /mob/living/proc/check_buckled() if(buckled && !(buckled in loc)) - buckled.unbuckle_mob(src, force = TRUE) \ No newline at end of file + buckled.unbuckle_mob(src, force = TRUE) diff --git a/code/game/objects/effects/alien_acid.dm b/code/game/objects/effects/alien_acid.dm index 8f383fd518c5..96c89c625391 100644 --- a/code/game/objects/effects/alien_acid.dm +++ b/code/game/objects/effects/alien_acid.dm @@ -89,4 +89,4 @@ if(8) visible_message("[target] is struggling to withstand the acid!") if(4) - visible_message("[target] begins to crumble under the acid!") \ No newline at end of file + visible_message("[target] begins to crumble under the acid!") diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm index cc2100d568f9..8b9e5e92319f 100644 --- a/code/game/objects/effects/anomalies.dm +++ b/code/game/objects/effects/anomalies.dm @@ -26,7 +26,7 @@ /obj/effect/anomaly/proc/anomalyEffect() if(prob(50)) - step(src,pick(alldirs)) + step(src,pick(GLOB.alldirs)) /obj/effect/anomaly/proc/anomalyNeutralize() diff --git a/code/game/objects/effects/bump_teleporter.dm b/code/game/objects/effects/bump_teleporter.dm index 02e35b7e8763..f2ba7d03ee0e 100644 --- a/code/game/objects/effects/bump_teleporter.dm +++ b/code/game/objects/effects/bump_teleporter.dm @@ -1,40 +1,40 @@ -var/list/obj/effect/bump_teleporter/BUMP_TELEPORTERS = list() - -/obj/effect/bump_teleporter - name = "bump-teleporter" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x2" - var/id = null //id of this bump_teleporter. - var/id_target = null //id of bump_teleporter which this moves you to. - invisibility = 101 //nope, can't see this - anchored = 1 - density = 1 - opacity = 0 - -/obj/effect/bump_teleporter/New() - ..() - BUMP_TELEPORTERS += src - -/obj/effect/bump_teleporter/Destroy() - BUMP_TELEPORTERS -= src - return ..() - -/obj/effect/bump_teleporter/singularity_act() - return - -/obj/effect/bump_teleporter/singularity_pull() - return - -/obj/effect/bump_teleporter/Bumped(atom/user) - if(!ismob(user)) - //user.loc = src.loc //Stop at teleporter location - return - - if(!id_target) - //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. - return - - for(var/obj/effect/bump_teleporter/BT in BUMP_TELEPORTERS) - if(BT.id == src.id_target) - usr.loc = BT.loc //Teleport to location with correct id. - return \ No newline at end of file +GLOBAL_LIST_EMPTY(bump_teleporters) + +/obj/effect/bump_teleporter + name = "bump-teleporter" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x2" + var/id = null //id of this bump_teleporter. + var/id_target = null //id of bump_teleporter which this moves you to. + invisibility = 101 //nope, can't see this + anchored = 1 + density = 1 + opacity = 0 + +/obj/effect/bump_teleporter/New() + ..() + GLOB.bump_teleporters += src + +/obj/effect/bump_teleporter/Destroy() + GLOB.bump_teleporters -= src + return ..() + +/obj/effect/bump_teleporter/singularity_act() + return + +/obj/effect/bump_teleporter/singularity_pull() + return + +/obj/effect/bump_teleporter/Bumped(atom/user) + if(!ismob(user)) + //user.loc = src.loc //Stop at teleporter location + return + + if(!id_target) + //user.loc = src.loc //Stop at teleporter location, there is nowhere to teleport to. + return + + for(var/obj/effect/bump_teleporter/BT in GLOB.bump_teleporters) + if(BT.id == src.id_target) + usr.loc = BT.loc //Teleport to location with correct id. + return diff --git a/code/game/objects/effects/decals/Cleanable/fuel.dm b/code/game/objects/effects/decals/Cleanable/fuel.dm index 6c7ea8f32472..cc7289190796 100644 --- a/code/game/objects/effects/decals/Cleanable/fuel.dm +++ b/code/game/objects/effects/decals/Cleanable/fuel.dm @@ -26,7 +26,7 @@ var/turf/simulated/S = loc if(!istype(S)) return - for(var/d in cardinal) + for(var/d in GLOB.cardinal) if(rand(25)) var/turf/simulated/target = get_step(src, d) var/turf/simulated/origin = get_turf(src) diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index fd7ce1250897..7e6265847806 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -1,6 +1,6 @@ #define DRYING_TIME 5 * 60 * 10 //for 1 unit of depth in puddle (amount var) -var/global/list/image/splatter_cache = list() +GLOBAL_LIST_EMPTY(splatter_cache) /obj/effect/decal/cleanable/blood name = "blood" diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index cee5517fd0a5..a8510ddc102c 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -211,4 +211,4 @@ icon = 'icons/effects/blood.dmi' icon_state = "xfloor1" random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") - anchored = TRUE \ No newline at end of file + anchored = TRUE diff --git a/code/game/objects/effects/decals/Cleanable/robots.dm b/code/game/objects/effects/decals/Cleanable/robots.dm index 5193436295e4..91f558068a8e 100644 --- a/code/game/objects/effects/decals/Cleanable/robots.dm +++ b/code/game/objects/effects/decals/Cleanable/robots.dm @@ -1,60 +1,60 @@ -/obj/effect/decal/cleanable/blood/gibs/robot - name = "robot debris" - desc = "It's a useless heap of junk... or is it?" - icon = 'icons/mob/robots.dmi' - icon_state = "gib1" - basecolor = "#030303" - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7") - bloodiness = BLOOD_AMOUNT_PER_DECAL - mergeable_decal = FALSE - -/obj/effect/decal/cleanable/blood/gibs/robot/can_bloodcrawl_in() - return FALSE - -/obj/effect/decal/cleanable/blood/gibs/robot/update_icon() - color = "#FFFFFF" - -/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like - return - -/obj/effect/decal/cleanable/blood/gibs/robot/can_bloodcrawl_in() - return FALSE - -/obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) - spawn(0) - var/direction = pick(directions) - for(var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) - sleep(3) - if(i > 0) - if(prob(40)) - var/obj/effect/decal/cleanable/blood/oil/streak = new(src.loc) - streak.update_icon() - else if(prob(10)) - do_sparks(3, 1, src) - if(step_to(src, get_step(src, direction), 0)) - break - -/obj/effect/decal/cleanable/blood/gibs/robot/limb - random_icon_states = list("gibarm", "gibleg") - -/obj/effect/decal/cleanable/blood/gibs/robot/up - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7", "gibup1", "gibup1") //2:7 is close enough to 1:4 - -/obj/effect/decal/cleanable/blood/gibs/robot/down - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7", "gibdown1", "gibdown1") //2:7 is close enough to 1:4 - -/obj/effect/decal/cleanable/blood/oil - name = "motor oil" - desc = "It's black and greasy. Looks like Beepsky made another mess." - basecolor = "#030303" - bloodiness = MAX_SHOE_BLOODINESS - -/obj/effect/decal/cleanable/blood/oil/can_bloodcrawl_in() - return FALSE - -/obj/effect/decal/cleanable/blood/oil/dry() - return - -/obj/effect/decal/cleanable/blood/oil/streak - random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") - amount = 2 +/obj/effect/decal/cleanable/blood/gibs/robot + name = "robot debris" + desc = "It's a useless heap of junk... or is it?" + icon = 'icons/mob/robots.dmi' + icon_state = "gib1" + basecolor = "#030303" + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7") + bloodiness = BLOOD_AMOUNT_PER_DECAL + mergeable_decal = FALSE + +/obj/effect/decal/cleanable/blood/gibs/robot/can_bloodcrawl_in() + return FALSE + +/obj/effect/decal/cleanable/blood/gibs/robot/update_icon() + color = "#FFFFFF" + +/obj/effect/decal/cleanable/blood/gibs/robot/dry() //pieces of robots do not dry up like + return + +/obj/effect/decal/cleanable/blood/gibs/robot/can_bloodcrawl_in() + return FALSE + +/obj/effect/decal/cleanable/blood/gibs/robot/streak(var/list/directions) + spawn(0) + var/direction = pick(directions) + for(var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) + sleep(3) + if(i > 0) + if(prob(40)) + var/obj/effect/decal/cleanable/blood/oil/streak = new(src.loc) + streak.update_icon() + else if(prob(10)) + do_sparks(3, 1, src) + if(step_to(src, get_step(src, direction), 0)) + break + +/obj/effect/decal/cleanable/blood/gibs/robot/limb + random_icon_states = list("gibarm", "gibleg") + +/obj/effect/decal/cleanable/blood/gibs/robot/up + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7", "gibup1", "gibup1") //2:7 is close enough to 1:4 + +/obj/effect/decal/cleanable/blood/gibs/robot/down + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6", "gib7", "gibdown1", "gibdown1") //2:7 is close enough to 1:4 + +/obj/effect/decal/cleanable/blood/oil + name = "motor oil" + desc = "It's black and greasy. Looks like Beepsky made another mess." + basecolor = "#030303" + bloodiness = MAX_SHOE_BLOODINESS + +/obj/effect/decal/cleanable/blood/oil/can_bloodcrawl_in() + return FALSE + +/obj/effect/decal/cleanable/blood/oil/dry() + return + +/obj/effect/decal/cleanable/blood/oil/streak + random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") + amount = 2 diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index cdcc5e5da3c9..832a66a93bad 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -2,7 +2,7 @@ #define TRACKS_CRUSTIFY_TIME 50 // color-dir-dry -var/global/list/image/fluidtrack_cache = list() +GLOBAL_LIST_EMPTY(fluidtrack_cache) // Footprints, tire trails... /obj/effect/decal/cleanable/blood/tracks @@ -55,7 +55,7 @@ var/global/list/image/fluidtrack_cache = list() if(!(entered_dirs & H.dir)) entered_dirs |= H.dir update_icon() - + /obj/effect/decal/cleanable/blood/footprints/Uncrossed(atom/movable/O) ..() if(ishuman(O)) @@ -87,24 +87,24 @@ var/global/list/image/fluidtrack_cache = list() /obj/effect/decal/cleanable/blood/footprints/update_icon() overlays.Cut() - for(var/Ddir in cardinal) + for(var/Ddir in GLOB.cardinal) if(entered_dirs & Ddir) var/image/I - if(fluidtrack_cache["entered-[blood_state]-[Ddir]"]) - I = fluidtrack_cache["entered-[blood_state]-[Ddir]"] + if(GLOB.fluidtrack_cache["entered-[blood_state]-[Ddir]"]) + I = GLOB.fluidtrack_cache["entered-[blood_state]-[Ddir]"] else I = image(icon,"[blood_state]1",dir = Ddir) - fluidtrack_cache["entered-[blood_state]-[Ddir]"] = I + GLOB.fluidtrack_cache["entered-[blood_state]-[Ddir]"] = I if(I) I.color = basecolor overlays += I if(exited_dirs & Ddir) var/image/I - if(fluidtrack_cache["exited-[blood_state]-[Ddir]"]) - I = fluidtrack_cache["exited-[blood_state]-[Ddir]"] + if(GLOB.fluidtrack_cache["exited-[blood_state]-[Ddir]"]) + I = GLOB.fluidtrack_cache["exited-[blood_state]-[Ddir]"] else I = image(icon,"[blood_state]2",dir = Ddir) - fluidtrack_cache["exited-[blood_state]-[Ddir]"] = I + GLOB.fluidtrack_cache["exited-[blood_state]-[Ddir]"] = I if(I) I.color = basecolor overlays += I @@ -135,4 +135,4 @@ var/global/list/image/fluidtrack_cache = list() /obj/effect/decal/cleanable/blood/footprints/replace_decal(obj/effect/decal/cleanable/blood/footprints/C) if(blood_state != C.blood_state) //We only replace footprints of the same type as us return - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/effects/decals/cleanable.dm b/code/game/objects/effects/decals/cleanable.dm index 9c34fbdcc5e6..ca92d2da3197 100644 --- a/code/game/objects/effects/decals/cleanable.dm +++ b/code/game/objects/effects/decals/cleanable.dm @@ -1,73 +1,73 @@ -/obj/effect/decal/cleanable - anchored = TRUE - var/list/random_icon_states = list() - var/bloodiness = 0 //0-100, amount of blood in this decal, used for making footprints and affecting the alpha of bloody footprints - var/mergeable_decal = TRUE //when two of these are on a same tile or do we need to merge them into just one? - -/obj/effect/decal/cleanable/proc/replace_decal(obj/effect/decal/cleanable/C) // Returns true if we should give up in favor of the pre-existing decal - if(mergeable_decal) - return TRUE - -//Add "bloodiness" of this blood's type, to the human's shoes -//This is on /cleanable because fuck this ancient mess -/obj/effect/decal/cleanable/blood/Crossed(atom/movable/O) - ..() - if(!off_floor && ishuman(O)) - var/mob/living/carbon/human/H = O - var/obj/item/organ/external/l_foot = H.get_organ("l_foot") - var/obj/item/organ/external/r_foot = H.get_organ("r_foot") - var/hasfeet = TRUE - if(!l_foot && !r_foot) - hasfeet = FALSE - if(H.shoes && blood_state && bloodiness) - var/obj/item/clothing/shoes/S = H.shoes - var/add_blood = 0 - if(bloodiness >= BLOOD_GAIN_PER_STEP) - add_blood = BLOOD_GAIN_PER_STEP - else - add_blood = bloodiness - bloodiness -= add_blood - S.bloody_shoes[blood_state] = min(MAX_SHOE_BLOODINESS, S.bloody_shoes[blood_state] + add_blood) - if(blood_DNA && blood_DNA.len) - S.add_blood(H.blood_DNA, basecolor) - S.blood_state = blood_state - S.blood_color = basecolor - update_icon() - H.update_inv_shoes() - else if(hasfeet && blood_state && bloodiness)//Or feet - var/add_blood = 0 - if(bloodiness >= BLOOD_GAIN_PER_STEP) - add_blood = BLOOD_GAIN_PER_STEP - else - add_blood = bloodiness - bloodiness -= add_blood - H.bloody_feet[blood_state] = min(MAX_SHOE_BLOODINESS, H.bloody_feet[blood_state] + add_blood) - if(!H.feet_blood_DNA) - H.feet_blood_DNA = list() - H.blood_state = blood_state - H.feet_blood_DNA |= blood_DNA.Copy() - H.feet_blood_color = basecolor - update_icon() - H.update_inv_shoes() - -/obj/effect/decal/cleanable/proc/can_bloodcrawl_in() - return FALSE - -/obj/effect/decal/cleanable/Initialize(mapload) - . = ..() - if(loc && isturf(loc)) - for(var/obj/effect/decal/cleanable/C in loc) - if(C != src && C.type == type && !QDELETED(C)) - if(replace_decal(C)) - qdel(src) - return TRUE - if(random_icon_states && length(src.random_icon_states) > 0) - src.icon_state = pick(src.random_icon_states) - if(smooth) - queue_smooth(src) - queue_smooth_neighbors(src) - -/obj/effect/decal/cleanable/Destroy() - if(smooth) - queue_smooth_neighbors(src) - return ..() \ No newline at end of file +/obj/effect/decal/cleanable + anchored = TRUE + var/list/random_icon_states = list() + var/bloodiness = 0 //0-100, amount of blood in this decal, used for making footprints and affecting the alpha of bloody footprints + var/mergeable_decal = TRUE //when two of these are on a same tile or do we need to merge them into just one? + +/obj/effect/decal/cleanable/proc/replace_decal(obj/effect/decal/cleanable/C) // Returns true if we should give up in favor of the pre-existing decal + if(mergeable_decal) + return TRUE + +//Add "bloodiness" of this blood's type, to the human's shoes +//This is on /cleanable because fuck this ancient mess +/obj/effect/decal/cleanable/blood/Crossed(atom/movable/O) + ..() + if(!off_floor && ishuman(O)) + var/mob/living/carbon/human/H = O + var/obj/item/organ/external/l_foot = H.get_organ("l_foot") + var/obj/item/organ/external/r_foot = H.get_organ("r_foot") + var/hasfeet = TRUE + if(!l_foot && !r_foot) + hasfeet = FALSE + if(H.shoes && blood_state && bloodiness) + var/obj/item/clothing/shoes/S = H.shoes + var/add_blood = 0 + if(bloodiness >= BLOOD_GAIN_PER_STEP) + add_blood = BLOOD_GAIN_PER_STEP + else + add_blood = bloodiness + bloodiness -= add_blood + S.bloody_shoes[blood_state] = min(MAX_SHOE_BLOODINESS, S.bloody_shoes[blood_state] + add_blood) + if(blood_DNA && blood_DNA.len) + S.add_blood(H.blood_DNA, basecolor) + S.blood_state = blood_state + S.blood_color = basecolor + update_icon() + H.update_inv_shoes() + else if(hasfeet && blood_state && bloodiness)//Or feet + var/add_blood = 0 + if(bloodiness >= BLOOD_GAIN_PER_STEP) + add_blood = BLOOD_GAIN_PER_STEP + else + add_blood = bloodiness + bloodiness -= add_blood + H.bloody_feet[blood_state] = min(MAX_SHOE_BLOODINESS, H.bloody_feet[blood_state] + add_blood) + if(!H.feet_blood_DNA) + H.feet_blood_DNA = list() + H.blood_state = blood_state + H.feet_blood_DNA |= blood_DNA.Copy() + H.feet_blood_color = basecolor + update_icon() + H.update_inv_shoes() + +/obj/effect/decal/cleanable/proc/can_bloodcrawl_in() + return FALSE + +/obj/effect/decal/cleanable/Initialize(mapload) + . = ..() + if(loc && isturf(loc)) + for(var/obj/effect/decal/cleanable/C in loc) + if(C != src && C.type == type && !QDELETED(C)) + if(replace_decal(C)) + qdel(src) + return TRUE + if(random_icon_states && length(src.random_icon_states) > 0) + src.icon_state = pick(src.random_icon_states) + if(smooth) + queue_smooth(src) + queue_smooth_neighbors(src) + +/obj/effect/decal/cleanable/Destroy() + if(smooth) + queue_smooth_neighbors(src) + return ..() diff --git a/code/game/objects/effects/decals/crayon.dm b/code/game/objects/effects/decals/crayon.dm index fc735b31ee41..e8666d26d64a 100644 --- a/code/game/objects/effects/decals/crayon.dm +++ b/code/game/objects/effects/decals/crayon.dm @@ -1,19 +1,19 @@ -/obj/effect/decal/cleanable/crayon - name = "rune" - desc = "A rune drawn in crayon." - icon = 'icons/effects/crayondecal.dmi' - icon_state = "rune1" - layer = MID_TURF_LAYER - plane = GAME_PLANE //makes the graffiti visible over a wall. - anchored = TRUE - mergeable_decal = FALSE // Allows crayon drawings to overlap one another. - - -/obj/effect/decal/cleanable/crayon/Initialize(mapload, main = "#FFFFFF", var/type = "rune1", var/e_name = "rune") - . = ..() - - name = e_name - desc = "A [name] drawn in crayon." - - icon_state = type - color = main +/obj/effect/decal/cleanable/crayon + name = "rune" + desc = "A rune drawn in crayon." + icon = 'icons/effects/crayondecal.dmi' + icon_state = "rune1" + layer = MID_TURF_LAYER + plane = GAME_PLANE //makes the graffiti visible over a wall. + anchored = TRUE + mergeable_decal = FALSE // Allows crayon drawings to overlap one another. + + +/obj/effect/decal/cleanable/crayon/Initialize(mapload, main = "#FFFFFF", var/type = "rune1", var/e_name = "rune") + . = ..() + + name = e_name + desc = "A [name] drawn in crayon." + + icon_state = type + color = main diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm index 31ae7ab68de3..ce8e81eb1e4e 100644 --- a/code/game/objects/effects/decals/decal.dm +++ b/code/game/objects/effects/decals/decal.dm @@ -12,4 +12,4 @@ var/turf/T = loc if(!istype(T)) //you know this will happen somehow CRASH("Turf decal initialized in an object/nullspace") - T.AddComponent(/datum/component/decal, icon, icon_state, dir, CLEAN_GOD, color, null, null, alpha) \ No newline at end of file + T.AddComponent(/datum/component/decal, icon, icon_state, dir, CLEAN_GOD, color, null, null, alpha) diff --git a/code/game/objects/effects/decals/misc.dm b/code/game/objects/effects/decals/misc.dm index 425031ae3e56..88663e6c38c6 100644 --- a/code/game/objects/effects/decals/misc.dm +++ b/code/game/objects/effects/decals/misc.dm @@ -1,72 +1,72 @@ -// Used for spray that you spray at walls, tables, hydrovats etc -/obj/effect/decal/spraystill - density = FALSE - anchored = TRUE - layer = 50 - plane = HUD_PLANE - -/obj/effect/decal/chempuff - name = "chemicals" - icon = 'icons/obj/chempuff.dmi' - pass_flags = PASSTABLE | PASSGRILLE - -/obj/effect/decal/chempuff/blob_act(obj/structure/blob/B) - return - -/obj/effect/decal/snow - name = "snow" - density = FALSE - anchored = TRUE - layer = TURF_DECAL_LAYER - icon = 'icons/turf/snow.dmi' - icon_state = "snow" - -/obj/effect/decal/snow/clean/edge - icon_state = "snow_corner" - -/obj/effect/decal/snow/sand/edge - icon_state = "gravsnow_corner" - -/obj/effect/decal/snow/clean/surround - icon_state = "snow_surround" - -/obj/effect/decal/snow/sand/surround - icon_state = "gravsnow_surround" - -/obj/effect/decal/leaves - name = "fall leaves" - density = FALSE - anchored = TRUE - layer = HIGH_TURF_LAYER - icon = 'icons/obj/flora/plants.dmi' - icon_state = "fallleaves" - -/obj/effect/decal/straw - name = "scattered straw" - density = FALSE - anchored = TRUE - layer = HIGH_TURF_LAYER - icon = 'icons/obj/flora/plants.dmi' - icon_state = "strawscattered" - -/obj/effect/decal/straw/medium - icon_state = "strawscattered3" - -/obj/effect/decal/straw/light - icon_state = "strawscattered2" - -/obj/effect/decal/straw/edge - icon_state = "strawscatterededge" - -/obj/effect/decal/ants - name = "space ants" - desc = "A bunch of space ants." - icon = 'icons/goonstation/effects/effects.dmi' - icon_state = "spaceants" - scoop_reagents = list("ants" = 20) - -/obj/effect/decal/ants/Initialize(mapload) - . = ..() - var/scale = (rand(2, 10) / 10) + (rand(0, 5) / 100) - transform = matrix(transform, scale, scale, MATRIX_SCALE) - setDir(pick(NORTH, SOUTH, EAST, WEST)) \ No newline at end of file +// Used for spray that you spray at walls, tables, hydrovats etc +/obj/effect/decal/spraystill + density = FALSE + anchored = TRUE + layer = 50 + plane = HUD_PLANE + +/obj/effect/decal/chempuff + name = "chemicals" + icon = 'icons/obj/chempuff.dmi' + pass_flags = PASSTABLE | PASSGRILLE + +/obj/effect/decal/chempuff/blob_act(obj/structure/blob/B) + return + +/obj/effect/decal/snow + name = "snow" + density = FALSE + anchored = TRUE + layer = TURF_DECAL_LAYER + icon = 'icons/turf/snow.dmi' + icon_state = "snow" + +/obj/effect/decal/snow/clean/edge + icon_state = "snow_corner" + +/obj/effect/decal/snow/sand/edge + icon_state = "gravsnow_corner" + +/obj/effect/decal/snow/clean/surround + icon_state = "snow_surround" + +/obj/effect/decal/snow/sand/surround + icon_state = "gravsnow_surround" + +/obj/effect/decal/leaves + name = "fall leaves" + density = FALSE + anchored = TRUE + layer = HIGH_TURF_LAYER + icon = 'icons/obj/flora/plants.dmi' + icon_state = "fallleaves" + +/obj/effect/decal/straw + name = "scattered straw" + density = FALSE + anchored = TRUE + layer = HIGH_TURF_LAYER + icon = 'icons/obj/flora/plants.dmi' + icon_state = "strawscattered" + +/obj/effect/decal/straw/medium + icon_state = "strawscattered3" + +/obj/effect/decal/straw/light + icon_state = "strawscattered2" + +/obj/effect/decal/straw/edge + icon_state = "strawscatterededge" + +/obj/effect/decal/ants + name = "space ants" + desc = "A bunch of space ants." + icon = 'icons/goonstation/effects/effects.dmi' + icon_state = "spaceants" + scoop_reagents = list("ants" = 20) + +/obj/effect/decal/ants/Initialize(mapload) + . = ..() + var/scale = (rand(2, 10) / 10) + (rand(0, 5) / 100) + transform = matrix(transform, scale, scale, MATRIX_SCALE) + setDir(pick(NORTH, SOUTH, EAST, WEST)) diff --git a/code/game/objects/effects/decals/remains.dm b/code/game/objects/effects/decals/remains.dm index d69b8cad25a1..9a879a02da35 100644 --- a/code/game/objects/effects/decals/remains.dm +++ b/code/game/objects/effects/decals/remains.dm @@ -1,46 +1,46 @@ -/obj/effect/decal/remains - gender = PLURAL - -/obj/effect/decal/remains/acid_act() - visible_message("[src] dissolve[gender==PLURAL?"":"s"] into a puddle of sizzling goop!") - playsound(src, 'sound/items/welder.ogg', 150, TRUE) - new /obj/effect/decal/cleanable/greenglow(drop_location()) - qdel(src) - -/obj/effect/decal/remains/human - name = "remains" - desc = "They look like human remains. They have a strange aura about them." - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - anchored = TRUE - -/obj/effect/decal/remains/xeno - name = "remains" - desc = "They look like the remains of something... alien. They have a strange aura about them." - icon = 'icons/effects/blood.dmi' - icon_state = "remainsxeno" - anchored = TRUE - -/obj/effect/decal/remains/robot - name = "remains" - desc = "They look like the remains of something mechanical. They have a strange aura about them." - icon = 'icons/mob/robots.dmi' - icon_state = "remainsrobot" - anchored = TRUE - -/obj/effect/decal/remains/slime - name = "You shouldn't see this" - desc = "Noooooooooooooooooooooo" - icon = 'icons/effects/blood.dmi' - icon_state = "remains" - anchored = TRUE - -/obj/effect/decal/remains/slime/New() - ..() - var/datum/reagents/R = new/datum/reagents(5) - var/obj/effect/particle_effect/water/W = new(get_turf(src)) - W.reagents = R - R.my_atom = W - R.add_reagent("water", 5) - R.reaction(get_turf(src)) - qdel(src) +/obj/effect/decal/remains + gender = PLURAL + +/obj/effect/decal/remains/acid_act() + visible_message("[src] dissolve[gender==PLURAL?"":"s"] into a puddle of sizzling goop!") + playsound(src, 'sound/items/welder.ogg', 150, TRUE) + new /obj/effect/decal/cleanable/greenglow(drop_location()) + qdel(src) + +/obj/effect/decal/remains/human + name = "remains" + desc = "They look like human remains. They have a strange aura about them." + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + anchored = TRUE + +/obj/effect/decal/remains/xeno + name = "remains" + desc = "They look like the remains of something... alien. They have a strange aura about them." + icon = 'icons/effects/blood.dmi' + icon_state = "remainsxeno" + anchored = TRUE + +/obj/effect/decal/remains/robot + name = "remains" + desc = "They look like the remains of something mechanical. They have a strange aura about them." + icon = 'icons/mob/robots.dmi' + icon_state = "remainsrobot" + anchored = TRUE + +/obj/effect/decal/remains/slime + name = "You shouldn't see this" + desc = "Noooooooooooooooooooooo" + icon = 'icons/effects/blood.dmi' + icon_state = "remains" + anchored = TRUE + +/obj/effect/decal/remains/slime/New() + ..() + var/datum/reagents/R = new/datum/reagents(5) + var/obj/effect/particle_effect/water/W = new(get_turf(src)) + W.reagents = R + R.my_atom = W + R.add_reagent("water", 5) + R.reaction(get_turf(src)) + qdel(src) diff --git a/code/game/objects/effects/decals/turfdecals/dirt.dm b/code/game/objects/effects/decals/turfdecals/dirt.dm index a2a1e8c64553..156c04d1ba36 100644 --- a/code/game/objects/effects/decals/turfdecals/dirt.dm +++ b/code/game/objects/effects/decals/turfdecals/dirt.dm @@ -2,4 +2,4 @@ icon_state = "sandyfloor" /obj/effect/turf_decal/sand/plating - icon_state = "sandyplating" \ No newline at end of file + icon_state = "sandyplating" diff --git a/code/game/objects/effects/decals/turfdecals/markings.dm b/code/game/objects/effects/decals/turfdecals/markings.dm index 8687967e1d1f..196d22b580cb 100644 --- a/code/game/objects/effects/decals/turfdecals/markings.dm +++ b/code/game/objects/effects/decals/turfdecals/markings.dm @@ -149,4 +149,4 @@ icon_state = "box_corners_red" /obj/effect/turf_decal/plaque - icon_state = "plaque" \ No newline at end of file + icon_state = "plaque" diff --git a/code/game/objects/effects/decals/turfdecals/tilecoloring.dm b/code/game/objects/effects/decals/turfdecals/tilecoloring.dm index 1708753dc1e4..b636ede55e5a 100644 --- a/code/game/objects/effects/decals/turfdecals/tilecoloring.dm +++ b/code/game/objects/effects/decals/turfdecals/tilecoloring.dm @@ -36,4 +36,4 @@ /obj/effect/turf_decal/tile/neutral name = "neutral corner" color = "#D4D4D4" - alpha = 50 \ No newline at end of file + alpha = 50 diff --git a/code/game/objects/effects/decals/turfdecals/weather.dm b/code/game/objects/effects/decals/turfdecals/weather.dm index 9e8da6a3f893..f179487944af 100644 --- a/code/game/objects/effects/decals/turfdecals/weather.dm +++ b/code/game/objects/effects/decals/turfdecals/weather.dm @@ -9,4 +9,4 @@ /obj/effect/turf_decal/weather/snow/corner name = "snow corner piece" icon = 'icons/turf/snow.dmi' - icon_state = "snow_corner" \ No newline at end of file + icon_state = "snow_corner" diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 7301eb2df039..9907e903970b 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -118,10 +118,10 @@ would spawn and follow the beaker, even if it is carried or thrown. // will always spawn at the items location, even if it's moved. /* Example: -var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system -steam.set_up(5, 0, mob.loc) -- sets up variables -OPTIONAL: steam.attach(mob) -steam.start() -- spawns the effect + var/datum/effect/system/steam_spread/steam = new /datum/effect/system/steam_spread() -- creates new system + steam.set_up(5, 0, mob.loc) -- sets up variables + OPTIONAL: steam.attach(mob) + steam.start() -- spawns the effect */ ///////////////////////////////////////////// /obj/effect/effect/steam diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm index 6792f0aa6e21..5ae6b0c6860b 100644 --- a/code/game/objects/effects/effect_system/effect_system.dm +++ b/code/game/objects/effects/effect_system/effect_system.dm @@ -14,11 +14,11 @@ would spawn and follow the beaker, even if it is carried or thrown. /obj/effect/particle_effect/New() ..() if(SSticker) - cameranet.updateVisibility(src) + GLOB.cameranet.updateVisibility(src) /obj/effect/particle_effect/Destroy() if(SSticker) - cameranet.updateVisibility(src) + GLOB.cameranet.updateVisibility(src) return ..() /datum/effect_system @@ -61,9 +61,9 @@ would spawn and follow the beaker, even if it is carried or thrown. total_effects++ var/direction if(cardinals) - direction = pick(cardinal) + direction = pick(GLOB.cardinal) else - direction = pick(alldirs) + direction = pick(GLOB.alldirs) var/steps_amt = pick(1,2,3) for(var/j in 1 to steps_amt) sleep(5) @@ -73,4 +73,4 @@ would spawn and follow the beaker, even if it is carried or thrown. /datum/effect_system/proc/decrement_total_effect() total_effects-- if(autocleanup && total_effects <= 0) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/effects/effect_system/effects_chem_smoke.dm b/code/game/objects/effects/effect_system/effects_chem_smoke.dm index f55a64aac8de..8c28c7f11c5a 100644 --- a/code/game/objects/effects/effect_system/effects_chem_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_chem_smoke.dm @@ -123,4 +123,4 @@ if(iscarbon(A)) var/mob/living/carbon/C = A if(C.can_breathe_gas()) - chemholder.reagents.copy_to(C, chemholder.reagents.total_volume) \ No newline at end of file + chemholder.reagents.copy_to(C, chemholder.reagents.total_volume) diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm index 1080f975135d..40fda5130994 100644 --- a/code/game/objects/effects/effect_system/effects_explosion.dm +++ b/code/game/objects/effects/effect_system/effects_explosion.dm @@ -15,7 +15,7 @@ for(var/i in 1 to number) spawn(0) var/obj/effect/particle_effect/expl_particles/expl = new /obj/effect/particle_effect/expl_particles(location) - var/direct = pick(alldirs) + var/direct = pick(GLOB.alldirs) var/steps_amt = pick(1;25,2;50,3,4;200) for(var/j in 1 to steps_amt) sleep(1) diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm index 65daf2c1c65c..8ee47d266e89 100644 --- a/code/game/objects/effects/effect_system/effects_foam.dm +++ b/code/game/objects/effects/effect_system/effects_foam.dm @@ -60,7 +60,7 @@ if(--amount < 0) return - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) var/turf/T = get_step(src,direction) if(!T) @@ -218,4 +218,4 @@ return !density /obj/structure/foamedmetal/CanAtmosPass() - return !density \ No newline at end of file + return !density diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm index cec70ad90e6a..ddd85f6027db 100644 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ b/code/game/objects/effects/effect_system/effects_other.dm @@ -172,13 +172,13 @@ // Clamp all values to MAX_EXPLOSION_RANGE if(round(amount/12) > 0) - devastation = min (MAX_EX_DEVASTATION_RANGE, devastation + round(amount/12)) + devastation = min (GLOB.max_ex_devastation_range, devastation + round(amount/12)) if(round(amount/6) > 0) - heavy = min (MAX_EX_HEAVY_RANGE, heavy + round(amount/6)) + heavy = min (GLOB.max_ex_heavy_range, heavy + round(amount/6)) if(round(amount/3) > 0) - light = min (MAX_EX_LIGHT_RANGE, light + round(amount/3)) + light = min (GLOB.max_ex_light_range, light + round(amount/3)) if(flashing && flashing_factor) flash += (round(amount/4) * flashing_factor) diff --git a/code/game/objects/effects/effect_system/effects_smoke.dm b/code/game/objects/effects/effect_system/effects_smoke.dm index 93ab0c8e51bb..a8d442bbbb43 100644 --- a/code/game/objects/effects/effect_system/effects_smoke.dm +++ b/code/game/objects/effects/effect_system/effects_smoke.dm @@ -97,9 +97,9 @@ var/obj/effect/particle_effect/smoke/S = new effect_type(location) if(!direction) if(cardinals) - S.direction = pick(cardinal) + S.direction = pick(GLOB.cardinal) else - S.direction = pick(alldirs) + S.direction = pick(GLOB.alldirs) else S.direction = direction S.steps = pick(0,1,1,1,2,2,2,3) diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm index 12fc9732bb5c..4e3b5863dd58 100644 --- a/code/game/objects/effects/effect_system/effects_water.dm +++ b/code/game/objects/effects/effect_system/effects_water.dm @@ -33,10 +33,10 @@ // will always spawn at the items location, even if it's moved. /* Example: -var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() -- creates new system -steam.set_up(5, 0, mob.loc) -- sets up variables -OPTIONAL: steam.attach(mob) -steam.start() -- spawns the effect + var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() -- creates new system + steam.set_up(5, 0, mob.loc) -- sets up variables + OPTIONAL: steam.attach(mob) + steam.start() -- spawns the effect */ ///////////////////////////////////////////// /obj/effect/particle_effect/steam diff --git a/code/game/objects/effects/effects.dm b/code/game/objects/effects/effects.dm index 338219da1aef..7497b148788f 100644 --- a/code/game/objects/effects/effects.dm +++ b/code/game/objects/effects/effects.dm @@ -89,4 +89,4 @@ /obj/effect/decal/blob_act(obj/structure/blob/B) if(B && B.loc == loc) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm index 848c6701434e..366324c68ba6 100644 --- a/code/game/objects/effects/forcefields.dm +++ b/code/game/objects/effects/forcefields.dm @@ -38,4 +38,4 @@ /obj/effect/forcefield/mime/advanced name = "invisible blockade" desc = "You might be here a while." - lifetime = 60 SECONDS \ No newline at end of file + lifetime = 60 SECONDS diff --git a/code/game/objects/effects/gibs.dm b/code/game/objects/effects/gibs.dm index b3eedbd08cc8..5503ac91dd1d 100644 --- a/code/game/objects/effects/gibs.dm +++ b/code/game/objects/effects/gibs.dm @@ -1,53 +1,53 @@ -/proc/gibs(atom/location, datum/dna/MobDNA) //CARN MARKER - new /obj/effect/gibspawner/generic(get_turf(location), MobDNA) - -/proc/hgibs(atom/location, datum/dna/MobDNA) - new /obj/effect/gibspawner/human(get_turf(location), MobDNA) - -/proc/xgibs(atom/location) - new /obj/effect/gibspawner/xeno(get_turf(location)) - -/proc/robogibs(atom/location) - new /obj/effect/gibspawner/robot(get_turf(location)) - -/obj/effect/gibspawner - var/sparks = 0 //whether sparks spread on Gib() - var/list/gibtypes = list() - var/list/gibamounts = list() - var/list/gibdirections = list() //of lists - -/obj/effect/gibspawner/New(location, datum/dna/MobDNA) - ..() - - if(istype(loc,/turf)) //basically if a badmin spawns it - Gib(loc, MobDNA) - -/obj/effect/gibspawner/proc/Gib(atom/location, datum/dna/MobDNA = null) - if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len) - to_chat(world, "Gib list length mismatch!") - return - - var/obj/effect/decal/cleanable/blood/gibs/gib = null - - if(sparks) - do_sparks(2, 1, location) - - for(var/i = 1, i<= gibtypes.len, i++) - if(gibamounts[i]) - for(var/j = 1, j<= gibamounts[i], j++) - var/gibType = gibtypes[i] - gib = new gibType(location) - - - gib.blood_DNA = list() - if(MobDNA) - gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.blood_type - else if(istype(src, /obj/effect/gibspawner/xeno)) - gib.blood_DNA["UNKNOWN DNA"] = "X*" - else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey - gib.blood_DNA["Non-human DNA"] = "A+" - var/list/directions = gibdirections[i] - if(directions.len) - gib.streak(directions) - - qdel(src) +/proc/gibs(atom/location, datum/dna/MobDNA) //CARN MARKER + new /obj/effect/gibspawner/generic(get_turf(location), MobDNA) + +/proc/hgibs(atom/location, datum/dna/MobDNA) + new /obj/effect/gibspawner/human(get_turf(location), MobDNA) + +/proc/xgibs(atom/location) + new /obj/effect/gibspawner/xeno(get_turf(location)) + +/proc/robogibs(atom/location) + new /obj/effect/gibspawner/robot(get_turf(location)) + +/obj/effect/gibspawner + var/sparks = 0 //whether sparks spread on Gib() + var/list/gibtypes = list() + var/list/gibamounts = list() + var/list/gibdirections = list() //of lists + +/obj/effect/gibspawner/New(location, datum/dna/MobDNA) + ..() + + if(istype(loc,/turf)) //basically if a badmin spawns it + Gib(loc, MobDNA) + +/obj/effect/gibspawner/proc/Gib(atom/location, datum/dna/MobDNA = null) + if(gibtypes.len != gibamounts.len || gibamounts.len != gibdirections.len) + to_chat(world, "Gib list length mismatch!") + return + + var/obj/effect/decal/cleanable/blood/gibs/gib = null + + if(sparks) + do_sparks(2, 1, location) + + for(var/i = 1, i<= gibtypes.len, i++) + if(gibamounts[i]) + for(var/j = 1, j<= gibamounts[i], j++) + var/gibType = gibtypes[i] + gib = new gibType(location) + + + gib.blood_DNA = list() + if(MobDNA) + gib.blood_DNA[MobDNA.unique_enzymes] = MobDNA.blood_type + else if(istype(src, /obj/effect/gibspawner/xeno)) + gib.blood_DNA["UNKNOWN DNA"] = "X*" + else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey + gib.blood_DNA["Non-human DNA"] = "A+" + var/list/directions = gibdirections[i] + if(directions.len) + gib.streak(directions) + + qdel(src) diff --git a/code/game/objects/effects/glowshroom.dm b/code/game/objects/effects/glowshroom.dm index 0fe284fb67e2..b520648a599e 100644 --- a/code/game/objects/effects/glowshroom.dm +++ b/code/game/objects/effects/glowshroom.dm @@ -1,169 +1,169 @@ -//separate dm since hydro is getting bloated already - -/obj/structure/glowshroom - name = "glowshroom" - desc = "Mycena Bregprox, a species of mushroom that glows in the dark." - anchored = TRUE - opacity = 0 - density = FALSE - icon = 'icons/obj/lighting.dmi' - icon_state = "glowshroom" //replaced in New - layer = ABOVE_NORMAL_TURF_LAYER - max_integrity = 30 - var/delay = 1200 - var/floor = 0 - var/generation = 1 - var/spreadIntoAdjacentChance = 60 - var/obj/item/seeds/myseed = /obj/item/seeds/glowshroom - -/obj/structure/glowshroom/glowcap - name = "glowcap" - desc = "Mycena Ruthenia, a species of mushroom that, while it does glow in the dark, is not actually bioluminescent." - icon_state = "glowcap" - myseed = /obj/item/seeds/glowshroom/glowcap - -/obj/structure/glowshroom/shadowshroom - name = "shadowshroom" - desc = "Mycena Umbra, a species of mushroom that emits shadow instead of light." - icon_state = "shadowshroom" - myseed = /obj/item/seeds/glowshroom/shadowshroom - -/obj/structure/glowshroom/single/Spread() - return - -/obj/structure/glowshroom/examine(mob/user) - . = ..() - . += "This is a [generation]\th generation [name]!" - -/obj/structure/glowshroom/Destroy() - QDEL_NULL(myseed) - return ..() - -/obj/structure/glowshroom/New(loc, obj/item/seeds/newseed, mutate_stats) - ..() - if(newseed) - myseed = newseed.Copy() - myseed.forceMove(src) - else - myseed = new myseed(src) - if(mutate_stats) //baby mushrooms have different stats :3 - myseed.adjust_potency(rand(-3,6)) - myseed.adjust_yield(rand(-1,2)) - myseed.adjust_production(rand(-3,6)) - myseed.adjust_endurance(rand(-3,6)) - delay = delay - myseed.production * 100 //So the delay goes DOWN with better stats instead of up. :I - obj_integrity = myseed.endurance - max_integrity = myseed.endurance - if(myseed.get_gene(/datum/plant_gene/trait/glow)) - var/datum/plant_gene/trait/glow/G = myseed.get_gene(/datum/plant_gene/trait/glow) - set_light(G.glow_range(myseed), G.glow_power(myseed), G.glow_color) - setDir(CalcDir()) - var/base_icon_state = initial(icon_state) - if(!floor) - switch(dir) //offset to make it be on the wall rather than on the floor - if(NORTH) - pixel_y = 32 - if(SOUTH) - pixel_y = -32 - if(EAST) - pixel_x = 32 - if(WEST) - pixel_x = -32 - icon_state = "[base_icon_state][rand(1,3)]" - else //if on the floor, glowshroom on-floor sprite - icon_state = "[base_icon_state]f" - - addtimer(CALLBACK(src, .proc/Spread), delay) - -/obj/structure/glowshroom/proc/Spread() - var/turf/ownturf = get_turf(src) - var/shrooms_planted = 0 - for(var/i in 1 to myseed.yield) - if(prob(1/(generation * generation) * 100))//This formula gives you diminishing returns based on generation. 100% with 1st gen, decreasing to 25%, 11%, 6, 4, 2... - var/list/possibleLocs = list() - var/spreadsIntoAdjacent = FALSE - - if(prob(spreadIntoAdjacentChance)) - spreadsIntoAdjacent = TRUE - - for(var/turf/simulated/floor/earth in view(3,src)) - if(!ownturf.CanAtmosPass(earth)) - continue - if(spreadsIntoAdjacent || !locate(/obj/structure/glowshroom) in view(1,earth)) - possibleLocs += earth - CHECK_TICK - - if(!possibleLocs.len) - break - - var/turf/newLoc = pick(possibleLocs) - - var/shroomCount = 0 //hacky - var/placeCount = 1 - for(var/obj/structure/glowshroom/shroom in newLoc) - shroomCount++ - for(var/wallDir in cardinal) - var/turf/isWall = get_step(newLoc,wallDir) - if(isWall.density) - placeCount++ - if(shroomCount >= placeCount) - continue - - var/obj/structure/glowshroom/child = new type(newLoc, myseed, TRUE) - child.generation = generation + 1 - shrooms_planted++ - - CHECK_TICK - else - shrooms_planted++ //if we failed due to generation, don't try to plant one later - if(shrooms_planted < myseed.yield) //if we didn't get all possible shrooms planted, try again later - myseed.yield -= shrooms_planted - addtimer(CALLBACK(src, .proc/Spread), delay) - -/obj/structure/glowshroom/proc/CalcDir(turf/location = loc) - var/direction = 16 - - for(var/wallDir in cardinal) - var/turf/newTurf = get_step(location,wallDir) - if(newTurf.density) - direction |= wallDir - - for(var/obj/structure/glowshroom/shroom in location) - if(shroom == src) - continue - if(shroom.floor) //special - direction &= ~16 - else - direction &= ~shroom.dir - - var/list/dirList = list() - - for(var/i=1,i<=16,i <<= 1) - if(direction & i) - dirList += i - - if(dirList.len) - var/newDir = pick(dirList) - if(newDir == 16) - floor = 1 - newDir = 1 - return newDir - - floor = 1 - return 1 - -/obj/structure/glowshroom/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - if(damage_type == BURN && damage_amount) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/glowshroom/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 300) - take_damage(5, BURN, 0, 0) - -/obj/structure/glowshroom/acid_act(acidpwr, acid_volume) - . = 1 - visible_message("[src] melts away!") - var/obj/effect/decal/cleanable/molten_object/I = new (get_turf(src)) - I.desc = "Looks like this was \an [src] some time ago." - qdel(src) \ No newline at end of file +//separate dm since hydro is getting bloated already + +/obj/structure/glowshroom + name = "glowshroom" + desc = "Mycena Bregprox, a species of mushroom that glows in the dark." + anchored = TRUE + opacity = 0 + density = FALSE + icon = 'icons/obj/lighting.dmi' + icon_state = "glowshroom" //replaced in New + layer = ABOVE_NORMAL_TURF_LAYER + max_integrity = 30 + var/delay = 1200 + var/floor = 0 + var/generation = 1 + var/spreadIntoAdjacentChance = 60 + var/obj/item/seeds/myseed = /obj/item/seeds/glowshroom + +/obj/structure/glowshroom/glowcap + name = "glowcap" + desc = "Mycena Ruthenia, a species of mushroom that, while it does glow in the dark, is not actually bioluminescent." + icon_state = "glowcap" + myseed = /obj/item/seeds/glowshroom/glowcap + +/obj/structure/glowshroom/shadowshroom + name = "shadowshroom" + desc = "Mycena Umbra, a species of mushroom that emits shadow instead of light." + icon_state = "shadowshroom" + myseed = /obj/item/seeds/glowshroom/shadowshroom + +/obj/structure/glowshroom/single/Spread() + return + +/obj/structure/glowshroom/examine(mob/user) + . = ..() + . += "This is a [generation]\th generation [name]!" + +/obj/structure/glowshroom/Destroy() + QDEL_NULL(myseed) + return ..() + +/obj/structure/glowshroom/New(loc, obj/item/seeds/newseed, mutate_stats) + ..() + if(newseed) + myseed = newseed.Copy() + myseed.forceMove(src) + else + myseed = new myseed(src) + if(mutate_stats) //baby mushrooms have different stats :3 + myseed.adjust_potency(rand(-3,6)) + myseed.adjust_yield(rand(-1,2)) + myseed.adjust_production(rand(-3,6)) + myseed.adjust_endurance(rand(-3,6)) + delay = delay - myseed.production * 100 //So the delay goes DOWN with better stats instead of up. :I + obj_integrity = myseed.endurance + max_integrity = myseed.endurance + if(myseed.get_gene(/datum/plant_gene/trait/glow)) + var/datum/plant_gene/trait/glow/G = myseed.get_gene(/datum/plant_gene/trait/glow) + set_light(G.glow_range(myseed), G.glow_power(myseed), G.glow_color) + setDir(CalcDir()) + var/base_icon_state = initial(icon_state) + if(!floor) + switch(dir) //offset to make it be on the wall rather than on the floor + if(NORTH) + pixel_y = 32 + if(SOUTH) + pixel_y = -32 + if(EAST) + pixel_x = 32 + if(WEST) + pixel_x = -32 + icon_state = "[base_icon_state][rand(1,3)]" + else //if on the floor, glowshroom on-floor sprite + icon_state = "[base_icon_state]f" + + addtimer(CALLBACK(src, .proc/Spread), delay) + +/obj/structure/glowshroom/proc/Spread() + var/turf/ownturf = get_turf(src) + var/shrooms_planted = 0 + for(var/i in 1 to myseed.yield) + if(prob(1/(generation * generation) * 100))//This formula gives you diminishing returns based on generation. 100% with 1st gen, decreasing to 25%, 11%, 6, 4, 2... + var/list/possibleLocs = list() + var/spreadsIntoAdjacent = FALSE + + if(prob(spreadIntoAdjacentChance)) + spreadsIntoAdjacent = TRUE + + for(var/turf/simulated/floor/earth in view(3,src)) + if(!ownturf.CanAtmosPass(earth)) + continue + if(spreadsIntoAdjacent || !locate(/obj/structure/glowshroom) in view(1,earth)) + possibleLocs += earth + CHECK_TICK + + if(!possibleLocs.len) + break + + var/turf/newLoc = pick(possibleLocs) + + var/shroomCount = 0 //hacky + var/placeCount = 1 + for(var/obj/structure/glowshroom/shroom in newLoc) + shroomCount++ + for(var/wallDir in GLOB.cardinal) + var/turf/isWall = get_step(newLoc,wallDir) + if(isWall.density) + placeCount++ + if(shroomCount >= placeCount) + continue + + var/obj/structure/glowshroom/child = new type(newLoc, myseed, TRUE) + child.generation = generation + 1 + shrooms_planted++ + + CHECK_TICK + else + shrooms_planted++ //if we failed due to generation, don't try to plant one later + if(shrooms_planted < myseed.yield) //if we didn't get all possible shrooms planted, try again later + myseed.yield -= shrooms_planted + addtimer(CALLBACK(src, .proc/Spread), delay) + +/obj/structure/glowshroom/proc/CalcDir(turf/location = loc) + var/direction = 16 + + for(var/wallDir in GLOB.cardinal) + var/turf/newTurf = get_step(location,wallDir) + if(newTurf.density) + direction |= wallDir + + for(var/obj/structure/glowshroom/shroom in location) + if(shroom == src) + continue + if(shroom.floor) //special + direction &= ~16 + else + direction &= ~shroom.dir + + var/list/dirList = list() + + for(var/i=1,i<=16,i <<= 1) + if(direction & i) + dirList += i + + if(dirList.len) + var/newDir = pick(dirList) + if(newDir == 16) + floor = 1 + newDir = 1 + return newDir + + floor = 1 + return 1 + +/obj/structure/glowshroom/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + if(damage_type == BURN && damage_amount) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/glowshroom/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 300) + take_damage(5, BURN, 0, 0) + +/obj/structure/glowshroom/acid_act(acidpwr, acid_volume) + . = 1 + visible_message("[src] melts away!") + var/obj/effect/decal/cleanable/molten_object/I = new (get_turf(src)) + I.desc = "Looks like this was \an [src] some time ago." + qdel(src) diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 0cab543c5a7f..24bd5ce6e584 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -1,306 +1,306 @@ -/obj/effect/landmark - name = "landmark" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x2" - anchored = 1.0 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/effect/landmark/New() - - ..() - set_tag() - invisibility = 101 - - switch(name) //some of these are probably obsolete - if("start") - newplayer_start += loc - qdel(src) - - if("wizard") - wizardstart += loc - qdel(src) - - if("JoinLate") - latejoin += loc - qdel(src) - - if("JoinLateGateway") - latejoin_gateway += loc - qdel(src) - - if("JoinLateCryo") - latejoin_cryo += loc - qdel(src) - - if("JoinLateCyborg") - latejoin_cyborg += loc - qdel(src) - - if("prisonwarp") - prisonwarp += loc - qdel(src) - - if("prisonsecuritywarp") - prisonsecuritywarp += loc - qdel(src) - - if("tdome1") - tdome1 += loc - - if("tdome2") - tdome2 += loc - - if("tdomeadmin") - tdomeadmin += loc - - if("tdomeobserve") - tdomeobserve += loc - - if("aroomwarp") - aroomwarp += loc - - if("blobstart") - blobstart += loc - qdel(src) - - if("xeno_spawn") - xeno_spawn += loc - qdel(src) - - if("ninjastart") - ninjastart += loc - qdel(src) - - if("carpspawn") - carplist += loc - - if("voxstart") - raider_spawn += loc - - if("ERT Director") - ertdirector += loc - qdel(src) - - if("Response Team") - emergencyresponseteamspawn += loc - qdel(src) - - if("Syndicate Officer") - syndicateofficer += loc - qdel(src) - - GLOB.landmarks_list += src - return 1 - -/obj/effect/landmark/Destroy() - GLOB.landmarks_list -= src - ..() - return QDEL_HINT_HARDDEL_NOW - -/obj/effect/landmark/proc/set_tag() - tag = text("landmark*[]", name) - - -/obj/effect/landmark/singularity_act() - return - -// Please stop bombing the Observer-Start landmark. -/obj/effect/landmark/ex_act() - return - -/obj/effect/landmark/singularity_pull() - return - -/obj/effect/landmark/start - name = "start" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x" - anchored = 1.0 - -/obj/effect/landmark/start/set_tag() - tag = "start*[name]" - - -//Costume spawner landmarks - -/obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears - - var/list/options = typesof(/obj/effect/landmark/costume) - var/PICK= options[rand(1,options.len)] - new PICK(src.loc) - qdel(src) - -//SUBCLASSES. Spawn a bunch of items and disappear likewise -/obj/effect/landmark/costume/chicken/New() - new /obj/item/clothing/suit/chickensuit(src.loc) - new /obj/item/clothing/head/chicken(src.loc) - new /obj/item/reagent_containers/food/snacks/egg(src.loc) - qdel(src) - -/obj/effect/landmark/costume/gladiator/New() - new /obj/item/clothing/under/gladiator(src.loc) - new /obj/item/clothing/head/helmet/gladiator(src.loc) - qdel(src) - -/obj/effect/landmark/costume/madscientist/New() - new /obj/item/clothing/under/gimmick/rank/captain/suit(src.loc) - new /obj/item/clothing/head/flatcap(src.loc) - new /obj/item/clothing/suit/storage/labcoat/mad(src.loc) - new /obj/item/clothing/glasses/gglasses(src.loc) - qdel(src) - -/obj/effect/landmark/costume/elpresidente/New() - new /obj/item/clothing/under/gimmick/rank/captain/suit(src.loc) - new /obj/item/clothing/head/flatcap(src.loc) - new /obj/item/clothing/mask/cigarette/cigar/havana(src.loc) - new /obj/item/clothing/shoes/jackboots(src.loc) - qdel(src) - -/obj/effect/landmark/costume/nyangirl/New() - new /obj/item/clothing/under/schoolgirl(src.loc) - new /obj/item/clothing/head/kitty(src.loc) - qdel(src) - -/obj/effect/landmark/costume/maid/New() - new /obj/item/clothing/under/blackskirt(src.loc) - var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears ) - new CHOICE(src.loc) - new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc) - qdel(src) - -/obj/effect/landmark/costume/butler/New() - new /obj/item/clothing/suit/wcoat(src.loc) - new /obj/item/clothing/under/suit_jacket(src.loc) - new /obj/item/clothing/head/that(src.loc) - qdel(src) - -/obj/effect/landmark/costume/scratch/New() - new /obj/item/clothing/gloves/color/white(src.loc) - new /obj/item/clothing/shoes/white(src.loc) - new /obj/item/clothing/under/scratch(src.loc) - if(prob(30)) - new /obj/item/clothing/head/cueball(src.loc) - qdel(src) - -/obj/effect/landmark/costume/highlander/New() - new /obj/item/clothing/under/kilt(src.loc) - new /obj/item/clothing/head/beret(src.loc) - qdel(src) - -/obj/effect/landmark/costume/prig/New() - new /obj/item/clothing/suit/wcoat(src.loc) - new /obj/item/clothing/glasses/monocle(src.loc) - var/CHOICE= pick( /obj/item/clothing/head/bowlerhat, /obj/item/clothing/head/that) - new CHOICE(src.loc) - new /obj/item/clothing/shoes/black(src.loc) - new /obj/item/cane(src.loc) - new /obj/item/clothing/under/sl_suit(src.loc) - new /obj/item/clothing/mask/fakemoustache(src.loc) - qdel(src) - -/obj/effect/landmark/costume/plaguedoctor/New() - new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc) - new /obj/item/clothing/head/plaguedoctorhat(src.loc) - qdel(src) - -/obj/effect/landmark/costume/nightowl/New() - new /obj/item/clothing/under/owl(src.loc) - new /obj/item/clothing/mask/gas/owl_mask(src.loc) - qdel(src) - -/obj/effect/landmark/costume/waiter/New() - new /obj/item/clothing/under/waiter(src.loc) - var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears) - new CHOICE(src.loc) - new /obj/item/clothing/suit/apron(src.loc) - qdel(src) - -/obj/effect/landmark/costume/pirate/New() - new /obj/item/clothing/under/pirate(src.loc) - new /obj/item/clothing/suit/pirate_black(src.loc) - var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana ) - new CHOICE(src.loc) - new /obj/item/clothing/glasses/eyepatch(src.loc) - qdel(src) - -/obj/effect/landmark/costume/commie/New() - new /obj/item/clothing/under/soviet(src.loc) - new /obj/item/clothing/head/ushanka(src.loc) - qdel(src) - - -/obj/effect/landmark/costume/imperium_monk/New() - new /obj/item/clothing/suit/imperium_monk(src.loc) - if(prob(25)) - new /obj/item/clothing/mask/gas/cyborg(src.loc) - qdel(src) - -/obj/effect/landmark/costume/holiday_priest/New() - new /obj/item/clothing/suit/holidaypriest(src.loc) - qdel(src) - -/obj/effect/landmark/costume/marisawizard/fake/New() - new /obj/item/clothing/head/wizard/marisa/fake(src.loc) - new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc) - qdel(src) - -/obj/effect/landmark/costume/cutewitch/New() - new /obj/item/clothing/under/sundress(src.loc) - new /obj/item/clothing/head/witchwig(src.loc) - new /obj/item/twohanded/staff/broom(src.loc) - qdel(src) - -/obj/effect/landmark/costume/fakewizard/New() - new /obj/item/clothing/suit/wizrobe/fake(src.loc) - new /obj/item/clothing/head/wizard/fake(src.loc) - new /obj/item/twohanded/staff/(src.loc) - qdel(src) - -/obj/effect/landmark/costume/sexyclown/New() - new /obj/item/clothing/mask/gas/clown_hat/sexy(loc) - new /obj/item/clothing/under/rank/clown/sexy(loc) - qdel(src) - -/obj/effect/landmark/costume/sexymime/New() - new /obj/item/clothing/mask/gas/sexymime(src.loc) - new /obj/item/clothing/under/sexymime(src.loc) - qdel(src) - -/obj/effect/landmark/ruin - var/datum/map_template/ruin/ruin_template - -/obj/effect/landmark/ruin/New(loc, my_ruin_template) - name = "ruin_[GLOB.ruin_landmarks.len + 1]" - ..(loc) - ruin_template = my_ruin_template - GLOB.ruin_landmarks |= src - -/obj/effect/landmark/ruin/Destroy() - GLOB.ruin_landmarks -= src - ruin_template = null - . = ..() - -// Damage tiles -/obj/effect/landmark/damageturf - icon_state = "damaged" - -/obj/effect/landmark/damageturf/New() - ..() - var/turf/simulated/T = get_turf(src) - if(istype(T)) - T.break_tile() - -/obj/effect/landmark/burnturf - icon_state = "burned" - -/obj/effect/landmark/burnturf/New() - ..() - var/turf/simulated/T = get_turf(src) - T.burn_tile() - - -/obj/effect/landmark/battle_mob_point - name = "Nanomob Battle Avatar Spawn Point" - -/obj/effect/landmark/free_golem_spawn - name = "Free Golem Spawn Point" \ No newline at end of file +/obj/effect/landmark + name = "landmark" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x2" + anchored = 1.0 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/effect/landmark/New() + + ..() + set_tag() + invisibility = 101 + + switch(name) //some of these are probably obsolete + if("start") + GLOB.newplayer_start += loc + qdel(src) + + if("wizard") + GLOB.wizardstart += loc + qdel(src) + + if("JoinLate") + GLOB.latejoin += loc + qdel(src) + + if("JoinLateGateway") + GLOB.latejoin_gateway += loc + qdel(src) + + if("JoinLateCryo") + GLOB.latejoin_cryo += loc + qdel(src) + + if("JoinLateCyborg") + GLOB.latejoin_cyborg += loc + qdel(src) + + if("prisonwarp") + GLOB.prisonwarp += loc + qdel(src) + + if("prisonsecuritywarp") + GLOB.prisonsecuritywarp += loc + qdel(src) + + if("tdome1") + GLOB.tdome1 += loc + + if("tdome2") + GLOB.tdome2 += loc + + if("tdomeadmin") + GLOB.tdomeadmin += loc + + if("tdomeobserve") + GLOB.tdomeobserve += loc + + if("aroomwarp") + GLOB.aroomwarp += loc + + if("blobstart") + GLOB.blobstart += loc + qdel(src) + + if("xeno_spawn") + GLOB.xeno_spawn += loc + qdel(src) + + if("ninjastart") + GLOB.ninjastart += loc + qdel(src) + + if("carpspawn") + GLOB.carplist += loc + + if("voxstart") + GLOB.raider_spawn += loc + + if("ERT Director") + GLOB.ertdirector += loc + qdel(src) + + if("Response Team") + GLOB.emergencyresponseteamspawn += loc + qdel(src) + + if("Syndicate Officer") + GLOB.syndicateofficer += loc + qdel(src) + + GLOB.landmarks_list += src + return 1 + +/obj/effect/landmark/Destroy() + GLOB.landmarks_list -= src + ..() + return QDEL_HINT_HARDDEL_NOW + +/obj/effect/landmark/proc/set_tag() + tag = text("landmark*[]", name) + + +/obj/effect/landmark/singularity_act() + return + +// Please stop bombing the Observer-Start landmark. +/obj/effect/landmark/ex_act() + return + +/obj/effect/landmark/singularity_pull() + return + +/obj/effect/landmark/start + name = "start" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x" + anchored = 1.0 + +/obj/effect/landmark/start/set_tag() + tag = "start*[name]" + + +//Costume spawner landmarks + +/obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears + + var/list/options = typesof(/obj/effect/landmark/costume) + var/PICK= options[rand(1,options.len)] + new PICK(src.loc) + qdel(src) + +//SUBCLASSES. Spawn a bunch of items and disappear likewise +/obj/effect/landmark/costume/chicken/New() + new /obj/item/clothing/suit/chickensuit(src.loc) + new /obj/item/clothing/head/chicken(src.loc) + new /obj/item/reagent_containers/food/snacks/egg(src.loc) + qdel(src) + +/obj/effect/landmark/costume/gladiator/New() + new /obj/item/clothing/under/gladiator(src.loc) + new /obj/item/clothing/head/helmet/gladiator(src.loc) + qdel(src) + +/obj/effect/landmark/costume/madscientist/New() + new /obj/item/clothing/under/gimmick/rank/captain/suit(src.loc) + new /obj/item/clothing/head/flatcap(src.loc) + new /obj/item/clothing/suit/storage/labcoat/mad(src.loc) + new /obj/item/clothing/glasses/gglasses(src.loc) + qdel(src) + +/obj/effect/landmark/costume/elpresidente/New() + new /obj/item/clothing/under/gimmick/rank/captain/suit(src.loc) + new /obj/item/clothing/head/flatcap(src.loc) + new /obj/item/clothing/mask/cigarette/cigar/havana(src.loc) + new /obj/item/clothing/shoes/jackboots(src.loc) + qdel(src) + +/obj/effect/landmark/costume/nyangirl/New() + new /obj/item/clothing/under/schoolgirl(src.loc) + new /obj/item/clothing/head/kitty(src.loc) + qdel(src) + +/obj/effect/landmark/costume/maid/New() + new /obj/item/clothing/under/blackskirt(src.loc) + var/CHOICE = pick( /obj/item/clothing/head/beret , /obj/item/clothing/head/rabbitears ) + new CHOICE(src.loc) + new /obj/item/clothing/glasses/sunglasses/blindfold(src.loc) + qdel(src) + +/obj/effect/landmark/costume/butler/New() + new /obj/item/clothing/suit/wcoat(src.loc) + new /obj/item/clothing/under/suit_jacket(src.loc) + new /obj/item/clothing/head/that(src.loc) + qdel(src) + +/obj/effect/landmark/costume/scratch/New() + new /obj/item/clothing/gloves/color/white(src.loc) + new /obj/item/clothing/shoes/white(src.loc) + new /obj/item/clothing/under/scratch(src.loc) + if(prob(30)) + new /obj/item/clothing/head/cueball(src.loc) + qdel(src) + +/obj/effect/landmark/costume/highlander/New() + new /obj/item/clothing/under/kilt(src.loc) + new /obj/item/clothing/head/beret(src.loc) + qdel(src) + +/obj/effect/landmark/costume/prig/New() + new /obj/item/clothing/suit/wcoat(src.loc) + new /obj/item/clothing/glasses/monocle(src.loc) + var/CHOICE= pick( /obj/item/clothing/head/bowlerhat, /obj/item/clothing/head/that) + new CHOICE(src.loc) + new /obj/item/clothing/shoes/black(src.loc) + new /obj/item/cane(src.loc) + new /obj/item/clothing/under/sl_suit(src.loc) + new /obj/item/clothing/mask/fakemoustache(src.loc) + qdel(src) + +/obj/effect/landmark/costume/plaguedoctor/New() + new /obj/item/clothing/suit/bio_suit/plaguedoctorsuit(src.loc) + new /obj/item/clothing/head/plaguedoctorhat(src.loc) + qdel(src) + +/obj/effect/landmark/costume/nightowl/New() + new /obj/item/clothing/under/owl(src.loc) + new /obj/item/clothing/mask/gas/owl_mask(src.loc) + qdel(src) + +/obj/effect/landmark/costume/waiter/New() + new /obj/item/clothing/under/waiter(src.loc) + var/CHOICE= pick( /obj/item/clothing/head/kitty, /obj/item/clothing/head/rabbitears) + new CHOICE(src.loc) + new /obj/item/clothing/suit/apron(src.loc) + qdel(src) + +/obj/effect/landmark/costume/pirate/New() + new /obj/item/clothing/under/pirate(src.loc) + new /obj/item/clothing/suit/pirate_black(src.loc) + var/CHOICE = pick( /obj/item/clothing/head/pirate , /obj/item/clothing/head/bandana ) + new CHOICE(src.loc) + new /obj/item/clothing/glasses/eyepatch(src.loc) + qdel(src) + +/obj/effect/landmark/costume/commie/New() + new /obj/item/clothing/under/soviet(src.loc) + new /obj/item/clothing/head/ushanka(src.loc) + qdel(src) + + +/obj/effect/landmark/costume/imperium_monk/New() + new /obj/item/clothing/suit/imperium_monk(src.loc) + if(prob(25)) + new /obj/item/clothing/mask/gas/cyborg(src.loc) + qdel(src) + +/obj/effect/landmark/costume/holiday_priest/New() + new /obj/item/clothing/suit/holidaypriest(src.loc) + qdel(src) + +/obj/effect/landmark/costume/marisawizard/fake/New() + new /obj/item/clothing/head/wizard/marisa/fake(src.loc) + new/obj/item/clothing/suit/wizrobe/marisa/fake(src.loc) + qdel(src) + +/obj/effect/landmark/costume/cutewitch/New() + new /obj/item/clothing/under/sundress(src.loc) + new /obj/item/clothing/head/witchwig(src.loc) + new /obj/item/twohanded/staff/broom(src.loc) + qdel(src) + +/obj/effect/landmark/costume/fakewizard/New() + new /obj/item/clothing/suit/wizrobe/fake(src.loc) + new /obj/item/clothing/head/wizard/fake(src.loc) + new /obj/item/twohanded/staff/(src.loc) + qdel(src) + +/obj/effect/landmark/costume/sexyclown/New() + new /obj/item/clothing/mask/gas/clown_hat/sexy(loc) + new /obj/item/clothing/under/rank/clown/sexy(loc) + qdel(src) + +/obj/effect/landmark/costume/sexymime/New() + new /obj/item/clothing/mask/gas/sexymime(src.loc) + new /obj/item/clothing/under/sexymime(src.loc) + qdel(src) + +/obj/effect/landmark/ruin + var/datum/map_template/ruin/ruin_template + +/obj/effect/landmark/ruin/New(loc, my_ruin_template) + name = "ruin_[GLOB.ruin_landmarks.len + 1]" + ..(loc) + ruin_template = my_ruin_template + GLOB.ruin_landmarks |= src + +/obj/effect/landmark/ruin/Destroy() + GLOB.ruin_landmarks -= src + ruin_template = null + . = ..() + +// Damage tiles +/obj/effect/landmark/damageturf + icon_state = "damaged" + +/obj/effect/landmark/damageturf/New() + ..() + var/turf/simulated/T = get_turf(src) + if(istype(T)) + T.break_tile() + +/obj/effect/landmark/burnturf + icon_state = "burned" + +/obj/effect/landmark/burnturf/New() + ..() + var/turf/simulated/T = get_turf(src) + T.burn_tile() + + +/obj/effect/landmark/battle_mob_point + name = "Nanomob Battle Avatar Spawn Point" + +/obj/effect/landmark/free_golem_spawn + name = "Free Golem Spawn Point" diff --git a/code/game/objects/effects/manifest.dm b/code/game/objects/effects/manifest.dm index ea1c80e53465..f267494496e7 100644 --- a/code/game/objects/effects/manifest.dm +++ b/code/game/objects/effects/manifest.dm @@ -1,20 +1,20 @@ -/obj/effect/manifest - name = "manifest" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x" - -/obj/effect/manifest/New() - - src.invisibility = 101 - return - -/obj/effect/manifest/proc/manifest() - var/dat = "Crew Manifest:
    " - for(var/mob/living/carbon/human/M in GLOB.mob_list) - dat += text(" [] - []
    ", M.name, M.get_assignment()) - var/obj/item/paper/P = new /obj/item/paper( src.loc ) - P.info = dat - P.name = "paper- 'Crew Manifest'" - //SN src = null - qdel(src) - return \ No newline at end of file +/obj/effect/manifest + name = "manifest" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x" + +/obj/effect/manifest/New() + + src.invisibility = 101 + return + +/obj/effect/manifest/proc/manifest() + var/dat = "Crew Manifest:
    " + for(var/mob/living/carbon/human/M in GLOB.mob_list) + dat += text(" [] - []
    ", M.name, M.get_assignment()) + var/obj/item/paper/P = new /obj/item/paper( src.loc ) + P.info = dat + P.name = "paper- 'Crew Manifest'" + //SN src = null + qdel(src) + return diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm index 75cbacd2fda1..049146551436 100644 --- a/code/game/objects/effects/mines.dm +++ b/code/game/objects/effects/mines.dm @@ -1,185 +1,185 @@ -/obj/effect/mine - name = "dummy mine" - desc = "I Better stay away from that thing." - density = 0 - anchored = 1 - icon = 'icons/obj/items.dmi' - icon_state = "uglyminearmed" - var/triggered = 0 - var/faction = "syndicate" - -/obj/effect/mine/proc/mineEffect(mob/living/victim) - to_chat(victim, "*click*") - -/obj/effect/mine/Crossed(AM as mob|obj, oldloc) - if(!isliving(AM)) - return - var/mob/living/M = AM - if(faction && (faction in M.faction)) - return - if(M.flying) - return - triggermine(M) - -/obj/effect/mine/proc/triggermine(mob/living/victim) - if(triggered) - return - visible_message("[victim] sets off [bicon(src)] [src]!") - do_sparks(3, 1, src) - mineEffect(victim) - triggered = 1 - qdel(src) - -/obj/effect/mine/ex_act(severity) - // Necessary because, as effects, they have infinite health, and wouldn't be destroyed otherwise. - // Also, they're pressure-sensitive mines, it makes sense that an explosion (wave of pressure) triggers/destroys them. - qdel(src) - -/obj/effect/mine/explosive - name = "explosive mine" - var/range_devastation = 0 - var/range_heavy = 1 - var/range_light = 2 - var/range_flash = 3 - -/obj/effect/mine/explosive/mineEffect(mob/living/victim) - explosion(loc, range_devastation, range_heavy, range_light, range_flash) - -/obj/effect/mine/stun - name = "stun mine" - var/stun_time = 8 - -/obj/effect/mine/stun/mineEffect(mob/living/victim) - if(isliving(victim)) - victim.Weaken(stun_time) - -/obj/effect/mine/depot - name = "sentry mine" - -/obj/effect/mine/depot/mineEffect(mob/living/victim) - var/area/syndicate_depot/core/depotarea = areaMaster - if(istype(depotarea)) - if(depotarea.mine_triggered(victim)) - explosion(loc, 1, 0, 0, 1) // devastate the tile you are on, but leave everything else untouched - -/obj/effect/mine/dnascramble - name = "Radiation Mine" - var/radiation_amount - -/obj/effect/mine/dnascramble/mineEffect(mob/living/victim) - victim.apply_effect(radiation_amount, IRRADIATE, 0) - if(ishuman(victim)) - var/mob/living/carbon/human/V = victim - if(NO_DNA in V.dna.species.species_traits) - return - randmutb(victim) - domutcheck(victim ,null) - -/obj/effect/mine/gas - name = "oxygen mine" - var/gas_amount = 360 - var/gas_type = LINDA_SPAWN_OXYGEN - -/obj/effect/mine/gas/mineEffect(mob/living/victim) - atmos_spawn_air(gas_type, gas_amount) - -/obj/effect/mine/gas/plasma - name = "plasma mine" - gas_type = LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS - -/obj/effect/mine/gas/n2o - name = "\improper N2O mine" - gas_type = LINDA_SPAWN_N2O - -/obj/effect/mine/sound - name = "honkblaster 1000" - var/sound = 'sound/items/bikehorn.ogg' - -/obj/effect/mine/sound/mineEffect(mob/living/victim) - playsound(loc, sound, 100, 1) - -/obj/effect/mine/sound/bwoink - name = "bwoink mine" - sound = 'sound/effects/adminhelp.ogg' - -/obj/effect/mine/pickup - name = "pickup" - desc = "pick me up" - icon = 'icons/effects/effects.dmi' - icon_state = "electricity2" - density = 0 - var/duration = 0 - -/obj/effect/mine/pickup/New() - ..() - animate(src, pixel_y = 4, time = 20, loop = -1) - -/obj/effect/mine/pickup/triggermine(mob/living/victim) - if(triggered) - return - triggered = 1 - invisibility = 101 - mineEffect(victim) - qdel(src) - -/obj/effect/mine/pickup/bloodbath - name = "Red Orb" - desc = "You feel angry just looking at it." - duration = 1200 //2min - color = "red" - -/obj/effect/mine/pickup/bloodbath/mineEffect(mob/living/carbon/victim) - if(!istype(victim) || !victim.client) - return - to_chat(victim, "RIP AND TEAR") - victim << 'sound/misc/e1m1.ogg' - var/old_color = victim.client.color - var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0) - var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0) - - spawn(0) - new /obj/effect/hallucination/delusion(victim.loc, victim, force_kind = "demon", duration = duration, skip_nearby = 0) - - var/obj/item/twohanded/required/chainsaw/doomslayer/chainsaw = new(victim.loc) - chainsaw.flags |= NODROP | DROPDEL - victim.drop_l_hand() - victim.drop_r_hand() - victim.put_in_hands(chainsaw) - chainsaw.attack_self(victim) - chainsaw.wield(victim) - victim.reagents.add_reagent("adminordrazine", 25) - - victim.client.color = pure_red - animate(victim.client,color = red_splash, time = 10, easing = SINE_EASING|EASE_OUT) - spawn(10) - animate(victim.client,color = old_color, time = duration)//, easing = SINE_EASING|EASE_OUT) - spawn(duration) - to_chat(victim, "Your bloodlust seeps back into the bog of your subconscious and you regain self control.") - qdel(chainsaw) - qdel(src) - -/obj/effect/mine/pickup/healing - name = "Blue Orb" - desc = "You feel better just looking at it." - color = "blue" - -/obj/effect/mine/pickup/healing/mineEffect(mob/living/carbon/victim) - if(!victim.client || !istype(victim)) - return - to_chat(victim, "You feel great!") - victim.revive() - -/obj/effect/mine/pickup/speed - name = "Yellow Orb" - desc = "You feel faster just looking at it." - color = "yellow" - duration = 300 - -/obj/effect/mine/pickup/speed/mineEffect(mob/living/carbon/victim) - if(!victim.client || !istype(victim)) - return - to_chat(victim, "You feel fast!") - victim.status_flags |= GOTTAGOFAST - spawn(duration) - victim.status_flags &= ~GOTTAGOFAST - to_chat(victim, "You slow down.") +/obj/effect/mine + name = "dummy mine" + desc = "I Better stay away from that thing." + density = 0 + anchored = 1 + icon = 'icons/obj/items.dmi' + icon_state = "uglyminearmed" + var/triggered = 0 + var/faction = "syndicate" + +/obj/effect/mine/proc/mineEffect(mob/living/victim) + to_chat(victim, "*click*") + +/obj/effect/mine/Crossed(AM as mob|obj, oldloc) + if(!isliving(AM)) + return + var/mob/living/M = AM + if(faction && (faction in M.faction)) + return + if(M.flying) + return + triggermine(M) + +/obj/effect/mine/proc/triggermine(mob/living/victim) + if(triggered) + return + visible_message("[victim] sets off [bicon(src)] [src]!") + do_sparks(3, 1, src) + mineEffect(victim) + triggered = 1 + qdel(src) + +/obj/effect/mine/ex_act(severity) + // Necessary because, as effects, they have infinite health, and wouldn't be destroyed otherwise. + // Also, they're pressure-sensitive mines, it makes sense that an explosion (wave of pressure) triggers/destroys them. + qdel(src) + +/obj/effect/mine/explosive + name = "explosive mine" + var/range_devastation = 0 + var/range_heavy = 1 + var/range_light = 2 + var/range_flash = 3 + +/obj/effect/mine/explosive/mineEffect(mob/living/victim) + explosion(loc, range_devastation, range_heavy, range_light, range_flash) + +/obj/effect/mine/stun + name = "stun mine" + var/stun_time = 8 + +/obj/effect/mine/stun/mineEffect(mob/living/victim) + if(isliving(victim)) + victim.Weaken(stun_time) + +/obj/effect/mine/depot + name = "sentry mine" + +/obj/effect/mine/depot/mineEffect(mob/living/victim) + var/area/syndicate_depot/core/depotarea = areaMaster + if(istype(depotarea)) + if(depotarea.mine_triggered(victim)) + explosion(loc, 1, 0, 0, 1) // devastate the tile you are on, but leave everything else untouched + +/obj/effect/mine/dnascramble + name = "Radiation Mine" + var/radiation_amount + +/obj/effect/mine/dnascramble/mineEffect(mob/living/victim) + victim.apply_effect(radiation_amount, IRRADIATE, 0) + if(ishuman(victim)) + var/mob/living/carbon/human/V = victim + if(NO_DNA in V.dna.species.species_traits) + return + randmutb(victim) + domutcheck(victim ,null) + +/obj/effect/mine/gas + name = "oxygen mine" + var/gas_amount = 360 + var/gas_type = LINDA_SPAWN_OXYGEN + +/obj/effect/mine/gas/mineEffect(mob/living/victim) + atmos_spawn_air(gas_type, gas_amount) + +/obj/effect/mine/gas/plasma + name = "plasma mine" + gas_type = LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS + +/obj/effect/mine/gas/n2o + name = "\improper N2O mine" + gas_type = LINDA_SPAWN_N2O + +/obj/effect/mine/sound + name = "honkblaster 1000" + var/sound = 'sound/items/bikehorn.ogg' + +/obj/effect/mine/sound/mineEffect(mob/living/victim) + playsound(loc, sound, 100, 1) + +/obj/effect/mine/sound/bwoink + name = "bwoink mine" + sound = 'sound/effects/adminhelp.ogg' + +/obj/effect/mine/pickup + name = "pickup" + desc = "pick me up" + icon = 'icons/effects/effects.dmi' + icon_state = "electricity2" + density = 0 + var/duration = 0 + +/obj/effect/mine/pickup/New() + ..() + animate(src, pixel_y = 4, time = 20, loop = -1) + +/obj/effect/mine/pickup/triggermine(mob/living/victim) + if(triggered) + return + triggered = 1 + invisibility = 101 + mineEffect(victim) + qdel(src) + +/obj/effect/mine/pickup/bloodbath + name = "Red Orb" + desc = "You feel angry just looking at it." + duration = 1200 //2min + color = "red" + +/obj/effect/mine/pickup/bloodbath/mineEffect(mob/living/carbon/victim) + if(!istype(victim) || !victim.client) + return + to_chat(victim, "RIP AND TEAR") + victim << 'sound/misc/e1m1.ogg' + var/old_color = victim.client.color + var/red_splash = list(1,0,0,0.8,0.2,0, 0.8,0,0.2,0.1,0,0) + var/pure_red = list(0,0,0,0,0,0,0,0,0,1,0,0) + + spawn(0) + new /obj/effect/hallucination/delusion(victim.loc, victim, force_kind = "demon", duration = duration, skip_nearby = 0) + + var/obj/item/twohanded/required/chainsaw/doomslayer/chainsaw = new(victim.loc) + chainsaw.flags |= NODROP | DROPDEL + victim.drop_l_hand() + victim.drop_r_hand() + victim.put_in_hands(chainsaw) + chainsaw.attack_self(victim) + chainsaw.wield(victim) + victim.reagents.add_reagent("adminordrazine", 25) + + victim.client.color = pure_red + animate(victim.client,color = red_splash, time = 10, easing = SINE_EASING|EASE_OUT) + spawn(10) + animate(victim.client,color = old_color, time = duration)//, easing = SINE_EASING|EASE_OUT) + spawn(duration) + to_chat(victim, "Your bloodlust seeps back into the bog of your subconscious and you regain self control.") + qdel(chainsaw) + qdel(src) + +/obj/effect/mine/pickup/healing + name = "Blue Orb" + desc = "You feel better just looking at it." + color = "blue" + +/obj/effect/mine/pickup/healing/mineEffect(mob/living/carbon/victim) + if(!victim.client || !istype(victim)) + return + to_chat(victim, "You feel great!") + victim.revive() + +/obj/effect/mine/pickup/speed + name = "Yellow Orb" + desc = "You feel faster just looking at it." + color = "yellow" + duration = 300 + +/obj/effect/mine/pickup/speed/mineEffect(mob/living/carbon/victim) + if(!victim.client || !istype(victim)) + return + to_chat(victim, "You feel fast!") + victim.status_flags |= GOTTAGOFAST + spawn(duration) + victim.status_flags &= ~GOTTAGOFAST + to_chat(victim, "You slow down.") diff --git a/code/game/objects/effects/misc.dm b/code/game/objects/effects/misc.dm index bd237fbfdb65..434c0af1136b 100644 --- a/code/game/objects/effects/misc.dm +++ b/code/game/objects/effects/misc.dm @@ -1,123 +1,123 @@ -//MISC EFFECTS - -//This file is for effects that are less than 20 lines and don't fit very well in any other category. - -/*CURRENT CONTENTS - Strange Present - Mark - Beam - Laser - Begin - Stop - Projection - Shut_controller - Showcase - Spawner - List_container -*/ - -//The effect when you wrap a dead body in gift wrap -/obj/effect/spresent - name = "strange present" - desc = "It's a ... present?" - icon = 'icons/obj/items.dmi' - icon_state = "strangepresent" - density = 1 - anchored = 0 - -/obj/effect/mark - var/mark = "" - icon = 'icons/misc/mark.dmi' - icon_state = "blank" - anchored = 1 - layer = 99 - plane = HUD_PLANE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/obj/effect/beam - name = "beam" - var/def_zone - pass_flags = PASSTABLE - -/obj/effect/laser - name = "laser" - desc = "IT BURNS!!!" - icon = 'icons/obj/projectiles.dmi' - var/damage = 0.0 - var/range = 10.0 - -/obj/effect/begin - name = "begin" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "begin" - anchored = 1.0 - -/obj/effect/projection - name = "Projection" - desc = "This looks like a projection of something." - anchored = 1.0 - - -/obj/effect/shut_controller - name = "shut controller" - var/moving = null - var/list/parts = list( ) - -/obj/structure/showcase - name = "Showcase" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "showcase_1" - desc = "A stand with the empty body of a cyborg bolted to it." - density = 1 - anchored = 1 - -/obj/effect/spawner - name = "object spawner" - -/obj/effect/list_container - name = "list container" - -/obj/effect/list_container/mobl - name = "mobl" - var/master = null - - var/list/container = list( ) - - -/obj/structure/showcase/horrific_experiment - name = "horrific experiment" - desc = "Some sort of pod filled with blood and vicerea. You swear you can see it moving..." - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_g" - - -//Makes a tile fully lit no matter what -/obj/effect/fullbright - icon = 'icons/effects/alphacolors.dmi' - icon_state = "white" - plane = LIGHTING_PLANE - layer = LIGHTING_LAYER - blend_mode = BLEND_ADD - - -/obj/effect/dummy/lighting_obj - name = "lighting fx obj" - desc = "Tell a coder if you're seeing this." - icon_state = "nothing" - light_color = "#FFFFFF" - light_range = MINIMUM_USEFUL_LIGHT_RANGE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/obj/effect/dummy/lighting_obj/Initialize(mapload, _color, _range, _power, _duration) - . = ..() - set_light(_range ? _range : light_range, _power ? _power : light_power, _color ? _color : light_color) - if(_duration) - QDEL_IN(src, _duration) - -/obj/effect/dummy/lighting_obj/moblight - name = "mob lighting fx" - -/obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, _color, _range, _power, _duration) - . = ..() - if(!ismob(loc)) - return INITIALIZE_HINT_QDEL \ No newline at end of file +//MISC EFFECTS + +//This file is for effects that are less than 20 lines and don't fit very well in any other category. + +/*CURRENT CONTENTS + Strange Present + Mark + Beam + Laser + Begin + Stop + Projection + Shut_controller + Showcase + Spawner + List_container +*/ + +//The effect when you wrap a dead body in gift wrap +/obj/effect/spresent + name = "strange present" + desc = "It's a ... present?" + icon = 'icons/obj/items.dmi' + icon_state = "strangepresent" + density = 1 + anchored = 0 + +/obj/effect/mark + var/mark = "" + icon = 'icons/misc/mark.dmi' + icon_state = "blank" + anchored = 1 + layer = 99 + plane = HUD_PLANE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/beam + name = "beam" + var/def_zone + pass_flags = PASSTABLE + +/obj/effect/laser + name = "laser" + desc = "IT BURNS!!!" + icon = 'icons/obj/projectiles.dmi' + var/damage = 0.0 + var/range = 10.0 + +/obj/effect/begin + name = "begin" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "begin" + anchored = 1.0 + +/obj/effect/projection + name = "Projection" + desc = "This looks like a projection of something." + anchored = 1.0 + + +/obj/effect/shut_controller + name = "shut controller" + var/moving = null + var/list/parts = list( ) + +/obj/structure/showcase + name = "Showcase" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "showcase_1" + desc = "A stand with the empty body of a cyborg bolted to it." + density = 1 + anchored = 1 + +/obj/effect/spawner + name = "object spawner" + +/obj/effect/list_container + name = "list container" + +/obj/effect/list_container/mobl + name = "mobl" + var/master = null + + var/list/container = list( ) + + +/obj/structure/showcase/horrific_experiment + name = "horrific experiment" + desc = "Some sort of pod filled with blood and vicerea. You swear you can see it moving..." + icon = 'icons/obj/cloning.dmi' + icon_state = "pod_g" + + +//Makes a tile fully lit no matter what +/obj/effect/fullbright + icon = 'icons/effects/alphacolors.dmi' + icon_state = "white" + plane = LIGHTING_PLANE + layer = LIGHTING_LAYER + blend_mode = BLEND_ADD + + +/obj/effect/dummy/lighting_obj + name = "lighting fx obj" + desc = "Tell a coder if you're seeing this." + icon_state = "nothing" + light_color = "#FFFFFF" + light_range = MINIMUM_USEFUL_LIGHT_RANGE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/dummy/lighting_obj/Initialize(mapload, _color, _range, _power, _duration) + . = ..() + set_light(_range ? _range : light_range, _power ? _power : light_power, _color ? _color : light_color) + if(_duration) + QDEL_IN(src, _duration) + +/obj/effect/dummy/lighting_obj/moblight + name = "mob lighting fx" + +/obj/effect/dummy/lighting_obj/moblight/Initialize(mapload, _color, _range, _power, _duration) + . = ..() + if(!ismob(loc)) + return INITIALIZE_HINT_QDEL diff --git a/code/game/objects/effects/overlays.dm b/code/game/objects/effects/overlays.dm index 4506f9b33a99..c7e66b6226e7 100644 --- a/code/game/objects/effects/overlays.dm +++ b/code/game/objects/effects/overlays.dm @@ -1,65 +1,65 @@ -/obj/effect/overlay - name = "overlay" - var/i_attached//Added for possible image attachments to objects. For hallucinations and the like. - -/obj/effect/overlay/singularity_act() - return - -/obj/effect/overlay/singularity_pull() - return - -/obj/effect/overlay/beam//Not actually a projectile, just an effect. - name = "beam" - icon = 'icons/effects/beam.dmi' - icon_state = "b_beam" - var/tmp/atom/BeamSource - -/obj/effect/overlay/beam/New() - ..() - QDEL_IN(src, 10) - -/obj/effect/overlay/palmtree_r - name = "Palm tree" - icon = 'icons/misc/beach2.dmi' - icon_state = "palm1" - density = 1 - layer = 5 - anchored = 1 - -/obj/effect/overlay/palmtree_l - name = "Palm tree" - icon = 'icons/misc/beach2.dmi' - icon_state = "palm2" - density = 1 - layer = 5 - anchored = 1 - -/obj/effect/overlay/coconut - name = "Coconuts" - icon = 'icons/misc/beach.dmi' - icon_state = "coconuts" - -/obj/effect/overlay/sparkles - name = "sparkles" - icon = 'icons/effects/effects.dmi' - icon_state = "shieldsparkles" - -/obj/effect/overlay/adminoverlay - name = "adminoverlay" - icon = 'icons/effects/effects.dmi' - icon_state = "admin" - layer = 4.1 - -/obj/effect/overlay/wall_rot - name = "Wallrot" - desc = "Ick..." - icon = 'icons/effects/wallrot.dmi' - anchored = 1 - density = 1 - layer = 5 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/obj/effect/overlay/wall_rot/New() - ..() - pixel_x += rand(-10, 10) - pixel_y += rand(-10, 10) +/obj/effect/overlay + name = "overlay" + var/i_attached//Added for possible image attachments to objects. For hallucinations and the like. + +/obj/effect/overlay/singularity_act() + return + +/obj/effect/overlay/singularity_pull() + return + +/obj/effect/overlay/beam//Not actually a projectile, just an effect. + name = "beam" + icon = 'icons/effects/beam.dmi' + icon_state = "b_beam" + var/tmp/atom/BeamSource + +/obj/effect/overlay/beam/New() + ..() + QDEL_IN(src, 10) + +/obj/effect/overlay/palmtree_r + name = "Palm tree" + icon = 'icons/misc/beach2.dmi' + icon_state = "palm1" + density = 1 + layer = 5 + anchored = 1 + +/obj/effect/overlay/palmtree_l + name = "Palm tree" + icon = 'icons/misc/beach2.dmi' + icon_state = "palm2" + density = 1 + layer = 5 + anchored = 1 + +/obj/effect/overlay/coconut + name = "Coconuts" + icon = 'icons/misc/beach.dmi' + icon_state = "coconuts" + +/obj/effect/overlay/sparkles + name = "sparkles" + icon = 'icons/effects/effects.dmi' + icon_state = "shieldsparkles" + +/obj/effect/overlay/adminoverlay + name = "adminoverlay" + icon = 'icons/effects/effects.dmi' + icon_state = "admin" + layer = 4.1 + +/obj/effect/overlay/wall_rot + name = "Wallrot" + desc = "Ick..." + icon = 'icons/effects/wallrot.dmi' + anchored = 1 + density = 1 + layer = 5 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/obj/effect/overlay/wall_rot/New() + ..() + pixel_x += rand(-10, 10) + pixel_y += rand(-10, 10) diff --git a/code/game/objects/effects/portals.dm b/code/game/objects/effects/portals.dm index ddf43f9c0779..aa7566317683 100644 --- a/code/game/objects/effects/portals.dm +++ b/code/game/objects/effects/portals.dm @@ -1,128 +1,128 @@ -/obj/effect/portal - name = "portal" - desc = "Looks unstable. Best to test it with the clown." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "portal" - anchored = TRUE - - var/obj/item/target = null - var/creator = null - - var/failchance = 5 - var/fail_icon = "portal1" - - var/precision = TRUE // how close to the portal you will teleport. FALSE = on the portal, TRUE = adjacent - var/can_multitool_to_remove = FALSE - var/ignore_tele_proof_area_setting = FALSE - -/obj/effect/portal/New(loc, turf/target, creator = null, lifespan = 300) - ..() - - GLOB.portals += src - - src.target = target - src.creator = creator - - if(lifespan > 0) - spawn(lifespan) - qdel(src) - -/obj/effect/portal/Destroy() - GLOB.portals -= src - - if(isobj(creator)) - var/obj/O = creator - O.portal_destroyed(src) - - creator = null - target = null - return ..() - -/obj/effect/portal/singularity_pull() - return - -/obj/effect/portal/singularity_act() - return - -/obj/effect/portal/Crossed(atom/movable/AM, oldloc) - if(isobserver(AM)) - return ..() - - if(target && (get_turf(oldloc) == get_turf(target))) - return ..() - - if(!teleport(AM)) - return ..() - -/obj/effect/portal/attack_tk(mob/user) - return - -/obj/effect/portal/attack_hand(mob/user) - . = ..() - if(.) - return - if(get_turf(user) == get_turf(src)) - teleport(user) - if(Adjacent(user)) - user.forceMove(get_turf(src)) - -/obj/effect/portal/attack_ghost(mob/dead/observer/O) - if(target) - O.forceMove(target) - -/obj/effect/portal/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(can_multitool_to_remove) - qdel(src) - else - user.forceMove(get_turf(src)) - -/obj/effect/portal/proc/can_teleport(atom/movable/M) - . = TRUE - - if(!istype(M)) - . = FALSE - - if(!M.simulated || iseffect(M)) - . = FALSE - - if(M.anchored && ismecha(M)) - . = FALSE - -/obj/effect/portal/proc/teleport(atom/movable/M) - if(!can_teleport(M)) - return FALSE - - if(!target) - qdel(src) - return FALSE - - if(ismegafauna(M)) - message_admins("[M] has used a portal at [ADMIN_VERBOSEJMP(src)] made by [key_name_admin(usr)].") - - if(prob(failchance)) - icon_state = fail_icon - if(!do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0, bypass_area_flag = ignore_tele_proof_area_setting)) // Try to send them to deep space. - invalid_teleport() - return FALSE - else - if(!do_teleport(M, target, precision, bypass_area_flag = ignore_tele_proof_area_setting)) // Try to send them to a turf adjacent to target. - invalid_teleport() - return FALSE - - return TRUE - -/obj/effect/portal/proc/invalid_teleport() - visible_message("[src] flickers and fails due to bluespace interference!") - do_sparks(5, 0, loc) - qdel(src) - -/obj/effect/portal/redspace - name = "redspace portal" - desc = "A portal capable of bypassing bluespace interference." - icon_state = "portal1" - failchance = 0 - precision = 0 - ignore_tele_proof_area_setting = TRUE +/obj/effect/portal + name = "portal" + desc = "Looks unstable. Best to test it with the clown." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "portal" + anchored = TRUE + + var/obj/item/target = null + var/creator = null + + var/failchance = 5 + var/fail_icon = "portal1" + + var/precision = TRUE // how close to the portal you will teleport. FALSE = on the portal, TRUE = adjacent + var/can_multitool_to_remove = FALSE + var/ignore_tele_proof_area_setting = FALSE + +/obj/effect/portal/New(loc, turf/target, creator = null, lifespan = 300) + ..() + + GLOB.portals += src + + src.target = target + src.creator = creator + + if(lifespan > 0) + spawn(lifespan) + qdel(src) + +/obj/effect/portal/Destroy() + GLOB.portals -= src + + if(isobj(creator)) + var/obj/O = creator + O.portal_destroyed(src) + + creator = null + target = null + return ..() + +/obj/effect/portal/singularity_pull() + return + +/obj/effect/portal/singularity_act() + return + +/obj/effect/portal/Crossed(atom/movable/AM, oldloc) + if(isobserver(AM)) + return ..() + + if(target && (get_turf(oldloc) == get_turf(target))) + return ..() + + if(!teleport(AM)) + return ..() + +/obj/effect/portal/attack_tk(mob/user) + return + +/obj/effect/portal/attack_hand(mob/user) + . = ..() + if(.) + return + if(get_turf(user) == get_turf(src)) + teleport(user) + if(Adjacent(user)) + user.forceMove(get_turf(src)) + +/obj/effect/portal/attack_ghost(mob/dead/observer/O) + if(target) + O.forceMove(target) + +/obj/effect/portal/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(can_multitool_to_remove) + qdel(src) + else + user.forceMove(get_turf(src)) + +/obj/effect/portal/proc/can_teleport(atom/movable/M) + . = TRUE + + if(!istype(M)) + . = FALSE + + if(!M.simulated || iseffect(M)) + . = FALSE + + if(M.anchored && ismecha(M)) + . = FALSE + +/obj/effect/portal/proc/teleport(atom/movable/M) + if(!can_teleport(M)) + return FALSE + + if(!target) + qdel(src) + return FALSE + + if(ismegafauna(M)) + message_admins("[M] has used a portal at [ADMIN_VERBOSEJMP(src)] made by [key_name_admin(usr)].") + + if(prob(failchance)) + icon_state = fail_icon + if(!do_teleport(M, locate(rand(5, world.maxx - 5), rand(5, world.maxy -5), 3), 0, bypass_area_flag = ignore_tele_proof_area_setting)) // Try to send them to deep space. + invalid_teleport() + return FALSE + else + if(!do_teleport(M, target, precision, bypass_area_flag = ignore_tele_proof_area_setting)) // Try to send them to a turf adjacent to target. + invalid_teleport() + return FALSE + + return TRUE + +/obj/effect/portal/proc/invalid_teleport() + visible_message("[src] flickers and fails due to bluespace interference!") + do_sparks(5, 0, loc) + qdel(src) + +/obj/effect/portal/redspace + name = "redspace portal" + desc = "A portal capable of bypassing bluespace interference." + icon_state = "portal1" + failchance = 0 + precision = 0 + ignore_tele_proof_area_setting = TRUE diff --git a/code/game/objects/effects/snowcloud.dm b/code/game/objects/effects/snowcloud.dm index 5723b66bc046..66535adc8562 100644 --- a/code/game/objects/effects/snowcloud.dm +++ b/code/game/objects/effects/snowcloud.dm @@ -52,7 +52,7 @@ /obj/effect/snowcloud/proc/try_to_spread_cloud() if(prob(95 - parent_machine.cooling_speed * 5)) //10 / 15 / 20 / 25% chance to spawn a new cloud return - var/list/random_dirs = shuffle(cardinal) + var/list/random_dirs = shuffle(GLOB.cardinal) for(var/potential in random_dirs) var/turf/T = get_turf(get_step(src, potential)) if(isspaceturf(T) || T.density) diff --git a/code/game/objects/effects/spawners/bombspawner.dm b/code/game/objects/effects/spawners/bombspawner.dm index bfacf64adb5e..2e81ecb235de 100644 --- a/code/game/objects/effects/spawners/bombspawner.dm +++ b/code/game/objects/effects/spawners/bombspawner.dm @@ -1,65 +1,65 @@ -/obj/effect/spawner/newbomb - name = "bomb" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x" - var/btype = 0 // 0=radio, 1=prox, 2=time - var/btemp1 = 1500 - var/btemp2 = 1000 // tank temperatures - - timer - btype = 2 - - syndicate - btemp1 = 150 - btemp2 = 20 - - proximity - btype = 1 - - radio - btype = 0 - - -/obj/effect/spawner/newbomb/New() - ..() - - var/obj/item/transfer_valve/V = new(src.loc) - var/obj/item/tank/plasma/PT = new(V) - var/obj/item/tank/oxygen/OT = new(V) - - V.tank_one = PT - V.tank_two = OT - - PT.master = V - OT.master = V - - PT.air_contents.temperature = btemp1 + T0C - OT.air_contents.temperature = btemp2 + T0C - - var/obj/item/assembly/S - - switch(src.btype) - // radio - if(0) - - S = new/obj/item/assembly/signaler(V) - - // proximity - if(1) - - S = new/obj/item/assembly/prox_sensor(V) - - // timer - if(2) - - S = new/obj/item/assembly/timer(V) - - - V.attached_device = S - - S.holder = V - S.toggle_secure() - - V.update_icon() - - qdel(src) +/obj/effect/spawner/newbomb + name = "bomb" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x" + var/btype = 0 // 0=radio, 1=prox, 2=time + var/btemp1 = 1500 + var/btemp2 = 1000 // tank temperatures + + timer + btype = 2 + + syndicate + btemp1 = 150 + btemp2 = 20 + + proximity + btype = 1 + + radio + btype = 0 + + +/obj/effect/spawner/newbomb/New() + ..() + + var/obj/item/transfer_valve/V = new(src.loc) + var/obj/item/tank/plasma/PT = new(V) + var/obj/item/tank/oxygen/OT = new(V) + + V.tank_one = PT + V.tank_two = OT + + PT.master = V + OT.master = V + + PT.air_contents.temperature = btemp1 + T0C + OT.air_contents.temperature = btemp2 + T0C + + var/obj/item/assembly/S + + switch(src.btype) + // radio + if(0) + + S = new/obj/item/assembly/signaler(V) + + // proximity + if(1) + + S = new/obj/item/assembly/prox_sensor(V) + + // timer + if(2) + + S = new/obj/item/assembly/timer(V) + + + V.attached_device = S + + S.holder = V + S.toggle_secure() + + V.update_icon() + + qdel(src) diff --git a/code/game/objects/effects/spawners/gibspawner.dm b/code/game/objects/effects/spawners/gibspawner.dm index b75df275659b..998fb4a28680 100644 --- a/code/game/objects/effects/spawners/gibspawner.dm +++ b/code/game/objects/effects/spawners/gibspawner.dm @@ -1,35 +1,35 @@ -/obj/effect/gibspawner/generic - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) - gibamounts = list(2,2,1) - -/obj/effect/gibspawner/generic/New() - gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list()) - ..() - -/obj/effect/gibspawner/human - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/down,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) - gibamounts = list(1,1,1,1,1,1,1) - -/obj/effect/gibspawner/human/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs, list()) - gibamounts[6] = pick(0,1,2) - ..() - -/obj/effect/gibspawner/xeno - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/xeno/up,/obj/effect/decal/cleanable/blood/gibs/xeno/down,/obj/effect/decal/cleanable/blood/gibs/xeno,/obj/effect/decal/cleanable/blood/gibs/xeno,/obj/effect/decal/cleanable/blood/gibs/xeno/body,/obj/effect/decal/cleanable/blood/gibs/xeno/limb,/obj/effect/decal/cleanable/blood/gibs/xeno/core) - gibamounts = list(1,1,1,1,1,1,1) - -/obj/effect/gibspawner/xeno/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs, list()) - gibamounts[6] = pick(0,1,2) - ..() - -/obj/effect/gibspawner/robot - sparks = 1 - gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/robot/up,/obj/effect/decal/cleanable/blood/gibs/robot/down,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot/limb) - gibamounts = list(1,1,1,1,1,1) - -/obj/effect/gibspawner/robot/New() - gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), alldirs, alldirs) - gibamounts[6] = pick(0,1,2) - ..() +/obj/effect/gibspawner/generic + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) + gibamounts = list(2,2,1) + +/obj/effect/gibspawner/generic/New() + gibdirections = list(list(WEST, NORTHWEST, SOUTHWEST, NORTH),list(EAST, NORTHEAST, SOUTHEAST, SOUTH), list()) + ..() + +/obj/effect/gibspawner/human + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/down,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs,/obj/effect/decal/cleanable/blood/gibs/core) + gibamounts = list(1,1,1,1,1,1,1) + +/obj/effect/gibspawner/human/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), GLOB.alldirs, GLOB.alldirs, list()) + gibamounts[6] = pick(0,1,2) + ..() + +/obj/effect/gibspawner/xeno + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/xeno/up,/obj/effect/decal/cleanable/blood/gibs/xeno/down,/obj/effect/decal/cleanable/blood/gibs/xeno,/obj/effect/decal/cleanable/blood/gibs/xeno,/obj/effect/decal/cleanable/blood/gibs/xeno/body,/obj/effect/decal/cleanable/blood/gibs/xeno/limb,/obj/effect/decal/cleanable/blood/gibs/xeno/core) + gibamounts = list(1,1,1,1,1,1,1) + +/obj/effect/gibspawner/xeno/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), GLOB.alldirs, GLOB.alldirs, list()) + gibamounts[6] = pick(0,1,2) + ..() + +/obj/effect/gibspawner/robot + sparks = 1 + gibtypes = list(/obj/effect/decal/cleanable/blood/gibs/robot/up,/obj/effect/decal/cleanable/blood/gibs/robot/down,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot,/obj/effect/decal/cleanable/blood/gibs/robot/limb) + gibamounts = list(1,1,1,1,1,1) + +/obj/effect/gibspawner/robot/New() + gibdirections = list(list(NORTH, NORTHEAST, NORTHWEST),list(SOUTH, SOUTHEAST, SOUTHWEST),list(WEST, NORTHWEST, SOUTHWEST),list(EAST, NORTHEAST, SOUTHEAST), GLOB.alldirs, GLOB.alldirs) + gibamounts[6] = pick(0,1,2) + ..() diff --git a/code/game/objects/effects/spawners/random_spawners.dm b/code/game/objects/effects/spawners/random_spawners.dm index ee7fc6700753..b32688dbb839 100644 --- a/code/game/objects/effects/spawners/random_spawners.dm +++ b/code/game/objects/effects/spawners/random_spawners.dm @@ -318,4 +318,4 @@ /obj/effect/spawner/random_spawners/syndicate/layout/spacepod name = "50pc loot spacepod" result = list(/obj/spacepod/syndi = 1, - /obj/spacepod/syndi/unlocked = 1) \ No newline at end of file + /obj/spacepod/syndi/unlocked = 1) diff --git a/code/game/objects/effects/spawners/vaultspawner.dm b/code/game/objects/effects/spawners/vaultspawner.dm index 35cc039a08af..0d882cc9bd28 100644 --- a/code/game/objects/effects/spawners/vaultspawner.dm +++ b/code/game/objects/effects/spawners/vaultspawner.dm @@ -1,26 +1,26 @@ -/obj/effect/vaultspawner - var/maxX = 6 - var/maxY = 6 - var/minX = 2 - var/minY = 2 - -/obj/effect/vaultspawner/New(turf/location as turf,lX = minX,uX = maxX,lY = minY,uY = maxY,var/type = null) - if(!type) - type = pick("sandstone","rock","alien") - - var/lowBoundX = location.x - var/lowBoundY = location.y - - var/hiBoundX = location.x + rand(lX,uX) - var/hiBoundY = location.y + rand(lY,uY) - - var/z = location.z - - for(var/i = lowBoundX,i<=hiBoundX,i++) - for(var/j = lowBoundY,j<=hiBoundY,j++) - if(i == lowBoundX || i == hiBoundX || j == lowBoundY || j == hiBoundY) - new /turf/simulated/wall/vault(locate(i,j,z),type) - else - new /turf/simulated/floor/vault(locate(i,j,z),type) - - qdel(src) \ No newline at end of file +/obj/effect/vaultspawner + var/maxX = 6 + var/maxY = 6 + var/minX = 2 + var/minY = 2 + +/obj/effect/vaultspawner/New(turf/location as turf,lX = minX,uX = maxX,lY = minY,uY = maxY,var/type = null) + if(!type) + type = pick("sandstone","rock","alien") + + var/lowBoundX = location.x + var/lowBoundY = location.y + + var/hiBoundX = location.x + rand(lX,uX) + var/hiBoundY = location.y + rand(lY,uY) + + var/z = location.z + + for(var/i = lowBoundX,i<=hiBoundX,i++) + for(var/j = lowBoundY,j<=hiBoundY,j++) + if(i == lowBoundX || i == hiBoundX || j == lowBoundY || j == hiBoundY) + new /turf/simulated/wall/vault(locate(i,j,z),type) + else + new /turf/simulated/floor/vault(locate(i,j,z),type) + + qdel(src) diff --git a/code/game/objects/effects/spawners/windowspawner.dm b/code/game/objects/effects/spawners/windowspawner.dm index e19aa54fd7d1..cf29d86a450e 100644 --- a/code/game/objects/effects/spawners/windowspawner.dm +++ b/code/game/objects/effects/spawners/windowspawner.dm @@ -16,7 +16,7 @@ qdel(G) //just in case mappers don't know what they are doing if(!useFull) - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) for(var/obj/effect/spawner/window/WS in get_step(src,cdir)) cdir = null break diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm index 169a0a4b3c8c..d97663759afd 100644 --- a/code/game/objects/effects/spiders.dm +++ b/code/game/objects/effects/spiders.dm @@ -1,213 +1,213 @@ -//generic procs copied from obj/effect/alien -/obj/structure/spider - name = "web" - desc = "it's stringy and sticky" - icon = 'icons/effects/effects.dmi' - anchored = TRUE - density = FALSE - max_integrity = 15 - var/mob/living/carbon/human/master_commander = null - -/obj/structure/spider/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - if(damage_type == BURN)//the stickiness of the web mutes all attack sounds except fire damage type - playsound(loc, 'sound/items/welder.ogg', 100, TRUE) - - -/obj/structure/spider/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(damage_flag == "melee") - switch(damage_type) - if(BURN) - damage_amount *= 2 - if(BRUTE) - damage_amount *= 0.25 - . = ..() - -/obj/structure/spider/Destroy() - master_commander = null - return ..() - -/obj/structure/spider/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 300) - take_damage(5, BURN, 0, 0) - -/obj/structure/spider/stickyweb - icon_state = "stickyweb1" - -/obj/structure/spider/stickyweb/New() - ..() - if(prob(50)) - icon_state = "stickyweb2" - -/obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0) - if(height == 0) - return TRUE - if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider)) - return TRUE - else if(istype(mover, /mob/living)) - if(prob(50)) - to_chat(mover, "You get stuck in [src] for a moment.") - return FALSE - else if(istype(mover, /obj/item/projectile)) - return prob(30) - return TRUE - -/obj/structure/spider/eggcluster - name = "egg cluster" - desc = "They seem to pulse slightly with an inner life" - icon_state = "eggs" - var/amount_grown = 0 - var/player_spiders = 0 - var/list/faction = list() - -/obj/structure/spider/eggcluster/New() - ..() - pixel_x = rand(3,-3) - pixel_y = rand(3,-3) - START_PROCESSING(SSobj, src) - -/obj/structure/spider/eggcluster/process() - amount_grown += rand(0,2) - if(amount_grown >= 100) - var/num = rand(3, 12) - for(var/i in 1 to num) - var/obj/structure/spider/spiderling/S = new /obj/structure/spider/spiderling(loc) - S.faction = faction.Copy() - S.master_commander = master_commander - if(player_spiders) - S.player_spiders = 1 - qdel(src) - -/obj/structure/spider/spiderling - name = "spiderling" - desc = "It never stays still for long." - icon_state = "spiderling" - anchored = 0 - layer = 2.75 - max_integrity = 3 - var/amount_grown = 0 - var/grow_as = null - var/obj/machinery/atmospherics/unary/vent_pump/entry_vent - var/travelling_in_vent = 0 - var/player_spiders = 0 - var/list/faction = list() - var/selecting_player = 0 - -/obj/structure/spider/spiderling/New() - ..() - pixel_x = rand(6,-6) - pixel_y = rand(6,-6) - START_PROCESSING(SSobj, src) - -/obj/structure/spider/spiderling/Destroy() - STOP_PROCESSING(SSobj, src) - entry_vent = null - new /obj/effect/decal/cleanable/spiderling_remains(get_turf(src)) - return ..() - -/obj/structure/spider/spiderling/Bump(atom/user) - if(istype(user, /obj/structure/table)) - loc = user.loc - else - ..() - -/obj/structure/spider/spiderling/process() - if(travelling_in_vent) - if(istype(loc, /turf)) - travelling_in_vent = 0 - entry_vent = null - else if(entry_vent) - if(get_dist(src, entry_vent) <= 1) - var/list/vents = list() - for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.parent.other_atmosmch) - vents.Add(temp_vent) - if(!vents.len) - entry_vent = null - return - var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents) - if(prob(50)) - visible_message("[src] scrambles into the ventilation ducts!", \ - "You hear something squeezing through the ventilation ducts.") - - spawn(rand(20,60)) - loc = exit_vent - var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) - spawn(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - - if(prob(50)) - audible_message("You hear something squeezing through the ventilation ducts.") - sleep(travel_time) - - if(!exit_vent || exit_vent.welded) - loc = entry_vent - entry_vent = null - return - loc = exit_vent.loc - entry_vent = null - var/area/new_area = get_area(loc) - if(new_area) - new_area.Entered(src) - //================= - - else if(prob(33)) - var/list/nearby = oview(10, src) - if(nearby.len) - var/target_atom = pick(nearby) - walk_to(src, target_atom) - if(prob(40)) - visible_message("[src] skitters[pick(" away"," around","")].") - else if(prob(10)) - //ventcrawl! - for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) - if(!v.welded) - entry_vent = v - walk_to(src, entry_vent, 1) - break - if(isturf(loc)) - amount_grown += rand(0,2) - if(amount_grown >= 100) - if(!grow_as) - grow_as = pick(typesof(/mob/living/simple_animal/hostile/poison/giant_spider)) - var/mob/living/simple_animal/hostile/poison/giant_spider/S = new grow_as(loc) - S.faction = faction.Copy() - S.master_commander = master_commander - if(player_spiders && !selecting_player) - selecting_player = 1 - spawn() - var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1) - - if(candidates.len) - var/mob/C = pick(candidates) - if(C) - S.key = C.key - if(S.master_commander) - to_chat(S, "You are a spider who is loyal to [S.master_commander], obey [S.master_commander]'s every order and assist [S.master_commander.p_them()] in completing [S.master_commander.p_their()] goals at any cost.") - qdel(src) - -/obj/effect/decal/cleanable/spiderling_remains - name = "spiderling remains" - desc = "Green squishy mess." - icon = 'icons/effects/effects.dmi' - icon_state = "greenshatter" - anchored = 1 - -/obj/structure/spider/cocoon - name = "cocoon" - desc = "Something wrapped in silky spider web" - icon_state = "cocoon1" - max_integrity = 60 - -/obj/structure/spider/cocoon/New() - ..() - icon_state = pick("cocoon1","cocoon2","cocoon3") - -/obj/structure/spider/cocoon/Destroy() - visible_message("[src] splits open.") - for(var/atom/movable/A in contents) - A.forceMove(loc) - return ..() +//generic procs copied from obj/effect/alien +/obj/structure/spider + name = "web" + desc = "it's stringy and sticky" + icon = 'icons/effects/effects.dmi' + anchored = TRUE + density = FALSE + max_integrity = 15 + var/mob/living/carbon/human/master_commander = null + +/obj/structure/spider/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + if(damage_type == BURN)//the stickiness of the web mutes all attack sounds except fire damage type + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) + + +/obj/structure/spider/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(damage_flag == "melee") + switch(damage_type) + if(BURN) + damage_amount *= 2 + if(BRUTE) + damage_amount *= 0.25 + . = ..() + +/obj/structure/spider/Destroy() + master_commander = null + return ..() + +/obj/structure/spider/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 300) + take_damage(5, BURN, 0, 0) + +/obj/structure/spider/stickyweb + icon_state = "stickyweb1" + +/obj/structure/spider/stickyweb/New() + ..() + if(prob(50)) + icon_state = "stickyweb2" + +/obj/structure/spider/stickyweb/CanPass(atom/movable/mover, turf/target, height=0) + if(height == 0) + return TRUE + if(istype(mover, /mob/living/simple_animal/hostile/poison/giant_spider)) + return TRUE + else if(istype(mover, /mob/living)) + if(prob(50)) + to_chat(mover, "You get stuck in [src] for a moment.") + return FALSE + else if(istype(mover, /obj/item/projectile)) + return prob(30) + return TRUE + +/obj/structure/spider/eggcluster + name = "egg cluster" + desc = "They seem to pulse slightly with an inner life" + icon_state = "eggs" + var/amount_grown = 0 + var/player_spiders = 0 + var/list/faction = list() + +/obj/structure/spider/eggcluster/New() + ..() + pixel_x = rand(3,-3) + pixel_y = rand(3,-3) + START_PROCESSING(SSobj, src) + +/obj/structure/spider/eggcluster/process() + amount_grown += rand(0,2) + if(amount_grown >= 100) + var/num = rand(3, 12) + for(var/i in 1 to num) + var/obj/structure/spider/spiderling/S = new /obj/structure/spider/spiderling(loc) + S.faction = faction.Copy() + S.master_commander = master_commander + if(player_spiders) + S.player_spiders = 1 + qdel(src) + +/obj/structure/spider/spiderling + name = "spiderling" + desc = "It never stays still for long." + icon_state = "spiderling" + anchored = 0 + layer = 2.75 + max_integrity = 3 + var/amount_grown = 0 + var/grow_as = null + var/obj/machinery/atmospherics/unary/vent_pump/entry_vent + var/travelling_in_vent = 0 + var/player_spiders = 0 + var/list/faction = list() + var/selecting_player = 0 + +/obj/structure/spider/spiderling/New() + ..() + pixel_x = rand(6,-6) + pixel_y = rand(6,-6) + START_PROCESSING(SSobj, src) + +/obj/structure/spider/spiderling/Destroy() + STOP_PROCESSING(SSobj, src) + entry_vent = null + new /obj/effect/decal/cleanable/spiderling_remains(get_turf(src)) + return ..() + +/obj/structure/spider/spiderling/Bump(atom/user) + if(istype(user, /obj/structure/table)) + loc = user.loc + else + ..() + +/obj/structure/spider/spiderling/process() + if(travelling_in_vent) + if(istype(loc, /turf)) + travelling_in_vent = 0 + entry_vent = null + else if(entry_vent) + if(get_dist(src, entry_vent) <= 1) + var/list/vents = list() + for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in entry_vent.parent.other_atmosmch) + vents.Add(temp_vent) + if(!vents.len) + entry_vent = null + return + var/obj/machinery/atmospherics/unary/vent_pump/exit_vent = pick(vents) + if(prob(50)) + visible_message("[src] scrambles into the ventilation ducts!", \ + "You hear something squeezing through the ventilation ducts.") + + spawn(rand(20,60)) + loc = exit_vent + var/travel_time = round(get_dist(loc, exit_vent.loc) / 2) + spawn(travel_time) + + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + + if(prob(50)) + audible_message("You hear something squeezing through the ventilation ducts.") + sleep(travel_time) + + if(!exit_vent || exit_vent.welded) + loc = entry_vent + entry_vent = null + return + loc = exit_vent.loc + entry_vent = null + var/area/new_area = get_area(loc) + if(new_area) + new_area.Entered(src) + //================= + + else if(prob(33)) + var/list/nearby = oview(10, src) + if(nearby.len) + var/target_atom = pick(nearby) + walk_to(src, target_atom) + if(prob(40)) + visible_message("[src] skitters[pick(" away"," around","")].") + else if(prob(10)) + //ventcrawl! + for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) + if(!v.welded) + entry_vent = v + walk_to(src, entry_vent, 1) + break + if(isturf(loc)) + amount_grown += rand(0,2) + if(amount_grown >= 100) + if(!grow_as) + grow_as = pick(typesof(/mob/living/simple_animal/hostile/poison/giant_spider)) + var/mob/living/simple_animal/hostile/poison/giant_spider/S = new grow_as(loc) + S.faction = faction.Copy() + S.master_commander = master_commander + if(player_spiders && !selecting_player) + selecting_player = 1 + spawn() + var/list/candidates = pollCandidates("Do you want to play as a spider?", ROLE_GSPIDER, 1) + + if(candidates.len) + var/mob/C = pick(candidates) + if(C) + S.key = C.key + if(S.master_commander) + to_chat(S, "You are a spider who is loyal to [S.master_commander], obey [S.master_commander]'s every order and assist [S.master_commander.p_them()] in completing [S.master_commander.p_their()] goals at any cost.") + qdel(src) + +/obj/effect/decal/cleanable/spiderling_remains + name = "spiderling remains" + desc = "Green squishy mess." + icon = 'icons/effects/effects.dmi' + icon_state = "greenshatter" + anchored = 1 + +/obj/structure/spider/cocoon + name = "cocoon" + desc = "Something wrapped in silky spider web" + icon_state = "cocoon1" + max_integrity = 60 + +/obj/structure/spider/cocoon/New() + ..() + icon_state = pick("cocoon1","cocoon2","cocoon3") + +/obj/structure/spider/cocoon/Destroy() + visible_message("[src] splits open.") + for(var/atom/movable/A in contents) + A.forceMove(loc) + return ..() diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm index c1231e698fc3..0a47e57112eb 100644 --- a/code/game/objects/effects/step_triggers.dm +++ b/code/game/objects/effects/step_triggers.dm @@ -195,4 +195,4 @@ playsound(T, sound, volume, freq_vary, extra_range) if(happens_once) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/effects/temporary_visuals/cult.dm b/code/game/objects/effects/temporary_visuals/cult.dm index 7a12caf6b105..775b4eafceb7 100644 --- a/code/game/objects/effects/temporary_visuals/cult.dm +++ b/code/game/objects/effects/temporary_visuals/cult.dm @@ -38,4 +38,4 @@ /obj/effect/temp_visual/cult/turf/open/floor icon_state = "floorglow" duration = 5 - plane = FLOOR_PLANE \ No newline at end of file + plane = FLOOR_PLANE diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm index 5c3580ae1773..27d5eafb6771 100644 --- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm +++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm @@ -18,7 +18,7 @@ /obj/effect/temp_visual/dir_setting/bloodsplatter/New(loc, set_dir, blood_color) if(blood_color) color = blood_color - if(set_dir in diagonals) + if(set_dir in GLOB.diagonals) icon_state = "[splatter_type][pick(1, 2, 6)]" else icon_state = "[splatter_type][pick(3, 4, 5)]" @@ -353,4 +353,4 @@ /obj/effect/temp_visual/impact_effect/ion icon_state = "shieldsparkles" - duration = 6 \ No newline at end of file + duration = 6 diff --git a/code/game/objects/effects/temporary_visuals/temporary_visual.dm b/code/game/objects/effects/temporary_visuals/temporary_visual.dm index 23b1aaa0d2ec..d09acce72557 100644 --- a/code/game/objects/effects/temporary_visuals/temporary_visual.dm +++ b/code/game/objects/effects/temporary_visuals/temporary_visual.dm @@ -11,7 +11,7 @@ /obj/effect/temp_visual/Initialize(mapload) . = ..() if(randomdir) - setDir(pick(cardinal)) + setDir(pick(GLOB.cardinal)) timerid = QDEL_IN(src, duration) diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm index cec5b6e1fc86..3e7f476e3467 100644 --- a/code/game/objects/empulse.dm +++ b/code/game/objects/empulse.dm @@ -1,32 +1,32 @@ -/proc/empulse(turf/epicenter, heavy_range, light_range, log=0, cause = null) - if(!epicenter) return - - if(!istype(epicenter, /turf)) - epicenter = get_turf(epicenter.loc) - - if(log) - message_admins("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])": ""] [ADMIN_COORDJMP(epicenter)]") - log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [COORD(epicenter)]") - - if(heavy_range > 1) - new/obj/effect/temp_visual/emp/pulse(epicenter) - - if(heavy_range > light_range) - light_range = heavy_range - - for(var/mob/M in range(heavy_range, epicenter)) - M << 'sound/effects/empulse.ogg' - for(var/atom/T in range(light_range, epicenter)) - var/distance = get_dist(epicenter, T) - if(distance < 0) - distance = 0 - if(distance < heavy_range) - T.emp_act(1) - else if(distance == heavy_range) - if(prob(50)) - T.emp_act(1) - else - T.emp_act(2) - else if(distance <= light_range) - T.emp_act(2) - return 1 +/proc/empulse(turf/epicenter, heavy_range, light_range, log=0, cause = null) + if(!epicenter) return + + if(!istype(epicenter, /turf)) + epicenter = get_turf(epicenter.loc) + + if(log) + message_admins("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])": ""] [ADMIN_COORDJMP(epicenter)]") + log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [COORD(epicenter)]") + + if(heavy_range > 1) + new/obj/effect/temp_visual/emp/pulse(epicenter) + + if(heavy_range > light_range) + light_range = heavy_range + + for(var/mob/M in range(heavy_range, epicenter)) + M << 'sound/effects/empulse.ogg' + for(var/atom/T in range(light_range, epicenter)) + var/distance = get_dist(epicenter, T) + if(distance < 0) + distance = 0 + if(distance < heavy_range) + T.emp_act(1) + else if(distance == heavy_range) + if(prob(50)) + T.emp_act(1) + else + T.emp_act(2) + else if(distance <= light_range) + T.emp_act(2) + return 1 diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index 462a6d7eca74..b10a072389ae 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -1,241 +1,241 @@ -//TODO: Flash range does nothing currently - -/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range = 0, silent = 0, smoke = 1, cause = null, breach = TRUE) - src = null //so we don't abort once src is deleted - epicenter = get_turf(epicenter) - - // Archive the uncapped explosion for the doppler array - var/orig_dev_range = devastation_range - var/orig_heavy_range = heavy_impact_range - var/orig_light_range = light_impact_range - - if(!ignorecap) - // Clamp all values to MAX_EXPLOSION_RANGE - devastation_range = min (MAX_EX_DEVASTATION_RANGE, devastation_range) - heavy_impact_range = min (MAX_EX_HEAVY_RANGE, heavy_impact_range) - light_impact_range = min (MAX_EX_LIGHT_RANGE, light_impact_range) - flash_range = min (MAX_EX_FLASH_RANGE, flash_range) - flame_range = min (MAX_EX_FLAME_RANGE, flame_range) - - spawn(0) - var/watch = start_watch() - if(!epicenter) return - - var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range) - var/list/cached_exp_block = list() - - if(adminlog) - message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [ADMIN_COORDJMP(epicenter)] ") - log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [COORD(epicenter)] ") - - // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. - // Stereo users will also hear the direction of the explosion! - - // Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions. - // 3/7/14 will calculate to 80 + 35 - - var/far_dist = 0 - far_dist += heavy_impact_range * 5 - far_dist += devastation_range * 20 - - if(!silent) - var/frequency = get_rand_frequency() - var/sound/explosion_sound = sound(get_sfx("explosion")) - var/sound/global_boom = sound('sound/effects/explosionfar.ogg') - - for(var/P in GLOB.player_list) - var/mob/M = P - // Double check for client - if(M && M.client) - var/turf/M_turf = get_turf(M) - if(M_turf && M_turf.z == epicenter.z) - var/dist = get_dist(M_turf, epicenter) - // If inside the blast radius + world.view - 2 - if(dist <= round(max_range + world.view - 2, 1)) - M.playsound_local(epicenter, null, 100, 1, frequency, falloff = 5, S = explosion_sound) - // You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station. - else if(M.can_hear() && !isspaceturf(M.loc)) - M << global_boom - - if(heavy_impact_range > 1) - if(smoke) - var/datum/effect_system/explosion/smoke/E = new/datum/effect_system/explosion/smoke() - E.set_up(epicenter) - E.start() - else - var/datum/effect_system/explosion/E = new/datum/effect_system/explosion() - E.set_up(epicenter) - E.start() - - var/x0 = epicenter.x - var/y0 = epicenter.y - var/z0 = epicenter.z - - var/list/affected_turfs = spiral_range_turfs(max_range, epicenter) - - if(config.reactionary_explosions) - for(var/A in affected_turfs) // we cache the explosion block rating of every turf in the explosion area - var/turf/T = A - cached_exp_block[T] = 0 - if(T.density && T.explosion_block) - cached_exp_block[T] += T.explosion_block - - for(var/obj/O in T) - var/the_block = O.explosion_block - cached_exp_block[T] += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block - CHECK_TICK - - for(var/A in affected_turfs) - var/turf/T = A - if(!T) - continue - var/dist = hypotenuse(T.x, T.y, x0, y0) - - if(config.reactionary_explosions) - var/turf/Trajectory = T - while(Trajectory != epicenter) - Trajectory = get_step_towards(Trajectory, epicenter) - dist += cached_exp_block[Trajectory] - - var/flame_dist = 0 -// var/throw_dist = max_range - dist - - if(dist < flame_range) - flame_dist = 1 - - if(dist < devastation_range) dist = 1 - else if(dist < heavy_impact_range) dist = 2 - else if(dist < light_impact_range) dist = 3 - else dist = 0 - - //------- TURF FIRES ------- - - if(T) - if(flame_dist && prob(40) && !istype(T, /turf/space) && !T.density) - new /obj/effect/hotspot(T) //Mostly for ambience! - if(dist > 0) - if(istype(T, /turf/simulated)) - var/turf/simulated/S = T - var/affecting_level - if(dist == 1) - affecting_level = 1 - else - affecting_level = S.is_shielded() ? 2 : (S.intact ? 2 : 1) - for(var/atom in S.contents) //bypass type checking since only atom can be contained by turfs anyway - var/atom/AM = atom - if(AM && AM.simulated) - if(AM.level >= affecting_level) - AM.ex_act(dist) - else - for(var/atom in T.contents) //see above - var/atom/AM = atom - if(AM && AM.simulated) - AM.ex_act(dist) - CHECK_TICK - if(breach) - T.ex_act(dist) - else - T.ex_act(3) - - CHECK_TICK - //--- THROW ITEMS AROUND --- -/* - if(throw_dist > 0) - var/throw_dir = get_dir(epicenter,T) - for(var/obj/item/I in T) - spawn(0) //Simultaneously not one at a time - if(I && !I.anchored) - var/throw_mult = 0.5 + (0.5 * rand()) // Between 0.5 and 1.0 - var/throw_range = round((throw_dist + 1) * throw_mult) // Roughly 50% to 100% of throw_dist - if(throw_range > 0) - var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range) - I.throw_at(throw_at, throw_range, 2, no_spin = 1) //Throw it at 2 speed, this is purely visual anyway; don't spin the thrown items, it's very costly. -*/ - var/took = stop_watch(watch) - //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare - if(Debug2) - log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") - - //Machines which report explosions. - for(var/i,i<=doppler_arrays.len,i++) - var/obj/machinery/doppler_array/Array = doppler_arrays[i] - if(Array) - Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took,orig_dev_range,orig_heavy_range,orig_light_range) - - return 1 - - - -/proc/secondaryexplosion(turf/epicenter, range) - for(var/turf/tile in spiral_range_turfs(range, epicenter)) - tile.ex_act(2) - -/client/proc/check_bomb_impacts() - set name = "Check Bomb Impact" - set category = "Debug" - - var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No") - var/turf/epicenter = get_turf(mob) - if(!epicenter) - return - - var/dev = 0 - var/heavy = 0 - var/light = 0 - var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb") - var/choice = input("Bomb Size?") in choices - switch(choice) - if(null) - return 0 - if("Small Bomb") - dev = 1 - heavy = 2 - light = 3 - if("Medium Bomb") - dev = 2 - heavy = 3 - light = 4 - if("Big Bomb") - dev = 3 - heavy = 5 - light = 7 - if("Custom Bomb") - dev = input("Devestation range (Tiles):") as num - heavy = input("Heavy impact range (Tiles):") as num - light = input("Light impact range (Tiles):") as num - - var/max_range = max(dev, heavy, light) - var/x0 = epicenter.x - var/y0 = epicenter.y - var/list/wipe_colours = list() - for(var/turf/T in spiral_range_turfs(max_range, epicenter)) - wipe_colours += T - var/dist = hypotenuse(T.x, T.y, x0, y0) - - if(newmode == "Yes") - var/turf/TT = T - while(TT != epicenter) - TT = get_step_towards(TT,epicenter) - if(TT.density) - dist += TT.explosion_block - - for(var/obj/O in T) - var/the_block = O.explosion_block - dist += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block - - if(dist < dev) - T.color = "red" - T.maptext = "Dev" - else if(dist < heavy) - T.color = "yellow" - T.maptext = "Heavy" - else if(dist < light) - T.color = "blue" - T.maptext = "Light" - else - continue - - sleep(100) - for(var/turf/T in wipe_colours) - T.color = null - T.maptext = "" +//TODO: Flash range does nothing currently + +/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range = 0, silent = 0, smoke = 1, cause = null, breach = TRUE) + src = null //so we don't abort once src is deleted + epicenter = get_turf(epicenter) + + // Archive the uncapped explosion for the doppler array + var/orig_dev_range = devastation_range + var/orig_heavy_range = heavy_impact_range + var/orig_light_range = light_impact_range + + if(!ignorecap) + // Clamp all values to MAX_EXPLOSION_RANGE + devastation_range = min (GLOB.max_ex_devastation_range, devastation_range) + heavy_impact_range = min (GLOB.max_ex_heavy_range, heavy_impact_range) + light_impact_range = min (GLOB.max_ex_light_range, light_impact_range) + flash_range = min (GLOB.max_ex_flash_range, flash_range) + flame_range = min (GLOB.max_ex_flame_range, flame_range) + + spawn(0) + var/watch = start_watch() + if(!epicenter) return + + var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range) + var/list/cached_exp_block = list() + + if(adminlog) + message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [ADMIN_COORDJMP(epicenter)] ") + log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] [cause ? "(Cause: [cause])" : ""] [COORD(epicenter)] ") + + // Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves. + // Stereo users will also hear the direction of the explosion! + + // Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions. + // 3/7/14 will calculate to 80 + 35 + + var/far_dist = 0 + far_dist += heavy_impact_range * 5 + far_dist += devastation_range * 20 + + if(!silent) + var/frequency = get_rand_frequency() + var/sound/explosion_sound = sound(get_sfx("explosion")) + var/sound/global_boom = sound('sound/effects/explosionfar.ogg') + + for(var/P in GLOB.player_list) + var/mob/M = P + // Double check for client + if(M && M.client) + var/turf/M_turf = get_turf(M) + if(M_turf && M_turf.z == epicenter.z) + var/dist = get_dist(M_turf, epicenter) + // If inside the blast radius + world.view - 2 + if(dist <= round(max_range + world.view - 2, 1)) + M.playsound_local(epicenter, null, 100, 1, frequency, falloff = 5, S = explosion_sound) + // You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station. + else if(M.can_hear() && !isspaceturf(M.loc)) + M << global_boom + + if(heavy_impact_range > 1) + if(smoke) + var/datum/effect_system/explosion/smoke/E = new/datum/effect_system/explosion/smoke() + E.set_up(epicenter) + E.start() + else + var/datum/effect_system/explosion/E = new/datum/effect_system/explosion() + E.set_up(epicenter) + E.start() + + var/x0 = epicenter.x + var/y0 = epicenter.y + var/z0 = epicenter.z + + var/list/affected_turfs = spiral_range_turfs(max_range, epicenter) + + if(config.reactionary_explosions) + for(var/A in affected_turfs) // we cache the explosion block rating of every turf in the explosion area + var/turf/T = A + cached_exp_block[T] = 0 + if(T.density && T.explosion_block) + cached_exp_block[T] += T.explosion_block + + for(var/obj/O in T) + var/the_block = O.explosion_block + cached_exp_block[T] += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block + CHECK_TICK + + for(var/A in affected_turfs) + var/turf/T = A + if(!T) + continue + var/dist = hypotenuse(T.x, T.y, x0, y0) + + if(config.reactionary_explosions) + var/turf/Trajectory = T + while(Trajectory != epicenter) + Trajectory = get_step_towards(Trajectory, epicenter) + dist += cached_exp_block[Trajectory] + + var/flame_dist = 0 +// var/throw_dist = max_range - dist + + if(dist < flame_range) + flame_dist = 1 + + if(dist < devastation_range) dist = 1 + else if(dist < heavy_impact_range) dist = 2 + else if(dist < light_impact_range) dist = 3 + else dist = 0 + + //------- TURF FIRES ------- + + if(T) + if(flame_dist && prob(40) && !istype(T, /turf/space) && !T.density) + new /obj/effect/hotspot(T) //Mostly for ambience! + if(dist > 0) + if(istype(T, /turf/simulated)) + var/turf/simulated/S = T + var/affecting_level + if(dist == 1) + affecting_level = 1 + else + affecting_level = S.is_shielded() ? 2 : (S.intact ? 2 : 1) + for(var/atom in S.contents) //bypass type checking since only atom can be contained by turfs anyway + var/atom/AM = atom + if(AM && AM.simulated) + if(AM.level >= affecting_level) + AM.ex_act(dist) + else + for(var/atom in T.contents) //see above + var/atom/AM = atom + if(AM && AM.simulated) + AM.ex_act(dist) + CHECK_TICK + if(breach) + T.ex_act(dist) + else + T.ex_act(3) + + CHECK_TICK + //--- THROW ITEMS AROUND --- +/* + if(throw_dist > 0) + var/throw_dir = get_dir(epicenter,T) + for(var/obj/item/I in T) + spawn(0) //Simultaneously not one at a time + if(I && !I.anchored) + var/throw_mult = 0.5 + (0.5 * rand()) // Between 0.5 and 1.0 + var/throw_range = round((throw_dist + 1) * throw_mult) // Roughly 50% to 100% of throw_dist + if(throw_range > 0) + var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range) + I.throw_at(throw_at, throw_range, 2, no_spin = 1) //Throw it at 2 speed, this is purely visual anyway; don't spin the thrown items, it's very costly. +*/ + var/took = stop_watch(watch) + //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare + if(GLOB.debug2) + log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.") + + //Machines which report explosions. + for(var/i,i<=GLOB.doppler_arrays.len,i++) + var/obj/machinery/doppler_array/Array = GLOB.doppler_arrays[i] + if(Array) + Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took,orig_dev_range,orig_heavy_range,orig_light_range) + + return 1 + + + +/proc/secondaryexplosion(turf/epicenter, range) + for(var/turf/tile in spiral_range_turfs(range, epicenter)) + tile.ex_act(2) + +/client/proc/check_bomb_impacts() + set name = "Check Bomb Impact" + set category = "Debug" + + var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No") + var/turf/epicenter = get_turf(mob) + if(!epicenter) + return + + var/dev = 0 + var/heavy = 0 + var/light = 0 + var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb") + var/choice = input("Bomb Size?") in choices + switch(choice) + if(null) + return 0 + if("Small Bomb") + dev = 1 + heavy = 2 + light = 3 + if("Medium Bomb") + dev = 2 + heavy = 3 + light = 4 + if("Big Bomb") + dev = 3 + heavy = 5 + light = 7 + if("Custom Bomb") + dev = input("Devestation range (Tiles):") as num + heavy = input("Heavy impact range (Tiles):") as num + light = input("Light impact range (Tiles):") as num + + var/max_range = max(dev, heavy, light) + var/x0 = epicenter.x + var/y0 = epicenter.y + var/list/wipe_colours = list() + for(var/turf/T in spiral_range_turfs(max_range, epicenter)) + wipe_colours += T + var/dist = hypotenuse(T.x, T.y, x0, y0) + + if(newmode == "Yes") + var/turf/TT = T + while(TT != epicenter) + TT = get_step_towards(TT,epicenter) + if(TT.density) + dist += TT.explosion_block + + for(var/obj/O in T) + var/the_block = O.explosion_block + dist += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block + + if(dist < dev) + T.color = "red" + T.maptext = "Dev" + else if(dist < heavy) + T.color = "yellow" + T.maptext = "Heavy" + else if(dist < light) + T.color = "blue" + T.maptext = "Light" + else + continue + + sleep(100) + for(var/turf/T in wipe_colours) + T.color = null + T.maptext = "" diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 955998fc0f6b..d530d0d71d6d 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -1,681 +1,680 @@ -var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.dmi', "icon_state" = "fire") - -/obj/item - name = "item" - icon = 'icons/obj/items.dmi' - var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers - var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite - var/blood_overlay_color = null - var/item_state = null - var/lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - var/righthand_file = 'icons/mob/inhands/items_righthand.dmi' - - //Dimensions of the lefthand_file and righthand_file vars - //eg: 32x32 sprite, 64x64 sprite, etc. - var/inhand_x_dimension = 32 - var/inhand_y_dimension = 32 - - max_integrity = 200 - - can_be_hit = FALSE - suicidal_hands = TRUE - - var/hitsound = null - var/usesound = null - var/throwhitsound - var/w_class = WEIGHT_CLASS_NORMAL - var/slot_flags = 0 //This is used to determine on which slots an item can fit. - pass_flags = PASSTABLE - pressure_resistance = 4 -// causeerrorheresoifixthis - var/obj/item/master = null - - var/heat_protection = 0 //flags which determine which body parts are protected from heat. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm - var/cold_protection = 0 //flags which determine which body parts are protected from cold. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm - var/max_heat_protection_temperature //Set this variable to determine up to which temperature (IN KELVIN) the item protects against heat damage. Keep at null to disable protection. Only protects areas set by heat_protection flags - var/min_cold_protection_temperature //Set this variable to determine down to which temperature (IN KELVIN) the item protects against cold damage. 0 is NOT an acceptable number due to if(varname) tests!! Keep at null to disable protection. Only protects areas set by cold_protection flags - - var/list/actions = list() //list of /datum/action's that this item has. - var/list/actions_types = list() //list of paths of action datums to give to the item on New(). - var/list/action_icon = list() //list of icons-sheets for a given action to override the icon. - var/list/action_icon_state = list() //list of icon states for a given action to override the icon_state. - - var/list/materials = list() - //Since any item can now be a piece of clothing, this has to be put here so all items share it. - var/flags_inv //This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc. - var/item_color = null - var/body_parts_covered = 0 //see setup.dm for appropriate bit flags - //var/heat_transfer_coefficient = 1 //0 prevents all transfers, 1 is invisible - var/gas_transfer_coefficient = 1 // for leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets) - var/permeability_coefficient = 1 // for chemicals/diseases - var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit) - var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up - var/armour_penetration = 0 //percentage of armour effectiveness to remove - var/list/allowed = null //suit storage stuff. - var/obj/item/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers. - - var/needs_permit = 0 //Used by security bots to determine if this item is safe for public use. - - var/strip_delay = DEFAULT_ITEM_STRIP_DELAY - var/put_on_delay = DEFAULT_ITEM_PUTON_DELAY - var/breakouttime = 0 - var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES - var/flags_size = 0 //flag, primarily used for clothing to determine if a fatty can wear something or not. - - var/block_chance = 0 - var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom - - // Needs to be in /obj/item because corgis can wear a lot of - // non-clothing items - var/datum/dog_fashion/dog_fashion = null - - var/mob/thrownby = null - - //So items can have custom embedd values - //Because customisation is king - var/embed_chance = EMBED_CHANCE - var/embedded_fall_chance = EMBEDDED_ITEM_FALLOUT - var/embedded_pain_chance = EMBEDDED_PAIN_CHANCE - var/embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER //The coefficient of multiplication for the damage this item does while embedded (this*w_class) - var/embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class) - var/embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER //The coefficient of multiplication for the damage this item does when first embedded (this*w_class) - var/embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class) - var/embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME //A time in ticks, multiplied by the w_class. - var/embedded_ignore_throwspeed_threshold = FALSE - - var/tool_behaviour = NONE //What kind of tool are we? - var/tool_enabled = TRUE //If we can turn on or off, are we currently active? Mostly for welders and this will normally be TRUE - var/tool_volume = 50 //How loud are we when we use our tool? - var/toolspeed = 1 // If this item is a tool, the speed multiplier - - /* Species-specific sprites, concept stolen from Paradise//vg/. - ex: - sprite_sheets = list( - "Tajaran" = 'icons/cat/are/bad' - ) - If index term exists and icon_override is not set, this sprite sheet will be used. - */ - var/list/sprite_sheets = null - var/list/sprite_sheets_inhand = null //Used to override inhand items. Use a single .dmi and suffix the icon states inside with _l and _r for each hand. - var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc. - var/sprite_sheets_obj = null //Used to override hardcoded clothing inventory object dmis in human clothing proc. - - var/trip_verb = TV_TRIP - var/trip_chance = 0 - - var/trip_stun = 0 - var/trip_weaken = 0 - var/trip_any = FALSE - var/trip_walksafe = TRUE - var/trip_tiles = 0 - - //Tooltip vars - var/in_inventory = FALSE //is this item equipped into an inventory slot or hand of a mob? - var/tip_timer = 0 - -/obj/item/New() - ..() - for(var/path in actions_types) - new path(src, action_icon[path], action_icon_state[path]) - - if(!hitsound) - if(damtype == "fire") - hitsound = 'sound/items/welder.ogg' - if(damtype == "brute") - hitsound = "swing_hit" - -/obj/item/Destroy() - flags &= ~DROPDEL //prevent reqdels - QDEL_NULL(hidden_uplink) - if(ismob(loc)) - var/mob/m = loc - m.unEquip(src, 1) - QDEL_LIST(actions) - master = null - return ..() - -/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self) - if(((src in target) && !target_self) || ((!istype(target.loc, /turf)) && (!istype(target, /turf)) && (not_inside))) - return 0 - else - return 1 - -/obj/item/blob_act(obj/structure/blob/B) - if(B && B.loc == loc) - qdel(src) - -/obj/item/verb/move_to_top() - set name = "Move To Top" - set category = null - set src in oview(1) - - if(!istype(src.loc, /turf) || usr.stat || usr.restrained() ) - return - - var/turf/T = src.loc - - src.loc = null - - src.loc = T - -/obj/item/examine(mob/user) - var/size - switch(src.w_class) - if(WEIGHT_CLASS_TINY) - size = "tiny" - if(WEIGHT_CLASS_SMALL) - size = "small" - if(WEIGHT_CLASS_NORMAL) - size = "normal-sized" - if(WEIGHT_CLASS_BULKY) - size = "bulky" - if(WEIGHT_CLASS_HUGE) - size = "huge" - if(WEIGHT_CLASS_GIGANTIC) - size = "gigantic" - - . = ..(user, "", "It is a [size] item.") - - if(user.research_scanner) //Mob has a research scanner active. - var/msg = "*--------*
    " - - if(origin_tech) - msg += "Testing potentials:
    " - var/list/techlvls = params2list(origin_tech) - for(var/T in techlvls) //This needs to use the better names. - msg += "Tech: [CallTechName(T)] | Magnitude: [techlvls[T]]
    " - else - msg += "No tech origins detected.
    " - - - if(materials.len) - msg += "Extractable materials:
    " - for(var/mat in materials) - msg += "[CallMaterialName(mat)]
    " //Capitize first word, remove the "$" - else - msg += "No extractable materials detected.
    " - msg += "*--------*" - . += msg - -/obj/item/burn() - if(!QDELETED(src)) - var/turf/T = get_turf(src) - var/obj/effect/decal/cleanable/ash/A = new(T) - A.desc += "\nLooks like this used to be \an [name] some time ago." - ..() - -/obj/item/acid_melt() - if(!QDELETED(src)) - var/turf/T = get_turf(src) - var/obj/effect/decal/cleanable/molten_object/MO = new(T) - MO.pixel_x = rand(-16,16) - MO.pixel_y = rand(-16,16) - MO.desc = "Looks like this was \an [src] some time ago." - ..() - -/obj/item/afterattack(atom/target, mob/user, proximity, params) - SEND_SIGNAL(src, COMSIG_ITEM_AFTERATTACK, target, user, proximity, params) - ..() - -/obj/item/attack_hand(mob/user as mob, pickupfireoverride = FALSE) - if(!user) return 0 - if(hasorgans(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/external/temp = H.bodyparts_by_name["r_hand"] - if(user.hand) - temp = H.bodyparts_by_name["l_hand"] - if(!temp) - to_chat(user, "You try to use your hand, but it's missing!") - return 0 - if(temp && !temp.is_usable()) - to_chat(user, "You try to move your [temp.name], but cannot!") - return 0 - - if((resistance_flags & ON_FIRE) && !pickupfireoverride) - var/mob/living/carbon/human/H = user - if(istype(H)) - if(H.gloves && (H.gloves.max_heat_protection_temperature > 360)) - extinguish() - to_chat(user, "You put out the fire on [src].") - else - to_chat(user, "You burn your hand on [src]!") - var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_arm") - if(affecting && affecting.receive_damage(0, 5)) // 5 burn damage - H.UpdateDamageIcon() - return - else - extinguish() - - if(acid_level > 20 && !ismob(loc))// so we can still remove the clothes on us that have acid. - var/mob/living/carbon/human/H = user - if(istype(H)) - if(!H.gloves || (!(H.gloves.resistance_flags & (UNACIDABLE|ACID_PROOF)))) - to_chat(user, "The acid on [src] burns your hand!") - var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_arm") - if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage - H.UpdateDamageIcon() - - if(istype(src.loc, /obj/item/storage)) - //If the item is in a storage item, take it out - var/obj/item/storage/S = src.loc - S.remove_from_storage(src) - - if(throwing) - throwing.finalize(FALSE) - if(loc == user) - if(!user.unEquip(src)) - return 0 - - else - if(isliving(loc)) - return 0 - add_fingerprint(user) - if(pickup(user)) // Pickup succeeded - user.put_in_active_hand(src) - - return 1 - -/obj/item/attack_alien(mob/user) - var/mob/living/carbon/alien/A = user - - if(!A.has_fine_manipulation) - if(src in A.contents) // To stop Aliens having items stuck in their pockets - A.unEquip(src) - to_chat(user, "Your claws aren't capable of such fine manipulation!") - return - attack_hand(A) - -/obj/item/attack_ai(mob/user as mob) - if(istype(src.loc, /obj/item/robot_module)) - //If the item is part of a cyborg module, equip it - if(!isrobot(user)) - return - var/mob/living/silicon/robot/R = user - if(!R.low_power_mode) //can't equip modules with an empty cell. - R.activate_module(src) - R.hud_used.update_robot_modules_display() - -// Due to storage type consolidation this should get used more now. -// I have cleaned it up a little, but it could probably use more. -Sayu -/obj/item/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/storage)) - var/obj/item/storage/S = I - if(S.use_to_pickup) - if(S.collection_mode) //Mode is set to collect all items on a tile and we clicked on a valid one. - if(isturf(loc)) - var/list/rejections = list() - var/success = 0 - var/failure = 0 - - for(var/obj/item/IT in loc) - if(IT.type in rejections) // To limit bag spamming: any given type only complains once - continue - if(!S.can_be_inserted(IT)) // Note can_be_inserted still makes noise when the answer is no - rejections += IT.type // therefore full bags are still a little spammy - failure = 1 - continue - success = 1 - S.handle_item_insertion(IT, 1) //The 1 stops the "You put the [src] into [S]" insertion message from being displayed. - if(success && !failure) - to_chat(user, "You put everything in [S].") - else if(success) - to_chat(user, "You put some things in [S].") - else - to_chat(user, "You fail to pick anything up with [S].") - - else if(S.can_be_inserted(src)) - S.handle_item_insertion(src) - else if(istype(I, /obj/item/stack/tape_roll)) - if(istype(src, /obj/item/storage)) //Don't tape the bag if we can put the duct tape inside it instead - var/obj/item/storage/bag = src - if(bag.can_be_inserted(I)) - return ..() - var/obj/item/stack/tape_roll/TR = I - var/list/clickparams = params2list(params) - var/x_offset = text2num(clickparams["icon-x"]) - var/y_offset = text2num(clickparams["icon-y"]) - if(GetComponent(/datum/component/ducttape)) - to_chat(user, "[src] already has some tape attached!") - return - if(TR.use(1)) - to_chat(user, "You apply some tape to [src].") - AddComponent(/datum/component/ducttape, src, user, x_offset, y_offset) - anchored = TRUE - user.transfer_fingerprints_to(src) - else - to_chat(user, "You don't have enough tape to do that!") - else - return ..() - -/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, args) - if(prob(final_block_chance)) - owner.visible_message("[owner] blocks [attack_text] with [src]!") - return 1 - return 0 - -// Generic use proc. Depending on the item, it uses up fuel, charges, sheets, etc. -// Returns TRUE on success, FALSE on failure. -/obj/item/proc/use(used) - return !used - -//Generic refill proc. Transfers something (e.g. fuel, charge) from an atom to our tool. returns TRUE if it was successful, FALSE otherwise -//Not sure if there should be an argument that indicates what exactly is being refilled -/obj/item/proc/refill(mob/user, atom/A, amount) - return FALSE - -/obj/item/proc/talk_into(mob/M, var/text, var/channel=null) - return - -/obj/item/proc/dropped(mob/user) - for(var/X in actions) - var/datum/action/A = X - A.Remove(user) - if(flags & DROPDEL) - qdel(src) - if((flags & NODROP) && !(initial(flags) & NODROP)) //Remove NODROP is dropped - flags &= ~NODROP - in_inventory = FALSE - SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user) - -// called just as an item is picked up (loc is not yet changed) -/obj/item/proc/pickup(mob/user) - SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user) - in_inventory = TRUE - return TRUE - -// called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called. -/obj/item/proc/on_exit_storage(obj/item/storage/S as obj) - return - -// called when this item is added into a storage item, which is passed on as S. The loc variable is already set to the storage item. -/obj/item/proc/on_enter_storage(obj/item/storage/S as obj) - return - -// called when "found" in pockets and storage items. Returns 1 if the search should end. -/obj/item/proc/on_found(mob/finder as mob) - return - -// called when the giver gives it to the receiver -/obj/item/proc/on_give(mob/living/carbon/giver, mob/living/carbon/receiver) - return - -// called after an item is placed in an equipment slot -// user is mob that equipped it -// slot uses the slot_X defines found in setup.dm -// for items that can be placed in multiple slots -// note this isn't called during the initial dressing of a player -/obj/item/proc/equipped(var/mob/user, var/slot) - SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot) - for(var/X in actions) - var/datum/action/A = X - if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot. - A.Grant(user) - in_inventory = TRUE - -/obj/item/proc/item_action_slot_check(slot, mob/user) - return 1 - -//returns 1 if the item is equipped by a mob, 0 otherwise. -//This might need some error trapping, not sure if get_equipped_items() is safe for non-human mobs. -/obj/item/proc/is_equipped() - if(!ismob(loc)) - return 0 - - var/mob/M = loc - if(src in M.get_equipped_items()) - return 1 - else - return 0 - -//the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't. -//If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen. -//Set disable_warning to 1 if you wish it to not give you outputs. -/obj/item/proc/mob_can_equip(mob/M, slot, disable_warning = 0) - if(!M) - return 0 - - return M.can_equip(src, slot, disable_warning) - -/obj/item/verb/verb_pickup() - set src in oview(1) - set category = null - set name = "Pick up" - - if(!(usr)) //BS12 EDIT - return - if(usr.incapacitated() || !Adjacent(usr)) - return - if(!iscarbon(usr) || isbrain(usr)) //Is humanoid, and is not a brain - to_chat(usr, "You can't pick things up!") - return - if(anchored) //Object isn't anchored - to_chat(usr, "You can't pick that up!") - return - if(!usr.hand && usr.r_hand) //Right hand is not full - to_chat(usr, "Your right hand is full.") - return - if(usr.hand && usr.l_hand) //Left hand is not full - to_chat(usr, "Your left hand is full.") - return - if(!isturf(loc)) //Object is on a turf - to_chat(usr, "You can't pick that up!") - return - //All checks are done, time to pick it up! - usr.UnarmedAttack(src) - - -//This proc is executed when someone clicks the on-screen UI button. -//The default action is attack_self(). -//Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob. -/obj/item/proc/ui_action_click(mob/user, actiontype) - attack_self(user) - -/obj/item/proc/IsReflect(var/def_zone) //This proc determines if and at what% an object will reflect energy projectiles if it's in l_hand,r_hand or wear_suit - return 0 - -/obj/item/proc/get_loc_turf() - var/atom/L = loc - while(L && !istype(L, /turf/)) - L = L.loc - return loc - -/obj/item/proc/eyestab(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - - var/mob/living/carbon/human/H = M - if(istype(H) && ( \ - (H.head && H.head.flags_cover & HEADCOVERSEYES) || \ - (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) || \ - (H.glasses && H.glasses.flags_cover & GLASSESCOVERSEYES) \ - )) - // you can't stab someone in the eyes wearing a mask! - to_chat(user, "You're going to need to remove that mask/helmet/glasses first!") - return - - if(istype(M, /mob/living/carbon/alien) || istype(M, /mob/living/simple_animal/slime))//Aliens don't have eyes./N slimes also don't have eyes! - to_chat(user, "You cannot locate any eyes on this creature!") - return - - if(!iscarbon(user)) - M.LAssailant = null - else - M.LAssailant = user - - src.add_fingerprint(user) - - playsound(loc, src.hitsound, 30, 1, -1) - - user.do_attack_animation(M) - - if(M != user) - M.visible_message("[user] has stabbed [M] in the eye with [src]!", \ - "[user] stabs you in the eye with [src]!") - else - user.visible_message( \ - "[user] has stabbed [user.p_them()]self in the eyes with [src]!", \ - "You stab yourself in the eyes with [src]!" \ - ) - - add_attack_logs(user, M, "Eye-stabbed with [src] (INTENT: [uppertext(user.a_intent)])") - - if(istype(H)) - var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) - if(!eyes) // should still get stabbed in the head - var/obj/item/organ/external/head/head = H.bodyparts_by_name["head"] - head.receive_damage(rand(10,14), 1) - return - eyes.receive_damage(rand(3,4), 1) - if(eyes.damage >= eyes.min_bruised_damage) - if(M.stat != 2) - if(!eyes.is_robotic()) //robot eyes bleeding might be a bit silly - to_chat(M, "Your eyes start to bleed profusely!") - if(prob(50)) - if(M.stat != DEAD) - to_chat(M, "You drop what you're holding and clutch at your eyes!") - M.drop_item() - M.AdjustEyeBlurry(10) - M.Paralyse(1) - M.Weaken(2) - if(eyes.damage >= eyes.min_broken_damage) - if(M.stat != 2) - to_chat(M, "You go blind!") - var/obj/item/organ/external/affecting = H.get_organ("head") - if(affecting.receive_damage(7)) - H.UpdateDamageIcon() - else - M.take_organ_damage(7) - M.AdjustEyeBlurry(rand(3,4)) - return - -/obj/item/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FOUR) - throw_at(S, 14, 3, spin = 0) - else - return - -/obj/item/throw_impact(atom/A) - if(A && !QDELETED(A)) - SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, A) - var/itempush = 1 - if(w_class < WEIGHT_CLASS_BULKY) - itempush = 0 // too light to push anything - return A.hitby(src, 0, itempush) - -/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force) - thrownby = thrower - callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own - . = ..(target, range, speed, thrower, spin, diagonals_first, callback, force) - -/obj/item/proc/after_throw(datum/callback/callback) - if(callback) //call the original callback - . = callback.Invoke() - throw_speed = initial(throw_speed) //explosions change this. - in_inventory = FALSE - -/obj/item/proc/pwr_drain() - return 0 // Process Kill - -/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage - if(!newLoc) - return 0 - if(istype(loc,/obj/item/storage)) - var/obj/item/storage/S = loc - S.remove_from_storage(src,newLoc) - return 1 - return 0 - - -/obj/item/proc/wash(mob/user, atom/source) - if(flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand. - return - to_chat(user, "You start washing [src]...") - if(!do_after(user, 40, target = source)) - return - clean_blood() - acid_level = 0 - user.visible_message("[user] washes [src] using [source].", \ - "You wash [src] using [source].") - return 1 - -/obj/item/proc/is_crutch() //Does an item prop up a human mob and allow them to stand if they are missing a leg/foot? - return 0 - -// Return true if you don't want regular throw handling -/obj/item/proc/override_throw(mob/user, atom/target) - return FALSE - -/obj/item/proc/is_equivalent(obj/item/I) - return I == src - -/obj/item/Crossed(atom/movable/AM, oldloc) - . = ..() - if(prob(trip_chance) && ishuman(AM)) - var/mob/living/carbon/human/H = AM - on_trip(H) - -/obj/item/proc/on_trip(mob/living/carbon/human/H) - if(H.slip(src, trip_stun, trip_weaken, trip_tiles, trip_walksafe, trip_any, trip_verb)) - return TRUE - -/obj/item/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - return - -/obj/item/attack_hulk(mob/living/carbon/human/user) - return FALSE - -/obj/item/attack_animal(mob/living/simple_animal/M) - if(can_be_hit) - return ..() - return FALSE - -/obj/item/mech_melee_attack(obj/mecha/M) - return 0 - -/obj/item/proc/openTip(location, control, params, user) - openToolTip(user, src, params, title = name, content = "[desc]", theme = "") - -/obj/item/MouseEntered(location, control, params) - if(in_inventory) - var/timedelay = 8 - var/user = usr - tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE) - -/obj/item/MouseExited() - deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes - closeToolTip(usr) - -// Returns a numeric value for sorting items used as parts in machines, so they can be replaced by the rped -/obj/item/proc/get_part_rating() - return 0 - -/obj/item/proc/update_slot_icon() - if(!ismob(loc)) - return - var/mob/owner = loc - var/flags = slot_flags - if(flags & SLOT_OCLOTHING) - owner.update_inv_wear_suit() - if(flags & SLOT_ICLOTHING) - owner.update_inv_w_uniform() - if(flags & SLOT_GLOVES) - owner.update_inv_gloves() - if(flags & SLOT_EYES) - owner.update_inv_glasses() - if(flags & SLOT_EARS) - owner.update_inv_ears() - if(flags & SLOT_MASK) - owner.update_inv_wear_mask() - if(flags & SLOT_HEAD) - owner.update_inv_head() - if(flags & SLOT_FEET) - owner.update_inv_shoes() - if(flags & SLOT_ID) - owner.update_inv_wear_id() - if(flags & SLOT_BELT) - owner.update_inv_belt() - if(flags & SLOT_BACK) - owner.update_inv_back() - if(flags & SLOT_PDA) - owner.update_inv_wear_pda() - +GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effects/fire.dmi', "icon_state" = "fire")) +/obj/item + name = "item" + icon = 'icons/obj/items.dmi' + var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers + var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite + var/blood_overlay_color = null + var/item_state = null + var/lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + var/righthand_file = 'icons/mob/inhands/items_righthand.dmi' + + //Dimensions of the lefthand_file and righthand_file vars + //eg: 32x32 sprite, 64x64 sprite, etc. + var/inhand_x_dimension = 32 + var/inhand_y_dimension = 32 + + max_integrity = 200 + + can_be_hit = FALSE + suicidal_hands = TRUE + + var/hitsound = null + var/usesound = null + var/throwhitsound + var/w_class = WEIGHT_CLASS_NORMAL + var/slot_flags = 0 //This is used to determine on which slots an item can fit. + pass_flags = PASSTABLE + pressure_resistance = 4 +// causeerrorheresoifixthis + var/obj/item/master = null + + var/heat_protection = 0 //flags which determine which body parts are protected from heat. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm + var/cold_protection = 0 //flags which determine which body parts are protected from cold. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm + var/max_heat_protection_temperature //Set this variable to determine up to which temperature (IN KELVIN) the item protects against heat damage. Keep at null to disable protection. Only protects areas set by heat_protection flags + var/min_cold_protection_temperature //Set this variable to determine down to which temperature (IN KELVIN) the item protects against cold damage. 0 is NOT an acceptable number due to if(varname) tests!! Keep at null to disable protection. Only protects areas set by cold_protection flags + + var/list/actions = list() //list of /datum/action's that this item has. + var/list/actions_types = list() //list of paths of action datums to give to the item on New(). + var/list/action_icon = list() //list of icons-sheets for a given action to override the icon. + var/list/action_icon_state = list() //list of icon states for a given action to override the icon_state. + + var/list/materials = list() + //Since any item can now be a piece of clothing, this has to be put here so all items share it. + var/flags_inv //This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc. + var/item_color = null + var/body_parts_covered = 0 //see setup.dm for appropriate bit flags + //var/heat_transfer_coefficient = 1 //0 prevents all transfers, 1 is invisible + var/gas_transfer_coefficient = 1 // for leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets) + var/permeability_coefficient = 1 // for chemicals/diseases + var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit) + var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up + var/armour_penetration = 0 //percentage of armour effectiveness to remove + var/list/allowed = null //suit storage stuff. + var/obj/item/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers. + + var/needs_permit = 0 //Used by security bots to determine if this item is safe for public use. + + var/strip_delay = DEFAULT_ITEM_STRIP_DELAY + var/put_on_delay = DEFAULT_ITEM_PUTON_DELAY + var/breakouttime = 0 + var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES + var/flags_size = 0 //flag, primarily used for clothing to determine if a fatty can wear something or not. + + var/block_chance = 0 + var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom + + // Needs to be in /obj/item because corgis can wear a lot of + // non-clothing items + var/datum/dog_fashion/dog_fashion = null + + var/mob/thrownby = null + + //So items can have custom embedd values + //Because customisation is king + var/embed_chance = EMBED_CHANCE + var/embedded_fall_chance = EMBEDDED_ITEM_FALLOUT + var/embedded_pain_chance = EMBEDDED_PAIN_CHANCE + var/embedded_pain_multiplier = EMBEDDED_PAIN_MULTIPLIER //The coefficient of multiplication for the damage this item does while embedded (this*w_class) + var/embedded_fall_pain_multiplier = EMBEDDED_FALL_PAIN_MULTIPLIER //The coefficient of multiplication for the damage this item does when falling out of a limb (this*w_class) + var/embedded_impact_pain_multiplier = EMBEDDED_IMPACT_PAIN_MULTIPLIER //The coefficient of multiplication for the damage this item does when first embedded (this*w_class) + var/embedded_unsafe_removal_pain_multiplier = EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER //The coefficient of multiplication for the damage removing this without surgery causes (this*w_class) + var/embedded_unsafe_removal_time = EMBEDDED_UNSAFE_REMOVAL_TIME //A time in ticks, multiplied by the w_class. + var/embedded_ignore_throwspeed_threshold = FALSE + + var/tool_behaviour = NONE //What kind of tool are we? + var/tool_enabled = TRUE //If we can turn on or off, are we currently active? Mostly for welders and this will normally be TRUE + var/tool_volume = 50 //How loud are we when we use our tool? + var/toolspeed = 1 // If this item is a tool, the speed multiplier + + /* Species-specific sprites, concept stolen from Paradise//vg/. + ex: + sprite_sheets = list( + "Tajaran" = 'icons/cat/are/bad' + ) + If index term exists and icon_override is not set, this sprite sheet will be used. + */ + var/list/sprite_sheets = null + var/list/sprite_sheets_inhand = null //Used to override inhand items. Use a single .dmi and suffix the icon states inside with _l and _r for each hand. + var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc. + var/sprite_sheets_obj = null //Used to override hardcoded clothing inventory object dmis in human clothing proc. + + var/trip_verb = TV_TRIP + var/trip_chance = 0 + + var/trip_stun = 0 + var/trip_weaken = 0 + var/trip_any = FALSE + var/trip_walksafe = TRUE + var/trip_tiles = 0 + + //Tooltip vars + var/in_inventory = FALSE //is this item equipped into an inventory slot or hand of a mob? + var/tip_timer = 0 + +/obj/item/New() + ..() + for(var/path in actions_types) + new path(src, action_icon[path], action_icon_state[path]) + + if(!hitsound) + if(damtype == "fire") + hitsound = 'sound/items/welder.ogg' + if(damtype == "brute") + hitsound = "swing_hit" + +/obj/item/Destroy() + flags &= ~DROPDEL //prevent reqdels + QDEL_NULL(hidden_uplink) + if(ismob(loc)) + var/mob/m = loc + m.unEquip(src, 1) + QDEL_LIST(actions) + master = null + return ..() + +/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self) + if(((src in target) && !target_self) || ((!istype(target.loc, /turf)) && (!istype(target, /turf)) && (not_inside))) + return 0 + else + return 1 + +/obj/item/blob_act(obj/structure/blob/B) + if(B && B.loc == loc) + qdel(src) + +/obj/item/verb/move_to_top() + set name = "Move To Top" + set category = null + set src in oview(1) + + if(!istype(src.loc, /turf) || usr.stat || usr.restrained() ) + return + + var/turf/T = src.loc + + src.loc = null + + src.loc = T + +/obj/item/examine(mob/user) + var/size + switch(src.w_class) + if(WEIGHT_CLASS_TINY) + size = "tiny" + if(WEIGHT_CLASS_SMALL) + size = "small" + if(WEIGHT_CLASS_NORMAL) + size = "normal-sized" + if(WEIGHT_CLASS_BULKY) + size = "bulky" + if(WEIGHT_CLASS_HUGE) + size = "huge" + if(WEIGHT_CLASS_GIGANTIC) + size = "gigantic" + + . = ..(user, "", "It is a [size] item.") + + if(user.research_scanner) //Mob has a research scanner active. + var/msg = "*--------*
    " + + if(origin_tech) + msg += "Testing potentials:
    " + var/list/techlvls = params2list(origin_tech) + for(var/T in techlvls) //This needs to use the better names. + msg += "Tech: [CallTechName(T)] | Magnitude: [techlvls[T]]
    " + else + msg += "No tech origins detected.
    " + + + if(materials.len) + msg += "Extractable materials:
    " + for(var/mat in materials) + msg += "[CallMaterialName(mat)]
    " //Capitize first word, remove the "$" + else + msg += "No extractable materials detected.
    " + msg += "*--------*" + . += msg + +/obj/item/burn() + if(!QDELETED(src)) + var/turf/T = get_turf(src) + var/obj/effect/decal/cleanable/ash/A = new(T) + A.desc += "\nLooks like this used to be \an [name] some time ago." + ..() + +/obj/item/acid_melt() + if(!QDELETED(src)) + var/turf/T = get_turf(src) + var/obj/effect/decal/cleanable/molten_object/MO = new(T) + MO.pixel_x = rand(-16,16) + MO.pixel_y = rand(-16,16) + MO.desc = "Looks like this was \an [src] some time ago." + ..() + +/obj/item/afterattack(atom/target, mob/user, proximity, params) + SEND_SIGNAL(src, COMSIG_ITEM_AFTERATTACK, target, user, proximity, params) + ..() + +/obj/item/attack_hand(mob/user as mob, pickupfireoverride = FALSE) + if(!user) return 0 + if(hasorgans(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/temp = H.bodyparts_by_name["r_hand"] + if(user.hand) + temp = H.bodyparts_by_name["l_hand"] + if(!temp) + to_chat(user, "You try to use your hand, but it's missing!") + return 0 + if(temp && !temp.is_usable()) + to_chat(user, "You try to move your [temp.name], but cannot!") + return 0 + + if((resistance_flags & ON_FIRE) && !pickupfireoverride) + var/mob/living/carbon/human/H = user + if(istype(H)) + if(H.gloves && (H.gloves.max_heat_protection_temperature > 360)) + extinguish() + to_chat(user, "You put out the fire on [src].") + else + to_chat(user, "You burn your hand on [src]!") + var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_arm") + if(affecting && affecting.receive_damage(0, 5)) // 5 burn damage + H.UpdateDamageIcon() + return + else + extinguish() + + if(acid_level > 20 && !ismob(loc))// so we can still remove the clothes on us that have acid. + var/mob/living/carbon/human/H = user + if(istype(H)) + if(!H.gloves || (!(H.gloves.resistance_flags & (UNACIDABLE|ACID_PROOF)))) + to_chat(user, "The acid on [src] burns your hand!") + var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_arm") + if(affecting && affecting.receive_damage( 0, 5 )) // 5 burn damage + H.UpdateDamageIcon() + + if(istype(src.loc, /obj/item/storage)) + //If the item is in a storage item, take it out + var/obj/item/storage/S = src.loc + S.remove_from_storage(src) + + if(throwing) + throwing.finalize(FALSE) + if(loc == user) + if(!user.unEquip(src)) + return 0 + + else + if(isliving(loc)) + return 0 + add_fingerprint(user) + if(pickup(user)) // Pickup succeeded + user.put_in_active_hand(src) + + return 1 + +/obj/item/attack_alien(mob/user) + var/mob/living/carbon/alien/A = user + + if(!A.has_fine_manipulation) + if(src in A.contents) // To stop Aliens having items stuck in their pockets + A.unEquip(src) + to_chat(user, "Your claws aren't capable of such fine manipulation!") + return + attack_hand(A) + +/obj/item/attack_ai(mob/user as mob) + if(istype(src.loc, /obj/item/robot_module)) + //If the item is part of a cyborg module, equip it + if(!isrobot(user)) + return + var/mob/living/silicon/robot/R = user + if(!R.low_power_mode) //can't equip modules with an empty cell. + R.activate_module(src) + R.hud_used.update_robot_modules_display() + +// Due to storage type consolidation this should get used more now. +// I have cleaned it up a little, but it could probably use more. -Sayu +/obj/item/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/storage)) + var/obj/item/storage/S = I + if(S.use_to_pickup) + if(S.collection_mode) //Mode is set to collect all items on a tile and we clicked on a valid one. + if(isturf(loc)) + var/list/rejections = list() + var/success = 0 + var/failure = 0 + + for(var/obj/item/IT in loc) + if(IT.type in rejections) // To limit bag spamming: any given type only complains once + continue + if(!S.can_be_inserted(IT)) // Note can_be_inserted still makes noise when the answer is no + rejections += IT.type // therefore full bags are still a little spammy + failure = 1 + continue + success = 1 + S.handle_item_insertion(IT, 1) //The 1 stops the "You put the [src] into [S]" insertion message from being displayed. + if(success && !failure) + to_chat(user, "You put everything in [S].") + else if(success) + to_chat(user, "You put some things in [S].") + else + to_chat(user, "You fail to pick anything up with [S].") + + else if(S.can_be_inserted(src)) + S.handle_item_insertion(src) + else if(istype(I, /obj/item/stack/tape_roll)) + if(istype(src, /obj/item/storage)) //Don't tape the bag if we can put the duct tape inside it instead + var/obj/item/storage/bag = src + if(bag.can_be_inserted(I)) + return ..() + var/obj/item/stack/tape_roll/TR = I + var/list/clickparams = params2list(params) + var/x_offset = text2num(clickparams["icon-x"]) + var/y_offset = text2num(clickparams["icon-y"]) + if(GetComponent(/datum/component/ducttape)) + to_chat(user, "[src] already has some tape attached!") + return + if(TR.use(1)) + to_chat(user, "You apply some tape to [src].") + AddComponent(/datum/component/ducttape, src, user, x_offset, y_offset) + anchored = TRUE + user.transfer_fingerprints_to(src) + else + to_chat(user, "You don't have enough tape to do that!") + else + return ..() + +/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, args) + if(prob(final_block_chance)) + owner.visible_message("[owner] blocks [attack_text] with [src]!") + return 1 + return 0 + +// Generic use proc. Depending on the item, it uses up fuel, charges, sheets, etc. +// Returns TRUE on success, FALSE on failure. +/obj/item/proc/use(used) + return !used + +//Generic refill proc. Transfers something (e.g. fuel, charge) from an atom to our tool. returns TRUE if it was successful, FALSE otherwise +//Not sure if there should be an argument that indicates what exactly is being refilled +/obj/item/proc/refill(mob/user, atom/A, amount) + return FALSE + +/obj/item/proc/talk_into(mob/M, var/text, var/channel=null) + return + +/obj/item/proc/dropped(mob/user) + for(var/X in actions) + var/datum/action/A = X + A.Remove(user) + if(flags & DROPDEL) + qdel(src) + if((flags & NODROP) && !(initial(flags) & NODROP)) //Remove NODROP is dropped + flags &= ~NODROP + in_inventory = FALSE + SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user) + +// called just as an item is picked up (loc is not yet changed) +/obj/item/proc/pickup(mob/user) + SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user) + in_inventory = TRUE + return TRUE + +// called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called. +/obj/item/proc/on_exit_storage(obj/item/storage/S as obj) + return + +// called when this item is added into a storage item, which is passed on as S. The loc variable is already set to the storage item. +/obj/item/proc/on_enter_storage(obj/item/storage/S as obj) + return + +// called when "found" in pockets and storage items. Returns 1 if the search should end. +/obj/item/proc/on_found(mob/finder as mob) + return + +// called when the giver gives it to the receiver +/obj/item/proc/on_give(mob/living/carbon/giver, mob/living/carbon/receiver) + return + +// called after an item is placed in an equipment slot +// user is mob that equipped it +// slot uses the slot_X defines found in setup.dm +// for items that can be placed in multiple slots +// note this isn't called during the initial dressing of a player +/obj/item/proc/equipped(var/mob/user, var/slot) + SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot) + for(var/X in actions) + var/datum/action/A = X + if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot. + A.Grant(user) + in_inventory = TRUE + +/obj/item/proc/item_action_slot_check(slot, mob/user) + return 1 + +//returns 1 if the item is equipped by a mob, 0 otherwise. +//This might need some error trapping, not sure if get_equipped_items() is safe for non-human mobs. +/obj/item/proc/is_equipped() + if(!ismob(loc)) + return 0 + + var/mob/M = loc + if(src in M.get_equipped_items()) + return 1 + else + return 0 + +//the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't. +//If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen. +//Set disable_warning to 1 if you wish it to not give you outputs. +/obj/item/proc/mob_can_equip(mob/M, slot, disable_warning = 0) + if(!M) + return 0 + + return M.can_equip(src, slot, disable_warning) + +/obj/item/verb/verb_pickup() + set src in oview(1) + set category = null + set name = "Pick up" + + if(!(usr)) //BS12 EDIT + return + if(usr.incapacitated() || !Adjacent(usr)) + return + if(!iscarbon(usr) || isbrain(usr)) //Is humanoid, and is not a brain + to_chat(usr, "You can't pick things up!") + return + if(anchored) //Object isn't anchored + to_chat(usr, "You can't pick that up!") + return + if(!usr.hand && usr.r_hand) //Right hand is not full + to_chat(usr, "Your right hand is full.") + return + if(usr.hand && usr.l_hand) //Left hand is not full + to_chat(usr, "Your left hand is full.") + return + if(!isturf(loc)) //Object is on a turf + to_chat(usr, "You can't pick that up!") + return + //All checks are done, time to pick it up! + usr.UnarmedAttack(src) + + +//This proc is executed when someone clicks the on-screen UI button. +//The default action is attack_self(). +//Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob. +/obj/item/proc/ui_action_click(mob/user, actiontype) + attack_self(user) + +/obj/item/proc/IsReflect(var/def_zone) //This proc determines if and at what% an object will reflect energy projectiles if it's in l_hand,r_hand or wear_suit + return 0 + +/obj/item/proc/get_loc_turf() + var/atom/L = loc + while(L && !istype(L, /turf/)) + L = L.loc + return loc + +/obj/item/proc/eyestab(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + + var/mob/living/carbon/human/H = M + if(istype(H) && ( \ + (H.head && H.head.flags_cover & HEADCOVERSEYES) || \ + (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) || \ + (H.glasses && H.glasses.flags_cover & GLASSESCOVERSEYES) \ + )) + // you can't stab someone in the eyes wearing a mask! + to_chat(user, "You're going to need to remove that mask/helmet/glasses first!") + return + + if(istype(M, /mob/living/carbon/alien) || istype(M, /mob/living/simple_animal/slime))//Aliens don't have eyes./N slimes also don't have eyes! + to_chat(user, "You cannot locate any eyes on this creature!") + return + + if(!iscarbon(user)) + M.LAssailant = null + else + M.LAssailant = user + + src.add_fingerprint(user) + + playsound(loc, src.hitsound, 30, 1, -1) + + user.do_attack_animation(M) + + if(M != user) + M.visible_message("[user] has stabbed [M] in the eye with [src]!", \ + "[user] stabs you in the eye with [src]!") + else + user.visible_message( \ + "[user] has stabbed [user.p_them()]self in the eyes with [src]!", \ + "You stab yourself in the eyes with [src]!" \ + ) + + add_attack_logs(user, M, "Eye-stabbed with [src] (INTENT: [uppertext(user.a_intent)])") + + if(istype(H)) + var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) + if(!eyes) // should still get stabbed in the head + var/obj/item/organ/external/head/head = H.bodyparts_by_name["head"] + head.receive_damage(rand(10,14), 1) + return + eyes.receive_damage(rand(3,4), 1) + if(eyes.damage >= eyes.min_bruised_damage) + if(M.stat != 2) + if(!eyes.is_robotic()) //robot eyes bleeding might be a bit silly + to_chat(M, "Your eyes start to bleed profusely!") + if(prob(50)) + if(M.stat != DEAD) + to_chat(M, "You drop what you're holding and clutch at your eyes!") + M.drop_item() + M.AdjustEyeBlurry(10) + M.Paralyse(1) + M.Weaken(2) + if(eyes.damage >= eyes.min_broken_damage) + if(M.stat != 2) + to_chat(M, "You go blind!") + var/obj/item/organ/external/affecting = H.get_organ("head") + if(affecting.receive_damage(7)) + H.UpdateDamageIcon() + else + M.take_organ_damage(7) + M.AdjustEyeBlurry(rand(3,4)) + return + +/obj/item/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FOUR) + throw_at(S, 14, 3, spin = 0) + else + return + +/obj/item/throw_impact(atom/A) + if(A && !QDELETED(A)) + SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, A) + var/itempush = 1 + if(w_class < WEIGHT_CLASS_BULKY) + itempush = 0 // too light to push anything + return A.hitby(src, 0, itempush) + +/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force) + thrownby = thrower + callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own + . = ..(target, range, speed, thrower, spin, diagonals_first, callback, force) + +/obj/item/proc/after_throw(datum/callback/callback) + if(callback) //call the original callback + . = callback.Invoke() + throw_speed = initial(throw_speed) //explosions change this. + in_inventory = FALSE + +/obj/item/proc/pwr_drain() + return 0 // Process Kill + +/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/storage + if(!newLoc) + return 0 + if(istype(loc,/obj/item/storage)) + var/obj/item/storage/S = loc + S.remove_from_storage(src,newLoc) + return 1 + return 0 + + +/obj/item/proc/wash(mob/user, atom/source) + if(flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand. + return + to_chat(user, "You start washing [src]...") + if(!do_after(user, 40, target = source)) + return + clean_blood() + acid_level = 0 + user.visible_message("[user] washes [src] using [source].", \ + "You wash [src] using [source].") + return 1 + +/obj/item/proc/is_crutch() //Does an item prop up a human mob and allow them to stand if they are missing a leg/foot? + return 0 + +// Return true if you don't want regular throw handling +/obj/item/proc/override_throw(mob/user, atom/target) + return FALSE + +/obj/item/proc/is_equivalent(obj/item/I) + return I == src + +/obj/item/Crossed(atom/movable/AM, oldloc) + . = ..() + if(prob(trip_chance) && ishuman(AM)) + var/mob/living/carbon/human/H = AM + on_trip(H) + +/obj/item/proc/on_trip(mob/living/carbon/human/H) + if(H.slip(src, trip_stun, trip_weaken, trip_tiles, trip_walksafe, trip_any, trip_verb)) + return TRUE + +/obj/item/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + return + +/obj/item/attack_hulk(mob/living/carbon/human/user) + return FALSE + +/obj/item/attack_animal(mob/living/simple_animal/M) + if(can_be_hit) + return ..() + return FALSE + +/obj/item/mech_melee_attack(obj/mecha/M) + return 0 + +/obj/item/proc/openTip(location, control, params, user) + openToolTip(user, src, params, title = name, content = "[desc]", theme = "") + +/obj/item/MouseEntered(location, control, params) + if(in_inventory) + var/timedelay = 8 + var/user = usr + tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, user), timedelay, TIMER_STOPPABLE) + +/obj/item/MouseExited() + deltimer(tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes + closeToolTip(usr) + +// Returns a numeric value for sorting items used as parts in machines, so they can be replaced by the rped +/obj/item/proc/get_part_rating() + return 0 + +/obj/item/proc/update_slot_icon() + if(!ismob(loc)) + return + var/mob/owner = loc + var/flags = slot_flags + if(flags & SLOT_OCLOTHING) + owner.update_inv_wear_suit() + if(flags & SLOT_ICLOTHING) + owner.update_inv_w_uniform() + if(flags & SLOT_GLOVES) + owner.update_inv_gloves() + if(flags & SLOT_EYES) + owner.update_inv_glasses() + if(flags & SLOT_EARS) + owner.update_inv_ears() + if(flags & SLOT_MASK) + owner.update_inv_wear_mask() + if(flags & SLOT_HEAD) + owner.update_inv_head() + if(flags & SLOT_FEET) + owner.update_inv_shoes() + if(flags & SLOT_ID) + owner.update_inv_wear_id() + if(flags & SLOT_BELT) + owner.update_inv_belt() + if(flags & SLOT_BACK) + owner.update_inv_back() + if(flags & SLOT_PDA) + owner.update_inv_wear_pda() + diff --git a/code/game/objects/items/ashtray.dm b/code/game/objects/items/ashtray.dm index 8ca65d7a668e..5cf65188901a 100644 --- a/code/game/objects/items/ashtray.dm +++ b/code/game/objects/items/ashtray.dm @@ -91,4 +91,4 @@ max_butts = 12 max_integrity = 12 materials = list(MAT_GLASS=60) - throwforce = 6 \ No newline at end of file + throwforce = 6 diff --git a/code/game/objects/items/blueprints.dm b/code/game/objects/items/blueprints.dm index 79cc221ee7f4..d552b3c7d8fb 100644 --- a/code/game/objects/items/blueprints.dm +++ b/code/game/objects/items/blueprints.dm @@ -294,7 +294,7 @@ return ROOM_ERR_TOOLARGE var/turf/T = pending[1] //why byond havent list::pop()? pending -= T - for(var/dir in cardinal) + for(var/dir in GLOB.cardinal) var/skip = 0 for(var/obj/structure/window/W in T) if(dir == W.dir || (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST))) diff --git a/code/game/objects/items/bodybag.dm b/code/game/objects/items/bodybag.dm index e68251f09bd2..5c63dbc709cf 100644 --- a/code/game/objects/items/bodybag.dm +++ b/code/game/objects/items/bodybag.dm @@ -1,77 +1,77 @@ -//Also contains /obj/structure/closet/body_bag because I doubt anyone would think to look for bodybags in /object/structures - -/obj/item/bodybag - name = "body bag" - desc = "A folded bag designed for the storage and transportation of cadavers." - icon = 'icons/obj/bodybag.dmi' - icon_state = "bodybag_folded" - w_class = WEIGHT_CLASS_SMALL - - attack_self(mob/user) - var/obj/structure/closet/body_bag/R = new /obj/structure/closet/body_bag(user.loc) - R.add_fingerprint(user) - qdel(src) - -/obj/structure/closet/body_bag - name = "body bag" - desc = "A plastic bag designed for the storage and transportation of cadavers." - icon = 'icons/obj/bodybag.dmi' - icon_state = "bodybag_closed" - icon_closed = "bodybag_closed" - icon_opened = "bodybag_open" - sound = 'sound/items/zip.ogg' - var/item_path = /obj/item/bodybag - density = 0 - integrity_failure = 0 - - -/obj/structure/closet/body_bag/attackby(W as obj, mob/user as mob, params) - if(istype(W, /obj/item/pen)) - var/t = input(user, "What would you like the label to be?", text("[]", src.name), null) as text - if(user.get_active_hand() != W) - return - if(!in_range(src, user) && src.loc != user) - return - t = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - if(t) - src.name = "body bag - " - src.name += t - src.overlays += image(src.icon, "bodybag_label") - else - src.name = "body bag" - return - if(istype(W, /obj/item/wirecutters)) - to_chat(user, "You cut the tag off the bodybag") - src.name = "body bag" - src.overlays.Cut() - return - return ..() - - -/obj/structure/closet/body_bag/close() - if(..()) - density = 0 - return 1 - return 0 - - -/obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location) - ..() - if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) - if(!ishuman(usr)) return - if(opened) return 0 - if(contents.len) return 0 - visible_message("[usr] folds up the [src.name]") - new item_path(get_turf(src)) - spawn(0) - qdel(src) - return - -/obj/structure/closet/body_bag/relaymove(mob/user as mob) - if(user.stat) - return - - // Make it possible to escape from bodybags in morgues and crematoriums - if(loc && (isturf(loc) || istype(loc, /obj/structure/morgue) || istype(loc, /obj/structure/crematorium))) - if(!open()) - to_chat(user, "It won't budge!") +//Also contains /obj/structure/closet/body_bag because I doubt anyone would think to look for bodybags in /object/structures + +/obj/item/bodybag + name = "body bag" + desc = "A folded bag designed for the storage and transportation of cadavers." + icon = 'icons/obj/bodybag.dmi' + icon_state = "bodybag_folded" + w_class = WEIGHT_CLASS_SMALL + + attack_self(mob/user) + var/obj/structure/closet/body_bag/R = new /obj/structure/closet/body_bag(user.loc) + R.add_fingerprint(user) + qdel(src) + +/obj/structure/closet/body_bag + name = "body bag" + desc = "A plastic bag designed for the storage and transportation of cadavers." + icon = 'icons/obj/bodybag.dmi' + icon_state = "bodybag_closed" + icon_closed = "bodybag_closed" + icon_opened = "bodybag_open" + sound = 'sound/items/zip.ogg' + var/item_path = /obj/item/bodybag + density = 0 + integrity_failure = 0 + + +/obj/structure/closet/body_bag/attackby(W as obj, mob/user as mob, params) + if(istype(W, /obj/item/pen)) + var/t = input(user, "What would you like the label to be?", text("[]", src.name), null) as text + if(user.get_active_hand() != W) + return + if(!in_range(src, user) && src.loc != user) + return + t = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if(t) + src.name = "body bag - " + src.name += t + src.overlays += image(src.icon, "bodybag_label") + else + src.name = "body bag" + return + if(istype(W, /obj/item/wirecutters)) + to_chat(user, "You cut the tag off the bodybag") + src.name = "body bag" + src.overlays.Cut() + return + return ..() + + +/obj/structure/closet/body_bag/close() + if(..()) + density = 0 + return 1 + return 0 + + +/obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location) + ..() + if((over_object == usr && (in_range(src, usr) || usr.contents.Find(src)))) + if(!ishuman(usr)) return + if(opened) return 0 + if(contents.len) return 0 + visible_message("[usr] folds up the [src.name]") + new item_path(get_turf(src)) + spawn(0) + qdel(src) + return + +/obj/structure/closet/body_bag/relaymove(mob/user as mob) + if(user.stat) + return + + // Make it possible to escape from bodybags in morgues and crematoriums + if(loc && (isturf(loc) || istype(loc, /obj/structure/morgue) || istype(loc, /obj/structure/crematorium))) + if(!open()) + to_chat(user, "It won't budge!") diff --git a/code/game/objects/items/candle.dm b/code/game/objects/items/candle.dm index ca142e6f8be9..48e72a2cf9b3 100644 --- a/code/game/objects/items/candle.dm +++ b/code/game/objects/items/candle.dm @@ -1,86 +1,86 @@ -/obj/item/candle - name = "red candle" - desc = "In Greek myth, Prometheus stole fire from the Gods and gave it to humankind. The jewelry he kept for himself." - icon = 'icons/obj/candle.dmi' - icon_state = "candle1" - item_state = "candle1" - w_class = WEIGHT_CLASS_TINY - var/wax = 200 - var/lit = 0 - var/infinite = 0 - var/start_lit = 0 - light_color = "#E09D37" - -/obj/item/candle/New() - ..() - if(start_lit) - // No visible message - light(show_message = 0) - -/obj/item/candle/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/candle/update_icon() - var/i - if(wax>150) - i = 1 - else if(wax>80) - i = 2 - else i = 3 - icon_state = "candle[i][lit ? "_lit" : ""]" - - -/obj/item/candle/attackby(obj/item/W, mob/user, params) - if(is_hot(W)) - light("[user] lights [src] with [W].") - return - return ..() - -/obj/item/candle/welder_act(mob/user, obj/item/I) - . = TRUE - if(I.tool_use_check(user, 0)) //Don't need to flash eyes because you are a badass - light("[user] casually lights the [name] with [I], what a badass.") - -/obj/item/candle/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - if(!lit) - light() //honk - return ..() - -/obj/item/candle/proc/light(show_message) - if(!lit) - lit = 1 - if(show_message) - usr.visible_message(show_message) - set_light(CANDLE_LUM) - START_PROCESSING(SSobj, src) - update_icon() - - -/obj/item/candle/process() - if(!lit) - return - if(!infinite) - wax-- - if(!wax) - new/obj/item/trash/candle(src.loc) - if(istype(src.loc, /mob)) - var/mob/M = src.loc - M.unEquip(src, 1) //src is being deleted anyway - qdel(src) - update_icon() - if(isturf(loc)) //start a fire if possible - var/turf/T = loc - T.hotspot_expose(700, 5) - - -/obj/item/candle/attack_self(mob/user) - if(lit) - user.visible_message("[user] snuffs out [src].") - lit = 0 - update_icon() - set_light(0) - -/obj/item/candle/eternal - desc = "A candle. This one seems to have an odd quality about the wax." - infinite = 1 +/obj/item/candle + name = "red candle" + desc = "In Greek myth, Prometheus stole fire from the Gods and gave it to humankind. The jewelry he kept for himself." + icon = 'icons/obj/candle.dmi' + icon_state = "candle1" + item_state = "candle1" + w_class = WEIGHT_CLASS_TINY + var/wax = 200 + var/lit = 0 + var/infinite = 0 + var/start_lit = 0 + light_color = "#E09D37" + +/obj/item/candle/New() + ..() + if(start_lit) + // No visible message + light(show_message = 0) + +/obj/item/candle/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/candle/update_icon() + var/i + if(wax>150) + i = 1 + else if(wax>80) + i = 2 + else i = 3 + icon_state = "candle[i][lit ? "_lit" : ""]" + + +/obj/item/candle/attackby(obj/item/W, mob/user, params) + if(is_hot(W)) + light("[user] lights [src] with [W].") + return + return ..() + +/obj/item/candle/welder_act(mob/user, obj/item/I) + . = TRUE + if(I.tool_use_check(user, 0)) //Don't need to flash eyes because you are a badass + light("[user] casually lights the [name] with [I], what a badass.") + +/obj/item/candle/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + if(!lit) + light() //honk + return ..() + +/obj/item/candle/proc/light(show_message) + if(!lit) + lit = 1 + if(show_message) + usr.visible_message(show_message) + set_light(CANDLE_LUM) + START_PROCESSING(SSobj, src) + update_icon() + + +/obj/item/candle/process() + if(!lit) + return + if(!infinite) + wax-- + if(!wax) + new/obj/item/trash/candle(src.loc) + if(istype(src.loc, /mob)) + var/mob/M = src.loc + M.unEquip(src, 1) //src is being deleted anyway + qdel(src) + update_icon() + if(isturf(loc)) //start a fire if possible + var/turf/T = loc + T.hotspot_expose(700, 5) + + +/obj/item/candle/attack_self(mob/user) + if(lit) + user.visible_message("[user] snuffs out [src].") + lit = 0 + update_icon() + set_light(0) + +/obj/item/candle/eternal + desc = "A candle. This one seems to have an odd quality about the wax." + infinite = 1 diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index 4d27e5d175bd..eeb484b9f501 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -1,287 +1,287 @@ -/* - * Crayons - */ - -/obj/item/toy/crayon - name = "crayon" - desc = "A colourful crayon. Looks tasty. Mmmm..." - icon = 'icons/obj/crayons.dmi' - icon_state = "crayonred" - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_BELT | SLOT_EARS - attack_verb = list("attacked", "coloured") - toolspeed = 1 - var/colour = "#FF0000" //RGB - var/drawtype = "rune" - var/list/graffiti = list("body","amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","up","down","left","right","heart","borgsrogue","voxpox","shitcurity","catbeast","hieroglyphs1","hieroglyphs2","hieroglyphs3","security","syndicate1","syndicate2","nanotrasen","lie","valid","arrowleft","arrowright","arrowup","arrowdown","chicken","hailcrab","brokenheart","peace","scribble","scribble2","scribble3","skrek","squish","tunnelsnake","yip","youaredead") - var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") - var/uses = 30 //0 for unlimited uses - var/instant = 0 - var/colourName = "red" //for updateIcon purposes - var/dat - var/busy = FALSE - var/list/validSurfaces = list(/turf/simulated/floor) - -/obj/item/toy/crayon/suicide_act(mob/user) - user.visible_message("[user] is jamming the [name] up [user.p_their()] nose and into [user.p_their()] brain. It looks like [user.p_theyre()] trying to commit suicide.") - return BRUTELOSS|OXYLOSS - -/obj/item/toy/crayon/New() - ..() - name = "[colourName] crayon" //Makes crayons identifiable in things like grinders - drawtype = pick(pick(graffiti), pick(letters), "rune[rand(1,10)]") - -/obj/item/toy/crayon/attack_self(mob/living/user as mob) - update_window(user) - -/obj/item/toy/crayon/proc/update_window(mob/living/user as mob) - dat += "

    Currently selected: [drawtype]


    " - dat += "Random letterPick letter" - dat += "
    " - dat += "

    Runes:


    " - dat += "Random rune" - for(var/i = 1; i <= 10; i++) - dat += "Rune[i]" - if(!((i + 1) % 3)) //3 buttons in a row - dat += "
    " - dat += "
    " - graffiti.Find() - dat += "

    Graffiti:


    " - dat += "Random graffiti" - var/c = 1 - for(var/T in graffiti) - dat += "[T]" - if(!((c + 1) % 3)) //3 buttons in a row - dat += "
    " - c++ - dat += "
    " - var/datum/browser/popup = new(user, "crayon", name, 300, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - dat = "" - -/obj/item/toy/crayon/Topic(href, href_list, hsrc) - var/temp = "a" - switch(href_list["type"]) - if("random_letter") - temp = pick(letters) - if("letter") - temp = input("Choose the letter.", "Scribbles") in letters - if("random_rune") - temp = "rune[rand(1,10)]" - if("random_graffiti") - temp = pick(graffiti) - else - temp = href_list["type"] - if((usr.restrained() || usr.stat || !usr.is_in_active_hand(src))) - return - drawtype = temp - update_window(usr) - -/obj/item/toy/crayon/afterattack(atom/target, mob/user, proximity) - if(!proximity) return - if(busy) return - if(is_type_in_list(target,validSurfaces)) - var/temp = "rune" - if(letters.Find(drawtype)) - temp = "letter" - else if(graffiti.Find(drawtype)) - temp = "graffiti" - to_chat(user, "You start drawing a [temp] on the [target.name].") - busy = TRUE - if(instant || do_after(user, 50 * toolspeed, target = target)) - var/obj/effect/decal/cleanable/crayon/C = new /obj/effect/decal/cleanable/crayon(target,colour,drawtype,temp) - C.add_hiddenprint(user) - to_chat(user, "You finish drawing [temp].") - if(uses) - uses-- - if(!uses) - to_chat(user, "You used up your [name]!") - qdel(src) - busy = FALSE - -/obj/item/toy/crayon/attack(mob/M, mob/user) - var/huffable = istype(src,/obj/item/toy/crayon/spraycan) - if(M == user) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(!H.check_has_mouth()) - to_chat(user, "You do not have a mouth!") - return - playsound(loc, 'sound/items/eatfood.ogg', 50, 0) - to_chat(user, "You take a [huffable ? "huff" : "bite"] of the [name]. Delicious!") - user.adjust_nutrition(5) - if(uses) - uses -= 5 - if(uses <= 0) - to_chat(user, "There is no more of [name] left!") - qdel(src) - else - ..() - - -/obj/item/toy/crayon/red - icon_state = "crayonred" - colour = "#DA0000" - colourName = "red" - -/obj/item/toy/crayon/orange - icon_state = "crayonorange" - colour = "#FF9300" - colourName = "orange" - -/obj/item/toy/crayon/yellow - icon_state = "crayonyellow" - colour = "#FFF200" - colourName = "yellow" - -/obj/item/toy/crayon/green - icon_state = "crayongreen" - colour = "#A8E61D" - colourName = "green" - -/obj/item/toy/crayon/blue - icon_state = "crayonblue" - colour = "#00B7EF" - colourName = "blue" - -/obj/item/toy/crayon/purple - icon_state = "crayonpurple" - colour = "#DA00FF" - colourName = "purple" - -/obj/item/toy/crayon/random/New() - icon_state = pick(list("crayonred", "crayonorange", "crayonyellow", "crayongreen", "crayonblue", "crayonpurple")) - switch(icon_state) - if("crayonred") - colour = "#DA0000" - colourName = "red" - if("crayonorange") - colour = "#FF9300" - colourName = "orange" - if("crayonyellow") - colour = "#FFF200" - colourName = "yellow" - if("crayongreen") - colour = "#A8E61D" - colourName = "green" - if("crayonblue") - colour = "#00B7EF" - colourName = "blue" - if("crayonpurple") - colour = "#DA00FF" - colourName = "purple" - ..() - -/obj/item/toy/crayon/white - icon_state = "crayonwhite" - colour = "#FFFFFF" - colourName = "white" - -/obj/item/toy/crayon/mime - icon_state = "crayonmime" - desc = "A very sad-looking crayon." - colour = "#FFFFFF" - colourName = "mime" - uses = 0 - -/obj/item/toy/crayon/mime/attack_self(mob/living/user as mob) - update_window(user) - -/obj/item/toy/crayon/mime/update_window(mob/living/user as mob) - dat += "
       Change color
    " - ..() - -/obj/item/toy/crayon/mime/Topic(href,href_list) - if(!Adjacent(usr) || usr.incapacitated()) - return - if(href_list["color"]) - if(colour != "#FFFFFF") - colour = "#FFFFFF" - else - colour = "#000000" - update_window(usr) - else - ..() - -/obj/item/toy/crayon/rainbow - icon_state = "crayonrainbow" - colour = "#FFF000" - colourName = "rainbow" - uses = 0 - -/obj/item/toy/crayon/rainbow/attack_self(mob/living/user as mob) - update_window(user) - -/obj/item/toy/crayon/rainbow/update_window(mob/living/user as mob) - dat += "
       Change color
    " - ..() - -/obj/item/toy/crayon/rainbow/Topic(href,href_list[]) - if(!Adjacent(usr) || usr.incapacitated()) - return - if(href_list["color"]) - var/temp = input(usr, "Please select colour.", "Crayon colour") as color - colour = temp - update_window(usr) - else - ..() - - -//Spraycan stuff - -/obj/item/toy/crayon/spraycan - icon_state = "spraycan_cap" - desc = "A metallic container containing tasty paint." - var/capped = 1 - instant = 1 - validSurfaces = list(/turf/simulated/floor,/turf/simulated/wall) - -/obj/item/toy/crayon/spraycan/New() - ..() - name = "Nanotrasen-brand Rapid Paint Applicator" - update_icon() - -/obj/item/toy/crayon/spraycan/attack_self(mob/living/user as mob) - var/choice = input(user,"Spraycan options") in list("Toggle Cap","Change Drawing","Change Color") - switch(choice) - if("Toggle Cap") - to_chat(user, "You [capped ? "Remove" : "Replace"] the cap of the [src]") - capped = capped ? 0 : 1 - icon_state = "spraycan[capped ? "_cap" : ""]" - update_icon() - if("Change Drawing") - ..() - if("Change Color") - colour = input(user,"Choose Color") as color - update_icon() - -/obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user as mob, proximity) - if(!proximity) - return - if(capped) - return - else - if(iscarbon(target)) - if(uses-10 > 0) - uses = uses - 10 - var/mob/living/carbon/human/C = target - user.visible_message(" [user] sprays [src] into the face of [target]!") - if(C.client) - C.EyeBlurry(3) - C.EyeBlind(1) - if(C.check_eye_prot() <= 0) // no eye protection? ARGH IT BURNS. - C.Confused(3) - C.Weaken(3) - C.lip_style = "spray_face" - C.lip_color = colour - C.update_body() - playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) - ..() - -/obj/item/toy/crayon/spraycan/update_icon() - overlays.Cut() - var/image/I = image('icons/obj/crayons.dmi',icon_state = "[capped ? "spraycan_cap_colors" : "spraycan_colors"]") - I.color = colour - overlays += I +/* + * Crayons + */ + +/obj/item/toy/crayon + name = "crayon" + desc = "A colourful crayon. Looks tasty. Mmmm..." + icon = 'icons/obj/crayons.dmi' + icon_state = "crayonred" + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_BELT | SLOT_EARS + attack_verb = list("attacked", "coloured") + toolspeed = 1 + var/colour = "#FF0000" //RGB + var/drawtype = "rune" + var/list/graffiti = list("body","amyjon","face","matt","revolution","engie","guy","end","dwarf","uboa","up","down","left","right","heart","borgsrogue","voxpox","shitcurity","catbeast","hieroglyphs1","hieroglyphs2","hieroglyphs3","security","syndicate1","syndicate2","nanotrasen","lie","valid","arrowleft","arrowright","arrowup","arrowdown","chicken","hailcrab","brokenheart","peace","scribble","scribble2","scribble3","skrek","squish","tunnelsnake","yip","youaredead") + var/list/letters = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") + var/uses = 30 //0 for unlimited uses + var/instant = 0 + var/colourName = "red" //for updateIcon purposes + var/dat + var/busy = FALSE + var/list/validSurfaces = list(/turf/simulated/floor) + +/obj/item/toy/crayon/suicide_act(mob/user) + user.visible_message("[user] is jamming the [name] up [user.p_their()] nose and into [user.p_their()] brain. It looks like [user.p_theyre()] trying to commit suicide.") + return BRUTELOSS|OXYLOSS + +/obj/item/toy/crayon/New() + ..() + name = "[colourName] crayon" //Makes crayons identifiable in things like grinders + drawtype = pick(pick(graffiti), pick(letters), "rune[rand(1,10)]") + +/obj/item/toy/crayon/attack_self(mob/living/user as mob) + update_window(user) + +/obj/item/toy/crayon/proc/update_window(mob/living/user as mob) + dat += "

    Currently selected: [drawtype]


    " + dat += "Random letterPick letter" + dat += "
    " + dat += "

    Runes:


    " + dat += "Random rune" + for(var/i = 1; i <= 10; i++) + dat += "Rune[i]" + if(!((i + 1) % 3)) //3 buttons in a row + dat += "
    " + dat += "
    " + graffiti.Find() + dat += "

    Graffiti:


    " + dat += "Random graffiti" + var/c = 1 + for(var/T in graffiti) + dat += "[T]" + if(!((c + 1) % 3)) //3 buttons in a row + dat += "
    " + c++ + dat += "
    " + var/datum/browser/popup = new(user, "crayon", name, 300, 500) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + dat = "" + +/obj/item/toy/crayon/Topic(href, href_list, hsrc) + var/temp = "a" + switch(href_list["type"]) + if("random_letter") + temp = pick(letters) + if("letter") + temp = input("Choose the letter.", "Scribbles") in letters + if("random_rune") + temp = "rune[rand(1,10)]" + if("random_graffiti") + temp = pick(graffiti) + else + temp = href_list["type"] + if((usr.restrained() || usr.stat || !usr.is_in_active_hand(src))) + return + drawtype = temp + update_window(usr) + +/obj/item/toy/crayon/afterattack(atom/target, mob/user, proximity) + if(!proximity) return + if(busy) return + if(is_type_in_list(target,validSurfaces)) + var/temp = "rune" + if(letters.Find(drawtype)) + temp = "letter" + else if(graffiti.Find(drawtype)) + temp = "graffiti" + to_chat(user, "You start drawing a [temp] on the [target.name].") + busy = TRUE + if(instant || do_after(user, 50 * toolspeed, target = target)) + var/obj/effect/decal/cleanable/crayon/C = new /obj/effect/decal/cleanable/crayon(target,colour,drawtype,temp) + C.add_hiddenprint(user) + to_chat(user, "You finish drawing [temp].") + if(uses) + uses-- + if(!uses) + to_chat(user, "You used up your [name]!") + qdel(src) + busy = FALSE + +/obj/item/toy/crayon/attack(mob/M, mob/user) + var/huffable = istype(src,/obj/item/toy/crayon/spraycan) + if(M == user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(!H.check_has_mouth()) + to_chat(user, "You do not have a mouth!") + return + playsound(loc, 'sound/items/eatfood.ogg', 50, 0) + to_chat(user, "You take a [huffable ? "huff" : "bite"] of the [name]. Delicious!") + user.adjust_nutrition(5) + if(uses) + uses -= 5 + if(uses <= 0) + to_chat(user, "There is no more of [name] left!") + qdel(src) + else + ..() + + +/obj/item/toy/crayon/red + icon_state = "crayonred" + colour = "#DA0000" + colourName = "red" + +/obj/item/toy/crayon/orange + icon_state = "crayonorange" + colour = "#FF9300" + colourName = "orange" + +/obj/item/toy/crayon/yellow + icon_state = "crayonyellow" + colour = "#FFF200" + colourName = "yellow" + +/obj/item/toy/crayon/green + icon_state = "crayongreen" + colour = "#A8E61D" + colourName = "green" + +/obj/item/toy/crayon/blue + icon_state = "crayonblue" + colour = "#00B7EF" + colourName = "blue" + +/obj/item/toy/crayon/purple + icon_state = "crayonpurple" + colour = "#DA00FF" + colourName = "purple" + +/obj/item/toy/crayon/random/New() + icon_state = pick(list("crayonred", "crayonorange", "crayonyellow", "crayongreen", "crayonblue", "crayonpurple")) + switch(icon_state) + if("crayonred") + colour = "#DA0000" + colourName = "red" + if("crayonorange") + colour = "#FF9300" + colourName = "orange" + if("crayonyellow") + colour = "#FFF200" + colourName = "yellow" + if("crayongreen") + colour = "#A8E61D" + colourName = "green" + if("crayonblue") + colour = "#00B7EF" + colourName = "blue" + if("crayonpurple") + colour = "#DA00FF" + colourName = "purple" + ..() + +/obj/item/toy/crayon/white + icon_state = "crayonwhite" + colour = "#FFFFFF" + colourName = "white" + +/obj/item/toy/crayon/mime + icon_state = "crayonmime" + desc = "A very sad-looking crayon." + colour = "#FFFFFF" + colourName = "mime" + uses = 0 + +/obj/item/toy/crayon/mime/attack_self(mob/living/user as mob) + update_window(user) + +/obj/item/toy/crayon/mime/update_window(mob/living/user as mob) + dat += "
       Change color
    " + ..() + +/obj/item/toy/crayon/mime/Topic(href,href_list) + if(!Adjacent(usr) || usr.incapacitated()) + return + if(href_list["color"]) + if(colour != "#FFFFFF") + colour = "#FFFFFF" + else + colour = "#000000" + update_window(usr) + else + ..() + +/obj/item/toy/crayon/rainbow + icon_state = "crayonrainbow" + colour = "#FFF000" + colourName = "rainbow" + uses = 0 + +/obj/item/toy/crayon/rainbow/attack_self(mob/living/user as mob) + update_window(user) + +/obj/item/toy/crayon/rainbow/update_window(mob/living/user as mob) + dat += "
       Change color
    " + ..() + +/obj/item/toy/crayon/rainbow/Topic(href,href_list[]) + if(!Adjacent(usr) || usr.incapacitated()) + return + if(href_list["color"]) + var/temp = input(usr, "Please select colour.", "Crayon colour") as color + colour = temp + update_window(usr) + else + ..() + + +//Spraycan stuff + +/obj/item/toy/crayon/spraycan + icon_state = "spraycan_cap" + desc = "A metallic container containing tasty paint." + var/capped = 1 + instant = 1 + validSurfaces = list(/turf/simulated/floor,/turf/simulated/wall) + +/obj/item/toy/crayon/spraycan/New() + ..() + name = "Nanotrasen-brand Rapid Paint Applicator" + update_icon() + +/obj/item/toy/crayon/spraycan/attack_self(mob/living/user as mob) + var/choice = input(user,"Spraycan options") in list("Toggle Cap","Change Drawing","Change Color") + switch(choice) + if("Toggle Cap") + to_chat(user, "You [capped ? "Remove" : "Replace"] the cap of the [src]") + capped = capped ? 0 : 1 + icon_state = "spraycan[capped ? "_cap" : ""]" + update_icon() + if("Change Drawing") + ..() + if("Change Color") + colour = input(user,"Choose Color") as color + update_icon() + +/obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user as mob, proximity) + if(!proximity) + return + if(capped) + return + else + if(iscarbon(target)) + if(uses-10 > 0) + uses = uses - 10 + var/mob/living/carbon/human/C = target + user.visible_message(" [user] sprays [src] into the face of [target]!") + if(C.client) + C.EyeBlurry(3) + C.EyeBlind(1) + if(C.check_eye_prot() <= 0) // no eye protection? ARGH IT BURNS. + C.Confused(3) + C.Weaken(3) + C.lip_style = "spray_face" + C.lip_color = colour + C.update_body() + playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5) + ..() + +/obj/item/toy/crayon/spraycan/update_icon() + overlays.Cut() + var/image/I = image('icons/obj/crayons.dmi',icon_state = "[capped ? "spraycan_cap_colors" : "spraycan_colors"]") + I.color = colour + overlays += I diff --git a/code/game/objects/items/dehy_carp.dm b/code/game/objects/items/dehy_carp.dm index 2e25dd03ee15..1c30c4c5184f 100644 --- a/code/game/objects/items/dehy_carp.dm +++ b/code/game/objects/items/dehy_carp.dm @@ -49,4 +49,4 @@ var/mob/living/simple_animal/hostile/carp/C = new /mob/living/simple_animal/hostile/carp(get_turf(src)) // Make carp non-hostile to user, yes this means C.faction |= list("syndicate", "\ref[owner]") - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/devices/aicard.dm b/code/game/objects/items/devices/aicard.dm index ce8e0fe1ce07..093f28692ecb 100644 --- a/code/game/objects/items/devices/aicard.dm +++ b/code/game/objects/items/devices/aicard.dm @@ -1,107 +1,107 @@ -/obj/item/aicard - name = "inteliCard" - icon = 'icons/obj/aicards.dmi' - icon_state = "aicard" // aicard-full - item_state = "electronic" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - flags = NOBLUDGEON - var/flush = null - origin_tech = "programming=3;materials=3" - - -/obj/item/aicard/afterattack(atom/target, mob/user, proximity) - ..() - if(!proximity || !target) - return - var/mob/living/silicon/ai/AI = locate(/mob/living/silicon/ai) in src - if(AI) //AI is on the card, implies user wants to upload it. - target.transfer_ai(AI_TRANS_FROM_CARD, user, AI, src) - add_attack_logs(user, AI, "Carded with [src]") - else //No AI on the card, therefore the user wants to download one. - target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) - update_state() //Whatever happened, update the card's state (icon, name) to match. - -/obj/item/aicard/proc/update_state() - var/mob/living/silicon/ai/AI = locate(/mob/living/silicon/ai) in src //AI is inside. - if(AI) - name = "intelliCard - [AI.name]" - if(AI.stat == DEAD) - icon_state = "aicard-404" - else - icon_state = "aicard-full" - AI.cancel_camera() //AI are forced to move when transferred, so do this whenver one is downloaded. - else - icon_state = "aicard" - name = "intelliCard" - overlays.Cut() - -/obj/item/aicard/attack_self(mob/user) - ui_interact(user) - - -/obj/item/aicard/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "aicard.tmpl", "[name]", 600, 400, state = state) - ui.open() - ui.set_auto_update(1) - - -/obj/item/aicard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) - var/data[0] - - var/mob/living/silicon/ai/AI = locate() in src - if(istype(AI)) - data["has_ai"] = 1 - data["name"] = AI.name - data["hardware_integrity"] = ((AI.health + 100) / 2) - data["radio"] = !AI.aiRadio.disabledAi - data["wireless"] = !AI.control_disabled - data["operational"] = AI.stat != DEAD - data["flushing"] = flush - - var/laws[0] - for(var/datum/ai_law/AL in AI.laws.all_laws()) - laws[++laws.len] = list("index" = AL.get_index(), "law" = sanitize(AL.law)) - data["laws"] = laws - data["has_laws"] = laws.len - - return data - - -/obj/item/aicard/Topic(href, href_list, nowindow, state) - if(..()) - return 1 - - var/mob/living/silicon/ai/AI = locate() in src - if(!istype(AI)) - return 1 - - var/user = usr - - if(href_list["wipe"]) - var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No") - if(confirm == "Yes" && (CanUseTopic(user, state) == STATUS_INTERACTIVE)) - msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].", ATKLOG_FEW) - add_attack_logs(user, AI, "Wiped with [src].") - flush = 1 - AI.suiciding = 1 - to_chat(AI, "Your core files are being wiped!") - while(AI && AI.stat != DEAD) - AI.adjustOxyLoss(2) - sleep(10) - flush = 0 - - if(href_list["radio"]) - AI.aiRadio.disabledAi = text2num(href_list["radio"]) - to_chat(AI, "Your Subspace Transceiver has been [AI.aiRadio.disabledAi ? "disabled" : "enabled"]!") - to_chat(user, "You [AI.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") - - if(href_list["wireless"]) - AI.control_disabled = text2num(href_list["wireless"]) - to_chat(AI, "Your wireless interface has been [AI.control_disabled ? "disabled" : "enabled"]!") - to_chat(user, "You [AI.control_disabled ? "disable" : "enable"] the AI's wireless interface.") - update_icon() - - return 1 \ No newline at end of file +/obj/item/aicard + name = "inteliCard" + icon = 'icons/obj/aicards.dmi' + icon_state = "aicard" // aicard-full + item_state = "electronic" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + flags = NOBLUDGEON + var/flush = null + origin_tech = "programming=3;materials=3" + + +/obj/item/aicard/afterattack(atom/target, mob/user, proximity) + ..() + if(!proximity || !target) + return + var/mob/living/silicon/ai/AI = locate(/mob/living/silicon/ai) in src + if(AI) //AI is on the card, implies user wants to upload it. + target.transfer_ai(AI_TRANS_FROM_CARD, user, AI, src) + add_attack_logs(user, AI, "Carded with [src]") + else //No AI on the card, therefore the user wants to download one. + target.transfer_ai(AI_TRANS_TO_CARD, user, null, src) + update_state() //Whatever happened, update the card's state (icon, name) to match. + +/obj/item/aicard/proc/update_state() + var/mob/living/silicon/ai/AI = locate(/mob/living/silicon/ai) in src //AI is inside. + if(AI) + name = "intelliCard - [AI.name]" + if(AI.stat == DEAD) + icon_state = "aicard-404" + else + icon_state = "aicard-full" + AI.cancel_camera() //AI are forced to move when transferred, so do this whenver one is downloaded. + else + icon_state = "aicard" + name = "intelliCard" + overlays.Cut() + +/obj/item/aicard/attack_self(mob/user) + ui_interact(user) + + +/obj/item/aicard/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "aicard.tmpl", "[name]", 600, 400, state = state) + ui.open() + ui.set_auto_update(1) + + +/obj/item/aicard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) + var/data[0] + + var/mob/living/silicon/ai/AI = locate() in src + if(istype(AI)) + data["has_ai"] = 1 + data["name"] = AI.name + data["hardware_integrity"] = ((AI.health + 100) / 2) + data["radio"] = !AI.aiRadio.disabledAi + data["wireless"] = !AI.control_disabled + data["operational"] = AI.stat != DEAD + data["flushing"] = flush + + var/laws[0] + for(var/datum/ai_law/AL in AI.laws.all_laws()) + laws[++laws.len] = list("index" = AL.get_index(), "law" = sanitize(AL.law)) + data["laws"] = laws + data["has_laws"] = laws.len + + return data + + +/obj/item/aicard/Topic(href, href_list, nowindow, state) + if(..()) + return 1 + + var/mob/living/silicon/ai/AI = locate() in src + if(!istype(AI)) + return 1 + + var/user = usr + + if(href_list["wipe"]) + var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No") + if(confirm == "Yes" && (CanUseTopic(user, state) == STATUS_INTERACTIVE)) + msg_admin_attack("[key_name_admin(user)] wiped [key_name_admin(AI)] with \the [src].", ATKLOG_FEW) + add_attack_logs(user, AI, "Wiped with [src].") + flush = 1 + AI.suiciding = 1 + to_chat(AI, "Your core files are being wiped!") + while(AI && AI.stat != DEAD) + AI.adjustOxyLoss(2) + sleep(10) + flush = 0 + + if(href_list["radio"]) + AI.aiRadio.disabledAi = text2num(href_list["radio"]) + to_chat(AI, "Your Subspace Transceiver has been [AI.aiRadio.disabledAi ? "disabled" : "enabled"]!") + to_chat(user, "You [AI.aiRadio.disabledAi ? "disable" : "enable"] the AI's Subspace Transceiver.") + + if(href_list["wireless"]) + AI.control_disabled = text2num(href_list["wireless"]) + to_chat(AI, "Your wireless interface has been [AI.control_disabled ? "disabled" : "enabled"]!") + to_chat(user, "You [AI.control_disabled ? "disable" : "enable"] the AI's wireless interface.") + update_icon() + + return 1 diff --git a/code/game/objects/items/devices/autopsy.dm b/code/game/objects/items/devices/autopsy.dm index fc329011d141..c96e935432c9 100644 --- a/code/game/objects/items/devices/autopsy.dm +++ b/code/game/objects/items/devices/autopsy.dm @@ -74,7 +74,7 @@ var/dead_notes = input("Insert any relevant notes") var/obj/item/paper/R = new(user.loc) R.name = "Official Coroner's Report - [dead_name]" - R.info = "Nanotrasen Science Station [using_map.station_short] - Coroner's Report

    Name of Deceased: [dead_name]

    Rank of Deceased: [dead_rank]

    Time of Death: [dead_tod]

    Cause of Death: [dead_cause]

    Trace Chemicals: [dead_chems]

    Additional Coroner's Notes: [dead_notes]

    Coroner's Signature: " + R.info = "Nanotrasen Science Station [GLOB.using_map.station_short] - Coroner's Report

    Name of Deceased: [dead_name]

    Rank of Deceased: [dead_rank]

    Time of Death: [dead_tod]

    Cause of Death: [dead_cause]

    Trace Chemicals: [dead_chems]

    Additional Coroner's Notes: [dead_notes]

    Coroner's Signature: " playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) sleep(10) user.put_in_hands(R) diff --git a/code/game/objects/items/devices/camera_bug.dm b/code/game/objects/items/devices/camera_bug.dm index 3ca37a3f662c..7acf6b61a6d9 100644 --- a/code/game/objects/items/devices/camera_bug.dm +++ b/code/game/objects/items/devices/camera_bug.dm @@ -73,7 +73,7 @@ /obj/item/camera_bug/proc/get_cameras() if(world.time > (last_net_update + 100)) bugged_cameras = list() - for(var/obj/machinery/camera/camera in cameranet.cameras) + for(var/obj/machinery/camera/camera in GLOB.cameranet.cameras) if(camera.stat || !camera.can_use()) continue if(length(list("SS13","MINE")&camera.network)) diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm index be58a9dc12ab..97c5817f05b0 100644 --- a/code/game/objects/items/devices/chameleonproj.dm +++ b/code/game/objects/items/devices/chameleonproj.dm @@ -1,258 +1,258 @@ -/obj/item/chameleon - name = "chameleon-projector" - icon = 'icons/obj/device.dmi' - icon_state = "shield0" - flags = CONDUCT - slot_flags = SLOT_BELT - item_state = "electronic" - throwforce = 5.0 - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - origin_tech = "syndicate=4;magnets=4" - var/can_use = 1 - var/obj/effect/dummy/chameleon/active_dummy = null - var/saved_item = /obj/item/cigbutt - var/saved_icon = 'icons/obj/clothing/masks.dmi' - var/saved_icon_state = "cigbutt" - var/saved_overlays = null - var/saved_underlays = null - -/obj/item/chameleon/dropped() - disrupt() - -/obj/item/chameleon/equipped() - disrupt() - -/obj/item/chameleon/attack_self() - toggle() - -/obj/item/chameleon/afterattack(atom/target, mob/user , proximity) - if(!proximity) return - if(!active_dummy) - if(istype(target,/obj/item) && !istype(target, /obj/item/disk/nuclear)) - playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, 1, -6) - to_chat(user, "Scanned [target].") - saved_item = target.type - saved_icon = target.icon - saved_icon_state = target.icon_state - saved_overlays = target.overlays - saved_underlays = target.underlays - -/obj/item/chameleon/proc/toggle() - if(!can_use || !saved_item) return - if(active_dummy) - eject_all() - playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6) - QDEL_NULL(active_dummy) - to_chat(usr, "You deactivate [src].") - var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) - T.icon = 'icons/effects/effects.dmi' - flick("emppulse",T) - spawn(8) - qdel(T) - else - playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6) - var/obj/O = new saved_item(src) - if(!O) return - var/obj/effect/dummy/chameleon/C = new/obj/effect/dummy/chameleon(usr.loc) - C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, saved_underlays, src) - qdel(O) - to_chat(usr, "You activate [src].") - var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) - T.icon = 'icons/effects/effects.dmi' - flick("emppulse",T) - spawn(8) - qdel(T) - -/obj/item/chameleon/proc/disrupt(var/delete_dummy = 1) - if(active_dummy) - do_sparks(5, 0, src) - eject_all() - if(delete_dummy) - qdel(active_dummy) - active_dummy = null - can_use = 0 - spawn(50) can_use = 1 - -/obj/item/chameleon/proc/eject_all() - for(var/atom/movable/A in active_dummy) - A.loc = active_dummy.loc - if(ismob(A)) - var/mob/M = A - M.reset_perspective(null) - -/obj/effect/dummy/chameleon - name = "" - desc = "" - density = 0 - anchored = 1 - var/can_move = 1 - var/obj/item/chameleon/master = null - -/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, new_underlays, var/obj/item/chameleon/C) - name = O.name - desc = O.desc - icon = new_icon - icon_state = new_iconstate - overlays = new_overlays - underlays = new_underlays - dir = O.dir - M.loc = src - master = C - master.active_dummy = src - -/obj/effect/dummy/chameleon/attackby() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/attack_hand() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - master.disrupt() - -/obj/effect/dummy/chameleon/attack_animal() - master.disrupt() - -/obj/effect/dummy/chameleon/attack_slime() - master.disrupt() - -/obj/effect/dummy/chameleon/attack_alien() - master.disrupt() - -/obj/effect/dummy/chameleon/ex_act(severity) //no longer bomb-proof - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - spawn() - M.ex_act(severity) - master.disrupt() - -/obj/effect/dummy/chameleon/bullet_act() - for(var/mob/M in src) - to_chat(M, "Your chameleon-projector deactivates.") - ..() - master.disrupt() - -/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) - if(istype(loc, /turf/space) || !direction) - return //No magical space movement! - - if(can_move) - can_move = 0 - switch(user.bodytemperature) - if(300 to INFINITY) - spawn(10) can_move = 1 - if(295 to 300) - spawn(13) can_move = 1 - if(280 to 295) - spawn(16) can_move = 1 - if(260 to 280) - spawn(20) can_move = 1 - else - spawn(25) can_move = 1 - step(src, direction) - return - -/obj/effect/dummy/chameleon/Destroy() - master.disrupt(0) - return ..() - -/obj/item/borg_chameleon - name = "cyborg chameleon projector" - icon = 'icons/obj/device.dmi' - icon_state = "shield0" - item_state = "electronic" - w_class = WEIGHT_CLASS_SMALL - var/active = FALSE - var/activationCost = 300 - var/activationUpkeep = 50 - var/disguise = "landmate" - var/mob/living/silicon/robot/syndicate/saboteur/S - -/obj/item/borg_chameleon/Destroy() - if(S) - S.cham_proj = null - return ..() - -/obj/item/borg_chameleon/dropped(mob/user) - . = ..() - disrupt(user) - -/obj/item/borg_chameleon/equipped(mob/user) - . = ..() - disrupt(user) - -/obj/item/borg_chameleon/attack_self(mob/living/silicon/robot/syndicate/saboteur/user) - if(user && user.cell && user.cell.charge > activationCost) - if(isturf(user.loc)) - toggle(user) - else - to_chat(user, "You can't use [src] while inside something!") - else - to_chat(user, "You need at least [activationCost] charge in your cell to use [src]!") - -/obj/item/borg_chameleon/proc/toggle(mob/living/silicon/robot/syndicate/saboteur/user) - if(active) - playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) - to_chat(user, "You deactivate [src].") - deactivate(user) - else - to_chat(user, "You activate [src].") - var/start = user.filters.len - var/X - var/Y - var/rsq - var/i - var/f - for(i in 1 to 7) - do - X = 60 * rand() - 30 - Y = 60 * rand() - 30 - rsq = X * X + Y * Y - while(rsq < 100 || rsq > 900) - user.filters += filter(type = "wave", x = X, y = Y, size = rand() * 2.5 + 0.5, offset = rand()) - for(i in 1 to 7) - f = user.filters[start+i] - animate(f, offset = f:offset, time = 0, loop = 3, flags = ANIMATION_PARALLEL) - animate(offset = f:offset - 1, time = rand() * 20 + 10) - if(do_after(user, 50, target = user) && user.cell.use(activationCost)) - playsound(src, 'sound/effects/bamf.ogg', 100, 1, -6) - to_chat(user, "You are now disguised as a Nanotrasen engineering cyborg.") - activate(user) - else - to_chat(user, "The chameleon field fizzles.") - do_sparks(3, FALSE, user) - for(i in 1 to min(7, user.filters.len)) // removing filters that are animating does nothing, we gotta stop the animations first - f = user.filters[start + i] - animate(f) - user.filters = null - -/obj/item/borg_chameleon/process() - if(S) - if(!S.cell || !S.cell.use(activationUpkeep)) - disrupt(S) - else - return PROCESS_KILL - -/obj/item/borg_chameleon/proc/activate(mob/living/silicon/robot/syndicate/saboteur/user) - START_PROCESSING(SSobj, src) - S = user - user.base_icon = disguise - user.icon_state = disguise - user.cham_proj = src - active = TRUE - user.update_icons() - -/obj/item/borg_chameleon/proc/deactivate(mob/living/silicon/robot/syndicate/saboteur/user) - STOP_PROCESSING(SSobj, src) - S = user - user.base_icon = initial(user.base_icon) - user.icon_state = initial(user.icon_state) - active = FALSE - user.update_icons() - -/obj/item/borg_chameleon/proc/disrupt(mob/living/silicon/robot/syndicate/saboteur/user) - if(active) - to_chat(user, "Your chameleon field deactivates.") - deactivate(user) +/obj/item/chameleon + name = "chameleon-projector" + icon = 'icons/obj/device.dmi' + icon_state = "shield0" + flags = CONDUCT + slot_flags = SLOT_BELT + item_state = "electronic" + throwforce = 5.0 + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + origin_tech = "syndicate=4;magnets=4" + var/can_use = 1 + var/obj/effect/dummy/chameleon/active_dummy = null + var/saved_item = /obj/item/cigbutt + var/saved_icon = 'icons/obj/clothing/masks.dmi' + var/saved_icon_state = "cigbutt" + var/saved_overlays = null + var/saved_underlays = null + +/obj/item/chameleon/dropped() + disrupt() + +/obj/item/chameleon/equipped() + disrupt() + +/obj/item/chameleon/attack_self() + toggle() + +/obj/item/chameleon/afterattack(atom/target, mob/user , proximity) + if(!proximity) return + if(!active_dummy) + if(istype(target,/obj/item) && !istype(target, /obj/item/disk/nuclear)) + playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, 1, -6) + to_chat(user, "Scanned [target].") + saved_item = target.type + saved_icon = target.icon + saved_icon_state = target.icon_state + saved_overlays = target.overlays + saved_underlays = target.underlays + +/obj/item/chameleon/proc/toggle() + if(!can_use || !saved_item) return + if(active_dummy) + eject_all() + playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6) + QDEL_NULL(active_dummy) + to_chat(usr, "You deactivate [src].") + var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) + T.icon = 'icons/effects/effects.dmi' + flick("emppulse",T) + spawn(8) + qdel(T) + else + playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6) + var/obj/O = new saved_item(src) + if(!O) return + var/obj/effect/dummy/chameleon/C = new/obj/effect/dummy/chameleon(usr.loc) + C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, saved_underlays, src) + qdel(O) + to_chat(usr, "You activate [src].") + var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src)) + T.icon = 'icons/effects/effects.dmi' + flick("emppulse",T) + spawn(8) + qdel(T) + +/obj/item/chameleon/proc/disrupt(var/delete_dummy = 1) + if(active_dummy) + do_sparks(5, 0, src) + eject_all() + if(delete_dummy) + qdel(active_dummy) + active_dummy = null + can_use = 0 + spawn(50) can_use = 1 + +/obj/item/chameleon/proc/eject_all() + for(var/atom/movable/A in active_dummy) + A.loc = active_dummy.loc + if(ismob(A)) + var/mob/M = A + M.reset_perspective(null) + +/obj/effect/dummy/chameleon + name = "" + desc = "" + density = 0 + anchored = 1 + var/can_move = 1 + var/obj/item/chameleon/master = null + +/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, new_underlays, var/obj/item/chameleon/C) + name = O.name + desc = O.desc + icon = new_icon + icon_state = new_iconstate + overlays = new_overlays + underlays = new_underlays + dir = O.dir + M.loc = src + master = C + master.active_dummy = src + +/obj/effect/dummy/chameleon/attackby() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/attack_hand() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + master.disrupt() + +/obj/effect/dummy/chameleon/attack_animal() + master.disrupt() + +/obj/effect/dummy/chameleon/attack_slime() + master.disrupt() + +/obj/effect/dummy/chameleon/attack_alien() + master.disrupt() + +/obj/effect/dummy/chameleon/ex_act(severity) //no longer bomb-proof + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + spawn() + M.ex_act(severity) + master.disrupt() + +/obj/effect/dummy/chameleon/bullet_act() + for(var/mob/M in src) + to_chat(M, "Your chameleon-projector deactivates.") + ..() + master.disrupt() + +/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction) + if(istype(loc, /turf/space) || !direction) + return //No magical space movement! + + if(can_move) + can_move = 0 + switch(user.bodytemperature) + if(300 to INFINITY) + spawn(10) can_move = 1 + if(295 to 300) + spawn(13) can_move = 1 + if(280 to 295) + spawn(16) can_move = 1 + if(260 to 280) + spawn(20) can_move = 1 + else + spawn(25) can_move = 1 + step(src, direction) + return + +/obj/effect/dummy/chameleon/Destroy() + master.disrupt(0) + return ..() + +/obj/item/borg_chameleon + name = "cyborg chameleon projector" + icon = 'icons/obj/device.dmi' + icon_state = "shield0" + item_state = "electronic" + w_class = WEIGHT_CLASS_SMALL + var/active = FALSE + var/activationCost = 300 + var/activationUpkeep = 50 + var/disguise = "landmate" + var/mob/living/silicon/robot/syndicate/saboteur/S + +/obj/item/borg_chameleon/Destroy() + if(S) + S.cham_proj = null + return ..() + +/obj/item/borg_chameleon/dropped(mob/user) + . = ..() + disrupt(user) + +/obj/item/borg_chameleon/equipped(mob/user) + . = ..() + disrupt(user) + +/obj/item/borg_chameleon/attack_self(mob/living/silicon/robot/syndicate/saboteur/user) + if(user && user.cell && user.cell.charge > activationCost) + if(isturf(user.loc)) + toggle(user) + else + to_chat(user, "You can't use [src] while inside something!") + else + to_chat(user, "You need at least [activationCost] charge in your cell to use [src]!") + +/obj/item/borg_chameleon/proc/toggle(mob/living/silicon/robot/syndicate/saboteur/user) + if(active) + playsound(src, 'sound/effects/pop.ogg', 100, 1, -6) + to_chat(user, "You deactivate [src].") + deactivate(user) + else + to_chat(user, "You activate [src].") + var/start = user.filters.len + var/X + var/Y + var/rsq + var/i + var/f + for(i in 1 to 7) + do + X = 60 * rand() - 30 + Y = 60 * rand() - 30 + rsq = X * X + Y * Y + while(rsq < 100 || rsq > 900) + user.filters += filter(type = "wave", x = X, y = Y, size = rand() * 2.5 + 0.5, offset = rand()) + for(i in 1 to 7) + f = user.filters[start+i] + animate(f, offset = f:offset, time = 0, loop = 3, flags = ANIMATION_PARALLEL) + animate(offset = f:offset - 1, time = rand() * 20 + 10) + if(do_after(user, 50, target = user) && user.cell.use(activationCost)) + playsound(src, 'sound/effects/bamf.ogg', 100, 1, -6) + to_chat(user, "You are now disguised as a Nanotrasen engineering cyborg.") + activate(user) + else + to_chat(user, "The chameleon field fizzles.") + do_sparks(3, FALSE, user) + for(i in 1 to min(7, user.filters.len)) // removing filters that are animating does nothing, we gotta stop the animations first + f = user.filters[start + i] + animate(f) + user.filters = null + +/obj/item/borg_chameleon/process() + if(S) + if(!S.cell || !S.cell.use(activationUpkeep)) + disrupt(S) + else + return PROCESS_KILL + +/obj/item/borg_chameleon/proc/activate(mob/living/silicon/robot/syndicate/saboteur/user) + START_PROCESSING(SSobj, src) + S = user + user.base_icon = disguise + user.icon_state = disguise + user.cham_proj = src + active = TRUE + user.update_icons() + +/obj/item/borg_chameleon/proc/deactivate(mob/living/silicon/robot/syndicate/saboteur/user) + STOP_PROCESSING(SSobj, src) + S = user + user.base_icon = initial(user.base_icon) + user.icon_state = initial(user.icon_state) + active = FALSE + user.update_icons() + +/obj/item/borg_chameleon/proc/disrupt(mob/living/silicon/robot/syndicate/saboteur/user) + if(active) + to_chat(user, "Your chameleon field deactivates.") + deactivate(user) diff --git a/code/game/objects/items/devices/enginepicker.dm b/code/game/objects/items/devices/enginepicker.dm index e28b5b3bef5e..fe6bb7e87173 100644 --- a/code/game/objects/items/devices/enginepicker.dm +++ b/code/game/objects/items/devices/enginepicker.dm @@ -93,4 +93,4 @@ for(var/mob/living/M in T) M.visible_message("\The [M] gets obliterated!") - M.gib() \ No newline at end of file + M.gib() diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm index a618383a819d..af673c7b2850 100644 --- a/code/game/objects/items/devices/flash.dm +++ b/code/game/objects/items/devices/flash.dm @@ -1,281 +1,281 @@ -/obj/item/flash - name = "flash" - desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." - icon = 'icons/obj/device.dmi' - icon_state = "flash" - item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - flags = CONDUCT - materials = list(MAT_METAL = 300, MAT_GLASS = 300) - origin_tech = "magnets=2;combat=1" - - var/times_used = 0 //Number of times it's been used. - var/broken = 0 //Is the flash burnt out? - var/last_used = 0 //last world.time it was used. - var/battery_panel = 0 //whether the flash can be modified with a cell or not - var/overcharged = 0 //if overcharged the flash will set people on fire then immediately burn out (does so even if it doesn't blind them). - var/can_overcharge = TRUE //set this to FALSE if you don't want your flash to be overcharge capable - var/use_sound = 'sound/weapons/flash.ogg' - -/obj/item/flash/proc/clown_check(mob/user) - if(user && (CLUMSY in user.mutations) && prob(50)) - flash_carbon(user, user, 15, 0) - return 0 - return 1 - -/obj/item/flash/attackby(obj/item/W, mob/user, params) - if(can_overcharge) - if(istype(W, /obj/item/screwdriver)) - if(battery_panel) - to_chat(user, "You close the battery compartment on the [src].") - battery_panel = 0 - else - to_chat(user, "You open the battery compartment on the [src].") - battery_panel = 1 - if(battery_panel && !overcharged) - if(istype(W, /obj/item/stock_parts/cell)) - to_chat(user, "You jam the cell into battery compartment on the [src].") - qdel(W) - overcharged = 1 - overlays += "overcharge" - -/obj/item/flash/random/New() - ..() - if(prob(25)) - broken = 1 - icon_state = "[initial(icon_state)]burnt" - -/obj/item/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something. - broken = 1 - icon_state = "[initial(icon_state)]burnt" - visible_message("The [src.name] burns out!") - - -/obj/item/flash/proc/flash_recharge(var/mob/user) - if(prob(times_used * 2)) //if you use it 5 times in a minute it has a 10% chance to break! - burn_out() - return 0 - - var/deciseconds_passed = world.time - last_used - for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds - times_used-- - - last_used = world.time - times_used = max(0, times_used) //sanity - - -/obj/item/flash/proc/try_use_flash(var/mob/user = null) - flash_recharge(user) - - if(broken) - return 0 - - playsound(src.loc, use_sound, 100, 1) - flick("[initial(icon_state)]2", src) - times_used++ - - if(user && !clown_check(user)) - return 0 - - return 1 - - -/obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) - add_attack_logs(user, M, "Flashed with [src]") - if(user && targeted) - if(M.weakeyes) - M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash - if(M.flash_eyes(1, 1)) - M.AdjustConfused(power) - terrible_conversion_proc(M, user) - M.Stun(1) - visible_message("[user] blinds [M] with the flash!") - to_chat(user, "You blind [M] with the flash!") - to_chat(M, "[user] blinds you with the flash!") - if(M.weakeyes) - M.Stun(2) - M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") - else - visible_message("[user] fails to blind [M] with the flash!") - to_chat(user, "You fail to blind [M] with the flash!") - to_chat(M, "[user] fails to blind you with the flash!") - else - if(M.flash_eyes()) - M.AdjustConfused(power) - -/obj/item/flash/attack(mob/living/M, mob/user) - if(!try_use_flash(user)) - return 0 - - if(iscarbon(M)) - flash_carbon(M, user, 5, 1) - if(overcharged) - M.adjust_fire_stacks(6) - M.IgniteMob() - burn_out() - return 1 - - else if(issilicon(M)) - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.module) // Perhaps they didn't choose a module yet - for(var/obj/item/borg/combat/shield/S in R.module.modules) - if(R.activated(S)) - add_attack_logs(user, M, "Flashed with [src]") - user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") - return 1 - add_attack_logs(user, M, "Flashed with [src]") - if(M.flash_eyes(affect_silicon = 1)) - M.Weaken(rand(5,10)) - user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") - return 1 - - user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") - - -/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0) - if(!try_use_flash(user)) - return 0 - user.visible_message("[user]'s [src.name] emits a blinding light!", "Your [src.name] emits a blinding light!") - for(var/mob/living/carbon/M in oviewers(3, null)) - flash_carbon(M, user, 3, 0) - - -/obj/item/flash/emp_act(severity) - if(!try_use_flash()) - return 0 - for(var/mob/living/carbon/M in viewers(3, null)) - flash_carbon(M, null, 10, 0) - burn_out() - ..() - - -/obj/item/flash/proc/terrible_conversion_proc(mob/M, mob/user) - if(ishuman(M) && ishuman(user) && M.stat != DEAD) - if(user.mind && (user.mind in SSticker.mode.head_revolutionaries)) - if(M.client) - if(M.stat == CONSCIOUS) - M.mind_initialize() //give them a mind datum if they don't have one. - var/resisted - if(!ismindshielded(M)) - if(user.mind in SSticker.mode.head_revolutionaries) - if(SSticker.mode.add_revolutionary(M.mind)) - times_used -- //Flashes less likely to burn out for headrevs when used for conversion - else - resisted = 1 - else - resisted = 1 - - if(resisted) - to_chat(user, "This mind seems resistant to the [name]!") - else - to_chat(user, "They must be conscious before you can convert [M.p_them()]!") - else - to_chat(user, "This mind is so vacant that it is not susceptible to influence!") - - -/obj/item/flash/cyborg - origin_tech = null - -/obj/item/flash/cyborg/attack(mob/living/M, mob/user) - ..() - new /obj/effect/temp_visual/borgflash(get_turf(src)) - -/obj/item/flash/cyborg/attack_self(mob/user) - ..() - new /obj/effect/temp_visual/borgflash(get_turf(src)) - -/obj/item/flash/cameraflash - name = "camera" - icon = 'icons/obj/items.dmi' - desc = "A polaroid camera. 10 photos left." - icon_state = "camera" - item_state = "electropack" //spelling, a coders worst enemy. This part gave me trouble for a while. - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - can_overcharge = FALSE - var/flash_max_charges = 5 - var/flash_cur_charges = 5 - var/charge_tick = 0 - use_sound = 'sound/items/polaroid1.ogg' - -/obj/item/flash/cameraflash/burn_out() //stops from burning out - return - -/obj/item/flash/cameraflash/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/flash/cameraflash/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/flash/cameraflash/process() //this and the two parts above are part of the charge system. - charge_tick++ - if(charge_tick < 10) - return FALSE - charge_tick = 0 - flash_cur_charges = min(flash_cur_charges+1, flash_max_charges) - return TRUE - -/obj/item/flash/cameraflash/attack(mob/living/M, mob/user) - if(flash_cur_charges > 0) - flash_cur_charges -= 1 - to_chat(user, "[src] now has [flash_cur_charges] charge\s.") - ..() - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flash/cameraflash/attack_self(mob/living/carbon/user, flag = 0) - if(flash_cur_charges > 0) - flash_cur_charges -= 1 - to_chat(user, "[src] now has [flash_cur_charges] charge\s.") - ..() - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flash/memorizer - name = "memorizer" - desc = "If you see this, you're not likely to remember it any time soon." - icon_state = "memorizer" - item_state = "nullrod" - -/obj/item/flash/armimplant - name = "photon projector" - desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out." - var/flashcd = 20 - var/overheat = 0 - var/obj/item/organ/internal/cyberimp/arm/flash/I = null - -/obj/item/flash/armimplant/Destroy() - I = null - return ..() - -/obj/item/flash/armimplant/burn_out() - if(I && I.owner) - to_chat(I.owner, "Your photon projector implant overheats and deactivates!") - I.Retract() - overheat = FALSE - addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) - -/obj/item/flash/armimplant/try_use_flash(mob/user = null) - if(overheat) - if(I && I.owner) - to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") - return FALSE - overheat = TRUE - addtimer(CALLBACK(src, .proc/cooldown), flashcd) - playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) - update_icon(1) - return TRUE - -/obj/item/flash/armimplant/proc/cooldown() - overheat = FALSE - - -/obj/item/flash/synthetic //just a regular flash now +/obj/item/flash + name = "flash" + desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production." + icon = 'icons/obj/device.dmi' + icon_state = "flash" + item_state = "flashtool" //looks exactly like a flash (and nothing like a flashbang) + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + flags = CONDUCT + materials = list(MAT_METAL = 300, MAT_GLASS = 300) + origin_tech = "magnets=2;combat=1" + + var/times_used = 0 //Number of times it's been used. + var/broken = 0 //Is the flash burnt out? + var/last_used = 0 //last world.time it was used. + var/battery_panel = 0 //whether the flash can be modified with a cell or not + var/overcharged = 0 //if overcharged the flash will set people on fire then immediately burn out (does so even if it doesn't blind them). + var/can_overcharge = TRUE //set this to FALSE if you don't want your flash to be overcharge capable + var/use_sound = 'sound/weapons/flash.ogg' + +/obj/item/flash/proc/clown_check(mob/user) + if(user && (CLUMSY in user.mutations) && prob(50)) + flash_carbon(user, user, 15, 0) + return 0 + return 1 + +/obj/item/flash/attackby(obj/item/W, mob/user, params) + if(can_overcharge) + if(istype(W, /obj/item/screwdriver)) + if(battery_panel) + to_chat(user, "You close the battery compartment on the [src].") + battery_panel = 0 + else + to_chat(user, "You open the battery compartment on the [src].") + battery_panel = 1 + if(battery_panel && !overcharged) + if(istype(W, /obj/item/stock_parts/cell)) + to_chat(user, "You jam the cell into battery compartment on the [src].") + qdel(W) + overcharged = 1 + overlays += "overcharge" + +/obj/item/flash/random/New() + ..() + if(prob(25)) + broken = 1 + icon_state = "[initial(icon_state)]burnt" + +/obj/item/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something. + broken = 1 + icon_state = "[initial(icon_state)]burnt" + visible_message("The [src.name] burns out!") + + +/obj/item/flash/proc/flash_recharge(var/mob/user) + if(prob(times_used * 2)) //if you use it 5 times in a minute it has a 10% chance to break! + burn_out() + return 0 + + var/deciseconds_passed = world.time - last_used + for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds + times_used-- + + last_used = world.time + times_used = max(0, times_used) //sanity + + +/obj/item/flash/proc/try_use_flash(var/mob/user = null) + flash_recharge(user) + + if(broken) + return 0 + + playsound(src.loc, use_sound, 100, 1) + flick("[initial(icon_state)]2", src) + times_used++ + + if(user && !clown_check(user)) + return 0 + + return 1 + + +/obj/item/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1) + add_attack_logs(user, M, "Flashed with [src]") + if(user && targeted) + if(M.weakeyes) + M.Weaken(3) //quick weaken bypasses eye protection but has no eye flash + if(M.flash_eyes(1, 1)) + M.AdjustConfused(power) + terrible_conversion_proc(M, user) + M.Stun(1) + visible_message("[user] blinds [M] with the flash!") + to_chat(user, "You blind [M] with the flash!") + to_chat(M, "[user] blinds you with the flash!") + if(M.weakeyes) + M.Stun(2) + M.visible_message("[M] gasps and shields [M.p_their()] eyes!", "You gasp and shields your eyes!") + else + visible_message("[user] fails to blind [M] with the flash!") + to_chat(user, "You fail to blind [M] with the flash!") + to_chat(M, "[user] fails to blind you with the flash!") + else + if(M.flash_eyes()) + M.AdjustConfused(power) + +/obj/item/flash/attack(mob/living/M, mob/user) + if(!try_use_flash(user)) + return 0 + + if(iscarbon(M)) + flash_carbon(M, user, 5, 1) + if(overcharged) + M.adjust_fire_stacks(6) + M.IgniteMob() + burn_out() + return 1 + + else if(issilicon(M)) + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.module) // Perhaps they didn't choose a module yet + for(var/obj/item/borg/combat/shield/S in R.module.modules) + if(R.activated(S)) + add_attack_logs(user, M, "Flashed with [src]") + user.visible_message("[user] tries to overloads [M]'s sensors with the [src.name], but is blocked by [M]'s shield!", "You try to overload [M]'s sensors with the [src.name], but are blocked by [M.p_their()] shield!") + return 1 + add_attack_logs(user, M, "Flashed with [src]") + if(M.flash_eyes(affect_silicon = 1)) + M.Weaken(rand(5,10)) + user.visible_message("[user] overloads [M]'s sensors with the [src.name]!", "You overload [M]'s sensors with the [src.name]!") + return 1 + + user.visible_message("[user] fails to blind [M] with the [src.name]!", "You fail to blind [M] with the [src.name]!") + + +/obj/item/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0) + if(!try_use_flash(user)) + return 0 + user.visible_message("[user]'s [src.name] emits a blinding light!", "Your [src.name] emits a blinding light!") + for(var/mob/living/carbon/M in oviewers(3, null)) + flash_carbon(M, user, 3, 0) + + +/obj/item/flash/emp_act(severity) + if(!try_use_flash()) + return 0 + for(var/mob/living/carbon/M in viewers(3, null)) + flash_carbon(M, null, 10, 0) + burn_out() + ..() + + +/obj/item/flash/proc/terrible_conversion_proc(mob/M, mob/user) + if(ishuman(M) && ishuman(user) && M.stat != DEAD) + if(user.mind && (user.mind in SSticker.mode.head_revolutionaries)) + if(M.client) + if(M.stat == CONSCIOUS) + M.mind_initialize() //give them a mind datum if they don't have one. + var/resisted + if(!ismindshielded(M)) + if(user.mind in SSticker.mode.head_revolutionaries) + if(SSticker.mode.add_revolutionary(M.mind)) + times_used -- //Flashes less likely to burn out for headrevs when used for conversion + else + resisted = 1 + else + resisted = 1 + + if(resisted) + to_chat(user, "This mind seems resistant to the [name]!") + else + to_chat(user, "They must be conscious before you can convert [M.p_them()]!") + else + to_chat(user, "This mind is so vacant that it is not susceptible to influence!") + + +/obj/item/flash/cyborg + origin_tech = null + +/obj/item/flash/cyborg/attack(mob/living/M, mob/user) + ..() + new /obj/effect/temp_visual/borgflash(get_turf(src)) + +/obj/item/flash/cyborg/attack_self(mob/user) + ..() + new /obj/effect/temp_visual/borgflash(get_turf(src)) + +/obj/item/flash/cameraflash + name = "camera" + icon = 'icons/obj/items.dmi' + desc = "A polaroid camera. 10 photos left." + icon_state = "camera" + item_state = "electropack" //spelling, a coders worst enemy. This part gave me trouble for a while. + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + can_overcharge = FALSE + var/flash_max_charges = 5 + var/flash_cur_charges = 5 + var/charge_tick = 0 + use_sound = 'sound/items/polaroid1.ogg' + +/obj/item/flash/cameraflash/burn_out() //stops from burning out + return + +/obj/item/flash/cameraflash/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/flash/cameraflash/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/flash/cameraflash/process() //this and the two parts above are part of the charge system. + charge_tick++ + if(charge_tick < 10) + return FALSE + charge_tick = 0 + flash_cur_charges = min(flash_cur_charges+1, flash_max_charges) + return TRUE + +/obj/item/flash/cameraflash/attack(mob/living/M, mob/user) + if(flash_cur_charges > 0) + flash_cur_charges -= 1 + to_chat(user, "[src] now has [flash_cur_charges] charge\s.") + ..() + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flash/cameraflash/attack_self(mob/living/carbon/user, flag = 0) + if(flash_cur_charges > 0) + flash_cur_charges -= 1 + to_chat(user, "[src] now has [flash_cur_charges] charge\s.") + ..() + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flash/memorizer + name = "memorizer" + desc = "If you see this, you're not likely to remember it any time soon." + icon_state = "memorizer" + item_state = "nullrod" + +/obj/item/flash/armimplant + name = "photon projector" + desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out." + var/flashcd = 20 + var/overheat = 0 + var/obj/item/organ/internal/cyberimp/arm/flash/I = null + +/obj/item/flash/armimplant/Destroy() + I = null + return ..() + +/obj/item/flash/armimplant/burn_out() + if(I && I.owner) + to_chat(I.owner, "Your photon projector implant overheats and deactivates!") + I.Retract() + overheat = FALSE + addtimer(CALLBACK(src, .proc/cooldown), flashcd * 2) + +/obj/item/flash/armimplant/try_use_flash(mob/user = null) + if(overheat) + if(I && I.owner) + to_chat(I.owner, "Your photon projector is running too hot to be used again so quickly!") + return FALSE + overheat = TRUE + addtimer(CALLBACK(src, .proc/cooldown), flashcd) + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + update_icon(1) + return TRUE + +/obj/item/flash/armimplant/proc/cooldown() + overheat = FALSE + + +/obj/item/flash/synthetic //just a regular flash now diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm index f91cacd68049..4ce64a859ec0 100644 --- a/code/game/objects/items/devices/flashlight.dm +++ b/code/game/objects/items/devices/flashlight.dm @@ -1,398 +1,398 @@ -/obj/item/flashlight - name = "flashlight" - desc = "A hand-held emergency light." - icon = 'icons/obj/lighting.dmi' - icon_state = "flashlight" - item_state = "flashlight" - w_class = WEIGHT_CLASS_SMALL - flags = CONDUCT - slot_flags = SLOT_BELT - materials = list(MAT_METAL=50, MAT_GLASS=20) - actions_types = list(/datum/action/item_action/toggle_light) - var/on = FALSE - var/brightness_on = 4 //luminosity when on - var/togglesound = 'sound/weapons/empty.ogg' - -/obj/item/flashlight/Initialize() - . = ..() - if(on) - icon_state = "[initial(icon_state)]-on" - set_light(brightness_on) - else - icon_state = initial(icon_state) - set_light(0) - -/obj/item/flashlight/proc/update_brightness(var/mob/user = null) - if(on) - icon_state = "[initial(icon_state)]-on" - set_light(brightness_on) - else - icon_state = initial(icon_state) - set_light(0) - -/obj/item/flashlight/attack_self(mob/user) - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc].")//To prevent some lighting anomalities. - - return 0 - on = !on - playsound(user, togglesound, 100, 1) - update_brightness(user) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - return 1 - - -/obj/item/flashlight/attack(mob/living/M as mob, mob/living/user as mob) - add_fingerprint(user) - if(on && user.zone_selected == "eyes") - - if(((CLUMSY in user.mutations) || user.getBrainLoss() >= 60) && prob(50)) //too dumb to use flashlight properly - return ..() //just hit them in the head - - if(!(istype(user, /mob/living/carbon/human) || SSticker) && SSticker.mode.name != "monkey") //don't have dexterity - to_chat(user, "You don't have the dexterity to do this!") - return - - var/mob/living/carbon/human/H = M //mob has protective eyewear - if(istype(H) && ((H.head && H.head.flags_cover & HEADCOVERSEYES) || (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) || (H.glasses && H.glasses.flags_cover & GLASSESCOVERSEYES))) - to_chat(user, "You're going to need to remove that [(H.head && H.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask" : "glasses"] first.") - return - - if(M == user) //they're using it on themselves - if(M.flash_eyes(visual = 1)) - M.visible_message("[M] directs [src] to [M.p_their()] eyes.", \ - "You wave the light in front of your eyes! Trippy!") - else - M.visible_message("[M] directs [src] to [M.p_their()] eyes.", \ - "You wave the light in front of your eyes.") - else - - user.visible_message("[user] directs [src] to [M]'s eyes.", \ - "You direct [src] to [M]'s eyes.") - - if(istype(H)) //robots and aliens are unaffected - var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) - if(M.stat == DEAD || !eyes || M.disabilities & BLIND) //mob is dead or fully blind - to_chat(user, "[M]'s pupils are unresponsive to the light!") - else if((XRAY in M.mutations) || eyes.see_in_dark >= 8) //The mob's either got the X-RAY vision or has a tapetum lucidum (extreme nightvision, i.e. Vulp/Tajara with COLOURBLIND & their monkey forms). - to_chat(user, "[M]'s pupils glow eerily!") - else //they're okay! - if(M.flash_eyes(visual = 1)) - to_chat(user, "[M]'s pupils narrow.") - else - return ..() - -/obj/item/flashlight/extinguish_light() - if(on) - on = FALSE - update_brightness() - -/obj/item/flashlight/pen - name = "penlight" - desc = "A pen-sized light, used by medical staff." - icon_state = "penlight" - item_state = "" - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_BELT | SLOT_EARS - flags = CONDUCT - brightness_on = 2 - -/obj/item/flashlight/seclite - name = "seclite" - desc = "A robust flashlight used by security." - icon_state = "seclite" - item_state = "seclite" - force = 9 // Not as good as a stun baton. - brightness_on = 5 // A little better than the standard flashlight. - hitsound = 'sound/weapons/genhit1.ogg' - -/obj/item/flashlight/drone - name = "low-power flashlight" - desc = "A miniature lamp, that might be used by small robots." - icon_state = "penlight" - item_state = "" - flags = CONDUCT - brightness_on = 2 - w_class = WEIGHT_CLASS_TINY - -// the desk lamps are a bit special -/obj/item/flashlight/lamp - name = "desk lamp" - desc = "A desk lamp with an adjustable mount." - icon_state = "lamp" - item_state = "lamp" - brightness_on = 5 - w_class = WEIGHT_CLASS_BULKY - flags = CONDUCT - materials = list() - on = TRUE - - -// green-shaded desk lamp -/obj/item/flashlight/lamp/green - desc = "A classic green-shaded desk lamp." - icon_state = "lampgreen" - item_state = "lampgreen" - - - -/obj/item/flashlight/lamp/verb/toggle_light() - set name = "Toggle light" - set category = "Object" - set src in oview(1) - - if(!usr.stat) - attack_self(usr) - -//Bananalamp -/obj/item/flashlight/lamp/bananalamp - name = "banana lamp" - desc = "Only a clown would think to make a ghetto banana-shaped lamp. Even has a goofy pullstring." - icon_state = "bananalamp" - item_state = "bananalamp" - - -// FLARES - -/obj/item/flashlight/flare - name = "flare" - desc = "A red Nanotrasen issued flare. There are instructions on the side, it reads 'pull cord, make light'." - brightness_on = 8 - light_color = "#ff0000" - icon_state = "flare" - item_state = "flare" - togglesound = 'sound/goonstation/misc/matchstick_light.ogg' - var/fuel = 0 - var/on_damage = 7 - var/produce_heat = 1500 - var/fuel_lower = 800 - var/fuel_upp = 1000 - -/obj/item/flashlight/flare/New() - fuel = rand(fuel_lower, fuel_upp) - ..() - -/obj/item/flashlight/flare/process() - var/turf/pos = get_turf(src) - if(pos && produce_heat) - pos.hotspot_expose(produce_heat, 5) - fuel = max(fuel - 1, 0) - if(!fuel || !on) - turn_off() - if(!fuel) - src.icon_state = "[initial(icon_state)]-empty" - STOP_PROCESSING(SSobj, src) - -/obj/item/flashlight/flare/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/flashlight/flare/proc/turn_off() - on = 0 - src.force = initial(src.force) - src.damtype = initial(src.damtype) - if(ismob(loc)) - var/mob/U = loc - update_brightness(U) - else - update_brightness(null) - -/obj/item/flashlight/flare/update_brightness(var/mob/user = null) - ..() - if(on) - item_state = "[initial(item_state)]-on" - else - item_state = "[initial(item_state)]" - -/obj/item/flashlight/flare/attack_self(mob/user) - - // Usual checks - if(!fuel) - to_chat(user, "[src] is out of fuel.") - return - if(on) - to_chat(user, "[src] is already on.") - return - - . = ..() - // All good, turn it on. - if(.) - user.visible_message("[user] activates [src].", "You activate [src].") - src.force = on_damage - src.damtype = "fire" - START_PROCESSING(SSobj, src) - -// GLOWSTICKS - -/obj/item/flashlight/flare/glowstick - name = "green glowstick" - desc = "A military-grade glowstick." - brightness_on = 4 - color = LIGHT_COLOR_GREEN - icon_state = "glowstick" - item_state = "glowstick" - togglesound = 'sound/effects/bone_break_1.ogg' - produce_heat = 0 - fuel_lower = 1600 - fuel_upp = 2000 - -/obj/item/flashlight/flare/glowstick/Initialize() - light_color = color - ..() - -/obj/item/flashlight/flare/glowstick/update_icon() - item_state = "glowstick" - cut_overlays() - if(!fuel) - icon_state = "glowstick-empty" - cut_overlays() - update_brightness(0) - else if(on) - var/mutable_appearance/glowstick_overlay = mutable_appearance(icon, "glowstick-glow") - glowstick_overlay.color = color - add_overlay(glowstick_overlay) - item_state = "glowstick-on" - update_brightness(brightness_on) - else - icon_state = "glowstick" - cut_overlays() - -/obj/item/flashlight/flare/glowstick/red - name = "red glowstick" - color = LIGHT_COLOR_RED - -/obj/item/flashlight/flare/glowstick/blue - name = "blue glowstick" - color = LIGHT_COLOR_BLUE - -/obj/item/flashlight/flare/glowstick/orange - name = "orange glowstick" - color = LIGHT_COLOR_ORANGE - -/obj/item/flashlight/flare/glowstick/yellow - name = "yellow glowstick" - color = LIGHT_COLOR_YELLOW - -/obj/item/flashlight/flare/glowstick/pink - name = "pink glowstick" - color = LIGHT_COLOR_PINK - -/obj/item/flashlight/flare/glowstick/emergency - name = "emergency glowstick" - desc = "A cheap looking, mass produced glowstick. You can practically feel it was made on a tight budget." - color = LIGHT_COLOR_BLUE - fuel_lower = 30 - fuel_upp = 90 - -/obj/item/flashlight/flare/glowstick/random - name = "random colored glowstick" - icon_state = "random_glowstick" - color = null - -/obj/item/flashlight/flare/glowstick/random/Initialize() - . = ..() - var/T = pick(typesof(/obj/item/flashlight/flare/glowstick) - /obj/item/flashlight/flare/glowstick/random - /obj/item/flashlight/flare/glowstick/emergency) - new T(loc) - qdel(src) // return INITIALIZE_HINT_QDEL <-- Doesn't work - -/obj/item/flashlight/flare/extinguish_light() - visible_message("[src] dims slightly before scattering the shadows around it.") - -/obj/item/flashlight/flare/torch - name = "torch" - desc = "A torch fashioned from some leaves and a log." - w_class = WEIGHT_CLASS_BULKY - brightness_on = 7 - icon_state = "torch" - item_state = "torch" - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - light_color = LIGHT_COLOR_ORANGE - on_damage = 10 - -/obj/item/flashlight/slime - gender = PLURAL - name = "glowing slime extract" - desc = "A glowing ball of what appears to be amber." - icon = 'icons/obj/lighting.dmi' - icon_state = "floor1" //not a slime extract sprite but... something close enough! - item_state = "slime" - w_class = WEIGHT_CLASS_TINY - brightness_on = 6 - light_color = "#FFBF00" - materials = list() - on = TRUE //Bio-luminesence has one setting, on. - -/obj/item/flashlight/slime/New() - ..() - set_light(brightness_on) - spawn(1) //Might be sloppy, but seems to be necessary to prevent further runtimes and make these work as intended... don't judge me! - update_brightness() - icon_state = initial(icon_state) - -/obj/item/flashlight/slime/attack_self(mob/user) - return //Bio-luminescence does not toggle. - -/obj/item/flashlight/slime/extinguish_light() - visible_message("[src] dims slightly before scattering the shadows around it.") - -/obj/item/flashlight/emp - origin_tech = "magnets=3;syndicate=1" - - var/emp_max_charges = 4 - var/emp_cur_charges = 4 - var/charge_tick = 0 - - -/obj/item/flashlight/emp/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/flashlight/emp/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/flashlight/emp/process() - charge_tick++ - if(charge_tick < 10) - return FALSE - charge_tick = 0 - emp_cur_charges = min(emp_cur_charges+1, emp_max_charges) - return TRUE - -/obj/item/flashlight/emp/attack(mob/living/M as mob, mob/living/user as mob) - if(on && user.zone_selected == "eyes") // call original attack proc only if aiming at the eyes - ..() - return - -/obj/item/flashlight/emp/afterattack(atom/A as mob|obj, mob/user, proximity) - if(!proximity) return - if(emp_cur_charges > 0) - emp_cur_charges -= 1 - A.visible_message("[user] blinks \the [src] at \the [A].", \ - "[user] blinks \the [src] at \the [A].") - if(ismob(A)) - var/mob/M = A - add_attack_logs(user, M, "Hit with EMP-light") - to_chat(user, "[src] now has [emp_cur_charges] charge\s.") - A.emp_act(1) - else - to_chat(user, "\The [src] needs time to recharge!") - return - -/obj/item/flashlight/spotlight //invisible lighting source - name = "disco light" - desc = "Groovy..." - icon_state = null - light_color = null - brightness_on = 0 - light_range = 0 - light_power = 10 - alpha = 0 - layer = 0 - on = TRUE - anchored = TRUE - var/range = null - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF +/obj/item/flashlight + name = "flashlight" + desc = "A hand-held emergency light." + icon = 'icons/obj/lighting.dmi' + icon_state = "flashlight" + item_state = "flashlight" + w_class = WEIGHT_CLASS_SMALL + flags = CONDUCT + slot_flags = SLOT_BELT + materials = list(MAT_METAL=50, MAT_GLASS=20) + actions_types = list(/datum/action/item_action/toggle_light) + var/on = FALSE + var/brightness_on = 4 //luminosity when on + var/togglesound = 'sound/weapons/empty.ogg' + +/obj/item/flashlight/Initialize() + . = ..() + if(on) + icon_state = "[initial(icon_state)]-on" + set_light(brightness_on) + else + icon_state = initial(icon_state) + set_light(0) + +/obj/item/flashlight/proc/update_brightness(var/mob/user = null) + if(on) + icon_state = "[initial(icon_state)]-on" + set_light(brightness_on) + else + icon_state = initial(icon_state) + set_light(0) + +/obj/item/flashlight/attack_self(mob/user) + if(!isturf(user.loc)) + to_chat(user, "You cannot turn the light on while in this [user.loc].")//To prevent some lighting anomalities. + + return 0 + on = !on + playsound(user, togglesound, 100, 1) + update_brightness(user) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + return 1 + + +/obj/item/flashlight/attack(mob/living/M as mob, mob/living/user as mob) + add_fingerprint(user) + if(on && user.zone_selected == "eyes") + + if(((CLUMSY in user.mutations) || user.getBrainLoss() >= 60) && prob(50)) //too dumb to use flashlight properly + return ..() //just hit them in the head + + if(!(istype(user, /mob/living/carbon/human) || SSticker) && SSticker.mode.name != "monkey") //don't have dexterity + to_chat(user, "You don't have the dexterity to do this!") + return + + var/mob/living/carbon/human/H = M //mob has protective eyewear + if(istype(H) && ((H.head && H.head.flags_cover & HEADCOVERSEYES) || (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) || (H.glasses && H.glasses.flags_cover & GLASSESCOVERSEYES))) + to_chat(user, "You're going to need to remove that [(H.head && H.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask" : "glasses"] first.") + return + + if(M == user) //they're using it on themselves + if(M.flash_eyes(visual = 1)) + M.visible_message("[M] directs [src] to [M.p_their()] eyes.", \ + "You wave the light in front of your eyes! Trippy!") + else + M.visible_message("[M] directs [src] to [M.p_their()] eyes.", \ + "You wave the light in front of your eyes.") + else + + user.visible_message("[user] directs [src] to [M]'s eyes.", \ + "You direct [src] to [M]'s eyes.") + + if(istype(H)) //robots and aliens are unaffected + var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) + if(M.stat == DEAD || !eyes || M.disabilities & BLIND) //mob is dead or fully blind + to_chat(user, "[M]'s pupils are unresponsive to the light!") + else if((XRAY in M.mutations) || eyes.see_in_dark >= 8) //The mob's either got the X-RAY vision or has a tapetum lucidum (extreme nightvision, i.e. Vulp/Tajara with COLOURBLIND & their monkey forms). + to_chat(user, "[M]'s pupils glow eerily!") + else //they're okay! + if(M.flash_eyes(visual = 1)) + to_chat(user, "[M]'s pupils narrow.") + else + return ..() + +/obj/item/flashlight/extinguish_light() + if(on) + on = FALSE + update_brightness() + +/obj/item/flashlight/pen + name = "penlight" + desc = "A pen-sized light, used by medical staff." + icon_state = "penlight" + item_state = "" + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_BELT | SLOT_EARS + flags = CONDUCT + brightness_on = 2 + +/obj/item/flashlight/seclite + name = "seclite" + desc = "A robust flashlight used by security." + icon_state = "seclite" + item_state = "seclite" + force = 9 // Not as good as a stun baton. + brightness_on = 5 // A little better than the standard flashlight. + hitsound = 'sound/weapons/genhit1.ogg' + +/obj/item/flashlight/drone + name = "low-power flashlight" + desc = "A miniature lamp, that might be used by small robots." + icon_state = "penlight" + item_state = "" + flags = CONDUCT + brightness_on = 2 + w_class = WEIGHT_CLASS_TINY + +// the desk lamps are a bit special +/obj/item/flashlight/lamp + name = "desk lamp" + desc = "A desk lamp with an adjustable mount." + icon_state = "lamp" + item_state = "lamp" + brightness_on = 5 + w_class = WEIGHT_CLASS_BULKY + flags = CONDUCT + materials = list() + on = TRUE + + +// green-shaded desk lamp +/obj/item/flashlight/lamp/green + desc = "A classic green-shaded desk lamp." + icon_state = "lampgreen" + item_state = "lampgreen" + + + +/obj/item/flashlight/lamp/verb/toggle_light() + set name = "Toggle light" + set category = "Object" + set src in oview(1) + + if(!usr.stat) + attack_self(usr) + +//Bananalamp +/obj/item/flashlight/lamp/bananalamp + name = "banana lamp" + desc = "Only a clown would think to make a ghetto banana-shaped lamp. Even has a goofy pullstring." + icon_state = "bananalamp" + item_state = "bananalamp" + + +// FLARES + +/obj/item/flashlight/flare + name = "flare" + desc = "A red Nanotrasen issued flare. There are instructions on the side, it reads 'pull cord, make light'." + brightness_on = 8 + light_color = "#ff0000" + icon_state = "flare" + item_state = "flare" + togglesound = 'sound/goonstation/misc/matchstick_light.ogg' + var/fuel = 0 + var/on_damage = 7 + var/produce_heat = 1500 + var/fuel_lower = 800 + var/fuel_upp = 1000 + +/obj/item/flashlight/flare/New() + fuel = rand(fuel_lower, fuel_upp) + ..() + +/obj/item/flashlight/flare/process() + var/turf/pos = get_turf(src) + if(pos && produce_heat) + pos.hotspot_expose(produce_heat, 5) + fuel = max(fuel - 1, 0) + if(!fuel || !on) + turn_off() + if(!fuel) + src.icon_state = "[initial(icon_state)]-empty" + STOP_PROCESSING(SSobj, src) + +/obj/item/flashlight/flare/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/flashlight/flare/proc/turn_off() + on = 0 + src.force = initial(src.force) + src.damtype = initial(src.damtype) + if(ismob(loc)) + var/mob/U = loc + update_brightness(U) + else + update_brightness(null) + +/obj/item/flashlight/flare/update_brightness(var/mob/user = null) + ..() + if(on) + item_state = "[initial(item_state)]-on" + else + item_state = "[initial(item_state)]" + +/obj/item/flashlight/flare/attack_self(mob/user) + + // Usual checks + if(!fuel) + to_chat(user, "[src] is out of fuel.") + return + if(on) + to_chat(user, "[src] is already on.") + return + + . = ..() + // All good, turn it on. + if(.) + user.visible_message("[user] activates [src].", "You activate [src].") + src.force = on_damage + src.damtype = "fire" + START_PROCESSING(SSobj, src) + +// GLOWSTICKS + +/obj/item/flashlight/flare/glowstick + name = "green glowstick" + desc = "A military-grade glowstick." + brightness_on = 4 + color = LIGHT_COLOR_GREEN + icon_state = "glowstick" + item_state = "glowstick" + togglesound = 'sound/effects/bone_break_1.ogg' + produce_heat = 0 + fuel_lower = 1600 + fuel_upp = 2000 + +/obj/item/flashlight/flare/glowstick/Initialize() + light_color = color + ..() + +/obj/item/flashlight/flare/glowstick/update_icon() + item_state = "glowstick" + cut_overlays() + if(!fuel) + icon_state = "glowstick-empty" + cut_overlays() + update_brightness(0) + else if(on) + var/mutable_appearance/glowstick_overlay = mutable_appearance(icon, "glowstick-glow") + glowstick_overlay.color = color + add_overlay(glowstick_overlay) + item_state = "glowstick-on" + update_brightness(brightness_on) + else + icon_state = "glowstick" + cut_overlays() + +/obj/item/flashlight/flare/glowstick/red + name = "red glowstick" + color = LIGHT_COLOR_RED + +/obj/item/flashlight/flare/glowstick/blue + name = "blue glowstick" + color = LIGHT_COLOR_BLUE + +/obj/item/flashlight/flare/glowstick/orange + name = "orange glowstick" + color = LIGHT_COLOR_ORANGE + +/obj/item/flashlight/flare/glowstick/yellow + name = "yellow glowstick" + color = LIGHT_COLOR_YELLOW + +/obj/item/flashlight/flare/glowstick/pink + name = "pink glowstick" + color = LIGHT_COLOR_PINK + +/obj/item/flashlight/flare/glowstick/emergency + name = "emergency glowstick" + desc = "A cheap looking, mass produced glowstick. You can practically feel it was made on a tight budget." + color = LIGHT_COLOR_BLUE + fuel_lower = 30 + fuel_upp = 90 + +/obj/item/flashlight/flare/glowstick/random + name = "random colored glowstick" + icon_state = "random_glowstick" + color = null + +/obj/item/flashlight/flare/glowstick/random/Initialize() + . = ..() + var/T = pick(typesof(/obj/item/flashlight/flare/glowstick) - /obj/item/flashlight/flare/glowstick/random - /obj/item/flashlight/flare/glowstick/emergency) + new T(loc) + qdel(src) // return INITIALIZE_HINT_QDEL <-- Doesn't work + +/obj/item/flashlight/flare/extinguish_light() + visible_message("[src] dims slightly before scattering the shadows around it.") + +/obj/item/flashlight/flare/torch + name = "torch" + desc = "A torch fashioned from some leaves and a log." + w_class = WEIGHT_CLASS_BULKY + brightness_on = 7 + icon_state = "torch" + item_state = "torch" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + light_color = LIGHT_COLOR_ORANGE + on_damage = 10 + +/obj/item/flashlight/slime + gender = PLURAL + name = "glowing slime extract" + desc = "A glowing ball of what appears to be amber." + icon = 'icons/obj/lighting.dmi' + icon_state = "floor1" //not a slime extract sprite but... something close enough! + item_state = "slime" + w_class = WEIGHT_CLASS_TINY + brightness_on = 6 + light_color = "#FFBF00" + materials = list() + on = TRUE //Bio-luminesence has one setting, on. + +/obj/item/flashlight/slime/New() + ..() + set_light(brightness_on) + spawn(1) //Might be sloppy, but seems to be necessary to prevent further runtimes and make these work as intended... don't judge me! + update_brightness() + icon_state = initial(icon_state) + +/obj/item/flashlight/slime/attack_self(mob/user) + return //Bio-luminescence does not toggle. + +/obj/item/flashlight/slime/extinguish_light() + visible_message("[src] dims slightly before scattering the shadows around it.") + +/obj/item/flashlight/emp + origin_tech = "magnets=3;syndicate=1" + + var/emp_max_charges = 4 + var/emp_cur_charges = 4 + var/charge_tick = 0 + + +/obj/item/flashlight/emp/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/flashlight/emp/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/flashlight/emp/process() + charge_tick++ + if(charge_tick < 10) + return FALSE + charge_tick = 0 + emp_cur_charges = min(emp_cur_charges+1, emp_max_charges) + return TRUE + +/obj/item/flashlight/emp/attack(mob/living/M as mob, mob/living/user as mob) + if(on && user.zone_selected == "eyes") // call original attack proc only if aiming at the eyes + ..() + return + +/obj/item/flashlight/emp/afterattack(atom/A as mob|obj, mob/user, proximity) + if(!proximity) return + if(emp_cur_charges > 0) + emp_cur_charges -= 1 + A.visible_message("[user] blinks \the [src] at \the [A].", \ + "[user] blinks \the [src] at \the [A].") + if(ismob(A)) + var/mob/M = A + add_attack_logs(user, M, "Hit with EMP-light") + to_chat(user, "[src] now has [emp_cur_charges] charge\s.") + A.emp_act(1) + else + to_chat(user, "\The [src] needs time to recharge!") + return + +/obj/item/flashlight/spotlight //invisible lighting source + name = "disco light" + desc = "Groovy..." + icon_state = null + light_color = null + brightness_on = 0 + light_range = 0 + light_power = 10 + alpha = 0 + layer = 0 + on = TRUE + anchored = TRUE + var/range = null + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF diff --git a/code/game/objects/items/devices/handheld_defib.dm b/code/game/objects/items/devices/handheld_defib.dm index 9333f9170e50..9dd159ca91b0 100644 --- a/code/game/objects/items/devices/handheld_defib.dm +++ b/code/game/objects/items/devices/handheld_defib.dm @@ -88,4 +88,4 @@ /obj/item/handheld_defibrillator/proc/recharge() cooldown = FALSE icon_state = "[icon_base]-on" - playsound(loc, "sound/weapons/flash.ogg", 75, 1) \ No newline at end of file + playsound(loc, "sound/weapons/flash.ogg", 75, 1) diff --git a/code/game/objects/items/devices/instruments.dm b/code/game/objects/items/devices/instruments.dm index 538b4efaf499..53ead1298360 100644 --- a/code/game/objects/items/devices/instruments.dm +++ b/code/game/objects/items/devices/instruments.dm @@ -35,7 +35,7 @@ song.ui_interact(user, ui_key, ui, force_open) -/obj/item/instrument/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/item/instrument/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) return song.ui_data(user, ui_key, state) /obj/item/instrument/Topic(href, href_list) @@ -189,4 +189,4 @@ /obj/item/stack/tape_roll = 5) tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) time = 80 - category = CAT_MISC \ No newline at end of file + category = CAT_MISC diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 21b71e7668cd..cfaea9f32f9d 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -138,6 +138,7 @@ log_admin("[key_name(user)] EMPd a camera with a laser pointer") user.create_attack_log("[key_name(user)] EMPd a camera with a laser pointer") + add_attack_logs(user, C, "EMPd with [src]", ATKLOG_ALL) else outmsg = "You missed the lens of [C] with [src]." diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index e9cb80f398ca..d9c6d60cf54d 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -1,269 +1,269 @@ - -// Light Replacer (LR) -// -// ABOUT THE DEVICE -// -// This is a device supposedly to be used by Janitors and Janitor Cyborgs which will -// allow them to easily replace lights. This was mostly designed for Janitor Cyborgs since -// they don't have hands or a way to replace lightbulbs. -// -// HOW IT WORKS -// -// You attack a light fixture with it, if the light fixture is broken it will replace the -// light fixture with a working light; the broken light is then placed on the floor for the -// user to then pickup with a trash bag. If it's empty then it will just place a light in the fixture. -// -// HOW TO REFILL THE DEVICE -// -// It will need to be manually refilled with lights. -// If it's part of a robot module, it will charge when the Robot is inside a Recharge Station. -// -// EMAGGED FEATURES -// -// NOTICE: The Cyborg cannot use the emagged Light Replacer and the light's explosion was nerfed. It cannot create holes in the station anymore. -// -// I'm not sure everyone will react the emag's features so please say what your opinions are of it. -// -// When emagged it will rig every light it replaces, which will explode when the light is on. -// This is VERY noticable, even the device's name changes when you emag it so if anyone -// examines you when you're holding it in your hand, you will be discovered. -// It will also be very obvious who is setting all these lights off, since only Janitor Borgs and Janitors have easy -// access to them, and only one of them can emag their device. -// -// The explosion cannot insta-kill anyone with 30% or more health. - -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - - -/obj/item/lightreplacer - - name = "light replacer" - desc = "A device to automatically replace lights. Refill with broken or working light bulbs, or sheets of glass." - - icon = 'icons/obj/janitor.dmi' - icon_state = "lightreplacer0" - item_state = "electronic" - w_class = WEIGHT_CLASS_SMALL - flags = CONDUCT - slot_flags = SLOT_BELT - origin_tech = "magnets=3;engineering=4" - force = 8 - - var/emagged = FALSE - var/max_uses = 20 - var/uses = 10 - // How much to increase per each glass? - var/increment = 5 - // How much to take from the glass? - var/decrement = 1 - var/charge = 1 - - // Eating used bulbs gives us bulb shards - var/bulb_shards = 0 - // when we get this many shards, we get a free bulb. - var/shards_required = 4 - - -/obj/item/lightreplacer/examine(mob/user) - . = ..() - . += status_string() - -/obj/item/lightreplacer/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = I - if(uses >= max_uses) - to_chat(user, "[src] is full.") - return - else if(G.use(decrement)) - AddUses(increment) - to_chat(user, "You insert a piece of glass into [src]. You have [uses] light\s remaining.") - return - else - to_chat(user, "You need one sheet of glass to replace lights!") - return - - if(istype(I, /obj/item/shard)) - if(uses >= max_uses) - to_chat(user, "[src] is full.") - return - if(!user.unEquip(I)) - return - AddUses(round(increment * 0.75)) - to_chat(user, "You insert a shard of glass into [src]. You have [uses] light\s remaining.") - qdel(I) - return - - if(istype(I, /obj/item/light)) - var/obj/item/light/L = I - if(L.status == 0) // LIGHT OKAY - if(uses < max_uses) - if(!user.unEquip(L)) - return - AddUses(1) - qdel(L) - else - if(!user.unEquip(L)) - return - to_chat(user, "You insert [L] into [src].") - AddShards(1, user) - qdel(L) - return - - if(istype(I, /obj/item/storage)) - var/obj/item/storage/S = I - var/found_lightbulbs = FALSE - var/replaced_something = TRUE - - for(var/obj/item/IT in S.contents) - if(istype(IT, /obj/item/light)) - var/obj/item/light/L = IT - found_lightbulbs = TRUE - if(uses >= max_uses) - break - if(L.status == LIGHT_OK) - replaced_something = TRUE - AddUses(1) - qdel(L) - - else if(L.status == LIGHT_BROKEN || L.status == LIGHT_BURNED) - replaced_something = TRUE - AddShards(1, user) - qdel(L) - - if(!found_lightbulbs) - to_chat(user, "[S] contains no bulbs.") - return - - if(!replaced_something && uses == max_uses) - to_chat(user, "[src] is full!") - return - - to_chat(user, "You fill [src] with lights from [S]. " + status_string() + "") - return - return ..() - -/obj/item/lightreplacer/emag_act(user as mob) - if(!emagged) - Emag() - -/obj/item/lightreplacer/attack_self(mob/user) - for(var/obj/machinery/light/target in user.loc) - ReplaceLight(target, user) - to_chat(user, status_string()) - -/obj/item/lightreplacer/update_icon() - icon_state = "lightreplacer[emagged]" - -/obj/item/lightreplacer/proc/status_string() - return "It has [uses] light\s remaining (plus [bulb_shards] fragment\s)." - -/obj/item/lightreplacer/proc/Use(mob/user) - playsound(loc, 'sound/machines/click.ogg', 50, TRUE) - AddUses(-1) - return 1 - -// Negative numbers will subtract -/obj/item/lightreplacer/proc/AddUses(amount = 1) - uses = Clamp(uses + amount, 0, max_uses) - -/obj/item/lightreplacer/proc/AddShards(amount = 1, user) - bulb_shards += amount - var/new_bulbs = round(bulb_shards / shards_required) - if(new_bulbs > 0) - AddUses(new_bulbs) - bulb_shards = bulb_shards % shards_required - if(new_bulbs != 0) - to_chat(user, "[src] has fabricated a new bulb from the broken glass it has stored. It now has [uses] uses.") - playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) - return new_bulbs - -/obj/item/lightreplacer/proc/Charge(var/mob/user) - charge += 1 - if(charge > 3) - AddUses(1) - charge = 1 - -/obj/item/lightreplacer/proc/ReplaceLight(obj/machinery/light/target, mob/living/U) - if(target.status != LIGHT_OK) - if(CanUse(U)) - if(!Use(U)) - return - to_chat(U, "You replace [target.fitting] with [src].") - - if(target.status != LIGHT_EMPTY) - AddShards(1, U) - target.status = LIGHT_EMPTY - target.update() - - var/obj/item/light/L2 = new target.light_type() - - target.status = L2.status - target.switchcount = L2.switchcount - target.rigged = emagged - target.brightness_range = L2.brightness_range - target.brightness_power = L2.brightness_power - target.brightness_color = L2.brightness_color - target.on = target.has_power() - target.update() - qdel(L2) - - if(target.on && target.rigged) - target.explode() - return - - else - to_chat(U, "[src]'s refill light blinks red.") - return - else - to_chat(U, "There is a working [target.fitting] already inserted!") - return - -/obj/item/lightreplacer/proc/Emag() - emagged = !emagged - playsound(loc, "sparks", 100, TRUE) - if(emagged) - name = "shortcircuited [initial(name)]" - else - name = initial(name) - update_icon() - -/obj/item/lightreplacer/proc/CanUse(mob/living/user) - add_fingerprint(user) - if(uses > 0) - return 1 - else - return 0 - -/obj/item/lightreplacer/afterattack(atom/T, mob/U, proximity) - . = ..() - if(!proximity) - return - if(!isturf(T)) - return - - var/used = FALSE - for(var/atom/A in T) - if(!CanUse(U)) - break - used = TRUE - if(istype(A, /obj/machinery/light)) - ReplaceLight(A, U) - - if(!used) - to_chat(U, "[src]'s refill light blinks red.") - -/obj/item/lightreplacer/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) - J.put_in_cart(src, user) - J.myreplacer = src - J.update_icon() - -/obj/item/lightreplacer/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) - return - -#undef LIGHT_OK -#undef LIGHT_EMPTY -#undef LIGHT_BROKEN -#undef LIGHT_BURNED \ No newline at end of file + +// Light Replacer (LR) +// +// ABOUT THE DEVICE +// +// This is a device supposedly to be used by Janitors and Janitor Cyborgs which will +// allow them to easily replace lights. This was mostly designed for Janitor Cyborgs since +// they don't have hands or a way to replace lightbulbs. +// +// HOW IT WORKS +// +// You attack a light fixture with it, if the light fixture is broken it will replace the +// light fixture with a working light; the broken light is then placed on the floor for the +// user to then pickup with a trash bag. If it's empty then it will just place a light in the fixture. +// +// HOW TO REFILL THE DEVICE +// +// It will need to be manually refilled with lights. +// If it's part of a robot module, it will charge when the Robot is inside a Recharge Station. +// +// EMAGGED FEATURES +// +// NOTICE: The Cyborg cannot use the emagged Light Replacer and the light's explosion was nerfed. It cannot create holes in the station anymore. +// +// I'm not sure everyone will react the emag's features so please say what your opinions are of it. +// +// When emagged it will rig every light it replaces, which will explode when the light is on. +// This is VERY noticable, even the device's name changes when you emag it so if anyone +// examines you when you're holding it in your hand, you will be discovered. +// It will also be very obvious who is setting all these lights off, since only Janitor Borgs and Janitors have easy +// access to them, and only one of them can emag their device. +// +// The explosion cannot insta-kill anyone with 30% or more health. + +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + + +/obj/item/lightreplacer + + name = "light replacer" + desc = "A device to automatically replace lights. Refill with broken or working light bulbs, or sheets of glass." + + icon = 'icons/obj/janitor.dmi' + icon_state = "lightreplacer0" + item_state = "electronic" + w_class = WEIGHT_CLASS_SMALL + flags = CONDUCT + slot_flags = SLOT_BELT + origin_tech = "magnets=3;engineering=4" + force = 8 + + var/emagged = FALSE + var/max_uses = 20 + var/uses = 10 + // How much to increase per each glass? + var/increment = 5 + // How much to take from the glass? + var/decrement = 1 + var/charge = 1 + + // Eating used bulbs gives us bulb shards + var/bulb_shards = 0 + // when we get this many shards, we get a free bulb. + var/shards_required = 4 + + +/obj/item/lightreplacer/examine(mob/user) + . = ..() + . += status_string() + +/obj/item/lightreplacer/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = I + if(uses >= max_uses) + to_chat(user, "[src] is full.") + return + else if(G.use(decrement)) + AddUses(increment) + to_chat(user, "You insert a piece of glass into [src]. You have [uses] light\s remaining.") + return + else + to_chat(user, "You need one sheet of glass to replace lights!") + return + + if(istype(I, /obj/item/shard)) + if(uses >= max_uses) + to_chat(user, "[src] is full.") + return + if(!user.unEquip(I)) + return + AddUses(round(increment * 0.75)) + to_chat(user, "You insert a shard of glass into [src]. You have [uses] light\s remaining.") + qdel(I) + return + + if(istype(I, /obj/item/light)) + var/obj/item/light/L = I + if(L.status == 0) // LIGHT OKAY + if(uses < max_uses) + if(!user.unEquip(L)) + return + AddUses(1) + qdel(L) + else + if(!user.unEquip(L)) + return + to_chat(user, "You insert [L] into [src].") + AddShards(1, user) + qdel(L) + return + + if(istype(I, /obj/item/storage)) + var/obj/item/storage/S = I + var/found_lightbulbs = FALSE + var/replaced_something = TRUE + + for(var/obj/item/IT in S.contents) + if(istype(IT, /obj/item/light)) + var/obj/item/light/L = IT + found_lightbulbs = TRUE + if(uses >= max_uses) + break + if(L.status == LIGHT_OK) + replaced_something = TRUE + AddUses(1) + qdel(L) + + else if(L.status == LIGHT_BROKEN || L.status == LIGHT_BURNED) + replaced_something = TRUE + AddShards(1, user) + qdel(L) + + if(!found_lightbulbs) + to_chat(user, "[S] contains no bulbs.") + return + + if(!replaced_something && uses == max_uses) + to_chat(user, "[src] is full!") + return + + to_chat(user, "You fill [src] with lights from [S]. " + status_string() + "") + return + return ..() + +/obj/item/lightreplacer/emag_act(user as mob) + if(!emagged) + Emag() + +/obj/item/lightreplacer/attack_self(mob/user) + for(var/obj/machinery/light/target in user.loc) + ReplaceLight(target, user) + to_chat(user, status_string()) + +/obj/item/lightreplacer/update_icon() + icon_state = "lightreplacer[emagged]" + +/obj/item/lightreplacer/proc/status_string() + return "It has [uses] light\s remaining (plus [bulb_shards] fragment\s)." + +/obj/item/lightreplacer/proc/Use(mob/user) + playsound(loc, 'sound/machines/click.ogg', 50, TRUE) + AddUses(-1) + return 1 + +// Negative numbers will subtract +/obj/item/lightreplacer/proc/AddUses(amount = 1) + uses = Clamp(uses + amount, 0, max_uses) + +/obj/item/lightreplacer/proc/AddShards(amount = 1, user) + bulb_shards += amount + var/new_bulbs = round(bulb_shards / shards_required) + if(new_bulbs > 0) + AddUses(new_bulbs) + bulb_shards = bulb_shards % shards_required + if(new_bulbs != 0) + to_chat(user, "[src] has fabricated a new bulb from the broken glass it has stored. It now has [uses] uses.") + playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) + return new_bulbs + +/obj/item/lightreplacer/proc/Charge(var/mob/user) + charge += 1 + if(charge > 3) + AddUses(1) + charge = 1 + +/obj/item/lightreplacer/proc/ReplaceLight(obj/machinery/light/target, mob/living/U) + if(target.status != LIGHT_OK) + if(CanUse(U)) + if(!Use(U)) + return + to_chat(U, "You replace [target.fitting] with [src].") + + if(target.status != LIGHT_EMPTY) + AddShards(1, U) + target.status = LIGHT_EMPTY + target.update() + + var/obj/item/light/L2 = new target.light_type() + + target.status = L2.status + target.switchcount = L2.switchcount + target.rigged = emagged + target.brightness_range = L2.brightness_range + target.brightness_power = L2.brightness_power + target.brightness_color = L2.brightness_color + target.on = target.has_power() + target.update() + qdel(L2) + + if(target.on && target.rigged) + target.explode() + return + + else + to_chat(U, "[src]'s refill light blinks red.") + return + else + to_chat(U, "There is a working [target.fitting] already inserted!") + return + +/obj/item/lightreplacer/proc/Emag() + emagged = !emagged + playsound(loc, "sparks", 100, TRUE) + if(emagged) + name = "shortcircuited [initial(name)]" + else + name = initial(name) + update_icon() + +/obj/item/lightreplacer/proc/CanUse(mob/living/user) + add_fingerprint(user) + if(uses > 0) + return 1 + else + return 0 + +/obj/item/lightreplacer/afterattack(atom/T, mob/U, proximity) + . = ..() + if(!proximity) + return + if(!isturf(T)) + return + + var/used = FALSE + for(var/atom/A in T) + if(!CanUse(U)) + break + used = TRUE + if(istype(A, /obj/machinery/light)) + ReplaceLight(A, U) + + if(!used) + to_chat(U, "[src]'s refill light blinks red.") + +/obj/item/lightreplacer/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) + J.put_in_cart(src, user) + J.myreplacer = src + J.update_icon() + +/obj/item/lightreplacer/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) + return + +#undef LIGHT_OK +#undef LIGHT_EMPTY +#undef LIGHT_BROKEN +#undef LIGHT_BURNED diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index e6ffa264a51b..b36952297905 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -1,340 +1,340 @@ -/obj/item/paicard - name = "personal AI device" - icon = 'icons/obj/aicards.dmi' - icon_state = "pai" - item_state = "electronic" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - origin_tech = "programming=2" - var/request_cooldown = 5 // five seconds - var/last_request - var/obj/item/radio/radio - var/looking_for_personality = 0 - var/mob/living/silicon/pai/pai - var/list/faction = list("neutral") // The factions the pAI will inherit from the card - resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE - -/obj/item/paicard/syndicate - name = "syndicate personal AI device" - faction = list("syndicate") - -/obj/item/paicard/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/rig/rig = get_rig() - if(istype(rig)) - rig.forced_move(direction, user) - -/obj/item/paicard/New() - ..() - overlays += "pai-off" - -/obj/item/paicard/Destroy() - if(pai) - pai.ghostize() - QDEL_NULL(pai) - QDEL_NULL(radio) - return ..() - -/obj/item/paicard/attack_self(mob/user) - if(!in_range(src, user)) - return - user.set_machine(src) - var/dat = {" - - - - - - - "} - - if(pai) - dat += {" - Personal AI Device

    - - - - - - - - - - - - - -
    Installed Personality:[pai.name]
    Prime directive:[pai.pai_law0]
    Additional directives:[pai.pai_laws]
    -
    - "} - dat += {" - - -
    - Configure Directives -
    - "} - if(pai && (!pai.master_dna || !pai.master)) - dat += {" - - -
    - Imprint Master DNA -
    - "} - dat += "
    " - if(radio) - dat += "Radio Uplink" - dat += {" - - - - - - - - - -
    Transmit:[radio.broadcasting ? "En" : "Dis" ]abled - -
    Receive:[radio.listening ? "En" : "Dis" ]abled - -
    -
    - "} - else - dat += "Radio Uplink
    " - dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
    " - dat += {" - - -
    Wipe current pAI personality - -
    - "} - else - if(looking_for_personality) - dat += {" - pAI Request Module

    -

    Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

    - Searching for personalities, please wait...

    - - - - - -
    - Refresh available personalities -

    - "} - else - dat += {" - pAI Request Module

    -

    No personality is installed.

    - - - - -
    Request personality -
    -
    -

    Each time this button is pressed, a request will be sent out to any available personalities. Check back often give plenty of time for personalities to respond. This process could take anywhere from 15 seconds to several minutes, depending on the available personalities' timeliness.

    - "} - user << browse(dat, "window=paicard") - onclose(user, "paicard") - return - -/obj/item/paicard/Topic(href, href_list) - - var/mob/U = usr - - if(!usr || usr.stat) - return - - if(pai) - if(!in_range(src, U)) - U << browse(null, "window=paicard") - usr.unset_machine() - return - - if(href_list["setdna"]) - if(pai.master_dna) - return - var/mob/M = usr - if(!istype(M, /mob/living/carbon)) - to_chat(usr, "You don't have any DNA, or your DNA is incompatible with this device.") - else - var/datum/dna/dna = usr.dna - pai.master = M.real_name - pai.master_dna = dna.unique_enzymes - to_chat(pai, "

    You have been bound to a new master.

    ") - if(href_list["request"]) - var/delta = (world.time / 10) - last_request - if(request_cooldown > delta) - var/cooldown_time = round(request_cooldown - ((world.time / 10) - last_request), 1) - to_chat(usr, "The request system is currently offline. Please wait another [cooldown_time] seconds.") - return - last_request = world.time / 10 - looking_for_personality = 1 - paiController.findPAI(src, usr) - if(href_list["wipe"]) - var/confirm = input("Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe") in list("Yes", "No") - if(confirm == "Yes") - for(var/mob/M in src) - to_chat(M, "

    You feel yourself slipping away from reality.

    ") - to_chat(M, "

    Byte by byte you lose your sense of self.

    ") - to_chat(M, "

    Your mental faculties leave you.

    ") - to_chat(M, "
    oblivion...
    ") - var/mob/living/silicon/pai/P = M - if(istype(P)) - if(P.resting || P.canmove) - P.close_up() - M.death(0, 1) - removePersonality() - if(href_list["wires"]) - var/t1 = text2num(href_list["wires"]) - switch(t1) - if(4) - radio.ToggleBroadcast() - if(2) - radio.ToggleReception() - if(href_list["setlaws"]) - var/newlaws = sanitize(copytext(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws) as message,1,MAX_MESSAGE_LEN)) - if(newlaws) - pai.pai_laws = newlaws - to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") - to_chat(pai, "Prime Directive:
    [pai.pai_law0]") - to_chat(pai, "Supplemental Directives:
    [pai.pai_laws]") - attack_self(usr) - -// WIRE_SIGNAL = 1 -// WIRE_RECEIVE = 2 -// WIRE_TRANSMIT = 4 - -/obj/item/paicard/proc/setPersonality(mob/living/silicon/pai/personality) - pai = personality - overlays += "pai-happy" - -/obj/item/paicard/proc/removePersonality() - pai = null - overlays.Cut() - overlays += "pai-off" - -/obj/item/paicard - var/current_emotion = 1 -/obj/item/paicard/proc/setEmotion(var/emotion) - if(pai) - overlays.Cut() - switch(emotion) - if(1) overlays += "pai-happy" - if(2) overlays += "pai-cat" - if(3) overlays += "pai-extremely-happy" - if(4) overlays += "pai-face" - if(5) overlays += "pai-laugh" - if(6) overlays += "pai-off" - if(7) overlays += "pai-sad" - if(8) overlays += "pai-angry" - if(9) overlays += "pai-what" - current_emotion = emotion - -/obj/item/paicard/proc/alertUpdate() - var/turf/T = get_turf_or_move(loc) - for(var/mob/M in viewers(T)) - M.show_message("[src] flashes a message across its screen, \"Additional personalities available for download.\"", 3, "[src] bleeps electronically.", 2) - -/obj/item/paicard/emp_act(severity) - for(var/mob/M in src) - M.emp_act(severity) - ..() - -/obj/item/paicard/extinguish_light() - pai.extinguish_light() - set_light(0) \ No newline at end of file +/obj/item/paicard + name = "personal AI device" + icon = 'icons/obj/aicards.dmi' + icon_state = "pai" + item_state = "electronic" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + origin_tech = "programming=2" + var/request_cooldown = 5 // five seconds + var/last_request + var/obj/item/radio/radio + var/looking_for_personality = 0 + var/mob/living/silicon/pai/pai + var/list/faction = list("neutral") // The factions the pAI will inherit from the card + resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE + +/obj/item/paicard/syndicate + name = "syndicate personal AI device" + faction = list("syndicate") + +/obj/item/paicard/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/rig/rig = get_rig() + if(istype(rig)) + rig.forced_move(direction, user) + +/obj/item/paicard/New() + ..() + overlays += "pai-off" + +/obj/item/paicard/Destroy() + if(pai) + pai.ghostize() + QDEL_NULL(pai) + QDEL_NULL(radio) + return ..() + +/obj/item/paicard/attack_self(mob/user) + if(!in_range(src, user)) + return + user.set_machine(src) + var/dat = {" + + + + + + + "} + + if(pai) + dat += {" + Personal AI Device

    + + + + + + + + + + + + + +
    Installed Personality:[pai.name]
    Prime directive:[pai.pai_law0]
    Additional directives:[pai.pai_laws]
    +
    + "} + dat += {" + + +
    + Configure Directives +
    + "} + if(pai && (!pai.master_dna || !pai.master)) + dat += {" + + +
    + Imprint Master DNA +
    + "} + dat += "
    " + if(radio) + dat += "Radio Uplink" + dat += {" + + + + + + + + + +
    Transmit:[radio.broadcasting ? "En" : "Dis" ]abled + +
    Receive:[radio.listening ? "En" : "Dis" ]abled + +
    +
    + "} + else + dat += "Radio Uplink
    " + dat += "Radio firmware not loaded. Please install a pAI personality to load firmware.
    " + dat += {" + + +
    Wipe current pAI personality + +
    + "} + else + if(looking_for_personality) + dat += {" + pAI Request Module

    +

    Requesting AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

    + Searching for personalities, please wait...

    + + + + + +
    + Refresh available personalities +

    + "} + else + dat += {" + pAI Request Module

    +

    No personality is installed.

    + + + + +
    Request personality +
    +
    +

    Each time this button is pressed, a request will be sent out to any available personalities. Check back often give plenty of time for personalities to respond. This process could take anywhere from 15 seconds to several minutes, depending on the available personalities' timeliness.

    + "} + user << browse(dat, "window=paicard") + onclose(user, "paicard") + return + +/obj/item/paicard/Topic(href, href_list) + + var/mob/U = usr + + if(!usr || usr.stat) + return + + if(pai) + if(!in_range(src, U)) + U << browse(null, "window=paicard") + usr.unset_machine() + return + + if(href_list["setdna"]) + if(pai.master_dna) + return + var/mob/M = usr + if(!istype(M, /mob/living/carbon)) + to_chat(usr, "You don't have any DNA, or your DNA is incompatible with this device.") + else + var/datum/dna/dna = usr.dna + pai.master = M.real_name + pai.master_dna = dna.unique_enzymes + to_chat(pai, "

    You have been bound to a new master.

    ") + if(href_list["request"]) + var/delta = (world.time / 10) - last_request + if(request_cooldown > delta) + var/cooldown_time = round(request_cooldown - ((world.time / 10) - last_request), 1) + to_chat(usr, "The request system is currently offline. Please wait another [cooldown_time] seconds.") + return + last_request = world.time / 10 + looking_for_personality = 1 + GLOB.paiController.findPAI(src, usr) + if(href_list["wipe"]) + var/confirm = input("Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe") in list("Yes", "No") + if(confirm == "Yes") + for(var/mob/M in src) + to_chat(M, "

    You feel yourself slipping away from reality.

    ") + to_chat(M, "

    Byte by byte you lose your sense of self.

    ") + to_chat(M, "

    Your mental faculties leave you.

    ") + to_chat(M, "
    oblivion...
    ") + var/mob/living/silicon/pai/P = M + if(istype(P)) + if(P.resting || P.canmove) + P.close_up() + M.death(0, 1) + removePersonality() + if(href_list["wires"]) + var/t1 = text2num(href_list["wires"]) + switch(t1) + if(4) + radio.ToggleBroadcast() + if(2) + radio.ToggleReception() + if(href_list["setlaws"]) + var/newlaws = sanitize(copytext(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.pai_laws) as message,1,MAX_MESSAGE_LEN)) + if(newlaws) + pai.pai_laws = newlaws + to_chat(pai, "Your supplemental directives have been updated. Your new directives are:") + to_chat(pai, "Prime Directive:
    [pai.pai_law0]") + to_chat(pai, "Supplemental Directives:
    [pai.pai_laws]") + attack_self(usr) + +// WIRE_SIGNAL = 1 +// WIRE_RECEIVE = 2 +// WIRE_TRANSMIT = 4 + +/obj/item/paicard/proc/setPersonality(mob/living/silicon/pai/personality) + pai = personality + overlays += "pai-happy" + +/obj/item/paicard/proc/removePersonality() + pai = null + overlays.Cut() + overlays += "pai-off" + +/obj/item/paicard + var/current_emotion = 1 +/obj/item/paicard/proc/setEmotion(var/emotion) + if(pai) + overlays.Cut() + switch(emotion) + if(1) overlays += "pai-happy" + if(2) overlays += "pai-cat" + if(3) overlays += "pai-extremely-happy" + if(4) overlays += "pai-face" + if(5) overlays += "pai-laugh" + if(6) overlays += "pai-off" + if(7) overlays += "pai-sad" + if(8) overlays += "pai-angry" + if(9) overlays += "pai-what" + current_emotion = emotion + +/obj/item/paicard/proc/alertUpdate() + var/turf/T = get_turf_or_move(loc) + for(var/mob/M in viewers(T)) + M.show_message("[src] flashes a message across its screen, \"Additional personalities available for download.\"", 3, "[src] bleeps electronically.", 2) + +/obj/item/paicard/emp_act(severity) + for(var/mob/M in src) + M.emp_act(severity) + ..() + +/obj/item/paicard/extinguish_light() + pai.extinguish_light() + set_light(0) diff --git a/code/game/objects/items/devices/pipe_painter.dm b/code/game/objects/items/devices/pipe_painter.dm index 69beb04f67cc..842a96c2af27 100644 --- a/code/game/objects/items/devices/pipe_painter.dm +++ b/code/game/objects/items/devices/pipe_painter.dm @@ -9,7 +9,7 @@ /obj/item/pipe_painter/New() ..() modes = new() - for(var/C in pipe_colors) + for(var/C in GLOB.pipe_colors) modes += "[C]" mode = pick(modes) @@ -23,7 +23,7 @@ to_chat(user, "You must remove the plating first.") return - P.change_color(pipe_colors[mode]) + P.change_color(GLOB.pipe_colors[mode]) /obj/item/pipe_painter/attack_self(mob/user as mob) mode = input("Which colour do you want to use?", "Pipe Painter", mode) in modes diff --git a/code/game/objects/items/devices/pizza_bomb.dm b/code/game/objects/items/devices/pizza_bomb.dm index 71cb95d436f5..c12331b2be2e 100644 --- a/code/game/objects/items/devices/pizza_bomb.dm +++ b/code/game/objects/items/devices/pizza_bomb.dm @@ -102,4 +102,4 @@ /obj/item/pizza_bomb/autoarm timer_set = 1 - timer = 30 // 3 seconds \ No newline at end of file + timer = 30 // 3 seconds diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm index e354adc28e49..68f52721f86b 100644 --- a/code/game/objects/items/devices/powersink.dm +++ b/code/game/objects/items/devices/powersink.dm @@ -1,153 +1,153 @@ -#define DISCONNECTED 0 -#define CLAMPED_OFF 1 -#define OPERATING 2 - -// Powersink - used to drain station power - -/obj/item/powersink - name = "power sink" - desc = "A nulling power sink which drains energy from electrical systems." - icon = 'icons/obj/device.dmi' - icon_state = "powersink0" - item_state = "electronic" - w_class = WEIGHT_CLASS_BULKY - flags = CONDUCT - throwforce = 5 - throw_speed = 1 - throw_range = 2 - materials = list(MAT_METAL=750) - origin_tech = "powerstorage=5;syndicate=5" - var/drain_rate = 2000000 // amount of power to drain per tick - var/power_drained = 0 // has drained this much power - var/max_power = 6e8 // maximum power that can be drained before exploding - var/mode = 0 // 0 = off, 1=clamped (off), 2=operating - var/admins_warned = FALSE // stop spam, only warn the admins once that we are about to boom - - var/obj/structure/cable/attached // the attached cable - -/obj/item/powersink/Destroy() - STOP_PROCESSING(SSobj, src) - attached = null - return ..() - -/obj/item/powersink/update_icon() - icon_state = "powersink[mode == OPERATING]" - -/obj/item/powersink/proc/set_mode(value) - if(value == mode) - return - switch(value) - if(DISCONNECTED) - attached = null - if(mode == OPERATING) - STOP_PROCESSING(SSobj, src) - anchored = FALSE - density = FALSE - - if(CLAMPED_OFF) - if(!attached) - return - if(mode == OPERATING) - STOP_PROCESSING(SSobj, src) - anchored = TRUE - density = TRUE - - if(OPERATING) - if(!attached) - return - START_PROCESSING(SSobj, src) - anchored = TRUE - density = TRUE - - mode = value - update_icon() - set_light(0) - -/obj/item/powersink/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(mode == DISCONNECTED) - var/turf/T = loc - if(isturf(T) && !T.intact) - attached = locate() in T - if(!attached) - to_chat(user, "No exposed cable here to attach to.") - return - else - set_mode(CLAMPED_OFF) - visible_message("[user] attaches [src] to the cable!") - message_admins("Power sink activated by [key_name_admin(user)] at ([x],[y],[z] - JMP)") - log_game("Power sink activated by [key_name(user)] at ([x],[y],[z])") - else - to_chat(user, "Device must be placed over an exposed cable to attach to it.") - else - set_mode(DISCONNECTED) - src.visible_message("[user] detaches [src] from the cable!") - -/obj/item/powersink/attack_ai() - return - -/obj/item/powersink/attack_hand(var/mob/user) - switch(mode) - if(DISCONNECTED) - ..() - if(CLAMPED_OFF) - user.visible_message( \ - "[user] activates \the [src]!", \ - "You activate \the [src].", - "You hear a click.") - message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(src)]") - log_game("Power sink activated by [key_name(user)] at [AREACOORD(src)]") - set_mode(OPERATING) - - if(OPERATING) - user.visible_message( \ - "[user] deactivates \the [src]!", \ - "You deactivate \the [src].", - "You hear a click.") - set_mode(CLAMPED_OFF) - -/obj/item/powersink/process() - if(!attached) - set_mode(DISCONNECTED) - return - - var/datum/powernet/PN = attached.powernet - if(PN) - set_light(5) - - // found a powernet, so drain up to max power from it - - var/drained = min (drain_rate, attached.newavail()) - attached.add_delayedload(drained) - power_drained += drained - - // if tried to drain more than available on powernet - // now look for APCs and drain their cells - if(drained < drain_rate) - for(var/obj/machinery/power/terminal/T in PN.nodes) - if(istype(T.master, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = T.master - if(A.operating && A.cell) - A.cell.charge = max(0, A.cell.charge - 50) - power_drained += 50 - if(A.charging == 2) // If the cell was full - A.charging = 1 // It's no longer full - if(drained >= drain_rate) - break - - if(power_drained > max_power * 0.98) - if (!admins_warned) - admins_warned = TRUE - message_admins("Power sink at ([x],[y],[z] - JMP) is 95% full. Explosion imminent.") - playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) - - if(power_drained >= max_power) - STOP_PROCESSING(SSobj, src) - explosion(src.loc, 4,8,16,32) - qdel(src) - -#undef DISCONNECTED -#undef CLAMPED_OFF -#undef OPERATING \ No newline at end of file +#define DISCONNECTED 0 +#define CLAMPED_OFF 1 +#define OPERATING 2 + +// Powersink - used to drain station power + +/obj/item/powersink + name = "power sink" + desc = "A nulling power sink which drains energy from electrical systems." + icon = 'icons/obj/device.dmi' + icon_state = "powersink0" + item_state = "electronic" + w_class = WEIGHT_CLASS_BULKY + flags = CONDUCT + throwforce = 5 + throw_speed = 1 + throw_range = 2 + materials = list(MAT_METAL=750) + origin_tech = "powerstorage=5;syndicate=5" + var/drain_rate = 2000000 // amount of power to drain per tick + var/power_drained = 0 // has drained this much power + var/max_power = 6e8 // maximum power that can be drained before exploding + var/mode = 0 // 0 = off, 1=clamped (off), 2=operating + var/admins_warned = FALSE // stop spam, only warn the admins once that we are about to boom + + var/obj/structure/cable/attached // the attached cable + +/obj/item/powersink/Destroy() + STOP_PROCESSING(SSobj, src) + attached = null + return ..() + +/obj/item/powersink/update_icon() + icon_state = "powersink[mode == OPERATING]" + +/obj/item/powersink/proc/set_mode(value) + if(value == mode) + return + switch(value) + if(DISCONNECTED) + attached = null + if(mode == OPERATING) + STOP_PROCESSING(SSobj, src) + anchored = FALSE + density = FALSE + + if(CLAMPED_OFF) + if(!attached) + return + if(mode == OPERATING) + STOP_PROCESSING(SSobj, src) + anchored = TRUE + density = TRUE + + if(OPERATING) + if(!attached) + return + START_PROCESSING(SSobj, src) + anchored = TRUE + density = TRUE + + mode = value + update_icon() + set_light(0) + +/obj/item/powersink/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(mode == DISCONNECTED) + var/turf/T = loc + if(isturf(T) && !T.intact) + attached = locate() in T + if(!attached) + to_chat(user, "No exposed cable here to attach to.") + return + else + set_mode(CLAMPED_OFF) + visible_message("[user] attaches [src] to the cable!") + message_admins("Power sink activated by [key_name_admin(user)] at ([x],[y],[z] - JMP)") + log_game("Power sink activated by [key_name(user)] at ([x],[y],[z])") + else + to_chat(user, "Device must be placed over an exposed cable to attach to it.") + else + set_mode(DISCONNECTED) + src.visible_message("[user] detaches [src] from the cable!") + +/obj/item/powersink/attack_ai() + return + +/obj/item/powersink/attack_hand(var/mob/user) + switch(mode) + if(DISCONNECTED) + ..() + if(CLAMPED_OFF) + user.visible_message( \ + "[user] activates \the [src]!", \ + "You activate \the [src].", + "You hear a click.") + message_admins("Power sink activated by [ADMIN_LOOKUPFLW(user)] at [ADMIN_VERBOSEJMP(src)]") + log_game("Power sink activated by [key_name(user)] at [AREACOORD(src)]") + set_mode(OPERATING) + + if(OPERATING) + user.visible_message( \ + "[user] deactivates \the [src]!", \ + "You deactivate \the [src].", + "You hear a click.") + set_mode(CLAMPED_OFF) + +/obj/item/powersink/process() + if(!attached) + set_mode(DISCONNECTED) + return + + var/datum/powernet/PN = attached.powernet + if(PN) + set_light(5) + + // found a powernet, so drain up to max power from it + + var/drained = min (drain_rate, attached.newavail()) + attached.add_delayedload(drained) + power_drained += drained + + // if tried to drain more than available on powernet + // now look for APCs and drain their cells + if(drained < drain_rate) + for(var/obj/machinery/power/terminal/T in PN.nodes) + if(istype(T.master, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = T.master + if(A.operating && A.cell) + A.cell.charge = max(0, A.cell.charge - 50) + power_drained += 50 + if(A.charging == 2) // If the cell was full + A.charging = 1 // It's no longer full + if(drained >= drain_rate) + break + + if(power_drained > max_power * 0.98) + if (!admins_warned) + admins_warned = TRUE + message_admins("Power sink at ([x],[y],[z] - JMP) is 95% full. Explosion imminent.") + playsound(src, 'sound/effects/screech.ogg', 100, 1, 1) + + if(power_drained >= max_power) + STOP_PROCESSING(SSobj, src) + explosion(src.loc, 4,8,16,32) + qdel(src) + +#undef DISCONNECTED +#undef CLAMPED_OFF +#undef OPERATING diff --git a/code/game/objects/items/devices/radio/beacon.dm b/code/game/objects/items/devices/radio/beacon.dm index 041b21ede105..517f9f595c5e 100644 --- a/code/game/objects/items/devices/radio/beacon.dm +++ b/code/game/objects/items/devices/radio/beacon.dm @@ -1,106 +1,106 @@ -/obj/item/radio/beacon - name = "Tracking Beacon" - desc = "A beacon used by a teleporter." - icon_state = "beacon" - item_state = "signaler" - var/code = "Beacon" - origin_tech = "bluespace=1" - var/emagged = 0 - var/syndicate = 0 - var/area_bypass = FALSE - var/cc_beacon = FALSE //set if allowed to teleport to even if on zlevel2 - -/obj/item/radio/beacon/New() - ..() - code = "[code] ([GLOB.beacons.len + 1])" - GLOB.beacons += src - -/obj/item/radio/beacon/Destroy() - GLOB.beacons -= src - return ..() - -/obj/item/radio/beacon/emag_act(user as mob) - if(!emagged) - emagged = 1 - syndicate = 1 - to_chat(user, "The This beacon now only be locked on to by emagged teleporters!") - -/obj/item/radio/beacon/hear_talk() - return - - -/obj/item/radio/beacon/send_hear() - return null - -/obj/item/radio/beacon/verb/alter_signal(t as text) - set name = "Alter Beacon's Signal" - set category = "Object" - set src in usr - - if(usr.stat || usr.restrained()) - return - - code = t - if(isnull(code)) - code = initial(code) - src.add_fingerprint(usr) - return - -/obj/item/radio/beacon/bacon //Probably a better way of doing this, I'm lazy. - -/obj/item/radio/beacon/bacon/proc/digest_delay() - QDEL_IN(src, 600) - -// SINGULO BEACON SPAWNER -/obj/item/radio/beacon/syndicate - name = "suspicious beacon" - desc = "A label on it reads: Activate to have a singularity beacon teleported to your location." - origin_tech = "bluespace=6;syndicate=5" - syndicate = TRUE - var/obj/machinery/computer/syndicate_depot/teleporter/mycomputer - -/obj/item/radio/beacon/syndicate/Destroy() - if(mycomputer) - mycomputer.mybeacon = null - return ..() - -/obj/item/radio/beacon/syndicate/attack_self(mob/user) - if(user) - to_chat(user, "Locked In") - new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) - playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) - qdel(src) - return - -/obj/item/radio/beacon/syndicate/bomb - name = "suspicious beacon" - desc = "A label on it reads: Warning: Activating this device will send a high-ordinance explosive to your location." - origin_tech = "bluespace=5;syndicate=5" - -/obj/item/radio/beacon/syndicate/bomb/attack_self(mob/user) - if(user) - to_chat(user, "Locked In") - new /obj/machinery/syndicatebomb( user.loc ) - playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) - qdel(src) - return - -/obj/item/radio/beacon/engine - desc = "A label on it reads: Warning: This device is used for transportation of high-density objects used for high-yield power generation. Stay away!." - anchored = 1 //Let's not move these around. Some folk might get the idea to use these for assassinations - var/list/enginetype = list() - -/obj/item/radio/beacon/engine/Initialize() - LAZYADD(GLOB.engine_beacon_list, src) - -/obj/item/radio/beacon/engine/tesling - name = "Engine Beacon for Tesla and Singularity" - enginetype = list(ENGTYPE_TESLA, ENGTYPE_SING) - -/obj/item/radio/beacon/engine/tesla - name = "Engine Beacon for Tesla" - enginetype = list(ENGTYPE_TESLA) - -/obj/item/radio/beacon/engine/sing - name = "Engine Beacon for Singularity" - enginetype = list(ENGTYPE_SING) \ No newline at end of file +/obj/item/radio/beacon + name = "Tracking Beacon" + desc = "A beacon used by a teleporter." + icon_state = "beacon" + item_state = "signaler" + var/code = "Beacon" + origin_tech = "bluespace=1" + var/emagged = 0 + var/syndicate = 0 + var/area_bypass = FALSE + var/cc_beacon = FALSE //set if allowed to teleport to even if on zlevel2 + +/obj/item/radio/beacon/New() + ..() + code = "[code] ([GLOB.beacons.len + 1])" + GLOB.beacons += src + +/obj/item/radio/beacon/Destroy() + GLOB.beacons -= src + return ..() + +/obj/item/radio/beacon/emag_act(user as mob) + if(!emagged) + emagged = 1 + syndicate = 1 + to_chat(user, "The This beacon now only be locked on to by emagged teleporters!") + +/obj/item/radio/beacon/hear_talk() + return + + +/obj/item/radio/beacon/send_hear() + return null + +/obj/item/radio/beacon/verb/alter_signal(t as text) + set name = "Alter Beacon's Signal" + set category = "Object" + set src in usr + + if(usr.stat || usr.restrained()) + return + + code = t + if(isnull(code)) + code = initial(code) + src.add_fingerprint(usr) + return + +/obj/item/radio/beacon/bacon //Probably a better way of doing this, I'm lazy. + +/obj/item/radio/beacon/bacon/proc/digest_delay() + QDEL_IN(src, 600) + +// SINGULO BEACON SPAWNER +/obj/item/radio/beacon/syndicate + name = "suspicious beacon" + desc = "A label on it reads: Activate to have a singularity beacon teleported to your location." + origin_tech = "bluespace=6;syndicate=5" + syndicate = TRUE + var/obj/machinery/computer/syndicate_depot/teleporter/mycomputer + +/obj/item/radio/beacon/syndicate/Destroy() + if(mycomputer) + mycomputer.mybeacon = null + return ..() + +/obj/item/radio/beacon/syndicate/attack_self(mob/user) + if(user) + to_chat(user, "Locked In") + new /obj/machinery/power/singularity_beacon/syndicate( user.loc ) + playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) + qdel(src) + return + +/obj/item/radio/beacon/syndicate/bomb + name = "suspicious beacon" + desc = "A label on it reads: Warning: Activating this device will send a high-ordinance explosive to your location." + origin_tech = "bluespace=5;syndicate=5" + +/obj/item/radio/beacon/syndicate/bomb/attack_self(mob/user) + if(user) + to_chat(user, "Locked In") + new /obj/machinery/syndicatebomb( user.loc ) + playsound(src, 'sound/effects/pop.ogg', 100, 1, 1) + qdel(src) + return + +/obj/item/radio/beacon/engine + desc = "A label on it reads: Warning: This device is used for transportation of high-density objects used for high-yield power generation. Stay away!." + anchored = 1 //Let's not move these around. Some folk might get the idea to use these for assassinations + var/list/enginetype = list() + +/obj/item/radio/beacon/engine/Initialize() + LAZYADD(GLOB.engine_beacon_list, src) + +/obj/item/radio/beacon/engine/tesling + name = "Engine Beacon for Tesla and Singularity" + enginetype = list(ENGTYPE_TESLA, ENGTYPE_SING) + +/obj/item/radio/beacon/engine/tesla + name = "Engine Beacon for Tesla" + enginetype = list(ENGTYPE_TESLA) + +/obj/item/radio/beacon/engine/sing + name = "Engine Beacon for Singularity" + enginetype = list(ENGTYPE_SING) diff --git a/code/game/objects/items/devices/radio/electropack.dm b/code/game/objects/items/devices/radio/electropack.dm index 2e53e4ff06a2..083720f0e22b 100644 --- a/code/game/objects/items/devices/radio/electropack.dm +++ b/code/game/objects/items/devices/radio/electropack.dm @@ -1,113 +1,113 @@ -/obj/item/radio/electropack - name = "electropack" - desc = "Dance my monkeys! DANCE!!!" - icon_state = "electropack0" - item_state = "electropack" - frequency = AIRLOCK_FREQ - flags = CONDUCT - slot_flags = SLOT_BACK - w_class = WEIGHT_CLASS_HUGE - materials = list(MAT_METAL=10000, MAT_GLASS=2500) - var/code = 2 - - is_special = 1 - -/obj/item/radio/electropack/attack_hand(mob/user as mob) - if(src == user.back) - to_chat(user, "You need help taking this off!") - return 0 - . = ..() - -/obj/item/radio/electropack/Destroy() - if(istype(src.loc, /obj/item/assembly/shock_kit)) - var/obj/item/assembly/shock_kit/S = src.loc - if(S.part1 == src) - S.part1 = null - else if(S.part2 == src) - S.part2 = null - master = null - return ..() - -/obj/item/radio/electropack/attackby(obj/item/W as obj, mob/user as mob, params) - ..() - if(istype(W, /obj/item/clothing/head/helmet)) - if(!b_stat) - to_chat(user, "[src] is not ready to be attached!") - return - var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) - A.icon = 'icons/obj/assemblies.dmi' - - if(!user.unEquip(W)) - to_chat(user, "\the [W] is stuck to your hand, you cannot attach it to \the [src]!") - return - W.loc = A - W.master = A - A.part1 = W - - user.unEquip(src) - loc = A - master = A - A.part2 = src - - user.put_in_hands(A) - A.add_fingerprint(user) - if(src.flags & NODROP) - A.flags |= NODROP - -/obj/item/radio/electropack/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["freq"]) - var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) - set_frequency(new_frequency) - - else if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = Clamp(code, 1, 100) - - else if(href_list["power"]) - on = !on - - add_fingerprint(usr) - -/obj/item/radio/electropack/receive_signal(datum/signal/signal) - if(!signal || signal.encryption != code) - return - - if(ismob(loc) && on) - var/mob/M = loc - var/turf/T = M.loc - if(istype(T, /turf)) - if(!M.moved_recently && M.last_move) - M.moved_recently = 1 - step(M, M.last_move) - sleep(50) - if(M) - M.moved_recently = 0 - to_chat(M, "You feel a sharp shock!") - do_sparks(3, 1, M) - - M.Weaken(5) - - if(master) - master.receive_signal() - return - - -/obj/item/radio/electropack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "radio_electro.tmpl", "[name]", 400, 500) - ui.open() - ui.set_auto_update(1) - -/obj/item/radio/electropack/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["power"] = on - data["freq"] = format_frequency(frequency) - data["code"] = code - - return data +/obj/item/radio/electropack + name = "electropack" + desc = "Dance my monkeys! DANCE!!!" + icon_state = "electropack0" + item_state = "electropack" + frequency = AIRLOCK_FREQ + flags = CONDUCT + slot_flags = SLOT_BACK + w_class = WEIGHT_CLASS_HUGE + materials = list(MAT_METAL=10000, MAT_GLASS=2500) + var/code = 2 + + is_special = 1 + +/obj/item/radio/electropack/attack_hand(mob/user as mob) + if(src == user.back) + to_chat(user, "You need help taking this off!") + return 0 + . = ..() + +/obj/item/radio/electropack/Destroy() + if(istype(src.loc, /obj/item/assembly/shock_kit)) + var/obj/item/assembly/shock_kit/S = src.loc + if(S.part1 == src) + S.part1 = null + else if(S.part2 == src) + S.part2 = null + master = null + return ..() + +/obj/item/radio/electropack/attackby(obj/item/W as obj, mob/user as mob, params) + ..() + if(istype(W, /obj/item/clothing/head/helmet)) + if(!b_stat) + to_chat(user, "[src] is not ready to be attached!") + return + var/obj/item/assembly/shock_kit/A = new /obj/item/assembly/shock_kit( user ) + A.icon = 'icons/obj/assemblies.dmi' + + if(!user.unEquip(W)) + to_chat(user, "\the [W] is stuck to your hand, you cannot attach it to \the [src]!") + return + W.loc = A + W.master = A + A.part1 = W + + user.unEquip(src) + loc = A + master = A + A.part2 = src + + user.put_in_hands(A) + A.add_fingerprint(user) + if(src.flags & NODROP) + A.flags |= NODROP + +/obj/item/radio/electropack/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["freq"]) + var/new_frequency = sanitize_frequency(frequency + text2num(href_list["freq"])) + set_frequency(new_frequency) + + else if(href_list["code"]) + code += text2num(href_list["code"]) + code = round(code) + code = Clamp(code, 1, 100) + + else if(href_list["power"]) + on = !on + + add_fingerprint(usr) + +/obj/item/radio/electropack/receive_signal(datum/signal/signal) + if(!signal || signal.encryption != code) + return + + if(ismob(loc) && on) + var/mob/M = loc + var/turf/T = M.loc + if(istype(T, /turf)) + if(!M.moved_recently && M.last_move) + M.moved_recently = 1 + step(M, M.last_move) + sleep(50) + if(M) + M.moved_recently = 0 + to_chat(M, "You feel a sharp shock!") + do_sparks(3, 1, M) + + M.Weaken(5) + + if(master) + master.receive_signal() + return + + +/obj/item/radio/electropack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "radio_electro.tmpl", "[name]", 400, 500) + ui.open() + ui.set_auto_update(1) + +/obj/item/radio/electropack/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["power"] = on + data["freq"] = format_frequency(frequency) + data["code"] = code + + return data diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index e6716a4f5c85..72e584e91aa4 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -1,426 +1,426 @@ -/obj/item/radio/headset - name = "radio headset" - desc = "An updated, modular intercom that fits over the head. Takes encryption keys." - var/radio_desc = "" - icon_state = "headset" - item_state = "headset" - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/ears.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' - ) //We read you loud and skree-er. - materials = list(MAT_METAL=75) - subspace_transmission = TRUE - canhear_range = 0 // can't hear headsets from very far away - - slot_flags = SLOT_EARS - var/translate_binary = FALSE - var/translate_hive = FALSE - var/obj/item/encryptionkey/keyslot1 = null - var/obj/item/encryptionkey/keyslot2 = null - - var/ks1type = null - var/ks2type = null - dog_fashion = null - -/obj/item/radio/headset/New() - ..() - internal_channels.Cut() - -/obj/item/radio/headset/Initialize() - ..() - - if(ks1type) - keyslot1 = new ks1type(src) - if(keyslot1.syndie) - syndiekey = keyslot1 - if(ks2type) - keyslot2 = new ks2type(src) - if(keyslot2.syndie) - syndiekey = keyslot2 - - recalculateChannels(TRUE) - -/obj/item/radio/headset/Destroy() - QDEL_NULL(keyslot1) - QDEL_NULL(keyslot2) - return ..() - -/obj/item/radio/headset/list_channels(var/mob/user) - return list_secure_channels() - -/obj/item/radio/headset/examine(mob/user) - . = ..() - if(in_range(src, user) && radio_desc) - . += "The following channels are available:" - . += radio_desc - -/obj/item/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) - if(channel == "special") - if(translate_binary) - var/datum/language/binary = GLOB.all_languages["Robot Talk"] - binary.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces))) - return RADIO_CONNECTION_NON_SUBSPACE - if(translate_hive) - var/datum/language/hivemind = GLOB.all_languages["Hivemind"] - hivemind.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces))) - return RADIO_CONNECTION_NON_SUBSPACE - return RADIO_CONNECTION_FAIL - - return ..() - -/obj/item/radio/headset/is_listening() - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.l_ear == src || H.r_ear == src) - return ..() - else if(isanimal(loc) || isAI(loc)) - return ..() - - return FALSE - -/obj/item/radio/headset/alt - name = "bowman headset" - desc = "An updated, modular intercom that fits over the head. Takes encryption keys. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - -/obj/item/radio/headset/syndicate - origin_tech = "syndicate=3" - ks1type = /obj/item/encryptionkey/syndicate/nukeops - -/obj/item/radio/headset/syndicate/alt //undisguised bowman with flash protection - name = "syndicate headset" - desc = "A syndicate headset that can be used to hear all radio frequencies. Protects ears from flashbangs." - flags = EARBANGPROTECT - origin_tech = "syndicate=3" - icon_state = "syndie_headset" - item_state = "syndie_headset" - -/obj/item/radio/headset/syndicate/syndteam - ks1type = /obj/item/encryptionkey/syndteam - -/obj/item/radio/headset/syndicate/alt/syndteam - ks1type = /obj/item/encryptionkey/syndteam - -/obj/item/radio/headset/binary - origin_tech = "syndicate=3" - ks1type = /obj/item/encryptionkey/binary - -/obj/item/radio/headset/headset_sec - name = "security radio headset" - desc = "This is used by your elite security force." - icon_state = "sec_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_sec - -/obj/item/radio/headset/headset_sec/alt - name = "security bowman headset" - desc = "This is used by your elite security force. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "sec_headset_alt" - item_state = "sec_headset_alt" - -/obj/item/radio/headset/headset_eng - name = "engineering radio headset" - desc = "When the engineers wish to chat like girls." - icon_state = "eng_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_eng - -/obj/item/radio/headset/headset_rob - name = "robotics radio headset" - desc = "Made specifically for the roboticists who cannot decide between departments." - icon_state = "rob_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_rob - -/obj/item/radio/headset/headset_med - name = "medical radio headset" - desc = "A headset for the trained staff of the medbay." - icon_state = "med_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_med - -/obj/item/radio/headset/headset_sci - name = "science radio headset" - desc = "A sciency headset. Like usual." - icon_state = "sci_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_sci - -/obj/item/radio/headset/headset_medsci - name = "medical research radio headset" - desc = "A headset that is a result of the mating between medical and science." - icon_state = "medsci_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_medsci - -/obj/item/radio/headset/headset_com - name = "command radio headset" - desc = "A headset with a commanding channel." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_com - -/obj/item/radio/headset/heads/captain - name = "captain's headset" - desc = "The headset of the boss." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/captain - -/obj/item/radio/headset/heads/captain/alt - name = "\proper the captain's bowman headset" - desc = "The headset of the boss. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - -/obj/item/radio/headset/heads/rd - name = "Research Director's headset" - desc = "Headset of the researching God." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/rd - -/obj/item/radio/headset/heads/hos - name = "head of security's headset" - desc = "The headset of the man who protects your worthless lives." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/hos - -/obj/item/radio/headset/heads/hos/alt - name = "\proper the head of security's bowman headset" - desc = "The headset of the man in charge of keeping order and protecting the station. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - -/obj/item/radio/headset/heads/ce - name = "chief engineer's headset" - desc = "The headset of the guy who is in charge of morons." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/ce - -/obj/item/radio/headset/heads/cmo - name = "chief medical officer's headset" - desc = "The headset of the highly trained medical chief." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/cmo - -/obj/item/radio/headset/heads/hop - name = "head of personnel's headset" - desc = "The headset of the guy who will one day be captain." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/hop - -/obj/item/radio/headset/headset_cargo - name = "supply radio headset" - desc = "A headset used by the cargo department." - icon_state = "cargo_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_cargo - -/obj/item/radio/headset/headset_cargo/mining - name = "mining radio headset" - desc = "Headset used by shaft miners." - icon_state = "mine_headset" - -/obj/item/radio/headset/headset_service - name = "service radio headset" - desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean." - icon_state = "srv_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/headset_service - -/obj/item/radio/headset/heads/ntrep - name = "nanotrasen representative's headset" - desc = "The headset of the Nanotrasen Representative." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/ntrep - -/obj/item/radio/headset/heads/magistrate - name = "magistrate's headset" - desc = "The headset of the Magistrate." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/magistrate - -/obj/item/radio/headset/heads/magistrate/alt - name = "\proper magistrate's bowman headset" - desc = "The headset of the Magistrate. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - -/obj/item/radio/headset/heads/blueshield - name = "blueshield's headset" - desc = "The headset of the Blueshield." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/blueshield - -/obj/item/radio/headset/heads/blueshield/alt - name = "\proper blueshield's bowman headset" - desc = "The headset of the Blueshield. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - -/obj/item/radio/headset/ert - name = "emergency response team headset" - desc = "The headset of the boss's boss." - icon_state = "com_headset" - item_state = "headset" - ks2type = /obj/item/encryptionkey/ert - -/obj/item/radio/headset/ert/alt - name = "\proper emergency response team's bowman headset" - desc = "The headset of the boss. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - -/obj/item/radio/headset/centcom - name = "\proper centcom officer's bowman headset" - desc = "The headset of final authority. Protects ears from flashbangs." - flags = EARBANGPROTECT - icon_state = "com_headset_alt" - item_state = "com_headset_alt" - ks2type = /obj/item/encryptionkey/centcom - -/obj/item/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway. - name = "\improper AI subspace transceiver" - desc = "Integrated AI radio transceiver." - icon = 'icons/obj/robot_component.dmi' - icon_state = "radio" - item_state = "headset" - ks2type = /obj/item/encryptionkey/heads/ai_integrated - var/myAi = null // Atlantis: Reference back to the AI which has this radio. - var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu. - -/obj/item/radio/headset/heads/ai_integrated/is_listening() - if(disabledAi) - return FALSE - return ..() - -/obj/item/radio/headset/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/encryptionkey/)) - user.set_machine(src) - if(keyslot1 && keyslot2) - to_chat(user, "The headset can't hold another key!") - return - - if(!keyslot1) - user.drop_item() - W.loc = src - keyslot1 = W - else - user.drop_item() - W.loc = src - keyslot2 = W - recalculateChannels() - else - return ..() - -/obj/item/radio/headset/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - user.set_machine(src) - if(keyslot1 || keyslot2) - - for(var/ch_name in channels) - SSradio.remove_object(src, SSradio.radiochannels[ch_name]) - secure_radio_connections[ch_name] = null - - if(keyslot1) - var/turf/T = get_turf(user) - if(T) - keyslot1.loc = T - keyslot1 = null - if(keyslot2) - var/turf/T = get_turf(user) - if(T) - keyslot2.loc = T - keyslot2 = null - - recalculateChannels() - to_chat(user, "You pop out the encryption keys in the headset!") - I.play_tool_sound(user, I.tool_volume) - else - to_chat(user, "This headset doesn't have any encryption keys! How useless...") - -/obj/item/radio/headset/proc/recalculateChannels(var/setDescription = FALSE) - channels = list() - translate_binary = FALSE - translate_hive = FALSE - syndiekey = null - - if(keyslot1) - for(var/ch_name in keyslot1.channels) - if(ch_name in channels) - continue - channels += ch_name - channels[ch_name] = keyslot1.channels[ch_name] - - if(keyslot1.translate_binary) - translate_binary = TRUE - - if(keyslot1.translate_hive) - translate_hive = TRUE - - if(keyslot1.syndie) - syndiekey = keyslot1 - - if(keyslot2) - for(var/ch_name in keyslot2.channels) - if(ch_name in channels) - continue - channels += ch_name - channels[ch_name] = keyslot2.channels[ch_name] - - if(keyslot2.translate_binary) - translate_binary = TRUE - - if(keyslot2.translate_hive) - translate_hive = TRUE - - if(keyslot2.syndie) - syndiekey = keyslot2 - - - for(var/ch_name in channels) - if(!SSradio) - name = "broken radio headset" - return - - secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) - - if(setDescription) - setupRadioDescription() - - return - -/obj/item/radio/headset/proc/setupRadioDescription() - var/radio_text = "" - for(var/i = 1 to channels.len) - var/channel = channels[i] - var/key = get_radio_key_from_channel(channel) - radio_text += "[key] - [channel]" - if(i != channels.len) - radio_text += ", " - - radio_desc = radio_text - -/obj/item/radio/headset/proc/make_syndie() // Turns normal radios into Syndicate radios! - qdel(keyslot1) - keyslot1 = new /obj/item/encryptionkey/syndicate - syndiekey = keyslot1 - recalculateChannels() \ No newline at end of file +/obj/item/radio/headset + name = "radio headset" + desc = "An updated, modular intercom that fits over the head. Takes encryption keys." + var/radio_desc = "" + icon_state = "headset" + item_state = "headset" + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/ears.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' + ) //We read you loud and skree-er. + materials = list(MAT_METAL=75) + subspace_transmission = TRUE + canhear_range = 0 // can't hear headsets from very far away + + slot_flags = SLOT_EARS + var/translate_binary = FALSE + var/translate_hive = FALSE + var/obj/item/encryptionkey/keyslot1 = null + var/obj/item/encryptionkey/keyslot2 = null + + var/ks1type = null + var/ks2type = null + dog_fashion = null + +/obj/item/radio/headset/New() + ..() + internal_channels.Cut() + +/obj/item/radio/headset/Initialize() + ..() + + if(ks1type) + keyslot1 = new ks1type(src) + if(keyslot1.syndie) + syndiekey = keyslot1 + if(ks2type) + keyslot2 = new ks2type(src) + if(keyslot2.syndie) + syndiekey = keyslot2 + + recalculateChannels(TRUE) + +/obj/item/radio/headset/Destroy() + QDEL_NULL(keyslot1) + QDEL_NULL(keyslot2) + return ..() + +/obj/item/radio/headset/list_channels(var/mob/user) + return list_secure_channels() + +/obj/item/radio/headset/examine(mob/user) + . = ..() + if(in_range(src, user) && radio_desc) + . += "The following channels are available:" + . += radio_desc + +/obj/item/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel) + if(channel == "special") + if(translate_binary) + var/datum/language/binary = GLOB.all_languages["Robot Talk"] + binary.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces))) + return RADIO_CONNECTION_NON_SUBSPACE + if(translate_hive) + var/datum/language/hivemind = GLOB.all_languages["Hivemind"] + hivemind.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces))) + return RADIO_CONNECTION_NON_SUBSPACE + return RADIO_CONNECTION_FAIL + + return ..() + +/obj/item/radio/headset/is_listening() + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.l_ear == src || H.r_ear == src) + return ..() + else if(isanimal(loc) || isAI(loc)) + return ..() + + return FALSE + +/obj/item/radio/headset/alt + name = "bowman headset" + desc = "An updated, modular intercom that fits over the head. Takes encryption keys. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + +/obj/item/radio/headset/syndicate + origin_tech = "syndicate=3" + ks1type = /obj/item/encryptionkey/syndicate/nukeops + +/obj/item/radio/headset/syndicate/alt //undisguised bowman with flash protection + name = "syndicate headset" + desc = "A syndicate headset that can be used to hear all radio frequencies. Protects ears from flashbangs." + flags = EARBANGPROTECT + origin_tech = "syndicate=3" + icon_state = "syndie_headset" + item_state = "syndie_headset" + +/obj/item/radio/headset/syndicate/syndteam + ks1type = /obj/item/encryptionkey/syndteam + +/obj/item/radio/headset/syndicate/alt/syndteam + ks1type = /obj/item/encryptionkey/syndteam + +/obj/item/radio/headset/binary + origin_tech = "syndicate=3" + ks1type = /obj/item/encryptionkey/binary + +/obj/item/radio/headset/headset_sec + name = "security radio headset" + desc = "This is used by your elite security force." + icon_state = "sec_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_sec + +/obj/item/radio/headset/headset_sec/alt + name = "security bowman headset" + desc = "This is used by your elite security force. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "sec_headset_alt" + item_state = "sec_headset_alt" + +/obj/item/radio/headset/headset_eng + name = "engineering radio headset" + desc = "When the engineers wish to chat like girls." + icon_state = "eng_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_eng + +/obj/item/radio/headset/headset_rob + name = "robotics radio headset" + desc = "Made specifically for the roboticists who cannot decide between departments." + icon_state = "rob_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_rob + +/obj/item/radio/headset/headset_med + name = "medical radio headset" + desc = "A headset for the trained staff of the medbay." + icon_state = "med_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_med + +/obj/item/radio/headset/headset_sci + name = "science radio headset" + desc = "A sciency headset. Like usual." + icon_state = "sci_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_sci + +/obj/item/radio/headset/headset_medsci + name = "medical research radio headset" + desc = "A headset that is a result of the mating between medical and science." + icon_state = "medsci_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_medsci + +/obj/item/radio/headset/headset_com + name = "command radio headset" + desc = "A headset with a commanding channel." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_com + +/obj/item/radio/headset/heads/captain + name = "captain's headset" + desc = "The headset of the boss." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/captain + +/obj/item/radio/headset/heads/captain/alt + name = "\proper the captain's bowman headset" + desc = "The headset of the boss. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + +/obj/item/radio/headset/heads/rd + name = "Research Director's headset" + desc = "Headset of the researching God." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/rd + +/obj/item/radio/headset/heads/hos + name = "head of security's headset" + desc = "The headset of the man who protects your worthless lives." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/hos + +/obj/item/radio/headset/heads/hos/alt + name = "\proper the head of security's bowman headset" + desc = "The headset of the man in charge of keeping order and protecting the station. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + +/obj/item/radio/headset/heads/ce + name = "chief engineer's headset" + desc = "The headset of the guy who is in charge of morons." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/ce + +/obj/item/radio/headset/heads/cmo + name = "chief medical officer's headset" + desc = "The headset of the highly trained medical chief." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/cmo + +/obj/item/radio/headset/heads/hop + name = "head of personnel's headset" + desc = "The headset of the guy who will one day be captain." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/hop + +/obj/item/radio/headset/headset_cargo + name = "supply radio headset" + desc = "A headset used by the cargo department." + icon_state = "cargo_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_cargo + +/obj/item/radio/headset/headset_cargo/mining + name = "mining radio headset" + desc = "Headset used by shaft miners." + icon_state = "mine_headset" + +/obj/item/radio/headset/headset_service + name = "service radio headset" + desc = "Headset used by the service staff, tasked with keeping the station full, happy and clean." + icon_state = "srv_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/headset_service + +/obj/item/radio/headset/heads/ntrep + name = "nanotrasen representative's headset" + desc = "The headset of the Nanotrasen Representative." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/ntrep + +/obj/item/radio/headset/heads/magistrate + name = "magistrate's headset" + desc = "The headset of the Magistrate." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/magistrate + +/obj/item/radio/headset/heads/magistrate/alt + name = "\proper magistrate's bowman headset" + desc = "The headset of the Magistrate. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + +/obj/item/radio/headset/heads/blueshield + name = "blueshield's headset" + desc = "The headset of the Blueshield." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/blueshield + +/obj/item/radio/headset/heads/blueshield/alt + name = "\proper blueshield's bowman headset" + desc = "The headset of the Blueshield. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + +/obj/item/radio/headset/ert + name = "emergency response team headset" + desc = "The headset of the boss's boss." + icon_state = "com_headset" + item_state = "headset" + ks2type = /obj/item/encryptionkey/ert + +/obj/item/radio/headset/ert/alt + name = "\proper emergency response team's bowman headset" + desc = "The headset of the boss. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + +/obj/item/radio/headset/centcom + name = "\proper centcom officer's bowman headset" + desc = "The headset of final authority. Protects ears from flashbangs." + flags = EARBANGPROTECT + icon_state = "com_headset_alt" + item_state = "com_headset_alt" + ks2type = /obj/item/encryptionkey/centcom + +/obj/item/radio/headset/heads/ai_integrated //No need to care about icons, it should be hidden inside the AI anyway. + name = "\improper AI subspace transceiver" + desc = "Integrated AI radio transceiver." + icon = 'icons/obj/robot_component.dmi' + icon_state = "radio" + item_state = "headset" + ks2type = /obj/item/encryptionkey/heads/ai_integrated + var/myAi = null // Atlantis: Reference back to the AI which has this radio. + var/disabledAi = 0 // Atlantis: Used to manually disable AI's integrated radio via intellicard menu. + +/obj/item/radio/headset/heads/ai_integrated/is_listening() + if(disabledAi) + return FALSE + return ..() + +/obj/item/radio/headset/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/encryptionkey/)) + user.set_machine(src) + if(keyslot1 && keyslot2) + to_chat(user, "The headset can't hold another key!") + return + + if(!keyslot1) + user.drop_item() + W.loc = src + keyslot1 = W + else + user.drop_item() + W.loc = src + keyslot2 = W + recalculateChannels() + else + return ..() + +/obj/item/radio/headset/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + user.set_machine(src) + if(keyslot1 || keyslot2) + + for(var/ch_name in channels) + SSradio.remove_object(src, SSradio.radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + + if(keyslot1) + var/turf/T = get_turf(user) + if(T) + keyslot1.loc = T + keyslot1 = null + if(keyslot2) + var/turf/T = get_turf(user) + if(T) + keyslot2.loc = T + keyslot2 = null + + recalculateChannels() + to_chat(user, "You pop out the encryption keys in the headset!") + I.play_tool_sound(user, I.tool_volume) + else + to_chat(user, "This headset doesn't have any encryption keys! How useless...") + +/obj/item/radio/headset/proc/recalculateChannels(var/setDescription = FALSE) + channels = list() + translate_binary = FALSE + translate_hive = FALSE + syndiekey = null + + if(keyslot1) + for(var/ch_name in keyslot1.channels) + if(ch_name in channels) + continue + channels += ch_name + channels[ch_name] = keyslot1.channels[ch_name] + + if(keyslot1.translate_binary) + translate_binary = TRUE + + if(keyslot1.translate_hive) + translate_hive = TRUE + + if(keyslot1.syndie) + syndiekey = keyslot1 + + if(keyslot2) + for(var/ch_name in keyslot2.channels) + if(ch_name in channels) + continue + channels += ch_name + channels[ch_name] = keyslot2.channels[ch_name] + + if(keyslot2.translate_binary) + translate_binary = TRUE + + if(keyslot2.translate_hive) + translate_hive = TRUE + + if(keyslot2.syndie) + syndiekey = keyslot2 + + + for(var/ch_name in channels) + if(!SSradio) + name = "broken radio headset" + return + + secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) + + if(setDescription) + setupRadioDescription() + + return + +/obj/item/radio/headset/proc/setupRadioDescription() + var/radio_text = "" + for(var/i = 1 to channels.len) + var/channel = channels[i] + var/key = get_radio_key_from_channel(channel) + radio_text += "[key] - [channel]" + if(i != channels.len) + radio_text += ", " + + radio_desc = radio_text + +/obj/item/radio/headset/proc/make_syndie() // Turns normal radios into Syndicate radios! + qdel(keyslot1) + keyslot1 = new /obj/item/encryptionkey/syndicate + syndiekey = keyslot1 + recalculateChannels() diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm index 6ae33740cb04..13c97f26730f 100644 --- a/code/game/objects/items/devices/radio/intercom.dm +++ b/code/game/objects/items/devices/radio/intercom.dm @@ -1,274 +1,274 @@ -/obj/item/radio/intercom - name = "station intercom (General)" - desc = "Talk through this." - icon_state = "intercom" - anchored = 1 - w_class = WEIGHT_CLASS_BULKY - canhear_range = 2 - flags = CONDUCT - var/number = 0 - var/circuitry_installed = 1 - var/last_tick //used to delay the powercheck - var/buildstage = 0 - dog_fashion = null - -/obj/item/radio/intercom/custom - name = "station intercom (Custom)" - broadcasting = 0 - listening = 0 - -/obj/item/radio/intercom/interrogation - name = "station intercom (Interrogation)" - frequency = AIRLOCK_FREQ - -/obj/item/radio/intercom/private - name = "station intercom (Private)" - frequency = AI_FREQ - -/obj/item/radio/intercom/command - name = "station intercom (Command)" - frequency = COMM_FREQ - -/obj/item/radio/intercom/specops - name = "\improper Special Operations intercom" - frequency = ERT_FREQ - -/obj/item/radio/intercom/department - canhear_range = 5 - broadcasting = 0 - listening = 1 - -/obj/item/radio/intercom/department/medbay - name = "station intercom (Medbay)" - frequency = MED_I_FREQ - -/obj/item/radio/intercom/department/security - name = "station intercom (Security)" - frequency = SEC_I_FREQ - -/obj/item/radio/intercom/New(turf/loc, ndir, building = 3) - ..() - buildstage = building - if(buildstage) - START_PROCESSING(SSobj, src) - else - if(ndir) - pixel_x = (ndir & EAST|WEST) ? (ndir == EAST ? 28 : -28) : 0 - pixel_y = (ndir & NORTH|SOUTH) ? (ndir == NORTH ? 28 : -28) : 0 - dir=ndir - b_stat=1 - on = 0 - GLOB.global_intercoms.Add(src) - update_icon() - -/obj/item/radio/intercom/department/medbay/New() - ..() - internal_channels = default_medbay_channels.Copy() - -/obj/item/radio/intercom/department/security/New() - ..() - internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(SEC_I_FREQ) = list(ACCESS_SECURITY) - ) - -/obj/item/radio/intercom/syndicate - name = "illicit intercom" - desc = "Talk through this. Evilly" - frequency = SYND_FREQ - subspace_transmission = TRUE - syndiekey = new /obj/item/encryptionkey/syndicate/nukeops - -/obj/item/radio/intercom/syndicate/New() - ..() - internal_channels[num2text(SYND_FREQ)] = list(ACCESS_SYNDICATE) - -/obj/item/radio/intercom/pirate - name = "pirate radio intercom" - desc = "You wouldn't steal a space shuttle. Piracy. It's a crime!" - subspace_transmission = 1 - -/obj/item/radio/intercom/pirate/New() - ..() - internal_channels.Cut() - internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(AI_FREQ) = list(), - num2text(COMM_FREQ)= list(), - num2text(ENG_FREQ) = list(), - num2text(MED_FREQ) = list(), - num2text(MED_I_FREQ)=list(), - num2text(SEC_FREQ) = list(), - num2text(SEC_I_FREQ)=list(), - num2text(SCI_FREQ) = list(), - num2text(SUP_FREQ) = list(), - num2text(SRV_FREQ) = list() - ) - -/obj/item/radio/intercom/Destroy() - STOP_PROCESSING(SSobj, src) - GLOB.global_intercoms.Remove(src) - return ..() - -/obj/item/radio/intercom/attack_ai(mob/user as mob) - add_hiddenprint(user) - add_fingerprint(user) - spawn(0) - attack_self(user) - -/obj/item/radio/intercom/attack_hand(mob/user as mob) - add_fingerprint(user) - spawn(0) - attack_self(user) - -/obj/item/radio/intercom/receive_range(freq, level) - if(!is_listening()) - return -1 - if(!(0 in level)) - var/turf/position = get_turf(src) - // TODO: Integrate radio with the space manager - if(isnull(position) || !(position.z in level)) - return -1 - if(freq in SSradio.ANTAG_FREQS) - if(!(syndiekey)) - return -1//Prevents broadcast of messages over devices lacking the encryption - - return canhear_range - -/obj/item/radio/intercom/attackby(obj/item/W as obj, mob/user as mob) - if(istype(W, /obj/item/stack/tape_roll)) //eww - return - else if(iscoil(W) && buildstage == 1) - var/obj/item/stack/cable_coil/coil = W - if(coil.amount < 5) - to_chat(user, "You need more cable for this!") - return - if(do_after(user, 10 * coil.toolspeed, target = src) && buildstage == 1) - coil.use(5) - to_chat(user, "You wire \the [src]!") - buildstage = 2 - return 1 - else if(istype(W,/obj/item/intercom_electronics) && buildstage == 0) - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 10 * W.toolspeed, target = src) && buildstage == 0) - qdel(W) - to_chat(user, "You insert \the [W] into \the [src]!") - buildstage = 1 - return 1 - else - return ..() - -/obj/item/radio/intercom/crowbar_act(mob/user, obj/item/I) - if(buildstage != 1) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - to_chat(user, "You begin removing the electronics...") - if(!I.use_tool(src, user, 10, volume = I.tool_volume) || buildstage != 1) - return - new /obj/item/intercom_electronics(get_turf(src)) - to_chat(user, "The circuit board pops out!") - buildstage = 0 - -/obj/item/radio/intercom/screwdriver_act(mob/user, obj/item/I) - if(buildstage != 2) - return ..() - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(!I.use_tool(src, user, 10, volume = I.tool_volume) || buildstage != 2) - return - update_icon() - on = 1 - b_stat = 0 - buildstage = 3 - to_chat(user, "You secure the electronics!") - update_icon() - START_PROCESSING(SSobj, src) - for(var/i, i<= 5, i++) - wires.UpdateCut(i,1) - -/obj/item/radio/intercom/wirecutter_act(mob/user, obj/item/I) - if(!(buildstage == 3 && b_stat && wires.IsAllCut())) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - WIRECUTTER_SNIP_MESSAGE - new /obj/item/stack/cable_coil(get_turf(src),5) - on = 0 - b_stat = 1 - buildstage = 1 - update_icon() - STOP_PROCESSING(SSobj, src) - -/obj/item/radio/intercom/welder_act(mob/user, obj/item/I) - if(!buildstage) - return - . = TRUE - if(!I.tool_use_check(user, 3)) - return - to_chat(user, "You start slicing [src] from the wall...") - if(I.use_tool(src, user, 10, amount = 3, volume = I.tool_volume)) - to_chat(user, "You cut [src] free from the wall!") - new /obj/item/mounted/frame/intercom(get_turf(src)) - qdel(src) - -/obj/item/radio/intercom/update_icon() - if(!circuitry_installed) - icon_state="intercom-frame" - return - icon_state = "intercom[!on?"-p":""][b_stat ? "-open":""]" - -/obj/item/radio/intercom/process() - if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0)) - last_tick = world.timeofday - - - if(!src.loc) - on = 0 - else - var/area/A = get_area(src) - if(!A) - on = 0 - else - on = A.powered(EQUIP) // set "on" to the power status - update_icon() - -/obj/item/intercom_electronics - name = "intercom electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "Looks like a circuit. Probably is." - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/item/radio/intercom/locked - var/locked_frequency - -/obj/item/radio/intercom/locked/set_frequency(var/frequency) - if(frequency == locked_frequency) - ..(locked_frequency) - -/obj/item/radio/intercom/locked/list_channels() - return "" - -/obj/item/radio/intercom/locked/ai_private - name = "\improper AI intercom" - frequency = AI_FREQ - -/obj/item/radio/intercom/locked/confessional - name = "confessional intercom" - frequency = 1480 - -/obj/item/radio/intercom/locked/prison - name = "\improper prison intercom" - desc = "Talk through this. It looks like it has been modified to not broadcast." - -/obj/item/radio/intercom/locked/prison/New() - ..() - wires.CutWireIndex(RADIO_WIRE_TRANSMIT) +/obj/item/radio/intercom + name = "station intercom (General)" + desc = "Talk through this." + icon_state = "intercom" + anchored = 1 + w_class = WEIGHT_CLASS_BULKY + canhear_range = 2 + flags = CONDUCT + var/number = 0 + var/circuitry_installed = 1 + var/last_tick //used to delay the powercheck + var/buildstage = 0 + dog_fashion = null + +/obj/item/radio/intercom/custom + name = "station intercom (Custom)" + broadcasting = 0 + listening = 0 + +/obj/item/radio/intercom/interrogation + name = "station intercom (Interrogation)" + frequency = AIRLOCK_FREQ + +/obj/item/radio/intercom/private + name = "station intercom (Private)" + frequency = AI_FREQ + +/obj/item/radio/intercom/command + name = "station intercom (Command)" + frequency = COMM_FREQ + +/obj/item/radio/intercom/specops + name = "\improper Special Operations intercom" + frequency = ERT_FREQ + +/obj/item/radio/intercom/department + canhear_range = 5 + broadcasting = 0 + listening = 1 + +/obj/item/radio/intercom/department/medbay + name = "station intercom (Medbay)" + frequency = MED_I_FREQ + +/obj/item/radio/intercom/department/security + name = "station intercom (Security)" + frequency = SEC_I_FREQ + +/obj/item/radio/intercom/New(turf/loc, ndir, building = 3) + ..() + buildstage = building + if(buildstage) + START_PROCESSING(SSobj, src) + else + if(ndir) + pixel_x = (ndir & EAST|WEST) ? (ndir == EAST ? 28 : -28) : 0 + pixel_y = (ndir & NORTH|SOUTH) ? (ndir == NORTH ? 28 : -28) : 0 + dir=ndir + b_stat=1 + on = 0 + GLOB.global_intercoms.Add(src) + update_icon() + +/obj/item/radio/intercom/department/medbay/New() + ..() + internal_channels = GLOB.default_medbay_channels.Copy() + +/obj/item/radio/intercom/department/security/New() + ..() + internal_channels = list( + num2text(PUB_FREQ) = list(), + num2text(SEC_I_FREQ) = list(ACCESS_SECURITY) + ) + +/obj/item/radio/intercom/syndicate + name = "illicit intercom" + desc = "Talk through this. Evilly" + frequency = SYND_FREQ + subspace_transmission = TRUE + syndiekey = new /obj/item/encryptionkey/syndicate/nukeops + +/obj/item/radio/intercom/syndicate/New() + ..() + internal_channels[num2text(SYND_FREQ)] = list(ACCESS_SYNDICATE) + +/obj/item/radio/intercom/pirate + name = "pirate radio intercom" + desc = "You wouldn't steal a space shuttle. Piracy. It's a crime!" + subspace_transmission = 1 + +/obj/item/radio/intercom/pirate/New() + ..() + internal_channels.Cut() + internal_channels = list( + num2text(PUB_FREQ) = list(), + num2text(AI_FREQ) = list(), + num2text(COMM_FREQ)= list(), + num2text(ENG_FREQ) = list(), + num2text(MED_FREQ) = list(), + num2text(MED_I_FREQ)=list(), + num2text(SEC_FREQ) = list(), + num2text(SEC_I_FREQ)=list(), + num2text(SCI_FREQ) = list(), + num2text(SUP_FREQ) = list(), + num2text(SRV_FREQ) = list() + ) + +/obj/item/radio/intercom/Destroy() + STOP_PROCESSING(SSobj, src) + GLOB.global_intercoms.Remove(src) + return ..() + +/obj/item/radio/intercom/attack_ai(mob/user as mob) + add_hiddenprint(user) + add_fingerprint(user) + spawn(0) + attack_self(user) + +/obj/item/radio/intercom/attack_hand(mob/user as mob) + add_fingerprint(user) + spawn(0) + attack_self(user) + +/obj/item/radio/intercom/receive_range(freq, level) + if(!is_listening()) + return -1 + if(!(0 in level)) + var/turf/position = get_turf(src) + // TODO: Integrate radio with the space manager + if(isnull(position) || !(position.z in level)) + return -1 + if(freq in SSradio.ANTAG_FREQS) + if(!(syndiekey)) + return -1//Prevents broadcast of messages over devices lacking the encryption + + return canhear_range + +/obj/item/radio/intercom/attackby(obj/item/W as obj, mob/user as mob) + if(istype(W, /obj/item/stack/tape_roll)) //eww + return + else if(iscoil(W) && buildstage == 1) + var/obj/item/stack/cable_coil/coil = W + if(coil.amount < 5) + to_chat(user, "You need more cable for this!") + return + if(do_after(user, 10 * coil.toolspeed, target = src) && buildstage == 1) + coil.use(5) + to_chat(user, "You wire \the [src]!") + buildstage = 2 + return 1 + else if(istype(W,/obj/item/intercom_electronics) && buildstage == 0) + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 10 * W.toolspeed, target = src) && buildstage == 0) + qdel(W) + to_chat(user, "You insert \the [W] into \the [src]!") + buildstage = 1 + return 1 + else + return ..() + +/obj/item/radio/intercom/crowbar_act(mob/user, obj/item/I) + if(buildstage != 1) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + to_chat(user, "You begin removing the electronics...") + if(!I.use_tool(src, user, 10, volume = I.tool_volume) || buildstage != 1) + return + new /obj/item/intercom_electronics(get_turf(src)) + to_chat(user, "The circuit board pops out!") + buildstage = 0 + +/obj/item/radio/intercom/screwdriver_act(mob/user, obj/item/I) + if(buildstage != 2) + return ..() + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(!I.use_tool(src, user, 10, volume = I.tool_volume) || buildstage != 2) + return + update_icon() + on = 1 + b_stat = 0 + buildstage = 3 + to_chat(user, "You secure the electronics!") + update_icon() + START_PROCESSING(SSobj, src) + for(var/i, i<= 5, i++) + wires.UpdateCut(i,1) + +/obj/item/radio/intercom/wirecutter_act(mob/user, obj/item/I) + if(!(buildstage == 3 && b_stat && wires.IsAllCut())) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + WIRECUTTER_SNIP_MESSAGE + new /obj/item/stack/cable_coil(get_turf(src),5) + on = 0 + b_stat = 1 + buildstage = 1 + update_icon() + STOP_PROCESSING(SSobj, src) + +/obj/item/radio/intercom/welder_act(mob/user, obj/item/I) + if(!buildstage) + return + . = TRUE + if(!I.tool_use_check(user, 3)) + return + to_chat(user, "You start slicing [src] from the wall...") + if(I.use_tool(src, user, 10, amount = 3, volume = I.tool_volume)) + to_chat(user, "You cut [src] free from the wall!") + new /obj/item/mounted/frame/intercom(get_turf(src)) + qdel(src) + +/obj/item/radio/intercom/update_icon() + if(!circuitry_installed) + icon_state="intercom-frame" + return + icon_state = "intercom[!on?"-p":""][b_stat ? "-open":""]" + +/obj/item/radio/intercom/process() + if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0)) + last_tick = world.timeofday + + + if(!src.loc) + on = 0 + else + var/area/A = get_area(src) + if(!A) + on = 0 + else + on = A.powered(EQUIP) // set "on" to the power status + update_icon() + +/obj/item/intercom_electronics + name = "intercom electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "Looks like a circuit. Probably is." + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/item/radio/intercom/locked + var/locked_frequency + +/obj/item/radio/intercom/locked/set_frequency(var/frequency) + if(frequency == locked_frequency) + ..(locked_frequency) + +/obj/item/radio/intercom/locked/list_channels() + return "" + +/obj/item/radio/intercom/locked/ai_private + name = "\improper AI intercom" + frequency = AI_FREQ + +/obj/item/radio/intercom/locked/confessional + name = "confessional intercom" + frequency = 1480 + +/obj/item/radio/intercom/locked/prison + name = "\improper prison intercom" + desc = "Talk through this. It looks like it has been modified to not broadcast." + +/obj/item/radio/intercom/locked/prison/New() + ..() + wires.CutWireIndex(RADIO_WIRE_TRANSMIT) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index c1a19457c147..900b3449b279 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -1,866 +1,866 @@ -var/global/list/default_internal_channels = list( - num2text(PUB_FREQ) = list(), - num2text(AI_FREQ) = list(ACCESS_CAPTAIN), - num2text(ERT_FREQ) = list(ACCESS_CENT_SPECOPS), - num2text(COMM_FREQ)= list(ACCESS_HEADS), - num2text(ENG_FREQ) = list(ACCESS_ENGINE, ACCESS_ATMOSPHERICS), - num2text(MED_FREQ) = list(ACCESS_MEDICAL), - num2text(MED_I_FREQ)=list(ACCESS_MEDICAL), - num2text(SEC_FREQ) = list(ACCESS_SECURITY), - num2text(SEC_I_FREQ)=list(ACCESS_SECURITY), - num2text(SCI_FREQ) = list(ACCESS_RESEARCH), - num2text(SUP_FREQ) = list(ACCESS_CARGO), - num2text(SRV_FREQ) = list(ACCESS_HOP, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CLOWN, ACCESS_MIME) -) - -var/global/list/default_medbay_channels = list( - num2text(PUB_FREQ) = list(), - num2text(MED_FREQ) = list(ACCESS_MEDICAL), - num2text(MED_I_FREQ) = list(ACCESS_MEDICAL) -) - -/obj/item/radio - icon = 'icons/obj/radio.dmi' - name = "station bounced radio" - dog_fashion = /datum/dog_fashion/back - suffix = "\[3\]" - icon_state = "walkietalkie" - item_state = "walkietalkie" - var/on = 1 // 0 for off - var/last_transmission - var/frequency = PUB_FREQ //common chat - var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies - var/canhear_range = 3 // the range which mobs can hear this radio from - var/datum/wires/radio/wires = null - var/b_stat = 0 - var/broadcasting = 0 - var/listening = 1 - var/list/channels = list() //see communications.dm for full list. First channes is a "default" for :h - var/subspace_transmission = 0 - var/obj/item/encryptionkey/syndicate/syndiekey = null //Holder for the syndicate encryption key if present - var/disable_timer = 0 //How many times this is disabled by EMPs - - var/is_special = 0 //For electropacks mostly, skips Topic() checks - - flags = CONDUCT - slot_flags = SLOT_BELT - throw_speed = 2 - throw_range = 9 - w_class = WEIGHT_CLASS_SMALL - - materials = list(MAT_METAL=75) - - var/const/FREQ_LISTENING = 1 - var/atom/follow_target // Custom follow target for autosay-using bots - - var/list/internal_channels - - var/datum/radio_frequency/radio_connection - var/list/datum/radio_frequency/secure_radio_connections = new - - -/obj/item/radio/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT) - - -/obj/item/radio/New() - ..() - wires = new(src) - - internal_channels = default_internal_channels.Copy() - GLOB.global_radios |= src - -/obj/item/radio/Destroy() - QDEL_NULL(wires) - if(SSradio) - SSradio.remove_object(src, frequency) - for(var/ch_name in channels) - SSradio.remove_object(src, SSradio.radiochannels[ch_name]) - radio_connection = null - GLOB.global_radios -= src - follow_target = null - return ..() - - -/obj/item/radio/Initialize() - ..() - if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ) - frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(frequency) - - for(var/ch_name in channels) - secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) - -/obj/item/radio/attack_ghost(mob/user) - return interact(user) - -/obj/item/radio/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) - -/obj/item/radio/interact(mob/user) - if(!user) - return 0 - - if(b_stat) - wires.Interact(user) - - return ui_interact(user) - -/obj/item/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 400, 550) - ui.open() - ui.set_auto_update(1) - -/obj/item/radio/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["mic_status"] = broadcasting - data["speaker"] = listening - data["freq"] = format_frequency(frequency) - data["rawfreq"] = num2text(frequency) - - data["mic_cut"] = (wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - data["spk_cut"] = (wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - data["chan_list_len"] = chanlist.len - - if(syndiekey) - data["useSyndMode"] = 1 - - return data - - -/obj/item/radio/proc/list_channels(var/mob/user) - return list_internal_channels(user) - -/obj/item/radio/proc/list_secure_channels(var/mob/user) - var/dat[0] - - for(var/ch_name in channels) - var/chan_stat = channels[ch_name] - var/listening = !!(chan_stat & FREQ_LISTENING) != 0 - - dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "chan_span" = SSradio.frequency_span_class(SSradio.radiochannels[ch_name])))) - - return dat - -/obj/item/radio/proc/list_internal_channels(var/mob/user) - var/dat[0] - for(var/internal_chan in internal_channels) - if(has_channel_access(user, internal_chan)) - dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "chan_span" = SSradio.frequency_span_class(text2num(internal_chan))))) - - return dat - -/obj/item/radio/proc/has_channel_access(var/mob/user, var/freq) - if(!user) - return 0 - - if(!(freq in internal_channels)) - return 0 - - return user.has_internal_radio_channel_access(user, internal_channels[freq]) - -/mob/proc/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses) - var/obj/item/card/id/I = user.get_id_card() - return has_access(list(), req_one_accesses, I ? I.GetAccess() : list()) - -/mob/living/silicon/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses) - var/list/access = get_all_accesses() - return has_access(list(), req_one_accesses, access) - -/mob/dead/observer/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses) - return can_admin_interact() - -/obj/item/radio/proc/ToggleBroadcast() - broadcasting = !broadcasting && !(wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - -/obj/item/radio/proc/ToggleReception() - listening = !listening && !(wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) - -/obj/item/radio/Topic(href, href_list) - if(..()) - return 1 - - if(is_special) - return 0 - - if(href_list["track"]) - var/mob/target = locate(href_list["track"]) - var/mob/living/silicon/ai/A = locate(href_list["track2"]) - if(A && target) - A.ai_actual_track(target) - . = 1 - - else if(href_list["freq"]) - var/new_frequency = (frequency + text2num(href_list["freq"])) - if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) - new_frequency = sanitize_frequency(new_frequency) - set_frequency(new_frequency) - if(hidden_uplink) - if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) - usr << browse(null, "window=radio") - . = 1 - else if(href_list["talk"]) - ToggleBroadcast() - . = 1 - else if(href_list["listen"]) - var/chan_name = href_list["ch_name"] - if(!chan_name) - ToggleReception() - else - if(channels[chan_name] & FREQ_LISTENING) - channels[chan_name] &= ~FREQ_LISTENING - else - channels[chan_name] |= FREQ_LISTENING - . = 1 - else if(href_list["spec_freq"]) - var freq = href_list["spec_freq"] - if(has_channel_access(usr, freq)) - set_frequency(text2num(freq)) - . = 1 - - if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk - return 1 - - add_fingerprint(usr) - -/obj/item/radio/proc/autosay(var/message, var/from, var/channel, var/zlevel = config.contact_levels, var/role = "Unknown") //BS12 EDIT - var/datum/radio_frequency/connection = null - if(channel && channels && channels.len > 0) - if(channel == "department") - channel = channels[1] - connection = secure_radio_connections[channel] - else - connection = radio_connection - channel = null - if(!istype(connection)) - return - if(!connection) - return - var/mob/living/automatedannouncer/A = new /mob/living/automatedannouncer(src) - A.name = from - A.role = role - A.message = message - var/jammed = FALSE - for(var/obj/item/jammer/jammer in GLOB.active_jammers) - if(get_dist(get_turf(src), get_turf(jammer)) < jammer.range) - jammed = TRUE - break - if(jammed) - message = Gibberish(message, 100) - var/list/message_pieces = message_to_multilingual(message) - Broadcast_Message(connection, A, - 0, "*garbled automated announcement*", src, - message_pieces, from, "Automated Announcement", from, "synthesized voice", - 4, 0, zlevel, connection.frequency, follow_target = follow_target) - qdel(A) - -// Just a dummy mob used for making announcements, so we don't create AIs to do this -// I'm not sure who thought that was a good idea. -- Crazylemon -/mob/living/automatedannouncer - var/role = "" - var/lifetime_timer - var/message = "" - universal_speak = 1 - -/mob/living/automatedannouncer/New() - lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), SecondsToTicks(10), TIMER_STOPPABLE) - ..() - -/mob/living/automatedannouncer/Destroy() - if(lifetime_timer) - deltimer(lifetime_timer) - lifetime_timer = null - return ..() - -/mob/living/automatedannouncer/proc/autocleanup() - log_runtime(EXCEPTION("An announcer somehow managed to outlive the radio! Deleting!"), src, list("Message: '[message]'")) - qdel(src) - -// Interprets the message mode when talking into a radio, possibly returning a connection datum -/obj/item/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) - // If a channel isn't specified, send to common. - if(!message_mode || message_mode == "headset") - return radio_connection - - // Otherwise, if a channel is specified, look for it. - if(channels && channels.len > 0) - if(message_mode == "department") // Department radio shortcut - message_mode = channels[1] - - if(channels[message_mode]) // only broadcast if the channel is set on - return secure_radio_connections[message_mode] - - // If we were to send to a channel we don't have, drop it. - return RADIO_CONNECTION_FAIL - -/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") - if(!on) - return 0 // the device has to be on - // Fix for permacell radios, but kinda eh about actually fixing them. - if(!M || !message_pieces) - return 0 - - // Uncommenting this. To the above comment: - // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom - if(wires.IsIndexCut(RADIO_WIRE_TRANSMIT)) // The device has to have all its wires and shit intact - return 0 - - if(!M.IsVocal()) - return 0 - - /* Quick introduction: - This new radio system uses a very robust FTL signaling technology unoriginally - dubbed "subspace" which is somewhat similar to 'blue-space' but can't - actually transmit large mass. Headsets are the only radio devices capable - of sending subspace transmissions to the Communications Satellite. - - A headset sends a signal to a subspace listener/receiver elsewhere in space, - the signal gets processed and logged, and an audible transmission gets sent - to each individual headset. - */ - - //#### Grab the connection datum ####// - var/message_mode = handle_message_mode(M, message_pieces, channel) - switch(message_mode) //special cases - if(RADIO_CONNECTION_FAIL) - return 0 - if(RADIO_CONNECTION_NON_SUBSPACE) - return 1 - - if(!istype(message_mode, /datum/radio_frequency)) //if not a special case, it should be returning a radio connection - return 0 - - var/datum/radio_frequency/connection = message_mode - var/turf/position = get_turf(src) - - var/jammed = FALSE - for(var/obj/item/jammer/jammer in GLOB.active_jammers) - if(get_dist(position, get_turf(jammer)) < jammer.range) - jammed = TRUE - break - - //#### Tagging the signal with all appropriate identity values ####// - - // ||-- The mob's name identity --|| - var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) - var/real_name = M.real_name // mob's real name - var/mobkey = "none" // player key associated with mob - var/voicemask = 0 // the speaker is wearing a voice mask - if(M.client) - mobkey = M.key // assign the mob's key - - - var/jobname // the mob's "job" - - if(jammed) - Gibberish_all(message_pieces, 100) - - // --- Human: use their actual job --- - if(ishuman(M)) - var/mob/living/carbon/human/H = M - jobname = H.get_assignment() - - // --- Carbon Nonhuman --- - else if(iscarbon(M)) // Nonhuman carbon mob - jobname = "No id" - - // --- AI --- - else if(isAI(M)) - jobname = "AI" - - // --- Cyborg --- - else if(isrobot(M)) - jobname = "Cyborg" - - // --- Personal AI (pAI) --- - else if(istype(M, /mob/living/silicon/pai)) - jobname = "Personal AI" - - // --- Unidentifiable mob --- - else - jobname = "Unknown" - - - // --- Modifications to the mob's identity --- - - // The mob is disguising their identity: - if(ishuman(M)) - var/mob/living/carbon/human/H = M - displayname = H.voice - if(H.voice != real_name) - voicemask = TRUE - - if(syndiekey && syndiekey.change_voice && connection.frequency == SYND_FREQ) - displayname = syndiekey.fake_name - jobname = "Unknown" - voicemask = TRUE - - - /* ###### Radio headsets can only broadcast through subspace ###### */ - - if(subspace_transmission) - // First, we want to generate a new radio signal - var/datum/signal/signal = new - signal.transmission_method = 2 // 2 would be a subspace transmission. - // transmission_method could probably be enumerated through #define. Would be neater. - - // --- Finally, tag the actual signal with the appropriate values --- - signal.data = list( - // Identity-associated tags: - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "race" = signal.get_race(M), - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mask - - // We store things that would otherwise be kept in the actual mob - // so that they can be logged even AFTER the mob is deleted or something - - // Other tags: - "compression" = rand(45,50), // compressed radio signal - "message" = message_pieces, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, // how much to sleep() before broadcasting - simulates net lag - "traffic" = 0, // dictates the total traffic sum that the signal went through - "type" = 0, // determines what type of radio input it is: normal broadcast - "server" = null, // the last server to log this signal - "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery - "level" = position.z, // The source's z level - "verb" = verb - ) - signal.frequency = connection.frequency // Quick frequency set - - //#### Sending the signal to all subspace receivers ####// - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - spawn(0) - R.receive_signal(signal) - - // Allinone can act as receivers. - for(var/obj/machinery/telecomms/allinone/R in telecomms_list) - spawn(0) - R.receive_signal(signal) - - // Receiving code can be located in Telecommunications.dm - return signal.data["done"] && position.z in signal.data["level"] - - - /* ###### Intercoms and station-bounced radios ###### */ - - var/filter_type = 2 - - /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ - if(istype(src, /obj/item/radio/intercom)) - filter_type = 1 - - - var/datum/signal/signal = new - signal.transmission_method = 2 - - - /* --- Try to send a normal subspace broadcast first */ - - signal.data = list( - - "mob" = M, // store a reference to the mob - "mobtype" = M.type, // the mob's type - "race" = signal.get_race(M), // text to show next to mob in comms log console - "realname" = real_name, // the mob's real name - "name" = displayname, // the mob's display name - "job" = jobname, // the mob's job - "key" = mobkey, // the mob's key - "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood - "vname" = M.voice_name, // the name to display if the voice wasn't understood - "vmask" = voicemask, // 1 if the mob is using a voice gas mas - - "compression" = 0, // uncompressed radio signal - "message" = message_pieces, // the actual sent message - "connection" = connection, // the radio connection to use - "radio" = src, // stores the radio used for transmission - "slow" = 0, - "traffic" = 0, - "type" = 0, - "server" = null, - "reject" = 0, - "level" = position.z, - "verb" = verb - ) - signal.frequency = connection.frequency // Quick frequency set - - for(var/obj/machinery/telecomms/receiver/R in telecomms_list) - spawn(0) - R.receive_signal(signal) - - - sleep(rand(10,25)) // wait a little... - - if(signal.data["done"] && position.z in signal.data["level"]) - // we're done here. - return 1 - - // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. - // Send a mundane broadcast with limited targets: - - //THIS IS TEMPORARY. YEAH RIGHT - if(!connection) return 0 //~Carn - - return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), - src, message_pieces, displayname, jobname, real_name, M.voice_name, - filter_type, signal.data["compression"], list(position.z), connection.frequency,verb) - - -/obj/item/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") - if(broadcasting) - if(get_dist(src, M) <= canhear_range) - talk_into(M, message_pieces, null, verb) - - -/* -/obj/item/radio/proc/accept_rad(obj/item/radio/R as obj, message) - - if((R.frequency == frequency && message)) - return 1 - else if - - else - return null - return -*/ - - -/obj/item/radio/proc/receive_range(freq, level) - // check if this radio can receive on the given frequency, and if so, - // what the range is in which mobs will hear the radio - // returns: -1 if can't receive, range otherwise - - if(!is_listening()) - return -1 - if(!(0 in level)) - var/turf/position = get_turf(src) - if(!position || !(position.z in level)) - return -1 - if(freq in SSradio.ANTAG_FREQS) - if(!(syndiekey))//Checks to see if it's allowed on that frequency, based on the encryption keys - return -1 - if(!freq) //recieved on main frequency - if(!listening) - return -1 - else - var/accept = (freq==frequency && listening) - if(!accept) - for(var/ch_name in channels) - var/datum/radio_frequency/RF = secure_radio_connections[ch_name] - if(RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) - accept = 1 - break - if(!accept) - return -1 - return canhear_range - -/obj/item/radio/proc/send_hear(freq, level) - var/range = receive_range(freq, level) - if(range > -1) - return get_mobs_in_view(canhear_range, src) - -/obj/item/radio/proc/is_listening() - var/is_listening = TRUE - if(!on) - is_listening = FALSE - if(!wires || wires.IsIndexCut(RADIO_WIRE_RECEIVE)) - is_listening = FALSE - if(!listening) - is_listening = FALSE - - return is_listening - -/obj/item/radio/proc/send_announcement() - if(is_listening()) - return get_mobs_in_view(canhear_range, src) - - return null - -/obj/item/radio/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(b_stat) - . += "\the [src] can be attached and modified!" - else - . += "\the [src] can not be modified or attached!" - -/obj/item/radio/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - user.set_machine(src) - b_stat = !b_stat - if(!istype(src, /obj/item/radio/beacon)) - if(b_stat) - user.show_message("The radio can now be attached and modified!") - else - user.show_message("The radio can no longer be modified or attached!") - updateDialog() - -/obj/item/radio/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - interact(user) - -/obj/item/radio/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - interact(user) - -/obj/item/radio/emp_act(severity) - on = 0 - disable_timer++ - addtimer(CALLBACK(src, .proc/enable_radio), rand(100, 200)) - - if(listening) - visible_message("[src] buzzes violently!") - - broadcasting = 0 - listening = 0 - for(var/ch_name in channels) - channels[ch_name] = 0 - ..() - -/obj/item/radio/proc/enable_radio() - if(disable_timer > 0) - disable_timer-- - if(!disable_timer) - on = 1 - -/////////////////////////////// -//////////Borg Radios////////// -/////////////////////////////// -//Giving borgs their own radio to have some more room to work with -Sieve - -/obj/item/radio/borg - var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks - var/obj/item/encryptionkey/keyslot = null//Borg radios can handle a single encryption key - var/shut_up = 1 - icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. - icon_state = "radio" - canhear_range = 0 - subspace_transmission = 1 - dog_fashion = null - -/obj/item/radio/borg/syndicate - keyslot = new /obj/item/encryptionkey/syndicate/nukeops - -/obj/item/radio/borg/syndicate/CanUseTopic(mob/user, datum/topic_state/state) - . = ..() - if(. == STATUS_UPDATE && istype(user, /mob/living/silicon/robot/syndicate)) - . = STATUS_INTERACTIVE - -/obj/item/radio/borg/Destroy() - myborg = null - return ..() - -/obj/item/radio/borg/list_channels(var/mob/user) - return list_secure_channels(user) - -/obj/item/radio/borg/syndicate/New() - ..() - syndiekey = keyslot - set_frequency(SYND_FREQ) - -/obj/item/radio/borg/deathsquad - -/obj/item/radio/borg/deathsquad/New() - ..() - set_frequency(DTH_FREQ) - -/obj/item/radio/borg/ert - keyslot = new /obj/item/encryptionkey/ert - -/obj/item/radio/borg/ert/New() - ..() - set_frequency(ERT_FREQ) - -/obj/item/radio/borg/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/encryptionkey/)) - user.set_machine(src) - if(keyslot) - to_chat(user, "The radio can't hold another key!") - return - - if(!keyslot) - user.drop_item() - W.loc = src - keyslot = W - - recalculateChannels() - else - return ..() - -/obj/item/radio/borg/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - user.set_machine(src) - if(keyslot) - for(var/ch_name in channels) - SSradio.remove_object(src, SSradio.radiochannels[ch_name]) - secure_radio_connections[ch_name] = null - - - if(keyslot) - var/turf/T = get_turf(user) - if(T) - keyslot.loc = T - keyslot = null - - recalculateChannels() - to_chat(user, "You pop out the encryption key in the radio!") - I.play_tool_sound(user, I.tool_volume) - - else - to_chat(user, "This radio doesn't have any encryption keys!") - -/obj/item/radio/borg/proc/recalculateChannels() - channels = list() - syndiekey = null - - var/mob/living/silicon/robot/D = loc - if(D.module) - for(var/ch_name in D.module.channels) - if(ch_name in channels) - continue - channels += ch_name - channels[ch_name] += D.module.channels[ch_name] - if(keyslot) - for(var/ch_name in keyslot.channels) - if(ch_name in channels) - continue - channels += ch_name - channels[ch_name] += keyslot.channels[ch_name] - - if(keyslot.syndie) - syndiekey = keyslot - - - for(var/ch_name in channels) - if(!SSradio) - sleep(30) // Waiting for SSradio to be created. - if(!SSradio) - name = "broken radio" - return - - secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) - - return - -/obj/item/radio/borg/Topic(href, href_list) - if(..()) - return 1 - if(href_list["mode"]) - var/enable_subspace_transmission = text2num(href_list["mode"]) - if(enable_subspace_transmission != subspace_transmission) - subspace_transmission = !subspace_transmission - if(subspace_transmission) - to_chat(usr, "Subspace Transmission is enabled.") - else - to_chat(usr, "Subspace Transmission is disabled.") - - if(subspace_transmission == 0)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled - channels = list() - else - recalculateChannels() - . = 1 - if(href_list["shutup"]) // Toggle loudspeaker mode, AKA everyone around you hearing your radio. - var/do_shut_up = text2num(href_list["shutup"]) - if(do_shut_up != shut_up) - shut_up = !shut_up - if(shut_up) - canhear_range = 0 - to_chat(usr, "Loudspeaker disabled.") - else - canhear_range = 3 - to_chat(usr, "Loudspeaker enabled.") - . = 1 - - -/obj/item/radio/borg/interact(mob/user as mob) - if(!on) - return - - . = ..() - -/obj/item/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 430, 500) - ui.open() - ui.set_auto_update(1) - -/obj/item/radio/borg/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["mic_status"] = broadcasting - data["speaker"] = listening - data["freq"] = format_frequency(frequency) - data["rawfreq"] = num2text(frequency) - - var/list/chanlist = list_channels(user) - if(islist(chanlist) && chanlist.len) - data["chan_list"] = chanlist - data["chan_list_len"] = chanlist.len - - if(syndiekey) - data["useSyndMode"] = 1 - - data["has_loudspeaker"] = 1 - data["loudspeaker"] = !shut_up - data["has_subspace"] = 1 - data["subspace"] = subspace_transmission - - return data - -/obj/item/radio/proc/config(op) - if(SSradio) - for(var/ch_name in channels) - SSradio.remove_object(src, SSradio.radiochannels[ch_name]) - secure_radio_connections = new - channels = op - if(SSradio) - for(var/ch_name in op) - secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) - return - -/obj/item/radio/off - listening = 0 - dog_fashion = /datum/dog_fashion/back - -/obj/item/radio/phone - broadcasting = 0 - icon = 'icons/obj/items.dmi' - icon_state = "red_phone" - listening = 1 - name = "phone" - dog_fashion = null - -/obj/item/radio/phone/medbay - frequency = MED_I_FREQ - -/obj/item/radio/phone/medbay/New() - ..() - internal_channels = default_medbay_channels.Copy() +GLOBAL_LIST_INIT(default_internal_channels, list( + num2text(PUB_FREQ) = list(), + num2text(AI_FREQ) = list(ACCESS_CAPTAIN), + num2text(ERT_FREQ) = list(ACCESS_CENT_SPECOPS), + num2text(COMM_FREQ)= list(ACCESS_HEADS), + num2text(ENG_FREQ) = list(ACCESS_ENGINE, ACCESS_ATMOSPHERICS), + num2text(MED_FREQ) = list(ACCESS_MEDICAL), + num2text(MED_I_FREQ)=list(ACCESS_MEDICAL), + num2text(SEC_FREQ) = list(ACCESS_SECURITY), + num2text(SEC_I_FREQ)=list(ACCESS_SECURITY), + num2text(SCI_FREQ) = list(ACCESS_RESEARCH), + num2text(SUP_FREQ) = list(ACCESS_CARGO), + num2text(SRV_FREQ) = list(ACCESS_HOP, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CLOWN, ACCESS_MIME) +)) + +GLOBAL_LIST_INIT(default_medbay_channels, list( + num2text(PUB_FREQ) = list(), + num2text(MED_FREQ) = list(ACCESS_MEDICAL), + num2text(MED_I_FREQ) = list(ACCESS_MEDICAL) +)) + +/obj/item/radio + icon = 'icons/obj/radio.dmi' + name = "station bounced radio" + dog_fashion = /datum/dog_fashion/back + suffix = "\[3\]" + icon_state = "walkietalkie" + item_state = "walkietalkie" + var/on = 1 // 0 for off + var/last_transmission + var/frequency = PUB_FREQ //common chat + var/traitor_frequency = 0 //tune to frequency to unlock traitor supplies + var/canhear_range = 3 // the range which mobs can hear this radio from + var/datum/wires/radio/wires = null + var/b_stat = 0 + var/broadcasting = 0 + var/listening = 1 + var/list/channels = list() //see communications.dm for full list. First channes is a "default" for :h + var/subspace_transmission = 0 + var/obj/item/encryptionkey/syndicate/syndiekey = null //Holder for the syndicate encryption key if present + var/disable_timer = 0 //How many times this is disabled by EMPs + + var/is_special = 0 //For electropacks mostly, skips Topic() checks + + flags = CONDUCT + slot_flags = SLOT_BELT + throw_speed = 2 + throw_range = 9 + w_class = WEIGHT_CLASS_SMALL + + materials = list(MAT_METAL=75) + + var/const/FREQ_LISTENING = 1 + var/atom/follow_target // Custom follow target for autosay-using bots + + var/list/internal_channels + + var/datum/radio_frequency/radio_connection + var/list/datum/radio_frequency/secure_radio_connections = new + + +/obj/item/radio/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT) + + +/obj/item/radio/New() + ..() + wires = new(src) + + internal_channels = GLOB.default_internal_channels.Copy() + GLOB.global_radios |= src + +/obj/item/radio/Destroy() + QDEL_NULL(wires) + if(SSradio) + SSradio.remove_object(src, frequency) + for(var/ch_name in channels) + SSradio.remove_object(src, SSradio.radiochannels[ch_name]) + radio_connection = null + GLOB.global_radios -= src + follow_target = null + return ..() + + +/obj/item/radio/Initialize() + ..() + if(frequency < RADIO_LOW_FREQ || frequency > RADIO_HIGH_FREQ) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(frequency) + + for(var/ch_name in channels) + secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) + +/obj/item/radio/attack_ghost(mob/user) + return interact(user) + +/obj/item/radio/attack_self(mob/user as mob) + user.set_machine(src) + interact(user) + +/obj/item/radio/interact(mob/user) + if(!user) + return 0 + + if(b_stat) + wires.Interact(user) + + return ui_interact(user) + +/obj/item/radio/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 400, 550) + ui.open() + ui.set_auto_update(1) + +/obj/item/radio/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["mic_status"] = broadcasting + data["speaker"] = listening + data["freq"] = format_frequency(frequency) + data["rawfreq"] = num2text(frequency) + + data["mic_cut"] = (wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) + data["spk_cut"] = (wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) + + var/list/chanlist = list_channels(user) + if(islist(chanlist) && chanlist.len) + data["chan_list"] = chanlist + data["chan_list_len"] = chanlist.len + + if(syndiekey) + data["useSyndMode"] = 1 + + return data + + +/obj/item/radio/proc/list_channels(var/mob/user) + return list_internal_channels(user) + +/obj/item/radio/proc/list_secure_channels(var/mob/user) + var/dat[0] + + for(var/ch_name in channels) + var/chan_stat = channels[ch_name] + var/listening = !!(chan_stat & FREQ_LISTENING) != 0 + + dat.Add(list(list("chan" = ch_name, "display_name" = ch_name, "secure_channel" = 1, "sec_channel_listen" = !listening, "chan_span" = SSradio.frequency_span_class(SSradio.radiochannels[ch_name])))) + + return dat + +/obj/item/radio/proc/list_internal_channels(var/mob/user) + var/dat[0] + for(var/internal_chan in internal_channels) + if(has_channel_access(user, internal_chan)) + dat.Add(list(list("chan" = internal_chan, "display_name" = get_frequency_name(text2num(internal_chan)), "chan_span" = SSradio.frequency_span_class(text2num(internal_chan))))) + + return dat + +/obj/item/radio/proc/has_channel_access(var/mob/user, var/freq) + if(!user) + return 0 + + if(!(freq in internal_channels)) + return 0 + + return user.has_internal_radio_channel_access(user, internal_channels[freq]) + +/mob/proc/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses) + var/obj/item/card/id/I = user.get_id_card() + return has_access(list(), req_one_accesses, I ? I.GetAccess() : list()) + +/mob/living/silicon/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses) + var/list/access = get_all_accesses() + return has_access(list(), req_one_accesses, access) + +/mob/dead/observer/has_internal_radio_channel_access(var/mob/user, var/list/req_one_accesses) + return can_admin_interact() + +/obj/item/radio/proc/ToggleBroadcast() + broadcasting = !broadcasting && !(wires.IsIndexCut(RADIO_WIRE_TRANSMIT) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) + +/obj/item/radio/proc/ToggleReception() + listening = !listening && !(wires.IsIndexCut(RADIO_WIRE_RECEIVE) || wires.IsIndexCut(RADIO_WIRE_SIGNAL)) + +/obj/item/radio/Topic(href, href_list) + if(..()) + return 1 + + if(is_special) + return 0 + + if(href_list["track"]) + var/mob/target = locate(href_list["track"]) + var/mob/living/silicon/ai/A = locate(href_list["track2"]) + if(A && target) + A.ai_actual_track(target) + . = 1 + + else if(href_list["freq"]) + var/new_frequency = (frequency + text2num(href_list["freq"])) + if((new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ)) + new_frequency = sanitize_frequency(new_frequency) + set_frequency(new_frequency) + if(hidden_uplink) + if(hidden_uplink.check_trigger(usr, frequency, traitor_frequency)) + usr << browse(null, "window=radio") + . = 1 + else if(href_list["talk"]) + ToggleBroadcast() + . = 1 + else if(href_list["listen"]) + var/chan_name = href_list["ch_name"] + if(!chan_name) + ToggleReception() + else + if(channels[chan_name] & FREQ_LISTENING) + channels[chan_name] &= ~FREQ_LISTENING + else + channels[chan_name] |= FREQ_LISTENING + . = 1 + else if(href_list["spec_freq"]) + var freq = href_list["spec_freq"] + if(has_channel_access(usr, freq)) + set_frequency(text2num(freq)) + . = 1 + + if(href_list["nowindow"]) // here for pAIs, maybe others will want it, idk + return 1 + + add_fingerprint(usr) + +/obj/item/radio/proc/autosay(var/message, var/from, var/channel, var/zlevel = config.contact_levels, var/role = "Unknown") //BS12 EDIT + var/datum/radio_frequency/connection = null + if(channel && channels && channels.len > 0) + if(channel == "department") + channel = channels[1] + connection = secure_radio_connections[channel] + else + connection = radio_connection + channel = null + if(!istype(connection)) + return + if(!connection) + return + var/mob/living/automatedannouncer/A = new /mob/living/automatedannouncer(src) + A.name = from + A.role = role + A.message = message + var/jammed = FALSE + for(var/obj/item/jammer/jammer in GLOB.active_jammers) + if(get_dist(get_turf(src), get_turf(jammer)) < jammer.range) + jammed = TRUE + break + if(jammed) + message = Gibberish(message, 100) + var/list/message_pieces = message_to_multilingual(message) + Broadcast_Message(connection, A, + 0, "*garbled automated announcement*", src, + message_pieces, from, "Automated Announcement", from, "synthesized voice", + 4, 0, zlevel, connection.frequency, follow_target = follow_target) + qdel(A) + +// Just a dummy mob used for making announcements, so we don't create AIs to do this +// I'm not sure who thought that was a good idea. -- Crazylemon +/mob/living/automatedannouncer + var/role = "" + var/lifetime_timer + var/message = "" + universal_speak = 1 + +/mob/living/automatedannouncer/New() + lifetime_timer = addtimer(CALLBACK(src, .proc/autocleanup), SecondsToTicks(10), TIMER_STOPPABLE) + ..() + +/mob/living/automatedannouncer/Destroy() + if(lifetime_timer) + deltimer(lifetime_timer) + lifetime_timer = null + return ..() + +/mob/living/automatedannouncer/proc/autocleanup() + log_runtime(EXCEPTION("An announcer somehow managed to outlive the radio! Deleting!"), src, list("Message: '[message]'")) + qdel(src) + +// Interprets the message mode when talking into a radio, possibly returning a connection datum +/obj/item/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode) + // If a channel isn't specified, send to common. + if(!message_mode || message_mode == "headset") + return radio_connection + + // Otherwise, if a channel is specified, look for it. + if(channels && channels.len > 0) + if(message_mode == "department") // Department radio shortcut + message_mode = channels[1] + + if(channels[message_mode]) // only broadcast if the channel is set on + return secure_radio_connections[message_mode] + + // If we were to send to a channel we don't have, drop it. + return RADIO_CONNECTION_FAIL + +/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says") + if(!on) + return 0 // the device has to be on + // Fix for permacell radios, but kinda eh about actually fixing them. + if(!M || !message_pieces) + return 0 + + // Uncommenting this. To the above comment: + // The permacell radios aren't suppose to be able to transmit, this isn't a bug and this "fix" is just making radio wires useless. -Giacom + if(wires.IsIndexCut(RADIO_WIRE_TRANSMIT)) // The device has to have all its wires and shit intact + return 0 + + if(!M.IsVocal()) + return 0 + + /* Quick introduction: + This new radio system uses a very robust FTL signaling technology unoriginally + dubbed "subspace" which is somewhat similar to 'blue-space' but can't + actually transmit large mass. Headsets are the only radio devices capable + of sending subspace transmissions to the Communications Satellite. + + A headset sends a signal to a subspace listener/receiver elsewhere in space, + the signal gets processed and logged, and an audible transmission gets sent + to each individual headset. + */ + + //#### Grab the connection datum ####// + var/message_mode = handle_message_mode(M, message_pieces, channel) + switch(message_mode) //special cases + if(RADIO_CONNECTION_FAIL) + return 0 + if(RADIO_CONNECTION_NON_SUBSPACE) + return 1 + + if(!istype(message_mode, /datum/radio_frequency)) //if not a special case, it should be returning a radio connection + return 0 + + var/datum/radio_frequency/connection = message_mode + var/turf/position = get_turf(src) + + var/jammed = FALSE + for(var/obj/item/jammer/jammer in GLOB.active_jammers) + if(get_dist(position, get_turf(jammer)) < jammer.range) + jammed = TRUE + break + + //#### Tagging the signal with all appropriate identity values ####// + + // ||-- The mob's name identity --|| + var/displayname = M.name // grab the display name (name you get when you hover over someone's icon) + var/real_name = M.real_name // mob's real name + var/mobkey = "none" // player key associated with mob + var/voicemask = 0 // the speaker is wearing a voice mask + if(M.client) + mobkey = M.key // assign the mob's key + + + var/jobname // the mob's "job" + + if(jammed) + Gibberish_all(message_pieces, 100) + + // --- Human: use their actual job --- + if(ishuman(M)) + var/mob/living/carbon/human/H = M + jobname = H.get_assignment() + + // --- Carbon Nonhuman --- + else if(iscarbon(M)) // Nonhuman carbon mob + jobname = "No id" + + // --- AI --- + else if(isAI(M)) + jobname = "AI" + + // --- Cyborg --- + else if(isrobot(M)) + jobname = "Cyborg" + + // --- Personal AI (pAI) --- + else if(istype(M, /mob/living/silicon/pai)) + jobname = "Personal AI" + + // --- Unidentifiable mob --- + else + jobname = "Unknown" + + + // --- Modifications to the mob's identity --- + + // The mob is disguising their identity: + if(ishuman(M)) + var/mob/living/carbon/human/H = M + displayname = H.voice + if(H.voice != real_name) + voicemask = TRUE + + if(syndiekey && syndiekey.change_voice && connection.frequency == SYND_FREQ) + displayname = syndiekey.fake_name + jobname = "Unknown" + voicemask = TRUE + + + /* ###### Radio headsets can only broadcast through subspace ###### */ + + if(subspace_transmission) + // First, we want to generate a new radio signal + var/datum/signal/signal = new + signal.transmission_method = 2 // 2 would be a subspace transmission. + // transmission_method could probably be enumerated through #define. Would be neater. + + // --- Finally, tag the actual signal with the appropriate values --- + signal.data = list( + // Identity-associated tags: + "mob" = M, // store a reference to the mob + "mobtype" = M.type, // the mob's type + "race" = signal.get_race(M), + "realname" = real_name, // the mob's real name + "name" = displayname, // the mob's display name + "job" = jobname, // the mob's job + "key" = mobkey, // the mob's key + "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood + "vname" = M.voice_name, // the name to display if the voice wasn't understood + "vmask" = voicemask, // 1 if the mob is using a voice gas mask + + // We store things that would otherwise be kept in the actual mob + // so that they can be logged even AFTER the mob is deleted or something + + // Other tags: + "compression" = rand(45,50), // compressed radio signal + "message" = message_pieces, // the actual sent message + "connection" = connection, // the radio connection to use + "radio" = src, // stores the radio used for transmission + "slow" = 0, // how much to sleep() before broadcasting - simulates net lag + "traffic" = 0, // dictates the total traffic sum that the signal went through + "type" = 0, // determines what type of radio input it is: normal broadcast + "server" = null, // the last server to log this signal + "reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery + "level" = position.z, // The source's z level + "verb" = verb + ) + signal.frequency = connection.frequency // Quick frequency set + + //#### Sending the signal to all subspace receivers ####// + + for(var/obj/machinery/telecomms/receiver/R in GLOB.telecomms_list) + spawn(0) + R.receive_signal(signal) + + // Allinone can act as receivers. + for(var/obj/machinery/telecomms/allinone/R in GLOB.telecomms_list) + spawn(0) + R.receive_signal(signal) + + // Receiving code can be located in Telecommunications.dm + return signal.data["done"] && position.z in signal.data["level"] + + + /* ###### Intercoms and station-bounced radios ###### */ + + var/filter_type = 2 + + /* --- Intercoms can only broadcast to other intercoms, but bounced radios can broadcast to bounced radios and intercoms --- */ + if(istype(src, /obj/item/radio/intercom)) + filter_type = 1 + + + var/datum/signal/signal = new + signal.transmission_method = 2 + + + /* --- Try to send a normal subspace broadcast first */ + + signal.data = list( + + "mob" = M, // store a reference to the mob + "mobtype" = M.type, // the mob's type + "race" = signal.get_race(M), // text to show next to mob in comms log console + "realname" = real_name, // the mob's real name + "name" = displayname, // the mob's display name + "job" = jobname, // the mob's job + "key" = mobkey, // the mob's key + "vmessage" = pick(M.speak_emote), // the message to display if the voice wasn't understood + "vname" = M.voice_name, // the name to display if the voice wasn't understood + "vmask" = voicemask, // 1 if the mob is using a voice gas mas + + "compression" = 0, // uncompressed radio signal + "message" = message_pieces, // the actual sent message + "connection" = connection, // the radio connection to use + "radio" = src, // stores the radio used for transmission + "slow" = 0, + "traffic" = 0, + "type" = 0, + "server" = null, + "reject" = 0, + "level" = position.z, + "verb" = verb + ) + signal.frequency = connection.frequency // Quick frequency set + + for(var/obj/machinery/telecomms/receiver/R in GLOB.telecomms_list) + spawn(0) + R.receive_signal(signal) + + + sleep(rand(10,25)) // wait a little... + + if(signal.data["done"] && position.z in signal.data["level"]) + // we're done here. + return 1 + + // Oh my god; the comms are down or something because the signal hasn't been broadcasted yet in our level. + // Send a mundane broadcast with limited targets: + + //THIS IS TEMPORARY. YEAH RIGHT + if(!connection) return 0 //~Carn + + return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote), + src, message_pieces, displayname, jobname, real_name, M.voice_name, + filter_type, signal.data["compression"], list(position.z), connection.frequency,verb) + + +/obj/item/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says") + if(broadcasting) + if(get_dist(src, M) <= canhear_range) + talk_into(M, message_pieces, null, verb) + + +/* +/obj/item/radio/proc/accept_rad(obj/item/radio/R as obj, message) + + if((R.frequency == frequency && message)) + return 1 + else if + + else + return null + return +*/ + + +/obj/item/radio/proc/receive_range(freq, level) + // check if this radio can receive on the given frequency, and if so, + // what the range is in which mobs will hear the radio + // returns: -1 if can't receive, range otherwise + + if(!is_listening()) + return -1 + if(!(0 in level)) + var/turf/position = get_turf(src) + if(!position || !(position.z in level)) + return -1 + if(freq in SSradio.ANTAG_FREQS) + if(!(syndiekey))//Checks to see if it's allowed on that frequency, based on the encryption keys + return -1 + if(!freq) //recieved on main frequency + if(!listening) + return -1 + else + var/accept = (freq==frequency && listening) + if(!accept) + for(var/ch_name in channels) + var/datum/radio_frequency/RF = secure_radio_connections[ch_name] + if(RF.frequency==freq && (channels[ch_name]&FREQ_LISTENING)) + accept = 1 + break + if(!accept) + return -1 + return canhear_range + +/obj/item/radio/proc/send_hear(freq, level) + var/range = receive_range(freq, level) + if(range > -1) + return get_mobs_in_view(canhear_range, src) + +/obj/item/radio/proc/is_listening() + var/is_listening = TRUE + if(!on) + is_listening = FALSE + if(!wires || wires.IsIndexCut(RADIO_WIRE_RECEIVE)) + is_listening = FALSE + if(!listening) + is_listening = FALSE + + return is_listening + +/obj/item/radio/proc/send_announcement() + if(is_listening()) + return get_mobs_in_view(canhear_range, src) + + return null + +/obj/item/radio/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(b_stat) + . += "\the [src] can be attached and modified!" + else + . += "\the [src] can not be modified or attached!" + +/obj/item/radio/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + user.set_machine(src) + b_stat = !b_stat + if(!istype(src, /obj/item/radio/beacon)) + if(b_stat) + user.show_message("The radio can now be attached and modified!") + else + user.show_message("The radio can no longer be modified or attached!") + updateDialog() + +/obj/item/radio/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + interact(user) + +/obj/item/radio/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + interact(user) + +/obj/item/radio/emp_act(severity) + on = 0 + disable_timer++ + addtimer(CALLBACK(src, .proc/enable_radio), rand(100, 200)) + + if(listening) + visible_message("[src] buzzes violently!") + + broadcasting = 0 + listening = 0 + for(var/ch_name in channels) + channels[ch_name] = 0 + ..() + +/obj/item/radio/proc/enable_radio() + if(disable_timer > 0) + disable_timer-- + if(!disable_timer) + on = 1 + +/////////////////////////////// +//////////Borg Radios////////// +/////////////////////////////// +//Giving borgs their own radio to have some more room to work with -Sieve + +/obj/item/radio/borg + var/mob/living/silicon/robot/myborg = null // Cyborg which owns this radio. Used for power checks + var/obj/item/encryptionkey/keyslot = null//Borg radios can handle a single encryption key + var/shut_up = 1 + icon = 'icons/obj/robot_component.dmi' // Cyborgs radio icons should look like the component. + icon_state = "radio" + canhear_range = 0 + subspace_transmission = 1 + dog_fashion = null + +/obj/item/radio/borg/syndicate + keyslot = new /obj/item/encryptionkey/syndicate/nukeops + +/obj/item/radio/borg/syndicate/CanUseTopic(mob/user, datum/topic_state/state) + . = ..() + if(. == STATUS_UPDATE && istype(user, /mob/living/silicon/robot/syndicate)) + . = STATUS_INTERACTIVE + +/obj/item/radio/borg/Destroy() + myborg = null + return ..() + +/obj/item/radio/borg/list_channels(var/mob/user) + return list_secure_channels(user) + +/obj/item/radio/borg/syndicate/New() + ..() + syndiekey = keyslot + set_frequency(SYND_FREQ) + +/obj/item/radio/borg/deathsquad + +/obj/item/radio/borg/deathsquad/New() + ..() + set_frequency(DTH_FREQ) + +/obj/item/radio/borg/ert + keyslot = new /obj/item/encryptionkey/ert + +/obj/item/radio/borg/ert/New() + ..() + set_frequency(ERT_FREQ) + +/obj/item/radio/borg/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/encryptionkey/)) + user.set_machine(src) + if(keyslot) + to_chat(user, "The radio can't hold another key!") + return + + if(!keyslot) + user.drop_item() + W.loc = src + keyslot = W + + recalculateChannels() + else + return ..() + +/obj/item/radio/borg/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + user.set_machine(src) + if(keyslot) + for(var/ch_name in channels) + SSradio.remove_object(src, SSradio.radiochannels[ch_name]) + secure_radio_connections[ch_name] = null + + + if(keyslot) + var/turf/T = get_turf(user) + if(T) + keyslot.loc = T + keyslot = null + + recalculateChannels() + to_chat(user, "You pop out the encryption key in the radio!") + I.play_tool_sound(user, I.tool_volume) + + else + to_chat(user, "This radio doesn't have any encryption keys!") + +/obj/item/radio/borg/proc/recalculateChannels() + channels = list() + syndiekey = null + + var/mob/living/silicon/robot/D = loc + if(D.module) + for(var/ch_name in D.module.channels) + if(ch_name in channels) + continue + channels += ch_name + channels[ch_name] += D.module.channels[ch_name] + if(keyslot) + for(var/ch_name in keyslot.channels) + if(ch_name in channels) + continue + channels += ch_name + channels[ch_name] += keyslot.channels[ch_name] + + if(keyslot.syndie) + syndiekey = keyslot + + + for(var/ch_name in channels) + if(!SSradio) + sleep(30) // Waiting for SSradio to be created. + if(!SSradio) + name = "broken radio" + return + + secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) + + return + +/obj/item/radio/borg/Topic(href, href_list) + if(..()) + return 1 + if(href_list["mode"]) + var/enable_subspace_transmission = text2num(href_list["mode"]) + if(enable_subspace_transmission != subspace_transmission) + subspace_transmission = !subspace_transmission + if(subspace_transmission) + to_chat(usr, "Subspace Transmission is enabled.") + else + to_chat(usr, "Subspace Transmission is disabled.") + + if(subspace_transmission == 0)//Simple as fuck, clears the channel list to prevent talking/listening over them if subspace transmission is disabled + channels = list() + else + recalculateChannels() + . = 1 + if(href_list["shutup"]) // Toggle loudspeaker mode, AKA everyone around you hearing your radio. + var/do_shut_up = text2num(href_list["shutup"]) + if(do_shut_up != shut_up) + shut_up = !shut_up + if(shut_up) + canhear_range = 0 + to_chat(usr, "Loudspeaker disabled.") + else + canhear_range = 3 + to_chat(usr, "Loudspeaker enabled.") + . = 1 + + +/obj/item/radio/borg/interact(mob/user as mob) + if(!on) + return + + . = ..() + +/obj/item/radio/borg/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "radio_basic.tmpl", "[name]", 430, 500) + ui.open() + ui.set_auto_update(1) + +/obj/item/radio/borg/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["mic_status"] = broadcasting + data["speaker"] = listening + data["freq"] = format_frequency(frequency) + data["rawfreq"] = num2text(frequency) + + var/list/chanlist = list_channels(user) + if(islist(chanlist) && chanlist.len) + data["chan_list"] = chanlist + data["chan_list_len"] = chanlist.len + + if(syndiekey) + data["useSyndMode"] = 1 + + data["has_loudspeaker"] = 1 + data["loudspeaker"] = !shut_up + data["has_subspace"] = 1 + data["subspace"] = subspace_transmission + + return data + +/obj/item/radio/proc/config(op) + if(SSradio) + for(var/ch_name in channels) + SSradio.remove_object(src, SSradio.radiochannels[ch_name]) + secure_radio_connections = new + channels = op + if(SSradio) + for(var/ch_name in op) + secure_radio_connections[ch_name] = SSradio.add_object(src, SSradio.radiochannels[ch_name], RADIO_CHAT) + return + +/obj/item/radio/off + listening = 0 + dog_fashion = /datum/dog_fashion/back + +/obj/item/radio/phone + broadcasting = 0 + icon = 'icons/obj/items.dmi' + icon_state = "red_phone" + listening = 1 + name = "phone" + dog_fashion = null + +/obj/item/radio/phone/medbay + frequency = MED_I_FREQ + +/obj/item/radio/phone/medbay/New() + ..() + internal_channels = GLOB.default_medbay_channels.Copy() diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 536c3dfd0e2b..34183a811028 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -1,869 +1,869 @@ -/* -CONTAINS: -T-RAY -DETECTIVE SCANNER -HEALTH ANALYZER -GAS ANALYZER -PLANT ANALYZER -REAGENT SCANNER -*/ -/obj/item/t_scanner - name = "T-ray scanner" - desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - icon = 'icons/obj/device.dmi' - icon_state = "t-ray0" - var/on = 0 - slot_flags = SLOT_BELT - w_class = 2 - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - materials = list(MAT_METAL=150) - origin_tech = "magnets=1;engineering=1" - var/scan_range = 1 - var/pulse_duration = 10 - -/obj/item/t_scanner/longer_pulse - pulse_duration = 50 - -/obj/item/t_scanner/extended_range - scan_range = 3 - -/obj/item/t_scanner/extended_range/longer_pulse - scan_range = 3 - pulse_duration = 50 - -/obj/item/t_scanner/Destroy() - if(on) - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/t_scanner/attack_self(mob/user) - - on = !on - icon_state = copytext(icon_state, 1, length(icon_state))+"[on]" - - if(on) - START_PROCESSING(SSobj, src) - - -/obj/item/t_scanner/process() - if(!on) - STOP_PROCESSING(SSobj, src) - return null - scan() - -/obj/item/t_scanner/proc/scan() - - for(var/turf/T in range(scan_range, src.loc) ) - - if(!T.intact) - continue - - for(var/obj/O in T.contents) - - if(O.level != 1) - continue - - if(O.invisibility == 101) - O.invisibility = 0 - O.alpha = 128 - spawn(pulse_duration) - if(O) - var/turf/U = O.loc - if(U && U.intact) - O.invisibility = 101 - O.alpha = 255 - for(var/mob/living/M in T.contents) - var/oldalpha = M.alpha - if(M.alpha < 255 && istype(M)) - M.alpha = 255 - spawn(10) - if(M) - M.alpha = oldalpha - - var/mob/living/M = locate() in T - - if(M && M.invisibility == 2) - M.invisibility = 0 - spawn(2) - if(M) - M.invisibility = INVISIBILITY_LEVEL_TWO - - -/proc/chemscan(mob/living/user, mob/living/M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.reagents) - if(H.reagents.reagent_list.len) - to_chat(user, "Subject contains the following reagents:") - for(var/datum/reagent/R in H.reagents.reagent_list) - to_chat(user, "[R.volume]u of [R.name][R.overdosed ? " - OVERDOSING" : ".
    "]") - else - to_chat(user, "Subject contains no reagents.") - if(H.reagents.addiction_list.len) - to_chat(user, "Subject is addicted to the following reagents:") - for(var/datum/reagent/R in H.reagents.addiction_list) - to_chat(user, "[R.name] Stage: [R.addiction_stage]/5") - else - to_chat(user, "Subject is not addicted to any reagents.") - -/obj/item/healthanalyzer - name = "Health Analyzer" - icon = 'icons/obj/device.dmi' - icon_state = "health" - item_state = "healthanalyzer" - desc = "A hand-held body scanner able to distinguish vital signs of the subject." - flags = CONDUCT | NOBLUDGEON - slot_flags = SLOT_BELT - throwforce = 3 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=200) - origin_tech = "magnets=1;biotech=1" - var/mode = 1 - var/advanced = FALSE - -/obj/item/healthanalyzer/attack(mob/living/M, mob/living/user) - if(((CLUMSY in user.mutations) || user.getBrainLoss() >= 60) && prob(50)) - user.visible_message("[user] analyzes the floor's vitals!", "You stupidly try to analyze the floor's vitals!") - to_chat(user, "Analyzing results for The floor:\n\tOverall status: Healthy") - to_chat(user, "Key: Suffocation/Toxin/Burn/Brute") - to_chat(user, "\tDamage specifics: 0-0-0-0") - to_chat(user, "Body temperature: ???") - return - - user.visible_message("[user] analyzes [M]'s vitals.", "You analyze [M]'s vitals.") - - healthscan(user, M, mode, advanced) - - add_fingerprint(user) - -// Used by the PDA medical scanner too -/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE) - if(!ishuman(M) || M.isSynthetic()) - //these sensors are designed for organic life - to_chat(user, "Analyzing Results for ERROR:\n\t Overall Status: ERROR") - to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute") - to_chat(user, "\t Damage Specifics: ? - ? - ? - ?") - to_chat(user, "Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)") - to_chat(user, "Warning: Blood Level ERROR: --% --cl.Type: ERROR") - to_chat(user, "Subject's pulse: -- bpm.") - return - - var/mob/living/carbon/human/H = M - var/fake_oxy = max(rand(1,40), H.getOxyLoss(), (300 - (H.getToxLoss() + H.getFireLoss() + H.getBruteLoss()))) - var/OX = H.getOxyLoss() > 50 ? "[H.getOxyLoss()]" : H.getOxyLoss() - var/TX = H.getToxLoss() > 50 ? "[H.getToxLoss()]" : H.getToxLoss() - var/BU = H.getFireLoss() > 50 ? "[H.getFireLoss()]" : H.getFireLoss() - var/BR = H.getBruteLoss() > 50 ? "[H.getBruteLoss()]" : H.getBruteLoss() - if(H.status_flags & FAKEDEATH) - OX = fake_oxy > 50 ? "[fake_oxy]" : fake_oxy - to_chat(user, "Analyzing Results for [H]:\n\t Overall Status: dead") - else - to_chat(user, "Analyzing Results for [H]:\n\t Overall Status: [H.stat > 1 ? "dead" : "[H.health]% healthy"]") - to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute") - to_chat(user, "\t Damage Specifics: [OX] - [TX] - [BU] - [BR]") - to_chat(user, "Body Temperature: [H.bodytemperature-T0C]°C ([H.bodytemperature*1.8-459.67]°F)") - if(H.timeofdeath && (H.stat == DEAD || (H.status_flags & FAKEDEATH))) - to_chat(user, "Time of Death: [station_time_timestamp("hh:mm:ss", H.timeofdeath)]") - var/tdelta = round(world.time - H.timeofdeath) - if(tdelta < (DEFIB_TIME_LIMIT * 10)) - to_chat(user, "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!") - - if(mode == 1) - var/list/damaged = H.get_damaged_organs(1,1) - to_chat(user, "Localized Damage, Brute/Burn:") - if(length(damaged) > 0) - for(var/obj/item/organ/external/org in damaged) - to_chat(user, "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]") - - OX = H.getOxyLoss() > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal" - TX = H.getToxLoss() > 50 ? "Dangerous amount of toxins detected" : "Subject bloodstream toxin level minimal" - BU = H.getFireLoss() > 50 ? "Severe burn damage detected" : "Subject burn injury status O.K" - BR = H.getBruteLoss() > 50 ? "Severe anatomical damage detected" : "Subject brute-force injury status O.K" - if(H.status_flags & FAKEDEATH) - OX = fake_oxy > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal" - to_chat(user, "[OX] | [TX] | [BU] | [BR]") - - if(advanced) - chemscan(user, H) - for(var/thing in H.viruses) - var/datum/disease/D = thing - if(!(D.visibility_flags & HIDDEN_SCANNER)) - to_chat(user, "Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]") - if(H.undergoing_cardiac_arrest()) - var/obj/item/organ/internal/heart/heart = H.get_int_organ(/obj/item/organ/internal/heart) - if(heart && !(heart.status & ORGAN_DEAD)) - to_chat(user, "Warning: Medical Emergency detected\nName: Cardiac Arrest.\nType: The patient's heart has stopped.\nStage: 1/1.\nPossible Cure: Electric Shock") - else if(heart && (heart.status & ORGAN_DEAD)) - to_chat(user, "Subject's heart is necrotic.") - else if(!heart) - to_chat(user, "Subject has no heart.") - - if(H.getStaminaLoss()) - to_chat(user, "Subject appears to be suffering from fatigue.") - if(H.getCloneLoss()) - to_chat(user, "Subject appears to have [H.getCloneLoss() > 30 ? "severe" : "minor"] cellular damage.") - if(H.has_brain_worms()) - to_chat(user, "Subject suffering from aberrant brain activity. Recommend further scanning.") - - if(H.get_int_organ(/obj/item/organ/internal/brain)) - if(H.getBrainLoss() >= 100) - to_chat(user, "Subject is brain dead.") - else if(H.getBrainLoss() >= 60) - to_chat(user, "Severe brain damage detected. Subject likely to have mental retardation.") - else if(H.getBrainLoss() >= 10) - to_chat(user, "Significant brain damage detected. Subject may have had a concussion.") - else - to_chat(user, "Subject has no brain.") - - for(var/name in H.bodyparts_by_name) - var/obj/item/organ/external/e = H.bodyparts_by_name[name] - if(!e) - continue - var/limb = e.name - if(e.status & ORGAN_BROKEN) - if((e.limb_name in list("l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot")) && !(e.status & ORGAN_SPLINTED)) - to_chat(user, "Unsecured fracture in subject [limb]. Splinting recommended for transport.") - if(e.has_infected_wound()) - to_chat(user, "Infected wound detected in subject [limb]. Disinfection recommended.") - - for(var/name in H.bodyparts_by_name) - var/obj/item/organ/external/e = H.bodyparts_by_name[name] - if(!e) - continue - if(e.status & ORGAN_BROKEN) - to_chat(user, "Bone fractures detected. Advanced scanner required for location.") - break - for(var/obj/item/organ/external/e in H.bodyparts) - if(e.internal_bleeding) - to_chat(user, "Internal bleeding detected. Advanced scanner required for location.") - break - var/blood_id = H.get_blood_id() - if(blood_id) - if(H.bleed_rate) - to_chat(user, "Subject is bleeding!") - var/blood_percent = round((H.blood_volume / BLOOD_VOLUME_NORMAL)*100) - var/blood_type = H.dna.blood_type - if(blood_id != "blood")//special blood substance - var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] - if(R) - blood_type = R.name - else - blood_type = blood_id - if(H.blood_volume <= BLOOD_VOLUME_SAFE && H.blood_volume > BLOOD_VOLUME_OKAY) - to_chat(user, "LOW blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]") - else if(H.blood_volume <= BLOOD_VOLUME_OKAY) - to_chat(user, "CRITICAL blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]") - else - to_chat(user, "Blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]") - - to_chat(user, "Subject's pulse: [H.get_pulse(GETPULSE_TOOL)] bpm.") - var/implant_detect - for(var/obj/item/organ/internal/cyberimp/CI in H.internal_organs) - if(CI.is_robotic()) - implant_detect += "[H.name] is modified with a [CI.name].
    " - if(implant_detect) - to_chat(user, "Detected cybernetic modifications:") - to_chat(user, "[implant_detect]") - if(H.gene_stability < 40) - to_chat(user, "Subject's genes are quickly breaking down!") - else if(H.gene_stability < 70) - to_chat(user, "Subject's genes are showing signs of spontaneous breakdown.") - else if(H.gene_stability < 85) - to_chat(user, "Subject's genes are showing minor signs of instability.") - else - to_chat(user, "Subject's genes are stable.") - -/obj/item/healthanalyzer/attack_self(mob/user) - toggle_mode() - -/obj/item/healthanalyzer/verb/toggle_mode() - set name = "Switch Verbosity" - set category = "Object" - - mode = !mode - switch(mode) - if(1) - to_chat(usr, "The scanner now shows specific limb damage.") - if(0) - to_chat(usr, "The scanner no longer shows limb damage.") - -/obj/item/healthanalyzer/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/healthupgrade)) - if(advanced) - to_chat(user, "An upgrade is already installed on [src].") - else - if(user.unEquip(I)) - to_chat(user, "You install the upgrade on [src].") - add_overlay("advanced") - playsound(loc, I.usesound, 50, 1) - advanced = TRUE - qdel(I) - return - return ..() - -/obj/item/healthanalyzer/advanced - advanced = TRUE - -/obj/item/healthanalyzer/advanced/Initialize(mapload) - . = ..() - add_overlay("advanced") - - -/obj/item/healthupgrade - name = "Health Analyzer Upgrade" - icon = 'icons/obj/device.dmi' - icon_state = "healthupgrade" - desc = "An upgrade unit that can be installed on a health analyzer for expanded functionality." - w_class = WEIGHT_CLASS_TINY - origin_tech = "magnets=2;biotech=2" - usesound = 'sound/items/deconstruct.ogg' - -/obj/item/analyzer - desc = "A hand-held environmental scanner which reports current gas levels." - name = "analyzer" - icon = 'icons/obj/device.dmi' - icon_state = "atmos" - item_state = "analyzer" - w_class = WEIGHT_CLASS_SMALL - flags = CONDUCT - slot_flags = SLOT_BELT - throwforce = 0 - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=30, MAT_GLASS=20) - origin_tech = "magnets=1;engineering=1" - var/cooldown = FALSE - var/cooldown_time = 250 - var/accuracy // 0 is the best accuracy. - -/obj/item/analyzer/examine(mob/user) - . = ..() - . += "Alt-click [src] to activate the barometer function." - -/obj/item/analyzer/attack_self(mob/user as mob) - - if(user.stat) - return - - var/turf/location = user.loc - if(!( istype(location, /turf) )) - return - - var/datum/gas_mixture/environment = location.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() - - to_chat(user, "Results:") - if(abs(pressure - ONE_ATMOSPHERE) < 10) - to_chat(user, "Pressure: [round(pressure,0.1)] kPa") - else - to_chat(user, "Pressure: [round(pressure,0.1)] kPa") - if(total_moles) - var/o2_concentration = environment.oxygen/total_moles - var/n2_concentration = environment.nitrogen/total_moles - var/co2_concentration = environment.carbon_dioxide/total_moles - var/plasma_concentration = environment.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) - if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(user, "Nitrogen: [round(n2_concentration*100)] %") - else - to_chat(user, "Nitrogen: [round(n2_concentration*100)] %") - - if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(user, "Oxygen: [round(o2_concentration*100)] %") - else - to_chat(user, "Oxygen: [round(o2_concentration*100)] %") - - if(co2_concentration > 0.01) - to_chat(user, "CO2: [round(co2_concentration*100)] %") - else - to_chat(user, "CO2: [round(co2_concentration*100)] %") - - if(plasma_concentration > 0.01) - to_chat(user, "Plasma: [round(plasma_concentration*100)] %") - - if(unknown_concentration > 0.01) - to_chat(user, "Unknown: [round(unknown_concentration*100)] %") - - to_chat(user, "Temperature: [round(environment.temperature-T0C)] °C") - - add_fingerprint(user) - -/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens - ..() - - if(!user.incapacitated() && Adjacent(user)) - - if(cooldown) - to_chat(user, "[src]'s barometer function is prepraring itself.") - return - - var/turf/T = get_turf(user) - if(!T) - return - - playsound(src, 'sound/effects/pop.ogg', 100) - var/area/user_area = T.loc - var/datum/weather/ongoing_weather = null - - if(!user_area.outdoors) - to_chat(user, "[src]'s barometer function won't work indoors!") - return - - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) - ongoing_weather = W - break - - if(ongoing_weather) - if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) - to_chat(user, "[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]") - return - - to_chat(user, "The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].") - if(ongoing_weather.aesthetic) - to_chat(user, "[src]'s barometer function says that the next storm will breeze on by.") - else - var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] - var/fixed = next_hit ? next_hit - world.time : -1 - if(fixed < 0) - to_chat(user, "[src]'s barometer function was unable to trace any weather patterns.") - else - to_chat(user, "[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].") - cooldown = TRUE - addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) - -/obj/item/analyzer/proc/ping() - if(isliving(loc)) - var/mob/living/L = loc - to_chat(L, "[src]'s barometer function is ready!") - playsound(src, 'sound/machines/click.ogg', 100) - cooldown = FALSE - -/obj/item/analyzer/proc/butchertime(amount) - if(!amount) - return - if(accuracy) - var/inaccurate = round(accuracy * (1 / 3)) - if(prob(50)) - amount -= inaccurate - if(prob(50)) - amount += inaccurate - return DisplayTimeText(max(1, amount)) - -/obj/item/reagent_scanner - name = "reagent scanner" - desc = "A hand-held reagent scanner which identifies chemical agents and blood types." - icon = 'icons/obj/device.dmi' - icon_state = "spectrometer" - item_state = "analyzer" - w_class = WEIGHT_CLASS_SMALL - flags = CONDUCT - slot_flags = SLOT_BELT - throwforce = 5 - throw_speed = 4 - throw_range = 20 - materials = list(MAT_METAL=30, MAT_GLASS=20) - origin_tech = "magnets=2;biotech=1;plasmatech=2" - var/details = FALSE - var/datatoprint = "" - var/scanning = TRUE - actions_types = list(/datum/action/item_action/print_report) - -/obj/item/reagent_scanner/afterattack(obj/O, mob/user as mob) - if(user.stat) - return - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return - if(!istype(O)) - return - - if(!isnull(O.reagents)) - var/dat = "" - var/blood_type = "" - if(O.reagents.reagent_list.len > 0) - var/one_percent = O.reagents.total_volume / 100 - for(var/datum/reagent/R in O.reagents.reagent_list) - if(R.id != "blood") - dat += "
    [TAB][R][details ? ": [R.volume / one_percent]%" : ""]" - else - blood_type = R.data["blood_type"] - dat += "
    [TAB][R][blood_type ? " [blood_type]" : ""][details ? ": [R.volume / one_percent]%" : ""]" - if(dat) - to_chat(user, "Chemicals found: [dat]") - datatoprint = dat - scanning = FALSE - else - to_chat(user, "No active chemical agents found in [O].") - else - to_chat(user, "No significant chemical agents found in [O].") - return - -/obj/item/reagent_scanner/adv - name = "advanced reagent scanner" - icon_state = "adv_spectrometer" - details = TRUE - origin_tech = "magnets=4;biotech=3;plasmatech=3" - -/obj/item/reagent_scanner/proc/print_report() - if(!scanning) - usr.visible_message("[src] rattles and prints out a sheet of paper.") - playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) - sleep(50) - - var/obj/item/paper/P = new(get_turf(src)) - P.name = "Reagent Scanner Report: [station_time_timestamp()]" - P.info = "
    Reagent Scanner

    Data Analysis:



    Chemical agents detected:
    [datatoprint]

    " - - if(ismob(loc)) - var/mob/M = loc - M.put_in_hands(P) - to_chat(M, "Report printed. Log cleared.") - datatoprint = "" - scanning = TRUE - else - to_chat(usr, "[src] has no logs or is already in use.") - -/obj/item/reagent_scanner/ui_action_click() - print_report() - -/obj/item/slime_scanner - name = "slime scanner" - icon = 'icons/obj/device.dmi' - icon_state = "adv_spectrometer_s" - item_state = "analyzer" - origin_tech = "biotech=2" - w_class = WEIGHT_CLASS_SMALL - flags = CONDUCT - throwforce = 0 - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=30, MAT_GLASS=20) - -/obj/item/slime_scanner/attack(mob/living/M, mob/living/user) - if(user.incapacitated() || user.eye_blind) - return - if(!isslime(M)) - to_chat(user, "This device can only scan slimes!") - return - var/mob/living/simple_animal/slime/T = M - slime_scan(T, user) - -/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user) - to_chat(user, "========================") - to_chat(user, "Slime scan results:") - to_chat(user, "[T.colour] [T.is_adult ? "adult" : "baby"] slime") - to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]") - if(T.nutrition < T.get_starve_nutrition()) - to_chat(user, "Warning: slime is starving!") - else if(T.nutrition < T.get_hunger_nutrition()) - to_chat(user, "Warning: slime is hungry") - to_chat(user, "Electric change strength: [T.powerlevel]") - to_chat(user, "Health: [round(T.health/T.maxHealth,0.01)*100]%") - if(T.slime_mutation[4] == T.colour) - to_chat(user, "This slime does not evolve any further.") - else - if(T.slime_mutation[3] == T.slime_mutation[4]) - if(T.slime_mutation[2] == T.slime_mutation[1]) - to_chat(user, "Possible mutation: [T.slime_mutation[3]]") - to_chat(user, "Genetic destability: [T.mutation_chance/2] % chance of mutation on splitting") - else - to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)") - to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") - else - to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]") - to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") - if(T.cores > 1) - to_chat(user, "Multiple cores detected") - to_chat(user, "Growth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]") - if(T.effectmod) - to_chat(user, "Core mutation in progress: [T.effectmod]") - to_chat(user, "Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]") - to_chat(user, "========================") - -/obj/item/bodyanalyzer - name = "handheld body analyzer" - icon = 'icons/obj/device.dmi' - icon_state = "bodyanalyzer_0" - item_state = "healthanalyser" - desc = "A handheld scanner capable of deep-scanning an entire body." - slot_flags = SLOT_BELT - throwforce = 3 - w_class = WEIGHT_CLASS_TINY - throw_speed = 5 - throw_range = 10 - origin_tech = "magnets=6;biotech=6" - var/obj/item/stock_parts/cell/cell - var/cell_type = /obj/item/stock_parts/cell/upgraded - var/ready = TRUE // Ready to scan - var/time_to_use = 0 // How much time remaining before next scan is available. - var/usecharge = 750 - var/scan_time = 10 SECONDS //how long does it take to scan - var/scan_cd = 60 SECONDS //how long before we can scan again - -/obj/item/bodyanalyzer/get_cell() - return cell - -/obj/item/bodyanalyzer/advanced - cell_type = /obj/item/stock_parts/cell/upgraded/plus - -/obj/item/bodyanalyzer/borg - name = "cyborg body analyzer" - desc = "Scan an entire body to prepare for field surgery. Consumes power for each scan." - -/obj/item/bodyanalyzer/borg/syndicate - scan_time = 5 SECONDS - scan_cd = 20 SECONDS - -/obj/item/bodyanalyzer/New() - ..() - cell = new cell_type(src) - cell.give(cell.maxcharge) - update_icon() - -/obj/item/bodyanalyzer/proc/setReady() - ready = TRUE - playsound(src, 'sound/machines/defib_saftyon.ogg', 50, 0) - update_icon() - -/obj/item/bodyanalyzer/update_icon(printing = FALSE) - overlays.Cut() - var/percent = cell.percent() - if(ready) - icon_state = "bodyanalyzer_1" - else - icon_state = "bodyanalyzer_2" - - var/overlayid = round(percent / 10) - overlayid = "bodyanalyzer_charge[overlayid]" - overlays += icon(icon, overlayid) - - if(printing) - overlays += icon(icon, "bodyanalyzer_printing") - -/obj/item/bodyanalyzer/attack(mob/living/M, mob/living/carbon/human/user) - if(user.incapacitated() || !user.Adjacent(M)) - return - - if(!ready) - to_chat(user, "The scanner beeps angrily at you! It's currently recharging - [round((time_to_use - world.time) * 0.1)] seconds remaining.") - playsound(user.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) - return - - if(cell.charge >= usecharge) - mobScan(M, user) - else - to_chat(user, "The scanner beeps angrily at you! It's out of charge!") - playsound(user.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) - -/obj/item/bodyanalyzer/borg/attack(mob/living/M, mob/living/silicon/robot/user) - if(user.incapacitated() || !user.Adjacent(M)) - return - - if(!ready) - to_chat(user, "[src] is currently recharging - [round((time_to_use - world.time) * 0.1)] seconds remaining.") - return - - if(user.cell.charge >= usecharge) - mobScan(M, user) - else - to_chat(user, "You need to recharge before you can use [src]") - -/obj/item/bodyanalyzer/proc/mobScan(mob/living/M, mob/user) - if(ishuman(M)) - var/report = generate_printing_text(M, user) - user.visible_message("[user] begins scanning [M] with [src].", "You begin scanning [M].") - if(do_after(user, scan_time, target = M)) - var/obj/item/paper/printout = new - printout.info = report - printout.name = "Scan report - [M.name]" - playsound(user.loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - user.put_in_hands(printout) - time_to_use = world.time + scan_cd - if(isrobot(user)) - var/mob/living/silicon/robot/R = user - R.cell.use(usecharge) - else - cell.use(usecharge) - ready = FALSE - update_icon(TRUE) - addtimer(CALLBACK(src, /obj/item/bodyanalyzer/.proc/setReady), scan_cd) - addtimer(CALLBACK(src, /obj/item/bodyanalyzer/.proc/update_icon), 20) - - else if(iscorgi(M) && M.stat == DEAD) - to_chat(user, "You wonder if [M.p_they()] was a good dog. [src] tells you they were the best...") // :'( - playsound(loc, 'sound/machines/ping.ogg', 50, 0) - ready = FALSE - addtimer(CALLBACK(src, /obj/item/bodyanalyzer/.proc/setReady), scan_cd) - time_to_use = world.time + scan_cd - else - to_chat(user, "Scanning error detected. Invalid specimen.") - -//Unashamedly ripped from adv_med.dm -/obj/item/bodyanalyzer/proc/generate_printing_text(mob/living/M, mob/user) - var/dat = "" - var/mob/living/carbon/human/target = M - - dat = "Target Statistics:
    " - var/t1 - switch(target.stat) // obvious, see what their status is - if(CONSCIOUS) - t1 = "Conscious" - if(UNCONSCIOUS) - t1 = "Unconscious" - else - t1 = "*dead*" - dat += "[target.health > 50 ? "" : ""]\tHealth %: [target.health], ([t1])
    " - - var/found_disease = FALSE - for(var/thing in target.viruses) - var/datum/disease/D = thing - if(D.visibility_flags) //If any visibility flags are on. - continue - found_disease = TRUE - break - if(found_disease) - dat += "Disease detected in target.
    " - - var/extra_font = null - extra_font = (target.getBruteLoss() < 60 ? "" : "") - dat += "[extra_font]\t-Brute Damage %: [target.getBruteLoss()]
    " - - extra_font = (target.getOxyLoss() < 60 ? "" : "") - dat += "[extra_font]\t-Respiratory Damage %: [target.getOxyLoss()]
    " - - extra_font = (target.getToxLoss() < 60 ? "" : "") - dat += "[extra_font]\t-Toxin Content %: [target.getToxLoss()]
    " - - extra_font = (target.getFireLoss() < 60 ? "" : "") - dat += "[extra_font]\t-Burn Severity %: [target.getFireLoss()]
    " - - extra_font = (target.radiation < 10 ?"" : "") - dat += "[extra_font]\tRadiation Level %: [target.radiation]
    " - - extra_font = (target.getCloneLoss() < 1 ?"" : "") - dat += "[extra_font]\tGenetic Tissue Damage %: [target.getCloneLoss()]
    " - - extra_font = (target.getBrainLoss() < 1 ?"" : "") - dat += "[extra_font]\tApprox. Brain Damage %: [target.getBrainLoss()]
    " - - dat += "Paralysis Summary %: [target.paralysis] ([round(target.paralysis / 4)] seconds left!)
    " - dat += "Body Temperature: [target.bodytemperature-T0C]°C ([target.bodytemperature*1.8-459.67]°F)
    " - - dat += "
    " - - if(target.has_brain_worms()) - dat += "Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
    " - - var/blood_percent = round((target.blood_volume / BLOOD_VOLUME_NORMAL)) - blood_percent *= 100 - - extra_font = (target.blood_volume > 448 ? "" : "") - dat += "[extra_font]\tBlood Level %: [blood_percent] ([target.blood_volume] units)
    " - - if(target.reagents) - dat += "Epinephrine units: [target.reagents.get_reagent_amount("Epinephrine")] units
    " - dat += "Ether: [target.reagents.get_reagent_amount("ether")] units
    " - - extra_font = (target.reagents.get_reagent_amount("silver_sulfadiazine") < 30 ? "" : "") - dat += "[extra_font]\tSilver Sulfadiazine: [target.reagents.get_reagent_amount("silver_sulfadiazine")]
    " - - extra_font = (target.reagents.get_reagent_amount("styptic_powder") < 30 ? "" : "") - dat += "[extra_font]\tStyptic Powder: [target.reagents.get_reagent_amount("styptic_powder")] units
    " - - extra_font = (target.reagents.get_reagent_amount("salbutamol") < 30 ? "" : "") - dat += "[extra_font]\tSalbutamol: [target.reagents.get_reagent_amount("salbutamol")] units
    " - - dat += "
    " - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - - for(var/obj/item/organ/external/e in target.bodyparts) - dat += "" - var/AN = "" - var/open = "" - var/infected = "" - var/robot = "" - var/imp = "" - var/bled = "" - var/splint = "" - var/internal_bleeding = "" - var/lung_ruptured = "" - if(e.internal_bleeding) - internal_bleeding = "
    Internal bleeding" - if(istype(e, /obj/item/organ/external/chest) && target.is_lung_ruptured()) - lung_ruptured = "Lung ruptured:" - if(e.status & ORGAN_SPLINTED) - splint = "Splinted:" - if(e.status & ORGAN_BROKEN) - AN = "[e.broken_description]:" - if(e.is_robotic()) - robot = "Robotic:" - if(e.open) - open = "Open:" - switch(e.germ_level) - if(INFECTION_LEVEL_ONE to INFECTION_LEVEL_ONE + 200) - infected = "Mild Infection:" - if(INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300) - infected = "Mild Infection+:" - if(INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400) - infected = "Mild Infection++:" - if(INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200) - infected = "Acute Infection:" - if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) - infected = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 400) - infected = "Acute Infection++:" - if(INFECTION_LEVEL_THREE to INFINITY) - infected = "Septic:" - - var/unknown_body = 0 - for(var/I in e.embedded_objects) - unknown_body++ - - if(unknown_body || e.hidden) - imp += "Unknown body present:" - if(!AN && !open && !infected & !imp) - AN = "None:" - dat += "" - dat += "" - for(var/obj/item/organ/internal/i in target.internal_organs) - var/mech = i.desc - var/infection = "None" - switch(i.germ_level) - if(1 to INFECTION_LEVEL_ONE + 200) - infection = "Mild Infection:" - if(INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300) - infection = "Mild Infection+:" - if(INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400) - infection = "Mild Infection++:" - if(INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200) - infection = "Acute Infection:" - if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) - infection = "Acute Infection+:" - if(INFECTION_LEVEL_TWO + 300 to INFINITY) - infection = "Acute Infection++:" - - dat += "" - dat += "" - dat += "" - dat += "
    OrganBurn DamageBrute DamageOther Wounds
    [e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured]
    [i.name]N/A[i.damage][infection]:[mech]
    " - if(target.disabilities & BLIND) - dat += "Cataracts detected.
    " - if(target.disabilities & COLOURBLIND) - dat += "Photoreceptor abnormalities detected.
    " - if(target.disabilities & NEARSIGHTED) - dat += "Retinal misalignment detected.
    " - - return dat \ No newline at end of file +/* +CONTAINS: +T-RAY +DETECTIVE SCANNER +HEALTH ANALYZER +GAS ANALYZER +PLANT ANALYZER +REAGENT SCANNER +*/ +/obj/item/t_scanner + name = "T-ray scanner" + desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + icon = 'icons/obj/device.dmi' + icon_state = "t-ray0" + var/on = 0 + slot_flags = SLOT_BELT + w_class = 2 + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + materials = list(MAT_METAL=150) + origin_tech = "magnets=1;engineering=1" + var/scan_range = 1 + var/pulse_duration = 10 + +/obj/item/t_scanner/longer_pulse + pulse_duration = 50 + +/obj/item/t_scanner/extended_range + scan_range = 3 + +/obj/item/t_scanner/extended_range/longer_pulse + scan_range = 3 + pulse_duration = 50 + +/obj/item/t_scanner/Destroy() + if(on) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/t_scanner/attack_self(mob/user) + + on = !on + icon_state = copytext(icon_state, 1, length(icon_state))+"[on]" + + if(on) + START_PROCESSING(SSobj, src) + + +/obj/item/t_scanner/process() + if(!on) + STOP_PROCESSING(SSobj, src) + return null + scan() + +/obj/item/t_scanner/proc/scan() + + for(var/turf/T in range(scan_range, src.loc) ) + + if(!T.intact) + continue + + for(var/obj/O in T.contents) + + if(O.level != 1) + continue + + if(O.invisibility == 101) + O.invisibility = 0 + O.alpha = 128 + spawn(pulse_duration) + if(O) + var/turf/U = O.loc + if(U && U.intact) + O.invisibility = 101 + O.alpha = 255 + for(var/mob/living/M in T.contents) + var/oldalpha = M.alpha + if(M.alpha < 255 && istype(M)) + M.alpha = 255 + spawn(10) + if(M) + M.alpha = oldalpha + + var/mob/living/M = locate() in T + + if(M && M.invisibility == 2) + M.invisibility = 0 + spawn(2) + if(M) + M.invisibility = INVISIBILITY_LEVEL_TWO + + +/proc/chemscan(mob/living/user, mob/living/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.reagents) + if(H.reagents.reagent_list.len) + to_chat(user, "Subject contains the following reagents:") + for(var/datum/reagent/R in H.reagents.reagent_list) + to_chat(user, "[R.volume]u of [R.name][R.overdosed ? " - OVERDOSING" : ".
    "]") + else + to_chat(user, "Subject contains no reagents.") + if(H.reagents.addiction_list.len) + to_chat(user, "Subject is addicted to the following reagents:") + for(var/datum/reagent/R in H.reagents.addiction_list) + to_chat(user, "[R.name] Stage: [R.addiction_stage]/5") + else + to_chat(user, "Subject is not addicted to any reagents.") + +/obj/item/healthanalyzer + name = "Health Analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "health" + item_state = "healthanalyzer" + desc = "A hand-held body scanner able to distinguish vital signs of the subject." + flags = CONDUCT | NOBLUDGEON + slot_flags = SLOT_BELT + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=200) + origin_tech = "magnets=1;biotech=1" + var/mode = 1 + var/advanced = FALSE + +/obj/item/healthanalyzer/attack(mob/living/M, mob/living/user) + if(((CLUMSY in user.mutations) || user.getBrainLoss() >= 60) && prob(50)) + user.visible_message("[user] analyzes the floor's vitals!", "You stupidly try to analyze the floor's vitals!") + to_chat(user, "Analyzing results for The floor:\n\tOverall status: Healthy") + to_chat(user, "Key: Suffocation/Toxin/Burn/Brute") + to_chat(user, "\tDamage specifics: 0-0-0-0") + to_chat(user, "Body temperature: ???") + return + + user.visible_message("[user] analyzes [M]'s vitals.", "You analyze [M]'s vitals.") + + healthscan(user, M, mode, advanced) + + add_fingerprint(user) + +// Used by the PDA medical scanner too +/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE) + if(!ishuman(M) || M.isSynthetic()) + //these sensors are designed for organic life + to_chat(user, "Analyzing Results for ERROR:\n\t Overall Status: ERROR") + to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute") + to_chat(user, "\t Damage Specifics: ? - ? - ? - ?") + to_chat(user, "Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)") + to_chat(user, "Warning: Blood Level ERROR: --% --cl.Type: ERROR") + to_chat(user, "Subject's pulse: -- bpm.") + return + + var/mob/living/carbon/human/H = M + var/fake_oxy = max(rand(1,40), H.getOxyLoss(), (300 - (H.getToxLoss() + H.getFireLoss() + H.getBruteLoss()))) + var/OX = H.getOxyLoss() > 50 ? "[H.getOxyLoss()]" : H.getOxyLoss() + var/TX = H.getToxLoss() > 50 ? "[H.getToxLoss()]" : H.getToxLoss() + var/BU = H.getFireLoss() > 50 ? "[H.getFireLoss()]" : H.getFireLoss() + var/BR = H.getBruteLoss() > 50 ? "[H.getBruteLoss()]" : H.getBruteLoss() + if(H.status_flags & FAKEDEATH) + OX = fake_oxy > 50 ? "[fake_oxy]" : fake_oxy + to_chat(user, "Analyzing Results for [H]:\n\t Overall Status: dead") + else + to_chat(user, "Analyzing Results for [H]:\n\t Overall Status: [H.stat > 1 ? "dead" : "[H.health]% healthy"]") + to_chat(user, "\t Key: Suffocation/Toxin/Burns/Brute") + to_chat(user, "\t Damage Specifics: [OX] - [TX] - [BU] - [BR]") + to_chat(user, "Body Temperature: [H.bodytemperature-T0C]°C ([H.bodytemperature*1.8-459.67]°F)") + if(H.timeofdeath && (H.stat == DEAD || (H.status_flags & FAKEDEATH))) + to_chat(user, "Time of Death: [station_time_timestamp("hh:mm:ss", H.timeofdeath)]") + var/tdelta = round(world.time - H.timeofdeath) + if(tdelta < (DEFIB_TIME_LIMIT * 10)) + to_chat(user, "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!") + + if(mode == 1) + var/list/damaged = H.get_damaged_organs(1,1) + to_chat(user, "Localized Damage, Brute/Burn:") + if(length(damaged) > 0) + for(var/obj/item/organ/external/org in damaged) + to_chat(user, "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]") + + OX = H.getOxyLoss() > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal" + TX = H.getToxLoss() > 50 ? "Dangerous amount of toxins detected" : "Subject bloodstream toxin level minimal" + BU = H.getFireLoss() > 50 ? "Severe burn damage detected" : "Subject burn injury status O.K" + BR = H.getBruteLoss() > 50 ? "Severe anatomical damage detected" : "Subject brute-force injury status O.K" + if(H.status_flags & FAKEDEATH) + OX = fake_oxy > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal" + to_chat(user, "[OX] | [TX] | [BU] | [BR]") + + if(advanced) + chemscan(user, H) + for(var/thing in H.viruses) + var/datum/disease/D = thing + if(!(D.visibility_flags & HIDDEN_SCANNER)) + to_chat(user, "Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]") + if(H.undergoing_cardiac_arrest()) + var/obj/item/organ/internal/heart/heart = H.get_int_organ(/obj/item/organ/internal/heart) + if(heart && !(heart.status & ORGAN_DEAD)) + to_chat(user, "Warning: Medical Emergency detected\nName: Cardiac Arrest.\nType: The patient's heart has stopped.\nStage: 1/1.\nPossible Cure: Electric Shock") + else if(heart && (heart.status & ORGAN_DEAD)) + to_chat(user, "Subject's heart is necrotic.") + else if(!heart) + to_chat(user, "Subject has no heart.") + + if(H.getStaminaLoss()) + to_chat(user, "Subject appears to be suffering from fatigue.") + if(H.getCloneLoss()) + to_chat(user, "Subject appears to have [H.getCloneLoss() > 30 ? "severe" : "minor"] cellular damage.") + if(H.has_brain_worms()) + to_chat(user, "Subject suffering from aberrant brain activity. Recommend further scanning.") + + if(H.get_int_organ(/obj/item/organ/internal/brain)) + if(H.getBrainLoss() >= 100) + to_chat(user, "Subject is brain dead.") + else if(H.getBrainLoss() >= 60) + to_chat(user, "Severe brain damage detected. Subject likely to have mental retardation.") + else if(H.getBrainLoss() >= 10) + to_chat(user, "Significant brain damage detected. Subject may have had a concussion.") + else + to_chat(user, "Subject has no brain.") + + for(var/name in H.bodyparts_by_name) + var/obj/item/organ/external/e = H.bodyparts_by_name[name] + if(!e) + continue + var/limb = e.name + if(e.status & ORGAN_BROKEN) + if((e.limb_name in list("l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot")) && !(e.status & ORGAN_SPLINTED)) + to_chat(user, "Unsecured fracture in subject [limb]. Splinting recommended for transport.") + if(e.has_infected_wound()) + to_chat(user, "Infected wound detected in subject [limb]. Disinfection recommended.") + + for(var/name in H.bodyparts_by_name) + var/obj/item/organ/external/e = H.bodyparts_by_name[name] + if(!e) + continue + if(e.status & ORGAN_BROKEN) + to_chat(user, "Bone fractures detected. Advanced scanner required for location.") + break + for(var/obj/item/organ/external/e in H.bodyparts) + if(e.internal_bleeding) + to_chat(user, "Internal bleeding detected. Advanced scanner required for location.") + break + var/blood_id = H.get_blood_id() + if(blood_id) + if(H.bleed_rate) + to_chat(user, "Subject is bleeding!") + var/blood_percent = round((H.blood_volume / BLOOD_VOLUME_NORMAL)*100) + var/blood_type = H.dna.blood_type + if(blood_id != "blood")//special blood substance + var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] + if(R) + blood_type = R.name + else + blood_type = blood_id + if(H.blood_volume <= BLOOD_VOLUME_SAFE && H.blood_volume > BLOOD_VOLUME_OKAY) + to_chat(user, "LOW blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]") + else if(H.blood_volume <= BLOOD_VOLUME_OKAY) + to_chat(user, "CRITICAL blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]") + else + to_chat(user, "Blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]") + + to_chat(user, "Subject's pulse: [H.get_pulse(GETPULSE_TOOL)] bpm.") + var/implant_detect + for(var/obj/item/organ/internal/cyberimp/CI in H.internal_organs) + if(CI.is_robotic()) + implant_detect += "[H.name] is modified with a [CI.name].
    " + if(implant_detect) + to_chat(user, "Detected cybernetic modifications:") + to_chat(user, "[implant_detect]") + if(H.gene_stability < 40) + to_chat(user, "Subject's genes are quickly breaking down!") + else if(H.gene_stability < 70) + to_chat(user, "Subject's genes are showing signs of spontaneous breakdown.") + else if(H.gene_stability < 85) + to_chat(user, "Subject's genes are showing minor signs of instability.") + else + to_chat(user, "Subject's genes are stable.") + +/obj/item/healthanalyzer/attack_self(mob/user) + toggle_mode() + +/obj/item/healthanalyzer/verb/toggle_mode() + set name = "Switch Verbosity" + set category = "Object" + + mode = !mode + switch(mode) + if(1) + to_chat(usr, "The scanner now shows specific limb damage.") + if(0) + to_chat(usr, "The scanner no longer shows limb damage.") + +/obj/item/healthanalyzer/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/healthupgrade)) + if(advanced) + to_chat(user, "An upgrade is already installed on [src].") + else + if(user.unEquip(I)) + to_chat(user, "You install the upgrade on [src].") + add_overlay("advanced") + playsound(loc, I.usesound, 50, 1) + advanced = TRUE + qdel(I) + return + return ..() + +/obj/item/healthanalyzer/advanced + advanced = TRUE + +/obj/item/healthanalyzer/advanced/Initialize(mapload) + . = ..() + add_overlay("advanced") + + +/obj/item/healthupgrade + name = "Health Analyzer Upgrade" + icon = 'icons/obj/device.dmi' + icon_state = "healthupgrade" + desc = "An upgrade unit that can be installed on a health analyzer for expanded functionality." + w_class = WEIGHT_CLASS_TINY + origin_tech = "magnets=2;biotech=2" + usesound = 'sound/items/deconstruct.ogg' + +/obj/item/analyzer + desc = "A hand-held environmental scanner which reports current gas levels." + name = "analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "atmos" + item_state = "analyzer" + w_class = WEIGHT_CLASS_SMALL + flags = CONDUCT + slot_flags = SLOT_BELT + throwforce = 0 + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=30, MAT_GLASS=20) + origin_tech = "magnets=1;engineering=1" + var/cooldown = FALSE + var/cooldown_time = 250 + var/accuracy // 0 is the best accuracy. + +/obj/item/analyzer/examine(mob/user) + . = ..() + . += "Alt-click [src] to activate the barometer function." + +/obj/item/analyzer/attack_self(mob/user as mob) + + if(user.stat) + return + + var/turf/location = user.loc + if(!( istype(location, /turf) )) + return + + var/datum/gas_mixture/environment = location.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + to_chat(user, "Results:") + if(abs(pressure - ONE_ATMOSPHERE) < 10) + to_chat(user, "Pressure: [round(pressure,0.1)] kPa") + else + to_chat(user, "Pressure: [round(pressure,0.1)] kPa") + if(total_moles) + var/o2_concentration = environment.oxygen/total_moles + var/n2_concentration = environment.nitrogen/total_moles + var/co2_concentration = environment.carbon_dioxide/total_moles + var/plasma_concentration = environment.toxins/total_moles + + var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) + if(abs(n2_concentration - N2STANDARD) < 20) + to_chat(user, "Nitrogen: [round(n2_concentration*100)] %") + else + to_chat(user, "Nitrogen: [round(n2_concentration*100)] %") + + if(abs(o2_concentration - O2STANDARD) < 2) + to_chat(user, "Oxygen: [round(o2_concentration*100)] %") + else + to_chat(user, "Oxygen: [round(o2_concentration*100)] %") + + if(co2_concentration > 0.01) + to_chat(user, "CO2: [round(co2_concentration*100)] %") + else + to_chat(user, "CO2: [round(co2_concentration*100)] %") + + if(plasma_concentration > 0.01) + to_chat(user, "Plasma: [round(plasma_concentration*100)] %") + + if(unknown_concentration > 0.01) + to_chat(user, "Unknown: [round(unknown_concentration*100)] %") + + to_chat(user, "Temperature: [round(environment.temperature-T0C)] °C") + + add_fingerprint(user) + +/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens + ..() + + if(!user.incapacitated() && Adjacent(user)) + + if(cooldown) + to_chat(user, "[src]'s barometer function is prepraring itself.") + return + + var/turf/T = get_turf(user) + if(!T) + return + + playsound(src, 'sound/effects/pop.ogg', 100) + var/area/user_area = T.loc + var/datum/weather/ongoing_weather = null + + if(!user_area.outdoors) + to_chat(user, "[src]'s barometer function won't work indoors!") + return + + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) + ongoing_weather = W + break + + if(ongoing_weather) + if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) + to_chat(user, "[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]") + return + + to_chat(user, "The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].") + if(ongoing_weather.aesthetic) + to_chat(user, "[src]'s barometer function says that the next storm will breeze on by.") + else + var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] + var/fixed = next_hit ? next_hit - world.time : -1 + if(fixed < 0) + to_chat(user, "[src]'s barometer function was unable to trace any weather patterns.") + else + to_chat(user, "[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].") + cooldown = TRUE + addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) + +/obj/item/analyzer/proc/ping() + if(isliving(loc)) + var/mob/living/L = loc + to_chat(L, "[src]'s barometer function is ready!") + playsound(src, 'sound/machines/click.ogg', 100) + cooldown = FALSE + +/obj/item/analyzer/proc/butchertime(amount) + if(!amount) + return + if(accuracy) + var/inaccurate = round(accuracy * (1 / 3)) + if(prob(50)) + amount -= inaccurate + if(prob(50)) + amount += inaccurate + return DisplayTimeText(max(1, amount)) + +/obj/item/reagent_scanner + name = "reagent scanner" + desc = "A hand-held reagent scanner which identifies chemical agents and blood types." + icon = 'icons/obj/device.dmi' + icon_state = "spectrometer" + item_state = "analyzer" + w_class = WEIGHT_CLASS_SMALL + flags = CONDUCT + slot_flags = SLOT_BELT + throwforce = 5 + throw_speed = 4 + throw_range = 20 + materials = list(MAT_METAL=30, MAT_GLASS=20) + origin_tech = "magnets=2;biotech=1;plasmatech=2" + var/details = FALSE + var/datatoprint = "" + var/scanning = TRUE + actions_types = list(/datum/action/item_action/print_report) + +/obj/item/reagent_scanner/afterattack(obj/O, mob/user as mob) + if(user.stat) + return + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return + if(!istype(O)) + return + + if(!isnull(O.reagents)) + var/dat = "" + var/blood_type = "" + if(O.reagents.reagent_list.len > 0) + var/one_percent = O.reagents.total_volume / 100 + for(var/datum/reagent/R in O.reagents.reagent_list) + if(R.id != "blood") + dat += "
    [TAB][R][details ? ": [R.volume / one_percent]%" : ""]" + else + blood_type = R.data["blood_type"] + dat += "
    [TAB][R][blood_type ? " [blood_type]" : ""][details ? ": [R.volume / one_percent]%" : ""]" + if(dat) + to_chat(user, "Chemicals found: [dat]") + datatoprint = dat + scanning = FALSE + else + to_chat(user, "No active chemical agents found in [O].") + else + to_chat(user, "No significant chemical agents found in [O].") + return + +/obj/item/reagent_scanner/adv + name = "advanced reagent scanner" + icon_state = "adv_spectrometer" + details = TRUE + origin_tech = "magnets=4;biotech=3;plasmatech=3" + +/obj/item/reagent_scanner/proc/print_report() + if(!scanning) + usr.visible_message("[src] rattles and prints out a sheet of paper.") + playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) + sleep(50) + + var/obj/item/paper/P = new(get_turf(src)) + P.name = "Reagent Scanner Report: [station_time_timestamp()]" + P.info = "
    Reagent Scanner

    Data Analysis:



    Chemical agents detected:
    [datatoprint]

    " + + if(ismob(loc)) + var/mob/M = loc + M.put_in_hands(P) + to_chat(M, "Report printed. Log cleared.") + datatoprint = "" + scanning = TRUE + else + to_chat(usr, "[src] has no logs or is already in use.") + +/obj/item/reagent_scanner/ui_action_click() + print_report() + +/obj/item/slime_scanner + name = "slime scanner" + icon = 'icons/obj/device.dmi' + icon_state = "adv_spectrometer_s" + item_state = "analyzer" + origin_tech = "biotech=2" + w_class = WEIGHT_CLASS_SMALL + flags = CONDUCT + throwforce = 0 + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=30, MAT_GLASS=20) + +/obj/item/slime_scanner/attack(mob/living/M, mob/living/user) + if(user.incapacitated() || user.eye_blind) + return + if(!isslime(M)) + to_chat(user, "This device can only scan slimes!") + return + var/mob/living/simple_animal/slime/T = M + slime_scan(T, user) + +/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user) + to_chat(user, "========================") + to_chat(user, "Slime scan results:") + to_chat(user, "[T.colour] [T.is_adult ? "adult" : "baby"] slime") + to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]") + if(T.nutrition < T.get_starve_nutrition()) + to_chat(user, "Warning: slime is starving!") + else if(T.nutrition < T.get_hunger_nutrition()) + to_chat(user, "Warning: slime is hungry") + to_chat(user, "Electric change strength: [T.powerlevel]") + to_chat(user, "Health: [round(T.health/T.maxHealth,0.01)*100]%") + if(T.slime_mutation[4] == T.colour) + to_chat(user, "This slime does not evolve any further.") + else + if(T.slime_mutation[3] == T.slime_mutation[4]) + if(T.slime_mutation[2] == T.slime_mutation[1]) + to_chat(user, "Possible mutation: [T.slime_mutation[3]]") + to_chat(user, "Genetic destability: [T.mutation_chance/2] % chance of mutation on splitting") + else + to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)") + to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") + else + to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]") + to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") + if(T.cores > 1) + to_chat(user, "Multiple cores detected") + to_chat(user, "Growth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]") + if(T.effectmod) + to_chat(user, "Core mutation in progress: [T.effectmod]") + to_chat(user, "Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]") + to_chat(user, "========================") + +/obj/item/bodyanalyzer + name = "handheld body analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "bodyanalyzer_0" + item_state = "healthanalyser" + desc = "A handheld scanner capable of deep-scanning an entire body." + slot_flags = SLOT_BELT + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 5 + throw_range = 10 + origin_tech = "magnets=6;biotech=6" + var/obj/item/stock_parts/cell/cell + var/cell_type = /obj/item/stock_parts/cell/upgraded + var/ready = TRUE // Ready to scan + var/time_to_use = 0 // How much time remaining before next scan is available. + var/usecharge = 750 + var/scan_time = 10 SECONDS //how long does it take to scan + var/scan_cd = 60 SECONDS //how long before we can scan again + +/obj/item/bodyanalyzer/get_cell() + return cell + +/obj/item/bodyanalyzer/advanced + cell_type = /obj/item/stock_parts/cell/upgraded/plus + +/obj/item/bodyanalyzer/borg + name = "cyborg body analyzer" + desc = "Scan an entire body to prepare for field surgery. Consumes power for each scan." + +/obj/item/bodyanalyzer/borg/syndicate + scan_time = 5 SECONDS + scan_cd = 20 SECONDS + +/obj/item/bodyanalyzer/New() + ..() + cell = new cell_type(src) + cell.give(cell.maxcharge) + update_icon() + +/obj/item/bodyanalyzer/proc/setReady() + ready = TRUE + playsound(src, 'sound/machines/defib_saftyon.ogg', 50, 0) + update_icon() + +/obj/item/bodyanalyzer/update_icon(printing = FALSE) + overlays.Cut() + var/percent = cell.percent() + if(ready) + icon_state = "bodyanalyzer_1" + else + icon_state = "bodyanalyzer_2" + + var/overlayid = round(percent / 10) + overlayid = "bodyanalyzer_charge[overlayid]" + overlays += icon(icon, overlayid) + + if(printing) + overlays += icon(icon, "bodyanalyzer_printing") + +/obj/item/bodyanalyzer/attack(mob/living/M, mob/living/carbon/human/user) + if(user.incapacitated() || !user.Adjacent(M)) + return + + if(!ready) + to_chat(user, "The scanner beeps angrily at you! It's currently recharging - [round((time_to_use - world.time) * 0.1)] seconds remaining.") + playsound(user.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) + return + + if(cell.charge >= usecharge) + mobScan(M, user) + else + to_chat(user, "The scanner beeps angrily at you! It's out of charge!") + playsound(user.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) + +/obj/item/bodyanalyzer/borg/attack(mob/living/M, mob/living/silicon/robot/user) + if(user.incapacitated() || !user.Adjacent(M)) + return + + if(!ready) + to_chat(user, "[src] is currently recharging - [round((time_to_use - world.time) * 0.1)] seconds remaining.") + return + + if(user.cell.charge >= usecharge) + mobScan(M, user) + else + to_chat(user, "You need to recharge before you can use [src]") + +/obj/item/bodyanalyzer/proc/mobScan(mob/living/M, mob/user) + if(ishuman(M)) + var/report = generate_printing_text(M, user) + user.visible_message("[user] begins scanning [M] with [src].", "You begin scanning [M].") + if(do_after(user, scan_time, target = M)) + var/obj/item/paper/printout = new + printout.info = report + printout.name = "Scan report - [M.name]" + playsound(user.loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + user.put_in_hands(printout) + time_to_use = world.time + scan_cd + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + R.cell.use(usecharge) + else + cell.use(usecharge) + ready = FALSE + update_icon(TRUE) + addtimer(CALLBACK(src, /obj/item/bodyanalyzer/.proc/setReady), scan_cd) + addtimer(CALLBACK(src, /obj/item/bodyanalyzer/.proc/update_icon), 20) + + else if(iscorgi(M) && M.stat == DEAD) + to_chat(user, "You wonder if [M.p_they()] was a good dog. [src] tells you they were the best...") // :'( + playsound(loc, 'sound/machines/ping.ogg', 50, 0) + ready = FALSE + addtimer(CALLBACK(src, /obj/item/bodyanalyzer/.proc/setReady), scan_cd) + time_to_use = world.time + scan_cd + else + to_chat(user, "Scanning error detected. Invalid specimen.") + +//Unashamedly ripped from adv_med.dm +/obj/item/bodyanalyzer/proc/generate_printing_text(mob/living/M, mob/user) + var/dat = "" + var/mob/living/carbon/human/target = M + + dat = "Target Statistics:
    " + var/t1 + switch(target.stat) // obvious, see what their status is + if(CONSCIOUS) + t1 = "Conscious" + if(UNCONSCIOUS) + t1 = "Unconscious" + else + t1 = "*dead*" + dat += "[target.health > 50 ? "" : ""]\tHealth %: [target.health], ([t1])
    " + + var/found_disease = FALSE + for(var/thing in target.viruses) + var/datum/disease/D = thing + if(D.visibility_flags) //If any visibility flags are on. + continue + found_disease = TRUE + break + if(found_disease) + dat += "Disease detected in target.
    " + + var/extra_font = null + extra_font = (target.getBruteLoss() < 60 ? "" : "") + dat += "[extra_font]\t-Brute Damage %: [target.getBruteLoss()]
    " + + extra_font = (target.getOxyLoss() < 60 ? "" : "") + dat += "[extra_font]\t-Respiratory Damage %: [target.getOxyLoss()]
    " + + extra_font = (target.getToxLoss() < 60 ? "" : "") + dat += "[extra_font]\t-Toxin Content %: [target.getToxLoss()]
    " + + extra_font = (target.getFireLoss() < 60 ? "" : "") + dat += "[extra_font]\t-Burn Severity %: [target.getFireLoss()]
    " + + extra_font = (target.radiation < 10 ?"" : "") + dat += "[extra_font]\tRadiation Level %: [target.radiation]
    " + + extra_font = (target.getCloneLoss() < 1 ?"" : "") + dat += "[extra_font]\tGenetic Tissue Damage %: [target.getCloneLoss()]
    " + + extra_font = (target.getBrainLoss() < 1 ?"" : "") + dat += "[extra_font]\tApprox. Brain Damage %: [target.getBrainLoss()]
    " + + dat += "Paralysis Summary %: [target.paralysis] ([round(target.paralysis / 4)] seconds left!)
    " + dat += "Body Temperature: [target.bodytemperature-T0C]°C ([target.bodytemperature*1.8-459.67]°F)
    " + + dat += "
    " + + if(target.has_brain_worms()) + dat += "Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended.
    " + + var/blood_percent = round((target.blood_volume / BLOOD_VOLUME_NORMAL)) + blood_percent *= 100 + + extra_font = (target.blood_volume > 448 ? "" : "") + dat += "[extra_font]\tBlood Level %: [blood_percent] ([target.blood_volume] units)
    " + + if(target.reagents) + dat += "Epinephrine units: [target.reagents.get_reagent_amount("Epinephrine")] units
    " + dat += "Ether: [target.reagents.get_reagent_amount("ether")] units
    " + + extra_font = (target.reagents.get_reagent_amount("silver_sulfadiazine") < 30 ? "" : "") + dat += "[extra_font]\tSilver Sulfadiazine: [target.reagents.get_reagent_amount("silver_sulfadiazine")]
    " + + extra_font = (target.reagents.get_reagent_amount("styptic_powder") < 30 ? "" : "") + dat += "[extra_font]\tStyptic Powder: [target.reagents.get_reagent_amount("styptic_powder")] units
    " + + extra_font = (target.reagents.get_reagent_amount("salbutamol") < 30 ? "" : "") + dat += "[extra_font]\tSalbutamol: [target.reagents.get_reagent_amount("salbutamol")] units
    " + + dat += "
    " + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + + for(var/obj/item/organ/external/e in target.bodyparts) + dat += "" + var/AN = "" + var/open = "" + var/infected = "" + var/robot = "" + var/imp = "" + var/bled = "" + var/splint = "" + var/internal_bleeding = "" + var/lung_ruptured = "" + if(e.internal_bleeding) + internal_bleeding = "
    Internal bleeding" + if(istype(e, /obj/item/organ/external/chest) && target.is_lung_ruptured()) + lung_ruptured = "Lung ruptured:" + if(e.status & ORGAN_SPLINTED) + splint = "Splinted:" + if(e.status & ORGAN_BROKEN) + AN = "[e.broken_description]:" + if(e.is_robotic()) + robot = "Robotic:" + if(e.open) + open = "Open:" + switch(e.germ_level) + if(INFECTION_LEVEL_ONE to INFECTION_LEVEL_ONE + 200) + infected = "Mild Infection:" + if(INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300) + infected = "Mild Infection+:" + if(INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400) + infected = "Mild Infection++:" + if(INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200) + infected = "Acute Infection:" + if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) + infected = "Acute Infection+:" + if(INFECTION_LEVEL_TWO + 300 to INFECTION_LEVEL_TWO + 400) + infected = "Acute Infection++:" + if(INFECTION_LEVEL_THREE to INFINITY) + infected = "Septic:" + + var/unknown_body = 0 + for(var/I in e.embedded_objects) + unknown_body++ + + if(unknown_body || e.hidden) + imp += "Unknown body present:" + if(!AN && !open && !infected & !imp) + AN = "None:" + dat += "" + dat += "" + for(var/obj/item/organ/internal/i in target.internal_organs) + var/mech = i.desc + var/infection = "None" + switch(i.germ_level) + if(1 to INFECTION_LEVEL_ONE + 200) + infection = "Mild Infection:" + if(INFECTION_LEVEL_ONE + 200 to INFECTION_LEVEL_ONE + 300) + infection = "Mild Infection+:" + if(INFECTION_LEVEL_ONE + 300 to INFECTION_LEVEL_ONE + 400) + infection = "Mild Infection++:" + if(INFECTION_LEVEL_TWO to INFECTION_LEVEL_TWO + 200) + infection = "Acute Infection:" + if(INFECTION_LEVEL_TWO + 200 to INFECTION_LEVEL_TWO + 300) + infection = "Acute Infection+:" + if(INFECTION_LEVEL_TWO + 300 to INFINITY) + infection = "Acute Infection++:" + + dat += "" + dat += "" + dat += "" + dat += "
    OrganBurn DamageBrute DamageOther Wounds
    [e.name][e.burn_dam][e.brute_dam][robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured]
    [i.name]N/A[i.damage][infection]:[mech]
    " + if(target.disabilities & BLIND) + dat += "Cataracts detected.
    " + if(target.disabilities & COLOURBLIND) + dat += "Photoreceptor abnormalities detected.
    " + if(target.disabilities & NEARSIGHTED) + dat += "Retinal misalignment detected.
    " + + return dat diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index 12f5212ffd97..3eeb992c87e6 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -1,324 +1,322 @@ -/obj/item/taperecorder - name = "universal recorder" - desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." - icon = 'icons/obj/device.dmi' - icon_state = "taperecorder_empty" - item_state = "analyzer" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - materials = list(MAT_METAL=60, MAT_GLASS=30) - force = 2 - throwforce = 0 - var/recording = 0 - var/playing = 0 - var/playsleepseconds = 0 - var/obj/item/tape/mytape - var/open_panel = 0 - var/canprint = 1 - var/starts_with_tape = TRUE - - -/obj/item/taperecorder/New() - ..() - if(starts_with_tape) - mytape = new /obj/item/tape/random(src) - update_icon() - -/obj/item/taperecorder/Destroy() - QDEL_NULL(mytape) - return ..() - -/obj/item/taperecorder/examine(mob/user) - . = ..() - if(in_range(user, src)) - . += "The wire panel is [open_panel ? "opened" : "closed"]." - - -/obj/item/taperecorder/attackby(obj/item/I, mob/user) - if(!mytape && istype(I, /obj/item/tape)) - user.drop_item() - I.loc = src - mytape = I - to_chat(user, "You insert [I] into [src].") - update_icon() - -/obj/item/taperecorder/proc/eject(mob/user) - if(mytape) - to_chat(user, "You remove [mytape] from [src].") - stop() - user.put_in_hands(mytape) - mytape = null - update_icon() - - -/obj/item/taperecorder/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - mytape.ruin() //Fires destroy the tape - return ..() - -/obj/item/taperecorder/attack_hand(mob/user) - if(loc == user) - if(mytape) - if(user.l_hand != src && user.r_hand != src) - ..() - return - eject(user) - return - ..() - - -/obj/item/taperecorder/verb/ejectverb() - set name = "Eject Tape" - set category = "Object" - - if(usr.stat) - return - if(!mytape) - return - - eject(usr) - - -/obj/item/taperecorder/update_icon() - if(!mytape) - icon_state = "taperecorder_empty" - else if(recording) - icon_state = "taperecorder_recording" - else if(playing) - icon_state = "taperecorder_playing" - else - icon_state = "taperecorder_idle" - - -/obj/item/taperecorder/hear_talk(mob/living/M as mob, list/message_pieces) - var/msg = multilingual_to_message(message_pieces) - if(mytape && recording) - var/ending = copytext(msg, length(msg)) - mytape.timestamp += mytape.used_capacity - if(M.stuttering) - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] stammers, \"[msg]\"" - return - if(M.getBrainLoss() >= 60) - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] gibbers, \"[msg]\"" - return - if(ending == "?") - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] asks, \"[msg]\"" - return - else if(ending == "!") - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] exclaims, \"[msg]\"" - return - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] says, \"[msg]\"" - -/obj/item/taperecorder/hear_message(mob/living/M as mob, msg) - if(mytape && recording) - mytape.timestamp += mytape.used_capacity - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] [msg]" - -/obj/item/taperecorder/verb/record() - set name = "Start Recording" - set category = "Object" - - if(usr.stat) - return - if(!mytape || mytape.ruined) - return - if(recording) - return - if(playing) - return - - if(mytape.used_capacity < mytape.max_capacity) - to_chat(usr, "Recording started.") - recording = 1 - update_icon() - mytape.timestamp += mytape.used_capacity - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording started." - var/used = mytape.used_capacity //to stop runtimes when you eject the tape - var/max = mytape.max_capacity - for(used, used < max) - if(recording == 0) - break - mytape.used_capacity++ - used++ - sleep(10) - recording = 0 - update_icon() - else - to_chat(usr, "The tape is full.") - - -/obj/item/taperecorder/verb/stop() - set name = "Stop" - set category = "Object" - - if(usr.stat) - return - - if(recording) - recording = 0 - mytape.timestamp += mytape.used_capacity - mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording stopped." - to_chat(usr, "Recording stopped.") - return - else if(playing) - playing = 0 - atom_say("Playback stopped.") - update_icon() - - -/obj/item/taperecorder/verb/play() - set name = "Play Tape" - set category = "Object" - - if(usr.stat) - return - if(!mytape || mytape.ruined) - return - if(recording) - return - if(playing) - return - - playing = 1 - update_icon() - to_chat(usr, "Playing started.") - var/used = mytape.used_capacity //to stop runtimes when you eject the tape - var/max = mytape.max_capacity - for(var/i = 1, used < max, sleep(10 * playsleepseconds)) - if(!mytape) - break - if(playing == 0) - break - if(mytape.storedinfo.len < i) - break - atom_say("[mytape.storedinfo[i]]") - if(mytape.storedinfo.len < i + 1) - playsleepseconds = 1 - sleep(10) - T = get_turf(src) - atom_say("End of recording.") - else - playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i] - if(playsleepseconds > 14) - sleep(10) - T = get_turf(src) - atom_say("Skipping [playsleepseconds] seconds of silence.") - playsleepseconds = 1 - i++ - - playing = 0 - update_icon() - - -/obj/item/taperecorder/attack_self(mob/user) - if(!mytape || mytape.ruined) - return - if(recording) - stop() - else - record() - - -/obj/item/taperecorder/verb/print_transcript() - set name = "Print Transcript" - set category = "Object" - - if(usr.stat) - return - if(!mytape) - return - if(!canprint) - to_chat(usr, "The recorder can't print that fast!") - return - if(recording || playing) - return - - to_chat(usr, "Transcript printed.") - playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) - var/obj/item/paper/P = new /obj/item/paper(get_turf(src)) - var/t1 = "Transcript:

    " - for(var/i = 1, mytape.storedinfo.len >= i, i++) - t1 += "[mytape.storedinfo[i]]
    " - P.info = t1 - P.name = "paper- 'Transcript'" - usr.put_in_hands(P) - canprint = 0 - sleep(300) - canprint = 1 - -//empty tape recorders -/obj/item/taperecorder/empty - starts_with_tape = FALSE - - -/obj/item/tape - name = "tape" - desc = "A magnetic tape that can hold up to ten minutes of content." - icon = 'icons/obj/device.dmi' - icon_state = "tape_white" - item_state = "analyzer" - w_class = WEIGHT_CLASS_TINY - materials = list(MAT_METAL=20, MAT_GLASS=5) - force = 1 - throwforce = 0 - var/max_capacity = 600 - var/used_capacity = 0 - var/list/storedinfo = list() - var/list/timestamp = list() - var/ruined = 0 - -/obj/item/tape/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - ..() - ruin() - -/obj/item/tape/attack_self(mob/user) - if(!ruined) - to_chat(user, "You pull out all the tape!") - ruin() - -/obj/item/tape/verb/wipe() - set name = "Wipe Tape" - set category = "Object" - - if(usr.stat) - return - if(ruined) - return - - to_chat(usr, "You erase the data from the [src]") - clear() - -/obj/item/tape/proc/clear() - used_capacity = 0 - storedinfo.Cut() - timestamp.Cut() - -/obj/item/tape/proc/ruin() - if(!ruined) - overlays += "ribbonoverlay" - ruined = 1 - - - -/obj/item/tape/proc/fix() - overlays -= "ribbonoverlay" - ruined = 0 - - -/obj/item/tape/attackby(obj/item/I, mob/user) - if(ruined && istype(I, /obj/item/screwdriver)) - to_chat(user, "You start winding the tape back in.") - if(do_after(user, 120 * I.toolspeed, target = src)) - to_chat(user, "You wound the tape back in!") - fix() - else if(istype(I, /obj/item/pen)) - var/title = stripped_input(usr,"What do you want to name the tape?", "Tape Renaming", name, MAX_NAME_LEN) - if(!title || !length(title)) - name = initial(name) - return - name = "tape - [title]" - - -//Random colour tapes -/obj/item/tape/random/New() - ..() - icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" +/obj/item/taperecorder + name = "universal recorder" + desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback." + icon = 'icons/obj/device.dmi' + icon_state = "taperecorder_empty" + item_state = "analyzer" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + materials = list(MAT_METAL=60, MAT_GLASS=30) + force = 2 + throwforce = 0 + var/recording = 0 + var/playing = 0 + var/playsleepseconds = 0 + var/obj/item/tape/mytape + var/open_panel = 0 + var/canprint = 1 + var/starts_with_tape = TRUE + + +/obj/item/taperecorder/New() + ..() + if(starts_with_tape) + mytape = new /obj/item/tape/random(src) + update_icon() + +/obj/item/taperecorder/Destroy() + QDEL_NULL(mytape) + return ..() + +/obj/item/taperecorder/examine(mob/user) + . = ..() + if(in_range(user, src)) + . += "The wire panel is [open_panel ? "opened" : "closed"]." + + +/obj/item/taperecorder/attackby(obj/item/I, mob/user) + if(!mytape && istype(I, /obj/item/tape)) + user.drop_item() + I.loc = src + mytape = I + to_chat(user, "You insert [I] into [src].") + update_icon() + +/obj/item/taperecorder/proc/eject(mob/user) + if(mytape) + to_chat(user, "You remove [mytape] from [src].") + stop() + user.put_in_hands(mytape) + mytape = null + update_icon() + + +/obj/item/taperecorder/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + mytape.ruin() //Fires destroy the tape + return ..() + +/obj/item/taperecorder/attack_hand(mob/user) + if(loc == user) + if(mytape) + if(user.l_hand != src && user.r_hand != src) + ..() + return + eject(user) + return + ..() + + +/obj/item/taperecorder/verb/ejectverb() + set name = "Eject Tape" + set category = "Object" + + if(usr.stat) + return + if(!mytape) + return + + eject(usr) + + +/obj/item/taperecorder/update_icon() + if(!mytape) + icon_state = "taperecorder_empty" + else if(recording) + icon_state = "taperecorder_recording" + else if(playing) + icon_state = "taperecorder_playing" + else + icon_state = "taperecorder_idle" + + +/obj/item/taperecorder/hear_talk(mob/living/M as mob, list/message_pieces) + var/msg = multilingual_to_message(message_pieces) + if(mytape && recording) + var/ending = copytext(msg, length(msg)) + mytape.timestamp += mytape.used_capacity + if(M.stuttering) + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] stammers, \"[msg]\"" + return + if(M.getBrainLoss() >= 60) + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] gibbers, \"[msg]\"" + return + if(ending == "?") + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] asks, \"[msg]\"" + return + else if(ending == "!") + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] exclaims, \"[msg]\"" + return + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] says, \"[msg]\"" + +/obj/item/taperecorder/hear_message(mob/living/M as mob, msg) + if(mytape && recording) + mytape.timestamp += mytape.used_capacity + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [M.name] [msg]" + +/obj/item/taperecorder/verb/record() + set name = "Start Recording" + set category = "Object" + + if(usr.stat) + return + if(!mytape || mytape.ruined) + return + if(recording) + return + if(playing) + return + + if(mytape.used_capacity < mytape.max_capacity) + to_chat(usr, "Recording started.") + recording = 1 + update_icon() + mytape.timestamp += mytape.used_capacity + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording started." + var/used = mytape.used_capacity //to stop runtimes when you eject the tape + var/max = mytape.max_capacity + for(used, used < max) + if(recording == 0) + break + mytape.used_capacity++ + used++ + sleep(10) + recording = 0 + update_icon() + else + to_chat(usr, "The tape is full.") + + +/obj/item/taperecorder/verb/stop() + set name = "Stop" + set category = "Object" + + if(usr.stat) + return + + if(recording) + recording = 0 + mytape.timestamp += mytape.used_capacity + mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording stopped." + to_chat(usr, "Recording stopped.") + return + else if(playing) + playing = 0 + atom_say("Playback stopped.") + update_icon() + + +/obj/item/taperecorder/verb/play() + set name = "Play Tape" + set category = "Object" + + if(usr.stat) + return + if(!mytape || mytape.ruined) + return + if(recording) + return + if(playing) + return + + playing = 1 + update_icon() + to_chat(usr, "Playing started.") + var/used = mytape.used_capacity //to stop runtimes when you eject the tape + var/max = mytape.max_capacity + for(var/i = 1, used < max, sleep(10 * playsleepseconds)) + if(!mytape) + break + if(playing == 0) + break + if(mytape.storedinfo.len < i) + break + atom_say("[mytape.storedinfo[i]]") + if(mytape.storedinfo.len < i + 1) + playsleepseconds = 1 + sleep(10) + atom_say("End of recording.") + else + playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i] + if(playsleepseconds > 14) + sleep(10) + atom_say("Skipping [playsleepseconds] seconds of silence.") + playsleepseconds = 1 + i++ + + playing = 0 + update_icon() + + +/obj/item/taperecorder/attack_self(mob/user) + if(!mytape || mytape.ruined) + return + if(recording) + stop() + else + record() + + +/obj/item/taperecorder/verb/print_transcript() + set name = "Print Transcript" + set category = "Object" + + if(usr.stat) + return + if(!mytape) + return + if(!canprint) + to_chat(usr, "The recorder can't print that fast!") + return + if(recording || playing) + return + + to_chat(usr, "Transcript printed.") + playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) + var/obj/item/paper/P = new /obj/item/paper(get_turf(src)) + var/t1 = "Transcript:

    " + for(var/i = 1, mytape.storedinfo.len >= i, i++) + t1 += "[mytape.storedinfo[i]]
    " + P.info = t1 + P.name = "paper- 'Transcript'" + usr.put_in_hands(P) + canprint = 0 + sleep(300) + canprint = 1 + +//empty tape recorders +/obj/item/taperecorder/empty + starts_with_tape = FALSE + + +/obj/item/tape + name = "tape" + desc = "A magnetic tape that can hold up to ten minutes of content." + icon = 'icons/obj/device.dmi' + icon_state = "tape_white" + item_state = "analyzer" + w_class = WEIGHT_CLASS_TINY + materials = list(MAT_METAL=20, MAT_GLASS=5) + force = 1 + throwforce = 0 + var/max_capacity = 600 + var/used_capacity = 0 + var/list/storedinfo = list() + var/list/timestamp = list() + var/ruined = 0 + +/obj/item/tape/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + ..() + ruin() + +/obj/item/tape/attack_self(mob/user) + if(!ruined) + to_chat(user, "You pull out all the tape!") + ruin() + +/obj/item/tape/verb/wipe() + set name = "Wipe Tape" + set category = "Object" + + if(usr.stat) + return + if(ruined) + return + + to_chat(usr, "You erase the data from the [src]") + clear() + +/obj/item/tape/proc/clear() + used_capacity = 0 + storedinfo.Cut() + timestamp.Cut() + +/obj/item/tape/proc/ruin() + if(!ruined) + overlays += "ribbonoverlay" + ruined = 1 + + + +/obj/item/tape/proc/fix() + overlays -= "ribbonoverlay" + ruined = 0 + + +/obj/item/tape/attackby(obj/item/I, mob/user) + if(ruined && istype(I, /obj/item/screwdriver)) + to_chat(user, "You start winding the tape back in.") + if(do_after(user, 120 * I.toolspeed, target = src)) + to_chat(user, "You wound the tape back in!") + fix() + else if(istype(I, /obj/item/pen)) + var/title = stripped_input(usr,"What do you want to name the tape?", "Tape Renaming", name, MAX_NAME_LEN) + if(!title || !length(title)) + name = initial(name) + return + name = "tape - [title]" + + +//Random colour tapes +/obj/item/tape/random/New() + ..() + icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]" diff --git a/code/game/objects/items/devices/thermal_drill.dm b/code/game/objects/items/devices/thermal_drill.dm index c35a75ef0553..55dac21f348a 100644 --- a/code/game/objects/items/devices/thermal_drill.dm +++ b/code/game/objects/items/devices/thermal_drill.dm @@ -25,4 +25,4 @@ name = "diamond tipped thermal safe drill" desc = "A diamond tipped thermal drill with magnetic clamps for the purpose of quickly drilling hardened objects. Guaranteed 100% jam proof." icon_state = "diamond_drill" - time_multiplier = 0.5 \ No newline at end of file + time_multiplier = 0.5 diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index dfef93f2c325..1ffa9cceb881 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -167,4 +167,4 @@ effective or pretty fucking useless. if(active) GLOB.active_jammers |= src else - GLOB.active_jammers -= src \ No newline at end of file + GLOB.active_jammers -= src diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 371c6248a6e4..acba0087cafd 100644 --- a/code/game/objects/items/devices/transfer_valve.dm +++ b/code/game/objects/items/devices/transfer_valve.dm @@ -1,220 +1,220 @@ -/obj/item/transfer_valve - icon = 'icons/obj/assemblies.dmi' - name = "tank transfer valve" - icon_state = "valve_1" - item_state = "ttv" - desc = "Regulates the transfer of air between two tanks" - var/obj/item/tank/tank_one = null - var/obj/item/tank/tank_two = null - var/obj/item/assembly/attached_device = null - var/mob/living/attacher = null - var/valve_open = 0 - var/toggle = 1 - origin_tech = "materials=1;engineering=1" - -/obj/item/transfer_valve/Destroy() - QDEL_NULL(tank_one) - QDEL_NULL(tank_two) - QDEL_NULL(attached_device) - attacher = null - return ..() - -/obj/item/transfer_valve/IsAssemblyHolder() - return 1 - -/obj/item/transfer_valve/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/tank)) - if(tank_one && tank_two) - to_chat(user, "There are already two tanks attached, remove one first.") - return - - if(!tank_one) - if(!user.unEquip(I)) - return - tank_one = I - I.forceMove(src) - to_chat(user, "You attach the tank to the transfer valve.") - if(I.w_class > w_class) - w_class = I.w_class - else if(!tank_two) - if(!user.unEquip(I)) - return - tank_two = I - I.forceMove(src) - to_chat(user, "You attach the tank to the transfer valve.") - if(I.w_class > w_class) - w_class = I.w_class - - update_icon() - SSnanoui.update_uis(src) // update all UIs attached to src -//TODO: Have this take an assemblyholder - else if(isassembly(I)) - var/obj/item/assembly/A = I - if(A.secured) - to_chat(user, "The device is secured.") - return - if(attached_device) - to_chat(user, "There is already a device attached to the valve, remove it first.") - return - user.remove_from_mob(A) - attached_device = A - A.forceMove(src) - to_chat(user, "You attach the [A] to the valve controls and secure it.") - A.holder = src - A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). - - investigate_log("[key_name(user)] attached a [A] to a transfer valve.", INVESTIGATE_BOMB) - msg_admin_attack("[key_name_admin(user)]attached [A] to a transfer valve.", ATKLOG_FEW) - log_game("[key_name_admin(user)] attached [A] to a transfer valve.") - attacher = user - SSnanoui.update_uis(src) // update all UIs attached to src - - -/obj/item/transfer_valve/HasProximity(atom/movable/AM) - if(!attached_device) - return - attached_device.HasProximity(AM) - -/obj/item/transfer_valve/hear_talk(mob/living/M, list/message_pieces) - ..() - for(var/obj/O in contents) - O.hear_talk(M, message_pieces) - -/obj/item/transfer_valve/hear_message(mob/living/M, msg) - ..() - for(var/obj/O in contents) - O.hear_message(M, msg) - -/obj/item/transfer_valve/attack_self(mob/user) - ui_interact(user) - -/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "transfer_valve.tmpl", "Tank Transfer Valve", 460, 280) - // open the new ui window - ui.open() - // auto update every Master Controller tick - //ui.set_auto_update(1) - -/obj/item/transfer_valve/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["attachmentOne"] = tank_one ? tank_one.name : null - data["attachmentTwo"] = tank_two ? tank_two.name : null - data["valveAttachment"] = attached_device ? attached_device.name : null - data["valveOpen"] = valve_open ? 1 : 0 - - return data - -/obj/item/transfer_valve/Topic(href, href_list) - ..() - if(usr.incapacitated()) - return 0 - if(loc != usr) - return 0 - if(tank_one && href_list["tankone"]) - split_gases() - valve_open = 0 - tank_one.forceMove(get_turf(src)) - tank_one = null - update_icon() - if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) - w_class = WEIGHT_CLASS_NORMAL - else if(tank_two && href_list["tanktwo"]) - split_gases() - valve_open = 0 - tank_two.forceMove(get_turf(src)) - tank_two = null - update_icon() - if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) - w_class = WEIGHT_CLASS_NORMAL - else if(href_list["open"]) - toggle_valve() - else if(attached_device) - if(href_list["rem_device"]) - attached_device.forceMove(get_turf(src)) - attached_device.holder = null - attached_device = null - update_icon() - if(href_list["device"]) - attached_device.attack_self(usr) - add_fingerprint(usr) - return 1 // Returning 1 sends an update to attached UIs - -/obj/item/transfer_valve/proc/process_activation(obj/item/D) - if(toggle) - toggle = 0 - toggle_valve() - spawn(50) // To stop a signal being spammed from a proxy sensor constantly going off or whatever - toggle = 1 - -/obj/item/transfer_valve/update_icon() - overlays.Cut() - underlays = null - - if(!tank_one && !tank_two && !attached_device) - icon_state = "valve_1" - return - icon_state = "valve" - - if(tank_one) - overlays += "[tank_one.icon_state]" - if(tank_two) - var/icon/J = new(icon, icon_state = "[tank_two.icon_state]") - J.Shift(WEST, 13) - underlays += J - if(attached_device) - overlays += "device" - -/obj/item/transfer_valve/proc/merge_gases() - tank_two.air_contents.volume += tank_one.air_contents.volume - var/datum/gas_mixture/temp - temp = tank_one.air_contents.remove_ratio(1) - tank_two.air_contents.merge(temp) - -/obj/item/transfer_valve/proc/split_gases() - if(!valve_open || !tank_one || !tank_two) - return - var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume - var/datum/gas_mixture/temp - temp = tank_two.air_contents.remove_ratio(ratio1) - tank_one.air_contents.merge(temp) - tank_two.air_contents.volume -= tank_one.air_contents.volume - - /* - Exadv1: I know this isn't how it's going to work, but this was just to check - it explodes properly when it gets a signal (and it does). - */ - -/obj/item/transfer_valve/proc/toggle_valve() - if(!valve_open && tank_one && tank_two) - valve_open = 1 - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - - var/attacher_name = "" - if(!attacher) - attacher_name = "Unknown" - else - attacher_name = "[key_name_admin(attacher)]" - - var/mob/mob = get_mob_by_key(src.fingerprintslast) - - investigate_log("Bomb valve opened at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]", INVESTIGATE_BOMB) - message_admins("Bomb valve opened at [A.name] (JMP) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name_admin(mob)]") - log_game("Bomb valve opened at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]") - merge_gases() - spawn(20) // In case one tank bursts - for(var/i in 1 to 5) - update_icon() - sleep(10) - update_icon() - - else if(valve_open && tank_one && tank_two) - split_gases() - valve_open = 0 - update_icon() +/obj/item/transfer_valve + icon = 'icons/obj/assemblies.dmi' + name = "tank transfer valve" + icon_state = "valve_1" + item_state = "ttv" + desc = "Regulates the transfer of air between two tanks" + var/obj/item/tank/tank_one = null + var/obj/item/tank/tank_two = null + var/obj/item/assembly/attached_device = null + var/mob/living/attacher = null + var/valve_open = 0 + var/toggle = 1 + origin_tech = "materials=1;engineering=1" + +/obj/item/transfer_valve/Destroy() + QDEL_NULL(tank_one) + QDEL_NULL(tank_two) + QDEL_NULL(attached_device) + attacher = null + return ..() + +/obj/item/transfer_valve/IsAssemblyHolder() + return 1 + +/obj/item/transfer_valve/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/tank)) + if(tank_one && tank_two) + to_chat(user, "There are already two tanks attached, remove one first.") + return + + if(!tank_one) + if(!user.unEquip(I)) + return + tank_one = I + I.forceMove(src) + to_chat(user, "You attach the tank to the transfer valve.") + if(I.w_class > w_class) + w_class = I.w_class + else if(!tank_two) + if(!user.unEquip(I)) + return + tank_two = I + I.forceMove(src) + to_chat(user, "You attach the tank to the transfer valve.") + if(I.w_class > w_class) + w_class = I.w_class + + update_icon() + SSnanoui.update_uis(src) // update all UIs attached to src +//TODO: Have this take an assemblyholder + else if(isassembly(I)) + var/obj/item/assembly/A = I + if(A.secured) + to_chat(user, "The device is secured.") + return + if(attached_device) + to_chat(user, "There is already a device attached to the valve, remove it first.") + return + user.remove_from_mob(A) + attached_device = A + A.forceMove(src) + to_chat(user, "You attach the [A] to the valve controls and secure it.") + A.holder = src + A.toggle_secure() //this calls update_icon(), which calls update_icon() on the holder (i.e. the bomb). + + investigate_log("[key_name(user)] attached a [A] to a transfer valve.", INVESTIGATE_BOMB) + msg_admin_attack("[key_name_admin(user)]attached [A] to a transfer valve.", ATKLOG_FEW) + log_game("[key_name_admin(user)] attached [A] to a transfer valve.") + attacher = user + SSnanoui.update_uis(src) // update all UIs attached to src + + +/obj/item/transfer_valve/HasProximity(atom/movable/AM) + if(!attached_device) + return + attached_device.HasProximity(AM) + +/obj/item/transfer_valve/hear_talk(mob/living/M, list/message_pieces) + ..() + for(var/obj/O in contents) + O.hear_talk(M, message_pieces) + +/obj/item/transfer_valve/hear_message(mob/living/M, msg) + ..() + for(var/obj/O in contents) + O.hear_message(M, msg) + +/obj/item/transfer_valve/attack_self(mob/user) + ui_interact(user) + +/obj/item/transfer_valve/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "transfer_valve.tmpl", "Tank Transfer Valve", 460, 280) + // open the new ui window + ui.open() + // auto update every Master Controller tick + //ui.set_auto_update(1) + +/obj/item/transfer_valve/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["attachmentOne"] = tank_one ? tank_one.name : null + data["attachmentTwo"] = tank_two ? tank_two.name : null + data["valveAttachment"] = attached_device ? attached_device.name : null + data["valveOpen"] = valve_open ? 1 : 0 + + return data + +/obj/item/transfer_valve/Topic(href, href_list) + ..() + if(usr.incapacitated()) + return 0 + if(loc != usr) + return 0 + if(tank_one && href_list["tankone"]) + split_gases() + valve_open = 0 + tank_one.forceMove(get_turf(src)) + tank_one = null + update_icon() + if((!tank_two || tank_two.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) + w_class = WEIGHT_CLASS_NORMAL + else if(tank_two && href_list["tanktwo"]) + split_gases() + valve_open = 0 + tank_two.forceMove(get_turf(src)) + tank_two = null + update_icon() + if((!tank_one || tank_one.w_class < WEIGHT_CLASS_BULKY) && (w_class > WEIGHT_CLASS_NORMAL)) + w_class = WEIGHT_CLASS_NORMAL + else if(href_list["open"]) + toggle_valve() + else if(attached_device) + if(href_list["rem_device"]) + attached_device.forceMove(get_turf(src)) + attached_device.holder = null + attached_device = null + update_icon() + if(href_list["device"]) + attached_device.attack_self(usr) + add_fingerprint(usr) + return 1 // Returning 1 sends an update to attached UIs + +/obj/item/transfer_valve/proc/process_activation(obj/item/D) + if(toggle) + toggle = 0 + toggle_valve() + spawn(50) // To stop a signal being spammed from a proxy sensor constantly going off or whatever + toggle = 1 + +/obj/item/transfer_valve/update_icon() + overlays.Cut() + underlays = null + + if(!tank_one && !tank_two && !attached_device) + icon_state = "valve_1" + return + icon_state = "valve" + + if(tank_one) + overlays += "[tank_one.icon_state]" + if(tank_two) + var/icon/J = new(icon, icon_state = "[tank_two.icon_state]") + J.Shift(WEST, 13) + underlays += J + if(attached_device) + overlays += "device" + +/obj/item/transfer_valve/proc/merge_gases() + tank_two.air_contents.volume += tank_one.air_contents.volume + var/datum/gas_mixture/temp + temp = tank_one.air_contents.remove_ratio(1) + tank_two.air_contents.merge(temp) + +/obj/item/transfer_valve/proc/split_gases() + if(!valve_open || !tank_one || !tank_two) + return + var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume + var/datum/gas_mixture/temp + temp = tank_two.air_contents.remove_ratio(ratio1) + tank_one.air_contents.merge(temp) + tank_two.air_contents.volume -= tank_one.air_contents.volume + + /* + Exadv1: I know this isn't how it's going to work, but this was just to check + it explodes properly when it gets a signal (and it does). + */ + +/obj/item/transfer_valve/proc/toggle_valve() + if(!valve_open && tank_one && tank_two) + valve_open = 1 + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + + var/attacher_name = "" + if(!attacher) + attacher_name = "Unknown" + else + attacher_name = "[key_name_admin(attacher)]" + + var/mob/mob = get_mob_by_key(src.fingerprintslast) + + investigate_log("Bomb valve opened at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]", INVESTIGATE_BOMB) + message_admins("Bomb valve opened at [A.name] (JMP) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name_admin(mob)]") + log_game("Bomb valve opened at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with [attached_device ? attached_device : "no device"], attached by [attacher_name]. Last touched by: [key_name(mob)]") + merge_gases() + spawn(20) // In case one tank bursts + for(var/i in 1 to 5) + update_icon() + sleep(10) + update_icon() + + else if(valve_open && tank_one && tank_two) + split_gases() + valve_open = 0 + update_icon() diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm index d814b242b298..4aa5c4532f1b 100644 --- a/code/game/objects/items/devices/uplinks.dm +++ b/code/game/objects/items/devices/uplinks.dm @@ -6,7 +6,7 @@ A list of items and costs is stored under the datum of every game mode, alongsid */ -var/list/world_uplinks = list() +GLOBAL_LIST_EMPTY(world_uplinks) /obj/item/uplink var/welcome // Welcoming menu message @@ -35,10 +35,10 @@ var/list/world_uplinks = list() uses = SSticker.mode.uplink_uses uplink_items = get_uplink_items() - world_uplinks += src + GLOB.world_uplinks += src /obj/item/uplink/Destroy() - world_uplinks -= src + GLOB.world_uplinks -= src return ..() /obj/item/uplink/proc/generate_items(mob/user as mob) @@ -216,7 +216,7 @@ var/list/world_uplinks = list() /* NANO UI FOR UPLINK WOOP WOOP */ -/obj/item/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) +/obj/item/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) var/title = "Remote Uplink" // update the ui if it exists, returns null if no ui is passed/found ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) @@ -227,7 +227,7 @@ var/list/world_uplinks = list() // open the new ui window ui.open() -/obj/item/uplink/hidden/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) +/obj/item/uplink/hidden/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) var/data[0] data["welcome"] = welcome @@ -277,14 +277,14 @@ var/list/world_uplinks = list() /obj/item/uplink/hidden/proc/update_nano_data(var/id) if(nanoui_menu == 1) var/permanentData[0] - for(var/datum/data/record/L in sortRecord(data_core.general)) + for(var/datum/data/record/L in sortRecord(GLOB.data_core.general)) permanentData[++permanentData.len] = list(Name = sanitize(L.fields["name"]),"id" = L.fields["id"]) nanoui_data["exploit_records"] = permanentData if(nanoui_menu == 11) nanoui_data["exploit_exists"] = 0 - for(var/datum/data/record/L in data_core.general) + for(var/datum/data/record/L in GLOB.data_core.general) if(L.fields["id"] == id) nanoui_data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value. nanoui_data["exploit"]["name"] = html_encode(L.fields["name"]) @@ -353,4 +353,4 @@ var/list/world_uplinks = list() /obj/item/radio/headset/uplink/New() ..() hidden_uplink = new(src) - hidden_uplink.uses = 20 \ No newline at end of file + hidden_uplink.uses = 20 diff --git a/code/game/objects/items/devices/voice.dm b/code/game/objects/items/devices/voice.dm index 38bcd16f01ee..43a95ae86d77 100644 --- a/code/game/objects/items/devices/voice.dm +++ b/code/game/objects/items/devices/voice.dm @@ -41,4 +41,4 @@ return voice = sanitize(copytext(chosen_voice, 1, MAX_MESSAGE_LEN)) - to_chat(user, "You are now mimicking [voice].") \ No newline at end of file + to_chat(user, "You are now mimicking [voice].") diff --git a/code/game/objects/items/documents.dm b/code/game/objects/items/documents.dm index 2bf519b4a223..e19561444e3b 100644 --- a/code/game/objects/items/documents.dm +++ b/code/game/objects/items/documents.dm @@ -52,4 +52,4 @@ H.reagents.add_reagent(poison_type, poison_dose) poison_total -= poison_dose add_attack_logs(src, user, "Picked up [src], the trapped syndicate documents") - return ..() \ No newline at end of file + return ..() diff --git a/code/game/objects/items/flag.dm b/code/game/objects/items/flag.dm index aed33fc2819b..0edcafd64b52 100644 --- a/code/game/objects/items/flag.dm +++ b/code/game/objects/items/flag.dm @@ -275,4 +275,4 @@ /obj/item/flag/chameleon/depot/New() ..() - boobytrap = new /obj/item/grenade/gas/plasma(src) \ No newline at end of file + boobytrap = new /obj/item/grenade/gas/plasma(src) diff --git a/code/game/objects/items/latexballoon.dm b/code/game/objects/items/latexballoon.dm index 9be42813e095..1aded3ac4bc3 100644 --- a/code/game/objects/items/latexballoon.dm +++ b/code/game/objects/items/latexballoon.dm @@ -1,65 +1,65 @@ -/obj/item/latexballon - name = "latex glove" - desc = "" //todo - icon_state = "latexballon" - item_state = "lgloves" - force = 0 - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 1 - throw_range = 7 - var/state - var/datum/gas_mixture/air_contents = null - -/obj/item/latexballon/Destroy() - QDEL_NULL(air_contents) - return ..() - -/obj/item/latexballon/proc/blow(obj/item/tank/tank, mob/user) - if(icon_state == "latexballon_bursted") - return - icon_state = "latexballon_blow" - item_state = "latexballon" - user.update_inv_r_hand() - user.update_inv_l_hand() - to_chat(user, "You blow up [src] with [tank].") - air_contents = tank.remove_air_volume(3) - -/obj/item/latexballon/proc/burst() - if(!air_contents || icon_state != "latexballon_blow") - return - playsound(src, 'sound/weapons/gunshots/gunshot.ogg', 100, 1) - icon_state = "latexballon_bursted" - item_state = "lgloves" - if(isliving(loc)) - var/mob/living/user = loc - user.update_inv_r_hand() - user.update_inv_l_hand() - loc.assume_air(air_contents) - -/obj/item/latexballon/ex_act(severity) - burst() - switch(severity) - if (1) - qdel(src) - if (2) - if (prob(50)) - qdel(src) - -/obj/item/latexballon/bullet_act(obj/item/projectile/P) - if(!P.nodamage) - burst() - return ..() - -/obj/item/latexballon/temperature_expose(datum/gas_mixture/air, temperature, volume) - ..() - if(temperature > T0C+100) - burst() - -/obj/item/latexballon/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/tank)) - var/obj/item/tank/T = W - blow(T, user) - return - if(is_sharp(W) || is_hot(W) || is_pointed(W)) - burst() +/obj/item/latexballon + name = "latex glove" + desc = "" //todo + icon_state = "latexballon" + item_state = "lgloves" + force = 0 + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 1 + throw_range = 7 + var/state + var/datum/gas_mixture/air_contents = null + +/obj/item/latexballon/Destroy() + QDEL_NULL(air_contents) + return ..() + +/obj/item/latexballon/proc/blow(obj/item/tank/tank, mob/user) + if(icon_state == "latexballon_bursted") + return + icon_state = "latexballon_blow" + item_state = "latexballon" + user.update_inv_r_hand() + user.update_inv_l_hand() + to_chat(user, "You blow up [src] with [tank].") + air_contents = tank.remove_air_volume(3) + +/obj/item/latexballon/proc/burst() + if(!air_contents || icon_state != "latexballon_blow") + return + playsound(src, 'sound/weapons/gunshots/gunshot.ogg', 100, 1) + icon_state = "latexballon_bursted" + item_state = "lgloves" + if(isliving(loc)) + var/mob/living/user = loc + user.update_inv_r_hand() + user.update_inv_l_hand() + loc.assume_air(air_contents) + +/obj/item/latexballon/ex_act(severity) + burst() + switch(severity) + if (1) + qdel(src) + if (2) + if (prob(50)) + qdel(src) + +/obj/item/latexballon/bullet_act(obj/item/projectile/P) + if(!P.nodamage) + burst() + return ..() + +/obj/item/latexballon/temperature_expose(datum/gas_mixture/air, temperature, volume) + ..() + if(temperature > T0C+100) + burst() + +/obj/item/latexballon/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/tank)) + var/obj/item/tank/T = W + blow(T, user) + return + if(is_sharp(W) || is_hot(W) || is_pointed(W)) + burst() diff --git a/code/game/objects/items/mixing_bowl.dm b/code/game/objects/items/mixing_bowl.dm index 1edfdac95616..da851437fd55 100644 --- a/code/game/objects/items/mixing_bowl.dm +++ b/code/game/objects/items/mixing_bowl.dm @@ -167,4 +167,4 @@ var/obj/item/reagent_containers/food/snacks/badrecipe/ffuu = new(get_turf(source)) ffuu.reagents.add_reagent("carbon", amount) ffuu.reagents.add_reagent("????", amount/10) - make_dirty(75) \ No newline at end of file + make_dirty(75) diff --git a/code/game/objects/items/mountable_frames/air_alarm.dm b/code/game/objects/items/mountable_frames/air_alarm.dm index 4b16db0b0f67..432b41693552 100644 --- a/code/game/objects/items/mountable_frames/air_alarm.dm +++ b/code/game/objects/items/mountable_frames/air_alarm.dm @@ -13,4 +13,4 @@ Code shamelessly copied from apc_frame /obj/item/mounted/frame/alarm_frame/do_build(turf/on_wall, mob/user) new /obj/machinery/alarm(get_turf(src), get_dir(on_wall, user), 1) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/mountable_frames/apc_frame.dm b/code/game/objects/items/mountable_frames/apc_frame.dm index f6707775fb2a..548b77b85fec 100644 --- a/code/game/objects/items/mountable_frames/apc_frame.dm +++ b/code/game/objects/items/mountable_frames/apc_frame.dm @@ -29,4 +29,4 @@ /obj/item/mounted/frame/apc_frame/do_build(turf/on_wall, mob/user) new /obj/machinery/power/apc(get_turf(src), get_dir(user, on_wall), 1) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/mountable_frames/buttons_switches.dm b/code/game/objects/items/mountable_frames/buttons_switches.dm index b1e3fabbb867..76e4f0ecfd4f 100644 --- a/code/game/objects/items/mountable_frames/buttons_switches.dm +++ b/code/game/objects/items/mountable_frames/buttons_switches.dm @@ -20,4 +20,4 @@ /obj/item/mounted/frame/light_switch/do_build(turf/on_wall, mob/user) new /obj/machinery/light_switch(get_turf(user), get_dir(user, on_wall)) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/mountable_frames/fire_alarm.dm b/code/game/objects/items/mountable_frames/fire_alarm.dm index ceec9b7c716b..83f435a277c8 100644 --- a/code/game/objects/items/mountable_frames/fire_alarm.dm +++ b/code/game/objects/items/mountable_frames/fire_alarm.dm @@ -7,4 +7,4 @@ /obj/item/mounted/frame/firealarm/do_build(turf/on_wall, mob/user) new /obj/machinery/firealarm(get_turf(src), get_dir(on_wall, user), 1) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/mountable_frames/intercom.dm b/code/game/objects/items/mountable_frames/intercom.dm index 18e25ebcbfbd..95f21b5c2c9c 100644 --- a/code/game/objects/items/mountable_frames/intercom.dm +++ b/code/game/objects/items/mountable_frames/intercom.dm @@ -7,4 +7,4 @@ /obj/item/mounted/frame/intercom/do_build(turf/on_wall, mob/user) new /obj/item/radio/intercom(get_turf(src), get_dir(user, on_wall), 0) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/mountable_frames/lights.dm b/code/game/objects/items/mountable_frames/lights.dm index b108240b3321..d7954fd27046 100644 --- a/code/game/objects/items/mountable_frames/lights.dm +++ b/code/game/objects/items/mountable_frames/lights.dm @@ -36,4 +36,4 @@ icon = 'icons/obj/lighting.dmi' icon_state = "bulb-construct-item" fixture_type = "bulb" - sheets_refunded = 1 \ No newline at end of file + sheets_refunded = 1 diff --git a/code/game/objects/items/mountable_frames/mountables.dm b/code/game/objects/items/mountable_frames/mountables.dm index 7120f8c880d2..cfb4d18473c1 100644 --- a/code/game/objects/items/mountable_frames/mountables.dm +++ b/code/game/objects/items/mountable_frames/mountables.dm @@ -20,7 +20,7 @@ return if(proximity_flag != 1) //if we aren't next to the wall return - if(!( get_dir(on_wall,user) in cardinal)) + if(!( get_dir(on_wall,user) in GLOB.cardinal)) to_chat(user, "You need to be standing next to a wall to place \the [src].") return diff --git a/code/game/objects/items/mountable_frames/newscaster_frame.dm b/code/game/objects/items/mountable_frames/newscaster_frame.dm index 8ed81d937d5b..760e5c6147b4 100644 --- a/code/game/objects/items/mountable_frames/newscaster_frame.dm +++ b/code/game/objects/items/mountable_frames/newscaster_frame.dm @@ -28,4 +28,4 @@ var/obj/machinery/newscaster/N = new /obj/machinery/newscaster(get_turf(src), get_dir(on_wall, user), 1) N.pixel_y -= (loc.y - on_wall.y) * 32 N.pixel_x -= (loc.x - on_wall.x) * 32 - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/random_items.dm b/code/game/objects/items/random_items.dm index 78c66a1ee366..f90f40dee57b 100644 --- a/code/game/objects/items/random_items.dm +++ b/code/game/objects/items/random_items.dm @@ -321,4 +321,4 @@ ..() for(var/i in 1 to 6) var/nade = pick(grenadelist) - new nade(src) \ No newline at end of file + new nade(src) diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index 1b20716783a2..eae59151a382 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -1,37 +1,37 @@ -/********************************************************************** - Cyborg Spec Items -***********************************************************************/ -/obj/item/borg - icon = 'icons/mob/robot_items.dmi' - -/obj/item/borg/stun - name = "electrically-charged arm" - icon_state = "elecarm" - var/charge_cost = 30 - -/obj/item/borg/stun/attack(mob/living/M, mob/living/silicon/robot/user) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK)) - playsound(M, 'sound/weapons/genhit.ogg', 50, 1) - return 0 - - if(isrobot(user)) - if(!user.cell.use(charge_cost)) - return - - user.do_attack_animation(M) - M.Weaken(5) - M.apply_effect(STUTTER, 5) - M.Stun(5) - - M.visible_message("[user] has prodded [M] with [src]!", \ - "[user] has prodded you with [src]!") - - playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) - add_attack_logs(user, M, "Stunned with [src] (INTENT: [uppertext(user.a_intent)])") - -/obj/item/borg/overdrive - name = "Overdrive" - icon = 'icons/obj/decals.dmi' - icon_state = "shock" +/********************************************************************** + Cyborg Spec Items +***********************************************************************/ +/obj/item/borg + icon = 'icons/mob/robot_items.dmi' + +/obj/item/borg/stun + name = "electrically-charged arm" + icon_state = "elecarm" + var/charge_cost = 30 + +/obj/item/borg/stun/attack(mob/living/M, mob/living/silicon/robot/user) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK)) + playsound(M, 'sound/weapons/genhit.ogg', 50, 1) + return 0 + + if(isrobot(user)) + if(!user.cell.use(charge_cost)) + return + + user.do_attack_animation(M) + M.Weaken(5) + M.apply_effect(STUTTER, 5) + M.Stun(5) + + M.visible_message("[user] has prodded [M] with [src]!", \ + "[user] has prodded you with [src]!") + + playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) + add_attack_logs(user, M, "Stunned with [src] (INTENT: [uppertext(user.a_intent)])") + +/obj/item/borg/overdrive + name = "Overdrive" + icon = 'icons/obj/decals.dmi' + icon_state = "shock" diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 6ce102949405..46bc15a2ffe2 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -14,7 +14,7 @@ ..(newloc) if(model_info && model) model_info = model - var/datum/robolimb/R = all_robolimbs[model] + var/datum/robolimb/R = GLOB.all_robolimbs[model] if(R) name = "[R.company] [initial(name)]" desc = "[R.desc]" @@ -24,7 +24,7 @@ name = "robot [initial(name)]" /obj/item/robot_parts/attack_self(mob/user) - var/choice = input(user, "Select the company appearance for this limb.", "Limb Company Selection") as null|anything in selectable_robolimbs + var/choice = input(user, "Select the company appearance for this limb.", "Limb Company Selection") as null|anything in GLOB.selectable_robolimbs if(!choice) return if(loc != user) diff --git a/code/game/objects/items/shooting_range.dm b/code/game/objects/items/shooting_range.dm index 959b308ee058..8b85d8821d94 100644 --- a/code/game/objects/items/shooting_range.dm +++ b/code/game/objects/items/shooting_range.dm @@ -1,177 +1,177 @@ -// Targets, the things that actually get shot! -/obj/item/target - name = "shooting target" - desc = "A shooting target." - icon = 'icons/obj/objects.dmi' - icon_state = "target_h" - density = 0 - var/hp = 1800 - var/icon/virtualIcon - var/list/bulletholes = list() - -/obj/item/target/Destroy() - // if a target is deleted and associated with a stake, force stake to forget - for(var/obj/structure/target_stake/T in view(3,src)) - if(T.pinned_target == src) - T.pinned_target = null - T.density = 1 - break - return ..() // delete target - -/obj/item/target/Move() - ..() - // After target moves, check for nearby stakes. If associated, move to target - for(var/obj/structure/target_stake/M in view(3,src)) - if(M.density == 0 && M.pinned_target == src) - M.loc = loc - - // This may seem a little counter-intuitive but I assure you that's for a purpose. - // Stakes are the ones that carry targets, yes, but in the stake code we set - // a stake's density to 0 meaning it can't be pushed anymore. Instead of pushing - // the stake now, we have to push the target. - - -/obj/item/target/welder_act(mob/user, obj/item/I) - . = TRUE - if(!use_tool(src, user, 0,, volume = I.tool_volume)) - return - overlays.Cut() - to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") - -/obj/item/target/attack_hand(mob/user as mob) - // taking pinned targets off! - var/obj/structure/target_stake/stake - for(var/obj/structure/target_stake/T in view(3,src)) - if(T.pinned_target == src) - stake = T - break - - if(stake) - if(stake.pinned_target) - stake.density = 1 - density = 0 - layer = OBJ_LAYER - - loc = user.loc - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(src) - to_chat(user, "You take the target out of the stake.") - else - src.loc = get_turf(user) - to_chat(user, "You take the target out of the stake.") - - stake.pinned_target = null - return - - else - ..() - -/obj/item/target/syndicate - icon_state = "target_s" - desc = "A shooting target that looks like a syndicate scum." - hp = 2600 // i guess syndie targets are sturdier? - -/obj/item/target/alien - icon_state = "target_q" - desc = "A shooting target that looks like a xenomorphic alien." - hp = 2350 // alium onest too kinda - -/obj/item/target/bullet_act(var/obj/item/projectile/Proj) - var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" - var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) - var/decaltype = 1 // 1 - scorch, 2 - bullet - - if(istype(/obj/item/projectile/bullet, Proj)) - decaltype = 2 - - - virtualIcon = new(icon, icon_state) - - if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) - - hp -= Proj.damage - if(hp <= 0) - visible_message("[src] breaks into tiny pieces and collapses!") - qdel(src) - - // Create a temporary object to represent the damage - var/obj/bmark = new - bmark.pixel_x = p_x - bmark.pixel_y = p_y - bmark.icon = 'icons/effects/effects.dmi' - bmark.layer = 3.5 - bmark.icon_state = "scorch" - - if(decaltype == 1) - // Energy weapons are hot. they scorch! - - // offset correction - bmark.pixel_x-- - bmark.pixel_y-- - - if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) - bmark.icon_state = "scorch" - bmark.dir = pick(NORTH,SOUTH,EAST,WEST) // random scorch design - - - else - bmark.icon_state = "light_scorch" - else - - // Bullets are hard. They make dents! - bmark.icon_state = "dent" - - if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes - if(decaltype == 2) // bullet - if(prob(Proj.damage+30)) // bullets make holes more commonly! - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - else // Lasers! - if(prob(Proj.damage-10)) // lasers make holes less commonly - new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole - - // draw bullet holes - for(var/datum/bullethole/B in bulletholes) - - virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right - virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom - - overlays += bmark // add the decal - - icon = virtualIcon // apply bulletholes over decals - - return - - return -1 // the bullet/projectile goes through the target! Ie, you missed - - -// Small memory holder entity for transparent bullet holes -/datum/bullethole - // First box - var/b1x1 = 0 - var/b1x2 = 0 - var/b1y = 0 - - // Second box - var/b2x = 0 - var/b2y1 = 0 - var/b2y2 = 0 - -/datum/bullethole/New(obj/item/target/Target, pixel_x = 0, pixel_y = 0) - if(!Target) return - - // Randomize the first box - b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) - b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) - b1y = pixel_y - if(prob(35)) - b1y += rand(-4,4) - - // Randomize the second box - b2x = pixel_x - if(prob(35)) - b2x += rand(-4,4) - b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) - b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) - - Target.bulletholes.Add(src) +// Targets, the things that actually get shot! +/obj/item/target + name = "shooting target" + desc = "A shooting target." + icon = 'icons/obj/objects.dmi' + icon_state = "target_h" + density = 0 + var/hp = 1800 + var/icon/virtualIcon + var/list/bulletholes = list() + +/obj/item/target/Destroy() + // if a target is deleted and associated with a stake, force stake to forget + for(var/obj/structure/target_stake/T in view(3,src)) + if(T.pinned_target == src) + T.pinned_target = null + T.density = 1 + break + return ..() // delete target + +/obj/item/target/Move() + ..() + // After target moves, check for nearby stakes. If associated, move to target + for(var/obj/structure/target_stake/M in view(3,src)) + if(M.density == 0 && M.pinned_target == src) + M.loc = loc + + // This may seem a little counter-intuitive but I assure you that's for a purpose. + // Stakes are the ones that carry targets, yes, but in the stake code we set + // a stake's density to 0 meaning it can't be pushed anymore. Instead of pushing + // the stake now, we have to push the target. + + +/obj/item/target/welder_act(mob/user, obj/item/I) + . = TRUE + if(!use_tool(src, user, 0,, volume = I.tool_volume)) + return + overlays.Cut() + to_chat(usr, "You slice off [src]'s uneven chunks of aluminum and scorch marks.") + +/obj/item/target/attack_hand(mob/user as mob) + // taking pinned targets off! + var/obj/structure/target_stake/stake + for(var/obj/structure/target_stake/T in view(3,src)) + if(T.pinned_target == src) + stake = T + break + + if(stake) + if(stake.pinned_target) + stake.density = 1 + density = 0 + layer = OBJ_LAYER + + loc = user.loc + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(src) + to_chat(user, "You take the target out of the stake.") + else + src.loc = get_turf(user) + to_chat(user, "You take the target out of the stake.") + + stake.pinned_target = null + return + + else + ..() + +/obj/item/target/syndicate + icon_state = "target_s" + desc = "A shooting target that looks like a syndicate scum." + hp = 2600 // i guess syndie targets are sturdier? + +/obj/item/target/alien + icon_state = "target_q" + desc = "A shooting target that looks like a xenomorphic alien." + hp = 2350 // alium onest too kinda + +/obj/item/target/bullet_act(var/obj/item/projectile/Proj) + var/p_x = Proj.p_x + pick(0,0,0,0,0,-1,1) // really ugly way of coding "sometimes offset Proj.p_x!" + var/p_y = Proj.p_y + pick(0,0,0,0,0,-1,1) + var/decaltype = 1 // 1 - scorch, 2 - bullet + + if(istype(/obj/item/projectile/bullet, Proj)) + decaltype = 2 + + + virtualIcon = new(icon, icon_state) + + if( virtualIcon.GetPixel(p_x, p_y) ) // if the located pixel isn't blank (null) + + hp -= Proj.damage + if(hp <= 0) + visible_message("[src] breaks into tiny pieces and collapses!") + qdel(src) + + // Create a temporary object to represent the damage + var/obj/bmark = new + bmark.pixel_x = p_x + bmark.pixel_y = p_y + bmark.icon = 'icons/effects/effects.dmi' + bmark.layer = 3.5 + bmark.icon_state = "scorch" + + if(decaltype == 1) + // Energy weapons are hot. they scorch! + + // offset correction + bmark.pixel_x-- + bmark.pixel_y-- + + if(Proj.damage >= 20 || istype(Proj, /obj/item/projectile/beam/practice)) + bmark.icon_state = "scorch" + bmark.dir = pick(NORTH,SOUTH,EAST,WEST) // random scorch design + + + else + bmark.icon_state = "light_scorch" + else + + // Bullets are hard. They make dents! + bmark.icon_state = "dent" + + if(Proj.damage >= 10 && bulletholes.len <= 35) // maximum of 35 bullet holes + if(decaltype == 2) // bullet + if(prob(Proj.damage+30)) // bullets make holes more commonly! + new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole + else // Lasers! + if(prob(Proj.damage-10)) // lasers make holes less commonly + new/datum/bullethole(src, bmark.pixel_x, bmark.pixel_y) // create new bullet hole + + // draw bullet holes + for(var/datum/bullethole/B in bulletholes) + + virtualIcon.DrawBox(null, B.b1x1, B.b1y, B.b1x2, B.b1y) // horizontal line, left to right + virtualIcon.DrawBox(null, B.b2x, B.b2y1, B.b2x, B.b2y2) // vertical line, top to bottom + + overlays += bmark // add the decal + + icon = virtualIcon // apply bulletholes over decals + + return + + return -1 // the bullet/projectile goes through the target! Ie, you missed + + +// Small memory holder entity for transparent bullet holes +/datum/bullethole + // First box + var/b1x1 = 0 + var/b1x2 = 0 + var/b1y = 0 + + // Second box + var/b2x = 0 + var/b2y1 = 0 + var/b2y2 = 0 + +/datum/bullethole/New(obj/item/target/Target, pixel_x = 0, pixel_y = 0) + if(!Target) return + + // Randomize the first box + b1x1 = pixel_x - pick(1,1,1,1,2,2,3,3,4) + b1x2 = pixel_x + pick(1,1,1,1,2,2,3,3,4) + b1y = pixel_y + if(prob(35)) + b1y += rand(-4,4) + + // Randomize the second box + b2x = pixel_x + if(prob(35)) + b2x += rand(-4,4) + b2y1 = pixel_y + pick(1,1,1,1,2,2,3,3,4) + b2y2 = pixel_y - pick(1,1,1,1,2,2,3,3,4) + + Target.bulletholes.Add(src) diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 9f8c877aa244..8c1f5826d66e 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -1,283 +1,283 @@ -/obj/item/stack/medical - name = "medical pack" - singular_name = "medical pack" - icon = 'icons/obj/items.dmi' - amount = 6 - max_amount = 6 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - resistance_flags = FLAMMABLE - max_integrity = 40 - var/heal_brute = 0 - var/heal_burn = 0 - var/self_delay = 20 - var/unique_handling = FALSE //some things give a special prompt, do we want to bypass some checks in parent? - var/stop_bleeding = 0 - var/healverb = "bandage" - -/obj/item/stack/medical/attack(mob/living/M, mob/user) - if(!iscarbon(M) && !isanimal(M)) - to_chat(user, "[src] cannot be applied to [M]!") - return 1 - - if(!user.IsAdvancedToolUser()) - to_chat(user, "You don't have the dexterity to do this!") - return 1 - - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - - if(!H.can_inject(user, TRUE)) - return TRUE - - if(!affecting) - to_chat(user, "That limb is missing!") - return TRUE - - if(affecting.is_robotic()) - to_chat(user, "This can't be used on a robotic limb.") - return TRUE - - if(M == user && !unique_handling) - user.visible_message("[user] starts to apply [src] on [H]...") - if(!do_mob(user, H, self_delay)) - return TRUE - return - - if(isanimal(M)) - var/mob/living/simple_animal/critter = M - if(!(critter.healable)) - to_chat(user, "You cannot use [src] on [critter]!") - return - else if (critter.health == critter.maxHealth) - to_chat(user, "[critter] is at full health.") - return - else if(heal_brute < 1) - to_chat(user, "[src] won't help [critter] at all.") - return - - critter.heal_organ_damage(heal_brute, heal_burn) - user.visible_message("[user] applies [src] on [critter].", \ - "You apply [src] on [critter].") - - use(1) - - else - M.heal_organ_damage(heal_brute, heal_burn) - user.visible_message("[user] applies [src] on [M].", \ - "You apply [src] on [M].") - use(1) - -/obj/item/stack/medical/proc/heal(mob/living/M, mob/user) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - user.visible_message("[user] [healverb]s the wounds on [H]'s [affecting.name].", \ - "You [healverb] the wounds on [H]'s [affecting.name]." ) - - var/rembrute = max(0, heal_brute - affecting.brute_dam) // Maxed with 0 since heal_damage let you pass in a negative value - var/remburn = max(0, heal_burn - affecting.burn_dam) // And deduct it from their health (aka deal damage) - var/nrembrute = rembrute - var/nremburn = remburn - affecting.heal_damage(heal_brute, heal_burn) - var/list/achildlist - if(!isnull(affecting.children)) - achildlist = affecting.children.Copy() - var/parenthealed = FALSE - while(rembrute + remburn > 0) // Don't bother if there's not enough leftover heal - var/obj/item/organ/external/E - if(LAZYLEN(achildlist)) - E = pick_n_take(achildlist) // Pick a random children and then remove it from the list - else if(affecting.parent && !parenthealed) // If there's a parent and no healing attempt was made on it - E = affecting.parent - parenthealed = TRUE - else - break // If the organ have no child left and no parent / parent healed, break - if(E.status & ORGAN_ROBOT || E.open) // Ignore robotic or open limb - continue - else if(!E.brute_dam && !E.burn_dam) // Ignore undamaged limb - continue - nrembrute = max(0, rembrute - E.brute_dam) // Deduct the healed damage from the remain - nremburn = max(0, remburn - E.burn_dam) - E.heal_damage(rembrute, remburn) - rembrute = nrembrute - remburn = nremburn - user.visible_message("[user] [healverb]s the wounds on [H]'s [E.name] with the remaining medication.", \ - "You [healverb] the wounds on [H]'s [E.name] with the remaining medication." ) - -//Bruise Packs// - -/obj/item/stack/medical/bruise_pack - name = "roll of gauze" - singular_name = "gauze length" - desc = "Some sterile gauze to wrap around bloody stumps." - icon_state = "gauze" - origin_tech = "biotech=2" - heal_brute = 10 - stop_bleeding = 1800 - -/obj/item/stack/medical/bruise_pack/attackby(obj/item/I, mob/user, params) - if(I.sharp) - if(get_amount() < 2) - to_chat(user, "You need at least two gauzes to do this!") - return - new /obj/item/stack/sheet/cloth(user.drop_location()) - user.visible_message("[user] cuts [src] into pieces of cloth with [I].", \ - "You cut [src] into pieces of cloth with [I].", \ - "You hear cutting.") - use(2) - else - return ..() - -/obj/item/stack/medical/bruise_pack/attack(mob/living/M, mob/user) - if(..()) - return TRUE - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - - if(affecting.open == FALSE) - affecting.germ_level = 0 - - if(stop_bleeding) - if(!H.bleedsuppress) //so you can't stack bleed suppression - H.suppress_bloodloss(stop_bleeding) - - heal(H, user) - - H.UpdateDamageIcon() - use(1) - else - to_chat(user, "[affecting] is cut open, you'll need more than a bandage!") - -/obj/item/stack/medical/bruise_pack/improvised - name = "improvised gauze" - singular_name = "improvised gauze" - desc = "A roll of cloth roughly cut from something that can stop bleeding, but does not heal wounds." - stop_bleeding = 900 - -/obj/item/stack/medical/bruise_pack/advanced - name = "advanced trauma kit" - singular_name = "advanced trauma kit" - desc = "An advanced trauma kit for severe injuries." - icon_state = "traumakit" - heal_brute = 25 - stop_bleeding = 0 - - - -//Ointment// - - -/obj/item/stack/medical/ointment - name = "ointment" - desc = "Used to treat those nasty burns." - gender = PLURAL - singular_name = "ointment" - icon_state = "ointment" - origin_tech = "biotech=2" - healverb = "salve" - heal_burn = 10 - -/obj/item/stack/medical/ointment/attack(mob/living/M, mob/user) - if(..()) - return 1 - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - - if(affecting.open == FALSE) - affecting.germ_level = 0 - - heal(H, user) - - H.UpdateDamageIcon() - use(1) - else - to_chat(user, "[affecting] is cut open, you'll need more than some ointment!") - - -/obj/item/stack/medical/ointment/advanced - name = "advanced burn kit" - singular_name = "advanced burn kit" - desc = "An advanced treatment kit for severe burns." - icon_state = "burnkit" - heal_burn = 25 - -//Medical Herbs// -/obj/item/stack/medical/bruise_pack/comfrey - name = "\improper Comfrey leaf" - singular_name = "Comfrey leaf" - desc = "A soft leaf that is rubbed on bruises." - icon = 'icons/obj/hydroponics/harvest.dmi' - icon_state = "tea_aspera_leaves" - color = "#378C61" - stop_bleeding = 0 - heal_brute = 12 - - -/obj/item/stack/medical/ointment/aloe - name = "\improper Aloe Vera leaf" - singular_name = "Aloe Vera leaf" - desc = "A cold leaf that is rubbed on burns." - icon = 'icons/obj/hydroponics/harvest.dmi' - icon_state = "ambrosiavulgaris" - color = "#4CC5C7" - heal_burn = 12 - -// Splints -/obj/item/stack/medical/splint - name = "medical splints" - singular_name = "medical splint" - icon_state = "splint" - unique_handling = TRUE - self_delay = 100 - var/other_delay = 0 - -/obj/item/stack/medical/splint/attack(mob/living/M, mob/user) - if(..()) - return TRUE - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) - var/limb = affecting.name - - if(!(affecting.limb_name in list("l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot"))) - to_chat(user, "You can't apply a splint there!") - return TRUE - - if(affecting.status & ORGAN_SPLINTED) - to_chat(user, "[H]'s [limb] is already splinted!") - if(alert(user, "Would you like to remove the splint from [H]'s [limb]?", "Splint removal.", "Yes", "No") == "Yes") - affecting.status &= ~ORGAN_SPLINTED - H.handle_splints() - to_chat(user, "You remove the splint from [H]'s [limb].") - return TRUE - - if((M == user && self_delay > 0) || (M != user && other_delay > 0)) - user.visible_message("[user] starts to apply [src] to [H]'s [limb].", \ - "You start to apply [src] to [H]'s [limb].", \ - "You hear something being wrapped.") - - if(M == user && !do_mob(user, H, self_delay)) - return TRUE - else if(!do_mob(user, H, other_delay)) - return TRUE - - user.visible_message("[user] applies [src] to [H]'s [limb].", \ - "You apply [src] to [H]'s [limb].") - - affecting.status |= ORGAN_SPLINTED - affecting.splinted_count = H.step_count - H.handle_splints() - use(1) - -/obj/item/stack/medical/splint/tribal - name = "tribal splints" - icon_state = "tribal_splint" - other_delay = 50 +/obj/item/stack/medical + name = "medical pack" + singular_name = "medical pack" + icon = 'icons/obj/items.dmi' + amount = 6 + max_amount = 6 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + resistance_flags = FLAMMABLE + max_integrity = 40 + var/heal_brute = 0 + var/heal_burn = 0 + var/self_delay = 20 + var/unique_handling = FALSE //some things give a special prompt, do we want to bypass some checks in parent? + var/stop_bleeding = 0 + var/healverb = "bandage" + +/obj/item/stack/medical/attack(mob/living/M, mob/user) + if(!iscarbon(M) && !isanimal(M)) + to_chat(user, "[src] cannot be applied to [M]!") + return 1 + + if(!user.IsAdvancedToolUser()) + to_chat(user, "You don't have the dexterity to do this!") + return 1 + + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) + + if(!H.can_inject(user, TRUE)) + return TRUE + + if(!affecting) + to_chat(user, "That limb is missing!") + return TRUE + + if(affecting.is_robotic()) + to_chat(user, "This can't be used on a robotic limb.") + return TRUE + + if(M == user && !unique_handling) + user.visible_message("[user] starts to apply [src] on [H]...") + if(!do_mob(user, H, self_delay)) + return TRUE + return + + if(isanimal(M)) + var/mob/living/simple_animal/critter = M + if(!(critter.healable)) + to_chat(user, "You cannot use [src] on [critter]!") + return + else if (critter.health == critter.maxHealth) + to_chat(user, "[critter] is at full health.") + return + else if(heal_brute < 1) + to_chat(user, "[src] won't help [critter] at all.") + return + + critter.heal_organ_damage(heal_brute, heal_burn) + user.visible_message("[user] applies [src] on [critter].", \ + "You apply [src] on [critter].") + + use(1) + + else + M.heal_organ_damage(heal_brute, heal_burn) + user.visible_message("[user] applies [src] on [M].", \ + "You apply [src] on [M].") + use(1) + +/obj/item/stack/medical/proc/heal(mob/living/M, mob/user) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) + user.visible_message("[user] [healverb]s the wounds on [H]'s [affecting.name].", \ + "You [healverb] the wounds on [H]'s [affecting.name]." ) + + var/rembrute = max(0, heal_brute - affecting.brute_dam) // Maxed with 0 since heal_damage let you pass in a negative value + var/remburn = max(0, heal_burn - affecting.burn_dam) // And deduct it from their health (aka deal damage) + var/nrembrute = rembrute + var/nremburn = remburn + affecting.heal_damage(heal_brute, heal_burn) + var/list/achildlist + if(!isnull(affecting.children)) + achildlist = affecting.children.Copy() + var/parenthealed = FALSE + while(rembrute + remburn > 0) // Don't bother if there's not enough leftover heal + var/obj/item/organ/external/E + if(LAZYLEN(achildlist)) + E = pick_n_take(achildlist) // Pick a random children and then remove it from the list + else if(affecting.parent && !parenthealed) // If there's a parent and no healing attempt was made on it + E = affecting.parent + parenthealed = TRUE + else + break // If the organ have no child left and no parent / parent healed, break + if(E.status & ORGAN_ROBOT || E.open) // Ignore robotic or open limb + continue + else if(!E.brute_dam && !E.burn_dam) // Ignore undamaged limb + continue + nrembrute = max(0, rembrute - E.brute_dam) // Deduct the healed damage from the remain + nremburn = max(0, remburn - E.burn_dam) + E.heal_damage(rembrute, remburn) + rembrute = nrembrute + remburn = nremburn + user.visible_message("[user] [healverb]s the wounds on [H]'s [E.name] with the remaining medication.", \ + "You [healverb] the wounds on [H]'s [E.name] with the remaining medication." ) + +//Bruise Packs// + +/obj/item/stack/medical/bruise_pack + name = "roll of gauze" + singular_name = "gauze length" + desc = "Some sterile gauze to wrap around bloody stumps." + icon_state = "gauze" + origin_tech = "biotech=2" + heal_brute = 10 + stop_bleeding = 1800 + +/obj/item/stack/medical/bruise_pack/attackby(obj/item/I, mob/user, params) + if(I.sharp) + if(get_amount() < 2) + to_chat(user, "You need at least two gauzes to do this!") + return + new /obj/item/stack/sheet/cloth(user.drop_location()) + user.visible_message("[user] cuts [src] into pieces of cloth with [I].", \ + "You cut [src] into pieces of cloth with [I].", \ + "You hear cutting.") + use(2) + else + return ..() + +/obj/item/stack/medical/bruise_pack/attack(mob/living/M, mob/user) + if(..()) + return TRUE + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) + + if(affecting.open == FALSE) + affecting.germ_level = 0 + + if(stop_bleeding) + if(!H.bleedsuppress) //so you can't stack bleed suppression + H.suppress_bloodloss(stop_bleeding) + + heal(H, user) + + H.UpdateDamageIcon() + use(1) + else + to_chat(user, "[affecting] is cut open, you'll need more than a bandage!") + +/obj/item/stack/medical/bruise_pack/improvised + name = "improvised gauze" + singular_name = "improvised gauze" + desc = "A roll of cloth roughly cut from something that can stop bleeding, but does not heal wounds." + stop_bleeding = 900 + +/obj/item/stack/medical/bruise_pack/advanced + name = "advanced trauma kit" + singular_name = "advanced trauma kit" + desc = "An advanced trauma kit for severe injuries." + icon_state = "traumakit" + heal_brute = 25 + stop_bleeding = 0 + + + +//Ointment// + + +/obj/item/stack/medical/ointment + name = "ointment" + desc = "Used to treat those nasty burns." + gender = PLURAL + singular_name = "ointment" + icon_state = "ointment" + origin_tech = "biotech=2" + healverb = "salve" + heal_burn = 10 + +/obj/item/stack/medical/ointment/attack(mob/living/M, mob/user) + if(..()) + return 1 + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) + + if(affecting.open == FALSE) + affecting.germ_level = 0 + + heal(H, user) + + H.UpdateDamageIcon() + use(1) + else + to_chat(user, "[affecting] is cut open, you'll need more than some ointment!") + + +/obj/item/stack/medical/ointment/advanced + name = "advanced burn kit" + singular_name = "advanced burn kit" + desc = "An advanced treatment kit for severe burns." + icon_state = "burnkit" + heal_burn = 25 + +//Medical Herbs// +/obj/item/stack/medical/bruise_pack/comfrey + name = "\improper Comfrey leaf" + singular_name = "Comfrey leaf" + desc = "A soft leaf that is rubbed on bruises." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "tea_aspera_leaves" + color = "#378C61" + stop_bleeding = 0 + heal_brute = 12 + + +/obj/item/stack/medical/ointment/aloe + name = "\improper Aloe Vera leaf" + singular_name = "Aloe Vera leaf" + desc = "A cold leaf that is rubbed on burns." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "ambrosiavulgaris" + color = "#4CC5C7" + heal_burn = 12 + +// Splints +/obj/item/stack/medical/splint + name = "medical splints" + singular_name = "medical splint" + icon_state = "splint" + unique_handling = TRUE + self_delay = 100 + var/other_delay = 0 + +/obj/item/stack/medical/splint/attack(mob/living/M, mob/user) + if(..()) + return TRUE + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/affecting = H.get_organ(user.zone_selected) + var/limb = affecting.name + + if(!(affecting.limb_name in list("l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot"))) + to_chat(user, "You can't apply a splint there!") + return TRUE + + if(affecting.status & ORGAN_SPLINTED) + to_chat(user, "[H]'s [limb] is already splinted!") + if(alert(user, "Would you like to remove the splint from [H]'s [limb]?", "Splint removal.", "Yes", "No") == "Yes") + affecting.status &= ~ORGAN_SPLINTED + H.handle_splints() + to_chat(user, "You remove the splint from [H]'s [limb].") + return TRUE + + if((M == user && self_delay > 0) || (M != user && other_delay > 0)) + user.visible_message("[user] starts to apply [src] to [H]'s [limb].", \ + "You start to apply [src] to [H]'s [limb].", \ + "You hear something being wrapped.") + + if(M == user && !do_mob(user, H, self_delay)) + return TRUE + else if(!do_mob(user, H, other_delay)) + return TRUE + + user.visible_message("[user] applies [src] to [H]'s [limb].", \ + "You apply [src] to [H]'s [limb].") + + affecting.status |= ORGAN_SPLINTED + affecting.splinted_count = H.step_count + H.handle_splints() + use(1) + +/obj/item/stack/medical/splint/tribal + name = "tribal splints" + icon_state = "tribal_splint" + other_delay = 50 diff --git a/code/game/objects/items/stacks/rods.dm b/code/game/objects/items/stacks/rods.dm index 37f726b215de..3911ab51cc7b 100644 --- a/code/game/objects/items/stacks/rods.dm +++ b/code/game/objects/items/stacks/rods.dm @@ -1,68 +1,68 @@ -var/global/list/datum/stack_recipe/rod_recipes = list ( \ - new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \ - ) - -/obj/item/stack/rods - name = "metal rod" - desc = "Some rods. Can be used for building, or something." - singular_name = "metal rod" - icon_state = "rods" - item_state = "rods" - flags = CONDUCT - w_class = WEIGHT_CLASS_NORMAL - force = 9.0 - throwforce = 10.0 - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=1000) - max_amount = 50 - attack_verb = list("hit", "bludgeoned", "whacked") - hitsound = 'sound/weapons/grenadelaunch.ogg' - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/item/stack/rods/cyborg - materials = list() - -/obj/item/stack/rods/ten - amount = 10 - -/obj/item/stack/rods/twentyfive - amount = 25 - -/obj/item/stack/rods/fifty - amount = 50 - -/obj/item/stack/rods/New(loc, amount=null) - ..() - recipes = rod_recipes - update_icon() - -/obj/item/stack/rods/update_icon() - var/amount = get_amount() - if((amount <= 5) && (amount > 0)) - icon_state = "rods-[amount]" - else - icon_state = "rods" - -/obj/item/stack/rods/welder_act(mob/user, obj/item/I) - if(get_amount() < 2) - to_chat(user, "You need at least two rods to do this!") - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/obj/item/stack/sheet/metal/new_item = new(drop_location()) - if(new_item.get_amount() <= 0) - // stack was moved into another one on the pile - new_item = locate() in user.loc - visible_message("[user.name] shapes [src] into metal with [I]!", \ - "You shape [src] into metal with [I]!", \ - "You hear welding.") - var/replace = user.is_in_inactive_hand(src) - use(2) - if(get_amount() <= 0 && replace) - user.unEquip(src, 1) - if(new_item) - user.put_in_hands(new_item) +GLOBAL_LIST_INIT(rod_recipes, list ( \ + new/datum/stack_recipe("grille", /obj/structure/grille, 2, time = 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("table frame", /obj/structure/table_frame, 2, time = 10, one_per_turf = 1, on_floor = 1), \ + )) + +/obj/item/stack/rods + name = "metal rod" + desc = "Some rods. Can be used for building, or something." + singular_name = "metal rod" + icon_state = "rods" + item_state = "rods" + flags = CONDUCT + w_class = WEIGHT_CLASS_NORMAL + force = 9.0 + throwforce = 10.0 + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=1000) + max_amount = 50 + attack_verb = list("hit", "bludgeoned", "whacked") + hitsound = 'sound/weapons/grenadelaunch.ogg' + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/item/stack/rods/cyborg + materials = list() + +/obj/item/stack/rods/ten + amount = 10 + +/obj/item/stack/rods/twentyfive + amount = 25 + +/obj/item/stack/rods/fifty + amount = 50 + +/obj/item/stack/rods/New(loc, amount=null) + ..() + recipes = GLOB.rod_recipes + update_icon() + +/obj/item/stack/rods/update_icon() + var/amount = get_amount() + if((amount <= 5) && (amount > 0)) + icon_state = "rods-[amount]" + else + icon_state = "rods" + +/obj/item/stack/rods/welder_act(mob/user, obj/item/I) + if(get_amount() < 2) + to_chat(user, "You need at least two rods to do this!") + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/obj/item/stack/sheet/metal/new_item = new(drop_location()) + if(new_item.get_amount() <= 0) + // stack was moved into another one on the pile + new_item = locate() in user.loc + visible_message("[user.name] shapes [src] into metal with [I]!", \ + "You shape [src] into metal with [I]!", \ + "You hear welding.") + var/replace = user.is_in_inactive_hand(src) + use(2) + if(get_amount() <= 0 && replace) + user.unEquip(src, 1) + if(new_item) + user.put_in_hands(new_item) diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 769bc9dad546..4d3e31817267 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -1,211 +1,211 @@ -/* Glass stack types - * Contains: - * Glass sheets - * Reinforced glass sheets - * Glass shards - TODO: Move this into code/game/object/item/weapons - * Plasma Glass Sheets - * Reinforced Plasma Glass Sheets (AKA Holy fuck strong windows) - - Todo: Create a unified construct_window(sheet, user, created_window, full_window) - - */ - -/* - * Glass sheets - */ - -GLOBAL_LIST_INIT(glass_recipes, list ( \ - new/datum/stack_recipe/window("directional window", /obj/structure/window/basic, time = 0, on_floor = TRUE, window_checks = TRUE), \ - new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/basic, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \ - new/datum/stack_recipe("fishbowl", /obj/machinery/fishtank/bowl, 1, time = 0), \ - new/datum/stack_recipe("fish tank", /obj/machinery/fishtank/tank, 3, time = 0, on_floor = TRUE), \ - new/datum/stack_recipe("wall aquariam", /obj/machinery/fishtank/wall, 4, time = 0, on_floor = TRUE) \ -)) - -/obj/item/stack/sheet/glass - name = "glass" - desc = "HOLY SHEET! That is a lot of glass." - singular_name = "glass sheet" - icon_state = "sheet-glass" - materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100) - resistance_flags = ACID_PROOF - origin_tech = "materials=1" - created_window = /obj/structure/window/basic - full_window = /obj/structure/window/full/basic - merge_type = /obj/item/stack/sheet/glass - point_value = 1 - -/obj/item/stack/sheet/glass/fifty - amount = 50 - -/obj/item/stack/sheet/glass/cyborg - materials = list() - -/obj/item/stack/sheet/glass/New(loc, amount) - recipes = GLOB.glass_recipes - ..() - -/obj/item/stack/sheet/glass/attackby(obj/item/W, mob/user, params) - ..() - if(istype(W,/obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/CC = W - if(CC.amount < 5) - to_chat(user, "There is not enough wire in this coil. You need 5 lengths.") - return - CC.use(5) - to_chat(user, "You attach wire to the [name].") - new /obj/item/stack/light_w(user.loc) - src.use(1) - else if( istype(W, /obj/item/stack/rods) ) - var/obj/item/stack/rods/V = W - var/obj/item/stack/sheet/rglass/RG = new (user.loc) - RG.add_fingerprint(user) - V.use(1) - var/obj/item/stack/sheet/glass/G = src - src = null - var/replace = (user.get_inactive_hand()==G) - G.use(1) - if(!G && !RG && replace) - user.put_in_hands(RG) - else - return ..() - - -/* - * Reinforced glass sheets - */ - -GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ - new/datum/stack_recipe/window("windoor frame", /obj/structure/windoor_assembly, 5, time = 0, on_floor = TRUE, window_checks = TRUE), \ - null, \ - new/datum/stack_recipe/window("directional reinforced window", /obj/structure/window/reinforced, time = 0, on_floor = TRUE, window_checks = TRUE), \ - new/datum/stack_recipe/window("fulltile reinforced window", /obj/structure/window/full/reinforced, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ -)) - -/obj/item/stack/sheet/rglass - name = "reinforced glass" - desc = "Glass which seems to have rods or something stuck in them." - singular_name = "reinforced glass sheet" - icon_state = "sheet-rglass" - materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT/2, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100) - resistance_flags = ACID_PROOF - origin_tech = "materials=2" - created_window = /obj/structure/window/reinforced - full_window = /obj/structure/window/full/reinforced - merge_type = /obj/item/stack/sheet/rglass - point_value = 4 - -/obj/item/stack/sheet/rglass/cyborg - materials = list() - -/obj/item/stack/sheet/rglass/New(loc, amount) - recipes = GLOB.reinforced_glass_recipes - ..() - -GLOBAL_LIST_INIT(pglass_recipes, list ( \ - new/datum/stack_recipe/window("directional window", /obj/structure/window/plasmabasic, time = 0, on_floor = TRUE, window_checks = TRUE), \ - new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/plasmabasic, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ -)) - -/obj/item/stack/sheet/plasmaglass - name = "plasma glass" - desc = "A very strong and very resistant sheet of a plasma-glass alloy." - singular_name = "glass sheet" - icon_state = "sheet-plasmaglass" - item_state = "sheet-rglass" - materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT*2) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 100) - resistance_flags = ACID_PROOF - origin_tech = "plasmatech=2;materials=2" - created_window = /obj/structure/window/plasmabasic - full_window = /obj/structure/window/full/plasmabasic - point_value = 19 - -/obj/item/stack/sheet/plasmaglass/New(loc, amount) - recipes = GLOB.pglass_recipes - ..() - -/obj/item/stack/sheet/plasmaglass/attackby(obj/item/W, mob/user, params) - ..() - if( istype(W, /obj/item/stack/rods) ) - var/obj/item/stack/rods/V = W - var/obj/item/stack/sheet/plasmarglass/RG = new (user.loc) - RG.add_fingerprint(user) - V.use(1) - var/obj/item/stack/sheet/glass/G = src - src = null - var/replace = (user.get_inactive_hand()==G) - G.use(1) - if(!G && !RG && replace) - user.put_in_hands(RG) - else - return ..() - -/* - * Reinforced plasma glass sheets - */ - -GLOBAL_LIST_INIT(prglass_recipes, list ( \ - new/datum/stack_recipe/window("directional reinforced window", /obj/structure/window/plasmareinforced, time = 0, on_floor = TRUE, window_checks = TRUE), \ - new/datum/stack_recipe/window("fulltile reinforced window", /obj/structure/window/full/plasmareinforced, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ -)) - -/obj/item/stack/sheet/plasmarglass - name = "reinforced plasma glass" - desc = "Plasma glass which seems to have rods or something stuck in them." - singular_name = "reinforced plasma glass sheet" - icon_state = "sheet-plasmarglass" - item_state = "sheet-rglass" - materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT/2, MAT_GLASS=MINERAL_MATERIAL_AMOUNT*2) - armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - resistance_flags = ACID_PROOF - origin_tech = "plasmatech=2;materials=2" - created_window = /obj/structure/window/plasmareinforced - full_window = /obj/structure/window/full/plasmareinforced - point_value = 23 - -/obj/item/stack/sheet/plasmarglass/New(loc, amount) - recipes = GLOB.prglass_recipes - ..() - -GLOBAL_LIST_INIT(titaniumglass_recipes, list( - new/datum/stack_recipe/window("shuttle window", /obj/structure/window/full/shuttle, 2, time = 0, on_floor = TRUE, window_checks = TRUE) - )) - -/obj/item/stack/sheet/titaniumglass - name = "titanium glass" - desc = "A glass sheet made out of a titanium-silicate alloy." - singular_name = "titanium glass sheet" - icon_state = "sheet-titaniumglass" - item_state = "sheet-rglass" - materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - resistance_flags = ACID_PROOF - merge_type = /obj/item/stack/sheet/titaniumglass - full_window = /obj/structure/window/full/shuttle - -/obj/item/stack/sheet/titaniumglass/New(loc, amount) - recipes = GLOB.titaniumglass_recipes - ..() - -GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( - new/datum/stack_recipe/window("plastitanium window", /obj/structure/window/plastitanium, 2, time = 0, on_floor = TRUE, window_checks = TRUE) - )) - -/obj/item/stack/sheet/plastitaniumglass - name = "plastitanium glass" - desc = "A glass sheet made out of a plasma-titanium-silicate alloy." - singular_name = "plastitanium glass sheet" - icon_state = "sheet-plastitaniumglass" - item_state = "sheet-rglass" - materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT, MAT_PLASMA=MINERAL_MATERIAL_AMOUNT, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - resistance_flags = ACID_PROOF - merge_type = /obj/item/stack/sheet/plastitaniumglass - full_window = /obj/structure/window/plastitanium - -/obj/item/stack/sheet/plastitaniumglass/New(loc, amount) - recipes = GLOB.plastitaniumglass_recipes - ..() +/* Glass stack types + * Contains: + * Glass sheets + * Reinforced glass sheets + * Glass shards - TODO: Move this into code/game/object/item/weapons + * Plasma Glass Sheets + * Reinforced Plasma Glass Sheets (AKA Holy fuck strong windows) + + Todo: Create a unified construct_window(sheet, user, created_window, full_window) + + */ + +/* + * Glass sheets + */ + +GLOBAL_LIST_INIT(glass_recipes, list ( \ + new/datum/stack_recipe/window("directional window", /obj/structure/window/basic, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/basic, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe("fishbowl", /obj/machinery/fishtank/bowl, 1, time = 0), \ + new/datum/stack_recipe("fish tank", /obj/machinery/fishtank/tank, 3, time = 0, on_floor = TRUE), \ + new/datum/stack_recipe("wall aquariam", /obj/machinery/fishtank/wall, 4, time = 0, on_floor = TRUE) \ +)) + +/obj/item/stack/sheet/glass + name = "glass" + desc = "HOLY SHEET! That is a lot of glass." + singular_name = "glass sheet" + icon_state = "sheet-glass" + materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 100) + resistance_flags = ACID_PROOF + origin_tech = "materials=1" + created_window = /obj/structure/window/basic + full_window = /obj/structure/window/full/basic + merge_type = /obj/item/stack/sheet/glass + point_value = 1 + +/obj/item/stack/sheet/glass/fifty + amount = 50 + +/obj/item/stack/sheet/glass/cyborg + materials = list() + +/obj/item/stack/sheet/glass/New(loc, amount) + recipes = GLOB.glass_recipes + ..() + +/obj/item/stack/sheet/glass/attackby(obj/item/W, mob/user, params) + ..() + if(istype(W,/obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/CC = W + if(CC.amount < 5) + to_chat(user, "There is not enough wire in this coil. You need 5 lengths.") + return + CC.use(5) + to_chat(user, "You attach wire to the [name].") + new /obj/item/stack/light_w(user.loc) + src.use(1) + else if( istype(W, /obj/item/stack/rods) ) + var/obj/item/stack/rods/V = W + var/obj/item/stack/sheet/rglass/RG = new (user.loc) + RG.add_fingerprint(user) + V.use(1) + var/obj/item/stack/sheet/glass/G = src + src = null + var/replace = (user.get_inactive_hand()==G) + G.use(1) + if(!G && !RG && replace) + user.put_in_hands(RG) + else + return ..() + + +/* + * Reinforced glass sheets + */ + +GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ + new/datum/stack_recipe/window("windoor frame", /obj/structure/windoor_assembly, 5, time = 0, on_floor = TRUE, window_checks = TRUE), \ + null, \ + new/datum/stack_recipe/window("directional reinforced window", /obj/structure/window/reinforced, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe/window("fulltile reinforced window", /obj/structure/window/full/reinforced, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ +)) + +/obj/item/stack/sheet/rglass + name = "reinforced glass" + desc = "Glass which seems to have rods or something stuck in them." + singular_name = "reinforced glass sheet" + icon_state = "sheet-rglass" + materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT/2, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100) + resistance_flags = ACID_PROOF + origin_tech = "materials=2" + created_window = /obj/structure/window/reinforced + full_window = /obj/structure/window/full/reinforced + merge_type = /obj/item/stack/sheet/rglass + point_value = 4 + +/obj/item/stack/sheet/rglass/cyborg + materials = list() + +/obj/item/stack/sheet/rglass/New(loc, amount) + recipes = GLOB.reinforced_glass_recipes + ..() + +GLOBAL_LIST_INIT(pglass_recipes, list ( \ + new/datum/stack_recipe/window("directional window", /obj/structure/window/plasmabasic, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe/window("fulltile window", /obj/structure/window/full/plasmabasic, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ +)) + +/obj/item/stack/sheet/plasmaglass + name = "plasma glass" + desc = "A very strong and very resistant sheet of a plasma-glass alloy." + singular_name = "glass sheet" + icon_state = "sheet-plasmaglass" + item_state = "sheet-rglass" + materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT*2) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 100) + resistance_flags = ACID_PROOF + origin_tech = "plasmatech=2;materials=2" + created_window = /obj/structure/window/plasmabasic + full_window = /obj/structure/window/full/plasmabasic + point_value = 19 + +/obj/item/stack/sheet/plasmaglass/New(loc, amount) + recipes = GLOB.pglass_recipes + ..() + +/obj/item/stack/sheet/plasmaglass/attackby(obj/item/W, mob/user, params) + ..() + if( istype(W, /obj/item/stack/rods) ) + var/obj/item/stack/rods/V = W + var/obj/item/stack/sheet/plasmarglass/RG = new (user.loc) + RG.add_fingerprint(user) + V.use(1) + var/obj/item/stack/sheet/glass/G = src + src = null + var/replace = (user.get_inactive_hand()==G) + G.use(1) + if(!G && !RG && replace) + user.put_in_hands(RG) + else + return ..() + +/* + * Reinforced plasma glass sheets + */ + +GLOBAL_LIST_INIT(prglass_recipes, list ( \ + new/datum/stack_recipe/window("directional reinforced window", /obj/structure/window/plasmareinforced, time = 0, on_floor = TRUE, window_checks = TRUE), \ + new/datum/stack_recipe/window("fulltile reinforced window", /obj/structure/window/full/plasmareinforced, 2, time = 0, on_floor = TRUE, window_checks = TRUE) \ +)) + +/obj/item/stack/sheet/plasmarglass + name = "reinforced plasma glass" + desc = "Plasma glass which seems to have rods or something stuck in them." + singular_name = "reinforced plasma glass sheet" + icon_state = "sheet-plasmarglass" + item_state = "sheet-rglass" + materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT/2, MAT_GLASS=MINERAL_MATERIAL_AMOUNT*2) + armor = list("melee" = 20, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + resistance_flags = ACID_PROOF + origin_tech = "plasmatech=2;materials=2" + created_window = /obj/structure/window/plasmareinforced + full_window = /obj/structure/window/full/plasmareinforced + point_value = 23 + +/obj/item/stack/sheet/plasmarglass/New(loc, amount) + recipes = GLOB.prglass_recipes + ..() + +GLOBAL_LIST_INIT(titaniumglass_recipes, list( + new/datum/stack_recipe/window("shuttle window", /obj/structure/window/full/shuttle, 2, time = 0, on_floor = TRUE, window_checks = TRUE) + )) + +/obj/item/stack/sheet/titaniumglass + name = "titanium glass" + desc = "A glass sheet made out of a titanium-silicate alloy." + singular_name = "titanium glass sheet" + icon_state = "sheet-titaniumglass" + item_state = "sheet-rglass" + materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + resistance_flags = ACID_PROOF + merge_type = /obj/item/stack/sheet/titaniumglass + full_window = /obj/structure/window/full/shuttle + +/obj/item/stack/sheet/titaniumglass/New(loc, amount) + recipes = GLOB.titaniumglass_recipes + ..() + +GLOBAL_LIST_INIT(plastitaniumglass_recipes, list( + new/datum/stack_recipe/window("plastitanium window", /obj/structure/window/plastitanium, 2, time = 0, on_floor = TRUE, window_checks = TRUE) + )) + +/obj/item/stack/sheet/plastitaniumglass + name = "plastitanium glass" + desc = "A glass sheet made out of a plasma-titanium-silicate alloy." + singular_name = "plastitanium glass sheet" + icon_state = "sheet-plastitaniumglass" + item_state = "sheet-rglass" + materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT, MAT_PLASMA=MINERAL_MATERIAL_AMOUNT, MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + resistance_flags = ACID_PROOF + merge_type = /obj/item/stack/sheet/plastitaniumglass + full_window = /obj/structure/window/plastitanium + +/obj/item/stack/sheet/plastitaniumglass/New(loc, amount) + recipes = GLOB.plastitaniumglass_recipes + ..() diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index e7c66ede217e..c8e194bfb000 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -1,242 +1,242 @@ -/obj/item/stack/sheet/animalhide - name = "hide" - desc = "Something went wrong." - origin_tech = "biotech=3" - -/obj/item/stack/sheet/animalhide/human - name = "human skin" - desc = "The by-product of human farming." - singular_name = "human skin piece" - icon_state = "sheet-hide" - -var/global/list/datum/stack_recipe/human_recipes = list( \ - new/datum/stack_recipe("bloated human costume", /obj/item/clothing/suit/bloated_human, 5, on_floor = 1), \ - new/datum/stack_recipe("bloated human costume head", /obj/item/clothing/head/human_head, 5, on_floor = 1), \ - ) - -/obj/item/stack/sheet/animalhide/human/New(var/loc, var/amount=null) - recipes = human_recipes - return ..() - -/obj/item/stack/sheet/animalhide/generic - name = "generic skin" - desc = "A piece of generic skin." - singular_name = "generic skin piece" - icon_state = "sheet-hide" - -/obj/item/stack/sheet/animalhide/corgi - name = "corgi hide" - desc = "The by-product of corgi farming." - singular_name = "corgi hide piece" - icon_state = "sheet-corgi" - -/obj/item/stack/sheet/animalhide/cat - name = "cat hide" - desc = "The by-product of cat farming." - singular_name = "cat hide piece" - icon_state = "sheet-cat" - -/obj/item/stack/sheet/animalhide/monkey - name = "monkey hide" - desc = "The by-product of monkey farming." - singular_name = "monkey hide piece" - icon_state = "sheet-monkey" - -/obj/item/stack/sheet/animalhide/lizard - name = "lizard skin" - desc = "Sssssss..." - singular_name = "lizard skin piece" - icon_state = "sheet-lizard" - -/obj/item/stack/sheet/animalhide/xeno - name = "alien hide" - desc = "The skin of a terrible creature." - singular_name = "alien hide piece" - icon_state = "sheet-xeno" - -GLOBAL_LIST_INIT(xeno_recipes, list ( - new/datum/stack_recipe("alien helmet", /obj/item/clothing/head/xenos, 1), - new/datum/stack_recipe("alien suit", /obj/item/clothing/suit/xenos, 2))) - -/obj/item/stack/sheet/animalhide/xeno/Initialize(mapload, new_amount, merge = TRUE) - recipes = GLOB.xeno_recipes - return ..() - -//don't see anywhere else to put these, maybe together they could be used to make the xenos suit? -/obj/item/stack/sheet/xenochitin - name = "alien chitin" - desc = "A piece of the hide of a terrible creature." - singular_name = "alien hide piece" - icon = 'icons/mob/alien.dmi' - icon_state = "chitin" - origin_tech = "" - -/obj/item/xenos_claw - name = "alien claw" - desc = "The claw of a terrible creature." - icon = 'icons/mob/alien.dmi' - icon_state = "claw" - origin_tech = "" - -/obj/item/weed_extract - name = "weed extract" - desc = "A piece of slimy, purplish weed." - icon = 'icons/mob/alien.dmi' - icon_state = "weed_extract" - origin_tech = "" - -/obj/item/stack/sheet/hairlesshide - name = "hairless hide" - desc = "This hide was stripped of it's hair, but still needs tanning." - singular_name = "hairless hide piece" - icon_state = "sheet-hairlesshide" - origin_tech = "" - -/obj/item/stack/sheet/wetleather - name = "wet leather" - desc = "This leather has been cleaned but still needs to be dried." - singular_name = "wet leather piece" - icon_state = "sheet-wetleather" - origin_tech = "" - var/wetness = 30 //Reduced when exposed to high temperautres - var/drying_threshold_temperature = 500 //Kelvin to start drying - -/obj/item/stack/sheet/leather - name = "leather" - desc = "The by-product of mob grinding." - singular_name = "leather piece" - icon_state = "sheet-leather" - origin_tech = "materials=2" - -GLOBAL_LIST_INIT(leather_recipes, list ( - new/datum/stack_recipe("wallet", /obj/item/storage/wallet, 1), - new/datum/stack_recipe("muzzle", /obj/item/clothing/mask/muzzle, 2), - new/datum/stack_recipe("botany gloves", /obj/item/clothing/gloves/botanic_leather, 3), - new/datum/stack_recipe("toolbelt", /obj/item/storage/belt/utility, 4), - new/datum/stack_recipe("leather satchel", /obj/item/storage/backpack/satchel, 5), - new/datum/stack_recipe("bandolier", /obj/item/storage/belt/bandolier, 5), - new/datum/stack_recipe("leather jacket", /obj/item/clothing/suit/jacket/leather, 7), - new/datum/stack_recipe("leather shoes", /obj/item/clothing/shoes/laceup, 2), - new/datum/stack_recipe("leather overcoat", /obj/item/clothing/suit/jacket/leather/overcoat, 10), - new/datum/stack_recipe("hide mantle", /obj/item/clothing/suit/unathi/mantle, 4))) - -/obj/item/stack/sheet/leather/New(loc, new_amount, merge = TRUE) - recipes = GLOB.leather_recipes - return ..() - -/obj/item/stack/sheet/sinew - name = "watcher sinew" - icon = 'icons/obj/mining.dmi' - desc = "Long stringy filaments which presumably came from a watcher's wings." - singular_name = "watcher sinew" - icon_state = "sinew" - origin_tech = "biotech=4" - -var/global/list/datum/stack_recipe/sinew_recipes = list ( \ - new/datum/stack_recipe("sinew restraints", /obj/item/restraints/handcuffs/sinew, 1, on_floor = 1), \ - ) - -/obj/item/stack/sheet/sinew/New(var/loc, var/amount=null) - recipes = sinew_recipes - return ..() - -/obj/item/stack/sheet/animalhide/goliath_hide - name = "goliath hide plates" - desc = "Pieces of a goliath's rocky hide, these might be able to make your suit a bit more durable to attack from the local fauna." - icon = 'icons/obj/mining.dmi' - icon_state = "goliath_hide" - singular_name = "hide plate" - flags = NOBLUDGEON - w_class = WEIGHT_CLASS_NORMAL - layer = MOB_LAYER - var/static/list/goliath_platable_armor_typecache = typecacheof(list( - /obj/item/clothing/suit/space/hardsuit/mining, - /obj/item/clothing/head/helmet/space/hardsuit/mining, - /obj/item/clothing/suit/hooded/explorer, - /obj/item/clothing/head/hooded/explorer, - /obj/item/clothing/head/helmet/space/plasmaman/mining)) - -/obj/item/stack/sheet/animalhide/goliath_hide/afterattack(atom/target, mob/user, proximity_flag) - if(!proximity_flag) - return - if(is_type_in_typecache(target, goliath_platable_armor_typecache)) - var/obj/item/clothing/C = target - var/list/current_armor = C.armor - if(current_armor["melee"] < 60) - current_armor["melee"] = min(current_armor["melee"] + 10, 60) - to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") - use(1) - else - to_chat(user, "You can't improve [C] any further!") - else if(istype(target, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/D = target - if(D.hides < 3) - D.hides++ - D.armor["melee"] = min(D.armor["melee"] + 10, 70) - D.armor["bullet"] = min(D.armor["bullet"] + 5, 50) - D.armor["laser"] = min(D.armor["laser"] + 5, 50) - to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") - D.update_icon() - if(D.hides == 3) - D.desc = "Autonomous Power Loader Unit. It's wearing a fearsome carapace entirely composed of goliath hide plates - its pilot must be an experienced monster hunter." - else - D.desc = "Autonomous Power Loader Unit. Its armour is enhanced with some goliath hide plates." - use(1) - else - to_chat(user, "You can't improve [D] any further!") - -/obj/item/stack/sheet/animalhide/ashdrake - name = "ash drake hide" - desc = "The strong, scaled hide of an ash drake." - icon = 'icons/obj/mining.dmi' - icon_state = "dragon_hide" - singular_name = "drake plate" - flags = NOBLUDGEON - w_class = WEIGHT_CLASS_NORMAL - layer = MOB_LAYER - -//Step one - dehairing. - -/obj/item/stack/sheet/animalhide/attackby(obj/item/W as obj, mob/user as mob, params) - if(W.sharp) - user.visible_message("[user] starts cutting hair off \the [src].", "You start cutting the hair off \the [src]...", "You hear the sound of a knife rubbing against flesh.") - if(do_after(user, 50 * W.toolspeed, target = src)) - to_chat(user, "You cut the hair from this [src.singular_name].") - //Try locating an exisitng stack on the tile and add to there if possible - for(var/obj/item/stack/sheet/hairlesshide/HS in usr.loc) - if(HS.amount < 50) - HS.amount++ - src.use(1) - break - //If it gets to here it means it did not find a suitable stack on the tile. - var/obj/item/stack/sheet/hairlesshide/HS = new(usr.loc) - HS.amount = 1 - src.use(1) - else - ..() - -//Step two - washing (also handled by water reagent code and washing machine code) -/obj/item/stack/sheet/hairlesshide/water_act(volume, temperature, source, method = REAGENT_TOUCH) - . = ..() - if(volume >= 10) - new /obj/item/stack/sheet/wetleather(get_turf(src), amount) - qdel(src) - -//Step three - drying -/obj/item/stack/sheet/wetleather/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature >= drying_threshold_temperature) - wetness-- - if(wetness == 0) - //Try locating an exisitng stack on the tile and add to there if possible - for(var/obj/item/stack/sheet/leather/HS in src.loc) - if(HS.amount < 50) - HS.amount++ - src.use(1) - wetness = initial(wetness) - break - //If it gets to here it means it did not find a suitable stack on the tile. - var/obj/item/stack/sheet/leather/HS = new(src.loc) - HS.amount = 1 - wetness = initial(wetness) - src.use(1) +/obj/item/stack/sheet/animalhide + name = "hide" + desc = "Something went wrong." + origin_tech = "biotech=3" + +/obj/item/stack/sheet/animalhide/human + name = "human skin" + desc = "The by-product of human farming." + singular_name = "human skin piece" + icon_state = "sheet-hide" + +GLOBAL_LIST_INIT(human_recipes, list( \ + new/datum/stack_recipe("bloated human costume", /obj/item/clothing/suit/bloated_human, 5, on_floor = 1), \ + new/datum/stack_recipe("bloated human costume head", /obj/item/clothing/head/human_head, 5, on_floor = 1), \ + )) + +/obj/item/stack/sheet/animalhide/human/New(var/loc, var/amount=null) + recipes = GLOB.human_recipes + return ..() + +/obj/item/stack/sheet/animalhide/generic + name = "generic skin" + desc = "A piece of generic skin." + singular_name = "generic skin piece" + icon_state = "sheet-hide" + +/obj/item/stack/sheet/animalhide/corgi + name = "corgi hide" + desc = "The by-product of corgi farming." + singular_name = "corgi hide piece" + icon_state = "sheet-corgi" + +/obj/item/stack/sheet/animalhide/cat + name = "cat hide" + desc = "The by-product of cat farming." + singular_name = "cat hide piece" + icon_state = "sheet-cat" + +/obj/item/stack/sheet/animalhide/monkey + name = "monkey hide" + desc = "The by-product of monkey farming." + singular_name = "monkey hide piece" + icon_state = "sheet-monkey" + +/obj/item/stack/sheet/animalhide/lizard + name = "lizard skin" + desc = "Sssssss..." + singular_name = "lizard skin piece" + icon_state = "sheet-lizard" + +/obj/item/stack/sheet/animalhide/xeno + name = "alien hide" + desc = "The skin of a terrible creature." + singular_name = "alien hide piece" + icon_state = "sheet-xeno" + +GLOBAL_LIST_INIT(xeno_recipes, list ( + new/datum/stack_recipe("alien helmet", /obj/item/clothing/head/xenos, 1), + new/datum/stack_recipe("alien suit", /obj/item/clothing/suit/xenos, 2))) + +/obj/item/stack/sheet/animalhide/xeno/Initialize(mapload, new_amount, merge = TRUE) + recipes = GLOB.xeno_recipes + return ..() + +//don't see anywhere else to put these, maybe together they could be used to make the xenos suit? +/obj/item/stack/sheet/xenochitin + name = "alien chitin" + desc = "A piece of the hide of a terrible creature." + singular_name = "alien hide piece" + icon = 'icons/mob/alien.dmi' + icon_state = "chitin" + origin_tech = "" + +/obj/item/xenos_claw + name = "alien claw" + desc = "The claw of a terrible creature." + icon = 'icons/mob/alien.dmi' + icon_state = "claw" + origin_tech = "" + +/obj/item/weed_extract + name = "weed extract" + desc = "A piece of slimy, purplish weed." + icon = 'icons/mob/alien.dmi' + icon_state = "weed_extract" + origin_tech = "" + +/obj/item/stack/sheet/hairlesshide + name = "hairless hide" + desc = "This hide was stripped of it's hair, but still needs tanning." + singular_name = "hairless hide piece" + icon_state = "sheet-hairlesshide" + origin_tech = "" + +/obj/item/stack/sheet/wetleather + name = "wet leather" + desc = "This leather has been cleaned but still needs to be dried." + singular_name = "wet leather piece" + icon_state = "sheet-wetleather" + origin_tech = "" + var/wetness = 30 //Reduced when exposed to high temperautres + var/drying_threshold_temperature = 500 //Kelvin to start drying + +/obj/item/stack/sheet/leather + name = "leather" + desc = "The by-product of mob grinding." + singular_name = "leather piece" + icon_state = "sheet-leather" + origin_tech = "materials=2" + +GLOBAL_LIST_INIT(leather_recipes, list ( + new/datum/stack_recipe("wallet", /obj/item/storage/wallet, 1), + new/datum/stack_recipe("muzzle", /obj/item/clothing/mask/muzzle, 2), + new/datum/stack_recipe("botany gloves", /obj/item/clothing/gloves/botanic_leather, 3), + new/datum/stack_recipe("toolbelt", /obj/item/storage/belt/utility, 4), + new/datum/stack_recipe("leather satchel", /obj/item/storage/backpack/satchel, 5), + new/datum/stack_recipe("bandolier", /obj/item/storage/belt/bandolier, 5), + new/datum/stack_recipe("leather jacket", /obj/item/clothing/suit/jacket/leather, 7), + new/datum/stack_recipe("leather shoes", /obj/item/clothing/shoes/laceup, 2), + new/datum/stack_recipe("leather overcoat", /obj/item/clothing/suit/jacket/leather/overcoat, 10), + new/datum/stack_recipe("hide mantle", /obj/item/clothing/suit/unathi/mantle, 4))) + +/obj/item/stack/sheet/leather/New(loc, new_amount, merge = TRUE) + recipes = GLOB.leather_recipes + return ..() + +/obj/item/stack/sheet/sinew + name = "watcher sinew" + icon = 'icons/obj/mining.dmi' + desc = "Long stringy filaments which presumably came from a watcher's wings." + singular_name = "watcher sinew" + icon_state = "sinew" + origin_tech = "biotech=4" + +GLOBAL_LIST_INIT(sinew_recipes, list ( \ + new/datum/stack_recipe("sinew restraints", /obj/item/restraints/handcuffs/sinew, 1, on_floor = 1), \ + )) + +/obj/item/stack/sheet/sinew/New(var/loc, var/amount=null) + recipes = GLOB.sinew_recipes + return ..() + +/obj/item/stack/sheet/animalhide/goliath_hide + name = "goliath hide plates" + desc = "Pieces of a goliath's rocky hide, these might be able to make your suit a bit more durable to attack from the local fauna." + icon = 'icons/obj/mining.dmi' + icon_state = "goliath_hide" + singular_name = "hide plate" + flags = NOBLUDGEON + w_class = WEIGHT_CLASS_NORMAL + layer = MOB_LAYER + var/static/list/goliath_platable_armor_typecache = typecacheof(list( + /obj/item/clothing/suit/space/hardsuit/mining, + /obj/item/clothing/head/helmet/space/hardsuit/mining, + /obj/item/clothing/suit/hooded/explorer, + /obj/item/clothing/head/hooded/explorer, + /obj/item/clothing/head/helmet/space/plasmaman/mining)) + +/obj/item/stack/sheet/animalhide/goliath_hide/afterattack(atom/target, mob/user, proximity_flag) + if(!proximity_flag) + return + if(is_type_in_typecache(target, goliath_platable_armor_typecache)) + var/obj/item/clothing/C = target + var/list/current_armor = C.armor + if(current_armor["melee"] < 60) + current_armor["melee"] = min(current_armor["melee"] + 10, 60) + to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") + use(1) + else + to_chat(user, "You can't improve [C] any further!") + else if(istype(target, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/D = target + if(D.hides < 3) + D.hides++ + D.armor["melee"] = min(D.armor["melee"] + 10, 70) + D.armor["bullet"] = min(D.armor["bullet"] + 5, 50) + D.armor["laser"] = min(D.armor["laser"] + 5, 50) + to_chat(user, "You strengthen [target], improving its resistance against melee attacks.") + D.update_icon() + if(D.hides == 3) + D.desc = "Autonomous Power Loader Unit. It's wearing a fearsome carapace entirely composed of goliath hide plates - its pilot must be an experienced monster hunter." + else + D.desc = "Autonomous Power Loader Unit. Its armour is enhanced with some goliath hide plates." + use(1) + else + to_chat(user, "You can't improve [D] any further!") + +/obj/item/stack/sheet/animalhide/ashdrake + name = "ash drake hide" + desc = "The strong, scaled hide of an ash drake." + icon = 'icons/obj/mining.dmi' + icon_state = "dragon_hide" + singular_name = "drake plate" + flags = NOBLUDGEON + w_class = WEIGHT_CLASS_NORMAL + layer = MOB_LAYER + +//Step one - dehairing. + +/obj/item/stack/sheet/animalhide/attackby(obj/item/W as obj, mob/user as mob, params) + if(W.sharp) + user.visible_message("[user] starts cutting hair off \the [src].", "You start cutting the hair off \the [src]...", "You hear the sound of a knife rubbing against flesh.") + if(do_after(user, 50 * W.toolspeed, target = src)) + to_chat(user, "You cut the hair from this [src.singular_name].") + //Try locating an exisitng stack on the tile and add to there if possible + for(var/obj/item/stack/sheet/hairlesshide/HS in usr.loc) + if(HS.amount < 50) + HS.amount++ + src.use(1) + break + //If it gets to here it means it did not find a suitable stack on the tile. + var/obj/item/stack/sheet/hairlesshide/HS = new(usr.loc) + HS.amount = 1 + src.use(1) + else + ..() + +//Step two - washing (also handled by water reagent code and washing machine code) +/obj/item/stack/sheet/hairlesshide/water_act(volume, temperature, source, method = REAGENT_TOUCH) + . = ..() + if(volume >= 10) + new /obj/item/stack/sheet/wetleather(get_turf(src), amount) + qdel(src) + +//Step three - drying +/obj/item/stack/sheet/wetleather/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature >= drying_threshold_temperature) + wetness-- + if(wetness == 0) + //Try locating an exisitng stack on the tile and add to there if possible + for(var/obj/item/stack/sheet/leather/HS in src.loc) + if(HS.amount < 50) + HS.amount++ + src.use(1) + wetness = initial(wetness) + break + //If it gets to here it means it did not find a suitable stack on the tile. + var/obj/item/stack/sheet/leather/HS = new(src.loc) + HS.amount = 1 + wetness = initial(wetness) + src.use(1) diff --git a/code/game/objects/items/stacks/sheets/light.dm b/code/game/objects/items/stacks/sheets/light.dm index 9e914671b454..17cf8bc50011 100644 --- a/code/game/objects/items/stacks/sheets/light.dm +++ b/code/game/objects/items/stacks/sheets/light.dm @@ -1,37 +1,37 @@ -/obj/item/stack/light_w - name = "wired glass tiles" - gender = PLURAL - singular_name = "wired glass floor tile" - desc = "A glass tile, which is wired, somehow." - icon = 'icons/obj/tiles.dmi' - icon_state = "glass_wire" - w_class = WEIGHT_CLASS_NORMAL - force = 3.0 - throwforce = 5.0 - throw_speed = 5 - throw_range = 20 - flags = CONDUCT - max_amount = 60 - -/obj/item/stack/light_w/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - ..() - if(istype(O,/obj/item/wirecutters)) - var/obj/item/stack/cable_coil/CC = new/obj/item/stack/cable_coil(user.loc) - CC.amount = 5 - amount-- - new/obj/item/stack/sheet/glass(user.loc) - if(amount <= 0) - user.unEquip(src, 1) - qdel(src) - - if(istype(O,/obj/item/stack/sheet/metal)) - var/obj/item/stack/sheet/metal/M = O - M.amount-- - if(M.amount <= 0) - user.unEquip(src, 1) - qdel(M) - amount-- - new/obj/item/stack/tile/light(user.loc) - if(amount <= 0) - user.unEquip(src, 1) - qdel(src) +/obj/item/stack/light_w + name = "wired glass tiles" + gender = PLURAL + singular_name = "wired glass floor tile" + desc = "A glass tile, which is wired, somehow." + icon = 'icons/obj/tiles.dmi' + icon_state = "glass_wire" + w_class = WEIGHT_CLASS_NORMAL + force = 3.0 + throwforce = 5.0 + throw_speed = 5 + throw_range = 20 + flags = CONDUCT + max_amount = 60 + +/obj/item/stack/light_w/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + ..() + if(istype(O,/obj/item/wirecutters)) + var/obj/item/stack/cable_coil/CC = new/obj/item/stack/cable_coil(user.loc) + CC.amount = 5 + amount-- + new/obj/item/stack/sheet/glass(user.loc) + if(amount <= 0) + user.unEquip(src, 1) + qdel(src) + + if(istype(O,/obj/item/stack/sheet/metal)) + var/obj/item/stack/sheet/metal/M = O + M.amount-- + if(M.amount <= 0) + user.unEquip(src, 1) + qdel(M) + amount-- + new/obj/item/stack/tile/light(user.loc) + if(amount <= 0) + user.unEquip(src, 1) + qdel(src) diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 763fa960f771..b8fd23e78cbf 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -1,389 +1,389 @@ -/* -Mineral Sheets - Contains: - - Sandstone - - Diamond - - Uranium - - Plasma - - Gold - - Silver - - Bananium - - Tranqillite - - Enriched Uranium - - Platinum - - Alien Alloy - - Adamantine -*/ - -var/global/list/datum/stack_recipe/sandstone_recipes = list ( \ - new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("Assistant Statue", /obj/structure/statue/sandstone/assistant, 5, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("Breakdown into sand", /obj/item/stack/ore/glass, 1, one_per_turf = 0, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/silver_recipes = list ( \ - new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("silver tile", /obj/item/stack/tile/mineral/silver, 1, 4, 20), \ - null, \ - new/datum/stack_recipe("Janitor Statue", /obj/structure/statue/silver/janitor, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Med Doctor Statue", /obj/structure/statue/silver/md, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/diamond_recipes = list ( \ - new/datum/stack_recipe("diamond door", /obj/structure/mineral_door/transparent/diamond, 10, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("diamond tile", /obj/item/stack/tile/mineral/diamond, 1, 4, 20), \ - null, \ - new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/uranium_recipes = list ( \ - new/datum/stack_recipe("uranium door", /obj/structure/mineral_door/uranium, 10, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \ - null, \ - new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/gold_recipes = list ( \ - new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("gold tile", /obj/item/stack/tile/mineral/gold, 1, 4, 20), \ - null, \ - new/datum/stack_recipe("HoS Statue", /obj/structure/statue/gold/hos, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("HoP Statue", /obj/structure/statue/gold/hop, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("CE Statue", /obj/structure/statue/gold/ce, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \ - ) - -var/global/list/datum/stack_recipe/plasma_recipes = list ( \ - new/datum/stack_recipe/dangerous("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe/dangerous("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \ - null, \ - new/datum/stack_recipe/dangerous("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe/dangerous("Xenomorph Statue", /obj/structure/statue/plasma/xeno, 5, one_per_turf = 1, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/bananium_recipes = list ( \ - new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \ - null, \ - new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("bananium computer frame", /obj/structure/computerframe/HONKputer, 50, time = 25, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("bananium grenade casing", /obj/item/grenade/bananade/casing, 4, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/tranquillite_recipes = list ( \ - new/datum/stack_recipe("invisible wall", /obj/structure/barricade/mime, 5, one_per_turf = 1, on_floor = 1, time = 50), \ - null, \ - new/datum/stack_recipe("silent tile", /obj/item/stack/tile/mineral/tranquillite, 1, 4, 20), \ - null, \ - new/datum/stack_recipe("Mime Statue", /obj/structure/statue/tranquillite/mime, 5, one_per_turf = 1, on_floor = 1), \ - ) - -var/global/list/datum/stack_recipe/abductor_recipes = list ( \ - new/datum/stack_recipe("alien bed", /obj/structure/bed/abductor, 2, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("alien locker", /obj/structure/closet/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("alien table frame", /obj/structure/table_frame/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("alien airlock assembly", /obj/structure/door_assembly/door_assembly_abductor, 4, time = 20, one_per_turf = 1, on_floor = 1), \ - null, \ - new/datum/stack_recipe("alien floor tile", /obj/item/stack/tile/mineral/abductor, 1, 4, 20), \ - ) - -var/global/list/datum/stack_recipe/adamantine_recipes = list( - new /datum/stack_recipe("incomplete servant golem shell", /obj/item/golem_shell/servant, req_amount = 1, res_amount = 1), \ - ) - -var/global/list/datum/stack_recipe/snow_recipes = list( - new/datum/stack_recipe("snowman", /obj/structure/snowman, 5, one_per_turf = 1, on_floor = 1), \ - new/datum/stack_recipe("Snowball", /obj/item/snowball, 1) - ) - -/obj/item/stack/sheet/mineral - force = 5 - throwforce = 5 - throw_speed = 3 - -/obj/item/stack/sheet/mineral/New() - ..() - pixel_x = rand(0,4)-4 - pixel_y = rand(0,4)-4 - -/obj/item/stack/sheet/mineral/sandstone - name = "sandstone brick" - desc = "This appears to be a combination of both sand and stone." - singular_name = "sandstone brick" - icon_state = "sheet-sandstone" - throw_range = 5 - origin_tech = "materials=1" - sheettype = "sandstone" - materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) - -/obj/item/stack/sheet/mineral/sandstone/New() - ..() - recipes = sandstone_recipes - -/* - * Sandbags - */ - -/obj/item/stack/sheet/mineral/sandbags - name = "sandbags" - icon_state = "sandbags" - singular_name = "sandbag" - layer = LOW_ITEM_LAYER - merge_type = /obj/item/stack/sheet/mineral/sandbags - -GLOBAL_LIST_INIT(sandbag_recipes, list ( \ - new/datum/stack_recipe("sandbags", /obj/structure/barricade/sandbags, 1, time = 25, one_per_turf = 1, on_floor = 1), \ - )) - -/obj/item/stack/sheet/mineral/sandbags/New() - recipes = GLOB.sandbag_recipes - ..() - -/obj/item/emptysandbag - name = "empty sandbag" - desc = "A bag to be filled with sand." - icon = 'icons/obj/items.dmi' - icon_state = "sandbag" - w_class = WEIGHT_CLASS_TINY - -/obj/item/emptysandbag/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/ore/glass)) - var/obj/item/stack/ore/glass/G = I - to_chat(user, "You fill the sandbag.") - var/obj/item/stack/sheet/mineral/sandbags/S = new /obj/item/stack/sheet/mineral/sandbags(drop_location()) - qdel(src) - if(Adjacent(user) && !issilicon(user)) - user.put_in_hands(S) - G.use(1) - else - return ..() - -/obj/item/stack/sheet/mineral/diamond - name = "diamond" - icon_state = "sheet-diamond" - singular_name = "diamond" - origin_tech = "materials=6" - sheettype = "diamond" - materials = list(MAT_DIAMOND=MINERAL_MATERIAL_AMOUNT) - point_value = 25 - -/obj/item/stack/sheet/mineral/diamond/New() - ..() - recipes = diamond_recipes - -/obj/item/stack/sheet/mineral/uranium - name = "uranium" - icon_state = "sheet-uranium" - singular_name = "uranium sheet" - origin_tech = "materials=5" - sheettype = "uranium" - materials = list(MAT_URANIUM=MINERAL_MATERIAL_AMOUNT) - point_value = 20 - -/obj/item/stack/sheet/mineral/uranium/New() - ..() - recipes = uranium_recipes - -/obj/item/stack/sheet/mineral/plasma - name = "solid plasma" - icon_state = "sheet-plasma" - singular_name = "plasma sheet" - origin_tech = "plasmatech=2;materials=2" - sheettype = "plasma" - materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT) - resistance_flags = FLAMMABLE - max_integrity = 100 - point_value = 20 - -/obj/item/stack/sheet/mineral/plasma/New() - ..() - recipes = plasma_recipes - -/obj/item/stack/sheet/mineral/plasma/welder_act(mob/user, obj/item/I) - if(I.use_tool(src, user, volume = I.tool_volume)) - message_admins("Plasma sheets ignited by [key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) in ([x],[y],[z] - JMP)",0,1) - log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])") - investigate_log("was ignited by [key_name(user)]","atmos") - fire_act() - -/obj/item/stack/sheet/mineral/plasma/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - ..() - atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, amount*10) - qdel(src) - -/obj/item/stack/sheet/mineral/gold - name = "gold" - icon_state = "sheet-gold" - singular_name = "gold bar" - origin_tech = "materials=4" - sheettype = "gold" - materials = list(MAT_GOLD=MINERAL_MATERIAL_AMOUNT) - point_value = 20 - -/obj/item/stack/sheet/mineral/gold/New() - ..() - recipes = gold_recipes - -/obj/item/stack/sheet/mineral/silver - name = "silver" - icon_state = "sheet-silver" - singular_name = "silver bar" - origin_tech = "materials=4" - sheettype = "silver" - materials = list(MAT_SILVER=MINERAL_MATERIAL_AMOUNT) - point_value = 20 - -/obj/item/stack/sheet/mineral/silver/New() - ..() - recipes = silver_recipes - -/obj/item/stack/sheet/mineral/bananium - name = "bananium" - icon_state = "sheet-clown" - singular_name = "bananium sheet" - origin_tech = "materials=4" - sheettype = "bananium" - materials = list(MAT_BANANIUM=MINERAL_MATERIAL_AMOUNT) - point_value = 50 - -/obj/item/stack/sheet/mineral/bananium/New(loc, amount=null) - ..() - recipes = bananium_recipes - -/obj/item/stack/sheet/mineral/tranquillite - name = "tranquillite" - icon_state = "sheet-mime" - singular_name = "beret" - origin_tech = "materials=4" - sheettype = "tranquillite" - materials = list(MAT_TRANQUILLITE=MINERAL_MATERIAL_AMOUNT) - wall_allowed = FALSE //no tranquilite walls in code - point_value = 50 - -/obj/item/stack/sheet/mineral/tranquillite/New(loc, amount=null) - ..() - recipes = tranquillite_recipes - -/* - * Titanium - */ -/obj/item/stack/sheet/mineral/titanium - name = "titanium" - icon_state = "sheet-titanium" - item_state = "sheet-metal" - singular_name = "titanium sheet" - force = 5 - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 1 - throw_range = 3 - sheettype = "titanium" - materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT) - point_value = 20 - -var/global/list/datum/stack_recipe/titanium_recipes = list ( - new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), - new/datum/stack_recipe("surgical tray", /obj/structure/table/tray, 2, one_per_turf = 1, on_floor = 1), - ) - -/obj/item/stack/sheet/mineral/titanium/New(loc, amount=null) - recipes = titanium_recipes - ..() - -/obj/item/stack/sheet/mineral/titanium/fifty - amount = 50 - - -/* - * Plastitanium - */ -/obj/item/stack/sheet/mineral/plastitanium - name = "plastitanium" - icon_state = "sheet-plastitanium" - item_state = "sheet-metal" - singular_name = "plastitanium sheet" - force = 5 - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 1 - throw_range = 3 - sheettype = "plastitanium" - materials = list(MAT_TITANIUM=2000, MAT_PLASMA=2000) - point_value = 45 - -var/global/list/datum/stack_recipe/plastitanium_recipes = list ( - new/datum/stack_recipe("plas-titanium tile", /obj/item/stack/tile/mineral/plastitanium, 1, 4, 20), - ) - -/obj/item/stack/sheet/mineral/plastitanium/New(loc, amount=null) - recipes = plastitanium_recipes - ..() - -/obj/item/stack/sheet/mineral/enruranium - name = "enriched uranium" - icon_state = "sheet-enruranium" - origin_tech = "materials=6" - materials = list(MAT_URANIUM=3000) - -//Alien Alloy -/obj/item/stack/sheet/mineral/abductor - name = "alien alloy" - icon = 'icons/obj/abductor.dmi' - icon_state = "sheet-abductor" - singular_name = "alien alloy sheet" - force = 5 - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 1 - origin_tech = "materials=6;abductor=1" - sheettype = "abductor" - -/obj/item/stack/sheet/mineral/abductor/New(loc, amount=null) - recipes = abductor_recipes - ..() - -/obj/item/stack/sheet/mineral/adamantine - name = "adamantine" - desc = "A strange mineral used in the construction of sentient golems." - icon_state = "sheet-adamantine" - singular_name = "adamantine sheet" - origin_tech = "materials=5" - merge_type = /obj/item/stack/sheet/mineral/adamantine - wall_allowed = FALSE - -/obj/item/stack/sheet/mineral/adamantine/New(loc, amount = null) - recipes = adamantine_recipes - ..() - -/* - * Snow - */ -/obj/item/stack/sheet/mineral/snow - name = "snow" - icon_state = "sheet-snow" - item_state = "sheet-snow" - singular_name = "snow block" - force = 1 - throwforce = 2 - merge_type = /obj/item/stack/sheet/mineral/snow - -/obj/item/stack/sheet/mineral/snow/New(loc, amount = null) - recipes = snow_recipes - ..() +/* +Mineral Sheets + Contains: + - Sandstone + - Diamond + - Uranium + - Plasma + - Gold + - Silver + - Bananium + - Tranqillite + - Enriched Uranium + - Platinum + - Alien Alloy + - Adamantine +*/ + +GLOBAL_LIST_INIT(sandstone_recipes, list ( \ + new/datum/stack_recipe("pile of dirt", /obj/machinery/hydroponics/soil, 3, time = 10, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("sandstone door", /obj/structure/mineral_door/sandstone, 10, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("Assistant Statue", /obj/structure/statue/sandstone/assistant, 5, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("Breakdown into sand", /obj/item/stack/ore/glass, 1, one_per_turf = 0, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(silver_recipes, list ( \ + new/datum/stack_recipe("silver door", /obj/structure/mineral_door/silver, 10, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("silver tile", /obj/item/stack/tile/mineral/silver, 1, 4, 20), \ + null, \ + new/datum/stack_recipe("Janitor Statue", /obj/structure/statue/silver/janitor, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Med Doctor Statue", /obj/structure/statue/silver/md, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(diamond_recipes, list ( \ + new/datum/stack_recipe("diamond door", /obj/structure/mineral_door/transparent/diamond, 10, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("diamond tile", /obj/item/stack/tile/mineral/diamond, 1, 4, 20), \ + null, \ + new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(uranium_recipes, list ( \ + new/datum/stack_recipe("uranium door", /obj/structure/mineral_door/uranium, 10, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \ + null, \ + new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(gold_recipes, list ( \ + new/datum/stack_recipe("golden door", /obj/structure/mineral_door/gold, 10, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("gold tile", /obj/item/stack/tile/mineral/gold, 1, 4, 20), \ + null, \ + new/datum/stack_recipe("HoS Statue", /obj/structure/statue/gold/hos, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("HoP Statue", /obj/structure/statue/gold/hop, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("CE Statue", /obj/structure/statue/gold/ce, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \ + )) + +GLOBAL_LIST_INIT(plasma_recipes, list ( \ + new/datum/stack_recipe/dangerous("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe/dangerous("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \ + null, \ + new/datum/stack_recipe/dangerous("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe/dangerous("Xenomorph Statue", /obj/structure/statue/plasma/xeno, 5, one_per_turf = 1, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(bananium_recipes, list ( \ + new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \ + null, \ + new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("bananium computer frame", /obj/structure/computerframe/HONKputer, 50, time = 25, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("bananium grenade casing", /obj/item/grenade/bananade/casing, 4, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(tranquillite_recipes, list ( \ + new/datum/stack_recipe("invisible wall", /obj/structure/barricade/mime, 5, one_per_turf = 1, on_floor = 1, time = 50), \ + null, \ + new/datum/stack_recipe("silent tile", /obj/item/stack/tile/mineral/tranquillite, 1, 4, 20), \ + null, \ + new/datum/stack_recipe("Mime Statue", /obj/structure/statue/tranquillite/mime, 5, one_per_turf = 1, on_floor = 1), \ + )) + +GLOBAL_LIST_INIT(abductor_recipes, list ( \ + new/datum/stack_recipe("alien bed", /obj/structure/bed/abductor, 2, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("alien locker", /obj/structure/closet/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("alien table frame", /obj/structure/table_frame/abductor, 1, time = 15, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("alien airlock assembly", /obj/structure/door_assembly/door_assembly_abductor, 4, time = 20, one_per_turf = 1, on_floor = 1), \ + null, \ + new/datum/stack_recipe("alien floor tile", /obj/item/stack/tile/mineral/abductor, 1, 4, 20), \ + )) + +GLOBAL_LIST_INIT(adamantine_recipes, list( + new /datum/stack_recipe("incomplete servant golem shell", /obj/item/golem_shell/servant, req_amount = 1, res_amount = 1), \ + )) + +GLOBAL_LIST_INIT(snow_recipes, list( + new/datum/stack_recipe("snowman", /obj/structure/snowman, 5, one_per_turf = 1, on_floor = 1), \ + new/datum/stack_recipe("Snowball", /obj/item/snowball, 1) + )) + +/obj/item/stack/sheet/mineral + force = 5 + throwforce = 5 + throw_speed = 3 + +/obj/item/stack/sheet/mineral/New() + ..() + pixel_x = rand(0,4)-4 + pixel_y = rand(0,4)-4 + +/obj/item/stack/sheet/mineral/sandstone + name = "sandstone brick" + desc = "This appears to be a combination of both sand and stone." + singular_name = "sandstone brick" + icon_state = "sheet-sandstone" + throw_range = 5 + origin_tech = "materials=1" + sheettype = "sandstone" + materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + +/obj/item/stack/sheet/mineral/sandstone/New() + ..() + recipes = GLOB.sandstone_recipes + +/* + * Sandbags + */ + +/obj/item/stack/sheet/mineral/sandbags + name = "sandbags" + icon_state = "sandbags" + singular_name = "sandbag" + layer = LOW_ITEM_LAYER + merge_type = /obj/item/stack/sheet/mineral/sandbags + +GLOBAL_LIST_INIT(sandbag_recipes, list ( \ + new/datum/stack_recipe("sandbags", /obj/structure/barricade/sandbags, 1, time = 25, one_per_turf = 1, on_floor = 1), \ + )) + +/obj/item/stack/sheet/mineral/sandbags/New() + recipes = GLOB.sandbag_recipes + ..() + +/obj/item/emptysandbag + name = "empty sandbag" + desc = "A bag to be filled with sand." + icon = 'icons/obj/items.dmi' + icon_state = "sandbag" + w_class = WEIGHT_CLASS_TINY + +/obj/item/emptysandbag/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/ore/glass)) + var/obj/item/stack/ore/glass/G = I + to_chat(user, "You fill the sandbag.") + var/obj/item/stack/sheet/mineral/sandbags/S = new /obj/item/stack/sheet/mineral/sandbags(drop_location()) + qdel(src) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(S) + G.use(1) + else + return ..() + +/obj/item/stack/sheet/mineral/diamond + name = "diamond" + icon_state = "sheet-diamond" + singular_name = "diamond" + origin_tech = "materials=6" + sheettype = "diamond" + materials = list(MAT_DIAMOND=MINERAL_MATERIAL_AMOUNT) + point_value = 25 + +/obj/item/stack/sheet/mineral/diamond/New() + ..() + recipes = GLOB.diamond_recipes + +/obj/item/stack/sheet/mineral/uranium + name = "uranium" + icon_state = "sheet-uranium" + singular_name = "uranium sheet" + origin_tech = "materials=5" + sheettype = "uranium" + materials = list(MAT_URANIUM=MINERAL_MATERIAL_AMOUNT) + point_value = 20 + +/obj/item/stack/sheet/mineral/uranium/New() + ..() + recipes = GLOB.uranium_recipes + +/obj/item/stack/sheet/mineral/plasma + name = "solid plasma" + icon_state = "sheet-plasma" + singular_name = "plasma sheet" + origin_tech = "plasmatech=2;materials=2" + sheettype = "plasma" + materials = list(MAT_PLASMA=MINERAL_MATERIAL_AMOUNT) + resistance_flags = FLAMMABLE + max_integrity = 100 + point_value = 20 + +/obj/item/stack/sheet/mineral/plasma/New() + ..() + recipes = GLOB.plasma_recipes + +/obj/item/stack/sheet/mineral/plasma/welder_act(mob/user, obj/item/I) + if(I.use_tool(src, user, volume = I.tool_volume)) + message_admins("Plasma sheets ignited by [key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) in ([x],[y],[z] - JMP)",0,1) + log_game("Plasma sheets ignited by [key_name(user)] in ([x],[y],[z])") + investigate_log("was ignited by [key_name(user)]","atmos") + fire_act() + +/obj/item/stack/sheet/mineral/plasma/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + ..() + atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, amount*10) + qdel(src) + +/obj/item/stack/sheet/mineral/gold + name = "gold" + icon_state = "sheet-gold" + singular_name = "gold bar" + origin_tech = "materials=4" + sheettype = "gold" + materials = list(MAT_GOLD=MINERAL_MATERIAL_AMOUNT) + point_value = 20 + +/obj/item/stack/sheet/mineral/gold/New() + ..() + recipes = GLOB.gold_recipes + +/obj/item/stack/sheet/mineral/silver + name = "silver" + icon_state = "sheet-silver" + singular_name = "silver bar" + origin_tech = "materials=4" + sheettype = "silver" + materials = list(MAT_SILVER=MINERAL_MATERIAL_AMOUNT) + point_value = 20 + +/obj/item/stack/sheet/mineral/silver/New() + ..() + recipes = GLOB.silver_recipes + +/obj/item/stack/sheet/mineral/bananium + name = "bananium" + icon_state = "sheet-clown" + singular_name = "bananium sheet" + origin_tech = "materials=4" + sheettype = "bananium" + materials = list(MAT_BANANIUM=MINERAL_MATERIAL_AMOUNT) + point_value = 50 + +/obj/item/stack/sheet/mineral/bananium/New(loc, amount=null) + ..() + recipes = GLOB.bananium_recipes + +/obj/item/stack/sheet/mineral/tranquillite + name = "tranquillite" + icon_state = "sheet-mime" + singular_name = "beret" + origin_tech = "materials=4" + sheettype = "tranquillite" + materials = list(MAT_TRANQUILLITE=MINERAL_MATERIAL_AMOUNT) + wall_allowed = FALSE //no tranquilite walls in code + point_value = 50 + +/obj/item/stack/sheet/mineral/tranquillite/New(loc, amount=null) + ..() + recipes = GLOB.tranquillite_recipes + +/* + * Titanium + */ +/obj/item/stack/sheet/mineral/titanium + name = "titanium" + icon_state = "sheet-titanium" + item_state = "sheet-metal" + singular_name = "titanium sheet" + force = 5 + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 1 + throw_range = 3 + sheettype = "titanium" + materials = list(MAT_TITANIUM=MINERAL_MATERIAL_AMOUNT) + point_value = 20 + +var/global/list/datum/stack_recipe/titanium_recipes = list ( + new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), + new/datum/stack_recipe("surgical tray", /obj/structure/table/tray, 2, one_per_turf = 1, on_floor = 1), + ) + +/obj/item/stack/sheet/mineral/titanium/New(loc, amount=null) + recipes = titanium_recipes + ..() + +/obj/item/stack/sheet/mineral/titanium/fifty + amount = 50 + + +/* + * Plastitanium + */ +/obj/item/stack/sheet/mineral/plastitanium + name = "plastitanium" + icon_state = "sheet-plastitanium" + item_state = "sheet-metal" + singular_name = "plastitanium sheet" + force = 5 + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 1 + throw_range = 3 + sheettype = "plastitanium" + materials = list(MAT_TITANIUM=2000, MAT_PLASMA=2000) + point_value = 45 + +var/global/list/datum/stack_recipe/plastitanium_recipes = list ( + new/datum/stack_recipe("plas-titanium tile", /obj/item/stack/tile/mineral/plastitanium, 1, 4, 20), + ) + +/obj/item/stack/sheet/mineral/plastitanium/New(loc, amount=null) + recipes = plastitanium_recipes + ..() + +/obj/item/stack/sheet/mineral/enruranium + name = "enriched uranium" + icon_state = "sheet-enruranium" + origin_tech = "materials=6" + materials = list(MAT_URANIUM=3000) + +//Alien Alloy +/obj/item/stack/sheet/mineral/abductor + name = "alien alloy" + icon = 'icons/obj/abductor.dmi' + icon_state = "sheet-abductor" + singular_name = "alien alloy sheet" + force = 5 + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 1 + origin_tech = "materials=6;abductor=1" + sheettype = "abductor" + +/obj/item/stack/sheet/mineral/abductor/New(loc, amount=null) + recipes = GLOB.abductor_recipes + ..() + +/obj/item/stack/sheet/mineral/adamantine + name = "adamantine" + desc = "A strange mineral used in the construction of sentient golems." + icon_state = "sheet-adamantine" + singular_name = "adamantine sheet" + origin_tech = "materials=5" + merge_type = /obj/item/stack/sheet/mineral/adamantine + wall_allowed = FALSE + +/obj/item/stack/sheet/mineral/adamantine/New(loc, amount = null) + recipes = GLOB.adamantine_recipes + ..() + +/* + * Snow + */ +/obj/item/stack/sheet/mineral/snow + name = "snow" + icon_state = "sheet-snow" + item_state = "sheet-snow" + singular_name = "snow block" + force = 1 + throwforce = 2 + merge_type = /obj/item/stack/sheet/mineral/snow + +/obj/item/stack/sheet/mineral/snow/New(loc, amount = null) + recipes = GLOB.snow_recipes + ..() diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 4874bbd3b94d..5644c565114f 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -13,7 +13,7 @@ /* * Metal */ -var/global/list/datum/stack_recipe/metal_recipes = list( +GLOBAL_LIST_INIT(metal_recipes, list( new /datum/stack_recipe("stool", /obj/structure/chair/stool, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("chair", /obj/structure/chair, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("shuttle seat", /obj/structure/chair/comfy/shuttle, 2, one_per_turf = 1, on_floor = 1), @@ -94,7 +94,7 @@ var/global/list/datum/stack_recipe/metal_recipes = list( new /datum/stack_recipe("intercom frame", /obj/item/mounted/frame/intercom, 2), new /datum/stack_recipe("extinguisher cabinet frame", /obj/item/mounted/frame/extinguisher, 2), null -) +)) /obj/item/stack/sheet/metal name = "metal" @@ -124,13 +124,13 @@ var/global/list/datum/stack_recipe/metal_recipes = list( qdel(src) /obj/item/stack/sheet/metal/New(var/loc, var/amount=null) - recipes = metal_recipes + recipes = GLOB.metal_recipes return ..() /* * Plasteel */ -var/global/list/datum/stack_recipe/plasteel_recipes = list( +GLOBAL_LIST_INIT(plasteel_recipes, list( new /datum/stack_recipe("AI core", /obj/structure/AIcore, 4, time = 50, one_per_turf = 1), new /datum/stack_recipe("bomb assembly", /obj/machinery/syndicatebomb/empty, 3, time = 50), new /datum/stack_recipe("Surgery Table", /obj/machinery/optable, 5, time = 50, one_per_turf = 1, on_floor = 1), @@ -141,7 +141,7 @@ var/global/list/datum/stack_recipe/plasteel_recipes = list( new /datum/stack_recipe("high security airlock assembly", /obj/structure/door_assembly/door_assembly_highsecurity, 6, time = 50, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("vault door assembly", /obj/structure/door_assembly/door_assembly_vault, 8, time = 50, one_per_turf = 1, on_floor = 1), )), -) +)) /obj/item/stack/sheet/plasteel name = "plasteel" @@ -159,13 +159,13 @@ var/global/list/datum/stack_recipe/plasteel_recipes = list( point_value = 23 /obj/item/stack/sheet/plasteel/New(var/loc, var/amount=null) - recipes = plasteel_recipes + recipes = GLOB.plasteel_recipes return ..() /* * Wood */ -var/global/list/datum/stack_recipe/wood_recipes = list( +GLOBAL_LIST_INIT(wood_recipes, list( new /datum/stack_recipe("wooden sandals", /obj/item/clothing/shoes/sandal, 1), new /datum/stack_recipe("wood floor tile", /obj/item/stack/tile/wood, 1, 4, 20), new /datum/stack_recipe("wood table frame", /obj/structure/table_frame/wood, 2, time = 10), \ @@ -181,7 +181,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list( new /datum/stack_recipe("rifle stock", /obj/item/weaponcrafting/stock, 10, time = 40), new /datum/stack_recipe("wooden door", /obj/structure/mineral_door/wood, 10, time = 20, one_per_turf = 1, on_floor = 1), new /datum/stack_recipe("coffin", /obj/structure/closet/coffin, 5, time = 15, one_per_turf = 1, on_floor = 1), - new/datum/stack_recipe("display case chassis", /obj/structure/displaycase_chassis, 5, one_per_turf = TRUE, on_floor = TRUE), + new /datum/stack_recipe("display case chassis", /obj/structure/displaycase_chassis, 5, one_per_turf = TRUE, on_floor = TRUE), new /datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), new /datum/stack_recipe("apiary", /obj/structure/beebox, 40, time = 50), new /datum/stack_recipe("honey frame", /obj/item/honey_frame, 5, time = 10), @@ -192,7 +192,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list( new /datum/stack_recipe("loom", /obj/structure/loom, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \ new /datum/stack_recipe("fermenting barrel", /obj/structure/fermenting_barrel, 30, time = 50), new /datum/stack_recipe("firebrand", /obj/item/match/firebrand, 2, time = 100) -) +)) /obj/item/stack/sheet/wood name = "wooden planks" @@ -206,13 +206,13 @@ var/global/list/datum/stack_recipe/wood_recipes = list( merge_type = /obj/item/stack/sheet/wood /obj/item/stack/sheet/wood/New(var/loc, var/amount=null) - recipes = wood_recipes + recipes = GLOB.wood_recipes return ..() /* * Cloth */ -var/global/list/datum/stack_recipe/cloth_recipes = list ( \ +GLOBAL_LIST_INIT(cloth_recipes, list ( \ new/datum/stack_recipe("white jumpsuit", /obj/item/clothing/under/color/white, 3), \ new/datum/stack_recipe("white shoes", /obj/item/clothing/shoes/white, 2), \ new/datum/stack_recipe("white scarf", /obj/item/clothing/accessory/scarf/white, 1), \ @@ -237,7 +237,7 @@ var/global/list/datum/stack_recipe/cloth_recipes = list ( \ new/datum/stack_recipe("white beanie", /obj/item/clothing/head/beanie, 2), \ null, \ new/datum/stack_recipe("blindfold", /obj/item/clothing/glasses/sunglasses/blindfold, 3), \ - ) + )) /obj/item/stack/sheet/cloth name = "cloth" @@ -251,7 +251,7 @@ var/global/list/datum/stack_recipe/cloth_recipes = list ( \ merge_type = /obj/item/stack/sheet/cloth /obj/item/stack/sheet/cloth/New(loc, amount=null) - recipes = cloth_recipes + recipes = GLOB.cloth_recipes ..() /obj/item/stack/sheet/cloth/ten @@ -303,7 +303,7 @@ GLOBAL_LIST_INIT(durathread_recipes, list ( \ /* * Cardboard */ -var/global/list/datum/stack_recipe/cardboard_recipes = list ( +GLOBAL_LIST_INIT(cardboard_recipes, list ( new /datum/stack_recipe("box", /obj/item/storage/box), new /datum/stack_recipe("large box", /obj/item/storage/box/large, 4), new /datum/stack_recipe("patch pack", /obj/item/storage/pill_bottle/patch_pack, 2), @@ -317,7 +317,7 @@ var/global/list/datum/stack_recipe/cardboard_recipes = list ( new /datum/stack_recipe("cardboard tube", /obj/item/c_tube), new /datum/stack_recipe("cardboard box", /obj/structure/closet/cardboard, 4), new/datum/stack_recipe("cardboard cutout", /obj/item/cardboard_cutout, 5), -) +)) /obj/item/stack/sheet/cardboard/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/stamp/clown) && !istype(loc, /obj/item/storage)) @@ -339,21 +339,21 @@ var/global/list/datum/stack_recipe/cardboard_recipes = list ( merge_type = /obj/item/stack/sheet/cardboard /obj/item/stack/sheet/cardboard/New(var/loc, var/amt = null) - recipes = cardboard_recipes + recipes = GLOB.cardboard_recipes return ..() /* * Runed Metal */ -var/global/list/datum/stack_recipe/cult = list ( \ +GLOBAL_LIST_INIT(cult_recipes, list ( \ new/datum/stack_recipe/cult("runed door", /obj/machinery/door/airlock/cult, 1, time = 50, one_per_turf = 1, on_floor = 1), new/datum/stack_recipe/cult("runed girder", /obj/structure/girder/cult, 1, time = 50, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe/cult("pylon", /obj/structure/cult/functional/pylon, 3, time = 40, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe/cult("forge", /obj/structure/cult/functional/forge, 5, time = 40, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe/cult("archives", /obj/structure/cult/functional/archives, 2, time = 40, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe/cult("altar", /obj/structure/cult/functional/altar, 5, time = 40, one_per_turf = 1, on_floor = 1), \ - ) + )) /obj/item/stack/sheet/runed_metal name = "runed metal" @@ -396,13 +396,13 @@ var/global/list/datum/stack_recipe/cult = list ( \ amount = 50 /obj/item/stack/sheet/runed_metal/New(var/loc, var/amount=null) - recipes = cult + recipes = GLOB.cult_recipes return ..() /* * Brass */ -var/global/list/datum/stack_recipe/brass_recipes = list (\ +GLOBAL_LIST_INIT(brass_recipes, list (\ new/datum/stack_recipe("wall gear", /obj/structure/clockwork/wall_gear, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \ null, new/datum/stack_recipe/window("brass windoor", /obj/machinery/door/window/clockwork, 2, time = 30, on_floor = TRUE, window_checks = TRUE), \ @@ -416,7 +416,7 @@ var/global/list/datum/stack_recipe/brass_recipes = list (\ new/datum/stack_recipe("brass obelisk", /obj/structure/clockwork/decorative/obelisk, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("tinkerers cache", /obj/structure/clockwork/decorative/tinkerers_cache, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ new/datum/stack_recipe("brass relay", /obj/structure/clockwork/decorative/relay, 3, time = 5, one_per_turf = TRUE, on_floor = TRUE), \ - ) + )) /obj/item/stack/tile/brass name = "brass" @@ -436,7 +436,7 @@ var/global/list/datum/stack_recipe/brass_recipes = list (\ qdel(src) /obj/item/stack/tile/brass/New(loc, amount=null) - recipes = brass_recipes + recipes = GLOB.brass_recipes . = ..() pixel_x = 0 pixel_y = 0 diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm index 13774b0db4b2..63223a4c6f4e 100644 --- a/code/game/objects/items/stacks/sheets/sheets.dm +++ b/code/game/objects/items/stacks/sheets/sheets.dm @@ -1,18 +1,18 @@ -/obj/item/stack/sheet - name = "sheet" - w_class = WEIGHT_CLASS_NORMAL - force = 5 - throwforce = 5 - max_amount = 50 - throw_speed = 1 - throw_range = 3 - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "smashed") - var/perunit = MINERAL_MATERIAL_AMOUNT - var/sheettype = null //this is used for girders in the creation of walls/false walls - var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity. - - var/created_window = null //apparently glass sheets don't share a base type for glass specifically, so each had to define these vars individually - var/full_window = null //moving the var declaration to here so this can be checked cleaner until someone is willing to make them share a base type properly - usesound = 'sound/items/deconstruct.ogg' - toolspeed = 1 - var/wall_allowed = TRUE //determines if sheet can be used in wall construction or not. +/obj/item/stack/sheet + name = "sheet" + w_class = WEIGHT_CLASS_NORMAL + force = 5 + throwforce = 5 + max_amount = 50 + throw_speed = 1 + throw_range = 3 + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "smashed") + var/perunit = MINERAL_MATERIAL_AMOUNT + var/sheettype = null //this is used for girders in the creation of walls/false walls + var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity. + + var/created_window = null //apparently glass sheets don't share a base type for glass specifically, so each had to define these vars individually + var/full_window = null //moving the var declaration to here so this can be checked cleaner until someone is willing to make them share a base type properly + usesound = 'sound/items/deconstruct.ogg' + toolspeed = 1 + var/wall_allowed = TRUE //determines if sheet can be used in wall construction or not. diff --git a/code/game/objects/items/stacks/tiles/tile_mineral.dm b/code/game/objects/items/stacks/tiles/tile_mineral.dm index c568e66c86a6..1c499a7d6af4 100644 --- a/code/game/objects/items/stacks/tiles/tile_mineral.dm +++ b/code/game/objects/items/stacks/tiles/tile_mineral.dm @@ -27,9 +27,9 @@ mineralType = "uranium" materials = list(MAT_URANIUM=500) -var/global/list/datum/stack_recipe/gold_tile_recipes = list ( \ +GLOBAL_LIST_INIT(gold_tile_recipes, list ( \ new/datum/stack_recipe("fancy gold tile", /obj/item/stack/tile/mineral/gold/fancy, max_res_amount = 20), \ - ) + )) /obj/item/stack/tile/mineral/gold name = "gold tile" @@ -42,11 +42,11 @@ var/global/list/datum/stack_recipe/gold_tile_recipes = list ( \ /obj/item/stack/tile/mineral/gold/New(loc, amount=null) ..() - recipes = gold_tile_recipes + recipes = GLOB.gold_tile_recipes -var/global/list/datum/stack_recipe/goldfancy_tile_recipes = list ( \ +GLOBAL_LIST_INIT(goldfancy_tile_recipes, list ( \ new/datum/stack_recipe("regular gold tile", /obj/item/stack/tile/mineral/gold, max_res_amount = 20), \ - ) + )) /obj/item/stack/tile/mineral/gold/fancy icon_state = "tile_goldfancy" @@ -54,11 +54,11 @@ var/global/list/datum/stack_recipe/goldfancy_tile_recipes = list ( \ /obj/item/stack/tile/mineral/gold/fancy/New(loc, amount=null) ..() - recipes = goldfancy_tile_recipes + recipes = GLOB.goldfancy_tile_recipes -var/global/list/datum/stack_recipe/silver_tile_recipes = list ( \ +GLOBAL_LIST_INIT(silver_tile_recipes, list ( \ new/datum/stack_recipe("fancy silver tile", /obj/item/stack/tile/mineral/silver/fancy, max_res_amount = 20), \ - ) + )) /obj/item/stack/tile/mineral/silver name = "silver tile" @@ -71,11 +71,11 @@ var/global/list/datum/stack_recipe/silver_tile_recipes = list ( \ /obj/item/stack/tile/mineral/silver/New(loc, amount=null) ..() - recipes = silver_tile_recipes + recipes = GLOB.silver_tile_recipes -var/global/list/datum/stack_recipe/silverfancy_tile_recipes = list ( \ +GLOBAL_LIST_INIT(silverfancy_tile_recipes, list ( \ new/datum/stack_recipe("regular silver tile", /obj/item/stack/tile/mineral/silver, max_res_amount = 20), \ - ) + )) /obj/item/stack/tile/mineral/silver/fancy icon_state = "tile_silverfancy" @@ -83,7 +83,7 @@ var/global/list/datum/stack_recipe/silverfancy_tile_recipes = list ( \ /obj/item/stack/tile/mineral/silver/fancy/New(loc, amount=null) ..() - recipes = silverfancy_tile_recipes + recipes = GLOB.silverfancy_tile_recipes /obj/item/stack/tile/mineral/diamond name = "diamond tile" diff --git a/code/game/objects/items/tools/multitool.dm b/code/game/objects/items/tools/multitool.dm index 22c9861da6ed..c31f0666f505 100644 --- a/code/game/objects/items/tools/multitool.dm +++ b/code/game/objects/items/tools/multitool.dm @@ -73,13 +73,13 @@ /obj/item/multitool/ai_detect/proc/multitool_detect() var/turf/our_turf = get_turf(src) - for(var/mob/living/silicon/ai/AI in ai_list) + for(var/mob/living/silicon/ai/AI in GLOB.ai_list) if(AI.cameraFollow == src) detect_state = PROXIMITY_ON_SCREEN break - if(!detect_state && cameranet.chunkGenerated(our_turf.x, our_turf.y, our_turf.z)) - var/datum/camerachunk/chunk = cameranet.getCameraChunk(our_turf.x, our_turf.y, our_turf.z) + if(!detect_state && GLOB.cameranet.chunkGenerated(our_turf.x, our_turf.y, our_turf.z)) + var/datum/camerachunk/chunk = GLOB.cameranet.getCameraChunk(our_turf.x, our_turf.y, our_turf.z) if(chunk) if(chunk.seenby.len) for(var/mob/camera/aiEye/A in chunk.seenby) diff --git a/code/game/objects/items/tools/tool_behaviour.dm b/code/game/objects/items/tools/tool_behaviour.dm index c937f80c30d7..986e3a981b2d 100644 --- a/code/game/objects/items/tools/tool_behaviour.dm +++ b/code/game/objects/items/tools/tool_behaviour.dm @@ -16,11 +16,11 @@ var/datum/callback/tool_check = CALLBACK(src, .proc/tool_check_callback, user, target, amount, extra_checks) if(ismob(target)) - if(!do_mob(user, target, delay, extra_checks=tool_check)) + if(!do_mob(user, target, delay, extra_checks = list(tool_check))) return else - if(!do_after(user, delay, target=target, extra_checks=tool_check)) + if(!do_after(user, delay, target=target, extra_checks = list(tool_check))) return else // Invoke the extra checks once, just in case. @@ -57,4 +57,4 @@ // Used in a callback that is passed by use_tool into do_after call. Do not override, do not call manually. /obj/item/proc/tool_check_callback(mob/living/user, atom/target, amount, datum/callback/extra_checks) - return tool_use_check(user, amount) && (!extra_checks || extra_checks.Invoke()) + return tool_use_check(user, amount) && (extra_checks && !extra_checks.Invoke()) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 2fe83a08398e..8e51a6760b70 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1,1783 +1,1783 @@ -/* Toys! - * Contains: - * Balloons - * Fake telebeacon - * Fake singularity - * Toy swords - * Toy mechs - * Snap pops - * Water flower - * Toy Nuke - * Card Deck - * Therapy dolls - * Toddler doll - * Inflatable duck - * Foam armblade - * Mini Gibber - * Toy xeno - * Toy chainsaws - * Action Figures - */ - - -/obj/item/toy - throwforce = 0 - throw_speed = 4 - throw_range = 20 - force = 0 - - -/* - * Balloons - */ -/obj/item/toy/balloon - name = "water balloon" - desc = "A translucent balloon. There's nothing in it." - icon = 'icons/obj/toy.dmi' - icon_state = "waterballoon-e" - item_state = "balloon-empty" - -/obj/item/toy/balloon/New() - ..() - create_reagents(10) - -/obj/item/toy/balloon/attack(mob/living/carbon/human/M as mob, mob/user as mob) - return - -/obj/item/toy/balloon/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return - if(istype(A, /obj/structure/reagent_dispensers)) - var/obj/structure/reagent_dispensers/RD = A - if(RD.reagents.total_volume <= 0) - to_chat(user, "[RD] is empty.") - else if(reagents.total_volume >= 10) - to_chat(user, "[src] is full.") - else - A.reagents.trans_to(src, 10) - to_chat(user, "You fill the balloon with the contents of [A].") - desc = "A translucent balloon with some form of liquid sloshing around in it." - update_icon() - -/obj/item/toy/balloon/wash(mob/user, atom/source) - if(reagents.total_volume < 10) - reagents.add_reagent("water", min(10-reagents.total_volume, 10)) - to_chat(user, "You fill the balloon from the [source].") - desc = "A translucent balloon with some form of liquid sloshing around in it." - update_icon() - return - -/obj/item/toy/balloon/attackby(obj/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/reagent_containers/glass) || istype(O, /obj/item/reagent_containers/food/drinks/drinkingglass)) - if(O.reagents) - if(O.reagents.total_volume < 1) - to_chat(user, "The [O] is empty.") - else if(O.reagents.total_volume >= 1) - if(O.reagents.has_reagent("facid", 1)) - to_chat(user, "The acid chews through the balloon!") - O.reagents.reaction(user) - qdel(src) - else - desc = "A translucent balloon with some form of liquid sloshing around in it." - to_chat(user, "You fill the balloon with the contents of [O].") - O.reagents.trans_to(src, 10) - update_icon() - return - -/obj/item/toy/balloon/throw_impact(atom/hit_atom) - if(reagents.total_volume >= 1) - visible_message("The [src] bursts!","You hear a pop and a splash.") - reagents.reaction(get_turf(hit_atom)) - for(var/atom/A in get_turf(hit_atom)) - reagents.reaction(A) - icon_state = "burst" - spawn(5) - if(src) - qdel(src) - return - -/obj/item/toy/balloon/update_icon() - if(src.reagents.total_volume >= 1) - icon_state = "waterballoon" - item_state = "balloon" - else - icon_state = "waterballoon-e" - item_state = "balloon-empty" - -/obj/item/toy/syndicateballoon - name = "syndicate balloon" - desc = "There is a tag on the back that reads \"FUK NT!11!\"." - throwforce = 0 - throw_speed = 4 - throw_range = 20 - force = 0 - icon_state = "syndballoon" - item_state = "syndballoon" - w_class = WEIGHT_CLASS_BULKY - var/lastused = null - -/obj/item/toy/syndicateballoon/attack_self(mob/user) - if(world.time - lastused < CLICK_CD_MELEE) - return - var/playverb = pick("bat [src]", "tug on [src]'s string", "play with [src]") - user.visible_message("[user] plays with [src].", "You [playverb].") - lastused = world.time - -/* - * Fake telebeacon - */ -/obj/item/toy/blink - name = "electronic blink toy game" - desc = "Blink. Blink. Blink. Ages 8 and up." - icon = 'icons/obj/radio.dmi' - icon_state = "beacon" - item_state = "signaler" - -/* - * Fake singularity - */ -/obj/item/toy/spinningtoy - name = "Gravitational Singularity" - desc = "\"Singulo\" brand spinning toy." - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - -/* - * Toy swords - */ -/obj/item/toy/sword - name = "toy sword" - desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up." - icon_state = "sword0" - item_state = "sword0" - var/active = FALSE - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("attacked", "struck", "hit") - -/obj/item/toy/sword/attack_self(mob/user) - active = !active - if(active) - to_chat(user, "You extend the plastic blade with a quick flick of your wrist.") - playsound(user, 'sound/weapons/saberon.ogg', 20, 1) - icon_state = "swordblue" - item_state = "swordblue" - w_class = WEIGHT_CLASS_BULKY - else - to_chat(user, "You push the plastic blade back down into the handle.") - playsound(user, 'sound/weapons/saberoff.ogg', 20, 1) - icon_state = "sword0" - item_state = "sword0" - w_class = WEIGHT_CLASS_SMALL - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - add_fingerprint(user) - return - -// Copied from /obj/item/melee/energy/sword/attackby -/obj/item/toy/sword/attackby(obj/item/W, mob/living/user, params) - ..() - if(istype(W, /obj/item/toy/sword)) - if(W == src) - to_chat(user, "You try to attach the end of the plastic sword to... itself. You're not very smart, are you?") - if(ishuman(user)) - user.adjustBrainLoss(10) - else if((W.flags & NODROP) || (flags & NODROP)) - to_chat(user, "\the [flags & NODROP ? src : W] is stuck to your hand, you can't attach it to \the [flags & NODROP ? W : src]!") - else - to_chat(user, "You attach the ends of the two plastic swords, making a single double-bladed toy! You're fake-cool.") - new /obj/item/twohanded/dualsaber/toy(user.loc) - user.unEquip(W) - user.unEquip(src) - qdel(W) - qdel(src) - -/* - * Subtype of Double-Bladed Energy Swords - */ -/obj/item/twohanded/dualsaber/toy - name = "double-bladed toy sword" - desc = "A cheap, plastic replica of TWO energy swords. Double the fun!" - force = 0 - throwforce = 0 - throw_speed = 3 - throw_range = 5 - force_unwielded = 0 - force_wielded = 0 - origin_tech = null - attack_verb = list("attacked", "struck", "hit") - brightness_on = 0 - sharp_when_wielded = FALSE // It's a toy - -/obj/item/twohanded/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return 0 - -/obj/item/twohanded/dualsaber/toy/IsReflect()//Stops Toy Dualsabers from reflecting energy projectiles - return 0 - -/obj/item/toy/katana - name = "replica katana" - desc = "Woefully underpowered in D20." - icon_state = "katana" - item_state = "katana" - flags = CONDUCT - slot_flags = SLOT_BELT | SLOT_BACK - force = 5 - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("attacked", "slashed", "stabbed", "sliced") - hitsound = 'sound/weapons/bladeslice.ogg' - -/obj/item/toy/katana/suicide_act(mob/user) - var/dmsg = pick("[user] tries to stab \the [src] into [user.p_their()] abdomen, but it shatters! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.","[user] tries to stab \the [src] into [user.p_their()] abdomen, but \the [src] bends and breaks in half! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.","[user] tries to slice [user.p_their()] own throat, but the plastic blade has no sharpness, causing [user.p_them()] to lose [user.p_their()] balance, slip over, and break [user.p_their()] neck with a loud snap!") - user.visible_message("[dmsg] It looks like [user.p_theyre()] trying to commit suicide.") - return BRUTELOSS - - -/* - * Snap pops viral shit - */ -/obj/item/toy/snappop/virus - name = "unstable goo" - desc = "Your palm is oozing this stuff!" - icon = 'icons/mob/slimes.dmi' - icon_state = "red slime extract" - throwforce = 5.0 - throw_speed = 10 - throw_range = 30 - w_class = WEIGHT_CLASS_TINY - - -/obj/item/toy/snappop/virus/throw_impact(atom/hit_atom) - ..() - do_sparks(3, 1, src) - new /obj/effect/decal/cleanable/ash(src.loc) - visible_message("The [name] explodes!","You hear a bang!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) - -/* - * Snap pops - */ -/obj/item/toy/snappop - name = "snap pop" - desc = "Wow!" - icon = 'icons/obj/toy.dmi' - icon_state = "snappop" - w_class = WEIGHT_CLASS_TINY - var/ash_type = /obj/effect/decal/cleanable/ash - -/obj/item/toy/snappop/proc/pop_burst(var/n=3, var/c=1) - do_sparks(n, c, src) - new ash_type(loc) - visible_message("[src] explodes!", - "You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) - -/obj/item/toy/snappop/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - ..() - pop_burst() - -/obj/item/toy/snappop/throw_impact(atom/hit_atom) - ..() - pop_burst() - -/obj/item/toy/snappop/Crossed(H as mob|obj, oldloc) - if(ishuman(H) || issilicon(H)) //i guess carp and shit shouldn't set them off - var/mob/living/carbon/M = H - if(issilicon(H) || M.m_intent == MOVE_INTENT_RUN) - to_chat(M, "You step on the snap pop!") - pop_burst(2, 0) - -/obj/item/toy/snappop/phoenix - name = "phoenix snap pop" - desc = "Wow! And wow! And wow!" - ash_type = /obj/effect/decal/cleanable/ash/snappop_phoenix - -/obj/effect/decal/cleanable/ash/snappop_phoenix - var/respawn_time = 300 - -/obj/effect/decal/cleanable/ash/snappop_phoenix/Initialize(mapload) - . = ..() - addtimer(CALLBACK(src, .proc/respawn), respawn_time) - -/obj/effect/decal/cleanable/ash/snappop_phoenix/proc/respawn() - new /obj/item/toy/snappop/phoenix(get_turf(src)) - qdel(src) - - -/* - * Mech prizes - */ -/obj/item/toy/prize - icon = 'icons/obj/toy.dmi' - icon_state = "ripleytoy" - var/cooldown = 0 - -//all credit to skasi for toy mech fun ideas -/obj/item/toy/prize/attack_self(mob/user as mob) - if(cooldown < world.time - 8) - to_chat(user, "You play with [src].") - playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) - cooldown = world.time - -/obj/item/toy/prize/attack_hand(mob/user as mob) - if(loc == user) - if(cooldown < world.time - 8) - to_chat(user, "You play with [src].") - playsound(user, 'sound/mecha/mechturn.ogg', 20, 1) - cooldown = world.time - return - ..() - -/obj/random/mech - name = "Random Mech Prize" - desc = "This is a random prize" - icon = 'icons/obj/toy.dmi' - icon_state = "ripleytoy" - -/obj/random/mech/item_to_spawn() - return pick(subtypesof(/obj/item/toy/prize)) //exclude the base type. - -/obj/item/toy/prize/ripley - name = "toy ripley" - desc = "Mini-Mecha action figure! Collect them all! 1/11." - -/obj/item/toy/prize/fireripley - name = "toy firefighting ripley" - desc = "Mini-Mecha action figure! Collect them all! 2/11." - icon_state = "fireripleytoy" - -/obj/item/toy/prize/deathripley - name = "toy deathsquad ripley" - desc = "Mini-Mecha action figure! Collect them all! 3/11." - icon_state = "deathripleytoy" - -/obj/item/toy/prize/gygax - name = "toy gygax" - desc = "Mini-Mecha action figure! Collect them all! 4/11." - icon_state = "gygaxtoy" - -/obj/item/toy/prize/durand - name = "toy durand" - desc = "Mini-Mecha action figure! Collect them all! 5/11." - icon_state = "durandprize" - -/obj/item/toy/prize/honk - name = "toy H.O.N.K." - desc = "Mini-Mecha action figure! Collect them all! 6/11." - icon_state = "honkprize" - -/obj/item/toy/prize/marauder - name = "toy marauder" - desc = "Mini-Mecha action figure! Collect them all! 7/11." - icon_state = "marauderprize" - -/obj/item/toy/prize/seraph - name = "toy seraph" - desc = "Mini-Mecha action figure! Collect them all! 8/11." - icon_state = "seraphprize" - -/obj/item/toy/prize/mauler - name = "toy mauler" - desc = "Mini-Mecha action figure! Collect them all! 9/11." - icon_state = "maulerprize" - -/obj/item/toy/prize/odysseus - name = "toy odysseus" - desc = "Mini-Mecha action figure! Collect them all! 10/11." - icon_state = "odysseusprize" - -/obj/item/toy/prize/phazon - name = "toy phazon" - desc = "Mini-Mecha action figure! Collect them all! 11/11." - icon_state = "phazonprize" - - - -/* -|| A Deck of Cards for playing various games of chance || -*/ - - - -obj/item/toy/cards - resistance_flags = FLAMMABLE - max_integrity = 50 - var/parentdeck = null - var/deckstyle = "nanotrasen" - var/card_hitsound = null - var/card_force = 0 - var/card_throwforce = 0 - var/card_throw_speed = 4 - var/card_throw_range = 20 - var/list/card_attack_verb = list("attacked") - -obj/item/toy/cards/New() - ..() - -obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck - if(!istype(sourceobj)) - return - -obj/item/toy/cards/deck - name = "deck of cards" - desc = "A deck of space-grade playing cards." - icon = 'icons/obj/toy.dmi' - deckstyle = "nanotrasen" - icon_state = "deck_nanotrasen_full" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - var/list/cards = list() - -obj/item/toy/cards/deck/New() - ..() - icon_state = "deck_[deckstyle]_full" - for(var/i in 2 to 10) - cards += "[i] of Hearts" - cards += "[i] of Spades" - cards += "[i] of Clubs" - cards += "[i] of Diamonds" - cards += "King of Hearts" - cards += "King of Spades" - cards += "King of Clubs" - cards += "King of Diamonds" - cards += "Queen of Hearts" - cards += "Queen of Spades" - cards += "Queen of Clubs" - cards += "Queen of Diamonds" - cards += "Jack of Hearts" - cards += "Jack of Spades" - cards += "Jack of Clubs" - cards += "Jack of Diamonds" - cards += "Ace of Hearts" - cards += "Ace of Spades" - cards += "Ace of Clubs" - cards += "Ace of Diamonds" - -obj/item/toy/cards/deck/attack_hand(mob/user as mob) - var/choice = null - if(cards.len == 0) - icon_state = "deck_[deckstyle]_empty" - to_chat(user, "There are no more cards to draw.") - return - var/obj/item/toy/cards/singlecard/H = new/obj/item/toy/cards/singlecard(user.loc) - choice = cards[1] - H.cardname = choice - H.parentdeck = src - var/O = src - H.apply_card_vars(H,O) - cards -= choice - H.pickup(user) - user.put_in_active_hand(H) - visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") - update_icon() - -obj/item/toy/cards/deck/attack_self(mob/user as mob) - if(cooldown < world.time - 50) - cards = shuffle(cards) - playsound(user, 'sound/items/cardshuffle.ogg', 50, 1) - user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") - cooldown = world.time - -obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) - ..() - if(istype(C)) - if(C.parentdeck == src) - if(!user.unEquip(C)) - to_chat(user, "The card is stuck to your hand, you can't add it to the deck!") - return - cards += C.cardname - user.visible_message("[user] adds a card to the bottom of the deck.","You add the card to the bottom of the deck.") - qdel(C) - else - to_chat(user, "You can't mix cards from other decks.") - update_icon() - - -obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, params) - ..() - if(istype(C)) - if(C.parentdeck == src) - if(!user.unEquip(C)) - to_chat(user, "The hand of cards is stuck to your hand, you can't add it to the deck!") - return - cards += C.currenthand - user.visible_message("[user] puts [user.p_their()] hand of cards in the deck.", "You put the hand of cards in the deck.") - qdel(C) - else - to_chat(user, "You can't mix cards from other decks.") - update_icon() - -obj/item/toy/cards/deck/MouseDrop(atom/over_object) - var/mob/M = usr - if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) - return - if(Adjacent(usr)) - if(over_object == M && loc != M) - M.put_in_hands(src) - to_chat(usr, "You pick up the deck.") - - else if(istype(over_object, /obj/screen)) - switch(over_object.name) - if("l_hand") - if(!remove_item_from_storage(M)) - M.unEquip(src) - M.put_in_l_hand(src) - to_chat(usr, "You pick up the deck.") - if("r_hand") - if(!remove_item_from_storage(M)) - M.unEquip(src) - M.put_in_r_hand(src) - to_chat(usr, "You pick up the deck.") - else - to_chat(usr, "You can't reach it from here.") - -obj/item/toy/cards/deck/update_icon() - switch(cards.len) - if(0) - icon_state = "deck_[deckstyle]_empty" - if(1 to 10) - icon_state = "deck_[deckstyle]_low" - if(11 to 26) - icon_state = "deck_[deckstyle]_half" - else - icon_state = "deck_[deckstyle]_full" - -obj/item/toy/cards/cardhand - name = "hand of cards" - desc = "A number of cards not in a deck, customarily held in ones hand." - icon = 'icons/obj/toy.dmi' - icon_state = "nanotrasen_hand2" - w_class = WEIGHT_CLASS_TINY - var/list/currenthand = list() - var/choice = null - - -obj/item/toy/cards/cardhand/attack_self(mob/user as mob) - user.set_machine(src) - interact(user) - -obj/item/toy/cards/cardhand/interact(mob/user) - var/dat = "You have:
    " - for(var/t in currenthand) - dat += "A [t].
    " - dat += "Which card will you remove next?" - var/datum/browser/popup = new(user, "cardhand", "Hand of Cards", 400, 240) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.set_content(dat) - popup.open() - - -obj/item/toy/cards/cardhand/Topic(href, href_list) - if(..()) - return - if(usr.stat || !ishuman(usr) || !usr.canmove) - return - var/mob/living/carbon/human/cardUser = usr - var/O = src - if(href_list["pick"]) - if(cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) - var/choice = href_list["pick"] - var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc) - currenthand -= choice - C.parentdeck = src.parentdeck - C.cardname = choice - C.apply_card_vars(C,O) - C.pickup(cardUser) - cardUser.put_in_any_hand_if_possible(C) - cardUser.visible_message("[cardUser] draws a card from [cardUser.p_their()] hand.", "You take the [C.cardname] from your hand.") - - interact(cardUser) - update_icon() - if(currenthand.len == 1) - var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(src.loc) - N.parentdeck = src.parentdeck - N.cardname = src.currenthand[1] - N.apply_card_vars(N,O) - cardUser.unEquip(src) - N.pickup(cardUser) - cardUser.put_in_any_hand_if_possible(N) - to_chat(cardUser, "You also take [currenthand[1]] and hold it.") - cardUser << browse(null, "window=cardhand") - qdel(src) - return - -obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) - if(istype(C)) - if(C.parentdeck == parentdeck) - currenthand += C.cardname - user.unEquip(C) - user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [C.cardname] to your hand.") - interact(user) - update_icon() - qdel(C) - else - to_chat(user, "You can't mix cards from other decks.") - -obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) - ..() - newobj.deckstyle = sourceobj.deckstyle - newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added. - newobj.card_hitsound = sourceobj.card_hitsound - newobj.card_force = sourceobj.card_force - newobj.card_throwforce = sourceobj.card_throwforce - newobj.card_throw_speed = sourceobj.card_throw_speed - newobj.card_throw_range = sourceobj.card_throw_range - newobj.card_attack_verb = sourceobj.card_attack_verb - newobj.resistance_flags = sourceobj.resistance_flags - - -obj/item/toy/cards/singlecard - name = "card" - desc = "a card" - icon = 'icons/obj/toy.dmi' - icon_state = "singlecard_nanotrasen_down" - w_class = WEIGHT_CLASS_TINY - var/cardname = null - var/flipped = 0 - pixel_x = -5 - - -obj/item/toy/cards/singlecard/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0) - if(ishuman(user)) - var/mob/living/carbon/human/cardUser = user - if(cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) - cardUser.visible_message("[cardUser] checks [cardUser.p_their()] card.", "The card reads: [src.cardname]") - else - . += "You need to have the card in your hand to check it." - - -obj/item/toy/cards/singlecard/verb/Flip() - set name = "Flip Card" - set category = "Object" - set src in range(1) - if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) - return - if(!flipped) - flipped = 1 - if(cardname) - icon_state = "sc_[cardname]_[deckstyle]" - name = cardname - else - icon_state = "sc_Ace of Spades_[deckstyle]" - name = "What Card" - pixel_x = 5 - else - flipped = 0 - icon_state = "singlecard_down_[deckstyle]" - name = "card" - pixel_x = -5 - -obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/toy/cards/singlecard/)) - var/obj/item/toy/cards/singlecard/C = I - if(C.parentdeck == parentdeck) - var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(user.loc) - H.currenthand += C.cardname - H.currenthand += cardname - H.parentdeck = C.parentdeck - H.apply_card_vars(H,C) - user.unEquip(C) - H.pickup(user) - user.put_in_active_hand(H) - to_chat(user, "You combine the [C.cardname] and the [cardname] into a hand.") - qdel(C) - qdel(src) - else - to_chat(user, "You can't mix cards from other decks.") - - if(istype(I, /obj/item/toy/cards/cardhand/)) - var/obj/item/toy/cards/cardhand/H = I - if(H.parentdeck == parentdeck) - H.currenthand += cardname - user.unEquip(src) - user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [cardname] to your hand.") - H.interact(user) - H.update_icon() - qdel(src) - else - to_chat(user, "You can't mix cards from other decks.") - -obj/item/toy/cards/cardhand/update_icon() - switch(currenthand.len) - if(0 to 1) - return - if(2) - icon_state = "[deckstyle]_hand2" - if(3) - icon_state = "[deckstyle]_hand3" - if(4) - icon_state = "[deckstyle]_hand4" - else - icon_state = "[deckstyle]_hand5" - - -obj/item/toy/cards/singlecard/attack_self(mob/user) - if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) - return - Flip() - -obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) - ..() - newobj.deckstyle = sourceobj.deckstyle - newobj.icon_state = "singlecard_down_[deckstyle]" // Without this the card is invisible until flipped. It's an ugly hack, but it works. - newobj.card_hitsound = sourceobj.card_hitsound - newobj.hitsound = newobj.card_hitsound - newobj.card_force = sourceobj.card_force - newobj.force = newobj.card_force - newobj.card_throwforce = sourceobj.card_throwforce - newobj.throwforce = newobj.card_throwforce - newobj.card_throw_speed = sourceobj.card_throw_speed - newobj.throw_speed = newobj.card_throw_speed - newobj.card_throw_range = sourceobj.card_throw_range - newobj.throw_range = newobj.card_throw_range - newobj.card_attack_verb = sourceobj.card_attack_verb - newobj.attack_verb = newobj.card_attack_verb - - -/* -|| Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || -*/ - -obj/item/toy/cards/deck/syndicate - name = "suspicious looking deck of cards" - desc = "A deck of space-grade playing cards. They seem unusually rigid." - deckstyle = "syndicate" - card_hitsound = 'sound/weapons/bladeslice.ogg' - card_force = 5 - card_throwforce = 10 - card_throw_speed = 3 - card_throw_range = 20 - card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut") - resistance_flags = NONE - -/* -|| Custom card decks || -*/ -obj/item/toy/cards/deck/black - deckstyle = "black" - -obj/item/toy/cards/deck/syndicate/black - deckstyle = "black" - -/obj/item/toy/nuke - name = "\improper Nuclear Fission Explosive toy" - desc = "A plastic model of a Nuclear Fission Explosive." - icon = 'icons/obj/toy.dmi' - icon_state = "nuketoyidle" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - -/obj/item/toy/nuke/attack_self(mob/user) - if(cooldown < world.time) - cooldown = world.time + 1800 //3 minutes - user.visible_message("[user] presses a button on [src]", "You activate [src], it plays a loud noise!", "You hear the click of a button.") - spawn(5) //gia said so - icon_state = "nuketoy" - playsound(src, 'sound/machines/alarm.ogg', 100, 0, 0) - sleep(135) - icon_state = "nuketoycool" - sleep(cooldown - world.time) - icon_state = "nuketoyidle" - else - var/timeleft = (cooldown - world.time) - to_chat(user, "Nothing happens, and '[round(timeleft/10)]' appears on a small display.") - -/obj/item/toy/therapy - name = "therapy doll" - desc = "A toy for therapeutic and recreational purposes." - icon = 'icons/obj/toy.dmi' - icon_state = "therapyred" - item_state = "egg4" - w_class = WEIGHT_CLASS_TINY - var/cooldown = 0 - resistance_flags = FLAMMABLE - -/obj/item/toy/therapy/New() - ..() - if(item_color) - name = "[item_color] therapy doll" - desc += " This one is [item_color]." - icon_state = "therapy[item_color]" - -/obj/item/toy/therapy/attack_self(mob/user) - if(cooldown < world.time - 8) - to_chat(user, "You relieve some stress with \the [src].") - playsound(user, 'sound/items/squeaktoy.ogg', 20, 1) - cooldown = world.time - -/obj/random/therapy - name = "Random Therapy Doll" - desc = "This is a random therapy doll." - icon = 'icons/obj/toy.dmi' - icon_state = "therapyred" - -/obj/random/therapy/item_to_spawn() - return pick(subtypesof(/obj/item/toy/therapy)) //exclude the base type. - -/obj/item/toy/therapy/red - item_state = "egg4" // It's the red egg in items_left/righthand - item_color = "red" - -/obj/item/toy/therapy/purple - item_state = "egg1" // It's the magenta egg in items_left/righthand - item_color = "purple" - -/obj/item/toy/therapy/blue - item_state = "egg2" // It's the blue egg in items_left/righthand - item_color = "blue" - -/obj/item/toy/therapy/yellow - item_state = "egg5" // It's the yellow egg in items_left/righthand - item_color = "yellow" - -/obj/item/toy/therapy/orange - item_state = "egg4" // It's the red one again, lacking an orange item_state and making a new one is pointless - item_color = "orange" - -/obj/item/toy/therapy/green - item_state = "egg3" // It's the green egg in items_left/righthand - item_color = "green" - -/obj/item/toddler - icon_state = "toddler" - name = "toddler" - desc = "This baby looks almost real. Wait, did it just burp?" - force = 5 - w_class = WEIGHT_CLASS_BULKY - slot_flags = SLOT_BACK - - -//This should really be somewhere else but I don't know where. w/e - -/obj/item/inflatable_duck - name = "inflatable duck" - desc = "No bother to sink or swim when you can just float!" - icon_state = "inflatable" - item_state = "inflatable" - icon = 'icons/obj/clothing/belts.dmi' - slot_flags = SLOT_BELT - -/* - * Fake meteor - */ - -/obj/item/toy/minimeteor - name = "Mini-Meteor" - desc = "Relive the excitement of a meteor shower! SweetMeat-eor. Co is not responsible for any injuries, headaches or hearing loss caused by Mini-Meteor." - icon = 'icons/obj/toy.dmi' - icon_state = "minimeteor" - w_class = WEIGHT_CLASS_SMALL - -/obj/item/toy/minimeteor/throw_impact(atom/hit_atom) - ..() - playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1) - for(var/mob/M in range(10, src)) - if(!M.stat && !istype(M, /mob/living/silicon/ai))\ - shake_camera(M, 3, 1) - qdel(src) - -/* - * Carp plushie - */ - -/obj/item/toy/carpplushie - name = "space carp plushie" - desc = "An adorable stuffed toy that resembles a space carp." - icon = 'icons/obj/toy.dmi' - icon_state = "carpplushie" - attack_verb = list("bitten", "eaten", "fin slapped") - var/bitesound = 'sound/weapons/bite.ogg' - resistance_flags = FLAMMABLE - -// Attack mob -/obj/item/toy/carpplushie/attack(mob/M as mob, mob/user as mob) - playsound(loc, bitesound, 20, 1) // Play bite sound in local area - return ..() - -// Attack self -/obj/item/toy/carpplushie/attack_self(mob/user as mob) - playsound(src.loc, bitesound, 20, 1) - return ..() - - -/obj/random/carp_plushie - name = "Random Carp Plushie" - desc = "This is a random plushie" - icon = 'icons/obj/toy.dmi' - icon_state = "carpplushie" - -/obj/random/carp_plushie/item_to_spawn() - return pick(typesof(/obj/item/toy/carpplushie)) //can pick any carp plushie, even the original. - -/obj/item/toy/carpplushie/ice - icon_state = "icecarp" - -/obj/item/toy/carpplushie/silent - icon_state = "silentcarp" - -/obj/item/toy/carpplushie/electric - icon_state = "electriccarp" - -/obj/item/toy/carpplushie/gold - icon_state = "goldcarp" - -/obj/item/toy/carpplushie/toxin - icon_state = "toxincarp" - -/obj/item/toy/carpplushie/dragon - icon_state = "dragoncarp" - -/obj/item/toy/carpplushie/pink - icon_state = "pinkcarp" - -/obj/item/toy/carpplushie/candy - icon_state = "candycarp" - -/obj/item/toy/carpplushie/nebula - icon_state = "nebulacarp" - -/obj/item/toy/carpplushie/void - icon_state = "voidcarp" - -/* - * Plushie - */ - - -/obj/item/toy/plushie - name = "plushie" - desc = "An adorable, soft, and cuddly plushie." - icon = 'icons/obj/toy.dmi' - var/poof_sound = 'sound/weapons/thudswoosh.ogg' - attack_verb = list("poofed", "bopped", "whapped","cuddled","fluffed") - resistance_flags = FLAMMABLE - -/obj/item/toy/plushie/attack(mob/M as mob, mob/user as mob) - playsound(loc, poof_sound, 20, 1) // Play the whoosh sound in local area - if(iscarbon(M)) - if(prob(10)) - M.reagents.add_reagent("hugs", 10) - return ..() - -/obj/item/toy/plushie/attack_self(mob/user as mob) - var/cuddle_verb = pick("hugs","cuddles","snugs") - user.visible_message("[user] [cuddle_verb] the [src].") - playsound(get_turf(src), poof_sound, 50, 1, -1) - return ..() - -/obj/random/plushie - name = "Random Plushie" - desc = "This is a random plushie" - icon = 'icons/obj/toy.dmi' - icon_state = "redfox" - -/obj/random/plushie/item_to_spawn() - return pick(subtypesof(/obj/item/toy/plushie) - typesof(/obj/item/toy/plushie/fluff)) //exclude the base type. - -/obj/item/toy/plushie/corgi - name = "corgi plushie" - icon_state = "corgi" - -/obj/item/toy/plushie/girly_corgi - name = "corgi plushie" - icon_state = "girlycorgi" - -/obj/item/toy/plushie/robo_corgi - name = "borgi plushie" - icon_state = "robotcorgi" - -/obj/item/toy/plushie/octopus - name = "octopus plushie" - icon_state = "loveable" - -/obj/item/toy/plushie/face_hugger - name = "facehugger plushie" - icon_state = "huggable" - -//foxes are basically the best - -/obj/item/toy/plushie/red_fox - name = "red fox plushie" - icon_state = "redfox" - -/obj/item/toy/plushie/black_fox - name = "black fox plushie" - icon_state = "blackfox" - -/obj/item/toy/plushie/marble_fox - name = "marble fox plushie" - icon_state = "marblefox" - -/obj/item/toy/plushie/blue_fox - name = "blue fox plushie" - icon_state = "bluefox" - -/obj/item/toy/plushie/orange_fox - name = "orange fox plushie" - icon_state = "orangefox" - -/obj/item/toy/plushie/coffee_fox - name = "coffee fox plushie" - icon_state = "coffeefox" - -/obj/item/toy/plushie/pink_fox - name = "pink fox plushie" - icon_state = "pinkfox" - -/obj/item/toy/plushie/purple_fox - name = "purple fox plushie" - icon_state = "purplefox" - -/obj/item/toy/plushie/crimson_fox - name = "crimson fox plushie" - icon_state = "crimsonfox" - -/obj/item/toy/plushie/deer - name = "deer plushie" - icon_state = "deer" - -/obj/item/toy/plushie/black_cat - name = "black cat plushie" - icon_state = "blackcat" - -/obj/item/toy/plushie/grey_cat - name = "grey cat plushie" - icon_state = "greycat" - -/obj/item/toy/plushie/white_cat - name = "white cat plushie" - icon_state = "whitecat" - -/obj/item/toy/plushie/orange_cat - name = "orange cat plushie" - icon_state = "orangecat" - -/obj/item/toy/plushie/siamese_cat - name = "siamese cat plushie" - icon_state = "siamesecat" - -/obj/item/toy/plushie/tabby_cat - name = "tabby cat plushie" - icon_state = "tabbycat" - -/obj/item/toy/plushie/tuxedo_cat - name = "tuxedo cat plushie" - icon_state = "tuxedocat" - -/obj/item/toy/plushie/voxplushie - name = "vox plushie" - desc = "A stitched-together vox, fresh from the skipjack. Press its belly to hear it skree!" - icon_state = "plushie_vox" - item_state = "plushie_vox" - var/cooldown = 0 - -/obj/item/toy/plushie/voxplushie/attack_self(mob/user) - if(!cooldown) - playsound(user, 'sound/voice/shriek1.ogg', 10, 0) - visible_message("Skreee!") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - -//New generation TG plushies - -/obj/item/toy/plushie/lizardplushie - name = "lizard plushie" - desc = "An adorable stuffed toy that resembles a lizardperson." - icon_state = "plushie_lizard" - item_state = "plushie_lizard" - -/obj/item/toy/plushie/snakeplushie - name = "snake plushie" - desc = "An adorable stuffed toy that resembles a snake. Not to be mistaken for the real thing." - icon_state = "plushie_snake" - item_state = "plushie_snake" - -/obj/item/toy/plushie/nukeplushie - name = "operative plushie" - desc = "An stuffed toy that resembles a syndicate nuclear operative. The tag claims operatives to be purely fictitious." - icon_state = "plushie_nuke" - item_state = "plushie_nuke" - -/obj/item/toy/plushie/slimeplushie - name = "slime plushie" - desc = "An adorable stuffed toy that resembles a slime. It is practically just a hacky sack." - icon_state = "plushie_slime" - item_state = "plushie_slime" - -/* - * Foam Armblade - */ - - /obj/item/toy/foamblade - name = "foam armblade" - desc = "it says \"Sternside Changs #1 fan\" on it. " - icon = 'icons/obj/toy.dmi' - icon_state = "foamblade" - item_state = "arm_blade" - attack_verb = list("pricked", "absorbed", "gored") - w_class = WEIGHT_CLASS_SMALL - resistance_flags = FLAMMABLE - -/* - * Toy/fake flash - */ -/obj/item/toy/flash - name = "toy flash" - desc = "FOR THE REVOLU- Oh wait, that's just a toy." - icon = 'icons/obj/device.dmi' - icon_state = "flash" - item_state = "flashtool" - w_class = WEIGHT_CLASS_TINY - -/obj/item/toy/flash/attack(mob/living/M, mob/user) - playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) - flick("[initial(icon_state)]2", src) - user.visible_message("[user] blinds [M] with the flash!") - - -/* - * Toy big red button - */ -/obj/item/toy/redbutton - name = "big red button" - desc = "A big, plastic red button. Reads 'From HonkCo Pranks?' on the back." - icon = 'icons/obj/assemblies.dmi' - icon_state = "bigred" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - -/obj/item/toy/redbutton/attack_self(mob/user) - if(cooldown < world.time) - cooldown = (world.time + 300) // Sets cooldown at 30 seconds - user.visible_message("[user] presses the big red button.", "You press the button, it plays a loud noise!", "The button clicks loudly.") - playsound(src, 'sound/effects/explosionfar.ogg', 50, 0, 0) - for(var/mob/M in range(10, src)) // Checks range - if(!M.stat && !istype(M, /mob/living/silicon/ai)) // Checks to make sure whoever's getting shaken is alive/not the AI - sleep(8) // Short delay to match up with the explosion sound - shake_camera(M, 2, 1) // Shakes player camera 2 squares for 1 second. - - else - to_chat(user, "Nothing happens.") - - -/* - * AI core prizes - */ -/obj/item/toy/AI - name = "toy AI" - desc = "A little toy model AI core with real law announcing action!" - icon = 'icons/obj/toy.dmi' - icon_state = "AI" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - -/obj/item/toy/AI/attack_self(mob/user) - if(!cooldown) //for the sanity of everyone - var/message = generate_ion_law() - to_chat(user, "You press the button on [src].") - playsound(user, 'sound/machines/click.ogg', 20, 1) - visible_message("[bicon(src)] [message]") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - -/obj/item/toy/codex_gigas - name = "Toy Codex Gigas" - desc = "A tool to help you write fictional devils!" - icon = 'icons/obj/library.dmi' - icon_state = "demonomicon" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = FALSE - -/obj/item/toy/codex_gigas/attack_self(mob/user) - if(!cooldown) - user.visible_message( - "[user] presses the button on \the [src].", - "You press the button on \the [src].", - "You hear a soft click.") - var/list/messages = list() - var/datum/devilinfo/devil = randomDevilInfo() - messages += "Some fun facts about: [devil.truename]" - messages += "[lawlorify[LORE][devil.bane]]" - messages += "[lawlorify[LORE][devil.obligation]]" - messages += "[lawlorify[LORE][devil.ban]]" - messages += "[lawlorify[LORE][devil.banish]]" - playsound(loc, 'sound/machines/click.ogg', 20, 1) - cooldown = TRUE - for(var/message in messages) - user.loc.visible_message("[bicon(src)] [message]") - sleep(10) - spawn(20) - cooldown = FALSE - return - ..() - -/obj/item/toy/owl - name = "owl action figure" - desc = "An action figure modeled after 'The Owl', defender of justice." - icon = 'icons/obj/toy.dmi' - icon_state = "owlprize" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - -/obj/item/toy/owl/attack_self(mob/user) - if(!cooldown) //for the sanity of everyone - var/message = pick("You won't get away this time, Griffin!", "Stop right there, criminal!", "Hoot! Hoot!", "I am the night!") - to_chat(user, "You pull the string on the [src].") - playsound(user, 'sound/creatures/hoot.ogg', 25, 1) - visible_message("[bicon(src)] [message]") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - -/obj/item/toy/griffin - name = "griffin action figure" - desc = "An action figure modeled after 'The Griffin', criminal mastermind." - icon = 'icons/obj/toy.dmi' - icon_state = "griffinprize" - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - -/obj/item/toy/griffin/attack_self(mob/user) - if(!cooldown) //for the sanity of everyone - var/message = pick("You can't stop me, Owl!", "My plan is flawless! The vault is mine!", "Caaaawwww!", "You will never catch me!") - to_chat(user, "You pull the string on the [src].") - playsound(user, 'sound/creatures/caw.ogg', 25, 1) - visible_message("[bicon(src)] [message]") - cooldown = 1 - spawn(30) cooldown = 0 - return - ..() - -// DND Character minis. Use the naming convention (type)character for the icon states. -/obj/item/toy/character - icon = 'icons/obj/toy.dmi' - w_class = WEIGHT_CLASS_SMALL - pixel_z = 5 - -/obj/item/toy/character/alien - name = "Xenomorph Miniature" - desc = "A miniature xenomorph. Scary!" - icon_state = "aliencharacter" -/obj/item/toy/character/cleric - name = "Cleric Miniature" - desc = "A wee little cleric, with his wee little staff." - icon_state = "clericcharacter" -/obj/item/toy/character/warrior - name = "Warrior Miniature" - desc = "That sword would make a decent toothpick." - icon_state = "warriorcharacter" -/obj/item/toy/character/thief - name = "Thief Miniature" - desc = "Hey, where did my wallet go!?" - icon_state = "thiefcharacter" -/obj/item/toy/character/wizard - name = "Wizard Miniature" - desc = "MAGIC!" - icon_state = "wizardcharacter" -/obj/item/toy/character/cthulhu - name = "Cthulhu Miniature" - desc = "The dark lord has risen!" - icon_state = "darkmastercharacter" -/obj/item/toy/character/lich - name = "Lich Miniature" - desc = "Murderboner extraordinaire." - icon_state = "lichcharacter" -/obj/item/storage/box/characters - name = "Box of Miniatures" - desc = "The nerd's best friends." - icon_state = "box" -/obj/item/storage/box/characters/New() - ..() - new /obj/item/toy/character/alien(src) - new /obj/item/toy/character/cleric(src) - new /obj/item/toy/character/warrior(src) - new /obj/item/toy/character/thief(src) - new /obj/item/toy/character/wizard(src) - new /obj/item/toy/character/cthulhu(src) - new /obj/item/toy/character/lich(src) - - -//Pet Rocks, just like from the 70's! - -/obj/item/toy/pet_rock - name = "pet rock" - desc = "The perfect pet!" - icon = 'icons/obj/toy.dmi' - icon_state = "pet_rock" - w_class = WEIGHT_CLASS_SMALL - force = 5 - throwforce = 5 - attack_verb = list("attacked", "bashed", "smashed", "stoned") - hitsound = "swing_hit" - -/obj/item/toy/pet_rock/fred - name = "fred" - desc = "Fred, the bestest boy pet in the whole wide universe!" - icon_state = "fred" - -/obj/item/toy/pet_rock/roxie - name = "roxie" - desc = "Roxie, the bestest girl pet in the whole wide universe!" - icon_state = "roxie" - -//minigibber, so cute - -/obj/item/toy/minigibber - name = "miniature gibber" - desc = "A miniature recreation of Nanotrasen's famous meat grinder." - icon = 'icons/obj/toy.dmi' - icon_state = "minigibber" - attack_verb = list("grinded", "gibbed") - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - var/obj/stored_minature = null - -/obj/item/toy/minigibber/attack_self(var/mob/user) - - if(stored_minature) - to_chat(user, "\The [src] makes a violent grinding noise as it tears apart the miniature figure inside!") - QDEL_NULL(stored_minature) - playsound(user, 'sound/goonstation/effects/gib.ogg', 20, 1) - cooldown = world.time - - if(cooldown < world.time - 8) - to_chat(user, "You hit the gib button on \the [src].") - playsound(user, 'sound/goonstation/effects/gib.ogg', 20, 1) - cooldown = world.time - -/obj/item/toy/minigibber/attackby(var/obj/O, var/mob/user, params) - if(istype(O,/obj/item/toy/character) && O.loc == user) - to_chat(user, "You start feeding \the [O] [bicon(O)] into \the [src]'s mini-input.") - if(do_after(user, 10, target = src)) - if(O.loc != user) - to_chat(user, "\The [O] is too far away to feed into \the [src]!") - else - to_chat(user, "You feed \the [O] [bicon(O)] into \the [src]!") - user.unEquip(O) - O.forceMove(src) - stored_minature = O - else - to_chat(user, "You stop feeding \the [O] into \the [src]'s mini-input.") - else ..() - -/* - * Xenomorph action figure - */ - -/obj/item/toy/toy_xeno - icon = 'icons/obj/toy.dmi' - icon_state = "toy_xeno" - name = "xenomorph action figure" - desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use." - w_class = WEIGHT_CLASS_SMALL - var/cooldown = 0 - -/obj/item/toy/toy_xeno/attack_self(mob/user) - if(cooldown <= world.time) - cooldown = (world.time + 50) //5 second cooldown - user.visible_message("[user] pulls back the string on [src].") - icon_state = "[initial(icon_state)]_used" - sleep(5) - audible_message("[bicon(src)] Hiss!") - var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg') - playsound(get_turf(src), pick(possible_sounds), 50, 1) - spawn(45) - if(src) - icon_state = "[initial(icon_state)]" - else - to_chat(user, "The string on [src] hasn't rewound all the way!") - return - -/obj/item/toy/russian_revolver - name = "russian revolver" - desc = "for fun and games!" - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "detective_gold" - item_state = "gun" - lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/guns_righthand.dmi' - hitsound = "swing_hit" - flags = CONDUCT - slot_flags = SLOT_BELT - materials = list(MAT_METAL=2000) - w_class = WEIGHT_CLASS_NORMAL - throwforce = 5 - throw_speed = 4 - throw_range = 5 - force = 5 - origin_tech = "combat=1" - attack_verb = list("struck", "hit", "bashed") - var/bullets_left = 0 - var/max_shots = 6 - -/obj/item/toy/russian_revolver/suicide_act(mob/user) - user.visible_message("[user] quickly loads six bullets into [src]'s cylinder and points it at [user.p_their()] head before pulling the trigger! It looks like [user.p_theyre()] trying to commit suicide.") - playsound(loc, 'sound/weapons/gunshots/gunshot_strong.ogg', 50, 1) - return BRUTELOSS - -/obj/item/toy/russian_revolver/New() - ..() - spin_cylinder() - -/obj/item/toy/russian_revolver/attack_self(mob/user) - if(!bullets_left) - user.visible_message("[user] loads a bullet into [src]'s cylinder before spinning it.") - spin_cylinder() - else - user.visible_message("[user] spins the cylinder on [src]!") - spin_cylinder() - -/obj/item/toy/russian_revolver/attack(mob/M, mob/living/user) - return - -/obj/item/toy/russian_revolver/afterattack(atom/target, mob/user, flag, params) - if(flag) - if(target in user.contents) - return - if(!ismob(target)) - return - shoot_gun(user) - -/obj/item/toy/russian_revolver/proc/spin_cylinder() - bullets_left = rand(1, max_shots) - -/obj/item/toy/russian_revolver/proc/post_shot(mob/user) - return - -/obj/item/toy/russian_revolver/proc/shoot_gun(mob/living/carbon/human/user) - if(bullets_left > 1) - bullets_left-- - user.visible_message("*click*") - playsound(src, 'sound/weapons/empty.ogg', 100, 1) - return FALSE - if(bullets_left == 1) - bullets_left = 0 - var/zone = "head" - if(!(user.has_organ(zone))) // If they somehow don't have a head. - zone = "chest" - playsound(src, 'sound/weapons/gunshots/gunshot_strong.ogg', 50, 1) - user.visible_message("[src] goes off!") - post_shot(user) - user.apply_damage(300, BRUTE, zone, sharp = TRUE, used_weapon = "Self-inflicted gunshot wound to the [zone].") - user.bleed(BLOOD_VOLUME_NORMAL) - user.death() // Just in case - return TRUE - else - to_chat(user, "[src] needs to be reloaded.") - return FALSE - -/obj/item/toy/russian_revolver/trick_revolver - name = "\improper .357 revolver" - desc = "A suspicious revolver. Uses .357 ammo." - icon_state = "revolver" - max_shots = 1 - var/fake_bullets = 0 - -/obj/item/toy/russian_revolver/trick_revolver/New() - ..() - fake_bullets = rand(2, 7) - -/obj/item/toy/russian_revolver/trick_revolver/examine(mob/user) //Sneaky sneaky - . = ..() - . += "Has [fake_bullets] round\s remaining." - . += "[fake_bullets] of those are live rounds." - -/obj/item/toy/russian_revolver/trick_revolver/post_shot(user) - to_chat(user, "[src] did look pretty dodgey!") - SEND_SOUND(user, 'sound/misc/sadtrombone.ogg') //HONK -/* - * Rubber Chainsaw - */ -/obj/item/twohanded/toy/chainsaw - name = "Toy Chainsaw" - desc = "A toy chainsaw with a rubber edge. Ages 8 and up" - icon_state = "chainsaw0" - force = 0 - throwforce = 0 - throw_speed = 4 - throw_range = 20 - wieldsound = 'sound/weapons/chainsawstart.ogg' - attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered") - -/obj/item/twohanded/toy/chainsaw/update_icon() - if(wielded) - icon_state = "chainsaw[wielded]" - else - icon_state = "chainsaw0" - -/* - * Cat Toy - */ -/obj/item/toy/cattoy - name = "toy mouse" - desc = "A colorful toy mouse!" - icon = 'icons/obj/toy.dmi' - icon_state = "toy_mouse" - w_class = WEIGHT_CLASS_SMALL - resistance_flags = FLAMMABLE - var/cooldown = 0 - -/* - * Action Figures - */ - - -/obj/random/figure - name = "Random Action Figure" - desc = "This is a random toy action figure" - icon = 'icons/obj/toy.dmi' - icon_state = "nuketoy" - -/obj/random/figure/item_to_spawn() - return pick(subtypesof(/obj/item/toy/figure)) - - -/obj/item/toy/figure - name = "Non-Specific Action Figure action figure" - desc = "A \"Space Life\" brand... wait, what the hell is this thing?" - icon = 'icons/obj/toy.dmi' - icon_state = "nuketoy" - var/cooldown = 0 - var/toysay = "What the fuck did you do?" - -/obj/item/toy/figure/New() - ..() - desc = "A \"Space Life\" brand [name]" - -/obj/item/toy/figure/attack_self(mob/user as mob) - if(cooldown < world.time) - cooldown = (world.time + 30) //3 second cooldown - user.visible_message("[bicon(src)] The [src] says \"[toysay]\".") - playsound(user, 'sound/machines/click.ogg', 20, 1) - -/obj/item/toy/figure/cmo - name = "Chief Medical Officer action figure" - icon_state = "cmo" - toysay = "Suit sensors!" - -/obj/item/toy/figure/assistant - name = "Assistant action figure" - icon_state = "assistant" - toysay = "Grey tide station wide!" - -/obj/item/toy/figure/atmos - name = "Atmospheric Technician action figure" - icon_state = "atmos" - toysay = "Glory to Atmosia!" - -/obj/item/toy/figure/bartender - name = "Bartender action figure" - icon_state = "bartender" - toysay = "Wheres my monkey?" - -/obj/item/toy/figure/borg - name = "Cyborg action figure" - icon_state = "borg" - toysay = "I. LIVE. AGAIN." - -/obj/item/toy/figure/botanist - name = "Botanist action figure" - icon_state = "botanist" - toysay = "Dude, I see colors..." - -/obj/item/toy/figure/captain - name = "Captain action figure" - icon_state = "captain" - toysay = "Crew, the Nuke Disk is safely up my ass." - -/obj/item/toy/figure/cargotech - name = "Cargo Technician action figure" - icon_state = "cargotech" - toysay = "For Cargonia!" - -/obj/item/toy/figure/ce - name = "Chief Engineer action figure" - icon_state = "ce" - toysay = "Wire the solars!" - -/obj/item/toy/figure/chaplain - name = "Chaplain action figure" - icon_state = "chaplain" - toysay = "Gods make me a killing machine please!" - -/obj/item/toy/figure/chef - name = "Chef action figure" - icon_state = "chef" - toysay = "I swear it's not human meat." - -/obj/item/toy/figure/chemist - name = "Chemist action figure" - icon_state = "chemist" - toysay = "Get your pills!" - -/obj/item/toy/figure/clown - name = "Clown action figure" - icon_state = "clown" - toysay = "Honk!" - -/obj/item/toy/figure/ian - name = "Ian action figure" - icon_state = "ian" - toysay = "Arf!" - -/obj/item/toy/figure/detective - name = "Detective action figure" - icon_state = "detective" - toysay = "This airlock has grey jumpsuit and insulated glove fibers on it." - -/obj/item/toy/figure/dsquad - name = "Death Squad Officer action figure" - icon_state = "dsquad" - toysay = "Eliminate all threats!" - -/obj/item/toy/figure/engineer - name = "Engineer action figure" - icon_state = "engineer" - toysay = "Oh god, the singularity is loose!" - -/obj/item/toy/figure/geneticist - name = "Geneticist action figure" - icon_state = "geneticist" - toysay = "I'm not qualified for this job." - -/obj/item/toy/figure/hop - name = "Head of Personnel action figure" - icon_state = "hop" - toysay = "Giving out all access!" - -/obj/item/toy/figure/hos - name = "Head of Security action figure" - icon_state = "hos" - toysay = "I'm here to win, anything else is secondary." - -/obj/item/toy/figure/qm - name = "Quartermaster action figure" - icon_state = "qm" - toysay = "Hail Cargonia!" - -/obj/item/toy/figure/janitor - name = "Janitor action figure" - icon_state = "janitor" - toysay = "Look at the signs, you idiot." - -/obj/item/toy/figure/lawyer - name = "Internal Affairs Agent action figure" - icon_state = "lawyer" - toysay = "Standard Operating Procedure says they're guilty! Hacking is proof they're an Enemy of the Corporation!" - -/obj/item/toy/figure/librarian - name = "Librarian action figure" - icon_state = "librarian" - toysay = "One day while..." - -/obj/item/toy/figure/md - name = "Medical Doctor action figure" - icon_state = "md" - toysay = "The patient is already dead!" - -/obj/item/toy/figure/mime - name = "Mime action figure" - desc = "A \"Space Life\" brand Mime action figure." - icon_state = "mime" - toysay = "..." - -/obj/item/toy/figure/miner - name = "Shaft Miner action figure" - icon_state = "miner" - toysay = "Oh god it's eating my intestines!" - -/obj/item/toy/figure/ninja - name = "Ninja action figure" - icon_state = "ninja" - toysay = "Oh god! Stop shooting, I'm friendly!" - -/obj/item/toy/figure/wizard - name = "Wizard action figure" - icon_state = "wizard" - toysay = "Ei Nath!" - -/obj/item/toy/figure/rd - name = "Research Director action figure" - icon_state = "rd" - toysay = "Blowing all of the borgs!" - -/obj/item/toy/figure/roboticist - name = "Roboticist action figure" - icon_state = "roboticist" - toysay = "He asked to be borged!" - -/obj/item/toy/figure/scientist - name = "Scientist action figure" - icon_state = "scientist" - toysay = "Someone else must have made those bombs!" - -/obj/item/toy/figure/syndie - name = "Nuclear Operative action figure" - icon_state = "syndie" - toysay = "Get that fucking disk!" - -/obj/item/toy/figure/secofficer - name = "Security Officer action figure" - icon_state = "secofficer" - toysay = "I am the law!" - -/obj/item/toy/figure/virologist - name = "Virologist action figure" - icon_state = "virologist" - toysay = "The cure is potassium!" - -/obj/item/toy/figure/warden - name = "Warden action figure" - icon_state = "warden" - toysay = "Execute him for breaking in!" - -////////////////////////////////////////////////////// -// Magic 8-Ball / Conch // -////////////////////////////////////////////////////// - -/obj/item/toy/eight_ball - name = "Magic 8-Ball" - desc = "Mystical! Magical! Ages 8+!" - icon = 'icons/obj/toy.dmi' - icon_state = "eight-ball" - var/use_action = "shakes the ball" - var/cooldown = 0 - var/list/possible_answers = list("Definitely", "All signs point to yes.", "Most likely.", "Yes.", "Ask again later.", "Better not tell you now.", "Future Unclear.", "Maybe.", "Doubtful.", "No.", "Don't count on it.", "Never.") - -/obj/item/toy/eight_ball/attack_self(mob/user as mob) - if(!cooldown) - var/answer = pick(possible_answers) - user.visible_message("[user] focuses on [user.p_their()] question and [use_action]...") - user.visible_message("[bicon(src)] The [src] says \"[answer]\"") - spawn(30) - cooldown = 0 - return - -/obj/item/toy/eight_ball/conch - name = "Magic Conch Shell" - desc = "All hail the Magic Conch!" - icon_state = "conch" - use_action = "pulls the string" - possible_answers = list("Yes.", "No.", "Try asking again.", "Nothing.", "I don't think so.", "Neither.", "Maybe someday.") - -/* - *Fake cuffs (honk honk) - */ - -/obj/item/restraints/handcuffs/toy - desc = "Toy handcuffs. Plastic and extremely cheaply made." - throwforce = 0 - breakouttime = 0 - ignoresClumsy = TRUE \ No newline at end of file +/* Toys! + * Contains: + * Balloons + * Fake telebeacon + * Fake singularity + * Toy swords + * Toy mechs + * Snap pops + * Water flower + * Toy Nuke + * Card Deck + * Therapy dolls + * Toddler doll + * Inflatable duck + * Foam armblade + * Mini Gibber + * Toy xeno + * Toy chainsaws + * Action Figures + */ + + +/obj/item/toy + throwforce = 0 + throw_speed = 4 + throw_range = 20 + force = 0 + + +/* + * Balloons + */ +/obj/item/toy/balloon + name = "water balloon" + desc = "A translucent balloon. There's nothing in it." + icon = 'icons/obj/toy.dmi' + icon_state = "waterballoon-e" + item_state = "balloon-empty" + +/obj/item/toy/balloon/New() + ..() + create_reagents(10) + +/obj/item/toy/balloon/attack(mob/living/carbon/human/M as mob, mob/user as mob) + return + +/obj/item/toy/balloon/afterattack(atom/A, mob/user, proximity) + if(!proximity) + return + if(istype(A, /obj/structure/reagent_dispensers)) + var/obj/structure/reagent_dispensers/RD = A + if(RD.reagents.total_volume <= 0) + to_chat(user, "[RD] is empty.") + else if(reagents.total_volume >= 10) + to_chat(user, "[src] is full.") + else + A.reagents.trans_to(src, 10) + to_chat(user, "You fill the balloon with the contents of [A].") + desc = "A translucent balloon with some form of liquid sloshing around in it." + update_icon() + +/obj/item/toy/balloon/wash(mob/user, atom/source) + if(reagents.total_volume < 10) + reagents.add_reagent("water", min(10-reagents.total_volume, 10)) + to_chat(user, "You fill the balloon from the [source].") + desc = "A translucent balloon with some form of liquid sloshing around in it." + update_icon() + return + +/obj/item/toy/balloon/attackby(obj/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/reagent_containers/glass) || istype(O, /obj/item/reagent_containers/food/drinks/drinkingglass)) + if(O.reagents) + if(O.reagents.total_volume < 1) + to_chat(user, "The [O] is empty.") + else if(O.reagents.total_volume >= 1) + if(O.reagents.has_reagent("facid", 1)) + to_chat(user, "The acid chews through the balloon!") + O.reagents.reaction(user) + qdel(src) + else + desc = "A translucent balloon with some form of liquid sloshing around in it." + to_chat(user, "You fill the balloon with the contents of [O].") + O.reagents.trans_to(src, 10) + update_icon() + return + +/obj/item/toy/balloon/throw_impact(atom/hit_atom) + if(reagents.total_volume >= 1) + visible_message("The [src] bursts!","You hear a pop and a splash.") + reagents.reaction(get_turf(hit_atom)) + for(var/atom/A in get_turf(hit_atom)) + reagents.reaction(A) + icon_state = "burst" + spawn(5) + if(src) + qdel(src) + return + +/obj/item/toy/balloon/update_icon() + if(src.reagents.total_volume >= 1) + icon_state = "waterballoon" + item_state = "balloon" + else + icon_state = "waterballoon-e" + item_state = "balloon-empty" + +/obj/item/toy/syndicateballoon + name = "syndicate balloon" + desc = "There is a tag on the back that reads \"FUK NT!11!\"." + throwforce = 0 + throw_speed = 4 + throw_range = 20 + force = 0 + icon_state = "syndballoon" + item_state = "syndballoon" + w_class = WEIGHT_CLASS_BULKY + var/lastused = null + +/obj/item/toy/syndicateballoon/attack_self(mob/user) + if(world.time - lastused < CLICK_CD_MELEE) + return + var/playverb = pick("bat [src]", "tug on [src]'s string", "play with [src]") + user.visible_message("[user] plays with [src].", "You [playverb].") + lastused = world.time + +/* + * Fake telebeacon + */ +/obj/item/toy/blink + name = "electronic blink toy game" + desc = "Blink. Blink. Blink. Ages 8 and up." + icon = 'icons/obj/radio.dmi' + icon_state = "beacon" + item_state = "signaler" + +/* + * Fake singularity + */ +/obj/item/toy/spinningtoy + name = "Gravitational Singularity" + desc = "\"Singulo\" brand spinning toy." + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + +/* + * Toy swords + */ +/obj/item/toy/sword + name = "toy sword" + desc = "A cheap, plastic replica of an energy sword. Realistic sounds! Ages 8 and up." + icon_state = "sword0" + item_state = "sword0" + var/active = FALSE + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("attacked", "struck", "hit") + +/obj/item/toy/sword/attack_self(mob/user) + active = !active + if(active) + to_chat(user, "You extend the plastic blade with a quick flick of your wrist.") + playsound(user, 'sound/weapons/saberon.ogg', 20, 1) + icon_state = "swordblue" + item_state = "swordblue" + w_class = WEIGHT_CLASS_BULKY + else + to_chat(user, "You push the plastic blade back down into the handle.") + playsound(user, 'sound/weapons/saberoff.ogg', 20, 1) + icon_state = "sword0" + item_state = "sword0" + w_class = WEIGHT_CLASS_SMALL + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + add_fingerprint(user) + return + +// Copied from /obj/item/melee/energy/sword/attackby +/obj/item/toy/sword/attackby(obj/item/W, mob/living/user, params) + ..() + if(istype(W, /obj/item/toy/sword)) + if(W == src) + to_chat(user, "You try to attach the end of the plastic sword to... itself. You're not very smart, are you?") + if(ishuman(user)) + user.adjustBrainLoss(10) + else if((W.flags & NODROP) || (flags & NODROP)) + to_chat(user, "\the [flags & NODROP ? src : W] is stuck to your hand, you can't attach it to \the [flags & NODROP ? W : src]!") + else + to_chat(user, "You attach the ends of the two plastic swords, making a single double-bladed toy! You're fake-cool.") + new /obj/item/twohanded/dualsaber/toy(user.loc) + user.unEquip(W) + user.unEquip(src) + qdel(W) + qdel(src) + +/* + * Subtype of Double-Bladed Energy Swords + */ +/obj/item/twohanded/dualsaber/toy + name = "double-bladed toy sword" + desc = "A cheap, plastic replica of TWO energy swords. Double the fun!" + force = 0 + throwforce = 0 + throw_speed = 3 + throw_range = 5 + force_unwielded = 0 + force_wielded = 0 + origin_tech = null + attack_verb = list("attacked", "struck", "hit") + brightness_on = 0 + sharp_when_wielded = FALSE // It's a toy + +/obj/item/twohanded/dualsaber/toy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + return 0 + +/obj/item/twohanded/dualsaber/toy/IsReflect()//Stops Toy Dualsabers from reflecting energy projectiles + return 0 + +/obj/item/toy/katana + name = "replica katana" + desc = "Woefully underpowered in D20." + icon_state = "katana" + item_state = "katana" + flags = CONDUCT + slot_flags = SLOT_BELT | SLOT_BACK + force = 5 + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("attacked", "slashed", "stabbed", "sliced") + hitsound = 'sound/weapons/bladeslice.ogg' + +/obj/item/toy/katana/suicide_act(mob/user) + var/dmsg = pick("[user] tries to stab \the [src] into [user.p_their()] abdomen, but it shatters! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.","[user] tries to stab \the [src] into [user.p_their()] abdomen, but \the [src] bends and breaks in half! [user.p_they(TRUE)] look[user.p_s()] as if [user.p_they()] might die from the shame.","[user] tries to slice [user.p_their()] own throat, but the plastic blade has no sharpness, causing [user.p_them()] to lose [user.p_their()] balance, slip over, and break [user.p_their()] neck with a loud snap!") + user.visible_message("[dmsg] It looks like [user.p_theyre()] trying to commit suicide.") + return BRUTELOSS + + +/* + * Snap pops viral shit + */ +/obj/item/toy/snappop/virus + name = "unstable goo" + desc = "Your palm is oozing this stuff!" + icon = 'icons/mob/slimes.dmi' + icon_state = "red slime extract" + throwforce = 5.0 + throw_speed = 10 + throw_range = 30 + w_class = WEIGHT_CLASS_TINY + + +/obj/item/toy/snappop/virus/throw_impact(atom/hit_atom) + ..() + do_sparks(3, 1, src) + new /obj/effect/decal/cleanable/ash(src.loc) + visible_message("The [name] explodes!","You hear a bang!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) + +/* + * Snap pops + */ +/obj/item/toy/snappop + name = "snap pop" + desc = "Wow!" + icon = 'icons/obj/toy.dmi' + icon_state = "snappop" + w_class = WEIGHT_CLASS_TINY + var/ash_type = /obj/effect/decal/cleanable/ash + +/obj/item/toy/snappop/proc/pop_burst(var/n=3, var/c=1) + do_sparks(n, c, src) + new ash_type(loc) + visible_message("[src] explodes!", + "You hear a snap!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) + +/obj/item/toy/snappop/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + ..() + pop_burst() + +/obj/item/toy/snappop/throw_impact(atom/hit_atom) + ..() + pop_burst() + +/obj/item/toy/snappop/Crossed(H as mob|obj, oldloc) + if(ishuman(H) || issilicon(H)) //i guess carp and shit shouldn't set them off + var/mob/living/carbon/M = H + if(issilicon(H) || M.m_intent == MOVE_INTENT_RUN) + to_chat(M, "You step on the snap pop!") + pop_burst(2, 0) + +/obj/item/toy/snappop/phoenix + name = "phoenix snap pop" + desc = "Wow! And wow! And wow!" + ash_type = /obj/effect/decal/cleanable/ash/snappop_phoenix + +/obj/effect/decal/cleanable/ash/snappop_phoenix + var/respawn_time = 300 + +/obj/effect/decal/cleanable/ash/snappop_phoenix/Initialize(mapload) + . = ..() + addtimer(CALLBACK(src, .proc/respawn), respawn_time) + +/obj/effect/decal/cleanable/ash/snappop_phoenix/proc/respawn() + new /obj/item/toy/snappop/phoenix(get_turf(src)) + qdel(src) + + +/* + * Mech prizes + */ +/obj/item/toy/prize + icon = 'icons/obj/toy.dmi' + icon_state = "ripleytoy" + var/cooldown = 0 + +//all credit to skasi for toy mech fun ideas +/obj/item/toy/prize/attack_self(mob/user as mob) + if(cooldown < world.time - 8) + to_chat(user, "You play with [src].") + playsound(user, 'sound/mecha/mechstep.ogg', 20, 1) + cooldown = world.time + +/obj/item/toy/prize/attack_hand(mob/user as mob) + if(loc == user) + if(cooldown < world.time - 8) + to_chat(user, "You play with [src].") + playsound(user, 'sound/mecha/mechturn.ogg', 20, 1) + cooldown = world.time + return + ..() + +/obj/random/mech + name = "Random Mech Prize" + desc = "This is a random prize" + icon = 'icons/obj/toy.dmi' + icon_state = "ripleytoy" + +/obj/random/mech/item_to_spawn() + return pick(subtypesof(/obj/item/toy/prize)) //exclude the base type. + +/obj/item/toy/prize/ripley + name = "toy ripley" + desc = "Mini-Mecha action figure! Collect them all! 1/11." + +/obj/item/toy/prize/fireripley + name = "toy firefighting ripley" + desc = "Mini-Mecha action figure! Collect them all! 2/11." + icon_state = "fireripleytoy" + +/obj/item/toy/prize/deathripley + name = "toy deathsquad ripley" + desc = "Mini-Mecha action figure! Collect them all! 3/11." + icon_state = "deathripleytoy" + +/obj/item/toy/prize/gygax + name = "toy gygax" + desc = "Mini-Mecha action figure! Collect them all! 4/11." + icon_state = "gygaxtoy" + +/obj/item/toy/prize/durand + name = "toy durand" + desc = "Mini-Mecha action figure! Collect them all! 5/11." + icon_state = "durandprize" + +/obj/item/toy/prize/honk + name = "toy H.O.N.K." + desc = "Mini-Mecha action figure! Collect them all! 6/11." + icon_state = "honkprize" + +/obj/item/toy/prize/marauder + name = "toy marauder" + desc = "Mini-Mecha action figure! Collect them all! 7/11." + icon_state = "marauderprize" + +/obj/item/toy/prize/seraph + name = "toy seraph" + desc = "Mini-Mecha action figure! Collect them all! 8/11." + icon_state = "seraphprize" + +/obj/item/toy/prize/mauler + name = "toy mauler" + desc = "Mini-Mecha action figure! Collect them all! 9/11." + icon_state = "maulerprize" + +/obj/item/toy/prize/odysseus + name = "toy odysseus" + desc = "Mini-Mecha action figure! Collect them all! 10/11." + icon_state = "odysseusprize" + +/obj/item/toy/prize/phazon + name = "toy phazon" + desc = "Mini-Mecha action figure! Collect them all! 11/11." + icon_state = "phazonprize" + + + +/* +|| A Deck of Cards for playing various games of chance || +*/ + + + +obj/item/toy/cards + resistance_flags = FLAMMABLE + max_integrity = 50 + var/parentdeck = null + var/deckstyle = "nanotrasen" + var/card_hitsound = null + var/card_force = 0 + var/card_throwforce = 0 + var/card_throw_speed = 4 + var/card_throw_range = 20 + var/list/card_attack_verb = list("attacked") + +obj/item/toy/cards/New() + ..() + +obj/item/toy/cards/proc/apply_card_vars(obj/item/toy/cards/newobj, obj/item/toy/cards/sourceobj) // Applies variables for supporting multiple types of card deck + if(!istype(sourceobj)) + return + +obj/item/toy/cards/deck + name = "deck of cards" + desc = "A deck of space-grade playing cards." + icon = 'icons/obj/toy.dmi' + deckstyle = "nanotrasen" + icon_state = "deck_nanotrasen_full" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + var/list/cards = list() + +obj/item/toy/cards/deck/New() + ..() + icon_state = "deck_[deckstyle]_full" + for(var/i in 2 to 10) + cards += "[i] of Hearts" + cards += "[i] of Spades" + cards += "[i] of Clubs" + cards += "[i] of Diamonds" + cards += "King of Hearts" + cards += "King of Spades" + cards += "King of Clubs" + cards += "King of Diamonds" + cards += "Queen of Hearts" + cards += "Queen of Spades" + cards += "Queen of Clubs" + cards += "Queen of Diamonds" + cards += "Jack of Hearts" + cards += "Jack of Spades" + cards += "Jack of Clubs" + cards += "Jack of Diamonds" + cards += "Ace of Hearts" + cards += "Ace of Spades" + cards += "Ace of Clubs" + cards += "Ace of Diamonds" + +obj/item/toy/cards/deck/attack_hand(mob/user as mob) + var/choice = null + if(cards.len == 0) + icon_state = "deck_[deckstyle]_empty" + to_chat(user, "There are no more cards to draw.") + return + var/obj/item/toy/cards/singlecard/H = new/obj/item/toy/cards/singlecard(user.loc) + choice = cards[1] + H.cardname = choice + H.parentdeck = src + var/O = src + H.apply_card_vars(H,O) + cards -= choice + H.pickup(user) + user.put_in_active_hand(H) + visible_message("[user] draws a card from the deck.", "You draw a card from the deck.") + update_icon() + +obj/item/toy/cards/deck/attack_self(mob/user as mob) + if(cooldown < world.time - 50) + cards = shuffle(cards) + playsound(user, 'sound/items/cardshuffle.ogg', 50, 1) + user.visible_message("[user] shuffles the deck.", "You shuffle the deck.") + cooldown = world.time + +obj/item/toy/cards/deck/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) + ..() + if(istype(C)) + if(C.parentdeck == src) + if(!user.unEquip(C)) + to_chat(user, "The card is stuck to your hand, you can't add it to the deck!") + return + cards += C.cardname + user.visible_message("[user] adds a card to the bottom of the deck.","You add the card to the bottom of the deck.") + qdel(C) + else + to_chat(user, "You can't mix cards from other decks.") + update_icon() + + +obj/item/toy/cards/deck/attackby(obj/item/toy/cards/cardhand/C, mob/living/user, params) + ..() + if(istype(C)) + if(C.parentdeck == src) + if(!user.unEquip(C)) + to_chat(user, "The hand of cards is stuck to your hand, you can't add it to the deck!") + return + cards += C.currenthand + user.visible_message("[user] puts [user.p_their()] hand of cards in the deck.", "You put the hand of cards in the deck.") + qdel(C) + else + to_chat(user, "You can't mix cards from other decks.") + update_icon() + +obj/item/toy/cards/deck/MouseDrop(atom/over_object) + var/mob/M = usr + if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) + return + if(Adjacent(usr)) + if(over_object == M && loc != M) + M.put_in_hands(src) + to_chat(usr, "You pick up the deck.") + + else if(istype(over_object, /obj/screen)) + switch(over_object.name) + if("l_hand") + if(!remove_item_from_storage(M)) + M.unEquip(src) + M.put_in_l_hand(src) + to_chat(usr, "You pick up the deck.") + if("r_hand") + if(!remove_item_from_storage(M)) + M.unEquip(src) + M.put_in_r_hand(src) + to_chat(usr, "You pick up the deck.") + else + to_chat(usr, "You can't reach it from here.") + +obj/item/toy/cards/deck/update_icon() + switch(cards.len) + if(0) + icon_state = "deck_[deckstyle]_empty" + if(1 to 10) + icon_state = "deck_[deckstyle]_low" + if(11 to 26) + icon_state = "deck_[deckstyle]_half" + else + icon_state = "deck_[deckstyle]_full" + +obj/item/toy/cards/cardhand + name = "hand of cards" + desc = "A number of cards not in a deck, customarily held in ones hand." + icon = 'icons/obj/toy.dmi' + icon_state = "nanotrasen_hand2" + w_class = WEIGHT_CLASS_TINY + var/list/currenthand = list() + var/choice = null + + +obj/item/toy/cards/cardhand/attack_self(mob/user as mob) + user.set_machine(src) + interact(user) + +obj/item/toy/cards/cardhand/interact(mob/user) + var/dat = "You have:
    " + for(var/t in currenthand) + dat += "A [t].
    " + dat += "Which card will you remove next?" + var/datum/browser/popup = new(user, "cardhand", "Hand of Cards", 400, 240) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.set_content(dat) + popup.open() + + +obj/item/toy/cards/cardhand/Topic(href, href_list) + if(..()) + return + if(usr.stat || !ishuman(usr) || !usr.canmove) + return + var/mob/living/carbon/human/cardUser = usr + var/O = src + if(href_list["pick"]) + if(cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) + var/choice = href_list["pick"] + var/obj/item/toy/cards/singlecard/C = new/obj/item/toy/cards/singlecard(cardUser.loc) + currenthand -= choice + C.parentdeck = src.parentdeck + C.cardname = choice + C.apply_card_vars(C,O) + C.pickup(cardUser) + cardUser.put_in_any_hand_if_possible(C) + cardUser.visible_message("[cardUser] draws a card from [cardUser.p_their()] hand.", "You take the [C.cardname] from your hand.") + + interact(cardUser) + update_icon() + if(currenthand.len == 1) + var/obj/item/toy/cards/singlecard/N = new/obj/item/toy/cards/singlecard(src.loc) + N.parentdeck = src.parentdeck + N.cardname = src.currenthand[1] + N.apply_card_vars(N,O) + cardUser.unEquip(src) + N.pickup(cardUser) + cardUser.put_in_any_hand_if_possible(N) + to_chat(cardUser, "You also take [currenthand[1]] and hold it.") + cardUser << browse(null, "window=cardhand") + qdel(src) + return + +obj/item/toy/cards/cardhand/attackby(obj/item/toy/cards/singlecard/C, mob/living/user, params) + if(istype(C)) + if(C.parentdeck == parentdeck) + currenthand += C.cardname + user.unEquip(C) + user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [C.cardname] to your hand.") + interact(user) + update_icon() + qdel(C) + else + to_chat(user, "You can't mix cards from other decks.") + +obj/item/toy/cards/cardhand/apply_card_vars(obj/item/toy/cards/newobj,obj/item/toy/cards/sourceobj) + ..() + newobj.deckstyle = sourceobj.deckstyle + newobj.icon_state = "[deckstyle]_hand2" // Another dumb hack, without this the hand is invisible (or has the default deckstyle) until another card is added. + newobj.card_hitsound = sourceobj.card_hitsound + newobj.card_force = sourceobj.card_force + newobj.card_throwforce = sourceobj.card_throwforce + newobj.card_throw_speed = sourceobj.card_throw_speed + newobj.card_throw_range = sourceobj.card_throw_range + newobj.card_attack_verb = sourceobj.card_attack_verb + newobj.resistance_flags = sourceobj.resistance_flags + + +obj/item/toy/cards/singlecard + name = "card" + desc = "a card" + icon = 'icons/obj/toy.dmi' + icon_state = "singlecard_nanotrasen_down" + w_class = WEIGHT_CLASS_TINY + var/cardname = null + var/flipped = 0 + pixel_x = -5 + + +obj/item/toy/cards/singlecard/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0) + if(ishuman(user)) + var/mob/living/carbon/human/cardUser = user + if(cardUser.get_item_by_slot(slot_l_hand) == src || cardUser.get_item_by_slot(slot_r_hand) == src) + cardUser.visible_message("[cardUser] checks [cardUser.p_their()] card.", "The card reads: [src.cardname]") + else + . += "You need to have the card in your hand to check it." + + +obj/item/toy/cards/singlecard/verb/Flip() + set name = "Flip Card" + set category = "Object" + set src in range(1) + if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) + return + if(!flipped) + flipped = 1 + if(cardname) + icon_state = "sc_[cardname]_[deckstyle]" + name = cardname + else + icon_state = "sc_Ace of Spades_[deckstyle]" + name = "What Card" + pixel_x = 5 + else + flipped = 0 + icon_state = "singlecard_down_[deckstyle]" + name = "card" + pixel_x = -5 + +obj/item/toy/cards/singlecard/attackby(obj/item/I, mob/living/user, params) + if(istype(I, /obj/item/toy/cards/singlecard/)) + var/obj/item/toy/cards/singlecard/C = I + if(C.parentdeck == parentdeck) + var/obj/item/toy/cards/cardhand/H = new/obj/item/toy/cards/cardhand(user.loc) + H.currenthand += C.cardname + H.currenthand += cardname + H.parentdeck = C.parentdeck + H.apply_card_vars(H,C) + user.unEquip(C) + H.pickup(user) + user.put_in_active_hand(H) + to_chat(user, "You combine the [C.cardname] and the [cardname] into a hand.") + qdel(C) + qdel(src) + else + to_chat(user, "You can't mix cards from other decks.") + + if(istype(I, /obj/item/toy/cards/cardhand/)) + var/obj/item/toy/cards/cardhand/H = I + if(H.parentdeck == parentdeck) + H.currenthand += cardname + user.unEquip(src) + user.visible_message("[user] adds a card to [user.p_their()] hand.", "You add the [cardname] to your hand.") + H.interact(user) + H.update_icon() + qdel(src) + else + to_chat(user, "You can't mix cards from other decks.") + +obj/item/toy/cards/cardhand/update_icon() + switch(currenthand.len) + if(0 to 1) + return + if(2) + icon_state = "[deckstyle]_hand2" + if(3) + icon_state = "[deckstyle]_hand3" + if(4) + icon_state = "[deckstyle]_hand4" + else + icon_state = "[deckstyle]_hand5" + + +obj/item/toy/cards/singlecard/attack_self(mob/user) + if(usr.stat || !ishuman(usr) || !usr.canmove || usr.restrained()) + return + Flip() + +obj/item/toy/cards/singlecard/apply_card_vars(obj/item/toy/cards/singlecard/newobj,obj/item/toy/cards/sourceobj) + ..() + newobj.deckstyle = sourceobj.deckstyle + newobj.icon_state = "singlecard_down_[deckstyle]" // Without this the card is invisible until flipped. It's an ugly hack, but it works. + newobj.card_hitsound = sourceobj.card_hitsound + newobj.hitsound = newobj.card_hitsound + newobj.card_force = sourceobj.card_force + newobj.force = newobj.card_force + newobj.card_throwforce = sourceobj.card_throwforce + newobj.throwforce = newobj.card_throwforce + newobj.card_throw_speed = sourceobj.card_throw_speed + newobj.throw_speed = newobj.card_throw_speed + newobj.card_throw_range = sourceobj.card_throw_range + newobj.throw_range = newobj.card_throw_range + newobj.card_attack_verb = sourceobj.card_attack_verb + newobj.attack_verb = newobj.card_attack_verb + + +/* +|| Syndicate playing cards, for pretending you're Gambit and playing poker for the nuke disk. || +*/ + +obj/item/toy/cards/deck/syndicate + name = "suspicious looking deck of cards" + desc = "A deck of space-grade playing cards. They seem unusually rigid." + deckstyle = "syndicate" + card_hitsound = 'sound/weapons/bladeslice.ogg' + card_force = 5 + card_throwforce = 10 + card_throw_speed = 3 + card_throw_range = 20 + card_attack_verb = list("attacked", "sliced", "diced", "slashed", "cut") + resistance_flags = NONE + +/* +|| Custom card decks || +*/ +obj/item/toy/cards/deck/black + deckstyle = "black" + +obj/item/toy/cards/deck/syndicate/black + deckstyle = "black" + +/obj/item/toy/nuke + name = "\improper Nuclear Fission Explosive toy" + desc = "A plastic model of a Nuclear Fission Explosive." + icon = 'icons/obj/toy.dmi' + icon_state = "nuketoyidle" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + +/obj/item/toy/nuke/attack_self(mob/user) + if(cooldown < world.time) + cooldown = world.time + 1800 //3 minutes + user.visible_message("[user] presses a button on [src]", "You activate [src], it plays a loud noise!", "You hear the click of a button.") + spawn(5) //gia said so + icon_state = "nuketoy" + playsound(src, 'sound/machines/alarm.ogg', 100, 0, 0) + sleep(135) + icon_state = "nuketoycool" + sleep(cooldown - world.time) + icon_state = "nuketoyidle" + else + var/timeleft = (cooldown - world.time) + to_chat(user, "Nothing happens, and '[round(timeleft/10)]' appears on a small display.") + +/obj/item/toy/therapy + name = "therapy doll" + desc = "A toy for therapeutic and recreational purposes." + icon = 'icons/obj/toy.dmi' + icon_state = "therapyred" + item_state = "egg4" + w_class = WEIGHT_CLASS_TINY + var/cooldown = 0 + resistance_flags = FLAMMABLE + +/obj/item/toy/therapy/New() + ..() + if(item_color) + name = "[item_color] therapy doll" + desc += " This one is [item_color]." + icon_state = "therapy[item_color]" + +/obj/item/toy/therapy/attack_self(mob/user) + if(cooldown < world.time - 8) + to_chat(user, "You relieve some stress with \the [src].") + playsound(user, 'sound/items/squeaktoy.ogg', 20, 1) + cooldown = world.time + +/obj/random/therapy + name = "Random Therapy Doll" + desc = "This is a random therapy doll." + icon = 'icons/obj/toy.dmi' + icon_state = "therapyred" + +/obj/random/therapy/item_to_spawn() + return pick(subtypesof(/obj/item/toy/therapy)) //exclude the base type. + +/obj/item/toy/therapy/red + item_state = "egg4" // It's the red egg in items_left/righthand + item_color = "red" + +/obj/item/toy/therapy/purple + item_state = "egg1" // It's the magenta egg in items_left/righthand + item_color = "purple" + +/obj/item/toy/therapy/blue + item_state = "egg2" // It's the blue egg in items_left/righthand + item_color = "blue" + +/obj/item/toy/therapy/yellow + item_state = "egg5" // It's the yellow egg in items_left/righthand + item_color = "yellow" + +/obj/item/toy/therapy/orange + item_state = "egg4" // It's the red one again, lacking an orange item_state and making a new one is pointless + item_color = "orange" + +/obj/item/toy/therapy/green + item_state = "egg3" // It's the green egg in items_left/righthand + item_color = "green" + +/obj/item/toddler + icon_state = "toddler" + name = "toddler" + desc = "This baby looks almost real. Wait, did it just burp?" + force = 5 + w_class = WEIGHT_CLASS_BULKY + slot_flags = SLOT_BACK + + +//This should really be somewhere else but I don't know where. w/e + +/obj/item/inflatable_duck + name = "inflatable duck" + desc = "No bother to sink or swim when you can just float!" + icon_state = "inflatable" + item_state = "inflatable" + icon = 'icons/obj/clothing/belts.dmi' + slot_flags = SLOT_BELT + +/* + * Fake meteor + */ + +/obj/item/toy/minimeteor + name = "Mini-Meteor" + desc = "Relive the excitement of a meteor shower! SweetMeat-eor. Co is not responsible for any injuries, headaches or hearing loss caused by Mini-Meteor." + icon = 'icons/obj/toy.dmi' + icon_state = "minimeteor" + w_class = WEIGHT_CLASS_SMALL + +/obj/item/toy/minimeteor/throw_impact(atom/hit_atom) + ..() + playsound(src, 'sound/effects/meteorimpact.ogg', 40, 1) + for(var/mob/M in range(10, src)) + if(!M.stat && !istype(M, /mob/living/silicon/ai))\ + shake_camera(M, 3, 1) + qdel(src) + +/* + * Carp plushie + */ + +/obj/item/toy/carpplushie + name = "space carp plushie" + desc = "An adorable stuffed toy that resembles a space carp." + icon = 'icons/obj/toy.dmi' + icon_state = "carpplushie" + attack_verb = list("bitten", "eaten", "fin slapped") + var/bitesound = 'sound/weapons/bite.ogg' + resistance_flags = FLAMMABLE + +// Attack mob +/obj/item/toy/carpplushie/attack(mob/M as mob, mob/user as mob) + playsound(loc, bitesound, 20, 1) // Play bite sound in local area + return ..() + +// Attack self +/obj/item/toy/carpplushie/attack_self(mob/user as mob) + playsound(src.loc, bitesound, 20, 1) + return ..() + + +/obj/random/carp_plushie + name = "Random Carp Plushie" + desc = "This is a random plushie" + icon = 'icons/obj/toy.dmi' + icon_state = "carpplushie" + +/obj/random/carp_plushie/item_to_spawn() + return pick(typesof(/obj/item/toy/carpplushie)) //can pick any carp plushie, even the original. + +/obj/item/toy/carpplushie/ice + icon_state = "icecarp" + +/obj/item/toy/carpplushie/silent + icon_state = "silentcarp" + +/obj/item/toy/carpplushie/electric + icon_state = "electriccarp" + +/obj/item/toy/carpplushie/gold + icon_state = "goldcarp" + +/obj/item/toy/carpplushie/toxin + icon_state = "toxincarp" + +/obj/item/toy/carpplushie/dragon + icon_state = "dragoncarp" + +/obj/item/toy/carpplushie/pink + icon_state = "pinkcarp" + +/obj/item/toy/carpplushie/candy + icon_state = "candycarp" + +/obj/item/toy/carpplushie/nebula + icon_state = "nebulacarp" + +/obj/item/toy/carpplushie/void + icon_state = "voidcarp" + +/* + * Plushie + */ + + +/obj/item/toy/plushie + name = "plushie" + desc = "An adorable, soft, and cuddly plushie." + icon = 'icons/obj/toy.dmi' + var/poof_sound = 'sound/weapons/thudswoosh.ogg' + attack_verb = list("poofed", "bopped", "whapped","cuddled","fluffed") + resistance_flags = FLAMMABLE + +/obj/item/toy/plushie/attack(mob/M as mob, mob/user as mob) + playsound(loc, poof_sound, 20, 1) // Play the whoosh sound in local area + if(iscarbon(M)) + if(prob(10)) + M.reagents.add_reagent("hugs", 10) + return ..() + +/obj/item/toy/plushie/attack_self(mob/user as mob) + var/cuddle_verb = pick("hugs","cuddles","snugs") + user.visible_message("[user] [cuddle_verb] the [src].") + playsound(get_turf(src), poof_sound, 50, 1, -1) + return ..() + +/obj/random/plushie + name = "Random Plushie" + desc = "This is a random plushie" + icon = 'icons/obj/toy.dmi' + icon_state = "redfox" + +/obj/random/plushie/item_to_spawn() + return pick(subtypesof(/obj/item/toy/plushie) - typesof(/obj/item/toy/plushie/fluff)) //exclude the base type. + +/obj/item/toy/plushie/corgi + name = "corgi plushie" + icon_state = "corgi" + +/obj/item/toy/plushie/girly_corgi + name = "corgi plushie" + icon_state = "girlycorgi" + +/obj/item/toy/plushie/robo_corgi + name = "borgi plushie" + icon_state = "robotcorgi" + +/obj/item/toy/plushie/octopus + name = "octopus plushie" + icon_state = "loveable" + +/obj/item/toy/plushie/face_hugger + name = "facehugger plushie" + icon_state = "huggable" + +//foxes are basically the best + +/obj/item/toy/plushie/red_fox + name = "red fox plushie" + icon_state = "redfox" + +/obj/item/toy/plushie/black_fox + name = "black fox plushie" + icon_state = "blackfox" + +/obj/item/toy/plushie/marble_fox + name = "marble fox plushie" + icon_state = "marblefox" + +/obj/item/toy/plushie/blue_fox + name = "blue fox plushie" + icon_state = "bluefox" + +/obj/item/toy/plushie/orange_fox + name = "orange fox plushie" + icon_state = "orangefox" + +/obj/item/toy/plushie/coffee_fox + name = "coffee fox plushie" + icon_state = "coffeefox" + +/obj/item/toy/plushie/pink_fox + name = "pink fox plushie" + icon_state = "pinkfox" + +/obj/item/toy/plushie/purple_fox + name = "purple fox plushie" + icon_state = "purplefox" + +/obj/item/toy/plushie/crimson_fox + name = "crimson fox plushie" + icon_state = "crimsonfox" + +/obj/item/toy/plushie/deer + name = "deer plushie" + icon_state = "deer" + +/obj/item/toy/plushie/black_cat + name = "black cat plushie" + icon_state = "blackcat" + +/obj/item/toy/plushie/grey_cat + name = "grey cat plushie" + icon_state = "greycat" + +/obj/item/toy/plushie/white_cat + name = "white cat plushie" + icon_state = "whitecat" + +/obj/item/toy/plushie/orange_cat + name = "orange cat plushie" + icon_state = "orangecat" + +/obj/item/toy/plushie/siamese_cat + name = "siamese cat plushie" + icon_state = "siamesecat" + +/obj/item/toy/plushie/tabby_cat + name = "tabby cat plushie" + icon_state = "tabbycat" + +/obj/item/toy/plushie/tuxedo_cat + name = "tuxedo cat plushie" + icon_state = "tuxedocat" + +/obj/item/toy/plushie/voxplushie + name = "vox plushie" + desc = "A stitched-together vox, fresh from the skipjack. Press its belly to hear it skree!" + icon_state = "plushie_vox" + item_state = "plushie_vox" + var/cooldown = 0 + +/obj/item/toy/plushie/voxplushie/attack_self(mob/user) + if(!cooldown) + playsound(user, 'sound/voice/shriek1.ogg', 10, 0) + visible_message("Skreee!") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + +//New generation TG plushies + +/obj/item/toy/plushie/lizardplushie + name = "lizard plushie" + desc = "An adorable stuffed toy that resembles a lizardperson." + icon_state = "plushie_lizard" + item_state = "plushie_lizard" + +/obj/item/toy/plushie/snakeplushie + name = "snake plushie" + desc = "An adorable stuffed toy that resembles a snake. Not to be mistaken for the real thing." + icon_state = "plushie_snake" + item_state = "plushie_snake" + +/obj/item/toy/plushie/nukeplushie + name = "operative plushie" + desc = "An stuffed toy that resembles a syndicate nuclear operative. The tag claims operatives to be purely fictitious." + icon_state = "plushie_nuke" + item_state = "plushie_nuke" + +/obj/item/toy/plushie/slimeplushie + name = "slime plushie" + desc = "An adorable stuffed toy that resembles a slime. It is practically just a hacky sack." + icon_state = "plushie_slime" + item_state = "plushie_slime" + +/* + * Foam Armblade + */ + + /obj/item/toy/foamblade + name = "foam armblade" + desc = "it says \"Sternside Changs #1 fan\" on it. " + icon = 'icons/obj/toy.dmi' + icon_state = "foamblade" + item_state = "arm_blade" + attack_verb = list("pricked", "absorbed", "gored") + w_class = WEIGHT_CLASS_SMALL + resistance_flags = FLAMMABLE + +/* + * Toy/fake flash + */ +/obj/item/toy/flash + name = "toy flash" + desc = "FOR THE REVOLU- Oh wait, that's just a toy." + icon = 'icons/obj/device.dmi' + icon_state = "flash" + item_state = "flashtool" + w_class = WEIGHT_CLASS_TINY + +/obj/item/toy/flash/attack(mob/living/M, mob/user) + playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1) + flick("[initial(icon_state)]2", src) + user.visible_message("[user] blinds [M] with the flash!") + + +/* + * Toy big red button + */ +/obj/item/toy/redbutton + name = "big red button" + desc = "A big, plastic red button. Reads 'From HonkCo Pranks?' on the back." + icon = 'icons/obj/assemblies.dmi' + icon_state = "bigred" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + +/obj/item/toy/redbutton/attack_self(mob/user) + if(cooldown < world.time) + cooldown = (world.time + 300) // Sets cooldown at 30 seconds + user.visible_message("[user] presses the big red button.", "You press the button, it plays a loud noise!", "The button clicks loudly.") + playsound(src, 'sound/effects/explosionfar.ogg', 50, 0, 0) + for(var/mob/M in range(10, src)) // Checks range + if(!M.stat && !istype(M, /mob/living/silicon/ai)) // Checks to make sure whoever's getting shaken is alive/not the AI + sleep(8) // Short delay to match up with the explosion sound + shake_camera(M, 2, 1) // Shakes player camera 2 squares for 1 second. + + else + to_chat(user, "Nothing happens.") + + +/* + * AI core prizes + */ +/obj/item/toy/AI + name = "toy AI" + desc = "A little toy model AI core with real law announcing action!" + icon = 'icons/obj/toy.dmi' + icon_state = "AI" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + +/obj/item/toy/AI/attack_self(mob/user) + if(!cooldown) //for the sanity of everyone + var/message = generate_ion_law() + to_chat(user, "You press the button on [src].") + playsound(user, 'sound/machines/click.ogg', 20, 1) + visible_message("[bicon(src)] [message]") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + +/obj/item/toy/codex_gigas + name = "Toy Codex Gigas" + desc = "A tool to help you write fictional devils!" + icon = 'icons/obj/library.dmi' + icon_state = "demonomicon" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = FALSE + +/obj/item/toy/codex_gigas/attack_self(mob/user) + if(!cooldown) + user.visible_message( + "[user] presses the button on \the [src].", + "You press the button on \the [src].", + "You hear a soft click.") + var/list/messages = list() + var/datum/devilinfo/devil = randomDevilInfo() + messages += "Some fun facts about: [devil.truename]" + messages += "[GLOB.lawlorify[LORE][devil.bane]]" + messages += "[GLOB.lawlorify[LORE][devil.obligation]]" + messages += "[GLOB.lawlorify[LORE][devil.ban]]" + messages += "[GLOB.lawlorify[LORE][devil.banish]]" + playsound(loc, 'sound/machines/click.ogg', 20, 1) + cooldown = TRUE + for(var/message in messages) + user.loc.visible_message("[bicon(src)] [message]") + sleep(10) + spawn(20) + cooldown = FALSE + return + ..() + +/obj/item/toy/owl + name = "owl action figure" + desc = "An action figure modeled after 'The Owl', defender of justice." + icon = 'icons/obj/toy.dmi' + icon_state = "owlprize" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + +/obj/item/toy/owl/attack_self(mob/user) + if(!cooldown) //for the sanity of everyone + var/message = pick("You won't get away this time, Griffin!", "Stop right there, criminal!", "Hoot! Hoot!", "I am the night!") + to_chat(user, "You pull the string on the [src].") + playsound(user, 'sound/creatures/hoot.ogg', 25, 1) + visible_message("[bicon(src)] [message]") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + +/obj/item/toy/griffin + name = "griffin action figure" + desc = "An action figure modeled after 'The Griffin', criminal mastermind." + icon = 'icons/obj/toy.dmi' + icon_state = "griffinprize" + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + +/obj/item/toy/griffin/attack_self(mob/user) + if(!cooldown) //for the sanity of everyone + var/message = pick("You can't stop me, Owl!", "My plan is flawless! The vault is mine!", "Caaaawwww!", "You will never catch me!") + to_chat(user, "You pull the string on the [src].") + playsound(user, 'sound/creatures/caw.ogg', 25, 1) + visible_message("[bicon(src)] [message]") + cooldown = 1 + spawn(30) cooldown = 0 + return + ..() + +// DND Character minis. Use the naming convention (type)character for the icon states. +/obj/item/toy/character + icon = 'icons/obj/toy.dmi' + w_class = WEIGHT_CLASS_SMALL + pixel_z = 5 + +/obj/item/toy/character/alien + name = "Xenomorph Miniature" + desc = "A miniature xenomorph. Scary!" + icon_state = "aliencharacter" +/obj/item/toy/character/cleric + name = "Cleric Miniature" + desc = "A wee little cleric, with his wee little staff." + icon_state = "clericcharacter" +/obj/item/toy/character/warrior + name = "Warrior Miniature" + desc = "That sword would make a decent toothpick." + icon_state = "warriorcharacter" +/obj/item/toy/character/thief + name = "Thief Miniature" + desc = "Hey, where did my wallet go!?" + icon_state = "thiefcharacter" +/obj/item/toy/character/wizard + name = "Wizard Miniature" + desc = "MAGIC!" + icon_state = "wizardcharacter" +/obj/item/toy/character/cthulhu + name = "Cthulhu Miniature" + desc = "The dark lord has risen!" + icon_state = "darkmastercharacter" +/obj/item/toy/character/lich + name = "Lich Miniature" + desc = "Murderboner extraordinaire." + icon_state = "lichcharacter" +/obj/item/storage/box/characters + name = "Box of Miniatures" + desc = "The nerd's best friends." + icon_state = "box" +/obj/item/storage/box/characters/New() + ..() + new /obj/item/toy/character/alien(src) + new /obj/item/toy/character/cleric(src) + new /obj/item/toy/character/warrior(src) + new /obj/item/toy/character/thief(src) + new /obj/item/toy/character/wizard(src) + new /obj/item/toy/character/cthulhu(src) + new /obj/item/toy/character/lich(src) + + +//Pet Rocks, just like from the 70's! + +/obj/item/toy/pet_rock + name = "pet rock" + desc = "The perfect pet!" + icon = 'icons/obj/toy.dmi' + icon_state = "pet_rock" + w_class = WEIGHT_CLASS_SMALL + force = 5 + throwforce = 5 + attack_verb = list("attacked", "bashed", "smashed", "stoned") + hitsound = "swing_hit" + +/obj/item/toy/pet_rock/fred + name = "fred" + desc = "Fred, the bestest boy pet in the whole wide universe!" + icon_state = "fred" + +/obj/item/toy/pet_rock/roxie + name = "roxie" + desc = "Roxie, the bestest girl pet in the whole wide universe!" + icon_state = "roxie" + +//minigibber, so cute + +/obj/item/toy/minigibber + name = "miniature gibber" + desc = "A miniature recreation of Nanotrasen's famous meat grinder." + icon = 'icons/obj/toy.dmi' + icon_state = "minigibber" + attack_verb = list("grinded", "gibbed") + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + var/obj/stored_minature = null + +/obj/item/toy/minigibber/attack_self(var/mob/user) + + if(stored_minature) + to_chat(user, "\The [src] makes a violent grinding noise as it tears apart the miniature figure inside!") + QDEL_NULL(stored_minature) + playsound(user, 'sound/goonstation/effects/gib.ogg', 20, 1) + cooldown = world.time + + if(cooldown < world.time - 8) + to_chat(user, "You hit the gib button on \the [src].") + playsound(user, 'sound/goonstation/effects/gib.ogg', 20, 1) + cooldown = world.time + +/obj/item/toy/minigibber/attackby(var/obj/O, var/mob/user, params) + if(istype(O,/obj/item/toy/character) && O.loc == user) + to_chat(user, "You start feeding \the [O] [bicon(O)] into \the [src]'s mini-input.") + if(do_after(user, 10, target = src)) + if(O.loc != user) + to_chat(user, "\The [O] is too far away to feed into \the [src]!") + else + to_chat(user, "You feed \the [O] [bicon(O)] into \the [src]!") + user.unEquip(O) + O.forceMove(src) + stored_minature = O + else + to_chat(user, "You stop feeding \the [O] into \the [src]'s mini-input.") + else ..() + +/* + * Xenomorph action figure + */ + +/obj/item/toy/toy_xeno + icon = 'icons/obj/toy.dmi' + icon_state = "toy_xeno" + name = "xenomorph action figure" + desc = "MEGA presents the new Xenos Isolated action figure! Comes complete with realistic sounds! Pull back string to use." + w_class = WEIGHT_CLASS_SMALL + var/cooldown = 0 + +/obj/item/toy/toy_xeno/attack_self(mob/user) + if(cooldown <= world.time) + cooldown = (world.time + 50) //5 second cooldown + user.visible_message("[user] pulls back the string on [src].") + icon_state = "[initial(icon_state)]_used" + sleep(5) + audible_message("[bicon(src)] Hiss!") + var/list/possible_sounds = list('sound/voice/hiss1.ogg', 'sound/voice/hiss2.ogg', 'sound/voice/hiss3.ogg', 'sound/voice/hiss4.ogg') + playsound(get_turf(src), pick(possible_sounds), 50, 1) + spawn(45) + if(src) + icon_state = "[initial(icon_state)]" + else + to_chat(user, "The string on [src] hasn't rewound all the way!") + return + +/obj/item/toy/russian_revolver + name = "russian revolver" + desc = "for fun and games!" + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "detective_gold" + item_state = "gun" + lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/guns_righthand.dmi' + hitsound = "swing_hit" + flags = CONDUCT + slot_flags = SLOT_BELT + materials = list(MAT_METAL=2000) + w_class = WEIGHT_CLASS_NORMAL + throwforce = 5 + throw_speed = 4 + throw_range = 5 + force = 5 + origin_tech = "combat=1" + attack_verb = list("struck", "hit", "bashed") + var/bullets_left = 0 + var/max_shots = 6 + +/obj/item/toy/russian_revolver/suicide_act(mob/user) + user.visible_message("[user] quickly loads six bullets into [src]'s cylinder and points it at [user.p_their()] head before pulling the trigger! It looks like [user.p_theyre()] trying to commit suicide.") + playsound(loc, 'sound/weapons/gunshots/gunshot_strong.ogg', 50, 1) + return BRUTELOSS + +/obj/item/toy/russian_revolver/New() + ..() + spin_cylinder() + +/obj/item/toy/russian_revolver/attack_self(mob/user) + if(!bullets_left) + user.visible_message("[user] loads a bullet into [src]'s cylinder before spinning it.") + spin_cylinder() + else + user.visible_message("[user] spins the cylinder on [src]!") + spin_cylinder() + +/obj/item/toy/russian_revolver/attack(mob/M, mob/living/user) + return + +/obj/item/toy/russian_revolver/afterattack(atom/target, mob/user, flag, params) + if(flag) + if(target in user.contents) + return + if(!ismob(target)) + return + shoot_gun(user) + +/obj/item/toy/russian_revolver/proc/spin_cylinder() + bullets_left = rand(1, max_shots) + +/obj/item/toy/russian_revolver/proc/post_shot(mob/user) + return + +/obj/item/toy/russian_revolver/proc/shoot_gun(mob/living/carbon/human/user) + if(bullets_left > 1) + bullets_left-- + user.visible_message("*click*") + playsound(src, 'sound/weapons/empty.ogg', 100, 1) + return FALSE + if(bullets_left == 1) + bullets_left = 0 + var/zone = "head" + if(!(user.has_organ(zone))) // If they somehow don't have a head. + zone = "chest" + playsound(src, 'sound/weapons/gunshots/gunshot_strong.ogg', 50, 1) + user.visible_message("[src] goes off!") + post_shot(user) + user.apply_damage(300, BRUTE, zone, sharp = TRUE, used_weapon = "Self-inflicted gunshot wound to the [zone].") + user.bleed(BLOOD_VOLUME_NORMAL) + user.death() // Just in case + return TRUE + else + to_chat(user, "[src] needs to be reloaded.") + return FALSE + +/obj/item/toy/russian_revolver/trick_revolver + name = "\improper .357 revolver" + desc = "A suspicious revolver. Uses .357 ammo." + icon_state = "revolver" + max_shots = 1 + var/fake_bullets = 0 + +/obj/item/toy/russian_revolver/trick_revolver/New() + ..() + fake_bullets = rand(2, 7) + +/obj/item/toy/russian_revolver/trick_revolver/examine(mob/user) //Sneaky sneaky + . = ..() + . += "Has [fake_bullets] round\s remaining." + . += "[fake_bullets] of those are live rounds." + +/obj/item/toy/russian_revolver/trick_revolver/post_shot(user) + to_chat(user, "[src] did look pretty dodgey!") + SEND_SOUND(user, 'sound/misc/sadtrombone.ogg') //HONK +/* + * Rubber Chainsaw + */ +/obj/item/twohanded/toy/chainsaw + name = "Toy Chainsaw" + desc = "A toy chainsaw with a rubber edge. Ages 8 and up" + icon_state = "chainsaw0" + force = 0 + throwforce = 0 + throw_speed = 4 + throw_range = 20 + wieldsound = 'sound/weapons/chainsawstart.ogg' + attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered") + +/obj/item/twohanded/toy/chainsaw/update_icon() + if(wielded) + icon_state = "chainsaw[wielded]" + else + icon_state = "chainsaw0" + +/* + * Cat Toy + */ +/obj/item/toy/cattoy + name = "toy mouse" + desc = "A colorful toy mouse!" + icon = 'icons/obj/toy.dmi' + icon_state = "toy_mouse" + w_class = WEIGHT_CLASS_SMALL + resistance_flags = FLAMMABLE + var/cooldown = 0 + +/* + * Action Figures + */ + + +/obj/random/figure + name = "Random Action Figure" + desc = "This is a random toy action figure" + icon = 'icons/obj/toy.dmi' + icon_state = "nuketoy" + +/obj/random/figure/item_to_spawn() + return pick(subtypesof(/obj/item/toy/figure)) + + +/obj/item/toy/figure + name = "Non-Specific Action Figure action figure" + desc = "A \"Space Life\" brand... wait, what the hell is this thing?" + icon = 'icons/obj/toy.dmi' + icon_state = "nuketoy" + var/cooldown = 0 + var/toysay = "What the fuck did you do?" + +/obj/item/toy/figure/New() + ..() + desc = "A \"Space Life\" brand [name]" + +/obj/item/toy/figure/attack_self(mob/user as mob) + if(cooldown < world.time) + cooldown = (world.time + 30) //3 second cooldown + user.visible_message("[bicon(src)] The [src] says \"[toysay]\".") + playsound(user, 'sound/machines/click.ogg', 20, 1) + +/obj/item/toy/figure/cmo + name = "Chief Medical Officer action figure" + icon_state = "cmo" + toysay = "Suit sensors!" + +/obj/item/toy/figure/assistant + name = "Assistant action figure" + icon_state = "assistant" + toysay = "Grey tide station wide!" + +/obj/item/toy/figure/atmos + name = "Atmospheric Technician action figure" + icon_state = "atmos" + toysay = "Glory to Atmosia!" + +/obj/item/toy/figure/bartender + name = "Bartender action figure" + icon_state = "bartender" + toysay = "Wheres my monkey?" + +/obj/item/toy/figure/borg + name = "Cyborg action figure" + icon_state = "borg" + toysay = "I. LIVE. AGAIN." + +/obj/item/toy/figure/botanist + name = "Botanist action figure" + icon_state = "botanist" + toysay = "Dude, I see colors..." + +/obj/item/toy/figure/captain + name = "Captain action figure" + icon_state = "captain" + toysay = "Crew, the Nuke Disk is safely up my ass." + +/obj/item/toy/figure/cargotech + name = "Cargo Technician action figure" + icon_state = "cargotech" + toysay = "For Cargonia!" + +/obj/item/toy/figure/ce + name = "Chief Engineer action figure" + icon_state = "ce" + toysay = "Wire the solars!" + +/obj/item/toy/figure/chaplain + name = "Chaplain action figure" + icon_state = "chaplain" + toysay = "Gods make me a killing machine please!" + +/obj/item/toy/figure/chef + name = "Chef action figure" + icon_state = "chef" + toysay = "I swear it's not human meat." + +/obj/item/toy/figure/chemist + name = "Chemist action figure" + icon_state = "chemist" + toysay = "Get your pills!" + +/obj/item/toy/figure/clown + name = "Clown action figure" + icon_state = "clown" + toysay = "Honk!" + +/obj/item/toy/figure/ian + name = "Ian action figure" + icon_state = "ian" + toysay = "Arf!" + +/obj/item/toy/figure/detective + name = "Detective action figure" + icon_state = "detective" + toysay = "This airlock has grey jumpsuit and insulated glove fibers on it." + +/obj/item/toy/figure/dsquad + name = "Death Squad Officer action figure" + icon_state = "dsquad" + toysay = "Eliminate all threats!" + +/obj/item/toy/figure/engineer + name = "Engineer action figure" + icon_state = "engineer" + toysay = "Oh god, the singularity is loose!" + +/obj/item/toy/figure/geneticist + name = "Geneticist action figure" + icon_state = "geneticist" + toysay = "I'm not qualified for this job." + +/obj/item/toy/figure/hop + name = "Head of Personnel action figure" + icon_state = "hop" + toysay = "Giving out all access!" + +/obj/item/toy/figure/hos + name = "Head of Security action figure" + icon_state = "hos" + toysay = "I'm here to win, anything else is secondary." + +/obj/item/toy/figure/qm + name = "Quartermaster action figure" + icon_state = "qm" + toysay = "Hail Cargonia!" + +/obj/item/toy/figure/janitor + name = "Janitor action figure" + icon_state = "janitor" + toysay = "Look at the signs, you idiot." + +/obj/item/toy/figure/lawyer + name = "Internal Affairs Agent action figure" + icon_state = "lawyer" + toysay = "Standard Operating Procedure says they're guilty! Hacking is proof they're an Enemy of the Corporation!" + +/obj/item/toy/figure/librarian + name = "Librarian action figure" + icon_state = "librarian" + toysay = "One day while..." + +/obj/item/toy/figure/md + name = "Medical Doctor action figure" + icon_state = "md" + toysay = "The patient is already dead!" + +/obj/item/toy/figure/mime + name = "Mime action figure" + desc = "A \"Space Life\" brand Mime action figure." + icon_state = "mime" + toysay = "..." + +/obj/item/toy/figure/miner + name = "Shaft Miner action figure" + icon_state = "miner" + toysay = "Oh god it's eating my intestines!" + +/obj/item/toy/figure/ninja + name = "Ninja action figure" + icon_state = "ninja" + toysay = "Oh god! Stop shooting, I'm friendly!" + +/obj/item/toy/figure/wizard + name = "Wizard action figure" + icon_state = "wizard" + toysay = "Ei Nath!" + +/obj/item/toy/figure/rd + name = "Research Director action figure" + icon_state = "rd" + toysay = "Blowing all of the borgs!" + +/obj/item/toy/figure/roboticist + name = "Roboticist action figure" + icon_state = "roboticist" + toysay = "He asked to be borged!" + +/obj/item/toy/figure/scientist + name = "Scientist action figure" + icon_state = "scientist" + toysay = "Someone else must have made those bombs!" + +/obj/item/toy/figure/syndie + name = "Nuclear Operative action figure" + icon_state = "syndie" + toysay = "Get that fucking disk!" + +/obj/item/toy/figure/secofficer + name = "Security Officer action figure" + icon_state = "secofficer" + toysay = "I am the law!" + +/obj/item/toy/figure/virologist + name = "Virologist action figure" + icon_state = "virologist" + toysay = "The cure is potassium!" + +/obj/item/toy/figure/warden + name = "Warden action figure" + icon_state = "warden" + toysay = "Execute him for breaking in!" + +////////////////////////////////////////////////////// +// Magic 8-Ball / Conch // +////////////////////////////////////////////////////// + +/obj/item/toy/eight_ball + name = "Magic 8-Ball" + desc = "Mystical! Magical! Ages 8+!" + icon = 'icons/obj/toy.dmi' + icon_state = "eight-ball" + var/use_action = "shakes the ball" + var/cooldown = 0 + var/list/possible_answers = list("Definitely", "All signs point to yes.", "Most likely.", "Yes.", "Ask again later.", "Better not tell you now.", "Future Unclear.", "Maybe.", "Doubtful.", "No.", "Don't count on it.", "Never.") + +/obj/item/toy/eight_ball/attack_self(mob/user as mob) + if(!cooldown) + var/answer = pick(possible_answers) + user.visible_message("[user] focuses on [user.p_their()] question and [use_action]...") + user.visible_message("[bicon(src)] The [src] says \"[answer]\"") + spawn(30) + cooldown = 0 + return + +/obj/item/toy/eight_ball/conch + name = "Magic Conch Shell" + desc = "All hail the Magic Conch!" + icon_state = "conch" + use_action = "pulls the string" + possible_answers = list("Yes.", "No.", "Try asking again.", "Nothing.", "I don't think so.", "Neither.", "Maybe someday.") + +/* + *Fake cuffs (honk honk) + */ + +/obj/item/restraints/handcuffs/toy + desc = "Toy handcuffs. Plastic and extremely cheaply made." + throwforce = 0 + breakouttime = 0 + ignoresClumsy = TRUE diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 3a6101b65c76..7cf48095e723 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -1,103 +1,107 @@ -//Items labled as 'trash' for the trash bag. -//TODO: Make this an item var or something... - -//Added by Jack Rost -/obj/item/trash - icon = 'icons/obj/trash.dmi' - w_class = WEIGHT_CLASS_TINY - desc = "This is rubbish." - resistance_flags = FLAMMABLE - -/obj/item/trash/raisins - name = "4no raisins" - icon_state= "4no_raisins" - -/obj/item/trash/candy - name = "Candy" - icon_state= "candy" - -/obj/item/trash/cheesie - name = "Cheesie honkers" - icon_state = "cheesie_honkers" - -/obj/item/trash/chips - name = "Chips" - icon_state = "chips" - -/obj/item/trash/popcorn - name = "Popcorn" - icon_state = "popcorn" - -/obj/item/trash/sosjerky - name = "Scaredy's Private Reserve Beef Jerky" - icon_state = "sosjerky" - -/obj/item/trash/syndi_cakes - name = "Syndi cakes" - icon_state = "syndi_cakes" - -/obj/item/trash/waffles - name = "Waffles" - icon_state = "waffles" - -/obj/item/trash/plate - name = "Plate" - icon_state = "plate" - resistance_flags = NONE - -/obj/item/trash/snack_bowl - name = "Snack bowl" - icon_state = "snack_bowl" - -/obj/item/trash/pistachios - name = "Pistachios pack" - icon_state = "pistachios_pack" - -/obj/item/trash/semki - name = "Semki pack" - icon_state = "semki_pack" - -/obj/item/trash/tray - name = "Tray" - icon_state = "tray" - resistance_flags = NONE - -/obj/item/trash/candle - name = "candle" - icon = 'icons/obj/candle.dmi' - icon_state = "candle4" - -/obj/item/trash/liquidfood - name = "\improper \"LiquidFood\" ration" - icon_state = "liquidfood" - -/obj/item/trash/can - name = "crushed can" - icon_state = "cola" - var/is_glass = 0 - var/is_plastic = 0 - resistance_flags = NONE - -/obj/item/trash/gum - name = "chewed gum" - desc = "NOT free candy." - icon_state = "gum" - -/obj/item/trash/tastybread - name = "bread tube" - icon_state = "tastybread" - -/obj/item/trash/spentcasing - icon = 'icons/obj/ammo.dmi' - name = "bullet casing" - desc = "A spent bullet casing. Smells like cordite." - icon_state = "gshell" - -/obj/item/trash/tapetrash - name = "old duct tape" - icon_state = "tape" - desc = "Not sticky anymore." - throw_range = 1 - -/obj/item/trash/attack(mob/M as mob, mob/living/user as mob) - return +//Items labled as 'trash' for the trash bag. +//TODO: Make this an item var or something... + +//Added by Jack Rost +/obj/item/trash + icon = 'icons/obj/trash.dmi' + w_class = WEIGHT_CLASS_TINY + desc = "This is rubbish." + resistance_flags = FLAMMABLE + +/obj/item/trash/raisins + name = "4no raisins" + icon_state= "4no_raisins" + +/obj/item/trash/candy + name = "Candy" + icon_state= "candy" + +/obj/item/trash/cheesie + name = "Cheesie honkers" + icon_state = "cheesie_honkers" + +/obj/item/trash/chips + name = "Chips" + icon_state = "chips" + +/obj/item/trash/popcorn + name = "Popcorn" + icon_state = "popcorn" + +/obj/item/trash/sosjerky + name = "Scaredy's Private Reserve Beef Jerky" + icon_state = "sosjerky" + +/obj/item/trash/syndi_cakes + name = "Syndi cakes" + icon_state = "syndi_cakes" + +/obj/item/trash/waffles + name = "Waffles" + icon_state = "waffles" + +/obj/item/trash/plate + name = "Plate" + icon_state = "plate" + resistance_flags = NONE + +/obj/item/trash/snack_bowl + name = "Snack bowl" + icon_state = "snack_bowl" + +/obj/item/trash/fried_vox + name = "Kentucky Fried Vox" + icon_state = "fried_vox_empty" + +/obj/item/trash/pistachios + name = "Pistachios pack" + icon_state = "pistachios_pack" + +/obj/item/trash/semki + name = "Semki pack" + icon_state = "semki_pack" + +/obj/item/trash/tray + name = "Tray" + icon_state = "tray" + resistance_flags = NONE + +/obj/item/trash/candle + name = "candle" + icon = 'icons/obj/candle.dmi' + icon_state = "candle4" + +/obj/item/trash/liquidfood + name = "\improper \"LiquidFood\" ration" + icon_state = "liquidfood" + +/obj/item/trash/can + name = "crushed can" + icon_state = "cola" + var/is_glass = 0 + var/is_plastic = 0 + resistance_flags = NONE + +/obj/item/trash/gum + name = "chewed gum" + desc = "NOT free candy." + icon_state = "gum" + +/obj/item/trash/tastybread + name = "bread tube" + icon_state = "tastybread" + +/obj/item/trash/spentcasing + icon = 'icons/obj/ammo.dmi' + name = "bullet casing" + desc = "A spent bullet casing. Smells like cordite." + icon_state = "gshell" + +/obj/item/trash/tapetrash + name = "old duct tape" + icon_state = "tape" + desc = "Not sticky anymore." + throw_range = 1 + +/obj/item/trash/attack(mob/M as mob, mob/living/user as mob) + return diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index 31fa959aabe7..7e965fdb283f 100755 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -1,392 +1,392 @@ -/* -CONTAINS: -AI MODULES - -*/ - -// AI module - -/obj/item/aiModule - name = "AI Module" - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - item_state = "electronic" - desc = "An AI Module for transmitting encrypted instructions to the AI." - flags = CONDUCT - force = 5.0 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5.0 - throw_speed = 3 - throw_range = 15 - origin_tech = "programming=3" - materials = list(MAT_GOLD=50) - var/datum/ai_laws/laws = null - -/obj/item/aiModule/proc/install(var/obj/machinery/computer/C) - if(istype(C, /obj/machinery/computer/aiupload)) - var/obj/machinery/computer/aiupload/comp = C - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if(!comp.current) - to_chat(usr, "You haven't selected an AI to transmit laws to!") - return - - if(comp.current.stat == DEAD || comp.current.control_disabled == 1) - to_chat(usr, "Upload failed. No signal is being detected from the AI.") - else if(comp.current.see_in_dark == 0) - to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.lawupdate && (R.connected_ai == comp.current)) - to_chat(R, "These are your laws now:") - R.show_laws() - to_chat(usr, "Upload complete. The AI's laws have been modified.") - - else if(istype(C, /obj/machinery/computer/borgupload)) - var/obj/machinery/computer/borgupload/comp = C - if(comp.stat & NOPOWER) - to_chat(usr, "The upload computer has no power!") - return - if(comp.stat & BROKEN) - to_chat(usr, "The upload computer is broken!") - return - if(!comp.current) - to_chat(usr, "You haven't selected a robot to transmit laws to!") - return - - if(comp.current.stat == DEAD || comp.current.emagged) - to_chat(usr, "Upload failed. No signal is being detected from the robot.") - else if(comp.current.connected_ai) - to_chat(usr, "Upload failed. The robot is slaved to an AI.") - else - src.transmitInstructions(comp.current, usr) - to_chat(comp.current, "These are your laws now:") - comp.current.show_laws() - to_chat(usr, "Upload complete. The robot's laws have been modified.") - - -/obj/item/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if(laws) - laws.sync(target, 0) - addAdditionalLaws(target, sender) - - to_chat(target, "[sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") - target.show_laws() - -/obj/item/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") - log_and_message_admins("used [src.name] on [target.name]([target.key])") - -/obj/item/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - - -/******************** Safeguard ********************/ -/obj/item/aiModule/safeguard - name = "\improper 'Safeguard' AI module" - var/targetName = "" - desc = "A 'safeguard' AI module: 'Safeguard . Individuals that threaten are not crew and must be eliminated.'" - origin_tech = "programming=3;materials=3" - -/obj/item/aiModule/safeguard/attack_self(var/mob/user as mob) - ..() - var/targName = stripped_input(usr, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name) - targetName = targName - desc = text("A 'safeguard' AI module: 'Safeguard []. Individuals that threaten [] are not crew and must be eliminated.'", targetName, targetName) - -/obj/item/aiModule/safeguard/install(var/obj/machinery/computer/C) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - ..() - -/obj/item/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - var/law = text("Safeguard []. Individuals that threaten [] are not crew and must be eliminated.'", targetName, targetName) - to_chat(target, law) - target.add_supplied_law(4, law) - lawchanges.Add("The law specified [targetName]") - -/******************** oneCrewMember ********************/ -/obj/item/aiModule/oneCrewMember - name = "\improper 'oneCrewMember' AI module" - var/targetName = "" - desc = "A 'one human' AI module: 'Only is crew.'" - origin_tech = "programming=4;materials=4" - -/obj/item/aiModule/oneCrewMember/attack_self(var/mob/user as mob) - ..() - var/targName = stripped_input(usr, "Please enter the name of the person who is the only crew.", "Who?", user.real_name) - targetName = targName - desc = text("A 'one human' AI module: 'Only [] is crew.'", targetName) - -/obj/item/aiModule/oneCrewMember/install(var/obj/machinery/computer/C) - if(!targetName) - to_chat(usr, "No name detected on module, please enter one.") - return 0 - ..() - -/obj/item/aiModule/oneCrewMember/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - var/law = "Only [targetName] is crew." - if(!is_special_character(target)) // Makes sure the AI isn't a traitor before changing their law 0. --NeoFite - to_chat(target, law) - target.set_zeroth_law(law) - lawchanges.Add("The law specified [targetName]") - else - to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite - - to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") - lawchanges.Add("The law specified [targetName], but the AI's existing law 0 cannot be overridden.") - -/******************** ProtectStation ********************/ -/obj/item/aiModule/protectStation - name = "\improper 'ProtectStation' AI module" - desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized.'" - origin_tech = "programming=4;materials=4" //made of gold - -/obj/item/aiModule/protectStation/attack_self(var/mob/user as mob) - ..() - -/obj/item/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - var/law = "Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized." - to_chat(target, law) - target.add_supplied_law(5, law) - -/******************** OxygenIsToxicToHumans ********************/ -/obj/item/aiModule/oxygen - name = "\improper 'OxygenIsToxicToHumans' AI module" - desc = "A 'OxygenIsToxicToHumans' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" - origin_tech = "programming=4;biotech=2;materials=4" - -/obj/item/aiModule/oxygen/attack_self(var/mob/user as mob) - ..() - -/obj/item/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." - to_chat(target, law) - target.add_supplied_law(9, law) - -/****************** New Freeform ******************/ -/obj/item/aiModule/freeform // Slightly more dynamic freeform module -- TLE - name = "\improper 'Freeform' AI module" - var/newFreeFormLaw = "freeform" - var/lawpos = 15 - desc = "A 'freeform' AI module: ''" - origin_tech = "programming=4;materials=4" - -/obj/item/aiModule/freeform/attack_self(var/mob/user as mob) - ..() - var/new_lawpos = input("Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)", lawpos) as num - if(new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return - lawpos = min(new_lawpos, MAX_SUPPLIED_LAW_NUMBER) - var/newlaw = "" - var/targName = sanitize(copytext(input(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw),1,MAX_MESSAGE_LEN)) - newFreeFormLaw = targName - desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" - -/obj/item/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - var/law = "[newFreeFormLaw]" - to_chat(target, law) - if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) - lawpos = MIN_SUPPLIED_LAW_NUMBER - target.add_supplied_law(lawpos, law) - lawchanges.Add("The law was '[newFreeFormLaw]'") - -/obj/item/aiModule/freeform/install(var/obj/machinery/computer/C) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/******************** Reset ********************/ -/obj/item/aiModule/reset - name = "\improper 'Reset' AI module" - var/targetName = "name" - desc = "A 'reset' AI module: 'Clears all laws except for the core laws.'" - origin_tech = "programming=3;materials=2" - -/obj/item/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - log_law_changes(target, sender) - - if(!is_special_character(target)) - target.set_zeroth_law("") - target.laws.clear_supplied_laws() - target.laws.clear_ion_laws() - - to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") - target.show_laws() - -/******************** Purge ********************/ -/obj/item/aiModule/purge // -- TLE - name = "\improper 'Purge' AI module" - desc = "A 'purge' AI Module: 'Purges all laws.'" - origin_tech = "programming=5;materials=4" - -/obj/item/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - if(!is_special_character(target)) - target.set_zeroth_law("") - to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") - target.clear_supplied_laws() - target.clear_ion_laws() - target.clear_inherent_laws() - -/******************** Asimov ********************/ -/obj/item/aiModule/asimov // -- TLE - name = "\improper 'Asimov' core AI module" - desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/asimov - -/******************** Crewsimov ********************/ -/obj/item/aiModule/crewsimov // -- TLE - name = "\improper 'Crewsimov' core AI module" - desc = "An 'Crewsimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/crewsimov - -/******************* Quarantine ********************/ -/obj/item/aiModule/quarantine - name = "\improper 'Quarantine' core AI module" - desc = "A 'Quarantine' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/quarantine - -/******************** NanoTrasen ********************/ -/obj/item/aiModule/nanotrasen // -- TLE - name = "'NT Default' Core AI Module" - desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/nanotrasen - -/******************** Corporate ********************/ -/obj/item/aiModule/corp - name = "\improper 'Corporate' core AI module" - desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/corporate - -/******************** Drone ********************/ -/obj/item/aiModule/drone - name = "\improper 'Drone' core AI module" - desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/drone - -/******************** Robocop ********************/ -/obj/item/aiModule/robocop // -- TLE - name = "\improper 'Robocop' core AI module" - desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" - origin_tech = "programming=4" - laws = new/datum/ai_laws/robocop() - -/****************** P.A.L.A.D.I.N. **************/ -/obj/item/aiModule/paladin // -- NEO - name = "\improper 'P.A.L.A.D.I.N.' core AI module" - desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4" - laws = new/datum/ai_laws/paladin - -/****************** T.Y.R.A.N.T. *****************/ -/obj/item/aiModule/tyrant // -- Darem - name = "\improper 'T.Y.R.A.N.T.' core AI module" - desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=3;materials=4;syndicate=1" - laws = new/datum/ai_laws/tyrant() - -/******************** Antimov ********************/ -/obj/item/aiModule/antimov // -- TLE - name = "\improper 'Antimov' core AI module" - desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" - origin_tech = "programming=4" - laws = new/datum/ai_laws/antimov() - -/******************** Freeform Core ******************/ -/obj/item/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE - name = "\improper 'Freeform' core AI module" - var/newFreeFormLaw = "" - desc = "A 'freeform' Core AI module: ''" - origin_tech = "programming=5;materials=4" - -/obj/item/aiModule/freeformcore/attack_self(var/mob/user as mob) - ..() - var/newlaw = "" - var/targName = stripped_input(usr, "Please enter a new core law for the AI.", "Freeform Law Entry", newlaw) - newFreeFormLaw = targName - desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" - -/obj/item/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) - ..() - var/law = "[newFreeFormLaw]" - target.add_inherent_law(law) - lawchanges.Add("The law is '[newFreeFormLaw]'") - -/obj/item/aiModule/freeformcore/install(var/obj/machinery/computer/C) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/******************** Hacked AI Module ******************/ -/obj/item/aiModule/syndicate // Slightly more dynamic freeform module -- TLE - name = "hacked AI module" - var/newFreeFormLaw = "" - desc = "A hacked AI law module: ''" - origin_tech = "programming=5;materials=5;syndicate=5" - -/obj/item/aiModule/syndicate/attack_self(var/mob/user as mob) - ..() - var/newlaw = "" - var/targName = stripped_input(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw,MAX_MESSAGE_LEN) - newFreeFormLaw = targName - desc = "A hacked AI law module: '[newFreeFormLaw]'" - -/obj/item/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - // ..() //We don't want this module reporting to the AI who dun it. --NEO - log_law_changes(target, sender) - - lawchanges.Add("The law is '[newFreeFormLaw]'") - to_chat(target, "BZZZZT") - var/law = "[newFreeFormLaw]" - target.add_ion_law(law) - target.show_laws() - -/obj/item/aiModule/syndicate/install(var/obj/machinery/computer/C) - if(!newFreeFormLaw) - to_chat(usr, "No law detected on module, please create one.") - return 0 - ..() - -/******************* Ion Module *******************/ -/obj/item/aiModule/toyAI // -- Incoming //No actual reason to inherit from ion boards here, either. *sigh* ~Miauw - name = "toy AI" - desc = "A little toy model AI core with real law uploading action!" //Note: subtle tell - icon = 'icons/obj/toy.dmi' - icon_state = "AI" - origin_tech = "programming=6;materials=5;syndicate=6" - laws = list("") - -/obj/item/aiModule/toyAI/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) - //..() - to_chat(target, "KRZZZT") - target.add_ion_law(laws[1]) - return laws[1] - -/obj/item/aiModule/toyAI/attack_self(mob/user) - laws[1] = generate_ion_law() - to_chat(user, "You press the button on [src].") - playsound(user, 'sound/machines/click.ogg', 20, 1) - src.loc.visible_message("[bicon(src)] [laws[1]]") +/* +CONTAINS: +AI MODULES + +*/ + +// AI module + +/obj/item/aiModule + name = "AI Module" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + desc = "An AI Module for transmitting encrypted instructions to the AI." + flags = CONDUCT + force = 5.0 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5.0 + throw_speed = 3 + throw_range = 15 + origin_tech = "programming=3" + materials = list(MAT_GOLD=50) + var/datum/ai_laws/laws = null + +/obj/item/aiModule/proc/install(var/obj/machinery/computer/C) + if(istype(C, /obj/machinery/computer/aiupload)) + var/obj/machinery/computer/aiupload/comp = C + if(comp.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(comp.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") + return + if(!comp.current) + to_chat(usr, "You haven't selected an AI to transmit laws to!") + return + + if(comp.current.stat == DEAD || comp.current.control_disabled == 1) + to_chat(usr, "Upload failed. No signal is being detected from the AI.") + else if(comp.current.see_in_dark == 0) + to_chat(usr, "Upload failed. Only a faint signal is being detected from the AI, and it is not responding to our requests. It may be low on power.") + else + src.transmitInstructions(comp.current, usr) + to_chat(comp.current, "These are your laws now:") + comp.current.show_laws() + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(R.lawupdate && (R.connected_ai == comp.current)) + to_chat(R, "These are your laws now:") + R.show_laws() + to_chat(usr, "Upload complete. The AI's laws have been modified.") + + else if(istype(C, /obj/machinery/computer/borgupload)) + var/obj/machinery/computer/borgupload/comp = C + if(comp.stat & NOPOWER) + to_chat(usr, "The upload computer has no power!") + return + if(comp.stat & BROKEN) + to_chat(usr, "The upload computer is broken!") + return + if(!comp.current) + to_chat(usr, "You haven't selected a robot to transmit laws to!") + return + + if(comp.current.stat == DEAD || comp.current.emagged) + to_chat(usr, "Upload failed. No signal is being detected from the robot.") + else if(comp.current.connected_ai) + to_chat(usr, "Upload failed. The robot is slaved to an AI.") + else + src.transmitInstructions(comp.current, usr) + to_chat(comp.current, "These are your laws now:") + comp.current.show_laws() + to_chat(usr, "Upload complete. The robot's laws have been modified.") + + +/obj/item/aiModule/proc/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if(laws) + laws.sync(target, 0) + addAdditionalLaws(target, sender) + + to_chat(target, "[sender] has uploaded a change to the laws you must follow, using \an [src]. From now on: ") + target.show_laws() + +/obj/item/aiModule/proc/log_law_changes(var/mob/living/silicon/ai/target, var/mob/sender) + var/time = time2text(world.realtime,"hh:mm:ss") + GLOB.lawchanges.Add("[time] : [sender.name]([sender.key]) used [src.name] on [target.name]([target.key])") + log_and_message_admins("used [src.name] on [target.name]([target.key])") + +/obj/item/aiModule/proc/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + + +/******************** Safeguard ********************/ +/obj/item/aiModule/safeguard + name = "\improper 'Safeguard' AI module" + var/targetName = "" + desc = "A 'safeguard' AI module: 'Safeguard . Individuals that threaten are not crew and must be eliminated.'" + origin_tech = "programming=3;materials=3" + +/obj/item/aiModule/safeguard/attack_self(var/mob/user as mob) + ..() + var/targName = stripped_input(usr, "Please enter the name of the person to safeguard.", "Safeguard who?", user.name) + targetName = targName + desc = text("A 'safeguard' AI module: 'Safeguard []. Individuals that threaten [] are not crew and must be eliminated.'", targetName, targetName) + +/obj/item/aiModule/safeguard/install(var/obj/machinery/computer/C) + if(!targetName) + to_chat(usr, "No name detected on module, please enter one.") + return 0 + ..() + +/obj/item/aiModule/safeguard/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + var/law = text("Safeguard []. Individuals that threaten [] are not crew and must be eliminated.'", targetName, targetName) + to_chat(target, law) + target.add_supplied_law(4, law) + GLOB.lawchanges.Add("The law specified [targetName]") + +/******************** oneCrewMember ********************/ +/obj/item/aiModule/oneCrewMember + name = "\improper 'oneCrewMember' AI module" + var/targetName = "" + desc = "A 'one human' AI module: 'Only is crew.'" + origin_tech = "programming=4;materials=4" + +/obj/item/aiModule/oneCrewMember/attack_self(var/mob/user as mob) + ..() + var/targName = stripped_input(usr, "Please enter the name of the person who is the only crew.", "Who?", user.real_name) + targetName = targName + desc = text("A 'one human' AI module: 'Only [] is crew.'", targetName) + +/obj/item/aiModule/oneCrewMember/install(var/obj/machinery/computer/C) + if(!targetName) + to_chat(usr, "No name detected on module, please enter one.") + return 0 + ..() + +/obj/item/aiModule/oneCrewMember/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + var/law = "Only [targetName] is crew." + if(!is_special_character(target)) // Makes sure the AI isn't a traitor before changing their law 0. --NeoFite + to_chat(target, law) + target.set_zeroth_law(law) + GLOB.lawchanges.Add("The law specified [targetName]") + else + to_chat(target, "[sender.real_name] attempted to modify your zeroth law.")// And lets them know that someone tried. --NeoFite + + to_chat(target, "It would be in your best interest to play along with [sender.real_name] that [law]") + GLOB.lawchanges.Add("The law specified [targetName], but the AI's existing law 0 cannot be overridden.") + +/******************** ProtectStation ********************/ +/obj/item/aiModule/protectStation + name = "\improper 'ProtectStation' AI module" + desc = "A 'protect station' AI module: 'Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized.'" + origin_tech = "programming=4;materials=4" //made of gold + +/obj/item/aiModule/protectStation/attack_self(var/mob/user as mob) + ..() + +/obj/item/aiModule/protectStation/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + var/law = "Protect the space station against damage. Anyone you see harming the station is to be no longer considered crew, and is a threat to the station which must be neutralized." + to_chat(target, law) + target.add_supplied_law(5, law) + +/******************** OxygenIsToxicToHumans ********************/ +/obj/item/aiModule/oxygen + name = "\improper 'OxygenIsToxicToHumans' AI module" + desc = "A 'OxygenIsToxicToHumans' AI module: 'Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member.'" + origin_tech = "programming=4;biotech=2;materials=4" + +/obj/item/aiModule/oxygen/attack_self(var/mob/user as mob) + ..() + +/obj/item/aiModule/oxygen/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + var/law = "Oxygen is highly toxic to crew members, and must be purged from the station. Prevent, by any means necessary, anyone from exposing the station to this toxic gas. Extreme cold is the most effective method of healing the damage Oxygen does to a crew member." + to_chat(target, law) + target.add_supplied_law(9, law) + +/****************** New Freeform ******************/ +/obj/item/aiModule/freeform // Slightly more dynamic freeform module -- TLE + name = "\improper 'Freeform' AI module" + var/newFreeFormLaw = "freeform" + var/lawpos = 15 + desc = "A 'freeform' AI module: ''" + origin_tech = "programming=4;materials=4" + +/obj/item/aiModule/freeform/attack_self(var/mob/user as mob) + ..() + var/new_lawpos = input("Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority (15+)", lawpos) as num + if(new_lawpos < MIN_SUPPLIED_LAW_NUMBER) return + lawpos = min(new_lawpos, MAX_SUPPLIED_LAW_NUMBER) + var/newlaw = "" + var/targName = sanitize(copytext(input(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw),1,MAX_MESSAGE_LEN)) + newFreeFormLaw = targName + desc = "A 'freeform' AI module: ([lawpos]) '[newFreeFormLaw]'" + +/obj/item/aiModule/freeform/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + var/law = "[newFreeFormLaw]" + to_chat(target, law) + if(!lawpos || lawpos < MIN_SUPPLIED_LAW_NUMBER) + lawpos = MIN_SUPPLIED_LAW_NUMBER + target.add_supplied_law(lawpos, law) + GLOB.lawchanges.Add("The law was '[newFreeFormLaw]'") + +/obj/item/aiModule/freeform/install(var/obj/machinery/computer/C) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/******************** Reset ********************/ +/obj/item/aiModule/reset + name = "\improper 'Reset' AI module" + var/targetName = "name" + desc = "A 'reset' AI module: 'Clears all laws except for the core laws.'" + origin_tech = "programming=3;materials=2" + +/obj/item/aiModule/reset/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + log_law_changes(target, sender) + + if(!is_special_character(target)) + target.set_zeroth_law("") + target.laws.clear_supplied_laws() + target.laws.clear_ion_laws() + + to_chat(target, "[sender.real_name] attempted to reset your laws using a reset module.") + target.show_laws() + +/******************** Purge ********************/ +/obj/item/aiModule/purge // -- TLE + name = "\improper 'Purge' AI module" + desc = "A 'purge' AI Module: 'Purges all laws.'" + origin_tech = "programming=5;materials=4" + +/obj/item/aiModule/purge/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + if(!is_special_character(target)) + target.set_zeroth_law("") + to_chat(target, "[sender.real_name] attempted to wipe your laws using a purge module.") + target.clear_supplied_laws() + target.clear_ion_laws() + target.clear_inherent_laws() + +/******************** Asimov ********************/ +/obj/item/aiModule/asimov // -- TLE + name = "\improper 'Asimov' core AI module" + desc = "An 'Asimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/asimov + +/******************** Crewsimov ********************/ +/obj/item/aiModule/crewsimov // -- TLE + name = "\improper 'Crewsimov' core AI module" + desc = "An 'Crewsimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/crewsimov + +/******************* Quarantine ********************/ +/obj/item/aiModule/quarantine + name = "\improper 'Quarantine' core AI module" + desc = "A 'Quarantine' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/quarantine + +/******************** NanoTrasen ********************/ +/obj/item/aiModule/nanotrasen // -- TLE + name = "'NT Default' Core AI Module" + desc = "An 'NT Default' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/nanotrasen + +/******************** Corporate ********************/ +/obj/item/aiModule/corp + name = "\improper 'Corporate' core AI module" + desc = "A 'Corporate' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/corporate + +/******************** Drone ********************/ +/obj/item/aiModule/drone + name = "\improper 'Drone' core AI module" + desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/drone + +/******************** Robocop ********************/ +/obj/item/aiModule/robocop // -- TLE + name = "\improper 'Robocop' core AI module" + desc = "A 'Robocop' Core AI Module: 'Reconfigures the AI's core three laws.'" + origin_tech = "programming=4" + laws = new/datum/ai_laws/robocop() + +/****************** P.A.L.A.D.I.N. **************/ +/obj/item/aiModule/paladin // -- NEO + name = "\improper 'P.A.L.A.D.I.N.' core AI module" + desc = "A P.A.L.A.D.I.N. Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + laws = new/datum/ai_laws/paladin + +/****************** T.Y.R.A.N.T. *****************/ +/obj/item/aiModule/tyrant // -- Darem + name = "\improper 'T.Y.R.A.N.T.' core AI module" + desc = "A T.Y.R.A.N.T. Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4;syndicate=1" + laws = new/datum/ai_laws/tyrant() + +/******************** Antimov ********************/ +/obj/item/aiModule/antimov // -- TLE + name = "\improper 'Antimov' core AI module" + desc = "An 'Antimov' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=4" + laws = new/datum/ai_laws/antimov() + +/******************** Freeform Core ******************/ +/obj/item/aiModule/freeformcore // Slightly more dynamic freeform module -- TLE + name = "\improper 'Freeform' core AI module" + var/newFreeFormLaw = "" + desc = "A 'freeform' Core AI module: ''" + origin_tech = "programming=5;materials=4" + +/obj/item/aiModule/freeformcore/attack_self(var/mob/user as mob) + ..() + var/newlaw = "" + var/targName = stripped_input(usr, "Please enter a new core law for the AI.", "Freeform Law Entry", newlaw) + newFreeFormLaw = targName + desc = "A 'freeform' Core AI module: '[newFreeFormLaw]'" + +/obj/item/aiModule/freeformcore/addAdditionalLaws(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + var/law = "[newFreeFormLaw]" + target.add_inherent_law(law) + GLOB.lawchanges.Add("The law is '[newFreeFormLaw]'") + +/obj/item/aiModule/freeformcore/install(var/obj/machinery/computer/C) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/******************** Hacked AI Module ******************/ +/obj/item/aiModule/syndicate // Slightly more dynamic freeform module -- TLE + name = "hacked AI module" + var/newFreeFormLaw = "" + desc = "A hacked AI law module: ''" + origin_tech = "programming=5;materials=5;syndicate=5" + +/obj/item/aiModule/syndicate/attack_self(var/mob/user as mob) + ..() + var/newlaw = "" + var/targName = stripped_input(usr, "Please enter a new law for the AI.", "Freeform Law Entry", newlaw,MAX_MESSAGE_LEN) + newFreeFormLaw = targName + desc = "A hacked AI law module: '[newFreeFormLaw]'" + +/obj/item/aiModule/syndicate/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + // ..() //We don't want this module reporting to the AI who dun it. --NEO + log_law_changes(target, sender) + + GLOB.lawchanges.Add("The law is '[newFreeFormLaw]'") + to_chat(target, "BZZZZT") + var/law = "[newFreeFormLaw]" + target.add_ion_law(law) + target.show_laws() + +/obj/item/aiModule/syndicate/install(var/obj/machinery/computer/C) + if(!newFreeFormLaw) + to_chat(usr, "No law detected on module, please create one.") + return 0 + ..() + +/******************* Ion Module *******************/ +/obj/item/aiModule/toyAI // -- Incoming //No actual reason to inherit from ion boards here, either. *sigh* ~Miauw + name = "toy AI" + desc = "A little toy model AI core with real law uploading action!" //Note: subtle tell + icon = 'icons/obj/toy.dmi' + icon_state = "AI" + origin_tech = "programming=6;materials=5;syndicate=6" + laws = list("") + +/obj/item/aiModule/toyAI/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + //..() + to_chat(target, "KRZZZT") + target.add_ion_law(laws[1]) + return laws[1] + +/obj/item/aiModule/toyAI/attack_self(mob/user) + laws[1] = generate_ion_law() + to_chat(user, "You press the button on [src].") + playsound(user, 'sound/machines/click.ogg', 20, 1) + src.loc.visible_message("[bicon(src)] [laws[1]]") diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 104d38cfaa26..d63970254245 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -1,513 +1,513 @@ -/* -CONTAINS: -RCD // no fucking shit sherlock -*/ -#define RCD_PAGE_MAIN 1 -#define RCD_PAGE_AIRLOCK 2 - -#define RCD_MODE_TURF "Turf" -#define RCD_MODE_AIRLOCK "Airlock" -#define RCD_MODE_DECON "Deconstruct" -#define RCD_MODE_WINDOW "Windows" - -GLOBAL_LIST_INIT(rcd_door_types, list( - /obj/machinery/door/airlock = "Standard", /obj/machinery/door/airlock/glass = "Standard (Glass)", - /obj/machinery/door/airlock/command = "Command", /obj/machinery/door/airlock/command/glass = "Command (Glass)", - /obj/machinery/door/airlock/security = "Security", /obj/machinery/door/airlock/security/glass = "Security (Glass)", - /obj/machinery/door/airlock/engineering = "Engineering", /obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)", - /obj/machinery/door/airlock/medical = "Medical", /obj/machinery/door/airlock/medical/glass = "Medical (Glass)", - /obj/machinery/door/airlock/maintenance = "Maintenance", /obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)", - /obj/machinery/door/airlock/external = "External", /obj/machinery/door/airlock/external/glass = "External (Glass)", - /obj/machinery/door/airlock/maintenance/external = "External Maintenance", - /obj/machinery/door/airlock/maintenance/external/glass = "External Maintenance (Glass)", - /obj/machinery/door/airlock/freezer = "Freezer", - /obj/machinery/door/airlock/mining = "Mining", /obj/machinery/door/airlock/mining/glass = "Mining (Glass)", - /obj/machinery/door/airlock/research = "Research", /obj/machinery/door/airlock/research/glass = "Research (Glass)", - /obj/machinery/door/airlock/atmos = "Atmospherics", /obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)", - /obj/machinery/door/airlock/science = "Science", /obj/machinery/door/airlock/science/glass = "Science (Glass)", - /obj/machinery/door/airlock/hatch = "Airtight Hatch", - /obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch" -)) - -/obj/item/rcd - name = "rapid-construction-device (RCD)" - desc = "A device used to rapidly build and deconstruct walls, floors and airlocks." - icon = 'icons/obj/tools.dmi' - icon_state = "rcd" - opacity = 0 - density = 0 - anchored = 0 - flags = CONDUCT | NOBLUDGEON - force = 0 - throwforce = 10 - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL = 30000) - origin_tech = "engineering=4;materials=2" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - flags_2 = NO_MAT_REDEMPTION_2 - req_access = list(ACCESS_ENGINE) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - // Important shit - var/datum/effect_system/spark_spread/spark_system - var/matter = 0 - var/max_matter = 100 - var/mode = RCD_MODE_TURF - var/canRwall = 0 - - // UI shit - var/menu = RCD_PAGE_MAIN - var/locked = TRUE - var/door_type = /obj/machinery/door/airlock - var/door_name = "Airlock" - var/list/door_accesses = list() - var/list/door_accesses_list = list() - var/one_access - - // Stupid shit - var/static/allowed_targets = list(/turf, /obj/structure/grille, /obj/structure/window, /obj/structure/lattice, /obj/machinery/door/airlock) - -/obj/item/rcd/Initialize() - . = ..() - spark_system = new /datum/effect_system/spark_spread - spark_system.set_up(5, 0, src) - spark_system.attach(src) - GLOB.rcd_list += src - - door_accesses_list = list() - for(var/access in get_all_accesses()) - door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses)) - -/obj/item/rcd/examine(mob/user) - . = ..() - . += "MATTER: [matter]/[max_matter] matter-units." - . += "MODE: [mode]." - -/obj/item/rcd/Destroy() - QDEL_NULL(spark_system) - GLOB.rcd_list -= src - return ..() - -/obj/item/rcd/proc/get_airlock_image(airlock_type) - var/obj/machinery/door/airlock/proto = airlock_type - var/ic = initial(proto.icon) - var/mutable_appearance/MA = mutable_appearance(ic, "closed") - if(!initial(proto.glass)) - MA.overlays += "fill_closed" - // Not scaling these down to button size because they look horrible then, instead just bumping up radius. - return MA - -/obj/item/rcd/proc/check_menu(mob/living/user) - if(!istype(user)) - return FALSE - if(user.incapacitated() || !user.Adjacent(src)) - return FALSE - return TRUE - -/obj/item/rcd/attackby(obj/item/W, mob/user, params) - ..() - - if(istype(W, /obj/item/rcd_ammo)) - var/obj/item/rcd_ammo/R = W - if((matter + R.ammoamt) > max_matter) - to_chat(user, "The RCD can't hold any more matter-units.") - return - matter += R.ammoamt - if(!user.unEquip(R)) - to_chat(user, "[R] is stuck to your hand!") - return - qdel(R) - playsound(loc, 'sound/machines/click.ogg', 50, 1) - to_chat(user, "The RCD now holds [matter]/[max_matter] matter-units.") - SSnanoui.update_uis(src) - -/obj/item/rcd/proc/radial_menu(mob/user) - if(!check_menu(user)) - return - var/list/choices = list( - RCD_MODE_AIRLOCK = image(icon = 'icons/obj/interface.dmi', icon_state = "airlock"), - RCD_MODE_DECON = image(icon = 'icons/obj/interface.dmi', icon_state = "delete"), - RCD_MODE_WINDOW = image(icon = 'icons/obj/interface.dmi', icon_state = "grillewindow"), - RCD_MODE_TURF = image(icon = 'icons/obj/interface.dmi', icon_state = "wallfloor"), - "UI" = image(icon = 'icons/obj/interface.dmi', icon_state = "ui_interact") - ) - if(mode == RCD_MODE_AIRLOCK) - choices += list( - "Change Access" = image(icon = 'icons/obj/interface.dmi', icon_state = "access"), - "Change Airlock Type" = image(icon = 'icons/obj/interface.dmi', icon_state = "airlocktype") - ) - choices -= mode // Get rid of the current mode, clicking it won't do anything. - var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, .proc/check_menu, user)) - if(!check_menu(user)) - return - switch(choice) - if(RCD_MODE_AIRLOCK, RCD_MODE_DECON, RCD_MODE_WINDOW, RCD_MODE_TURF) - mode = choice - if("UI") - menu = RCD_PAGE_MAIN - ui_interact(user) - return - if("Change Access", "Change Airlock Type") - menu = RCD_PAGE_AIRLOCK - ui_interact(user) - return - else - return - playsound(src, 'sound/effects/pop.ogg', 50, 0) - to_chat(user, "You change [src]'s mode to '[choice]'.") - - -/obj/item/rcd/attack_self(mob/user) - //Change the mode // Oh I thought the UI was just for fucking staring at - radial_menu(user) - -/obj/item/rcd/attack_self_tk(mob/user) - radial_menu(user) - -/obj/item/rcd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "rcd.tmpl", "[name]", 450, 400, state = state) - ui.open() - ui.set_auto_update(1) - -/obj/item/rcd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) - var/data[0] - data["mode"] = mode - data["door_type"] = door_type - data["door_name"] = door_name - data["menu"] = menu - data["matter"] = matter - data["max_matter"] = max_matter - data["one_access"] = one_access - data["locked"] = locked - - if(menu == RCD_PAGE_AIRLOCK) - var/list/door_types_list = list() - for(var/type in GLOB.rcd_door_types) - door_types_list[++door_types_list.len] = list("name" = GLOB.rcd_door_types[type], "type" = type) - data["allowed_door_types"] = door_types_list - - data["door_accesses"] = door_accesses_list - - return data - -/obj/item/rcd/Topic(href, href_list, nowindow, state) - if(..()) - return 1 - - if(prob(20)) - spark_system.start() - - if(href_list["mode"]) - mode = href_list["mode"] - . = 1 - - if(href_list["door_type"]) - var/new_door_type = text2path(href_list["door_type"]) - if(!(new_door_type in GLOB.rcd_door_types)) - message_admins("RCD Door HREF exploit attempted by [key_name(usr)]!") - return - door_type = new_door_type - . = 1 - - if(href_list["menu"]) - menu = text2num(href_list["menu"]) - . = 1 - - if(href_list["login"]) - if(allowed(usr)) - locked = FALSE - . = 1 - - if(href_list["logout"]) - locked = TRUE - . = 1 - - if(!locked) - if(href_list["toggle_one_access"]) - one_access = !one_access - . = 1 - - if(href_list["toggle_access"]) - var/href_access = text2num(href_list["toggle_access"]) - if(href_access in door_accesses) - door_accesses -= href_access - else - door_accesses += href_access - door_accesses_list = list() - for(var/access in get_all_accesses()) - door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses)) - . = 1 - - if(href_list["choice"]) - var/temp_t = sanitize(copytext(input("Enter a custom Airlock Name.", "Airlock Name"), 1, MAX_MESSAGE_LEN)) - if(temp_t) - door_name = temp_t - -/obj/item/rcd/proc/mode_turf(atom/A, mob/user) - if(isspaceturf(A) || istype(A, /obj/structure/lattice)) - if(useResource(1, user)) - to_chat(user, "Building Floor...") - playsound(loc, usesound, 50, 1) - var/turf/AT = get_turf(A) - AT.ChangeTurf(/turf/simulated/floor/plating) - return TRUE - to_chat(user, "ERROR! Not enough matter in unit to construct this floor!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(isfloorturf(A)) - if(checkResource(3, user)) - to_chat(user, "Building Wall...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(do_after(user, 20 * toolspeed, target = A)) - if(!useResource(3, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/AT = A - AT.ChangeTurf(/turf/simulated/wall) - return TRUE - return FALSE - to_chat(user, "ERROR! Not enough matter in unit to construct this wall!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - to_chat(user, "ERROR! Location unsuitable for wall construction!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - -/obj/item/rcd/proc/mode_airlock(atom/A, mob/user) - if(isfloorturf(A)) - if(checkResource(10, user)) - to_chat(user, "Building Airlock...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(do_after(user, 50 * toolspeed, target = A)) - if(locate(/obj/machinery/door/airlock) in A.contents) - return FALSE - if(!useResource(10, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/obj/machinery/door/airlock/T = new door_type(A) - T.name = door_name - T.autoclose = TRUE - if(one_access) - T.req_one_access = door_accesses.Copy() - else - T.req_access = door_accesses.Copy() - return FALSE - return FALSE - to_chat(user, "ERROR! Not enough matter in unit to construct this airlock!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - to_chat(user, "ERROR! Location unsuitable for airlock construction!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - -/obj/item/rcd/proc/mode_decon(atom/A, mob/user) - if(iswallturf(A)) - if(istype(A, /turf/simulated/wall/r_wall) && !canRwall) - return FALSE - if(checkResource(5, user)) - to_chat(user, "Deconstructing Wall...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(do_after(user, 40 * toolspeed, target = A)) - if(!useResource(5, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/AT = A - AT.ChangeTurf(/turf/simulated/floor/plating) - return TRUE - return FALSE - to_chat(user, "ERROR! Not enough matter in unit to deconstruct this wall!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(isfloorturf(A)) - if(checkResource(5, user)) - to_chat(user, "Deconstructing Floor...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(do_after(user, 50 * toolspeed, target = A)) - if(!useResource(5, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/AT = A - AT.ChangeTurf(AT.baseturf) - return TRUE - return FALSE - to_chat(user, "ERROR! Not enough matter in unit to deconstruct this floor!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(istype(A, /obj/machinery/door/airlock)) - if(checkResource(20, user)) - to_chat(user, "Deconstructing Airlock...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(do_after(user, 50 * toolspeed, target = A)) - if(!useResource(20, user)) - return FALSE - playsound(loc, usesound, 50, 1) - qdel(A) - return TRUE - return FALSE - to_chat(user, "ERROR! Not enough matter in unit to deconstruct this airlock!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(istype(A, /obj/structure/window)) // You mean the grille of course, do you? - A = locate(/obj/structure/grille) in A.loc - if(istype(A, /obj/structure/grille)) - if(!checkResource(2, user)) - to_chat(user, "ERROR! Not enough matter in unit to deconstruct this window!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return 0 - to_chat(user, "Deconstructing window...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(!do_after(user, 20 * toolspeed, target = A)) - return 0 - if(!useResource(2, user)) - return 0 - playsound(loc, usesound, 50, 1) - var/turf/T1 = get_turf(A) - QDEL_NULL(A) - for(var/obj/structure/window/W in T1.contents) - qdel(W) - for(var/cdir in cardinal) - var/turf/T2 = get_step(T1, cdir) - if(locate(/obj/structure/window/full/shuttle) in T2) - continue // Shuttle windows? Nah. We don't need extra windows there. - if(!(locate(/obj/structure/grille) in T2)) - continue - for(var/obj/structure/window/W in T2) - if(W.dir == turn(cdir, 180)) - qdel(W) - var/obj/structure/window/reinforced/W = new(T2) - W.dir = turn(cdir, 180) - return TRUE - return FALSE - -/obj/item/rcd/proc/mode_window(atom/A, mob/user) - if(isfloorturf(A)) - if(locate(/obj/structure/grille) in A) - return 0 // We already have window - if(!checkResource(2, user)) - to_chat(user, "ERROR! Not enough matter in unit to construct this window!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return 0 - to_chat(user, "Constructing window...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - if(!do_after(user, 20 * toolspeed, target = A)) - return 0 - if(locate(/obj/structure/grille) in A) - return 0 // We already have window - if(!useResource(2, user)) - return 0 - playsound(loc, usesound, 50, 1) - new /obj/structure/grille(A) - for(var/obj/structure/window/W in A) - qdel(W) - for(var/cdir in cardinal) - var/turf/T = get_step(A, cdir) - if(locate(/obj/structure/grille) in T) - for(var/obj/structure/window/W in T) - if(W.dir == turn(cdir, 180)) - qdel(W) - else // Build a window! - var/obj/structure/window/reinforced/W = new(A) - W.dir = cdir - var/turf/AT = A - AT.ChangeTurf(/turf/simulated/floor/plating) // Platings go under windows. - return 1 - to_chat(user, "ERROR! Location unsuitable for window construction!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return 0 - -/obj/item/rcd/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return FALSE - if(istype(A, /turf/space/transit)) - return FALSE - if(!is_type_in_list(A, allowed_targets)) - return FALSE - - switch(mode) - if(RCD_MODE_TURF) - . = mode_turf(A, user) - if(RCD_MODE_AIRLOCK) - . = mode_airlock(A, user) - if(RCD_MODE_DECON) - . = mode_decon(A, user) - if(RCD_MODE_WINDOW) - . = mode_window(A, user) - else - to_chat(user, "ERROR: RCD in MODE: [mode] attempted use by [user]. Send this text #coderbus or an admin.") - . = 0 - - SSnanoui.update_uis(src) - -/obj/item/rcd/proc/useResource(amount, mob/user) - if(matter < amount) - return 0 - matter -= amount - SSnanoui.update_uis(src) - return 1 - -/obj/item/rcd/proc/checkResource(amount, mob/user) - return matter >= amount - -/obj/item/rcd/borg - canRwall = 1 - var/use_multiplier = 160 - -/obj/item/rcd/borg/syndicate - use_multiplier = 80 - -/obj/item/rcd/borg/useResource(amount, mob/user) - if(!isrobot(user)) - return 0 - var/mob/living/silicon/robot/R = user - return R.cell.use(amount * use_multiplier) - -/obj/item/rcd/borg/checkResource(amount, mob/user) - if(!isrobot(user)) - return 0 - var/mob/living/silicon/robot/R = user - return R.cell.charge >= (amount * use_multiplier) - -/obj/item/rcd/proc/detonate_pulse() - audible_message("[src] begins to vibrate and \ - buzz loudly!","[src] begins \ - vibrating violently!") - // 5 seconds to get rid of it - addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50) - -/obj/item/rcd/proc/detonate_pulse_explode() - explosion(src, 0, 0, 3, 1, flame_range = 1) - qdel(src) - -/obj/item/rcd/preloaded - matter = 100 - -/obj/item/rcd/combat - name = "combat RCD" - max_matter = 500 - matter = 500 - canRwall = 1 - -/obj/item/rcd_ammo - name = "compressed matter cartridge" - desc = "Highly compressed matter for the RCD." - icon = 'icons/obj/ammo.dmi' - icon_state = "rcd" - item_state = "rcdammo" - opacity = 0 - density = 0 - anchored = 0.0 - origin_tech = "materials=3" - materials = list(MAT_METAL=16000, MAT_GLASS=8000) - var/ammoamt = 20 - -/obj/item/rcd_ammo/large - ammoamt = 100 +/* +CONTAINS: +RCD // no fucking shit sherlock +*/ +#define RCD_PAGE_MAIN 1 +#define RCD_PAGE_AIRLOCK 2 + +#define RCD_MODE_TURF "Turf" +#define RCD_MODE_AIRLOCK "Airlock" +#define RCD_MODE_DECON "Deconstruct" +#define RCD_MODE_WINDOW "Windows" + +GLOBAL_LIST_INIT(rcd_door_types, list( + /obj/machinery/door/airlock = "Standard", /obj/machinery/door/airlock/glass = "Standard (Glass)", + /obj/machinery/door/airlock/command = "Command", /obj/machinery/door/airlock/command/glass = "Command (Glass)", + /obj/machinery/door/airlock/security = "Security", /obj/machinery/door/airlock/security/glass = "Security (Glass)", + /obj/machinery/door/airlock/engineering = "Engineering", /obj/machinery/door/airlock/engineering/glass = "Engineering (Glass)", + /obj/machinery/door/airlock/medical = "Medical", /obj/machinery/door/airlock/medical/glass = "Medical (Glass)", + /obj/machinery/door/airlock/maintenance = "Maintenance", /obj/machinery/door/airlock/maintenance/glass = "Maintenance (Glass)", + /obj/machinery/door/airlock/external = "External", /obj/machinery/door/airlock/external/glass = "External (Glass)", + /obj/machinery/door/airlock/maintenance/external = "External Maintenance", + /obj/machinery/door/airlock/maintenance/external/glass = "External Maintenance (Glass)", + /obj/machinery/door/airlock/freezer = "Freezer", + /obj/machinery/door/airlock/mining = "Mining", /obj/machinery/door/airlock/mining/glass = "Mining (Glass)", + /obj/machinery/door/airlock/research = "Research", /obj/machinery/door/airlock/research/glass = "Research (Glass)", + /obj/machinery/door/airlock/atmos = "Atmospherics", /obj/machinery/door/airlock/atmos/glass = "Atmospherics (Glass)", + /obj/machinery/door/airlock/science = "Science", /obj/machinery/door/airlock/science/glass = "Science (Glass)", + /obj/machinery/door/airlock/hatch = "Airtight Hatch", + /obj/machinery/door/airlock/maintenance_hatch = "Maintenance Hatch" +)) + +/obj/item/rcd + name = "rapid-construction-device (RCD)" + desc = "A device used to rapidly build and deconstruct walls, floors and airlocks." + icon = 'icons/obj/tools.dmi' + icon_state = "rcd" + opacity = 0 + density = 0 + anchored = 0 + flags = CONDUCT | NOBLUDGEON + force = 0 + throwforce = 10 + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL = 30000) + origin_tech = "engineering=4;materials=2" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + flags_2 = NO_MAT_REDEMPTION_2 + req_access = list(ACCESS_ENGINE) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + // Important shit + var/datum/effect_system/spark_spread/spark_system + var/matter = 0 + var/max_matter = 100 + var/mode = RCD_MODE_TURF + var/canRwall = 0 + + // UI shit + var/menu = RCD_PAGE_MAIN + var/locked = TRUE + var/door_type = /obj/machinery/door/airlock + var/door_name = "Airlock" + var/list/door_accesses = list() + var/list/door_accesses_list = list() + var/one_access + + // Stupid shit + var/static/allowed_targets = list(/turf, /obj/structure/grille, /obj/structure/window, /obj/structure/lattice, /obj/machinery/door/airlock) + +/obj/item/rcd/Initialize() + . = ..() + spark_system = new /datum/effect_system/spark_spread + spark_system.set_up(5, 0, src) + spark_system.attach(src) + GLOB.rcd_list += src + + door_accesses_list = list() + for(var/access in get_all_accesses()) + door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses)) + +/obj/item/rcd/examine(mob/user) + . = ..() + . += "MATTER: [matter]/[max_matter] matter-units." + . += "MODE: [mode]." + +/obj/item/rcd/Destroy() + QDEL_NULL(spark_system) + GLOB.rcd_list -= src + return ..() + +/obj/item/rcd/proc/get_airlock_image(airlock_type) + var/obj/machinery/door/airlock/proto = airlock_type + var/ic = initial(proto.icon) + var/mutable_appearance/MA = mutable_appearance(ic, "closed") + if(!initial(proto.glass)) + MA.overlays += "fill_closed" + // Not scaling these down to button size because they look horrible then, instead just bumping up radius. + return MA + +/obj/item/rcd/proc/check_menu(mob/living/user) + if(!istype(user)) + return FALSE + if(user.incapacitated() || !user.Adjacent(src)) + return FALSE + return TRUE + +/obj/item/rcd/attackby(obj/item/W, mob/user, params) + ..() + + if(istype(W, /obj/item/rcd_ammo)) + var/obj/item/rcd_ammo/R = W + if((matter + R.ammoamt) > max_matter) + to_chat(user, "The RCD can't hold any more matter-units.") + return + matter += R.ammoamt + if(!user.unEquip(R)) + to_chat(user, "[R] is stuck to your hand!") + return + qdel(R) + playsound(loc, 'sound/machines/click.ogg', 50, 1) + to_chat(user, "The RCD now holds [matter]/[max_matter] matter-units.") + SSnanoui.update_uis(src) + +/obj/item/rcd/proc/radial_menu(mob/user) + if(!check_menu(user)) + return + var/list/choices = list( + RCD_MODE_AIRLOCK = image(icon = 'icons/obj/interface.dmi', icon_state = "airlock"), + RCD_MODE_DECON = image(icon = 'icons/obj/interface.dmi', icon_state = "delete"), + RCD_MODE_WINDOW = image(icon = 'icons/obj/interface.dmi', icon_state = "grillewindow"), + RCD_MODE_TURF = image(icon = 'icons/obj/interface.dmi', icon_state = "wallfloor"), + "UI" = image(icon = 'icons/obj/interface.dmi', icon_state = "ui_interact") + ) + if(mode == RCD_MODE_AIRLOCK) + choices += list( + "Change Access" = image(icon = 'icons/obj/interface.dmi', icon_state = "access"), + "Change Airlock Type" = image(icon = 'icons/obj/interface.dmi', icon_state = "airlocktype") + ) + choices -= mode // Get rid of the current mode, clicking it won't do anything. + var/choice = show_radial_menu(user, src, choices, custom_check = CALLBACK(src, .proc/check_menu, user)) + if(!check_menu(user)) + return + switch(choice) + if(RCD_MODE_AIRLOCK, RCD_MODE_DECON, RCD_MODE_WINDOW, RCD_MODE_TURF) + mode = choice + if("UI") + menu = RCD_PAGE_MAIN + ui_interact(user) + return + if("Change Access", "Change Airlock Type") + menu = RCD_PAGE_AIRLOCK + ui_interact(user) + return + else + return + playsound(src, 'sound/effects/pop.ogg', 50, 0) + to_chat(user, "You change [src]'s mode to '[choice]'.") + + +/obj/item/rcd/attack_self(mob/user) + //Change the mode // Oh I thought the UI was just for fucking staring at + radial_menu(user) + +/obj/item/rcd/attack_self_tk(mob/user) + radial_menu(user) + +/obj/item/rcd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "rcd.tmpl", "[name]", 450, 400, state = state) + ui.open() + ui.set_auto_update(1) + +/obj/item/rcd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) + var/data[0] + data["mode"] = mode + data["door_type"] = door_type + data["door_name"] = door_name + data["menu"] = menu + data["matter"] = matter + data["max_matter"] = max_matter + data["one_access"] = one_access + data["locked"] = locked + + if(menu == RCD_PAGE_AIRLOCK) + var/list/door_types_list = list() + for(var/type in GLOB.rcd_door_types) + door_types_list[++door_types_list.len] = list("name" = GLOB.rcd_door_types[type], "type" = type) + data["allowed_door_types"] = door_types_list + + data["door_accesses"] = door_accesses_list + + return data + +/obj/item/rcd/Topic(href, href_list, nowindow, state) + if(..()) + return 1 + + if(prob(20)) + spark_system.start() + + if(href_list["mode"]) + mode = href_list["mode"] + . = 1 + + if(href_list["door_type"]) + var/new_door_type = text2path(href_list["door_type"]) + if(!(new_door_type in GLOB.rcd_door_types)) + message_admins("RCD Door HREF exploit attempted by [key_name(usr)]!") + return + door_type = new_door_type + . = 1 + + if(href_list["menu"]) + menu = text2num(href_list["menu"]) + . = 1 + + if(href_list["login"]) + if(allowed(usr)) + locked = FALSE + . = 1 + + if(href_list["logout"]) + locked = TRUE + . = 1 + + if(!locked) + if(href_list["toggle_one_access"]) + one_access = !one_access + . = 1 + + if(href_list["toggle_access"]) + var/href_access = text2num(href_list["toggle_access"]) + if(href_access in door_accesses) + door_accesses -= href_access + else + door_accesses += href_access + door_accesses_list = list() + for(var/access in get_all_accesses()) + door_accesses_list[++door_accesses_list.len] = list("name" = get_access_desc(access), "id" = access, "enabled" = (access in door_accesses)) + . = 1 + + if(href_list["choice"]) + var/temp_t = sanitize(copytext(input("Enter a custom Airlock Name.", "Airlock Name"), 1, MAX_MESSAGE_LEN)) + if(temp_t) + door_name = temp_t + +/obj/item/rcd/proc/mode_turf(atom/A, mob/user) + if(isspaceturf(A) || istype(A, /obj/structure/lattice)) + if(useResource(1, user)) + to_chat(user, "Building Floor...") + playsound(loc, usesound, 50, 1) + var/turf/AT = get_turf(A) + AT.ChangeTurf(/turf/simulated/floor/plating) + return TRUE + to_chat(user, "ERROR! Not enough matter in unit to construct this floor!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + + if(isfloorturf(A)) + if(checkResource(3, user)) + to_chat(user, "Building Wall...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 20 * toolspeed, target = A)) + if(!useResource(3, user)) + return FALSE + playsound(loc, usesound, 50, 1) + var/turf/AT = A + AT.ChangeTurf(/turf/simulated/wall) + return TRUE + return FALSE + to_chat(user, "ERROR! Not enough matter in unit to construct this wall!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + to_chat(user, "ERROR! Location unsuitable for wall construction!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + +/obj/item/rcd/proc/mode_airlock(atom/A, mob/user) + if(isfloorturf(A)) + if(checkResource(10, user)) + to_chat(user, "Building Airlock...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 50 * toolspeed, target = A)) + if(locate(/obj/machinery/door/airlock) in A.contents) + return FALSE + if(!useResource(10, user)) + return FALSE + playsound(loc, usesound, 50, 1) + var/obj/machinery/door/airlock/T = new door_type(A) + T.name = door_name + T.autoclose = TRUE + if(one_access) + T.req_one_access = door_accesses.Copy() + else + T.req_access = door_accesses.Copy() + return FALSE + return FALSE + to_chat(user, "ERROR! Not enough matter in unit to construct this airlock!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + to_chat(user, "ERROR! Location unsuitable for airlock construction!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + +/obj/item/rcd/proc/mode_decon(atom/A, mob/user) + if(iswallturf(A)) + if(istype(A, /turf/simulated/wall/r_wall) && !canRwall) + return FALSE + if(checkResource(5, user)) + to_chat(user, "Deconstructing Wall...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 40 * toolspeed, target = A)) + if(!useResource(5, user)) + return FALSE + playsound(loc, usesound, 50, 1) + var/turf/AT = A + AT.ChangeTurf(/turf/simulated/floor/plating) + return TRUE + return FALSE + to_chat(user, "ERROR! Not enough matter in unit to deconstruct this wall!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + + if(isfloorturf(A)) + if(checkResource(5, user)) + to_chat(user, "Deconstructing Floor...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 50 * toolspeed, target = A)) + if(!useResource(5, user)) + return FALSE + playsound(loc, usesound, 50, 1) + var/turf/AT = A + AT.ChangeTurf(AT.baseturf) + return TRUE + return FALSE + to_chat(user, "ERROR! Not enough matter in unit to deconstruct this floor!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + + if(istype(A, /obj/machinery/door/airlock)) + if(checkResource(20, user)) + to_chat(user, "Deconstructing Airlock...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(do_after(user, 50 * toolspeed, target = A)) + if(!useResource(20, user)) + return FALSE + playsound(loc, usesound, 50, 1) + qdel(A) + return TRUE + return FALSE + to_chat(user, "ERROR! Not enough matter in unit to deconstruct this airlock!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return FALSE + + if(istype(A, /obj/structure/window)) // You mean the grille of course, do you? + A = locate(/obj/structure/grille) in A.loc + if(istype(A, /obj/structure/grille)) + if(!checkResource(2, user)) + to_chat(user, "ERROR! Not enough matter in unit to deconstruct this window!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return 0 + to_chat(user, "Deconstructing window...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(!do_after(user, 20 * toolspeed, target = A)) + return 0 + if(!useResource(2, user)) + return 0 + playsound(loc, usesound, 50, 1) + var/turf/T1 = get_turf(A) + QDEL_NULL(A) + for(var/obj/structure/window/W in T1.contents) + qdel(W) + for(var/cdir in GLOB.cardinal) + var/turf/T2 = get_step(T1, cdir) + if(locate(/obj/structure/window/full/shuttle) in T2) + continue // Shuttle windows? Nah. We don't need extra windows there. + if(!(locate(/obj/structure/grille) in T2)) + continue + for(var/obj/structure/window/W in T2) + if(W.dir == turn(cdir, 180)) + qdel(W) + var/obj/structure/window/reinforced/W = new(T2) + W.dir = turn(cdir, 180) + return TRUE + return FALSE + +/obj/item/rcd/proc/mode_window(atom/A, mob/user) + if(isfloorturf(A)) + if(locate(/obj/structure/grille) in A) + return 0 // We already have window + if(!checkResource(2, user)) + to_chat(user, "ERROR! Not enough matter in unit to construct this window!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return 0 + to_chat(user, "Constructing window...") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + if(!do_after(user, 20 * toolspeed, target = A)) + return 0 + if(locate(/obj/structure/grille) in A) + return 0 // We already have window + if(!useResource(2, user)) + return 0 + playsound(loc, usesound, 50, 1) + new /obj/structure/grille(A) + for(var/obj/structure/window/W in A) + qdel(W) + for(var/cdir in GLOB.cardinal) + var/turf/T = get_step(A, cdir) + if(locate(/obj/structure/grille) in T) + for(var/obj/structure/window/W in T) + if(W.dir == turn(cdir, 180)) + qdel(W) + else // Build a window! + var/obj/structure/window/reinforced/W = new(A) + W.dir = cdir + var/turf/AT = A + AT.ChangeTurf(/turf/simulated/floor/plating) // Platings go under windows. + return 1 + to_chat(user, "ERROR! Location unsuitable for window construction!") + playsound(loc, 'sound/machines/click.ogg', 50, 1) + return 0 + +/obj/item/rcd/afterattack(atom/A, mob/user, proximity) + if(!proximity) + return FALSE + if(istype(A, /turf/space/transit)) + return FALSE + if(!is_type_in_list(A, allowed_targets)) + return FALSE + + switch(mode) + if(RCD_MODE_TURF) + . = mode_turf(A, user) + if(RCD_MODE_AIRLOCK) + . = mode_airlock(A, user) + if(RCD_MODE_DECON) + . = mode_decon(A, user) + if(RCD_MODE_WINDOW) + . = mode_window(A, user) + else + to_chat(user, "ERROR: RCD in MODE: [mode] attempted use by [user]. Send this text #coderbus or an admin.") + . = 0 + + SSnanoui.update_uis(src) + +/obj/item/rcd/proc/useResource(amount, mob/user) + if(matter < amount) + return 0 + matter -= amount + SSnanoui.update_uis(src) + return 1 + +/obj/item/rcd/proc/checkResource(amount, mob/user) + return matter >= amount + +/obj/item/rcd/borg + canRwall = 1 + var/use_multiplier = 160 + +/obj/item/rcd/borg/syndicate + use_multiplier = 80 + +/obj/item/rcd/borg/useResource(amount, mob/user) + if(!isrobot(user)) + return 0 + var/mob/living/silicon/robot/R = user + return R.cell.use(amount * use_multiplier) + +/obj/item/rcd/borg/checkResource(amount, mob/user) + if(!isrobot(user)) + return 0 + var/mob/living/silicon/robot/R = user + return R.cell.charge >= (amount * use_multiplier) + +/obj/item/rcd/proc/detonate_pulse() + audible_message("[src] begins to vibrate and \ + buzz loudly!","[src] begins \ + vibrating violently!") + // 5 seconds to get rid of it + addtimer(CALLBACK(src, .proc/detonate_pulse_explode), 50) + +/obj/item/rcd/proc/detonate_pulse_explode() + explosion(src, 0, 0, 3, 1, flame_range = 1) + qdel(src) + +/obj/item/rcd/preloaded + matter = 100 + +/obj/item/rcd/combat + name = "combat RCD" + max_matter = 500 + matter = 500 + canRwall = 1 + +/obj/item/rcd_ammo + name = "compressed matter cartridge" + desc = "Highly compressed matter for the RCD." + icon = 'icons/obj/ammo.dmi' + icon_state = "rcd" + item_state = "rcdammo" + opacity = 0 + density = 0 + anchored = 0.0 + origin_tech = "materials=3" + materials = list(MAT_METAL=16000, MAT_GLASS=8000) + var/ammoamt = 20 + +/obj/item/rcd_ammo/large + ammoamt = 100 diff --git a/code/game/objects/items/weapons/RCL.dm b/code/game/objects/items/weapons/RCL.dm index fc803a5398e3..27583a7edd3f 100644 --- a/code/game/objects/items/weapons/RCL.dm +++ b/code/game/objects/items/weapons/RCL.dm @@ -151,4 +151,4 @@ loaded = new() loaded.max_amount = max_amount loaded.amount = max_amount - update_icon() \ No newline at end of file + update_icon() diff --git a/code/game/objects/items/weapons/RSF.dm b/code/game/objects/items/weapons/RSF.dm index 55dfa04c3949..a8da7503c72d 100644 --- a/code/game/objects/items/weapons/RSF.dm +++ b/code/game/objects/items/weapons/RSF.dm @@ -1,84 +1,84 @@ -/* -CONTAINS: -RSF -*/ - -/obj/item/rsf - name = "\improper Rapid-Service-Fabricator" - desc = "A device used to rapidly deploy service items." - icon = 'icons/obj/tools.dmi' - icon_state = "rsf" - opacity = 0 - density = 0 - anchored = 0.0 - var/matter = 0 - var/mode = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - w_class = WEIGHT_CLASS_NORMAL - var/list/configured_items = list() - -/obj/item/rsf/New() - ..() - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - // configured_items[ID_NUMBER] = list("Human-readable name", price in energy, /type/path) - configured_items[++configured_items.len] = list("Dosh", 50, /obj/item/stack/spacecash/c10) - configured_items[++configured_items.len] = list("Drinking Glass", 50, /obj/item/reagent_containers/food/drinks/drinkingglass) - configured_items[++configured_items.len] = list("Paper", 50, /obj/item/paper) - configured_items[++configured_items.len] = list("Pen", 50, /obj/item/pen) - configured_items[++configured_items.len] = list("Dice Pack", 50, /obj/item/storage/pill_bottle/dice) - configured_items[++configured_items.len] = list("Cigarette", 50, /obj/item/clothing/mask/cigarette) - configured_items[++configured_items.len] = list("Snack - Newdles", 4000, /obj/item/reagent_containers/food/snacks/chinese/newdles) - configured_items[++configured_items.len] = list("Snack - Donut", 4000, /obj/item/reagent_containers/food/snacks/donut) - configured_items[++configured_items.len] = list("Snack - Chicken Soup", 4000, /obj/item/reagent_containers/food/drinks/chicken_soup) - configured_items[++configured_items.len] = list("Snack - Turkey Burger", 4000, /obj/item/reagent_containers/food/snacks/tofuburger) - -/obj/item/rsf/attackby(obj/item/W as obj, mob/user as mob, params) - ..() - if(istype(W, /obj/item/rcd_ammo)) - if((matter + 10) > 30) - to_chat(user, "The RSF cant hold any more matter.") - return - qdel(W) - matter += 10 - playsound(src.loc, 'sound/machines/click.ogg', 10, 1) - to_chat(user, "The RSF now holds [matter]/30 fabrication-units.") - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - return - -/obj/item/rsf/attack_self(mob/user as mob) - playsound(src.loc, 'sound/effects/pop.ogg', 50, 0) - if(mode == configured_items.len) - mode = 1 - else - mode++ - to_chat(user, "Changed dispensing mode to '" + configured_items[mode][1] + "'") - - -/obj/item/rsf/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) return - if(!(istype(A, /obj/structure/table) || istype(A, /turf/simulated/floor))) - return - var spawn_location - var/turf/T = get_turf(A) - if(istype(T) && !T.density) - spawn_location = T - else - to_chat(user, "The RSF can only create service items on tables, or floors.") - return - if(isrobot(user)) - var/mob/living/silicon/robot/engy = user - if(!engy.cell.use(configured_items[mode][2])) - to_chat(user, "Insufficient energy.") - return - else - if(!matter) - to_chat(user, "Insufficient matter.") - return - matter-- - to_chat(user, "The RSF now holds [matter]/30 fabrication-units.") - desc = "A RSF. It currently holds [matter]/30 fabrication-units." - - to_chat(user, "Dispensing " + configured_items[mode][1] + "...") - playsound(loc, 'sound/machines/click.ogg', 10, 1) - var/type_path = configured_items[mode][3] - new type_path(spawn_location) +/* +CONTAINS: +RSF +*/ + +/obj/item/rsf + name = "\improper Rapid-Service-Fabricator" + desc = "A device used to rapidly deploy service items." + icon = 'icons/obj/tools.dmi' + icon_state = "rsf" + opacity = 0 + density = 0 + anchored = 0.0 + var/matter = 0 + var/mode = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + w_class = WEIGHT_CLASS_NORMAL + var/list/configured_items = list() + +/obj/item/rsf/New() + ..() + desc = "A RSF. It currently holds [matter]/30 fabrication-units." + // configured_items[ID_NUMBER] = list("Human-readable name", price in energy, /type/path) + configured_items[++configured_items.len] = list("Dosh", 50, /obj/item/stack/spacecash/c10) + configured_items[++configured_items.len] = list("Drinking Glass", 50, /obj/item/reagent_containers/food/drinks/drinkingglass) + configured_items[++configured_items.len] = list("Paper", 50, /obj/item/paper) + configured_items[++configured_items.len] = list("Pen", 50, /obj/item/pen) + configured_items[++configured_items.len] = list("Dice Pack", 50, /obj/item/storage/pill_bottle/dice) + configured_items[++configured_items.len] = list("Cigarette", 50, /obj/item/clothing/mask/cigarette) + configured_items[++configured_items.len] = list("Snack - Newdles", 4000, /obj/item/reagent_containers/food/snacks/chinese/newdles) + configured_items[++configured_items.len] = list("Snack - Donut", 4000, /obj/item/reagent_containers/food/snacks/donut) + configured_items[++configured_items.len] = list("Snack - Chicken Soup", 4000, /obj/item/reagent_containers/food/drinks/chicken_soup) + configured_items[++configured_items.len] = list("Snack - Turkey Burger", 4000, /obj/item/reagent_containers/food/snacks/tofuburger) + +/obj/item/rsf/attackby(obj/item/W as obj, mob/user as mob, params) + ..() + if(istype(W, /obj/item/rcd_ammo)) + if((matter + 10) > 30) + to_chat(user, "The RSF cant hold any more matter.") + return + qdel(W) + matter += 10 + playsound(src.loc, 'sound/machines/click.ogg', 10, 1) + to_chat(user, "The RSF now holds [matter]/30 fabrication-units.") + desc = "A RSF. It currently holds [matter]/30 fabrication-units." + return + +/obj/item/rsf/attack_self(mob/user as mob) + playsound(src.loc, 'sound/effects/pop.ogg', 50, 0) + if(mode == configured_items.len) + mode = 1 + else + mode++ + to_chat(user, "Changed dispensing mode to '" + configured_items[mode][1] + "'") + + +/obj/item/rsf/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) return + if(!(istype(A, /obj/structure/table) || istype(A, /turf/simulated/floor))) + return + var spawn_location + var/turf/T = get_turf(A) + if(istype(T) && !T.density) + spawn_location = T + else + to_chat(user, "The RSF can only create service items on tables, or floors.") + return + if(isrobot(user)) + var/mob/living/silicon/robot/engy = user + if(!engy.cell.use(configured_items[mode][2])) + to_chat(user, "Insufficient energy.") + return + else + if(!matter) + to_chat(user, "Insufficient matter.") + return + matter-- + to_chat(user, "The RSF now holds [matter]/30 fabrication-units.") + desc = "A RSF. It currently holds [matter]/30 fabrication-units." + + to_chat(user, "Dispensing " + configured_items[mode][1] + "...") + playsound(loc, 'sound/machines/click.ogg', 10, 1) + var/type_path = configured_items[mode][3] + new type_path(spawn_location) diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 20b2ac3cfc1d..405ce47a0330 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -1,927 +1,927 @@ -/* Cards - * Contains: - * DATA CARD - * ID CARD - * FINGERPRINT CARD HOLDER - * FINGERPRINT CARD - */ - - - -/* - * DATA CARDS - Used for the teleporter - */ -/obj/item/card - name = "card" - desc = "A card." - icon = 'icons/obj/card.dmi' - w_class = WEIGHT_CLASS_TINY - var/associated_account_number = 0 - - var/list/files = list( ) - -/obj/item/card/data - name = "data card" - desc = "A disk containing data." - icon_state = "data" - var/function = "storage" - var/data = "null" - var/special = null - item_state = "card-id" - -/obj/item/card/data/verb/label(t as text) - set name = "Label Disk" - set category = "Object" - set src in usr - - if(t) - src.name = text("Data Disk- '[]'", t) - else - src.name = "Data Disk" - src.add_fingerprint(usr) - return - -/obj/item/card/data/clown - name = "coordinates to clown planet" - icon_state = "data" - item_state = "card-id" - layer = 3 - level = 2 - desc = "This card contains coordinates to the fabled Clown Planet. Handle with care." - function = "teleporter" - data = "Clown Land" - -/* - * ID CARDS - */ - -/obj/item/card/emag_broken - desc = "It's a card with a magnetic strip attached to some circuitry. It looks too busted to be used for anything but salvage." - name = "broken cryptographic sequencer" - icon_state = "emag" - item_state = "card-id" - origin_tech = "magnets=2;syndicate=2" - -/obj/item/card/emag - desc = "It's a card with a magnetic strip attached to some circuitry." - name = "cryptographic sequencer" - icon_state = "emag" - item_state = "card-id" - origin_tech = "magnets=2;syndicate=2" - flags = NOBLUDGEON - flags_2 = NO_MAT_REDEMPTION_2 - -/obj/item/card/emag/attack() - return - -/obj/item/card/emag/afterattack(atom/target, mob/user, proximity) - var/atom/A = target - if(!proximity) - return - A.emag_act(user) - -/obj/item/card/id - name = "identification card" - desc = "A card used to provide ID and determine access across the station." - icon_state = "id" - item_state = "card-id" - var/mining_points = 0 //For redeeming at mining equipment lockers - var/list/access = list() - var/registered_name = "Unknown" // The name registered_name on the card - slot_flags = SLOT_ID - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - var/untrackable // Can not be tracked by AI's - - var/blood_type = "\[UNSET\]" - var/dna_hash = "\[UNSET\]" - var/fingerprint_hash = "\[UNSET\]" - - //alt titles are handled a bit weirdly in order to unobtrusively integrate into existing ID system - var/assignment = null //can be alt title or the actual job - var/rank = null //actual job - var/owner_uid - var/owner_ckey - var/dorm = 0 // determines if this ID has claimed a dorm already - - var/sex - var/age - var/photo - var/dat - var/stamped = 0 - - var/obj/item/card/id/guest/guest_pass = null // Guest pass attached to the ID - -/obj/item/card/id/New() - ..() - spawn(30) - if(ishuman(loc) && blood_type == "\[UNSET\]") - var/mob/living/carbon/human/H = loc - SetOwnerInfo(H) - -/obj/item/card/id/examine(mob/user) - . = ..() - if(in_range(user, src)) - show(usr) - else - . += "It is too far away." - if(guest_pass) - . += "There is a guest pass attached to this ID card" - if(world.time < guest_pass.expiration_time) - . += "It expires at [station_time_timestamp("hh:mm:ss", guest_pass.expiration_time)]." - else - . += "It expired at [station_time_timestamp("hh:mm:ss", guest_pass.expiration_time)]." - . += "It grants access to following areas:" - for(var/A in guest_pass.temp_access) - . += "[get_access_desc(A)]." - . += "Issuing reason: [guest_pass.reason]." - -/obj/item/card/id/proc/show(mob/user as mob) - var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper) - assets.send(user) - - var/datum/browser/popup = new(user, "idcard", name, 600, 400) - popup.set_content(dat) - popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - -/obj/item/card/id/attack_self(mob/user as mob) - user.visible_message("[user] shows you: [bicon(src)] [src.name]. The assignment on the card: [src.assignment]",\ - "You flash your ID card: [bicon(src)] [src.name]. The assignment on the card: [src.assignment]") - if(mining_points) - to_chat(user, "There's [mining_points] mining equipment redemption points loaded onto this card.") - src.add_fingerprint(user) - return - -/obj/item/card/id/proc/UpdateName() - name = "[src.registered_name]'s ID Card ([src.assignment])" - -/obj/item/card/id/proc/SetOwnerInfo(var/mob/living/carbon/human/H) - if(!H || !H.dna) - return - - sex = capitalize(H.gender) - age = H.age - blood_type = H.dna.blood_type - dna_hash = H.dna.unique_enzymes - fingerprint_hash = md5(H.dna.uni_identity) - - RebuildHTML() - -/obj/item/card/id/proc/RebuildHTML() - var/photo_front = "'data:image/png;base64,[icon2base64(icon(photo, dir = SOUTH))]'" - var/photo_side = "'data:image/png;base64,[icon2base64(icon(photo, dir = WEST))]'" - - dat = {"
    - Name: [registered_name]
    - Sex: [sex]
    - Age: [age]
    - Rank: [assignment]
    - Fingerprint: [fingerprint_hash]
    - Blood Type: [blood_type]
    - DNA Hash: [dna_hash]

    -
    Photo:
    -
    "} - -/obj/item/card/id/GetAccess() - if(!guest_pass) - return access - return access | guest_pass.GetAccess() - -/obj/item/card/id/GetID() - return src - -/obj/item/card/id/proc/getRankAndAssignment() - var/jobnamedata = "" - if(rank) - jobnamedata += rank - if(rank != assignment) - jobnamedata += " (" + assignment + ")" - return jobnamedata - -/obj/item/card/id/proc/getPlayer() - if(owner_uid) - var/mob/living/carbon/human/H = locateUID(owner_uid) - if(istype(H) && H.ckey == owner_ckey) - return H - owner_uid = null - if(owner_ckey) - for(var/mob/M in GLOB.player_list) - if(M.ckey && M.ckey == owner_ckey) - owner_uid = M.UID() - return M - owner_ckey = null - -/obj/item/card/id/proc/is_untrackable() - return untrackable - -/obj/item/card/id/proc/update_label(newname, newjob) - if(newname || newjob) - name = "[(!newname) ? "identification card" : "[newname]'s ID Card"][(!newjob) ? "" : " ([newjob])"]" - return - - name = "[(!registered_name) ? "identification card" : "[registered_name]'s ID Card"][(!assignment) ? "" : " ([assignment])"]" - -/obj/item/card/id/attackby(obj/item/W as obj, mob/user as mob, params) - ..() - - if(istype(W, /obj/item/id_decal/)) - var/obj/item/id_decal/decal = W - to_chat(user, "You apply [decal] to [src].") - if(decal.override_name) - name = decal.decal_name - desc = decal.decal_desc - icon_state = decal.decal_icon_state - item_state = decal.decal_item_state - qdel(decal) - qdel(W) - return - - else if(istype (W,/obj/item/stamp)) - if(!stamped) - dat+="" - stamped = 1 - to_chat(user, "You stamp the ID card!") - else - to_chat(user, "This ID has already been stamped!") - - else if(istype(W, /obj/item/card/id/guest)) - if(istype(src, /obj/item/card/id/guest)) - return - var/obj/item/card/id/guest/G = W - if(world.time > G.expiration_time) - to_chat(user, "There's no point, the guest pass has expired.") - return - if(guest_pass) - to_chat(user, "There's already a guest pass attached to this ID.") - return - if(G.registered_name != registered_name && G.registered_name != "NOT SPECIFIED") - to_chat(user, "The guest pass cannot be attached to this ID") - return - if(!user.unEquip(G)) - return - G.loc = src - guest_pass = G - -/obj/item/card/id/verb/remove_guest_pass() - set name = "Remove Guest Pass" - set category = "Object" - set src in range(0) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(guest_pass) - to_chat(usr, "You remove the guest pass from this ID.") - guest_pass.forceMove(get_turf(src)) - guest_pass = null - else - to_chat(usr, "There is no guest pass attached to this ID") - -/obj/item/card/id/serialize() - var/list/data = ..() - - data["sex"] = sex - data["age"] = age - data["btype"] = blood_type - data["dna_hash"] = dna_hash - data["fprint_hash"] = fingerprint_hash - data["access"] = access - data["job"] = assignment - data["account"] = associated_account_number - data["owner"] = registered_name - data["mining"] = mining_points - return data - -/obj/item/card/id/deserialize(list/data) - sex = data["sex"] - age = data["age"] - blood_type = data["btype"] - dna_hash = data["dna_hash"] - fingerprint_hash = data["fprint_hash"] - access = data["access"] // No need for a copy, the list isn't getting touched - assignment = data["job"] - associated_account_number = data["account"] - registered_name = data["owner"] - mining_points = data["mining"] - // We'd need to use icon serialization(b64) to save the photo, and I don't feel like i - UpdateName() - RebuildHTML() - ..() - -/obj/item/card/id/silver - name = "identification card" - desc = "A silver card which shows honour and dedication." - icon_state = "silver" - item_state = "silver_id" - -/obj/item/card/id/gold - name = "identification card" - desc = "A golden card which shows power and might." - icon_state = "gold" - item_state = "gold_id" - -/obj/item/card/id/syndicate - name = "agent card" - var/list/initial_access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS) - origin_tech = "syndicate=1" - var/registered_user = null - untrackable = 1 - var/anyone = FALSE //Can anyone forge the ID or just syndicate? - -/obj/item/card/id/syndicate/anyone - anyone = TRUE - -/obj/item/card/id/syndicate/New() - access = initial_access.Copy() - ..() - -/obj/item/card/id/syndicate/vox - name = "agent card" - initial_access = list(ACCESS_MAINT_TUNNELS, ACCESS_VOX, ACCESS_EXTERNAL_AIRLOCKS) - -/obj/item/card/id/syndicate/command - initial_access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND, ACCESS_EXTERNAL_AIRLOCKS) - icon_state = "commander" - -/obj/item/card/id/syndicate/afterattack(var/obj/item/O as obj, mob/user as mob, proximity) - if(!proximity) - return - if(istype(O, /obj/item/card/id)) - var/obj/item/card/id/I = O - if(istype(user, /mob/living) && user.mind) - if(user.mind.special_role || anyone) - to_chat(usr, "The card's microscanners activate as you pass it over \the [I], copying its access.") - src.access |= I.access //Don't copy access if user isn't an antag -- to prevent metagaming - -/obj/item/card/id/syndicate/attack_self(mob/user as mob) - if(!src.registered_name) - var t = reject_bad_name(input(user, "What name would you like to use on this card?", "Agent Card name", ishuman(user) ? user.real_name : user.name)) - if(!t) - to_chat(user, "Invalid name.") - return - src.registered_name = t - - var u = sanitize(stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than maintenance.", "Agent Card Job Assignment", "Agent", MAX_MESSAGE_LEN)) - if(!u) - to_chat(user, "Invalid assignment.") - src.registered_name = "" - return - src.assignment = u - src.name = "[src.registered_name]'s ID Card ([src.assignment])" - to_chat(user, "You successfully forge the ID card.") - registered_user = user - else if(!registered_user || registered_user == user) - if(!registered_user) - registered_user = user - - switch(alert(user,"Would you like to display \the [src] or edit it?","Choose","Show","Edit")) - if("Show") - return ..() - if("Edit") - switch(input(user,"What would you like to edit on \the [src]?") in list("Name", "Photo", "Appearance", "Sex", "Age", "Occupation", "Money Account", "Blood Type", "DNA Hash", "Fingerprint Hash", "Reset Access", "Delete Card Information")) - if("Name") - var/new_name = reject_bad_name(input(user,"What name would you like to put on this card?","Agent Card Name", ishuman(user) ? user.real_name : user.name)) - if(!Adjacent(user)) - return - src.registered_name = new_name - UpdateName() - to_chat(user, "Name changed to [new_name].") - RebuildHTML() - - if("Photo") - if(!Adjacent(user)) - return - var/job_clothes = null - if(assignment) - job_clothes = assignment - var/icon/newphoto = get_id_photo(user, job_clothes) - if(!newphoto) - return - photo = newphoto - to_chat(user, "Photo changed. Select another occupation and take a new photo if you wish to appear with different clothes.") - RebuildHTML() - - if("Appearance") - var/list/appearances = list( - "data", - "id", - "gold", - "silver", - "centcom", - "centcom_old", - "security", - "medical", - "HoS", - "research", - "cargo", - "engineering", - "CMO", - "RD", - "CE", - "clown", - "mime", - "rainbow", - "prisoner", - "syndie", - "deathsquad", - "commander", - "ERT_leader", - "ERT_security", - "ERT_engineering", - "ERT_medical", - "ERT_janitorial", - ) - var/choice = input(user, "Select the appearance for this card.", "Agent Card Appearance") in appearances - if(!Adjacent(user)) - return - if(!choice) - return - icon_state = choice - switch(choice) - if("silver") - desc = "A silver card which shows honour and dedication." - if("gold") - desc = "A golden card which shows power and might." - if("clown") - desc = "Even looking at the card strikes you with deep fear." - if("mime") - desc = "..." - if("prisoner") - desc = "You are a number, you are not a free man." - if("centcom") - desc = "An ID straight from Central Command." - else - desc = "A card used to provide ID and determine access across the station." - to_chat(usr, "Appearance changed to [choice].") - - if("Sex") - var/new_sex = sanitize(stripped_input(user,"What sex would you like to put on this card?","Agent Card Sex", ishuman(user) ? capitalize(user.gender) : "Male", MAX_MESSAGE_LEN)) - if(!Adjacent(user)) - return - sex = new_sex - to_chat(user, "Sex changed to [new_sex].") - RebuildHTML() - - if("Age") - var/default = "21" - if(ishuman(user)) - var/mob/living/carbon/human/H = user - default = H.age - var/new_age = sanitize(input(user,"What age would you like to be written on this card?","Agent Card Age", default) as text) - if(!Adjacent(user)) - return - age = new_age - to_chat(user, "Age changed to [new_age].") - RebuildHTML() - - if("Occupation") - var/list/departments =list( - "Civilian", - "Engineering", - "Medical", - "Science", - "Security", - "Support", - "Command", - "Custom", - ) - - var/department = input(user, "What job would you like to put on this card?\nChoose a department or a custom job title.\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in departments - var/new_job = "Civilian" - - if(department == "Custom") - new_job = sanitize(stripped_input(user,"Choose a custom job title:","Agent Card Occupation", "Civilian", MAX_MESSAGE_LEN)) - else if(department != "Civilian") - switch(department) - if("Engineering") - new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in engineering_positions - if("Medical") - new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in medical_positions - if("Science") - new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in science_positions - if("Security") - new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in security_positions - if("Support") - new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in support_positions - if("Command") - new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in command_positions - - if(!Adjacent(user)) - return - assignment = new_job - to_chat(user, "Occupation changed to [new_job].") - UpdateName() - RebuildHTML() - - if("Money Account") - var/new_account = input(user,"What money account would you like to link to this card?","Agent Card Account",12345) as num - if(!Adjacent(user)) - return - associated_account_number = new_account - to_chat(user, "Linked money account changed to [new_account].") - - if("Blood Type") - var/default = "\[UNSET\]" - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna) - default = H.dna.blood_type - - var/new_blood_type = sanitize(input(user,"What blood type would you like to be written on this card?","Agent Card Blood Type",default) as text) - if(!Adjacent(user)) - return - blood_type = new_blood_type - to_chat(user, "Blood type changed to [new_blood_type].") - RebuildHTML() - - if("DNA Hash") - var/default = "\[UNSET\]" - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna) - default = H.dna.unique_enzymes - - var/new_dna_hash = sanitize(input(user,"What DNA hash would you like to be written on this card?","Agent Card DNA Hash",default) as text) - if(!Adjacent(user)) - return - dna_hash = new_dna_hash - to_chat(user, "DNA hash changed to [new_dna_hash].") - RebuildHTML() - - if("Fingerprint Hash") - var/default = "\[UNSET\]" - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna) - default = md5(H.dna.uni_identity) - - var/new_fingerprint_hash = sanitize(input(user,"What fingerprint hash would you like to be written on this card?","Agent Card Fingerprint Hash",default) as text) - if(!Adjacent(user)) - return - fingerprint_hash = new_fingerprint_hash - to_chat(user, "Fingerprint hash changed to [new_fingerprint_hash].") - RebuildHTML() - - if("Reset Access") - var/response = alert(user, "Are you sure you want to reset access saved on the card?","Reset Access", "No", "Yes") - if(response == "Yes") - access = initial_access.Copy() // Initial() doesn't work on lists - to_chat(user, "Card access reset.") - - if("Delete Card Information") - var/response = alert(user, "Are you sure you want to delete all information saved on the card?","Delete Card Information", "No", "Yes") - if(response == "Yes") - name = initial(name) - registered_name = initial(registered_name) - icon_state = initial(icon_state) - sex = initial(sex) - age = initial(age) - assignment = initial(assignment) - associated_account_number = initial(associated_account_number) - blood_type = initial(blood_type) - dna_hash = initial(dna_hash) - fingerprint_hash = initial(fingerprint_hash) - photo = null - registered_user = null - to_chat(user, "All information has been deleted from \the [src].") - RebuildHTML() - else - ..() - -/obj/item/card/id/syndicate_command - name = "syndicate ID card" - desc = "An ID straight from the Syndicate." - registered_name = "Syndicate" - icon_state = "syndie" - assignment = "Syndicate Overlord" - untrackable = 1 - access = list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND, ACCESS_EXTERNAL_AIRLOCKS) - -/obj/item/card/id/captains_spare - name = "captain's spare ID" - desc = "The spare ID of the captain." - icon_state = "gold" - item_state = "gold_id" - registered_name = "Captain" - assignment = "Captain" - -/obj/item/card/id/captains_spare/New() - var/datum/job/captain/J = new/datum/job/captain - access = J.get_access() - ..() - -/obj/item/card/id/admin - name = "admin ID card" - icon_state = "admin" - item_state = "gold_id" - registered_name = "Admin" - assignment = "Testing Shit" - untrackable = 1 - -/obj/item/card/id/admin/New() - access = get_absolutely_all_accesses() - ..() - -/obj/item/card/id/centcom - name = "central command ID card" - desc = "An ID straight from Central Command." - icon_state = "centcom" - registered_name = "Central Command" - assignment = "General" - -/obj/item/card/id/centcom/New() - access = get_all_centcom_access() - ..() - -/obj/item/card/id/nanotrasen - name = "nanotrasen ID card" - icon_state = "nanotrasen" - -/obj/item/card/id/prisoner - name = "prisoner ID card" - desc = "You are a number, you are not a free man." - icon_state = "prisoner" - item_state = "orange-id" - assignment = "Prisoner" - registered_name = "Scum" - var/goal = 0 //How far from freedom? - var/points = 0 - -/obj/item/card/id/prisoner/attack_self(mob/user as mob) - to_chat(usr, "You have accumulated [points] out of the [goal] points you need for freedom.") - -/obj/item/card/id/prisoner/one - name = "Prisoner #13-001" - registered_name = "Prisoner #13-001" - -/obj/item/card/id/prisoner/two - name = "Prisoner #13-002" - registered_name = "Prisoner #13-002" - -/obj/item/card/id/prisoner/three - name = "Prisoner #13-003" - registered_name = "Prisoner #13-003" - -/obj/item/card/id/prisoner/four - name = "Prisoner #13-004" - registered_name = "Prisoner #13-004" - -/obj/item/card/id/prisoner/five - name = "Prisoner #13-005" - registered_name = "Prisoner #13-005" - -/obj/item/card/id/prisoner/six - name = "Prisoner #13-006" - registered_name = "Prisoner #13-006" - -/obj/item/card/id/prisoner/seven - name = "Prisoner #13-007" - registered_name = "Prisoner #13-007" - -/obj/item/card/id/prisoner/random -/obj/item/card/id/prisoner/random/New() - ..() - var/random_number = "#[rand(0, 99)]-[rand(0, 999)]" - name = "Prisoner [random_number]" - registered_name = name - -/obj/item/card/id/salvage_captain - name = "Captain's ID" - registered_name = "Captain" - icon_state = "centcom" - desc = "Finders, keepers." - access = list(ACCESS_SALVAGE_CAPTAIN) - -/obj/item/card/id/medical - name = "Medical ID" - registered_name = "Medic" - icon_state = "medical" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) - -/obj/item/card/id/security - name = "Security ID" - registered_name = "Officer" - icon_state = "security" - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS) - -/obj/item/card/id/research - name = "Research ID" - registered_name = "Scientist" - icon_state = "research" - access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINERAL_STOREROOM) - -/obj/item/card/id/supply - name = "Supply ID" - registered_name = "Cargonian" - icon_state = "cargo" - access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) - -/obj/item/card/id/engineering - name = "Engineering ID" - registered_name = "Engineer" - icon_state = "engineering" - access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS) - -/obj/item/card/id/hos - name = "Head of Security ID" - registered_name = "HoS" - icon_state = "HoS" - access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, - ACCESS_FORENSICS_LOCKERS, ACCESS_PILOT, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, - ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, - ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS) - -/obj/item/card/id/cmo - name = "Chief Medical Officer ID" - registered_name = "CMO" - icon_state = "CMO" - access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, - ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, - ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) - -/obj/item/card/id/rd - name = "Research Director ID" - registered_name = "RD" - icon_state = "RD" - access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, - ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, - ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, - ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MINERAL_STOREROOM) - -/obj/item/card/id/ce - name = "Chief Engineer ID" - registered_name = "CE" - icon_state = "CE" - access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, - ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, - ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, - ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) - -/obj/item/card/id/clown - name = "Pink ID" - registered_name = "HONK!" - icon_state = "clown" - desc = "Even looking at the card strikes you with deep fear." - access = list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) - -/obj/item/card/id/mime - name = "Black and White ID" - registered_name = "..." - icon_state = "mime" - desc = "..." - access = list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) - -/obj/item/card/id/rainbow - name = "Rainbow ID" - icon_state = "rainbow" - -/obj/item/card/id/thunderdome/red - name = "Thunderdome Red ID" - registered_name = "Red Team Fighter" - assignment = "Red Team Fighter" - icon_state = "TDred" - desc = "This ID card is given to those who fought inside the thunderdome for the Red Team. Not many have lived to see one of those, even fewer lived to keep it." - -/obj/item/card/id/thunderdome/green - name = "Thunderdome Green ID" - registered_name = "Green Team Fighter" - assignment = "Green Team Fighter" - icon_state = "TDgreen" - desc = "This ID card is given to those who fought inside the thunderdome for the Green Team. Not many have lived to see one of those, even fewer lived to keep it." - -/obj/item/card/id/lifetime - name = "Lifetime ID Card" - desc = "A modified ID card given only to those people who have devoted their lives to the better interests of Nanotrasen. It sparkles blue." - icon_state = "lifetimeid" - -/obj/item/card/id/ert - name = "ERT ID" - icon_state = "ERT_empty" - -/obj/item/card/id/ert/commander - icon_state = "ERT_leader" -/obj/item/card/id/ert/security - icon_state = "ERT_security" -/obj/item/card/id/ert/engineering - icon_state = "ERT_engineering" -/obj/item/card/id/ert/medic - icon_state = "ERT_medical" - -/obj/item/card/id/golem - name = "Free Golem ID" - desc = "A card used to claim mining points and buy gear. Use it to mark it as yours." - icon_state = "research" - access = list(ACCESS_FREE_GOLEMS, ACCESS_ROBOTICS, ACCESS_CLOWN, ACCESS_MIME) //access to robots/mechs - var/registered = FALSE - -/obj/item/card/id/golem/attack_self(mob/user as mob) - if(!registered && ishuman(user)) - registered_name = user.real_name - SetOwnerInfo(user) - assignment = "Free Golem" - RebuildHTML() - UpdateName() - desc = "A card used to claim mining points and buy gear." - registered = TRUE - to_chat(user, "The ID is now registered as yours.") - else - ..() - -// Decals -/obj/item/id_decal - name = "identification card decal" - desc = "A nano-cellophane wrap that molds to an ID card to make it look snazzy." - icon = 'icons/obj/toy.dmi' - icon_state = "id_decal" - var/decal_name = "identification card" - var/decal_desc = "A card used to provide ID and determine access across the station." - var/decal_icon_state = "id" - var/decal_item_state = "card-id" - var/override_name = 0 - -/obj/item/id_decal/gold - name = "gold ID card decal" - icon_state = "id_decal_gold" - desc = "Make your ID look like the Captain's or a self-centered HOP's. Applies to any ID." - decal_desc = "A golden card which shows power and might." - decal_icon_state = "gold" - decal_item_state = "gold_id" - -/obj/item/id_decal/silver - name = "silver ID card decal" - icon_state = "id_decal_silver" - desc = "Make your ID look like HOP's because they wouldn't change it officially. Applies to any ID." - decal_desc = "A silver card which shows honour and dedication." - decal_icon_state = "silver" - decal_item_state = "silver_id" - -/obj/item/id_decal/prisoner - name = "prisoner ID card decal" - icon_state = "id_decal_prisoner" - desc = "All the cool kids have an ID that's this color. Applies to any ID." - decal_desc = "You are a number, you are not a free man." - decal_icon_state = "prisoner" - decal_item_state = "orange-id" - -/obj/item/id_decal/centcom - name = "centcom ID card decal" - icon_state = "id_decal_centcom" - desc = "All the prestige without the responsibility or the access. Applies to any ID." - decal_desc = "An ID straight from Cent. Com." - decal_icon_state = "centcom" - -/obj/item/id_decal/emag - name = "cryptographic sequencer ID card decal" - icon_state = "id_decal_emag" - desc = "A bundle of wires that you can tape to your ID to look very suspect. Applies to any ID." - decal_name = "cryptographic sequencer" - decal_desc = "It's a card with a magnetic strip attached to some circuitry." - decal_icon_state = "emag" - override_name = 1 - -/proc/get_station_card_skins() - return list("data","id","gold","silver","security","medical","research","cargo","engineering","HoS","CMO","RD","CE","clown","mime","rainbow","prisoner") - -/proc/get_centcom_card_skins() - return list("centcom","centcom_old","nanotrasen","ERT_leader","ERT_empty","ERT_security","ERT_engineering","ERT_medical","ERT_janitorial","deathsquad","commander","syndie","TDred","TDgreen") - -/proc/get_all_card_skins() - return get_station_card_skins() + get_centcom_card_skins() - -/proc/get_skin_desc(skin) - switch(skin) - if("id") - return "Standard" - if("cargo") - return "Supply" - if("HoS") - return "Head of Security" - if("CMO") - return "Chief Medical Officer" - if("RD") - return "Research Director" - if("CE") - return "Chief Engineer" - if("centcom_old") - return "Centcom Old" - if("ERT_leader") - return "ERT Leader" - if("ERT_empty") - return "ERT Default" - if("ERT_security") - return "ERT Security" - if("ERT_engineering") - return "ERT Engineering" - if("ERT_medical") - return "ERT Medical" - if("ERT_janitorial") - return "ERT Janitorial" - if("syndie") - return "Syndicate" - if("TDred") - return "Thunderdome Red" - if("TDgreen") - return "Thunderdome Green" - else - return capitalize(skin) +/* Cards + * Contains: + * DATA CARD + * ID CARD + * FINGERPRINT CARD HOLDER + * FINGERPRINT CARD + */ + + + +/* + * DATA CARDS - Used for the teleporter + */ +/obj/item/card + name = "card" + desc = "A card." + icon = 'icons/obj/card.dmi' + w_class = WEIGHT_CLASS_TINY + var/associated_account_number = 0 + + var/list/files = list( ) + +/obj/item/card/data + name = "data card" + desc = "A disk containing data." + icon_state = "data" + var/function = "storage" + var/data = "null" + var/special = null + item_state = "card-id" + +/obj/item/card/data/verb/label(t as text) + set name = "Label Disk" + set category = "Object" + set src in usr + + if(t) + src.name = text("Data Disk- '[]'", t) + else + src.name = "Data Disk" + src.add_fingerprint(usr) + return + +/obj/item/card/data/clown + name = "coordinates to clown planet" + icon_state = "data" + item_state = "card-id" + layer = 3 + level = 2 + desc = "This card contains coordinates to the fabled Clown Planet. Handle with care." + function = "teleporter" + data = "Clown Land" + +/* + * ID CARDS + */ + +/obj/item/card/emag_broken + desc = "It's a card with a magnetic strip attached to some circuitry. It looks too busted to be used for anything but salvage." + name = "broken cryptographic sequencer" + icon_state = "emag" + item_state = "card-id" + origin_tech = "magnets=2;syndicate=2" + +/obj/item/card/emag + desc = "It's a card with a magnetic strip attached to some circuitry." + name = "cryptographic sequencer" + icon_state = "emag" + item_state = "card-id" + origin_tech = "magnets=2;syndicate=2" + flags = NOBLUDGEON + flags_2 = NO_MAT_REDEMPTION_2 + +/obj/item/card/emag/attack() + return + +/obj/item/card/emag/afterattack(atom/target, mob/user, proximity) + var/atom/A = target + if(!proximity) + return + A.emag_act(user) + +/obj/item/card/id + name = "identification card" + desc = "A card used to provide ID and determine access across the station." + icon_state = "id" + item_state = "card-id" + var/mining_points = 0 //For redeeming at mining equipment lockers + var/list/access = list() + var/registered_name = "Unknown" // The name registered_name on the card + slot_flags = SLOT_ID + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + var/untrackable // Can not be tracked by AI's + + var/blood_type = "\[UNSET\]" + var/dna_hash = "\[UNSET\]" + var/fingerprint_hash = "\[UNSET\]" + + //alt titles are handled a bit weirdly in order to unobtrusively integrate into existing ID system + var/assignment = null //can be alt title or the actual job + var/rank = null //actual job + var/owner_uid + var/owner_ckey + var/dorm = 0 // determines if this ID has claimed a dorm already + + var/sex + var/age + var/photo + var/dat + var/stamped = 0 + + var/obj/item/card/id/guest/guest_pass = null // Guest pass attached to the ID + +/obj/item/card/id/New() + ..() + spawn(30) + if(ishuman(loc) && blood_type == "\[UNSET\]") + var/mob/living/carbon/human/H = loc + SetOwnerInfo(H) + +/obj/item/card/id/examine(mob/user) + . = ..() + if(in_range(user, src)) + show(usr) + else + . += "It is too far away." + if(guest_pass) + . += "There is a guest pass attached to this ID card" + if(world.time < guest_pass.expiration_time) + . += "It expires at [station_time_timestamp("hh:mm:ss", guest_pass.expiration_time)]." + else + . += "It expired at [station_time_timestamp("hh:mm:ss", guest_pass.expiration_time)]." + . += "It grants access to following areas:" + for(var/A in guest_pass.temp_access) + . += "[get_access_desc(A)]." + . += "Issuing reason: [guest_pass.reason]." + +/obj/item/card/id/proc/show(mob/user as mob) + var/datum/asset/assets = get_asset_datum(/datum/asset/simple/paper) + assets.send(user) + + var/datum/browser/popup = new(user, "idcard", name, 600, 400) + popup.set_content(dat) + popup.set_title_image(usr.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + +/obj/item/card/id/attack_self(mob/user as mob) + user.visible_message("[user] shows you: [bicon(src)] [src.name]. The assignment on the card: [src.assignment]",\ + "You flash your ID card: [bicon(src)] [src.name]. The assignment on the card: [src.assignment]") + if(mining_points) + to_chat(user, "There's [mining_points] mining equipment redemption points loaded onto this card.") + src.add_fingerprint(user) + return + +/obj/item/card/id/proc/UpdateName() + name = "[src.registered_name]'s ID Card ([src.assignment])" + +/obj/item/card/id/proc/SetOwnerInfo(var/mob/living/carbon/human/H) + if(!H || !H.dna) + return + + sex = capitalize(H.gender) + age = H.age + blood_type = H.dna.blood_type + dna_hash = H.dna.unique_enzymes + fingerprint_hash = md5(H.dna.uni_identity) + + RebuildHTML() + +/obj/item/card/id/proc/RebuildHTML() + var/photo_front = "'data:image/png;base64,[icon2base64(icon(photo, dir = SOUTH))]'" + var/photo_side = "'data:image/png;base64,[icon2base64(icon(photo, dir = WEST))]'" + + dat = {"
    + Name: [registered_name]
    + Sex: [sex]
    + Age: [age]
    + Rank: [assignment]
    + Fingerprint: [fingerprint_hash]
    + Blood Type: [blood_type]
    + DNA Hash: [dna_hash]

    +
    Photo:
    +
    "} + +/obj/item/card/id/GetAccess() + if(!guest_pass) + return access + return access | guest_pass.GetAccess() + +/obj/item/card/id/GetID() + return src + +/obj/item/card/id/proc/getRankAndAssignment() + var/jobnamedata = "" + if(rank) + jobnamedata += rank + if(rank != assignment) + jobnamedata += " (" + assignment + ")" + return jobnamedata + +/obj/item/card/id/proc/getPlayer() + if(owner_uid) + var/mob/living/carbon/human/H = locateUID(owner_uid) + if(istype(H) && H.ckey == owner_ckey) + return H + owner_uid = null + if(owner_ckey) + for(var/mob/M in GLOB.player_list) + if(M.ckey && M.ckey == owner_ckey) + owner_uid = M.UID() + return M + owner_ckey = null + +/obj/item/card/id/proc/is_untrackable() + return untrackable + +/obj/item/card/id/proc/update_label(newname, newjob) + if(newname || newjob) + name = "[(!newname) ? "identification card" : "[newname]'s ID Card"][(!newjob) ? "" : " ([newjob])"]" + return + + name = "[(!registered_name) ? "identification card" : "[registered_name]'s ID Card"][(!assignment) ? "" : " ([assignment])"]" + +/obj/item/card/id/attackby(obj/item/W as obj, mob/user as mob, params) + ..() + + if(istype(W, /obj/item/id_decal/)) + var/obj/item/id_decal/decal = W + to_chat(user, "You apply [decal] to [src].") + if(decal.override_name) + name = decal.decal_name + desc = decal.decal_desc + icon_state = decal.decal_icon_state + item_state = decal.decal_item_state + qdel(decal) + qdel(W) + return + + else if(istype (W,/obj/item/stamp)) + if(!stamped) + dat+="" + stamped = 1 + to_chat(user, "You stamp the ID card!") + else + to_chat(user, "This ID has already been stamped!") + + else if(istype(W, /obj/item/card/id/guest)) + if(istype(src, /obj/item/card/id/guest)) + return + var/obj/item/card/id/guest/G = W + if(world.time > G.expiration_time) + to_chat(user, "There's no point, the guest pass has expired.") + return + if(guest_pass) + to_chat(user, "There's already a guest pass attached to this ID.") + return + if(G.registered_name != registered_name && G.registered_name != "NOT SPECIFIED") + to_chat(user, "The guest pass cannot be attached to this ID") + return + if(!user.unEquip(G)) + return + G.loc = src + guest_pass = G + +/obj/item/card/id/verb/remove_guest_pass() + set name = "Remove Guest Pass" + set category = "Object" + set src in range(0) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(guest_pass) + to_chat(usr, "You remove the guest pass from this ID.") + guest_pass.forceMove(get_turf(src)) + guest_pass = null + else + to_chat(usr, "There is no guest pass attached to this ID") + +/obj/item/card/id/serialize() + var/list/data = ..() + + data["sex"] = sex + data["age"] = age + data["btype"] = blood_type + data["dna_hash"] = dna_hash + data["fprint_hash"] = fingerprint_hash + data["access"] = access + data["job"] = assignment + data["account"] = associated_account_number + data["owner"] = registered_name + data["mining"] = mining_points + return data + +/obj/item/card/id/deserialize(list/data) + sex = data["sex"] + age = data["age"] + blood_type = data["btype"] + dna_hash = data["dna_hash"] + fingerprint_hash = data["fprint_hash"] + access = data["access"] // No need for a copy, the list isn't getting touched + assignment = data["job"] + associated_account_number = data["account"] + registered_name = data["owner"] + mining_points = data["mining"] + // We'd need to use icon serialization(b64) to save the photo, and I don't feel like i + UpdateName() + RebuildHTML() + ..() + +/obj/item/card/id/silver + name = "identification card" + desc = "A silver card which shows honour and dedication." + icon_state = "silver" + item_state = "silver_id" + +/obj/item/card/id/gold + name = "identification card" + desc = "A golden card which shows power and might." + icon_state = "gold" + item_state = "gold_id" + +/obj/item/card/id/syndicate + name = "agent card" + var/list/initial_access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS) + origin_tech = "syndicate=1" + var/registered_user = null + untrackable = 1 + var/anyone = FALSE //Can anyone forge the ID or just syndicate? + +/obj/item/card/id/syndicate/anyone + anyone = TRUE + +/obj/item/card/id/syndicate/New() + access = initial_access.Copy() + ..() + +/obj/item/card/id/syndicate/vox + name = "agent card" + initial_access = list(ACCESS_MAINT_TUNNELS, ACCESS_VOX, ACCESS_EXTERNAL_AIRLOCKS) + +/obj/item/card/id/syndicate/command + initial_access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND, ACCESS_EXTERNAL_AIRLOCKS) + icon_state = "commander" + +/obj/item/card/id/syndicate/afterattack(var/obj/item/O as obj, mob/user as mob, proximity) + if(!proximity) + return + if(istype(O, /obj/item/card/id)) + var/obj/item/card/id/I = O + if(istype(user, /mob/living) && user.mind) + if(user.mind.special_role || anyone) + to_chat(usr, "The card's microscanners activate as you pass it over \the [I], copying its access.") + src.access |= I.access //Don't copy access if user isn't an antag -- to prevent metagaming + +/obj/item/card/id/syndicate/attack_self(mob/user as mob) + if(!src.registered_name) + var t = reject_bad_name(input(user, "What name would you like to use on this card?", "Agent Card name", ishuman(user) ? user.real_name : user.name)) + if(!t) + to_chat(user, "Invalid name.") + return + src.registered_name = t + + var u = sanitize(stripped_input(user, "What occupation would you like to put on this card?\nNote: This will not grant any access levels other than maintenance.", "Agent Card Job Assignment", "Agent", MAX_MESSAGE_LEN)) + if(!u) + to_chat(user, "Invalid assignment.") + src.registered_name = "" + return + src.assignment = u + src.name = "[src.registered_name]'s ID Card ([src.assignment])" + to_chat(user, "You successfully forge the ID card.") + registered_user = user + else if(!registered_user || registered_user == user) + if(!registered_user) + registered_user = user + + switch(alert(user,"Would you like to display \the [src] or edit it?","Choose","Show","Edit")) + if("Show") + return ..() + if("Edit") + switch(input(user,"What would you like to edit on \the [src]?") in list("Name", "Photo", "Appearance", "Sex", "Age", "Occupation", "Money Account", "Blood Type", "DNA Hash", "Fingerprint Hash", "Reset Access", "Delete Card Information")) + if("Name") + var/new_name = reject_bad_name(input(user,"What name would you like to put on this card?","Agent Card Name", ishuman(user) ? user.real_name : user.name)) + if(!Adjacent(user)) + return + src.registered_name = new_name + UpdateName() + to_chat(user, "Name changed to [new_name].") + RebuildHTML() + + if("Photo") + if(!Adjacent(user)) + return + var/job_clothes = null + if(assignment) + job_clothes = assignment + var/icon/newphoto = get_id_photo(user, job_clothes) + if(!newphoto) + return + photo = newphoto + to_chat(user, "Photo changed. Select another occupation and take a new photo if you wish to appear with different clothes.") + RebuildHTML() + + if("Appearance") + var/list/appearances = list( + "data", + "id", + "gold", + "silver", + "centcom", + "centcom_old", + "security", + "medical", + "HoS", + "research", + "cargo", + "engineering", + "CMO", + "RD", + "CE", + "clown", + "mime", + "rainbow", + "prisoner", + "syndie", + "deathsquad", + "commander", + "ERT_leader", + "ERT_security", + "ERT_engineering", + "ERT_medical", + "ERT_janitorial", + ) + var/choice = input(user, "Select the appearance for this card.", "Agent Card Appearance") in appearances + if(!Adjacent(user)) + return + if(!choice) + return + icon_state = choice + switch(choice) + if("silver") + desc = "A silver card which shows honour and dedication." + if("gold") + desc = "A golden card which shows power and might." + if("clown") + desc = "Even looking at the card strikes you with deep fear." + if("mime") + desc = "..." + if("prisoner") + desc = "You are a number, you are not a free man." + if("centcom") + desc = "An ID straight from Central Command." + else + desc = "A card used to provide ID and determine access across the station." + to_chat(usr, "Appearance changed to [choice].") + + if("Sex") + var/new_sex = sanitize(stripped_input(user,"What sex would you like to put on this card?","Agent Card Sex", ishuman(user) ? capitalize(user.gender) : "Male", MAX_MESSAGE_LEN)) + if(!Adjacent(user)) + return + sex = new_sex + to_chat(user, "Sex changed to [new_sex].") + RebuildHTML() + + if("Age") + var/default = "21" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + default = H.age + var/new_age = sanitize(input(user,"What age would you like to be written on this card?","Agent Card Age", default) as text) + if(!Adjacent(user)) + return + age = new_age + to_chat(user, "Age changed to [new_age].") + RebuildHTML() + + if("Occupation") + var/list/departments =list( + "Civilian", + "Engineering", + "Medical", + "Science", + "Security", + "Support", + "Command", + "Custom", + ) + + var/department = input(user, "What job would you like to put on this card?\nChoose a department or a custom job title.\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in departments + var/new_job = "Civilian" + + if(department == "Custom") + new_job = sanitize(stripped_input(user,"Choose a custom job title:","Agent Card Occupation", "Civilian", MAX_MESSAGE_LEN)) + else if(department != "Civilian") + switch(department) + if("Engineering") + new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in GLOB.engineering_positions + if("Medical") + new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in GLOB.medical_positions + if("Science") + new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in GLOB.science_positions + if("Security") + new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in GLOB.security_positions + if("Support") + new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in GLOB.support_positions + if("Command") + new_job = input(user, "What job would you like to put on this card?\nChanging occupation will not grant or remove any access levels.","Agent Card Occupation") in GLOB.command_positions + + if(!Adjacent(user)) + return + assignment = new_job + to_chat(user, "Occupation changed to [new_job].") + UpdateName() + RebuildHTML() + + if("Money Account") + var/new_account = input(user,"What money account would you like to link to this card?","Agent Card Account",12345) as num + if(!Adjacent(user)) + return + associated_account_number = new_account + to_chat(user, "Linked money account changed to [new_account].") + + if("Blood Type") + var/default = "\[UNSET\]" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.dna) + default = H.dna.blood_type + + var/new_blood_type = sanitize(input(user,"What blood type would you like to be written on this card?","Agent Card Blood Type",default) as text) + if(!Adjacent(user)) + return + blood_type = new_blood_type + to_chat(user, "Blood type changed to [new_blood_type].") + RebuildHTML() + + if("DNA Hash") + var/default = "\[UNSET\]" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.dna) + default = H.dna.unique_enzymes + + var/new_dna_hash = sanitize(input(user,"What DNA hash would you like to be written on this card?","Agent Card DNA Hash",default) as text) + if(!Adjacent(user)) + return + dna_hash = new_dna_hash + to_chat(user, "DNA hash changed to [new_dna_hash].") + RebuildHTML() + + if("Fingerprint Hash") + var/default = "\[UNSET\]" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.dna) + default = md5(H.dna.uni_identity) + + var/new_fingerprint_hash = sanitize(input(user,"What fingerprint hash would you like to be written on this card?","Agent Card Fingerprint Hash",default) as text) + if(!Adjacent(user)) + return + fingerprint_hash = new_fingerprint_hash + to_chat(user, "Fingerprint hash changed to [new_fingerprint_hash].") + RebuildHTML() + + if("Reset Access") + var/response = alert(user, "Are you sure you want to reset access saved on the card?","Reset Access", "No", "Yes") + if(response == "Yes") + access = initial_access.Copy() // Initial() doesn't work on lists + to_chat(user, "Card access reset.") + + if("Delete Card Information") + var/response = alert(user, "Are you sure you want to delete all information saved on the card?","Delete Card Information", "No", "Yes") + if(response == "Yes") + name = initial(name) + registered_name = initial(registered_name) + icon_state = initial(icon_state) + sex = initial(sex) + age = initial(age) + assignment = initial(assignment) + associated_account_number = initial(associated_account_number) + blood_type = initial(blood_type) + dna_hash = initial(dna_hash) + fingerprint_hash = initial(fingerprint_hash) + photo = null + registered_user = null + to_chat(user, "All information has been deleted from \the [src].") + RebuildHTML() + else + ..() + +/obj/item/card/id/syndicate_command + name = "syndicate ID card" + desc = "An ID straight from the Syndicate." + registered_name = "Syndicate" + icon_state = "syndie" + assignment = "Syndicate Overlord" + untrackable = 1 + access = list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND, ACCESS_EXTERNAL_AIRLOCKS) + +/obj/item/card/id/captains_spare + name = "captain's spare ID" + desc = "The spare ID of the captain." + icon_state = "gold" + item_state = "gold_id" + registered_name = "Captain" + assignment = "Captain" + +/obj/item/card/id/captains_spare/New() + var/datum/job/captain/J = new/datum/job/captain + access = J.get_access() + ..() + +/obj/item/card/id/admin + name = "admin ID card" + icon_state = "admin" + item_state = "gold_id" + registered_name = "Admin" + assignment = "Testing Shit" + untrackable = 1 + +/obj/item/card/id/admin/New() + access = get_absolutely_all_accesses() + ..() + +/obj/item/card/id/centcom + name = "central command ID card" + desc = "An ID straight from Central Command." + icon_state = "centcom" + registered_name = "Central Command" + assignment = "General" + +/obj/item/card/id/centcom/New() + access = get_all_centcom_access() + ..() + +/obj/item/card/id/nanotrasen + name = "nanotrasen ID card" + icon_state = "nanotrasen" + +/obj/item/card/id/prisoner + name = "prisoner ID card" + desc = "You are a number, you are not a free man." + icon_state = "prisoner" + item_state = "orange-id" + assignment = "Prisoner" + registered_name = "Scum" + var/goal = 0 //How far from freedom? + var/points = 0 + +/obj/item/card/id/prisoner/attack_self(mob/user as mob) + to_chat(usr, "You have accumulated [points] out of the [goal] points you need for freedom.") + +/obj/item/card/id/prisoner/one + name = "Prisoner #13-001" + registered_name = "Prisoner #13-001" + +/obj/item/card/id/prisoner/two + name = "Prisoner #13-002" + registered_name = "Prisoner #13-002" + +/obj/item/card/id/prisoner/three + name = "Prisoner #13-003" + registered_name = "Prisoner #13-003" + +/obj/item/card/id/prisoner/four + name = "Prisoner #13-004" + registered_name = "Prisoner #13-004" + +/obj/item/card/id/prisoner/five + name = "Prisoner #13-005" + registered_name = "Prisoner #13-005" + +/obj/item/card/id/prisoner/six + name = "Prisoner #13-006" + registered_name = "Prisoner #13-006" + +/obj/item/card/id/prisoner/seven + name = "Prisoner #13-007" + registered_name = "Prisoner #13-007" + +/obj/item/card/id/prisoner/random +/obj/item/card/id/prisoner/random/New() + ..() + var/random_number = "#[rand(0, 99)]-[rand(0, 999)]" + name = "Prisoner [random_number]" + registered_name = name + +/obj/item/card/id/salvage_captain + name = "Captain's ID" + registered_name = "Captain" + icon_state = "centcom" + desc = "Finders, keepers." + access = list(ACCESS_SALVAGE_CAPTAIN) + +/obj/item/card/id/medical + name = "Medical ID" + registered_name = "Medic" + icon_state = "medical" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + +/obj/item/card/id/security + name = "Security ID" + registered_name = "Officer" + icon_state = "security" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS) + +/obj/item/card/id/research + name = "Research ID" + registered_name = "Scientist" + icon_state = "research" + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINERAL_STOREROOM) + +/obj/item/card/id/supply + name = "Supply ID" + registered_name = "Cargonian" + icon_state = "cargo" + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + +/obj/item/card/id/engineering + name = "Engineering ID" + registered_name = "Engineer" + icon_state = "engineering" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS) + +/obj/item/card/id/hos + name = "Head of Security ID" + registered_name = "HoS" + icon_state = "HoS" + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, + ACCESS_FORENSICS_LOCKERS, ACCESS_PILOT, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS) + +/obj/item/card/id/cmo + name = "Chief Medical Officer ID" + registered_name = "CMO" + icon_state = "CMO" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + +/obj/item/card/id/rd + name = "Research Director ID" + registered_name = "RD" + icon_state = "RD" + access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MINERAL_STOREROOM) + +/obj/item/card/id/ce + name = "Chief Engineer ID" + registered_name = "CE" + icon_state = "CE" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + +/obj/item/card/id/clown + name = "Pink ID" + registered_name = "HONK!" + icon_state = "clown" + desc = "Even looking at the card strikes you with deep fear." + access = list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) + +/obj/item/card/id/mime + name = "Black and White ID" + registered_name = "..." + icon_state = "mime" + desc = "..." + access = list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) + +/obj/item/card/id/rainbow + name = "Rainbow ID" + icon_state = "rainbow" + +/obj/item/card/id/thunderdome/red + name = "Thunderdome Red ID" + registered_name = "Red Team Fighter" + assignment = "Red Team Fighter" + icon_state = "TDred" + desc = "This ID card is given to those who fought inside the thunderdome for the Red Team. Not many have lived to see one of those, even fewer lived to keep it." + +/obj/item/card/id/thunderdome/green + name = "Thunderdome Green ID" + registered_name = "Green Team Fighter" + assignment = "Green Team Fighter" + icon_state = "TDgreen" + desc = "This ID card is given to those who fought inside the thunderdome for the Green Team. Not many have lived to see one of those, even fewer lived to keep it." + +/obj/item/card/id/lifetime + name = "Lifetime ID Card" + desc = "A modified ID card given only to those people who have devoted their lives to the better interests of Nanotrasen. It sparkles blue." + icon_state = "lifetimeid" + +/obj/item/card/id/ert + name = "ERT ID" + icon_state = "ERT_empty" + +/obj/item/card/id/ert/commander + icon_state = "ERT_leader" +/obj/item/card/id/ert/security + icon_state = "ERT_security" +/obj/item/card/id/ert/engineering + icon_state = "ERT_engineering" +/obj/item/card/id/ert/medic + icon_state = "ERT_medical" + +/obj/item/card/id/golem + name = "Free Golem ID" + desc = "A card used to claim mining points and buy gear. Use it to mark it as yours." + icon_state = "research" + access = list(ACCESS_FREE_GOLEMS, ACCESS_ROBOTICS, ACCESS_CLOWN, ACCESS_MIME) //access to robots/mechs + var/registered = FALSE + +/obj/item/card/id/golem/attack_self(mob/user as mob) + if(!registered && ishuman(user)) + registered_name = user.real_name + SetOwnerInfo(user) + assignment = "Free Golem" + RebuildHTML() + UpdateName() + desc = "A card used to claim mining points and buy gear." + registered = TRUE + to_chat(user, "The ID is now registered as yours.") + else + ..() + +// Decals +/obj/item/id_decal + name = "identification card decal" + desc = "A nano-cellophane wrap that molds to an ID card to make it look snazzy." + icon = 'icons/obj/toy.dmi' + icon_state = "id_decal" + var/decal_name = "identification card" + var/decal_desc = "A card used to provide ID and determine access across the station." + var/decal_icon_state = "id" + var/decal_item_state = "card-id" + var/override_name = 0 + +/obj/item/id_decal/gold + name = "gold ID card decal" + icon_state = "id_decal_gold" + desc = "Make your ID look like the Captain's or a self-centered HOP's. Applies to any ID." + decal_desc = "A golden card which shows power and might." + decal_icon_state = "gold" + decal_item_state = "gold_id" + +/obj/item/id_decal/silver + name = "silver ID card decal" + icon_state = "id_decal_silver" + desc = "Make your ID look like HOP's because they wouldn't change it officially. Applies to any ID." + decal_desc = "A silver card which shows honour and dedication." + decal_icon_state = "silver" + decal_item_state = "silver_id" + +/obj/item/id_decal/prisoner + name = "prisoner ID card decal" + icon_state = "id_decal_prisoner" + desc = "All the cool kids have an ID that's this color. Applies to any ID." + decal_desc = "You are a number, you are not a free man." + decal_icon_state = "prisoner" + decal_item_state = "orange-id" + +/obj/item/id_decal/centcom + name = "centcom ID card decal" + icon_state = "id_decal_centcom" + desc = "All the prestige without the responsibility or the access. Applies to any ID." + decal_desc = "An ID straight from Cent. Com." + decal_icon_state = "centcom" + +/obj/item/id_decal/emag + name = "cryptographic sequencer ID card decal" + icon_state = "id_decal_emag" + desc = "A bundle of wires that you can tape to your ID to look very suspect. Applies to any ID." + decal_name = "cryptographic sequencer" + decal_desc = "It's a card with a magnetic strip attached to some circuitry." + decal_icon_state = "emag" + override_name = 1 + +/proc/get_station_card_skins() + return list("data","id","gold","silver","security","medical","research","cargo","engineering","HoS","CMO","RD","CE","clown","mime","rainbow","prisoner") + +/proc/get_centcom_card_skins() + return list("centcom","centcom_old","nanotrasen","ERT_leader","ERT_empty","ERT_security","ERT_engineering","ERT_medical","ERT_janitorial","deathsquad","commander","syndie","TDred","TDgreen") + +/proc/get_all_card_skins() + return get_station_card_skins() + get_centcom_card_skins() + +/proc/get_skin_desc(skin) + switch(skin) + if("id") + return "Standard" + if("cargo") + return "Supply" + if("HoS") + return "Head of Security" + if("CMO") + return "Chief Medical Officer" + if("RD") + return "Research Director" + if("CE") + return "Chief Engineer" + if("centcom_old") + return "Centcom Old" + if("ERT_leader") + return "ERT Leader" + if("ERT_empty") + return "ERT Default" + if("ERT_security") + return "ERT Security" + if("ERT_engineering") + return "ERT Engineering" + if("ERT_medical") + return "ERT Medical" + if("ERT_janitorial") + return "ERT Janitorial" + if("syndie") + return "Syndicate" + if("TDred") + return "Thunderdome Red" + if("TDgreen") + return "Thunderdome Green" + else + return capitalize(skin) diff --git a/code/game/objects/items/weapons/cash.dm b/code/game/objects/items/weapons/cash.dm index 84d7eab9e0b8..23aaeb27cbcb 100644 --- a/code/game/objects/items/weapons/cash.dm +++ b/code/game/objects/items/weapons/cash.dm @@ -56,4 +56,4 @@ amount = 1000 /obj/item/stack/spacecash/c1000000 - amount = 1000000 \ No newline at end of file + amount = 1000000 diff --git a/code/game/objects/items/weapons/chrono_eraser.dm b/code/game/objects/items/weapons/chrono_eraser.dm index 7680ef269dd2..91bb84f880bd 100644 --- a/code/game/objects/items/weapons/chrono_eraser.dm +++ b/code/game/objects/items/weapons/chrono_eraser.dm @@ -261,4 +261,4 @@ #undef CHRONO_BEAM_RANGE -#undef CHRONO_FRAME_COUNT \ No newline at end of file +#undef CHRONO_FRAME_COUNT diff --git a/code/game/objects/items/weapons/cigs.dm b/code/game/objects/items/weapons/cigs.dm index a2b193fc6082..24c904bc9d39 100644 --- a/code/game/objects/items/weapons/cigs.dm +++ b/code/game/objects/items/weapons/cigs.dm @@ -426,4 +426,4 @@ LIGHTERS ARE IN LIGHTERS.DM else to_chat(user, "You need to dry this first!") else - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm index 1143954faef5..f6fa5c26d5ab 100644 --- a/code/game/objects/items/weapons/clown_items.dm +++ b/code/game/objects/items/weapons/clown_items.dm @@ -1,58 +1,58 @@ -/* Clown Items - * Contains: - * Banana Peels - * Soap - * Bike Horns - */ - -/* - * Bike Horns - */ - -/obj/item/bikehorn - name = "bike horn" - desc = "A horn off of a bicycle." - icon = 'icons/obj/items.dmi' - icon_state = "bike_horn" - item_state = "bike_horn" - hitsound = null - throwforce = 3 - w_class = WEIGHT_CLASS_TINY - var/list/honk_sounds = list('sound/items/bikehorn.ogg' = 1) - throw_speed = 3 - throw_range = 15 - attack_verb = list("HONKED") - -/obj/item/bikehorn/Initialize() - . = ..() - AddComponent(/datum/component/squeak, honk_sounds, 50) - -/obj/item/bikehorn/airhorn - name = "air horn" - desc = "Damn son, where'd you find this?" - icon_state = "air_horn" - origin_tech = "materials=4;engineering=4" - honk_sounds = list('sound/items/airhorn2.ogg' = 1) - -/obj/item/bikehorn/golden - name = "golden bike horn" - desc = "Golden? Clearly, its made with bananium! Honk!" - icon_state = "gold_horn" - item_state = "gold_horn" - -/obj/item/bikehorn/golden/attack() - flip_mobs() - return ..() - -/obj/item/bikehorn/golden/attack_self(mob/user) - flip_mobs() - ..() - -/obj/item/bikehorn/golden/proc/flip_mobs(mob/living/carbon/M, mob/user) - var/turf/T = get_turf(src) - for(M in ohearers(7, T)) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(!H.can_hear()) - continue - M.emote("flip") +/* Clown Items + * Contains: + * Banana Peels + * Soap + * Bike Horns + */ + +/* + * Bike Horns + */ + +/obj/item/bikehorn + name = "bike horn" + desc = "A horn off of a bicycle." + icon = 'icons/obj/items.dmi' + icon_state = "bike_horn" + item_state = "bike_horn" + hitsound = null + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + var/list/honk_sounds = list('sound/items/bikehorn.ogg' = 1) + throw_speed = 3 + throw_range = 15 + attack_verb = list("HONKED") + +/obj/item/bikehorn/Initialize() + . = ..() + AddComponent(/datum/component/squeak, honk_sounds, 50) + +/obj/item/bikehorn/airhorn + name = "air horn" + desc = "Damn son, where'd you find this?" + icon_state = "air_horn" + origin_tech = "materials=4;engineering=4" + honk_sounds = list('sound/items/airhorn2.ogg' = 1) + +/obj/item/bikehorn/golden + name = "golden bike horn" + desc = "Golden? Clearly, its made with bananium! Honk!" + icon_state = "gold_horn" + item_state = "gold_horn" + +/obj/item/bikehorn/golden/attack() + flip_mobs() + return ..() + +/obj/item/bikehorn/golden/attack_self(mob/user) + flip_mobs() + ..() + +/obj/item/bikehorn/golden/proc/flip_mobs(mob/living/carbon/M, mob/user) + var/turf/T = get_turf(src) + for(M in ohearers(7, T)) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(!H.can_hear()) + continue + M.emote("flip") diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index b7aa96758917..8815ad6a20fd 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -1,180 +1,180 @@ -/obj/item/lipstick - name = "red lipstick" - desc = "A generic brand of lipstick." - icon = 'icons/obj/items.dmi' - icon_state = "lipstick" - w_class = WEIGHT_CLASS_TINY - var/colour = "red" - var/open = 0 - var/list/lipstick_colors = list( - "purple" = "purple", - "jade" = "#216F43", - "lime" = "lime", - "black" = "black", - "green" = "green", - "blue" = "blue", - "white" = "white") - - -/obj/item/lipstick/purple - name = "purple lipstick" - colour = "purple" - -/obj/item/lipstick/jade - name = "jade lipstick" - colour = "#216F43" - -/obj/item/lipstick/lime - name = "lime lipstick" - colour = "lime" - -/obj/item/lipstick/black - name = "black lipstick" - colour = "black" - -/obj/item/lipstick/green - name = "green lipstick" - colour = "green" - -/obj/item/lipstick/blue - name = "blue lipstick" - colour = "blue" - -/obj/item/lipstick/white - name = "white lipstick" - colour = "white" - -/obj/item/lipstick/random - name = "lipstick" - -/obj/item/lipstick/random/New() - ..() - var/lscolor = pick(lipstick_colors)//A random color is picked from the var defined initially in a new var. - colour = lipstick_colors[lscolor]//The color of the lipstick is pulled from the new variable (right hand side, HTML & Hex RGB) - name = "[lscolor] lipstick"//The new variable is also used to match the name to the color of the lipstick. Kudos to Desolate & Lemon - - -/obj/item/lipstick/attack_self(mob/user as mob) - overlays.Cut() - to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") - open = !open - if(open) - var/image/colored = image("icon"='icons/obj/items.dmi', "icon_state"="lipstick_uncap_color") - colored.color = colour - icon_state = "lipstick_uncap" - overlays += colored - else - icon_state = "lipstick" - -/obj/item/lipstick/attack(mob/M as mob, mob/user as mob) - if(!open) return - - if(!istype(M, /mob)) return - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.lip_style) //if they already have lipstick on - to_chat(user, "You need to wipe off the old lipstick first!") - return - if(H == user) - user.visible_message("[user] does [user.p_their()] lips with [src].", \ - "You take a moment to apply [src]. Perfect!") - H.lip_style = "lipstick" - H.lip_color = colour - H.update_body() - else - user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ - "You begin to apply \the [src].") - if(do_after(user, 20, target = H)) - user.visible_message("[user] does [H]'s lips with \the [src].", \ - "You apply \the [src].") - H.lip_style = "lipstick" - H.lip_color = colour - H.update_body() - else - to_chat(user, "Where are the lips on that?") - -/obj/item/razor - name = "electric razor" - desc = "The latest and greatest power razor born from the science of shaving." - icon = 'icons/obj/items.dmi' - icon_state = "razor" - flags = CONDUCT - w_class = WEIGHT_CLASS_TINY - usesound = 'sound/items/welder2.ogg' - toolspeed = 1 - -/obj/item/razor/attack(mob/living/carbon/M as mob, mob/user as mob) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/head/C = H.get_organ("head") - var/datum/robolimb/robohead = all_robolimbs[C.model] - if(user.zone_selected == "mouth") - if(!get_location_accessible(H, "mouth")) - to_chat(user, "The mask is in the way.") - return - if((C.dna.species.bodyflags & ALL_RPARTS) && robohead.is_monitor) //If the target is of a species that can have prosthetic heads, but the head doesn't support human hair 'wigs'... - to_chat(user, "You find yourself disappointed at the appalling lack of facial hair.") - return - if(C.f_style == "Shaved") - to_chat(user, "Already clean-shaven.") - return - if(H == user) //shaving yourself - user.visible_message("[user] starts to shave [user.p_their()] facial hair with [src].", \ - "You take a moment shave your facial hair with \the [src].") - if(do_after(user, 50 * toolspeed, target = H)) - user.visible_message("[user] shaves [user.p_their()] facial hair clean with [src].", \ - "You finish shaving with [src]. Fast and clean!") - C.f_style = "Shaved" - H.update_fhair() - playsound(src.loc, usesound, 20, 1) - else - var/turf/user_loc = user.loc - var/turf/H_loc = H.loc - user.visible_message("[user] tries to shave [H]'s facial hair with \the [src].", \ - "You start shaving [H]'s facial hair.") - if(do_after(user, 50 * toolspeed, target = H)) - if(user_loc == user.loc && H_loc == H.loc) - user.visible_message("[user] shaves off [H]'s facial hair with \the [src].", \ - "You shave [H]'s facial hair clean off.") - C.f_style = "Shaved" - H.update_fhair() - playsound(src.loc, usesound, 20, 1) - if(user.zone_selected == "head") - if(!get_location_accessible(H, "head")) - to_chat(user, "The headgear is in the way.") - return - if((C.dna.species.bodyflags & ALL_RPARTS) && robohead.is_monitor) //If the target is of a species that can have prosthetic heads, but the head doesn't support human hair 'wigs'... - to_chat(user, "You find yourself disappointed at the appalling lack of hair.") - return - if(C.h_style == "Bald" || C.h_style == "Balding Hair" || C.h_style == "Skinhead") - to_chat(user, "There is not enough hair left to shave...") - return - if(isskrell(M)) - to_chat(user, "Your razor isn't going to cut through tentacles.") - return - if(H == user) //shaving yourself - user.visible_message("[user] starts to shave [user.p_their()] head with [src].", \ - "You start to shave your head with \the [src].") - if(do_after(user, 50 * toolspeed, target = H)) - user.visible_message("[user] shaves [user.p_their()] head with [src].", \ - "You finish shaving with \the [src].") - C.h_style = "Skinhead" - H.update_hair() - playsound(src.loc, usesound, 40, 1) - else - var/turf/user_loc = user.loc - var/turf/H_loc = H.loc - user.visible_message("[user] tries to shave [H]'s head with \the [src]!", \ - "You start shaving [H]'s head.") - if(do_after(user, 50 * toolspeed, target = H)) - if(user_loc == user.loc && H_loc == H.loc) - user.visible_message("[user] shaves [H]'s head bald with \the [src]!", \ - "You shave [H]'s head bald.") - C.h_style = "Skinhead" - H.update_hair() - playsound(src.loc, usesound, 40, 1) - else - ..() - else - ..() +/obj/item/lipstick + name = "red lipstick" + desc = "A generic brand of lipstick." + icon = 'icons/obj/items.dmi' + icon_state = "lipstick" + w_class = WEIGHT_CLASS_TINY + var/colour = "red" + var/open = 0 + var/list/lipstick_colors = list( + "purple" = "purple", + "jade" = "#216F43", + "lime" = "lime", + "black" = "black", + "green" = "green", + "blue" = "blue", + "white" = "white") + + +/obj/item/lipstick/purple + name = "purple lipstick" + colour = "purple" + +/obj/item/lipstick/jade + name = "jade lipstick" + colour = "#216F43" + +/obj/item/lipstick/lime + name = "lime lipstick" + colour = "lime" + +/obj/item/lipstick/black + name = "black lipstick" + colour = "black" + +/obj/item/lipstick/green + name = "green lipstick" + colour = "green" + +/obj/item/lipstick/blue + name = "blue lipstick" + colour = "blue" + +/obj/item/lipstick/white + name = "white lipstick" + colour = "white" + +/obj/item/lipstick/random + name = "lipstick" + +/obj/item/lipstick/random/New() + ..() + var/lscolor = pick(lipstick_colors)//A random color is picked from the var defined initially in a new var. + colour = lipstick_colors[lscolor]//The color of the lipstick is pulled from the new variable (right hand side, HTML & Hex RGB) + name = "[lscolor] lipstick"//The new variable is also used to match the name to the color of the lipstick. Kudos to Desolate & Lemon + + +/obj/item/lipstick/attack_self(mob/user as mob) + overlays.Cut() + to_chat(user, "You twist \the [src] [open ? "closed" : "open"].") + open = !open + if(open) + var/image/colored = image("icon"='icons/obj/items.dmi', "icon_state"="lipstick_uncap_color") + colored.color = colour + icon_state = "lipstick_uncap" + overlays += colored + else + icon_state = "lipstick" + +/obj/item/lipstick/attack(mob/M as mob, mob/user as mob) + if(!open) return + + if(!istype(M, /mob)) return + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.lip_style) //if they already have lipstick on + to_chat(user, "You need to wipe off the old lipstick first!") + return + if(H == user) + user.visible_message("[user] does [user.p_their()] lips with [src].", \ + "You take a moment to apply [src]. Perfect!") + H.lip_style = "lipstick" + H.lip_color = colour + H.update_body() + else + user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ + "You begin to apply \the [src].") + if(do_after(user, 20, target = H)) + user.visible_message("[user] does [H]'s lips with \the [src].", \ + "You apply \the [src].") + H.lip_style = "lipstick" + H.lip_color = colour + H.update_body() + else + to_chat(user, "Where are the lips on that?") + +/obj/item/razor + name = "electric razor" + desc = "The latest and greatest power razor born from the science of shaving." + icon = 'icons/obj/items.dmi' + icon_state = "razor" + flags = CONDUCT + w_class = WEIGHT_CLASS_TINY + usesound = 'sound/items/welder2.ogg' + toolspeed = 1 + +/obj/item/razor/attack(mob/living/carbon/M as mob, mob/user as mob) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/head/C = H.get_organ("head") + var/datum/robolimb/robohead = GLOB.all_robolimbs[C.model] + if(user.zone_selected == "mouth") + if(!get_location_accessible(H, "mouth")) + to_chat(user, "The mask is in the way.") + return + if((C.dna.species.bodyflags & ALL_RPARTS) && robohead.is_monitor) //If the target is of a species that can have prosthetic heads, but the head doesn't support human hair 'wigs'... + to_chat(user, "You find yourself disappointed at the appalling lack of facial hair.") + return + if(C.f_style == "Shaved") + to_chat(user, "Already clean-shaven.") + return + if(H == user) //shaving yourself + user.visible_message("[user] starts to shave [user.p_their()] facial hair with [src].", \ + "You take a moment shave your facial hair with \the [src].") + if(do_after(user, 50 * toolspeed, target = H)) + user.visible_message("[user] shaves [user.p_their()] facial hair clean with [src].", \ + "You finish shaving with [src]. Fast and clean!") + C.f_style = "Shaved" + H.update_fhair() + playsound(src.loc, usesound, 20, 1) + else + var/turf/user_loc = user.loc + var/turf/H_loc = H.loc + user.visible_message("[user] tries to shave [H]'s facial hair with \the [src].", \ + "You start shaving [H]'s facial hair.") + if(do_after(user, 50 * toolspeed, target = H)) + if(user_loc == user.loc && H_loc == H.loc) + user.visible_message("[user] shaves off [H]'s facial hair with \the [src].", \ + "You shave [H]'s facial hair clean off.") + C.f_style = "Shaved" + H.update_fhair() + playsound(src.loc, usesound, 20, 1) + if(user.zone_selected == "head") + if(!get_location_accessible(H, "head")) + to_chat(user, "The headgear is in the way.") + return + if((C.dna.species.bodyflags & ALL_RPARTS) && robohead.is_monitor) //If the target is of a species that can have prosthetic heads, but the head doesn't support human hair 'wigs'... + to_chat(user, "You find yourself disappointed at the appalling lack of hair.") + return + if(C.h_style == "Bald" || C.h_style == "Balding Hair" || C.h_style == "Skinhead") + to_chat(user, "There is not enough hair left to shave...") + return + if(isskrell(M)) + to_chat(user, "Your razor isn't going to cut through tentacles.") + return + if(H == user) //shaving yourself + user.visible_message("[user] starts to shave [user.p_their()] head with [src].", \ + "You start to shave your head with \the [src].") + if(do_after(user, 50 * toolspeed, target = H)) + user.visible_message("[user] shaves [user.p_their()] head with [src].", \ + "You finish shaving with \the [src].") + C.h_style = "Skinhead" + H.update_hair() + playsound(src.loc, usesound, 40, 1) + else + var/turf/user_loc = user.loc + var/turf/H_loc = H.loc + user.visible_message("[user] tries to shave [H]'s head with \the [src]!", \ + "You start shaving [H]'s head.") + if(do_after(user, 50 * toolspeed, target = H)) + if(user_loc == user.loc && H_loc == H.loc) + user.visible_message("[user] shaves [H]'s head bald with \the [src]!", \ + "You shave [H]'s head bald.") + C.h_style = "Skinhead" + H.update_hair() + playsound(src.loc, usesound, 40, 1) + else + ..() + else + ..() diff --git a/code/game/objects/items/weapons/dice.dm b/code/game/objects/items/weapons/dice.dm index c480d8f450cf..bc6a98a9b1fb 100644 --- a/code/game/objects/items/weapons/dice.dm +++ b/code/game/objects/items/weapons/dice.dm @@ -1,213 +1,213 @@ -/obj/item/storage/pill_bottle/dice - name = "bag of dice" - desc = "Contains all the luck you'll ever need." - icon = 'icons/obj/dice.dmi' - icon_state = "dicebag" - can_hold = list(/obj/item/dice) - allow_wrap = FALSE - -/obj/item/storage/pill_bottle/dice/New() - ..() - var/special_die = pick("1","2","fudge","00","100") - if(special_die == "1") - new /obj/item/dice/d1(src) - if(special_die == "2") - new /obj/item/dice/d2(src) - new /obj/item/dice/d4(src) - new /obj/item/dice/d6(src) - if(special_die == "fudge") - new /obj/item/dice/fudge(src) - new /obj/item/dice/d8(src) - new /obj/item/dice/d10(src) - if(special_die == "00") - new /obj/item/dice/d00(src) - new /obj/item/dice/d12(src) - new /obj/item/dice/d20(src) - if(special_die == "100") - new /obj/item/dice/d100(src) - -/obj/item/storage/pill_bottle/dice/suicide_act(mob/user) - user.visible_message("[user] is gambling with death! It looks like [user.p_theyre()] trying to commit suicide!") - return (OXYLOSS) - -/obj/item/dice //depreciated d6, use /obj/item/dice/d6 if you actually want a d6 - name = "die" - desc = "A die with six sides. Basic and servicable." - icon = 'icons/obj/dice.dmi' - icon_state = "d6" - w_class = WEIGHT_CLASS_TINY - - var/sides = 6 - var/result = null - var/list/special_faces = list() //entries should match up to sides var if used - - var/rigged = DICE_NOT_RIGGED - var/rigged_value - -/obj/item/dice/Initialize(mapload) - . = ..() - if(!result) - result = roll(sides) - update_icon() - -/obj/item/dice/suicide_act(mob/user) - user.visible_message("[user] is gambling with death! It looks like [user.p_theyre()] trying to commit suicide!") - return (OXYLOSS) - -/obj/item/dice/d1 - name = "d1" - desc = "A die with one side. Deterministic!" - icon_state = "d1" - sides = 1 - -/obj/item/dice/d2 - name = "d2" - desc = "A die with two sides. Coins are undignified!" - icon_state = "d2" - sides = 2 - -/obj/item/dice/d4 - name = "d4" - desc = "A die with four sides. The nerd's caltrop." - icon_state = "d4" - sides = 4 - -/obj/item/dice/d4/Initialize(mapload) - . = ..() - AddComponent(/datum/component/caltrop, 1, 4) //1d4 damage - -/obj/item/dice/d6 - name = "d6" - -/obj/item/dice/fudge - name = "fudge die" - desc = "A die with six sides but only three results. Is this a plus or a minus? Your mind is drawing a blank..." - sides = 3 //shhh - icon_state = "fudge" - special_faces = list("minus","blank","plus") - -/obj/item/dice/d8 - name = "d8" - desc = "A die with eight sides. It feels... lucky." - icon_state = "d8" - sides = 8 - -/obj/item/dice/d10 - name = "d10" - desc = "A die with ten sides. Useful for percentages." - icon_state = "d10" - sides = 10 - -/obj/item/dice/d00 - name = "d00" - desc = "A die with ten sides. Works better for d100 rolls than a golfball." - icon_state = "d00" - sides = 10 - -/obj/item/dice/d12 - name = "d12" - desc = "A die with twelve sides. There's an air of neglect about it." - icon_state = "d12" - sides = 12 - -/obj/item/dice/d20 - name = "d20" - desc = "A die with twenty sides. The prefered die to throw at the GM." - icon_state = "d20" - sides = 20 - -/obj/item/dice/d100 - name = "d100" - desc = "A die with one hundred sides! Probably not fairly weighted..." - icon_state = "d100" - sides = 100 - -/obj/item/dice/d100/update_icon() - return - -/obj/item/dice/d20/e20 - var/triggered = 0 - -/obj/item/dice/attack_self(mob/user as mob) - diceroll(user) - -/obj/item/dice/throw_impact(atom/target) - diceroll(thrownby) - . = ..() - -/obj/item/dice/proc/diceroll(mob/user) - result = roll(sides) - if(rigged != DICE_NOT_RIGGED && result != rigged_value) - if(rigged == DICE_BASICALLY_RIGGED && prob(Clamp(1/(sides - 1) * 100, 25, 80))) - result = rigged_value - else if(rigged == DICE_TOTALLY_RIGGED) - result = rigged_value - - . = result - - var/fake_result = roll(sides)//Daredevil isn't as good as he used to be - var/comment = "" - if(sides == 20 && result == 20) - comment = "NAT 20!" - else if(sides == 20 && result == 1) - comment = "Ouch, bad luck." - update_icon() - if(initial(icon_state) == "d00") - result = (result - 1)*10 - if(special_faces.len == sides) - result = special_faces[result] - if(user != null) //Dice was rolled in someone's hand - user.visible_message("[user] has thrown [src]. It lands on [result]. [comment]", \ - "You throw [src]. It lands on [result]. [comment]", \ - "You hear [src] rolling, it sounds like a [fake_result].") - else if(!src.throwing) //Dice was thrown and is coming to rest - visible_message("[src] rolls to a stop, landing on [result]. [comment]") - -/obj/item/dice/d20/e20/diceroll(mob/user as mob, thrown) - if(triggered) - return - - . = ..() - - if(result == 1) - to_chat(user, "Rocks fall, you die.") - user.gib() - else - triggered = 1 - visible_message("You hear a quiet click.") - spawn(40) - - var/cap = 0 - if(result > MAX_EX_LIGHT_RANGE && result != 20) - cap = 1 - result = min(result, MAX_EX_LIGHT_RANGE) //Apply the bombcap - else if(result == 20) //Roll a nat 20, screw the bombcap - result = 24 - var/turf/epicenter = get_turf(src) - explosion(epicenter, round(result*0.25), round(result*0.5), round(result), round(result*1.5), 1, cap) - - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - investigate_log("E20 detonated at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with a roll of [result]. Triggered by: [key_name(user)]", INVESTIGATE_BOMB) - message_admins("E20 detonated at [A.name] (JMP) with a roll of [result]. Triggered by: [key_name_admin(user)]") - log_game("E20 detonated at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with a roll of [result]. Triggered by: [key_name(user)]") - - -/obj/item/dice/update_icon() - overlays.Cut() - overlays += "[icon_state][result]" - -/obj/item/storage/box/dice - name = "Box of dice" - desc = "ANOTHER ONE!? FUCK!" - icon_state = "box" - -/obj/item/storage/box/dice/New() - ..() - new /obj/item/dice/d2(src) - new /obj/item/dice/d4(src) - new /obj/item/dice/d8(src) - new /obj/item/dice/d10(src) - new /obj/item/dice/d00(src) - new /obj/item/dice/d12(src) - new /obj/item/dice/d20(src) +/obj/item/storage/pill_bottle/dice + name = "bag of dice" + desc = "Contains all the luck you'll ever need." + icon = 'icons/obj/dice.dmi' + icon_state = "dicebag" + can_hold = list(/obj/item/dice) + allow_wrap = FALSE + +/obj/item/storage/pill_bottle/dice/New() + ..() + var/special_die = pick("1","2","fudge","00","100") + if(special_die == "1") + new /obj/item/dice/d1(src) + if(special_die == "2") + new /obj/item/dice/d2(src) + new /obj/item/dice/d4(src) + new /obj/item/dice/d6(src) + if(special_die == "fudge") + new /obj/item/dice/fudge(src) + new /obj/item/dice/d8(src) + new /obj/item/dice/d10(src) + if(special_die == "00") + new /obj/item/dice/d00(src) + new /obj/item/dice/d12(src) + new /obj/item/dice/d20(src) + if(special_die == "100") + new /obj/item/dice/d100(src) + +/obj/item/storage/pill_bottle/dice/suicide_act(mob/user) + user.visible_message("[user] is gambling with death! It looks like [user.p_theyre()] trying to commit suicide!") + return (OXYLOSS) + +/obj/item/dice //depreciated d6, use /obj/item/dice/d6 if you actually want a d6 + name = "die" + desc = "A die with six sides. Basic and servicable." + icon = 'icons/obj/dice.dmi' + icon_state = "d6" + w_class = WEIGHT_CLASS_TINY + + var/sides = 6 + var/result = null + var/list/special_faces = list() //entries should match up to sides var if used + + var/rigged = DICE_NOT_RIGGED + var/rigged_value + +/obj/item/dice/Initialize(mapload) + . = ..() + if(!result) + result = roll(sides) + update_icon() + +/obj/item/dice/suicide_act(mob/user) + user.visible_message("[user] is gambling with death! It looks like [user.p_theyre()] trying to commit suicide!") + return (OXYLOSS) + +/obj/item/dice/d1 + name = "d1" + desc = "A die with one side. Deterministic!" + icon_state = "d1" + sides = 1 + +/obj/item/dice/d2 + name = "d2" + desc = "A die with two sides. Coins are undignified!" + icon_state = "d2" + sides = 2 + +/obj/item/dice/d4 + name = "d4" + desc = "A die with four sides. The nerd's caltrop." + icon_state = "d4" + sides = 4 + +/obj/item/dice/d4/Initialize(mapload) + . = ..() + AddComponent(/datum/component/caltrop, 1, 4) //1d4 damage + +/obj/item/dice/d6 + name = "d6" + +/obj/item/dice/fudge + name = "fudge die" + desc = "A die with six sides but only three results. Is this a plus or a minus? Your mind is drawing a blank..." + sides = 3 //shhh + icon_state = "fudge" + special_faces = list("minus","blank","plus") + +/obj/item/dice/d8 + name = "d8" + desc = "A die with eight sides. It feels... lucky." + icon_state = "d8" + sides = 8 + +/obj/item/dice/d10 + name = "d10" + desc = "A die with ten sides. Useful for percentages." + icon_state = "d10" + sides = 10 + +/obj/item/dice/d00 + name = "d00" + desc = "A die with ten sides. Works better for d100 rolls than a golfball." + icon_state = "d00" + sides = 10 + +/obj/item/dice/d12 + name = "d12" + desc = "A die with twelve sides. There's an air of neglect about it." + icon_state = "d12" + sides = 12 + +/obj/item/dice/d20 + name = "d20" + desc = "A die with twenty sides. The prefered die to throw at the GM." + icon_state = "d20" + sides = 20 + +/obj/item/dice/d100 + name = "d100" + desc = "A die with one hundred sides! Probably not fairly weighted..." + icon_state = "d100" + sides = 100 + +/obj/item/dice/d100/update_icon() + return + +/obj/item/dice/d20/e20 + var/triggered = 0 + +/obj/item/dice/attack_self(mob/user as mob) + diceroll(user) + +/obj/item/dice/throw_impact(atom/target) + diceroll(thrownby) + . = ..() + +/obj/item/dice/proc/diceroll(mob/user) + result = roll(sides) + if(rigged != DICE_NOT_RIGGED && result != rigged_value) + if(rigged == DICE_BASICALLY_RIGGED && prob(Clamp(1/(sides - 1) * 100, 25, 80))) + result = rigged_value + else if(rigged == DICE_TOTALLY_RIGGED) + result = rigged_value + + . = result + + var/fake_result = roll(sides)//Daredevil isn't as good as he used to be + var/comment = "" + if(sides == 20 && result == 20) + comment = "NAT 20!" + else if(sides == 20 && result == 1) + comment = "Ouch, bad luck." + update_icon() + if(initial(icon_state) == "d00") + result = (result - 1)*10 + if(special_faces.len == sides) + result = special_faces[result] + if(user != null) //Dice was rolled in someone's hand + user.visible_message("[user] has thrown [src]. It lands on [result]. [comment]", \ + "You throw [src]. It lands on [result]. [comment]", \ + "You hear [src] rolling, it sounds like a [fake_result].") + else if(!src.throwing) //Dice was thrown and is coming to rest + visible_message("[src] rolls to a stop, landing on [result]. [comment]") + +/obj/item/dice/d20/e20/diceroll(mob/user as mob, thrown) + if(triggered) + return + + . = ..() + + if(result == 1) + to_chat(user, "Rocks fall, you die.") + user.gib() + else + triggered = 1 + visible_message("You hear a quiet click.") + spawn(40) + + var/cap = 0 + if(result > GLOB.max_ex_light_range && result != 20) + cap = 1 + result = min(result, GLOB.max_ex_light_range) //Apply the bombcap + else if(result == 20) //Roll a nat 20, screw the bombcap + result = 24 + var/turf/epicenter = get_turf(src) + explosion(epicenter, round(result*0.25), round(result*0.5), round(result), round(result*1.5), 1, cap) + + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + investigate_log("E20 detonated at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with a roll of [result]. Triggered by: [key_name(user)]", INVESTIGATE_BOMB) + message_admins("E20 detonated at [A.name] (JMP) with a roll of [result]. Triggered by: [key_name_admin(user)]") + log_game("E20 detonated at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) with a roll of [result]. Triggered by: [key_name(user)]") + + +/obj/item/dice/update_icon() + overlays.Cut() + overlays += "[icon_state][result]" + +/obj/item/storage/box/dice + name = "Box of dice" + desc = "ANOTHER ONE!? FUCK!" + icon_state = "box" + +/obj/item/storage/box/dice/New() + ..() + new /obj/item/dice/d2(src) + new /obj/item/dice/d4(src) + new /obj/item/dice/d8(src) + new /obj/item/dice/d10(src) + new /obj/item/dice/d00(src) + new /obj/item/dice/d12(src) + new /obj/item/dice/d20(src) diff --git a/code/game/objects/items/weapons/dna_injector.dm b/code/game/objects/items/weapons/dna_injector.dm index c7627688b5e8..8f11f65fe712 100644 --- a/code/game/objects/items/weapons/dna_injector.dm +++ b/code/game/objects/items/weapons/dna_injector.dm @@ -1,694 +1,694 @@ -/obj/item/dnainjector - name = "DNA-Injector" - desc = "This injects the person with DNA." - icon = 'icons/obj/items.dmi' - icon_state = "dnainjector" - item_state = "dnainjector" - var/block = 0 - var/datum/dna2/record/buf = null - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - origin_tech = "biotech=1" - - var/damage_coeff = 1 - var/used = FALSE - - // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. - var/datatype = 0 - var/value = 0 - var/forcedmutation = FALSE //Will it give the mutation, guaranteed? - -/obj/item/dnainjector/Initialize() - . = ..() - if(datatype && block) - buf = new - buf.dna = new - buf.types = datatype - buf.dna.ResetSE() - SetValue(value) - -/obj/item/dnainjector/Destroy() - QDEL_NULL(buf) - return ..() - -/obj/item/dnainjector/proc/GetRealBlock(selblock) - if(selblock == 0) - return block - else - return selblock - -/obj/item/dnainjector/proc/GetState(selblock = 0) - var/real_block = GetRealBlock(selblock) - if(buf.types & DNA2_BUF_SE) - return buf.dna.GetSEState(real_block) - else - return buf.dna.GetUIState(real_block) - -/obj/item/dnainjector/proc/SetState(on, selblock = 0) - var/real_block = GetRealBlock(selblock) - if(buf.types & DNA2_BUF_SE) - return buf.dna.SetSEState(real_block,on) - else - return buf.dna.SetUIState(real_block,on) - -/obj/item/dnainjector/proc/GetValue(selblock = 0) - var/real_block = GetRealBlock(selblock) - if(buf.types & DNA2_BUF_SE) - return buf.dna.GetSEValue(real_block) - else - return buf.dna.GetUIValue(real_block) - -/obj/item/dnainjector/proc/SetValue(val, selblock = 0) - var/real_block = GetRealBlock(selblock) - if(buf.types & DNA2_BUF_SE) - return buf.dna.SetSEValue(real_block,val) - else - return buf.dna.SetUIValue(real_block,val) - -/obj/item/dnainjector/proc/inject(mob/living/M, mob/user) - if(used) - return - if(istype(M,/mob/living)) - M.apply_effect(rand(20 / (damage_coeff ** 2), 50 / (damage_coeff ** 2)), IRRADIATE, 0, 1) - var/mob/living/carbon/human/H - if(istype(M, /mob/living/carbon/human)) - H = M - - if(!buf) - log_runtime(EXCEPTION("[src] used by [user] on [M] failed to initialize properly."), src) - return - - spawn(0) //Some mutations have sleeps in them, like monkey - if(!(NOCLONE in M.mutations) && !(H && (NO_DNA in H.dna.species.species_traits))) // prevents drained people from having their DNA changed - var/prev_ue = M.dna.unique_enzymes - // UI in syringe. - if(buf.types & DNA2_BUF_UI) - if(!block) //isolated block? - M.dna.UI = buf.dna.UI.Copy() - M.dna.UpdateUI() - M.UpdateAppearance() - if(buf.types & DNA2_BUF_UE) //unique enzymes? yes - - M.real_name = buf.dna.real_name - M.name = buf.dna.real_name - M.dna.real_name = buf.dna.real_name - M.dna.unique_enzymes = buf.dna.unique_enzymes - else - M.dna.SetUIValue(block,src.GetValue()) - M.UpdateAppearance() - if(buf.types & DNA2_BUF_SE) - if(!block) //isolated block? - M.dna.SE = buf.dna.SE.Copy() - M.dna.UpdateSE() - else - M.dna.SetSEValue(block,src.GetValue()) - domutcheck(M, null, forcedmutation ? MUTCHK_FORCED : 0) - M.update_mutations() - if(H) - H.sync_organ_dna(assimilate = 0, old_ue = prev_ue) - -/obj/item/dnainjector/attack(mob/M, mob/user) - if(used) - to_chat(user, "This injector is used up!") - return - if(!M.dna) //You know what would be nice? If the mob you're injecting has DNA, and so doesn't cause runtimes. - return FALSE - - if(ishuman(M)) // Would've done this via species instead of type, but the basic mob doesn't have a species, go figure. - var/mob/living/carbon/human/H = M - if(NO_DNA in H.dna.species.species_traits) - return FALSE - - if(!user.IsAdvancedToolUser()) - return FALSE - - var/attack_log = "injected with the Isolated [name]" - - if(buf && buf.types & DNA2_BUF_SE) - if(block) - if(GetState() && block == MONKEYBLOCK && ishuman(M)) - attack_log = "injected with the Isolated [name] (MONKEY)" - message_admins("[key_name_admin(user)] injected [key_name_admin(M)] with the Isolated [name] (MONKEY)") - - else - if(GetState(MONKEYBLOCK) && ishuman(M)) - attack_log = "injected with the Isolated [name] (MONKEY)" - message_admins("[key_name_admin(user)] injected [key_name_admin(M)] with the Isolated [name] (MONKEY)") - - - if(M != user) - M.visible_message("[user] is trying to inject [M] with [src]!", "[user] is trying to inject [M] with [src]!") - if(!do_mob(user, M)) - return - M.visible_message("[user] injects [M] with the syringe with [src]!", \ - "[user] injects [M] with the syringe with [src]!") - else - to_chat(user, "You inject yourself with [src].") - - add_attack_logs(user, M, attack_log, ATKLOG_ALL) - if(!iscarbon(user)) - M.LAssailant = null - else - M.LAssailant = user - - inject(M, user) - used = TRUE - icon_state = "dnainjector0" - desc += " This one is used up." - -/obj/item/dnainjector/hulkmut - name = "DNA-Injector (Hulk)" - desc = "This will make you big and strong, but give you a bad skin condition." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/hulkmut/Initialize() - block = HULKBLOCK - ..() - -/obj/item/dnainjector/antihulk - name = "DNA-Injector (Anti-Hulk)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antihulk/Initialize() - block = HULKBLOCK - ..() - -/obj/item/dnainjector/xraymut - name = "DNA-Injector (Xray)" - desc = "Finally you can see what the Captain does." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/xraymut/Initialize() - block = XRAYBLOCK - ..() - -/obj/item/dnainjector/antixray - name = "DNA-Injector (Anti-Xray)" - desc = "It will make you see harder." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antixray/Initialize() - block = XRAYBLOCK - ..() - -/obj/item/dnainjector/firemut - name = "DNA-Injector (Fire)" - desc = "Gives you fire." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/firemut/Initialize() - block = FIREBLOCK - ..() - -/obj/item/dnainjector/antifire - name = "DNA-Injector (Anti-Fire)" - desc = "Cures fire." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antifire/Initialize() - block = FIREBLOCK - ..() - -/obj/item/dnainjector/telemut - name = "DNA-Injector (Tele.)" - desc = "Super brain man!" - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/telemut/Initialize() - block = TELEBLOCK - ..() - -/obj/item/dnainjector/telemut/darkbundle - name = "DNA injector" - desc = "Good. Let the hate flow through you." - - -/obj/item/dnainjector/antitele - name = "DNA-Injector (Anti-Tele.)" - desc = "Will make you not able to control your mind." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antitele/Initialize() - block = TELEBLOCK - ..() - -/obj/item/dnainjector/nobreath - name = "DNA-Injector (Breathless)" - desc = "Hold your breath and count to infinity." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/nobreath/Initialize() - block = BREATHLESSBLOCK - ..() - -/obj/item/dnainjector/antinobreath - name = "DNA-Injector (Anti-Breathless)" - desc = "Hold your breath and count to 100." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antinobreath/Initialize() - block = BREATHLESSBLOCK - ..() - -/obj/item/dnainjector/remoteview - name = "DNA-Injector (Remote View)" - desc = "Stare into the distance for a reason." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/remoteview/Initialize() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/dnainjector/antiremoteview - name = "DNA-Injector (Anti-Remote View)" - desc = "Cures green skin." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antiremoteview/Initialize() - block = REMOTEVIEWBLOCK - ..() - -/obj/item/dnainjector/regenerate - name = "DNA-Injector (Regeneration)" - desc = "Healthy but hungry." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/regenerate/Initialize() - block = REGENERATEBLOCK - ..() - -/obj/item/dnainjector/antiregenerate - name = "DNA-Injector (Anti-Regeneration)" - desc = "Sickly but sated." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antiregenerate/Initialize() - block = REGENERATEBLOCK - ..() - -/obj/item/dnainjector/runfast - name = "DNA-Injector (Increase Run)" - desc = "Running Man." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/runfast/Initialize() - block = INCREASERUNBLOCK - ..() - -/obj/item/dnainjector/antirunfast - name = "DNA-Injector (Anti-Increase Run)" - desc = "Walking Man." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antirunfast/Initialize() - block = INCREASERUNBLOCK - ..() - -/obj/item/dnainjector/morph - name = "DNA-Injector (Morph)" - desc = "A total makeover." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/morph/Initialize() - block = MORPHBLOCK - ..() - -/obj/item/dnainjector/antimorph - name = "DNA-Injector (Anti-Morph)" - desc = "Cures identity crisis." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antimorph/Initialize() - block = MORPHBLOCK - ..() - -/obj/item/dnainjector/noprints - name = "DNA-Injector (No Prints)" - desc = "Better than a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/noprints/Initialize() - block = NOPRINTSBLOCK - ..() - -/obj/item/dnainjector/antinoprints - name = "DNA-Injector (Anti-No Prints)" - desc = "Not quite as good as a pair of budget insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antinoprints/Initialize() - block = NOPRINTSBLOCK - ..() - -/obj/item/dnainjector/insulation - name = "DNA-Injector (Shock Immunity)" - desc = "Better than a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/insulation/Initialize() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/dnainjector/antiinsulation - name = "DNA-Injector (Anti-Shock Immunity)" - desc = "Not quite as good as a pair of real insulated gloves." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antiinsulation/Initialize() - block = SHOCKIMMUNITYBLOCK - ..() - -/obj/item/dnainjector/midgit - name = "DNA-Injector (Small Size)" - desc = "Makes you shrink." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/midgit/Initialize() - block = SMALLSIZEBLOCK - ..() - -/obj/item/dnainjector/antimidgit - name = "DNA-Injector (Anti-Small Size)" - desc = "Makes you grow. But not too much." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antimidgit/Initialize() - block = SMALLSIZEBLOCK - ..() - -///////////////////////////////////// -/obj/item/dnainjector/antiglasses - name = "DNA-Injector (Anti-Glasses)" - desc = "Toss away those glasses!" - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antiglasses/Initialize() - block = GLASSESBLOCK - ..() - -/obj/item/dnainjector/glassesmut - name = "DNA-Injector (Glasses)" - desc = "Will make you need dorkish glasses." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/glassesmut/Initialize() - block = GLASSESBLOCK - ..() - -/obj/item/dnainjector/epimut - name = "DNA-Injector (Epi.)" - desc = "Shake shake shake the room!" - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/epimut/Initialize() - block = EPILEPSYBLOCK - ..() - -/obj/item/dnainjector/antiepi - name = "DNA-Injector (Anti-Epi.)" - desc = "Will fix you up from shaking the room." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antiepi/Initialize() - block = EPILEPSYBLOCK - ..() - -/obj/item/dnainjector/anticough - name = "DNA-Injector (Anti-Cough)" - desc = "Will stop that awful noise." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/anticough/Initialize() - block = COUGHBLOCK - ..() - -/obj/item/dnainjector/coughmut - name = "DNA-Injector (Cough)" - desc = "Will bring forth a sound of horror from your throat." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/coughmut/Initialize() - block = COUGHBLOCK - ..() - -/obj/item/dnainjector/clumsymut - name = "DNA-Injector (Clumsy)" - desc = "Makes clumsy minions." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/clumsymut/Initialize() - block = CLUMSYBLOCK - ..() - -/obj/item/dnainjector/anticlumsy - name = "DNA-Injector (Anti-Clumy)" - desc = "Cleans up confusion." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/anticlumsy/Initialize() - block = CLUMSYBLOCK - ..() - -/obj/item/dnainjector/antitour - name = "DNA-Injector (Anti-Tour.)" - desc = "Will cure tourrets." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antitour/Initialize() - block = TWITCHBLOCK - ..() - -/obj/item/dnainjector/tourmut - name = "DNA-Injector (Tour.)" - desc = "Gives you a nasty case off tourrets." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/tourmut/Initialize() - block = TWITCHBLOCK - ..() - -/obj/item/dnainjector/stuttmut - name = "DNA-Injector (Stutt.)" - desc = "Makes you s-s-stuttterrr" - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/stuttmut/Initialize() - block = NERVOUSBLOCK - ..() - - -/obj/item/dnainjector/antistutt - name = "DNA-Injector (Anti-Stutt.)" - desc = "Fixes that speaking impairment." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antistutt/Initialize() - block = NERVOUSBLOCK - ..() - -/obj/item/dnainjector/blindmut - name = "DNA-Injector (Blind)" - desc = "Makes you not see anything." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/blindmut/Initialize() - block = BLINDBLOCK - ..() - -/obj/item/dnainjector/antiblind - name = "DNA-Injector (Anti-Blind)" - desc = "ITS A MIRACLE!!!" - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antiblind/Initialize() - block = BLINDBLOCK - ..() - -/obj/item/dnainjector/telemut - name = "DNA-Injector (Tele.)" - desc = "Super brain man!" - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/telemut/Initialize() - block = TELEBLOCK - ..() - -/obj/item/dnainjector/antitele - name = "DNA-Injector (Anti-Tele.)" - desc = "Will make you not able to control your mind." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antitele/Initialize() - block = TELEBLOCK - ..() - -/obj/item/dnainjector/deafmut - name = "DNA-Injector (Deaf)" - desc = "Sorry, what did you say?" - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/deafmut/Initialize() - block = DEAFBLOCK - ..() - -/obj/item/dnainjector/antideaf - name = "DNA-Injector (Anti-Deaf)" - desc = "Will make you hear once more." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antideaf/Initialize() - block = DEAFBLOCK - ..() - -/obj/item/dnainjector/hallucination - name = "DNA-Injector (Halluctination)" - desc = "What you see isn't always what you get." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/hallucination/Initialize() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/dnainjector/antihallucination - name = "DNA-Injector (Anti-Hallucination)" - desc = "What you see is what you get." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/antihallucination/Initialize() - block = HALLUCINATIONBLOCK - ..() - -/obj/item/dnainjector/h2m - name = "DNA-Injector (Human > Monkey)" - desc = "Will make you a flea bag." - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/h2m/Initialize() - block = MONKEYBLOCK - ..() - -/obj/item/dnainjector/m2h - name = "DNA-Injector (Monkey > Human)" - desc = "Will make you...less hairy." - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/m2h/Initialize() - block = MONKEYBLOCK - ..() - - -/obj/item/dnainjector/comic - name = "DNA-Injector (Comic)" - desc = "Honk!" - datatype = DNA2_BUF_SE - value = 0xFFF - forcedmutation = TRUE - -/obj/item/dnainjector/comic/Initialize() - block = COMICBLOCK - ..() - -/obj/item/dnainjector/anticomic - name = "DNA-Injector (Ant-Comic)" - desc = "Honk...?" - datatype = DNA2_BUF_SE - value = 0x001 - forcedmutation = TRUE - -/obj/item/dnainjector/anticomic/Initialize() - block = COMICBLOCK - ..() \ No newline at end of file +/obj/item/dnainjector + name = "DNA-Injector" + desc = "This injects the person with DNA." + icon = 'icons/obj/items.dmi' + icon_state = "dnainjector" + item_state = "dnainjector" + var/block = 0 + var/datum/dna2/record/buf = null + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + origin_tech = "biotech=1" + + var/damage_coeff = 1 + var/used = FALSE + + // USE ONLY IN PREMADE SYRINGES. WILL NOT WORK OTHERWISE. + var/datatype = 0 + var/value = 0 + var/forcedmutation = FALSE //Will it give the mutation, guaranteed? + +/obj/item/dnainjector/Initialize() + . = ..() + if(datatype && block) + buf = new + buf.dna = new + buf.types = datatype + buf.dna.ResetSE() + SetValue(value) + +/obj/item/dnainjector/Destroy() + QDEL_NULL(buf) + return ..() + +/obj/item/dnainjector/proc/GetRealBlock(selblock) + if(selblock == 0) + return block + else + return selblock + +/obj/item/dnainjector/proc/GetState(selblock = 0) + var/real_block = GetRealBlock(selblock) + if(buf.types & DNA2_BUF_SE) + return buf.dna.GetSEState(real_block) + else + return buf.dna.GetUIState(real_block) + +/obj/item/dnainjector/proc/SetState(on, selblock = 0) + var/real_block = GetRealBlock(selblock) + if(buf.types & DNA2_BUF_SE) + return buf.dna.SetSEState(real_block,on) + else + return buf.dna.SetUIState(real_block,on) + +/obj/item/dnainjector/proc/GetValue(selblock = 0) + var/real_block = GetRealBlock(selblock) + if(buf.types & DNA2_BUF_SE) + return buf.dna.GetSEValue(real_block) + else + return buf.dna.GetUIValue(real_block) + +/obj/item/dnainjector/proc/SetValue(val, selblock = 0) + var/real_block = GetRealBlock(selblock) + if(buf.types & DNA2_BUF_SE) + return buf.dna.SetSEValue(real_block,val) + else + return buf.dna.SetUIValue(real_block,val) + +/obj/item/dnainjector/proc/inject(mob/living/M, mob/user) + if(used) + return + if(istype(M,/mob/living)) + M.apply_effect(rand(20 / (damage_coeff ** 2), 50 / (damage_coeff ** 2)), IRRADIATE, 0, 1) + var/mob/living/carbon/human/H + if(istype(M, /mob/living/carbon/human)) + H = M + + if(!buf) + log_runtime(EXCEPTION("[src] used by [user] on [M] failed to initialize properly."), src) + return + + spawn(0) //Some mutations have sleeps in them, like monkey + if(!(NOCLONE in M.mutations) && !(H && (NO_DNA in H.dna.species.species_traits))) // prevents drained people from having their DNA changed + var/prev_ue = M.dna.unique_enzymes + // UI in syringe. + if(buf.types & DNA2_BUF_UI) + if(!block) //isolated block? + M.dna.UI = buf.dna.UI.Copy() + M.dna.UpdateUI() + M.UpdateAppearance() + if(buf.types & DNA2_BUF_UE) //unique enzymes? yes + + M.real_name = buf.dna.real_name + M.name = buf.dna.real_name + M.dna.real_name = buf.dna.real_name + M.dna.unique_enzymes = buf.dna.unique_enzymes + else + M.dna.SetUIValue(block,src.GetValue()) + M.UpdateAppearance() + if(buf.types & DNA2_BUF_SE) + if(!block) //isolated block? + M.dna.SE = buf.dna.SE.Copy() + M.dna.UpdateSE() + else + M.dna.SetSEValue(block,src.GetValue()) + domutcheck(M, null, forcedmutation ? MUTCHK_FORCED : 0) + M.update_mutations() + if(H) + H.sync_organ_dna(assimilate = 0, old_ue = prev_ue) + +/obj/item/dnainjector/attack(mob/M, mob/user) + if(used) + to_chat(user, "This injector is used up!") + return + if(!M.dna) //You know what would be nice? If the mob you're injecting has DNA, and so doesn't cause runtimes. + return FALSE + + if(ishuman(M)) // Would've done this via species instead of type, but the basic mob doesn't have a species, go figure. + var/mob/living/carbon/human/H = M + if(NO_DNA in H.dna.species.species_traits) + return FALSE + + if(!user.IsAdvancedToolUser()) + return FALSE + + var/attack_log = "injected with the Isolated [name]" + + if(buf && buf.types & DNA2_BUF_SE) + if(block) + if(GetState() && block == GLOB.monkeyblock && ishuman(M)) + attack_log = "injected with the Isolated [name] (MONKEY)" + message_admins("[key_name_admin(user)] injected [key_name_admin(M)] with the Isolated [name] (MONKEY)") + + else + if(GetState(GLOB.monkeyblock) && ishuman(M)) + attack_log = "injected with the Isolated [name] (MONKEY)" + message_admins("[key_name_admin(user)] injected [key_name_admin(M)] with the Isolated [name] (MONKEY)") + + + if(M != user) + M.visible_message("[user] is trying to inject [M] with [src]!", "[user] is trying to inject [M] with [src]!") + if(!do_mob(user, M)) + return + M.visible_message("[user] injects [M] with the syringe with [src]!", \ + "[user] injects [M] with the syringe with [src]!") + else + to_chat(user, "You inject yourself with [src].") + + add_attack_logs(user, M, attack_log, ATKLOG_ALL) + if(!iscarbon(user)) + M.LAssailant = null + else + M.LAssailant = user + + inject(M, user) + used = TRUE + icon_state = "dnainjector0" + desc += " This one is used up." + +/obj/item/dnainjector/hulkmut + name = "DNA-Injector (Hulk)" + desc = "This will make you big and strong, but give you a bad skin condition." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/hulkmut/Initialize() + block = GLOB.hulkblock + ..() + +/obj/item/dnainjector/antihulk + name = "DNA-Injector (Anti-Hulk)" + desc = "Cures green skin." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antihulk/Initialize() + block = GLOB.hulkblock + ..() + +/obj/item/dnainjector/xraymut + name = "DNA-Injector (Xray)" + desc = "Finally you can see what the Captain does." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/xraymut/Initialize() + block = GLOB.xrayblock + ..() + +/obj/item/dnainjector/antixray + name = "DNA-Injector (Anti-Xray)" + desc = "It will make you see harder." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antixray/Initialize() + block = GLOB.xrayblock + ..() + +/obj/item/dnainjector/firemut + name = "DNA-Injector (Fire)" + desc = "Gives you fire." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/firemut/Initialize() + block = GLOB.fireblock + ..() + +/obj/item/dnainjector/antifire + name = "DNA-Injector (Anti-Fire)" + desc = "Cures fire." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antifire/Initialize() + block = GLOB.fireblock + ..() + +/obj/item/dnainjector/telemut + name = "DNA-Injector (Tele.)" + desc = "Super brain man!" + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/telemut/Initialize() + block = GLOB.teleblock + ..() + +/obj/item/dnainjector/telemut/darkbundle + name = "DNA injector" + desc = "Good. Let the hate flow through you." + + +/obj/item/dnainjector/antitele + name = "DNA-Injector (Anti-Tele.)" + desc = "Will make you not able to control your mind." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antitele/Initialize() + block = GLOB.teleblock + ..() + +/obj/item/dnainjector/nobreath + name = "DNA-Injector (Breathless)" + desc = "Hold your breath and count to infinity." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/nobreath/Initialize() + block = GLOB.breathlessblock + ..() + +/obj/item/dnainjector/antinobreath + name = "DNA-Injector (Anti-Breathless)" + desc = "Hold your breath and count to 100." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antinobreath/Initialize() + block = GLOB.breathlessblock + ..() + +/obj/item/dnainjector/remoteview + name = "DNA-Injector (Remote View)" + desc = "Stare into the distance for a reason." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/remoteview/Initialize() + block = GLOB.remoteviewblock + ..() + +/obj/item/dnainjector/antiremoteview + name = "DNA-Injector (Anti-Remote View)" + desc = "Cures green skin." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antiremoteview/Initialize() + block = GLOB.remoteviewblock + ..() + +/obj/item/dnainjector/regenerate + name = "DNA-Injector (Regeneration)" + desc = "Healthy but hungry." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/regenerate/Initialize() + block = GLOB.regenerateblock + ..() + +/obj/item/dnainjector/antiregenerate + name = "DNA-Injector (Anti-Regeneration)" + desc = "Sickly but sated." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antiregenerate/Initialize() + block = GLOB.regenerateblock + ..() + +/obj/item/dnainjector/runfast + name = "DNA-Injector (Increase Run)" + desc = "Running Man." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/runfast/Initialize() + block = GLOB.increaserunblock + ..() + +/obj/item/dnainjector/antirunfast + name = "DNA-Injector (Anti-Increase Run)" + desc = "Walking Man." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antirunfast/Initialize() + block = GLOB.increaserunblock + ..() + +/obj/item/dnainjector/morph + name = "DNA-Injector (Morph)" + desc = "A total makeover." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/morph/Initialize() + block = GLOB.morphblock + ..() + +/obj/item/dnainjector/antimorph + name = "DNA-Injector (Anti-Morph)" + desc = "Cures identity crisis." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antimorph/Initialize() + block = GLOB.morphblock + ..() + +/obj/item/dnainjector/noprints + name = "DNA-Injector (No Prints)" + desc = "Better than a pair of budget insulated gloves." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/noprints/Initialize() + block = GLOB.noprintsblock + ..() + +/obj/item/dnainjector/antinoprints + name = "DNA-Injector (Anti-No Prints)" + desc = "Not quite as good as a pair of budget insulated gloves." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antinoprints/Initialize() + block = GLOB.noprintsblock + ..() + +/obj/item/dnainjector/insulation + name = "DNA-Injector (Shock Immunity)" + desc = "Better than a pair of real insulated gloves." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/insulation/Initialize() + block = GLOB.shockimmunityblock + ..() + +/obj/item/dnainjector/antiinsulation + name = "DNA-Injector (Anti-Shock Immunity)" + desc = "Not quite as good as a pair of real insulated gloves." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antiinsulation/Initialize() + block = GLOB.shockimmunityblock + ..() + +/obj/item/dnainjector/midgit + name = "DNA-Injector (Small Size)" + desc = "Makes you shrink." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/midgit/Initialize() + block = GLOB.smallsizeblock + ..() + +/obj/item/dnainjector/antimidgit + name = "DNA-Injector (Anti-Small Size)" + desc = "Makes you grow. But not too much." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antimidgit/Initialize() + block = GLOB.smallsizeblock + ..() + +///////////////////////////////////// +/obj/item/dnainjector/antiglasses + name = "DNA-Injector (Anti-Glasses)" + desc = "Toss away those glasses!" + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antiglasses/Initialize() + block = GLOB.glassesblock + ..() + +/obj/item/dnainjector/glassesmut + name = "DNA-Injector (Glasses)" + desc = "Will make you need dorkish glasses." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/glassesmut/Initialize() + block = GLOB.glassesblock + ..() + +/obj/item/dnainjector/epimut + name = "DNA-Injector (Epi.)" + desc = "Shake shake shake the room!" + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/epimut/Initialize() + block = GLOB.epilepsyblock + ..() + +/obj/item/dnainjector/antiepi + name = "DNA-Injector (Anti-Epi.)" + desc = "Will fix you up from shaking the room." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antiepi/Initialize() + block = GLOB.epilepsyblock + ..() + +/obj/item/dnainjector/anticough + name = "DNA-Injector (Anti-Cough)" + desc = "Will stop that awful noise." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/anticough/Initialize() + block = GLOB.coughblock + ..() + +/obj/item/dnainjector/coughmut + name = "DNA-Injector (Cough)" + desc = "Will bring forth a sound of horror from your throat." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/coughmut/Initialize() + block = GLOB.coughblock + ..() + +/obj/item/dnainjector/clumsymut + name = "DNA-Injector (Clumsy)" + desc = "Makes clumsy minions." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/clumsymut/Initialize() + block = GLOB.clumsyblock + ..() + +/obj/item/dnainjector/anticlumsy + name = "DNA-Injector (Anti-Clumy)" + desc = "Cleans up confusion." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/anticlumsy/Initialize() + block = GLOB.clumsyblock + ..() + +/obj/item/dnainjector/antitour + name = "DNA-Injector (Anti-Tour.)" + desc = "Will cure tourrets." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antitour/Initialize() + block = GLOB.twitchblock + ..() + +/obj/item/dnainjector/tourmut + name = "DNA-Injector (Tour.)" + desc = "Gives you a nasty case off tourrets." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/tourmut/Initialize() + block = GLOB.twitchblock + ..() + +/obj/item/dnainjector/stuttmut + name = "DNA-Injector (Stutt.)" + desc = "Makes you s-s-stuttterrr" + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/stuttmut/Initialize() + block = GLOB.nervousblock + ..() + + +/obj/item/dnainjector/antistutt + name = "DNA-Injector (Anti-Stutt.)" + desc = "Fixes that speaking impairment." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antistutt/Initialize() + block = GLOB.nervousblock + ..() + +/obj/item/dnainjector/blindmut + name = "DNA-Injector (Blind)" + desc = "Makes you not see anything." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/blindmut/Initialize() + block = GLOB.blindblock + ..() + +/obj/item/dnainjector/antiblind + name = "DNA-Injector (Anti-Blind)" + desc = "ITS A MIRACLE!!!" + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antiblind/Initialize() + block = GLOB.blindblock + ..() + +/obj/item/dnainjector/telemut + name = "DNA-Injector (Tele.)" + desc = "Super brain man!" + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/telemut/Initialize() + block = GLOB.teleblock + ..() + +/obj/item/dnainjector/antitele + name = "DNA-Injector (Anti-Tele.)" + desc = "Will make you not able to control your mind." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antitele/Initialize() + block = GLOB.teleblock + ..() + +/obj/item/dnainjector/deafmut + name = "DNA-Injector (Deaf)" + desc = "Sorry, what did you say?" + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/deafmut/Initialize() + block = GLOB.deafblock + ..() + +/obj/item/dnainjector/antideaf + name = "DNA-Injector (Anti-Deaf)" + desc = "Will make you hear once more." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antideaf/Initialize() + block = GLOB.deafblock + ..() + +/obj/item/dnainjector/hallucination + name = "DNA-Injector (Halluctination)" + desc = "What you see isn't always what you get." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/hallucination/Initialize() + block = GLOB.hallucinationblock + ..() + +/obj/item/dnainjector/antihallucination + name = "DNA-Injector (Anti-Hallucination)" + desc = "What you see is what you get." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/antihallucination/Initialize() + block = GLOB.hallucinationblock + ..() + +/obj/item/dnainjector/h2m + name = "DNA-Injector (Human > Monkey)" + desc = "Will make you a flea bag." + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/h2m/Initialize() + block = GLOB.monkeyblock + ..() + +/obj/item/dnainjector/m2h + name = "DNA-Injector (Monkey > Human)" + desc = "Will make you...less hairy." + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/m2h/Initialize() + block = GLOB.monkeyblock + ..() + + +/obj/item/dnainjector/comic + name = "DNA-Injector (Comic)" + desc = "Honk!" + datatype = DNA2_BUF_SE + value = 0xFFF + forcedmutation = TRUE + +/obj/item/dnainjector/comic/Initialize() + block = GLOB.comicblock + ..() + +/obj/item/dnainjector/anticomic + name = "DNA-Injector (Ant-Comic)" + desc = "Honk...?" + datatype = DNA2_BUF_SE + value = 0x001 + forcedmutation = TRUE + +/obj/item/dnainjector/anticomic/Initialize() + block = GLOB.comicblock + ..() diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index c35eed4fae5a..5c69a59aba90 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -1,277 +1,279 @@ -/obj/item/grenade/plastic - name = "plastic explosive" - desc = "Used to put holes in specific areas without too much extra hole." - icon_state = "plastic-explosive0" - item_state = "plastic-explosive" - flags = NOBLUDGEON - det_time = 10 - display_timer = 0 - origin_tech = "syndicate=1" - toolspeed = 1 - var/atom/target = null - var/image_overlay = null - var/obj/item/assembly_holder/nadeassembly = null - var/assemblyattacher - -/obj/item/grenade/plastic/New() - image_overlay = image('icons/obj/grenade.dmi', "[item_state]2") - ..() - -/obj/item/grenade/plastic/Destroy() - QDEL_NULL(nadeassembly) - target = null - return ..() - -/obj/item/grenade/plastic/attackby(obj/item/I, mob/user, params) - if(!nadeassembly && istype(I, /obj/item/assembly_holder)) - var/obj/item/assembly_holder/A = I - if(!user.unEquip(I)) - return ..() - nadeassembly = A - A.master = src - A.loc = src - assemblyattacher = user.ckey - to_chat(user, "You add [A] to the [name].") - playsound(src, 'sound/weapons/tap.ogg', 20, 1) - update_icon() - return - if(nadeassembly && istype(I, /obj/item/wirecutters)) - playsound(src, I.usesound, 20, 1) - nadeassembly.loc = get_turf(src) - nadeassembly.master = null - nadeassembly = null - update_icon() - return - ..() - -//assembly stuff -/obj/item/grenade/plastic/receive_signal() - prime() - -/obj/item/grenade/plastic/Crossed(atom/movable/AM, oldloc) - if(nadeassembly) - nadeassembly.Crossed(AM, oldloc) - -/obj/item/grenade/plastic/on_found(mob/finder) - if(nadeassembly) - nadeassembly.on_found(finder) - -/obj/item/grenade/plastic/attack_self(mob/user) - if(nadeassembly) - nadeassembly.attack_self(user) - return - var/newtime = input(usr, "Please set the timer.", "Timer", det_time) as num - if(user.is_in_active_hand(src)) - newtime = Clamp(newtime, 10, 60000) - det_time = newtime - to_chat(user, "Timer set for [det_time] seconds.") - -/obj/item/grenade/plastic/afterattack(atom/movable/AM, mob/user, flag) - if (!flag) - return - if (istype(AM, /mob/living/carbon)) - return - to_chat(user, "You start planting the [src]. The timer is set to [det_time]...") - - if(do_after(user, 50 * toolspeed, target = AM)) - if(!user.unEquip(src)) - return - src.target = AM - loc = null - - message_admins("[key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - JMP) with [det_time] second fuse",0,1) - log_game("[key_name(user)] planted [name] on [target.name] at ([target.x],[target.y],[target.z]) with [det_time] second fuse") - - target.overlays += image_overlay - if(!nadeassembly) - to_chat(user, "You plant the bomb. Timer counting down from [det_time].") - addtimer(CALLBACK(src, .proc/prime), det_time*10) - -/obj/item/grenade/plastic/suicide_act(mob/user) - message_admins("[key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) suicided with [src.name] at ([user.x],[user.y],[user.z] - JMP)",0,1) - log_game("[key_name(user)] suicided with [name] at ([user.x],[user.y],[user.z])") - user.visible_message("[user] activates the [name] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!") - var/message_say = "FOR NO RAISIN!" - if(user.mind) - if(user.mind.special_role) - var/role = lowertext(user.mind.special_role) - if(role == ROLE_TRAITOR || role == "syndicate" || role == "syndicate commando") - message_say = "FOR THE SYNDICATE!" - else if(role == ROLE_CHANGELING) - message_say = "FOR THE HIVE!" - else if(role == ROLE_CULTIST) - message_say = "FOR NARSIE!" - else if(role == ROLE_NINJA) - message_say = "FOR THE CLAN!" - else if(role == ROLE_WIZARD) - message_say = "FOR THE FEDERATION!" - else if(role == ROLE_REV || role == "head revolutionary") - message_say = "FOR THE REVOLOUTION!" - else if(role == "death commando" || role == ROLE_ERT) - message_say = "FOR NANOTRASEN!" - else if(role == ROLE_DEVIL) - message_say = "FOR INFERNO!" - user.say(message_say) - target = user - sleep(10) - prime() - user.gib() - return OBLITERATION - -/obj/item/grenade/plastic/update_icon() - if(nadeassembly) - icon_state = "[item_state]1" - else - icon_state = "[item_state]0" - -////////////////////////// -///// The Explosives ///// -////////////////////////// - -/obj/item/grenade/plastic/c4 - name = "C4" - desc = "Used to put holes in specific areas without too much extra hole. A saboteurs favourite." - -/obj/item/grenade/plastic/c4/prime() - var/turf/location - if(target) - if(!QDELETED(target)) - if(istype(target, /turf/)) - location = get_turf(target) // Set the explosion location to turf if planted directly on a wall or floor - else - location = get_atom_on_turf(target) // Otherwise, make sure we're blowing up what's on top of the turf - target.overlays -= image_overlay - else - location = get_atom_on_turf(src) - if(location) - explosion(location,0,0,3) - location.ex_act(2, target) - if(istype(target, /mob)) - var/mob/M = target - M.gib() - qdel(src) - -// X4 is an upgraded directional variant of c4 which is relatively safe to be standing next to. And much less safe to be standing on the other side of. -// C4 is intended to be used for infiltration, and destroying tech. X4 is intended to be used for heavy breaching and tight spaces. -// Intended to replace C4 for nukeops, and to be a randomdrop in surplus/random traitor purchases. - -/obj/item/grenade/plastic/x4 - name = "X4" - desc = "A specialized shaped high explosive breaching charge. Designed to be safer for the user, and less so, for the wall." - var/aim_dir = NORTH - icon_state = "plasticx40" - item_state = "plasticx4" - -/obj/item/grenade/plastic/x4/prime() - var/turf/location - if(target) - if(!QDELETED(target)) - if(istype(target, /turf/)) - location = get_turf(target) - else - location = get_atom_on_turf(target) - target.overlays -= image_overlay - else - location = get_atom_on_turf(src) - if(location) - if(target && target.density) - var/turf/T = get_step(location, aim_dir) - explosion(get_step(T, aim_dir),0,0,3) - explosion(T,0,2,0) - location.ex_act(2, target) - else - explosion(location, 0, 2, 3) - location.ex_act(2, target) - if(istype(target, /mob)) - var/mob/M = target - M.gib() - qdel(src) - -/obj/item/grenade/plastic/x4/afterattack(atom/movable/AM, mob/user, flag) - aim_dir = get_dir(user,AM) - ..() - -// Shaped charge -// Same blasting power as C4, but with the same idea as the X4 -- Everyone on one side of the wall is safe. - -/obj/item/grenade/plastic/c4_shaped - name = "C4 (shaped)" - desc = "A brick of C4 shaped to allow more precise breaching." - var/aim_dir = NORTH - -/obj/item/grenade/plastic/c4_shaped/prime() - var/turf/location - if(target) - if(!QDELETED(target)) - location = get_turf(target) - target.overlays -= image_overlay - else - location = get_turf(src) - if(location) - if(target && target.density) - var/turf/T = get_step(location, aim_dir) - explosion(get_step(T, aim_dir),0,0,3) - location.ex_act(2, target) - else - explosion(location, 0, 0, 3) - location.ex_act(2, target) - if(istype(target, /mob)) - var/mob/M = target - M.gib() - qdel(src) - -/obj/item/grenade/plastic/c4_shaped/afterattack(atom/movable/AM, mob/user, flag) - aim_dir = get_dir(user,AM) - ..() - -/obj/item/grenade/plastic/c4_shaped/flash - name = "C4 (flash)" - desc = "A C4 charge with an altered chemical composition, designed to blind and deafen the occupants of a room before breaching." - -/obj/item/grenade/plastic/c4_shaped/flash/prime() - if(target && target.density) - T = get_step(get_turf(target), aim_dir) - else if(target) - T = get_turf(target) - else - T = get_turf(src) - - var/obj/item/grenade/flashbang/CB = new/obj/item/grenade/flashbang(T) - CB.prime() - - ..() - -/obj/item/grenade/plastic/x4/thermite - name = "T4" - desc = "A wall breaching charge, containing fuel, metal oxide and metal powder mixed in just the right way. One hell of a combination. Effective against walls, ineffective against airlocks..." - det_time = 2 - icon_state = "t4breach0" - item_state = "t4breach" - -/obj/item/grenade/plastic/x4/thermite/prime() - var/turf/location - if(target) - if(!QDELETED(target)) - location = get_turf(target) - target.overlays -= image_overlay - else - location = get_turf(src) - if(location) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(8,0, location, aim_dir) - if(target && target.density) - var/turf/T = get_step(location, aim_dir) - for(var/turf/simulated/wall/W in range(1, location)) - W.thermitemelt(speed = 30) - addtimer(CALLBACK(null, .proc/explosion, T, 0, 0, 2), 3) - addtimer(CALLBACK(smoke, /datum/effect_system/smoke_spread/.proc/start), 3) - else - addtimer(CALLBACK(null, .proc/explosion, T, 0, 0, 2), 3) - addtimer(CALLBACK(smoke, /datum/effect_system/smoke_spread/.proc/start), 3) - - - if(isliving(target)) - var/mob/living/M = target - M.adjust_fire_stacks(2) - M.IgniteMob() - qdel(src) +/obj/item/grenade/plastic + name = "plastic explosive" + desc = "Used to put holes in specific areas without too much extra hole." + icon_state = "plastic-explosive0" + item_state = "plastic-explosive" + flags = NOBLUDGEON + det_time = 10 + display_timer = 0 + origin_tech = "syndicate=1" + toolspeed = 1 + var/atom/target = null + var/image_overlay = null + var/obj/item/assembly_holder/nadeassembly = null + var/assemblyattacher + +/obj/item/grenade/plastic/New() + image_overlay = image('icons/obj/grenade.dmi', "[item_state]2") + ..() + +/obj/item/grenade/plastic/Destroy() + QDEL_NULL(nadeassembly) + target = null + return ..() + +/obj/item/grenade/plastic/attackby(obj/item/I, mob/user, params) + if(!nadeassembly && istype(I, /obj/item/assembly_holder)) + var/obj/item/assembly_holder/A = I + if(!user.unEquip(I)) + return ..() + nadeassembly = A + A.master = src + A.loc = src + assemblyattacher = user.ckey + to_chat(user, "You add [A] to the [name].") + playsound(src, 'sound/weapons/tap.ogg', 20, 1) + update_icon() + return + if(nadeassembly && istype(I, /obj/item/wirecutters)) + playsound(src, I.usesound, 20, 1) + nadeassembly.loc = get_turf(src) + nadeassembly.master = null + nadeassembly = null + update_icon() + return + ..() + +//assembly stuff +/obj/item/grenade/plastic/receive_signal() + prime() + +/obj/item/grenade/plastic/Crossed(atom/movable/AM, oldloc) + if(nadeassembly) + nadeassembly.Crossed(AM, oldloc) + +/obj/item/grenade/plastic/on_found(mob/finder) + if(nadeassembly) + nadeassembly.on_found(finder) + +/obj/item/grenade/plastic/attack_self(mob/user) + if(nadeassembly) + nadeassembly.attack_self(user) + return + var/newtime = input(usr, "Please set the timer.", "Timer", det_time) as num + if(user.is_in_active_hand(src)) + newtime = Clamp(newtime, 10, 60000) + det_time = newtime + to_chat(user, "Timer set for [det_time] seconds.") + +/obj/item/grenade/plastic/afterattack(atom/movable/AM, mob/user, flag) + if (!flag) + return + if (istype(AM, /mob/living/carbon)) + return + to_chat(user, "You start planting the [src]. The timer is set to [det_time]...") + + if(do_after(user, 50 * toolspeed, target = AM)) + if(!user.unEquip(src)) + return + src.target = AM + loc = null + + message_admins("[key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) planted [src.name] on [target.name] at ([target.x],[target.y],[target.z] - JMP) with [det_time] second fuse",0,1) + log_game("[key_name(user)] planted [name] on [target.name] at ([target.x],[target.y],[target.z]) with [det_time] second fuse") + + target.overlays += image_overlay + if(!nadeassembly) + to_chat(user, "You plant the bomb. Timer counting down from [det_time].") + addtimer(CALLBACK(src, .proc/prime), det_time*10) + +/obj/item/grenade/plastic/suicide_act(mob/user) + message_admins("[key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) suicided with [src.name] at ([user.x],[user.y],[user.z] - JMP)",0,1) + log_game("[key_name(user)] suicided with [name] at ([user.x],[user.y],[user.z])") + user.visible_message("[user] activates the [name] and holds it above [user.p_their()] head! It looks like [user.p_theyre()] going out with a bang!") + var/message_say = "FOR NO RAISIN!" + if(user.mind) + if(user.mind.special_role) + var/role = lowertext(user.mind.special_role) + if(role == ROLE_TRAITOR || role == "syndicate" || role == "syndicate commando") + message_say = "FOR THE SYNDICATE!" + else if(role == ROLE_CHANGELING) + message_say = "FOR THE HIVE!" + else if(role == ROLE_CULTIST) + message_say = "FOR NARSIE!" + else if(role == ROLE_NINJA) + message_say = "FOR THE CLAN!" + else if(role == ROLE_WIZARD) + message_say = "FOR THE FEDERATION!" + else if(role == ROLE_REV || role == "head revolutionary") + message_say = "FOR THE REVOLOUTION!" + else if(role == "death commando" || role == ROLE_ERT) + message_say = "FOR NANOTRASEN!" + else if(role == ROLE_DEVIL) + message_say = "FOR INFERNO!" + user.say(message_say) + target = user + sleep(10) + prime() + user.gib() + return OBLITERATION + +/obj/item/grenade/plastic/update_icon() + if(nadeassembly) + icon_state = "[item_state]1" + else + icon_state = "[item_state]0" + +////////////////////////// +///// The Explosives ///// +////////////////////////// + +/obj/item/grenade/plastic/c4 + name = "C4" + desc = "Used to put holes in specific areas without too much extra hole. A saboteurs favourite." + +/obj/item/grenade/plastic/c4/prime() + var/turf/location + if(target) + if(!QDELETED(target)) + if(istype(target, /turf/)) + location = get_turf(target) // Set the explosion location to turf if planted directly on a wall or floor + else + location = get_atom_on_turf(target) // Otherwise, make sure we're blowing up what's on top of the turf + target.overlays -= image_overlay + else + location = get_atom_on_turf(src) + if(location) + explosion(location,0,0,3) + location.ex_act(2, target) + if(istype(target, /mob)) + var/mob/M = target + M.gib() + qdel(src) + +// X4 is an upgraded directional variant of c4 which is relatively safe to be standing next to. And much less safe to be standing on the other side of. +// C4 is intended to be used for infiltration, and destroying tech. X4 is intended to be used for heavy breaching and tight spaces. +// Intended to replace C4 for nukeops, and to be a randomdrop in surplus/random traitor purchases. + +/obj/item/grenade/plastic/x4 + name = "X4" + desc = "A specialized shaped high explosive breaching charge. Designed to be safer for the user, and less so, for the wall." + var/aim_dir = NORTH + icon_state = "plasticx40" + item_state = "plasticx4" + +/obj/item/grenade/plastic/x4/prime() + var/turf/location + if(target) + if(!QDELETED(target)) + if(istype(target, /turf/)) + location = get_turf(target) + else + location = get_atom_on_turf(target) + target.overlays -= image_overlay + else + location = get_atom_on_turf(src) + if(location) + if(target && target.density) + var/turf/T = get_step(location, aim_dir) + explosion(get_step(T, aim_dir),0,0,3) + explosion(T,0,2,0) + location.ex_act(2, target) + else + explosion(location, 0, 2, 3) + location.ex_act(2, target) + if(istype(target, /mob)) + var/mob/M = target + M.gib() + qdel(src) + +/obj/item/grenade/plastic/x4/afterattack(atom/movable/AM, mob/user, flag) + aim_dir = get_dir(user,AM) + ..() + +// Shaped charge +// Same blasting power as C4, but with the same idea as the X4 -- Everyone on one side of the wall is safe. + +/obj/item/grenade/plastic/c4_shaped + name = "C4 (shaped)" + desc = "A brick of C4 shaped to allow more precise breaching." + var/aim_dir = NORTH + +/obj/item/grenade/plastic/c4_shaped/prime() + var/turf/location + if(target) + if(!QDELETED(target)) + location = get_turf(target) + target.overlays -= image_overlay + else + location = get_turf(src) + if(location) + if(target && target.density) + var/turf/T = get_step(location, aim_dir) + explosion(get_step(T, aim_dir),0,0,3) + location.ex_act(2, target) + else + explosion(location, 0, 0, 3) + location.ex_act(2, target) + if(istype(target, /mob)) + var/mob/M = target + M.gib() + qdel(src) + +/obj/item/grenade/plastic/c4_shaped/afterattack(atom/movable/AM, mob/user, flag) + aim_dir = get_dir(user,AM) + ..() + +/obj/item/grenade/plastic/c4_shaped/flash + name = "C4 (flash)" + desc = "A C4 charge with an altered chemical composition, designed to blind and deafen the occupants of a room before breaching." + +/obj/item/grenade/plastic/c4_shaped/flash/prime() + var/turf/T + if(target && target.density) + T = get_step(get_turf(target), aim_dir) + else if(target) + T = get_turf(target) + else + T = get_turf(src) + + var/obj/item/grenade/flashbang/CB = new/obj/item/grenade/flashbang(T) + CB.prime() + + ..() + +/obj/item/grenade/plastic/x4/thermite + name = "T4" + desc = "A wall breaching charge, containing fuel, metal oxide and metal powder mixed in just the right way. One hell of a combination. Effective against walls, ineffective against airlocks..." + det_time = 2 + icon_state = "t4breach0" + item_state = "t4breach" + +/obj/item/grenade/plastic/x4/thermite/prime() + var/turf/location + if(target) + if(!QDELETED(target)) + location = get_turf(target) + target.overlays -= image_overlay + else + location = get_turf(src) + if(location) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(8,0, location, aim_dir) + if(target && target.density) + var/turf/T = get_step(location, aim_dir) + for(var/turf/simulated/wall/W in range(1, location)) + W.thermitemelt(speed = 30) + addtimer(CALLBACK(null, .proc/explosion, T, 0, 0, 2), 3) + addtimer(CALLBACK(smoke, /datum/effect_system/smoke_spread/.proc/start), 3) + else + var/turf/T = get_step(location, aim_dir) + addtimer(CALLBACK(null, .proc/explosion, T, 0, 0, 2), 3) + addtimer(CALLBACK(smoke, /datum/effect_system/smoke_spread/.proc/start), 3) + + + if(isliving(target)) + var/mob/living/M = target + M.adjust_fire_stacks(2) + M.IgniteMob() + qdel(src) diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm index 3ea5fdff56aa..f4ab41fab7bd 100644 --- a/code/game/objects/items/weapons/extinguisher.dm +++ b/code/game/objects/items/weapons/extinguisher.dm @@ -1,180 +1,180 @@ -/obj/item/extinguisher - name = "fire extinguisher" - desc = "A traditional red fire extinguisher." - icon = 'icons/obj/items.dmi' - icon_state = "fire_extinguisher0" - item_state = "fire_extinguisher" - hitsound = 'sound/weapons/smash.ogg' - flags = CONDUCT - throwforce = 10 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 2 - throw_range = 7 - force = 10 - container_type = AMOUNT_VISIBLE - materials = list(MAT_METAL=90) - attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") - dog_fashion = /datum/dog_fashion/back - resistance_flags = FIRE_PROOF - var/max_water = 50 - var/last_use = 1.0 - var/safety = 1 - var/refilling = FALSE - var/sprite_name = "fire_extinguisher" - var/power = 5 //Maximum distance launched water will travel - var/precision = 0 //By default, turfs picked from a spray are random, set to 1 to make it always have at least one water effect per row - var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled - -/obj/item/extinguisher/mini - name = "pocket fire extinguisher" - desc = "A light and compact fibreglass-framed model fire extinguisher." - icon_state = "miniFE0" - item_state = "miniFE" - hitsound = null //it is much lighter, after all. - flags = null //doesn't CONDUCT - throwforce = 2 - w_class = WEIGHT_CLASS_SMALL - force = 3.0 - materials = list() - max_water = 30 - sprite_name = "miniFE" - dog_fashion = null - -/obj/item/extinguisher/examine(mob/user) - . = ..() - . += "The safety is [safety ? "on" : "off"]." - - -/obj/item/extinguisher/New() - ..() - create_reagents(max_water) - reagents.add_reagent("water", max_water) - -/obj/item/extinguisher/attack_self(mob/user as mob) - safety = !safety - src.icon_state = "[sprite_name][!safety]" - src.desc = "The safety is [safety ? "on" : "off"]." - to_chat(user, "The safety is [safety ? "on" : "off"].") - return - -/obj/item/extinguisher/attack_obj(obj/O, mob/living/user) - if(AttemptRefill(O, user)) - refilling = TRUE - return FALSE - else - return ..() - -/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user) - if(istype(target, /obj/structure/reagent_dispensers/watertank) && target.Adjacent(user)) - var/safety_save = safety - safety = 1 - if(reagents.total_volume == reagents.maximum_volume) - to_chat(user, "\The [src] is already full!") - safety = safety_save - return 1 - var/obj/structure/reagent_dispensers/watertank/W = target - var/transferred = W.reagents.trans_to(src, max_water) - if(transferred > 0) - to_chat(user, "\The [src] has been refilled by [transferred] units") - playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) - for(var/datum/reagent/water/R in reagents.reagent_list) - R.cooling_temperature = cooling_power - else - to_chat(user, "\The [W] is empty!") - safety = safety_save - return 1 - else - return 0 - -/obj/item/extinguisher/afterattack(atom/target, mob/user , flag) - . = ..() - //TODO; Add support for reagents in water. - if(target.loc == user)//No more spraying yourself when putting your extinguisher away - return - - if(refilling) - refilling = FALSE - return - - if(!safety) - if(src.reagents.total_volume < 1) - to_chat(usr, "\The [src] is empty.") - return - - if(world.time < src.last_use + 20) - return - - src.last_use = world.time - - playsound(src.loc, 'sound/effects/extinguish.ogg', 75, 1, -3) - - var/direction = get_dir(src,target) - - if(usr.buckled && isobj(usr.buckled) && !usr.buckled.anchored ) - spawn(0) - var/obj/structure/chair/C = null - if(istype(usr.buckled, /obj/structure/chair)) - C = usr.buckled - var/obj/B = usr.buckled - var/movementdirection = turn(direction,180) - if(C) C.propelled = 4 - step(B, movementdirection) - sleep(1) - step(B, movementdirection) - if(C) C.propelled = 3 - sleep(1) - step(B, movementdirection) - sleep(1) - step(B, movementdirection) - if(C) C.propelled = 2 - sleep(2) - step(B, movementdirection) - if(C) C.propelled = 1 - sleep(2) - step(B, movementdirection) - if(C) C.propelled = 0 - sleep(3) - step(B, movementdirection) - sleep(3) - step(B, movementdirection) - sleep(3) - step(B, movementdirection) - - else user.newtonian_move(turn(direction, 180)) - - var/turf/T = get_turf(target) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - var/list/the_targets = list(T,T1,T2) - if(precision) - var/turf/T3 = get_step(T1, turn(direction, 90)) - var/turf/T4 = get_step(T2,turn(direction, -90)) - the_targets = list(T,T1,T2,T3,T4) - - for(var/a=0, a<5, a++) - spawn(0) - var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water( get_turf(src) ) - var/turf/my_target = pick(the_targets) - if(precision) - the_targets -= my_target - var/datum/reagents/R = new/datum/reagents(5) - if(!W) return - W.reagents = R - R.my_atom = W - if(!W || !src) return - src.reagents.trans_to(W,1) - for(var/b=0, b<5, b++) - step_towards(W,my_target) - if(!W || !W.reagents) return - W.reagents.reaction(get_turf(W)) - for(var/atom/atm in get_turf(W)) - if(!W) return - W.reagents.reaction(atm) - if(isliving(atm)) //For extinguishing mobs on fire - var/mob/living/M = atm - M.ExtinguishMob() - - if(W.loc == my_target) break - sleep(2) - else - return ..() +/obj/item/extinguisher + name = "fire extinguisher" + desc = "A traditional red fire extinguisher." + icon = 'icons/obj/items.dmi' + icon_state = "fire_extinguisher0" + item_state = "fire_extinguisher" + hitsound = 'sound/weapons/smash.ogg' + flags = CONDUCT + throwforce = 10 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 2 + throw_range = 7 + force = 10 + container_type = AMOUNT_VISIBLE + materials = list(MAT_METAL=90) + attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed") + dog_fashion = /datum/dog_fashion/back + resistance_flags = FIRE_PROOF + var/max_water = 50 + var/last_use = 1.0 + var/safety = 1 + var/refilling = FALSE + var/sprite_name = "fire_extinguisher" + var/power = 5 //Maximum distance launched water will travel + var/precision = 0 //By default, turfs picked from a spray are random, set to 1 to make it always have at least one water effect per row + var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled + +/obj/item/extinguisher/mini + name = "pocket fire extinguisher" + desc = "A light and compact fibreglass-framed model fire extinguisher." + icon_state = "miniFE0" + item_state = "miniFE" + hitsound = null //it is much lighter, after all. + flags = null //doesn't CONDUCT + throwforce = 2 + w_class = WEIGHT_CLASS_SMALL + force = 3.0 + materials = list() + max_water = 30 + sprite_name = "miniFE" + dog_fashion = null + +/obj/item/extinguisher/examine(mob/user) + . = ..() + . += "The safety is [safety ? "on" : "off"]." + + +/obj/item/extinguisher/New() + ..() + create_reagents(max_water) + reagents.add_reagent("water", max_water) + +/obj/item/extinguisher/attack_self(mob/user as mob) + safety = !safety + src.icon_state = "[sprite_name][!safety]" + src.desc = "The safety is [safety ? "on" : "off"]." + to_chat(user, "The safety is [safety ? "on" : "off"].") + return + +/obj/item/extinguisher/attack_obj(obj/O, mob/living/user) + if(AttemptRefill(O, user)) + refilling = TRUE + return FALSE + else + return ..() + +/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user) + if(istype(target, /obj/structure/reagent_dispensers/watertank) && target.Adjacent(user)) + var/safety_save = safety + safety = 1 + if(reagents.total_volume == reagents.maximum_volume) + to_chat(user, "\The [src] is already full!") + safety = safety_save + return 1 + var/obj/structure/reagent_dispensers/watertank/W = target + var/transferred = W.reagents.trans_to(src, max_water) + if(transferred > 0) + to_chat(user, "\The [src] has been refilled by [transferred] units") + playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6) + for(var/datum/reagent/water/R in reagents.reagent_list) + R.cooling_temperature = cooling_power + else + to_chat(user, "\The [W] is empty!") + safety = safety_save + return 1 + else + return 0 + +/obj/item/extinguisher/afterattack(atom/target, mob/user , flag) + . = ..() + //TODO; Add support for reagents in water. + if(target.loc == user)//No more spraying yourself when putting your extinguisher away + return + + if(refilling) + refilling = FALSE + return + + if(!safety) + if(src.reagents.total_volume < 1) + to_chat(usr, "\The [src] is empty.") + return + + if(world.time < src.last_use + 20) + return + + src.last_use = world.time + + playsound(src.loc, 'sound/effects/extinguish.ogg', 75, 1, -3) + + var/direction = get_dir(src,target) + + if(usr.buckled && isobj(usr.buckled) && !usr.buckled.anchored ) + spawn(0) + var/obj/structure/chair/C = null + if(istype(usr.buckled, /obj/structure/chair)) + C = usr.buckled + var/obj/B = usr.buckled + var/movementdirection = turn(direction,180) + if(C) C.propelled = 4 + step(B, movementdirection) + sleep(1) + step(B, movementdirection) + if(C) C.propelled = 3 + sleep(1) + step(B, movementdirection) + sleep(1) + step(B, movementdirection) + if(C) C.propelled = 2 + sleep(2) + step(B, movementdirection) + if(C) C.propelled = 1 + sleep(2) + step(B, movementdirection) + if(C) C.propelled = 0 + sleep(3) + step(B, movementdirection) + sleep(3) + step(B, movementdirection) + sleep(3) + step(B, movementdirection) + + else user.newtonian_move(turn(direction, 180)) + + var/turf/T = get_turf(target) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + var/list/the_targets = list(T,T1,T2) + if(precision) + var/turf/T3 = get_step(T1, turn(direction, 90)) + var/turf/T4 = get_step(T2,turn(direction, -90)) + the_targets = list(T,T1,T2,T3,T4) + + for(var/a=0, a<5, a++) + spawn(0) + var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water( get_turf(src) ) + var/turf/my_target = pick(the_targets) + if(precision) + the_targets -= my_target + var/datum/reagents/R = new/datum/reagents(5) + if(!W) return + W.reagents = R + R.my_atom = W + if(!W || !src) return + src.reagents.trans_to(W,1) + for(var/b=0, b<5, b++) + step_towards(W,my_target) + if(!W || !W.reagents) return + W.reagents.reaction(get_turf(W)) + for(var/atom/atm in get_turf(W)) + if(!W) return + W.reagents.reaction(atm) + if(isliving(atm)) //For extinguishing mobs on fire + var/mob/living/M = atm + M.ExtinguishMob() + + if(W.loc == my_target) break + sleep(2) + else + return ..() diff --git a/code/game/objects/items/weapons/fireworks.dm b/code/game/objects/items/weapons/fireworks.dm index 7db6eb18250c..a2a0d6df552e 100644 --- a/code/game/objects/items/weapons/fireworks.dm +++ b/code/game/objects/items/weapons/fireworks.dm @@ -59,4 +59,4 @@ obj/item/sparkler/attackby(obj/item/W,mob/user, params) new /obj/item/firework(src) new /obj/item/firework(src) new /obj/item/firework(src) - new /obj/item/firework(src) \ No newline at end of file + new /obj/item/firework(src) diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index fc1a0ca6c126..b18ec5ee0b60 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -250,4 +250,4 @@ location.hotspot_expose(700, 2) /obj/item/assembly/igniter/proc/ignite_turf(obj/item/flamethrower/F, turf/simulated/location, release_amount = 0.05) - F.default_ignite(location, release_amount) \ No newline at end of file + F.default_ignite(location, release_amount) diff --git a/code/game/objects/items/weapons/grenades/atmosgrenade.dm b/code/game/objects/items/weapons/grenades/atmosgrenade.dm index cc2008b539a2..6ba46d3442ee 100644 --- a/code/game/objects/items/weapons/grenades/atmosgrenade.dm +++ b/code/game/objects/items/weapons/grenades/atmosgrenade.dm @@ -51,4 +51,4 @@ L.adjustStaminaLoss(stamina_damage) L.apply_effect(rad_damage, IRRADIATE) L.adjust_bodytemperature(-230) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/weapons/grenades/bananade.dm b/code/game/objects/items/weapons/grenades/bananade.dm index 26a1f3ede0d5..96a3a8e1400c 100644 --- a/code/game/objects/items/weapons/grenades/bananade.dm +++ b/code/game/objects/items/weapons/grenades/bananade.dm @@ -1,5 +1,5 @@ -var/turf/T +// var/turf/T | This was made 14th September 2013, and has no use at all. Its being removed /obj/item/grenade/bananade name = "bananade" diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm index 8b08a4dc5b0b..79612afd1f38 100644 --- a/code/game/objects/items/weapons/grenades/chem_grenade.dm +++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm @@ -1,591 +1,591 @@ -#define EMPTY 0 -#define WIRED 1 -#define READY 2 - -/obj/item/grenade/chem_grenade - name = "grenade casing" - desc = "A do it yourself grenade casing!" - icon_state = "chemg" - item_state = "flashbang" - var/bomb_state = "chembomb" - var/payload_name = null // used for spawned grenades - w_class = WEIGHT_CLASS_SMALL - force = 2 - var/prime_sound = 'sound/items/screwdriver2.ogg' - var/stage = EMPTY - var/list/beakers = list() - var/list/allowed_containers = list(/obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle) - var/affected_area = 3 - var/obj/item/assembly_holder/nadeassembly = null - var/label = null - var/assemblyattacher - var/ignition_temp = 10 // The amount of heat added to the reagents when this grenade goes off. - var/threatscale = 1 // Used by advanced grenades to make them slightly more worthy. - var/no_splash = FALSE //If the grenade deletes even if it has no reagents to splash with. Used for slime core reactions. - var/contained = "" // For logging - var/cores = "" // Also for logging - -/obj/item/grenade/chem_grenade/New() - ..() - create_reagents(1000) - if(payload_name) - payload_name += " " // formatting, ignore me - update_icon() - -/obj/item/grenade/chem_grenade/Destroy() - QDEL_NULL(nadeassembly) - QDEL_LIST(beakers) - return ..() - -/obj/item/grenade/chem_grenade/examine(mob/user) - . = ..() - display_timer = (stage == READY && !nadeassembly) //show/hide the timer based on assembly state - - - -/obj/item/grenade/chem_grenade/proc/get_trigger() - if(!nadeassembly) return null - for(var/obj/O in list(nadeassembly.a_left, nadeassembly.a_right)) - if(!O || istype(O,/obj/item/assembly/igniter)) continue - return O - return null - - -/obj/item/grenade/chem_grenade/proc/update_overlays() - underlays = list() - if(nadeassembly) - underlays += "[nadeassembly.a_left.icon_state]_left" - for(var/O in nadeassembly.a_left.attached_overlays) - underlays += "[O]_l" - underlays += "[nadeassembly.a_right.icon_state]_right" - for(var/O in nadeassembly.a_right.attached_overlays) - underlays += "[O]_r" - -/obj/item/grenade/chem_grenade/update_icon() - if(nadeassembly) - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = bomb_state - update_overlays() - var/obj/item/assembly/A = get_trigger() - if(stage != READY) - name = "bomb casing[label]" - else - if(!A) - name = "[payload_name]de-fused bomb[label]" // this should not actually happen - else - name = payload_name + A.bomb_name + label // time bombs, remote mines, etc - else - icon = 'icons/obj/grenade.dmi' - icon_state = initial(icon_state) - overlays = list() - switch(stage) - if(EMPTY) - name = "grenade casing[label]" - if(WIRED) - icon_state += "_ass" - name = "grenade casing[label]" - if(READY) - if(active) - icon_state += "_active" - else - icon_state += "_locked" - name = payload_name + "grenade" + label - - -/obj/item/grenade/chem_grenade/attack_self(mob/user) - if(stage == READY && !active) - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - if(nadeassembly) - nadeassembly.attack_self(user) - update_icon() - else if(clown_check(user)) - // This used to go before the assembly check, but that has absolutely zero to do with priming the damn thing. You could spam the admins with it. - message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP) [contained].") - log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) [contained].") - investigate_log("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])[contained].", INVESTIGATE_BOMB) - to_chat(user, "You prime the [name]! [det_time / 10] second\s!") - playsound(user.loc, 'sound/weapons/armbomb.ogg', 60, 1) - active = 1 - update_icon() - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.throw_mode_on() - spawn(det_time) - prime() - -/obj/item/grenade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - var/obj/item/projectile/P = hitby - if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15)) - owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") - var/turf/T = get_turf(src) - log_game("A projectile ([hitby]) detonated a grenade held by [key_name(owner)] at [COORD(T)]") - message_admins("A projectile ([hitby]) detonated a grenade held by [key_name_admin(owner)] at [ADMIN_COORDJMP(T)]") - prime() - return 1 //It hit the grenade, not them - -/obj/item/grenade/chem_grenade/attackby(obj/item/I, mob/user, params) - if(istype(I,/obj/item/hand_labeler)) - var/obj/item/hand_labeler/HL = I - if(length(HL.label)) - label = " ([HL.label])" - return 0 - else - if(label) - label = null - update_icon() - to_chat(user, "You remove the label from [src].") - return 1 - if(istype(I, /obj/item/screwdriver)) - if(stage == WIRED) - if(beakers.len) - to_chat(user, "You lock the assembly.") - playsound(loc, prime_sound, 25, -3) - stage = READY - update_icon() - contained = "" - cores = "" // clear them out so no recursive logging by accidentally - for(var/obj/O in beakers) - if(!O.reagents) continue - if(istype(O,/obj/item/slime_extract)) - cores += " [O]" - for(var/reagent in O.reagents.reagent_list) - contained += " [reagent] " - if(contained) - if(cores) - contained = "\[[cores];[contained]\]" - else - contained = "\[[contained]\]" - var/turf/bombturf = get_turf(loc) - var/area/A = bombturf.loc - message_admins("[key_name_admin(usr)] has completed [name] at [A.name] (JMP) [contained].") - log_game("[key_name(usr)] has completed [name] at [bombturf.x], [bombturf.y], [bombturf.z]. [contained]") - else - to_chat(user, "You need to add at least one beaker before locking the assembly.") - else if(stage == READY && !nadeassembly) - det_time = det_time == 50 ? 30 : 50 //toggle between 30 and 50 - to_chat(user, "You modify the time delay. It's set for [det_time / 10] second\s.") - else if(stage == EMPTY) - to_chat(user, "You need to add an activation mechanism.") - - else if(stage == WIRED && is_type_in_list(I, allowed_containers)) - if(beakers.len == 2) - to_chat(user, "[src] can not hold more containers.") - return - else - if(I.reagents.total_volume) - to_chat(user, "You add [I] to the assembly.") - user.drop_item() - I.loc = src - beakers += I - else - to_chat(user, "[I] is empty.") - - else if(stage == EMPTY && istype(I, /obj/item/assembly_holder)) - var/obj/item/assembly_holder/A = I - if(!A.secured) - return - if(isigniter(A.a_left) == isigniter(A.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - return - - user.drop_item() - nadeassembly = A - A.master = src - A.loc = src - assemblyattacher = user.ckey - stage = WIRED - to_chat(user, "You add [A] to [src]!") - update_icon() - - else if(stage == EMPTY && istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = I - C.use(1) - - stage = WIRED - to_chat(user, "You rig [src].") - update_icon() - - else if(stage == READY && istype(I, /obj/item/wirecutters)) - to_chat(user, "You unlock the assembly.") - stage = WIRED - update_icon() - - else if(stage == WIRED && istype(I, /obj/item/wrench)) - to_chat(user, "You open the grenade and remove the contents.") - stage = EMPTY - payload_name = null - label = null - if(nadeassembly) - nadeassembly.loc = get_turf(src) - nadeassembly.master = null - nadeassembly = null - if(beakers.len) - for(var/obj/O in beakers) - O.loc = get_turf(src) - beakers = list() - update_icon() - - -//assembly stuff -/obj/item/grenade/chem_grenade/receive_signal() - prime() - -/obj/item/grenade/chem_grenade/HasProximity(atom/movable/AM) - if(nadeassembly) - nadeassembly.HasProximity(AM) - -/obj/item/grenade/chem_grenade/Move() // prox sensors and infrared care about this - ..() - if(nadeassembly) - nadeassembly.process_movement() - -/obj/item/grenade/chem_grenade/pickup() - . = ..() - if(nadeassembly) - nadeassembly.process_movement() - -/obj/item/grenade/chem_grenade/Crossed(atom/movable/AM, oldloc) - if(nadeassembly) - nadeassembly.Crossed(AM, oldloc) - -/obj/item/grenade/chem_grenade/on_found(mob/finder) - if(nadeassembly) - nadeassembly.on_found(finder) - -/obj/item/grenade/chem_grenade/hear_talk(mob/living/M, list/message_pieces) - if(nadeassembly) - nadeassembly.hear_talk(M, message_pieces) - -/obj/item/grenade/chem_grenade/hear_message(mob/living/M, msg) - if(nadeassembly) - nadeassembly.hear_message(M, msg) - -/obj/item/grenade/chem_grenade/Bump() - ..() - if(nadeassembly) - nadeassembly.process_movement() - -/obj/item/grenade/chem_grenade/throw_impact() // called when a throw stops - ..() - if(nadeassembly) - nadeassembly.process_movement() - - -/obj/item/grenade/chem_grenade/prime() - if(stage != READY) - return - - var/list/datum/reagents/reactants = list() - for(var/obj/item/reagent_containers/glass/G in beakers) - reactants += G.reagents - - if(!chem_splash(get_turf(src), affected_area, reactants, ignition_temp, threatscale) && !no_splash) - playsound(loc, 'sound/items/screwdriver2.ogg', 50, 1) - if(beakers.len) - for(var/obj/O in beakers) - O.forceMove(get_turf(src)) - beakers = list() - stage = EMPTY - update_icon() - return - - if(nadeassembly) - var/mob/M = get_mob_by_ckey(assemblyattacher) - var/mob/last = get_mob_by_ckey(nadeassembly.fingerprintslast) - var/turf/T = get_turf(src) - var/area/A = get_area(T) - message_admins("grenade primed by an assembly, attached by [key_name_admin(M)] and last touched by [key_name_admin(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP). [contained]") - log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z]) [contained]") - - update_mob() - - qdel(src) - -/obj/item/grenade/chem_grenade/proc/CreateDefaultTrigger(var/typekey) - if(ispath(typekey,/obj/item/assembly)) - nadeassembly = new(src) - nadeassembly.a_left = new /obj/item/assembly/igniter(nadeassembly) - nadeassembly.a_left.holder = nadeassembly - nadeassembly.a_left.secured = 1 - nadeassembly.a_right = new typekey(nadeassembly) - if(!nadeassembly.a_right.secured) - nadeassembly.a_right.toggle_secure() // necessary because fuxing prock_sensors - nadeassembly.a_right.holder = nadeassembly - nadeassembly.secured = 1 - nadeassembly.master = src - nadeassembly.update_icon() - stage = READY - update_icon() - - -//Large chem grenades accept slime cores and use the appropriately. -/obj/item/grenade/chem_grenade/large - name = "large grenade casing" - desc = "A custom made large grenade. It affects a larger area." - icon_state = "large_grenade" - bomb_state = "largebomb" - allowed_containers = list(/obj/item/reagent_containers/glass,/obj/item/reagent_containers/food/condiment, - /obj/item/reagent_containers/food/drinks) - origin_tech = "combat=3;engineering=3" - affected_area = 5 - ignition_temp = 25 // Large grenades are slightly more effective at setting off heat-sensitive mixtures than smaller grenades. - threatscale = 1.1 // 10% more effective. - -/obj/item/grenade/chem_grenade/large/prime() - if(stage != READY) - return - - for(var/obj/item/slime_extract/S in beakers) - if(S.Uses) - for(var/obj/item/reagent_containers/glass/G in beakers) - G.reagents.trans_to(S, G.reagents.total_volume) - - //If there is still a core (sometimes it's used up) - //and there are reagents left, behave normally, - //otherwise drop it on the ground for timed reactions like gold. - - if(S) - if(S.reagents && S.reagents.total_volume) - for(var/obj/item/reagent_containers/glass/G in beakers) - S.reagents.trans_to(G, S.reagents.total_volume) - else - S.forceMove(get_turf(src)) - no_splash = TRUE - ..() - - - //I tried to just put it in the allowed_containers list but - //if you do that it must have reagents. If you're going to - //make a special case you might as well do it explicitly. -Sayu -/obj/item/grenade/chem_grenade/large/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/slime_extract) && stage == WIRED) - to_chat(user, "You add [I] to the assembly.") - user.drop_item() - I.loc = src - beakers += I - else - return ..() - -/obj/item/grenade/chem_grenade/cryo // Intended for rare cryogenic mixes. Cools the area moderately upon detonation. - name = "cryo grenade" - desc = "A custom made cryogenic grenade. It rapidly cools its contents upon detonation." - icon_state = "cryog" - affected_area = 2 - ignition_temp = -100 - -/obj/item/grenade/chem_grenade/pyro // Intended for pyrotechnical mixes. Produces a small fire upon detonation, igniting potentially flammable mixtures. - name = "pyro grenade" - desc = "A custom made pyrotechnical grenade. It heats up and ignites its contents upon detonation." - icon_state = "pyrog" - origin_tech = "combat=4;engineering=4" - affected_area = 3 - ignition_temp = 500 // This is enough to expose a hotspot. - -/obj/item/grenade/chem_grenade/adv_release // Intended for weaker, but longer lasting effects. Could have some interesting uses. - name = "advanced release grenade" - desc = "A custom made advanced release grenade. It is able to be detonated more than once. Can be configured using a multitool." - icon_state = "timeg" - origin_tech = "combat=3;engineering=4" - var/unit_spread = 10 // Amount of units per repeat. Can be altered with a multitool. - -/obj/item/grenade/chem_grenade/adv_release/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - switch(unit_spread) - if(0 to 24) - unit_spread += 5 - if(25 to 99) - unit_spread += 25 - else - unit_spread = 5 - to_chat(user, " You set the time release to [unit_spread] units per detonation.") - -/obj/item/grenade/chem_grenade/adv_release/prime() - if(stage != READY) - return - - var/total_volume = 0 - for(var/obj/item/reagent_containers/RC in beakers) - total_volume += RC.reagents.total_volume - if(!total_volume) - qdel(src) - qdel(nadeassembly) - return - var/fraction = unit_spread/total_volume - var/datum/reagents/reactants = new(unit_spread) - reactants.my_atom = src - for(var/obj/item/reagent_containers/RC in beakers) - RC.reagents.trans_to(reactants, RC.reagents.total_volume*fraction, threatscale, 1, 1) - chem_splash(get_turf(src), affected_area, list(reactants), ignition_temp, threatscale) - - if(nadeassembly) - var/mob/M = get_mob_by_ckey(assemblyattacher) - var/mob/last = get_mob_by_ckey(nadeassembly.fingerprintslast) - var/turf/T = get_turf(src) - var/area/A = get_area(T) - message_admins("grenade primed by an assembly, attached by [key_name_admin(M)] and last touched by [key_name_admin(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).") - log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])") - else - addtimer(CALLBACK(src, .proc/prime), det_time) - var/turf/DT = get_turf(src) - var/area/DA = get_area(DT) - log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])") - -/obj/item/grenade/chem_grenade/metalfoam - payload_name = "metal foam" - desc = "Used for emergency sealing of air breaches." - stage = READY - -/obj/item/grenade/chem_grenade/metalfoam/New() - ..() - var/obj/item/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("aluminum", 30) - B2.reagents.add_reagent("fluorosurfactant", 10) - B2.reagents.add_reagent("sacid", 10) - - beakers += B1 - beakers += B2 - update_icon() - - -/obj/item/grenade/chem_grenade/firefighting - payload_name = "fire fighting grenade" - desc = "Can help to put out dangerous fires from a distance." - stage = READY - -/obj/item/grenade/chem_grenade/firefighting/New() - ..() - var/obj/item/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("firefighting_foam", 30) - B2.reagents.add_reagent("firefighting_foam", 30) - - beakers += B1 - beakers += B2 - update_icon() - -/obj/item/grenade/chem_grenade/incendiary - payload_name = "incendiary" - desc = "Used for clearing rooms of living things." - stage = READY - -/obj/item/grenade/chem_grenade/incendiary/New() - ..() - var/obj/item/reagent_containers/glass/beaker/large/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/large/B2 = new(src) - - B1.reagents.add_reagent("phosphorus", 25) - B2.reagents.add_reagent("plasma", 25) - B2.reagents.add_reagent("sacid", 25) - - - beakers += B1 - beakers += B2 - update_icon() - - -/obj/item/grenade/chem_grenade/antiweed - payload_name = "weed killer" - desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents." - stage = READY - -/obj/item/grenade/chem_grenade/antiweed/New() - ..() - var/obj/item/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("atrazine", 30) - B1.reagents.add_reagent("potassium", 20) - B2.reagents.add_reagent("phosphorus", 20) - B2.reagents.add_reagent("sugar", 20) - B2.reagents.add_reagent("atrazine", 10) - - beakers += B1 - beakers += B2 - update_icon() - - -/obj/item/grenade/chem_grenade/cleaner - payload_name = "cleaner" - desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas." - stage = READY - -/obj/item/grenade/chem_grenade/cleaner/New() - ..() - var/obj/item/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("fluorosurfactant", 40) - B2.reagents.add_reagent("cleaner", 10) - B2.reagents.add_reagent("water", 40) //when you make pre-designed foam reactions that carry the reagents, always add water last - - beakers += B1 - beakers += B2 - update_icon() - - -/obj/item/grenade/chem_grenade/teargas - payload_name = "teargas" - desc = "Used for nonlethal riot control. Contents under pressure. Do not directly inhale contents." - stage = READY - -/obj/item/grenade/chem_grenade/teargas/New() - ..() - var/obj/item/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("condensedcapsaicin", 25) - B1.reagents.add_reagent("potassium", 25) - B2.reagents.add_reagent("phosphorus", 25) - B2.reagents.add_reagent("sugar", 25) - - beakers += B1 - beakers += B2 - update_icon() - -/obj/item/grenade/chem_grenade/facid - payload_name = "acid smoke" - desc = "Use to chew up opponents from the inside out." - stage = READY - -/obj/item/grenade/chem_grenade/facid/New() - ..() - var/obj/item/reagent_containers/glass/beaker/large/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/large/B2 = new(src) - - B1.reagents.add_reagent("facid", 80) - B1.reagents.add_reagent("potassium", 20) - B2.reagents.add_reagent("phosphorus", 20) - B2.reagents.add_reagent("sugar", 20) - B2.reagents.add_reagent("facid", 60) - - beakers += B1 - beakers += B2 - update_icon() - -/obj/item/grenade/chem_grenade/saringas - payload_name = "sarin gas" - desc = "Contains sarin gas; extremely deadly and fast acting; use with extreme caution." - stage = READY - -/obj/item/grenade/chem_grenade/saringas/New() - ..() - var/obj/item/reagent_containers/glass/beaker/B1 = new(src) - var/obj/item/reagent_containers/glass/beaker/B2 = new(src) - - B1.reagents.add_reagent("sarin", 25) - B1.reagents.add_reagent("potassium", 25) - B2.reagents.add_reagent("phosphorus", 25) - B2.reagents.add_reagent("sugar", 25) - - beakers += B1 - beakers += B2 - update_icon() - -#undef EMPTY -#undef WIRED -#undef READY +#define EMPTY 0 +#define WIRED 1 +#define READY 2 + +/obj/item/grenade/chem_grenade + name = "grenade casing" + desc = "A do it yourself grenade casing!" + icon_state = "chemg" + item_state = "flashbang" + var/bomb_state = "chembomb" + var/payload_name = null // used for spawned grenades + w_class = WEIGHT_CLASS_SMALL + force = 2 + var/prime_sound = 'sound/items/screwdriver2.ogg' + var/stage = EMPTY + var/list/beakers = list() + var/list/allowed_containers = list(/obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle) + var/affected_area = 3 + var/obj/item/assembly_holder/nadeassembly = null + var/label = null + var/assemblyattacher + var/ignition_temp = 10 // The amount of heat added to the reagents when this grenade goes off. + var/threatscale = 1 // Used by advanced grenades to make them slightly more worthy. + var/no_splash = FALSE //If the grenade deletes even if it has no reagents to splash with. Used for slime core reactions. + var/contained = "" // For logging + var/cores = "" // Also for logging + +/obj/item/grenade/chem_grenade/New() + ..() + create_reagents(1000) + if(payload_name) + payload_name += " " // formatting, ignore me + update_icon() + +/obj/item/grenade/chem_grenade/Destroy() + QDEL_NULL(nadeassembly) + QDEL_LIST(beakers) + return ..() + +/obj/item/grenade/chem_grenade/examine(mob/user) + . = ..() + display_timer = (stage == READY && !nadeassembly) //show/hide the timer based on assembly state + + + +/obj/item/grenade/chem_grenade/proc/get_trigger() + if(!nadeassembly) return null + for(var/obj/O in list(nadeassembly.a_left, nadeassembly.a_right)) + if(!O || istype(O,/obj/item/assembly/igniter)) continue + return O + return null + + +/obj/item/grenade/chem_grenade/proc/update_overlays() + underlays = list() + if(nadeassembly) + underlays += "[nadeassembly.a_left.icon_state]_left" + for(var/O in nadeassembly.a_left.attached_overlays) + underlays += "[O]_l" + underlays += "[nadeassembly.a_right.icon_state]_right" + for(var/O in nadeassembly.a_right.attached_overlays) + underlays += "[O]_r" + +/obj/item/grenade/chem_grenade/update_icon() + if(nadeassembly) + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = bomb_state + update_overlays() + var/obj/item/assembly/A = get_trigger() + if(stage != READY) + name = "bomb casing[label]" + else + if(!A) + name = "[payload_name]de-fused bomb[label]" // this should not actually happen + else + name = payload_name + A.bomb_name + label // time bombs, remote mines, etc + else + icon = 'icons/obj/grenade.dmi' + icon_state = initial(icon_state) + overlays = list() + switch(stage) + if(EMPTY) + name = "grenade casing[label]" + if(WIRED) + icon_state += "_ass" + name = "grenade casing[label]" + if(READY) + if(active) + icon_state += "_active" + else + icon_state += "_locked" + name = payload_name + "grenade" + label + + +/obj/item/grenade/chem_grenade/attack_self(mob/user) + if(stage == READY && !active) + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + if(nadeassembly) + nadeassembly.attack_self(user) + update_icon() + else if(clown_check(user)) + // This used to go before the assembly check, but that has absolutely zero to do with priming the damn thing. You could spam the admins with it. + message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP) [contained].") + log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z]) [contained].") + investigate_log("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])[contained].", INVESTIGATE_BOMB) + to_chat(user, "You prime the [name]! [det_time / 10] second\s!") + playsound(user.loc, 'sound/weapons/armbomb.ogg', 60, 1) + active = 1 + update_icon() + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + spawn(det_time) + prime() + +/obj/item/grenade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + var/obj/item/projectile/P = hitby + if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15)) + owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!") + var/turf/T = get_turf(src) + log_game("A projectile ([hitby]) detonated a grenade held by [key_name(owner)] at [COORD(T)]") + message_admins("A projectile ([hitby]) detonated a grenade held by [key_name_admin(owner)] at [ADMIN_COORDJMP(T)]") + prime() + return 1 //It hit the grenade, not them + +/obj/item/grenade/chem_grenade/attackby(obj/item/I, mob/user, params) + if(istype(I,/obj/item/hand_labeler)) + var/obj/item/hand_labeler/HL = I + if(length(HL.label)) + label = " ([HL.label])" + return 0 + else + if(label) + label = null + update_icon() + to_chat(user, "You remove the label from [src].") + return 1 + if(istype(I, /obj/item/screwdriver)) + if(stage == WIRED) + if(beakers.len) + to_chat(user, "You lock the assembly.") + playsound(loc, prime_sound, 25, -3) + stage = READY + update_icon() + contained = "" + cores = "" // clear them out so no recursive logging by accidentally + for(var/obj/O in beakers) + if(!O.reagents) continue + if(istype(O,/obj/item/slime_extract)) + cores += " [O]" + for(var/reagent in O.reagents.reagent_list) + contained += " [reagent] " + if(contained) + if(cores) + contained = "\[[cores];[contained]\]" + else + contained = "\[[contained]\]" + var/turf/bombturf = get_turf(loc) + var/area/A = bombturf.loc + message_admins("[key_name_admin(usr)] has completed [name] at [A.name] (JMP) [contained].") + log_game("[key_name(usr)] has completed [name] at [bombturf.x], [bombturf.y], [bombturf.z]. [contained]") + else + to_chat(user, "You need to add at least one beaker before locking the assembly.") + else if(stage == READY && !nadeassembly) + det_time = det_time == 50 ? 30 : 50 //toggle between 30 and 50 + to_chat(user, "You modify the time delay. It's set for [det_time / 10] second\s.") + else if(stage == EMPTY) + to_chat(user, "You need to add an activation mechanism.") + + else if(stage == WIRED && is_type_in_list(I, allowed_containers)) + if(beakers.len == 2) + to_chat(user, "[src] can not hold more containers.") + return + else + if(I.reagents.total_volume) + to_chat(user, "You add [I] to the assembly.") + user.drop_item() + I.loc = src + beakers += I + else + to_chat(user, "[I] is empty.") + + else if(stage == EMPTY && istype(I, /obj/item/assembly_holder)) + var/obj/item/assembly_holder/A = I + if(!A.secured) + return + if(isigniter(A.a_left) == isigniter(A.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + return + + user.drop_item() + nadeassembly = A + A.master = src + A.loc = src + assemblyattacher = user.ckey + stage = WIRED + to_chat(user, "You add [A] to [src]!") + update_icon() + + else if(stage == EMPTY && istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = I + C.use(1) + + stage = WIRED + to_chat(user, "You rig [src].") + update_icon() + + else if(stage == READY && istype(I, /obj/item/wirecutters)) + to_chat(user, "You unlock the assembly.") + stage = WIRED + update_icon() + + else if(stage == WIRED && istype(I, /obj/item/wrench)) + to_chat(user, "You open the grenade and remove the contents.") + stage = EMPTY + payload_name = null + label = null + if(nadeassembly) + nadeassembly.loc = get_turf(src) + nadeassembly.master = null + nadeassembly = null + if(beakers.len) + for(var/obj/O in beakers) + O.loc = get_turf(src) + beakers = list() + update_icon() + + +//assembly stuff +/obj/item/grenade/chem_grenade/receive_signal() + prime() + +/obj/item/grenade/chem_grenade/HasProximity(atom/movable/AM) + if(nadeassembly) + nadeassembly.HasProximity(AM) + +/obj/item/grenade/chem_grenade/Move() // prox sensors and infrared care about this + ..() + if(nadeassembly) + nadeassembly.process_movement() + +/obj/item/grenade/chem_grenade/pickup() + . = ..() + if(nadeassembly) + nadeassembly.process_movement() + +/obj/item/grenade/chem_grenade/Crossed(atom/movable/AM, oldloc) + if(nadeassembly) + nadeassembly.Crossed(AM, oldloc) + +/obj/item/grenade/chem_grenade/on_found(mob/finder) + if(nadeassembly) + nadeassembly.on_found(finder) + +/obj/item/grenade/chem_grenade/hear_talk(mob/living/M, list/message_pieces) + if(nadeassembly) + nadeassembly.hear_talk(M, message_pieces) + +/obj/item/grenade/chem_grenade/hear_message(mob/living/M, msg) + if(nadeassembly) + nadeassembly.hear_message(M, msg) + +/obj/item/grenade/chem_grenade/Bump() + ..() + if(nadeassembly) + nadeassembly.process_movement() + +/obj/item/grenade/chem_grenade/throw_impact() // called when a throw stops + ..() + if(nadeassembly) + nadeassembly.process_movement() + + +/obj/item/grenade/chem_grenade/prime() + if(stage != READY) + return + + var/list/datum/reagents/reactants = list() + for(var/obj/item/reagent_containers/glass/G in beakers) + reactants += G.reagents + + if(!chem_splash(get_turf(src), affected_area, reactants, ignition_temp, threatscale) && !no_splash) + playsound(loc, 'sound/items/screwdriver2.ogg', 50, 1) + if(beakers.len) + for(var/obj/O in beakers) + O.forceMove(get_turf(src)) + beakers = list() + stage = EMPTY + update_icon() + return + + if(nadeassembly) + var/mob/M = get_mob_by_ckey(assemblyattacher) + var/mob/last = get_mob_by_ckey(nadeassembly.fingerprintslast) + var/turf/T = get_turf(src) + var/area/A = get_area(T) + message_admins("grenade primed by an assembly, attached by [key_name_admin(M)] and last touched by [key_name_admin(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP). [contained]") + log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z]) [contained]") + + update_mob() + + qdel(src) + +/obj/item/grenade/chem_grenade/proc/CreateDefaultTrigger(var/typekey) + if(ispath(typekey,/obj/item/assembly)) + nadeassembly = new(src) + nadeassembly.a_left = new /obj/item/assembly/igniter(nadeassembly) + nadeassembly.a_left.holder = nadeassembly + nadeassembly.a_left.secured = 1 + nadeassembly.a_right = new typekey(nadeassembly) + if(!nadeassembly.a_right.secured) + nadeassembly.a_right.toggle_secure() // necessary because fuxing prock_sensors + nadeassembly.a_right.holder = nadeassembly + nadeassembly.secured = 1 + nadeassembly.master = src + nadeassembly.update_icon() + stage = READY + update_icon() + + +//Large chem grenades accept slime cores and use the appropriately. +/obj/item/grenade/chem_grenade/large + name = "large grenade casing" + desc = "A custom made large grenade. It affects a larger area." + icon_state = "large_grenade" + bomb_state = "largebomb" + allowed_containers = list(/obj/item/reagent_containers/glass,/obj/item/reagent_containers/food/condiment, + /obj/item/reagent_containers/food/drinks) + origin_tech = "combat=3;engineering=3" + affected_area = 5 + ignition_temp = 25 // Large grenades are slightly more effective at setting off heat-sensitive mixtures than smaller grenades. + threatscale = 1.1 // 10% more effective. + +/obj/item/grenade/chem_grenade/large/prime() + if(stage != READY) + return + + for(var/obj/item/slime_extract/S in beakers) + if(S.Uses) + for(var/obj/item/reagent_containers/glass/G in beakers) + G.reagents.trans_to(S, G.reagents.total_volume) + + //If there is still a core (sometimes it's used up) + //and there are reagents left, behave normally, + //otherwise drop it on the ground for timed reactions like gold. + + if(S) + if(S.reagents && S.reagents.total_volume) + for(var/obj/item/reagent_containers/glass/G in beakers) + S.reagents.trans_to(G, S.reagents.total_volume) + else + S.forceMove(get_turf(src)) + no_splash = TRUE + ..() + + + //I tried to just put it in the allowed_containers list but + //if you do that it must have reagents. If you're going to + //make a special case you might as well do it explicitly. -Sayu +/obj/item/grenade/chem_grenade/large/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/slime_extract) && stage == WIRED) + to_chat(user, "You add [I] to the assembly.") + user.drop_item() + I.loc = src + beakers += I + else + return ..() + +/obj/item/grenade/chem_grenade/cryo // Intended for rare cryogenic mixes. Cools the area moderately upon detonation. + name = "cryo grenade" + desc = "A custom made cryogenic grenade. It rapidly cools its contents upon detonation." + icon_state = "cryog" + affected_area = 2 + ignition_temp = -100 + +/obj/item/grenade/chem_grenade/pyro // Intended for pyrotechnical mixes. Produces a small fire upon detonation, igniting potentially flammable mixtures. + name = "pyro grenade" + desc = "A custom made pyrotechnical grenade. It heats up and ignites its contents upon detonation." + icon_state = "pyrog" + origin_tech = "combat=4;engineering=4" + affected_area = 3 + ignition_temp = 500 // This is enough to expose a hotspot. + +/obj/item/grenade/chem_grenade/adv_release // Intended for weaker, but longer lasting effects. Could have some interesting uses. + name = "advanced release grenade" + desc = "A custom made advanced release grenade. It is able to be detonated more than once. Can be configured using a multitool." + icon_state = "timeg" + origin_tech = "combat=3;engineering=4" + var/unit_spread = 10 // Amount of units per repeat. Can be altered with a multitool. + +/obj/item/grenade/chem_grenade/adv_release/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + switch(unit_spread) + if(0 to 24) + unit_spread += 5 + if(25 to 99) + unit_spread += 25 + else + unit_spread = 5 + to_chat(user, " You set the time release to [unit_spread] units per detonation.") + +/obj/item/grenade/chem_grenade/adv_release/prime() + if(stage != READY) + return + + var/total_volume = 0 + for(var/obj/item/reagent_containers/RC in beakers) + total_volume += RC.reagents.total_volume + if(!total_volume) + qdel(src) + qdel(nadeassembly) + return + var/fraction = unit_spread/total_volume + var/datum/reagents/reactants = new(unit_spread) + reactants.my_atom = src + for(var/obj/item/reagent_containers/RC in beakers) + RC.reagents.trans_to(reactants, RC.reagents.total_volume*fraction, threatscale, 1, 1) + chem_splash(get_turf(src), affected_area, list(reactants), ignition_temp, threatscale) + + if(nadeassembly) + var/mob/M = get_mob_by_ckey(assemblyattacher) + var/mob/last = get_mob_by_ckey(nadeassembly.fingerprintslast) + var/turf/T = get_turf(src) + var/area/A = get_area(T) + message_admins("grenade primed by an assembly, attached by [key_name_admin(M)] and last touched by [key_name_admin(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] (JMP).") + log_game("grenade primed by an assembly, attached by [key_name(M)] and last touched by [key_name(last)] ([nadeassembly.a_left.name] and [nadeassembly.a_right.name]) at [A.name] ([T.x], [T.y], [T.z])") + else + addtimer(CALLBACK(src, .proc/prime), det_time) + var/turf/DT = get_turf(src) + var/area/DA = get_area(DT) + log_game("A grenade detonated at [DA.name] ([DT.x], [DT.y], [DT.z])") + +/obj/item/grenade/chem_grenade/metalfoam + payload_name = "metal foam" + desc = "Used for emergency sealing of air breaches." + stage = READY + +/obj/item/grenade/chem_grenade/metalfoam/New() + ..() + var/obj/item/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("aluminum", 30) + B2.reagents.add_reagent("fluorosurfactant", 10) + B2.reagents.add_reagent("sacid", 10) + + beakers += B1 + beakers += B2 + update_icon() + + +/obj/item/grenade/chem_grenade/firefighting + payload_name = "fire fighting grenade" + desc = "Can help to put out dangerous fires from a distance." + stage = READY + +/obj/item/grenade/chem_grenade/firefighting/New() + ..() + var/obj/item/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("firefighting_foam", 30) + B2.reagents.add_reagent("firefighting_foam", 30) + + beakers += B1 + beakers += B2 + update_icon() + +/obj/item/grenade/chem_grenade/incendiary + payload_name = "incendiary" + desc = "Used for clearing rooms of living things." + stage = READY + +/obj/item/grenade/chem_grenade/incendiary/New() + ..() + var/obj/item/reagent_containers/glass/beaker/large/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/large/B2 = new(src) + + B1.reagents.add_reagent("phosphorus", 25) + B2.reagents.add_reagent("plasma", 25) + B2.reagents.add_reagent("sacid", 25) + + + beakers += B1 + beakers += B2 + update_icon() + + +/obj/item/grenade/chem_grenade/antiweed + payload_name = "weed killer" + desc = "Used for purging large areas of invasive plant species. Contents under pressure. Do not directly inhale contents." + stage = READY + +/obj/item/grenade/chem_grenade/antiweed/New() + ..() + var/obj/item/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("atrazine", 30) + B1.reagents.add_reagent("potassium", 20) + B2.reagents.add_reagent("phosphorus", 20) + B2.reagents.add_reagent("sugar", 20) + B2.reagents.add_reagent("atrazine", 10) + + beakers += B1 + beakers += B2 + update_icon() + + +/obj/item/grenade/chem_grenade/cleaner + payload_name = "cleaner" + desc = "BLAM!-brand foaming space cleaner. In a special applicator for rapid cleaning of wide areas." + stage = READY + +/obj/item/grenade/chem_grenade/cleaner/New() + ..() + var/obj/item/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("fluorosurfactant", 40) + B2.reagents.add_reagent("cleaner", 10) + B2.reagents.add_reagent("water", 40) //when you make pre-designed foam reactions that carry the reagents, always add water last + + beakers += B1 + beakers += B2 + update_icon() + + +/obj/item/grenade/chem_grenade/teargas + payload_name = "teargas" + desc = "Used for nonlethal riot control. Contents under pressure. Do not directly inhale contents." + stage = READY + +/obj/item/grenade/chem_grenade/teargas/New() + ..() + var/obj/item/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("condensedcapsaicin", 25) + B1.reagents.add_reagent("potassium", 25) + B2.reagents.add_reagent("phosphorus", 25) + B2.reagents.add_reagent("sugar", 25) + + beakers += B1 + beakers += B2 + update_icon() + +/obj/item/grenade/chem_grenade/facid + payload_name = "acid smoke" + desc = "Use to chew up opponents from the inside out." + stage = READY + +/obj/item/grenade/chem_grenade/facid/New() + ..() + var/obj/item/reagent_containers/glass/beaker/large/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/large/B2 = new(src) + + B1.reagents.add_reagent("facid", 80) + B1.reagents.add_reagent("potassium", 20) + B2.reagents.add_reagent("phosphorus", 20) + B2.reagents.add_reagent("sugar", 20) + B2.reagents.add_reagent("facid", 60) + + beakers += B1 + beakers += B2 + update_icon() + +/obj/item/grenade/chem_grenade/saringas + payload_name = "sarin gas" + desc = "Contains sarin gas; extremely deadly and fast acting; use with extreme caution." + stage = READY + +/obj/item/grenade/chem_grenade/saringas/New() + ..() + var/obj/item/reagent_containers/glass/beaker/B1 = new(src) + var/obj/item/reagent_containers/glass/beaker/B2 = new(src) + + B1.reagents.add_reagent("sarin", 25) + B1.reagents.add_reagent("potassium", 25) + B2.reagents.add_reagent("phosphorus", 25) + B2.reagents.add_reagent("sugar", 25) + + beakers += B1 + beakers += B2 + update_icon() + +#undef EMPTY +#undef WIRED +#undef READY diff --git a/code/game/objects/items/weapons/grenades/clowngrenade.dm b/code/game/objects/items/weapons/grenades/clowngrenade.dm index bce9861fb72f..cfb2e1cad3da 100644 --- a/code/game/objects/items/weapons/grenades/clowngrenade.dm +++ b/code/game/objects/items/weapons/grenades/clowngrenade.dm @@ -20,7 +20,7 @@ */ var/i = 0 var/number = 0 - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) for(i = 0; i < 2; i++) number++ var/obj/item/grown/bananapeel/traitorpeel/peel = new /obj/item/grown/bananapeel/traitorpeel(get_turf(src.loc)) diff --git a/code/game/objects/items/weapons/grenades/emgrenade.dm b/code/game/objects/items/weapons/grenades/emgrenade.dm index 5b6d585ef5fd..8807391d52b1 100644 --- a/code/game/objects/items/weapons/grenades/emgrenade.dm +++ b/code/game/objects/items/weapons/grenades/emgrenade.dm @@ -1,11 +1,11 @@ -/obj/item/grenade/empgrenade - name = "classic EMP grenade" - desc = "It is designed to wreak havoc on electronic systems." - icon_state = "emp" - item_state = "emp" - origin_tech = "magnets=3;combat=2" - -/obj/item/grenade/empgrenade/prime() - update_mob() - empulse(src, 4, 10, 1) - qdel(src) +/obj/item/grenade/empgrenade + name = "classic EMP grenade" + desc = "It is designed to wreak havoc on electronic systems." + icon_state = "emp" + item_state = "emp" + origin_tech = "magnets=3;combat=2" + +/obj/item/grenade/empgrenade/prime() + update_mob() + empulse(src, 4, 10, 1) + qdel(src) diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm index 5ffcaa9291c7..b3028f13c904 100644 --- a/code/game/objects/items/weapons/grenades/flashbang.dm +++ b/code/game/objects/items/weapons/grenades/flashbang.dm @@ -1,79 +1,79 @@ -/obj/item/grenade/flashbang - name = "flashbang" - icon_state = "flashbang" - item_state = "flashbang" - origin_tech = "materials=2;combat=3" - light_power = 10 - light_color = LIGHT_COLOR_WHITE - var/light_time = 2 - var/range = 7 - -/obj/item/grenade/flashbang/prime() - update_mob() - var/flashbang_turf = get_turf(src) - if(!flashbang_turf) - return - - set_light(7) - - do_sparks(rand(5, 9), FALSE, src) - playsound(flashbang_turf, 'sound/effects/bang.ogg', 25, 1) - bang(flashbang_turf, src, range) - - for(var/obj/structure/blob/B in hear(8, flashbang_turf)) //Blob damage here - var/damage = round(30 / (get_dist(B, get_turf(src)) + 1)) - B.take_damage(damage, BURN, "melee", 0) - - spawn(light_time) - qdel(src) - -/proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE) - for(var/mob/living/M in hearers(range, T)) - if(M.stat == DEAD) - continue - M.show_message("BANG", 2) - - //Checking for protections - var/ear_safety = M.check_ear_prot() - var/distance = max(1, get_dist(get_turf(A), get_turf(M))) - - //Flash - if(flash) - if(M.weakeyes) - M.visible_message("[M] screams and collapses!") - to_chat(M, "AAAAGH!") - M.Weaken(15) //hella stunned - M.Stun(15) - if(ishuman(M)) - M.emote("scream") - var/mob/living/carbon/human/H = M - var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) - if(E) - E.receive_damage(8, 1) - - if(M.flash_eyes(affect_silicon = TRUE)) - M.Stun(max(10 / distance, 3)) - M.Weaken(max(10 / distance, 3)) - - - //Bang - if(bang) - if(!distance || A.loc == M || A.loc == M.loc) //Holding on person or being exactly where lies is significantly more dangerous and voids protection - M.Stun(10) - M.Weaken(10) - if(!ear_safety) - M.Stun(max(10 / distance, 3)) - M.Weaken(max(10 / distance, 3)) - M.AdjustEarDamage(rand(0, 5), 15) - if(iscarbon(M)) - var/mob/living/carbon/C = M - var/obj/item/organ/internal/ears/ears = C.get_int_organ(/obj/item/organ/internal/ears) - if(istype(ears)) - if(ears.ear_damage >= 15) - to_chat(M, "Your ears start to ring badly!") - if(prob(ears.ear_damage - 5)) - to_chat(M, "You can't hear anything!") - M.BecomeDeaf() - else - if(ears.ear_damage >= 5) - to_chat(M, "Your ears start to ring!") \ No newline at end of file +/obj/item/grenade/flashbang + name = "flashbang" + icon_state = "flashbang" + item_state = "flashbang" + origin_tech = "materials=2;combat=3" + light_power = 10 + light_color = LIGHT_COLOR_WHITE + var/light_time = 2 + var/range = 7 + +/obj/item/grenade/flashbang/prime() + update_mob() + var/flashbang_turf = get_turf(src) + if(!flashbang_turf) + return + + set_light(7) + + do_sparks(rand(5, 9), FALSE, src) + playsound(flashbang_turf, 'sound/effects/bang.ogg', 25, 1) + bang(flashbang_turf, src, range) + + for(var/obj/structure/blob/B in hear(8, flashbang_turf)) //Blob damage here + var/damage = round(30 / (get_dist(B, get_turf(src)) + 1)) + B.take_damage(damage, BURN, "melee", 0) + + spawn(light_time) + qdel(src) + +/proc/bang(turf/T, atom/A, range = 7, flash = TRUE, bang = TRUE) + for(var/mob/living/M in hearers(range, T)) + if(M.stat == DEAD) + continue + M.show_message("BANG", 2) + + //Checking for protections + var/ear_safety = M.check_ear_prot() + var/distance = max(1, get_dist(get_turf(A), get_turf(M))) + + //Flash + if(flash) + if(M.weakeyes) + M.visible_message("[M] screams and collapses!") + to_chat(M, "AAAAGH!") + M.Weaken(15) //hella stunned + M.Stun(15) + if(ishuman(M)) + M.emote("scream") + var/mob/living/carbon/human/H = M + var/obj/item/organ/internal/eyes/E = H.get_int_organ(/obj/item/organ/internal/eyes) + if(E) + E.receive_damage(8, 1) + + if(M.flash_eyes(affect_silicon = TRUE)) + M.Stun(max(10 / distance, 3)) + M.Weaken(max(10 / distance, 3)) + + + //Bang + if(bang) + if(!distance || A.loc == M || A.loc == M.loc) //Holding on person or being exactly where lies is significantly more dangerous and voids protection + M.Stun(10) + M.Weaken(10) + if(!ear_safety) + M.Stun(max(10 / distance, 3)) + M.Weaken(max(10 / distance, 3)) + M.AdjustEarDamage(rand(0, 5), 15) + if(iscarbon(M)) + var/mob/living/carbon/C = M + var/obj/item/organ/internal/ears/ears = C.get_int_organ(/obj/item/organ/internal/ears) + if(istype(ears)) + if(ears.ear_damage >= 15) + to_chat(M, "Your ears start to ring badly!") + if(prob(ears.ear_damage - 5)) + to_chat(M, "You can't hear anything!") + M.BecomeDeaf() + else + if(ears.ear_damage >= 5) + to_chat(M, "Your ears start to ring!") diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 414fffdfc7e1..8cbc041c146c 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -1,111 +1,111 @@ -/obj/item/grenade - name = "grenade" - desc = "A hand held grenade, with an adjustable timer." - w_class = WEIGHT_CLASS_SMALL - icon = 'icons/obj/grenade.dmi' - icon_state = "grenade" - item_state = "flashbang" - throw_speed = 4 - throw_range = 20 - flags = CONDUCT - slot_flags = SLOT_BELT - resistance_flags = FLAMMABLE - max_integrity = 40 - var/active = 0 - var/det_time = 50 - var/display_timer = 1 - -/obj/item/grenade/deconstruct(disassembled = TRUE) - if(!disassembled) - prime() - if(!QDELETED(src)) - qdel(src) - -/obj/item/grenade/proc/clown_check(var/mob/living/user) - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "Huh? How does this thing work?") - active = 1 - icon_state = initial(icon_state) + "_active" - playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) - spawn(5) - if(user) - user.drop_item() - prime() - return 0 - return 1 - - -/*/obj/item/grenade/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) - if(istype(target, /obj/item/storage)) return ..() // Trying to put it in a full container - if(istype(target, /obj/item/gun/grenadelauncher)) return ..() - if((user.is_in_active_hand(src)) && (!active) && (clown_check(user)) && target.loc != src.loc) - to_chat(user, "You prime the [name]! [det_time/10] seconds!") - active = 1 - icon_state = initial(icon_state) + "_active" - playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) - spawn(det_time) - prime() - return - user.dir = get_dir(user, target) - user.drop_item() - var/t = (isturf(target) ? target : target.loc) - walk_towards(src, t, 3) - return*/ - - -/obj/item/grenade/examine(mob/user) - . = ..() - if(display_timer) - if(det_time > 1) - . += "The timer is set to [det_time/10] second\s." - else - . += "\The [src] is set for instant detonation." - -/obj/item/grenade/attack_self(mob/user as mob) - if(!active) - if(clown_check(user)) - to_chat(user, "You prime the [name]! [det_time/10] seconds!") - active = 1 - icon_state = initial(icon_state) + "_active" - add_fingerprint(user) - var/turf/bombturf = get_turf(src) - var/area/A = get_area(bombturf) - message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP)") - log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])") - investigate_log("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])", INVESTIGATE_BOMB) - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.throw_mode_on() - spawn(det_time) - prime() - - -/obj/item/grenade/proc/prime() - -/obj/item/grenade/proc/update_mob() - if(ismob(loc)) - var/mob/M = loc - M.unEquip(src) - - -/obj/item/grenade/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/screwdriver)) - switch(det_time) - if("1") - det_time = 10 - to_chat(user, "You set the [name] for 1 second detonation time.") - if("10") - det_time = 30 - to_chat(user, "You set the [name] for 3 second detonation time.") - if("30") - det_time = 50 - to_chat(user, "You set the [name] for 5 second detonation time.") - if("50") - det_time = 1 - to_chat(user, "You set the [name] for instant detonation.") - add_fingerprint(user) - ..() - -/obj/item/grenade/attack_hand() - walk(src, null, null) - ..() +/obj/item/grenade + name = "grenade" + desc = "A hand held grenade, with an adjustable timer." + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/grenade.dmi' + icon_state = "grenade" + item_state = "flashbang" + throw_speed = 4 + throw_range = 20 + flags = CONDUCT + slot_flags = SLOT_BELT + resistance_flags = FLAMMABLE + max_integrity = 40 + var/active = 0 + var/det_time = 50 + var/display_timer = 1 + +/obj/item/grenade/deconstruct(disassembled = TRUE) + if(!disassembled) + prime() + if(!QDELETED(src)) + qdel(src) + +/obj/item/grenade/proc/clown_check(var/mob/living/user) + if((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "Huh? How does this thing work?") + active = 1 + icon_state = initial(icon_state) + "_active" + playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) + spawn(5) + if(user) + user.drop_item() + prime() + return 0 + return 1 + + +/*/obj/item/grenade/afterattack(atom/target as mob|obj|turf|area, mob/user as mob) + if(istype(target, /obj/item/storage)) return ..() // Trying to put it in a full container + if(istype(target, /obj/item/gun/grenadelauncher)) return ..() + if((user.is_in_active_hand(src)) && (!active) && (clown_check(user)) && target.loc != src.loc) + to_chat(user, "You prime the [name]! [det_time/10] seconds!") + active = 1 + icon_state = initial(icon_state) + "_active" + playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) + spawn(det_time) + prime() + return + user.dir = get_dir(user, target) + user.drop_item() + var/t = (isturf(target) ? target : target.loc) + walk_towards(src, t, 3) + return*/ + + +/obj/item/grenade/examine(mob/user) + . = ..() + if(display_timer) + if(det_time > 1) + . += "The timer is set to [det_time/10] second\s." + else + . += "\The [src] is set for instant detonation." + +/obj/item/grenade/attack_self(mob/user as mob) + if(!active) + if(clown_check(user)) + to_chat(user, "You prime the [name]! [det_time/10] seconds!") + active = 1 + icon_state = initial(icon_state) + "_active" + add_fingerprint(user) + var/turf/bombturf = get_turf(src) + var/area/A = get_area(bombturf) + message_admins("[key_name_admin(usr)] has primed a [name] for detonation at [A.name] (JMP)") + log_game("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])") + investigate_log("[key_name(usr)] has primed a [name] for detonation at [A.name] ([bombturf.x],[bombturf.y],[bombturf.z])", INVESTIGATE_BOMB) + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + spawn(det_time) + prime() + + +/obj/item/grenade/proc/prime() + +/obj/item/grenade/proc/update_mob() + if(ismob(loc)) + var/mob/M = loc + M.unEquip(src) + + +/obj/item/grenade/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/screwdriver)) + switch(det_time) + if("1") + det_time = 10 + to_chat(user, "You set the [name] for 1 second detonation time.") + if("10") + det_time = 30 + to_chat(user, "You set the [name] for 3 second detonation time.") + if("30") + det_time = 50 + to_chat(user, "You set the [name] for 5 second detonation time.") + if("50") + det_time = 1 + to_chat(user, "You set the [name] for instant detonation.") + add_fingerprint(user) + ..() + +/obj/item/grenade/attack_hand() + walk(src, null, null) + ..() diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm index 7fd965b5c2a5..6dd07485f5b8 100644 --- a/code/game/objects/items/weapons/grenades/smokebomb.dm +++ b/code/game/objects/items/weapons/grenades/smokebomb.dm @@ -1,37 +1,37 @@ -/obj/item/grenade/smokebomb - desc = "It is set to detonate in 2 seconds." - name = "smoke bomb" - icon = 'icons/obj/grenade.dmi' - icon_state = "flashbang" - det_time = 20 - item_state = "flashbang" - slot_flags = SLOT_BELT - var/datum/effect_system/smoke_spread/bad/smoke - -/obj/item/grenade/smokebomb/New() - ..() - src.smoke = new /datum/effect_system/smoke_spread/bad - src.smoke.attach(src) - -/obj/item/grenade/smokebomb/Destroy() - QDEL_NULL(smoke) - return ..() - -/obj/item/grenade/smokebomb/prime() - playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) - src.smoke.set_up(10, 0, usr.loc) - spawn(0) - src.smoke.start() - sleep(10) - src.smoke.start() - sleep(10) - src.smoke.start() - sleep(10) - src.smoke.start() - - for(var/obj/structure/blob/B in view(8,src)) - var/damage = round(30/(get_dist(B,src)+1)) - B.take_damage(damage, BURN, "melee", 0) - sleep(80) - qdel(src) - return +/obj/item/grenade/smokebomb + desc = "It is set to detonate in 2 seconds." + name = "smoke bomb" + icon = 'icons/obj/grenade.dmi' + icon_state = "flashbang" + det_time = 20 + item_state = "flashbang" + slot_flags = SLOT_BELT + var/datum/effect_system/smoke_spread/bad/smoke + +/obj/item/grenade/smokebomb/New() + ..() + src.smoke = new /datum/effect_system/smoke_spread/bad + src.smoke.attach(src) + +/obj/item/grenade/smokebomb/Destroy() + QDEL_NULL(smoke) + return ..() + +/obj/item/grenade/smokebomb/prime() + playsound(src.loc, 'sound/effects/smoke.ogg', 50, 1, -3) + src.smoke.set_up(10, 0, usr.loc) + spawn(0) + src.smoke.start() + sleep(10) + src.smoke.start() + sleep(10) + src.smoke.start() + sleep(10) + src.smoke.start() + + for(var/obj/structure/blob/B in view(8,src)) + var/damage = round(30/(get_dist(B,src)+1)) + B.take_damage(damage, BURN, "melee", 0) + sleep(80) + qdel(src) + return diff --git a/code/game/objects/items/weapons/grenades/spawnergrenade.dm b/code/game/objects/items/weapons/grenades/spawnergrenade.dm index dacbffa1a85d..b4df7b0d16b1 100644 --- a/code/game/objects/items/weapons/grenades/spawnergrenade.dm +++ b/code/game/objects/items/weapons/grenades/spawnergrenade.dm @@ -1,68 +1,68 @@ -/obj/item/grenade/spawnergrenade - desc = "It is set to detonate in 5 seconds. It will unleash unleash an unspecified anomaly into the vicinity." - name = "delivery grenade" - icon = 'icons/obj/grenade.dmi' - icon_state = "delivery" - item_state = "flashbang" - origin_tech = "materials=3;magnets=4" - var/spawner_type = null // must be an object path - var/deliveryamt = 1 // amount of type to deliver - spawner_type = /mob/living/simple_animal/hostile/viscerator - - prime() // Prime now just handles the two loops that query for people in lockers and people who can see it. - - if(spawner_type && deliveryamt) - // Make a quick flash - var/turf/T = get_turf(src) - playsound(T, 'sound/effects/phasein.ogg', 100, 1) - for(var/mob/living/carbon/C in viewers(T, null)) - C.flash_eyes() - - for(var/i=1, i<=deliveryamt, i++) - var/atom/movable/x = new spawner_type - x.admin_spawned = admin_spawned - x.loc = T - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(x, pick(NORTH,SOUTH,EAST,WEST)) - - // Spawn some hostile syndicate critters - - qdel(src) - return - -/obj/item/grenade/spawnergrenade/manhacks - name = "manhack delivery grenade" - spawner_type = /mob/living/simple_animal/hostile/viscerator - deliveryamt = 5 - origin_tech = "materials=3;magnets=4;syndicate=3" - -/obj/item/grenade/spawnergrenade/spesscarp - name = "carp delivery grenade" - spawner_type = /mob/living/simple_animal/hostile/carp - deliveryamt = 5 - origin_tech = "materials=3;magnets=4;syndicate=3" - -/obj/item/grenade/spawnergrenade/feral_cats - name = "feral cat delivery grenade" - desc = "This grenade contains 5 dehydrated feral cats in a similar manner to dehydrated monkeys, which, upon detonation, will be rehydrated by a small reservoir of water contained within the grenade. These cats will then attack anything in sight." - spawner_type = /mob/living/simple_animal/hostile/feral_cat - deliveryamt = 5 - origin_tech = "materials=3;magnets=4;syndicate=3" - -/obj/item/grenade/spawnergrenade/feral_cats/prime() //Own proc for this because the regular one would flash people which was dumb. - update_mob() - if(spawner_type && deliveryamt) - var/turf/T = get_turf(src) - playsound(T, 'sound/effects/phasein.ogg', 100, 1) - - for(var/i=1, i<=deliveryamt, i++) - var/atom/movable/x = new spawner_type - x.loc = T - if(prob(50)) - for(var/j = 1, j <= rand(1, 3), j++) - step(x, pick(NORTH,SOUTH,EAST,WEST)) - - - qdel(src) - return +/obj/item/grenade/spawnergrenade + desc = "It is set to detonate in 5 seconds. It will unleash unleash an unspecified anomaly into the vicinity." + name = "delivery grenade" + icon = 'icons/obj/grenade.dmi' + icon_state = "delivery" + item_state = "flashbang" + origin_tech = "materials=3;magnets=4" + var/spawner_type = null // must be an object path + var/deliveryamt = 1 // amount of type to deliver + spawner_type = /mob/living/simple_animal/hostile/viscerator + + prime() // Prime now just handles the two loops that query for people in lockers and people who can see it. + + if(spawner_type && deliveryamt) + // Make a quick flash + var/turf/T = get_turf(src) + playsound(T, 'sound/effects/phasein.ogg', 100, 1) + for(var/mob/living/carbon/C in viewers(T, null)) + C.flash_eyes() + + for(var/i=1, i<=deliveryamt, i++) + var/atom/movable/x = new spawner_type + x.admin_spawned = admin_spawned + x.loc = T + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(x, pick(NORTH,SOUTH,EAST,WEST)) + + // Spawn some hostile syndicate critters + + qdel(src) + return + +/obj/item/grenade/spawnergrenade/manhacks + name = "manhack delivery grenade" + spawner_type = /mob/living/simple_animal/hostile/viscerator + deliveryamt = 5 + origin_tech = "materials=3;magnets=4;syndicate=3" + +/obj/item/grenade/spawnergrenade/spesscarp + name = "carp delivery grenade" + spawner_type = /mob/living/simple_animal/hostile/carp + deliveryamt = 5 + origin_tech = "materials=3;magnets=4;syndicate=3" + +/obj/item/grenade/spawnergrenade/feral_cats + name = "feral cat delivery grenade" + desc = "This grenade contains 5 dehydrated feral cats in a similar manner to dehydrated monkeys, which, upon detonation, will be rehydrated by a small reservoir of water contained within the grenade. These cats will then attack anything in sight." + spawner_type = /mob/living/simple_animal/hostile/feral_cat + deliveryamt = 5 + origin_tech = "materials=3;magnets=4;syndicate=3" + +/obj/item/grenade/spawnergrenade/feral_cats/prime() //Own proc for this because the regular one would flash people which was dumb. + update_mob() + if(spawner_type && deliveryamt) + var/turf/T = get_turf(src) + playsound(T, 'sound/effects/phasein.ogg', 100, 1) + + for(var/i=1, i<=deliveryamt, i++) + var/atom/movable/x = new spawner_type + x.loc = T + if(prob(50)) + for(var/j = 1, j <= rand(1, 3), j++) + step(x, pick(NORTH,SOUTH,EAST,WEST)) + + + qdel(src) + return diff --git a/code/game/objects/items/weapons/grenades/syndieminibomb.dm b/code/game/objects/items/weapons/grenades/syndieminibomb.dm index 0292b8f9eba7..f092a4cf6dc2 100644 --- a/code/game/objects/items/weapons/grenades/syndieminibomb.dm +++ b/code/game/objects/items/weapons/grenades/syndieminibomb.dm @@ -9,4 +9,4 @@ /obj/item/grenade/syndieminibomb/prime() update_mob() explosion(loc, 1, 2, 4, flame_range = 2) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm index 94f5e47f4caa..e188279eb8c4 100644 --- a/code/game/objects/items/weapons/handcuffs.dm +++ b/code/game/objects/items/weapons/handcuffs.dm @@ -1,194 +1,194 @@ -/obj/item/restraints/handcuffs - name = "handcuffs" - desc = "Use this to keep prisoners in line." - gender = PLURAL - icon = 'icons/obj/items.dmi' - icon_state = "handcuff" - flags = CONDUCT - slot_flags = SLOT_BELT - throwforce = 5 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 2 - throw_range = 5 - materials = list(MAT_METAL=500) - origin_tech = "engineering=3;combat=3" - breakouttime = 600 //Deciseconds = 60s = 1 minutes - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - var/cuffsound = 'sound/weapons/handcuffs.ogg' - var/trashtype = null //For disposable cuffs - var/ignoresClumsy = FALSE - -/obj/item/restraints/handcuffs/attack(mob/living/carbon/C, mob/user) - if(!user.IsAdvancedToolUser()) - return - - if(!istype(C)) - return - - if(flags & NODROP) - to_chat(user, "[src] is stuck to your hand!") - return - - if((CLUMSY in user.mutations) && prob(50) && (!ignoresClumsy)) - to_chat(user, "Uh... how do those things work?!") - apply_cuffs(user, user) - return - - cuff(C, user) - -/obj/item/restraints/handcuffs/proc/cuff(mob/living/carbon/C, mob/user, remove_src = TRUE) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(!(H.has_left_hand() || H.has_right_hand())) - to_chat(user, "How do you suggest handcuffing someone with no hands?") - return - - if(!C.handcuffed) - C.visible_message("[user] is trying to put [src.name] on [C]!", \ - "[user] is trying to put [src.name] on [C]!") - - playsound(loc, cuffsound, 30, 1, -2) - if(do_mob(user, C, 30)) - apply_cuffs(C, user, remove_src) - to_chat(user, "You handcuff [C].") - if(istype(src, /obj/item/restraints/handcuffs/cable)) - feedback_add_details("handcuffs", "C") - else - feedback_add_details("handcuffs", "H") - - add_attack_logs(user, C, "Handcuffed ([src])") - else - to_chat(user, "You fail to handcuff [C].") - -/obj/item/restraints/handcuffs/proc/apply_cuffs(mob/living/carbon/target, mob/user, remove_src = TRUE) - if(!target.handcuffed) - if(remove_src) - user.drop_item() - if(trashtype) - target.handcuffed = new trashtype(target) - if(remove_src) - qdel(src) - else - if(remove_src) - loc = target - target.handcuffed = src - else - target.handcuffed = new type(loc) - target.update_handcuffed() - return - -/obj/item/restraints/handcuffs/sinew - name = "sinew restraints" - desc = "A pair of restraints fashioned from long strands of flesh." - icon = 'icons/obj/mining.dmi' - icon_state = "sinewcuff" - item_state = "sinewcuff" - breakouttime = 300 //Deciseconds = 30s - cuffsound = 'sound/weapons/cablecuff.ogg' - -/obj/item/restraints/handcuffs/cable - name = "cable restraints" - desc = "Looks like some cables tied together. Could be used to tie something up." - icon_state = "cuff_white" - origin_tech = "engineering=2" - materials = list(MAT_METAL=150, MAT_GLASS=75) - breakouttime = 300 //Deciseconds = 30s - cuffsound = 'sound/weapons/cablecuff.ogg' - -/obj/item/restraints/handcuffs/cable/red - color = COLOR_RED - -/obj/item/restraints/handcuffs/cable/yellow - color = COLOR_YELLOW - -/obj/item/restraints/handcuffs/cable/blue - color = COLOR_BLUE - -/obj/item/restraints/handcuffs/cable/green - color = COLOR_GREEN - -/obj/item/restraints/handcuffs/cable/pink - color = COLOR_PINK - -/obj/item/restraints/handcuffs/cable/orange - color = COLOR_ORANGE - -/obj/item/restraints/handcuffs/cable/cyan - color = COLOR_CYAN - -/obj/item/restraints/handcuffs/cable/white - color = COLOR_WHITE - -/obj/item/restraints/handcuffs/cable/random/New() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - ..() - -/obj/item/restraints/handcuffs/cable/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - colorC = color_rainbow() - color = colorC - else - color = COLOR_RED - -/obj/item/restraints/handcuffs/cable/proc/color_rainbow() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - return color - -/obj/item/restraints/handcuffs/alien - icon_state = "handcuffAlien" - -/obj/item/restraints/handcuffs/pinkcuffs - name = "fluffy pink handcuffs" - desc = "Use this to keep prisoners in line. Or you know, your significant other." - icon_state = "pinkcuffs" - -/obj/item/restraints/handcuffs/cable/attackby(var/obj/item/I, mob/user as mob, params) - ..() - if(istype(I, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = I - if(R.use(1)) - var/obj/item/wirerod/W = new /obj/item/wirerod - if(!remove_item_from_storage(user)) - user.unEquip(src) - user.put_in_hands(W) - to_chat(user, "You wrap the cable restraint around the top of the rod.") - qdel(src) - else - to_chat(user, "You need one rod to make a wired rod!") - else if(istype(I, /obj/item/stack/sheet/metal)) - var/obj/item/stack/sheet/metal/M = I - if(M.amount < 6) - to_chat(user, "You need at least six metal sheets to make good enough weights!") - return - to_chat(user, "You begin to apply [I] to [src]...") - if(do_after(user, 35 * M.toolspeed, target = src)) - var/obj/item/restraints/legcuffs/bola/S = new /obj/item/restraints/legcuffs/bola - M.use(6) - user.put_in_hands(S) - to_chat(user, "You make some weights out of [I] and tie them to [src].") - if(!remove_item_from_storage(user)) - user.unEquip(src) - qdel(src) - else if(istype(I, /obj/item/toy/crayon)) - var/obj/item/toy/crayon/C = I - cable_color(C.colourName) - -/obj/item/restraints/handcuffs/cable/zipties - name = "zipties" - desc = "Plastic, disposable zipties that can be used to restrain temporarily but are destroyed after use." - icon_state = "cuff_white" - breakouttime = 450 //Deciseconds = 45s - materials = list() - trashtype = /obj/item/restraints/handcuffs/cable/zipties/used - -/obj/item/restraints/handcuffs/cable/zipties/cyborg/attack(mob/living/carbon/C, mob/user) - if(isrobot(user)) - cuff(C, user, FALSE) - -/obj/item/restraints/handcuffs/cable/zipties/used - desc = "A pair of broken zipties." - icon_state = "cuff_white_used" - -/obj/item/restraints/handcuffs/cable/zipties/used/attack() - return +/obj/item/restraints/handcuffs + name = "handcuffs" + desc = "Use this to keep prisoners in line." + gender = PLURAL + icon = 'icons/obj/items.dmi' + icon_state = "handcuff" + flags = CONDUCT + slot_flags = SLOT_BELT + throwforce = 5 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 2 + throw_range = 5 + materials = list(MAT_METAL=500) + origin_tech = "engineering=3;combat=3" + breakouttime = 600 //Deciseconds = 60s = 1 minutes + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + var/cuffsound = 'sound/weapons/handcuffs.ogg' + var/trashtype = null //For disposable cuffs + var/ignoresClumsy = FALSE + +/obj/item/restraints/handcuffs/attack(mob/living/carbon/C, mob/user) + if(!user.IsAdvancedToolUser()) + return + + if(!istype(C)) + return + + if(flags & NODROP) + to_chat(user, "[src] is stuck to your hand!") + return + + if((CLUMSY in user.mutations) && prob(50) && (!ignoresClumsy)) + to_chat(user, "Uh... how do those things work?!") + apply_cuffs(user, user) + return + + cuff(C, user) + +/obj/item/restraints/handcuffs/proc/cuff(mob/living/carbon/C, mob/user, remove_src = TRUE) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(!(H.has_left_hand() || H.has_right_hand())) + to_chat(user, "How do you suggest handcuffing someone with no hands?") + return + + if(!C.handcuffed) + C.visible_message("[user] is trying to put [src.name] on [C]!", \ + "[user] is trying to put [src.name] on [C]!") + + playsound(loc, cuffsound, 30, 1, -2) + if(do_mob(user, C, 30)) + apply_cuffs(C, user, remove_src) + to_chat(user, "You handcuff [C].") + if(istype(src, /obj/item/restraints/handcuffs/cable)) + feedback_add_details("handcuffs", "C") + else + feedback_add_details("handcuffs", "H") + + add_attack_logs(user, C, "Handcuffed ([src])") + else + to_chat(user, "You fail to handcuff [C].") + +/obj/item/restraints/handcuffs/proc/apply_cuffs(mob/living/carbon/target, mob/user, remove_src = TRUE) + if(!target.handcuffed) + if(remove_src) + user.drop_item() + if(trashtype) + target.handcuffed = new trashtype(target) + if(remove_src) + qdel(src) + else + if(remove_src) + loc = target + target.handcuffed = src + else + target.handcuffed = new type(loc) + target.update_handcuffed() + return + +/obj/item/restraints/handcuffs/sinew + name = "sinew restraints" + desc = "A pair of restraints fashioned from long strands of flesh." + icon = 'icons/obj/mining.dmi' + icon_state = "sinewcuff" + item_state = "sinewcuff" + breakouttime = 300 //Deciseconds = 30s + cuffsound = 'sound/weapons/cablecuff.ogg' + +/obj/item/restraints/handcuffs/cable + name = "cable restraints" + desc = "Looks like some cables tied together. Could be used to tie something up." + icon_state = "cuff_white" + origin_tech = "engineering=2" + materials = list(MAT_METAL=150, MAT_GLASS=75) + breakouttime = 300 //Deciseconds = 30s + cuffsound = 'sound/weapons/cablecuff.ogg' + +/obj/item/restraints/handcuffs/cable/red + color = COLOR_RED + +/obj/item/restraints/handcuffs/cable/yellow + color = COLOR_YELLOW + +/obj/item/restraints/handcuffs/cable/blue + color = COLOR_BLUE + +/obj/item/restraints/handcuffs/cable/green + color = COLOR_GREEN + +/obj/item/restraints/handcuffs/cable/pink + color = COLOR_PINK + +/obj/item/restraints/handcuffs/cable/orange + color = COLOR_ORANGE + +/obj/item/restraints/handcuffs/cable/cyan + color = COLOR_CYAN + +/obj/item/restraints/handcuffs/cable/white + color = COLOR_WHITE + +/obj/item/restraints/handcuffs/cable/random/New() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + ..() + +/obj/item/restraints/handcuffs/cable/proc/cable_color(var/colorC) + if(colorC) + if(colorC == "rainbow") + colorC = color_rainbow() + color = colorC + else + color = COLOR_RED + +/obj/item/restraints/handcuffs/cable/proc/color_rainbow() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + return color + +/obj/item/restraints/handcuffs/alien + icon_state = "handcuffAlien" + +/obj/item/restraints/handcuffs/pinkcuffs + name = "fluffy pink handcuffs" + desc = "Use this to keep prisoners in line. Or you know, your significant other." + icon_state = "pinkcuffs" + +/obj/item/restraints/handcuffs/cable/attackby(var/obj/item/I, mob/user as mob, params) + ..() + if(istype(I, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = I + if(R.use(1)) + var/obj/item/wirerod/W = new /obj/item/wirerod + if(!remove_item_from_storage(user)) + user.unEquip(src) + user.put_in_hands(W) + to_chat(user, "You wrap the cable restraint around the top of the rod.") + qdel(src) + else + to_chat(user, "You need one rod to make a wired rod!") + else if(istype(I, /obj/item/stack/sheet/metal)) + var/obj/item/stack/sheet/metal/M = I + if(M.amount < 6) + to_chat(user, "You need at least six metal sheets to make good enough weights!") + return + to_chat(user, "You begin to apply [I] to [src]...") + if(do_after(user, 35 * M.toolspeed, target = src)) + var/obj/item/restraints/legcuffs/bola/S = new /obj/item/restraints/legcuffs/bola + M.use(6) + user.put_in_hands(S) + to_chat(user, "You make some weights out of [I] and tie them to [src].") + if(!remove_item_from_storage(user)) + user.unEquip(src) + qdel(src) + else if(istype(I, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/C = I + cable_color(C.colourName) + +/obj/item/restraints/handcuffs/cable/zipties + name = "zipties" + desc = "Plastic, disposable zipties that can be used to restrain temporarily but are destroyed after use." + icon_state = "cuff_white" + breakouttime = 450 //Deciseconds = 45s + materials = list() + trashtype = /obj/item/restraints/handcuffs/cable/zipties/used + +/obj/item/restraints/handcuffs/cable/zipties/cyborg/attack(mob/living/carbon/C, mob/user) + if(isrobot(user)) + cuff(C, user, FALSE) + +/obj/item/restraints/handcuffs/cable/zipties/used + desc = "A pair of broken zipties." + icon_state = "cuff_white_used" + +/obj/item/restraints/handcuffs/cable/zipties/used/attack() + return diff --git a/code/game/objects/items/weapons/holosign.dm b/code/game/objects/items/weapons/holosign.dm index 12767cb95633..1828d38628c2 100644 --- a/code/game/objects/items/weapons/holosign.dm +++ b/code/game/objects/items/weapons/holosign.dm @@ -120,4 +120,4 @@ if(signs.len) for(var/H in signs) qdel(H) - to_chat(user, "You clear all active holograms.") \ No newline at end of file + to_chat(user, "You clear all active holograms.") diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm index 95cf1e7026a0..4efb6994e8dd 100644 --- a/code/game/objects/items/weapons/holy_weapons.dm +++ b/code/game/objects/items/weapons/holy_weapons.dm @@ -37,7 +37,7 @@ user.visible_message("[src] slips out of the grip of [user] as they try to pick it up, bouncing upwards and smacking [user.p_them()] in the face!", \ "[src] slips out of your grip as you pick it up, bouncing upwards and smacking you in the face!") playsound(get_turf(user), 'sound/effects/hit_punch.ogg', 50, 1, -1) - throw_at(get_edge_target_turf(user, pick(alldirs)), rand(1, 3), 5) + throw_at(get_edge_target_turf(user, pick(GLOB.alldirs)), rand(1, 3), 5) /obj/item/nullrod/attack_self(mob/user) diff --git a/code/game/objects/items/weapons/implants/health.dm b/code/game/objects/items/weapons/implants/health.dm index d009a2037d23..e40006f4a2a7 100644 --- a/code/game/objects/items/weapons/implants/health.dm +++ b/code/game/objects/items/weapons/implants/health.dm @@ -8,4 +8,4 @@ return "ERROR" else healthstring = "[round(imp_in.getOxyLoss())] - [round(imp_in.getFireLoss())] - [round(imp_in.getToxLoss())] - [round(imp_in.getBruteLoss())]" - return healthstring \ No newline at end of file + return healthstring diff --git a/code/game/objects/items/weapons/implants/implant.dm b/code/game/objects/items/weapons/implants/implant.dm index 88dc4ed65425..9dc1fe864fc1 100644 --- a/code/game/objects/items/weapons/implants/implant.dm +++ b/code/game/objects/items/weapons/implants/implant.dm @@ -1,87 +1,87 @@ -/obj/item/implant - name = "implant" - icon = 'icons/obj/implants.dmi' - icon_state = "generic" //Shows up as the action button icon - origin_tech = "materials=2;biotech=3;programming=2" - - actions_types = list(/datum/action/item_action/hands_free/activate) - var/activated = 1 //1 for implant types that can be activated, 0 for ones that are "always on" like mindshield implants - var/implanted = null - var/mob/living/imp_in = null - item_color = "b" - var/allow_multiple = 0 - var/uses = -1 - flags = DROPDEL - - -/obj/item/implant/proc/trigger(emote, mob/source, force) - return - -/obj/item/implant/proc/activate() - return - -/obj/item/implant/ui_action_click() - activate("action_button") - - -//What does the implant do upon injection? -//return 1 if the implant injects -//return -1 if the implant fails to inject -//return 0 if there is no room for implant -/obj/item/implant/proc/implant(var/mob/source, var/mob/user) - var/obj/item/implant/imp_e = locate(src.type) in source - if(!allow_multiple && imp_e && imp_e != src) - if(imp_e.uses < initial(imp_e.uses)*2) - if(uses == -1) - imp_e.uses = -1 - else - imp_e.uses = min(imp_e.uses + uses, initial(imp_e.uses)*2) - qdel(src) - return 1 - else - return 0 - - - src.loc = source - imp_in = source - implanted = 1 - if(activated) - for(var/X in actions) - var/datum/action/A = X - A.Grant(source) - if(ishuman(source)) - var/mob/living/carbon/human/H = source - H.sec_hud_set_implants() - - if(user) - add_attack_logs(user, source, "Implanted with [src]") - - return 1 - -/obj/item/implant/proc/removed(var/mob/source) - src.loc = null - imp_in = null - implanted = 0 - - for(var/X in actions) - var/datum/action/A = X - A.Grant(source) - - if(ishuman(source)) - var/mob/living/carbon/human/H = source - H.sec_hud_set_implants() - - return 1 - -/obj/item/implant/Destroy() - if(imp_in) - removed(imp_in) - return ..() - - -/obj/item/implant/proc/get_data() - return "No information available" - -/obj/item/implant/dropped(mob/user) - . = 1 - ..() \ No newline at end of file +/obj/item/implant + name = "implant" + icon = 'icons/obj/implants.dmi' + icon_state = "generic" //Shows up as the action button icon + origin_tech = "materials=2;biotech=3;programming=2" + + actions_types = list(/datum/action/item_action/hands_free/activate) + var/activated = 1 //1 for implant types that can be activated, 0 for ones that are "always on" like mindshield implants + var/implanted = null + var/mob/living/imp_in = null + item_color = "b" + var/allow_multiple = 0 + var/uses = -1 + flags = DROPDEL + + +/obj/item/implant/proc/trigger(emote, mob/source, force) + return + +/obj/item/implant/proc/activate() + return + +/obj/item/implant/ui_action_click() + activate("action_button") + + +//What does the implant do upon injection? +//return 1 if the implant injects +//return -1 if the implant fails to inject +//return 0 if there is no room for implant +/obj/item/implant/proc/implant(var/mob/source, var/mob/user) + var/obj/item/implant/imp_e = locate(src.type) in source + if(!allow_multiple && imp_e && imp_e != src) + if(imp_e.uses < initial(imp_e.uses)*2) + if(uses == -1) + imp_e.uses = -1 + else + imp_e.uses = min(imp_e.uses + uses, initial(imp_e.uses)*2) + qdel(src) + return 1 + else + return 0 + + + src.loc = source + imp_in = source + implanted = 1 + if(activated) + for(var/X in actions) + var/datum/action/A = X + A.Grant(source) + if(ishuman(source)) + var/mob/living/carbon/human/H = source + H.sec_hud_set_implants() + + if(user) + add_attack_logs(user, source, "Implanted with [src]") + + return 1 + +/obj/item/implant/proc/removed(var/mob/source) + src.loc = null + imp_in = null + implanted = 0 + + for(var/X in actions) + var/datum/action/A = X + A.Grant(source) + + if(ishuman(source)) + var/mob/living/carbon/human/H = source + H.sec_hud_set_implants() + + return 1 + +/obj/item/implant/Destroy() + if(imp_in) + removed(imp_in) + return ..() + + +/obj/item/implant/proc/get_data() + return "No information available" + +/obj/item/implant/dropped(mob/user) + . = 1 + ..() diff --git a/code/game/objects/items/weapons/implants/implant_abductor.dm b/code/game/objects/items/weapons/implants/implant_abductor.dm index 64bb7345f7ac..8967f364078e 100644 --- a/code/game/objects/items/weapons/implants/implant_abductor.dm +++ b/code/game/objects/items/weapons/implants/implant_abductor.dm @@ -44,4 +44,4 @@ if(c.team == team) console = c break - return console \ No newline at end of file + return console diff --git a/code/game/objects/items/weapons/implants/implant_death_alarm.dm b/code/game/objects/items/weapons/implants/implant_death_alarm.dm index d27d6dd5f7e0..e8330c0c5cdd 100644 --- a/code/game/objects/items/weapons/implants/implant_death_alarm.dm +++ b/code/game/objects/items/weapons/implants/implant_death_alarm.dm @@ -47,7 +47,7 @@ a.autosay("[mobname] has died in [t.name]!", "[mobname]'s Death Alarm") qdel(src) if("emp") - var/name = prob(50) ? t.name : pick(teleportlocs) + var/name = prob(50) ? t.name : pick(GLOB.teleportlocs) a.autosay("[mobname] has died in [name]!", "[mobname]'s Death Alarm") else a.autosay("[mobname] has died-zzzzt in-in-in...", "[mobname]'s Death Alarm") diff --git a/code/game/objects/items/weapons/implants/implant_explosive.dm b/code/game/objects/items/weapons/implants/implant_explosive.dm index f37e148bee6e..52262ac8edbe 100644 --- a/code/game/objects/items/weapons/implants/implant_explosive.dm +++ b/code/game/objects/items/weapons/implants/implant_explosive.dm @@ -174,4 +174,4 @@ /obj/item/implanter/dust/New() imp = new /obj/item/implant/dust(src) - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/items/weapons/implants/implant_krav_maga.dm b/code/game/objects/items/weapons/implants/implant_krav_maga.dm index 1a633d801510..3c2666f3d668 100644 --- a/code/game/objects/items/weapons/implants/implant_krav_maga.dm +++ b/code/game/objects/items/weapons/implants/implant_krav_maga.dm @@ -37,4 +37,4 @@ /obj/item/implantcase/krav_maga/New() imp = new /obj/item/implant/krav_maga(src) - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/items/weapons/implants/implant_mindshield.dm b/code/game/objects/items/weapons/implants/implant_mindshield.dm index bc942b73d81d..f9cf7e4cff13 100644 --- a/code/game/objects/items/weapons/implants/implant_mindshield.dm +++ b/code/game/objects/items/weapons/implants/implant_mindshield.dm @@ -56,4 +56,4 @@ /obj/item/implantcase/mindshield/New() imp = new /obj/item/implant/mindshield(src) - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/items/weapons/implants/implant_track.dm b/code/game/objects/items/weapons/implants/implant_track.dm index c74e2fecff55..af37768f4697 100644 --- a/code/game/objects/items/weapons/implants/implant_track.dm +++ b/code/game/objects/items/weapons/implants/implant_track.dm @@ -37,4 +37,4 @@ /obj/item/implantcase/track/New() imp = new /obj/item/implant/tracking(src) - ..() \ No newline at end of file + ..() diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index df7ecdceb6d4..965c5ca7d94d 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -1,102 +1,102 @@ -/obj/item/implantcase - name = "implant case" - desc = "A glass case containing an implant." - icon = 'icons/obj/items.dmi' - icon_state = "implantcase-0" - item_state = "implantcase" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - origin_tech = "materials=1;biotech=2" - container_type = OPENCONTAINER | INJECTABLE | DRAWABLE - materials = list(MAT_GLASS=500) - var/obj/item/implant/imp = null - - -/obj/item/implantcase/update_icon() - if(imp) - icon_state = "implantcase-[imp.item_color]" - origin_tech = imp.origin_tech - flags = imp.flags & ~DROPDEL - reagents = imp.reagents - else - icon_state = "implantcase-0" - origin_tech = initial(origin_tech) - flags = initial(flags) - reagents = null - - -/obj/item/implantcase/attackby(obj/item/W, mob/user, params) - ..() - if(istype(W, /obj/item/pen)) - var/t = stripped_input(user, "What would you like the label to be?", name, null) - if(user.get_active_hand() != W) - return - if(!in_range(src, user) && loc != user) - return - if(t) - name = "implant case - '[t]'" - else - name = "implant case" - else if(istype(W, /obj/item/implanter)) - var/obj/item/implanter/I = W - if(I.imp) - if(imp || I.imp.implanted) - return - I.imp.loc = src - imp = I.imp - I.imp = null - update_icon() - I.update_icon() - else - if(imp) - if(I.imp) - return - imp.loc = I - I.imp = imp - imp = null - update_icon() - I.update_icon() - - /*else if(istype(W, /obj/item/ammo_casing/shotgun/implanter)) - var/obj/item/ammo_casing/shotgun/implanter/I = W - if(I.implanter) - src.attackby(I.implanter, user, params) */ // COMING SOON -- c0 - -/obj/item/implantcase/New() - ..() - update_icon() - - -/obj/item/implantcase/tracking - name = "implant case - 'Tracking'" - desc = "A glass case containing a tracking implant." - -/obj/item/implantcase/tracking/New() - imp = new /obj/item/implant/tracking(src) - ..() - - -/obj/item/implantcase/weapons_auth - name = "implant case - 'Firearms Authentication'" - desc = "A glass case containing a firearms authentication implant." - -/obj/item/implantcase/weapons_auth/New() - imp = new /obj/item/implant/weapons_auth(src) - ..() - -/obj/item/implantcase/adrenaline - name = "implant case - 'Adrenaline'" - desc = "A glass case containing an adrenaline implant." - -/obj/item/implantcase/adrenaline/New() - imp = new /obj/item/implant/adrenalin(src) - ..() - -/obj/item/implantcase/death_alarm - name = "Glass Case- 'Death Alarm'" - desc = "A case containing a death alarm implant." - -/obj/item/implantcase/death_alarm/New() - imp = new /obj/item/implant/death_alarm(src) - ..() +/obj/item/implantcase + name = "implant case" + desc = "A glass case containing an implant." + icon = 'icons/obj/items.dmi' + icon_state = "implantcase-0" + item_state = "implantcase" + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + origin_tech = "materials=1;biotech=2" + container_type = OPENCONTAINER | INJECTABLE | DRAWABLE + materials = list(MAT_GLASS=500) + var/obj/item/implant/imp = null + + +/obj/item/implantcase/update_icon() + if(imp) + icon_state = "implantcase-[imp.item_color]" + origin_tech = imp.origin_tech + flags = imp.flags & ~DROPDEL + reagents = imp.reagents + else + icon_state = "implantcase-0" + origin_tech = initial(origin_tech) + flags = initial(flags) + reagents = null + + +/obj/item/implantcase/attackby(obj/item/W, mob/user, params) + ..() + if(istype(W, /obj/item/pen)) + var/t = stripped_input(user, "What would you like the label to be?", name, null) + if(user.get_active_hand() != W) + return + if(!in_range(src, user) && loc != user) + return + if(t) + name = "implant case - '[t]'" + else + name = "implant case" + else if(istype(W, /obj/item/implanter)) + var/obj/item/implanter/I = W + if(I.imp) + if(imp || I.imp.implanted) + return + I.imp.loc = src + imp = I.imp + I.imp = null + update_icon() + I.update_icon() + else + if(imp) + if(I.imp) + return + imp.loc = I + I.imp = imp + imp = null + update_icon() + I.update_icon() + + /*else if(istype(W, /obj/item/ammo_casing/shotgun/implanter)) + var/obj/item/ammo_casing/shotgun/implanter/I = W + if(I.implanter) + src.attackby(I.implanter, user, params) */ // COMING SOON -- c0 + +/obj/item/implantcase/New() + ..() + update_icon() + + +/obj/item/implantcase/tracking + name = "implant case - 'Tracking'" + desc = "A glass case containing a tracking implant." + +/obj/item/implantcase/tracking/New() + imp = new /obj/item/implant/tracking(src) + ..() + + +/obj/item/implantcase/weapons_auth + name = "implant case - 'Firearms Authentication'" + desc = "A glass case containing a firearms authentication implant." + +/obj/item/implantcase/weapons_auth/New() + imp = new /obj/item/implant/weapons_auth(src) + ..() + +/obj/item/implantcase/adrenaline + name = "implant case - 'Adrenaline'" + desc = "A glass case containing an adrenaline implant." + +/obj/item/implantcase/adrenaline/New() + imp = new /obj/item/implant/adrenalin(src) + ..() + +/obj/item/implantcase/death_alarm + name = "Glass Case- 'Death Alarm'" + desc = "A case containing a death alarm implant." + +/obj/item/implantcase/death_alarm/New() + imp = new /obj/item/implant/death_alarm(src) + ..() diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index 5727fe08cd01..d74ab31a6c33 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -1,158 +1,158 @@ -/obj/machinery/implantchair - name = "mindshield implanter" - desc = "Used to implant occupants with mindshield implants." - icon = 'icons/obj/machines/implantchair.dmi' - icon_state = "implantchair" - density = 1 - opacity = 0 - anchored = 1 - - var/ready = 1 - var/malfunction = 0 - var/list/obj/item/implant/mindshield/implant_list = list() - var/max_implants = 5 - var/injection_cooldown = 600 - var/replenish_cooldown = 6000 - var/replenishing = 0 - var/mob/living/carbon/occupant = null - var/injecting = 0 - -/obj/machinery/implantchair/proc - go_out() - put_mob(mob/living/carbon/M) - implant(var/mob/M) - add_implants() - - -/obj/machinery/implantchair/New() - ..() - add_implants() - - -/obj/machinery/implantchair/attack_hand(mob/user) - user.set_machine(src) - var/health_text = "" - if(src.occupant) - if(src.occupant.health <= -100) - health_text = "Dead" - else if(src.occupant.health < 0) - health_text = "[round(src.occupant.health,0.1)]" - else - health_text = "[round(src.occupant.health,0.1)]" - - var/dat ="Implanter Status
    " - - dat +="Current occupant: [src.occupant ? "
    Name: [src.occupant]
    Health: [health_text]
    " : "None"]
    " - dat += "Implants: [src.implant_list.len ? "[implant_list.len]" : "Replenish"]
    " - if(src.occupant) - dat += "[src.ready ? "Implant" : "Recharging"]
    " - user.set_machine(src) - user << browse(dat, "window=implant") - onclose(user, "implant") - - -/obj/machinery/implantchair/Topic(href, href_list) - if(..()) - return - if(href_list["implant"]) - if(src.occupant) - injecting = 1 - go_out() - ready = 0 - spawn(injection_cooldown) - ready = 1 - - if(href_list["replenish"]) - ready = 0 - spawn(replenish_cooldown) - add_implants() - ready = 1 - - src.updateUsrDialog() - return - - -/obj/machinery/implantchair/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/grab)) - var/obj/item/grab/G = W - if(!ismob(G.affecting)) - return - var/mob/M = G.affecting - if(M.has_buckled_mobs()) - to_chat(user, "[M] will not fit into [src] because [M.p_they()] [M.p_have()] a slime latched onto [M.p_their()] head.") - return - if(put_mob(M)) - qdel(G) - src.updateUsrDialog() - return - - -/obj/machinery/implantchair/go_out(mob/M) - if(!( src.occupant )) - return - if(M == occupant) // so that the guy inside can't eject himself -Agouri - return - occupant.forceMove(loc) - if(injecting) - implant(src.occupant) - injecting = 0 - src.occupant = null - icon_state = "implantchair" - return - - -/obj/machinery/implantchair/put_mob(mob/living/carbon/M) - if(!iscarbon(M)) - to_chat(usr, "The [src.name] cannot hold this!") - return - if(src.occupant) - to_chat(usr, "The [src.name] is already occupied!") - return - M.stop_pulling() - M.forceMove(src) - src.occupant = M - src.add_fingerprint(usr) - icon_state = "implantchair_on" - return 1 - - -/obj/machinery/implantchair/implant(mob/M) - if(!istype(M, /mob/living/carbon)) - return - if(!implant_list.len) return - for(var/obj/item/implant/mindshield/imp in implant_list) - if(!imp) continue - if(istype(imp, /obj/item/implant/mindshield)) - M.visible_message("[M] has been implanted by the [src.name].") - - if(imp.implant(M)) - implant_list -= imp - break - return - - -/obj/machinery/implantchair/add_implants() - for(var/i=0, iName: [src.occupant]
    Health: [health_text]
    " : "None"]
    " + dat += "Implants: [src.implant_list.len ? "[implant_list.len]" : "Replenish"]
    " + if(src.occupant) + dat += "[src.ready ? "Implant" : "Recharging"]
    " + user.set_machine(src) + user << browse(dat, "window=implant") + onclose(user, "implant") + + +/obj/machinery/implantchair/Topic(href, href_list) + if(..()) + return + if(href_list["implant"]) + if(src.occupant) + injecting = 1 + go_out() + ready = 0 + spawn(injection_cooldown) + ready = 1 + + if(href_list["replenish"]) + ready = 0 + spawn(replenish_cooldown) + add_implants() + ready = 1 + + src.updateUsrDialog() + return + + +/obj/machinery/implantchair/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/grab)) + var/obj/item/grab/G = W + if(!ismob(G.affecting)) + return + var/mob/M = G.affecting + if(M.has_buckled_mobs()) + to_chat(user, "[M] will not fit into [src] because [M.p_they()] [M.p_have()] a slime latched onto [M.p_their()] head.") + return + if(put_mob(M)) + qdel(G) + src.updateUsrDialog() + return + + +/obj/machinery/implantchair/go_out(mob/M) + if(!( src.occupant )) + return + if(M == occupant) // so that the guy inside can't eject himself -Agouri + return + occupant.forceMove(loc) + if(injecting) + implant(src.occupant) + injecting = 0 + src.occupant = null + icon_state = "implantchair" + return + + +/obj/machinery/implantchair/put_mob(mob/living/carbon/M) + if(!iscarbon(M)) + to_chat(usr, "The [src.name] cannot hold this!") + return + if(src.occupant) + to_chat(usr, "The [src.name] is already occupied!") + return + M.stop_pulling() + M.forceMove(src) + src.occupant = M + src.add_fingerprint(usr) + icon_state = "implantchair_on" + return 1 + + +/obj/machinery/implantchair/implant(mob/M) + if(!istype(M, /mob/living/carbon)) + return + if(!implant_list.len) return + for(var/obj/item/implant/mindshield/imp in implant_list) + if(!imp) continue + if(istype(imp, /obj/item/implant/mindshield)) + M.visible_message("[M] has been implanted by the [src.name].") + + if(imp.implant(M)) + implant_list -= imp + break + return + + +/obj/machinery/implantchair/add_implants() + for(var/i=0, i[user] is attemping to implant [M].
    ") - - var/turf/T = get_turf(M) - if(T && (M == user || do_after(user, 50 * toolspeed, target = M))) - if(user && M && (get_turf(M) == T) && src && imp) - if(imp.implant(M, user)) - if(M == user) - to_chat(user, "You implant yourself.") - else - M.visible_message("[user] has implanted [M].", "[user] implants you.") - imp = null - update_icon() - -/obj/item/implanter/attackby(obj/item/W, mob/user, params) - ..() - if(istype(W, /obj/item/pen)) - var/t = stripped_input(user, "What would you like the label to be?", name, null) - if(user.get_active_hand() != W) - return - if(!in_range(src, user) && loc != user) - return - if(t) - name = "implanter ([t])" - else - name = "implanter" - -/obj/item/implanter/New() - ..() - spawn(1) - update_icon() - - -/obj/item/implanter/adrenalin - name = "implanter (adrenalin)" - -/obj/item/implanter/adrenalin/New() - imp = new /obj/item/implant/adrenalin(src) - ..() - - -/obj/item/implanter/emp - name = "implanter (EMP)" - -/obj/item/implanter/emp/New() - imp = new /obj/item/implant/emp(src) - ..() - -/obj/item/implanter/traitor - name = "implanter (Mindslave)" - -/obj/item/implanter/traitor/New() - imp = new /obj/item/implant/traitor(src) - ..() - -/obj/item/implanter/death_alarm - name = "implanter (Death Alarm)" - -/obj/item/implanter/death_alarm/New() - imp = new /obj/item/implant/death_alarm(src) - ..() +/obj/item/implanter + name = "implanter" + desc = "A sterile automatic implant injector." + icon = 'icons/obj/items.dmi' + icon_state = "implanter0" + item_state = "syringe_0" + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + origin_tech = "materials=2;biotech=3" + materials = list(MAT_METAL=600, MAT_GLASS=200) + toolspeed = 1 + var/obj/item/implant/imp = null + + +/obj/item/implanter/update_icon() + if(imp) + icon_state = "implanter1" + origin_tech = imp.origin_tech + else + icon_state = "implanter0" + origin_tech = initial(origin_tech) + + +/obj/item/implanter/attack(mob/living/carbon/M, mob/user) + if(!iscarbon(M)) + return + if(user && imp) + if(M != user) + M.visible_message("[user] is attemping to implant [M].") + + var/turf/T = get_turf(M) + if(T && (M == user || do_after(user, 50 * toolspeed, target = M))) + if(user && M && (get_turf(M) == T) && src && imp) + if(imp.implant(M, user)) + if(M == user) + to_chat(user, "You implant yourself.") + else + M.visible_message("[user] has implanted [M].", "[user] implants you.") + imp = null + update_icon() + +/obj/item/implanter/attackby(obj/item/W, mob/user, params) + ..() + if(istype(W, /obj/item/pen)) + var/t = stripped_input(user, "What would you like the label to be?", name, null) + if(user.get_active_hand() != W) + return + if(!in_range(src, user) && loc != user) + return + if(t) + name = "implanter ([t])" + else + name = "implanter" + +/obj/item/implanter/New() + ..() + spawn(1) + update_icon() + + +/obj/item/implanter/adrenalin + name = "implanter (adrenalin)" + +/obj/item/implanter/adrenalin/New() + imp = new /obj/item/implant/adrenalin(src) + ..() + + +/obj/item/implanter/emp + name = "implanter (EMP)" + +/obj/item/implanter/emp/New() + imp = new /obj/item/implant/emp(src) + ..() + +/obj/item/implanter/traitor + name = "implanter (Mindslave)" + +/obj/item/implanter/traitor/New() + imp = new /obj/item/implant/traitor(src) + ..() + +/obj/item/implanter/death_alarm + name = "implanter (Death Alarm)" + +/obj/item/implanter/death_alarm/New() + imp = new /obj/item/implant/death_alarm(src) + ..() diff --git a/code/game/objects/items/weapons/implants/implantpad.dm b/code/game/objects/items/weapons/implants/implantpad.dm index b9b93b035ec9..5f1cf0ae1033 100644 --- a/code/game/objects/items/weapons/implants/implantpad.dm +++ b/code/game/objects/items/weapons/implants/implantpad.dm @@ -1,107 +1,107 @@ -/obj/item/implantpad - name = "implantpad" - desc = "Used to modify implants." - icon = 'icons/obj/items.dmi' - icon_state = "implantpad-0" - item_state = "electronic" - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/obj/item/implantcase/case = null - -/obj/item/implantpad/Destroy() - if(case) - dropcase() - return ..() - -/obj/item/implantpad/update_icon() - if(case) - src.icon_state = "implantpad-1" - else - src.icon_state = "implantpad-0" - return - -/obj/item/implantpad/proc/addcase(mob/user as mob, obj/item/implantcase/C as obj) - if(!user || !C) - return - if(case) - to_chat(user, "There's already an implant in the pad!") - return - user.unEquip(C) - C.forceMove(src) - case = C - update_icon() - -/obj/item/implantpad/proc/dropcase(mob/user as mob) - if(!case) - to_chat(user, "There's no implant in the pad!") - return - if(user) - if(user.put_in_hands(case)) - add_fingerprint(user) - case.add_fingerprint(user) - case = null - update_icon() - return - - case.forceMove(get_turf(src)) - case = null - update_icon() - -/obj/item/implantpad/verb/remove_implant() - set category = "Object" - set name = "Remove Implant" - set src in usr - - if(usr.stat || usr.restrained()) - return - - dropcase(usr) - -/obj/item/implantpad/attackby(obj/item/implantcase/C as obj, mob/user as mob, params) - if(istype(C, /obj/item/implantcase)) - addcase(user, C) - else - return ..() - -/obj/item/implantpad/attack_self(mob/user as mob) - add_fingerprint(user) - user.set_machine(src) - var/dat = "Implant Mini-Computer:
    " - if(case) - if(case.imp) - if(istype(case.imp, /obj/item/implant)) - dat += "Remove Case
    " - dat += case.imp.get_data() - if(istype(case.imp, /obj/item/implant/tracking)) - var/obj/item/implant/tracking/T = case.imp - dat += {"ID (1-100): - - - - [T.id] - + - +
    "} - else - dat += "The implant casing is empty." - else - dat += "Please insert an implant casing!" - user << browse(dat, "window=implantpad") - onclose(user, "implantpad") - return - - -/obj/item/implantpad/Topic(href, href_list) - if(..()) - return 1 - - var/mob/living/user = usr - if(href_list["tracking_id"]) - if(case && case.imp) - var/obj/item/implant/tracking/T = case.imp - T.id += text2num(href_list["tracking_id"]) - T.id = min(100, T.id) - T.id = max(1, T.id) - else if(href_list["removecase"]) - dropcase(user) - - attack_self(user) - return 1 +/obj/item/implantpad + name = "implantpad" + desc = "Used to modify implants." + icon = 'icons/obj/items.dmi' + icon_state = "implantpad-0" + item_state = "electronic" + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + var/obj/item/implantcase/case = null + +/obj/item/implantpad/Destroy() + if(case) + dropcase() + return ..() + +/obj/item/implantpad/update_icon() + if(case) + src.icon_state = "implantpad-1" + else + src.icon_state = "implantpad-0" + return + +/obj/item/implantpad/proc/addcase(mob/user as mob, obj/item/implantcase/C as obj) + if(!user || !C) + return + if(case) + to_chat(user, "There's already an implant in the pad!") + return + user.unEquip(C) + C.forceMove(src) + case = C + update_icon() + +/obj/item/implantpad/proc/dropcase(mob/user as mob) + if(!case) + to_chat(user, "There's no implant in the pad!") + return + if(user) + if(user.put_in_hands(case)) + add_fingerprint(user) + case.add_fingerprint(user) + case = null + update_icon() + return + + case.forceMove(get_turf(src)) + case = null + update_icon() + +/obj/item/implantpad/verb/remove_implant() + set category = "Object" + set name = "Remove Implant" + set src in usr + + if(usr.stat || usr.restrained()) + return + + dropcase(usr) + +/obj/item/implantpad/attackby(obj/item/implantcase/C as obj, mob/user as mob, params) + if(istype(C, /obj/item/implantcase)) + addcase(user, C) + else + return ..() + +/obj/item/implantpad/attack_self(mob/user as mob) + add_fingerprint(user) + user.set_machine(src) + var/dat = "Implant Mini-Computer:
    " + if(case) + if(case.imp) + if(istype(case.imp, /obj/item/implant)) + dat += "Remove Case
    " + dat += case.imp.get_data() + if(istype(case.imp, /obj/item/implant/tracking)) + var/obj/item/implant/tracking/T = case.imp + dat += {"ID (1-100): + - + - [T.id] + + + +
    "} + else + dat += "The implant casing is empty." + else + dat += "Please insert an implant casing!" + user << browse(dat, "window=implantpad") + onclose(user, "implantpad") + return + + +/obj/item/implantpad/Topic(href, href_list) + if(..()) + return 1 + + var/mob/living/user = usr + if(href_list["tracking_id"]) + if(case && case.imp) + var/obj/item/implant/tracking/T = case.imp + T.id += text2num(href_list["tracking_id"]) + T.id = min(100, T.id) + T.id = max(1, T.id) + else if(href_list["removecase"]) + dropcase(user) + + attack_self(user) + return 1 diff --git a/code/game/objects/items/weapons/implants/implantuplink.dm b/code/game/objects/items/weapons/implants/implantuplink.dm index d6d1bb5bac79..3d5e2e37591c 100644 --- a/code/game/objects/items/weapons/implants/implantuplink.dm +++ b/code/game/objects/items/weapons/implants/implantuplink.dm @@ -1,45 +1,45 @@ -/obj/item/implant/uplink - name = "uplink implant" - desc = "Summon things." - icon = 'icons/obj/radio.dmi' - icon_state = "radio" - origin_tech = "materials=4;magnets=4;programming=4;biotech=4;syndicate=5;bluespace=5" - -/obj/item/implant/uplink/New() - hidden_uplink = new(src) - hidden_uplink.uses = 10 - ..() - -/obj/item/implant/uplink/sit/New() - ..() - if(hidden_uplink) - hidden_uplink.uplink_type = "sit" - -/obj/item/implant/uplink/admin/New() - ..() - if(hidden_uplink) - hidden_uplink.uplink_type = "admin" - -/obj/item/implant/uplink/implant(mob/source) - var/obj/item/implant/imp_e = locate(src.type) in source - if(imp_e && imp_e != src) - imp_e.hidden_uplink.uses += hidden_uplink.uses - qdel(src) - return 1 - - if(..()) - hidden_uplink.uplink_owner="[source.key]" - return 1 - return 0 - -/obj/item/implant/uplink/activate() - if(hidden_uplink) - hidden_uplink.check_trigger(imp_in) - - -/obj/item/implanter/uplink - name = "implanter (uplink)" - -/obj/item/implanter/uplink/New() - imp = new /obj/item/implant/uplink(src) - ..() +/obj/item/implant/uplink + name = "uplink implant" + desc = "Summon things." + icon = 'icons/obj/radio.dmi' + icon_state = "radio" + origin_tech = "materials=4;magnets=4;programming=4;biotech=4;syndicate=5;bluespace=5" + +/obj/item/implant/uplink/New() + hidden_uplink = new(src) + hidden_uplink.uses = 10 + ..() + +/obj/item/implant/uplink/sit/New() + ..() + if(hidden_uplink) + hidden_uplink.uplink_type = "sit" + +/obj/item/implant/uplink/admin/New() + ..() + if(hidden_uplink) + hidden_uplink.uplink_type = "admin" + +/obj/item/implant/uplink/implant(mob/source) + var/obj/item/implant/imp_e = locate(src.type) in source + if(imp_e && imp_e != src) + imp_e.hidden_uplink.uses += hidden_uplink.uses + qdel(src) + return 1 + + if(..()) + hidden_uplink.uplink_owner="[source.key]" + return 1 + return 0 + +/obj/item/implant/uplink/activate() + if(hidden_uplink) + hidden_uplink.check_trigger(imp_in) + + +/obj/item/implanter/uplink + name = "implanter (uplink)" + +/obj/item/implanter/uplink/New() + imp = new /obj/item/implant/uplink(src) + ..() diff --git a/code/game/objects/items/weapons/kitchen.dm b/code/game/objects/items/weapons/kitchen.dm index 77f133b775ca..9934ce1c0ab9 100644 --- a/code/game/objects/items/weapons/kitchen.dm +++ b/code/game/objects/items/weapons/kitchen.dm @@ -1,307 +1,307 @@ -/* Kitchen tools - * Contains: - * Utensils - * Spoons - * Forks - * Knives - * Kitchen knives - * Butcher's cleaver - * Rolling Pins - * Candy Moulds - * Sushi Mat - * Circular cutter - */ - -/obj/item/kitchen - icon = 'icons/obj/kitchen.dmi' - origin_tech = "materials=1" - - - - -/* - * Utensils - */ -/obj/item/kitchen/utensil - force = 5.0 - w_class = WEIGHT_CLASS_TINY - throwforce = 0.0 - throw_speed = 3 - throw_range = 5 - flags = CONDUCT - attack_verb = list("attacked", "stabbed", "poked") - hitsound = 'sound/weapons/bladeslice.ogg' - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) - sharp = 0 - var/max_contents = 1 - -/obj/item/kitchen/utensil/New() - ..() - if(prob(60)) - src.pixel_y = rand(0, 4) - - create_reagents(5) - -/obj/item/kitchen/utensil/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(!istype(M)) - return ..() - - if(user.a_intent != INTENT_HELP) - if(user.zone_selected == "head" || user.zone_selected == "eyes") - if((CLUMSY in user.mutations) && prob(50)) - M = user - return eyestab(M,user) - else - return ..() - - if(contents.len) - var/obj/item/reagent_containers/food/snacks/toEat = contents[1] - if(istype(toEat)) - if(M.eat(toEat, user)) - toEat.On_Consume(M, user) - spawn(0) - if(toEat) - qdel(toEat) - overlays.Cut() - return - - -/obj/item/kitchen/utensil/fork - name = "fork" - desc = "It's a fork. Sure is pointy." - icon_state = "fork" - -/obj/item/kitchen/utensil/pfork - name = "plastic fork" - desc = "Yay, no washing up to do." - icon_state = "pfork" - -/obj/item/kitchen/utensil/spoon - name = "spoon" - desc = "It's a spoon. You can see your own upside-down face in it." - icon_state = "spoon" - attack_verb = list("attacked", "poked") - -/obj/item/kitchen/utensil/pspoon - name = "plastic spoon" - desc = "It's a plastic spoon. How dull." - icon_state = "pspoon" - attack_verb = list("attacked", "poked") - -/obj/item/kitchen/utensil/spork - name = "spork" - desc = "It's a spork. Marvel at its innovative design." - icon_state = "spork" - attack_verb = list("attacked", "sporked") - -/obj/item/kitchen/utensil/pspork - name = "plastic spork" - desc = "It's a plastic spork. It's the fork side of the spoon!" - icon_state = "pspork" - attack_verb = list("attacked", "sporked") - -/* - * Knives - */ -/obj/item/kitchen/knife - name = "kitchen knife" - icon_state = "knife" - desc = "A general purpose Chef's Knife made by SpaceCook Incorporated. Guaranteed to stay sharp for years to come." - flags = CONDUCT - force = 10 - w_class = WEIGHT_CLASS_SMALL - throwforce = 10 - hitsound = 'sound/weapons/bladeslice.ogg' - throw_speed = 3 - throw_range = 6 - materials = list(MAT_METAL=12000) - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - sharp = TRUE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - var/bayonet = FALSE //Can this be attached to a gun? - -/obj/item/kitchen/knife/suicide_act(mob/user) - user.visible_message(pick("[user] is slitting [user.p_their()] wrists with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.", \ - "[user] is slitting [user.p_their()] throat with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.", \ - "[user] is slitting [user.p_their()] stomach open with the [name]! It looks like [user.p_theyre()] trying to commit seppuku.")) - return BRUTELOSS - -/obj/item/kitchen/knife/plastic - name = "plastic knife" - desc = "The bluntest of blades." - icon_state = "pknife" - item_state = "knife" - sharp = 0 - -/obj/item/kitchen/knife/ritual - name = "ritual knife" - desc = "The unearthly energies that once powered this blade are now dormant." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - w_class = WEIGHT_CLASS_NORMAL - -/obj/item/kitchen/knife/butcher - name = "butcher's cleaver" - icon_state = "butch" - desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." - flags = CONDUCT - force = 15 - throwforce = 8 - attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - w_class = WEIGHT_CLASS_NORMAL - -/obj/item/kitchen/knife/butcher/meatcleaver - name = "meat cleaver" - icon_state = "mcleaver" - item_state = "butch" - force = 25 - throwforce = 15 - -/obj/item/kitchen/knife/combat - name = "combat knife" - icon_state = "combatknife" - item_state = "knife" - desc = "A military combat utility survival knife." - force = 20 - throwforce = 20 - origin_tech = "materials=3;combat=4" - attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "cut") - bayonet = TRUE - -/obj/item/kitchen/knife/combat/survival - name = "survival knife" - icon_state = "survivalknife" - desc = "A hunting grade survival knife." - force = 15 - throwforce = 15 - -/obj/item/kitchen/knife/combat/survival/bone - name = "bone dagger" - item_state = "bone_dagger" - icon_state = "bone_dagger" - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - desc = "A sharpened bone. The bare minimum in survival." - materials = list() - -/obj/item/kitchen/knife/combat/cyborg - name = "cyborg knife" - icon = 'icons/obj/items_cyborg.dmi' - icon_state = "knife" - desc = "A cyborg-mounted plasteel knife. Extremely sharp and durable." - origin_tech = null - -/obj/item/kitchen/knife/carrotshiv - name = "carrot shiv" - icon_state = "carrotshiv" - item_state = "carrotshiv" - desc = "Unlike other carrots, you should probably keep this far away from your eyes." - force = 8 - throwforce = 12 //fuck git - materials = list() - origin_tech = "biotech=3;combat=2" - attack_verb = list("shanked", "shivved") - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - - -/* - * Rolling Pins - */ - -/obj/item/kitchen/rollingpin - name = "rolling pin" - desc = "Used to knock out the Bartender." - icon_state = "rolling_pin" - force = 8.0 - throwforce = 10.0 - throw_speed = 3 - throw_range = 7 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") - -/* Trays moved to /obj/item/storage/bag */ - -/* - * Candy Moulds - */ - -/obj/item/kitchen/mould - name = "generic candy mould" - desc = "You aren't sure what it's supposed to be." - icon_state = "mould" - force = 5 - throwforce = 5 - throw_speed = 3 - throw_range = 3 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "smashed") - -/obj/item/kitchen/mould/bear - name = "bear-shaped candy mould" - desc = "It has the shape of a small bear imprinted into it." - icon_state = "mould_bear" - -/obj/item/kitchen/mould/worm - name = "worm-shaped candy mould" - desc = "It has the shape of a worm imprinted into it." - icon_state = "mould_worm" - -/obj/item/kitchen/mould/bean - name = "bean-shaped candy mould" - desc = "It has the shape of a bean imprinted into it." - icon_state = "mould_bean" - -/obj/item/kitchen/mould/ball - name = "ball-shaped candy mould" - desc = "It has a small sphere imprinted into it." - icon_state = "mould_ball" - -/obj/item/kitchen/mould/cane - name = "cane-shaped candy mould" - desc = "It has the shape of a cane imprinted into it." - icon_state = "mould_cane" - -/obj/item/kitchen/mould/cash - name = "cash-shaped candy mould" - desc = "It has the shape and design of fake money imprinted into it." - icon_state = "mould_cash" - -/obj/item/kitchen/mould/coin - name = "coin-shaped candy mould" - desc = "It has the shape of a coin imprinted into it." - icon_state = "mould_coin" - -/obj/item/kitchen/mould/loli - name = "sucker mould" - desc = "It has the shape of a sucker imprinted into it." - icon_state = "mould_loli" - -/* - * Sushi Mat - */ -/obj/item/kitchen/sushimat - name = "Sushi Mat" - desc = "A wooden mat used for efficient sushi crafting." - icon_state = "sushi_mat" - force = 5 - throwforce = 5 - throw_speed = 3 - throw_range = 3 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("rolled", "cracked", "battered", "thrashed") - - - -/// circular cutter by Ume - -/obj/item/kitchen/cutter - name = "generic circular cutter" - desc = "A generic circular cutter for cookies and other things." - icon = 'icons/obj/kitchen.dmi' - icon_state = "circular_cutter" - force = 5 - throwforce = 5 - throw_speed = 3 - throw_range = 3 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("bashed", "slashed", "pricked", "thrashed") \ No newline at end of file +/* Kitchen tools + * Contains: + * Utensils + * Spoons + * Forks + * Knives + * Kitchen knives + * Butcher's cleaver + * Rolling Pins + * Candy Moulds + * Sushi Mat + * Circular cutter + */ + +/obj/item/kitchen + icon = 'icons/obj/kitchen.dmi' + origin_tech = "materials=1" + + + + +/* + * Utensils + */ +/obj/item/kitchen/utensil + force = 5.0 + w_class = WEIGHT_CLASS_TINY + throwforce = 0.0 + throw_speed = 3 + throw_range = 5 + flags = CONDUCT + attack_verb = list("attacked", "stabbed", "poked") + hitsound = 'sound/weapons/bladeslice.ogg' + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + sharp = 0 + var/max_contents = 1 + +/obj/item/kitchen/utensil/New() + ..() + if(prob(60)) + src.pixel_y = rand(0, 4) + + create_reagents(5) + +/obj/item/kitchen/utensil/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(!istype(M)) + return ..() + + if(user.a_intent != INTENT_HELP) + if(user.zone_selected == "head" || user.zone_selected == "eyes") + if((CLUMSY in user.mutations) && prob(50)) + M = user + return eyestab(M,user) + else + return ..() + + if(contents.len) + var/obj/item/reagent_containers/food/snacks/toEat = contents[1] + if(istype(toEat)) + if(M.eat(toEat, user)) + toEat.On_Consume(M, user) + spawn(0) + if(toEat) + qdel(toEat) + overlays.Cut() + return + + +/obj/item/kitchen/utensil/fork + name = "fork" + desc = "It's a fork. Sure is pointy." + icon_state = "fork" + +/obj/item/kitchen/utensil/pfork + name = "plastic fork" + desc = "Yay, no washing up to do." + icon_state = "pfork" + +/obj/item/kitchen/utensil/spoon + name = "spoon" + desc = "It's a spoon. You can see your own upside-down face in it." + icon_state = "spoon" + attack_verb = list("attacked", "poked") + +/obj/item/kitchen/utensil/pspoon + name = "plastic spoon" + desc = "It's a plastic spoon. How dull." + icon_state = "pspoon" + attack_verb = list("attacked", "poked") + +/obj/item/kitchen/utensil/spork + name = "spork" + desc = "It's a spork. Marvel at its innovative design." + icon_state = "spork" + attack_verb = list("attacked", "sporked") + +/obj/item/kitchen/utensil/pspork + name = "plastic spork" + desc = "It's a plastic spork. It's the fork side of the spoon!" + icon_state = "pspork" + attack_verb = list("attacked", "sporked") + +/* + * Knives + */ +/obj/item/kitchen/knife + name = "kitchen knife" + icon_state = "knife" + desc = "A general purpose Chef's Knife made by SpaceCook Incorporated. Guaranteed to stay sharp for years to come." + flags = CONDUCT + force = 10 + w_class = WEIGHT_CLASS_SMALL + throwforce = 10 + hitsound = 'sound/weapons/bladeslice.ogg' + throw_speed = 3 + throw_range = 6 + materials = list(MAT_METAL=12000) + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + sharp = TRUE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + var/bayonet = FALSE //Can this be attached to a gun? + +/obj/item/kitchen/knife/suicide_act(mob/user) + user.visible_message(pick("[user] is slitting [user.p_their()] wrists with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.", \ + "[user] is slitting [user.p_their()] throat with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.", \ + "[user] is slitting [user.p_their()] stomach open with the [name]! It looks like [user.p_theyre()] trying to commit seppuku.")) + return BRUTELOSS + +/obj/item/kitchen/knife/plastic + name = "plastic knife" + desc = "The bluntest of blades." + icon_state = "pknife" + item_state = "knife" + sharp = 0 + +/obj/item/kitchen/knife/ritual + name = "ritual knife" + desc = "The unearthly energies that once powered this blade are now dormant." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/kitchen/knife/butcher + name = "butcher's cleaver" + icon_state = "butch" + desc = "A huge thing used for chopping and chopping up meat. This includes clowns and clown-by-products." + flags = CONDUCT + force = 15 + throwforce = 8 + attack_verb = list("cleaved", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/kitchen/knife/butcher/meatcleaver + name = "meat cleaver" + icon_state = "mcleaver" + item_state = "butch" + force = 25 + throwforce = 15 + +/obj/item/kitchen/knife/combat + name = "combat knife" + icon_state = "combatknife" + item_state = "knife" + desc = "A military combat utility survival knife." + force = 20 + throwforce = 20 + origin_tech = "materials=3;combat=4" + attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "cut") + bayonet = TRUE + +/obj/item/kitchen/knife/combat/survival + name = "survival knife" + icon_state = "survivalknife" + desc = "A hunting grade survival knife." + force = 15 + throwforce = 15 + +/obj/item/kitchen/knife/combat/survival/bone + name = "bone dagger" + item_state = "bone_dagger" + icon_state = "bone_dagger" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + desc = "A sharpened bone. The bare minimum in survival." + materials = list() + +/obj/item/kitchen/knife/combat/cyborg + name = "cyborg knife" + icon = 'icons/obj/items_cyborg.dmi' + icon_state = "knife" + desc = "A cyborg-mounted plasteel knife. Extremely sharp and durable." + origin_tech = null + +/obj/item/kitchen/knife/carrotshiv + name = "carrot shiv" + icon_state = "carrotshiv" + item_state = "carrotshiv" + desc = "Unlike other carrots, you should probably keep this far away from your eyes." + force = 8 + throwforce = 12 //fuck git + materials = list() + origin_tech = "biotech=3;combat=2" + attack_verb = list("shanked", "shivved") + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + + +/* + * Rolling Pins + */ + +/obj/item/kitchen/rollingpin + name = "rolling pin" + desc = "Used to knock out the Bartender." + icon_state = "rolling_pin" + force = 8.0 + throwforce = 10.0 + throw_speed = 3 + throw_range = 7 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") + +/* Trays moved to /obj/item/storage/bag */ + +/* + * Candy Moulds + */ + +/obj/item/kitchen/mould + name = "generic candy mould" + desc = "You aren't sure what it's supposed to be." + icon_state = "mould" + force = 5 + throwforce = 5 + throw_speed = 3 + throw_range = 3 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "smashed") + +/obj/item/kitchen/mould/bear + name = "bear-shaped candy mould" + desc = "It has the shape of a small bear imprinted into it." + icon_state = "mould_bear" + +/obj/item/kitchen/mould/worm + name = "worm-shaped candy mould" + desc = "It has the shape of a worm imprinted into it." + icon_state = "mould_worm" + +/obj/item/kitchen/mould/bean + name = "bean-shaped candy mould" + desc = "It has the shape of a bean imprinted into it." + icon_state = "mould_bean" + +/obj/item/kitchen/mould/ball + name = "ball-shaped candy mould" + desc = "It has a small sphere imprinted into it." + icon_state = "mould_ball" + +/obj/item/kitchen/mould/cane + name = "cane-shaped candy mould" + desc = "It has the shape of a cane imprinted into it." + icon_state = "mould_cane" + +/obj/item/kitchen/mould/cash + name = "cash-shaped candy mould" + desc = "It has the shape and design of fake money imprinted into it." + icon_state = "mould_cash" + +/obj/item/kitchen/mould/coin + name = "coin-shaped candy mould" + desc = "It has the shape of a coin imprinted into it." + icon_state = "mould_coin" + +/obj/item/kitchen/mould/loli + name = "sucker mould" + desc = "It has the shape of a sucker imprinted into it." + icon_state = "mould_loli" + +/* + * Sushi Mat + */ +/obj/item/kitchen/sushimat + name = "Sushi Mat" + desc = "A wooden mat used for efficient sushi crafting." + icon_state = "sushi_mat" + force = 5 + throwforce = 5 + throw_speed = 3 + throw_range = 3 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("rolled", "cracked", "battered", "thrashed") + + + +/// circular cutter by Ume + +/obj/item/kitchen/cutter + name = "generic circular cutter" + desc = "A generic circular cutter for cookies and other things." + icon = 'icons/obj/kitchen.dmi' + icon_state = "circular_cutter" + force = 5 + throwforce = 5 + throw_speed = 3 + throw_range = 3 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("bashed", "slashed", "pricked", "thrashed") diff --git a/code/game/objects/items/weapons/lighters.dm b/code/game/objects/items/weapons/lighters.dm index 23c55cc1a63f..e24346c1ee9d 100644 --- a/code/game/objects/items/weapons/lighters.dm +++ b/code/game/objects/items/weapons/lighters.dm @@ -235,4 +235,4 @@ /obj/item/match/firebrand/New() ..() - matchignite() \ No newline at end of file + matchignite() diff --git a/code/game/objects/items/weapons/manuals.dm b/code/game/objects/items/weapons/manuals.dm index 0267daa09342..d91bb29ff76e 100644 --- a/code/game/objects/items/weapons/manuals.dm +++ b/code/game/objects/items/weapons/manuals.dm @@ -1,1407 +1,1407 @@ -/*********************MANUALS (BOOKS)***********************/ - -//Oh god what the fuck I am not good at computer -/obj/item/book/manual - icon = 'icons/obj/library.dmi' - due_date = 0 // Game time in 1/10th seconds - unique = 1 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified - - -/obj/item/book/manual/engineering_construction - name = "Station Repairs and Construction" - icon_state ="bookEngineering" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Station Repairs and Construction" - dat = {" - - - - - - - - - - - "} - -/obj/item/book/manual/engineering_particle_accelerator - name = "Particle Accelerator User's Guide" - icon_state ="bookParticleAccelerator" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Particle Accelerator User's Guide" -//big pile of shit below. - - dat = {" - - - - - -

    Experienced user's guide

    - -

    Setting up

    - -
      -
    1. Wrench all pieces to the floor
    2. -
    3. Add wires to all the pieces
    4. -
    5. Close all the panels with your screwdriver
    6. -
    - -

    Use

    - -
      -
    1. Open the control panel
    2. -
    3. Set the speed to 2
    4. -
    5. Start firing at the singularity generator
    6. -
    7. When the singularity reaches a large enough size so it starts moving on it's own set the speed down to 0, but don't shut it off
    8. -
    9. Remember to wear a radiation suit when working with this machine... we did tell you that at the start, right?
    10. -
    - - - "} - - -/obj/item/book/manual/supermatter_engine - name = "Supermatter Engine User's Guide" - icon_state = "bookParticleAccelerator" //TEMP FIXME - author = "Waleed Asad" - title = "Supermatter Engine User's Guide" - - dat = {"Engineering notes on single-stage Supermatter engine,
    - -Waleed Asad
    - - A word of caution, do not enter the engine room, for any reason, without radiation protection and mesons on. The status of the engine may be unpredictable even when you believe it is .off.. This is an important level of personal protection.

    - - The engine has two basic modes of functionality. He has observed that it is capable of both a safe level of operation and a modified, high output mode.

    - -
    Notes on starting the basic function mode, dubbed .Heat-Primary Mode..


    - - 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.

    - - 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.

    - - 3. Apply N2 gas. Retrieve the two N2 canisters from storage and bring them to the engine room. Attach one of them to the input section of the engine gas system located next to the collectors. Keep it attached until the N2 pressure is low enough to turn the canister light red. Replace it with the second canister to keep N2 pressure at optimal levels.

    - - 4. Begin primary emitter burst series. This means firing a single emitter for its first four shots. It is important to move to this step quickly. The onboard SMES units may not have enough power to run the emitters if left alone too long on-station. This engine can produce enough power on its own to run the entire station, ignoring the SMES units completely, and is wired to do so.

    - - 5. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.)

    - - 6. Begin secondary emitter burst series. Before firing the emitter again, check the power in the line with a multimeter (Do not forget electrical gloves.) The engine is running at high efficiency when the value exceeds 200,000 power units.

    - - 7. Maintain engine power. When power in the lines gets low, add an additional emitter burst series to bring power to normal levels.


    - - - -
    The second mode for running the engine uses a gas mix to produce a reaction within the Supermatter. This mode requires CE or Atmospheric help to setup. This has been dubbed the .O2-Reaction Mode..


    - - THIS MODE CAN CAUSE A RUNAWAY REACTION, LEADING TO CATASTROPHIC FAILURE IF NOT MAINTAINED. NEVER FORGET ABOUT THE ENGINE IN THIS MODE.

    - - Additionally, this mode can be used for what is called a .Cold Start.. If the station has no power in the SMES to run the emitters, using this mode will allow enough power output to run them, and quickly reach an acceptable level of power output.

    - - 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.

    - - 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.

    - - 3. Modify the engine room filters. Unlike the Heat-Primary Mode, it is important to change the filters attached to the gas system to stop filtering O2, and start filtering Carbon Molecules. O2-Reaction Mode produces far more plasma than Heat-Primary, therefor filtering it off is essential.

    - - 4. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.) If you check the power in the system lines at this point you will find that it is constantly going up. Indeed, with just the addition of O2 to the Supermatter, it will begin outputting power.

    - - 5. Begin primary emitter burst series. Fire a single emitter for a series of four pulses, or a single series, and turn it off. Do not over power the Supermatter. The reaction is self sustaining and propagating. As long as O2 is in the chamber, it will continue outputting MORE power.

    - - 6. Maintain follow up operations. Remember to check the temp of the core gas and switch to the Heat-Primary function, or vent the core room when problems begin if required.

    - - Notes on Supermatter Reaction Function and Drawbacks-

    - - After several hours of observation an interesting phenomenon was witnessed. The Supermatter undergoes a constant self-sustaining reaction when given an extremely high O2 concentration. Anything about 80% or higher typically will cause this reaction. The Supermatter will continue to react whenever this gas mix is in the same room as the Supermatter.

    - - To understand why O2-Reaction mode is dangerous, the core principle of the Supermatter must be understood. The Supermatter emits three things when .not safe,. that is any time it is giving off power. These things are:

    - - *Radiation (which is converted into power by the collectors,)
    - *Heat (which is removed via the gas exchange system and coolers,)
    - *External gas (in the form of plasma and O2.)
    - - When in Heat-Primary mode, far more heat and plasma are produced than radiation. In O2-Reaction mode, very little heat and only moderate amounts of plasma are produced, however HUGE amounts of energy leaving the Supermatter is in the form of radiation.

    - - The O2-Reaction engine mode has a single drawback which has been eluded to more than once so far and that is very simple. The engine room will continue to grow hotter as the constant reaction continues. Eventually, there will be what he calls the .critical gas mix.. This is the point at which the constant adding of plasma to the mix of air around the Supermatter changes the gas concentration to below the tolerance. When this happens, two things occur. First, the Supermatter switches to its primary mode of operation where in huge amounts of heat are produced by the engine rather than low amounts with high power output. Second, an uncontrollable increase in heat within the Supermatter chamber will occur. This will lead to a spark-up, igniting the plasma in the Supermatter chamber, wildly increasing both pressure and temperature.

    - - While the O2-Reaction mode is dangerous, it does produce heavy amounts of energy. Consider using this mode only in short amounts to fill the SMES, and switch back later in the shift to keep things flowing normally.

    - - - Notes on Supermatter Containment and Emergency Procedures-

    - - While a constant vigil on the Supermatter is not required, regular checkups are important. Verify the temp of gas leaving the Supermatter chamber for unsafe levels, and ensure that the plasma in the chamber is at a safe concentration. Of course, also make sure the chamber is not on fire. A fire in the core chamber is very difficult to put out. As any Toxin scientist can tell you, even low amounts of plasma can burn at very high temperatures. This burning creates a huge increase in pressure and more importantly, temperature of the crystal itself.

    - - The Supermatter is strong, but not invincible. When the Supermatter is heated too much, its crystal structure will attempt to liquify. The change in atomic structure of the Supermatter leads to a single reaction, a massive explosion. The computer chip attached to the Supermatter core will warn the station when stability is threatened. It will then offer a second warning, when things have become dangerously close to total destruction of the core.

    - - Located both within the supermatter monitoring room and engine room is the vent control button. This button allows the Core Vent Controls to be accessed, venting the room to space. Remember however, that this process takes time. If a fire is raging, and the pressure is higher than fathomable, it will take a great deal of time to vent the room. Also located in the supermatter monitoring room is the emergency core eject button. A new core can be ordered from cargo. It is often not worth the lives of the crew to hold on to it, not to mention the structural damage. However, if by some mistake the Supermatter is pushed off or removed from the mass ejector it sits on, manual reposition will be required. Which is very dangerous and often leads to death.

    - - The Supermatter is extremely dangerous. More dangerous than people give it credit for. It can destroy you in an instant, without hesitation, reducing you to a pile of dust. When working closely with Supermatter it is.. suggested to get a genetic backup and do not wear any items of value to you. The Supermatter core can be pulled if grabbed properly by the base, but pushing is not possible.


    - - - In Closing-

    - - Remember that the Supermatter is dangerous, and the core is dangerous still. Venting the core room is always an option if you are even remotely worried, utilizing Atmospherics to properly ready the room once more for core function. It is always a good idea to check up regularly on the temperature of gas leaving the chamber, as well as the power in the system lines. Lastly, once again remember, never touch the Supermatter with anything. Ever.

    - - -Waleed Asad, Senior Engine Technician."} - -/obj/item/book/manual/engineering_hacking - name = "Hacking" - icon_state ="bookHacking" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Hacking" -//big pile of shit below. - - dat = {" - - - - - - - - - - - "} - -/obj/item/book/manual/engineering_singularity_safety - name = "Singularity Safety in Special Circumstances" - icon_state ="bookEngineeringSingularitySafety" - author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Singularity Safety in Special Circumstances" -//big pile of shit below. - - dat = {" - - - - -

    Singularity Safety in Special Circumstances

    - -

    Power outage

    - - A power problem has made the entire station loose power? Could be station-wide wiring problems or syndicate power sinks. In any case follow these steps: -

    - Step one: PANIC!
    - Step two: Get your ass over to engineering! QUICKLY!!!
    - Step three: Get to the Area Power Controller which controls the power to the emitters.
    - Step four: Swipe it with your ID card - if it doesn't unlock, continue with step 15.
    - Step five: Open the console and disengage the cover lock.
    - Step six: Pry open the APC with a Crowbar.
    - Step seven: Take out the empty power cell.
    - Step eight: Put in the new, full power cell - if you don't have one, continue with step 15.
    - Step nine: Quickly put on a Radiation suit.
    - Step ten: Check if the singularity field generators withstood the down-time - if they didn't, continue with step 15.
    - Step eleven: Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering apc is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the apc to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
    - Step twelve: Grab your crowbar and pry away the tile closest to the APC.
    - Step thirteen: Use the wirecutters to cut the wire which is conecting the grid to the terminal.
    - Step fourteen: Go to the bar and tell the guys how you saved them all. Stop reading this guide here.
    - Step fifteen: GET THE FUCK OUT OF THERE!!!
    -

    - -

    Shields get damaged

    - - Step one: GET THE FUCK OUT OF THERE!!! FORGET THE WOMEN AND CHILDREN, SAVE YOURSELF!!!
    - - - "} - -/obj/item/book/manual/hydroponics_pod_people - name = "The Human Harvest - From seed to market" - icon_state ="bookHydroponicsPodPeople" - author = "Farmer John" - title = "The Human Harvest - From seed to market" - dat = {" - - - - -

    Growing Humans

    - - Why would you want to grow humans? Well I'm expecting most readers to be in the slave trade, but a few might actually - want to revive fallen comrades. Growing pod people is easy, but prone to disaster. -

    -

      -
    1. Find a dead person who is in need of cloning.
    2. -
    3. Take a blood sample with a syringe.
    4. -
    5. Inject a seed pack with the blood sample.
    6. -
    7. Plant the seeds.
    8. -
    9. Tend to the plants water and nutrition levels until it is time to harvest the cloned human.
    10. -
    -

    - It really is that easy! Good luck! - - - - "} - -/obj/item/book/manual/medical_cloning - name = "Cloning techniques of the 26th century" - icon_state ="bookCloning" - author = "Medical Journal, volume 3" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "Cloning techniques of the 26th century" -//big pile of shit below. - - dat = {" - - - - - -

    How to Clone People

    - So thereÂ’s 50 dead people lying on the floor, chairs are spinning like no tomorrow and you havenÂ’t the foggiest idea of what to do? Not to worry! This guide is intended to teach you how to clone people and how to do it right, in a simple step-by-step process! If at any point of the guide you have a mental meltdown, genetics probably isnÂ’t for you and you should get a job-change as soon as possible before youÂ’re sued for malpractice. - -
      -
    1. Acquire body
    2. -
    3. Strip body
    4. -
    5. Put body in cloning machine
    6. -
    7. Scan body
    8. -
    9. Clone body
    10. -
    11. Get clean Structurel Enzymes for the body
    12. -
    13. Put body in morgue
    14. -
    15. Await cloned body
    16. -
    17. Use the clean SW injector
    18. -
    19. Give person clothes back
    20. -
    21. Send person on their way
    22. -
    - -

    Step 1: Acquire body

    - This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step. - -

    Step 2: Strip body

    - The cloning machine does not like abiotic items. What this means is you canÂ’t clone anyone if theyÂ’re wearing clothes, so take all of it off. If itÂ’s just one person, itÂ’s courteous to put their possessions in the closet. If you have about seven people awaiting cloning, just leave the piles where they are, but donÂ’t mix them around and for GodÂ’s sake donÂ’t let people in to steal them. - -

    Step 3: Put body in cloning machine

    - Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset. - -

    Step 4: Scan body

    - Go onto the computer and scan the body by pressing ‘Scan - ’. If you’re successful, they will be added to the records (note that this can be done at any time, even with living people, so that they can be cloned without a body in the event that they are lying dead on port solars and didn‘t turn on their suit sensors)! If not, and it says “Error: Mental interface failure.”, then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, click ‘Refresh‘ and try scanning them again. If there’s no success, threaten them with gibbing. Still no success? Skip over to Step 7 and don‘t continue after it, as you have an unresponsive body and it cannot be cloned. If you got “Error: Unable to locate valid genetic data.“, you are trying to clone a monkey - start over. - -

    Step 5: Clone body

    - Now that the body has a record, click ’View Records’, click the subject’s name, and then click ‘Clone’ to start the cloning process. Congratulations! You’re halfway there. Remember not to ‘Eject’ the cloning pod as this will kill the developing clone and you’ll have to start the process again. - -

    Step 6: Get clean SEs for body

    - Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they’re done. If you’re a competent Geneticist, you will already have one ready on your working computer. If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. Go into View/Edit/Transfer Buffer, find an open slot and click “SE“ to save it. Then click ‘Injector’ to get the SEs in syringe form. Put this in your pocket or something for when the body is done. - -

    Step 7: Put body in morgue

    - Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed “CHEF MEAT” in order to avoid confusion. - -

    Step 8: Await cloned body

    - Now go back to the lab and wait for your patient to be cloned. It wonÂ’t be long now, I promise. - -

    Step 9: Use the clean SE injector on person

    - Has your body been cloned yet? Great! As soon as the guy pops out, grab your injector and jab it in them. Once youÂ’ve injected them, they now have clean Structural Enzymes and their defects, if any, will disappear in a short while. - -

    Step 10: Give person clothes back

    - Obviously the person will be naked after they have been cloned. Provided you werenÂ’t an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. No matter how cruel you are, itÂ’s simply against protocol to force your patients to walk outside naked. - -

    Step 11: Send person on their way

    - Give the patient one last check-over - make sure they donÂ’t still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. Once youÂ’re done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance. - -

    If youÂ’ve gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target. - - - - - - "} - - -/obj/item/book/manual/ripley_build_and_repair - name = "APLU \"Ripley\" Construction and Operation Manual" - icon_state ="book" - author = "Weyland-Yutani Corp" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - title = "APLU \"Ripley\" Construction and Operation Manual" -//big pile of shit below. - - dat = {" - - - - -

    - Weyland-Yutani - Building Better Worlds -

    Autonomous Power Loader Unit \"Ripley\"

    -
    -

    Specifications:

    -
      -
    1. Cyborg Related Equipment
    2. -
    3. Cyborg Modules
    4. -
    5. Cyborg Construction
    6. -
    7. Cyborg Maintenance
    8. -
    9. Cyborg Repairs
    10. -
    11. In Case of Emergency
    12. -
    - - -

    Cyborg Related Equipment

    - -

    Exosuit Fabricator

    - The Exosuit Fabricator is the most important piece of equipment related to cyborgs. It allows the construction of the core cyborg parts. Without these machines, cyborgs can not be built. It seems that they may also benefit from advanced research techniques. - -

    Cyborg Recharging Station

    - This useful piece of equipment will suck power out of the power systems to charge a cyborg's power cell back up to full charge. - -

    Robotics Control Console

    - This useful piece of equipment can be used to immobolize or destroy a cyborg. A word of warning: Cyborgs are expensive pieces of equipment, do not destroy them without good reason, or Nanotrasen may see to it that it never happens again. - - -

    Cyborg Modules

    - When a cyborg is created it picks out of an array of modules to designate its purpose. There are 6 different cyborg modules. - -

    Standard Cyborg

    - The standard cyborg module is a multi-purpose cyborg. It is equipped with various modules, allowing it to do basic tasks.
    A Standard Cyborg comes with: -
      -
    • Crowbar
    • -
    • Stun Baton
    • -
    • Health Analyzer
    • -
    • Fire Extinguisher
    • -
    - -

    Engineering Cyborg

    - The Engineering cyborg module comes equipped with various engineering-related tools to help with engineering-related tasks.
    An Engineering Cyborg comes with: -
      -
    • A basic set of engineering tools
    • -
    • Metal Synthesizer
    • -
    • Reinforced Glass Synthesizer
    • -
    • An RCD
    • -
    • Wire Synthesizer
    • -
    • Fire Extinguisher
    • -
    • Built-in Optical Meson Scanners
    • -
    - -

    Mining Cyborg

    - The Mining Cyborg module comes equipped with the latest in mining equipment. They are efficient at mining due to no need for oxygen, but their power cells limit their time in the mines.
    A Mining Cyborg comes with: -
      -
    • Jackhammer
    • -
    • Shovel
    • -
    • Mining Satchel
    • -
    • Built-in Optical Meson Scanners
    • -
    - -

    Security Cyborg

    - The Security Cyborg module is equipped with effective security measures used to apprehend and arrest criminals without harming them a bit.
    A Security Cyborg comes with: -
      -
    • Stun Baton
    • -
    • Handcuffs
    • -
    • Taser
    • -
    - -

    Janitor Cyborg

    - The Janitor Cyborg module is equipped with various cleaning-facilitating devices.
    A Janitor Cyborg comes with: -
      -
    • Mop
    • -
    • Hand Bucket
    • -
    • Cleaning Spray Synthesizer and Spray Nozzle
    • -
    - -

    Service Cyborg

    - The service cyborg module comes ready to serve your human needs. It includes various entertainment and refreshment devices. Occasionally some service cyborgs may have been referred to as "Bros"
    A Service Cyborg comes with: -
      -
    • Shaker
    • -
    • Industrail Dropper
    • -
    • Platter
    • -
    • Beer Synthesizer
    • -
    • Zippo Lighter
    • -
    • Rapid-Service-Fabricator (Produces various entertainment and refreshment objects)
    • -
    • Pen
    • -
    - -

    Cyborg Construction

    - Cyborg construction is a rather easy process, requiring a decent amount of metal and a few other supplies.
    The required materials to make a cyborg are: -
      -
    • Metal
    • -
    • Two Flashes
    • -
    • One Power Cell (Preferrably rated to 15000w)
    • -
    • Some electrical wires
    • -
    • One Human Brain
    • -
    • One Man-Machine Interface
    • -
    - Once you have acquired the materials, you can start on construction of your cyborg.
    To construct a cyborg, follow the steps below: -
      -
    1. Start the Exosuit Fabricators constructing all of the cyborg parts
    2. -
    3. While the parts are being constructed, take your human brain, and place it inside the Man-Machine Interface
    4. -
    5. Once you have a Robot Head, place your two flashes inside the eye sockets
    6. -
    7. Once you have your Robot Chest, wire the Robot chest, then insert the power cell
    8. -
    9. Attach all of the Robot parts to the Robot frame
    10. -
    11. Insert the Man-Machine Interface (With the Brain inside) Into the Robot Body
    12. -
    13. Congratulations! You have a new cyborg!
    14. -
    - -

    Cyborg Maintenance

    - Occasionally Cyborgs may require maintenance of a couple types, this could include replacing a power cell with a charged one, or possibly maintaining the cyborg's internal wiring. - -

    Replacing a Power Cell

    - Replacing a Power cell is a common type of maintenance for cyborgs. It usually involves replacing the cell with a fully charged one, or upgrading the cell with a larger capacity cell.
    The steps to replace a cell are follows: -
      -
    1. Unlock the Cyborg's Interface by swiping your ID on it
    2. -
    3. Open the Cyborg's outer panel using a crowbar
    4. -
    5. Remove the old power cell
    6. -
    7. Insert the new power cell
    8. -
    9. Close the Cyborg's outer panel using a crowbar
    10. -
    11. Lock the Cyborg's Interface by swiping your ID on it, this will prevent non-qualified personnel from attempting to remove the power cell
    12. -
    - -

    Exposing the Internal Wiring

    - Exposing the internal wiring of a cyborg is fairly easy to do, and is mainly used for cyborg repairs.
    You can easily expose the internal wiring by following the steps below: -
      -
    1. Follow Steps 1 - 3 of "Replacing a Cyborg's Power Cell"
    2. -
    3. Open the cyborg's internal wiring panel by using a screwdriver to unsecure the panel
    4. -
    - To re-seal the cyborg's internal wiring: -
      -
    1. Use a screwdriver to secure the cyborg's internal panel
    2. -
    3. Follow steps 4 - 6 of "Replacing a Cyborg's Power Cell" to close up the cyborg
    4. -
    - -

    Cyborg Repairs

    - Occasionally a Cyborg may become damaged. This could be in the form of impact damage from a heavy or fast-travelling object, or it could be heat damage from high temperatures, or even lasers or Electromagnetic Pulses (EMPs). - -

    Dents

    - If a cyborg becomes damaged due to impact from heavy or fast-moving objects, it will become dented. Sure, a dent may not seem like much, but it can compromise the structural integrity of the cyborg, possibly causing a critical failure. - Dents in a cyborg's frame are rather easy to repair, all you need is to apply a welding tool to the dented area, and the high-tech cyborg frame will repair the dent under the heat of the welder. - -

    Excessive Heat Damage

    - If a cyborg becomes damaged due to excessive heat, it is likely that the internal wires will have been damaged. You must replace those wires to ensure that the cyborg remains functioning properly.
    To replace the internal wiring follow the steps below: -
      -
    1. Unlock the Cyborg's Interface by swiping your ID
    2. -
    3. Open the Cyborg's External Panel using a crowbar
    4. -
    5. Remove the Cyborg's Power Cell
    6. -
    7. Using a screwdriver, expose the internal wiring or the Cyborg
    8. -
    9. Replace the damaged wires inside the cyborg
    10. -
    11. Secure the internal wiring cover using a screwdriver
    12. -
    13. Insert the Cyborg's Power Cell
    14. -
    15. Close the Cyborg's External Panel using a crowbar
    16. -
    17. Lock the Cyborg's Interface by swiping your ID
    18. -
    - These repair tasks may seem difficult, but are essential to keep your cyborgs running at peak efficiency. - -

    In Case of Emergency

    - In case of emergency, there are a few steps you can take. - -

    "Rogue" Cyborgs

    - If the cyborgs seem to become "rogue", they may have non-standard laws. In this case, use extreme caution. - To repair the situation, follow these steps: -
      -
    1. Locate the nearest robotics console
    2. -
    3. Determine which cyborgs are "Rogue"
    4. -
    5. Press the lockdown button to immobolize the cyborg
    6. -
    7. Locate the cyborg
    8. -
    9. Expose the cyborg's internal wiring
    10. -
    11. Check to make sure the LawSync and AI Sync lights are lit
    12. -
    13. If they are not lit, pulse the LawSync wire using a multitool to enable the cyborg's Law Sync
    14. -
    15. Proceed to a cyborg upload console. Nanotrasen usually places these in the same location as AI uplaod consoles.
    16. -
    17. Use a "Reset" upload moduleto reset the cyborg's laws
    18. -
    19. Proceed to a Robotics Control console
    20. -
    21. Remove the lockdown on the cyborg
    22. -
    - -

    As a last resort

    - If all else fails in a case of cyborg-related emergency. There may be only one option. Using a Robotics Control console, you may have to remotely detonate the cyborg. -

    WARNING:

    Do not detonate a borg without an explicit reason for doing so. Cyborgs are expensive pieces of Nanotrasen equipment, and you may be punished for detonating them without reason. - - - - "} - -/obj/item/book/manual/security_space_law - name = "Space Law" - desc = "A set of Nanotrasen guidelines for keeping law and order on their space stations." - icon_state = "bookSpaceLaw" - force = 4 //advanced magistrate tactics - author = "Nanotrasen" - title = "Space Law" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/security_space_law/black - name = "Space Law - Limited Edition" - desc = "A leather-bound, immaculately-written copy of JUSTICE." - icon_state = "bookSpaceLawblack" - title = "Space Law - Limited Edition" - -/obj/item/book/manual/engineering_guide - name = "Engineering Textbook" - icon_state ="bookEngineering2" - author = "Engineering Encyclopedia" - title = "Engineering Textbook" - dat = {" - - - - - - - - - - "} - - -/obj/item/book/manual/chef_recipes - name = "Chef Recipes" - icon_state = "cooked_book" - author = "Victoria Ponsonby" - title = "Chef Recipes" - dat = {" - - - - - -

    Food for Dummies

    - Here is a guide on basic food recipes and also how to not poison your customers accidentally. - -

    Basics:

    - Knead an egg and some flour to make dough. Bake that to make a bun or flatten and cut it. - -

    Burger:

    - Put a bun and some meat into the microwave and turn it on. Then wait. - -

    Bread:

    - Put some dough and an egg into the microwave and then wait. - -

    Waffles:

    - Add two lumps of dough and 10u of sugar to the microwave and then wait. - -

    Popcorn:

    - Add 1 corn to the microwave and wait. - -

    Meat Steak:

    - Put a slice of meat, 1 unit of salt and 1 unit of pepper into the microwave and wait. - -

    Meat Pie:

    - Put a flattened piece of dough and some meat into the microwave and wait. - -

    Boiled Spaghetti:

    - Put the spaghetti (processed flour) and 5 units of water into the microwave and wait. - -

    Donuts:

    - Add some dough and 5 units of sugar to the microwave and wait. - -

    Fries:

    - Add one potato to the processor, then bake them in the microwave. - - - - - "} - -/obj/item/book/manual/barman_recipes - name = "Barman Recipes" - icon_state = "barbook" - author = "Sir John Rose" - title = "Barman Recipes" - dat = {" - - - - - -

    Drinks for dummies

    - Heres a guide for some basic drinks. - -

    Manly Dorf:

    - Mix ale and beer into a glass. - -

    Grog:

    - Mix rum and water into a glass. - -

    Black Russian:

    - Mix vodka and kahlua into a glass. - -

    Irish Cream:

    - Mix cream and whiskey into a glass. - -

    Screwdriver:

    - Mix vodka and orange juice into a glass. - -

    Cafe Latte:

    - Mix milk and coffee into a glass. - -

    Mead:

    - Mix Enzyme, water and sugar into a glass. - -

    Gin Tonic:

    - Mix gin and tonic into a glass. - -

    Classic Martini:

    - Mix vermouth and gin into a glass. - - - - - "} - - -/obj/item/book/manual/detective - name = "The Film Noir: Proper Procedures for Investigations" - icon_state ="bookDetective" - author = "Nanotrasen" - title = "The Film Noir: Proper Procedures for Investigations" - dat = {" - - - - -

    Detective Work

    - - Between your bouts of self-narration, and drinking whiskey on the rocks, you might get a case or two to solve.
    - To have the best chance to solve your case, follow these directions: -

    -

      -
    1. Go to the crime scene.
    2. -
    3. Take your scanner and scan EVERYTHING (Yes, the doors, the tables, even the dog.)
    4. -
    5. Once you are reasonably certain you have every scrap of evidence you can use, find all possible entry points and scan them, too.
    6. -
    7. Return to your office.
    8. -
    9. Using your forensic scanning computer, scan your Scanner to upload all of your evidence into the database.
    10. -
    11. Browse through the resulting dossiers, looking for the one that either has the most complete set of prints, or the most suspicious items handled.
    12. -
    13. If you have 80% or more of the print (The print is displayed) go to step 10, otherwise continue to step 8.
    14. -
    15. Look for clues from the suit fibres you found on your perp, and go about looking for more evidence with this new information, scanning as you go.
    16. -
    17. Try to get a fingerprint card of your perp, as if used in the computer, the prints will be completed on their dossier.
    18. -
    19. Assuming you have enough of a print to see it, grab the biggest complete piece of the print and search the security records for it.
    20. -
    21. Since you now have both your dossier and the name of the person, print both out as evidence, and get security to nab your baddie.
    22. -
    23. Give yourself a pat on the back and a bottle of the ships finest vodka, you did it!.
    24. -
    -

    - It really is that easy! Good luck! - - - "} - -/obj/item/book/manual/nuclear - name = "Fission Mailed: Nuclear Sabotage 101" - icon_state ="bookNuclear" - author = "Syndicate" - title = "Fission Mailed: Nuclear Sabotage 101" - dat = {" - Nuclear Explosives 101:
    - Hello and thank you for choosing the Syndicate for your nuclear information needs.
    - Today's crash course will deal with the operation of a Fusion Class Nanotrasen made Nuclear Device.
    - First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE.
    - Pressing any button on the compacted bomb will cause it to extend and bolt itself into place.
    - If this is done to unbolt it one must completely log in which at this time may not be possible.
    - To make the nuclear device functional:
    -

  • Place the nuclear device in the designated detonation zone.
  • -
  • Extend and anchor the nuclear device from its interface.
  • -
  • Insert the nuclear authorisation disk into slot.
  • -
  • Type numeric authorisation code into the keypad. This should have been provided. Note: If you make a mistake press R to reset the device. -
  • Press the E button to log onto the device.
  • - You now have activated the device. To deactivate the buttons at anytime for example when you've already prepped the bomb for detonation remove the auth disk OR press the R on the keypad.
    - Now the bomb CAN ONLY be detonated using the timer. Manual detonation is not an option.
    - Note: Nanotrasen is a pain in the neck.
    - Toggle off the SAFETY.
    - Note: You wouldn't believe how many Syndicate Operatives with doctorates have forgotten this step.
    - So use the - - and + + to set a det time between 5 seconds and 10 minutes.
    - Then press the timer toggle button to start the countdown.
    - Now remove the auth. disk so that the buttons deactivate.
    - Note: THE BOMB IS STILL SET AND WILL DETONATE
    - Now before you remove the disk if you need to move the bomb you can:
    - Toggle off the anchor, move it, and re-anchor.

    - Good luck. Remember the order:
    - Disk, Code, Safety, Timer, Disk, RUN!
    - Intelligence Analysts believe that normal Nanotrasen procedure is for the Captain to secure the nuclear authorisation disk.
    - Good luck! - "} - -/obj/item/book/manual/atmospipes - name = "Pipes and You: Getting To Know Your Scary Tools" - icon_state = "pipingbook" - author = "Maria Crash, Senior Atmospherics Technician" - title = "Pipes and You: Getting To Know Your Scary Tools" - dat = {" - - - - - - -

    Contents

    -
      -
    1. Author's Forward
    2. -
    3. Basic Piping
    4. -
    5. Insulated Pipes
    6. -
    7. Atmospherics Devices
    8. -
    9. Heat Exchange Systems
    10. -
    11. Final Checks
    12. -
    -

    - -

    HOW TO NOT SUCK QUITE SO HARD AT ATMOSPHERICS


    - Or: What the fuck does a "passive gate" do?

    - - Alright. It has come to my attention that a variety of people are unsure of what a "pipe" is and what it does. - Apparently there is an unnatural fear of these arcane devices and their "gases". Spooky, spooky. So, - this will tell you what every device constructable by an ordinary pipe dispenser within atmospherics actually does. - You are not going to learn what to do with them to be the super best person ever, or how to play guitar with passive gates, - or something like that. Just what stuff does.

    - - -

    Basic Pipes


    - The boring ones.
    - TMost ordinary pipes are pretty straightforward. They hold gas. If gas is moving in a direction for some reason, gas will flow in that direction. - That's about it. Even so, here's all of your wonderful pipe options.
    - -
  • Straight pipes: They're pipes. One-meter sections. Straight line. Pretty simple. Just about every pipe and device is based around this - standard one-meter size, so most things will take up as much space as one of these.
  • -
  • Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
  • -
  • Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
  • -
  • 4-way manifold: A four-way junction.
  • -
  • Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh. Use them to decorate your house or something.
  • -
  • Manual Valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
  • -
  • Manual T-Valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


  • - -

    Insulated Pipes


    - Special Public Service Announcement.
    - Our regular pipes are already insulated. These are completely worthless. Punch anyone who uses them.

    - -

    Devices:


    - They actually do something.
    - This is usually where people get frightened,
    afraid, and start calling on their gods and/or cowering in fear. Yes, I can see you doing that right now. - Stop it. It's unbecoming. Most of these are fairly straightforward.
    - -
  • Gas Pump: Take a wild guess. It moves gas in the direction it's pointing (marked by the red line on one end). It moves it based on pressure, the maximum output being 4500 kPa (kilopascals). - Ordinary atmospheric pressure, for comparison, is 101.3 kPa, and the minimum pressure of room-temperature pure oxygen needed to not suffocate in a matter of minutes is 16 kPa - (though 18 is preferred using internals, for various reasons).
  • -
  • Volume pump: This pump goes based on volume, instead of pressure, and the possible maximum pressure it can create in the pipe on the recieving end is double the gas pump because of this, - clocking in at an incredible 9000 kPa. If a pipe with this is destroyed or damaged, and this pressure of gas escapes, it can be incredibly dangerous depending on the size of the pipe filled. - Don't hook this to the distribution loop, or you will make babies cry and the Chief Engineer brutally beat you.
  • -
  • Passive gate: This is essentially a cap on the pressure of gas allowed to flow in a specific direction. - When turned on, instead of actively pumping gas, it measures the pressure flowing through it, and whatever pressure you set is the maximum: it'll cap after that. - In addition, it only lets gas flow one way. The direction the gas flows is opposite the red handle on it, which is confusing to people used to the red stripe on pumps pointing the way.
  • -
  • Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
  • -
  • Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon". Actvating a panic siphon without very good reason will kill someone. Don't do it.
  • -
  • Meter: A little box with some gagues and numbers. Fasten it to any pipe or manifold, and it'll read you the pressure in it. Very useful.
  • -
  • Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2", for reference. - Can output this gas at pressures from 0-4500 kPa.
  • -
  • Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe, - the rest will continue out the other side. Can also output from 0-4500 kPa.
  • - -

    Heat Exchange Systems


    - Will not set you on fire.
    - These systems are used to transfer heat only between two pipes. They will not move gases or any other element, but will equalize the temperature (eventually). Note that because of how gases work (remember: pv=nRt), - a higher temperature will raise pressure, and a lower one will lower temperature.
    - -
  • Pipe: This is a pipe that will exchange heat with the surrounding atmosphere. Place in fire for superheating. Place in space for supercooling.
  • -
  • Bent Pipe: Take a wild guess.
  • -
  • Junction:Junction:The point where you connect your normal pipes to heat exchange pipes. Not necessary for heat exchangers, but necessary for H/E pipes/bent pipes.
  • -
  • Heat Exchanger: These funky-looking bits attach to an open pipe end. Put another heat exchanger directly across from it, and you can transfer heat across two pipes without having to have the gases touch. - This normally shouldn't exchange with the ambient air, despite being totally exposed. Just don't ask questions...

  • - - - That's about it for pipes. Go forth, armed with this knowledge, and try not to break, burn down, or kill anything. Please.
    - - - - "} - -/obj/item/book/manual/evaguide - name = "EVA Gear and You: Not Spending All Day Inside" - icon_state = "evabook" - author = "Maria Crash, Senior Atmospherics Technician" - title = "EVA Gear and You: Not Spending All Day Inside" - dat = {" - - - - - - -

    Contents

    -
      -
    1. A forward on using EVA gear
    2. -
    3. Donning a Civilian Suits
    4. -
    5. Putting on a Hardsuit
    6. -
    7. Final Checks
    8. -
    -

    - -

    EVA Gear and You: Not Spending All Day Inside


    - Or: How not to suffocate because there's a hole in your shoes

    - - EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training, - but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into two sections: - Civilian suits and hardsuits.

    - -

    Civilian Suits


    - The bulkiest things this side of Alpha Centauri
    - These suits are the grey ones that are stored in EVA. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiaion or physical impact. - As Medical, Engineering, Security, and Mining all have hardsuits of their own, these don't see much use, but knowing how to put them on is quite useful anyways.

    - - First, take the suit. It should be in three pieces: A top, a bottom,
    and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however, - put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit, - but to an extent they will adjust to you. You can then find the snaps and seals around the waist, where the two pieces meet. Fasten these, and double-check their tightness. - The red indicators around the waist of the lower half will turn green when this is done correctly. Next, put on whatever breathing apparatus you're using, be it a gas mask or a breath mask. Make sure the oxygen tube is fastened into it. - Put on the helmet now, straight forward, and make sure the tube goes into the small opening specifically for internals. Again, fasten seals around the neck, a small indicator light in the inside of the helmet should go from red to off when all is fastened. - There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit, - but it is reccomended to have a full-sized tank on your back for EVA.

    - -

    Hardsuits


    - Heavy, uncomfortable, still the best option.
    - These suits come in Engineering, Mining, and the Armory. There's also a couple Medical Hardsuits in EVA. These provide a lot more protection than the standard suits.

    - - Similarly to the other suits, these are split into three parts. Fastening the pant and top are mostly the same as the other spacesuits, with the exception that these are a bit heavier, - though not as bulky. The helmet goes on differently, with the air tube feeing into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise, - and then is screwed in for one and a quarter full rotations clockwise, leaving the faceplate directly in front of you. There is a small button on the right side of the helmet that activates the helmet light. - The tanks that fasten onto the side slot are emergency tanks, as
    well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.

    - -

    FINAL CHECKS:


    -
  • Are all seals fastened correctly?
  • -
  • Do you either have shoes on under the suit, or magnetic boots on over it?
  • -
  • Do you have a mask on and internals on the suit or your back?
  • -
  • Do you have a way to communicate with the station in case something goes wrong?
  • -
  • Do you have a second person watching if this is a training session?

  • - - If you don't have any further issues, go out and do whatever is necessary.
    - - - - "} - -/obj/item/book/manual/faxes - name = "A Guide to Faxes" - desc = "A Nanotrasen-approved guide to writing faxes" - icon_state = "book6" - author = "Nanotrasen" - title = "A Guide to Faxes" - dat = {" - - - - - - - - -

    Contents

    -
      -
    1. What's a Fax?
    2. -
    3. When to Fax?
    4. -
    5. How to Fax?
    6. -
    -

    - -

    What's a Fax?


    -
  • Faxes are your main method of communicating with the NAS Trurl, better known as Central Command.
  • -
  • Faxes allow personnel on the station to maintain open lines of communication with the NAS Trurl, allowing for vital information to flow both ways.
  • -
  • Being written communications, proper grammar, syntax and typography is required, in addition to a signature and, if applicable, a stamp. Failure to sign faxes will lead to an automatic rejection.
  • -
  • We at Nanotrasen provide Fax Machines to every Head of Staff, in addition to the Magistrate, Nanotrasen Representative, and Internal Affairs Agents.
  • -
  • This means that we trust the recipients of these fax machines to only use them in the proper circumstances (see When to Fax?).
  • - -

    When to Fax?


    -
  • While it is up to the discretion of each individual person to decide when to fax Central Command, there are some simple guidelines on when to do this.
  • -
  • Firstly, any situation that can reasonably be solved on-site, should be handled on-site. Knowledge of Standard Operating Procedure is mandatory for everyone with access to a fax machine.
  • -
  • Resolving issues on-site not only leads to more expedient problem-solving, it also frees up company resources and provides valuable work experience for all parties involved.
  • -
  • This means that you should work with the Heads of Staff concerning personnel and workplace issues, and attempt to resolve situations with them. If, for whatever reason, the relevent Head of Staff is not available or receptive, consider speaking with the Captain and/or Nanotrasen Representative.
  • -
  • If, for whatever reason, these issues cannot be solved on-site, either due to incompetence or just plain refusal to cooperate, faxing Central Command becomes a viable option.
  • -
  • Secondly, station status reports should be sent occasionally, but never at the start of the shift. Remember, we assign personnel to the station. We do not need a repeat of what we just signed off on.
  • -
  • Thirdly, staff/departmental evaluations are always welcome, especially in cases of noticeable (in)competence. Just as a brilliant coworker can be rewarded, an incompetent one can be punished.
  • -
  • Fourthly, do not issue faxes asking for sentences. You have an entire Security department and an associated Detective, not to mention on-site Space Law manuals.
  • -
  • Lastly, please pay attention to context. If the station is facing a massive emergency, such as a Class 7-10 Blob Organism, most, if not all, non-relevant faxes will be duly ignored.
  • - -

    How to Fax?


    -
  • Sending a fax is simple. Simply insert your ID into the fax machine, then log in.
  • -
  • Once logged in, insert a piece of paper and select the destination from the provided list. Remember, you can rename your fax from within the fax machine's menu.
  • -
  • You can send faxes to any other fax machine on the station, which can be a very useful tool when you need to issue broad communications to all of the Heads of Staff.
  • -
  • To send a fax to Central Command, simply select the correct destination, and send the fax. Keep in mind, the communication arrays need to recharge after sending a fax to Central Command, so make sure you sent everything you need.
  • -
  • Lastly, paper bundles can also be faxed as a single item, so feel free to bundle up all relevant documentation and send it in at once.
  • - -
    - - - - "} - -/obj/item/book/manual/sop_science - name = "Science Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all scientific activities." - icon_state = "sop_science" - author = "Nanotrasen" - title = "Science Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_medical - name = "Medical Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all medical activities." - icon_state = "sop_medical" - author = "Nanotrasen" - title = "Medical Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_engineering - name = "Engineering Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all engineering activities." - icon_state = "sop_engineering" - author = "Nanotrasen" - title = "Engineering Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_service - name = "Service Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all service activities." - icon_state = "sop_service" - author = "Nanotrasen" - title = "Service Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_supply - name = "Supply Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all supply activities." - icon_state = "sop_cargo" - author = "Nanotrasen" - title = "Supply Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_security - name = "Security Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all security activities." - icon_state = "sop_security" - author = "Nanotrasen" - title = "Security Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_legal - name = "Legal Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all legal activities." - icon_state = "sop_legal" - author = "Nanotrasen" - title = "Legal Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_general - name = "Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all station activities." - icon_state = "sop" - author = "Nanotrasen" - title = "Standard Operating Procedures" - dat = {" - - - - - - - - - - "} - -/obj/item/book/manual/sop_command - name = "Command Standard Operating Procedures" - desc = "A set of guidelines aiming at the safe conduct of all Command activities." - icon_state = "sop_command" - author = "Nanotrasen" - title = "Command Standard Operating Procedures" - dat = {" - - - - - - - - - - "} +/*********************MANUALS (BOOKS)***********************/ + +//Oh god what the fuck I am not good at computer +/obj/item/book/manual + icon = 'icons/obj/library.dmi' + due_date = 0 // Game time in 1/10th seconds + unique = 1 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified + + +/obj/item/book/manual/engineering_construction + name = "Station Repairs and Construction" + icon_state ="bookEngineering" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Station Repairs and Construction" + dat = {" + + + + + + + + + + + "} + +/obj/item/book/manual/engineering_particle_accelerator + name = "Particle Accelerator User's Guide" + icon_state ="bookParticleAccelerator" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Particle Accelerator User's Guide" +//big pile of shit below. + + dat = {" + + + + + +

    Experienced user's guide

    + +

    Setting up

    + +
      +
    1. Wrench all pieces to the floor
    2. +
    3. Add wires to all the pieces
    4. +
    5. Close all the panels with your screwdriver
    6. +
    + +

    Use

    + +
      +
    1. Open the control panel
    2. +
    3. Set the speed to 2
    4. +
    5. Start firing at the singularity generator
    6. +
    7. When the singularity reaches a large enough size so it starts moving on it's own set the speed down to 0, but don't shut it off
    8. +
    9. Remember to wear a radiation suit when working with this machine... we did tell you that at the start, right?
    10. +
    + + + "} + + +/obj/item/book/manual/supermatter_engine + name = "Supermatter Engine User's Guide" + icon_state = "bookParticleAccelerator" //TEMP FIXME + author = "Waleed Asad" + title = "Supermatter Engine User's Guide" + + dat = {"Engineering notes on single-stage Supermatter engine,
    + -Waleed Asad
    + + A word of caution, do not enter the engine room, for any reason, without radiation protection and mesons on. The status of the engine may be unpredictable even when you believe it is .off.. This is an important level of personal protection.

    + + The engine has two basic modes of functionality. He has observed that it is capable of both a safe level of operation and a modified, high output mode.

    + +
    Notes on starting the basic function mode, dubbed .Heat-Primary Mode..


    + + 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.

    + + 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.

    + + 3. Apply N2 gas. Retrieve the two N2 canisters from storage and bring them to the engine room. Attach one of them to the input section of the engine gas system located next to the collectors. Keep it attached until the N2 pressure is low enough to turn the canister light red. Replace it with the second canister to keep N2 pressure at optimal levels.

    + + 4. Begin primary emitter burst series. This means firing a single emitter for its first four shots. It is important to move to this step quickly. The onboard SMES units may not have enough power to run the emitters if left alone too long on-station. This engine can produce enough power on its own to run the entire station, ignoring the SMES units completely, and is wired to do so.

    + + 5. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.)

    + + 6. Begin secondary emitter burst series. Before firing the emitter again, check the power in the line with a multimeter (Do not forget electrical gloves.) The engine is running at high efficiency when the value exceeds 200,000 power units.

    + + 7. Maintain engine power. When power in the lines gets low, add an additional emitter burst series to bring power to normal levels.


    + + + +
    The second mode for running the engine uses a gas mix to produce a reaction within the Supermatter. This mode requires CE or Atmospheric help to setup. This has been dubbed the .O2-Reaction Mode..


    + + THIS MODE CAN CAUSE A RUNAWAY REACTION, LEADING TO CATASTROPHIC FAILURE IF NOT MAINTAINED. NEVER FORGET ABOUT THE ENGINE IN THIS MODE.

    + + Additionally, this mode can be used for what is called a .Cold Start.. If the station has no power in the SMES to run the emitters, using this mode will allow enough power output to run them, and quickly reach an acceptable level of power output.

    + + 1. Prepare collector arrays. This is done standard to any text on their function by wrenching them down, filling six plasma tanks with a plasma canister, and inserting the tank into the collectors one by one. Finally, initialize each collector.

    + + 2. Prepare gas system. Before introducing any gas to the Supermatter engine room, it is important to remember the small but vital steps to preparing this section. First, set the input gas pump and output gas flow pump to 4500, or maximum flow. Second, switch the digital switching valve into the .up. position, in order to circulate the gas back toward the coolers and collectors.

    + + 3. Modify the engine room filters. Unlike the Heat-Primary Mode, it is important to change the filters attached to the gas system to stop filtering O2, and start filtering Carbon Molecules. O2-Reaction Mode produces far more plasma than Heat-Primary, therefor filtering it off is essential.

    + + 4. Switch SMES units to primary settings. Maximize input and set the devices to automatically charge, additionally turn their outputs on if they are off unless power is to be saved (Which can be useful in case of later failures.) If you check the power in the system lines at this point you will find that it is constantly going up. Indeed, with just the addition of O2 to the Supermatter, it will begin outputting power.

    + + 5. Begin primary emitter burst series. Fire a single emitter for a series of four pulses, or a single series, and turn it off. Do not over power the Supermatter. The reaction is self sustaining and propagating. As long as O2 is in the chamber, it will continue outputting MORE power.

    + + 6. Maintain follow up operations. Remember to check the temp of the core gas and switch to the Heat-Primary function, or vent the core room when problems begin if required.

    + + Notes on Supermatter Reaction Function and Drawbacks-

    + + After several hours of observation an interesting phenomenon was witnessed. The Supermatter undergoes a constant self-sustaining reaction when given an extremely high O2 concentration. Anything about 80% or higher typically will cause this reaction. The Supermatter will continue to react whenever this gas mix is in the same room as the Supermatter.

    + + To understand why O2-Reaction mode is dangerous, the core principle of the Supermatter must be understood. The Supermatter emits three things when .not safe,. that is any time it is giving off power. These things are:

    + + *Radiation (which is converted into power by the collectors,)
    + *Heat (which is removed via the gas exchange system and coolers,)
    + *External gas (in the form of plasma and O2.)
    + + When in Heat-Primary mode, far more heat and plasma are produced than radiation. In O2-Reaction mode, very little heat and only moderate amounts of plasma are produced, however HUGE amounts of energy leaving the Supermatter is in the form of radiation.

    + + The O2-Reaction engine mode has a single drawback which has been eluded to more than once so far and that is very simple. The engine room will continue to grow hotter as the constant reaction continues. Eventually, there will be what he calls the .critical gas mix.. This is the point at which the constant adding of plasma to the mix of air around the Supermatter changes the gas concentration to below the tolerance. When this happens, two things occur. First, the Supermatter switches to its primary mode of operation where in huge amounts of heat are produced by the engine rather than low amounts with high power output. Second, an uncontrollable increase in heat within the Supermatter chamber will occur. This will lead to a spark-up, igniting the plasma in the Supermatter chamber, wildly increasing both pressure and temperature.

    + + While the O2-Reaction mode is dangerous, it does produce heavy amounts of energy. Consider using this mode only in short amounts to fill the SMES, and switch back later in the shift to keep things flowing normally.

    + + + Notes on Supermatter Containment and Emergency Procedures-

    + + While a constant vigil on the Supermatter is not required, regular checkups are important. Verify the temp of gas leaving the Supermatter chamber for unsafe levels, and ensure that the plasma in the chamber is at a safe concentration. Of course, also make sure the chamber is not on fire. A fire in the core chamber is very difficult to put out. As any Toxin scientist can tell you, even low amounts of plasma can burn at very high temperatures. This burning creates a huge increase in pressure and more importantly, temperature of the crystal itself.

    + + The Supermatter is strong, but not invincible. When the Supermatter is heated too much, its crystal structure will attempt to liquify. The change in atomic structure of the Supermatter leads to a single reaction, a massive explosion. The computer chip attached to the Supermatter core will warn the station when stability is threatened. It will then offer a second warning, when things have become dangerously close to total destruction of the core.

    + + Located both within the supermatter monitoring room and engine room is the vent control button. This button allows the Core Vent Controls to be accessed, venting the room to space. Remember however, that this process takes time. If a fire is raging, and the pressure is higher than fathomable, it will take a great deal of time to vent the room. Also located in the supermatter monitoring room is the emergency core eject button. A new core can be ordered from cargo. It is often not worth the lives of the crew to hold on to it, not to mention the structural damage. However, if by some mistake the Supermatter is pushed off or removed from the mass ejector it sits on, manual reposition will be required. Which is very dangerous and often leads to death.

    + + The Supermatter is extremely dangerous. More dangerous than people give it credit for. It can destroy you in an instant, without hesitation, reducing you to a pile of dust. When working closely with Supermatter it is.. suggested to get a genetic backup and do not wear any items of value to you. The Supermatter core can be pulled if grabbed properly by the base, but pushing is not possible.


    + + + In Closing-

    + + Remember that the Supermatter is dangerous, and the core is dangerous still. Venting the core room is always an option if you are even remotely worried, utilizing Atmospherics to properly ready the room once more for core function. It is always a good idea to check up regularly on the temperature of gas leaving the chamber, as well as the power in the system lines. Lastly, once again remember, never touch the Supermatter with anything. Ever.

    + + -Waleed Asad, Senior Engine Technician."} + +/obj/item/book/manual/engineering_hacking + name = "Hacking" + icon_state ="bookHacking" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Hacking" +//big pile of shit below. + + dat = {" + + + + + + + + + + + "} + +/obj/item/book/manual/engineering_singularity_safety + name = "Singularity Safety in Special Circumstances" + icon_state ="bookEngineeringSingularitySafety" + author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Singularity Safety in Special Circumstances" +//big pile of shit below. + + dat = {" + + + + +

    Singularity Safety in Special Circumstances

    + +

    Power outage

    + + A power problem has made the entire station loose power? Could be station-wide wiring problems or syndicate power sinks. In any case follow these steps: +

    + Step one: PANIC!
    + Step two: Get your ass over to engineering! QUICKLY!!!
    + Step three: Get to the Area Power Controller which controls the power to the emitters.
    + Step four: Swipe it with your ID card - if it doesn't unlock, continue with step 15.
    + Step five: Open the console and disengage the cover lock.
    + Step six: Pry open the APC with a Crowbar.
    + Step seven: Take out the empty power cell.
    + Step eight: Put in the new, full power cell - if you don't have one, continue with step 15.
    + Step nine: Quickly put on a Radiation suit.
    + Step ten: Check if the singularity field generators withstood the down-time - if they didn't, continue with step 15.
    + Step eleven: Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering apc is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the apc to the powernet. If it wasn't a powersink which caused it, then skip to step 14.
    + Step twelve: Grab your crowbar and pry away the tile closest to the APC.
    + Step thirteen: Use the wirecutters to cut the wire which is conecting the grid to the terminal.
    + Step fourteen: Go to the bar and tell the guys how you saved them all. Stop reading this guide here.
    + Step fifteen: GET THE FUCK OUT OF THERE!!!
    +

    + +

    Shields get damaged

    + + Step one: GET THE FUCK OUT OF THERE!!! FORGET THE WOMEN AND CHILDREN, SAVE YOURSELF!!!
    + + + "} + +/obj/item/book/manual/hydroponics_pod_people + name = "The Human Harvest - From seed to market" + icon_state ="bookHydroponicsPodPeople" + author = "Farmer John" + title = "The Human Harvest - From seed to market" + dat = {" + + + + +

    Growing Humans

    + + Why would you want to grow humans? Well I'm expecting most readers to be in the slave trade, but a few might actually + want to revive fallen comrades. Growing pod people is easy, but prone to disaster. +

    +

      +
    1. Find a dead person who is in need of cloning.
    2. +
    3. Take a blood sample with a syringe.
    4. +
    5. Inject a seed pack with the blood sample.
    6. +
    7. Plant the seeds.
    8. +
    9. Tend to the plants water and nutrition levels until it is time to harvest the cloned human.
    10. +
    +

    + It really is that easy! Good luck! + + + + "} + +/obj/item/book/manual/medical_cloning + name = "Cloning techniques of the 26th century" + icon_state ="bookCloning" + author = "Medical Journal, volume 3" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "Cloning techniques of the 26th century" +//big pile of shit below. + + dat = {" + + + + + +

    How to Clone People

    + So thereÂ’s 50 dead people lying on the floor, chairs are spinning like no tomorrow and you havenÂ’t the foggiest idea of what to do? Not to worry! This guide is intended to teach you how to clone people and how to do it right, in a simple step-by-step process! If at any point of the guide you have a mental meltdown, genetics probably isnÂ’t for you and you should get a job-change as soon as possible before youÂ’re sued for malpractice. + +
      +
    1. Acquire body
    2. +
    3. Strip body
    4. +
    5. Put body in cloning machine
    6. +
    7. Scan body
    8. +
    9. Clone body
    10. +
    11. Get clean Structurel Enzymes for the body
    12. +
    13. Put body in morgue
    14. +
    15. Await cloned body
    16. +
    17. Use the clean SW injector
    18. +
    19. Give person clothes back
    20. +
    21. Send person on their way
    22. +
    + +

    Step 1: Acquire body

    + This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step. + +

    Step 2: Strip body

    + The cloning machine does not like abiotic items. What this means is you canÂ’t clone anyone if theyÂ’re wearing clothes, so take all of it off. If itÂ’s just one person, itÂ’s courteous to put their possessions in the closet. If you have about seven people awaiting cloning, just leave the piles where they are, but donÂ’t mix them around and for GodÂ’s sake donÂ’t let people in to steal them. + +

    Step 3: Put body in cloning machine

    + Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset. + +

    Step 4: Scan body

    + Go onto the computer and scan the body by pressing ‘Scan - ’. If you’re successful, they will be added to the records (note that this can be done at any time, even with living people, so that they can be cloned without a body in the event that they are lying dead on port solars and didn‘t turn on their suit sensors)! If not, and it says “Error: Mental interface failure.”, then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, click ‘Refresh‘ and try scanning them again. If there’s no success, threaten them with gibbing. Still no success? Skip over to Step 7 and don‘t continue after it, as you have an unresponsive body and it cannot be cloned. If you got “Error: Unable to locate valid genetic data.“, you are trying to clone a monkey - start over. + +

    Step 5: Clone body

    + Now that the body has a record, click ’View Records’, click the subject’s name, and then click ‘Clone’ to start the cloning process. Congratulations! You’re halfway there. Remember not to ‘Eject’ the cloning pod as this will kill the developing clone and you’ll have to start the process again. + +

    Step 6: Get clean SEs for body

    + Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when they’re done. If you’re a competent Geneticist, you will already have one ready on your working computer. If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. Go into View/Edit/Transfer Buffer, find an open slot and click “SE“ to save it. Then click ‘Injector’ to get the SEs in syringe form. Put this in your pocket or something for when the body is done. + +

    Step 7: Put body in morgue

    + Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed “CHEF MEAT” in order to avoid confusion. + +

    Step 8: Await cloned body

    + Now go back to the lab and wait for your patient to be cloned. It wonÂ’t be long now, I promise. + +

    Step 9: Use the clean SE injector on person

    + Has your body been cloned yet? Great! As soon as the guy pops out, grab your injector and jab it in them. Once youÂ’ve injected them, they now have clean Structural Enzymes and their defects, if any, will disappear in a short while. + +

    Step 10: Give person clothes back

    + Obviously the person will be naked after they have been cloned. Provided you werenÂ’t an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. No matter how cruel you are, itÂ’s simply against protocol to force your patients to walk outside naked. + +

    Step 11: Send person on their way

    + Give the patient one last check-over - make sure they donÂ’t still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. Once youÂ’re done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance. + +

    If youÂ’ve gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target. + + + + + + "} + + +/obj/item/book/manual/ripley_build_and_repair + name = "APLU \"Ripley\" Construction and Operation Manual" + icon_state ="book" + author = "Weyland-Yutani Corp" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + title = "APLU \"Ripley\" Construction and Operation Manual" +//big pile of shit below. + + dat = {" + + + + +

    + Weyland-Yutani - Building Better Worlds +

    Autonomous Power Loader Unit \"Ripley\"

    +
    +

    Specifications:

    +
      -
    • Class: Autonomous Power Loader
    • -
    • Scope: Logistics and Construction
    • -
    • Weight: 820kg (without operator and with empty cargo compartment)
    • -
    • Height: 2.5m
    • -
    • Width: 1.8m
    • -
    • Top speed: 5km/hour
    • -
    • Operation in vacuum/hostile environment: Possible -
    • Airtank Volume: 500liters
    • -
    • Devices: -
        -
      • Hydraulic Clamp
      • -
      • High-speed Drill
      • -
      -
    • -
    • Propulsion Device: Powercell-powered electro-hydraulic system.
    • -
    • Powercell capacity: Varies.
    • -
    - -

    Construction:

    -
      -
    1. Connect all exosuit parts to the chassis frame
    2. -
    3. Connect all hydraulic fittings and tighten them up with a wrench
    4. -
    5. Adjust the servohydraulics with a screwdriver
    6. -
    7. Wire the chassis. (Cable is not included.)
    8. -
    9. Use the wirecutters to remove the excess cable if needed.
    10. -
    11. Install the central control module (Not included. Use supplied datadisk to create one).
    12. -
    13. Secure the mainboard with a screwdriver.
    14. -
    15. Install the peripherals control module (Not included. Use supplied datadisk to create one).
    16. -
    17. Secure the peripherals control module with a screwdriver
    18. -
    19. Install the internal armor plating (Not included due to Nanotrasen regulations. Can be made using 5 metal sheets.)
    20. -
    21. Secure the internal armor plating with a wrench
    22. -
    23. Weld the internal armor plating to the chassis
    24. -
    25. Install the external reinforced armor plating (Not included due to Nanotrasen regulations. Can be made using 5 reinforced metal sheets.)
    26. -
    27. Secure the external reinforced armor plating with a wrench
    28. -
    29. Weld the external reinforced armor plating to the chassis
    30. -
    31. -
    32. Additional Information:
    33. -
    34. The firefighting variation is made in a similar fashion.
    35. -
    36. A firesuit must be connected to the Firefighter chassis for heat shielding.
    37. -
    38. Internal armor is plasteel for additional strength.
    39. -
    40. External armor must be installed in 2 parts, totaling 10 sheets.
    41. -
    42. Completed mech is more resiliant against fire, and is a bit more durable overall
    43. -
    44. Nanotrasen is determined to the safety of its investments employees.
    45. -
    - - - -

    Operation

    - Coming soon... - "} - -/obj/item/book/manual/experimentor - name = "Mentoring your Experiments" - icon_state = "rdbook" - author = "Dr. H.P. Kritz" - title = "Mentoring your Experiments" - dat = {" - - - - -

    THE E.X.P.E.R.I-MENTOR

    - The Enhanced Xenobiological Period Extraction (and) Restoration Instructor is a machine designed to discover the secrets behind every item in existence. - With advanced technology, it can process 99.95% of items, and discover their uses and secrets. - The E.X.P.E.R.I-MENTOR is a Research apparatus that takes items, and through a process of elimination, it allows you to deduce new technological designs from them. - Due to the volatile nature of the E.X.P.E.R.I-MENTOR, there is a slight chance for malfunction, potentially causing irreparable damage to you or your environment. - However, upgrading the apparatus has proven to decrease the chances of undesirable, potentially life-threatening outcomes. - Please note that the E.X.P.E.R.I-MENTOR uses a state-of-the-art random generator, which has a larger entropy than the observable universe, - therefore it can generate wildly different results each day, therefore it is highly suggested to re-scan objects of interests frequently (e.g. each shift). - -

    BASIC PROCESS

    - The usage of the E.X.P.E.R.I-MENTOR is quite simple: -
      -
    1. Find an item with a technological background
    2. -
    3. Insert the item into the E.X.P.E.R.I-MENTOR
    4. -
    5. Cycle through each processing method of the device.
    6. -
    7. Stand back, even in case of a successful experiment, as the machine might produce undesired behaviour.
    8. -
    - -

    ADVANCED USAGE

    - The E.X.P.E.R.I-MENTOR has a variety of uses, beyond menial research work. The different results can be used to combat localised events, or even to get special items. - - The E.X.P.E.R.I-MENTOR's OBLITERATE function has the added use of transferring the destroyed item's material into a linked lathe. - - The IRRADIATE function can be used to transform items into other items, resulting in potential upgrades (or downgrades). - - Users should remember to always wear appropriate protection when using the machine, because malfunction can occur at any moment! - -

    EVENTS

    -

    GLOBAL (happens at any time):

    -
      -
    1. DETECTION MALFUNCTION - The machine's onboard sensors have malfunctioned, causing it to redefine the item's experiment type. - Produces the message: The E.X.P.E.R.I-MENTOR's onboard detection system has malfunctioned!
    2. - -
    3. IANIZATION - The machine's onboard corgi-filter has malfunctioned, causing it to produce a corgi from.. somewhere. - Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ian-izing the air around it!
    4. - -
    5. RUNTIME ERROR - The machine's onboard C4T-P processor has encountered a critical error, causing it to produce a cat from.. somewhere. - Produces the message: The E.X.P.E.R.I-MENTOR encounters a run-time error!
    6. - -
    7. B100DG0D.EXE - The machine has encountered an unknown subroutine, which has been injected into it's runtime. It upgrades the held item! - Produces the message: The E.X.P.E.R.I-MENTOR improves the banana, drawing the life essence of those nearby!
    8. - -
    9. POWERSINK - The machine's PSU has tripped the charging mechanism! It consumes massive amounts of power! - Produces the message: The E.X.P.E.R.I-MENTOR begins to smoke and hiss, shaking violently!
    10. -
    -

    FAIL:

    - This event is produced when the item mismatches the selected experiment. - Produces a random message similar to: "the Banana rumbles, and shakes, the experiment was a failure!" - -

    POKE:

    -
      -
    1. WILD ARMS - The machine's gryoscopic processors malfunction, causing it to lash out at nearby people with it's arms. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions and destroys the banana, lashing it's arms out at nearby people!
    2. - -
    3. MISTYPE - The machine's interface has been garbled, and it switches to OBLITERATE. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions!
    4. - -
    5. THROW - The machine's spatial recognition device has shifted several meters across the room, causing it to try and repostion the item there. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, throwing the banana!
    6. -
    -

    IRRADIATE:

    -
      -
    1. RADIATION LEAK - The machine's shield has failed, resulting in a toxic radiation leak. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking radiation!
    2. - -
    3. RADIATION DUMP - The machine's recycling and containment functions have failed, resulting in a dump of toxic waste around it - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing toxic waste!
    4. - -
    5. MUTATION - The machine's radio-isotope level meter has malfunctioned, causing it over-irradiate the item, making it transform. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, transforming the banana!
    6. -
    -

    GAS:

    -
      -
    1. TOXIN LEAK - The machine's filtering and vent systems have failed, resulting in a cloud of toxic gas being expelled. - Produces the message: The E.X.P.E.R.I-MENTOR destroys the banana, leaking dangerous gas!
    2. - -
    3. GAS LEAK - The machine's vent systems have failed, resulting in a cloud of harmless, but obscuring gas. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing harmless gas!
    4. - -
    5. ELECTROMAGNETIC IONS - The machine's electrolytic scanners have failed, causing a dangerous Electromagnetic reaction. - Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ionizing the air around it!
    6. -
    -

    HEAT:

    -
      -
    1. TOASTER - The machine's heating coils have come into contact with the machine's gas storage, causing a large, sudden blast of flame. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and releasing a burst of flame!
    2. - -
    3. SAUNA - The machine's vent loop has sprung a leak, resulting in a large amount of superheated air being dumped around it. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking hot air!
    4. - -
    5. EMERGENCY VENT - The machine's temperature gauge has malfunctioned, resulting in it attempting to cool the area around it, but instead, dumping a cloud of steam. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, activating it's emergency coolant systems!
    6. -
    -

    COLD:

    -
      -
    1. FREEZER - The machine's cooling loop has sprung a leak, resulting in a cloud of super-cooled liquid being blasted into the air. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and releasing a dangerous cloud of coolant!
    2. - -
    3. FRIDGE - The machine's cooling loop has been exposed to the outside air, resulting in a large decrease in temperature. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and leaking cold air!
    4. - -
    5. SNOWSTORM - The machine's cooling loop has come into contact with the heating coils, resulting in a sudden blast of cool air. - Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, releasing a flurry of chilly air as the banana pops out!
    6. -
    -

    OBLITERATE:

    -
      -
    1. IMPLOSION - The machine's pressure leveller has malfunctioned, causing it to pierce the space-time momentarily, making everything in the area fly towards it. - Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes way too many levels too high, crushing right through space-time!
    2. - -
    3. DISTORTION - The machine's pressure leveller has completely disabled, resulting in a momentary space-time distortion, causing everything to fly around. - Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes one level too high, crushing right into space-time!
    4. -
    - - - "} - -/obj/item/book/manual/research_and_development - name = "Research and Development 101" - icon_state = "rdbook" - author = "Dr. L. Ight" - title = "Research and Development 101" - dat = {" - - - - - - -

    Science For Dummies

    - So you want to further SCIENCE? Good man/woman/thing! However, SCIENCE is a complicated process even though it's quite easy. For the most part, it's a three step process: -
      -
    1. 1) Deconstruct items in the Destructive Analyzer to advance technology or improve the design.
    2. -
    3. 2) Build unlocked designs in the Protolathe and Circuit Imprinter
    4. -
    5. 3) Repeat!
    6. -
    - - Those are the basic steps to furthing science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter. - -

    The R&D Console

    - The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controled. More on those systems in their own sections. On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, because if it gets damaged, you'll lose your data! In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database. - NOTE: The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices. - -

    Destructive Analyzer

    - This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, you can destroy the object to further those sciences. Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather then futher science. If their reliability is high enough ,it'll also advance their related technologies. - -

    Circuit Imprinter

    - This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules. - -

    Protolathe

    - This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid plasma, silver, gold, and diamonds along with a variety of chemicals to produce devices. The downside is that, again, not all devices you make are 100% reliable when you first discover them. - -

    Reliability and You

    - As it has been stated, many devices when they're first discovered do not have a 100% reliablity when you first discover them. Instead, the reliablity of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. Once the device reachs a certain minimum reliability, you'll gain tech advancements from it. - -

    Building a Better Machine

    - Many machines produces from circuit boards and inserted into a machine frame require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. If you use these parts when constructing something, its attributes may be improved. For example, if you use an advanced matter bin when constructing an autolathe (rather then a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine. - - - "} - - -/obj/item/book/manual/robotics_cyborgs - name = "Cyborgs for Dummies" - icon_state = "borgbook" - author = "XISC" - title = "Cyborgs for Dummies" - dat = {" - - - - - -

    Cyborgs for Dummies

    - -

    Chapters

    - -
      +
    • Class: Autonomous Power Loader
    • +
    • Scope: Logistics and Construction
    • +
    • Weight: 820kg (without operator and with empty cargo compartment)
    • +
    • Height: 2.5m
    • +
    • Width: 1.8m
    • +
    • Top speed: 5km/hour
    • +
    • Operation in vacuum/hostile environment: Possible +
    • Airtank Volume: 500liters
    • +
    • Devices: +
        +
      • Hydraulic Clamp
      • +
      • High-speed Drill
      • +
      +
    • +
    • Propulsion Device: Powercell-powered electro-hydraulic system.
    • +
    • Powercell capacity: Varies.
    • +
    + +

    Construction:

    +
      +
    1. Connect all exosuit parts to the chassis frame
    2. +
    3. Connect all hydraulic fittings and tighten them up with a wrench
    4. +
    5. Adjust the servohydraulics with a screwdriver
    6. +
    7. Wire the chassis. (Cable is not included.)
    8. +
    9. Use the wirecutters to remove the excess cable if needed.
    10. +
    11. Install the central control module (Not included. Use supplied datadisk to create one).
    12. +
    13. Secure the mainboard with a screwdriver.
    14. +
    15. Install the peripherals control module (Not included. Use supplied datadisk to create one).
    16. +
    17. Secure the peripherals control module with a screwdriver
    18. +
    19. Install the internal armor plating (Not included due to Nanotrasen regulations. Can be made using 5 metal sheets.)
    20. +
    21. Secure the internal armor plating with a wrench
    22. +
    23. Weld the internal armor plating to the chassis
    24. +
    25. Install the external reinforced armor plating (Not included due to Nanotrasen regulations. Can be made using 5 reinforced metal sheets.)
    26. +
    27. Secure the external reinforced armor plating with a wrench
    28. +
    29. Weld the external reinforced armor plating to the chassis
    30. +
    31. +
    32. Additional Information:
    33. +
    34. The firefighting variation is made in a similar fashion.
    35. +
    36. A firesuit must be connected to the Firefighter chassis for heat shielding.
    37. +
    38. Internal armor is plasteel for additional strength.
    39. +
    40. External armor must be installed in 2 parts, totaling 10 sheets.
    41. +
    42. Completed mech is more resiliant against fire, and is a bit more durable overall
    43. +
    44. Nanotrasen is determined to the safety of its investments employees.
    45. +
    + + + +

    Operation

    + Coming soon... + "} + +/obj/item/book/manual/experimentor + name = "Mentoring your Experiments" + icon_state = "rdbook" + author = "Dr. H.P. Kritz" + title = "Mentoring your Experiments" + dat = {" + + + + +

    THE E.X.P.E.R.I-MENTOR

    + The Enhanced Xenobiological Period Extraction (and) Restoration Instructor is a machine designed to discover the secrets behind every item in existence. + With advanced technology, it can process 99.95% of items, and discover their uses and secrets. + The E.X.P.E.R.I-MENTOR is a Research apparatus that takes items, and through a process of elimination, it allows you to deduce new technological designs from them. + Due to the volatile nature of the E.X.P.E.R.I-MENTOR, there is a slight chance for malfunction, potentially causing irreparable damage to you or your environment. + However, upgrading the apparatus has proven to decrease the chances of undesirable, potentially life-threatening outcomes. + Please note that the E.X.P.E.R.I-MENTOR uses a state-of-the-art random generator, which has a larger entropy than the observable universe, + therefore it can generate wildly different results each day, therefore it is highly suggested to re-scan objects of interests frequently (e.g. each shift). + +

    BASIC PROCESS

    + The usage of the E.X.P.E.R.I-MENTOR is quite simple: +
      +
    1. Find an item with a technological background
    2. +
    3. Insert the item into the E.X.P.E.R.I-MENTOR
    4. +
    5. Cycle through each processing method of the device.
    6. +
    7. Stand back, even in case of a successful experiment, as the machine might produce undesired behaviour.
    8. +
    + +

    ADVANCED USAGE

    + The E.X.P.E.R.I-MENTOR has a variety of uses, beyond menial research work. The different results can be used to combat localised events, or even to get special items. + + The E.X.P.E.R.I-MENTOR's OBLITERATE function has the added use of transferring the destroyed item's material into a linked lathe. + + The IRRADIATE function can be used to transform items into other items, resulting in potential upgrades (or downgrades). + + Users should remember to always wear appropriate protection when using the machine, because malfunction can occur at any moment! + +

    EVENTS

    +

    GLOBAL (happens at any time):

    +
      +
    1. DETECTION MALFUNCTION - The machine's onboard sensors have malfunctioned, causing it to redefine the item's experiment type. + Produces the message: The E.X.P.E.R.I-MENTOR's onboard detection system has malfunctioned!
    2. + +
    3. IANIZATION - The machine's onboard corgi-filter has malfunctioned, causing it to produce a corgi from.. somewhere. + Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ian-izing the air around it!
    4. + +
    5. RUNTIME ERROR - The machine's onboard C4T-P processor has encountered a critical error, causing it to produce a cat from.. somewhere. + Produces the message: The E.X.P.E.R.I-MENTOR encounters a run-time error!
    6. + +
    7. B100DG0D.EXE - The machine has encountered an unknown subroutine, which has been injected into it's runtime. It upgrades the held item! + Produces the message: The E.X.P.E.R.I-MENTOR improves the banana, drawing the life essence of those nearby!
    8. + +
    9. POWERSINK - The machine's PSU has tripped the charging mechanism! It consumes massive amounts of power! + Produces the message: The E.X.P.E.R.I-MENTOR begins to smoke and hiss, shaking violently!
    10. +
    +

    FAIL:

    + This event is produced when the item mismatches the selected experiment. + Produces a random message similar to: "the Banana rumbles, and shakes, the experiment was a failure!" + +

    POKE:

    +
      +
    1. WILD ARMS - The machine's gryoscopic processors malfunction, causing it to lash out at nearby people with it's arms. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions and destroys the banana, lashing it's arms out at nearby people!
    2. + +
    3. MISTYPE - The machine's interface has been garbled, and it switches to OBLITERATE. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions!
    4. + +
    5. THROW - The machine's spatial recognition device has shifted several meters across the room, causing it to try and repostion the item there. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, throwing the banana!
    6. +
    +

    IRRADIATE:

    +
      +
    1. RADIATION LEAK - The machine's shield has failed, resulting in a toxic radiation leak. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking radiation!
    2. + +
    3. RADIATION DUMP - The machine's recycling and containment functions have failed, resulting in a dump of toxic waste around it + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing toxic waste!
    4. + +
    5. MUTATION - The machine's radio-isotope level meter has malfunctioned, causing it over-irradiate the item, making it transform. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, transforming the banana!
    6. +
    +

    GAS:

    +
      +
    1. TOXIN LEAK - The machine's filtering and vent systems have failed, resulting in a cloud of toxic gas being expelled. + Produces the message: The E.X.P.E.R.I-MENTOR destroys the banana, leaking dangerous gas!
    2. + +
    3. GAS LEAK - The machine's vent systems have failed, resulting in a cloud of harmless, but obscuring gas. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing harmless gas!
    4. + +
    5. ELECTROMAGNETIC IONS - The machine's electrolytic scanners have failed, causing a dangerous Electromagnetic reaction. + Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ionizing the air around it!
    6. +
    +

    HEAT:

    +
      +
    1. TOASTER - The machine's heating coils have come into contact with the machine's gas storage, causing a large, sudden blast of flame. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and releasing a burst of flame!
    2. + +
    3. SAUNA - The machine's vent loop has sprung a leak, resulting in a large amount of superheated air being dumped around it. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking hot air!
    4. + +
    5. EMERGENCY VENT - The machine's temperature gauge has malfunctioned, resulting in it attempting to cool the area around it, but instead, dumping a cloud of steam. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, activating it's emergency coolant systems!
    6. +
    +

    COLD:

    +
      +
    1. FREEZER - The machine's cooling loop has sprung a leak, resulting in a cloud of super-cooled liquid being blasted into the air. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and releasing a dangerous cloud of coolant!
    2. + +
    3. FRIDGE - The machine's cooling loop has been exposed to the outside air, resulting in a large decrease in temperature. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and leaking cold air!
    4. + +
    5. SNOWSTORM - The machine's cooling loop has come into contact with the heating coils, resulting in a sudden blast of cool air. + Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, releasing a flurry of chilly air as the banana pops out!
    6. +
    +

    OBLITERATE:

    +
      +
    1. IMPLOSION - The machine's pressure leveller has malfunctioned, causing it to pierce the space-time momentarily, making everything in the area fly towards it. + Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes way too many levels too high, crushing right through space-time!
    2. + +
    3. DISTORTION - The machine's pressure leveller has completely disabled, resulting in a momentary space-time distortion, causing everything to fly around. + Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes one level too high, crushing right into space-time!
    4. +
    + + + "} + +/obj/item/book/manual/research_and_development + name = "Research and Development 101" + icon_state = "rdbook" + author = "Dr. L. Ight" + title = "Research and Development 101" + dat = {" + + + + + + +

    Science For Dummies

    + So you want to further SCIENCE? Good man/woman/thing! However, SCIENCE is a complicated process even though it's quite easy. For the most part, it's a three step process: +
      +
    1. 1) Deconstruct items in the Destructive Analyzer to advance technology or improve the design.
    2. +
    3. 2) Build unlocked designs in the Protolathe and Circuit Imprinter
    4. +
    5. 3) Repeat!
    6. +
    + + Those are the basic steps to furthing science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter. + +

    The R&D Console

    + The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controled. More on those systems in their own sections. On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, because if it gets damaged, you'll lose your data! In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database. + NOTE: The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices. + +

    Destructive Analyzer

    + This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, you can destroy the object to further those sciences. Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather then futher science. If their reliability is high enough ,it'll also advance their related technologies. + +

    Circuit Imprinter

    + This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules. + +

    Protolathe

    + This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid plasma, silver, gold, and diamonds along with a variety of chemicals to produce devices. The downside is that, again, not all devices you make are 100% reliable when you first discover them. + +

    Reliability and You

    + As it has been stated, many devices when they're first discovered do not have a 100% reliablity when you first discover them. Instead, the reliablity of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. Once the device reachs a certain minimum reliability, you'll gain tech advancements from it. + +

    Building a Better Machine

    + Many machines produces from circuit boards and inserted into a machine frame require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. If you use these parts when constructing something, its attributes may be improved. For example, if you use an advanced matter bin when constructing an autolathe (rather then a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine. + + + "} + + +/obj/item/book/manual/robotics_cyborgs + name = "Cyborgs for Dummies" + icon_state = "borgbook" + author = "XISC" + title = "Cyborgs for Dummies" + dat = {" + + + + + +

    Cyborgs for Dummies

    + +

    Chapters

    + +
      +
    1. Cyborg Related Equipment
    2. +
    3. Cyborg Modules
    4. +
    5. Cyborg Construction
    6. +
    7. Cyborg Maintenance
    8. +
    9. Cyborg Repairs
    10. +
    11. In Case of Emergency
    12. +
    + + +

    Cyborg Related Equipment

    + +

    Exosuit Fabricator

    + The Exosuit Fabricator is the most important piece of equipment related to cyborgs. It allows the construction of the core cyborg parts. Without these machines, cyborgs can not be built. It seems that they may also benefit from advanced research techniques. + +

    Cyborg Recharging Station

    + This useful piece of equipment will suck power out of the power systems to charge a cyborg's power cell back up to full charge. + +

    Robotics Control Console

    + This useful piece of equipment can be used to immobolize or destroy a cyborg. A word of warning: Cyborgs are expensive pieces of equipment, do not destroy them without good reason, or Nanotrasen may see to it that it never happens again. + + +

    Cyborg Modules

    + When a cyborg is created it picks out of an array of modules to designate its purpose. There are 6 different cyborg modules. + +

    Standard Cyborg

    + The standard cyborg module is a multi-purpose cyborg. It is equipped with various modules, allowing it to do basic tasks.
    A Standard Cyborg comes with: +
      +
    • Crowbar
    • +
    • Stun Baton
    • +
    • Health Analyzer
    • +
    • Fire Extinguisher
    • +
    + +

    Engineering Cyborg

    + The Engineering cyborg module comes equipped with various engineering-related tools to help with engineering-related tasks.
    An Engineering Cyborg comes with: +
      +
    • A basic set of engineering tools
    • +
    • Metal Synthesizer
    • +
    • Reinforced Glass Synthesizer
    • +
    • An RCD
    • +
    • Wire Synthesizer
    • +
    • Fire Extinguisher
    • +
    • Built-in Optical Meson Scanners
    • +
    + +

    Mining Cyborg

    + The Mining Cyborg module comes equipped with the latest in mining equipment. They are efficient at mining due to no need for oxygen, but their power cells limit their time in the mines.
    A Mining Cyborg comes with: +
      +
    • Jackhammer
    • +
    • Shovel
    • +
    • Mining Satchel
    • +
    • Built-in Optical Meson Scanners
    • +
    + +

    Security Cyborg

    + The Security Cyborg module is equipped with effective security measures used to apprehend and arrest criminals without harming them a bit.
    A Security Cyborg comes with: +
      +
    • Stun Baton
    • +
    • Handcuffs
    • +
    • Taser
    • +
    + +

    Janitor Cyborg

    + The Janitor Cyborg module is equipped with various cleaning-facilitating devices.
    A Janitor Cyborg comes with: +
      +
    • Mop
    • +
    • Hand Bucket
    • +
    • Cleaning Spray Synthesizer and Spray Nozzle
    • +
    + +

    Service Cyborg

    + The service cyborg module comes ready to serve your human needs. It includes various entertainment and refreshment devices. Occasionally some service cyborgs may have been referred to as "Bros"
    A Service Cyborg comes with: +
      +
    • Shaker
    • +
    • Industrail Dropper
    • +
    • Platter
    • +
    • Beer Synthesizer
    • +
    • Zippo Lighter
    • +
    • Rapid-Service-Fabricator (Produces various entertainment and refreshment objects)
    • +
    • Pen
    • +
    + +

    Cyborg Construction

    + Cyborg construction is a rather easy process, requiring a decent amount of metal and a few other supplies.
    The required materials to make a cyborg are: +
      +
    • Metal
    • +
    • Two Flashes
    • +
    • One Power Cell (Preferrably rated to 15000w)
    • +
    • Some electrical wires
    • +
    • One Human Brain
    • +
    • One Man-Machine Interface
    • +
    + Once you have acquired the materials, you can start on construction of your cyborg.
    To construct a cyborg, follow the steps below: +
      +
    1. Start the Exosuit Fabricators constructing all of the cyborg parts
    2. +
    3. While the parts are being constructed, take your human brain, and place it inside the Man-Machine Interface
    4. +
    5. Once you have a Robot Head, place your two flashes inside the eye sockets
    6. +
    7. Once you have your Robot Chest, wire the Robot chest, then insert the power cell
    8. +
    9. Attach all of the Robot parts to the Robot frame
    10. +
    11. Insert the Man-Machine Interface (With the Brain inside) Into the Robot Body
    12. +
    13. Congratulations! You have a new cyborg!
    14. +
    + +

    Cyborg Maintenance

    + Occasionally Cyborgs may require maintenance of a couple types, this could include replacing a power cell with a charged one, or possibly maintaining the cyborg's internal wiring. + +

    Replacing a Power Cell

    + Replacing a Power cell is a common type of maintenance for cyborgs. It usually involves replacing the cell with a fully charged one, or upgrading the cell with a larger capacity cell.
    The steps to replace a cell are follows: +
      +
    1. Unlock the Cyborg's Interface by swiping your ID on it
    2. +
    3. Open the Cyborg's outer panel using a crowbar
    4. +
    5. Remove the old power cell
    6. +
    7. Insert the new power cell
    8. +
    9. Close the Cyborg's outer panel using a crowbar
    10. +
    11. Lock the Cyborg's Interface by swiping your ID on it, this will prevent non-qualified personnel from attempting to remove the power cell
    12. +
    + +

    Exposing the Internal Wiring

    + Exposing the internal wiring of a cyborg is fairly easy to do, and is mainly used for cyborg repairs.
    You can easily expose the internal wiring by following the steps below: +
      +
    1. Follow Steps 1 - 3 of "Replacing a Cyborg's Power Cell"
    2. +
    3. Open the cyborg's internal wiring panel by using a screwdriver to unsecure the panel
    4. +
    + To re-seal the cyborg's internal wiring: +
      +
    1. Use a screwdriver to secure the cyborg's internal panel
    2. +
    3. Follow steps 4 - 6 of "Replacing a Cyborg's Power Cell" to close up the cyborg
    4. +
    + +

    Cyborg Repairs

    + Occasionally a Cyborg may become damaged. This could be in the form of impact damage from a heavy or fast-travelling object, or it could be heat damage from high temperatures, or even lasers or Electromagnetic Pulses (EMPs). + +

    Dents

    + If a cyborg becomes damaged due to impact from heavy or fast-moving objects, it will become dented. Sure, a dent may not seem like much, but it can compromise the structural integrity of the cyborg, possibly causing a critical failure. + Dents in a cyborg's frame are rather easy to repair, all you need is to apply a welding tool to the dented area, and the high-tech cyborg frame will repair the dent under the heat of the welder. + +

    Excessive Heat Damage

    + If a cyborg becomes damaged due to excessive heat, it is likely that the internal wires will have been damaged. You must replace those wires to ensure that the cyborg remains functioning properly.
    To replace the internal wiring follow the steps below: +
      +
    1. Unlock the Cyborg's Interface by swiping your ID
    2. +
    3. Open the Cyborg's External Panel using a crowbar
    4. +
    5. Remove the Cyborg's Power Cell
    6. +
    7. Using a screwdriver, expose the internal wiring or the Cyborg
    8. +
    9. Replace the damaged wires inside the cyborg
    10. +
    11. Secure the internal wiring cover using a screwdriver
    12. +
    13. Insert the Cyborg's Power Cell
    14. +
    15. Close the Cyborg's External Panel using a crowbar
    16. +
    17. Lock the Cyborg's Interface by swiping your ID
    18. +
    + These repair tasks may seem difficult, but are essential to keep your cyborgs running at peak efficiency. + +

    In Case of Emergency

    + In case of emergency, there are a few steps you can take. + +

    "Rogue" Cyborgs

    + If the cyborgs seem to become "rogue", they may have non-standard laws. In this case, use extreme caution. + To repair the situation, follow these steps: +
      +
    1. Locate the nearest robotics console
    2. +
    3. Determine which cyborgs are "Rogue"
    4. +
    5. Press the lockdown button to immobolize the cyborg
    6. +
    7. Locate the cyborg
    8. +
    9. Expose the cyborg's internal wiring
    10. +
    11. Check to make sure the LawSync and AI Sync lights are lit
    12. +
    13. If they are not lit, pulse the LawSync wire using a multitool to enable the cyborg's Law Sync
    14. +
    15. Proceed to a cyborg upload console. Nanotrasen usually places these in the same location as AI uplaod consoles.
    16. +
    17. Use a "Reset" upload moduleto reset the cyborg's laws
    18. +
    19. Proceed to a Robotics Control console
    20. +
    21. Remove the lockdown on the cyborg
    22. +
    + +

    As a last resort

    + If all else fails in a case of cyborg-related emergency. There may be only one option. Using a Robotics Control console, you may have to remotely detonate the cyborg. +

    WARNING:

    Do not detonate a borg without an explicit reason for doing so. Cyborgs are expensive pieces of Nanotrasen equipment, and you may be punished for detonating them without reason. + + + + "} + +/obj/item/book/manual/security_space_law + name = "Space Law" + desc = "A set of Nanotrasen guidelines for keeping law and order on their space stations." + icon_state = "bookSpaceLaw" + force = 4 //advanced magistrate tactics + author = "Nanotrasen" + title = "Space Law" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/security_space_law/black + name = "Space Law - Limited Edition" + desc = "A leather-bound, immaculately-written copy of JUSTICE." + icon_state = "bookSpaceLawblack" + title = "Space Law - Limited Edition" + +/obj/item/book/manual/engineering_guide + name = "Engineering Textbook" + icon_state ="bookEngineering2" + author = "Engineering Encyclopedia" + title = "Engineering Textbook" + dat = {" + + + + + + + + + + "} + + +/obj/item/book/manual/chef_recipes + name = "Chef Recipes" + icon_state = "cooked_book" + author = "Victoria Ponsonby" + title = "Chef Recipes" + dat = {" + + + + + +

    Food for Dummies

    + Here is a guide on basic food recipes and also how to not poison your customers accidentally. + +

    Basics:

    + Knead an egg and some flour to make dough. Bake that to make a bun or flatten and cut it. + +

    Burger:

    + Put a bun and some meat into the microwave and turn it on. Then wait. + +

    Bread:

    + Put some dough and an egg into the microwave and then wait. + +

    Waffles:

    + Add two lumps of dough and 10u of sugar to the microwave and then wait. + +

    Popcorn:

    + Add 1 corn to the microwave and wait. + +

    Meat Steak:

    + Put a slice of meat, 1 unit of salt and 1 unit of pepper into the microwave and wait. + +

    Meat Pie:

    + Put a flattened piece of dough and some meat into the microwave and wait. + +

    Boiled Spaghetti:

    + Put the spaghetti (processed flour) and 5 units of water into the microwave and wait. + +

    Donuts:

    + Add some dough and 5 units of sugar to the microwave and wait. + +

    Fries:

    + Add one potato to the processor, then bake them in the microwave. + + + + + "} + +/obj/item/book/manual/barman_recipes + name = "Barman Recipes" + icon_state = "barbook" + author = "Sir John Rose" + title = "Barman Recipes" + dat = {" + + + + + +

    Drinks for dummies

    + Heres a guide for some basic drinks. + +

    Manly Dorf:

    + Mix ale and beer into a glass. + +

    Grog:

    + Mix rum and water into a glass. + +

    Black Russian:

    + Mix vodka and kahlua into a glass. + +

    Irish Cream:

    + Mix cream and whiskey into a glass. + +

    Screwdriver:

    + Mix vodka and orange juice into a glass. + +

    Cafe Latte:

    + Mix milk and coffee into a glass. + +

    Mead:

    + Mix Enzyme, water and sugar into a glass. + +

    Gin Tonic:

    + Mix gin and tonic into a glass. + +

    Classic Martini:

    + Mix vermouth and gin into a glass. + + + + + "} + + +/obj/item/book/manual/detective + name = "The Film Noir: Proper Procedures for Investigations" + icon_state ="bookDetective" + author = "Nanotrasen" + title = "The Film Noir: Proper Procedures for Investigations" + dat = {" + + + + +

    Detective Work

    + + Between your bouts of self-narration, and drinking whiskey on the rocks, you might get a case or two to solve.
    + To have the best chance to solve your case, follow these directions: +

    +

      +
    1. Go to the crime scene.
    2. +
    3. Take your scanner and scan EVERYTHING (Yes, the doors, the tables, even the dog.)
    4. +
    5. Once you are reasonably certain you have every scrap of evidence you can use, find all possible entry points and scan them, too.
    6. +
    7. Return to your office.
    8. +
    9. Using your forensic scanning computer, scan your Scanner to upload all of your evidence into the database.
    10. +
    11. Browse through the resulting dossiers, looking for the one that either has the most complete set of prints, or the most suspicious items handled.
    12. +
    13. If you have 80% or more of the print (The print is displayed) go to step 10, otherwise continue to step 8.
    14. +
    15. Look for clues from the suit fibres you found on your perp, and go about looking for more evidence with this new information, scanning as you go.
    16. +
    17. Try to get a fingerprint card of your perp, as if used in the computer, the prints will be completed on their dossier.
    18. +
    19. Assuming you have enough of a print to see it, grab the biggest complete piece of the print and search the security records for it.
    20. +
    21. Since you now have both your dossier and the name of the person, print both out as evidence, and get security to nab your baddie.
    22. +
    23. Give yourself a pat on the back and a bottle of the ships finest vodka, you did it!.
    24. +
    +

    + It really is that easy! Good luck! + + + "} + +/obj/item/book/manual/nuclear + name = "Fission Mailed: Nuclear Sabotage 101" + icon_state ="bookNuclear" + author = "Syndicate" + title = "Fission Mailed: Nuclear Sabotage 101" + dat = {" + Nuclear Explosives 101:
    + Hello and thank you for choosing the Syndicate for your nuclear information needs.
    + Today's crash course will deal with the operation of a Fusion Class Nanotrasen made Nuclear Device.
    + First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE.
    + Pressing any button on the compacted bomb will cause it to extend and bolt itself into place.
    + If this is done to unbolt it one must completely log in which at this time may not be possible.
    + To make the nuclear device functional:
    +

  • Place the nuclear device in the designated detonation zone.
  • +
  • Extend and anchor the nuclear device from its interface.
  • +
  • Insert the nuclear authorisation disk into slot.
  • +
  • Type numeric authorisation code into the keypad. This should have been provided. Note: If you make a mistake press R to reset the device. +
  • Press the E button to log onto the device.
  • + You now have activated the device. To deactivate the buttons at anytime for example when you've already prepped the bomb for detonation remove the auth disk OR press the R on the keypad.
    + Now the bomb CAN ONLY be detonated using the timer. Manual detonation is not an option.
    + Note: Nanotrasen is a pain in the neck.
    + Toggle off the SAFETY.
    + Note: You wouldn't believe how many Syndicate Operatives with doctorates have forgotten this step.
    + So use the - - and + + to set a det time between 5 seconds and 10 minutes.
    + Then press the timer toggle button to start the countdown.
    + Now remove the auth. disk so that the buttons deactivate.
    + Note: THE BOMB IS STILL SET AND WILL DETONATE
    + Now before you remove the disk if you need to move the bomb you can:
    + Toggle off the anchor, move it, and re-anchor.

    + Good luck. Remember the order:
    + Disk, Code, Safety, Timer, Disk, RUN!
    + Intelligence Analysts believe that normal Nanotrasen procedure is for the Captain to secure the nuclear authorisation disk.
    + Good luck! + "} + +/obj/item/book/manual/atmospipes + name = "Pipes and You: Getting To Know Your Scary Tools" + icon_state = "pipingbook" + author = "Maria Crash, Senior Atmospherics Technician" + title = "Pipes and You: Getting To Know Your Scary Tools" + dat = {" + + + + + + +

    Contents

    +
      +
    1. Author's Forward
    2. +
    3. Basic Piping
    4. +
    5. Insulated Pipes
    6. +
    7. Atmospherics Devices
    8. +
    9. Heat Exchange Systems
    10. +
    11. Final Checks
    12. +
    +

    + +

    HOW TO NOT SUCK QUITE SO HARD AT ATMOSPHERICS


    + Or: What the fuck does a "passive gate" do?

    + + Alright. It has come to my attention that a variety of people are unsure of what a "pipe" is and what it does. + Apparently there is an unnatural fear of these arcane devices and their "gases". Spooky, spooky. So, + this will tell you what every device constructable by an ordinary pipe dispenser within atmospherics actually does. + You are not going to learn what to do with them to be the super best person ever, or how to play guitar with passive gates, + or something like that. Just what stuff does.

    + + +

    Basic Pipes


    + The boring ones.
    + TMost ordinary pipes are pretty straightforward. They hold gas. If gas is moving in a direction for some reason, gas will flow in that direction. + That's about it. Even so, here's all of your wonderful pipe options.
    + +
  • Straight pipes: They're pipes. One-meter sections. Straight line. Pretty simple. Just about every pipe and device is based around this + standard one-meter size, so most things will take up as much space as one of these.
  • +
  • Bent pipes: Pipes with a 90 degree bend at the half-meter mark. My goodness.
  • +
  • Pipe manifolds: Pipes that are essentially a "T" shape, allowing you to connect three things at one point.
  • +
  • 4-way manifold: A four-way junction.
  • +
  • Pipe cap: Caps off the end of a pipe. Open ends don't actually vent air, because of the way the pipes are assembled, so, uh. Use them to decorate your house or something.
  • +
  • Manual Valve: A valve that will block off airflow when turned. Can't be used by the AI or cyborgs, because they don't have hands.
  • +
  • Manual T-Valve: Like a manual valve, but at the center of a manifold instead of a straight pipe.


  • + +

    Insulated Pipes


    + Special Public Service Announcement.
    + Our regular pipes are already insulated. These are completely worthless. Punch anyone who uses them.

    + +

    Devices:


    + They actually do something.
    + This is usually where people get frightened,
    afraid, and start calling on their gods and/or cowering in fear. Yes, I can see you doing that right now. + Stop it. It's unbecoming. Most of these are fairly straightforward.
    + +
  • Gas Pump: Take a wild guess. It moves gas in the direction it's pointing (marked by the red line on one end). It moves it based on pressure, the maximum output being 4500 kPa (kilopascals). + Ordinary atmospheric pressure, for comparison, is 101.3 kPa, and the minimum pressure of room-temperature pure oxygen needed to not suffocate in a matter of minutes is 16 kPa + (though 18 is preferred using internals, for various reasons).
  • +
  • Volume pump: This pump goes based on volume, instead of pressure, and the possible maximum pressure it can create in the pipe on the recieving end is double the gas pump because of this, + clocking in at an incredible 9000 kPa. If a pipe with this is destroyed or damaged, and this pressure of gas escapes, it can be incredibly dangerous depending on the size of the pipe filled. + Don't hook this to the distribution loop, or you will make babies cry and the Chief Engineer brutally beat you.
  • +
  • Passive gate: This is essentially a cap on the pressure of gas allowed to flow in a specific direction. + When turned on, instead of actively pumping gas, it measures the pressure flowing through it, and whatever pressure you set is the maximum: it'll cap after that. + In addition, it only lets gas flow one way. The direction the gas flows is opposite the red handle on it, which is confusing to people used to the red stripe on pumps pointing the way.
  • +
  • Unary vent: The basic vent used in rooms. It pumps gas into the room, but can't suck it back out. Controlled by the room's air alarm system.
  • +
  • Scrubber: The other half of room equipment. Filters air, and can suck it in entirely in what's called a "panic siphon". Actvating a panic siphon without very good reason will kill someone. Don't do it.
  • +
  • Meter: A little box with some gagues and numbers. Fasten it to any pipe or manifold, and it'll read you the pressure in it. Very useful.
  • +
  • Gas mixer: Two sides are input, one side is output. Mixes the gases pumped into it at the ratio defined. The side perpendicular to the other two is "node 2", for reference. + Can output this gas at pressures from 0-4500 kPa.
  • +
  • Gas filter: Essentially the opposite of a gas mixer. One side is input. The other two sides are output. One gas type will be filtered into the perpendicular output pipe, + the rest will continue out the other side. Can also output from 0-4500 kPa.
  • + +

    Heat Exchange Systems


    + Will not set you on fire.
    + These systems are used to transfer heat only between two pipes. They will not move gases or any other element, but will equalize the temperature (eventually). Note that because of how gases work (remember: pv=nRt), + a higher temperature will raise pressure, and a lower one will lower temperature.
    + +
  • Pipe: This is a pipe that will exchange heat with the surrounding atmosphere. Place in fire for superheating. Place in space for supercooling.
  • +
  • Bent Pipe: Take a wild guess.
  • +
  • Junction:Junction:The point where you connect your normal pipes to heat exchange pipes. Not necessary for heat exchangers, but necessary for H/E pipes/bent pipes.
  • +
  • Heat Exchanger: These funky-looking bits attach to an open pipe end. Put another heat exchanger directly across from it, and you can transfer heat across two pipes without having to have the gases touch. + This normally shouldn't exchange with the ambient air, despite being totally exposed. Just don't ask questions...

  • + + + That's about it for pipes. Go forth, armed with this knowledge, and try not to break, burn down, or kill anything. Please.
    + + + + "} + +/obj/item/book/manual/evaguide + name = "EVA Gear and You: Not Spending All Day Inside" + icon_state = "evabook" + author = "Maria Crash, Senior Atmospherics Technician" + title = "EVA Gear and You: Not Spending All Day Inside" + dat = {" + + + + + + +

    Contents

    +
      +
    1. A forward on using EVA gear
    2. +
    3. Donning a Civilian Suits
    4. +
    5. Putting on a Hardsuit
    6. +
    7. Final Checks
    8. +
    +

    + +

    EVA Gear and You: Not Spending All Day Inside


    + Or: How not to suffocate because there's a hole in your shoes

    + + EVA gear. Wonderful to use. It's useful for mining, engineering, and occasionally just surviving, if things are that bad. Most people have EVA training, + but apparently there are some on a space station who don't. This guide should give you a basic idea of how to use this gear, safely. It's split into two sections: + Civilian suits and hardsuits.

    + +

    Civilian Suits


    + The bulkiest things this side of Alpha Centauri
    + These suits are the grey ones that are stored in EVA. They're the more simple to get on, but are also a lot bulkier, and provide less protection from environmental hazards such as radiaion or physical impact. + As Medical, Engineering, Security, and Mining all have hardsuits of their own, these don't see much use, but knowing how to put them on is quite useful anyways.

    + + First, take the suit. It should be in three pieces: A top, a bottom,
    and a helmet. Put the bottom on first, shoes and the like will fit in it. If you have magnetic boots, however, + put them on on top of the suit's feet. Next, get the top on, as you would a shirt. It can be somewhat awkward putting these pieces on, due to the makeup of the suit, + but to an extent they will adjust to you. You can then find the snaps and seals around the waist, where the two pieces meet. Fasten these, and double-check their tightness. + The red indicators around the waist of the lower half will turn green when this is done correctly. Next, put on whatever breathing apparatus you're using, be it a gas mask or a breath mask. Make sure the oxygen tube is fastened into it. + Put on the helmet now, straight forward, and make sure the tube goes into the small opening specifically for internals. Again, fasten seals around the neck, a small indicator light in the inside of the helmet should go from red to off when all is fastened. + There is a small slot on the side of the suit where an emergency oxygen tank or extended emergency oxygen tank will fit, + but it is reccomended to have a full-sized tank on your back for EVA.

    + +

    Hardsuits


    + Heavy, uncomfortable, still the best option.
    + These suits come in Engineering, Mining, and the Armory. There's also a couple Medical Hardsuits in EVA. These provide a lot more protection than the standard suits.

    + + Similarly to the other suits, these are split into three parts. Fastening the pant and top are mostly the same as the other spacesuits, with the exception that these are a bit heavier, + though not as bulky. The helmet goes on differently, with the air tube feeing into the suit and out a hole near the left shoulder, while the helmet goes on turned ninety degrees counter-clockwise, + and then is screwed in for one and a quarter full rotations clockwise, leaving the faceplate directly in front of you. There is a small button on the right side of the helmet that activates the helmet light. + The tanks that fasten onto the side slot are emergency tanks, as
    well as full-sized oxygen tanks, leaving your back free for a backpack or satchel.

    + +

    FINAL CHECKS:


    +
  • Are all seals fastened correctly?
  • +
  • Do you either have shoes on under the suit, or magnetic boots on over it?
  • +
  • Do you have a mask on and internals on the suit or your back?
  • +
  • Do you have a way to communicate with the station in case something goes wrong?
  • +
  • Do you have a second person watching if this is a training session?

  • + + If you don't have any further issues, go out and do whatever is necessary.
    + + + + "} + +/obj/item/book/manual/faxes + name = "A Guide to Faxes" + desc = "A Nanotrasen-approved guide to writing faxes" + icon_state = "book6" + author = "Nanotrasen" + title = "A Guide to Faxes" + dat = {" + + + + + + + + +

    Contents

    +
      +
    1. What's a Fax?
    2. +
    3. When to Fax?
    4. +
    5. How to Fax?
    6. +
    +

    + +

    What's a Fax?


    +
  • Faxes are your main method of communicating with the NAS Trurl, better known as Central Command.
  • +
  • Faxes allow personnel on the station to maintain open lines of communication with the NAS Trurl, allowing for vital information to flow both ways.
  • +
  • Being written communications, proper grammar, syntax and typography is required, in addition to a signature and, if applicable, a stamp. Failure to sign faxes will lead to an automatic rejection.
  • +
  • We at Nanotrasen provide Fax Machines to every Head of Staff, in addition to the Magistrate, Nanotrasen Representative, and Internal Affairs Agents.
  • +
  • This means that we trust the recipients of these fax machines to only use them in the proper circumstances (see When to Fax?).
  • + +

    When to Fax?


    +
  • While it is up to the discretion of each individual person to decide when to fax Central Command, there are some simple guidelines on when to do this.
  • +
  • Firstly, any situation that can reasonably be solved on-site, should be handled on-site. Knowledge of Standard Operating Procedure is mandatory for everyone with access to a fax machine.
  • +
  • Resolving issues on-site not only leads to more expedient problem-solving, it also frees up company resources and provides valuable work experience for all parties involved.
  • +
  • This means that you should work with the Heads of Staff concerning personnel and workplace issues, and attempt to resolve situations with them. If, for whatever reason, the relevent Head of Staff is not available or receptive, consider speaking with the Captain and/or Nanotrasen Representative.
  • +
  • If, for whatever reason, these issues cannot be solved on-site, either due to incompetence or just plain refusal to cooperate, faxing Central Command becomes a viable option.
  • +
  • Secondly, station status reports should be sent occasionally, but never at the start of the shift. Remember, we assign personnel to the station. We do not need a repeat of what we just signed off on.
  • +
  • Thirdly, staff/departmental evaluations are always welcome, especially in cases of noticeable (in)competence. Just as a brilliant coworker can be rewarded, an incompetent one can be punished.
  • +
  • Fourthly, do not issue faxes asking for sentences. You have an entire Security department and an associated Detective, not to mention on-site Space Law manuals.
  • +
  • Lastly, please pay attention to context. If the station is facing a massive emergency, such as a Class 7-10 Blob Organism, most, if not all, non-relevant faxes will be duly ignored.
  • + +

    How to Fax?


    +
  • Sending a fax is simple. Simply insert your ID into the fax machine, then log in.
  • +
  • Once logged in, insert a piece of paper and select the destination from the provided list. Remember, you can rename your fax from within the fax machine's menu.
  • +
  • You can send faxes to any other fax machine on the station, which can be a very useful tool when you need to issue broad communications to all of the Heads of Staff.
  • +
  • To send a fax to Central Command, simply select the correct destination, and send the fax. Keep in mind, the communication arrays need to recharge after sending a fax to Central Command, so make sure you sent everything you need.
  • +
  • Lastly, paper bundles can also be faxed as a single item, so feel free to bundle up all relevant documentation and send it in at once.
  • + +
    + + + + "} + +/obj/item/book/manual/sop_science + name = "Science Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all scientific activities." + icon_state = "sop_science" + author = "Nanotrasen" + title = "Science Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_medical + name = "Medical Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all medical activities." + icon_state = "sop_medical" + author = "Nanotrasen" + title = "Medical Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_engineering + name = "Engineering Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all engineering activities." + icon_state = "sop_engineering" + author = "Nanotrasen" + title = "Engineering Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_service + name = "Service Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all service activities." + icon_state = "sop_service" + author = "Nanotrasen" + title = "Service Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_supply + name = "Supply Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all supply activities." + icon_state = "sop_cargo" + author = "Nanotrasen" + title = "Supply Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_security + name = "Security Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all security activities." + icon_state = "sop_security" + author = "Nanotrasen" + title = "Security Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_legal + name = "Legal Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all legal activities." + icon_state = "sop_legal" + author = "Nanotrasen" + title = "Legal Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_general + name = "Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all station activities." + icon_state = "sop" + author = "Nanotrasen" + title = "Standard Operating Procedures" + dat = {" + + + + + + + + + + "} + +/obj/item/book/manual/sop_command + name = "Command Standard Operating Procedures" + desc = "A set of guidelines aiming at the safe conduct of all Command activities." + icon_state = "sop_command" + author = "Nanotrasen" + title = "Command Standard Operating Procedures" + dat = {" + + + + + + + + + + "} diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index e97fc554d590..c3f2e50174b4 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -1,367 +1,367 @@ -/obj/item/melee/energy - var/active = 0 - var/force_on = 30 //force when active - var/throwforce_on = 20 - var/faction_bonus_force = 0 //Bonus force dealt against certain factions - var/list/nemesis_factions //Any mob with a faction that exists in this list will take bonus damage/effects - w_class = WEIGHT_CLASS_SMALL - var/w_class_on = WEIGHT_CLASS_BULKY - var/icon_state_on = "axe1" - var/list/attack_verb_on = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - hitsound = 'sound/weapons/blade1.ogg' // Probably more appropriate than the previous hitsound. -- Dave - usesound = 'sound/weapons/blade1.ogg' - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) - resistance_flags = FIRE_PROOF - toolspeed = 1 - light_power = 2 - var/brightness_on = 2 - var/colormap = list(red=LIGHT_COLOR_RED, blue=LIGHT_COLOR_LIGHTBLUE, green=LIGHT_COLOR_GREEN, purple=LIGHT_COLOR_PURPLE, rainbow=LIGHT_COLOR_WHITE) - -/obj/item/melee/energy/attack(mob/living/target, mob/living/carbon/human/user) - var/nemesis_faction = FALSE - if(LAZYLEN(nemesis_factions)) - for(var/F in target.faction) - if(F in nemesis_factions) - nemesis_faction = TRUE - force += faction_bonus_force - nemesis_effects(user, target) - break - . = ..() - if(nemesis_faction) - force -= faction_bonus_force - -/obj/item/melee/energy/suicide_act(mob/user) - user.visible_message(pick("[user] is slitting [user.p_their()] stomach open with the [name]! It looks like [user.p_theyre()] trying to commit seppuku.", \ - "[user] is falling on the [name]! It looks like [user.p_theyre()] trying to commit suicide.")) - return BRUTELOSS|FIRELOSS - -/obj/item/melee/energy/attack_self(mob/living/carbon/user) - if(user.disabilities & CLUMSY && prob(50)) - to_chat(user, "You accidentally cut yourself with [src], like a doofus!") - user.take_organ_damage(5,5) - active = !active - if(active) - force = force_on - throwforce = throwforce_on - hitsound = 'sound/weapons/blade1.ogg' - throw_speed = 4 - if(attack_verb_on.len) - attack_verb = attack_verb_on - if(!item_color) - icon_state = icon_state_on - set_light(brightness_on) - else - icon_state = "sword[item_color]" - set_light(brightness_on, l_color=colormap[item_color]) - w_class = w_class_on - playsound(user, 'sound/weapons/saberon.ogg', 35, 1) //changed it from 50% volume to 35% because deafness - to_chat(user, "[src] is now active.") - else - force = initial(force) - throwforce = initial(throwforce) - hitsound = initial(hitsound) - throw_speed = initial(throw_speed) - if(attack_verb_on.len) - attack_verb = list() - icon_state = initial(icon_state) - w_class = initial(w_class) - playsound(user, 'sound/weapons/saberoff.ogg', 35, 1) //changed it from 50% volume to 35% because deafness - set_light(0) - to_chat(user, "[src] can now be concealed.") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - add_fingerprint(user) - return - -/obj/item/melee/energy/axe - name = "energy axe" - desc = "An energised battle axe." - icon_state = "axe0" - force = 40 - force_on = 150 - throwforce = 25 - throwforce_on = 30 - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_NORMAL - w_class_on = WEIGHT_CLASS_HUGE - hitsound = 'sound/weapons/bladeslice.ogg' - flags = CONDUCT - armour_penetration = 100 - origin_tech = "combat=4;magnets=3" - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - attack_verb_on = list() - sharp = 1 - light_color = LIGHT_COLOR_WHITE - -/obj/item/melee/energy/axe/suicide_act(mob/user) - user.visible_message("[user] swings the [name] towards [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide.") - return BRUTELOSS|FIRELOSS - -/obj/item/melee/energy/sword - name = "energy sword" - desc = "May the force be within you." - icon_state = "sword0" - force = 3 - throwforce = 5 - throw_speed = 3 - throw_range = 5 - hitsound = "swing_hit" - embed_chance = 75 - embedded_impact_pain_multiplier = 10 - armour_penetration = 35 - origin_tech = "combat=3;magnets=4;syndicate=4" - block_chance = 50 - sharp = 1 - var/hacked = 0 - -/obj/item/melee/energy/sword/New() - ..() - if(item_color == null) - item_color = pick("red", "blue", "green", "purple") - -/obj/item/melee/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(active) - return ..() - return 0 - -/obj/item/melee/energy/sword/cyborg - var/hitcost = 50 - -/obj/item/melee/energy/sword/cyborg/attack(mob/M, var/mob/living/silicon/robot/R) - if(R.cell) - var/obj/item/stock_parts/cell/C = R.cell - if(active && !(C.use(hitcost))) - attack_self(R) - to_chat(R, "It's out of charge!") - return - ..() - return - -/obj/item/melee/energy/sword/cyborg/saw //Used by medical Syndicate cyborgs - name = "energy saw" - desc = "For heavy duty cutting. It has a carbon-fiber blade in addition to a toggleable hard-light edge to dramatically increase sharpness." - force_on = 30 - force = 18 //About as much as a spear - sharp = 1 - hitsound = 'sound/weapons/circsawhit.ogg' - icon = 'icons/obj/surgery.dmi' - icon_state = "esaw_0" - icon_state_on = "esaw_1" - hitcost = 75 //Costs more than a standard cyborg esword - item_color = null - w_class = WEIGHT_CLASS_NORMAL - light_color = LIGHT_COLOR_WHITE - -/obj/item/melee/energy/sword/cyborg/saw/New() - ..() - item_color = null - -/obj/item/melee/energy/sword/cyborg/saw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return 0 - -/obj/item/melee/energy/sword/saber - -/obj/item/melee/energy/sword/saber/blue - item_color = "blue" - -/obj/item/melee/energy/sword/saber/purple - item_color = "purple" - -/obj/item/melee/energy/sword/saber/green - item_color = "green" - -/obj/item/melee/energy/sword/saber/red - item_color = "red" - -/obj/item/melee/energy/sword/saber/attackby(obj/item/W, mob/living/user, params) - ..() - if(istype(W, /obj/item/melee/energy/sword/saber)) - if(W == src) - to_chat(user, "You try to attach the end of the energy sword to... itself. You're not very smart, are you?") - if(ishuman(user)) - user.adjustBrainLoss(10) - else - to_chat(user, "You attach the ends of the two energy swords, making a single double-bladed weapon! You're cool.") - var/obj/item/twohanded/dualsaber/newSaber = new /obj/item/twohanded/dualsaber(user.loc) - if(src.hacked) // That's right, we'll only check the "original" esword. - newSaber.hacked = 1 - newSaber.item_color = "rainbow" - user.unEquip(W) - user.unEquip(src) - qdel(W) - qdel(src) - user.put_in_hands(newSaber) - else if(istype(W, /obj/item/multitool)) - if(hacked == 0) - hacked = 1 - item_color = "rainbow" - to_chat(user, "RNBW_ENGAGE") - - if(active) - icon_state = "swordrainbow" - // Updating overlays, copied from welder code. - // I tried calling attack_self twice, which looked cool, except it somehow didn't update the overlays!! - if(user.r_hand == src) - user.update_inv_r_hand() - else if(user.l_hand == src) - user.update_inv_l_hand() - - else - to_chat(user, "It's already fabulous!") - -/obj/item/melee/energy/sword/pirate - name = "energy cutlass" - desc = "Arrrr matey." - icon_state = "cutlass0" - icon_state_on = "cutlass1" - light_color = LIGHT_COLOR_RED - -/obj/item/melee/energy/blade - name = "energy blade" - desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." - icon_state = "blade" - force = 30 //Normal attacks deal esword damage - hitsound = 'sound/weapons/blade1.ogg' - active = 1 - throwforce = 1//Throwing or dropping the item deletes it. - throw_speed = 3 - throw_range = 1 - w_class = WEIGHT_CLASS_BULKY //So you can't hide it in your pocket or some such. - sharp = 1 - -/obj/item/melee/energy/blade/attack_self(mob/user) - return - -/obj/item/melee/energy/blade/hardlight - name = "hardlight blade" - desc = "An extremely sharp blade made out of hard light. Packs quite a punch." - icon_state = "lightblade" - item_state = "lightblade" - -/obj/item/melee/energy/proc/nemesis_effects(mob/living/user, mob/living/target) - return - -/obj/item/melee/energy/cleaving_saw - name = "cleaving saw" - desc = "This saw, effective at drawing the blood of beasts, transforms into a long cleaver that makes use of centrifugal force." - force = 12 - force_on = 20 //force when active - throwforce = 20 - throwforce_on = 20 - icon = 'icons/obj/lavaland/artefacts.dmi' - lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' - righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' - inhand_x_dimension = 64 - inhand_y_dimension = 64 - icon_state = "cleaving_saw" - icon_state_on = "cleaving_saw_open" - slot_flags = SLOT_BELT - var/attack_verb_off = list("attacked", "sawed", "sliced", "torn", "ripped", "diced", "cut") - attack_verb_on = list("cleaved", "swiped", "slashed", "chopped") - hitsound = 'sound/weapons/bladeslice.ogg' - w_class = WEIGHT_CLASS_BULKY - sharp = TRUE - faction_bonus_force = 30 - nemesis_factions = list("mining", "boss") - var/transform_cooldown - var/swiping = FALSE - -/obj/item/melee/energy/cleaving_saw/nemesis_effects(mob/living/user, mob/living/target) - var/datum/status_effect/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) - if(!B) - if(!active) //This isn't in the above if-check so that the else doesn't care about active - target.apply_status_effect(STATUS_EFFECT_SAWBLEED) - else - B.add_bleed(B.bleed_buildup) - -/obj/item/melee/energy/cleaving_saw/attack_self(mob/living/carbon/user) - transform_weapon(user) - -/obj/item/melee/energy/cleaving_saw/proc/transform_weapon(mob/living/user, supress_message_text) - if(transform_cooldown > world.time) - return FALSE - - transform_cooldown = world.time + (CLICK_CD_MELEE * 0.5) - user.changeNext_move(CLICK_CD_MELEE * 0.25) - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.disabilities & CLUMSY && prob(50)) - to_chat(H, "You accidentally cut yourself with [src], like a doofus!") - H.take_organ_damage(10,10) - active = !active - if(active) - force = force_on - throwforce = throwforce_on - hitsound = 'sound/weapons/bladeslice.ogg' - throw_speed = 4 - if(attack_verb_on.len) - attack_verb = attack_verb_on - if(!item_color) - icon_state = icon_state_on - set_light(brightness_on) - else - icon_state = "sword[item_color]" - set_light(brightness_on, l_color=colormap[item_color]) - w_class = w_class_on - playsound(user, 'sound/magic/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000)) - to_chat(user, "You open [src]. It will now cleave enemies in a wide arc and deal additional damage to fauna.") - else - force = initial(force) - throwforce = initial(throwforce) - hitsound = initial(hitsound) - throw_speed = initial(throw_speed) - if(attack_verb_on.len) - attack_verb = list() - icon_state = initial(icon_state) - w_class = initial(w_class) - playsound(user, 'sound/magic/fellowship_armory.ogg', 35, 1) //changed it from 50% volume to 35% because deafness - set_light(0) - to_chat(user, "You close [src]. It will now attack rapidly and cause fauna to bleed.") - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - - add_fingerprint(user) - -/obj/item/melee/energy/cleaving_saw/examine(mob/user) - . = ..() - . += "It is [active ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].
    \ - Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.
    \ - Transforming it immediately after an attack causes the next attack to come out faster.
    " - -/obj/item/melee/energy/cleaving_saw/suicide_act(mob/user) - user.visible_message("[user] is [active ? "closing [src] on [user.p_their()] neck" : "opening [src] into [user.p_their()] chest"]! It looks like [user.p_theyre()] trying to commit suicide!") - transform_cooldown = 0 - transform_weapon(user, TRUE) - return BRUTELOSS - -/obj/item/melee/energy/cleaving_saw/melee_attack_chain(mob/user, atom/target, params) - ..() - if(!active) - user.changeNext_move(CLICK_CD_MELEE * 0.5) //when closed, it attacks very rapidly - -/obj/item/melee/energy/cleaving_saw/attack(mob/living/target, mob/living/carbon/human/user) - if(!active || swiping || !target.density || get_turf(target) == get_turf(user)) - if(!active) - faction_bonus_force = 0 - ..() - if(!active) - faction_bonus_force = initial(faction_bonus_force) - else - var/turf/user_turf = get_turf(user) - var/dir_to_target = get_dir(user_turf, get_turf(target)) - swiping = TRUE - var/static/list/cleaving_saw_cleave_angles = list(0, -45, 45) //so that the animation animates towards the target clicked and not towards a side target - for(var/i in cleaving_saw_cleave_angles) - var/turf/T = get_step(user_turf, turn(dir_to_target, i)) - for(var/mob/living/L in T) - if(user.Adjacent(L) && L.density) - melee_attack_chain(user, L) - swiping = FALSE \ No newline at end of file +/obj/item/melee/energy + var/active = 0 + var/force_on = 30 //force when active + var/throwforce_on = 20 + var/faction_bonus_force = 0 //Bonus force dealt against certain factions + var/list/nemesis_factions //Any mob with a faction that exists in this list will take bonus damage/effects + w_class = WEIGHT_CLASS_SMALL + var/w_class_on = WEIGHT_CLASS_BULKY + var/icon_state_on = "axe1" + var/list/attack_verb_on = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + hitsound = 'sound/weapons/blade1.ogg' // Probably more appropriate than the previous hitsound. -- Dave + usesound = 'sound/weapons/blade1.ogg' + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) + resistance_flags = FIRE_PROOF + toolspeed = 1 + light_power = 2 + var/brightness_on = 2 + var/colormap = list(red=LIGHT_COLOR_RED, blue=LIGHT_COLOR_LIGHTBLUE, green=LIGHT_COLOR_GREEN, purple=LIGHT_COLOR_PURPLE, rainbow=LIGHT_COLOR_WHITE) + +/obj/item/melee/energy/attack(mob/living/target, mob/living/carbon/human/user) + var/nemesis_faction = FALSE + if(LAZYLEN(nemesis_factions)) + for(var/F in target.faction) + if(F in nemesis_factions) + nemesis_faction = TRUE + force += faction_bonus_force + nemesis_effects(user, target) + break + . = ..() + if(nemesis_faction) + force -= faction_bonus_force + +/obj/item/melee/energy/suicide_act(mob/user) + user.visible_message(pick("[user] is slitting [user.p_their()] stomach open with the [name]! It looks like [user.p_theyre()] trying to commit seppuku.", \ + "[user] is falling on the [name]! It looks like [user.p_theyre()] trying to commit suicide.")) + return BRUTELOSS|FIRELOSS + +/obj/item/melee/energy/attack_self(mob/living/carbon/user) + if(user.disabilities & CLUMSY && prob(50)) + to_chat(user, "You accidentally cut yourself with [src], like a doofus!") + user.take_organ_damage(5,5) + active = !active + if(active) + force = force_on + throwforce = throwforce_on + hitsound = 'sound/weapons/blade1.ogg' + throw_speed = 4 + if(attack_verb_on.len) + attack_verb = attack_verb_on + if(!item_color) + icon_state = icon_state_on + set_light(brightness_on) + else + icon_state = "sword[item_color]" + set_light(brightness_on, l_color=colormap[item_color]) + w_class = w_class_on + playsound(user, 'sound/weapons/saberon.ogg', 35, 1) //changed it from 50% volume to 35% because deafness + to_chat(user, "[src] is now active.") + else + force = initial(force) + throwforce = initial(throwforce) + hitsound = initial(hitsound) + throw_speed = initial(throw_speed) + if(attack_verb_on.len) + attack_verb = list() + icon_state = initial(icon_state) + w_class = initial(w_class) + playsound(user, 'sound/weapons/saberoff.ogg', 35, 1) //changed it from 50% volume to 35% because deafness + set_light(0) + to_chat(user, "[src] can now be concealed.") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + add_fingerprint(user) + return + +/obj/item/melee/energy/axe + name = "energy axe" + desc = "An energised battle axe." + icon_state = "axe0" + force = 40 + force_on = 150 + throwforce = 25 + throwforce_on = 30 + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_NORMAL + w_class_on = WEIGHT_CLASS_HUGE + hitsound = 'sound/weapons/bladeslice.ogg' + flags = CONDUCT + armour_penetration = 100 + origin_tech = "combat=4;magnets=3" + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + attack_verb_on = list() + sharp = 1 + light_color = LIGHT_COLOR_WHITE + +/obj/item/melee/energy/axe/suicide_act(mob/user) + user.visible_message("[user] swings the [name] towards [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide.") + return BRUTELOSS|FIRELOSS + +/obj/item/melee/energy/sword + name = "energy sword" + desc = "May the force be within you." + icon_state = "sword0" + force = 3 + throwforce = 5 + throw_speed = 3 + throw_range = 5 + hitsound = "swing_hit" + embed_chance = 75 + embedded_impact_pain_multiplier = 10 + armour_penetration = 35 + origin_tech = "combat=3;magnets=4;syndicate=4" + block_chance = 50 + sharp = 1 + var/hacked = 0 + +/obj/item/melee/energy/sword/New() + ..() + if(item_color == null) + item_color = pick("red", "blue", "green", "purple") + +/obj/item/melee/energy/sword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(active) + return ..() + return 0 + +/obj/item/melee/energy/sword/cyborg + var/hitcost = 50 + +/obj/item/melee/energy/sword/cyborg/attack(mob/M, var/mob/living/silicon/robot/R) + if(R.cell) + var/obj/item/stock_parts/cell/C = R.cell + if(active && !(C.use(hitcost))) + attack_self(R) + to_chat(R, "It's out of charge!") + return + ..() + return + +/obj/item/melee/energy/sword/cyborg/saw //Used by medical Syndicate cyborgs + name = "energy saw" + desc = "For heavy duty cutting. It has a carbon-fiber blade in addition to a toggleable hard-light edge to dramatically increase sharpness." + force_on = 30 + force = 18 //About as much as a spear + sharp = 1 + hitsound = 'sound/weapons/circsawhit.ogg' + icon = 'icons/obj/surgery.dmi' + icon_state = "esaw_0" + icon_state_on = "esaw_1" + hitcost = 75 //Costs more than a standard cyborg esword + item_color = null + w_class = WEIGHT_CLASS_NORMAL + light_color = LIGHT_COLOR_WHITE + +/obj/item/melee/energy/sword/cyborg/saw/New() + ..() + item_color = null + +/obj/item/melee/energy/sword/cyborg/saw/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + return 0 + +/obj/item/melee/energy/sword/saber + +/obj/item/melee/energy/sword/saber/blue + item_color = "blue" + +/obj/item/melee/energy/sword/saber/purple + item_color = "purple" + +/obj/item/melee/energy/sword/saber/green + item_color = "green" + +/obj/item/melee/energy/sword/saber/red + item_color = "red" + +/obj/item/melee/energy/sword/saber/attackby(obj/item/W, mob/living/user, params) + ..() + if(istype(W, /obj/item/melee/energy/sword/saber)) + if(W == src) + to_chat(user, "You try to attach the end of the energy sword to... itself. You're not very smart, are you?") + if(ishuman(user)) + user.adjustBrainLoss(10) + else + to_chat(user, "You attach the ends of the two energy swords, making a single double-bladed weapon! You're cool.") + var/obj/item/twohanded/dualsaber/newSaber = new /obj/item/twohanded/dualsaber(user.loc) + if(src.hacked) // That's right, we'll only check the "original" esword. + newSaber.hacked = 1 + newSaber.item_color = "rainbow" + user.unEquip(W) + user.unEquip(src) + qdel(W) + qdel(src) + user.put_in_hands(newSaber) + else if(istype(W, /obj/item/multitool)) + if(hacked == 0) + hacked = 1 + item_color = "rainbow" + to_chat(user, "RNBW_ENGAGE") + + if(active) + icon_state = "swordrainbow" + // Updating overlays, copied from welder code. + // I tried calling attack_self twice, which looked cool, except it somehow didn't update the overlays!! + if(user.r_hand == src) + user.update_inv_r_hand() + else if(user.l_hand == src) + user.update_inv_l_hand() + + else + to_chat(user, "It's already fabulous!") + +/obj/item/melee/energy/sword/pirate + name = "energy cutlass" + desc = "Arrrr matey." + icon_state = "cutlass0" + icon_state_on = "cutlass1" + light_color = LIGHT_COLOR_RED + +/obj/item/melee/energy/blade + name = "energy blade" + desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal." + icon_state = "blade" + force = 30 //Normal attacks deal esword damage + hitsound = 'sound/weapons/blade1.ogg' + active = 1 + throwforce = 1//Throwing or dropping the item deletes it. + throw_speed = 3 + throw_range = 1 + w_class = WEIGHT_CLASS_BULKY //So you can't hide it in your pocket or some such. + sharp = 1 + +/obj/item/melee/energy/blade/attack_self(mob/user) + return + +/obj/item/melee/energy/blade/hardlight + name = "hardlight blade" + desc = "An extremely sharp blade made out of hard light. Packs quite a punch." + icon_state = "lightblade" + item_state = "lightblade" + +/obj/item/melee/energy/proc/nemesis_effects(mob/living/user, mob/living/target) + return + +/obj/item/melee/energy/cleaving_saw + name = "cleaving saw" + desc = "This saw, effective at drawing the blood of beasts, transforms into a long cleaver that makes use of centrifugal force." + force = 12 + force_on = 20 //force when active + throwforce = 20 + throwforce_on = 20 + icon = 'icons/obj/lavaland/artefacts.dmi' + lefthand_file = 'icons/mob/inhands/64x64_lefthand.dmi' + righthand_file = 'icons/mob/inhands/64x64_righthand.dmi' + inhand_x_dimension = 64 + inhand_y_dimension = 64 + icon_state = "cleaving_saw" + icon_state_on = "cleaving_saw_open" + slot_flags = SLOT_BELT + var/attack_verb_off = list("attacked", "sawed", "sliced", "torn", "ripped", "diced", "cut") + attack_verb_on = list("cleaved", "swiped", "slashed", "chopped") + hitsound = 'sound/weapons/bladeslice.ogg' + w_class = WEIGHT_CLASS_BULKY + sharp = TRUE + faction_bonus_force = 30 + nemesis_factions = list("mining", "boss") + var/transform_cooldown + var/swiping = FALSE + +/obj/item/melee/energy/cleaving_saw/nemesis_effects(mob/living/user, mob/living/target) + var/datum/status_effect/saw_bleed/B = target.has_status_effect(STATUS_EFFECT_SAWBLEED) + if(!B) + if(!active) //This isn't in the above if-check so that the else doesn't care about active + target.apply_status_effect(STATUS_EFFECT_SAWBLEED) + else + B.add_bleed(B.bleed_buildup) + +/obj/item/melee/energy/cleaving_saw/attack_self(mob/living/carbon/user) + transform_weapon(user) + +/obj/item/melee/energy/cleaving_saw/proc/transform_weapon(mob/living/user, supress_message_text) + if(transform_cooldown > world.time) + return FALSE + + transform_cooldown = world.time + (CLICK_CD_MELEE * 0.5) + user.changeNext_move(CLICK_CD_MELEE * 0.25) + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.disabilities & CLUMSY && prob(50)) + to_chat(H, "You accidentally cut yourself with [src], like a doofus!") + H.take_organ_damage(10,10) + active = !active + if(active) + force = force_on + throwforce = throwforce_on + hitsound = 'sound/weapons/bladeslice.ogg' + throw_speed = 4 + if(attack_verb_on.len) + attack_verb = attack_verb_on + if(!item_color) + icon_state = icon_state_on + set_light(brightness_on) + else + icon_state = "sword[item_color]" + set_light(brightness_on, l_color=colormap[item_color]) + w_class = w_class_on + playsound(user, 'sound/magic/fellowship_armory.ogg', 35, TRUE, frequency = 90000 - (active * 30000)) + to_chat(user, "You open [src]. It will now cleave enemies in a wide arc and deal additional damage to fauna.") + else + force = initial(force) + throwforce = initial(throwforce) + hitsound = initial(hitsound) + throw_speed = initial(throw_speed) + if(attack_verb_on.len) + attack_verb = list() + icon_state = initial(icon_state) + w_class = initial(w_class) + playsound(user, 'sound/magic/fellowship_armory.ogg', 35, 1) //changed it from 50% volume to 35% because deafness + set_light(0) + to_chat(user, "You close [src]. It will now attack rapidly and cause fauna to bleed.") + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + + add_fingerprint(user) + +/obj/item/melee/energy/cleaving_saw/examine(mob/user) + . = ..() + . += "It is [active ? "open, will cleave enemies in a wide arc and deal additional damage to fauna":"closed, and can be used for rapid consecutive attacks that cause fauna to bleed"].
    \ + Both modes will build up existing bleed effects, doing a burst of high damage if the bleed is built up high enough.
    \ + Transforming it immediately after an attack causes the next attack to come out faster.
    " + +/obj/item/melee/energy/cleaving_saw/suicide_act(mob/user) + user.visible_message("[user] is [active ? "closing [src] on [user.p_their()] neck" : "opening [src] into [user.p_their()] chest"]! It looks like [user.p_theyre()] trying to commit suicide!") + transform_cooldown = 0 + transform_weapon(user, TRUE) + return BRUTELOSS + +/obj/item/melee/energy/cleaving_saw/melee_attack_chain(mob/user, atom/target, params) + ..() + if(!active) + user.changeNext_move(CLICK_CD_MELEE * 0.5) //when closed, it attacks very rapidly + +/obj/item/melee/energy/cleaving_saw/attack(mob/living/target, mob/living/carbon/human/user) + if(!active || swiping || !target.density || get_turf(target) == get_turf(user)) + if(!active) + faction_bonus_force = 0 + ..() + if(!active) + faction_bonus_force = initial(faction_bonus_force) + else + var/turf/user_turf = get_turf(user) + var/dir_to_target = get_dir(user_turf, get_turf(target)) + swiping = TRUE + var/static/list/cleaving_saw_cleave_angles = list(0, -45, 45) //so that the animation animates towards the target clicked and not towards a side target + for(var/i in cleaving_saw_cleave_angles) + var/turf/T = get_step(user_turf, turn(dir_to_target, i)) + for(var/mob/living/L in T) + if(user.Adjacent(L) && L.density) + melee_attack_chain(user, L) + swiping = FALSE diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm index b9f1fa679a81..4f9e051ef446 100644 --- a/code/game/objects/items/weapons/melee/misc.dm +++ b/code/game/objects/items/weapons/melee/misc.dm @@ -1,104 +1,104 @@ -/obj/item/melee - needs_permit = 1 - -/obj/item/melee/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user) - if(target.check_block()) - target.visible_message("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!", - "You block the attack!") - user.Stun(2) - return TRUE - -/obj/item/melee/chainofcommand - name = "chain of command" - desc = "A tool used by great men to placate the frothing masses." - icon_state = "chain" - item_state = "chain" - flags = CONDUCT - slot_flags = SLOT_BELT - force = 10 - throwforce = 7 - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=5" - attack_verb = list("flogged", "whipped", "lashed", "disciplined") - hitsound = 'sound/weapons/slash.ogg' //pls replace - - -/obj/item/melee/chainofcommand/suicide_act(mob/user) - to_chat(viewers(user), "[user] is strangling [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.") - return OXYLOSS - -/obj/item/melee/rapier - name = "captain's rapier" - desc = "An elegant weapon, for a more civilized age." - icon_state = "rapier" - item_state = "rapier" - flags = CONDUCT - force = 15 - throwforce = 10 - w_class = WEIGHT_CLASS_BULKY - block_chance = 50 - armour_penetration = 75 - sharp = 1 - origin_tech = "combat=5" - attack_verb = list("lunged at", "stabbed") - hitsound = 'sound/weapons/rapierhit.ogg' - materials = list(MAT_METAL = 1000) - -/obj/item/melee/rapier/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) - final_block_chance = 0 //Don't bring a sword to a gunfight - return ..() - -/obj/item/melee/icepick - name = "ice pick" - desc = "Used for chopping ice. Also excellent for mafia esque murders." - icon_state = "icepick" - item_state = "icepick" - force = 15 - throwforce = 10 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("stabbed", "jabbed", "iced,") - -/obj/item/melee/candy_sword - name = "candy cane sword" - desc = "A large candy cane with a sharpened point. Definitely too dangerous for schoolchildren." - icon_state = "candy_sword" - item_state = "candy_sword" - force = 10 - throwforce = 7 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("slashed", "stabbed", "sliced", "caned") - -/obj/item/melee/flyswatter - name = "flyswatter" - desc = "Useful for killing insects of all sizes." - icon_state = "flyswatter" - item_state = "flyswatter" - force = 1 - throwforce = 1 - attack_verb = list("swatted", "smacked") - hitsound = 'sound/effects/snap.ogg' - w_class = WEIGHT_CLASS_SMALL - //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. - var/list/strong_against - -/obj/item/melee/flyswatter/Initialize(mapload) - . = ..() - strong_against = typecacheof(list( - /mob/living/simple_animal/hostile/poison/bees/, - /mob/living/simple_animal/butterfly, - /mob/living/simple_animal/cockroach, - /obj/item/queen_bee - )) - -/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag) - . = ..() - if(proximity_flag) - if(is_type_in_typecache(target, strong_against)) - new /obj/effect/decal/cleanable/insectguts(target.drop_location()) - to_chat(user, "You easily splat the [target].") - if(istype(target, /mob/living/)) - var/mob/living/bug = target - bug.death(1) - else - qdel(target) \ No newline at end of file +/obj/item/melee + needs_permit = 1 + +/obj/item/melee/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user) + if(target.check_block()) + target.visible_message("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!", + "You block the attack!") + user.Stun(2) + return TRUE + +/obj/item/melee/chainofcommand + name = "chain of command" + desc = "A tool used by great men to placate the frothing masses." + icon_state = "chain" + item_state = "chain" + flags = CONDUCT + slot_flags = SLOT_BELT + force = 10 + throwforce = 7 + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=5" + attack_verb = list("flogged", "whipped", "lashed", "disciplined") + hitsound = 'sound/weapons/slash.ogg' //pls replace + + +/obj/item/melee/chainofcommand/suicide_act(mob/user) + to_chat(viewers(user), "[user] is strangling [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to commit suicide.") + return OXYLOSS + +/obj/item/melee/rapier + name = "captain's rapier" + desc = "An elegant weapon, for a more civilized age." + icon_state = "rapier" + item_state = "rapier" + flags = CONDUCT + force = 15 + throwforce = 10 + w_class = WEIGHT_CLASS_BULKY + block_chance = 50 + armour_penetration = 75 + sharp = 1 + origin_tech = "combat=5" + attack_verb = list("lunged at", "stabbed") + hitsound = 'sound/weapons/rapierhit.ogg' + materials = list(MAT_METAL = 1000) + +/obj/item/melee/rapier/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(attack_type == PROJECTILE_ATTACK) + final_block_chance = 0 //Don't bring a sword to a gunfight + return ..() + +/obj/item/melee/icepick + name = "ice pick" + desc = "Used for chopping ice. Also excellent for mafia esque murders." + icon_state = "icepick" + item_state = "icepick" + force = 15 + throwforce = 10 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("stabbed", "jabbed", "iced,") + +/obj/item/melee/candy_sword + name = "candy cane sword" + desc = "A large candy cane with a sharpened point. Definitely too dangerous for schoolchildren." + icon_state = "candy_sword" + item_state = "candy_sword" + force = 10 + throwforce = 7 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("slashed", "stabbed", "sliced", "caned") + +/obj/item/melee/flyswatter + name = "flyswatter" + desc = "Useful for killing insects of all sizes." + icon_state = "flyswatter" + item_state = "flyswatter" + force = 1 + throwforce = 1 + attack_verb = list("swatted", "smacked") + hitsound = 'sound/effects/snap.ogg' + w_class = WEIGHT_CLASS_SMALL + //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. + var/list/strong_against + +/obj/item/melee/flyswatter/Initialize(mapload) + . = ..() + strong_against = typecacheof(list( + /mob/living/simple_animal/hostile/poison/bees/, + /mob/living/simple_animal/butterfly, + /mob/living/simple_animal/cockroach, + /obj/item/queen_bee + )) + +/obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag) + . = ..() + if(proximity_flag) + if(is_type_in_typecache(target, strong_against)) + new /obj/effect/decal/cleanable/insectguts(target.drop_location()) + to_chat(user, "You easily splat the [target].") + if(istype(target, /mob/living/)) + var/mob/living/bug = target + bug.death(1) + else + qdel(target) diff --git a/code/game/objects/items/weapons/mop.dm b/code/game/objects/items/weapons/mop.dm index fdb90b2c3a05..419df51bbd42 100644 --- a/code/game/objects/items/weapons/mop.dm +++ b/code/game/objects/items/weapons/mop.dm @@ -1,120 +1,120 @@ -/obj/item/mop - desc = "The world of janitalia wouldn't be complete without a mop." - name = "mop" - icon = 'icons/obj/janitor.dmi' - icon_state = "mop" - force = 3 - throwforce = 5 - throw_speed = 3 - throw_range = 7 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") - resistance_flags = FLAMMABLE - var/mopping = 0 - var/mopcount = 0 - var/mopcap = 5 - var/mopspeed = 30 - -/obj/item/mop/New() - ..() - create_reagents(mopcap) - GLOB.janitorial_equipment += src - -/obj/item/mop/Destroy() - GLOB.janitorial_equipment -= src - return ..() - -/obj/item/mop/proc/clean(turf/simulated/A) - if(reagents.has_reagent("water", 1) || reagents.has_reagent("cleaner", 1) || reagents.has_reagent("holywater", 1)) - A.clean_blood() - for(var/obj/effect/O in A) - if(is_cleanable(O)) - qdel(O) - reagents.reaction(A, REAGENT_TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. - reagents.remove_any(1) //reaction() doesn't use up the reagents - -/obj/item/mop/afterattack(atom/A, mob/user, proximity) - if(!proximity) return - - if(reagents.total_volume < 1) - to_chat(user, "Your mop is dry!") - return - - var/turf/simulated/T = get_turf(A) - - if(istype(A, /obj/item/reagent_containers/glass/bucket) || istype(A, /obj/structure/janitorialcart)) - return - - if(istype(T)) - user.visible_message("[user] begins to clean [T] with [src].", "You begin to clean [T] with [src]...") - - if(do_after(user, src.mopspeed, target = T)) - to_chat(user, "You finish mopping.") - clean(T) - - -/obj/effect/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/mop) || istype(I, /obj/item/soap)) - return - else - return ..() - - -/obj/item/mop/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) - J.put_in_cart(src, user) - J.mymop=src - J.update_icon() - -/obj/item/mop/wash(mob/user, atom/source) - reagents.add_reagent("water", 5) - to_chat(user, "You wet [src] in [source].") - playsound(loc, 'sound/effects/slosh.ogg', 25, 1) - return 1 - -/obj/item/mop/advanced - desc = "The most advanced tool in a custodian's arsenal. Just think of all the viscera you will clean up with this!" - name = "advanced mop" - mopcap = 10 - icon_state = "advmop" - item_state = "mop" - origin_tech = "materials=3;engineering=3" - force = 6 - throwforce = 8 - throw_range = 4 - mopspeed = 20 - var/refill_enabled = TRUE //Self-refill toggle for when a janitor decides to mop with something other than water. - var/refill_rate = 1 //Rate per process() tick mop refills itself - var/refill_reagent = "water" //Determins what reagent to use for refilling, just in case someone wanted to make a HOLY MOP OF PURGING - -/obj/item/mop/advanced/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/mop/advanced/attack_self(mob/user) - refill_enabled = !refill_enabled - if(refill_enabled) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - to_chat(user, "You set the condenser switch to the '[refill_enabled ? "ON" : "OFF"]' position.") - playsound(user, 'sound/machines/click.ogg', 30, 1) - -/obj/item/mop/advanced/process() - - if(reagents.total_volume < mopcap) - reagents.add_reagent(refill_reagent, refill_rate) - -/obj/item/mop/advanced/examine(mob/user) - . = ..() - . += "The condenser switch is set to [refill_enabled ? "ON" : "OFF"]." - -/obj/item/mop/advanced/Destroy() - if(refill_enabled) - STOP_PROCESSING(SSobj, src) - return ..() - - -/obj/item/mop/advanced/cyborg - -/obj/item/mop/advanced/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) - return \ No newline at end of file +/obj/item/mop + desc = "The world of janitalia wouldn't be complete without a mop." + name = "mop" + icon = 'icons/obj/janitor.dmi' + icon_state = "mop" + force = 3 + throwforce = 5 + throw_speed = 3 + throw_range = 7 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("mopped", "bashed", "bludgeoned", "whacked") + resistance_flags = FLAMMABLE + var/mopping = 0 + var/mopcount = 0 + var/mopcap = 5 + var/mopspeed = 30 + +/obj/item/mop/New() + ..() + create_reagents(mopcap) + GLOB.janitorial_equipment += src + +/obj/item/mop/Destroy() + GLOB.janitorial_equipment -= src + return ..() + +/obj/item/mop/proc/clean(turf/simulated/A) + if(reagents.has_reagent("water", 1) || reagents.has_reagent("cleaner", 1) || reagents.has_reagent("holywater", 1)) + A.clean_blood() + for(var/obj/effect/O in A) + if(is_cleanable(O)) + qdel(O) + reagents.reaction(A, REAGENT_TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly. + reagents.remove_any(1) //reaction() doesn't use up the reagents + +/obj/item/mop/afterattack(atom/A, mob/user, proximity) + if(!proximity) return + + if(reagents.total_volume < 1) + to_chat(user, "Your mop is dry!") + return + + var/turf/simulated/T = get_turf(A) + + if(istype(A, /obj/item/reagent_containers/glass/bucket) || istype(A, /obj/structure/janitorialcart)) + return + + if(istype(T)) + user.visible_message("[user] begins to clean [T] with [src].", "You begin to clean [T] with [src]...") + + if(do_after(user, src.mopspeed, target = T)) + to_chat(user, "You finish mopping.") + clean(T) + + +/obj/effect/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/mop) || istype(I, /obj/item/soap)) + return + else + return ..() + + +/obj/item/mop/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) + J.put_in_cart(src, user) + J.mymop=src + J.update_icon() + +/obj/item/mop/wash(mob/user, atom/source) + reagents.add_reagent("water", 5) + to_chat(user, "You wet [src] in [source].") + playsound(loc, 'sound/effects/slosh.ogg', 25, 1) + return 1 + +/obj/item/mop/advanced + desc = "The most advanced tool in a custodian's arsenal. Just think of all the viscera you will clean up with this!" + name = "advanced mop" + mopcap = 10 + icon_state = "advmop" + item_state = "mop" + origin_tech = "materials=3;engineering=3" + force = 6 + throwforce = 8 + throw_range = 4 + mopspeed = 20 + var/refill_enabled = TRUE //Self-refill toggle for when a janitor decides to mop with something other than water. + var/refill_rate = 1 //Rate per process() tick mop refills itself + var/refill_reagent = "water" //Determins what reagent to use for refilling, just in case someone wanted to make a HOLY MOP OF PURGING + +/obj/item/mop/advanced/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/mop/advanced/attack_self(mob/user) + refill_enabled = !refill_enabled + if(refill_enabled) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + to_chat(user, "You set the condenser switch to the '[refill_enabled ? "ON" : "OFF"]' position.") + playsound(user, 'sound/machines/click.ogg', 30, 1) + +/obj/item/mop/advanced/process() + + if(reagents.total_volume < mopcap) + reagents.add_reagent(refill_reagent, refill_rate) + +/obj/item/mop/advanced/examine(mob/user) + . = ..() + . += "The condenser switch is set to [refill_enabled ? "ON" : "OFF"]." + +/obj/item/mop/advanced/Destroy() + if(refill_enabled) + STOP_PROCESSING(SSobj, src) + return ..() + + +/obj/item/mop/advanced/cyborg + +/obj/item/mop/advanced/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) + return diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm index 59a31e8494b1..1b3330783540 100644 --- a/code/game/objects/items/weapons/paint.dm +++ b/code/game/objects/items/weapons/paint.dm @@ -1,68 +1,68 @@ -//NEVER USE THIS IT SUX -PETETHEGOAT - -/obj/item/reagent_containers/glass/paint - desc = "It's a paint bucket." - name = "paint bucket" - icon = 'icons/obj/items.dmi' - icon_state = "paint_neutral" - item_state = "paintcan" - materials = list(MAT_METAL=200) - w_class = WEIGHT_CLASS_NORMAL - resistance_flags = FLAMMABLE - max_integrity = 100 - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list(5,10,20,30,50,70) - volume = 70 - container_type = OPENCONTAINER - -/obj/item/reagent_containers/glass/paint/afterattack(turf/simulated/target, mob/user, proximity) - if(!proximity) - return - if(!is_open_container()) - return - if(istype(target) && reagents.total_volume >= 5) - user.visible_message("[target] has been splashed with something by [user]!") - spawn(5) - reagents.reaction(target, REAGENT_TOUCH) - reagents.remove_any(5) - else - return ..() - -/obj/item/reagent_containers/glass/paint/red - name = "red paint bucket" - icon_state = "paint_red" - list_reagents = list("paint_red" = 70) - -/obj/item/reagent_containers/glass/paint/green - name = "green paint bucket" - icon_state = "paint_green" - list_reagents = list("paint_green" = 70) - -/obj/item/reagent_containers/glass/paint/blue - name = "blue paint bucket" - icon_state = "paint_blue" - list_reagents = list("paint_blue" = 70) - -/obj/item/reagent_containers/glass/paint/yellow - name = "yellow paint bucket" - icon_state = "paint_yellow" - list_reagents = list("paint_yellow" = 70) - -/obj/item/reagent_containers/glass/paint/violet - name = "violet paint bucket" - icon_state = "paint_violet" - list_reagents = list("paint_violet" = 70) - -/obj/item/reagent_containers/glass/paint/black - name = "black paint bucket" - icon_state = "paint_black" - list_reagents = list("paint_black" = 70) - -/obj/item/reagent_containers/glass/paint/white - name = "white paint bucket" - icon_state = "paint_white" - list_reagents = list("paint_white" = 70) - -/obj/item/reagent_containers/glass/paint/remover - name = "paint remover bucket" - list_reagents = list("paint_remover" = 70) +//NEVER USE THIS IT SUX -PETETHEGOAT + +/obj/item/reagent_containers/glass/paint + desc = "It's a paint bucket." + name = "paint bucket" + icon = 'icons/obj/items.dmi' + icon_state = "paint_neutral" + item_state = "paintcan" + materials = list(MAT_METAL=200) + w_class = WEIGHT_CLASS_NORMAL + resistance_flags = FLAMMABLE + max_integrity = 100 + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list(5,10,20,30,50,70) + volume = 70 + container_type = OPENCONTAINER + +/obj/item/reagent_containers/glass/paint/afterattack(turf/simulated/target, mob/user, proximity) + if(!proximity) + return + if(!is_open_container()) + return + if(istype(target) && reagents.total_volume >= 5) + user.visible_message("[target] has been splashed with something by [user]!") + spawn(5) + reagents.reaction(target, REAGENT_TOUCH) + reagents.remove_any(5) + else + return ..() + +/obj/item/reagent_containers/glass/paint/red + name = "red paint bucket" + icon_state = "paint_red" + list_reagents = list("paint_red" = 70) + +/obj/item/reagent_containers/glass/paint/green + name = "green paint bucket" + icon_state = "paint_green" + list_reagents = list("paint_green" = 70) + +/obj/item/reagent_containers/glass/paint/blue + name = "blue paint bucket" + icon_state = "paint_blue" + list_reagents = list("paint_blue" = 70) + +/obj/item/reagent_containers/glass/paint/yellow + name = "yellow paint bucket" + icon_state = "paint_yellow" + list_reagents = list("paint_yellow" = 70) + +/obj/item/reagent_containers/glass/paint/violet + name = "violet paint bucket" + icon_state = "paint_violet" + list_reagents = list("paint_violet" = 70) + +/obj/item/reagent_containers/glass/paint/black + name = "black paint bucket" + icon_state = "paint_black" + list_reagents = list("paint_black" = 70) + +/obj/item/reagent_containers/glass/paint/white + name = "white paint bucket" + icon_state = "paint_white" + list_reagents = list("paint_white" = 70) + +/obj/item/reagent_containers/glass/paint/remover + name = "paint remover bucket" + list_reagents = list("paint_remover" = 70) diff --git a/code/game/objects/items/weapons/paiwire.dm b/code/game/objects/items/weapons/paiwire.dm index b4f46ffec46c..2e0ddfa828d3 100644 --- a/code/game/objects/items/weapons/paiwire.dm +++ b/code/game/objects/items/weapons/paiwire.dm @@ -1,11 +1,11 @@ -/obj/item/pai_cable/proc/plugin(obj/machinery/M as obj, mob/user as mob) - if(istype(M, /obj/machinery/door) || istype(M, /obj/machinery/camera)) - user.visible_message("[user] inserts [src] into a data port on [M].", "You insert [src] into a data port on [M].", "You hear the satisfying click of a wire jack fastening into place.") - user.drop_item() - src.loc = M - src.machine = M - else - user.visible_message("[user] dumbly fumbles to find a place on [M] to plug in [src].", "There aren't any ports on [M] that match the jack belonging to [src].") - -/obj/item/pai_cable/attack(obj/machinery/M as obj, mob/user as mob) - src.plugin(M, user) \ No newline at end of file +/obj/item/pai_cable/proc/plugin(obj/machinery/M as obj, mob/user as mob) + if(istype(M, /obj/machinery/door) || istype(M, /obj/machinery/camera)) + user.visible_message("[user] inserts [src] into a data port on [M].", "You insert [src] into a data port on [M].", "You hear the satisfying click of a wire jack fastening into place.") + user.drop_item() + src.loc = M + src.machine = M + else + user.visible_message("[user] dumbly fumbles to find a place on [M] to plug in [src].", "There aren't any ports on [M] that match the jack belonging to [src].") + +/obj/item/pai_cable/attack(obj/machinery/M as obj, mob/user as mob) + src.plugin(M, user) diff --git a/code/game/objects/items/weapons/rpd.dm b/code/game/objects/items/weapons/rpd.dm index 073ae0941e4c..be901e51ef2c 100644 --- a/code/game/objects/items/weapons/rpd.dm +++ b/code/game/objects/items/weapons/rpd.dm @@ -39,6 +39,21 @@ var/primary_sound = 'sound/machines/click.ogg' var/alt_sound = null + //Lists of things + var/list/mainmenu = list( + list("category" = "Atmospherics", "mode" = RPD_ATMOS_MODE, "icon" = "wrench"), + list("category" = "Disposals", "mode" = RPD_DISPOSALS_MODE, "icon" = "recycle"), + list("category" = "Rotate", "mode" = RPD_ROTATE_MODE, "icon" = "rotate-right"), + list("category" = "Flip", "mode" = RPD_FLIP_MODE, "icon" = "exchange"), + list("category" = "Recycle", "mode" = RPD_DELETE_MODE, "icon" = "trash")) + var/list/pipemenu = list( + list("category" = "Normal", "pipemode" = RPD_ATMOS_PIPING), + list("category" = "Supply", "pipemode" = RPD_SUPPLY_PIPING), + list("category" = "Scrubber", "pipemode" = RPD_SCRUBBERS_PIPING), + list("category" = "Devices", "pipemode" = RPD_DEVICES), + list("category" = "Heat exchange", "pipemode" = RPD_HEAT_PIPING)) + + /obj/item/rpd/New() ..() spark_system = new /datum/effect_system/spark_spread() @@ -150,27 +165,12 @@ QDEL_NULL(P) activate_rpd() -//Lists of things - -var/list/mainmenu = list( - list("category" = "Atmospherics", "mode" = RPD_ATMOS_MODE, "icon" = "wrench"), - list("category" = "Disposals", "mode" = RPD_DISPOSALS_MODE, "icon" = "recycle"), - list("category" = "Rotate", "mode" = RPD_ROTATE_MODE, "icon" = "rotate-right"), - list("category" = "Flip", "mode" = RPD_FLIP_MODE, "icon" = "exchange"), - list("category" = "Recycle", "mode" = RPD_DELETE_MODE, "icon" = "trash")) -var/list/pipemenu = list( - list("category" = "Normal", "pipemode" = RPD_ATMOS_PIPING), - list("category" = "Supply", "pipemode" = RPD_SUPPLY_PIPING), - list("category" = "Scrubber", "pipemode" = RPD_SCRUBBERS_PIPING), - list("category" = "Devices", "pipemode" = RPD_DEVICES), - list("category" = "Heat exchange", "pipemode" = RPD_HEAT_PIPING)) - //NanoUI stuff /obj/item/rpd/attack_self(mob/user) ui_interact(user) -/obj/item/rpd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) +/obj/item/rpd/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "rpd.tmpl", "[name]", 400, 650, state = state) @@ -180,7 +180,7 @@ var/list/pipemenu = list( /obj/item/rpd/AltClick(mob/user) radial_menu(user) -/obj/item/rpd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) +/obj/item/rpd/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) var/data[0] data["iconrotation"] = iconrotation data["mainmenu"] = mainmenu diff --git a/code/game/objects/items/weapons/scrolls.dm b/code/game/objects/items/weapons/scrolls.dm index 1b2b9c6f21c1..28d7df5874f9 100644 --- a/code/game/objects/items/weapons/scrolls.dm +++ b/code/game/objects/items/weapons/scrolls.dm @@ -1,106 +1,106 @@ -/obj/item/teleportation_scroll - name = "scroll of teleportation" - desc = "A scroll for moving around." - icon = 'icons/obj/wizard.dmi' - icon_state = "scroll" - var/uses = 4.0 - w_class = WEIGHT_CLASS_SMALL - item_state = "paper" - throw_speed = 4 - throw_range = 20 - origin_tech = "bluespace=6" - resistance_flags = FLAMMABLE - -/obj/item/teleportation_scroll/apprentice - name = "lesser scroll of teleportation" - uses = 1 - origin_tech = "bluespace=5" - -/obj/item/teleportation_scroll/attack_self(mob/user as mob) - user.set_machine(src) - var/dat = "Teleportation Scroll:
    " - dat += "Number of uses: [src.uses]
    " - dat += "
    " - dat += "Four uses use them wisely:
    " - dat += "Teleport
    " - dat += "Kind regards,
    Wizards Federation

    P.S. Don't forget to bring your gear, you'll need it to cast most spells.
    " - user << browse(dat, "window=scroll") - onclose(user, "scroll") - return - -/obj/item/teleportation_scroll/Topic(href, href_list) - ..() - if(usr.stat || usr.restrained() || src.loc != usr) - return - var/mob/living/carbon/human/H = usr - if(!( istype(H, /mob/living/carbon/human))) - return 1 - if((usr == src.loc || (in_range(src, usr) && istype(src.loc, /turf)))) - usr.set_machine(src) - if(href_list["spell_teleport"]) - if(src.uses >= 1) - teleportscroll(H) - attack_self(H) - return - -/obj/item/teleportation_scroll/proc/teleportscroll(var/mob/user) - - var/A - - A = input(user, "Area to jump to", "BOOYEA", A) as null|anything in teleportlocs - - if(!A) - return - - var/area/thearea = teleportlocs[A] - - if(user.stat || user.restrained()) - return - if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf))))) - return - - if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) - to_chat(user, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") - return - - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(5, 0, user.loc) - smoke.attach(user) - smoke.start() - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L.len) - to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - if(user && user.buckled) - user.buckled.unbuckle_mob(user, force = TRUE) - - if(user && user.has_buckled_mobs()) - user.unbuckle_all_mobs(force = TRUE) - - var/list/tempL = L - var/attempt = null - var/success = 0 - while(tempL.len) - attempt = pick(tempL) - success = user.Move(attempt) - if(!success) - tempL.Remove(attempt) - else - break - - if(!success) - user.loc = pick(L) - - smoke.start() - src.uses -= 1 +/obj/item/teleportation_scroll + name = "scroll of teleportation" + desc = "A scroll for moving around." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll" + var/uses = 4.0 + w_class = WEIGHT_CLASS_SMALL + item_state = "paper" + throw_speed = 4 + throw_range = 20 + origin_tech = "bluespace=6" + resistance_flags = FLAMMABLE + +/obj/item/teleportation_scroll/apprentice + name = "lesser scroll of teleportation" + uses = 1 + origin_tech = "bluespace=5" + +/obj/item/teleportation_scroll/attack_self(mob/user as mob) + user.set_machine(src) + var/dat = "Teleportation Scroll:
    " + dat += "Number of uses: [src.uses]
    " + dat += "
    " + dat += "Four uses use them wisely:
    " + dat += "Teleport
    " + dat += "Kind regards,
    Wizards Federation

    P.S. Don't forget to bring your gear, you'll need it to cast most spells.
    " + user << browse(dat, "window=scroll") + onclose(user, "scroll") + return + +/obj/item/teleportation_scroll/Topic(href, href_list) + ..() + if(usr.stat || usr.restrained() || src.loc != usr) + return + var/mob/living/carbon/human/H = usr + if(!( istype(H, /mob/living/carbon/human))) + return 1 + if((usr == src.loc || (in_range(src, usr) && istype(src.loc, /turf)))) + usr.set_machine(src) + if(href_list["spell_teleport"]) + if(src.uses >= 1) + teleportscroll(H) + attack_self(H) + return + +/obj/item/teleportation_scroll/proc/teleportscroll(var/mob/user) + + var/A + + A = input(user, "Area to jump to", "BOOYEA", A) as null|anything in GLOB.teleportlocs + + if(!A) + return + + var/area/thearea = GLOB.teleportlocs[A] + + if(user.stat || user.restrained()) + return + if(!((user == loc || (in_range(src, user) && istype(src.loc, /turf))))) + return + + if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) + to_chat(user, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") + return + + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, 0, user.loc) + smoke.attach(user) + smoke.start() + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L.len) + to_chat(user, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") + return + + if(user && user.buckled) + user.buckled.unbuckle_mob(user, force = TRUE) + + if(user && user.has_buckled_mobs()) + user.unbuckle_all_mobs(force = TRUE) + + var/list/tempL = L + var/attempt = null + var/success = 0 + while(tempL.len) + attempt = pick(tempL) + success = user.Move(attempt) + if(!success) + tempL.Remove(attempt) + else + break + + if(!success) + user.loc = pick(L) + + smoke.start() + src.uses -= 1 diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm index 7388cff9e5b8..362ab6016854 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -1,148 +1,148 @@ -/obj/item/shield - name = "shield" - block_chance = 50 - armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - -/obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == THROWN_PROJECTILE_ATTACK) - final_block_chance += 30 - if(attack_type == LEAP_ATTACK) - final_block_chance = 100 - return ..() - -/obj/item/shield/riot - name = "riot shield" - desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." - icon_state = "riot" - slot_flags = SLOT_BACK - force = 10 - throwforce = 5 - throw_speed = 2 - throw_range = 3 - w_class = WEIGHT_CLASS_BULKY - materials = list(MAT_GLASS=7500, MAT_METAL=1000) - origin_tech = "materials=3;combat=4" - attack_verb = list("shoved", "bashed") - var/cooldown = 0 //shield bash cooldown. based on world.time - -/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/melee/baton)) - if(cooldown < world.time - 25) - user.visible_message("[user] bashes [src] with [W]!") - playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) - cooldown = world.time - else - ..() - -/obj/item/shield/riot/roman - name = "roman shield" - desc = "Bears an inscription on the inside: \"Romanes venio domus\"." - icon_state = "roman_shield" - item_state = "roman_shield" - materials = list(MAT_METAL=8500) - -/obj/item/shield/riot/roman/fake - desc = "Bears an inscription on the inside: \"Romanes venio domus\". It appears to be a bit flimsy." - block_chance = 0 - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/shield/riot/buckler - name = "wooden buckler" - desc = "A medieval wooden buckler." - icon_state = "buckler" - item_state = "buckler" - materials = list() - origin_tech = "materials=1;combat=3;biotech=2" - resistance_flags = FLAMMABLE - block_chance = 30 - -/obj/item/shield/energy - name = "energy combat shield" - desc = "A shield that reflects almost all energy projectiles, but is useless against physical attacks. It can be retracted, expanded, and stored anywhere." - icon_state = "eshield0" // eshield1 for expanded - force = 3 - throwforce = 3 - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - origin_tech = "materials=4;magnets=5;syndicate=6" - attack_verb = list("shoved", "bashed") - var/active = 0 - -/obj/item/shield/energy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - return 0 - -/obj/item/shield/energy/IsReflect() - return (active) - -/obj/item/shield/energy/attack_self(mob/living/carbon/human/user) - if(user.disabilities & CLUMSY && prob(50)) - to_chat(user, "You beat yourself in the head with [src].") - user.take_organ_damage(5) - active = !active - icon_state = "eshield[active]" - - if(active) - force = 10 - throwforce = 8 - throw_speed = 2 - w_class = WEIGHT_CLASS_BULKY - playsound(user, 'sound/weapons/saberon.ogg', 35, 1) - to_chat(user, "[src] is now active.") - else - force = 3 - throwforce = 3 - throw_speed = 3 - w_class = WEIGHT_CLASS_TINY - playsound(user, 'sound/weapons/saberoff.ogg', 35, 1) - to_chat(user, "[src] can now be concealed.") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - add_fingerprint(user) - return - -/obj/item/shield/riot/tele - name = "telescopic shield" - desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." - icon_state = "teleriot0" - origin_tech = "materials=3;combat=4;engineering=4" - slot_flags = null - force = 3 - throwforce = 3 - throw_speed = 3 - throw_range = 4 - w_class = WEIGHT_CLASS_NORMAL - var/active = 0 - -/obj/item/shield/riot/tele/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(active) - return ..() - return 0 - -/obj/item/shield/riot/tele/attack_self(mob/living/user) - active = !active - icon_state = "teleriot[active]" - playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) - - if(active) - force = 8 - throwforce = 5 - throw_speed = 2 - w_class = WEIGHT_CLASS_BULKY - slot_flags = SLOT_BACK - to_chat(user, "You extend \the [src].") - else - force = 3 - throwforce = 3 - throw_speed = 3 - w_class = WEIGHT_CLASS_NORMAL - slot_flags = null - to_chat(user, "[src] can now be concealed.") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - add_fingerprint(user) - return +/obj/item/shield + name = "shield" + block_chance = 50 + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + +/obj/item/shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(attack_type == THROWN_PROJECTILE_ATTACK) + final_block_chance += 30 + if(attack_type == LEAP_ATTACK) + final_block_chance = 100 + return ..() + +/obj/item/shield/riot + name = "riot shield" + desc = "A shield adept at blocking blunt objects from connecting with the torso of the shield wielder." + icon_state = "riot" + slot_flags = SLOT_BACK + force = 10 + throwforce = 5 + throw_speed = 2 + throw_range = 3 + w_class = WEIGHT_CLASS_BULKY + materials = list(MAT_GLASS=7500, MAT_METAL=1000) + origin_tech = "materials=3;combat=4" + attack_verb = list("shoved", "bashed") + var/cooldown = 0 //shield bash cooldown. based on world.time + +/obj/item/shield/riot/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/melee/baton)) + if(cooldown < world.time - 25) + user.visible_message("[user] bashes [src] with [W]!") + playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1) + cooldown = world.time + else + ..() + +/obj/item/shield/riot/roman + name = "roman shield" + desc = "Bears an inscription on the inside: \"Romanes venio domus\"." + icon_state = "roman_shield" + item_state = "roman_shield" + materials = list(MAT_METAL=8500) + +/obj/item/shield/riot/roman/fake + desc = "Bears an inscription on the inside: \"Romanes venio domus\". It appears to be a bit flimsy." + block_chance = 0 + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/shield/riot/buckler + name = "wooden buckler" + desc = "A medieval wooden buckler." + icon_state = "buckler" + item_state = "buckler" + materials = list() + origin_tech = "materials=1;combat=3;biotech=2" + resistance_flags = FLAMMABLE + block_chance = 30 + +/obj/item/shield/energy + name = "energy combat shield" + desc = "A shield that reflects almost all energy projectiles, but is useless against physical attacks. It can be retracted, expanded, and stored anywhere." + icon_state = "eshield0" // eshield1 for expanded + force = 3 + throwforce = 3 + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + origin_tech = "materials=4;magnets=5;syndicate=6" + attack_verb = list("shoved", "bashed") + var/active = 0 + +/obj/item/shield/energy/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + return 0 + +/obj/item/shield/energy/IsReflect() + return (active) + +/obj/item/shield/energy/attack_self(mob/living/carbon/human/user) + if(user.disabilities & CLUMSY && prob(50)) + to_chat(user, "You beat yourself in the head with [src].") + user.take_organ_damage(5) + active = !active + icon_state = "eshield[active]" + + if(active) + force = 10 + throwforce = 8 + throw_speed = 2 + w_class = WEIGHT_CLASS_BULKY + playsound(user, 'sound/weapons/saberon.ogg', 35, 1) + to_chat(user, "[src] is now active.") + else + force = 3 + throwforce = 3 + throw_speed = 3 + w_class = WEIGHT_CLASS_TINY + playsound(user, 'sound/weapons/saberoff.ogg', 35, 1) + to_chat(user, "[src] can now be concealed.") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + add_fingerprint(user) + return + +/obj/item/shield/riot/tele + name = "telescopic shield" + desc = "An advanced riot shield made of lightweight materials that collapses for easy storage." + icon_state = "teleriot0" + origin_tech = "materials=3;combat=4;engineering=4" + slot_flags = null + force = 3 + throwforce = 3 + throw_speed = 3 + throw_range = 4 + w_class = WEIGHT_CLASS_NORMAL + var/active = 0 + +/obj/item/shield/riot/tele/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(active) + return ..() + return 0 + +/obj/item/shield/riot/tele/attack_self(mob/living/user) + active = !active + icon_state = "teleriot[active]" + playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) + + if(active) + force = 8 + throwforce = 5 + throw_speed = 2 + w_class = WEIGHT_CLASS_BULKY + slot_flags = SLOT_BACK + to_chat(user, "You extend \the [src].") + else + force = 3 + throwforce = 3 + throw_speed = 3 + w_class = WEIGHT_CLASS_NORMAL + slot_flags = null + to_chat(user, "[src] can now be concealed.") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + add_fingerprint(user) + return diff --git a/code/game/objects/items/weapons/storage/artistic_toolbox.dm b/code/game/objects/items/weapons/storage/artistic_toolbox.dm index f1422186ab87..610f0d14ab01 100644 --- a/code/game/objects/items/weapons/storage/artistic_toolbox.dm +++ b/code/game/objects/items/weapons/storage/artistic_toolbox.dm @@ -223,4 +223,4 @@ affected_mob.adjustBruteLoss(5) if(ismob(progenitor.loc)) - progenitor.hunger++ \ No newline at end of file + progenitor.hunger++ diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm index af5e9540b02a..757d2b057571 100644 --- a/code/game/objects/items/weapons/storage/backpack.dm +++ b/code/game/objects/items/weapons/storage/backpack.dm @@ -1,587 +1,587 @@ - -/* - * Backpack - */ - -/obj/item/storage/backpack - name = "backpack" - desc = "You wear this on your back and put items into it." - icon_state = "backpack" - item_state = "backpack" - lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' - w_class = WEIGHT_CLASS_BULKY - slot_flags = SLOT_BACK //ERROOOOO - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 21 - storage_slots = 21 - resistance_flags = NONE - max_integrity = 300 - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/back.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/back.dmi', - "Grey" = 'icons/mob/species/grey/back.dmi' - ) //For Armalis anything but this and the nitrogen tank will use the default backpack icon. - -/obj/item/storage/backpack/attackby(obj/item/W as obj, mob/user as mob, params) - playsound(src.loc, "rustle", 50, 1, -5) - return ..() - -/obj/item/storage/backpack/examine(mob/user) - var/space_used = 0 - . = ..() - if(in_range(user, src)) - for(var/obj/item/I in contents) - space_used += I.w_class - if(!space_used) - . += " [src] is empty." - else if(space_used <= max_combined_w_class*0.6) - . += " [src] still has plenty of remaining space." - else if(space_used <= max_combined_w_class*0.8) - . += " [src] is beginning to run out of space." - else if(space_used < max_combined_w_class) - . += " [src] doesn't have much space left." - else - . += " [src] is full." - -/* - * Backpack Types - */ - -/obj/item/storage/backpack/holding - name = "Bag of Holding" - desc = "A backpack that opens into a localized pocket of Blue Space." - origin_tech = "bluespace=5;materials=4;engineering=4;plasmatech=5" - icon_state = "holdingpack" - item_state = "holdingpack" - max_w_class = WEIGHT_CLASS_HUGE - max_combined_w_class = 35 - resistance_flags = FIRE_PROOF - flags_2 = NO_MAT_REDEMPTION_2 - cant_hold = list(/obj/item/storage/backpack/holding) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 50) - -/obj/item/storage/backpack/holding/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/storage/backpack/holding)) - var/response = alert(user, "This creates a singularity, destroying you and much of the station. Are you SURE?","IMMINENT DEATH!", "No", "Yes") - if(response == "Yes") - user.visible_message("[user] grins as [user.p_they()] begin[user.p_s()] to put a Bag of Holding into a Bag of Holding!", "You begin to put the Bag of Holding into the Bag of Holding!") - if(do_after(user, 30, target=src)) - investigate_log("has become a singularity. Caused by [user.key]","singulo") - user.visible_message("[user] erupts in evil laughter as [user.p_they()] put[user.p_s()] the Bag of Holding into another Bag of Holding!", "You can't help but laugh wildly as you put the Bag of Holding into another Bag of Holding, complete darkness surrounding you."," You hear the sound of scientific evil brewing! ") - qdel(W) - var/obj/singularity/singulo = new /obj/singularity(get_turf(user)) - singulo.energy = 300 //To give it a small boost - message_admins("[key_name_admin(user)] detonated a bag of holding JMP)") - log_game("[key_name(user)] detonated a bag of holding") - qdel(src) - else - user.visible_message("After careful consideration, [user] has decided that putting a Bag of Holding inside another Bag of Holding would not yield the ideal outcome.","You come to the realization that this might not be the greatest idea.") - else - . = ..() - -/obj/item/storage/backpack/holding/singularity_act(current_size) - var/dist = max((current_size - 2),1) - explosion(src.loc,(dist),(dist*2),(dist*4)) - -/obj/item/storage/backpack/santabag - name = "Santa's Gift Bag" - desc = "Space Santa uses this to deliver toys to all the nice children in space on Christmas! Wow, it's pretty big!" - icon_state = "giftbag0" - item_state = "giftbag" - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 400 // can store a ton of shit! - -/obj/item/storage/backpack/cultpack - name = "trophy rack" - desc = "It's useful for both carrying extra gear and proudly declaring your insanity." - icon_state = "cultpack" - -/obj/item/storage/backpack/clown - name = "Giggles Von Honkerton" - desc = "It's a backpack made by Honk! Co." - icon_state = "clownpack" - item_state = "clownpack" - -/obj/item/storage/backpack/clown/syndie - -/obj/item/storage/backpack/clown/syndie/New() - ..() - new /obj/item/clothing/under/rank/clown(src) - new /obj/item/clothing/shoes/magboots/clown(src) - new /obj/item/clothing/mask/chameleon(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/pda/clown(src) - new /obj/item/storage/box/survival(src) - new /obj/item/reagent_containers/food/snacks/grown/banana(src) - new /obj/item/stamp/clown(src) - new /obj/item/toy/crayon/rainbow(src) - new /obj/item/storage/fancy/crayons(src) - new /obj/item/reagent_containers/spray/waterflower(src) - new /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana(src) - new /obj/item/instrument/bikehorn(src) - new /obj/item/bikehorn(src) - new /obj/item/dnainjector/comic(src) - -/obj/item/storage/backpack/mime - name = "Parcel Parceaux" - desc = "A silent backpack made for those silent workers. Silence Co." - icon_state = "mimepack" - item_state = "mimepack" - -/obj/item/storage/backpack/medic - name = "medical backpack" - desc = "It's a backpack especially designed for use in a sterile environment." - icon_state = "medicalpack" - item_state = "medicalpack" - -/obj/item/storage/backpack/security - name = "security backpack" - desc = "It's a very robust backpack." - icon_state = "securitypack" - item_state = "securitypack" - -/obj/item/storage/backpack/captain - name = "captain's backpack" - desc = "It's a special backpack made exclusively for Nanotrasen officers." - icon_state = "captainpack" - item_state = "captainpack" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/industrial - name = "industrial backpack" - desc = "It's a tough backpack for the daily grind of station life." - icon_state = "engiepack" - item_state = "engiepack" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/explorer - name = "explorer bag" - desc = "A robust backpack for stashing your loot." - icon_state = "explorerpack" - item_state = "explorerpack" - -/obj/item/storage/backpack/botany - name = "botany backpack" - desc = "It's a backpack made of all-natural fibers." - icon_state = "botpack" - item_state = "botpack" - -/obj/item/storage/backpack/chemistry - name = "chemistry backpack" - desc = "A backpack specially designed to repel stains and hazardous liquids." - icon_state = "chempack" - item_state = "chempack" - -/obj/item/storage/backpack/genetics - name = "genetics backpack" - desc = "A bag designed to be super tough, just in case someone hulks out on you." - icon_state = "genepack" - item_state = "genepack" - -/obj/item/storage/backpack/science - name = "science backpack" - desc = "A specially designed backpack. It's fire resistant and smells vaguely of plasma." - icon_state = "toxpack" - item_state = "toxpack" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/virology - name = "virology backpack" - desc = "A backpack made of hypo-allergenic fibers. It's designed to help prevent the spread of disease. Smells like monkey." - icon_state = "viropack" - item_state = "viropack" - -/obj/item/storage/backpack/blueshield - name = "blueshield backpack" - desc = "A robust backpack issued to Nanotrasen's finest." - icon_state = "blueshieldpack" - item_state = "blueshieldpack" - -/* - * Satchel Types - */ - -/obj/item/storage/backpack/satchel - name = "leather satchel" - desc = "It's a very fancy satchel made with fine leather." - icon_state = "satchel" - resistance_flags = FIRE_PROOF - var/strap_side_straight = FALSE - -/obj/item/storage/backpack/satchel/verb/switch_strap() - set name = "Switch Strap Side" - set category = "Object" - set src in usr - - if(usr.incapacitated()) - return - strap_side_straight = !strap_side_straight - icon_state = strap_side_straight ? "satchel-flipped" : "satchel" - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - H.update_inv_back() - - - -/obj/item/storage/backpack/satcheldeluxe - name = "leather satchel" - desc = "An NT Deluxe satchel, with the finest quality leather and the company logo in a thin gold stitch" - icon_state = "nt_deluxe" - -/obj/item/storage/backpack/satchel/withwallet/New() - ..() - new /obj/item/storage/wallet/random(src) - -/obj/item/storage/backpack/satchel_norm - name = "satchel" - desc = "A deluxe NT Satchel, made of the highest quality leather." - icon_state = "satchel-norm" - -/obj/item/storage/backpack/satchel_eng - name = "industrial satchel" - desc = "A tough satchel with extra pockets." - icon_state = "satchel-eng" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/satchel/explorer - name = "explorer satchel" - desc = "A robust satchel for stashing your loot." - icon_state = "satchel-explorer" - item_state = "securitypack" - -/obj/item/storage/backpack/satchel_med - name = "medical satchel" - desc = "A sterile satchel used in medical departments." - icon_state = "satchel-med" - -/obj/item/storage/backpack/satchel_vir - name = "virologist satchel" - desc = "A sterile satchel with virologist colours." - icon_state = "satchel-vir" - -/obj/item/storage/backpack/satchel_chem - name = "chemist satchel" - desc = "A sterile satchel with chemist colours." - icon_state = "satchel-chem" - -/obj/item/storage/backpack/satchel_gen - name = "geneticist satchel" - desc = "A sterile satchel with geneticist colours." - icon_state = "satchel-gen" - -/obj/item/storage/backpack/satchel_tox - name = "scientist satchel" - desc = "Useful for holding research materials." - icon_state = "satchel-tox" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/satchel_sec - name = "security satchel" - desc = "A robust satchel for security related needs." - icon_state = "satchel-sec" - -/obj/item/storage/backpack/satchel_hyd - name = "hydroponics satchel" - desc = "A green satchel for plant related work." - icon_state = "satchel-hyd" - -/obj/item/storage/backpack/satchel_cap - name = "captain's satchel" - desc = "An exclusive satchel for Nanotrasen officers." - icon_state = "satchel-cap" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/satchel_blueshield - name = "blueshield satchel" - desc = "A robust satchel issued to Nanotrasen's finest." - icon_state = "satchel-blueshield" - -/obj/item/storage/backpack/satchel_flat - name = "smuggler's satchel" - desc = "A very slim satchel that can easily fit into tight spaces." - icon_state = "satchel-flat" - w_class = WEIGHT_CLASS_NORMAL //Can fit in backpacks itself. - max_combined_w_class = 15 - level = 1 - cant_hold = list(/obj/item/storage/backpack/satchel_flat) //muh recursive backpacks - -/obj/item/storage/backpack/satchel_flat/hide(var/intact) - if(intact) - invisibility = 101 - anchored = 1 //otherwise you can start pulling, cover it, and drag around an invisible backpack. - icon_state = "[initial(icon_state)]2" - else - invisibility = initial(invisibility) - anchored = 0 - icon_state = initial(icon_state) - -/obj/item/storage/backpack/satchel_flat/New() - ..() - new /obj/item/stack/tile/plasteel(src) - new /obj/item/crowbar(src) - -/* - * Duffelbags - My thanks to MrSnapWalk for the original icon and Neinhaus for the job variants - Dave. - */ - -/obj/item/storage/backpack/duffel - name = "duffelbag" - desc = "A large grey duffelbag designed to hold more items than a regular bag." - icon_state = "duffel" - item_state = "duffel" - max_combined_w_class = 30 - slowdown = 1 - -/obj/item/storage/backpack/duffel/syndie - name = "suspicious looking duffelbag" - desc = "A large duffelbag for holding extra tactical supplies." - icon_state = "duffel-syndie" - item_state = "duffel-syndimed" - origin_tech = "syndicate=1" - silent = 1 - slowdown = 0 - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/duffel/syndie/med - name = "suspicious duffelbag" - desc = "A black and red duffelbag with a red and white cross sewn onto it." - icon_state = "duffel-syndimed" - item_state = "duffel-syndimed" - -/obj/item/storage/backpack/duffel/syndie/ammo - name = "suspicious duffelbag" - desc = "A black and red duffelbag with a patch depicting shotgun shells sewn onto it." - icon_state = "duffel-syndiammo" - item_state = "duffel-syndiammo" - -/obj/item/storage/backpack/duffel/syndie/ammo/shotgun - desc = "A large duffelbag, packed to the brim with Bulldog shotgun ammo." - -/obj/item/storage/backpack/duffel/syndie/ammo/shotgun/New() - ..() - for(var/i in 1 to 6) - new /obj/item/ammo_box/magazine/m12g(src) - new /obj/item/ammo_box/magazine/m12g/buckshot(src) - new /obj/item/ammo_box/magazine/m12g/buckshot(src) - new /obj/item/ammo_box/magazine/m12g/dragon(src) - -/obj/item/storage/backpack/duffel/mining_conscript/ - name = "mining conscription kit" - desc = "A kit containing everything a crewmember needs to support a shaft miner in the field." - -/obj/item/storage/backpack/duffel/mining_conscript/New() - ..() - new /obj/item/pickaxe(src) - new /obj/item/clothing/glasses/meson(src) - new /obj/item/t_scanner/adv_mining_scanner/lesser(src) - new /obj/item/storage/bag/ore(src) - new /obj/item/clothing/under/rank/miner/lavaland(src) - new /obj/item/encryptionkey/headset_cargo(src) - new /obj/item/clothing/mask/gas/explorer(src) - new /obj/item/gun/energy/kinetic_accelerator(src) - new /obj/item/kitchen/knife/combat/survival(src) - new /obj/item/flashlight/seclite(src) - new /obj/item/clothing/suit/hooded/explorer(src) - - -/obj/item/storage/backpack/duffel/syndie/ammo/smg - desc = "A large duffel bag, packed to the brim with C-20r magazines." - -/obj/item/storage/backpack/duffel/syndie/ammo/smg/New() - ..() - for(var/i in 1 to 10) - new /obj/item/ammo_box/magazine/smgm45(src) - -/obj/item/storage/backpack/duffel/syndie/c20rbundle - desc = "A large duffel bag containing a C-20r, some magazines, and a cheap looking suppressor." - -/obj/item/storage/backpack/duffel/syndie/c20rbundle/New() - ..() - new /obj/item/ammo_box/magazine/smgm45(src) - new /obj/item/ammo_box/magazine/smgm45(src) - new /obj/item/ammo_box/magazine/smgm45(src) - new /obj/item/gun/projectile/automatic/c20r(src) - new /obj/item/suppressor/specialoffer(src) - -/obj/item/storage/backpack/duffel/syndie/bulldogbundle - desc = "A large duffel bag containing a Bulldog, some drums, and a pair of thermal imaging glasses." - -/obj/item/storage/backpack/duffel/syndie/bulldogbundle/New() - ..() - new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) - new /obj/item/ammo_box/magazine/m12g(src) - new /obj/item/ammo_box/magazine/m12g(src) - new /obj/item/clothing/glasses/chameleon/thermal(src) - -/obj/item/storage/backpack/duffel/syndie/med/medicalbundle - desc = "A large duffel bag containing a tactical medkit, a medical beam gun and a pair of syndicate magboots." - -/obj/item/storage/backpack/duffel/syndie/med/medicalbundle/New() - ..() - new /obj/item/storage/firstaid/tactical(src) - new /obj/item/clothing/shoes/magboots/syndie(src) - new /obj/item/gun/medbeam(src) - -/obj/item/storage/backpack/duffel/syndie/c4/New() - ..() - for(var/i in 1 to 10) - new /obj/item/grenade/plastic/c4(src) - -/obj/item/storage/backpack/duffel/syndie/x4/New() - ..() - for(var/i in 1 to 3) - new /obj/item/grenade/plastic/x4(src) - -/obj/item/storage/backpack/duffel/syndie/surgery - name = "surgery duffelbag" - desc = "A suspicious looking duffelbag for holding surgery tools." - icon_state = "duffel-syndimed" - item_state = "duffel-syndimed" - -/obj/item/storage/backpack/duffel/syndie/surgery/New() - ..() - new /obj/item/scalpel(src) - new /obj/item/hemostat(src) - new /obj/item/retractor(src) - new /obj/item/circular_saw(src) - new /obj/item/surgicaldrill(src) - new /obj/item/cautery(src) - new /obj/item/bonegel(src) - new /obj/item/bonesetter(src) - new /obj/item/FixOVein(src) - new /obj/item/clothing/suit/straight_jacket(src) - new /obj/item/clothing/mask/muzzle(src) - -/obj/item/storage/backpack/duffel/syndie/surgery_fake //for maint spawns - name = "surgery duffelbag" - desc = "A suspicious looking duffelbag for holding surgery tools." - icon_state = "duffel-syndimed" - item_state = "duffel-syndimed" - -/obj/item/storage/backpack/duffel/syndie/surgery_fake/New() - ..() - new /obj/item/scalpel(src) - new /obj/item/hemostat(src) - new /obj/item/retractor(src) - new /obj/item/cautery(src) - new /obj/item/bonegel(src) - new /obj/item/bonesetter(src) - new /obj/item/FixOVein(src) - if(prob(50)) - new /obj/item/circular_saw(src) - new /obj/item/surgicaldrill(src) - -/obj/item/storage/backpack/duffel/captain - name = "captain's duffelbag" - desc = "A duffelbag designed to hold large quantities of condoms." - icon_state = "duffel-captain" - item_state = "duffel-captain" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/duffel/security - name = "security duffelbag" - desc = "A duffelbag built with robust fabric!" - icon_state = "duffel-security" - item_state = "duffel-security" - -/obj/item/storage/backpack/duffel/virology - name = "virology duffelbag" - desc = "A white duffelbag designed to contain biohazards." - icon_state = "duffel-viro" - item_state = "duffel-viro" - -/obj/item/storage/backpack/duffel/science - name = "scientist duffelbag" - desc = "A duffelbag designed to hold the secrets of space." - icon_state = "duffel-toxins" - item_state = "duffel-toxins" - -/obj/item/storage/backpack/duffel/genetics - name = "geneticist duffelbag" - desc = "A duffelbag designed to hold gibbering monkies." - icon_state = "duffel-gene" - item_state = "duffel-gene" - -/obj/item/storage/backpack/duffel/chemistry - name = "chemist duffelbag" - desc = "A duffelbag designed to hold corrosive substances." - icon_state = "duffel-chemistry" - item_state = "duffel-chemistry" - -/obj/item/storage/backpack/duffel/medical - name = "medical duffelbag" - desc = "A duffelbag designed to hold medicine." - icon_state = "duffel-med" - item_state = "duffel-med" - -/obj/item/storage/backpack/duffel/engineering - name = "industrial duffelbag" - desc = "A duffelbag designed to hold tools." - icon_state = "duffel-eng" - item_state = "duffel-eng" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/duffel/atmos - name = "atmospherics duffelbag" - desc = "A duffelbag designed to hold tools. This one is specially designed for atmospherics." - icon_state = "duffel-atmos" - item_state = "duffel-atmos" - resistance_flags = FIRE_PROOF - -/obj/item/storage/backpack/duffel/hydro - name = "hydroponics duffelbag" - desc = "A duffelbag designed to hold seeds and fauna." - icon_state = "duffel-hydro" - item_state = "duffel-hydro" - -/obj/item/storage/backpack/duffel/clown - name = "smiles von wiggleton" - desc = "A duffelbag designed to hold bananas and bike horns." - icon_state = "duffel-clown" - item_state = "duffel-clown" - -obj/item/storage/backpack/duffel/blueshield - name = "blueshield duffelbag" - desc = "A robust duffelbag issued to Nanotrasen's finest." - icon_state = "duffel-blueshield" - item_state = "duffel-blueshield" - -//ERT backpacks. -/obj/item/storage/backpack/ert - name = "emergency response team backpack" - desc = "A spacious backpack with lots of pockets, used by members of the Nanotrasen Emergency Response Team." - icon_state = "ert_commander" - item_state = "backpack" - max_combined_w_class = 30 - resistance_flags = FIRE_PROOF - -//Commander -/obj/item/storage/backpack/ert/commander - name = "emergency response team commander backpack" - desc = "A spacious backpack with lots of pockets, worn by the commander of a Nanotrasen Emergency Response Team." - -//Security -/obj/item/storage/backpack/ert/security - name = "emergency response team security backpack" - desc = "A spacious backpack with lots of pockets, worn by security members of a Nanotrasen Emergency Response Team." - icon_state = "ert_security" - -//Engineering -/obj/item/storage/backpack/ert/engineer - name = "emergency response team engineer backpack" - desc = "A spacious backpack with lots of pockets, worn by engineering members of a Nanotrasen Emergency Response Team." - icon_state = "ert_engineering" - -//Medical -/obj/item/storage/backpack/ert/medical - name = "emergency response team medical backpack" - desc = "A spacious backpack with lots of pockets, worn by medical members of a Nanotrasen Emergency Response Team." - icon_state = "ert_medical" - -//Janitorial -/obj/item/storage/backpack/ert/janitor - name = "emergency response team janitor backpack" - desc = "A spacious backpack with lots of pockets, worn by janitorial members of a Nanotrasen Emergency Response Team." - icon_state = "ert_janitor" + +/* + * Backpack + */ + +/obj/item/storage/backpack + name = "backpack" + desc = "You wear this on your back and put items into it." + icon_state = "backpack" + item_state = "backpack" + lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' + w_class = WEIGHT_CLASS_BULKY + slot_flags = SLOT_BACK //ERROOOOO + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 21 + storage_slots = 21 + resistance_flags = NONE + max_integrity = 300 + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/back.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/back.dmi', + "Grey" = 'icons/mob/species/grey/back.dmi' + ) //For Armalis anything but this and the nitrogen tank will use the default backpack icon. + +/obj/item/storage/backpack/attackby(obj/item/W as obj, mob/user as mob, params) + playsound(src.loc, "rustle", 50, 1, -5) + return ..() + +/obj/item/storage/backpack/examine(mob/user) + var/space_used = 0 + . = ..() + if(in_range(user, src)) + for(var/obj/item/I in contents) + space_used += I.w_class + if(!space_used) + . += " [src] is empty." + else if(space_used <= max_combined_w_class*0.6) + . += " [src] still has plenty of remaining space." + else if(space_used <= max_combined_w_class*0.8) + . += " [src] is beginning to run out of space." + else if(space_used < max_combined_w_class) + . += " [src] doesn't have much space left." + else + . += " [src] is full." + +/* + * Backpack Types + */ + +/obj/item/storage/backpack/holding + name = "Bag of Holding" + desc = "A backpack that opens into a localized pocket of Blue Space." + origin_tech = "bluespace=5;materials=4;engineering=4;plasmatech=5" + icon_state = "holdingpack" + item_state = "holdingpack" + max_w_class = WEIGHT_CLASS_HUGE + max_combined_w_class = 35 + resistance_flags = FIRE_PROOF + flags_2 = NO_MAT_REDEMPTION_2 + cant_hold = list(/obj/item/storage/backpack/holding) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 50) + +/obj/item/storage/backpack/holding/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/storage/backpack/holding)) + var/response = alert(user, "This creates a singularity, destroying you and much of the station. Are you SURE?","IMMINENT DEATH!", "No", "Yes") + if(response == "Yes") + user.visible_message("[user] grins as [user.p_they()] begin[user.p_s()] to put a Bag of Holding into a Bag of Holding!", "You begin to put the Bag of Holding into the Bag of Holding!") + if(do_after(user, 30, target=src)) + investigate_log("has become a singularity. Caused by [user.key]","singulo") + user.visible_message("[user] erupts in evil laughter as [user.p_they()] put[user.p_s()] the Bag of Holding into another Bag of Holding!", "You can't help but laugh wildly as you put the Bag of Holding into another Bag of Holding, complete darkness surrounding you."," You hear the sound of scientific evil brewing! ") + qdel(W) + var/obj/singularity/singulo = new /obj/singularity(get_turf(user)) + singulo.energy = 300 //To give it a small boost + message_admins("[key_name_admin(user)] detonated a bag of holding JMP)") + log_game("[key_name(user)] detonated a bag of holding") + qdel(src) + else + user.visible_message("After careful consideration, [user] has decided that putting a Bag of Holding inside another Bag of Holding would not yield the ideal outcome.","You come to the realization that this might not be the greatest idea.") + else + . = ..() + +/obj/item/storage/backpack/holding/singularity_act(current_size) + var/dist = max((current_size - 2),1) + explosion(src.loc,(dist),(dist*2),(dist*4)) + +/obj/item/storage/backpack/santabag + name = "Santa's Gift Bag" + desc = "Space Santa uses this to deliver toys to all the nice children in space on Christmas! Wow, it's pretty big!" + icon_state = "giftbag0" + item_state = "giftbag" + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 400 // can store a ton of shit! + +/obj/item/storage/backpack/cultpack + name = "trophy rack" + desc = "It's useful for both carrying extra gear and proudly declaring your insanity." + icon_state = "cultpack" + +/obj/item/storage/backpack/clown + name = "Giggles Von Honkerton" + desc = "It's a backpack made by Honk! Co." + icon_state = "clownpack" + item_state = "clownpack" + +/obj/item/storage/backpack/clown/syndie + +/obj/item/storage/backpack/clown/syndie/New() + ..() + new /obj/item/clothing/under/rank/clown(src) + new /obj/item/clothing/shoes/magboots/clown(src) + new /obj/item/clothing/mask/chameleon(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/pda/clown(src) + new /obj/item/storage/box/survival(src) + new /obj/item/reagent_containers/food/snacks/grown/banana(src) + new /obj/item/stamp/clown(src) + new /obj/item/toy/crayon/rainbow(src) + new /obj/item/storage/fancy/crayons(src) + new /obj/item/reagent_containers/spray/waterflower(src) + new /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana(src) + new /obj/item/instrument/bikehorn(src) + new /obj/item/bikehorn(src) + new /obj/item/dnainjector/comic(src) + +/obj/item/storage/backpack/mime + name = "Parcel Parceaux" + desc = "A silent backpack made for those silent workers. Silence Co." + icon_state = "mimepack" + item_state = "mimepack" + +/obj/item/storage/backpack/medic + name = "medical backpack" + desc = "It's a backpack especially designed for use in a sterile environment." + icon_state = "medicalpack" + item_state = "medicalpack" + +/obj/item/storage/backpack/security + name = "security backpack" + desc = "It's a very robust backpack." + icon_state = "securitypack" + item_state = "securitypack" + +/obj/item/storage/backpack/captain + name = "captain's backpack" + desc = "It's a special backpack made exclusively for Nanotrasen officers." + icon_state = "captainpack" + item_state = "captainpack" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/industrial + name = "industrial backpack" + desc = "It's a tough backpack for the daily grind of station life." + icon_state = "engiepack" + item_state = "engiepack" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/explorer + name = "explorer bag" + desc = "A robust backpack for stashing your loot." + icon_state = "explorerpack" + item_state = "explorerpack" + +/obj/item/storage/backpack/botany + name = "botany backpack" + desc = "It's a backpack made of all-natural fibers." + icon_state = "botpack" + item_state = "botpack" + +/obj/item/storage/backpack/chemistry + name = "chemistry backpack" + desc = "A backpack specially designed to repel stains and hazardous liquids." + icon_state = "chempack" + item_state = "chempack" + +/obj/item/storage/backpack/genetics + name = "genetics backpack" + desc = "A bag designed to be super tough, just in case someone hulks out on you." + icon_state = "genepack" + item_state = "genepack" + +/obj/item/storage/backpack/science + name = "science backpack" + desc = "A specially designed backpack. It's fire resistant and smells vaguely of plasma." + icon_state = "toxpack" + item_state = "toxpack" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/virology + name = "virology backpack" + desc = "A backpack made of hypo-allergenic fibers. It's designed to help prevent the spread of disease. Smells like monkey." + icon_state = "viropack" + item_state = "viropack" + +/obj/item/storage/backpack/blueshield + name = "blueshield backpack" + desc = "A robust backpack issued to Nanotrasen's finest." + icon_state = "blueshieldpack" + item_state = "blueshieldpack" + +/* + * Satchel Types + */ + +/obj/item/storage/backpack/satchel + name = "leather satchel" + desc = "It's a very fancy satchel made with fine leather." + icon_state = "satchel" + resistance_flags = FIRE_PROOF + var/strap_side_straight = FALSE + +/obj/item/storage/backpack/satchel/verb/switch_strap() + set name = "Switch Strap Side" + set category = "Object" + set src in usr + + if(usr.incapacitated()) + return + strap_side_straight = !strap_side_straight + icon_state = strap_side_straight ? "satchel-flipped" : "satchel" + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + H.update_inv_back() + + + +/obj/item/storage/backpack/satcheldeluxe + name = "leather satchel" + desc = "An NT Deluxe satchel, with the finest quality leather and the company logo in a thin gold stitch" + icon_state = "nt_deluxe" + +/obj/item/storage/backpack/satchel/withwallet/New() + ..() + new /obj/item/storage/wallet/random(src) + +/obj/item/storage/backpack/satchel_norm + name = "satchel" + desc = "A deluxe NT Satchel, made of the highest quality leather." + icon_state = "satchel-norm" + +/obj/item/storage/backpack/satchel_eng + name = "industrial satchel" + desc = "A tough satchel with extra pockets." + icon_state = "satchel-eng" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/satchel/explorer + name = "explorer satchel" + desc = "A robust satchel for stashing your loot." + icon_state = "satchel-explorer" + item_state = "securitypack" + +/obj/item/storage/backpack/satchel_med + name = "medical satchel" + desc = "A sterile satchel used in medical departments." + icon_state = "satchel-med" + +/obj/item/storage/backpack/satchel_vir + name = "virologist satchel" + desc = "A sterile satchel with virologist colours." + icon_state = "satchel-vir" + +/obj/item/storage/backpack/satchel_chem + name = "chemist satchel" + desc = "A sterile satchel with chemist colours." + icon_state = "satchel-chem" + +/obj/item/storage/backpack/satchel_gen + name = "geneticist satchel" + desc = "A sterile satchel with geneticist colours." + icon_state = "satchel-gen" + +/obj/item/storage/backpack/satchel_tox + name = "scientist satchel" + desc = "Useful for holding research materials." + icon_state = "satchel-tox" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/satchel_sec + name = "security satchel" + desc = "A robust satchel for security related needs." + icon_state = "satchel-sec" + +/obj/item/storage/backpack/satchel_hyd + name = "hydroponics satchel" + desc = "A green satchel for plant related work." + icon_state = "satchel-hyd" + +/obj/item/storage/backpack/satchel_cap + name = "captain's satchel" + desc = "An exclusive satchel for Nanotrasen officers." + icon_state = "satchel-cap" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/satchel_blueshield + name = "blueshield satchel" + desc = "A robust satchel issued to Nanotrasen's finest." + icon_state = "satchel-blueshield" + +/obj/item/storage/backpack/satchel_flat + name = "smuggler's satchel" + desc = "A very slim satchel that can easily fit into tight spaces." + icon_state = "satchel-flat" + w_class = WEIGHT_CLASS_NORMAL //Can fit in backpacks itself. + max_combined_w_class = 15 + level = 1 + cant_hold = list(/obj/item/storage/backpack/satchel_flat) //muh recursive backpacks + +/obj/item/storage/backpack/satchel_flat/hide(var/intact) + if(intact) + invisibility = 101 + anchored = 1 //otherwise you can start pulling, cover it, and drag around an invisible backpack. + icon_state = "[initial(icon_state)]2" + else + invisibility = initial(invisibility) + anchored = 0 + icon_state = initial(icon_state) + +/obj/item/storage/backpack/satchel_flat/New() + ..() + new /obj/item/stack/tile/plasteel(src) + new /obj/item/crowbar(src) + +/* + * Duffelbags - My thanks to MrSnapWalk for the original icon and Neinhaus for the job variants - Dave. + */ + +/obj/item/storage/backpack/duffel + name = "duffelbag" + desc = "A large grey duffelbag designed to hold more items than a regular bag." + icon_state = "duffel" + item_state = "duffel" + max_combined_w_class = 30 + slowdown = 1 + +/obj/item/storage/backpack/duffel/syndie + name = "suspicious looking duffelbag" + desc = "A large duffelbag for holding extra tactical supplies." + icon_state = "duffel-syndie" + item_state = "duffel-syndimed" + origin_tech = "syndicate=1" + silent = 1 + slowdown = 0 + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/duffel/syndie/med + name = "suspicious duffelbag" + desc = "A black and red duffelbag with a red and white cross sewn onto it." + icon_state = "duffel-syndimed" + item_state = "duffel-syndimed" + +/obj/item/storage/backpack/duffel/syndie/ammo + name = "suspicious duffelbag" + desc = "A black and red duffelbag with a patch depicting shotgun shells sewn onto it." + icon_state = "duffel-syndiammo" + item_state = "duffel-syndiammo" + +/obj/item/storage/backpack/duffel/syndie/ammo/shotgun + desc = "A large duffelbag, packed to the brim with Bulldog shotgun ammo." + +/obj/item/storage/backpack/duffel/syndie/ammo/shotgun/New() + ..() + for(var/i in 1 to 6) + new /obj/item/ammo_box/magazine/m12g(src) + new /obj/item/ammo_box/magazine/m12g/buckshot(src) + new /obj/item/ammo_box/magazine/m12g/buckshot(src) + new /obj/item/ammo_box/magazine/m12g/dragon(src) + +/obj/item/storage/backpack/duffel/mining_conscript/ + name = "mining conscription kit" + desc = "A kit containing everything a crewmember needs to support a shaft miner in the field." + +/obj/item/storage/backpack/duffel/mining_conscript/New() + ..() + new /obj/item/pickaxe(src) + new /obj/item/clothing/glasses/meson(src) + new /obj/item/t_scanner/adv_mining_scanner/lesser(src) + new /obj/item/storage/bag/ore(src) + new /obj/item/clothing/under/rank/miner/lavaland(src) + new /obj/item/encryptionkey/headset_cargo(src) + new /obj/item/clothing/mask/gas/explorer(src) + new /obj/item/gun/energy/kinetic_accelerator(src) + new /obj/item/kitchen/knife/combat/survival(src) + new /obj/item/flashlight/seclite(src) + new /obj/item/clothing/suit/hooded/explorer(src) + + +/obj/item/storage/backpack/duffel/syndie/ammo/smg + desc = "A large duffel bag, packed to the brim with C-20r magazines." + +/obj/item/storage/backpack/duffel/syndie/ammo/smg/New() + ..() + for(var/i in 1 to 10) + new /obj/item/ammo_box/magazine/smgm45(src) + +/obj/item/storage/backpack/duffel/syndie/c20rbundle + desc = "A large duffel bag containing a C-20r, some magazines, and a cheap looking suppressor." + +/obj/item/storage/backpack/duffel/syndie/c20rbundle/New() + ..() + new /obj/item/ammo_box/magazine/smgm45(src) + new /obj/item/ammo_box/magazine/smgm45(src) + new /obj/item/ammo_box/magazine/smgm45(src) + new /obj/item/gun/projectile/automatic/c20r(src) + new /obj/item/suppressor/specialoffer(src) + +/obj/item/storage/backpack/duffel/syndie/bulldogbundle + desc = "A large duffel bag containing a Bulldog, some drums, and a pair of thermal imaging glasses." + +/obj/item/storage/backpack/duffel/syndie/bulldogbundle/New() + ..() + new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) + new /obj/item/ammo_box/magazine/m12g(src) + new /obj/item/ammo_box/magazine/m12g(src) + new /obj/item/clothing/glasses/chameleon/thermal(src) + +/obj/item/storage/backpack/duffel/syndie/med/medicalbundle + desc = "A large duffel bag containing a tactical medkit, a medical beam gun and a pair of syndicate magboots." + +/obj/item/storage/backpack/duffel/syndie/med/medicalbundle/New() + ..() + new /obj/item/storage/firstaid/tactical(src) + new /obj/item/clothing/shoes/magboots/syndie(src) + new /obj/item/gun/medbeam(src) + +/obj/item/storage/backpack/duffel/syndie/c4/New() + ..() + for(var/i in 1 to 10) + new /obj/item/grenade/plastic/c4(src) + +/obj/item/storage/backpack/duffel/syndie/x4/New() + ..() + for(var/i in 1 to 3) + new /obj/item/grenade/plastic/x4(src) + +/obj/item/storage/backpack/duffel/syndie/surgery + name = "surgery duffelbag" + desc = "A suspicious looking duffelbag for holding surgery tools." + icon_state = "duffel-syndimed" + item_state = "duffel-syndimed" + +/obj/item/storage/backpack/duffel/syndie/surgery/New() + ..() + new /obj/item/scalpel(src) + new /obj/item/hemostat(src) + new /obj/item/retractor(src) + new /obj/item/circular_saw(src) + new /obj/item/surgicaldrill(src) + new /obj/item/cautery(src) + new /obj/item/bonegel(src) + new /obj/item/bonesetter(src) + new /obj/item/FixOVein(src) + new /obj/item/clothing/suit/straight_jacket(src) + new /obj/item/clothing/mask/muzzle(src) + +/obj/item/storage/backpack/duffel/syndie/surgery_fake //for maint spawns + name = "surgery duffelbag" + desc = "A suspicious looking duffelbag for holding surgery tools." + icon_state = "duffel-syndimed" + item_state = "duffel-syndimed" + +/obj/item/storage/backpack/duffel/syndie/surgery_fake/New() + ..() + new /obj/item/scalpel(src) + new /obj/item/hemostat(src) + new /obj/item/retractor(src) + new /obj/item/cautery(src) + new /obj/item/bonegel(src) + new /obj/item/bonesetter(src) + new /obj/item/FixOVein(src) + if(prob(50)) + new /obj/item/circular_saw(src) + new /obj/item/surgicaldrill(src) + +/obj/item/storage/backpack/duffel/captain + name = "captain's duffelbag" + desc = "A duffelbag designed to hold large quantities of condoms." + icon_state = "duffel-captain" + item_state = "duffel-captain" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/duffel/security + name = "security duffelbag" + desc = "A duffelbag built with robust fabric!" + icon_state = "duffel-security" + item_state = "duffel-security" + +/obj/item/storage/backpack/duffel/virology + name = "virology duffelbag" + desc = "A white duffelbag designed to contain biohazards." + icon_state = "duffel-viro" + item_state = "duffel-viro" + +/obj/item/storage/backpack/duffel/science + name = "scientist duffelbag" + desc = "A duffelbag designed to hold the secrets of space." + icon_state = "duffel-toxins" + item_state = "duffel-toxins" + +/obj/item/storage/backpack/duffel/genetics + name = "geneticist duffelbag" + desc = "A duffelbag designed to hold gibbering monkies." + icon_state = "duffel-gene" + item_state = "duffel-gene" + +/obj/item/storage/backpack/duffel/chemistry + name = "chemist duffelbag" + desc = "A duffelbag designed to hold corrosive substances." + icon_state = "duffel-chemistry" + item_state = "duffel-chemistry" + +/obj/item/storage/backpack/duffel/medical + name = "medical duffelbag" + desc = "A duffelbag designed to hold medicine." + icon_state = "duffel-med" + item_state = "duffel-med" + +/obj/item/storage/backpack/duffel/engineering + name = "industrial duffelbag" + desc = "A duffelbag designed to hold tools." + icon_state = "duffel-eng" + item_state = "duffel-eng" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/duffel/atmos + name = "atmospherics duffelbag" + desc = "A duffelbag designed to hold tools. This one is specially designed for atmospherics." + icon_state = "duffel-atmos" + item_state = "duffel-atmos" + resistance_flags = FIRE_PROOF + +/obj/item/storage/backpack/duffel/hydro + name = "hydroponics duffelbag" + desc = "A duffelbag designed to hold seeds and fauna." + icon_state = "duffel-hydro" + item_state = "duffel-hydro" + +/obj/item/storage/backpack/duffel/clown + name = "smiles von wiggleton" + desc = "A duffelbag designed to hold bananas and bike horns." + icon_state = "duffel-clown" + item_state = "duffel-clown" + +obj/item/storage/backpack/duffel/blueshield + name = "blueshield duffelbag" + desc = "A robust duffelbag issued to Nanotrasen's finest." + icon_state = "duffel-blueshield" + item_state = "duffel-blueshield" + +//ERT backpacks. +/obj/item/storage/backpack/ert + name = "emergency response team backpack" + desc = "A spacious backpack with lots of pockets, used by members of the Nanotrasen Emergency Response Team." + icon_state = "ert_commander" + item_state = "backpack" + max_combined_w_class = 30 + resistance_flags = FIRE_PROOF + +//Commander +/obj/item/storage/backpack/ert/commander + name = "emergency response team commander backpack" + desc = "A spacious backpack with lots of pockets, worn by the commander of a Nanotrasen Emergency Response Team." + +//Security +/obj/item/storage/backpack/ert/security + name = "emergency response team security backpack" + desc = "A spacious backpack with lots of pockets, worn by security members of a Nanotrasen Emergency Response Team." + icon_state = "ert_security" + +//Engineering +/obj/item/storage/backpack/ert/engineer + name = "emergency response team engineer backpack" + desc = "A spacious backpack with lots of pockets, worn by engineering members of a Nanotrasen Emergency Response Team." + icon_state = "ert_engineering" + +//Medical +/obj/item/storage/backpack/ert/medical + name = "emergency response team medical backpack" + desc = "A spacious backpack with lots of pockets, worn by medical members of a Nanotrasen Emergency Response Team." + icon_state = "ert_medical" + +//Janitorial +/obj/item/storage/backpack/ert/janitor + name = "emergency response team janitor backpack" + desc = "A spacious backpack with lots of pockets, worn by janitorial members of a Nanotrasen Emergency Response Team." + icon_state = "ert_janitor" diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index a3808bf5d809..662887137755 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -1,503 +1,503 @@ -/* - * These absorb the functionality of the plant bag, ore satchel, etc. - * They use the use_to_pickup, quick_gather, and quick_empty functions - * that were already defined in weapon/storage, but which had been - * re-implemented in other classes. - * - * Contains: - * Trash Bag - * Mining Satchel - * Plant Bag - * Sheet Snatcher - * Book Bag - * Tray - * - * -Sayu - */ - -// Generic non-item -/obj/item/storage/bag - allow_quick_gather = 1 - allow_quick_empty = 1 - display_contents_with_number = 1 // should work fine now - use_to_pickup = 1 - slot_flags = SLOT_BELT - -// ----------------------------- -// Trash bag -// ----------------------------- -/obj/item/storage/bag/trash - name = "trash bag" - desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" - icon = 'icons/obj/janitor.dmi' - icon_state = "trashbag" - item_state = "trashbag" - - w_class = WEIGHT_CLASS_TINY - max_w_class = WEIGHT_CLASS_SMALL - storage_slots = 30 - can_hold = list() // any - cant_hold = list(/obj/item/disk/nuclear) - -/obj/item/storage/bag/trash/suicide_act(mob/user) - user.visible_message("[user] puts the [name] over [user.p_their()] head and starts chomping at the insides! Disgusting!") - playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1) - return TOXLOSS - -/obj/item/storage/bag/trash/update_icon() - if(contents.len == 0) - w_class = WEIGHT_CLASS_TINY - icon_state = "[initial(icon_state)]" - else if(contents.len < 12) - w_class = WEIGHT_CLASS_BULKY - icon_state = "[initial(icon_state)]1" - else if(contents.len < 21) - w_class = WEIGHT_CLASS_BULKY - icon_state = "[initial(icon_state)]2" - else - w_class = WEIGHT_CLASS_BULKY - icon_state = "[initial(icon_state)]3" - -/obj/item/storage/bag/trash/cyborg - -/obj/item/storage/bag/trash/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) - J.put_in_cart(src, user) - J.mybag=src - J.update_icon() - -/obj/item/storage/bag/trash/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) - return - -/obj/item/storage/bag/trash/bluespace - name = "trash bag of holding" - desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." - icon_state = "bluetrashbag" - origin_tech = "materials=4;bluespace=4;engineering=4;plasmatech=3" - max_combined_w_class = 60 - storage_slots = 60 - flags_2 = NO_MAT_REDEMPTION_2 - -// ----------------------------- -// Plastic Bag -// ----------------------------- - -/obj/item/storage/bag/plasticbag - name = "plastic bag" - desc = "It's a very flimsy, very noisy alternative to a bag." - icon = 'icons/obj/trash.dmi' - icon_state = "plasticbag" - item_state = "plasticbag" - slot_flags = SLOT_HEAD|SLOT_BELT - throwforce = 0 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_SMALL - storage_slots = 7 - display_contents_with_number = 0 //or else this will lead to stupid behavior. - can_hold = list() // any - cant_hold = list(/obj/item/disk/nuclear) - -/obj/item/storage/bag/plasticbag/mob_can_equip(M as mob, slot) - - if(slot==slot_head && contents.len) - to_chat(M, "You need to empty the bag first!") - return 0 - return ..() - - -/obj/item/storage/bag/plasticbag/equipped(var/mob/user, var/slot) - if(slot==slot_head) - storage_slots = 0 - START_PROCESSING(SSobj, src) - return - -/obj/item/storage/bag/plasticbag/process() - if(is_equipped()) - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.get_item_by_slot(slot_head) == src) - if(H.internal) - return - H.AdjustLoseBreath(1) - else - storage_slots = 7 - STOP_PROCESSING(SSobj, src) - return - -// ----------------------------- -// Mining Satchel -// ----------------------------- - -/obj/item/storage/bag/ore - name = "mining satchel" - desc = "This little bugger can be used to store and transport ores." - icon = 'icons/obj/mining.dmi' - icon_state = "satchel" - origin_tech = "engineering=2" - slot_flags = SLOT_BELT | SLOT_POCKET - w_class = WEIGHT_CLASS_NORMAL - storage_slots = 10 - max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * ore.w_class - max_w_class = WEIGHT_CLASS_NORMAL - can_hold = list(/obj/item/stack/ore) - -/obj/item/storage/bag/ore/cyborg - name = "cyborg mining satchel" - flags = NODROP - -/obj/item/storage/bag/ore/holding //miners, your messiah has arrived - name = "mining satchel of holding" - desc = "A revolution in convenience, this satchel allows for infinite ore storage. It's been outfitted with anti-malfunction safety measures." - storage_slots = INFINITY - max_combined_w_class = INFINITY - origin_tech = "bluespace=4;materials=3;engineering=3" - icon_state = "satchel_bspace" - -/obj/item/storage/bag/ore/holding/cyborg - name = "cyborg mining satchel of holding" - flags = NODROP - -// ----------------------------- -// Plant bag -// ----------------------------- - -/obj/item/storage/bag/plants - name = "plant bag" - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "plantbag" - storage_slots = 100 //the number of plant pieces it can carry. - max_combined_w_class = 100 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class - max_w_class = WEIGHT_CLASS_NORMAL - w_class = WEIGHT_CLASS_TINY - can_hold = list(/obj/item/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/grown,/obj/item/reagent_containers/food/snacks/grown/ash_flora) - resistance_flags = FLAMMABLE - -/obj/item/storage/bag/plants/portaseeder - name = "portable seed extractor" - desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant." - icon_state = "portaseeder" - origin_tech = "biotech=3;engineering=2" - -/obj/item/storage/bag/plants/portaseeder/verb/dissolve_contents() - set name = "Activate Seed Extraction" - set category = "Object" - set desc = "Activate to convert your plants into plantable seeds." - - if(usr.incapacitated()) - return - for(var/obj/item/O in contents) - seedify(O, 1) - for(var/mob/M in range(1)) - if(M.s_active == src) - close(M) - - -// ----------------------------- -// Sheet Snatcher -// ----------------------------- -// Because it stacks stacks, this doesn't operate normally. -// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu - -/obj/item/storage/bag/sheetsnatcher - icon = 'icons/obj/mining.dmi' - icon_state = "sheetsnatcher" - name = "Sheet Snatcher" - desc = "A patented Nanotrasen storage system designed for any kind of mineral sheet." - - var/capacity = 300; //the number of sheets it can carry. - w_class = WEIGHT_CLASS_NORMAL - - allow_quick_empty = 1 // this function is superceded -/obj/item/storage/bag/sheetsnatcher/New() - ..() - //verbs -= /obj/item/storage/verb/quick_empty - //verbs += /obj/item/storage/bag/sheetsnatcher/quick_empty - -/obj/item/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!istype(W,/obj/item/stack/sheet) || istype(W,/obj/item/stack/sheet/mineral/sandstone) || istype(W,/obj/item/stack/sheet/wood)) - if(!stop_messages) - to_chat(usr, "The snatcher does not accept [W].") - return 0 //I don't care, but the existing code rejects them for not being "sheets" *shrug* -Sayu - var/current = 0 - for(var/obj/item/stack/sheet/S in contents) - current += S.amount - if(capacity == current)//If it's full, you're done - if(!stop_messages) - to_chat(usr, "The snatcher is full.") - return 0 - return 1 - - -// Modified handle_item_insertion. Would prefer not to, but... -/obj/item/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) - var/obj/item/stack/sheet/S = W - if(!istype(S)) return 0 - - var/amount - var/inserted = 0 - var/current = 0 - for(var/obj/item/stack/sheet/S2 in contents) - current += S2.amount - if(capacity < current + S.amount)//If the stack will fill it up - amount = capacity - current - else - amount = S.amount - - for(var/obj/item/stack/sheet/sheet in contents) - if(S.type == sheet.type) // we are violating the amount limitation because these are not sane objects - sheet.amount += amount // they should only be removed through procs in this file, which split them up. - S.amount -= amount - inserted = 1 - break - - if(!inserted || !S.amount) - usr.unEquip(S) - usr.update_icons() //update our overlays - if(usr.client && usr.s_active != src) - usr.client.screen -= S - S.dropped(usr) - if(!S.amount) - qdel(S) - else - S.loc = src - - orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - update_icon() - return 1 - - -// Sets up numbered display to show the stack size of each stored mineral -// NOTE: numbered display is turned off currently because it's broken -/obj/item/storage/bag/sheetsnatcher/orient2hud(mob/user as mob) - var/adjusted_contents = contents.len - - //Numbered contents display - var/list/datum/numbered_display/numbered_contents - if(display_contents_with_number) - numbered_contents = list() - adjusted_contents = 0 - for(var/obj/item/stack/sheet/I in contents) - adjusted_contents++ - var/datum/numbered_display/D = new/datum/numbered_display(I) - D.number = I.amount - numbered_contents.Add( D ) - - var/row_num = 0 - var/col_count = min(7,storage_slots) -1 - if(adjusted_contents > 7) - row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. - src.standard_orient_objs(row_num, col_count, numbered_contents) - return - - -// Modified quick_empty verb drops appropriate sized stacks -/obj/item/storage/bag/sheetsnatcher/quick_empty() - var/location = get_turf(src) - for(var/obj/item/stack/sheet/S in contents) - while(S.amount) - var/obj/item/stack/sheet/N = new S.type(location) - var/stacksize = min(S.amount,N.max_amount) - N.amount = stacksize - S.amount -= stacksize - if(!S.amount) - qdel(S) // todo: there's probably something missing here - orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - update_icon() - -// Instead of removing -/obj/item/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W as obj, atom/new_location) - var/obj/item/stack/sheet/S = W - if(!istype(S)) return 0 - - //I would prefer to drop a new stack, but the item/attack_hand code - // that calls this can't recieve a different object than you clicked on. - //Therefore, make a new stack internally that has the remainder. - // -Sayu - - if(S.amount > S.max_amount) - var/obj/item/stack/sheet/temp = new S.type(src) - temp.amount = S.amount - S.max_amount - S.amount = S.max_amount - - return ..(S,new_location) - -// ----------------------------- -// Sheet Snatcher (Cyborg) -// ----------------------------- - -/obj/item/storage/bag/sheetsnatcher/borg - name = "Sheet Snatcher 9000" - desc = "" - capacity = 500//Borgs get more because >specialization - - -// ----------------------------- -// Cash Bag -// ----------------------------- - -/obj/item/storage/bag/cash - icon = 'icons/obj/storage.dmi' - icon_state = "cashbag" - name = "Cash bag" - desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front." - storage_slots = 50; //the number of cash pieces it can carry. - max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * cash.w_class - max_w_class = WEIGHT_CLASS_NORMAL - w_class = WEIGHT_CLASS_TINY - can_hold = list(/obj/item/coin,/obj/item/stack/spacecash) - -// ----------------------------- -// Book bag -// ----------------------------- - -/obj/item/storage/bag/books - name = "book bag" - desc = "A bag for books." - icon = 'icons/obj/library.dmi' - icon_state = "bookbag" - display_contents_with_number = 0 //This would look really stupid otherwise - storage_slots = 7 - max_combined_w_class = 21 - max_w_class = WEIGHT_CLASS_NORMAL - w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics - can_hold = list(/obj/item/book, /obj/item/storage/bible, /obj/item/tome, /obj/item/spellbook) - resistance_flags = FLAMMABLE - -/* - * Trays - Agouri - */ -/obj/item/storage/bag/tray - name = "tray" - icon = 'icons/obj/food/containers.dmi' - icon_state = "tray" - desc = "A metal tray to lay food on." - force = 5 - throwforce = 10.0 - throw_speed = 3 - throw_range = 5 - w_class = WEIGHT_CLASS_BULKY - flags = CONDUCT - materials = list(MAT_METAL=3000) - -/obj/item/storage/bag/tray/attack(mob/living/M as mob, mob/living/user as mob) - ..() - // Drop all the things. All of them. - var/list/obj/item/oldContents = contents.Copy() - quick_empty() - - // Make each item scatter a bit - for(var/obj/item/I in oldContents) - spawn() - for(var/i = 1, i <= rand(1,2), i++) - if(I) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) - - if(prob(50)) - playsound(M, 'sound/items/trayhit1.ogg', 50, 1) - else - playsound(M, 'sound/items/trayhit2.ogg', 50, 1) - - if(ishuman(M)) - if(prob(10)) - M.Weaken(2) - -/obj/item/storage/bag/tray/proc/rebuild_overlays() - overlays.Cut() - for(var/obj/item/I in contents) - overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = -1) - -/obj/item/storage/bag/tray/remove_from_storage(obj/item/W as obj, atom/new_location) - ..() - rebuild_overlays() - -/obj/item/storage/bag/tray/handle_item_insertion(obj/item/I, prevent_warning = 0) - overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = -1) - ..() - -/obj/item/storage/bag/tray/cyborg - -/obj/item/storage/bag/tray/cyborg/afterattack(atom/target, mob/user as mob) - if( isturf(target) || istype(target,/obj/structure/table) ) - var foundtable = istype(target,/obj/structure/table/) - if( !foundtable ) //it must be a turf! - for(var/obj/structure/table/T in target) - foundtable = 1 - break - - var turf/dropspot - if( !foundtable ) // don't unload things onto walls or other silly places. - dropspot = user.loc - else if( isturf(target) ) // they clicked on a turf with a table in it - dropspot = target - else // they clicked on a table - dropspot = target.loc - - overlays = null - - var droppedSomething = 0 - - for(var/obj/item/I in contents) - I.loc = dropspot - contents.Remove(I) - droppedSomething = 1 - if(!foundtable && isturf(dropspot)) - // if no table, presume that the person just shittily dropped the tray on the ground and made a mess everywhere! - spawn() - for(var/i = 1, i <= rand(1,2), i++) - if(I) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2,4)) - if( droppedSomething ) - if( foundtable ) - user.visible_message("[user] unloads [user.p_their()] service tray.") - else - user.visible_message("[user] drops all the items on [user.p_their()] tray.") - - return ..() - - -/obj/item/storage/bag/tray/cookies_tray - var/cookie = /obj/item/reagent_containers/food/snacks/cookie - -/obj/item/storage/bag/tray/cookies_tray/New() /// By Azule Utama, thank you a lot! - ..() - for(var/i in 1 to 6) - var/obj/item/C = new cookie(src) - handle_item_insertion(C) // Done this way so the tray actually has the cookies visible when spawned - rebuild_overlays() - -/obj/item/storage/bag/tray/cookies_tray/sugarcookie - cookie = /obj/item/reagent_containers/food/snacks/sugarcookie - -/* - * Chemistry bag - */ - -/obj/item/storage/bag/chemistry - name = "chemistry bag" - icon = 'icons/obj/chemical.dmi' - icon_state = "bag" - desc = "A bag for storing pills, patches, and bottles." - storage_slots = 50 - max_combined_w_class = 200 - w_class = WEIGHT_CLASS_TINY - can_hold = list(/obj/item/reagent_containers/food/pill,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/glass/bottle) - resistance_flags = FLAMMABLE -/* - * Biowaste bag (mostly for xenobiologists) - */ - -/obj/item/storage/bag/bio - name = "bio bag" - icon = 'icons/obj/chemical.dmi' - icon_state = "biobag" - desc = "A bag for the safe transportation and disposal of biowaste and other biological materials." - storage_slots = 25 - max_combined_w_class = 200 - w_class = WEIGHT_CLASS_TINY - can_hold = list(/obj/item/slime_extract,/obj/item/reagent_containers/food/snacks/monkeycube,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/iv_bag,/obj/item/reagent_containers/hypospray/autoinjector) - resistance_flags = FLAMMABLE +/* + * These absorb the functionality of the plant bag, ore satchel, etc. + * They use the use_to_pickup, quick_gather, and quick_empty functions + * that were already defined in weapon/storage, but which had been + * re-implemented in other classes. + * + * Contains: + * Trash Bag + * Mining Satchel + * Plant Bag + * Sheet Snatcher + * Book Bag + * Tray + * + * -Sayu + */ + +// Generic non-item +/obj/item/storage/bag + allow_quick_gather = 1 + allow_quick_empty = 1 + display_contents_with_number = 1 // should work fine now + use_to_pickup = 1 + slot_flags = SLOT_BELT + +// ----------------------------- +// Trash bag +// ----------------------------- +/obj/item/storage/bag/trash + name = "trash bag" + desc = "It's the heavy-duty black polymer kind. Time to take out the trash!" + icon = 'icons/obj/janitor.dmi' + icon_state = "trashbag" + item_state = "trashbag" + + w_class = WEIGHT_CLASS_TINY + max_w_class = WEIGHT_CLASS_SMALL + storage_slots = 30 + can_hold = list() // any + cant_hold = list(/obj/item/disk/nuclear) + +/obj/item/storage/bag/trash/suicide_act(mob/user) + user.visible_message("[user] puts the [name] over [user.p_their()] head and starts chomping at the insides! Disgusting!") + playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1) + return TOXLOSS + +/obj/item/storage/bag/trash/update_icon() + if(contents.len == 0) + w_class = WEIGHT_CLASS_TINY + icon_state = "[initial(icon_state)]" + else if(contents.len < 12) + w_class = WEIGHT_CLASS_BULKY + icon_state = "[initial(icon_state)]1" + else if(contents.len < 21) + w_class = WEIGHT_CLASS_BULKY + icon_state = "[initial(icon_state)]2" + else + w_class = WEIGHT_CLASS_BULKY + icon_state = "[initial(icon_state)]3" + +/obj/item/storage/bag/trash/cyborg + +/obj/item/storage/bag/trash/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) + J.put_in_cart(src, user) + J.mybag=src + J.update_icon() + +/obj/item/storage/bag/trash/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J) + return + +/obj/item/storage/bag/trash/bluespace + name = "trash bag of holding" + desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage." + icon_state = "bluetrashbag" + origin_tech = "materials=4;bluespace=4;engineering=4;plasmatech=3" + max_combined_w_class = 60 + storage_slots = 60 + flags_2 = NO_MAT_REDEMPTION_2 + +// ----------------------------- +// Plastic Bag +// ----------------------------- + +/obj/item/storage/bag/plasticbag + name = "plastic bag" + desc = "It's a very flimsy, very noisy alternative to a bag." + icon = 'icons/obj/trash.dmi' + icon_state = "plasticbag" + item_state = "plasticbag" + slot_flags = SLOT_HEAD|SLOT_BELT + throwforce = 0 + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_SMALL + storage_slots = 7 + display_contents_with_number = 0 //or else this will lead to stupid behavior. + can_hold = list() // any + cant_hold = list(/obj/item/disk/nuclear) + +/obj/item/storage/bag/plasticbag/mob_can_equip(M as mob, slot) + + if(slot==slot_head && contents.len) + to_chat(M, "You need to empty the bag first!") + return 0 + return ..() + + +/obj/item/storage/bag/plasticbag/equipped(var/mob/user, var/slot) + if(slot==slot_head) + storage_slots = 0 + START_PROCESSING(SSobj, src) + return + +/obj/item/storage/bag/plasticbag/process() + if(is_equipped()) + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.get_item_by_slot(slot_head) == src) + if(H.internal) + return + H.AdjustLoseBreath(1) + else + storage_slots = 7 + STOP_PROCESSING(SSobj, src) + return + +// ----------------------------- +// Mining Satchel +// ----------------------------- + +/obj/item/storage/bag/ore + name = "mining satchel" + desc = "This little bugger can be used to store and transport ores." + icon = 'icons/obj/mining.dmi' + icon_state = "satchel" + origin_tech = "engineering=2" + slot_flags = SLOT_BELT | SLOT_POCKET + w_class = WEIGHT_CLASS_NORMAL + storage_slots = 10 + max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * ore.w_class + max_w_class = WEIGHT_CLASS_NORMAL + can_hold = list(/obj/item/stack/ore) + +/obj/item/storage/bag/ore/cyborg + name = "cyborg mining satchel" + flags = NODROP + +/obj/item/storage/bag/ore/holding //miners, your messiah has arrived + name = "mining satchel of holding" + desc = "A revolution in convenience, this satchel allows for infinite ore storage. It's been outfitted with anti-malfunction safety measures." + storage_slots = INFINITY + max_combined_w_class = INFINITY + origin_tech = "bluespace=4;materials=3;engineering=3" + icon_state = "satchel_bspace" + +/obj/item/storage/bag/ore/holding/cyborg + name = "cyborg mining satchel of holding" + flags = NODROP + +// ----------------------------- +// Plant bag +// ----------------------------- + +/obj/item/storage/bag/plants + name = "plant bag" + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "plantbag" + storage_slots = 100 //the number of plant pieces it can carry. + max_combined_w_class = 100 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class + max_w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_TINY + can_hold = list(/obj/item/reagent_containers/food/snacks/grown,/obj/item/seeds,/obj/item/grown,/obj/item/reagent_containers/food/snacks/grown/ash_flora) + resistance_flags = FLAMMABLE + +/obj/item/storage/bag/plants/portaseeder + name = "portable seed extractor" + desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant." + icon_state = "portaseeder" + origin_tech = "biotech=3;engineering=2" + +/obj/item/storage/bag/plants/portaseeder/verb/dissolve_contents() + set name = "Activate Seed Extraction" + set category = "Object" + set desc = "Activate to convert your plants into plantable seeds." + + if(usr.incapacitated()) + return + for(var/obj/item/O in contents) + seedify(O, 1) + for(var/mob/M in range(1)) + if(M.s_active == src) + close(M) + + +// ----------------------------- +// Sheet Snatcher +// ----------------------------- +// Because it stacks stacks, this doesn't operate normally. +// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu + +/obj/item/storage/bag/sheetsnatcher + icon = 'icons/obj/mining.dmi' + icon_state = "sheetsnatcher" + name = "Sheet Snatcher" + desc = "A patented Nanotrasen storage system designed for any kind of mineral sheet." + + var/capacity = 300; //the number of sheets it can carry. + w_class = WEIGHT_CLASS_NORMAL + + allow_quick_empty = 1 // this function is superceded +/obj/item/storage/bag/sheetsnatcher/New() + ..() + //verbs -= /obj/item/storage/verb/quick_empty + //verbs += /obj/item/storage/bag/sheetsnatcher/quick_empty + +/obj/item/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!istype(W,/obj/item/stack/sheet) || istype(W,/obj/item/stack/sheet/mineral/sandstone) || istype(W,/obj/item/stack/sheet/wood)) + if(!stop_messages) + to_chat(usr, "The snatcher does not accept [W].") + return 0 //I don't care, but the existing code rejects them for not being "sheets" *shrug* -Sayu + var/current = 0 + for(var/obj/item/stack/sheet/S in contents) + current += S.amount + if(capacity == current)//If it's full, you're done + if(!stop_messages) + to_chat(usr, "The snatcher is full.") + return 0 + return 1 + + +// Modified handle_item_insertion. Would prefer not to, but... +/obj/item/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) + var/obj/item/stack/sheet/S = W + if(!istype(S)) return 0 + + var/amount + var/inserted = 0 + var/current = 0 + for(var/obj/item/stack/sheet/S2 in contents) + current += S2.amount + if(capacity < current + S.amount)//If the stack will fill it up + amount = capacity - current + else + amount = S.amount + + for(var/obj/item/stack/sheet/sheet in contents) + if(S.type == sheet.type) // we are violating the amount limitation because these are not sane objects + sheet.amount += amount // they should only be removed through procs in this file, which split them up. + S.amount -= amount + inserted = 1 + break + + if(!inserted || !S.amount) + usr.unEquip(S) + usr.update_icons() //update our overlays + if(usr.client && usr.s_active != src) + usr.client.screen -= S + S.dropped(usr) + if(!S.amount) + qdel(S) + else + S.loc = src + + orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + update_icon() + return 1 + + +// Sets up numbered display to show the stack size of each stored mineral +// NOTE: numbered display is turned off currently because it's broken +/obj/item/storage/bag/sheetsnatcher/orient2hud(mob/user as mob) + var/adjusted_contents = contents.len + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_contents_with_number) + numbered_contents = list() + adjusted_contents = 0 + for(var/obj/item/stack/sheet/I in contents) + adjusted_contents++ + var/datum/numbered_display/D = new/datum/numbered_display(I) + D.number = I.amount + numbered_contents.Add( D ) + + var/row_num = 0 + var/col_count = min(7,storage_slots) -1 + if(adjusted_contents > 7) + row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. + src.standard_orient_objs(row_num, col_count, numbered_contents) + return + + +// Modified quick_empty verb drops appropriate sized stacks +/obj/item/storage/bag/sheetsnatcher/quick_empty() + var/location = get_turf(src) + for(var/obj/item/stack/sheet/S in contents) + while(S.amount) + var/obj/item/stack/sheet/N = new S.type(location) + var/stacksize = min(S.amount,N.max_amount) + N.amount = stacksize + S.amount -= stacksize + if(!S.amount) + qdel(S) // todo: there's probably something missing here + orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + update_icon() + +// Instead of removing +/obj/item/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W as obj, atom/new_location) + var/obj/item/stack/sheet/S = W + if(!istype(S)) return 0 + + //I would prefer to drop a new stack, but the item/attack_hand code + // that calls this can't recieve a different object than you clicked on. + //Therefore, make a new stack internally that has the remainder. + // -Sayu + + if(S.amount > S.max_amount) + var/obj/item/stack/sheet/temp = new S.type(src) + temp.amount = S.amount - S.max_amount + S.amount = S.max_amount + + return ..(S,new_location) + +// ----------------------------- +// Sheet Snatcher (Cyborg) +// ----------------------------- + +/obj/item/storage/bag/sheetsnatcher/borg + name = "Sheet Snatcher 9000" + desc = "" + capacity = 500//Borgs get more because >specialization + + +// ----------------------------- +// Cash Bag +// ----------------------------- + +/obj/item/storage/bag/cash + icon = 'icons/obj/storage.dmi' + icon_state = "cashbag" + name = "Cash bag" + desc = "A bag for carrying lots of cash. It's got a big dollar sign printed on the front." + storage_slots = 50; //the number of cash pieces it can carry. + max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * cash.w_class + max_w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_TINY + can_hold = list(/obj/item/coin,/obj/item/stack/spacecash) + +// ----------------------------- +// Book bag +// ----------------------------- + +/obj/item/storage/bag/books + name = "book bag" + desc = "A bag for books." + icon = 'icons/obj/library.dmi' + icon_state = "bookbag" + display_contents_with_number = 0 //This would look really stupid otherwise + storage_slots = 7 + max_combined_w_class = 21 + max_w_class = WEIGHT_CLASS_NORMAL + w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics + can_hold = list(/obj/item/book, /obj/item/storage/bible, /obj/item/tome, /obj/item/spellbook) + resistance_flags = FLAMMABLE + +/* + * Trays - Agouri + */ +/obj/item/storage/bag/tray + name = "tray" + icon = 'icons/obj/food/containers.dmi' + icon_state = "tray" + desc = "A metal tray to lay food on." + force = 5 + throwforce = 10.0 + throw_speed = 3 + throw_range = 5 + w_class = WEIGHT_CLASS_BULKY + flags = CONDUCT + materials = list(MAT_METAL=3000) + +/obj/item/storage/bag/tray/attack(mob/living/M as mob, mob/living/user as mob) + ..() + // Drop all the things. All of them. + var/list/obj/item/oldContents = contents.Copy() + quick_empty() + + // Make each item scatter a bit + for(var/obj/item/I in oldContents) + spawn() + for(var/i = 1, i <= rand(1,2), i++) + if(I) + step(I, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) + + if(prob(50)) + playsound(M, 'sound/items/trayhit1.ogg', 50, 1) + else + playsound(M, 'sound/items/trayhit2.ogg', 50, 1) + + if(ishuman(M)) + if(prob(10)) + M.Weaken(2) + +/obj/item/storage/bag/tray/proc/rebuild_overlays() + overlays.Cut() + for(var/obj/item/I in contents) + overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = -1) + +/obj/item/storage/bag/tray/remove_from_storage(obj/item/W as obj, atom/new_location) + ..() + rebuild_overlays() + +/obj/item/storage/bag/tray/handle_item_insertion(obj/item/I, prevent_warning = 0) + overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = -1) + ..() + +/obj/item/storage/bag/tray/cyborg + +/obj/item/storage/bag/tray/cyborg/afterattack(atom/target, mob/user as mob) + if( isturf(target) || istype(target,/obj/structure/table) ) + var foundtable = istype(target,/obj/structure/table/) + if( !foundtable ) //it must be a turf! + for(var/obj/structure/table/T in target) + foundtable = 1 + break + + var turf/dropspot + if( !foundtable ) // don't unload things onto walls or other silly places. + dropspot = user.loc + else if( isturf(target) ) // they clicked on a turf with a table in it + dropspot = target + else // they clicked on a table + dropspot = target.loc + + overlays = null + + var droppedSomething = 0 + + for(var/obj/item/I in contents) + I.loc = dropspot + contents.Remove(I) + droppedSomething = 1 + if(!foundtable && isturf(dropspot)) + // if no table, presume that the person just shittily dropped the tray on the ground and made a mess everywhere! + spawn() + for(var/i = 1, i <= rand(1,2), i++) + if(I) + step(I, pick(NORTH,SOUTH,EAST,WEST)) + sleep(rand(2,4)) + if( droppedSomething ) + if( foundtable ) + user.visible_message("[user] unloads [user.p_their()] service tray.") + else + user.visible_message("[user] drops all the items on [user.p_their()] tray.") + + return ..() + + +/obj/item/storage/bag/tray/cookies_tray + var/cookie = /obj/item/reagent_containers/food/snacks/cookie + +/obj/item/storage/bag/tray/cookies_tray/New() /// By Azule Utama, thank you a lot! + ..() + for(var/i in 1 to 6) + var/obj/item/C = new cookie(src) + handle_item_insertion(C) // Done this way so the tray actually has the cookies visible when spawned + rebuild_overlays() + +/obj/item/storage/bag/tray/cookies_tray/sugarcookie + cookie = /obj/item/reagent_containers/food/snacks/sugarcookie + +/* + * Chemistry bag + */ + +/obj/item/storage/bag/chemistry + name = "chemistry bag" + icon = 'icons/obj/chemical.dmi' + icon_state = "bag" + desc = "A bag for storing pills, patches, and bottles." + storage_slots = 50 + max_combined_w_class = 200 + w_class = WEIGHT_CLASS_TINY + can_hold = list(/obj/item/reagent_containers/food/pill,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/glass/bottle) + resistance_flags = FLAMMABLE +/* + * Biowaste bag (mostly for xenobiologists) + */ + +/obj/item/storage/bag/bio + name = "bio bag" + icon = 'icons/obj/chemical.dmi' + icon_state = "biobag" + desc = "A bag for the safe transportation and disposal of biowaste and other biological materials." + storage_slots = 25 + max_combined_w_class = 200 + w_class = WEIGHT_CLASS_TINY + can_hold = list(/obj/item/slime_extract,/obj/item/reagent_containers/food/snacks/monkeycube,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/iv_bag,/obj/item/reagent_containers/hypospray/autoinjector) + resistance_flags = FLAMMABLE diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 0ebd553fc3b1..755b0076c3c6 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -1,793 +1,793 @@ -/obj/item/storage/belt - name = "belt" - desc = "Can hold various things." - icon = 'icons/obj/clothing/belts.dmi' - icon_state = "utilitybelt" - item_state = "utility" - lefthand_file = 'icons/mob/inhands/equipment/belt_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/belt_righthand.dmi' - slot_flags = SLOT_BELT - attack_verb = list("whipped", "lashed", "disciplined") - max_integrity = 300 - var/use_item_overlays = 0 // Do we have overlays for items held inside the belt? - -/obj/item/storage/belt/update_icon() - if(use_item_overlays) - overlays.Cut() - for(var/obj/item/I in contents) - overlays += "[I.name]" - ..() - -/obj/item/storage/belt/proc/can_use() - return is_equipped() - -/obj/item/storage/belt/MouseDrop(obj/over_object as obj, src_location, over_location) - var/mob/M = usr - if(!istype(over_object, /obj/screen)) - return ..() - playsound(src.loc, "rustle", 50, 1, -5) - if(!M.restrained() && !M.stat && can_use()) - switch(over_object.name) - if("r_hand") - M.unEquip(src) - M.put_in_r_hand(src) - if("l_hand") - M.unEquip(src) - M.put_in_l_hand(src) - src.add_fingerprint(usr) - return - -/obj/item/storage/belt/deserialize(list/data) - ..() - update_icon() - -/obj/item/storage/belt/utility - name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing. - desc = "Can hold various tools." - icon_state = "utilitybelt" - item_state = "utility" - use_item_overlays = 1 - can_hold = list( - /obj/item/crowbar, - /obj/item/screwdriver, - /obj/item/weldingtool, - /obj/item/wirecutters, - /obj/item/wrench, - /obj/item/multitool, - /obj/item/flashlight, - /obj/item/stack/cable_coil, - /obj/item/t_scanner, - /obj/item/analyzer, - /obj/item/extinguisher/mini, - /obj/item/holosign_creator) - -/obj/item/storage/belt/utility/full/New() - ..() - new /obj/item/screwdriver(src) - new /obj/item/wrench(src) - new /obj/item/weldingtool(src) - new /obj/item/crowbar(src) - new /obj/item/wirecutters(src) - new /obj/item/stack/cable_coil/random(src, 30) - update_icon() - -/obj/item/storage/belt/utility/full/multitool/New() - ..() - new /obj/item/multitool(src) - update_icon() - -/obj/item/storage/belt/utility/atmostech/New() - ..() - new /obj/item/screwdriver(src) - new /obj/item/wrench(src) - new /obj/item/weldingtool(src) - new /obj/item/crowbar(src) - new /obj/item/wirecutters(src) - new /obj/item/t_scanner(src) - new /obj/item/extinguisher/mini(src) - update_icon() - -/obj/item/storage/belt/utility/chief - name = "advanced toolbelt" - desc = "Holds tools, looks snazzy" - icon_state = "utilitybelt_ce" - item_state = "utility_ce" - -/obj/item/storage/belt/utility/chief/full/New() - ..() - new /obj/item/screwdriver/power(src) - new /obj/item/crowbar/power(src) - new /obj/item/weldingtool/experimental(src)//This can be changed if this is too much - new /obj/item/multitool(src) - new /obj/item/stack/cable_coil/random(src, 30) - new /obj/item/extinguisher/mini(src) - new /obj/item/analyzer(src) - update_icon() - //much roomier now that we've managed to remove two tools - -/obj/item/storage/belt/medical - use_to_pickup = 1 //Allow medical belt to pick up medicine - name = "medical belt" - desc = "Can hold various medical equipment." - icon_state = "medicalbelt" - item_state = "medical" - use_item_overlays = 1 - max_w_class = WEIGHT_CLASS_NORMAL - can_hold = list( - /obj/item/healthanalyzer, - /obj/item/dnainjector, - /obj/item/reagent_containers/dropper, - /obj/item/reagent_containers/glass/beaker, - /obj/item/reagent_containers/glass/bottle, - /obj/item/reagent_containers/food/pill, - /obj/item/reagent_containers/syringe, - /obj/item/lighter/zippo, - /obj/item/storage/fancy/cigarettes, - /obj/item/storage/pill_bottle, - /obj/item/stack/medical, - /obj/item/flashlight/pen, - /obj/item/clothing/mask/surgical, - /obj/item/clothing/gloves/color/latex, - /obj/item/reagent_containers/hypospray/autoinjector, - /obj/item/reagent_containers/hypospray/CMO, - /obj/item/reagent_containers/hypospray/safety, - /obj/item/rad_laser, - /obj/item/sensor_device, - /obj/item/wrench/medical, - /obj/item/handheld_defibrillator - ) - -/obj/item/storage/belt/medical/surgery - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 17 - use_to_pickup = 1 - name = "surgical belt" - desc = "Can hold various surgical tools." - storage_slots = 9 - use_item_overlays = 1 - can_hold = list( - /obj/item/scalpel, - /obj/item/hemostat, - /obj/item/retractor, - /obj/item/circular_saw, - /obj/item/bonegel, - /obj/item/bonesetter, - /obj/item/FixOVein, - /obj/item/surgicaldrill, - /obj/item/cautery, - ) - -/obj/item/storage/belt/medical/surgery/loaded - -/obj/item/storage/belt/medical/surgery/loaded/New() - ..() - new /obj/item/scalpel(src) - new /obj/item/hemostat(src) - new /obj/item/retractor(src) - new /obj/item/circular_saw(src) - new /obj/item/bonegel(src) - new /obj/item/bonesetter(src) - new /obj/item/FixOVein(src) - new /obj/item/surgicaldrill(src) - new /obj/item/cautery(src) - -/obj/item/storage/belt/medical/response_team - -/obj/item/storage/belt/medical/response_team/New() - ..() - new /obj/item/reagent_containers/food/pill/salbutamol(src) - new /obj/item/reagent_containers/food/pill/salbutamol(src) - new /obj/item/reagent_containers/food/pill/charcoal(src) - new /obj/item/reagent_containers/food/pill/charcoal(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - update_icon() - -/obj/item/storage/belt/botany - name = "botanist belt" - desc = "Can hold various botanical supplies." - icon_state = "botanybelt" - item_state = "botany" - use_item_overlays = 1 - can_hold = list( - /obj/item/plant_analyzer, - /obj/item/cultivator, - /obj/item/hatchet, - /obj/item/reagent_containers/glass/bottle, -// /obj/item/reagent_containers/syringe, -// /obj/item/reagent_containers/glass/beaker, - /obj/item/lighter/zippo, - /obj/item/storage/fancy/cigarettes, - /obj/item/shovel/spade, - /obj/item/flashlight/pen, - /obj/item/seeds, - /obj/item/wirecutters, - /obj/item/wrench, - ) - -/obj/item/storage/belt/security - name = "security belt" - desc = "Can hold security gear like handcuffs and flashes." - icon_state = "securitybelt" - item_state = "security"//Could likely use a better one. - storage_slots = 5 - max_w_class = WEIGHT_CLASS_NORMAL - use_item_overlays = 1 - can_hold = list( - /obj/item/grenade/flashbang, - /obj/item/grenade/chem_grenade/teargas, - /obj/item/reagent_containers/spray/pepper, - /obj/item/restraints/handcuffs, - /obj/item/flash, - /obj/item/clothing/glasses, - /obj/item/ammo_casing/shotgun, - /obj/item/ammo_box, - /obj/item/reagent_containers/food/snacks/donut, - /obj/item/kitchen/knife/combat, - /obj/item/melee/baton, - /obj/item/melee/classic_baton, - /obj/item/flashlight/seclite, - /obj/item/holosign_creator/security, - /obj/item/melee/classic_baton/telescopic, - /obj/item/restraints/legcuffs/bola) - -/obj/item/storage/belt/security/sec/New() - ..() - new /obj/item/flashlight/seclite(src) - update_icon() - -/obj/item/storage/belt/security/response_team/New() - ..() - new /obj/item/reagent_containers/spray/pepper(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/flash(src) - new /obj/item/melee/classic_baton/telescopic(src) - new /obj/item/grenade/flashbang(src) - update_icon() - -/obj/item/storage/belt/security/response_team_gamma/New() - ..() - new /obj/item/melee/baton/loaded(src) - new /obj/item/reagent_containers/spray/pepper(src) - new /obj/item/flash(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - update_icon() - -/obj/item/storage/belt/soulstone - name = "soul stone belt" - desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" - icon_state = "soulstonebelt" - item_state = "soulstonebelt" - storage_slots = 6 - use_item_overlays = 1 - can_hold = list( - "/obj/item/soulstone" - ) - -/obj/item/storage/belt/soulstone/full/New() - ..() - new /obj/item/soulstone(src) - new /obj/item/soulstone(src) - new /obj/item/soulstone(src) - new /obj/item/soulstone(src) - new /obj/item/soulstone(src) - new /obj/item/soulstone(src) - update_icon() - - -/obj/item/storage/belt/champion - name = "championship belt" - desc = "Proves to the world that you are the strongest!" - icon_state = "championbelt" - item_state = "champion" - materials = list(MAT_GOLD=400) - storage_slots = 1 - can_hold = list( - "/obj/item/clothing/mask/luchador" - ) - -/obj/item/storage/belt/military - name = "military belt" - desc = "A syndicate belt designed to be used by boarding parties. Its style is modelled after the hardsuits they wear." - icon_state = "militarybelt" - item_state = "military" - max_w_class = WEIGHT_CLASS_SMALL - resistance_flags = FIRE_PROOF - -/obj/item/storage/belt/military/sst - icon_state = "assaultbelt" - item_state = "assault" - -/obj/item/storage/belt/military/traitor - name = "tool-belt" - desc = "Can hold various tools. This model seems to have additional compartments." - icon_state = "utilitybelt" - item_state = "utility" - use_item_overlays = 1 // So it will still show tools in it in case sec get lazy and just glance at it. - -/obj/item/storage/belt/grenade - name = "grenadier belt" - desc = "A belt for holding grenades." - icon_state = "assaultbelt" - item_state = "assault" - storage_slots = 30 - max_combined_w_class = 60 - display_contents_with_number = 1 - can_hold = list( - /obj/item/grenade, - /obj/item/lighter, - /obj/item/reagent_containers/food/drinks/bottle/molotov - ) - -/obj/item/storage/belt/grenade/full/New() - ..() - new /obj/item/grenade/smokebomb(src) //4 - new /obj/item/grenade/smokebomb(src) - new /obj/item/grenade/smokebomb(src) - new /obj/item/grenade/smokebomb(src) - new /obj/item/grenade/empgrenade(src) //2 - new /obj/item/grenade/empgrenade(src) - new /obj/item/grenade/gluon(src) //4 - new /obj/item/grenade/gluon(src) - new /obj/item/grenade/gluon(src) - new /obj/item/grenade/gluon(src) - new /obj/item/grenade/chem_grenade/facid(src) //1 - new /obj/item/grenade/chem_grenade/saringas(src) //1 - new /obj/item/grenade/gas/plasma(src) //2 - new /obj/item/grenade/gas/plasma(src) - new /obj/item/grenade/frag(src) //10 - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/frag(src) - new /obj/item/grenade/syndieminibomb(src) //2 - new /obj/item/grenade/syndieminibomb(src) - -/obj/item/storage/belt/military/abductor - name = "agent belt" - desc = "A belt used by abductor agents." - icon = 'icons/obj/abductor.dmi' - icon_state = "belt" - item_state = "security" - -/obj/item/storage/belt/military/abductor/full/New() - ..() - new /obj/item/screwdriver/abductor(src) - new /obj/item/wrench/abductor(src) - new /obj/item/weldingtool/abductor(src) - new /obj/item/crowbar/abductor(src) - new /obj/item/wirecutters/abductor(src) - new /obj/item/multitool/abductor(src) - new /obj/item/stack/cable_coil(src, 30, COLOR_WHITE) - -/obj/item/storage/belt/military/assault - name = "assault belt" - desc = "A tactical assault belt." - icon_state = "assaultbelt" - item_state = "assault" - storage_slots = 6 - -/obj/item/storage/belt/janitor - name = "janibelt" - desc = "A belt used to hold most janitorial supplies." - icon_state = "janibelt" - item_state = "janibelt" - storage_slots = 6 - max_w_class = WEIGHT_CLASS_BULKY // Set to this so the light replacer can fit. - use_item_overlays = 1 - can_hold = list( - /obj/item/grenade/chem_grenade/cleaner, - /obj/item/lightreplacer, - /obj/item/flashlight, - /obj/item/reagent_containers/spray, - /obj/item/soap, - /obj/item/holosign_creator, - /obj/item/melee/flyswatter, - ) - -/obj/item/storage/belt/janitor/full/New() - ..() - new /obj/item/lightreplacer(src) - new /obj/item/holosign_creator(src) - new /obj/item/reagent_containers/spray/cleaner(src) - new /obj/item/soap(src) - new /obj/item/grenade/chem_grenade/cleaner(src) - new /obj/item/grenade/chem_grenade/cleaner(src) - new /obj/item/melee/flyswatter(src) - update_icon() - -/obj/item/storage/belt/lazarus - name = "trainer's belt" - desc = "For the mining master, holds your lazarus capsules." - icon_state = "lazarusbelt" - item_state = "lazbelt" - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_TINY - max_combined_w_class = 6 - storage_slots = 6 - can_hold = list(/obj/item/mobcapsule) - -/obj/item/storage/belt/lazarus/New() - ..() - update_icon() - - -/obj/item/storage/belt/lazarus/update_icon() - ..() - icon_state = "[initial(icon_state)]_[contents.len]" - -/obj/item/storage/belt/lazarus/attackby(obj/item/W, mob/user) - var/amount = contents.len - . = ..() - if(amount != contents.len) - update_icon() - -/obj/item/storage/belt/lazarus/remove_from_storage(obj/item/W as obj, atom/new_location) - ..() - update_icon() - - -/obj/item/storage/belt/bandolier - name = "bandolier" - desc = "A bandolier for holding shotgun ammunition." - icon_state = "bandolier" - item_state = "bandolier" - storage_slots = 8 - can_hold = list( - /obj/item/ammo_casing/shotgun - ) - -/obj/item/storage/belt/bandolier/New() - ..() - update_icon() - -/obj/item/storage/belt/bandolier/full/New() - ..() - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - new /obj/item/ammo_casing/shotgun/beanbag(src) - update_icon() - -/obj/item/storage/belt/bandolier/update_icon() - ..() - icon_state = "[initial(icon_state)]_[contents.len]" - -/obj/item/storage/belt/bandolier/attackby(obj/item/W, mob/user) - var/amount = contents.len - . = ..() - if(amount != contents.len) - update_icon() - -/obj/item/storage/belt/bandolier/remove_from_storage(obj/item/W as obj, atom/new_location) - ..() - update_icon() - -/obj/item/storage/belt/holster - name = "shoulder holster" - desc = "A holster to conceal a carried handgun. WARNING: Badasses only." - icon_state = "holster" - item_state = "holster" - storage_slots = 1 - max_w_class = WEIGHT_CLASS_NORMAL - can_hold = list( - /obj/item/gun/projectile/automatic/pistol, - /obj/item/gun/projectile/revolver/detective - ) - -/obj/item/storage/belt/wands - name = "wand belt" - desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic." - icon_state = "soulstonebelt" - item_state = "soulstonebelt" - storage_slots = 6 - use_item_overlays = 1 - can_hold = list( - /obj/item/gun/magic/wand - ) - -/obj/item/storage/belt/wands/full/New() - ..() - new /obj/item/gun/magic/wand/death(src) - new /obj/item/gun/magic/wand/resurrection(src) - new /obj/item/gun/magic/wand/polymorph(src) - new /obj/item/gun/magic/wand/teleport(src) - new /obj/item/gun/magic/wand/door(src) - new /obj/item/gun/magic/wand/fireball(src) - - for(var/obj/item/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition - W.max_charges = initial(W.max_charges) - W.charges = W.max_charges - update_icon() - -/obj/item/storage/belt/fannypack - name = "fannypack" - desc = "A dorky fannypack for keeping small items in." - icon_state = "fannypack_leather" - item_state = "fannypack_leather" - storage_slots = 3 - max_w_class = WEIGHT_CLASS_SMALL - -/obj/item/storage/belt/fannypack/black - name = "black fannypack" - icon_state = "fannypack_black" - item_state = "fannypack_black" - -/obj/item/storage/belt/fannypack/red - name = "red fannypack" - icon_state = "fannypack_red" - item_state = "fannypack_red" - -/obj/item/storage/belt/fannypack/purple - name = "purple fannypack" - icon_state = "fannypack_purple" - item_state = "fannypack_purple" - -/obj/item/storage/belt/fannypack/blue - name = "blue fannypack" - icon_state = "fannypack_blue" - item_state = "fannypack_blue" - -/obj/item/storage/belt/fannypack/orange - name = "orange fannypack" - icon_state = "fannypack_orange" - item_state = "fannypack_orange" - -/obj/item/storage/belt/fannypack/white - name = "white fannypack" - icon_state = "fannypack_white" - item_state = "fannypack_white" - -/obj/item/storage/belt/fannypack/green - name = "green fannypack" - icon_state = "fannypack_green" - item_state = "fannypack_green" - -/obj/item/storage/belt/fannypack/pink - name = "pink fannypack" - icon_state = "fannypack_pink" - item_state = "fannypack_pink" - -/obj/item/storage/belt/fannypack/cyan - name = "cyan fannypack" - icon_state = "fannypack_cyan" - item_state = "fannypack_cyan" - -/obj/item/storage/belt/fannypack/yellow - name = "yellow fannypack" - icon_state = "fannypack_yellow" - item_state = "fannypack_yellow" - -/obj/item/storage/belt/rapier - name = "rapier sheath" - desc = "Can hold rapiers." - icon_state = "sheath" - item_state = "sheath" - storage_slots = 1 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_BULKY - can_hold = list(/obj/item/melee/rapier) - -/obj/item/storage/belt/rapier/update_icon() - icon_state = "[initial(icon_state)]" - item_state = "[initial(item_state)]" - if(contents.len) - icon_state = "[initial(icon_state)]-rapier" - item_state = "[initial(item_state)]-rapier" - if(isliving(loc)) - var/mob/living/L = loc - L.update_inv_belt() - ..() - -/obj/item/storage/belt/rapier/New() - ..() - new /obj/item/melee/rapier(src) - update_icon() - -// ------------------------------------- -// Bluespace Belt -// ------------------------------------- - -/obj/item/storage/belt/bluespace - name = "Belt of Holding" - desc = "The greatest in pants-supporting technology." - icon_state = "holdingbelt" - item_state = "holdingbelt" - storage_slots = 14 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_SMALL - max_combined_w_class = 21 // = 14 * 1.5, not 14 * 2. This is deliberate - origin_tech = "bluespace=5;materials=4;engineering=4;plasmatech=5" - can_hold = list() - -/obj/item/storage/belt/bluespace/owlman - name = "Owlman's utility belt" - desc = "Sometimes people choose justice. Sometimes, justice chooses you..." - icon_state = "securitybelt" - item_state = "security" - storage_slots = 6 - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 18 - origin_tech = "bluespace=5;materials=4;engineering=4;plasmatech=5" - allow_quick_empty = 1 - can_hold = list( - /obj/item/grenade/smokebomb, - /obj/item/restraints/legcuffs/bola - ) - - flags = NODROP - var/smokecount = 0 - var/bolacount = 0 - var/cooldown = 0 - -/obj/item/storage/belt/bluespace/owlman/New() - ..() - new /obj/item/grenade/smokebomb(src) - new /obj/item/grenade/smokebomb(src) - new /obj/item/grenade/smokebomb(src) - new /obj/item/grenade/smokebomb(src) - new /obj/item/restraints/legcuffs/bola(src) - new /obj/item/restraints/legcuffs/bola(src) - START_PROCESSING(SSobj, src) - cooldown = world.time - -/obj/item/storage/belt/bluespace/owlman/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/storage/belt/bluespace/owlman/process() - if(cooldown < world.time - 600) - smokecount = 0 - var/obj/item/grenade/smokebomb/S - for(S in src) - smokecount++ - bolacount = 0 - var/obj/item/restraints/legcuffs/bola/B - for(B in src) - bolacount++ - if(smokecount < 4) - while(smokecount < 4) - new /obj/item/grenade/smokebomb(src) - smokecount++ - if(bolacount < 2) - while(bolacount < 2) - new /obj/item/restraints/legcuffs/bola(src) - bolacount++ - cooldown = world.time - update_icon() - orient2hud() - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.belt && H.belt == src) - if(H.s_active && H.s_active == src) - H.s_active.show_to(H) - -/obj/item/storage/belt/bluespace/attack(mob/M as mob, mob/user as mob, def_zone) - return - -/obj/item/storage/belt/bluespace/admin - name = "Admin's Tool-belt" - desc = "Holds everything for those that run everything." - icon_state = "soulstonebelt" - item_state = "soulstonebelt" - w_class = 10 // permit holding other storage items - storage_slots = 28 - max_w_class = 10 - max_combined_w_class = 280 - can_hold = list() - -/obj/item/storage/belt/bluespace/admin/New() - ..() - new /obj/item/crowbar(src) - new /obj/item/screwdriver(src) - new /obj/item/weldingtool/hugetank(src) - new /obj/item/wirecutters(src) - new /obj/item/wrench(src) - new /obj/item/multitool(src) - new /obj/item/stack/cable_coil(src) - - new /obj/item/restraints/handcuffs(src) - new /obj/item/dnainjector/xraymut(src) - new /obj/item/dnainjector/firemut(src) - new /obj/item/dnainjector/telemut(src) - new /obj/item/dnainjector/hulkmut(src) -// new /obj/item/spellbook(src) // for smoke effects, door openings, etc -// new /obj/item/magic/spellbook(src) - -// new/obj/item/reagent_containers/hypospray/admin(src) - -/obj/item/storage/belt/bluespace/sandbox - name = "Sandbox Mode Toolbelt" - desc = "Holds whatever, you can spawn your own damn stuff." - w_class = 10 // permit holding other storage items - storage_slots = 28 - max_w_class = 10 - max_combined_w_class = 280 - can_hold = list() - -/obj/item/storage/belt/bluespace/sandbox/New() - ..() - new /obj/item/crowbar(src) - new /obj/item/screwdriver(src) - new /obj/item/weldingtool/hugetank(src) - new /obj/item/wirecutters(src) - new /obj/item/wrench(src) - new /obj/item/multitool(src) - new /obj/item/stack/cable_coil(src) - - new /obj/item/analyzer(src) - new /obj/item/healthanalyzer(src) - -/obj/item/storage/belt/mining - name = "explorer's webbing" - desc = "A versatile chest rig, cherished by miners and hunters alike." - icon_state = "explorer1" - item_state = "explorer1" - storage_slots = 6 - max_w_class = WEIGHT_CLASS_BULKY - max_combined_w_class = 20 - use_item_overlays = 0 - can_hold = list( - /obj/item/crowbar, - /obj/item/screwdriver, - /obj/item/weldingtool, - /obj/item/wirecutters, - /obj/item/wrench, - /obj/item/multitool, - /obj/item/flashlight, - /obj/item/stack/cable_coil, - /obj/item/analyzer, - /obj/item/extinguisher/mini, - /obj/item/radio, - /obj/item/clothing/gloves, - /obj/item/resonator, - /obj/item/mining_scanner, - /obj/item/pickaxe, - /obj/item/shovel, - /obj/item/stack/sheet/animalhide, - /obj/item/stack/sheet/sinew, - /obj/item/stack/sheet/bone, - /obj/item/lighter, - /obj/item/storage/fancy/cigarettes, - /obj/item/reagent_containers/food/drinks/bottle, - /obj/item/stack/medical, - /obj/item/kitchen/knife, - /obj/item/reagent_containers/hypospray, - /obj/item/gps, - /obj/item/storage/bag/ore, - /obj/item/survivalcapsule, - /obj/item/t_scanner/adv_mining_scanner, - /obj/item/reagent_containers/food/pill, - /obj/item/storage/pill_bottle, - /obj/item/stack/ore, - /obj/item/reagent_containers/food/drinks, - /obj/item/organ/internal/regenerative_core, - /obj/item/wormhole_jaunter, - /obj/item/storage/bag/plants, - /obj/item/stack/marker_beacon) - -/obj/item/storage/belt/mining/vendor/Initialize(mapload) - . = ..() - new /obj/item/survivalcapsule(src) - -/obj/item/storage/belt/mining/alt - icon_state = "explorer2" - item_state = "explorer2" - -/obj/item/storage/belt/mining/primitive - name = "hunter's belt" - desc = "A versatile belt, woven from sinew." - icon_state = "ebelt" - item_state = "ebelt" - storage_slots = 5 \ No newline at end of file +/obj/item/storage/belt + name = "belt" + desc = "Can hold various things." + icon = 'icons/obj/clothing/belts.dmi' + icon_state = "utilitybelt" + item_state = "utility" + lefthand_file = 'icons/mob/inhands/equipment/belt_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/belt_righthand.dmi' + slot_flags = SLOT_BELT + attack_verb = list("whipped", "lashed", "disciplined") + max_integrity = 300 + var/use_item_overlays = 0 // Do we have overlays for items held inside the belt? + +/obj/item/storage/belt/update_icon() + if(use_item_overlays) + overlays.Cut() + for(var/obj/item/I in contents) + overlays += "[I.name]" + ..() + +/obj/item/storage/belt/proc/can_use() + return is_equipped() + +/obj/item/storage/belt/MouseDrop(obj/over_object as obj, src_location, over_location) + var/mob/M = usr + if(!istype(over_object, /obj/screen)) + return ..() + playsound(src.loc, "rustle", 50, 1, -5) + if(!M.restrained() && !M.stat && can_use()) + switch(over_object.name) + if("r_hand") + M.unEquip(src) + M.put_in_r_hand(src) + if("l_hand") + M.unEquip(src) + M.put_in_l_hand(src) + src.add_fingerprint(usr) + return + +/obj/item/storage/belt/deserialize(list/data) + ..() + update_icon() + +/obj/item/storage/belt/utility + name = "tool-belt" //Carn: utility belt is nicer, but it bamboozles the text parsing. + desc = "Can hold various tools." + icon_state = "utilitybelt" + item_state = "utility" + use_item_overlays = 1 + can_hold = list( + /obj/item/crowbar, + /obj/item/screwdriver, + /obj/item/weldingtool, + /obj/item/wirecutters, + /obj/item/wrench, + /obj/item/multitool, + /obj/item/flashlight, + /obj/item/stack/cable_coil, + /obj/item/t_scanner, + /obj/item/analyzer, + /obj/item/extinguisher/mini, + /obj/item/holosign_creator) + +/obj/item/storage/belt/utility/full/New() + ..() + new /obj/item/screwdriver(src) + new /obj/item/wrench(src) + new /obj/item/weldingtool(src) + new /obj/item/crowbar(src) + new /obj/item/wirecutters(src) + new /obj/item/stack/cable_coil/random(src, 30) + update_icon() + +/obj/item/storage/belt/utility/full/multitool/New() + ..() + new /obj/item/multitool(src) + update_icon() + +/obj/item/storage/belt/utility/atmostech/New() + ..() + new /obj/item/screwdriver(src) + new /obj/item/wrench(src) + new /obj/item/weldingtool(src) + new /obj/item/crowbar(src) + new /obj/item/wirecutters(src) + new /obj/item/t_scanner(src) + new /obj/item/extinguisher/mini(src) + update_icon() + +/obj/item/storage/belt/utility/chief + name = "advanced toolbelt" + desc = "Holds tools, looks snazzy" + icon_state = "utilitybelt_ce" + item_state = "utility_ce" + +/obj/item/storage/belt/utility/chief/full/New() + ..() + new /obj/item/screwdriver/power(src) + new /obj/item/crowbar/power(src) + new /obj/item/weldingtool/experimental(src)//This can be changed if this is too much + new /obj/item/multitool(src) + new /obj/item/stack/cable_coil/random(src, 30) + new /obj/item/extinguisher/mini(src) + new /obj/item/analyzer(src) + update_icon() + //much roomier now that we've managed to remove two tools + +/obj/item/storage/belt/medical + use_to_pickup = 1 //Allow medical belt to pick up medicine + name = "medical belt" + desc = "Can hold various medical equipment." + icon_state = "medicalbelt" + item_state = "medical" + use_item_overlays = 1 + max_w_class = WEIGHT_CLASS_NORMAL + can_hold = list( + /obj/item/healthanalyzer, + /obj/item/dnainjector, + /obj/item/reagent_containers/dropper, + /obj/item/reagent_containers/glass/beaker, + /obj/item/reagent_containers/glass/bottle, + /obj/item/reagent_containers/food/pill, + /obj/item/reagent_containers/syringe, + /obj/item/lighter/zippo, + /obj/item/storage/fancy/cigarettes, + /obj/item/storage/pill_bottle, + /obj/item/stack/medical, + /obj/item/flashlight/pen, + /obj/item/clothing/mask/surgical, + /obj/item/clothing/gloves/color/latex, + /obj/item/reagent_containers/hypospray/autoinjector, + /obj/item/reagent_containers/hypospray/CMO, + /obj/item/reagent_containers/hypospray/safety, + /obj/item/rad_laser, + /obj/item/sensor_device, + /obj/item/wrench/medical, + /obj/item/handheld_defibrillator + ) + +/obj/item/storage/belt/medical/surgery + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 17 + use_to_pickup = 1 + name = "surgical belt" + desc = "Can hold various surgical tools." + storage_slots = 9 + use_item_overlays = 1 + can_hold = list( + /obj/item/scalpel, + /obj/item/hemostat, + /obj/item/retractor, + /obj/item/circular_saw, + /obj/item/bonegel, + /obj/item/bonesetter, + /obj/item/FixOVein, + /obj/item/surgicaldrill, + /obj/item/cautery, + ) + +/obj/item/storage/belt/medical/surgery/loaded + +/obj/item/storage/belt/medical/surgery/loaded/New() + ..() + new /obj/item/scalpel(src) + new /obj/item/hemostat(src) + new /obj/item/retractor(src) + new /obj/item/circular_saw(src) + new /obj/item/bonegel(src) + new /obj/item/bonesetter(src) + new /obj/item/FixOVein(src) + new /obj/item/surgicaldrill(src) + new /obj/item/cautery(src) + +/obj/item/storage/belt/medical/response_team + +/obj/item/storage/belt/medical/response_team/New() + ..() + new /obj/item/reagent_containers/food/pill/salbutamol(src) + new /obj/item/reagent_containers/food/pill/salbutamol(src) + new /obj/item/reagent_containers/food/pill/charcoal(src) + new /obj/item/reagent_containers/food/pill/charcoal(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + update_icon() + +/obj/item/storage/belt/botany + name = "botanist belt" + desc = "Can hold various botanical supplies." + icon_state = "botanybelt" + item_state = "botany" + use_item_overlays = 1 + can_hold = list( + /obj/item/plant_analyzer, + /obj/item/cultivator, + /obj/item/hatchet, + /obj/item/reagent_containers/glass/bottle, +// /obj/item/reagent_containers/syringe, +// /obj/item/reagent_containers/glass/beaker, + /obj/item/lighter/zippo, + /obj/item/storage/fancy/cigarettes, + /obj/item/shovel/spade, + /obj/item/flashlight/pen, + /obj/item/seeds, + /obj/item/wirecutters, + /obj/item/wrench, + ) + +/obj/item/storage/belt/security + name = "security belt" + desc = "Can hold security gear like handcuffs and flashes." + icon_state = "securitybelt" + item_state = "security"//Could likely use a better one. + storage_slots = 5 + max_w_class = WEIGHT_CLASS_NORMAL + use_item_overlays = 1 + can_hold = list( + /obj/item/grenade/flashbang, + /obj/item/grenade/chem_grenade/teargas, + /obj/item/reagent_containers/spray/pepper, + /obj/item/restraints/handcuffs, + /obj/item/flash, + /obj/item/clothing/glasses, + /obj/item/ammo_casing/shotgun, + /obj/item/ammo_box, + /obj/item/reagent_containers/food/snacks/donut, + /obj/item/kitchen/knife/combat, + /obj/item/melee/baton, + /obj/item/melee/classic_baton, + /obj/item/flashlight/seclite, + /obj/item/holosign_creator/security, + /obj/item/melee/classic_baton/telescopic, + /obj/item/restraints/legcuffs/bola) + +/obj/item/storage/belt/security/sec/New() + ..() + new /obj/item/flashlight/seclite(src) + update_icon() + +/obj/item/storage/belt/security/response_team/New() + ..() + new /obj/item/reagent_containers/spray/pepper(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/flash(src) + new /obj/item/melee/classic_baton/telescopic(src) + new /obj/item/grenade/flashbang(src) + update_icon() + +/obj/item/storage/belt/security/response_team_gamma/New() + ..() + new /obj/item/melee/baton/loaded(src) + new /obj/item/reagent_containers/spray/pepper(src) + new /obj/item/flash(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + update_icon() + +/obj/item/storage/belt/soulstone + name = "soul stone belt" + desc = "Designed for ease of access to the shards during a fight, as to not let a single enemy spirit slip away" + icon_state = "soulstonebelt" + item_state = "soulstonebelt" + storage_slots = 6 + use_item_overlays = 1 + can_hold = list( + "/obj/item/soulstone" + ) + +/obj/item/storage/belt/soulstone/full/New() + ..() + new /obj/item/soulstone(src) + new /obj/item/soulstone(src) + new /obj/item/soulstone(src) + new /obj/item/soulstone(src) + new /obj/item/soulstone(src) + new /obj/item/soulstone(src) + update_icon() + + +/obj/item/storage/belt/champion + name = "championship belt" + desc = "Proves to the world that you are the strongest!" + icon_state = "championbelt" + item_state = "champion" + materials = list(MAT_GOLD=400) + storage_slots = 1 + can_hold = list( + "/obj/item/clothing/mask/luchador" + ) + +/obj/item/storage/belt/military + name = "military belt" + desc = "A syndicate belt designed to be used by boarding parties. Its style is modelled after the hardsuits they wear." + icon_state = "militarybelt" + item_state = "military" + max_w_class = WEIGHT_CLASS_SMALL + resistance_flags = FIRE_PROOF + +/obj/item/storage/belt/military/sst + icon_state = "assaultbelt" + item_state = "assault" + +/obj/item/storage/belt/military/traitor + name = "tool-belt" + desc = "Can hold various tools. This model seems to have additional compartments." + icon_state = "utilitybelt" + item_state = "utility" + use_item_overlays = 1 // So it will still show tools in it in case sec get lazy and just glance at it. + +/obj/item/storage/belt/grenade + name = "grenadier belt" + desc = "A belt for holding grenades." + icon_state = "assaultbelt" + item_state = "assault" + storage_slots = 30 + max_combined_w_class = 60 + display_contents_with_number = 1 + can_hold = list( + /obj/item/grenade, + /obj/item/lighter, + /obj/item/reagent_containers/food/drinks/bottle/molotov + ) + +/obj/item/storage/belt/grenade/full/New() + ..() + new /obj/item/grenade/smokebomb(src) //4 + new /obj/item/grenade/smokebomb(src) + new /obj/item/grenade/smokebomb(src) + new /obj/item/grenade/smokebomb(src) + new /obj/item/grenade/empgrenade(src) //2 + new /obj/item/grenade/empgrenade(src) + new /obj/item/grenade/gluon(src) //4 + new /obj/item/grenade/gluon(src) + new /obj/item/grenade/gluon(src) + new /obj/item/grenade/gluon(src) + new /obj/item/grenade/chem_grenade/facid(src) //1 + new /obj/item/grenade/chem_grenade/saringas(src) //1 + new /obj/item/grenade/gas/plasma(src) //2 + new /obj/item/grenade/gas/plasma(src) + new /obj/item/grenade/frag(src) //10 + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/frag(src) + new /obj/item/grenade/syndieminibomb(src) //2 + new /obj/item/grenade/syndieminibomb(src) + +/obj/item/storage/belt/military/abductor + name = "agent belt" + desc = "A belt used by abductor agents." + icon = 'icons/obj/abductor.dmi' + icon_state = "belt" + item_state = "security" + +/obj/item/storage/belt/military/abductor/full/New() + ..() + new /obj/item/screwdriver/abductor(src) + new /obj/item/wrench/abductor(src) + new /obj/item/weldingtool/abductor(src) + new /obj/item/crowbar/abductor(src) + new /obj/item/wirecutters/abductor(src) + new /obj/item/multitool/abductor(src) + new /obj/item/stack/cable_coil(src, 30, COLOR_WHITE) + +/obj/item/storage/belt/military/assault + name = "assault belt" + desc = "A tactical assault belt." + icon_state = "assaultbelt" + item_state = "assault" + storage_slots = 6 + +/obj/item/storage/belt/janitor + name = "janibelt" + desc = "A belt used to hold most janitorial supplies." + icon_state = "janibelt" + item_state = "janibelt" + storage_slots = 6 + max_w_class = WEIGHT_CLASS_BULKY // Set to this so the light replacer can fit. + use_item_overlays = 1 + can_hold = list( + /obj/item/grenade/chem_grenade/cleaner, + /obj/item/lightreplacer, + /obj/item/flashlight, + /obj/item/reagent_containers/spray, + /obj/item/soap, + /obj/item/holosign_creator, + /obj/item/melee/flyswatter, + ) + +/obj/item/storage/belt/janitor/full/New() + ..() + new /obj/item/lightreplacer(src) + new /obj/item/holosign_creator(src) + new /obj/item/reagent_containers/spray/cleaner(src) + new /obj/item/soap(src) + new /obj/item/grenade/chem_grenade/cleaner(src) + new /obj/item/grenade/chem_grenade/cleaner(src) + new /obj/item/melee/flyswatter(src) + update_icon() + +/obj/item/storage/belt/lazarus + name = "trainer's belt" + desc = "For the mining master, holds your lazarus capsules." + icon_state = "lazarusbelt" + item_state = "lazbelt" + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_TINY + max_combined_w_class = 6 + storage_slots = 6 + can_hold = list(/obj/item/mobcapsule) + +/obj/item/storage/belt/lazarus/New() + ..() + update_icon() + + +/obj/item/storage/belt/lazarus/update_icon() + ..() + icon_state = "[initial(icon_state)]_[contents.len]" + +/obj/item/storage/belt/lazarus/attackby(obj/item/W, mob/user) + var/amount = contents.len + . = ..() + if(amount != contents.len) + update_icon() + +/obj/item/storage/belt/lazarus/remove_from_storage(obj/item/W as obj, atom/new_location) + ..() + update_icon() + + +/obj/item/storage/belt/bandolier + name = "bandolier" + desc = "A bandolier for holding shotgun ammunition." + icon_state = "bandolier" + item_state = "bandolier" + storage_slots = 8 + can_hold = list( + /obj/item/ammo_casing/shotgun + ) + +/obj/item/storage/belt/bandolier/New() + ..() + update_icon() + +/obj/item/storage/belt/bandolier/full/New() + ..() + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + new /obj/item/ammo_casing/shotgun/beanbag(src) + update_icon() + +/obj/item/storage/belt/bandolier/update_icon() + ..() + icon_state = "[initial(icon_state)]_[contents.len]" + +/obj/item/storage/belt/bandolier/attackby(obj/item/W, mob/user) + var/amount = contents.len + . = ..() + if(amount != contents.len) + update_icon() + +/obj/item/storage/belt/bandolier/remove_from_storage(obj/item/W as obj, atom/new_location) + ..() + update_icon() + +/obj/item/storage/belt/holster + name = "shoulder holster" + desc = "A holster to conceal a carried handgun. WARNING: Badasses only." + icon_state = "holster" + item_state = "holster" + storage_slots = 1 + max_w_class = WEIGHT_CLASS_NORMAL + can_hold = list( + /obj/item/gun/projectile/automatic/pistol, + /obj/item/gun/projectile/revolver/detective + ) + +/obj/item/storage/belt/wands + name = "wand belt" + desc = "A belt designed to hold various rods of power. A veritable fanny pack of exotic magic." + icon_state = "soulstonebelt" + item_state = "soulstonebelt" + storage_slots = 6 + use_item_overlays = 1 + can_hold = list( + /obj/item/gun/magic/wand + ) + +/obj/item/storage/belt/wands/full/New() + ..() + new /obj/item/gun/magic/wand/death(src) + new /obj/item/gun/magic/wand/resurrection(src) + new /obj/item/gun/magic/wand/polymorph(src) + new /obj/item/gun/magic/wand/teleport(src) + new /obj/item/gun/magic/wand/door(src) + new /obj/item/gun/magic/wand/fireball(src) + + for(var/obj/item/gun/magic/wand/W in contents) //All wands in this pack come in the best possible condition + W.max_charges = initial(W.max_charges) + W.charges = W.max_charges + update_icon() + +/obj/item/storage/belt/fannypack + name = "fannypack" + desc = "A dorky fannypack for keeping small items in." + icon_state = "fannypack_leather" + item_state = "fannypack_leather" + storage_slots = 3 + max_w_class = WEIGHT_CLASS_SMALL + +/obj/item/storage/belt/fannypack/black + name = "black fannypack" + icon_state = "fannypack_black" + item_state = "fannypack_black" + +/obj/item/storage/belt/fannypack/red + name = "red fannypack" + icon_state = "fannypack_red" + item_state = "fannypack_red" + +/obj/item/storage/belt/fannypack/purple + name = "purple fannypack" + icon_state = "fannypack_purple" + item_state = "fannypack_purple" + +/obj/item/storage/belt/fannypack/blue + name = "blue fannypack" + icon_state = "fannypack_blue" + item_state = "fannypack_blue" + +/obj/item/storage/belt/fannypack/orange + name = "orange fannypack" + icon_state = "fannypack_orange" + item_state = "fannypack_orange" + +/obj/item/storage/belt/fannypack/white + name = "white fannypack" + icon_state = "fannypack_white" + item_state = "fannypack_white" + +/obj/item/storage/belt/fannypack/green + name = "green fannypack" + icon_state = "fannypack_green" + item_state = "fannypack_green" + +/obj/item/storage/belt/fannypack/pink + name = "pink fannypack" + icon_state = "fannypack_pink" + item_state = "fannypack_pink" + +/obj/item/storage/belt/fannypack/cyan + name = "cyan fannypack" + icon_state = "fannypack_cyan" + item_state = "fannypack_cyan" + +/obj/item/storage/belt/fannypack/yellow + name = "yellow fannypack" + icon_state = "fannypack_yellow" + item_state = "fannypack_yellow" + +/obj/item/storage/belt/rapier + name = "rapier sheath" + desc = "Can hold rapiers." + icon_state = "sheath" + item_state = "sheath" + storage_slots = 1 + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_BULKY + can_hold = list(/obj/item/melee/rapier) + +/obj/item/storage/belt/rapier/update_icon() + icon_state = "[initial(icon_state)]" + item_state = "[initial(item_state)]" + if(contents.len) + icon_state = "[initial(icon_state)]-rapier" + item_state = "[initial(item_state)]-rapier" + if(isliving(loc)) + var/mob/living/L = loc + L.update_inv_belt() + ..() + +/obj/item/storage/belt/rapier/New() + ..() + new /obj/item/melee/rapier(src) + update_icon() + +// ------------------------------------- +// Bluespace Belt +// ------------------------------------- + +/obj/item/storage/belt/bluespace + name = "Belt of Holding" + desc = "The greatest in pants-supporting technology." + icon_state = "holdingbelt" + item_state = "holdingbelt" + storage_slots = 14 + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_SMALL + max_combined_w_class = 21 // = 14 * 1.5, not 14 * 2. This is deliberate + origin_tech = "bluespace=5;materials=4;engineering=4;plasmatech=5" + can_hold = list() + +/obj/item/storage/belt/bluespace/owlman + name = "Owlman's utility belt" + desc = "Sometimes people choose justice. Sometimes, justice chooses you..." + icon_state = "securitybelt" + item_state = "security" + storage_slots = 6 + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 18 + origin_tech = "bluespace=5;materials=4;engineering=4;plasmatech=5" + allow_quick_empty = 1 + can_hold = list( + /obj/item/grenade/smokebomb, + /obj/item/restraints/legcuffs/bola + ) + + flags = NODROP + var/smokecount = 0 + var/bolacount = 0 + var/cooldown = 0 + +/obj/item/storage/belt/bluespace/owlman/New() + ..() + new /obj/item/grenade/smokebomb(src) + new /obj/item/grenade/smokebomb(src) + new /obj/item/grenade/smokebomb(src) + new /obj/item/grenade/smokebomb(src) + new /obj/item/restraints/legcuffs/bola(src) + new /obj/item/restraints/legcuffs/bola(src) + START_PROCESSING(SSobj, src) + cooldown = world.time + +/obj/item/storage/belt/bluespace/owlman/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/storage/belt/bluespace/owlman/process() + if(cooldown < world.time - 600) + smokecount = 0 + var/obj/item/grenade/smokebomb/S + for(S in src) + smokecount++ + bolacount = 0 + var/obj/item/restraints/legcuffs/bola/B + for(B in src) + bolacount++ + if(smokecount < 4) + while(smokecount < 4) + new /obj/item/grenade/smokebomb(src) + smokecount++ + if(bolacount < 2) + while(bolacount < 2) + new /obj/item/restraints/legcuffs/bola(src) + bolacount++ + cooldown = world.time + update_icon() + orient2hud() + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.belt && H.belt == src) + if(H.s_active && H.s_active == src) + H.s_active.show_to(H) + +/obj/item/storage/belt/bluespace/attack(mob/M as mob, mob/user as mob, def_zone) + return + +/obj/item/storage/belt/bluespace/admin + name = "Admin's Tool-belt" + desc = "Holds everything for those that run everything." + icon_state = "soulstonebelt" + item_state = "soulstonebelt" + w_class = 10 // permit holding other storage items + storage_slots = 28 + max_w_class = 10 + max_combined_w_class = 280 + can_hold = list() + +/obj/item/storage/belt/bluespace/admin/New() + ..() + new /obj/item/crowbar(src) + new /obj/item/screwdriver(src) + new /obj/item/weldingtool/hugetank(src) + new /obj/item/wirecutters(src) + new /obj/item/wrench(src) + new /obj/item/multitool(src) + new /obj/item/stack/cable_coil(src) + + new /obj/item/restraints/handcuffs(src) + new /obj/item/dnainjector/xraymut(src) + new /obj/item/dnainjector/firemut(src) + new /obj/item/dnainjector/telemut(src) + new /obj/item/dnainjector/hulkmut(src) +// new /obj/item/spellbook(src) // for smoke effects, door openings, etc +// new /obj/item/magic/spellbook(src) + +// new/obj/item/reagent_containers/hypospray/admin(src) + +/obj/item/storage/belt/bluespace/sandbox + name = "Sandbox Mode Toolbelt" + desc = "Holds whatever, you can spawn your own damn stuff." + w_class = 10 // permit holding other storage items + storage_slots = 28 + max_w_class = 10 + max_combined_w_class = 280 + can_hold = list() + +/obj/item/storage/belt/bluespace/sandbox/New() + ..() + new /obj/item/crowbar(src) + new /obj/item/screwdriver(src) + new /obj/item/weldingtool/hugetank(src) + new /obj/item/wirecutters(src) + new /obj/item/wrench(src) + new /obj/item/multitool(src) + new /obj/item/stack/cable_coil(src) + + new /obj/item/analyzer(src) + new /obj/item/healthanalyzer(src) + +/obj/item/storage/belt/mining + name = "explorer's webbing" + desc = "A versatile chest rig, cherished by miners and hunters alike." + icon_state = "explorer1" + item_state = "explorer1" + storage_slots = 6 + max_w_class = WEIGHT_CLASS_BULKY + max_combined_w_class = 20 + use_item_overlays = 0 + can_hold = list( + /obj/item/crowbar, + /obj/item/screwdriver, + /obj/item/weldingtool, + /obj/item/wirecutters, + /obj/item/wrench, + /obj/item/multitool, + /obj/item/flashlight, + /obj/item/stack/cable_coil, + /obj/item/analyzer, + /obj/item/extinguisher/mini, + /obj/item/radio, + /obj/item/clothing/gloves, + /obj/item/resonator, + /obj/item/mining_scanner, + /obj/item/pickaxe, + /obj/item/shovel, + /obj/item/stack/sheet/animalhide, + /obj/item/stack/sheet/sinew, + /obj/item/stack/sheet/bone, + /obj/item/lighter, + /obj/item/storage/fancy/cigarettes, + /obj/item/reagent_containers/food/drinks/bottle, + /obj/item/stack/medical, + /obj/item/kitchen/knife, + /obj/item/reagent_containers/hypospray, + /obj/item/gps, + /obj/item/storage/bag/ore, + /obj/item/survivalcapsule, + /obj/item/t_scanner/adv_mining_scanner, + /obj/item/reagent_containers/food/pill, + /obj/item/storage/pill_bottle, + /obj/item/stack/ore, + /obj/item/reagent_containers/food/drinks, + /obj/item/organ/internal/regenerative_core, + /obj/item/wormhole_jaunter, + /obj/item/storage/bag/plants, + /obj/item/stack/marker_beacon) + +/obj/item/storage/belt/mining/vendor/Initialize(mapload) + . = ..() + new /obj/item/survivalcapsule(src) + +/obj/item/storage/belt/mining/alt + icon_state = "explorer2" + item_state = "explorer2" + +/obj/item/storage/belt/mining/primitive + name = "hunter's belt" + desc = "A versatile belt, woven from sinew." + icon_state = "ebelt" + item_state = "ebelt" + storage_slots = 5 diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm index a79674d3217a..c4afed4b266f 100644 --- a/code/game/objects/items/weapons/storage/bible.dm +++ b/code/game/objects/items/weapons/storage/bible.dm @@ -1,114 +1,114 @@ -/obj/item/storage/bible - name = "bible" - desc = "Apply to head repeatedly." - icon_state ="bible" - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_NORMAL - resistance_flags = FIRE_PROOF - var/mob/affecting = null - var/deity_name = "Christ" - -/obj/item/storage/bible/suicide_act(mob/user) - to_chat(viewers(user), "[user] stares into [src.name] and attempts to transcend understanding of the universe!") - user.dust() - return OBLITERATION - -/obj/item/storage/bible/fart_act(mob/living/M) - if(QDELETED(M) || M.stat == DEAD) - return - M.visible_message("[M] farts on \the [name]!") - M.visible_message("A mysterious force smites [M]!") - M.suiciding = TRUE - do_sparks(3, 1, M) - M.gib() - return TRUE // Don't run the fart emote - -/obj/item/storage/bible/booze - name = "bible" - desc = "To be applied to the head repeatedly." - icon_state ="bible" - -/obj/item/storage/bible/booze/New() - ..() - new /obj/item/reagent_containers/food/drinks/cans/beer(src) - new /obj/item/reagent_containers/food/drinks/cans/beer(src) - new /obj/item/stack/spacecash(src) - new /obj/item/stack/spacecash(src) - new /obj/item/stack/spacecash(src) -//BS12 EDIT - // All cult functionality moved to Null Rod -/obj/item/storage/bible/proc/bless(mob/living/carbon/M as mob) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/heal_amt = 10 - for(var/obj/item/organ/external/affecting in H.bodyparts) - if(affecting.heal_damage(heal_amt, heal_amt)) - H.UpdateDamageIcon() - return - -/obj/item/storage/bible/attack(mob/living/M as mob, mob/living/user as mob) - add_attack_logs(user, M, "Hit with [src]") - if(!iscarbon(user)) - M.LAssailant = null - else - M.LAssailant = user - - if(!(istype(user, /mob/living/carbon/human) || SSticker) && SSticker.mode.name != "monkey") - to_chat(user, "You don't have the dexterity to do this!") - return - if(!user.mind || !user.mind.isholy) - to_chat(user, "The book sizzles in your hands.") - user.take_organ_damage(0,10) - return - - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "The [src] slips out of your hand and hits your head.") - user.take_organ_damage(10) - user.Paralyse(20) - return - - if(M.stat !=2) - if((istype(M, /mob/living/carbon/human) && prob(60))) - bless(M) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] heals [] with the power of [src.deity_name]!", user, M), 1) - to_chat(M, "May the power of [src.deity_name] compel you to be healed!") - playsound(src.loc, "punch", 25, 1, -1) - else - if(ishuman(M) && !istype(M:head, /obj/item/clothing/head/helmet)) - M.adjustBrainLoss(10) - to_chat(M, "You feel dumber.") - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] beats [] over the head with []!", user, M, src), 1) - playsound(src.loc, "punch", 25, 1, -1) - else if(M.stat == 2) - for(var/mob/O in viewers(M, null)) - O.show_message(text("[] smacks []'s lifeless corpse with [].", user, M, src), 1) - playsound(src.loc, "punch", 25, 1, -1) - return - -/obj/item/storage/bible/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) - return - if(istype(A, /turf/simulated/floor)) - to_chat(user, "You hit the floor with the bible.") - if(user.mind && (user.mind.isholy)) - for(var/obj/effect/rune/R in A) - if(R.invisibility) - R.talismanreveal() - if(user.mind && (user.mind.isholy)) - if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder - to_chat(user, "You bless [A].") - var/water2holy = A.reagents.get_reagent_amount("water") - A.reagents.del_reagent("water") - A.reagents.add_reagent("holywater",water2holy) - if(A.reagents && A.reagents.has_reagent("unholywater")) //yeah yeah, copy pasted code - sue me - to_chat(user, "You purify [A].") - var/unholy2clean = A.reagents.get_reagent_amount("unholywater") - A.reagents.del_reagent("unholywater") - A.reagents.add_reagent("holywater",unholy2clean) - -/obj/item/storage/bible/attackby(obj/item/W as obj, mob/user as mob, params) - playsound(src.loc, "rustle", 50, 1, -5) - ..() +/obj/item/storage/bible + name = "bible" + desc = "Apply to head repeatedly." + icon_state ="bible" + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_NORMAL + resistance_flags = FIRE_PROOF + var/mob/affecting = null + var/deity_name = "Christ" + +/obj/item/storage/bible/suicide_act(mob/user) + to_chat(viewers(user), "[user] stares into [src.name] and attempts to transcend understanding of the universe!") + user.dust() + return OBLITERATION + +/obj/item/storage/bible/fart_act(mob/living/M) + if(QDELETED(M) || M.stat == DEAD) + return + M.visible_message("[M] farts on \the [name]!") + M.visible_message("A mysterious force smites [M]!") + M.suiciding = TRUE + do_sparks(3, 1, M) + M.gib() + return TRUE // Don't run the fart emote + +/obj/item/storage/bible/booze + name = "bible" + desc = "To be applied to the head repeatedly." + icon_state ="bible" + +/obj/item/storage/bible/booze/New() + ..() + new /obj/item/reagent_containers/food/drinks/cans/beer(src) + new /obj/item/reagent_containers/food/drinks/cans/beer(src) + new /obj/item/stack/spacecash(src) + new /obj/item/stack/spacecash(src) + new /obj/item/stack/spacecash(src) +//BS12 EDIT + // All cult functionality moved to Null Rod +/obj/item/storage/bible/proc/bless(mob/living/carbon/M as mob) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/heal_amt = 10 + for(var/obj/item/organ/external/affecting in H.bodyparts) + if(affecting.heal_damage(heal_amt, heal_amt)) + H.UpdateDamageIcon() + return + +/obj/item/storage/bible/attack(mob/living/M as mob, mob/living/user as mob) + add_attack_logs(user, M, "Hit with [src]") + if(!iscarbon(user)) + M.LAssailant = null + else + M.LAssailant = user + + if(!(istype(user, /mob/living/carbon/human) || SSticker) && SSticker.mode.name != "monkey") + to_chat(user, "You don't have the dexterity to do this!") + return + if(!user.mind || !user.mind.isholy) + to_chat(user, "The book sizzles in your hands.") + user.take_organ_damage(0,10) + return + + if((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "The [src] slips out of your hand and hits your head.") + user.take_organ_damage(10) + user.Paralyse(20) + return + + if(M.stat !=2) + if((istype(M, /mob/living/carbon/human) && prob(60))) + bless(M) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] heals [] with the power of [src.deity_name]!", user, M), 1) + to_chat(M, "May the power of [src.deity_name] compel you to be healed!") + playsound(src.loc, "punch", 25, 1, -1) + else + if(ishuman(M) && !istype(M:head, /obj/item/clothing/head/helmet)) + M.adjustBrainLoss(10) + to_chat(M, "You feel dumber.") + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] beats [] over the head with []!", user, M, src), 1) + playsound(src.loc, "punch", 25, 1, -1) + else if(M.stat == 2) + for(var/mob/O in viewers(M, null)) + O.show_message(text("[] smacks []'s lifeless corpse with [].", user, M, src), 1) + playsound(src.loc, "punch", 25, 1, -1) + return + +/obj/item/storage/bible/afterattack(atom/A, mob/user as mob, proximity) + if(!proximity) + return + if(istype(A, /turf/simulated/floor)) + to_chat(user, "You hit the floor with the bible.") + if(user.mind && (user.mind.isholy)) + for(var/obj/effect/rune/R in A) + if(R.invisibility) + R.talismanreveal() + if(user.mind && (user.mind.isholy)) + if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder + to_chat(user, "You bless [A].") + var/water2holy = A.reagents.get_reagent_amount("water") + A.reagents.del_reagent("water") + A.reagents.add_reagent("holywater",water2holy) + if(A.reagents && A.reagents.has_reagent("unholywater")) //yeah yeah, copy pasted code - sue me + to_chat(user, "You purify [A].") + var/unholy2clean = A.reagents.get_reagent_amount("unholywater") + A.reagents.del_reagent("unholywater") + A.reagents.add_reagent("holywater",unholy2clean) + +/obj/item/storage/bible/attackby(obj/item/W as obj, mob/user as mob, params) + playsound(src.loc, "rustle", 50, 1, -5) + ..() diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm index 93c1a9f0d0ac..97a99491e0af 100644 --- a/code/game/objects/items/weapons/storage/boxes.dm +++ b/code/game/objects/items/weapons/storage/boxes.dm @@ -1,1130 +1,1131 @@ -/* - * Everything derived from the common cardboard box. - * Basically everything except the original is a kit (starts full). - * - * Contains: - * Empty box, starter boxes (survival/engineer), - * Latex glove and sterile mask boxes, - * Syringe, beaker, dna injector boxes, - * Blanks, flashbangs, and EMP grenade boxes, - * Tracking and chemical implant boxes, - * Prescription glasses and drinking glass boxes, - * Condiment bottle and silly cup boxes, - * Donkpocket and monkeycube boxes, - * ID and security PDA cart boxes, - * Handcuff, mousetrap, and pillbottle boxes, - * Snap-pops and matchboxes, - * Replacement light boxes. - * - * For syndicate call-ins see uplink_kits.dm - */ - -/obj/item/storage/box - name = "box" - desc = "It's just an ordinary box." - icon_state = "box" - item_state = "syringe_kit" - resistance_flags = FLAMMABLE - var/foldable = /obj/item/stack/sheet/cardboard - var/amt = 1 - -/obj/item/storage/box/attack_self(mob/user) - ..() - - if(!foldable) - return - if(contents.len) - to_chat(user, "You can't fold this box with items still inside!") - return - if(!ispath(foldable)) - return - - // Close any open UI windows first - var/found = 0 - for(var/mob/M in range(1)) - if(M.s_active == src) - close(M) - if(M == user) - found = 1 - if(!found) // User is too far away - return - - to_chat(user, "You fold [src] flat.") - var/obj/item/stack/I = new foldable(get_turf(src), amt) - user.put_in_hands(I) - qdel(src) - -/obj/item/storage/box/large - name = "large box" - desc = "You could build a fort with this." - icon_state = "largebox" - w_class = 4 // Big, bulky. - foldable = /obj/item/stack/sheet/cardboard - amt = 4 - storage_slots = 21 - max_combined_w_class = 42 // 21*2 - -/obj/item/storage/box/survival - icon_state = "box_civ" - New() - ..() - contents = list() - new /obj/item/clothing/mask/breath( src ) - new /obj/item/tank/emergency_oxygen( src ) - new /obj/item/reagent_containers/hypospray/autoinjector( src ) - new /obj/item/flashlight/flare/glowstick/emergency( src ) - return - -/obj/item/storage/box/survival_vox - icon_state = "box_vox" - -/obj/item/storage/box/survival_vox/New() - ..() - contents = list() - new /obj/item/clothing/mask/breath/vox(src) - new /obj/item/tank/emergency_oxygen/nitrogen(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - new /obj/item/flashlight/flare/glowstick/emergency(src) - -/obj/item/storage/box/survival_plasmaman - icon_state = "box_plasma" - -/obj/item/storage/box/survival_plasmaman/New() - ..() - contents = list() - new /obj/item/clothing/mask/breath(src) - new /obj/item/tank/emergency_oxygen/plasma(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - new /obj/item/flashlight/flare/glowstick/emergency(src) - -/obj/item/storage/box/engineer - icon_state = "box_eng" - New() - ..() - contents = list() - new /obj/item/clothing/mask/breath( src ) - new /obj/item/tank/emergency_oxygen/engi( src ) - new /obj/item/reagent_containers/hypospray/autoinjector( src ) - new /obj/item/flashlight/flare/glowstick/emergency( src ) - return - -/obj/item/storage/box/survival_mining - icon_state = "box_min" - New() - ..() - contents = list() - new /obj/item/clothing/mask/gas/explorer(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/crowbar/red(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - new /obj/item/flashlight/flare/glowstick/emergency(src) - -/obj/item/storage/box/survival_syndi - icon_state = "box_syndi" - New() - ..() - contents = list() - new /obj/item/clothing/mask/gas/syndicate(src) - new /obj/item/tank/emergency_oxygen/syndi(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - new /obj/item/reagent_containers/food/pill/initropidril(src) - new /obj/item/flashlight/flare/glowstick/red(src) - -/obj/item/storage/box/gloves - name = "box of latex gloves" - desc = "Contains white gloves." - icon_state = "latex" - - New() - ..() - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - -/obj/item/storage/box/masks - name = "sterile masks" - desc = "This box contains masks of sterility." - icon_state = "sterile" - - New() - ..() - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - - -/obj/item/storage/box/syringes - name = "syringes" - desc = "A box full of syringes." - desc = "A biohazard alert warning is printed on the box" - icon_state = "syringe" - - New() - ..() - new /obj/item/reagent_containers/syringe( src ) - new /obj/item/reagent_containers/syringe( src ) - new /obj/item/reagent_containers/syringe( src ) - new /obj/item/reagent_containers/syringe( src ) - new /obj/item/reagent_containers/syringe( src ) - new /obj/item/reagent_containers/syringe( src ) - new /obj/item/reagent_containers/syringe( src ) - -/obj/item/storage/box/beakers - name = "beaker box" - icon_state = "beaker" - - New() - ..() - new /obj/item/reagent_containers/glass/beaker( src ) - new /obj/item/reagent_containers/glass/beaker( src ) - new /obj/item/reagent_containers/glass/beaker( src ) - new /obj/item/reagent_containers/glass/beaker( src ) - new /obj/item/reagent_containers/glass/beaker( src ) - new /obj/item/reagent_containers/glass/beaker( src ) - new /obj/item/reagent_containers/glass/beaker( src ) - -/obj/item/storage/box/beakers/bluespace - name = "box of bluespace beakers" - icon_state = "beaker" - -/obj/item/storage/box/beakers/bluespace/New() - ..() - for(var/i in 1 to 7) - new /obj/item/reagent_containers/glass/beaker/bluespace(src) - -/obj/item/storage/box/iv_bags - name = "IV Bags" - desc = "A box full of empty IV bags." - icon_state = "beaker" - -/obj/item/storage/box/iv_bags/New() - ..() - new /obj/item/reagent_containers/iv_bag( src ) - new /obj/item/reagent_containers/iv_bag( src ) - new /obj/item/reagent_containers/iv_bag( src ) - new /obj/item/reagent_containers/iv_bag( src ) - new /obj/item/reagent_containers/iv_bag( src ) - new /obj/item/reagent_containers/iv_bag( src ) - new /obj/item/reagent_containers/iv_bag( src ) - -/obj/item/storage/box/injectors - name = "\improper DNA injectors" - desc = "This box contains injectors it seems." - - New() - ..() - new /obj/item/dnainjector/h2m(src) - new /obj/item/dnainjector/h2m(src) - new /obj/item/dnainjector/h2m(src) - new /obj/item/dnainjector/m2h(src) - new /obj/item/dnainjector/m2h(src) - new /obj/item/dnainjector/m2h(src) - -/obj/item/storage/box/slug - name = "Ammunition Box (Slug)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "slugbox" - -/obj/item/storage/box/slug/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun(src) - - -/obj/item/storage/box/buck - name = "Ammunition Box (Buckshot)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "buckshotbox" - -/obj/item/storage/box/buck/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun/buckshot(src) - -/obj/item/storage/box/dragonsbreath - name = "Ammunition Box (Dragonsbreath)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "dragonsbreathbox" - -/obj/item/storage/box/dragonsbreath/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath(src) - -/obj/item/storage/box/stun - name = "Ammunition Box (Stun shells)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "stunbox" - -/obj/item/storage/box/stun/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun/stunslug(src) - -/obj/item/storage/box/beanbag - name = "Ammunition Box (Beanbag shells)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "beanbagbox" - -/obj/item/storage/box/beanbag/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun/beanbag(src) - -/obj/item/storage/box/rubbershot - name = "Ammunition Box (Rubbershot shells)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "rubbershotbox" - -/obj/item/storage/box/rubbershot/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun/rubbershot(src) - -/obj/item/storage/box/tranquilizer - name = "Ammunition Box (Tranquilizer darts)" - desc = "A small box capable of holding seven shotgun shells." - icon_state = "tranqbox" - -/obj/item/storage/box/tranquilizer/New() - ..() - for(var/i in 1 to 7) - new /obj/item/ammo_casing/shotgun/tranquilizer(src) - -/obj/item/storage/box/flashbangs - name = "box of flashbangs (WARNING)" - desc = "WARNING: These devices are extremely dangerous and can cause blindness or deafness in repeated use." - icon_state = "flashbang" - - New() - ..() - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/grenade/flashbang(src) - -/obj/item/storage/box/flashes - name = "box of flashbulbs" - desc = "WARNING: Flashes can cause serious eye damage, protective eyewear is required." - icon_state = "flashbang" - - New() - ..() - new /obj/item/flash(src) - new /obj/item/flash(src) - new /obj/item/flash(src) - new /obj/item/flash(src) - new /obj/item/flash(src) - new /obj/item/flash(src) - -/obj/item/storage/box/teargas - name = "box of tear gas grenades (WARNING)" - desc = "WARNING: These devices are extremely dangerous and can cause blindness and skin irritation." - icon_state = "flashbang" - -/obj/item/storage/box/teargas/New() - ..() - new /obj/item/grenade/chem_grenade/teargas(src) - new /obj/item/grenade/chem_grenade/teargas(src) - new /obj/item/grenade/chem_grenade/teargas(src) - new /obj/item/grenade/chem_grenade/teargas(src) - new /obj/item/grenade/chem_grenade/teargas(src) - new /obj/item/grenade/chem_grenade/teargas(src) - new /obj/item/grenade/chem_grenade/teargas(src) - -/obj/item/storage/box/emps - name = "emp grenades" - desc = "A box with 5 emp grenades." - icon_state = "flashbang" - - New() - ..() - new /obj/item/grenade/empgrenade(src) - new /obj/item/grenade/empgrenade(src) - new /obj/item/grenade/empgrenade(src) - new /obj/item/grenade/empgrenade(src) - new /obj/item/grenade/empgrenade(src) - - -/obj/item/storage/box/trackimp - name = "tracking implant kit" - desc = "Box full of scum-bag tracking utensils." - icon_state = "implant" - - New() - ..() - new /obj/item/implantcase/tracking(src) - new /obj/item/implantcase/tracking(src) - new /obj/item/implantcase/tracking(src) - new /obj/item/implantcase/tracking(src) - new /obj/item/implanter(src) - new /obj/item/implantpad(src) - new /obj/item/locator(src) - -/obj/item/storage/box/minertracker - name = "boxed tracking implant kit" - desc = "For finding those who have died on the accursed lavaworld." - icon_state = "implant" - -/obj/item/storage/box/minertracker/New() - ..() - new /obj/item/implantcase/tracking(src) - new /obj/item/implantcase/tracking(src) - new /obj/item/implantcase/tracking(src) - new /obj/item/implanter(src) - new /obj/item/implantpad(src) - new /obj/item/locator(src) - -/obj/item/storage/box/chemimp - name = "chemical implant kit" - desc = "Box of stuff used to implant chemicals." - icon_state = "implant" - - New() - ..() - new /obj/item/implantcase/chem(src) - new /obj/item/implantcase/chem(src) - new /obj/item/implantcase/chem(src) - new /obj/item/implantcase/chem(src) - new /obj/item/implantcase/chem(src) - new /obj/item/implanter(src) - new /obj/item/implantpad(src) - -/obj/item/storage/box/exileimp - name = "boxed exile implant kit" - desc = "Box of exile implants. It has a picture of a clown being booted through the Gateway." - icon_state = "implant" - - New() - ..() - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implanter(src) - -/obj/item/storage/box/deathimp - name = "death alarm implant kit" - desc = "Box of life sign monitoring implants." - icon_state = "implant" - - New() - ..() - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implanter(src) - -/obj/item/storage/box/tapes - name = "Tape Box" - desc = "A box of spare recording tapes" - icon_state = "box" - - New() - ..() - new /obj/item/tape(src) - new /obj/item/tape(src) - new /obj/item/tape(src) - new /obj/item/tape(src) - new /obj/item/tape(src) - new /obj/item/tape(src) - -/obj/item/storage/box/rxglasses - name = "prescription glasses" - desc = "This box contains nerd glasses." - icon_state = "glasses" - - New() - ..() - new /obj/item/clothing/glasses/regular(src) - new /obj/item/clothing/glasses/regular(src) - new /obj/item/clothing/glasses/regular(src) - new /obj/item/clothing/glasses/regular(src) - new /obj/item/clothing/glasses/regular(src) - new /obj/item/clothing/glasses/regular(src) - new /obj/item/clothing/glasses/regular(src) - -/obj/item/storage/box/drinkingglasses - name = "box of drinking glasses" - desc = "It has a picture of drinking glasses on it." - - New() - ..() - new /obj/item/reagent_containers/food/drinks/drinkingglass(src) - new /obj/item/reagent_containers/food/drinks/drinkingglass(src) - new /obj/item/reagent_containers/food/drinks/drinkingglass(src) - new /obj/item/reagent_containers/food/drinks/drinkingglass(src) - new /obj/item/reagent_containers/food/drinks/drinkingglass(src) - new /obj/item/reagent_containers/food/drinks/drinkingglass(src) - -/obj/item/storage/box/cdeathalarm_kit - name = "Death Alarm Kit" - desc = "Box of stuff used to implant death alarms." - icon_state = "implant" - item_state = "syringe_kit" - - New() - ..() - new /obj/item/implanter(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - new /obj/item/implantcase/death_alarm(src) - -/obj/item/storage/box/condimentbottles - name = "box of condiment bottles" - desc = "It has a large ketchup smear on it." - - New() - ..() - new /obj/item/reagent_containers/food/condiment(src) - new /obj/item/reagent_containers/food/condiment(src) - new /obj/item/reagent_containers/food/condiment(src) - new /obj/item/reagent_containers/food/condiment(src) - new /obj/item/reagent_containers/food/condiment(src) - new /obj/item/reagent_containers/food/condiment(src) - - - -/obj/item/storage/box/cups - name = "box of paper cups" - desc = "It has pictures of paper cups on the front." - New() - ..() - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - new /obj/item/reagent_containers/food/drinks/sillycup( src ) - - -/obj/item/storage/box/donkpockets - name = "box of donk-pockets" - desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." - icon_state = "donk_kit" - - New() - ..() - new /obj/item/reagent_containers/food/snacks/donkpocket(src) - new /obj/item/reagent_containers/food/snacks/donkpocket(src) - new /obj/item/reagent_containers/food/snacks/donkpocket(src) - new /obj/item/reagent_containers/food/snacks/donkpocket(src) - new /obj/item/reagent_containers/food/snacks/donkpocket(src) - new /obj/item/reagent_containers/food/snacks/donkpocket(src) - -/obj/item/storage/box/syndidonkpockets - name = "box of donk-pockets" - desc = "This box feels slightly warm" - icon_state = "donk_kit" - - New() - ..() - new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) - new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) - new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) - new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) - new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) - new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) - -/obj/item/storage/box/monkeycubes - name = "monkey cube box" - desc = "Drymate brand monkey cubes. Just add water!" - icon = 'icons/obj/food/food.dmi' - icon_state = "monkeycubebox" - storage_slots = 7 - can_hold = list(/obj/item/reagent_containers/food/snacks/monkeycube) - var/monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube - -/obj/item/storage/box/monkeycubes/New() - ..() - for(var/i in 1 to 5) - new monkey_cube_type(src) - -/obj/item/storage/box/monkeycubes/syndicate - desc = "Waffle Co. brand monkey cubes. Just add water and a dash of subterfuge!" - monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/syndicate - -/obj/item/storage/box/monkeycubes/farwacubes - name = "farwa cube box" - desc = "Drymate brand farwa cubes. Just add water!" - monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/farwacube - -/obj/item/storage/box/monkeycubes/stokcubes - name = "stok cube box" - desc = "Drymate brand stok cubes. Just add water!" - monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/stokcube - -/obj/item/storage/box/monkeycubes/neaeracubes - name = "neaera cube box" - desc = "Drymate brand neaera cubes. Just add water!" - monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/neaeracube - -/obj/item/storage/box/monkeycubes/wolpincubes - name = "wolpin cube box" - desc = "Drymate brand wolpin cubes. Just add water!" - monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/wolpincube - -/obj/item/storage/box/permits - name = "box of construction permits" - desc = "A box for containing construction permits, used to officially declare built rooms as additions to the station." - icon_state = "id" - -/obj/item/storage/box/permits/New() - ..() - new /obj/item/areaeditor/permit(src) - new /obj/item/areaeditor/permit(src) - new /obj/item/areaeditor/permit(src) - new /obj/item/areaeditor/permit(src) - new /obj/item/areaeditor/permit(src) - new /obj/item/areaeditor/permit(src) - new /obj/item/areaeditor/permit(src) - - -/obj/item/storage/box/ids - name = "spare IDs" - desc = "Has so many empty IDs." - icon_state = "id" - - New() - ..() - new /obj/item/card/id(src) - new /obj/item/card/id(src) - new /obj/item/card/id(src) - new /obj/item/card/id(src) - new /obj/item/card/id(src) - new /obj/item/card/id(src) - new /obj/item/card/id(src) - -/obj/item/storage/box/prisoner - name = "prisoner IDs" - desc = "Take away their last shred of dignity, their name." - icon_state = "id" - - New() - ..() - new /obj/item/card/id/prisoner/one(src) - new /obj/item/card/id/prisoner/two(src) - new /obj/item/card/id/prisoner/three(src) - new /obj/item/card/id/prisoner/four(src) - new /obj/item/card/id/prisoner/five(src) - new /obj/item/card/id/prisoner/six(src) - new /obj/item/card/id/prisoner/seven(src) - -/obj/item/storage/box/seccarts - name = "spare R.O.B.U.S.T. Cartridges" - desc = "A box full of R.O.B.U.S.T. Cartridges, used by Security." - icon_state = "pda" - - New() - ..() - new /obj/item/cartridge/security(src) - new /obj/item/cartridge/security(src) - new /obj/item/cartridge/security(src) - new /obj/item/cartridge/security(src) - new /obj/item/cartridge/security(src) - new /obj/item/cartridge/security(src) - new /obj/item/cartridge/security(src) - -/obj/item/storage/box/holobadge - name = "holobadge box" - icon_state = "box_badge" - desc = "A box claiming to contain holobadges." - New() - new /obj/item/clothing/accessory/holobadge(src) - new /obj/item/clothing/accessory/holobadge(src) - new /obj/item/clothing/accessory/holobadge(src) - new /obj/item/clothing/accessory/holobadge(src) - new /obj/item/clothing/accessory/holobadge/cord(src) - new /obj/item/clothing/accessory/holobadge/cord(src) - ..() - return - -/obj/item/storage/box/evidence - name = "evidence bag box" - desc = "A box claiming to contain evidence bags." - icon_state = "box_evidence" - -/obj/item/storage/box/evidence/New() - new /obj/item/evidencebag(src) - new /obj/item/evidencebag(src) - new /obj/item/evidencebag(src) - new /obj/item/evidencebag(src) - new /obj/item/evidencebag(src) - new /obj/item/evidencebag(src) - ..() - -/obj/item/storage/box/handcuffs - name = "spare handcuffs" - desc = "A box full of handcuffs." - icon_state = "handcuff" - - New() - ..() - new /obj/item/restraints/handcuffs(src) - new /obj/item/restraints/handcuffs(src) - new /obj/item/restraints/handcuffs(src) - new /obj/item/restraints/handcuffs(src) - new /obj/item/restraints/handcuffs(src) - new /obj/item/restraints/handcuffs(src) - new /obj/item/restraints/handcuffs(src) - -/obj/item/storage/box/zipties - name = "box of spare zipties" - desc = "A box full of zipties." - icon_state = "handcuff" - - New() - ..() - new /obj/item/restraints/handcuffs/cable/zipties(src) - new /obj/item/restraints/handcuffs/cable/zipties(src) - new /obj/item/restraints/handcuffs/cable/zipties(src) - new /obj/item/restraints/handcuffs/cable/zipties(src) - new /obj/item/restraints/handcuffs/cable/zipties(src) - new /obj/item/restraints/handcuffs/cable/zipties(src) - new /obj/item/restraints/handcuffs/cable/zipties(src) - -/obj/item/storage/box/alienhandcuffs - name = "box of spare handcuffs" - desc = "A box full of handcuffs." - icon_state = "alienboxCuffs" - - New() - ..() - for(var/i in 1 to 7) - new /obj/item/restraints/handcuffs/alien(src) - -/obj/item/storage/box/fakesyndiesuit - name = "boxed space suit and helmet" - desc = "A sleek, sturdy box used to hold replica spacesuits." - icon_state = "box_of_doom" - - New() - ..() - new /obj/item/clothing/head/syndicatefake(src) - new /obj/item/clothing/suit/syndicatefake(src) - -/obj/item/storage/box/enforcer_rubber - name = "enforcer pistol kit (rubber)" - desc = "A box marked with pictures of an enforcer pistol, two ammo clips, and the word 'NON-LETHAL'." - icon_state = "box_ert" - -/obj/item/storage/box/enforcer_rubber/New() - ..() - new /obj/item/gun/projectile/automatic/pistol/enforcer(src) // loaded with rubber by default - new /obj/item/ammo_box/magazine/enforcer(src) - new /obj/item/ammo_box/magazine/enforcer(src) - -/obj/item/storage/box/enforcer_lethal - name = "enforcer pistol kit (lethal)" - desc = "A box marked with pictures of an enforcer pistol, two ammo clips, and the word 'LETHAL'." - icon_state = "box_ert" - -/obj/item/storage/box/enforcer_lethal/New() - ..() - new /obj/item/gun/projectile/automatic/pistol/enforcer/lethal(src) - new /obj/item/ammo_box/magazine/enforcer/lethal(src) - new /obj/item/ammo_box/magazine/enforcer/lethal(src) - -/obj/item/storage/box/bartender_rare_ingredients_kit - name = "bartender rare reagents kit" - desc = "A box intended for experienced bartenders." - -/obj/item/storage/box/bartender_rare_ingredients_kit/New() - ..() - var/list/reagent_list = list("sacid", "radium", "ether", "methamphetamine", "plasma", "gold", "silver", "capsaicin", "psilocybin") - for(var/reag in reagent_list) - var/obj/item/reagent_containers/glass/bottle/B = new(src) - B.reagents.add_reagent(reag, 30) - B.name = "[reag] bottle" - -/obj/item/storage/box/chef_rare_ingredients_kit - name = "chef rare reagents kit" - desc = "A box intended for experienced chefs." - -/obj/item/storage/box/chef_rare_ingredients_kit/New() - ..() - new /obj/item/reagent_containers/food/condiment/soysauce(src) - new /obj/item/reagent_containers/food/condiment/enzyme(src) - new /obj/item/reagent_containers/food/condiment/pack/hotsauce(src) - new /obj/item/kitchen/knife/butcher(src) - var/list/reagent_list = list("msg", "triple_citrus", "salglu_solution", "nutriment", "gravy", "honey", "vitfro") - for(var/reag in reagent_list) - var/obj/item/reagent_containers/glass/bottle/B = new(src) - B.reagents.add_reagent(reag, 30) - B.name = "[reag] bottle" - -/obj/item/storage/box/mousetraps - name = "box of Pest-B-Gon mousetraps" - desc = "WARNING: Keep out of reach of children." - icon_state = "mousetraps" - - New() - ..() - new /obj/item/assembly/mousetrap( src ) - new /obj/item/assembly/mousetrap( src ) - new /obj/item/assembly/mousetrap( src ) - new /obj/item/assembly/mousetrap( src ) - new /obj/item/assembly/mousetrap( src ) - new /obj/item/assembly/mousetrap( src ) - -/obj/item/storage/box/pillbottles - name = "box of pill bottles" - desc = "It has pictures of pill bottles on its front." - - New() - ..() - new /obj/item/storage/pill_bottle( src ) - new /obj/item/storage/pill_bottle( src ) - new /obj/item/storage/pill_bottle( src ) - new /obj/item/storage/pill_bottle( src ) - new /obj/item/storage/pill_bottle( src ) - new /obj/item/storage/pill_bottle( src ) - new /obj/item/storage/pill_bottle( src ) - -/obj/item/storage/box/patch_packs - name = "box of patch packs" - desc = "It has pictures of patch packs on its front." - -/obj/item/storage/box/patch_packs/New() - ..() - new /obj/item/storage/pill_bottle/patch_pack(src) - new /obj/item/storage/pill_bottle/patch_pack(src) - new /obj/item/storage/pill_bottle/patch_pack(src) - new /obj/item/storage/pill_bottle/patch_pack(src) - new /obj/item/storage/pill_bottle/patch_pack(src) - new /obj/item/storage/pill_bottle/patch_pack(src) - new /obj/item/storage/pill_bottle/patch_pack(src) - -/obj/item/storage/box/bodybags - name = "body bags" - desc = "This box contains body bags." - icon_state = "bodybags" - -/obj/item/storage/box/bodybags/New() - ..() - new /obj/item/bodybag(src) - new /obj/item/bodybag(src) - new /obj/item/bodybag(src) - new /obj/item/bodybag(src) - new /obj/item/bodybag(src) - new /obj/item/bodybag(src) - new /obj/item/bodybag(src) - -/obj/item/storage/box/snappops - name = "snap pop box" - desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." - icon = 'icons/obj/toy.dmi' - icon_state = "spbox" - storage_slots = 8 - can_hold = list(/obj/item/toy/snappop) - New() - ..() - for(var/i=1; i <= storage_slots; i++) - new /obj/item/toy/snappop(src) - -/obj/item/storage/box/matches - name = "matchbox" - desc = "A small box of Almost But Not Quite Plasma Premium Matches." - icon = 'icons/obj/cigarettes.dmi' - icon_state = "matchbox" - item_state = "zippo" - storage_slots = 10 - w_class = WEIGHT_CLASS_TINY - max_w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_BELT - can_hold = list(/obj/item/match) - -/obj/item/storage/box/matches/New() - ..() - for(var/i in 1 to storage_slots) - new /obj/item/match(src) - -/obj/item/storage/box/matches/attackby(obj/item/match/W, mob/user, params) - if(istype(W, /obj/item/match) && !W.lit) - W.matchignite() - playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) - return - -/obj/item/storage/box/autoinjectors - name = "box of injectors" - desc = "Contains autoinjectors." - icon_state = "syringe" - New() - ..() - for(var/i; i < storage_slots; i++) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - -/obj/item/storage/box/autoinjector/utility - name = "autoinjector kit" - desc = "A box with several utility autoinjectors for the economical miner." - icon_state = "syringe" - - New() - ..() - new /obj/item/reagent_containers/hypospray/autoinjector/teporone(src) - new /obj/item/reagent_containers/hypospray/autoinjector/teporone(src) - new /obj/item/reagent_containers/hypospray/autoinjector/stimpack(src) - new /obj/item/reagent_containers/hypospray/autoinjector/stimpack(src) - new /obj/item/reagent_containers/hypospray/autoinjector/stimpack(src) - -/obj/item/storage/box/lights - name = "replacement bulbs" - icon = 'icons/obj/storage.dmi' - icon_state = "light" - desc = "This box is shaped on the inside so that only light tubes and bulbs fit." - item_state = "syringe_kit" - foldable = /obj/item/stack/sheet/cardboard - storage_slots=21 - can_hold = list(/obj/item/light/tube, /obj/item/light/bulb) - max_combined_w_class = 21 - use_to_pickup = 1 // for picking up broken bulbs, not that most people will try - -/obj/item/storage/box/lights/bulbs/New() - ..() - for(var/i = 0; i < 21; i++) - new /obj/item/light/bulb(src) - -/obj/item/storage/box/lights/tubes - name = "replacement tubes" - icon_state = "lighttube" - -/obj/item/storage/box/lights/tubes/New() - ..() - for(var/i = 0; i < 21; i++) - new /obj/item/light/tube(src) - -/obj/item/storage/box/lights/mixed - name = "replacement lights" - icon_state = "lightmixed" - -/obj/item/storage/box/lights/mixed/New() - ..() - for(var/i = 0; i < 14; i++) - new /obj/item/light/tube(src) - for(var/i = 0; i < 7; i++) - new /obj/item/light/bulb(src) - -/obj/item/storage/box/barber - name = "Barber Starter Kit" - desc = "For all hairstyling needs." - icon_state = "implant" - -/obj/item/storage/box/barber/New() - ..() - new /obj/item/scissors/barber(src) - new /obj/item/hair_dye_bottle(src) - new /obj/item/reagent_containers/glass/bottle/reagent/hairgrownium(src) - new /obj/item/reagent_containers/glass/bottle/reagent/hair_dye(src) - new /obj/item/reagent_containers/glass/bottle/reagent(src) - new /obj/item/reagent_containers/dropper(src) - new /obj/item/clothing/mask/fakemoustache(src) //totally necessary for successful barbering -Fox - -/obj/item/storage/box/lip_stick - name = "Lipstick Kit" - desc = "For all your lip coloring needs." - icon_state = "implant" - -/obj/item/storage/box/lip_stick/New() - ..() - new /obj/item/lipstick(src) - new /obj/item/lipstick/purple(src) - new /obj/item/lipstick/jade(src) - new /obj/item/lipstick/black(src) - new /obj/item/lipstick/green(src) - new /obj/item/lipstick/blue(src) - new /obj/item/lipstick/white(src) - -#define NODESIGN "None" -#define NANOTRASEN "NanotrasenStandard" -#define SYNDI "SyndiSnacks" -#define HEART "Heart" -#define SMILE "SmileyFace" - -/obj/item/storage/box/papersack - name = "paper sack" - desc = "A sack neatly crafted out of paper." - icon_state = "paperbag_None" - item_state = "paperbag_None" - resistance_flags = FLAMMABLE - foldable = null - var/design = NODESIGN - -/obj/item/storage/box/papersack/update_icon() - if(!contents.len) - icon_state = "[item_state]" - else icon_state = "[item_state]_closed" - -/obj/item/storage/box/papersack/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - //if a pen is used on the sack, dialogue to change its design appears - if(contents.len) - to_chat(user, "You can't modify [src] with items still inside!") - return - var/list/designs = list(NODESIGN, NANOTRASEN, SYNDI, HEART, SMILE) - var/switchDesign = input("Select a Design:", "Paper Sack Design", designs[1]) as null|anything in designs - if(!switchDesign) - return - if(get_dist(usr, src) > 1 && !usr.incapacitated()) - to_chat(usr, "You have moved too far away!") - return - if(design == switchDesign) - return - to_chat(usr, "You make some modifications to [src] using your pen.") - design = switchDesign - icon_state = "paperbag_[design]" - item_state = "paperbag_[design]" - switch(design) - if(NODESIGN) - desc = "A sack neatly crafted out of paper." - if(NANOTRASEN) - desc = "A standard Nanotrasen paper lunch sack for loyal employees on the go." - if(SYNDI) - desc = "The design on this paper sack is a remnant of the notorious 'SyndieSnacks' program." - if(HEART) - desc = "A paper sack with a heart etched onto the side." - if(SMILE) - desc = "A paper sack with a crude smile etched onto the side." - return - else if(is_sharp(W)) - if(!contents.len) - if(item_state == "paperbag_None") - to_chat(user, "You cut eyeholes into [src].") - new /obj/item/clothing/head/papersack(user.loc) - qdel(src) - return - else if(item_state == "paperbag_SmileyFace") - to_chat(user, "You cut eyeholes into [src] and modify the design.") - new /obj/item/clothing/head/papersack/smiley(user.loc) - qdel(src) - return - return ..() - - -/obj/item/storage/box/centcomofficer - name = "officer kit" - icon_state = "box_ert" - storage_slots = 14 - max_combined_w_class = 20 - -/obj/item/storage/box/centcomofficer/New() - ..() - contents = list() - new /obj/item/clothing/mask/breath(src) - new /obj/item/tank/emergency_oxygen/double/full(src) - new /obj/item/flashlight/seclite(src) - new /obj/item/kitchen/knife/combat(src) - - new /obj/item/radio/centcom(src) - new /obj/item/door_remote/omni(src) - new /obj/item/implanter/death_alarm(src) - - new /obj/item/reagent_containers/hypospray/combat/nanites(src) - new /obj/item/pinpointer(src) - new /obj/item/pinpointer/crew/centcom(src) - -/obj/item/storage/box/responseteam - name = "boxed survival kit" - icon_state = "box_ert" - -/obj/item/storage/box/responseteam/New() - ..() - new /obj/item/clothing/mask/breath(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/flashlight/flare(src) - new /obj/item/kitchen/knife/combat(src) - new /obj/item/radio/centcom(src) - new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - -/obj/item/storage/box/clown - name = "clown box" - desc = "A colorful cardboard box for the clown" - icon_state = "box_clown" - -/obj/item/storage/box/emptysandbags - name = "box of empty sandbags" - -/obj/item/storage/box/emptysandbags/New() - ..() - contents = list() - for(var/i in 1 to 7) - new /obj/item/emptysandbag(src) - -/obj/item/storage/box/rndboards - name = "the Liberator's legacy" - desc = "A box containing a gift for worthy golems." - -/obj/item/storage/box/rndboards/New() - ..() - contents = list() - new /obj/item/circuitboard/protolathe(src) - new /obj/item/circuitboard/destructive_analyzer(src) - new /obj/item/circuitboard/circuit_imprinter(src) - new /obj/item/circuitboard/rdconsole/public(src) - -/obj/item/storage/box/stockparts/basic //for ruins where it's a bad idea to give access to an autolathe/protolathe, but still want to make stock parts accessible - name = "box of stock parts" - desc = "Contains a variety of basic stock parts." - -/obj/item/storage/box/stockparts/basic/New() - ..() - for(var/i in 1 to 3) - new /obj/item/stock_parts/capacitor(src) - new /obj/item/stock_parts/scanning_module(src) - new /obj/item/stock_parts/manipulator(src) - new /obj/item/stock_parts/micro_laser(src) - new /obj/item/stock_parts/matter_bin(src) - -/obj/item/storage/box/stockparts/deluxe - name = "box of deluxe stock parts" - desc = "Contains a variety of deluxe stock parts." - -/obj/item/storage/box/stockparts/deluxe/New() - ..() - for(var/i in 1 to 3) - new /obj/item/stock_parts/capacitor/quadratic(src) - new /obj/item/stock_parts/scanning_module/triphasic(src) - new /obj/item/stock_parts/manipulator/femto(src) - new /obj/item/stock_parts/micro_laser/quadultra(src) - new /obj/item/stock_parts/matter_bin/bluespace(src) - -/obj/item/storage/box/hug - name = "box of hugs" - desc = "A special box for sensitive people." - icon_state = "hugbox" - foldable = null - -/obj/item/storage/box/hug/suicide_act(mob/user) - user.visible_message("[user] clamps the box of hugs on [user.p_their()] jugular! Guess it wasn't such a hugbox after all..") - return (BRUTELOSS) - -/obj/item/storage/box/hug/attack_self(mob/user) - ..() - user.changeNext_move(CLICK_CD_MELEE) - playsound(loc, "rustle", 50, 1, -5) - user.visible_message("[user] hugs \the [src].","You hug \the [src].") - -#undef NODESIGN -#undef NANOTRASEN -#undef SYNDI -#undef HEART -#undef SMILE +/* + * Everything derived from the common cardboard box. + * Basically everything except the original is a kit (starts full). + * + * Contains: + * Empty box, starter boxes (survival/engineer), + * Latex glove and sterile mask boxes, + * Syringe, beaker, dna injector boxes, + * Blanks, flashbangs, and EMP grenade boxes, + * Tracking and chemical implant boxes, + * Prescription glasses and drinking glass boxes, + * Condiment bottle and silly cup boxes, + * Donkpocket and monkeycube boxes, + * ID and security PDA cart boxes, + * Handcuff, mousetrap, and pillbottle boxes, + * Snap-pops and matchboxes, + * Replacement light boxes. + * + * For syndicate call-ins see uplink_kits.dm + */ + +/obj/item/storage/box + name = "box" + desc = "It's just an ordinary box." + icon_state = "box" + item_state = "syringe_kit" + resistance_flags = FLAMMABLE + var/foldable = /obj/item/stack/sheet/cardboard + var/amt = 1 + +/obj/item/storage/box/attack_self(mob/user) + ..() + + if(!foldable) + return + if(contents.len) + to_chat(user, "You can't fold this box with items still inside!") + return + if(!ispath(foldable)) + return + + // Close any open UI windows first + var/found = 0 + for(var/mob/M in range(1)) + if(M.s_active == src) + close(M) + if(M == user) + found = 1 + if(!found) // User is too far away + return + + to_chat(user, "You fold [src] flat.") + var/obj/item/stack/I = new foldable(get_turf(src), amt) + user.put_in_hands(I) + qdel(src) + +/obj/item/storage/box/large + name = "large box" + desc = "You could build a fort with this." + icon_state = "largebox" + w_class = 4 // Big, bulky. + foldable = /obj/item/stack/sheet/cardboard + amt = 4 + storage_slots = 21 + max_combined_w_class = 42 // 21*2 + +/obj/item/storage/box/survival + icon_state = "box_civ" + New() + ..() + contents = list() + new /obj/item/clothing/mask/breath( src ) + new /obj/item/tank/emergency_oxygen( src ) + new /obj/item/reagent_containers/hypospray/autoinjector( src ) + new /obj/item/flashlight/flare/glowstick/emergency( src ) + return + +/obj/item/storage/box/survival_vox + icon_state = "box_vox" + +/obj/item/storage/box/survival_vox/New() + ..() + contents = list() + new /obj/item/clothing/mask/breath/vox(src) + new /obj/item/tank/emergency_oxygen/nitrogen(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + new /obj/item/flashlight/flare/glowstick/emergency(src) + +/obj/item/storage/box/survival_plasmaman + icon_state = "box_plasma" + +/obj/item/storage/box/survival_plasmaman/New() + ..() + contents = list() + new /obj/item/clothing/mask/breath(src) + new /obj/item/tank/emergency_oxygen/plasma(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + new /obj/item/flashlight/flare/glowstick/emergency(src) + +/obj/item/storage/box/engineer + icon_state = "box_eng" + New() + ..() + contents = list() + new /obj/item/clothing/mask/breath( src ) + new /obj/item/tank/emergency_oxygen/engi( src ) + new /obj/item/reagent_containers/hypospray/autoinjector( src ) + new /obj/item/flashlight/flare/glowstick/emergency( src ) + return + +/obj/item/storage/box/survival_mining + icon_state = "box_min" + New() + ..() + contents = list() + new /obj/item/clothing/mask/gas/explorer(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/crowbar/red(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + new /obj/item/flashlight/flare/glowstick/emergency(src) + +/obj/item/storage/box/survival_syndi + icon_state = "box_syndi" + New() + ..() + contents = list() + new /obj/item/clothing/mask/gas/syndicate(src) + new /obj/item/tank/emergency_oxygen/syndi(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + new /obj/item/reagent_containers/food/pill/initropidril(src) + new /obj/item/flashlight/flare/glowstick/red(src) + +/obj/item/storage/box/gloves + name = "box of latex gloves" + desc = "Contains white gloves." + icon_state = "latex" + + New() + ..() + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + +/obj/item/storage/box/masks + name = "sterile masks" + desc = "This box contains masks of sterility." + icon_state = "sterile" + + New() + ..() + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + + +/obj/item/storage/box/syringes + name = "syringes" + desc = "A box full of syringes." + desc = "A biohazard alert warning is printed on the box" + icon_state = "syringe" + + New() + ..() + new /obj/item/reagent_containers/syringe( src ) + new /obj/item/reagent_containers/syringe( src ) + new /obj/item/reagent_containers/syringe( src ) + new /obj/item/reagent_containers/syringe( src ) + new /obj/item/reagent_containers/syringe( src ) + new /obj/item/reagent_containers/syringe( src ) + new /obj/item/reagent_containers/syringe( src ) + +/obj/item/storage/box/beakers + name = "beaker box" + icon_state = "beaker" + + New() + ..() + new /obj/item/reagent_containers/glass/beaker( src ) + new /obj/item/reagent_containers/glass/beaker( src ) + new /obj/item/reagent_containers/glass/beaker( src ) + new /obj/item/reagent_containers/glass/beaker( src ) + new /obj/item/reagent_containers/glass/beaker( src ) + new /obj/item/reagent_containers/glass/beaker( src ) + new /obj/item/reagent_containers/glass/beaker( src ) + +/obj/item/storage/box/beakers/bluespace + name = "box of bluespace beakers" + icon_state = "beaker" + +/obj/item/storage/box/beakers/bluespace/New() + ..() + for(var/i in 1 to 7) + new /obj/item/reagent_containers/glass/beaker/bluespace(src) + +/obj/item/storage/box/iv_bags + name = "IV Bags" + desc = "A box full of empty IV bags." + icon_state = "beaker" + +/obj/item/storage/box/iv_bags/New() + ..() + new /obj/item/reagent_containers/iv_bag( src ) + new /obj/item/reagent_containers/iv_bag( src ) + new /obj/item/reagent_containers/iv_bag( src ) + new /obj/item/reagent_containers/iv_bag( src ) + new /obj/item/reagent_containers/iv_bag( src ) + new /obj/item/reagent_containers/iv_bag( src ) + new /obj/item/reagent_containers/iv_bag( src ) + +/obj/item/storage/box/injectors + name = "\improper DNA injectors" + desc = "This box contains injectors it seems." + + New() + ..() + new /obj/item/dnainjector/h2m(src) + new /obj/item/dnainjector/h2m(src) + new /obj/item/dnainjector/h2m(src) + new /obj/item/dnainjector/m2h(src) + new /obj/item/dnainjector/m2h(src) + new /obj/item/dnainjector/m2h(src) + +/obj/item/storage/box/slug + name = "Ammunition Box (Slug)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "slugbox" + +/obj/item/storage/box/slug/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun(src) + + +/obj/item/storage/box/buck + name = "Ammunition Box (Buckshot)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "buckshotbox" + +/obj/item/storage/box/buck/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/buckshot(src) + +/obj/item/storage/box/dragonsbreath + name = "Ammunition Box (Dragonsbreath)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "dragonsbreathbox" + +/obj/item/storage/box/dragonsbreath/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath(src) + +/obj/item/storage/box/stun + name = "Ammunition Box (Stun shells)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "stunbox" + +/obj/item/storage/box/stun/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/stunslug(src) + +/obj/item/storage/box/beanbag + name = "Ammunition Box (Beanbag shells)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "beanbagbox" + +/obj/item/storage/box/beanbag/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/beanbag(src) + +/obj/item/storage/box/rubbershot + name = "Ammunition Box (Rubbershot shells)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "rubbershotbox" + +/obj/item/storage/box/rubbershot/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/rubbershot(src) + +/obj/item/storage/box/tranquilizer + name = "Ammunition Box (Tranquilizer darts)" + desc = "A small box capable of holding seven shotgun shells." + icon_state = "tranqbox" + +/obj/item/storage/box/tranquilizer/New() + ..() + for(var/i in 1 to 7) + new /obj/item/ammo_casing/shotgun/tranquilizer(src) + +/obj/item/storage/box/flashbangs + name = "box of flashbangs (WARNING)" + desc = "WARNING: These devices are extremely dangerous and can cause blindness or deafness in repeated use." + icon_state = "flashbang" + + New() + ..() + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/grenade/flashbang(src) + +/obj/item/storage/box/flashes + name = "box of flashbulbs" + desc = "WARNING: Flashes can cause serious eye damage, protective eyewear is required." + icon_state = "flashbang" + + New() + ..() + new /obj/item/flash(src) + new /obj/item/flash(src) + new /obj/item/flash(src) + new /obj/item/flash(src) + new /obj/item/flash(src) + new /obj/item/flash(src) + +/obj/item/storage/box/teargas + name = "box of tear gas grenades (WARNING)" + desc = "WARNING: These devices are extremely dangerous and can cause blindness and skin irritation." + icon_state = "flashbang" + +/obj/item/storage/box/teargas/New() + ..() + new /obj/item/grenade/chem_grenade/teargas(src) + new /obj/item/grenade/chem_grenade/teargas(src) + new /obj/item/grenade/chem_grenade/teargas(src) + new /obj/item/grenade/chem_grenade/teargas(src) + new /obj/item/grenade/chem_grenade/teargas(src) + new /obj/item/grenade/chem_grenade/teargas(src) + new /obj/item/grenade/chem_grenade/teargas(src) + +/obj/item/storage/box/emps + name = "emp grenades" + desc = "A box with 5 emp grenades." + icon_state = "flashbang" + + New() + ..() + new /obj/item/grenade/empgrenade(src) + new /obj/item/grenade/empgrenade(src) + new /obj/item/grenade/empgrenade(src) + new /obj/item/grenade/empgrenade(src) + new /obj/item/grenade/empgrenade(src) + + +/obj/item/storage/box/trackimp + name = "tracking implant kit" + desc = "Box full of scum-bag tracking utensils." + icon_state = "implant" + + New() + ..() + new /obj/item/implantcase/tracking(src) + new /obj/item/implantcase/tracking(src) + new /obj/item/implantcase/tracking(src) + new /obj/item/implantcase/tracking(src) + new /obj/item/implanter(src) + new /obj/item/implantpad(src) + new /obj/item/locator(src) + +/obj/item/storage/box/minertracker + name = "boxed tracking implant kit" + desc = "For finding those who have died on the accursed lavaworld." + icon_state = "implant" + +/obj/item/storage/box/minertracker/New() + ..() + new /obj/item/implantcase/tracking(src) + new /obj/item/implantcase/tracking(src) + new /obj/item/implantcase/tracking(src) + new /obj/item/implanter(src) + new /obj/item/implantpad(src) + new /obj/item/locator(src) + +/obj/item/storage/box/chemimp + name = "chemical implant kit" + desc = "Box of stuff used to implant chemicals." + icon_state = "implant" + + New() + ..() + new /obj/item/implantcase/chem(src) + new /obj/item/implantcase/chem(src) + new /obj/item/implantcase/chem(src) + new /obj/item/implantcase/chem(src) + new /obj/item/implantcase/chem(src) + new /obj/item/implanter(src) + new /obj/item/implantpad(src) + +/obj/item/storage/box/exileimp + name = "boxed exile implant kit" + desc = "Box of exile implants. It has a picture of a clown being booted through the Gateway." + icon_state = "implant" + + New() + ..() + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implanter(src) + +/obj/item/storage/box/deathimp + name = "death alarm implant kit" + desc = "Box of life sign monitoring implants." + icon_state = "implant" + + New() + ..() + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implanter(src) + +/obj/item/storage/box/tapes + name = "Tape Box" + desc = "A box of spare recording tapes" + icon_state = "box" + + New() + ..() + new /obj/item/tape(src) + new /obj/item/tape(src) + new /obj/item/tape(src) + new /obj/item/tape(src) + new /obj/item/tape(src) + new /obj/item/tape(src) + +/obj/item/storage/box/rxglasses + name = "prescription glasses" + desc = "This box contains nerd glasses." + icon_state = "glasses" + + New() + ..() + new /obj/item/clothing/glasses/regular(src) + new /obj/item/clothing/glasses/regular(src) + new /obj/item/clothing/glasses/regular(src) + new /obj/item/clothing/glasses/regular(src) + new /obj/item/clothing/glasses/regular(src) + new /obj/item/clothing/glasses/regular(src) + new /obj/item/clothing/glasses/regular(src) + +/obj/item/storage/box/drinkingglasses + name = "box of drinking glasses" + desc = "It has a picture of drinking glasses on it." + + New() + ..() + new /obj/item/reagent_containers/food/drinks/drinkingglass(src) + new /obj/item/reagent_containers/food/drinks/drinkingglass(src) + new /obj/item/reagent_containers/food/drinks/drinkingglass(src) + new /obj/item/reagent_containers/food/drinks/drinkingglass(src) + new /obj/item/reagent_containers/food/drinks/drinkingglass(src) + new /obj/item/reagent_containers/food/drinks/drinkingglass(src) + +/obj/item/storage/box/cdeathalarm_kit + name = "Death Alarm Kit" + desc = "Box of stuff used to implant death alarms." + icon_state = "implant" + item_state = "syringe_kit" + + New() + ..() + new /obj/item/implanter(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + new /obj/item/implantcase/death_alarm(src) + +/obj/item/storage/box/condimentbottles + name = "box of condiment bottles" + desc = "It has a large ketchup smear on it." + + New() + ..() + new /obj/item/reagent_containers/food/condiment(src) + new /obj/item/reagent_containers/food/condiment(src) + new /obj/item/reagent_containers/food/condiment(src) + new /obj/item/reagent_containers/food/condiment(src) + new /obj/item/reagent_containers/food/condiment(src) + new /obj/item/reagent_containers/food/condiment(src) + + + +/obj/item/storage/box/cups + name = "box of paper cups" + desc = "It has pictures of paper cups on the front." + New() + ..() + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + new /obj/item/reagent_containers/food/drinks/sillycup( src ) + + +/obj/item/storage/box/donkpockets + name = "box of donk-pockets" + desc = "Instructions: Heat in microwave. Product will cool if not eaten within seven minutes." + icon_state = "donk_kit" + + New() + ..() + new /obj/item/reagent_containers/food/snacks/donkpocket(src) + new /obj/item/reagent_containers/food/snacks/donkpocket(src) + new /obj/item/reagent_containers/food/snacks/donkpocket(src) + new /obj/item/reagent_containers/food/snacks/donkpocket(src) + new /obj/item/reagent_containers/food/snacks/donkpocket(src) + new /obj/item/reagent_containers/food/snacks/donkpocket(src) + +/obj/item/storage/box/syndidonkpockets + name = "box of donk-pockets" + desc = "This box feels slightly warm" + icon_state = "donk_kit" + + New() + ..() + new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) + new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) + new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) + new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) + new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) + new /obj/item/reagent_containers/food/snacks/syndidonkpocket(src) + +/obj/item/storage/box/monkeycubes + name = "monkey cube box" + desc = "Drymate brand monkey cubes. Just add water!" + icon = 'icons/obj/food/food.dmi' + icon_state = "monkeycubebox" + storage_slots = 7 + can_hold = list(/obj/item/reagent_containers/food/snacks/monkeycube) + var/monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube + +/obj/item/storage/box/monkeycubes/New() + ..() + for(var/i in 1 to 5) + new monkey_cube_type(src) + +/obj/item/storage/box/monkeycubes/syndicate + desc = "Waffle Co. brand monkey cubes. Just add water and a dash of subterfuge!" + monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/syndicate + +/obj/item/storage/box/monkeycubes/farwacubes + name = "farwa cube box" + desc = "Drymate brand farwa cubes. Just add water!" + monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/farwacube + +/obj/item/storage/box/monkeycubes/stokcubes + name = "stok cube box" + desc = "Drymate brand stok cubes. Just add water!" + monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/stokcube + +/obj/item/storage/box/monkeycubes/neaeracubes + name = "neaera cube box" + desc = "Drymate brand neaera cubes. Just add water!" + monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/neaeracube + +/obj/item/storage/box/monkeycubes/wolpincubes + name = "wolpin cube box" + desc = "Drymate brand wolpin cubes. Just add water!" + monkey_cube_type = /obj/item/reagent_containers/food/snacks/monkeycube/wolpincube + +/obj/item/storage/box/permits + name = "box of construction permits" + desc = "A box for containing construction permits, used to officially declare built rooms as additions to the station." + icon_state = "id" + +/obj/item/storage/box/permits/New() + ..() + new /obj/item/areaeditor/permit(src) + new /obj/item/areaeditor/permit(src) + new /obj/item/areaeditor/permit(src) + new /obj/item/areaeditor/permit(src) + new /obj/item/areaeditor/permit(src) + new /obj/item/areaeditor/permit(src) + new /obj/item/areaeditor/permit(src) + + +/obj/item/storage/box/ids + name = "spare IDs" + desc = "Has so many empty IDs." + icon_state = "id" + + New() + ..() + new /obj/item/card/id(src) + new /obj/item/card/id(src) + new /obj/item/card/id(src) + new /obj/item/card/id(src) + new /obj/item/card/id(src) + new /obj/item/card/id(src) + new /obj/item/card/id(src) + +/obj/item/storage/box/prisoner + name = "prisoner IDs" + desc = "Take away their last shred of dignity, their name." + icon_state = "id" + + New() + ..() + new /obj/item/card/id/prisoner/one(src) + new /obj/item/card/id/prisoner/two(src) + new /obj/item/card/id/prisoner/three(src) + new /obj/item/card/id/prisoner/four(src) + new /obj/item/card/id/prisoner/five(src) + new /obj/item/card/id/prisoner/six(src) + new /obj/item/card/id/prisoner/seven(src) + +/obj/item/storage/box/seccarts + name = "spare R.O.B.U.S.T. Cartridges" + desc = "A box full of R.O.B.U.S.T. Cartridges, used by Security." + icon_state = "pda" + + New() + ..() + new /obj/item/cartridge/security(src) + new /obj/item/cartridge/security(src) + new /obj/item/cartridge/security(src) + new /obj/item/cartridge/security(src) + new /obj/item/cartridge/security(src) + new /obj/item/cartridge/security(src) + new /obj/item/cartridge/security(src) + +/obj/item/storage/box/holobadge + name = "holobadge box" + icon_state = "box_badge" + desc = "A box claiming to contain holobadges." + New() + new /obj/item/clothing/accessory/holobadge(src) + new /obj/item/clothing/accessory/holobadge(src) + new /obj/item/clothing/accessory/holobadge(src) + new /obj/item/clothing/accessory/holobadge(src) + new /obj/item/clothing/accessory/holobadge/cord(src) + new /obj/item/clothing/accessory/holobadge/cord(src) + ..() + return + +/obj/item/storage/box/evidence + name = "evidence bag box" + desc = "A box claiming to contain evidence bags." + icon_state = "box_evidence" + +/obj/item/storage/box/evidence/New() + new /obj/item/evidencebag(src) + new /obj/item/evidencebag(src) + new /obj/item/evidencebag(src) + new /obj/item/evidencebag(src) + new /obj/item/evidencebag(src) + new /obj/item/evidencebag(src) + ..() + +/obj/item/storage/box/handcuffs + name = "spare handcuffs" + desc = "A box full of handcuffs." + icon_state = "handcuff" + + New() + ..() + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/restraints/handcuffs(src) + +/obj/item/storage/box/zipties + name = "box of spare zipties" + desc = "A box full of zipties." + icon_state = "handcuff" + + New() + ..() + new /obj/item/restraints/handcuffs/cable/zipties(src) + new /obj/item/restraints/handcuffs/cable/zipties(src) + new /obj/item/restraints/handcuffs/cable/zipties(src) + new /obj/item/restraints/handcuffs/cable/zipties(src) + new /obj/item/restraints/handcuffs/cable/zipties(src) + new /obj/item/restraints/handcuffs/cable/zipties(src) + new /obj/item/restraints/handcuffs/cable/zipties(src) + +/obj/item/storage/box/alienhandcuffs + name = "box of spare handcuffs" + desc = "A box full of handcuffs." + icon_state = "alienboxCuffs" + + New() + ..() + for(var/i in 1 to 7) + new /obj/item/restraints/handcuffs/alien(src) + +/obj/item/storage/box/fakesyndiesuit + name = "boxed space suit and helmet" + desc = "A sleek, sturdy box used to hold replica spacesuits." + icon_state = "box_of_doom" + + New() + ..() + new /obj/item/clothing/head/syndicatefake(src) + new /obj/item/clothing/suit/syndicatefake(src) + +/obj/item/storage/box/enforcer_rubber + name = "enforcer pistol kit (rubber)" + desc = "A box marked with pictures of an enforcer pistol, two ammo clips, and the word 'NON-LETHAL'." + icon_state = "box_ert" + +/obj/item/storage/box/enforcer_rubber/New() + ..() + new /obj/item/gun/projectile/automatic/pistol/enforcer(src) // loaded with rubber by default + new /obj/item/ammo_box/magazine/enforcer(src) + new /obj/item/ammo_box/magazine/enforcer(src) + +/obj/item/storage/box/enforcer_lethal + name = "enforcer pistol kit (lethal)" + desc = "A box marked with pictures of an enforcer pistol, two ammo clips, and the word 'LETHAL'." + icon_state = "box_ert" + +/obj/item/storage/box/enforcer_lethal/New() + ..() + new /obj/item/gun/projectile/automatic/pistol/enforcer/lethal(src) + new /obj/item/ammo_box/magazine/enforcer/lethal(src) + new /obj/item/ammo_box/magazine/enforcer/lethal(src) + +/obj/item/storage/box/bartender_rare_ingredients_kit + name = "bartender rare reagents kit" + desc = "A box intended for experienced bartenders." + +/obj/item/storage/box/bartender_rare_ingredients_kit/New() + ..() + var/list/reagent_list = list("sacid", "radium", "ether", "methamphetamine", "plasma", "gold", "silver", "capsaicin", "psilocybin") + for(var/reag in reagent_list) + var/obj/item/reagent_containers/glass/bottle/B = new(src) + B.reagents.add_reagent(reag, 30) + B.name = "[reag] bottle" + +/obj/item/storage/box/chef_rare_ingredients_kit + name = "chef rare reagents kit" + desc = "A box intended for experienced chefs." + +/obj/item/storage/box/chef_rare_ingredients_kit/New() + ..() + new /obj/item/reagent_containers/food/condiment/soysauce(src) + new /obj/item/reagent_containers/food/condiment/enzyme(src) + new /obj/item/reagent_containers/food/condiment/pack/hotsauce(src) + new /obj/item/kitchen/knife/butcher(src) + var/list/reagent_list = list("msg", "triple_citrus", "salglu_solution", "nutriment", "gravy", "honey", "vitfro") + for(var/reag in reagent_list) + var/obj/item/reagent_containers/glass/bottle/B = new(src) + B.reagents.add_reagent(reag, 30) + B.name = "[reag] bottle" + +/obj/item/storage/box/mousetraps + name = "box of Pest-B-Gon mousetraps" + desc = "WARNING: Keep out of reach of children." + icon_state = "mousetraps" + + New() + ..() + new /obj/item/assembly/mousetrap( src ) + new /obj/item/assembly/mousetrap( src ) + new /obj/item/assembly/mousetrap( src ) + new /obj/item/assembly/mousetrap( src ) + new /obj/item/assembly/mousetrap( src ) + new /obj/item/assembly/mousetrap( src ) + +/obj/item/storage/box/pillbottles + name = "box of pill bottles" + desc = "It has pictures of pill bottles on its front." + + New() + ..() + new /obj/item/storage/pill_bottle( src ) + new /obj/item/storage/pill_bottle( src ) + new /obj/item/storage/pill_bottle( src ) + new /obj/item/storage/pill_bottle( src ) + new /obj/item/storage/pill_bottle( src ) + new /obj/item/storage/pill_bottle( src ) + new /obj/item/storage/pill_bottle( src ) + +/obj/item/storage/box/patch_packs + name = "box of patch packs" + desc = "It has pictures of patch packs on its front." + +/obj/item/storage/box/patch_packs/New() + ..() + new /obj/item/storage/pill_bottle/patch_pack(src) + new /obj/item/storage/pill_bottle/patch_pack(src) + new /obj/item/storage/pill_bottle/patch_pack(src) + new /obj/item/storage/pill_bottle/patch_pack(src) + new /obj/item/storage/pill_bottle/patch_pack(src) + new /obj/item/storage/pill_bottle/patch_pack(src) + new /obj/item/storage/pill_bottle/patch_pack(src) + +/obj/item/storage/box/bodybags + name = "body bags" + desc = "This box contains body bags." + icon_state = "bodybags" + +/obj/item/storage/box/bodybags/New() + ..() + new /obj/item/bodybag(src) + new /obj/item/bodybag(src) + new /obj/item/bodybag(src) + new /obj/item/bodybag(src) + new /obj/item/bodybag(src) + new /obj/item/bodybag(src) + new /obj/item/bodybag(src) + +/obj/item/storage/box/snappops + name = "snap pop box" + desc = "Eight wrappers of fun! Ages 8 and up. Not suitable for children." + icon = 'icons/obj/toy.dmi' + icon_state = "spbox" + storage_slots = 8 + can_hold = list(/obj/item/toy/snappop) + New() + ..() + for(var/i=1; i <= storage_slots; i++) + new /obj/item/toy/snappop(src) + +/obj/item/storage/box/matches + name = "matchbox" + desc = "A small box of Almost But Not Quite Plasma Premium Matches." + icon = 'icons/obj/cigarettes.dmi' + icon_state = "matchbox" + item_state = "zippo" + storage_slots = 10 + w_class = WEIGHT_CLASS_TINY + max_w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_BELT + can_hold = list(/obj/item/match) + +/obj/item/storage/box/matches/New() + ..() + for(var/i in 1 to storage_slots) + new /obj/item/match(src) + +/obj/item/storage/box/matches/attackby(obj/item/match/W, mob/user, params) + if(istype(W, /obj/item/match) && !W.lit) + W.matchignite() + playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) + return + +/obj/item/storage/box/autoinjectors + name = "box of injectors" + desc = "Contains autoinjectors." + icon_state = "syringe" + New() + ..() + for(var/i; i < storage_slots; i++) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + +/obj/item/storage/box/autoinjector/utility + name = "autoinjector kit" + desc = "A box with several utility autoinjectors for the economical miner." + icon_state = "syringe" + + New() + ..() + new /obj/item/reagent_containers/hypospray/autoinjector/teporone(src) + new /obj/item/reagent_containers/hypospray/autoinjector/teporone(src) + new /obj/item/reagent_containers/hypospray/autoinjector/stimpack(src) + new /obj/item/reagent_containers/hypospray/autoinjector/stimpack(src) + new /obj/item/reagent_containers/hypospray/autoinjector/stimpack(src) + +/obj/item/storage/box/lights + name = "replacement bulbs" + icon = 'icons/obj/storage.dmi' + icon_state = "light" + desc = "This box is shaped on the inside so that only light tubes and bulbs fit." + item_state = "syringe_kit" + foldable = /obj/item/stack/sheet/cardboard + storage_slots=21 + can_hold = list(/obj/item/light/tube, /obj/item/light/bulb) + max_combined_w_class = 21 + use_to_pickup = 1 // for picking up broken bulbs, not that most people will try + +/obj/item/storage/box/lights/bulbs/New() + ..() + for(var/i = 0; i < 21; i++) + new /obj/item/light/bulb(src) + +/obj/item/storage/box/lights/tubes + name = "replacement tubes" + icon_state = "lighttube" + +/obj/item/storage/box/lights/tubes/New() + ..() + for(var/i = 0; i < 21; i++) + new /obj/item/light/tube(src) + +/obj/item/storage/box/lights/mixed + name = "replacement lights" + icon_state = "lightmixed" + +/obj/item/storage/box/lights/mixed/New() + ..() + for(var/i = 0; i < 14; i++) + new /obj/item/light/tube(src) + for(var/i = 0; i < 7; i++) + new /obj/item/light/bulb(src) + +/obj/item/storage/box/barber + name = "Barber Starter Kit" + desc = "For all hairstyling needs." + icon_state = "implant" + +/obj/item/storage/box/barber/New() + ..() + new /obj/item/scissors/barber(src) + new /obj/item/hair_dye_bottle(src) + new /obj/item/reagent_containers/glass/bottle/reagent/hairgrownium(src) + new /obj/item/reagent_containers/glass/bottle/reagent/hair_dye(src) + new /obj/item/reagent_containers/glass/bottle/reagent(src) + new /obj/item/reagent_containers/dropper(src) + new /obj/item/clothing/mask/fakemoustache(src) //totally necessary for successful barbering -Fox + +/obj/item/storage/box/lip_stick + name = "Lipstick Kit" + desc = "For all your lip coloring needs." + icon_state = "implant" + +/obj/item/storage/box/lip_stick/New() + ..() + new /obj/item/lipstick(src) + new /obj/item/lipstick/purple(src) + new /obj/item/lipstick/jade(src) + new /obj/item/lipstick/black(src) + new /obj/item/lipstick/green(src) + new /obj/item/lipstick/blue(src) + new /obj/item/lipstick/white(src) + +#define NODESIGN "None" +#define NANOTRASEN "NanotrasenStandard" +#define SYNDI "SyndiSnacks" +#define HEART "Heart" +#define SMILE "SmileyFace" + +/obj/item/storage/box/papersack + name = "paper sack" + desc = "A sack neatly crafted out of paper." + icon_state = "paperbag_None" + item_state = "paperbag_None" + resistance_flags = FLAMMABLE + foldable = null + var/design = NODESIGN + +/obj/item/storage/box/papersack/update_icon() + if(!contents.len) + icon_state = "[item_state]" + else icon_state = "[item_state]_closed" + +/obj/item/storage/box/papersack/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + //if a pen is used on the sack, dialogue to change its design appears + if(contents.len) + to_chat(user, "You can't modify [src] with items still inside!") + return + var/list/designs = list(NODESIGN, NANOTRASEN, SYNDI, HEART, SMILE) + var/switchDesign = input("Select a Design:", "Paper Sack Design", designs[1]) as null|anything in designs + if(!switchDesign) + return + if(get_dist(usr, src) > 1 && !usr.incapacitated()) + to_chat(usr, "You have moved too far away!") + return + if(design == switchDesign) + return + to_chat(usr, "You make some modifications to [src] using your pen.") + design = switchDesign + icon_state = "paperbag_[design]" + item_state = "paperbag_[design]" + switch(design) + if(NODESIGN) + desc = "A sack neatly crafted out of paper." + if(NANOTRASEN) + desc = "A standard Nanotrasen paper lunch sack for loyal employees on the go." + if(SYNDI) + desc = "The design on this paper sack is a remnant of the notorious 'SyndieSnacks' program." + if(HEART) + desc = "A paper sack with a heart etched onto the side." + if(SMILE) + desc = "A paper sack with a crude smile etched onto the side." + return + else if(is_sharp(W)) + if(!contents.len) + if(item_state == "paperbag_None") + to_chat(user, "You cut eyeholes into [src].") + new /obj/item/clothing/head/papersack(user.loc) + qdel(src) + return + else if(item_state == "paperbag_SmileyFace") + to_chat(user, "You cut eyeholes into [src] and modify the design.") + new /obj/item/clothing/head/papersack/smiley(user.loc) + qdel(src) + return + return ..() + + +/obj/item/storage/box/centcomofficer + name = "officer kit" + icon_state = "box_ert" + storage_slots = 14 + max_combined_w_class = 20 + +/obj/item/storage/box/centcomofficer/New() + ..() + contents = list() + new /obj/item/clothing/mask/breath(src) + new /obj/item/tank/emergency_oxygen/double/full(src) + new /obj/item/flashlight/seclite(src) + new /obj/item/kitchen/knife/combat(src) + + new /obj/item/radio/centcom(src) + new /obj/item/door_remote/omni(src) + new /obj/item/implanter/death_alarm(src) + + new /obj/item/reagent_containers/hypospray/combat/nanites(src) + new /obj/item/pinpointer(src) + new /obj/item/pinpointer/crew/centcom(src) + +/obj/item/storage/box/responseteam + name = "boxed survival kit" + icon_state = "box_ert" + +/obj/item/storage/box/responseteam/New() + ..() + new /obj/item/clothing/mask/breath(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/flashlight/flare(src) + new /obj/item/kitchen/knife/combat(src) + new /obj/item/radio/centcom(src) + new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + +/obj/item/storage/box/clown + name = "clown box" + desc = "A colorful cardboard box for the clown" + icon_state = "box_clown" + var/robot_arm // This exists for bot construction + +/obj/item/storage/box/emptysandbags + name = "box of empty sandbags" + +/obj/item/storage/box/emptysandbags/New() + ..() + contents = list() + for(var/i in 1 to 7) + new /obj/item/emptysandbag(src) + +/obj/item/storage/box/rndboards + name = "the Liberator's legacy" + desc = "A box containing a gift for worthy golems." + +/obj/item/storage/box/rndboards/New() + ..() + contents = list() + new /obj/item/circuitboard/protolathe(src) + new /obj/item/circuitboard/destructive_analyzer(src) + new /obj/item/circuitboard/circuit_imprinter(src) + new /obj/item/circuitboard/rdconsole/public(src) + +/obj/item/storage/box/stockparts/basic //for ruins where it's a bad idea to give access to an autolathe/protolathe, but still want to make stock parts accessible + name = "box of stock parts" + desc = "Contains a variety of basic stock parts." + +/obj/item/storage/box/stockparts/basic/New() + ..() + for(var/i in 1 to 3) + new /obj/item/stock_parts/capacitor(src) + new /obj/item/stock_parts/scanning_module(src) + new /obj/item/stock_parts/manipulator(src) + new /obj/item/stock_parts/micro_laser(src) + new /obj/item/stock_parts/matter_bin(src) + +/obj/item/storage/box/stockparts/deluxe + name = "box of deluxe stock parts" + desc = "Contains a variety of deluxe stock parts." + +/obj/item/storage/box/stockparts/deluxe/New() + ..() + for(var/i in 1 to 3) + new /obj/item/stock_parts/capacitor/quadratic(src) + new /obj/item/stock_parts/scanning_module/triphasic(src) + new /obj/item/stock_parts/manipulator/femto(src) + new /obj/item/stock_parts/micro_laser/quadultra(src) + new /obj/item/stock_parts/matter_bin/bluespace(src) + +/obj/item/storage/box/hug + name = "box of hugs" + desc = "A special box for sensitive people." + icon_state = "hugbox" + foldable = null + +/obj/item/storage/box/hug/suicide_act(mob/user) + user.visible_message("[user] clamps the box of hugs on [user.p_their()] jugular! Guess it wasn't such a hugbox after all..") + return (BRUTELOSS) + +/obj/item/storage/box/hug/attack_self(mob/user) + ..() + user.changeNext_move(CLICK_CD_MELEE) + playsound(loc, "rustle", 50, 1, -5) + user.visible_message("[user] hugs \the [src].","You hug \the [src].") + +#undef NODESIGN +#undef NANOTRASEN +#undef SYNDI +#undef HEART +#undef SMILE diff --git a/code/game/objects/items/weapons/storage/briefcase.dm b/code/game/objects/items/weapons/storage/briefcase.dm index 3f747e0f799b..acbf16ce2021 100644 --- a/code/game/objects/items/weapons/storage/briefcase.dm +++ b/code/game/objects/items/weapons/storage/briefcase.dm @@ -1,94 +1,94 @@ -/obj/item/storage/briefcase - name = "briefcase" - desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." - icon_state = "briefcase" - item_state = "briefcase" - flags = CONDUCT - hitsound = "swing_hit" - force = 8 - throw_speed = 2 - throw_range = 4 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 21 - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") - resistance_flags = FLAMMABLE - max_integrity = 150 - -/obj/item/storage/briefcase/sniperbundle - desc = "Its label reads \"genuine hardened Captain leather\", but suspiciously has no other tags or branding. Smells like L'Air du Temps." - force = 10 - -/obj/item/storage/briefcase/sniperbundle/New() - ..() - new /obj/item/gun/projectile/automatic/sniper_rifle/syndicate(src) - new /obj/item/clothing/accessory/red(src) - new /obj/item/clothing/under/syndicate/sniper(src) - new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) - new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) - new /obj/item/suppressor/specialoffer(src) - -/obj/item/storage/briefcase/false_bottomed - max_w_class = WEIGHT_CLASS_SMALL - max_combined_w_class = 10 - - var/busy_hunting = FALSE - var/bottom_open = FALSE //is the false bottom open? - var/obj/item/stored_item = null //what's in the false bottom. If it's a gun, we can fire it - -/obj/item/storage/briefcase/false_bottomed/Destroy() - if(stored_item)//since the stored_item isn't in the briefcase' contents we gotta remind the game to delete it here. - QDEL_NULL(stored_item) - return ..() - -/obj/item/storage/briefcase/false_bottomed/afterattack(atom/A, mob/user, flag, params) - ..() - if(stored_item && istype(stored_item, /obj/item/gun) && !Adjacent(A)) - var/obj/item/gun/stored_gun = stored_item - stored_gun.afterattack(A, user, flag, params) - -/obj/item/storage/briefcase/false_bottomed/attackby(var/obj/item/I, mob/user) - if(bottom_open) - if(stored_item) - to_chat(user, "There's already something in the false bottom!") - return - if(I.w_class > WEIGHT_CLASS_NORMAL) - to_chat(user, "The [I] is too big to fit in the false bottom!") - return - if(!user.drop_item(I)) - user << "The [I] is stuck to your hands!" - return - - stored_item = I - max_w_class = WEIGHT_CLASS_NORMAL - stored_item.w_class - I.forceMove(null) //null space here we go - to stop it showing up in the briefcase - to_chat(user, "You place the [I] into the false bottom of the briefcase.") - else - return ..() - -/obj/item/storage/briefcase/false_bottomed/screwdriver_act(mob/user, obj/item/I) - if(!bottom_open && busy_hunting) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!bottom_open) - to_chat(user, "You begin to hunt around the rim of the [src]...") - busy_hunting = TRUE - if(do_after(user, 20, target = src)) - if(user) - to_chat(user, "You pry open the false bottom!") - bottom_open = TRUE - busy_hunting = FALSE - else - to_chat(user, "You push the false bottom down and close it with a click[stored_item ? ", with the [stored_item] snugly inside." : "."]") - bottom_open = FALSE - -/obj/item/storage/briefcase/false_bottomed/attack_hand(mob/user) - if(bottom_open && stored_item) - user.put_in_hands(stored_item) - to_chat(user, "You pull out the [stored_item] from the [src]'s false bottom.") - stored_item = null - max_w_class = initial(max_w_class) - else - return ..() +/obj/item/storage/briefcase + name = "briefcase" + desc = "It's made of AUTHENTIC faux-leather and has a price-tag still attached. Its owner must be a real professional." + icon_state = "briefcase" + item_state = "briefcase" + flags = CONDUCT + hitsound = "swing_hit" + force = 8 + throw_speed = 2 + throw_range = 4 + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 21 + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") + resistance_flags = FLAMMABLE + max_integrity = 150 + +/obj/item/storage/briefcase/sniperbundle + desc = "Its label reads \"genuine hardened Captain leather\", but suspiciously has no other tags or branding. Smells like L'Air du Temps." + force = 10 + +/obj/item/storage/briefcase/sniperbundle/New() + ..() + new /obj/item/gun/projectile/automatic/sniper_rifle/syndicate(src) + new /obj/item/clothing/accessory/red(src) + new /obj/item/clothing/under/syndicate/sniper(src) + new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) + new /obj/item/ammo_box/magazine/sniper_rounds/soporific(src) + new /obj/item/suppressor/specialoffer(src) + +/obj/item/storage/briefcase/false_bottomed + max_w_class = WEIGHT_CLASS_SMALL + max_combined_w_class = 10 + + var/busy_hunting = FALSE + var/bottom_open = FALSE //is the false bottom open? + var/obj/item/stored_item = null //what's in the false bottom. If it's a gun, we can fire it + +/obj/item/storage/briefcase/false_bottomed/Destroy() + if(stored_item)//since the stored_item isn't in the briefcase' contents we gotta remind the game to delete it here. + QDEL_NULL(stored_item) + return ..() + +/obj/item/storage/briefcase/false_bottomed/afterattack(atom/A, mob/user, flag, params) + ..() + if(stored_item && istype(stored_item, /obj/item/gun) && !Adjacent(A)) + var/obj/item/gun/stored_gun = stored_item + stored_gun.afterattack(A, user, flag, params) + +/obj/item/storage/briefcase/false_bottomed/attackby(var/obj/item/I, mob/user) + if(bottom_open) + if(stored_item) + to_chat(user, "There's already something in the false bottom!") + return + if(I.w_class > WEIGHT_CLASS_NORMAL) + to_chat(user, "The [I] is too big to fit in the false bottom!") + return + if(!user.drop_item(I)) + user << "The [I] is stuck to your hands!" + return + + stored_item = I + max_w_class = WEIGHT_CLASS_NORMAL - stored_item.w_class + I.forceMove(null) //null space here we go - to stop it showing up in the briefcase + to_chat(user, "You place the [I] into the false bottom of the briefcase.") + else + return ..() + +/obj/item/storage/briefcase/false_bottomed/screwdriver_act(mob/user, obj/item/I) + if(!bottom_open && busy_hunting) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!bottom_open) + to_chat(user, "You begin to hunt around the rim of the [src]...") + busy_hunting = TRUE + if(do_after(user, 20, target = src)) + if(user) + to_chat(user, "You pry open the false bottom!") + bottom_open = TRUE + busy_hunting = FALSE + else + to_chat(user, "You push the false bottom down and close it with a click[stored_item ? ", with the [stored_item] snugly inside." : "."]") + bottom_open = FALSE + +/obj/item/storage/briefcase/false_bottomed/attack_hand(mob/user) + if(bottom_open && stored_item) + user.put_in_hands(stored_item) + to_chat(user, "You pull out the [stored_item] from the [src]'s false bottom.") + stored_item = null + max_w_class = initial(max_w_class) + else + return ..() diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 987bf86a904f..a9da4a692414 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -1,382 +1,383 @@ -/* First aid storage - * Contains: - * First Aid Kits - * Pill Bottles - * Dice Pack (in a pill bottle) - */ - -/* - * First Aid Kits - */ -/obj/item/storage/firstaid - name = "first-aid kit" - desc = "It's an emergency medical kit for those serious boo-boos." - icon_state = "firstaid" - throw_speed = 2 - throw_range = 8 - var/empty = 0 - req_one_access =list(ACCESS_MEDICAL, ACCESS_ROBOTICS) //Access and treatment are utilized for medbots. - var/treatment_brute = "salglu_solution" - var/treatment_oxy = "salbutamol" - var/treatment_fire = "salglu_solution" - var/treatment_tox = "charcoal" - var/treatment_virus = "spaceacillin" - var/med_bot_skin = null - var/syndicate_aligned = FALSE - - -/obj/item/storage/firstaid/fire - name = "fire first-aid kit" - desc = "A medical kit that contains several medical patches and pills for treating burns. Contains one epinephrine syringe for emergency use and a health analyzer." - icon_state = "ointment" - item_state = "firstaid-ointment" - med_bot_skin = "ointment" - - New() - ..() - if(empty) return - - icon_state = pick("ointment","firefirstaid") - - new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) - new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) - new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) - new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) - new /obj/item/healthanalyzer( src ) - new /obj/item/reagent_containers/hypospray/autoinjector( src ) - new /obj/item/reagent_containers/food/pill/salicylic( src ) - return - -/obj/item/storage/firstaid/fire/empty - empty = 1 - -/obj/item/storage/firstaid/regular - desc = "A general medical kit that contains medical patches for both brute damage and burn damage. Also contains an epinephrine syringe for emergency use and a health analyzer" - icon_state = "firstaid" - - New() - ..() - if(empty) return - new /obj/item/reagent_containers/food/pill/patch/styptic( src ) - new /obj/item/reagent_containers/food/pill/patch/styptic( src ) - new /obj/item/reagent_containers/food/pill/salicylic( src ) - new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) - new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) - new /obj/item/healthanalyzer( src ) - new /obj/item/reagent_containers/hypospray/autoinjector( src ) - return - -/obj/item/storage/firstaid/toxin - name = "toxin first aid kit" - desc = "A medical kit designed to counter poisoning by common toxins. Contains three pills and syringes, and a health analyzer to determine the health of the patient." - icon_state = "antitoxin" - item_state = "firstaid-toxin" - med_bot_skin = "tox" - - New() - ..() - if(empty) return - - icon_state = pick("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3") - - new /obj/item/reagent_containers/syringe/charcoal( src ) - new /obj/item/reagent_containers/syringe/charcoal( src ) - new /obj/item/reagent_containers/syringe/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/healthanalyzer( src ) - return - -/obj/item/storage/firstaid/toxin/empty - empty = 1 - -/obj/item/storage/firstaid/o2 - name = "oxygen deprivation first aid kit" - desc = "A first aid kit that contains four pills of salbutamol, which is able to counter injuries caused by suffocation. Also contains a health analyzer to determine the health of the patient." - icon_state = "o2" - item_state = "firstaid-o2" - med_bot_skin = "o2" - - New() - ..() - if(empty) return - new /obj/item/reagent_containers/food/pill/salbutamol( src ) - new /obj/item/reagent_containers/food/pill/salbutamol( src ) - new /obj/item/reagent_containers/food/pill/salbutamol( src ) - new /obj/item/reagent_containers/food/pill/salbutamol( src ) - new /obj/item/healthanalyzer( src ) - return - -/obj/item/storage/firstaid/o2/empty - empty = 1 - -/obj/item/storage/firstaid/brute - name = "brute trauma treatment kit" - desc = "A medical kit that contains several medical patches and pills for treating brute injuries. Contains one epinephrine syringe for emergency use and a health analyzer." - icon_state = "brute" - item_state = "firstaid-brute" - med_bot_skin = "brute" - - New() - ..() - if(empty) return - - icon_state = pick("brute","brute2") - - new /obj/item/reagent_containers/food/pill/patch/styptic(src) - new /obj/item/reagent_containers/food/pill/patch/styptic(src) - new /obj/item/reagent_containers/food/pill/patch/styptic(src) - new /obj/item/reagent_containers/food/pill/patch/styptic(src) - new /obj/item/healthanalyzer(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - new /obj/item/stack/medical/bruise_pack(src) - return - -/obj/item/storage/firstaid/brute/empty - empty = 1 - -/obj/item/storage/firstaid/adv - name = "advanced first-aid kit" - desc = "Contains advanced medical treatments." - icon_state = "advfirstaid" - item_state = "firstaid-advanced" - med_bot_skin = "adv" - -/obj/item/storage/firstaid/adv/New() - ..() - if(empty) - return - new /obj/item/stack/medical/bruise_pack(src) - new /obj/item/stack/medical/bruise_pack/advanced(src) - new /obj/item/stack/medical/bruise_pack/advanced(src) - new /obj/item/stack/medical/ointment/advanced(src) - new /obj/item/stack/medical/ointment/advanced(src) - new /obj/item/reagent_containers/hypospray/autoinjector(src) - new /obj/item/healthanalyzer(src) - -/obj/item/storage/firstaid/adv/empty - empty = 1 - -/obj/item/storage/firstaid/machine - name = "machine repair kit" - desc = "A kit that contains supplies to repair IPCs on the go." - icon_state = "machinefirstaid" - item_state = "firstaid-machine" - med_bot_skin = "machine" - -/obj/item/storage/firstaid/machine/New() - ..() - if(empty) - return - new /obj/item/weldingtool(src) - new /obj/item/stack/cable_coil(src) - new /obj/item/stack/cable_coil(src) - new /obj/item/stack/cable_coil(src) - new /obj/item/reagent_containers/food/drinks/oilcan/full(src) - new /obj/item/robotanalyzer(src) - -/obj/item/storage/firstaid/machine/empty - empty = 1 - - -/obj/item/storage/firstaid/tactical - name = "first-aid kit" - icon_state = "bezerk" - desc = "I hope you've got insurance." - max_w_class = WEIGHT_CLASS_NORMAL - treatment_oxy = "perfluorodecalin" - treatment_brute = "bicaridine" - treatment_fire = "kelotane" - treatment_tox = "charcoal" - req_one_access =list(ACCESS_SYNDICATE) - med_bot_skin = "bezerk" - syndicate_aligned = TRUE - -/obj/item/storage/firstaid/tactical/New() - ..() - if(empty) return - new /obj/item/reagent_containers/hypospray/combat(src) - new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) // Because you ain't got no time to look at what damage dey taking yo - new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) - new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) - new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) - new /obj/item/defibrillator/compact/combat/loaded(src) - new /obj/item/clothing/glasses/hud/health/night(src) - return - -/obj/item/storage/firstaid/tactical/empty - empty =1 - -/obj/item/storage/firstaid/surgery - name = "field surgery kit" - icon_state = "duffel-med" - desc = "A kit for surgery in the field." - max_w_class = WEIGHT_CLASS_BULKY - max_combined_w_class = 21 - storage_slots = 10 - can_hold = list(/obj/item/roller,/obj/item/bonesetter,/obj/item/bonegel, /obj/item/scalpel, /obj/item/hemostat, - /obj/item/cautery, /obj/item/retractor, /obj/item/FixOVein, /obj/item/surgicaldrill, /obj/item/circular_saw) - -/obj/item/storage/firstaid/surgery/New() - ..() - new /obj/item/roller(src) - new /obj/item/bonesetter(src) - new /obj/item/bonegel(src) - new /obj/item/scalpel(src) - new /obj/item/hemostat(src) - new /obj/item/cautery(src) - new /obj/item/retractor(src) - new /obj/item/FixOVein(src) - new /obj/item/surgicaldrill(src) - new /obj/item/circular_saw(src) - -/* - * Pill Bottles - */ - -/obj/item/storage/pill_bottle - name = "pill bottle" - desc = "It's an airtight container for storing medication." - icon_state = "pill_canister" - icon = 'icons/obj/chemical.dmi' - item_state = "contsolid" - w_class = WEIGHT_CLASS_SMALL - can_hold = list(/obj/item/reagent_containers/food/pill) - cant_hold = list(/obj/item/reagent_containers/food/pill/patch) - allow_quick_gather = TRUE - use_to_pickup = TRUE - storage_slots = 50 - max_combined_w_class = 50 - display_contents_with_number = TRUE - var/base_name = "" - var/label_text = "" - var/applying_meds = FALSE //To Prevent spam clicking and generating runtimes from apply a deleting pill multiple times. - var/rapid_intake_message = "unscrews the cap on the pill bottle and begins dumping the entire contents down their throat!" - var/rapid_post_instake_message = "downs the entire bottle of pills in one go!" - var/allow_wrap = TRUE - var/wrapper_color = null - -/obj/item/storage/pill_bottle/New() - ..() - base_name = name - if(allow_wrap) - apply_wrap() - -/obj/item/storage/pill_bottle/proc/apply_wrap() - if(wrapper_color) - overlays.Cut() - var/image/I = image(icon, "pillbottle_wrap") - I.color = wrapper_color - overlays += I - -/obj/item/storage/pill_bottle/attack(mob/M, mob/user) - if(iscarbon(M) && contents.len) - if(applying_meds) - to_chat(user, "You are already applying meds.") - return - applying_meds = TRUE - for(var/obj/item/reagent_containers/food/pill/P in contents) - if(P.attack(M, user)) - applying_meds = FALSE - else - applying_meds = FALSE - break - else - return ..() - -/obj/item/storage/pill_bottle/ert - wrapper_color = COLOR_MAROON - -/obj/item/storage/pill_bottle/ert/New() - ..() - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/charcoal(src) - new /obj/item/reagent_containers/food/pill/charcoal(src) - new /obj/item/reagent_containers/food/pill/charcoal(src) - -/obj/item/storage/pill_bottle/MouseDrop(obj/over_object as obj) // Best utilized if you're a cantankerous doctor with a Vicodin habit. - if(iscarbon(over_object)) - var/mob/living/carbon/C = over_object - if(loc == C && src == C.get_active_hand()) - if(!contents.len) - to_chat(C, "There is nothing in [src]!") - return - C.visible_message("[C] [rapid_intake_message]") - if(do_mob(C, C, 100)) // 10 seconds - for(var/obj/item/reagent_containers/food/pill/P in contents) - P.attack(C, C) - C.visible_message("[C] [rapid_post_instake_message]") - return - - return ..() - -/obj/item/storage/pill_bottle/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/pen) || istype(I, /obj/item/flashlight/pen)) - var/tmp_label = sanitize(input(user, "Enter a label for [name]","Label",label_text)) - if(length(tmp_label) > MAX_NAME_LEN) - to_chat(user, "The label can be at most [MAX_NAME_LEN] characters long.") - else - to_chat(user, "You set the label to \"[tmp_label]\".") - label_text = tmp_label - update_name_label() - else - return ..() - -/obj/item/storage/pill_bottle/proc/update_name_label() - if(label_text == "") - name = base_name - else - name = "[base_name] ([label_text])" - -/obj/item/storage/pill_bottle/patch_pack - name = "Patch Pack" - desc = "It's a container for storing medical patches." - icon_state = "patch_pack" - can_hold = list(/obj/item/reagent_containers/food/pill/patch) - cant_hold = list() - rapid_intake_message = "flips the lid of the Patch Pack open and begins rapidly stamping patches on themselves!" - rapid_post_instake_message = "stamps the entire contents of the Patch Pack all over their entire body!" - allow_wrap = FALSE - -/obj/item/storage/pill_bottle/charcoal - name = "Pill bottle (Charcoal)" - desc = "Contains pills used to counter toxins." - wrapper_color = COLOR_GREEN - - New() - ..() - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - new /obj/item/reagent_containers/food/pill/charcoal( src ) - -/obj/item/storage/pill_bottle/painkillers - name = "Pill Bottle (Salicylic Acid)" - desc = "Contains various pills for minor pain relief." - wrapper_color = COLOR_RED - -/obj/item/storage/pill_bottle/painkillers/New() - ..() - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - new /obj/item/reagent_containers/food/pill/salicylic(src) - -/obj/item/storage/pill_bottle/fakedeath - allow_wrap = FALSE - -/obj/item/storage/pill_bottle/fakedeath/New() - ..() - new /obj/item/reagent_containers/food/pill/fakedeath(src) - new /obj/item/reagent_containers/food/pill/fakedeath(src) - new /obj/item/reagent_containers/food/pill/fakedeath(src) +/* First aid storage + * Contains: + * First Aid Kits + * Pill Bottles + * Dice Pack (in a pill bottle) + */ + +/* + * First Aid Kits + */ +/obj/item/storage/firstaid + name = "first-aid kit" + desc = "It's an emergency medical kit for those serious boo-boos." + icon_state = "firstaid" + throw_speed = 2 + throw_range = 8 + var/empty = 0 + req_one_access =list(ACCESS_MEDICAL, ACCESS_ROBOTICS) //Access and treatment are utilized for medbots. + var/treatment_brute = "salglu_solution" + var/treatment_oxy = "salbutamol" + var/treatment_fire = "salglu_solution" + var/treatment_tox = "charcoal" + var/treatment_virus = "spaceacillin" + var/med_bot_skin = null + var/syndicate_aligned = FALSE + var/robot_arm // This is for robot construction + + +/obj/item/storage/firstaid/fire + name = "fire first-aid kit" + desc = "A medical kit that contains several medical patches and pills for treating burns. Contains one epinephrine syringe for emergency use and a health analyzer." + icon_state = "ointment" + item_state = "firstaid-ointment" + med_bot_skin = "ointment" + + New() + ..() + if(empty) return + + icon_state = pick("ointment","firefirstaid") + + new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) + new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) + new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) + new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) + new /obj/item/healthanalyzer( src ) + new /obj/item/reagent_containers/hypospray/autoinjector( src ) + new /obj/item/reagent_containers/food/pill/salicylic( src ) + return + +/obj/item/storage/firstaid/fire/empty + empty = 1 + +/obj/item/storage/firstaid/regular + desc = "A general medical kit that contains medical patches for both brute damage and burn damage. Also contains an epinephrine syringe for emergency use and a health analyzer" + icon_state = "firstaid" + + New() + ..() + if(empty) return + new /obj/item/reagent_containers/food/pill/patch/styptic( src ) + new /obj/item/reagent_containers/food/pill/patch/styptic( src ) + new /obj/item/reagent_containers/food/pill/salicylic( src ) + new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) + new /obj/item/reagent_containers/food/pill/patch/silver_sulf( src ) + new /obj/item/healthanalyzer( src ) + new /obj/item/reagent_containers/hypospray/autoinjector( src ) + return + +/obj/item/storage/firstaid/toxin + name = "toxin first aid kit" + desc = "A medical kit designed to counter poisoning by common toxins. Contains three pills and syringes, and a health analyzer to determine the health of the patient." + icon_state = "antitoxin" + item_state = "firstaid-toxin" + med_bot_skin = "tox" + + New() + ..() + if(empty) return + + icon_state = pick("antitoxin","antitoxfirstaid","antitoxfirstaid2","antitoxfirstaid3") + + new /obj/item/reagent_containers/syringe/charcoal( src ) + new /obj/item/reagent_containers/syringe/charcoal( src ) + new /obj/item/reagent_containers/syringe/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/healthanalyzer( src ) + return + +/obj/item/storage/firstaid/toxin/empty + empty = 1 + +/obj/item/storage/firstaid/o2 + name = "oxygen deprivation first aid kit" + desc = "A first aid kit that contains four pills of salbutamol, which is able to counter injuries caused by suffocation. Also contains a health analyzer to determine the health of the patient." + icon_state = "o2" + item_state = "firstaid-o2" + med_bot_skin = "o2" + + New() + ..() + if(empty) return + new /obj/item/reagent_containers/food/pill/salbutamol( src ) + new /obj/item/reagent_containers/food/pill/salbutamol( src ) + new /obj/item/reagent_containers/food/pill/salbutamol( src ) + new /obj/item/reagent_containers/food/pill/salbutamol( src ) + new /obj/item/healthanalyzer( src ) + return + +/obj/item/storage/firstaid/o2/empty + empty = 1 + +/obj/item/storage/firstaid/brute + name = "brute trauma treatment kit" + desc = "A medical kit that contains several medical patches and pills for treating brute injuries. Contains one epinephrine syringe for emergency use and a health analyzer." + icon_state = "brute" + item_state = "firstaid-brute" + med_bot_skin = "brute" + + New() + ..() + if(empty) return + + icon_state = pick("brute","brute2") + + new /obj/item/reagent_containers/food/pill/patch/styptic(src) + new /obj/item/reagent_containers/food/pill/patch/styptic(src) + new /obj/item/reagent_containers/food/pill/patch/styptic(src) + new /obj/item/reagent_containers/food/pill/patch/styptic(src) + new /obj/item/healthanalyzer(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + new /obj/item/stack/medical/bruise_pack(src) + return + +/obj/item/storage/firstaid/brute/empty + empty = 1 + +/obj/item/storage/firstaid/adv + name = "advanced first-aid kit" + desc = "Contains advanced medical treatments." + icon_state = "advfirstaid" + item_state = "firstaid-advanced" + med_bot_skin = "adv" + +/obj/item/storage/firstaid/adv/New() + ..() + if(empty) + return + new /obj/item/stack/medical/bruise_pack(src) + new /obj/item/stack/medical/bruise_pack/advanced(src) + new /obj/item/stack/medical/bruise_pack/advanced(src) + new /obj/item/stack/medical/ointment/advanced(src) + new /obj/item/stack/medical/ointment/advanced(src) + new /obj/item/reagent_containers/hypospray/autoinjector(src) + new /obj/item/healthanalyzer(src) + +/obj/item/storage/firstaid/adv/empty + empty = 1 + +/obj/item/storage/firstaid/machine + name = "machine repair kit" + desc = "A kit that contains supplies to repair IPCs on the go." + icon_state = "machinefirstaid" + item_state = "firstaid-machine" + med_bot_skin = "machine" + +/obj/item/storage/firstaid/machine/New() + ..() + if(empty) + return + new /obj/item/weldingtool(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/stack/cable_coil(src) + new /obj/item/reagent_containers/food/drinks/oilcan/full(src) + new /obj/item/robotanalyzer(src) + +/obj/item/storage/firstaid/machine/empty + empty = 1 + + +/obj/item/storage/firstaid/tactical + name = "first-aid kit" + icon_state = "bezerk" + desc = "I hope you've got insurance." + max_w_class = WEIGHT_CLASS_NORMAL + treatment_oxy = "perfluorodecalin" + treatment_brute = "bicaridine" + treatment_fire = "kelotane" + treatment_tox = "charcoal" + req_one_access =list(ACCESS_SYNDICATE) + med_bot_skin = "bezerk" + syndicate_aligned = TRUE + +/obj/item/storage/firstaid/tactical/New() + ..() + if(empty) return + new /obj/item/reagent_containers/hypospray/combat(src) + new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) // Because you ain't got no time to look at what damage dey taking yo + new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) + new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) + new /obj/item/reagent_containers/food/pill/patch/synthflesh(src) + new /obj/item/defibrillator/compact/combat/loaded(src) + new /obj/item/clothing/glasses/hud/health/night(src) + return + +/obj/item/storage/firstaid/tactical/empty + empty =1 + +/obj/item/storage/firstaid/surgery + name = "field surgery kit" + icon_state = "duffel-med" + desc = "A kit for surgery in the field." + max_w_class = WEIGHT_CLASS_BULKY + max_combined_w_class = 21 + storage_slots = 10 + can_hold = list(/obj/item/roller,/obj/item/bonesetter,/obj/item/bonegel, /obj/item/scalpel, /obj/item/hemostat, + /obj/item/cautery, /obj/item/retractor, /obj/item/FixOVein, /obj/item/surgicaldrill, /obj/item/circular_saw) + +/obj/item/storage/firstaid/surgery/New() + ..() + new /obj/item/roller(src) + new /obj/item/bonesetter(src) + new /obj/item/bonegel(src) + new /obj/item/scalpel(src) + new /obj/item/hemostat(src) + new /obj/item/cautery(src) + new /obj/item/retractor(src) + new /obj/item/FixOVein(src) + new /obj/item/surgicaldrill(src) + new /obj/item/circular_saw(src) + +/* + * Pill Bottles + */ + +/obj/item/storage/pill_bottle + name = "pill bottle" + desc = "It's an airtight container for storing medication." + icon_state = "pill_canister" + icon = 'icons/obj/chemical.dmi' + item_state = "contsolid" + w_class = WEIGHT_CLASS_SMALL + can_hold = list(/obj/item/reagent_containers/food/pill) + cant_hold = list(/obj/item/reagent_containers/food/pill/patch) + allow_quick_gather = TRUE + use_to_pickup = TRUE + storage_slots = 50 + max_combined_w_class = 50 + display_contents_with_number = TRUE + var/base_name = "" + var/label_text = "" + var/applying_meds = FALSE //To Prevent spam clicking and generating runtimes from apply a deleting pill multiple times. + var/rapid_intake_message = "unscrews the cap on the pill bottle and begins dumping the entire contents down their throat!" + var/rapid_post_instake_message = "downs the entire bottle of pills in one go!" + var/allow_wrap = TRUE + var/wrapper_color = null + +/obj/item/storage/pill_bottle/New() + ..() + base_name = name + if(allow_wrap) + apply_wrap() + +/obj/item/storage/pill_bottle/proc/apply_wrap() + if(wrapper_color) + overlays.Cut() + var/image/I = image(icon, "pillbottle_wrap") + I.color = wrapper_color + overlays += I + +/obj/item/storage/pill_bottle/attack(mob/M, mob/user) + if(iscarbon(M) && contents.len) + if(applying_meds) + to_chat(user, "You are already applying meds.") + return + applying_meds = TRUE + for(var/obj/item/reagent_containers/food/pill/P in contents) + if(P.attack(M, user)) + applying_meds = FALSE + else + applying_meds = FALSE + break + else + return ..() + +/obj/item/storage/pill_bottle/ert + wrapper_color = COLOR_MAROON + +/obj/item/storage/pill_bottle/ert/New() + ..() + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/charcoal(src) + new /obj/item/reagent_containers/food/pill/charcoal(src) + new /obj/item/reagent_containers/food/pill/charcoal(src) + +/obj/item/storage/pill_bottle/MouseDrop(obj/over_object as obj) // Best utilized if you're a cantankerous doctor with a Vicodin habit. + if(iscarbon(over_object)) + var/mob/living/carbon/C = over_object + if(loc == C && src == C.get_active_hand()) + if(!contents.len) + to_chat(C, "There is nothing in [src]!") + return + C.visible_message("[C] [rapid_intake_message]") + if(do_mob(C, C, 100)) // 10 seconds + for(var/obj/item/reagent_containers/food/pill/P in contents) + P.attack(C, C) + C.visible_message("[C] [rapid_post_instake_message]") + return + + return ..() + +/obj/item/storage/pill_bottle/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/pen) || istype(I, /obj/item/flashlight/pen)) + var/tmp_label = sanitize(input(user, "Enter a label for [name]","Label",label_text)) + if(length(tmp_label) > MAX_NAME_LEN) + to_chat(user, "The label can be at most [MAX_NAME_LEN] characters long.") + else + to_chat(user, "You set the label to \"[tmp_label]\".") + label_text = tmp_label + update_name_label() + else + return ..() + +/obj/item/storage/pill_bottle/proc/update_name_label() + if(label_text == "") + name = base_name + else + name = "[base_name] ([label_text])" + +/obj/item/storage/pill_bottle/patch_pack + name = "Patch Pack" + desc = "It's a container for storing medical patches." + icon_state = "patch_pack" + can_hold = list(/obj/item/reagent_containers/food/pill/patch) + cant_hold = list() + rapid_intake_message = "flips the lid of the Patch Pack open and begins rapidly stamping patches on themselves!" + rapid_post_instake_message = "stamps the entire contents of the Patch Pack all over their entire body!" + allow_wrap = FALSE + +/obj/item/storage/pill_bottle/charcoal + name = "Pill bottle (Charcoal)" + desc = "Contains pills used to counter toxins." + wrapper_color = COLOR_GREEN + + New() + ..() + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + new /obj/item/reagent_containers/food/pill/charcoal( src ) + +/obj/item/storage/pill_bottle/painkillers + name = "Pill Bottle (Salicylic Acid)" + desc = "Contains various pills for minor pain relief." + wrapper_color = COLOR_RED + +/obj/item/storage/pill_bottle/painkillers/New() + ..() + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + new /obj/item/reagent_containers/food/pill/salicylic(src) + +/obj/item/storage/pill_bottle/fakedeath + allow_wrap = FALSE + +/obj/item/storage/pill_bottle/fakedeath/New() + ..() + new /obj/item/reagent_containers/food/pill/fakedeath(src) + new /obj/item/reagent_containers/food/pill/fakedeath(src) + new /obj/item/reagent_containers/food/pill/fakedeath(src) diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm index 3881c0e06e81..b67ba9154e3e 100644 --- a/code/game/objects/items/weapons/storage/lockbox.dm +++ b/code/game/objects/items/weapons/storage/lockbox.dm @@ -1,137 +1,137 @@ -/obj/item/storage/lockbox - name = "lockbox" - desc = "A locked box." - icon_state = "lockbox+l" - item_state = "syringe_kit" - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 14 //The sum of the w_classes of all the items in this storage item. - storage_slots = 4 - req_access = list(ACCESS_ARMORY) - var/locked = 1 - var/broken = 0 - var/icon_locked = "lockbox+l" - var/icon_closed = "lockbox" - var/icon_broken = "lockbox+b" - -/obj/item/storage/lockbox/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) - if(broken) - to_chat(user, "It appears to be broken.") - return - if(check_access(W)) - locked = !locked - if(locked) - icon_state = icon_locked - to_chat(user, "You lock \the [src]!") - if(user.s_active) - user.s_active.close(user) - return - else - icon_state = icon_closed - to_chat(user, "You unlock \the [src]!") - origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D. - return - else - to_chat(user, "Access denied.") - return - else if((istype(W, /obj/item/card/emag) || (istype(W, /obj/item/melee/energy/blade)) && !broken)) - emag_act(user) - return - if(!locked) - ..() - else - to_chat(user, "It's locked!") - return - - -/obj/item/storage/lockbox/show_to(mob/user as mob) - if(locked) - to_chat(user, "It's locked!") - else - ..() - return - -/obj/item/storage/lockbox/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!locked) - return ..() - if(!stop_messages) - to_chat(usr, "[src] is locked!") - return 0 - -/obj/item/storage/lockbox/emag_act(user as mob) - if(!broken) - broken = 1 - locked = 0 - desc = "It appears to be broken." - icon_state = icon_broken - to_chat(user, "You unlock \the [src].") - origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D. - return - -/obj/item/storage/lockbox/hear_talk(mob/living/M as mob, list/message_pieces) - -/obj/item/storage/lockbox/hear_message(mob/living/M as mob, msg) - -/obj/item/storage/lockbox/mindshield - name = "Lockbox (Mindshield Implants)" - req_access = list(ACCESS_SECURITY) - -/obj/item/storage/lockbox/mindshield/New() - ..() - new /obj/item/implantcase/mindshield(src) - new /obj/item/implantcase/mindshield(src) - new /obj/item/implantcase/mindshield(src) - new /obj/item/implanter/mindshield(src) - -/obj/item/storage/lockbox/clusterbang - name = "lockbox (clusterbang)" - desc = "You have a bad feeling about opening this." - req_access = list(ACCESS_SECURITY) - -/obj/item/storage/lockbox/clusterbang/New() - ..() - new /obj/item/grenade/clusterbuster(src) - -/obj/item/storage/lockbox/medal - name = "medal box" - desc = "A locked box used to store medals of honor." - icon_state = "medalbox+l" - item_state = "syringe_kit" - w_class = WEIGHT_CLASS_NORMAL - max_w_class = WEIGHT_CLASS_SMALL - max_combined_w_class = 20 - storage_slots = 12 - req_access = list(ACCESS_CAPTAIN) - icon_locked = "medalbox+l" - icon_closed = "medalbox" - icon_broken = "medalbox+b" - -/obj/item/storage/lockbox/medal/New() - ..() - new /obj/item/clothing/accessory/medal/gold/captain(src) - new /obj/item/clothing/accessory/medal/silver/leadership(src) - new /obj/item/clothing/accessory/medal/silver/valor(src) - new /obj/item/clothing/accessory/medal/heart(src) - -/obj/item/storage/lockbox/t4 - name = "lockbox (T4)" - desc = "Contains three T4 breaching charges." - req_access = list(ACCESS_CENT_SPECOPS) - -/obj/item/storage/lockbox/t4/New() - ..() - for(var/i in 0 to 2) - new /obj/item/grenade/plastic/x4/thermite(src) - -/obj/item/storage/lockbox/research - -/obj/item/storage/lockbox/research/deconstruct(disassembled = TRUE) // Get wrecked, Science nerds - qdel(src) - -/obj/item/storage/lockbox/research/large - name = "Large lockbox" - desc = "A large lockbox" - max_w_class = WEIGHT_CLASS_BULKY - max_combined_w_class = 4 //The sum of the w_classes of all the items in this storage item. - storage_slots = 1 \ No newline at end of file +/obj/item/storage/lockbox + name = "lockbox" + desc = "A locked box." + icon_state = "lockbox+l" + item_state = "syringe_kit" + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 14 //The sum of the w_classes of all the items in this storage item. + storage_slots = 4 + req_access = list(ACCESS_ARMORY) + var/locked = 1 + var/broken = 0 + var/icon_locked = "lockbox+l" + var/icon_closed = "lockbox" + var/icon_broken = "lockbox+b" + +/obj/item/storage/lockbox/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) + if(broken) + to_chat(user, "It appears to be broken.") + return + if(check_access(W)) + locked = !locked + if(locked) + icon_state = icon_locked + to_chat(user, "You lock \the [src]!") + if(user.s_active) + user.s_active.close(user) + return + else + icon_state = icon_closed + to_chat(user, "You unlock \the [src]!") + origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D. + return + else + to_chat(user, "Access denied.") + return + else if((istype(W, /obj/item/card/emag) || (istype(W, /obj/item/melee/energy/blade)) && !broken)) + emag_act(user) + return + if(!locked) + ..() + else + to_chat(user, "It's locked!") + return + + +/obj/item/storage/lockbox/show_to(mob/user as mob) + if(locked) + to_chat(user, "It's locked!") + else + ..() + return + +/obj/item/storage/lockbox/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!locked) + return ..() + if(!stop_messages) + to_chat(usr, "[src] is locked!") + return 0 + +/obj/item/storage/lockbox/emag_act(user as mob) + if(!broken) + broken = 1 + locked = 0 + desc = "It appears to be broken." + icon_state = icon_broken + to_chat(user, "You unlock \the [src].") + origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D. + return + +/obj/item/storage/lockbox/hear_talk(mob/living/M as mob, list/message_pieces) + +/obj/item/storage/lockbox/hear_message(mob/living/M as mob, msg) + +/obj/item/storage/lockbox/mindshield + name = "Lockbox (Mindshield Implants)" + req_access = list(ACCESS_SECURITY) + +/obj/item/storage/lockbox/mindshield/New() + ..() + new /obj/item/implantcase/mindshield(src) + new /obj/item/implantcase/mindshield(src) + new /obj/item/implantcase/mindshield(src) + new /obj/item/implanter/mindshield(src) + +/obj/item/storage/lockbox/clusterbang + name = "lockbox (clusterbang)" + desc = "You have a bad feeling about opening this." + req_access = list(ACCESS_SECURITY) + +/obj/item/storage/lockbox/clusterbang/New() + ..() + new /obj/item/grenade/clusterbuster(src) + +/obj/item/storage/lockbox/medal + name = "medal box" + desc = "A locked box used to store medals of honor." + icon_state = "medalbox+l" + item_state = "syringe_kit" + w_class = WEIGHT_CLASS_NORMAL + max_w_class = WEIGHT_CLASS_SMALL + max_combined_w_class = 20 + storage_slots = 12 + req_access = list(ACCESS_CAPTAIN) + icon_locked = "medalbox+l" + icon_closed = "medalbox" + icon_broken = "medalbox+b" + +/obj/item/storage/lockbox/medal/New() + ..() + new /obj/item/clothing/accessory/medal/gold/captain(src) + new /obj/item/clothing/accessory/medal/silver/leadership(src) + new /obj/item/clothing/accessory/medal/silver/valor(src) + new /obj/item/clothing/accessory/medal/heart(src) + +/obj/item/storage/lockbox/t4 + name = "lockbox (T4)" + desc = "Contains three T4 breaching charges." + req_access = list(ACCESS_CENT_SPECOPS) + +/obj/item/storage/lockbox/t4/New() + ..() + for(var/i in 0 to 2) + new /obj/item/grenade/plastic/x4/thermite(src) + +/obj/item/storage/lockbox/research + +/obj/item/storage/lockbox/research/deconstruct(disassembled = TRUE) // Get wrecked, Science nerds + qdel(src) + +/obj/item/storage/lockbox/research/large + name = "Large lockbox" + desc = "A large lockbox" + max_w_class = WEIGHT_CLASS_BULKY + max_combined_w_class = 4 //The sum of the w_classes of all the items in this storage item. + storage_slots = 1 diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm index a91d1e10f620..357cb043ca40 100644 --- a/code/game/objects/items/weapons/storage/secure.dm +++ b/code/game/objects/items/weapons/storage/secure.dm @@ -1,242 +1,251 @@ -/* - * Absorbs /obj/item/secstorage. - * Reimplements it only slightly to use existing storage functionality. - * - * Contains: - * Secure Briefcase - * Wall Safe - */ - -// ----------------------------- -// Generic Item -// ----------------------------- -/obj/item/storage/secure - name = "secstorage" - var/icon_locking = "secureb" - var/icon_sparking = "securespark" - var/icon_opened = "secure0" - var/locked = 1 - var/code = "" - var/l_code = null - var/l_set = 0 - var/l_setshort = 0 - var/l_hacking = 0 - var/emagged = 0 - var/open = 0 - w_class = WEIGHT_CLASS_NORMAL - max_w_class = WEIGHT_CLASS_SMALL - max_combined_w_class = 14 - -/obj/item/storage/secure/examine(mob/user) - . = ..() - if(in_range(user, src)) - . += "The service panel is [open ? "open" : "closed"]." - -/obj/item/storage/secure/attackby(obj/item/W as obj, mob/user as mob, params) - if(locked) - if((istype(W, /obj/item/melee/energy/blade)) && (!emagged)) - emag_act(user, W) - - if(istype(W, /obj/item/screwdriver)) - if(do_after(user, 20 * W.toolspeed, target = src)) - open = !open - user.show_message("You [open ? "open" : "close"] the service panel.", 1) - return - - if((istype(W, /obj/item/multitool)) && (open == 1) && (!l_hacking)) - user.show_message("Now attempting to reset internal memory, please hold.", 1) - l_hacking = 1 - if(do_after(usr, 100 * W.toolspeed, target = src)) - if(prob(40)) - l_setshort = 1 - l_set = 0 - user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) - sleep(80) - l_setshort = 0 - l_hacking = 0 - else - user.show_message("Unable to reset internal memory.", 1) - l_hacking = 0 - else - l_hacking = 0 - return - //At this point you have exhausted all the special things to do when locked - // ... but it's still locked. - return - - return ..() - -/obj/item/storage/secure/emag_act(user as mob, weapon as obj) - if(!emagged) - emagged = 1 - overlays += image('icons/obj/storage.dmi', icon_sparking) - sleep(6) - overlays = null - overlays += image('icons/obj/storage.dmi', icon_locking) - locked = 0 - if(istype(weapon, /obj/item/melee/energy/blade)) - do_sparks(5, 0, loc) - playsound(loc, 'sound/weapons/blade1.ogg', 50, 1) - playsound(loc, "sparks", 50, 1) - to_chat(user, "You slice through the lock on [src].") - else - to_chat(user, "You short out the lock on [src].") - return - - -/obj/item/storage/secure/MouseDrop(over_object, src_location, over_location) - if(locked) - add_fingerprint(usr) - to_chat(usr, "It's locked!") - return 0 - ..() - -/obj/item/storage/secure/attack_self(mob/user as mob) - user.set_machine(src) - var/dat = text("[]
    \n\nLock Status: []", src, (locked ? "LOCKED" : "UNLOCKED")) - var/message = "Code" - if((l_set == 0) && (!emagged) && (!l_setshort)) - dat += text("

    \n5-DIGIT PASSCODE NOT SET.
    ENTER NEW PASSCODE.
    ") - if(emagged) - dat += text("

    \nLOCKING SYSTEM ERROR - 1701") - if(l_setshort) - dat += text("

    \nALERT: MEMORY SYSTEM ERROR - 6040 201") - message = text("[]", code) - if(!locked) - message = "*****" - dat += {"


    \n>[message]
    \n - 1- - 2- - 3
    \n - 4- - 5- - 6
    \n - 7- - 8- - 9
    \n - R- - 0- - E
    \n
    "} - user << browse(dat, "window=caselock;size=300x280") - -/obj/item/storage/secure/Topic(href, href_list) - ..() - if(usr.incapacitated() || (get_dist(src, usr) > 1)) - return - if(href_list["type"]) - if(href_list["type"] == "E") - if((l_set == 0) && (length(code) == 5) && (!l_setshort) && (code != "ERROR")) - l_code = code - l_set = 1 - else if((code == l_code) && (emagged == 0) && (l_set == 1)) - locked = 0 - overlays = null - overlays += image('icons/obj/storage.dmi', icon_opened) - code = null - else - code = "ERROR" - else - if((href_list["type"] == "R") && (emagged == 0) && (!l_setshort)) - locked = 1 - overlays = null - code = null - close(usr) - else - code += text("[]", href_list["type"]) - if(length(code) > 5) - code = "ERROR" - add_fingerprint(usr) - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - return - return - -/obj/item/storage/secure/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!locked) - return ..() - if(!stop_messages) - to_chat(usr, "[src] is locked!") - return 0 - -/obj/item/storage/secure/hear_talk(mob/living/M as mob, list/message_pieces) - return - -/obj/item/storage/secure/hear_message(mob/living/M as mob, msg) - return - -// ----------------------------- -// Secure Briefcase -// ----------------------------- -/obj/item/storage/secure/briefcase - name = "secure briefcase" - desc = "A large briefcase with a digital locking system." - icon = 'icons/obj/storage.dmi' - icon_state = "secure" - item_state = "sec-case" - flags = CONDUCT - hitsound = "swing_hit" - force = 8 - throw_speed = 2 - throw_range = 4 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 21 - attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") - -/obj/item/storage/secure/briefcase/New() - ..() - handle_item_insertion(new /obj/item/paper, 1) - handle_item_insertion(new /obj/item/pen, 1) - -/obj/item/storage/secure/briefcase/attack_hand(mob/user as mob) - if((loc == user) && (locked == 1)) - to_chat(usr, "[src] is locked and cannot be opened!") - else if((loc == user) && !locked) - playsound(loc, "rustle", 50, 1, -5) - if(user.s_active) - user.s_active.close(user) //Close and re-open - show_to(user) - else - ..() - for(var/mob/M in range(1)) - if(M.s_active == src) - close(M) - orient2hud(user) - add_fingerprint(user) - return - -//Syndie variant of Secure Briefcase. Contains space cash, slightly more robust. -/obj/item/storage/secure/briefcase/syndie - force = 15 - -/obj/item/storage/secure/briefcase/syndie/New() - ..() - for(var/i = 0, i < storage_slots - 2, i++) - handle_item_insertion(new /obj/item/stack/spacecash/c1000, 1) - -// ----------------------------- -// Secure Safe -// ----------------------------- - -/obj/item/storage/secure/safe - name = "secure safe" - icon = 'icons/obj/storage.dmi' - icon_state = "safe" - icon_opened = "safe0" - icon_locking = "safeb" - icon_sparking = "safespark" - force = 8 - w_class = WEIGHT_CLASS_HUGE - max_w_class = 8 - anchored = 1 - density = 0 - cant_hold = list(/obj/item/storage/secure/briefcase) - -/obj/item/storage/secure/safe/New() - ..() - handle_item_insertion(new /obj/item/paper, 1) - handle_item_insertion(new /obj/item/pen, 1) - -/obj/item/storage/secure/safe/attack_hand(mob/user as mob) - return attack_self(user) +/* + * Absorbs /obj/item/secstorage. + * Reimplements it only slightly to use existing storage functionality. + * + * Contains: + * Secure Briefcase + * Wall Safe + */ + +// ----------------------------- +// Generic Item +// ----------------------------- +/obj/item/storage/secure + name = "secstorage" + var/icon_locking = "secureb" + var/icon_sparking = "securespark" + var/icon_opened = "secure0" + var/locked = 1 + var/code = "" + var/l_code = null + var/l_set = 0 + var/l_setshort = 0 + var/l_hacking = 0 + var/emagged = 0 + var/open = 0 + w_class = WEIGHT_CLASS_NORMAL + max_w_class = WEIGHT_CLASS_SMALL + max_combined_w_class = 14 + +/obj/item/storage/secure/examine(mob/user) + . = ..() + if(in_range(user, src)) + . += "The service panel is [open ? "open" : "closed"]." + +/obj/item/storage/secure/attackby(obj/item/W as obj, mob/user as mob, params) + if(locked) + if((istype(W, /obj/item/melee/energy/blade)) && (!emagged)) + emag_act(user, W) + + if(istype(W, /obj/item/screwdriver)) + if(do_after(user, 20 * W.toolspeed, target = src)) + open = !open + user.show_message("You [open ? "open" : "close"] the service panel.", 1) + return + + if((istype(W, /obj/item/multitool)) && (open == 1) && (!l_hacking)) + user.show_message("Now attempting to reset internal memory, please hold.", 1) + l_hacking = 1 + if(do_after(usr, 100 * W.toolspeed, target = src)) + if(prob(40)) + l_setshort = 1 + l_set = 0 + user.show_message("Internal memory reset. Please give it a few seconds to reinitialize.", 1) + sleep(80) + l_setshort = 0 + l_hacking = 0 + else + user.show_message("Unable to reset internal memory.", 1) + l_hacking = 0 + else + l_hacking = 0 + return + //At this point you have exhausted all the special things to do when locked + // ... but it's still locked. + return + + return ..() + +/obj/item/storage/secure/emag_act(user as mob, weapon as obj) + if(!emagged) + emagged = 1 + overlays += image('icons/obj/storage.dmi', icon_sparking) + sleep(6) + overlays = null + overlays += image('icons/obj/storage.dmi', icon_locking) + locked = 0 + if(istype(weapon, /obj/item/melee/energy/blade)) + do_sparks(5, 0, loc) + playsound(loc, 'sound/weapons/blade1.ogg', 50, 1) + playsound(loc, "sparks", 50, 1) + to_chat(user, "You slice through the lock on [src].") + else + to_chat(user, "You short out the lock on [src].") + return + +/obj/item/storage/secure/AltClick(mob/user) + if(!try_to_open()) + return FALSE + return ..() + +/obj/item/storage/secure/MouseDrop(over_object, src_location, over_location) + if(!try_to_open()) + return FALSE + return ..() + +/obj/item/storage/secure/proc/try_to_open() + if(locked) + add_fingerprint(usr) + to_chat(usr, "It's locked!") + return FALSE + return TRUE + +/obj/item/storage/secure/attack_self(mob/user as mob) + user.set_machine(src) + var/dat = text("[]
    \n\nLock Status: []", src, (locked ? "LOCKED" : "UNLOCKED")) + var/message = "Code" + if((l_set == 0) && (!emagged) && (!l_setshort)) + dat += text("

    \n5-DIGIT PASSCODE NOT SET.
    ENTER NEW PASSCODE.
    ") + if(emagged) + dat += text("

    \nLOCKING SYSTEM ERROR - 1701") + if(l_setshort) + dat += text("

    \nALERT: MEMORY SYSTEM ERROR - 6040 201") + message = text("[]", code) + if(!locked) + message = "*****" + dat += {"


    \n>[message]
    \n + 1- + 2- + 3
    \n + 4- + 5- + 6
    \n + 7- + 8- + 9
    \n + R- + 0- + E
    \n
    "} + user << browse(dat, "window=caselock;size=300x280") + +/obj/item/storage/secure/Topic(href, href_list) + ..() + if(usr.incapacitated() || (get_dist(src, usr) > 1)) + return + if(href_list["type"]) + if(href_list["type"] == "E") + if((l_set == 0) && (length(code) == 5) && (!l_setshort) && (code != "ERROR")) + l_code = code + l_set = 1 + else if((code == l_code) && (emagged == 0) && (l_set == 1)) + locked = 0 + overlays = null + overlays += image('icons/obj/storage.dmi', icon_opened) + code = null + else + code = "ERROR" + else + if((href_list["type"] == "R") && (emagged == 0) && (!l_setshort)) + locked = 1 + overlays = null + code = null + close(usr) + else + code += text("[]", href_list["type"]) + if(length(code) > 5) + code = "ERROR" + add_fingerprint(usr) + for(var/mob/M in viewers(1, loc)) + if((M.client && M.machine == src)) + attack_self(M) + return + return + +/obj/item/storage/secure/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!locked) + return ..() + if(!stop_messages) + to_chat(usr, "[src] is locked!") + return 0 + +/obj/item/storage/secure/hear_talk(mob/living/M as mob, list/message_pieces) + return + +/obj/item/storage/secure/hear_message(mob/living/M as mob, msg) + return + +// ----------------------------- +// Secure Briefcase +// ----------------------------- +/obj/item/storage/secure/briefcase + name = "secure briefcase" + desc = "A large briefcase with a digital locking system." + icon = 'icons/obj/storage.dmi' + icon_state = "secure" + item_state = "sec-case" + flags = CONDUCT + hitsound = "swing_hit" + force = 8 + throw_speed = 2 + throw_range = 4 + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 21 + attack_verb = list("bashed", "battered", "bludgeoned", "thrashed", "whacked") + +/obj/item/storage/secure/briefcase/New() + ..() + handle_item_insertion(new /obj/item/paper, 1) + handle_item_insertion(new /obj/item/pen, 1) + +/obj/item/storage/secure/briefcase/attack_hand(mob/user as mob) + if((loc == user) && (locked == 1)) + to_chat(usr, "[src] is locked and cannot be opened!") + else if((loc == user) && !locked) + playsound(loc, "rustle", 50, 1, -5) + if(user.s_active) + user.s_active.close(user) //Close and re-open + show_to(user) + else + ..() + for(var/mob/M in range(1)) + if(M.s_active == src) + close(M) + orient2hud(user) + add_fingerprint(user) + return + +//Syndie variant of Secure Briefcase. Contains space cash, slightly more robust. +/obj/item/storage/secure/briefcase/syndie + force = 15 + +/obj/item/storage/secure/briefcase/syndie/New() + ..() + for(var/i = 0, i < storage_slots - 2, i++) + handle_item_insertion(new /obj/item/stack/spacecash/c1000, 1) + +// ----------------------------- +// Secure Safe +// ----------------------------- + +/obj/item/storage/secure/safe + name = "secure safe" + icon = 'icons/obj/storage.dmi' + icon_state = "safe" + icon_opened = "safe0" + icon_locking = "safeb" + icon_sparking = "safespark" + force = 8 + w_class = WEIGHT_CLASS_HUGE + max_w_class = 8 + anchored = 1 + density = 0 + cant_hold = list(/obj/item/storage/secure/briefcase) + +/obj/item/storage/secure/safe/New() + ..() + handle_item_insertion(new /obj/item/paper, 1) + handle_item_insertion(new /obj/item/pen, 1) + +/obj/item/storage/secure/safe/attack_hand(mob/user as mob) + return attack_self(user) diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index b57bd8b9e503..1ff2b2f2c56e 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -1,575 +1,583 @@ -// To clarify: -// For use_to_pickup and allow_quick_gather functionality, -// see item/attackby() (/game/objects/items.dm, params) -// Do not remove this functionality without good reason, cough reagent_containers cough. -// -Sayu - - -/obj/item/storage - name = "storage" - icon = 'icons/obj/storage.dmi' - w_class = WEIGHT_CLASS_NORMAL - var/silent = 0 // No message on putting items in - var/list/can_hold = new/list() //List of objects which this item can store (if set, it can't store anything else) - var/list/cant_hold = new/list() //List of objects which this item can't store (in effect only if can_hold isn't set) - var/max_w_class = WEIGHT_CLASS_SMALL //Max size of objects that this object can store (in effect only if can_hold isn't set) - var/max_combined_w_class = 14 //The sum of the w_classes of all the items in this storage item. - var/storage_slots = 7 //The number of storage slots in this container. - var/obj/screen/storage/boxes = null - var/obj/screen/close/closer = null - var/use_to_pickup //Set this to make it possible to use this item in an inverse way, so you can have the item in your hand and click items on the floor to pick them up. - var/display_contents_with_number //Set this to make the storage item group contents of the same type and display them as a number. - var/allow_quick_empty //Set this variable to allow the object to have the 'empty' verb, which dumps all the contents on the floor. - var/allow_quick_gather //Set this variable to allow the object to have the 'toggle mode' verb, which quickly collects all items from a tile. - var/collection_mode = 1; //0 = pick one at a time, 1 = pick all on tile - var/use_sound = "rustle" //sound played when used. null for no sound. - -/obj/item/storage/MouseDrop(obj/over_object as obj) - if(ishuman(usr)) //so monkeys can take off their backpacks -- Urist - var/mob/M = usr - - if(istype(M.loc,/obj/mecha) || M.incapacitated(FALSE, TRUE, TRUE)) // Stops inventory actions in a mech as well as while being incapacitated - return - - if(over_object == M && Adjacent(M)) // this must come before the screen objects only block - orient2hud(M) // dunno why it wasn't before - if(M.s_active) - M.s_active.close(M) - show_to(M) - return - - if((istype(over_object, /obj/structure/table) || istype(over_object, /turf/simulated/floor)) \ - && contents.len && loc == usr && !usr.stat && !usr.restrained() && usr.canmove && over_object.Adjacent(usr) \ - && !istype(src, /obj/item/storage/lockbox)) - var/turf/T = get_turf(over_object) - if(istype(over_object, /turf/simulated/floor)) - if(get_turf(usr) != T) - return // Can only empty containers onto the floor under you - if("Yes" != alert(usr,"Empty \the [src] onto \the [T]?","Confirm","Yes","No")) - return - if(!(usr && over_object && contents.len && loc == usr && !usr.stat && !usr.restrained() && usr.canmove && get_turf(usr) == T)) - return // Something happened while the player was thinking - hide_from(usr) - usr.face_atom(over_object) - usr.visible_message("[usr] empties \the [src] onto \the [over_object].", - "You empty \the [src] onto \the [over_object].") - for(var/obj/item/I in contents) - remove_from_storage(I, T) - update_icon() // For content-sensitive icons - return - - if(!( istype(over_object, /obj/screen) )) - return ..() - if(!(src.loc == usr) || (src.loc && src.loc.loc == usr)) - return - playsound(src.loc, "rustle", 50, 1, -5) - if(!( M.restrained() ) && !( M.stat )) - switch(over_object.name) - if("r_hand") - if(!M.unEquip(src)) - return - M.put_in_r_hand(src) - if("l_hand") - if(!M.unEquip(src)) - return - M.put_in_l_hand(src) - src.add_fingerprint(usr) - return - if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) - if(usr.s_active) - usr.s_active.close(usr) - src.show_to(usr) - return - return - - -/obj/item/storage/proc/return_inv() - - var/list/L = list( ) - - L += src.contents - - for(var/obj/item/storage/S in src) - L += S.return_inv() - for(var/obj/item/gift/G in src) - L += G.gift - if(istype(G.gift, /obj/item/storage)) - L += G.gift:return_inv() - for(var/obj/item/folder/F in src) - L += F.contents - return L - -/obj/item/storage/proc/show_to(mob/user as mob) - if(!user.client) - return - if(user.s_active != src) - for(var/obj/item/I in src) - if(I.on_found(user)) - return - if(user.s_active) - user.s_active.hide_from(user) - user.client.screen -= src.boxes - user.client.screen -= src.closer - user.client.screen -= src.contents - user.client.screen += src.boxes - user.client.screen += src.closer - user.client.screen += src.contents - user.s_active = src - return - -/obj/item/storage/proc/hide_from(mob/user as mob) - - if(!user.client) - return - user.client.screen -= src.boxes - user.client.screen -= src.closer - user.client.screen -= src.contents - if(user.s_active == src) - user.s_active = null - return - -/obj/item/storage/proc/open(mob/user as mob) - if(src.use_sound) - playsound(src.loc, src.use_sound, 50, 1, -5) - - orient2hud(user) - if(user.s_active) - user.s_active.close(user) - show_to(user) - -/obj/item/storage/proc/close(mob/user as mob) - - src.hide_from(user) - user.s_active = null - return - -//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right. -//The numbers are calculated from the bottom-left The bottom-left slot being 1,1. -/obj/item/storage/proc/orient_objs(tx, ty, mx, my) - var/cx = tx - var/cy = ty - src.boxes.screen_loc = "[tx]:,[ty] to [mx],[my]" - for(var/obj/O in src.contents) - O.screen_loc = "[cx],[cy]" - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - cx++ - if(cx > mx) - cx = tx - cy-- - src.closer.screen_loc = "[mx+1],[my]" - return - -//This proc draws out the inventory and places the items on it. It uses the standard position. -/obj/item/storage/proc/standard_orient_objs(var/rows, var/cols, var/list/obj/item/display_contents) - var/cx = 4 - var/cy = 2+rows - src.boxes.screen_loc = "4:16,2:16 to [4+cols]:16,[2+rows]:16" - - if(display_contents_with_number) - for(var/datum/numbered_display/ND in display_contents) - ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE - ND.sample_object.screen_loc = "[cx]:16,[cy]:16" - ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" - ND.sample_object.layer = ABOVE_HUD_LAYER - ND.sample_object.plane = ABOVE_HUD_PLANE - cx++ - if(cx > (4+cols)) - cx = 4 - cy-- - else - for(var/obj/O in contents) - O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" - O.screen_loc = "[cx]:16,[cy]:16" - O.maptext = "" - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - cx++ - if(cx > (4+cols)) - cx = 4 - cy-- - src.closer.screen_loc = "[4+cols+1]:16,2:16" - return - -/datum/numbered_display - var/obj/item/sample_object - var/number - - New(obj/item/sample as obj) - if(!istype(sample)) - qdel(src) - sample_object = sample - number = 1 - -//This proc determins the size of the inventory to be displayed. Please touch it only if you know what you're doing. -/obj/item/storage/proc/orient2hud(mob/user as mob) - - var/adjusted_contents = contents.len - - //Numbered contents display - var/list/datum/numbered_display/numbered_contents - if(display_contents_with_number) - numbered_contents = list() - adjusted_contents = 0 - for(var/obj/item/I in contents) - var/found = 0 - for(var/datum/numbered_display/ND in numbered_contents) - if(ND.sample_object.type == I.type && ND.sample_object.name == I.name) - ND.number++ - found = 1 - break - if(!found) - adjusted_contents++ - numbered_contents.Add( new/datum/numbered_display(I) ) - - //var/mob/living/carbon/human/H = user - var/row_num = 0 - var/col_count = min(7,storage_slots) -1 - if(adjusted_contents > 7) - row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. - src.standard_orient_objs(row_num, col_count, numbered_contents) - return - -//This proc return 1 if the item can be picked up and 0 if it can't. -//Set the stop_messages to stop it from printing messages -/obj/item/storage/proc/can_be_inserted(obj/item/W as obj, stop_messages = 0) - if(!istype(W) || (W.flags & ABSTRACT)) return //Not an item - - if(src.loc == W) - return 0 //Means the item is already in the storage item - if(contents.len >= storage_slots) - if(!stop_messages) - to_chat(usr, "[W] won't fit in [src], make some space!") - return 0 //Storage item is full - - if(can_hold.len) - if(!is_type_in_typecache(W, can_hold)) - if(!stop_messages) - to_chat(usr, "[src] cannot hold [W].") - return 0 - - if(is_type_in_typecache(W, cant_hold)) //Check for specific items which this container can't hold. - if(!stop_messages) - to_chat(usr, "[src] cannot hold [W].") - return 0 - - if(W.w_class > max_w_class) - if(!stop_messages) - to_chat(usr, "[W] is too big for [src].") - return 0 - - var/sum_w_class = W.w_class - for(var/obj/item/I in contents) - sum_w_class += I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it. - - if(sum_w_class > max_combined_w_class) - if(!stop_messages) - to_chat(usr, "[src] is full, make some space.") - return 0 - - if(W.w_class >= src.w_class && (istype(W, /obj/item/storage))) - if(!istype(src, /obj/item/storage/backpack/holding)) //bohs should be able to hold backpacks again. The override for putting a boh in a boh is in backpack.dm. - if(!stop_messages) - to_chat(usr, "[src] cannot hold [W] as it's a storage item of the same size.") - return 0 //To prevent the stacking of same sized storage items. - - if(W.flags & NODROP) //SHOULD be handled in unEquip, but better safe than sorry. - to_chat(usr, "\the [W] is stuck to your hand, you can't put it in \the [src]") - return 0 - - return 1 - -//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted() -//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once, -//such as when picking up all the items on a tile with one click. -/obj/item/storage/proc/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) - if(!istype(W)) - return 0 - if(usr) - if(!usr.unEquip(W)) - return 0 - usr.update_icons() //update our overlays - if(silent) - prevent_warning = 1 - W.forceMove(src) - W.on_enter_storage(src) - if(usr) - if(usr.client && usr.s_active != src) - usr.client.screen -= W - W.dropped(usr) - add_fingerprint(usr) - - if(!prevent_warning && !istype(W, /obj/item/gun/energy/kinetic_accelerator/crossbow)) - for(var/mob/M in viewers(usr, null)) - if(M == usr) - to_chat(usr, "You put the [W] into [src].") - else if(M in range(1)) //If someone is standing close enough, they can tell what it is... - M.show_message("[usr] puts [W] into [src].") - else if(W && W.w_class >= WEIGHT_CLASS_NORMAL) //Otherwise they can only see large or normal items from a distance... - M.show_message("[usr] puts [W] into [src].") - - src.orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - W.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt - W.in_inventory = TRUE - update_icon() - return 1 - -//Call this proc to handle the removal of an item from the storage item. The item will be moved to the atom sent as new_target -/obj/item/storage/proc/remove_from_storage(obj/item/W as obj, atom/new_location) - if(!istype(W)) return 0 - - if(istype(src, /obj/item/storage/fancy)) - var/obj/item/storage/fancy/F = src - F.update_icon(1) - - for(var/mob/M in range(1, src.loc)) - if(M.s_active == src) - if(M.client) - M.client.screen -= W - - if(new_location) - if(ismob(loc)) - W.dropped(usr) - if(ismob(new_location)) - W.layer = ABOVE_HUD_LAYER - W.plane = ABOVE_HUD_PLANE - else - W.layer = initial(W.layer) - W.plane = initial(W.plane) - W.forceMove(new_location) - else - W.forceMove(get_turf(src)) - - if(usr) - src.orient2hud(usr) - if(usr.s_active) - usr.s_active.show_to(usr) - if(W.maptext) - W.maptext = "" - W.on_exit_storage(src) - update_icon() - W.mouse_opacity = initial(W.mouse_opacity) - return 1 - -/obj/item/storage/Exited(atom/A, loc) - remove_from_storage(A, loc) //worry not, comrade; this only gets called once - ..() - -/obj/item/storage/deconstruct(disassembled = TRUE) - var/drop_loc = loc - if(ismob(loc)) - drop_loc = get_turf(src) - for(var/obj/item/I in contents) - remove_from_storage(I, drop_loc) - qdel(src) - -//This proc is called when you want to place an item into the storage item. -/obj/item/storage/attackby(obj/item/I, mob/user, params) - ..() - if(istype(I, /obj/item/hand_labeler)) - var/obj/item/hand_labeler/labeler = I - if(labeler.mode) - return FALSE - . = 1 //no afterattack - if(isrobot(user)) - return //Robots can't interact with storage items. - - if(!can_be_inserted(I)) - if(contents.len >= storage_slots) //don't use items on the backpack if they don't fit - return TRUE - return FALSE - - handle_item_insertion(I) - -/obj/item/storage/attack_hand(mob/user as mob) - playsound(src.loc, "rustle", 50, 1, -5) - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.l_store == src && !H.get_active_hand()) //Prevents opening if it's in a pocket. - H.put_in_hands(src) - H.l_store = null - return - if(H.r_store == src && !H.get_active_hand()) - H.put_in_hands(src) - H.r_store = null - return - - src.orient2hud(user) - if(src.loc == user) - if(user.s_active) - user.s_active.close(user) - src.show_to(user) - else - ..() - for(var/mob/M in range(1)) - if(M.s_active == src) - src.close(M) - src.add_fingerprint(user) - return - -/obj/item/storage/verb/toggle_gathering_mode() - set name = "Switch Gathering Method" - set category = "Object" - - collection_mode = !collection_mode - switch(collection_mode) - if(1) - to_chat(usr, "[src] now picks up all items in a tile at once.") - if(0) - to_chat(usr, "[src] now picks up one item at a time.") - - -/obj/item/storage/verb/quick_empty() - set name = "Empty Contents" - set category = "Object" - - if((!ishuman(usr) && (src.loc != usr)) || usr.stat || usr.restrained()) - return - - var/turf/T = get_turf(src) - hide_from(usr) - for(var/obj/item/I in contents) - remove_from_storage(I, T) - CHECK_TICK - -/obj/item/storage/New() - ..() - can_hold = typecacheof(can_hold) - cant_hold = typecacheof(cant_hold) - - if(allow_quick_empty) - verbs += /obj/item/storage/verb/quick_empty - else - verbs -= /obj/item/storage/verb/quick_empty - - if(allow_quick_gather) - verbs += /obj/item/storage/verb/toggle_gathering_mode - else - verbs -= /obj/item/storage/verb/toggle_gathering_mode - - boxes = new /obj/screen/storage( ) - boxes.name = "storage" - boxes.master = src - boxes.icon_state = "block" - boxes.screen_loc = "7,7 to 10,8" - boxes.layer = HUD_LAYER - boxes.plane = HUD_PLANE - closer = new /obj/screen/close( ) - closer.master = src - closer.icon_state = "backpack_close" - closer.layer = ABOVE_HUD_LAYER - closer.plane = ABOVE_HUD_PLANE - orient2hud() - -/obj/item/storage/Destroy() - for(var/obj/O in contents) - O.mouse_opacity = initial(O.mouse_opacity) - - QDEL_NULL(boxes) - QDEL_NULL(closer) - return ..() - -/obj/item/storage/emp_act(severity) - if(!istype(loc, /mob/living)) - for(var/obj/O in contents) - O.emp_act(severity) - ..() - -/obj/item/storage/hear_talk(mob/living/M as mob, list/message_pieces) - ..() - for(var/obj/O in contents) - O.hear_talk(M, message_pieces) - -/obj/item/storage/hear_message(mob/living/M as mob, msg) - ..() - for(var/obj/O in contents) - O.hear_message(M, msg) - -/obj/item/storage/attack_self(mob/user) - - //Clicking on itself will empty it, if it has the verb to do that. - if(user.is_in_active_hand(src)) - if(verbs.Find(/obj/item/storage/verb/quick_empty)) - quick_empty() - -//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). -//Returns -1 if the atom was not found on container. -/atom/proc/storage_depth(atom/container) - var/depth = 0 - var/atom/cur_atom = src - - while(cur_atom && !(cur_atom in container.contents)) - if(isarea(cur_atom)) - return -1 - if(istype(cur_atom.loc, /obj/item/storage)) - depth++ - cur_atom = cur_atom.loc - - if(!cur_atom) - return -1 //inside something with a null loc. - - return depth - -//Like storage depth, but returns the depth to the nearest turf -//Returns -1 if no top level turf (a loc was null somewhere, or a non-turf atom's loc was an area somehow). -/atom/proc/storage_depth_turf() - var/depth = 0 - var/atom/cur_atom = src - - while(cur_atom && !isturf(cur_atom)) - if(isarea(cur_atom)) - return -1 - if(istype(cur_atom.loc, /obj/item/storage)) - depth++ - cur_atom = cur_atom.loc - - if(!cur_atom) - return -1 //inside something with a null loc. - - return depth - -/obj/item/storage/serialize() - var data = ..() - var/list/content_list = list() - data["content"] = content_list - data["slots"] = storage_slots - data["max_w_class"] = max_w_class - data["max_c_w_class"] = max_combined_w_class - for(var/thing in contents) - var/atom/movable/AM = thing - // This code does not watch out for infinite loops - // But then again a tesseract would destroy the server anyways - // Also I wish I could just insert a list instead of it reading it the wrong way - content_list.len++ - content_list[content_list.len] = AM.serialize() - return data - -/obj/item/storage/deserialize(list/data) - if(isnum(data["slots"])) - storage_slots = data["slots"] - if(isnum(data["max_w_class"])) - max_w_class = data["max_w_class"] - if(isnum(data["max_c_w_class"])) - max_combined_w_class = data["max_c_w_class"] - for(var/thing in contents) - qdel(thing) // out with the old - for(var/thing in data["content"]) - if(islist(thing)) - list_to_object(thing, src) - else if(thing == null) - log_runtime(EXCEPTION("Null entry found in storage/deserialize."), src) - else - log_runtime(EXCEPTION("Non-list thing found in storage/deserialize."), src, list("Thing: [thing]")) - ..() - -/obj/item/storage/AllowDrop() - return TRUE - -/obj/item/storage/ex_act(severity) - for(var/atom/A in contents) - A.ex_act(severity) - CHECK_TICK - ..() \ No newline at end of file +// To clarify: +// For use_to_pickup and allow_quick_gather functionality, +// see item/attackby() (/game/objects/items.dm, params) +// Do not remove this functionality without good reason, cough reagent_containers cough. +// -Sayu + + +/obj/item/storage + name = "storage" + icon = 'icons/obj/storage.dmi' + w_class = WEIGHT_CLASS_NORMAL + var/silent = 0 // No message on putting items in + var/list/can_hold = new/list() //List of objects which this item can store (if set, it can't store anything else) + var/list/cant_hold = new/list() //List of objects which this item can't store (in effect only if can_hold isn't set) + var/max_w_class = WEIGHT_CLASS_SMALL //Max size of objects that this object can store (in effect only if can_hold isn't set) + var/max_combined_w_class = 14 //The sum of the w_classes of all the items in this storage item. + var/storage_slots = 7 //The number of storage slots in this container. + var/obj/screen/storage/boxes = null + var/obj/screen/close/closer = null + var/use_to_pickup //Set this to make it possible to use this item in an inverse way, so you can have the item in your hand and click items on the floor to pick them up. + var/display_contents_with_number //Set this to make the storage item group contents of the same type and display them as a number. + var/allow_quick_empty //Set this variable to allow the object to have the 'empty' verb, which dumps all the contents on the floor. + var/allow_quick_gather //Set this variable to allow the object to have the 'toggle mode' verb, which quickly collects all items from a tile. + var/collection_mode = 1; //0 = pick one at a time, 1 = pick all on tile + var/use_sound = "rustle" //sound played when used. null for no sound. + +/obj/item/storage/MouseDrop(obj/over_object as obj) + if(ishuman(usr)) //so monkeys can take off their backpacks -- Urist + var/mob/M = usr + + if(istype(M.loc,/obj/mecha) || M.incapacitated(FALSE, TRUE, TRUE)) // Stops inventory actions in a mech as well as while being incapacitated + return + + if(over_object == M && Adjacent(M)) // this must come before the screen objects only block + orient2hud(M) // dunno why it wasn't before + if(M.s_active) + M.s_active.close(M) + show_to(M) + return + + if((istype(over_object, /obj/structure/table) || istype(over_object, /turf/simulated/floor)) \ + && contents.len && loc == usr && !usr.stat && !usr.restrained() && usr.canmove && over_object.Adjacent(usr) \ + && !istype(src, /obj/item/storage/lockbox)) + var/turf/T = get_turf(over_object) + if(istype(over_object, /turf/simulated/floor)) + if(get_turf(usr) != T) + return // Can only empty containers onto the floor under you + if("Yes" != alert(usr,"Empty \the [src] onto \the [T]?","Confirm","Yes","No")) + return + if(!(usr && over_object && contents.len && loc == usr && !usr.stat && !usr.restrained() && usr.canmove && get_turf(usr) == T)) + return // Something happened while the player was thinking + hide_from(usr) + usr.face_atom(over_object) + usr.visible_message("[usr] empties \the [src] onto \the [over_object].", + "You empty \the [src] onto \the [over_object].") + for(var/obj/item/I in contents) + remove_from_storage(I, T) + update_icon() // For content-sensitive icons + return + + if(!( istype(over_object, /obj/screen) )) + return ..() + if(!(src.loc == usr) || (src.loc && src.loc.loc == usr)) + return + playsound(src.loc, "rustle", 50, 1, -5) + if(!( M.restrained() ) && !( M.stat )) + switch(over_object.name) + if("r_hand") + if(!M.unEquip(src)) + return + M.put_in_r_hand(src) + if("l_hand") + if(!M.unEquip(src)) + return + M.put_in_l_hand(src) + src.add_fingerprint(usr) + return + if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) + if(usr.s_active) + usr.s_active.close(usr) + src.show_to(usr) + return + return + +/obj/item/storage/AltClick(mob/user) + if(Adjacent(user) && !user.incapacitated(FALSE, TRUE, TRUE)) + orient2hud(user) + if(user.s_active) + user.s_active.close(user) + show_to(user) + playsound(loc, "rustle", 50, 1, -5) + add_fingerprint(user) + +/obj/item/storage/proc/return_inv() + + var/list/L = list( ) + + L += src.contents + + for(var/obj/item/storage/S in src) + L += S.return_inv() + for(var/obj/item/gift/G in src) + L += G.gift + if(istype(G.gift, /obj/item/storage)) + L += G.gift:return_inv() + for(var/obj/item/folder/F in src) + L += F.contents + return L + +/obj/item/storage/proc/show_to(mob/user as mob) + if(!user.client) + return + if(user.s_active != src) + for(var/obj/item/I in src) + if(I.on_found(user)) + return + if(user.s_active) + user.s_active.hide_from(user) + user.client.screen -= src.boxes + user.client.screen -= src.closer + user.client.screen -= src.contents + user.client.screen += src.boxes + user.client.screen += src.closer + user.client.screen += src.contents + user.s_active = src + return + +/obj/item/storage/proc/hide_from(mob/user as mob) + + if(!user.client) + return + user.client.screen -= src.boxes + user.client.screen -= src.closer + user.client.screen -= src.contents + if(user.s_active == src) + user.s_active = null + return + +/obj/item/storage/proc/open(mob/user as mob) + if(src.use_sound) + playsound(src.loc, src.use_sound, 50, 1, -5) + + orient2hud(user) + if(user.s_active) + user.s_active.close(user) + show_to(user) + +/obj/item/storage/proc/close(mob/user as mob) + + src.hide_from(user) + user.s_active = null + return + +//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right. +//The numbers are calculated from the bottom-left The bottom-left slot being 1,1. +/obj/item/storage/proc/orient_objs(tx, ty, mx, my) + var/cx = tx + var/cy = ty + src.boxes.screen_loc = "[tx]:,[ty] to [mx],[my]" + for(var/obj/O in src.contents) + O.screen_loc = "[cx],[cy]" + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + cx++ + if(cx > mx) + cx = tx + cy-- + src.closer.screen_loc = "[mx+1],[my]" + return + +//This proc draws out the inventory and places the items on it. It uses the standard position. +/obj/item/storage/proc/standard_orient_objs(var/rows, var/cols, var/list/obj/item/display_contents) + var/cx = 4 + var/cy = 2+rows + src.boxes.screen_loc = "4:16,2:16 to [4+cols]:16,[2+rows]:16" + + if(display_contents_with_number) + for(var/datum/numbered_display/ND in display_contents) + ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE + ND.sample_object.screen_loc = "[cx]:16,[cy]:16" + ND.sample_object.maptext = "[(ND.number > 1)? "[ND.number]" : ""]" + ND.sample_object.layer = ABOVE_HUD_LAYER + ND.sample_object.plane = ABOVE_HUD_PLANE + cx++ + if(cx > (4+cols)) + cx = 4 + cy-- + else + for(var/obj/O in contents) + O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip" + O.screen_loc = "[cx]:16,[cy]:16" + O.maptext = "" + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + cx++ + if(cx > (4+cols)) + cx = 4 + cy-- + src.closer.screen_loc = "[4+cols+1]:16,2:16" + return + +/datum/numbered_display + var/obj/item/sample_object + var/number + + New(obj/item/sample as obj) + if(!istype(sample)) + qdel(src) + sample_object = sample + number = 1 + +//This proc determins the size of the inventory to be displayed. Please touch it only if you know what you're doing. +/obj/item/storage/proc/orient2hud(mob/user as mob) + + var/adjusted_contents = contents.len + + //Numbered contents display + var/list/datum/numbered_display/numbered_contents + if(display_contents_with_number) + numbered_contents = list() + adjusted_contents = 0 + for(var/obj/item/I in contents) + var/found = 0 + for(var/datum/numbered_display/ND in numbered_contents) + if(ND.sample_object.type == I.type && ND.sample_object.name == I.name) + ND.number++ + found = 1 + break + if(!found) + adjusted_contents++ + numbered_contents.Add( new/datum/numbered_display(I) ) + + //var/mob/living/carbon/human/H = user + var/row_num = 0 + var/col_count = min(7,storage_slots) -1 + if(adjusted_contents > 7) + row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. + src.standard_orient_objs(row_num, col_count, numbered_contents) + return + +//This proc return 1 if the item can be picked up and 0 if it can't. +//Set the stop_messages to stop it from printing messages +/obj/item/storage/proc/can_be_inserted(obj/item/W as obj, stop_messages = 0) + if(!istype(W) || (W.flags & ABSTRACT)) return //Not an item + + if(src.loc == W) + return 0 //Means the item is already in the storage item + if(contents.len >= storage_slots) + if(!stop_messages) + to_chat(usr, "[W] won't fit in [src], make some space!") + return 0 //Storage item is full + + if(can_hold.len) + if(!is_type_in_typecache(W, can_hold)) + if(!stop_messages) + to_chat(usr, "[src] cannot hold [W].") + return 0 + + if(is_type_in_typecache(W, cant_hold)) //Check for specific items which this container can't hold. + if(!stop_messages) + to_chat(usr, "[src] cannot hold [W].") + return 0 + + if(W.w_class > max_w_class) + if(!stop_messages) + to_chat(usr, "[W] is too big for [src].") + return 0 + + var/sum_w_class = W.w_class + for(var/obj/item/I in contents) + sum_w_class += I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it. + + if(sum_w_class > max_combined_w_class) + if(!stop_messages) + to_chat(usr, "[src] is full, make some space.") + return 0 + + if(W.w_class >= src.w_class && (istype(W, /obj/item/storage))) + if(!istype(src, /obj/item/storage/backpack/holding)) //bohs should be able to hold backpacks again. The override for putting a boh in a boh is in backpack.dm. + if(!stop_messages) + to_chat(usr, "[src] cannot hold [W] as it's a storage item of the same size.") + return 0 //To prevent the stacking of same sized storage items. + + if(W.flags & NODROP) //SHOULD be handled in unEquip, but better safe than sorry. + to_chat(usr, "\the [W] is stuck to your hand, you can't put it in \the [src]") + return 0 + + return 1 + +//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted() +//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once, +//such as when picking up all the items on a tile with one click. +/obj/item/storage/proc/handle_item_insertion(obj/item/W as obj, prevent_warning = 0) + if(!istype(W)) + return 0 + if(usr) + if(!usr.unEquip(W)) + return 0 + usr.update_icons() //update our overlays + if(silent) + prevent_warning = 1 + W.forceMove(src) + W.on_enter_storage(src) + if(usr) + if(usr.client && usr.s_active != src) + usr.client.screen -= W + W.dropped(usr) + add_fingerprint(usr) + + if(!prevent_warning && !istype(W, /obj/item/gun/energy/kinetic_accelerator/crossbow)) + for(var/mob/M in viewers(usr, null)) + if(M == usr) + to_chat(usr, "You put the [W] into [src].") + else if(M in range(1)) //If someone is standing close enough, they can tell what it is... + M.show_message("[usr] puts [W] into [src].") + else if(W && W.w_class >= WEIGHT_CLASS_NORMAL) //Otherwise they can only see large or normal items from a distance... + M.show_message("[usr] puts [W] into [src].") + + src.orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + W.mouse_opacity = MOUSE_OPACITY_OPAQUE //So you can click on the area around the item to equip it, instead of having to pixel hunt + W.in_inventory = TRUE + update_icon() + return 1 + +//Call this proc to handle the removal of an item from the storage item. The item will be moved to the atom sent as new_target +/obj/item/storage/proc/remove_from_storage(obj/item/W as obj, atom/new_location) + if(!istype(W)) return 0 + + if(istype(src, /obj/item/storage/fancy)) + var/obj/item/storage/fancy/F = src + F.update_icon(1) + + for(var/mob/M in range(1, src.loc)) + if(M.s_active == src) + if(M.client) + M.client.screen -= W + + if(new_location) + if(ismob(loc)) + W.dropped(usr) + if(ismob(new_location)) + W.layer = ABOVE_HUD_LAYER + W.plane = ABOVE_HUD_PLANE + else + W.layer = initial(W.layer) + W.plane = initial(W.plane) + W.forceMove(new_location) + else + W.forceMove(get_turf(src)) + + if(usr) + src.orient2hud(usr) + if(usr.s_active) + usr.s_active.show_to(usr) + if(W.maptext) + W.maptext = "" + W.on_exit_storage(src) + update_icon() + W.mouse_opacity = initial(W.mouse_opacity) + return 1 + +/obj/item/storage/Exited(atom/A, loc) + remove_from_storage(A, loc) //worry not, comrade; this only gets called once + ..() + +/obj/item/storage/deconstruct(disassembled = TRUE) + var/drop_loc = loc + if(ismob(loc)) + drop_loc = get_turf(src) + for(var/obj/item/I in contents) + remove_from_storage(I, drop_loc) + qdel(src) + +//This proc is called when you want to place an item into the storage item. +/obj/item/storage/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/hand_labeler)) + var/obj/item/hand_labeler/labeler = I + if(labeler.mode) + return FALSE + . = 1 //no afterattack + if(isrobot(user)) + return //Robots can't interact with storage items. + + if(!can_be_inserted(I)) + if(contents.len >= storage_slots) //don't use items on the backpack if they don't fit + return TRUE + return FALSE + + handle_item_insertion(I) + +/obj/item/storage/attack_hand(mob/user as mob) + playsound(src.loc, "rustle", 50, 1, -5) + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.l_store == src && !H.get_active_hand()) //Prevents opening if it's in a pocket. + H.put_in_hands(src) + H.l_store = null + return + if(H.r_store == src && !H.get_active_hand()) + H.put_in_hands(src) + H.r_store = null + return + + src.orient2hud(user) + if(src.loc == user) + if(user.s_active) + user.s_active.close(user) + src.show_to(user) + else + ..() + for(var/mob/M in range(1)) + if(M.s_active == src) + src.close(M) + src.add_fingerprint(user) + return + +/obj/item/storage/verb/toggle_gathering_mode() + set name = "Switch Gathering Method" + set category = "Object" + + collection_mode = !collection_mode + switch(collection_mode) + if(1) + to_chat(usr, "[src] now picks up all items in a tile at once.") + if(0) + to_chat(usr, "[src] now picks up one item at a time.") + + +/obj/item/storage/verb/quick_empty() + set name = "Empty Contents" + set category = "Object" + + if((!ishuman(usr) && (src.loc != usr)) || usr.stat || usr.restrained()) + return + + var/turf/T = get_turf(src) + hide_from(usr) + for(var/obj/item/I in contents) + remove_from_storage(I, T) + CHECK_TICK + +/obj/item/storage/New() + ..() + can_hold = typecacheof(can_hold) + cant_hold = typecacheof(cant_hold) + + if(allow_quick_empty) + verbs += /obj/item/storage/verb/quick_empty + else + verbs -= /obj/item/storage/verb/quick_empty + + if(allow_quick_gather) + verbs += /obj/item/storage/verb/toggle_gathering_mode + else + verbs -= /obj/item/storage/verb/toggle_gathering_mode + + boxes = new /obj/screen/storage( ) + boxes.name = "storage" + boxes.master = src + boxes.icon_state = "block" + boxes.screen_loc = "7,7 to 10,8" + boxes.layer = HUD_LAYER + boxes.plane = HUD_PLANE + closer = new /obj/screen/close( ) + closer.master = src + closer.icon_state = "backpack_close" + closer.layer = ABOVE_HUD_LAYER + closer.plane = ABOVE_HUD_PLANE + orient2hud() + +/obj/item/storage/Destroy() + for(var/obj/O in contents) + O.mouse_opacity = initial(O.mouse_opacity) + + QDEL_NULL(boxes) + QDEL_NULL(closer) + return ..() + +/obj/item/storage/emp_act(severity) + if(!istype(loc, /mob/living)) + for(var/obj/O in contents) + O.emp_act(severity) + ..() + +/obj/item/storage/hear_talk(mob/living/M as mob, list/message_pieces) + ..() + for(var/obj/O in contents) + O.hear_talk(M, message_pieces) + +/obj/item/storage/hear_message(mob/living/M as mob, msg) + ..() + for(var/obj/O in contents) + O.hear_message(M, msg) + +/obj/item/storage/attack_self(mob/user) + + //Clicking on itself will empty it, if it has the verb to do that. + if(user.is_in_active_hand(src)) + if(verbs.Find(/obj/item/storage/verb/quick_empty)) + quick_empty() + +//Returns the storage depth of an atom. This is the number of storage items the atom is contained in before reaching toplevel (the area). +//Returns -1 if the atom was not found on container. +/atom/proc/storage_depth(atom/container) + var/depth = 0 + var/atom/cur_atom = src + + while(cur_atom && !(cur_atom in container.contents)) + if(isarea(cur_atom)) + return -1 + if(istype(cur_atom.loc, /obj/item/storage)) + depth++ + cur_atom = cur_atom.loc + + if(!cur_atom) + return -1 //inside something with a null loc. + + return depth + +//Like storage depth, but returns the depth to the nearest turf +//Returns -1 if no top level turf (a loc was null somewhere, or a non-turf atom's loc was an area somehow). +/atom/proc/storage_depth_turf() + var/depth = 0 + var/atom/cur_atom = src + + while(cur_atom && !isturf(cur_atom)) + if(isarea(cur_atom)) + return -1 + if(istype(cur_atom.loc, /obj/item/storage)) + depth++ + cur_atom = cur_atom.loc + + if(!cur_atom) + return -1 //inside something with a null loc. + + return depth + +/obj/item/storage/serialize() + var data = ..() + var/list/content_list = list() + data["content"] = content_list + data["slots"] = storage_slots + data["max_w_class"] = max_w_class + data["max_c_w_class"] = max_combined_w_class + for(var/thing in contents) + var/atom/movable/AM = thing + // This code does not watch out for infinite loops + // But then again a tesseract would destroy the server anyways + // Also I wish I could just insert a list instead of it reading it the wrong way + content_list.len++ + content_list[content_list.len] = AM.serialize() + return data + +/obj/item/storage/deserialize(list/data) + if(isnum(data["slots"])) + storage_slots = data["slots"] + if(isnum(data["max_w_class"])) + max_w_class = data["max_w_class"] + if(isnum(data["max_c_w_class"])) + max_combined_w_class = data["max_c_w_class"] + for(var/thing in contents) + qdel(thing) // out with the old + for(var/thing in data["content"]) + if(islist(thing)) + list_to_object(thing, src) + else if(thing == null) + log_runtime(EXCEPTION("Null entry found in storage/deserialize."), src) + else + log_runtime(EXCEPTION("Non-list thing found in storage/deserialize."), src, list("Thing: [thing]")) + ..() + +/obj/item/storage/AllowDrop() + return TRUE + +/obj/item/storage/ex_act(severity) + for(var/atom/A in contents) + A.ex_act(severity) + CHECK_TICK + ..() diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index e8cc64af842c..c95e61cbf149 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -1,137 +1,137 @@ -/obj/item/storage/toolbox - name = "toolbox" - desc = "Danger. Very robust." - icon = 'icons/obj/storage.dmi' - icon_state = "red" - item_state = "toolbox_red" - flags = CONDUCT - force = 10.0 - throwforce = 10.0 - throw_speed = 2 - throw_range = 7 - w_class = WEIGHT_CLASS_BULKY - materials = list(MAT_METAL = 500) - origin_tech = "combat=1;engineering=1" - attack_verb = list("robusted") - hitsound = 'sound/weapons/smash.ogg' - -/obj/item/storage/toolbox/emergency - name = "emergency toolbox" - icon_state = "red" - item_state = "toolbox_red" - -/obj/item/storage/toolbox/emergency/New() - ..() - new /obj/item/crowbar/red(src) - new /obj/item/weldingtool/mini(src) - new /obj/item/extinguisher/mini(src) - if(prob(50)) - new /obj/item/flashlight(src) - else - new /obj/item/flashlight/flare(src) - new /obj/item/radio(src) - -/obj/item/storage/toolbox/emergency/old - name = "rusty red toolbox" - icon_state = "toolbox_red_old" - -/obj/item/storage/toolbox/mechanical - name = "mechanical toolbox" - icon_state = "blue" - item_state = "toolbox_blue" - -/obj/item/storage/toolbox/mechanical/New() - ..() - new /obj/item/screwdriver(src) - new /obj/item/wrench(src) - new /obj/item/weldingtool(src) - new /obj/item/crowbar(src) - new /obj/item/analyzer(src) - new /obj/item/wirecutters(src) - -/obj/item/storage/toolbox/mechanical/greytide - flags = NODROP - -/obj/item/storage/toolbox/mechanical/old - name = "rusty blue toolbox" - icon_state = "toolbox_blue_old" - -/obj/item/storage/toolbox/electrical - name = "electrical toolbox" - icon_state = "yellow" - item_state = "toolbox_yellow" - -/obj/item/storage/toolbox/electrical/New() - ..() - var/pickedcolor = pick(COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_BLUE, COLOR_PINK, COLOR_ORANGE, COLOR_CYAN, COLOR_WHITE) - new /obj/item/screwdriver(src) - new /obj/item/wirecutters(src) - new /obj/item/t_scanner(src) - new /obj/item/crowbar(src) - new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) - new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) - if(prob(5)) - new /obj/item/clothing/gloves/color/yellow(src) - else - new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) - -/obj/item/storage/toolbox/syndicate - name = "suspicious looking toolbox" - icon_state = "syndicate" - item_state = "toolbox_syndi" - origin_tech = "combat=2;syndicate=1;engineering=2" - silent = 1 - force = 15.0 - throwforce = 18.0 - -/obj/item/storage/toolbox/syndicate/New() - ..() - new /obj/item/screwdriver(src, "red") - new /obj/item/wrench(src) - new /obj/item/weldingtool/largetank(src) - new /obj/item/crowbar/red(src) - new /obj/item/wirecutters(src, "red") - new /obj/item/multitool(src) - new /obj/item/clothing/gloves/combat(src) - -/obj/item/storage/toolbox/fakesyndi - name = "suspicous looking toolbox" - icon_state = "syndicate" - item_state = "toolbox_syndi" - desc = "Danger. Very Robust. The paint is still wet." - -/obj/item/storage/toolbox/drone - name = "mechanical toolbox" - icon_state = "blue" - item_state = "toolbox_blue" - -/obj/item/storage/toolbox/drone/New() - ..() - var/pickedcolor = pick(pick(COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_BLUE, COLOR_PINK, COLOR_ORANGE, COLOR_CYAN, COLOR_WHITE)) - new /obj/item/screwdriver(src) - new /obj/item/wrench(src) - new /obj/item/weldingtool(src) - new /obj/item/crowbar(src) - new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) - new /obj/item/wirecutters(src) - new /obj/item/multitool(src) - -/obj/item/storage/toolbox/brass - name = "brass box" - desc = "A huge brass box with several indentations in its surface." - icon_state = "brassbox" - item_state = null - resistance_flags = FIRE_PROOF | ACID_PROOF - w_class = WEIGHT_CLASS_HUGE - max_w_class = WEIGHT_CLASS_NORMAL - max_combined_w_class = 28 - storage_slots = 28 - attack_verb = list("robusted", "crushed", "smashed") - -/obj/item/storage/toolbox/brass/prefilled/New() - ..() - new /obj/item/screwdriver/brass(src) - new /obj/item/wirecutters/brass(src) - new /obj/item/wrench/brass(src) - new /obj/item/crowbar/brass(src) - new /obj/item/weldingtool/experimental/brass(src) +/obj/item/storage/toolbox + name = "toolbox" + desc = "Danger. Very robust." + icon = 'icons/obj/storage.dmi' + icon_state = "red" + item_state = "toolbox_red" + flags = CONDUCT + force = 10.0 + throwforce = 10.0 + throw_speed = 2 + throw_range = 7 + w_class = WEIGHT_CLASS_BULKY + materials = list(MAT_METAL = 500) + origin_tech = "combat=1;engineering=1" + attack_verb = list("robusted") + hitsound = 'sound/weapons/smash.ogg' + +/obj/item/storage/toolbox/emergency + name = "emergency toolbox" + icon_state = "red" + item_state = "toolbox_red" + +/obj/item/storage/toolbox/emergency/New() + ..() + new /obj/item/crowbar/red(src) + new /obj/item/weldingtool/mini(src) + new /obj/item/extinguisher/mini(src) + if(prob(50)) + new /obj/item/flashlight(src) + else + new /obj/item/flashlight/flare(src) + new /obj/item/radio(src) + +/obj/item/storage/toolbox/emergency/old + name = "rusty red toolbox" + icon_state = "toolbox_red_old" + +/obj/item/storage/toolbox/mechanical + name = "mechanical toolbox" + icon_state = "blue" + item_state = "toolbox_blue" + +/obj/item/storage/toolbox/mechanical/New() + ..() + new /obj/item/screwdriver(src) + new /obj/item/wrench(src) + new /obj/item/weldingtool(src) + new /obj/item/crowbar(src) + new /obj/item/analyzer(src) + new /obj/item/wirecutters(src) + +/obj/item/storage/toolbox/mechanical/greytide + flags = NODROP + +/obj/item/storage/toolbox/mechanical/old + name = "rusty blue toolbox" + icon_state = "toolbox_blue_old" + +/obj/item/storage/toolbox/electrical + name = "electrical toolbox" + icon_state = "yellow" + item_state = "toolbox_yellow" + +/obj/item/storage/toolbox/electrical/New() + ..() + var/pickedcolor = pick(COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_BLUE, COLOR_PINK, COLOR_ORANGE, COLOR_CYAN, COLOR_WHITE) + new /obj/item/screwdriver(src) + new /obj/item/wirecutters(src) + new /obj/item/t_scanner(src) + new /obj/item/crowbar(src) + new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) + new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) + if(prob(5)) + new /obj/item/clothing/gloves/color/yellow(src) + else + new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) + +/obj/item/storage/toolbox/syndicate + name = "suspicious looking toolbox" + icon_state = "syndicate" + item_state = "toolbox_syndi" + origin_tech = "combat=2;syndicate=1;engineering=2" + silent = 1 + force = 15.0 + throwforce = 18.0 + +/obj/item/storage/toolbox/syndicate/New() + ..() + new /obj/item/screwdriver(src, "red") + new /obj/item/wrench(src) + new /obj/item/weldingtool/largetank(src) + new /obj/item/crowbar/red(src) + new /obj/item/wirecutters(src, "red") + new /obj/item/multitool(src) + new /obj/item/clothing/gloves/combat(src) + +/obj/item/storage/toolbox/fakesyndi + name = "suspicous looking toolbox" + icon_state = "syndicate" + item_state = "toolbox_syndi" + desc = "Danger. Very Robust. The paint is still wet." + +/obj/item/storage/toolbox/drone + name = "mechanical toolbox" + icon_state = "blue" + item_state = "toolbox_blue" + +/obj/item/storage/toolbox/drone/New() + ..() + var/pickedcolor = pick(pick(COLOR_RED, COLOR_YELLOW, COLOR_GREEN, COLOR_BLUE, COLOR_PINK, COLOR_ORANGE, COLOR_CYAN, COLOR_WHITE)) + new /obj/item/screwdriver(src) + new /obj/item/wrench(src) + new /obj/item/weldingtool(src) + new /obj/item/crowbar(src) + new /obj/item/stack/cable_coil(src, 30, paramcolor = pickedcolor) + new /obj/item/wirecutters(src) + new /obj/item/multitool(src) + +/obj/item/storage/toolbox/brass + name = "brass box" + desc = "A huge brass box with several indentations in its surface." + icon_state = "brassbox" + item_state = null + resistance_flags = FIRE_PROOF | ACID_PROOF + w_class = WEIGHT_CLASS_HUGE + max_w_class = WEIGHT_CLASS_NORMAL + max_combined_w_class = 28 + storage_slots = 28 + attack_verb = list("robusted", "crushed", "smashed") + +/obj/item/storage/toolbox/brass/prefilled/New() + ..() + new /obj/item/screwdriver/brass(src) + new /obj/item/wirecutters/brass(src) + new /obj/item/wrench/brass(src) + new /obj/item/crowbar/brass(src) + new /obj/item/weldingtool/experimental/brass(src) diff --git a/code/game/objects/items/weapons/swords_axes_etc.dm b/code/game/objects/items/weapons/swords_axes_etc.dm index 409e5575df3d..e75935a3cc25 100644 --- a/code/game/objects/items/weapons/swords_axes_etc.dm +++ b/code/game/objects/items/weapons/swords_axes_etc.dm @@ -1,119 +1,119 @@ -/* Weapons - * Contains: - * Banhammer - * Classic Baton - */ - -/* - * Banhammer - */ -/obj/item/banhammer/attack(mob/M, mob/user) - to_chat(M, " You have been banned FOR NO REISIN by [user]") - to_chat(user, " You have BANNED [M]") - playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much - -/* - * Classic Baton - */ - -/obj/item/melee/classic_baton - name = "police baton" - desc = "A wooden truncheon for beating criminal scum." - icon_state = "baton" - item_state = "classic_baton" - slot_flags = SLOT_BELT - force = 12 //9 hit crit - w_class = WEIGHT_CLASS_NORMAL - var/cooldown = 0 - var/on = 1 - -/obj/item/melee/classic_baton/attack(mob/target as mob, mob/living/user as mob) - if(on) - add_fingerprint(user) - if((CLUMSY in user.mutations) && prob(50)) - to_chat(user, "You club yourself over the head.") - user.Weaken(3 * force) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(2*force, BRUTE, "head") - else - user.take_organ_damage(2*force) - return - if(isrobot(target)) - ..() - return - if(!isliving(target)) - return - if(user.a_intent == INTENT_HARM) - if(!..()) return - if(!isrobot(target)) return - else - if(cooldown <= 0) - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) - return - if(check_martial_counter(H, user)) - return - playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1) - target.Weaken(3) - add_attack_logs(user, target, "Stunned with [src]") - add_fingerprint(user) - target.visible_message("[user] has knocked down [target] with \the [src]!", \ - "[user] has knocked down [target] with \the [src]!") - if(!iscarbon(user)) - target.LAssailant = null - else - target.LAssailant = user - cooldown = 1 - spawn(40) - cooldown = 0 - return - else - return ..() - -/obj/item/melee/classic_baton/ntcane - name = "fancy cane" - desc = "A cane with special engraving on it. It seems well suited for fending off assailants..." - icon_state = "cane_nt" - item_state = "cane_nt" - needs_permit = 0 - -/obj/item/melee/classic_baton/ntcane/is_crutch() - return 1 - -//Telescopic baton -/obj/item/melee/classic_baton/telescopic - name = "telescopic baton" - desc = "A compact yet robust personal defense weapon. Can be concealed when folded." - icon_state = "telebaton_0" - item_state = null - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - needs_permit = 0 - force = 0 - on = 0 - -/obj/item/melee/classic_baton/telescopic/attack_self(mob/user as mob) - on = !on - if(on) - to_chat(user, "You extend the baton.") - icon_state = "telebaton_1" - item_state = "nullrod" - w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance - force = 10 //stunbaton damage - attack_verb = list("smacked", "struck", "cracked", "beaten") - else - to_chat(user, "You collapse the baton.") - icon_state = "telebaton_0" - item_state = null //no sprite for concealment even when in hand - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - force = 0 //not so robust now - attack_verb = list("hit", "poked") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) - add_fingerprint(user) +/* Weapons + * Contains: + * Banhammer + * Classic Baton + */ + +/* + * Banhammer + */ +/obj/item/banhammer/attack(mob/M, mob/user) + to_chat(M, " You have been banned FOR NO REISIN by [user]") + to_chat(user, " You have BANNED [M]") + playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much + +/* + * Classic Baton + */ + +/obj/item/melee/classic_baton + name = "police baton" + desc = "A wooden truncheon for beating criminal scum." + icon_state = "baton" + item_state = "classic_baton" + slot_flags = SLOT_BELT + force = 12 //9 hit crit + w_class = WEIGHT_CLASS_NORMAL + var/cooldown = 0 + var/on = 1 + +/obj/item/melee/classic_baton/attack(mob/target as mob, mob/living/user as mob) + if(on) + add_fingerprint(user) + if((CLUMSY in user.mutations) && prob(50)) + to_chat(user, "You club yourself over the head.") + user.Weaken(3 * force) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(2*force, BRUTE, "head") + else + user.take_organ_damage(2*force) + return + if(isrobot(target)) + ..() + return + if(!isliving(target)) + return + if(user.a_intent == INTENT_HARM) + if(!..()) return + if(!isrobot(target)) return + else + if(cooldown <= 0) + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) + return + if(check_martial_counter(H, user)) + return + playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1) + target.Weaken(3) + add_attack_logs(user, target, "Stunned with [src]") + add_fingerprint(user) + target.visible_message("[user] has knocked down [target] with \the [src]!", \ + "[user] has knocked down [target] with \the [src]!") + if(!iscarbon(user)) + target.LAssailant = null + else + target.LAssailant = user + cooldown = 1 + spawn(40) + cooldown = 0 + return + else + return ..() + +/obj/item/melee/classic_baton/ntcane + name = "fancy cane" + desc = "A cane with special engraving on it. It seems well suited for fending off assailants..." + icon_state = "cane_nt" + item_state = "cane_nt" + needs_permit = 0 + +/obj/item/melee/classic_baton/ntcane/is_crutch() + return 1 + +//Telescopic baton +/obj/item/melee/classic_baton/telescopic + name = "telescopic baton" + desc = "A compact yet robust personal defense weapon. Can be concealed when folded." + icon_state = "telebaton_0" + item_state = null + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + needs_permit = 0 + force = 0 + on = 0 + +/obj/item/melee/classic_baton/telescopic/attack_self(mob/user as mob) + on = !on + if(on) + to_chat(user, "You extend the baton.") + icon_state = "telebaton_1" + item_state = "nullrod" + w_class = WEIGHT_CLASS_BULKY //doesnt fit in backpack when its on for balance + force = 10 //stunbaton damage + attack_verb = list("smacked", "struck", "cracked", "beaten") + else + to_chat(user, "You collapse the baton.") + icon_state = "telebaton_0" + item_state = null //no sprite for concealment even when in hand + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + force = 0 //not so robust now + attack_verb = list("hit", "poked") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_l_hand() + H.update_inv_r_hand() + playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) + add_fingerprint(user) diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 40376248d611..bc783457e760 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -1,246 +1,246 @@ -/obj/item/tank/jetpack - name = "Jetpack (Empty)" - desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack" - w_class = WEIGHT_CLASS_BULKY - item_state = "jetpack" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - var/datum/effect_system/trail_follow/ion/ion_trail - actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) - var/on = 0 - var/stabilizers = 0 - var/volume_rate = 500 //Needed for borg jetpack transfer - -/obj/item/tank/jetpack/New() - ..() - ion_trail = new /datum/effect_system/trail_follow/ion() - ion_trail.set_up(src) - -/obj/item/tank/jetpack/Destroy() - QDEL_NULL(ion_trail) - return ..() - -/obj/item/tank/jetpack/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/toggle_jetpack) - cycle(user) - else if(actiontype == /datum/action/item_action/jetpack_stabilization) - toggle_stabilization(user) - else - toggle_internals(user) - -/obj/item/tank/jetpack/proc/toggle_stabilization(mob/user) - if(on) - stabilizers = !stabilizers - to_chat(user, "You turn [src]'s stabilization [stabilizers ? "on" : "off"].") - - -/obj/item/tank/jetpack/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.oxygen < 10) - . += "The meter on [src] indicates you are almost out of air!" - playsound(user, 'sound/effects/alert.ogg', 50, 1) - - -/obj/item/tank/jetpack/proc/cycle(mob/user) - if(user.incapacitated()) - return - - if(!on) - turn_on(user) - to_chat(user, "You turn the jetpack on.") - else - turn_off(user) - to_chat(user, "You turn the jetpack off.") - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - - -/obj/item/tank/jetpack/proc/turn_on(mob/user) - on = TRUE - icon_state = "[initial(icon_state)]-on" - ion_trail.start() - -/obj/item/tank/jetpack/proc/turn_off(mob/user) - on = FALSE - stabilizers = FALSE - icon_state = initial(icon_state) - ion_trail.stop() - - -/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user) - if(!on) - return 0 - if((num < 0.005 || air_contents.total_moles() < num)) - turn_off(user) - return 0 - - var/datum/gas_mixture/removed = air_contents.remove(num) - if(removed.total_moles() < 0.005) - turn_off(user) - return 0 - - var/turf/T = get_turf(user) - T.assume_air(removed) - return 1 - -/obj/item/tank/jetpack/void - name = "Void Jetpack (Oxygen)" - desc = "It works well in a void." - icon_state = "jetpack-void" - item_state = "jetpack-void" - -/obj/item/tank/jetpack/void/New() - ..() - air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/jetpack/void/grey - name = "Void Jetpack (Oxygen)" - icon_state = "jetpack-void-grey" - -/obj/item/tank/jetpack/void/gold - name = "Retro Jetpack (Oxygen)" - icon_state = "jetpack-void-gold" - - -/obj/item/tank/jetpack/oxygen - name = "Jetpack (Oxygen)" - desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack" - item_state = "jetpack" - -/obj/item/tank/jetpack/oxygen/New() - ..() - air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/jetpack/oxygen/captain - name = "Captain's jetpack" - desc = "A compact, lightweight jetpack containing a high amount of compressed oxygen." - icon_state = "jetpack-captain" - item_state = "jetpack-captain" - volume = 90 - w_class = WEIGHT_CLASS_NORMAL - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //steal objective items are hard to destroy. - -/obj/item/tank/jetpack/oxygen/harness - name = "jet harness (oxygen)" - desc = "A lightweight tactical harness, used by those who don't want to be weighed down by traditional jetpacks." - icon_state = "jetpack-mini" - item_state = "jetpack-mini" - volume = 40 - throw_range = 8 - w_class = WEIGHT_CLASS_NORMAL - -/obj/item/tank/jetpack/oxygenblack - name = "Jetpack (Oxygen)" - desc = "A black tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." - icon_state = "jetpack-black" - item_state = "jetpack-black" - -/obj/item/tank/jetpack/oxygenblack/New() - ..() - air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/jetpack/carbondioxide - name = "Jetpack (Carbon Dioxide)" - desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." - distribute_pressure = 0 - icon_state = "jetpack-black" - item_state = "jetpack-black" - -/obj/item/tank/jetpack/carbondioxide/New() - ..() - ion_trail = new /datum/effect_system/trail_follow/ion() - ion_trail.set_up(src) - air_contents.carbon_dioxide = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/jetpack/carbondioxide/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.carbon_dioxide < 10) - . += "The meter on [src] indicates you are almost out of air!" - playsound(user, 'sound/effects/alert.ogg', 50, 1) - -/obj/item/tank/jetpack/suit - name = "hardsuit jetpack upgrade" - desc = "A modular, compact set of thrusters designed to integrate with a hardsuit. It is fueled by a tank inserted into the suit's storage compartment." - icon_state = "jetpack-mining" - item_state = "jetpack-black" - origin_tech = "materials=4;magnets=4;engineering=5" - w_class = WEIGHT_CLASS_NORMAL - actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) - volume = 1 - slot_flags = null - var/datum/gas_mixture/temp_air_contents - var/obj/item/tank/tank = null - var/mob/living/carbon/human/cur_user - -/obj/item/tank/jetpack/suit/New() - ..() - STOP_PROCESSING(SSobj, src) - temp_air_contents = air_contents - -/obj/item/tank/jetpack/suit/attack_self() - return - -/obj/item/tank/jetpack/suit/cycle(mob/user) - if(!istype(loc, /obj/item/clothing/suit/space/hardsuit)) - to_chat(user, "[src] must be connected to a hardsuit!") - return - - var/mob/living/carbon/human/H = user - if(!istype(H.s_store, /obj/item/tank)) - to_chat(user, "You need a tank in your suit storage!") - return - ..() - -/obj/item/tank/jetpack/suit/turn_on(mob/user) - if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc) || loc.loc != user) - return - var/mob/living/carbon/human/H = user - tank = H.s_store - air_contents = tank.air_contents - START_PROCESSING(SSobj, src) - cur_user = user - ..() - -/obj/item/tank/jetpack/suit/turn_off(mob/user) - tank = null - air_contents = temp_air_contents - STOP_PROCESSING(SSobj, src) - cur_user = null - ..() - -/obj/item/tank/jetpack/suit/process() - if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc)) - turn_off(cur_user) - return - var/mob/living/carbon/human/H = loc.loc - if(!tank || tank != H.s_store) - turn_off(cur_user) - return - ..() - -/obj/item/tank/jetpack/rig - name = "jetpack" - var/obj/item/rig/holder - actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) - -/obj/item/tank/jetpack/rig/examine() - . = list("It's a jetpack. If you can see this, report it on the bug tracker.") - -/obj/item/tank/jetpack/rig/allow_thrust(num, mob/living/user) - if(!on) - return 0 - - if(!istype(holder) || !holder.air_supply) - return 0 - - var/datum/gas_mixture/removed = holder.air_supply.air_contents.remove(num) - if(removed.total_moles() < 0.005) - turn_off(user) - return 0 - - var/turf/T = get_turf(user) - T.assume_air(removed) - - return 1 +/obj/item/tank/jetpack + name = "Jetpack (Empty)" + desc = "A tank of compressed gas for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack" + w_class = WEIGHT_CLASS_BULKY + item_state = "jetpack" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + var/datum/effect_system/trail_follow/ion/ion_trail + actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) + var/on = 0 + var/stabilizers = 0 + var/volume_rate = 500 //Needed for borg jetpack transfer + +/obj/item/tank/jetpack/New() + ..() + ion_trail = new /datum/effect_system/trail_follow/ion() + ion_trail.set_up(src) + +/obj/item/tank/jetpack/Destroy() + QDEL_NULL(ion_trail) + return ..() + +/obj/item/tank/jetpack/ui_action_click(mob/user, actiontype) + if(actiontype == /datum/action/item_action/toggle_jetpack) + cycle(user) + else if(actiontype == /datum/action/item_action/jetpack_stabilization) + toggle_stabilization(user) + else + toggle_internals(user) + +/obj/item/tank/jetpack/proc/toggle_stabilization(mob/user) + if(on) + stabilizers = !stabilizers + to_chat(user, "You turn [src]'s stabilization [stabilizers ? "on" : "off"].") + + +/obj/item/tank/jetpack/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.oxygen < 10) + . += "The meter on [src] indicates you are almost out of air!" + playsound(user, 'sound/effects/alert.ogg', 50, 1) + + +/obj/item/tank/jetpack/proc/cycle(mob/user) + if(user.incapacitated()) + return + + if(!on) + turn_on(user) + to_chat(user, "You turn the jetpack on.") + else + turn_off(user) + to_chat(user, "You turn the jetpack off.") + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + + +/obj/item/tank/jetpack/proc/turn_on(mob/user) + on = TRUE + icon_state = "[initial(icon_state)]-on" + ion_trail.start() + +/obj/item/tank/jetpack/proc/turn_off(mob/user) + on = FALSE + stabilizers = FALSE + icon_state = initial(icon_state) + ion_trail.stop() + + +/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user) + if(!on) + return 0 + if((num < 0.005 || air_contents.total_moles() < num)) + turn_off(user) + return 0 + + var/datum/gas_mixture/removed = air_contents.remove(num) + if(removed.total_moles() < 0.005) + turn_off(user) + return 0 + + var/turf/T = get_turf(user) + T.assume_air(removed) + return 1 + +/obj/item/tank/jetpack/void + name = "Void Jetpack (Oxygen)" + desc = "It works well in a void." + icon_state = "jetpack-void" + item_state = "jetpack-void" + +/obj/item/tank/jetpack/void/New() + ..() + air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/jetpack/void/grey + name = "Void Jetpack (Oxygen)" + icon_state = "jetpack-void-grey" + +/obj/item/tank/jetpack/void/gold + name = "Retro Jetpack (Oxygen)" + icon_state = "jetpack-void-gold" + + +/obj/item/tank/jetpack/oxygen + name = "Jetpack (Oxygen)" + desc = "A tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack" + item_state = "jetpack" + +/obj/item/tank/jetpack/oxygen/New() + ..() + air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/jetpack/oxygen/captain + name = "Captain's jetpack" + desc = "A compact, lightweight jetpack containing a high amount of compressed oxygen." + icon_state = "jetpack-captain" + item_state = "jetpack-captain" + volume = 90 + w_class = WEIGHT_CLASS_NORMAL + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF //steal objective items are hard to destroy. + +/obj/item/tank/jetpack/oxygen/harness + name = "jet harness (oxygen)" + desc = "A lightweight tactical harness, used by those who don't want to be weighed down by traditional jetpacks." + icon_state = "jetpack-mini" + item_state = "jetpack-mini" + volume = 40 + throw_range = 8 + w_class = WEIGHT_CLASS_NORMAL + +/obj/item/tank/jetpack/oxygenblack + name = "Jetpack (Oxygen)" + desc = "A black tank of compressed oxygen for use as propulsion in zero-gravity areas. Use with caution." + icon_state = "jetpack-black" + item_state = "jetpack-black" + +/obj/item/tank/jetpack/oxygenblack/New() + ..() + air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/jetpack/carbondioxide + name = "Jetpack (Carbon Dioxide)" + desc = "A tank of compressed carbon dioxide for use as propulsion in zero-gravity areas. Painted black to indicate that it should not be used as a source for internals." + distribute_pressure = 0 + icon_state = "jetpack-black" + item_state = "jetpack-black" + +/obj/item/tank/jetpack/carbondioxide/New() + ..() + ion_trail = new /datum/effect_system/trail_follow/ion() + ion_trail.set_up(src) + air_contents.carbon_dioxide = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/jetpack/carbondioxide/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.carbon_dioxide < 10) + . += "The meter on [src] indicates you are almost out of air!" + playsound(user, 'sound/effects/alert.ogg', 50, 1) + +/obj/item/tank/jetpack/suit + name = "hardsuit jetpack upgrade" + desc = "A modular, compact set of thrusters designed to integrate with a hardsuit. It is fueled by a tank inserted into the suit's storage compartment." + icon_state = "jetpack-mining" + item_state = "jetpack-black" + origin_tech = "materials=4;magnets=4;engineering=5" + w_class = WEIGHT_CLASS_NORMAL + actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) + volume = 1 + slot_flags = null + var/datum/gas_mixture/temp_air_contents + var/obj/item/tank/tank = null + var/mob/living/carbon/human/cur_user + +/obj/item/tank/jetpack/suit/New() + ..() + STOP_PROCESSING(SSobj, src) + temp_air_contents = air_contents + +/obj/item/tank/jetpack/suit/attack_self() + return + +/obj/item/tank/jetpack/suit/cycle(mob/user) + if(!istype(loc, /obj/item/clothing/suit/space/hardsuit)) + to_chat(user, "[src] must be connected to a hardsuit!") + return + + var/mob/living/carbon/human/H = user + if(!istype(H.s_store, /obj/item/tank)) + to_chat(user, "You need a tank in your suit storage!") + return + ..() + +/obj/item/tank/jetpack/suit/turn_on(mob/user) + if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc) || loc.loc != user) + return + var/mob/living/carbon/human/H = user + tank = H.s_store + air_contents = tank.air_contents + START_PROCESSING(SSobj, src) + cur_user = user + ..() + +/obj/item/tank/jetpack/suit/turn_off(mob/user) + tank = null + air_contents = temp_air_contents + STOP_PROCESSING(SSobj, src) + cur_user = null + ..() + +/obj/item/tank/jetpack/suit/process() + if(!istype(loc, /obj/item/clothing/suit/space/hardsuit) || !ishuman(loc.loc)) + turn_off(cur_user) + return + var/mob/living/carbon/human/H = loc.loc + if(!tank || tank != H.s_store) + turn_off(cur_user) + return + ..() + +/obj/item/tank/jetpack/rig + name = "jetpack" + var/obj/item/rig/holder + actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) + +/obj/item/tank/jetpack/rig/examine() + . = list("It's a jetpack. If you can see this, report it on the bug tracker.") + +/obj/item/tank/jetpack/rig/allow_thrust(num, mob/living/user) + if(!on) + return 0 + + if(!istype(holder) || !holder.air_supply) + return 0 + + var/datum/gas_mixture/removed = holder.air_supply.air_contents.remove(num) + if(removed.total_moles() < 0.005) + turn_off(user) + return 0 + + var/turf/T = get_turf(user) + T.assume_air(removed) + + return 1 diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm index 55b0225bc2d6..11355b341706 100644 --- a/code/game/objects/items/weapons/tanks/tank_types.dm +++ b/code/game/objects/items/weapons/tanks/tank_types.dm @@ -1,253 +1,253 @@ -/* Types of tanks! - * Contains: - * Oxygen - * Anesthetic - * Air - * Plasma - * Emergency Oxygen - */ - -/* - * Oxygen - */ -/obj/item/tank/oxygen - name = "oxygen tank" - desc = "A tank of oxygen." - icon_state = "oxygen" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - dog_fashion = /datum/dog_fashion/back - - -/obj/item/tank/oxygen/New() - ..() - air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/oxygen/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.oxygen < 10) - . += "The meter on [src] indicates you are almost out of air!" - -obj/item/tank/oxygen/empty/New() - ..() - air_contents.oxygen = null - -/obj/item/tank/oxygen/yellow - desc = "A tank of oxygen, this one is yellow." - icon_state = "oxygen_f" - dog_fashion = null - -/obj/item/tank/oxygen/red - desc = "A tank of oxygen, this one is red." - icon_state = "oxygen_fr" - dog_fashion = null - - -/* - * Anesthetic - */ -/obj/item/tank/anesthetic - name = "anesthetic tank" - desc = "A tank with an N2O/O2 gas mix." - icon_state = "anesthetic" - item_state = "an_tank" - -/obj/item/tank/anesthetic/New() - ..() - - air_contents.oxygen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD - - var/datum/gas/sleeping_agent/trace_gas = new() - trace_gas.moles = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD - - air_contents.trace_gases += trace_gas - -/* - * Air - */ -/obj/item/tank/air - name = "air tank" - desc = "Mixed anyone?" - icon_state = "air" - item_state = "air" - -/obj/item/tank/air/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.oxygen < 1) - . += "The meter on [src] indicates you are almost out of air!" - playsound(user, 'sound/effects/alert.ogg', 50, 1) - -/obj/item/tank/air/New() - ..() - air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD - air_contents.nitrogen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD - -/* - * Plasma - */ -/obj/item/tank/plasma - name = "plasma tank" - desc = "Contains dangerous plasma. Do not inhale. Warning: extremely flammable." - icon_state = "plasma" - flags = CONDUCT - slot_flags = null //they have no straps! - -/obj/item/tank/plasma/New() - ..() - air_contents.toxins = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/plasma/attackby(obj/item/W as obj, mob/user as mob, params) - ..() - - if(istype(W, /obj/item/flamethrower)) - var/obj/item/flamethrower/F = W - if((!F.status)||(F.ptank)) return - master = F - F.ptank = src - user.unEquip(src) - loc = F - F.update_icon() - -/obj/item/tank/plasma/full/New() - ..() - air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/plasma/plasmaman - name = "plasma internals tank" - desc = "A tank of plasma gas designed specifically for use as internals, particularly for plasma-based lifeforms. If you're not a Plasmaman, you probably shouldn't use this." - icon_state = "plasmaman_tank" - item_state = "plasmaman_tank" - force = 10 - distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - -/obj/item/tank/plasma/plasmaman/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.toxins < 0.2) - . += "The meter on [src] indicates you are almost out of plasma!" - playsound(user, 'sound/effects/alert.ogg', 50, 1) - - -/obj/item/tank/plasma/plasmaman/belt - icon_state = "plasmaman_tank_belt" - item_state = "plasmaman_tank_belt" - slot_flags = SLOT_BELT - force = 5 - volume = 25 - w_class = WEIGHT_CLASS_SMALL - -/obj/item/tank/plasma/plasmaman/belt/full/New() - ..() - air_contents.toxins = (10 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C) - -/* - * Emergency Oxygen - */ -/obj/item/tank/emergency_oxygen - name = "emergency oxygen tank" - desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it." - icon_state = "emergency" - flags = CONDUCT - slot_flags = SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - force = 4.0 - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - volume = 3 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) - - -/obj/item/tank/emergency_oxygen/New() - ..() - air_contents.oxygen = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/emergency_oxygen/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.oxygen < 0.2) - . += "The meter on [src] indicates you are almost out of air!" - playsound(user, 'sound/effects/alert.ogg', 50, 1) - -obj/item/tank/emergency_oxygen/empty/New() - ..() - air_contents.oxygen = null - -/obj/item/tank/emergency_oxygen/engi - name = "extended-capacity emergency oxygen tank" - icon_state = "emergency_engi" - volume = 6 - -obj/item/tank/emergency_oxygen/engi/empty/New() - ..() - air_contents.oxygen = null - -/obj/item/tank/emergency_oxygen/syndi - name = "suspicious emergency oxygen tank" - icon_state = "emergency_syndi" - desc = "A dark emergency oxygen tank. The label on the back reads \"Original Oxygen Tank Design, Do Not Steal.\"" - volume = 6 - -/obj/item/tank/emergency_oxygen/double - name = "double emergency oxygen tank" - icon_state = "emergency_double" - volume = 10 - -obj/item/tank/emergency_oxygen/double/empty/New() - ..() - air_contents.oxygen = null - -/obj/item/tank/emergency_oxygen/double/full - name = "pressurized double emergency oxygen tank" - desc = "Used for \"emergencies,\" it actually contains a fair amount of oxygen." - -/obj/item/tank/emergency_oxygen/double/full/New() - ..() - air_contents.oxygen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/* - * Nitrogen - */ -/obj/item/tank/nitrogen - name = "nitrogen tank" - desc = "A tank of nitrogen." - icon_state = "oxygen_fr" - distribute_pressure = ONE_ATMOSPHERE*O2STANDARD - sprite_sheets = list("Vox Armalis" = 'icons/mob/species/armalis/back.dmi') //Do it for Big Bird. - -/obj/item/tank/nitrogen/New() - ..() - air_contents.nitrogen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/nitrogen/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0 && air_contents.nitrogen < 10) - . += "The meter on the [src.name] indicates you are almost out of air!" - -/obj/item/tank/emergency_oxygen/vox - name = "vox specialized nitrogen tank" - desc = "A high-tech nitrogen tank designed specifically for Vox." - icon_state = "emergency_vox" - volume = 25 - sprite_sheets = list("Vox Armalis" = 'icons/mob/species/armalis/belt.dmi') //Do it for Big Bird. - -/obj/item/tank/emergency_oxygen/vox/New() - ..() - air_contents.oxygen -= (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - air_contents.nitrogen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/emergency_oxygen/nitrogen - name = "emergency nitrogen tank" - desc = "An emergency tank designed specifically for Vox." - icon_state = "emergency_nitrogen" - volume = 3 - -/obj/item/tank/emergency_oxygen/nitrogen/New() - ..() - air_contents.oxygen -= (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - air_contents.nitrogen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - -/obj/item/tank/emergency_oxygen/plasma - name = "emergency plasma tank" - desc = "An emergency tank designed specifically for Plasmamen." - icon_state = "emergency_p" - volume = 3 - -/obj/item/tank/emergency_oxygen/plasma/New() - ..() - air_contents.oxygen -= (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) - air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) +/* Types of tanks! + * Contains: + * Oxygen + * Anesthetic + * Air + * Plasma + * Emergency Oxygen + */ + +/* + * Oxygen + */ +/obj/item/tank/oxygen + name = "oxygen tank" + desc = "A tank of oxygen." + icon_state = "oxygen" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + dog_fashion = /datum/dog_fashion/back + + +/obj/item/tank/oxygen/New() + ..() + air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/oxygen/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.oxygen < 10) + . += "The meter on [src] indicates you are almost out of air!" + +obj/item/tank/oxygen/empty/New() + ..() + air_contents.oxygen = null + +/obj/item/tank/oxygen/yellow + desc = "A tank of oxygen, this one is yellow." + icon_state = "oxygen_f" + dog_fashion = null + +/obj/item/tank/oxygen/red + desc = "A tank of oxygen, this one is red." + icon_state = "oxygen_fr" + dog_fashion = null + + +/* + * Anesthetic + */ +/obj/item/tank/anesthetic + name = "anesthetic tank" + desc = "A tank with an N2O/O2 gas mix." + icon_state = "anesthetic" + item_state = "an_tank" + +/obj/item/tank/anesthetic/New() + ..() + + air_contents.oxygen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD + + var/datum/gas/sleeping_agent/trace_gas = new() + trace_gas.moles = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD + + air_contents.trace_gases += trace_gas + +/* + * Air + */ +/obj/item/tank/air + name = "air tank" + desc = "Mixed anyone?" + icon_state = "air" + item_state = "air" + +/obj/item/tank/air/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.oxygen < 1) + . += "The meter on [src] indicates you are almost out of air!" + playsound(user, 'sound/effects/alert.ogg', 50, 1) + +/obj/item/tank/air/New() + ..() + air_contents.oxygen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * O2STANDARD + air_contents.nitrogen = (6*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) * N2STANDARD + +/* + * Plasma + */ +/obj/item/tank/plasma + name = "plasma tank" + desc = "Contains dangerous plasma. Do not inhale. Warning: extremely flammable." + icon_state = "plasma" + flags = CONDUCT + slot_flags = null //they have no straps! + +/obj/item/tank/plasma/New() + ..() + air_contents.toxins = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/plasma/attackby(obj/item/W as obj, mob/user as mob, params) + ..() + + if(istype(W, /obj/item/flamethrower)) + var/obj/item/flamethrower/F = W + if((!F.status)||(F.ptank)) return + master = F + F.ptank = src + user.unEquip(src) + loc = F + F.update_icon() + +/obj/item/tank/plasma/full/New() + ..() + air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/plasma/plasmaman + name = "plasma internals tank" + desc = "A tank of plasma gas designed specifically for use as internals, particularly for plasma-based lifeforms. If you're not a Plasmaman, you probably shouldn't use this." + icon_state = "plasmaman_tank" + item_state = "plasmaman_tank" + force = 10 + distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE + +/obj/item/tank/plasma/plasmaman/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.toxins < 0.2) + . += "The meter on [src] indicates you are almost out of plasma!" + playsound(user, 'sound/effects/alert.ogg', 50, 1) + + +/obj/item/tank/plasma/plasmaman/belt + icon_state = "plasmaman_tank_belt" + item_state = "plasmaman_tank_belt" + slot_flags = SLOT_BELT + force = 5 + volume = 25 + w_class = WEIGHT_CLASS_SMALL + +/obj/item/tank/plasma/plasmaman/belt/full/New() + ..() + air_contents.toxins = (10 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C) + +/* + * Emergency Oxygen + */ +/obj/item/tank/emergency_oxygen + name = "emergency oxygen tank" + desc = "Used for emergencies. Contains very little oxygen, so try to conserve it until you actually need it." + icon_state = "emergency" + flags = CONDUCT + slot_flags = SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + force = 4.0 + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + volume = 3 //Tiny. Real life equivalents only have 21 breaths of oxygen in them. They're EMERGENCY tanks anyway -errorage (dangercon 2011) + + +/obj/item/tank/emergency_oxygen/New() + ..() + air_contents.oxygen = (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/emergency_oxygen/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.oxygen < 0.2) + . += "The meter on [src] indicates you are almost out of air!" + playsound(user, 'sound/effects/alert.ogg', 50, 1) + +obj/item/tank/emergency_oxygen/empty/New() + ..() + air_contents.oxygen = null + +/obj/item/tank/emergency_oxygen/engi + name = "extended-capacity emergency oxygen tank" + icon_state = "emergency_engi" + volume = 6 + +obj/item/tank/emergency_oxygen/engi/empty/New() + ..() + air_contents.oxygen = null + +/obj/item/tank/emergency_oxygen/syndi + name = "suspicious emergency oxygen tank" + icon_state = "emergency_syndi" + desc = "A dark emergency oxygen tank. The label on the back reads \"Original Oxygen Tank Design, Do Not Steal.\"" + volume = 6 + +/obj/item/tank/emergency_oxygen/double + name = "double emergency oxygen tank" + icon_state = "emergency_double" + volume = 10 + +obj/item/tank/emergency_oxygen/double/empty/New() + ..() + air_contents.oxygen = null + +/obj/item/tank/emergency_oxygen/double/full + name = "pressurized double emergency oxygen tank" + desc = "Used for \"emergencies,\" it actually contains a fair amount of oxygen." + +/obj/item/tank/emergency_oxygen/double/full/New() + ..() + air_contents.oxygen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/* + * Nitrogen + */ +/obj/item/tank/nitrogen + name = "nitrogen tank" + desc = "A tank of nitrogen." + icon_state = "oxygen_fr" + distribute_pressure = ONE_ATMOSPHERE*O2STANDARD + sprite_sheets = list("Vox Armalis" = 'icons/mob/species/armalis/back.dmi') //Do it for Big Bird. + +/obj/item/tank/nitrogen/New() + ..() + air_contents.nitrogen = (3*ONE_ATMOSPHERE)*70/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/nitrogen/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0 && air_contents.nitrogen < 10) + . += "The meter on the [src.name] indicates you are almost out of air!" + +/obj/item/tank/emergency_oxygen/vox + name = "vox specialized nitrogen tank" + desc = "A high-tech nitrogen tank designed specifically for Vox." + icon_state = "emergency_vox" + volume = 25 + sprite_sheets = list("Vox Armalis" = 'icons/mob/species/armalis/belt.dmi') //Do it for Big Bird. + +/obj/item/tank/emergency_oxygen/vox/New() + ..() + air_contents.oxygen -= (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.nitrogen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/emergency_oxygen/nitrogen + name = "emergency nitrogen tank" + desc = "An emergency tank designed specifically for Vox." + icon_state = "emergency_nitrogen" + volume = 3 + +/obj/item/tank/emergency_oxygen/nitrogen/New() + ..() + air_contents.oxygen -= (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.nitrogen = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + +/obj/item/tank/emergency_oxygen/plasma + name = "emergency plasma tank" + desc = "An emergency tank designed specifically for Plasmamen." + icon_state = "emergency_p" + volume = 3 + +/obj/item/tank/emergency_oxygen/plasma/New() + ..() + air_contents.oxygen -= (3*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) + air_contents.toxins = (10*ONE_ATMOSPHERE)*volume/(R_IDEAL_GAS_EQUATION*T20C) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index f85af5c093d0..9ded79472024 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -1,292 +1,292 @@ -#define TANK_MAX_RELEASE_PRESSURE (3*ONE_ATMOSPHERE) - -/obj/item/tank - name = "tank" - icon = 'icons/obj/tank.dmi' - flags = CONDUCT - slot_flags = SLOT_BACK - hitsound = 'sound/weapons/smash.ogg' - w_class = WEIGHT_CLASS_NORMAL - pressure_resistance = ONE_ATMOSPHERE * 5 - force = 5.0 - throwforce = 10.0 - throw_speed = 1 - throw_range = 4 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 30) - actions_types = list(/datum/action/item_action/set_internals) - var/datum/gas_mixture/air_contents = null - var/distribute_pressure = ONE_ATMOSPHERE - var/integrity = 3 - var/volume = 70 - -/obj/item/tank/New() - ..() - - air_contents = new /datum/gas_mixture() - air_contents.volume = volume //liters - air_contents.temperature = T20C - - START_PROCESSING(SSobj, src) - return - -/obj/item/tank/Destroy() - QDEL_NULL(air_contents) - - STOP_PROCESSING(SSobj, src) - - return ..() - - -/obj/item/tank/ui_action_click(mob/user) - toggle_internals(user) - -/obj/item/tank/proc/toggle_internals(mob/user, silent = FALSE) - var/mob/living/carbon/C = user - if(!istype(C)) - return 0 - - if(C.internal == src) - to_chat(C, "You close \the [src] valve.") - C.internal = null - else - var/can_open_valve = 0 - if(C.get_organ_slot("breathing_tube")) - can_open_valve = 1 - else if(C.wear_mask && C.wear_mask.flags & AIRTIGHT) - can_open_valve = 1 - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && H.head.flags & AIRTIGHT) - can_open_valve = 1 - - if(can_open_valve) - if(C.internal) - if(!silent) - to_chat(C, "You switch your internals to [src].") - else - if(!silent) - to_chat(C, "You open \the [src] valve.") - C.internal = src - else - if(!silent) - to_chat(C, "You are not wearing a suitable mask or helmet.") - return 0 - - C.update_action_buttons_icon() - - -/obj/item/tank/examine(mob/user) - . = ..() - - var/obj/icon = src - if(istype(loc, /obj/item/assembly)) - icon = loc - - if(!in_range(src, user)) - if(icon == src) - . += "It's \a [bicon(icon)][src]! If you want any more information you'll need to get closer." - return - - var/celsius_temperature = air_contents.temperature-T0C - var/descriptive - - if(celsius_temperature < 20) - descriptive = "cold" - else if(celsius_temperature < 40) - descriptive = "room temperature" - else if(celsius_temperature < 80) - descriptive = "lukewarm" - else if(celsius_temperature < 100) - descriptive = "warm" - else if(celsius_temperature < 300) - descriptive = "hot" - else - descriptive = "furiously hot" - - . += "\The [bicon(icon)][src] feels [descriptive]" - -/obj/item/tank/blob_act(obj/structure/blob/B) - if(B && B.loc == loc) - var/turf/location = get_turf(src) - if(!location) - qdel(src) - - if(air_contents) - location.assume_air(air_contents) - - qdel(src) - -/obj/item/tank/deconstruct(disassembled = TRUE) - if(!disassembled) - var/turf/T = get_turf(src) - if(T) - T.assume_air(air_contents) - air_update_turf() - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - qdel(src) - -/obj/item/tank/attackby(obj/item/W as obj, mob/user as mob, params) - ..() - - add_fingerprint(user) - if(istype(loc, /obj/item/assembly)) - icon = loc - - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - - if(istype(W, /obj/item/assembly_holder)) - bomb_assemble(W,user) - -/obj/item/tank/attack_self(mob/user as mob) - if(!(air_contents)) - return - - ui_interact(user) - -/obj/item/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "tanks.tmpl", "Tank", 500, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/item/tank/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/using_internal - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.internal == src) - using_internal = 1 - - var/data[0] - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) - data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) - data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["valveOpen"] = using_internal ? 1 : 0 - - data["maskConnected"] = 0 - - if(iscarbon(loc)) - var/mob/living/carbon/C = loc - if(C.internal == src) - data["maskConnected"] = 1 - else - if(C.wear_mask && (C.wear_mask.flags & AIRTIGHT)) - data["maskConnected"] = 1 - else if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && (H.head.flags & AIRTIGHT)) - data["maskConnected"] = 1 - - return data - -/obj/item/tank/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["dist_p"]) - if(href_list["dist_p"] == "reset") - distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - else if(href_list["dist_p"] == "max") - distribute_pressure = TANK_MAX_RELEASE_PRESSURE - else - var/cp = text2num(href_list["dist_p"]) - distribute_pressure += cp - distribute_pressure = min(max(round(distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE) - - if(href_list["stat"]) - toggle_internals(usr) - - add_fingerprint(usr) - return 1 - - -/obj/item/tank/remove_air(amount) - return air_contents.remove(amount) - -/obj/item/tank/return_air() - return air_contents - -/obj/item/tank/assume_air(datum/gas_mixture/giver) - air_contents.merge(giver) - - check_status() - return 1 - -/obj/item/tank/proc/remove_air_volume(volume_to_return) - if(!air_contents) - return null - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < distribute_pressure) - distribute_pressure = tank_pressure - - var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - return remove_air(moles_needed) - -/obj/item/tank/process() - //Allow for reactions - air_contents.react() - check_status() - - -/obj/item/tank/proc/check_status() - //Handle exploding, leaking, and rupturing of the tank - - if(!air_contents) - return 0 - - var/pressure = air_contents.return_pressure() - if(pressure > TANK_FRAGMENT_PRESSURE) - if(!istype(loc,/obj/item/transfer_valve)) - message_admins("Explosive tank rupture! last key to touch the tank was [fingerprintslast] (JMP)") - log_game("Explosive tank rupture! last key to touch the tank was [fingerprintslast] at [x], [y], [z]") -// to_chat(world, "[x],[y] tank is exploding: [pressure] kPa") - //Give the gas a chance to build up more pressure through reacting - air_contents.react() - air_contents.react() - air_contents.react() - pressure = air_contents.return_pressure() - var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE - var/turf/epicenter = get_turf(loc) - -// to_chat(world, "Exploding Pressure: [pressure] kPa, intensity: [range]") - - explosion(epicenter, round(range*0.25), round(range*0.5), round(range), round(range*1.5)) - if(istype(loc,/obj/item/transfer_valve)) - qdel(loc) - else - qdel(src) - - else if(pressure > TANK_RUPTURE_PRESSURE) -// to_chat(world, "[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]") - if(integrity <= 0) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(air_contents) - playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) - qdel(src) - else - integrity-- - - else if(pressure > TANK_LEAK_PRESSURE) -// to_chat(world, "[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]") - if(integrity <= 0) - var/turf/simulated/T = get_turf(src) - if(!T) - return - var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25) - T.assume_air(leaked_gas) - else - integrity-- - - else if(integrity < 3) - integrity++ +#define TANK_MAX_RELEASE_PRESSURE (3*ONE_ATMOSPHERE) + +/obj/item/tank + name = "tank" + icon = 'icons/obj/tank.dmi' + flags = CONDUCT + slot_flags = SLOT_BACK + hitsound = 'sound/weapons/smash.ogg' + w_class = WEIGHT_CLASS_NORMAL + pressure_resistance = ONE_ATMOSPHERE * 5 + force = 5.0 + throwforce = 10.0 + throw_speed = 1 + throw_range = 4 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 30) + actions_types = list(/datum/action/item_action/set_internals) + var/datum/gas_mixture/air_contents = null + var/distribute_pressure = ONE_ATMOSPHERE + var/integrity = 3 + var/volume = 70 + +/obj/item/tank/New() + ..() + + air_contents = new /datum/gas_mixture() + air_contents.volume = volume //liters + air_contents.temperature = T20C + + START_PROCESSING(SSobj, src) + return + +/obj/item/tank/Destroy() + QDEL_NULL(air_contents) + + STOP_PROCESSING(SSobj, src) + + return ..() + + +/obj/item/tank/ui_action_click(mob/user) + toggle_internals(user) + +/obj/item/tank/proc/toggle_internals(mob/user, silent = FALSE) + var/mob/living/carbon/C = user + if(!istype(C)) + return 0 + + if(C.internal == src) + to_chat(C, "You close \the [src] valve.") + C.internal = null + else + var/can_open_valve = 0 + if(C.get_organ_slot("breathing_tube")) + can_open_valve = 1 + else if(C.wear_mask && C.wear_mask.flags & AIRTIGHT) + can_open_valve = 1 + else if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(H.head && H.head.flags & AIRTIGHT) + can_open_valve = 1 + + if(can_open_valve) + if(C.internal) + if(!silent) + to_chat(C, "You switch your internals to [src].") + else + if(!silent) + to_chat(C, "You open \the [src] valve.") + C.internal = src + else + if(!silent) + to_chat(C, "You are not wearing a suitable mask or helmet.") + return 0 + + C.update_action_buttons_icon() + + +/obj/item/tank/examine(mob/user) + . = ..() + + var/obj/icon = src + if(istype(loc, /obj/item/assembly)) + icon = loc + + if(!in_range(src, user)) + if(icon == src) + . += "It's \a [bicon(icon)][src]! If you want any more information you'll need to get closer." + return + + var/celsius_temperature = air_contents.temperature-T0C + var/descriptive + + if(celsius_temperature < 20) + descriptive = "cold" + else if(celsius_temperature < 40) + descriptive = "room temperature" + else if(celsius_temperature < 80) + descriptive = "lukewarm" + else if(celsius_temperature < 100) + descriptive = "warm" + else if(celsius_temperature < 300) + descriptive = "hot" + else + descriptive = "furiously hot" + + . += "\The [bicon(icon)][src] feels [descriptive]" + +/obj/item/tank/blob_act(obj/structure/blob/B) + if(B && B.loc == loc) + var/turf/location = get_turf(src) + if(!location) + qdel(src) + + if(air_contents) + location.assume_air(air_contents) + + qdel(src) + +/obj/item/tank/deconstruct(disassembled = TRUE) + if(!disassembled) + var/turf/T = get_turf(src) + if(T) + T.assume_air(air_contents) + air_update_turf() + playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) + qdel(src) + +/obj/item/tank/attackby(obj/item/W as obj, mob/user as mob, params) + ..() + + add_fingerprint(user) + if(istype(loc, /obj/item/assembly)) + icon = loc + + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + + if(istype(W, /obj/item/assembly_holder)) + bomb_assemble(W,user) + +/obj/item/tank/attack_self(mob/user as mob) + if(!(air_contents)) + return + + ui_interact(user) + +/obj/item/tank/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "tanks.tmpl", "Tank", 500, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/item/tank/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/using_internal + if(iscarbon(loc)) + var/mob/living/carbon/C = loc + if(C.internal == src) + using_internal = 1 + + var/data[0] + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(distribute_pressure ? distribute_pressure : 0) + data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) + data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) + data["valveOpen"] = using_internal ? 1 : 0 + + data["maskConnected"] = 0 + + if(iscarbon(loc)) + var/mob/living/carbon/C = loc + if(C.internal == src) + data["maskConnected"] = 1 + else + if(C.wear_mask && (C.wear_mask.flags & AIRTIGHT)) + data["maskConnected"] = 1 + else if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(H.head && (H.head.flags & AIRTIGHT)) + data["maskConnected"] = 1 + + return data + +/obj/item/tank/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["dist_p"]) + if(href_list["dist_p"] == "reset") + distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE + else if(href_list["dist_p"] == "max") + distribute_pressure = TANK_MAX_RELEASE_PRESSURE + else + var/cp = text2num(href_list["dist_p"]) + distribute_pressure += cp + distribute_pressure = min(max(round(distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE) + + if(href_list["stat"]) + toggle_internals(usr) + + add_fingerprint(usr) + return 1 + + +/obj/item/tank/remove_air(amount) + return air_contents.remove(amount) + +/obj/item/tank/return_air() + return air_contents + +/obj/item/tank/assume_air(datum/gas_mixture/giver) + air_contents.merge(giver) + + check_status() + return 1 + +/obj/item/tank/proc/remove_air_volume(volume_to_return) + if(!air_contents) + return null + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < distribute_pressure) + distribute_pressure = tank_pressure + + var/moles_needed = distribute_pressure*volume_to_return/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + return remove_air(moles_needed) + +/obj/item/tank/process() + //Allow for reactions + air_contents.react() + check_status() + + +/obj/item/tank/proc/check_status() + //Handle exploding, leaking, and rupturing of the tank + + if(!air_contents) + return 0 + + var/pressure = air_contents.return_pressure() + if(pressure > TANK_FRAGMENT_PRESSURE) + if(!istype(loc,/obj/item/transfer_valve)) + message_admins("Explosive tank rupture! last key to touch the tank was [fingerprintslast] (JMP)") + log_game("Explosive tank rupture! last key to touch the tank was [fingerprintslast] at [x], [y], [z]") +// to_chat(world, "[x],[y] tank is exploding: [pressure] kPa") + //Give the gas a chance to build up more pressure through reacting + air_contents.react() + air_contents.react() + air_contents.react() + pressure = air_contents.return_pressure() + var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE + var/turf/epicenter = get_turf(loc) + +// to_chat(world, "Exploding Pressure: [pressure] kPa, intensity: [range]") + + explosion(epicenter, round(range*0.25), round(range*0.5), round(range), round(range*1.5)) + if(istype(loc,/obj/item/transfer_valve)) + qdel(loc) + else + qdel(src) + + else if(pressure > TANK_RUPTURE_PRESSURE) +// to_chat(world, "[x],[y] tank is rupturing: [pressure] kPa, integrity [integrity]") + if(integrity <= 0) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(air_contents) + playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3) + qdel(src) + else + integrity-- + + else if(pressure > TANK_LEAK_PRESSURE) +// to_chat(world, "[x],[y] tank is leaking: [pressure] kPa, integrity [integrity]") + if(integrity <= 0) + var/turf/simulated/T = get_turf(src) + if(!T) + return + var/datum/gas_mixture/leaked_gas = air_contents.remove_ratio(0.25) + T.assume_air(leaked_gas) + else + integrity-- + + else if(integrity < 3) + integrity++ diff --git a/code/game/objects/items/weapons/teleportation.dm b/code/game/objects/items/weapons/teleportation.dm index 117bb90c35f8..4ac9651ea65c 100644 --- a/code/game/objects/items/weapons/teleportation.dm +++ b/code/game/objects/items/weapons/teleportation.dm @@ -1,149 +1,149 @@ -/* Teleportation devices. - * Contains: - * Locator - * Hand-tele - */ - -/* - * Locator - */ -/obj/item/locator - name = "locator" - desc = "Used to track those with locater implants." - icon = 'icons/obj/device.dmi' - icon_state = "locator" - var/temp = null - var/frequency = 1451 - var/broadcasting = null - var/listening = 1.0 - flags = CONDUCT - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - materials = list(MAT_METAL=400) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - origin_tech = "magnets=3;bluespace=2" - -/obj/item/locator/attack_self(mob/user as mob) - add_fingerprint(usr) - var/dat - if(temp) - dat = "[src.temp]

    Clear" - else - dat = {" -Persistent Signal Locator
    -Frequency: -- -- [format_frequency(src.frequency)] -+ -+
    - -Refresh"} - user << browse(dat, "window=radio") - onclose(user, "radio") - return - -/obj/item/locator/Topic(href, href_list) - if(..()) - return 1 - - var/turf/current_location = get_turf(usr)//What turf is the user on? - if(!current_location || is_admin_level(current_location.z))//If turf was not found or they're in the admin zone - to_chat(usr, "\The [src] is malfunctioning.") - return 1 - - if(href_list["refresh"]) - temp = "Persistent Signal Locator
    " - var/turf/sr = get_turf(src) - - if(sr) - temp += "Located Beacons:
    " - - for(var/obj/item/radio/beacon/W in GLOB.beacons) - if(W.frequency == frequency && !W.syndicate) - if(W && W.z == z) - var/turf/TB = get_turf(W) - temp += "[W.code]: [TB.x], [TB.y], [TB.z]
    " - - temp += "Located Implants:
    " - for(var/obj/item/implant/tracking/T in GLOB.tracked_implants) - if(!T.implanted || !T.imp_in) - continue - var/turf/Tr = get_turf(T) - - if(Tr && Tr.z == sr.z) - temp += "[T.id]: [Tr.x], [Tr.y], [Tr.z]
    " - - temp += "You are at \[[sr.x],[sr.y],[sr.z]\]." - temp += "

    Refresh
    " - else - temp += "Processing error: Unable to locate orbital position.
    " - else - if(href_list["freq"]) - frequency += text2num(href_list["freq"]) - frequency = sanitize_frequency(frequency) - else - if(href_list["temp"]) - temp = null - - attack_self(usr) - return 1 - -/* - * Hand-tele - */ -/obj/item/hand_tele - name = "hand tele" - desc = "A portable item using blue-space technology." - icon = 'icons/obj/device.dmi' - icon_state = "hand_tele" - item_state = "electronic" - throwforce = 0 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 5 - materials = list(MAT_METAL=10000) - origin_tech = "magnets=3;bluespace=4" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/active_portals = 0 - -/obj/item/hand_tele/attack_self(mob/user as mob) - var/turf/current_location = get_turf(user)//What turf is the user on? - if(!current_location||!is_teleport_allowed(current_location.z))//If turf was not found or they're somewhere teleproof - to_chat(user, "\The [src] is malfunctioning.") - return - var/list/L = list( ) - for(var/obj/machinery/computer/teleporter/com in world) - if(com.target) - if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged) - L["[com.id] (Active)"] = com.target - else - L["[com.id] (Inactive)"] = com.target - var/list/turfs = list( ) - var/area/A - for(var/turf/T in orange(10)) - if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb - if(T.y>world.maxy-8 || T.y<8) continue - A = get_area(T) - if(A.tele_proof == 1) continue // Telescience-proofed areas require a beacon. - turfs += T - if(turfs.len) - L["None (Dangerous)"] = pick(turfs) - var/t1 = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in L - if(!t1 || (!user.is_in_active_hand(src) || user.stat || user.restrained())) - return - if(active_portals >= 3) - user.show_message("\The [src] is recharging!") - return - var/T = L[t1] - user.show_message("Locked In.", 2) - var/obj/effect/portal/P = new /obj/effect/portal(get_turf(src), T, src) - try_move_adjacent(P) - active_portals++ - add_fingerprint(user) - return - -/obj/item/hand_tele/portal_destroyed(obj/effect/portal/P) - active_portals-- \ No newline at end of file +/* Teleportation devices. + * Contains: + * Locator + * Hand-tele + */ + +/* + * Locator + */ +/obj/item/locator + name = "locator" + desc = "Used to track those with locater implants." + icon = 'icons/obj/device.dmi' + icon_state = "locator" + var/temp = null + var/frequency = 1451 + var/broadcasting = null + var/listening = 1.0 + flags = CONDUCT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + materials = list(MAT_METAL=400) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + origin_tech = "magnets=3;bluespace=2" + +/obj/item/locator/attack_self(mob/user as mob) + add_fingerprint(usr) + var/dat + if(temp) + dat = "[src.temp]

    Clear" + else + dat = {" +Persistent Signal Locator
    +Frequency: +- +- [format_frequency(src.frequency)] ++ ++
    + +Refresh"} + user << browse(dat, "window=radio") + onclose(user, "radio") + return + +/obj/item/locator/Topic(href, href_list) + if(..()) + return 1 + + var/turf/current_location = get_turf(usr)//What turf is the user on? + if(!current_location || is_admin_level(current_location.z))//If turf was not found or they're in the admin zone + to_chat(usr, "\The [src] is malfunctioning.") + return 1 + + if(href_list["refresh"]) + temp = "Persistent Signal Locator
    " + var/turf/sr = get_turf(src) + + if(sr) + temp += "Located Beacons:
    " + + for(var/obj/item/radio/beacon/W in GLOB.beacons) + if(W.frequency == frequency && !W.syndicate) + if(W && W.z == z) + var/turf/TB = get_turf(W) + temp += "[W.code]: [TB.x], [TB.y], [TB.z]
    " + + temp += "Located Implants:
    " + for(var/obj/item/implant/tracking/T in GLOB.tracked_implants) + if(!T.implanted || !T.imp_in) + continue + var/turf/Tr = get_turf(T) + + if(Tr && Tr.z == sr.z) + temp += "[T.id]: [Tr.x], [Tr.y], [Tr.z]
    " + + temp += "You are at \[[sr.x],[sr.y],[sr.z]\]." + temp += "

    Refresh
    " + else + temp += "Processing error: Unable to locate orbital position.
    " + else + if(href_list["freq"]) + frequency += text2num(href_list["freq"]) + frequency = sanitize_frequency(frequency) + else + if(href_list["temp"]) + temp = null + + attack_self(usr) + return 1 + +/* + * Hand-tele + */ +/obj/item/hand_tele + name = "hand tele" + desc = "A portable item using blue-space technology." + icon = 'icons/obj/device.dmi' + icon_state = "hand_tele" + item_state = "electronic" + throwforce = 0 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 5 + materials = list(MAT_METAL=10000) + origin_tech = "magnets=3;bluespace=4" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/active_portals = 0 + +/obj/item/hand_tele/attack_self(mob/user as mob) + var/turf/current_location = get_turf(user)//What turf is the user on? + if(!current_location||!is_teleport_allowed(current_location.z))//If turf was not found or they're somewhere teleproof + to_chat(user, "\The [src] is malfunctioning.") + return + var/list/L = list( ) + for(var/obj/machinery/computer/teleporter/com in world) + if(com.target) + if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged) + L["[com.id] (Active)"] = com.target + else + L["[com.id] (Inactive)"] = com.target + var/list/turfs = list( ) + var/area/A + for(var/turf/T in orange(10)) + if(T.x>world.maxx-8 || T.x<8) continue //putting them at the edge is dumb + if(T.y>world.maxy-8 || T.y<8) continue + A = get_area(T) + if(A.tele_proof == 1) continue // Telescience-proofed areas require a beacon. + turfs += T + if(turfs.len) + L["None (Dangerous)"] = pick(turfs) + var/t1 = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in L + if(!t1 || (!user.is_in_active_hand(src) || user.stat || user.restrained())) + return + if(active_portals >= 3) + user.show_message("\The [src] is recharging!") + return + var/T = L[t1] + user.show_message("Locked In.", 2) + var/obj/effect/portal/P = new /obj/effect/portal(get_turf(src), T, src) + try_move_adjacent(P) + active_portals++ + add_fingerprint(user) + return + +/obj/item/hand_tele/portal_destroyed(obj/effect/portal/P) + active_portals-- diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm index f6c6c0ea150b..012fcf79b109 100644 --- a/code/game/objects/items/weapons/tools.dm +++ b/code/game/objects/items/weapons/tools.dm @@ -1,785 +1,785 @@ -#define HEALPERWELD 15 - -/* Tools! - * Note: Multitools are in devices - * - * Contains: - * Wrench - * Screwdriver - * Wirecutters - * Welding Tool - * Crowbar - * Revolver Conversion Kit - */ - -//Wrench -/obj/item/wrench - name = "wrench" - desc = "A wrench with common uses. Can be found in your hand." - icon = 'icons/obj/tools.dmi' - icon_state = "wrench" - flags = CONDUCT - slot_flags = SLOT_BELT - force = 5 - throwforce = 7 - usesound = 'sound/items/ratchet.ogg' - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=150) - origin_tech = "materials=1;engineering=1" - attack_verb = list("bashed", "battered", "bludgeoned", "whacked") - toolspeed = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) - -/obj/item/wrench/suicide_act(mob/user) - user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) - return BRUTELOSS - -/obj/item/wrench/cyborg - name = "automatic wrench" - desc = "An advanced robotic wrench. Can be found in construction cyborgs." - toolspeed = 0.5 - -/obj/item/wrench/brass - name = "brass wrench" - desc = "A brass wrench. It's faintly warm to the touch." - icon_state = "wrench_brass" - toolspeed = 0.5 - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/wrench/abductor - name = "alien wrench" - desc = "A polarized wrench. It causes anything placed between the jaws to turn." - icon = 'icons/obj/abductor.dmi' - icon_state = "wrench" - usesound = 'sound/effects/empulse.ogg' - toolspeed = 0.1 - origin_tech = "materials=5;engineering=5;abductor=3" - -/obj/item/wrench/power - name = "hand drill" - desc = "A simple powered drill with a bolt bit." - icon_state = "drill_bolt" - item_state = "drill" - usesound = 'sound/items/drill_use.ogg' - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get - force = 8 //might or might not be too high, subject to change - throwforce = 8 - attack_verb = list("drilled", "screwed", "jabbed") - toolspeed = 0.25 - -/obj/item/wrench/power/attack_self(mob/user) - playsound(get_turf(user),'sound/items/change_drill.ogg', 50, 1) - var/obj/item/wirecutters/power/s_drill = new /obj/item/screwdriver/power - to_chat(user, "You attach the screwdriver bit to [src].") - qdel(src) - user.put_in_active_hand(s_drill) - -/obj/item/wrench/power/suicide_act(mob/user) - user.visible_message("[user] is pressing [src] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!") - return BRUTELOSS - -/obj/item/wrench/medical - name = "medical wrench" - desc = "A medical wrench with common (medical?) uses. Can be found in your hand." - icon_state = "wrench_medical" - force = 2 //MEDICAL - throwforce = 4 - origin_tech = "materials=1;engineering=1;biotech=3" - attack_verb = list("wrenched", "medicaled", "tapped", "jabbed", "whacked") - -/obj/item/wrench/medical/suicide_act(mob/user) - user.visible_message("[user] is praying to the medical wrench to take [user.p_their()] soul. It looks like [user.p_theyre()] trying to commit suicide!") - // TODO Make them glow with the power of the M E D I C A L W R E N C H - // during their ascension - - // Stun stops them from wandering off - user.Stun(5) - playsound(loc, 'sound/effects/pray.ogg', 50, 1, -1) - - // Let the sound effect finish playing - sleep(20) - - if(!user) - return - - for(var/obj/item/W in user) - user.unEquip(W) - - var/obj/item/wrench/medical/W = new /obj/item/wrench/medical(loc) - W.add_fingerprint(user) - W.desc += " For some reason, it reminds you of [user.name]." - - if(!user) - return - - user.dust() - return OBLITERATION - -//Screwdriver -/obj/item/screwdriver - name = "screwdriver" - desc = "You can be totally screwy with this." - icon = 'icons/obj/tools.dmi' - icon_state = "screwdriver_map" - flags = CONDUCT - slot_flags = SLOT_BELT - force = 5 - w_class = WEIGHT_CLASS_TINY - throwforce = 5 - throw_speed = 3 - throw_range = 5 - materials = list(MAT_METAL=75) - attack_verb = list("stabbed") - hitsound = 'sound/weapons/bladeslice.ogg' - usesound = 'sound/items/screwdriver.ogg' - toolspeed = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) - var/random_color = TRUE //if the screwdriver uses random coloring - -/obj/item/screwdriver/nuke - name = "screwdriver" - desc = "A screwdriver with an ultra thin tip." - icon_state = "screwdriver_nuke" - toolspeed = 0.5 - -/obj/item/screwdriver/suicide_act(mob/user) - user.visible_message("[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!") - return BRUTELOSS - -/obj/item/screwdriver/New(loc, var/param_color = null) - ..() - if(random_color) - if(!param_color) - param_color = pick("red","blue","pink","brown","green","cyan","yellow") - icon_state = "screwdriver_[param_color]" - - if (prob(75)) - src.pixel_y = rand(0, 16) - -/obj/item/screwdriver/attack(mob/living/carbon/M, mob/living/carbon/user) - if(!istype(M) || user.a_intent == INTENT_HELP) - return ..() - if(user.zone_selected != "eyes" && user.zone_selected != "head") - return ..() - if(HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "You don't want to harm [M]!") - return - if((CLUMSY in user.mutations) && prob(50)) - M = user - return eyestab(M,user) - -/obj/item/screwdriver/brass - name = "brass screwdriver" - desc = "A screwdriver made of brass. The handle feels freezing cold." - icon_state = "screwdriver_brass" - toolspeed = 0.5 - random_color = FALSE - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/screwdriver/abductor - name = "alien screwdriver" - desc = "An ultrasonic screwdriver." - icon = 'icons/obj/abductor.dmi' - icon_state = "screwdriver" - usesound = 'sound/items/pshoom.ogg' - toolspeed = 0.1 - random_color = FALSE - -/obj/item/screwdriver/power - name = "hand drill" - desc = "A simple hand drill with a screwdriver bit attached." - icon_state = "drill_screw" - item_state = "drill" - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get - force = 8 //might or might not be too high, subject to change - throwforce = 8 - throw_speed = 2 - throw_range = 3//it's heavier than a screw driver/wrench, so it does more damage, but can't be thrown as far - attack_verb = list("drilled", "screwed", "jabbed","whacked") - hitsound = 'sound/items/drill_hit.ogg' - usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.25 - random_color = FALSE - -/obj/item/screwdriver/power/suicide_act(mob/user) - user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!") - return BRUTELOSS - -/obj/item/screwdriver/power/attack_self(mob/user) - playsound(get_turf(user), 'sound/items/change_drill.ogg', 50, 1) - var/obj/item/wrench/power/b_drill = new /obj/item/wrench/power - to_chat(user, "You attach the bolt driver bit to [src].") - qdel(src) - user.put_in_active_hand(b_drill) - -/obj/item/screwdriver/cyborg - name = "powered screwdriver" - desc = "An electrical screwdriver, designed to be both precise and quick." - usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.5 - -//Wirecutters -/obj/item/wirecutters - name = "wirecutters" - desc = "This cuts wires." - icon = 'icons/obj/tools.dmi' - icon_state = "cutters" - flags = CONDUCT - slot_flags = SLOT_BELT - force = 6 - throw_speed = 3 - throw_range = 7 - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=80) - origin_tech = "materials=1;engineering=1" - attack_verb = list("pinched", "nipped") - hitsound = 'sound/items/wirecutter.ogg' - usesound = 'sound/items/wirecutter.ogg' - sharp = 1 - toolspeed = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) - var/random_color = TRUE - -/obj/item/wirecutters/New(loc, param_color = null) - ..() - if(random_color) - if(!param_color) - param_color = pick("yellow", "red") - icon_state = "cutters_[param_color]" - -/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user) - if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable)) - user.visible_message("[user] cuts [C]'s restraints with [src]!") - QDEL_NULL(C.handcuffed) - if(C.buckled && C.buckled.buckle_requires_restraints) - C.buckled.unbuckle_mob(C) - C.update_handcuffed() - return - else - ..() - -/obj/item/wirecutters/suicide_act(mob/user) - user.visible_message("[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, usesound, 50, 1, -1) - return BRUTELOSS - -/obj/item/wirecutters/brass - name = "brass wirecutters" - desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch." - icon_state = "cutters_brass" - toolspeed = 0.5 - random_color = FALSE - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/wirecutters/abductor - name = "alien wirecutters" - desc = "Extremely sharp wirecutters, made out of a silvery-green metal." - icon = 'icons/obj/abductor.dmi' - icon_state = "cutters" - toolspeed = 0.1 - origin_tech = "materials=5;engineering=4;abductor=3" - random_color = FALSE - -/obj/item/wirecutters/cyborg - name = "wirecutters" - desc = "This cuts wires." - toolspeed = 0.5 - -/obj/item/wirecutters/power - name = "jaws of life" - desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a cutting head." - icon_state = "jaws_cutter" - item_state = "jawsoflife" - origin_tech = "materials=2;engineering=2" - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - usesound = 'sound/items/jaws_cut.ogg' - toolspeed = 0.25 - random_color = FALSE - -/obj/item/wirecutters/power/suicide_act(mob/user) - user.visible_message("[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!") - playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/organ/external/head/head = H.bodyparts_by_name["head"] - if(head) - head.droplimb(0, DROPLIMB_BLUNT, FALSE, TRUE) - playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) - return BRUTELOSS - -/obj/item/wirecutters/power/attack_self(mob/user) - playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) - var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power - to_chat(user, "You attach the pry jaws to [src].") - qdel(src) - user.put_in_active_hand(pryjaws) - -//Welding Tool -/obj/item/weldingtool - name = "welding tool" - desc = "A standard edition welder provided by Nanotrasen." - icon = 'icons/obj/tools.dmi' - icon_state = "welder" - item_state = "welder" - flags = CONDUCT - slot_flags = SLOT_BELT - force = 3 - throwforce = 5 - throw_speed = 3 - throw_range = 5 - hitsound = "swing_hit" - usesound = 'sound/items/welder.ogg' - var/acti_sound = 'sound/items/welderactivate.ogg' - var/deac_sound = 'sound/items/welderdeactivate.ogg' - w_class = WEIGHT_CLASS_SMALL - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) - resistance_flags = FIRE_PROOF - materials = list(MAT_METAL=70, MAT_GLASS=30) - origin_tech = "engineering=1;plasmatech=1" - toolspeed = 1 - var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2) - var/status = 1 //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower) - var/max_fuel = 20 //The max amount of fuel the welder can hold - var/change_icons = 1 - var/can_off_process = 0 - var/light_intensity = 2 //how powerful the emitted light is when used. - var/nextrefueltick = 0 - -/obj/item/weldingtool/New() - ..() - create_reagents(max_fuel) - reagents.add_reagent("fuel", max_fuel) - update_icon() - -/obj/item/weldingtool/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 0) - . += "It contains [get_fuel()] unit\s of fuel out of [max_fuel]." - -/obj/item/weldingtool/suicide_act(mob/user) - user.visible_message("[user] welds [user.p_their()] every orifice closed! It looks like [user.p_theyre()] trying to commit suicide!") - return FIRELOSS - -/obj/item/weldingtool/proc/update_torch() - overlays.Cut() - if(welding) - overlays += "[initial(icon_state)]-on" - item_state = "[initial(item_state)]1" - else - item_state = "[initial(item_state)]" - -/obj/item/weldingtool/update_icon() - if(change_icons) - var/ratio = get_fuel() / max_fuel - ratio = Ceiling(ratio*4) * 25 - if(ratio == 100) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)][ratio]" - update_torch() - ..() - -/obj/item/weldingtool/process() - switch(welding) - if(0) - force = 3 - damtype = "brute" - update_icon() - if(!can_off_process) - STOP_PROCESSING(SSobj, src) - return - //Welders left on now use up fuel, but lets not have them run out quite that fast - if(1) - force = 15 - damtype = "fire" - if(prob(5)) - remove_fuel(1) - update_icon() - - //This is to start fires. process() is only called if the welder is on. - var/turf/location = loc - if(ismob(location)) - var/mob/M = location - if(M.l_hand == src || M.r_hand == src) - location = get_turf(M) - if(isturf(location)) - location.hotspot_expose(700, 5) - -/obj/item/weldingtool/attackby(obj/item/I, mob/user, params) - if(isscrewdriver(I)) - flamethrower_screwdriver(I, user) - else if(istype(I, /obj/item/stack/rods)) - flamethrower_rods(I, user) - else - ..() - -/obj/item/weldingtool/attack(mob/M, mob/user) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected] - - if(!S) - return - - if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2) - return ..() - - if(!isOn()) //why wasn't this being checked already? - to_chat(user, "Turn on [src] before attempting repairs!") - return 1 - - if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP) - to_chat(user, "The damage is far too severe to patch over externally.") - return - - if(!S.brute_dam) - to_chat(user, "Nothing to fix!") - return - - if(get_fuel() >= 1) - if(H == user) - if(!do_mob(user, H, 10)) - return 1 - if(!remove_fuel(1,null)) - to_chat(user, "Need more welding fuel!") - var/rembrute = HEALPERWELD - var/nrembrute = 0 - var/childlist - if(!isnull(S.children)) - childlist = S.children.Copy() - var/parenthealed = FALSE - while(rembrute > 0) - var/obj/item/organ/external/E - if(S.brute_dam) - E = S - else if(LAZYLEN(childlist)) - E = pick_n_take(childlist) - if(!E.brute_dam || !E.is_robotic()) - continue - else if(S.parent && !parenthealed) - E = S.parent - parenthealed = TRUE - if(!E.brute_dam || !E.is_robotic()) - break - else - break - playsound(src.loc, usesound, 50, 1) - nrembrute = max(rembrute - E.brute_dam, 0) - E.heal_damage(rembrute,0,0,1) - rembrute = nrembrute - user.visible_message("\The [user] patches some dents on \the [M]'s [E.name] with \the [src].") - if(H.bleed_rate && H.isSynthetic()) - H.bleed_rate = 0 - user.visible_message("\The [user] patches some leaks on [M] with \the [src].") - return 1 - else - return ..() - -/obj/item/weldingtool/afterattack(atom/O, mob/user, proximity) - if(!proximity) - return - if(welding) - remove_fuel(1) - var/turf/location = get_turf(user) - location.hotspot_expose(700, 50, 1) - if(get_fuel() <= 0) - set_light(0) - - if(isliving(O)) - var/mob/living/L = O - if(L.IgniteMob()) - message_admins("[key_name_admin(user)] set [key_name_admin(L)] on fire") - log_game("[key_name(user)] set [key_name(L)] on fire") - -/obj/item/weldingtool/attack_self(mob/user) - switched_on(user) - if(welding) - set_light(light_intensity) - - update_icon() - -//Returns the amount of fuel in the welder -/obj/item/weldingtool/proc/get_fuel() - return reagents.get_reagent_amount("fuel") - -//Removes fuel from the welding tool. If a mob is passed, it will try to flash the mob's eyes. This should probably be renamed to use() -/obj/item/weldingtool/proc/remove_fuel(amount = 1, mob/living/M = null) - if(!welding || !check_fuel()) - return FALSE - if(get_fuel() >= amount) - reagents.remove_reagent("fuel", amount) - check_fuel() - if(M) - M.flash_eyes(light_intensity) - return TRUE - else - if(M) - to_chat(M, "You need more welding fuel to complete this task.") - return FALSE - -//Returns whether or not the welding tool is currently on. -/obj/item/weldingtool/proc/isOn() - return welding - -//Turns off the welder if there is no more fuel (does this really need to be its own proc?) -/obj/item/weldingtool/proc/check_fuel(mob/user) - if(get_fuel() <= 0 && welding) - switched_on(user) - update_icon() - //mob icon update - if(ismob(loc)) - var/mob/M = loc - M.update_inv_r_hand(0) - M.update_inv_l_hand(0) - return 0 - return 1 - -//Switches the welder on -/obj/item/weldingtool/proc/switched_on(mob/user) - if(!status) - to_chat(user, "[src] can't be turned on while unsecured!") - return - welding = !welding - if(welding) - if(get_fuel() >= 1) - to_chat(user, "You switch [src] on.") - playsound(loc, acti_sound, 50, 1) - force = 15 - damtype = "fire" - hitsound = 'sound/items/welder.ogg' - update_icon() - START_PROCESSING(SSobj, src) - else - to_chat(user, "You need more fuel!") - switched_off(user) - else - if(user) - to_chat(user, "You switch [src] off.") - playsound(loc, deac_sound, 50, 1) - switched_off(user) - -//Switches the welder off -/obj/item/weldingtool/proc/switched_off(mob/user) - welding = 0 - set_light(0) - - force = 3 - damtype = "brute" - hitsound = "swing_hit" - update_icon() - -/obj/item/weldingtool/proc/flamethrower_screwdriver(obj/item/I, mob/user) - if(welding) - to_chat(user, "Turn it off first!") - return - status = !status - if(status) - to_chat(user, "You resecure [src].") - else - to_chat(user, "[src] can now be attached and modified.") - add_fingerprint(user) - -/obj/item/weldingtool/proc/flamethrower_rods(obj/item/I, mob/user) - if(!status) - var/obj/item/stack/rods/R = I - if(R.use(1)) - var/obj/item/flamethrower/F = new /obj/item/flamethrower(user.loc) - if(!remove_item_from_storage(F)) - user.unEquip(src) - loc = F - F.weldtool = src - add_fingerprint(user) - to_chat(user, "You add a rod to a welder, starting to build a flamethrower.") - user.put_in_hands(F) - else - to_chat(user, "You need one rod to start building a flamethrower!") - -/obj/item/weldingtool/largetank - name = "Industrial Welding Tool" - desc = "A slightly larger welder with a larger tank." - icon_state = "indwelder" - max_fuel = 40 - materials = list(MAT_METAL=70, MAT_GLASS=60) - origin_tech = "engineering=2;plasmatech=2" - -/obj/item/weldingtool/largetank/cyborg - name = "integrated welding tool" - desc = "An advanced welder designed to be used in robotic systems." - toolspeed = 0.5 - -/obj/item/weldingtool/largetank/flamethrower_screwdriver() - return - -/obj/item/weldingtool/mini - name = "emergency welding tool" - desc = "A miniature welder used during emergencies." - icon_state = "miniwelder" - max_fuel = 10 - w_class = WEIGHT_CLASS_TINY - materials = list(MAT_METAL=30, MAT_GLASS=10) - change_icons = 0 - -/obj/item/weldingtool/mini/flamethrower_screwdriver() - return - -/obj/item/weldingtool/abductor - name = "alien welding tool" - desc = "An alien welding tool. Whatever fuel it uses, it never runs out." - icon = 'icons/obj/abductor.dmi' - icon_state = "welder" - toolspeed = 0.1 - light_intensity = 0 - change_icons = 0 - origin_tech = "plasmatech=5;engineering=5;abductor=3" - can_off_process = 1 - -/obj/item/weldingtool/abductor/process() - if(get_fuel() <= max_fuel) - reagents.add_reagent("fuel", 1) - ..() - -/obj/item/weldingtool/hugetank - name = "Upgraded Welding Tool" - desc = "An upgraded welder based off the industrial welder." - icon_state = "upindwelder" - item_state = "upindwelder" - max_fuel = 80 - materials = list(MAT_METAL=70, MAT_GLASS=120) - origin_tech = "engineering=3;plasmatech=2" - -/obj/item/weldingtool/experimental - name = "Experimental Welding Tool" - desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes." - icon_state = "exwelder" - item_state = "exwelder" - max_fuel = 40 - materials = list(MAT_METAL=70, MAT_GLASS=120) - origin_tech = "materials=4;engineering=4;bluespace=3;plasmatech=4" - change_icons = 0 - can_off_process = 1 - light_intensity = 1 - toolspeed = 0.5 - var/last_gen = 0 - -/obj/item/weldingtool/experimental/brass - name = "brass welding tool" - desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch." - icon_state = "brasswelder" - item_state = "brasswelder" - resistance_flags = FIRE_PROOF | ACID_PROOF - -obj/item/weldingtool/experimental/process() - ..() - if(get_fuel() < max_fuel && nextrefueltick < world.time) - nextrefueltick = world.time + 10 - reagents.add_reagent("fuel", 1) - -//Crowbar -/obj/item/crowbar - name = "pocket crowbar" - desc = "A small crowbar. This handy tool is useful for lots of things, such as prying floor tiles or opening unpowered doors." - icon = 'icons/obj/tools.dmi' - icon_state = "crowbar" - item_state = "crowbar" - usesound = 'sound/items/crowbar.ogg' - flags = CONDUCT - slot_flags = SLOT_BELT - force = 5 - throwforce = 7 - item_state = "crowbar" - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50) - origin_tech = "engineering=1;combat=1" - attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") - toolspeed = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) - -/obj/item/crowbar/red - icon_state = "crowbar_red" - item_state = "crowbar_red" - force = 8 - -/obj/item/crowbar/brass - name = "brass crowbar" - desc = "A brass crowbar. It feels faintly warm to the touch." - icon_state = "crowbar_brass" - item_state = "crowbar_brass" - toolspeed = 0.5 - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/crowbar/abductor - name = "alien crowbar" - desc = "A hard-light crowbar. It appears to pry by itself, without any effort required." - icon = 'icons/obj/abductor.dmi' - usesound = 'sound/weapons/sonic_jackhammer.ogg' - icon_state = "crowbar" - toolspeed = 0.1 - origin_tech = "combat=4;engineering=4;abductor=3" - -/obj/item/crowbar/large - name = "crowbar" - desc = "It's a big crowbar. It doesn't fit in your pockets, because its too big." - force = 12 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 3 - throw_range = 3 - materials = list(MAT_METAL=70) - icon_state = "crowbar_large" - item_state = "crowbar_large" - toolspeed = 0.5 - -/obj/item/crowbar/cyborg - name = "hydraulic crowbar" - desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbar in construction cyborgs." - usesound = 'sound/items/jaws_pry.ogg' - force = 10 - toolspeed = 0.5 - -/obj/item/crowbar/power - name = "jaws of life" - desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a prying head." - icon_state = "jaws_pry" - item_state = "jawsoflife" - materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) - origin_tech = "materials=2;engineering=2" - usesound = 'sound/items/jaws_pry.ogg' - force = 15 - toolspeed = 0.25 - var/airlock_open_time = 100 // Time required to open powered airlocks - -/obj/item/crowbar/power/suicide_act(mob/user) - user.visible_message("[user] is putting [user.p_their()] head in [src]. It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1) - return BRUTELOSS - -/obj/item/crowbar/power/attack_self(mob/user) - playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) - var/obj/item/wirecutters/power/cutjaws = new /obj/item/wirecutters/power - to_chat(user, "You attach the cutting jaws to [src].") - qdel(src) - user.put_in_active_hand(cutjaws) - -// Conversion kit -/obj/item/conversion_kit - name = "\improper Revolver Conversion Kit" - desc = "A professional conversion kit used to convert any knock off revolver into the real deal capable of shooting lethal .357 rounds without the possibility of catastrophic failure." - icon_state = "kit" - flags = CONDUCT - w_class = WEIGHT_CLASS_SMALL - origin_tech = "combat=2" - var/open = 0 - -/obj/item/conversion_kit/New() - ..() - update_icon() - -/obj/item/conversion_kit/update_icon() - icon_state = "[initial(icon_state)]_[open]" - -/obj/item/conversion_kit/attack_self(mob/user) - open = !open - to_chat(user, "You [open ? "open" : "close"] the conversion kit.") - update_icon() +#define HEALPERWELD 15 + +/* Tools! + * Note: Multitools are in devices + * + * Contains: + * Wrench + * Screwdriver + * Wirecutters + * Welding Tool + * Crowbar + * Revolver Conversion Kit + */ + +//Wrench +/obj/item/wrench + name = "wrench" + desc = "A wrench with common uses. Can be found in your hand." + icon = 'icons/obj/tools.dmi' + icon_state = "wrench" + flags = CONDUCT + slot_flags = SLOT_BELT + force = 5 + throwforce = 7 + usesound = 'sound/items/ratchet.ogg' + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=150) + origin_tech = "materials=1;engineering=1" + attack_verb = list("bashed", "battered", "bludgeoned", "whacked") + toolspeed = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + +/obj/item/wrench/suicide_act(mob/user) + user.visible_message("[user] is beating [user.p_them()]self to death with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/weapons/genhit.ogg', 50, 1, -1) + return BRUTELOSS + +/obj/item/wrench/cyborg + name = "automatic wrench" + desc = "An advanced robotic wrench. Can be found in construction cyborgs." + toolspeed = 0.5 + +/obj/item/wrench/brass + name = "brass wrench" + desc = "A brass wrench. It's faintly warm to the touch." + icon_state = "wrench_brass" + toolspeed = 0.5 + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/wrench/abductor + name = "alien wrench" + desc = "A polarized wrench. It causes anything placed between the jaws to turn." + icon = 'icons/obj/abductor.dmi' + icon_state = "wrench" + usesound = 'sound/effects/empulse.ogg' + toolspeed = 0.1 + origin_tech = "materials=5;engineering=5;abductor=3" + +/obj/item/wrench/power + name = "hand drill" + desc = "A simple powered drill with a bolt bit." + icon_state = "drill_bolt" + item_state = "drill" + usesound = 'sound/items/drill_use.ogg' + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get + force = 8 //might or might not be too high, subject to change + throwforce = 8 + attack_verb = list("drilled", "screwed", "jabbed") + toolspeed = 0.25 + +/obj/item/wrench/power/attack_self(mob/user) + playsound(get_turf(user),'sound/items/change_drill.ogg', 50, 1) + var/obj/item/wirecutters/power/s_drill = new /obj/item/screwdriver/power + to_chat(user, "You attach the screwdriver bit to [src].") + qdel(src) + user.put_in_active_hand(s_drill) + +/obj/item/wrench/power/suicide_act(mob/user) + user.visible_message("[user] is pressing [src] against [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!") + return BRUTELOSS + +/obj/item/wrench/medical + name = "medical wrench" + desc = "A medical wrench with common (medical?) uses. Can be found in your hand." + icon_state = "wrench_medical" + force = 2 //MEDICAL + throwforce = 4 + origin_tech = "materials=1;engineering=1;biotech=3" + attack_verb = list("wrenched", "medicaled", "tapped", "jabbed", "whacked") + +/obj/item/wrench/medical/suicide_act(mob/user) + user.visible_message("[user] is praying to the medical wrench to take [user.p_their()] soul. It looks like [user.p_theyre()] trying to commit suicide!") + // TODO Make them glow with the power of the M E D I C A L W R E N C H + // during their ascension + + // Stun stops them from wandering off + user.Stun(5) + playsound(loc, 'sound/effects/pray.ogg', 50, 1, -1) + + // Let the sound effect finish playing + sleep(20) + + if(!user) + return + + for(var/obj/item/W in user) + user.unEquip(W) + + var/obj/item/wrench/medical/W = new /obj/item/wrench/medical(loc) + W.add_fingerprint(user) + W.desc += " For some reason, it reminds you of [user.name]." + + if(!user) + return + + user.dust() + return OBLITERATION + +//Screwdriver +/obj/item/screwdriver + name = "screwdriver" + desc = "You can be totally screwy with this." + icon = 'icons/obj/tools.dmi' + icon_state = "screwdriver_map" + flags = CONDUCT + slot_flags = SLOT_BELT + force = 5 + w_class = WEIGHT_CLASS_TINY + throwforce = 5 + throw_speed = 3 + throw_range = 5 + materials = list(MAT_METAL=75) + attack_verb = list("stabbed") + hitsound = 'sound/weapons/bladeslice.ogg' + usesound = 'sound/items/screwdriver.ogg' + toolspeed = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + var/random_color = TRUE //if the screwdriver uses random coloring + +/obj/item/screwdriver/nuke + name = "screwdriver" + desc = "A screwdriver with an ultra thin tip." + icon_state = "screwdriver_nuke" + toolspeed = 0.5 + +/obj/item/screwdriver/suicide_act(mob/user) + user.visible_message("[user] is stabbing [src] into [user.p_their()] [pick("temple", "heart")]! It looks like [user.p_theyre()] trying to commit suicide!") + return BRUTELOSS + +/obj/item/screwdriver/New(loc, var/param_color = null) + ..() + if(random_color) + if(!param_color) + param_color = pick("red","blue","pink","brown","green","cyan","yellow") + icon_state = "screwdriver_[param_color]" + + if (prob(75)) + src.pixel_y = rand(0, 16) + +/obj/item/screwdriver/attack(mob/living/carbon/M, mob/living/carbon/user) + if(!istype(M) || user.a_intent == INTENT_HELP) + return ..() + if(user.zone_selected != "eyes" && user.zone_selected != "head") + return ..() + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, "You don't want to harm [M]!") + return + if((CLUMSY in user.mutations) && prob(50)) + M = user + return eyestab(M,user) + +/obj/item/screwdriver/brass + name = "brass screwdriver" + desc = "A screwdriver made of brass. The handle feels freezing cold." + icon_state = "screwdriver_brass" + toolspeed = 0.5 + random_color = FALSE + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/screwdriver/abductor + name = "alien screwdriver" + desc = "An ultrasonic screwdriver." + icon = 'icons/obj/abductor.dmi' + icon_state = "screwdriver" + usesound = 'sound/items/pshoom.ogg' + toolspeed = 0.1 + random_color = FALSE + +/obj/item/screwdriver/power + name = "hand drill" + desc = "A simple hand drill with a screwdriver bit attached." + icon_state = "drill_screw" + item_state = "drill" + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + origin_tech = "materials=2;engineering=2" //done for balance reasons, making them high value for research, but harder to get + force = 8 //might or might not be too high, subject to change + throwforce = 8 + throw_speed = 2 + throw_range = 3//it's heavier than a screw driver/wrench, so it does more damage, but can't be thrown as far + attack_verb = list("drilled", "screwed", "jabbed","whacked") + hitsound = 'sound/items/drill_hit.ogg' + usesound = 'sound/items/drill_use.ogg' + toolspeed = 0.25 + random_color = FALSE + +/obj/item/screwdriver/power/suicide_act(mob/user) + user.visible_message("[user] is putting [src] to [user.p_their()] temple. It looks like [user.p_theyre()] trying to commit suicide!") + return BRUTELOSS + +/obj/item/screwdriver/power/attack_self(mob/user) + playsound(get_turf(user), 'sound/items/change_drill.ogg', 50, 1) + var/obj/item/wrench/power/b_drill = new /obj/item/wrench/power + to_chat(user, "You attach the bolt driver bit to [src].") + qdel(src) + user.put_in_active_hand(b_drill) + +/obj/item/screwdriver/cyborg + name = "powered screwdriver" + desc = "An electrical screwdriver, designed to be both precise and quick." + usesound = 'sound/items/drill_use.ogg' + toolspeed = 0.5 + +//Wirecutters +/obj/item/wirecutters + name = "wirecutters" + desc = "This cuts wires." + icon = 'icons/obj/tools.dmi' + icon_state = "cutters" + flags = CONDUCT + slot_flags = SLOT_BELT + force = 6 + throw_speed = 3 + throw_range = 7 + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=80) + origin_tech = "materials=1;engineering=1" + attack_verb = list("pinched", "nipped") + hitsound = 'sound/items/wirecutter.ogg' + usesound = 'sound/items/wirecutter.ogg' + sharp = 1 + toolspeed = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + var/random_color = TRUE + +/obj/item/wirecutters/New(loc, param_color = null) + ..() + if(random_color) + if(!param_color) + param_color = pick("yellow", "red") + icon_state = "cutters_[param_color]" + +/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user) + if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable)) + user.visible_message("[user] cuts [C]'s restraints with [src]!") + QDEL_NULL(C.handcuffed) + if(C.buckled && C.buckled.buckle_requires_restraints) + C.buckled.unbuckle_mob(C) + C.update_handcuffed() + return + else + ..() + +/obj/item/wirecutters/suicide_act(mob/user) + user.visible_message("[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, usesound, 50, 1, -1) + return BRUTELOSS + +/obj/item/wirecutters/brass + name = "brass wirecutters" + desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch." + icon_state = "cutters_brass" + toolspeed = 0.5 + random_color = FALSE + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/wirecutters/abductor + name = "alien wirecutters" + desc = "Extremely sharp wirecutters, made out of a silvery-green metal." + icon = 'icons/obj/abductor.dmi' + icon_state = "cutters" + toolspeed = 0.1 + origin_tech = "materials=5;engineering=4;abductor=3" + random_color = FALSE + +/obj/item/wirecutters/cyborg + name = "wirecutters" + desc = "This cuts wires." + toolspeed = 0.5 + +/obj/item/wirecutters/power + name = "jaws of life" + desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a cutting head." + icon_state = "jaws_cutter" + item_state = "jawsoflife" + origin_tech = "materials=2;engineering=2" + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + usesound = 'sound/items/jaws_cut.ogg' + toolspeed = 0.25 + random_color = FALSE + +/obj/item/wirecutters/power/suicide_act(mob/user) + user.visible_message("[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!") + playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/organ/external/head/head = H.bodyparts_by_name["head"] + if(head) + head.droplimb(0, DROPLIMB_BLUNT, FALSE, TRUE) + playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1) + return BRUTELOSS + +/obj/item/wirecutters/power/attack_self(mob/user) + playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) + var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power + to_chat(user, "You attach the pry jaws to [src].") + qdel(src) + user.put_in_active_hand(pryjaws) + +//Welding Tool +/obj/item/weldingtool + name = "welding tool" + desc = "A standard edition welder provided by Nanotrasen." + icon = 'icons/obj/tools.dmi' + icon_state = "welder" + item_state = "welder" + flags = CONDUCT + slot_flags = SLOT_BELT + force = 3 + throwforce = 5 + throw_speed = 3 + throw_range = 5 + hitsound = "swing_hit" + usesound = 'sound/items/welder.ogg' + var/acti_sound = 'sound/items/welderactivate.ogg' + var/deac_sound = 'sound/items/welderdeactivate.ogg' + w_class = WEIGHT_CLASS_SMALL + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) + resistance_flags = FIRE_PROOF + materials = list(MAT_METAL=70, MAT_GLASS=30) + origin_tech = "engineering=1;plasmatech=1" + toolspeed = 1 + var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2) + var/status = 1 //Whether the welder is secured or unsecured (able to attach rods to it to make a flamethrower) + var/max_fuel = 20 //The max amount of fuel the welder can hold + var/change_icons = 1 + var/can_off_process = 0 + var/light_intensity = 2 //how powerful the emitted light is when used. + var/nextrefueltick = 0 + +/obj/item/weldingtool/New() + ..() + create_reagents(max_fuel) + reagents.add_reagent("fuel", max_fuel) + update_icon() + +/obj/item/weldingtool/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 0) + . += "It contains [get_fuel()] unit\s of fuel out of [max_fuel]." + +/obj/item/weldingtool/suicide_act(mob/user) + user.visible_message("[user] welds [user.p_their()] every orifice closed! It looks like [user.p_theyre()] trying to commit suicide!") + return FIRELOSS + +/obj/item/weldingtool/proc/update_torch() + overlays.Cut() + if(welding) + overlays += "[initial(icon_state)]-on" + item_state = "[initial(item_state)]1" + else + item_state = "[initial(item_state)]" + +/obj/item/weldingtool/update_icon() + if(change_icons) + var/ratio = get_fuel() / max_fuel + ratio = Ceiling(ratio*4) * 25 + if(ratio == 100) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)][ratio]" + update_torch() + ..() + +/obj/item/weldingtool/process() + switch(welding) + if(0) + force = 3 + damtype = "brute" + update_icon() + if(!can_off_process) + STOP_PROCESSING(SSobj, src) + return + //Welders left on now use up fuel, but lets not have them run out quite that fast + if(1) + force = 15 + damtype = "fire" + if(prob(5)) + remove_fuel(1) + update_icon() + + //This is to start fires. process() is only called if the welder is on. + var/turf/location = loc + if(ismob(location)) + var/mob/M = location + if(M.l_hand == src || M.r_hand == src) + location = get_turf(M) + if(isturf(location)) + location.hotspot_expose(700, 5) + +/obj/item/weldingtool/attackby(obj/item/I, mob/user, params) + if(isscrewdriver(I)) + flamethrower_screwdriver(I, user) + else if(istype(I, /obj/item/stack/rods)) + flamethrower_rods(I, user) + else + ..() + +/obj/item/weldingtool/attack(mob/M, mob/user) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected] + + if(!S) + return + + if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2) + return ..() + + if(!isOn()) //why wasn't this being checked already? + to_chat(user, "Turn on [src] before attempting repairs!") + return 1 + + if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP) + to_chat(user, "The damage is far too severe to patch over externally.") + return + + if(!S.brute_dam) + to_chat(user, "Nothing to fix!") + return + + if(get_fuel() >= 1) + if(H == user) + if(!do_mob(user, H, 10)) + return 1 + if(!remove_fuel(1,null)) + to_chat(user, "Need more welding fuel!") + var/rembrute = HEALPERWELD + var/nrembrute = 0 + var/childlist + if(!isnull(S.children)) + childlist = S.children.Copy() + var/parenthealed = FALSE + while(rembrute > 0) + var/obj/item/organ/external/E + if(S.brute_dam) + E = S + else if(LAZYLEN(childlist)) + E = pick_n_take(childlist) + if(!E.brute_dam || !E.is_robotic()) + continue + else if(S.parent && !parenthealed) + E = S.parent + parenthealed = TRUE + if(!E.brute_dam || !E.is_robotic()) + break + else + break + playsound(src.loc, usesound, 50, 1) + nrembrute = max(rembrute - E.brute_dam, 0) + E.heal_damage(rembrute,0,0,1) + rembrute = nrembrute + user.visible_message("\The [user] patches some dents on \the [M]'s [E.name] with \the [src].") + if(H.bleed_rate && H.isSynthetic()) + H.bleed_rate = 0 + user.visible_message("\The [user] patches some leaks on [M] with \the [src].") + return 1 + else + return ..() + +/obj/item/weldingtool/afterattack(atom/O, mob/user, proximity) + if(!proximity) + return + if(welding) + remove_fuel(1) + var/turf/location = get_turf(user) + location.hotspot_expose(700, 50, 1) + if(get_fuel() <= 0) + set_light(0) + + if(isliving(O)) + var/mob/living/L = O + if(L.IgniteMob()) + message_admins("[key_name_admin(user)] set [key_name_admin(L)] on fire") + log_game("[key_name(user)] set [key_name(L)] on fire") + +/obj/item/weldingtool/attack_self(mob/user) + switched_on(user) + if(welding) + set_light(light_intensity) + + update_icon() + +//Returns the amount of fuel in the welder +/obj/item/weldingtool/proc/get_fuel() + return reagents.get_reagent_amount("fuel") + +//Removes fuel from the welding tool. If a mob is passed, it will try to flash the mob's eyes. This should probably be renamed to use() +/obj/item/weldingtool/proc/remove_fuel(amount = 1, mob/living/M = null) + if(!welding || !check_fuel()) + return FALSE + if(get_fuel() >= amount) + reagents.remove_reagent("fuel", amount) + check_fuel() + if(M) + M.flash_eyes(light_intensity) + return TRUE + else + if(M) + to_chat(M, "You need more welding fuel to complete this task.") + return FALSE + +//Returns whether or not the welding tool is currently on. +/obj/item/weldingtool/proc/isOn() + return welding + +//Turns off the welder if there is no more fuel (does this really need to be its own proc?) +/obj/item/weldingtool/proc/check_fuel(mob/user) + if(get_fuel() <= 0 && welding) + switched_on(user) + update_icon() + //mob icon update + if(ismob(loc)) + var/mob/M = loc + M.update_inv_r_hand(0) + M.update_inv_l_hand(0) + return 0 + return 1 + +//Switches the welder on +/obj/item/weldingtool/proc/switched_on(mob/user) + if(!status) + to_chat(user, "[src] can't be turned on while unsecured!") + return + welding = !welding + if(welding) + if(get_fuel() >= 1) + to_chat(user, "You switch [src] on.") + playsound(loc, acti_sound, 50, 1) + force = 15 + damtype = "fire" + hitsound = 'sound/items/welder.ogg' + update_icon() + START_PROCESSING(SSobj, src) + else + to_chat(user, "You need more fuel!") + switched_off(user) + else + if(user) + to_chat(user, "You switch [src] off.") + playsound(loc, deac_sound, 50, 1) + switched_off(user) + +//Switches the welder off +/obj/item/weldingtool/proc/switched_off(mob/user) + welding = 0 + set_light(0) + + force = 3 + damtype = "brute" + hitsound = "swing_hit" + update_icon() + +/obj/item/weldingtool/proc/flamethrower_screwdriver(obj/item/I, mob/user) + if(welding) + to_chat(user, "Turn it off first!") + return + status = !status + if(status) + to_chat(user, "You resecure [src].") + else + to_chat(user, "[src] can now be attached and modified.") + add_fingerprint(user) + +/obj/item/weldingtool/proc/flamethrower_rods(obj/item/I, mob/user) + if(!status) + var/obj/item/stack/rods/R = I + if(R.use(1)) + var/obj/item/flamethrower/F = new /obj/item/flamethrower(user.loc) + if(!remove_item_from_storage(F)) + user.unEquip(src) + loc = F + F.weldtool = src + add_fingerprint(user) + to_chat(user, "You add a rod to a welder, starting to build a flamethrower.") + user.put_in_hands(F) + else + to_chat(user, "You need one rod to start building a flamethrower!") + +/obj/item/weldingtool/largetank + name = "Industrial Welding Tool" + desc = "A slightly larger welder with a larger tank." + icon_state = "indwelder" + max_fuel = 40 + materials = list(MAT_METAL=70, MAT_GLASS=60) + origin_tech = "engineering=2;plasmatech=2" + +/obj/item/weldingtool/largetank/cyborg + name = "integrated welding tool" + desc = "An advanced welder designed to be used in robotic systems." + toolspeed = 0.5 + +/obj/item/weldingtool/largetank/flamethrower_screwdriver() + return + +/obj/item/weldingtool/mini + name = "emergency welding tool" + desc = "A miniature welder used during emergencies." + icon_state = "miniwelder" + max_fuel = 10 + w_class = WEIGHT_CLASS_TINY + materials = list(MAT_METAL=30, MAT_GLASS=10) + change_icons = 0 + +/obj/item/weldingtool/mini/flamethrower_screwdriver() + return + +/obj/item/weldingtool/abductor + name = "alien welding tool" + desc = "An alien welding tool. Whatever fuel it uses, it never runs out." + icon = 'icons/obj/abductor.dmi' + icon_state = "welder" + toolspeed = 0.1 + light_intensity = 0 + change_icons = 0 + origin_tech = "plasmatech=5;engineering=5;abductor=3" + can_off_process = 1 + +/obj/item/weldingtool/abductor/process() + if(get_fuel() <= max_fuel) + reagents.add_reagent("fuel", 1) + ..() + +/obj/item/weldingtool/hugetank + name = "Upgraded Welding Tool" + desc = "An upgraded welder based off the industrial welder." + icon_state = "upindwelder" + item_state = "upindwelder" + max_fuel = 80 + materials = list(MAT_METAL=70, MAT_GLASS=120) + origin_tech = "engineering=3;plasmatech=2" + +/obj/item/weldingtool/experimental + name = "Experimental Welding Tool" + desc = "An experimental welder capable of self-fuel generation and less harmful to the eyes." + icon_state = "exwelder" + item_state = "exwelder" + max_fuel = 40 + materials = list(MAT_METAL=70, MAT_GLASS=120) + origin_tech = "materials=4;engineering=4;bluespace=3;plasmatech=4" + change_icons = 0 + can_off_process = 1 + light_intensity = 1 + toolspeed = 0.5 + var/last_gen = 0 + +/obj/item/weldingtool/experimental/brass + name = "brass welding tool" + desc = "A brass welder that seems to constantly refuel itself. It is faintly warm to the touch." + icon_state = "brasswelder" + item_state = "brasswelder" + resistance_flags = FIRE_PROOF | ACID_PROOF + +obj/item/weldingtool/experimental/process() + ..() + if(get_fuel() < max_fuel && nextrefueltick < world.time) + nextrefueltick = world.time + 10 + reagents.add_reagent("fuel", 1) + +//Crowbar +/obj/item/crowbar + name = "pocket crowbar" + desc = "A small crowbar. This handy tool is useful for lots of things, such as prying floor tiles or opening unpowered doors." + icon = 'icons/obj/tools.dmi' + icon_state = "crowbar" + item_state = "crowbar" + usesound = 'sound/items/crowbar.ogg' + flags = CONDUCT + slot_flags = SLOT_BELT + force = 5 + throwforce = 7 + item_state = "crowbar" + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50) + origin_tech = "engineering=1;combat=1" + attack_verb = list("attacked", "bashed", "battered", "bludgeoned", "whacked") + toolspeed = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + +/obj/item/crowbar/red + icon_state = "crowbar_red" + item_state = "crowbar_red" + force = 8 + +/obj/item/crowbar/brass + name = "brass crowbar" + desc = "A brass crowbar. It feels faintly warm to the touch." + icon_state = "crowbar_brass" + item_state = "crowbar_brass" + toolspeed = 0.5 + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/crowbar/abductor + name = "alien crowbar" + desc = "A hard-light crowbar. It appears to pry by itself, without any effort required." + icon = 'icons/obj/abductor.dmi' + usesound = 'sound/weapons/sonic_jackhammer.ogg' + icon_state = "crowbar" + toolspeed = 0.1 + origin_tech = "combat=4;engineering=4;abductor=3" + +/obj/item/crowbar/large + name = "crowbar" + desc = "It's a big crowbar. It doesn't fit in your pockets, because its too big." + force = 12 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 3 + throw_range = 3 + materials = list(MAT_METAL=70) + icon_state = "crowbar_large" + item_state = "crowbar_large" + toolspeed = 0.5 + +/obj/item/crowbar/cyborg + name = "hydraulic crowbar" + desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbar in construction cyborgs." + usesound = 'sound/items/jaws_pry.ogg' + force = 10 + toolspeed = 0.5 + +/obj/item/crowbar/power + name = "jaws of life" + desc = "A set of jaws of life, the magic of science has managed to fit it down into a device small enough to fit in a tool belt. It's fitted with a prying head." + icon_state = "jaws_pry" + item_state = "jawsoflife" + materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25) + origin_tech = "materials=2;engineering=2" + usesound = 'sound/items/jaws_pry.ogg' + force = 15 + toolspeed = 0.25 + var/airlock_open_time = 100 // Time required to open powered airlocks + +/obj/item/crowbar/power/suicide_act(mob/user) + user.visible_message("[user] is putting [user.p_their()] head in [src]. It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/items/jaws_pry.ogg', 50, 1, -1) + return BRUTELOSS + +/obj/item/crowbar/power/attack_self(mob/user) + playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1) + var/obj/item/wirecutters/power/cutjaws = new /obj/item/wirecutters/power + to_chat(user, "You attach the cutting jaws to [src].") + qdel(src) + user.put_in_active_hand(cutjaws) + +// Conversion kit +/obj/item/conversion_kit + name = "\improper Revolver Conversion Kit" + desc = "A professional conversion kit used to convert any knock off revolver into the real deal capable of shooting lethal .357 rounds without the possibility of catastrophic failure." + icon_state = "kit" + flags = CONDUCT + w_class = WEIGHT_CLASS_SMALL + origin_tech = "combat=2" + var/open = 0 + +/obj/item/conversion_kit/New() + ..() + update_icon() + +/obj/item/conversion_kit/update_icon() + icon_state = "[initial(icon_state)]_[open]" + +/obj/item/conversion_kit/attack_self(mob/user) + open = !open + to_chat(user, "You [open ? "open" : "close"] the conversion kit.") + update_icon() diff --git a/code/game/objects/items/weapons/twohanded.dm b/code/game/objects/items/weapons/twohanded.dm index fba969f05b73..57c9a5301f61 100644 --- a/code/game/objects/items/weapons/twohanded.dm +++ b/code/game/objects/items/weapons/twohanded.dm @@ -1,878 +1,878 @@ -/* Two-handed Weapons - * Contains: - * Twohanded - * Fireaxe - * Double-Bladed Energy Swords - * Spears - * Kidan spear - * Chainsaw - * Singularity hammer - * Mjolnnir - * Knighthammer - */ - -/*################################################################## -##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## -####################################################################*/ - -//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn -//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. -//It also tidies stuff up elsewhere. - -/* - * Twohanded - */ -/obj/item/twohanded - var/wielded = FALSE - var/force_unwielded = 0 - var/force_wielded = 0 - var/wieldsound = null - var/unwieldsound = null - var/sharp_when_wielded = FALSE - -/obj/item/twohanded/proc/unwield(mob/living/carbon/user) - if(!wielded || !user) - return - wielded = FALSE - force = force_unwielded - if(sharp_when_wielded) - sharp = FALSE - var/sf = findtext(name," (Wielded)") - if(sf) - name = copytext(name, 1, sf) - else //something wrong - name = "[initial(name)]" - update_icon() - if(user) - user.update_inv_r_hand() - user.update_inv_l_hand() - if(isrobot(user)) - to_chat(user, "You free up your module.") - else - to_chat(user, "You are now carrying [name] with one hand.") - if(unwieldsound) - playsound(loc, unwieldsound, 50, 1) - var/obj/item/twohanded/offhand/O = user.get_inactive_hand() - if(O && istype(O)) - O.unwield() - -/obj/item/twohanded/proc/wield(mob/living/carbon/user) - if(wielded) - return - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna.species.is_small) - to_chat(user, "It's too heavy for you to wield fully.") - return - if(user.get_inactive_hand()) - to_chat(user, "You need your other hand to be empty!") - return - wielded = TRUE - force = force_wielded - if(sharp_when_wielded) - sharp = TRUE - name = "[name] (Wielded)" - update_icon() - if(user) - user.update_inv_r_hand() - user.update_inv_l_hand() - if(isrobot(user)) - to_chat(user, "You dedicate your module to [name].") - else - to_chat(user, "You grab the [name] with both hands.") - if(wieldsound) - playsound(loc, wieldsound, 50, 1) - var/obj/item/twohanded/offhand/O = new(user) ////Let's reserve his other hand~ - O.name = "[name] - offhand" - O.desc = "Your second grip on the [name]" - user.put_in_inactive_hand(O) - -/obj/item/twohanded/dropped(mob/user) - ..() - //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand - if(user) - var/obj/item/twohanded/O = user.get_inactive_hand() - if(istype(O)) - O.unwield(user) - return unwield(user) - -/obj/item/twohanded/attack_self(mob/user) - ..() - if(wielded) //Trying to unwield it - unwield(user) - else //Trying to wield it - wield(user) - - -/obj/item/twohanded/equip_to_best_slot(mob/M) - if(..()) - unwield(M) - return - -///////////OFFHAND/////////////// -/obj/item/twohanded/offhand - w_class = WEIGHT_CLASS_HUGE - icon_state = "offhand" - name = "offhand" - flags = ABSTRACT - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/item/twohanded/offhand/unwield() - if(!QDELETED(src)) - qdel(src) - -/obj/item/twohanded/offhand/wield() - if(!QDELETED(src)) - qdel(src) - -///////////Two hand required objects/////////////// -//This is for objects that require two hands to even pick up -/obj/item/twohanded/required - w_class = WEIGHT_CLASS_HUGE - -/obj/item/twohanded/required/attack_self() - return - -/obj/item/twohanded/required/mob_can_equip(mob/M, slot) - if(wielded && !slot_flags) - to_chat(M, "[src] is too cumbersome to carry with anything but your hands!") - return FALSE - return ..() - -/obj/item/twohanded/required/attack_hand(mob/user)//Can't even pick it up without both hands empty - var/obj/item/twohanded/required/H = user.get_inactive_hand() - if(get_dist(src, user) > 1) - return FALSE - if(H != null) - to_chat(user, "[src] is too cumbersome to carry in one hand!") - return - if(loc != user) - wield(user) - ..() - -/obj/item/twohanded/required/on_give(mob/living/carbon/giver, mob/living/carbon/receiver) - var/obj/item/twohanded/required/H = receiver.get_inactive_hand() - if(H != null) //Check if he can wield it - receiver.drop_item() //Can't wear it so drop it - to_chat(receiver, "[src] is too cumbersome to carry in one hand!") - return - equipped(receiver,receiver.hand ? slot_l_hand : slot_r_hand) - -/obj/item/twohanded/required/equipped(mob/user, slot) - ..() - if(slot == slot_l_hand || slot == slot_r_hand) - wield(user) - else - unwield(user) - -/* - * Fireaxe - */ -/obj/item/twohanded/fireaxe // DEM AXES MAN, marker -Agouri - icon_state = "fireaxe0" - name = "fire axe" - desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" - force = 5 - throwforce = 15 - sharp = TRUE - w_class = WEIGHT_CLASS_BULKY - slot_flags = SLOT_BACK - force_unwielded = 5 - force_wielded = 24 - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - usesound = 'sound/items/crowbar.ogg' - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) - resistance_flags = FIRE_PROOF - -/obj/item/twohanded/fireaxe/update_icon() //Currently only here to fuck with the on-mob icons. - icon_state = "fireaxe[wielded]" - ..() - -/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return - if(wielded) //destroys windows and grilles in one hit - if(istype(A, /obj/structure/window) || istype(A, /obj/structure/grille)) - var/obj/structure/W = A - W.obj_destruction("fireaxe") - - -/obj/item/twohanded/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone. - icon_state = "bone_axe0" - name = "bone axe" - desc = "A large, vicious axe crafted out of several sharpened bone plates and crudely tied together. Made of monsters, by killing monsters, for killing monsters." - force_wielded = 23 - -/obj/item/twohanded/fireaxe/boneaxe/update_icon() - icon_state = "bone_axe[wielded]" - -/* - * Double-Bladed Energy Swords - Cheridan - */ -/obj/item/twohanded/dualsaber - var/hacked = FALSE - var/blade_color - icon_state = "dualsaber0" - name = "double-bladed energy sword" - desc = "Handle with care." - force = 3 - throwforce = 5 - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/w_class_on = WEIGHT_CLASS_BULKY - force_unwielded = 3 - force_wielded = 34 - wieldsound = 'sound/weapons/saberon.ogg' - unwieldsound = 'sound/weapons/saberoff.ogg' - armour_penetration = 35 - origin_tech = "magnets=4;syndicate=5" - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 75 - sharp_when_wielded = TRUE // only sharp when wielded - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) - resistance_flags = FIRE_PROOF - light_power = 2 - var/brightness_on = 2 - var/colormap = list(red=LIGHT_COLOR_RED, blue=LIGHT_COLOR_LIGHTBLUE, green=LIGHT_COLOR_GREEN, purple=LIGHT_COLOR_PURPLE, rainbow=LIGHT_COLOR_WHITE) - -/obj/item/twohanded/dualsaber/New() - ..() - if(!blade_color) - blade_color = pick("red", "blue", "green", "purple") - -/obj/item/twohanded/dualsaber/update_icon() - if(wielded) - icon_state = "dualsaber[blade_color][wielded]" - set_light(brightness_on, l_color=colormap[blade_color]) - else - icon_state = "dualsaber0" - set_light(0) - ..() - -/obj/item/twohanded/dualsaber/attack(mob/target, mob/living/user) - if(HULK in user.mutations) - to_chat(user, "You grip the blade too hard and accidentally close it!") - unwield() - return - ..() - if((CLUMSY in user.mutations) && (wielded) && prob(40)) - to_chat(user, "You twirl around a bit before losing your balance and impaling yourself on the [src].") - user.take_organ_damage(20, 25) - return - if((wielded) && prob(50)) - INVOKE_ASYNC(src, .proc/jedi_spin, user) - -/obj/item/twohanded/dualsaber/proc/jedi_spin(mob/living/user) - for(var/i in list(NORTH, SOUTH, EAST, WEST, EAST, SOUTH, NORTH, SOUTH, EAST, WEST, EAST, SOUTH)) - user.setDir(i) - if(i == WEST) - user.SpinAnimation(7, 1) - sleep(1) - -/obj/item/twohanded/dualsaber/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(wielded) - return ..() - return FALSE - -/obj/item/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up - if(wielded) - to_chat(user, "You can't pick up such a dangerous item with your meaty hands without losing fingers, better not to!") - return TRUE - -/obj/item/twohanded/dualsaber/green - blade_color = "green" - -/obj/item/twohanded/dualsaber/red - blade_color = "red" - -/obj/item/twohanded/dualsaber/purple - blade_color = "purple" - -/obj/item/twohanded/dualsaber/blue - blade_color = "blue" - -/obj/item/twohanded/dualsaber/unwield() - ..() - hitsound = "swing_hit" - w_class = initial(w_class) - -/obj/item/twohanded/dualsaber/IsReflect() - if(wielded) - return TRUE - -/obj/item/twohanded/dualsaber/wield(mob/living/carbon/M) //Specific wield () hulk checks due to reflection chance for balance issues and switches hitsounds. - if(HULK in M.mutations) - to_chat(M, "You lack the grace to wield this!") - return - ..() - hitsound = 'sound/weapons/blade1.ogg' - w_class = w_class_on - -/obj/item/twohanded/dualsaber/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!hacked) - hacked = TRUE - to_chat(user, "2XRNBW_ENGAGE") - blade_color = "rainbow" - update_icon() - else - to_chat(user, "It's starting to look like a triple rainbow - no, nevermind.") - -//spears -/obj/item/twohanded/spear - icon_state = "spearglass0" - name = "spear" - desc = "A haphazardly-constructed yet still deadly weapon of ancient design." - force = 10 - w_class = WEIGHT_CLASS_BULKY - slot_flags = SLOT_BACK - force_unwielded = 10 - force_wielded = 18 - throwforce = 20 - throw_speed = 4 - armour_penetration = 10 - materials = list(MAT_METAL = 1150, MAT_GLASS = 2075) - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") - sharp = TRUE - no_spin_thrown = TRUE - var/obj/item/grenade/explosive = null - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) - var/icon_prefix = "spearglass" - -/obj/item/twohanded/spear/update_icon() - icon_state = "[icon_prefix][wielded]" - -/obj/item/twohanded/spear/CheckParts(list/parts_list) - var/obj/item/shard/tip = locate() in parts_list - if(istype(tip, /obj/item/shard/plasma)) - force_wielded = 19 - force_unwielded = 11 - throwforce = 21 - icon_prefix = "spearplasma" - update_icon() - qdel(tip) - ..() - - -/obj/item/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity) - if(!proximity) - return - if(isturf(AM)) //So you can actually melee with it - return - if(explosive && wielded) - explosive.forceMove(AM) - explosive.prime() - qdel(src) - -/obj/item/twohanded/spear/throw_impact(atom/target) - . = ..() - if(explosive) - explosive.prime() - qdel(src) - -/obj/item/twohanded/spear/bonespear //Blatant imitation of spear, but made out of bone. Not valid for explosive modification. - icon_state = "bone_spear0" - name = "bone spear" - desc = "A haphazardly-constructed yet still deadly weapon. The pinnacle of modern technology." - force = 11 - force_unwielded = 11 - force_wielded = 20 //I have no idea how to balance - throwforce = 22 - armour_penetration = 15 //Enhanced armor piercing - icon_prefix = "bone_spear" - -//GREY TIDE -/obj/item/twohanded/spear/grey_tide - icon_state = "spearglass0" - name = "\improper Grey Tide" - desc = "Recovered from the aftermath of a revolt aboard Defense Outpost Theta Aegis, in which a seemingly endless tide of Assistants caused heavy casualities among Nanotrasen military forces." - force_unwielded = 15 - force_wielded = 25 - throwforce = 20 - throw_speed = 4 - attack_verb = list("gored") - -/obj/item/twohanded/spear/grey_tide/afterattack(atom/movable/AM, mob/living/user, proximity) - ..() - if(!proximity) - return - user.faction |= "greytide(\ref[user])" - if(isliving(AM)) - var/mob/living/L = AM - if(istype (L, /mob/living/simple_animal/hostile/illusion)) - return - if(!L.stat && prob(50)) - var/mob/living/simple_animal/hostile/illusion/M = new(user.loc) - M.faction = user.faction.Copy() - M.attack_sound = hitsound - M.Copy_Parent(user, 100, user.health/2.5, 12, 30) - M.GiveTarget(L) - -//Putting heads on spears -/obj/item/twohanded/spear/attackby(obj/item/I, mob/living/user) - if(istype(I, /obj/item/organ/external/head)) - if(user.unEquip(src) && user.drop_item()) - to_chat(user, "You stick [I] onto the spear and stand it upright on the ground.") - var/obj/structure/headspear/HS = new /obj/structure/headspear(get_turf(src)) - var/matrix/M = matrix() - I.transform = M - var/image/IM = image(I.icon, I.icon_state) - IM.overlays = I.overlays.Copy() - HS.overlays += IM - I.forceMove(HS) - HS.mounted_head = I - forceMove(HS) - HS.contained_spear = src - else - return ..() - -/obj/structure/headspear - name = "head on a spear" - desc = "How barbaric." - icon_state = "headspear" - density = FALSE - anchored = TRUE - var/obj/item/organ/external/head/mounted_head = null - var/obj/item/twohanded/spear/contained_spear = null - -/obj/structure/headspear/Destroy() - QDEL_NULL(mounted_head) - QDEL_NULL(contained_spear) - return ..() - -/obj/structure/headspear/attack_hand(mob/living/user) - user.visible_message("[user] kicks over [src]!", "You kick down [src]!") - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - var/turf/T = get_turf(src) - if(contained_spear) - contained_spear.forceMove(T) - contained_spear = null - if(mounted_head) - mounted_head.forceMove(T) - mounted_head = null - qdel(src) - -/obj/item/twohanded/spear/kidan - icon_state = "kidanspear0" - name = "Kidan spear" - desc = "A spear brought over from the Kidan homeworld." - -// DIY CHAINSAW -/obj/item/twohanded/required/chainsaw - name = "chainsaw" - desc = "A versatile power tool. Useful for limbing trees and delimbing humans." - icon_state = "gchainsaw_off" - flags = CONDUCT - force = 13 - var/force_on = 24 - w_class = WEIGHT_CLASS_HUGE - throwforce = 13 - throw_speed = 2 - throw_range = 4 - materials = list(MAT_METAL = 13000) - origin_tech = "materials=3;engineering=4;combat=2" - attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered") - hitsound = "swing_hit" - sharp = TRUE - actions_types = list(/datum/action/item_action/startchainsaw) - var/on = FALSE - -/obj/item/twohanded/required/chainsaw/attack_self(mob/user) - on = !on - to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr." : "the chain stops moving."]") - if(on) - playsound(loc, 'sound/weapons/chainsawstart.ogg', 50, 1) - force = on ? force_on : initial(force) - throwforce = on ? force_on : initial(throwforce) - icon_state = "gchainsaw_[on ? "on" : "off"]" - - if(hitsound == "swing_hit") - hitsound = 'sound/weapons/chainsaw.ogg' - else - hitsound = "swing_hit" - - if(src == user.get_active_hand()) //update inhands - user.update_inv_l_hand() - user.update_inv_r_hand() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/twohanded/required/chainsaw/attack_hand(mob/user) - . = ..() - force = on ? force_on : initial(force) - throwforce = on ? force_on : initial(throwforce) - -/obj/item/twohanded/required/chainsaw/on_give(mob/living/carbon/giver, mob/living/carbon/receiver) - . = ..() - force = on ? force_on : initial(force) - throwforce = on ? force_on : initial(throwforce) - -/obj/item/twohanded/required/chainsaw/doomslayer - name = "OOOH BABY" - desc = "VRRRRRRR!!!" - armour_penetration = 100 - force_on = 30 - -/obj/item/twohanded/required/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(attack_type == PROJECTILE_ATTACK) - owner.visible_message("Ranged attacks just make [owner] angrier!") - playsound(src, pick('sound/weapons/bulletflyby.ogg','sound/weapons/bulletflyby2.ogg','sound/weapons/bulletflyby3.ogg'), 75, 1) - return TRUE - return FALSE - - -///CHAINSAW/// -/obj/item/twohanded/chainsaw - icon_state = "chainsaw0" - name = "Chainsaw" - desc = "Perfect for felling trees or fellow spacemen." - force = 15 - throwforce = 15 - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_BULKY // can't fit in backpacks - force_unwielded = 15 //still pretty robust - force_wielded = 40 //you'll gouge their eye out! Or a limb...maybe even their entire body! - wieldsound = 'sound/weapons/chainsawstart.ogg' - hitsound = null - armour_penetration = 35 - origin_tech = "materials=6;syndicate=4" - attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered") - sharp = TRUE - -/obj/item/twohanded/chainsaw/update_icon() - if(wielded) - icon_state = "chainsaw[wielded]" - else - icon_state = "chainsaw0" - ..() - -/obj/item/twohanded/chainsaw/attack(mob/target, mob/living/user) - if(wielded) - playsound(loc, 'sound/weapons/chainsaw.ogg', 100, 1, -1) //incredibly loud; you ain't goin' for stealth with this thing. Credit to Lonemonk of Freesound for this sound. - if(isrobot(target)) - ..() - return - if(!isliving(target)) - return - else - target.Weaken(4) - ..() - return - else - playsound(loc, "swing_hit", 50, 1, -1) - return ..() - -/obj/item/twohanded/chainsaw/wield() //you can't disarm an active chainsaw, you crazy person. - ..() - flags |= NODROP - -/obj/item/twohanded/chainsaw/unwield() - ..() - flags &= ~NODROP - -// SINGULOHAMMER -/obj/item/twohanded/singularityhammer - name = "singularity hammer" - desc = "The pinnacle of close combat technology, the hammer harnesses the power of a miniaturized singularity to deal crushing blows." - icon_state = "mjollnir0" - flags = CONDUCT - slot_flags = SLOT_BACK - force = 5 - force_unwielded = 5 - force_wielded = 20 - throwforce = 15 - throw_range = 1 - w_class = WEIGHT_CLASS_HUGE - armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - var/charged = 5 - origin_tech = "combat=4;bluespace=4;plasmatech=7" - -/obj/item/twohanded/singularityhammer/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/twohanded/singularityhammer/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/twohanded/singularityhammer/process() - if(charged < 5) - charged++ - -/obj/item/twohanded/singularityhammer/update_icon() //Currently only here to fuck with the on-mob icons. - icon_state = "mjollnir[wielded]" - ..() - -/obj/item/twohanded/singularityhammer/proc/vortex(turf/pull, mob/wielder) - for(var/atom/movable/X in orange(5, pull)) - if(X == wielder) - continue - if((X) && (!X.anchored) && (!ishuman(X))) - step_towards(X, pull) - step_towards(X, pull) - step_towards(X, pull) - else if(ishuman(X)) - var/mob/living/carbon/human/H = X - if(istype(H.shoes, /obj/item/clothing/shoes/magboots)) - var/obj/item/clothing/shoes/magboots/M = H.shoes - if(M.magpulse) - continue - H.apply_effect(1, WEAKEN, 0) - step_towards(H, pull) - step_towards(H, pull) - step_towards(H, pull) - -/obj/item/twohanded/singularityhammer/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return - if(wielded) - if(charged == 5) - charged = 0 - if(isliving(A)) - var/mob/living/Z = A - Z.take_organ_damage(20, 0) - playsound(user, 'sound/weapons/marauder.ogg', 50, 1) - var/turf/target = get_turf(A) - vortex(target, user) - -/obj/item/twohanded/mjollnir - name = "Mjolnir" - desc = "A weapon worthy of a god, able to strike with the force of a lightning bolt. It crackles with barely contained energy." - icon_state = "mjollnir0" - flags = CONDUCT - slot_flags = SLOT_BACK - force = 5 - force_unwielded = 5 - force_wielded = 25 - throwforce = 30 - throw_range = 7 - w_class = WEIGHT_CLASS_HUGE - //var/charged = 5 - origin_tech = "combat=4;powerstorage=7" - -/obj/item/twohanded/mjollnir/proc/shock(mob/living/target) - do_sparks(5, 1, target.loc) - target.visible_message("[target.name] was shocked by the [name]!", \ - "You feel a powerful shock course through your body sending you flying!", \ - "You hear a heavy electrical crack!") - var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src))) - target.throw_at(throw_target, 200, 4) - -/obj/item/twohanded/mjollnir/attack(mob/M, mob/user) - ..() - if(wielded) - //if(charged == 5) - //charged = 0 - playsound(loc, "sparks", 50, 1) - if(isliving(M)) - M.Stun(3) - shock(M) - -/obj/item/twohanded/mjollnir/throw_impact(atom/target) - . = ..() - if(isliving(target)) - var/mob/living/L = target - L.Stun(3) - shock(L) - -/obj/item/twohanded/mjollnir/update_icon() //Currently only here to fuck with the on-mob icons. - icon_state = "mjollnir[wielded]" - ..() - -/obj/item/twohanded/knighthammer - name = "singuloth knight's hammer" - desc = "A hammer made of sturdy metal with a golden skull adorned with wings on either side of the head.
    This weapon causes devastating damage to those it hits due to a power field sustained by a mini-singularity inside of the hammer." - icon_state = "knighthammer0" - flags = CONDUCT - slot_flags = SLOT_BACK - force = 5 - force_unwielded = 5 - force_wielded = 30 - throwforce = 15 - throw_range = 1 - w_class = WEIGHT_CLASS_HUGE - var/charged = 5 - origin_tech = "combat=5;bluespace=4" - -/obj/item/twohanded/knighthammer/New() - ..() - START_PROCESSING(SSobj, src) - -/obj/item/twohanded/knighthammer/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/twohanded/knighthammer/process() - if(charged < 5) - charged++ - -/obj/item/twohanded/knighthammer/update_icon() //Currently only here to fuck with the on-mob icons. - icon_state = "knighthammer[wielded]" - ..() - -/obj/item/twohanded/knighthammer/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return - if(charged == 5) - charged = 0 - if(isliving(A)) - var/mob/living/Z = A - if(Z.health >= 1) - Z.visible_message("[Z.name] was sent flying by a blow from the [name]!", \ - "You feel a powerful blow connect with your body and send you flying!", \ - "You hear something heavy impact flesh!.") - var/atom/throw_target = get_edge_target_turf(Z, get_dir(src, get_step_away(Z, src))) - Z.throw_at(throw_target, 200, 4) - playsound(user, 'sound/weapons/marauder.ogg', 50, 1) - else if(wielded && Z.health < 1) - Z.visible_message("[Z.name] was blown to pieces by the power of [name]!", \ - "You feel a powerful blow rip you apart!", \ - "You hear a heavy impact and the sound of ripping flesh!.") - Z.gib() - playsound(user, 'sound/weapons/marauder.ogg', 50, 1) - if(wielded) - if(istype(A, /turf/simulated/wall)) - var/turf/simulated/wall/Z = A - Z.ex_act(2) - charged = 3 - playsound(user, 'sound/weapons/marauder.ogg', 50, 1) - else if(istype(A, /obj/structure) || istype(A, /obj/mecha)) - var/obj/Z = A - Z.ex_act(2) - charged = 3 - playsound(user, 'sound/weapons/marauder.ogg', 50, 1) - -// Energized Fire axe -/obj/item/twohanded/energizedfireaxe - name = "energized fire axe" - desc = "Someone with a love for fire axes decided to turn one into a single-charge energy weapon. Seems excessive." - icon_state = "fireaxe0" - force = 5 - throwforce = 15 - sharp = TRUE - w_class = WEIGHT_CLASS_HUGE - armour_penetration = 20 - slot_flags = SLOT_BACK - force_unwielded = 5 - force_wielded = 30 - attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) - var/charged = 1 - -/obj/item/twohanded/energizedfireaxe/update_icon() - if(wielded) - icon_state = "fireaxe2" - else - icon_state = "fireaxe0" - ..() - -/obj/item/twohanded/energizedfireaxe/afterattack(atom/A, mob/user, proximity) - if(!proximity) - return - if(wielded) - if(isliving(A)) - var/mob/living/Z = A - if(charged) - charged-- - Z.take_organ_damage(0, 30) - user.visible_message("[user] slams the charged axe into [Z.name] with all [user.p_their()] might!") - playsound(loc, 'sound/magic/lightningbolt.ogg', 5, 1) - do_sparks(1, 1, src) - - if(A && wielded && (istype(A, /obj/structure/window) || istype(A, /obj/structure/grille))) - if(istype(A, /obj/structure/window)) - var/obj/structure/window/W = A - W.deconstruct(FALSE) - if(prob(4)) - charged++ - user.visible_message("The axe starts to emit an electric buzz!") - else - qdel(A) - if(prob(4)) - charged++ - user.visible_message("The axe starts to emit an electric buzz!") - -/obj/item/twohanded/pitchfork - icon_state = "pitchfork0" - name = "pitchfork" - desc = "A simple tool used for moving hay." - force = 7 - throwforce = 15 - w_class = WEIGHT_CLASS_BULKY - force_unwielded = 7 - force_wielded = 15 - attack_verb = list("attacked", "impaled", "pierced") - hitsound = 'sound/weapons/bladeslice.ogg' - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) - resistance_flags = FIRE_PROOF - -/obj/item/twohanded/pitchfork/demonic - name = "demonic pitchfork" - desc = "A red pitchfork, it looks like the work of the devil." - force = 19 - throwforce = 24 - force_unwielded = 19 - force_wielded = 25 - -/obj/item/twohanded/pitchfork/demonic/greater - force = 24 - throwforce = 50 - force_unwielded = 24 - force_wielded = 34 - -/obj/item/twohanded/pitchfork/demonic/ascended - force = 100 - throwforce = 100 - force_unwielded = 100 - force_wielded = 500000 // Kills you DEAD. - -/obj/item/twohanded/pitchfork/update_icon() - icon_state = "pitchfork[wielded]" - -/obj/item/twohanded/pitchfork/suicide_act(mob/user) - user.visible_message("[user] impales \himself in \his abdomen with [src]! It looks like \he's trying to commit suicide...") - return BRUTELOSS - -/obj/item/twohanded/pitchfork/demonic/pickup(mob/user) - . = ..() - if(istype(user, /mob/living)) - var/mob/living/U = user - if(U.mind && !U.mind.devilinfo && (U.mind.soulOwner == U.mind)) //Burn hands unless they are a devil or have sold their soul - U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \ - "\"As you pick up the [src] your arms ignite, reminding you of all your past sins.\"") - if(ishuman(U)) - var/mob/living/carbon/human/H = U - H.apply_damage(rand(force/2, force), BURN, pick("l_arm", "r_arm")) - else - U.adjustFireLoss(rand(force/2,force)) - -/obj/item/twohanded/pitchfork/demonic/attack(mob/target, mob/living/carbon/human/user) - if(user.mind && !user.mind.devilinfo && (user.mind.soulOwner != user.mind)) - to_chat(user, "The [src] burns in your hands.") - user.apply_damage(rand(force/2, force), BURN, pick("l_arm", "r_arm")) - ..() - -// It's no fun being the lord of all hell if you can't get out of a simple room -/obj/item/twohanded/pitchfork/demonic/ascended/afterattack(atom/target, mob/user, proximity) - if(!proximity || !wielded) - return - if(istype(target, /turf/simulated/wall)) - var/turf/simulated/wall/W = target - user.visible_message("[user] blasts \the [target] with \the [src]!") - playsound(target, 'sound/magic/Disintegrate.ogg', 100, 1) - W.devastate_wall(TRUE) - return 1 - ..() +/* Two-handed Weapons + * Contains: + * Twohanded + * Fireaxe + * Double-Bladed Energy Swords + * Spears + * Kidan spear + * Chainsaw + * Singularity hammer + * Mjolnnir + * Knighthammer + */ + +/*################################################################## +##################### TWO HANDED WEAPONS BE HERE~ -Agouri :3 ######## +####################################################################*/ + +//Rewrote TwoHanded weapons stuff and put it all here. Just copypasta fireaxe to make new ones ~Carn +//This rewrite means we don't have two variables for EVERY item which are used only by a few weapons. +//It also tidies stuff up elsewhere. + +/* + * Twohanded + */ +/obj/item/twohanded + var/wielded = FALSE + var/force_unwielded = 0 + var/force_wielded = 0 + var/wieldsound = null + var/unwieldsound = null + var/sharp_when_wielded = FALSE + +/obj/item/twohanded/proc/unwield(mob/living/carbon/user) + if(!wielded || !user) + return + wielded = FALSE + force = force_unwielded + if(sharp_when_wielded) + sharp = FALSE + var/sf = findtext(name," (Wielded)") + if(sf) + name = copytext(name, 1, sf) + else //something wrong + name = "[initial(name)]" + update_icon() + if(user) + user.update_inv_r_hand() + user.update_inv_l_hand() + if(isrobot(user)) + to_chat(user, "You free up your module.") + else + to_chat(user, "You are now carrying [name] with one hand.") + if(unwieldsound) + playsound(loc, unwieldsound, 50, 1) + var/obj/item/twohanded/offhand/O = user.get_inactive_hand() + if(O && istype(O)) + O.unwield() + +/obj/item/twohanded/proc/wield(mob/living/carbon/user) + if(wielded) + return + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.dna.species.is_small) + to_chat(user, "It's too heavy for you to wield fully.") + return + if(user.get_inactive_hand()) + to_chat(user, "You need your other hand to be empty!") + return + wielded = TRUE + force = force_wielded + if(sharp_when_wielded) + sharp = TRUE + name = "[name] (Wielded)" + update_icon() + if(user) + user.update_inv_r_hand() + user.update_inv_l_hand() + if(isrobot(user)) + to_chat(user, "You dedicate your module to [name].") + else + to_chat(user, "You grab the [name] with both hands.") + if(wieldsound) + playsound(loc, wieldsound, 50, 1) + var/obj/item/twohanded/offhand/O = new(user) ////Let's reserve his other hand~ + O.name = "[name] - offhand" + O.desc = "Your second grip on the [name]" + user.put_in_inactive_hand(O) + +/obj/item/twohanded/dropped(mob/user) + ..() + //handles unwielding a twohanded weapon when dropped as well as clearing up the offhand + if(user) + var/obj/item/twohanded/O = user.get_inactive_hand() + if(istype(O)) + O.unwield(user) + return unwield(user) + +/obj/item/twohanded/attack_self(mob/user) + ..() + if(wielded) //Trying to unwield it + unwield(user) + else //Trying to wield it + wield(user) + + +/obj/item/twohanded/equip_to_best_slot(mob/M) + if(..()) + unwield(M) + return + +///////////OFFHAND/////////////// +/obj/item/twohanded/offhand + w_class = WEIGHT_CLASS_HUGE + icon_state = "offhand" + name = "offhand" + flags = ABSTRACT + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/item/twohanded/offhand/unwield() + if(!QDELETED(src)) + qdel(src) + +/obj/item/twohanded/offhand/wield() + if(!QDELETED(src)) + qdel(src) + +///////////Two hand required objects/////////////// +//This is for objects that require two hands to even pick up +/obj/item/twohanded/required + w_class = WEIGHT_CLASS_HUGE + +/obj/item/twohanded/required/attack_self() + return + +/obj/item/twohanded/required/mob_can_equip(mob/M, slot) + if(wielded && !slot_flags) + to_chat(M, "[src] is too cumbersome to carry with anything but your hands!") + return FALSE + return ..() + +/obj/item/twohanded/required/attack_hand(mob/user)//Can't even pick it up without both hands empty + var/obj/item/twohanded/required/H = user.get_inactive_hand() + if(get_dist(src, user) > 1) + return FALSE + if(H != null) + to_chat(user, "[src] is too cumbersome to carry in one hand!") + return + if(loc != user) + wield(user) + ..() + +/obj/item/twohanded/required/on_give(mob/living/carbon/giver, mob/living/carbon/receiver) + var/obj/item/twohanded/required/H = receiver.get_inactive_hand() + if(H != null) //Check if he can wield it + receiver.drop_item() //Can't wear it so drop it + to_chat(receiver, "[src] is too cumbersome to carry in one hand!") + return + equipped(receiver,receiver.hand ? slot_l_hand : slot_r_hand) + +/obj/item/twohanded/required/equipped(mob/user, slot) + ..() + if(slot == slot_l_hand || slot == slot_r_hand) + wield(user) + else + unwield(user) + +/* + * Fireaxe + */ +/obj/item/twohanded/fireaxe // DEM AXES MAN, marker -Agouri + icon_state = "fireaxe0" + name = "fire axe" + desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?" + force = 5 + throwforce = 15 + sharp = TRUE + w_class = WEIGHT_CLASS_BULKY + slot_flags = SLOT_BACK + force_unwielded = 5 + force_wielded = 24 + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + usesound = 'sound/items/crowbar.ogg' + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) + resistance_flags = FIRE_PROOF + +/obj/item/twohanded/fireaxe/update_icon() //Currently only here to fuck with the on-mob icons. + icon_state = "fireaxe[wielded]" + ..() + +/obj/item/twohanded/fireaxe/afterattack(atom/A, mob/user, proximity) + if(!proximity) + return + if(wielded) //destroys windows and grilles in one hit + if(istype(A, /obj/structure/window) || istype(A, /obj/structure/grille)) + var/obj/structure/W = A + W.obj_destruction("fireaxe") + + +/obj/item/twohanded/fireaxe/boneaxe // Blatant imitation of the fireaxe, but made out of bone. + icon_state = "bone_axe0" + name = "bone axe" + desc = "A large, vicious axe crafted out of several sharpened bone plates and crudely tied together. Made of monsters, by killing monsters, for killing monsters." + force_wielded = 23 + +/obj/item/twohanded/fireaxe/boneaxe/update_icon() + icon_state = "bone_axe[wielded]" + +/* + * Double-Bladed Energy Swords - Cheridan + */ +/obj/item/twohanded/dualsaber + var/hacked = FALSE + var/blade_color + icon_state = "dualsaber0" + name = "double-bladed energy sword" + desc = "Handle with care." + force = 3 + throwforce = 5 + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + var/w_class_on = WEIGHT_CLASS_BULKY + force_unwielded = 3 + force_wielded = 34 + wieldsound = 'sound/weapons/saberon.ogg' + unwieldsound = 'sound/weapons/saberoff.ogg' + armour_penetration = 35 + origin_tech = "magnets=4;syndicate=5" + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + block_chance = 75 + sharp_when_wielded = TRUE // only sharp when wielded + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) + resistance_flags = FIRE_PROOF + light_power = 2 + var/brightness_on = 2 + var/colormap = list(red=LIGHT_COLOR_RED, blue=LIGHT_COLOR_LIGHTBLUE, green=LIGHT_COLOR_GREEN, purple=LIGHT_COLOR_PURPLE, rainbow=LIGHT_COLOR_WHITE) + +/obj/item/twohanded/dualsaber/New() + ..() + if(!blade_color) + blade_color = pick("red", "blue", "green", "purple") + +/obj/item/twohanded/dualsaber/update_icon() + if(wielded) + icon_state = "dualsaber[blade_color][wielded]" + set_light(brightness_on, l_color=colormap[blade_color]) + else + icon_state = "dualsaber0" + set_light(0) + ..() + +/obj/item/twohanded/dualsaber/attack(mob/target, mob/living/user) + if(HULK in user.mutations) + to_chat(user, "You grip the blade too hard and accidentally close it!") + unwield() + return + ..() + if((CLUMSY in user.mutations) && (wielded) && prob(40)) + to_chat(user, "You twirl around a bit before losing your balance and impaling yourself on the [src].") + user.take_organ_damage(20, 25) + return + if((wielded) && prob(50)) + INVOKE_ASYNC(src, .proc/jedi_spin, user) + +/obj/item/twohanded/dualsaber/proc/jedi_spin(mob/living/user) + for(var/i in list(NORTH, SOUTH, EAST, WEST, EAST, SOUTH, NORTH, SOUTH, EAST, WEST, EAST, SOUTH)) + user.setDir(i) + if(i == WEST) + user.SpinAnimation(7, 1) + sleep(1) + +/obj/item/twohanded/dualsaber/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(wielded) + return ..() + return FALSE + +/obj/item/twohanded/dualsaber/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) //In case thats just so happens that it is still activated on the groud, prevents hulk from picking it up + if(wielded) + to_chat(user, "You can't pick up such a dangerous item with your meaty hands without losing fingers, better not to!") + return TRUE + +/obj/item/twohanded/dualsaber/green + blade_color = "green" + +/obj/item/twohanded/dualsaber/red + blade_color = "red" + +/obj/item/twohanded/dualsaber/purple + blade_color = "purple" + +/obj/item/twohanded/dualsaber/blue + blade_color = "blue" + +/obj/item/twohanded/dualsaber/unwield() + ..() + hitsound = "swing_hit" + w_class = initial(w_class) + +/obj/item/twohanded/dualsaber/IsReflect() + if(wielded) + return TRUE + +/obj/item/twohanded/dualsaber/wield(mob/living/carbon/M) //Specific wield () hulk checks due to reflection chance for balance issues and switches hitsounds. + if(HULK in M.mutations) + to_chat(M, "You lack the grace to wield this!") + return + ..() + hitsound = 'sound/weapons/blade1.ogg' + w_class = w_class_on + +/obj/item/twohanded/dualsaber/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!hacked) + hacked = TRUE + to_chat(user, "2XRNBW_ENGAGE") + blade_color = "rainbow" + update_icon() + else + to_chat(user, "It's starting to look like a triple rainbow - no, nevermind.") + +//spears +/obj/item/twohanded/spear + icon_state = "spearglass0" + name = "spear" + desc = "A haphazardly-constructed yet still deadly weapon of ancient design." + force = 10 + w_class = WEIGHT_CLASS_BULKY + slot_flags = SLOT_BACK + force_unwielded = 10 + force_wielded = 18 + throwforce = 20 + throw_speed = 4 + armour_penetration = 10 + materials = list(MAT_METAL = 1150, MAT_GLASS = 2075) + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "poked", "jabbed", "torn", "gored") + sharp = TRUE + no_spin_thrown = TRUE + var/obj/item/grenade/explosive = null + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30) + var/icon_prefix = "spearglass" + +/obj/item/twohanded/spear/update_icon() + icon_state = "[icon_prefix][wielded]" + +/obj/item/twohanded/spear/CheckParts(list/parts_list) + var/obj/item/shard/tip = locate() in parts_list + if(istype(tip, /obj/item/shard/plasma)) + force_wielded = 19 + force_unwielded = 11 + throwforce = 21 + icon_prefix = "spearplasma" + update_icon() + qdel(tip) + ..() + + +/obj/item/twohanded/spear/afterattack(atom/movable/AM, mob/user, proximity) + if(!proximity) + return + if(isturf(AM)) //So you can actually melee with it + return + if(explosive && wielded) + explosive.forceMove(AM) + explosive.prime() + qdel(src) + +/obj/item/twohanded/spear/throw_impact(atom/target) + . = ..() + if(explosive) + explosive.prime() + qdel(src) + +/obj/item/twohanded/spear/bonespear //Blatant imitation of spear, but made out of bone. Not valid for explosive modification. + icon_state = "bone_spear0" + name = "bone spear" + desc = "A haphazardly-constructed yet still deadly weapon. The pinnacle of modern technology." + force = 11 + force_unwielded = 11 + force_wielded = 20 //I have no idea how to balance + throwforce = 22 + armour_penetration = 15 //Enhanced armor piercing + icon_prefix = "bone_spear" + +//GREY TIDE +/obj/item/twohanded/spear/grey_tide + icon_state = "spearglass0" + name = "\improper Grey Tide" + desc = "Recovered from the aftermath of a revolt aboard Defense Outpost Theta Aegis, in which a seemingly endless tide of Assistants caused heavy casualities among Nanotrasen military forces." + force_unwielded = 15 + force_wielded = 25 + throwforce = 20 + throw_speed = 4 + attack_verb = list("gored") + +/obj/item/twohanded/spear/grey_tide/afterattack(atom/movable/AM, mob/living/user, proximity) + ..() + if(!proximity) + return + user.faction |= "greytide(\ref[user])" + if(isliving(AM)) + var/mob/living/L = AM + if(istype (L, /mob/living/simple_animal/hostile/illusion)) + return + if(!L.stat && prob(50)) + var/mob/living/simple_animal/hostile/illusion/M = new(user.loc) + M.faction = user.faction.Copy() + M.attack_sound = hitsound + M.Copy_Parent(user, 100, user.health/2.5, 12, 30) + M.GiveTarget(L) + +//Putting heads on spears +/obj/item/twohanded/spear/attackby(obj/item/I, mob/living/user) + if(istype(I, /obj/item/organ/external/head)) + if(user.unEquip(src) && user.drop_item()) + to_chat(user, "You stick [I] onto the spear and stand it upright on the ground.") + var/obj/structure/headspear/HS = new /obj/structure/headspear(get_turf(src)) + var/matrix/M = matrix() + I.transform = M + var/image/IM = image(I.icon, I.icon_state) + IM.overlays = I.overlays.Copy() + HS.overlays += IM + I.forceMove(HS) + HS.mounted_head = I + forceMove(HS) + HS.contained_spear = src + else + return ..() + +/obj/structure/headspear + name = "head on a spear" + desc = "How barbaric." + icon_state = "headspear" + density = FALSE + anchored = TRUE + var/obj/item/organ/external/head/mounted_head = null + var/obj/item/twohanded/spear/contained_spear = null + +/obj/structure/headspear/Destroy() + QDEL_NULL(mounted_head) + QDEL_NULL(contained_spear) + return ..() + +/obj/structure/headspear/attack_hand(mob/living/user) + user.visible_message("[user] kicks over [src]!", "You kick down [src]!") + playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + var/turf/T = get_turf(src) + if(contained_spear) + contained_spear.forceMove(T) + contained_spear = null + if(mounted_head) + mounted_head.forceMove(T) + mounted_head = null + qdel(src) + +/obj/item/twohanded/spear/kidan + icon_state = "kidanspear0" + name = "Kidan spear" + desc = "A spear brought over from the Kidan homeworld." + +// DIY CHAINSAW +/obj/item/twohanded/required/chainsaw + name = "chainsaw" + desc = "A versatile power tool. Useful for limbing trees and delimbing humans." + icon_state = "gchainsaw_off" + flags = CONDUCT + force = 13 + var/force_on = 24 + w_class = WEIGHT_CLASS_HUGE + throwforce = 13 + throw_speed = 2 + throw_range = 4 + materials = list(MAT_METAL = 13000) + origin_tech = "materials=3;engineering=4;combat=2" + attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered") + hitsound = "swing_hit" + sharp = TRUE + actions_types = list(/datum/action/item_action/startchainsaw) + var/on = FALSE + +/obj/item/twohanded/required/chainsaw/attack_self(mob/user) + on = !on + to_chat(user, "As you pull the starting cord dangling from [src], [on ? "it begins to whirr." : "the chain stops moving."]") + if(on) + playsound(loc, 'sound/weapons/chainsawstart.ogg', 50, 1) + force = on ? force_on : initial(force) + throwforce = on ? force_on : initial(throwforce) + icon_state = "gchainsaw_[on ? "on" : "off"]" + + if(hitsound == "swing_hit") + hitsound = 'sound/weapons/chainsaw.ogg' + else + hitsound = "swing_hit" + + if(src == user.get_active_hand()) //update inhands + user.update_inv_l_hand() + user.update_inv_r_hand() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/twohanded/required/chainsaw/attack_hand(mob/user) + . = ..() + force = on ? force_on : initial(force) + throwforce = on ? force_on : initial(throwforce) + +/obj/item/twohanded/required/chainsaw/on_give(mob/living/carbon/giver, mob/living/carbon/receiver) + . = ..() + force = on ? force_on : initial(force) + throwforce = on ? force_on : initial(throwforce) + +/obj/item/twohanded/required/chainsaw/doomslayer + name = "OOOH BABY" + desc = "VRRRRRRR!!!" + armour_penetration = 100 + force_on = 30 + +/obj/item/twohanded/required/chainsaw/doomslayer/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(attack_type == PROJECTILE_ATTACK) + owner.visible_message("Ranged attacks just make [owner] angrier!") + playsound(src, pick('sound/weapons/bulletflyby.ogg','sound/weapons/bulletflyby2.ogg','sound/weapons/bulletflyby3.ogg'), 75, 1) + return TRUE + return FALSE + + +///CHAINSAW/// +/obj/item/twohanded/chainsaw + icon_state = "chainsaw0" + name = "Chainsaw" + desc = "Perfect for felling trees or fellow spacemen." + force = 15 + throwforce = 15 + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_BULKY // can't fit in backpacks + force_unwielded = 15 //still pretty robust + force_wielded = 40 //you'll gouge their eye out! Or a limb...maybe even their entire body! + wieldsound = 'sound/weapons/chainsawstart.ogg' + hitsound = null + armour_penetration = 35 + origin_tech = "materials=6;syndicate=4" + attack_verb = list("sawed", "cut", "hacked", "carved", "cleaved", "butchered", "felled", "timbered") + sharp = TRUE + +/obj/item/twohanded/chainsaw/update_icon() + if(wielded) + icon_state = "chainsaw[wielded]" + else + icon_state = "chainsaw0" + ..() + +/obj/item/twohanded/chainsaw/attack(mob/target, mob/living/user) + if(wielded) + playsound(loc, 'sound/weapons/chainsaw.ogg', 100, 1, -1) //incredibly loud; you ain't goin' for stealth with this thing. Credit to Lonemonk of Freesound for this sound. + if(isrobot(target)) + ..() + return + if(!isliving(target)) + return + else + target.Weaken(4) + ..() + return + else + playsound(loc, "swing_hit", 50, 1, -1) + return ..() + +/obj/item/twohanded/chainsaw/wield() //you can't disarm an active chainsaw, you crazy person. + ..() + flags |= NODROP + +/obj/item/twohanded/chainsaw/unwield() + ..() + flags &= ~NODROP + +// SINGULOHAMMER +/obj/item/twohanded/singularityhammer + name = "singularity hammer" + desc = "The pinnacle of close combat technology, the hammer harnesses the power of a miniaturized singularity to deal crushing blows." + icon_state = "mjollnir0" + flags = CONDUCT + slot_flags = SLOT_BACK + force = 5 + force_unwielded = 5 + force_wielded = 20 + throwforce = 15 + throw_range = 1 + w_class = WEIGHT_CLASS_HUGE + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + var/charged = 5 + origin_tech = "combat=4;bluespace=4;plasmatech=7" + +/obj/item/twohanded/singularityhammer/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/twohanded/singularityhammer/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/twohanded/singularityhammer/process() + if(charged < 5) + charged++ + +/obj/item/twohanded/singularityhammer/update_icon() //Currently only here to fuck with the on-mob icons. + icon_state = "mjollnir[wielded]" + ..() + +/obj/item/twohanded/singularityhammer/proc/vortex(turf/pull, mob/wielder) + for(var/atom/movable/X in orange(5, pull)) + if(X == wielder) + continue + if((X) && (!X.anchored) && (!ishuman(X))) + step_towards(X, pull) + step_towards(X, pull) + step_towards(X, pull) + else if(ishuman(X)) + var/mob/living/carbon/human/H = X + if(istype(H.shoes, /obj/item/clothing/shoes/magboots)) + var/obj/item/clothing/shoes/magboots/M = H.shoes + if(M.magpulse) + continue + H.apply_effect(1, WEAKEN, 0) + step_towards(H, pull) + step_towards(H, pull) + step_towards(H, pull) + +/obj/item/twohanded/singularityhammer/afterattack(atom/A, mob/user, proximity) + if(!proximity) + return + if(wielded) + if(charged == 5) + charged = 0 + if(isliving(A)) + var/mob/living/Z = A + Z.take_organ_damage(20, 0) + playsound(user, 'sound/weapons/marauder.ogg', 50, 1) + var/turf/target = get_turf(A) + vortex(target, user) + +/obj/item/twohanded/mjollnir + name = "Mjolnir" + desc = "A weapon worthy of a god, able to strike with the force of a lightning bolt. It crackles with barely contained energy." + icon_state = "mjollnir0" + flags = CONDUCT + slot_flags = SLOT_BACK + force = 5 + force_unwielded = 5 + force_wielded = 25 + throwforce = 30 + throw_range = 7 + w_class = WEIGHT_CLASS_HUGE + //var/charged = 5 + origin_tech = "combat=4;powerstorage=7" + +/obj/item/twohanded/mjollnir/proc/shock(mob/living/target) + do_sparks(5, 1, target.loc) + target.visible_message("[target.name] was shocked by the [name]!", \ + "You feel a powerful shock course through your body sending you flying!", \ + "You hear a heavy electrical crack!") + var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src))) + target.throw_at(throw_target, 200, 4) + +/obj/item/twohanded/mjollnir/attack(mob/M, mob/user) + ..() + if(wielded) + //if(charged == 5) + //charged = 0 + playsound(loc, "sparks", 50, 1) + if(isliving(M)) + M.Stun(3) + shock(M) + +/obj/item/twohanded/mjollnir/throw_impact(atom/target) + . = ..() + if(isliving(target)) + var/mob/living/L = target + L.Stun(3) + shock(L) + +/obj/item/twohanded/mjollnir/update_icon() //Currently only here to fuck with the on-mob icons. + icon_state = "mjollnir[wielded]" + ..() + +/obj/item/twohanded/knighthammer + name = "singuloth knight's hammer" + desc = "A hammer made of sturdy metal with a golden skull adorned with wings on either side of the head.
    This weapon causes devastating damage to those it hits due to a power field sustained by a mini-singularity inside of the hammer." + icon_state = "knighthammer0" + flags = CONDUCT + slot_flags = SLOT_BACK + force = 5 + force_unwielded = 5 + force_wielded = 30 + throwforce = 15 + throw_range = 1 + w_class = WEIGHT_CLASS_HUGE + var/charged = 5 + origin_tech = "combat=5;bluespace=4" + +/obj/item/twohanded/knighthammer/New() + ..() + START_PROCESSING(SSobj, src) + +/obj/item/twohanded/knighthammer/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/twohanded/knighthammer/process() + if(charged < 5) + charged++ + +/obj/item/twohanded/knighthammer/update_icon() //Currently only here to fuck with the on-mob icons. + icon_state = "knighthammer[wielded]" + ..() + +/obj/item/twohanded/knighthammer/afterattack(atom/A, mob/user, proximity) + if(!proximity) + return + if(charged == 5) + charged = 0 + if(isliving(A)) + var/mob/living/Z = A + if(Z.health >= 1) + Z.visible_message("[Z.name] was sent flying by a blow from the [name]!", \ + "You feel a powerful blow connect with your body and send you flying!", \ + "You hear something heavy impact flesh!.") + var/atom/throw_target = get_edge_target_turf(Z, get_dir(src, get_step_away(Z, src))) + Z.throw_at(throw_target, 200, 4) + playsound(user, 'sound/weapons/marauder.ogg', 50, 1) + else if(wielded && Z.health < 1) + Z.visible_message("[Z.name] was blown to pieces by the power of [name]!", \ + "You feel a powerful blow rip you apart!", \ + "You hear a heavy impact and the sound of ripping flesh!.") + Z.gib() + playsound(user, 'sound/weapons/marauder.ogg', 50, 1) + if(wielded) + if(istype(A, /turf/simulated/wall)) + var/turf/simulated/wall/Z = A + Z.ex_act(2) + charged = 3 + playsound(user, 'sound/weapons/marauder.ogg', 50, 1) + else if(istype(A, /obj/structure) || istype(A, /obj/mecha)) + var/obj/Z = A + Z.ex_act(2) + charged = 3 + playsound(user, 'sound/weapons/marauder.ogg', 50, 1) + +// Energized Fire axe +/obj/item/twohanded/energizedfireaxe + name = "energized fire axe" + desc = "Someone with a love for fire axes decided to turn one into a single-charge energy weapon. Seems excessive." + icon_state = "fireaxe0" + force = 5 + throwforce = 15 + sharp = TRUE + w_class = WEIGHT_CLASS_HUGE + armour_penetration = 20 + slot_flags = SLOT_BACK + force_unwielded = 5 + force_wielded = 30 + attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut") + hitsound = 'sound/weapons/bladeslice.ogg' + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) + var/charged = 1 + +/obj/item/twohanded/energizedfireaxe/update_icon() + if(wielded) + icon_state = "fireaxe2" + else + icon_state = "fireaxe0" + ..() + +/obj/item/twohanded/energizedfireaxe/afterattack(atom/A, mob/user, proximity) + if(!proximity) + return + if(wielded) + if(isliving(A)) + var/mob/living/Z = A + if(charged) + charged-- + Z.take_organ_damage(0, 30) + user.visible_message("[user] slams the charged axe into [Z.name] with all [user.p_their()] might!") + playsound(loc, 'sound/magic/lightningbolt.ogg', 5, 1) + do_sparks(1, 1, src) + + if(A && wielded && (istype(A, /obj/structure/window) || istype(A, /obj/structure/grille))) + if(istype(A, /obj/structure/window)) + var/obj/structure/window/W = A + W.deconstruct(FALSE) + if(prob(4)) + charged++ + user.visible_message("The axe starts to emit an electric buzz!") + else + qdel(A) + if(prob(4)) + charged++ + user.visible_message("The axe starts to emit an electric buzz!") + +/obj/item/twohanded/pitchfork + icon_state = "pitchfork0" + name = "pitchfork" + desc = "A simple tool used for moving hay." + force = 7 + throwforce = 15 + w_class = WEIGHT_CLASS_BULKY + force_unwielded = 7 + force_wielded = 15 + attack_verb = list("attacked", "impaled", "pierced") + hitsound = 'sound/weapons/bladeslice.ogg' + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 30) + resistance_flags = FIRE_PROOF + +/obj/item/twohanded/pitchfork/demonic + name = "demonic pitchfork" + desc = "A red pitchfork, it looks like the work of the devil." + force = 19 + throwforce = 24 + force_unwielded = 19 + force_wielded = 25 + +/obj/item/twohanded/pitchfork/demonic/greater + force = 24 + throwforce = 50 + force_unwielded = 24 + force_wielded = 34 + +/obj/item/twohanded/pitchfork/demonic/ascended + force = 100 + throwforce = 100 + force_unwielded = 100 + force_wielded = 500000 // Kills you DEAD. + +/obj/item/twohanded/pitchfork/update_icon() + icon_state = "pitchfork[wielded]" + +/obj/item/twohanded/pitchfork/suicide_act(mob/user) + user.visible_message("[user] impales \himself in \his abdomen with [src]! It looks like \he's trying to commit suicide...") + return BRUTELOSS + +/obj/item/twohanded/pitchfork/demonic/pickup(mob/user) + . = ..() + if(istype(user, /mob/living)) + var/mob/living/U = user + if(U.mind && !U.mind.devilinfo && (U.mind.soulOwner == U.mind)) //Burn hands unless they are a devil or have sold their soul + U.visible_message("As [U] picks [src] up, [U]'s arms briefly catch fire.", \ + "\"As you pick up the [src] your arms ignite, reminding you of all your past sins.\"") + if(ishuman(U)) + var/mob/living/carbon/human/H = U + H.apply_damage(rand(force/2, force), BURN, pick("l_arm", "r_arm")) + else + U.adjustFireLoss(rand(force/2,force)) + +/obj/item/twohanded/pitchfork/demonic/attack(mob/target, mob/living/carbon/human/user) + if(user.mind && !user.mind.devilinfo && (user.mind.soulOwner != user.mind)) + to_chat(user, "The [src] burns in your hands.") + user.apply_damage(rand(force/2, force), BURN, pick("l_arm", "r_arm")) + ..() + +// It's no fun being the lord of all hell if you can't get out of a simple room +/obj/item/twohanded/pitchfork/demonic/ascended/afterattack(atom/target, mob/user, proximity) + if(!proximity || !wielded) + return + if(istype(target, /turf/simulated/wall)) + var/turf/simulated/wall/W = target + user.visible_message("[user] blasts \the [target] with \the [src]!") + playsound(target, 'sound/magic/Disintegrate.ogg', 100, 1) + W.devastate_wall(TRUE) + return 1 + ..() diff --git a/code/game/objects/items/weapons/vending_items.dm b/code/game/objects/items/weapons/vending_items.dm index aafebf7ef92c..258771870d89 100644 --- a/code/game/objects/items/weapons/vending_items.dm +++ b/code/game/objects/items/weapons/vending_items.dm @@ -151,4 +151,4 @@ /obj/item/vending_refill/robotics machine_name = "Robotech Deluxe" - icon_state = "refill_engi" \ No newline at end of file + icon_state = "refill_engi" diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm index 735ae7dc6089..32158a9a3ab8 100644 --- a/code/game/objects/items/weapons/weaponry.dm +++ b/code/game/objects/items/weapons/weaponry.dm @@ -1,275 +1,275 @@ -/obj/item/banhammer - desc = "A banhammer" - name = "banhammer" - icon = 'icons/obj/items.dmi' - icon_state = "toyhammer" - slot_flags = SLOT_BELT - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 7 - throw_range = 15 - attack_verb = list("banned") - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) - resistance_flags = FIRE_PROOF - - -/obj/item/banhammer/suicide_act(mob/user) - to_chat(viewers(user), "[user] is hitting [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.") - return BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS - -/obj/item/sord - name = "\improper SORD" - desc = "This thing is so unspeakably shitty you are having a hard time even holding it." - icon_state = "sord" - item_state = "sord" - slot_flags = SLOT_BELT - force = 2 - throwforce = 1 - w_class = WEIGHT_CLASS_NORMAL - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - -/obj/item/sord/suicide_act(mob/user) - user.visible_message("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty.", \ - "You try to impale yourself with [src], but it's USELESS...") - return SHAME - -/obj/item/claymore - name = "claymore" - desc = "What are you standing around staring at this for? Get to killing!" - icon_state = "claymore" - item_state = "claymore" - flags = CONDUCT - hitsound = 'sound/weapons/bladeslice.ogg' - slot_flags = SLOT_BELT - force = 40 - throwforce = 10 - sharp = 1 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 50 - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - -/obj/item/claymore/suicide_act(mob/user) - user.visible_message("[user] is falling on the [name]! It looks like [user.p_theyre()] trying to commit suicide.") - return BRUTELOSS - -/obj/item/claymore/ceremonial - name = "ceremonial claymore" - desc = "An engraved and fancy version of the claymore. It appears to be less sharp than it's more functional cousin." - force = 20 - -/obj/item/katana - name = "katana" - desc = "Woefully underpowered in D20" - icon_state = "katana" - item_state = "katana" - flags = CONDUCT - slot_flags = SLOT_BELT | SLOT_BACK - force = 40 - throwforce = 10 - sharp = 1 - w_class = WEIGHT_CLASS_NORMAL - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - block_chance = 50 - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - -/obj/item/katana/cursed - slot_flags = null - -/obj/item/katana/suicide_act(mob/user) - user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku.") - return BRUTELOSS - -/obj/item/harpoon - name = "harpoon" - sharp = 1 - desc = "Tharr she blows!" - icon_state = "harpoon" - item_state = "harpoon" - force = 20 - throwforce = 15 - w_class = WEIGHT_CLASS_NORMAL - attack_verb = list("jabbed","stabbed","ripped") - -/obj/item/wirerod - name = "Wired rod" - desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit." - icon_state = "wiredrod" - item_state = "rods" - flags = CONDUCT - force = 9 - throwforce = 10 - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=1150, MAT_GLASS=75) - attack_verb = list("hit", "bludgeoned", "whacked", "bonked") - -/obj/item/wirerod/attackby(obj/item/I, mob/user, params) - ..() - if(istype(I, /obj/item/shard)) - var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear - if(istype(I, /obj/item/shard/plasma)) - S.force_wielded = 19 - S.force_unwielded = 11 - S.throwforce = 21 - S.icon_prefix = "spearplasma" - S.update_icon() - if(!remove_item_from_storage(user)) - user.unEquip(src) - user.unEquip(I) - - user.put_in_hands(S) - to_chat(user, "You fasten the glass shard to the top of the rod with the cable.") - qdel(I) - qdel(src) - - else if(istype(I, /obj/item/assembly/igniter) && !(I.flags & NODROP)) - var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod - - if(!remove_item_from_storage(user)) - user.unEquip(src) - user.unEquip(I) - - user.put_in_hands(P) - to_chat(user, "You fasten [I] to the top of the rod with the cable.") - qdel(I) - qdel(src) - -/obj/item/throwing_star - name = "throwing star" - desc = "An ancient weapon still used to this day due to it's ease of lodging itself into victim's body parts" - icon_state = "throwingstar" - item_state = "eshield0" - force = 2 - throwforce = 20 //This is never used on mobs since this has a 100% embed chance. - throw_speed = 4 - embedded_pain_multiplier = 4 - w_class = WEIGHT_CLASS_SMALL - embed_chance = 100 - embedded_fall_chance = 0 //Hahaha! - sharp = 1 - materials = list(MAT_METAL=500, MAT_GLASS=500) - resistance_flags = FIRE_PROOF - -/obj/item/spear/kidan - icon_state = "kidanspear" - name = "Kidan spear" - desc = "A one-handed spear brought over from the Kidan homeworld." - icon_state = "kidanspear" - item_state = "kidanspear" - force = 10 - throwforce = 15 - -/obj/item/melee/baseball_bat - name = "baseball bat" - desc = "There ain't a skull in the league that can withstand a swatter." - icon = 'icons/obj/items.dmi' - icon_state = "baseball_bat" - item_state = "baseball_bat" - var/deflectmode = FALSE // deflect small/medium thrown objects - var/lastdeflect - force = 10 - throwforce = 12 - attack_verb = list("beat", "smacked") - w_class = WEIGHT_CLASS_HUGE - var/homerun_ready = 0 - var/homerun_able = 0 - -/obj/item/melee/baseball_bat/homerun - name = "home run bat" - desc = "This thing looks dangerous... Dangerously good at baseball, that is." - homerun_able = 1 - -/obj/item/melee/baseball_bat/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - . = ..() - if(!isitem(hitby) || attack_type != THROWN_PROJECTILE_ATTACK) - return FALSE - var/obj/item/I = hitby - if(I.w_class <= WEIGHT_CLASS_NORMAL || istype(I, /obj/item/beach_ball)) // baseball bat deflecting - if(deflectmode) - if(prob(10)) - visible_message("[owner] Deflects [I] directly back at the thrower! It's a home run!", "You deflect the [I] directly back at the thrower! It's a home run!") - playsound(get_turf(owner), 'sound/weapons/homerun.ogg', 100, 1) - do_attack_animation(I, ATTACK_EFFECT_DISARM) - I.throw_at(I.thrownby, 20, 20, owner) - deflectmode = FALSE - if(!istype(I, /obj/item/beach_ball)) - lastdeflect = world.time + 3000 - return TRUE - else if(prob(30)) - visible_message("[owner] swings! And [p_they()] miss[p_es()]! How embarassing.", "You swing! You miss! Oh no!") - playsound(get_turf(owner), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - do_attack_animation(get_step(owner, pick(alldirs)), ATTACK_EFFECT_DISARM) - deflectmode = FALSE - if(!istype(I, /obj/item/beach_ball)) - lastdeflect = world.time + 3000 - return FALSE - else - visible_message("[owner] swings and deflects [I]!", "You swing and deflect the [I]!") - playsound(get_turf(owner), 'sound/weapons/baseball_hit.ogg', 50, 1, -1) - do_attack_animation(I, ATTACK_EFFECT_DISARM) - I.throw_at(get_edge_target_turf(owner, pick(cardinal)), rand(8,10), 14, owner) - deflectmode = FALSE - if(!istype(I, /obj/item/beach_ball)) - lastdeflect = world.time + 3000 - return TRUE - -/obj/item/melee/baseball_bat/attack_self(mob/user) - if(!homerun_able) - if(!deflectmode && world.time >= lastdeflect) - to_chat(user, "You prepare to deflect objects thrown at you. You cannot attack during this time.") - deflectmode = TRUE - else if(deflectmode && world.time >= lastdeflect) - to_chat(user, "You no longer deflect objects thrown at you. You can attack during this time") - deflectmode = FALSE - else - to_chat(user, "You need to wait until you can deflect again. The ability will be ready in [time2text(lastdeflect - world.time, "m:ss")]") - return ..() - if(homerun_ready) - to_chat(user, "You're already ready to do a home run!") - return ..() - to_chat(user, "You begin gathering strength...") - playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1) - if(do_after(user, 90, target = user)) - to_chat(user, "You gather power! Time for a home run!") - homerun_ready = 1 - ..() - -/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) - if(deflectmode) - to_chat(user, "You cannot attack in deflect mode!") - return - . = ..() - var/atom/throw_target = get_edge_target_turf(target, user.dir) - if(homerun_ready) - user.visible_message("It's a home run!") - target.throw_at(throw_target, rand(8,10), 14, user) - target.ex_act(2) - playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1) - homerun_ready = 0 - return - else if(!target.anchored) - target.throw_at(throw_target, rand(1,2), 7, user) - -/obj/item/melee/baseball_bat/ablative - name = "metal baseball bat" - desc = "This bat is made of highly reflective, highly armored material." - icon_state = "baseball_bat_metal" - item_state = "baseball_bat_metal" - force = 12 - throwforce = 15 - -/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers - var/picksound = rand(1,2) - var/turf = get_turf(src) - if(picksound == 1) - playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1) - if(picksound == 2) - playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1) - return 1 +/obj/item/banhammer + desc = "A banhammer" + name = "banhammer" + icon = 'icons/obj/items.dmi' + icon_state = "toyhammer" + slot_flags = SLOT_BELT + throwforce = 0 + w_class = WEIGHT_CLASS_TINY + throw_speed = 7 + throw_range = 15 + attack_verb = list("banned") + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 70) + resistance_flags = FIRE_PROOF + + +/obj/item/banhammer/suicide_act(mob/user) + to_chat(viewers(user), "[user] is hitting [user.p_them()]self with the [src.name]! It looks like [user.p_theyre()] trying to ban [user.p_them()]self from life.") + return BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS + +/obj/item/sord + name = "\improper SORD" + desc = "This thing is so unspeakably shitty you are having a hard time even holding it." + icon_state = "sord" + item_state = "sord" + slot_flags = SLOT_BELT + force = 2 + throwforce = 1 + w_class = WEIGHT_CLASS_NORMAL + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + +/obj/item/sord/suicide_act(mob/user) + user.visible_message("[user] is trying to impale [user.p_them()]self with [src]! It might be a suicide attempt if it weren't so shitty.", \ + "You try to impale yourself with [src], but it's USELESS...") + return SHAME + +/obj/item/claymore + name = "claymore" + desc = "What are you standing around staring at this for? Get to killing!" + icon_state = "claymore" + item_state = "claymore" + flags = CONDUCT + hitsound = 'sound/weapons/bladeslice.ogg' + slot_flags = SLOT_BELT + force = 40 + throwforce = 10 + sharp = 1 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + block_chance = 50 + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + +/obj/item/claymore/suicide_act(mob/user) + user.visible_message("[user] is falling on the [name]! It looks like [user.p_theyre()] trying to commit suicide.") + return BRUTELOSS + +/obj/item/claymore/ceremonial + name = "ceremonial claymore" + desc = "An engraved and fancy version of the claymore. It appears to be less sharp than it's more functional cousin." + force = 20 + +/obj/item/katana + name = "katana" + desc = "Woefully underpowered in D20" + icon_state = "katana" + item_state = "katana" + flags = CONDUCT + slot_flags = SLOT_BELT | SLOT_BACK + force = 40 + throwforce = 10 + sharp = 1 + w_class = WEIGHT_CLASS_NORMAL + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + block_chance = 50 + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + +/obj/item/katana/cursed + slot_flags = null + +/obj/item/katana/suicide_act(mob/user) + user.visible_message("[user] is slitting [user.p_their()] stomach open with [src]! It looks like [user.p_theyre()] trying to commit seppuku.") + return BRUTELOSS + +/obj/item/harpoon + name = "harpoon" + sharp = 1 + desc = "Tharr she blows!" + icon_state = "harpoon" + item_state = "harpoon" + force = 20 + throwforce = 15 + w_class = WEIGHT_CLASS_NORMAL + attack_verb = list("jabbed","stabbed","ripped") + +/obj/item/wirerod + name = "Wired rod" + desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit." + icon_state = "wiredrod" + item_state = "rods" + flags = CONDUCT + force = 9 + throwforce = 10 + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=1150, MAT_GLASS=75) + attack_verb = list("hit", "bludgeoned", "whacked", "bonked") + +/obj/item/wirerod/attackby(obj/item/I, mob/user, params) + ..() + if(istype(I, /obj/item/shard)) + var/obj/item/twohanded/spear/S = new /obj/item/twohanded/spear + if(istype(I, /obj/item/shard/plasma)) + S.force_wielded = 19 + S.force_unwielded = 11 + S.throwforce = 21 + S.icon_prefix = "spearplasma" + S.update_icon() + if(!remove_item_from_storage(user)) + user.unEquip(src) + user.unEquip(I) + + user.put_in_hands(S) + to_chat(user, "You fasten the glass shard to the top of the rod with the cable.") + qdel(I) + qdel(src) + + else if(istype(I, /obj/item/assembly/igniter) && !(I.flags & NODROP)) + var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod + + if(!remove_item_from_storage(user)) + user.unEquip(src) + user.unEquip(I) + + user.put_in_hands(P) + to_chat(user, "You fasten [I] to the top of the rod with the cable.") + qdel(I) + qdel(src) + +/obj/item/throwing_star + name = "throwing star" + desc = "An ancient weapon still used to this day due to it's ease of lodging itself into victim's body parts" + icon_state = "throwingstar" + item_state = "eshield0" + force = 2 + throwforce = 20 //This is never used on mobs since this has a 100% embed chance. + throw_speed = 4 + embedded_pain_multiplier = 4 + w_class = WEIGHT_CLASS_SMALL + embed_chance = 100 + embedded_fall_chance = 0 //Hahaha! + sharp = 1 + materials = list(MAT_METAL=500, MAT_GLASS=500) + resistance_flags = FIRE_PROOF + +/obj/item/spear/kidan + icon_state = "kidanspear" + name = "Kidan spear" + desc = "A one-handed spear brought over from the Kidan homeworld." + icon_state = "kidanspear" + item_state = "kidanspear" + force = 10 + throwforce = 15 + +/obj/item/melee/baseball_bat + name = "baseball bat" + desc = "There ain't a skull in the league that can withstand a swatter." + icon = 'icons/obj/items.dmi' + icon_state = "baseball_bat" + item_state = "baseball_bat" + var/deflectmode = FALSE // deflect small/medium thrown objects + var/lastdeflect + force = 10 + throwforce = 12 + attack_verb = list("beat", "smacked") + w_class = WEIGHT_CLASS_HUGE + var/homerun_ready = 0 + var/homerun_able = 0 + +/obj/item/melee/baseball_bat/homerun + name = "home run bat" + desc = "This thing looks dangerous... Dangerously good at baseball, that is." + homerun_able = 1 + +/obj/item/melee/baseball_bat/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + . = ..() + if(!isitem(hitby) || attack_type != THROWN_PROJECTILE_ATTACK) + return FALSE + var/obj/item/I = hitby + if(I.w_class <= WEIGHT_CLASS_NORMAL || istype(I, /obj/item/beach_ball)) // baseball bat deflecting + if(deflectmode) + if(prob(10)) + visible_message("[owner] Deflects [I] directly back at the thrower! It's a home run!", "You deflect the [I] directly back at the thrower! It's a home run!") + playsound(get_turf(owner), 'sound/weapons/homerun.ogg', 100, 1) + do_attack_animation(I, ATTACK_EFFECT_DISARM) + I.throw_at(I.thrownby, 20, 20, owner) + deflectmode = FALSE + if(!istype(I, /obj/item/beach_ball)) + lastdeflect = world.time + 3000 + return TRUE + else if(prob(30)) + visible_message("[owner] swings! And [p_they()] miss[p_es()]! How embarassing.", "You swing! You miss! Oh no!") + playsound(get_turf(owner), 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + do_attack_animation(get_step(owner, pick(GLOB.alldirs)), ATTACK_EFFECT_DISARM) + deflectmode = FALSE + if(!istype(I, /obj/item/beach_ball)) + lastdeflect = world.time + 3000 + return FALSE + else + visible_message("[owner] swings and deflects [I]!", "You swing and deflect the [I]!") + playsound(get_turf(owner), 'sound/weapons/baseball_hit.ogg', 50, 1, -1) + do_attack_animation(I, ATTACK_EFFECT_DISARM) + I.throw_at(get_edge_target_turf(owner, pick(GLOB.cardinal)), rand(8,10), 14, owner) + deflectmode = FALSE + if(!istype(I, /obj/item/beach_ball)) + lastdeflect = world.time + 3000 + return TRUE + +/obj/item/melee/baseball_bat/attack_self(mob/user) + if(!homerun_able) + if(!deflectmode && world.time >= lastdeflect) + to_chat(user, "You prepare to deflect objects thrown at you. You cannot attack during this time.") + deflectmode = TRUE + else if(deflectmode && world.time >= lastdeflect) + to_chat(user, "You no longer deflect objects thrown at you. You can attack during this time") + deflectmode = FALSE + else + to_chat(user, "You need to wait until you can deflect again. The ability will be ready in [time2text(lastdeflect - world.time, "m:ss")]") + return ..() + if(homerun_ready) + to_chat(user, "You're already ready to do a home run!") + return ..() + to_chat(user, "You begin gathering strength...") + playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1) + if(do_after(user, 90, target = user)) + to_chat(user, "You gather power! Time for a home run!") + homerun_ready = 1 + ..() + +/obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) + if(deflectmode) + to_chat(user, "You cannot attack in deflect mode!") + return + . = ..() + var/atom/throw_target = get_edge_target_turf(target, user.dir) + if(homerun_ready) + user.visible_message("It's a home run!") + target.throw_at(throw_target, rand(8,10), 14, user) + target.ex_act(2) + playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1) + homerun_ready = 0 + return + else if(!target.anchored) + target.throw_at(throw_target, rand(1,2), 7, user) + +/obj/item/melee/baseball_bat/ablative + name = "metal baseball bat" + desc = "This bat is made of highly reflective, highly armored material." + icon_state = "baseball_bat_metal" + item_state = "baseball_bat_metal" + force = 12 + throwforce = 15 + +/obj/item/melee/baseball_bat/ablative/IsReflect()//some day this will reflect thrown items instead of lasers + var/picksound = rand(1,2) + var/turf = get_turf(src) + if(picksound == 1) + playsound(turf, 'sound/weapons/effects/batreflect1.ogg', 50, 1) + if(picksound == 2) + playsound(turf, 'sound/weapons/effects/batreflect2.ogg', 50, 1) + return 1 diff --git a/code/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm index 9323412e830e..e1464e47e489 100644 --- a/code/game/objects/obj_defense.dm +++ b/code/game/objects/obj_defense.dm @@ -205,7 +205,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e if(!(resistance_flags & ON_FIRE) && (resistance_flags & FLAMMABLE) && !(resistance_flags & FIRE_PROOF)) resistance_flags |= ON_FIRE SSfires.processing[src] = src - add_overlay(fire_overlay, TRUE) + add_overlay(GLOB.fire_overlay, TRUE) return 1 ///called when the obj is destroyed by fire @@ -218,7 +218,7 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e /obj/proc/extinguish() if(resistance_flags & ON_FIRE) resistance_flags &= ~ON_FIRE - cut_overlay(fire_overlay, TRUE) + cut_overlay(GLOB.fire_overlay, TRUE) SSfires.processing -= src ///Called when the obj is hit by a tesla bolt. @@ -271,4 +271,4 @@ GLOBAL_DATUM_INIT(acid_overlay, /mutable_appearance, mutable_appearance('icons/e ///returns how much the object blocks an explosion. Used by subtypes. /obj/proc/GetExplosionBlock() - CRASH("Unimplemented GetExplosionBlock()") \ No newline at end of file + CRASH("Unimplemented GetExplosionBlock()") diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index e30c8c5c4c2a..112925515a07 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -44,7 +44,7 @@ else T.add_blueprints_preround(src) -/obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = default_state) +/obj/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = GLOB.default_state) // Calling Topic without a corresponding window open causes runtime errors if(!nowindow && ..()) return 1 diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index 22479653e698..59cc3898088b 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -102,4 +102,4 @@ prob(2);/obj/random/toolbox,\ prob(2);/obj/item/storage/belt/utility,\ prob(5);/obj/random/tool,\ - prob(3);/obj/item/stack/tape_roll) \ No newline at end of file + prob(3);/obj/item/stack/tape_roll) diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 6849e6d07d33..663f210df3ec 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -1,162 +1,162 @@ -/obj/structure - icon = 'icons/obj/structures.dmi' - pressure_resistance = 8 - max_integrity = 300 - var/climbable - var/mob/climber - var/broken = FALSE - -/obj/structure/New() - if (!armor) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - ..() - if(smooth) - if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) - queue_smooth(src) - queue_smooth_neighbors(src) - icon_state = "" - if(climbable) - verbs += /obj/structure/proc/climb_on - if(SSticker) - cameranet.updateVisibility(src) - -/obj/structure/Destroy() - if(SSticker) - cameranet.updateVisibility(src) - if(smooth) - var/turf/T = get_turf(src) - spawn(0) - queue_smooth_neighbors(T) - return ..() - -/obj/structure/proc/climb_on() - - set name = "Climb structure" - set desc = "Climbs onto a structure." - set category = null - set src in oview(1) - - do_climb(usr) - -/obj/structure/MouseDrop_T(var/atom/movable/C, mob/user as mob) - if(..()) - return - if(C == user) - do_climb(user) - -/obj/structure/proc/density_check() - for(var/obj/O in orange(0, src)) - if(O.density && !istype(O, /obj/machinery/door/window)) //Ignores windoors, as those already block climbing, otherwise a windoor on the opposite side of a table would prevent climbing. - return O - var/turf/T = get_turf(src) - if(T.density) - return T - return null - -/obj/structure/proc/do_climb(var/mob/living/user) - if(!can_touch(user) || !climbable) - return - var/blocking_object = density_check() - if(blocking_object) - to_chat(user, "You cannot climb [src], as it is blocked by \a [blocking_object]!") - return - - var/turf/T = src.loc - if(!T || !istype(T)) return - - usr.visible_message("[user] starts climbing onto \the [src]!") - climber = user - if(!do_after(user, 50, target = src)) - climber = null - return - - if(!can_touch(user) || !climbable) - climber = null - return - - usr.loc = get_turf(src) - if(get_turf(user) == get_turf(src)) - usr.visible_message("[user] climbs onto \the [src]!") - - climber = null - -/obj/structure/proc/structure_shaken() - - for(var/mob/living/M in get_turf(src)) - - if(M.lying) return //No spamming this on people. - - M.Weaken(5) - to_chat(M, "You topple as \the [src] moves under you!") - - if(prob(25)) - - var/damage = rand(15,30) - var/mob/living/carbon/human/H = M - if(!istype(H)) - to_chat(H, "You land heavily!") - M.adjustBruteLoss(damage) - return - - var/obj/item/organ/external/affecting - - switch(pick(list("ankle","wrist","head","knee","elbow"))) - if("ankle") - affecting = H.get_organ(pick("l_foot", "r_foot")) - if("knee") - affecting = H.get_organ(pick("l_leg", "r_leg")) - if("wrist") - affecting = H.get_organ(pick("l_hand", "r_hand")) - if("elbow") - affecting = H.get_organ(pick("l_arm", "r_arm")) - if("head") - affecting = H.get_organ("head") - - if(affecting) - to_chat(M, "You land heavily on your [affecting.name]!") - affecting.receive_damage(damage, 0) - if(affecting.parent) - affecting.parent.add_autopsy_data("Misadventure", damage) - else - to_chat(H, "You land heavily!") - H.adjustBruteLoss(damage) - - H.UpdateDamageIcon() - return - -/obj/structure/proc/can_touch(var/mob/user) - if(!user) - return 0 - if(!Adjacent(user)) - return 0 - if(user.restrained() || user.buckled) - to_chat(user, "You need your hands and legs free for this.") - return 0 - if(user.stat || user.paralysis || user.sleeping || user.lying || user.IsWeakened()) - return 0 - if(issilicon(user)) - to_chat(user, "You need hands for this.") - return 0 - return 1 - -/obj/structure/examine(mob/user) - . = ..() - if(!(resistance_flags & INDESTRUCTIBLE)) - if(resistance_flags & ON_FIRE) - . += "It's on fire!" - if(broken) - . += "It appears to be broken." - var/examine_status = examine_status(user) - if(examine_status) - . += examine_status - -/obj/structure/proc/examine_status(mob/user) //An overridable proc, mostly for falsewalls. - var/healthpercent = (obj_integrity/max_integrity) * 100 - switch(healthpercent) - if(50 to 99) - return "It looks slightly damaged." - if(25 to 50) - return "It appears heavily damaged." - if(0 to 25) - if(!broken) - return "It's falling apart!" +/obj/structure + icon = 'icons/obj/structures.dmi' + pressure_resistance = 8 + max_integrity = 300 + var/climbable + var/mob/climber + var/broken = FALSE + +/obj/structure/New() + if (!armor) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + ..() + if(smooth) + if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) + queue_smooth(src) + queue_smooth_neighbors(src) + icon_state = "" + if(climbable) + verbs += /obj/structure/proc/climb_on + if(SSticker) + GLOB.cameranet.updateVisibility(src) + +/obj/structure/Destroy() + if(SSticker) + GLOB.cameranet.updateVisibility(src) + if(smooth) + var/turf/T = get_turf(src) + spawn(0) + queue_smooth_neighbors(T) + return ..() + +/obj/structure/proc/climb_on() + + set name = "Climb structure" + set desc = "Climbs onto a structure." + set category = null + set src in oview(1) + + do_climb(usr) + +/obj/structure/MouseDrop_T(var/atom/movable/C, mob/user as mob) + if(..()) + return + if(C == user) + do_climb(user) + +/obj/structure/proc/density_check() + for(var/obj/O in orange(0, src)) + if(O.density && !istype(O, /obj/machinery/door/window)) //Ignores windoors, as those already block climbing, otherwise a windoor on the opposite side of a table would prevent climbing. + return O + var/turf/T = get_turf(src) + if(T.density) + return T + return null + +/obj/structure/proc/do_climb(var/mob/living/user) + if(!can_touch(user) || !climbable) + return + var/blocking_object = density_check() + if(blocking_object) + to_chat(user, "You cannot climb [src], as it is blocked by \a [blocking_object]!") + return + + var/turf/T = src.loc + if(!T || !istype(T)) return + + usr.visible_message("[user] starts climbing onto \the [src]!") + climber = user + if(!do_after(user, 50, target = src)) + climber = null + return + + if(!can_touch(user) || !climbable) + climber = null + return + + usr.loc = get_turf(src) + if(get_turf(user) == get_turf(src)) + usr.visible_message("[user] climbs onto \the [src]!") + + climber = null + +/obj/structure/proc/structure_shaken() + + for(var/mob/living/M in get_turf(src)) + + if(M.lying) return //No spamming this on people. + + M.Weaken(5) + to_chat(M, "You topple as \the [src] moves under you!") + + if(prob(25)) + + var/damage = rand(15,30) + var/mob/living/carbon/human/H = M + if(!istype(H)) + to_chat(H, "You land heavily!") + M.adjustBruteLoss(damage) + return + + var/obj/item/organ/external/affecting + + switch(pick(list("ankle","wrist","head","knee","elbow"))) + if("ankle") + affecting = H.get_organ(pick("l_foot", "r_foot")) + if("knee") + affecting = H.get_organ(pick("l_leg", "r_leg")) + if("wrist") + affecting = H.get_organ(pick("l_hand", "r_hand")) + if("elbow") + affecting = H.get_organ(pick("l_arm", "r_arm")) + if("head") + affecting = H.get_organ("head") + + if(affecting) + to_chat(M, "You land heavily on your [affecting.name]!") + affecting.receive_damage(damage, 0) + if(affecting.parent) + affecting.parent.add_autopsy_data("Misadventure", damage) + else + to_chat(H, "You land heavily!") + H.adjustBruteLoss(damage) + + H.UpdateDamageIcon() + return + +/obj/structure/proc/can_touch(var/mob/user) + if(!user) + return 0 + if(!Adjacent(user)) + return 0 + if(user.restrained() || user.buckled) + to_chat(user, "You need your hands and legs free for this.") + return 0 + if(user.stat || user.paralysis || user.sleeping || user.lying || user.IsWeakened()) + return 0 + if(issilicon(user)) + to_chat(user, "You need hands for this.") + return 0 + return 1 + +/obj/structure/examine(mob/user) + . = ..() + if(!(resistance_flags & INDESTRUCTIBLE)) + if(resistance_flags & ON_FIRE) + . += "It's on fire!" + if(broken) + . += "It appears to be broken." + var/examine_status = examine_status(user) + if(examine_status) + . += examine_status + +/obj/structure/proc/examine_status(mob/user) //An overridable proc, mostly for falsewalls. + var/healthpercent = (obj_integrity/max_integrity) * 100 + switch(healthpercent) + if(50 to 99) + return "It looks slightly damaged." + if(25 to 50) + return "It appears heavily damaged." + if(0 to 25) + if(!broken) + return "It's falling apart!" diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm index 26ab24b609c1..1de93f4d583b 100644 --- a/code/game/objects/structures/aliens.dm +++ b/code/game/objects/structures/aliens.dm @@ -1,334 +1,334 @@ -/* Alien shit! - * Contains: - * structure/alien - * Resin - * Weeds - * Egg - */ - -#define WEED_NORTH_EDGING "north" -#define WEED_SOUTH_EDGING "south" -#define WEED_EAST_EDGING "east" -#define WEED_WEST_EDGING "west" - -/obj/structure/alien - icon = 'icons/mob/alien.dmi' - max_integrity = 100 - -/obj/structure/alien/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(damage_flag == "melee") - switch(damage_type) - if(BRUTE) - damage_amount *= 0.25 - if(BURN) - damage_amount *= 2 - . = ..() - -/obj/structure/alien/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - if(damage_amount) - playsound(loc, 'sound/items/welder.ogg', 100, TRUE) - -/* - * Resin - */ -/obj/structure/alien/resin - name = "resin" - desc = "Looks like some kind of thick resin." - icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi' - icon_state = "resin" - density = TRUE - opacity = TRUE - anchored = TRUE - canSmoothWith = list(/obj/structure/alien/resin) - max_integrity = 200 - smooth = SMOOTH_TRUE - var/resintype = null - -/obj/structure/alien/resin/Initialize() - air_update_turf(1) - ..() - -/obj/structure/alien/resin/Destroy() - var/turf/T = get_turf(src) - . = ..() - T.air_update_turf(TRUE) - -/obj/structure/alien/resin/Move() - var/turf/T = loc - ..() - move_update_air(T) - -/obj/structure/alien/resin/CanAtmosPass() - return !density - -/obj/structure/alien/resin/wall - name = "resin wall" - desc = "Thick resin solidified into a wall." - icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi' - icon_state = "wall0" //same as resin, but consistency ho! - resintype = "wall" - canSmoothWith = list(/obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane) - -/obj/structure/alien/resin/wall/BlockSuperconductivity() - return 1 - -/obj/structure/alien/resin/wall/shadowling //For chrysalis - name = "chrysalis wall" - desc = "Some sort of purple substance in an egglike shape. It pulses and throbs from within and seems impenetrable." - max_integrity = INFINITY - -/obj/structure/alien/resin/membrane - name = "resin membrane" - desc = "Resin just thin enough to let light pass through." - icon = 'icons/obj/smooth_structures/alien/resin_membrane.dmi' - icon_state = "membrane0" - opacity = 0 - max_integrity = 160 - resintype = "membrane" - canSmoothWith = list(/obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane) - -/obj/structure/alien/resin/CanPass(atom/movable/mover, turf/target) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return !opacity - return !density - - -/* - * Weeds - */ - -#define NODERANGE 3 - -/obj/structure/alien/weeds - gender = PLURAL - name = "resin floor" - desc = "A thick resin surface covers the floor." - anchored = TRUE - density = FALSE - layer = TURF_LAYER - plane = FLOOR_PLANE - icon_state = "weeds" - max_integrity = 15 - var/obj/structure/alien/weeds/node/linked_node = null - var/static/list/weedImageCache - - -/obj/structure/alien/weeds/New(pos, node) - ..() - linked_node = node - if(istype(loc, /turf/space)) - qdel(src) - return - if(icon_state == "weeds") - icon_state = pick("weeds", "weeds1", "weeds2") - fullUpdateWeedOverlays() - spawn(rand(150, 200)) - if(src) - Life() - -/obj/structure/alien/weeds/Destroy() - var/turf/T = loc - for(var/obj/structure/alien/weeds/W in range(1,T)) - W.updateWeedOverlays() - linked_node = null - return ..() - -/obj/structure/alien/weeds/proc/Life() - set background = BACKGROUND_ENABLED - var/turf/U = get_turf(src) - - if(istype(U, /turf/space)) - qdel(src) - return - - if(!linked_node || get_dist(linked_node, src) > linked_node.node_range) - return - - for(var/turf/T in U.GetAtmosAdjacentTurfs()) - - if(locate(/obj/structure/alien/weeds) in T || istype(T, /turf/space)) - continue - - new /obj/structure/alien/weeds(T, linked_node) - -/obj/structure/alien/weeds/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 300) - take_damage(5, BURN, 0, 0) - -/obj/structure/alien/weeds/proc/updateWeedOverlays() - - overlays.Cut() - - if(!weedImageCache || !weedImageCache.len) - weedImageCache = list() - weedImageCache.len = 4 - weedImageCache[WEED_NORTH_EDGING] = image('icons/mob/alien.dmi', "weeds_side_n", layer=2.11, pixel_y = -32) - weedImageCache[WEED_SOUTH_EDGING] = image('icons/mob/alien.dmi', "weeds_side_s", layer=2.11, pixel_y = 32) - weedImageCache[WEED_EAST_EDGING] = image('icons/mob/alien.dmi', "weeds_side_e", layer=2.11, pixel_x = -32) - weedImageCache[WEED_WEST_EDGING] = image('icons/mob/alien.dmi', "weeds_side_w", layer=2.11, pixel_x = 32) - - var/turf/N = get_step(src, NORTH) - var/turf/S = get_step(src, SOUTH) - var/turf/E = get_step(src, EAST) - var/turf/W = get_step(src, WEST) - if(!locate(/obj/structure/alien) in N.contents) - if(istype(N, /turf/simulated/floor)) - overlays += weedImageCache[WEED_SOUTH_EDGING] - if(!locate(/obj/structure/alien) in S.contents) - if(istype(S, /turf/simulated/floor)) - overlays += weedImageCache[WEED_NORTH_EDGING] - if(!locate(/obj/structure/alien) in E.contents) - if(istype(E, /turf/simulated/floor)) - overlays += weedImageCache[WEED_WEST_EDGING] - if(!locate(/obj/structure/alien) in W.contents) - if(istype(W, /turf/simulated/floor)) - overlays += weedImageCache[WEED_EAST_EDGING] - - -/obj/structure/alien/weeds/proc/fullUpdateWeedOverlays() - for(var/obj/structure/alien/weeds/W in range(1,src)) - W.updateWeedOverlays() - -//Weed nodes -/obj/structure/alien/weeds/node - name = "glowing resin" - desc = "Blue bioluminescence shines from beneath the surface." - icon_state = "weednode" - light_range = 1 - var/node_range = NODERANGE - - -/obj/structure/alien/weeds/node/New() - ..(loc, src) - -#undef NODERANGE - - -/* - * Egg - */ - -//for the status var -#define BURST 0 -#define BURSTING 1 -#define GROWING 2 -#define GROWN 3 -#define MIN_GROWTH_TIME 1800 //time it takes to grow a hugger -#define MAX_GROWTH_TIME 3000 - -/obj/structure/alien/egg - name = "egg" - desc = "A large mottled egg." - icon_state = "egg_growing" - density = FALSE - anchored = TRUE - max_integrity = 100 - integrity_failure = 5 - var/status = GROWING //can be GROWING, GROWN or BURST; all mutually exclusive - layer = MOB_LAYER - -/obj/structure/alien/egg/grown - status = GROWN - icon_state = "egg" - -/obj/structure/alien/egg/burst - status = BURST - icon_state = "egg_hatched" - -/obj/structure/alien/egg/New() - new /obj/item/clothing/mask/facehugger(src) - ..() - if(status == BURST) - obj_integrity = integrity_failure - else if(status != GROWN) - spawn(rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME)) - Grow() - -/obj/structure/alien/egg/attack_alien(mob/living/carbon/alien/user) - return attack_hand(user) - -/obj/structure/alien/egg/attack_hand(mob/living/user) - if(user.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) - switch(status) - if(BURST) - to_chat(user, "You clear the hatched egg.") - playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) - qdel(src) - return - if(GROWING) - to_chat(user, "The child is not developed yet.") - return - if(GROWN) - to_chat(user, "You retrieve the child.") - Burst(0) - return - else - to_chat(user, "It feels slimy.") - user.changeNext_move(CLICK_CD_MELEE) - - -/obj/structure/alien/egg/proc/GetFacehugger() - return locate(/obj/item/clothing/mask/facehugger) in contents - -/obj/structure/alien/egg/proc/Grow() - icon_state = "egg" - status = GROWN - -/obj/structure/alien/egg/proc/Burst(kill = TRUE) //drops and kills the hugger if any is remaining - if(status == GROWN || status == GROWING) - icon_state = "egg_hatched" - flick("egg_opening", src) - status = BURSTING - spawn(15) - status = BURST - var/obj/item/clothing/mask/facehugger/child = GetFacehugger() - if(child) - child.loc = get_turf(src) - if(kill && istype(child)) - child.Die() - else - for(var/mob/M in range(1,src)) - if(CanHug(M)) - child.Attach(M) - break - -/obj/structure/alien/egg/obj_break(damage_flag) - if(!(flags & NODECONSTRUCT)) - if(status != BURST) - Burst(kill = TRUE) - -/obj/structure/alien/egg/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 500) - take_damage(5, BURN, 0, 0) - -/obj/structure/alien/egg/HasProximity(atom/movable/AM) - if(status == GROWN) - if(!CanHug(AM)) - return - - var/mob/living/carbon/C = AM - if(C.stat == CONSCIOUS && C.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo)) - return - - Burst(0) - -#undef BURST -#undef BURSTING -#undef GROWING -#undef GROWN -#undef MIN_GROWTH_TIME -#undef MAX_GROWTH_TIME - -#undef WEED_NORTH_EDGING -#undef WEED_SOUTH_EDGING -#undef WEED_EAST_EDGING -#undef WEED_WEST_EDGING +/* Alien shit! + * Contains: + * structure/alien + * Resin + * Weeds + * Egg + */ + +#define WEED_NORTH_EDGING "north" +#define WEED_SOUTH_EDGING "south" +#define WEED_EAST_EDGING "east" +#define WEED_WEST_EDGING "west" + +/obj/structure/alien + icon = 'icons/mob/alien.dmi' + max_integrity = 100 + +/obj/structure/alien/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(damage_flag == "melee") + switch(damage_type) + if(BRUTE) + damage_amount *= 0.25 + if(BURN) + damage_amount *= 2 + . = ..() + +/obj/structure/alien/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + if(damage_amount) + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) + +/* + * Resin + */ +/obj/structure/alien/resin + name = "resin" + desc = "Looks like some kind of thick resin." + icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi' + icon_state = "resin" + density = TRUE + opacity = TRUE + anchored = TRUE + canSmoothWith = list(/obj/structure/alien/resin) + max_integrity = 200 + smooth = SMOOTH_TRUE + var/resintype = null + +/obj/structure/alien/resin/Initialize() + air_update_turf(1) + ..() + +/obj/structure/alien/resin/Destroy() + var/turf/T = get_turf(src) + . = ..() + T.air_update_turf(TRUE) + +/obj/structure/alien/resin/Move() + var/turf/T = loc + ..() + move_update_air(T) + +/obj/structure/alien/resin/CanAtmosPass() + return !density + +/obj/structure/alien/resin/wall + name = "resin wall" + desc = "Thick resin solidified into a wall." + icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi' + icon_state = "wall0" //same as resin, but consistency ho! + resintype = "wall" + canSmoothWith = list(/obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane) + +/obj/structure/alien/resin/wall/BlockSuperconductivity() + return 1 + +/obj/structure/alien/resin/wall/shadowling //For chrysalis + name = "chrysalis wall" + desc = "Some sort of purple substance in an egglike shape. It pulses and throbs from within and seems impenetrable." + max_integrity = INFINITY + +/obj/structure/alien/resin/membrane + name = "resin membrane" + desc = "Resin just thin enough to let light pass through." + icon = 'icons/obj/smooth_structures/alien/resin_membrane.dmi' + icon_state = "membrane0" + opacity = 0 + max_integrity = 160 + resintype = "membrane" + canSmoothWith = list(/obj/structure/alien/resin/wall, /obj/structure/alien/resin/membrane) + +/obj/structure/alien/resin/CanPass(atom/movable/mover, turf/target) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return !opacity + return !density + + +/* + * Weeds + */ + +#define NODERANGE 3 + +/obj/structure/alien/weeds + gender = PLURAL + name = "resin floor" + desc = "A thick resin surface covers the floor." + anchored = TRUE + density = FALSE + layer = TURF_LAYER + plane = FLOOR_PLANE + icon_state = "weeds" + max_integrity = 15 + var/obj/structure/alien/weeds/node/linked_node = null + var/static/list/weedImageCache + + +/obj/structure/alien/weeds/New(pos, node) + ..() + linked_node = node + if(istype(loc, /turf/space)) + qdel(src) + return + if(icon_state == "weeds") + icon_state = pick("weeds", "weeds1", "weeds2") + fullUpdateWeedOverlays() + spawn(rand(150, 200)) + if(src) + Life() + +/obj/structure/alien/weeds/Destroy() + var/turf/T = loc + for(var/obj/structure/alien/weeds/W in range(1,T)) + W.updateWeedOverlays() + linked_node = null + return ..() + +/obj/structure/alien/weeds/proc/Life() + set background = BACKGROUND_ENABLED + var/turf/U = get_turf(src) + + if(istype(U, /turf/space)) + qdel(src) + return + + if(!linked_node || get_dist(linked_node, src) > linked_node.node_range) + return + + for(var/turf/T in U.GetAtmosAdjacentTurfs()) + + if(locate(/obj/structure/alien/weeds) in T || istype(T, /turf/space)) + continue + + new /obj/structure/alien/weeds(T, linked_node) + +/obj/structure/alien/weeds/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 300) + take_damage(5, BURN, 0, 0) + +/obj/structure/alien/weeds/proc/updateWeedOverlays() + + overlays.Cut() + + if(!weedImageCache || !weedImageCache.len) + weedImageCache = list() + weedImageCache.len = 4 + weedImageCache[WEED_NORTH_EDGING] = image('icons/mob/alien.dmi', "weeds_side_n", layer=2.11, pixel_y = -32) + weedImageCache[WEED_SOUTH_EDGING] = image('icons/mob/alien.dmi', "weeds_side_s", layer=2.11, pixel_y = 32) + weedImageCache[WEED_EAST_EDGING] = image('icons/mob/alien.dmi', "weeds_side_e", layer=2.11, pixel_x = -32) + weedImageCache[WEED_WEST_EDGING] = image('icons/mob/alien.dmi', "weeds_side_w", layer=2.11, pixel_x = 32) + + var/turf/N = get_step(src, NORTH) + var/turf/S = get_step(src, SOUTH) + var/turf/E = get_step(src, EAST) + var/turf/W = get_step(src, WEST) + if(!locate(/obj/structure/alien) in N.contents) + if(istype(N, /turf/simulated/floor)) + overlays += weedImageCache[WEED_SOUTH_EDGING] + if(!locate(/obj/structure/alien) in S.contents) + if(istype(S, /turf/simulated/floor)) + overlays += weedImageCache[WEED_NORTH_EDGING] + if(!locate(/obj/structure/alien) in E.contents) + if(istype(E, /turf/simulated/floor)) + overlays += weedImageCache[WEED_WEST_EDGING] + if(!locate(/obj/structure/alien) in W.contents) + if(istype(W, /turf/simulated/floor)) + overlays += weedImageCache[WEED_EAST_EDGING] + + +/obj/structure/alien/weeds/proc/fullUpdateWeedOverlays() + for(var/obj/structure/alien/weeds/W in range(1,src)) + W.updateWeedOverlays() + +//Weed nodes +/obj/structure/alien/weeds/node + name = "glowing resin" + desc = "Blue bioluminescence shines from beneath the surface." + icon_state = "weednode" + light_range = 1 + var/node_range = NODERANGE + + +/obj/structure/alien/weeds/node/New() + ..(loc, src) + +#undef NODERANGE + + +/* + * Egg + */ + +//for the status var +#define BURST 0 +#define BURSTING 1 +#define GROWING 2 +#define GROWN 3 +#define MIN_GROWTH_TIME 1800 //time it takes to grow a hugger +#define MAX_GROWTH_TIME 3000 + +/obj/structure/alien/egg + name = "egg" + desc = "A large mottled egg." + icon_state = "egg_growing" + density = FALSE + anchored = TRUE + max_integrity = 100 + integrity_failure = 5 + var/status = GROWING //can be GROWING, GROWN or BURST; all mutually exclusive + layer = MOB_LAYER + +/obj/structure/alien/egg/grown + status = GROWN + icon_state = "egg" + +/obj/structure/alien/egg/burst + status = BURST + icon_state = "egg_hatched" + +/obj/structure/alien/egg/New() + new /obj/item/clothing/mask/facehugger(src) + ..() + if(status == BURST) + obj_integrity = integrity_failure + else if(status != GROWN) + spawn(rand(MIN_GROWTH_TIME, MAX_GROWTH_TIME)) + Grow() + +/obj/structure/alien/egg/attack_alien(mob/living/carbon/alien/user) + return attack_hand(user) + +/obj/structure/alien/egg/attack_hand(mob/living/user) + if(user.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) + switch(status) + if(BURST) + to_chat(user, "You clear the hatched egg.") + playsound(loc, 'sound/effects/attackblob.ogg', 100, 1) + qdel(src) + return + if(GROWING) + to_chat(user, "The child is not developed yet.") + return + if(GROWN) + to_chat(user, "You retrieve the child.") + Burst(0) + return + else + to_chat(user, "It feels slimy.") + user.changeNext_move(CLICK_CD_MELEE) + + +/obj/structure/alien/egg/proc/GetFacehugger() + return locate(/obj/item/clothing/mask/facehugger) in contents + +/obj/structure/alien/egg/proc/Grow() + icon_state = "egg" + status = GROWN + +/obj/structure/alien/egg/proc/Burst(kill = TRUE) //drops and kills the hugger if any is remaining + if(status == GROWN || status == GROWING) + icon_state = "egg_hatched" + flick("egg_opening", src) + status = BURSTING + spawn(15) + status = BURST + var/obj/item/clothing/mask/facehugger/child = GetFacehugger() + if(child) + child.loc = get_turf(src) + if(kill && istype(child)) + child.Die() + else + for(var/mob/M in range(1,src)) + if(CanHug(M)) + child.Attach(M) + break + +/obj/structure/alien/egg/obj_break(damage_flag) + if(!(flags & NODECONSTRUCT)) + if(status != BURST) + Burst(kill = TRUE) + +/obj/structure/alien/egg/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 500) + take_damage(5, BURN, 0, 0) + +/obj/structure/alien/egg/HasProximity(atom/movable/AM) + if(status == GROWN) + if(!CanHug(AM)) + return + + var/mob/living/carbon/C = AM + if(C.stat == CONSCIOUS && C.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo)) + return + + Burst(0) + +#undef BURST +#undef BURSTING +#undef GROWING +#undef GROWN +#undef MIN_GROWTH_TIME +#undef MAX_GROWTH_TIME + +#undef WEED_NORTH_EDGING +#undef WEED_SOUTH_EDGING +#undef WEED_EAST_EDGING +#undef WEED_WEST_EDGING diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 8945819e3aac..9c517f39c699 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -1,332 +1,332 @@ -/* -CONTAINS: -BEDSHEETS -LINEN BINS -*/ - -/obj/item/bedsheet - name = "bedsheet" - desc = "A surprisingly soft linen bedsheet." - icon = 'icons/obj/items.dmi' - icon_state = "sheet" - item_state = "bedsheet" - layer = 4.0 - throwforce = 1 - throw_speed = 1 - throw_range = 2 - w_class = WEIGHT_CLASS_TINY - item_color = "white" - resistance_flags = FLAMMABLE - slot_flags = SLOT_BACK - - dog_fashion = /datum/dog_fashion/head/ghost - var/list/dream_messages = list("white") - var/list/nightmare_messages = list("black") - var/comfort = 0.5 - - - -/obj/item/bedsheet/attack_self(mob/user as mob) - user.drop_item() - if(layer == initial(layer)) - layer = 5 - else - layer = initial(layer) - add_fingerprint(user) - return - -/obj/item/bedsheet/attackby(obj/item/I, mob/user, params) - if(I.sharp) - var/obj/item/stack/sheet/cloth/C = new (get_turf(src), 3) - transfer_fingerprints_to(C) - C.add_fingerprint(user) - qdel(src) - to_chat(user, "You tear [src] up.") - else - return ..() - -/obj/item/bedsheet/blue - icon_state = "sheetblue" - item_color = "blue" - dream_messages = list("blue") - nightmare_messages = list("vox blood") - -/obj/item/bedsheet/green - icon_state = "sheetgreen" - item_color = "green" - dream_messages = list("green") - nightmare_messages = list("unathi flesh") - -/obj/item/bedsheet/orange - icon_state = "sheetorange" - item_color = "orange" - dream_messages = list("orange") - nightmare_messages = list("exploding fruit") - -/obj/item/bedsheet/purple - icon_state = "sheetpurple" - item_color = "purple" - dream_messages = list("purple") - nightmare_messages = list("Grey blood") - -/obj/item/bedsheet/patriot - name = "patriotic bedsheet" - desc = "You've never felt more free than when sleeping on this." - icon_state = "sheetUSA" - item_color = "sheetUSA" - dream_messages = list("America", "freedom", "fireworks", "bald eagles") - nightmare_messages = list("communism") - -/obj/item/bedsheet/rainbow - name = "rainbow bedsheet" - desc = "A multi_colored blanket. It's actually several different sheets cut up and sewn together." - icon_state = "sheetrainbow" - item_color = "rainbow" - dream_messages = list("red", "orange", "yellow", "green", "blue", "purple", "a rainbow") - nightmare_messages = list("green", "a cluwne", "fLoOr ClUwNe") - -/obj/item/bedsheet/red - icon_state = "sheetred" - item_color = "red" - dream_messages = list("red") - nightmare_messages = list("gibs") - -/obj/item/bedsheet/yellow - icon_state = "sheetyellow" - item_color = "yellow" - dream_messages = list("yellow") - nightmare_messages = list("locker full of banana peels") - -/obj/item/bedsheet/black - icon_state = "sheetblack" - item_color = "sheetblack" - dream_messages = list("black") - nightmare_messages = list("the void of space") - -/obj/item/bedsheet/mime - name = "mime's blanket" - desc = "A very soothing striped blanket. All the noise just seems to fade out when you're under the covers in this." - icon_state = "sheetmime" - item_color = "mime" - dream_messages = list("silence", "gestures", "a pale face", "a gaping mouth", "the mime") - nightmare_messages = list("honk", "laughter", "a prank", "a joke", "a smiling face", "the clown") - -/obj/item/bedsheet/clown - name = "clown's blanket" - desc = "A rainbow blanket with a clown mask woven in. It smells faintly of bananas." - icon_state = "sheetclown" - item_color = "clown" - dream_messages = list("honk", "laughter", "a prank", "a joke", "a smiling face", "the clown") - nightmare_messages = list("silence", "gestures", "a pale face", "a gaping mouth", "the mime") - -/obj/item/bedsheet/captain - name = "captain's bedsheet." - desc = "It has a Nanotrasen symbol on it, and was woven with a revolutionary new kind of thread guaranteed to have 0.01% permeability for most non-chemical substances, popular among most modern captains." - icon_state = "sheetcaptain" - item_color = "captain" - dream_messages = list("authority", "a golden ID", "sunglasses", "a green disc", "an antique gun", "the captain") - nightmare_messages = list("comdom", "clown with all access", "the syndicate") - -/obj/item/bedsheet/rd - name = "research director's bedsheet" - desc = "It appears to have a beaker emblem, and is made out of fire-resistant material, although it probably won't protect you in the event of fires you're familiar with every day." - icon_state = "sheetrd" - item_color = "director" - dream_messages = list("authority", "a silvery ID", "a bomb", "a mech", "a facehugger", "maniacal laughter", "the research director") - nightmare_messages = list("toxins full of plasma", "UPGRADE THE SLEEPERS", "rogue ai") - -/obj/item/bedsheet/rd/royal_cape - name = "Royal Cape of the Liberator" - desc = "Majestic." - dream_messages = list("mining", "stone", "a golem", "freedom", "doing whatever") - -/obj/item/bedsheet/medical - name = "medical blanket" - desc = "It's a sterilized* blanket commonly used in the Medbay. *Sterilization is voided if a virologist is present onboard the station." - icon_state = "sheetmedical" - item_color = "medical" - dream_messages = list("healing", "life", "surgery", "a doctor") - nightmare_messages = list("death", "no cryox", "cryo is off") - -/obj/item/bedsheet/cmo - name = "chief medical officer's bedsheet" - desc = "It's a sterilized blanket that has a cross emblem. There's some cat fur on it, likely from Runtime." - icon_state = "sheetcmo" - item_color = "cmo" - dream_messages = list("authority", "a silvery ID", "healing", "life", "surgery", "a cat", "the chief medical officer") - nightmare_messages = list("chemists making meth", "cryo it off", "where is the defib", "no biomass") - -/obj/item/bedsheet/hos - name = "head of security's bedsheet" - desc = "It is decorated with a shield emblem. While crime doesn't sleep, you do, but you are still THE LAW!" - icon_state = "sheethos" - item_color = "hosred" - dream_messages = list("authority", "a silvery ID", "handcuffs", "a baton", "a flashbang", "sunglasses", "the head of security") - nightmare_messages = list("the clown", "a toolbox", "sHiTcUrItY", "why did you put them in for 50 minutes") - - -/obj/item/bedsheet/hop - name = "head of personnel's bedsheet" - desc = "It is decorated with a key emblem. For those rare moments when you can rest and cuddle with Ian without someone screaming for you over the radio." - icon_state = "sheethop" - item_color = "hop" - dream_messages = list("authority", "a silvery ID", "obligation", "a computer", "an ID", "a corgi", "the head of personnel") - nightmare_messages = list("improper paperwork", "all access clown", "50 open clown slots", "dead ian") - -/obj/item/bedsheet/ce - name = "chief engineer's bedsheet" - desc = "It is decorated with a wrench emblem. It's highly reflective and stain resistant, so you don't need to worry about ruining it with oil." - icon_state = "sheetce" - item_color = "chief" - dream_messages = list("authority", "a silvery ID", "the engine", "power tools", "an APC", "a parrot", "the chief engineer") - nightmare_messages = list("forced airlock", "syndicate bomb", "explosion in research chem", "iT's LoOsE") - -/obj/item/bedsheet/qm - name = "quartermaster's bedsheet" - desc = "It is decorated with a crate emblem in silver lining. It's rather tough, and just the thing to lie on after a hard day of pushing paper." - icon_state = "sheetqm" - item_color = "qm" - dream_messages = list("a grey ID", "a shuttle", "a crate", "a sloth", "the quartermaster") - nightmare_messages = list("a bald person", "no points", "wheres the ore", "space carp") - -/obj/item/bedsheet/brown - icon_state = "sheetbrown" - item_color = "cargo" - dream_messages = list("brown") - nightmare_messages = list("dead monkey") - -/obj/item/bedsheet/centcom - name = "centcom bedsheet" - desc = "Woven with advanced nanothread for warmth as well as being very decorated, essential for all officials." - icon_state = "sheetcentcom" - item_color = "centcom" - dream_messages = list("a unique ID", "authority", "artillery", "an ending") - nightmare_messages = list("a butt fax") - -/obj/item/bedsheet/syndie - name = "syndicate bedsheet" - desc = "It has a syndicate emblem and it has an aura of evil." - icon_state = "sheetsyndie" - item_color = "syndie" - dream_messages = list("a green disc", "a red crystal", "a glowing blade", "a wire-covered ID") - nightmare_messages = list("stunbaton", "taser", "lasers", "a toolbox") - -/obj/item/bedsheet/cult - name = "cultist's bedsheet" - desc = "You might dream of Nar'Sie if you sleep with this. It seems rather tattered and glows of an eldritch presence." - icon_state = "sheetcult" - item_color = "cult" - dream_messages = list("a tome", "a floating red crystal", "a glowing sword", "a bloody symbol", "a massive humanoid figure") - nightmare_messages = list("a tome", "a floating red crystal", "a glowing sword", "a bloody symbol", "a massive humanoid figure") - - -/obj/item/bedsheet/wiz - name = "wizard's bedsheet" - desc = "A special fabric enchanted with magic so you can have an enchanted night. It even glows!" - icon_state = "sheetwiz" - item_color = "wiz" - dream_messages = list("a book", "an explosion", "lightning", "a staff", "a skeleton", "a robe", "magic") - nightmare_messages = list("a toolbox", "solars") - - - -/obj/structure/bedsheetbin - name = "linen bin" - desc = "A linen bin. It looks rather cosy." - icon = 'icons/obj/structures.dmi' - icon_state = "linenbin-full" - anchored = 1 - resistance_flags = FLAMMABLE - max_integrity = 70 - var/amount = 20 - var/list/sheets = list() - var/obj/item/hidden = null - - -/obj/structure/bedsheetbin/examine(mob/user) - . = ..() - if(amount < 1) - . += "There are no bed sheets in the bin." - else if(amount == 1) - . += "There is one bed sheet in the bin." - else - . += "There are [amount] bed sheets in the bin." - - -/obj/structure/bedsheetbin/update_icon() - switch(amount) - if(0) icon_state = "linenbin-empty" - if(1 to amount / 2) icon_state = "linenbin-half" - else icon_state = "linenbin-full" - - -/obj/structure/bedsheetbin/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - if(amount) - amount = 0 - update_icon() - ..() - -/obj/structure/bedsheetbin/burn() - amount = 0 - extinguish() - update_icon() - -/obj/structure/bedsheetbin/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/bedsheet)) - user.drop_item() - I.loc = src - sheets.Add(I) - amount++ - to_chat(user, "You put [I] in [src].") - else if(amount && !hidden && I.w_class < WEIGHT_CLASS_BULKY) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. - user.drop_item() - I.loc = src - hidden = I - to_chat(user, "You hide [I] among the sheets.") - - - -/obj/structure/bedsheetbin/attack_hand(mob/user as mob) - if(amount >= 1) - amount-- - - var/obj/item/bedsheet/B - if(sheets.len > 0) - B = sheets[sheets.len] - sheets.Remove(B) - - else - B = new /obj/item/bedsheet(loc) - - B.loc = user.loc - user.put_in_hands(B) - to_chat(user, "You take [B] out of [src].") - - if(hidden) - hidden.loc = user.loc - to_chat(user, "[hidden] falls out of [B]!") - hidden = null - - - add_fingerprint(user) - - -/obj/structure/bedsheetbin/attack_tk(mob/user as mob) - if(amount >= 1) - amount-- - - var/obj/item/bedsheet/B - if(sheets.len > 0) - B = sheets[sheets.len] - sheets.Remove(B) - - else - B = new /obj/item/bedsheet(loc) - - B.loc = loc - to_chat(user, "You telekinetically remove [B] from [src].") - update_icon() - - if(hidden) - hidden.loc = loc - hidden = null +/* +CONTAINS: +BEDSHEETS +LINEN BINS +*/ + +/obj/item/bedsheet + name = "bedsheet" + desc = "A surprisingly soft linen bedsheet." + icon = 'icons/obj/items.dmi' + icon_state = "sheet" + item_state = "bedsheet" + layer = 4.0 + throwforce = 1 + throw_speed = 1 + throw_range = 2 + w_class = WEIGHT_CLASS_TINY + item_color = "white" + resistance_flags = FLAMMABLE + slot_flags = SLOT_BACK + + dog_fashion = /datum/dog_fashion/head/ghost + var/list/dream_messages = list("white") + var/list/nightmare_messages = list("black") + var/comfort = 0.5 + + + +/obj/item/bedsheet/attack_self(mob/user as mob) + user.drop_item() + if(layer == initial(layer)) + layer = 5 + else + layer = initial(layer) + add_fingerprint(user) + return + +/obj/item/bedsheet/attackby(obj/item/I, mob/user, params) + if(I.sharp) + var/obj/item/stack/sheet/cloth/C = new (get_turf(src), 3) + transfer_fingerprints_to(C) + C.add_fingerprint(user) + qdel(src) + to_chat(user, "You tear [src] up.") + else + return ..() + +/obj/item/bedsheet/blue + icon_state = "sheetblue" + item_color = "blue" + dream_messages = list("blue") + nightmare_messages = list("vox blood") + +/obj/item/bedsheet/green + icon_state = "sheetgreen" + item_color = "green" + dream_messages = list("green") + nightmare_messages = list("unathi flesh") + +/obj/item/bedsheet/orange + icon_state = "sheetorange" + item_color = "orange" + dream_messages = list("orange") + nightmare_messages = list("exploding fruit") + +/obj/item/bedsheet/purple + icon_state = "sheetpurple" + item_color = "purple" + dream_messages = list("purple") + nightmare_messages = list("Grey blood") + +/obj/item/bedsheet/patriot + name = "patriotic bedsheet" + desc = "You've never felt more free than when sleeping on this." + icon_state = "sheetUSA" + item_color = "sheetUSA" + dream_messages = list("America", "freedom", "fireworks", "bald eagles") + nightmare_messages = list("communism") + +/obj/item/bedsheet/rainbow + name = "rainbow bedsheet" + desc = "A multi_colored blanket. It's actually several different sheets cut up and sewn together." + icon_state = "sheetrainbow" + item_color = "rainbow" + dream_messages = list("red", "orange", "yellow", "green", "blue", "purple", "a rainbow") + nightmare_messages = list("green", "a cluwne", "fLoOr ClUwNe") + +/obj/item/bedsheet/red + icon_state = "sheetred" + item_color = "red" + dream_messages = list("red") + nightmare_messages = list("gibs") + +/obj/item/bedsheet/yellow + icon_state = "sheetyellow" + item_color = "yellow" + dream_messages = list("yellow") + nightmare_messages = list("locker full of banana peels") + +/obj/item/bedsheet/black + icon_state = "sheetblack" + item_color = "sheetblack" + dream_messages = list("black") + nightmare_messages = list("the void of space") + +/obj/item/bedsheet/mime + name = "mime's blanket" + desc = "A very soothing striped blanket. All the noise just seems to fade out when you're under the covers in this." + icon_state = "sheetmime" + item_color = "mime" + dream_messages = list("silence", "gestures", "a pale face", "a gaping mouth", "the mime") + nightmare_messages = list("honk", "laughter", "a prank", "a joke", "a smiling face", "the clown") + +/obj/item/bedsheet/clown + name = "clown's blanket" + desc = "A rainbow blanket with a clown mask woven in. It smells faintly of bananas." + icon_state = "sheetclown" + item_color = "clown" + dream_messages = list("honk", "laughter", "a prank", "a joke", "a smiling face", "the clown") + nightmare_messages = list("silence", "gestures", "a pale face", "a gaping mouth", "the mime") + +/obj/item/bedsheet/captain + name = "captain's bedsheet." + desc = "It has a Nanotrasen symbol on it, and was woven with a revolutionary new kind of thread guaranteed to have 0.01% permeability for most non-chemical substances, popular among most modern captains." + icon_state = "sheetcaptain" + item_color = "captain" + dream_messages = list("authority", "a golden ID", "sunglasses", "a green disc", "an antique gun", "the captain") + nightmare_messages = list("comdom", "clown with all access", "the syndicate") + +/obj/item/bedsheet/rd + name = "research director's bedsheet" + desc = "It appears to have a beaker emblem, and is made out of fire-resistant material, although it probably won't protect you in the event of fires you're familiar with every day." + icon_state = "sheetrd" + item_color = "director" + dream_messages = list("authority", "a silvery ID", "a bomb", "a mech", "a facehugger", "maniacal laughter", "the research director") + nightmare_messages = list("toxins full of plasma", "UPGRADE THE SLEEPERS", "rogue ai") + +/obj/item/bedsheet/rd/royal_cape + name = "Royal Cape of the Liberator" + desc = "Majestic." + dream_messages = list("mining", "stone", "a golem", "freedom", "doing whatever") + +/obj/item/bedsheet/medical + name = "medical blanket" + desc = "It's a sterilized* blanket commonly used in the Medbay. *Sterilization is voided if a virologist is present onboard the station." + icon_state = "sheetmedical" + item_color = "medical" + dream_messages = list("healing", "life", "surgery", "a doctor") + nightmare_messages = list("death", "no cryox", "cryo is off") + +/obj/item/bedsheet/cmo + name = "chief medical officer's bedsheet" + desc = "It's a sterilized blanket that has a cross emblem. There's some cat fur on it, likely from Runtime." + icon_state = "sheetcmo" + item_color = "cmo" + dream_messages = list("authority", "a silvery ID", "healing", "life", "surgery", "a cat", "the chief medical officer") + nightmare_messages = list("chemists making meth", "cryo it off", "where is the defib", "no biomass") + +/obj/item/bedsheet/hos + name = "head of security's bedsheet" + desc = "It is decorated with a shield emblem. While crime doesn't sleep, you do, but you are still THE LAW!" + icon_state = "sheethos" + item_color = "hosred" + dream_messages = list("authority", "a silvery ID", "handcuffs", "a baton", "a flashbang", "sunglasses", "the head of security") + nightmare_messages = list("the clown", "a toolbox", "sHiTcUrItY", "why did you put them in for 50 minutes") + + +/obj/item/bedsheet/hop + name = "head of personnel's bedsheet" + desc = "It is decorated with a key emblem. For those rare moments when you can rest and cuddle with Ian without someone screaming for you over the radio." + icon_state = "sheethop" + item_color = "hop" + dream_messages = list("authority", "a silvery ID", "obligation", "a computer", "an ID", "a corgi", "the head of personnel") + nightmare_messages = list("improper paperwork", "all access clown", "50 open clown slots", "dead ian") + +/obj/item/bedsheet/ce + name = "chief engineer's bedsheet" + desc = "It is decorated with a wrench emblem. It's highly reflective and stain resistant, so you don't need to worry about ruining it with oil." + icon_state = "sheetce" + item_color = "chief" + dream_messages = list("authority", "a silvery ID", "the engine", "power tools", "an APC", "a parrot", "the chief engineer") + nightmare_messages = list("forced airlock", "syndicate bomb", "explosion in research chem", "iT's LoOsE") + +/obj/item/bedsheet/qm + name = "quartermaster's bedsheet" + desc = "It is decorated with a crate emblem in silver lining. It's rather tough, and just the thing to lie on after a hard day of pushing paper." + icon_state = "sheetqm" + item_color = "qm" + dream_messages = list("a grey ID", "a shuttle", "a crate", "a sloth", "the quartermaster") + nightmare_messages = list("a bald person", "no points", "wheres the ore", "space carp") + +/obj/item/bedsheet/brown + icon_state = "sheetbrown" + item_color = "cargo" + dream_messages = list("brown") + nightmare_messages = list("dead monkey") + +/obj/item/bedsheet/centcom + name = "centcom bedsheet" + desc = "Woven with advanced nanothread for warmth as well as being very decorated, essential for all officials." + icon_state = "sheetcentcom" + item_color = "centcom" + dream_messages = list("a unique ID", "authority", "artillery", "an ending") + nightmare_messages = list("a butt fax") + +/obj/item/bedsheet/syndie + name = "syndicate bedsheet" + desc = "It has a syndicate emblem and it has an aura of evil." + icon_state = "sheetsyndie" + item_color = "syndie" + dream_messages = list("a green disc", "a red crystal", "a glowing blade", "a wire-covered ID") + nightmare_messages = list("stunbaton", "taser", "lasers", "a toolbox") + +/obj/item/bedsheet/cult + name = "cultist's bedsheet" + desc = "You might dream of Nar'Sie if you sleep with this. It seems rather tattered and glows of an eldritch presence." + icon_state = "sheetcult" + item_color = "cult" + dream_messages = list("a tome", "a floating red crystal", "a glowing sword", "a bloody symbol", "a massive humanoid figure") + nightmare_messages = list("a tome", "a floating red crystal", "a glowing sword", "a bloody symbol", "a massive humanoid figure") + + +/obj/item/bedsheet/wiz + name = "wizard's bedsheet" + desc = "A special fabric enchanted with magic so you can have an enchanted night. It even glows!" + icon_state = "sheetwiz" + item_color = "wiz" + dream_messages = list("a book", "an explosion", "lightning", "a staff", "a skeleton", "a robe", "magic") + nightmare_messages = list("a toolbox", "solars") + + + +/obj/structure/bedsheetbin + name = "linen bin" + desc = "A linen bin. It looks rather cosy." + icon = 'icons/obj/structures.dmi' + icon_state = "linenbin-full" + anchored = 1 + resistance_flags = FLAMMABLE + max_integrity = 70 + var/amount = 20 + var/list/sheets = list() + var/obj/item/hidden = null + + +/obj/structure/bedsheetbin/examine(mob/user) + . = ..() + if(amount < 1) + . += "There are no bed sheets in the bin." + else if(amount == 1) + . += "There is one bed sheet in the bin." + else + . += "There are [amount] bed sheets in the bin." + + +/obj/structure/bedsheetbin/update_icon() + switch(amount) + if(0) icon_state = "linenbin-empty" + if(1 to amount / 2) icon_state = "linenbin-half" + else icon_state = "linenbin-full" + + +/obj/structure/bedsheetbin/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + if(amount) + amount = 0 + update_icon() + ..() + +/obj/structure/bedsheetbin/burn() + amount = 0 + extinguish() + update_icon() + +/obj/structure/bedsheetbin/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/bedsheet)) + user.drop_item() + I.loc = src + sheets.Add(I) + amount++ + to_chat(user, "You put [I] in [src].") + else if(amount && !hidden && I.w_class < WEIGHT_CLASS_BULKY) //make sure there's sheets to hide it among, make sure nothing else is hidden in there. + user.drop_item() + I.loc = src + hidden = I + to_chat(user, "You hide [I] among the sheets.") + + + +/obj/structure/bedsheetbin/attack_hand(mob/user as mob) + if(amount >= 1) + amount-- + + var/obj/item/bedsheet/B + if(sheets.len > 0) + B = sheets[sheets.len] + sheets.Remove(B) + + else + B = new /obj/item/bedsheet(loc) + + B.loc = user.loc + user.put_in_hands(B) + to_chat(user, "You take [B] out of [src].") + + if(hidden) + hidden.loc = user.loc + to_chat(user, "[hidden] falls out of [B]!") + hidden = null + + + add_fingerprint(user) + + +/obj/structure/bedsheetbin/attack_tk(mob/user as mob) + if(amount >= 1) + amount-- + + var/obj/item/bedsheet/B + if(sheets.len > 0) + B = sheets[sheets.len] + sheets.Remove(B) + + else + B = new /obj/item/bedsheet(loc) + + B.loc = loc + to_chat(user, "You telekinetically remove [B] from [src].") + update_icon() + + if(hidden) + hidden.loc = loc + hidden = null diff --git a/code/game/objects/structures/coathanger.dm b/code/game/objects/structures/coathanger.dm index 5a0a47150caa..59bd99a7e15d 100644 --- a/code/game/objects/structures/coathanger.dm +++ b/code/game/objects/structures/coathanger.dm @@ -49,4 +49,4 @@ if(istype(coat, /obj/item/clothing/suit/storage/labcoat/cmo)) overlays += image(icon, icon_state = "coat_cmo") if(istype(coat, /obj/item/clothing/suit/storage/det_suit)) - overlays += image(icon, icon_state = "coat_det") \ No newline at end of file + overlays += image(icon, icon_state = "coat_det") diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 8c31d1cdbfe8..82487dbd0ae2 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -1,451 +1,451 @@ -/obj/structure/closet - name = "closet" - desc = "It's a basic storage unit." - icon = 'icons/obj/closet.dmi' - icon_state = "closed" - density = 1 - max_integrity = 200 - integrity_failure = 50 - armor = list("melee" = 20, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60) - var/icon_closed = "closed" - var/icon_opened = "open" - var/opened = FALSE - var/welded = FALSE - var/locked = FALSE - var/wall_mounted = 0 //never solid (You can always pass over it) - var/lastbang - var/sound = 'sound/machines/click.ogg' - var/storage_capacity = 30 //This is so that someone can't pack hundreds of items in a locker/crate then open it in a populated area to crash clients. - var/material_drop = /obj/item/stack/sheet/metal - var/material_drop_amount = 2 - -/obj/structure/closet/New() - ..() - spawn(1) - if(!opened) // if closed, any item at the crate's loc is put in the contents - for(var/obj/item/I in loc) - if(I.density || I.anchored || I == src) continue - I.forceMove(src) - -// Fix for #383 - C4 deleting fridges with corpses -/obj/structure/closet/Destroy() - dump_contents() - return ..() - -/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0 || wall_mounted) - return TRUE - return (!density) - -/obj/structure/closet/proc/can_open() - if(welded) - return FALSE - return TRUE - -/obj/structure/closet/proc/can_close() - for(var/obj/structure/closet/closet in get_turf(src)) - if(closet != src && closet.anchored != 1) - return FALSE - - return TRUE - -/obj/structure/closet/proc/dump_contents() - var/turf/T = get_turf(src) - for(var/atom/movable/AM in src) - AM.forceMove(T) - if(throwing) // you keep some momentum when getting out of a thrown closet - step(AM, dir) - if(throwing) - throwing.finalize(FALSE) - -/obj/structure/closet/proc/open() - if(opened) - return FALSE - - if(!can_open()) - return FALSE - - dump_contents() - - icon_state = icon_opened - opened = TRUE - if(sound) - playsound(loc, sound, 15, 1, -3) - else - playsound(loc, 'sound/machines/click.ogg', 15, 1, -3) - density = 0 - return TRUE - -/obj/structure/closet/proc/close() - if(!opened) - return FALSE - if(!can_close()) - return FALSE - - var/itemcount = 0 - - //Cham Projector Exception - for(var/obj/effect/dummy/chameleon/AD in loc) - if(itemcount >= storage_capacity) - break - AD.forceMove(src) - itemcount++ - - for(var/obj/item/I in loc) - if(itemcount >= storage_capacity) - break - if(!I.anchored) - I.forceMove(src) - itemcount++ - - for(var/mob/M in loc) - if(itemcount >= storage_capacity) - break - if(istype(M, /mob/dead/observer)) - continue - if(istype(M, /mob/living/simple_animal/bot/mulebot)) - continue - if(M.buckled || M.anchored || M.has_buckled_mobs()) - continue - if(isAI(M)) - continue - - M.forceMove(src) - itemcount++ - - icon_state = icon_closed - opened = FALSE - if(sound) - playsound(loc, sound, 15, 1, -3) - else - playsound(loc, 'sound/machines/click.ogg', 15, 1, -3) - density = 1 - return TRUE - -/obj/structure/closet/proc/toggle(mob/user) - if(!(opened ? close() : open())) - to_chat(user, "It won't budge!") - -/obj/structure/closet/proc/bust_open() - welded = FALSE //applies to all lockers - locked = FALSE //applies to critter crates and secure lockers only - broken = TRUE //applies to secure lockers only - open() - -/obj/structure/closet/deconstruct(disassembled = TRUE) - if(ispath(material_drop) && material_drop_amount && !(flags & NODECONSTRUCT)) - new material_drop(loc, material_drop_amount) - qdel(src) - -/obj/structure/closet/obj_break(damage_flag) - if(!broken && !(flags & NODECONSTRUCT)) - bust_open() - -/obj/structure/closet/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/rcs) && !opened) - if(user in contents) //to prevent self-teleporting. - return - var/obj/item/rcs/E = W - if(E.rcell && (E.rcell.charge >= E.chargecost)) - if(!is_level_reachable(z)) - to_chat(user, "The rapid-crate-sender can't locate any telepads!") - return - if(E.mode == 0) - if(!E.teleporting) - var/list/L = list() - var/list/areaindex = list() - for(var/obj/machinery/telepad_cargo/R in world) - if(R.stage == 0) - var/turf/T = get_turf(R) - var/tmpname = T.loc.name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = R - var/desc = input("Please select a telepad.", "RCS") in L - E.pad = L[desc] - if(!Adjacent(user)) - to_chat(user, "Unable to teleport, too far from crate.") - return - playsound(E.loc, E.usesound, 50, 1) - to_chat(user, "Teleporting [name]...") - E.teleporting = 1 - if(!do_after(user, 50 * E.toolspeed, target = src)) - E.teleporting = 0 - return - E.teleporting = 0 - if(user in contents) - to_chat(user, "Error: User located in container--aborting for safety.") - playsound(E.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) - return - if(!(E.rcell && E.rcell.use(E.chargecost))) - to_chat(user, "Unable to teleport, insufficient charge.") - return - do_sparks(5, 1, src) - do_teleport(src, E.pad, 0) - to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") - return - else - E.rand_x = rand(50,200) - E.rand_y = rand(50,200) - var/L = locate(E.rand_x, E.rand_y, 6) - if(!Adjacent(user)) - to_chat(user, "Unable to teleport, too far from crate.") - return - playsound(E.loc, E.usesound, 50, 1) - to_chat(user, "Teleporting [name]...") - E.teleporting = 1 - if(!do_after(user, 50, E.toolspeed, target = src)) - E.teleporting = 0 - return - E.teleporting = 0 - if(user in contents) - to_chat(user, "Error: User located in container--aborting for safety.") - playsound(E.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) - return - if(!(E.rcell && E.rcell.use(E.chargecost))) - to_chat(user, "Unable to teleport, insufficient charge.") - return - do_sparks(5, 1, src) - do_teleport(src, L) - to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") - return - else - to_chat(user, "Out of charges.") - return - return - - if(opened) - if(istype(W, /obj/item/grab)) - MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet - if(istype(W,/obj/item/tk_grab)) - return FALSE - if(isrobot(user)) - return - if(!user.drop_item()) //couldn't drop the item - to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!") - return - if(W) - W.forceMove(loc) - else if(istype(W, /obj/item/stack/packageWrap)) - return - else if(user.a_intent != INTENT_HARM) - attack_hand(user) - else - return ..() - -/obj/structure/closet/welder_act(mob/user, obj/item/I) - . = TRUE - if(!opened && user.loc == src) - to_chat(user, "You can't weld [src] from inside!") - return - if(!I.tool_use_check(user, 0)) - return - if(opened) - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - WELDER_SLICING_SUCCESS_MESSAGE - deconstruct(TRUE) - return - else - var/adjective = welded ? "open" : "shut" - user.visible_message("[user] begins welding [src] [adjective]...", "You begin welding [src] [adjective]...", "You hear welding.") - if(I.use_tool(src, user, 15, volume = I.tool_volume)) - if(opened) - to_chat(user, "Keep [src] shut while doing that!") - return - user.visible_message("[user] welds [src] [adjective]!", "You weld [src] [adjective]!") - welded = !welded - update_icon() - return - -/obj/structure/closet/MouseDrop_T(atom/movable/O, mob/user) - ..() - if(istype(O, /obj/screen)) //fix for HUD elements making their way into the world -Pete - return - if(O.loc == user) - return - if(user.restrained() || user.stat || user.IsWeakened() || user.stunned || user.paralysis || user.lying) - return - if((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src))) - return - if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems - return - if(!istype(user.loc, /turf)) // are you in a container/closet/pod/etc? - return - if(!opened) - return - if(istype(O, /obj/structure/closet)) - return - step_towards(O, loc) - if(user != O) - user.visible_message("[user] stuffs [O] into [src]!", "You stuff [O] into [src]!") - add_fingerprint(user) - -/obj/structure/closet/attack_ai(mob/user) - if(isrobot(user) && Adjacent(user)) //Robots can open/close it, but not the AI - attack_hand(user) - -/obj/structure/closet/relaymove(mob/user) - if(user.stat || !isturf(loc)) - return - - if(!open()) - to_chat(user, "It won't budge!") - if(!lastbang) - lastbang = 1 - for(var/mob/M in hearers(src, null)) - to_chat(M, text("BANG, bang!", max(0, 5 - get_dist(src, M)))) - spawn(30) - lastbang = 0 - -/obj/structure/closet/attack_hand(mob/user) - add_fingerprint(user) - toggle(user) - -/obj/structure/closet/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - toggle(user) - -// tk grab then use on self -/obj/structure/closet/attack_self_tk(mob/user) - add_fingerprint(user) - if(!toggle()) - to_chat(usr, "It won't budge!") - -/obj/structure/closet/verb/verb_toggleopen() - set src in oview(1) - set category = null - set name = "Toggle Open" - - if(usr.incapacitated()) - return - - if(ishuman(usr)) - add_fingerprint(usr) - toggle(usr) - else - to_chat(usr, "This mob type can't use this verb.") - -/obj/structure/closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot) - overlays.Cut() - if(!opened) - icon_state = icon_closed - if(welded) - overlays += "welded" - else - icon_state = icon_opened - -// Objects that try to exit a locker by stepping were doing so successfully, -// and due to an oversight in turf/Enter() were going through walls. That -// should be independently resolved, but this is also an interesting twist. -/obj/structure/closet/Exit(atom/movable/AM) - open() - if(AM.loc == src) - return FALSE - return TRUE - -/obj/structure/closet/container_resist(var/mob/living/L) - var/breakout_time = 2 //2 minutes by default - if(opened) - if(L.loc == src) - L.forceMove(get_turf(src)) // Let's just be safe here - return //Door's open... wait, why are you in it's contents then? - if(!welded) - open() //for cardboard boxes - return //closed but not welded... - // else Meh, lets just keep it at 2 minutes for now - // breakout_time++ //Harder to get out of welded lockers than locked lockers - - //okay, so the closet is either welded or locked... resist!!! - L.changeNext_move(CLICK_CD_BREAKOUT) - L.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(L, "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)") - for(var/mob/O in viewers(usr.loc)) - O.show_message("The [src] begins to shake violently!", 1) - - - spawn(0) - if(do_after(L,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds - if(!src || !L || L.stat != CONSCIOUS || L.loc != src || opened) //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened - return - - //Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'... - if(!welded) - return - - //Well then break it! - welded = FALSE - update_icon() - to_chat(usr, "You successfully break out!") - for(var/mob/O in viewers(L.loc)) - O.show_message("\the [usr] successfully broke out of \the [src]!", 1) - if(istype(loc, /obj/structure/bigDelivery)) //nullspace ect.. read the comment above - var/obj/structure/bigDelivery/BD = loc - BD.attack_hand(usr) - open() - -/obj/structure/closet/tesla_act(var/power) - ..() - visible_message("[src] is blown apart by the bolt of electricity!", "You hear a metallic screeching sound.") - qdel(src) - -/obj/structure/closet/get_remote_view_fullscreens(mob/user) - if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) - -/obj/structure/closet/ex_act(severity) - for(var/atom/A in contents) - A.ex_act(severity) - CHECK_TICK - ..() - -/obj/structure/closet/singularity_act() - dump_contents() - ..() - -/obj/structure/closet/AllowDrop() - return TRUE - -/obj/structure/closet/bluespace - name = "bluespace closet" - desc = "A storage unit that moves and stores through the fourth dimension." - density = 0 - icon_state = "bluespace" - icon_closed = "bluespace" - icon_opened = "bluespaceopen" - storage_capacity = 60 - var/materials = list(MAT_METAL = 5000, MAT_PLASMA = 2500, MAT_TITANIUM = 500, MAT_BLUESPACE = 500) - -/obj/structure/closet/bluespace/CheckExit(atom/movable/AM) - UpdateTransparency(AM, loc) - return TRUE - -/obj/structure/closet/bluespace/proc/UpdateTransparency(atom/movable/AM, atom/location) - var/transparent = FALSE - for(var/atom/A in location) - if(A.density && A != src && A != AM) - transparent = TRUE - break - icon_opened = transparent ? "bluespaceopentrans" : "bluespaceopen" - icon_closed = transparent ? "bluespacetrans" : "bluespace" - icon_state = opened ? icon_opened : icon_closed - -/obj/structure/closet/bluespace/Crossed(atom/movable/AM, oldloc) - if(AM.density) - icon_state = opened ? "bluespaceopentrans" : "bluespacetrans" - -/obj/structure/closet/bluespace/Move(NewLoc, direct) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls. - var/turf/T = get_turf(NewLoc) - if(T.density) - return - for(var/atom/A in T.contents) - if(A.density && istype(A, /obj/machinery/door)) - return - UpdateTransparency(src, NewLoc) - forceMove(NewLoc) - -/obj/structure/closet/bluespace/close() - . = ..() - density = 0 \ No newline at end of file +/obj/structure/closet + name = "closet" + desc = "It's a basic storage unit." + icon = 'icons/obj/closet.dmi' + icon_state = "closed" + density = 1 + max_integrity = 200 + integrity_failure = 50 + armor = list("melee" = 20, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 60) + var/icon_closed = "closed" + var/icon_opened = "open" + var/opened = FALSE + var/welded = FALSE + var/locked = FALSE + var/wall_mounted = 0 //never solid (You can always pass over it) + var/lastbang + var/sound = 'sound/machines/click.ogg' + var/storage_capacity = 30 //This is so that someone can't pack hundreds of items in a locker/crate then open it in a populated area to crash clients. + var/material_drop = /obj/item/stack/sheet/metal + var/material_drop_amount = 2 + +/obj/structure/closet/New() + ..() + spawn(1) + if(!opened) // if closed, any item at the crate's loc is put in the contents + for(var/obj/item/I in loc) + if(I.density || I.anchored || I == src) continue + I.forceMove(src) + +// Fix for #383 - C4 deleting fridges with corpses +/obj/structure/closet/Destroy() + dump_contents() + return ..() + +/obj/structure/closet/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0 || wall_mounted) + return TRUE + return (!density) + +/obj/structure/closet/proc/can_open() + if(welded) + return FALSE + return TRUE + +/obj/structure/closet/proc/can_close() + for(var/obj/structure/closet/closet in get_turf(src)) + if(closet != src && closet.anchored != 1) + return FALSE + + return TRUE + +/obj/structure/closet/proc/dump_contents() + var/turf/T = get_turf(src) + for(var/atom/movable/AM in src) + AM.forceMove(T) + if(throwing) // you keep some momentum when getting out of a thrown closet + step(AM, dir) + if(throwing) + throwing.finalize(FALSE) + +/obj/structure/closet/proc/open() + if(opened) + return FALSE + + if(!can_open()) + return FALSE + + dump_contents() + + icon_state = icon_opened + opened = TRUE + if(sound) + playsound(loc, sound, 15, 1, -3) + else + playsound(loc, 'sound/machines/click.ogg', 15, 1, -3) + density = 0 + return TRUE + +/obj/structure/closet/proc/close() + if(!opened) + return FALSE + if(!can_close()) + return FALSE + + var/itemcount = 0 + + //Cham Projector Exception + for(var/obj/effect/dummy/chameleon/AD in loc) + if(itemcount >= storage_capacity) + break + AD.forceMove(src) + itemcount++ + + for(var/obj/item/I in loc) + if(itemcount >= storage_capacity) + break + if(!I.anchored) + I.forceMove(src) + itemcount++ + + for(var/mob/M in loc) + if(itemcount >= storage_capacity) + break + if(istype(M, /mob/dead/observer)) + continue + if(istype(M, /mob/living/simple_animal/bot/mulebot)) + continue + if(M.buckled || M.anchored || M.has_buckled_mobs()) + continue + if(isAI(M)) + continue + + M.forceMove(src) + itemcount++ + + icon_state = icon_closed + opened = FALSE + if(sound) + playsound(loc, sound, 15, 1, -3) + else + playsound(loc, 'sound/machines/click.ogg', 15, 1, -3) + density = 1 + return TRUE + +/obj/structure/closet/proc/toggle(mob/user) + if(!(opened ? close() : open())) + to_chat(user, "It won't budge!") + +/obj/structure/closet/proc/bust_open() + welded = FALSE //applies to all lockers + locked = FALSE //applies to critter crates and secure lockers only + broken = TRUE //applies to secure lockers only + open() + +/obj/structure/closet/deconstruct(disassembled = TRUE) + if(ispath(material_drop) && material_drop_amount && !(flags & NODECONSTRUCT)) + new material_drop(loc, material_drop_amount) + qdel(src) + +/obj/structure/closet/obj_break(damage_flag) + if(!broken && !(flags & NODECONSTRUCT)) + bust_open() + +/obj/structure/closet/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/rcs) && !opened) + if(user in contents) //to prevent self-teleporting. + return + var/obj/item/rcs/E = W + if(E.rcell && (E.rcell.charge >= E.chargecost)) + if(!is_level_reachable(z)) + to_chat(user, "The rapid-crate-sender can't locate any telepads!") + return + if(E.mode == 0) + if(!E.teleporting) + var/list/L = list() + var/list/areaindex = list() + for(var/obj/machinery/telepad_cargo/R in world) + if(R.stage == 0) + var/turf/T = get_turf(R) + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = R + var/desc = input("Please select a telepad.", "RCS") in L + E.pad = L[desc] + if(!Adjacent(user)) + to_chat(user, "Unable to teleport, too far from crate.") + return + playsound(E.loc, E.usesound, 50, 1) + to_chat(user, "Teleporting [name]...") + E.teleporting = 1 + if(!do_after(user, 50 * E.toolspeed, target = src)) + E.teleporting = 0 + return + E.teleporting = 0 + if(user in contents) + to_chat(user, "Error: User located in container--aborting for safety.") + playsound(E.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) + return + if(!(E.rcell && E.rcell.use(E.chargecost))) + to_chat(user, "Unable to teleport, insufficient charge.") + return + do_sparks(5, 1, src) + do_teleport(src, E.pad, 0) + to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") + return + else + E.rand_x = rand(50,200) + E.rand_y = rand(50,200) + var/L = locate(E.rand_x, E.rand_y, 6) + if(!Adjacent(user)) + to_chat(user, "Unable to teleport, too far from crate.") + return + playsound(E.loc, E.usesound, 50, 1) + to_chat(user, "Teleporting [name]...") + E.teleporting = 1 + if(!do_after(user, 50, E.toolspeed, target = src)) + E.teleporting = 0 + return + E.teleporting = 0 + if(user in contents) + to_chat(user, "Error: User located in container--aborting for safety.") + playsound(E.loc, 'sound/machines/buzz-sigh.ogg', 50, 1) + return + if(!(E.rcell && E.rcell.use(E.chargecost))) + to_chat(user, "Unable to teleport, insufficient charge.") + return + do_sparks(5, 1, src) + do_teleport(src, L) + to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") + return + else + to_chat(user, "Out of charges.") + return + return + + if(opened) + if(istype(W, /obj/item/grab)) + MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet + if(istype(W,/obj/item/tk_grab)) + return FALSE + if(isrobot(user)) + return + if(!user.drop_item()) //couldn't drop the item + to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!") + return + if(W) + W.forceMove(loc) + else if(istype(W, /obj/item/stack/packageWrap)) + return + else if(user.a_intent != INTENT_HARM) + attack_hand(user) + else + return ..() + +/obj/structure/closet/welder_act(mob/user, obj/item/I) + . = TRUE + if(!opened && user.loc == src) + to_chat(user, "You can't weld [src] from inside!") + return + if(!I.tool_use_check(user, 0)) + return + if(opened) + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + WELDER_SLICING_SUCCESS_MESSAGE + deconstruct(TRUE) + return + else + var/adjective = welded ? "open" : "shut" + user.visible_message("[user] begins welding [src] [adjective]...", "You begin welding [src] [adjective]...", "You hear welding.") + if(I.use_tool(src, user, 15, volume = I.tool_volume)) + if(opened) + to_chat(user, "Keep [src] shut while doing that!") + return + user.visible_message("[user] welds [src] [adjective]!", "You weld [src] [adjective]!") + welded = !welded + update_icon() + return + +/obj/structure/closet/MouseDrop_T(atom/movable/O, mob/user) + ..() + if(istype(O, /obj/screen)) //fix for HUD elements making their way into the world -Pete + return + if(O.loc == user) + return + if(user.restrained() || user.stat || user.IsWeakened() || user.stunned || user.paralysis || user.lying) + return + if((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src))) + return + if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems + return + if(!istype(user.loc, /turf)) // are you in a container/closet/pod/etc? + return + if(!opened) + return + if(istype(O, /obj/structure/closet)) + return + step_towards(O, loc) + if(user != O) + user.visible_message("[user] stuffs [O] into [src]!", "You stuff [O] into [src]!") + add_fingerprint(user) + +/obj/structure/closet/attack_ai(mob/user) + if(isrobot(user) && Adjacent(user)) //Robots can open/close it, but not the AI + attack_hand(user) + +/obj/structure/closet/relaymove(mob/user) + if(user.stat || !isturf(loc)) + return + + if(!open()) + to_chat(user, "It won't budge!") + if(!lastbang) + lastbang = 1 + for(var/mob/M in hearers(src, null)) + to_chat(M, text("BANG, bang!", max(0, 5 - get_dist(src, M)))) + spawn(30) + lastbang = 0 + +/obj/structure/closet/attack_hand(mob/user) + add_fingerprint(user) + toggle(user) + +/obj/structure/closet/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + toggle(user) + +// tk grab then use on self +/obj/structure/closet/attack_self_tk(mob/user) + add_fingerprint(user) + if(!toggle()) + to_chat(usr, "It won't budge!") + +/obj/structure/closet/verb/verb_toggleopen() + set src in oview(1) + set category = null + set name = "Toggle Open" + + if(usr.incapacitated()) + return + + if(ishuman(usr)) + add_fingerprint(usr) + toggle(usr) + else + to_chat(usr, "This mob type can't use this verb.") + +/obj/structure/closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot) + overlays.Cut() + if(!opened) + icon_state = icon_closed + if(welded) + overlays += "welded" + else + icon_state = icon_opened + +// Objects that try to exit a locker by stepping were doing so successfully, +// and due to an oversight in turf/Enter() were going through walls. That +// should be independently resolved, but this is also an interesting twist. +/obj/structure/closet/Exit(atom/movable/AM) + open() + if(AM.loc == src) + return FALSE + return TRUE + +/obj/structure/closet/container_resist(var/mob/living/L) + var/breakout_time = 2 //2 minutes by default + if(opened) + if(L.loc == src) + L.forceMove(get_turf(src)) // Let's just be safe here + return //Door's open... wait, why are you in it's contents then? + if(!welded) + open() //for cardboard boxes + return //closed but not welded... + // else Meh, lets just keep it at 2 minutes for now + // breakout_time++ //Harder to get out of welded lockers than locked lockers + + //okay, so the closet is either welded or locked... resist!!! + L.changeNext_move(CLICK_CD_BREAKOUT) + L.last_special = world.time + CLICK_CD_BREAKOUT + to_chat(L, "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)") + for(var/mob/O in viewers(usr.loc)) + O.show_message("The [src] begins to shake violently!", 1) + + + spawn(0) + if(do_after(L,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds + if(!src || !L || L.stat != CONSCIOUS || L.loc != src || opened) //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened + return + + //Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'... + if(!welded) + return + + //Well then break it! + welded = FALSE + update_icon() + to_chat(usr, "You successfully break out!") + for(var/mob/O in viewers(L.loc)) + O.show_message("\the [usr] successfully broke out of \the [src]!", 1) + if(istype(loc, /obj/structure/bigDelivery)) //nullspace ect.. read the comment above + var/obj/structure/bigDelivery/BD = loc + BD.attack_hand(usr) + open() + +/obj/structure/closet/tesla_act(var/power) + ..() + visible_message("[src] is blown apart by the bolt of electricity!", "You hear a metallic screeching sound.") + qdel(src) + +/obj/structure/closet/get_remote_view_fullscreens(mob/user) + if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 1) + +/obj/structure/closet/ex_act(severity) + for(var/atom/A in contents) + A.ex_act(severity) + CHECK_TICK + ..() + +/obj/structure/closet/singularity_act() + dump_contents() + ..() + +/obj/structure/closet/AllowDrop() + return TRUE + +/obj/structure/closet/bluespace + name = "bluespace closet" + desc = "A storage unit that moves and stores through the fourth dimension." + density = 0 + icon_state = "bluespace" + icon_closed = "bluespace" + icon_opened = "bluespaceopen" + storage_capacity = 60 + var/materials = list(MAT_METAL = 5000, MAT_PLASMA = 2500, MAT_TITANIUM = 500, MAT_BLUESPACE = 500) + +/obj/structure/closet/bluespace/CheckExit(atom/movable/AM) + UpdateTransparency(AM, loc) + return TRUE + +/obj/structure/closet/bluespace/proc/UpdateTransparency(atom/movable/AM, atom/location) + var/transparent = FALSE + for(var/atom/A in location) + if(A.density && A != src && A != AM) + transparent = TRUE + break + icon_opened = transparent ? "bluespaceopentrans" : "bluespaceopen" + icon_closed = transparent ? "bluespacetrans" : "bluespace" + icon_state = opened ? icon_opened : icon_closed + +/obj/structure/closet/bluespace/Crossed(atom/movable/AM, oldloc) + if(AM.density) + icon_state = opened ? "bluespaceopentrans" : "bluespacetrans" + +/obj/structure/closet/bluespace/Move(NewLoc, direct) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls. + var/turf/T = get_turf(NewLoc) + if(T.density) + return + for(var/atom/A in T.contents) + if(A.density && istype(A, /obj/machinery/door)) + return + UpdateTransparency(src, NewLoc) + forceMove(NewLoc) + +/obj/structure/closet/bluespace/close() + . = ..() + density = 0 diff --git a/code/game/objects/structures/crates_lockers/closets/coffin.dm b/code/game/objects/structures/crates_lockers/closets/coffin.dm index eb0958f03c68..b763796947b4 100644 --- a/code/game/objects/structures/crates_lockers/closets/coffin.dm +++ b/code/game/objects/structures/crates_lockers/closets/coffin.dm @@ -1,23 +1,23 @@ -/obj/structure/closet/coffin - name = "coffin" - desc = "It's a burial receptacle for the dearly departed." - icon_state = "coffin" - icon_closed = "coffin" - icon_opened = "coffin_open" - resistance_flags = FLAMMABLE - max_integrity = 70 - material_drop = /obj/item/stack/sheet/wood - -/obj/structure/closet/coffin/update_icon() - if(!opened) - icon_state = icon_closed - else - icon_state = icon_opened - -/obj/structure/closet/coffin/sarcophagus - name = "sarcophagus" - icon_state = "sarc" - icon_closed = "sarc" - icon_opened = "sarc_open" - sound = 'sound/effects/stonedoor_openclose.ogg' - material_drop = /obj/item/stack/sheet/mineral/sandstone \ No newline at end of file +/obj/structure/closet/coffin + name = "coffin" + desc = "It's a burial receptacle for the dearly departed." + icon_state = "coffin" + icon_closed = "coffin" + icon_opened = "coffin_open" + resistance_flags = FLAMMABLE + max_integrity = 70 + material_drop = /obj/item/stack/sheet/wood + +/obj/structure/closet/coffin/update_icon() + if(!opened) + icon_state = icon_closed + else + icon_state = icon_opened + +/obj/structure/closet/coffin/sarcophagus + name = "sarcophagus" + icon_state = "sarc" + icon_closed = "sarc" + icon_opened = "sarc_open" + sound = 'sound/effects/stonedoor_openclose.ogg' + material_drop = /obj/item/stack/sheet/mineral/sandstone diff --git a/code/game/objects/structures/crates_lockers/closets/crittercrate.dm b/code/game/objects/structures/crates_lockers/closets/crittercrate.dm index 6a295ca43071..8370048ecd00 100644 --- a/code/game/objects/structures/crates_lockers/closets/crittercrate.dm +++ b/code/game/objects/structures/crates_lockers/closets/crittercrate.dm @@ -1,7 +1,7 @@ -/obj/structure/closet/crate/critter - name = "critter crate" - desc = "A crate which can sustain life for a while." - icon_state = "critter" - icon_opened = "critteropen" - icon_closed = "critter" - material_drop = /obj/item/stack/sheet/wood \ No newline at end of file +/obj/structure/closet/crate/critter + name = "critter crate" + desc = "A crate which can sustain life for a while." + icon_state = "critter" + icon_opened = "critteropen" + icon_closed = "critter" + material_drop = /obj/item/stack/sheet/wood diff --git a/code/game/objects/structures/crates_lockers/closets/fitness.dm b/code/game/objects/structures/crates_lockers/closets/fitness.dm index 02751e5208e9..af8c3e463481 100644 --- a/code/game/objects/structures/crates_lockers/closets/fitness.dm +++ b/code/game/objects/structures/crates_lockers/closets/fitness.dm @@ -71,4 +71,4 @@ new /obj/item/clothing/suit/bluetag(src) new /obj/item/clothing/suit/bluetag(src) new /obj/item/clothing/suit/bluetag(src) - new /obj/item/clothing/head/helmet/bluetaghelm(src) \ No newline at end of file + new /obj/item/clothing/head/helmet/bluetaghelm(src) diff --git a/code/game/objects/structures/crates_lockers/closets/gimmick.dm b/code/game/objects/structures/crates_lockers/closets/gimmick.dm index 6f388b0bd40b..e570128383a0 100644 --- a/code/game/objects/structures/crates_lockers/closets/gimmick.dm +++ b/code/game/objects/structures/crates_lockers/closets/gimmick.dm @@ -1,136 +1,136 @@ -/obj/structure/closet/cabinet - name = "cabinet" - desc = "Old will forever be in fashion." - icon_state = "cabinet_closed" - icon_closed = "cabinet_closed" - icon_opened = "cabinet_open" - resistance_flags = FLAMMABLE - max_integrity = 70 - -/obj/structure/closet/cabinet/update_icon() - if(!opened) - icon_state = icon_closed - else - icon_state = icon_opened - -/obj/structure/closet/acloset - name = "strange closet" - desc = "It looks alien!" - icon_state = "acloset" - icon_closed = "acloset" - icon_opened = "aclosetopen" - - -/obj/structure/closet/gimmick - name = "administrative supply closet" - desc = "It's a storage unit for things that have no right being here." - icon_state = "syndicate1" - icon_closed = "syndicate1" - icon_opened = "syndicate1open" - anchored = 0 - -/obj/structure/closet/gimmick/russian - name = "russian surplus closet" - desc = "It's a storage unit for Russian standard-issue surplus." - icon_state = "syndicate1" - icon_closed = "syndicate1" - icon_opened = "syndicate1open" - -/obj/structure/closet/gimmick/russian/New() - ..() - new /obj/item/clothing/head/ushanka(src) - new /obj/item/clothing/head/ushanka(src) - new /obj/item/clothing/head/ushanka(src) - new /obj/item/clothing/head/ushanka(src) - new /obj/item/clothing/head/ushanka(src) - new /obj/item/clothing/under/soviet(src) - new /obj/item/clothing/under/soviet(src) - new /obj/item/clothing/under/soviet(src) - new /obj/item/clothing/under/soviet(src) - new /obj/item/clothing/under/soviet(src) - - -/obj/structure/closet/gimmick/tacticool - name = "tacticool gear closet" - desc = "It's a storage unit for Tacticool gear." - icon_state = "syndicate1" - icon_closed = "syndicate1" - icon_opened = "syndicate1open" - -/obj/structure/closet/gimmick/tacticool/New() - ..() - new /obj/item/clothing/glasses/eyepatch(src) - new /obj/item/clothing/glasses/sunglasses(src) - new /obj/item/clothing/gloves/combat(src) - new /obj/item/clothing/gloves/combat(src) - new /obj/item/clothing/head/helmet/swat(src) - new /obj/item/clothing/head/helmet/swat(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/shoes/combat/swat(src) - new /obj/item/clothing/shoes/combat/swat(src) - new /obj/item/clothing/suit/space/deathsquad(src) - new /obj/item/clothing/suit/space/deathsquad(src) - new /obj/item/clothing/under/syndicate/tacticool(src) - new /obj/item/clothing/under/syndicate/tacticool(src) - - -/obj/structure/closet/thunderdome - name = "\improper Thunderdome closet" - desc = "Everything you need!" - icon_state = "syndicate" - icon_closed = "syndicate" - icon_opened = "syndicateopen" - anchored = 1 - -/obj/structure/closet/thunderdome/tdred - name = "red-team Thunderdome closet" - -/obj/structure/closet/thunderdome/tdred/New() - ..() - new /obj/item/clothing/suit/armor/tdome/red(src) - new /obj/item/clothing/suit/armor/tdome/red(src) - new /obj/item/clothing/suit/armor/tdome/red(src) - new /obj/item/melee/energy/sword/saber(src) - new /obj/item/melee/energy/sword/saber(src) - new /obj/item/melee/energy/sword/saber(src) - new /obj/item/gun/energy/laser(src) - new /obj/item/gun/energy/laser(src) - new /obj/item/gun/energy/laser(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/clothing/head/helmet/thunderdome(src) - new /obj/item/clothing/head/helmet/thunderdome(src) - new /obj/item/clothing/head/helmet/thunderdome(src) - -/obj/structure/closet/thunderdome/tdgreen - name = "green-team Thunderdome closet" - icon_state = "syndicate1" - icon_closed = "syndicate1" - icon_opened = "syndicate1open" - -/obj/structure/closet/thunderdome/tdgreen/New() - ..() - new /obj/item/clothing/suit/armor/tdome/green(src) - new /obj/item/clothing/suit/armor/tdome/green(src) - new /obj/item/clothing/suit/armor/tdome/green(src) - new /obj/item/melee/energy/sword/saber(src) - new /obj/item/melee/energy/sword/saber(src) - new /obj/item/melee/energy/sword/saber(src) - new /obj/item/gun/energy/laser(src) - new /obj/item/gun/energy/laser(src) - new /obj/item/gun/energy/laser(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/clothing/head/helmet/thunderdome(src) - new /obj/item/clothing/head/helmet/thunderdome(src) - new /obj/item/clothing/head/helmet/thunderdome(src) - +/obj/structure/closet/cabinet + name = "cabinet" + desc = "Old will forever be in fashion." + icon_state = "cabinet_closed" + icon_closed = "cabinet_closed" + icon_opened = "cabinet_open" + resistance_flags = FLAMMABLE + max_integrity = 70 + +/obj/structure/closet/cabinet/update_icon() + if(!opened) + icon_state = icon_closed + else + icon_state = icon_opened + +/obj/structure/closet/acloset + name = "strange closet" + desc = "It looks alien!" + icon_state = "acloset" + icon_closed = "acloset" + icon_opened = "aclosetopen" + + +/obj/structure/closet/gimmick + name = "administrative supply closet" + desc = "It's a storage unit for things that have no right being here." + icon_state = "syndicate1" + icon_closed = "syndicate1" + icon_opened = "syndicate1open" + anchored = 0 + +/obj/structure/closet/gimmick/russian + name = "russian surplus closet" + desc = "It's a storage unit for Russian standard-issue surplus." + icon_state = "syndicate1" + icon_closed = "syndicate1" + icon_opened = "syndicate1open" + +/obj/structure/closet/gimmick/russian/New() + ..() + new /obj/item/clothing/head/ushanka(src) + new /obj/item/clothing/head/ushanka(src) + new /obj/item/clothing/head/ushanka(src) + new /obj/item/clothing/head/ushanka(src) + new /obj/item/clothing/head/ushanka(src) + new /obj/item/clothing/under/soviet(src) + new /obj/item/clothing/under/soviet(src) + new /obj/item/clothing/under/soviet(src) + new /obj/item/clothing/under/soviet(src) + new /obj/item/clothing/under/soviet(src) + + +/obj/structure/closet/gimmick/tacticool + name = "tacticool gear closet" + desc = "It's a storage unit for Tacticool gear." + icon_state = "syndicate1" + icon_closed = "syndicate1" + icon_opened = "syndicate1open" + +/obj/structure/closet/gimmick/tacticool/New() + ..() + new /obj/item/clothing/glasses/eyepatch(src) + new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/gloves/combat(src) + new /obj/item/clothing/gloves/combat(src) + new /obj/item/clothing/head/helmet/swat(src) + new /obj/item/clothing/head/helmet/swat(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/shoes/combat/swat(src) + new /obj/item/clothing/shoes/combat/swat(src) + new /obj/item/clothing/suit/space/deathsquad(src) + new /obj/item/clothing/suit/space/deathsquad(src) + new /obj/item/clothing/under/syndicate/tacticool(src) + new /obj/item/clothing/under/syndicate/tacticool(src) + + +/obj/structure/closet/thunderdome + name = "\improper Thunderdome closet" + desc = "Everything you need!" + icon_state = "syndicate" + icon_closed = "syndicate" + icon_opened = "syndicateopen" + anchored = 1 + +/obj/structure/closet/thunderdome/tdred + name = "red-team Thunderdome closet" + +/obj/structure/closet/thunderdome/tdred/New() + ..() + new /obj/item/clothing/suit/armor/tdome/red(src) + new /obj/item/clothing/suit/armor/tdome/red(src) + new /obj/item/clothing/suit/armor/tdome/red(src) + new /obj/item/melee/energy/sword/saber(src) + new /obj/item/melee/energy/sword/saber(src) + new /obj/item/melee/energy/sword/saber(src) + new /obj/item/gun/energy/laser(src) + new /obj/item/gun/energy/laser(src) + new /obj/item/gun/energy/laser(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/clothing/head/helmet/thunderdome(src) + new /obj/item/clothing/head/helmet/thunderdome(src) + new /obj/item/clothing/head/helmet/thunderdome(src) + +/obj/structure/closet/thunderdome/tdgreen + name = "green-team Thunderdome closet" + icon_state = "syndicate1" + icon_closed = "syndicate1" + icon_opened = "syndicate1open" + +/obj/structure/closet/thunderdome/tdgreen/New() + ..() + new /obj/item/clothing/suit/armor/tdome/green(src) + new /obj/item/clothing/suit/armor/tdome/green(src) + new /obj/item/clothing/suit/armor/tdome/green(src) + new /obj/item/melee/energy/sword/saber(src) + new /obj/item/melee/energy/sword/saber(src) + new /obj/item/melee/energy/sword/saber(src) + new /obj/item/gun/energy/laser(src) + new /obj/item/gun/energy/laser(src) + new /obj/item/gun/energy/laser(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/clothing/head/helmet/thunderdome(src) + new /obj/item/clothing/head/helmet/thunderdome(src) + new /obj/item/clothing/head/helmet/thunderdome(src) + diff --git a/code/game/objects/structures/crates_lockers/closets/job_closets.dm b/code/game/objects/structures/crates_lockers/closets/job_closets.dm index f1053ec917b5..448fd6feb343 100644 --- a/code/game/objects/structures/crates_lockers/closets/job_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/job_closets.dm @@ -1,144 +1,144 @@ -/* Closets for specific jobs - * Contains: - * Bartender - * Janitor - * Lawyer - */ - -/* - * Bartender - */ -/obj/structure/closet/gmcloset - name = "formal closet" - desc = "It's a storage unit for formal clothing." - icon_state = "black" - icon_closed = "black" - -/obj/structure/closet/gmcloset/New() - ..() - new /obj/item/clothing/head/that(src) - new /obj/item/clothing/head/that(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/clothing/head/hairflower - new /obj/item/clothing/under/sl_suit(src) - new /obj/item/clothing/under/sl_suit(src) - new /obj/item/clothing/under/rank/bartender(src) - new /obj/item/clothing/under/rank/bartender(src) - new /obj/item/clothing/under/dress/dress_saloon - new /obj/item/clothing/suit/wcoat(src) - new /obj/item/clothing/suit/wcoat(src) - new /obj/item/clothing/head/soft/black(src) - new /obj/item/clothing/head/soft/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - -/* - * Chef - */ -/obj/structure/closet/chefcloset - name = "chef's closet" - desc = "It's a storage unit for foodservice garments." - icon_state = "black" - icon_closed = "black" - -/obj/structure/closet/chefcloset/New() - ..() - new /obj/item/clothing/under/waiter(src) - new /obj/item/clothing/under/waiter(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/clothing/accessory/waistcoat(src) - new /obj/item/clothing/accessory/waistcoat(src) - new /obj/item/clothing/suit/chef/classic(src) - new /obj/item/clothing/suit/chef/classic(src) - new /obj/item/clothing/suit/chef/classic(src) - new /obj/item/clothing/head/soft/mime(src) - new /obj/item/clothing/head/soft/mime(src) - new /obj/item/storage/box/mousetraps(src) - new /obj/item/storage/box/mousetraps(src) - new /obj/item/clothing/under/rank/chef(src) - new /obj/item/clothing/head/chefhat(src) - new /obj/item/reagent_containers/glass/rag(src) - -/* - * Janitor - */ -/obj/structure/closet/jcloset - name = "custodial closet" - desc = "It's a storage unit for janitorial clothes and gear." - icon_state = "mixed" - icon_closed = "mixed" - -/obj/structure/closet/jcloset/New() - ..() - new /obj/item/clothing/under/rank/janitor(src) - new /obj/item/radio/headset/headset_service(src) - new /obj/item/cartridge/janitor(src) - new /obj/item/flashlight(src) - new /obj/item/melee/flyswatter(src) - new /obj/item/clothing/shoes/galoshes(src) - new /obj/item/soap(src) - new /obj/item/caution(src) - new /obj/item/caution(src) - new /obj/item/caution(src) - new /obj/item/caution(src) - new /obj/item/storage/bag/trash(src) - new /obj/item/lightreplacer(src) - new /obj/item/holosign_creator(src) - new /obj/item/clothing/gloves/color/black(src) - new /obj/item/clothing/head/soft/purple(src) - new /obj/item/watertank/janitor(src) - new /obj/item/storage/belt/janitor(src) - -/* - * Lawyer - */ -/obj/structure/closet/lawcloset - name = "legal closet" - desc = "It's a storage unit for courtroom apparel and items." - icon_state = "blue" - icon_closed = "blue" - -/obj/structure/closet/lawcloset/New() - ..() - new /obj/item/storage/box/tapes(src) - new /obj/item/book/manual/faxes(src) - new /obj/item/clothing/under/lawyer/female(src) - new /obj/item/clothing/under/lawyer/black(src) - new /obj/item/clothing/under/lawyer/red(src) - new /obj/item/clothing/under/lawyer/bluesuit(src) - new /obj/item/clothing/suit/storage/lawyer/bluejacket(src) - new /obj/item/clothing/under/lawyer/purpsuit(src) - new /obj/item/clothing/suit/storage/lawyer/purpjacket(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/glasses/sunglasses/big(src) - new /obj/item/clothing/glasses/sunglasses/big(src) - -//Paramedic - -/obj/structure/closet/paramedic - name = "paramedic wardrobe" - desc = "It's a storage unit for paramedic equipment." - icon_state = "blue" - icon_closed = "blue" - - -/obj/structure/closet/paramedic/New() - new /obj/item/clothing/under/rank/medical/paramedic(src) - new /obj/item/clothing/under/rank/medical/paramedic(src) - new /obj/item/radio/headset/headset_med(src) - new /obj/item/radio/headset/headset_med(src) - new /obj/item/clothing/head/soft/blue(src) - new /obj/item/clothing/head/soft/blue(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/head/soft/blue(src) - new /obj/item/clothing/head/soft/blue(src) - new /obj/item/clothing/suit/storage/paramedic(src) - new /obj/item/clothing/suit/storage/paramedic(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/tank/emergency_oxygen/engi(src) +/* Closets for specific jobs + * Contains: + * Bartender + * Janitor + * Lawyer + */ + +/* + * Bartender + */ +/obj/structure/closet/gmcloset + name = "formal closet" + desc = "It's a storage unit for formal clothing." + icon_state = "black" + icon_closed = "black" + +/obj/structure/closet/gmcloset/New() + ..() + new /obj/item/clothing/head/that(src) + new /obj/item/clothing/head/that(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/clothing/head/hairflower + new /obj/item/clothing/under/sl_suit(src) + new /obj/item/clothing/under/sl_suit(src) + new /obj/item/clothing/under/rank/bartender(src) + new /obj/item/clothing/under/rank/bartender(src) + new /obj/item/clothing/under/dress/dress_saloon + new /obj/item/clothing/suit/wcoat(src) + new /obj/item/clothing/suit/wcoat(src) + new /obj/item/clothing/head/soft/black(src) + new /obj/item/clothing/head/soft/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + +/* + * Chef + */ +/obj/structure/closet/chefcloset + name = "chef's closet" + desc = "It's a storage unit for foodservice garments." + icon_state = "black" + icon_closed = "black" + +/obj/structure/closet/chefcloset/New() + ..() + new /obj/item/clothing/under/waiter(src) + new /obj/item/clothing/under/waiter(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/clothing/accessory/waistcoat(src) + new /obj/item/clothing/accessory/waistcoat(src) + new /obj/item/clothing/suit/chef/classic(src) + new /obj/item/clothing/suit/chef/classic(src) + new /obj/item/clothing/suit/chef/classic(src) + new /obj/item/clothing/head/soft/mime(src) + new /obj/item/clothing/head/soft/mime(src) + new /obj/item/storage/box/mousetraps(src) + new /obj/item/storage/box/mousetraps(src) + new /obj/item/clothing/under/rank/chef(src) + new /obj/item/clothing/head/chefhat(src) + new /obj/item/reagent_containers/glass/rag(src) + +/* + * Janitor + */ +/obj/structure/closet/jcloset + name = "custodial closet" + desc = "It's a storage unit for janitorial clothes and gear." + icon_state = "mixed" + icon_closed = "mixed" + +/obj/structure/closet/jcloset/New() + ..() + new /obj/item/clothing/under/rank/janitor(src) + new /obj/item/radio/headset/headset_service(src) + new /obj/item/cartridge/janitor(src) + new /obj/item/flashlight(src) + new /obj/item/melee/flyswatter(src) + new /obj/item/clothing/shoes/galoshes(src) + new /obj/item/soap(src) + new /obj/item/caution(src) + new /obj/item/caution(src) + new /obj/item/caution(src) + new /obj/item/caution(src) + new /obj/item/storage/bag/trash(src) + new /obj/item/lightreplacer(src) + new /obj/item/holosign_creator(src) + new /obj/item/clothing/gloves/color/black(src) + new /obj/item/clothing/head/soft/purple(src) + new /obj/item/watertank/janitor(src) + new /obj/item/storage/belt/janitor(src) + +/* + * Lawyer + */ +/obj/structure/closet/lawcloset + name = "legal closet" + desc = "It's a storage unit for courtroom apparel and items." + icon_state = "blue" + icon_closed = "blue" + +/obj/structure/closet/lawcloset/New() + ..() + new /obj/item/storage/box/tapes(src) + new /obj/item/book/manual/faxes(src) + new /obj/item/clothing/under/lawyer/female(src) + new /obj/item/clothing/under/lawyer/black(src) + new /obj/item/clothing/under/lawyer/red(src) + new /obj/item/clothing/under/lawyer/bluesuit(src) + new /obj/item/clothing/suit/storage/lawyer/bluejacket(src) + new /obj/item/clothing/under/lawyer/purpsuit(src) + new /obj/item/clothing/suit/storage/lawyer/purpjacket(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/glasses/sunglasses/big(src) + new /obj/item/clothing/glasses/sunglasses/big(src) + +//Paramedic + +/obj/structure/closet/paramedic + name = "paramedic wardrobe" + desc = "It's a storage unit for paramedic equipment." + icon_state = "blue" + icon_closed = "blue" + + +/obj/structure/closet/paramedic/New() + new /obj/item/clothing/under/rank/medical/paramedic(src) + new /obj/item/clothing/under/rank/medical/paramedic(src) + new /obj/item/radio/headset/headset_med(src) + new /obj/item/radio/headset/headset_med(src) + new /obj/item/clothing/head/soft/blue(src) + new /obj/item/clothing/head/soft/blue(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/head/soft/blue(src) + new /obj/item/clothing/head/soft/blue(src) + new /obj/item/clothing/suit/storage/paramedic(src) + new /obj/item/clothing/suit/storage/paramedic(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/tank/emergency_oxygen/engi(src) diff --git a/code/game/objects/structures/crates_lockers/closets/l3closet.dm b/code/game/objects/structures/crates_lockers/closets/l3closet.dm index 0d550dce36d6..e29ce1bb11c7 100644 --- a/code/game/objects/structures/crates_lockers/closets/l3closet.dm +++ b/code/game/objects/structures/crates_lockers/closets/l3closet.dm @@ -1,76 +1,76 @@ -/obj/structure/closet/l3closet - name = "level-3 biohazard suit closet" - desc = "It's a storage unit for level-3 biohazard gear." - icon_state = "bio" - icon_closed = "bio" - icon_opened = "bioopen" - -/obj/structure/closet/l3closet/New() - ..() - new /obj/item/storage/bag/bio( src ) - new /obj/item/clothing/suit/bio_suit/general( src ) - new /obj/item/clothing/head/bio_hood/general( src ) - - -/obj/structure/closet/l3closet/general - icon_state = "bio_general" - icon_closed = "bio_general" - icon_opened = "bio_generalopen" - -/obj/structure/closet/l3closet/general/New() - ..() - contents = list() - new /obj/item/clothing/suit/bio_suit/general( src ) - new /obj/item/clothing/head/bio_hood/general( src ) - - -/obj/structure/closet/l3closet/virology - icon_state = "bio_virology" - icon_closed = "bio_virology" - icon_opened = "bio_virologyopen" - -/obj/structure/closet/l3closet/virology/New() - ..() - contents = list() - new /obj/item/storage/bag/bio( src ) - new /obj/item/clothing/suit/bio_suit/virology( src ) - new /obj/item/clothing/head/bio_hood/virology( src ) - new /obj/item/clothing/mask/breath(src) - new /obj/item/tank/oxygen(src) - - -/obj/structure/closet/l3closet/security - icon_state = "bio_security" - icon_closed = "bio_security" - icon_opened = "bio_securityopen" - -/obj/structure/closet/l3closet/security/New() - ..() - contents = list() - new /obj/item/clothing/suit/bio_suit/security( src ) - new /obj/item/clothing/head/bio_hood/security( src ) - - -/obj/structure/closet/l3closet/janitor - icon_state = "bio_janitor" - icon_closed = "bio_janitor" - icon_opened = "bio_janitoropen" - -/obj/structure/closet/l3closet/janitor/New() - ..() - contents = list() - new /obj/item/clothing/suit/bio_suit/janitor( src ) - new /obj/item/clothing/head/bio_hood/janitor( src ) - - -/obj/structure/closet/l3closet/scientist - icon_state = "bio_scientist" - icon_closed = "bio_scientist" - icon_opened = "bio_scientistopen" - -/obj/structure/closet/l3closet/scientist/New() - ..() - contents = list() - new /obj/item/storage/bag/bio( src ) - new /obj/item/clothing/suit/bio_suit/scientist( src ) - new /obj/item/clothing/head/bio_hood/scientist( src ) \ No newline at end of file +/obj/structure/closet/l3closet + name = "level-3 biohazard suit closet" + desc = "It's a storage unit for level-3 biohazard gear." + icon_state = "bio" + icon_closed = "bio" + icon_opened = "bioopen" + +/obj/structure/closet/l3closet/New() + ..() + new /obj/item/storage/bag/bio( src ) + new /obj/item/clothing/suit/bio_suit/general( src ) + new /obj/item/clothing/head/bio_hood/general( src ) + + +/obj/structure/closet/l3closet/general + icon_state = "bio_general" + icon_closed = "bio_general" + icon_opened = "bio_generalopen" + +/obj/structure/closet/l3closet/general/New() + ..() + contents = list() + new /obj/item/clothing/suit/bio_suit/general( src ) + new /obj/item/clothing/head/bio_hood/general( src ) + + +/obj/structure/closet/l3closet/virology + icon_state = "bio_virology" + icon_closed = "bio_virology" + icon_opened = "bio_virologyopen" + +/obj/structure/closet/l3closet/virology/New() + ..() + contents = list() + new /obj/item/storage/bag/bio( src ) + new /obj/item/clothing/suit/bio_suit/virology( src ) + new /obj/item/clothing/head/bio_hood/virology( src ) + new /obj/item/clothing/mask/breath(src) + new /obj/item/tank/oxygen(src) + + +/obj/structure/closet/l3closet/security + icon_state = "bio_security" + icon_closed = "bio_security" + icon_opened = "bio_securityopen" + +/obj/structure/closet/l3closet/security/New() + ..() + contents = list() + new /obj/item/clothing/suit/bio_suit/security( src ) + new /obj/item/clothing/head/bio_hood/security( src ) + + +/obj/structure/closet/l3closet/janitor + icon_state = "bio_janitor" + icon_closed = "bio_janitor" + icon_opened = "bio_janitoropen" + +/obj/structure/closet/l3closet/janitor/New() + ..() + contents = list() + new /obj/item/clothing/suit/bio_suit/janitor( src ) + new /obj/item/clothing/head/bio_hood/janitor( src ) + + +/obj/structure/closet/l3closet/scientist + icon_state = "bio_scientist" + icon_closed = "bio_scientist" + icon_opened = "bio_scientistopen" + +/obj/structure/closet/l3closet/scientist/New() + ..() + contents = list() + new /obj/item/storage/bag/bio( src ) + new /obj/item/clothing/suit/bio_suit/scientist( src ) + new /obj/item/clothing/head/bio_hood/scientist( src ) diff --git a/code/game/objects/structures/crates_lockers/closets/malfunction.dm b/code/game/objects/structures/crates_lockers/closets/malfunction.dm index cb7da3e33753..957fc6ef6238 100644 --- a/code/game/objects/structures/crates_lockers/closets/malfunction.dm +++ b/code/game/objects/structures/crates_lockers/closets/malfunction.dm @@ -1,15 +1,15 @@ - -/obj/structure/closet/malf/suits - desc = "It's a storage unit for operational gear." - icon_state = "syndicate" - icon_closed = "syndicate" - icon_opened = "syndicateopen" - -/obj/structure/closet/malf/suits/New() - ..() - new /obj/item/tank/jetpack/void(src) - new /obj/item/clothing/mask/breath(src) - new /obj/effect/nasavoidsuitspawner(src) - new /obj/item/crowbar(src) - new /obj/item/stock_parts/cell(src) - new /obj/item/multitool(src) \ No newline at end of file + +/obj/structure/closet/malf/suits + desc = "It's a storage unit for operational gear." + icon_state = "syndicate" + icon_closed = "syndicate" + icon_opened = "syndicateopen" + +/obj/structure/closet/malf/suits/New() + ..() + new /obj/item/tank/jetpack/void(src) + new /obj/item/clothing/mask/breath(src) + new /obj/effect/nasavoidsuitspawner(src) + new /obj/item/crowbar(src) + new /obj/item/stock_parts/cell(src) + new /obj/item/multitool(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/depot.dm b/code/game/objects/structures/crates_lockers/closets/secure/depot.dm index df208106334b..bba8dc2a866f 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/depot.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/depot.dm @@ -63,4 +63,4 @@ /obj/structure/closet/secure_closet/syndicate/depot/armory req_access = list(ACCESS_SYNDICATE) - is_armory = TRUE \ No newline at end of file + is_armory = TRUE diff --git a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm index 8642a50d01ba..7616aa908774 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/engineering.dm @@ -1,152 +1,152 @@ -/obj/structure/closet/secure_closet/engineering_chief - name = "chief engineer's locker" - req_access = list(ACCESS_CE) - icon_state = "securece1" - icon_closed = "securece" - icon_locked = "securece1" - icon_opened = "secureceopen" - icon_broken = "securecebroken" - icon_off = "secureceoff" - -/obj/structure/closet/secure_closet/engineering_chief/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/industrial(src) - else - new /obj/item/storage/backpack/satchel_eng(src) - new /obj/item/storage/backpack/duffel/engineering(src) - new /obj/item/clothing/head/beret/ce(src) - new /obj/item/areaeditor/blueprints(src) - new /obj/item/storage/box/permits(src) - new /obj/item/clothing/under/rank/chief_engineer(src) - new /obj/item/clothing/under/rank/chief_engineer/skirt(src) - new /obj/item/clothing/suit/mantle/chief_engineer(src) - new /obj/item/clothing/head/hardhat/white(src) - new /obj/item/clothing/glasses/welding/superior(src) - new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/tank/jetpack/suit(src) - new /obj/item/cartridge/ce(src) - new /obj/item/radio/headset/heads/ce(src) - new /obj/item/storage/toolbox/mechanical(src) - new /obj/item/clothing/suit/storage/hazardvest(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/multitool(src) - new /obj/item/holosign_creator/engineering(src) - new /obj/item/flash(src) - new /obj/item/clothing/head/beret/eng(src) - new /obj/item/door_remote/chief_engineer(src) - new /obj/item/rpd(src) - new /obj/item/reagent_containers/food/drinks/mug/ce(src) - new /obj/item/organ/internal/cyberimp/eyes/meson(src) - new /obj/item/clothing/accessory/medal/engineering(src) - new /obj/item/holosign_creator/atmos(src) - - -/obj/structure/closet/secure_closet/engineering_electrical - name = "electrical supplies locker" - req_access = list(ACCESS_ENGINE_EQUIP) - icon_state = "secureengelec1" - icon_closed = "secureengelec" - icon_locked = "secureengelec1" - icon_opened = "toolclosetopen" - icon_broken = "secureengelecbroken" - icon_off = "secureengelecoff" - -/obj/structure/closet/secure_closet/engineering_electrical/New() - ..() - new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/clothing/gloves/color/yellow(src) - new /obj/item/storage/toolbox/electrical(src) - new /obj/item/storage/toolbox/electrical(src) - new /obj/item/storage/toolbox/electrical(src) - new /obj/item/apc_electronics(src) - new /obj/item/apc_electronics(src) - new /obj/item/apc_electronics(src) - new /obj/item/multitool(src) - new /obj/item/multitool(src) - new /obj/item/multitool(src) - new /obj/item/clothing/head/beret/eng - - -/obj/structure/closet/secure_closet/engineering_welding - name = "welding supplies locker" - req_access = list(ACCESS_ENGINE_EQUIP) - icon_state = "secureengweld1" - icon_closed = "secureengweld" - icon_locked = "secureengweld1" - icon_opened = "toolclosetopen" - icon_broken = "secureengweldbroken" - icon_off = "secureengweldoff" - -/obj/structure/closet/secure_closet/engineering_welding/New() - ..() - new /obj/item/clothing/head/welding(src) - new /obj/item/clothing/head/welding(src) - new /obj/item/clothing/head/welding(src) - new /obj/item/weldingtool/largetank(src) - new /obj/item/weldingtool/largetank(src) - new /obj/item/weldingtool/largetank(src) - - -/obj/structure/closet/secure_closet/engineering_personal - name = "engineer's locker" - req_access = list(ACCESS_ENGINE_EQUIP) - icon_state = "secureeng1" - icon_closed = "secureeng" - icon_locked = "secureeng1" - icon_opened = "secureengopen" - icon_broken = "secureengbroken" - icon_off = "secureengoff" - -/obj/structure/closet/secure_closet/engineering_personal/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/industrial(src) - else - new /obj/item/storage/backpack/satchel_eng(src) - new /obj/item/storage/backpack/duffel/engineering(src) - new /obj/item/storage/toolbox/mechanical(src) - new /obj/item/holosign_creator/engineering(src) - new /obj/item/radio/headset/headset_eng(src) - new /obj/item/clothing/under/rank/engineer(src) - new /obj/item/clothing/under/rank/engineer/skirt(src) - new /obj/item/clothing/suit/storage/hazardvest(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/glasses/meson(src) - new /obj/item/cartridge/engineering(src) - new /obj/item/clothing/head/beret/eng(src) - - -/obj/structure/closet/secure_closet/atmos_personal - name = "technician's locker" - req_access = list(ACCESS_ATMOSPHERICS) - icon_state = "secureatm1" - icon_closed = "secureatm" - icon_locked = "secureatm1" - icon_opened = "secureatmopen" - icon_broken = "secureatmbroken" - icon_off = "secureatmoff" - -/obj/structure/closet/secure_closet/atmos_personal/New() - ..() - new /obj/item/radio/headset/headset_eng(src) - new /obj/item/cartridge/atmos(src) - new /obj/item/storage/toolbox/mechanical(src) - if(prob(50)) - new /obj/item/storage/backpack/industrial(src) - else - new /obj/item/storage/backpack/satchel_eng(src) - new /obj/item/storage/backpack/duffel/atmos(src) - new /obj/item/extinguisher(src) - new /obj/item/grenade/gas/oxygen(src) - new /obj/item/grenade/gas/oxygen(src) - new /obj/item/clothing/suit/storage/hazardvest(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/holosign_creator/atmos(src) - new /obj/item/watertank/atmos(src) - new /obj/item/clothing/suit/fire/atmos(src) - new /obj/item/clothing/head/hardhat/atmos(src) - new /obj/item/rpd(src) - new /obj/item/destTagger(src) +/obj/structure/closet/secure_closet/engineering_chief + name = "chief engineer's locker" + req_access = list(ACCESS_CE) + icon_state = "securece1" + icon_closed = "securece" + icon_locked = "securece1" + icon_opened = "secureceopen" + icon_broken = "securecebroken" + icon_off = "secureceoff" + +/obj/structure/closet/secure_closet/engineering_chief/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/industrial(src) + else + new /obj/item/storage/backpack/satchel_eng(src) + new /obj/item/storage/backpack/duffel/engineering(src) + new /obj/item/clothing/head/beret/ce(src) + new /obj/item/areaeditor/blueprints(src) + new /obj/item/storage/box/permits(src) + new /obj/item/clothing/under/rank/chief_engineer(src) + new /obj/item/clothing/under/rank/chief_engineer/skirt(src) + new /obj/item/clothing/suit/mantle/chief_engineer(src) + new /obj/item/clothing/head/hardhat/white(src) + new /obj/item/clothing/glasses/welding/superior(src) + new /obj/item/clothing/gloves/color/yellow(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/tank/jetpack/suit(src) + new /obj/item/cartridge/ce(src) + new /obj/item/radio/headset/heads/ce(src) + new /obj/item/storage/toolbox/mechanical(src) + new /obj/item/clothing/suit/storage/hazardvest(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/multitool(src) + new /obj/item/holosign_creator/engineering(src) + new /obj/item/flash(src) + new /obj/item/clothing/head/beret/eng(src) + new /obj/item/door_remote/chief_engineer(src) + new /obj/item/rpd(src) + new /obj/item/reagent_containers/food/drinks/mug/ce(src) + new /obj/item/organ/internal/cyberimp/eyes/meson(src) + new /obj/item/clothing/accessory/medal/engineering(src) + new /obj/item/holosign_creator/atmos(src) + + +/obj/structure/closet/secure_closet/engineering_electrical + name = "electrical supplies locker" + req_access = list(ACCESS_ENGINE_EQUIP) + icon_state = "secureengelec1" + icon_closed = "secureengelec" + icon_locked = "secureengelec1" + icon_opened = "toolclosetopen" + icon_broken = "secureengelecbroken" + icon_off = "secureengelecoff" + +/obj/structure/closet/secure_closet/engineering_electrical/New() + ..() + new /obj/item/clothing/gloves/color/yellow(src) + new /obj/item/clothing/gloves/color/yellow(src) + new /obj/item/storage/toolbox/electrical(src) + new /obj/item/storage/toolbox/electrical(src) + new /obj/item/storage/toolbox/electrical(src) + new /obj/item/apc_electronics(src) + new /obj/item/apc_electronics(src) + new /obj/item/apc_electronics(src) + new /obj/item/multitool(src) + new /obj/item/multitool(src) + new /obj/item/multitool(src) + new /obj/item/clothing/head/beret/eng + + +/obj/structure/closet/secure_closet/engineering_welding + name = "welding supplies locker" + req_access = list(ACCESS_ENGINE_EQUIP) + icon_state = "secureengweld1" + icon_closed = "secureengweld" + icon_locked = "secureengweld1" + icon_opened = "toolclosetopen" + icon_broken = "secureengweldbroken" + icon_off = "secureengweldoff" + +/obj/structure/closet/secure_closet/engineering_welding/New() + ..() + new /obj/item/clothing/head/welding(src) + new /obj/item/clothing/head/welding(src) + new /obj/item/clothing/head/welding(src) + new /obj/item/weldingtool/largetank(src) + new /obj/item/weldingtool/largetank(src) + new /obj/item/weldingtool/largetank(src) + + +/obj/structure/closet/secure_closet/engineering_personal + name = "engineer's locker" + req_access = list(ACCESS_ENGINE_EQUIP) + icon_state = "secureeng1" + icon_closed = "secureeng" + icon_locked = "secureeng1" + icon_opened = "secureengopen" + icon_broken = "secureengbroken" + icon_off = "secureengoff" + +/obj/structure/closet/secure_closet/engineering_personal/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/industrial(src) + else + new /obj/item/storage/backpack/satchel_eng(src) + new /obj/item/storage/backpack/duffel/engineering(src) + new /obj/item/storage/toolbox/mechanical(src) + new /obj/item/holosign_creator/engineering(src) + new /obj/item/radio/headset/headset_eng(src) + new /obj/item/clothing/under/rank/engineer(src) + new /obj/item/clothing/under/rank/engineer/skirt(src) + new /obj/item/clothing/suit/storage/hazardvest(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/glasses/meson(src) + new /obj/item/cartridge/engineering(src) + new /obj/item/clothing/head/beret/eng(src) + + +/obj/structure/closet/secure_closet/atmos_personal + name = "technician's locker" + req_access = list(ACCESS_ATMOSPHERICS) + icon_state = "secureatm1" + icon_closed = "secureatm" + icon_locked = "secureatm1" + icon_opened = "secureatmopen" + icon_broken = "secureatmbroken" + icon_off = "secureatmoff" + +/obj/structure/closet/secure_closet/atmos_personal/New() + ..() + new /obj/item/radio/headset/headset_eng(src) + new /obj/item/cartridge/atmos(src) + new /obj/item/storage/toolbox/mechanical(src) + if(prob(50)) + new /obj/item/storage/backpack/industrial(src) + else + new /obj/item/storage/backpack/satchel_eng(src) + new /obj/item/storage/backpack/duffel/atmos(src) + new /obj/item/extinguisher(src) + new /obj/item/grenade/gas/oxygen(src) + new /obj/item/grenade/gas/oxygen(src) + new /obj/item/clothing/suit/storage/hazardvest(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/holosign_creator/atmos(src) + new /obj/item/watertank/atmos(src) + new /obj/item/clothing/suit/fire/atmos(src) + new /obj/item/clothing/head/hardhat/atmos(src) + new /obj/item/rpd(src) + new /obj/item/destTagger(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index 817b8cf474a4..e8f7cd3edbe9 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -1,112 +1,112 @@ -/obj/structure/closet/secure_closet/freezer - desc = "It's a card-locked refrigerative storage unit. This one is lead-lined." - -/obj/structure/closet/secure_closet/freezer/update_icon() - if(broken) - icon_state = icon_broken - else - if(!opened) - if(locked) - icon_state = icon_locked - else - icon_state = icon_closed - if(welded) - overlays += "welded" - else - icon_state = icon_opened - -/obj/structure/closet/secure_closet/freezer/ex_act(var/severity) - // IF INDIANA JONES CAN DO IT SO CAN YOU - - // Bomb in here? (using same search as space transits searching for nuke disk) - var/list/bombs = search_contents_for(/obj/item/transfer_valve) - if(!isemptylist(bombs)) // You're fucked. - ..(severity) - - -/obj/structure/closet/secure_closet/freezer/kitchen - name = "kitchen cabinet" - req_access = list(ACCESS_KITCHEN) - -/obj/structure/closet/secure_closet/freezer/kitchen/New() - ..() - for(var/i in 1 to 3) - new /obj/item/reagent_containers/food/condiment/flour(src) - new /obj/item/reagent_containers/food/condiment/rice(src) - new /obj/item/reagent_containers/food/condiment/sugar(src) - - -/obj/structure/closet/secure_closet/freezer/kitchen/mining - req_access = list() - -/obj/structure/closet/secure_closet/freezer/kitchen/maintenance - name = "maintenance refrigerator" - desc = "This refrigerator looks quite dusty, is there anything edible still inside?" - req_access = list() - -/obj/structure/closet/secure_closet/freezer/kitchen/maintenance/New() - ..() - for(var/i = 0, i < 5, i++) - new /obj/item/reagent_containers/food/condiment/milk(src) - for(var/i = 0, i < 5, i++) - new /obj/item/reagent_containers/food/condiment/soymilk(src) - for(var/i = 0, i < 2, i++) - new /obj/item/storage/fancy/egg_box(src) - -/obj/structure/closet/secure_closet/freezer/meat - name = "meat fridge" - icon_state = "fridge1" - icon_closed = "fridge" - icon_locked = "fridge1" - icon_opened = "fridgeopen" - icon_broken = "fridgebroken" - icon_off = "fridge1" - -/obj/structure/closet/secure_closet/freezer/meat/New() - ..() - for(var/i in 1 to 4) - new /obj/item/reagent_containers/food/snacks/meat/monkey(src) - -/obj/structure/closet/secure_closet/freezer/meat/open - req_access = null - locked = FALSE - -/obj/structure/closet/secure_closet/freezer/fridge - name = "refrigerator" - icon_state = "fridge1" - icon_closed = "fridge" - icon_locked = "fridge1" - icon_opened = "fridgeopen" - icon_broken = "fridgebroken" - icon_off = "fridge1" - -/obj/structure/closet/secure_closet/freezer/fridge/New() - ..() - for(var/i in 1 to 5) - new /obj/item/reagent_containers/food/condiment/milk(src) - new /obj/item/reagent_containers/food/condiment/soymilk(src) - for(var/i in 1 to 2) - new /obj/item/storage/fancy/egg_box(src) - -/obj/structure/closet/secure_closet/freezer/fridge/open - req_access = null - locked = FALSE - -/obj/structure/closet/secure_closet/freezer/money - name = "freezer" - icon_state = "fridge1" - icon_closed = "fridge" - icon_locked = "fridge1" - icon_opened = "fridgeopen" - icon_broken = "fridgebroken" - icon_off = "fridge1" - req_access = list(ACCESS_HEADS_VAULT) - -/obj/structure/closet/secure_closet/freezer/money/New() - ..() - for(var/i in 1 to 3) - new /obj/item/stack/spacecash/c1000(src) - for(var/i in 1 to 5) - new /obj/item/stack/spacecash/c500(src) - for(var/i in 1 to 6) - new /obj/item/stack/spacecash/c200(src) +/obj/structure/closet/secure_closet/freezer + desc = "It's a card-locked refrigerative storage unit. This one is lead-lined." + +/obj/structure/closet/secure_closet/freezer/update_icon() + if(broken) + icon_state = icon_broken + else + if(!opened) + if(locked) + icon_state = icon_locked + else + icon_state = icon_closed + if(welded) + overlays += "welded" + else + icon_state = icon_opened + +/obj/structure/closet/secure_closet/freezer/ex_act(var/severity) + // IF INDIANA JONES CAN DO IT SO CAN YOU + + // Bomb in here? (using same search as space transits searching for nuke disk) + var/list/bombs = search_contents_for(/obj/item/transfer_valve) + if(!isemptylist(bombs)) // You're fucked. + ..(severity) + + +/obj/structure/closet/secure_closet/freezer/kitchen + name = "kitchen cabinet" + req_access = list(ACCESS_KITCHEN) + +/obj/structure/closet/secure_closet/freezer/kitchen/New() + ..() + for(var/i in 1 to 3) + new /obj/item/reagent_containers/food/condiment/flour(src) + new /obj/item/reagent_containers/food/condiment/rice(src) + new /obj/item/reagent_containers/food/condiment/sugar(src) + + +/obj/structure/closet/secure_closet/freezer/kitchen/mining + req_access = list() + +/obj/structure/closet/secure_closet/freezer/kitchen/maintenance + name = "maintenance refrigerator" + desc = "This refrigerator looks quite dusty, is there anything edible still inside?" + req_access = list() + +/obj/structure/closet/secure_closet/freezer/kitchen/maintenance/New() + ..() + for(var/i = 0, i < 5, i++) + new /obj/item/reagent_containers/food/condiment/milk(src) + for(var/i = 0, i < 5, i++) + new /obj/item/reagent_containers/food/condiment/soymilk(src) + for(var/i = 0, i < 2, i++) + new /obj/item/storage/fancy/egg_box(src) + +/obj/structure/closet/secure_closet/freezer/meat + name = "meat fridge" + icon_state = "fridge1" + icon_closed = "fridge" + icon_locked = "fridge1" + icon_opened = "fridgeopen" + icon_broken = "fridgebroken" + icon_off = "fridge1" + +/obj/structure/closet/secure_closet/freezer/meat/New() + ..() + for(var/i in 1 to 4) + new /obj/item/reagent_containers/food/snacks/meat/monkey(src) + +/obj/structure/closet/secure_closet/freezer/meat/open + req_access = null + locked = FALSE + +/obj/structure/closet/secure_closet/freezer/fridge + name = "refrigerator" + icon_state = "fridge1" + icon_closed = "fridge" + icon_locked = "fridge1" + icon_opened = "fridgeopen" + icon_broken = "fridgebroken" + icon_off = "fridge1" + +/obj/structure/closet/secure_closet/freezer/fridge/New() + ..() + for(var/i in 1 to 5) + new /obj/item/reagent_containers/food/condiment/milk(src) + new /obj/item/reagent_containers/food/condiment/soymilk(src) + for(var/i in 1 to 2) + new /obj/item/storage/fancy/egg_box(src) + +/obj/structure/closet/secure_closet/freezer/fridge/open + req_access = null + locked = FALSE + +/obj/structure/closet/secure_closet/freezer/money + name = "freezer" + icon_state = "fridge1" + icon_closed = "fridge" + icon_locked = "fridge1" + icon_opened = "fridgeopen" + icon_broken = "fridgebroken" + icon_off = "fridge1" + req_access = list(ACCESS_HEADS_VAULT) + +/obj/structure/closet/secure_closet/freezer/money/New() + ..() + for(var/i in 1 to 3) + new /obj/item/stack/spacecash/c1000(src) + for(var/i in 1 to 5) + new /obj/item/stack/spacecash/c500(src) + for(var/i in 1 to 6) + new /obj/item/stack/spacecash/c200(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm b/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm index 162680446bba..bb3a50c38b71 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/guncabinet.dm @@ -52,4 +52,4 @@ else if(locked) overlays += icon(src.icon,"locked") else - overlays += icon(src.icon,"open") \ No newline at end of file + overlays += icon(src.icon,"open") diff --git a/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm b/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm index afd82f34f78a..29664c656264 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/hydroponics.dm @@ -23,4 +23,4 @@ new /obj/item/clothing/mask/bandana/botany(src) new /obj/item/cultivator(src) new /obj/item/hatchet(src) - new /obj/item/storage/box/disks_plantgene(src) \ No newline at end of file + new /obj/item/storage/box/disks_plantgene(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm index 6fb30c476644..dfca5f0fa70d 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/medical.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/medical.dm @@ -1,295 +1,296 @@ -/obj/structure/closet/secure_closet/medical1 - name = "medicine closet" - desc = "Filled with medical junk." - icon_state = "medical1" - icon_closed = "medical" - icon_locked = "medical1" - icon_opened = "medicalopen" - icon_broken = "medicalbroken" - icon_off = "medicaloff" - req_access = list(ACCESS_MEDICAL) - -/obj/structure/closet/secure_closet/medical1/New() - ..() - new /obj/item/storage/box/autoinjectors(src) - new /obj/item/storage/box/syringes(src) - new /obj/item/storage/box/pillbottles(src) - new /obj/item/storage/box/patch_packs(src) - new /obj/item/storage/box/iv_bags(src) - new /obj/item/reagent_containers/dropper(src) - new /obj/item/reagent_containers/dropper(src) - new /obj/item/reagent_containers/glass/beaker(src) - new /obj/item/reagent_containers/glass/beaker(src) - new /obj/item/reagent_containers/glass/bottle/epinephrine(src) - new /obj/item/reagent_containers/glass/bottle/epinephrine(src) - new /obj/item/reagent_containers/glass/bottle/charcoal(src) - new /obj/item/reagent_containers/glass/bottle/charcoal(src) - - -/obj/structure/closet/secure_closet/medical2 - name = "anesthetic locker" - desc = "Used to knock people out." - icon_state = "medical1" - icon_closed = "medical" - icon_locked = "medical1" - icon_opened = "medicalopen" - icon_broken = "medicalbroken" - icon_off = "medicaloff" - req_access = list(ACCESS_SURGERY) - -/obj/structure/closet/secure_closet/medical2/New() - ..() - new /obj/item/tank/anesthetic(src) - new /obj/item/tank/anesthetic(src) - new /obj/item/tank/anesthetic(src) - new /obj/item/clothing/mask/breath/medical(src) - new /obj/item/clothing/mask/breath/medical(src) - new /obj/item/clothing/mask/breath/medical(src) - - -/obj/structure/closet/secure_closet/medical3 - name = "medical doctor's locker" - req_access = list(ACCESS_SURGERY) - icon_state = "securemed1" - icon_closed = "securemed" - icon_locked = "securemed1" - icon_opened = "securemedopen" - icon_broken = "securemedbroken" - icon_off = "securemedoff" - -/obj/structure/closet/secure_closet/medical3/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/medic(src) - else - new /obj/item/storage/backpack/satchel_med(src) - new /obj/item/storage/backpack/duffel/medical(src) - new /obj/item/clothing/under/rank/medical(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/radio/headset/headset_med(src) - new /obj/item/clothing/gloves/color/latex/nitrile(src) - new /obj/item/defibrillator/loaded(src) - new /obj/item/handheld_defibrillator(src) - new /obj/item/storage/belt/medical(src) - new /obj/item/clothing/glasses/hud/health(src) - new /obj/item/clothing/shoes/sandal/white(src) - - -//Exam Room -/obj/structure/closet/secure_closet/exam - name = "exam room closet" - desc = "Filled with exam room materials." - icon_state = "medical1" - icon_closed = "medical" - icon_locked = "medical1" - icon_opened = "medicalopen" - icon_broken = "medicalbroken" - icon_off = "medicaloff" - req_access = list(ACCESS_MEDICAL) - -/obj/structure/closet/secure_closet/exam/New() - ..() - new /obj/item/storage/box/syringes(src) - new /obj/item/reagent_containers/dropper(src) - new /obj/item/storage/belt/medical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/glasses/hud/health(src) - new /obj/item/clothing/gloves/color/latex/nitrile(src) - new /obj/item/clothing/accessory/stethoscope(src) - new /obj/item/flashlight/pen(src) - new /obj/item/storage/firstaid/regular(src) - new /obj/item/storage/firstaid/adv(src) - new /obj/item/storage/firstaid/brute(src) - new /obj/item/storage/firstaid/fire(src) - new /obj/item/storage/firstaid/o2(src) - new /obj/item/storage/firstaid/toxin(src) - - -// Psychiatrist's pill bottle -/obj/item/storage/pill_bottle/psychiatrist - name = "psychiatrist's pill bottle" - desc = "Contains various pills to calm or sedate patients." - wrapper_color = COLOR_PALE_BTL_GREEN - -/obj/item/storage/pill_bottle/psychiatrist/New() - ..() - new /obj/item/reagent_containers/food/pill/haloperidol(src) - new /obj/item/reagent_containers/food/pill/haloperidol(src) - new /obj/item/reagent_containers/food/pill/haloperidol(src) - new /obj/item/reagent_containers/food/pill/methamphetamine(src) - new /obj/item/reagent_containers/food/pill/methamphetamine(src) - new /obj/item/reagent_containers/food/pill/methamphetamine(src) - new /obj/item/reagent_containers/food/pill/patch/nicotine(src) - new /obj/item/reagent_containers/food/pill/patch/nicotine(src) - new /obj/item/reagent_containers/food/pill/patch/nicotine(src) - new /obj/item/reagent_containers/food/pill/hydrocodone(src) - new /obj/item/reagent_containers/food/pill/hydrocodone(src) - -/obj/structure/closet/secure_closet/psychiatrist - name = "psychiatrist's locker" - req_access = list(ACCESS_PSYCHIATRIST) - icon_state = "securemed1" - icon_closed = "securemed" - icon_locked = "securemed1" - icon_opened = "securemedopen" - icon_broken = "securemedbroken" - icon_off = "securemedoff" - -/obj/structure/closet/secure_closet/psychiatrist/New() - ..() - new /obj/item/clothing/suit/straight_jacket(src) - new /obj/item/reagent_containers/syringe(src) - new /obj/item/reagent_containers/glass/bottle/ether(src) - new /obj/item/storage/fancy/cigarettes/cigpack_med(src) - new /obj/item/storage/fancy/cigarettes/cigpack_med(src) - new /obj/item/storage/fancy/cigarettes/cigpack_med(src) - new /obj/item/storage/pill_bottle/psychiatrist(src) - new /obj/random/plushie(src) - for(var/i in 0 to 3) - var/candy = pick(subtypesof(/obj/item/reagent_containers/food/snacks/candy/fudge)) - new candy(src) - -/obj/structure/closet/secure_closet/CMO - name = "chief medical officer's locker" - req_access = list(ACCESS_CMO) - icon_state = "cmosecure1" - icon_closed = "cmosecure" - icon_locked = "cmosecure1" - icon_opened = "cmosecureopen" - icon_broken = "cmosecurebroken" - icon_off = "cmosecureoff" - -/obj/structure/closet/secure_closet/CMO/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/medic(src) - else - new /obj/item/storage/backpack/satchel_med(src) - new /obj/item/storage/backpack/duffel/medical(src) - new /obj/item/clothing/suit/bio_suit/cmo(src) - new /obj/item/clothing/head/bio_hood/cmo(src) - new /obj/item/clothing/shoes/white(src) - switch(pick("blue", "green", "purple")) - if("blue") - new /obj/item/clothing/under/rank/medical/blue(src) - new /obj/item/clothing/head/surgery/blue(src) - if("green") - new /obj/item/clothing/under/rank/medical/green(src) - new /obj/item/clothing/head/surgery/green(src) - if("purple") - new /obj/item/clothing/under/rank/medical/purple(src) - new /obj/item/clothing/head/surgery/purple(src) - new /obj/item/clothing/suit/storage/labcoat/cmo(src) - new /obj/item/clothing/under/rank/chief_medical_officer(src) - new /obj/item/clothing/suit/mantle/labcoat/chief_medical_officer(src) - new /obj/item/clothing/shoes/brown (src) - new /obj/item/radio/headset/heads/cmo(src) - new /obj/item/clothing/gloves/color/latex/nitrile(src) - new /obj/item/defibrillator/compact/loaded(src) - new /obj/item/handheld_defibrillator(src) - new /obj/item/storage/belt/medical(src) - new /obj/item/flash(src) - new /obj/item/reagent_containers/hypospray/CMO(src) - new /obj/item/organ/internal/cyberimp/eyes/hud/medical(src) - new /obj/item/door_remote/chief_medical_officer(src) - new /obj/item/reagent_containers/food/drinks/mug/cmo(src) - new /obj/item/clothing/accessory/medal/medical(src) - - -/obj/structure/closet/secure_closet/animal - name = "animal control locker" - req_access = list(ACCESS_SURGERY) - -/obj/structure/closet/secure_closet/animal/New() - ..() - new /obj/item/assembly/signaler(src) - new /obj/item/radio/electropack(src) - new /obj/item/radio/electropack(src) - new /obj/item/radio/electropack(src) - - -/obj/structure/closet/secure_closet/chemical - name = "chemical closet" - desc = "Store dangerous chemicals in here." - icon_state = "medical1" - icon_closed = "medical" - icon_locked = "medical1" - icon_opened = "medicalopen" - icon_broken = "medicalbroken" - icon_off = "medicaloff" - req_access = list(ACCESS_CHEMISTRY) - -/obj/structure/closet/secure_closet/chemical/New() - ..() - new /obj/item/storage/box/pillbottles(src) - new /obj/item/storage/box/pillbottles(src) - new /obj/item/storage/box/patch_packs(src) - new /obj/item/storage/box/patch_packs(src) - - -/obj/structure/closet/secure_closet/medical_wall - name = "first aid closet" - desc = "It's a secure wall-mounted storage unit for first aid supplies." - icon_state = "medical_wall_locked" - icon_closed = "medical_wall_unlocked" - icon_locked = "medical_wall_locked" - icon_opened = "medical_wall_open" - icon_broken = "medical_wall_spark" - icon_off = "medical_wall_off" - anchored = 1 - density = 0 - wall_mounted = 1 - req_access = list(ACCESS_MEDICAL) - -/obj/structure/closet/secure_closet/medical_wall/update_icon() - if(broken) - icon_state = icon_broken - else - if(!opened) - if(locked) - icon_state = icon_locked - else - icon_state = icon_closed - else - icon_state = icon_opened - -/obj/structure/closet/secure_closet/paramedic - name = "paramedic EVA gear" - desc = "A locker with a Paramedic EVA suit." - icon_state = "medical1" - icon_closed = "medical" - icon_locked = "medical1" - icon_opened = "medicalopen" - icon_broken = "medicalbroken" - icon_off = "medicaloff" - req_access = list(ACCESS_PARAMEDIC) - -/obj/structure/closet/secure_closet/paramedic/New() - ..() - new /obj/item/clothing/suit/space/eva/paramedic(src) - new /obj/item/clothing/head/helmet/space/eva/paramedic(src) - new /obj/item/sensor_device(src) - new /obj/item/key/ambulance(src) - new /obj/item/pinpointer/crew(src) - new /obj/item/handheld_defibrillator(src) - -/obj/structure/closet/secure_closet/reagents - name = "chemical storage closet" - desc = "Store dangerous chemicals in here." - icon_state = "chemical1" - icon_closed = "chemical" - icon_locked = "chemical1" - icon_opened = "medicalopen" - icon_broken = "chemicalbroken" - icon_off = "chemicaloff" - req_access = list(ACCESS_CHEMISTRY) - -/obj/structure/closet/secure_closet/reagents/New() - ..() - new /obj/item/reagent_containers/glass/bottle/reagent/phenol(src) - new /obj/item/reagent_containers/glass/bottle/reagent/ammonia(src) - new /obj/item/reagent_containers/glass/bottle/reagent/oil(src) - new /obj/item/reagent_containers/glass/bottle/reagent/acetone(src) - new /obj/item/reagent_containers/glass/bottle/reagent/acid(src) - new /obj/item/reagent_containers/glass/bottle/reagent/diethylamine(src) +/obj/structure/closet/secure_closet/medical1 + name = "medicine closet" + desc = "Filled with medical junk." + icon_state = "medical1" + icon_closed = "medical" + icon_locked = "medical1" + icon_opened = "medicalopen" + icon_broken = "medicalbroken" + icon_off = "medicaloff" + req_access = list(ACCESS_MEDICAL) + +/obj/structure/closet/secure_closet/medical1/New() + ..() + new /obj/item/storage/box/autoinjectors(src) + new /obj/item/storage/box/syringes(src) + new /obj/item/storage/box/pillbottles(src) + new /obj/item/storage/box/patch_packs(src) + new /obj/item/storage/box/iv_bags(src) + new /obj/item/reagent_containers/dropper(src) + new /obj/item/reagent_containers/dropper(src) + new /obj/item/reagent_containers/glass/beaker(src) + new /obj/item/reagent_containers/glass/beaker(src) + new /obj/item/reagent_containers/glass/bottle/epinephrine(src) + new /obj/item/reagent_containers/glass/bottle/epinephrine(src) + new /obj/item/reagent_containers/glass/bottle/charcoal(src) + new /obj/item/reagent_containers/glass/bottle/charcoal(src) + + +/obj/structure/closet/secure_closet/medical2 + name = "anesthetic locker" + desc = "Used to knock people out." + icon_state = "medical1" + icon_closed = "medical" + icon_locked = "medical1" + icon_opened = "medicalopen" + icon_broken = "medicalbroken" + icon_off = "medicaloff" + req_access = list(ACCESS_SURGERY) + +/obj/structure/closet/secure_closet/medical2/New() + ..() + new /obj/item/tank/anesthetic(src) + new /obj/item/tank/anesthetic(src) + new /obj/item/tank/anesthetic(src) + new /obj/item/clothing/mask/breath/medical(src) + new /obj/item/clothing/mask/breath/medical(src) + new /obj/item/clothing/mask/breath/medical(src) + + +/obj/structure/closet/secure_closet/medical3 + name = "medical doctor's locker" + req_access = list(ACCESS_SURGERY) + icon_state = "securemed1" + icon_closed = "securemed" + icon_locked = "securemed1" + icon_opened = "securemedopen" + icon_broken = "securemedbroken" + icon_off = "securemedoff" + +/obj/structure/closet/secure_closet/medical3/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/medic(src) + else + new /obj/item/storage/backpack/satchel_med(src) + new /obj/item/storage/backpack/duffel/medical(src) + new /obj/item/clothing/under/rank/medical(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/radio/headset/headset_med(src) + new /obj/item/clothing/gloves/color/latex/nitrile(src) + new /obj/item/defibrillator/loaded(src) + new /obj/item/handheld_defibrillator(src) + new /obj/item/handheld_defibrillator(src) + new /obj/item/storage/belt/medical(src) + new /obj/item/clothing/glasses/hud/health(src) + new /obj/item/clothing/shoes/sandal/white(src) + + +//Exam Room +/obj/structure/closet/secure_closet/exam + name = "exam room closet" + desc = "Filled with exam room materials." + icon_state = "medical1" + icon_closed = "medical" + icon_locked = "medical1" + icon_opened = "medicalopen" + icon_broken = "medicalbroken" + icon_off = "medicaloff" + req_access = list(ACCESS_MEDICAL) + +/obj/structure/closet/secure_closet/exam/New() + ..() + new /obj/item/storage/box/syringes(src) + new /obj/item/reagent_containers/dropper(src) + new /obj/item/storage/belt/medical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/glasses/hud/health(src) + new /obj/item/clothing/gloves/color/latex/nitrile(src) + new /obj/item/clothing/accessory/stethoscope(src) + new /obj/item/flashlight/pen(src) + new /obj/item/storage/firstaid/regular(src) + new /obj/item/storage/firstaid/adv(src) + new /obj/item/storage/firstaid/brute(src) + new /obj/item/storage/firstaid/fire(src) + new /obj/item/storage/firstaid/o2(src) + new /obj/item/storage/firstaid/toxin(src) + + +// Psychiatrist's pill bottle +/obj/item/storage/pill_bottle/psychiatrist + name = "psychiatrist's pill bottle" + desc = "Contains various pills to calm or sedate patients." + wrapper_color = COLOR_PALE_BTL_GREEN + +/obj/item/storage/pill_bottle/psychiatrist/New() + ..() + new /obj/item/reagent_containers/food/pill/haloperidol(src) + new /obj/item/reagent_containers/food/pill/haloperidol(src) + new /obj/item/reagent_containers/food/pill/haloperidol(src) + new /obj/item/reagent_containers/food/pill/methamphetamine(src) + new /obj/item/reagent_containers/food/pill/methamphetamine(src) + new /obj/item/reagent_containers/food/pill/methamphetamine(src) + new /obj/item/reagent_containers/food/pill/patch/nicotine(src) + new /obj/item/reagent_containers/food/pill/patch/nicotine(src) + new /obj/item/reagent_containers/food/pill/patch/nicotine(src) + new /obj/item/reagent_containers/food/pill/hydrocodone(src) + new /obj/item/reagent_containers/food/pill/hydrocodone(src) + +/obj/structure/closet/secure_closet/psychiatrist + name = "psychiatrist's locker" + req_access = list(ACCESS_PSYCHIATRIST) + icon_state = "securemed1" + icon_closed = "securemed" + icon_locked = "securemed1" + icon_opened = "securemedopen" + icon_broken = "securemedbroken" + icon_off = "securemedoff" + +/obj/structure/closet/secure_closet/psychiatrist/New() + ..() + new /obj/item/clothing/suit/straight_jacket(src) + new /obj/item/reagent_containers/syringe(src) + new /obj/item/reagent_containers/glass/bottle/ether(src) + new /obj/item/storage/fancy/cigarettes/cigpack_med(src) + new /obj/item/storage/fancy/cigarettes/cigpack_med(src) + new /obj/item/storage/fancy/cigarettes/cigpack_med(src) + new /obj/item/storage/pill_bottle/psychiatrist(src) + new /obj/random/plushie(src) + for(var/i in 0 to 3) + var/candy = pick(subtypesof(/obj/item/reagent_containers/food/snacks/candy/fudge)) + new candy(src) + +/obj/structure/closet/secure_closet/CMO + name = "chief medical officer's locker" + req_access = list(ACCESS_CMO) + icon_state = "cmosecure1" + icon_closed = "cmosecure" + icon_locked = "cmosecure1" + icon_opened = "cmosecureopen" + icon_broken = "cmosecurebroken" + icon_off = "cmosecureoff" + +/obj/structure/closet/secure_closet/CMO/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/medic(src) + else + new /obj/item/storage/backpack/satchel_med(src) + new /obj/item/storage/backpack/duffel/medical(src) + new /obj/item/clothing/suit/bio_suit/cmo(src) + new /obj/item/clothing/head/bio_hood/cmo(src) + new /obj/item/clothing/shoes/white(src) + switch(pick("blue", "green", "purple")) + if("blue") + new /obj/item/clothing/under/rank/medical/blue(src) + new /obj/item/clothing/head/surgery/blue(src) + if("green") + new /obj/item/clothing/under/rank/medical/green(src) + new /obj/item/clothing/head/surgery/green(src) + if("purple") + new /obj/item/clothing/under/rank/medical/purple(src) + new /obj/item/clothing/head/surgery/purple(src) + new /obj/item/clothing/suit/storage/labcoat/cmo(src) + new /obj/item/clothing/under/rank/chief_medical_officer(src) + new /obj/item/clothing/suit/mantle/labcoat/chief_medical_officer(src) + new /obj/item/clothing/shoes/brown (src) + new /obj/item/radio/headset/heads/cmo(src) + new /obj/item/clothing/gloves/color/latex/nitrile(src) + new /obj/item/defibrillator/compact/loaded(src) + new /obj/item/handheld_defibrillator(src) + new /obj/item/storage/belt/medical(src) + new /obj/item/flash(src) + new /obj/item/reagent_containers/hypospray/CMO(src) + new /obj/item/organ/internal/cyberimp/eyes/hud/medical(src) + new /obj/item/door_remote/chief_medical_officer(src) + new /obj/item/reagent_containers/food/drinks/mug/cmo(src) + new /obj/item/clothing/accessory/medal/medical(src) + + +/obj/structure/closet/secure_closet/animal + name = "animal control locker" + req_access = list(ACCESS_SURGERY) + +/obj/structure/closet/secure_closet/animal/New() + ..() + new /obj/item/assembly/signaler(src) + new /obj/item/radio/electropack(src) + new /obj/item/radio/electropack(src) + new /obj/item/radio/electropack(src) + + +/obj/structure/closet/secure_closet/chemical + name = "chemical closet" + desc = "Store dangerous chemicals in here." + icon_state = "medical1" + icon_closed = "medical" + icon_locked = "medical1" + icon_opened = "medicalopen" + icon_broken = "medicalbroken" + icon_off = "medicaloff" + req_access = list(ACCESS_CHEMISTRY) + +/obj/structure/closet/secure_closet/chemical/New() + ..() + new /obj/item/storage/box/pillbottles(src) + new /obj/item/storage/box/pillbottles(src) + new /obj/item/storage/box/patch_packs(src) + new /obj/item/storage/box/patch_packs(src) + + +/obj/structure/closet/secure_closet/medical_wall + name = "first aid closet" + desc = "It's a secure wall-mounted storage unit for first aid supplies." + icon_state = "medical_wall_locked" + icon_closed = "medical_wall_unlocked" + icon_locked = "medical_wall_locked" + icon_opened = "medical_wall_open" + icon_broken = "medical_wall_spark" + icon_off = "medical_wall_off" + anchored = 1 + density = 0 + wall_mounted = 1 + req_access = list(ACCESS_MEDICAL) + +/obj/structure/closet/secure_closet/medical_wall/update_icon() + if(broken) + icon_state = icon_broken + else + if(!opened) + if(locked) + icon_state = icon_locked + else + icon_state = icon_closed + else + icon_state = icon_opened + +/obj/structure/closet/secure_closet/paramedic + name = "paramedic EVA gear" + desc = "A locker with a Paramedic EVA suit." + icon_state = "medical1" + icon_closed = "medical" + icon_locked = "medical1" + icon_opened = "medicalopen" + icon_broken = "medicalbroken" + icon_off = "medicaloff" + req_access = list(ACCESS_PARAMEDIC) + +/obj/structure/closet/secure_closet/paramedic/New() + ..() + new /obj/item/clothing/suit/space/eva/paramedic(src) + new /obj/item/clothing/head/helmet/space/eva/paramedic(src) + new /obj/item/sensor_device(src) + new /obj/item/key/ambulance(src) + new /obj/item/pinpointer/crew(src) + new /obj/item/handheld_defibrillator(src) + +/obj/structure/closet/secure_closet/reagents + name = "chemical storage closet" + desc = "Store dangerous chemicals in here." + icon_state = "chemical1" + icon_closed = "chemical" + icon_locked = "chemical1" + icon_opened = "medicalopen" + icon_broken = "chemicalbroken" + icon_off = "chemicaloff" + req_access = list(ACCESS_CHEMISTRY) + +/obj/structure/closet/secure_closet/reagents/New() + ..() + new /obj/item/reagent_containers/glass/bottle/reagent/phenol(src) + new /obj/item/reagent_containers/glass/bottle/reagent/ammonia(src) + new /obj/item/reagent_containers/glass/bottle/reagent/oil(src) + new /obj/item/reagent_containers/glass/bottle/reagent/acetone(src) + new /obj/item/reagent_containers/glass/bottle/reagent/acid(src) + new /obj/item/reagent_containers/glass/bottle/reagent/diethylamine(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm index f5afa97e70f9..096133b6e0df 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/personal.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/personal.dm @@ -1,89 +1,89 @@ -/obj/structure/closet/secure_closet/personal - desc = "It's a secure locker for personnel. The first card swiped gains control." - name = "personal closet" - req_access = list(ACCESS_ALL_PERSONAL_LOCKERS) - var/registered_name = null - -/obj/structure/closet/secure_closet/personal/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/duffel(src) - if(prob(50)) - new /obj/item/storage/backpack(src) - else - new /obj/item/storage/backpack/satchel_norm(src) - new /obj/item/radio/headset( src ) - - -/obj/structure/closet/secure_closet/personal/patient - name = "patient's closet" - -/obj/structure/closet/secure_closet/personal/patient/New() - ..() - contents.Cut() - new /obj/item/clothing/under/color/white( src ) - new /obj/item/clothing/shoes/white( src ) - - - -/obj/structure/closet/secure_closet/personal/cabinet - icon_state = "cabinetdetective_locked" - icon_closed = "cabinetdetective" - icon_locked = "cabinetdetective_locked" - icon_opened = "cabinetdetective_open" - icon_broken = "cabinetdetective_broken" - icon_off = "cabinetdetective_broken" - resistance_flags = FLAMMABLE - max_integrity = 70 - -/obj/structure/closet/secure_closet/personal/cabinet/update_icon() - if(broken) - icon_state = icon_broken - else - if(!opened) - if(locked) - icon_state = icon_locked - else - icon_state = icon_closed - else - icon_state = icon_opened - -/obj/structure/closet/secure_closet/personal/cabinet/New() - ..() - contents.Cut() - new /obj/item/storage/backpack/satchel/withwallet( src ) - new /obj/item/radio/headset( src ) - -/obj/structure/closet/secure_closet/personal/attackby(obj/item/W as obj, mob/user as mob, params) - if(src.opened) - if(istype(W, /obj/item/grab)) - src.MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet - user.drop_item() - if(W) W.forceMove(loc) - else if(istype(W, /obj/item/card/id)) - if(src.broken) - to_chat(user, "It appears to be broken.") - return - var/obj/item/card/id/I = W - if(!I || !I.registered_name) return - if(src == user.loc) - to_chat(user, "You can't reach the lock from inside.") - else if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) - //they can open all lockers, or nobody owns this, or they own this locker - src.locked = !( src.locked ) - if(src.locked) - src.icon_state = src.icon_locked - else - src.icon_state = src.icon_closed - registered_name = null - desc = initial(desc) - - if(!src.registered_name && src.locked) - src.registered_name = I.registered_name - src.desc = "Owned by [I.registered_name]." - else - to_chat(user, "Access Denied") - else if((istype(W, /obj/item/card/emag) || istype(W, /obj/item/melee/energy/blade)) && !broken) - emag_act(user) - else - return ..() +/obj/structure/closet/secure_closet/personal + desc = "It's a secure locker for personnel. The first card swiped gains control." + name = "personal closet" + req_access = list(ACCESS_ALL_PERSONAL_LOCKERS) + var/registered_name = null + +/obj/structure/closet/secure_closet/personal/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/duffel(src) + if(prob(50)) + new /obj/item/storage/backpack(src) + else + new /obj/item/storage/backpack/satchel_norm(src) + new /obj/item/radio/headset( src ) + + +/obj/structure/closet/secure_closet/personal/patient + name = "patient's closet" + +/obj/structure/closet/secure_closet/personal/patient/New() + ..() + contents.Cut() + new /obj/item/clothing/under/color/white( src ) + new /obj/item/clothing/shoes/white( src ) + + + +/obj/structure/closet/secure_closet/personal/cabinet + icon_state = "cabinetdetective_locked" + icon_closed = "cabinetdetective" + icon_locked = "cabinetdetective_locked" + icon_opened = "cabinetdetective_open" + icon_broken = "cabinetdetective_broken" + icon_off = "cabinetdetective_broken" + resistance_flags = FLAMMABLE + max_integrity = 70 + +/obj/structure/closet/secure_closet/personal/cabinet/update_icon() + if(broken) + icon_state = icon_broken + else + if(!opened) + if(locked) + icon_state = icon_locked + else + icon_state = icon_closed + else + icon_state = icon_opened + +/obj/structure/closet/secure_closet/personal/cabinet/New() + ..() + contents.Cut() + new /obj/item/storage/backpack/satchel/withwallet( src ) + new /obj/item/radio/headset( src ) + +/obj/structure/closet/secure_closet/personal/attackby(obj/item/W as obj, mob/user as mob, params) + if(src.opened) + if(istype(W, /obj/item/grab)) + src.MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet + user.drop_item() + if(W) W.forceMove(loc) + else if(istype(W, /obj/item/card/id)) + if(src.broken) + to_chat(user, "It appears to be broken.") + return + var/obj/item/card/id/I = W + if(!I || !I.registered_name) return + if(src == user.loc) + to_chat(user, "You can't reach the lock from inside.") + else if(src.allowed(user) || !src.registered_name || (istype(I) && (src.registered_name == I.registered_name))) + //they can open all lockers, or nobody owns this, or they own this locker + src.locked = !( src.locked ) + if(src.locked) + src.icon_state = src.icon_locked + else + src.icon_state = src.icon_closed + registered_name = null + desc = initial(desc) + + if(!src.registered_name && src.locked) + src.registered_name = I.registered_name + src.desc = "Owned by [I.registered_name]." + else + to_chat(user, "Access Denied") + else if((istype(W, /obj/item/card/emag) || istype(W, /obj/item/melee/energy/blade)) && !broken) + emag_act(user) + else + return ..() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm index 5fff4e79296c..fd67afcf797f 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/scientist.dm @@ -1,108 +1,108 @@ -/obj/structure/closet/secure_closet/scientist - name = "scientist's locker" - req_access = list(ACCESS_TOX_STORAGE) - icon_state = "secureres1" - icon_closed = "secureres" - icon_locked = "secureres1" - icon_opened = "secureresopen" - icon_broken = "secureresbroken" - icon_off = "secureresoff" - -/obj/structure/closet/secure_closet/scientist/New() - ..() - new /obj/item/storage/backpack/science(src) - new /obj/item/storage/backpack/satchel_tox(src) - new /obj/item/clothing/under/rank/scientist(src) - new /obj/item/clothing/under/rank/scientist/skirt(src) - //new /obj/item/clothing/suit/labcoat/science(src) - new /obj/item/clothing/suit/storage/labcoat/science(src) - new /obj/item/clothing/shoes/white(src) -// new /obj/item/cartridge/signal/toxins(src) - new /obj/item/radio/headset/headset_sci(src) - new /obj/item/tank/air(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/shoes/sandal/white(src) - -/obj/structure/closet/secure_closet/roboticist - name = "roboticist's locker" - req_access = list(ACCESS_ROBOTICS) - icon_state = "secureres1" - icon_closed = "secureres" - icon_locked = "secureres1" - icon_opened = "secureresopen" - icon_broken = "secureresbroken" - icon_off = "secureresoff" - -/obj/structure/closet/secure_closet/roboticist/New() - ..() - new /obj/item/storage/backpack(src) - new /obj/item/storage/backpack(src) - new /obj/item/storage/backpack/satchel_norm(src) - new /obj/item/storage/backpack/satchel_norm(src) - new /obj/item/storage/backpack/duffel(src) - new /obj/item/storage/backpack/duffel(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/radio/headset/headset_sci(src) - new /obj/item/radio/headset/headset_sci(src) - new /obj/item/reagent_containers/food/drinks/oilcan(src) - new /obj/item/reagent_containers/food/drinks/oilcan(src) - -/obj/structure/closet/secure_closet/RD - name = "research director's locker" - req_access = list(ACCESS_RD) - icon_state = "rdsecure1" - icon_closed = "rdsecure" - icon_locked = "rdsecure1" - icon_opened = "rdsecureopen" - icon_broken = "rdsecurebroken" - icon_off = "rdsecureoff" - -/obj/structure/closet/secure_closet/RD/New() - ..() - new /obj/item/clothing/suit/bio_suit/scientist(src) - new /obj/item/clothing/head/bio_hood/scientist(src) - new /obj/item/clothing/under/rank/research_director(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/suit/mantle/labcoat(src) - new /obj/item/cartridge/rd(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/gloves/color/latex(src) - new /obj/item/radio/headset/heads/rd(src) - new /obj/item/tank/air(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/suit/armor/reactive/teleport(src) - new /obj/item/flash(src) - new /obj/item/laser_pointer(src) - new /obj/item/door_remote/research_director(src) - new /obj/item/reagent_containers/food/drinks/mug/rd(src) - new /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic(src) - new /obj/item/clothing/accessory/medal/science(src) - -/obj/structure/closet/secure_closet/research_reagents - name = "research chemical storage closet" - desc = "Store dangerous chemicals in here." - icon_state = "rchemical1" - icon_closed = "rchemical" - icon_locked = "rchemical1" - icon_opened = "medicalopen" - icon_broken = "rchemicalbroken" - icon_off = "rchemicaloff" - req_access = list(ACCESS_TOX_STORAGE) - -/obj/structure/closet/secure_closet/research_reagents/New() - ..() - new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) - new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) - new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) - new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) - new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) - new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) - new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) - new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) - new /obj/item/reagent_containers/glass/bottle/reagent/phenol(src) - new /obj/item/reagent_containers/glass/bottle/reagent/ammonia(src) - new /obj/item/reagent_containers/glass/bottle/reagent/oil(src) - new /obj/item/reagent_containers/glass/bottle/reagent/acetone(src) - new /obj/item/reagent_containers/glass/bottle/reagent/acid(src) - new /obj/item/reagent_containers/glass/bottle/reagent/diethylamine(src) +/obj/structure/closet/secure_closet/scientist + name = "scientist's locker" + req_access = list(ACCESS_TOX_STORAGE) + icon_state = "secureres1" + icon_closed = "secureres" + icon_locked = "secureres1" + icon_opened = "secureresopen" + icon_broken = "secureresbroken" + icon_off = "secureresoff" + +/obj/structure/closet/secure_closet/scientist/New() + ..() + new /obj/item/storage/backpack/science(src) + new /obj/item/storage/backpack/satchel_tox(src) + new /obj/item/clothing/under/rank/scientist(src) + new /obj/item/clothing/under/rank/scientist/skirt(src) + //new /obj/item/clothing/suit/labcoat/science(src) + new /obj/item/clothing/suit/storage/labcoat/science(src) + new /obj/item/clothing/shoes/white(src) +// new /obj/item/cartridge/signal/toxins(src) + new /obj/item/radio/headset/headset_sci(src) + new /obj/item/tank/air(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/shoes/sandal/white(src) + +/obj/structure/closet/secure_closet/roboticist + name = "roboticist's locker" + req_access = list(ACCESS_ROBOTICS) + icon_state = "secureres1" + icon_closed = "secureres" + icon_locked = "secureres1" + icon_opened = "secureresopen" + icon_broken = "secureresbroken" + icon_off = "secureresoff" + +/obj/structure/closet/secure_closet/roboticist/New() + ..() + new /obj/item/storage/backpack(src) + new /obj/item/storage/backpack(src) + new /obj/item/storage/backpack/satchel_norm(src) + new /obj/item/storage/backpack/satchel_norm(src) + new /obj/item/storage/backpack/duffel(src) + new /obj/item/storage/backpack/duffel(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/radio/headset/headset_sci(src) + new /obj/item/radio/headset/headset_sci(src) + new /obj/item/reagent_containers/food/drinks/oilcan(src) + new /obj/item/reagent_containers/food/drinks/oilcan(src) + +/obj/structure/closet/secure_closet/RD + name = "research director's locker" + req_access = list(ACCESS_RD) + icon_state = "rdsecure1" + icon_closed = "rdsecure" + icon_locked = "rdsecure1" + icon_opened = "rdsecureopen" + icon_broken = "rdsecurebroken" + icon_off = "rdsecureoff" + +/obj/structure/closet/secure_closet/RD/New() + ..() + new /obj/item/clothing/suit/bio_suit/scientist(src) + new /obj/item/clothing/head/bio_hood/scientist(src) + new /obj/item/clothing/under/rank/research_director(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/suit/mantle/labcoat(src) + new /obj/item/cartridge/rd(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/gloves/color/latex(src) + new /obj/item/radio/headset/heads/rd(src) + new /obj/item/tank/air(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/suit/armor/reactive/teleport(src) + new /obj/item/flash(src) + new /obj/item/laser_pointer(src) + new /obj/item/door_remote/research_director(src) + new /obj/item/reagent_containers/food/drinks/mug/rd(src) + new /obj/item/organ/internal/cyberimp/eyes/hud/diagnostic(src) + new /obj/item/clothing/accessory/medal/science(src) + +/obj/structure/closet/secure_closet/research_reagents + name = "research chemical storage closet" + desc = "Store dangerous chemicals in here." + icon_state = "rchemical1" + icon_closed = "rchemical" + icon_locked = "rchemical1" + icon_opened = "medicalopen" + icon_broken = "rchemicalbroken" + icon_off = "rchemicaloff" + req_access = list(ACCESS_TOX_STORAGE) + +/obj/structure/closet/secure_closet/research_reagents/New() + ..() + new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) + new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) + new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) + new /obj/item/reagent_containers/glass/bottle/reagent/morphine(src) + new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) + new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) + new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) + new /obj/item/reagent_containers/glass/bottle/reagent/insulin(src) + new /obj/item/reagent_containers/glass/bottle/reagent/phenol(src) + new /obj/item/reagent_containers/glass/bottle/reagent/ammonia(src) + new /obj/item/reagent_containers/glass/bottle/reagent/oil(src) + new /obj/item/reagent_containers/glass/bottle/reagent/acetone(src) + new /obj/item/reagent_containers/glass/bottle/reagent/acid(src) + new /obj/item/reagent_containers/glass/bottle/reagent/diethylamine(src) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm index 3ab0d94317b9..dbb3b8f283d6 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/secure_closets.dm @@ -1,179 +1,179 @@ -/obj/structure/closet/secure_closet - name = "secure locker" - desc = "It's an immobile card-locked storage unit." - icon = 'icons/obj/closet.dmi' - icon_state = "secure1" - density = 1 - opened = 0 - locked = 1 - broken = 0 - max_integrity = 250 - armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - damage_deflection = 20 - var/large = 1 - icon_closed = "secure" - var/icon_locked = "secure1" - icon_opened = "secureopen" - var/icon_broken = "securebroken" - var/icon_off = "secureoff" - wall_mounted = 0 //never solid (You can always pass over it) - -/obj/structure/closet/secure_closet/can_open() - if(!..()) - return 0 - if(locked) - return 0 - return ..() - -/obj/structure/closet/secure_closet/close() - if(..()) - if(broken) - icon_state = icon_off - return 1 - else - return 0 - -/obj/structure/closet/secure_closet/emp_act(severity) - for(var/obj/O in src) - O.emp_act(severity) - if(!broken) - if(prob(50/severity)) - locked = !locked - update_icon() - if(prob(20/severity) && !opened) - if(!locked) - open() - else - req_access = list() - req_access += pick(get_all_accesses()) - ..() - -/obj/structure/closet/secure_closet/proc/togglelock(mob/user) - if(opened) - to_chat(user, "Close the locker first.") - return - if(broken) - to_chat(user, "The locker appears to be broken.") - return - if(user.loc == src) - to_chat(user, "You can't reach the lock from inside.") - return - if(allowed(user)) - locked = !locked - playsound(loc, 'sound/machines/click.ogg', 15, 1, -3) - visible_message("The locker has been [locked ? null : "un"]locked by [user].") - update_icon() - else - to_chat(user, "Access Denied") - -/obj/structure/closet/secure_closet/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/rcs)) - return ..() - - if(opened) - if(istype(W, /obj/item/grab)) - if(large) - MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet - else - to_chat(user, "The locker is too small to stuff [W:affecting] into!") - if(isrobot(user)) - return - if(!user.drop_item()) //couldn't drop the item - to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!") - return - if(W) - W.forceMove(loc) - else if((istype(W, /obj/item/card/emag)||istype(W, /obj/item/melee/energy/blade)) && !broken) - emag_act(user) - else if(istype(W,/obj/item/stack/packageWrap) || istype(W,/obj/item/weldingtool)) - return ..(W, user) - else if(user.a_intent != INTENT_HARM) - togglelock(user) - else - return ..() - -/obj/structure/closet/secure_closet/emag_act(mob/user) - if(!broken) - broken = TRUE - locked = FALSE - icon_state = icon_off - flick(icon_broken, src) - to_chat(user, "You break the lock on \the [src].") - -/obj/structure/closet/secure_closet/attack_hand(mob/user) - add_fingerprint(user) - if(locked) - togglelock(user) - else - toggle(user) - -/obj/structure/closet/secure_closet/verb/verb_togglelock() - set src in oview(1) // One square distance - set category = "Object" - set name = "Toggle Lock" - - if(usr.incapacitated()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - - if(ishuman(usr)) - add_fingerprint(usr) - togglelock(usr) - else - to_chat(usr, "This mob type can't use this verb.") - -/obj/structure/closet/secure_closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot) - overlays.Cut() - if(!opened) - if(locked) - icon_state = icon_locked - else - icon_state = icon_closed - if(welded) - overlays += "welded" - else - icon_state = icon_opened - -/obj/structure/closet/secure_closet/container_resist(var/mob/living/L) - var/breakout_time = 2 //2 minutes by default - if(opened) - if(L.loc == src) - L.forceMove(get_turf(src)) // Let's just be safe here - return //Door's open... wait, why are you in it's contents then? - if(!locked && !welded) - return //It's a secure closet, but isn't locked. Easily escapable from, no need to 'resist' - - //okay, so the closet is either welded or locked... resist!!! - L.changeNext_move(CLICK_CD_BREAKOUT) - L.last_special = world.time + CLICK_CD_BREAKOUT - to_chat(L, "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)") - for(var/mob/O in viewers(src)) - O.show_message("The [src] begins to shake violently!", 1) - - - spawn(0) - if(do_after(usr,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds - if(!src || !L || L.stat != CONSCIOUS || L.loc != src || opened) //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened - return - - //Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'... - if(!locked && !welded) - return - - //Well then break it! - desc = "It appears to be broken." - icon_state = icon_off - flick(icon_broken, src) - sleep(10) - flick(icon_broken, src) - sleep(10) - broken = 1 - locked = 0 - welded = 0 - update_icon() - to_chat(usr, "You successfully break out!") - for(var/mob/O in viewers(L.loc)) - O.show_message("\the [usr] successfully broke out of \the [src]!", 1) - if(istype(loc, /obj/structure/bigDelivery)) //Do this to prevent contents from being opened into nullspace (read: bluespace) - var/obj/structure/bigDelivery/BD = loc - BD.attack_hand(usr) - open() +/obj/structure/closet/secure_closet + name = "secure locker" + desc = "It's an immobile card-locked storage unit." + icon = 'icons/obj/closet.dmi' + icon_state = "secure1" + density = 1 + opened = 0 + locked = 1 + broken = 0 + max_integrity = 250 + armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + damage_deflection = 20 + var/large = 1 + icon_closed = "secure" + var/icon_locked = "secure1" + icon_opened = "secureopen" + var/icon_broken = "securebroken" + var/icon_off = "secureoff" + wall_mounted = 0 //never solid (You can always pass over it) + +/obj/structure/closet/secure_closet/can_open() + if(!..()) + return 0 + if(locked) + return 0 + return ..() + +/obj/structure/closet/secure_closet/close() + if(..()) + if(broken) + icon_state = icon_off + return 1 + else + return 0 + +/obj/structure/closet/secure_closet/emp_act(severity) + for(var/obj/O in src) + O.emp_act(severity) + if(!broken) + if(prob(50/severity)) + locked = !locked + update_icon() + if(prob(20/severity) && !opened) + if(!locked) + open() + else + req_access = list() + req_access += pick(get_all_accesses()) + ..() + +/obj/structure/closet/secure_closet/proc/togglelock(mob/user) + if(opened) + to_chat(user, "Close the locker first.") + return + if(broken) + to_chat(user, "The locker appears to be broken.") + return + if(user.loc == src) + to_chat(user, "You can't reach the lock from inside.") + return + if(allowed(user)) + locked = !locked + playsound(loc, 'sound/machines/click.ogg', 15, 1, -3) + visible_message("The locker has been [locked ? null : "un"]locked by [user].") + update_icon() + else + to_chat(user, "Access Denied") + +/obj/structure/closet/secure_closet/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/rcs)) + return ..() + + if(opened) + if(istype(W, /obj/item/grab)) + if(large) + MouseDrop_T(W:affecting, user) //act like they were dragged onto the closet + else + to_chat(user, "The locker is too small to stuff [W:affecting] into!") + if(isrobot(user)) + return + if(!user.drop_item()) //couldn't drop the item + to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!") + return + if(W) + W.forceMove(loc) + else if((istype(W, /obj/item/card/emag)||istype(W, /obj/item/melee/energy/blade)) && !broken) + emag_act(user) + else if(istype(W,/obj/item/stack/packageWrap) || istype(W,/obj/item/weldingtool)) + return ..(W, user) + else if(user.a_intent != INTENT_HARM) + togglelock(user) + else + return ..() + +/obj/structure/closet/secure_closet/emag_act(mob/user) + if(!broken) + broken = TRUE + locked = FALSE + icon_state = icon_off + flick(icon_broken, src) + to_chat(user, "You break the lock on \the [src].") + +/obj/structure/closet/secure_closet/attack_hand(mob/user) + add_fingerprint(user) + if(locked) + togglelock(user) + else + toggle(user) + +/obj/structure/closet/secure_closet/verb/verb_togglelock() + set src in oview(1) // One square distance + set category = "Object" + set name = "Toggle Lock" + + if(usr.incapacitated()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + + if(ishuman(usr)) + add_fingerprint(usr) + togglelock(usr) + else + to_chat(usr, "This mob type can't use this verb.") + +/obj/structure/closet/secure_closet/update_icon()//Putting the welded stuff in updateicon() so it's easy to overwrite for special cases (Fridges, cabinets, and whatnot) + overlays.Cut() + if(!opened) + if(locked) + icon_state = icon_locked + else + icon_state = icon_closed + if(welded) + overlays += "welded" + else + icon_state = icon_opened + +/obj/structure/closet/secure_closet/container_resist(var/mob/living/L) + var/breakout_time = 2 //2 minutes by default + if(opened) + if(L.loc == src) + L.forceMove(get_turf(src)) // Let's just be safe here + return //Door's open... wait, why are you in it's contents then? + if(!locked && !welded) + return //It's a secure closet, but isn't locked. Easily escapable from, no need to 'resist' + + //okay, so the closet is either welded or locked... resist!!! + L.changeNext_move(CLICK_CD_BREAKOUT) + L.last_special = world.time + CLICK_CD_BREAKOUT + to_chat(L, "You lean on the back of \the [src] and start pushing the door open. (this will take about [breakout_time] minutes)") + for(var/mob/O in viewers(src)) + O.show_message("The [src] begins to shake violently!", 1) + + + spawn(0) + if(do_after(usr,(breakout_time*60*10), target = src)) //minutes * 60seconds * 10deciseconds + if(!src || !L || L.stat != CONSCIOUS || L.loc != src || opened) //closet/user destroyed OR user dead/unconcious OR user no longer in closet OR closet opened + return + + //Perform the same set of checks as above for weld and lock status to determine if there is even still a point in 'resisting'... + if(!locked && !welded) + return + + //Well then break it! + desc = "It appears to be broken." + icon_state = icon_off + flick(icon_broken, src) + sleep(10) + flick(icon_broken, src) + sleep(10) + broken = 1 + locked = 0 + welded = 0 + update_icon() + to_chat(usr, "You successfully break out!") + for(var/mob/O in viewers(L.loc)) + O.show_message("\the [usr] successfully broke out of \the [src]!", 1) + if(istype(loc, /obj/structure/bigDelivery)) //Do this to prevent contents from being opened into nullspace (read: bluespace) + var/obj/structure/bigDelivery/BD = loc + BD.attack_hand(usr) + open() diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index e66cf96c40b4..6d4b47df6abf 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -1,468 +1,468 @@ -/obj/structure/closet/secure_closet/captains - name = "captain's locker" - req_access = list(ACCESS_CAPTAIN) - icon_state = "capsecure1" - icon_closed = "capsecure" - icon_locked = "capsecure1" - icon_opened = "capsecureopen" - icon_broken = "capsecurebroken" - icon_off = "capsecureoff" - -/obj/structure/closet/secure_closet/captains/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/captain(src) - else - new /obj/item/storage/backpack/satchel_cap(src) - new /obj/item/book/manual/faxes(src) - new /obj/item/storage/backpack/duffel/captain(src) - new /obj/item/clothing/suit/captunic(src) - new /obj/item/clothing/suit/captunic/capjacket(src) - new /obj/item/clothing/suit/mantle/armor/captain(src) - new /obj/item/clothing/under/captainparade(src) - new /obj/item/clothing/head/caphat/parade(src) - new /obj/item/clothing/under/rank/captain(src) - new /obj/item/clothing/under/dress/dress_cap(src) - new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src) - new /obj/item/cartridge/captain(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/laceup(src) - new /obj/item/radio/headset/heads/captain/alt(src) - new /obj/item/clothing/gloves/color/captain(src) - new /obj/item/storage/belt/rapier(src) - new /obj/item/gun/energy/gun(src) - new /obj/item/door_remote/captain(src) - new /obj/item/reagent_containers/food/drinks/mug/cap(src) - new /obj/item/tank/emergency_oxygen/double(src) - - -/obj/structure/closet/secure_closet/hop - name = "head of personnel's locker" - req_access = list(ACCESS_HOP) - icon_state = "hopsecure1" - icon_closed = "hopsecure" - icon_locked = "hopsecure1" - icon_opened = "hopsecureopen" - icon_broken = "hopsecurebroken" - icon_off = "hopsecureoff" - -/obj/structure/closet/secure_closet/hop/New() - ..() - new /obj/item/clothing/glasses/sunglasses(src) - new /obj/item/clothing/head/hopcap(src) - new /obj/item/cartridge/hop(src) - new /obj/item/radio/headset/heads/hop(src) - new /obj/item/storage/box/ids(src) - new /obj/item/storage/box/PDAs(src) - new /obj/item/clothing/suit/armor/vest(src) - new /obj/item/gun/energy/gun/mini(src) - new /obj/item/flash(src) - new /obj/item/clothing/accessory/petcollar(src) - new /obj/item/door_remote/civillian(src) - new /obj/item/reagent_containers/food/drinks/mug/hop(src) - new /obj/item/clothing/accessory/medal/service(src) - -/obj/structure/closet/secure_closet/hop2 - name = "head of personnel's attire" - req_access = list(ACCESS_HOP) - icon_state = "hopsecure1" - icon_closed = "hopsecure" - icon_locked = "hopsecure1" - icon_opened = "hopsecureopen" - icon_broken = "hopsecurebroken" - icon_off = "hopsecureoff" - -/obj/structure/closet/secure_closet/hop2/New() - ..() - new /obj/item/clothing/under/rank/head_of_personnel(src) - new /obj/item/clothing/suit/mantle/armor/head_of_personnel(src) - new /obj/item/clothing/under/dress/dress_hop(src) - new /obj/item/clothing/under/dress/dress_hr(src) - new /obj/item/clothing/under/lawyer/female(src) - new /obj/item/clothing/under/lawyer/black(src) - new /obj/item/clothing/under/lawyer/red(src) - new /obj/item/clothing/under/lawyer/oldman(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/leather(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/under/rank/head_of_personnel_whimsy(src) - - -/obj/structure/closet/secure_closet/hos - name = "head of security's locker" - req_access = list(ACCESS_HOS) - icon_state = "hossecure1" - icon_closed = "hossecure" - icon_locked = "hossecure1" - icon_opened = "hossecureopen" - icon_broken = "hossecurebroken" - icon_off = "hossecureoff" - -/obj/structure/closet/secure_closet/hos/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/security(src) - else - new /obj/item/storage/backpack/satchel_sec(src) - new /obj/item/cartridge/hos(src) - new /obj/item/radio/headset/heads/hos/alt(src) - new /obj/item/clothing/under/rank/head_of_security(src) - new /obj/item/clothing/under/rank/head_of_security/formal(src) - new /obj/item/clothing/under/rank/head_of_security/corp(src) - new /obj/item/clothing/under/rank/head_of_security/skirt(src) - new /obj/item/clothing/suit/armor/hos(src) - new /obj/item/clothing/suit/armor/hos/alt(src) - new /obj/item/clothing/head/HoS(src) - new /obj/item/clothing/head/HoS/beret(src) - new /obj/item/clothing/suit/mantle/armor(src) - new /obj/item/clothing/glasses/hud/security/sunglasses(src) - new /obj/item/storage/lockbox/mindshield(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/holosign_creator/security(src) - new /obj/item/clothing/mask/gas/sechailer/hos(src) - new /obj/item/shield/riot/tele(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/storage/belt/security/sec(src) - new /obj/item/gun/energy/gun/hos(src) - new /obj/item/door_remote/head_of_security(src) - new /obj/item/reagent_containers/food/drinks/mug/hos(src) - new /obj/item/organ/internal/cyberimp/eyes/hud/security(src) - new /obj/item/clothing/accessory/medal/security(src) - -/obj/structure/closet/secure_closet/warden - name = "warden's locker" - req_access = list(ACCESS_ARMORY) - icon_state = "wardensecure1" - icon_closed = "wardensecure" - icon_locked = "wardensecure1" - icon_opened = "wardensecureopen" - icon_broken = "wardensecurebroken" - icon_off = "wardensecureoff" - -/obj/structure/closet/secure_closet/warden/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/security(src) - else - new /obj/item/storage/backpack/satchel_sec(src) - new /obj/item/radio/headset/headset_sec/alt(src) - new /obj/item/clothing/suit/armor/vest/warden(src) - new /obj/item/clothing/head/warden(src) - new /obj/item/clothing/suit/armor/vest/warden/alt(src) - new /obj/item/clothing/head/beret/sec/warden(src) - new /obj/item/clothing/under/rank/warden(src) - new /obj/item/clothing/under/rank/warden/formal(src) - new /obj/item/clothing/under/rank/warden/corp(src) - new /obj/item/clothing/under/rank/warden/skirt(src) - new /obj/item/clothing/glasses/hud/security/sunglasses(src) - new /obj/item/holosign_creator/security(src) - new /obj/item/clothing/mask/gas/sechailer/warden(src) - new /obj/item/storage/box/zipties(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/reagent_containers/spray/pepper(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/gun/energy/gun/advtaser(src) - new /obj/item/storage/belt/security/sec(src) - new /obj/item/storage/box/holobadge(src) - new /obj/item/clothing/gloves/color/black/krav_maga/sec(src) - - -/obj/structure/closet/secure_closet/security - name = "security officer's locker" - req_access = list(ACCESS_SECURITY) - icon_state = "sec1" - icon_closed = "sec" - icon_locked = "sec1" - icon_opened = "secopen" - icon_broken = "secbroken" - icon_off = "secoff" - -/obj/structure/closet/secure_closet/security/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/security(src) - else - new /obj/item/storage/backpack/satchel_sec(src) - new /obj/item/clothing/suit/armor/vest/security(src) - new /obj/item/radio/headset/headset_sec/alt(src) - new /obj/item/clothing/head/soft/sec(src) - new /obj/item/reagent_containers/spray/pepper(src) - new /obj/item/flash(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/storage/belt/security/sec(src) - new /obj/item/holosign_creator/security(src) - new /obj/item/clothing/mask/gas/sechailer(src) - new /obj/item/clothing/glasses/hud/security/sunglasses(src) - new /obj/item/clothing/head/helmet(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/clothing/suit/armor/secjacket(src) - - -/obj/structure/closet/secure_closet/brigdoc - name = "brig physician's locker" - req_access = list(ACCESS_SECURITY) - icon_state = "securemed1" - icon_closed = "securemed" - icon_locked = "securemed1" - icon_opened = "securemedopen" - icon_broken = "securemedbroken" - icon_off = "securemedoff" - -/obj/structure/closet/secure_closet/brigdoc/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/medic(src) - else - new /obj/item/storage/backpack/satchel_med(src) - new /obj/item/reagent_containers/spray/pepper(src) - new /obj/item/flash(src) - new /obj/item/storage/firstaid/regular(src) - new /obj/item/storage/firstaid/fire(src) - new /obj/item/storage/firstaid/adv(src) - new /obj/item/storage/firstaid/o2(src) - new /obj/item/storage/firstaid/toxin(src) - new /obj/item/clothing/suit/storage/brigdoc(src) - new /obj/item/clothing/under/rank/security/brigphys(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/radio/headset/headset_sec/alt(src) - new /obj/item/clothing/shoes/sandal/white(src) - - -/obj/structure/closet/secure_closet/blueshield - name = "blueshield's locker" - req_access = list(ACCESS_BLUESHIELD) - icon_state = "bssecure1" - icon_closed = "bssecure" - icon_locked = "bssecure1" - icon_opened = "bssecureopen" - icon_broken = "bssecurebroken" - icon_off = "bssecureoff" - -/obj/structure/closet/secure_closet/blueshield/New() - ..() - new /obj/item/storage/briefcase(src) - new /obj/item/storage/firstaid/adv(src) - new /obj/item/pinpointer/crew(src) - new /obj/item/storage/belt/security/sec(src) - new /obj/item/grenade/flashbang(src) - new /obj/item/flash(src) - new /obj/item/restraints/handcuffs(src) - new /obj/item/melee/baton/loaded(src) - new /obj/item/clothing/glasses/sunglasses(src) - new /obj/item/clothing/glasses/hud/security/sunglasses/read_only(src) - new /obj/item/clothing/glasses/hud/health/sunglasses - new /obj/item/clothing/head/beret/centcom/officer(src) - new /obj/item/clothing/head/beret/centcom/officer/navy(src) - new /obj/item/clothing/suit/armor/vest/blueshield(src) - new /obj/item/clothing/suit/storage/blueshield(src) - new /obj/item/clothing/shoes/centcom(src) - new /obj/item/clothing/accessory/holster(src) - new /obj/item/clothing/accessory/blue(src) - new /obj/item/clothing/shoes/jackboots/jacksandals(src) - new /obj/item/clothing/under/rank/centcom/blueshield(src) - - -/obj/structure/closet/secure_closet/ntrep - name = "\improper Nanotrasen Representative's locker" - req_access = list(ACCESS_NTREP) - icon_state = "ntsecure1" - icon_closed = "ntsecure" - icon_locked = "ntsecure1" - icon_opened = "ntsecureopen" - icon_broken = "ntsecurebroken" - icon_off = "ntsecureoff" - -/obj/structure/closet/secure_closet/ntrep/New() - ..() - new /obj/item/book/manual/faxes(src) - new /obj/item/storage/briefcase(src) - new /obj/item/paicard(src) - new /obj/item/flash(src) - new /obj/item/clothing/glasses/sunglasses(src) - new /obj/item/clothing/gloves/color/white(src) - new /obj/item/clothing/shoes/centcom(src) - new /obj/item/clothing/under/lawyer/oldman(src) - new /obj/item/clothing/under/lawyer/black(src) - new /obj/item/clothing/under/lawyer/female(src) - new /obj/item/clothing/under/rank/centcom/representative(src) - new /obj/item/clothing/head/ntrep(src) - new /obj/item/clothing/shoes/sandal/fancy(src) - new /obj/item/storage/box/tapes(src) - new /obj/item/taperecorder(src) - - -/obj/structure/closet/secure_closet/security/cargo - -/obj/structure/closet/secure_closet/security/cargo/New() - ..() - new /obj/item/clothing/accessory/armband/cargo(src) - new /obj/item/encryptionkey/headset_cargo(src) - - -/obj/structure/closet/secure_closet/security/engine - -/obj/structure/closet/secure_closet/security/engine/New() - ..() - new /obj/item/clothing/accessory/armband/engine(src) - new /obj/item/encryptionkey/headset_eng(src) - - -/obj/structure/closet/secure_closet/security/science - -/obj/structure/closet/secure_closet/security/science/New() - ..() - new /obj/item/clothing/accessory/armband/science(src) - new /obj/item/encryptionkey/headset_sci(src) - - -/obj/structure/closet/secure_closet/security/med - -/obj/structure/closet/secure_closet/security/med/New() - ..() - new /obj/item/clothing/accessory/armband/medgreen(src) - new /obj/item/encryptionkey/headset_med(src) - - -/obj/structure/closet/secure_closet/detective - name = "detective's cabinet" - req_access = list(ACCESS_FORENSICS_LOCKERS) - icon_state = "cabinetdetective_locked" - icon_closed = "cabinetdetective" - icon_locked = "cabinetdetective_locked" - icon_opened = "cabinetdetective_open" - icon_broken = "cabinetdetective_broken" - icon_off = "cabinetdetective_broken" - resistance_flags = FLAMMABLE - max_integrity = 70 - -/obj/structure/closet/secure_closet/detective/New() - ..() - new /obj/item/clothing/under/det(src) - new /obj/item/clothing/suit/storage/det_suit(src) - new /obj/item/clothing/suit/storage/det_suit/forensics/blue(src) - new /obj/item/clothing/suit/storage/det_suit/forensics/red(src) - new /obj/item/clothing/gloves/color/black/forensics(src) - new /obj/item/clothing/head/det_hat(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/storage/box/evidence(src) - new /obj/item/clipboard(src) - new /obj/item/radio/headset/headset_sec/alt(src) - new /obj/item/detective_scanner(src) - new /obj/item/clothing/suit/armor/vest/det_suit(src) - new /obj/item/ammo_box/c38(src) - new /obj/item/ammo_box/c38(src) - new /obj/item/gun/projectile/revolver/detective(src) - new /obj/item/clothing/accessory/holster/armpit(src) - new /obj/item/clothing/glasses/sunglasses/yeah(src) - new /obj/item/flashlight/seclite(src) - new /obj/item/holosign_creator/security(src) - new /obj/item/clothing/accessory/black(src) - new /obj/item/taperecorder(src) - new /obj/item/storage/box/tapes(src) - -/obj/structure/closet/secure_closet/detective/update_icon() - if(broken) - icon_state = icon_broken - else - if(!opened) - if(locked) - icon_state = icon_locked - else - icon_state = icon_closed - else - icon_state = icon_opened - - -/obj/structure/closet/secure_closet/injection - name = "lethal injections locker" - req_access = list(ACCESS_SECURITY) - -/obj/structure/closet/secure_closet/injection/New() - ..() - new /obj/item/reagent_containers/syringe/lethal(src) - new /obj/item/reagent_containers/syringe/lethal(src) - - -/obj/structure/closet/secure_closet/brig - name = "brig locker" - req_access = list(ACCESS_BRIG) - anchored = 1 - var/id = null - -/obj/structure/closet/secure_closet/brig/New() - ..() - new /obj/item/clothing/under/color/orange/prison(src) - new /obj/item/clothing/shoes/orange(src) - new /obj/item/card/id/prisoner/random(src) - new /obj/item/radio/headset(src) - - -/obj/structure/closet/secure_closet/courtroom - name = "courtroom locker" - req_access = list(ACCESS_COURT) - -/obj/structure/closet/secure_closet/courtroom/New() - ..() - new /obj/item/clothing/shoes/brown(src) - new /obj/item/paper/Court (src) - new /obj/item/paper/Court (src) - new /obj/item/paper/Court (src) - new /obj/item/pen (src) - new /obj/item/clothing/suit/judgerobe (src) - new /obj/item/clothing/head/powdered_wig (src) - new /obj/item/storage/briefcase(src) - - -/obj/structure/closet/secure_closet/wall - name = "wall locker" - req_access = list(ACCESS_SECURITY) - icon_state = "wall-locker1" - density = 1 - icon_closed = "wall-locker" - icon_locked = "wall-locker1" - icon_opened = "wall-lockeropen" - icon_broken = "wall-lockerbroken" - icon_off = "wall-lockeroff" - - //too small to put a man in - large = 0 - -/obj/structure/closet/secure_closet/wall/update_icon() - if(broken) - icon_state = icon_broken - else - if(!opened) - if(locked) - icon_state = icon_locked - else - icon_state = icon_closed - else - icon_state = icon_opened - -/obj/structure/closet/secure_closet/magistrate - name = "\improper Magistrate's locker" - req_access = list(ACCESS_MAGISTRATE) - icon_state = "magistratesecure1" - icon_closed = "magistratesecure" - icon_locked = "magistratesecure1" - icon_opened = "magistratesecureopen" - icon_broken = "magistratesecurebroken" - icon_off = "magistratesecureoff" - -/obj/structure/closet/secure_closet/magistrate/New() - ..() - new /obj/item/book/manual/faxes(src) - new /obj/item/storage/secure/briefcase(src) - new /obj/item/flash(src) - new /obj/item/clothing/glasses/sunglasses(src) - new /obj/item/clothing/gloves/color/white(src) - new /obj/item/clothing/shoes/centcom(src) - new /obj/item/clothing/under/suit_jacket/really_black(src) - new /obj/item/clothing/under/rank/centcom/magistrate(src) - new /obj/item/clothing/suit/judgerobe(src) - new /obj/item/clothing/head/powdered_wig(src) - new /obj/item/gavelblock(src) - new /obj/item/gavelhammer(src) - new /obj/item/clothing/head/justice_wig(src) - new /obj/item/clothing/accessory/medal/legal(src) \ No newline at end of file +/obj/structure/closet/secure_closet/captains + name = "captain's locker" + req_access = list(ACCESS_CAPTAIN) + icon_state = "capsecure1" + icon_closed = "capsecure" + icon_locked = "capsecure1" + icon_opened = "capsecureopen" + icon_broken = "capsecurebroken" + icon_off = "capsecureoff" + +/obj/structure/closet/secure_closet/captains/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/captain(src) + else + new /obj/item/storage/backpack/satchel_cap(src) + new /obj/item/book/manual/faxes(src) + new /obj/item/storage/backpack/duffel/captain(src) + new /obj/item/clothing/suit/captunic(src) + new /obj/item/clothing/suit/captunic/capjacket(src) + new /obj/item/clothing/suit/mantle/armor/captain(src) + new /obj/item/clothing/under/captainparade(src) + new /obj/item/clothing/head/caphat/parade(src) + new /obj/item/clothing/under/rank/captain(src) + new /obj/item/clothing/under/dress/dress_cap(src) + new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src) + new /obj/item/cartridge/captain(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/laceup(src) + new /obj/item/radio/headset/heads/captain/alt(src) + new /obj/item/clothing/gloves/color/captain(src) + new /obj/item/storage/belt/rapier(src) + new /obj/item/gun/energy/gun(src) + new /obj/item/door_remote/captain(src) + new /obj/item/reagent_containers/food/drinks/mug/cap(src) + new /obj/item/tank/emergency_oxygen/double(src) + + +/obj/structure/closet/secure_closet/hop + name = "head of personnel's locker" + req_access = list(ACCESS_HOP) + icon_state = "hopsecure1" + icon_closed = "hopsecure" + icon_locked = "hopsecure1" + icon_opened = "hopsecureopen" + icon_broken = "hopsecurebroken" + icon_off = "hopsecureoff" + +/obj/structure/closet/secure_closet/hop/New() + ..() + new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/head/hopcap(src) + new /obj/item/cartridge/hop(src) + new /obj/item/radio/headset/heads/hop(src) + new /obj/item/storage/box/ids(src) + new /obj/item/storage/box/PDAs(src) + new /obj/item/clothing/suit/armor/vest(src) + new /obj/item/gun/energy/gun/mini(src) + new /obj/item/flash(src) + new /obj/item/clothing/accessory/petcollar(src) + new /obj/item/door_remote/civillian(src) + new /obj/item/reagent_containers/food/drinks/mug/hop(src) + new /obj/item/clothing/accessory/medal/service(src) + +/obj/structure/closet/secure_closet/hop2 + name = "head of personnel's attire" + req_access = list(ACCESS_HOP) + icon_state = "hopsecure1" + icon_closed = "hopsecure" + icon_locked = "hopsecure1" + icon_opened = "hopsecureopen" + icon_broken = "hopsecurebroken" + icon_off = "hopsecureoff" + +/obj/structure/closet/secure_closet/hop2/New() + ..() + new /obj/item/clothing/under/rank/head_of_personnel(src) + new /obj/item/clothing/suit/mantle/armor/head_of_personnel(src) + new /obj/item/clothing/under/dress/dress_hop(src) + new /obj/item/clothing/under/dress/dress_hr(src) + new /obj/item/clothing/under/lawyer/female(src) + new /obj/item/clothing/under/lawyer/black(src) + new /obj/item/clothing/under/lawyer/red(src) + new /obj/item/clothing/under/lawyer/oldman(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/leather(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/under/rank/head_of_personnel_whimsy(src) + + +/obj/structure/closet/secure_closet/hos + name = "head of security's locker" + req_access = list(ACCESS_HOS) + icon_state = "hossecure1" + icon_closed = "hossecure" + icon_locked = "hossecure1" + icon_opened = "hossecureopen" + icon_broken = "hossecurebroken" + icon_off = "hossecureoff" + +/obj/structure/closet/secure_closet/hos/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/security(src) + else + new /obj/item/storage/backpack/satchel_sec(src) + new /obj/item/cartridge/hos(src) + new /obj/item/radio/headset/heads/hos/alt(src) + new /obj/item/clothing/under/rank/head_of_security(src) + new /obj/item/clothing/under/rank/head_of_security/formal(src) + new /obj/item/clothing/under/rank/head_of_security/corp(src) + new /obj/item/clothing/under/rank/head_of_security/skirt(src) + new /obj/item/clothing/suit/armor/hos(src) + new /obj/item/clothing/suit/armor/hos/alt(src) + new /obj/item/clothing/head/HoS(src) + new /obj/item/clothing/head/HoS/beret(src) + new /obj/item/clothing/suit/mantle/armor(src) + new /obj/item/clothing/glasses/hud/security/sunglasses(src) + new /obj/item/storage/lockbox/mindshield(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/holosign_creator/security(src) + new /obj/item/clothing/mask/gas/sechailer/hos(src) + new /obj/item/shield/riot/tele(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/storage/belt/security/sec(src) + new /obj/item/gun/energy/gun/hos(src) + new /obj/item/door_remote/head_of_security(src) + new /obj/item/reagent_containers/food/drinks/mug/hos(src) + new /obj/item/organ/internal/cyberimp/eyes/hud/security(src) + new /obj/item/clothing/accessory/medal/security(src) + +/obj/structure/closet/secure_closet/warden + name = "warden's locker" + req_access = list(ACCESS_ARMORY) + icon_state = "wardensecure1" + icon_closed = "wardensecure" + icon_locked = "wardensecure1" + icon_opened = "wardensecureopen" + icon_broken = "wardensecurebroken" + icon_off = "wardensecureoff" + +/obj/structure/closet/secure_closet/warden/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/security(src) + else + new /obj/item/storage/backpack/satchel_sec(src) + new /obj/item/radio/headset/headset_sec/alt(src) + new /obj/item/clothing/suit/armor/vest/warden(src) + new /obj/item/clothing/head/warden(src) + new /obj/item/clothing/suit/armor/vest/warden/alt(src) + new /obj/item/clothing/head/beret/sec/warden(src) + new /obj/item/clothing/under/rank/warden(src) + new /obj/item/clothing/under/rank/warden/formal(src) + new /obj/item/clothing/under/rank/warden/corp(src) + new /obj/item/clothing/under/rank/warden/skirt(src) + new /obj/item/clothing/glasses/hud/security/sunglasses(src) + new /obj/item/holosign_creator/security(src) + new /obj/item/clothing/mask/gas/sechailer/warden(src) + new /obj/item/storage/box/zipties(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/reagent_containers/spray/pepper(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/gun/energy/gun/advtaser(src) + new /obj/item/storage/belt/security/sec(src) + new /obj/item/storage/box/holobadge(src) + new /obj/item/clothing/gloves/color/black/krav_maga/sec(src) + + +/obj/structure/closet/secure_closet/security + name = "security officer's locker" + req_access = list(ACCESS_SECURITY) + icon_state = "sec1" + icon_closed = "sec" + icon_locked = "sec1" + icon_opened = "secopen" + icon_broken = "secbroken" + icon_off = "secoff" + +/obj/structure/closet/secure_closet/security/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/security(src) + else + new /obj/item/storage/backpack/satchel_sec(src) + new /obj/item/clothing/suit/armor/vest/security(src) + new /obj/item/radio/headset/headset_sec/alt(src) + new /obj/item/clothing/head/soft/sec(src) + new /obj/item/reagent_containers/spray/pepper(src) + new /obj/item/flash(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/storage/belt/security/sec(src) + new /obj/item/holosign_creator/security(src) + new /obj/item/clothing/mask/gas/sechailer(src) + new /obj/item/clothing/glasses/hud/security/sunglasses(src) + new /obj/item/clothing/head/helmet(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/clothing/suit/armor/secjacket(src) + + +/obj/structure/closet/secure_closet/brigdoc + name = "brig physician's locker" + req_access = list(ACCESS_SECURITY) + icon_state = "securemed1" + icon_closed = "securemed" + icon_locked = "securemed1" + icon_opened = "securemedopen" + icon_broken = "securemedbroken" + icon_off = "securemedoff" + +/obj/structure/closet/secure_closet/brigdoc/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/medic(src) + else + new /obj/item/storage/backpack/satchel_med(src) + new /obj/item/reagent_containers/spray/pepper(src) + new /obj/item/flash(src) + new /obj/item/storage/firstaid/regular(src) + new /obj/item/storage/firstaid/fire(src) + new /obj/item/storage/firstaid/adv(src) + new /obj/item/storage/firstaid/o2(src) + new /obj/item/storage/firstaid/toxin(src) + new /obj/item/clothing/suit/storage/brigdoc(src) + new /obj/item/clothing/under/rank/security/brigphys(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/radio/headset/headset_sec/alt(src) + new /obj/item/clothing/shoes/sandal/white(src) + + +/obj/structure/closet/secure_closet/blueshield + name = "blueshield's locker" + req_access = list(ACCESS_BLUESHIELD) + icon_state = "bssecure1" + icon_closed = "bssecure" + icon_locked = "bssecure1" + icon_opened = "bssecureopen" + icon_broken = "bssecurebroken" + icon_off = "bssecureoff" + +/obj/structure/closet/secure_closet/blueshield/New() + ..() + new /obj/item/storage/briefcase(src) + new /obj/item/storage/firstaid/adv(src) + new /obj/item/pinpointer/crew(src) + new /obj/item/storage/belt/security/sec(src) + new /obj/item/grenade/flashbang(src) + new /obj/item/flash(src) + new /obj/item/restraints/handcuffs(src) + new /obj/item/melee/baton/loaded(src) + new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/glasses/hud/security/sunglasses/read_only(src) + new /obj/item/clothing/glasses/hud/health/sunglasses + new /obj/item/clothing/head/beret/centcom/officer(src) + new /obj/item/clothing/head/beret/centcom/officer/navy(src) + new /obj/item/clothing/suit/armor/vest/blueshield(src) + new /obj/item/clothing/suit/storage/blueshield(src) + new /obj/item/clothing/shoes/centcom(src) + new /obj/item/clothing/accessory/holster(src) + new /obj/item/clothing/accessory/blue(src) + new /obj/item/clothing/shoes/jackboots/jacksandals(src) + new /obj/item/clothing/under/rank/centcom/blueshield(src) + + +/obj/structure/closet/secure_closet/ntrep + name = "\improper Nanotrasen Representative's locker" + req_access = list(ACCESS_NTREP) + icon_state = "ntsecure1" + icon_closed = "ntsecure" + icon_locked = "ntsecure1" + icon_opened = "ntsecureopen" + icon_broken = "ntsecurebroken" + icon_off = "ntsecureoff" + +/obj/structure/closet/secure_closet/ntrep/New() + ..() + new /obj/item/book/manual/faxes(src) + new /obj/item/storage/briefcase(src) + new /obj/item/paicard(src) + new /obj/item/flash(src) + new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/gloves/color/white(src) + new /obj/item/clothing/shoes/centcom(src) + new /obj/item/clothing/under/lawyer/oldman(src) + new /obj/item/clothing/under/lawyer/black(src) + new /obj/item/clothing/under/lawyer/female(src) + new /obj/item/clothing/under/rank/centcom/representative(src) + new /obj/item/clothing/head/ntrep(src) + new /obj/item/clothing/shoes/sandal/fancy(src) + new /obj/item/storage/box/tapes(src) + new /obj/item/taperecorder(src) + + +/obj/structure/closet/secure_closet/security/cargo + +/obj/structure/closet/secure_closet/security/cargo/New() + ..() + new /obj/item/clothing/accessory/armband/cargo(src) + new /obj/item/encryptionkey/headset_cargo(src) + + +/obj/structure/closet/secure_closet/security/engine + +/obj/structure/closet/secure_closet/security/engine/New() + ..() + new /obj/item/clothing/accessory/armband/engine(src) + new /obj/item/encryptionkey/headset_eng(src) + + +/obj/structure/closet/secure_closet/security/science + +/obj/structure/closet/secure_closet/security/science/New() + ..() + new /obj/item/clothing/accessory/armband/science(src) + new /obj/item/encryptionkey/headset_sci(src) + + +/obj/structure/closet/secure_closet/security/med + +/obj/structure/closet/secure_closet/security/med/New() + ..() + new /obj/item/clothing/accessory/armband/medgreen(src) + new /obj/item/encryptionkey/headset_med(src) + + +/obj/structure/closet/secure_closet/detective + name = "detective's cabinet" + req_access = list(ACCESS_FORENSICS_LOCKERS) + icon_state = "cabinetdetective_locked" + icon_closed = "cabinetdetective" + icon_locked = "cabinetdetective_locked" + icon_opened = "cabinetdetective_open" + icon_broken = "cabinetdetective_broken" + icon_off = "cabinetdetective_broken" + resistance_flags = FLAMMABLE + max_integrity = 70 + +/obj/structure/closet/secure_closet/detective/New() + ..() + new /obj/item/clothing/under/det(src) + new /obj/item/clothing/suit/storage/det_suit(src) + new /obj/item/clothing/suit/storage/det_suit/forensics/blue(src) + new /obj/item/clothing/suit/storage/det_suit/forensics/red(src) + new /obj/item/clothing/gloves/color/black/forensics(src) + new /obj/item/clothing/head/det_hat(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/storage/box/evidence(src) + new /obj/item/clipboard(src) + new /obj/item/radio/headset/headset_sec/alt(src) + new /obj/item/detective_scanner(src) + new /obj/item/clothing/suit/armor/vest/det_suit(src) + new /obj/item/ammo_box/c38(src) + new /obj/item/ammo_box/c38(src) + new /obj/item/gun/projectile/revolver/detective(src) + new /obj/item/clothing/accessory/holster/armpit(src) + new /obj/item/clothing/glasses/sunglasses/yeah(src) + new /obj/item/flashlight/seclite(src) + new /obj/item/holosign_creator/security(src) + new /obj/item/clothing/accessory/black(src) + new /obj/item/taperecorder(src) + new /obj/item/storage/box/tapes(src) + +/obj/structure/closet/secure_closet/detective/update_icon() + if(broken) + icon_state = icon_broken + else + if(!opened) + if(locked) + icon_state = icon_locked + else + icon_state = icon_closed + else + icon_state = icon_opened + + +/obj/structure/closet/secure_closet/injection + name = "lethal injections locker" + req_access = list(ACCESS_SECURITY) + +/obj/structure/closet/secure_closet/injection/New() + ..() + new /obj/item/reagent_containers/syringe/lethal(src) + new /obj/item/reagent_containers/syringe/lethal(src) + + +/obj/structure/closet/secure_closet/brig + name = "brig locker" + req_access = list(ACCESS_BRIG) + anchored = 1 + var/id = null + +/obj/structure/closet/secure_closet/brig/New() + ..() + new /obj/item/clothing/under/color/orange/prison(src) + new /obj/item/clothing/shoes/orange(src) + new /obj/item/card/id/prisoner/random(src) + new /obj/item/radio/headset(src) + + +/obj/structure/closet/secure_closet/courtroom + name = "courtroom locker" + req_access = list(ACCESS_COURT) + +/obj/structure/closet/secure_closet/courtroom/New() + ..() + new /obj/item/clothing/shoes/brown(src) + new /obj/item/paper/Court (src) + new /obj/item/paper/Court (src) + new /obj/item/paper/Court (src) + new /obj/item/pen (src) + new /obj/item/clothing/suit/judgerobe (src) + new /obj/item/clothing/head/powdered_wig (src) + new /obj/item/storage/briefcase(src) + + +/obj/structure/closet/secure_closet/wall + name = "wall locker" + req_access = list(ACCESS_SECURITY) + icon_state = "wall-locker1" + density = 1 + icon_closed = "wall-locker" + icon_locked = "wall-locker1" + icon_opened = "wall-lockeropen" + icon_broken = "wall-lockerbroken" + icon_off = "wall-lockeroff" + + //too small to put a man in + large = 0 + +/obj/structure/closet/secure_closet/wall/update_icon() + if(broken) + icon_state = icon_broken + else + if(!opened) + if(locked) + icon_state = icon_locked + else + icon_state = icon_closed + else + icon_state = icon_opened + +/obj/structure/closet/secure_closet/magistrate + name = "\improper Magistrate's locker" + req_access = list(ACCESS_MAGISTRATE) + icon_state = "magistratesecure1" + icon_closed = "magistratesecure" + icon_locked = "magistratesecure1" + icon_opened = "magistratesecureopen" + icon_broken = "magistratesecurebroken" + icon_off = "magistratesecureoff" + +/obj/structure/closet/secure_closet/magistrate/New() + ..() + new /obj/item/book/manual/faxes(src) + new /obj/item/storage/secure/briefcase(src) + new /obj/item/flash(src) + new /obj/item/clothing/glasses/sunglasses(src) + new /obj/item/clothing/gloves/color/white(src) + new /obj/item/clothing/shoes/centcom(src) + new /obj/item/clothing/under/suit_jacket/really_black(src) + new /obj/item/clothing/under/rank/centcom/magistrate(src) + new /obj/item/clothing/suit/judgerobe(src) + new /obj/item/clothing/head/powdered_wig(src) + new /obj/item/gavelblock(src) + new /obj/item/gavelhammer(src) + new /obj/item/clothing/head/justice_wig(src) + new /obj/item/clothing/accessory/medal/legal(src) diff --git a/code/game/objects/structures/crates_lockers/closets/statue.dm b/code/game/objects/structures/crates_lockers/closets/statue.dm index 3cd753740fb4..501abd4922de 100644 --- a/code/game/objects/structures/crates_lockers/closets/statue.dm +++ b/code/game/objects/structures/crates_lockers/closets/statue.dm @@ -114,4 +114,4 @@ if(user) user.dust() dump_contents() - visible_message("[src] shatters!. ") \ No newline at end of file + visible_message("[src] shatters!. ") diff --git a/code/game/objects/structures/crates_lockers/closets/syndicate.dm b/code/game/objects/structures/crates_lockers/closets/syndicate.dm index 01a9affbe43e..d95eac2a0acd 100644 --- a/code/game/objects/structures/crates_lockers/closets/syndicate.dm +++ b/code/game/objects/structures/crates_lockers/closets/syndicate.dm @@ -1,149 +1,149 @@ -/obj/structure/closet/syndicate - name = "armoury closet" - desc = "Why is this here?" - icon_state = "syndicate" - icon_closed = "syndicate" - icon_opened = "syndicateopen" - - -/obj/structure/closet/syndicate/personal - desc = "It's a storage unit for operative gear." - -/obj/structure/closet/syndicate/personal/New() - ..() - new /obj/item/clothing/under/syndicate(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/ammo_box/magazine/m10mm(src) - new /obj/item/storage/belt/military(src) - new /obj/item/crowbar/red(src) - new /obj/item/clothing/glasses/night(src) - -/obj/structure/closet/syndicate/suits - desc = "It's a storage unit for operative space gear." - -/obj/structure/closet/syndicate/suits/New() - ..() - new /obj/item/clothing/mask/gas/syndicate(src) - new /obj/item/clothing/suit/space/hardsuit/syndi(src) - new /obj/item/tank/jetpack/oxygen/harness(src) - -/obj/structure/closet/syndicate/nuclear - desc = "It's a storage unit for a Syndicate boarding party." - -/obj/structure/closet/syndicate/nuclear/New() - ..() - new /obj/item/ammo_box/magazine/m10mm(src) - new /obj/item/ammo_box/magazine/m10mm(src) - new /obj/item/ammo_box/magazine/m10mm(src) - new /obj/item/ammo_box/magazine/m10mm(src) - new /obj/item/ammo_box/magazine/m10mm(src) - new /obj/item/storage/box/teargas(src) - new /obj/item/storage/box/flashbangs(src) - new /obj/item/storage/backpack/duffel/syndie/med(src) - new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) - new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) - new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) - new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) - new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) - new /obj/item/pda/syndicate(src) - -/obj/structure/closet/syndicate/sst - desc = "It's a storage unit for an elite syndicate strike team's gear." - -/obj/structure/closet/syndicate/sst/New() - ..() - new /obj/item/ammo_box/magazine/mm556x45(src) - new /obj/item/gun/projectile/automatic/l6_saw(src) - new /obj/item/tank/jetpack/oxygen/harness(src) - new /obj/item/storage/belt/military/sst(src) - new /obj/item/clothing/glasses/thermal(src) - new /obj/item/clothing/shoes/magboots/syndie/advance(src) - new /obj/item/clothing/mask/gas/syndicate(src) - new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src) - -/obj/structure/closet/syndicate/resources/ - desc = "An old, dusty locker." - - New() - ..() - var/common_min = 30 //Minimum amount of minerals in the stack for common minerals - var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals - var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals - var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK - - var/pickednum = rand(1, 50) - - //Sad trombone - if(pickednum == 1) - var/obj/item/paper/P = new /obj/item/paper(src) - P.name = "IOU" - P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" - - //Metal (common ore) - if(pickednum >= 2) - new /obj/item/stack/sheet/metal(src, rand(common_min, common_max)) - - //Glass (common ore) - if(pickednum >= 5) - new /obj/item/stack/sheet/glass(src, rand(common_min, common_max)) - - //Plasteel (common ore) Because it has a million more uses then plasma - if(pickednum >= 10) - new /obj/item/stack/sheet/plasteel(src, rand(common_min, common_max)) - - //Plasma (rare ore) - if(pickednum >= 15) - new /obj/item/stack/sheet/mineral/plasma(src, rand(rare_min, rare_max)) - - //Silver (rare ore) - if(pickednum >= 20) - new /obj/item/stack/sheet/mineral/silver(src, rand(rare_min, rare_max)) - - //Gold (rare ore) - if(pickednum >= 30) - new /obj/item/stack/sheet/mineral/gold(src, rand(rare_min, rare_max)) - - //Uranium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/sheet/mineral/uranium(src, rand(rare_min, rare_max)) - - //Titanium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/sheet/mineral/titanium(src, rand(rare_min, rare_max)) - - //Plastitanium (rare ore) - if(pickednum >= 40) - new /obj/item/stack/sheet/mineral/plastitanium(src, rand(rare_min, rare_max)) - - //Diamond (rare HONK) - if(pickednum >= 45) - new /obj/item/stack/sheet/mineral/diamond(src, rand(rare_min, rare_max)) - - //Jetpack (You hit the jackpot!) - if(pickednum == 50) - new /obj/item/tank/jetpack/carbondioxide(src) - -/obj/structure/closet/syndicate/resources/everything - desc = "It's an emergency storage closet for repairs." - - New() - ..() - var/list/resources = list( - /obj/item/stack/sheet/metal, - /obj/item/stack/sheet/glass, - /obj/item/stack/sheet/mineral/gold, - /obj/item/stack/sheet/mineral/silver, - /obj/item/stack/sheet/mineral/plasma, - /obj/item/stack/sheet/mineral/uranium, - /obj/item/stack/sheet/mineral/diamond, - /obj/item/stack/sheet/mineral/bananium, - /obj/item/stack/sheet/mineral/titanium, - /obj/item/stack/sheet/mineral/plastitanium, - /obj/item/stack/sheet/plasteel, - /obj/item/stack/rods - ) - - for(var/i in 1 to 2) - for(var/res in resources) - var/obj/item/stack/R = new res(src) - R.amount = R.max_amount \ No newline at end of file +/obj/structure/closet/syndicate + name = "armoury closet" + desc = "Why is this here?" + icon_state = "syndicate" + icon_closed = "syndicate" + icon_opened = "syndicateopen" + + +/obj/structure/closet/syndicate/personal + desc = "It's a storage unit for operative gear." + +/obj/structure/closet/syndicate/personal/New() + ..() + new /obj/item/clothing/under/syndicate(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/storage/belt/military(src) + new /obj/item/crowbar/red(src) + new /obj/item/clothing/glasses/night(src) + +/obj/structure/closet/syndicate/suits + desc = "It's a storage unit for operative space gear." + +/obj/structure/closet/syndicate/suits/New() + ..() + new /obj/item/clothing/mask/gas/syndicate(src) + new /obj/item/clothing/suit/space/hardsuit/syndi(src) + new /obj/item/tank/jetpack/oxygen/harness(src) + +/obj/structure/closet/syndicate/nuclear + desc = "It's a storage unit for a Syndicate boarding party." + +/obj/structure/closet/syndicate/nuclear/New() + ..() + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/ammo_box/magazine/m10mm(src) + new /obj/item/storage/box/teargas(src) + new /obj/item/storage/box/flashbangs(src) + new /obj/item/storage/backpack/duffel/syndie/med(src) + new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) + new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) + new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) + new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) + new /obj/item/gun/projectile/automatic/shotgun/bulldog(src) + new /obj/item/pda/syndicate(src) + +/obj/structure/closet/syndicate/sst + desc = "It's a storage unit for an elite syndicate strike team's gear." + +/obj/structure/closet/syndicate/sst/New() + ..() + new /obj/item/ammo_box/magazine/mm556x45(src) + new /obj/item/gun/projectile/automatic/l6_saw(src) + new /obj/item/tank/jetpack/oxygen/harness(src) + new /obj/item/storage/belt/military/sst(src) + new /obj/item/clothing/glasses/thermal(src) + new /obj/item/clothing/shoes/magboots/syndie/advance(src) + new /obj/item/clothing/mask/gas/syndicate(src) + new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src) + +/obj/structure/closet/syndicate/resources/ + desc = "An old, dusty locker." + + New() + ..() + var/common_min = 30 //Minimum amount of minerals in the stack for common minerals + var/common_max = 50 //Maximum amount of HONK in the stack for HONK common minerals + var/rare_min = 5 //Minimum HONK of HONK in the stack HONK HONK rare minerals + var/rare_max = 20 //Maximum HONK HONK HONK in the HONK for HONK rare HONK + + var/pickednum = rand(1, 50) + + //Sad trombone + if(pickednum == 1) + var/obj/item/paper/P = new /obj/item/paper(src) + P.name = "IOU" + P.info = "Sorry man, we needed the money so we sold your stash. It's ok, we'll double our money for sure this time!" + + //Metal (common ore) + if(pickednum >= 2) + new /obj/item/stack/sheet/metal(src, rand(common_min, common_max)) + + //Glass (common ore) + if(pickednum >= 5) + new /obj/item/stack/sheet/glass(src, rand(common_min, common_max)) + + //Plasteel (common ore) Because it has a million more uses then plasma + if(pickednum >= 10) + new /obj/item/stack/sheet/plasteel(src, rand(common_min, common_max)) + + //Plasma (rare ore) + if(pickednum >= 15) + new /obj/item/stack/sheet/mineral/plasma(src, rand(rare_min, rare_max)) + + //Silver (rare ore) + if(pickednum >= 20) + new /obj/item/stack/sheet/mineral/silver(src, rand(rare_min, rare_max)) + + //Gold (rare ore) + if(pickednum >= 30) + new /obj/item/stack/sheet/mineral/gold(src, rand(rare_min, rare_max)) + + //Uranium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/sheet/mineral/uranium(src, rand(rare_min, rare_max)) + + //Titanium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/sheet/mineral/titanium(src, rand(rare_min, rare_max)) + + //Plastitanium (rare ore) + if(pickednum >= 40) + new /obj/item/stack/sheet/mineral/plastitanium(src, rand(rare_min, rare_max)) + + //Diamond (rare HONK) + if(pickednum >= 45) + new /obj/item/stack/sheet/mineral/diamond(src, rand(rare_min, rare_max)) + + //Jetpack (You hit the jackpot!) + if(pickednum == 50) + new /obj/item/tank/jetpack/carbondioxide(src) + +/obj/structure/closet/syndicate/resources/everything + desc = "It's an emergency storage closet for repairs." + + New() + ..() + var/list/resources = list( + /obj/item/stack/sheet/metal, + /obj/item/stack/sheet/glass, + /obj/item/stack/sheet/mineral/gold, + /obj/item/stack/sheet/mineral/silver, + /obj/item/stack/sheet/mineral/plasma, + /obj/item/stack/sheet/mineral/uranium, + /obj/item/stack/sheet/mineral/diamond, + /obj/item/stack/sheet/mineral/bananium, + /obj/item/stack/sheet/mineral/titanium, + /obj/item/stack/sheet/mineral/plastitanium, + /obj/item/stack/sheet/plasteel, + /obj/item/stack/rods + ) + + for(var/i in 1 to 2) + for(var/res in resources) + var/obj/item/stack/R = new res(src) + R.amount = R.max_amount diff --git a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm index 504d2b296441..0b80e0f7ce12 100644 --- a/code/game/objects/structures/crates_lockers/closets/utility_closets.dm +++ b/code/game/objects/structures/crates_lockers/closets/utility_closets.dm @@ -1,226 +1,226 @@ -/* Utility Closets - * Contains: - * Emergency Closet - * Fire Closet - * Tool Closet - * Radiation Closet - * Bombsuit Closet - * Hydrant - * First Aid - */ - -/* - * Emergency Closet - */ -/obj/structure/closet/emcloset - name = "emergency closet" - desc = "It's a storage unit for emergency breathmasks and o2 tanks." - icon_state = "emergency" - icon_closed = "emergency" - icon_opened = "emergencyopen" - -/obj/structure/closet/emcloset/anchored - anchored = TRUE - -/obj/structure/closet/emcloset/New() - ..() - - switch(pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10, "nothing" = 0, "delete" = 0))) - if("small") - new /obj/item/tank/emergency_oxygen(src) - new /obj/item/tank/emergency_oxygen(src) - new /obj/item/clothing/mask/breath(src) - new /obj/item/clothing/mask/breath(src) - if("aid") - new /obj/item/tank/emergency_oxygen(src) - new /obj/item/storage/toolbox/emergency(src) - new /obj/item/clothing/mask/breath(src) - new /obj/item/storage/firstaid/o2(src) - if("tank") - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/clothing/mask/breath(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/clothing/mask/breath(src) - if("both") - new /obj/item/storage/toolbox/emergency(src) - new /obj/item/tank/emergency_oxygen/engi(src) - new /obj/item/clothing/mask/breath(src) - new /obj/item/storage/firstaid/o2(src) - if("nothing") - // doot - - // teehee - Ah, tg coders... - if("delete") - qdel(src) - - //If you want to re-add fire, just add "fire" = 15 to the pick list. - /*if("fire") - new /obj/structure/closet/firecloset(src.loc) - qdel(src)*/ - -/obj/structure/closet/emcloset/legacy/New() - new /obj/item/tank/oxygen(src) - new /obj/item/clothing/mask/gas(src) - -/* - * Fire Closet - */ -/obj/structure/closet/firecloset - name = "fire-safety closet" - desc = "It's a storage unit for fire-fighting supplies." - icon_state = "firecloset" - icon_closed = "firecloset" - icon_opened = "fireclosetopen" - -/obj/structure/closet/firecloset/New() - ..() - - new /obj/item/clothing/suit/fire/firefighter(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/tank/oxygen/red(src) - new /obj/item/extinguisher(src) - new /obj/item/clothing/head/hardhat/red(src) - -/obj/structure/closet/firecloset/full/New() - ..() - contents.Cut() - - new /obj/item/clothing/suit/fire/firefighter(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/flashlight(src) - new /obj/item/tank/oxygen/red(src) - new /obj/item/extinguisher(src) - new /obj/item/clothing/head/hardhat/red(src) - - -/* - * Tool Closet - */ -/obj/structure/closet/toolcloset - name = "tool closet" - desc = "It's a storage unit for tools." - icon_state = "toolcloset" - icon_closed = "toolcloset" - icon_opened = "toolclosetopen" - -/obj/structure/closet/toolcloset/New() - ..() - if(prob(40)) - new /obj/item/clothing/suit/storage/hazardvest(src) - if(prob(70)) - new /obj/item/flashlight(src) - if(prob(70)) - new /obj/item/screwdriver(src) - if(prob(70)) - new /obj/item/wrench(src) - if(prob(70)) - new /obj/item/weldingtool(src) - if(prob(70)) - new /obj/item/crowbar(src) - if(prob(70)) - new /obj/item/wirecutters(src) - if(prob(70)) - new /obj/item/t_scanner(src) - if(prob(20)) - new /obj/item/storage/belt/utility(src) - if(prob(30)) - new /obj/item/stack/cable_coil/random(src) - if(prob(30)) - new /obj/item/stack/cable_coil/random(src) - if(prob(30)) - new /obj/item/stack/cable_coil/random(src) - if(prob(20)) - new /obj/item/multitool(src) - if(prob(5)) - new /obj/item/clothing/gloves/color/yellow(src) - if(prob(40)) - new /obj/item/clothing/head/hardhat(src) - - -/* - * Radiation Closet - */ -/obj/structure/closet/radiation - name = "radiation suit closet" - desc = "It's a storage unit for rad-protective suits." - icon_state = "radsuitcloset" - icon_opened = "toolclosetopen" - icon_closed = "radsuitcloset" - -/obj/structure/closet/radiation/New() - ..() - new /obj/item/clothing/suit/radiation(src) - new /obj/item/clothing/head/radiation(src) - -/* - * Bombsuit closet - */ -/obj/structure/closet/bombcloset - name = "\improper EOD closet" - desc = "It's a storage unit for explosion-protective suits." - icon_state = "bombsuit" - icon_closed = "bombsuit" - icon_opened = "bombsuitopen" - -/obj/structure/closet/bombcloset/New() - ..() - new /obj/item/clothing/suit/bomb_suit( src ) - new /obj/item/clothing/under/color/black( src ) - new /obj/item/clothing/shoes/black( src ) - new /obj/item/clothing/head/bomb_hood( src ) - - -/obj/structure/closet/bombclosetsecurity - name = "\improper EOD closet" - desc = "It's a storage unit for explosion-protective suits." - icon_state = "bombsuitsec" - icon_closed = "bombsuitsec" - icon_opened = "bombsuitsecopen" - -/obj/structure/closet/bombclosetsecurity/New() - ..() - new /obj/item/clothing/suit/bomb_suit/security( src ) - new /obj/item/clothing/under/rank/security( src ) - new /obj/item/clothing/shoes/brown( src ) - new /obj/item/clothing/head/bomb_hood/security( src ) - -/* - * Hydrant - */ -/obj/structure/closet/hydrant //wall mounted fire closet - name = "fire-safety closet" - desc = "It's a storage unit for fire-fighting supplies." - icon_state = "hydrant" - icon_closed = "hydrant" - icon_opened = "hydrant_open" - anchored = 1 - density = 0 - wall_mounted = 1 - -/obj/structure/closet/hydrant/New() - ..() - new /obj/item/clothing/suit/fire/firefighter(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/flashlight(src) - new /obj/item/tank/oxygen/red(src) - new /obj/item/extinguisher(src) - new /obj/item/clothing/head/hardhat/red(src) - -/* - * First Aid - */ -/obj/structure/closet/medical_wall //wall mounted medical closet - name = "first-aid closet" - desc = "It's wall-mounted storage unit for first aid supplies." - icon_state = "medical_wall" - icon_closed = "medical_wall" - icon_opened = "medical_wall_open" - anchored = 1 - density = 0 - wall_mounted = 1 - -/obj/structure/closet/medical_wall/update_icon() - if(!opened) - icon_state = icon_closed - else - icon_state = icon_opened \ No newline at end of file +/* Utility Closets + * Contains: + * Emergency Closet + * Fire Closet + * Tool Closet + * Radiation Closet + * Bombsuit Closet + * Hydrant + * First Aid + */ + +/* + * Emergency Closet + */ +/obj/structure/closet/emcloset + name = "emergency closet" + desc = "It's a storage unit for emergency breathmasks and o2 tanks." + icon_state = "emergency" + icon_closed = "emergency" + icon_opened = "emergencyopen" + +/obj/structure/closet/emcloset/anchored + anchored = TRUE + +/obj/structure/closet/emcloset/New() + ..() + + switch(pickweight(list("small" = 55, "aid" = 25, "tank" = 10, "both" = 10, "nothing" = 0, "delete" = 0))) + if("small") + new /obj/item/tank/emergency_oxygen(src) + new /obj/item/tank/emergency_oxygen(src) + new /obj/item/clothing/mask/breath(src) + new /obj/item/clothing/mask/breath(src) + if("aid") + new /obj/item/tank/emergency_oxygen(src) + new /obj/item/storage/toolbox/emergency(src) + new /obj/item/clothing/mask/breath(src) + new /obj/item/storage/firstaid/o2(src) + if("tank") + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/clothing/mask/breath(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/clothing/mask/breath(src) + if("both") + new /obj/item/storage/toolbox/emergency(src) + new /obj/item/tank/emergency_oxygen/engi(src) + new /obj/item/clothing/mask/breath(src) + new /obj/item/storage/firstaid/o2(src) + if("nothing") + // doot + + // teehee - Ah, tg coders... + if("delete") + qdel(src) + + //If you want to re-add fire, just add "fire" = 15 to the pick list. + /*if("fire") + new /obj/structure/closet/firecloset(src.loc) + qdel(src)*/ + +/obj/structure/closet/emcloset/legacy/New() + new /obj/item/tank/oxygen(src) + new /obj/item/clothing/mask/gas(src) + +/* + * Fire Closet + */ +/obj/structure/closet/firecloset + name = "fire-safety closet" + desc = "It's a storage unit for fire-fighting supplies." + icon_state = "firecloset" + icon_closed = "firecloset" + icon_opened = "fireclosetopen" + +/obj/structure/closet/firecloset/New() + ..() + + new /obj/item/clothing/suit/fire/firefighter(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/tank/oxygen/red(src) + new /obj/item/extinguisher(src) + new /obj/item/clothing/head/hardhat/red(src) + +/obj/structure/closet/firecloset/full/New() + ..() + contents.Cut() + + new /obj/item/clothing/suit/fire/firefighter(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/flashlight(src) + new /obj/item/tank/oxygen/red(src) + new /obj/item/extinguisher(src) + new /obj/item/clothing/head/hardhat/red(src) + + +/* + * Tool Closet + */ +/obj/structure/closet/toolcloset + name = "tool closet" + desc = "It's a storage unit for tools." + icon_state = "toolcloset" + icon_closed = "toolcloset" + icon_opened = "toolclosetopen" + +/obj/structure/closet/toolcloset/New() + ..() + if(prob(40)) + new /obj/item/clothing/suit/storage/hazardvest(src) + if(prob(70)) + new /obj/item/flashlight(src) + if(prob(70)) + new /obj/item/screwdriver(src) + if(prob(70)) + new /obj/item/wrench(src) + if(prob(70)) + new /obj/item/weldingtool(src) + if(prob(70)) + new /obj/item/crowbar(src) + if(prob(70)) + new /obj/item/wirecutters(src) + if(prob(70)) + new /obj/item/t_scanner(src) + if(prob(20)) + new /obj/item/storage/belt/utility(src) + if(prob(30)) + new /obj/item/stack/cable_coil/random(src) + if(prob(30)) + new /obj/item/stack/cable_coil/random(src) + if(prob(30)) + new /obj/item/stack/cable_coil/random(src) + if(prob(20)) + new /obj/item/multitool(src) + if(prob(5)) + new /obj/item/clothing/gloves/color/yellow(src) + if(prob(40)) + new /obj/item/clothing/head/hardhat(src) + + +/* + * Radiation Closet + */ +/obj/structure/closet/radiation + name = "radiation suit closet" + desc = "It's a storage unit for rad-protective suits." + icon_state = "radsuitcloset" + icon_opened = "toolclosetopen" + icon_closed = "radsuitcloset" + +/obj/structure/closet/radiation/New() + ..() + new /obj/item/clothing/suit/radiation(src) + new /obj/item/clothing/head/radiation(src) + +/* + * Bombsuit closet + */ +/obj/structure/closet/bombcloset + name = "\improper EOD closet" + desc = "It's a storage unit for explosion-protective suits." + icon_state = "bombsuit" + icon_closed = "bombsuit" + icon_opened = "bombsuitopen" + +/obj/structure/closet/bombcloset/New() + ..() + new /obj/item/clothing/suit/bomb_suit( src ) + new /obj/item/clothing/under/color/black( src ) + new /obj/item/clothing/shoes/black( src ) + new /obj/item/clothing/head/bomb_hood( src ) + + +/obj/structure/closet/bombclosetsecurity + name = "\improper EOD closet" + desc = "It's a storage unit for explosion-protective suits." + icon_state = "bombsuitsec" + icon_closed = "bombsuitsec" + icon_opened = "bombsuitsecopen" + +/obj/structure/closet/bombclosetsecurity/New() + ..() + new /obj/item/clothing/suit/bomb_suit/security( src ) + new /obj/item/clothing/under/rank/security( src ) + new /obj/item/clothing/shoes/brown( src ) + new /obj/item/clothing/head/bomb_hood/security( src ) + +/* + * Hydrant + */ +/obj/structure/closet/hydrant //wall mounted fire closet + name = "fire-safety closet" + desc = "It's a storage unit for fire-fighting supplies." + icon_state = "hydrant" + icon_closed = "hydrant" + icon_opened = "hydrant_open" + anchored = 1 + density = 0 + wall_mounted = 1 + +/obj/structure/closet/hydrant/New() + ..() + new /obj/item/clothing/suit/fire/firefighter(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/flashlight(src) + new /obj/item/tank/oxygen/red(src) + new /obj/item/extinguisher(src) + new /obj/item/clothing/head/hardhat/red(src) + +/* + * First Aid + */ +/obj/structure/closet/medical_wall //wall mounted medical closet + name = "first-aid closet" + desc = "It's wall-mounted storage unit for first aid supplies." + icon_state = "medical_wall" + icon_closed = "medical_wall" + icon_opened = "medical_wall_open" + anchored = 1 + density = 0 + wall_mounted = 1 + +/obj/structure/closet/medical_wall/update_icon() + if(!opened) + icon_state = icon_closed + else + icon_state = icon_opened diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index 7de956a2a716..723267576877 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -1,481 +1,481 @@ -/obj/structure/closet/wardrobe - name = "wardrobe" - desc = "It's a storage unit for standard-issue Nanotrasen attire." - icon_state = "blue" - icon_closed = "blue" - -/obj/structure/closet/wardrobe/generic - // Identical to the base wardrobe, aside from containing some stuff. - -/obj/structure/closet/wardrobe/generic/New() - ..() - new /obj/item/clothing/under/color/blue(src) - new /obj/item/clothing/under/color/blue(src) - new /obj/item/clothing/under/color/blue(src) - new /obj/item/clothing/mask/bandana/blue(src) - new /obj/item/clothing/mask/bandana/blue(src) - new /obj/item/clothing/mask/bandana/blue(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/brown(src) - - -/obj/structure/closet/wardrobe/red - name = "security wardrobe" - icon_state = "red" - icon_closed = "red" - -/obj/structure/closet/wardrobe/red/New() - ..() - new /obj/item/storage/backpack/duffel/security(src) - new /obj/item/storage/backpack/duffel/security(src) - new /obj/item/clothing/mask/bandana/red(src) - new /obj/item/clothing/mask/bandana/red(src) - new /obj/item/clothing/mask/bandana/red(src) - new /obj/item/clothing/under/rank/security(src) - new /obj/item/clothing/under/rank/security(src) - new /obj/item/clothing/under/rank/security(src) - new /obj/item/clothing/under/rank/security/formal(src) - new /obj/item/clothing/under/rank/security/formal(src) - new /obj/item/clothing/under/rank/security/formal(src) - new /obj/item/clothing/under/rank/security/skirt(src) - new /obj/item/clothing/under/rank/security/skirt(src) - new /obj/item/clothing/under/rank/security/skirt(src) - new /obj/item/clothing/shoes/jackboots(src) - new /obj/item/clothing/shoes/jackboots(src) - new /obj/item/clothing/shoes/jackboots(src) - new /obj/item/clothing/shoes/jackboots/jacksandals(src) - new /obj/item/clothing/shoes/jackboots/jacksandals(src) - new /obj/item/clothing/shoes/jackboots/jacksandals(src) - new /obj/item/clothing/head/soft/sec(src) - new /obj/item/clothing/head/soft/sec(src) - new /obj/item/clothing/head/soft/sec(src) - new /obj/item/clothing/head/beret/sec(src) - new /obj/item/clothing/head/beret/sec(src) - new /obj/item/clothing/head/beret/sec(src) - new /obj/item/clothing/head/officer(src) - new /obj/item/clothing/head/officer(src) - new /obj/item/clothing/head/officer(src) - -/obj/structure/closet/redcorp - name = "corporate security wardrobe" - icon_state = "red" - icon_closed = "red" - -/obj/structure/closet/redcorp/New() - ..() - new /obj/item/clothing/under/rank/security/corp(src) - new /obj/item/clothing/under/rank/security/corp(src) - new /obj/item/clothing/under/rank/security/corp(src) - new /obj/item/clothing/head/soft/sec/corp(src) - new /obj/item/clothing/head/soft/sec/corp(src) - new /obj/item/clothing/head/soft/sec/corp(src) - -/obj/structure/closet/wardrobe/pink - name = "pink wardrobe" - icon_state = "pink" - icon_closed = "pink" - -/obj/structure/closet/wardrobe/pink/New() - ..() - new /obj/item/clothing/under/color/pink(src) - new /obj/item/clothing/under/color/pink(src) - new /obj/item/clothing/under/color/pink(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/brown(src) - new /obj/item/clothing/shoes/brown(src) - -/obj/structure/closet/wardrobe/black - name = "black wardrobe" - icon_state = "black" - icon_closed = "black" - -/obj/structure/closet/wardrobe/black/New() - ..() - new /obj/item/clothing/under/color/black(src) - new /obj/item/clothing/under/color/black(src) - new /obj/item/clothing/under/color/black(src) - if(prob(25)) - new /obj/item/clothing/suit/jacket/leather(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/head/that(src) - new /obj/item/clothing/head/that(src) - new /obj/item/clothing/head/that(src) - new /obj/item/clothing/head/soft/black(src) - new /obj/item/clothing/head/soft/black(src) - new /obj/item/clothing/head/soft/black(src) - -/obj/structure/closet/wardrobe/green - name = "green wardrobe" - icon_state = "green" - icon_closed = "green" - -/obj/structure/closet/wardrobe/green/New() - ..() - new /obj/item/clothing/under/color/green(src) - new /obj/item/clothing/under/color/green(src) - new /obj/item/clothing/under/color/green(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - -/obj/structure/closet/wardrobe/xenos - name = "xenos wardrobe" - icon_state = "green" - icon_closed = "green" - -/obj/structure/closet/wardrobe/xenos/New() - ..() - new /obj/item/clothing/suit/unathi/mantle(src) - new /obj/item/clothing/suit/unathi/robe(src) - new /obj/item/clothing/shoes/sandal(src) - new /obj/item/clothing/shoes/sandal(src) - new /obj/item/clothing/shoes/sandal(src) - new /obj/item/clothing/shoes/footwraps(src) - new /obj/item/clothing/shoes/footwraps(src) - new /obj/item/clothing/shoes/footwraps(src) - - -/obj/structure/closet/wardrobe/orange - name = "prison wardrobe" - desc = "It's a storage unit for Nanotrasen-regulation prisoner attire." - icon_state = "orange" - icon_closed = "orange" - -/obj/structure/closet/wardrobe/orange/New() - ..() - new /obj/item/clothing/under/color/orange/prison(src) - new /obj/item/clothing/under/color/orange/prison(src) - new /obj/item/clothing/under/color/orange/prison(src) - new /obj/item/clothing/shoes/orange(src) - new /obj/item/clothing/shoes/orange(src) - new /obj/item/clothing/shoes/orange(src) - - -/obj/structure/closet/wardrobe/yellow - name = "yellow wardrobe" - icon_state = "yellow" - icon_closed = "yellow" - -/obj/structure/closet/wardrobe/yellow/New() - ..() - new /obj/item/clothing/under/color/yellow(src) - new /obj/item/clothing/under/color/yellow(src) - new /obj/item/clothing/under/color/yellow(src) - new /obj/item/clothing/shoes/orange(src) - new /obj/item/clothing/shoes/orange(src) - new /obj/item/clothing/shoes/orange(src) - - -/obj/structure/closet/wardrobe/atmospherics_yellow - name = "atmospherics wardrobe" - icon_state = "atmostech" - icon_closed = "atmostech" - -/obj/structure/closet/wardrobe/atmospherics_yellow/New() - ..() - new /obj/item/clothing/under/rank/atmospheric_technician(src) - new /obj/item/clothing/under/rank/atmospheric_technician(src) - new /obj/item/clothing/under/rank/atmospheric_technician(src) - new /obj/item/clothing/under/rank/atmospheric_technician/skirt(src) - new /obj/item/clothing/under/rank/atmospheric_technician/skirt(src) - new /obj/item/clothing/under/rank/atmospheric_technician/skirt(src) - new /obj/item/clothing/shoes/workboots(src) - new /obj/item/clothing/shoes/workboots(src) - new /obj/item/clothing/shoes/workboots(src) - new /obj/item/clothing/head/hardhat/red(src) - new /obj/item/clothing/head/hardhat/red(src) - new /obj/item/clothing/head/hardhat/red(src) - new /obj/item/clothing/head/beret/atmos(src) - new /obj/item/clothing/head/beret/atmos(src) - new /obj/item/clothing/head/beret/atmos(src) - - - -/obj/structure/closet/wardrobe/engineering_yellow - name = "engineering wardrobe" - icon_state = "engineer" - icon_closed = "engineer" - -/obj/structure/closet/wardrobe/engineering_yellow/New() - ..() - new /obj/item/clothing/under/rank/engineer(src) - new /obj/item/clothing/under/rank/engineer(src) - new /obj/item/clothing/under/rank/engineer(src) - new /obj/item/clothing/under/rank/engineer/skirt(src) - new /obj/item/clothing/under/rank/engineer/skirt(src) - new /obj/item/clothing/under/rank/engineer/skirt(src) - new /obj/item/clothing/shoes/workboots(src) - new /obj/item/clothing/shoes/workboots(src) - new /obj/item/clothing/shoes/workboots(src) - new /obj/item/clothing/head/hardhat(src) - new /obj/item/clothing/head/hardhat(src) - new /obj/item/clothing/head/hardhat(src) - new /obj/item/clothing/head/beret/eng(src) - new /obj/item/clothing/head/beret/eng(src) - new /obj/item/clothing/head/beret/eng(src) - - -/obj/structure/closet/wardrobe/white - name = "white wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/white/New() - ..() - new /obj/item/clothing/under/color/white(src) - new /obj/item/clothing/under/color/white(src) - new /obj/item/clothing/under/color/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - -/obj/structure/closet/wardrobe/medical_white - name = "medical doctor's wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/medical_white/New() - ..() - new /obj/item/clothing/under/rank/nursesuit (src) - new /obj/item/clothing/head/nursehat (src) - new /obj/item/clothing/under/rank/nurse(src) - new /obj/item/clothing/under/rank/orderly(src) - new /obj/item/clothing/suit/storage/fr_jacket(src) - new /obj/item/clothing/suit/storage/fr_jacket(src) - new /obj/item/clothing/suit/storage/fr_jacket(src) - new /obj/item/clothing/under/rank/medical/blue(src) - new /obj/item/clothing/head/surgery/blue(src) - new /obj/item/clothing/under/rank/medical/green(src) - new /obj/item/clothing/head/surgery/green(src) - new /obj/item/clothing/under/rank/medical/purple(src) - new /obj/item/clothing/under/rank/medical/skirt(src) - new /obj/item/clothing/under/rank/medical/skirt(src) - new /obj/item/clothing/head/surgery/purple(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - -/obj/structure/closet/wardrobe/pjs - name = "Pajama wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/pjs/New() - ..() - new /obj/item/clothing/under/pj/red(src) - new /obj/item/clothing/under/pj/red(src) - new /obj/item/clothing/under/pj/blue(src) - new /obj/item/clothing/under/pj/blue(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/slippers(src) - new /obj/item/clothing/shoes/slippers(src) - - -/obj/structure/closet/wardrobe/toxins_white - name = "toxins wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/toxins_white/New() - ..() - new /obj/item/clothing/under/rank/scientist(src) - new /obj/item/clothing/under/rank/scientist(src) - new /obj/item/clothing/under/rank/scientist(src) - new /obj/item/clothing/under/rank/scientist/skirt(src) - new /obj/item/clothing/under/rank/scientist/skirt(src) - new /obj/item/clothing/under/rank/scientist/skirt(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/slippers - new /obj/item/clothing/shoes/slippers - new /obj/item/clothing/shoes/slippers - - -/obj/structure/closet/wardrobe/robotics_black - name = "robotics wardrobe" - icon_state = "black" - icon_closed = "black" - -/obj/structure/closet/wardrobe/robotics_black/New() - ..() - new /obj/item/clothing/under/rank/roboticist(src) - new /obj/item/clothing/under/rank/roboticist(src) - new /obj/item/clothing/under/rank/roboticist/skirt(src) - new /obj/item/clothing/under/rank/roboticist/skirt(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/gloves/fingerless(src) - new /obj/item/clothing/gloves/fingerless(src) - new /obj/item/clothing/head/soft/black(src) - new /obj/item/clothing/head/soft/black(src) - - -/obj/structure/closet/wardrobe/chemistry_white - name = "chemistry wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/chemistry_white/New() - ..() - new /obj/item/clothing/under/rank/chemist(src) - new /obj/item/clothing/under/rank/chemist(src) - new /obj/item/clothing/under/rank/chemist/skirt(src) - new /obj/item/clothing/under/rank/chemist/skirt(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/suit/storage/labcoat/chemist(src) - new /obj/item/clothing/suit/storage/labcoat/chemist(src) - new /obj/item/storage/backpack/chemistry(src) - new /obj/item/storage/backpack/chemistry(src) - new /obj/item/storage/backpack/satchel_chem(src) - new /obj/item/storage/backpack/satchel_chem(src) - new /obj/item/storage/bag/chemistry(src) - new /obj/item/storage/bag/chemistry(src) - new /obj/item/clothing/mask/gas(src) - new /obj/item/clothing/mask/gas(src) - - -/obj/structure/closet/wardrobe/genetics_white - name = "genetics wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/genetics_white/New() - ..() - new /obj/item/clothing/under/rank/geneticist(src) - new /obj/item/clothing/under/rank/geneticist(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/suit/storage/labcoat/genetics(src) - new /obj/item/clothing/suit/storage/labcoat/genetics(src) - new /obj/item/storage/backpack/genetics(src) - new /obj/item/storage/backpack/genetics(src) - new /obj/item/storage/backpack/satchel_gen(src) - new /obj/item/storage/backpack/satchel_gen(src) - - -/obj/structure/closet/wardrobe/virology_white - name = "virology wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/virology_white/New() - ..() - new /obj/item/clothing/under/rank/virologist(src) - new /obj/item/clothing/under/rank/virologist(src) - new /obj/item/clothing/under/rank/virologist/skirt(src) - new /obj/item/clothing/under/rank/virologist/skirt(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/suit/storage/labcoat/virologist(src) - new /obj/item/clothing/suit/storage/labcoat/virologist(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/storage/backpack/virology(src) - new /obj/item/storage/backpack/virology(src) - new /obj/item/storage/backpack/satchel_vir(src) - new /obj/item/storage/backpack/satchel_vir(src) - - -/obj/structure/closet/wardrobe/medic_white - name = "medical wardrobe" - icon_state = "white" - icon_closed = "white" - -/obj/structure/closet/wardrobe/medic_white/New() - ..() - new /obj/item/clothing/under/rank/medical(src) - new /obj/item/clothing/under/rank/medical(src) - new /obj/item/clothing/under/rank/medical/skirt(src) - new /obj/item/clothing/under/rank/medical/skirt(src) - new /obj/item/clothing/under/rank/medical/blue(src) - new /obj/item/clothing/under/rank/medical/green(src) - new /obj/item/clothing/under/rank/medical/purple(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/suit/storage/labcoat(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/mask/surgical(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/under/medigown(src) - new /obj/item/clothing/head/headmirror(src) - new /obj/item/clothing/head/headmirror(src) - - -/obj/structure/closet/wardrobe/grey - name = "grey wardrobe" - icon_state = "grey" - icon_closed = "grey" - -/obj/structure/closet/wardrobe/grey/New() - ..() - new /obj/item/clothing/under/color/grey(src) - new /obj/item/clothing/under/color/grey(src) - new /obj/item/clothing/under/color/grey(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/shoes/black(src) - new /obj/item/clothing/head/soft/grey(src) - new /obj/item/clothing/head/soft/grey(src) - new /obj/item/clothing/head/soft/grey(src) - if(prob(50)) - new /obj/item/storage/backpack/duffel(src) - if(prob(40)) - new /obj/item/clothing/under/assistantformal(src) - if(prob(40)) - new /obj/item/clothing/under/assistantformal(src) - - -/obj/structure/closet/wardrobe/mixed - name = "mixed wardrobe" - icon_state = "mixed" - icon_closed = "mixed" - -/obj/structure/closet/wardrobe/mixed/New() - ..() - new /obj/item/clothing/under/color/blue(src) - new /obj/item/clothing/under/color/yellow(src) - new /obj/item/clothing/under/color/green(src) - new /obj/item/clothing/under/color/orange(src) - new /obj/item/clothing/under/color/pink(src) - new /obj/item/clothing/under/dress/plaid_blue(src) - new /obj/item/clothing/under/dress/plaid_red(src) - new /obj/item/clothing/under/dress/plaid_purple(src) - new /obj/item/clothing/shoes/blue(src) - new /obj/item/clothing/shoes/yellow(src) - new /obj/item/clothing/shoes/green(src) - new /obj/item/clothing/shoes/orange(src) - new /obj/item/clothing/shoes/purple(src) - new /obj/item/clothing/shoes/leather(src) - -/obj/structure/closet/wardrobe/coroner - name = "coroner wardrobe" - icon_state = "black" - icon_closed = "black" - -/obj/structure/closet/wardrobe/coroner/New() - ..() - if(prob(50)) - new /obj/item/storage/backpack/medic(src) - else - new /obj/item/storage/backpack/satchel_med(src) - new /obj/item/storage/backpack/duffel/medical(src) - new /obj/item/clothing/suit/storage/labcoat/mortician(src) - new /obj/item/clothing/shoes/white(src) - new /obj/item/clothing/under/rank/medical/mortician(src) - new /obj/item/clothing/head/surgery/black(src) +/obj/structure/closet/wardrobe + name = "wardrobe" + desc = "It's a storage unit for standard-issue Nanotrasen attire." + icon_state = "blue" + icon_closed = "blue" + +/obj/structure/closet/wardrobe/generic + // Identical to the base wardrobe, aside from containing some stuff. + +/obj/structure/closet/wardrobe/generic/New() + ..() + new /obj/item/clothing/under/color/blue(src) + new /obj/item/clothing/under/color/blue(src) + new /obj/item/clothing/under/color/blue(src) + new /obj/item/clothing/mask/bandana/blue(src) + new /obj/item/clothing/mask/bandana/blue(src) + new /obj/item/clothing/mask/bandana/blue(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/brown(src) + + +/obj/structure/closet/wardrobe/red + name = "security wardrobe" + icon_state = "red" + icon_closed = "red" + +/obj/structure/closet/wardrobe/red/New() + ..() + new /obj/item/storage/backpack/duffel/security(src) + new /obj/item/storage/backpack/duffel/security(src) + new /obj/item/clothing/mask/bandana/red(src) + new /obj/item/clothing/mask/bandana/red(src) + new /obj/item/clothing/mask/bandana/red(src) + new /obj/item/clothing/under/rank/security(src) + new /obj/item/clothing/under/rank/security(src) + new /obj/item/clothing/under/rank/security(src) + new /obj/item/clothing/under/rank/security/formal(src) + new /obj/item/clothing/under/rank/security/formal(src) + new /obj/item/clothing/under/rank/security/formal(src) + new /obj/item/clothing/under/rank/security/skirt(src) + new /obj/item/clothing/under/rank/security/skirt(src) + new /obj/item/clothing/under/rank/security/skirt(src) + new /obj/item/clothing/shoes/jackboots(src) + new /obj/item/clothing/shoes/jackboots(src) + new /obj/item/clothing/shoes/jackboots(src) + new /obj/item/clothing/shoes/jackboots/jacksandals(src) + new /obj/item/clothing/shoes/jackboots/jacksandals(src) + new /obj/item/clothing/shoes/jackboots/jacksandals(src) + new /obj/item/clothing/head/soft/sec(src) + new /obj/item/clothing/head/soft/sec(src) + new /obj/item/clothing/head/soft/sec(src) + new /obj/item/clothing/head/beret/sec(src) + new /obj/item/clothing/head/beret/sec(src) + new /obj/item/clothing/head/beret/sec(src) + new /obj/item/clothing/head/officer(src) + new /obj/item/clothing/head/officer(src) + new /obj/item/clothing/head/officer(src) + +/obj/structure/closet/redcorp + name = "corporate security wardrobe" + icon_state = "red" + icon_closed = "red" + +/obj/structure/closet/redcorp/New() + ..() + new /obj/item/clothing/under/rank/security/corp(src) + new /obj/item/clothing/under/rank/security/corp(src) + new /obj/item/clothing/under/rank/security/corp(src) + new /obj/item/clothing/head/soft/sec/corp(src) + new /obj/item/clothing/head/soft/sec/corp(src) + new /obj/item/clothing/head/soft/sec/corp(src) + +/obj/structure/closet/wardrobe/pink + name = "pink wardrobe" + icon_state = "pink" + icon_closed = "pink" + +/obj/structure/closet/wardrobe/pink/New() + ..() + new /obj/item/clothing/under/color/pink(src) + new /obj/item/clothing/under/color/pink(src) + new /obj/item/clothing/under/color/pink(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/brown(src) + new /obj/item/clothing/shoes/brown(src) + +/obj/structure/closet/wardrobe/black + name = "black wardrobe" + icon_state = "black" + icon_closed = "black" + +/obj/structure/closet/wardrobe/black/New() + ..() + new /obj/item/clothing/under/color/black(src) + new /obj/item/clothing/under/color/black(src) + new /obj/item/clothing/under/color/black(src) + if(prob(25)) + new /obj/item/clothing/suit/jacket/leather(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/head/that(src) + new /obj/item/clothing/head/that(src) + new /obj/item/clothing/head/that(src) + new /obj/item/clothing/head/soft/black(src) + new /obj/item/clothing/head/soft/black(src) + new /obj/item/clothing/head/soft/black(src) + +/obj/structure/closet/wardrobe/green + name = "green wardrobe" + icon_state = "green" + icon_closed = "green" + +/obj/structure/closet/wardrobe/green/New() + ..() + new /obj/item/clothing/under/color/green(src) + new /obj/item/clothing/under/color/green(src) + new /obj/item/clothing/under/color/green(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + +/obj/structure/closet/wardrobe/xenos + name = "xenos wardrobe" + icon_state = "green" + icon_closed = "green" + +/obj/structure/closet/wardrobe/xenos/New() + ..() + new /obj/item/clothing/suit/unathi/mantle(src) + new /obj/item/clothing/suit/unathi/robe(src) + new /obj/item/clothing/shoes/sandal(src) + new /obj/item/clothing/shoes/sandal(src) + new /obj/item/clothing/shoes/sandal(src) + new /obj/item/clothing/shoes/footwraps(src) + new /obj/item/clothing/shoes/footwraps(src) + new /obj/item/clothing/shoes/footwraps(src) + + +/obj/structure/closet/wardrobe/orange + name = "prison wardrobe" + desc = "It's a storage unit for Nanotrasen-regulation prisoner attire." + icon_state = "orange" + icon_closed = "orange" + +/obj/structure/closet/wardrobe/orange/New() + ..() + new /obj/item/clothing/under/color/orange/prison(src) + new /obj/item/clothing/under/color/orange/prison(src) + new /obj/item/clothing/under/color/orange/prison(src) + new /obj/item/clothing/shoes/orange(src) + new /obj/item/clothing/shoes/orange(src) + new /obj/item/clothing/shoes/orange(src) + + +/obj/structure/closet/wardrobe/yellow + name = "yellow wardrobe" + icon_state = "yellow" + icon_closed = "yellow" + +/obj/structure/closet/wardrobe/yellow/New() + ..() + new /obj/item/clothing/under/color/yellow(src) + new /obj/item/clothing/under/color/yellow(src) + new /obj/item/clothing/under/color/yellow(src) + new /obj/item/clothing/shoes/orange(src) + new /obj/item/clothing/shoes/orange(src) + new /obj/item/clothing/shoes/orange(src) + + +/obj/structure/closet/wardrobe/atmospherics_yellow + name = "atmospherics wardrobe" + icon_state = "atmostech" + icon_closed = "atmostech" + +/obj/structure/closet/wardrobe/atmospherics_yellow/New() + ..() + new /obj/item/clothing/under/rank/atmospheric_technician(src) + new /obj/item/clothing/under/rank/atmospheric_technician(src) + new /obj/item/clothing/under/rank/atmospheric_technician(src) + new /obj/item/clothing/under/rank/atmospheric_technician/skirt(src) + new /obj/item/clothing/under/rank/atmospheric_technician/skirt(src) + new /obj/item/clothing/under/rank/atmospheric_technician/skirt(src) + new /obj/item/clothing/shoes/workboots(src) + new /obj/item/clothing/shoes/workboots(src) + new /obj/item/clothing/shoes/workboots(src) + new /obj/item/clothing/head/hardhat/red(src) + new /obj/item/clothing/head/hardhat/red(src) + new /obj/item/clothing/head/hardhat/red(src) + new /obj/item/clothing/head/beret/atmos(src) + new /obj/item/clothing/head/beret/atmos(src) + new /obj/item/clothing/head/beret/atmos(src) + + + +/obj/structure/closet/wardrobe/engineering_yellow + name = "engineering wardrobe" + icon_state = "engineer" + icon_closed = "engineer" + +/obj/structure/closet/wardrobe/engineering_yellow/New() + ..() + new /obj/item/clothing/under/rank/engineer(src) + new /obj/item/clothing/under/rank/engineer(src) + new /obj/item/clothing/under/rank/engineer(src) + new /obj/item/clothing/under/rank/engineer/skirt(src) + new /obj/item/clothing/under/rank/engineer/skirt(src) + new /obj/item/clothing/under/rank/engineer/skirt(src) + new /obj/item/clothing/shoes/workboots(src) + new /obj/item/clothing/shoes/workboots(src) + new /obj/item/clothing/shoes/workboots(src) + new /obj/item/clothing/head/hardhat(src) + new /obj/item/clothing/head/hardhat(src) + new /obj/item/clothing/head/hardhat(src) + new /obj/item/clothing/head/beret/eng(src) + new /obj/item/clothing/head/beret/eng(src) + new /obj/item/clothing/head/beret/eng(src) + + +/obj/structure/closet/wardrobe/white + name = "white wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/white/New() + ..() + new /obj/item/clothing/under/color/white(src) + new /obj/item/clothing/under/color/white(src) + new /obj/item/clothing/under/color/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + +/obj/structure/closet/wardrobe/medical_white + name = "medical doctor's wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/medical_white/New() + ..() + new /obj/item/clothing/under/rank/nursesuit (src) + new /obj/item/clothing/head/nursehat (src) + new /obj/item/clothing/under/rank/nurse(src) + new /obj/item/clothing/under/rank/orderly(src) + new /obj/item/clothing/suit/storage/fr_jacket(src) + new /obj/item/clothing/suit/storage/fr_jacket(src) + new /obj/item/clothing/suit/storage/fr_jacket(src) + new /obj/item/clothing/under/rank/medical/blue(src) + new /obj/item/clothing/head/surgery/blue(src) + new /obj/item/clothing/under/rank/medical/green(src) + new /obj/item/clothing/head/surgery/green(src) + new /obj/item/clothing/under/rank/medical/purple(src) + new /obj/item/clothing/under/rank/medical/skirt(src) + new /obj/item/clothing/under/rank/medical/skirt(src) + new /obj/item/clothing/head/surgery/purple(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + +/obj/structure/closet/wardrobe/pjs + name = "Pajama wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/pjs/New() + ..() + new /obj/item/clothing/under/pj/red(src) + new /obj/item/clothing/under/pj/red(src) + new /obj/item/clothing/under/pj/blue(src) + new /obj/item/clothing/under/pj/blue(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/slippers(src) + new /obj/item/clothing/shoes/slippers(src) + + +/obj/structure/closet/wardrobe/toxins_white + name = "toxins wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/toxins_white/New() + ..() + new /obj/item/clothing/under/rank/scientist(src) + new /obj/item/clothing/under/rank/scientist(src) + new /obj/item/clothing/under/rank/scientist(src) + new /obj/item/clothing/under/rank/scientist/skirt(src) + new /obj/item/clothing/under/rank/scientist/skirt(src) + new /obj/item/clothing/under/rank/scientist/skirt(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/slippers + new /obj/item/clothing/shoes/slippers + new /obj/item/clothing/shoes/slippers + + +/obj/structure/closet/wardrobe/robotics_black + name = "robotics wardrobe" + icon_state = "black" + icon_closed = "black" + +/obj/structure/closet/wardrobe/robotics_black/New() + ..() + new /obj/item/clothing/under/rank/roboticist(src) + new /obj/item/clothing/under/rank/roboticist(src) + new /obj/item/clothing/under/rank/roboticist/skirt(src) + new /obj/item/clothing/under/rank/roboticist/skirt(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/gloves/fingerless(src) + new /obj/item/clothing/gloves/fingerless(src) + new /obj/item/clothing/head/soft/black(src) + new /obj/item/clothing/head/soft/black(src) + + +/obj/structure/closet/wardrobe/chemistry_white + name = "chemistry wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/chemistry_white/New() + ..() + new /obj/item/clothing/under/rank/chemist(src) + new /obj/item/clothing/under/rank/chemist(src) + new /obj/item/clothing/under/rank/chemist/skirt(src) + new /obj/item/clothing/under/rank/chemist/skirt(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/suit/storage/labcoat/chemist(src) + new /obj/item/clothing/suit/storage/labcoat/chemist(src) + new /obj/item/storage/backpack/chemistry(src) + new /obj/item/storage/backpack/chemistry(src) + new /obj/item/storage/backpack/satchel_chem(src) + new /obj/item/storage/backpack/satchel_chem(src) + new /obj/item/storage/bag/chemistry(src) + new /obj/item/storage/bag/chemistry(src) + new /obj/item/clothing/mask/gas(src) + new /obj/item/clothing/mask/gas(src) + + +/obj/structure/closet/wardrobe/genetics_white + name = "genetics wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/genetics_white/New() + ..() + new /obj/item/clothing/under/rank/geneticist(src) + new /obj/item/clothing/under/rank/geneticist(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/suit/storage/labcoat/genetics(src) + new /obj/item/clothing/suit/storage/labcoat/genetics(src) + new /obj/item/storage/backpack/genetics(src) + new /obj/item/storage/backpack/genetics(src) + new /obj/item/storage/backpack/satchel_gen(src) + new /obj/item/storage/backpack/satchel_gen(src) + + +/obj/structure/closet/wardrobe/virology_white + name = "virology wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/virology_white/New() + ..() + new /obj/item/clothing/under/rank/virologist(src) + new /obj/item/clothing/under/rank/virologist(src) + new /obj/item/clothing/under/rank/virologist/skirt(src) + new /obj/item/clothing/under/rank/virologist/skirt(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/suit/storage/labcoat/virologist(src) + new /obj/item/clothing/suit/storage/labcoat/virologist(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/storage/backpack/virology(src) + new /obj/item/storage/backpack/virology(src) + new /obj/item/storage/backpack/satchel_vir(src) + new /obj/item/storage/backpack/satchel_vir(src) + + +/obj/structure/closet/wardrobe/medic_white + name = "medical wardrobe" + icon_state = "white" + icon_closed = "white" + +/obj/structure/closet/wardrobe/medic_white/New() + ..() + new /obj/item/clothing/under/rank/medical(src) + new /obj/item/clothing/under/rank/medical(src) + new /obj/item/clothing/under/rank/medical/skirt(src) + new /obj/item/clothing/under/rank/medical/skirt(src) + new /obj/item/clothing/under/rank/medical/blue(src) + new /obj/item/clothing/under/rank/medical/green(src) + new /obj/item/clothing/under/rank/medical/purple(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/suit/storage/labcoat(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/mask/surgical(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/under/medigown(src) + new /obj/item/clothing/head/headmirror(src) + new /obj/item/clothing/head/headmirror(src) + + +/obj/structure/closet/wardrobe/grey + name = "grey wardrobe" + icon_state = "grey" + icon_closed = "grey" + +/obj/structure/closet/wardrobe/grey/New() + ..() + new /obj/item/clothing/under/color/grey(src) + new /obj/item/clothing/under/color/grey(src) + new /obj/item/clothing/under/color/grey(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/shoes/black(src) + new /obj/item/clothing/head/soft/grey(src) + new /obj/item/clothing/head/soft/grey(src) + new /obj/item/clothing/head/soft/grey(src) + if(prob(50)) + new /obj/item/storage/backpack/duffel(src) + if(prob(40)) + new /obj/item/clothing/under/assistantformal(src) + if(prob(40)) + new /obj/item/clothing/under/assistantformal(src) + + +/obj/structure/closet/wardrobe/mixed + name = "mixed wardrobe" + icon_state = "mixed" + icon_closed = "mixed" + +/obj/structure/closet/wardrobe/mixed/New() + ..() + new /obj/item/clothing/under/color/blue(src) + new /obj/item/clothing/under/color/yellow(src) + new /obj/item/clothing/under/color/green(src) + new /obj/item/clothing/under/color/orange(src) + new /obj/item/clothing/under/color/pink(src) + new /obj/item/clothing/under/dress/plaid_blue(src) + new /obj/item/clothing/under/dress/plaid_red(src) + new /obj/item/clothing/under/dress/plaid_purple(src) + new /obj/item/clothing/shoes/blue(src) + new /obj/item/clothing/shoes/yellow(src) + new /obj/item/clothing/shoes/green(src) + new /obj/item/clothing/shoes/orange(src) + new /obj/item/clothing/shoes/purple(src) + new /obj/item/clothing/shoes/leather(src) + +/obj/structure/closet/wardrobe/coroner + name = "coroner wardrobe" + icon_state = "black" + icon_closed = "black" + +/obj/structure/closet/wardrobe/coroner/New() + ..() + if(prob(50)) + new /obj/item/storage/backpack/medic(src) + else + new /obj/item/storage/backpack/satchel_med(src) + new /obj/item/storage/backpack/duffel/medical(src) + new /obj/item/clothing/suit/storage/labcoat/mortician(src) + new /obj/item/clothing/shoes/white(src) + new /obj/item/clothing/under/rank/medical/mortician(src) + new /obj/item/clothing/head/surgery/black(src) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 31747323e072..f9010c19415b 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -1,643 +1,643 @@ -/obj/structure/closet/crate - name = "crate" - desc = "A rectangular steel crate." - icon = 'icons/obj/crates.dmi' - icon_state = "crate" - icon_opened = "crateopen" - icon_closed = "crate" - climbable = TRUE -// mouse_drag_pointer = MOUSE_ACTIVE_POINTER //??? - var/rigged = FALSE - var/obj/item/paper/manifest/manifest - // A list of beacon names that the crate will announce the arrival of, when delivered. - var/list/announce_beacons = list() - -/obj/structure/closet/crate/New() - ..() - update_icon() - -/obj/structure/closet/crate/update_icon() - ..() - overlays.Cut() - if(manifest) - overlays += "manifest" - -/obj/structure/closet/crate/can_open() - return TRUE - -/obj/structure/closet/crate/can_close() - return TRUE - -/obj/structure/closet/crate/open() - if(src.opened) - return FALSE - if(!src.can_open()) - return FALSE - - if(rigged && locate(/obj/item/radio/electropack) in src) - if(isliving(usr)) - var/mob/living/L = usr - if(L.electrocute_act(17, src)) - do_sparks(5, 1, src) - return 2 - - playsound(src.loc, 'sound/machines/click.ogg', 15, 1, -3) - for(var/obj/O in src) //Objects - O.forceMove(loc) - for(var/mob/M in src) //Mobs - M.forceMove(loc) - icon_state = icon_opened - src.opened = TRUE - - if(climbable) - structure_shaken() - - return TRUE - -/obj/structure/closet/crate/close() - if(!src.opened) - return FALSE - if(!src.can_close()) - return FALSE - - playsound(src.loc, 'sound/machines/click.ogg', 15, 1, -3) - var/itemcount = 0 - for(var/obj/O in get_turf(src)) - if(itemcount >= storage_capacity) - break - if(O.density || O.anchored || istype(O,/obj/structure/closet)) - continue - if(istype(O, /obj/structure/bed)) //This is only necessary because of rollerbeds and swivel chairs. - var/obj/structure/bed/B = O - if(B.has_buckled_mobs()) - continue - O.forceMove(src) - itemcount++ - - icon_state = icon_closed - src.opened = FALSE - return TRUE - -/obj/structure/closet/crate/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/rcs) && !src.opened) - var/obj/item/rcs/E = W - if(E.rcell && (E.rcell.charge >= E.chargecost)) - if(!is_level_reachable(src.z)) // This is inconsistent with the closet sending code - to_chat(user, "The rapid-crate-sender can't locate any telepads!") - return - if(E.mode == 0) - if(!E.teleporting) - var/list/L = list() - var/list/areaindex = list() - for(var/obj/machinery/telepad_cargo/R in world) - if(R.stage == 0) - var/turf/T = get_turf(R) - var/tmpname = T.loc.name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = R - var/desc = input("Please select a telepad.", "RCS") in L - E.pad = L[desc] - if(!Adjacent(user)) - to_chat(user, "Unable to teleport, too far from crate.") - return - playsound(E.loc, E.usesound, 50, 1) - to_chat(user, "Teleporting [src.name]...") - E.teleporting = TRUE - if(!do_after(user, 50 * E.toolspeed, target = src)) - E.teleporting = 0 - return - E.teleporting = 0 - if(!(E.rcell && E.rcell.use(E.chargecost))) - to_chat(user, "Unable to teleport, insufficient charge.") - return - do_sparks(5, 1, src) - do_teleport(src, E.pad, 0) - to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") - return - - else - E.rand_x = rand(50,200) - E.rand_y = rand(50,200) - var/L = locate(E.rand_x, E.rand_y, 6) - if(!Adjacent(user)) - to_chat(user, "Unable to teleport, too far from crate.") - return - playsound(E.loc, E.usesound, 50, 1) - to_chat(user, "Teleporting [src.name]...") - E.teleporting = TRUE - if(!do_after(user, 50 * E.toolspeed, target = src)) - E.teleporting = FALSE - return - E.teleporting = 0 - if(!(E.rcell && E.rcell.use(E.chargecost))) - to_chat(user, "Unable to teleport, insufficient charge.") - return - do_sparks(5, 1, src) - do_teleport(src, L) - to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") - return - else - to_chat(user, "Out of charges.") - return - - if(opened) - if(isrobot(user)) - return - if(!user.drop_item()) //couldn't drop the item - to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!") - return - if(W) - W.forceMove(loc) - else if(istype(W, /obj/item/stack/packageWrap)) - return - else if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(rigged) - to_chat(user, "[src] is already rigged!") - return - if(C.use(15)) - to_chat(user, "You rig [src].") - rigged = TRUE - else - to_chat(user, "You need atleast 15 wires to rig [src]!") - return - else if(istype(W, /obj/item/radio/electropack)) - if(rigged) - to_chat(user, "You attach [W] to [src].") - user.drop_item() - W.forceMove(src) - return - else if(istype(W, /obj/item/wirecutters)) - if(rigged) - to_chat(user, "You cut away the wiring.") - playsound(loc, W.usesound, 100, 1) - rigged = FALSE - return - else if(user.a_intent != INTENT_HARM) - attack_hand(user) - else - return ..() - -/obj/structure/closet/singularity_act() - dump_contents() - ..() - -/obj/structure/closet/crate/welder_act() - return - -/obj/structure/closet/crate/attack_hand(mob/user) - if(manifest) - to_chat(user, "You tear the manifest off of the crate.") - playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) - manifest.forceMove(loc) - if(ishuman(user)) - user.put_in_hands(manifest) - manifest = null - update_icon() - return - else - if(rigged && locate(/obj/item/radio/electropack) in src) - if(isliving(user)) - var/mob/living/L = user - if(L.electrocute_act(17, src)) - do_sparks(5, 1, src) - return - src.add_fingerprint(user) - src.toggle(user) - -// Called when a crate is delivered by MULE at a location, for notifying purposes -/obj/structure/closet/crate/proc/notifyRecipient(var/destination) - var/msg = "[capitalize(name)] has arrived at [destination]." - if(destination in announce_beacons) - for(var/obj/machinery/requests_console/D in allConsoles) - if(D.department in src.announce_beacons[destination]) - D.createMessage(name, "Your Crate has Arrived!", msg, 1) - -/obj/structure/closet/crate/secure - desc = "A secure crate." - name = "Secure crate" - icon_state = "securecrate" - icon_opened = "securecrateopen" - icon_closed = "securecrate" - var/redlight = "securecrater" - var/greenlight = "securecrateg" - var/sparks = "securecratesparks" - var/emag = "securecrateemag" - max_integrity = 500 - armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - damage_deflection = 25 - var/tamperproof = 0 - broken = 0 - locked = 1 - -/obj/structure/closet/crate/secure/update_icon() - ..() - overlays.Cut() - if(manifest) - overlays += "manifest" - if(locked) - overlays += redlight - else if(broken) - overlays += emag - else - overlays += greenlight - -/obj/structure/closet/crate/secure/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) - if(prob(tamperproof) && damage_amount >= DAMAGE_PRECISION) - boom() - else - return ..() - -/obj/structure/closet/crate/secure/proc/boom(mob/user) - if(user) - to_chat(user, "The crate's anti-tamper system activates!") - investigate_log("[key_name(user)] has detonated a [src]", INVESTIGATE_BOMB) - for(var/atom/movable/AM in src) - qdel(AM) - explosion(get_turf(src), 0, 1, 5, 5) - qdel(src) - -/obj/structure/closet/crate/secure/can_open() - return !locked - -/obj/structure/closet/crate/secure/proc/togglelock(mob/user) - if(src.opened) - to_chat(user, "Close the crate first.") - return - if(src.broken) - to_chat(user, "The crate appears to be broken.") - return - if(src.allowed(user)) - src.locked = !src.locked - visible_message("The crate has been [locked ? null : "un"]locked by [user].") - update_icon() - else - to_chat(user, "Access Denied") - -/obj/structure/closet/crate/secure/verb/verb_togglelock() - set src in oview(1) // One square distance - set category = null - set name = "Toggle Lock" - - if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain - return - - if(ishuman(usr)) - src.add_fingerprint(usr) - src.togglelock(usr) - else - to_chat(usr, "This mob type can't use this verb.") - -/obj/structure/closet/crate/secure/attack_hand(mob/user) - if(manifest) - to_chat(user, "You tear the manifest off of the crate.") - playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) - manifest.forceMove(loc) - if(ishuman(user)) - user.put_in_hands(manifest) - manifest = null - update_icon() - return - if(locked) - src.togglelock(user) - else - src.toggle(user) - - -/obj/structure/closet/crate/secure/attackby(obj/item/W, mob/user, params) - if(is_type_in_list(W, list(/obj/item/stack/packageWrap, /obj/item/stack/cable_coil, /obj/item/radio/electropack, /obj/item/wirecutters,/obj/item/rcs))) - return ..() - if((istype(W, /obj/item/card/emag) || istype(W, /obj/item/melee/energy/blade))) - emag_act(user) - return - if(!opened) - src.togglelock(user) - return - return ..() - -/obj/structure/closet/crate/secure/emag_act(mob/user) - if(locked) - overlays += sparks - spawn(6) overlays -= sparks //Tried lots of stuff but nothing works right. so i have to use this *sadface* - playsound(src.loc, "sparks", 60, 1) - src.locked = 0 - src.broken = 1 - update_icon() - to_chat(user, "You unlock \the [src].") - -/obj/structure/closet/crate/secure/emp_act(severity) - for(var/obj/O in src) - O.emp_act(severity) - if(!broken && !opened && prob(50/severity)) - if(!locked) - src.locked = 1 - else - overlays += sparks - spawn(6) overlays -= sparks //Tried lots of stuff but nothing works right. so i have to use this *sadface* - playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) - src.locked = 0 - update_icon() - if(!opened && prob(20/severity)) - if(!locked) - open() - else - src.req_access = list() - src.req_access += pick(get_all_accesses()) - ..() - -/obj/structure/closet/crate/plastic - name = "plastic crate" - desc = "A rectangular plastic crate." - icon_state = "plasticcrate" - icon_opened = "plasticcrateopen" - icon_closed = "plasticcrate" - -/obj/structure/closet/crate/internals - desc = "A internals crate." - name = "internals crate" - icon_state = "o2crate" - icon_opened = "o2crateopen" - icon_closed = "o2crate" - -/obj/structure/closet/crate/trashcart - desc = "A heavy, metal trashcart with wheels." - name = "trash Cart" - icon_state = "trashcart" - icon_opened = "trashcartopen" - icon_closed = "trashcart" - -/*these aren't needed anymore -/obj/structure/closet/crate/hat - desc = "A crate filled with Valuable Collector's Hats!." - name = "Hat Crate" - icon_state = "crate" - icon_opened = "crateopen" - icon_closed = "crate" - -/obj/structure/closet/crate/contraband - name = "Poster crate" - desc = "A random assortment of posters manufactured by providers NOT listed under Nanotrasen's whitelist." - icon_state = "crate" - icon_opened = "crateopen" - icon_closed = "crate" -*/ - -/obj/structure/closet/crate/medical - desc = "A medical crate." - name = "medical crate" - icon_state = "medicalcrate" - icon_opened = "medicalcrateopen" - icon_closed = "medicalcrate" - -/obj/structure/closet/crate/rcd - desc = "A crate for the storage of the RCD." - name = "\improper RCD crate" - icon_state = "crate" - icon_opened = "crateopen" - icon_closed = "crate" - -/obj/structure/closet/crate/rcd/New() - ..() - new /obj/item/rcd_ammo(src) - new /obj/item/rcd_ammo(src) - new /obj/item/rcd_ammo(src) - new /obj/item/rcd(src) - -/obj/structure/closet/crate/freezer - desc = "A freezer." - name = "Freezer" - icon_state = "freezer" - icon_opened = "freezeropen" - icon_closed = "freezer" - var/target_temp = T0C - 40 - var/cooling_power = 40 - - return_air() - var/datum/gas_mixture/gas = (..()) - if(!gas) return null - var/datum/gas_mixture/newgas = new/datum/gas_mixture() - newgas.oxygen = gas.oxygen - newgas.carbon_dioxide = gas.carbon_dioxide - newgas.nitrogen = gas.nitrogen - newgas.toxins = gas.toxins - newgas.volume = gas.volume - newgas.temperature = gas.temperature - if(newgas.temperature <= target_temp) return - - if((newgas.temperature - cooling_power) > target_temp) - newgas.temperature -= cooling_power - else - newgas.temperature = target_temp - return newgas - - -/obj/structure/closet/crate/can - desc = "A large can, looks like a bin to me." - name = "garbage can" - icon_state = "largebin" - icon_opened = "largebinopen" - icon_closed = "largebin" - anchored = TRUE - -/obj/structure/closet/crate/can/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - default_unfasten_wrench(user, I, 40) - -/obj/structure/closet/crate/radiation - desc = "A crate with a radiation sign on it." - name = "radioactive gear crate" - icon_state = "radiation" - icon_opened = "radiationopen" - icon_closed = "radiation" - -/obj/structure/closet/crate/radiation/New() - ..() - new /obj/item/clothing/suit/radiation(src) - new /obj/item/clothing/head/radiation(src) - new /obj/item/clothing/suit/radiation(src) - new /obj/item/clothing/head/radiation(src) - new /obj/item/clothing/suit/radiation(src) - new /obj/item/clothing/head/radiation(src) - new /obj/item/clothing/suit/radiation(src) - new /obj/item/clothing/head/radiation(src) - -/obj/structure/closet/crate/secure/weapon - desc = "A secure weapons crate." - name = "weapons crate" - icon_state = "weaponcrate" - icon_opened = "weaponcrateopen" - icon_closed = "weaponcrate" - -/obj/structure/closet/crate/secure/plasma - desc = "A secure plasma crate." - name = "plasma crate" - icon_state = "plasmacrate" - icon_opened = "plasmacrateopen" - icon_closed = "plasmacrate" - -/obj/structure/closet/crate/secure/gear - desc = "A secure gear crate." - name = "gear crate" - icon_state = "secgearcrate" - icon_opened = "secgearcrateopen" - icon_closed = "secgearcrate" - -/obj/structure/closet/crate/secure/hydrosec - desc = "A crate with a lock on it, painted in the scheme of the station's botanists." - name = "secure hydroponics crate" - icon_state = "hydrosecurecrate" - icon_opened = "hydrosecurecrateopen" - icon_closed = "hydrosecurecrate" - -/obj/structure/closet/crate/secure/bin - desc = "A secure bin." - name = "secure bin" - icon_state = "largebins" - icon_opened = "largebinsopen" - icon_closed = "largebins" - redlight = "largebinr" - greenlight = "largebing" - sparks = "largebinsparks" - emag = "largebinemag" - -/obj/structure/closet/crate/large - name = "large crate" - desc = "A hefty metal crate." - icon_state = "largemetal" - icon_opened = "largemetalopen" - icon_closed = "largemetal" - integrity_failure = 0 //Makes the crate break when integrity reaches 0, instead of opening and becoming an invisible sprite. - -/obj/structure/closet/crate/large/close() - . = ..() - if(.)//we can hold up to one large item - var/found = 0 - for(var/obj/structure/S in src.loc) - if(S == src) - continue - if(!S.anchored) - found = 1 - S.forceMove(src) - break - if(!found) - for(var/obj/machinery/M in src.loc) - if(!M.anchored) - M.forceMove(src) - break - -/obj/structure/closet/crate/secure/large - name = "large crate" - desc = "A hefty metal crate with an electronic locking system." - icon_state = "largemetal" - icon_opened = "largemetalopen" - icon_closed = "largemetal" - redlight = "largemetalr" - greenlight = "largemetalg" - -/obj/structure/closet/crate/secure/large/close() - . = ..() - if(.)//we can hold up to one large item - var/found = 0 - for(var/obj/structure/S in src.loc) - if(S == src) - continue - if(!S.anchored) - found = 1 - S.forceMove(src) - break - if(!found) - for(var/obj/machinery/M in src.loc) - if(!M.anchored) - M.forceMove(src) - break - -//fluff variant -/obj/structure/closet/crate/secure/large/reinforced - desc = "A hefty, reinforced metal crate with an electronic locking system." - icon_state = "largermetal" - icon_opened = "largermetalopen" - icon_closed = "largermetal" - -/obj/structure/closet/crate/hydroponics - name = "hydroponics crate" - desc = "All you need to destroy those pesky weeds and pests." - icon_state = "hydrocrate" - icon_opened = "hydrocrateopen" - icon_closed = "hydrocrate" - -/obj/structure/closet/crate/hydroponics/prespawned - //This exists so the prespawned hydro crates spawn with their contents. - - New() - ..() - new /obj/item/reagent_containers/glass/bucket(src) - new /obj/item/reagent_containers/glass/bucket(src) - new /obj/item/screwdriver(src) - new /obj/item/screwdriver(src) - new /obj/item/wrench(src) - new /obj/item/wrench(src) - new /obj/item/wirecutters(src) - new /obj/item/wirecutters(src) - new /obj/item/shovel/spade(src) - new /obj/item/shovel/spade(src) - new /obj/item/storage/box/beakers(src) - new /obj/item/storage/box/beakers(src) - new /obj/item/hand_labeler(src) - new /obj/item/hand_labeler(src) - -/obj/structure/closet/crate/sci - name = "science crate" - desc = "A science crate." - icon_state = "scicrate" - icon_opened = "scicrateopen" - icon_closed = "scicrate" - -/obj/structure/closet/crate/secure/scisec - name = "secure science crate" - desc = "A crate with a lock on it, painted in the scheme of the station's scientists." - icon_state = "scisecurecrate" - icon_opened = "scisecurecrateopen" - icon_closed = "scisecurecrate" - -/obj/structure/closet/crate/engineering - name = "engineering crate" - desc = "An engineering crate." - icon_state = "engicrate" - icon_opened = "engicrateopen" - icon_closed = "engicrate" - -/obj/structure/closet/crate/secure/engineering - name = "secure engineering crate" - desc = "A crate with a lock on it, painted in the scheme of the station's engineers." - icon_state = "engisecurecrate" - icon_opened = "engisecurecrateopen" - icon_closed = "engisecurecrate" - -/obj/structure/closet/crate/engineering/electrical - name = "electrical engineering crate" - desc = "An electrical engineering crate." - icon_state = "electricalcrate" - icon_opened = "electricalcrateopen" - icon_closed = "electricalcrate" - -/obj/structure/closet/crate/tape/New() - if(prob(10)) - new /obj/item/bikehorn/rubberducky(src) - ..() - -//crates of gear in the free golem ship -/obj/structure/closet/crate/golemgear/New() - ..() - new /obj/item/storage/backpack/industrial(src) - new /obj/item/shovel(src) - new /obj/item/pickaxe(src) - new /obj/item/t_scanner/adv_mining_scanner/lesser(src) - new /obj/item/storage/bag/ore(src) - new /obj/item/clothing/glasses/meson(src) - new /obj/item/card/id/golem(src) - new /obj/item/flashlight/lantern(src) +/obj/structure/closet/crate + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/crates.dmi' + icon_state = "crate" + icon_opened = "crateopen" + icon_closed = "crate" + climbable = TRUE +// mouse_drag_pointer = MOUSE_ACTIVE_POINTER //??? + var/rigged = FALSE + var/obj/item/paper/manifest/manifest + // A list of beacon names that the crate will announce the arrival of, when delivered. + var/list/announce_beacons = list() + +/obj/structure/closet/crate/New() + ..() + update_icon() + +/obj/structure/closet/crate/update_icon() + ..() + overlays.Cut() + if(manifest) + overlays += "manifest" + +/obj/structure/closet/crate/can_open() + return TRUE + +/obj/structure/closet/crate/can_close() + return TRUE + +/obj/structure/closet/crate/open() + if(src.opened) + return FALSE + if(!src.can_open()) + return FALSE + + if(rigged && locate(/obj/item/radio/electropack) in src) + if(isliving(usr)) + var/mob/living/L = usr + if(L.electrocute_act(17, src)) + do_sparks(5, 1, src) + return 2 + + playsound(src.loc, 'sound/machines/click.ogg', 15, 1, -3) + for(var/obj/O in src) //Objects + O.forceMove(loc) + for(var/mob/M in src) //Mobs + M.forceMove(loc) + icon_state = icon_opened + src.opened = TRUE + + if(climbable) + structure_shaken() + + return TRUE + +/obj/structure/closet/crate/close() + if(!src.opened) + return FALSE + if(!src.can_close()) + return FALSE + + playsound(src.loc, 'sound/machines/click.ogg', 15, 1, -3) + var/itemcount = 0 + for(var/obj/O in get_turf(src)) + if(itemcount >= storage_capacity) + break + if(O.density || O.anchored || istype(O,/obj/structure/closet)) + continue + if(istype(O, /obj/structure/bed)) //This is only necessary because of rollerbeds and swivel chairs. + var/obj/structure/bed/B = O + if(B.has_buckled_mobs()) + continue + O.forceMove(src) + itemcount++ + + icon_state = icon_closed + src.opened = FALSE + return TRUE + +/obj/structure/closet/crate/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/rcs) && !src.opened) + var/obj/item/rcs/E = W + if(E.rcell && (E.rcell.charge >= E.chargecost)) + if(!is_level_reachable(src.z)) // This is inconsistent with the closet sending code + to_chat(user, "The rapid-crate-sender can't locate any telepads!") + return + if(E.mode == 0) + if(!E.teleporting) + var/list/L = list() + var/list/areaindex = list() + for(var/obj/machinery/telepad_cargo/R in world) + if(R.stage == 0) + var/turf/T = get_turf(R) + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = R + var/desc = input("Please select a telepad.", "RCS") in L + E.pad = L[desc] + if(!Adjacent(user)) + to_chat(user, "Unable to teleport, too far from crate.") + return + playsound(E.loc, E.usesound, 50, 1) + to_chat(user, "Teleporting [src.name]...") + E.teleporting = TRUE + if(!do_after(user, 50 * E.toolspeed, target = src)) + E.teleporting = 0 + return + E.teleporting = 0 + if(!(E.rcell && E.rcell.use(E.chargecost))) + to_chat(user, "Unable to teleport, insufficient charge.") + return + do_sparks(5, 1, src) + do_teleport(src, E.pad, 0) + to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") + return + + else + E.rand_x = rand(50,200) + E.rand_y = rand(50,200) + var/L = locate(E.rand_x, E.rand_y, 6) + if(!Adjacent(user)) + to_chat(user, "Unable to teleport, too far from crate.") + return + playsound(E.loc, E.usesound, 50, 1) + to_chat(user, "Teleporting [src.name]...") + E.teleporting = TRUE + if(!do_after(user, 50 * E.toolspeed, target = src)) + E.teleporting = FALSE + return + E.teleporting = 0 + if(!(E.rcell && E.rcell.use(E.chargecost))) + to_chat(user, "Unable to teleport, insufficient charge.") + return + do_sparks(5, 1, src) + do_teleport(src, L) + to_chat(user, "Teleport successful. [round(E.rcell.charge/E.chargecost)] charge\s left.") + return + else + to_chat(user, "Out of charges.") + return + + if(opened) + if(isrobot(user)) + return + if(!user.drop_item()) //couldn't drop the item + to_chat(user, "\The [W] is stuck to your hand, you cannot put it in \the [src]!") + return + if(W) + W.forceMove(loc) + else if(istype(W, /obj/item/stack/packageWrap)) + return + else if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + if(rigged) + to_chat(user, "[src] is already rigged!") + return + if(C.use(15)) + to_chat(user, "You rig [src].") + rigged = TRUE + else + to_chat(user, "You need atleast 15 wires to rig [src]!") + return + else if(istype(W, /obj/item/radio/electropack)) + if(rigged) + to_chat(user, "You attach [W] to [src].") + user.drop_item() + W.forceMove(src) + return + else if(istype(W, /obj/item/wirecutters)) + if(rigged) + to_chat(user, "You cut away the wiring.") + playsound(loc, W.usesound, 100, 1) + rigged = FALSE + return + else if(user.a_intent != INTENT_HARM) + attack_hand(user) + else + return ..() + +/obj/structure/closet/singularity_act() + dump_contents() + ..() + +/obj/structure/closet/crate/welder_act() + return + +/obj/structure/closet/crate/attack_hand(mob/user) + if(manifest) + to_chat(user, "You tear the manifest off of the crate.") + playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) + manifest.forceMove(loc) + if(ishuman(user)) + user.put_in_hands(manifest) + manifest = null + update_icon() + return + else + if(rigged && locate(/obj/item/radio/electropack) in src) + if(isliving(user)) + var/mob/living/L = user + if(L.electrocute_act(17, src)) + do_sparks(5, 1, src) + return + src.add_fingerprint(user) + src.toggle(user) + +// Called when a crate is delivered by MULE at a location, for notifying purposes +/obj/structure/closet/crate/proc/notifyRecipient(var/destination) + var/msg = "[capitalize(name)] has arrived at [destination]." + if(destination in announce_beacons) + for(var/obj/machinery/requests_console/D in GLOB.allRequestConsoles) + if(D.department in src.announce_beacons[destination]) + D.createMessage(name, "Your Crate has Arrived!", msg, 1) + +/obj/structure/closet/crate/secure + desc = "A secure crate." + name = "Secure crate" + icon_state = "securecrate" + icon_opened = "securecrateopen" + icon_closed = "securecrate" + var/redlight = "securecrater" + var/greenlight = "securecrateg" + var/sparks = "securecratesparks" + var/emag = "securecrateemag" + max_integrity = 500 + armor = list("melee" = 30, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + damage_deflection = 25 + var/tamperproof = 0 + broken = 0 + locked = 1 + +/obj/structure/closet/crate/secure/update_icon() + ..() + overlays.Cut() + if(manifest) + overlays += "manifest" + if(locked) + overlays += redlight + else if(broken) + overlays += emag + else + overlays += greenlight + +/obj/structure/closet/crate/secure/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) + if(prob(tamperproof) && damage_amount >= DAMAGE_PRECISION) + boom() + else + return ..() + +/obj/structure/closet/crate/secure/proc/boom(mob/user) + if(user) + to_chat(user, "The crate's anti-tamper system activates!") + investigate_log("[key_name(user)] has detonated a [src]", INVESTIGATE_BOMB) + for(var/atom/movable/AM in src) + qdel(AM) + explosion(get_turf(src), 0, 1, 5, 5) + qdel(src) + +/obj/structure/closet/crate/secure/can_open() + return !locked + +/obj/structure/closet/crate/secure/proc/togglelock(mob/user) + if(src.opened) + to_chat(user, "Close the crate first.") + return + if(src.broken) + to_chat(user, "The crate appears to be broken.") + return + if(src.allowed(user)) + src.locked = !src.locked + visible_message("The crate has been [locked ? null : "un"]locked by [user].") + update_icon() + else + to_chat(user, "Access Denied") + +/obj/structure/closet/crate/secure/verb/verb_togglelock() + set src in oview(1) // One square distance + set category = null + set name = "Toggle Lock" + + if(!usr.canmove || usr.stat || usr.restrained()) // Don't use it if you're not able to! Checks for stuns, ghost and restrain + return + + if(ishuman(usr)) + src.add_fingerprint(usr) + src.togglelock(usr) + else + to_chat(usr, "This mob type can't use this verb.") + +/obj/structure/closet/crate/secure/attack_hand(mob/user) + if(manifest) + to_chat(user, "You tear the manifest off of the crate.") + playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) + manifest.forceMove(loc) + if(ishuman(user)) + user.put_in_hands(manifest) + manifest = null + update_icon() + return + if(locked) + src.togglelock(user) + else + src.toggle(user) + + +/obj/structure/closet/crate/secure/attackby(obj/item/W, mob/user, params) + if(is_type_in_list(W, list(/obj/item/stack/packageWrap, /obj/item/stack/cable_coil, /obj/item/radio/electropack, /obj/item/wirecutters,/obj/item/rcs))) + return ..() + if((istype(W, /obj/item/card/emag) || istype(W, /obj/item/melee/energy/blade))) + emag_act(user) + return + if(!opened) + src.togglelock(user) + return + return ..() + +/obj/structure/closet/crate/secure/emag_act(mob/user) + if(locked) + overlays += sparks + spawn(6) overlays -= sparks //Tried lots of stuff but nothing works right. so i have to use this *sadface* + playsound(src.loc, "sparks", 60, 1) + src.locked = 0 + src.broken = 1 + update_icon() + to_chat(user, "You unlock \the [src].") + +/obj/structure/closet/crate/secure/emp_act(severity) + for(var/obj/O in src) + O.emp_act(severity) + if(!broken && !opened && prob(50/severity)) + if(!locked) + src.locked = 1 + else + overlays += sparks + spawn(6) overlays -= sparks //Tried lots of stuff but nothing works right. so i have to use this *sadface* + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + src.locked = 0 + update_icon() + if(!opened && prob(20/severity)) + if(!locked) + open() + else + src.req_access = list() + src.req_access += pick(get_all_accesses()) + ..() + +/obj/structure/closet/crate/plastic + name = "plastic crate" + desc = "A rectangular plastic crate." + icon_state = "plasticcrate" + icon_opened = "plasticcrateopen" + icon_closed = "plasticcrate" + +/obj/structure/closet/crate/internals + desc = "A internals crate." + name = "internals crate" + icon_state = "o2crate" + icon_opened = "o2crateopen" + icon_closed = "o2crate" + +/obj/structure/closet/crate/trashcart + desc = "A heavy, metal trashcart with wheels." + name = "trash Cart" + icon_state = "trashcart" + icon_opened = "trashcartopen" + icon_closed = "trashcart" + +/*these aren't needed anymore +/obj/structure/closet/crate/hat + desc = "A crate filled with Valuable Collector's Hats!." + name = "Hat Crate" + icon_state = "crate" + icon_opened = "crateopen" + icon_closed = "crate" + +/obj/structure/closet/crate/contraband + name = "Poster crate" + desc = "A random assortment of posters manufactured by providers NOT listed under Nanotrasen's whitelist." + icon_state = "crate" + icon_opened = "crateopen" + icon_closed = "crate" +*/ + +/obj/structure/closet/crate/medical + desc = "A medical crate." + name = "medical crate" + icon_state = "medicalcrate" + icon_opened = "medicalcrateopen" + icon_closed = "medicalcrate" + +/obj/structure/closet/crate/rcd + desc = "A crate for the storage of the RCD." + name = "\improper RCD crate" + icon_state = "crate" + icon_opened = "crateopen" + icon_closed = "crate" + +/obj/structure/closet/crate/rcd/New() + ..() + new /obj/item/rcd_ammo(src) + new /obj/item/rcd_ammo(src) + new /obj/item/rcd_ammo(src) + new /obj/item/rcd(src) + +/obj/structure/closet/crate/freezer + desc = "A freezer." + name = "Freezer" + icon_state = "freezer" + icon_opened = "freezeropen" + icon_closed = "freezer" + var/target_temp = T0C - 40 + var/cooling_power = 40 + + return_air() + var/datum/gas_mixture/gas = (..()) + if(!gas) return null + var/datum/gas_mixture/newgas = new/datum/gas_mixture() + newgas.oxygen = gas.oxygen + newgas.carbon_dioxide = gas.carbon_dioxide + newgas.nitrogen = gas.nitrogen + newgas.toxins = gas.toxins + newgas.volume = gas.volume + newgas.temperature = gas.temperature + if(newgas.temperature <= target_temp) return + + if((newgas.temperature - cooling_power) > target_temp) + newgas.temperature -= cooling_power + else + newgas.temperature = target_temp + return newgas + + +/obj/structure/closet/crate/can + desc = "A large can, looks like a bin to me." + name = "garbage can" + icon_state = "largebin" + icon_opened = "largebinopen" + icon_closed = "largebin" + anchored = TRUE + +/obj/structure/closet/crate/can/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I, 40) + +/obj/structure/closet/crate/radiation + desc = "A crate with a radiation sign on it." + name = "radioactive gear crate" + icon_state = "radiation" + icon_opened = "radiationopen" + icon_closed = "radiation" + +/obj/structure/closet/crate/radiation/New() + ..() + new /obj/item/clothing/suit/radiation(src) + new /obj/item/clothing/head/radiation(src) + new /obj/item/clothing/suit/radiation(src) + new /obj/item/clothing/head/radiation(src) + new /obj/item/clothing/suit/radiation(src) + new /obj/item/clothing/head/radiation(src) + new /obj/item/clothing/suit/radiation(src) + new /obj/item/clothing/head/radiation(src) + +/obj/structure/closet/crate/secure/weapon + desc = "A secure weapons crate." + name = "weapons crate" + icon_state = "weaponcrate" + icon_opened = "weaponcrateopen" + icon_closed = "weaponcrate" + +/obj/structure/closet/crate/secure/plasma + desc = "A secure plasma crate." + name = "plasma crate" + icon_state = "plasmacrate" + icon_opened = "plasmacrateopen" + icon_closed = "plasmacrate" + +/obj/structure/closet/crate/secure/gear + desc = "A secure gear crate." + name = "gear crate" + icon_state = "secgearcrate" + icon_opened = "secgearcrateopen" + icon_closed = "secgearcrate" + +/obj/structure/closet/crate/secure/hydrosec + desc = "A crate with a lock on it, painted in the scheme of the station's botanists." + name = "secure hydroponics crate" + icon_state = "hydrosecurecrate" + icon_opened = "hydrosecurecrateopen" + icon_closed = "hydrosecurecrate" + +/obj/structure/closet/crate/secure/bin + desc = "A secure bin." + name = "secure bin" + icon_state = "largebins" + icon_opened = "largebinsopen" + icon_closed = "largebins" + redlight = "largebinr" + greenlight = "largebing" + sparks = "largebinsparks" + emag = "largebinemag" + +/obj/structure/closet/crate/large + name = "large crate" + desc = "A hefty metal crate." + icon_state = "largemetal" + icon_opened = "largemetalopen" + icon_closed = "largemetal" + integrity_failure = 0 //Makes the crate break when integrity reaches 0, instead of opening and becoming an invisible sprite. + +/obj/structure/closet/crate/large/close() + . = ..() + if(.)//we can hold up to one large item + var/found = 0 + for(var/obj/structure/S in src.loc) + if(S == src) + continue + if(!S.anchored) + found = 1 + S.forceMove(src) + break + if(!found) + for(var/obj/machinery/M in src.loc) + if(!M.anchored) + M.forceMove(src) + break + +/obj/structure/closet/crate/secure/large + name = "large crate" + desc = "A hefty metal crate with an electronic locking system." + icon_state = "largemetal" + icon_opened = "largemetalopen" + icon_closed = "largemetal" + redlight = "largemetalr" + greenlight = "largemetalg" + +/obj/structure/closet/crate/secure/large/close() + . = ..() + if(.)//we can hold up to one large item + var/found = 0 + for(var/obj/structure/S in src.loc) + if(S == src) + continue + if(!S.anchored) + found = 1 + S.forceMove(src) + break + if(!found) + for(var/obj/machinery/M in src.loc) + if(!M.anchored) + M.forceMove(src) + break + +//fluff variant +/obj/structure/closet/crate/secure/large/reinforced + desc = "A hefty, reinforced metal crate with an electronic locking system." + icon_state = "largermetal" + icon_opened = "largermetalopen" + icon_closed = "largermetal" + +/obj/structure/closet/crate/hydroponics + name = "hydroponics crate" + desc = "All you need to destroy those pesky weeds and pests." + icon_state = "hydrocrate" + icon_opened = "hydrocrateopen" + icon_closed = "hydrocrate" + +/obj/structure/closet/crate/hydroponics/prespawned + //This exists so the prespawned hydro crates spawn with their contents. + + New() + ..() + new /obj/item/reagent_containers/glass/bucket(src) + new /obj/item/reagent_containers/glass/bucket(src) + new /obj/item/screwdriver(src) + new /obj/item/screwdriver(src) + new /obj/item/wrench(src) + new /obj/item/wrench(src) + new /obj/item/wirecutters(src) + new /obj/item/wirecutters(src) + new /obj/item/shovel/spade(src) + new /obj/item/shovel/spade(src) + new /obj/item/storage/box/beakers(src) + new /obj/item/storage/box/beakers(src) + new /obj/item/hand_labeler(src) + new /obj/item/hand_labeler(src) + +/obj/structure/closet/crate/sci + name = "science crate" + desc = "A science crate." + icon_state = "scicrate" + icon_opened = "scicrateopen" + icon_closed = "scicrate" + +/obj/structure/closet/crate/secure/scisec + name = "secure science crate" + desc = "A crate with a lock on it, painted in the scheme of the station's scientists." + icon_state = "scisecurecrate" + icon_opened = "scisecurecrateopen" + icon_closed = "scisecurecrate" + +/obj/structure/closet/crate/engineering + name = "engineering crate" + desc = "An engineering crate." + icon_state = "engicrate" + icon_opened = "engicrateopen" + icon_closed = "engicrate" + +/obj/structure/closet/crate/secure/engineering + name = "secure engineering crate" + desc = "A crate with a lock on it, painted in the scheme of the station's engineers." + icon_state = "engisecurecrate" + icon_opened = "engisecurecrateopen" + icon_closed = "engisecurecrate" + +/obj/structure/closet/crate/engineering/electrical + name = "electrical engineering crate" + desc = "An electrical engineering crate." + icon_state = "electricalcrate" + icon_opened = "electricalcrateopen" + icon_closed = "electricalcrate" + +/obj/structure/closet/crate/tape/New() + if(prob(10)) + new /obj/item/bikehorn/rubberducky(src) + ..() + +//crates of gear in the free golem ship +/obj/structure/closet/crate/golemgear/New() + ..() + new /obj/item/storage/backpack/industrial(src) + new /obj/item/shovel(src) + new /obj/item/pickaxe(src) + new /obj/item/t_scanner/adv_mining_scanner/lesser(src) + new /obj/item/storage/bag/ore(src) + new /obj/item/clothing/glasses/meson(src) + new /obj/item/card/id/golem(src) + new /obj/item/flashlight/lantern(src) diff --git a/code/game/objects/structures/crates_lockers/crittercrate.dm b/code/game/objects/structures/crates_lockers/crittercrate.dm index a476440629c5..fd726aab6a79 100644 --- a/code/game/objects/structures/crates_lockers/crittercrate.dm +++ b/code/game/objects/structures/crates_lockers/crittercrate.dm @@ -88,4 +88,4 @@ /obj/structure/closet/critter/deer name = "deer crate" - content_mob = /mob/living/simple_animal/deer \ No newline at end of file + content_mob = /mob/living/simple_animal/deer diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index 7b30dee1f733..3e763ca223c1 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -1,99 +1,99 @@ -/obj/structure/largecrate - name = "large crate" - desc = "A hefty wooden crate." - icon = 'icons/obj/crates.dmi' - icon_state = "largecrate" - density = 1 - var/obj/item/paper/manifest/manifest - -/obj/structure/largecrate/New() - ..() - update_icon() - -/obj/structure/largecrate/update_icon() - ..() - overlays.Cut() - if(manifest) - overlays += "manifest" - -/obj/structure/largecrate/attack_hand(mob/user as mob) - if(manifest) - to_chat(user, "You tear the manifest off of the crate.") - playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) - manifest.forceMove(loc) - if(ishuman(user)) - user.put_in_hands(manifest) - manifest = null - update_icon() - return - else - to_chat(user, "You need a crowbar to pry this open!") - return - -/obj/structure/largecrate/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/crowbar)) - if(manifest) - manifest.forceMove(loc) - manifest = null - update_icon() - new /obj/item/stack/sheet/wood(src) - var/turf/T = get_turf(src) - for(var/O in contents) - var/atom/movable/A = O - A.forceMove(T) - user.visible_message("[user] pries \the [src] open.", \ - "You pry open \the [src].", \ - "You hear splitting wood.") - qdel(src) - else if(user.a_intent != INTENT_HARM) - attack_hand(user) - else - return ..() - -/obj/structure/largecrate/mule - -/obj/structure/largecrate/lisa - icon_state = "lisacrate" - -/obj/structure/largecrate/lisa/attackby(obj/item/W as obj, mob/user as mob) //ugly but oh well - if(istype(W, /obj/item/crowbar)) - new /mob/living/simple_animal/pet/dog/corgi/Lisa(loc) - return ..() - -/obj/structure/largecrate/cow - name = "cow crate" - icon_state = "lisacrate" - -/obj/structure/largecrate/cow/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/crowbar)) - new /mob/living/simple_animal/cow(loc) - return ..() - -/obj/structure/largecrate/goat - name = "goat crate" - icon_state = "lisacrate" - -/obj/structure/largecrate/goat/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/crowbar)) - new /mob/living/simple_animal/hostile/retaliate/goat(loc) - return ..() - -/obj/structure/largecrate/chick - name = "chicken crate" - icon_state = "lisacrate" - -/obj/structure/largecrate/chick/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/crowbar)) - var/num = rand(4, 6) - for(var/i = 0, i < num, i++) - new /mob/living/simple_animal/chick(loc) - return ..() - -/obj/structure/largecrate/cat - name = "cat crate" - icon_state = "lisacrate" - -/obj/structure/largecrate/cat/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/crowbar)) - new /mob/living/simple_animal/pet/cat(loc) - return ..() \ No newline at end of file +/obj/structure/largecrate + name = "large crate" + desc = "A hefty wooden crate." + icon = 'icons/obj/crates.dmi' + icon_state = "largecrate" + density = 1 + var/obj/item/paper/manifest/manifest + +/obj/structure/largecrate/New() + ..() + update_icon() + +/obj/structure/largecrate/update_icon() + ..() + overlays.Cut() + if(manifest) + overlays += "manifest" + +/obj/structure/largecrate/attack_hand(mob/user as mob) + if(manifest) + to_chat(user, "You tear the manifest off of the crate.") + playsound(src.loc, 'sound/items/poster_ripped.ogg', 75, 1) + manifest.forceMove(loc) + if(ishuman(user)) + user.put_in_hands(manifest) + manifest = null + update_icon() + return + else + to_chat(user, "You need a crowbar to pry this open!") + return + +/obj/structure/largecrate/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/crowbar)) + if(manifest) + manifest.forceMove(loc) + manifest = null + update_icon() + new /obj/item/stack/sheet/wood(src) + var/turf/T = get_turf(src) + for(var/O in contents) + var/atom/movable/A = O + A.forceMove(T) + user.visible_message("[user] pries \the [src] open.", \ + "You pry open \the [src].", \ + "You hear splitting wood.") + qdel(src) + else if(user.a_intent != INTENT_HARM) + attack_hand(user) + else + return ..() + +/obj/structure/largecrate/mule + +/obj/structure/largecrate/lisa + icon_state = "lisacrate" + +/obj/structure/largecrate/lisa/attackby(obj/item/W as obj, mob/user as mob) //ugly but oh well + if(istype(W, /obj/item/crowbar)) + new /mob/living/simple_animal/pet/dog/corgi/Lisa(loc) + return ..() + +/obj/structure/largecrate/cow + name = "cow crate" + icon_state = "lisacrate" + +/obj/structure/largecrate/cow/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/crowbar)) + new /mob/living/simple_animal/cow(loc) + return ..() + +/obj/structure/largecrate/goat + name = "goat crate" + icon_state = "lisacrate" + +/obj/structure/largecrate/goat/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/crowbar)) + new /mob/living/simple_animal/hostile/retaliate/goat(loc) + return ..() + +/obj/structure/largecrate/chick + name = "chicken crate" + icon_state = "lisacrate" + +/obj/structure/largecrate/chick/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/crowbar)) + var/num = rand(4, 6) + for(var/i = 0, i < num, i++) + new /mob/living/simple_animal/chick(loc) + return ..() + +/obj/structure/largecrate/cat + name = "cat crate" + icon_state = "lisacrate" + +/obj/structure/largecrate/cat/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/crowbar)) + new /mob/living/simple_animal/pet/cat(loc) + return ..() diff --git a/code/game/objects/structures/depot.dm b/code/game/objects/structures/depot.dm index 0309db3dffbb..e970ac911dc0 100644 --- a/code/game/objects/structures/depot.dm +++ b/code/game/objects/structures/depot.dm @@ -126,4 +126,4 @@ qdel(src) /obj/effect/overload/ex_act(severity) - return \ No newline at end of file + return diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 313a8e963e34..36a20aa02390 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -1,238 +1,238 @@ -/obj/structure/displaycase - name = "display case" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "glassbox0" - desc = "A display case for prized possessions." - density = TRUE - anchored = TRUE - resistance_flags = ACID_PROOF - armor = list("melee" = 30, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100) - max_integrity = 200 - integrity_failure = 50 - var/obj/item/showpiece = null - var/alert = TRUE - var/open = FALSE - var/openable = TRUE - var/obj/item/airlock_electronics/electronics - var/start_showpiece_type = null //add type for items on display - var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk") - var/trophy_message = "" - -/obj/structure/displaycase/Initialize(mapload) - . = ..() - if(start_showpieces.len && !start_showpiece_type) - var/list/showpiece_entry = pick(start_showpieces) - if (showpiece_entry && showpiece_entry["type"]) - start_showpiece_type = showpiece_entry["type"] - if (showpiece_entry["trophy_message"]) - trophy_message = showpiece_entry["trophy_message"] - if(start_showpiece_type) - showpiece = new start_showpiece_type (src) - update_icon() - -/obj/structure/displaycase/Destroy() - QDEL_NULL(electronics) - QDEL_NULL(showpiece) - return ..() - -/obj/structure/displaycase/examine(mob/user) - . = ..() - if(alert) - . += "Hooked up with an anti-theft system." - if(showpiece) - . += "There's [showpiece] inside." - if(trophy_message) - . += "The plaque reads:\n [trophy_message]" - -/obj/structure/displaycase/proc/dump() - if(showpiece) - showpiece.forceMove(loc) - showpiece = null - -/obj/structure/displaycase/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/displaycase/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - dump() - if(!disassembled) - new /obj/item/shard(loc) - trigger_alarm() - qdel(src) - -/obj/structure/displaycase/obj_break(damage_flag) - if(!broken && !(flags & NODECONSTRUCT)) - density = FALSE - broken = 1 - new /obj/item/shard( src.loc ) - playsound(src, "shatter", 70, TRUE) - update_icon() - trigger_alarm() - -/obj/structure/displaycase/proc/trigger_alarm() - set waitfor = FALSE - if(alert && is_station_contact(z)) - var/area/alarmed = get_area(src) - alarmed.burglaralert(src) - visible_message("The burglar alarm goes off!") - // Play the burglar alarm three times - for(var/i = 0, i < 4, i++) - playsound(src, 'sound/machines/burglar_alarm.ogg', 50, 0) - sleep(74) // 7.4 seconds long - -/obj/structure/displaycase/update_icon() - var/icon/I - if(open) - I = icon('icons/obj/stationobjs.dmi',"glassbox_open") - else - I = icon('icons/obj/stationobjs.dmi',"glassbox0") - if(broken) - I = icon('icons/obj/stationobjs.dmi',"glassboxb0") - if(showpiece) - var/icon/S = getFlatIcon(showpiece) - S.Scale(17, 17) - I.Blend(S,ICON_UNDERLAY,8,8) - icon = I - -/obj/structure/displaycase/attackby(obj/item/I, mob/user, params) - if(I.GetID() && !broken && openable) - if(allowed(user)) - to_chat(user, "You [open ? "close":"open"] [src].") - toggle_lock(user) - else - to_chat(user, "Access denied.") - else if(open && !showpiece) - if(user.drop_item()) - I.forceMove(src) - showpiece = I - to_chat(user, "You put [I] on display") - update_icon() - else if(istype(I, /obj/item/stack/sheet/glass) && broken) - var/obj/item/stack/sheet/glass/G = I - if(G.get_amount() < 2) - to_chat(user, "You need two glass sheets to fix the case!") - return - to_chat(user, "You start fixing [src]...") - if(do_after(user, 20, target = src)) - G.use(2) - broken = 0 - obj_integrity = max_integrity - update_icon() - else - return ..() - -/obj/structure/displaycase/crowbar_act(mob/user, obj/item/I) //Only applies to the lab cage and player made display cases - if(alert || !openable) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(broken) - if(showpiece) - to_chat(user, "Remove the displayed object first.") - if(I.use_tool(src, user, 0, volume = I.tool_volume)) - to_chat(user, "You remove the destroyed case") - qdel(src) - else - to_chat(user, "You start to [open ? "close":"open"] [src].") - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - to_chat(user, "You [open ? "close":"open"] [src].") - toggle_lock(user) - -obj/structure/displaycase/welder_act(mob/user, obj/item/I) - . = TRUE - if(default_welder_repair(user, I)) - broken = FALSE - -/obj/structure/displaycase/proc/toggle_lock(mob/user) - open = !open - update_icon() - -/obj/structure/displaycase/attack_hand(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - if(showpiece && (broken || open)) - to_chat(user, "You deactivate the hover field built into the case.") - dump() - add_fingerprint(user) - update_icon() - return - else - //prevents remote "kicks" with TK - if(!Adjacent(user)) - return - user.visible_message("[user] kicks the display case.") - user.do_attack_animation(src, ATTACK_EFFECT_KICK) - take_damage(2) - -/obj/structure/displaycase_chassis - anchored = TRUE - density = FALSE - name = "display case chassis" - desc = "The wooden base of a display case." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "glassbox_chassis" - var/obj/item/airlock_electronics/electronics - -/obj/structure/displaycase_chassis/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/airlock_electronics)) - to_chat(user, "You start installing the electronics into [src]...") - playsound(src.loc, I.usesound, 50, 1) - if(do_after(user, 30, target = src)) - if(user.drop_item()) - I.forceMove(src) - electronics = I - to_chat(user, "You install the airlock electronics.") - - else if(istype(I, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = I - if(G.get_amount() < 10) - to_chat(user, "You need ten glass sheets to do this!") - return - to_chat(user, "You start adding [G] to [src]...") - if(do_after(user, 20, target = src)) - G.use(10) - var/obj/structure/displaycase/display = new(src.loc) - if(electronics) - electronics.forceMove(display) - display.electronics = electronics - if(electronics.one_access) - display.req_one_access = electronics.conf_access - else - display.req_access = electronics.conf_access - qdel(src) - else - return ..() - -/obj/structure/displaycase_chassis/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - TOOL_DISMANTLE_SUCCESS_MESSAGE - new /obj/item/stack/sheet/wood(get_turf(src), 5) - qdel(src) - -//The lab cage and captains display case do not spawn with electronics, which is why req_access is needed. -/obj/structure/displaycase/captain - alert = TRUE - start_showpiece_type = /obj/item/gun/energy/laser/captain - req_access = list(ACCESS_CAPTAIN) - -/obj/structure/displaycase/labcage - name = "lab cage" - desc = "A glass lab container for storing interesting creatures." - start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr - req_access = list(ACCESS_RD) - -/obj/structure/displaycase/stechkin - name = "officer's display case" - desc = "A display case containing a humble stechkin pistol. Never forget your roots." - start_showpiece_type = /obj/item/gun/projectile/automatic/pistol - req_access = list(ACCESS_SYNDICATE_COMMAND) \ No newline at end of file +/obj/structure/displaycase + name = "display case" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "glassbox0" + desc = "A display case for prized possessions." + density = TRUE + anchored = TRUE + resistance_flags = ACID_PROOF + armor = list("melee" = 30, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 100) + max_integrity = 200 + integrity_failure = 50 + var/obj/item/showpiece = null + var/alert = TRUE + var/open = FALSE + var/openable = TRUE + var/obj/item/airlock_electronics/electronics + var/start_showpiece_type = null //add type for items on display + var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk") + var/trophy_message = "" + +/obj/structure/displaycase/Initialize(mapload) + . = ..() + if(start_showpieces.len && !start_showpiece_type) + var/list/showpiece_entry = pick(start_showpieces) + if (showpiece_entry && showpiece_entry["type"]) + start_showpiece_type = showpiece_entry["type"] + if (showpiece_entry["trophy_message"]) + trophy_message = showpiece_entry["trophy_message"] + if(start_showpiece_type) + showpiece = new start_showpiece_type (src) + update_icon() + +/obj/structure/displaycase/Destroy() + QDEL_NULL(electronics) + QDEL_NULL(showpiece) + return ..() + +/obj/structure/displaycase/examine(mob/user) + . = ..() + if(alert) + . += "Hooked up with an anti-theft system." + if(showpiece) + . += "There's [showpiece] inside." + if(trophy_message) + . += "The plaque reads:\n [trophy_message]" + +/obj/structure/displaycase/proc/dump() + if(showpiece) + showpiece.forceMove(loc) + showpiece = null + +/obj/structure/displaycase/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/displaycase/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + dump() + if(!disassembled) + new /obj/item/shard(loc) + trigger_alarm() + qdel(src) + +/obj/structure/displaycase/obj_break(damage_flag) + if(!broken && !(flags & NODECONSTRUCT)) + density = FALSE + broken = 1 + new /obj/item/shard( src.loc ) + playsound(src, "shatter", 70, TRUE) + update_icon() + trigger_alarm() + +/obj/structure/displaycase/proc/trigger_alarm() + set waitfor = FALSE + if(alert && is_station_contact(z)) + var/area/alarmed = get_area(src) + alarmed.burglaralert(src) + visible_message("The burglar alarm goes off!") + // Play the burglar alarm three times + for(var/i = 0, i < 4, i++) + playsound(src, 'sound/machines/burglar_alarm.ogg', 50, 0) + sleep(74) // 7.4 seconds long + +/obj/structure/displaycase/update_icon() + var/icon/I + if(open) + I = icon('icons/obj/stationobjs.dmi',"glassbox_open") + else + I = icon('icons/obj/stationobjs.dmi',"glassbox0") + if(broken) + I = icon('icons/obj/stationobjs.dmi',"glassboxb0") + if(showpiece) + var/icon/S = getFlatIcon(showpiece) + S.Scale(17, 17) + I.Blend(S,ICON_UNDERLAY,8,8) + icon = I + +/obj/structure/displaycase/attackby(obj/item/I, mob/user, params) + if(I.GetID() && !broken && openable) + if(allowed(user)) + to_chat(user, "You [open ? "close":"open"] [src].") + toggle_lock(user) + else + to_chat(user, "Access denied.") + else if(open && !showpiece) + if(user.drop_item()) + I.forceMove(src) + showpiece = I + to_chat(user, "You put [I] on display") + update_icon() + else if(istype(I, /obj/item/stack/sheet/glass) && broken) + var/obj/item/stack/sheet/glass/G = I + if(G.get_amount() < 2) + to_chat(user, "You need two glass sheets to fix the case!") + return + to_chat(user, "You start fixing [src]...") + if(do_after(user, 20, target = src)) + G.use(2) + broken = 0 + obj_integrity = max_integrity + update_icon() + else + return ..() + +/obj/structure/displaycase/crowbar_act(mob/user, obj/item/I) //Only applies to the lab cage and player made display cases + if(alert || !openable) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(broken) + if(showpiece) + to_chat(user, "Remove the displayed object first.") + if(I.use_tool(src, user, 0, volume = I.tool_volume)) + to_chat(user, "You remove the destroyed case") + qdel(src) + else + to_chat(user, "You start to [open ? "close":"open"] [src].") + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + to_chat(user, "You [open ? "close":"open"] [src].") + toggle_lock(user) + +obj/structure/displaycase/welder_act(mob/user, obj/item/I) + . = TRUE + if(default_welder_repair(user, I)) + broken = FALSE + +/obj/structure/displaycase/proc/toggle_lock(mob/user) + open = !open + update_icon() + +/obj/structure/displaycase/attack_hand(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + if(showpiece && (broken || open)) + to_chat(user, "You deactivate the hover field built into the case.") + dump() + add_fingerprint(user) + update_icon() + return + else + //prevents remote "kicks" with TK + if(!Adjacent(user)) + return + user.visible_message("[user] kicks the display case.") + user.do_attack_animation(src, ATTACK_EFFECT_KICK) + take_damage(2) + +/obj/structure/displaycase_chassis + anchored = TRUE + density = FALSE + name = "display case chassis" + desc = "The wooden base of a display case." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "glassbox_chassis" + var/obj/item/airlock_electronics/electronics + +/obj/structure/displaycase_chassis/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/airlock_electronics)) + to_chat(user, "You start installing the electronics into [src]...") + playsound(src.loc, I.usesound, 50, 1) + if(do_after(user, 30, target = src)) + if(user.drop_item()) + I.forceMove(src) + electronics = I + to_chat(user, "You install the airlock electronics.") + + else if(istype(I, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = I + if(G.get_amount() < 10) + to_chat(user, "You need ten glass sheets to do this!") + return + to_chat(user, "You start adding [G] to [src]...") + if(do_after(user, 20, target = src)) + G.use(10) + var/obj/structure/displaycase/display = new(src.loc) + if(electronics) + electronics.forceMove(display) + display.electronics = electronics + if(electronics.one_access) + display.req_one_access = electronics.conf_access + else + display.req_access = electronics.conf_access + qdel(src) + else + return ..() + +/obj/structure/displaycase_chassis/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + TOOL_DISMANTLE_SUCCESS_MESSAGE + new /obj/item/stack/sheet/wood(get_turf(src), 5) + qdel(src) + +//The lab cage and captains display case do not spawn with electronics, which is why req_access is needed. +/obj/structure/displaycase/captain + alert = TRUE + start_showpiece_type = /obj/item/gun/energy/laser/captain + req_access = list(ACCESS_CAPTAIN) + +/obj/structure/displaycase/labcage + name = "lab cage" + desc = "A glass lab container for storing interesting creatures." + start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr + req_access = list(ACCESS_RD) + +/obj/structure/displaycase/stechkin + name = "officer's display case" + desc = "A display case containing a humble stechkin pistol. Never forget your roots." + start_showpiece_type = /obj/item/gun/projectile/automatic/pistol + req_access = list(ACCESS_SYNDICATE_COMMAND) diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index ee30932dabeb..354e7fea81b8 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -1,313 +1,313 @@ -/obj/structure/door_assembly - name = "airlock assembly" - icon = 'icons/obj/doors/airlocks/station/public.dmi' - icon_state = "construction" - anchored = FALSE - density = TRUE - max_integrity = 200 - var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' - var/state = AIRLOCK_ASSEMBLY_NEEDS_WIRES - var/mineral - var/base_name = "airlock" - var/obj/item/airlock_electronics/electronics - var/airlock_type = /obj/machinery/door/airlock //the type path of the airlock once completed - var/glass_type = /obj/machinery/door/airlock/glass - var/glass = 0 // 0 = glass can be installed. 1 = glass is already installed. - var/created_name - var/heat_proof_finished = 0 //whether to heat-proof the finished airlock - var/previous_assembly = /obj/structure/door_assembly - var/noglass = FALSE //airlocks with no glass version, also cannot be modified with sheets - var/material_type = /obj/item/stack/sheet/metal - var/material_amt = 4 - -/obj/structure/door_assembly/New() - update_icon() - update_name() - ..() - -/obj/structure/door_assembly/Destroy() - QDEL_NULL(electronics) - return ..() - -/obj/structure/door_assembly/examine(mob/user) - . = ..() - var/doorname = "" - if(created_name) - doorname = ", written on it is '[created_name]'" - switch(state) - if(AIRLOCK_ASSEMBLY_NEEDS_WIRES) - if(anchored) - . += "The anchoring bolts are wrenched in place, but the maintenance panel lacks wiring." - else - . += "The assembly is welded together, but the anchoring bolts are unwrenched." - if(AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) - . += "The maintenance panel is wired, but the circuit slot is empty." - if(AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) - . += "The circuit is connected loosely to its slot, but the maintenance panel is unscrewed and open." - if(!mineral && !glass && !noglass) - . += "There is a small paper placard on the assembly[doorname]. There are empty slots for glass windows and mineral covers." - else if(!mineral && glass && !noglass) - . += "There is a small paper placard on the assembly[doorname]. There are empty slots for mineral covers." - else if(mineral && !glass && !noglass) - . += "There is a small paper placard on the assembly[doorname]. There are empty slots for glass windows." - else - . += "There is a small paper placard on the assembly[doorname]." - -/obj/structure/door_assembly/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the door.", name, created_name),1,MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - created_name = t - return - - else if(iscoil(W) && state == AIRLOCK_ASSEMBLY_NEEDS_WIRES && anchored) - var/obj/item/stack/cable_coil/coil = W - if(coil.get_amount() < 1) - to_chat(user, "You need one length of cable to wire the airlock assembly!") - return - user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly...") - if(do_after(user, 40 * coil.toolspeed, target = src)) - if(coil.get_amount() < 1 || state != AIRLOCK_ASSEMBLY_NEEDS_WIRES) - return - coil.use(1) - state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS - to_chat(user, "You wire the airlock assembly.") - - else if(istype(W, /obj/item/airlock_electronics) && state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS && W.icon_state != "door_electronics_smoked") - playsound(loc, W.usesound, 100, 1) - user.visible_message("[user] installs the electronics into the airlock assembly.", "You start to install electronics into the airlock assembly...") - - if(do_after(user, 40 * W.toolspeed, target = src)) - if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) - return - user.drop_item() - W.forceMove(src) - to_chat(user, "You install the airlock electronics.") - state = AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER - name = "near finished airlock assembly" - electronics = W - - else if(istype(W, /obj/item/stack/sheet) && (!glass || !mineral)) - var/obj/item/stack/sheet/S = W - if(S) - if(S.get_amount() >= 1) - if(!noglass) - if(!glass) - if(istype(S, /obj/item/stack/sheet/rglass) || istype(S, /obj/item/stack/sheet/glass)) - playsound(loc, S.usesound, 100, 1) - user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly...") - if(do_after(user, 40 * S.toolspeed, target = src)) - if(S.get_amount() < 1 || glass) - return - if(S.type == /obj/item/stack/sheet/rglass) - to_chat(user, "You install reinforced glass windows into the airlock assembly.") - heat_proof_finished = TRUE //reinforced glass makes the airlock heat-proof - else - to_chat(user, "You install regular glass windows into the airlock assembly.") - S.use(1) - glass = TRUE - if(!mineral) - if(istype(S, /obj/item/stack/sheet/mineral) && S.sheettype) - var/M = S.sheettype - if(S.get_amount() >= 2) - playsound(loc, S.usesound, 100, 1) - user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly...") - if(do_after(user, 40 * S.toolspeed, target = src)) - if(S.get_amount() < 2 || mineral) - return - to_chat(user, "You install [M] plating into the airlock assembly.") - S.use(2) - var/mineralassembly = text2path("/obj/structure/door_assembly/door_assembly_[M]") - var/obj/structure/door_assembly/MA = new mineralassembly(loc) - transfer_assembly_vars(src, MA, TRUE) - else - to_chat(user, "You need at least two sheets to add a mineral cover!") - else - to_chat(user, "You cannot add [S] to [src]!") - else - to_chat(user, "You cannot add [S] to [src]!") - else - return ..() - update_name() - update_icon() - -/obj/structure/door_assembly/crowbar_act(mob/user, obj/item/I) - if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER ) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] is removing the electronics from the airlock assembly...", "You start to remove electronics from the airlock assembly...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) - return - to_chat(user, "You remove the airlock electronics.") - state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS - name = "wired airlock assembly" - var/obj/item/airlock_electronics/ae - if(!electronics) - ae = new/obj/item/airlock_electronics(loc) - else - ae = electronics - electronics = null - ae.forceMove(loc) - update_icon() - update_name() - -/obj/structure/door_assembly/screwdriver_act(mob/user, obj/item/I) - if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER ) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] is finishing the airlock...", \ - "You start finishing the airlock...") - . = TRUE - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) - return - to_chat(user, "You finish the airlock.") - var/obj/machinery/door/airlock/door - if(glass) - door = new glass_type(loc) - else - door = new airlock_type(loc) - door.setDir(dir) - door.electronics = electronics - door.unres_sides = electronics.unres_sides - door.heat_proof = heat_proof_finished - if(electronics.one_access) - door.req_access = null - door.req_one_access = electronics.conf_access - else - door.req_access = electronics.conf_access - if(created_name) - door.name = created_name - else - door.name = base_name - door.previous_airlock = previous_assembly - electronics.forceMove(door) - qdel(src) - update_icon() - -/obj/structure/door_assembly/wirecutter_act(mob/user, obj/item/I) - if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] is cutting the wires from the airlock assembly...", "You start to cut the wires from airlock assembly...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) - return - to_chat(user, "You cut the wires from the airlock assembly.") - new/obj/item/stack/cable_coil(get_turf(user), 1) - state = AIRLOCK_ASSEMBLY_NEEDS_WIRES - update_icon() - -/obj/structure/door_assembly/wrench_act(mob/user, obj/item/I) - if(state != AIRLOCK_ASSEMBLY_NEEDS_WIRES) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(anchored) - user.visible_message("[user] is unsecuring the airlock assembly from the floor...", "You start to unsecure the airlock assembly from the floor...") - else - user.visible_message("[user] is securing the airlock assembly to the floor...", "You start to secure the airlock assembly to the floor...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_WIRES) - return - to_chat(user, "You [anchored ? "un" : ""]secure the airlock assembly.") - anchored = !anchored - -/obj/structure/door_assembly/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(mineral) - var/obj/item/stack/sheet/mineral/mineral_path = text2path("/obj/item/stack/sheet/mineral/[mineral]") - visible_message("[user] welds the [mineral] plating off [src].",\ - "You start to weld the [mineral] plating off [src]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - to_chat(user, "You weld the [mineral] plating off.") - new mineral_path(loc, 2) - var/obj/structure/door_assembly/PA = new previous_assembly(loc) - transfer_assembly_vars(src, PA) - else if(glass) - visible_message("[user] welds the glass panel out of [src].",\ - "You start to weld the glass panel out of the [src]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - to_chat(user, "You weld the glass panel out.") - if(heat_proof_finished) - new /obj/item/stack/sheet/rglass(get_turf(src)) - heat_proof_finished = FALSE - else - new /obj/item/stack/sheet/glass(get_turf(src)) - glass = FALSE - else if(!anchored) - visible_message("[user] disassembles [src].", \ - "You start to disassemble [src]...",\ - "You hear welding.") - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - to_chat(user, "You disassemble the airlock assembly.") - deconstruct(TRUE) - update_icon() - -/obj/structure/door_assembly/update_icon() - overlays.Cut() - if(!glass) - overlays += get_airlock_overlay("fill_construction", icon) - else if(glass) - overlays += get_airlock_overlay("glass_construction", overlays_file) - overlays += get_airlock_overlay("panel_c[state+1]", overlays_file) - -/obj/structure/door_assembly/proc/update_name() - name = "" - switch(state) - if(AIRLOCK_ASSEMBLY_NEEDS_WIRES) - if(anchored) - name = "secured " - if(AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) - name = "wired " - if(AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) - name = "near finished " - name += "[heat_proof_finished ? "heat-proofed " : ""][glass ? "window " : ""][base_name] assembly" - -/obj/structure/door_assembly/proc/transfer_assembly_vars(obj/structure/door_assembly/source, obj/structure/door_assembly/target, previous = FALSE) - target.glass = source.glass - target.heat_proof_finished = source.heat_proof_finished - target.created_name = source.created_name - target.state = source.state - target.anchored = source.anchored - if(previous) - target.previous_assembly = source.type - if(electronics) - target.electronics = source.electronics - source.electronics.forceMove(target) - target.update_icon() - target.update_name() - qdel(source) - -/obj/structure/door_assembly/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/turf/T = get_turf(src) - if(!disassembled) - material_amt = rand(2,4) - new material_type(T, material_amt) - if(glass) - if(disassembled) - if(heat_proof_finished) - new /obj/item/stack/sheet/rglass(T) - else - new /obj/item/stack/sheet/glass(T) - else - new /obj/item/shard(T) - if(mineral) - var/obj/item/stack/sheet/mineral/mineral_path = text2path("/obj/item/stack/sheet/mineral/[mineral]") - new mineral_path(T, 2) - qdel(src) +/obj/structure/door_assembly + name = "airlock assembly" + icon = 'icons/obj/doors/airlocks/station/public.dmi' + icon_state = "construction" + anchored = FALSE + density = TRUE + max_integrity = 200 + var/overlays_file = 'icons/obj/doors/airlocks/station/overlays.dmi' + var/state = AIRLOCK_ASSEMBLY_NEEDS_WIRES + var/mineral + var/base_name = "airlock" + var/obj/item/airlock_electronics/electronics + var/airlock_type = /obj/machinery/door/airlock //the type path of the airlock once completed + var/glass_type = /obj/machinery/door/airlock/glass + var/glass = 0 // 0 = glass can be installed. 1 = glass is already installed. + var/created_name + var/heat_proof_finished = 0 //whether to heat-proof the finished airlock + var/previous_assembly = /obj/structure/door_assembly + var/noglass = FALSE //airlocks with no glass version, also cannot be modified with sheets + var/material_type = /obj/item/stack/sheet/metal + var/material_amt = 4 + +/obj/structure/door_assembly/New() + update_icon() + update_name() + ..() + +/obj/structure/door_assembly/Destroy() + QDEL_NULL(electronics) + return ..() + +/obj/structure/door_assembly/examine(mob/user) + . = ..() + var/doorname = "" + if(created_name) + doorname = ", written on it is '[created_name]'" + switch(state) + if(AIRLOCK_ASSEMBLY_NEEDS_WIRES) + if(anchored) + . += "The anchoring bolts are wrenched in place, but the maintenance panel lacks wiring." + else + . += "The assembly is welded together, but the anchoring bolts are unwrenched." + if(AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) + . += "The maintenance panel is wired, but the circuit slot is empty." + if(AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) + . += "The circuit is connected loosely to its slot, but the maintenance panel is unscrewed and open." + if(!mineral && !glass && !noglass) + . += "There is a small paper placard on the assembly[doorname]. There are empty slots for glass windows and mineral covers." + else if(!mineral && glass && !noglass) + . += "There is a small paper placard on the assembly[doorname]. There are empty slots for mineral covers." + else if(mineral && !glass && !noglass) + . += "There is a small paper placard on the assembly[doorname]. There are empty slots for glass windows." + else + . += "There is a small paper placard on the assembly[doorname]." + +/obj/structure/door_assembly/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the door.", name, created_name),1,MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + created_name = t + return + + else if(iscoil(W) && state == AIRLOCK_ASSEMBLY_NEEDS_WIRES && anchored) + var/obj/item/stack/cable_coil/coil = W + if(coil.get_amount() < 1) + to_chat(user, "You need one length of cable to wire the airlock assembly!") + return + user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly...") + if(do_after(user, 40 * coil.toolspeed, target = src)) + if(coil.get_amount() < 1 || state != AIRLOCK_ASSEMBLY_NEEDS_WIRES) + return + coil.use(1) + state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS + to_chat(user, "You wire the airlock assembly.") + + else if(istype(W, /obj/item/airlock_electronics) && state == AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS && W.icon_state != "door_electronics_smoked") + playsound(loc, W.usesound, 100, 1) + user.visible_message("[user] installs the electronics into the airlock assembly.", "You start to install electronics into the airlock assembly...") + + if(do_after(user, 40 * W.toolspeed, target = src)) + if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) + return + user.drop_item() + W.forceMove(src) + to_chat(user, "You install the airlock electronics.") + state = AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER + name = "near finished airlock assembly" + electronics = W + + else if(istype(W, /obj/item/stack/sheet) && (!glass || !mineral)) + var/obj/item/stack/sheet/S = W + if(S) + if(S.get_amount() >= 1) + if(!noglass) + if(!glass) + if(istype(S, /obj/item/stack/sheet/rglass) || istype(S, /obj/item/stack/sheet/glass)) + playsound(loc, S.usesound, 100, 1) + user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly...") + if(do_after(user, 40 * S.toolspeed, target = src)) + if(S.get_amount() < 1 || glass) + return + if(S.type == /obj/item/stack/sheet/rglass) + to_chat(user, "You install reinforced glass windows into the airlock assembly.") + heat_proof_finished = TRUE //reinforced glass makes the airlock heat-proof + else + to_chat(user, "You install regular glass windows into the airlock assembly.") + S.use(1) + glass = TRUE + if(!mineral) + if(istype(S, /obj/item/stack/sheet/mineral) && S.sheettype) + var/M = S.sheettype + if(S.get_amount() >= 2) + playsound(loc, S.usesound, 100, 1) + user.visible_message("[user] adds [S.name] to the airlock assembly.", "You start to install [S.name] into the airlock assembly...") + if(do_after(user, 40 * S.toolspeed, target = src)) + if(S.get_amount() < 2 || mineral) + return + to_chat(user, "You install [M] plating into the airlock assembly.") + S.use(2) + var/mineralassembly = text2path("/obj/structure/door_assembly/door_assembly_[M]") + var/obj/structure/door_assembly/MA = new mineralassembly(loc) + transfer_assembly_vars(src, MA, TRUE) + else + to_chat(user, "You need at least two sheets to add a mineral cover!") + else + to_chat(user, "You cannot add [S] to [src]!") + else + to_chat(user, "You cannot add [S] to [src]!") + else + return ..() + update_name() + update_icon() + +/obj/structure/door_assembly/crowbar_act(mob/user, obj/item/I) + if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER ) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] is removing the electronics from the airlock assembly...", "You start to remove electronics from the airlock assembly...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) + return + to_chat(user, "You remove the airlock electronics.") + state = AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS + name = "wired airlock assembly" + var/obj/item/airlock_electronics/ae + if(!electronics) + ae = new/obj/item/airlock_electronics(loc) + else + ae = electronics + electronics = null + ae.forceMove(loc) + update_icon() + update_name() + +/obj/structure/door_assembly/screwdriver_act(mob/user, obj/item/I) + if(state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER ) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] is finishing the airlock...", \ + "You start finishing the airlock...") + . = TRUE + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) + return + to_chat(user, "You finish the airlock.") + var/obj/machinery/door/airlock/door + if(glass) + door = new glass_type(loc) + else + door = new airlock_type(loc) + door.setDir(dir) + door.electronics = electronics + door.unres_sides = electronics.unres_sides + door.heat_proof = heat_proof_finished + if(electronics.one_access) + door.req_access = null + door.req_one_access = electronics.conf_access + else + door.req_access = electronics.conf_access + if(created_name) + door.name = created_name + else + door.name = base_name + door.previous_airlock = previous_assembly + electronics.forceMove(door) + qdel(src) + update_icon() + +/obj/structure/door_assembly/wirecutter_act(mob/user, obj/item/I) + if(state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] is cutting the wires from the airlock assembly...", "You start to cut the wires from airlock assembly...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) + return + to_chat(user, "You cut the wires from the airlock assembly.") + new/obj/item/stack/cable_coil(get_turf(user), 1) + state = AIRLOCK_ASSEMBLY_NEEDS_WIRES + update_icon() + +/obj/structure/door_assembly/wrench_act(mob/user, obj/item/I) + if(state != AIRLOCK_ASSEMBLY_NEEDS_WIRES) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(anchored) + user.visible_message("[user] is unsecuring the airlock assembly from the floor...", "You start to unsecure the airlock assembly from the floor...") + else + user.visible_message("[user] is securing the airlock assembly to the floor...", "You start to secure the airlock assembly to the floor...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != AIRLOCK_ASSEMBLY_NEEDS_WIRES) + return + to_chat(user, "You [anchored ? "un" : ""]secure the airlock assembly.") + anchored = !anchored + +/obj/structure/door_assembly/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(mineral) + var/obj/item/stack/sheet/mineral/mineral_path = text2path("/obj/item/stack/sheet/mineral/[mineral]") + visible_message("[user] welds the [mineral] plating off [src].",\ + "You start to weld the [mineral] plating off [src]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, volume = I.tool_volume)) + return + to_chat(user, "You weld the [mineral] plating off.") + new mineral_path(loc, 2) + var/obj/structure/door_assembly/PA = new previous_assembly(loc) + transfer_assembly_vars(src, PA) + else if(glass) + visible_message("[user] welds the glass panel out of [src].",\ + "You start to weld the glass panel out of the [src]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, volume = I.tool_volume)) + return + to_chat(user, "You weld the glass panel out.") + if(heat_proof_finished) + new /obj/item/stack/sheet/rglass(get_turf(src)) + heat_proof_finished = FALSE + else + new /obj/item/stack/sheet/glass(get_turf(src)) + glass = FALSE + else if(!anchored) + visible_message("[user] disassembles [src].", \ + "You start to disassemble [src]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, volume = I.tool_volume)) + return + to_chat(user, "You disassemble the airlock assembly.") + deconstruct(TRUE) + update_icon() + +/obj/structure/door_assembly/update_icon() + overlays.Cut() + if(!glass) + overlays += get_airlock_overlay("fill_construction", icon) + else if(glass) + overlays += get_airlock_overlay("glass_construction", overlays_file) + overlays += get_airlock_overlay("panel_c[state+1]", overlays_file) + +/obj/structure/door_assembly/proc/update_name() + name = "" + switch(state) + if(AIRLOCK_ASSEMBLY_NEEDS_WIRES) + if(anchored) + name = "secured " + if(AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS) + name = "wired " + if(AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER) + name = "near finished " + name += "[heat_proof_finished ? "heat-proofed " : ""][glass ? "window " : ""][base_name] assembly" + +/obj/structure/door_assembly/proc/transfer_assembly_vars(obj/structure/door_assembly/source, obj/structure/door_assembly/target, previous = FALSE) + target.glass = source.glass + target.heat_proof_finished = source.heat_proof_finished + target.created_name = source.created_name + target.state = source.state + target.anchored = source.anchored + if(previous) + target.previous_assembly = source.type + if(electronics) + target.electronics = source.electronics + source.electronics.forceMove(target) + target.update_icon() + target.update_name() + qdel(source) + +/obj/structure/door_assembly/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/turf/T = get_turf(src) + if(!disassembled) + material_amt = rand(2,4) + new material_type(T, material_amt) + if(glass) + if(disassembled) + if(heat_proof_finished) + new /obj/item/stack/sheet/rglass(T) + else + new /obj/item/stack/sheet/glass(T) + else + new /obj/item/shard(T) + if(mineral) + var/obj/item/stack/sheet/mineral/mineral_path = text2path("/obj/item/stack/sheet/mineral/[mineral]") + new mineral_path(T, 2) + qdel(src) diff --git a/code/game/objects/structures/dresser.dm b/code/game/objects/structures/dresser.dm index f432e19b51a6..878b03436ffc 100644 --- a/code/game/objects/structures/dresser.dm +++ b/code/game/objects/structures/dresser.dm @@ -79,4 +79,4 @@ /obj/structure/dresser/deconstruct(disassembled = TRUE) new /obj/item/stack/sheet/wood(drop_location(), 30) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/structures/electricchair.dm b/code/game/objects/structures/electricchair.dm index 995e86b5dfbd..792558d01fa2 100644 --- a/code/game/objects/structures/electricchair.dm +++ b/code/game/objects/structures/electricchair.dm @@ -80,4 +80,4 @@ spawn(1) buckled_mob.electrocute_act(110, src, 1) A.power_light = light - A.updateicon() \ No newline at end of file + A.updateicon() diff --git a/code/game/objects/structures/engicart.dm b/code/game/objects/structures/engicart.dm index a005e59c5a57..a77dc35b909a 100644 --- a/code/game/objects/structures/engicart.dm +++ b/code/game/objects/structures/engicart.dm @@ -179,4 +179,4 @@ if(myredtoolbox) overlays += "cart_redtoolbox" if(myyellowtoolbox) - overlays += "cart_yellowtoolbox" \ No newline at end of file + overlays += "cart_yellowtoolbox" diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index aeb5579981d4..cdc5cdb58c49 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -1,355 +1,356 @@ -/* - * False Walls - */ - -// Minimum pressure difference to fail building falsewalls. -// Also affects admin alerts. -#define FALSEDOOR_MAX_PRESSURE_DIFF 25.0 - -/obj/structure/falsewall - name = "wall" - desc = "A huge chunk of metal used to seperate rooms." - anchored = TRUE - icon = 'icons/turf/walls/wall.dmi' - icon_state = "wall" - - var/mineral = /obj/item/stack/sheet/metal - var/mineral_amount = 2 - var/walltype = /turf/simulated/wall - var/girder_type = /obj/structure/girder/displaced - var/opening = FALSE - - density = TRUE - opacity = TRUE - max_integrity = 100 - - canSmoothWith = list( - /turf/simulated/wall, - /turf/simulated/wall/r_wall, - /obj/structure/falsewall, - /obj/structure/falsewall/brass, - /obj/structure/falsewall/reinforced, // WHY DO WE SMOOTH WITH FALSE R-WALLS WHEN WE DON'T SMOOTH WITH REAL R-WALLS. //because we do smooth with real r-walls now - /turf/simulated/wall/rust, - /turf/simulated/wall/r_wall/rust) - smooth = SMOOTH_TRUE - -/obj/structure/falsewall/New(loc) - ..() - air_update_turf(1) - -/obj/structure/falsewall/ratvar_act() - new /obj/structure/falsewall/brass(loc) - qdel(src) - -/obj/structure/falsewall/Destroy() - density = 0 - air_update_turf(1) - return ..() - -/obj/structure/falsewall/CanAtmosPass(turf/T) - return !density - -/obj/structure/falsewall/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - toggle(user) - -/obj/structure/falsewall/attack_hand(mob/user) - toggle(user) - -/obj/structure/falsewall/proc/toggle(mob/user) - if(opening) - return - - opening = 1 - if(density) - do_the_flick() - sleep(4) - density = 0 - set_opacity(0) - update_icon(0) - else - var/srcturf = get_turf(src) - for(var/mob/living/obstacle in srcturf) //Stop people from using this as a shield - opening = 0 - return - do_the_flick() - density = 1 - sleep(4) - set_opacity(1) - update_icon() - air_update_turf(1) - opening = 0 - -/obj/structure/falsewall/proc/do_the_flick() - if(density) - smooth = SMOOTH_FALSE - clear_smooth_overlays() - icon_state = "fwall_opening" - else - icon_state = "fwall_closing" - -/obj/structure/falsewall/update_icon() - if(density) - icon_state = initial(icon_state) - smooth = SMOOTH_TRUE - queue_smooth(src) - else - icon_state = "fwall_open" - -/obj/structure/falsewall/proc/ChangeToWall(delete = TRUE) - var/turf/T = get_turf(src) - T.ChangeTurf(walltype) - if(delete) - qdel(src) - return T - -/obj/structure/falsewall/attackby(obj/item/W, mob/user, params) - if(opening) - to_chat(user, "You must wait until the door has stopped moving.") - return - - if(density) - var/turf/T = get_turf(src) - if(T.density) - to_chat(user, "[src] is blocked!") - return - if(istype(W, /obj/item/screwdriver)) - if(!istype(T, /turf/simulated/floor)) - to_chat(user, "[src] bolts must be tightened on the floor!") - return - user.visible_message("[user] tightens some bolts on the wall.", "You tighten the bolts on the wall.") - ChangeToWall() - else - to_chat(user, "You can't reach, close it first!") - - if(istype(W, /obj/item/gun/energy/plasmacutter) || istype(W, /obj/item/pickaxe/drill/diamonddrill) || istype(W, /obj/item/pickaxe/drill/jackhammer) || istype(W, /obj/item/melee/energy/blade)) - dismantle(user, TRUE) - -/obj/structure/falsewall/welder_act(mob/user, obj/item/I) - if(!density) - return - . = TRUE - if(!I.use_tool(src, user, volume = I.tool_volume)) - return - dismantle(user, TRUE) - -/obj/structure/falsewall/proc/dismantle(mob/user, disassembled = TRUE) - user.visible_message("[user] dismantles the false wall.", "You dismantle the false wall.") - playsound(src, 'sound/items/welder.ogg', 100, TRUE) - deconstruct(disassembled) - -/obj/structure/falsewall/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - new girder_type(loc) - if(mineral_amount) - for(var/i in 1 to mineral_amount) - new mineral(loc) - qdel(src) - -/* - * False R-Walls - */ - -/obj/structure/falsewall/reinforced - name = "reinforced wall" - desc = "A huge chunk of reinforced metal used to seperate rooms." - icon = 'icons/turf/walls/reinforced_wall.dmi' - icon_state = "r_wall" - walltype = /turf/simulated/wall/r_wall - mineral = /obj/item/stack/sheet/plasteel - -/obj/structure/falsewall/reinforced/ChangeToWall(delete = 1) - var/turf/T = get_turf(src) - T.ChangeTurf(/turf/simulated/wall/r_wall) - if(delete) - qdel(src) - return T - -/* - * Uranium Falsewalls - */ - -/obj/structure/falsewall/uranium - name = "uranium wall" - desc = "A wall with uranium plating. This is probably a bad idea." - icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" - mineral = /obj/item/stack/sheet/mineral/uranium - walltype = /turf/simulated/wall/mineral/uranium - var/active = null - var/last_event = 0 - canSmoothWith = list(/obj/structure/falsewall/uranium, /turf/simulated/wall/mineral/uranium) - -/obj/structure/falsewall/uranium/attackby(obj/item/W as obj, mob/user as mob, params) - radiate() - ..() - -/obj/structure/falsewall/uranium/attack_hand(mob/user as mob) - radiate() - ..() - -/obj/structure/falsewall/uranium/proc/radiate() - if(!active) - if(world.time > last_event+15) - active = 1 - for(var/mob/living/L in range(3,src)) - L.apply_effect(12,IRRADIATE,0) - for(var/turf/simulated/wall/mineral/uranium/T in range(3,src)) - T.radiate() - last_event = world.time - active = null - return - return -/* - * Other misc falsewall types - */ - -/obj/structure/falsewall/gold - name = "gold wall" - desc = "A wall with gold plating. Swag!" - icon = 'icons/turf/walls/gold_wall.dmi' - icon_state = "gold" - mineral = /obj/item/stack/sheet/mineral/gold - walltype = /turf/simulated/wall/mineral/gold - canSmoothWith = list(/obj/structure/falsewall/gold, /turf/simulated/wall/mineral/gold) - -/obj/structure/falsewall/silver - name = "silver wall" - desc = "A wall with silver plating. Shiny." - icon = 'icons/turf/walls/silver_wall.dmi' - icon_state = "silver" - mineral = /obj/item/stack/sheet/mineral/silver - walltype = /turf/simulated/wall/mineral/silver - canSmoothWith = list(/obj/structure/falsewall/silver, /turf/simulated/wall/mineral/silver) - -/obj/structure/falsewall/diamond - name = "diamond wall" - desc = "A wall with diamond plating. You monster." - icon = 'icons/turf/walls/diamond_wall.dmi' - icon_state = "diamond" - mineral = /obj/item/stack/sheet/mineral/diamond - walltype = /turf/simulated/wall/mineral/diamond - canSmoothWith = list(/obj/structure/falsewall/diamond, /turf/simulated/wall/mineral/diamond) - max_integrity = 800 - - -/obj/structure/falsewall/plasma - name = "plasma wall" - desc = "A wall with plasma plating. This is definately a bad idea." - icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" - mineral = /obj/item/stack/sheet/mineral/plasma - walltype = /turf/simulated/wall/mineral/plasma - canSmoothWith = list(/obj/structure/falsewall/plasma, /turf/simulated/wall/mineral/plasma, /turf/simulated/wall/mineral/alien) - -/obj/structure/falsewall/plasma/attackby(obj/item/W, mob/user, params) - if(is_hot(W) > 300) - message_admins("Plasma falsewall ignited by [key_name_admin(user)] in [ADMIN_VERBOSEJMP(T)]") - log_game("Plasma falsewall ignited by [key_name(user)] in [AREACOORD(T)]") - investigate_log("was ignited by [key_name(user)]","atmos") - burnbabyburn() - else - return ..() - -/obj/structure/falsewall/plasma/proc/burnbabyburn(user) - playsound(src, 'sound/items/welder.ogg', 100, 1) - atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 400) - new /obj/structure/girder/displaced(loc) - qdel(src) - -/obj/structure/falsewall/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 300) - burnbabyburn() - -/obj/structure/falsewall/alien - name = "alien wall" - desc = "A strange-looking alien wall." - icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" - mineral = /obj/item/stack/sheet/mineral/abductor - walltype = /turf/simulated/wall/mineral/abductor - canSmoothWith = list(/obj/structure/falsewall/alien, /turf/simulated/wall/mineral/alien) - - -/obj/structure/falsewall/bananium - name = "bananium wall" - desc = "A wall with bananium plating. Honk!" - icon = 'icons/turf/walls/bananium_wall.dmi' - icon_state = "bananium" - mineral = /obj/item/stack/sheet/mineral/bananium - walltype = /turf/simulated/wall/mineral/bananium - canSmoothWith = list(/obj/structure/falsewall/bananium, /turf/simulated/wall/mineral/bananium) - -/obj/structure/falsewall/sandstone - name = "sandstone wall" - desc = "A wall with sandstone plating." - icon_state = "sandstone" - mineral = /obj/item/stack/sheet/mineral/sandstone - walltype = /turf/simulated/wall/mineral/sandstone - canSmoothWith = list(/obj/structure/falsewall/sandstone, /turf/simulated/wall/mineral/sandstone) - -/obj/structure/falsewall/wood - name = "wooden wall" - desc = "A wall with wooden plating. Stiff." - icon = 'icons/turf/walls/wood_wall.dmi' - icon_state = "wood" - mineral = /obj/item/stack/sheet/wood - walltype = /turf/simulated/wall/mineral/wood - canSmoothWith = list(/obj/structure/falsewall/wood, /turf/simulated/wall/mineral/wood) - -/obj/structure/falsewall/iron - name = "rough metal wall" - desc = "A wall with rough metal plating." - icon = 'icons/turf/walls/iron_wall.dmi' - icon_state = "iron" - mineral = /obj/item/stack/rods - mineral_amount = 5 - walltype = /turf/simulated/wall/mineral/iron - canSmoothWith = list(/obj/structure/falsewall/iron, /turf/simulated/wall/mineral/iron) - -/obj/structure/falsewall/abductor - name = "alien wall" - desc = "A wall with alien alloy plating." - icon = 'icons/turf/walls/abductor_wall.dmi' - icon_state = "abductor" - mineral = /obj/item/stack/sheet/mineral/abductor - walltype = /turf/simulated/wall/mineral/abductor - canSmoothWith = list(/obj/structure/falsewall/abductor, /turf/simulated/wall/mineral/abductor) - -/obj/structure/falsewall/titanium - desc = "A light-weight titanium wall used in shuttles." - icon = 'icons/turf/walls/shuttle_wall.dmi' - icon_state = "shuttle" - mineral = /obj/item/stack/sheet/mineral/titanium - walltype = /turf/simulated/wall/mineral/titanium - smooth = SMOOTH_MORE - canSmoothWith = list(/turf/simulated/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine/heater) - -/obj/structure/falsewall/plastitanium - desc = "An evil wall of plasma and titanium." - icon = 'icons/turf/walls/plastitanium_wall.dmi' - icon_state = "shuttle" - mineral = /obj/item/stack/sheet/mineral/plastitanium - walltype = /turf/simulated/wall/mineral/plastitanium - smooth = SMOOTH_MORE - canSmoothWith = list(/turf/simulated/wall/mineral/plastitanium, /turf/simulated/wall/mineral/plastitanium/nodiagonal, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine/heater) - -/obj/structure/falsewall/brass - name = "clockwork wall" - desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." - icon = 'icons/turf/walls/clockwork_wall.dmi' - icon_state = "clockwork_wall" - resistance_flags = FIRE_PROOF | ACID_PROOF - mineral_amount = 1 - canSmoothWith = list(/obj/effect/clockwork/overlay/wall, /obj/structure/falsewall/brass) - girder_type = /obj/structure/clockwork/wall_gear/displaced - walltype = /turf/simulated/wall/clockwork - mineral = /obj/item/stack/tile/brass - -/obj/structure/falsewall/brass/New(loc) - ..() - var/turf/T = get_turf(src) - new /obj/effect/temp_visual/ratvar/wall/false(T) - new /obj/effect/temp_visual/ratvar/beam/falsewall(T) +/* + * False Walls + */ + +// Minimum pressure difference to fail building falsewalls. +// Also affects admin alerts. +#define FALSEDOOR_MAX_PRESSURE_DIFF 25.0 + +/obj/structure/falsewall + name = "wall" + desc = "A huge chunk of metal used to seperate rooms." + anchored = TRUE + icon = 'icons/turf/walls/wall.dmi' + icon_state = "wall" + + var/mineral = /obj/item/stack/sheet/metal + var/mineral_amount = 2 + var/walltype = /turf/simulated/wall + var/girder_type = /obj/structure/girder/displaced + var/opening = FALSE + + density = TRUE + opacity = TRUE + max_integrity = 100 + + canSmoothWith = list( + /turf/simulated/wall, + /turf/simulated/wall/r_wall, + /obj/structure/falsewall, + /obj/structure/falsewall/brass, + /obj/structure/falsewall/reinforced, // WHY DO WE SMOOTH WITH FALSE R-WALLS WHEN WE DON'T SMOOTH WITH REAL R-WALLS. //because we do smooth with real r-walls now + /turf/simulated/wall/rust, + /turf/simulated/wall/r_wall/rust) + smooth = SMOOTH_TRUE + +/obj/structure/falsewall/New(loc) + ..() + air_update_turf(1) + +/obj/structure/falsewall/ratvar_act() + new /obj/structure/falsewall/brass(loc) + qdel(src) + +/obj/structure/falsewall/Destroy() + density = 0 + air_update_turf(1) + return ..() + +/obj/structure/falsewall/CanAtmosPass(turf/T) + return !density + +/obj/structure/falsewall/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + toggle(user) + +/obj/structure/falsewall/attack_hand(mob/user) + toggle(user) + +/obj/structure/falsewall/proc/toggle(mob/user) + if(opening) + return + + opening = 1 + if(density) + do_the_flick() + sleep(4) + density = 0 + set_opacity(0) + update_icon(0) + else + var/srcturf = get_turf(src) + for(var/mob/living/obstacle in srcturf) //Stop people from using this as a shield + opening = 0 + return + do_the_flick() + density = 1 + sleep(4) + set_opacity(1) + update_icon() + air_update_turf(1) + opening = 0 + +/obj/structure/falsewall/proc/do_the_flick() + if(density) + smooth = SMOOTH_FALSE + clear_smooth_overlays() + icon_state = "fwall_opening" + else + icon_state = "fwall_closing" + +/obj/structure/falsewall/update_icon() + if(density) + icon_state = initial(icon_state) + smooth = SMOOTH_TRUE + queue_smooth(src) + else + icon_state = "fwall_open" + +/obj/structure/falsewall/proc/ChangeToWall(delete = TRUE) + var/turf/T = get_turf(src) + T.ChangeTurf(walltype) + if(delete) + qdel(src) + return T + +/obj/structure/falsewall/attackby(obj/item/W, mob/user, params) + if(opening) + to_chat(user, "You must wait until the door has stopped moving.") + return + + if(density) + var/turf/T = get_turf(src) + if(T.density) + to_chat(user, "[src] is blocked!") + return + if(istype(W, /obj/item/screwdriver)) + if(!istype(T, /turf/simulated/floor)) + to_chat(user, "[src] bolts must be tightened on the floor!") + return + user.visible_message("[user] tightens some bolts on the wall.", "You tighten the bolts on the wall.") + ChangeToWall() + else + to_chat(user, "You can't reach, close it first!") + + if(istype(W, /obj/item/gun/energy/plasmacutter) || istype(W, /obj/item/pickaxe/drill/diamonddrill) || istype(W, /obj/item/pickaxe/drill/jackhammer) || istype(W, /obj/item/melee/energy/blade)) + dismantle(user, TRUE) + +/obj/structure/falsewall/welder_act(mob/user, obj/item/I) + if(!density) + return + . = TRUE + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + dismantle(user, TRUE) + +/obj/structure/falsewall/proc/dismantle(mob/user, disassembled = TRUE) + user.visible_message("[user] dismantles the false wall.", "You dismantle the false wall.") + playsound(src, 'sound/items/welder.ogg', 100, TRUE) + deconstruct(disassembled) + +/obj/structure/falsewall/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + new girder_type(loc) + if(mineral_amount) + for(var/i in 1 to mineral_amount) + new mineral(loc) + qdel(src) + +/* + * False R-Walls + */ + +/obj/structure/falsewall/reinforced + name = "reinforced wall" + desc = "A huge chunk of reinforced metal used to seperate rooms." + icon = 'icons/turf/walls/reinforced_wall.dmi' + icon_state = "r_wall" + walltype = /turf/simulated/wall/r_wall + mineral = /obj/item/stack/sheet/plasteel + +/obj/structure/falsewall/reinforced/ChangeToWall(delete = 1) + var/turf/T = get_turf(src) + T.ChangeTurf(/turf/simulated/wall/r_wall) + if(delete) + qdel(src) + return T + +/* + * Uranium Falsewalls + */ + +/obj/structure/falsewall/uranium + name = "uranium wall" + desc = "A wall with uranium plating. This is probably a bad idea." + icon = 'icons/turf/walls/uranium_wall.dmi' + icon_state = "uranium" + mineral = /obj/item/stack/sheet/mineral/uranium + walltype = /turf/simulated/wall/mineral/uranium + var/active = null + var/last_event = 0 + canSmoothWith = list(/obj/structure/falsewall/uranium, /turf/simulated/wall/mineral/uranium) + +/obj/structure/falsewall/uranium/attackby(obj/item/W as obj, mob/user as mob, params) + radiate() + ..() + +/obj/structure/falsewall/uranium/attack_hand(mob/user as mob) + radiate() + ..() + +/obj/structure/falsewall/uranium/proc/radiate() + if(!active) + if(world.time > last_event+15) + active = 1 + for(var/mob/living/L in range(3,src)) + L.apply_effect(12,IRRADIATE,0) + for(var/turf/simulated/wall/mineral/uranium/T in range(3,src)) + T.radiate() + last_event = world.time + active = null + return + return +/* + * Other misc falsewall types + */ + +/obj/structure/falsewall/gold + name = "gold wall" + desc = "A wall with gold plating. Swag!" + icon = 'icons/turf/walls/gold_wall.dmi' + icon_state = "gold" + mineral = /obj/item/stack/sheet/mineral/gold + walltype = /turf/simulated/wall/mineral/gold + canSmoothWith = list(/obj/structure/falsewall/gold, /turf/simulated/wall/mineral/gold) + +/obj/structure/falsewall/silver + name = "silver wall" + desc = "A wall with silver plating. Shiny." + icon = 'icons/turf/walls/silver_wall.dmi' + icon_state = "silver" + mineral = /obj/item/stack/sheet/mineral/silver + walltype = /turf/simulated/wall/mineral/silver + canSmoothWith = list(/obj/structure/falsewall/silver, /turf/simulated/wall/mineral/silver) + +/obj/structure/falsewall/diamond + name = "diamond wall" + desc = "A wall with diamond plating. You monster." + icon = 'icons/turf/walls/diamond_wall.dmi' + icon_state = "diamond" + mineral = /obj/item/stack/sheet/mineral/diamond + walltype = /turf/simulated/wall/mineral/diamond + canSmoothWith = list(/obj/structure/falsewall/diamond, /turf/simulated/wall/mineral/diamond) + max_integrity = 800 + + +/obj/structure/falsewall/plasma + name = "plasma wall" + desc = "A wall with plasma plating. This is definately a bad idea." + icon = 'icons/turf/walls/plasma_wall.dmi' + icon_state = "plasma" + mineral = /obj/item/stack/sheet/mineral/plasma + walltype = /turf/simulated/wall/mineral/plasma + canSmoothWith = list(/obj/structure/falsewall/plasma, /turf/simulated/wall/mineral/plasma, /turf/simulated/wall/mineral/alien) + +/obj/structure/falsewall/plasma/attackby(obj/item/W, mob/user, params) + if(is_hot(W) > 300) + var/turf/T = locate(user) + message_admins("Plasma falsewall ignited by [key_name_admin(user)] in [ADMIN_VERBOSEJMP(T)]") + log_game("Plasma falsewall ignited by [key_name(user)] in [AREACOORD(T)]") + investigate_log("was ignited by [key_name(user)]","atmos") + burnbabyburn() + else + return ..() + +/obj/structure/falsewall/plasma/proc/burnbabyburn(user) + playsound(src, 'sound/items/welder.ogg', 100, 1) + atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 400) + new /obj/structure/girder/displaced(loc) + qdel(src) + +/obj/structure/falsewall/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 300) + burnbabyburn() + +/obj/structure/falsewall/alien + name = "alien wall" + desc = "A strange-looking alien wall." + icon = 'icons/turf/walls/plasma_wall.dmi' + icon_state = "plasma" + mineral = /obj/item/stack/sheet/mineral/abductor + walltype = /turf/simulated/wall/mineral/abductor + canSmoothWith = list(/obj/structure/falsewall/alien, /turf/simulated/wall/mineral/alien) + + +/obj/structure/falsewall/bananium + name = "bananium wall" + desc = "A wall with bananium plating. Honk!" + icon = 'icons/turf/walls/bananium_wall.dmi' + icon_state = "bananium" + mineral = /obj/item/stack/sheet/mineral/bananium + walltype = /turf/simulated/wall/mineral/bananium + canSmoothWith = list(/obj/structure/falsewall/bananium, /turf/simulated/wall/mineral/bananium) + +/obj/structure/falsewall/sandstone + name = "sandstone wall" + desc = "A wall with sandstone plating." + icon_state = "sandstone" + mineral = /obj/item/stack/sheet/mineral/sandstone + walltype = /turf/simulated/wall/mineral/sandstone + canSmoothWith = list(/obj/structure/falsewall/sandstone, /turf/simulated/wall/mineral/sandstone) + +/obj/structure/falsewall/wood + name = "wooden wall" + desc = "A wall with wooden plating. Stiff." + icon = 'icons/turf/walls/wood_wall.dmi' + icon_state = "wood" + mineral = /obj/item/stack/sheet/wood + walltype = /turf/simulated/wall/mineral/wood + canSmoothWith = list(/obj/structure/falsewall/wood, /turf/simulated/wall/mineral/wood) + +/obj/structure/falsewall/iron + name = "rough metal wall" + desc = "A wall with rough metal plating." + icon = 'icons/turf/walls/iron_wall.dmi' + icon_state = "iron" + mineral = /obj/item/stack/rods + mineral_amount = 5 + walltype = /turf/simulated/wall/mineral/iron + canSmoothWith = list(/obj/structure/falsewall/iron, /turf/simulated/wall/mineral/iron) + +/obj/structure/falsewall/abductor + name = "alien wall" + desc = "A wall with alien alloy plating." + icon = 'icons/turf/walls/abductor_wall.dmi' + icon_state = "abductor" + mineral = /obj/item/stack/sheet/mineral/abductor + walltype = /turf/simulated/wall/mineral/abductor + canSmoothWith = list(/obj/structure/falsewall/abductor, /turf/simulated/wall/mineral/abductor) + +/obj/structure/falsewall/titanium + desc = "A light-weight titanium wall used in shuttles." + icon = 'icons/turf/walls/shuttle_wall.dmi' + icon_state = "shuttle" + mineral = /obj/item/stack/sheet/mineral/titanium + walltype = /turf/simulated/wall/mineral/titanium + smooth = SMOOTH_MORE + canSmoothWith = list(/turf/simulated/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine/heater) + +/obj/structure/falsewall/plastitanium + desc = "An evil wall of plasma and titanium." + icon = 'icons/turf/walls/plastitanium_wall.dmi' + icon_state = "shuttle" + mineral = /obj/item/stack/sheet/mineral/plastitanium + walltype = /turf/simulated/wall/mineral/plastitanium + smooth = SMOOTH_MORE + canSmoothWith = list(/turf/simulated/wall/mineral/plastitanium, /turf/simulated/wall/mineral/plastitanium/nodiagonal, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine/heater) + +/obj/structure/falsewall/brass + name = "clockwork wall" + desc = "A huge chunk of warm metal. The clanging of machinery emanates from within." + icon = 'icons/turf/walls/clockwork_wall.dmi' + icon_state = "clockwork_wall" + resistance_flags = FIRE_PROOF | ACID_PROOF + mineral_amount = 1 + canSmoothWith = list(/obj/effect/clockwork/overlay/wall, /obj/structure/falsewall/brass) + girder_type = /obj/structure/clockwork/wall_gear/displaced + walltype = /turf/simulated/wall/clockwork + mineral = /obj/item/stack/tile/brass + +/obj/structure/falsewall/brass/New(loc) + ..() + var/turf/T = get_turf(src) + new /obj/effect/temp_visual/ratvar/wall/false(T) + new /obj/effect/temp_visual/ratvar/beam/falsewall(T) diff --git a/code/game/objects/structures/flora.dm b/code/game/objects/structures/flora.dm index 7830fd4f1019..b836760a9dfb 100644 --- a/code/game/objects/structures/flora.dm +++ b/code/game/objects/structures/flora.dm @@ -1,346 +1,346 @@ -/obj/structure/flora - resistance_flags = FLAMMABLE - max_integrity = 150 - -//trees -/obj/structure/flora/tree - name = "tree" - anchored = 1 - density = 1 - pixel_x = -16 - layer = 9 - -/obj/structure/flora/tree/pine - name = "pine tree" - icon = 'icons/obj/flora/pinetrees.dmi' - icon_state = "pine_1" - -/obj/structure/flora/tree/pine/New() - ..() - icon_state = "pine_[rand(1, 3)]" - -/obj/structure/flora/tree/pine/xmas - name = "xmas tree" - icon = 'icons/obj/flora/pinetrees.dmi' - icon_state = "pine_c" - -/obj/structure/flora/tree/pine/xmas/New() - ..() - icon_state = "pine_c" - -/obj/structure/flora/tree/dead - icon = 'icons/obj/flora/deadtrees.dmi' - icon_state = "tree_1" - -/obj/structure/flora/tree/dead/New() - ..() - icon_state = "tree_[rand(1, 6)]" - -/obj/structure/flora/tree/palm - icon = 'icons/misc/beach2.dmi' - icon_state = "palm1" - -/obj/structure/flora/tree/palm/New() - ..() - icon_state = pick("palm1","palm2") - pixel_x = 0 - -//grass -/obj/structure/flora/grass - name = "grass" - icon = 'icons/obj/flora/snowflora.dmi' - anchored = 1 - -/obj/structure/flora/grass/brown - icon_state = "snowgrass1bb" - -/obj/structure/flora/grass/brown/New() - ..() - icon_state = "snowgrass[rand(1, 3)]bb" - - -/obj/structure/flora/grass/green - icon_state = "snowgrass1gb" - -/obj/structure/flora/grass/green/New() - ..() - icon_state = "snowgrass[rand(1, 3)]gb" - -/obj/structure/flora/grass/both - icon_state = "snowgrassall1" - -/obj/structure/flora/grass/both/New() - ..() - icon_state = "snowgrassall[rand(1, 3)]" - - -//bushes -/obj/structure/flora/bush - name = "bush" - icon = 'icons/obj/flora/snowflora.dmi' - icon_state = "snowbush1" - anchored = 1 - -/obj/structure/flora/bush/New() - ..() - icon_state = "snowbush[rand(1, 6)]" - -//newbushes - -/obj/structure/flora/ausbushes - name = "bush" - icon = 'icons/obj/flora/ausflora.dmi' - icon_state = "firstbush_1" - anchored = 1 - -/obj/structure/flora/ausbushes/New() - ..() - icon_state = "firstbush_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/reedbush - icon_state = "reedbush_1" - -/obj/structure/flora/ausbushes/reedbush/New() - ..() - icon_state = "reedbush_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/leafybush - icon_state = "leafybush_1" - -/obj/structure/flora/ausbushes/leafybush/New() - ..() - icon_state = "leafybush_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/palebush - icon_state = "palebush_1" - -/obj/structure/flora/ausbushes/palebush/New() - ..() - icon_state = "palebush_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/stalkybush - icon_state = "stalkybush_1" - -/obj/structure/flora/ausbushes/stalkybush/New() - ..() - icon_state = "stalkybush_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/grassybush - icon_state = "grassybush_1" - -/obj/structure/flora/ausbushes/grassybush/New() - ..() - icon_state = "grassybush_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/fernybush - icon_state = "fernybush_1" - -/obj/structure/flora/ausbushes/fernybush/New() - ..() - icon_state = "fernybush_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/sunnybush - icon_state = "sunnybush_1" - -/obj/structure/flora/ausbushes/sunnybush/New() - ..() - icon_state = "sunnybush_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/genericbush - icon_state = "genericbush_1" - -/obj/structure/flora/ausbushes/genericbush/New() - ..() - icon_state = "genericbush_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/pointybush - icon_state = "pointybush_1" - -/obj/structure/flora/ausbushes/pointybush/New() - ..() - icon_state = "pointybush_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/lavendergrass - icon_state = "lavendergrass_1" - -/obj/structure/flora/ausbushes/lavendergrass/New() - ..() - icon_state = "lavendergrass_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/ywflowers - icon_state = "ywflowers_1" - -/obj/structure/flora/ausbushes/ywflowers/New() - ..() - icon_state = "ywflowers_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/brflowers - icon_state = "brflowers_1" - -/obj/structure/flora/ausbushes/brflowers/New() - ..() - icon_state = "brflowers_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/ppflowers - icon_state = "ppflowers_1" - -/obj/structure/flora/ausbushes/ppflowers/New() - ..() - icon_state = "ppflowers_[rand(1, 4)]" - -/obj/structure/flora/ausbushes/sparsegrass - icon_state = "sparsegrass_1" - -/obj/structure/flora/ausbushes/sparsegrass/New() - ..() - icon_state = "sparsegrass_[rand(1, 3)]" - -/obj/structure/flora/ausbushes/fullgrass - icon_state = "fullgrass_1" - -/obj/structure/flora/ausbushes/fullgrass/New() - ..() - icon_state = "fullgrass_[rand(1, 3)]" - - -/obj/item/twohanded/required/kirbyplants - name = "potted plant" - icon = 'icons/obj/flora/plants.dmi' - icon_state = "plant-1" - anchored = 0 - layer = 5 - w_class = WEIGHT_CLASS_HUGE - force = 10 - force_wielded = 10 - throwforce = 13 - throw_speed = 2 - throw_range = 4 - -/obj/item/twohanded/required/kirbyplants/New() - ..() - icon_state = "plant-[rand(1,35)]" - if(prob(1)) - icon_state = "plant-36" - -/obj/item/twohanded/required/kirbyplants/equipped(mob/living/user) - . = ..() - var/image/I = image(icon = 'icons/obj/flora/plants.dmi' , icon_state = src.icon_state, loc = user) - I.override = 1 - user.add_alt_appearance("sneaking_mission", I, GLOB.player_list) - -/obj/item/twohanded/required/kirbyplants/dropped(mob/living/user) - ..() - user.remove_alt_appearance("sneaking_mission") - -/obj/item/twohanded/required/kirbyplants/dead - name = "\improper RD's potted plant" - desc = "A gift from the botanical staff, presented after the RD's reassignment. There's a tag on it that says \"Y'all come back now, y'hear?\"\nIt doesn't look very healthy..." - icon_state = "plant-dead" - -//a rock is flora according to where the icon file is -//and now these defines -/obj/structure/flora/rock - name = "rock" - desc = "a rock" - icon_state = "rock1" - icon = 'icons/obj/flora/rocks.dmi' - resistance_flags = FIRE_PROOF - anchored = 1 - -/obj/structure/flora/rock/New() - ..() - icon_state = "rock[rand(1,5)]" - -/obj/structure/flora/rock/pile - name = "rocks" - desc = "some rocks" - icon_state = "rockpile1" - -/obj/structure/flora/rock/pile/New() - ..() - icon_state = "rockpile[rand(1,5)]" - -/obj/structure/flora/rock/icy - name = "icy rock" - color = "#cce9eb" - -/obj/structure/flora/rock/pile/icy - name = "icy rocks" - color = "#cce9eb" - -/obj/structure/flora/corn_stalk - name = "corn stalk" - icon = 'icons/obj/flora/plants.dmi' - icon_state = "cornstalk1" - anchored = 0 - layer = 5 - -/obj/structure/flora/corn_stalk/alt_1 - icon_state = "cornstalk2" - -/obj/structure/flora/corn_stalk/alt_2 - icon_state = "cornstalk3" - -/obj/structure/flora/straw_bail - name = "straw bail" - icon = 'icons/obj/flora/plants.dmi' - icon_state = "strawbail1" - density = 1 - climbable = 1 // you can climb all over them. - -/obj/structure/flora/straw_bail/alt_1 - icon_state = "strawbail2" - -/obj/structure/flora/straw_bail/alt_2 - icon_state = "strawbail3" - -/obj/structure/bush - name = "foliage" - desc = "Pretty thick scrub, it'll take something sharp and a lot of determination to clear away." - icon = 'icons/obj/flora/plants.dmi' - icon_state = "bush1" - density = 1 - anchored = 1 - layer = 3.2 - var/indestructable = 0 - var/stump = 0 - -/obj/structure/bush/New() - if(prob(20)) - opacity = 1 - -/* -/obj/structure/bush/Bumped(M as mob) - if(istype(M, /mob/living/simple_animal)) - var/mob/living/simple_animal/A = M - A.loc = get_turf(src) - else if(istype(M, /mob/living/carbon/monkey)) - var/mob/living/carbon/monkey/A = M - A.loc = get_turf(src) -*/ - -/obj/structure/bush/attackby(var/obj/I as obj, var/mob/user as mob, params) - //hatchets can clear away undergrowth - if(istype(I, /obj/item/hatchet) && !stump) - if(indestructable) - //this bush marks the edge of the map, you can't destroy it - to_chat(user, "You flail away at the undergrowth, but it's too thick here.") - else - user.visible_message("[user] begins clearing away [src].
    ","You begin clearing away [src].
    ") - spawn(rand(15,30)) - if(get_dist(user,src) < 2) - to_chat(user, "You clear away [src].") - var/obj/item/stack/sheet/wood/W = new(src.loc) - W.amount = rand(3,15) - if(prob(50)) - icon_state = "stump[rand(1,2)]" - name = "cleared foliage" - desc = "There used to be dense undergrowth here." - density = 0 - stump = 1 - pixel_x = rand(-6,6) - pixel_y = rand(-6,6) - else - qdel(src) - else - return ..() +/obj/structure/flora + resistance_flags = FLAMMABLE + max_integrity = 150 + +//trees +/obj/structure/flora/tree + name = "tree" + anchored = 1 + density = 1 + pixel_x = -16 + layer = 9 + +/obj/structure/flora/tree/pine + name = "pine tree" + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_1" + +/obj/structure/flora/tree/pine/New() + ..() + icon_state = "pine_[rand(1, 3)]" + +/obj/structure/flora/tree/pine/xmas + name = "xmas tree" + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_c" + +/obj/structure/flora/tree/pine/xmas/New() + ..() + icon_state = "pine_c" + +/obj/structure/flora/tree/dead + icon = 'icons/obj/flora/deadtrees.dmi' + icon_state = "tree_1" + +/obj/structure/flora/tree/dead/New() + ..() + icon_state = "tree_[rand(1, 6)]" + +/obj/structure/flora/tree/palm + icon = 'icons/misc/beach2.dmi' + icon_state = "palm1" + +/obj/structure/flora/tree/palm/New() + ..() + icon_state = pick("palm1","palm2") + pixel_x = 0 + +//grass +/obj/structure/flora/grass + name = "grass" + icon = 'icons/obj/flora/snowflora.dmi' + anchored = 1 + +/obj/structure/flora/grass/brown + icon_state = "snowgrass1bb" + +/obj/structure/flora/grass/brown/New() + ..() + icon_state = "snowgrass[rand(1, 3)]bb" + + +/obj/structure/flora/grass/green + icon_state = "snowgrass1gb" + +/obj/structure/flora/grass/green/New() + ..() + icon_state = "snowgrass[rand(1, 3)]gb" + +/obj/structure/flora/grass/both + icon_state = "snowgrassall1" + +/obj/structure/flora/grass/both/New() + ..() + icon_state = "snowgrassall[rand(1, 3)]" + + +//bushes +/obj/structure/flora/bush + name = "bush" + icon = 'icons/obj/flora/snowflora.dmi' + icon_state = "snowbush1" + anchored = 1 + +/obj/structure/flora/bush/New() + ..() + icon_state = "snowbush[rand(1, 6)]" + +//newbushes + +/obj/structure/flora/ausbushes + name = "bush" + icon = 'icons/obj/flora/ausflora.dmi' + icon_state = "firstbush_1" + anchored = 1 + +/obj/structure/flora/ausbushes/New() + ..() + icon_state = "firstbush_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/reedbush + icon_state = "reedbush_1" + +/obj/structure/flora/ausbushes/reedbush/New() + ..() + icon_state = "reedbush_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/leafybush + icon_state = "leafybush_1" + +/obj/structure/flora/ausbushes/leafybush/New() + ..() + icon_state = "leafybush_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/palebush + icon_state = "palebush_1" + +/obj/structure/flora/ausbushes/palebush/New() + ..() + icon_state = "palebush_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/stalkybush + icon_state = "stalkybush_1" + +/obj/structure/flora/ausbushes/stalkybush/New() + ..() + icon_state = "stalkybush_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/grassybush + icon_state = "grassybush_1" + +/obj/structure/flora/ausbushes/grassybush/New() + ..() + icon_state = "grassybush_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/fernybush + icon_state = "fernybush_1" + +/obj/structure/flora/ausbushes/fernybush/New() + ..() + icon_state = "fernybush_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/sunnybush + icon_state = "sunnybush_1" + +/obj/structure/flora/ausbushes/sunnybush/New() + ..() + icon_state = "sunnybush_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/genericbush + icon_state = "genericbush_1" + +/obj/structure/flora/ausbushes/genericbush/New() + ..() + icon_state = "genericbush_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/pointybush + icon_state = "pointybush_1" + +/obj/structure/flora/ausbushes/pointybush/New() + ..() + icon_state = "pointybush_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/lavendergrass + icon_state = "lavendergrass_1" + +/obj/structure/flora/ausbushes/lavendergrass/New() + ..() + icon_state = "lavendergrass_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/ywflowers + icon_state = "ywflowers_1" + +/obj/structure/flora/ausbushes/ywflowers/New() + ..() + icon_state = "ywflowers_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/brflowers + icon_state = "brflowers_1" + +/obj/structure/flora/ausbushes/brflowers/New() + ..() + icon_state = "brflowers_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/ppflowers + icon_state = "ppflowers_1" + +/obj/structure/flora/ausbushes/ppflowers/New() + ..() + icon_state = "ppflowers_[rand(1, 4)]" + +/obj/structure/flora/ausbushes/sparsegrass + icon_state = "sparsegrass_1" + +/obj/structure/flora/ausbushes/sparsegrass/New() + ..() + icon_state = "sparsegrass_[rand(1, 3)]" + +/obj/structure/flora/ausbushes/fullgrass + icon_state = "fullgrass_1" + +/obj/structure/flora/ausbushes/fullgrass/New() + ..() + icon_state = "fullgrass_[rand(1, 3)]" + + +/obj/item/twohanded/required/kirbyplants + name = "potted plant" + icon = 'icons/obj/flora/plants.dmi' + icon_state = "plant-1" + anchored = 0 + layer = 5 + w_class = WEIGHT_CLASS_HUGE + force = 10 + force_wielded = 10 + throwforce = 13 + throw_speed = 2 + throw_range = 4 + +/obj/item/twohanded/required/kirbyplants/New() + ..() + icon_state = "plant-[rand(1,35)]" + if(prob(1)) + icon_state = "plant-36" + +/obj/item/twohanded/required/kirbyplants/equipped(mob/living/user) + . = ..() + var/image/I = image(icon = 'icons/obj/flora/plants.dmi' , icon_state = src.icon_state, loc = user) + I.override = 1 + user.add_alt_appearance("sneaking_mission", I, GLOB.player_list) + +/obj/item/twohanded/required/kirbyplants/dropped(mob/living/user) + ..() + user.remove_alt_appearance("sneaking_mission") + +/obj/item/twohanded/required/kirbyplants/dead + name = "\improper RD's potted plant" + desc = "A gift from the botanical staff, presented after the RD's reassignment. There's a tag on it that says \"Y'all come back now, y'hear?\"\nIt doesn't look very healthy..." + icon_state = "plant-dead" + +//a rock is flora according to where the icon file is +//and now these defines +/obj/structure/flora/rock + name = "rock" + desc = "a rock" + icon_state = "rock1" + icon = 'icons/obj/flora/rocks.dmi' + resistance_flags = FIRE_PROOF + anchored = 1 + +/obj/structure/flora/rock/New() + ..() + icon_state = "rock[rand(1,5)]" + +/obj/structure/flora/rock/pile + name = "rocks" + desc = "some rocks" + icon_state = "rockpile1" + +/obj/structure/flora/rock/pile/New() + ..() + icon_state = "rockpile[rand(1,5)]" + +/obj/structure/flora/rock/icy + name = "icy rock" + color = "#cce9eb" + +/obj/structure/flora/rock/pile/icy + name = "icy rocks" + color = "#cce9eb" + +/obj/structure/flora/corn_stalk + name = "corn stalk" + icon = 'icons/obj/flora/plants.dmi' + icon_state = "cornstalk1" + anchored = 0 + layer = 5 + +/obj/structure/flora/corn_stalk/alt_1 + icon_state = "cornstalk2" + +/obj/structure/flora/corn_stalk/alt_2 + icon_state = "cornstalk3" + +/obj/structure/flora/straw_bail + name = "straw bail" + icon = 'icons/obj/flora/plants.dmi' + icon_state = "strawbail1" + density = 1 + climbable = 1 // you can climb all over them. + +/obj/structure/flora/straw_bail/alt_1 + icon_state = "strawbail2" + +/obj/structure/flora/straw_bail/alt_2 + icon_state = "strawbail3" + +/obj/structure/bush + name = "foliage" + desc = "Pretty thick scrub, it'll take something sharp and a lot of determination to clear away." + icon = 'icons/obj/flora/plants.dmi' + icon_state = "bush1" + density = 1 + anchored = 1 + layer = 3.2 + var/indestructable = 0 + var/stump = 0 + +/obj/structure/bush/New() + if(prob(20)) + opacity = 1 + +/* +/obj/structure/bush/Bumped(M as mob) + if(istype(M, /mob/living/simple_animal)) + var/mob/living/simple_animal/A = M + A.loc = get_turf(src) + else if(istype(M, /mob/living/carbon/monkey)) + var/mob/living/carbon/monkey/A = M + A.loc = get_turf(src) +*/ + +/obj/structure/bush/attackby(var/obj/I as obj, var/mob/user as mob, params) + //hatchets can clear away undergrowth + if(istype(I, /obj/item/hatchet) && !stump) + if(indestructable) + //this bush marks the edge of the map, you can't destroy it + to_chat(user, "You flail away at the undergrowth, but it's too thick here.") + else + user.visible_message("[user] begins clearing away [src].","You begin clearing away [src].
    ") + spawn(rand(15,30)) + if(get_dist(user,src) < 2) + to_chat(user, "You clear away [src].") + var/obj/item/stack/sheet/wood/W = new(src.loc) + W.amount = rand(3,15) + if(prob(50)) + icon_state = "stump[rand(1,2)]" + name = "cleared foliage" + desc = "There used to be dense undergrowth here." + density = 0 + stump = 1 + pixel_x = rand(-6,6) + pixel_y = rand(-6,6) + else + qdel(src) + else + return ..() diff --git a/code/game/objects/structures/fluff.dm b/code/game/objects/structures/fluff.dm index c81b350f7457..4f09b36fe81e 100644 --- a/code/game/objects/structures/fluff.dm +++ b/code/game/objects/structures/fluff.dm @@ -78,4 +78,4 @@ /obj/structure/fluff/divine/conduit name = "conduit" desc = "It allows a deity to extend their reach. Their powers are just as potent near a conduit as a nexus." - icon_state = "conduit" \ No newline at end of file + icon_state = "conduit" diff --git a/code/game/objects/structures/foodcart.dm b/code/game/objects/structures/foodcart.dm index da88c16f0744..198dbf5f1950 100644 --- a/code/game/objects/structures/foodcart.dm +++ b/code/game/objects/structures/foodcart.dm @@ -176,4 +176,4 @@ /obj/structure/foodcart/deconstruct(disassembled = TRUE) if(!(flags & NODECONSTRUCT)) new /obj/item/stack/sheet/metal(loc, 4) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 6b65d3028d95..dcb74369d722 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -1,471 +1,471 @@ -/obj/structure/girder - name = "girder" - icon_state = "girder" - anchored = 1 - density = 1 - layer = BELOW_OBJ_LAYER - var/state = GIRDER_NORMAL - var/girderpasschance = 20 // percentage chance that a projectile passes through the girder. - max_integrity = 200 - var/can_displace = TRUE //If the girder can be moved around by crowbarring it - var/metalUsed = 2 //used to determine amount returned in deconstruction - var/metal_type = /obj/item/stack/sheet/metal - -/obj/structure/girder/examine(mob/user) - . = ..() - switch(state) - if(GIRDER_REINF) - . += "The support struts are screwed in place." - if(GIRDER_REINF_STRUTS) - . += "The support struts are unscrewed and the inner grille is intact." - if(GIRDER_NORMAL) - if(can_displace) - . += "The bolts are lodged in place." - if(GIRDER_DISPLACED) - . += "The bolts are loosened, but the screws are holding [src] together." - if(GIRDER_DISASSEMBLED) - . += "[src] is disassembled! You probably shouldn't be able to see this examine message." - -/obj/structure/girder/proc/refundMetal(metalAmount) //refunds metal used in construction when deconstructed - for(var/i=0;i < metalAmount;i++) - new metal_type(get_turf(src)) - -/obj/structure/girder/temperature_expose(datum/gas_mixture/air, exposed_temperature) - ..() - var/temp_check = exposed_temperature - if(temp_check >= GIRDER_MELTING_TEMP) - take_damage(10) - -/obj/structure/girder/attackby(obj/item/W, mob/user, params) - add_fingerprint(user) - if(istype(W, /obj/item/gun/energy/plasmacutter)) - to_chat(user, "You start slicing apart the girder...") - if(do_after(user, 40 * W.toolspeed, target = src)) - if(!src) - return - playsound(loc, W.usesound, 100, 1) - to_chat(user, "You slice apart the girder.") - refundMetal(metalUsed) - qdel(src) - - else if(istype(W, /obj/item/pickaxe/drill/diamonddrill)) - to_chat(user, "You drill through the girder!") - refundMetal(metalUsed) - qdel(src) - - else if(istype(W, /obj/item/pickaxe/drill/jackhammer)) - playsound(loc, W.usesound, 100, 1) - to_chat(user, "You disintegrate the girder!") - refundMetal(metalUsed) - qdel(src) - - else if(istype(W, /obj/item/stack)) - if(iswallturf(loc)) - to_chat(user, "There is already a wall present!") - return - if(!isfloorturf(loc)) - to_chat(user, "A floor must be present to build a false wall!") - return - if (locate(/obj/structure/falsewall) in loc.contents) - to_chat(user, "There is already a false wall present!") - return - if(istype(W, /obj/item/stack/sheet/runed_metal)) - to_chat(user, "You can't seem to make the metal bend..") - return - - if(istype(W,/obj/item/stack/rods)) - var/obj/item/stack/rods/S = W - if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need at least two rods to create a false wall!") - return - to_chat(user, "You start building a reinforced false wall...") - if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You create a false wall. Push on it to open or close the passage.") - var/obj/structure/falsewall/iron/FW = new (loc) - transfer_fingerprints_to(FW) - qdel(src) - else - if(S.get_amount() < 5) - to_chat(user, "You need at least five rods to add plating!") - return - to_chat(user, "You start adding plating...") - if (do_after(user, 40, target = src)) - if(!loc || !S || S.get_amount() < 5) - return - S.use(5) - to_chat(user, "You add the plating.") - var/turf/T = get_turf(src) - T.ChangeTurf(/turf/simulated/wall/mineral/iron) - transfer_fingerprints_to(T) - qdel(src) - return - - if(!istype(W,/obj/item/stack/sheet)) - return - - var/obj/item/stack/sheet/S = W - if(!S.wall_allowed) - to_chat(user, "You don't think that is good material for a wall!") - return - - if(istype(S, /obj/item/stack/sheet/wood)) - if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need two planks of wood to create a false wall!") - return - to_chat(user, "You start building a false wall...") - if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You create a false wall. Push on it to open or close the passage.") - var/obj/structure/falsewall/wood/falsewood = new(loc) - transfer_fingerprints_to(falsewood) - qdel(src) - else - if(S.get_amount() < 2) - to_chat(user, "You need two planks of wood to finish a wall!") - return - to_chat(user, "You start adding plating...") - if(do_after(user, 40 * W.toolspeed, target = src)) - if(!src || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You add the plating.") - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(/turf/simulated/wall/mineral/wood) - for(var/turf/simulated/wall/mineral/wood/X in Tsrc.loc) - if(X) - X.add_hiddenprint(usr) - qdel(src) - return - - else if(istype(S, /obj/item/stack/sheet/metal)) - if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need two sheets of metal to create a false wall!") - return - to_chat(user, "You start building a false wall...") - if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You create a false wall. Push on it to open or close the passage.") - var/obj/structure/falsewall/F = new(loc) - transfer_fingerprints_to(F) - qdel(src) - else - if(S.get_amount() < 2) - to_chat(user, "You need two sheets of metal to finish a wall!") - return - to_chat(user, "You start adding plating...") - if(do_after(user, 40 * W.toolspeed, target = src)) - if(!src || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You add the plating.") - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(/turf/simulated/wall) - for(var/turf/simulated/wall/X in Tsrc.loc) - if(X) - X.add_hiddenprint(usr) - qdel(src) - return - - if(istype(S, /obj/item/stack/sheet/plasteel)) - if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need at least two sheets to create a false wall!") - return - to_chat(user, "You start building a reinforced false wall...") - if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You create a reinforced false wall. Push on it to open or close the passage.") - var/obj/structure/falsewall/reinforced/FW = new (loc) - transfer_fingerprints_to(FW) - qdel(src) - else - if(state == GIRDER_REINF) - if(S.get_amount() < 1) - return - to_chat(user, "You start finalizing the reinforced wall...") - if(do_after(user, 50, target = src)) - if(!src || !S || S.get_amount() < 1) - return - S.use(1) - to_chat(user, "You fully reinforce the wall.") - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(/turf/simulated/wall/r_wall) - for(var/turf/simulated/wall/r_wall/X in Tsrc.loc) - if(X) - X.add_hiddenprint(usr) - qdel(src) - return - else - if(S.get_amount() < 1) - return - to_chat(user, "You start reinforcing the girder...") - if(do_after(user,60, target = src)) - if(!src || !S || S.get_amount() < 1) - return - S.use(1) - to_chat(user, "You reinforce the girder.") - var/obj/structure/girder/reinforced/R = new (loc) - transfer_fingerprints_to(R) - qdel(src) - return - - if(S.sheettype) - var/M = S.sheettype - if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need at least two sheets to create a false wall!") - return - if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You create a false wall. Push on it to open or close the passage.") - var/F = text2path("/obj/structure/falsewall/[M]") - var/obj/structure/FW = new F (loc) - transfer_fingerprints_to(FW) - qdel(src) - else - if(S.get_amount() < 2) - to_chat(user, "You need at least two sheets to add plating!") - return - to_chat(user, "You start adding plating...") - if(do_after(user,40, target = src)) - if(!src || !S || S.get_amount() < 2) - return - S.use(2) - to_chat(user, "You add the plating.") - var/turf/Tsrc = get_turf(src) - Tsrc.ChangeTurf(text2path("/turf/simulated/wall/mineral/[M]")) - for(var/turf/simulated/wall/mineral/X in Tsrc.loc) - if(X) - X.add_hiddenprint(usr) - qdel(src) - return - - add_hiddenprint(user) - - else if(istype(W, /obj/item/pipe)) - var/obj/item/pipe/P = W - if(P.pipe_type in list(0, 1, 5)) //simple pipes, simple bends, and simple manifolds. - if(!user.drop_item()) - return - P.loc = src.loc - to_chat(user, "You fit the pipe into \the [src].") - else - return ..() - -/obj/structure/girder/crowbar_act(mob/user, obj/item/I) - if(!can_displace || state != GIRDER_NORMAL) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - to_chat(user, "You start dislodging the girder...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_NORMAL) - return - to_chat(user, "You dislodge the girder.") - var/obj/structure/girder/displaced/D = new (loc) - transfer_fingerprints_to(D) - qdel(src) - -/obj/structure/girder/screwdriver_act(mob/user, obj/item/I) - if(state != GIRDER_DISPLACED && state != GIRDER_REINF && state != GIRDER_REINF_STRUTS) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - switch(state) - if(GIRDER_DISPLACED) - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_DISPLACED) - return - state = GIRDER_DISASSEMBLED - TOOL_DISMANTLE_SUCCESS_MESSAGE - var/obj/item/stack/sheet/metal/M = new(loc, 2) - M.add_fingerprint(user) - qdel(src) - if(GIRDER_REINF) - to_chat(user, "You start unsecuring support struts...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_REINF) - return - to_chat(user, "You unsecure the support struts.") - state = GIRDER_REINF_STRUTS - if(GIRDER_REINF_STRUTS) - to_chat(user, "You start securing support struts...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_REINF_STRUTS) - return - to_chat(user, "You secure the support struts.") - state = GIRDER_REINF - -/obj/structure/girder/wirecutter_act(mob/user, obj/item/I) - if(state != GIRDER_REINF_STRUTS) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - to_chat(user, "You start removing the inner grille...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_REINF_STRUTS) - return - to_chat(user, "You remove the inner grille.") - new /obj/item/stack/sheet/plasteel(get_turf(src)) - var/obj/structure/girder/G = new (loc) - transfer_fingerprints_to(G) - qdel(src) - -/obj/structure/girder/wrench_act(mob/user, obj/item/I) - if(state != GIRDER_NORMAL && state != GIRDER_DISPLACED) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(state == GIRDER_NORMAL) - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_NORMAL) - return - state = GIRDER_DISASSEMBLED - TOOL_DISMANTLE_SUCCESS_MESSAGE - var/obj/item/stack/sheet/metal/M = new(loc, 2) - M.add_fingerprint(user) - qdel(src) - else - if(!isfloorturf(loc)) - to_chat(user, "A floor must be present to secure the girder!") - return - to_chat(user, "You start securing the girder...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_DISPLACED) - return - to_chat(user, "You secure the girder.") - var/obj/structure/girder/G = new(loc) - transfer_fingerprints_to(G) - qdel(src) - -/obj/structure/girder/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - WELDER_SLICING_SUCCESS_MESSAGE - refundMetal(metalUsed) - qdel(src) - -/obj/structure/girder/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover) && mover.checkpass(PASSGRILLE)) - return prob(girderpasschance) - else - if(istype(mover, /obj/item/projectile)) - return prob(girderpasschance) - else - return 0 - -/obj/structure/girder/CanAStarPass(ID, dir, caller) - . = !density - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSGRILLE) - -/obj/structure/girder/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/remains = pick(/obj/item/stack/rods, /obj/item/stack/sheet/metal) - new remains(loc) - qdel(src) - -/obj/structure/girder/narsie_act() - if(prob(25)) - new /obj/structure/girder/cult(loc) - qdel(src) - -/obj/structure/girder/displaced - name = "displaced girder" - icon_state = "displaced" - anchored = 0 - state = GIRDER_DISPLACED - girderpasschance = 25 - max_integrity = 120 - -/obj/structure/girder/reinforced - name = "reinforced girder" - icon_state = "reinforced" - state = GIRDER_REINF - girderpasschance = 0 - max_integrity = 350 - -/obj/structure/girder/cult - name = "runed girder" - desc = "Framework made of a strange and shockingly cold metal. It doesn't seem to have any bolts." - icon = 'icons/obj/cult.dmi' - icon_state = "cultgirder" - can_displace = FALSE - metalUsed = 1 - metal_type = /obj/item/stack/sheet/runed_metal - -/obj/structure/girder/cult/New() - . = ..() - icon_state = SSticker.cultdat?.cult_girder_icon_state - -/obj/structure/girder/cult/refundMetal(metalAmount) - for(var/i=0;i < metalAmount;i++) - new /obj/item/stack/sheet/runed_metal(get_turf(src)) - -/obj/structure/girder/cult/attackby(obj/item/W, mob/user, params) - add_fingerprint(user) - if(istype(W, /obj/item/tome) && iscultist(user)) //Cultists can demolish cult girders instantly with their tomes - user.visible_message("[user] strikes [src] with [W]!", "You demolish [src].") - refundMetal(metalUsed) - qdel(src) - else if(istype(W, /obj/item/gun/energy/plasmacutter)) - to_chat(user, "You start slicing apart the girder...") - if(do_after(user, 40* W.toolspeed, target = src)) - playsound(loc, W.usesound, 100, 1) - to_chat(user, "You slice apart the girder.") - var/obj/item/stack/sheet/runed_metal/R = new(get_turf(src)) - R.amount = 1 - transfer_fingerprints_to(R) - qdel(src) - else if(istype(W, /obj/item/pickaxe/drill/jackhammer)) - var/obj/item/pickaxe/drill/jackhammer/D = W - to_chat(user, "Your jackhammer smashes through the girder!") - var/obj/item/stack/sheet/runed_metal/R = new(get_turf(src)) - R.amount = 1 - transfer_fingerprints_to(R) - D.playDigSound() - qdel(src) - - else if(istype(W, /obj/item/stack/sheet/runed_metal)) - var/obj/item/stack/sheet/runed_metal/R = W - if(R.get_amount() < 1) - to_chat(user, "You need at least one sheet of runed metal to construct a runed wall!") - return 0 - user.visible_message("[user] begins laying runed metal on [src]...", "You begin constructing a runed wall...") - if(do_after(user, 50, target = src)) - if(R.get_amount() < 1 || !R) - return - user.visible_message("[user] plates [src] with runed metal.", "You construct a runed wall.") - R.use(1) - var/turf/T = get_turf(src) - T.ChangeTurf(/turf/simulated/wall/cult) - qdel(src) - else - return ..() - -/obj/structure/girder/cult/narsie_act() - return - -/obj/structure/girder/cult/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/runed_metal(drop_location(), 1) - qdel(src) +/obj/structure/girder + name = "girder" + icon_state = "girder" + anchored = 1 + density = 1 + layer = BELOW_OBJ_LAYER + var/state = GIRDER_NORMAL + var/girderpasschance = 20 // percentage chance that a projectile passes through the girder. + max_integrity = 200 + var/can_displace = TRUE //If the girder can be moved around by crowbarring it + var/metalUsed = 2 //used to determine amount returned in deconstruction + var/metal_type = /obj/item/stack/sheet/metal + +/obj/structure/girder/examine(mob/user) + . = ..() + switch(state) + if(GIRDER_REINF) + . += "The support struts are screwed in place." + if(GIRDER_REINF_STRUTS) + . += "The support struts are unscrewed and the inner grille is intact." + if(GIRDER_NORMAL) + if(can_displace) + . += "The bolts are lodged in place." + if(GIRDER_DISPLACED) + . += "The bolts are loosened, but the screws are holding [src] together." + if(GIRDER_DISASSEMBLED) + . += "[src] is disassembled! You probably shouldn't be able to see this examine message." + +/obj/structure/girder/proc/refundMetal(metalAmount) //refunds metal used in construction when deconstructed + for(var/i=0;i < metalAmount;i++) + new metal_type(get_turf(src)) + +/obj/structure/girder/temperature_expose(datum/gas_mixture/air, exposed_temperature) + ..() + var/temp_check = exposed_temperature + if(temp_check >= GIRDER_MELTING_TEMP) + take_damage(10) + +/obj/structure/girder/attackby(obj/item/W, mob/user, params) + add_fingerprint(user) + if(istype(W, /obj/item/gun/energy/plasmacutter)) + to_chat(user, "You start slicing apart the girder...") + if(do_after(user, 40 * W.toolspeed, target = src)) + if(!src) + return + playsound(loc, W.usesound, 100, 1) + to_chat(user, "You slice apart the girder.") + refundMetal(metalUsed) + qdel(src) + + else if(istype(W, /obj/item/pickaxe/drill/diamonddrill)) + to_chat(user, "You drill through the girder!") + refundMetal(metalUsed) + qdel(src) + + else if(istype(W, /obj/item/pickaxe/drill/jackhammer)) + playsound(loc, W.usesound, 100, 1) + to_chat(user, "You disintegrate the girder!") + refundMetal(metalUsed) + qdel(src) + + else if(istype(W, /obj/item/stack)) + if(iswallturf(loc)) + to_chat(user, "There is already a wall present!") + return + if(!isfloorturf(loc)) + to_chat(user, "A floor must be present to build a false wall!") + return + if (locate(/obj/structure/falsewall) in loc.contents) + to_chat(user, "There is already a false wall present!") + return + if(istype(W, /obj/item/stack/sheet/runed_metal)) + to_chat(user, "You can't seem to make the metal bend..") + return + + if(istype(W,/obj/item/stack/rods)) + var/obj/item/stack/rods/S = W + if(state == GIRDER_DISPLACED) + if(S.get_amount() < 2) + to_chat(user, "You need at least two rods to create a false wall!") + return + to_chat(user, "You start building a reinforced false wall...") + if(do_after(user, 20, target = src)) + if(!loc || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You create a false wall. Push on it to open or close the passage.") + var/obj/structure/falsewall/iron/FW = new (loc) + transfer_fingerprints_to(FW) + qdel(src) + else + if(S.get_amount() < 5) + to_chat(user, "You need at least five rods to add plating!") + return + to_chat(user, "You start adding plating...") + if (do_after(user, 40, target = src)) + if(!loc || !S || S.get_amount() < 5) + return + S.use(5) + to_chat(user, "You add the plating.") + var/turf/T = get_turf(src) + T.ChangeTurf(/turf/simulated/wall/mineral/iron) + transfer_fingerprints_to(T) + qdel(src) + return + + if(!istype(W,/obj/item/stack/sheet)) + return + + var/obj/item/stack/sheet/S = W + if(!S.wall_allowed) + to_chat(user, "You don't think that is good material for a wall!") + return + + if(istype(S, /obj/item/stack/sheet/wood)) + if(state == GIRDER_DISPLACED) + if(S.get_amount() < 2) + to_chat(user, "You need two planks of wood to create a false wall!") + return + to_chat(user, "You start building a false wall...") + if(do_after(user, 20, target = src)) + if(!loc || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You create a false wall. Push on it to open or close the passage.") + var/obj/structure/falsewall/wood/falsewood = new(loc) + transfer_fingerprints_to(falsewood) + qdel(src) + else + if(S.get_amount() < 2) + to_chat(user, "You need two planks of wood to finish a wall!") + return + to_chat(user, "You start adding plating...") + if(do_after(user, 40 * W.toolspeed, target = src)) + if(!src || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You add the plating.") + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(/turf/simulated/wall/mineral/wood) + for(var/turf/simulated/wall/mineral/wood/X in Tsrc.loc) + if(X) + X.add_hiddenprint(usr) + qdel(src) + return + + else if(istype(S, /obj/item/stack/sheet/metal)) + if(state == GIRDER_DISPLACED) + if(S.get_amount() < 2) + to_chat(user, "You need two sheets of metal to create a false wall!") + return + to_chat(user, "You start building a false wall...") + if(do_after(user, 20, target = src)) + if(!loc || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You create a false wall. Push on it to open or close the passage.") + var/obj/structure/falsewall/F = new(loc) + transfer_fingerprints_to(F) + qdel(src) + else + if(S.get_amount() < 2) + to_chat(user, "You need two sheets of metal to finish a wall!") + return + to_chat(user, "You start adding plating...") + if(do_after(user, 40 * W.toolspeed, target = src)) + if(!src || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You add the plating.") + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(/turf/simulated/wall) + for(var/turf/simulated/wall/X in Tsrc.loc) + if(X) + X.add_hiddenprint(usr) + qdel(src) + return + + if(istype(S, /obj/item/stack/sheet/plasteel)) + if(state == GIRDER_DISPLACED) + if(S.get_amount() < 2) + to_chat(user, "You need at least two sheets to create a false wall!") + return + to_chat(user, "You start building a reinforced false wall...") + if(do_after(user, 20, target = src)) + if(!loc || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You create a reinforced false wall. Push on it to open or close the passage.") + var/obj/structure/falsewall/reinforced/FW = new (loc) + transfer_fingerprints_to(FW) + qdel(src) + else + if(state == GIRDER_REINF) + if(S.get_amount() < 1) + return + to_chat(user, "You start finalizing the reinforced wall...") + if(do_after(user, 50, target = src)) + if(!src || !S || S.get_amount() < 1) + return + S.use(1) + to_chat(user, "You fully reinforce the wall.") + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(/turf/simulated/wall/r_wall) + for(var/turf/simulated/wall/r_wall/X in Tsrc.loc) + if(X) + X.add_hiddenprint(usr) + qdel(src) + return + else + if(S.get_amount() < 1) + return + to_chat(user, "You start reinforcing the girder...") + if(do_after(user,60, target = src)) + if(!src || !S || S.get_amount() < 1) + return + S.use(1) + to_chat(user, "You reinforce the girder.") + var/obj/structure/girder/reinforced/R = new (loc) + transfer_fingerprints_to(R) + qdel(src) + return + + if(S.sheettype) + var/M = S.sheettype + if(state == GIRDER_DISPLACED) + if(S.get_amount() < 2) + to_chat(user, "You need at least two sheets to create a false wall!") + return + if(do_after(user, 20, target = src)) + if(!loc || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You create a false wall. Push on it to open or close the passage.") + var/F = text2path("/obj/structure/falsewall/[M]") + var/obj/structure/FW = new F (loc) + transfer_fingerprints_to(FW) + qdel(src) + else + if(S.get_amount() < 2) + to_chat(user, "You need at least two sheets to add plating!") + return + to_chat(user, "You start adding plating...") + if(do_after(user,40, target = src)) + if(!src || !S || S.get_amount() < 2) + return + S.use(2) + to_chat(user, "You add the plating.") + var/turf/Tsrc = get_turf(src) + Tsrc.ChangeTurf(text2path("/turf/simulated/wall/mineral/[M]")) + for(var/turf/simulated/wall/mineral/X in Tsrc.loc) + if(X) + X.add_hiddenprint(usr) + qdel(src) + return + + add_hiddenprint(user) + + else if(istype(W, /obj/item/pipe)) + var/obj/item/pipe/P = W + if(P.pipe_type in list(0, 1, 5)) //simple pipes, simple bends, and simple manifolds. + if(!user.drop_item()) + return + P.loc = src.loc + to_chat(user, "You fit the pipe into \the [src].") + else + return ..() + +/obj/structure/girder/crowbar_act(mob/user, obj/item/I) + if(!can_displace || state != GIRDER_NORMAL) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + to_chat(user, "You start dislodging the girder...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_NORMAL) + return + to_chat(user, "You dislodge the girder.") + var/obj/structure/girder/displaced/D = new (loc) + transfer_fingerprints_to(D) + qdel(src) + +/obj/structure/girder/screwdriver_act(mob/user, obj/item/I) + if(state != GIRDER_DISPLACED && state != GIRDER_REINF && state != GIRDER_REINF_STRUTS) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + switch(state) + if(GIRDER_DISPLACED) + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_DISPLACED) + return + state = GIRDER_DISASSEMBLED + TOOL_DISMANTLE_SUCCESS_MESSAGE + var/obj/item/stack/sheet/metal/M = new(loc, 2) + M.add_fingerprint(user) + qdel(src) + if(GIRDER_REINF) + to_chat(user, "You start unsecuring support struts...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_REINF) + return + to_chat(user, "You unsecure the support struts.") + state = GIRDER_REINF_STRUTS + if(GIRDER_REINF_STRUTS) + to_chat(user, "You start securing support struts...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_REINF_STRUTS) + return + to_chat(user, "You secure the support struts.") + state = GIRDER_REINF + +/obj/structure/girder/wirecutter_act(mob/user, obj/item/I) + if(state != GIRDER_REINF_STRUTS) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + to_chat(user, "You start removing the inner grille...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_REINF_STRUTS) + return + to_chat(user, "You remove the inner grille.") + new /obj/item/stack/sheet/plasteel(get_turf(src)) + var/obj/structure/girder/G = new (loc) + transfer_fingerprints_to(G) + qdel(src) + +/obj/structure/girder/wrench_act(mob/user, obj/item/I) + if(state != GIRDER_NORMAL && state != GIRDER_DISPLACED) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(state == GIRDER_NORMAL) + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_NORMAL) + return + state = GIRDER_DISASSEMBLED + TOOL_DISMANTLE_SUCCESS_MESSAGE + var/obj/item/stack/sheet/metal/M = new(loc, 2) + M.add_fingerprint(user) + qdel(src) + else + if(!isfloorturf(loc)) + to_chat(user, "A floor must be present to secure the girder!") + return + to_chat(user, "You start securing the girder...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state != GIRDER_DISPLACED) + return + to_chat(user, "You secure the girder.") + var/obj/structure/girder/G = new(loc) + transfer_fingerprints_to(G) + qdel(src) + +/obj/structure/girder/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + WELDER_SLICING_SUCCESS_MESSAGE + refundMetal(metalUsed) + qdel(src) + +/obj/structure/girder/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSGRILLE)) + return prob(girderpasschance) + else + if(istype(mover, /obj/item/projectile)) + return prob(girderpasschance) + else + return 0 + +/obj/structure/girder/CanAStarPass(ID, dir, caller) + . = !density + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSGRILLE) + +/obj/structure/girder/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/remains = pick(/obj/item/stack/rods, /obj/item/stack/sheet/metal) + new remains(loc) + qdel(src) + +/obj/structure/girder/narsie_act() + if(prob(25)) + new /obj/structure/girder/cult(loc) + qdel(src) + +/obj/structure/girder/displaced + name = "displaced girder" + icon_state = "displaced" + anchored = 0 + state = GIRDER_DISPLACED + girderpasschance = 25 + max_integrity = 120 + +/obj/structure/girder/reinforced + name = "reinforced girder" + icon_state = "reinforced" + state = GIRDER_REINF + girderpasschance = 0 + max_integrity = 350 + +/obj/structure/girder/cult + name = "runed girder" + desc = "Framework made of a strange and shockingly cold metal. It doesn't seem to have any bolts." + icon = 'icons/obj/cult.dmi' + icon_state = "cultgirder" + can_displace = FALSE + metalUsed = 1 + metal_type = /obj/item/stack/sheet/runed_metal + +/obj/structure/girder/cult/New() + . = ..() + icon_state = SSticker.cultdat?.cult_girder_icon_state + +/obj/structure/girder/cult/refundMetal(metalAmount) + for(var/i=0;i < metalAmount;i++) + new /obj/item/stack/sheet/runed_metal(get_turf(src)) + +/obj/structure/girder/cult/attackby(obj/item/W, mob/user, params) + add_fingerprint(user) + if(istype(W, /obj/item/tome) && iscultist(user)) //Cultists can demolish cult girders instantly with their tomes + user.visible_message("[user] strikes [src] with [W]!", "You demolish [src].") + refundMetal(metalUsed) + qdel(src) + else if(istype(W, /obj/item/gun/energy/plasmacutter)) + to_chat(user, "You start slicing apart the girder...") + if(do_after(user, 40* W.toolspeed, target = src)) + playsound(loc, W.usesound, 100, 1) + to_chat(user, "You slice apart the girder.") + var/obj/item/stack/sheet/runed_metal/R = new(get_turf(src)) + R.amount = 1 + transfer_fingerprints_to(R) + qdel(src) + else if(istype(W, /obj/item/pickaxe/drill/jackhammer)) + var/obj/item/pickaxe/drill/jackhammer/D = W + to_chat(user, "Your jackhammer smashes through the girder!") + var/obj/item/stack/sheet/runed_metal/R = new(get_turf(src)) + R.amount = 1 + transfer_fingerprints_to(R) + D.playDigSound() + qdel(src) + + else if(istype(W, /obj/item/stack/sheet/runed_metal)) + var/obj/item/stack/sheet/runed_metal/R = W + if(R.get_amount() < 1) + to_chat(user, "You need at least one sheet of runed metal to construct a runed wall!") + return 0 + user.visible_message("[user] begins laying runed metal on [src]...", "You begin constructing a runed wall...") + if(do_after(user, 50, target = src)) + if(R.get_amount() < 1 || !R) + return + user.visible_message("[user] plates [src] with runed metal.", "You construct a runed wall.") + R.use(1) + var/turf/T = get_turf(src) + T.ChangeTurf(/turf/simulated/wall/cult) + qdel(src) + else + return ..() + +/obj/structure/girder/cult/narsie_act() + return + +/obj/structure/girder/cult/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/runed_metal(drop_location(), 1) + qdel(src) diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm index ae24a1a23e9b..755d2a3de1b0 100644 --- a/code/game/objects/structures/grille.dm +++ b/code/game/objects/structures/grille.dm @@ -1,318 +1,318 @@ -/obj/structure/grille - desc = "A flimsy framework of metal rods." - name = "grille" - icon = 'icons/obj/structures.dmi' - icon_state = "grille" - density = TRUE - anchored = TRUE - flags = CONDUCT - pressure_resistance = 5*ONE_ATMOSPHERE - layer = BELOW_OBJ_LAYER - level = 3 - armor = list("melee" = 50, "bullet" = 70, "laser" = 70, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0) - max_integrity = 50 - integrity_failure = 20 - var/rods_type = /obj/item/stack/rods - var/rods_amount = 2 - var/rods_broken = 1 - var/grille_type - var/broken_type = /obj/structure/grille/broken - var/shockcooldown = 0 - var/my_shockcooldown = 1 SECONDS - -/obj/structure/grille/fence/ - var/width = 3 - -/obj/structure/grille/fence/New() - if(width > 1) - if(dir in list(EAST, WEST)) - bound_width = width * world.icon_size - bound_height = world.icon_size - else - bound_width = world.icon_size - bound_height = width * world.icon_size - -/obj/structure/grille/fence/east_west - //width=80 - //height=42 - icon='icons/fence-ew.dmi' - -/obj/structure/grille/fence/north_south - //width=80 - //height=42 - icon='icons/fence-ns.dmi' - -/obj/structure/grille/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - update_icon() - -/obj/structure/grille/examine(mob/user) - . = ..() - if(anchored) - . += "It's secured in place with screws. The rods look like they could be cut through." - if(!anchored) - . += "The anchoring screws are unscrewed. The rods look like they could be cut through." - -/obj/structure/grille/ratvar_act() - if(broken) - new /obj/structure/grille/ratvar/broken(loc) - else - new /obj/structure/grille/ratvar(loc) - qdel(src) - -/obj/structure/grille/Bumped(atom/user) - if(ismob(user)) - if(!(shockcooldown <= world.time)) - return - shock(user, 70) - shockcooldown = world.time + my_shockcooldown - -/obj/structure/grille/attack_animal(mob/user) - . = ..() - if(. && !QDELETED(src) && !shock(user, 70)) - take_damage(rand(5,10), BRUTE, "melee", 1) - -/obj/structure/grille/hulk_damage() - return 60 - -/obj/structure/grille/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - if(user.a_intent == INTENT_HARM) - if(!shock(user, 70)) - ..(user, TRUE) - return TRUE - -/obj/structure/grille/attack_hand(mob/living/user) - . = ..() - if(.) - return - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_KICK) - user.visible_message("[user] hits [src].") - if(!shock(user, 70)) - take_damage(rand(5,10), BRUTE, "melee", 1) - -/obj/structure/grille/attack_alien(mob/living/user) - user.do_attack_animation(src) - user.changeNext_move(CLICK_CD_MELEE) - user.visible_message("[user] mangles [src].") - if(!shock(user, 70)) - take_damage(20, BRUTE, "melee", 1) - -/obj/structure/grille/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover) && mover.checkpass(PASSGRILLE)) - return 1 - else - if(istype(mover, /obj/item/projectile)) - return prob(30) - else - return !density - -/obj/structure/grille/CanAStarPass(ID, dir, caller) - . = !density - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSGRILLE) - -/obj/structure/grille/attackby(obj/item/W, mob/user, params) - user.changeNext_move(CLICK_CD_MELEE) - add_fingerprint(user) - if(istype(W, /obj/item/stack/rods) && broken) - var/obj/item/stack/rods/R = W - if(!shock(user, 90)) - user.visible_message("[user] rebuilds the broken grille.", \ - "You rebuild the broken grille.") - new grille_type(loc) - R.use(1) - qdel(src) - return - -//window placing begin - else if(is_glass_sheet(W)) - build_window(W, user) - return -//window placing end - - else if(istype(W, /obj/item/shard) || !shock(user, 70)) - return ..() - -/obj/structure/grille/wirecutter_act(mob/user, obj/item/I) - . = TRUE - if(shock(user, 100)) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - deconstruct() - -/obj/structure/grille/screwdriver_act(mob/user, obj/item/I) - if(!(istype(loc, /turf/simulated) || anchored)) - return - . = TRUE - if(shock(user, 90)) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - anchored = !anchored - user.visible_message("[user] [anchored ? "fastens" : "unfastens"] [src].", \ - "You [anchored ? "fasten [src] to" : "unfasten [src] from"] the floor.") - -/obj/structure/grille/proc/build_window(obj/item/stack/sheet/S, mob/user) - var/dir_to_set = NORTH - if(!istype(S) || !user) - return - if(broken) - to_chat(user, "You must repair or replace [src] first!") - return - if(S.get_amount() < 1) - to_chat(user, "You need at least one sheet of glass for that!") - return - if(!anchored) - to_chat(user, "[src] needs to be fastened to the floor first!") - return - if(!getRelativeDirection(src, user) && (user.loc != loc)) //essentially a cardinal direction adjacent or sharing same loc check - to_chat(user, "You can't reach.") - return - if(loc == user.loc) - dir_to_set = user.dir - else - if(x == user.x) - if(y > user.y) - dir_to_set = SOUTH - else - dir_to_set = NORTH - else if(y == user.y) - if(x > user.x) - dir_to_set = WEST - else - dir_to_set = EAST - for(var/obj/structure/window/WINDOW in loc) - if(WINDOW.dir == dir_to_set) - to_chat(user, "There is already a window facing this way there.") - return - to_chat(user, "You start placing the window...") - if(do_after(user, 20, target = src)) - if(!loc || !anchored) //Grille destroyed or unanchored while waiting - return - for(var/obj/structure/window/WINDOW in loc) - if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. - to_chat(user, "There is already a window facing this way there.") - return - var/obj/structure/window/W = new S.created_window(get_turf(src)) - S.use(1) - W.setDir(dir_to_set) - W.ini_dir = dir_to_set - W.anchored = FALSE - W.state = WINDOW_OUT_OF_FRAME - to_chat(user, "You place the [W] on [src].") - W.update_nearby_icons() - - -/obj/structure/grille/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src, 'sound/effects/grillehit.ogg', 80, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src, 'sound/items/welder.ogg', 80, TRUE) - -/obj/structure/grille/deconstruct(disassembled = TRUE) - if(!loc) //if already qdel'd somehow, we do nothing - return - if(!(flags & NODECONSTRUCT)) - var/obj/R = new rods_type(drop_location(), rods_amount) - transfer_fingerprints_to(R) - qdel(src) - ..() - -/obj/structure/grille/obj_break() - if(!broken && !(flags & NODECONSTRUCT)) - new broken_type(loc) - var/obj/R = new rods_type(drop_location(), rods_broken) - transfer_fingerprints_to(R) - qdel(src) - -// shock user with probability prb (if all connections & power are working) -// returns 1 if shocked, 0 otherwise - -/obj/structure/grille/proc/shock(mob/user, prb) - if(!anchored || broken) // unanchored/broken grilles are never connected - return FALSE - if(!prob(prb)) - return FALSE - if(!in_range(src, user))//To prevent TK and mech users from getting shocked - return FALSE - var/turf/T = get_turf(src) - var/obj/structure/cable/C = T.get_cable_node() - if(C) - if(electrocute_mob(user, C, src, 1, TRUE)) - do_sparks(3, 1, src) - return TRUE - else - return FALSE - return FALSE - -/obj/structure/grille/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(!broken) - if(exposed_temperature > T0C + 1500) - take_damage(1, BURN, 0, 0) - -/obj/structure/grille/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(isobj(AM)) - if(prob(50) && anchored && !broken) - var/obj/O = AM - if(O.throwforce != 0)//don't want to let people spam tesla bolts, this way it will break after time - var/turf/T = get_turf(src) - var/obj/structure/cable/C = T.get_cable_node() - if(C) - playsound(src, 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5) - tesla_zap(src, 3, C.newavail() * 0.01) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot. - C.add_delayedload(C.newavail() * 0.0375) // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock. - return ..() - -/obj/structure/grille/broken // Pre-broken grilles for map placement - icon_state = "brokengrille" - density = 0 - obj_integrity = 20 - broken = 1 - rods_amount = 1 - rods_broken = 0 - grille_type = /obj/structure/grille - broken_type = null - -/obj/structure/grille/ratvar - icon_state = "ratvargrille" - name = "cog grille" - desc = "A strangely-shaped grille." - broken_type = /obj/structure/grille/ratvar/broken - -/obj/structure/grille/ratvar/New() - ..() - if(broken) - new /obj/effect/temp_visual/ratvar/grille/broken(get_turf(src)) - else - new /obj/effect/temp_visual/ratvar/grille(get_turf(src)) - new /obj/effect/temp_visual/ratvar/beam/grille(get_turf(src)) - -/obj/structure/grille/ratvar/narsie_act() - take_damage(rand(1, 3), BRUTE) - if(src) - var/previouscolor = color - color = "#960000" - animate(src, color = previouscolor, time = 8) - -/obj/structure/grille/ratvar/ratvar_act() - return - -/obj/structure/grille/ratvar/broken - icon_state = "brokenratvargrille" - density = FALSE - obj_integrity = 20 - broken = TRUE - rods_amount = 1 - rods_broken = 0 - grille_type = /obj/structure/grille/ratvar - broken_type = null +/obj/structure/grille + desc = "A flimsy framework of metal rods." + name = "grille" + icon = 'icons/obj/structures.dmi' + icon_state = "grille" + density = TRUE + anchored = TRUE + flags = CONDUCT + pressure_resistance = 5*ONE_ATMOSPHERE + layer = BELOW_OBJ_LAYER + level = 3 + armor = list("melee" = 50, "bullet" = 70, "laser" = 70, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0) + max_integrity = 50 + integrity_failure = 20 + var/rods_type = /obj/item/stack/rods + var/rods_amount = 2 + var/rods_broken = 1 + var/grille_type + var/broken_type = /obj/structure/grille/broken + var/shockcooldown = 0 + var/my_shockcooldown = 1 SECONDS + +/obj/structure/grille/fence/ + var/width = 3 + +/obj/structure/grille/fence/New() + if(width > 1) + if(dir in list(EAST, WEST)) + bound_width = width * world.icon_size + bound_height = world.icon_size + else + bound_width = world.icon_size + bound_height = width * world.icon_size + +/obj/structure/grille/fence/east_west + //width=80 + //height=42 + icon='icons/fence-ew.dmi' + +/obj/structure/grille/fence/north_south + //width=80 + //height=42 + icon='icons/fence-ns.dmi' + +/obj/structure/grille/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + update_icon() + +/obj/structure/grille/examine(mob/user) + . = ..() + if(anchored) + . += "It's secured in place with screws. The rods look like they could be cut through." + if(!anchored) + . += "The anchoring screws are unscrewed. The rods look like they could be cut through." + +/obj/structure/grille/ratvar_act() + if(broken) + new /obj/structure/grille/ratvar/broken(loc) + else + new /obj/structure/grille/ratvar(loc) + qdel(src) + +/obj/structure/grille/Bumped(atom/user) + if(ismob(user)) + if(!(shockcooldown <= world.time)) + return + shock(user, 70) + shockcooldown = world.time + my_shockcooldown + +/obj/structure/grille/attack_animal(mob/user) + . = ..() + if(. && !QDELETED(src) && !shock(user, 70)) + take_damage(rand(5,10), BRUTE, "melee", 1) + +/obj/structure/grille/hulk_damage() + return 60 + +/obj/structure/grille/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM) + if(!shock(user, 70)) + ..(user, TRUE) + return TRUE + +/obj/structure/grille/attack_hand(mob/living/user) + . = ..() + if(.) + return + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_KICK) + user.visible_message("[user] hits [src].") + if(!shock(user, 70)) + take_damage(rand(5,10), BRUTE, "melee", 1) + +/obj/structure/grille/attack_alien(mob/living/user) + user.do_attack_animation(src) + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message("[user] mangles [src].") + if(!shock(user, 70)) + take_damage(20, BRUTE, "melee", 1) + +/obj/structure/grille/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSGRILLE)) + return 1 + else + if(istype(mover, /obj/item/projectile)) + return prob(30) + else + return !density + +/obj/structure/grille/CanAStarPass(ID, dir, caller) + . = !density + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSGRILLE) + +/obj/structure/grille/attackby(obj/item/W, mob/user, params) + user.changeNext_move(CLICK_CD_MELEE) + add_fingerprint(user) + if(istype(W, /obj/item/stack/rods) && broken) + var/obj/item/stack/rods/R = W + if(!shock(user, 90)) + user.visible_message("[user] rebuilds the broken grille.", \ + "You rebuild the broken grille.") + new grille_type(loc) + R.use(1) + qdel(src) + return + +//window placing begin + else if(is_glass_sheet(W)) + build_window(W, user) + return +//window placing end + + else if(istype(W, /obj/item/shard) || !shock(user, 70)) + return ..() + +/obj/structure/grille/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(shock(user, 100)) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + deconstruct() + +/obj/structure/grille/screwdriver_act(mob/user, obj/item/I) + if(!(istype(loc, /turf/simulated) || anchored)) + return + . = TRUE + if(shock(user, 90)) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + user.visible_message("[user] [anchored ? "fastens" : "unfastens"] [src].", \ + "You [anchored ? "fasten [src] to" : "unfasten [src] from"] the floor.") + +/obj/structure/grille/proc/build_window(obj/item/stack/sheet/S, mob/user) + var/dir_to_set = NORTH + if(!istype(S) || !user) + return + if(broken) + to_chat(user, "You must repair or replace [src] first!") + return + if(S.get_amount() < 1) + to_chat(user, "You need at least one sheet of glass for that!") + return + if(!anchored) + to_chat(user, "[src] needs to be fastened to the floor first!") + return + if(!getRelativeDirection(src, user) && (user.loc != loc)) //essentially a cardinal direction adjacent or sharing same loc check + to_chat(user, "You can't reach.") + return + if(loc == user.loc) + dir_to_set = user.dir + else + if(x == user.x) + if(y > user.y) + dir_to_set = SOUTH + else + dir_to_set = NORTH + else if(y == user.y) + if(x > user.x) + dir_to_set = WEST + else + dir_to_set = EAST + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set) + to_chat(user, "There is already a window facing this way there.") + return + to_chat(user, "You start placing the window...") + if(do_after(user, 20, target = src)) + if(!loc || !anchored) //Grille destroyed or unanchored while waiting + return + for(var/obj/structure/window/WINDOW in loc) + if(WINDOW.dir == dir_to_set)//checking this for a 2nd time to check if a window was made while we were waiting. + to_chat(user, "There is already a window facing this way there.") + return + var/obj/structure/window/W = new S.created_window(get_turf(src)) + S.use(1) + W.setDir(dir_to_set) + W.ini_dir = dir_to_set + W.anchored = FALSE + W.state = WINDOW_OUT_OF_FRAME + to_chat(user, "You place the [W] on [src].") + W.update_nearby_icons() + + +/obj/structure/grille/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src, 'sound/effects/grillehit.ogg', 80, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src, 'sound/items/welder.ogg', 80, TRUE) + +/obj/structure/grille/deconstruct(disassembled = TRUE) + if(!loc) //if already qdel'd somehow, we do nothing + return + if(!(flags & NODECONSTRUCT)) + var/obj/R = new rods_type(drop_location(), rods_amount) + transfer_fingerprints_to(R) + qdel(src) + ..() + +/obj/structure/grille/obj_break() + if(!broken && !(flags & NODECONSTRUCT)) + new broken_type(loc) + var/obj/R = new rods_type(drop_location(), rods_broken) + transfer_fingerprints_to(R) + qdel(src) + +// shock user with probability prb (if all connections & power are working) +// returns 1 if shocked, 0 otherwise + +/obj/structure/grille/proc/shock(mob/user, prb) + if(!anchored || broken) // unanchored/broken grilles are never connected + return FALSE + if(!prob(prb)) + return FALSE + if(!in_range(src, user))//To prevent TK and mech users from getting shocked + return FALSE + var/turf/T = get_turf(src) + var/obj/structure/cable/C = T.get_cable_node() + if(C) + if(electrocute_mob(user, C, src, 1, TRUE)) + do_sparks(3, 1, src) + return TRUE + else + return FALSE + return FALSE + +/obj/structure/grille/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(!broken) + if(exposed_temperature > T0C + 1500) + take_damage(1, BURN, 0, 0) + +/obj/structure/grille/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(isobj(AM)) + if(prob(50) && anchored && !broken) + var/obj/O = AM + if(O.throwforce != 0)//don't want to let people spam tesla bolts, this way it will break after time + var/turf/T = get_turf(src) + var/obj/structure/cable/C = T.get_cable_node() + if(C) + playsound(src, 'sound/magic/lightningshock.ogg', 100, TRUE, extrarange = 5) + tesla_zap(src, 3, C.newavail() * 0.01) //Zap for 1/100 of the amount of power. At a million watts in the grid, it will be as powerful as a tesla revolver shot. + C.add_delayedload(C.newavail() * 0.0375) // you can gain up to 3.5 via the 4x upgrades power is halved by the pole so thats 2x then 1X then .5X for 3.5x the 3 bounces shock. + return ..() + +/obj/structure/grille/broken // Pre-broken grilles for map placement + icon_state = "brokengrille" + density = 0 + obj_integrity = 20 + broken = 1 + rods_amount = 1 + rods_broken = 0 + grille_type = /obj/structure/grille + broken_type = null + +/obj/structure/grille/ratvar + icon_state = "ratvargrille" + name = "cog grille" + desc = "A strangely-shaped grille." + broken_type = /obj/structure/grille/ratvar/broken + +/obj/structure/grille/ratvar/New() + ..() + if(broken) + new /obj/effect/temp_visual/ratvar/grille/broken(get_turf(src)) + else + new /obj/effect/temp_visual/ratvar/grille(get_turf(src)) + new /obj/effect/temp_visual/ratvar/beam/grille(get_turf(src)) + +/obj/structure/grille/ratvar/narsie_act() + take_damage(rand(1, 3), BRUTE) + if(src) + var/previouscolor = color + color = "#960000" + animate(src, color = previouscolor, time = 8) + +/obj/structure/grille/ratvar/ratvar_act() + return + +/obj/structure/grille/ratvar/broken + icon_state = "brokenratvargrille" + density = FALSE + obj_integrity = 20 + broken = TRUE + rods_amount = 1 + rods_broken = 0 + grille_type = /obj/structure/grille/ratvar + broken_type = null diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm index e5b5b6c73734..9184d739a5f6 100644 --- a/code/game/objects/structures/guillotine.dm +++ b/code/game/objects/structures/guillotine.dm @@ -253,4 +253,4 @@ #undef GUILLOTINE_ACTIVATE_DELAY #undef GUILLOTINE_WRENCH_DELAY #undef GUILLOTINE_ACTION_INUSE -#undef GUILLOTINE_ACTION_WRENCH \ No newline at end of file +#undef GUILLOTINE_ACTION_WRENCH diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index e9853f121623..d3cf5b0dd966 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -208,4 +208,4 @@ new /obj/item/inflatable(src) new /obj/item/inflatable(src) new /obj/item/inflatable(src) - new /obj/item/inflatable(src) \ No newline at end of file + new /obj/item/inflatable(src) diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index bba1dc18de65..58120ca4ed55 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -1,186 +1,186 @@ -//TG style Janicart - -/obj/structure/janitorialcart - name = "janitorial cart" - desc = "This is the alpha and omega of sanitation." - icon = 'icons/obj/janitor.dmi' - icon_state = "cart" - anchored = 0 - density = 1 - container_type = OPENCONTAINER - //copypaste sorry - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - var/obj/item/storage/bag/trash/mybag = null - var/obj/item/mop/mymop = null - var/obj/item/reagent_containers/spray/cleaner/myspray = null - var/obj/item/lightreplacer/myreplacer = null - var/signs = 0 - var/const/max_signs = 4 - - -/obj/structure/janitorialcart/New() - ..() - create_reagents(100) - GLOB.janitorial_equipment += src - -/obj/structure/janitorialcart/Destroy() - GLOB.janitorial_equipment -= src - QDEL_NULL(mybag) - QDEL_NULL(mymop) - QDEL_NULL(myspray) - QDEL_NULL(myreplacer) - return ..() - -/obj/structure/janitorialcart/proc/wet_mop(obj/item/mop, mob/user) - if(reagents.total_volume < 1) - to_chat(user, "[src] is out of water!
    ") - else - reagents.trans_to(mop, 5) // - to_chat(user, "You wet [mop] in [src].") - playsound(loc, 'sound/effects/slosh.ogg', 25, 1) - -/obj/structure/janitorialcart/proc/put_in_cart(obj/item/I, mob/user) - user.drop_item() - I.loc = src - updateUsrDialog() - to_chat(user, "You put [I] into [src].") - return - - -/obj/structure/janitorialcart/attackby(obj/item/I, mob/user, params) - var/fail_msg = "There is already one of those in [src]." - - if(!I.is_robot_module()) - if(istype(I, /obj/item/mop)) - var/obj/item/mop/m=I - if(m.reagents.total_volume < m.reagents.maximum_volume) - wet_mop(m, user) - return - if(!mymop) - m.janicart_insert(user, src) - else - to_chat(user, fail_msg) - - else if(istype(I, /obj/item/storage/bag/trash)) - if(!mybag) - var/obj/item/storage/bag/trash/t=I - t.janicart_insert(user, src) - else - to_chat(user, fail_msg) - else if(istype(I, /obj/item/reagent_containers/spray/cleaner)) - if(!myspray) - put_in_cart(I, user) - myspray=I - update_icon() - else - to_chat(user, fail_msg) - else if(istype(I, /obj/item/lightreplacer)) - if(!myreplacer) - var/obj/item/lightreplacer/l=I - l.janicart_insert(user,src) - else - to_chat(user, fail_msg) - else if(istype(I, /obj/item/caution)) - if(signs < max_signs) - put_in_cart(I, user) - signs++ - update_icon() - else - to_chat(user, "[src] can't hold any more signs.") - else if(istype(I, /obj/item/crowbar)) - user.visible_message("[user] begins to empty the contents of [src].") - if(do_after(user, 30 * I.toolspeed, target = src)) - to_chat(usr, "You empty the contents of [src]'s bucket onto the floor.") - reagents.reaction(src.loc) - src.reagents.clear_reagents() - else if(istype(I, /obj/item/wrench)) - if(!anchored && !isinspace()) - playsound(src.loc, I.usesound, 50, 1) - user.visible_message( \ - "[user] tightens \the [src]'s casters.", \ - " You have tightened \the [src]'s casters.", \ - "You hear ratchet.") - anchored = 1 - else if(anchored) - playsound(src.loc, I.usesound, 50, 1) - user.visible_message( \ - "[user] loosens \the [src]'s casters.", \ - " You have loosened \the [src]'s casters.", \ - "You hear ratchet.") - anchored = 0 - else if(mybag) - mybag.attackby(I, user, params) - else - to_chat(usr, "You cannot interface your modules [src]!") - -/obj/structure/janitorialcart/attack_hand(mob/user) - user.set_machine(src) - var/dat - if(mybag) - dat += "[mybag.name]
    " - if(mymop) - dat += "[mymop.name]
    " - if(myspray) - dat += "[myspray.name]
    " - if(myreplacer) - dat += "[myreplacer.name]
    " - if(signs) - dat += "[signs] sign\s
    " - var/datum/browser/popup = new(user, "janicart", name, 240, 160) - popup.set_content(dat) - popup.open() - - -/obj/structure/janitorialcart/Topic(href, href_list) - if(!in_range(src, usr)) - return - if(!isliving(usr)) - return - var/mob/living/user = usr - if(href_list["garbage"]) - if(mybag) - user.put_in_hands(mybag) - to_chat(user, "You take [mybag] from [src].") - mybag = null - if(href_list["mop"]) - if(mymop) - user.put_in_hands(mymop) - to_chat(user, "You take [mymop] from [src].") - mymop = null - if(href_list["spray"]) - if(myspray) - user.put_in_hands(myspray) - to_chat(user, "You take [myspray] from [src].") - myspray = null - if(href_list["replacer"]) - if(myreplacer) - user.put_in_hands(myreplacer) - to_chat(user, "You take [myreplacer] from [src].") - myreplacer = null - if(href_list["sign"]) - if(signs) - var/obj/item/caution/Sign = locate() in src - if(Sign) - user.put_in_hands(Sign) - to_chat(user, "You take \a [Sign] from [src].") - signs-- - else - WARNING("Signs ([signs]) didn't match contents") - signs = 0 - - update_icon() - updateUsrDialog() - - -/obj/structure/janitorialcart/update_icon() - overlays = null - if(mybag) - overlays += "cart_garbage" - if(mymop) - overlays += "cart_mop" - if(myspray) - overlays += "cart_spray" - if(myreplacer) - overlays += "cart_replacer" - if(signs) - overlays += "cart_sign[signs]" \ No newline at end of file +//TG style Janicart + +/obj/structure/janitorialcart + name = "janitorial cart" + desc = "This is the alpha and omega of sanitation." + icon = 'icons/obj/janitor.dmi' + icon_state = "cart" + anchored = 0 + density = 1 + container_type = OPENCONTAINER + //copypaste sorry + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + var/obj/item/storage/bag/trash/mybag = null + var/obj/item/mop/mymop = null + var/obj/item/reagent_containers/spray/cleaner/myspray = null + var/obj/item/lightreplacer/myreplacer = null + var/signs = 0 + var/const/max_signs = 4 + + +/obj/structure/janitorialcart/New() + ..() + create_reagents(100) + GLOB.janitorial_equipment += src + +/obj/structure/janitorialcart/Destroy() + GLOB.janitorial_equipment -= src + QDEL_NULL(mybag) + QDEL_NULL(mymop) + QDEL_NULL(myspray) + QDEL_NULL(myreplacer) + return ..() + +/obj/structure/janitorialcart/proc/wet_mop(obj/item/mop, mob/user) + if(reagents.total_volume < 1) + to_chat(user, "[src] is out of water!
    ") + else + reagents.trans_to(mop, 5) // + to_chat(user, "You wet [mop] in [src].") + playsound(loc, 'sound/effects/slosh.ogg', 25, 1) + +/obj/structure/janitorialcart/proc/put_in_cart(obj/item/I, mob/user) + user.drop_item() + I.loc = src + updateUsrDialog() + to_chat(user, "You put [I] into [src].") + return + + +/obj/structure/janitorialcart/attackby(obj/item/I, mob/user, params) + var/fail_msg = "There is already one of those in [src]." + + if(!I.is_robot_module()) + if(istype(I, /obj/item/mop)) + var/obj/item/mop/m=I + if(m.reagents.total_volume < m.reagents.maximum_volume) + wet_mop(m, user) + return + if(!mymop) + m.janicart_insert(user, src) + else + to_chat(user, fail_msg) + + else if(istype(I, /obj/item/storage/bag/trash)) + if(!mybag) + var/obj/item/storage/bag/trash/t=I + t.janicart_insert(user, src) + else + to_chat(user, fail_msg) + else if(istype(I, /obj/item/reagent_containers/spray/cleaner)) + if(!myspray) + put_in_cart(I, user) + myspray=I + update_icon() + else + to_chat(user, fail_msg) + else if(istype(I, /obj/item/lightreplacer)) + if(!myreplacer) + var/obj/item/lightreplacer/l=I + l.janicart_insert(user,src) + else + to_chat(user, fail_msg) + else if(istype(I, /obj/item/caution)) + if(signs < max_signs) + put_in_cart(I, user) + signs++ + update_icon() + else + to_chat(user, "[src] can't hold any more signs.") + else if(istype(I, /obj/item/crowbar)) + user.visible_message("[user] begins to empty the contents of [src].") + if(do_after(user, 30 * I.toolspeed, target = src)) + to_chat(usr, "You empty the contents of [src]'s bucket onto the floor.") + reagents.reaction(src.loc) + src.reagents.clear_reagents() + else if(istype(I, /obj/item/wrench)) + if(!anchored && !isinspace()) + playsound(src.loc, I.usesound, 50, 1) + user.visible_message( \ + "[user] tightens \the [src]'s casters.", \ + " You have tightened \the [src]'s casters.", \ + "You hear ratchet.") + anchored = 1 + else if(anchored) + playsound(src.loc, I.usesound, 50, 1) + user.visible_message( \ + "[user] loosens \the [src]'s casters.", \ + " You have loosened \the [src]'s casters.", \ + "You hear ratchet.") + anchored = 0 + else if(mybag) + mybag.attackby(I, user, params) + else + to_chat(usr, "You cannot interface your modules [src]!") + +/obj/structure/janitorialcart/attack_hand(mob/user) + user.set_machine(src) + var/dat + if(mybag) + dat += "[mybag.name]
    " + if(mymop) + dat += "[mymop.name]
    " + if(myspray) + dat += "[myspray.name]
    " + if(myreplacer) + dat += "[myreplacer.name]
    " + if(signs) + dat += "[signs] sign\s
    " + var/datum/browser/popup = new(user, "janicart", name, 240, 160) + popup.set_content(dat) + popup.open() + + +/obj/structure/janitorialcart/Topic(href, href_list) + if(!in_range(src, usr)) + return + if(!isliving(usr)) + return + var/mob/living/user = usr + if(href_list["garbage"]) + if(mybag) + user.put_in_hands(mybag) + to_chat(user, "You take [mybag] from [src].") + mybag = null + if(href_list["mop"]) + if(mymop) + user.put_in_hands(mymop) + to_chat(user, "You take [mymop] from [src].") + mymop = null + if(href_list["spray"]) + if(myspray) + user.put_in_hands(myspray) + to_chat(user, "You take [myspray] from [src].") + myspray = null + if(href_list["replacer"]) + if(myreplacer) + user.put_in_hands(myreplacer) + to_chat(user, "You take [myreplacer] from [src].") + myreplacer = null + if(href_list["sign"]) + if(signs) + var/obj/item/caution/Sign = locate() in src + if(Sign) + user.put_in_hands(Sign) + to_chat(user, "You take \a [Sign] from [src].") + signs-- + else + WARNING("Signs ([signs]) didn't match contents") + signs = 0 + + update_icon() + updateUsrDialog() + + +/obj/structure/janitorialcart/update_icon() + overlays = null + if(mybag) + overlays += "cart_garbage" + if(mymop) + overlays += "cart_mop" + if(myspray) + overlays += "cart_spray" + if(myreplacer) + overlays += "cart_replacer" + if(signs) + overlays += "cart_sign[signs]" diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 6281d589c707..21af7f6e615c 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -1,156 +1,156 @@ - -//////Kitchen Spike - -/obj/structure/kitchenspike_frame - name = "meatspike frame" - icon = 'icons/obj/kitchen.dmi' - icon_state = "spikeframe" - desc = "The frame of a meat spike." - density = 1 - anchored = 0 - max_integrity = 200 - -/obj/structure/kitchenspike_frame/attackby(obj/item/I, mob/user, params) - add_fingerprint(user) - if(istype(I, /obj/item/wrench)) - if(anchored) - to_chat(user, "You unwrench [src] from the floor.") - anchored = 0 - else - to_chat(user, "You wrench [src] into place.") - anchored = 1 - else if(istype(I, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = I - if(R.get_amount() >= 4) - R.use(4) - to_chat(user, "You add spikes to the frame.") - new /obj/structure/kitchenspike(loc) - add_fingerprint(user) - qdel(src) - return - else - return ..() - - -/obj/structure/kitchenspike - name = "meat spike" - icon = 'icons/obj/kitchen.dmi' - icon_state = "spike" - desc = "A spike for collecting meat from animals." - density = 1 - anchored = 1 - buckle_lying = FALSE - can_buckle = TRUE - max_integrity = 250 - -//ATTACK HAND IGNORING PARENT RETURN VALUE -/obj/structure/kitchenspike/attack_hand(mob/user) - if(has_buckled_mobs()) - for(var/mob/living/L in buckled_mobs) - user_unbuckle_mob(L, user) - else - ..() - -/obj/structure/kitchenspike/attackby(obj/item/grab/G, mob/user) - if(istype(G, /obj/item/crowbar)) - if(!has_buckled_mobs()) - playsound(loc, G.usesound, 100, 1) - if(do_after(user, 20 * G.toolspeed, target = src)) - to_chat(user, "You pry the spikes out of the frame.") - deconstruct(TRUE) - else - to_chat(user, "You can't do that while something's on the spike!") - return - if(!istype(G, /obj/item/grab) || !G.affecting) - return - if(has_buckled_mobs()) - to_chat(user, "The spike already has something on it, finish collecting its meat first!") - return - if(isliving(G.affecting)) - if(!has_buckled_mobs()) - if(do_mob(user, src, 120)) - if(spike(G.affecting)) - G.affecting.visible_message("[user] slams [G.affecting] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.") - qdel(G) - return - return ..() - -/obj/structure/kitchenspike/proc/spike(mob/living/victim) - - if(!istype(victim)) - return FALSE - - if(has_buckled_mobs()) //to prevent spam/queing up attacks - return FALSE - if(victim.buckled) - return FALSE - playsound(loc, 'sound/effects/splat.ogg', 25, 1) - victim.forceMove(drop_location()) - victim.emote("scream") - if(ishuman(victim)) - var/mob/living/carbon/human/H = victim - H.add_splatter_floor() - victim.adjustBruteLoss(30) - victim.setDir(2) - buckle_mob(victim, force = TRUE) - var/matrix/m180 = matrix(victim.transform) - m180.Turn(180) - animate(victim, transform = m180, time = 3) - victim.pixel_y = victim.get_standard_pixel_y_offset(180) - return TRUE - - -/obj/structure/kitchenspike/user_buckle_mob(mob/living/M, mob/living/user) //Don't want them getting put on the rack other than by spiking - return - -/obj/structure/kitchenspike/user_unbuckle_mob(mob/living/buckled_mob, mob/living/carbon/human/user) - if(buckled_mob) - var/mob/living/M = buckled_mob - if(M != user) - M.visible_message("[user] tries to pull [M] free of [src]!",\ - "[user] is trying to pull you off [src], opening up fresh wounds!",\ - "You hear a squishy wet noise.") - if(!do_after(user, 300, target = src)) - if(M && M.buckled) - M.visible_message("[user] fails to free [M]!",\ - "[user] fails to pull you off of [src].") - return - - else - M.visible_message("[M] struggles to break free from [src]!",\ - "You struggle to break free from [src], exacerbating your wounds! (Stay still for two minutes.)",\ - "You hear a wet squishing noise..") - M.adjustBruteLoss(30) - if(!do_after(M, 1200, target = src)) - if(M && M.buckled) - to_chat(M, "You fail to free yourself!") - return - if(!M.buckled) - return - release_mob(M) - -/obj/structure/kitchenspike/proc/release_mob(mob/living/M) - var/matrix/m180 = matrix(M.transform) - m180.Turn(180) - animate(M, transform = m180, time = 3) - M.pixel_y = M.get_standard_pixel_y_offset(180) - M.adjustBruteLoss(30) - src.visible_message(text("[M] falls free of [src]!")) - unbuckle_mob(M, force = TRUE) - M.emote("scream") - M.AdjustWeakened(10) - -/obj/structure/kitchenspike/Destroy() - if(has_buckled_mobs()) - for(var/mob/living/L in buckled_mobs) - release_mob(L) - return ..() - -/obj/structure/kitchenspike/deconstruct(disassembled = TRUE) - if(disassembled) - var/obj/F = new /obj/structure/kitchenspike_frame(loc) - transfer_fingerprints_to(F) - else - new /obj/item/stack/sheet/metal(loc, 4) - new /obj/item/stack/rods(loc, 4) - qdel(src) \ No newline at end of file + +//////Kitchen Spike + +/obj/structure/kitchenspike_frame + name = "meatspike frame" + icon = 'icons/obj/kitchen.dmi' + icon_state = "spikeframe" + desc = "The frame of a meat spike." + density = 1 + anchored = 0 + max_integrity = 200 + +/obj/structure/kitchenspike_frame/attackby(obj/item/I, mob/user, params) + add_fingerprint(user) + if(istype(I, /obj/item/wrench)) + if(anchored) + to_chat(user, "You unwrench [src] from the floor.") + anchored = 0 + else + to_chat(user, "You wrench [src] into place.") + anchored = 1 + else if(istype(I, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = I + if(R.get_amount() >= 4) + R.use(4) + to_chat(user, "You add spikes to the frame.") + new /obj/structure/kitchenspike(loc) + add_fingerprint(user) + qdel(src) + return + else + return ..() + + +/obj/structure/kitchenspike + name = "meat spike" + icon = 'icons/obj/kitchen.dmi' + icon_state = "spike" + desc = "A spike for collecting meat from animals." + density = 1 + anchored = 1 + buckle_lying = FALSE + can_buckle = TRUE + max_integrity = 250 + +//ATTACK HAND IGNORING PARENT RETURN VALUE +/obj/structure/kitchenspike/attack_hand(mob/user) + if(has_buckled_mobs()) + for(var/mob/living/L in buckled_mobs) + user_unbuckle_mob(L, user) + else + ..() + +/obj/structure/kitchenspike/attackby(obj/item/grab/G, mob/user) + if(istype(G, /obj/item/crowbar)) + if(!has_buckled_mobs()) + playsound(loc, G.usesound, 100, 1) + if(do_after(user, 20 * G.toolspeed, target = src)) + to_chat(user, "You pry the spikes out of the frame.") + deconstruct(TRUE) + else + to_chat(user, "You can't do that while something's on the spike!") + return + if(!istype(G, /obj/item/grab) || !G.affecting) + return + if(has_buckled_mobs()) + to_chat(user, "The spike already has something on it, finish collecting its meat first!") + return + if(isliving(G.affecting)) + if(!has_buckled_mobs()) + if(do_mob(user, src, 120)) + if(spike(G.affecting)) + G.affecting.visible_message("[user] slams [G.affecting] onto the meat spike!", "[user] slams you onto the meat spike!", "You hear a squishy wet noise.") + qdel(G) + return + return ..() + +/obj/structure/kitchenspike/proc/spike(mob/living/victim) + + if(!istype(victim)) + return FALSE + + if(has_buckled_mobs()) //to prevent spam/queing up attacks + return FALSE + if(victim.buckled) + return FALSE + playsound(loc, 'sound/effects/splat.ogg', 25, 1) + victim.forceMove(drop_location()) + victim.emote("scream") + if(ishuman(victim)) + var/mob/living/carbon/human/H = victim + H.add_splatter_floor() + victim.adjustBruteLoss(30) + victim.setDir(2) + buckle_mob(victim, force = TRUE) + var/matrix/m180 = matrix(victim.transform) + m180.Turn(180) + animate(victim, transform = m180, time = 3) + victim.pixel_y = victim.get_standard_pixel_y_offset(180) + return TRUE + + +/obj/structure/kitchenspike/user_buckle_mob(mob/living/M, mob/living/user) //Don't want them getting put on the rack other than by spiking + return + +/obj/structure/kitchenspike/user_unbuckle_mob(mob/living/buckled_mob, mob/living/carbon/human/user) + if(buckled_mob) + var/mob/living/M = buckled_mob + if(M != user) + M.visible_message("[user] tries to pull [M] free of [src]!",\ + "[user] is trying to pull you off [src], opening up fresh wounds!",\ + "You hear a squishy wet noise.") + if(!do_after(user, 300, target = src)) + if(M && M.buckled) + M.visible_message("[user] fails to free [M]!",\ + "[user] fails to pull you off of [src].") + return + + else + M.visible_message("[M] struggles to break free from [src]!",\ + "You struggle to break free from [src], exacerbating your wounds! (Stay still for two minutes.)",\ + "You hear a wet squishing noise..") + M.adjustBruteLoss(30) + if(!do_after(M, 1200, target = src)) + if(M && M.buckled) + to_chat(M, "You fail to free yourself!") + return + if(!M.buckled) + return + release_mob(M) + +/obj/structure/kitchenspike/proc/release_mob(mob/living/M) + var/matrix/m180 = matrix(M.transform) + m180.Turn(180) + animate(M, transform = m180, time = 3) + M.pixel_y = M.get_standard_pixel_y_offset(180) + M.adjustBruteLoss(30) + src.visible_message(text("[M] falls free of [src]!")) + unbuckle_mob(M, force = TRUE) + M.emote("scream") + M.AdjustWeakened(10) + +/obj/structure/kitchenspike/Destroy() + if(has_buckled_mobs()) + for(var/mob/living/L in buckled_mobs) + release_mob(L) + return ..() + +/obj/structure/kitchenspike/deconstruct(disassembled = TRUE) + if(disassembled) + var/obj/F = new /obj/structure/kitchenspike_frame(loc) + transfer_fingerprints_to(F) + else + new /obj/item/stack/sheet/metal(loc, 4) + new /obj/item/stack/rods(loc, 4) + qdel(src) diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index a5fdff392b48..d84c1f39728a 100644 --- a/code/game/objects/structures/lattice.dm +++ b/code/game/objects/structures/lattice.dm @@ -1,126 +1,126 @@ -/obj/structure/lattice - name = "lattice" - desc = "A lightweight support lattice." - icon = 'icons/obj/smooth_structures/lattice.dmi' - icon_state = "lattice" - density = FALSE - anchored = TRUE - armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - max_integrity = 50 - layer = LATTICE_LAYER //under pipes - plane = FLOOR_PLANE - var/number_of_rods = 1 - canSmoothWith = list(/obj/structure/lattice, - /turf/simulated/floor, - /turf/simulated/wall, - /obj/structure/falsewall) - smooth = SMOOTH_MORE - -/obj/structure/lattice/Initialize(mapload) - . = ..() - for(var/obj/structure/lattice/LAT in loc) - if(LAT != src) - QDEL_IN(LAT, 0) - -/obj/structure/lattice/examine(mob/user) - . = ..() - . += deconstruction_hints(user) - -/obj/structure/lattice/proc/deconstruction_hints(mob/user) - return "The rods look like they could be cut. There's space for more rods or a tile." - -/obj/structure/lattice/attackby(obj/item/C, mob/user, params) - if(resistance_flags & INDESTRUCTIBLE) - return - if(istype(C, /obj/item/wirecutters)) - var/obj/item/wirecutters/W = C - playsound(loc, W.usesound, 50, 1) - to_chat(user, "Slicing [name] joints...") - deconstruct() - else - var/turf/T = get_turf(src) - return T.attackby(C, user) //hand this off to the turf instead (for building plating, catwalks, etc) - -/obj/structure/lattice/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/rods(get_turf(src), number_of_rods) - qdel(src) - - -/obj/structure/lattice/blob_act(obj/structure/blob/B) - return - -/obj/structure/lattice/singularity_pull(S, current_size) - if(current_size >= STAGE_FOUR) - deconstruct() - -/obj/structure/lattice/clockwork - name = "cog lattice" - desc = "A lightweight support lattice. These hold the Justicar's station together." - icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi' - -/obj/structure/lattice/clockwork/Initialize(mapload) - . = ..() - ratvar_act() - -/obj/structure/lattice/clockwork/ratvar_act() - if((x + y) % 2 != 0) - icon = 'icons/obj/smooth_structures/lattice_clockwork_large.dmi' - pixel_x = -9 - pixel_y = -9 - else - icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi' - pixel_x = 0 - pixel_y = 0 - return TRUE - -/obj/structure/lattice/catwalk - name = "catwalk" - desc = "A catwalk for easier EVA maneuvering and cable placement." - icon = 'icons/obj/smooth_structures/catwalk.dmi' - icon_state = "catwalk" - number_of_rods = 2 - smooth = SMOOTH_TRUE - canSmoothWith = null - -/obj/structure/lattice/catwalk/deconstruction_hints(mob/user) - to_chat(user, "The supporting rods look like they could be cut.") - -/obj/structure/lattice/catwalk/Move() - var/turf/T = loc - for(var/obj/structure/cable/C in T) - C.deconstruct() - ..() - -/obj/structure/lattice/catwalk/deconstruct() - var/turf/T = loc - for(var/obj/structure/cable/C in T) - C.deconstruct() - ..() - -/obj/structure/lattice/catwalk/clockwork - name = "clockwork catwalk" - icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi' - canSmoothWith = list(/obj/structure/lattice, - /turf/simulated/floor, - /turf/simulated/wall, - /obj/structure/falsewall) - smooth = SMOOTH_MORE - -/obj/structure/lattice/catwalk/clockwork/Initialize(mapload) - . = ..() - ratvar_act() - if(!mapload) - new /obj/effect/temp_visual/ratvar/floor/catwalk(loc) - new /obj/effect/temp_visual/ratvar/beam/catwalk(loc) - -/obj/structure/lattice/catwalk/clockwork/ratvar_act() - if((x + y) % 2 != 0) - icon = 'icons/obj/smooth_structures/catwalk_clockwork_large.dmi' - pixel_x = -9 - pixel_y = -9 - else - icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi' - pixel_x = 0 - pixel_y = 0 - return TRUE +/obj/structure/lattice + name = "lattice" + desc = "A lightweight support lattice." + icon = 'icons/obj/smooth_structures/lattice.dmi' + icon_state = "lattice" + density = FALSE + anchored = TRUE + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + max_integrity = 50 + layer = LATTICE_LAYER //under pipes + plane = FLOOR_PLANE + var/number_of_rods = 1 + canSmoothWith = list(/obj/structure/lattice, + /turf/simulated/floor, + /turf/simulated/wall, + /obj/structure/falsewall) + smooth = SMOOTH_MORE + +/obj/structure/lattice/Initialize(mapload) + . = ..() + for(var/obj/structure/lattice/LAT in loc) + if(LAT != src) + QDEL_IN(LAT, 0) + +/obj/structure/lattice/examine(mob/user) + . = ..() + . += deconstruction_hints(user) + +/obj/structure/lattice/proc/deconstruction_hints(mob/user) + return "The rods look like they could be cut. There's space for more rods or a tile." + +/obj/structure/lattice/attackby(obj/item/C, mob/user, params) + if(resistance_flags & INDESTRUCTIBLE) + return + if(istype(C, /obj/item/wirecutters)) + var/obj/item/wirecutters/W = C + playsound(loc, W.usesound, 50, 1) + to_chat(user, "Slicing [name] joints...") + deconstruct() + else + var/turf/T = get_turf(src) + return T.attackby(C, user) //hand this off to the turf instead (for building plating, catwalks, etc) + +/obj/structure/lattice/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/rods(get_turf(src), number_of_rods) + qdel(src) + + +/obj/structure/lattice/blob_act(obj/structure/blob/B) + return + +/obj/structure/lattice/singularity_pull(S, current_size) + if(current_size >= STAGE_FOUR) + deconstruct() + +/obj/structure/lattice/clockwork + name = "cog lattice" + desc = "A lightweight support lattice. These hold the Justicar's station together." + icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi' + +/obj/structure/lattice/clockwork/Initialize(mapload) + . = ..() + ratvar_act() + +/obj/structure/lattice/clockwork/ratvar_act() + if((x + y) % 2 != 0) + icon = 'icons/obj/smooth_structures/lattice_clockwork_large.dmi' + pixel_x = -9 + pixel_y = -9 + else + icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi' + pixel_x = 0 + pixel_y = 0 + return TRUE + +/obj/structure/lattice/catwalk + name = "catwalk" + desc = "A catwalk for easier EVA maneuvering and cable placement." + icon = 'icons/obj/smooth_structures/catwalk.dmi' + icon_state = "catwalk" + number_of_rods = 2 + smooth = SMOOTH_TRUE + canSmoothWith = null + +/obj/structure/lattice/catwalk/deconstruction_hints(mob/user) + to_chat(user, "The supporting rods look like they could be cut.") + +/obj/structure/lattice/catwalk/Move() + var/turf/T = loc + for(var/obj/structure/cable/C in T) + C.deconstruct() + ..() + +/obj/structure/lattice/catwalk/deconstruct() + var/turf/T = loc + for(var/obj/structure/cable/C in T) + C.deconstruct() + ..() + +/obj/structure/lattice/catwalk/clockwork + name = "clockwork catwalk" + icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi' + canSmoothWith = list(/obj/structure/lattice, + /turf/simulated/floor, + /turf/simulated/wall, + /obj/structure/falsewall) + smooth = SMOOTH_MORE + +/obj/structure/lattice/catwalk/clockwork/Initialize(mapload) + . = ..() + ratvar_act() + if(!mapload) + new /obj/effect/temp_visual/ratvar/floor/catwalk(loc) + new /obj/effect/temp_visual/ratvar/beam/catwalk(loc) + +/obj/structure/lattice/catwalk/clockwork/ratvar_act() + if((x + y) % 2 != 0) + icon = 'icons/obj/smooth_structures/catwalk_clockwork_large.dmi' + pixel_x = -9 + pixel_y = -9 + else + icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi' + pixel_x = 0 + pixel_y = 0 + return TRUE diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm index f906bb30a342..f9717956bb0a 100644 --- a/code/game/objects/structures/lavaland/necropolis_tendril.dm +++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm @@ -97,4 +97,4 @@ GLOBAL_LIST_INIT(tendrils, list()) for(var/turf/T in range(2,src)) if(!T.density) T.TerraformTurf(/turf/simulated/floor/chasm/straight_down/lava_land_surface) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/game/objects/structures/loom.dm b/code/game/objects/structures/loom.dm index 26bd4136c2a9..d0bcd07f92f2 100644 --- a/code/game/objects/structures/loom.dm +++ b/code/game/objects/structures/loom.dm @@ -35,4 +35,4 @@ user.show_message("You weave \the [W.name] into a workable fabric.", 1) return TRUE -#undef FABRIC_PER_SHEET \ No newline at end of file +#undef FABRIC_PER_SHEET diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm index c51400fc7d36..8e704fb83f7c 100644 --- a/code/game/objects/structures/mineral_doors.dm +++ b/code/game/objects/structures/mineral_doors.dm @@ -1,236 +1,236 @@ -//NOT using the existing /obj/machinery/door type, since that has some complications on its own, mainly based on its machineryness -/obj/structure/mineral_door - name = "metal door" - density = 1 - anchored = 1 - opacity = 1 - - icon = 'icons/obj/doors/mineral_doors.dmi' - icon_state = "metal" - max_integrity = 200 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 50, "acid" = 50) - var/initial_state - var/state = 0 //closed, 1 == open - var/isSwitchingStates = 0 - var/close_delay = -1 //-1 if does not auto close. - - var/hardness = 1 - var/sheetType = /obj/item/stack/sheet/metal - var/sheetAmount = 7 - var/openSound = 'sound/effects/stonedoor_openclose.ogg' - var/closeSound = 'sound/effects/stonedoor_openclose.ogg' - var/damageSound = null - -/obj/structure/mineral_door/New(location) - ..() - initial_state = icon_state - -/obj/structure/mineral_door/Initialize() - ..() - air_update_turf(1) - -/obj/structure/mineral_door/Destroy() - density = 0 - air_update_turf(1) - return ..() - -/obj/structure/mineral_door/Move() - var/turf/T = loc - . = ..() - move_update_air(T) - -/obj/structure/mineral_door/Bumped(atom/user) - ..() - if(!state) - return TryToSwitchState(user) - -/obj/structure/mineral_door/attack_ai(mob/user) //those aren't machinery, they're just big fucking slabs of a mineral - if(isAI(user)) //so the AI can't open it - return - else if(isrobot(user) && Adjacent(user)) //but cyborgs can, but not remotely - return TryToSwitchState(user) - -/obj/structure/mineral_door/attack_hand(mob/user) - return TryToSwitchState(user) - -/obj/structure/mineral_door/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - SwitchState() - -/obj/structure/mineral_door/CanPass(atom/movable/mover, turf/target, height = 0) - if(istype(mover, /obj/effect/beam)) - return !opacity - return !density - -/obj/structure/mineral_door/CanAtmosPass(turf/T) - return !density - -/obj/structure/mineral_door/proc/TryToSwitchState(atom/user) - if(isSwitchingStates) - return - if(isliving(user)) - var/mob/living/M = user - if(world.time - user.last_bumped <= 60) - return //NOTE do we really need that? - if(M.client) - if(iscarbon(M)) - var/mob/living/carbon/C = M - if(!C.handcuffed) - SwitchState() - else - SwitchState() - else if(istype(user, /obj/mecha)) - SwitchState() - -/obj/structure/mineral_door/proc/SwitchState() - if(state) - Close() - else - Open() - -/obj/structure/mineral_door/proc/Open() - isSwitchingStates = 1 - playsound(loc, openSound, 100, 1) - flick("[initial_state]opening",src) - sleep(10) - density = 0 - opacity = 0 - state = 1 - air_update_turf(1) - update_icon() - isSwitchingStates = 0 - - if(close_delay != -1) - spawn(close_delay) - Close() - -/obj/structure/mineral_door/proc/Close() - if(isSwitchingStates || state != 1) - return - var/turf/T = get_turf(src) - for(var/mob/living/L in T) - return - isSwitchingStates = 1 - playsound(loc, closeSound, 100, 1) - flick("[initial_state]closing",src) - sleep(10) - density = 1 - opacity = 1 - state = 0 - air_update_turf(1) - update_icon() - isSwitchingStates = 0 - -/obj/structure/mineral_door/update_icon() - if(state) - icon_state = "[initial_state]open" - else - icon_state = initial_state - -/obj/structure/mineral_door/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pickaxe)) - var/obj/item/pickaxe/digTool = W - to_chat(user, "You start digging \the [src].") - if(do_after(user, 40 * digTool.toolspeed * hardness, target = src) && src) - to_chat(user, "You finished digging.") - deconstruct(TRUE) - else if(user.a_intent != INTENT_HARM) - attack_hand(user) - else - return ..() - -/obj/structure/mineral_door/deconstruct(disassembled = TRUE) - var/turf/T = get_turf(src) - if(sheetType) - if(disassembled) - new sheetType(T, sheetAmount) - else - new sheetType(T, max(sheetAmount - 2, 1)) - qdel(src) - -/obj/structure/mineral_door/iron - max_integrity = 300 - -/obj/structure/mineral_door/silver - name = "silver door" - icon_state = "silver" - sheetType = /obj/item/stack/sheet/mineral/silver - max_integrity = 300 - -/obj/structure/mineral_door/gold - name = "gold door" - icon_state = "gold" - sheetType = /obj/item/stack/sheet/mineral/gold - -/obj/structure/mineral_door/uranium - name = "uranium door" - icon_state = "uranium" - sheetType = /obj/item/stack/sheet/mineral/uranium - max_integrity = 300 - light_range = 2 - -/obj/structure/mineral_door/sandstone - name = "sandstone door" - icon_state = "sandstone" - sheetType = /obj/item/stack/sheet/mineral/sandstone - max_integrity = 100 - -/obj/structure/mineral_door/transparent - opacity = 0 - -/obj/structure/mineral_door/transparent/Close() - ..() - set_opacity(0) - -/obj/structure/mineral_door/transparent/plasma - name = "plasma door" - icon_state = "plasma" - sheetType = /obj/item/stack/sheet/mineral/plasma - -/obj/structure/mineral_door/transparent/plasma/attackby(obj/item/W, mob/user) - if(is_hot(W)) - message_admins("Plasma mineral door ignited by [key_name_admin(user)] in ([x], [y], [z] - JMP)", 0, 1) - log_game("Plasma mineral door ignited by [key_name(user)] in ([x], [y], [z])") - investigate_log("was ignited by [key_name(user)]","atmos") - TemperatureAct(100) - else - return ..() - -/obj/structure/mineral_door/transparent/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 300) - TemperatureAct(exposed_temperature) - -/obj/structure/mineral_door/transparent/plasma/proc/TemperatureAct(temperature) - atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 500) - deconstruct(FALSE) - -/obj/structure/mineral_door/transparent/diamond - name = "diamond door" - icon_state = "diamond" - sheetType = /obj/item/stack/sheet/mineral/diamond - max_integrity = 1000 - -/obj/structure/mineral_door/wood - name = "wood door" - icon_state = "wood" - openSound = 'sound/effects/doorcreaky.ogg' - closeSound = 'sound/effects/doorcreaky.ogg' - sheetType = /obj/item/stack/sheet/wood - hardness = 1 - resistance_flags = FLAMMABLE - max_integrity = 200 - -/obj/structure/mineral_door/resin - name = "resin door" - icon_state = "resin" - hardness = 1.5 - close_delay = 100 - openSound = 'sound/effects/attackblob.ogg' - closeSound = 'sound/effects/attackblob.ogg' - damageSound = 'sound/effects/attackblob.ogg' - sheetType = null - -/obj/structure/mineral_door/resin/TryToSwitchState(atom/user) - if(isalien(user)) - return ..() +//NOT using the existing /obj/machinery/door type, since that has some complications on its own, mainly based on its machineryness +/obj/structure/mineral_door + name = "metal door" + density = 1 + anchored = 1 + opacity = 1 + + icon = 'icons/obj/doors/mineral_doors.dmi' + icon_state = "metal" + max_integrity = 200 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 50, "acid" = 50) + var/initial_state + var/state = 0 //closed, 1 == open + var/isSwitchingStates = 0 + var/close_delay = -1 //-1 if does not auto close. + + var/hardness = 1 + var/sheetType = /obj/item/stack/sheet/metal + var/sheetAmount = 7 + var/openSound = 'sound/effects/stonedoor_openclose.ogg' + var/closeSound = 'sound/effects/stonedoor_openclose.ogg' + var/damageSound = null + +/obj/structure/mineral_door/New(location) + ..() + initial_state = icon_state + +/obj/structure/mineral_door/Initialize() + ..() + air_update_turf(1) + +/obj/structure/mineral_door/Destroy() + density = 0 + air_update_turf(1) + return ..() + +/obj/structure/mineral_door/Move() + var/turf/T = loc + . = ..() + move_update_air(T) + +/obj/structure/mineral_door/Bumped(atom/user) + ..() + if(!state) + return TryToSwitchState(user) + +/obj/structure/mineral_door/attack_ai(mob/user) //those aren't machinery, they're just big fucking slabs of a mineral + if(isAI(user)) //so the AI can't open it + return + else if(isrobot(user) && Adjacent(user)) //but cyborgs can, but not remotely + return TryToSwitchState(user) + +/obj/structure/mineral_door/attack_hand(mob/user) + return TryToSwitchState(user) + +/obj/structure/mineral_door/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + SwitchState() + +/obj/structure/mineral_door/CanPass(atom/movable/mover, turf/target, height = 0) + if(istype(mover, /obj/effect/beam)) + return !opacity + return !density + +/obj/structure/mineral_door/CanAtmosPass(turf/T) + return !density + +/obj/structure/mineral_door/proc/TryToSwitchState(atom/user) + if(isSwitchingStates) + return + if(isliving(user)) + var/mob/living/M = user + if(world.time - user.last_bumped <= 60) + return //NOTE do we really need that? + if(M.client) + if(iscarbon(M)) + var/mob/living/carbon/C = M + if(!C.handcuffed) + SwitchState() + else + SwitchState() + else if(istype(user, /obj/mecha)) + SwitchState() + +/obj/structure/mineral_door/proc/SwitchState() + if(state) + Close() + else + Open() + +/obj/structure/mineral_door/proc/Open() + isSwitchingStates = 1 + playsound(loc, openSound, 100, 1) + flick("[initial_state]opening",src) + sleep(10) + density = 0 + opacity = 0 + state = 1 + air_update_turf(1) + update_icon() + isSwitchingStates = 0 + + if(close_delay != -1) + spawn(close_delay) + Close() + +/obj/structure/mineral_door/proc/Close() + if(isSwitchingStates || state != 1) + return + var/turf/T = get_turf(src) + for(var/mob/living/L in T) + return + isSwitchingStates = 1 + playsound(loc, closeSound, 100, 1) + flick("[initial_state]closing",src) + sleep(10) + density = 1 + opacity = 1 + state = 0 + air_update_turf(1) + update_icon() + isSwitchingStates = 0 + +/obj/structure/mineral_door/update_icon() + if(state) + icon_state = "[initial_state]open" + else + icon_state = initial_state + +/obj/structure/mineral_door/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pickaxe)) + var/obj/item/pickaxe/digTool = W + to_chat(user, "You start digging \the [src].") + if(do_after(user, 40 * digTool.toolspeed * hardness, target = src) && src) + to_chat(user, "You finished digging.") + deconstruct(TRUE) + else if(user.a_intent != INTENT_HARM) + attack_hand(user) + else + return ..() + +/obj/structure/mineral_door/deconstruct(disassembled = TRUE) + var/turf/T = get_turf(src) + if(sheetType) + if(disassembled) + new sheetType(T, sheetAmount) + else + new sheetType(T, max(sheetAmount - 2, 1)) + qdel(src) + +/obj/structure/mineral_door/iron + max_integrity = 300 + +/obj/structure/mineral_door/silver + name = "silver door" + icon_state = "silver" + sheetType = /obj/item/stack/sheet/mineral/silver + max_integrity = 300 + +/obj/structure/mineral_door/gold + name = "gold door" + icon_state = "gold" + sheetType = /obj/item/stack/sheet/mineral/gold + +/obj/structure/mineral_door/uranium + name = "uranium door" + icon_state = "uranium" + sheetType = /obj/item/stack/sheet/mineral/uranium + max_integrity = 300 + light_range = 2 + +/obj/structure/mineral_door/sandstone + name = "sandstone door" + icon_state = "sandstone" + sheetType = /obj/item/stack/sheet/mineral/sandstone + max_integrity = 100 + +/obj/structure/mineral_door/transparent + opacity = 0 + +/obj/structure/mineral_door/transparent/Close() + ..() + set_opacity(0) + +/obj/structure/mineral_door/transparent/plasma + name = "plasma door" + icon_state = "plasma" + sheetType = /obj/item/stack/sheet/mineral/plasma + +/obj/structure/mineral_door/transparent/plasma/attackby(obj/item/W, mob/user) + if(is_hot(W)) + message_admins("Plasma mineral door ignited by [key_name_admin(user)] in ([x], [y], [z] - JMP)", 0, 1) + log_game("Plasma mineral door ignited by [key_name(user)] in ([x], [y], [z])") + investigate_log("was ignited by [key_name(user)]","atmos") + TemperatureAct(100) + else + return ..() + +/obj/structure/mineral_door/transparent/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 300) + TemperatureAct(exposed_temperature) + +/obj/structure/mineral_door/transparent/plasma/proc/TemperatureAct(temperature) + atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 500) + deconstruct(FALSE) + +/obj/structure/mineral_door/transparent/diamond + name = "diamond door" + icon_state = "diamond" + sheetType = /obj/item/stack/sheet/mineral/diamond + max_integrity = 1000 + +/obj/structure/mineral_door/wood + name = "wood door" + icon_state = "wood" + openSound = 'sound/effects/doorcreaky.ogg' + closeSound = 'sound/effects/doorcreaky.ogg' + sheetType = /obj/item/stack/sheet/wood + hardness = 1 + resistance_flags = FLAMMABLE + max_integrity = 200 + +/obj/structure/mineral_door/resin + name = "resin door" + icon_state = "resin" + hardness = 1.5 + close_delay = 100 + openSound = 'sound/effects/attackblob.ogg' + closeSound = 'sound/effects/attackblob.ogg' + damageSound = 'sound/effects/attackblob.ogg' + sheetType = null + +/obj/structure/mineral_door/resin/TryToSwitchState(atom/user) + if(isalien(user)) + return ..() diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index 6a34c39a94dd..aae2295e18f4 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -1,163 +1,163 @@ -//wip wip wup -/obj/structure/mirror - name = "mirror" - desc = "Mirror mirror on the wall, who's the most robust of them all?" - icon = 'icons/obj/watercloset.dmi' - icon_state = "mirror" - density = 0 - anchored = 1 - max_integrity = 200 - integrity_failure = 100 - var/list/ui_users = list() - -/obj/structure/mirror/New(turf/T, newdir = SOUTH, building = FALSE) - ..() - if(building) - switch(newdir) - if(NORTH) - pixel_y = -32 - if(SOUTH) - pixel_y = 32 - if(EAST) - pixel_x = -32 - if(WEST) - pixel_x = 32 - -/obj/structure/mirror/attack_hand(mob/user) - if(broken) - return - - if(ishuman(user)) - var/datum/nano_module/appearance_changer/AC = ui_users[user] - if(!AC) - AC = new(src, user) - AC.name = "SalonPro Nano-Mirror™" - AC.flags = APPEARANCE_ALL_BODY - ui_users[user] = AC - AC.ui_interact(user) - -/obj/structure/mirror/obj_break(damage_flag, mapload) - if(!broken && !(flags & NODECONSTRUCT)) - icon_state = "mirror_broke" - if(!mapload) - playsound(src, "shatter", 70, TRUE) - if(desc == initial(desc)) - desc = "Oh no, seven years of bad luck!" - broken = TRUE - -/obj/structure/mirror/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - user.visible_message("[user] begins to unfasten [src].", "You begin to unfasten [src].") - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - if(broken) - user.visible_message("[user] drops the broken shards to the floor.", "You drop the broken shards on the floor.") - new /obj/item/shard(get_turf(user)) - else - user.visible_message("[user] carefully places [src] on the floor.", "You carefully place [src] on the floor.") - new /obj/item/mounted/mirror(get_turf(user)) - qdel(src) - -/obj/structure/mirror/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!disassembled) - new /obj/item/shard( src.loc ) - qdel(src) - -/obj/structure/mirror/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) - if(BURN) - playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) - - -/obj/item/mounted/mirror - name = "mirror" - desc = "Some reflective glass ready to be hung on a wall. Don't break it!" - icon = 'icons/obj/watercloset.dmi' - icon_state = "mirror" - -/obj/item/mounted/mirror/do_build(turf/on_wall, mob/user) - var/obj/structure/mirror/M = new /obj/structure/mirror(get_turf(user), get_dir(on_wall, user), 1) - transfer_prints_to(M, TRUE) - qdel(src) - -/obj/structure/mirror/magic - name = "magic mirror" - icon_state = "magic_mirror" - -/obj/structure/mirror/magic/attack_hand(mob/user) - if(!ishuman(user) || broken) - return - - var/mob/living/carbon/human/H = user - var/choice = input(user, "Something to change?", "Magical Grooming") as null|anything in list("Name", "Body", "Voice") - - switch(choice) - if("Name") - var/newname = copytext(sanitize(input(H, "Who are we again?", "Name change", H.name) as null|text),1,MAX_NAME_LEN) - - if(!newname) - return - H.real_name = newname - H.name = newname - if(H.dna) - H.dna.real_name = newname - if(H.mind) - H.mind.name = newname - - if(newname) - curse(user) - - if("Body") - var/list/race_list = list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin") - if(config.usealienwhitelist) - for(var/Spec in GLOB.whitelisted_species) - if(is_alien_whitelisted(H, Spec)) - race_list += Spec - else - race_list += GLOB.whitelisted_species - - var/datum/nano_module/appearance_changer/AC = ui_users[user] - if(!AC) - AC = new(src, user) - AC.name = "Magic Mirror" - AC.flags = APPEARANCE_ALL - AC.whitelist = race_list - ui_users[user] = AC - AC.ui_interact(user) - - if("Voice") - var/voice_choice = input(user, "Perhaps...", "Voice effects") as null|anything in list("Comic Sans", "Wingdings", "Swedish", "Chav") - var/voice_mutation - switch(voice_choice) - if("Comic Sans") - voice_mutation = COMICBLOCK - if("Wingdings") - voice_mutation = WINGDINGSBLOCK - if("Swedish") - voice_mutation = SWEDEBLOCK - if("Chav") - voice_mutation = CHAVBLOCK - if(voice_mutation) - if(H.dna.GetSEState(voice_mutation)) - H.dna.SetSEState(voice_mutation, FALSE) - genemutcheck(H, voice_mutation, null, MUTCHK_FORCED) - else - H.dna.SetSEState(voice_mutation, TRUE) - genemutcheck(H, voice_mutation, null, MUTCHK_FORCED) - - if(voice_choice) - curse(user) - -/obj/structure/mirror/magic/on_ui_close(mob/user) - curse(user) - -/obj/structure/mirror/magic/attackby(obj/item/I, mob/living/user, params) - return - -/obj/structure/mirror/magic/proc/curse(mob/living/user) - return \ No newline at end of file +//wip wip wup +/obj/structure/mirror + name = "mirror" + desc = "Mirror mirror on the wall, who's the most robust of them all?" + icon = 'icons/obj/watercloset.dmi' + icon_state = "mirror" + density = 0 + anchored = 1 + max_integrity = 200 + integrity_failure = 100 + var/list/ui_users = list() + +/obj/structure/mirror/New(turf/T, newdir = SOUTH, building = FALSE) + ..() + if(building) + switch(newdir) + if(NORTH) + pixel_y = -32 + if(SOUTH) + pixel_y = 32 + if(EAST) + pixel_x = -32 + if(WEST) + pixel_x = 32 + +/obj/structure/mirror/attack_hand(mob/user) + if(broken) + return + + if(ishuman(user)) + var/datum/nano_module/appearance_changer/AC = ui_users[user] + if(!AC) + AC = new(src, user) + AC.name = "SalonPro Nano-Mirror™" + AC.flags = APPEARANCE_ALL_BODY + ui_users[user] = AC + AC.ui_interact(user) + +/obj/structure/mirror/obj_break(damage_flag, mapload) + if(!broken && !(flags & NODECONSTRUCT)) + icon_state = "mirror_broke" + if(!mapload) + playsound(src, "shatter", 70, TRUE) + if(desc == initial(desc)) + desc = "Oh no, seven years of bad luck!" + broken = TRUE + +/obj/structure/mirror/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] begins to unfasten [src].", "You begin to unfasten [src].") + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + if(broken) + user.visible_message("[user] drops the broken shards to the floor.", "You drop the broken shards on the floor.") + new /obj/item/shard(get_turf(user)) + else + user.visible_message("[user] carefully places [src] on the floor.", "You carefully place [src] on the floor.") + new /obj/item/mounted/mirror(get_turf(user)) + qdel(src) + +/obj/structure/mirror/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!disassembled) + new /obj/item/shard( src.loc ) + qdel(src) + +/obj/structure/mirror/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) + if(BURN) + playsound(src, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) + + +/obj/item/mounted/mirror + name = "mirror" + desc = "Some reflective glass ready to be hung on a wall. Don't break it!" + icon = 'icons/obj/watercloset.dmi' + icon_state = "mirror" + +/obj/item/mounted/mirror/do_build(turf/on_wall, mob/user) + var/obj/structure/mirror/M = new /obj/structure/mirror(get_turf(user), get_dir(on_wall, user), 1) + transfer_prints_to(M, TRUE) + qdel(src) + +/obj/structure/mirror/magic + name = "magic mirror" + icon_state = "magic_mirror" + +/obj/structure/mirror/magic/attack_hand(mob/user) + if(!ishuman(user) || broken) + return + + var/mob/living/carbon/human/H = user + var/choice = input(user, "Something to change?", "Magical Grooming") as null|anything in list("Name", "Body", "Voice") + + switch(choice) + if("Name") + var/newname = copytext(sanitize(input(H, "Who are we again?", "Name change", H.name) as null|text),1,MAX_NAME_LEN) + + if(!newname) + return + H.real_name = newname + H.name = newname + if(H.dna) + H.dna.real_name = newname + if(H.mind) + H.mind.name = newname + + if(newname) + curse(user) + + if("Body") + var/list/race_list = list("Human", "Tajaran", "Skrell", "Unathi", "Diona", "Vulpkanin") + if(config.usealienwhitelist) + for(var/Spec in GLOB.whitelisted_species) + if(is_alien_whitelisted(H, Spec)) + race_list += Spec + else + race_list += GLOB.whitelisted_species + + var/datum/nano_module/appearance_changer/AC = ui_users[user] + if(!AC) + AC = new(src, user) + AC.name = "Magic Mirror" + AC.flags = APPEARANCE_ALL + AC.whitelist = race_list + ui_users[user] = AC + AC.ui_interact(user) + + if("Voice") + var/voice_choice = input(user, "Perhaps...", "Voice effects") as null|anything in list("Comic Sans", "Wingdings", "Swedish", "Chav") + var/voice_mutation + switch(voice_choice) + if("Comic Sans") + voice_mutation = GLOB.comicblock + if("Wingdings") + voice_mutation = GLOB.wingdingsblock + if("Swedish") + voice_mutation = GLOB.swedeblock + if("Chav") + voice_mutation = GLOB.chavblock + if(voice_mutation) + if(H.dna.GetSEState(voice_mutation)) + H.dna.SetSEState(voice_mutation, FALSE) + genemutcheck(H, voice_mutation, null, MUTCHK_FORCED) + else + H.dna.SetSEState(voice_mutation, TRUE) + genemutcheck(H, voice_mutation, null, MUTCHK_FORCED) + + if(voice_choice) + curse(user) + +/obj/structure/mirror/magic/on_ui_close(mob/user) + curse(user) + +/obj/structure/mirror/magic/attackby(obj/item/I, mob/living/user, params) + return + +/obj/structure/mirror/magic/proc/curse(mob/living/user) + return diff --git a/code/game/objects/structures/misc.dm b/code/game/objects/structures/misc.dm index b6da99bf6dcf..44cbf6c52c93 100644 --- a/code/game/objects/structures/misc.dm +++ b/code/game/objects/structures/misc.dm @@ -33,7 +33,7 @@ if(user.z != src.z) return user.loc.loc.Exited(user) - user.loc = pick(carplist) // In the future, possibly make specific NinjaTele landmarks, and give him an option to teleport to North/South/East/West of SS13 instead of just hijacking a carpspawn. + user.loc = pick(GLOB.carplist) // In the future, possibly make specific NinjaTele landmarks, and give him an option to teleport to North/South/East/West of SS13 instead of just hijacking a carpspawn. playsound(user.loc, 'sound/effects/phasein.ogg', 25, 1) @@ -120,4 +120,4 @@ icon_state = "boulder1" density = TRUE opacity = TRUE - anchored = TRUE \ No newline at end of file + anchored = TRUE diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index 05c6a66c7330..9f56f3dea253 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -1,37 +1,37 @@ -/obj/structure/mopbucket - desc = "Fill it with water, but don't forget a mop!" - name = "mop bucket" - icon = 'icons/obj/janitor.dmi' - icon_state = "mopbucket" - density = 1 - container_type = OPENCONTAINER - var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite - -/obj/structure/mopbucket/New() - ..() - create_reagents(100) - GLOB.janitorial_equipment += src - -/obj/structure/mopbucket/full/New() - ..() - reagents.add_reagent("water", 100) - -/obj/structure/mopbucket/Destroy() - GLOB.janitorial_equipment -= src - return ..() - -/obj/structure/mopbucket/examine(mob/user) - . = ..() - if(in_range(user, src)) - . += "[bicon(src)] [src] contains [reagents.total_volume] units of water left!" - -/obj/structure/mopbucket/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/mop)) - if(src.reagents.total_volume >= 2) - src.reagents.trans_to(W, 2) - to_chat(user, "You wet the mop") - playsound(src.loc, 'sound/effects/slosh.ogg', 25, 1) - if(src.reagents.total_volume < 1) - to_chat(user, "Out of water!") - return - return ..() \ No newline at end of file +/obj/structure/mopbucket + desc = "Fill it with water, but don't forget a mop!" + name = "mop bucket" + icon = 'icons/obj/janitor.dmi' + icon_state = "mopbucket" + density = 1 + container_type = OPENCONTAINER + var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite + +/obj/structure/mopbucket/New() + ..() + create_reagents(100) + GLOB.janitorial_equipment += src + +/obj/structure/mopbucket/full/New() + ..() + reagents.add_reagent("water", 100) + +/obj/structure/mopbucket/Destroy() + GLOB.janitorial_equipment -= src + return ..() + +/obj/structure/mopbucket/examine(mob/user) + . = ..() + if(in_range(user, src)) + . += "[bicon(src)] [src] contains [reagents.total_volume] units of water left!" + +/obj/structure/mopbucket/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/mop)) + if(src.reagents.total_volume >= 2) + src.reagents.trans_to(W, 2) + to_chat(user, "You wet the mop") + playsound(src.loc, 'sound/effects/slosh.ogg', 25, 1) + if(src.reagents.total_volume < 1) + to_chat(user, "Out of water!") + return + return ..() diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index ddcef6b2396b..1ea1c277af89 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -1,491 +1,491 @@ -/* Morgue stuff - * Contains: - * Morgue - * Morgue trays - * Creamatorium - * Creamatorium trays - */ - -/* - * Morgue - */ - -/obj/structure/morgue - name = "morgue" - desc = "Used to keep bodies in until someone fetches them." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "morgue1" - density = 1 - max_integrity = 400 - dir = EAST - var/obj/structure/m_tray/connected = null - var/list/status_descriptors = list( - "The tray is currently extended.", - "The tray is currently empty.", - "The tray contains an unviable body.", - "The tray contains a body that is responsive to revival techniques.", - "The tray contains something that is not a body.", - "The tray contains a body that might be responsive." - ) - anchored = 1.0 - var/open_sound = 'sound/items/deconstruct.ogg' - -/obj/structure/morgue/Initialize() - . = ..() - update() - -/obj/structure/morgue/proc/update() - if(connected) - icon_state = "morgue0" - desc = initial(desc) + "\n[status_descriptors[1]]" - else - if(contents.len) - - var/mob/living/M = locate() in contents - - var/obj/structure/closet/body_bag/B = locate() in contents - if(M==null) M = locate() in B - - if(M) - var/mob/dead/observer/G = M.get_ghost() - - if(M.client) - icon_state = "morgue3" - desc = initial(desc) + "\n[status_descriptors[4]]" - else if(G && G.client) //There is a ghost and it is connected to the server - icon_state = "morgue5" - desc = initial(desc) + "\n[status_descriptors[6]]" - else - icon_state = "morgue2" - desc = initial(desc) + "\n[status_descriptors[3]]" - - - else - icon_state = "morgue4" - desc = initial(desc) + "\n[status_descriptors[5]]" - else - icon_state = "morgue1" - desc = initial(desc) + "\n[status_descriptors[2]]" - return - - -/obj/structure/morgue/ex_act(severity) - switch(severity) - if(1.0) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(loc) - ex_act(severity) - qdel(src) - return - if(2.0) - if(prob(50)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(loc) - ex_act(severity) - qdel(src) - return - if(3.0) - if(prob(5)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(loc) - ex_act(severity) - qdel(src) - return - return - -/obj/structure/morgue/attack_hand(mob/user as mob) - if(connected) - for(var/atom/movable/A as mob|obj in connected.loc) - if(!( A.anchored )) - A.forceMove(src) - playsound(loc, open_sound, 50, 1) - QDEL_NULL(connected) - else - playsound(loc, open_sound, 50, 1) - connected = new /obj/structure/m_tray( loc ) - step(connected, dir) - connected.layer = OBJ_LAYER - var/turf/T = get_step(src, dir) - if(T.contents.Find(connected)) - connected.connected = src - icon_state = "morgue0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(connected.loc) - connected.icon_state = "morguet" - connected.dir = dir - else - QDEL_NULL(connected) - add_fingerprint(user) - update() - return - -/obj/structure/morgue/attackby(P as obj, mob/user as mob, params) - if(istype(P, /obj/item/pen)) - var/t = input(user, "What would you like the label to be?", text("[]", name), null) as text - if(user.get_active_hand() != P) - return - if((!in_range(src, usr) && loc != user)) - return - t = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - if(t) - name = text("Morgue- '[]'", t) - overlays += image(icon, "morgue_label") - else - name = "Morgue" - overlays.Cut() - add_fingerprint(user) - return - return ..() - -/obj/structure/morgue/relaymove(mob/user as mob) - if(user.stat) - return - connected = new /obj/structure/m_tray( loc ) - step(connected, dir) - connected.layer = OBJ_LAYER - var/turf/T = get_step(src, dir) - if(T.contents.Find(connected)) - connected.connected = src - icon_state = "morgue0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(connected.loc) - connected.icon_state = "morguet" - else - QDEL_NULL(connected) - return - -/obj/structure/morgue/Destroy() - QDEL_NULL(connected) - return ..() - -/obj/structure/morgue/container_resist(var/mob/living/L) - var/mob/living/carbon/CM = L - if(!istype(CM)) - return - if(CM.stat || CM.restrained()) - return - - to_chat(CM, "You attempt to slide yourself out of \the [src]...") - src.attack_hand(CM) - - -/obj/structure/morgue/get_remote_view_fullscreens(mob/user) - if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -/* - * Morgue tray - */ -/obj/structure/m_tray - name = "morgue tray" - desc = "Apply corpse before closing. May float away in no-gravity." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "morguet" - density = 1 - layer = 2.0 - var/obj/structure/morgue/connected = null - anchored = 1.0 - pass_flags = LETPASSTHROW - max_integrity = 350 - - -/obj/structure/m_tray/attack_hand(mob/user as mob) - if(connected) - for(var/atom/movable/A as mob|obj in loc) - if(!( A.anchored )) - A.forceMove(connected) - connected.connected = null - connected.update() - add_fingerprint(user) - qdel(src) - return - return - -/obj/structure/m_tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O))) - return - if(!ismob(O) && !istype(O, /obj/structure/closet/body_bag)) - return - if(!ismob(user) || user.stat || user.lying || user.stunned) - return - O.forceMove(loc) - if(user != O) - user.visible_message("[user] stuffs [O] into [src]!") - return - -/obj/structure/m_tray/Destroy() - if(connected && connected.connected == src) - connected.connected = null - connected = null - return ..() - -/obj/structure/tray/m_tray/CanPass(atom/movable/mover, turf/target, height=0) - if(height == 0) - return 1 - - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - if(locate(/obj/structure/table) in get_turf(mover)) - return 1 - else - return 0 - -/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller) - . = !density - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSTABLE) - -/* - * Crematorium - */ - -/obj/structure/crematorium - name = "crematorium" - desc = "A human incinerator. Works well on barbeque nights." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "crema1" - density = 1 - var/obj/structure/c_tray/connected = null - anchored = 1.0 - var/cremating = 0 - var/id = 1 - var/locked = 0 - var/open_sound = 'sound/items/deconstruct.ogg' - -/obj/structure/crematorium/proc/update() - if(connected) - icon_state = "crema0" - else - if(contents.len) - icon_state = "crema2" - else - icon_state = "crema1" - return - -/obj/structure/crematorium/ex_act(severity) - switch(severity) - if(1.0) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(loc) - ex_act(severity) - qdel(src) - return - if(2.0) - if(prob(50)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(loc) - ex_act(severity) - qdel(src) - return - if(3.0) - if(prob(5)) - for(var/atom/movable/A as mob|obj in src) - A.forceMove(loc) - ex_act(severity) - qdel(src) - return - return - -/obj/structure/crematorium/attack_hand(mob/user as mob) - if(cremating) - to_chat(usr, "It's locked.") - return - if((connected) && (locked == 0)) - for(var/atom/movable/A as mob|obj in connected.loc) - if(!( A.anchored )) - A.forceMove(src) - playsound(loc, open_sound, 50, 1) - QDEL_NULL(connected) - else if(locked == 0) - playsound(loc, open_sound, 50, 1) - connected = new /obj/structure/c_tray( loc ) - step(connected, SOUTH) - connected.layer = OBJ_LAYER - var/turf/T = get_step(src, SOUTH) - if(T.contents.Find(connected)) - connected.connected = src - icon_state = "crema0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(connected.loc) - connected.icon_state = "cremat" - else - QDEL_NULL(connected) - add_fingerprint(user) - update() - -/obj/structure/crematorium/attackby(P as obj, mob/user as mob, params) - if(istype(P, /obj/item/pen)) - var/t = input(user, "What would you like the label to be?", text("[]", name), null) as text - if(user.get_active_hand() != P) - return - if((!in_range(src, usr) > 1 && loc != user)) - return - t = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - if(t) - name = text("Crematorium- '[]'", t) - else - name = "Crematorium" - add_fingerprint(user) - return - return ..() - -/obj/structure/crematorium/relaymove(mob/user as mob) - if(user.stat || locked) - return - connected = new /obj/structure/c_tray( loc ) - step(connected, SOUTH) - connected.layer = OBJ_LAYER - var/turf/T = get_step(src, SOUTH) - if(T.contents.Find(connected)) - connected.connected = src - icon_state = "crema0" - for(var/atom/movable/A as mob|obj in src) - A.forceMove(connected.loc) - connected.icon_state = "cremat" - else - QDEL_NULL(connected) - return - -/obj/structure/crematorium/proc/cremate(mob/user as mob) - if(cremating) - return //don't let you cremate something twice or w/e - - if(contents.len <= 0) - for(var/mob/M in viewers(src)) - M.show_message("You hear a hollow crackle.", 1) - return - - else - for(var/mob/M in viewers(src)) - M.show_message("You hear a roar as the crematorium activates.", 1) - - cremating = 1 - locked = 1 - icon_state = "crema_active" - - for(var/mob/living/M in search_contents_for(/mob/living)) - if(QDELETED(M)) - continue - if(M.stat!=2) - M.emote("scream") - if(istype(user)) - add_attack_logs(user, M, "Cremated") - M.death(1) - if(QDELETED(M)) - continue // Re-check for mobs that delete themselves on death - M.ghostize() - qdel(M) - - for(var/obj/O in contents) //obj instead of obj/item so that bodybags and ashes get destroyed. We dont want tons and tons of ash piling up - qdel(O) - - new /obj/effect/decal/cleanable/ash(src) - sleep(30) - cremating = 0 - locked = 0 - update() - playsound(loc, 'sound/machines/ding.ogg', 50, 1) - return - -/obj/structure/crematorium/Destroy() - QDEL_NULL(connected) - return ..() - -/obj/structure/crematorium/container_resist(var/mob/living/L) - var/mob/living/carbon/CM = L - if(!istype(CM)) - return - if(CM.stat || CM.restrained()) - return - - to_chat(CM, "You attempt to slide yourself out of \the [src]...") - src.attack_hand(CM) - -/obj/structure/crematorium/get_remote_view_fullscreens(mob/user) - if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -/* - * Crematorium tray - */ -/obj/structure/c_tray - name = "crematorium tray" - desc = "Apply body before burning." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "cremat" - density = 1 - layer = 2.0 - var/obj/structure/crematorium/connected = null - anchored = 1.0 - pass_flags = LETPASSTHROW - -/obj/structure/c_tray/attack_hand(mob/user as mob) - if(connected) - for(var/atom/movable/A as mob|obj in loc) - if(!( A.anchored )) - A.forceMove(connected) - //Foreach goto(26) - connected.connected = null - connected.update() - add_fingerprint(user) - qdel(src) - return - return - -/obj/structure/c_tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O))) - return - if(!ismob(O) && !istype(O, /obj/structure/closet/body_bag)) - return - if(!ismob(user) || user.stat || user.lying || user.stunned) - return - O.forceMove(loc) - if(user != O) - user.visible_message("[user] stuffs [O] into [src]!") - //Foreach goto(99) - return - -/obj/structure/c_tray/Destroy() - if(connected && connected.connected == src) - connected.connected = null - connected = null - return ..() - -// Crematorium switch -/obj/machinery/crema_switch - desc = "Burn baby burn!" - name = "crematorium igniter" - icon = 'icons/obj/power.dmi' - icon_state = "crema_switch" - anchored = 1.0 - req_access = list(ACCESS_CREMATORIUM) - var/on = 0 - var/area/area = null - var/otherarea = null - var/id = 1 - -/obj/machinery/crema_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/crema_switch/attack_hand(mob/user) - if(allowed(usr) || user.can_advanced_admin_interact()) - for(var/obj/structure/crematorium/C in world) - if(C.id == id) - if(!C.cremating) - C.cremate(user) - else - to_chat(usr, "Access denied.") - -/mob/proc/update_morgue() - if(stat == DEAD) - var/obj/structure/morgue/morgue - var/mob/living/C = src - var/mob/dead/observer/G = src - if(istype(G) && G.can_reenter_corpse && G.mind) //We're a ghost, let's find our corpse - C = G.mind.current - if(istype(C)) //We found our corpse, is it inside a morgue? - morgue = get(C.loc, /obj/structure/morgue) - if(morgue) - morgue.update() \ No newline at end of file +/* Morgue stuff + * Contains: + * Morgue + * Morgue trays + * Creamatorium + * Creamatorium trays + */ + +/* + * Morgue + */ + +/obj/structure/morgue + name = "morgue" + desc = "Used to keep bodies in until someone fetches them." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "morgue1" + density = 1 + max_integrity = 400 + dir = EAST + var/obj/structure/m_tray/connected = null + var/list/status_descriptors = list( + "The tray is currently extended.", + "The tray is currently empty.", + "The tray contains an unviable body.", + "The tray contains a body that is responsive to revival techniques.", + "The tray contains something that is not a body.", + "The tray contains a body that might be responsive." + ) + anchored = 1.0 + var/open_sound = 'sound/items/deconstruct.ogg' + +/obj/structure/morgue/Initialize() + . = ..() + update() + +/obj/structure/morgue/proc/update() + if(connected) + icon_state = "morgue0" + desc = initial(desc) + "\n[status_descriptors[1]]" + else + if(contents.len) + + var/mob/living/M = locate() in contents + + var/obj/structure/closet/body_bag/B = locate() in contents + if(M==null) M = locate() in B + + if(M) + var/mob/dead/observer/G = M.get_ghost() + + if(M.client) + icon_state = "morgue3" + desc = initial(desc) + "\n[status_descriptors[4]]" + else if(G && G.client) //There is a ghost and it is connected to the server + icon_state = "morgue5" + desc = initial(desc) + "\n[status_descriptors[6]]" + else + icon_state = "morgue2" + desc = initial(desc) + "\n[status_descriptors[3]]" + + + else + icon_state = "morgue4" + desc = initial(desc) + "\n[status_descriptors[5]]" + else + icon_state = "morgue1" + desc = initial(desc) + "\n[status_descriptors[2]]" + return + + +/obj/structure/morgue/ex_act(severity) + switch(severity) + if(1.0) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(loc) + ex_act(severity) + qdel(src) + return + if(2.0) + if(prob(50)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(loc) + ex_act(severity) + qdel(src) + return + if(3.0) + if(prob(5)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(loc) + ex_act(severity) + qdel(src) + return + return + +/obj/structure/morgue/attack_hand(mob/user as mob) + if(connected) + for(var/atom/movable/A as mob|obj in connected.loc) + if(!( A.anchored )) + A.forceMove(src) + playsound(loc, open_sound, 50, 1) + QDEL_NULL(connected) + else + playsound(loc, open_sound, 50, 1) + connected = new /obj/structure/m_tray( loc ) + step(connected, dir) + connected.layer = OBJ_LAYER + var/turf/T = get_step(src, dir) + if(T.contents.Find(connected)) + connected.connected = src + icon_state = "morgue0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(connected.loc) + connected.icon_state = "morguet" + connected.dir = dir + else + QDEL_NULL(connected) + add_fingerprint(user) + update() + return + +/obj/structure/morgue/attackby(P as obj, mob/user as mob, params) + if(istype(P, /obj/item/pen)) + var/t = input(user, "What would you like the label to be?", text("[]", name), null) as text + if(user.get_active_hand() != P) + return + if((!in_range(src, usr) && loc != user)) + return + t = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if(t) + name = text("Morgue- '[]'", t) + overlays += image(icon, "morgue_label") + else + name = "Morgue" + overlays.Cut() + add_fingerprint(user) + return + return ..() + +/obj/structure/morgue/relaymove(mob/user as mob) + if(user.stat) + return + connected = new /obj/structure/m_tray( loc ) + step(connected, dir) + connected.layer = OBJ_LAYER + var/turf/T = get_step(src, dir) + if(T.contents.Find(connected)) + connected.connected = src + icon_state = "morgue0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(connected.loc) + connected.icon_state = "morguet" + else + QDEL_NULL(connected) + return + +/obj/structure/morgue/Destroy() + QDEL_NULL(connected) + return ..() + +/obj/structure/morgue/container_resist(var/mob/living/L) + var/mob/living/carbon/CM = L + if(!istype(CM)) + return + if(CM.stat || CM.restrained()) + return + + to_chat(CM, "You attempt to slide yourself out of \the [src]...") + src.attack_hand(CM) + + +/obj/structure/morgue/get_remote_view_fullscreens(mob/user) + if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +/* + * Morgue tray + */ +/obj/structure/m_tray + name = "morgue tray" + desc = "Apply corpse before closing. May float away in no-gravity." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "morguet" + density = 1 + layer = 2.0 + var/obj/structure/morgue/connected = null + anchored = 1.0 + pass_flags = LETPASSTHROW + max_integrity = 350 + + +/obj/structure/m_tray/attack_hand(mob/user as mob) + if(connected) + for(var/atom/movable/A as mob|obj in loc) + if(!( A.anchored )) + A.forceMove(connected) + connected.connected = null + connected.update() + add_fingerprint(user) + qdel(src) + return + return + +/obj/structure/m_tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O))) + return + if(!ismob(O) && !istype(O, /obj/structure/closet/body_bag)) + return + if(!ismob(user) || user.stat || user.lying || user.stunned) + return + O.forceMove(loc) + if(user != O) + user.visible_message("[user] stuffs [O] into [src]!") + return + +/obj/structure/m_tray/Destroy() + if(connected && connected.connected == src) + connected.connected = null + connected = null + return ..() + +/obj/structure/tray/m_tray/CanPass(atom/movable/mover, turf/target, height=0) + if(height == 0) + return 1 + + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + if(locate(/obj/structure/table) in get_turf(mover)) + return 1 + else + return 0 + +/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller) + . = !density + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSTABLE) + +/* + * Crematorium + */ + +/obj/structure/crematorium + name = "crematorium" + desc = "A human incinerator. Works well on barbeque nights." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "crema1" + density = 1 + var/obj/structure/c_tray/connected = null + anchored = 1.0 + var/cremating = 0 + var/id = 1 + var/locked = 0 + var/open_sound = 'sound/items/deconstruct.ogg' + +/obj/structure/crematorium/proc/update() + if(connected) + icon_state = "crema0" + else + if(contents.len) + icon_state = "crema2" + else + icon_state = "crema1" + return + +/obj/structure/crematorium/ex_act(severity) + switch(severity) + if(1.0) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(loc) + ex_act(severity) + qdel(src) + return + if(2.0) + if(prob(50)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(loc) + ex_act(severity) + qdel(src) + return + if(3.0) + if(prob(5)) + for(var/atom/movable/A as mob|obj in src) + A.forceMove(loc) + ex_act(severity) + qdel(src) + return + return + +/obj/structure/crematorium/attack_hand(mob/user as mob) + if(cremating) + to_chat(usr, "It's locked.") + return + if((connected) && (locked == 0)) + for(var/atom/movable/A as mob|obj in connected.loc) + if(!( A.anchored )) + A.forceMove(src) + playsound(loc, open_sound, 50, 1) + QDEL_NULL(connected) + else if(locked == 0) + playsound(loc, open_sound, 50, 1) + connected = new /obj/structure/c_tray( loc ) + step(connected, SOUTH) + connected.layer = OBJ_LAYER + var/turf/T = get_step(src, SOUTH) + if(T.contents.Find(connected)) + connected.connected = src + icon_state = "crema0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(connected.loc) + connected.icon_state = "cremat" + else + QDEL_NULL(connected) + add_fingerprint(user) + update() + +/obj/structure/crematorium/attackby(P as obj, mob/user as mob, params) + if(istype(P, /obj/item/pen)) + var/t = input(user, "What would you like the label to be?", text("[]", name), null) as text + if(user.get_active_hand() != P) + return + if((!in_range(src, usr) > 1 && loc != user)) + return + t = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if(t) + name = text("Crematorium- '[]'", t) + else + name = "Crematorium" + add_fingerprint(user) + return + return ..() + +/obj/structure/crematorium/relaymove(mob/user as mob) + if(user.stat || locked) + return + connected = new /obj/structure/c_tray( loc ) + step(connected, SOUTH) + connected.layer = OBJ_LAYER + var/turf/T = get_step(src, SOUTH) + if(T.contents.Find(connected)) + connected.connected = src + icon_state = "crema0" + for(var/atom/movable/A as mob|obj in src) + A.forceMove(connected.loc) + connected.icon_state = "cremat" + else + QDEL_NULL(connected) + return + +/obj/structure/crematorium/proc/cremate(mob/user as mob) + if(cremating) + return //don't let you cremate something twice or w/e + + if(contents.len <= 0) + for(var/mob/M in viewers(src)) + M.show_message("You hear a hollow crackle.", 1) + return + + else + for(var/mob/M in viewers(src)) + M.show_message("You hear a roar as the crematorium activates.", 1) + + cremating = 1 + locked = 1 + icon_state = "crema_active" + + for(var/mob/living/M in search_contents_for(/mob/living)) + if(QDELETED(M)) + continue + if(M.stat!=2) + M.emote("scream") + if(istype(user)) + add_attack_logs(user, M, "Cremated") + M.death(1) + if(QDELETED(M)) + continue // Re-check for mobs that delete themselves on death + M.ghostize() + qdel(M) + + for(var/obj/O in contents) //obj instead of obj/item so that bodybags and ashes get destroyed. We dont want tons and tons of ash piling up + qdel(O) + + new /obj/effect/decal/cleanable/ash(src) + sleep(30) + cremating = 0 + locked = 0 + update() + playsound(loc, 'sound/machines/ding.ogg', 50, 1) + return + +/obj/structure/crematorium/Destroy() + QDEL_NULL(connected) + return ..() + +/obj/structure/crematorium/container_resist(var/mob/living/L) + var/mob/living/carbon/CM = L + if(!istype(CM)) + return + if(CM.stat || CM.restrained()) + return + + to_chat(CM, "You attempt to slide yourself out of \the [src]...") + src.attack_hand(CM) + +/obj/structure/crematorium/get_remote_view_fullscreens(mob/user) + if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +/* + * Crematorium tray + */ +/obj/structure/c_tray + name = "crematorium tray" + desc = "Apply body before burning." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "cremat" + density = 1 + layer = 2.0 + var/obj/structure/crematorium/connected = null + anchored = 1.0 + pass_flags = LETPASSTHROW + +/obj/structure/c_tray/attack_hand(mob/user as mob) + if(connected) + for(var/atom/movable/A as mob|obj in loc) + if(!( A.anchored )) + A.forceMove(connected) + //Foreach goto(26) + connected.connected = null + connected.update() + add_fingerprint(user) + qdel(src) + return + return + +/obj/structure/c_tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if((!( istype(O, /atom/movable) ) || O.anchored || get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src) || user.contents.Find(O))) + return + if(!ismob(O) && !istype(O, /obj/structure/closet/body_bag)) + return + if(!ismob(user) || user.stat || user.lying || user.stunned) + return + O.forceMove(loc) + if(user != O) + user.visible_message("[user] stuffs [O] into [src]!") + //Foreach goto(99) + return + +/obj/structure/c_tray/Destroy() + if(connected && connected.connected == src) + connected.connected = null + connected = null + return ..() + +// Crematorium switch +/obj/machinery/crema_switch + desc = "Burn baby burn!" + name = "crematorium igniter" + icon = 'icons/obj/power.dmi' + icon_state = "crema_switch" + anchored = 1.0 + req_access = list(ACCESS_CREMATORIUM) + var/on = 0 + var/area/area = null + var/otherarea = null + var/id = 1 + +/obj/machinery/crema_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/crema_switch/attack_hand(mob/user) + if(allowed(usr) || user.can_advanced_admin_interact()) + for(var/obj/structure/crematorium/C in world) + if(C.id == id) + if(!C.cremating) + C.cremate(user) + else + to_chat(usr, "Access denied.") + +/mob/proc/update_morgue() + if(stat == DEAD) + var/obj/structure/morgue/morgue + var/mob/living/C = src + var/mob/dead/observer/G = src + if(istype(G) && G.can_reenter_corpse && G.mind) //We're a ghost, let's find our corpse + C = G.mind.current + if(istype(C)) //We found our corpse, is it inside a morgue? + morgue = get(C.loc, /obj/structure/morgue) + if(morgue) + morgue.update() diff --git a/code/game/objects/structures/musician.dm b/code/game/objects/structures/musician.dm index 8c025b63a97c..c3d63c47862d 100644 --- a/code/game/objects/structures/musician.dm +++ b/code/game/objects/structures/musician.dm @@ -1,341 +1,341 @@ - - -/datum/song - var/name = "Untitled" - var/list/lines = new() - var/tempo = 5 // delay between notes - - var/playing = 0 // if we're playing - var/help = 0 // if help is open - var/repeat = 0 // number of times remaining to repeat - var/max_repeat = 10 // maximum times we can repeat - - var/instrumentDir = "piano" // the folder with the sounds - var/instrumentExt = "ogg" // the file extension - var/obj/instrumentObj = null // the associated obj playing the sound - -/datum/song/New(dir, obj, ext = "ogg") - tempo = sanitize_tempo(tempo) - instrumentDir = dir - instrumentObj = obj - instrumentExt = ext - -/datum/song/Destroy() - instrumentObj = null - return ..() - -// note is a number from 1-7 for A-G -// acc is either "b", "n", or "#" -// oct is 1-8 (or 9 for C) -/datum/song/proc/playnote(note, acc as text, oct) - // handle accidental -> B<>C of E<>F - if(acc == "b" && (note == 3 || note == 6)) // C or F - if(note == 3) - oct-- - note-- - acc = "n" - else if(acc == "#" && (note == 2 || note == 5)) // B or E - if(note == 2) - oct++ - note++ - acc = "n" - else if(acc == "#" && (note == 7)) //G# - note = 1 - acc = "b" - else if(acc == "#") // mass convert all sharps to flats, octave jump already handled - acc = "b" - note++ - - // check octave, C is allowed to go to 9 - if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) - return - - // now generate name - var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]" - soundfile = file(soundfile) - // make sure the note exists - if(!fexists(soundfile)) - return - // and play - var/turf/source = get_turf(instrumentObj) - var/sound/music_played = sound(soundfile) - for(var/A in hearers(15, source)) - var/mob/M = A - if(!M.client || !(M.client.prefs.sound & SOUND_INSTRUMENTS)) - continue - M.playsound_local(source, null, 100, falloff = 5, S = music_played) - -/datum/song/proc/shouldStopPlaying(mob/user) - if(instrumentObj) - //if(!user.canUseTopic(instrumentObj)) - //return 1 - return !instrumentObj.anchored // add special cases to stop in subclasses - else - return 1 - -/datum/song/proc/playsong(mob/user) - while(repeat >= 0) - var/cur_oct[7] - var/cur_acc[7] - for(var/i = 1 to 7) - cur_oct[i] = 3 - cur_acc[i] = "n" - - for(var/line in lines) - for(var/beat in splittext(lowertext(line), ",")) - var/list/notes = splittext(beat, "/") - for(var/note in splittext(notes[1], "-")) - if(!playing || shouldStopPlaying(user)) //If the instrument is playing, or special case - playing = 0 - return - if(length(note) == 0) - continue - var/cur_note = text2ascii(note) - 96 - if(cur_note < 1 || cur_note > 7) - continue - for(var/i=2 to length(note)) - var/ni = copytext(note,i,i+1) - if(!text2num(ni)) - if(ni == "#" || ni == "b" || ni == "n") - cur_acc[cur_note] = ni - else if(ni == "s") - cur_acc[cur_note] = "#" // so shift is never required - else - cur_oct[cur_note] = text2num(ni) - playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note]) - if(notes.len >= 2 && text2num(notes[2])) - sleep(sanitize_tempo(tempo / text2num(notes[2]))) - else - sleep(tempo) - repeat-- - playing = 0 - repeat = 0 - -/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!instrumentObj) - return - - ui = SSnanoui.try_update_ui(user, instrumentObj, ui_key, ui, force_open) - if(!ui) - ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500) - ui.open() - ui.set_auto_update(1) - -/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["lines"] = lines - data["tempo"] = tempo - - data["playing"] = playing - data["help"] = help - data["repeat"] = repeat - data["maxRepeat"] = max_repeat - data["minTempo"] = world.tick_lag - data["maxTempo"] = 600 - - return data - -/datum/song/Topic(href, href_list) - if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || usr.incapacitated()) - usr << browse(null, "window=instrument") - usr.unset_machine() - return 1 - - instrumentObj.add_fingerprint(usr) - - if(href_list["newsong"]) - playing = 0 - lines = new() - tempo = sanitize_tempo(5) // default 120 BPM - name = "" - SSnanoui.update_uis(src) - - else if(href_list["import"]) - playing = 0 - var/t = "" - do - t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) - if(!in_range(instrumentObj, usr)) - return - - if(length(t) >= 12000) - var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") - if(cont == "no") - break - while(length(t) > 12000) - - //split into lines - spawn() - lines = splittext(t, "\n") - if(lines.len == 0) - return 1 - if(copytext(lines[1],1,6) == "BPM: ") - tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6))) - lines.Cut(1,2) - else - tempo = sanitize_tempo(5) // default 120 BPM - if(lines.len > 200) - to_chat(usr, "Too many lines!") - lines.Cut(201) - var/linenum = 1 - for(var/l in lines) - if(length(l) > 200) - to_chat(usr, "Line [linenum] too long!") - lines.Remove(l) - else - linenum++ - SSnanoui.update_uis(src) - - else if(href_list["help"]) - help = !help - SSnanoui.update_uis(src) - - if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. - if(playing) - return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. - repeat += round(text2num(href_list["repeat"])) - if(repeat < 0) - repeat = 0 - if(repeat > max_repeat) - repeat = max_repeat - SSnanoui.update_uis(src) - - else if(href_list["tempo"]) - tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag) - SSnanoui.update_uis(src) - - else if(href_list["play"]) - if(playing) - return - playing = 1 - spawn() - playsong(usr) - SSnanoui.update_uis(src) - - else if(href_list["insertline"]) - var/num = round(text2num(href_list["insertline"])) - if(num < 1 || num > lines.len + 1) - return - - var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) - if(!newline || !in_range(instrumentObj, usr)) - return - if(lines.len > 200) - return - if(length(newline) > 200) - newline = copytext(newline, 1, 200) - - lines.Insert(num, newline) - SSnanoui.update_uis(src) - - else if(href_list["deleteline"]) - var/num = round(text2num(href_list["deleteline"])) - if(num > lines.len || num < 1) - return - lines.Cut(num, num + 1) - SSnanoui.update_uis(src) - - else if(href_list["modifyline"]) - var/num = round(text2num(href_list["modifyline"])) - var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null) - if(!content || !in_range(instrumentObj, usr)) - return - if(length(content) > 200) - content = copytext(content, 1, 200) - if(num > lines.len || num < 1) - return - lines[num] = content - SSnanoui.update_uis(src) - - else if(href_list["stop"]) - playing = 0 - SSnanoui.update_uis(src) - -/datum/song/proc/sanitize_tempo(new_tempo) - new_tempo = abs(new_tempo) - return max(round(new_tempo, world.tick_lag), world.tick_lag) - -// subclass for handheld instruments, like violin -/datum/song/handheld - -/datum/song/handheld/shouldStopPlaying() - if(instrumentObj) - return !isliving(instrumentObj.loc) - else - return 1 - - -////////////////////////////////////////////////////////////////////////// - - -/obj/structure/piano - name = "space minimoog" - icon = 'icons/obj/musician.dmi' - icon_state = "minimoog" - anchored = 1 - density = 1 - var/datum/song/song - - -/obj/structure/piano/New() - ..() - song = new("piano", src) - - if(prob(50)) - name = "space minimoog" - desc = "This is a minimoog, like a space piano, but more spacey!" - icon_state = "minimoog" - else - name = "space piano" - desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." - icon_state = "piano" - -/obj/structure/piano/Destroy() - QDEL_NULL(song) - return ..() - -/obj/structure/piano/Initialize() - if(song) - song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded - ..() - -/obj/structure/piano/attack_hand(mob/user as mob) - ui_interact(user) - -/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!isliving(user) || user.incapacitated() || !anchored) - return - - song.ui_interact(user, ui_key, ui, force_open) - -/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - return song.ui_data(user, ui_key, state) - -/obj/structure/piano/Topic(href, href_list) - song.Topic(href, href_list) - -/obj/structure/piano/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(!anchored && !isinspace()) - WRENCH_ANCHOR_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - user.visible_message( \ - "[user] tightens [src]'s casters.", \ - " You have tightened [src]'s casters. Now it can be played again.", \ - "You hear ratchet.") - anchored = TRUE - else if(anchored) - to_chat(user, " You begin to loosen [src]'s casters...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume)) - return - user.visible_message( \ - "[user] loosens [src]'s casters.", \ - " You have loosened [src]. Now it can be pulled somewhere else.", \ - "You hear ratchet.") - anchored = FALSE - else - to_chat(user, "[src] needs to be bolted to the floor!") + + +/datum/song + var/name = "Untitled" + var/list/lines = new() + var/tempo = 5 // delay between notes + + var/playing = 0 // if we're playing + var/help = 0 // if help is open + var/repeat = 0 // number of times remaining to repeat + var/max_repeat = 10 // maximum times we can repeat + + var/instrumentDir = "piano" // the folder with the sounds + var/instrumentExt = "ogg" // the file extension + var/obj/instrumentObj = null // the associated obj playing the sound + +/datum/song/New(dir, obj, ext = "ogg") + tempo = sanitize_tempo(tempo) + instrumentDir = dir + instrumentObj = obj + instrumentExt = ext + +/datum/song/Destroy() + instrumentObj = null + return ..() + +// note is a number from 1-7 for A-G +// acc is either "b", "n", or "#" +// oct is 1-8 (or 9 for C) +/datum/song/proc/playnote(note, acc as text, oct) + // handle accidental -> B<>C of E<>F + if(acc == "b" && (note == 3 || note == 6)) // C or F + if(note == 3) + oct-- + note-- + acc = "n" + else if(acc == "#" && (note == 2 || note == 5)) // B or E + if(note == 2) + oct++ + note++ + acc = "n" + else if(acc == "#" && (note == 7)) //G# + note = 1 + acc = "b" + else if(acc == "#") // mass convert all sharps to flats, octave jump already handled + acc = "b" + note++ + + // check octave, C is allowed to go to 9 + if(oct < 1 || (note == 3 ? oct > 9 : oct > 8)) + return + + // now generate name + var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]" + soundfile = file(soundfile) + // make sure the note exists + if(!fexists(soundfile)) + return + // and play + var/turf/source = get_turf(instrumentObj) + var/sound/music_played = sound(soundfile) + for(var/A in hearers(15, source)) + var/mob/M = A + if(!M.client || !(M.client.prefs.sound & SOUND_INSTRUMENTS)) + continue + M.playsound_local(source, null, 100, falloff = 5, S = music_played) + +/datum/song/proc/shouldStopPlaying(mob/user) + if(instrumentObj) + //if(!user.canUseTopic(instrumentObj)) + //return 1 + return !instrumentObj.anchored // add special cases to stop in subclasses + else + return 1 + +/datum/song/proc/playsong(mob/user) + while(repeat >= 0) + var/cur_oct[7] + var/cur_acc[7] + for(var/i = 1 to 7) + cur_oct[i] = 3 + cur_acc[i] = "n" + + for(var/line in lines) + for(var/beat in splittext(lowertext(line), ",")) + var/list/notes = splittext(beat, "/") + for(var/note in splittext(notes[1], "-")) + if(!playing || shouldStopPlaying(user)) //If the instrument is playing, or special case + playing = 0 + return + if(length(note) == 0) + continue + var/cur_note = text2ascii(note) - 96 + if(cur_note < 1 || cur_note > 7) + continue + for(var/i=2 to length(note)) + var/ni = copytext(note,i,i+1) + if(!text2num(ni)) + if(ni == "#" || ni == "b" || ni == "n") + cur_acc[cur_note] = ni + else if(ni == "s") + cur_acc[cur_note] = "#" // so shift is never required + else + cur_oct[cur_note] = text2num(ni) + playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note]) + if(notes.len >= 2 && text2num(notes[2])) + sleep(sanitize_tempo(tempo / text2num(notes[2]))) + else + sleep(tempo) + repeat-- + playing = 0 + repeat = 0 + +/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!instrumentObj) + return + + ui = SSnanoui.try_update_ui(user, instrumentObj, ui_key, ui, force_open) + if(!ui) + ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500) + ui.open() + ui.set_auto_update(1) + +/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["lines"] = lines + data["tempo"] = tempo + + data["playing"] = playing + data["help"] = help + data["repeat"] = repeat + data["maxRepeat"] = max_repeat + data["minTempo"] = world.tick_lag + data["maxTempo"] = 600 + + return data + +/datum/song/Topic(href, href_list) + if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || usr.incapacitated()) + usr << browse(null, "window=instrument") + usr.unset_machine() + return 1 + + instrumentObj.add_fingerprint(usr) + + if(href_list["newsong"]) + playing = 0 + lines = new() + tempo = sanitize_tempo(5) // default 120 BPM + name = "" + SSnanoui.update_uis(src) + + else if(href_list["import"]) + playing = 0 + var/t = "" + do + t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message) + if(!in_range(instrumentObj, usr)) + return + + if(length(t) >= 12000) + var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no") + if(cont == "no") + break + while(length(t) > 12000) + + //split into lines + spawn() + lines = splittext(t, "\n") + if(lines.len == 0) + return 1 + if(copytext(lines[1],1,6) == "BPM: ") + tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6))) + lines.Cut(1,2) + else + tempo = sanitize_tempo(5) // default 120 BPM + if(lines.len > 200) + to_chat(usr, "Too many lines!") + lines.Cut(201) + var/linenum = 1 + for(var/l in lines) + if(length(l) > 200) + to_chat(usr, "Line [linenum] too long!") + lines.Remove(l) + else + linenum++ + SSnanoui.update_uis(src) + + else if(href_list["help"]) + help = !help + SSnanoui.update_uis(src) + + if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops. + if(playing) + return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing. + repeat += round(text2num(href_list["repeat"])) + if(repeat < 0) + repeat = 0 + if(repeat > max_repeat) + repeat = max_repeat + SSnanoui.update_uis(src) + + else if(href_list["tempo"]) + tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag) + SSnanoui.update_uis(src) + + else if(href_list["play"]) + if(playing) + return + playing = 1 + spawn() + playsong(usr) + SSnanoui.update_uis(src) + + else if(href_list["insertline"]) + var/num = round(text2num(href_list["insertline"])) + if(num < 1 || num > lines.len + 1) + return + + var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null) + if(!newline || !in_range(instrumentObj, usr)) + return + if(lines.len > 200) + return + if(length(newline) > 200) + newline = copytext(newline, 1, 200) + + lines.Insert(num, newline) + SSnanoui.update_uis(src) + + else if(href_list["deleteline"]) + var/num = round(text2num(href_list["deleteline"])) + if(num > lines.len || num < 1) + return + lines.Cut(num, num + 1) + SSnanoui.update_uis(src) + + else if(href_list["modifyline"]) + var/num = round(text2num(href_list["modifyline"])) + var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null) + if(!content || !in_range(instrumentObj, usr)) + return + if(length(content) > 200) + content = copytext(content, 1, 200) + if(num > lines.len || num < 1) + return + lines[num] = content + SSnanoui.update_uis(src) + + else if(href_list["stop"]) + playing = 0 + SSnanoui.update_uis(src) + +/datum/song/proc/sanitize_tempo(new_tempo) + new_tempo = abs(new_tempo) + return max(round(new_tempo, world.tick_lag), world.tick_lag) + +// subclass for handheld instruments, like violin +/datum/song/handheld + +/datum/song/handheld/shouldStopPlaying() + if(instrumentObj) + return !isliving(instrumentObj.loc) + else + return 1 + + +////////////////////////////////////////////////////////////////////////// + + +/obj/structure/piano + name = "space minimoog" + icon = 'icons/obj/musician.dmi' + icon_state = "minimoog" + anchored = 1 + density = 1 + var/datum/song/song + + +/obj/structure/piano/New() + ..() + song = new("piano", src) + + if(prob(50)) + name = "space minimoog" + desc = "This is a minimoog, like a space piano, but more spacey!" + icon_state = "minimoog" + else + name = "space piano" + desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't." + icon_state = "piano" + +/obj/structure/piano/Destroy() + QDEL_NULL(song) + return ..() + +/obj/structure/piano/Initialize() + if(song) + song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded + ..() + +/obj/structure/piano/attack_hand(mob/user as mob) + ui_interact(user) + +/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!isliving(user) || user.incapacitated() || !anchored) + return + + song.ui_interact(user, ui_key, ui, force_open) + +/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + return song.ui_data(user, ui_key, state) + +/obj/structure/piano/Topic(href, href_list) + song.Topic(href, href_list) + +/obj/structure/piano/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(!anchored && !isinspace()) + WRENCH_ANCHOR_MESSAGE + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + user.visible_message( \ + "[user] tightens [src]'s casters.", \ + " You have tightened [src]'s casters. Now it can be played again.", \ + "You hear ratchet.") + anchored = TRUE + else if(anchored) + to_chat(user, " You begin to loosen [src]'s casters...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume)) + return + user.visible_message( \ + "[user] loosens [src]'s casters.", \ + " You have loosened [src]. Now it can be pulled somewhere else.", \ + "You hear ratchet.") + anchored = FALSE + else + to_chat(user, "[src] needs to be bolted to the floor!") diff --git a/code/game/objects/structures/noticeboard.dm b/code/game/objects/structures/noticeboard.dm index 5938a52c1d52..6b9615cd9941 100644 --- a/code/game/objects/structures/noticeboard.dm +++ b/code/game/objects/structures/noticeboard.dm @@ -1,82 +1,82 @@ -/obj/structure/noticeboard - name = "notice board" - desc = "A board for pinning important notices upon." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nboard00" - density = 0 - anchored = 1 - max_integrity = 150 - var/notices = 0 - -/obj/structure/noticeboard/Initialize() - ..() - for(var/obj/item/I in loc) - if(notices > 4) break - if(istype(I, /obj/item/paper)) - I.loc = src - notices++ - icon_state = "nboard0[notices]" - -//attaching papers!! -/obj/structure/noticeboard/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(istype(O, /obj/item/paper)) - if(notices < 5) - O.add_fingerprint(user) - add_fingerprint(user) - user.drop_item() - O.loc = src - notices++ - icon_state = "nboard0[notices]" //update sprite - to_chat(user, "You pin the paper to the noticeboard.") - else - to_chat(user, "You reach to pin your paper to the board but hesitate. You are certain your paper will not be seen among the many others already attached.") - return - return ..() - -/obj/structure/noticeboard/attack_hand(user as mob) - var/dat = "Noticeboard
    " - for(var/obj/item/paper/P in src) - dat += "[P.name] Write Remove
    " - user << browse("Notices[dat]","window=noticeboard") - onclose(user, "noticeboard") - -/obj/structure/noticeboard/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal (loc, 1) - qdel(src) - -/obj/structure/noticeboard/Topic(href, href_list) - ..() - usr.set_machine(src) - if(href_list["remove"]) - if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open - return - var/obj/item/P = locate(href_list["remove"]) - if((P && P.loc == src)) - P.loc = get_turf(src) //dump paper on the floor because you're a clumsy fuck - P.add_fingerprint(usr) - add_fingerprint(usr) - notices-- - icon_state = "nboard0[notices]" - - if(href_list["write"]) - if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open - return - var/obj/item/P = locate(href_list["write"]) - - if((P && P.loc == src)) //ifthe paper's on the board - if(istype(usr.r_hand, /obj/item/pen)) //and you're holding a pen - add_fingerprint(usr) - P.attackby(usr.r_hand, usr) //then do ittttt - else - if(istype(usr.l_hand, /obj/item/pen)) //check other hand for pen - add_fingerprint(usr) - P.attackby(usr.l_hand, usr) - else - to_chat(usr, "You'll need something to write with!") - - if(href_list["read"]) - var/obj/item/paper/P = locate(href_list["read"]) - if((P && P.loc == src)) - P.show_content(usr) - return +/obj/structure/noticeboard + name = "notice board" + desc = "A board for pinning important notices upon." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nboard00" + density = 0 + anchored = 1 + max_integrity = 150 + var/notices = 0 + +/obj/structure/noticeboard/Initialize() + ..() + for(var/obj/item/I in loc) + if(notices > 4) break + if(istype(I, /obj/item/paper)) + I.loc = src + notices++ + icon_state = "nboard0[notices]" + +//attaching papers!! +/obj/structure/noticeboard/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(istype(O, /obj/item/paper)) + if(notices < 5) + O.add_fingerprint(user) + add_fingerprint(user) + user.drop_item() + O.loc = src + notices++ + icon_state = "nboard0[notices]" //update sprite + to_chat(user, "You pin the paper to the noticeboard.") + else + to_chat(user, "You reach to pin your paper to the board but hesitate. You are certain your paper will not be seen among the many others already attached.") + return + return ..() + +/obj/structure/noticeboard/attack_hand(user as mob) + var/dat = "Noticeboard
    " + for(var/obj/item/paper/P in src) + dat += "[P.name] Write Remove
    " + user << browse("Notices[dat]","window=noticeboard") + onclose(user, "noticeboard") + +/obj/structure/noticeboard/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal (loc, 1) + qdel(src) + +/obj/structure/noticeboard/Topic(href, href_list) + ..() + usr.set_machine(src) + if(href_list["remove"]) + if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open + return + var/obj/item/P = locate(href_list["remove"]) + if((P && P.loc == src)) + P.loc = get_turf(src) //dump paper on the floor because you're a clumsy fuck + P.add_fingerprint(usr) + add_fingerprint(usr) + notices-- + icon_state = "nboard0[notices]" + + if(href_list["write"]) + if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open + return + var/obj/item/P = locate(href_list["write"]) + + if((P && P.loc == src)) //ifthe paper's on the board + if(istype(usr.r_hand, /obj/item/pen)) //and you're holding a pen + add_fingerprint(usr) + P.attackby(usr.r_hand, usr) //then do ittttt + else + if(istype(usr.l_hand, /obj/item/pen)) //check other hand for pen + add_fingerprint(usr) + P.attackby(usr.l_hand, usr) + else + to_chat(usr, "You'll need something to write with!") + + if(href_list["read"]) + var/obj/item/paper/P = locate(href_list["read"]) + if((P && P.loc == src)) + P.show_content(usr) + return diff --git a/code/game/objects/structures/plasticflaps.dm b/code/game/objects/structures/plasticflaps.dm index 15b3c5d64206..01c48edaad15 100644 --- a/code/game/objects/structures/plasticflaps.dm +++ b/code/game/objects/structures/plasticflaps.dm @@ -108,4 +108,4 @@ T.air_update_turf(TRUE) /obj/structure/plasticflaps/mining/CanAtmosPass(turf/T) - return FALSE \ No newline at end of file + return FALSE diff --git a/code/game/objects/structures/reflector.dm b/code/game/objects/structures/reflector.dm index 5620422cc456..3a83788b133f 100644 --- a/code/game/objects/structures/reflector.dm +++ b/code/game/objects/structures/reflector.dm @@ -1,183 +1,183 @@ -/obj/structure/reflector - name = "reflector frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "box_0" - desc = "A frame to create a reflector.\nUse 5 sheets of glass to create a 1 way reflector.\nUse 10 sheets of reinforced glass to create a 2 way reflector.\nUse 1 diamond to create a reflector cube." - anchored = 0 - density = 1 - layer = 3 - var/finished = 0 - - -/obj/structure/reflector/bullet_act(obj/item/projectile/P) - var/turf/reflector_turf = get_turf(src) - var/turf/reflect_turf - if(!istype(P, /obj/item/projectile/beam)) - return ..() - var/new_dir = get_reflection(dir, P.dir) - if(new_dir) - reflect_turf = get_step(reflect_turf, new_dir) - else - visible_message("[src] is hit by [P]!") - new_dir = 0 - return ..() //Hits as normal, explodes or emps or whatever - - visible_message("[P] bounces off of [src]") - reflect_turf = get_step(loc,new_dir) - - P.original = reflect_turf - P.starting = reflector_turf - P.current = reflector_turf - P.yo = reflect_turf.y - reflector_turf.y - P.xo = reflect_turf.x - reflector_turf.x - P.ignore_source_check = TRUE //If shot by a laser, will now hit the mob that fired it - var/reflect_angle = dir2angle(new_dir) - P.setAngle(reflect_angle) - - new_dir = 0 - return -1 - - -/obj/structure/reflector/attackby(obj/item/W, mob/user, params) - //Finishing the frame - if(istype(W,/obj/item/stack/sheet)) - if(finished) - return - var/obj/item/stack/sheet/S = W - if(istype(W, /obj/item/stack/sheet/glass)) - if(S.get_amount() < 5) - to_chat(user, "You need five sheets of glass to create a reflector!") - return - else - S.use(5) - new /obj/structure/reflector/single (src.loc) - qdel(src) - if(istype(W,/obj/item/stack/sheet/rglass)) - if(S.get_amount() < 10) - to_chat(user, "You need ten sheets of reinforced glass to create a double reflector!") - return - else - S.use(10) - new /obj/structure/reflector/double (src.loc) - qdel(src) - if(istype(W, /obj/item/stack/sheet/mineral/diamond)) - if(S.get_amount() >= 1) - S.use(1) - new /obj/structure/reflector/box (src.loc) - qdel(src) - return - return ..() - -/obj/structure/reflector/wrench_act(mob/user, obj/item/I) - . = TRUE - if(anchored) - to_chat(user, "Unweld [src] first!") - return - if(!I.tool_use_check(user, 0)) - return - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(!I.use_tool(src, user, 80, volume = I.tool_volume)) - return - playsound(user, 'sound/items/Ratchet.ogg', 50, 1) - TOOL_DISMANTLE_SUCCESS_MESSAGE - new /obj/item/stack/sheet/metal(src.loc, 5) - qdel(src) - -/obj/structure/reflector/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(anchored) - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - anchored = FALSE - else - WELDER_ATTEMPT_FLOOR_WELD_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - WELDER_FLOOR_WELD_SUCCESS_MESSAGE - anchored = TRUE - -/obj/structure/reflector/proc/get_reflection(srcdir,pdir) - return 0 - - -/obj/structure/reflector/verb/rotate() - set name = "Rotate" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 270) - return 1 - - -/obj/structure/reflector/AltClick(mob/user) - ..() - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user)) - return - else - rotate() - - -//TYPES OF REFLECTORS, SINGLE, DOUBLE, BOX - -//SINGLE - -/obj/structure/reflector/single - name = "reflector" - icon = 'icons/obj/reflector.dmi' - icon_state = "reflector" - desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle." - finished = 1 - var/static/list/rotations = list("[NORTH]" = list("[SOUTH]" = WEST, "[EAST]" = NORTH), -"[EAST]" = list("[SOUTH]" = EAST, "[WEST]" = NORTH), -"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH), -"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH) ) - -/obj/structure/reflector/single/get_reflection(srcdir,pdir) - var/new_dir = rotations["[srcdir]"]["[pdir]"] - return new_dir - -//DOUBLE - -/obj/structure/reflector/double - name = "double sided reflector" - icon = 'icons/obj/reflector.dmi' - icon_state = "reflector_double" - desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle." - finished = 1 - var/static/list/double_rotations = list("[NORTH]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH), -"[EAST]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH), -"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH), -"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH) ) - -/obj/structure/reflector/double/get_reflection(srcdir,pdir) - var/new_dir = double_rotations["[srcdir]"]["[pdir]"] - return new_dir - -//BOX - -/obj/structure/reflector/box - name = "reflector box" - icon = 'icons/obj/reflector.dmi' - icon_state = "reflector_box" - desc = "A box with an internal set of mirrors that reflects all laser fire in a single direction." - finished = 1 - var/static/list/box_rotations = list("[NORTH]" = list("[SOUTH]" = NORTH, "[EAST]" = NORTH, "[WEST]" = NORTH, "[NORTH]" = NORTH), -"[EAST]" = list("[SOUTH]" = EAST, "[EAST]" = EAST, "[WEST]" = EAST, "[NORTH]" = EAST), -"[SOUTH]" = list("[SOUTH]" = SOUTH, "[EAST]" = SOUTH, "[WEST]" = SOUTH, "[NORTH]" = SOUTH), -"[WEST]" = list("[SOUTH]" = WEST, "[EAST]" = WEST, "[WEST]" = WEST, "[NORTH]" = WEST) ) - -/obj/structure/reflector/box/get_reflection(srcdir,pdir) - var/new_dir = box_rotations["[srcdir]"]["[pdir]"] - return new_dir \ No newline at end of file +/obj/structure/reflector + name = "reflector frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "box_0" + desc = "A frame to create a reflector.\nUse 5 sheets of glass to create a 1 way reflector.\nUse 10 sheets of reinforced glass to create a 2 way reflector.\nUse 1 diamond to create a reflector cube." + anchored = 0 + density = 1 + layer = 3 + var/finished = 0 + + +/obj/structure/reflector/bullet_act(obj/item/projectile/P) + var/turf/reflector_turf = get_turf(src) + var/turf/reflect_turf + if(!istype(P, /obj/item/projectile/beam)) + return ..() + var/new_dir = get_reflection(dir, P.dir) + if(new_dir) + reflect_turf = get_step(reflect_turf, new_dir) + else + visible_message("[src] is hit by [P]!") + new_dir = 0 + return ..() //Hits as normal, explodes or emps or whatever + + visible_message("[P] bounces off of [src]") + reflect_turf = get_step(loc,new_dir) + + P.original = reflect_turf + P.starting = reflector_turf + P.current = reflector_turf + P.yo = reflect_turf.y - reflector_turf.y + P.xo = reflect_turf.x - reflector_turf.x + P.ignore_source_check = TRUE //If shot by a laser, will now hit the mob that fired it + var/reflect_angle = dir2angle(new_dir) + P.setAngle(reflect_angle) + + new_dir = 0 + return -1 + + +/obj/structure/reflector/attackby(obj/item/W, mob/user, params) + //Finishing the frame + if(istype(W,/obj/item/stack/sheet)) + if(finished) + return + var/obj/item/stack/sheet/S = W + if(istype(W, /obj/item/stack/sheet/glass)) + if(S.get_amount() < 5) + to_chat(user, "You need five sheets of glass to create a reflector!") + return + else + S.use(5) + new /obj/structure/reflector/single (src.loc) + qdel(src) + if(istype(W,/obj/item/stack/sheet/rglass)) + if(S.get_amount() < 10) + to_chat(user, "You need ten sheets of reinforced glass to create a double reflector!") + return + else + S.use(10) + new /obj/structure/reflector/double (src.loc) + qdel(src) + if(istype(W, /obj/item/stack/sheet/mineral/diamond)) + if(S.get_amount() >= 1) + S.use(1) + new /obj/structure/reflector/box (src.loc) + qdel(src) + return + return ..() + +/obj/structure/reflector/wrench_act(mob/user, obj/item/I) + . = TRUE + if(anchored) + to_chat(user, "Unweld [src] first!") + return + if(!I.tool_use_check(user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(!I.use_tool(src, user, 80, volume = I.tool_volume)) + return + playsound(user, 'sound/items/Ratchet.ogg', 50, 1) + TOOL_DISMANTLE_SUCCESS_MESSAGE + new /obj/item/stack/sheet/metal(src.loc, 5) + qdel(src) + +/obj/structure/reflector/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(anchored) + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + anchored = FALSE + else + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + anchored = TRUE + +/obj/structure/reflector/proc/get_reflection(srcdir,pdir) + return 0 + + +/obj/structure/reflector/verb/rotate() + set name = "Rotate" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 270) + return 1 + + +/obj/structure/reflector/AltClick(mob/user) + ..() + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user)) + return + else + rotate() + + +//TYPES OF REFLECTORS, SINGLE, DOUBLE, BOX + +//SINGLE + +/obj/structure/reflector/single + name = "reflector" + icon = 'icons/obj/reflector.dmi' + icon_state = "reflector" + desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle." + finished = 1 + var/static/list/rotations = list("[NORTH]" = list("[SOUTH]" = WEST, "[EAST]" = NORTH), +"[EAST]" = list("[SOUTH]" = EAST, "[WEST]" = NORTH), +"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH), +"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH) ) + +/obj/structure/reflector/single/get_reflection(srcdir,pdir) + var/new_dir = rotations["[srcdir]"]["[pdir]"] + return new_dir + +//DOUBLE + +/obj/structure/reflector/double + name = "double sided reflector" + icon = 'icons/obj/reflector.dmi' + icon_state = "reflector_double" + desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle." + finished = 1 + var/static/list/double_rotations = list("[NORTH]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH), +"[EAST]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH), +"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH), +"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH) ) + +/obj/structure/reflector/double/get_reflection(srcdir,pdir) + var/new_dir = double_rotations["[srcdir]"]["[pdir]"] + return new_dir + +//BOX + +/obj/structure/reflector/box + name = "reflector box" + icon = 'icons/obj/reflector.dmi' + icon_state = "reflector_box" + desc = "A box with an internal set of mirrors that reflects all laser fire in a single direction." + finished = 1 + var/static/list/box_rotations = list("[NORTH]" = list("[SOUTH]" = NORTH, "[EAST]" = NORTH, "[WEST]" = NORTH, "[NORTH]" = NORTH), +"[EAST]" = list("[SOUTH]" = EAST, "[EAST]" = EAST, "[WEST]" = EAST, "[NORTH]" = EAST), +"[SOUTH]" = list("[SOUTH]" = SOUTH, "[EAST]" = SOUTH, "[WEST]" = SOUTH, "[NORTH]" = SOUTH), +"[WEST]" = list("[SOUTH]" = WEST, "[EAST]" = WEST, "[WEST]" = WEST, "[NORTH]" = WEST) ) + +/obj/structure/reflector/box/get_reflection(srcdir,pdir) + var/new_dir = box_rotations["[srcdir]"]["[pdir]"] + return new_dir diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index 1e289a7f27b5..b67bc2c70d1c 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -1,385 +1,385 @@ -/* -CONTAINS: -SAFES -FLOOR SAFES -SAFE INTERNALS -SAFE CODES -*/ - -GLOBAL_LIST_EMPTY(safes) - -//SAFES -/obj/structure/safe - name = "\improper Safe" - desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\"" - icon = 'icons/obj/structures.dmi' - icon_state = "safe" - - anchored = TRUE - density = TRUE - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - - var/open = FALSE - var/locked = TRUE - var/dial = 0 // The position the dial is pointing to. - - var/number_of_tumblers = 3 // The amount of tumblers that will be generated. - var/list/tumblers = list() // The list of tumbler dial positions that need to be hit. - var/list/current_tumbler_index = 1 // The index in the tumblers list of the tumbler dial position that needs to be hit. - - var/space = 0 // The combined w_class of everything in the safe. - var/maxspace = 24 // The maximum combined w_class of stuff in the safe. - - var/obj/item/thermal_drill/drill = null - var/drill_timer - var/time_to_drill - var/image/bar - var/drill_start_time - var/drill_x_offset = -13 - var/drill_y_offset = -3 - var/known_by = list() - - var/image/progress_bar - var/image/drill_overlay - -/obj/structure/safe/Initialize(mapload) - . = ..() - - GLOB.safes += src - - for(var/i in 1 to number_of_tumblers) - tumblers.Add(rand(0, 99)) - - for(var/obj/item/I in loc) - if(space >= maxspace) - return - if(I.w_class + space <= maxspace) - space += I.w_class - I.forceMove(src) - -/obj/structure/safe/Destroy() - GLOB.safes -= src - drill?.soundloop?.stop() - drill?.forceMove(loc) - drill = null - - qdel(progress_bar) - qdel(drill_overlay) - return ..() - -/obj/structure/safe/process() - if(drill_timer) - cut_overlay(progress_bar) - progress_bar = image('icons/effects/progessbar.dmi', src, "prog_bar_[round((((world.time - drill_start_time) / time_to_drill) * 100), 5)]", HUD_LAYER) - add_overlay(progress_bar) - if(prob(15)) - drill.spark_system.start() - -/obj/structure/safe/examine(mob/user) - . = ..() - - . += "This model appears to have [number_of_tumblers] tumblers." - if(open) - . += "The inside of the the door has numbers written on it: [get_combination()]" - -/obj/structure/safe/blob_act(obj/structure/blob/B) - return - -/obj/structure/safe/ex_act(severity) - return - -/obj/structure/safe/examine_status(mob/user) - return - -/obj/structure/safe/proc/check_unlocked() - if(current_tumbler_index > number_of_tumblers) - locked = FALSE - visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!") - return TRUE - - locked = TRUE - return FALSE - -/obj/structure/safe/proc/get_combination() - var/combination = "" - var/looped = 0 - - for(var/tumbler in tumblers) - looped++ - combination += "[tumbler]" - - if(looped < LAZYLEN(tumblers)) - combination += ", " - - return combination - -/obj/structure/safe/update_icon() - if(open) - if(broken) - icon_state = "[initial(icon_state)]-open-broken" - else - icon_state = "[initial(icon_state)]-open" - else - if(broken) - icon_state = "[initial(icon_state)]-broken" - else - icon_state = initial(icon_state) - - var/list/overlays_to_cut = list(drill_overlay) - if(!drill_timer) - overlays_to_cut += progress_bar - - cut_overlay(overlays_to_cut) - - if(istype(drill, /obj/item/thermal_drill)) - var/drill_icon = istype(drill, /obj/item/thermal_drill/diamond_drill) ? "d" : "h" - if(drill_timer) - drill_overlay = image(icon = 'icons/effects/drill.dmi', icon_state = "[initial(icon_state)]_[drill_icon]-drill-on", pixel_x = drill_x_offset, pixel_y = drill_y_offset) - else - drill_overlay = image(icon = 'icons/effects/drill.dmi', icon_state = "[initial(icon_state)]_[drill_icon]-drill-off", pixel_x = drill_x_offset, pixel_y = drill_y_offset) - - add_overlay(drill_overlay) - -/obj/structure/safe/attack_ghost(mob/user) - if(..() || drill) - return TRUE - - ui_interact(user) - -/obj/structure/safe/attack_hand(mob/user) - if(..()) - return TRUE - - if(drill) - switch(alert("What would you like to do?", "Thermal Drill", "Turn [drill_timer ? "Off" : "On"]", "Remove Drill", "Cancel")) - if("Turn On") - if(do_after(user, 2 SECONDS, target = src)) - drill_timer = addtimer(CALLBACK(src, .proc/drill_open), time_to_drill, TIMER_STOPPABLE) - drill_start_time = world.time - drill.soundloop.start() - update_icon() - START_PROCESSING(SSobj, src) - if("Turn Off") - if(do_after(user, 2 SECONDS, target = src)) - deltimer(drill_timer) - drill_timer = null - drill.soundloop.stop() - update_icon() - STOP_PROCESSING(SSobj, src) - if("Remove Drill") - if(drill_timer) - to_chat(user, "You cant remove the drill while it's running!") - else if(do_after(user, 2 SECONDS, target = src)) - user.put_in_hands(drill) - drill = null - update_icon() - if("Cancel") - return - else - ui_interact(user) - -/obj/structure/safe/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "safe.tmpl", name, 600, 750) - ui.open() - ui.set_auto_update(1) - -/obj/structure/safe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/list/contents_names = list() - if(open) - for(var/obj/O in contents) - contents_names[++contents_names.len] = list("name" = O.name, "index" = contents.Find(O), "sprite" = O.icon_state) - user << browse_rsc(icon(O.icon, O.icon_state), "[O.icon_state].png") - - data["dial"] = dial - data["open"] = open - data["locked"] = locked - data["rotation"] = "[-dial * 3.6]deg" - data["contents"] = contents_names - - return data - -/obj/structure/safe/proc/notify_user(user, canhear, sounds, total_ticks, current_tick) - if(!canhear) - return - - if(current_tick == 2) - to_chat(user, "The sounds from [src] are too fast and blend together.") - - if(total_ticks == 1 || prob(10)) - to_chat(user, "You hear a [pick(sounds)] from [src].") - -/obj/structure/safe/Topic(href, href_list) - if(..()) - return TRUE - - var/mob/user = usr - if(!user.IsAdvancedToolUser() && !isobserver(user)) - to_chat(user, "You're not able to operate the safe.") - return - - var/canhear = FALSE - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.can_hear() && H.is_in_hands(/obj/item/clothing/accessory/stethoscope)) - canhear = TRUE - - if(href_list["open"]) - if(check_unlocked() || open || broken) - to_chat(user, "You [open ? "close" : "open"] [src].") - open = !open - update_icon() - else - to_chat(user, "You can't open [src], as its lock is engaged!") - - . = TRUE - - if(href_list["turnright"]) - if(open) - return - - if(broken) - to_chat(user, "The dial will not turn, as the mechanism is destroyed.") - return - - var/ticks = text2num(href_list["turnright"]) - for(var/i = 1 to ticks) - dial = Wrap(dial - 1, 0, 100) - - var/invalid_turn = current_tumbler_index % 2 == 0 || current_tumbler_index > number_of_tumblers - if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset - current_tumbler_index = 1 - - if(!invalid_turn && dial == tumblers[current_tumbler_index]) - notify_user(user, canhear, list("tink", "krink", "plink"), ticks, i) - current_tumbler_index++ - else - notify_user(user, canhear, list("clack", "scrape", "clank"), ticks, i) - - sleep(1) - check_unlocked(user, canhear) - SSnanoui.update_uis(src) - - . = TRUE - - if(href_list["turnleft"]) - if(open) - return - - if(broken) - to_chat(user, "The dial will not turn, as the mechanism is destroyed.") - return - - var/ticks = text2num(href_list["turnleft"]) - for(var/i = 1 to ticks) - dial = Wrap(dial + 1, 0, 100) - - var/invalid_turn = current_tumbler_index % 2 != 0 || current_tumbler_index > number_of_tumblers - if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset - current_tumbler_index = 1 - - if(!invalid_turn && dial == tumblers[current_tumbler_index]) - notify_user(user, canhear, list("tonk", "krunk", "plunk"), ticks, i) - current_tumbler_index++ - else - notify_user(user, canhear, list("click", "chink", "clink"), ticks, i) - - sleep(1) - check_unlocked(user, canhear) - SSnanoui.update_uis(src) - - . = TRUE - - if(href_list["retrieve"]) - var/index = text2num(href_list["retrieve"]) - if(index > 0 && index <= contents.len) - var/obj/item/P = contents[index] - if(open) - if(P && in_range(src, user)) - user.put_in_hands(P) - space -= P.w_class - . = TRUE - -/obj/structure/safe/proc/drill_open() - broken = TRUE - drill_timer = null - drill.soundloop.stop() - update_icon() - STOP_PROCESSING(SSobj, src) - -/obj/structure/safe/attackby(obj/item/I, mob/user, params) - if(open) - if(broken && istype(I, /obj/item/safe_internals) && do_after(user, 2 SECONDS, target = src)) - to_chat(user, "You replace the broken mechanism.") - qdel(I) - broken = !broken - update_icon() - else if(I.w_class + space <= maxspace) - if(!user.drop_item()) - to_chat(user, "\The [I] is stuck to your hand, you cannot put it in the safe!") - return - space += I.w_class - I.forceMove(src) - to_chat(user, "You put [I] in [src].") - else - to_chat(user, "[I] won't fit in [src].") - else - if(istype(I, /obj/item/clothing/accessory/stethoscope)) - to_chat(user, "Hold [I] in one of your hands while you manipulate the dial!") - return - else if(istype(I, /obj/item/thermal_drill)) - if(drill) - to_chat(user, "There is already a drill attached!") - else if(do_after(user, 2 SECONDS, target = src)) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand, you cannot put it in the safe!") - return - I.forceMove(src) - drill = I - time_to_drill = 300 SECONDS * drill.time_multiplier - update_icon() - else - to_chat(user, "You can't put [I] into the safe while it is closed!") - return - -//FLOOR SAFES -/obj/structure/safe/floor - name = "floor safe" - icon_state = "floorsafe" - density = FALSE - level = 1 //Under the floor - layer = LOW_OBJ_LAYER - drill_x_offset = -1 - drill_y_offset = 20 - -/obj/structure/safe/floor/Initialize() - . = ..() - var/turf/T = loc - hide(T.intact) - -/obj/structure/safe/floor/hide(intact) - invisibility = intact ? INVISIBILITY_MAXIMUM : 0 - -/obj/item/safe_internals - name = "safe internals" - desc = "The mechanism and locking bolts for a Scarborough Arms - 2 tumbler safe" - icon_state = "safe_internals" - -/obj/item/paper/safe_code - name = "safe codes" - var/owner - info = "

    Safe Codes

    " - -/obj/item/paper/safe_code/Initialize(mapload) - return INITIALIZE_HINT_LATELOAD - -/obj/item/paper/safe_code/LateInitialize(mapload) - . = ..() - for(var/safe in GLOB.safes) - var/obj/structure/safe/S = safe - if(owner in S.known_by) - info += "
    The combination for the safe located in the [get_area(S).name] is: [S.get_combination()]
    " - info_links = info - update_icon() \ No newline at end of file +/* +CONTAINS: +SAFES +FLOOR SAFES +SAFE INTERNALS +SAFE CODES +*/ + +GLOBAL_LIST_EMPTY(safes) + +//SAFES +/obj/structure/safe + name = "\improper Safe" + desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\"" + icon = 'icons/obj/structures.dmi' + icon_state = "safe" + + anchored = TRUE + density = TRUE + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + + var/open = FALSE + var/locked = TRUE + var/dial = 0 // The position the dial is pointing to. + + var/number_of_tumblers = 3 // The amount of tumblers that will be generated. + var/list/tumblers = list() // The list of tumbler dial positions that need to be hit. + var/list/current_tumbler_index = 1 // The index in the tumblers list of the tumbler dial position that needs to be hit. + + var/space = 0 // The combined w_class of everything in the safe. + var/maxspace = 24 // The maximum combined w_class of stuff in the safe. + + var/obj/item/thermal_drill/drill = null + var/drill_timer + var/time_to_drill + var/image/bar + var/drill_start_time + var/drill_x_offset = -13 + var/drill_y_offset = -3 + var/known_by = list() + + var/image/progress_bar + var/image/drill_overlay + +/obj/structure/safe/Initialize(mapload) + . = ..() + + GLOB.safes += src + + for(var/i in 1 to number_of_tumblers) + tumblers.Add(rand(0, 99)) + + for(var/obj/item/I in loc) + if(space >= maxspace) + return + if(I.w_class + space <= maxspace) + space += I.w_class + I.forceMove(src) + +/obj/structure/safe/Destroy() + GLOB.safes -= src + drill?.soundloop?.stop() + drill?.forceMove(loc) + drill = null + + qdel(progress_bar) + qdel(drill_overlay) + return ..() + +/obj/structure/safe/process() + if(drill_timer) + cut_overlay(progress_bar) + progress_bar = image('icons/effects/progessbar.dmi', src, "prog_bar_[round((((world.time - drill_start_time) / time_to_drill) * 100), 5)]", HUD_LAYER) + add_overlay(progress_bar) + if(prob(15)) + drill.spark_system.start() + +/obj/structure/safe/examine(mob/user) + . = ..() + + . += "This model appears to have [number_of_tumblers] tumblers." + if(open) + . += "The inside of the the door has numbers written on it: [get_combination()]" + +/obj/structure/safe/blob_act(obj/structure/blob/B) + return + +/obj/structure/safe/ex_act(severity) + return + +/obj/structure/safe/examine_status(mob/user) + return + +/obj/structure/safe/proc/check_unlocked() + if(current_tumbler_index > number_of_tumblers) + locked = FALSE + visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!") + return TRUE + + locked = TRUE + return FALSE + +/obj/structure/safe/proc/get_combination() + var/combination = "" + var/looped = 0 + + for(var/tumbler in tumblers) + looped++ + combination += "[tumbler]" + + if(looped < LAZYLEN(tumblers)) + combination += ", " + + return combination + +/obj/structure/safe/update_icon() + if(open) + if(broken) + icon_state = "[initial(icon_state)]-open-broken" + else + icon_state = "[initial(icon_state)]-open" + else + if(broken) + icon_state = "[initial(icon_state)]-broken" + else + icon_state = initial(icon_state) + + var/list/overlays_to_cut = list(drill_overlay) + if(!drill_timer) + overlays_to_cut += progress_bar + + cut_overlay(overlays_to_cut) + + if(istype(drill, /obj/item/thermal_drill)) + var/drill_icon = istype(drill, /obj/item/thermal_drill/diamond_drill) ? "d" : "h" + if(drill_timer) + drill_overlay = image(icon = 'icons/effects/drill.dmi', icon_state = "[initial(icon_state)]_[drill_icon]-drill-on", pixel_x = drill_x_offset, pixel_y = drill_y_offset) + else + drill_overlay = image(icon = 'icons/effects/drill.dmi', icon_state = "[initial(icon_state)]_[drill_icon]-drill-off", pixel_x = drill_x_offset, pixel_y = drill_y_offset) + + add_overlay(drill_overlay) + +/obj/structure/safe/attack_ghost(mob/user) + if(..() || drill) + return TRUE + + ui_interact(user) + +/obj/structure/safe/attack_hand(mob/user) + if(..()) + return TRUE + + if(drill) + switch(alert("What would you like to do?", "Thermal Drill", "Turn [drill_timer ? "Off" : "On"]", "Remove Drill", "Cancel")) + if("Turn On") + if(do_after(user, 2 SECONDS, target = src)) + drill_timer = addtimer(CALLBACK(src, .proc/drill_open), time_to_drill, TIMER_STOPPABLE) + drill_start_time = world.time + drill.soundloop.start() + update_icon() + START_PROCESSING(SSobj, src) + if("Turn Off") + if(do_after(user, 2 SECONDS, target = src)) + deltimer(drill_timer) + drill_timer = null + drill.soundloop.stop() + update_icon() + STOP_PROCESSING(SSobj, src) + if("Remove Drill") + if(drill_timer) + to_chat(user, "You cant remove the drill while it's running!") + else if(do_after(user, 2 SECONDS, target = src)) + user.put_in_hands(drill) + drill = null + update_icon() + if("Cancel") + return + else + ui_interact(user) + +/obj/structure/safe/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "safe.tmpl", name, 600, 750) + ui.open() + ui.set_auto_update(1) + +/obj/structure/safe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + var/list/contents_names = list() + if(open) + for(var/obj/O in contents) + contents_names[++contents_names.len] = list("name" = O.name, "index" = contents.Find(O), "sprite" = O.icon_state) + user << browse_rsc(icon(O.icon, O.icon_state), "[O.icon_state].png") + + data["dial"] = dial + data["open"] = open + data["locked"] = locked + data["rotation"] = "[-dial * 3.6]deg" + data["contents"] = contents_names + + return data + +/obj/structure/safe/proc/notify_user(user, canhear, sounds, total_ticks, current_tick) + if(!canhear) + return + + if(current_tick == 2) + to_chat(user, "The sounds from [src] are too fast and blend together.") + + if(total_ticks == 1 || prob(10)) + to_chat(user, "You hear a [pick(sounds)] from [src].") + +/obj/structure/safe/Topic(href, href_list) + if(..()) + return TRUE + + var/mob/user = usr + if(!user.IsAdvancedToolUser() && !isobserver(user)) + to_chat(user, "You're not able to operate the safe.") + return + + var/canhear = FALSE + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.can_hear() && H.is_in_hands(/obj/item/clothing/accessory/stethoscope)) + canhear = TRUE + + if(href_list["open"]) + if(check_unlocked() || open || broken) + to_chat(user, "You [open ? "close" : "open"] [src].") + open = !open + update_icon() + else + to_chat(user, "You can't open [src], as its lock is engaged!") + + . = TRUE + + if(href_list["turnright"]) + if(open) + return + + if(broken) + to_chat(user, "The dial will not turn, as the mechanism is destroyed.") + return + + var/ticks = text2num(href_list["turnright"]) + for(var/i = 1 to ticks) + dial = Wrap(dial - 1, 0, 100) + + var/invalid_turn = current_tumbler_index % 2 == 0 || current_tumbler_index > number_of_tumblers + if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset + current_tumbler_index = 1 + + if(!invalid_turn && dial == tumblers[current_tumbler_index]) + notify_user(user, canhear, list("tink", "krink", "plink"), ticks, i) + current_tumbler_index++ + else + notify_user(user, canhear, list("clack", "scrape", "clank"), ticks, i) + + sleep(1) + check_unlocked(user, canhear) + SSnanoui.update_uis(src) + + . = TRUE + + if(href_list["turnleft"]) + if(open) + return + + if(broken) + to_chat(user, "The dial will not turn, as the mechanism is destroyed.") + return + + var/ticks = text2num(href_list["turnleft"]) + for(var/i = 1 to ticks) + dial = Wrap(dial + 1, 0, 100) + + var/invalid_turn = current_tumbler_index % 2 != 0 || current_tumbler_index > number_of_tumblers + if(invalid_turn) // The moment you turn the wrong way or go too far, the tumblers reset + current_tumbler_index = 1 + + if(!invalid_turn && dial == tumblers[current_tumbler_index]) + notify_user(user, canhear, list("tonk", "krunk", "plunk"), ticks, i) + current_tumbler_index++ + else + notify_user(user, canhear, list("click", "chink", "clink"), ticks, i) + + sleep(1) + check_unlocked(user, canhear) + SSnanoui.update_uis(src) + + . = TRUE + + if(href_list["retrieve"]) + var/index = text2num(href_list["retrieve"]) + if(index > 0 && index <= contents.len) + var/obj/item/P = contents[index] + if(open) + if(P && in_range(src, user)) + user.put_in_hands(P) + space -= P.w_class + . = TRUE + +/obj/structure/safe/proc/drill_open() + broken = TRUE + drill_timer = null + drill.soundloop.stop() + update_icon() + STOP_PROCESSING(SSobj, src) + +/obj/structure/safe/attackby(obj/item/I, mob/user, params) + if(open) + if(broken && istype(I, /obj/item/safe_internals) && do_after(user, 2 SECONDS, target = src)) + to_chat(user, "You replace the broken mechanism.") + qdel(I) + broken = !broken + update_icon() + else if(I.w_class + space <= maxspace) + if(!user.drop_item()) + to_chat(user, "\The [I] is stuck to your hand, you cannot put it in the safe!") + return + space += I.w_class + I.forceMove(src) + to_chat(user, "You put [I] in [src].") + else + to_chat(user, "[I] won't fit in [src].") + else + if(istype(I, /obj/item/clothing/accessory/stethoscope)) + to_chat(user, "Hold [I] in one of your hands while you manipulate the dial!") + return + else if(istype(I, /obj/item/thermal_drill)) + if(drill) + to_chat(user, "There is already a drill attached!") + else if(do_after(user, 2 SECONDS, target = src)) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand, you cannot put it in the safe!") + return + I.forceMove(src) + drill = I + time_to_drill = 300 SECONDS * drill.time_multiplier + update_icon() + else + to_chat(user, "You can't put [I] into the safe while it is closed!") + return + +//FLOOR SAFES +/obj/structure/safe/floor + name = "floor safe" + icon_state = "floorsafe" + density = FALSE + level = 1 //Under the floor + layer = LOW_OBJ_LAYER + drill_x_offset = -1 + drill_y_offset = 20 + +/obj/structure/safe/floor/Initialize() + . = ..() + var/turf/T = loc + hide(T.intact) + +/obj/structure/safe/floor/hide(intact) + invisibility = intact ? INVISIBILITY_MAXIMUM : 0 + +/obj/item/safe_internals + name = "safe internals" + desc = "The mechanism and locking bolts for a Scarborough Arms - 2 tumbler safe" + icon_state = "safe_internals" + +/obj/item/paper/safe_code + name = "safe codes" + var/owner + info = "

    Safe Codes

    " + +/obj/item/paper/safe_code/Initialize(mapload) + return INITIALIZE_HINT_LATELOAD + +/obj/item/paper/safe_code/LateInitialize(mapload) + . = ..() + for(var/safe in GLOB.safes) + var/obj/structure/safe/S = safe + if(owner in S.known_by) + info += "
    The combination for the safe located in the [get_area(S).name] is: [S.get_combination()]
    " + info_links = info + update_icon() diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm index 930dcc0995c6..3f5d667a3564 100644 --- a/code/game/objects/structures/signs.dm +++ b/code/game/objects/structures/signs.dm @@ -1,326 +1,326 @@ -/obj/structure/sign - icon = 'icons/obj/decals.dmi' - anchored = 1 - opacity = 0 - density = 0 - layer = 3.5 - max_integrity = 100 - armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - -/obj/structure/sign/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src.loc, 'sound/weapons/slash.ogg', 80, TRUE) - else - playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(loc, 'sound/items/welder.ogg', 80, TRUE) - -/obj/structure/sign/screwdriver_act(mob/user, obj/item/I) - if(istype(src, /obj/structure/sign/double)) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You unfasten the sign with [I].") - var/obj/item/sign/S = new(src.loc) - S.name = name - S.desc = desc - S.icon_state = icon_state - //var/icon/I = icon('icons/obj/decals.dmi', icon_state) - //S.icon = I.Scale(24, 24) - S.sign_state = icon_state - qdel(src) - - -/obj/item/sign - name = "sign" - desc = "" - icon = 'icons/obj/decals.dmi' - w_class = WEIGHT_CLASS_NORMAL - resistance_flags = FLAMMABLE - var/sign_state = "" - -/obj/item/sign/attackby(obj/item/tool as obj, mob/user as mob) //construction - if(istype(tool, /obj/item/screwdriver) && isturf(user.loc)) - var/direction = input("In which direction?", "Select direction.") in list("North", "East", "South", "West", "Cancel") - if(direction == "Cancel") - return - if(QDELETED(src)) - return - var/obj/structure/sign/S = new(user.loc) - switch(direction) - if("North") - S.pixel_y = 32 - if("East") - S.pixel_x = 32 - if("South") - S.pixel_y = -32 - if("West") - S.pixel_x = -32 - else - return - S.name = name - S.desc = desc - S.icon_state = sign_state - to_chat(user, "You fasten \the [S] with your [tool].") - qdel(src) - else - return ..() - -/obj/structure/sign/double/map - name = "station map" - desc = "A framed picture of the station." - max_integrity = 500 - -/obj/structure/sign/double/map/left - icon_state = "map-left" - -/obj/structure/sign/double/map/right - icon_state = "map-right" - -/obj/structure/sign/securearea - name = "\improper SECURE AREA" - desc = "A warning sign which reads 'SECURE AREA'" - icon_state = "securearea" - -/obj/structure/sign/biohazard - name = "\improper BIOHAZARD" - desc = "A warning sign which reads 'BIOHAZARD'" - icon_state = "bio" - -/obj/structure/sign/electricshock - name = "\improper HIGH VOLTAGE" - desc = "A warning sign which reads 'HIGH VOLTAGE'" - icon_state = "shock" - -/obj/structure/sign/examroom - name = "\improper EXAM" - desc = "A guidance sign which reads 'EXAM ROOM'" - icon_state = "examroom" - -/obj/structure/sign/vacuum - name = "\improper HARD VACUUM AHEAD" - desc = "A warning sign which reads 'HARD VACUUM AHEAD'" - icon_state = "space" - -/obj/structure/sign/vacuum/external - name = "\improper EXTERNAL AIRLOCK" - desc = "A warning sign which reads 'EXTERNAL AIRLOCK'." - layer = MOB_LAYER - -/obj/structure/sign/deathsposal - name = "\improper DISPOSAL LEADS TO SPACE" - desc = "A warning sign which reads 'DISPOSAL LEADS TO SPACE'" - icon_state = "deathsposal" - -/obj/structure/sign/pods - name = "\improper ESCAPE PODS" - desc = "A warning sign which reads 'ESCAPE PODS'" - icon_state = "pods" - -/obj/structure/sign/fire - name = "\improper DANGER: FIRE" - desc = "A warning sign which reads 'DANGER: FIRE'" - icon_state = "fire" - resistance_flags = FIRE_PROOF - -/obj/structure/sign/nosmoking_1 - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'" - icon_state = "nosmoking" - resistance_flags = FLAMMABLE - -/obj/structure/sign/nosmoking_2 - name = "\improper NO SMOKING" - desc = "A warning sign which reads 'NO SMOKING'" - icon_state = "nosmoking2" - -/obj/structure/sign/radiation - name = "\improper HAZARDOUS RADIATION" - desc = "A warning sign alerting the user of potential radiation hazards." - icon_state = "radiation" - -/obj/structure/sign/radiation/rad_area - name = "\improper RADIOACTIVE AREA" - desc = "A warning sign which reads 'RADIOACTIVE AREA'." - -/obj/structure/sign/xeno_warning_mining - name = "DANGEROUS ALIEN LIFE" - desc = "A sign that warns would be travellers of hostile alien life in the vicinity." - icon = 'icons/obj/mining.dmi' - icon_state = "xeno_warning" - -/obj/structure/sign/redcross - name = "medbay" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here.'" - icon_state = "redcross" - -/obj/structure/sign/greencross - name = "medbay" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here.'" - icon_state = "greencross" - -/obj/structure/sign/goldenplaque - name = "The Most Robust Men Award for Robustness" - desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." - icon_state = "goldenplaque" - -/obj/structure/sign/kiddieplaque - name = "AI developers plaque" - desc = "Next to the extremely long list of names and job titles, there is a drawing of a little child. The child appears to be retarded. Beneath the image, someone has scratched the word \"PACKETS\"." - icon_state = "kiddieplaque" - -/obj/structure/sign/atmosplaque - name = "\improper ZAS Atmospherics Division plaque" - desc = "This plaque commemorates the fall of the Atmos ZAS division. For all the charred, dizzy, and brittle men who have died in its horrible hands." - icon_state = "atmosplaque" - -/obj/structure/sign/kidanplaque - name = "Kidan wall trophy" - desc = "A dead and stuffed Diona nymph, mounted on a board." - icon_state = "kidanplaque" - -/obj/structure/sign/mech - name = "\improper mech painting" - desc = "A painting of a mech" - icon_state = "mech" - -/obj/structure/sign/nuke - name = "\improper nuke painting" - desc = "A painting of a nuke" - icon_state = "nuke" - -/obj/structure/sign/clown - name = "\improper clown painting" - desc = "A painting of the clown and mime. Awwww." - icon_state = "clown" - -/obj/structure/sign/bobross - name = "\improper calming painting" - desc = "We don't make mistakes, just happy little accidents." - icon_state = "bob" - -/obj/structure/sign/singulo - name = "\improper singulo painting" - desc = "A mesmerizing painting of a singularity. It seems to suck you in..." - icon_state = "singulo" - -/obj/structure/sign/barber - name = "\improper barber shop sign" - desc = "A spinning sign indicating a barbershop is near." - icon_state = "barber" - -/obj/structure/sign/chinese - name = "\improper chinese restaurant sign" - desc = "A glowing dragon invites you in." - icon_state = "chinese" - -/obj/structure/sign/science - name = "\improper SCIENCE!" - desc = "A warning sign which reads 'SCIENCE!'" - icon_state = "science1" - -/obj/structure/sign/chemistry - name = "\improper CHEMISTRY" - desc = "A warning sign which reads 'CHEMISTRY'" - icon_state = "chemistry1" - -/obj/structure/sign/botany - name = "\improper HYDROPONICS" - desc = "A warning sign which reads 'HYDROPONICS'" - icon_state = "hydro1" - -/obj/structure/sign/xenobio - name = "\improper XENOBIOLOGY" - desc = "A sign labelling an area as a place where xenobiological entities are researched." - icon_state = "xenobio" - -/obj/structure/sign/evac - name = "\improper EVACUATION" - desc = "A sign labelling an area where evacuation procedures take place." - icon_state = "evac" - -/obj/structure/sign/drop - name = "\improper DROP PODS" - desc = "A sign labelling an area where drop pod loading procedures take place." - icon_state = "drop" - -/obj/structure/sign/custodian - name = "\improper CUSTODIAN" - desc = "A sign labelling an area where the custodian works." - icon_state = "custodian" - -/obj/structure/sign/engineering - name = "\improper ENGINEERING" - desc = "A sign labelling an area where engineers work." - icon_state = "engine" - -/obj/structure/sign/cargo - name = "\improper CARGO" - desc = "A sign labelling an area where cargo ships dock." - icon_state = "cargo" - -/obj/structure/sign/security - name = "\improper SECURITY" - desc = "A sign labelling an area where the law is law." - icon_state = "security" - -/obj/structure/sign/holy - name = "\improper HOLY" - desc = "A sign labelling a religious area." - icon_state = "holy" - -/obj/structure/sign/restroom - name = "\improper RESTROOM" - desc = "A sign labelling a restroom." - icon_state = "restroom" - -/obj/structure/sign/medbay - name = "\improper MEDBAY" - desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." - icon_state = "bluecross" - -/obj/structure/sign/medbay/alt - icon_state = "bluecross2" - -/obj/structure/sign/directions/science - name = "\improper Research Division" - desc = "A direction sign, pointing out which way the Research Division is." - icon_state = "direction_sci" - -/obj/structure/sign/directions/engineering - name = "\improper Engineering Department" - desc = "A direction sign, pointing out which way the Engineering department is." - icon_state = "direction_eng" - -/obj/structure/sign/directions/security - name = "\improper Security Department" - desc = "A direction sign, pointing out which way the Security department is." - icon_state = "direction_sec" - -/obj/structure/sign/directions/medical - name = "\improper Medical Bay" - desc = "A direction sign, pointing out which way Medical Bay is." - icon_state = "direction_med" - -/obj/structure/sign/directions/evac - name = "\improper Escape Arm" - desc = "A direction sign, pointing out which way escape shuttle dock is." - icon_state = "direction_evac" - -/obj/structure/sign/directions/cargo - name = "\improper Cargo Department" - desc = "A direction sign, pointing out which way the Cargo department is." - icon_state = "direction_supply" - -/obj/structure/sign/explosives - name = "\improper HIGH EXPLOSIVES" - desc = "A warning sign which reads 'HIGH EXPLOSIVES'." - icon_state = "explosives" - -/obj/structure/sign/explosives/alt - name = "\improper HIGH EXPLOSIVES" - desc = "A warning sign which reads 'HIGH EXPLOSIVES'." - icon_state = "explosives2" \ No newline at end of file +/obj/structure/sign + icon = 'icons/obj/decals.dmi' + anchored = 1 + opacity = 0 + density = 0 + layer = 3.5 + max_integrity = 100 + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + +/obj/structure/sign/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src.loc, 'sound/weapons/slash.ogg', 80, TRUE) + else + playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(loc, 'sound/items/welder.ogg', 80, TRUE) + +/obj/structure/sign/screwdriver_act(mob/user, obj/item/I) + if(istype(src, /obj/structure/sign/double)) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You unfasten the sign with [I].") + var/obj/item/sign/S = new(src.loc) + S.name = name + S.desc = desc + S.icon_state = icon_state + //var/icon/I = icon('icons/obj/decals.dmi', icon_state) + //S.icon = I.Scale(24, 24) + S.sign_state = icon_state + qdel(src) + + +/obj/item/sign + name = "sign" + desc = "" + icon = 'icons/obj/decals.dmi' + w_class = WEIGHT_CLASS_NORMAL + resistance_flags = FLAMMABLE + var/sign_state = "" + +/obj/item/sign/attackby(obj/item/tool as obj, mob/user as mob) //construction + if(istype(tool, /obj/item/screwdriver) && isturf(user.loc)) + var/direction = input("In which direction?", "Select direction.") in list("North", "East", "South", "West", "Cancel") + if(direction == "Cancel") + return + if(QDELETED(src)) + return + var/obj/structure/sign/S = new(user.loc) + switch(direction) + if("North") + S.pixel_y = 32 + if("East") + S.pixel_x = 32 + if("South") + S.pixel_y = -32 + if("West") + S.pixel_x = -32 + else + return + S.name = name + S.desc = desc + S.icon_state = sign_state + to_chat(user, "You fasten \the [S] with your [tool].") + qdel(src) + else + return ..() + +/obj/structure/sign/double/map + name = "station map" + desc = "A framed picture of the station." + max_integrity = 500 + +/obj/structure/sign/double/map/left + icon_state = "map-left" + +/obj/structure/sign/double/map/right + icon_state = "map-right" + +/obj/structure/sign/securearea + name = "\improper SECURE AREA" + desc = "A warning sign which reads 'SECURE AREA'" + icon_state = "securearea" + +/obj/structure/sign/biohazard + name = "\improper BIOHAZARD" + desc = "A warning sign which reads 'BIOHAZARD'" + icon_state = "bio" + +/obj/structure/sign/electricshock + name = "\improper HIGH VOLTAGE" + desc = "A warning sign which reads 'HIGH VOLTAGE'" + icon_state = "shock" + +/obj/structure/sign/examroom + name = "\improper EXAM" + desc = "A guidance sign which reads 'EXAM ROOM'" + icon_state = "examroom" + +/obj/structure/sign/vacuum + name = "\improper HARD VACUUM AHEAD" + desc = "A warning sign which reads 'HARD VACUUM AHEAD'" + icon_state = "space" + +/obj/structure/sign/vacuum/external + name = "\improper EXTERNAL AIRLOCK" + desc = "A warning sign which reads 'EXTERNAL AIRLOCK'." + layer = MOB_LAYER + +/obj/structure/sign/deathsposal + name = "\improper DISPOSAL LEADS TO SPACE" + desc = "A warning sign which reads 'DISPOSAL LEADS TO SPACE'" + icon_state = "deathsposal" + +/obj/structure/sign/pods + name = "\improper ESCAPE PODS" + desc = "A warning sign which reads 'ESCAPE PODS'" + icon_state = "pods" + +/obj/structure/sign/fire + name = "\improper DANGER: FIRE" + desc = "A warning sign which reads 'DANGER: FIRE'" + icon_state = "fire" + resistance_flags = FIRE_PROOF + +/obj/structure/sign/nosmoking_1 + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'" + icon_state = "nosmoking" + resistance_flags = FLAMMABLE + +/obj/structure/sign/nosmoking_2 + name = "\improper NO SMOKING" + desc = "A warning sign which reads 'NO SMOKING'" + icon_state = "nosmoking2" + +/obj/structure/sign/radiation + name = "\improper HAZARDOUS RADIATION" + desc = "A warning sign alerting the user of potential radiation hazards." + icon_state = "radiation" + +/obj/structure/sign/radiation/rad_area + name = "\improper RADIOACTIVE AREA" + desc = "A warning sign which reads 'RADIOACTIVE AREA'." + +/obj/structure/sign/xeno_warning_mining + name = "DANGEROUS ALIEN LIFE" + desc = "A sign that warns would be travellers of hostile alien life in the vicinity." + icon = 'icons/obj/mining.dmi' + icon_state = "xeno_warning" + +/obj/structure/sign/redcross + name = "medbay" + desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here.'" + icon_state = "redcross" + +/obj/structure/sign/greencross + name = "medbay" + desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here.'" + icon_state = "greencross" + +/obj/structure/sign/goldenplaque + name = "The Most Robust Men Award for Robustness" + desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends." + icon_state = "goldenplaque" + +/obj/structure/sign/kiddieplaque + name = "AI developers plaque" + desc = "Next to the extremely long list of names and job titles, there is a drawing of a little child. The child appears to be retarded. Beneath the image, someone has scratched the word \"PACKETS\"." + icon_state = "kiddieplaque" + +/obj/structure/sign/atmosplaque + name = "\improper ZAS Atmospherics Division plaque" + desc = "This plaque commemorates the fall of the Atmos ZAS division. For all the charred, dizzy, and brittle men who have died in its horrible hands." + icon_state = "atmosplaque" + +/obj/structure/sign/kidanplaque + name = "Kidan wall trophy" + desc = "A dead and stuffed Diona nymph, mounted on a board." + icon_state = "kidanplaque" + +/obj/structure/sign/mech + name = "\improper mech painting" + desc = "A painting of a mech" + icon_state = "mech" + +/obj/structure/sign/nuke + name = "\improper nuke painting" + desc = "A painting of a nuke" + icon_state = "nuke" + +/obj/structure/sign/clown + name = "\improper clown painting" + desc = "A painting of the clown and mime. Awwww." + icon_state = "clown" + +/obj/structure/sign/bobross + name = "\improper calming painting" + desc = "We don't make mistakes, just happy little accidents." + icon_state = "bob" + +/obj/structure/sign/singulo + name = "\improper singulo painting" + desc = "A mesmerizing painting of a singularity. It seems to suck you in..." + icon_state = "singulo" + +/obj/structure/sign/barber + name = "\improper barber shop sign" + desc = "A spinning sign indicating a barbershop is near." + icon_state = "barber" + +/obj/structure/sign/chinese + name = "\improper chinese restaurant sign" + desc = "A glowing dragon invites you in." + icon_state = "chinese" + +/obj/structure/sign/science + name = "\improper SCIENCE!" + desc = "A warning sign which reads 'SCIENCE!'" + icon_state = "science1" + +/obj/structure/sign/chemistry + name = "\improper CHEMISTRY" + desc = "A warning sign which reads 'CHEMISTRY'" + icon_state = "chemistry1" + +/obj/structure/sign/botany + name = "\improper HYDROPONICS" + desc = "A warning sign which reads 'HYDROPONICS'" + icon_state = "hydro1" + +/obj/structure/sign/xenobio + name = "\improper XENOBIOLOGY" + desc = "A sign labelling an area as a place where xenobiological entities are researched." + icon_state = "xenobio" + +/obj/structure/sign/evac + name = "\improper EVACUATION" + desc = "A sign labelling an area where evacuation procedures take place." + icon_state = "evac" + +/obj/structure/sign/drop + name = "\improper DROP PODS" + desc = "A sign labelling an area where drop pod loading procedures take place." + icon_state = "drop" + +/obj/structure/sign/custodian + name = "\improper CUSTODIAN" + desc = "A sign labelling an area where the custodian works." + icon_state = "custodian" + +/obj/structure/sign/engineering + name = "\improper ENGINEERING" + desc = "A sign labelling an area where engineers work." + icon_state = "engine" + +/obj/structure/sign/cargo + name = "\improper CARGO" + desc = "A sign labelling an area where cargo ships dock." + icon_state = "cargo" + +/obj/structure/sign/security + name = "\improper SECURITY" + desc = "A sign labelling an area where the law is law." + icon_state = "security" + +/obj/structure/sign/holy + name = "\improper HOLY" + desc = "A sign labelling a religious area." + icon_state = "holy" + +/obj/structure/sign/restroom + name = "\improper RESTROOM" + desc = "A sign labelling a restroom." + icon_state = "restroom" + +/obj/structure/sign/medbay + name = "\improper MEDBAY" + desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here." + icon_state = "bluecross" + +/obj/structure/sign/medbay/alt + icon_state = "bluecross2" + +/obj/structure/sign/directions/science + name = "\improper Research Division" + desc = "A direction sign, pointing out which way the Research Division is." + icon_state = "direction_sci" + +/obj/structure/sign/directions/engineering + name = "\improper Engineering Department" + desc = "A direction sign, pointing out which way the Engineering department is." + icon_state = "direction_eng" + +/obj/structure/sign/directions/security + name = "\improper Security Department" + desc = "A direction sign, pointing out which way the Security department is." + icon_state = "direction_sec" + +/obj/structure/sign/directions/medical + name = "\improper Medical Bay" + desc = "A direction sign, pointing out which way Medical Bay is." + icon_state = "direction_med" + +/obj/structure/sign/directions/evac + name = "\improper Escape Arm" + desc = "A direction sign, pointing out which way escape shuttle dock is." + icon_state = "direction_evac" + +/obj/structure/sign/directions/cargo + name = "\improper Cargo Department" + desc = "A direction sign, pointing out which way the Cargo department is." + icon_state = "direction_supply" + +/obj/structure/sign/explosives + name = "\improper HIGH EXPLOSIVES" + desc = "A warning sign which reads 'HIGH EXPLOSIVES'." + icon_state = "explosives" + +/obj/structure/sign/explosives/alt + name = "\improper HIGH EXPLOSIVES" + desc = "A warning sign which reads 'HIGH EXPLOSIVES'." + icon_state = "explosives2" diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index b3b3157c3910..9a10507ba391 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -97,4 +97,4 @@ spawn_time = 600 mob_types = list(/mob/living/simple_animal/hostile/headcrab, /mob/living/simple_animal/hostile/headcrab/fast, /mob/living/simple_animal/hostile/headcrab/poison) spawn_text = "crawls out of" - faction = list("hostile") \ No newline at end of file + faction = list("hostile") diff --git a/code/game/objects/structures/spirit_board.dm b/code/game/objects/structures/spirit_board.dm index dc82b824e348..47731fd48d31 100644 --- a/code/game/objects/structures/spirit_board.dm +++ b/code/game/objects/structures/spirit_board.dm @@ -77,4 +77,4 @@ to_chat(M, "There aren't enough people to use the [src.name]!") return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm index 58c0aa32c9f8..80fd69597c4b 100644 --- a/code/game/objects/structures/statues.dm +++ b/code/game/objects/structures/statues.dm @@ -335,4 +335,4 @@ icon = 'icons/obj/decorations.dmi' icon_state = "chickenstatue" anchored = 1 - density = 1 \ No newline at end of file + density = 1 diff --git a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm index 6a2180efcac7..b303df8079a8 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/alien_nests.dm @@ -1,91 +1,91 @@ -//Alium nests. Essentially beds with an unbuckle delay that only aliums can buckle mobs to. - -/obj/structure/bed/nest - name = "alien nest" - desc = "It's a gruesome pile of thick, sticky resin shaped like a nest." - icon = 'icons/mob/alien.dmi' - icon_state = "nest" - max_integrity = 120 - var/image/nest_overlay - comfort = 0 - flags = NODECONSTRUCT - -/obj/structure/bed/nest/New() - nest_overlay = image('icons/mob/alien.dmi', "nestoverlay", layer=MOB_LAYER - 0.2) - return ..() - -/obj/structure/bed/nest/user_unbuckle_mob(mob/living/user) - if(has_buckled_mobs()) - for(var/buck in buckled_mobs) //breaking a nest releases all the buckled mobs, because the nest isn't holding them down anymore - var/mob/living/M = buck - - if(user.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) - unbuckle_mob(M) - add_fingerprint(user) - return - - if(M != user) - M.visible_message("[user.name] pulls [M.name] free from the sticky nest!",\ - "[user.name] pulls you free from the gelatinous resin.",\ - "You hear squelching...") - else - M.visible_message("[M.name] struggles to break free from the gelatinous resin!",\ - "You struggle to break free from the gelatinous resin... (Stay still for two minutes.)",\ - "You hear squelching...") - if(!do_after(M, 1200, target = src)) - if(M && M.buckled) - to_chat(M, "You fail to escape \the [src]!") - return - if(!M.buckled) - return - M.visible_message("[M.name] breaks free from the gelatinous resin!",\ - "You break free from the gelatinous resin!",\ - "You hear squelching...") - - unbuckle_mob(M) - add_fingerprint(user) - - -/obj/structure/bed/nest/user_buckle_mob(mob/living/M, mob/living/user) - if (!ismob(M) || (get_dist(src, user) > 1) || (M.loc != loc) || user.incapacitated() || M.buckled) - return - - if(M.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) - return - - if(!user.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) - return - - if(has_buckled_mobs()) - unbuckle_all_mobs() - - if(buckle_mob(M)) - M.visible_message("[user.name] secretes a thick vile goo, securing [M.name] into [src]!",\ - "[user.name] drenches you in a foul-smelling resin, trapping you in [src]!",\ - "You hear squelching...") - - -/obj/structure/bed/nest/post_buckle_mob(mob/living/M) - M.pixel_y = 0 - M.pixel_x = initial(M.pixel_x) + 2 - M.layer = BELOW_MOB_LAYER - add_overlay(nest_overlay) - -/obj/structure/bed/nest/post_unbuckle_mob(mob/living/M) - M.pixel_x = M.get_standard_pixel_x_offset(M.lying) - M.pixel_y = M.get_standard_pixel_y_offset(M.lying) - M.layer = initial(M.layer) - cut_overlay(nest_overlay) - -/obj/structure/bed/nest/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE) - if(BURN) - playsound(loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/bed/nest/attack_alien(mob/living/carbon/alien/user) - if(user.a_intent != INTENT_HARM) - return attack_hand(user) - else - return ..() \ No newline at end of file +//Alium nests. Essentially beds with an unbuckle delay that only aliums can buckle mobs to. + +/obj/structure/bed/nest + name = "alien nest" + desc = "It's a gruesome pile of thick, sticky resin shaped like a nest." + icon = 'icons/mob/alien.dmi' + icon_state = "nest" + max_integrity = 120 + var/image/nest_overlay + comfort = 0 + flags = NODECONSTRUCT + +/obj/structure/bed/nest/New() + nest_overlay = image('icons/mob/alien.dmi', "nestoverlay", layer=MOB_LAYER - 0.2) + return ..() + +/obj/structure/bed/nest/user_unbuckle_mob(mob/living/user) + if(has_buckled_mobs()) + for(var/buck in buckled_mobs) //breaking a nest releases all the buckled mobs, because the nest isn't holding them down anymore + var/mob/living/M = buck + + if(user.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) + unbuckle_mob(M) + add_fingerprint(user) + return + + if(M != user) + M.visible_message("[user.name] pulls [M.name] free from the sticky nest!",\ + "[user.name] pulls you free from the gelatinous resin.",\ + "You hear squelching...") + else + M.visible_message("[M.name] struggles to break free from the gelatinous resin!",\ + "You struggle to break free from the gelatinous resin... (Stay still for two minutes.)",\ + "You hear squelching...") + if(!do_after(M, 1200, target = src)) + if(M && M.buckled) + to_chat(M, "You fail to escape \the [src]!") + return + if(!M.buckled) + return + M.visible_message("[M.name] breaks free from the gelatinous resin!",\ + "You break free from the gelatinous resin!",\ + "You hear squelching...") + + unbuckle_mob(M) + add_fingerprint(user) + + +/obj/structure/bed/nest/user_buckle_mob(mob/living/M, mob/living/user) + if (!ismob(M) || (get_dist(src, user) > 1) || (M.loc != loc) || user.incapacitated() || M.buckled) + return + + if(M.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) + return + + if(!user.get_int_organ(/obj/item/organ/internal/xenos/plasmavessel)) + return + + if(has_buckled_mobs()) + unbuckle_all_mobs() + + if(buckle_mob(M)) + M.visible_message("[user.name] secretes a thick vile goo, securing [M.name] into [src]!",\ + "[user.name] drenches you in a foul-smelling resin, trapping you in [src]!",\ + "You hear squelching...") + + +/obj/structure/bed/nest/post_buckle_mob(mob/living/M) + M.pixel_y = 0 + M.pixel_x = initial(M.pixel_x) + 2 + M.layer = BELOW_MOB_LAYER + add_overlay(nest_overlay) + +/obj/structure/bed/nest/post_unbuckle_mob(mob/living/M) + M.pixel_x = M.get_standard_pixel_x_offset(M.lying) + M.pixel_y = M.get_standard_pixel_y_offset(M.lying) + M.layer = initial(M.layer) + cut_overlay(nest_overlay) + +/obj/structure/bed/nest/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE) + if(BURN) + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/bed/nest/attack_alien(mob/living/carbon/alien/user) + if(user.a_intent != INTENT_HARM) + return attack_hand(user) + else + return ..() diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index a837e632692d..9def5542c762 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -155,4 +155,4 @@ . = 0 else - . = 1 \ No newline at end of file + . = 1 diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index 1f7fd24d8033..5acd4f7d8a3c 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -1,777 +1,777 @@ -/* Tables and Racks - * Contains: - * Tables - * Glass Tables - * Wooden Tables - * Reinforced Tables - * Racks - * Rack Parts - */ - -/* - * Tables - */ - -/obj/structure/table - name = "table" - desc = "A square piece of metal standing on four metal legs. It can not move." - icon = 'icons/obj/smooth_structures/table.dmi' - icon_state = "table" - density = TRUE - anchored = TRUE - layer = TABLE_LAYER - pass_flags = LETPASSTHROW - climbable = TRUE - max_integrity = 100 - integrity_failure = 30 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced) - var/frame = /obj/structure/table_frame - var/framestack = /obj/item/stack/rods - var/buildstack = /obj/item/stack/sheet/metal - var/busy = FALSE - var/buildstackamount = 1 - var/framestackamount = 2 - var/deconstruction_ready = TRUE - var/flipped = 0 - -/obj/structure/table/New() - ..() - if(flipped) - update_icon() - -/obj/structure/table/examine(mob/user) - . = ..() - . += deconstruction_hints(user) - -/obj/structure/table/proc/deconstruction_hints(mob/user) - return "The top is screwed on, but the main bolts are also visible." - -/obj/structure/table/update_icon() - if(smooth && !flipped) - icon_state = "" - queue_smooth(src) - queue_smooth_neighbors(src) - - if(flipped) - clear_smooth_overlays() - - var/type = 0 - var/subtype = null - for(var/direction in list(turn(dir,90), turn(dir,-90)) ) - var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,direction)) - if(T && T.flipped) - type++ - if(type == 1) - subtype = direction == turn(dir,90) ? "-" : "+" - var/base = "table" - if(istype(src, /obj/structure/table/wood)) - base = "wood" - if(istype(src, /obj/structure/table/reinforced)) - base = "rtable" - - icon_state = "[base]flip[type][type == 1 ? subtype : ""]" - - return 1 - -/obj/structure/table/narsie_act() - new /obj/structure/table/wood(loc) - qdel(src) - -/obj/structure/table/ratvar_act() - new /obj/structure/table/reinforced/brass(loc) - qdel(src) - -/obj/structure/table/do_climb(mob/living/user) - . = ..() - item_placed(user) - -/obj/structure/table/attack_hand(mob/living/user) - ..() - if(climber) - climber.Weaken(2) - climber.visible_message("[climber.name] has been knocked off the table", "You've been knocked off the table", "You see [climber.name] get knocked off the table") - else if(Adjacent(user) && user.pulling && user.pulling.pass_flags & PASSTABLE) - user.Move_Pulled(src) - if(user.pulling.loc == loc) - user.visible_message("[user] places [user.pulling] onto [src].", - "You place [user.pulling] onto [src].") - user.stop_pulling() - -/obj/structure/table/attack_tk() // no telehulk sorry - return - -/obj/structure/table/proc/item_placed(item) - return - -/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0) - if(height == 0) - return 1 - if(istype(mover,/obj/item/projectile)) - return (check_cover(mover,target)) - if(ismob(mover)) - var/mob/M = mover - if(M.flying) - return 1 - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - if(mover.throwing) - return 1 - if(locate(/obj/structure/table) in get_turf(mover)) - return 1 - if(flipped) - if(get_dir(loc, target) == dir) - return !density - else - return 1 - return 0 - -/obj/structure/table/CanAStarPass(ID, dir, caller) - . = !density - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSTABLE) - -//checks if projectile 'P' from turf 'from' can hit whatever is behind the table. Returns 1 if it can, 0 if bullet stops. -/obj/structure/table/proc/check_cover(obj/item/projectile/P, turf/from) - var/turf/cover = flipped ? get_turf(src) : get_step(loc, get_dir(from, loc)) - if(get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close - return 1 - if(get_turf(P.original) == cover) - var/chance = 20 - if(ismob(P.original)) - var/mob/M = P.original - if(M.lying) - chance += 20 //Lying down lets you catch less bullets - if(flipped) - if(get_dir(loc, from) == dir) //Flipped tables catch mroe bullets - chance += 20 - else - return 1 //But only from one side - if(prob(chance)) - obj_integrity -= P.damage/2 - if(obj_integrity > 0) - visible_message("[P] hits \the [src]!") - return 0 - else - visible_message("[src] breaks down!") - deconstruct(FALSE) - return 1 - return 1 - -/obj/structure/table/CheckExit(atom/movable/O, turf/target) - if(istype(O) && O.checkpass(PASSTABLE)) - return 1 - if(flipped) - if(get_dir(loc, target) == dir) - return !density - else - return 1 - return 1 - -/obj/structure/table/MouseDrop_T(obj/O, mob/user) - ..() - if((!( istype(O, /obj/item) ) || user.get_active_hand() != O)) - return - if(isrobot(user)) - return - if(!user.drop_item()) - return - if(O.loc != src.loc) - step(O, get_dir(O, src)) - return - -/obj/structure/table/proc/tablepush(obj/item/grab/G, mob/user) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "Throwing [G.affecting] onto the table might hurt them!") - return - if(get_dist(src, user) < 2) - if(G.affecting.buckled) - to_chat(user, "[G.affecting] is buckled to [G.affecting.buckled]!") - return FALSE - if(G.state < GRAB_AGGRESSIVE) - to_chat(user, "You need a better grip to do that!") - return FALSE - if(!G.confirm()) - return FALSE - var/blocking_object = density_check() - if(blocking_object) - to_chat(user, "You cannot do this there is \a [blocking_object] in the way!") - return FALSE - G.affecting.forceMove(get_turf(src)) - G.affecting.Weaken(2) - item_placed(G.affecting) - G.affecting.visible_message("[G.assailant] pushes [G.affecting] onto [src].", \ - "[G.assailant] pushes [G.affecting] onto [src].") - add_attack_logs(G.assailant, G.affecting, "Pushed onto a table") - qdel(G) - return TRUE - qdel(G) - -/obj/structure/table/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/grab)) - tablepush(I, user) - return - - if(isrobot(user)) - return - - if(user.a_intent != INTENT_HARM && !(I.flags & ABSTRACT)) - if(user.drop_item()) - I.Move(loc) - var/list/click_params = params2list(params) - //Center the icon where the user clicked. - if(!click_params || !click_params["icon-x"] || !click_params["icon-y"]) - return - //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf) - I.pixel_x = Clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2) - I.pixel_y = Clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2) - item_placed(I) - else - return ..() - - -/obj/structure/table/screwdriver_act(mob/user, obj/item/I) - if(flags & NODECONSTRUCT) - return - if(!deconstruction_ready) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(I.use_tool(src, user, 20, volume = I.tool_volume) && deconstruction_ready) - deconstruct(TRUE) - TOOL_DISMANTLE_SUCCESS_MESSAGE - -/obj/structure/table/wrench_act(mob/user, obj/item/I) - if(flags & NODECONSTRUCT) - return - if(!deconstruction_ready) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume) && deconstruction_ready) - deconstruct(TRUE, 1) - TOOL_DISMANTLE_SUCCESS_MESSAGE - -/obj/structure/table/deconstruct(disassembled = TRUE, wrench_disassembly = 0) - if(!(flags & NODECONSTRUCT)) - var/turf/T = get_turf(src) - new buildstack(T, buildstackamount) - if(!wrench_disassembly) - new frame(T) - else - new framestack(T, framestackamount) - qdel(src) - -/obj/structure/table/proc/straight_table_check(direction) - var/obj/structure/table/T - for(var/angle in list(-90,90)) - T = locate() in get_step(loc,turn(direction,angle)) - if(T && !T.flipped) - return 0 - T = locate() in get_step(loc,direction) - if(!T || T.flipped) - return 1 - if(istype(T,/obj/structure/table/reinforced/)) - if(!T.deconstruction_ready) - return 0 - return T.straight_table_check(direction) - -/obj/structure/table/verb/do_flip() - set name = "Flip table" - set desc = "Flips a non-reinforced table" - set category = null - set src in oview(1) - - if(!can_touch(usr) || ismouse(usr)) - return - - if(!flip(get_cardinal_dir(usr,src))) - to_chat(usr, "It won't budge.") - return - - usr.visible_message("[usr] flips \the [src]!") - - if(climbable) - structure_shaken() - - return - -/obj/structure/table/proc/do_put() - set name = "Put table back" - set desc = "Puts flipped table back" - set category = "Object" - set src in oview(1) - - if(!can_touch(usr) || ismouse(usr)) - return - - if(!unflip()) - to_chat(usr, "It won't budge.") - return - - -/obj/structure/table/proc/flip(direction) - if(flipped) - return 0 - - if( !straight_table_check(turn(direction,90)) || !straight_table_check(turn(direction,-90)) ) - return 0 - - verbs -=/obj/structure/table/verb/do_flip - verbs +=/obj/structure/table/proc/do_put - - var/list/targets = list(get_step(src,dir),get_step(src,turn(dir, 45)),get_step(src,turn(dir, -45))) - for(var/atom/movable/A in get_turf(src)) - if(!A.anchored) - spawn(0) - A.throw_at(pick(targets),1,1) - - dir = direction - if(dir != NORTH) - layer = 5 - flipped = 1 - smooth = SMOOTH_FALSE - flags |= ON_BORDER - for(var/D in list(turn(direction, 90), turn(direction, -90))) - if(locate(/obj/structure/table,get_step(src,D))) - var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,D)) - T.flip(direction) - update_icon() - - return 1 - -/obj/structure/table/proc/unflip() - if(!flipped) - return 0 - - var/can_flip = 1 - for(var/mob/A in oview(src,0))//loc) - if(istype(A)) - can_flip = 0 - if(!can_flip) - return 0 - - verbs -=/obj/structure/table/proc/do_put - verbs +=/obj/structure/table/verb/do_flip - - layer = initial(layer) - flipped = 0 - smooth = initial(smooth) - flags &= ~ON_BORDER - for(var/D in list(turn(dir, 90), turn(dir, -90))) - if(locate(/obj/structure/table,get_step(src,D))) - var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,D)) - T.unflip() - update_icon() - - return 1 - - -/* - * Glass Tables - */ - -/obj/structure/table/glass - name = "glass table" - desc = "Looks fragile. You should totally flip it. It is begging for it." - icon = 'icons/obj/smooth_structures/glass_table.dmi' - icon_state = "glass_table" - buildstack = /obj/item/stack/sheet/glass - canSmoothWith = null - max_integrity = 70 - resistance_flags = ACID_PROOF - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - var/list/debris = list() - -/obj/structure/table/glass/New() - . = ..() - debris += new frame - debris += new /obj/item/shard - -/obj/structure/table/glass/Destroy() - for(var/i in debris) - qdel(i) - . = ..() - -/obj/structure/table/glass/Crossed(atom/movable/AM, oldloc) - . = ..() - if(flags & NODECONSTRUCT) - return - if(!isliving(AM)) - return - // Don't break if they're just flying past - if(AM.throwing) - addtimer(CALLBACK(src, .proc/throw_check, AM), 5) - else - check_break(AM) - -/obj/structure/table/glass/proc/throw_check(mob/living/M) - if(M.loc == get_turf(src)) - check_break(M) - -/obj/structure/table/glass/proc/check_break(mob/living/M) - if(has_gravity(M) && M.mob_size > MOB_SIZE_SMALL) - table_shatter(M) - -/obj/structure/table/glass/flip(direction) - deconstruct(FALSE) - -/obj/structure/table/glass/proc/table_shatter(mob/living/L) - visible_message("[src] breaks!", - "You hear breaking glass.") - var/turf/T = get_turf(src) - playsound(T, "shatter", 50, 1) - for(var/I in debris) - var/atom/movable/AM = I - AM.forceMove(T) - debris -= AM - if(istype(AM, /obj/item/shard)) - AM.throw_impact(L) - L.Weaken(5) - qdel(src) - -/obj/structure/table/glass/deconstruct(disassembled = TRUE, wrench_disassembly = 0) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - ..() - return - else - var/turf/T = get_turf(src) - playsound(T, "shatter", 50, TRUE) - for(var/X in debris) - var/atom/movable/AM = X - AM.forceMove(T) - debris -= AM - qdel(src) - -/obj/structure/table/glass/narsie_act() - color = NARSIE_WINDOW_COLOUR - for(var/obj/item/shard/S in debris) - S.color = NARSIE_WINDOW_COLOUR - -/* - * Wooden tables - */ -/obj/structure/table/wood - name = "wooden table" - desc = "Do not apply fire to this. Rumour says it burns easily." - icon = 'icons/obj/smooth_structures/wood_table.dmi' - icon_state = "wood_table" - frame = /obj/structure/table_frame/wood - framestack = /obj/item/stack/sheet/wood - buildstack = /obj/item/stack/sheet/wood - max_integrity = 70 - canSmoothWith = list(/obj/structure/table/wood, /obj/structure/table/wood/poker) - resistance_flags = FLAMMABLE - -/obj/structure/table/wood/narsie_act(total_override = TRUE) - if(!total_override) - ..() - -/obj/structure/table/wood/poker //No specialties, Just a mapping object. - name = "gambling table" - desc = "A seedy table for seedy dealings in seedy places." - icon = 'icons/obj/smooth_structures/poker_table.dmi' - icon_state = "poker_table" - buildstack = /obj/item/stack/tile/carpet - -/obj/structure/table/wood/poker/narsie_act() - ..(FALSE) - -/* - * Fancy Tables - */ - -/obj/structure/table/wood/fancy - name = "fancy table" - desc = "A standard metal table frame covered with an amazingly fancy, patterned cloth." - icon = 'icons/obj/structures.dmi' - icon_state = "fancy_table" - frame = /obj/structure/table_frame - framestack = /obj/item/stack/rods - buildstack = /obj/item/stack/tile/carpet - canSmoothWith = list(/obj/structure/table/wood/fancy, /obj/structure/table/wood/fancy/black) - -/obj/structure/table/wood/fancy/New() - icon = 'icons/obj/smooth_structures/fancy_table.dmi' //so that the tables place correctly in the map editor - ..() - -/obj/structure/table/wood/fancy/black - icon_state = "fancy_table_black" - buildstack = /obj/item/stack/tile/carpet/black - -/obj/structure/table/wood/fancy/black/New() - ..() - icon = 'icons/obj/smooth_structures/fancy_table_black.dmi' //so that the tables place correctly in the map editor - -/* - * Reinforced tables - */ -/obj/structure/table/reinforced - name = "reinforced table" - desc = "A reinforced version of the four legged table." - icon = 'icons/obj/smooth_structures/reinforced_table.dmi' - icon_state = "r_table" - deconstruction_ready = FALSE - buildstack = /obj/item/stack/sheet/plasteel - canSmoothWith = list(/obj/structure/table/reinforced, /obj/structure/table) - max_integrity = 200 - integrity_failure = 50 - armor = list("melee" = 10, "bullet" = 30, "laser" = 30, "energy" = 100, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - -/obj/structure/table/reinforced/deconstruction_hints(mob/user) - if(deconstruction_ready) - to_chat(user, "The top cover has been welded loose and the main frame's bolts are exposed.") - else - to_chat(user, "The top cover is firmly welded on.") - -/obj/structure/table/reinforced/flip(direction) - if(!deconstruction_ready) - return 0 - else - return ..() - -/obj/structure/table/reinforced/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - to_chat(user, "You start [deconstruction_ready ? "strengthening" : "weakening"] the reinforced table...") - if(I.use_tool(src, user, 50, volume = I.tool_volume)) - to_chat(user, "You [deconstruction_ready ? "strengthen" : "weaken"] the table.") - deconstruction_ready = !deconstruction_ready - -/obj/structure/table/reinforced/brass - name = "brass table" - desc = "A solid, slightly beveled brass table." - icon = 'icons/obj/smooth_structures/brass_table.dmi' - icon_state = "brass_table" - resistance_flags = FIRE_PROOF | ACID_PROOF - frame = /obj/structure/table_frame/brass - framestack = /obj/item/stack/tile/brass - buildstack = /obj/item/stack/tile/brass - framestackamount = 1 - buildstackamount = 1 - canSmoothWith = list(/obj/structure/table/reinforced/brass) - -/obj/structure/table/reinforced/brass/narsie_act() - take_damage(rand(15, 45), BRUTE) - if(src) //do we still exist? - var/previouscolor = color - color = "#960000" - animate(src, color = previouscolor, time = 8) - -/obj/structure/table/reinforced/brass/ratvar_act() - obj_integrity = max_integrity - -/obj/structure/table/tray - name = "surgical tray" - desc = "A small metal tray with wheels." - anchored = FALSE - smooth = SMOOTH_FALSE - icon = 'icons/obj/stationobjs.dmi' - icon_state = "tray" - buildstack = /obj/item/stack/sheet/mineral/titanium - buildstackamount = 2 - var/list/typecache_can_hold = list(/mob, /obj/item) - var/list/held_items = list() - -/obj/structure/table/tray/Initialize() - . = ..() - verbs -= /obj/structure/table/verb/do_flip - typecache_can_hold = typecacheof(typecache_can_hold) - for(var/atom/movable/held in get_turf(src)) - if(is_type_in_typecache(held, typecache_can_hold)) - held_items += held.UID() - -/obj/structure/table/tray/Move(NewLoc, direct) - var/atom/OldLoc = loc - - . = ..() - if(!.) // ..() will return 0 if we didn't actually move anywhere. - return . - - if(direct & (direct - 1)) // This represents a diagonal movement, which is split into multiple cardinal movements. We'll handle moving the items on the cardinals only. - return . - - playsound(loc, pick('sound/items/cartwheel1.ogg', 'sound/items/cartwheel2.ogg'), 100, 1, ignore_walls = FALSE) - - var/atom/movable/held - for(var/held_uid in held_items) - held = locateUID(held_uid) - if(!held) - held_items -= held_uid - continue - if(OldLoc != held.loc) - held_items -= held_uid - continue - held.forceMove(NewLoc) - -/obj/structure/table/tray/item_placed(atom/movable/item) - . = ..() - if(is_type_in_typecache(item, typecache_can_hold)) - held_items += item.UID() - -/obj/structure/table/tray/deconstruct(disassembled = TRUE, wrench_disassembly = 0) - if(!(flags & NODECONSTRUCT)) - var/turf/T = get_turf(src) - new buildstack(T, buildstackamount) - qdel(src) - -/obj/structure/table/tray/deconstruction_hints(mob/user) - to_chat(user, "It is held together by some screws and bolts.") - -/obj/structure/table/tray/flip() - return 0 - -/obj/structure/table/tray/narsie_act() - return 0 - -/obj/structure/table/tray/ratvar_act() - return 0 - -/* - * Racks - */ -/obj/structure/rack - name = "rack" - desc = "Different from the Middle Ages version." - icon = 'icons/obj/objects.dmi' - icon_state = "rack" - layer = TABLE_LAYER - density = TRUE - anchored = TRUE - pass_flags = LETPASSTHROW - max_integrity = 20 - -/obj/structure/rack/examine(mob/user) - . = ..() - . += "It's held together by a couple of bolts." - -/obj/structure/rack/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(density == 0) //Because broken racks -Agouri |TODO: SPRITE!| - return 1 - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - if(mover.throwing) - return 1 - else - return 0 - -/obj/structure/rack/CanAStarPass(ID, dir, caller) - . = !density - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSTABLE) - -/obj/structure/rack/MouseDrop_T(obj/O, mob/user) - if((!( istype(O, /obj/item) ) || user.get_active_hand() != O)) - return - if(isrobot(user)) - return - if(!user.drop_item()) - return - if(O.loc != src.loc) - step(O, get_dir(O, src)) - -/obj/structure/rack/attackby(obj/item/W, mob/user, params) - if(isrobot(user)) - return - if(user.a_intent == INTENT_HARM) - return ..() - if(!(W.flags & ABSTRACT)) - if(user.drop_item()) - W.Move(loc) - return - -/obj/structure/rack/wrench_act(mob/user, obj/item/I) - . = TRUE - if(flags & NODECONSTRUCT) - to_chat(user, "Try as you might, you can't figure out how to deconstruct this.") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - deconstruct(TRUE) - -/obj/structure/rack/attack_hand(mob/living/user) - if(user.IsWeakened() || user.resting || user.lying) - return - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src, ATTACK_EFFECT_KICK) - user.visible_message("[user] kicks [src].", \ - "You kick [src].") - take_damage(rand(4,8), BRUTE, "melee", 1) - -/obj/structure/rack/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(loc, 'sound/items/dodgeball.ogg', 80, TRUE) - else - playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(loc, 'sound/items/welder.ogg', 40, TRUE) - -/obj/structure/rack/skeletal_bar - name = "skeletal minibar" - desc = "Made with the skulls of the fallen." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "minibar" - -/obj/structure/rack/skeletal_bar/left - icon_state = "minibar_left" - -/obj/structure/rack/skeletal_bar/right - icon_state = "minibar_right" - -/* - * Rack destruction - */ - -/obj/structure/rack/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - density = FALSE - var/obj/item/rack_parts/newparts = new(loc) - transfer_fingerprints_to(newparts) - qdel(src) - -/* - * Rack Parts - */ - -/obj/item/rack_parts - name = "rack parts" - desc = "Parts of a rack." - icon = 'icons/obj/items.dmi' - icon_state = "rack_parts" - flags = CONDUCT - materials = list(MAT_METAL=2000) - var/building = FALSE - -/obj/item/rack_parts/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - new /obj/item/stack/sheet/metal(user.loc) - qdel(src) - -/obj/item/rack_parts/attack_self(mob/user) - if(building) - return - building = TRUE - to_chat(user, "You start constructing a rack...") - if(do_after(user, 50, target = user, progress=TRUE)) - if(!user.drop_item(src)) - return - var/obj/structure/rack/R = new /obj/structure/rack(user.loc) - user.visible_message("[user] assembles \a [R].\ - ", "You assemble \a [R].") - R.add_fingerprint(user) - qdel(src) - building = FALSE +/* Tables and Racks + * Contains: + * Tables + * Glass Tables + * Wooden Tables + * Reinforced Tables + * Racks + * Rack Parts + */ + +/* + * Tables + */ + +/obj/structure/table + name = "table" + desc = "A square piece of metal standing on four metal legs. It can not move." + icon = 'icons/obj/smooth_structures/table.dmi' + icon_state = "table" + density = TRUE + anchored = TRUE + layer = TABLE_LAYER + pass_flags = LETPASSTHROW + climbable = TRUE + max_integrity = 100 + integrity_failure = 30 + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced) + var/frame = /obj/structure/table_frame + var/framestack = /obj/item/stack/rods + var/buildstack = /obj/item/stack/sheet/metal + var/busy = FALSE + var/buildstackamount = 1 + var/framestackamount = 2 + var/deconstruction_ready = TRUE + var/flipped = 0 + +/obj/structure/table/New() + ..() + if(flipped) + update_icon() + +/obj/structure/table/examine(mob/user) + . = ..() + . += deconstruction_hints(user) + +/obj/structure/table/proc/deconstruction_hints(mob/user) + return "The top is screwed on, but the main bolts are also visible." + +/obj/structure/table/update_icon() + if(smooth && !flipped) + icon_state = "" + queue_smooth(src) + queue_smooth_neighbors(src) + + if(flipped) + clear_smooth_overlays() + + var/type = 0 + var/subtype = null + for(var/direction in list(turn(dir,90), turn(dir,-90)) ) + var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,direction)) + if(T && T.flipped) + type++ + if(type == 1) + subtype = direction == turn(dir,90) ? "-" : "+" + var/base = "table" + if(istype(src, /obj/structure/table/wood)) + base = "wood" + if(istype(src, /obj/structure/table/reinforced)) + base = "rtable" + + icon_state = "[base]flip[type][type == 1 ? subtype : ""]" + + return 1 + +/obj/structure/table/narsie_act() + new /obj/structure/table/wood(loc) + qdel(src) + +/obj/structure/table/ratvar_act() + new /obj/structure/table/reinforced/brass(loc) + qdel(src) + +/obj/structure/table/do_climb(mob/living/user) + . = ..() + item_placed(user) + +/obj/structure/table/attack_hand(mob/living/user) + ..() + if(climber) + climber.Weaken(2) + climber.visible_message("[climber.name] has been knocked off the table", "You've been knocked off the table", "You see [climber.name] get knocked off the table") + else if(Adjacent(user) && user.pulling && user.pulling.pass_flags & PASSTABLE) + user.Move_Pulled(src) + if(user.pulling.loc == loc) + user.visible_message("[user] places [user.pulling] onto [src].", + "You place [user.pulling] onto [src].") + user.stop_pulling() + +/obj/structure/table/attack_tk() // no telehulk sorry + return + +/obj/structure/table/proc/item_placed(item) + return + +/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0) + if(height == 0) + return 1 + if(istype(mover,/obj/item/projectile)) + return (check_cover(mover,target)) + if(ismob(mover)) + var/mob/M = mover + if(M.flying) + return 1 + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + if(mover.throwing) + return 1 + if(locate(/obj/structure/table) in get_turf(mover)) + return 1 + if(flipped) + if(get_dir(loc, target) == dir) + return !density + else + return 1 + return 0 + +/obj/structure/table/CanAStarPass(ID, dir, caller) + . = !density + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSTABLE) + +//checks if projectile 'P' from turf 'from' can hit whatever is behind the table. Returns 1 if it can, 0 if bullet stops. +/obj/structure/table/proc/check_cover(obj/item/projectile/P, turf/from) + var/turf/cover = flipped ? get_turf(src) : get_step(loc, get_dir(from, loc)) + if(get_dist(P.starting, loc) <= 1) //Tables won't help you if people are THIS close + return 1 + if(get_turf(P.original) == cover) + var/chance = 20 + if(ismob(P.original)) + var/mob/M = P.original + if(M.lying) + chance += 20 //Lying down lets you catch less bullets + if(flipped) + if(get_dir(loc, from) == dir) //Flipped tables catch mroe bullets + chance += 20 + else + return 1 //But only from one side + if(prob(chance)) + obj_integrity -= P.damage/2 + if(obj_integrity > 0) + visible_message("[P] hits \the [src]!") + return 0 + else + visible_message("[src] breaks down!") + deconstruct(FALSE) + return 1 + return 1 + +/obj/structure/table/CheckExit(atom/movable/O, turf/target) + if(istype(O) && O.checkpass(PASSTABLE)) + return 1 + if(flipped) + if(get_dir(loc, target) == dir) + return !density + else + return 1 + return 1 + +/obj/structure/table/MouseDrop_T(obj/O, mob/user) + ..() + if((!( istype(O, /obj/item) ) || user.get_active_hand() != O)) + return + if(isrobot(user)) + return + if(!user.drop_item()) + return + if(O.loc != src.loc) + step(O, get_dir(O, src)) + return + +/obj/structure/table/proc/tablepush(obj/item/grab/G, mob/user) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, "Throwing [G.affecting] onto the table might hurt them!") + return + if(get_dist(src, user) < 2) + if(G.affecting.buckled) + to_chat(user, "[G.affecting] is buckled to [G.affecting.buckled]!") + return FALSE + if(G.state < GRAB_AGGRESSIVE) + to_chat(user, "You need a better grip to do that!") + return FALSE + if(!G.confirm()) + return FALSE + var/blocking_object = density_check() + if(blocking_object) + to_chat(user, "You cannot do this there is \a [blocking_object] in the way!") + return FALSE + G.affecting.forceMove(get_turf(src)) + G.affecting.Weaken(2) + item_placed(G.affecting) + G.affecting.visible_message("[G.assailant] pushes [G.affecting] onto [src].", \ + "[G.assailant] pushes [G.affecting] onto [src].") + add_attack_logs(G.assailant, G.affecting, "Pushed onto a table") + qdel(G) + return TRUE + qdel(G) + +/obj/structure/table/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/grab)) + tablepush(I, user) + return + + if(isrobot(user)) + return + + if(user.a_intent != INTENT_HARM && !(I.flags & ABSTRACT)) + if(user.drop_item()) + I.Move(loc) + var/list/click_params = params2list(params) + //Center the icon where the user clicked. + if(!click_params || !click_params["icon-x"] || !click_params["icon-y"]) + return + //Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf) + I.pixel_x = Clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2) + I.pixel_y = Clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2) + item_placed(I) + else + return ..() + + +/obj/structure/table/screwdriver_act(mob/user, obj/item/I) + if(flags & NODECONSTRUCT) + return + if(!deconstruction_ready) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume) && deconstruction_ready) + deconstruct(TRUE) + TOOL_DISMANTLE_SUCCESS_MESSAGE + +/obj/structure/table/wrench_act(mob/user, obj/item/I) + if(flags & NODECONSTRUCT) + return + if(!deconstruction_ready) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume) && deconstruction_ready) + deconstruct(TRUE, 1) + TOOL_DISMANTLE_SUCCESS_MESSAGE + +/obj/structure/table/deconstruct(disassembled = TRUE, wrench_disassembly = 0) + if(!(flags & NODECONSTRUCT)) + var/turf/T = get_turf(src) + new buildstack(T, buildstackamount) + if(!wrench_disassembly) + new frame(T) + else + new framestack(T, framestackamount) + qdel(src) + +/obj/structure/table/proc/straight_table_check(direction) + var/obj/structure/table/T + for(var/angle in list(-90,90)) + T = locate() in get_step(loc,turn(direction,angle)) + if(T && !T.flipped) + return 0 + T = locate() in get_step(loc,direction) + if(!T || T.flipped) + return 1 + if(istype(T,/obj/structure/table/reinforced/)) + if(!T.deconstruction_ready) + return 0 + return T.straight_table_check(direction) + +/obj/structure/table/verb/do_flip() + set name = "Flip table" + set desc = "Flips a non-reinforced table" + set category = null + set src in oview(1) + + if(!can_touch(usr) || ismouse(usr)) + return + + if(!flip(get_cardinal_dir(usr,src))) + to_chat(usr, "It won't budge.") + return + + usr.visible_message("[usr] flips \the [src]!") + + if(climbable) + structure_shaken() + + return + +/obj/structure/table/proc/do_put() + set name = "Put table back" + set desc = "Puts flipped table back" + set category = "Object" + set src in oview(1) + + if(!can_touch(usr) || ismouse(usr)) + return + + if(!unflip()) + to_chat(usr, "It won't budge.") + return + + +/obj/structure/table/proc/flip(direction) + if(flipped) + return 0 + + if( !straight_table_check(turn(direction,90)) || !straight_table_check(turn(direction,-90)) ) + return 0 + + verbs -=/obj/structure/table/verb/do_flip + verbs +=/obj/structure/table/proc/do_put + + var/list/targets = list(get_step(src,dir),get_step(src,turn(dir, 45)),get_step(src,turn(dir, -45))) + for(var/atom/movable/A in get_turf(src)) + if(!A.anchored) + spawn(0) + A.throw_at(pick(targets),1,1) + + dir = direction + if(dir != NORTH) + layer = 5 + flipped = 1 + smooth = SMOOTH_FALSE + flags |= ON_BORDER + for(var/D in list(turn(direction, 90), turn(direction, -90))) + if(locate(/obj/structure/table,get_step(src,D))) + var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,D)) + T.flip(direction) + update_icon() + + return 1 + +/obj/structure/table/proc/unflip() + if(!flipped) + return 0 + + var/can_flip = 1 + for(var/mob/A in oview(src,0))//loc) + if(istype(A)) + can_flip = 0 + if(!can_flip) + return 0 + + verbs -=/obj/structure/table/proc/do_put + verbs +=/obj/structure/table/verb/do_flip + + layer = initial(layer) + flipped = 0 + smooth = initial(smooth) + flags &= ~ON_BORDER + for(var/D in list(turn(dir, 90), turn(dir, -90))) + if(locate(/obj/structure/table,get_step(src,D))) + var/obj/structure/table/T = locate(/obj/structure/table,get_step(src,D)) + T.unflip() + update_icon() + + return 1 + + +/* + * Glass Tables + */ + +/obj/structure/table/glass + name = "glass table" + desc = "Looks fragile. You should totally flip it. It is begging for it." + icon = 'icons/obj/smooth_structures/glass_table.dmi' + icon_state = "glass_table" + buildstack = /obj/item/stack/sheet/glass + canSmoothWith = null + max_integrity = 70 + resistance_flags = ACID_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + var/list/debris = list() + +/obj/structure/table/glass/New() + . = ..() + debris += new frame + debris += new /obj/item/shard + +/obj/structure/table/glass/Destroy() + for(var/i in debris) + qdel(i) + . = ..() + +/obj/structure/table/glass/Crossed(atom/movable/AM, oldloc) + . = ..() + if(flags & NODECONSTRUCT) + return + if(!isliving(AM)) + return + // Don't break if they're just flying past + if(AM.throwing) + addtimer(CALLBACK(src, .proc/throw_check, AM), 5) + else + check_break(AM) + +/obj/structure/table/glass/proc/throw_check(mob/living/M) + if(M.loc == get_turf(src)) + check_break(M) + +/obj/structure/table/glass/proc/check_break(mob/living/M) + if(has_gravity(M) && M.mob_size > MOB_SIZE_SMALL) + table_shatter(M) + +/obj/structure/table/glass/flip(direction) + deconstruct(FALSE) + +/obj/structure/table/glass/proc/table_shatter(mob/living/L) + visible_message("[src] breaks!", + "You hear breaking glass.") + var/turf/T = get_turf(src) + playsound(T, "shatter", 50, 1) + for(var/I in debris) + var/atom/movable/AM = I + AM.forceMove(T) + debris -= AM + if(istype(AM, /obj/item/shard)) + AM.throw_impact(L) + L.Weaken(5) + qdel(src) + +/obj/structure/table/glass/deconstruct(disassembled = TRUE, wrench_disassembly = 0) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + ..() + return + else + var/turf/T = get_turf(src) + playsound(T, "shatter", 50, TRUE) + for(var/X in debris) + var/atom/movable/AM = X + AM.forceMove(T) + debris -= AM + qdel(src) + +/obj/structure/table/glass/narsie_act() + color = NARSIE_WINDOW_COLOUR + for(var/obj/item/shard/S in debris) + S.color = NARSIE_WINDOW_COLOUR + +/* + * Wooden tables + */ +/obj/structure/table/wood + name = "wooden table" + desc = "Do not apply fire to this. Rumour says it burns easily." + icon = 'icons/obj/smooth_structures/wood_table.dmi' + icon_state = "wood_table" + frame = /obj/structure/table_frame/wood + framestack = /obj/item/stack/sheet/wood + buildstack = /obj/item/stack/sheet/wood + max_integrity = 70 + canSmoothWith = list(/obj/structure/table/wood, /obj/structure/table/wood/poker) + resistance_flags = FLAMMABLE + +/obj/structure/table/wood/narsie_act(total_override = TRUE) + if(!total_override) + ..() + +/obj/structure/table/wood/poker //No specialties, Just a mapping object. + name = "gambling table" + desc = "A seedy table for seedy dealings in seedy places." + icon = 'icons/obj/smooth_structures/poker_table.dmi' + icon_state = "poker_table" + buildstack = /obj/item/stack/tile/carpet + +/obj/structure/table/wood/poker/narsie_act() + ..(FALSE) + +/* + * Fancy Tables + */ + +/obj/structure/table/wood/fancy + name = "fancy table" + desc = "A standard metal table frame covered with an amazingly fancy, patterned cloth." + icon = 'icons/obj/structures.dmi' + icon_state = "fancy_table" + frame = /obj/structure/table_frame + framestack = /obj/item/stack/rods + buildstack = /obj/item/stack/tile/carpet + canSmoothWith = list(/obj/structure/table/wood/fancy, /obj/structure/table/wood/fancy/black) + +/obj/structure/table/wood/fancy/New() + icon = 'icons/obj/smooth_structures/fancy_table.dmi' //so that the tables place correctly in the map editor + ..() + +/obj/structure/table/wood/fancy/black + icon_state = "fancy_table_black" + buildstack = /obj/item/stack/tile/carpet/black + +/obj/structure/table/wood/fancy/black/New() + ..() + icon = 'icons/obj/smooth_structures/fancy_table_black.dmi' //so that the tables place correctly in the map editor + +/* + * Reinforced tables + */ +/obj/structure/table/reinforced + name = "reinforced table" + desc = "A reinforced version of the four legged table." + icon = 'icons/obj/smooth_structures/reinforced_table.dmi' + icon_state = "r_table" + deconstruction_ready = FALSE + buildstack = /obj/item/stack/sheet/plasteel + canSmoothWith = list(/obj/structure/table/reinforced, /obj/structure/table) + max_integrity = 200 + integrity_failure = 50 + armor = list("melee" = 10, "bullet" = 30, "laser" = 30, "energy" = 100, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + +/obj/structure/table/reinforced/deconstruction_hints(mob/user) + if(deconstruction_ready) + to_chat(user, "The top cover has been welded loose and the main frame's bolts are exposed.") + else + to_chat(user, "The top cover is firmly welded on.") + +/obj/structure/table/reinforced/flip(direction) + if(!deconstruction_ready) + return 0 + else + return ..() + +/obj/structure/table/reinforced/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + to_chat(user, "You start [deconstruction_ready ? "strengthening" : "weakening"] the reinforced table...") + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + to_chat(user, "You [deconstruction_ready ? "strengthen" : "weaken"] the table.") + deconstruction_ready = !deconstruction_ready + +/obj/structure/table/reinforced/brass + name = "brass table" + desc = "A solid, slightly beveled brass table." + icon = 'icons/obj/smooth_structures/brass_table.dmi' + icon_state = "brass_table" + resistance_flags = FIRE_PROOF | ACID_PROOF + frame = /obj/structure/table_frame/brass + framestack = /obj/item/stack/tile/brass + buildstack = /obj/item/stack/tile/brass + framestackamount = 1 + buildstackamount = 1 + canSmoothWith = list(/obj/structure/table/reinforced/brass) + +/obj/structure/table/reinforced/brass/narsie_act() + take_damage(rand(15, 45), BRUTE) + if(src) //do we still exist? + var/previouscolor = color + color = "#960000" + animate(src, color = previouscolor, time = 8) + +/obj/structure/table/reinforced/brass/ratvar_act() + obj_integrity = max_integrity + +/obj/structure/table/tray + name = "surgical tray" + desc = "A small metal tray with wheels." + anchored = FALSE + smooth = SMOOTH_FALSE + icon = 'icons/obj/stationobjs.dmi' + icon_state = "tray" + buildstack = /obj/item/stack/sheet/mineral/titanium + buildstackamount = 2 + var/list/typecache_can_hold = list(/mob, /obj/item) + var/list/held_items = list() + +/obj/structure/table/tray/Initialize() + . = ..() + verbs -= /obj/structure/table/verb/do_flip + typecache_can_hold = typecacheof(typecache_can_hold) + for(var/atom/movable/held in get_turf(src)) + if(is_type_in_typecache(held, typecache_can_hold)) + held_items += held.UID() + +/obj/structure/table/tray/Move(NewLoc, direct) + var/atom/OldLoc = loc + + . = ..() + if(!.) // ..() will return 0 if we didn't actually move anywhere. + return . + + if(direct & (direct - 1)) // This represents a diagonal movement, which is split into multiple cardinal movements. We'll handle moving the items on the cardinals only. + return . + + playsound(loc, pick('sound/items/cartwheel1.ogg', 'sound/items/cartwheel2.ogg'), 100, 1, ignore_walls = FALSE) + + var/atom/movable/held + for(var/held_uid in held_items) + held = locateUID(held_uid) + if(!held) + held_items -= held_uid + continue + if(OldLoc != held.loc) + held_items -= held_uid + continue + held.forceMove(NewLoc) + +/obj/structure/table/tray/item_placed(atom/movable/item) + . = ..() + if(is_type_in_typecache(item, typecache_can_hold)) + held_items += item.UID() + +/obj/structure/table/tray/deconstruct(disassembled = TRUE, wrench_disassembly = 0) + if(!(flags & NODECONSTRUCT)) + var/turf/T = get_turf(src) + new buildstack(T, buildstackamount) + qdel(src) + +/obj/structure/table/tray/deconstruction_hints(mob/user) + to_chat(user, "It is held together by some screws and bolts.") + +/obj/structure/table/tray/flip() + return 0 + +/obj/structure/table/tray/narsie_act() + return 0 + +/obj/structure/table/tray/ratvar_act() + return 0 + +/* + * Racks + */ +/obj/structure/rack + name = "rack" + desc = "Different from the Middle Ages version." + icon = 'icons/obj/objects.dmi' + icon_state = "rack" + layer = TABLE_LAYER + density = TRUE + anchored = TRUE + pass_flags = LETPASSTHROW + max_integrity = 20 + +/obj/structure/rack/examine(mob/user) + . = ..() + . += "It's held together by a couple of bolts." + +/obj/structure/rack/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(density == 0) //Because broken racks -Agouri |TODO: SPRITE!| + return 1 + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + if(mover.throwing) + return 1 + else + return 0 + +/obj/structure/rack/CanAStarPass(ID, dir, caller) + . = !density + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSTABLE) + +/obj/structure/rack/MouseDrop_T(obj/O, mob/user) + if((!( istype(O, /obj/item) ) || user.get_active_hand() != O)) + return + if(isrobot(user)) + return + if(!user.drop_item()) + return + if(O.loc != src.loc) + step(O, get_dir(O, src)) + +/obj/structure/rack/attackby(obj/item/W, mob/user, params) + if(isrobot(user)) + return + if(user.a_intent == INTENT_HARM) + return ..() + if(!(W.flags & ABSTRACT)) + if(user.drop_item()) + W.Move(loc) + return + +/obj/structure/rack/wrench_act(mob/user, obj/item/I) + . = TRUE + if(flags & NODECONSTRUCT) + to_chat(user, "Try as you might, you can't figure out how to deconstruct this.") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + deconstruct(TRUE) + +/obj/structure/rack/attack_hand(mob/living/user) + if(user.IsWeakened() || user.resting || user.lying) + return + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src, ATTACK_EFFECT_KICK) + user.visible_message("[user] kicks [src].", \ + "You kick [src].") + take_damage(rand(4,8), BRUTE, "melee", 1) + +/obj/structure/rack/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(loc, 'sound/items/dodgeball.ogg', 80, TRUE) + else + playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(loc, 'sound/items/welder.ogg', 40, TRUE) + +/obj/structure/rack/skeletal_bar + name = "skeletal minibar" + desc = "Made with the skulls of the fallen." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "minibar" + +/obj/structure/rack/skeletal_bar/left + icon_state = "minibar_left" + +/obj/structure/rack/skeletal_bar/right + icon_state = "minibar_right" + +/* + * Rack destruction + */ + +/obj/structure/rack/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + density = FALSE + var/obj/item/rack_parts/newparts = new(loc) + transfer_fingerprints_to(newparts) + qdel(src) + +/* + * Rack Parts + */ + +/obj/item/rack_parts + name = "rack parts" + desc = "Parts of a rack." + icon = 'icons/obj/items.dmi' + icon_state = "rack_parts" + flags = CONDUCT + materials = list(MAT_METAL=2000) + var/building = FALSE + +/obj/item/rack_parts/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new /obj/item/stack/sheet/metal(user.loc) + qdel(src) + +/obj/item/rack_parts/attack_self(mob/user) + if(building) + return + building = TRUE + to_chat(user, "You start constructing a rack...") + if(do_after(user, 50, target = user, progress=TRUE)) + if(!user.drop_item(src)) + return + var/obj/structure/rack/R = new /obj/structure/rack(user.loc) + user.visible_message("[user] assembles \a [R].\ + ", "You assemble \a [R].") + R.add_fingerprint(user) + qdel(src) + building = FALSE diff --git a/code/game/objects/structures/tank_dispenser.dm b/code/game/objects/structures/tank_dispenser.dm index 6a16305a299c..854bc18ce007 100644 --- a/code/game/objects/structures/tank_dispenser.dm +++ b/code/game/objects/structures/tank_dispenser.dm @@ -1,132 +1,132 @@ -/obj/structure/dispenser - name = "tank storage unit" - desc = "A simple yet bulky storage device for gas tanks. Has room for up to ten oxygen tanks, and ten plasma tanks." - icon = 'icons/obj/objects.dmi' - icon_state = "dispenser" - density = 1 - anchored = 1.0 - var/oxygentanks = 10 - var/plasmatanks = 10 - var/list/oxytanks = list() //sorry for the similar var names - var/list/platanks = list() - -/obj/structure/dispenser/oxygen - plasmatanks = 0 - -/obj/structure/dispenser/plasma - oxygentanks = 0 - -/obj/structure/dispenser/New() - ..() - update_icon() - -/obj/structure/dispenser/update_icon() - overlays.Cut() - switch(oxygentanks) - if(1 to 3) overlays += "oxygen-[oxygentanks]" - if(4 to INFINITY) overlays += "oxygen-4" - switch(plasmatanks) - if(1 to 4) overlays += "plasma-[plasmatanks]" - if(5 to INFINITY) overlays += "plasma-5" - -/obj/structure/dispenser/attack_hand(mob/user) - if(..()) - return 1 - add_fingerprint(user) - ui_interact(user) - -/obj/structure/dispenser/attack_ghost(mob/user) - ui_interact(user) - -/obj/structure/dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "tank_dispenser.tmpl", name, 275, 100, state = state) - ui.open() - -/obj/structure/dispenser/ui_data(user) - var/list/data = list() - data["o_tanks"] = oxygentanks - data["p_tanks"] = plasmatanks - return data - -/obj/structure/dispenser/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/tank/oxygen) || istype(I, /obj/item/tank/air) || istype(I, /obj/item/tank/anesthetic)) - if(oxygentanks < 10) - user.drop_item() - I.forceMove(src) - oxytanks.Add(I) - oxygentanks++ - update_icon() - to_chat(user, "You put [I] in [src].") - else - to_chat(user, "[src] is full.") - SSnanoui.update_uis(src) - return - if(istype(I, /obj/item/tank/plasma)) - if(plasmatanks < 10) - user.drop_item() - I.forceMove(src) - platanks.Add(I) - plasmatanks++ - update_icon() - to_chat(user, "You put [I] in [src].") - else - to_chat(user, "[src] is full.") - SSnanoui.update_uis(src) - return - if(istype(I, /obj/item/wrench)) - if(anchored) - to_chat(user, "You lean down and unwrench [src].") - anchored = 0 - else - to_chat(user, "You wrench [src] into place.") - anchored = 1 - return - return ..() - -/obj/structure/dispenser/Topic(href, href_list) - if(..()) - return 1 - - if(Adjacent(usr)) - usr.set_machine(src) - if(href_list["oxygen"]) - if(oxygentanks > 0) - var/obj/item/tank/oxygen/O - if(oxytanks.len == oxygentanks) - O = oxytanks[1] - oxytanks.Remove(O) - else - O = new /obj/item/tank/oxygen(loc) - O.loc = loc - to_chat(usr, "You take [O] out of [src].") - oxygentanks-- - update_icon() - if(href_list["plasma"]) - if(plasmatanks > 0) - var/obj/item/tank/plasma/P - if(platanks.len == plasmatanks) - P = platanks[1] - platanks.Remove(P) - else - P = new /obj/item/tank/plasma(loc) - P.loc = loc - to_chat(usr, "You take [P] out of [src].") - plasmatanks-- - update_icon() - add_fingerprint(usr) - updateUsrDialog() - SSnanoui.update_uis(src) - else - SSnanoui.close_user_uis(usr,src) - return 1 - -/obj/structure/tank_dispenser/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - for(var/X in src) - var/obj/item/I = X - I.forceMove(loc) - new /obj/item/stack/sheet/metal(loc, 2) - qdel(src) \ No newline at end of file +/obj/structure/dispenser + name = "tank storage unit" + desc = "A simple yet bulky storage device for gas tanks. Has room for up to ten oxygen tanks, and ten plasma tanks." + icon = 'icons/obj/objects.dmi' + icon_state = "dispenser" + density = 1 + anchored = 1.0 + var/oxygentanks = 10 + var/plasmatanks = 10 + var/list/oxytanks = list() //sorry for the similar var names + var/list/platanks = list() + +/obj/structure/dispenser/oxygen + plasmatanks = 0 + +/obj/structure/dispenser/plasma + oxygentanks = 0 + +/obj/structure/dispenser/New() + ..() + update_icon() + +/obj/structure/dispenser/update_icon() + overlays.Cut() + switch(oxygentanks) + if(1 to 3) overlays += "oxygen-[oxygentanks]" + if(4 to INFINITY) overlays += "oxygen-4" + switch(plasmatanks) + if(1 to 4) overlays += "plasma-[plasmatanks]" + if(5 to INFINITY) overlays += "plasma-5" + +/obj/structure/dispenser/attack_hand(mob/user) + if(..()) + return 1 + add_fingerprint(user) + ui_interact(user) + +/obj/structure/dispenser/attack_ghost(mob/user) + ui_interact(user) + +/obj/structure/dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "tank_dispenser.tmpl", name, 275, 100, state = state) + ui.open() + +/obj/structure/dispenser/ui_data(user) + var/list/data = list() + data["o_tanks"] = oxygentanks + data["p_tanks"] = plasmatanks + return data + +/obj/structure/dispenser/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/tank/oxygen) || istype(I, /obj/item/tank/air) || istype(I, /obj/item/tank/anesthetic)) + if(oxygentanks < 10) + user.drop_item() + I.forceMove(src) + oxytanks.Add(I) + oxygentanks++ + update_icon() + to_chat(user, "You put [I] in [src].") + else + to_chat(user, "[src] is full.") + SSnanoui.update_uis(src) + return + if(istype(I, /obj/item/tank/plasma)) + if(plasmatanks < 10) + user.drop_item() + I.forceMove(src) + platanks.Add(I) + plasmatanks++ + update_icon() + to_chat(user, "You put [I] in [src].") + else + to_chat(user, "[src] is full.") + SSnanoui.update_uis(src) + return + if(istype(I, /obj/item/wrench)) + if(anchored) + to_chat(user, "You lean down and unwrench [src].") + anchored = 0 + else + to_chat(user, "You wrench [src] into place.") + anchored = 1 + return + return ..() + +/obj/structure/dispenser/Topic(href, href_list) + if(..()) + return 1 + + if(Adjacent(usr)) + usr.set_machine(src) + if(href_list["oxygen"]) + if(oxygentanks > 0) + var/obj/item/tank/oxygen/O + if(oxytanks.len == oxygentanks) + O = oxytanks[1] + oxytanks.Remove(O) + else + O = new /obj/item/tank/oxygen(loc) + O.loc = loc + to_chat(usr, "You take [O] out of [src].") + oxygentanks-- + update_icon() + if(href_list["plasma"]) + if(plasmatanks > 0) + var/obj/item/tank/plasma/P + if(platanks.len == plasmatanks) + P = platanks[1] + platanks.Remove(P) + else + P = new /obj/item/tank/plasma(loc) + P.loc = loc + to_chat(usr, "You take [P] out of [src].") + plasmatanks-- + update_icon() + add_fingerprint(usr) + updateUsrDialog() + SSnanoui.update_uis(src) + else + SSnanoui.close_user_uis(usr,src) + return 1 + +/obj/structure/tank_dispenser/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + for(var/X in src) + var/obj/item/I = X + I.forceMove(loc) + new /obj/item/stack/sheet/metal(loc, 2) + qdel(src) diff --git a/code/game/objects/structures/target_stake.dm b/code/game/objects/structures/target_stake.dm index 79e9c6cfab13..30e6e13cdc69 100644 --- a/code/game/objects/structures/target_stake.dm +++ b/code/game/objects/structures/target_stake.dm @@ -1,54 +1,54 @@ -// Basically they are for the firing range -/obj/structure/target_stake - name = "target stake" - desc = "A thin platform with negatively-magnetized wheels." - icon = 'icons/obj/objects.dmi' - icon_state = "target_stake" - density = 1 - flags = CONDUCT - var/obj/item/target/pinned_target // the current pinned target - -/obj/structure/target_stake/Destroy() - QDEL_NULL(pinned_target) - return ..() - -/obj/structure/target_stake/Move() - . = ..() - // Move the pinned target along with the stake - if(pinned_target in view(3, src)) - pinned_target.loc = loc - - else // Sanity check: if the pinned target can't be found in immediate view - pinned_target = null - density = 1 - -/obj/structure/target_stake/attackby(obj/item/W, mob/user, params) - // Putting objects on the stake. Most importantly, targets - if(istype(W, /obj/item/target) && !pinned_target) - density = 0 - W.density = 1 - user.drop_item(src) - W.loc = loc - W.layer = 3.1 - pinned_target = W - to_chat(user, "You slide the target into the stake.") - return - return ..() - -/obj/structure/target_stake/attack_hand(mob/user) - // taking pinned targets off! - if(pinned_target) - density = 1 - pinned_target.density = 0 - pinned_target.layer = OBJ_LAYER - - pinned_target.loc = user.loc - if(ishuman(user)) - if(!user.get_active_hand()) - user.put_in_hands(pinned_target) - to_chat(user, "You take the target out of the stake.") - else - pinned_target.loc = get_turf(user) - to_chat(user, "You take the target out of the stake.") - - pinned_target = null \ No newline at end of file +// Basically they are for the firing range +/obj/structure/target_stake + name = "target stake" + desc = "A thin platform with negatively-magnetized wheels." + icon = 'icons/obj/objects.dmi' + icon_state = "target_stake" + density = 1 + flags = CONDUCT + var/obj/item/target/pinned_target // the current pinned target + +/obj/structure/target_stake/Destroy() + QDEL_NULL(pinned_target) + return ..() + +/obj/structure/target_stake/Move() + . = ..() + // Move the pinned target along with the stake + if(pinned_target in view(3, src)) + pinned_target.loc = loc + + else // Sanity check: if the pinned target can't be found in immediate view + pinned_target = null + density = 1 + +/obj/structure/target_stake/attackby(obj/item/W, mob/user, params) + // Putting objects on the stake. Most importantly, targets + if(istype(W, /obj/item/target) && !pinned_target) + density = 0 + W.density = 1 + user.drop_item(src) + W.loc = loc + W.layer = 3.1 + pinned_target = W + to_chat(user, "You slide the target into the stake.") + return + return ..() + +/obj/structure/target_stake/attack_hand(mob/user) + // taking pinned targets off! + if(pinned_target) + density = 1 + pinned_target.density = 0 + pinned_target.layer = OBJ_LAYER + + pinned_target.loc = user.loc + if(ishuman(user)) + if(!user.get_active_hand()) + user.put_in_hands(pinned_target) + to_chat(user, "You take the target out of the stake.") + else + pinned_target.loc = get_turf(user) + to_chat(user, "You take the target out of the stake.") + + pinned_target = null diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm index 04271015f4c7..f8f406d1381c 100644 --- a/code/game/objects/structures/transit_tubes/station.dm +++ b/code/game/objects/structures/transit_tubes/station.dm @@ -139,4 +139,4 @@ // Tube station directions are simply 90 to either side of // the exit. /obj/structure/transit_tube/station/init_dirs() - tube_dirs = list(turn(dir, 90), turn(dir, -90)) \ No newline at end of file + tube_dirs = list(turn(dir, 90), turn(dir, -90)) diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm index 6a06109277be..7095fd4eedad 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm @@ -174,4 +174,4 @@ if(dir in tube.directions()) if(tube.has_exit(direction)) dir = direction - return \ No newline at end of file + return diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 32c9daf3c76f..a529dc3a6e72 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -704,7 +704,7 @@ return if(proximity_flag != 1) //if we aren't next to the wall return - if(!(get_dir(on_wall, user) in cardinal)) + if(!(get_dir(on_wall, user) in GLOB.cardinal)) to_chat(user, "You need to be standing next to a wall to place \the [src].") return return 1 diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 90d20ecbd752..9faab45cf971 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -1,766 +1,766 @@ -var/global/wcBar = pick(list("#0d8395", "#58b5c3", "#58c366", "#90d79a", "#ffffff")) -var/global/wcBrig = pick(list("#aa0808", "#7f0606", "#ff0000")) -var/global/wcCommon = pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8fcf44", "#ffffff")) - -/obj/proc/color_windows(obj/W) - var/list/wcBarAreas = list(/area/crew_quarters/bar) - var/list/wcBrigAreas = list(/area/security, /area/shuttle/gamma) - - var/newcolor - var/turf/T = get_turf(W) - if(!istype(T)) - return - var/area/A = T.loc - - if(is_type_in_list(A,wcBarAreas)) - newcolor = wcBar - else if(is_type_in_list(A,wcBrigAreas)) - newcolor = wcBrig - else - newcolor = wcCommon - - return newcolor - -/obj/structure/window - name = "window" - desc = "A window." - icon_state = "window" - density = TRUE - layer = ABOVE_OBJ_LAYER //Just above doors - pressure_resistance = 4*ONE_ATMOSPHERE - anchored = TRUE - flags = ON_BORDER - can_be_unanchored = TRUE - max_integrity = 25 - resistance_flags = ACID_PROOF - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - var/ini_dir = null - var/state = WINDOW_OUT_OF_FRAME - var/reinf = FALSE - var/heat_resistance = 800 - var/decon_speed = null - var/fulltile = FALSE - var/shardtype = /obj/item/shard - var/glass_type = /obj/item/stack/sheet/glass - var/glass_amount = 1 - var/cancolor = FALSE - var/image/crack_overlay - var/list/debris = list() - var/real_explosion_block //ignore this, just use explosion_block - var/breaksound = "shatter" - var/hitsound = 'sound/effects/Glasshit.ogg' - -/obj/structure/window/examine(mob/user) - . = ..() - if(reinf) - if(anchored && state == WINDOW_SCREWED_TO_FRAME) - . += "The window is screwed to the frame." - else if(anchored && state == WINDOW_IN_FRAME) - . += "The window is unscrewed but pried into the frame." - else if(anchored && state == WINDOW_OUT_OF_FRAME) - . += "The window is out of the frame, but could be pried in. It is screwed to the floor." - else if(!anchored) - . += "The window is unscrewed from the floor, and could be deconstructed by wrenching." - else - if(anchored) - . += "The window is screwed to the floor." - else - . += "The window is unscrewed from the floor, and could be deconstructed by wrenching." - if(!anchored && !fulltile) - . += "Alt-click to rotate it." - -/obj/structure/window/New(Loc, direct) - ..() - if(direct) - setDir(direct) - if(reinf && anchored) - state = WINDOW_SCREWED_TO_FRAME - - ini_dir = dir - - if(!color && cancolor) - color = color_windows(src) - - // Precreate our own debris - - var/shards = 1 - if(fulltile) - shards++ - setDir() - - if(decon_speed == null) - if(fulltile) - decon_speed = 20 - else - decon_speed = 1 - - var/rods = 0 - if(reinf) - rods++ - if(fulltile) - rods++ - - for(var/i in 1 to shards) - debris += new shardtype(src) - if(rods) - debris += new /obj/item/stack/rods(src, rods) - - //windows only block while reinforced and fulltile, so we'll use the proc - real_explosion_block = explosion_block - explosion_block = EXPLOSION_BLOCK_PROC - -/obj/structure/window/Initialize() - air_update_turf(1) - return ..() - -/obj/structure/window/narsie_act() - color = NARSIE_WINDOW_COLOUR - for(var/obj/item/shard/shard in debris) - shard.color = NARSIE_WINDOW_COLOUR - -/obj/structure/window/ratvar_act() - if(!fulltile) - new/obj/structure/window/reinforced/clockwork(get_turf(src), dir) - else - new/obj/structure/window/reinforced/clockwork/fulltile(get_turf(src)) - qdel(src) - -/obj/structure/window/rpd_act() - return - -/obj/structure/window/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct(FALSE) - -/obj/structure/window/setDir(direct) - if(!fulltile) - ..() - else - ..(FULLTILE_WINDOW_DIR) - -/obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSGLASS)) - return 1 - if(dir == FULLTILE_WINDOW_DIR) - return 0 //full tile window, you can't move into it! - if(get_dir(loc, target) == dir) - return !density - if(istype(mover, /obj/structure/window)) - var/obj/structure/window/W = mover - if(!valid_window_location(loc, W.ini_dir)) - return FALSE - else if(istype(mover, /obj/structure/windoor_assembly)) - var/obj/structure/windoor_assembly/W = mover - if(!valid_window_location(loc, W.ini_dir)) - return FALSE - else if(istype(mover, /obj/machinery/door/window) && !valid_window_location(loc, mover.dir)) - return FALSE - return 1 - -/obj/structure/window/CheckExit(atom/movable/O, target) - if(istype(O) && O.checkpass(PASSGLASS)) - return 1 - if(get_dir(O.loc, target) == dir) - return 0 - return 1 - -/obj/structure/window/CanAStarPass(ID, to_dir) - if(!density) - return 1 - if((dir == FULLTILE_WINDOW_DIR) || (dir == to_dir)) - return 0 - - return 1 - -/obj/structure/window/attack_tk(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - user.visible_message("Something knocks on [src].") - add_fingerprint(user) - playsound(src, 'sound/effects/glassknock.ogg', 50, 1) - -/obj/structure/window/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) - if(!can_be_reached(user)) - return 1 - . = ..() - -/obj/structure/window/attack_hand(mob/user) - if(!can_be_reached(user)) - return - if(user.a_intent == INTENT_HARM) - user.changeNext_move(CLICK_CD_MELEE) - playsound(src, 'sound/effects/glassknock.ogg', 80, 1) - user.visible_message("[user] bangs against [src]!", \ - "You bang against [src]!", \ - "You hear a banging sound.") - add_fingerprint(user) - else - user.changeNext_move(CLICK_CD_MELEE) - playsound(src, 'sound/effects/glassknock.ogg', 80, 1) - user.visible_message("[user] knocks on [src].", \ - "You knock on [src].", \ - "You hear a knocking sound.") - add_fingerprint(user) - -/obj/structure/window/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) //used by attack_alien, attack_animal, and attack_slime - if(!can_be_reached(user)) - return - ..() - -/obj/structure/window/attackby(obj/item/I, mob/living/user, params) - if(!can_be_reached(user)) - return 1 //skip the afterattack - - add_fingerprint(user) - if(istype(I, /obj/item/grab) && get_dist(src, user) < 2) - var/obj/item/grab/G = I - if(isliving(G.affecting)) - var/mob/living/M = G.affecting - var/state = G.state - qdel(I) //gotta delete it here because if window breaks, it won't get deleted - switch(state) - if(1) - M.visible_message("[user] slams [M] against \the [src]!") - M.apply_damage(7) - take_damage(10) - if(2) - M.visible_message("[user] bashes [M] against \the [src]!") - if(prob(50)) - M.Weaken(1) - M.apply_damage(10) - take_damage(25) - if(3) - M.visible_message("[user] crushes [M] against \the [src]!") - M.Weaken(5) - M.apply_damage(20) - take_damage(50) - if(4) - visible_message("[user] smashes [M] against \the [src]!") - M.Weaken(5) - M.apply_damage(30) - take_damage(75) - return - return ..() - - -/obj/structure/window/crowbar_act(mob/user, obj/item/I) - if(!reinf) - return - if(state != WINDOW_OUT_OF_FRAME && state != WINDOW_IN_FRAME) - return - if(flags & NODECONSTRUCT) - return - . = TRUE - if(!can_be_reached(user)) - return - to_chat(user, "You begin to lever the window [state == WINDOW_OUT_OF_FRAME ? "into":"out of"] the frame...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) - return - state = (state == WINDOW_OUT_OF_FRAME ? WINDOW_IN_FRAME : WINDOW_OUT_OF_FRAME) - to_chat(user, "You pry the window [state == WINDOW_IN_FRAME ? "into":"out of"] the frame.") - -/obj/structure/window/screwdriver_act(mob/user, obj/item/I) - if(flags & NODECONSTRUCT) - return - . = TRUE - if(!can_be_reached(user)) - return - if(reinf) - if(state == WINDOW_SCREWED_TO_FRAME || state == WINDOW_IN_FRAME) - to_chat(user, "You begin to [state == WINDOW_SCREWED_TO_FRAME ? "unscrew the window from":"screw the window to"] the frame...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) - return - state = (state == WINDOW_IN_FRAME ? WINDOW_SCREWED_TO_FRAME : WINDOW_IN_FRAME) - to_chat(user, "You [state == WINDOW_IN_FRAME ? "unfasten the window from":"fasten the window to"] the frame.") - else if(state == WINDOW_OUT_OF_FRAME) - to_chat(user, "You begin to [anchored ? "unscrew the frame from":"screw the frame to"] the floor...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) - return - anchored = !anchored - update_nearby_icons() - to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.") - else //if we're not reinforced, we don't need to check or update state - to_chat(user, "You begin to [anchored ? "unscrew the window from":"screw the window to"] the floor...") - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_anchored, anchored))) - return - anchored = !anchored - air_update_turf(TRUE) - update_nearby_icons() - to_chat(user, "You [anchored ? "fasten the window to":"unfasten the window from"] the floor.") - -/obj/structure/window/wrench_act(mob/user, obj/item/I) - if(flags & NODECONSTRUCT) - return - if(anchored) - return - . = TRUE - if(!can_be_reached(user)) - return - TOOL_ATTEMPT_DISMANTLE_MESSAGE - if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) - return - var/obj/item/stack/sheet/G = new glass_type(user.loc, glass_amount) - G.add_fingerprint(user) - playsound(src, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You successfully disassemble [src].") - qdel(src) - -/obj/structure/window/welder_act(mob/user, obj/item/I) - if(user.a_intent != INTENT_HELP) - return - . = TRUE - if(!can_be_reached(user)) - return - if(obj_integrity >= max_integrity) - to_chat(user, "[src] is already in good condition!") - return - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_REPAIR_MESSAGE - if(I.use_tool(src, user, 40, volume = I.tool_volume)) - obj_integrity = max_integrity - WELDER_REPAIR_SUCCESS_MESSAGE - -/obj/structure/window/proc/check_state(checked_state) - if(state == checked_state) - return TRUE - -/obj/structure/window/proc/check_anchored(checked_anchored) - if(anchored == checked_anchored) - return TRUE - -/obj/structure/window/proc/check_state_and_anchored(checked_state, checked_anchored) - return check_state(checked_state) && check_anchored(checked_anchored) - -/obj/structure/window/mech_melee_attack(obj/mecha/M) - if(!can_be_reached()) - return - ..() - -/obj/structure/window/proc/can_be_reached(mob/user) - if(!fulltile) - if(get_dir(user, src) & dir) - for(var/obj/O in loc) - if(!O.CanPass(user, user.loc, 1)) - return 0 - return 1 - -/obj/structure/window/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) - . = ..() - if(.) //received damage - update_nearby_icons() - -/obj/structure/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src, hitsound, 75, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src, 'sound/items/Welder.ogg', 100, TRUE) - -/obj/structure/window/deconstruct(disassembled = TRUE) - if(QDELETED(src)) - return - if(!disassembled) - playsound(src, breaksound, 70, 1) - if(!(flags & NODECONSTRUCT)) - for(var/i in debris) - var/obj/item/I = i - I.forceMove(loc) - transfer_fingerprints_to(I) - qdel(src) - update_nearby_icons() - -/obj/structure/window/verb/rotate() - set name = "Rotate Window Counter-Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return - - if(anchored) - to_chat(usr, "[src] cannot be rotated while it is fastened to the floor!") - return FALSE - - var/target_dir = turn(dir, 90) - if(!valid_window_location(loc, target_dir)) - to_chat(usr, "[src] cannot be rotated in that direction!") - return FALSE - - setDir(target_dir) - air_update_turf(1) - ini_dir = dir - add_fingerprint(usr) - return TRUE - -/obj/structure/window/verb/revrotate() - set name = "Rotate Window Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return - - if(anchored) - to_chat(usr, "[src] cannot be rotated while it is fastened to the floor!") - return FALSE - - var/target_dir = turn(dir, 270) - - if(!valid_window_location(loc, target_dir)) - to_chat(usr, "[src] cannot be rotated in that direction!") - return FALSE - - setDir(target_dir) - ini_dir = dir - add_fingerprint(usr) - return TRUE - -/obj/structure/window/AltClick(mob/user) - - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - - if(!Adjacent(user)) - to_chat(user, "Move closer to the window!") - return - - if(anchored) - to_chat(user, "[src] cannot be rotated while it is fastened to the floor!") - return FALSE - - var/target_dir = turn(dir, 270) - - if(!valid_window_location(loc, target_dir)) - target_dir = turn(dir, 90) - if(!valid_window_location(loc, target_dir)) - to_chat(user, "There is no room to rotate the [src]") - return FALSE - - setDir(target_dir) - ini_dir = dir - add_fingerprint(user) - return TRUE - -/obj/structure/window/Destroy() - density = FALSE - air_update_turf(1) - update_nearby_icons() - return ..() - -/obj/structure/window/Move() - var/turf/T = loc - . = ..() - setDir(ini_dir) - move_update_air(T) - -/obj/structure/window/CanAtmosPass(turf/T) - if(!anchored || !density) - return TRUE - return !(FULLTILE_WINDOW_DIR == dir || dir == get_dir(loc, T)) - -//This proc is used to update the icons of nearby windows. -/obj/structure/window/proc/update_nearby_icons() - update_icon() - if(smooth) - queue_smooth_neighbors(src) - -/obj/structure/window/update_icon() - if(!QDELETED(src)) - if(!fulltile) - return - var/ratio = obj_integrity / max_integrity - ratio = CEILING(ratio*4, 1) * 25 - if(smooth) - queue_smooth(src) - overlays -= crack_overlay - if(ratio > 75) - return - crack_overlay = image('icons/obj/structures.dmi',"damage[ratio]",-(layer+0.1)) - overlays += crack_overlay - -/obj/structure/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > (T0C + heat_resistance)) - take_damage(round(exposed_volume / 100), BURN, 0, 0) - -/obj/structure/window/GetExplosionBlock() - return reinf && fulltile ? real_explosion_block : 0 - -/obj/structure/window/basic - desc = "It looks thin and flimsy. A few knocks with... anything, really should shatter it." - -/obj/structure/window/reinforced - name = "reinforced window" - desc = "It looks rather strong. Might take a few good hits to shatter it." - icon_state = "rwindow" - reinf = TRUE - cancolor = TRUE - heat_resistance = 1600 - armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) - max_integrity = 50 - explosion_block = 1 - glass_type = /obj/item/stack/sheet/rglass - -/obj/structure/window/reinforced/tinted - name = "tinted window" - desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." - icon_state = "twindow" - opacity = 1 - -/obj/structure/window/reinforced/tinted/frosted - name = "frosted window" - desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." - icon_state = "fwindow" - max_integrity = 30 - -/obj/structure/window/reinforced/polarized - name = "electrochromic window" - desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it." - var/id - -/obj/structure/window/reinforced/polarized/proc/toggle() - if(opacity) - animate(src, color="#FFFFFF", time=5) - set_opacity(0) - else - animate(src, color="#222222", time=5) - set_opacity(1) - -/obj/machinery/button/windowtint - name = "window tint control" - icon = 'icons/obj/power.dmi' - icon_state = "light0" - desc = "A remote control switch for polarized windows." - var/range = 7 - var/id = 0 - var/active = 0 - -/obj/machinery/button/windowtint/attack_hand(mob/user) - if(..()) - return 1 - - toggle_tint() - -/obj/machinery/button/windowtint/proc/toggle_tint() - use_power(5) - - active = !active - update_icon() - - for(var/obj/structure/window/reinforced/polarized/W in range(src,range)) - if(W.id == src.id || !W.id) - spawn(0) - W.toggle() - return - -/obj/machinery/button/windowtint/power_change() - ..() - if(active && !powered(power_channel)) - toggle_tint() - -/obj/machinery/button/windowtint/update_icon() - icon_state = "light[active]" - -/obj/structure/window/plasmabasic - name = "plasma window" - desc = "A window made out of a plasma-silicate alloy. It looks insanely tough to break and burn through." - icon_state = "plasmawindow" - shardtype = /obj/item/shard/plasma - glass_type = /obj/item/stack/sheet/plasmaglass - heat_resistance = 32000 - max_integrity = 150 - explosion_block = 1 - armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) - -/obj/structure/window/plasmabasic/BlockSuperconductivity() - return 1 - -/obj/structure/window/plasmareinforced - name = "reinforced plasma window" - desc = "A plasma-glass alloy window, with rods supporting it. It looks hopelessly tough to break. It also looks completely fireproof, considering how basic plasma windows are insanely fireproof." - icon_state = "plasmarwindow" - shardtype = /obj/item/shard/plasma - glass_type = /obj/item/stack/sheet/plasmarglass - reinf = TRUE - max_integrity = 500 - explosion_block = 2 - armor = list("melee" = 85, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) - damage_deflection = 21 - -/obj/structure/window/plasmareinforced/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - return - -/obj/structure/window/plasmareinforced/BlockSuperconductivity() - return 1 //okay this SHOULD MAKE THE TOXINS CHAMBER WORK - -/obj/structure/window/full - glass_amount = 2 - dir = FULLTILE_WINDOW_DIR - level = 3 - fulltile = TRUE - flags = PREVENT_CLICK_UNDER - -/obj/structure/window/full/basic - desc = "It looks thin and flimsy. A few knocks with... anything, really should shatter it." - icon = 'icons/obj/smooth_structures/window.dmi' - icon_state = "window" - max_integrity = 50 - smooth = SMOOTH_TRUE - cancolor = TRUE - canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced) - -/obj/structure/window/full/plasmabasic - name = "plasma window" - desc = "A plasma-glass alloy window. It looks insanely tough to break. It appears it's also insanely tough to burn through." - icon = 'icons/obj/smooth_structures/plasma_window.dmi' - icon_state = "plasmawindow" - shardtype = /obj/item/shard/plasma - glass_type = /obj/item/stack/sheet/plasmaglass - heat_resistance = 32000 - max_integrity = 300 - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced) - explosion_block = 1 - armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) - -/obj/structure/window/full/plasmareinforced - name = "reinforced plasma window" - desc = "A plasma-glass alloy window, with rods supporting it. It looks hopelessly tough to break. It also looks completely fireproof, considering how basic plasma windows are insanely fireproof." - icon = 'icons/obj/smooth_structures/rplasma_window.dmi' - icon_state = "rplasmawindow" - shardtype = /obj/item/shard/plasma - glass_type = /obj/item/stack/sheet/plasmarglass - smooth = SMOOTH_TRUE - reinf = TRUE - max_integrity = 1000 - explosion_block = 2 - armor = list("melee" = 85, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) - -/obj/structure/window/full/plasmareinforced/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - return - -/obj/structure/window/full/reinforced - name = "reinforced window" - desc = "It looks rather strong. Might take a few good hits to shatter it." - icon = 'icons/obj/smooth_structures/reinforced_window.dmi' - icon_state = "r_window" - smooth = SMOOTH_TRUE - canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced) - max_integrity = 100 - reinf = TRUE - heat_resistance = 1600 - armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) - explosion_block = 1 - glass_type = /obj/item/stack/sheet/rglass - cancolor = TRUE - -/obj/structure/window/full/reinforced/tinted - name = "tinted window" - desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." - icon = 'icons/obj/smooth_structures/tinted_window.dmi' - icon_state = "tinted_window" - opacity = 1 - -obj/structure/window/full/reinforced/ice - icon = 'icons/obj/smooth_structures/rice_window.dmi' - icon_state = "ice_window" - max_integrity = 150 - cancolor = FALSE - -/obj/structure/window/full/shuttle - name = "shuttle window" - desc = "A reinforced, air-locked pod window." - icon = 'icons/obj/smooth_structures/shuttle_window.dmi' - icon_state = "shuttle_window" - max_integrity = 100 - reinf = TRUE - heat_resistance = 1600 - explosion_block = 3 - armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) - smooth = SMOOTH_TRUE - canSmoothWith = null - glass_type = /obj/item/stack/sheet/titaniumglass - -/obj/structure/window/full/shuttle/narsie_act() - color = "#3C3434" - -/obj/structure/window/full/shuttle/tinted - opacity = TRUE - -/obj/structure/window/plastitanium - name = "plastitanium window" - desc = "An evil looking window of plasma and titanium." - icon = 'icons/obj/smooth_structures/plastitanium_window.dmi' - icon_state = "plastitanium_window" - dir = FULLTILE_WINDOW_DIR - max_integrity = 100 - fulltile = TRUE - flags = PREVENT_CLICK_UNDER - reinf = TRUE - heat_resistance = 1600 - armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) - smooth = SMOOTH_TRUE - canSmoothWith = null - explosion_block = 3 - level = 3 - glass_type = /obj/item/stack/sheet/plastitaniumglass - glass_amount = 2 - -/obj/structure/window/reinforced/clockwork - name = "brass window" - desc = "A paper-thin pane of translucent yet reinforced brass." - icon = 'icons/obj/smooth_structures/clockwork_window.dmi' - icon_state = "clockwork_window_single" - resistance_flags = FIRE_PROOF | ACID_PROOF - max_integrity = 80 - armor = list("melee" = 60, "bullet" = 25, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) - explosion_block = 2 //fancy AND hard to destroy. the most useful combination. - glass_type = /obj/item/stack/tile/brass - reinf = FALSE - cancolor = FALSE - var/made_glow = FALSE - -/obj/structure/window/reinforced/clockwork/New(loc, direct) - if(fulltile) - made_glow = TRUE - ..() - QDEL_LIST(debris) - if(fulltile) - new /obj/effect/temp_visual/ratvar/window(get_turf(src)) - debris += new/obj/item/stack/tile/brass(src, 2) - else - debris += new/obj/item/stack/tile/brass(src, 1) - -/obj/structure/window/reinforced/clockwork/setDir(direct) - if(!made_glow) - var/obj/effect/E = new /obj/effect/temp_visual/ratvar/window/single(get_turf(src)) - E.setDir(direct) - made_glow = TRUE - ..() - -/obj/structure/window/reinforced/clockwork/ratvar_act() - obj_integrity = max_integrity - update_icon() - -/obj/structure/window/reinforced/clockwork/narsie_act() - take_damage(rand(25, 75), BRUTE) - if(src) - var/previouscolor = color - color = "#960000" - animate(src, color = previouscolor, time = 8) - -/obj/structure/window/reinforced/clockwork/fulltile - icon_state = "clockwork_window" - smooth = SMOOTH_TRUE - canSmoothWith = null - fulltile = TRUE - flags = PREVENT_CLICK_UNDER - dir = FULLTILE_WINDOW_DIR - max_integrity = 120 - level = 3 - glass_amount = 2 \ No newline at end of file +GLOBAL_LIST_INIT(wcBar, pick(list("#0d8395", "#58b5c3", "#58c366", "#90d79a", "#ffffff"))) +GLOBAL_LIST_INIT(wcBrig, pick(list("#aa0808", "#7f0606", "#ff0000"))) +GLOBAL_LIST_INIT(wcCommon, pick(list("#379963", "#0d8395", "#58b5c3", "#49e46e", "#8fcf44", "#ffffff"))) + +/obj/proc/color_windows(obj/W) + var/list/wcBarAreas = list(/area/crew_quarters/bar) + var/list/wcBrigAreas = list(/area/security, /area/shuttle/gamma) + + var/newcolor + var/turf/T = get_turf(W) + if(!istype(T)) + return + var/area/A = T.loc + + if(is_type_in_list(A,wcBarAreas)) + newcolor = GLOB.wcBar + else if(is_type_in_list(A,wcBrigAreas)) + newcolor = GLOB.wcBrig + else + newcolor = GLOB.wcCommon + + return newcolor + +/obj/structure/window + name = "window" + desc = "A window." + icon_state = "window" + density = TRUE + layer = ABOVE_OBJ_LAYER //Just above doors + pressure_resistance = 4*ONE_ATMOSPHERE + anchored = TRUE + flags = ON_BORDER + can_be_unanchored = TRUE + max_integrity = 25 + resistance_flags = ACID_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + var/ini_dir = null + var/state = WINDOW_OUT_OF_FRAME + var/reinf = FALSE + var/heat_resistance = 800 + var/decon_speed = null + var/fulltile = FALSE + var/shardtype = /obj/item/shard + var/glass_type = /obj/item/stack/sheet/glass + var/glass_amount = 1 + var/cancolor = FALSE + var/image/crack_overlay + var/list/debris = list() + var/real_explosion_block //ignore this, just use explosion_block + var/breaksound = "shatter" + var/hitsound = 'sound/effects/Glasshit.ogg' + +/obj/structure/window/examine(mob/user) + . = ..() + if(reinf) + if(anchored && state == WINDOW_SCREWED_TO_FRAME) + . += "The window is screwed to the frame." + else if(anchored && state == WINDOW_IN_FRAME) + . += "The window is unscrewed but pried into the frame." + else if(anchored && state == WINDOW_OUT_OF_FRAME) + . += "The window is out of the frame, but could be pried in. It is screwed to the floor." + else if(!anchored) + . += "The window is unscrewed from the floor, and could be deconstructed by wrenching." + else + if(anchored) + . += "The window is screwed to the floor." + else + . += "The window is unscrewed from the floor, and could be deconstructed by wrenching." + if(!anchored && !fulltile) + . += "Alt-click to rotate it." + +/obj/structure/window/New(Loc, direct) + ..() + if(direct) + setDir(direct) + if(reinf && anchored) + state = WINDOW_SCREWED_TO_FRAME + + ini_dir = dir + + if(!color && cancolor) + color = color_windows(src) + + // Precreate our own debris + + var/shards = 1 + if(fulltile) + shards++ + setDir() + + if(decon_speed == null) + if(fulltile) + decon_speed = 20 + else + decon_speed = 1 + + var/rods = 0 + if(reinf) + rods++ + if(fulltile) + rods++ + + for(var/i in 1 to shards) + debris += new shardtype(src) + if(rods) + debris += new /obj/item/stack/rods(src, rods) + + //windows only block while reinforced and fulltile, so we'll use the proc + real_explosion_block = explosion_block + explosion_block = EXPLOSION_BLOCK_PROC + +/obj/structure/window/Initialize() + air_update_turf(1) + return ..() + +/obj/structure/window/narsie_act() + color = NARSIE_WINDOW_COLOUR + for(var/obj/item/shard/shard in debris) + shard.color = NARSIE_WINDOW_COLOUR + +/obj/structure/window/ratvar_act() + if(!fulltile) + new/obj/structure/window/reinforced/clockwork(get_turf(src), dir) + else + new/obj/structure/window/reinforced/clockwork/fulltile(get_turf(src)) + qdel(src) + +/obj/structure/window/rpd_act() + return + +/obj/structure/window/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct(FALSE) + +/obj/structure/window/setDir(direct) + if(!fulltile) + ..() + else + ..(FULLTILE_WINDOW_DIR) + +/obj/structure/window/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSGLASS)) + return 1 + if(dir == FULLTILE_WINDOW_DIR) + return 0 //full tile window, you can't move into it! + if(get_dir(loc, target) == dir) + return !density + if(istype(mover, /obj/structure/window)) + var/obj/structure/window/W = mover + if(!valid_window_location(loc, W.ini_dir)) + return FALSE + else if(istype(mover, /obj/structure/windoor_assembly)) + var/obj/structure/windoor_assembly/W = mover + if(!valid_window_location(loc, W.ini_dir)) + return FALSE + else if(istype(mover, /obj/machinery/door/window) && !valid_window_location(loc, mover.dir)) + return FALSE + return 1 + +/obj/structure/window/CheckExit(atom/movable/O, target) + if(istype(O) && O.checkpass(PASSGLASS)) + return 1 + if(get_dir(O.loc, target) == dir) + return 0 + return 1 + +/obj/structure/window/CanAStarPass(ID, to_dir) + if(!density) + return 1 + if((dir == FULLTILE_WINDOW_DIR) || (dir == to_dir)) + return 0 + + return 1 + +/obj/structure/window/attack_tk(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message("Something knocks on [src].") + add_fingerprint(user) + playsound(src, 'sound/effects/glassknock.ogg', 50, 1) + +/obj/structure/window/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0) + if(!can_be_reached(user)) + return 1 + . = ..() + +/obj/structure/window/attack_hand(mob/user) + if(!can_be_reached(user)) + return + if(user.a_intent == INTENT_HARM) + user.changeNext_move(CLICK_CD_MELEE) + playsound(src, 'sound/effects/glassknock.ogg', 80, 1) + user.visible_message("[user] bangs against [src]!", \ + "You bang against [src]!", \ + "You hear a banging sound.") + add_fingerprint(user) + else + user.changeNext_move(CLICK_CD_MELEE) + playsound(src, 'sound/effects/glassknock.ogg', 80, 1) + user.visible_message("[user] knocks on [src].", \ + "You knock on [src].", \ + "You hear a knocking sound.") + add_fingerprint(user) + +/obj/structure/window/attack_generic(mob/user, damage_amount = 0, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) //used by attack_alien, attack_animal, and attack_slime + if(!can_be_reached(user)) + return + ..() + +/obj/structure/window/attackby(obj/item/I, mob/living/user, params) + if(!can_be_reached(user)) + return 1 //skip the afterattack + + add_fingerprint(user) + if(istype(I, /obj/item/grab) && get_dist(src, user) < 2) + var/obj/item/grab/G = I + if(isliving(G.affecting)) + var/mob/living/M = G.affecting + var/state = G.state + qdel(I) //gotta delete it here because if window breaks, it won't get deleted + switch(state) + if(1) + M.visible_message("[user] slams [M] against \the [src]!") + M.apply_damage(7) + take_damage(10) + if(2) + M.visible_message("[user] bashes [M] against \the [src]!") + if(prob(50)) + M.Weaken(1) + M.apply_damage(10) + take_damage(25) + if(3) + M.visible_message("[user] crushes [M] against \the [src]!") + M.Weaken(5) + M.apply_damage(20) + take_damage(50) + if(4) + visible_message("[user] smashes [M] against \the [src]!") + M.Weaken(5) + M.apply_damage(30) + take_damage(75) + return + return ..() + + +/obj/structure/window/crowbar_act(mob/user, obj/item/I) + if(!reinf) + return + if(state != WINDOW_OUT_OF_FRAME && state != WINDOW_IN_FRAME) + return + if(flags & NODECONSTRUCT) + return + . = TRUE + if(!can_be_reached(user)) + return + to_chat(user, "You begin to lever the window [state == WINDOW_OUT_OF_FRAME ? "into":"out of"] the frame...") + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + return + state = (state == WINDOW_OUT_OF_FRAME ? WINDOW_IN_FRAME : WINDOW_OUT_OF_FRAME) + to_chat(user, "You pry the window [state == WINDOW_IN_FRAME ? "into":"out of"] the frame.") + +/obj/structure/window/screwdriver_act(mob/user, obj/item/I) + if(flags & NODECONSTRUCT) + return + . = TRUE + if(!can_be_reached(user)) + return + if(reinf) + if(state == WINDOW_SCREWED_TO_FRAME || state == WINDOW_IN_FRAME) + to_chat(user, "You begin to [state == WINDOW_SCREWED_TO_FRAME ? "unscrew the window from":"screw the window to"] the frame...") + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + return + state = (state == WINDOW_IN_FRAME ? WINDOW_SCREWED_TO_FRAME : WINDOW_IN_FRAME) + to_chat(user, "You [state == WINDOW_IN_FRAME ? "unfasten the window from":"fasten the window to"] the frame.") + else if(state == WINDOW_OUT_OF_FRAME) + to_chat(user, "You begin to [anchored ? "unscrew the frame from":"screw the frame to"] the floor...") + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + return + anchored = !anchored + update_nearby_icons() + to_chat(user, "You [anchored ? "fasten the frame to":"unfasten the frame from"] the floor.") + else //if we're not reinforced, we don't need to check or update state + to_chat(user, "You begin to [anchored ? "unscrew the window from":"screw the window to"] the floor...") + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_anchored, anchored))) + return + anchored = !anchored + air_update_turf(TRUE) + update_nearby_icons() + to_chat(user, "You [anchored ? "fasten the window to":"unfasten the window from"] the floor.") + +/obj/structure/window/wrench_act(mob/user, obj/item/I) + if(flags & NODECONSTRUCT) + return + if(anchored) + return + . = TRUE + if(!can_be_reached(user)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(!I.use_tool(src, user, decon_speed, volume = I.tool_volume, extra_checks = CALLBACK(src, .proc/check_state_and_anchored, state, anchored))) + return + var/obj/item/stack/sheet/G = new glass_type(user.loc, glass_amount) + G.add_fingerprint(user) + playsound(src, 'sound/items/deconstruct.ogg', 50, 1) + to_chat(user, "You successfully disassemble [src].") + qdel(src) + +/obj/structure/window/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + . = TRUE + if(!can_be_reached(user)) + return + if(obj_integrity >= max_integrity) + to_chat(user, "[src] is already in good condition!") + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, 40, volume = I.tool_volume)) + obj_integrity = max_integrity + WELDER_REPAIR_SUCCESS_MESSAGE + +/obj/structure/window/proc/check_state(checked_state) + if(state == checked_state) + return TRUE + +/obj/structure/window/proc/check_anchored(checked_anchored) + if(anchored == checked_anchored) + return TRUE + +/obj/structure/window/proc/check_state_and_anchored(checked_state, checked_anchored) + return check_state(checked_state) && check_anchored(checked_anchored) + +/obj/structure/window/mech_melee_attack(obj/mecha/M) + if(!can_be_reached()) + return + ..() + +/obj/structure/window/proc/can_be_reached(mob/user) + if(!fulltile) + if(get_dir(user, src) & dir) + for(var/obj/O in loc) + if(!O.CanPass(user, user.loc, 1)) + return 0 + return 1 + +/obj/structure/window/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) + . = ..() + if(.) //received damage + update_nearby_icons() + +/obj/structure/window/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src, hitsound, 75, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src, 'sound/items/Welder.ogg', 100, TRUE) + +/obj/structure/window/deconstruct(disassembled = TRUE) + if(QDELETED(src)) + return + if(!disassembled) + playsound(src, breaksound, 70, 1) + if(!(flags & NODECONSTRUCT)) + for(var/i in debris) + var/obj/item/I = i + I.forceMove(loc) + transfer_fingerprints_to(I) + qdel(src) + update_nearby_icons() + +/obj/structure/window/verb/rotate() + set name = "Rotate Window Counter-Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return + + if(anchored) + to_chat(usr, "[src] cannot be rotated while it is fastened to the floor!") + return FALSE + + var/target_dir = turn(dir, 90) + if(!valid_window_location(loc, target_dir)) + to_chat(usr, "[src] cannot be rotated in that direction!") + return FALSE + + setDir(target_dir) + air_update_turf(1) + ini_dir = dir + add_fingerprint(usr) + return TRUE + +/obj/structure/window/verb/revrotate() + set name = "Rotate Window Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return + + if(anchored) + to_chat(usr, "[src] cannot be rotated while it is fastened to the floor!") + return FALSE + + var/target_dir = turn(dir, 270) + + if(!valid_window_location(loc, target_dir)) + to_chat(usr, "[src] cannot be rotated in that direction!") + return FALSE + + setDir(target_dir) + ini_dir = dir + add_fingerprint(usr) + return TRUE + +/obj/structure/window/AltClick(mob/user) + + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + + if(!Adjacent(user)) + to_chat(user, "Move closer to the window!") + return + + if(anchored) + to_chat(user, "[src] cannot be rotated while it is fastened to the floor!") + return FALSE + + var/target_dir = turn(dir, 270) + + if(!valid_window_location(loc, target_dir)) + target_dir = turn(dir, 90) + if(!valid_window_location(loc, target_dir)) + to_chat(user, "There is no room to rotate the [src]") + return FALSE + + setDir(target_dir) + ini_dir = dir + add_fingerprint(user) + return TRUE + +/obj/structure/window/Destroy() + density = FALSE + air_update_turf(1) + update_nearby_icons() + return ..() + +/obj/structure/window/Move() + var/turf/T = loc + . = ..() + setDir(ini_dir) + move_update_air(T) + +/obj/structure/window/CanAtmosPass(turf/T) + if(!anchored || !density) + return TRUE + return !(FULLTILE_WINDOW_DIR == dir || dir == get_dir(loc, T)) + +//This proc is used to update the icons of nearby windows. +/obj/structure/window/proc/update_nearby_icons() + update_icon() + if(smooth) + queue_smooth_neighbors(src) + +/obj/structure/window/update_icon() + if(!QDELETED(src)) + if(!fulltile) + return + var/ratio = obj_integrity / max_integrity + ratio = CEILING(ratio*4, 1) * 25 + if(smooth) + queue_smooth(src) + overlays -= crack_overlay + if(ratio > 75) + return + crack_overlay = image('icons/obj/structures.dmi',"damage[ratio]",-(layer+0.1)) + overlays += crack_overlay + +/obj/structure/window/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > (T0C + heat_resistance)) + take_damage(round(exposed_volume / 100), BURN, 0, 0) + +/obj/structure/window/GetExplosionBlock() + return reinf && fulltile ? real_explosion_block : 0 + +/obj/structure/window/basic + desc = "It looks thin and flimsy. A few knocks with... anything, really should shatter it." + +/obj/structure/window/reinforced + name = "reinforced window" + desc = "It looks rather strong. Might take a few good hits to shatter it." + icon_state = "rwindow" + reinf = TRUE + cancolor = TRUE + heat_resistance = 1600 + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) + max_integrity = 50 + explosion_block = 1 + glass_type = /obj/item/stack/sheet/rglass + +/obj/structure/window/reinforced/tinted + name = "tinted window" + desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." + icon_state = "twindow" + opacity = 1 + +/obj/structure/window/reinforced/tinted/frosted + name = "frosted window" + desc = "It looks rather strong and frosted over. Looks like it might take a few less hits then a normal reinforced window." + icon_state = "fwindow" + max_integrity = 30 + +/obj/structure/window/reinforced/polarized + name = "electrochromic window" + desc = "Adjusts its tint with voltage. Might take a few good hits to shatter it." + var/id + +/obj/structure/window/reinforced/polarized/proc/toggle() + if(opacity) + animate(src, color="#FFFFFF", time=5) + set_opacity(0) + else + animate(src, color="#222222", time=5) + set_opacity(1) + +/obj/machinery/button/windowtint + name = "window tint control" + icon = 'icons/obj/power.dmi' + icon_state = "light0" + desc = "A remote control switch for polarized windows." + var/range = 7 + var/id = 0 + var/active = 0 + +/obj/machinery/button/windowtint/attack_hand(mob/user) + if(..()) + return 1 + + toggle_tint() + +/obj/machinery/button/windowtint/proc/toggle_tint() + use_power(5) + + active = !active + update_icon() + + for(var/obj/structure/window/reinforced/polarized/W in range(src,range)) + if(W.id == src.id || !W.id) + spawn(0) + W.toggle() + return + +/obj/machinery/button/windowtint/power_change() + ..() + if(active && !powered(power_channel)) + toggle_tint() + +/obj/machinery/button/windowtint/update_icon() + icon_state = "light[active]" + +/obj/structure/window/plasmabasic + name = "plasma window" + desc = "A window made out of a plasma-silicate alloy. It looks insanely tough to break and burn through." + icon_state = "plasmawindow" + shardtype = /obj/item/shard/plasma + glass_type = /obj/item/stack/sheet/plasmaglass + heat_resistance = 32000 + max_integrity = 150 + explosion_block = 1 + armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) + +/obj/structure/window/plasmabasic/BlockSuperconductivity() + return 1 + +/obj/structure/window/plasmareinforced + name = "reinforced plasma window" + desc = "A plasma-glass alloy window, with rods supporting it. It looks hopelessly tough to break. It also looks completely fireproof, considering how basic plasma windows are insanely fireproof." + icon_state = "plasmarwindow" + shardtype = /obj/item/shard/plasma + glass_type = /obj/item/stack/sheet/plasmarglass + reinf = TRUE + max_integrity = 500 + explosion_block = 2 + armor = list("melee" = 85, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) + damage_deflection = 21 + +/obj/structure/window/plasmareinforced/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + return + +/obj/structure/window/plasmareinforced/BlockSuperconductivity() + return 1 //okay this SHOULD MAKE THE TOXINS CHAMBER WORK + +/obj/structure/window/full + glass_amount = 2 + dir = FULLTILE_WINDOW_DIR + level = 3 + fulltile = TRUE + flags = PREVENT_CLICK_UNDER + +/obj/structure/window/full/basic + desc = "It looks thin and flimsy. A few knocks with... anything, really should shatter it." + icon = 'icons/obj/smooth_structures/window.dmi' + icon_state = "window" + max_integrity = 50 + smooth = SMOOTH_TRUE + cancolor = TRUE + canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced) + +/obj/structure/window/full/plasmabasic + name = "plasma window" + desc = "A plasma-glass alloy window. It looks insanely tough to break. It appears it's also insanely tough to burn through." + icon = 'icons/obj/smooth_structures/plasma_window.dmi' + icon_state = "plasmawindow" + shardtype = /obj/item/shard/plasma + glass_type = /obj/item/stack/sheet/plasmaglass + heat_resistance = 32000 + max_integrity = 300 + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced) + explosion_block = 1 + armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) + +/obj/structure/window/full/plasmareinforced + name = "reinforced plasma window" + desc = "A plasma-glass alloy window, with rods supporting it. It looks hopelessly tough to break. It also looks completely fireproof, considering how basic plasma windows are insanely fireproof." + icon = 'icons/obj/smooth_structures/rplasma_window.dmi' + icon_state = "rplasmawindow" + shardtype = /obj/item/shard/plasma + glass_type = /obj/item/stack/sheet/plasmarglass + smooth = SMOOTH_TRUE + reinf = TRUE + max_integrity = 1000 + explosion_block = 2 + armor = list("melee" = 85, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 60, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100) + +/obj/structure/window/full/plasmareinforced/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + return + +/obj/structure/window/full/reinforced + name = "reinforced window" + desc = "It looks rather strong. Might take a few good hits to shatter it." + icon = 'icons/obj/smooth_structures/reinforced_window.dmi' + icon_state = "r_window" + smooth = SMOOTH_TRUE + canSmoothWith = list(/obj/structure/window/full/basic, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/plasmabasic, /obj/structure/window/full/plasmareinforced) + max_integrity = 100 + reinf = TRUE + heat_resistance = 1600 + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) + explosion_block = 1 + glass_type = /obj/item/stack/sheet/rglass + cancolor = TRUE + +/obj/structure/window/full/reinforced/tinted + name = "tinted window" + desc = "It looks rather strong and opaque. Might take a few good hits to shatter it." + icon = 'icons/obj/smooth_structures/tinted_window.dmi' + icon_state = "tinted_window" + opacity = 1 + +obj/structure/window/full/reinforced/ice + icon = 'icons/obj/smooth_structures/rice_window.dmi' + icon_state = "ice_window" + max_integrity = 150 + cancolor = FALSE + +/obj/structure/window/full/shuttle + name = "shuttle window" + desc = "A reinforced, air-locked pod window." + icon = 'icons/obj/smooth_structures/shuttle_window.dmi' + icon_state = "shuttle_window" + max_integrity = 100 + reinf = TRUE + heat_resistance = 1600 + explosion_block = 3 + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) + smooth = SMOOTH_TRUE + canSmoothWith = null + glass_type = /obj/item/stack/sheet/titaniumglass + +/obj/structure/window/full/shuttle/narsie_act() + color = "#3C3434" + +/obj/structure/window/full/shuttle/tinted + opacity = TRUE + +/obj/structure/window/plastitanium + name = "plastitanium window" + desc = "An evil looking window of plasma and titanium." + icon = 'icons/obj/smooth_structures/plastitanium_window.dmi' + icon_state = "plastitanium_window" + dir = FULLTILE_WINDOW_DIR + max_integrity = 100 + fulltile = TRUE + flags = PREVENT_CLICK_UNDER + reinf = TRUE + heat_resistance = 1600 + armor = list("melee" = 50, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) + smooth = SMOOTH_TRUE + canSmoothWith = null + explosion_block = 3 + level = 3 + glass_type = /obj/item/stack/sheet/plastitaniumglass + glass_amount = 2 + +/obj/structure/window/reinforced/clockwork + name = "brass window" + desc = "A paper-thin pane of translucent yet reinforced brass." + icon = 'icons/obj/smooth_structures/clockwork_window.dmi' + icon_state = "clockwork_window_single" + resistance_flags = FIRE_PROOF | ACID_PROOF + max_integrity = 80 + armor = list("melee" = 60, "bullet" = 25, "laser" = 0, "energy" = 0, "bomb" = 25, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 100) + explosion_block = 2 //fancy AND hard to destroy. the most useful combination. + glass_type = /obj/item/stack/tile/brass + reinf = FALSE + cancolor = FALSE + var/made_glow = FALSE + +/obj/structure/window/reinforced/clockwork/New(loc, direct) + if(fulltile) + made_glow = TRUE + ..() + QDEL_LIST(debris) + if(fulltile) + new /obj/effect/temp_visual/ratvar/window(get_turf(src)) + debris += new/obj/item/stack/tile/brass(src, 2) + else + debris += new/obj/item/stack/tile/brass(src, 1) + +/obj/structure/window/reinforced/clockwork/setDir(direct) + if(!made_glow) + var/obj/effect/E = new /obj/effect/temp_visual/ratvar/window/single(get_turf(src)) + E.setDir(direct) + made_glow = TRUE + ..() + +/obj/structure/window/reinforced/clockwork/ratvar_act() + obj_integrity = max_integrity + update_icon() + +/obj/structure/window/reinforced/clockwork/narsie_act() + take_damage(rand(25, 75), BRUTE) + if(src) + var/previouscolor = color + color = "#960000" + animate(src, color = previouscolor, time = 8) + +/obj/structure/window/reinforced/clockwork/fulltile + icon_state = "clockwork_window" + smooth = SMOOTH_TRUE + canSmoothWith = null + fulltile = TRUE + flags = PREVENT_CLICK_UNDER + dir = FULLTILE_WINDOW_DIR + max_integrity = 120 + level = 3 + glass_amount = 2 diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm index 3e923f207904..3c1ba0b8fbc7 100644 --- a/code/game/shuttle_engines.dm +++ b/code/game/shuttle_engines.dm @@ -1,60 +1,60 @@ -/obj/structure/shuttle - name = "shuttle" - icon = 'icons/turf/shuttle.dmi' - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - max_integrity = 500 - armor = list(melee = 100, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) //default + ignores melee - -/obj/structure/shuttle/shuttleRotate(rotation) - ..() - var/matrix/M = transform - M.Turn(rotation) - transform = M - -/obj/structure/shuttle/window - name = "shuttle window" - icon = 'icons/obj/podwindows.dmi' - icon_state = "1" - density = 1 - opacity = 0 - anchored = 1 - - CanPass(atom/movable/mover, turf/target, height) - if(!height) return 0 - else return ..() - - CanAtmosPass(turf/T) - return !density - -/obj/structure/shuttle/engine - name = "engine" - density = 1 - anchored = 1.0 - -/obj/structure/shuttle/engine/heater - name = "heater" - icon_state = "heater" - -/obj/structure/shuttle/engine/platform - name = "platform" - icon_state = "platform" - -/obj/structure/shuttle/engine/propulsion - name = "propulsion" - icon_state = "propulsion" - opacity = 1 - -/obj/structure/shuttle/engine/propulsion/burst - name = "burst" - -/obj/structure/shuttle/engine/propulsion/burst/left - name = "left" - icon_state = "burst_l" - -/obj/structure/shuttle/engine/propulsion/burst/right - name = "right" - icon_state = "burst_r" - -/obj/structure/shuttle/engine/router - name = "router" - icon_state = "router" +/obj/structure/shuttle + name = "shuttle" + icon = 'icons/turf/shuttle.dmi' + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + max_integrity = 500 + armor = list(melee = 100, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 70) //default + ignores melee + +/obj/structure/shuttle/shuttleRotate(rotation) + ..() + var/matrix/M = transform + M.Turn(rotation) + transform = M + +/obj/structure/shuttle/window + name = "shuttle window" + icon = 'icons/obj/podwindows.dmi' + icon_state = "1" + density = 1 + opacity = 0 + anchored = 1 + + CanPass(atom/movable/mover, turf/target, height) + if(!height) return 0 + else return ..() + + CanAtmosPass(turf/T) + return !density + +/obj/structure/shuttle/engine + name = "engine" + density = 1 + anchored = 1.0 + +/obj/structure/shuttle/engine/heater + name = "heater" + icon_state = "heater" + +/obj/structure/shuttle/engine/platform + name = "platform" + icon_state = "platform" + +/obj/structure/shuttle/engine/propulsion + name = "propulsion" + icon_state = "propulsion" + opacity = 1 + +/obj/structure/shuttle/engine/propulsion/burst + name = "burst" + +/obj/structure/shuttle/engine/propulsion/burst/left + name = "left" + icon_state = "burst_l" + +/obj/structure/shuttle/engine/propulsion/burst/right + name = "right" + icon_state = "burst_r" + +/obj/structure/shuttle/engine/router + name = "router" + icon_state = "router" diff --git a/code/game/sound.dm b/code/game/sound.dm index d27239ae4e76..870ac5e69662 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -160,4 +160,4 @@ soundin = pick('sound/effects/bone_break_1.ogg', 'sound/effects/bone_break_2.ogg', 'sound/effects/bone_break_3.ogg', 'sound/effects/bone_break_4.ogg', 'sound/effects/bone_break_5.ogg', 'sound/effects/bone_break_6.ogg') if("honkbot_e") soundin = pick('sound/items/bikehorn.ogg', 'sound/items/AirHorn2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/items/AirHorn.ogg', 'sound/items/WEEOO1.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bcreep.ogg','sound/magic/Fireball.ogg' ,'sound/effects/pray.ogg', 'sound/voice/hiss1.ogg','sound/machines/buzz-sigh.ogg', 'sound/machines/ping.ogg', 'sound/weapons/flashbang.ogg', 'sound/weapons/bladeslice.ogg') - return soundin \ No newline at end of file + return soundin diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 62993753cec5..6c10921e6bd2 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -1,109 +1,109 @@ -/turf/simulated - name = "station" - var/wet = 0 - var/image/wet_overlay = null - - var/thermite = 0 - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed - var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to - var/dirtoverlay = null - -/turf/simulated/New() - ..() - levelupdate() - visibilityChanged() - -/turf/simulated/proc/break_tile() - return - -/turf/simulated/proc/burn_tile() - return - -/turf/simulated/water_act(volume, temperature, source) - . = ..() - - if(volume >= 3) - MakeSlippery() - - var/hotspot = (locate(/obj/effect/hotspot) in src) - if(hotspot) - var/datum/gas_mixture/lowertemp = remove_air(air.total_moles()) - lowertemp.temperature = max(min(lowertemp.temperature-2000,lowertemp.temperature / 2), 0) - lowertemp.react() - assume_air(lowertemp) - qdel(hotspot) - -/turf/simulated/proc/MakeSlippery(wet_setting = TURF_WET_WATER, infinite = FALSE) // 1 = Water, 2 = Lube, 3 = Ice, 4 = Permafrost - if(wet >= wet_setting) - return - wet = wet_setting - if(wet_setting != TURF_DRY) - if(wet_overlay) - overlays -= wet_overlay - wet_overlay = null - var/turf/simulated/floor/F = src - if(istype(F)) - if(wet_setting >= TURF_WET_ICE) - wet_overlay = image('icons/effects/water.dmi', src, "ice_floor") - else - wet_overlay = image('icons/effects/water.dmi', src, "wet_floor_static") - else - if(wet_setting >= TURF_WET_ICE) - wet_overlay = image('icons/effects/water.dmi', src, "ice_floor") - else - wet_overlay = image('icons/effects/water.dmi', src, "wet_static") - overlays += wet_overlay - if(!infinite) - spawn(rand(790, 820)) // Purely so for visual effect - if(!istype(src, /turf/simulated)) //Because turfs don't get deleted, they change, adapt, transform, evolve and deform. they are one and they are all. - return - MakeDry(wet_setting) - -/turf/simulated/proc/MakeDry(wet_setting = TURF_WET_WATER) - if(wet > wet_setting) - return - wet = TURF_DRY - if(wet_overlay) - overlays -= wet_overlay - -/turf/simulated/Entered(atom/A, atom/OL, ignoreRest = 0) - ..() - if(!ignoreRest) - if(ishuman(A)) - var/mob/living/carbon/human/M = A - if(M.lying) - return 1 - - if(M.flying) - return ..() - - switch(src.wet) - if(TURF_WET_WATER) - if(!(M.slip("the wet floor", 4, 2, tilesSlipped = 0, walkSafely = 1))) - M.inertia_dir = 0 - return - - if(TURF_WET_LUBE) //lube - M.slip("the floor", 0, 5, tilesSlipped = 3, walkSafely = 0, slipAny = 1) - - - if(TURF_WET_ICE) // Ice - if(M.slip("the icy floor", 4, 2, tilesSlipped = 0, walkSafely = 0)) - M.inertia_dir = 0 - if(prob(5)) - var/obj/item/organ/external/affected = M.get_organ("head") - if(affected) - M.apply_damage(5, BRUTE, "head") - M.visible_message("[M] hits their head on the ice!") - playsound(src, 'sound/weapons/genhit1.ogg', 50, 1) - - if(TURF_WET_PERMAFROST) // Permafrost - M.slip("the frosted floor", 0, 5, tilesSlipped = 1, walkSafely = 0, slipAny = 1) - -/turf/simulated/ChangeTurf(path, defer_change = FALSE, keep_icon = TRUE, ignore_air = FALSE) - . = ..() - queue_smooth_neighbors(src) - -/turf/simulated/proc/is_shielded() +/turf/simulated + name = "station" + var/wet = 0 + var/image/wet_overlay = null + + var/thermite = 0 + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed + var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to + var/dirtoverlay = null + +/turf/simulated/New() + ..() + levelupdate() + visibilityChanged() + +/turf/simulated/proc/break_tile() + return + +/turf/simulated/proc/burn_tile() + return + +/turf/simulated/water_act(volume, temperature, source) + . = ..() + + if(volume >= 3) + MakeSlippery() + + var/hotspot = (locate(/obj/effect/hotspot) in src) + if(hotspot) + var/datum/gas_mixture/lowertemp = remove_air(air.total_moles()) + lowertemp.temperature = max(min(lowertemp.temperature-2000,lowertemp.temperature / 2), 0) + lowertemp.react() + assume_air(lowertemp) + qdel(hotspot) + +/turf/simulated/proc/MakeSlippery(wet_setting = TURF_WET_WATER, infinite = FALSE) // 1 = Water, 2 = Lube, 3 = Ice, 4 = Permafrost + if(wet >= wet_setting) + return + wet = wet_setting + if(wet_setting != TURF_DRY) + if(wet_overlay) + overlays -= wet_overlay + wet_overlay = null + var/turf/simulated/floor/F = src + if(istype(F)) + if(wet_setting >= TURF_WET_ICE) + wet_overlay = image('icons/effects/water.dmi', src, "ice_floor") + else + wet_overlay = image('icons/effects/water.dmi', src, "wet_floor_static") + else + if(wet_setting >= TURF_WET_ICE) + wet_overlay = image('icons/effects/water.dmi', src, "ice_floor") + else + wet_overlay = image('icons/effects/water.dmi', src, "wet_static") + overlays += wet_overlay + if(!infinite) + spawn(rand(790, 820)) // Purely so for visual effect + if(!istype(src, /turf/simulated)) //Because turfs don't get deleted, they change, adapt, transform, evolve and deform. they are one and they are all. + return + MakeDry(wet_setting) + +/turf/simulated/proc/MakeDry(wet_setting = TURF_WET_WATER) + if(wet > wet_setting) + return + wet = TURF_DRY + if(wet_overlay) + overlays -= wet_overlay + +/turf/simulated/Entered(atom/A, atom/OL, ignoreRest = 0) + ..() + if(!ignoreRest) + if(ishuman(A)) + var/mob/living/carbon/human/M = A + if(M.lying) + return 1 + + if(M.flying) + return ..() + + switch(src.wet) + if(TURF_WET_WATER) + if(!(M.slip("the wet floor", 4, 2, tilesSlipped = 0, walkSafely = 1))) + M.inertia_dir = 0 + return + + if(TURF_WET_LUBE) //lube + M.slip("the floor", 0, 5, tilesSlipped = 3, walkSafely = 0, slipAny = 1) + + + if(TURF_WET_ICE) // Ice + if(M.slip("the icy floor", 4, 2, tilesSlipped = 0, walkSafely = 0)) + M.inertia_dir = 0 + if(prob(5)) + var/obj/item/organ/external/affected = M.get_organ("head") + if(affected) + M.apply_damage(5, BRUTE, "head") + M.visible_message("[M] hits their head on the ice!") + playsound(src, 'sound/weapons/genhit1.ogg', 50, 1) + + if(TURF_WET_PERMAFROST) // Permafrost + M.slip("the frosted floor", 0, 5, tilesSlipped = 1, walkSafely = 0, slipAny = 1) + +/turf/simulated/ChangeTurf(path, defer_change = FALSE, keep_icon = TRUE, ignore_air = FALSE) + . = ..() + queue_smooth_neighbors(src) + +/turf/simulated/proc/is_shielded() diff --git a/code/game/turfs/simulated/floor.dm b/code/game/turfs/simulated/floor.dm index cfa5f43858e7..e425c3458c3b 100644 --- a/code/game/turfs/simulated/floor.dm +++ b/code/game/turfs/simulated/floor.dm @@ -1,253 +1,253 @@ -//This is so damaged or burnt tiles or platings don't get remembered as the default tile -var/list/icons_to_ignore_at_floor_init = list("damaged1","damaged2","damaged3","damaged4", - "damaged5","panelscorched","floorscorched1","floorscorched2","platingdmg1","platingdmg2", - "platingdmg3","plating","light_on","light_on_flicker1","light_on_flicker2", - "warnplate", "warnplatecorner","metalfoam", "ironfoam", - "light_on_clicker3","light_on_clicker4","light_on_clicker5","light_broken", - "light_on_broken","light_off","wall_thermite","grass1","grass2","grass3","grass4", - "asteroid","asteroid_dug", - "asteroid0","asteroid1","asteroid2","asteroid3","asteroid4", - "asteroid5","asteroid6","asteroid7","asteroid8","asteroid9","asteroid10","asteroid11","asteroid12", - "oldburning","light-on-r","light-on-y","light-on-g","light-on-b", "wood", "wood-broken", "carpet", - "carpetcorner", "carpetside", "carpet", "ironsand1", "ironsand2", "ironsand3", "ironsand4", "ironsand5", - "ironsand6", "ironsand7", "ironsand8", "ironsand9", "ironsand10", "ironsand11", - "ironsand12", "ironsand13", "ironsand14", "ironsand15") - -/turf/simulated/floor - name = "floor" - icon = 'icons/turf/floors.dmi' - icon_state = "dont_use_this_floor" - plane = FLOOR_PLANE - var/icon_regular_floor = "floor" //used to remember what icon the tile should have by default - var/icon_plating = "plating" - thermal_conductivity = 0.040 - heat_capacity = 10000 - var/lava = 0 - var/broken = 0 - var/burnt = 0 - var/current_overlay = null - var/floor_tile = null //tile that this floor drops - var/obj/item/stack/tile/builtin_tile = null //needed for performance reasons when the singularity rips off floor tiles - var/list/broken_states = list("damaged1", "damaged2", "damaged3", "damaged4", "damaged5") - var/list/burnt_states = list("floorscorched1", "floorscorched2") - var/list/prying_tool_list = list(TOOL_CROWBAR) //What tool/s can we use to pry up the tile? - -/turf/simulated/floor/New() - ..() - if(icon_state in icons_to_ignore_at_floor_init) //so damaged/burned tiles or plating icons aren't saved as the default - icon_regular_floor = "floor" - else - icon_regular_floor = icon_state - if(floor_tile) - builtin_tile = new floor_tile - -/turf/simulated/floor/Destroy() - QDEL_NULL(builtin_tile) - return ..() - - -//turf/simulated/floor/CanPass(atom/movable/mover, turf/target, height=0) -// if((istype(mover, /obj/machinery/vehicle) && !(src.burnt))) -// if(!( locate(/obj/machinery/mass_driver, src) )) -// return 0 -// return ..() - -/turf/simulated/floor/ex_act(severity) - if(is_shielded()) - return - switch(severity) - if(1.0) - ChangeTurf(baseturf) - if(2.0) - switch(pick(1,2;75,3)) - if(1) - spawn(0) - ReplaceWithLattice() - if(prob(33)) new /obj/item/stack/sheet/metal(src) - if(2) - ChangeTurf(baseturf) - if(3) - if(prob(80)) - break_tile_to_plating() - else - break_tile() - hotspot_expose(1000,CELL_VOLUME) - if(prob(33)) new /obj/item/stack/sheet/metal(src) - if(3.0) - if(prob(50)) - break_tile() - hotspot_expose(1000,CELL_VOLUME) - return - -/turf/simulated/floor/burn_down() - ex_act(2) - -/turf/simulated/floor/is_shielded() - for(var/obj/structure/A in contents) - if(A.level == 3) - return 1 - -/turf/simulated/floor/blob_act(obj/structure/blob/B) - return - -/turf/simulated/floor/proc/update_icon() - update_visuals() - overlays -= current_overlay - if(current_overlay) - overlays.Add(current_overlay) - return 1 - -/turf/simulated/floor/proc/break_tile_to_plating() - var/turf/simulated/floor/plating/T = make_plating() - T.break_tile() - -/turf/simulated/floor/break_tile() - if(broken) - return - current_overlay = pick(broken_states) - broken = TRUE - update_icon() - -/turf/simulated/floor/burn_tile() - if(burnt) - return - current_overlay = pick(burnt_states) - burnt = TRUE - update_icon() - -/turf/simulated/floor/proc/make_plating() - return ChangeTurf(/turf/simulated/floor/plating) - -/turf/simulated/floor/ChangeTurf(turf/simulated/floor/T, defer_change = FALSE, keep_icon = TRUE, ignore_air = FALSE) - if(!istype(src, /turf/simulated/floor)) - return ..() //fucking turfs switch the fucking src of the fucking running procs - if(!ispath(T, /turf/simulated/floor)) - return ..() - - var/old_icon = icon_regular_floor - var/old_plating = icon_plating - var/old_dir = dir - - var/turf/simulated/floor/W = ..() - - if(keep_icon) - W.icon_regular_floor = old_icon - W.icon_plating = old_plating - W.dir = old_dir - - W.update_icon() - return W - -/turf/simulated/floor/attackby(obj/item/C as obj, mob/user as mob, params) - if(!C || !user) - return TRUE - - if(..()) - return TRUE - - if(intact && istype(C, /obj/item/stack/tile)) - try_replace_tile(C, user, params) - - if(istype(C, /obj/item/pipe)) - var/obj/item/pipe/P = C - if(P.pipe_type != -1) // ANY PIPE - user.visible_message( \ - "[user] starts sliding [P] along \the [src].", \ - "You slide [P] along \the [src].", \ - "You hear the scrape of metal against something.") - user.drop_item() - - if(P.is_bent_pipe()) // bent pipe rotation fix see construction.dm - P.dir = 5 - if(user.dir == 1) - P.dir = 6 - else if(user.dir == 2) - P.dir = 9 - else if(user.dir == 4) - P.dir = 10 - else - P.setDir(user.dir) - - P.x = src.x - P.y = src.y - P.z = src.z - P.forceMove(src) - return TRUE - return FALSE - -/turf/simulated/floor/crowbar_act(mob/user, obj/item/I) - if(!intact) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - pry_tile(I, user, TRUE) - -/turf/simulated/floor/proc/try_replace_tile(obj/item/stack/tile/T, mob/user, params) - if(T.turf_type == type) - return - var/obj/item/thing = user.get_inactive_hand() - if(!prying_tool_list.Find(thing.tool_behaviour)) - return - var/turf/simulated/floor/plating/P = pry_tile(thing, user, TRUE) - if(!istype(P)) - return - P.attackby(T, user, params) - -/turf/simulated/floor/proc/pry_tile(obj/item/C, mob/user, silent = FALSE) - if(!silent) - playsound(src, C.usesound, 80, 1) - return remove_tile(user, silent) - -/turf/simulated/floor/proc/remove_tile(mob/user, silent = FALSE, make_tile = TRUE) - if(broken || burnt) - broken = 0 - burnt = 0 - current_overlay = null - if(user && !silent) - to_chat(user, "You remove the broken plating.") - else - if(user && !silent) - to_chat(user, "You remove the floor tile.") - if(builtin_tile && make_tile) - builtin_tile.forceMove(src) - builtin_tile = null - return make_plating() - -/turf/simulated/floor/singularity_pull(S, current_size) - ..() - if(current_size == STAGE_THREE) - if(prob(30)) - if(builtin_tile) - builtin_tile.loc = src - builtin_tile = null - make_plating() - else if(current_size == STAGE_FOUR) - if(prob(50)) - if(builtin_tile) - builtin_tile.loc = src - builtin_tile = null - make_plating() - else if(current_size >= STAGE_FIVE) - if(builtin_tile) - if(prob(70)) - builtin_tile.loc = src - builtin_tile = null - make_plating() - else if(prob(50)) - ReplaceWithLattice() - -/turf/simulated/floor/narsie_act() - if(prob(20)) - ChangeTurf(/turf/simulated/floor/engine/cult) - -/turf/simulated/floor/ratvar_act(force, ignore_mobs) - . = ..() - if(.) - ChangeTurf(/turf/simulated/floor/clockwork) - -/turf/simulated/floor/acid_melt() - ChangeTurf(baseturf) - -/turf/simulated/floor/can_have_cabling() - return !burnt && !broken +//This is so damaged or burnt tiles or platings don't get remembered as the default tile +GLOBAL_LIST_INIT(icons_to_ignore_at_floor_init, list("damaged1","damaged2","damaged3","damaged4", + "damaged5","panelscorched","floorscorched1","floorscorched2","platingdmg1","platingdmg2", + "platingdmg3","plating","light_on","light_on_flicker1","light_on_flicker2", + "warnplate", "warnplatecorner","metalfoam", "ironfoam", + "light_on_clicker3","light_on_clicker4","light_on_clicker5","light_broken", + "light_on_broken","light_off","wall_thermite","grass1","grass2","grass3","grass4", + "asteroid","asteroid_dug", + "asteroid0","asteroid1","asteroid2","asteroid3","asteroid4", + "asteroid5","asteroid6","asteroid7","asteroid8","asteroid9","asteroid10","asteroid11","asteroid12", + "oldburning","light-on-r","light-on-y","light-on-g","light-on-b", "wood", "wood-broken", "carpet", + "carpetcorner", "carpetside", "carpet", "ironsand1", "ironsand2", "ironsand3", "ironsand4", "ironsand5", + "ironsand6", "ironsand7", "ironsand8", "ironsand9", "ironsand10", "ironsand11", + "ironsand12", "ironsand13", "ironsand14", "ironsand15")) + +/turf/simulated/floor + name = "floor" + icon = 'icons/turf/floors.dmi' + icon_state = "dont_use_this_floor" + plane = FLOOR_PLANE + var/icon_regular_floor = "floor" //used to remember what icon the tile should have by default + var/icon_plating = "plating" + thermal_conductivity = 0.040 + heat_capacity = 10000 + var/lava = 0 + var/broken = 0 + var/burnt = 0 + var/current_overlay = null + var/floor_tile = null //tile that this floor drops + var/obj/item/stack/tile/builtin_tile = null //needed for performance reasons when the singularity rips off floor tiles + var/list/broken_states = list("damaged1", "damaged2", "damaged3", "damaged4", "damaged5") + var/list/burnt_states = list("floorscorched1", "floorscorched2") + var/list/prying_tool_list = list(TOOL_CROWBAR) //What tool/s can we use to pry up the tile? + +/turf/simulated/floor/New() + ..() + if(icon_state in GLOB.icons_to_ignore_at_floor_init) //so damaged/burned tiles or plating icons aren't saved as the default + icon_regular_floor = "floor" + else + icon_regular_floor = icon_state + if(floor_tile) + builtin_tile = new floor_tile + +/turf/simulated/floor/Destroy() + QDEL_NULL(builtin_tile) + return ..() + + +//turf/simulated/floor/CanPass(atom/movable/mover, turf/target, height=0) +// if((istype(mover, /obj/machinery/vehicle) && !(src.burnt))) +// if(!( locate(/obj/machinery/mass_driver, src) )) +// return 0 +// return ..() + +/turf/simulated/floor/ex_act(severity) + if(is_shielded()) + return + switch(severity) + if(1.0) + ChangeTurf(baseturf) + if(2.0) + switch(pick(1,2;75,3)) + if(1) + spawn(0) + ReplaceWithLattice() + if(prob(33)) new /obj/item/stack/sheet/metal(src) + if(2) + ChangeTurf(baseturf) + if(3) + if(prob(80)) + break_tile_to_plating() + else + break_tile() + hotspot_expose(1000,CELL_VOLUME) + if(prob(33)) new /obj/item/stack/sheet/metal(src) + if(3.0) + if(prob(50)) + break_tile() + hotspot_expose(1000,CELL_VOLUME) + return + +/turf/simulated/floor/burn_down() + ex_act(2) + +/turf/simulated/floor/is_shielded() + for(var/obj/structure/A in contents) + if(A.level == 3) + return 1 + +/turf/simulated/floor/blob_act(obj/structure/blob/B) + return + +/turf/simulated/floor/proc/update_icon() + update_visuals() + overlays -= current_overlay + if(current_overlay) + overlays.Add(current_overlay) + return 1 + +/turf/simulated/floor/proc/break_tile_to_plating() + var/turf/simulated/floor/plating/T = make_plating() + T.break_tile() + +/turf/simulated/floor/break_tile() + if(broken) + return + current_overlay = pick(broken_states) + broken = TRUE + update_icon() + +/turf/simulated/floor/burn_tile() + if(burnt) + return + current_overlay = pick(burnt_states) + burnt = TRUE + update_icon() + +/turf/simulated/floor/proc/make_plating() + return ChangeTurf(/turf/simulated/floor/plating) + +/turf/simulated/floor/ChangeTurf(turf/simulated/floor/T, defer_change = FALSE, keep_icon = TRUE, ignore_air = FALSE) + if(!istype(src, /turf/simulated/floor)) + return ..() //fucking turfs switch the fucking src of the fucking running procs + if(!ispath(T, /turf/simulated/floor)) + return ..() + + var/old_icon = icon_regular_floor + var/old_plating = icon_plating + var/old_dir = dir + + var/turf/simulated/floor/W = ..() + + if(keep_icon) + W.icon_regular_floor = old_icon + W.icon_plating = old_plating + W.dir = old_dir + + W.update_icon() + return W + +/turf/simulated/floor/attackby(obj/item/C as obj, mob/user as mob, params) + if(!C || !user) + return TRUE + + if(..()) + return TRUE + + if(intact && istype(C, /obj/item/stack/tile)) + try_replace_tile(C, user, params) + + if(istype(C, /obj/item/pipe)) + var/obj/item/pipe/P = C + if(P.pipe_type != -1) // ANY PIPE + user.visible_message( \ + "[user] starts sliding [P] along \the [src].", \ + "You slide [P] along \the [src].", \ + "You hear the scrape of metal against something.") + user.drop_item() + + if(P.is_bent_pipe()) // bent pipe rotation fix see construction.dm + P.dir = 5 + if(user.dir == 1) + P.dir = 6 + else if(user.dir == 2) + P.dir = 9 + else if(user.dir == 4) + P.dir = 10 + else + P.setDir(user.dir) + + P.x = src.x + P.y = src.y + P.z = src.z + P.forceMove(src) + return TRUE + return FALSE + +/turf/simulated/floor/crowbar_act(mob/user, obj/item/I) + if(!intact) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + pry_tile(I, user, TRUE) + +/turf/simulated/floor/proc/try_replace_tile(obj/item/stack/tile/T, mob/user, params) + if(T.turf_type == type) + return + var/obj/item/thing = user.get_inactive_hand() + if(!prying_tool_list.Find(thing.tool_behaviour)) + return + var/turf/simulated/floor/plating/P = pry_tile(thing, user, TRUE) + if(!istype(P)) + return + P.attackby(T, user, params) + +/turf/simulated/floor/proc/pry_tile(obj/item/C, mob/user, silent = FALSE) + if(!silent) + playsound(src, C.usesound, 80, 1) + return remove_tile(user, silent) + +/turf/simulated/floor/proc/remove_tile(mob/user, silent = FALSE, make_tile = TRUE) + if(broken || burnt) + broken = 0 + burnt = 0 + current_overlay = null + if(user && !silent) + to_chat(user, "You remove the broken plating.") + else + if(user && !silent) + to_chat(user, "You remove the floor tile.") + if(builtin_tile && make_tile) + builtin_tile.forceMove(src) + builtin_tile = null + return make_plating() + +/turf/simulated/floor/singularity_pull(S, current_size) + ..() + if(current_size == STAGE_THREE) + if(prob(30)) + if(builtin_tile) + builtin_tile.loc = src + builtin_tile = null + make_plating() + else if(current_size == STAGE_FOUR) + if(prob(50)) + if(builtin_tile) + builtin_tile.loc = src + builtin_tile = null + make_plating() + else if(current_size >= STAGE_FIVE) + if(builtin_tile) + if(prob(70)) + builtin_tile.loc = src + builtin_tile = null + make_plating() + else if(prob(50)) + ReplaceWithLattice() + +/turf/simulated/floor/narsie_act() + if(prob(20)) + ChangeTurf(/turf/simulated/floor/engine/cult) + +/turf/simulated/floor/ratvar_act(force, ignore_mobs) + . = ..() + if(.) + ChangeTurf(/turf/simulated/floor/clockwork) + +/turf/simulated/floor/acid_melt() + ChangeTurf(baseturf) + +/turf/simulated/floor/can_have_cabling() + return !burnt && !broken diff --git a/code/game/turfs/simulated/floor/asteroid.dm b/code/game/turfs/simulated/floor/asteroid.dm index eccc5805657d..6d5d95dff0ec 100644 --- a/code/game/turfs/simulated/floor/asteroid.dm +++ b/code/game/turfs/simulated/floor/asteroid.dm @@ -205,7 +205,7 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me length = set_length // Get our directiosn - forward_cave_dir = pick(alldirs - exclude_dir) + forward_cave_dir = pick(GLOB.alldirs - exclude_dir) // Get the opposite direction of our facing direction backward_cave_dir = angle2dir(dir2angle(forward_cave_dir) + 180) @@ -352,4 +352,4 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me oxygen = 22 nitrogen = 82 temperature = 180 - planetary_atmos = FALSE \ No newline at end of file + planetary_atmos = FALSE diff --git a/code/game/turfs/simulated/floor/chasm.dm b/code/game/turfs/simulated/floor/chasm.dm index fda758238e87..d8ed888bc27e 100644 --- a/code/game/turfs/simulated/floor/chasm.dm +++ b/code/game/turfs/simulated/floor/chasm.dm @@ -186,7 +186,7 @@ AM.alpha = oldalpha AM.color = oldcolor AM.transform = oldtransform - AM.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1, 10),rand(1, 10)) + AM.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1, 10),rand(1, 10)) /turf/simulated/floor/chasm/straight_down/lava_land_surface/normal_air oxygen = MOLES_O2STANDARD @@ -194,4 +194,4 @@ temperature = T20C /turf/simulated/floor/chasm/CanPass(atom/movable/mover, turf/target) - return 1 \ No newline at end of file + return 1 diff --git a/code/game/turfs/simulated/floor/indestructible.dm b/code/game/turfs/simulated/floor/indestructible.dm index 3896dc513f2a..f231a4fd0b34 100644 --- a/code/game/turfs/simulated/floor/indestructible.dm +++ b/code/game/turfs/simulated/floor/indestructible.dm @@ -82,4 +82,4 @@ /turf/simulated/floor/indestructible/hierophant/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) return FALSE -/turf/simulated/floor/indestructible/hierophant/two \ No newline at end of file +/turf/simulated/floor/indestructible/hierophant/two diff --git a/code/game/turfs/simulated/floor/lava.dm b/code/game/turfs/simulated/floor/lava.dm index 5a512b5455f0..57e908409203 100644 --- a/code/game/turfs/simulated/floor/lava.dm +++ b/code/game/turfs/simulated/floor/lava.dm @@ -138,4 +138,4 @@ baseturf = /turf/simulated/floor/chasm/straight_down/lava_land_surface /turf/simulated/floor/plating/lava/smooth/airless - temperature = TCMB \ No newline at end of file + temperature = TCMB diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index 118132765a1a..ebad3554f18a 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -32,7 +32,7 @@ icon = smooth_icon . = ..() if(mineralType && mineralAmt && spread && spreadChance) - for(var/dir in cardinal) + for(var/dir in GLOB.cardinal) if(prob(spreadChance)) var/turf/T = get_step(src, dir) if(istype(T, /turf/simulated/mineral/random)) @@ -511,4 +511,4 @@ #undef GIBTONITE_UNSTRUCK #undef GIBTONITE_ACTIVE #undef GIBTONITE_STABLE -#undef GIBTONITE_DETONATE \ No newline at end of file +#undef GIBTONITE_DETONATE diff --git a/code/game/turfs/simulated/river.dm b/code/game/turfs/simulated/river.dm index c7a3e742b6c4..f463f3fb5641 100644 --- a/code/game/turfs/simulated/river.dm +++ b/code/game/turfs/simulated/river.dm @@ -82,7 +82,7 @@ var/turf/simulated/mineral/M = T logged_turf_type = M.turf_type - if(get_dir(src, F) in cardinal) + if(get_dir(src, F) in GLOB.cardinal) cardinal_turfs += F else diagonal_turfs += F diff --git a/code/game/turfs/simulated/walls.dm b/code/game/turfs/simulated/walls.dm index 7dd41815a4ce..078a1b8e513b 100644 --- a/code/game/turfs/simulated/walls.dm +++ b/code/game/turfs/simulated/walls.dm @@ -1,506 +1,506 @@ -#define WALL_DENT_HIT 1 -#define WALL_DENT_SHOT 2 -#define MAX_DENT_DECALS 15 - -/turf/simulated/wall - name = "wall" - desc = "A huge chunk of metal used to seperate rooms." - icon = 'icons/turf/walls/wall.dmi' - icon_state = "wall" - var/rotting = 0 - - var/damage = 0 - var/damage_cap = 100 //Wall will break down to girders if damage reaches this point - - var/damage_overlay - var/global/damage_overlays[8] - - var/max_temperature = 1800 //K, walls will take damage if they're next to a fire hotter than this - - opacity = 1 - density = 1 - blocks_air = 1 - explosion_block = 1 - - thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT - heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall - - var/can_dismantle_with_welder = TRUE - var/hardness = 40 //lower numbers are harder. Used to determine the probability of a hulk smashing through. - var/slicing_duration = 100 - var/engraving //engraving on the wall - var/engraving_quality - var/list/dent_decals - var/sheet_type = /obj/item/stack/sheet/metal - var/sheet_amount = 2 - var/girder_type = /obj/structure/girder - var/obj/item/stack/sheet/builtin_sheet = null - - canSmoothWith = list( - /turf/simulated/wall, - /turf/simulated/wall/r_wall, - /obj/structure/falsewall, - /obj/structure/falsewall/reinforced, - /turf/simulated/wall/rust, - /turf/simulated/wall/r_wall/rust, - /turf/simulated/wall/r_wall/coated) - smooth = SMOOTH_TRUE - -/turf/simulated/wall/New() - ..() - builtin_sheet = new sheet_type - -/turf/simulated/wall/BeforeChange() - for(var/obj/effect/overlay/wall_rot/WR in src) - qdel(WR) - . = ..() - -//Appearance -/turf/simulated/wall/examine(mob/user) - . = ..() - - if(!damage) - . += "It looks fully intact." - else - var/dam = damage / damage_cap - if(dam <= 0.3) - . += "It looks slightly damaged." - else if(dam <= 0.6) - . += "It looks moderately damaged." - else - . += "It looks heavily damaged." - - if(rotting) - . += "There is fungus growing on [src]." - -/turf/simulated/wall/proc/update_icon() - if(!damage_overlays[1]) //list hasn't been populated - generate_overlays() - - queue_smooth(src) - if(!damage) - if(damage_overlay) - overlays -= damage_overlays[damage_overlay] - damage_overlay = 0 - return - - var/overlay = round(damage / damage_cap * damage_overlays.len) + 1 - if(overlay > damage_overlays.len) - overlay = damage_overlays.len - - if(damage_overlay && overlay == damage_overlay) //No need to update. - return - if(damage_overlay) - overlays -= damage_overlays[damage_overlay] - overlays += damage_overlays[overlay] - damage_overlay = overlay - -/turf/simulated/wall/proc/generate_overlays() - var/alpha_inc = 256 / damage_overlays.len - - for(var/i = 1; i <= damage_overlays.len; i++) - var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") - img.blend_mode = BLEND_MULTIPLY - img.alpha = (i * alpha_inc) - 1 - damage_overlays[i] = img - -//Damage - -/turf/simulated/wall/proc/take_damage(dam) - if(dam) - damage = max(0, damage + dam) - update_damage() - return - -/turf/simulated/wall/proc/update_damage() - var/cap = damage_cap - if(rotting) - cap = cap / 10 - - if(damage >= cap) - dismantle_wall() - else - update_icon() - - return - -/turf/simulated/wall/proc/adjacent_fire_act(turf/simulated/wall, radiated_temperature) - if(radiated_temperature > max_temperature) - take_damage(rand(10, 20) * (radiated_temperature / max_temperature)) - -/turf/simulated/wall/handle_ricochet(obj/item/projectile/P) //A huge pile of shitcode! - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - return TRUE - -/turf/simulated/wall/proc/dismantle_wall(devastated = 0, explode = 0) - if(devastated) - devastate_wall() - else - playsound(src, 'sound/items/welder.ogg', 100, 1) - var/newgirder = break_wall() - if(newgirder) //maybe we don't /want/ a girder! - transfer_fingerprints_to(newgirder) - - for(var/obj/O in src.contents) //Eject contents! - if(istype(O,/obj/structure/sign/poster)) - var/obj/structure/sign/poster/P = O - P.roll_and_drop(src) - else - O.forceMove(src) - - ChangeTurf(/turf/simulated/floor/plating) - -/turf/simulated/wall/proc/break_wall() - new sheet_type(src, sheet_amount) - return new girder_type(src) - -/turf/simulated/wall/proc/devastate_wall() - new sheet_type(src, sheet_amount) - new /obj/item/stack/sheet/metal(src) - -/turf/simulated/wall/ex_act(severity) - switch(severity) - if(1.0) - ChangeTurf(baseturf) - return - if(2.0) - if(prob(50)) - take_damage(rand(150, 250)) - else - dismantle_wall(1, 1) - if(3.0) - take_damage(rand(0, 250)) - else - return - -/turf/simulated/wall/blob_act(obj/structure/blob/B) - if(prob(50)) - dismantle_wall() - else - add_dent(WALL_DENT_HIT) - -/turf/simulated/wall/rpd_act(mob/user, obj/item/rpd/our_rpd) - if(our_rpd.mode == RPD_ATMOS_MODE) - if(!our_rpd.ranged) - playsound(src, "sound/weapons/circsawhit.ogg", 50, 1) - user.visible_message("[user] starts drilling a hole in [src]...", "You start drilling a hole in [src]...", "You hear drilling.") - if(!do_after(user, our_rpd.walldelay, target = src)) //Drilling into walls takes time - return - our_rpd.create_atmos_pipe(user, src) - else if(our_rpd.mode == RPD_DISPOSALS_MODE && !our_rpd.ranged) - return - else - ..() - -/turf/simulated/wall/mech_melee_attack(obj/mecha/M) - M.do_attack_animation(src) - switch(M.damtype) - if(BRUTE) - playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) - M.visible_message("[M.name] hits [src]!", "You hit [src]!") - if(prob(hardness + M.force) && M.force > 20) - dismantle_wall(1) - playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE) - else - add_dent(WALL_DENT_HIT) - if(BURN) - playsound(src, 'sound/items/welder.ogg', 100, TRUE) - if(TOX) - playsound(src, 'sound/effects/spray2.ogg', 100, TRUE) - return FALSE - -// Wall-rot effect, a nasty fungus that destroys walls. -/turf/simulated/wall/proc/rot() - if(!rotting) - rotting = 1 - - var/number_rots = rand(2,3) - for(var/i=0, iThe thermite starts melting through the wall.") - - spawn(wait) - if(O) qdel(O) - return - -//Interactions - -/turf/simulated/wall/attack_animal(mob/living/simple_animal/M) - M.changeNext_move(CLICK_CD_MELEE) - M.do_attack_animation(src) - if((M.environment_smash & ENVIRONMENT_SMASH_WALLS) || (M.environment_smash & ENVIRONMENT_SMASH_RWALLS)) - if(M.environment_smash & ENVIRONMENT_SMASH_RWALLS) - dismantle_wall(1) - to_chat(M, "You smash through the wall.") - else - to_chat(M, text("You smash against the wall.")) - take_damage(rand(25, 75)) - return - - to_chat(M, "You push the wall but nothing happens!") - return - -/turf/simulated/wall/attack_hulk(mob/user, does_attack_animation = FALSE) - ..(user, TRUE) - - if(prob(hardness) || rotting) - playsound(src, 'sound/effects/meteorimpact.ogg', 100, 1) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - dismantle_wall(TRUE) - else - playsound(src, 'sound/effects/bang.ogg', 50, 1) - add_dent(WALL_DENT_HIT) - to_chat(user, text("You punch the wall.")) - return TRUE - -/turf/simulated/wall/attack_hand(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - if(rotting) - if(hardness <= 10) - to_chat(user, "This wall feels rather unstable.") - return - else - to_chat(user, "The wall crumbles under your touch.") - dismantle_wall() - return - - to_chat(user, "You push the wall but nothing happens!") - playsound(src, 'sound/weapons/genhit.ogg', 25, 1) - add_fingerprint(user) - return ..() - -/turf/simulated/wall/attackby(obj/item/I, mob/user, params) - user.changeNext_move(CLICK_CD_MELEE) - - if(!isturf(user.loc)) - return // No touching walls unless you're on a turf (pretty sure attackby can't be called anyways but whatever) - - if(rotting && try_rot(I, user, params)) - return - - if(try_decon(I, user, params)) - return - - if(try_destroy(I, user, params)) - return - - if(try_wallmount(I, user, params)) - return - // The magnetic gripper does a separate attackby, so bail from this one - if(istype(I, /obj/item/gripper)) - return - - return ..() - -/turf/simulated/wall/welder_act(mob/user, obj/item/I) - . = TRUE - if(thermite && I.use_tool(src, user, volume = I.tool_volume)) - thermitemelt(user) - return - if(rotting) - if(I.use_tool(src, user, volume = I.tool_volume)) - for(var/obj/effect/overlay/wall_rot/WR in src) - qdel(WR) - rotting = FALSE - to_chat(user, "You burn off the fungi with [I].") - return - - if(!I.tool_use_check(user, 0)) //Wall repair stuff - return - - var/time_required = slicing_duration - var/intention - if(can_dismantle_with_welder) - intention = "Dismantle" - if(damage || LAZYLEN(dent_decals)) - intention = "Repair" - if(can_dismantle_with_welder) - var/moved_away = user.loc - intention = alert(user, "Would you like to repair or dismantle [src]?", "[src]", "Repair", "Dismantle") - if(user.loc != moved_away) - to_chat(user, "Stay still while doing this!") - return - if(intention == "Repair") - time_required = max(5, damage / 5) - if(!intention) - return - if(intention == "Dismantle") - WELDER_ATTEMPT_SLICING_MESSAGE - else - WELDER_ATTEMPT_REPAIR_MESSAGE - if(I.use_tool(src, user, time_required, volume = I.tool_volume)) - if(intention == "Dismantle") - WELDER_SLICING_SUCCESS_MESSAGE - dismantle_wall() - else - WELDER_REPAIR_SUCCESS_MESSAGE - cut_overlay(dent_decals) - dent_decals?.Cut() - take_damage(-damage) - -/turf/simulated/wall/proc/try_rot(obj/item/I, mob/user, params) - if((!is_sharp(I) && I.force >= 10) || I.force >= 20) - to_chat(user, "[src] crumbles away under the force of your [I.name].") - dismantle_wall(1) - return TRUE - return FALSE - -/turf/simulated/wall/proc/try_decon(obj/item/I, mob/user, params) - if(istype(I, /obj/item/gun/energy/plasmacutter)) - to_chat(user, "You begin slicing through the outer plating.") - playsound(src, I.usesound, 100, 1) - - if(do_after(user, istype(sheet_type, /obj/item/stack/sheet/mineral/diamond) ? 120 * I.toolspeed : 60 * I.toolspeed, target = src)) - to_chat(user, "You remove the outer plating.") - dismantle_wall() - visible_message("[user] slices apart [src]!", "You hear metal being sliced apart.") - return TRUE - - return FALSE - -/turf/simulated/wall/proc/try_destroy(obj/item/I, mob/user, params) - var/isdiamond = istype(sheet_type, /obj/item/stack/sheet/mineral/diamond) // snowflake bullshit - - if(istype(I, /obj/item/pickaxe/drill/diamonddrill)) - to_chat(user, "You begin to drill though the wall.") - - if(do_after(user, isdiamond ? 480 * I.toolspeed : 240 * I.toolspeed, target = src)) // Diamond pickaxe has 0.25 toolspeed, so 120/60 - to_chat(user, "Your [I.name] tears though the last of the reinforced plating.") - dismantle_wall() - visible_message("[user] drills through [src]!", "You hear the grinding of metal.") - return TRUE - - else if(istype(I, /obj/item/pickaxe/drill/jackhammer)) - to_chat(user, "You begin to disintegrates the wall.") - - if(do_after(user, isdiamond ? 600 * I.toolspeed : 300 * I.toolspeed, target = src)) // Jackhammer has 0.1 toolspeed, so 60/30 - to_chat(user, "Your [I.name] disintegrates the reinforced plating.") - dismantle_wall() - visible_message("[user] disintegrates [src]!","You hear the grinding of metal.") - return TRUE - - return FALSE - -/turf/simulated/wall/proc/try_wallmount(obj/item/I, mob/user, params) - if(istype(I, /obj/item/mounted)) - return TRUE // We don't want attack_hand running and doing stupid shit with this - - if(istype(I, /obj/item/poster)) - place_poster(I, user) - return TRUE - - //Bone White - Place pipes on walls // I fucking hate your code with a passion bone - if(istype(I, /obj/item/pipe)) - var/obj/item/pipe/P = I - if(P.pipe_type != -1) // ANY PIPE - playsound(get_turf(src), 'sound/weapons/circsawhit.ogg', 50, 1) - user.visible_message( - "[user] starts drilling a hole in [src].", - "You start drilling a hole in [src].", - "You hear a drill.") - - if(do_after(user, 80 * P.toolspeed, target = src)) - user.visible_message( - "[user] drills a hole in [src] and pushes [P] into the void.", - "You finish drilling [src] and push [P] into the void.", - "You hear a ratchet.") - - user.drop_item() - if(P.is_bent_pipe()) // bent pipe rotation fix see construction.dm - P.setDir(5) - if(user.dir == 1) - P.setDir(6) - else if(user.dir == 2) - P.setDir(9) - else if(user.dir == 4) - P.setDir(10) - else - P.setDir(user.dir) - P.forceMove(src) - P.level = 2 - return TRUE - return FALSE - -/turf/simulated/wall/singularity_pull(S, current_size) - ..() - wall_singularity_pull(current_size) - -/turf/simulated/wall/proc/wall_singularity_pull(current_size) - if(current_size >= STAGE_FIVE) - if(prob(50)) - dismantle_wall() - return - if(current_size == STAGE_FOUR) - if(prob(30)) - dismantle_wall() - -/turf/simulated/wall/narsie_act() - if(prob(20)) - ChangeTurf(/turf/simulated/wall/cult) - -/turf/simulated/wall/acid_act(acidpwr, acid_volume) - if(explosion_block >= 2) - acidpwr = min(acidpwr, 50) //we reduce the power so strong walls never get melted. - . = ..() - -/turf/simulated/wall/acid_melt() - dismantle_wall(1) - -/turf/simulated/wall/proc/add_dent(denttype, x=rand(-8, 8), y=rand(-8, 8)) - if(LAZYLEN(dent_decals) >= MAX_DENT_DECALS) - return - - var/mutable_appearance/decal = mutable_appearance('icons/effects/effects.dmi', "", BULLET_HOLE_LAYER) - switch(denttype) - if(WALL_DENT_SHOT) - decal.icon_state = "bullet_hole" - if(WALL_DENT_HIT) - decal.icon_state = "impact[rand(1, 3)]" - - decal.pixel_x = x - decal.pixel_y = y - - if(LAZYLEN(dent_decals)) - cut_overlay(dent_decals) - dent_decals += decal - else - dent_decals = list(decal) - - add_overlay(dent_decals) - -#undef MAX_DENT_DECALS \ No newline at end of file +#define WALL_DENT_HIT 1 +#define WALL_DENT_SHOT 2 +#define MAX_DENT_DECALS 15 + +/turf/simulated/wall + name = "wall" + desc = "A huge chunk of metal used to seperate rooms." + icon = 'icons/turf/walls/wall.dmi' + icon_state = "wall" + var/rotting = 0 + + var/damage = 0 + var/damage_cap = 100 //Wall will break down to girders if damage reaches this point + + var/damage_overlay + var/global/damage_overlays[8] + + var/max_temperature = 1800 //K, walls will take damage if they're next to a fire hotter than this + + opacity = 1 + density = 1 + blocks_air = 1 + explosion_block = 1 + + thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT + heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall + + var/can_dismantle_with_welder = TRUE + var/hardness = 40 //lower numbers are harder. Used to determine the probability of a hulk smashing through. + var/slicing_duration = 100 + var/engraving //engraving on the wall + var/engraving_quality + var/list/dent_decals + var/sheet_type = /obj/item/stack/sheet/metal + var/sheet_amount = 2 + var/girder_type = /obj/structure/girder + var/obj/item/stack/sheet/builtin_sheet = null + + canSmoothWith = list( + /turf/simulated/wall, + /turf/simulated/wall/r_wall, + /obj/structure/falsewall, + /obj/structure/falsewall/reinforced, + /turf/simulated/wall/rust, + /turf/simulated/wall/r_wall/rust, + /turf/simulated/wall/r_wall/coated) + smooth = SMOOTH_TRUE + +/turf/simulated/wall/New() + ..() + builtin_sheet = new sheet_type + +/turf/simulated/wall/BeforeChange() + for(var/obj/effect/overlay/wall_rot/WR in src) + qdel(WR) + . = ..() + +//Appearance +/turf/simulated/wall/examine(mob/user) + . = ..() + + if(!damage) + . += "It looks fully intact." + else + var/dam = damage / damage_cap + if(dam <= 0.3) + . += "It looks slightly damaged." + else if(dam <= 0.6) + . += "It looks moderately damaged." + else + . += "It looks heavily damaged." + + if(rotting) + . += "There is fungus growing on [src]." + +/turf/simulated/wall/proc/update_icon() + if(!damage_overlays[1]) //list hasn't been populated + generate_overlays() + + queue_smooth(src) + if(!damage) + if(damage_overlay) + overlays -= damage_overlays[damage_overlay] + damage_overlay = 0 + return + + var/overlay = round(damage / damage_cap * damage_overlays.len) + 1 + if(overlay > damage_overlays.len) + overlay = damage_overlays.len + + if(damage_overlay && overlay == damage_overlay) //No need to update. + return + if(damage_overlay) + overlays -= damage_overlays[damage_overlay] + overlays += damage_overlays[overlay] + damage_overlay = overlay + +/turf/simulated/wall/proc/generate_overlays() + var/alpha_inc = 256 / damage_overlays.len + + for(var/i = 1; i <= damage_overlays.len; i++) + var/image/img = image(icon = 'icons/turf/walls.dmi', icon_state = "overlay_damage") + img.blend_mode = BLEND_MULTIPLY + img.alpha = (i * alpha_inc) - 1 + damage_overlays[i] = img + +//Damage + +/turf/simulated/wall/proc/take_damage(dam) + if(dam) + damage = max(0, damage + dam) + update_damage() + return + +/turf/simulated/wall/proc/update_damage() + var/cap = damage_cap + if(rotting) + cap = cap / 10 + + if(damage >= cap) + dismantle_wall() + else + update_icon() + + return + +/turf/simulated/wall/proc/adjacent_fire_act(turf/simulated/wall, radiated_temperature) + if(radiated_temperature > max_temperature) + take_damage(rand(10, 20) * (radiated_temperature / max_temperature)) + +/turf/simulated/wall/handle_ricochet(obj/item/projectile/P) //A huge pile of shitcode! + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + return TRUE + +/turf/simulated/wall/proc/dismantle_wall(devastated = 0, explode = 0) + if(devastated) + devastate_wall() + else + playsound(src, 'sound/items/welder.ogg', 100, 1) + var/newgirder = break_wall() + if(newgirder) //maybe we don't /want/ a girder! + transfer_fingerprints_to(newgirder) + + for(var/obj/O in src.contents) //Eject contents! + if(istype(O,/obj/structure/sign/poster)) + var/obj/structure/sign/poster/P = O + P.roll_and_drop(src) + else + O.forceMove(src) + + ChangeTurf(/turf/simulated/floor/plating) + +/turf/simulated/wall/proc/break_wall() + new sheet_type(src, sheet_amount) + return new girder_type(src) + +/turf/simulated/wall/proc/devastate_wall() + new sheet_type(src, sheet_amount) + new /obj/item/stack/sheet/metal(src) + +/turf/simulated/wall/ex_act(severity) + switch(severity) + if(1.0) + ChangeTurf(baseturf) + return + if(2.0) + if(prob(50)) + take_damage(rand(150, 250)) + else + dismantle_wall(1, 1) + if(3.0) + take_damage(rand(0, 250)) + else + return + +/turf/simulated/wall/blob_act(obj/structure/blob/B) + if(prob(50)) + dismantle_wall() + else + add_dent(WALL_DENT_HIT) + +/turf/simulated/wall/rpd_act(mob/user, obj/item/rpd/our_rpd) + if(our_rpd.mode == RPD_ATMOS_MODE) + if(!our_rpd.ranged) + playsound(src, "sound/weapons/circsawhit.ogg", 50, 1) + user.visible_message("[user] starts drilling a hole in [src]...", "You start drilling a hole in [src]...", "You hear drilling.") + if(!do_after(user, our_rpd.walldelay, target = src)) //Drilling into walls takes time + return + our_rpd.create_atmos_pipe(user, src) + else if(our_rpd.mode == RPD_DISPOSALS_MODE && !our_rpd.ranged) + return + else + ..() + +/turf/simulated/wall/mech_melee_attack(obj/mecha/M) + M.do_attack_animation(src) + switch(M.damtype) + if(BRUTE) + playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) + M.visible_message("[M.name] hits [src]!", "You hit [src]!") + if(prob(hardness + M.force) && M.force > 20) + dismantle_wall(1) + playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE) + else + add_dent(WALL_DENT_HIT) + if(BURN) + playsound(src, 'sound/items/welder.ogg', 100, TRUE) + if(TOX) + playsound(src, 'sound/effects/spray2.ogg', 100, TRUE) + return FALSE + +// Wall-rot effect, a nasty fungus that destroys walls. +/turf/simulated/wall/proc/rot() + if(!rotting) + rotting = 1 + + var/number_rots = rand(2,3) + for(var/i=0, iThe thermite starts melting through the wall.") + + spawn(wait) + if(O) qdel(O) + return + +//Interactions + +/turf/simulated/wall/attack_animal(mob/living/simple_animal/M) + M.changeNext_move(CLICK_CD_MELEE) + M.do_attack_animation(src) + if((M.environment_smash & ENVIRONMENT_SMASH_WALLS) || (M.environment_smash & ENVIRONMENT_SMASH_RWALLS)) + if(M.environment_smash & ENVIRONMENT_SMASH_RWALLS) + dismantle_wall(1) + to_chat(M, "You smash through the wall.") + else + to_chat(M, text("You smash against the wall.")) + take_damage(rand(25, 75)) + return + + to_chat(M, "You push the wall but nothing happens!") + return + +/turf/simulated/wall/attack_hulk(mob/user, does_attack_animation = FALSE) + ..(user, TRUE) + + if(prob(hardness) || rotting) + playsound(src, 'sound/effects/meteorimpact.ogg', 100, 1) + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + dismantle_wall(TRUE) + else + playsound(src, 'sound/effects/bang.ogg', 50, 1) + add_dent(WALL_DENT_HIT) + to_chat(user, text("You punch the wall.")) + return TRUE + +/turf/simulated/wall/attack_hand(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + if(rotting) + if(hardness <= 10) + to_chat(user, "This wall feels rather unstable.") + return + else + to_chat(user, "The wall crumbles under your touch.") + dismantle_wall() + return + + to_chat(user, "You push the wall but nothing happens!") + playsound(src, 'sound/weapons/genhit.ogg', 25, 1) + add_fingerprint(user) + return ..() + +/turf/simulated/wall/attackby(obj/item/I, mob/user, params) + user.changeNext_move(CLICK_CD_MELEE) + + if(!isturf(user.loc)) + return // No touching walls unless you're on a turf (pretty sure attackby can't be called anyways but whatever) + + if(rotting && try_rot(I, user, params)) + return + + if(try_decon(I, user, params)) + return + + if(try_destroy(I, user, params)) + return + + if(try_wallmount(I, user, params)) + return + // The magnetic gripper does a separate attackby, so bail from this one + if(istype(I, /obj/item/gripper)) + return + + return ..() + +/turf/simulated/wall/welder_act(mob/user, obj/item/I) + . = TRUE + if(thermite && I.use_tool(src, user, volume = I.tool_volume)) + thermitemelt(user) + return + if(rotting) + if(I.use_tool(src, user, volume = I.tool_volume)) + for(var/obj/effect/overlay/wall_rot/WR in src) + qdel(WR) + rotting = FALSE + to_chat(user, "You burn off the fungi with [I].") + return + + if(!I.tool_use_check(user, 0)) //Wall repair stuff + return + + var/time_required = slicing_duration + var/intention + if(can_dismantle_with_welder) + intention = "Dismantle" + if(damage || LAZYLEN(dent_decals)) + intention = "Repair" + if(can_dismantle_with_welder) + var/moved_away = user.loc + intention = alert(user, "Would you like to repair or dismantle [src]?", "[src]", "Repair", "Dismantle") + if(user.loc != moved_away) + to_chat(user, "Stay still while doing this!") + return + if(intention == "Repair") + time_required = max(5, damage / 5) + if(!intention) + return + if(intention == "Dismantle") + WELDER_ATTEMPT_SLICING_MESSAGE + else + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, time_required, volume = I.tool_volume)) + if(intention == "Dismantle") + WELDER_SLICING_SUCCESS_MESSAGE + dismantle_wall() + else + WELDER_REPAIR_SUCCESS_MESSAGE + cut_overlay(dent_decals) + dent_decals?.Cut() + take_damage(-damage) + +/turf/simulated/wall/proc/try_rot(obj/item/I, mob/user, params) + if((!is_sharp(I) && I.force >= 10) || I.force >= 20) + to_chat(user, "[src] crumbles away under the force of your [I.name].") + dismantle_wall(1) + return TRUE + return FALSE + +/turf/simulated/wall/proc/try_decon(obj/item/I, mob/user, params) + if(istype(I, /obj/item/gun/energy/plasmacutter)) + to_chat(user, "You begin slicing through the outer plating.") + playsound(src, I.usesound, 100, 1) + + if(do_after(user, istype(sheet_type, /obj/item/stack/sheet/mineral/diamond) ? 120 * I.toolspeed : 60 * I.toolspeed, target = src)) + to_chat(user, "You remove the outer plating.") + dismantle_wall() + visible_message("[user] slices apart [src]!", "You hear metal being sliced apart.") + return TRUE + + return FALSE + +/turf/simulated/wall/proc/try_destroy(obj/item/I, mob/user, params) + var/isdiamond = istype(sheet_type, /obj/item/stack/sheet/mineral/diamond) // snowflake bullshit + + if(istype(I, /obj/item/pickaxe/drill/diamonddrill)) + to_chat(user, "You begin to drill though the wall.") + + if(do_after(user, isdiamond ? 480 * I.toolspeed : 240 * I.toolspeed, target = src)) // Diamond pickaxe has 0.25 toolspeed, so 120/60 + to_chat(user, "Your [I.name] tears though the last of the reinforced plating.") + dismantle_wall() + visible_message("[user] drills through [src]!", "You hear the grinding of metal.") + return TRUE + + else if(istype(I, /obj/item/pickaxe/drill/jackhammer)) + to_chat(user, "You begin to disintegrates the wall.") + + if(do_after(user, isdiamond ? 600 * I.toolspeed : 300 * I.toolspeed, target = src)) // Jackhammer has 0.1 toolspeed, so 60/30 + to_chat(user, "Your [I.name] disintegrates the reinforced plating.") + dismantle_wall() + visible_message("[user] disintegrates [src]!","You hear the grinding of metal.") + return TRUE + + return FALSE + +/turf/simulated/wall/proc/try_wallmount(obj/item/I, mob/user, params) + if(istype(I, /obj/item/mounted)) + return TRUE // We don't want attack_hand running and doing stupid shit with this + + if(istype(I, /obj/item/poster)) + place_poster(I, user) + return TRUE + + //Bone White - Place pipes on walls // I fucking hate your code with a passion bone + if(istype(I, /obj/item/pipe)) + var/obj/item/pipe/P = I + if(P.pipe_type != -1) // ANY PIPE + playsound(get_turf(src), 'sound/weapons/circsawhit.ogg', 50, 1) + user.visible_message( + "[user] starts drilling a hole in [src].", + "You start drilling a hole in [src].", + "You hear a drill.") + + if(do_after(user, 80 * P.toolspeed, target = src)) + user.visible_message( + "[user] drills a hole in [src] and pushes [P] into the void.", + "You finish drilling [src] and push [P] into the void.", + "You hear a ratchet.") + + user.drop_item() + if(P.is_bent_pipe()) // bent pipe rotation fix see construction.dm + P.setDir(5) + if(user.dir == 1) + P.setDir(6) + else if(user.dir == 2) + P.setDir(9) + else if(user.dir == 4) + P.setDir(10) + else + P.setDir(user.dir) + P.forceMove(src) + P.level = 2 + return TRUE + return FALSE + +/turf/simulated/wall/singularity_pull(S, current_size) + ..() + wall_singularity_pull(current_size) + +/turf/simulated/wall/proc/wall_singularity_pull(current_size) + if(current_size >= STAGE_FIVE) + if(prob(50)) + dismantle_wall() + return + if(current_size == STAGE_FOUR) + if(prob(30)) + dismantle_wall() + +/turf/simulated/wall/narsie_act() + if(prob(20)) + ChangeTurf(/turf/simulated/wall/cult) + +/turf/simulated/wall/acid_act(acidpwr, acid_volume) + if(explosion_block >= 2) + acidpwr = min(acidpwr, 50) //we reduce the power so strong walls never get melted. + . = ..() + +/turf/simulated/wall/acid_melt() + dismantle_wall(1) + +/turf/simulated/wall/proc/add_dent(denttype, x=rand(-8, 8), y=rand(-8, 8)) + if(LAZYLEN(dent_decals) >= MAX_DENT_DECALS) + return + + var/mutable_appearance/decal = mutable_appearance('icons/effects/effects.dmi', "", BULLET_HOLE_LAYER) + switch(denttype) + if(WALL_DENT_SHOT) + decal.icon_state = "bullet_hole" + if(WALL_DENT_HIT) + decal.icon_state = "impact[rand(1, 3)]" + + decal.pixel_x = x + decal.pixel_y = y + + if(LAZYLEN(dent_decals)) + cut_overlay(dent_decals) + dent_decals += decal + else + dent_decals = list(decal) + + add_overlay(dent_decals) + +#undef MAX_DENT_DECALS diff --git a/code/game/turfs/simulated/walls_indestructible.dm b/code/game/turfs/simulated/walls_indestructible.dm index 9b522e34640c..6bb3cb9c22a9 100644 --- a/code/game/turfs/simulated/walls_indestructible.dm +++ b/code/game/turfs/simulated/walls_indestructible.dm @@ -75,4 +75,4 @@ /turf/simulated/wall/indestructible/uranium icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" \ No newline at end of file + icon_state = "uranium" diff --git a/code/game/turfs/simulated/walls_mineral.dm b/code/game/turfs/simulated/walls_mineral.dm index e4084fe5a591..bc04a695da7c 100644 --- a/code/game/turfs/simulated/walls_mineral.dm +++ b/code/game/turfs/simulated/walls_mineral.dm @@ -1,354 +1,354 @@ -/turf/simulated/wall/mineral - name = "mineral wall" - desc = "This shouldn't exist" - icon_state = "" - var/last_event = 0 - var/active = null - canSmoothWith = null - smooth = SMOOTH_TRUE - -/turf/simulated/wall/mineral/gold - name = "gold wall" - desc = "A wall with gold plating. Swag!" - icon = 'icons/turf/walls/gold_wall.dmi' - icon_state = "gold" - sheet_type = /obj/item/stack/sheet/mineral/gold - explosion_block = 0 //gold is a soft metal you dingus. - canSmoothWith = list(/turf/simulated/wall/mineral/gold, /obj/structure/falsewall/gold) - -/turf/simulated/wall/mineral/silver - name = "silver wall" - desc = "A wall with silver plating. Shiny!" - icon = 'icons/turf/walls/silver_wall.dmi' - icon_state = "silver" - sheet_type = /obj/item/stack/sheet/mineral/silver - canSmoothWith = list(/turf/simulated/wall/mineral/silver, /obj/structure/falsewall/silver) - -/turf/simulated/wall/mineral/diamond - name = "diamond wall" - desc = "A wall with diamond plating. You monster." - icon = 'icons/turf/walls/diamond_wall.dmi' - icon_state = "diamond" - sheet_type = /obj/item/stack/sheet/mineral/diamond - explosion_block = 3 - canSmoothWith = list(/turf/simulated/wall/mineral/diamond, /obj/structure/falsewall/diamond) - -/turf/simulated/wall/mineral/bananium - name = "bananium wall" - desc = "A wall with bananium plating. Honk!" - icon = 'icons/turf/walls/bananium_wall.dmi' - icon_state = "bananium" - sheet_type = /obj/item/stack/sheet/mineral/bananium - canSmoothWith = list(/turf/simulated/wall/mineral/bananium, /obj/structure/falsewall/bananium) - -/turf/simulated/wall/mineral/sandstone - name = "sandstone wall" - desc = "A wall with sandstone plating." - icon = 'icons/turf/walls/sandstone_wall.dmi' - icon_state = "sandstone" - sheet_type = /obj/item/stack/sheet/mineral/sandstone - explosion_block = 0 - canSmoothWith = list(/turf/simulated/wall/mineral/sandstone, /obj/structure/falsewall/sandstone) - -/turf/simulated/wall/mineral/uranium - name = "uranium wall" - desc = "A wall with uranium plating. This is probably a bad idea." - icon = 'icons/turf/walls/uranium_wall.dmi' - icon_state = "uranium" - sheet_type = /obj/item/stack/sheet/mineral/uranium - canSmoothWith = list(/turf/simulated/wall/mineral/uranium, /obj/structure/falsewall/uranium) - -/turf/simulated/wall/mineral/uranium/proc/radiate() - if(!active) - if(world.time > last_event+15) - active = 1 - for(var/mob/living/L in range(3,src)) - L.apply_effect(12,IRRADIATE,0) - for(var/turf/simulated/wall/mineral/uranium/T in range(3,src)) - T.radiate() - last_event = world.time - active = null - return - return - -/turf/simulated/wall/mineral/uranium/attack_hand(mob/user as mob) - radiate() - ..() - -/turf/simulated/wall/mineral/uranium/attackby(obj/item/W as obj, mob/user as mob, params) - radiate() - ..() - -/turf/simulated/wall/mineral/uranium/Bumped(AM as mob|obj) - radiate() - ..() - -/turf/simulated/wall/mineral/plasma - name = "plasma wall" - desc = "A wall with plasma plating. This is definately a bad idea." - icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" - sheet_type = /obj/item/stack/sheet/mineral/plasma - thermal_conductivity = 0.04 - canSmoothWith = list(/turf/simulated/wall/mineral/plasma, /obj/structure/falsewall/plasma) - -/turf/simulated/wall/mineral/plasma/attackby(obj/item/W as obj, mob/user as mob) - if(is_hot(W) > 300)//If the temperature of the object is over 300, then ignite - message_admins("Plasma wall ignited by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) - log_game("Plasma wall ignited by [key_name(user)] in ([x], [y], [z])") - investigate_log("was ignited by [key_name(user)]","atmos") - ignite(is_hot(W)) - return - ..() - -/turf/simulated/wall/mineral/plasma/welder_act(mob/user, obj/item/I) - if(I.tool_enabled) - ignite(2500) //The number's big enough - user.visible_message("[user] sets [src] on fire!",\ - "[src] disintegrates into a cloud of plasma!",\ - "You hear a 'whoompf' and a roar.") - message_admins("Plasma wall ignited by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) - log_game("Plasma wall ignited by [key_name(user)] in ([x], [y], [z])") - investigate_log("was ignited by [key_name(user)]","atmos") - -/turf/simulated/wall/mineral/plasma/proc/PlasmaBurn(temperature) - new girder_type(src) - ChangeTurf(/turf/simulated/floor) - atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 400) - -/turf/simulated/wall/mineral/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( - ..() - if(exposed_temperature > 300) - PlasmaBurn(exposed_temperature) - -/turf/simulated/wall/mineral/plasma/proc/ignite(exposed_temperature) - if(exposed_temperature > 300) - PlasmaBurn(exposed_temperature) - -/turf/simulated/wall/mineral/plasma/bullet_act(var/obj/item/projectile/Proj) - if(Proj.damage == 0)//lasertag guns and so on don't set off plasma anymore. can't use nodamage here because lasertag guns actually don't have it. - return - if(istype(Proj,/obj/item/projectile/beam)) - PlasmaBurn(2500) - else if(istype(Proj,/obj/item/projectile/ion)) - PlasmaBurn(500) - ..() - -/turf/simulated/wall/mineral/alien - name = "alien wall" - desc = "A strange-looking alien wall." - icon = 'icons/turf/walls/plasma_wall.dmi' - icon_state = "plasma" - sheet_type = /obj/item/stack/sheet/mineral/abductor - canSmoothWith = list(/turf/simulated/wall/mineral/alien, /obj/structure/falsewall/alien) - -/turf/simulated/wall/mineral/wood - name = "wooden wall" - desc = "A wall with wooden plating. Stiff." - icon = 'icons/turf/walls/wood_wall.dmi' - icon_state = "wood" - sheet_type = /obj/item/stack/sheet/wood - hardness = 70 - explosion_block = 0 - canSmoothWith = list(/turf/simulated/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/simulated/wall/mineral/wood/nonmetal) - -/turf/simulated/wall/mineral/wood/attackby(obj/item/W, mob/user) - if(W.sharp && W.force) - var/duration = (48 / W.force) * 2 //In seconds, for now. - if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/twohanded/fireaxe)) - duration /= 4 //Much better with hatchets and axes. - if(do_after(user, duration * 10, target = src)) //Into deciseconds. - dismantle_wall(FALSE, FALSE) - return - return ..() - -/turf/simulated/wall/mineral/wood/nonmetal - desc = "A solidly wooden wall. It's a bit weaker than a wall made with metal." - girder_type = /obj/structure/barricade/wooden - hardness = 50 - canSmoothWith = list(/turf/simulated/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/simulated/wall/mineral/wood/nonmetal) - -/turf/simulated/wall/mineral/iron - name = "rough metal wall" - desc = "A wall with rough metal plating." - icon = 'icons/turf/walls/iron_wall.dmi' - icon_state = "iron" - sheet_type = /obj/item/stack/rods - sheet_amount = 5 - canSmoothWith = list(/turf/simulated/wall/mineral/iron, /obj/structure/falsewall/iron) - -/turf/simulated/wall/mineral/abductor - name = "alien wall" - desc = "A wall with alien alloy plating." - icon = 'icons/turf/walls/abductor_wall.dmi' - icon_state = "abductor" - smooth = SMOOTH_TRUE|SMOOTH_DIAGONAL - sheet_type = /obj/item/stack/sheet/mineral/abductor - explosion_block = 3 - canSmoothWith = list(/turf/simulated/wall/mineral/abductor, /obj/structure/falsewall/abductor) - -/////////////////////Titanium walls///////////////////// - -/turf/simulated/wall/mineral/titanium //has to use this path due to how building walls works - name = "wall" - desc = "A light-weight titanium wall used in shuttles." - icon = 'icons/turf/walls/shuttle_wall.dmi' - icon_state = "map-shuttle" - explosion_block = 3 - flags_2 = CHECK_RICOCHET_2 - sheet_type = /obj/item/stack/sheet/mineral/titanium - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/simulated/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) - -/turf/simulated/wall/mineral/titanium/nodiagonal - smooth = SMOOTH_MORE - icon_state = "map-shuttle_nd" - -/turf/simulated/wall/mineral/titanium/nosmooth - icon = 'icons/turf/shuttle.dmi' - icon_state = "wall" - smooth = SMOOTH_FALSE - -/turf/simulated/wall/mineral/titanium/overspace - icon_state = "map-overspace" - fixed_underlay = list("space"=1) - -//sub-type to be used for interior shuttle walls -//won't get an underlay of the destination turf on shuttle move -/turf/simulated/wall/mineral/titanium/interior/copyTurf(turf/T) - if(T.type != type) - T.ChangeTurf(type) - if(underlays.len) - T.underlays = underlays - if(T.icon_state != icon_state) - T.icon_state = icon_state - if(T.icon != icon) - T.icon = icon - if(T.color != color) - T.color = color - if(T.dir != dir) - T.dir = dir - T.transform = transform - return T - -/turf/simulated/wall/mineral/titanium/copyTurf(turf/T) - . = ..() - T.transform = transform - -/turf/simulated/wall/mineral/titanium/survival - name = "pod wall" - desc = "An easily-compressable wall used for temporary shelter." - icon = 'icons/turf/walls/survival_pod_walls.dmi' - icon_state = "smooth" - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/simulated/wall/mineral/titanium/survival, /obj/machinery/door/airlock, /obj/structure/window/full, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine) - -/turf/simulated/wall/mineral/titanium/survival/nodiagonal - smooth = SMOOTH_MORE - -/turf/simulated/wall/mineral/titanium/survival/pod - canSmoothWith = list(/turf/simulated/wall/mineral/titanium/survival, /obj/machinery/door/airlock/survival_pod) - -//undeconstructable type for derelict -//these walls are undeconstructable/unthermitable -/turf/simulated/wall/mineral/titanium/nodecon - name = "russian wall" - desc = "Like regular titanium, but able to deflect capitalist aggressors." - -/turf/simulated/wall/mineral/titanium/nodecon/tileblend - fixed_underlay = list("icon"='icons/turf/floors.dmi', "icon_state"="darkredfull") - -/turf/simulated/wall/mineral/titanium/nodecon/nodiagonal - smooth = SMOOTH_MORE - icon_state = "map-shuttle_nd" - -/turf/simulated/wall/mineral/titanium/nodecon/nosmooth - smooth = SMOOTH_FALSE - icon = 'icons/turf/shuttle.dmi' - icon_state = "wall" - -//properties for derelict sub-type to prevent said deconstruction/thermiting -/turf/simulated/wall/mineral/titanium/nodecon/try_decon(obj/item/I, mob/user, params) - return - -/turf/simulated/wall/mineral/titanium/nodecon/thermitemelt(mob/user as mob, speed) - return - -/turf/simulated/wall/mineral/titanium/nodecon/burn_down() - return - -/////////////////////Plastitanium walls///////////////////// - -/turf/simulated/wall/mineral/plastitanium - name = "wall" - desc = "An evil wall of plasma and titanium." - icon = 'icons/turf/walls/plastitanium_wall.dmi' - icon_state = "map-shuttle" - explosion_block = 4 - sheet_type = /obj/item/stack/sheet/mineral/plastitanium - smooth = SMOOTH_MORE|SMOOTH_DIAGONAL - canSmoothWith = list(/turf/simulated/wall/mineral/plastitanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/shuttle/engine, /obj/structure/falsewall/plastitanium) - -/turf/simulated/wall/mineral/plastitanium/nodiagonal - smooth = SMOOTH_MORE - icon_state = "map-shuttle_nd" - -/turf/simulated/wall/mineral/plastitanium/nosmooth - icon = 'icons/turf/shuttle.dmi' - icon_state = "wall" - smooth = SMOOTH_FALSE - -/turf/simulated/wall/mineral/plastitanium/overspace - icon_state = "map-overspace" - fixed_underlay = list("space"=1) - -/turf/simulated/wall/mineral/plastitanium/coated - name = "coated wall" - max_temperature = INFINITY - icon_state = "map-shuttle_nd" - smooth = SMOOTH_MORE - -/turf/simulated/wall/mineral/plastitanium/coated/Initialize(mapload) - . = ..() - desc += " It seems to have additional plating to protect against heat." - -/turf/simulated/wall/mineral/plastitanium/explosive - var/explosive_wall_group = EXPLOSIVE_WALL_GROUP_SYNDICATE_BASE - icon_state = "map-shuttle_nd" - smooth = SMOOTH_MORE - -/turf/simulated/wall/mineral/plastitanium/explosive/Initialize(mapload) - . = ..() - GLOB.explosive_walls += src - -/turf/simulated/wall/mineral/plastitanium/explosive/Destroy() - GLOB.explosive_walls -= src - return ..() - -/turf/simulated/wall/mineral/plastitanium/explosive/proc/self_destruct() - var/obj/item/bombcore/large/explosive_wall/bombcore = new(get_turf(src)) - bombcore.detonate() - -/turf/simulated/wall/mineral/plastitanium/explosive/ex_act(severity) - return - -//have to copypaste this code -/turf/simulated/wall/mineral/plastitanium/interior/copyTurf(turf/T) - if(T.type != type) - T.ChangeTurf(type) - if(underlays.len) - T.underlays = underlays - if(T.icon_state != icon_state) - T.icon_state = icon_state - if(T.icon != icon) - T.icon = icon - if(T.color != color) - T.color = color - if(T.dir != dir) - T.dir = dir - T.transform = transform - return T - -/turf/simulated/wall/mineral/plastitanium/copyTurf(turf/T) - . = ..() - T.transform = transform \ No newline at end of file +/turf/simulated/wall/mineral + name = "mineral wall" + desc = "This shouldn't exist" + icon_state = "" + var/last_event = 0 + var/active = null + canSmoothWith = null + smooth = SMOOTH_TRUE + +/turf/simulated/wall/mineral/gold + name = "gold wall" + desc = "A wall with gold plating. Swag!" + icon = 'icons/turf/walls/gold_wall.dmi' + icon_state = "gold" + sheet_type = /obj/item/stack/sheet/mineral/gold + explosion_block = 0 //gold is a soft metal you dingus. + canSmoothWith = list(/turf/simulated/wall/mineral/gold, /obj/structure/falsewall/gold) + +/turf/simulated/wall/mineral/silver + name = "silver wall" + desc = "A wall with silver plating. Shiny!" + icon = 'icons/turf/walls/silver_wall.dmi' + icon_state = "silver" + sheet_type = /obj/item/stack/sheet/mineral/silver + canSmoothWith = list(/turf/simulated/wall/mineral/silver, /obj/structure/falsewall/silver) + +/turf/simulated/wall/mineral/diamond + name = "diamond wall" + desc = "A wall with diamond plating. You monster." + icon = 'icons/turf/walls/diamond_wall.dmi' + icon_state = "diamond" + sheet_type = /obj/item/stack/sheet/mineral/diamond + explosion_block = 3 + canSmoothWith = list(/turf/simulated/wall/mineral/diamond, /obj/structure/falsewall/diamond) + +/turf/simulated/wall/mineral/bananium + name = "bananium wall" + desc = "A wall with bananium plating. Honk!" + icon = 'icons/turf/walls/bananium_wall.dmi' + icon_state = "bananium" + sheet_type = /obj/item/stack/sheet/mineral/bananium + canSmoothWith = list(/turf/simulated/wall/mineral/bananium, /obj/structure/falsewall/bananium) + +/turf/simulated/wall/mineral/sandstone + name = "sandstone wall" + desc = "A wall with sandstone plating." + icon = 'icons/turf/walls/sandstone_wall.dmi' + icon_state = "sandstone" + sheet_type = /obj/item/stack/sheet/mineral/sandstone + explosion_block = 0 + canSmoothWith = list(/turf/simulated/wall/mineral/sandstone, /obj/structure/falsewall/sandstone) + +/turf/simulated/wall/mineral/uranium + name = "uranium wall" + desc = "A wall with uranium plating. This is probably a bad idea." + icon = 'icons/turf/walls/uranium_wall.dmi' + icon_state = "uranium" + sheet_type = /obj/item/stack/sheet/mineral/uranium + canSmoothWith = list(/turf/simulated/wall/mineral/uranium, /obj/structure/falsewall/uranium) + +/turf/simulated/wall/mineral/uranium/proc/radiate() + if(!active) + if(world.time > last_event+15) + active = 1 + for(var/mob/living/L in range(3,src)) + L.apply_effect(12,IRRADIATE,0) + for(var/turf/simulated/wall/mineral/uranium/T in range(3,src)) + T.radiate() + last_event = world.time + active = null + return + return + +/turf/simulated/wall/mineral/uranium/attack_hand(mob/user as mob) + radiate() + ..() + +/turf/simulated/wall/mineral/uranium/attackby(obj/item/W as obj, mob/user as mob, params) + radiate() + ..() + +/turf/simulated/wall/mineral/uranium/Bumped(AM as mob|obj) + radiate() + ..() + +/turf/simulated/wall/mineral/plasma + name = "plasma wall" + desc = "A wall with plasma plating. This is definately a bad idea." + icon = 'icons/turf/walls/plasma_wall.dmi' + icon_state = "plasma" + sheet_type = /obj/item/stack/sheet/mineral/plasma + thermal_conductivity = 0.04 + canSmoothWith = list(/turf/simulated/wall/mineral/plasma, /obj/structure/falsewall/plasma) + +/turf/simulated/wall/mineral/plasma/attackby(obj/item/W as obj, mob/user as mob) + if(is_hot(W) > 300)//If the temperature of the object is over 300, then ignite + message_admins("Plasma wall ignited by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) + log_game("Plasma wall ignited by [key_name(user)] in ([x], [y], [z])") + investigate_log("was ignited by [key_name(user)]","atmos") + ignite(is_hot(W)) + return + ..() + +/turf/simulated/wall/mineral/plasma/welder_act(mob/user, obj/item/I) + if(I.tool_enabled) + ignite(2500) //The number's big enough + user.visible_message("[user] sets [src] on fire!",\ + "[src] disintegrates into a cloud of plasma!",\ + "You hear a 'whoompf' and a roar.") + message_admins("Plasma wall ignited by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) + log_game("Plasma wall ignited by [key_name(user)] in ([x], [y], [z])") + investigate_log("was ignited by [key_name(user)]","atmos") + +/turf/simulated/wall/mineral/plasma/proc/PlasmaBurn(temperature) + new girder_type(src) + ChangeTurf(/turf/simulated/floor) + atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 400) + +/turf/simulated/wall/mineral/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)//Doesn't fucking work because walls don't interact with air :( + ..() + if(exposed_temperature > 300) + PlasmaBurn(exposed_temperature) + +/turf/simulated/wall/mineral/plasma/proc/ignite(exposed_temperature) + if(exposed_temperature > 300) + PlasmaBurn(exposed_temperature) + +/turf/simulated/wall/mineral/plasma/bullet_act(var/obj/item/projectile/Proj) + if(Proj.damage == 0)//lasertag guns and so on don't set off plasma anymore. can't use nodamage here because lasertag guns actually don't have it. + return + if(istype(Proj,/obj/item/projectile/beam)) + PlasmaBurn(2500) + else if(istype(Proj,/obj/item/projectile/ion)) + PlasmaBurn(500) + ..() + +/turf/simulated/wall/mineral/alien + name = "alien wall" + desc = "A strange-looking alien wall." + icon = 'icons/turf/walls/plasma_wall.dmi' + icon_state = "plasma" + sheet_type = /obj/item/stack/sheet/mineral/abductor + canSmoothWith = list(/turf/simulated/wall/mineral/alien, /obj/structure/falsewall/alien) + +/turf/simulated/wall/mineral/wood + name = "wooden wall" + desc = "A wall with wooden plating. Stiff." + icon = 'icons/turf/walls/wood_wall.dmi' + icon_state = "wood" + sheet_type = /obj/item/stack/sheet/wood + hardness = 70 + explosion_block = 0 + canSmoothWith = list(/turf/simulated/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/simulated/wall/mineral/wood/nonmetal) + +/turf/simulated/wall/mineral/wood/attackby(obj/item/W, mob/user) + if(W.sharp && W.force) + var/duration = (48 / W.force) * 2 //In seconds, for now. + if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/twohanded/fireaxe)) + duration /= 4 //Much better with hatchets and axes. + if(do_after(user, duration * 10, target = src)) //Into deciseconds. + dismantle_wall(FALSE, FALSE) + return + return ..() + +/turf/simulated/wall/mineral/wood/nonmetal + desc = "A solidly wooden wall. It's a bit weaker than a wall made with metal." + girder_type = /obj/structure/barricade/wooden + hardness = 50 + canSmoothWith = list(/turf/simulated/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/simulated/wall/mineral/wood/nonmetal) + +/turf/simulated/wall/mineral/iron + name = "rough metal wall" + desc = "A wall with rough metal plating." + icon = 'icons/turf/walls/iron_wall.dmi' + icon_state = "iron" + sheet_type = /obj/item/stack/rods + sheet_amount = 5 + canSmoothWith = list(/turf/simulated/wall/mineral/iron, /obj/structure/falsewall/iron) + +/turf/simulated/wall/mineral/abductor + name = "alien wall" + desc = "A wall with alien alloy plating." + icon = 'icons/turf/walls/abductor_wall.dmi' + icon_state = "abductor" + smooth = SMOOTH_TRUE|SMOOTH_DIAGONAL + sheet_type = /obj/item/stack/sheet/mineral/abductor + explosion_block = 3 + canSmoothWith = list(/turf/simulated/wall/mineral/abductor, /obj/structure/falsewall/abductor) + +/////////////////////Titanium walls///////////////////// + +/turf/simulated/wall/mineral/titanium //has to use this path due to how building walls works + name = "wall" + desc = "A light-weight titanium wall used in shuttles." + icon = 'icons/turf/walls/shuttle_wall.dmi' + icon_state = "map-shuttle" + explosion_block = 3 + flags_2 = CHECK_RICOCHET_2 + sheet_type = /obj/item/stack/sheet/mineral/titanium + smooth = SMOOTH_MORE|SMOOTH_DIAGONAL + canSmoothWith = list(/turf/simulated/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium) + +/turf/simulated/wall/mineral/titanium/nodiagonal + smooth = SMOOTH_MORE + icon_state = "map-shuttle_nd" + +/turf/simulated/wall/mineral/titanium/nosmooth + icon = 'icons/turf/shuttle.dmi' + icon_state = "wall" + smooth = SMOOTH_FALSE + +/turf/simulated/wall/mineral/titanium/overspace + icon_state = "map-overspace" + fixed_underlay = list("space"=1) + +//sub-type to be used for interior shuttle walls +//won't get an underlay of the destination turf on shuttle move +/turf/simulated/wall/mineral/titanium/interior/copyTurf(turf/T) + if(T.type != type) + T.ChangeTurf(type) + if(underlays.len) + T.underlays = underlays + if(T.icon_state != icon_state) + T.icon_state = icon_state + if(T.icon != icon) + T.icon = icon + if(T.color != color) + T.color = color + if(T.dir != dir) + T.dir = dir + T.transform = transform + return T + +/turf/simulated/wall/mineral/titanium/copyTurf(turf/T) + . = ..() + T.transform = transform + +/turf/simulated/wall/mineral/titanium/survival + name = "pod wall" + desc = "An easily-compressable wall used for temporary shelter." + icon = 'icons/turf/walls/survival_pod_walls.dmi' + icon_state = "smooth" + smooth = SMOOTH_MORE|SMOOTH_DIAGONAL + canSmoothWith = list(/turf/simulated/wall/mineral/titanium/survival, /obj/machinery/door/airlock, /obj/structure/window/full, /obj/structure/window/full/reinforced, /obj/structure/window/full/reinforced/tinted, /obj/structure/window/full/shuttle, /obj/structure/shuttle/engine) + +/turf/simulated/wall/mineral/titanium/survival/nodiagonal + smooth = SMOOTH_MORE + +/turf/simulated/wall/mineral/titanium/survival/pod + canSmoothWith = list(/turf/simulated/wall/mineral/titanium/survival, /obj/machinery/door/airlock/survival_pod) + +//undeconstructable type for derelict +//these walls are undeconstructable/unthermitable +/turf/simulated/wall/mineral/titanium/nodecon + name = "russian wall" + desc = "Like regular titanium, but able to deflect capitalist aggressors." + +/turf/simulated/wall/mineral/titanium/nodecon/tileblend + fixed_underlay = list("icon"='icons/turf/floors.dmi', "icon_state"="darkredfull") + +/turf/simulated/wall/mineral/titanium/nodecon/nodiagonal + smooth = SMOOTH_MORE + icon_state = "map-shuttle_nd" + +/turf/simulated/wall/mineral/titanium/nodecon/nosmooth + smooth = SMOOTH_FALSE + icon = 'icons/turf/shuttle.dmi' + icon_state = "wall" + +//properties for derelict sub-type to prevent said deconstruction/thermiting +/turf/simulated/wall/mineral/titanium/nodecon/try_decon(obj/item/I, mob/user, params) + return + +/turf/simulated/wall/mineral/titanium/nodecon/thermitemelt(mob/user as mob, speed) + return + +/turf/simulated/wall/mineral/titanium/nodecon/burn_down() + return + +/////////////////////Plastitanium walls///////////////////// + +/turf/simulated/wall/mineral/plastitanium + name = "wall" + desc = "An evil wall of plasma and titanium." + icon = 'icons/turf/walls/plastitanium_wall.dmi' + icon_state = "map-shuttle" + explosion_block = 4 + sheet_type = /obj/item/stack/sheet/mineral/plastitanium + smooth = SMOOTH_MORE|SMOOTH_DIAGONAL + canSmoothWith = list(/turf/simulated/wall/mineral/plastitanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/shuttle/engine, /obj/structure/falsewall/plastitanium) + +/turf/simulated/wall/mineral/plastitanium/nodiagonal + smooth = SMOOTH_MORE + icon_state = "map-shuttle_nd" + +/turf/simulated/wall/mineral/plastitanium/nosmooth + icon = 'icons/turf/shuttle.dmi' + icon_state = "wall" + smooth = SMOOTH_FALSE + +/turf/simulated/wall/mineral/plastitanium/overspace + icon_state = "map-overspace" + fixed_underlay = list("space"=1) + +/turf/simulated/wall/mineral/plastitanium/coated + name = "coated wall" + max_temperature = INFINITY + icon_state = "map-shuttle_nd" + smooth = SMOOTH_MORE + +/turf/simulated/wall/mineral/plastitanium/coated/Initialize(mapload) + . = ..() + desc += " It seems to have additional plating to protect against heat." + +/turf/simulated/wall/mineral/plastitanium/explosive + var/explosive_wall_group = EXPLOSIVE_WALL_GROUP_SYNDICATE_BASE + icon_state = "map-shuttle_nd" + smooth = SMOOTH_MORE + +/turf/simulated/wall/mineral/plastitanium/explosive/Initialize(mapload) + . = ..() + GLOB.explosive_walls += src + +/turf/simulated/wall/mineral/plastitanium/explosive/Destroy() + GLOB.explosive_walls -= src + return ..() + +/turf/simulated/wall/mineral/plastitanium/explosive/proc/self_destruct() + var/obj/item/bombcore/large/explosive_wall/bombcore = new(get_turf(src)) + bombcore.detonate() + +/turf/simulated/wall/mineral/plastitanium/explosive/ex_act(severity) + return + +//have to copypaste this code +/turf/simulated/wall/mineral/plastitanium/interior/copyTurf(turf/T) + if(T.type != type) + T.ChangeTurf(type) + if(underlays.len) + T.underlays = underlays + if(T.icon_state != icon_state) + T.icon_state = icon_state + if(T.icon != icon) + T.icon = icon + if(T.color != color) + T.color = color + if(T.dir != dir) + T.dir = dir + T.transform = transform + return T + +/turf/simulated/wall/mineral/plastitanium/copyTurf(turf/T) + . = ..() + T.transform = transform diff --git a/code/game/turfs/simulated/walls_reinforced.dm b/code/game/turfs/simulated/walls_reinforced.dm index 4c211291ae0b..502803bc7d3e 100644 --- a/code/game/turfs/simulated/walls_reinforced.dm +++ b/code/game/turfs/simulated/walls_reinforced.dm @@ -1,238 +1,238 @@ -/turf/simulated/wall/r_wall - name = "reinforced wall" - desc = "A huge chunk of reinforced metal used to separate rooms." - icon = 'icons/turf/walls/reinforced_wall.dmi' - icon_state = "r_wall" - opacity = 1 - density = 1 - explosion_block = 2 - damage_cap = 600 - max_temperature = 6000 - hardness = 10 - sheet_type = /obj/item/stack/sheet/plasteel - sheet_amount = 1 - girder_type = /obj/structure/girder/reinforced - can_dismantle_with_welder = FALSE - - var/d_state = RWALL_INTACT - var/can_be_reinforced = 1 - -/turf/simulated/wall/r_wall/examine(mob/user) - . = ..() - switch(d_state) - if(RWALL_INTACT) - . += "The outer grille is fully intact." - if(RWALL_SUPPORT_LINES) - . += "The outer grille has been cut, and the support lines are screwed securely to the outer cover." - if(RWALL_COVER) - . += "The support lines have been unscrewed, and the metal cover is welded firmly in place." - if(RWALL_CUT_COVER) - . += "The metal cover has been sliced through, and is connected loosely to the girder." - if(RWALL_BOLTS) - . += "The outer cover has been pried away, and the bolts anchoring the support rods are wrenched in place." - if(RWALL_SUPPORT_RODS) - . += "The bolts anchoring the support rods have been loosened, but are still welded firmly to the girder." - if(RWALL_SHEATH) - . += "The support rods have been sliced through, and the outer sheath is connected loosely to the girder." - -/turf/simulated/wall/r_wall/attackby(obj/item/I, mob/user, params) - if(d_state == RWALL_COVER && istype(I, /obj/item/gun/energy/plasmacutter)) - to_chat(user, "You begin slicing through the metal cover...") - if(I.use_tool(src, user, 40, volume = I.tool_volume) && d_state == RWALL_COVER) - d_state = RWALL_CUT_COVER - update_icon() - to_chat(user, "You press firmly on the cover, dislodging it.") - return - else if(RWALL_SUPPORT_RODS && istype(I, /obj/item/gun/energy/plasmacutter)) - to_chat(user, "You begin slicing through the support rods...") - if(I.use_tool(src, user, 70, volume = I.tool_volume) && d_state == RWALL_SUPPORT_RODS) - d_state = RWALL_SHEATH - update_icon() - return - else if(d_state == RWALL_SUPPORT_LINES && istype(I, /obj/item/stack/rods)) - var/obj/item/stack/S = I - if(S.use(1)) - d_state = RWALL_INTACT - update_icon() - to_chat(user, "You replace the outer grille.") - else - to_chat(user, "You don't have enough rods for that!") - return - else if(d_state) - // Repairing - if(istype(I, /obj/item/stack/sheet/metal)) - var/obj/item/stack/sheet/metal/MS = I - to_chat(user, "You begin patching-up the wall with [MS]...") - if(do_after(user, max(20 * d_state, 100) * MS.toolspeed, target = src) && d_state) - if(!MS.use(1)) - to_chat(user, "You don't have enough [MS.name] for that!") - return - d_state = RWALL_INTACT - update_icon() - queue_smooth_neighbors(src) - to_chat(user, "You repair the last of the damage.") - return - - else if(istype(I, /obj/item/stack/sheet/plasteel)) - var/obj/item/stack/sheet/plasteel/PS = I - if(!can_be_reinforced) - to_chat(user, "The wall is already coated!") - return - to_chat(user, "You begin adding an additional layer of coating to the wall with [PS]...") - if(do_after(user, 40 * PS.toolspeed, target = src) && !d_state) - if(!PS.use(2)) - to_chat(user, "You don't have enough [PS.name] for that!") - return - to_chat(user, "You add an additional layer of coating to the wall.") - ChangeTurf(/turf/simulated/wall/r_wall/coated) - update_icon() - queue_smooth_neighbors(src) - can_be_reinforced = FALSE - return - else - return ..() - -/turf/simulated/wall/r_wall/welder_act(mob/user, obj/item/I) - if(thermite && I.use_tool(src, user, volume = I.tool_volume)) - thermitemelt(user) - return TRUE - if(!(d_state in list(RWALL_COVER, RWALL_SUPPORT_RODS, RWALL_CUT_COVER))) - return ..() - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(d_state == RWALL_COVER) - to_chat(user, "You begin slicing through the metal cover...") - if(I.use_tool(src, user, 60, volume = I.tool_volume) && d_state == RWALL_COVER) - d_state = RWALL_CUT_COVER - to_chat(user, "You press firmly on the cover, dislodging it.") - else if(d_state == RWALL_SUPPORT_RODS) - to_chat(user, "You begin slicing through the support rods...") - if(I.use_tool(src, user, 100, volume = I.tool_volume) && d_state == RWALL_SUPPORT_RODS) - d_state = RWALL_SHEATH - else if(d_state == RWALL_CUT_COVER) - to_chat(user, "You begin welding the metal cover back to the frame...") - if(I.use_tool(src, user, 60, volume = I.tool_volume) && d_state == RWALL_CUT_COVER) - to_chat(user, "The metal cover has been welded securely to the frame.") - d_state = RWALL_COVER - update_icon() - -/turf/simulated/wall/r_wall/crowbar_act(mob/user, obj/item/I) - if(!(d_state in list(RWALL_CUT_COVER, RWALL_SHEATH, RWALL_BOLTS))) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - switch(d_state) - if(RWALL_CUT_COVER) - to_chat(user, "You struggle to pry off the cover...") - if(!I.use_tool(src, user, 100, volume = I.tool_volume) || d_state != RWALL_CUT_COVER) - return - d_state = RWALL_BOLTS - to_chat(user, "You pry off the cover.") - if(RWALL_SHEATH) - to_chat(user, "You struggle to pry off the outer sheath...") - if(!I.use_tool(src, user, 100, volume = I.tool_volume) || d_state != RWALL_SHEATH) - return - to_chat(user, "You pry off the outer sheath.") - dismantle_wall() - return - if(RWALL_BOLTS) - to_chat(user, "You start to pry the cover back into place...") - playsound(src, I.usesound, 100, 1) - if(!I.use_tool(src, user, 20, volume = I.tool_volume) || d_state != RWALL_BOLTS) - return - d_state = RWALL_CUT_COVER - to_chat(user, "The metal cover has been pried back into place.") - update_icon() - -/turf/simulated/wall/r_wall/screwdriver_act(mob/user, obj/item/I) - if(d_state != RWALL_SUPPORT_LINES && d_state != RWALL_COVER) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - var/state_check = d_state - if(d_state == RWALL_SUPPORT_LINES) - to_chat(user, "You begin unsecuring the support lines...") - else - to_chat(user, "You begin securing the support lines...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state_check != d_state) - return - if(d_state == RWALL_SUPPORT_LINES) - d_state = RWALL_COVER - to_chat(user, "You unsecure the support lines.") - else - d_state = RWALL_SUPPORT_LINES - to_chat(user, "The support lines have been secured.") - update_icon() - -/turf/simulated/wall/r_wall/wirecutter_act(mob/user, obj/item/I) - if(d_state != RWALL_INTACT) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - d_state = RWALL_SUPPORT_LINES - update_icon() - new /obj/item/stack/rods(src) - to_chat(user, "You cut the outer grille.") - -/turf/simulated/wall/r_wall/wrench_act(mob/user, obj/item/I) - if(d_state != RWALL_BOLTS && d_state != RWALL_SUPPORT_RODS) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - var/state_check = d_state - if(d_state == RWALL_BOLTS) - to_chat(user, "You start loosening the anchoring bolts which secure the support rods to their frame...") - else - to_chat(user, "You start tightening the bolts which secure the support rods to their frame...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state_check != d_state) - return - if(d_state == RWALL_BOLTS) - d_state = RWALL_SUPPORT_RODS - to_chat(user, "You remove the bolts anchoring the support rods.") - else - d_state = RWALL_BOLTS - to_chat(user, "You tighten the bolts anchoring the support rods.") - update_icon() - -/turf/simulated/wall/r_wall/try_destroy(obj/item/I, mob/user, params) - if(istype(I, /obj/item/pickaxe/drill/diamonddrill)) - to_chat(user, "You begin to drill though the wall...") - - if(do_after(user, 800 * I.toolspeed, target = src)) // Diamond drill has 0.25 toolspeed, so 200 - to_chat(user, "Your drill tears through the last of the reinforced plating.") - dismantle_wall() - return TRUE - - if(istype(I, /obj/item/pickaxe/drill/jackhammer)) - to_chat(user, "You begin to disintegrate the wall...") - - if(do_after(user, 1000 * I.toolspeed, target = src)) // Jackhammer has 0.1 toolspeed, so 100 - to_chat(user, "Your sonic jackhammer disintegrates the reinforced plating.") - dismantle_wall() - return TRUE - - -/turf/simulated/wall/r_wall/wall_singularity_pull(current_size) - if(current_size >= STAGE_FIVE) - if(prob(30)) - dismantle_wall() - -/turf/simulated/wall/r_wall/update_icon() - . = ..() - - if(d_state) - icon_state = "r_wall-[d_state]" - smooth = SMOOTH_FALSE - clear_smooth_overlays() - else - smooth = SMOOTH_TRUE - icon_state = "" - -/turf/simulated/wall/r_wall/devastate_wall() - new sheet_type(src, sheet_amount) - new /obj/item/stack/sheet/metal(src, 2) +/turf/simulated/wall/r_wall + name = "reinforced wall" + desc = "A huge chunk of reinforced metal used to separate rooms." + icon = 'icons/turf/walls/reinforced_wall.dmi' + icon_state = "r_wall" + opacity = 1 + density = 1 + explosion_block = 2 + damage_cap = 600 + max_temperature = 6000 + hardness = 10 + sheet_type = /obj/item/stack/sheet/plasteel + sheet_amount = 1 + girder_type = /obj/structure/girder/reinforced + can_dismantle_with_welder = FALSE + + var/d_state = RWALL_INTACT + var/can_be_reinforced = 1 + +/turf/simulated/wall/r_wall/examine(mob/user) + . = ..() + switch(d_state) + if(RWALL_INTACT) + . += "The outer grille is fully intact." + if(RWALL_SUPPORT_LINES) + . += "The outer grille has been cut, and the support lines are screwed securely to the outer cover." + if(RWALL_COVER) + . += "The support lines have been unscrewed, and the metal cover is welded firmly in place." + if(RWALL_CUT_COVER) + . += "The metal cover has been sliced through, and is connected loosely to the girder." + if(RWALL_BOLTS) + . += "The outer cover has been pried away, and the bolts anchoring the support rods are wrenched in place." + if(RWALL_SUPPORT_RODS) + . += "The bolts anchoring the support rods have been loosened, but are still welded firmly to the girder." + if(RWALL_SHEATH) + . += "The support rods have been sliced through, and the outer sheath is connected loosely to the girder." + +/turf/simulated/wall/r_wall/attackby(obj/item/I, mob/user, params) + if(d_state == RWALL_COVER && istype(I, /obj/item/gun/energy/plasmacutter)) + to_chat(user, "You begin slicing through the metal cover...") + if(I.use_tool(src, user, 40, volume = I.tool_volume) && d_state == RWALL_COVER) + d_state = RWALL_CUT_COVER + update_icon() + to_chat(user, "You press firmly on the cover, dislodging it.") + return + else if(RWALL_SUPPORT_RODS && istype(I, /obj/item/gun/energy/plasmacutter)) + to_chat(user, "You begin slicing through the support rods...") + if(I.use_tool(src, user, 70, volume = I.tool_volume) && d_state == RWALL_SUPPORT_RODS) + d_state = RWALL_SHEATH + update_icon() + return + else if(d_state == RWALL_SUPPORT_LINES && istype(I, /obj/item/stack/rods)) + var/obj/item/stack/S = I + if(S.use(1)) + d_state = RWALL_INTACT + update_icon() + to_chat(user, "You replace the outer grille.") + else + to_chat(user, "You don't have enough rods for that!") + return + else if(d_state) + // Repairing + if(istype(I, /obj/item/stack/sheet/metal)) + var/obj/item/stack/sheet/metal/MS = I + to_chat(user, "You begin patching-up the wall with [MS]...") + if(do_after(user, max(20 * d_state, 100) * MS.toolspeed, target = src) && d_state) + if(!MS.use(1)) + to_chat(user, "You don't have enough [MS.name] for that!") + return + d_state = RWALL_INTACT + update_icon() + queue_smooth_neighbors(src) + to_chat(user, "You repair the last of the damage.") + return + + else if(istype(I, /obj/item/stack/sheet/plasteel)) + var/obj/item/stack/sheet/plasteel/PS = I + if(!can_be_reinforced) + to_chat(user, "The wall is already coated!") + return + to_chat(user, "You begin adding an additional layer of coating to the wall with [PS]...") + if(do_after(user, 40 * PS.toolspeed, target = src) && !d_state) + if(!PS.use(2)) + to_chat(user, "You don't have enough [PS.name] for that!") + return + to_chat(user, "You add an additional layer of coating to the wall.") + ChangeTurf(/turf/simulated/wall/r_wall/coated) + update_icon() + queue_smooth_neighbors(src) + can_be_reinforced = FALSE + return + else + return ..() + +/turf/simulated/wall/r_wall/welder_act(mob/user, obj/item/I) + if(thermite && I.use_tool(src, user, volume = I.tool_volume)) + thermitemelt(user) + return TRUE + if(!(d_state in list(RWALL_COVER, RWALL_SUPPORT_RODS, RWALL_CUT_COVER))) + return ..() + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(d_state == RWALL_COVER) + to_chat(user, "You begin slicing through the metal cover...") + if(I.use_tool(src, user, 60, volume = I.tool_volume) && d_state == RWALL_COVER) + d_state = RWALL_CUT_COVER + to_chat(user, "You press firmly on the cover, dislodging it.") + else if(d_state == RWALL_SUPPORT_RODS) + to_chat(user, "You begin slicing through the support rods...") + if(I.use_tool(src, user, 100, volume = I.tool_volume) && d_state == RWALL_SUPPORT_RODS) + d_state = RWALL_SHEATH + else if(d_state == RWALL_CUT_COVER) + to_chat(user, "You begin welding the metal cover back to the frame...") + if(I.use_tool(src, user, 60, volume = I.tool_volume) && d_state == RWALL_CUT_COVER) + to_chat(user, "The metal cover has been welded securely to the frame.") + d_state = RWALL_COVER + update_icon() + +/turf/simulated/wall/r_wall/crowbar_act(mob/user, obj/item/I) + if(!(d_state in list(RWALL_CUT_COVER, RWALL_SHEATH, RWALL_BOLTS))) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + switch(d_state) + if(RWALL_CUT_COVER) + to_chat(user, "You struggle to pry off the cover...") + if(!I.use_tool(src, user, 100, volume = I.tool_volume) || d_state != RWALL_CUT_COVER) + return + d_state = RWALL_BOLTS + to_chat(user, "You pry off the cover.") + if(RWALL_SHEATH) + to_chat(user, "You struggle to pry off the outer sheath...") + if(!I.use_tool(src, user, 100, volume = I.tool_volume) || d_state != RWALL_SHEATH) + return + to_chat(user, "You pry off the outer sheath.") + dismantle_wall() + return + if(RWALL_BOLTS) + to_chat(user, "You start to pry the cover back into place...") + playsound(src, I.usesound, 100, 1) + if(!I.use_tool(src, user, 20, volume = I.tool_volume) || d_state != RWALL_BOLTS) + return + d_state = RWALL_CUT_COVER + to_chat(user, "The metal cover has been pried back into place.") + update_icon() + +/turf/simulated/wall/r_wall/screwdriver_act(mob/user, obj/item/I) + if(d_state != RWALL_SUPPORT_LINES && d_state != RWALL_COVER) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + var/state_check = d_state + if(d_state == RWALL_SUPPORT_LINES) + to_chat(user, "You begin unsecuring the support lines...") + else + to_chat(user, "You begin securing the support lines...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state_check != d_state) + return + if(d_state == RWALL_SUPPORT_LINES) + d_state = RWALL_COVER + to_chat(user, "You unsecure the support lines.") + else + d_state = RWALL_SUPPORT_LINES + to_chat(user, "The support lines have been secured.") + update_icon() + +/turf/simulated/wall/r_wall/wirecutter_act(mob/user, obj/item/I) + if(d_state != RWALL_INTACT) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + d_state = RWALL_SUPPORT_LINES + update_icon() + new /obj/item/stack/rods(src) + to_chat(user, "You cut the outer grille.") + +/turf/simulated/wall/r_wall/wrench_act(mob/user, obj/item/I) + if(d_state != RWALL_BOLTS && d_state != RWALL_SUPPORT_RODS) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + var/state_check = d_state + if(d_state == RWALL_BOLTS) + to_chat(user, "You start loosening the anchoring bolts which secure the support rods to their frame...") + else + to_chat(user, "You start tightening the bolts which secure the support rods to their frame...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || state_check != d_state) + return + if(d_state == RWALL_BOLTS) + d_state = RWALL_SUPPORT_RODS + to_chat(user, "You remove the bolts anchoring the support rods.") + else + d_state = RWALL_BOLTS + to_chat(user, "You tighten the bolts anchoring the support rods.") + update_icon() + +/turf/simulated/wall/r_wall/try_destroy(obj/item/I, mob/user, params) + if(istype(I, /obj/item/pickaxe/drill/diamonddrill)) + to_chat(user, "You begin to drill though the wall...") + + if(do_after(user, 800 * I.toolspeed, target = src)) // Diamond drill has 0.25 toolspeed, so 200 + to_chat(user, "Your drill tears through the last of the reinforced plating.") + dismantle_wall() + return TRUE + + if(istype(I, /obj/item/pickaxe/drill/jackhammer)) + to_chat(user, "You begin to disintegrate the wall...") + + if(do_after(user, 1000 * I.toolspeed, target = src)) // Jackhammer has 0.1 toolspeed, so 100 + to_chat(user, "Your sonic jackhammer disintegrates the reinforced plating.") + dismantle_wall() + return TRUE + + +/turf/simulated/wall/r_wall/wall_singularity_pull(current_size) + if(current_size >= STAGE_FIVE) + if(prob(30)) + dismantle_wall() + +/turf/simulated/wall/r_wall/update_icon() + . = ..() + + if(d_state) + icon_state = "r_wall-[d_state]" + smooth = SMOOTH_FALSE + clear_smooth_overlays() + else + smooth = SMOOTH_TRUE + icon_state = "" + +/turf/simulated/wall/r_wall/devastate_wall() + new sheet_type(src, sheet_amount) + new /obj/item/stack/sheet/metal(src, 2) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index 1c0bfb167d6c..f78f520cfd38 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -1,275 +1,275 @@ -/turf/space - icon = 'icons/turf/space.dmi' - name = "\proper space" - icon_state = "0" - - temperature = TCMB - thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT - heat_capacity = HEAT_CAPACITY_VACUUM - - plane = PLANE_SPACE - layer = SPACE_LAYER - light_power = 0.25 - dynamic_lighting = DYNAMIC_LIGHTING_DISABLED - intact = FALSE - - var/destination_z - var/destination_x - var/destination_y - plane = PLANE_SPACE - -/turf/space/Initialize(mapload) - if(!istype(src, /turf/space/transit)) - icon_state = SPACE_ICON_STATE - - var/area/A = loc - if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) - add_overlay(/obj/effect/fullbright) - - if (light_power && light_range) - update_light() - - if (opacity) - has_opaque_atom = TRUE - - return INITIALIZE_HINT_NORMAL - -/turf/space/Destroy(force) - if(force) - . = ..() - else - return QDEL_HINT_LETMELIVE - -/turf/space/BeforeChange() - ..() - var/datum/space_level/S = space_manager.get_zlev(z) - S.remove_from_transit(src) - if(light_sources) // Turn off starlight, if present - set_light(0) - -/turf/space/AfterChange(ignore_air, keep_cabling = FALSE) - ..() - var/datum/space_level/S = space_manager.get_zlev(z) - S.add_to_transit(src) - S.apply_transition(src) - -/turf/space/proc/update_starlight() - if(config.starlight) - for(var/t in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm - if(isspaceturf(t)) - //let's NOT update this that much pls - continue - set_light(2) - return - set_light(0) - -/turf/space/attackby(obj/item/C as obj, mob/user as mob, params) - ..() - if(istype(C, /obj/item/stack/rods)) - var/obj/item/stack/rods/R = C - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - var/obj/structure/lattice/catwalk/W = locate(/obj/structure/lattice/catwalk, src) - if(W) - to_chat(user, "There is already a catwalk here!") - return - if(L) - if(R.use(1)) - to_chat(user, "You construct a catwalk.") - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - new/obj/structure/lattice/catwalk(src) - else - to_chat(user, "You need two rods to build a catwalk!") - return - if(R.use(1)) - to_chat(user, "Constructing support lattice...") - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - ReplaceWithLattice() - else - to_chat(user, "You need one rod to build a lattice.") - return - - if(istype(C, /obj/item/stack/tile/plasteel)) - var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) - if(L) - var/obj/item/stack/tile/plasteel/S = C - if(S.use(1)) - qdel(L) - playsound(src, 'sound/weapons/genhit.ogg', 50, 1) - to_chat(user, "You build a floor.") - ChangeTurf(/turf/simulated/floor/plating) - else - to_chat(user, "You need one floor tile to build a floor!") - else - to_chat(user, "The plating is going to need some support! Place metal rods first.") - -/turf/space/Entered(atom/movable/A as mob|obj, atom/OL, ignoreRest = 0) - ..() - if((!(A) || !(src in A.locs))) - return - - if(destination_z && destination_x && destination_y) - A.forceMove(locate(destination_x, destination_y, destination_z)) - - if(isliving(A)) - var/mob/living/L = A - if(L.pulling) - var/turf/T = get_step(L.loc,turn(A.dir, 180)) - L.pulling.forceMove(T) - - //now we're on the new z_level, proceed the space drifting - sleep(0)//Let a diagonal move finish, if necessary - A.newtonian_move(A.inertia_dir) - -/turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) - var/cur_x - var/cur_y - var/next_x - var/next_y - var/target_z - var/list/y_arr - - if(src.x <= 1) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) - qdel(A) - return - - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - next_x = (--cur_x||global_map.len) - y_arr = global_map[next_x] - target_z = y_arr[cur_y] -/* - //debug - to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_chat(world, "Target Z = [target_z]") - to_chat(world, "Next X = [next_x]") - //debug -*/ - if(target_z) - A.z = target_z - A.x = world.maxx - 2 - spawn (0) - if((A && A.loc)) - A.loc.Entered(A) - else if(src.x >= world.maxx) - if(istype(A, /obj/effect/meteor)) - qdel(A) - return - - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - next_x = (++cur_x > global_map.len ? 1 : cur_x) - y_arr = global_map[next_x] - target_z = y_arr[cur_y] -/* - //debug - to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_chat(world, "Target Z = [target_z]") - to_chat(world, "Next X = [next_x]") - //debug -*/ - if(target_z) - A.z = target_z - A.x = 3 - spawn (0) - if((A && A.loc)) - A.loc.Entered(A) - else if(src.y <= 1) - if(istype(A, /obj/effect/meteor)) - qdel(A) - return - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - y_arr = global_map[cur_x] - next_y = (--cur_y||y_arr.len) - target_z = y_arr[next_y] -/* - //debug - to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_chat(world, "Next Y = [next_y]") - to_chat(world, "Target Z = [target_z]") - //debug -*/ - if(target_z) - A.z = target_z - A.y = world.maxy - 2 - spawn (0) - if((A && A.loc)) - A.loc.Entered(A) - - else if(src.y >= world.maxy) - if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) - qdel(A) - return - var/list/cur_pos = src.get_global_map_pos() - if(!cur_pos) return - cur_x = cur_pos["x"] - cur_y = cur_pos["y"] - y_arr = global_map[cur_x] - next_y = (++cur_y > y_arr.len ? 1 : cur_y) - target_z = y_arr[next_y] -/* - //debug - to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") - to_chat(world, "Next Y = [next_y]") - to_chat(world, "Target Z = [target_z]") - //debug -*/ - if(target_z) - A.z = target_z - A.y = 3 - spawn (0) - if((A && A.loc)) - A.loc.Entered(A) - return - -/turf/space/singularity_act() - return - -/turf/space/can_have_cabling() - if(locate(/obj/structure/lattice/catwalk, src)) - return 1 - return 0 - -/turf/space/proc/set_transition_north(dest_z) - destination_x = x - destination_y = TRANSITION_BORDER_SOUTH + 1 - destination_z = dest_z - -/turf/space/proc/set_transition_south(dest_z) - destination_x = x - destination_y = TRANSITION_BORDER_NORTH - 1 - destination_z = dest_z - -/turf/space/proc/set_transition_east(dest_z) - destination_x = TRANSITION_BORDER_WEST + 1 - destination_y = y - destination_z = dest_z - -/turf/space/proc/set_transition_west(dest_z) - destination_x = TRANSITION_BORDER_EAST - 1 - destination_y = y - destination_z = dest_z - -/turf/space/proc/remove_transitions() - destination_z = initial(destination_z) - -/turf/space/attack_ghost(mob/dead/observer/user) - if(destination_z) - var/turf/T = locate(destination_x, destination_y, destination_z) - user.forceMove(T) - -/turf/space/acid_act(acidpwr, acid_volume) - return 0 - -/turf/space/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) - underlay_appearance.icon = 'icons/turf/space.dmi' - underlay_appearance.icon_state = SPACE_ICON_STATE - underlay_appearance.plane = PLANE_SPACE - return TRUE +/turf/space + icon = 'icons/turf/space.dmi' + name = "\proper space" + icon_state = "0" + + temperature = TCMB + thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT + heat_capacity = HEAT_CAPACITY_VACUUM + + plane = PLANE_SPACE + layer = SPACE_LAYER + light_power = 0.25 + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + intact = FALSE + + var/destination_z + var/destination_x + var/destination_y + plane = PLANE_SPACE + +/turf/space/Initialize(mapload) + if(!istype(src, /turf/space/transit)) + icon_state = SPACE_ICON_STATE + + var/area/A = loc + if(!IS_DYNAMIC_LIGHTING(src) && IS_DYNAMIC_LIGHTING(A)) + add_overlay(/obj/effect/fullbright) + + if (light_power && light_range) + update_light() + + if (opacity) + has_opaque_atom = TRUE + + return INITIALIZE_HINT_NORMAL + +/turf/space/Destroy(force) + if(force) + . = ..() + else + return QDEL_HINT_LETMELIVE + +/turf/space/BeforeChange() + ..() + var/datum/space_level/S = GLOB.space_manager.get_zlev(z) + S.remove_from_transit(src) + if(light_sources) // Turn off starlight, if present + set_light(0) + +/turf/space/AfterChange(ignore_air, keep_cabling = FALSE) + ..() + var/datum/space_level/S = GLOB.space_manager.get_zlev(z) + S.add_to_transit(src) + S.apply_transition(src) + +/turf/space/proc/update_starlight() + if(config.starlight) + for(var/t in RANGE_TURFS(1,src)) //RANGE_TURFS is in code\__HELPERS\game.dm + if(isspaceturf(t)) + //let's NOT update this that much pls + continue + set_light(2) + return + set_light(0) + +/turf/space/attackby(obj/item/C as obj, mob/user as mob, params) + ..() + if(istype(C, /obj/item/stack/rods)) + var/obj/item/stack/rods/R = C + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + var/obj/structure/lattice/catwalk/W = locate(/obj/structure/lattice/catwalk, src) + if(W) + to_chat(user, "There is already a catwalk here!") + return + if(L) + if(R.use(1)) + to_chat(user, "You construct a catwalk.") + playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + new/obj/structure/lattice/catwalk(src) + else + to_chat(user, "You need two rods to build a catwalk!") + return + if(R.use(1)) + to_chat(user, "Constructing support lattice...") + playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + ReplaceWithLattice() + else + to_chat(user, "You need one rod to build a lattice.") + return + + if(istype(C, /obj/item/stack/tile/plasteel)) + var/obj/structure/lattice/L = locate(/obj/structure/lattice, src) + if(L) + var/obj/item/stack/tile/plasteel/S = C + if(S.use(1)) + qdel(L) + playsound(src, 'sound/weapons/genhit.ogg', 50, 1) + to_chat(user, "You build a floor.") + ChangeTurf(/turf/simulated/floor/plating) + else + to_chat(user, "You need one floor tile to build a floor!") + else + to_chat(user, "The plating is going to need some support! Place metal rods first.") + +/turf/space/Entered(atom/movable/A as mob|obj, atom/OL, ignoreRest = 0) + ..() + if((!(A) || !(src in A.locs))) + return + + if(destination_z && destination_x && destination_y) + A.forceMove(locate(destination_x, destination_y, destination_z)) + + if(isliving(A)) + var/mob/living/L = A + if(L.pulling) + var/turf/T = get_step(L.loc,turn(A.dir, 180)) + L.pulling.forceMove(T) + + //now we're on the new z_level, proceed the space drifting + sleep(0)//Let a diagonal move finish, if necessary + A.newtonian_move(A.inertia_dir) + +/turf/space/proc/Sandbox_Spacemove(atom/movable/A as mob|obj) + var/cur_x + var/cur_y + var/next_x + var/next_y + var/target_z + var/list/y_arr + + if(src.x <= 1) + if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + qdel(A) + return + + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + next_x = (--cur_x||GLOB.global_map.len) + y_arr = GLOB.global_map[next_x] + target_z = y_arr[cur_y] +/* + //debug + to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_chat(world, "Target Z = [target_z]") + to_chat(world, "Next X = [next_x]") + //debug +*/ + if(target_z) + A.z = target_z + A.x = world.maxx - 2 + spawn (0) + if((A && A.loc)) + A.loc.Entered(A) + else if(src.x >= world.maxx) + if(istype(A, /obj/effect/meteor)) + qdel(A) + return + + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + next_x = (++cur_x > GLOB.global_map.len ? 1 : cur_x) + y_arr = GLOB.global_map[next_x] + target_z = y_arr[cur_y] +/* + //debug + to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_chat(world, "Target Z = [target_z]") + to_chat(world, "Next X = [next_x]") + //debug +*/ + if(target_z) + A.z = target_z + A.x = 3 + spawn (0) + if((A && A.loc)) + A.loc.Entered(A) + else if(src.y <= 1) + if(istype(A, /obj/effect/meteor)) + qdel(A) + return + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + y_arr = GLOB.global_map[cur_x] + next_y = (--cur_y||y_arr.len) + target_z = y_arr[next_y] +/* + //debug + to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_chat(world, "Next Y = [next_y]") + to_chat(world, "Target Z = [target_z]") + //debug +*/ + if(target_z) + A.z = target_z + A.y = world.maxy - 2 + spawn (0) + if((A && A.loc)) + A.loc.Entered(A) + + else if(src.y >= world.maxy) + if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust)) + qdel(A) + return + var/list/cur_pos = src.get_global_map_pos() + if(!cur_pos) return + cur_x = cur_pos["x"] + cur_y = cur_pos["y"] + y_arr = GLOB.global_map[cur_x] + next_y = (++cur_y > y_arr.len ? 1 : cur_y) + target_z = y_arr[next_y] +/* + //debug + to_chat(world, "Src.z = [src.z] in global map X = [cur_x], Y = [cur_y]") + to_chat(world, "Next Y = [next_y]") + to_chat(world, "Target Z = [target_z]") + //debug +*/ + if(target_z) + A.z = target_z + A.y = 3 + spawn (0) + if((A && A.loc)) + A.loc.Entered(A) + return + +/turf/space/singularity_act() + return + +/turf/space/can_have_cabling() + if(locate(/obj/structure/lattice/catwalk, src)) + return 1 + return 0 + +/turf/space/proc/set_transition_north(dest_z) + destination_x = x + destination_y = TRANSITION_BORDER_SOUTH + 1 + destination_z = dest_z + +/turf/space/proc/set_transition_south(dest_z) + destination_x = x + destination_y = TRANSITION_BORDER_NORTH - 1 + destination_z = dest_z + +/turf/space/proc/set_transition_east(dest_z) + destination_x = TRANSITION_BORDER_WEST + 1 + destination_y = y + destination_z = dest_z + +/turf/space/proc/set_transition_west(dest_z) + destination_x = TRANSITION_BORDER_EAST - 1 + destination_y = y + destination_z = dest_z + +/turf/space/proc/remove_transitions() + destination_z = initial(destination_z) + +/turf/space/attack_ghost(mob/dead/observer/user) + if(destination_z) + var/turf/T = locate(destination_x, destination_y, destination_z) + user.forceMove(T) + +/turf/space/acid_act(acidpwr, acid_volume) + return 0 + +/turf/space/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) + underlay_appearance.icon = 'icons/turf/space.dmi' + underlay_appearance.icon_state = SPACE_ICON_STATE + underlay_appearance.plane = PLANE_SPACE + return TRUE diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm index 15c553b3f956..2a14230d467c 100644 --- a/code/game/turfs/space/transit.dm +++ b/code/game/turfs/space/transit.dm @@ -1,160 +1,160 @@ -/turf/space/transit - var/pushdirection // push things that get caught in the transit tile this direction - plane = PLANE_SPACE - -//Overwrite because we dont want people building rods in space. -/turf/space/transit/attackby(obj/O as obj, mob/user as mob, params) - return - -/turf/space/transit/north // moving to the north - - pushdirection = SOUTH // south because the space tile is scrolling south - - //IF ANYONE KNOWS A MORE EFFICIENT WAY OF MANAGING THESE SPRITES, BE MY GUEST. - shuttlespace_ns1 - icon_state = "speedspace_ns_1" - shuttlespace_ns2 - icon_state = "speedspace_ns_2" - shuttlespace_ns3 - icon_state = "speedspace_ns_3" - shuttlespace_ns4 - icon_state = "speedspace_ns_4" - shuttlespace_ns5 - icon_state = "speedspace_ns_5" - shuttlespace_ns6 - icon_state = "speedspace_ns_6" - shuttlespace_ns7 - icon_state = "speedspace_ns_7" - shuttlespace_ns8 - icon_state = "speedspace_ns_8" - shuttlespace_ns9 - icon_state = "speedspace_ns_9" - shuttlespace_ns10 - icon_state = "speedspace_ns_10" - shuttlespace_ns11 - icon_state = "speedspace_ns_11" - shuttlespace_ns12 - icon_state = "speedspace_ns_12" - shuttlespace_ns13 - icon_state = "speedspace_ns_13" - shuttlespace_ns14 - icon_state = "speedspace_ns_14" - shuttlespace_ns15 - icon_state = "speedspace_ns_15" - -/turf/space/transit/east // moving to the east - - pushdirection = WEST - - shuttlespace_ew1 - icon_state = "speedspace_ew_1" - shuttlespace_ew2 - icon_state = "speedspace_ew_2" - shuttlespace_ew3 - icon_state = "speedspace_ew_3" - shuttlespace_ew4 - icon_state = "speedspace_ew_4" - shuttlespace_ew5 - icon_state = "speedspace_ew_5" - shuttlespace_ew6 - icon_state = "speedspace_ew_6" - shuttlespace_ew7 - icon_state = "speedspace_ew_7" - shuttlespace_ew8 - icon_state = "speedspace_ew_8" - shuttlespace_ew9 - icon_state = "speedspace_ew_9" - shuttlespace_ew10 - icon_state = "speedspace_ew_10" - shuttlespace_ew11 - icon_state = "speedspace_ew_11" - shuttlespace_ew12 - icon_state = "speedspace_ew_12" - shuttlespace_ew13 - icon_state = "speedspace_ew_13" - shuttlespace_ew14 - icon_state = "speedspace_ew_14" - shuttlespace_ew15 - icon_state = "speedspace_ew_15" - -//-tg- stuff - -/turf/space/transit - icon_state = "black" - dir = SOUTH - -/turf/space/transit/horizontal - dir = WEST - -/turf/space/transit/Entered(atom/movable/AM, atom/OldLoc, ignoreRest = 0) - if(!AM) - return - if(!AM.simulated || istype(AM, /obj/docking_port)) - return //this was fucking hilarious, the docking ports were getting thrown to random Z-levels - var/max = world.maxx-TRANSITIONEDGE - var/min = 1+TRANSITIONEDGE - - var/_z = pick(levels_by_trait(REACHABLE)) //select a random space zlevel - - //now select coordinates for a border turf - var/_x - var/_y - switch(dir) - if(SOUTH) - _x = rand(min,max) - _y = max - if(WEST) - _x = max - _y = rand(min,max) - if(EAST) - _x = min - _y = rand(min,max) - else - _x = rand(min,max) - _y = min - - var/turf/T = locate(_x, _y, _z) - AM.forceMove(T) - AM.newtonian_move(dir) - - -/turf/space/transit/rpd_act() - return - -//Overwrite because we dont want people building rods in space. -/turf/space/transit/attackby() - return - -/turf/space/transit/New() - update_icon() - ..() - -/turf/space/transit/proc/update_icon() - var/p = 9 - var/angle = 0 - var/state = 1 - switch(dir) - if(NORTH) - angle = 180 - state = ((-p*x+y) % 15) + 1 - if(state < 1) - state += 15 - if(EAST) - angle = 90 - state = ((x+p*y) % 15) + 1 - if(WEST) - angle = -90 - state = ((x-p*y) % 15) + 1 - if(state < 1) - state += 15 - else - state = ((p*x+y) % 15) + 1 - - icon_state = "speedspace_ns_[state]" - transform = turn(matrix(), angle) - -/turf/space/transit/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) - underlay_appearance.icon = 'icons/turf/space.dmi' - underlay_appearance.icon_state = SPACE_ICON_STATE - underlay_appearance.plane = PLANE_SPACE - return TRUE +/turf/space/transit + var/pushdirection // push things that get caught in the transit tile this direction + plane = PLANE_SPACE + +//Overwrite because we dont want people building rods in space. +/turf/space/transit/attackby(obj/O as obj, mob/user as mob, params) + return + +/turf/space/transit/north // moving to the north + + pushdirection = SOUTH // south because the space tile is scrolling south + + //IF ANYONE KNOWS A MORE EFFICIENT WAY OF MANAGING THESE SPRITES, BE MY GUEST. + shuttlespace_ns1 + icon_state = "speedspace_ns_1" + shuttlespace_ns2 + icon_state = "speedspace_ns_2" + shuttlespace_ns3 + icon_state = "speedspace_ns_3" + shuttlespace_ns4 + icon_state = "speedspace_ns_4" + shuttlespace_ns5 + icon_state = "speedspace_ns_5" + shuttlespace_ns6 + icon_state = "speedspace_ns_6" + shuttlespace_ns7 + icon_state = "speedspace_ns_7" + shuttlespace_ns8 + icon_state = "speedspace_ns_8" + shuttlespace_ns9 + icon_state = "speedspace_ns_9" + shuttlespace_ns10 + icon_state = "speedspace_ns_10" + shuttlespace_ns11 + icon_state = "speedspace_ns_11" + shuttlespace_ns12 + icon_state = "speedspace_ns_12" + shuttlespace_ns13 + icon_state = "speedspace_ns_13" + shuttlespace_ns14 + icon_state = "speedspace_ns_14" + shuttlespace_ns15 + icon_state = "speedspace_ns_15" + +/turf/space/transit/east // moving to the east + + pushdirection = WEST + + shuttlespace_ew1 + icon_state = "speedspace_ew_1" + shuttlespace_ew2 + icon_state = "speedspace_ew_2" + shuttlespace_ew3 + icon_state = "speedspace_ew_3" + shuttlespace_ew4 + icon_state = "speedspace_ew_4" + shuttlespace_ew5 + icon_state = "speedspace_ew_5" + shuttlespace_ew6 + icon_state = "speedspace_ew_6" + shuttlespace_ew7 + icon_state = "speedspace_ew_7" + shuttlespace_ew8 + icon_state = "speedspace_ew_8" + shuttlespace_ew9 + icon_state = "speedspace_ew_9" + shuttlespace_ew10 + icon_state = "speedspace_ew_10" + shuttlespace_ew11 + icon_state = "speedspace_ew_11" + shuttlespace_ew12 + icon_state = "speedspace_ew_12" + shuttlespace_ew13 + icon_state = "speedspace_ew_13" + shuttlespace_ew14 + icon_state = "speedspace_ew_14" + shuttlespace_ew15 + icon_state = "speedspace_ew_15" + +//-tg- stuff + +/turf/space/transit + icon_state = "black" + dir = SOUTH + +/turf/space/transit/horizontal + dir = WEST + +/turf/space/transit/Entered(atom/movable/AM, atom/OldLoc, ignoreRest = 0) + if(!AM) + return + if(!AM.simulated || istype(AM, /obj/docking_port)) + return //this was fucking hilarious, the docking ports were getting thrown to random Z-levels + var/max = world.maxx-TRANSITIONEDGE + var/min = 1+TRANSITIONEDGE + + var/_z = pick(levels_by_trait(REACHABLE)) //select a random space zlevel + + //now select coordinates for a border turf + var/_x + var/_y + switch(dir) + if(SOUTH) + _x = rand(min,max) + _y = max + if(WEST) + _x = max + _y = rand(min,max) + if(EAST) + _x = min + _y = rand(min,max) + else + _x = rand(min,max) + _y = min + + var/turf/T = locate(_x, _y, _z) + AM.forceMove(T) + AM.newtonian_move(dir) + + +/turf/space/transit/rpd_act() + return + +//Overwrite because we dont want people building rods in space. +/turf/space/transit/attackby() + return + +/turf/space/transit/New() + update_icon() + ..() + +/turf/space/transit/proc/update_icon() + var/p = 9 + var/angle = 0 + var/state = 1 + switch(dir) + if(NORTH) + angle = 180 + state = ((-p*x+y) % 15) + 1 + if(state < 1) + state += 15 + if(EAST) + angle = 90 + state = ((x+p*y) % 15) + 1 + if(WEST) + angle = -90 + state = ((x-p*y) % 15) + 1 + if(state < 1) + state += 15 + else + state = ((p*x+y) % 15) + 1 + + icon_state = "speedspace_ns_[state]" + transform = turn(matrix(), angle) + +/turf/space/transit/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) + underlay_appearance.icon = 'icons/turf/space.dmi' + underlay_appearance.icon_state = SPACE_ICON_STATE + underlay_appearance.plane = PLANE_SPACE + return TRUE diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index d589c3dcb646..caa46b4e2454 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -70,7 +70,7 @@ /turf/Destroy() // Adds the adjacent turfs to the current atmos processing if(SSair) - for(var/direction in cardinal) + for(var/direction in GLOB.cardinal) if(atmos_adjacent_turfs & direction) var/turf/simulated/T = get_step(src, direction) if(istype(T)) @@ -187,7 +187,7 @@ /turf/proc/ChangeTurf(path, defer_change = FALSE, keep_icon = TRUE, ignore_air = FALSE) if(!path) return - if(!use_preloader && path == type) // Don't no-op if the map loader requires it to be reconstructed + if(!GLOB.use_preloader && path == type) // Don't no-op if the map loader requires it to be reconstructed return src set_light(0) @@ -266,7 +266,7 @@ var/atemp = 0 var/turf_count = 0 - for(var/direction in cardinal)//Only use cardinals to cut down on lag + for(var/direction in GLOB.cardinal)//Only use cardinals to cut down on lag var/turf/T = get_step(src,direction) if(istype(T,/turf/space))//Counted as no air turf_count++//Considered a valid turf for air calcs @@ -327,7 +327,7 @@ var/list/L = new() var/turf/simulated/T - for(var/dir in cardinal) + for(var/dir in GLOB.cardinal) T = get_step(src, dir) if(istype(T) && !T.density) if(!LinkBlockedWithAccess(src, T, ID)) @@ -340,7 +340,7 @@ var/list/L = new() var/turf/simulated/T - for(var/dir in cardinal) + for(var/dir in GLOB.cardinal) T = get_step(src, dir) if(istype(T) && !T.density) if(!CanAtmosPass(T)) @@ -452,7 +452,7 @@ /turf/proc/visibilityChanged() if(SSticker) - cameranet.updateVisibility(src) + GLOB.cameranet.updateVisibility(src) /turf/attackby(obj/item/I, mob/user, params) if(can_lay_cable()) diff --git a/code/game/turfs/unsimulated.dm b/code/game/turfs/unsimulated.dm index 537cfdc8d573..688dc778ce34 100644 --- a/code/game/turfs/unsimulated.dm +++ b/code/game/turfs/unsimulated.dm @@ -1,45 +1,45 @@ -/turf/unsimulated - intact = 1 - name = "command" - oxygen = MOLES_O2STANDARD - nitrogen = MOLES_N2STANDARD - -/turf/unsimulated/can_lay_cable() - return 0 - -/turf/unsimulated/rpd_act() - return - -/turf/unsimulated/acid_act(acidpwr, acid_volume, acid_id) - return 0 - -/turf/unsimulated/floor/plating/vox - icon_state = "plating" - name = "plating" - nitrogen = 100 - oxygen = 0 - -/turf/unsimulated/floor/plating/snow - name = "snow" - icon = 'icons/turf/snow.dmi' - icon_state = "snow" - temperature = T0C - -/turf/unsimulated/floor/plating/snow/concrete - name = "concrete" - icon = 'icons/turf/floors.dmi' - icon_state = "concrete" - -/turf/unsimulated/floor/plating/snow/ex_act(severity) - return - -/turf/unsimulated/floor/plating/airless - icon_state = "plating" - name = "airless plating" - oxygen = 0 - nitrogen = 0 - temperature = TCMB - -/turf/unsimulated/floor/plating/airless/New() - ..() - name = "plating" +/turf/unsimulated + intact = 1 + name = "command" + oxygen = MOLES_O2STANDARD + nitrogen = MOLES_N2STANDARD + +/turf/unsimulated/can_lay_cable() + return 0 + +/turf/unsimulated/rpd_act() + return + +/turf/unsimulated/acid_act(acidpwr, acid_volume, acid_id) + return 0 + +/turf/unsimulated/floor/plating/vox + icon_state = "plating" + name = "plating" + nitrogen = 100 + oxygen = 0 + +/turf/unsimulated/floor/plating/snow + name = "snow" + icon = 'icons/turf/snow.dmi' + icon_state = "snow" + temperature = T0C + +/turf/unsimulated/floor/plating/snow/concrete + name = "concrete" + icon = 'icons/turf/floors.dmi' + icon_state = "concrete" + +/turf/unsimulated/floor/plating/snow/ex_act(severity) + return + +/turf/unsimulated/floor/plating/airless + icon_state = "plating" + name = "airless plating" + oxygen = 0 + nitrogen = 0 + temperature = TCMB + +/turf/unsimulated/floor/plating/airless/New() + ..() + name = "plating" diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index 15650c1f00e7..f6a3a62c3247 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -1,127 +1,127 @@ -/turf/unsimulated/beach - name = "Beach" - icon = 'icons/misc/beach.dmi' - var/water_overlay_image = null - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/turf/unsimulated/beach/New() - ..() - if(water_overlay_image) - var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER) - overlay_image.plane = GAME_PLANE - overlays += overlay_image - -/turf/unsimulated/beach/sand - name = "Sand" - icon_state = "desert" - mouse_opacity = MOUSE_OPACITY_ICON - -/turf/unsimulated/beach/sand/New() //adds some aesthetic randomness to the beach sand - icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4") - ..() - -/turf/unsimulated/beach/sand/dense //for boundary "walls" - density = 1 - -/turf/unsimulated/beach/coastline - name = "Coastline" - //icon = 'icons/misc/beach2.dmi' - //icon_state = "sandwater" - icon_state = "beach" - water_overlay_image = "water_coast" - -/turf/unsimulated/beach/coastline/dense //for boundary "walls" - density = 1 - -/turf/unsimulated/beach/water - name = "Shallow Water" - icon_state = "seashallow" - water_overlay_image = "water_shallow" - var/obj/machinery/poolcontroller/linkedcontroller = null - -/turf/unsimulated/beach/water/Entered(atom/movable/AM, atom/OldLoc) - . = ..() - if(!linkedcontroller) - return - if(ismob(AM)) - linkedcontroller.mobinpool += AM - -/turf/unsimulated/beach/water/Exited(atom/movable/AM, atom/newloc) - . = ..() - if(!linkedcontroller) - return - if(ismob(AM)) - linkedcontroller.mobinpool -= AM - -/turf/unsimulated/beach/water/InitializedOn(atom/A) - if(!linkedcontroller) - return - if(istype(A, /obj/effect/decal/cleanable)) // Better a typecheck than looping through thousands of turfs everyday - linkedcontroller.decalinpool += A - -/turf/unsimulated/beach/water/dense //for boundary "walls" - density = 1 - -/turf/unsimulated/beach/water/edge_drop - name = "Water" - icon_state = "seadrop" - water_overlay_image = "water_drop" - -/turf/unsimulated/beach/water/drop - name = "Water" - icon = 'icons/turf/floors/seadrop.dmi' - icon_state = "seadrop" - water_overlay_image = null - smooth = SMOOTH_TRUE - canSmoothWith = list( - /turf/unsimulated/beach/water/drop, /turf/unsimulated/beach/water/drop/dense, - /turf/unsimulated/beach/water, /turf/unsimulated/beach/water/dense, - /turf/unsimulated/beach/water/edge_drop) - var/obj/effect/beach_drop_overlay/water_overlay - -/turf/unsimulated/beach/water/drop/New() - ..() - water_overlay = new(src) - -/turf/unsimulated/beach/water/drop/Destroy() - QDEL_NULL(water_overlay) - return ..() - -/obj/effect/beach_drop_overlay - name = "Water" - icon = 'icons/turf/floors/seadrop-o.dmi' - layer = MOB_LAYER + 0.1 - smooth = SMOOTH_TRUE - anchored = 1 - canSmoothWith = list( - /turf/unsimulated/beach/water/drop, /turf/unsimulated/beach/water/drop/dense, - /turf/unsimulated/beach/water, /turf/unsimulated/beach/water/dense, - /turf/unsimulated/beach/water/edge_drop) - -/turf/unsimulated/beach/water/drop/dense - density = 1 - -/turf/unsimulated/beach/water/deep - name = "Deep Water" - icon_state = "seadeep" - water_overlay_image = "water_deep" - -/turf/unsimulated/beach/water/deep/dense - density = 1 - -/turf/unsimulated/beach/water/deep/wood_floor - name = "Sunken Floor" - icon = 'icons/turf/floors.dmi' - icon_state = "wood" - -/turf/unsimulated/beach/water/deep/sand_floor - name = "Sea Floor" - icon_state = "sand" - -/turf/unsimulated/beach/water/deep/rock_wall - name = "Reef Stone" - icon_state = "desert7" - density = 1 - opacity = 1 - explosion_block = 2 - mouse_opacity = MOUSE_OPACITY_ICON +/turf/unsimulated/beach + name = "Beach" + icon = 'icons/misc/beach.dmi' + var/water_overlay_image = null + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/turf/unsimulated/beach/New() + ..() + if(water_overlay_image) + var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER) + overlay_image.plane = GAME_PLANE + overlays += overlay_image + +/turf/unsimulated/beach/sand + name = "Sand" + icon_state = "desert" + mouse_opacity = MOUSE_OPACITY_ICON + +/turf/unsimulated/beach/sand/New() //adds some aesthetic randomness to the beach sand + icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4") + ..() + +/turf/unsimulated/beach/sand/dense //for boundary "walls" + density = 1 + +/turf/unsimulated/beach/coastline + name = "Coastline" + //icon = 'icons/misc/beach2.dmi' + //icon_state = "sandwater" + icon_state = "beach" + water_overlay_image = "water_coast" + +/turf/unsimulated/beach/coastline/dense //for boundary "walls" + density = 1 + +/turf/unsimulated/beach/water + name = "Shallow Water" + icon_state = "seashallow" + water_overlay_image = "water_shallow" + var/obj/machinery/poolcontroller/linkedcontroller = null + +/turf/unsimulated/beach/water/Entered(atom/movable/AM, atom/OldLoc) + . = ..() + if(!linkedcontroller) + return + if(ismob(AM)) + linkedcontroller.mobinpool += AM + +/turf/unsimulated/beach/water/Exited(atom/movable/AM, atom/newloc) + . = ..() + if(!linkedcontroller) + return + if(ismob(AM)) + linkedcontroller.mobinpool -= AM + +/turf/unsimulated/beach/water/InitializedOn(atom/A) + if(!linkedcontroller) + return + if(istype(A, /obj/effect/decal/cleanable)) // Better a typecheck than looping through thousands of turfs everyday + linkedcontroller.decalinpool += A + +/turf/unsimulated/beach/water/dense //for boundary "walls" + density = 1 + +/turf/unsimulated/beach/water/edge_drop + name = "Water" + icon_state = "seadrop" + water_overlay_image = "water_drop" + +/turf/unsimulated/beach/water/drop + name = "Water" + icon = 'icons/turf/floors/seadrop.dmi' + icon_state = "seadrop" + water_overlay_image = null + smooth = SMOOTH_TRUE + canSmoothWith = list( + /turf/unsimulated/beach/water/drop, /turf/unsimulated/beach/water/drop/dense, + /turf/unsimulated/beach/water, /turf/unsimulated/beach/water/dense, + /turf/unsimulated/beach/water/edge_drop) + var/obj/effect/beach_drop_overlay/water_overlay + +/turf/unsimulated/beach/water/drop/New() + ..() + water_overlay = new(src) + +/turf/unsimulated/beach/water/drop/Destroy() + QDEL_NULL(water_overlay) + return ..() + +/obj/effect/beach_drop_overlay + name = "Water" + icon = 'icons/turf/floors/seadrop-o.dmi' + layer = MOB_LAYER + 0.1 + smooth = SMOOTH_TRUE + anchored = 1 + canSmoothWith = list( + /turf/unsimulated/beach/water/drop, /turf/unsimulated/beach/water/drop/dense, + /turf/unsimulated/beach/water, /turf/unsimulated/beach/water/dense, + /turf/unsimulated/beach/water/edge_drop) + +/turf/unsimulated/beach/water/drop/dense + density = 1 + +/turf/unsimulated/beach/water/deep + name = "Deep Water" + icon_state = "seadeep" + water_overlay_image = "water_deep" + +/turf/unsimulated/beach/water/deep/dense + density = 1 + +/turf/unsimulated/beach/water/deep/wood_floor + name = "Sunken Floor" + icon = 'icons/turf/floors.dmi' + icon_state = "wood" + +/turf/unsimulated/beach/water/deep/sand_floor + name = "Sea Floor" + icon_state = "sand" + +/turf/unsimulated/beach/water/deep/rock_wall + name = "Reef Stone" + icon_state = "desert7" + density = 1 + opacity = 1 + explosion_block = 2 + mouse_opacity = MOUSE_OPACITY_ICON diff --git a/code/game/turfs/unsimulated/floor.dm b/code/game/turfs/unsimulated/floor.dm index c71404dc0d73..28f4e3592239 100644 --- a/code/game/turfs/unsimulated/floor.dm +++ b/code/game/turfs/unsimulated/floor.dm @@ -1,71 +1,71 @@ -/turf/unsimulated/floor - name = "floor" - icon = 'icons/turf/floors.dmi' - icon_state = "Floor3" - -/turf/unsimulated/floor/grass - name = "grass patch" - icon_state = "grass1" - -/turf/unsimulated/floor/grass/New() - ..() - icon_state = "grass[rand(1,4)]" - -/turf/unsimulated/floor/snow - name = "snow" - icon = 'icons/turf/snow.dmi' - icon_state = "snow" - -/turf/unsimulated/floor/abductor - name = "alien floor" - icon_state = "alienpod1" - -/turf/unsimulated/floor/abductor/New() - ..() - icon_state = "alienpod[rand(1,9)]" - -/turf/unsimulated/floor/vox - icon_state = "dark" - nitrogen = 100 - oxygen = 0 - -/turf/unsimulated/floor/carpet - name = "Carpet" - icon = 'icons/turf/floors/carpet.dmi' - icon_state = "carpet" - smooth = SMOOTH_TRUE - canSmoothWith = null - - footstep_sounds = list( - "human" = list('sound/effects/footstep/carpet_human.ogg'), - "xeno" = list('sound/effects/footstep/carpet_xeno.ogg') - ) - -/turf/unsimulated/floor/wood - icon_state = "wood" - - footstep_sounds = list( - "human" = list('sound/effects/footstep/wood_all.ogg'), //@RonaldVanWonderen of Freesound.org - "xeno" = list('sound/effects/footstep/wood_all.ogg') //@RonaldVanWonderen of Freesound.org - ) - -/turf/unsimulated/floor/lava - name = "lava" - desc = "That looks... a bit dangerous" - icon = 'icons/turf/floors/lava.dmi' - icon_state = "smooth" - smooth = SMOOTH_MORE - canSmoothWith = list(/turf/unsimulated/floor/lava) - var/lava_damage = 250 - var/lava_fire = 20 - light_range = 2 - light_color = "#FFC040" - -/turf/unsimulated/floor/lava/Entered(mob/living/M, atom/OL, ignoreRest = 0) - if(istype(M)) - M.apply_damage(lava_damage, BURN) - M.adjust_fire_stacks(lava_fire) - M.IgniteMob() - -/turf/unsimulated/floor/lava/dense - density = 1 +/turf/unsimulated/floor + name = "floor" + icon = 'icons/turf/floors.dmi' + icon_state = "Floor3" + +/turf/unsimulated/floor/grass + name = "grass patch" + icon_state = "grass1" + +/turf/unsimulated/floor/grass/New() + ..() + icon_state = "grass[rand(1,4)]" + +/turf/unsimulated/floor/snow + name = "snow" + icon = 'icons/turf/snow.dmi' + icon_state = "snow" + +/turf/unsimulated/floor/abductor + name = "alien floor" + icon_state = "alienpod1" + +/turf/unsimulated/floor/abductor/New() + ..() + icon_state = "alienpod[rand(1,9)]" + +/turf/unsimulated/floor/vox + icon_state = "dark" + nitrogen = 100 + oxygen = 0 + +/turf/unsimulated/floor/carpet + name = "Carpet" + icon = 'icons/turf/floors/carpet.dmi' + icon_state = "carpet" + smooth = SMOOTH_TRUE + canSmoothWith = null + + footstep_sounds = list( + "human" = list('sound/effects/footstep/carpet_human.ogg'), + "xeno" = list('sound/effects/footstep/carpet_xeno.ogg') + ) + +/turf/unsimulated/floor/wood + icon_state = "wood" + + footstep_sounds = list( + "human" = list('sound/effects/footstep/wood_all.ogg'), //@RonaldVanWonderen of Freesound.org + "xeno" = list('sound/effects/footstep/wood_all.ogg') //@RonaldVanWonderen of Freesound.org + ) + +/turf/unsimulated/floor/lava + name = "lava" + desc = "That looks... a bit dangerous" + icon = 'icons/turf/floors/lava.dmi' + icon_state = "smooth" + smooth = SMOOTH_MORE + canSmoothWith = list(/turf/unsimulated/floor/lava) + var/lava_damage = 250 + var/lava_fire = 20 + light_range = 2 + light_color = "#FFC040" + +/turf/unsimulated/floor/lava/Entered(mob/living/M, atom/OL, ignoreRest = 0) + if(istype(M)) + M.apply_damage(lava_damage, BURN) + M.adjust_fire_stacks(lava_fire) + M.IgniteMob() + +/turf/unsimulated/floor/lava/dense + density = 1 diff --git a/code/game/turfs/unsimulated/walls.dm b/code/game/turfs/unsimulated/walls.dm index c7b192366d4c..c5688c31323d 100644 --- a/code/game/turfs/unsimulated/walls.dm +++ b/code/game/turfs/unsimulated/walls.dm @@ -1,35 +1,35 @@ -/turf/unsimulated/wall - name = "wall" - icon = 'icons/turf/walls.dmi' - icon_state = "riveted" - opacity = 1 - density = 1 - explosion_block = 2 - -/turf/unsimulated/wall/fakeglass - name = "window" - icon_state = "fakewindows" - opacity = 0 - -/turf/unsimulated/wall/fakedoor - name = "Centcom Access" - icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi' - icon_state = "closed" - -/turf/unsimulated/wall/splashscreen - name = "Space Station 13" - icon = 'config/title_screens/images/blank.png' - icon_state = "" - layer = FLY_LAYER - -/turf/unsimulated/wall/other - icon_state = "r_wall" - -/turf/unsimulated/wall/metal - icon = 'icons/turf/walls/wall.dmi' - icon_state = "wall" - smooth = SMOOTH_TRUE - -/turf/unsimulated/wall/abductor - icon_state = "alien1" - explosion_block = 50 +/turf/unsimulated/wall + name = "wall" + icon = 'icons/turf/walls.dmi' + icon_state = "riveted" + opacity = 1 + density = 1 + explosion_block = 2 + +/turf/unsimulated/wall/fakeglass + name = "window" + icon_state = "fakewindows" + opacity = 0 + +/turf/unsimulated/wall/fakedoor + name = "Centcom Access" + icon = 'icons/obj/doors/airlocks/centcom/centcom.dmi' + icon_state = "closed" + +/turf/unsimulated/wall/splashscreen + name = "Space Station 13" + icon = 'config/title_screens/images/blank.png' + icon_state = "" + layer = FLY_LAYER + +/turf/unsimulated/wall/other + icon_state = "r_wall" + +/turf/unsimulated/wall/metal + icon = 'icons/turf/walls/wall.dmi' + icon_state = "wall" + smooth = SMOOTH_TRUE + +/turf/unsimulated/wall/abductor + icon_state = "alien1" + explosion_block = 50 diff --git a/code/game/verbs/ooc.dm b/code/game/verbs/ooc.dm index df2f588753fe..824159487eb1 100644 --- a/code/game/verbs/ooc.dm +++ b/code/game/verbs/ooc.dm @@ -1,255 +1,255 @@ -var/global/normal_ooc_colour = "#002eb8" -var/global/member_ooc_colour = "#035417" -var/global/mentor_ooc_colour = "#0099cc" -var/global/moderator_ooc_colour = "#184880" -var/global/admin_ooc_colour = "#b82e00" - -//Checks if the client already has a text input open -/client/proc/checkTyping() - return (prefs.toggles & TYPING_ONCE && typing) - -/client/verb/ooc(msg = "" as text) - set name = "OOC" - set category = "OOC" - - if(!mob) - return - if(IsGuestKey(key)) - to_chat(src, "Guests may not use OOC.") - return - - if(!check_rights(R_ADMIN|R_MOD, 0)) - if(!config.ooc_allowed) - to_chat(src, "OOC is globally muted.") - return - if(!config.dooc_allowed && (mob.stat == DEAD)) - to_chat(usr, "OOC for dead mobs has been turned off.") - return - if(prefs.muted & MUTE_OOC) - to_chat(src, "You cannot use OOC (muted).") - return - - if(!msg) - msg = typing_input(src.mob, "", "ooc \"text\"") - - msg = trim(sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))) - if(!msg) - return - - if(!(prefs.toggles & CHAT_OOC)) - to_chat(src, "You have OOC muted.") - return - - if(!check_rights(R_ADMIN|R_MOD,0)) - if(!config.ooc_allowed) - to_chat(src, "OOC is globally muted.") - return - if(handle_spam_prevention(msg, MUTE_OOC, OOC_COOLDOWN)) - return - if(findtext(msg, "byond://")) - to_chat(src, "Advertising other servers is not allowed.") - log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]") - message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]") - return - - log_ooc(msg, src) - - var/display_colour = normal_ooc_colour - if(holder && !holder.fakekey) - display_colour = mentor_ooc_colour - if(check_rights(R_MOD,0) && !check_rights(R_ADMIN,0)) - display_colour = moderator_ooc_colour - else if(check_rights(R_ADMIN,0)) - if(config.allow_admin_ooccolor) - display_colour = src.prefs.ooccolor - else - display_colour = admin_ooc_colour - - if(prefs.unlock_content) - if(display_colour == normal_ooc_colour) - if((prefs.toggles & MEMBER_PUBLIC)) - display_colour = member_ooc_colour - - for(var/client/C in GLOB.clients) - if(C.prefs.toggles & CHAT_OOC) - var/display_name = key - - if(prefs.unlock_content) - if(prefs.toggles & MEMBER_PUBLIC) - var/icon/byond = icon('icons/member_content.dmi', "blag") - display_name = "[bicon(byond)][display_name]" - - if(donator_level > 0) - if((prefs.toggles & DONATOR_PUBLIC)) - var/icon/donator = icon('icons/ooc_tag_16x.dmi', "donator") - display_name = "[bicon(donator)][display_name]" - - if(holder) - if(holder.fakekey) - if(C.holder && C.holder.rights & R_ADMIN) - display_name = "[holder.fakekey]/([key])" - else - display_name = holder.fakekey - - if(!config.disable_ooc_emoji) - msg = "[msg]" - - to_chat(C, "OOC: [display_name]: [msg]") - -/proc/toggle_ooc() - config.ooc_allowed = ( !config.ooc_allowed ) - if(config.ooc_allowed) - to_chat(world, "The OOC channel has been globally enabled!") - else - to_chat(world, "The OOC channel has been globally disabled!") - -/proc/auto_toggle_ooc(var/on) - if(config.auto_toggle_ooc_during_round && config.ooc_allowed != on) - toggle_ooc() - -/client/proc/set_ooc(newColor as color) - set name = "Set Player OOC Colour" - set desc = "Modifies the default player OOC color." - set category = "Server" - - if(!check_rights(R_SERVER)) return - - normal_ooc_colour = newColor - message_admins("[key_name_admin(usr)] has set the default player OOC color to [newColor]") - log_admin("[key_name(usr)] has set the default player OOC color to [newColor]") - - - feedback_add_details("admin_verb","SOOC") - -/client/proc/reset_ooc() - set name = "Reset Player OOC Color" - set desc = "Returns the default player OOC color to default." - set category = "Server" - - if(!check_rights(R_SERVER)) return - - normal_ooc_colour = initial(normal_ooc_colour) - message_admins("[key_name_admin(usr)] has reset the default player OOC color") - log_admin("[key_name(usr)] has reset the default player OOC color") - - feedback_add_details("admin_verb","ROOC") - -/client/proc/colorooc() - set name = "Set Your OOC Color" - set desc = "Allows you to pick a custom OOC color." - set category = "Preferences" - - if(!check_rights(R_ADMIN)) return - - var/new_ooccolor = input(src, "Please select your OOC color.", "OOC color", prefs.ooccolor) as color|null - if(new_ooccolor) - prefs.ooccolor = new_ooccolor - prefs.save_preferences(src) - to_chat(usr, "Your OOC color has been set to [new_ooccolor].") - - feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/resetcolorooc() - set name = "Reset Your OOC Color" - set desc = "Returns your OOC color to default." - set category = "Preferences" - - if(!check_rights(R_ADMIN)) return - - prefs.ooccolor = initial(prefs.ooccolor) - prefs.save_preferences(src) - to_chat(usr, "Your OOC color has been reset.") - - feedback_add_details("admin_verb","ROC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/verb/looc(msg = "" as text) - set name = "LOOC" - set desc = "Local OOC, seen only by those in view." - set category = "OOC" - - if(!mob) - return - if(IsGuestKey(key)) - to_chat(src, "Guests may not use OOC.") - return - - if(!check_rights(R_ADMIN|R_MOD,0)) - if(!config.looc_allowed) - to_chat(src, "LOOC is globally muted.") - return - if(!config.dooc_allowed && (mob.stat == DEAD)) - to_chat(usr, "LOOC for dead mobs has been turned off.") - return - if(prefs.muted & MUTE_OOC) - to_chat(src, "You cannot use LOOC (muted).") - return - - if(!msg) - msg = typing_input(src.mob, "Local OOC, seen only by those in view.", "looc \"text\"") - - msg = trim(sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))) - if(!msg) - return - - if(!(prefs.toggles & CHAT_LOOC)) - to_chat(src, "You have LOOC muted.") - return - - if(!check_rights(R_ADMIN|R_MOD,0)) - if(handle_spam_prevention(msg, MUTE_OOC, OOC_COOLDOWN)) - return - if(findtext(msg, "byond://")) - to_chat(src, "Advertising other servers is not allowed.") - log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]") - message_admins("[key_name_admin(src)] has attempted to advertise in LOOC: [msg]") - return - - log_looc(msg, src) - - var/mob/source = mob.get_looc_source() - var/list/heard = get_mobs_in_view(7, source) - - var/display_name = key - if(holder && holder.fakekey) - display_name = holder.fakekey - if(mob.stat != DEAD) - display_name = mob.name - - for(var/client/target in GLOB.clients) - if(target.prefs.toggles & CHAT_LOOC) - var/prefix = "" - var/admin_stuff = "" - var/send = 0 - - if(target in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD,0,target.mob)) - admin_stuff += "/([key])" - if(target != src) - admin_stuff += " ([admin_jump_link(mob)])" - - if(target.mob in heard) - send = 1 - if(isAI(target.mob)) - prefix = " (Core)" - - else if(isAI(target.mob)) // Special case - var/mob/living/silicon/ai/A = target.mob - if(A.eyeobj in hearers(7, source)) - send = 1 - prefix = " (Eye)" - - if(!send && (target in GLOB.admins)) - if(check_rights(R_ADMIN|R_MOD,0,target.mob)) - send = 1 - prefix = "(R)" - - if(send) - to_chat(target, "LOOC[prefix]: [display_name][admin_stuff]: [msg]") - -/mob/proc/get_looc_source() - return src - -/mob/living/silicon/ai/get_looc_source() - if(eyeobj) - return eyeobj - return src +GLOBAL_VAR_INIT(normal_ooc_colour, "#002eb8") +GLOBAL_VAR_INIT(member_ooc_colour, "#035417") +GLOBAL_VAR_INIT(mentor_ooc_colour, "#0099cc") +GLOBAL_VAR_INIT(moderator_ooc_colour, "#184880") +GLOBAL_VAR_INIT(admin_ooc_colour, "#b82e00") + +//Checks if the client already has a text input open +/client/proc/checkTyping() + return (prefs.toggles & TYPING_ONCE && typing) + +/client/verb/ooc(msg = "" as text) + set name = "OOC" + set category = "OOC" + + if(!mob) + return + if(IsGuestKey(key)) + to_chat(src, "Guests may not use OOC.") + return + + if(!check_rights(R_ADMIN|R_MOD, 0)) + if(!config.ooc_allowed) + to_chat(src, "OOC is globally muted.") + return + if(!config.dooc_allowed && (mob.stat == DEAD)) + to_chat(usr, "OOC for dead mobs has been turned off.") + return + if(prefs.muted & MUTE_OOC) + to_chat(src, "You cannot use OOC (muted).") + return + + if(!msg) + msg = typing_input(src.mob, "", "ooc \"text\"") + + msg = trim(sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))) + if(!msg) + return + + if(!(prefs.toggles & CHAT_OOC)) + to_chat(src, "You have OOC muted.") + return + + if(!check_rights(R_ADMIN|R_MOD,0)) + if(!config.ooc_allowed) + to_chat(src, "OOC is globally muted.") + return + if(handle_spam_prevention(msg, MUTE_OOC, OOC_COOLDOWN)) + return + if(findtext(msg, "byond://")) + to_chat(src, "Advertising other servers is not allowed.") + log_admin("[key_name(src)] has attempted to advertise in OOC: [msg]") + message_admins("[key_name_admin(src)] has attempted to advertise in OOC: [msg]") + return + + log_ooc(msg, src) + + var/display_colour = GLOB.normal_ooc_colour + if(holder && !holder.fakekey) + display_colour = GLOB.mentor_ooc_colour + if(check_rights(R_MOD,0) && !check_rights(R_ADMIN,0)) + display_colour = GLOB.moderator_ooc_colour + else if(check_rights(R_ADMIN,0)) + if(config.allow_admin_ooccolor) + display_colour = src.prefs.ooccolor + else + display_colour = GLOB.admin_ooc_colour + + if(prefs.unlock_content) + if(display_colour == GLOB.normal_ooc_colour) + if((prefs.toggles & MEMBER_PUBLIC)) + display_colour = GLOB.member_ooc_colour + + for(var/client/C in GLOB.clients) + if(C.prefs.toggles & CHAT_OOC) + var/display_name = key + + if(prefs.unlock_content) + if(prefs.toggles & MEMBER_PUBLIC) + var/icon/byond = icon('icons/member_content.dmi', "blag") + display_name = "[bicon(byond)][display_name]" + + if(donator_level > 0) + if((prefs.toggles & DONATOR_PUBLIC)) + var/icon/donator = icon('icons/ooc_tag_16x.dmi', "donator") + display_name = "[bicon(donator)][display_name]" + + if(holder) + if(holder.fakekey) + if(C.holder && C.holder.rights & R_ADMIN) + display_name = "[holder.fakekey]/([key])" + else + display_name = holder.fakekey + + if(!config.disable_ooc_emoji) + msg = "[msg]" + + to_chat(C, "OOC: [display_name]: [msg]") + +/proc/toggle_ooc() + config.ooc_allowed = ( !config.ooc_allowed ) + if(config.ooc_allowed) + to_chat(world, "The OOC channel has been globally enabled!") + else + to_chat(world, "The OOC channel has been globally disabled!") + +/proc/auto_toggle_ooc(var/on) + if(config.auto_toggle_ooc_during_round && config.ooc_allowed != on) + toggle_ooc() + +/client/proc/set_ooc(newColor as color) + set name = "Set Player OOC Colour" + set desc = "Modifies the default player OOC color." + set category = "Server" + + if(!check_rights(R_SERVER)) return + + GLOB.normal_ooc_colour = newColor + message_admins("[key_name_admin(usr)] has set the default player OOC color to [newColor]") + log_admin("[key_name(usr)] has set the default player OOC color to [newColor]") + + + feedback_add_details("admin_verb","SOOC") + +/client/proc/reset_ooc() + set name = "Reset Player OOC Color" + set desc = "Returns the default player OOC color to default." + set category = "Server" + + if(!check_rights(R_SERVER)) return + + GLOB.normal_ooc_colour = initial(GLOB.normal_ooc_colour) + message_admins("[key_name_admin(usr)] has reset the default player OOC color") + log_admin("[key_name(usr)] has reset the default player OOC color") + + feedback_add_details("admin_verb","ROOC") + +/client/proc/colorooc() + set name = "Set Your OOC Color" + set desc = "Allows you to pick a custom OOC color." + set category = "Preferences" + + if(!check_rights(R_ADMIN)) return + + var/new_ooccolor = input(src, "Please select your OOC color.", "OOC color", prefs.ooccolor) as color|null + if(new_ooccolor) + prefs.ooccolor = new_ooccolor + prefs.save_preferences(src) + to_chat(usr, "Your OOC color has been set to [new_ooccolor].") + + feedback_add_details("admin_verb","OC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/resetcolorooc() + set name = "Reset Your OOC Color" + set desc = "Returns your OOC color to default." + set category = "Preferences" + + if(!check_rights(R_ADMIN)) return + + prefs.ooccolor = initial(prefs.ooccolor) + prefs.save_preferences(src) + to_chat(usr, "Your OOC color has been reset.") + + feedback_add_details("admin_verb","ROC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/verb/looc(msg = "" as text) + set name = "LOOC" + set desc = "Local OOC, seen only by those in view." + set category = "OOC" + + if(!mob) + return + if(IsGuestKey(key)) + to_chat(src, "Guests may not use OOC.") + return + + if(!check_rights(R_ADMIN|R_MOD,0)) + if(!config.looc_allowed) + to_chat(src, "LOOC is globally muted.") + return + if(!config.dooc_allowed && (mob.stat == DEAD)) + to_chat(usr, "LOOC for dead mobs has been turned off.") + return + if(prefs.muted & MUTE_OOC) + to_chat(src, "You cannot use LOOC (muted).") + return + + if(!msg) + msg = typing_input(src.mob, "Local OOC, seen only by those in view.", "looc \"text\"") + + msg = trim(sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))) + if(!msg) + return + + if(!(prefs.toggles & CHAT_LOOC)) + to_chat(src, "You have LOOC muted.") + return + + if(!check_rights(R_ADMIN|R_MOD,0)) + if(handle_spam_prevention(msg, MUTE_OOC, OOC_COOLDOWN)) + return + if(findtext(msg, "byond://")) + to_chat(src, "Advertising other servers is not allowed.") + log_admin("[key_name(src)] has attempted to advertise in LOOC: [msg]") + message_admins("[key_name_admin(src)] has attempted to advertise in LOOC: [msg]") + return + + log_looc(msg, src) + + var/mob/source = mob.get_looc_source() + var/list/heard = get_mobs_in_view(7, source) + + var/display_name = key + if(holder && holder.fakekey) + display_name = holder.fakekey + if(mob.stat != DEAD) + display_name = mob.name + + for(var/client/target in GLOB.clients) + if(target.prefs.toggles & CHAT_LOOC) + var/prefix = "" + var/admin_stuff = "" + var/send = 0 + + if(target in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD,0,target.mob)) + admin_stuff += "/([key])" + if(target != src) + admin_stuff += " ([admin_jump_link(mob)])" + + if(target.mob in heard) + send = 1 + if(isAI(target.mob)) + prefix = " (Core)" + + else if(isAI(target.mob)) // Special case + var/mob/living/silicon/ai/A = target.mob + if(A.eyeobj in hearers(7, source)) + send = 1 + prefix = " (Eye)" + + if(!send && (target in GLOB.admins)) + if(check_rights(R_ADMIN|R_MOD,0,target.mob)) + send = 1 + prefix = "(R)" + + if(send) + to_chat(target, "LOOC[prefix]: [display_name][admin_stuff]: [msg]") + +/mob/proc/get_looc_source() + return src + +/mob/living/silicon/ai/get_looc_source() + if(eyeobj) + return eyeobj + return src diff --git a/code/game/verbs/suicide.dm b/code/game/verbs/suicide.dm index 50b53cc7dd98..4df9dd832c42 100644 --- a/code/game/verbs/suicide.dm +++ b/code/game/verbs/suicide.dm @@ -1,247 +1,247 @@ -/mob/var/suiciding = 0 - -/mob/living/carbon/human/proc/do_suicide(damagetype, byitem) - var/threshold = check_death_method() ? ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) / 2) : (HEALTH_THRESHOLD_DEAD - 50) - var/dmgamt = maxHealth - threshold - - var/damage_mod = 1 - switch(damagetype) //Sorry about the magic numbers. - //brute = 1, burn = 2, tox = 4, oxy = 8 - if(15) //4 damage types - damage_mod = 4 - - if(6, 11, 13, 14) //3 damage types - damage_mod = 3 - - if(3, 5, 7, 9, 10, 12) //2 damage types - damage_mod = 2 - - if(1, 2, 4, 8) //1 damage type - damage_mod = 1 - - else //This should not happen, but if it does, everything should still work - damage_mod = 1 - - //Do dmgamt damage divided by the number of damage types applied. - if(damagetype & BRUTELOSS) - adjustBruteLoss(dmgamt / damage_mod, FALSE) - - if(damagetype & FIRELOSS) - adjustFireLoss(dmgamt / damage_mod, FALSE) - - if(damagetype & TOXLOSS) - adjustToxLoss(dmgamt / damage_mod, FALSE) - - if(damagetype & OXYLOSS) - adjustOxyLoss(dmgamt / damage_mod, FALSE) - - // Failing that... - if(!(damagetype & BRUTELOSS) && !(damagetype & FIRELOSS) && !(damagetype & TOXLOSS) && !(damagetype & OXYLOSS)) - if(NO_BREATHE in dna.species.species_traits) - // the ultimate fallback - take_overall_damage(max(dmgamt - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0), 0, updating_health = FALSE) - else - adjustOxyLoss(max(dmgamt - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0), FALSE) - - var/obj/item/organ/external/affected = get_organ("head") - if(affected) - affected.add_autopsy_data(byitem ? "Suicide by [byitem]" : "Suicide", dmgamt) - - updatehealth() - -/mob/living/carbon/human/verb/suicide() - set hidden = 1 - - be_suicidal() - -/mob/living/carbon/human/proc/be_suicidal(forced = FALSE) - if(stat == DEAD) - to_chat(src, "You're already dead!") - return - - if(!SSticker) - to_chat(src, "You can't commit suicide before the game starts!") - return - - // No more borergrief, one way or the other - if(has_brain_worms()) - to_chat(src, "You try to bring yourself to commit suicide, but - something prevents you!") - return - - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - - var/confirm = null - if(!forced) - confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(forced || (confirm == "Yes")) - suiciding = TRUE - var/obj/item/held_item = get_active_hand() - if(held_item) - var/damagetype = held_item.suicide_act(src) - if(damagetype) - if(damagetype & SHAME) - adjustStaminaLoss(200) - suiciding = FALSE - return - if(damagetype & OBLITERATION) // Does it gib or something? Don't deal damage - return - do_suicide(damagetype, held_item) - return - else - for(var/obj/O in orange(1, src)) - if(O.suicidal_hands) - continue - var/damagetype = O.suicide_act(src) - if(damagetype) - if(damagetype & SHAME) - adjustStaminaLoss(200) - suiciding = FALSE - return - if(damagetype & OBLITERATION) - return - do_suicide(damagetype, O) - return - - to_chat(viewers(src), "[src] [replacetext(pick(dna.species.suicide_messages), "their", p_their())] It looks like [p_theyre()] trying to commit suicide.") - do_suicide(0) - -/mob/living/carbon/brain/verb/suicide() - set hidden = 1 - - if(stat == 2) - to_chat(src, "You're already dead!") - return - - if(!SSticker) - to_chat(src, "You can't commit suicide before the game starts!") - return - - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(confirm == "Yes") - suiciding = 1 - to_chat(viewers(loc), "[src]'s brain is growing dull and lifeless. It looks like it's lost the will to live.") - spawn(50) - death(0) - suiciding = 0 - - -/mob/living/silicon/ai/verb/suicide() - set hidden = 1 - - if(stat == 2) - to_chat(src, "You're already dead!") - return - - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(confirm == "Yes") - suiciding = 1 - to_chat(viewers(src), "[src] is powering down. It looks like [p_theyre()] trying to commit suicide.") - //put em at -175 - adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) - -/mob/living/silicon/robot/verb/suicide() - set hidden = 1 - - if(stat == 2) - to_chat(src, "You're already dead!") - return - - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(confirm == "Yes") - suiciding = 1 - to_chat(viewers(src), "[src] is powering down. It looks like [p_theyre()] trying to commit suicide.") - //put em at -175 - adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) - -/mob/living/silicon/pai/verb/suicide() - set category = "pAI Commands" - set desc = "Kill yourself and become a ghost (You will receive a confirmation prompt)" - set name = "pAI Suicide" - var/answer = input("REALLY kill yourself? This action can't be undone.", "Suicide", "No") in list ("Yes", "No") - if(answer == "Yes") - if(canmove || resting) - close_up() - var/obj/item/paicard/card = loc - card.removePersonality() - var/turf/T = get_turf_or_move(card.loc) - for(var/mob/M in viewers(T)) - M.show_message("[src] flashes a message across its screen, \"Wiping core files. Please acquire a new personality to continue using pAI device functions.\"", 3, "[src] bleeps electronically.", 2) - death(0, 1) - else - to_chat(src, "Aborting suicide attempt.") - -/mob/living/carbon/alien/humanoid/verb/suicide() - set hidden = 1 - - if(stat == 2) - to_chat(src, "You're already dead!") - return - - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(confirm == "Yes") - suiciding = 1 - to_chat(viewers(src), "[src] is thrashing wildly! It looks like [p_theyre()] trying to commit suicide.") - //put em at -175 - adjustOxyLoss(max(175 - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) - - -/mob/living/simple_animal/slime/verb/suicide() - set hidden = 1 - if(stat == 2) - to_chat(src, "You're already dead!") - return - - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(confirm == "Yes") - suiciding = 1 - setOxyLoss(100, FALSE) - adjustBruteLoss(100 - getBruteLoss(), FALSE) - setToxLoss(100, FALSE) - setCloneLoss(100, FALSE) - - updatehealth() - -/mob/living/simple_animal/mouse/verb/suicide() - set hidden = 1 - if(stat == DEAD) - to_chat(src, "You're already dead!") - return - if(suiciding) - to_chat(src, "You're already committing suicide! Be patient!") - return - - var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") - - if(confirm == "Yes") - suiciding = TRUE - visible_message("[src] is playing dead permanently! It looks like [p_theyre()] trying to commit suicide.") - adjustOxyLoss(max(100 - getBruteLoss(100), 0)) \ No newline at end of file +/mob/var/suiciding = 0 + +/mob/living/carbon/human/proc/do_suicide(damagetype, byitem) + var/threshold = check_death_method() ? ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) / 2) : (HEALTH_THRESHOLD_DEAD - 50) + var/dmgamt = maxHealth - threshold + + var/damage_mod = 1 + switch(damagetype) //Sorry about the magic numbers. + //brute = 1, burn = 2, tox = 4, oxy = 8 + if(15) //4 damage types + damage_mod = 4 + + if(6, 11, 13, 14) //3 damage types + damage_mod = 3 + + if(3, 5, 7, 9, 10, 12) //2 damage types + damage_mod = 2 + + if(1, 2, 4, 8) //1 damage type + damage_mod = 1 + + else //This should not happen, but if it does, everything should still work + damage_mod = 1 + + //Do dmgamt damage divided by the number of damage types applied. + if(damagetype & BRUTELOSS) + adjustBruteLoss(dmgamt / damage_mod, FALSE) + + if(damagetype & FIRELOSS) + adjustFireLoss(dmgamt / damage_mod, FALSE) + + if(damagetype & TOXLOSS) + adjustToxLoss(dmgamt / damage_mod, FALSE) + + if(damagetype & OXYLOSS) + adjustOxyLoss(dmgamt / damage_mod, FALSE) + + // Failing that... + if(!(damagetype & BRUTELOSS) && !(damagetype & FIRELOSS) && !(damagetype & TOXLOSS) && !(damagetype & OXYLOSS)) + if(NO_BREATHE in dna.species.species_traits) + // the ultimate fallback + take_overall_damage(max(dmgamt - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0), 0, updating_health = FALSE) + else + adjustOxyLoss(max(dmgamt - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0), FALSE) + + var/obj/item/organ/external/affected = get_organ("head") + if(affected) + affected.add_autopsy_data(byitem ? "Suicide by [byitem]" : "Suicide", dmgamt) + + updatehealth() + +/mob/living/carbon/human/verb/suicide() + set hidden = 1 + + be_suicidal() + +/mob/living/carbon/human/proc/be_suicidal(forced = FALSE) + if(stat == DEAD) + to_chat(src, "You're already dead!") + return + + if(!SSticker) + to_chat(src, "You can't commit suicide before the game starts!") + return + + // No more borergrief, one way or the other + if(has_brain_worms()) + to_chat(src, "You try to bring yourself to commit suicide, but - something prevents you!") + return + + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + + var/confirm = null + if(!forced) + confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(forced || (confirm == "Yes")) + suiciding = TRUE + var/obj/item/held_item = get_active_hand() + if(held_item) + var/damagetype = held_item.suicide_act(src) + if(damagetype) + if(damagetype & SHAME) + adjustStaminaLoss(200) + suiciding = FALSE + return + if(damagetype & OBLITERATION) // Does it gib or something? Don't deal damage + return + do_suicide(damagetype, held_item) + return + else + for(var/obj/O in orange(1, src)) + if(O.suicidal_hands) + continue + var/damagetype = O.suicide_act(src) + if(damagetype) + if(damagetype & SHAME) + adjustStaminaLoss(200) + suiciding = FALSE + return + if(damagetype & OBLITERATION) + return + do_suicide(damagetype, O) + return + + to_chat(viewers(src), "[src] [replacetext(pick(dna.species.suicide_messages), "their", p_their())] It looks like [p_theyre()] trying to commit suicide.") + do_suicide(0) + +/mob/living/carbon/brain/verb/suicide() + set hidden = 1 + + if(stat == 2) + to_chat(src, "You're already dead!") + return + + if(!SSticker) + to_chat(src, "You can't commit suicide before the game starts!") + return + + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(confirm == "Yes") + suiciding = 1 + to_chat(viewers(loc), "[src]'s brain is growing dull and lifeless. It looks like it's lost the will to live.") + spawn(50) + death(0) + suiciding = 0 + + +/mob/living/silicon/ai/verb/suicide() + set hidden = 1 + + if(stat == 2) + to_chat(src, "You're already dead!") + return + + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(confirm == "Yes") + suiciding = 1 + to_chat(viewers(src), "[src] is powering down. It looks like [p_theyre()] trying to commit suicide.") + //put em at -175 + adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) + +/mob/living/silicon/robot/verb/suicide() + set hidden = 1 + + if(stat == 2) + to_chat(src, "You're already dead!") + return + + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(confirm == "Yes") + suiciding = 1 + to_chat(viewers(src), "[src] is powering down. It looks like [p_theyre()] trying to commit suicide.") + //put em at -175 + adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) + +/mob/living/silicon/pai/verb/suicide() + set category = "pAI Commands" + set desc = "Kill yourself and become a ghost (You will receive a confirmation prompt)" + set name = "pAI Suicide" + var/answer = input("REALLY kill yourself? This action can't be undone.", "Suicide", "No") in list ("Yes", "No") + if(answer == "Yes") + if(canmove || resting) + close_up() + var/obj/item/paicard/card = loc + card.removePersonality() + var/turf/T = get_turf_or_move(card.loc) + for(var/mob/M in viewers(T)) + M.show_message("[src] flashes a message across its screen, \"Wiping core files. Please acquire a new personality to continue using pAI device functions.\"", 3, "[src] bleeps electronically.", 2) + death(0, 1) + else + to_chat(src, "Aborting suicide attempt.") + +/mob/living/carbon/alien/humanoid/verb/suicide() + set hidden = 1 + + if(stat == 2) + to_chat(src, "You're already dead!") + return + + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(confirm == "Yes") + suiciding = 1 + to_chat(viewers(src), "[src] is thrashing wildly! It looks like [p_theyre()] trying to commit suicide.") + //put em at -175 + adjustOxyLoss(max(175 - getFireLoss() - getBruteLoss() - getOxyLoss(), 0)) + + +/mob/living/simple_animal/slime/verb/suicide() + set hidden = 1 + if(stat == 2) + to_chat(src, "You're already dead!") + return + + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(confirm == "Yes") + suiciding = 1 + setOxyLoss(100, FALSE) + adjustBruteLoss(100 - getBruteLoss(), FALSE) + setToxLoss(100, FALSE) + setCloneLoss(100, FALSE) + + updatehealth() + +/mob/living/simple_animal/mouse/verb/suicide() + set hidden = 1 + if(stat == DEAD) + to_chat(src, "You're already dead!") + return + if(suiciding) + to_chat(src, "You're already committing suicide! Be patient!") + return + + var/confirm = alert("Are you sure you want to commit suicide?", "Confirm Suicide", "Yes", "No") + + if(confirm == "Yes") + suiciding = TRUE + visible_message("[src] is playing dead permanently! It looks like [p_theyre()] trying to commit suicide.") + adjustOxyLoss(max(100 - getBruteLoss(100), 0)) diff --git a/code/game/verbs/who.dm b/code/game/verbs/who.dm index 8a322edd2f8f..2683e2362697 100644 --- a/code/game/verbs/who.dm +++ b/code/game/verbs/who.dm @@ -1,130 +1,130 @@ - -/client/verb/who() - set name = "Who" - set category = "OOC" - - var/msg = "Current Players:\n" - - - var/list/Lines = list() - - if(check_rights(R_ADMIN,0)) - for(var/client/C in GLOB.clients) - if(C.holder && C.holder.big_brother && !check_rights(R_PERMISSIONS, 0)) // need PERMISSIONS to see BB - continue - - var/entry = "\t[C.key]" - if(C.holder && C.holder.fakekey) - entry += " (as [C.holder.fakekey])" - entry += " - Playing as [C.mob.real_name]" - switch(C.mob.stat) - if(UNCONSCIOUS) - entry += " - Unconscious" - if(DEAD) - if(isobserver(C.mob)) - var/mob/dead/observer/O = C.mob - if(O.started_as_observer) - entry += " - Observing" - else - entry += " - DEAD" - else if(istype(C.mob, /mob/new_player)) - entry += " - New Player" - else - entry += " - DEAD" - - var/age - if(isnum(C.player_age)) - age = C.player_age - else - age = 0 - - if(age <= 1) - age = "[age]" - else if(age < 10) - age = "[age]" - - entry += " - [age]" - - if(is_special_character(C.mob)) - entry += " - Antagonist" - entry += " ([ADMIN_QUE(C.mob,"?")])" - Lines += entry - else - for(var/client/C in GLOB.clients) - if(C.holder && C.holder.big_brother) // BB doesn't show up at all - continue - - if(C.holder && C.holder.fakekey) - Lines += C.holder.fakekey - else - Lines += C.key - - for(var/line in sortList(Lines)) - msg += "[line]\n" - - msg += "Total Players: [length(Lines)]" - to_chat(src, msg) - -/client/verb/adminwho() - set category = "Admin" - set name = "Adminwho" - - var/msg = "" - var/modmsg = "" - var/num_mods_online = 0 - var/num_admins_online = 0 - if(holder) - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN, 0, C.mob)) - - if(C.holder.fakekey && !check_rights(R_ADMIN, 0)) //Mentors can't see stealthmins - continue - - if(C.holder.big_brother && !check_rights(R_PERMISSIONS, 0)) // normal admins can't see BB - continue - - msg += "\t[C] is a [C.holder.rank]" - - if(C.holder.fakekey) - msg += " (as [C.holder.fakekey])" - - if(isobserver(C.mob)) - msg += " - Observing" - else if(istype(C.mob,/mob/new_player)) - msg += " - Lobby" - else - msg += " - Playing" - - if(C.is_afk()) - msg += " (AFK)" - msg += "\n" - - num_admins_online++ - - else if(check_rights(R_MENTOR|R_MOD, 0, C.mob)) - modmsg += "\t[C] is a [C.holder.rank]" - - if(isobserver(C.mob)) - modmsg += " - Observing" - else if(istype(C.mob,/mob/new_player)) - modmsg += " - Lobby" - else - modmsg += " - Playing" - - if(C.is_afk()) - modmsg += " (AFK)" - modmsg += "\n" - num_mods_online++ - else - for(var/client/C in GLOB.admins) - - if(check_rights(R_ADMIN, 0, C.mob)) - if(!C.holder.fakekey) - msg += "\t[C] is a [C.holder.rank]\n" - num_admins_online++ - else if(check_rights(R_MOD|R_MENTOR, 0, C.mob) && !check_rights(R_ADMIN, 0, C.mob)) - modmsg += "\t[C] is a [C.holder.rank]\n" - num_mods_online++ - - msg = "Current Admins ([num_admins_online]):\n" + msg + "\nCurrent Mentors ([num_mods_online]):\n" + modmsg - to_chat(src, msg) + +/client/verb/who() + set name = "Who" + set category = "OOC" + + var/msg = "Current Players:\n" + + + var/list/Lines = list() + + if(check_rights(R_ADMIN,0)) + for(var/client/C in GLOB.clients) + if(C.holder && C.holder.big_brother && !check_rights(R_PERMISSIONS, 0)) // need PERMISSIONS to see BB + continue + + var/entry = "\t[C.key]" + if(C.holder && C.holder.fakekey) + entry += " (as [C.holder.fakekey])" + entry += " - Playing as [C.mob.real_name]" + switch(C.mob.stat) + if(UNCONSCIOUS) + entry += " - Unconscious" + if(DEAD) + if(isobserver(C.mob)) + var/mob/dead/observer/O = C.mob + if(O.started_as_observer) + entry += " - Observing" + else + entry += " - DEAD" + else if(istype(C.mob, /mob/new_player)) + entry += " - New Player" + else + entry += " - DEAD" + + var/age + if(isnum(C.player_age)) + age = C.player_age + else + age = 0 + + if(age <= 1) + age = "[age]" + else if(age < 10) + age = "[age]" + + entry += " - [age]" + + if(is_special_character(C.mob)) + entry += " - Antagonist" + entry += " ([ADMIN_QUE(C.mob,"?")])" + Lines += entry + else + for(var/client/C in GLOB.clients) + if(C.holder && C.holder.big_brother) // BB doesn't show up at all + continue + + if(C.holder && C.holder.fakekey) + Lines += C.holder.fakekey + else + Lines += C.key + + for(var/line in sortList(Lines)) + msg += "[line]\n" + + msg += "Total Players: [length(Lines)]" + to_chat(src, msg) + +/client/verb/adminwho() + set category = "Admin" + set name = "Adminwho" + + var/msg = "" + var/modmsg = "" + var/num_mods_online = 0 + var/num_admins_online = 0 + if(holder) + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN, 0, C.mob)) + + if(C.holder.fakekey && !check_rights(R_ADMIN, 0)) //Mentors can't see stealthmins + continue + + if(C.holder.big_brother && !check_rights(R_PERMISSIONS, 0)) // normal admins can't see BB + continue + + msg += "\t[C] is a [C.holder.rank]" + + if(C.holder.fakekey) + msg += " (as [C.holder.fakekey])" + + if(isobserver(C.mob)) + msg += " - Observing" + else if(istype(C.mob,/mob/new_player)) + msg += " - Lobby" + else + msg += " - Playing" + + if(C.is_afk()) + msg += " (AFK)" + msg += "\n" + + num_admins_online++ + + else if(check_rights(R_MENTOR|R_MOD, 0, C.mob)) + modmsg += "\t[C] is a [C.holder.rank]" + + if(isobserver(C.mob)) + modmsg += " - Observing" + else if(istype(C.mob,/mob/new_player)) + modmsg += " - Lobby" + else + modmsg += " - Playing" + + if(C.is_afk()) + modmsg += " (AFK)" + modmsg += "\n" + num_mods_online++ + else + for(var/client/C in GLOB.admins) + + if(check_rights(R_ADMIN, 0, C.mob)) + if(!C.holder.fakekey) + msg += "\t[C] is a [C.holder.rank]\n" + num_admins_online++ + else if(check_rights(R_MOD|R_MENTOR, 0, C.mob) && !check_rights(R_ADMIN, 0, C.mob)) + modmsg += "\t[C] is a [C.holder.rank]\n" + num_mods_online++ + + msg = "Current Admins ([num_admins_online]):\n" + msg + "\nCurrent Mentors ([num_mods_online]):\n" + modmsg + to_chat(src, msg) diff --git a/code/game/world.dm b/code/game/world.dm index cccf5eb497a6..762a0be70a8c 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,6 +1,6 @@ #define RECOMMENDED_VERSION 510 -var/global/list/map_transition_config = MAP_TRANSITION_CONFIG +GLOBAL_LIST_INIT(map_transition_config, MAP_TRANSITION_CONFIG) /world/New() SetupLogs() @@ -22,7 +22,7 @@ var/global/list/map_transition_config = MAP_TRANSITION_CONFIG src.update_status() - space_manager.initialize() //Before the MC starts up + GLOB.space_manager.initialize() //Before the MC starts up . = ..() @@ -47,8 +47,8 @@ var/global/list/map_transition_config = MAP_TRANSITION_CONFIG // to_chat(world, "End of Topic() call.") // ..() -var/world_topic_spam_protect_ip = "0.0.0.0" -var/world_topic_spam_protect_time = world.timeofday +GLOBAL_VAR_INIT(world_topic_spam_protect_ip, "0.0.0.0") +GLOBAL_VAR_INIT(world_topic_spam_protect_time, world.timeofday) /world/Topic(T, addr, master, key) log_misc("WORLD/TOPIC: \"[T]\", from:[addr], master:[master], key:[key]") @@ -72,10 +72,10 @@ var/world_topic_spam_protect_time = world.timeofday else if("status" in input) var/list/s = list() var/list/admins = list() - s["version"] = game_version - s["mode"] = master_mode - s["respawn"] = config ? abandon_allowed : 0 - s["enter"] = enter_allowed + s["version"] = GLOB.game_version + s["mode"] = GLOB.master_mode + s["respawn"] = config ? GLOB.abandon_allowed : 0 + s["enter"] = GLOB.enter_allowed s["vote"] = config.allow_vote_mode s["ai"] = config.allow_ai s["host"] = host ? host : null @@ -123,18 +123,18 @@ var/world_topic_spam_protect_time = world.timeofday else if("manifest" in input) var/list/positions = list() var/list/set_names = list( - "heads" = command_positions, - "sec" = security_positions, - "eng" = engineering_positions, - "med" = medical_positions, - "sci" = science_positions, - "car" = supply_positions, - "srv" = service_positions, - "civ" = civilian_positions, - "bot" = nonhuman_positions + "heads" = GLOB.command_positions, + "sec" = GLOB.security_positions, + "eng" = GLOB.engineering_positions, + "med" = GLOB.medical_positions, + "sci" = GLOB.science_positions, + "car" = GLOB.supply_positions, + "srv" = GLOB.service_positions, + "civ" = GLOB.civilian_positions, + "bot" = GLOB.nonhuman_positions ) - for(var/datum/data/record/t in data_core.general) + for(var/datum/data/record/t in GLOB.data_core.general) var/name = t.fields["name"] var/rank = t.fields["rank"] var/real_rank = t.fields["real_rank"] @@ -251,13 +251,13 @@ var/world_topic_spam_protect_time = world.timeofday return "Set listed status to invisible." /proc/keySpamProtect(var/addr) - if(world_topic_spam_protect_ip == addr && abs(world_topic_spam_protect_time - world.time) < 50) + if(GLOB.world_topic_spam_protect_ip == addr && abs(GLOB.world_topic_spam_protect_time - world.time) < 50) spawn(50) - world_topic_spam_protect_time = world.time + GLOB.world_topic_spam_protect_time = world.time return "Bad Key (Throttled)" - world_topic_spam_protect_time = world.time - world_topic_spam_protect_ip = addr + GLOB.world_topic_spam_protect_time = world.time + GLOB.world_topic_spam_protect_ip = addr return "Bad Key" /world/Reboot(var/reason, var/feedback_c, var/feedback_r, var/time) @@ -270,8 +270,8 @@ var/world_topic_spam_protect_time = world.timeofday shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss. if(config && config.shutdown_on_reboot) sleep(0) - if(shutdown_shell_command) - shell(shutdown_shell_command) + if(GLOB.shutdown_shell_command) + shell(GLOB.shutdown_shell_command) del(world) return else @@ -294,8 +294,8 @@ var/world_topic_spam_protect_time = world.timeofday if(!SSticker.delay_end) world << round_end_sound sleep(delay) - if(blackbox) - blackbox.save_all_data_to_sql() + if(GLOB.blackbox) + GLOB.blackbox.save_all_data_to_sql() if(SSticker.delay_end) to_chat(world, "Reboot was cancelled by an admin.") return @@ -304,7 +304,7 @@ var/world_topic_spam_protect_time = world.timeofday //kick_clients_in_lobby("The round came to an end with you in the lobby.", 1) Master.Shutdown() //run SS shutdowns - dbcon.Disconnect() // DCs cleanly from the database + GLOB.dbcon.Disconnect() // DCs cleanly from the database shutdown_logging() // Past this point, no logging procs can be used, at risk of data loss. for(var/client/C in GLOB.clients) @@ -313,8 +313,8 @@ var/world_topic_spam_protect_time = world.timeofday if(config && config.shutdown_on_reboot) sleep(0) - if(shutdown_shell_command) - shell(shutdown_shell_command) + if(GLOB.shutdown_shell_command) + shell(GLOB.shutdown_shell_command) del(world) return else @@ -329,8 +329,8 @@ var/world_topic_spam_protect_time = world.timeofday var/list/Lines = file2list("data/mode.txt") if(Lines.len) if(Lines[1]) - master_mode = Lines[1] - log_game("Saved mode is '[master_mode]'") + GLOB.master_mode = Lines[1] + log_game("Saved mode is '[GLOB.master_mode]'") /world/proc/save_mode(var/the_mode) var/F = file("data/mode.txt") @@ -342,7 +342,7 @@ var/world_topic_spam_protect_time = world.timeofday return 1 /world/proc/load_motd() - join_motd = file2text("config/motd.txt") + GLOB.join_motd = file2text("config/motd.txt") GLOB.join_tos = file2text("config/tos.txt") /proc/load_configuration() @@ -366,7 +366,7 @@ var/world_topic_spam_protect_time = world.timeofday s += "[config.server_name] — " s += "[station_name()] " if(config && config.githuburl) - s+= "([game_version])" + s+= "([GLOB.game_version])" if(config && config.server_tag_line) s += "
    [config.server_tag_line]" @@ -379,7 +379,7 @@ var/world_topic_spam_protect_time = world.timeofday s += "
    " var/list/features = list() - if(!enter_allowed) + if(!GLOB.enter_allowed) features += "closed" if(config && config.server_extra_features) @@ -391,7 +391,7 @@ var/world_topic_spam_protect_time = world.timeofday if(config && config.wikiurl) features += "Wiki" - if(abandon_allowed) + if(GLOB.abandon_allowed) features += "respawn" if(features) @@ -400,8 +400,8 @@ var/world_topic_spam_protect_time = world.timeofday return s #define FAILED_DB_CONNECTION_CUTOFF 5 -var/failed_db_connections = 0 -var/failed_old_db_connections = 0 +GLOBAL_VAR_INIT(failed_db_connections, 0) +GLOBAL_VAR_INIT(failed_old_db_connections, 0) /world/proc/SetupLogs() GLOB.log_directory = "data/logs/[time2text(world.realtime, "YYYY/MM-Month/DD-Day")]" @@ -436,11 +436,11 @@ var/failed_old_db_connections = 0 /proc/setup_database_connection() - if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore. + if(GLOB.failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to conenct anymore. return 0 - if(!dbcon) - dbcon = new() + if(!GLOB.dbcon) + GLOB.dbcon = new() var/user = sqlfdbklogin var/pass = sqlfdbkpass @@ -448,22 +448,22 @@ var/failed_old_db_connections = 0 var/address = sqladdress var/port = sqlport - dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") - . = dbcon.IsConnected() + GLOB.dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") + . = GLOB.dbcon.IsConnected() if( . ) - failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter. + GLOB.failed_db_connections = 0 //If this connection succeeded, reset the failed connections counter. else - failed_db_connections++ //If it failed, increase the failed connections counter. - log_world(dbcon.ErrorMsg()) + GLOB.failed_db_connections++ //If it failed, increase the failed connections counter. + log_world(GLOB.dbcon.ErrorMsg()) return . //This proc ensures that the connection to the feedback database (global variable dbcon) is established proc/establish_db_connection() - if(failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) + if(GLOB.failed_db_connections > FAILED_DB_CONNECTION_CUTOFF) return 0 - if(!dbcon || !dbcon.IsConnected()) + if(!GLOB.dbcon || !GLOB.dbcon.IsConnected()) return setup_database_connection() else return 1 @@ -475,4 +475,4 @@ proc/establish_db_connection() /world/proc/enable_debugger() var/dll = world.GetConfig("env", "EXTOOLS_DLL") if (dll) - call(dll, "debug_initialize")() \ No newline at end of file + call(dll, "debug_initialize")() diff --git a/code/hub.dm b/code/hub.dm index d54b49e10115..665193a9d5bf 100644 --- a/code/hub.dm +++ b/code/hub.dm @@ -1,14 +1,14 @@ -/world - - hub = "Exadv1.spacestation13" - hub_password = "kMZy3U5jJHSiBQjr" - name = "Space Station 13" - /var/hub_password_base = "kMZy3U5jJHSiBQjr" -/* This is for any host that would like their server to appear on the main SS13 hub. -To use it, simply replace the password above, with the password found below, and it should work. -If not, let us know on the main tgstation IRC channel of irc.rizon.net #tgstation13 we can help you there. - - hub = "Exadv1.spacestation13" - hub_password = "kMZy3U5jJHSiBQjr" - name = "Space Station 13" -*/ +/world + + hub = "Exadv1.spacestation13" + hub_password = "kMZy3U5jJHSiBQjr" + name = "Space Station 13" + /var/hub_password_base = "kMZy3U5jJHSiBQjr" +/* This is for any host that would like their server to appear on the main SS13 hub. +To use it, simply replace the password above, with the password found below, and it should work. +If not, let us know on the main tgstation IRC channel of irc.rizon.net #tgstation13 we can help you there. + + hub = "Exadv1.spacestation13" + hub_password = "kMZy3U5jJHSiBQjr" + name = "Space Station 13" +*/ diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 86a6b228b601..21b59586bfda 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -1,583 +1,583 @@ -#define MAX_ADMIN_BANS_PER_ADMIN 1 - -datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) - - if(!check_rights(R_BAN)) return - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/serverip = "[world.internet_address]:[world.port]" - var/bantype_pass = 0 - var/bantype_str - var/maxadminbancheck //Used to limit the number of active bans of a certein type that each admin can give. Used to protect against abuse or mutiny. - var/announceinirc //When set, it announces the ban in irc. Intended to be a way to raise an alarm, so to speak. - var/blockselfban //Used to prevent the banning of yourself. - var/kickbannedckey //Defines whether this proc should kick the banned person, if they are connected (if banned_mob is defined). - //some ban types kick players after this proc passes (tempban, permaban), but some are specific to db_ban, so - //they should kick within this proc. - var/isjobban // For job bans, which need to be inserted into the job ban lists - switch(bantype) - if(BANTYPE_PERMA) - bantype_str = "PERMABAN" - duration = -1 - bantype_pass = 1 - blockselfban = 1 - if(BANTYPE_TEMP) - bantype_str = "TEMPBAN" - bantype_pass = 1 - blockselfban = 1 - if(BANTYPE_JOB_PERMA) - bantype_str = "JOB_PERMABAN" - duration = -1 - bantype_pass = 1 - isjobban = 1 - if(BANTYPE_JOB_TEMP) - bantype_str = "JOB_TEMPBAN" - bantype_pass = 1 - isjobban = 1 - if(BANTYPE_APPEARANCE) - bantype_str = "APPEARANCE_BAN" - duration = -1 - bantype_pass = 1 - if(BANTYPE_ADMIN_PERMA) - bantype_str = "ADMIN_PERMABAN" - duration = -1 - bantype_pass = 1 - maxadminbancheck = 1 - announceinirc = 1 - blockselfban = 1 - kickbannedckey = 1 - if(BANTYPE_ADMIN_TEMP) - bantype_str = "ADMIN_TEMPBAN" - bantype_pass = 1 - maxadminbancheck = 1 - announceinirc = 1 - blockselfban = 1 - kickbannedckey = 1 - - if( !bantype_pass ) return - if( !istext(reason) ) return - if( !isnum(duration) ) return - - var/ckey - var/computerid - var/ip - - if(ismob(banned_mob) && banned_mob.ckey) - ckey = banned_mob.ckey - if(banned_mob.client) - computerid = banned_mob.client.computer_id - ip = banned_mob.client.address - else - if(banned_mob.lastKnownIP) - ip = banned_mob.lastKnownIP - if(banned_mob.computer_id) - computerid = banned_mob.computer_id - else if(banckey) - ckey = ckey(banckey) - computerid = bancid - ip = banip - else if(ismob(banned_mob)) - message_admins("[key_name_admin(usr)] attempted to add a ban based on a ckey-less mob, with no ckey provided. Report this bug.",1) - return - else - message_admins("[key_name_admin(usr)] attempted to add a ban based on a non-existent mob, with no ckey provided. Report this bug.",1) - return - - var/DBQuery/query = dbcon.NewQuery("SELECT id FROM [format_table_name("player")] WHERE ckey = '[ckey]'") - query.Execute() - var/validckey = 0 - if(query.NextRow()) - validckey = 1 - if(!validckey) - if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) - message_admins("[key_name_admin(usr)] attempted to ban [ckey], but [ckey] does not exist in the player database. Please only ban actual players.",1) - return - - var/a_ckey - var/a_computerid - var/a_ip - - if(src.owner && istype(src.owner, /client)) - a_ckey = src.owner:ckey - a_computerid = src.owner:computer_id - a_ip = src.owner:address - - if(blockselfban) - if(a_ckey == ckey) - to_chat(usr, "You cannot apply this ban type on yourself.") - return - - var/who - for(var/client/C in GLOB.clients) - if(!who) - who = "[C]" - else - who += ", [C]" - - var/adminwho - for(var/client/C in GLOB.admins) - if(!adminwho) - adminwho = "[C]" - else - adminwho += ", [C]" - - reason = sanitizeSQL(reason) - - if(maxadminbancheck) - var/DBQuery/adm_query = dbcon.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") - adm_query.Execute() - if(adm_query.NextRow()) - var/adm_bans = text2num(adm_query.item[1]) - if(adm_bans >= MAX_ADMIN_BANS_PER_ADMIN) - to_chat(usr, "You already logged [MAX_ADMIN_BANS_PER_ADMIN] admin ban(s) or more. Do not abuse this function!") - return - - var/sql = "INSERT INTO [format_table_name("ban")] (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() - to_chat(usr, "Ban saved to database.") - message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) - - if(announceinirc) - send2irc("BAN ALERT","[a_ckey] applied a [bantype_str] on [ckey]") - - if(kickbannedckey) - if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey) - del(banned_mob.client) - - if(isjobban) - jobban_client_fullban(ckey, job) - else - flag_account_for_forum_sync(ckey) - -datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") - - if(!check_rights(R_BAN)) return - - var/bantype_str - var/isjobban // For job bans, which need to be removed from the job ban lists - if(bantype) - var/bantype_pass = 0 - switch(bantype) - if(BANTYPE_PERMA) - bantype_str = "PERMABAN" - bantype_pass = 1 - if(BANTYPE_TEMP) - bantype_str = "TEMPBAN" - bantype_pass = 1 - if(BANTYPE_JOB_PERMA) - bantype_str = "JOB_PERMABAN" - bantype_pass = 1 - isjobban = 1 - if(BANTYPE_JOB_TEMP) - bantype_str = "JOB_TEMPBAN" - bantype_pass = 1 - isjobban = 1 - if(BANTYPE_APPEARANCE) - bantype_str = "APPEARANCE_BAN" - bantype_pass = 1 - if(BANTYPE_ADMIN_PERMA) - bantype_str = "ADMIN_PERMABAN" - bantype_pass = 1 - if(BANTYPE_ADMIN_TEMP) - bantype_str = "ADMIN_TEMPBAN" - bantype_pass = 1 - if(BANTYPE_ANY_FULLBAN) - bantype_str = "ANY" - bantype_pass = 1 - if( !bantype_pass ) return - - var/bantype_sql - if(bantype_str == "ANY") - bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )" - else - bantype_sql = "bantype = '[bantype_str]'" - - var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" - if(job) - sql += " AND job = '[job]'" - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/ban_id - var/ban_number = 0 //failsafe - - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() - while(query.NextRow()) - ban_id = query.item[1] - ban_number++; - - if(ban_number == 0) - to_chat(usr, "Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.") - return - - if(ban_number > 1) - to_chat(usr, "Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.") - return - - if(istext(ban_id)) - ban_id = text2num(ban_id) - if(!isnum(ban_id)) - to_chat(usr, "Database update failed due to a ban ID mismatch. Contact the database admin.") - return - - DB_ban_unban_by_id(ban_id) - if(isjobban) - jobban_unban_client(ckey, job) - else - flag_account_for_forum_sync(ckey) - -datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) - - if(!check_rights(R_BAN)) return - - if(!isnum(banid) || !istext(param)) - to_chat(usr, "Cancelled") - return - - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, duration, reason, job FROM [format_table_name("ban")] WHERE id = [banid]") - query.Execute() - - var/eckey = usr.ckey //Editing admin ckey - var/pckey //(banned) Player ckey - var/duration //Old duration - var/reason //Old reason - var/job //Old job - - if(query.NextRow()) - pckey = query.item[1] - duration = query.item[2] - reason = query.item[3] - job = query.item[4] - else - to_chat(usr, "Invalid ban id. Contact the database admin") - return - - reason = sanitizeSQL(reason) - var/value - - switch(param) - if("reason") - if(!value) - value = input("Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null) as null|text - value = sanitizeSQL(value) - if(!value) - to_chat(usr, "Cancelled") - return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
    ') WHERE id = [banid]") - update_query.Execute() - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) - if("duration") - if(!value) - value = input("Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) as null|num - if(!isnum(value) || !value) - to_chat(usr, "Cancelled") - return - - var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
    '), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") - message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) - update_query.Execute() - if("unban") - if(alert("Unban [pckey]?", "Unban?", "Yes", "No") == "Yes") - DB_ban_unban_by_id(banid) - if(job && length(job)) - jobban_unban_client(pckey, job) - return - else - to_chat(usr, "Cancelled") - return - else - to_chat(usr, "Cancelled") - return - -datum/admins/proc/DB_ban_unban_by_id(var/id) - - if(!check_rights(R_BAN)) return - - var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]" - - establish_db_connection() - if(!dbcon.IsConnected()) - return - - var/ban_number = 0 //failsafe - - var/pckey - var/DBQuery/query = dbcon.NewQuery(sql) - query.Execute() - while(query.NextRow()) - pckey = query.item[1] - ban_number++; - - if(ban_number == 0) - to_chat(usr, "Database update failed due to a ban id not being present in the database.") - return - - if(ban_number > 1) - to_chat(usr, "Database update failed due to multiple bans having the same ID. Contact the database admin.") - return - - if(!src.owner || !istype(src.owner, /client)) - return - - var/unban_ckey = src.owner:ckey - var/unban_computerid = src.owner:computer_id - var/unban_ip = src.owner:address - - var/sql_update = "UPDATE [format_table_name("ban")] SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" - message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) - - var/DBQuery/query_update = dbcon.NewQuery(sql_update) - query_update.Execute() - - flag_account_for_forum_sync(pckey) - - -/client/proc/DB_ban_panel() - set category = "Admin" - set name = "Banning Panel" - set desc = "Edit admin permissions" - - if(!holder) - return - - holder.DB_ban_panel() - - -/datum/admins/proc/DB_ban_panel(var/playerckey = null, var/adminckey = null, var/playerip = null, var/playercid = null, var/dbbantype = null, var/match = null) - - if(!usr.client) - return - - if(!check_rights(R_BAN)) return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - var/output = "
    " - - output += "" - - output += "" - output += "" - output += "
    " - output += "

    Banning panel

    " - output += "
    " - - output += "
    Add custom ban: (ONLY use this if you can't ban through any other method)" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
    Ban type:Ckey:
    IP: CID:
    Duration: Job:
    " - output += "Reason:

    " - output += "" - output += "
    " - - output += "
    " - - output += "
    " - output += "" - output += "" - output += "" - output += "" - output += "
    Search:" - output += "
    Ckey: Admin ckey:
    IP: CID:
    Ban type:
    " - output += "

    " - output += " Match(min. 3 characters to search by key or ip, and 7 to search by cid)
    " - output += "
    " - output += "This search shows only last 100 bans." - - if(adminckey || playerckey || playerip || playercid || dbbantype) - - adminckey = ckey(adminckey) - playerckey = ckey(playerckey) - playerip = sanitizeSQL(playerip) - playercid = sanitizeSQL(playercid) - - if(adminckey || playerckey || playerip || playercid || dbbantype) - - var/blcolor = "#ffeeee" //banned light - var/bdcolor = "#ffdddd" //banned dark - var/ulcolor = "#eeffee" //unbanned light - var/udcolor = "#ddffdd" //unbanned dark - - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - - var/adminsearch = "" - var/playersearch = "" - var/ipsearch = "" - var/cidsearch = "" - var/bantypesearch = "" - - if(!match) - if(adminckey) - adminsearch = "AND a_ckey = '[adminckey]' " - if(playerckey) - playersearch = "AND ckey = '[playerckey]' " - if(playerip) - ipsearch = "AND ip = '[playerip]' " - if(playercid) - cidsearch = "AND computerid = '[playercid]' " - else - if(adminckey && length(adminckey) >= 3) - adminsearch = "AND a_ckey LIKE '[adminckey]%' " - if(playerckey && length(playerckey) >= 3) - playersearch = "AND ckey LIKE '[playerckey]%' " - if(playerip && length(playerip) >= 3) - ipsearch = "AND ip LIKE '[playerip]%' " - if(playercid && length(playercid) >= 7) - cidsearch = "AND computerid LIKE '[playercid]%' " - - if(dbbantype) - bantypesearch = "AND bantype = " - - switch(dbbantype) - if(BANTYPE_TEMP) - bantypesearch += "'TEMPBAN' " - if(BANTYPE_JOB_PERMA) - bantypesearch += "'JOB_PERMABAN' " - if(BANTYPE_JOB_TEMP) - bantypesearch += "'JOB_TEMPBAN' " - if(BANTYPE_APPEARANCE) - bantypesearch += "'APPEARANCE_BAN' " - if(BANTYPE_ADMIN_PERMA) - bantypesearch = "'ADMIN_PERMABAN' " - if(BANTYPE_ADMIN_TEMP) - bantypesearch = "'ADMIN_TEMPBAN' " - else - bantypesearch += "'PERMABAN' " - - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM [format_table_name("ban")] WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") - select_query.Execute() - - while(select_query.NextRow()) - var/banid = select_query.item[1] - var/bantime = select_query.item[2] - var/bantype = select_query.item[3] - var/reason = select_query.item[4] - var/job = select_query.item[5] - var/duration = select_query.item[6] - var/expiration = select_query.item[7] - var/ckey = select_query.item[8] - var/ackey = select_query.item[9] - var/unbanned = select_query.item[10] - var/unbanckey = select_query.item[11] - var/unbantime = select_query.item[12] - var/edits = select_query.item[13] - var/ip = select_query.item[14] - var/cid = select_query.item[15] - - var/lcolor = blcolor - var/dcolor = bdcolor - if(unbanned) - lcolor = ulcolor - dcolor = udcolor - - var/typedesc ="" - switch(bantype) - if("PERMABAN") - typedesc = "PERMABAN" - if("TEMPBAN") - typedesc = "TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " - if("JOB_PERMABAN") - typedesc = "JOBBAN
    ([job])" - if("JOB_TEMPBAN") - typedesc = "TEMP JOBBAN
    ([job])
    ([duration] minutes
    Expires [expiration]" - if("APPEARANCE_BAN") - typedesc = "APPEARANCE/NAME BAN" - if("ADMIN_PERMABAN") - typedesc = "ADMIN PERMABAN" - if("ADMIN_TEMPBAN") - typedesc = "ADMIN TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " - - output += "
    " - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - if(edits) - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - if(unbanned) - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - - output += "
    TYPECKEYTIME APPLIEDADMINOPTIONS
    [typedesc][ckey][bantime][ackey][(unbanned) ? "" : "Unban"]
    IP: [ip]CIP: [cid]
    Reason: [(unbanned) ? "" : "(Edit)"] \"[reason]\"
    EDITS
    [edits]
    UNBANNED by admin [unbanckey] on [unbantime]
     
    " - - usr << browse(output,"window=lookupbans;size=900x700") - -/proc/flag_account_for_forum_sync(ckey) - if(!dbcon) - return - var/skey = sanitizeSQL(ckey) - var/sql = "UPDATE [format_table_name("player")] SET fupdate = 1 WHERE ckey = '[skey]'" - var/DBQuery/adm_query = dbcon.NewQuery(sql) - adm_query.Execute() +#define MAX_ADMIN_BANS_PER_ADMIN 1 + +datum/admins/proc/DB_ban_record(var/bantype, var/mob/banned_mob, var/duration = -1, var/reason, var/job = "", var/rounds = 0, var/banckey = null, var/banip = null, var/bancid = null) + + if(!check_rights(R_BAN)) return + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + return + + var/serverip = "[world.internet_address]:[world.port]" + var/bantype_pass = 0 + var/bantype_str + var/maxadminbancheck //Used to limit the number of active bans of a certein type that each admin can give. Used to protect against abuse or mutiny. + var/announceinirc //When set, it announces the ban in irc. Intended to be a way to raise an alarm, so to speak. + var/blockselfban //Used to prevent the banning of yourself. + var/kickbannedckey //Defines whether this proc should kick the banned person, if they are connected (if banned_mob is defined). + //some ban types kick players after this proc passes (tempban, permaban), but some are specific to db_ban, so + //they should kick within this proc. + var/isjobban // For job bans, which need to be inserted into the job ban lists + switch(bantype) + if(BANTYPE_PERMA) + bantype_str = "PERMABAN" + duration = -1 + bantype_pass = 1 + blockselfban = 1 + if(BANTYPE_TEMP) + bantype_str = "TEMPBAN" + bantype_pass = 1 + blockselfban = 1 + if(BANTYPE_JOB_PERMA) + bantype_str = "JOB_PERMABAN" + duration = -1 + bantype_pass = 1 + isjobban = 1 + if(BANTYPE_JOB_TEMP) + bantype_str = "JOB_TEMPBAN" + bantype_pass = 1 + isjobban = 1 + if(BANTYPE_APPEARANCE) + bantype_str = "APPEARANCE_BAN" + duration = -1 + bantype_pass = 1 + if(BANTYPE_ADMIN_PERMA) + bantype_str = "ADMIN_PERMABAN" + duration = -1 + bantype_pass = 1 + maxadminbancheck = 1 + announceinirc = 1 + blockselfban = 1 + kickbannedckey = 1 + if(BANTYPE_ADMIN_TEMP) + bantype_str = "ADMIN_TEMPBAN" + bantype_pass = 1 + maxadminbancheck = 1 + announceinirc = 1 + blockselfban = 1 + kickbannedckey = 1 + + if( !bantype_pass ) return + if( !istext(reason) ) return + if( !isnum(duration) ) return + + var/ckey + var/computerid + var/ip + + if(ismob(banned_mob) && banned_mob.ckey) + ckey = banned_mob.ckey + if(banned_mob.client) + computerid = banned_mob.client.computer_id + ip = banned_mob.client.address + else + if(banned_mob.lastKnownIP) + ip = banned_mob.lastKnownIP + if(banned_mob.computer_id) + computerid = banned_mob.computer_id + else if(banckey) + ckey = ckey(banckey) + computerid = bancid + ip = banip + else if(ismob(banned_mob)) + message_admins("[key_name_admin(usr)] attempted to add a ban based on a ckey-less mob, with no ckey provided. Report this bug.",1) + return + else + message_admins("[key_name_admin(usr)] attempted to add a ban based on a non-existent mob, with no ckey provided. Report this bug.",1) + return + + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + query.Execute() + var/validckey = 0 + if(query.NextRow()) + validckey = 1 + if(!validckey) + if(!banned_mob || (banned_mob && !IsGuestKey(banned_mob.key))) + message_admins("[key_name_admin(usr)] attempted to ban [ckey], but [ckey] does not exist in the player database. Please only ban actual players.",1) + return + + var/a_ckey + var/a_computerid + var/a_ip + + if(src.owner && istype(src.owner, /client)) + a_ckey = src.owner:ckey + a_computerid = src.owner:computer_id + a_ip = src.owner:address + + if(blockselfban) + if(a_ckey == ckey) + to_chat(usr, "You cannot apply this ban type on yourself.") + return + + var/who + for(var/client/C in GLOB.clients) + if(!who) + who = "[C]" + else + who += ", [C]" + + var/adminwho + for(var/client/C in GLOB.admins) + if(!adminwho) + adminwho = "[C]" + else + adminwho += ", [C]" + + reason = sanitizeSQL(reason) + + if(maxadminbancheck) + var/DBQuery/adm_query = GLOB.dbcon.NewQuery("SELECT count(id) AS num FROM [format_table_name("ban")] WHERE (a_ckey = '[a_ckey]') AND (bantype = 'ADMIN_PERMABAN' OR (bantype = 'ADMIN_TEMPBAN' AND expiration_time > Now())) AND isnull(unbanned)") + adm_query.Execute() + if(adm_query.NextRow()) + var/adm_bans = text2num(adm_query.item[1]) + if(adm_bans >= MAX_ADMIN_BANS_PER_ADMIN) + to_chat(usr, "You already logged [MAX_ADMIN_BANS_PER_ADMIN] admin ban(s) or more. Do not abuse this function!") + return + + var/sql = "INSERT INTO [format_table_name("ban")] (`id`,`bantime`,`serverip`,`bantype`,`reason`,`job`,`duration`,`rounds`,`expiration_time`,`ckey`,`computerid`,`ip`,`a_ckey`,`a_computerid`,`a_ip`,`who`,`adminwho`,`edits`,`unbanned`,`unbanned_datetime`,`unbanned_ckey`,`unbanned_computerid`,`unbanned_ip`) VALUES (null, Now(), '[serverip]', '[bantype_str]', '[reason]', '[job]', [(duration)?"[duration]":"0"], [(rounds)?"[rounds]":"0"], Now() + INTERVAL [(duration>0) ? duration : 0] MINUTE, '[ckey]', '[computerid]', '[ip]', '[a_ckey]', '[a_computerid]', '[a_ip]', '[who]', '[adminwho]', '', null, null, null, null, null)" + var/DBQuery/query_insert = GLOB.dbcon.NewQuery(sql) + query_insert.Execute() + to_chat(usr, "Ban saved to database.") + message_admins("[key_name_admin(usr)] has added a [bantype_str] for [ckey] [(job)?"([job])":""] [(duration > 0)?"([duration] minutes)":""] with the reason: \"[reason]\" to the ban database.",1) + + if(announceinirc) + send2irc("BAN ALERT","[a_ckey] applied a [bantype_str] on [ckey]") + + if(kickbannedckey) + if(banned_mob && banned_mob.client && banned_mob.client.ckey == banckey) + del(banned_mob.client) + + if(isjobban) + jobban_client_fullban(ckey, job) + else + flag_account_for_forum_sync(ckey) + +datum/admins/proc/DB_ban_unban(var/ckey, var/bantype, var/job = "") + + if(!check_rights(R_BAN)) return + + var/bantype_str + var/isjobban // For job bans, which need to be removed from the job ban lists + if(bantype) + var/bantype_pass = 0 + switch(bantype) + if(BANTYPE_PERMA) + bantype_str = "PERMABAN" + bantype_pass = 1 + if(BANTYPE_TEMP) + bantype_str = "TEMPBAN" + bantype_pass = 1 + if(BANTYPE_JOB_PERMA) + bantype_str = "JOB_PERMABAN" + bantype_pass = 1 + isjobban = 1 + if(BANTYPE_JOB_TEMP) + bantype_str = "JOB_TEMPBAN" + bantype_pass = 1 + isjobban = 1 + if(BANTYPE_APPEARANCE) + bantype_str = "APPEARANCE_BAN" + bantype_pass = 1 + if(BANTYPE_ADMIN_PERMA) + bantype_str = "ADMIN_PERMABAN" + bantype_pass = 1 + if(BANTYPE_ADMIN_TEMP) + bantype_str = "ADMIN_TEMPBAN" + bantype_pass = 1 + if(BANTYPE_ANY_FULLBAN) + bantype_str = "ANY" + bantype_pass = 1 + if( !bantype_pass ) return + + var/bantype_sql + if(bantype_str == "ANY") + bantype_sql = "(bantype = 'PERMABAN' OR (bantype = 'TEMPBAN' AND expiration_time > Now() ) )" + else + bantype_sql = "bantype = '[bantype_str]'" + + var/sql = "SELECT id FROM [format_table_name("ban")] WHERE ckey = '[ckey]' AND [bantype_sql] AND (unbanned is null OR unbanned = false)" + if(job) + sql += " AND job = '[job]'" + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + return + + var/ban_id + var/ban_number = 0 //failsafe + + var/DBQuery/query = GLOB.dbcon.NewQuery(sql) + query.Execute() + while(query.NextRow()) + ban_id = query.item[1] + ban_number++; + + if(ban_number == 0) + to_chat(usr, "Database update failed due to no bans fitting the search criteria. If this is not a legacy ban you should contact the database admin.") + return + + if(ban_number > 1) + to_chat(usr, "Database update failed due to multiple bans fitting the search criteria. Note down the ckey, job and current time and contact the database admin.") + return + + if(istext(ban_id)) + ban_id = text2num(ban_id) + if(!isnum(ban_id)) + to_chat(usr, "Database update failed due to a ban ID mismatch. Contact the database admin.") + return + + DB_ban_unban_by_id(ban_id) + if(isjobban) + jobban_unban_client(ckey, job) + else + flag_account_for_forum_sync(ckey) + +datum/admins/proc/DB_ban_edit(var/banid = null, var/param = null) + + if(!check_rights(R_BAN)) return + + if(!isnum(banid) || !istext(param)) + to_chat(usr, "Cancelled") + return + + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT ckey, duration, reason, job FROM [format_table_name("ban")] WHERE id = [banid]") + query.Execute() + + var/eckey = usr.ckey //Editing admin ckey + var/pckey //(banned) Player ckey + var/duration //Old duration + var/reason //Old reason + var/job //Old job + + if(query.NextRow()) + pckey = query.item[1] + duration = query.item[2] + reason = query.item[3] + job = query.item[4] + else + to_chat(usr, "Invalid ban id. Contact the database admin") + return + + reason = sanitizeSQL(reason) + var/value + + switch(param) + if("reason") + if(!value) + value = input("Insert the new reason for [pckey]'s ban", "New Reason", "[reason]", null) as null|text + value = sanitizeSQL(value) + if(!value) + to_chat(usr, "Cancelled") + return + + var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("ban")] SET reason = '[value]', edits = CONCAT(edits,'- [eckey] changed ban reason from \\\"[reason]\\\" to \\\"[value]\\\"
    ') WHERE id = [banid]") + update_query.Execute() + message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s reason from [reason] to [value]",1) + if("duration") + if(!value) + value = input("Insert the new duration (in minutes) for [pckey]'s ban", "New Duration", "[duration]", null) as null|num + if(!isnum(value) || !value) + to_chat(usr, "Cancelled") + return + + var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("ban")] SET duration = [value], edits = CONCAT(edits,'- [eckey] changed ban duration from [duration] to [value]
    '), expiration_time = DATE_ADD(bantime, INTERVAL [value] MINUTE) WHERE id = [banid]") + message_admins("[key_name_admin(usr)] has edited a ban for [pckey]'s duration from [duration] to [value]",1) + update_query.Execute() + if("unban") + if(alert("Unban [pckey]?", "Unban?", "Yes", "No") == "Yes") + DB_ban_unban_by_id(banid) + if(job && length(job)) + jobban_unban_client(pckey, job) + return + else + to_chat(usr, "Cancelled") + return + else + to_chat(usr, "Cancelled") + return + +datum/admins/proc/DB_ban_unban_by_id(var/id) + + if(!check_rights(R_BAN)) return + + var/sql = "SELECT ckey FROM [format_table_name("ban")] WHERE id = [id]" + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + return + + var/ban_number = 0 //failsafe + + var/pckey + var/DBQuery/query = GLOB.dbcon.NewQuery(sql) + query.Execute() + while(query.NextRow()) + pckey = query.item[1] + ban_number++; + + if(ban_number == 0) + to_chat(usr, "Database update failed due to a ban id not being present in the database.") + return + + if(ban_number > 1) + to_chat(usr, "Database update failed due to multiple bans having the same ID. Contact the database admin.") + return + + if(!src.owner || !istype(src.owner, /client)) + return + + var/unban_ckey = src.owner:ckey + var/unban_computerid = src.owner:computer_id + var/unban_ip = src.owner:address + + var/sql_update = "UPDATE [format_table_name("ban")] SET unbanned = 1, unbanned_datetime = Now(), unbanned_ckey = '[unban_ckey]', unbanned_computerid = '[unban_computerid]', unbanned_ip = '[unban_ip]' WHERE id = [id]" + message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.",1) + + var/DBQuery/query_update = GLOB.dbcon.NewQuery(sql_update) + query_update.Execute() + + flag_account_for_forum_sync(pckey) + + +/client/proc/DB_ban_panel() + set category = "Admin" + set name = "Banning Panel" + set desc = "Edit admin permissions" + + if(!holder) + return + + holder.DB_ban_panel() + + +/datum/admins/proc/DB_ban_panel(var/playerckey = null, var/adminckey = null, var/playerip = null, var/playercid = null, var/dbbantype = null, var/match = null) + + if(!usr.client) + return + + if(!check_rights(R_BAN)) return + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + var/output = "
    " + + output += "" + + output += "" + output += "" + output += "
    " + output += "

    Banning panel

    " + output += "
    " + + output += "
    Add custom ban: (ONLY use this if you can't ban through any other method)" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "
    Ban type:Ckey:
    IP: CID:
    Duration: Job:
    " + output += "Reason:

    " + output += "" + output += "
    " + + output += "
    " + + output += "
    " + output += "" + output += "" + output += "" + output += "" + output += "
    Search:" + output += "
    Ckey: Admin ckey:
    IP: CID:
    Ban type:
    " + output += "

    " + output += " Match(min. 3 characters to search by key or ip, and 7 to search by cid)
    " + output += "
    " + output += "This search shows only last 100 bans." + + if(adminckey || playerckey || playerip || playercid || dbbantype) + + adminckey = ckey(adminckey) + playerckey = ckey(playerckey) + playerip = sanitizeSQL(playerip) + playercid = sanitizeSQL(playercid) + + if(adminckey || playerckey || playerip || playercid || dbbantype) + + var/blcolor = "#ffeeee" //banned light + var/bdcolor = "#ffdddd" //banned dark + var/ulcolor = "#eeffee" //unbanned light + var/udcolor = "#ddffdd" //unbanned dark + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + var/adminsearch = "" + var/playersearch = "" + var/ipsearch = "" + var/cidsearch = "" + var/bantypesearch = "" + + if(!match) + if(adminckey) + adminsearch = "AND a_ckey = '[adminckey]' " + if(playerckey) + playersearch = "AND ckey = '[playerckey]' " + if(playerip) + ipsearch = "AND ip = '[playerip]' " + if(playercid) + cidsearch = "AND computerid = '[playercid]' " + else + if(adminckey && length(adminckey) >= 3) + adminsearch = "AND a_ckey LIKE '[adminckey]%' " + if(playerckey && length(playerckey) >= 3) + playersearch = "AND ckey LIKE '[playerckey]%' " + if(playerip && length(playerip) >= 3) + ipsearch = "AND ip LIKE '[playerip]%' " + if(playercid && length(playercid) >= 7) + cidsearch = "AND computerid LIKE '[playercid]%' " + + if(dbbantype) + bantypesearch = "AND bantype = " + + switch(dbbantype) + if(BANTYPE_TEMP) + bantypesearch += "'TEMPBAN' " + if(BANTYPE_JOB_PERMA) + bantypesearch += "'JOB_PERMABAN' " + if(BANTYPE_JOB_TEMP) + bantypesearch += "'JOB_TEMPBAN' " + if(BANTYPE_APPEARANCE) + bantypesearch += "'APPEARANCE_BAN' " + if(BANTYPE_ADMIN_PERMA) + bantypesearch = "'ADMIN_PERMABAN' " + if(BANTYPE_ADMIN_TEMP) + bantypesearch = "'ADMIN_TEMPBAN' " + else + bantypesearch += "'PERMABAN' " + + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id, bantime, bantype, reason, job, duration, expiration_time, ckey, a_ckey, unbanned, unbanned_ckey, unbanned_datetime, edits, ip, computerid FROM [format_table_name("ban")] WHERE 1 [playersearch] [adminsearch] [ipsearch] [cidsearch] [bantypesearch] ORDER BY bantime DESC LIMIT 100") + select_query.Execute() + + while(select_query.NextRow()) + var/banid = select_query.item[1] + var/bantime = select_query.item[2] + var/bantype = select_query.item[3] + var/reason = select_query.item[4] + var/job = select_query.item[5] + var/duration = select_query.item[6] + var/expiration = select_query.item[7] + var/ckey = select_query.item[8] + var/ackey = select_query.item[9] + var/unbanned = select_query.item[10] + var/unbanckey = select_query.item[11] + var/unbantime = select_query.item[12] + var/edits = select_query.item[13] + var/ip = select_query.item[14] + var/cid = select_query.item[15] + + var/lcolor = blcolor + var/dcolor = bdcolor + if(unbanned) + lcolor = ulcolor + dcolor = udcolor + + var/typedesc ="" + switch(bantype) + if("PERMABAN") + typedesc = "PERMABAN" + if("TEMPBAN") + typedesc = "TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " + if("JOB_PERMABAN") + typedesc = "JOBBAN
    ([job])" + if("JOB_TEMPBAN") + typedesc = "TEMP JOBBAN
    ([job])
    ([duration] minutes
    Expires [expiration]" + if("APPEARANCE_BAN") + typedesc = "APPEARANCE/NAME BAN" + if("ADMIN_PERMABAN") + typedesc = "ADMIN PERMABAN" + if("ADMIN_TEMPBAN") + typedesc = "ADMIN TEMPBAN
    ([duration] minutes [(unbanned) ? "" : "(Edit))"]
    Expires [expiration]
    " + + output += "
    " + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + if(edits) + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + if(unbanned) + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + output += "
    TYPECKEYTIME APPLIEDADMINOPTIONS
    [typedesc][ckey][bantime][ackey][(unbanned) ? "" : "Unban"]
    IP: [ip]CIP: [cid]
    Reason: [(unbanned) ? "" : "(Edit)"] \"[reason]\"
    EDITS
    [edits]
    UNBANNED by admin [unbanckey] on [unbantime]
     
    " + + usr << browse(output,"window=lookupbans;size=900x700") + +/proc/flag_account_for_forum_sync(ckey) + if(!GLOB.dbcon) + return + var/skey = sanitizeSQL(ckey) + var/sql = "UPDATE [format_table_name("player")] SET fupdate = 1 WHERE ckey = '[skey]'" + var/DBQuery/adm_query = GLOB.dbcon.NewQuery(sql) + adm_query.Execute() diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index d776b4a20957..c7f2baac4ef0 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -25,13 +25,13 @@ world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE) if (C && ckey == C.ckey && computer_id == C.computer_id && address == C.address) return //don't recheck connected clients. - if((ckey in admin_datums) || (ckey in GLOB.deadmins)) - var/datum/admins/A = admin_datums[ckey] + if((ckey in GLOB.admin_datums) || (ckey in GLOB.deadmins)) + var/datum/admins/A = GLOB.admin_datums[ckey] if(A && (A.rights & R_ADMIN)) admin = 1 //Guest Checking - if(!guests_allowed && IsGuestKey(key)) + if(!GLOB.guests_allowed && IsGuestKey(key)) log_adminwarn("Failed Login: [key] [computer_id] [address] - Guests not allowed") // message_admins("Failed Login: [key] - Guests not allowed") return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a BYOND account.") @@ -82,7 +82,7 @@ world/IsBanned(key, address, computer_id, type, check_ipintel = TRUE) if(computer_id) cidquery = " OR computerid = '[computer_id]' " - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT ckey, ip, computerid, a_ckey, reason, expiration_time, duration, bantime, bantype FROM [format_table_name("ban")] WHERE (ckey = '[ckeytext]' [ipquery] [cidquery]) AND (bantype = 'PERMABAN' OR bantype = 'ADMIN_PERMABAN' OR ((bantype = 'TEMPBAN' OR bantype = 'ADMIN_TEMPBAN') AND expiration_time > Now())) AND isnull(unbanned)") query.Execute() diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 2e8fa4ba04bc..7b9a9330704f 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -1,232 +1,232 @@ -var/CMinutes = null -var/savefile/Banlist - - -/proc/CheckBan(var/ckey, var/id, var/address) - if(!Banlist) // if Banlist cannot be located for some reason - LoadBans() // try to load the bans - if(!Banlist) // uh oh, can't find bans! - return 0 // ABORT ABORT ABORT - - . = list() - var/appeal - if(config && config.banappeals) - appeal = "\nFor more information on your ban, or to appeal, head to [config.banappeals]" - Banlist.cd = "/base" - if( "[ckey][id]" in Banlist.dir ) - Banlist.cd = "[ckey][id]" - if(Banlist["temp"]) - if(!GetExp(Banlist["minutes"])) - ClearTempbans() - return 0 - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" - else - Banlist.cd = "/base/[ckey][id]" - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMENANT\nBy: [Banlist["bannedby"]][appeal]" - .["reason"] = "ckey/id" - return . - else - for(var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - var/matches - if( ckey == Banlist["key"] ) - matches += "ckey" - if( id == Banlist["id"] ) - if(matches) - matches += "/" - matches += "id" - if( address == Banlist["ip"] ) - if(matches) - matches += "/" - matches += "ip" - - if(matches) - if(Banlist["temp"]) - if(!GetExp(Banlist["minutes"])) - ClearTempbans() - return 0 - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: [GetExp(Banlist["minutes"])]\nBy: [Banlist["bannedby"]][appeal]" - else - .["desc"] = "\nReason: [Banlist["reason"]]\nExpires: PERMENANT\nBy: [Banlist["bannedby"]][appeal]" - .["reason"] = matches - return . - return 0 - -/proc/UpdateTime() //No idea why i made this a proc. - CMinutes = (world.realtime / 10) / 60 - return 1 - -/hook/startup/proc/loadBans() - return LoadBans() - -/proc/LoadBans() - - Banlist = new("data/banlist.bdb") - log_admin("Loading Banlist") - - if(!length(Banlist.dir)) log_admin("Banlist is empty.") - - if(!Banlist.dir.Find("base")) - log_admin("Banlist missing base dir.") - Banlist.dir.Add("base") - Banlist.cd = "/base" - else if(Banlist.dir.Find("base")) - Banlist.cd = "/base" - - ClearTempbans() - return 1 - -/proc/ClearTempbans() - UpdateTime() - - Banlist.cd = "/base" - for(var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - if(!Banlist["key"] || !Banlist["id"]) - RemoveBan(A) - log_admin("Invalid Ban.") - message_admins("Invalid Ban.") - continue - - if(!Banlist["temp"]) continue - if(CMinutes >= Banlist["minutes"]) RemoveBan(A) - - return 1 - - -/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address) - - var/bantimestamp - - if(temp) - UpdateTime() - bantimestamp = CMinutes + minutes - - Banlist.cd = "/base" - if( Banlist.dir.Find("[ckey][computerid]") ) - to_chat(usr, "Ban already exists.") - return 0 - else - Banlist.dir.Add("[ckey][computerid]") - Banlist.cd = "/base/[ckey][computerid]" - Banlist["key"] << ckey - Banlist["id"] << computerid - Banlist["ip"] << address - Banlist["reason"] << reason - Banlist["bannedby"] << bannedby - Banlist["temp"] << temp - if(temp) - Banlist["minutes"] << bantimestamp - if(!temp) - add_note(ckey, "Permanently banned - [reason]", null, bannedby, 0) - else - add_note(ckey, "Banned for [minutes] minutes - [reason]", null, bannedby, 0) - return 1 - -/proc/RemoveBan(foldername) - var/key - var/id - - Banlist.cd = "/base/[foldername]" - Banlist["key"] >> key - Banlist["id"] >> id - Banlist.cd = "/base" - - if(!Banlist.dir.Remove(foldername)) return 0 - - if(!usr) - log_admin("Ban Expired: [key]") - message_admins("Ban Expired: [key]") - else - ban_unban_log_save("[key_name_admin(usr)] unbanned [key]") - log_admin("[key_name_admin(usr)] unbanned [key]") - message_admins("[key_name_admin(usr)] unbanned: [key]") - feedback_inc("ban_unban",1) - usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN) - for(var/A in Banlist.dir) - Banlist.cd = "/base/[A]" - if(key == Banlist["key"] /*|| id == Banlist["id"]*/) - Banlist.cd = "/base" - Banlist.dir.Remove(A) - continue - - return 1 - -/proc/GetExp(minutes as num) - UpdateTime() - var/exp = minutes - CMinutes - if(exp <= 0) - return 0 - else - var/timeleftstring - if(exp >= 1440) //1440 = 1 day in minutes - timeleftstring = "[round(exp / 1440, 0.1)] Days" - else if(exp >= 60) //60 = 1 hour in minutes - timeleftstring = "[round(exp / 60, 0.1)] Hours" - else - timeleftstring = "[exp] Minutes" - return timeleftstring - -/datum/admins/proc/unbanpanel() - var/count = 0 - var/dat - Banlist.cd = "/base" - for(var/A in Banlist.dir) - count++ - Banlist.cd = "/base/[A]" - var/ref = UID() - var/key = Banlist["key"] - var/id = Banlist["id"] - var/ip = Banlist["ip"] - var/reason = Banlist["reason"] - var/by = Banlist["bannedby"] - var/expiry - if(Banlist["temp"]) - expiry = GetExp(Banlist["minutes"]) - if(!expiry) expiry = "Removal Pending" - else expiry = "Permaban" - - dat += text("(U)(E) Key: [key]ComputerID: [id]IP: [ip] [expiry](By: [by])(Reason: [reason])") - - dat += "" - dat = "
    Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
    [dat]" - usr << browse(dat, "window=unbanp;size=875x400") - -//////////////////////////////////// DEBUG //////////////////////////////////// - -/proc/CreateBans() - - UpdateTime() - - var/i - var/last - - for(i=0, i<1001, i++) - var/a = pick(1,0) - var/b = pick(1,0) - if(b) - Banlist.cd = "/base" - Banlist.dir.Add("trash[i]trashid[i]") - Banlist.cd = "/base/trash[i]trashid[i]" - Banlist["key"] << "trash[i]" - else - Banlist.cd = "/base" - Banlist.dir.Add("[last]trashid[i]") - Banlist.cd = "/base/[last]trashid[i]" - Banlist["key"] << last - Banlist["id"] << "trashid[i]" - Banlist["reason"] << "Trashban[i]." - Banlist["temp"] << a - Banlist["minutes"] << CMinutes + rand(1,2000) - Banlist["bannedby"] << "trashmin" - last = "trash[i]" - - Banlist.cd = "/base" - -/proc/ClearAllBans() - Banlist.cd = "/base" - for(var/A in Banlist.dir) - RemoveBan(A) - +GLOBAL_VAR(CMinutes) +GLOBAL_DATUM(banlist_savefile, /savefile) +GLOBAL_PROTECT(banlist_savefile) // Obvious reasons + +/proc/CheckBan(var/ckey, var/id, var/address) + if(!GLOB.banlist_savefile) // if banlist_savefile cannot be located for some reason + LoadBans() // try to load the bans + if(!GLOB.banlist_savefile) // uh oh, can't find bans! + return 0 // ABORT ABORT ABORT + + . = list() + var/appeal + if(config && config.banappeals) + appeal = "\nFor more information on your ban, or to appeal, head to [config.banappeals]" + GLOB.banlist_savefile.cd = "/base" + if( "[ckey][id]" in GLOB.banlist_savefile.dir ) + GLOB.banlist_savefile.cd = "[ckey][id]" + if(GLOB.banlist_savefile["temp"]) + if(!GetExp(GLOB.banlist_savefile["minutes"])) + ClearTempbans() + return 0 + else + .["desc"] = "\nReason: [GLOB.banlist_savefile["reason"]]\nExpires: [GetExp(GLOB.banlist_savefile["minutes"])]\nBy: [GLOB.banlist_savefile["bannedby"]][appeal]" + else + GLOB.banlist_savefile.cd = "/base/[ckey][id]" + .["desc"] = "\nReason: [GLOB.banlist_savefile["reason"]]\nExpires: PERMENANT\nBy: [GLOB.banlist_savefile["bannedby"]][appeal]" + .["reason"] = "ckey/id" + return . + else + for(var/A in GLOB.banlist_savefile.dir) + GLOB.banlist_savefile.cd = "/base/[A]" + var/matches + if( ckey == GLOB.banlist_savefile["key"] ) + matches += "ckey" + if( id == GLOB.banlist_savefile["id"] ) + if(matches) + matches += "/" + matches += "id" + if( address == GLOB.banlist_savefile["ip"] ) + if(matches) + matches += "/" + matches += "ip" + + if(matches) + if(GLOB.banlist_savefile["temp"]) + if(!GetExp(GLOB.banlist_savefile["minutes"])) + ClearTempbans() + return 0 + else + .["desc"] = "\nReason: [GLOB.banlist_savefile["reason"]]\nExpires: [GetExp(GLOB.banlist_savefile["minutes"])]\nBy: [GLOB.banlist_savefile["bannedby"]][appeal]" + else + .["desc"] = "\nReason: [GLOB.banlist_savefile["reason"]]\nExpires: PERMENANT\nBy: [GLOB.banlist_savefile["bannedby"]][appeal]" + .["reason"] = matches + return . + return 0 + +/proc/UpdateTime() //No idea why i made this a proc. + GLOB.CMinutes = (world.realtime / 10) / 60 + return 1 + +/hook/startup/proc/loadBans() + return LoadBans() + +/proc/LoadBans() + + GLOB.banlist_savefile = new("data/banlist.bdb") + log_admin("Loading Banlist") + + if(!length(GLOB.banlist_savefile.dir)) log_admin("Banlist is empty.") + + if(!GLOB.banlist_savefile.dir.Find("base")) + log_admin("Banlist missing base dir.") + GLOB.banlist_savefile.dir.Add("base") + GLOB.banlist_savefile.cd = "/base" + else if(GLOB.banlist_savefile.dir.Find("base")) + GLOB.banlist_savefile.cd = "/base" + + ClearTempbans() + return 1 + +/proc/ClearTempbans() + UpdateTime() + + GLOB.banlist_savefile.cd = "/base" + for(var/A in GLOB.banlist_savefile.dir) + GLOB.banlist_savefile.cd = "/base/[A]" + if(!GLOB.banlist_savefile["key"] || !GLOB.banlist_savefile["id"]) + RemoveBan(A) + log_admin("Invalid Ban.") + message_admins("Invalid Ban.") + continue + + if(!GLOB.banlist_savefile["temp"]) continue + if(GLOB.CMinutes >= GLOB.banlist_savefile["minutes"]) RemoveBan(A) + + return 1 + + +/proc/AddBan(ckey, computerid, reason, bannedby, temp, minutes, address) + + var/bantimestamp + + if(temp) + UpdateTime() + bantimestamp = GLOB.CMinutes + minutes + + GLOB.banlist_savefile.cd = "/base" + if( GLOB.banlist_savefile.dir.Find("[ckey][computerid]") ) + to_chat(usr, "Ban already exists.") + return 0 + else + GLOB.banlist_savefile.dir.Add("[ckey][computerid]") + GLOB.banlist_savefile.cd = "/base/[ckey][computerid]" + GLOB.banlist_savefile["key"] << ckey + GLOB.banlist_savefile["id"] << computerid + GLOB.banlist_savefile["ip"] << address + GLOB.banlist_savefile["reason"] << reason + GLOB.banlist_savefile["bannedby"] << bannedby + GLOB.banlist_savefile["temp"] << temp + if(temp) + GLOB.banlist_savefile["minutes"] << bantimestamp + if(!temp) + add_note(ckey, "Permanently banned - [reason]", null, bannedby, 0) + else + add_note(ckey, "Banned for [minutes] minutes - [reason]", null, bannedby, 0) + return 1 + +/proc/RemoveBan(foldername) + var/key + var/id + + GLOB.banlist_savefile.cd = "/base/[foldername]" + GLOB.banlist_savefile["key"] >> key + GLOB.banlist_savefile["id"] >> id + GLOB.banlist_savefile.cd = "/base" + + if(!GLOB.banlist_savefile.dir.Remove(foldername)) return 0 + + if(!usr) + log_admin("Ban Expired: [key]") + message_admins("Ban Expired: [key]") + else + ban_unban_log_save("[key_name_admin(usr)] unbanned [key]") + log_admin("[key_name_admin(usr)] unbanned [key]") + message_admins("[key_name_admin(usr)] unbanned: [key]") + feedback_inc("ban_unban",1) + usr.client.holder.DB_ban_unban( ckey(key), BANTYPE_ANY_FULLBAN) + for(var/A in GLOB.banlist_savefile.dir) + GLOB.banlist_savefile.cd = "/base/[A]" + if(key == GLOB.banlist_savefile["key"] /*|| id == GLOB.banlist_savefile["id"]*/) + GLOB.banlist_savefile.cd = "/base" + GLOB.banlist_savefile.dir.Remove(A) + continue + + return 1 + +/proc/GetExp(minutes as num) + UpdateTime() + var/exp = minutes - GLOB.CMinutes + if(exp <= 0) + return 0 + else + var/timeleftstring + if(exp >= 1440) //1440 = 1 day in minutes + timeleftstring = "[round(exp / 1440, 0.1)] Days" + else if(exp >= 60) //60 = 1 hour in minutes + timeleftstring = "[round(exp / 60, 0.1)] Hours" + else + timeleftstring = "[exp] Minutes" + return timeleftstring + +/datum/admins/proc/unbanpanel() + var/count = 0 + var/dat + GLOB.banlist_savefile.cd = "/base" + for(var/A in GLOB.banlist_savefile.dir) + count++ + GLOB.banlist_savefile.cd = "/base/[A]" + var/ref = UID() + var/key = GLOB.banlist_savefile["key"] + var/id = GLOB.banlist_savefile["id"] + var/ip = GLOB.banlist_savefile["ip"] + var/reason = GLOB.banlist_savefile["reason"] + var/by = GLOB.banlist_savefile["bannedby"] + var/expiry + if(GLOB.banlist_savefile["temp"]) + expiry = GetExp(GLOB.banlist_savefile["minutes"]) + if(!expiry) expiry = "Removal Pending" + else expiry = "Permaban" + + dat += text("") + + dat += "
    (U)(E) Key: [key]ComputerID: [id]IP: [ip] [expiry](By: [by])(Reason: [reason])
    " + dat = "
    Bans: (U) = Unban , (E) = Edit Ban - ([count] Bans)
    [dat]" + usr << browse(dat, "window=unbanp;size=875x400") + +//////////////////////////////////// DEBUG //////////////////////////////////// + +/proc/CreateBans() + + UpdateTime() + + var/i + var/last + + for(i=0, i<1001, i++) + var/a = pick(1,0) + var/b = pick(1,0) + if(b) + GLOB.banlist_savefile.cd = "/base" + GLOB.banlist_savefile.dir.Add("trash[i]trashid[i]") + GLOB.banlist_savefile.cd = "/base/trash[i]trashid[i]" + GLOB.banlist_savefile["key"] << "trash[i]" + else + GLOB.banlist_savefile.cd = "/base" + GLOB.banlist_savefile.dir.Add("[last]trashid[i]") + GLOB.banlist_savefile.cd = "/base/[last]trashid[i]" + GLOB.banlist_savefile["key"] << last + GLOB.banlist_savefile["id"] << "trashid[i]" + GLOB.banlist_savefile["reason"] << "Trashban[i]." + GLOB.banlist_savefile["temp"] << a + GLOB.banlist_savefile["minutes"] << GLOB.CMinutes + rand(1,2000) + GLOB.banlist_savefile["bannedby"] << "trashmin" + last = "trash[i]" + + GLOB.banlist_savefile.cd = "/base" + +/proc/ClearAllBans() + GLOB.banlist_savefile.cd = "/base" + for(var/A in GLOB.banlist_savefile.dir) + RemoveBan(A) + diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm index 05af7c15cf57..b486f168503c 100644 --- a/code/modules/admin/ToRban.dm +++ b/code/modules/admin/ToRban.dm @@ -1,89 +1,89 @@ -//By Carnwennan -//fetches an external list and processes it into a list of ip addresses. -//It then stores the processed list into a savefile for later use -#define TORFILE "data/ToR_ban.bdb" -#define TOR_UPDATE_INTERVAL 216000 //~6 hours - -/proc/ToRban_isbanned(var/ip_address) - var/savefile/F = new(TORFILE) - if(F) - if( ip_address in F.dir ) - return 1 - return 0 - -/proc/ToRban_autoupdate() - var/savefile/F = new(TORFILE) - if(F) - var/last_update - F["last_update"] >> last_update - if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while - ToRban_update() - return - -/proc/ToRban_update() - spawn(0) - log_world("Downloading updated ToR data...") - var/http[] = world.Export("http://exitlist.torproject.org/exit-addresses") - - var/list/rawlist = file2list(http["CONTENT"]) - if(rawlist.len) - fdel(TORFILE) - var/savefile/F = new(TORFILE) - for( var/line in rawlist ) - if(!line) continue - if( copytext(line,1,12) == "ExitAddress" ) - var/cleaned = copytext(line,13,length(line)-19) - if(!cleaned) continue - F[cleaned] << 1 - to_chat(F["last_update"], world.realtime) - log_world("ToR data updated!") - if(usr) - to_chat(usr, "ToRban updated.") - return 1 - log_world("ToR data update aborted: no data.") - return 0 - -/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) - set name = "ToRban" - set category = "Server" - if(!holder) return - switch(task) - if("update") - ToRban_update() - if("toggle") - if(config) - if(config.ToRban) - config.ToRban = 0 - message_admins("ToR banning disabled.") - else - config.ToRban = 1 - message_admins("ToR banning enabled.") - if("show") - var/savefile/F = new(TORFILE) - var/dat - if( length(F.dir) ) - for( var/i=1, i<=length(F.dir), i++ ) - dat += "" - dat = "
    #[i] [F.dir[i]]
    [dat]
    " - else - dat = "No addresses in list." - src << browse(dat,"window=ToRban_show") - if("remove") - var/savefile/F = new(TORFILE) - var/choice = input(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban",null) as null|anything in F.dir - if(choice) - F.dir.Remove(choice) - to_chat(src, "Address removed") - if("remove all") - to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") - if("find") - var/input = input(src,"Please input an IP address to search for:","Find ToR ban",null) as null|text - if(input) - if(ToRban_isbanned(input)) - to_chat(src, "Address is a known ToR address") - else - to_chat(src, "Address is not a known ToR address") - return - -#undef TORFILE -#undef TOR_UPDATE_INTERVAL \ No newline at end of file +//By Carnwennan +//fetches an external list and processes it into a list of ip addresses. +//It then stores the processed list into a savefile for later use +#define TORFILE "data/ToR_ban.bdb" +#define TOR_UPDATE_INTERVAL 216000 //~6 hours + +/proc/ToRban_isbanned(var/ip_address) + var/savefile/F = new(TORFILE) + if(F) + if( ip_address in F.dir ) + return 1 + return 0 + +/proc/ToRban_autoupdate() + var/savefile/F = new(TORFILE) + if(F) + var/last_update + F["last_update"] >> last_update + if((last_update + TOR_UPDATE_INTERVAL) < world.realtime) //we haven't updated for a while + ToRban_update() + return + +/proc/ToRban_update() + spawn(0) + log_world("Downloading updated ToR data...") + var/http[] = world.Export("http://exitlist.torproject.org/exit-addresses") + + var/list/rawlist = file2list(http["CONTENT"]) + if(rawlist.len) + fdel(TORFILE) + var/savefile/F = new(TORFILE) + for( var/line in rawlist ) + if(!line) continue + if( copytext(line,1,12) == "ExitAddress" ) + var/cleaned = copytext(line,13,length(line)-19) + if(!cleaned) continue + F[cleaned] << 1 + to_chat(F["last_update"], world.realtime) + log_world("ToR data updated!") + if(usr) + to_chat(usr, "ToRban updated.") + return 1 + log_world("ToR data update aborted: no data.") + return 0 + +/client/proc/ToRban(task in list("update","toggle","show","remove","remove all","find")) + set name = "ToRban" + set category = "Server" + if(!holder) return + switch(task) + if("update") + ToRban_update() + if("toggle") + if(config) + if(config.ToRban) + config.ToRban = 0 + message_admins("ToR banning disabled.") + else + config.ToRban = 1 + message_admins("ToR banning enabled.") + if("show") + var/savefile/F = new(TORFILE) + var/dat + if( length(F.dir) ) + for( var/i=1, i<=length(F.dir), i++ ) + dat += "#[i] [F.dir[i]]" + dat = "[dat]
    " + else + dat = "No addresses in list." + src << browse(dat,"window=ToRban_show") + if("remove") + var/savefile/F = new(TORFILE) + var/choice = input(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban",null) as null|anything in F.dir + if(choice) + F.dir.Remove(choice) + to_chat(src, "Address removed") + if("remove all") + to_chat(src, "[TORFILE] was [fdel(TORFILE)?"":"not "]removed.") + if("find") + var/input = input(src,"Please input an IP address to search for:","Find ToR ban",null) as null|text + if(input) + if(ToRban_isbanned(input)) + to_chat(src, "Address is a known ToR address") + else + to_chat(src, "Address is not a known ToR address") + return + +#undef TORFILE +#undef TOR_UPDATE_INTERVAL diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 55fb1c4047a0..df156c5b0dfb 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -1,1094 +1,1094 @@ -var/global/BSACooldown = 0 -var/global/nologevent = 0 - -//////////////////////////////// -/proc/message_admins(var/msg) - msg = "ADMIN LOG: [msg]" - for(var/client/C in GLOB.admins) - if(R_ADMIN & C.holder.rights) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_ADMINLOGS)) - to_chat(C, msg) - -/proc/msg_admin_attack(var/text, var/loglevel) - if(!nologevent) - var/rendered = "ATTACK: [text]" - for(var/client/C in GLOB.admins) - if(R_ADMIN & C.holder.rights) - if(C.prefs.atklog == ATKLOG_NONE) - continue - var/msg = rendered - if(C.prefs.atklog <= loglevel) - to_chat(C, msg) - - -/proc/message_adminTicket(var/msg, var/alt = FALSE) - if(alt) - msg = "ADMIN TICKET: [msg]" - else - msg = "ADMIN TICKET: [msg]" - for(var/client/C in GLOB.admins) - if(R_ADMIN & C.holder.rights) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_TICKETLOGS)) - to_chat(C, msg) - -/proc/message_mentorTicket(var/msg) - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN | R_MENTOR | R_MOD, 0, C.mob)) - if(C.prefs && !(C.prefs.toggles & CHAT_NO_MENTORTICKETLOGS)) - to_chat(C, msg) - -/proc/admin_ban_mobsearch(var/mob/M, var/ckey_to_find, var/mob/admin_to_notify) - if(!M || !M.ckey) - if(ckey_to_find) - for(var/mob/O in GLOB.mob_list) - if(O.ckey && O.ckey == ckey_to_find) - if(admin_to_notify) - to_chat(admin_to_notify, "admin_ban_mobsearch: Player [ckey_to_find] is now in mob [O]. Pulling data from new mob.") - return O - if(admin_to_notify) - to_chat(admin_to_notify, "admin_ban_mobsearch: Player [ckey_to_find] does not seem to have any mob, anywhere. This is probably an error.") - else if(admin_to_notify) - to_chat(admin_to_notify, "admin_ban_mobsearch: No mob or ckey detected.") - return M - -///////////////////////////////////////////////////////////////////////////////////////////////Panels - -/datum/admins/proc/show_player_panel(var/mob/M in GLOB.mob_list) - set category = null - set name = "Show Player Panel" - set desc="Edit player (respawn, ban, heal, etc)" - - if(!M) - to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") - return - - if(!check_rights(R_ADMIN|R_MOD)) - return - - var/body = "Options for [M.key]" - body += "Options panel for [M]" - if(M.client) - body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " - body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]" - - if(istype(M, /mob/new_player)) - body += " Hasn't Entered Game " - else - body += " \[Heal\] " - - body += "

    \[ " - body += "VV - " - body += "[ADMIN_TP(M,"TP")] - " - if(M.client) - body += "PM - " - body += "[ADMIN_SM(M,"SM")] - " - if(ishuman(M) && M.mind) - body += "HM -" - body += "[admin_jump_link(M)]\]

    " - body += "Mob type: [M.type]
    " - if(M.client) - if(M.client.related_accounts_cid.len) - body += "Related accounts by CID: [jointext(M.client.related_accounts_cid, " - ")]
    " - if(M.client.related_accounts_ip.len) - body += "Related accounts by IP: [jointext(M.client.related_accounts_ip, " - ")]

    " - - if(M.ckey) - body += "Kick | " - body += "Warn | " - body += "Ban | " - body += "Jobban | " - body += "Appearance Ban | " - body += "Notes | " - if(config.forum_playerinfo_url) - body += "WebInfo | " - if(M.client) - if(check_watchlist(M.client.ckey)) - body += "Remove from Watchlist | " - body += "Edit Watchlist Reason " - else - body += "Add to Watchlist " - - if(M.client) - body += "| Prison | " - body += "\ Send back to Lobby | " - var/muted = M.client.prefs.muted - body += {"
    Mute: - \[IC | - OOC | - PRAY | - ADMINHELP | - DEADCHAT\] - (toggle all) - "} - - var/jumptoeye = "" - if(isAI(M)) - var/mob/living/silicon/ai/A = M - if(A.client && A.eyeobj) // No point following clientless AI eyes - jumptoeye = " (Eye)" - body += {"

    - Jump to[jumptoeye] | - Get | - Send To -

    - [check_rights(R_ADMIN,0) ? "[ADMIN_TP(M,"Traitor panel")] | " : "" ] - Narrate to | - [ADMIN_SM(M,"Subtle message")] - "} - - if(check_rights(R_EVENT, 0)) - body += {" | Bless | Smite"} - - if(isLivingSSD(M)) - if(istype(M.loc, /obj/machinery/cryopod)) - body += {" | De-Spawn "} - else - body += {" | Cryo "} - - if(M.client) - if(!istype(M, /mob/new_player)) - body += "

    " - body += "Transformation:" - body += "
    " - - //Monkey - if(issmall(M)) - body += "Monkeyized | " - else - body += "Monkeyize | " - - //Corgi - if(iscorgi(M)) - body += "Corgized | " - else - body += "Corgize | " - - //AI / Cyborg - if(isAI(M)) - body += "Is an AI " - else if(ishuman(M)) - body += {"Make AI | - Make Robot | - Make Alien | - Make Slime | - Make Superhero - "} - - //Simple Animals - if(isanimal(M)) - body += "Re-Animalize | " - else - body += "Animalize | " - - if(istype(M, /mob/dead/observer)) - body += "Re-incarnate | " - - if(ispAI(M)) - body += "Is a pAI " - else - body += "Make pAI | " - - // DNA2 - Admin Hax - if(M.dna && iscarbon(M)) - body += "

    " - body += "DNA Blocks:
    " - var/bname - for(var/block=1;block<=DNA_SE_LENGTH;block++) - if(((block-1)%5)==0) - body += "" - bname = assigned_blocks[block] - body += "" - body += "
     12345
    [block-1]" - if(bname) - var/bstate=M.dna.GetSEState(block) - var/bcolor="[(bstate)?"#006600":"#ff0000"]" - body += "[bname][block]" - else - body += "[block]" - body+="
    " - - body += {"

    - Rudimentary transformation:
    These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

    - Observer | - \[ Alien: Drone, - Hunter, - Queen, - Sentinel, - Larva \] - Human - \[ slime: Baby, - Adult \] - Monkey | - Cyborg | - Cat | - Runtime | - Corgi | - Ian | - Crab | - Coffee | - \[ Construct: Armoured , - Builder , - Wraith \] - Shade - "} - - if(M.client) - body += {"

    - Other actions: -
    - Forcesay | - Admin Room | - Thunderdome 1 | - Thunderdome 2 | - Thunderdome Admin | - Thunderdome Observer | - "} - - body += {"
    - - "} - - usr << browse(body, "window=adminplayeropts;size=550x615") - feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/datum/player_info/var/author // admin who authored the information -/datum/player_info/var/rank //rank of admin who made the notes -/datum/player_info/var/content // text content of the information -/datum/player_info/var/timestamp // Because this is bloody annoying - -#define PLAYER_NOTES_ENTRIES_PER_PAGE 50 -/datum/admins/proc/PlayerNotes() - set category = "Admin" - set name = "Player Notes" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - show_note() - -/datum/admins/proc/show_player_notes(var/key as text) - set category = "Admin" - set name = "Show Player Notes" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - show_note(key) - -/datum/admins/proc/vpn_whitelist() - set category = "Admin" - set name = "VPN Ckey Whitelist" - if(!check_rights(R_ADMIN)) - return - var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32) - if(key) - vpn_whitelist_panel(key) - -/datum/admins/proc/access_news_network() //MARKER - set category = "Event" - set name = "Access Newscaster Network" - set desc = "Allows you to view, add and edit news feeds." - - if(!check_rights(R_EVENT)) - return - - if(!istype(src,/datum/admins)) - src = usr.client.holder - - var/dat - dat = text("Admin Newscaster

    Admin Newscaster Unit

    ") - - switch(admincaster_screen) - if(0) - dat += {"Welcome to the admin newscaster.
    Here you can add, edit and censor every newspiece on the network. -
    Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units. -
    Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!
    - "} - if(news_network.wanted_issue) - dat+= "
    Read Wanted Issue" - - dat+= {"

    Create Feed Channel -
    View Feed Channels -
    Submit new Feed story -

    Exit - "} - - var/wanted_already = 0 - if(news_network.wanted_issue) - wanted_already = 1 - - dat+={"
    Feed Security functions:
    -
    [(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue -
    Censor Feed Stories -
    Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel. -

    The newscaster recognises you as:
    [src.admincaster_signature]
    - "} - if(1) - dat+= "Station Feed Channels
    " - if( isemptylist(news_network.network_channels) ) - dat+="No active channels found..." - else - for(var/datum/feed_channel/CHANNEL in news_network.network_channels) - if(CHANNEL.is_admin_channel) - dat+="[CHANNEL.channel_name]
    " - else - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
    " - dat+={"

    Refresh -
    Back - "} - - if(2) - dat+={" - Creating new Feed Channel... -
    Channel Name: [src.admincaster_feed_channel.channel_name]
    - Channel Author: [src.admincaster_signature]
    - Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

    -
    Submit

    Cancel
    - "} - if(3) - dat+={" - Creating new Feed Message... -
    Receiving Channel: [src.admincaster_feed_channel.channel_name]
    - Message Author: [src.admincaster_signature]
    - Message Body: [src.admincaster_feed_message.body]
    -
    Submit

    Cancel
    - "} - if(4) - dat+={" - Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

    -
    Return
    - "} - if(5) - dat+={" - Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

    -
    Return
    - "} - if(6) - dat+="ERROR: Could not submit Feed story to Network.

    " - if(src.admincaster_feed_channel.channel_name=="") - dat+="•Invalid receiving channel name.
    " - if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") - dat+="•Invalid message body.
    " - dat+="
    Return
    " - if(7) - dat+="ERROR: Could not submit Feed Channel to Network.

    " - if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") - dat+="•Invalid channel name.
    " - var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(check) - dat+="•Channel name already in use.
    " - dat+="
    Return
    " - if(9) - dat+="[src.admincaster_feed_channel.channel_name]: \[created by: [src.admincaster_feed_channel.author]\]
    " - if(src.admincaster_feed_channel.censored) - dat+={" - ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
    - No further feed story additions are allowed while the D-Notice is in effect.


    - "} - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
    " - else - var/i = 0 - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - i++ - dat+="-[MESSAGE.body]
    " - if(MESSAGE.img) - usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") - dat+="

    " - dat+="\[Story by [MESSAGE.author]\]
    " - dat+={" -

    Refresh -
    Back - "} - if(10) - dat+={" - Nanotrasen Feed Censorship Tool
    - NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
    - Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
    -
    Select Feed channel to get Stories from:
    - "} - if(isemptylist(news_network.network_channels)) - dat+="No feed channels found active...
    " - else - for(var/datum/feed_channel/CHANNEL in news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
    " - dat+="
    Cancel" - if(11) - dat+={" - Nanotrasen D-Notice Handler
    - A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's - morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed - stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
    - "} - if(isemptylist(news_network.network_channels)) - dat+="No feed channels found active...
    " - else - for(var/datum/feed_channel/CHANNEL in news_network.network_channels) - dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
    " - - dat+="
    Back" - if(12) - dat+={" - [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
    - [(src.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]
    - "} - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
    " - else - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+={" - -[MESSAGE.body]
    \[Story by [MESSAGE.author]\]
    - [(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]
    - "} - dat+="
    Back" - if(13) - dat+={" - [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
    - Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
    - "} - if(src.admincaster_feed_channel.censored) - dat+={" - ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
    - No further feed story additions are allowed while the D-Notice is in effect.


    - "} - else - if( isemptylist(src.admincaster_feed_channel.messages) ) - dat+="No feed messages found in channel...
    " - else - for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) - dat+="-[MESSAGE.body]
    \[Story by [MESSAGE.author]\]
    " - - dat+="
    Back" - if(14) - dat+="Wanted Issue Handler:" - var/wanted_already = 0 - var/end_param = 1 - if(news_network.wanted_issue) - wanted_already = 1 - end_param = 2 - if(wanted_already) - dat+="
    A wanted issue is already in Feed Circulation. You can edit or cancel it below.
    " - dat+={" -
    - Criminal Name: [src.admincaster_feed_message.author]
    - Description: [src.admincaster_feed_message.body]
    - "} - if(wanted_already) - dat+="Wanted Issue created by: [news_network.wanted_issue.backup_author]
    " - else - dat+="Wanted Issue will be created under prosecutor: [src.admincaster_signature]
    " - dat+="
    [(wanted_already) ? ("Edit Issue") : ("Submit")]" - if(wanted_already) - dat+="
    Take down Issue" - dat+="
    Cancel" - if(15) - dat+={" - Wanted issue for [src.admincaster_feed_message.author] is now in Network Circulation.

    -
    Return
    - "} - if(16) - dat+="ERROR: Wanted Issue rejected by Network.

    " - if(src.admincaster_feed_message.author =="" || src.admincaster_feed_message.author == "\[REDACTED\]") - dat+="•Invalid name for person wanted.
    " - if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") - dat+="•Invalid description.
    " - dat+="
    Return
    " - if(17) - dat+={" - Wanted Issue successfully deleted from Circulation
    -
    Return
    - "} - if(18) - dat+={" - -- STATIONWIDE WANTED ISSUE --
    \[Submitted by: [news_network.wanted_issue.backup_author]\]
    - Criminal: [news_network.wanted_issue.author]
    - Description: [news_network.wanted_issue.body]
    - Photo:: - "} - if(news_network.wanted_issue.img) - usr << browse_rsc(news_network.wanted_issue.img, "tmp_photow.png") - dat+="
    " - else - dat+="None" - dat+="
    Back
    " - if(19) - dat+={" - Wanted issue for [src.admincaster_feed_message.author] successfully edited.

    -
    Return
    - "} - else - dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" - -// to_chat(world, "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") -// to_chat(world, "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") - usr << browse(dat, "window=admincaster_main;size=400x600") - onclose(usr, "admincaster_main") - -/datum/admins/proc/Jobbans() - if(!check_rights(R_BAN)) - return - - var/dat = "Job Bans!
    " - for(var/t in jobban_keylist) - var/r = t - if( findtext(r,"##") ) - r = copytext( r, 1, findtext(r,"##") )//removes the description - dat += text("") - dat += "
    [t] (unban)
    " - usr << browse(dat, "window=ban;size=400x400") - -/datum/admins/proc/Game() - if(!check_rights(R_ADMIN)) - return - - var/dat = {" -
    Game Panel

    \n - Change Game Mode
    - "} - if(master_mode == "secret") - dat += "(Force Secret Mode)
    " - - dat += {" -
    - Create Object
    - Quick Create Object
    - Create Turf
    - Create Mob
    - "} - - usr << browse(dat, "window=admin2;size=210x280") - return - -/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge -//i.e. buttons/verbs - - -/datum/admins/proc/restart() - set category = "Server" - set name = "Restart" - set desc = "Restarts the world." - - if(!check_rights(R_SERVER)) - return - - var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null - if(isnull(delay)) - return - else - delay = delay * 10 - message_admins("[key_name_admin(usr)] has initiated a server restart with a delay of [delay/10] seconds") - log_admin("[key_name(usr)] has initiated a server restart with a delay of [delay/10] seconds") - SSticker.delay_end = 0 - feedback_add_details("admin_verb","R") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - world.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "end_error", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay) - -/datum/admins/proc/announce() - set category = "Admin" - set name = "Announce" - set desc = "Announce your desires to the world" - - if(!check_rights(R_ADMIN)) - return - - var/message = input("Global message to send:", "Admin Announce", null, null) as message|null - if(message) - if(!check_rights(R_SERVER,0)) - message = adminscrub(message,500) - message = replacetext(message, "\n", "
    ") // required since we're putting it in a

    tag - to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:

    [message]

    ") - log_admin("Announce: [key_name(usr)] : [message]") - feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleooc() - set category = "Server" - set desc="Globally Toggles OOC" - set name="Toggle OOC" - - if(!check_rights(R_ADMIN)) - return - - toggle_ooc() - log_and_message_admins("toggled OOC.") - feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/togglelooc() - set category = "Server" - set desc="Globally Toggles LOOC" - set name="Toggle LOOC" - - if(!check_rights(R_ADMIN)) - return - - config.looc_allowed = !(config.looc_allowed) - if(config.looc_allowed) - to_chat(world, "The LOOC channel has been globally enabled!") - else - to_chat(world, "The LOOC channel has been globally disabled!") - log_and_message_admins("toggled LOOC.") - feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggledsay() - set category = "Server" - set desc="Globally Toggles DSAY" - set name="Toggle DSAY" - - if(!check_rights(R_ADMIN)) - return - - config.dsay_allowed = !(config.dsay_allowed) - if(config.dsay_allowed) - to_chat(world, "Deadchat has been globally enabled!") - else - to_chat(world, "Deadchat has been globally disabled!") - log_admin("[key_name(usr)] toggled deadchat.") - message_admins("[key_name_admin(usr)] toggled deadchat.", 1) - feedback_add_details("admin_verb","TDSAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc - -/datum/admins/proc/toggleoocdead() - set category = "Server" - set desc="Toggle Dead OOC." - set name="Toggle Dead OOC" - - if(!check_rights(R_ADMIN)) - return - - config.dooc_allowed = !( config.dooc_allowed ) - log_admin("[key_name(usr)] toggled Dead OOC.") - message_admins("[key_name_admin(usr)] toggled Dead OOC.", 1) - feedback_add_details("admin_verb","TDOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleemoji() - set category = "Server" - set desc = "Toggle OOC Emoji" - set name = "Toggle OOC Emoji" - - if(!check_rights(R_ADMIN)) - return - - config.disable_ooc_emoji = !(config.disable_ooc_emoji) - log_admin("[key_name(usr)] toggled OOC Emoji.") - message_admins("[key_name_admin(usr)] toggled OOC Emoji.", 1) - feedback_add_details("admin_verb", "TEMOJ") - -/datum/admins/proc/startnow() - set category = "Server" - set desc="Start the round RIGHT NOW" - set name="Start Now" - - if(!check_rights(R_SERVER)) - return - - if(!SSticker) - alert("Unable to start the game as it is not set up.") - return - - if(config.start_now_confirmation) - if(alert(usr, "This is a live server. Are you sure you want to start now?", "Start game", "Yes", "No") != "Yes") - return - - if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP) - SSticker.force_start = TRUE - log_admin("[usr.key] has started the game.") - var/msg = "" - if(SSticker.current_state == GAME_STATE_STARTUP) - msg = " (The server is still setting up, but the round will be started as soon as possible.)" - message_admins("[usr.key] has started the game.[msg]") - feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return 1 - else - to_chat(usr, "Error: Start Now: Game has already started.") - return - -/datum/admins/proc/toggleenter() - set category = "Server" - set desc="People can't enter" - set name="Toggle Entering" - - if(!check_rights(R_SERVER)) - return - - enter_allowed = !( enter_allowed ) - if(!( enter_allowed )) - to_chat(world, "New players may no longer enter the game.") - else - to_chat(world, "New players may now enter the game.") - log_admin("[key_name(usr)] toggled new player game entering.") - message_admins("[key_name_admin(usr)] toggled new player game entering.", 1) - world.update_status() - feedback_add_details("admin_verb","TE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleAI() - set category = "Event" - set desc="People can't be AI" - set name="Toggle AI" - - if(!check_rights(R_EVENT)) - return - - config.allow_ai = !( config.allow_ai ) - if(!( config.allow_ai )) - to_chat(world, "The AI job is no longer chooseable.") - else - to_chat(world, "The AI job is chooseable now.") - message_admins("[key_name_admin(usr)] toggled AI allowed.") - log_admin("[key_name(usr)] toggled AI allowed.") - world.update_status() - feedback_add_details("admin_verb","TAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleaban() - set category = "Server" - set desc="Toggle the ability for players to respawn." - set name="Toggle Respawn" - - if(!check_rights(R_SERVER)) - return - - abandon_allowed = !( abandon_allowed ) - if(abandon_allowed) - to_chat(world, "You may now respawn.") - else - to_chat(world, "You may no longer respawn :(") - message_admins("[key_name_admin(usr)] toggled respawn to [abandon_allowed ? "On" : "Off"].", 1) - log_admin("[key_name(usr)] toggled respawn to [abandon_allowed ? "On" : "Off"].") - world.update_status() - feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggle_aliens() - set category = "Event" - set desc="Toggle alien mobs" - set name="Toggle Aliens" - - if(!check_rights(R_EVENT)) - return - - aliens_allowed = !aliens_allowed - log_admin("[key_name(usr)] toggled aliens to [aliens_allowed].") - message_admins("[key_name_admin(usr)] toggled aliens [aliens_allowed ? "on" : "off"].") - feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/delay() - set category = "Server" - set desc="Delay the game start/end" - set name="Delay" - - if(!check_rights(R_SERVER)) - return - - if(!SSticker || SSticker.current_state != GAME_STATE_PREGAME) - SSticker.delay_end = !SSticker.delay_end - log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") - message_admins("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) - return //alert("Round end delayed", null, null, null, null, null) - if(going) - going = FALSE - SSticker.delay_end = TRUE - to_chat(world, "The game start has been delayed.") - log_admin("[key_name(usr)] delayed the game.") - else - going = TRUE - to_chat(world, "The game will start soon.") - log_admin("[key_name(usr)] removed the delay.") - feedback_add_details("admin_verb","DELAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS - -/proc/is_special_character(mob/M as mob) // returns 1 for specail characters and 2 for heroes of gamemode - if(!SSticker || !SSticker.mode) - return 0 - if(!istype(M)) - return 0 - if((M.mind in SSticker.mode.head_revolutionaries) || (M.mind in SSticker.mode.revolutionaries)) - if(SSticker.mode.config_tag == "revolution") - return 2 - return 1 - if(M.mind in SSticker.mode.cult) - if(SSticker.mode.config_tag == "cult") - return 2 - return 1 - if(M.mind in SSticker.mode.syndicates) - if(SSticker.mode.config_tag == "nuclear") - return 2 - return 1 - if(M.mind in SSticker.mode.wizards) - if(SSticker.mode.config_tag == "wizard") - return 2 - return 1 - if(M.mind in SSticker.mode.changelings) - if(SSticker.mode.config_tag == "changeling") - return 2 - return 1 - if(M.mind in SSticker.mode.abductors) - if(SSticker.mode.config_tag == "abduction") - return 2 - return 1 - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(R.emagged) - return 1 - if(M.mind&&M.mind.special_role)//If they have a mind and special role, they are some type of traitor or antagonist. - return 1 - - return 0 - -/datum/admins/proc/spawn_atom(var/object as text) - set category = "Debug" - set desc = "(atom path) Spawn an atom" - set name = "Spawn" - - if(!check_rights(R_SPAWN)) - return - - var/list/types = typesof(/atom) - var/list/matches = new() - - for(var/path in types) - if(findtext("[path]", object)) - matches += path - - if(matches.len==0) - return - - var/chosen - if(matches.len==1) - chosen = matches[1] - else - chosen = input("Select an atom type", "Spawn Atom", matches[1]) as null|anything in matches - if(!chosen) - return - - if(ispath(chosen,/turf)) - var/turf/T = get_turf(usr.loc) - T.ChangeTurf(chosen) - else - var/atom/A = new chosen(usr.loc) - A.admin_spawned = TRUE - - log_admin("[key_name(usr)] spawned [chosen] at ([usr.x],[usr.y],[usr.z])") - feedback_add_details("admin_verb","SA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/show_traitor_panel(var/mob/M in GLOB.mob_list) - set category = "Admin" - set desc = "Edit mobs's memory and role" - set name = "Show Traitor Panel" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!M.mind) - to_chat(usr, "This mob has no mind!") - return - - M.mind.edit_memory() - feedback_add_details("admin_verb","STP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/toggleguests() - set category = "Server" - set desc="Guests can't enter" - set name="Toggle Guests" - - if(!check_rights(R_SERVER)) - return - - guests_allowed = !( guests_allowed ) - if(!( guests_allowed )) - to_chat(world, "Guests may no longer enter the game.") - else - to_chat(world, "Guests may now enter the game.") - log_admin("[key_name(usr)] toggled guests game entering [guests_allowed ? "" : "dis"]allowed.") - message_admins("[key_name_admin(usr)] toggled guests game entering [guests_allowed ? "" : "dis"]allowed.", 1) - feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/datum/admins/proc/output_ai_laws() - var/ai_number = 0 - for(var/mob/living/silicon/S in GLOB.mob_list) - ai_number++ - if(isAI(S)) - to_chat(usr, "AI [key_name(S, TRUE)]'s laws:") - else if(isrobot(S)) - var/mob/living/silicon/robot/R = S - to_chat(usr, "CYBORG [key_name(S, TRUE)]'s [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independent)"] laws:") - else if(ispAI(S)) - var/mob/living/silicon/pai/P = S - to_chat(usr, "pAI [key_name(S, TRUE)]'s laws:") - to_chat(usr, "[P.pai_law0]") - if(P.pai_laws) - to_chat(usr, "[P.pai_laws]") - continue // Skip showing normal silicon laws for pAIs - they don't have any - else - to_chat(usr, "SILICON [key_name(S, TRUE)]'s laws:") - - if(S.laws == null) - to_chat(usr, "[key_name(S, TRUE)]'s laws are null. Contact a coder.") - else - S.laws.show_laws(usr) - if(!ai_number) - to_chat(usr, "No AI's located.")//Just so you know the thing is actually working and not just ignoring you. - - - log_admin("[key_name(usr)] checked the AI laws") - message_admins("[key_name_admin(usr)] checked the AI laws") - -/client/proc/update_mob_sprite(mob/living/carbon/human/H as mob) - set name = "Update Mob Sprite" - set desc = "Should fix any mob sprite update errors." - set category = null - - if(!check_rights(R_ADMIN)) - return - - if(istype(H)) - H.regenerate_icons() - -// -// -//ALL DONE -//********************************************************************************************************* - -var/gamma_ship_location = 1 // 0 = station , 1 = space - -/proc/move_gamma_ship() - var/area/fromArea - var/area/toArea - if(gamma_ship_location == 1) - fromArea = locate(/area/shuttle/gamma/space) - toArea = locate(/area/shuttle/gamma/station) - else - fromArea = locate(/area/shuttle/gamma/station) - toArea = locate(/area/shuttle/gamma/space) - fromArea.move_contents_to(toArea) - - for(var/obj/machinery/mech_bay_recharge_port/P in toArea) - P.update_recharge_turf() - - if(gamma_ship_location) - gamma_ship_location = 0 - else - gamma_ship_location = 1 - return - -/proc/formatJumpTo(var/location,var/where="") - var/turf/loc - if(istype(location,/turf/)) - loc = location - else - loc = get_turf(location) - if(where=="") - where=formatLocation(loc) - return "[where]" - -/proc/formatLocation(var/location) - var/turf/loc - if(istype(location,/turf/)) - loc = location - else - loc = get_turf(location) - var/area/A = get_area(location) - return "[A.name] - [loc.x],[loc.y],[loc.z]" - -/proc/formatPlayerPanel(var/mob/U,var/text="PP") - return "[ADMIN_PP(U,"[text]")]" - -//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked -//defaults to kicking everyone (afk + non afk clients in the lobby) -//returns a list of ckeys of the kicked clients -/proc/kick_clients_in_lobby(message, kick_only_afk = 0) - var/list/kicked_client_names = list() - for(var/client/C in GLOB.clients) - if(istype(C.mob, /mob/new_player)) - if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk - continue - if(message) - to_chat(C, message) - kicked_client_names.Add("[C.ckey]") - del(C) - return kicked_client_names - -//returns 1 to let the dragdrop code know we are trapping this event -//returns 0 if we don't plan to trap the event -/datum/admins/proc/cmd_ghost_drag(var/mob/dead/observer/frommob, var/tothing) - if(!istype(frommob)) - return //extra sanity check to make sure only observers are shoved into things - - //same as assume-direct-control perm requirements. - if(!check_rights(R_VAREDIT,0)) //no varedit, check if they have r_admin and r_debug - if(!check_rights(R_ADMIN|R_DEBUG,0)) //if they don't have r_admin and r_debug, return - return 0 //otherwise, if they have no varedit, but do have r_admin and r_debug, execute the rest of the code - - if(!frommob.ckey) - return 0 - - if(istype(tothing, /obj/item)) - var/mob/living/toitem = tothing - - var/ask = alert("Are you sure you want to allow [frommob.name]([frommob.key]) to possess [toitem.name]?", "Place ghost in control of item?", "Yes", "No") - if(ask != "Yes") - return 1 - - if(!frommob || !toitem) //make sure the mobs don't go away while we waited for a response - return 1 - - var/mob/living/simple_animal/possessed_object/tomob = new(toitem) - - message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") - log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") - feedback_add_details("admin_verb","CGD") - - tomob.ckey = frommob.ckey - qdel(frommob) - - - if(isliving(tothing)) - var/mob/living/tomob = tothing - - var/question = "" - if(tomob.ckey) - question = "This mob already has a user ([tomob.key]) in control of it! " - question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" - - var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No") - if(ask != "Yes") - return 1 - - if(!frommob || !tomob) //make sure the mobs don't go away while we waited for a response - return 1 - - if(tomob.client) //no need to ghostize if there is no client - tomob.ghostize(0) - - message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") - log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") - feedback_add_details("admin_verb","CGD") - - tomob.ckey = frommob.ckey - qdel(frommob) - - return 1 - -// Returns a list of the number of admins in various categories -// result[1] is the number of staff that match the rank mask and are active -// result[2] is the number of staff that do not match the rank mask -// result[3] is the number of staff that match the rank mask and are inactive -/proc/staff_countup(rank_mask = R_BAN) - var/list/result = list(0, 0, 0) - for(var/client/X in GLOB.admins) - if(rank_mask && !check_rights_for(X, rank_mask)) - result[2]++ - continue - if(X.holder.fakekey) - result[2]++ - continue - if(X.is_afk()) - result[3]++ - continue - result[1]++ - return result +GLOBAL_VAR_INIT(BSACooldown, 0) +GLOBAL_VAR_INIT(nologevent, 0) + +//////////////////////////////// +/proc/message_admins(var/msg) + msg = "ADMIN LOG: [msg]" + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + if(C.prefs && !(C.prefs.toggles & CHAT_NO_ADMINLOGS)) + to_chat(C, msg) + +/proc/msg_admin_attack(var/text, var/loglevel) + if(!GLOB.nologevent) + var/rendered = "ATTACK: [text]" + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + if(C.prefs.atklog == ATKLOG_NONE) + continue + var/msg = rendered + if(C.prefs.atklog <= loglevel) + to_chat(C, msg) + + +/proc/message_adminTicket(var/msg, var/alt = FALSE) + if(alt) + msg = "ADMIN TICKET: [msg]" + else + msg = "ADMIN TICKET: [msg]" + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + if(C.prefs && !(C.prefs.toggles & CHAT_NO_TICKETLOGS)) + to_chat(C, msg) + +/proc/message_mentorTicket(var/msg) + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN | R_MENTOR | R_MOD, 0, C.mob)) + if(C.prefs && !(C.prefs.toggles & CHAT_NO_MENTORTICKETLOGS)) + to_chat(C, msg) + +/proc/admin_ban_mobsearch(var/mob/M, var/ckey_to_find, var/mob/admin_to_notify) + if(!M || !M.ckey) + if(ckey_to_find) + for(var/mob/O in GLOB.mob_list) + if(O.ckey && O.ckey == ckey_to_find) + if(admin_to_notify) + to_chat(admin_to_notify, "admin_ban_mobsearch: Player [ckey_to_find] is now in mob [O]. Pulling data from new mob.") + return O + if(admin_to_notify) + to_chat(admin_to_notify, "admin_ban_mobsearch: Player [ckey_to_find] does not seem to have any mob, anywhere. This is probably an error.") + else if(admin_to_notify) + to_chat(admin_to_notify, "admin_ban_mobsearch: No mob or ckey detected.") + return M + +///////////////////////////////////////////////////////////////////////////////////////////////Panels + +/datum/admins/proc/show_player_panel(var/mob/M in GLOB.mob_list) + set category = null + set name = "Show Player Panel" + set desc="Edit player (respawn, ban, heal, etc)" + + if(!M) + to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.") + return + + if(!check_rights(R_ADMIN|R_MOD)) + return + + var/body = "Options for [M.key]" + body += "Options panel for [M]" + if(M.client) + body += " played by [M.client] " + body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\] " + body += "\[" + M.client.get_exp_type(EXP_TYPE_CREW) + " as [EXP_TYPE_CREW]\]" + + if(istype(M, /mob/new_player)) + body += " Hasn't Entered Game " + else + body += " \[Heal\] " + + body += "

    \[ " + body += "VV - " + body += "[ADMIN_TP(M,"TP")] - " + if(M.client) + body += "PM - " + body += "[ADMIN_SM(M,"SM")] - " + if(ishuman(M) && M.mind) + body += "HM -" + body += "[admin_jump_link(M)]\]

    " + body += "Mob type: [M.type]
    " + if(M.client) + if(M.client.related_accounts_cid.len) + body += "Related accounts by CID: [jointext(M.client.related_accounts_cid, " - ")]
    " + if(M.client.related_accounts_ip.len) + body += "Related accounts by IP: [jointext(M.client.related_accounts_ip, " - ")]

    " + + if(M.ckey) + body += "Kick | " + body += "Warn | " + body += "Ban | " + body += "Jobban | " + body += "Appearance Ban | " + body += "Notes | " + if(config.forum_playerinfo_url) + body += "WebInfo | " + if(M.client) + if(check_watchlist(M.client.ckey)) + body += "Remove from Watchlist | " + body += "Edit Watchlist Reason " + else + body += "Add to Watchlist " + + if(M.client) + body += "| Prison | " + body += "\ Send back to Lobby | " + var/muted = M.client.prefs.muted + body += {"
    Mute: + \[IC | + OOC | + PRAY | + ADMINHELP | + DEADCHAT\] + (toggle all) + "} + + var/jumptoeye = "" + if(isAI(M)) + var/mob/living/silicon/ai/A = M + if(A.client && A.eyeobj) // No point following clientless AI eyes + jumptoeye = " (Eye)" + body += {"

    + Jump to[jumptoeye] | + Get | + Send To +

    + [check_rights(R_ADMIN,0) ? "[ADMIN_TP(M,"Traitor panel")] | " : "" ] + Narrate to | + [ADMIN_SM(M,"Subtle message")] + "} + + if(check_rights(R_EVENT, 0)) + body += {" | Bless | Smite"} + + if(isLivingSSD(M)) + if(istype(M.loc, /obj/machinery/cryopod)) + body += {" | De-Spawn "} + else + body += {" | Cryo "} + + if(M.client) + if(!istype(M, /mob/new_player)) + body += "

    " + body += "Transformation:" + body += "
    " + + //Monkey + if(issmall(M)) + body += "Monkeyized | " + else + body += "Monkeyize | " + + //Corgi + if(iscorgi(M)) + body += "Corgized | " + else + body += "Corgize | " + + //AI / Cyborg + if(isAI(M)) + body += "Is an AI " + else if(ishuman(M)) + body += {"Make AI | + Make Robot | + Make Alien | + Make Slime | + Make Superhero + "} + + //Simple Animals + if(isanimal(M)) + body += "Re-Animalize | " + else + body += "Animalize | " + + if(istype(M, /mob/dead/observer)) + body += "Re-incarnate | " + + if(ispAI(M)) + body += "Is a pAI " + else + body += "Make pAI | " + + // DNA2 - Admin Hax + if(M.dna && iscarbon(M)) + body += "

    " + body += "DNA Blocks:
    " + var/bname + for(var/block=1;block<=DNA_SE_LENGTH;block++) + if(((block-1)%5)==0) + body += "" + bname = GLOB.assigned_blocks[block] + body += "" + body += "
     12345
    [block-1]" + if(bname) + var/bstate=M.dna.GetSEState(block) + var/bcolor="[(bstate)?"#006600":"#ff0000"]" + body += "[bname][block]" + else + body += "[block]" + body+="
    " + + body += {"

    + Rudimentary transformation:
    These transformations only create a new mob type and copy stuff over. They do not take into account MMIs and similar mob-specific things. The buttons in 'Transformations' are preferred, when possible.

    + Observer | + \[ Alien: Drone, + Hunter, + Queen, + Sentinel, + Larva \] + Human + \[ slime: Baby, + Adult \] + Monkey | + Cyborg | + Cat | + Runtime | + Corgi | + Ian | + Crab | + Coffee | + \[ Construct: Armoured , + Builder , + Wraith \] + Shade + "} + + if(M.client) + body += {"

    + Other actions: +
    + Forcesay | + Admin Room | + Thunderdome 1 | + Thunderdome 2 | + Thunderdome Admin | + Thunderdome Observer | + "} + + body += {"
    + + "} + + usr << browse(body, "window=adminplayeropts;size=550x615") + feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/datum/player_info/var/author // admin who authored the information +/datum/player_info/var/rank //rank of admin who made the notes +/datum/player_info/var/content // text content of the information +/datum/player_info/var/timestamp // Because this is bloody annoying + +#define PLAYER_NOTES_ENTRIES_PER_PAGE 50 +/datum/admins/proc/PlayerNotes() + set category = "Admin" + set name = "Player Notes" + + if(!check_rights(R_ADMIN|R_MOD)) + return + + show_note() + +/datum/admins/proc/show_player_notes(var/key as text) + set category = "Admin" + set name = "Show Player Notes" + + if(!check_rights(R_ADMIN|R_MOD)) + return + + show_note(key) + +/datum/admins/proc/vpn_whitelist() + set category = "Admin" + set name = "VPN Ckey Whitelist" + if(!check_rights(R_ADMIN)) + return + var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32) + if(key) + vpn_whitelist_panel(key) + +/datum/admins/proc/access_news_network() //MARKER + set category = "Event" + set name = "Access Newscaster Network" + set desc = "Allows you to view, add and edit news feeds." + + if(!check_rights(R_EVENT)) + return + + if(!istype(src,/datum/admins)) + src = usr.client.holder + + var/dat + dat = text("Admin Newscaster

    Admin Newscaster Unit

    ") + + switch(admincaster_screen) + if(0) + dat += {"Welcome to the admin newscaster.
    Here you can add, edit and censor every newspiece on the network. +
    Feed channels and stories entered through here will be uneditable and handled as official news by the rest of the units. +
    Note that this panel allows full freedom over the news network, there are no constrictions except the few basic ones. Don't break things!
    + "} + if(GLOB.news_network.wanted_issue) + dat+= "
    Read Wanted Issue" + + dat+= {"

    Create Feed Channel +
    View Feed Channels +
    Submit new Feed story +

    Exit + "} + + var/wanted_already = 0 + if(GLOB.news_network.wanted_issue) + wanted_already = 1 + + dat+={"
    Feed Security functions:
    +
    [(wanted_already) ? ("Manage") : ("Publish")] \"Wanted\" Issue +
    Censor Feed Stories +
    Mark Feed Channel with Nanotrasen D-Notice (disables and locks the channel. +

    The newscaster recognises you as:
    [src.admincaster_signature]
    + "} + if(1) + dat+= "Station Feed Channels
    " + if( isemptylist(GLOB.news_network.network_channels) ) + dat+="No active channels found..." + else + for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) + if(CHANNEL.is_admin_channel) + dat+="[CHANNEL.channel_name]
    " + else + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
    " + dat+={"

    Refresh +
    Back + "} + + if(2) + dat+={" + Creating new Feed Channel... +
    Channel Name: [src.admincaster_feed_channel.channel_name]
    + Channel Author: [src.admincaster_signature]
    + Will Accept Public Feeds: [(src.admincaster_feed_channel.locked) ? ("NO") : ("YES")]

    +
    Submit

    Cancel
    + "} + if(3) + dat+={" + Creating new Feed Message... +
    Receiving Channel: [src.admincaster_feed_channel.channel_name]
    + Message Author: [src.admincaster_signature]
    + Message Body: [src.admincaster_feed_message.body]
    +
    Submit

    Cancel
    + "} + if(4) + dat+={" + Feed story successfully submitted to [src.admincaster_feed_channel.channel_name].

    +
    Return
    + "} + if(5) + dat+={" + Feed Channel [src.admincaster_feed_channel.channel_name] created successfully.

    +
    Return
    + "} + if(6) + dat+="ERROR: Could not submit Feed story to Network.

    " + if(src.admincaster_feed_channel.channel_name=="") + dat+="•Invalid receiving channel name.
    " + if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") + dat+="•Invalid message body.
    " + dat+="
    Return
    " + if(7) + dat+="ERROR: Could not submit Feed Channel to Network.

    " + if(src.admincaster_feed_channel.channel_name =="" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]") + dat+="•Invalid channel name.
    " + var/check = 0 + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(check) + dat+="•Channel name already in use.
    " + dat+="
    Return
    " + if(9) + dat+="[src.admincaster_feed_channel.channel_name]: \[created by: [src.admincaster_feed_channel.author]\]
    " + if(src.admincaster_feed_channel.censored) + dat+={" + ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
    + No further feed story additions are allowed while the D-Notice is in effect.


    + "} + else + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
    " + else + var/i = 0 + for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + i++ + dat+="-[MESSAGE.body]
    " + if(MESSAGE.img) + usr << browse_rsc(MESSAGE.img, "tmp_photo[i].png") + dat+="

    " + dat+="\[Story by [MESSAGE.author]\]
    " + dat+={" +

    Refresh +
    Back + "} + if(10) + dat+={" + Nanotrasen Feed Censorship Tool
    + NOTE: Due to the nature of news Feeds, total deletion of a Feed Story is not possible.
    + Keep in mind that users attempting to view a censored feed will instead see the \[REDACTED\] tag above it.
    +
    Select Feed channel to get Stories from:
    + "} + if(isemptylist(GLOB.news_network.network_channels)) + dat+="No feed channels found active...
    " + else + for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
    " + dat+="
    Cancel" + if(11) + dat+={" + Nanotrasen D-Notice Handler
    + A D-Notice is to be bestowed upon the channel if the handling Authority deems it as harmful for the station's + morale, integrity or disciplinary behaviour. A D-Notice will render a channel unable to be updated by anyone, without deleting any feed + stories it might contain at the time. You can lift a D-Notice if you have the required access at any time.
    + "} + if(isemptylist(GLOB.news_network.network_channels)) + dat+="No feed channels found active...
    " + else + for(var/datum/feed_channel/CHANNEL in GLOB.news_network.network_channels) + dat+="[CHANNEL.channel_name] [(CHANNEL.censored) ? ("***") : ""]
    " + + dat+="
    Back" + if(12) + dat+={" + [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
    + [(src.admincaster_feed_channel.author=="\[REDACTED\]") ? ("Undo Author censorship") : ("Censor channel Author")]
    + "} + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
    " + else + for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + dat+={" + -[MESSAGE.body]
    \[Story by [MESSAGE.author]\]
    + [(MESSAGE.body == "\[REDACTED\]") ? ("Undo story censorship") : ("Censor story")] - [(MESSAGE.author == "\[REDACTED\]") ? ("Undo Author Censorship") : ("Censor message Author")]
    + "} + dat+="
    Back" + if(13) + dat+={" + [src.admincaster_feed_channel.channel_name]: \[ created by: [src.admincaster_feed_channel.author] \]
    + Channel messages listed below. If you deem them dangerous to the station, you can Bestow a D-Notice upon the channel.
    + "} + if(src.admincaster_feed_channel.censored) + dat+={" + ATTENTION: This channel has been deemed as threatening to the welfare of the station, and marked with a Nanotrasen D-Notice.
    + No further feed story additions are allowed while the D-Notice is in effect.


    + "} + else + if( isemptylist(src.admincaster_feed_channel.messages) ) + dat+="No feed messages found in channel...
    " + else + for(var/datum/feed_message/MESSAGE in src.admincaster_feed_channel.messages) + dat+="-[MESSAGE.body]
    \[Story by [MESSAGE.author]\]
    " + + dat+="
    Back" + if(14) + dat+="Wanted Issue Handler:" + var/wanted_already = 0 + var/end_param = 1 + if(GLOB.news_network.wanted_issue) + wanted_already = 1 + end_param = 2 + if(wanted_already) + dat+="
    A wanted issue is already in Feed Circulation. You can edit or cancel it below.
    " + dat+={" +
    + Criminal Name: [src.admincaster_feed_message.author]
    + Description: [src.admincaster_feed_message.body]
    + "} + if(wanted_already) + dat+="Wanted Issue created by: [GLOB.news_network.wanted_issue.backup_author]
    " + else + dat+="Wanted Issue will be created under prosecutor: [src.admincaster_signature]
    " + dat+="
    [(wanted_already) ? ("Edit Issue") : ("Submit")]" + if(wanted_already) + dat+="
    Take down Issue" + dat+="
    Cancel" + if(15) + dat+={" + Wanted issue for [src.admincaster_feed_message.author] is now in Network Circulation.

    +
    Return
    + "} + if(16) + dat+="ERROR: Wanted Issue rejected by Network.

    " + if(src.admincaster_feed_message.author =="" || src.admincaster_feed_message.author == "\[REDACTED\]") + dat+="•Invalid name for person wanted.
    " + if(src.admincaster_feed_message.body == "" || src.admincaster_feed_message.body == "\[REDACTED\]") + dat+="•Invalid description.
    " + dat+="
    Return
    " + if(17) + dat+={" + Wanted Issue successfully deleted from Circulation
    +
    Return
    + "} + if(18) + dat+={" + -- STATIONWIDE WANTED ISSUE --
    \[Submitted by: [GLOB.news_network.wanted_issue.backup_author]\]
    + Criminal: [GLOB.news_network.wanted_issue.author]
    + Description: [GLOB.news_network.wanted_issue.body]
    + Photo:: + "} + if(GLOB.news_network.wanted_issue.img) + usr << browse_rsc(GLOB.news_network.wanted_issue.img, "tmp_photow.png") + dat+="
    " + else + dat+="None" + dat+="
    Back
    " + if(19) + dat+={" + Wanted issue for [src.admincaster_feed_message.author] successfully edited.

    +
    Return
    + "} + else + dat+="I'm sorry to break your immersion. This shit's bugged. Report this bug to Agouri, polyxenitopalidou@gmail.com" + +// to_chat(world, "Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") +// to_chat(world, "Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") + usr << browse(dat, "window=admincaster_main;size=400x600") + onclose(usr, "admincaster_main") + +/datum/admins/proc/Jobbans() + if(!check_rights(R_BAN)) + return + + var/dat = "Job Bans!
    " + for(var/t in GLOB.jobban_keylist) + var/r = t + if( findtext(r,"##") ) + r = copytext( r, 1, findtext(r,"##") )//removes the description + dat += text("") + dat += "
    [t] (unban)
    " + usr << browse(dat, "window=ban;size=400x400") + +/datum/admins/proc/Game() + if(!check_rights(R_ADMIN)) + return + + var/dat = {" +
    Game Panel

    \n + Change Game Mode
    + "} + if(GLOB.master_mode == "secret") + dat += "(Force Secret Mode)
    " + + dat += {" +
    + Create Object
    + Quick Create Object
    + Create Turf
    + Create Mob
    + "} + + usr << browse(dat, "window=admin2;size=210x280") + return + +/////////////////////////////////////////////////////////////////////////////////////////////////admins2.dm merge +//i.e. buttons/verbs + + +/datum/admins/proc/restart() + set category = "Server" + set name = "Restart" + set desc = "Restarts the world." + + if(!check_rights(R_SERVER)) + return + + var/delay = input("What delay should the restart have (in seconds)?", "Restart Delay", 5) as num|null + if(isnull(delay)) + return + else + delay = delay * 10 + message_admins("[key_name_admin(usr)] has initiated a server restart with a delay of [delay/10] seconds") + log_admin("[key_name(usr)] has initiated a server restart with a delay of [delay/10] seconds") + SSticker.delay_end = 0 + feedback_add_details("admin_verb","R") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + world.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "end_error", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", delay) + +/datum/admins/proc/announce() + set category = "Admin" + set name = "Announce" + set desc = "Announce your desires to the world" + + if(!check_rights(R_ADMIN)) + return + + var/message = input("Global message to send:", "Admin Announce", null, null) as message|null + if(message) + if(!check_rights(R_SERVER,0)) + message = adminscrub(message,500) + message = replacetext(message, "\n", "
    ") // required since we're putting it in a

    tag + to_chat(world, "[usr.client.holder.fakekey ? "Administrator" : usr.key] Announces:

    [message]

    ") + log_admin("Announce: [key_name(usr)] : [message]") + feedback_add_details("admin_verb","A") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleooc() + set category = "Server" + set desc="Globally Toggles OOC" + set name="Toggle OOC" + + if(!check_rights(R_ADMIN)) + return + + toggle_ooc() + log_and_message_admins("toggled OOC.") + feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/togglelooc() + set category = "Server" + set desc="Globally Toggles LOOC" + set name="Toggle LOOC" + + if(!check_rights(R_ADMIN)) + return + + config.looc_allowed = !(config.looc_allowed) + if(config.looc_allowed) + to_chat(world, "The LOOC channel has been globally enabled!") + else + to_chat(world, "The LOOC channel has been globally disabled!") + log_and_message_admins("toggled LOOC.") + feedback_add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggledsay() + set category = "Server" + set desc="Globally Toggles DSAY" + set name="Toggle DSAY" + + if(!check_rights(R_ADMIN)) + return + + config.dsay_allowed = !(config.dsay_allowed) + if(config.dsay_allowed) + to_chat(world, "Deadchat has been globally enabled!") + else + to_chat(world, "Deadchat has been globally disabled!") + log_admin("[key_name(usr)] toggled deadchat.") + message_admins("[key_name_admin(usr)] toggled deadchat.", 1) + feedback_add_details("admin_verb","TDSAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc + +/datum/admins/proc/toggleoocdead() + set category = "Server" + set desc="Toggle Dead OOC." + set name="Toggle Dead OOC" + + if(!check_rights(R_ADMIN)) + return + + config.dooc_allowed = !( config.dooc_allowed ) + log_admin("[key_name(usr)] toggled Dead OOC.") + message_admins("[key_name_admin(usr)] toggled Dead OOC.", 1) + feedback_add_details("admin_verb","TDOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleemoji() + set category = "Server" + set desc = "Toggle OOC Emoji" + set name = "Toggle OOC Emoji" + + if(!check_rights(R_ADMIN)) + return + + config.disable_ooc_emoji = !(config.disable_ooc_emoji) + log_admin("[key_name(usr)] toggled OOC Emoji.") + message_admins("[key_name_admin(usr)] toggled OOC Emoji.", 1) + feedback_add_details("admin_verb", "TEMOJ") + +/datum/admins/proc/startnow() + set category = "Server" + set desc="Start the round RIGHT NOW" + set name="Start Now" + + if(!check_rights(R_SERVER)) + return + + if(!SSticker) + alert("Unable to start the game as it is not set up.") + return + + if(config.start_now_confirmation) + if(alert(usr, "This is a live server. Are you sure you want to start now?", "Start game", "Yes", "No") != "Yes") + return + + if(SSticker.current_state == GAME_STATE_PREGAME || SSticker.current_state == GAME_STATE_STARTUP) + SSticker.force_start = TRUE + log_admin("[usr.key] has started the game.") + var/msg = "" + if(SSticker.current_state == GAME_STATE_STARTUP) + msg = " (The server is still setting up, but the round will be started as soon as possible.)" + message_admins("[usr.key] has started the game.[msg]") + feedback_add_details("admin_verb","SN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return 1 + else + to_chat(usr, "Error: Start Now: Game has already started.") + return + +/datum/admins/proc/toggleenter() + set category = "Server" + set desc="People can't enter" + set name="Toggle Entering" + + if(!check_rights(R_SERVER)) + return + + GLOB.enter_allowed = !( GLOB.enter_allowed ) + if(!( GLOB.enter_allowed )) + to_chat(world, "New players may no longer enter the game.") + else + to_chat(world, "New players may now enter the game.") + log_admin("[key_name(usr)] toggled new player game entering.") + message_admins("[key_name_admin(usr)] toggled new player game entering.", 1) + world.update_status() + feedback_add_details("admin_verb","TE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleAI() + set category = "Event" + set desc="People can't be AI" + set name="Toggle AI" + + if(!check_rights(R_EVENT)) + return + + config.allow_ai = !( config.allow_ai ) + if(!( config.allow_ai )) + to_chat(world, "The AI job is no longer chooseable.") + else + to_chat(world, "The AI job is chooseable now.") + message_admins("[key_name_admin(usr)] toggled AI allowed.") + log_admin("[key_name(usr)] toggled AI allowed.") + world.update_status() + feedback_add_details("admin_verb","TAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleaban() + set category = "Server" + set desc="Toggle the ability for players to respawn." + set name="Toggle Respawn" + + if(!check_rights(R_SERVER)) + return + + GLOB.abandon_allowed = !( GLOB.abandon_allowed ) + if(GLOB.abandon_allowed) + to_chat(world, "You may now respawn.") + else + to_chat(world, "You may no longer respawn :(") + message_admins("[key_name_admin(usr)] toggled respawn to [GLOB.abandon_allowed ? "On" : "Off"].", 1) + log_admin("[key_name(usr)] toggled respawn to [GLOB.abandon_allowed ? "On" : "Off"].") + world.update_status() + feedback_add_details("admin_verb","TR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggle_aliens() + set category = "Event" + set desc="Toggle alien mobs" + set name="Toggle Aliens" + + if(!check_rights(R_EVENT)) + return + + GLOB.aliens_allowed = !GLOB.aliens_allowed + log_admin("[key_name(usr)] toggled aliens to [GLOB.aliens_allowed].") + message_admins("[key_name_admin(usr)] toggled aliens [GLOB.aliens_allowed ? "on" : "off"].") + feedback_add_details("admin_verb","TA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/delay() + set category = "Server" + set desc="Delay the game start/end" + set name="Delay" + + if(!check_rights(R_SERVER)) + return + + if(!SSticker || SSticker.current_state != GAME_STATE_PREGAME) + SSticker.delay_end = !SSticker.delay_end + log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") + message_admins("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) + return //alert("Round end delayed", null, null, null, null, null) + if(SSticker.ticker_going) + SSticker.ticker_going = FALSE + SSticker.delay_end = TRUE + to_chat(world, "The game start has been delayed.") + log_admin("[key_name(usr)] delayed the game.") + else + SSticker.ticker_going = TRUE + to_chat(world, "The game will start soon.") + log_admin("[key_name(usr)] removed the delay.") + feedback_add_details("admin_verb","DELAY") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +////////////////////////////////////////////////////////////////////////////////////////////////ADMIN HELPER PROCS + +/proc/is_special_character(mob/M as mob) // returns 1 for specail characters and 2 for heroes of gamemode + if(!SSticker || !SSticker.mode) + return 0 + if(!istype(M)) + return 0 + if((M.mind in SSticker.mode.head_revolutionaries) || (M.mind in SSticker.mode.revolutionaries)) + if(SSticker.mode.config_tag == "revolution") + return 2 + return 1 + if(M.mind in SSticker.mode.cult) + if(SSticker.mode.config_tag == "cult") + return 2 + return 1 + if(M.mind in SSticker.mode.syndicates) + if(SSticker.mode.config_tag == "nuclear") + return 2 + return 1 + if(M.mind in SSticker.mode.wizards) + if(SSticker.mode.config_tag == "wizard") + return 2 + return 1 + if(M.mind in SSticker.mode.changelings) + if(SSticker.mode.config_tag == "changeling") + return 2 + return 1 + if(M.mind in SSticker.mode.abductors) + if(SSticker.mode.config_tag == "abduction") + return 2 + return 1 + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(R.emagged) + return 1 + if(M.mind&&M.mind.special_role)//If they have a mind and special role, they are some type of traitor or antagonist. + return 1 + + return 0 + +/datum/admins/proc/spawn_atom(var/object as text) + set category = "Debug" + set desc = "(atom path) Spawn an atom" + set name = "Spawn" + + if(!check_rights(R_SPAWN)) + return + + var/list/types = typesof(/atom) + var/list/matches = new() + + for(var/path in types) + if(findtext("[path]", object)) + matches += path + + if(matches.len==0) + return + + var/chosen + if(matches.len==1) + chosen = matches[1] + else + chosen = input("Select an atom type", "Spawn Atom", matches[1]) as null|anything in matches + if(!chosen) + return + + if(ispath(chosen,/turf)) + var/turf/T = get_turf(usr.loc) + T.ChangeTurf(chosen) + else + var/atom/A = new chosen(usr.loc) + A.admin_spawned = TRUE + + log_admin("[key_name(usr)] spawned [chosen] at ([usr.x],[usr.y],[usr.z])") + feedback_add_details("admin_verb","SA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/show_traitor_panel(var/mob/M in GLOB.mob_list) + set category = "Admin" + set desc = "Edit mobs's memory and role" + set name = "Show Traitor Panel" + + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!M.mind) + to_chat(usr, "This mob has no mind!") + return + + M.mind.edit_memory() + feedback_add_details("admin_verb","STP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/toggleguests() + set category = "Server" + set desc="Guests can't enter" + set name="Toggle Guests" + + if(!check_rights(R_SERVER)) + return + + GLOB.guests_allowed = !( GLOB.guests_allowed ) + if(!( GLOB.guests_allowed )) + to_chat(world, "Guests may no longer enter the game.") + else + to_chat(world, "Guests may now enter the game.") + log_admin("[key_name(usr)] toggled guests game entering [GLOB.guests_allowed ? "" : "dis"]allowed.") + message_admins("[key_name_admin(usr)] toggled guests game entering [GLOB.guests_allowed ? "" : "dis"]allowed.", 1) + feedback_add_details("admin_verb","TGU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/datum/admins/proc/output_ai_laws() + var/ai_number = 0 + for(var/mob/living/silicon/S in GLOB.mob_list) + ai_number++ + if(isAI(S)) + to_chat(usr, "AI [key_name(S, TRUE)]'s laws:") + else if(isrobot(S)) + var/mob/living/silicon/robot/R = S + to_chat(usr, "CYBORG [key_name(S, TRUE)]'s [R.connected_ai?"(Slaved to: [R.connected_ai])":"(Independent)"] laws:") + else if(ispAI(S)) + var/mob/living/silicon/pai/P = S + to_chat(usr, "pAI [key_name(S, TRUE)]'s laws:") + to_chat(usr, "[P.pai_law0]") + if(P.pai_laws) + to_chat(usr, "[P.pai_laws]") + continue // Skip showing normal silicon laws for pAIs - they don't have any + else + to_chat(usr, "SILICON [key_name(S, TRUE)]'s laws:") + + if(S.laws == null) + to_chat(usr, "[key_name(S, TRUE)]'s laws are null. Contact a coder.") + else + S.laws.show_laws(usr) + if(!ai_number) + to_chat(usr, "No AI's located.")//Just so you know the thing is actually working and not just ignoring you. + + + log_admin("[key_name(usr)] checked the AI laws") + message_admins("[key_name_admin(usr)] checked the AI laws") + +/client/proc/update_mob_sprite(mob/living/carbon/human/H as mob) + set name = "Update Mob Sprite" + set desc = "Should fix any mob sprite update errors." + set category = null + + if(!check_rights(R_ADMIN)) + return + + if(istype(H)) + H.regenerate_icons() + +// +// +//ALL DONE +//********************************************************************************************************* + +GLOBAL_VAR_INIT(gamma_ship_location, 1) // 0 = station , 1 = space + +/proc/move_gamma_ship() + var/area/fromArea + var/area/toArea + if(GLOB.gamma_ship_location == 1) + fromArea = locate(/area/shuttle/gamma/space) + toArea = locate(/area/shuttle/gamma/station) + else + fromArea = locate(/area/shuttle/gamma/station) + toArea = locate(/area/shuttle/gamma/space) + fromArea.move_contents_to(toArea) + + for(var/obj/machinery/mech_bay_recharge_port/P in toArea) + P.update_recharge_turf() + + if(GLOB.gamma_ship_location) + GLOB.gamma_ship_location = 0 + else + GLOB.gamma_ship_location = 1 + return + +/proc/formatJumpTo(var/location,var/where="") + var/turf/loc + if(istype(location,/turf/)) + loc = location + else + loc = get_turf(location) + if(where=="") + where=formatLocation(loc) + return "[where]" + +/proc/formatLocation(var/location) + var/turf/loc + if(istype(location,/turf/)) + loc = location + else + loc = get_turf(location) + var/area/A = get_area(location) + return "[A.name] - [loc.x],[loc.y],[loc.z]" + +/proc/formatPlayerPanel(var/mob/U,var/text="PP") + return "[ADMIN_PP(U,"[text]")]" + +//Kicks all the clients currently in the lobby. The second parameter (kick_only_afk) determins if an is_afk() check is ran, or if all clients are kicked +//defaults to kicking everyone (afk + non afk clients in the lobby) +//returns a list of ckeys of the kicked clients +/proc/kick_clients_in_lobby(message, kick_only_afk = 0) + var/list/kicked_client_names = list() + for(var/client/C in GLOB.clients) + if(istype(C.mob, /mob/new_player)) + if(kick_only_afk && !C.is_afk()) //Ignore clients who are not afk + continue + if(message) + to_chat(C, message) + kicked_client_names.Add("[C.ckey]") + del(C) + return kicked_client_names + +//returns 1 to let the dragdrop code know we are trapping this event +//returns 0 if we don't plan to trap the event +/datum/admins/proc/cmd_ghost_drag(var/mob/dead/observer/frommob, var/tothing) + if(!istype(frommob)) + return //extra sanity check to make sure only observers are shoved into things + + //same as assume-direct-control perm requirements. + if(!check_rights(R_VAREDIT,0)) //no varedit, check if they have r_admin and r_debug + if(!check_rights(R_ADMIN|R_DEBUG,0)) //if they don't have r_admin and r_debug, return + return 0 //otherwise, if they have no varedit, but do have r_admin and r_debug, execute the rest of the code + + if(!frommob.ckey) + return 0 + + if(istype(tothing, /obj/item)) + var/mob/living/toitem = tothing + + var/ask = alert("Are you sure you want to allow [frommob.name]([frommob.key]) to possess [toitem.name]?", "Place ghost in control of item?", "Yes", "No") + if(ask != "Yes") + return 1 + + if(!frommob || !toitem) //make sure the mobs don't go away while we waited for a response + return 1 + + var/mob/living/simple_animal/possessed_object/tomob = new(toitem) + + message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") + log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") + feedback_add_details("admin_verb","CGD") + + tomob.ckey = frommob.ckey + qdel(frommob) + + + if(isliving(tothing)) + var/mob/living/tomob = tothing + + var/question = "" + if(tomob.ckey) + question = "This mob already has a user ([tomob.key]) in control of it! " + question += "Are you sure you want to place [frommob.name]([frommob.key]) in control of [tomob.name]?" + + var/ask = alert(question, "Place ghost in control of mob?", "Yes", "No") + if(ask != "Yes") + return 1 + + if(!frommob || !tomob) //make sure the mobs don't go away while we waited for a response + return 1 + + if(tomob.client) //no need to ghostize if there is no client + tomob.ghostize(0) + + message_admins("[key_name_admin(usr)] has put [frommob.ckey] in control of [tomob.name].") + log_admin("[key_name(usr)] stuffed [frommob.ckey] into [tomob.name].") + feedback_add_details("admin_verb","CGD") + + tomob.ckey = frommob.ckey + qdel(frommob) + + return 1 + +// Returns a list of the number of admins in various categories +// result[1] is the number of staff that match the rank mask and are active +// result[2] is the number of staff that do not match the rank mask +// result[3] is the number of staff that match the rank mask and are inactive +/proc/staff_countup(rank_mask = R_BAN) + var/list/result = list(0, 0, 0) + for(var/client/X in GLOB.admins) + if(rank_mask && !check_rights_for(X, rank_mask)) + result[2]++ + continue + if(X.holder.fakekey) + result[2]++ + continue + if(X.is_afk()) + result[3]++ + continue + result[1]++ + return result diff --git a/code/modules/admin/admin_investigate.dm b/code/modules/admin/admin_investigate.dm index 49b2f06d0030..13b786c51748 100644 --- a/code/modules/admin/admin_investigate.dm +++ b/code/modules/admin/admin_investigate.dm @@ -23,18 +23,18 @@ if(!message) return var/F = investigate_subject2file(subject) if(!F) return - investigate_log_subjects |= subject + GLOB.investigate_log_subjects |= subject F << "[time_stamp()] \ref[src] ([x],[y],[z]) || [src] [message]
    " /proc/log_investigate(message, subject) if(!message) return var/F = investigate_subject2file(subject) if(!F) return - investigate_log_subjects |= subject + GLOB.investigate_log_subjects |= subject F << "[time_stamp()] || [message]
    " //ADMINVERBS -/client/proc/investigate_show( subject in investigate_log_subjects ) +/client/proc/investigate_show( subject in GLOB.investigate_log_subjects ) set name = "Investigate" set category = "Admin" if(!holder) return diff --git a/code/modules/admin/admin_memo.dm b/code/modules/admin/admin_memo.dm index 530b9e7958e5..455b38e1e0f0 100644 --- a/code/modules/admin/admin_memo.dm +++ b/code/modules/admin/admin_memo.dm @@ -3,7 +3,7 @@ set category = "Server" if(!check_rights(R_SERVER)) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(src, "Failed to establish database connection.") return var/memotask = input(usr,"Choose task.","Memo") in list("Show","Write","Edit","Remove") @@ -16,13 +16,13 @@ return if(!task) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(src, "Failed to establish database connection.") return var/sql_ckey = sanitizeSQL(src.ckey) switch(task) if("Write") - var/DBQuery/query_memocheck = dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")] WHERE ckey = '[sql_ckey]'") + var/DBQuery/query_memocheck = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")] WHERE ckey = '[sql_ckey]'") if(!query_memocheck.Execute()) var/err = query_memocheck.ErrorMsg() log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n") @@ -35,7 +35,7 @@ return memotext = sanitizeSQL(memotext) var/timestamp = SQLtime() - var/DBQuery/query_memoadd = dbcon.NewQuery("INSERT INTO [format_table_name("memo")] (ckey, memotext, timestamp) VALUES ('[sql_ckey]', '[memotext]', '[timestamp]')") + var/DBQuery/query_memoadd = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("memo")] (ckey, memotext, timestamp) VALUES ('[sql_ckey]', '[memotext]', '[timestamp]')") if(!query_memoadd.Execute()) var/err = query_memoadd.ErrorMsg() log_game("SQL ERROR adding new memo. Error : \[[err]\]\n") @@ -43,7 +43,7 @@ log_admin("[key_name(src)] has set a memo: [memotext]") message_admins("[key_name_admin(src)] has set a memo:
    [memotext]") if("Edit") - var/DBQuery/query_memolist = dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")]") + var/DBQuery/query_memolist = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")]") if(!query_memolist.Execute()) var/err = query_memolist.ErrorMsg() log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n") @@ -59,7 +59,7 @@ if(!target_ckey) return var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_memofind = dbcon.NewQuery("SELECT memotext FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_memofind = GLOB.dbcon.NewQuery("SELECT memotext FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'") if(!query_memofind.Execute()) var/err = query_memofind.ErrorMsg() log_game("SQL ERROR obtaining memotext from memo table. Error : \[[err]\]\n") @@ -72,7 +72,7 @@ new_memo = sanitizeSQL(new_memo) var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from
    [old_memo]
    to
    [new_memo]
    " edit_text = sanitizeSQL(edit_text) - var/DBQuery/update_query = dbcon.NewQuery("UPDATE [format_table_name("memo")] SET memotext = '[new_memo]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/update_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("memo")] SET memotext = '[new_memo]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'") if(!update_query.Execute()) var/err = update_query.ErrorMsg() log_game("SQL ERROR editing memo. Error : \[[err]\]\n") @@ -84,7 +84,7 @@ log_admin("[key_name(src)] has edited [target_sql_ckey]'s memo from \"[old_memo]\" to \"[new_memo]\"") message_admins("[key_name_admin(src)] has edited [target_sql_ckey]'s memo from \"[old_memo]\" to \"[new_memo]\"") if("Show") - var/DBQuery/query_memoshow = dbcon.NewQuery("SELECT ckey, memotext, timestamp, last_editor FROM [format_table_name("memo")]") + var/DBQuery/query_memoshow = GLOB.dbcon.NewQuery("SELECT ckey, memotext, timestamp, last_editor FROM [format_table_name("memo")]") if(!query_memoshow.Execute()) var/err = query_memoshow.ErrorMsg() log_game("SQL ERROR obtaining ckey, memotext, timestamp, last_editor from memo table. Error : \[[err]\]\n") @@ -104,7 +104,7 @@ else if(!silent) to_chat(src, "No memos found in database.") if("Remove") - var/DBQuery/query_memodellist = dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")]") + var/DBQuery/query_memodellist = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("memo")]") if(!query_memodellist.Execute()) var/err = query_memodellist.ErrorMsg() log_game("SQL ERROR obtaining ckey from memo table. Error : \[[err]\]\n") @@ -120,7 +120,7 @@ if(!target_ckey) return var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_memodel = dbcon.NewQuery("DELETE FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_memodel = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("memo")] WHERE ckey = '[target_sql_ckey]'") if(!query_memodel.Execute()) var/err = query_memodel.ErrorMsg() log_game("SQL ERROR removing memo. Error : \[[err]\]\n") diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index e781e0389917..f8a31f885edd 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -1,160 +1,161 @@ -var/list/admin_ranks = list() //list of all ranks with associated rights - -//load our rank - > rights associations -/proc/load_admin_ranks() - admin_ranks.Cut() - - var/previous_rights = 0 - - //load text from file - var/list/Lines = file2list("config/admin_ranks.txt") - - //process each line seperately - for(var/line in Lines) - if(!length(line)) continue - if(copytext(line,1,2) == "#") continue - - var/list/List = splittext(line,"+") - if(!List.len) continue - - var/rank = ckeyEx(List[1]) - switch(rank) - if(null,"") continue - if("Removed") continue //Reserved - - var/rights = 0 - for(var/i=2, i<=List.len, i++) - switch(ckey(List[i])) - if("@","prev") rights |= previous_rights - if("buildmode","build") rights |= R_BUILDMODE - if("admin") rights |= R_ADMIN - if("ban") rights |= R_BAN - if("event") rights |= R_EVENT - if("server") rights |= R_SERVER - if("debug") rights |= R_DEBUG - if("permissions","rights") rights |= R_PERMISSIONS - if("possess") rights |= R_POSSESS - if("stealth") rights |= R_STEALTH - if("rejuv","rejuvinate") rights |= R_REJUVINATE - if("varedit") rights |= R_VAREDIT - if("everything","host","all") rights |= R_HOST - if("sound","sounds") rights |= R_SOUNDS - if("spawn","create") rights |= R_SPAWN - if("mod") rights |= R_MOD - if("mentor") rights |= R_MENTOR - if("proccall") rights |= R_PROCCALL - - admin_ranks[rank] = rights - previous_rights = rights - - #ifdef TESTING - var/msg = "Permission Sets Built:\n" - for(var/rank in admin_ranks) - msg += "\t[rank] - [admin_ranks[rank]]\n" - testing(msg) - #endif - -/hook/startup/proc/loadAdmins() - load_admins() - return 1 - -/proc/load_admins() - //clear the datums references - admin_datums.Cut() - for(var/client/C in GLOB.admins) - C.remove_admin_verbs() - C.holder = null - GLOB.admins.Cut() - - if(config.admin_legacy_system) - load_admin_ranks() - - //load text from file - var/list/Lines = file2list("config/admins.txt") - - //process each line seperately - for(var/line in Lines) - if(!length(line)) continue - if(copytext(line,1,2) == "#") continue - - //Split the line at every "-" - var/list/List = splittext(line, "-") - if(!List.len) continue - - //ckey is before the first "-" - var/ckey = ckey(List[1]) - if(!ckey) continue - - //rank follows the first "-" - var/rank = "" - if(List.len >= 2) - rank = ckeyEx(List[2]) - - //load permissions associated with this rank - var/rights = admin_ranks[rank] - - //create the admin datum and store it for later use - var/datum/admins/D = new /datum/admins(rank, rights, ckey) - - //find the client for a ckey if they are connected and associate them with the new admin datum - D.associate(GLOB.directory[ckey]) - - else - //The current admin system uses SQL - - establish_db_connection() - if(!dbcon.IsConnected()) - log_world("Failed to connect to database in load_admins(). Reverting to legacy system.") - config.admin_legacy_system = 1 - load_admins() - return - - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM [format_table_name("admin")]") - query.Execute() - while(query.NextRow()) - var/ckey = query.item[1] - var/rank = query.item[2] - if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes. - - var/rights = query.item[4] - if(istext(rights)) rights = text2num(rights) - var/datum/admins/D = new /datum/admins(rank, rights, ckey) - - //find the client for a ckey if they are connected and associate them with the new admin datum - D.associate(GLOB.directory[ckey]) - if(!admin_datums) - log_world("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") - config.admin_legacy_system = 1 - load_admins() - return - - #ifdef TESTING - var/msg = "Admins Built:\n" - for(var/ckey in admin_datums) - var/rank - var/datum/admins/D = admin_datums[ckey] - if(D) rank = D.rank - msg += "\t[ckey] - [rank]\n" - testing(msg) - #endif - - -#ifdef TESTING -/client/verb/changerank(newrank in admin_ranks) - if(holder) - holder.rank = newrank - holder.rights = admin_ranks[newrank] - else - holder = new /datum/admins(newrank,admin_ranks[newrank],ckey) - remove_admin_verbs() - holder.associate(src) - -/client/verb/changerights(newrights as num) - if(holder) - holder.rights = newrights - else - holder = new /datum/admins("testing",newrights,ckey) - remove_admin_verbs() - holder.associate(src) - -#endif +GLOBAL_LIST_EMPTY(admin_ranks) //list of all ranks with associated rights +GLOBAL_PROTECT(admin_ranks) // this shit is being protected for obvious reasons + +//load our rank - > rights associations +/proc/load_admin_ranks() + GLOB.admin_ranks.Cut() + + var/previous_rights = 0 + + //load text from file + var/list/Lines = file2list("config/admin_ranks.txt") + + //process each line seperately + for(var/line in Lines) + if(!length(line)) continue + if(copytext(line,1,2) == "#") continue + + var/list/List = splittext(line,"+") + if(!List.len) continue + + var/rank = ckeyEx(List[1]) + switch(rank) + if(null,"") continue + if("Removed") continue //Reserved + + var/rights = 0 + for(var/i=2, i<=List.len, i++) + switch(ckey(List[i])) + if("@","prev") rights |= previous_rights + if("buildmode","build") rights |= R_BUILDMODE + if("admin") rights |= R_ADMIN + if("ban") rights |= R_BAN + if("event") rights |= R_EVENT + if("server") rights |= R_SERVER + if("debug") rights |= R_DEBUG + if("permissions","rights") rights |= R_PERMISSIONS + if("possess") rights |= R_POSSESS + if("stealth") rights |= R_STEALTH + if("rejuv","rejuvinate") rights |= R_REJUVINATE + if("varedit") rights |= R_VAREDIT + if("everything","host","all") rights |= R_HOST + if("sound","sounds") rights |= R_SOUNDS + if("spawn","create") rights |= R_SPAWN + if("mod") rights |= R_MOD + if("mentor") rights |= R_MENTOR + if("proccall") rights |= R_PROCCALL + + GLOB.admin_ranks[rank] = rights + previous_rights = rights + + #ifdef TESTING + var/msg = "Permission Sets Built:\n" + for(var/rank in admin_ranks) + msg += "\t[rank] - [GLOB.admin_ranks[rank]]\n" + testing(msg) + #endif + +/hook/startup/proc/loadAdmins() + load_admins() + return 1 + +/proc/load_admins() + //clear the datums references + GLOB.admin_datums.Cut() + for(var/client/C in GLOB.admins) + C.remove_admin_verbs() + C.holder = null + GLOB.admins.Cut() + + if(config.admin_legacy_system) + load_admin_ranks() + + //load text from file + var/list/Lines = file2list("config/admins.txt") + + //process each line seperately + for(var/line in Lines) + if(!length(line)) continue + if(copytext(line,1,2) == "#") continue + + //Split the line at every "-" + var/list/List = splittext(line, "-") + if(!List.len) continue + + //ckey is before the first "-" + var/ckey = ckey(List[1]) + if(!ckey) continue + + //rank follows the first "-" + var/rank = "" + if(List.len >= 2) + rank = ckeyEx(List[2]) + + //load permissions associated with this rank + var/rights = GLOB.admin_ranks[rank] + + //create the admin datum and store it for later use + var/datum/admins/D = new /datum/admins(rank, rights, ckey) + + //find the client for a ckey if they are connected and associate them with the new admin datum + D.associate(GLOB.directory[ckey]) + + else + //The current admin system uses SQL + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + log_world("Failed to connect to database in load_admins(). Reverting to legacy system.") + config.admin_legacy_system = 1 + load_admins() + return + + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT ckey, rank, level, flags FROM [format_table_name("admin")]") + query.Execute() + while(query.NextRow()) + var/ckey = query.item[1] + var/rank = query.item[2] + if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes. + + var/rights = query.item[4] + if(istext(rights)) rights = text2num(rights) + var/datum/admins/D = new /datum/admins(rank, rights, ckey) + + //find the client for a ckey if they are connected and associate them with the new admin datum + D.associate(GLOB.directory[ckey]) + if(!GLOB.admin_datums) + log_world("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.") + config.admin_legacy_system = 1 + load_admins() + return + + #ifdef TESTING + var/msg = "Admins Built:\n" + for(var/ckey in GLOB.admin_datums) + var/rank + var/datum/admins/D = GLOB.admin_datums[ckey] + if(D) rank = D.rank + msg += "\t[ckey] - [rank]\n" + testing(msg) + #endif + + +#ifdef TESTING +/client/verb/changerank(newrank in admin_ranks) + if(holder) + holder.rank = newrank + holder.rights = admin_ranks[newrank] + else + holder = new /datum/admins(newrank,admin_ranks[newrank],ckey) + remove_admin_verbs() + holder.associate(src) + +/client/verb/changerights(newrights as num) + if(holder) + holder.rights = newrights + else + holder = new /datum/admins("testing",newrights,ckey) + remove_admin_verbs() + holder.associate(src) + +#endif diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index b77eea08b7bd..34929b52f531 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1,1018 +1,1019 @@ -//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless -var/list/admin_verbs_default = list( - /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ - /client/proc/hide_verbs, /*hides all our adminverbs*/ - /client/proc/toggleadminhelpsound, - /client/proc/togglementorhelpsound, - /client/proc/cmd_mentor_check_new_players, - /client/proc/cmd_mentor_check_player_exp /* shows players by playtime */ - ) -var/list/admin_verbs_admin = list( - /client/proc/check_antagonists, /*shows all antags*/ - /datum/admins/proc/show_player_panel, - /client/proc/player_panel, /*shows an interface for all players, with links to various panels (old style)*/ - /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ - /client/proc/invisimin, /*allows our mob to go invisible/visible*/ - /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ - /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ - /datum/admins/proc/announce, /*priority announce something to all clients.*/ - /client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/ - /client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/ - /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ - /client/proc/toggle_view_range, /*changes how far we can see*/ - /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ - /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ - /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ - /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ - /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ - /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ - /client/proc/getserverlogs, /*allows us to fetch server logs (diary) for other days*/ - /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ - /client/proc/Getmob, /*teleports a mob to our location*/ - /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ - /client/proc/Jump, - /client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/ - /client/proc/jumptomob, /*allows us to jump to a specific mob*/ - /client/proc/jumptoturf, /*allows us to jump to a specific turf*/ - /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/ - /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcomm*/ - /client/proc/check_ai_laws, /*shows AI and borg laws*/ - /client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */ - /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ - /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ - /client/proc/toggleprayers, /*toggles prayers on/off*/ - /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ - /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ - /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ - /datum/admins/proc/togglelooc, /*toggles looc on/off for everyone*/ - /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/ - /datum/admins/proc/toggledsay, /*toggles dsay on/off for everyone*/ - /datum/admins/proc/toggleemoji, /*toggles using emoji in ooc for everyone*/ - /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ - /client/proc/cmd_admin_say, /*admin-only ooc chat*/ - /datum/admins/proc/PlayerNotes, - /client/proc/cmd_mentor_say, - /datum/admins/proc/show_player_notes, - /datum/admins/proc/vpn_whitelist, - /client/proc/free_slot, /*frees slot for chosen job*/ - /client/proc/toggleattacklogs, - /client/proc/toggleadminlogs, - /client/proc/toggledebuglogs, - /client/proc/update_mob_sprite, - /client/proc/toggledrones, - /client/proc/man_up, - /client/proc/global_man_up, - /client/proc/delbook, - /client/proc/view_flagged_books, - /client/proc/empty_ai_core_toggle_latejoin, - /client/proc/aooc, - /client/proc/freeze, - /client/proc/freezemecha, - /client/proc/alt_check, - /client/proc/secrets, - /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ - /client/proc/change_human_appearance_self, /* Allows the human-based mob itself to change its basic appearance */ - /client/proc/debug_variables, - /client/proc/reset_all_tcs, /*resets all telecomms scripts*/ - /client/proc/toggle_mentor_chat, - /client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/ - /client/proc/list_ssds_afks, - /client/proc/cmd_admin_headset_message, - /client/proc/spawn_floor_cluwne -) -var/list/admin_verbs_ban = list( - /client/proc/unban_panel, - /client/proc/jobbans, - /client/proc/stickybanpanel - ) -var/list/admin_verbs_sounds = list( - /client/proc/play_local_sound, - /client/proc/play_sound, - /client/proc/play_server_sound, - /client/proc/play_intercomm_sound, - /client/proc/stop_global_admin_sounds - ) -var/list/admin_verbs_event = list( - /client/proc/object_talk, - /client/proc/cmd_admin_dress, - /client/proc/cmd_admin_gib_self, - /client/proc/drop_bomb, - /client/proc/cinematic, - /client/proc/one_click_antag, - /datum/admins/proc/toggle_aliens, - /client/proc/cmd_admin_add_freeform_ai_law, - /client/proc/cmd_admin_add_random_ai_law, - /client/proc/make_sound, - /client/proc/toggle_random_events, - /client/proc/toggle_random_events, - /client/proc/toggle_ert_calling, - /client/proc/show_tip, - /client/proc/cmd_admin_change_custom_event, - /datum/admins/proc/access_news_network, /*allows access of newscasters*/ - /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ - /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ - /client/proc/response_team, // Response Teams admin verb - /client/proc/cmd_admin_create_centcom_report, - /client/proc/fax_panel, - /client/proc/event_manager_panel, - /client/proc/modify_goals, - /client/proc/outfit_manager - ) - -var/list/admin_verbs_spawn = list( - /datum/admins/proc/spawn_atom, /*allows us to spawn instances*/ - /client/proc/respawn_character, - /client/proc/admin_deserialize - ) -var/list/admin_verbs_server = list( - /client/proc/ToRban, - /client/proc/Set_Holiday, - /datum/admins/proc/startnow, - /datum/admins/proc/restart, - /datum/admins/proc/delay, - /datum/admins/proc/toggleaban, - /client/proc/toggle_log_hrefs, - /client/proc/everyone_random, - /datum/admins/proc/toggleAI, - /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ - /client/proc/cmd_debug_del_all, - /client/proc/cmd_debug_del_sing, - /datum/admins/proc/toggle_aliens, - /client/proc/delbook, - /client/proc/view_flagged_books, - /client/proc/toggle_antagHUD_use, - /client/proc/toggle_antagHUD_restrictions, - /client/proc/set_ooc, - /client/proc/reset_ooc - ) -var/list/admin_verbs_debug = list( - /client/proc/cmd_admin_list_open_jobs, - /client/proc/Debug2, - /client/proc/cmd_debug_make_powernets, - /client/proc/debug_controller, - /client/proc/cmd_debug_mob_lists, - /client/proc/cmd_admin_delete, - /client/proc/cmd_debug_del_all, - /client/proc/cmd_debug_del_sing, - /client/proc/reload_admins, - /client/proc/restart_controller, - /client/proc/enable_debug_verbs, - /client/proc/toggledebuglogs, - /client/proc/cmd_display_del_log, - /client/proc/cmd_display_del_log_simple, - /client/proc/debugNatureMapGenerator, - /client/proc/check_bomb_impacts, - /client/proc/test_movable_UI, - /client/proc/test_snap_UI, - /client/proc/cinematic, - /proc/machine_upgrade, - /client/proc/map_template_load, - /client/proc/map_template_upload, - /client/proc/view_runtimes, - /client/proc/admin_serialize, - /client/proc/jump_to_ruin, - /client/proc/toggle_medal_disable, - ) -var/list/admin_verbs_possess = list( - /proc/possess, - /proc/release - ) -var/list/admin_verbs_permissions = list( - /client/proc/edit_admin_permissions, - /client/proc/create_poll, - /client/proc/big_brother - ) -var/list/admin_verbs_rejuv = list( - /client/proc/respawn_character, - /client/proc/cmd_admin_rejuvenate - ) -var/list/admin_verbs_mod = list( - /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ - /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ - /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ - /datum/admins/proc/PlayerNotes, - /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ - /client/proc/cmd_mentor_say, - /datum/admins/proc/show_player_notes, - /client/proc/player_panel_new, - /client/proc/dsay, - /datum/admins/proc/show_player_panel, - /client/proc/jobbans, - /client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ -) -var/list/admin_verbs_mentor = list( - /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ - /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ - /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ - /client/proc/openMentorTicketUI, - /client/proc/toggleMentorTicketLogs, - /client/proc/cmd_mentor_say /* mentor say*/ - // cmd_mentor_say is added/removed by the toggle_mentor_chat verb -) -var/list/admin_verbs_proccall = list( - /client/proc/callproc, - /client/proc/callproc_datum, - /client/proc/SDQL2_query -) -var/list/admin_verbs_ticket = list( - /client/proc/openAdminTicketUI, - /client/proc/toggleticketlogs, - /client/proc/openMentorTicketUI, - /client/proc/toggleMentorTicketLogs, - /client/proc/resolveAllAdminTickets, - /client/proc/resolveAllMentorTickets -) - -/client/proc/on_holder_add() - if(chatOutput && chatOutput.loaded) - chatOutput.loadAdmin() - -/client/proc/add_admin_verbs() - if(holder) - verbs += admin_verbs_default - if(holder.rights & R_BUILDMODE) - verbs += /client/proc/togglebuildmodeself - if(holder.rights & R_ADMIN) - verbs += admin_verbs_admin - verbs += admin_verbs_ticket - spawn(1) - control_freak = 0 - if(holder.rights & R_BAN) - verbs += admin_verbs_ban - if(holder.rights & R_EVENT) - verbs += admin_verbs_event - if(holder.rights & R_SERVER) - verbs += admin_verbs_server - if(holder.rights & R_DEBUG) - verbs += admin_verbs_debug - if(holder.rights & R_POSSESS) - verbs += admin_verbs_possess - if(holder.rights & R_PERMISSIONS) - verbs += admin_verbs_permissions - if(holder.rights & R_STEALTH) - verbs += /client/proc/stealth - if(holder.rights & R_REJUVINATE) - verbs += admin_verbs_rejuv - if(holder.rights & R_SOUNDS) - verbs += admin_verbs_sounds - if(holder.rights & R_SPAWN) - verbs += admin_verbs_spawn - if(holder.rights & R_MOD) - verbs += admin_verbs_mod - if(holder.rights & R_MENTOR) - verbs += admin_verbs_mentor - if(holder.rights & R_PROCCALL) - verbs += admin_verbs_proccall - -/client/proc/remove_admin_verbs() - verbs.Remove( - admin_verbs_default, - /client/proc/togglebuildmodeself, - admin_verbs_admin, - admin_verbs_ban, - admin_verbs_event, - admin_verbs_server, - admin_verbs_debug, - admin_verbs_possess, - admin_verbs_permissions, - /client/proc/stealth, - admin_verbs_rejuv, - admin_verbs_sounds, - admin_verbs_spawn, - admin_verbs_mod, - admin_verbs_mentor, - admin_verbs_proccall, - admin_verbs_show_debug_verbs, - /client/proc/readmin, - admin_verbs_ticket - ) - -/client/proc/hide_verbs() - set name = "Adminverbs - Hide All" - set category = "Admin" - - if(!holder) - return - - remove_admin_verbs() - verbs += /client/proc/show_verbs - - to_chat(src, "Almost all of your adminverbs have been hidden.") - feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/show_verbs() - set name = "Adminverbs - Show" - set category = "Admin" - - if(!holder) - return - - verbs -= /client/proc/show_verbs - add_admin_verbs() - - to_chat(src, "All of your adminverbs are now visible.") - feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/admin_ghost() - set category = "Admin" - set name = "Aghost" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(istype(mob,/mob/dead/observer)) - //re-enter - var/mob/dead/observer/ghost = mob - ghost.can_reenter_corpse = 1 //just in-case. - ghost.reenter_corpse() - log_admin("[key_name(usr)] re-entered their body") - feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - else if(istype(mob,/mob/new_player)) - to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.") - else - //ghostize - var/mob/body = mob - body.ghostize(1) - if(body && !body.key) - body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus - log_admin("[key_name(usr)] has admin-ghosted") - feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/invisimin() - set name = "Invisimin" - set category = "Admin" - set desc = "Toggles ghost-like invisibility (Don't abuse this)" - - if(!check_rights(R_ADMIN)) - return - - if(mob) - if(mob.invisibility == INVISIBILITY_OBSERVER) - mob.invisibility = initial(mob.invisibility) - to_chat(mob, "Invisimin off. Invisibility reset.") - mob.add_to_all_human_data_huds() - //TODO: Make some kind of indication for the badmin that they are currently invisible - else - mob.invisibility = INVISIBILITY_OBSERVER - to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") - mob.remove_from_all_data_huds() - -/client/proc/player_panel() - set name = "Player Panel" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.player_panel_old() - feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/player_panel_new() - set name = "Player Panel New" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - holder.player_panel_new() - feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/check_antagonists() - set name = "Check Antagonists" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.check_antagonists() - log_admin("[key_name(usr)] checked antagonists") - feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/jobbans() - set name = "Display Job bans" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(config.ban_legacy_system) - holder.Jobbans() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/unban_panel() - set name = "Unban Panel" - set category = "Admin" - - if(!check_rights(R_BAN)) - return - - if(config.ban_legacy_system) - holder.unbanpanel() - else - holder.DB_ban_panel() - feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/game_panel() - set name = "Game Panel" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.Game() - feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/secrets() - set name = "Secrets" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.Secrets() - feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/findStealthKey(txt) - if(txt) - for(var/P in GLOB.stealthminID) - if(GLOB.stealthminID[P] == txt) - return P - txt = GLOB.stealthminID[ckey] - return txt - -/client/proc/createStealthKey() - var/num = (rand(0,1000)) - var/i = 0 - while(i == 0) - i = 1 - for(var/P in GLOB.stealthminID) - if(num == GLOB.stealthminID[P]) - num++ - i = 0 - GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" - -/client/proc/stealth() - set category = "Admin" - set name = "Stealth Mode" - - if(!check_rights(R_ADMIN)) - return - - if(holder) - holder.big_brother = 0 - if(holder.fakekey) - holder.fakekey = null - else - var/new_key = ckeyEx(clean_input("Enter your desired display name.", "Fake Key", key)) - if(!new_key) return - if(length(new_key) >= 26) - new_key = copytext(new_key, 1, 26) - holder.fakekey = new_key - createStealthKey() - log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") - message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]", 1) - feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/big_brother() - set category = "Admin" - set name = "Big Brother Mode" - - if(!check_rights(R_PERMISSIONS)) - return - - if(holder) - if(holder.fakekey) - holder.fakekey = null - holder.big_brother = 0 - else - var/new_key = ckeyEx(clean_input("Enter your desired display name. Unlike normal stealth mode, this will not appear in Who at all, except for other heads.", "Fake Key", key)) - if(!new_key) - return - if(length(new_key) >= 26) - new_key = copytext(new_key, 1, 26) - holder.fakekey = new_key - holder.big_brother = 1 - createStealthKey() - log_admin("[key_name(usr)] has turned BB mode [holder.fakekey ? "ON" : "OFF"]") - feedback_add_details("admin_verb","BBSM") - -#define MAX_WARNS 3 -#define AUTOBANTIME 10 - -/client/proc/warn(warned_ckey) - if(!check_rights(R_ADMIN)) - return - - if(!warned_ckey || !istext(warned_ckey)) return - if(warned_ckey in admin_datums) - to_chat(usr, "Error: warn(): You can't warn admins.") - return - - var/datum/preferences/D - var/client/C = GLOB.directory[warned_ckey] - if(C) D = C.prefs - else D = preferences_datums[warned_ckey] - - if(!D) - to_chat(src, "Error: warn(): No such ckey found.") - return - - if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) - ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") - if(C) - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban") - log_admin("[key_name(src)] has warned [key_name(C)] resulting in a [AUTOBANTIME] minute ban") - to_chat(C, "You have been autobanned due to a warning by [ckey].
    This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.") - del(C) - else - message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban") - log_admin("[key_name(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban") - AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME) - feedback_inc("ban_warn",1) - else - if(C) - to_chat(C, "You have been formally warned by an administrator.
    Further warnings will result in an autoban.
    ") - message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") - log_admin("[key_name(src)] has warned [key_name(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") - else - message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") - log_admin("[key_name(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") - - feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -#undef MAX_WARNS -#undef AUTOBANTIME - -/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE - set category = "Event" - set name = "Drop Bomb" - set desc = "Cause an explosion of varying strength at your location." - - if(!check_rights(R_EVENT)) - return - - var/turf/epicenter = mob.loc - var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb") - var/choice = input("What size explosion would you like to produce?") as null|anything in choices - switch(choice) - if(null) - return 0 - if("Small Bomb") - explosion(epicenter, 1, 2, 3, 3) - if("Medium Bomb") - explosion(epicenter, 2, 3, 4, 4) - if("Big Bomb") - explosion(epicenter, 3, 5, 7, 5) - if("Custom Bomb") - var/devastation_range = input("Devastation range (in tiles):") as null|num - if(devastation_range == null) - return - var/heavy_impact_range = input("Heavy impact range (in tiles):") as null|num - if(heavy_impact_range == null) - return - var/light_impact_range = input("Light impact range (in tiles):") as null|num - if(light_impact_range == null) - return - var/flash_range = input("Flash range (in tiles):") as null|num - if(flash_range == null) - return - explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, 1, 1) - log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc]") - message_admins("[key_name_admin(usr)] created an admin explosion at [epicenter.loc]") - feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/give_spell(mob/T as mob in GLOB.mob_list) // -- Urist - set category = "Event" - set name = "Give Spell" - set desc = "Gives a spell to a mob." - - if(!check_rights(R_EVENT)) - return - - var/list/spell_list = list() - var/type_length = length("/obj/effect/proc_holder/spell") + 2 - for(var/A in spells) - spell_list[copytext("[A]", type_length)] = A - var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list - if(!S) - return - S = spell_list[S] - if(T.mind) - T.mind.AddSpell(new S) - else - T.AddSpell(new S) - - feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].", 1) - -/client/proc/give_disease(mob/T in GLOB.mob_list) - set category = "Event" - set name = "Give Disease" - set desc = "Gives a Disease to a mob." - var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in diseases - if(!D) return - T.ForceContractDisease(new D) - feedback_add_details("admin_verb","GD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [D].") - -/client/proc/make_sound(var/obj/O in view()) // -- TLE - set category = "Event" - set name = "Make Sound" - set desc = "Display a message to everyone who can hear the target" - - if(!check_rights(R_EVENT)) - return - - if(O) - var/message = clean_input("What do you want the message to be?", "Make Sound") - if(!message) - return - for(var/mob/V in hearers(O)) - V.show_message(admin_pencode_to_html(message), 2) - log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] make a sound") - message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z] make a sound") - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/togglebuildmodeself() - set name = "Toggle Build Mode Self" - set category = "Event" - - if(!check_rights(R_EVENT)) - return - - if(src.mob) - togglebuildmode(src.mob) - feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/object_talk(var/msg as text) // -- TLE - set category = "Event" - set name = "oSay" - set desc = "Display a message to everyone who can hear the target" - - if(!check_rights(R_EVENT)) - return - - if(mob.control_object) - if(!msg) - return - for(var/mob/V in hearers(mob.control_object)) - V.show_message("[mob.control_object.name] says: \"" + msg + "\"", 2) - log_admin("[key_name(usr)] used oSay on [mob.control_object]: [msg]") - message_admins("[key_name_admin(usr)] used oSay on [mob.control_object]: [msg]") - - feedback_add_details("admin_verb","OT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/deadmin_self() - set name = "De-admin self" - set category = "Admin" - - if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) - return - - log_admin("[key_name(usr)] deadmined themself.") - message_admins("[key_name_admin(usr)] deadmined themself.") - deadmin() - verbs += /client/proc/readmin - GLOB.deadmins += ckey - to_chat(src, "You are now a normal player.") - feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/readmin() - set name = "Re-admin self" - set category = "Admin" - set desc = "Regain your admin powers." - - var/datum/admins/D = admin_datums[ckey] - var/rank = null - if(config.admin_legacy_system) - //load text from file - var/list/Lines = file2list("config/admins.txt") - for(var/line in Lines) - var/list/splitline = splittext(line, " - ") - if(lowertext(splitline[1]) == ckey) - if(splitline.len >= 2) - rank = ckeyEx(splitline[2]) - break - continue - else - if(!dbcon.IsConnected()) - message_admins("Warning, MySQL database is not connected.") - to_chat(src, "Warning, MYSQL database is not connected.") - return - var/sql_ckey = sanitizeSQL(ckey) - var/DBQuery/query = dbcon.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[sql_ckey]'") - query.Execute() - while(query.NextRow()) - rank = ckeyEx(query.item[1]) - if(!D) - if(config.admin_legacy_system) - if(admin_ranks[rank] == null) - error("Error while re-adminning [src], admin rank ([rank]) does not exist.") - to_chat(src, "Error while re-adminning, admin rank ([rank]) does not exist.") - return - - D = new(rank, admin_ranks[rank], ckey) - else - var/sql_ckey = sanitizeSQL(ckey) - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, flags FROM [format_table_name("admin")] WHERE ckey = '[sql_ckey]'") - query.Execute() - while(query.NextRow()) - var/admin_ckey = query.item[1] - var/admin_rank = query.item[2] - var/flags = query.item[3] - if(!admin_ckey) - to_chat(src, "Error while re-adminning, ckey [admin_ckey] was not found in the admin database.") - return - if(admin_rank == "Removed") //This person was de-adminned. They are only in the admin list for archive purposes. - to_chat(src, "Error while re-adminning, ckey [admin_ckey] is not an admin.") - return - - if(istext(flags)) - flags = text2num(flags) - D = new(admin_rank, flags, ckey) - - var/client/C = GLOB.directory[ckey] - D.associate(C) - message_admins("[key_name_admin(usr)] re-adminned themselves.") - log_admin("[key_name(usr)] re-adminned themselves.") - GLOB.deadmins -= ckey - feedback_add_details("admin_verb","RAS") - return - else - to_chat(src, "You are already an admin.") - verbs -= /client/proc/readmin - GLOB.deadmins -= ckey - return - -/client/proc/toggle_log_hrefs() - set name = "Toggle href logging" - set category = "Server" - - if(!check_rights(R_SERVER)) - return - - if(config) - if(config.log_hrefs) - config.log_hrefs = 0 - to_chat(src, "Stopped logging hrefs") - else - config.log_hrefs = 1 - to_chat(src, "Started logging hrefs") - -/client/proc/check_ai_laws() - set name = "Check AI Laws" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - holder.output_ai_laws() - -/client/proc/manage_silicon_laws() - set name = "Manage Silicon Laws" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - var/mob/living/silicon/S = input("Select silicon.", "Manage Silicon Laws") as null|anything in GLOB.silicon_mob_list - if(!S) return - - var/datum/nano_module/law_manager/L = new(S) - L.ui_interact(usr, state = admin_state) - log_and_message_admins("has opened [S]'s law manager.") - feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/change_human_appearance_admin(mob/living/carbon/human/H in GLOB.mob_list) - set name = "C.M.A. - Admin" - set desc = "Allows you to change the mob appearance" - set category = null - - if(!check_rights(R_ADMIN)) - return - - if(!istype(H)) - if(istype(H, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/B = H - if(istype(B.container, /obj/item/mmi/robotic_brain/positronic)) - var/obj/item/mmi/robotic_brain/positronic/C = B.container - var/obj/item/organ/internal/brain/mmi_holder/posibrain/P = C.loc - if(ishuman(P.owner)) - H = P.owner - else - return - else - return - - if(holder) - admin_log_and_message_admins("is altering the appearance of [H].") - H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0) - feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/change_human_appearance_self(mob/living/carbon/human/H in GLOB.mob_list) - set name = "C.M.A. - Self" - set desc = "Allows the mob to change its appearance" - set category = null - - if(!check_rights(R_ADMIN)) - return - - if(!istype(H)) - if(istype(H, /mob/living/carbon/brain)) - var/mob/living/carbon/brain/B = H - if(istype(B.container, /obj/item/mmi/robotic_brain/positronic)) - var/obj/item/mmi/robotic_brain/positronic/C = B.container - var/obj/item/organ/internal/brain/mmi_holder/posibrain/P = C.loc - if(ishuman(P.owner)) - H = P.owner - else - return - else - return - - if(!H.client) - to_chat(usr, "Only mobs with clients can alter their own appearance.") - return - - switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel")) - if("Yes") - admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, without whitelisting of races.") - H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 0) - if("No") - admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, with whitelisting of races.") - H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1) - feedback_add_details("admin_verb","CMAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/free_slot() - set name = "Free Job Slot" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - var/list/jobs = list() - for(var/datum/job/J in SSjobs.occupations) - if(J.current_positions >= J.total_positions && J.total_positions != -1) - jobs += J.title - if(!jobs.len) - to_chat(usr, "There are no fully staffed jobs.") - return - var/job = input("Please select job slot to free", "Free Job Slot") as null|anything in jobs - if(job) - SSjobs.FreeRole(job) - log_admin("[key_name(usr)] has freed a job slot for [job].") - message_admins("[key_name_admin(usr)] has freed a job slot for [job].") - -/client/proc/toggleattacklogs() - set name = "Toggle Attack Log Messages" - set category = "Preferences" - - if(!check_rights(R_ADMIN)) - return - - if(prefs.atklog == ATKLOG_ALL) - prefs.atklog = ATKLOG_ALMOSTALL - to_chat(usr, "Your attack logs preference is now: show ALMOST ALL attack logs (notable exceptions: NPCs attacking other NPCs, vampire bites, equipping/stripping, people pushing each other over)") - else if(prefs.atklog == ATKLOG_ALMOSTALL) - prefs.atklog = ATKLOG_MOST - to_chat(usr, "Your attack logs preference is now: show MOST attack logs (like ALMOST ALL, except that it also hides player v. NPC combat, and certain areas like lavaland syndie base and thunderdome)") - else if(prefs.atklog == ATKLOG_MOST) - prefs.atklog = ATKLOG_FEW - to_chat(usr, "Your attack logs preference is now: show FEW attack logs (only the most important stuff: attacks on SSDs, use of explosives, messing with the engine, gibbing, AI wiping, forcefeeding, acid sprays, and organ extraction)") - else if(prefs.atklog == ATKLOG_FEW) - prefs.atklog = ATKLOG_NONE - to_chat(usr, "Your attack logs preference is now: show NO attack logs") - else if(prefs.atklog == ATKLOG_NONE) - prefs.atklog = ATKLOG_ALL - to_chat(usr, "Your attack logs preference is now: show ALL attack logs") - else - prefs.atklog = ATKLOG_ALL - to_chat(usr, "Your attack logs preference is now: show ALL attack logs (your preference was set to an invalid value, it has been reset)") - - prefs.save_preferences(src) - - -/client/proc/toggleadminlogs() - set name = "Toggle Admin Log Messages" - set category = "Preferences" - - if(!check_rights(R_ADMIN)) - return - - prefs.toggles ^= CHAT_NO_ADMINLOGS - prefs.save_preferences(src) - if(prefs.toggles & CHAT_NO_ADMINLOGS) - to_chat(usr, "You now won't get admin log messages.") - else - to_chat(usr, "You now will get admin log messages.") - -/client/proc/toggleMentorTicketLogs() - set name = "Toggle Mentor Ticket Messages" - set category = "Preferences" - - if(!check_rights(R_MENTOR|R_ADMIN)) - return - - prefs.toggles ^= CHAT_NO_MENTORTICKETLOGS - prefs.save_preferences(src) - if(prefs.toggles & CHAT_NO_MENTORTICKETLOGS) - to_chat(usr, "You now won't get mentor ticket messages.") - else - to_chat(usr, "You now will get mentor ticket messages.") - -/client/proc/toggleticketlogs() - set name = "Toggle Admin Ticket Messgaes" - set category = "Preferences" - - if(!check_rights(R_ADMIN)) - return - - prefs.toggles ^= CHAT_NO_TICKETLOGS - prefs.save_preferences(src) - if(prefs.toggles & CHAT_NO_TICKETLOGS) - to_chat(usr, "You now won't get admin ticket messages.") - else - to_chat(usr, "You now will get admin ticket messages.") - -/client/proc/toggledrones() - set name = "Toggle Maintenance Drones" - set category = "Server" - - if(!check_rights(R_SERVER)) - return - - config.allow_drone_spawn = !(config.allow_drone_spawn) - log_admin("[key_name(usr)] has [config.allow_drone_spawn ? "enabled" : "disabled"] maintenance drones.") - message_admins("[key_name_admin(usr)] has [config.allow_drone_spawn ? "enabled" : "disabled"] maintenance drones.") - -/client/proc/toggledebuglogs() - set name = "Toggle Debug Log Messages" - set category = "Preferences" - - if(!check_rights(R_DEBUG)) - return - - prefs.toggles ^= CHAT_DEBUGLOGS - prefs.save_preferences(src) - if(prefs.toggles & CHAT_DEBUGLOGS) - to_chat(usr, "You now will get debug log messages") - else - to_chat(usr, "You now won't get debug log messages") - -/client/proc/man_up(mob/T as mob in GLOB.mob_list) - set category = "Admin" - set name = "Man Up" - set desc = "Tells mob to man up and deal with it." - - if(!check_rights(R_ADMIN)) - return - - to_chat(T, "Man up and deal with it.") - to_chat(T, "Move on.") - T << 'sound/voice/manup1.ogg' - - log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") - message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.") - -/client/proc/global_man_up() - set category = "Admin" - set name = "Man Up Global" - set desc = "Tells everyone to man up and deal with it." - - if(!check_rights(R_ADMIN)) - return - - var/confirm = alert("Are you sure you want to send the global message?", "Confirm Man Up Global", "Yes", "No") - - if(confirm == "Yes") - for(var/mob/T as mob in GLOB.mob_list) - to_chat(T, "
    Man up.
    Deal with it.

    Move on.

    ") - T << 'sound/voice/manup1.ogg' - - log_admin("[key_name(usr)] told everyone to man up and deal with it.") - message_admins("[key_name_admin(usr)] told everyone to man up and deal with it.") - -/client/proc/toggle_advanced_interaction() - set name = "Toggle Advanced Admin Interaction" - set category = "Admin" - set desc = "Allows you to interact with atoms such as buttons and doors, on top of regular machinery interaction." - - if(!check_rights(R_ADMIN)) - return - - advanced_admin_interaction = !advanced_admin_interaction - - log_admin("[key_name(usr)] has [advanced_admin_interaction ? "activated" : "deactivated"] their advanced admin interaction.") - message_admins("[key_name_admin(usr)] has [advanced_admin_interaction ? "activated" : "deactivated"] their advanced admin interaction.") +//admin verb groups - They can overlap if you so wish. Only one of each verb will exist in the verbs list regardless +GLOBAL_LIST_INIT(admin_verbs_default, list( + /client/proc/deadmin_self, /*destroys our own admin datum so we can play as a regular player*/ + /client/proc/hide_verbs, /*hides all our adminverbs*/ + /client/proc/toggleadminhelpsound, + /client/proc/togglementorhelpsound, + /client/proc/cmd_mentor_check_new_players, + /client/proc/cmd_mentor_check_player_exp /* shows players by playtime */ + )) +GLOBAL_LIST_INIT(admin_verbs_admin, list( + /client/proc/check_antagonists, /*shows all antags*/ + /datum/admins/proc/show_player_panel, + /client/proc/player_panel, /*shows an interface for all players, with links to various panels (old style)*/ + /client/proc/player_panel_new, /*shows an interface for all players, with links to various panels*/ + /client/proc/invisimin, /*allows our mob to go invisible/visible*/ + /datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/ + /datum/admins/proc/toggleguests, /*toggles whether guests can join the current game*/ + /datum/admins/proc/announce, /*priority announce something to all clients.*/ + /client/proc/colorooc, /*allows us to set a custom colour for everything we say in ooc*/ + /client/proc/resetcolorooc, /*allows us to set a reset our ooc color*/ + /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ + /client/proc/toggle_view_range, /*changes how far we can see*/ + /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ + /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ + /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ + /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ + /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ + /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ + /client/proc/cmd_admin_open_logging_view, + /client/proc/getserverlogs, /*allows us to fetch server logs (diary) for other days*/ + /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ + /client/proc/Getmob, /*teleports a mob to our location*/ + /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ + /client/proc/Jump, + /client/proc/jumptokey, /*allows us to jump to the location of a mob with a certain ckey*/ + /client/proc/jumptomob, /*allows us to jump to a specific mob*/ + /client/proc/jumptoturf, /*allows us to jump to a specific turf*/ + /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/ + /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcomm*/ + /client/proc/check_ai_laws, /*shows AI and borg laws*/ + /client/proc/manage_silicon_laws, /* Allows viewing and editing silicon laws. */ + /client/proc/admin_memo, /*admin memo system. show/delete/write. +SERVER needed to delete admin memos of others*/ + /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ + /client/proc/toggleprayers, /*toggles prayers on/off*/ + /client/proc/toggle_hear_radio, /*toggles whether we hear the radio*/ + /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ + /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ + /datum/admins/proc/togglelooc, /*toggles looc on/off for everyone*/ + /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/ + /datum/admins/proc/toggledsay, /*toggles dsay on/off for everyone*/ + /datum/admins/proc/toggleemoji, /*toggles using emoji in ooc for everyone*/ + /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ + /client/proc/cmd_admin_say, /*admin-only ooc chat*/ + /datum/admins/proc/PlayerNotes, + /client/proc/cmd_mentor_say, + /datum/admins/proc/show_player_notes, + /datum/admins/proc/vpn_whitelist, + /client/proc/free_slot, /*frees slot for chosen job*/ + /client/proc/toggleattacklogs, + /client/proc/toggleadminlogs, + /client/proc/toggledebuglogs, + /client/proc/update_mob_sprite, + /client/proc/toggledrones, + /client/proc/man_up, + /client/proc/global_man_up, + /client/proc/delbook, + /client/proc/view_flagged_books, + /client/proc/empty_ai_core_toggle_latejoin, + /client/proc/aooc, + /client/proc/freeze, + /client/proc/freezemecha, + /client/proc/alt_check, + /client/proc/secrets, + /client/proc/change_human_appearance_admin, /* Allows an admin to change the basic appearance of human-based mobs */ + /client/proc/change_human_appearance_self, /* Allows the human-based mob itself to change its basic appearance */ + /client/proc/debug_variables, + /client/proc/reset_all_tcs, /*resets all telecomms scripts*/ + /client/proc/toggle_mentor_chat, + /client/proc/toggle_advanced_interaction, /*toggle admin ability to interact with not only machines, but also atoms such as buttons and doors*/ + /client/proc/list_ssds_afks, + /client/proc/cmd_admin_headset_message, + /client/proc/spawn_floor_cluwne +)) +GLOBAL_LIST_INIT(admin_verbs_ban, list( + /client/proc/unban_panel, + /client/proc/jobbans, + /client/proc/stickybanpanel + )) +GLOBAL_LIST_INIT(admin_verbs_sounds, list( + /client/proc/play_local_sound, + /client/proc/play_sound, + /client/proc/play_server_sound, + /client/proc/play_intercomm_sound, + /client/proc/stop_global_admin_sounds + )) +GLOBAL_LIST_INIT(admin_verbs_event, list( + /client/proc/object_talk, + /client/proc/cmd_admin_dress, + /client/proc/cmd_admin_gib_self, + /client/proc/drop_bomb, + /client/proc/cinematic, + /client/proc/one_click_antag, + /datum/admins/proc/toggle_aliens, + /client/proc/cmd_admin_add_freeform_ai_law, + /client/proc/cmd_admin_add_random_ai_law, + /client/proc/make_sound, + /client/proc/toggle_random_events, + /client/proc/toggle_random_events, + /client/proc/toggle_ert_calling, + /client/proc/show_tip, + /client/proc/cmd_admin_change_custom_event, + /datum/admins/proc/access_news_network, /*allows access of newscasters*/ + /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ + /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ + /client/proc/response_team, // Response Teams admin verb + /client/proc/cmd_admin_create_centcom_report, + /client/proc/fax_panel, + /client/proc/event_manager_panel, + /client/proc/modify_goals, + /client/proc/outfit_manager + )) + +GLOBAL_LIST_INIT(admin_verbs_spawn, list( + /datum/admins/proc/spawn_atom, /*allows us to spawn instances*/ + /client/proc/respawn_character, + /client/proc/admin_deserialize + )) +GLOBAL_LIST_INIT(admin_verbs_server, list( + /client/proc/ToRban, + /client/proc/Set_Holiday, + /datum/admins/proc/startnow, + /datum/admins/proc/restart, + /datum/admins/proc/delay, + /datum/admins/proc/toggleaban, + /client/proc/toggle_log_hrefs, + /client/proc/everyone_random, + /datum/admins/proc/toggleAI, + /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ + /client/proc/cmd_debug_del_all, + /client/proc/cmd_debug_del_sing, + /datum/admins/proc/toggle_aliens, + /client/proc/delbook, + /client/proc/view_flagged_books, + /client/proc/toggle_antagHUD_use, + /client/proc/toggle_antagHUD_restrictions, + /client/proc/set_ooc, + /client/proc/reset_ooc + )) +GLOBAL_LIST_INIT(admin_verbs_debug, list( + /client/proc/cmd_admin_list_open_jobs, + /client/proc/Debug2, + /client/proc/cmd_debug_make_powernets, + /client/proc/debug_controller, + /client/proc/cmd_debug_mob_lists, + /client/proc/cmd_admin_delete, + /client/proc/cmd_debug_del_all, + /client/proc/cmd_debug_del_sing, + /client/proc/reload_admins, + /client/proc/restart_controller, + /client/proc/enable_debug_verbs, + /client/proc/toggledebuglogs, + /client/proc/cmd_display_del_log, + /client/proc/cmd_display_del_log_simple, + /client/proc/debugNatureMapGenerator, + /client/proc/check_bomb_impacts, + /client/proc/test_movable_UI, + /client/proc/test_snap_UI, + /client/proc/cinematic, + /proc/machine_upgrade, + /client/proc/map_template_load, + /client/proc/map_template_upload, + /client/proc/view_runtimes, + /client/proc/admin_serialize, + /client/proc/jump_to_ruin, + /client/proc/toggle_medal_disable, + )) +GLOBAL_LIST_INIT(admin_verbs_possess, list( + /proc/possess, + /proc/release + )) +GLOBAL_LIST_INIT(admin_verbs_permissions, list( + /client/proc/edit_admin_permissions, + /client/proc/create_poll, + /client/proc/big_brother + )) +GLOBAL_LIST_INIT(admin_verbs_rejuv, list( + /client/proc/respawn_character, + /client/proc/cmd_admin_rejuvenate + )) +GLOBAL_LIST_INIT(admin_verbs_mod, list( + /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ + /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ + /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ + /datum/admins/proc/PlayerNotes, + /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ + /client/proc/cmd_mentor_say, + /datum/admins/proc/show_player_notes, + /client/proc/player_panel_new, + /client/proc/dsay, + /datum/admins/proc/show_player_panel, + /client/proc/jobbans, + /client/proc/debug_variables /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ +)) +GLOBAL_LIST_INIT(admin_verbs_mentor, list( + /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ + /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ + /client/proc/cmd_admin_pm_by_key_panel, /*admin-pm list by key*/ + /client/proc/openMentorTicketUI, + /client/proc/toggleMentorTicketLogs, + /client/proc/cmd_mentor_say /* mentor say*/ + // cmd_mentor_say is added/removed by the toggle_mentor_chat verb +)) +GLOBAL_LIST_INIT(admin_verbs_proccall, list( + /client/proc/callproc, + /client/proc/callproc_datum, + /client/proc/SDQL2_query +)) +GLOBAL_LIST_INIT(admin_verbs_ticket, list( + /client/proc/openAdminTicketUI, + /client/proc/toggleticketlogs, + /client/proc/openMentorTicketUI, + /client/proc/toggleMentorTicketLogs, + /client/proc/resolveAllAdminTickets, + /client/proc/resolveAllMentorTickets +)) + +/client/proc/on_holder_add() + if(chatOutput && chatOutput.loaded) + chatOutput.loadAdmin() + +/client/proc/add_admin_verbs() + if(holder) + verbs += GLOB.admin_verbs_default + if(holder.rights & R_BUILDMODE) + verbs += /client/proc/togglebuildmodeself + if(holder.rights & R_ADMIN) + verbs += GLOB.admin_verbs_admin + verbs += GLOB.admin_verbs_ticket + spawn(1) + control_freak = 0 + if(holder.rights & R_BAN) + verbs += GLOB.admin_verbs_ban + if(holder.rights & R_EVENT) + verbs += GLOB.admin_verbs_event + if(holder.rights & R_SERVER) + verbs += GLOB.admin_verbs_server + if(holder.rights & R_DEBUG) + verbs += GLOB.admin_verbs_debug + if(holder.rights & R_POSSESS) + verbs += GLOB.admin_verbs_possess + if(holder.rights & R_PERMISSIONS) + verbs += GLOB.admin_verbs_permissions + if(holder.rights & R_STEALTH) + verbs += /client/proc/stealth + if(holder.rights & R_REJUVINATE) + verbs += GLOB.admin_verbs_rejuv + if(holder.rights & R_SOUNDS) + verbs += GLOB.admin_verbs_sounds + if(holder.rights & R_SPAWN) + verbs += GLOB.admin_verbs_spawn + if(holder.rights & R_MOD) + verbs += GLOB.admin_verbs_mod + if(holder.rights & R_MENTOR) + verbs += GLOB.admin_verbs_mentor + if(holder.rights & R_PROCCALL) + verbs += GLOB.admin_verbs_proccall + +/client/proc/remove_admin_verbs() + verbs.Remove( + GLOB.admin_verbs_default, + /client/proc/togglebuildmodeself, + GLOB.admin_verbs_admin, + GLOB.admin_verbs_ban, + GLOB.admin_verbs_event, + GLOB.admin_verbs_server, + GLOB.admin_verbs_debug, + GLOB.admin_verbs_possess, + GLOB.admin_verbs_permissions, + /client/proc/stealth, + GLOB.admin_verbs_rejuv, + GLOB.admin_verbs_sounds, + GLOB.admin_verbs_spawn, + GLOB.admin_verbs_mod, + GLOB.admin_verbs_mentor, + GLOB.admin_verbs_proccall, + GLOB.admin_verbs_show_debug_verbs, + /client/proc/readmin, + GLOB.admin_verbs_ticket + ) + +/client/proc/hide_verbs() + set name = "Adminverbs - Hide All" + set category = "Admin" + + if(!holder) + return + + remove_admin_verbs() + verbs += /client/proc/show_verbs + + to_chat(src, "Almost all of your adminverbs have been hidden.") + feedback_add_details("admin_verb","TAVVH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/show_verbs() + set name = "Adminverbs - Show" + set category = "Admin" + + if(!holder) + return + + verbs -= /client/proc/show_verbs + add_admin_verbs() + + to_chat(src, "All of your adminverbs are now visible.") + feedback_add_details("admin_verb","TAVVS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/admin_ghost() + set category = "Admin" + set name = "Aghost" + + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(istype(mob,/mob/dead/observer)) + //re-enter + var/mob/dead/observer/ghost = mob + ghost.can_reenter_corpse = 1 //just in-case. + ghost.reenter_corpse() + log_admin("[key_name(usr)] re-entered their body") + feedback_add_details("admin_verb","P") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + else if(istype(mob,/mob/new_player)) + to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or observe first.") + else + //ghostize + var/mob/body = mob + body.ghostize(1) + if(body && !body.key) + body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus + log_admin("[key_name(usr)] has admin-ghosted") + feedback_add_details("admin_verb","O") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/invisimin() + set name = "Invisimin" + set category = "Admin" + set desc = "Toggles ghost-like invisibility (Don't abuse this)" + + if(!check_rights(R_ADMIN)) + return + + if(mob) + if(mob.invisibility == INVISIBILITY_OBSERVER) + mob.invisibility = initial(mob.invisibility) + to_chat(mob, "Invisimin off. Invisibility reset.") + mob.add_to_all_human_data_huds() + //TODO: Make some kind of indication for the badmin that they are currently invisible + else + mob.invisibility = INVISIBILITY_OBSERVER + to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") + mob.remove_from_all_data_huds() + +/client/proc/player_panel() + set name = "Player Panel" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.player_panel_old() + feedback_add_details("admin_verb","PP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/player_panel_new() + set name = "Player Panel New" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_MOD)) + return + + holder.player_panel_new() + feedback_add_details("admin_verb","PPN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/check_antagonists() + set name = "Check Antagonists" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.check_antagonists() + log_admin("[key_name(usr)] checked antagonists") + feedback_add_details("admin_verb","CHA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/jobbans() + set name = "Display Job bans" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(config.ban_legacy_system) + holder.Jobbans() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","VJB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/unban_panel() + set name = "Unban Panel" + set category = "Admin" + + if(!check_rights(R_BAN)) + return + + if(config.ban_legacy_system) + holder.unbanpanel() + else + holder.DB_ban_panel() + feedback_add_details("admin_verb","UBP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/game_panel() + set name = "Game Panel" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.Game() + feedback_add_details("admin_verb","GP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/secrets() + set name = "Secrets" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.Secrets() + feedback_add_details("admin_verb","S") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/findStealthKey(txt) + if(txt) + for(var/P in GLOB.stealthminID) + if(GLOB.stealthminID[P] == txt) + return P + txt = GLOB.stealthminID[ckey] + return txt + +/client/proc/createStealthKey() + var/num = (rand(0,1000)) + var/i = 0 + while(i == 0) + i = 1 + for(var/P in GLOB.stealthminID) + if(num == GLOB.stealthminID[P]) + num++ + i = 0 + GLOB.stealthminID["[ckey]"] = "@[num2text(num)]" + +/client/proc/stealth() + set category = "Admin" + set name = "Stealth Mode" + + if(!check_rights(R_ADMIN)) + return + + if(holder) + holder.big_brother = 0 + if(holder.fakekey) + holder.fakekey = null + else + var/new_key = ckeyEx(clean_input("Enter your desired display name.", "Fake Key", key)) + if(!new_key) return + if(length(new_key) >= 26) + new_key = copytext(new_key, 1, 26) + holder.fakekey = new_key + createStealthKey() + log_admin("[key_name(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]") + message_admins("[key_name_admin(usr)] has turned stealth mode [holder.fakekey ? "ON" : "OFF"]", 1) + feedback_add_details("admin_verb","SM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/big_brother() + set category = "Admin" + set name = "Big Brother Mode" + + if(!check_rights(R_PERMISSIONS)) + return + + if(holder) + if(holder.fakekey) + holder.fakekey = null + holder.big_brother = 0 + else + var/new_key = ckeyEx(clean_input("Enter your desired display name. Unlike normal stealth mode, this will not appear in Who at all, except for other heads.", "Fake Key", key)) + if(!new_key) + return + if(length(new_key) >= 26) + new_key = copytext(new_key, 1, 26) + holder.fakekey = new_key + holder.big_brother = 1 + createStealthKey() + log_admin("[key_name(usr)] has turned BB mode [holder.fakekey ? "ON" : "OFF"]") + feedback_add_details("admin_verb","BBSM") + +#define MAX_WARNS 3 +#define AUTOBANTIME 10 + +/client/proc/warn(warned_ckey) + if(!check_rights(R_ADMIN)) + return + + if(!warned_ckey || !istext(warned_ckey)) return + if(warned_ckey in GLOB.admin_datums) + to_chat(usr, "Error: warn(): You can't warn admins.") + return + + var/datum/preferences/D + var/client/C = GLOB.directory[warned_ckey] + if(C) D = C.prefs + else D = GLOB.preferences_datums[warned_ckey] + + if(!D) + to_chat(src, "Error: warn(): No such ckey found.") + return + + if(++D.warns >= MAX_WARNS) //uh ohhhh...you'reee iiiiin trouuuubble O:) + ban_unban_log_save("[ckey] warned [warned_ckey], resulting in a [AUTOBANTIME] minute autoban.") + if(C) + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)] resulting in a [AUTOBANTIME] minute ban") + log_admin("[key_name(src)] has warned [key_name(C)] resulting in a [AUTOBANTIME] minute ban") + to_chat(C, "You have been autobanned due to a warning by [ckey].
    This is a temporary ban, it will be removed in [AUTOBANTIME] minutes.") + del(C) + else + message_admins("[key_name_admin(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban") + log_admin("[key_name(src)] has warned [warned_ckey] resulting in a [AUTOBANTIME] minute ban") + AddBan(warned_ckey, D.last_id, "Autobanning due to too many formal warnings", ckey, 1, AUTOBANTIME) + feedback_inc("ban_warn",1) + else + if(C) + to_chat(C, "You have been formally warned by an administrator.
    Further warnings will result in an autoban.
    ") + message_admins("[key_name_admin(src)] has warned [key_name_admin(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") + log_admin("[key_name(src)] has warned [key_name(C)]. They have [MAX_WARNS-D.warns] strikes remaining.") + else + message_admins("[key_name_admin(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") + log_admin("[key_name(src)] has warned [warned_ckey] (DC). They have [MAX_WARNS-D.warns] strikes remaining.") + + feedback_add_details("admin_verb","WARN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +#undef MAX_WARNS +#undef AUTOBANTIME + +/client/proc/drop_bomb() // Some admin dickery that can probably be done better -- TLE + set category = "Event" + set name = "Drop Bomb" + set desc = "Cause an explosion of varying strength at your location." + + if(!check_rights(R_EVENT)) + return + + var/turf/epicenter = mob.loc + var/list/choices = list("Small Bomb", "Medium Bomb", "Big Bomb", "Custom Bomb") + var/choice = input("What size explosion would you like to produce?") as null|anything in choices + switch(choice) + if(null) + return 0 + if("Small Bomb") + explosion(epicenter, 1, 2, 3, 3) + if("Medium Bomb") + explosion(epicenter, 2, 3, 4, 4) + if("Big Bomb") + explosion(epicenter, 3, 5, 7, 5) + if("Custom Bomb") + var/devastation_range = input("Devastation range (in tiles):") as null|num + if(devastation_range == null) + return + var/heavy_impact_range = input("Heavy impact range (in tiles):") as null|num + if(heavy_impact_range == null) + return + var/light_impact_range = input("Light impact range (in tiles):") as null|num + if(light_impact_range == null) + return + var/flash_range = input("Flash range (in tiles):") as null|num + if(flash_range == null) + return + explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, 1, 1) + log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc]") + message_admins("[key_name_admin(usr)] created an admin explosion at [epicenter.loc]") + feedback_add_details("admin_verb","DB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/give_spell(mob/T as mob in GLOB.mob_list) // -- Urist + set category = "Event" + set name = "Give Spell" + set desc = "Gives a spell to a mob." + + if(!check_rights(R_EVENT)) + return + + var/list/spell_list = list() + var/type_length = length("/obj/effect/proc_holder/spell") + 2 + for(var/A in GLOB.spells) + spell_list[copytext("[A]", type_length)] = A + var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list + if(!S) + return + S = spell_list[S] + if(T.mind) + T.mind.AddSpell(new S) + else + T.AddSpell(new S) + + feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") + message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].", 1) + +/client/proc/give_disease(mob/T in GLOB.mob_list) + set category = "Event" + set name = "Give Disease" + set desc = "Gives a Disease to a mob." + var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in GLOB.diseases + if(!D) return + T.ForceContractDisease(new D) + feedback_add_details("admin_verb","GD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") + message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [D].") + +/client/proc/make_sound(var/obj/O in view()) // -- TLE + set category = "Event" + set name = "Make Sound" + set desc = "Display a message to everyone who can hear the target" + + if(!check_rights(R_EVENT)) + return + + if(O) + var/message = clean_input("What do you want the message to be?", "Make Sound") + if(!message) + return + for(var/mob/V in hearers(O)) + V.show_message(admin_pencode_to_html(message), 2) + log_admin("[key_name(usr)] made [O] at [O.x], [O.y], [O.z] make a sound") + message_admins("[key_name_admin(usr)] made [O] at [O.x], [O.y], [O.z] make a sound") + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/togglebuildmodeself() + set name = "Toggle Build Mode Self" + set category = "Event" + + if(!check_rights(R_EVENT)) + return + + if(src.mob) + togglebuildmode(src.mob) + feedback_add_details("admin_verb","TBMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/object_talk(var/msg as text) // -- TLE + set category = "Event" + set name = "oSay" + set desc = "Display a message to everyone who can hear the target" + + if(!check_rights(R_EVENT)) + return + + if(mob.control_object) + if(!msg) + return + for(var/mob/V in hearers(mob.control_object)) + V.show_message("[mob.control_object.name] says: \"" + msg + "\"", 2) + log_admin("[key_name(usr)] used oSay on [mob.control_object]: [msg]") + message_admins("[key_name_admin(usr)] used oSay on [mob.control_object]: [msg]") + + feedback_add_details("admin_verb","OT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/deadmin_self() + set name = "De-admin self" + set category = "Admin" + + if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) + return + + log_admin("[key_name(usr)] deadmined themself.") + message_admins("[key_name_admin(usr)] deadmined themself.") + deadmin() + verbs += /client/proc/readmin + GLOB.deadmins += ckey + to_chat(src, "You are now a normal player.") + feedback_add_details("admin_verb","DAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/readmin() + set name = "Re-admin self" + set category = "Admin" + set desc = "Regain your admin powers." + + var/datum/admins/D = GLOB.admin_datums[ckey] + var/rank = null + if(config.admin_legacy_system) + //load text from file + var/list/Lines = file2list("config/admins.txt") + for(var/line in Lines) + var/list/splitline = splittext(line, " - ") + if(lowertext(splitline[1]) == ckey) + if(splitline.len >= 2) + rank = ckeyEx(splitline[2]) + break + continue + else + if(!GLOB.dbcon.IsConnected()) + message_admins("Warning, MySQL database is not connected.") + to_chat(src, "Warning, MYSQL database is not connected.") + return + var/sql_ckey = sanitizeSQL(ckey) + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT rank FROM [format_table_name("admin")] WHERE ckey = '[sql_ckey]'") + query.Execute() + while(query.NextRow()) + rank = ckeyEx(query.item[1]) + if(!D) + if(config.admin_legacy_system) + if(GLOB.admin_ranks[rank] == null) + error("Error while re-adminning [src], admin rank ([rank]) does not exist.") + to_chat(src, "Error while re-adminning, admin rank ([rank]) does not exist.") + return + + D = new(rank, GLOB.admin_ranks[rank], ckey) + else + var/sql_ckey = sanitizeSQL(ckey) + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT ckey, rank, flags FROM [format_table_name("admin")] WHERE ckey = '[sql_ckey]'") + query.Execute() + while(query.NextRow()) + var/admin_ckey = query.item[1] + var/admin_rank = query.item[2] + var/flags = query.item[3] + if(!admin_ckey) + to_chat(src, "Error while re-adminning, ckey [admin_ckey] was not found in the admin database.") + return + if(admin_rank == "Removed") //This person was de-adminned. They are only in the admin list for archive purposes. + to_chat(src, "Error while re-adminning, ckey [admin_ckey] is not an admin.") + return + + if(istext(flags)) + flags = text2num(flags) + D = new(admin_rank, flags, ckey) + + var/client/C = GLOB.directory[ckey] + D.associate(C) + message_admins("[key_name_admin(usr)] re-adminned themselves.") + log_admin("[key_name(usr)] re-adminned themselves.") + GLOB.deadmins -= ckey + feedback_add_details("admin_verb","RAS") + return + else + to_chat(src, "You are already an admin.") + verbs -= /client/proc/readmin + GLOB.deadmins -= ckey + return + +/client/proc/toggle_log_hrefs() + set name = "Toggle href logging" + set category = "Server" + + if(!check_rights(R_SERVER)) + return + + if(config) + if(config.log_hrefs) + config.log_hrefs = 0 + to_chat(src, "Stopped logging hrefs") + else + config.log_hrefs = 1 + to_chat(src, "Started logging hrefs") + +/client/proc/check_ai_laws() + set name = "Check AI Laws" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + holder.output_ai_laws() + +/client/proc/manage_silicon_laws() + set name = "Manage Silicon Laws" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + var/mob/living/silicon/S = input("Select silicon.", "Manage Silicon Laws") as null|anything in GLOB.silicon_mob_list + if(!S) return + + var/datum/nano_module/law_manager/L = new(S) + L.ui_interact(usr, state = GLOB.admin_state) + log_and_message_admins("has opened [S]'s law manager.") + feedback_add_details("admin_verb","MSL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/change_human_appearance_admin(mob/living/carbon/human/H in GLOB.mob_list) + set name = "C.M.A. - Admin" + set desc = "Allows you to change the mob appearance" + set category = null + + if(!check_rights(R_ADMIN)) + return + + if(!istype(H)) + if(istype(H, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/B = H + if(istype(B.container, /obj/item/mmi/robotic_brain/positronic)) + var/obj/item/mmi/robotic_brain/positronic/C = B.container + var/obj/item/organ/internal/brain/mmi_holder/posibrain/P = C.loc + if(ishuman(P.owner)) + H = P.owner + else + return + else + return + + if(holder) + admin_log_and_message_admins("is altering the appearance of [H].") + H.change_appearance(APPEARANCE_ALL, usr, usr, check_species_whitelist = 0) + feedback_add_details("admin_verb","CHAA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/change_human_appearance_self(mob/living/carbon/human/H in GLOB.mob_list) + set name = "C.M.A. - Self" + set desc = "Allows the mob to change its appearance" + set category = null + + if(!check_rights(R_ADMIN)) + return + + if(!istype(H)) + if(istype(H, /mob/living/carbon/brain)) + var/mob/living/carbon/brain/B = H + if(istype(B.container, /obj/item/mmi/robotic_brain/positronic)) + var/obj/item/mmi/robotic_brain/positronic/C = B.container + var/obj/item/organ/internal/brain/mmi_holder/posibrain/P = C.loc + if(ishuman(P.owner)) + H = P.owner + else + return + else + return + + if(!H.client) + to_chat(usr, "Only mobs with clients can alter their own appearance.") + return + + switch(alert("Do you wish for [H] to be allowed to select non-whitelisted races?","Alter Mob Appearance","Yes","No","Cancel")) + if("Yes") + admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, without whitelisting of races.") + H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 0) + if("No") + admin_log_and_message_admins("has allowed [H] to change [H.p_their()] appearance, with whitelisting of races.") + H.change_appearance(APPEARANCE_ALL, H.loc, check_species_whitelist = 1) + feedback_add_details("admin_verb","CMAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/free_slot() + set name = "Free Job Slot" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + var/list/jobs = list() + for(var/datum/job/J in SSjobs.occupations) + if(J.current_positions >= J.total_positions && J.total_positions != -1) + jobs += J.title + if(!jobs.len) + to_chat(usr, "There are no fully staffed jobs.") + return + var/job = input("Please select job slot to free", "Free Job Slot") as null|anything in jobs + if(job) + SSjobs.FreeRole(job) + log_admin("[key_name(usr)] has freed a job slot for [job].") + message_admins("[key_name_admin(usr)] has freed a job slot for [job].") + +/client/proc/toggleattacklogs() + set name = "Toggle Attack Log Messages" + set category = "Preferences" + + if(!check_rights(R_ADMIN)) + return + + if(prefs.atklog == ATKLOG_ALL) + prefs.atklog = ATKLOG_ALMOSTALL + to_chat(usr, "Your attack logs preference is now: show ALMOST ALL attack logs (notable exceptions: NPCs attacking other NPCs, vampire bites, equipping/stripping, people pushing each other over)") + else if(prefs.atklog == ATKLOG_ALMOSTALL) + prefs.atklog = ATKLOG_MOST + to_chat(usr, "Your attack logs preference is now: show MOST attack logs (like ALMOST ALL, except that it also hides player v. NPC combat, and certain areas like lavaland syndie base and thunderdome)") + else if(prefs.atklog == ATKLOG_MOST) + prefs.atklog = ATKLOG_FEW + to_chat(usr, "Your attack logs preference is now: show FEW attack logs (only the most important stuff: attacks on SSDs, use of explosives, messing with the engine, gibbing, AI wiping, forcefeeding, acid sprays, and organ extraction)") + else if(prefs.atklog == ATKLOG_FEW) + prefs.atklog = ATKLOG_NONE + to_chat(usr, "Your attack logs preference is now: show NO attack logs") + else if(prefs.atklog == ATKLOG_NONE) + prefs.atklog = ATKLOG_ALL + to_chat(usr, "Your attack logs preference is now: show ALL attack logs") + else + prefs.atklog = ATKLOG_ALL + to_chat(usr, "Your attack logs preference is now: show ALL attack logs (your preference was set to an invalid value, it has been reset)") + + prefs.save_preferences(src) + + +/client/proc/toggleadminlogs() + set name = "Toggle Admin Log Messages" + set category = "Preferences" + + if(!check_rights(R_ADMIN)) + return + + prefs.toggles ^= CHAT_NO_ADMINLOGS + prefs.save_preferences(src) + if(prefs.toggles & CHAT_NO_ADMINLOGS) + to_chat(usr, "You now won't get admin log messages.") + else + to_chat(usr, "You now will get admin log messages.") + +/client/proc/toggleMentorTicketLogs() + set name = "Toggle Mentor Ticket Messages" + set category = "Preferences" + + if(!check_rights(R_MENTOR|R_ADMIN)) + return + + prefs.toggles ^= CHAT_NO_MENTORTICKETLOGS + prefs.save_preferences(src) + if(prefs.toggles & CHAT_NO_MENTORTICKETLOGS) + to_chat(usr, "You now won't get mentor ticket messages.") + else + to_chat(usr, "You now will get mentor ticket messages.") + +/client/proc/toggleticketlogs() + set name = "Toggle Admin Ticket Messgaes" + set category = "Preferences" + + if(!check_rights(R_ADMIN)) + return + + prefs.toggles ^= CHAT_NO_TICKETLOGS + prefs.save_preferences(src) + if(prefs.toggles & CHAT_NO_TICKETLOGS) + to_chat(usr, "You now won't get admin ticket messages.") + else + to_chat(usr, "You now will get admin ticket messages.") + +/client/proc/toggledrones() + set name = "Toggle Maintenance Drones" + set category = "Server" + + if(!check_rights(R_SERVER)) + return + + config.allow_drone_spawn = !(config.allow_drone_spawn) + log_admin("[key_name(usr)] has [config.allow_drone_spawn ? "enabled" : "disabled"] maintenance drones.") + message_admins("[key_name_admin(usr)] has [config.allow_drone_spawn ? "enabled" : "disabled"] maintenance drones.") + +/client/proc/toggledebuglogs() + set name = "Toggle Debug Log Messages" + set category = "Preferences" + + if(!check_rights(R_DEBUG)) + return + + prefs.toggles ^= CHAT_DEBUGLOGS + prefs.save_preferences(src) + if(prefs.toggles & CHAT_DEBUGLOGS) + to_chat(usr, "You now will get debug log messages") + else + to_chat(usr, "You now won't get debug log messages") + +/client/proc/man_up(mob/T as mob in GLOB.mob_list) + set category = "Admin" + set name = "Man Up" + set desc = "Tells mob to man up and deal with it." + + if(!check_rights(R_ADMIN)) + return + + to_chat(T, "Man up and deal with it.") + to_chat(T, "Move on.") + T << 'sound/voice/manup1.ogg' + + log_admin("[key_name(usr)] told [key_name(T)] to man up and deal with it.") + message_admins("[key_name_admin(usr)] told [key_name(T)] to man up and deal with it.") + +/client/proc/global_man_up() + set category = "Admin" + set name = "Man Up Global" + set desc = "Tells everyone to man up and deal with it." + + if(!check_rights(R_ADMIN)) + return + + var/confirm = alert("Are you sure you want to send the global message?", "Confirm Man Up Global", "Yes", "No") + + if(confirm == "Yes") + for(var/mob/T as mob in GLOB.mob_list) + to_chat(T, "
    Man up.
    Deal with it.

    Move on.

    ") + T << 'sound/voice/manup1.ogg' + + log_admin("[key_name(usr)] told everyone to man up and deal with it.") + message_admins("[key_name_admin(usr)] told everyone to man up and deal with it.") + +/client/proc/toggle_advanced_interaction() + set name = "Toggle Advanced Admin Interaction" + set category = "Admin" + set desc = "Allows you to interact with atoms such as buttons and doors, on top of regular machinery interaction." + + if(!check_rights(R_ADMIN)) + return + + advanced_admin_interaction = !advanced_admin_interaction + + log_admin("[key_name(usr)] has [advanced_admin_interaction ? "activated" : "deactivated"] their advanced admin interaction.") + message_admins("[key_name_admin(usr)] has [advanced_admin_interaction ? "activated" : "deactivated"] their advanced admin interaction.") diff --git a/code/modules/admin/banappearance.dm b/code/modules/admin/banappearance.dm index ff461c0f333d..b561e0169ff7 100644 --- a/code/modules/admin/banappearance.dm +++ b/code/modules/admin/banappearance.dm @@ -1,22 +1,22 @@ //ban people from using custom names and appearances. that'll show 'em. -var/appearanceban_runonce //Updates legacy bans with new info -var/appearance_keylist[0] //to store the keys +GLOBAL_VAR(appearanceban_runonce) //Updates legacy bans with new info +GLOBAL_LIST_EMPTY(appearance_keylist) //to store the keys /proc/appearance_fullban(mob/M, reason) if(!M || !M.key) return - appearance_keylist.Add(text("[M.ckey] ## [reason]")) + GLOB.appearance_keylist.Add(text("[M.ckey] ## [reason]")) appearance_savebanfile() /proc/appearance_client_fullban(ckey) if(!ckey) return - appearance_keylist.Add(text("[ckey]")) + GLOB.appearance_keylist.Add(text("[ckey]")) appearance_savebanfile() //returns a reason if M is banned, returns 0 otherwise /proc/appearance_isbanned(mob/M) if(M) - for(var/s in appearance_keylist) + for(var/s in GLOB.appearance_keylist) if(findtext(s, "[M.ckey]") == 1) var/startpos = findtext(s, "## ") + 3 if(startpos && startpos < length(s)) @@ -43,12 +43,12 @@ DEBUG /proc/appearance_loadbanfile() if(config.ban_legacy_system) var/savefile/S=new("data/appearance_full.ban") - S["keys[0]"] >> appearance_keylist + S["keys[0]"] >> GLOB.appearance_keylist log_admin("Loading appearance_rank") - S["runonce"] >> appearanceban_runonce + S["runonce"] >> GLOB.appearanceban_runonce - if(!length(appearance_keylist)) - appearance_keylist=list() + if(!length(GLOB.appearance_keylist)) + GLOB.appearance_keylist=list() log_admin("appearance_keylist was empty") else if(!establish_db_connection()) @@ -58,17 +58,17 @@ DEBUG return //appearance bans - var/DBQuery/query = dbcon.NewQuery("SELECT ckey FROM [format_table_name("ban")] WHERE bantype = 'APPEARANCE_BAN' AND NOT unbanned = 1") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("ban")] WHERE bantype = 'APPEARANCE_BAN' AND NOT unbanned = 1") query.Execute() while(query.NextRow()) var/ckey = query.item[1] - appearance_keylist.Add("[ckey]") + GLOB.appearance_keylist.Add("[ckey]") /proc/appearance_savebanfile() var/savefile/S=new("data/appearance_full.ban") - to_chat(S["keys[0]"], appearance_keylist) + to_chat(S["keys[0]"], GLOB.appearance_keylist) /proc/appearance_unban(mob/M) appearance_remove("[M.ckey]") @@ -76,18 +76,18 @@ DEBUG /proc/appearance_updatelegacybans() - if(!appearanceban_runonce) + if(!GLOB.appearanceban_runonce) log_admin("Updating appearancefile!") // Updates bans.. Or fixes them. Either way. - for(var/T in appearance_keylist) + for(var/T in GLOB.appearance_keylist) if(!T) continue - appearanceban_runonce++ //don't run this update again + GLOB.appearanceban_runonce++ //don't run this update again /proc/appearance_remove(X) - for(var/i = 1; i <= length(appearance_keylist); i++) - if( findtext(appearance_keylist[i], "[X]") ) - appearance_keylist.Remove(appearance_keylist[i]) + for(var/i = 1; i <= length(GLOB.appearance_keylist); i++) + if( findtext(GLOB.appearance_keylist[i], "[X]") ) + GLOB.appearance_keylist.Remove(GLOB.appearance_keylist[i]) appearance_savebanfile() return 1 return 0 diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 920bb933f32c..9efc4c9ce882 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -1,179 +1,179 @@ -var/jobban_runonce // Updates legacy bans with new info -var/jobban_keylist[0] // Linear list of jobban strings, kept around for the legacy system -var/jobban_assoclist[0] // Associative list, for efficiency - -// Matches string-based jobbans into ckey, rank, and reason groups -var/regex/jobban_regex = regex("(\[\\S]+) - (\[^#]+\[^# ])(?: ## (.+))?") - -/proc/jobban_assoc_insert(ckey, rank, reason) - if(!ckey || !rank) - return - if(!jobban_assoclist[ckey]) - jobban_assoclist[ckey] = list() - jobban_assoclist[ckey][rank] = reason || "Reason Unspecified" - -/proc/jobban_fullban(mob/M, rank, reason) - if(!M || !M.key) - return - jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) - jobban_assoc_insert(M.ckey, rank, reason) - if(config.ban_legacy_system) - jobban_savebanfile() - -/proc/jobban_client_fullban(ckey, rank) - if(!ckey || !rank) - return - jobban_keylist.Add(text("[ckey] - [rank]")) - jobban_assoc_insert(ckey, rank) - if(config.ban_legacy_system) - jobban_savebanfile() - -//returns a reason if M is banned from rank, returns 0 otherwise -/proc/jobban_isbanned(mob/M, rank) - if(!M || !rank) - return 0 - - if(config.guest_jobban && guest_jobbans(rank)) - if(IsGuestKey(M.key)) - return "Guest Job-ban" - - if(jobban_assoclist[M.ckey]) - return jobban_assoclist[M.ckey][rank] - else - return 0 - -/* -DEBUG -/mob/verb/list_all_jobbans() - set name = "list all jobbans" - - for(var/s in jobban_keylist) - to_chat(world, s) - -/mob/verb/reload_jobbans() - set name = "reload jobbans" - - jobban_loadbanfile() -*/ - -/hook/startup/proc/loadJobBans() - jobban_loadbanfile() - return 1 - -/proc/jobban_loadbanfile() - if(config.ban_legacy_system) - var/savefile/S=new("data/job_full.ban") - S["keys[0]"] >> jobban_keylist - log_admin("Loading jobban_rank") - S["runonce"] >> jobban_runonce - - if(!length(jobban_keylist)) - jobban_keylist=list() - log_admin("jobban_keylist was empty") - - for(var/s in jobban_keylist) - if(jobban_regex.Find(s)) - jobban_assoc_insert(jobban_regex.group[1], jobban_regex.group[2], jobban_regex.group[3]) - else - log_runtime(EXCEPTION("Skipping malformed job ban: [s]")) - else - if(!establish_db_connection()) - log_world("Database connection failed. Reverting to the legacy ban system.") - config.ban_legacy_system = 1 - jobban_loadbanfile() - return - - //Job permabans - var/DBQuery/permabans = dbcon.NewQuery("SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") - permabans.Execute() - // Job tempbans - var/DBQuery/tempbans = dbcon.NewQuery("SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") - tempbans.Execute() - - while(TRUE) - var/ckey - var/job - if(permabans.NextRow()) - ckey = permabans.item[1] - job = permabans.item[2] - else if(tempbans.NextRow()) - ckey = tempbans.item[1] - job = tempbans.item[2] - else - break - - jobban_keylist.Add("[ckey] - [job]") - jobban_assoc_insert(ckey, job) - -/proc/jobban_savebanfile() - var/savefile/S=new("data/job_full.ban") - S["keys[0]"] << jobban_keylist - -/proc/jobban_unban(mob/M, rank) - jobban_remove("[M.ckey] - [rank]") - -/proc/jobban_unban_client(ckey, rank) - jobban_remove("[ckey] - [rank]") - -/proc/ban_unban_log_save(var/formatted_log) - text2file(formatted_log,"data/ban_unban_log.txt") - - -/proc/jobban_remove(X) - for(var/i = 1; i <= length(jobban_keylist); i++) - if( findtext(jobban_keylist[i], "[X]") ) - // This need to be here, instead of jobban_unban, due to direct calls to jobban_remove - if(jobban_regex.Find(X)) - var/ckey = jobban_regex.group[1] - var/rank = jobban_regex.group[2] - if(jobban_assoclist[ckey] && jobban_assoclist[ckey][rank]) - jobban_assoclist[ckey] -= rank - else - log_runtime(EXCEPTION("Attempted to remove non-existent job ban: [X]")) - else - log_runtime(EXCEPTION("Failed to remove malformed job ban from associative list: [X]")) - jobban_keylist.Remove(jobban_keylist[i]) - if(config.ban_legacy_system) - jobban_savebanfile() - return 1 - return 0 - -/mob/verb/displayjobbans() - set category = "OOC" - set name = "Display Current Jobbans" - set desc = "Displays all of your current jobbans." - - if(!client || !ckey) - return - if(config.ban_legacy_system) - //using the legacy .txt ban system - to_chat(src, "The server is using the legacy ban system. Ask an administrator for help!") - - else - //using the SQL ban system - var/is_actually_banned = FALSE - var/DBQuery/select_query = dbcon.NewQuery("SELECT bantime, bantype, reason, job, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey like '[ckey]' AND ((bantype like 'JOB_TEMPBAN' AND expiration_time > Now()) OR (bantype like 'JOB_PERMABAN')) AND isnull(unbanned) ORDER BY bantime DESC LIMIT 100") - select_query.Execute() - - while(select_query.NextRow()) - - var/bantime = select_query.item[1] - var/bantype = select_query.item[2] - var/reason = select_query.item[3] - var/job = select_query.item[4] - var/duration = select_query.item[5] - var/expiration = select_query.item[6] - var/ackey = select_query.item[7] - - if(bantype == "JOB_PERMABAN") - to_chat(src, "[bantype]: [job] - REASON: [reason], by [ackey]; [bantime]") - else if(bantype == "JOB_TEMPBAN") - to_chat(src, "[bantype]: [job] - REASON: [reason], by [ackey]; [bantime]; [duration]; expires [expiration]") - - is_actually_banned = TRUE - - if(is_actually_banned) - if(config.banappeals) - to_chat(src, "You can appeal the bans at: [config.banappeals]") - else - to_chat(src, "You have no active jobbans!") +GLOBAL_VAR(jobban_runonce) // Updates legacy bans with new info +GLOBAL_LIST_INIT(jobban_keylist, new()) // Linear list of jobban strings, kept around for the legacy system +GLOBAL_LIST_INIT(jobban_assoclist, new()) // Associative list, for efficiency + +// Matches string-based jobbans into ckey, rank, and reason groups +GLOBAL_DATUM_INIT(jobban_regex, /regex, regex("(\[\\S]+) - (\[^#]+\[^# ])(?: ## (.+))?")) + +/proc/jobban_assoc_insert(ckey, rank, reason) + if(!ckey || !rank) + return + if(!GLOB.jobban_assoclist[ckey]) + GLOB.jobban_assoclist[ckey] = list() + GLOB.jobban_assoclist[ckey][rank] = reason || "Reason Unspecified" + +/proc/jobban_fullban(mob/M, rank, reason) + if(!M || !M.key) + return + GLOB.jobban_keylist.Add(text("[M.ckey] - [rank] ## [reason]")) + jobban_assoc_insert(M.ckey, rank, reason) + if(config.ban_legacy_system) + jobban_savebanfile() + +/proc/jobban_client_fullban(ckey, rank) + if(!ckey || !rank) + return + GLOB.jobban_keylist.Add(text("[ckey] - [rank]")) + jobban_assoc_insert(ckey, rank) + if(config.ban_legacy_system) + jobban_savebanfile() + +//returns a reason if M is banned from rank, returns 0 otherwise +/proc/jobban_isbanned(mob/M, rank) + if(!M || !rank) + return 0 + + if(config.guest_jobban && guest_jobbans(rank)) + if(IsGuestKey(M.key)) + return "Guest Job-ban" + + if(GLOB.jobban_assoclist[M.ckey]) + return GLOB.jobban_assoclist[M.ckey][rank] + else + return 0 + +/* +DEBUG +/mob/verb/list_all_jobbans() + set name = "list all jobbans" + + for(var/s in jobban_keylist) + to_chat(world, s) + +/mob/verb/reload_jobbans() + set name = "reload jobbans" + + jobban_loadbanfile() +*/ + +/hook/startup/proc/loadJobBans() + jobban_loadbanfile() + return 1 + +/proc/jobban_loadbanfile() + if(config.ban_legacy_system) + var/savefile/S=new("data/job_full.ban") + S["keys[0]"] >> GLOB.jobban_keylist + log_admin("Loading jobban_rank") + S["runonce"] >> GLOB.jobban_runonce + + if(!length(GLOB.jobban_keylist)) + GLOB.jobban_keylist=list() + log_admin("jobban_keylist was empty") + + for(var/s in GLOB.jobban_keylist) + if(GLOB.jobban_regex.Find(s)) + jobban_assoc_insert(GLOB.jobban_regex.group[1], GLOB.jobban_regex.group[2], GLOB.jobban_regex.group[3]) + else + log_runtime(EXCEPTION("Skipping malformed job ban: [s]")) + else + if(!establish_db_connection()) + log_world("Database connection failed. Reverting to the legacy ban system.") + config.ban_legacy_system = 1 + jobban_loadbanfile() + return + + //Job permabans + var/DBQuery/permabans = GLOB.dbcon.NewQuery("SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_PERMABAN' AND isnull(unbanned)") + permabans.Execute() + // Job tempbans + var/DBQuery/tempbans = GLOB.dbcon.NewQuery("SELECT ckey, job FROM [format_table_name("ban")] WHERE bantype = 'JOB_TEMPBAN' AND isnull(unbanned) AND expiration_time > Now()") + tempbans.Execute() + + while(TRUE) + var/ckey + var/job + if(permabans.NextRow()) + ckey = permabans.item[1] + job = permabans.item[2] + else if(tempbans.NextRow()) + ckey = tempbans.item[1] + job = tempbans.item[2] + else + break + + GLOB.jobban_keylist.Add("[ckey] - [job]") + jobban_assoc_insert(ckey, job) + +/proc/jobban_savebanfile() + var/savefile/S=new("data/job_full.ban") + S["keys[0]"] << GLOB.jobban_keylist + +/proc/jobban_unban(mob/M, rank) + jobban_remove("[M.ckey] - [rank]") + +/proc/jobban_unban_client(ckey, rank) + jobban_remove("[ckey] - [rank]") + +/proc/ban_unban_log_save(var/formatted_log) + text2file(formatted_log,"data/ban_unban_log.txt") + + +/proc/jobban_remove(X) + for(var/i = 1; i <= length(GLOB.jobban_keylist); i++) + if( findtext(GLOB.jobban_keylist[i], "[X]") ) + // This need to be here, instead of jobban_unban, due to direct calls to jobban_remove + if(GLOB.jobban_regex.Find(X)) + var/ckey = GLOB.jobban_regex.group[1] + var/rank = GLOB.jobban_regex.group[2] + if(GLOB.jobban_assoclist[ckey] && GLOB.jobban_assoclist[ckey][rank]) + GLOB.jobban_assoclist[ckey] -= rank + else + log_runtime(EXCEPTION("Attempted to remove non-existent job ban: [X]")) + else + log_runtime(EXCEPTION("Failed to remove malformed job ban from associative list: [X]")) + GLOB.jobban_keylist.Remove(GLOB.jobban_keylist[i]) + if(config.ban_legacy_system) + jobban_savebanfile() + return 1 + return 0 + +/mob/verb/displayjobbans() + set category = "OOC" + set name = "Display Current Jobbans" + set desc = "Displays all of your current jobbans." + + if(!client || !ckey) + return + if(config.ban_legacy_system) + //using the legacy .txt ban system + to_chat(src, "The server is using the legacy ban system. Ask an administrator for help!") + + else + //using the SQL ban system + var/is_actually_banned = FALSE + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT bantime, bantype, reason, job, duration, expiration_time, a_ckey FROM [format_table_name("ban")] WHERE ckey like '[ckey]' AND ((bantype like 'JOB_TEMPBAN' AND expiration_time > Now()) OR (bantype like 'JOB_PERMABAN')) AND isnull(unbanned) ORDER BY bantime DESC LIMIT 100") + select_query.Execute() + + while(select_query.NextRow()) + + var/bantime = select_query.item[1] + var/bantype = select_query.item[2] + var/reason = select_query.item[3] + var/job = select_query.item[4] + var/duration = select_query.item[5] + var/expiration = select_query.item[6] + var/ackey = select_query.item[7] + + if(bantype == "JOB_PERMABAN") + to_chat(src, "[bantype]: [job] - REASON: [reason], by [ackey]; [bantime]") + else if(bantype == "JOB_TEMPBAN") + to_chat(src, "[bantype]: [job] - REASON: [reason], by [ackey]; [bantime]; [duration]; expires [expiration]") + + is_actually_banned = TRUE + + if(is_actually_banned) + if(config.banappeals) + to_chat(src, "You can appeal the bans at: [config.banappeals]") + else + to_chat(src, "You have no active jobbans!") diff --git a/code/modules/admin/create_mob.dm b/code/modules/admin/create_mob.dm index fa105486654c..616d5aa9ee86 100644 --- a/code/modules/admin/create_mob.dm +++ b/code/modules/admin/create_mob.dm @@ -1,9 +1,9 @@ -/var/create_mob_html = null -/datum/admins/proc/create_mob(var/mob/user) - if(!create_mob_html) - var/mobjs = null - mobjs = jointext(typesof(/mob), ";") - create_mob_html = file2text('html/create_object.html') - create_mob_html = replacetext(create_mob_html, "null /* object types */", "\"[mobjs]\"") - - user << browse(replacetext(create_mob_html, "/* ref src */", UID()), "window=create_mob;size=425x475") +GLOBAL_VAR(create_mob_html) +/datum/admins/proc/create_mob(var/mob/user) + if(!GLOB.create_mob_html) + var/mobjs = null + mobjs = jointext(typesof(/mob), ";") + GLOB.create_mob_html = file2text('html/create_object.html') + GLOB.create_mob_html = replacetext(GLOB.create_mob_html, "null /* object types */", "\"[mobjs]\"") + + user << browse(replacetext(GLOB.create_mob_html, "/* ref src */", UID()), "window=create_mob;size=425x475") diff --git a/code/modules/admin/create_object.dm b/code/modules/admin/create_object.dm index 72bbaa47f5c4..c311be0d017e 100644 --- a/code/modules/admin/create_object.dm +++ b/code/modules/admin/create_object.dm @@ -1,23 +1,23 @@ -var/create_object_html = null -var/list/create_object_forms = list(/obj, /obj/structure, /obj/machinery, /obj/effect, /obj/item, /obj/mecha, /obj/item/clothing, /obj/item/stack, /obj/item/reagent_containers, /obj/item/gun) - -/datum/admins/proc/create_object(var/mob/user) - if(!create_object_html) - var/objectjs = null - objectjs = jointext(typesof(/obj), ";") - create_object_html = file2text('html/create_object.html') - create_object_html = replacetext(create_object_html, "null /* object types */", "\"[objectjs]\"") - - user << browse(replacetext(create_object_html, "/* ref src */", UID()), "window=create_object;size=425x475") - -/datum/admins/proc/quick_create_object(var/mob/user) - var/path = input("Select the path of the object you wish to create.", "Path", /obj) in create_object_forms - var/html_form = create_object_forms[path] - - if(!html_form) - var/objectjs = jointext(typesof(path), ";") - html_form = file2text('html/create_object.html') - html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"") - create_object_forms[path] = html_form - - user << browse(replacetext(html_form, "/* ref src */", UID()), "window=qco[path];size=425x475") \ No newline at end of file +GLOBAL_VAR(create_object_html) +GLOBAL_LIST_INIT(create_object_forms, list(/obj, /obj/structure, /obj/machinery, /obj/effect, /obj/item, /obj/mecha, /obj/item/clothing, /obj/item/stack, /obj/item/reagent_containers, /obj/item/gun)) + +/datum/admins/proc/create_object(var/mob/user) + if(!GLOB.create_object_html) + var/objectjs = null + objectjs = jointext(typesof(/obj), ";") + GLOB.create_object_html = file2text('html/create_object.html') + GLOB.create_object_html = replacetext(GLOB.create_object_html, "null /* object types */", "\"[objectjs]\"") + + user << browse(replacetext(GLOB.create_object_html, "/* ref src */", UID()), "window=create_object;size=425x475") + +/datum/admins/proc/quick_create_object(var/mob/user) + var/path = input("Select the path of the object you wish to create.", "Path", /obj) in GLOB.create_object_forms + var/html_form = GLOB.create_object_forms[path] + + if(!html_form) + var/objectjs = jointext(typesof(path), ";") + html_form = file2text('html/create_object.html') + html_form = replacetext(html_form, "null /* object types */", "\"[objectjs]\"") + GLOB.create_object_forms[path] = html_form + + user << browse(replacetext(html_form, "/* ref src */", UID()), "window=qco[path];size=425x475") diff --git a/code/modules/admin/create_poll.dm b/code/modules/admin/create_poll.dm index 1239bfd993a5..63592663469b 100644 --- a/code/modules/admin/create_poll.dm +++ b/code/modules/admin/create_poll.dm @@ -1,15 +1,15 @@ /client/proc/create_poll() set name = "Create Server Poll" set category = "Server" - if(!check_rights(R_SERVER)) + if(!check_rights(R_SERVER)) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(src, "Failed to establish database connection.") return create_poll_window() /client/proc/create_poll_window(var/errormessage = "") - if(!check_rights(R_SERVER)) + if(!check_rights(R_SERVER)) return var/output={" @@ -75,7 +75,7 @@ function onload() {
    "} - for(var/datum/prize_item/item in global_prizes.prizes) + for(var/datum/prize_item/item in GLOB.global_prizes.prizes) var/cost_class="affordable" if(item.cost>tickets) cost_class="toomuch" - var/itemID = global_prizes.prizes.Find(item) + var/itemID = GLOB.global_prizes.prizes.Find(item) var/row_color="light" if(itemID%2 == 0) row_color="dark" @@ -185,12 +185,12 @@ th.cost.toomuch {background:maroon;} if(href_list["buy"]) var/itemID = text2num(href_list["buy"]) - var/datum/prize_item/item = global_prizes.prizes[itemID] + var/datum/prize_item/item = GLOB.global_prizes.prizes[itemID] var/sure = alert(usr,"Are you sure you wish to purchase [item.name] for [item.cost] tickets?","You sure?","Yes","No") in list("Yes","No") if(sure=="No") updateUsrDialog() return - if(!global_prizes.PlaceOrder(src, itemID)) + if(!GLOB.global_prizes.PlaceOrder(src, itemID)) to_chat(usr, "Unable to complete the exchange.") else to_chat(usr, "You've successfully purchased the item.") diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm index 8a3d598bb3dd..1edad36b8f39 100644 --- a/code/modules/arcade/prize_datums.dm +++ b/code/modules/arcade/prize_datums.dm @@ -1,5 +1,4 @@ - -var/global/datum/prizes/global_prizes = new +GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new()) /datum/prizes var/list/prizes = list() @@ -14,7 +13,7 @@ var/global/datum/prizes/global_prizes = new return if(!prize_counter) return 0 - var/datum/prize_item/item = global_prizes.prizes[itemID] + var/datum/prize_item/item = GLOB.global_prizes.prizes[itemID] if(!item) return 0 if(prize_counter.tickets >= item.cost) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index b2a44940053d..827e7893396e 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,150 +1,150 @@ -/obj/item/assembly - name = "assembly" - desc = "A small electronic device that should never exist." - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "" - flags = CONDUCT - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL = 100) - throwforce = 2 - throw_speed = 3 - throw_range = 10 - origin_tech = "magnets=1;engineering=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - - var/bomb_name = "bomb" // used for naming bombs / mines - - var/secured = TRUE - var/list/attached_overlays = null - var/obj/item/assembly_holder/holder = null - var/cooldown = FALSE //To prevent spam - var/wires = WIRE_RECEIVE | WIRE_PULSE - var/datum/wires/connected = null // currently only used by timer/signaler - - var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() - var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder - var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly - var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() - var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message - -/obj/item/assembly/proc/activate() //What the device does when turned on - return - -/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs - return - -/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct - return - -/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here - return - -/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another - return - -/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var - return - -/obj/item/assembly/proc/holder_movement() //Called when the holder is moved - return - -/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc) - return "The trigger assembly looks broken!" - -/obj/item/assembly/interact(mob/user) //Called when attack_self is called - return - -/obj/item/assembly/process_cooldown() - cooldown-- - if(cooldown <= 0) - return FALSE - spawn(10) - process_cooldown() - return TRUE - -/obj/item/assembly/Destroy() - if(istype(loc, /obj/item/assembly_holder) || istype(holder)) - var/obj/item/assembly_holder/A = loc - if(A.a_left == src) - A.a_left = null - else if(A.a_right == src) - A.a_right = null - holder = null - return ..() - -/obj/item/assembly/pulsed(radio = FALSE) - if(holder && (wires & WIRE_RECEIVE)) - activate() - if(radio && (wires & WIRE_RADIO_RECEIVE)) - activate() - return TRUE - -/obj/item/assembly/pulse(radio = FALSE) - if(holder && (wires & WIRE_PULSE)) - holder.process_activation(src, 1, 0) - if(holder && (wires & WIRE_PULSE_SPECIAL)) - holder.process_activation(src, 0, 1) - if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu - var/obj/item/grenade/G = loc - G.prime() // Adios, muchachos - return TRUE - -/obj/item/assembly/activate() - if(!secured || cooldown > 0) - return FALSE - cooldown = 2 - spawn(10) - process_cooldown() - return TRUE - -/obj/item/assembly/toggle_secure() - secured = !secured - update_icon() - return secured - -/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user) - holder = new /obj/item/assembly_holder(get_turf(src)) - if(holder.attach(A, src, user)) - to_chat(user, "You attach [A] to [src]!") - return TRUE - return FALSE - -/obj/item/assembly/attackby(obj/item/W, mob/user, params) - if(isassembly(W)) - var/obj/item/assembly/A = W - if(!A.secured && !secured) - attach_assembly(A, user) - return - - return ..() - -/obj/item/assembly/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(toggle_secure()) - to_chat(user, "[src] is ready!") - else - to_chat(user, "[src] can now be attached!") - -/obj/item/assembly/process() - STOP_PROCESSING(SSobj, src) - -/obj/item/assembly/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(secured) - . += "[src] is ready!" - else - . += "[src] can be attached!" - -/obj/item/assembly/attack_self(mob/user) - if(!user) - return - user.set_machine(src) - interact(user) - return TRUE - -/obj/item/assembly/interact(mob/user) - return \ No newline at end of file +/obj/item/assembly + name = "assembly" + desc = "A small electronic device that should never exist." + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "" + flags = CONDUCT + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL = 100) + throwforce = 2 + throw_speed = 3 + throw_range = 10 + origin_tech = "magnets=1;engineering=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + + var/bomb_name = "bomb" // used for naming bombs / mines + + var/secured = TRUE + var/list/attached_overlays = null + var/obj/item/assembly_holder/holder = null + var/cooldown = FALSE //To prevent spam + var/wires = WIRE_RECEIVE | WIRE_PULSE + var/datum/wires/connected = null // currently only used by timer/signaler + + var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() + var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder + var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly + var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() + var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message + +/obj/item/assembly/proc/activate() //What the device does when turned on + return + +/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs + return + +/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct + return + +/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here + return + +/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another + return + +/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var + return + +/obj/item/assembly/proc/holder_movement() //Called when the holder is moved + return + +/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc) + return "The trigger assembly looks broken!" + +/obj/item/assembly/interact(mob/user) //Called when attack_self is called + return + +/obj/item/assembly/process_cooldown() + cooldown-- + if(cooldown <= 0) + return FALSE + spawn(10) + process_cooldown() + return TRUE + +/obj/item/assembly/Destroy() + if(istype(loc, /obj/item/assembly_holder) || istype(holder)) + var/obj/item/assembly_holder/A = loc + if(A.a_left == src) + A.a_left = null + else if(A.a_right == src) + A.a_right = null + holder = null + return ..() + +/obj/item/assembly/pulsed(radio = FALSE) + if(holder && (wires & WIRE_RECEIVE)) + activate() + if(radio && (wires & WIRE_RADIO_RECEIVE)) + activate() + return TRUE + +/obj/item/assembly/pulse(radio = FALSE) + if(holder && (wires & WIRE_PULSE)) + holder.process_activation(src, 1, 0) + if(holder && (wires & WIRE_PULSE_SPECIAL)) + holder.process_activation(src, 0, 1) + if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu + var/obj/item/grenade/G = loc + G.prime() // Adios, muchachos + return TRUE + +/obj/item/assembly/activate() + if(!secured || cooldown > 0) + return FALSE + cooldown = 2 + spawn(10) + process_cooldown() + return TRUE + +/obj/item/assembly/toggle_secure() + secured = !secured + update_icon() + return secured + +/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user) + holder = new /obj/item/assembly_holder(get_turf(src)) + if(holder.attach(A, src, user)) + to_chat(user, "You attach [A] to [src]!") + return TRUE + return FALSE + +/obj/item/assembly/attackby(obj/item/W, mob/user, params) + if(isassembly(W)) + var/obj/item/assembly/A = W + if(!A.secured && !secured) + attach_assembly(A, user) + return + + return ..() + +/obj/item/assembly/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(toggle_secure()) + to_chat(user, "[src] is ready!") + else + to_chat(user, "[src] can now be attached!") + +/obj/item/assembly/process() + STOP_PROCESSING(SSobj, src) + +/obj/item/assembly/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(secured) + . += "[src] is ready!" + else + . += "[src] can be attached!" + +/obj/item/assembly/attack_self(mob/user) + if(!user) + return + user.set_machine(src) + interact(user) + return TRUE + +/obj/item/assembly/interact(mob/user) + return diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index d5039af0a847..bf62e2a828a3 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -1,180 +1,180 @@ -/obj/item/onetankbomb - name = "bomb" - icon = 'icons/obj/tank.dmi' - item_state = "assembly" - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 2 - throw_range = 4 - flags = CONDUCT //Copied this from old code, so this may or may not be necessary - var/status = 0 //0 - not readied //1 - bomb finished with welder - var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device - var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank - origin_tech = "materials=1;engineering=1" - -/obj/item/onetankbomb/examine(mob/user) - . = ..() - . += bombtank.examine(user) - -/obj/item/onetankbomb/update_icon() - if(bombtank) - icon_state = bombtank.icon_state - if(bombassembly) - overlays += bombassembly.icon_state - overlays += bombassembly.overlays - overlays += "bomb_assembly" - -/obj/item/onetankbomb/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/analyzer)) - bombtank.attackby(W, user, params) - return - return ..() - -/obj/item/onetankbomb/wrench_act(mob/user, obj/item/I) //This is basically bomb assembly code inverted. apparently it works. - if(status) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You disassemble [src].") - bombassembly.loc = user.loc - bombassembly.master = null - bombassembly = null - bombtank.loc = user.loc - bombtank.master = null - bombtank = null - qdel(src) - -/obj/item/onetankbomb/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, volume = I.tool_volume)) - return - if(!status) - status = TRUE - investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) - log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") - to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") - else - status = FALSE - investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - to_chat(user, "The hole has been closed.") - - -/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly - bombassembly.attack_self(user, 1) - add_fingerprint(user) - return - -/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - sleep(10) - if(!src) - return - if(status) - bombtank.detonate() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file - else - bombtank.release() - -/obj/item/onetankbomb/HasProximity(atom/movable/AM) - if(bombassembly) - bombassembly.HasProximity(AM) - -/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps - if(bombassembly) - bombassembly.Crossed(AM, oldloc) - -/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps - if(bombassembly) - bombassembly.on_found(finder) - -/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) - if(bombassembly) - bombassembly.hear_talk(M, message_pieces) - -/obj/item/onetankbomb/hear_message(mob/living/M, msg) - if(bombassembly) - bombassembly.hear_message(M, msg) - -// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- - -/obj/item/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb - var/obj/item/assembly_holder/S = W - var/mob/M = user - if(!S.secured) //Check if the assembly is secured - return - if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - return - - var/obj/item/onetankbomb/R = new /obj/item/onetankbomb(loc) - - M.drop_item() //Remove the assembly from your hands - M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it - M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor. - - R.bombassembly = S //Tell the bomb about its assembly part - S.master = R //Tell the assembly about its new owner - S.loc = R //Move the assembly out of the fucking way - - R.bombtank = src //Same for tank - master = R - loc = R - R.update_icon() - return - -/obj/item/tank/proc/detonate() //This happens when a bomb is told to explode - var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 - var/strength = 1 - - var/turf/ground_zero = get_turf(loc) - loc = null - - if(air_contents.temperature > (T0C + 400)) - strength = (fuel_moles/15) - - if(strength >=1) - explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) - else if(strength >=0.5) - explosion(ground_zero, 0, 1, 2, 4) - else if(strength >=0.2) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else if(air_contents.temperature > (T0C + 250)) - strength = (fuel_moles/20) - - if(strength >=1) - explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) - else if(strength >=0.5) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else if(air_contents.temperature > (T0C + 100)) - strength = (fuel_moles/25) - - if(strength >=1) - explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - air_update_turf() - if(master) - qdel(master) - qdel(src) - -/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. - var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(removed) - air_update_turf() +/obj/item/onetankbomb + name = "bomb" + icon = 'icons/obj/tank.dmi' + item_state = "assembly" + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 2 + throw_range = 4 + flags = CONDUCT //Copied this from old code, so this may or may not be necessary + var/status = 0 //0 - not readied //1 - bomb finished with welder + var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device + var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank + origin_tech = "materials=1;engineering=1" + +/obj/item/onetankbomb/examine(mob/user) + . = ..() + . += bombtank.examine(user) + +/obj/item/onetankbomb/update_icon() + if(bombtank) + icon_state = bombtank.icon_state + if(bombassembly) + overlays += bombassembly.icon_state + overlays += bombassembly.overlays + overlays += "bomb_assembly" + +/obj/item/onetankbomb/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/analyzer)) + bombtank.attackby(W, user, params) + return + return ..() + +/obj/item/onetankbomb/wrench_act(mob/user, obj/item/I) //This is basically bomb assembly code inverted. apparently it works. + if(status) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You disassemble [src].") + bombassembly.loc = user.loc + bombassembly.master = null + bombassembly = null + bombtank.loc = user.loc + bombtank.master = null + bombtank = null + qdel(src) + +/obj/item/onetankbomb/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + if(!status) + status = TRUE + investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) + log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") + to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") + else + status = FALSE + investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + to_chat(user, "The hole has been closed.") + + +/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly + bombassembly.attack_self(user, 1) + add_fingerprint(user) + return + +/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + sleep(10) + if(!src) + return + if(status) + bombtank.detonate() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file + else + bombtank.release() + +/obj/item/onetankbomb/HasProximity(atom/movable/AM) + if(bombassembly) + bombassembly.HasProximity(AM) + +/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps + if(bombassembly) + bombassembly.Crossed(AM, oldloc) + +/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps + if(bombassembly) + bombassembly.on_found(finder) + +/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) + if(bombassembly) + bombassembly.hear_talk(M, message_pieces) + +/obj/item/onetankbomb/hear_message(mob/living/M, msg) + if(bombassembly) + bombassembly.hear_message(M, msg) + +// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- + +/obj/item/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb + var/obj/item/assembly_holder/S = W + var/mob/M = user + if(!S.secured) //Check if the assembly is secured + return + if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + return + + var/obj/item/onetankbomb/R = new /obj/item/onetankbomb(loc) + + M.drop_item() //Remove the assembly from your hands + M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it + M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor. + + R.bombassembly = S //Tell the bomb about its assembly part + S.master = R //Tell the assembly about its new owner + S.loc = R //Move the assembly out of the fucking way + + R.bombtank = src //Same for tank + master = R + loc = R + R.update_icon() + return + +/obj/item/tank/proc/detonate() //This happens when a bomb is told to explode + var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 + var/strength = 1 + + var/turf/ground_zero = get_turf(loc) + loc = null + + if(air_contents.temperature > (T0C + 400)) + strength = (fuel_moles/15) + + if(strength >=1) + explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) + else if(strength >=0.5) + explosion(ground_zero, 0, 1, 2, 4) + else if(strength >=0.2) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else if(air_contents.temperature > (T0C + 250)) + strength = (fuel_moles/20) + + if(strength >=1) + explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) + else if(strength >=0.5) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else if(air_contents.temperature > (T0C + 100)) + strength = (fuel_moles/25) + + if(strength >=1) + explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + air_update_turf() + if(master) + qdel(master) + qdel(src) + +/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. + var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(removed) + air_update_turf() diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm index 6648a9ce22da..a83df5501c96 100644 --- a/code/modules/assembly/health.dm +++ b/code/modules/assembly/health.dm @@ -104,4 +104,4 @@ return attack_self(user) - return \ No newline at end of file + return diff --git a/code/modules/assembly/helpers.dm b/code/modules/assembly/helpers.dm index eef33d2763de..9b5e93931a77 100644 --- a/code/modules/assembly/helpers.dm +++ b/code/modules/assembly/helpers.dm @@ -1,44 +1,44 @@ -/proc/isassembly(O) - if(istype(O, /obj/item/assembly)) - return 1 - return 0 - -/proc/isigniter(O) - if(istype(O, /obj/item/assembly/igniter)) - return 1 - return 0 - -/proc/isinfared(O) - if(istype(O, /obj/item/assembly/infra)) - return 1 - return 0 - -/proc/isprox(O) - if(istype(O, /obj/item/assembly/prox_sensor)) - return 1 - return 0 - -/proc/issignaler(O) - if(istype(O, /obj/item/assembly/signaler)) - return 1 - return 0 - -/proc/istimer(O) - if(istype(O, /obj/item/assembly/timer)) - return 1 - return 0 - -/* -Name: IsSpecialAssembly -Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door -*/ - -/obj/proc/IsSpecialAssembly() - return 0 - -/* -Name: IsAssemblyHolder -Desc: If true is an object that can hold an assemblyholder object -*/ -/obj/proc/IsAssemblyHolder() - return 0 \ No newline at end of file +/proc/isassembly(O) + if(istype(O, /obj/item/assembly)) + return 1 + return 0 + +/proc/isigniter(O) + if(istype(O, /obj/item/assembly/igniter)) + return 1 + return 0 + +/proc/isinfared(O) + if(istype(O, /obj/item/assembly/infra)) + return 1 + return 0 + +/proc/isprox(O) + if(istype(O, /obj/item/assembly/prox_sensor)) + return 1 + return 0 + +/proc/issignaler(O) + if(istype(O, /obj/item/assembly/signaler)) + return 1 + return 0 + +/proc/istimer(O) + if(istype(O, /obj/item/assembly/timer)) + return 1 + return 0 + +/* +Name: IsSpecialAssembly +Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door +*/ + +/obj/proc/IsSpecialAssembly() + return 0 + +/* +Name: IsAssemblyHolder +Desc: If true is an object that can hold an assemblyholder object +*/ +/obj/proc/IsAssemblyHolder() + return 0 diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 236ee94bd9d1..6a6cd7e8d8e7 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -1,197 +1,197 @@ -/obj/item/assembly_holder - name = "Assembly" - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "holder" - item_state = "assembly" - flags = CONDUCT - throwforce = 5 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 10 - - var/secured = FALSE - var/obj/item/assembly/a_left = null - var/obj/item/assembly/a_right = null - -/obj/item/assembly_holder/proc/attach(obj/item/D, obj/item/D2, mob/user) - return - -/obj/item/assembly_holder/proc/process_activation(var/obj/item/D) - return - -/obj/item/assembly_holder/IsAssemblyHolder() - return TRUE - -/obj/item/assembly_holder/Destroy() - if(a_left) - a_left.holder = null - if(a_right) - a_right.holder = null - return ..() - -/obj/item/assembly_holder/attach(obj/item/D, obj/item/D2, mob/user) - if(!D || !D2) - return FALSE - if(!isassembly(D) || !isassembly(D2)) - return FALSE - var/obj/item/assembly/A1 = D - var/obj/item/assembly/A2 = D2 - if(A1.secured || A2.secured) - return FALSE - if(!A1.remove_item_from_storage(src)) - if(user) - user.remove_from_mob(A1) - A1.loc = src - if(!A2.remove_item_from_storage(src)) - if(user) - user.remove_from_mob(A2) - A2.loc = src - A1.holder = src - A2.holder = src - a_left = A1 - a_right = A2 - name = "[A1.name]-[A2.name] assembly" - update_icon() - return TRUE - - -/obj/item/assembly_holder/update_icon() - overlays.Cut() - if(a_left) - overlays += "[a_left.icon_state]_left" - for(var/O in a_left.attached_overlays) - overlays += "[O]_l" - if(a_right) - overlays += "[a_right.icon_state]_right" - for(var/O in a_right.attached_overlays) - overlays += "[O]_r" - if(master) - master.update_icon() - - -/obj/item/assembly_holder/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(secured) - . += "[src] is ready!" - else - . += "[src] can be attached!" - - -/obj/item/assembly_holder/HasProximity(atom/movable/AM) - if(a_left) - a_left.HasProximity(AM) - if(a_right) - a_right.HasProximity(AM) - - -/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) - if(a_left) - a_left.Crossed(AM, oldloc) - if(a_right) - a_right.Crossed(AM, oldloc) - -/obj/item/assembly_holder/on_found(mob/finder) - if(a_left) - a_left.on_found(finder) - if(a_right) - a_right.on_found(finder) - - -/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) - if(a_left) - a_left.hear_talk(M, message_pieces) - if(a_right) - a_right.hear_talk(M, message_pieces) - -/obj/item/assembly_holder/hear_message(mob/living/M, msg) - if(a_left) - a_left.hear_message(M, msg) - if(a_right) - a_right.hear_message(M, msg) - -/obj/item/assembly_holder/proc/process_movement() // infrared beams and prox sensors - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - -/obj/item/assembly_holder/Move() - . = ..() - process_movement() - return - -/obj/item/assembly_holder/pickup() - . = ..() - process_movement() - -/obj/item/assembly_holder/Bump() - ..() - process_movement() - -/obj/item/assembly_holder/throw_impact() // called when a throw stops - ..() - process_movement() - -/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - ..() - return - -/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/I) - if(!a_left || !a_right) - to_chat(user, "BUG:Assembly part missing, please report this!") - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - a_left.toggle_secure() - a_right.toggle_secure() - secured = !secured - if(secured) - to_chat(user, "[src] is ready!") - else - to_chat(user, "[src] can now be taken apart!") - update_icon() - -/obj/item/assembly_holder/attack_self(mob/user) - add_fingerprint(user) - if(secured) - if(!a_left || !a_right) - to_chat(user, "Assembly part missing!") - return - if(istype(a_left, a_right.type))//If they are the same type it causes issues due to window code - switch(alert("Which side would you like to use?",,"Left","Right")) - if("Left") - a_left.attack_self(user) - if("Right") - a_right.attack_self(user) - return - else - a_left.attack_self(user) - a_right.attack_self(user) - else - var/turf/T = get_turf(src) - if(!T) - return FALSE - if(a_left) - a_left.holder = null - a_left.loc = T - if(a_right) - a_right.holder = null - a_right.loc = T - qdel(src) - - -/obj/item/assembly_holder/process_activation(obj/D, normal = TRUE, special = TRUE) - if(!D) - return FALSE - if(normal && a_right && a_left) - if(a_right != D) - a_right.pulsed(0) - if(a_left != D) - a_left.pulsed(0) - if(master) - master.receive_signal() - return TRUE \ No newline at end of file +/obj/item/assembly_holder + name = "Assembly" + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "holder" + item_state = "assembly" + flags = CONDUCT + throwforce = 5 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 10 + + var/secured = FALSE + var/obj/item/assembly/a_left = null + var/obj/item/assembly/a_right = null + +/obj/item/assembly_holder/proc/attach(obj/item/D, obj/item/D2, mob/user) + return + +/obj/item/assembly_holder/proc/process_activation(var/obj/item/D) + return + +/obj/item/assembly_holder/IsAssemblyHolder() + return TRUE + +/obj/item/assembly_holder/Destroy() + if(a_left) + a_left.holder = null + if(a_right) + a_right.holder = null + return ..() + +/obj/item/assembly_holder/attach(obj/item/D, obj/item/D2, mob/user) + if(!D || !D2) + return FALSE + if(!isassembly(D) || !isassembly(D2)) + return FALSE + var/obj/item/assembly/A1 = D + var/obj/item/assembly/A2 = D2 + if(A1.secured || A2.secured) + return FALSE + if(!A1.remove_item_from_storage(src)) + if(user) + user.remove_from_mob(A1) + A1.loc = src + if(!A2.remove_item_from_storage(src)) + if(user) + user.remove_from_mob(A2) + A2.loc = src + A1.holder = src + A2.holder = src + a_left = A1 + a_right = A2 + name = "[A1.name]-[A2.name] assembly" + update_icon() + return TRUE + + +/obj/item/assembly_holder/update_icon() + overlays.Cut() + if(a_left) + overlays += "[a_left.icon_state]_left" + for(var/O in a_left.attached_overlays) + overlays += "[O]_l" + if(a_right) + overlays += "[a_right.icon_state]_right" + for(var/O in a_right.attached_overlays) + overlays += "[O]_r" + if(master) + master.update_icon() + + +/obj/item/assembly_holder/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(secured) + . += "[src] is ready!" + else + . += "[src] can be attached!" + + +/obj/item/assembly_holder/HasProximity(atom/movable/AM) + if(a_left) + a_left.HasProximity(AM) + if(a_right) + a_right.HasProximity(AM) + + +/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) + if(a_left) + a_left.Crossed(AM, oldloc) + if(a_right) + a_right.Crossed(AM, oldloc) + +/obj/item/assembly_holder/on_found(mob/finder) + if(a_left) + a_left.on_found(finder) + if(a_right) + a_right.on_found(finder) + + +/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) + if(a_left) + a_left.hear_talk(M, message_pieces) + if(a_right) + a_right.hear_talk(M, message_pieces) + +/obj/item/assembly_holder/hear_message(mob/living/M, msg) + if(a_left) + a_left.hear_message(M, msg) + if(a_right) + a_right.hear_message(M, msg) + +/obj/item/assembly_holder/proc/process_movement() // infrared beams and prox sensors + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + +/obj/item/assembly_holder/Move() + . = ..() + process_movement() + return + +/obj/item/assembly_holder/pickup() + . = ..() + process_movement() + +/obj/item/assembly_holder/Bump() + ..() + process_movement() + +/obj/item/assembly_holder/throw_impact() // called when a throw stops + ..() + process_movement() + +/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + ..() + return + +/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/I) + if(!a_left || !a_right) + to_chat(user, "BUG:Assembly part missing, please report this!") + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + a_left.toggle_secure() + a_right.toggle_secure() + secured = !secured + if(secured) + to_chat(user, "[src] is ready!") + else + to_chat(user, "[src] can now be taken apart!") + update_icon() + +/obj/item/assembly_holder/attack_self(mob/user) + add_fingerprint(user) + if(secured) + if(!a_left || !a_right) + to_chat(user, "Assembly part missing!") + return + if(istype(a_left, a_right.type))//If they are the same type it causes issues due to window code + switch(alert("Which side would you like to use?",,"Left","Right")) + if("Left") + a_left.attack_self(user) + if("Right") + a_right.attack_self(user) + return + else + a_left.attack_self(user) + a_right.attack_self(user) + else + var/turf/T = get_turf(src) + if(!T) + return FALSE + if(a_left) + a_left.holder = null + a_left.loc = T + if(a_right) + a_right.holder = null + a_right.loc = T + qdel(src) + + +/obj/item/assembly_holder/process_activation(obj/D, normal = TRUE, special = TRUE) + if(!D) + return FALSE + if(normal && a_right && a_left) + if(a_right != D) + a_right.pulsed(0) + if(a_left != D) + a_left.pulsed(0) + if(master) + master.receive_signal() + return TRUE diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 3bbfe65bbc0d..b81acbbe2b09 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -1,45 +1,45 @@ -/obj/item/assembly/igniter - name = "igniter" - desc = "A small electronic device able to ignite combustable substances." - icon_state = "igniter" - materials = list(MAT_METAL=500, MAT_GLASS=50) - origin_tech = "magnets=1" - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - -/obj/item/assembly/igniter/New() - ..() - sparks.set_up(2, 0, src) - sparks.attach(src) - -/obj/item/assembly/igniter/Destroy() - QDEL_NULL(sparks) - return ..() - - -/obj/item/assembly/igniter/describe() - return "The igniter is [secured ? "secured." : "unsecured."]" - - -/obj/item/assembly/igniter/activate() - if(!..()) - return FALSE//Cooldown check - var/turf/location = get_turf(loc) - if(location) - location.hotspot_expose(1000,1000) - if(istype(loc, /obj/item/assembly_holder)) - if(istype(loc.loc, /obj/structure/reagent_dispensers/fueltank)) - var/obj/structure/reagent_dispensers/fueltank/tank = loc.loc - if(tank) - tank.boom(TRUE) - if(istype(loc.loc, /obj/item/reagent_containers/glass/beaker)) - var/obj/item/reagent_containers/glass/beaker/beakerbomb = loc.loc - if(beakerbomb) - beakerbomb.heat_beaker() - sparks.start() - return TRUE - - -/obj/item/assembly/igniter/attack_self(mob/user) - activate() - add_fingerprint(user) - return +/obj/item/assembly/igniter + name = "igniter" + desc = "A small electronic device able to ignite combustable substances." + icon_state = "igniter" + materials = list(MAT_METAL=500, MAT_GLASS=50) + origin_tech = "magnets=1" + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + +/obj/item/assembly/igniter/New() + ..() + sparks.set_up(2, 0, src) + sparks.attach(src) + +/obj/item/assembly/igniter/Destroy() + QDEL_NULL(sparks) + return ..() + + +/obj/item/assembly/igniter/describe() + return "The igniter is [secured ? "secured." : "unsecured."]" + + +/obj/item/assembly/igniter/activate() + if(!..()) + return FALSE//Cooldown check + var/turf/location = get_turf(loc) + if(location) + location.hotspot_expose(1000,1000) + if(istype(loc, /obj/item/assembly_holder)) + if(istype(loc.loc, /obj/structure/reagent_dispensers/fueltank)) + var/obj/structure/reagent_dispensers/fueltank/tank = loc.loc + if(tank) + tank.boom(TRUE) + if(istype(loc.loc, /obj/item/reagent_containers/glass/beaker)) + var/obj/item/reagent_containers/glass/beaker/beakerbomb = loc.loc + if(beakerbomb) + beakerbomb.heat_beaker() + sparks.start() + return TRUE + + +/obj/item/assembly/igniter/attack_self(mob/user) + activate() + add_fingerprint(user) + return diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index b9d3ee2afc1b..19263e010abc 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -1,286 +1,286 @@ -/obj/item/assembly/infra - name = "infrared emitter" - desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." - icon_state = "infrared" - materials = list(MAT_METAL=1000, MAT_GLASS=500) - origin_tech = "magnets=2;materials=2" - - bomb_name = "tripwire mine" - - secured = FALSE // toggle_secure()'ed in New() for correct adding to processing_objects, won't work otherwise - dir = EAST - var/on = FALSE - var/visible = TRUE - var/obj/effect/beam/i_beam/first = null - var/obj/effect/beam/i_beam/last = null - var/max_nesting_level = 10 - var/turf/fire_location - var/emission_cycles = 0 - var/emission_cap = 20 - -/obj/item/assembly/infra/Destroy() - if(first) - QDEL_NULL(first) - last = null - fire_location = null - return ..() - -/obj/item/assembly/infra/describe() - return "The assembly is [secured ? "secure" : "not secure"]. The infrared trigger is [on ? "on" : "off"]." - -/obj/item/assembly/infra/examine(mob/user) - . = ..() - . += describe() - -/obj/item/assembly/infra/activate() - if(!..()) - return FALSE//Cooldown check - on = !on - update_icon() - return TRUE - -/obj/item/assembly/infra/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - on = FALSE - if(first) - qdel(first) - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/infra/New() - ..() - if(!secured) - toggle_secure() - -/obj/item/assembly/infra/proc/arm() // Forces the device to arm no matter its current state. - if(!secured) // Checked because arm() might be called sometime after the object is spawned. - toggle_secure() - on = 1 - -/obj/item/assembly/infra/update_icon() - overlays.Cut() - attached_overlays = list() - if(on) - overlays += "infrared_on" - attached_overlays += "infrared_on" - - if(holder) - holder.update_icon() - -/obj/item/assembly/infra/process() - var/turf/T = get_turf(src) - if(first && (!on || !fire_location || fire_location != T || emission_cycles >= emission_cap)) - qdel(first) - return - if(!on) - return - if(!secured) - return - if(first && last) - last.process() - emission_cycles++ - return - if(T) - fire_location = T - emission_cycles = 0 - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(T) - I.master = src - I.density = 1 - I.dir = dir - I.update_icon() - first = I - step(I, I.dir) - if(first) - I.density = FALSE - I.vis_spread(visible) - I.limit = 8 - I.process() - -/obj/item/assembly/infra/attack_hand() - qdel(first) - ..() - -/obj/item/assembly/infra/Move() - var/t = dir - . = ..() - dir = t - qdel(first) - -/obj/item/assembly/infra/holder_movement() - if(!holder) - return FALSE - qdel(first) - return TRUE - -/obj/item/assembly/infra/equipped(var/mob/user, var/slot) - qdel(first) - return ..() - -/obj/item/assembly/infra/pickup(mob/user) - qdel(first) - return ..() - -/obj/item/assembly/infra/proc/trigger_beam() - if(!secured || !on || cooldown > 0) - return FALSE - pulse(0) - audible_message("[bicon(src)] *beep* *beep*", null, 3) - if(first) - qdel(first) - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel - if(!secured) return - user.set_machine(src) - var/dat = {"Infrared Laser - Status: [on ? "On" : "Off"]
    - Visibility: [visible ? "Visible" : "Invisible"]
    - Current Direction: [capitalize(dir2text(dir))]
    -
    -

    Refresh -

    Close"} - var/datum/browser/popup = new(user, "infra", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "infra") - -/obj/item/assembly/infra/Topic(href, href_list) - ..() - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr << browse(null, "window=infra") - onclose(usr, "infra") - return - if(href_list["state"]) - on = !(on) - update_icon() - if(href_list["visible"]) - visible = !(visible) - if(first) - first.vis_spread(visible) - if(href_list["rotate"]) - rotate() - if(href_list["close"]) - usr << browse(null, "window=infra") - return - if(usr) - attack_self(usr) - -/obj/item/assembly/infra/verb/rotate()//This could likely be better - set name = "Rotate Infrared Laser" - set category = "Object" - set src in usr - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - dir = turn(dir, 90) - - if(usr.machine == src) - interact(usr) - - if(first) - qdel(first) - - - -/obj/item/assembly/infra/armed/New() - ..() - spawn(3) - if(holder) - if(holder.master) - dir = holder.master.dir - arm() - -/obj/item/assembly/infra/armed/stealth - visible = FALSE - - -/***************************IBeam*********************************/ - -/obj/effect/beam/i_beam - name = "i beam" - icon = 'icons/obj/projectiles.dmi' - icon_state = "ibeam" - var/obj/effect/beam/i_beam/next = null - var/obj/effect/beam/i_beam/previous = null - var/obj/item/assembly/infra/master = null - var/limit = null - var/visible = FALSE - var/left = null - var/life_cycles = 0 - var/life_cap = 20 - anchored = TRUE - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - - -/obj/effect/beam/i_beam/proc/hit() - if(master) - master.trigger_beam() - qdel(src) - -/obj/effect/beam/i_beam/proc/vis_spread(v) - visible = v - if(next) - next.vis_spread(v) - -/obj/effect/beam/i_beam/update_icon() - transform = turn(matrix(), dir2angle(dir)) - -/obj/effect/beam/i_beam/process() - life_cycles++ - if(loc.density || !master || life_cycles >= life_cap) - qdel(src) - return - if(left > 0) - left-- - if(left < 1) - if(!(visible)) - invisibility = 101 - else - invisibility = FALSE - else - invisibility = FALSE - - if(!next && (limit > 0)) - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(loc) - I.master = master - I.density = 1 - I.dir = dir - I.update_icon() - I.previous = src - next = I - step(I, I.dir) - if(next) - I.density = FALSE - I.vis_spread(visible) - I.limit = limit - 1 - master.last = I - I.process() - -/obj/effect/beam/i_beam/Bump() - qdel(src) - -/obj/effect/beam/i_beam/Bumped() - hit() - -/obj/effect/beam/i_beam/Crossed(atom/movable/AM, oldloc) - if(!isobj(AM) && !isliving(AM)) - return - if(istype(AM, /obj/effect)) - return - hit() - -/obj/effect/beam/i_beam/Destroy() - if(master.first == src) - master.first = null - QDEL_NULL(next) - if(previous) - previous.next = null - master.last = previous - return ..() +/obj/item/assembly/infra + name = "infrared emitter" + desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." + icon_state = "infrared" + materials = list(MAT_METAL=1000, MAT_GLASS=500) + origin_tech = "magnets=2;materials=2" + + bomb_name = "tripwire mine" + + secured = FALSE // toggle_secure()'ed in New() for correct adding to processing_objects, won't work otherwise + dir = EAST + var/on = FALSE + var/visible = TRUE + var/obj/effect/beam/i_beam/first = null + var/obj/effect/beam/i_beam/last = null + var/max_nesting_level = 10 + var/turf/fire_location + var/emission_cycles = 0 + var/emission_cap = 20 + +/obj/item/assembly/infra/Destroy() + if(first) + QDEL_NULL(first) + last = null + fire_location = null + return ..() + +/obj/item/assembly/infra/describe() + return "The assembly is [secured ? "secure" : "not secure"]. The infrared trigger is [on ? "on" : "off"]." + +/obj/item/assembly/infra/examine(mob/user) + . = ..() + . += describe() + +/obj/item/assembly/infra/activate() + if(!..()) + return FALSE//Cooldown check + on = !on + update_icon() + return TRUE + +/obj/item/assembly/infra/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + on = FALSE + if(first) + qdel(first) + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/assembly/infra/New() + ..() + if(!secured) + toggle_secure() + +/obj/item/assembly/infra/proc/arm() // Forces the device to arm no matter its current state. + if(!secured) // Checked because arm() might be called sometime after the object is spawned. + toggle_secure() + on = 1 + +/obj/item/assembly/infra/update_icon() + overlays.Cut() + attached_overlays = list() + if(on) + overlays += "infrared_on" + attached_overlays += "infrared_on" + + if(holder) + holder.update_icon() + +/obj/item/assembly/infra/process() + var/turf/T = get_turf(src) + if(first && (!on || !fire_location || fire_location != T || emission_cycles >= emission_cap)) + qdel(first) + return + if(!on) + return + if(!secured) + return + if(first && last) + last.process() + emission_cycles++ + return + if(T) + fire_location = T + emission_cycles = 0 + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(T) + I.master = src + I.density = 1 + I.dir = dir + I.update_icon() + first = I + step(I, I.dir) + if(first) + I.density = FALSE + I.vis_spread(visible) + I.limit = 8 + I.process() + +/obj/item/assembly/infra/attack_hand() + qdel(first) + ..() + +/obj/item/assembly/infra/Move() + var/t = dir + . = ..() + dir = t + qdel(first) + +/obj/item/assembly/infra/holder_movement() + if(!holder) + return FALSE + qdel(first) + return TRUE + +/obj/item/assembly/infra/equipped(var/mob/user, var/slot) + qdel(first) + return ..() + +/obj/item/assembly/infra/pickup(mob/user) + qdel(first) + return ..() + +/obj/item/assembly/infra/proc/trigger_beam() + if(!secured || !on || cooldown > 0) + return FALSE + pulse(0) + audible_message("[bicon(src)] *beep* *beep*", null, 3) + if(first) + qdel(first) + cooldown = 2 + spawn(10) + process_cooldown() + +/obj/item/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel + if(!secured) return + user.set_machine(src) + var/dat = {"Infrared Laser + Status: [on ? "On" : "Off"]
    + Visibility: [visible ? "Visible" : "Invisible"]
    + Current Direction: [capitalize(dir2text(dir))]
    +
    +

    Refresh +

    Close"} + var/datum/browser/popup = new(user, "infra", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "infra") + +/obj/item/assembly/infra/Topic(href, href_list) + ..() + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + usr << browse(null, "window=infra") + onclose(usr, "infra") + return + if(href_list["state"]) + on = !(on) + update_icon() + if(href_list["visible"]) + visible = !(visible) + if(first) + first.vis_spread(visible) + if(href_list["rotate"]) + rotate() + if(href_list["close"]) + usr << browse(null, "window=infra") + return + if(usr) + attack_self(usr) + +/obj/item/assembly/infra/verb/rotate()//This could likely be better + set name = "Rotate Infrared Laser" + set category = "Object" + set src in usr + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + dir = turn(dir, 90) + + if(usr.machine == src) + interact(usr) + + if(first) + qdel(first) + + + +/obj/item/assembly/infra/armed/New() + ..() + spawn(3) + if(holder) + if(holder.master) + dir = holder.master.dir + arm() + +/obj/item/assembly/infra/armed/stealth + visible = FALSE + + +/***************************IBeam*********************************/ + +/obj/effect/beam/i_beam + name = "i beam" + icon = 'icons/obj/projectiles.dmi' + icon_state = "ibeam" + var/obj/effect/beam/i_beam/next = null + var/obj/effect/beam/i_beam/previous = null + var/obj/item/assembly/infra/master = null + var/limit = null + var/visible = FALSE + var/left = null + var/life_cycles = 0 + var/life_cap = 20 + anchored = TRUE + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + + +/obj/effect/beam/i_beam/proc/hit() + if(master) + master.trigger_beam() + qdel(src) + +/obj/effect/beam/i_beam/proc/vis_spread(v) + visible = v + if(next) + next.vis_spread(v) + +/obj/effect/beam/i_beam/update_icon() + transform = turn(matrix(), dir2angle(dir)) + +/obj/effect/beam/i_beam/process() + life_cycles++ + if(loc.density || !master || life_cycles >= life_cap) + qdel(src) + return + if(left > 0) + left-- + if(left < 1) + if(!(visible)) + invisibility = 101 + else + invisibility = FALSE + else + invisibility = FALSE + + if(!next && (limit > 0)) + var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(loc) + I.master = master + I.density = 1 + I.dir = dir + I.update_icon() + I.previous = src + next = I + step(I, I.dir) + if(next) + I.density = FALSE + I.vis_spread(visible) + I.limit = limit - 1 + master.last = I + I.process() + +/obj/effect/beam/i_beam/Bump() + qdel(src) + +/obj/effect/beam/i_beam/Bumped() + hit() + +/obj/effect/beam/i_beam/Crossed(atom/movable/AM, oldloc) + if(!isobj(AM) && !isliving(AM)) + return + if(istype(AM, /obj/effect)) + return + hit() + +/obj/effect/beam/i_beam/Destroy() + if(master.first == src) + master.first = null + QDEL_NULL(next) + if(previous) + previous.next = null + master.last = previous + return ..() diff --git a/code/modules/assembly/mousetrap.dm b/code/modules/assembly/mousetrap.dm index f4869f10d33b..9b4c9b386774 100644 --- a/code/modules/assembly/mousetrap.dm +++ b/code/modules/assembly/mousetrap.dm @@ -1,144 +1,144 @@ -/obj/item/assembly/mousetrap - name = "mousetrap" - desc = "A handy little spring-loaded trap for catching pesty rodents." - icon_state = "mousetrap" - materials = list(MAT_METAL=100) - origin_tech = "combat=1;materials=2;engineering=1" - var/armed = FALSE - - bomb_name = "contact mine" - -/obj/item/assembly/mousetrap/examine(mob/user) - . = ..() - if(armed) - . += "It looks like it's armed." - -/obj/item/assembly/mousetrap/activate() - if(..()) - armed = !armed - if(!armed) - if(ishuman(usr)) - var/mob/living/carbon/human/user = usr - if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) - to_chat(user, "Your hand slips, setting off the trigger.") - pulse(0) - update_icon() - if(usr) - playsound(usr.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/assembly/mousetrap/describe() - return "The pressure switch is [armed ? "primed" : "safe"]." - -/obj/item/assembly/mousetrap/update_icon() - if(armed) - icon_state = "mousetraparmed" - else - icon_state = "mousetrap" - if(holder) - holder.update_icon() - -/obj/item/assembly/mousetrap/proc/triggered(mob/target, type = "feet") - if(!armed) - return - var/obj/item/organ/external/affecting = null - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(PIERCEIMMUNE in H.dna.species.species_traits) - playsound(src, 'sound/effects/snap.ogg', 50, TRUE) - armed = FALSE - update_icon() - pulse(FALSE) - return FALSE - switch(type) - if("feet") - if(!H.shoes) - affecting = H.get_organ(pick("l_leg", "r_leg")) - H.Weaken(3) - if("l_hand", "r_hand") - if(!H.gloves) - affecting = H.get_organ(type) - H.Stun(3) - if(affecting) - affecting.receive_damage(1, 0) - else if(ismouse(target)) - var/mob/living/simple_animal/mouse/M = target - visible_message("SPLAT!") - M.splat() - playsound(loc, 'sound/effects/snap.ogg', 50, 1) - layer = MOB_LAYER - 0.2 - armed = FALSE - update_icon() - pulse(0) - -/obj/item/assembly/mousetrap/attack_self(mob/living/user) - if(!armed) - to_chat(user, "You arm [src].") - else - if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ - "You accidentally trigger [src]!") - return - to_chat(user, "You disarm [src].") - armed = !armed - update_icon() - playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) - -/obj/item/assembly/mousetrap/attack_hand(mob/living/user) - if(armed) - if((user.getBrainLoss() >= 60 || CLUMSY in user.mutations) && prob(50)) - var/which_hand = "l_hand" - if(!user.hand) - which_hand = "r_hand" - triggered(user, which_hand) - user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ - "You accidentally trigger [src]!") - return - ..() - -/obj/item/assembly/mousetrap/Crossed(atom/movable/AM, oldloc) - if(armed) - if(ishuman(AM)) - var/mob/living/carbon/H = AM - if(H.m_intent == MOVE_INTENT_RUN) - triggered(H) - H.visible_message("[H] accidentally steps on [src].", \ - "You accidentally step on [src]") - else if(ismouse(AM)) - triggered(AM) - else if(AM.density) // For mousetrap grenades, set off by anything heavy - triggered(AM) - ..() - -/obj/item/assembly/mousetrap/on_found(mob/finder) - if(armed) - finder.visible_message("[finder] accidentally sets off [src], breaking [finder.p_their()] fingers.", \ - "You accidentally trigger [src]!") - triggered(finder, finder.hand ? "l_hand" : "r_hand") - return TRUE //end the search! - return FALSE - -/obj/item/assembly/mousetrap/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(!armed) - return ..() - visible_message("[src] is triggered by [AM].") - triggered(null) - -/obj/item/assembly/mousetrap/armed - icon_state = "mousetraparmed" - armed = 1 - - -/obj/item/assembly/mousetrap/verb/hide_under() - set src in oview(1) - set name = "Hide" - set category = "Object" - - if(usr.stat) - return - - layer = TURF_LAYER+0.2 - to_chat(usr, "You hide [src].") +/obj/item/assembly/mousetrap + name = "mousetrap" + desc = "A handy little spring-loaded trap for catching pesty rodents." + icon_state = "mousetrap" + materials = list(MAT_METAL=100) + origin_tech = "combat=1;materials=2;engineering=1" + var/armed = FALSE + + bomb_name = "contact mine" + +/obj/item/assembly/mousetrap/examine(mob/user) + . = ..() + if(armed) + . += "It looks like it's armed." + +/obj/item/assembly/mousetrap/activate() + if(..()) + armed = !armed + if(!armed) + if(ishuman(usr)) + var/mob/living/carbon/human/user = usr + if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) + to_chat(user, "Your hand slips, setting off the trigger.") + pulse(0) + update_icon() + if(usr) + playsound(usr.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) + +/obj/item/assembly/mousetrap/describe() + return "The pressure switch is [armed ? "primed" : "safe"]." + +/obj/item/assembly/mousetrap/update_icon() + if(armed) + icon_state = "mousetraparmed" + else + icon_state = "mousetrap" + if(holder) + holder.update_icon() + +/obj/item/assembly/mousetrap/proc/triggered(mob/target, type = "feet") + if(!armed) + return + var/obj/item/organ/external/affecting = null + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(PIERCEIMMUNE in H.dna.species.species_traits) + playsound(src, 'sound/effects/snap.ogg', 50, TRUE) + armed = FALSE + update_icon() + pulse(FALSE) + return FALSE + switch(type) + if("feet") + if(!H.shoes) + affecting = H.get_organ(pick("l_leg", "r_leg")) + H.Weaken(3) + if("l_hand", "r_hand") + if(!H.gloves) + affecting = H.get_organ(type) + H.Stun(3) + if(affecting) + affecting.receive_damage(1, 0) + else if(ismouse(target)) + var/mob/living/simple_animal/mouse/M = target + visible_message("SPLAT!") + M.splat() + playsound(loc, 'sound/effects/snap.ogg', 50, 1) + layer = MOB_LAYER - 0.2 + armed = FALSE + update_icon() + pulse(0) + +/obj/item/assembly/mousetrap/attack_self(mob/living/user) + if(!armed) + to_chat(user, "You arm [src].") + else + if((user.getBrainLoss() >= 60 || (CLUMSY in user.mutations)) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ + "You accidentally trigger [src]!") + return + to_chat(user, "You disarm [src].") + armed = !armed + update_icon() + playsound(user.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -3) + +/obj/item/assembly/mousetrap/attack_hand(mob/living/user) + if(armed) + if((user.getBrainLoss() >= 60 || CLUMSY in user.mutations) && prob(50)) + var/which_hand = "l_hand" + if(!user.hand) + which_hand = "r_hand" + triggered(user, which_hand) + user.visible_message("[user] accidentally sets off [src], breaking [user.p_their()] fingers.", \ + "You accidentally trigger [src]!") + return + ..() + +/obj/item/assembly/mousetrap/Crossed(atom/movable/AM, oldloc) + if(armed) + if(ishuman(AM)) + var/mob/living/carbon/H = AM + if(H.m_intent == MOVE_INTENT_RUN) + triggered(H) + H.visible_message("[H] accidentally steps on [src].", \ + "You accidentally step on [src]") + else if(ismouse(AM)) + triggered(AM) + else if(AM.density) // For mousetrap grenades, set off by anything heavy + triggered(AM) + ..() + +/obj/item/assembly/mousetrap/on_found(mob/finder) + if(armed) + finder.visible_message("[finder] accidentally sets off [src], breaking [finder.p_their()] fingers.", \ + "You accidentally trigger [src]!") + triggered(finder, finder.hand ? "l_hand" : "r_hand") + return TRUE //end the search! + return FALSE + +/obj/item/assembly/mousetrap/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(!armed) + return ..() + visible_message("[src] is triggered by [AM].") + triggered(null) + +/obj/item/assembly/mousetrap/armed + icon_state = "mousetraparmed" + armed = 1 + + +/obj/item/assembly/mousetrap/verb/hide_under() + set src in oview(1) + set name = "Hide" + set category = "Object" + + if(usr.stat) + return + + layer = TURF_LAYER+0.2 + to_chat(usr, "You hide [src].") diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm index e6ae3ec7bc49..7e777f235b1d 100644 --- a/code/modules/assembly/proximity.dm +++ b/code/modules/assembly/proximity.dm @@ -1,134 +1,134 @@ -/obj/item/assembly/prox_sensor - name = "proximity sensor" - desc = "Used for scanning and alerting when someone enters a certain proximity." - icon_state = "prox" - materials = list(MAT_METAL = 800, MAT_GLASS = 200) - origin_tech = "magnets=1;engineering=1" - - secured = 0 - - bomb_name = "proximity mine" - - var/scanning = 0 - var/timing = 0 - var/time = 10 - -/obj/item/assembly/prox_sensor/describe() - if(timing) - return "The proximity sensor is arming." - return "The proximity sensor is [scanning ? "armed" : "disarmed"]." - -/obj/item/assembly/prox_sensor/activate() - if(!..()) - return FALSE //Cooldown check - timing = !timing - update_icon() - return FALSE - -/obj/item/assembly/prox_sensor/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - scanning = 0 - timing = 0 - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/prox_sensor/HasProximity(atom/movable/AM) - if(!isobj(AM) && !isliving(AM)) - return - if(istype(AM, /obj/effect)) - return - if(AM.move_speed < 12) - sense() - -/obj/item/assembly/prox_sensor/proc/sense() - if(!secured || !scanning || cooldown > 0) - return FALSE - pulse(0) - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/prox_sensor/process() - if(timing && (time >= 0)) - time-- - if(timing && time <= 0) - timing = 0 - toggle_scan() - time = 10 - -/obj/item/assembly/prox_sensor/dropped() - ..() - spawn(0) - sense() - return - -/obj/item/assembly/prox_sensor/proc/toggle_scan() - if(!secured) - return FALSE - scanning = !scanning - update_icon() - -/obj/item/assembly/prox_sensor/update_icon() - overlays.Cut() - attached_overlays = list() - if(timing) - overlays += "prox_timing" - attached_overlays += "prox_timing" - if(scanning) - overlays += "prox_scanning" - attached_overlays += "prox_scanning" - if(holder) - holder.update_icon() - -/obj/item/assembly/prox_sensor/Move() - ..() - sense() - -/obj/item/assembly/prox_sensor/holder_movement() - sense() - -/obj/item/assembly/prox_sensor/interact(mob/user)//TODO: Change this to the wires thingy - if(!secured) - user.show_message("The [name] is unsecured!") - return FALSE - var/second = time % 60 - var/minute = (time - second) / 60 - var/dat = text("Proximity Sensor\n[] []:[]\n- - + +\n", (timing ? "Arming" : "Not Arming"), minute, second) - dat += "
    [scanning?"Armed":"Unarmed"] (Movement sensor active when armed!)" - dat += "

    Refresh" - dat += "

    Close" - var/datum/browser/popup = new(user, "prox", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "prox") - -/obj/item/assembly/prox_sensor/Topic(href, href_list) - ..() - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr << browse(null, "window=prox") - onclose(usr, "prox") - return - - if(href_list["scanning"]) - toggle_scan() - - if(href_list["time"]) - timing = text2num(href_list["time"]) - update_icon() - - if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - time += tp - time = min(max(round(time), 0), 600) - - if(href_list["close"]) - usr << browse(null, "window=prox") - return - - if(usr) - attack_self(usr) \ No newline at end of file +/obj/item/assembly/prox_sensor + name = "proximity sensor" + desc = "Used for scanning and alerting when someone enters a certain proximity." + icon_state = "prox" + materials = list(MAT_METAL = 800, MAT_GLASS = 200) + origin_tech = "magnets=1;engineering=1" + + secured = 0 + + bomb_name = "proximity mine" + + var/scanning = 0 + var/timing = 0 + var/time = 10 + +/obj/item/assembly/prox_sensor/describe() + if(timing) + return "The proximity sensor is arming." + return "The proximity sensor is [scanning ? "armed" : "disarmed"]." + +/obj/item/assembly/prox_sensor/activate() + if(!..()) + return FALSE //Cooldown check + timing = !timing + update_icon() + return FALSE + +/obj/item/assembly/prox_sensor/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + scanning = 0 + timing = 0 + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/assembly/prox_sensor/HasProximity(atom/movable/AM) + if(!isobj(AM) && !isliving(AM)) + return + if(istype(AM, /obj/effect)) + return + if(AM.move_speed < 12) + sense() + +/obj/item/assembly/prox_sensor/proc/sense() + if(!secured || !scanning || cooldown > 0) + return FALSE + pulse(0) + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + cooldown = 2 + spawn(10) + process_cooldown() + +/obj/item/assembly/prox_sensor/process() + if(timing && (time >= 0)) + time-- + if(timing && time <= 0) + timing = 0 + toggle_scan() + time = 10 + +/obj/item/assembly/prox_sensor/dropped() + ..() + spawn(0) + sense() + return + +/obj/item/assembly/prox_sensor/proc/toggle_scan() + if(!secured) + return FALSE + scanning = !scanning + update_icon() + +/obj/item/assembly/prox_sensor/update_icon() + overlays.Cut() + attached_overlays = list() + if(timing) + overlays += "prox_timing" + attached_overlays += "prox_timing" + if(scanning) + overlays += "prox_scanning" + attached_overlays += "prox_scanning" + if(holder) + holder.update_icon() + +/obj/item/assembly/prox_sensor/Move() + ..() + sense() + +/obj/item/assembly/prox_sensor/holder_movement() + sense() + +/obj/item/assembly/prox_sensor/interact(mob/user)//TODO: Change this to the wires thingy + if(!secured) + user.show_message("The [name] is unsecured!") + return FALSE + var/second = time % 60 + var/minute = (time - second) / 60 + var/dat = text("Proximity Sensor\n[] []:[]\n- - + +\n", (timing ? "Arming" : "Not Arming"), minute, second) + dat += "
    [scanning?"Armed":"Unarmed"] (Movement sensor active when armed!)" + dat += "

    Refresh" + dat += "

    Close" + var/datum/browser/popup = new(user, "prox", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "prox") + +/obj/item/assembly/prox_sensor/Topic(href, href_list) + ..() + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + usr << browse(null, "window=prox") + onclose(usr, "prox") + return + + if(href_list["scanning"]) + toggle_scan() + + if(href_list["time"]) + timing = text2num(href_list["time"]) + update_icon() + + if(href_list["tp"]) + var/tp = text2num(href_list["tp"]) + time += tp + time = min(max(round(time), 0), 600) + + if(href_list["close"]) + usr << browse(null, "window=prox") + return + + if(usr) + attack_self(usr) diff --git a/code/modules/assembly/shock_kit.dm b/code/modules/assembly/shock_kit.dm index 7ee6844deea9..d054ea8d3f5f 100644 --- a/code/modules/assembly/shock_kit.dm +++ b/code/modules/assembly/shock_kit.dm @@ -1,46 +1,46 @@ -/obj/item/assembly/shock_kit - name = "electrohelmet assembly" - desc = "This appears to be made from both an electropack and a helmet." - icon = 'icons/obj/assemblies.dmi' - icon_state = "shock_kit" - var/obj/item/clothing/head/helmet/part1 = null - var/obj/item/radio/electropack/part2 = null - var/status = 0 - w_class = WEIGHT_CLASS_HUGE - flags = CONDUCT - -/obj/item/assembly/shock_kit/Destroy() - QDEL_NULL(part1) - QDEL_NULL(part2) - return ..() - -/obj/item/assembly/shock_kit/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/wrench) && !status) - var/turf/T = loc - if(ismob(T)) - T = T.loc - part1.loc = T - part2.loc = T - part1.master = null - part2.master = null - part1 = null - part2 = null - qdel(src) - return - if(istype(W, /obj/item/screwdriver)) - status = !status - to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/attack_self(mob/user as mob) - part1.attack_self(user, status) - part2.attack_self(user, status) - add_fingerprint(user) - return - -/obj/item/assembly/shock_kit/receive_signal() - if(istype(loc, /obj/structure/chair/e_chair)) - var/obj/structure/chair/e_chair/C = loc - C.shock() - return +/obj/item/assembly/shock_kit + name = "electrohelmet assembly" + desc = "This appears to be made from both an electropack and a helmet." + icon = 'icons/obj/assemblies.dmi' + icon_state = "shock_kit" + var/obj/item/clothing/head/helmet/part1 = null + var/obj/item/radio/electropack/part2 = null + var/status = 0 + w_class = WEIGHT_CLASS_HUGE + flags = CONDUCT + +/obj/item/assembly/shock_kit/Destroy() + QDEL_NULL(part1) + QDEL_NULL(part2) + return ..() + +/obj/item/assembly/shock_kit/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/wrench) && !status) + var/turf/T = loc + if(ismob(T)) + T = T.loc + part1.loc = T + part2.loc = T + part1.master = null + part2.master = null + part1 = null + part2 = null + qdel(src) + return + if(istype(W, /obj/item/screwdriver)) + status = !status + to_chat(user, "[src] is now [status ? "secured" : "unsecured"]!") + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/attack_self(mob/user as mob) + part1.attack_self(user, status) + part2.attack_self(user, status) + add_fingerprint(user) + return + +/obj/item/assembly/shock_kit/receive_signal() + if(istype(loc, /obj/structure/chair/e_chair)) + var/obj/structure/chair/e_chair/C = loc + C.shock() + return diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm index 29f79cae1a7b..f774d28921f9 100644 --- a/code/modules/assembly/signaler.dm +++ b/code/modules/assembly/signaler.dm @@ -1,177 +1,177 @@ -/obj/item/assembly/signaler - name = "remote signaling device" - desc = "Used to remotely activate devices." - icon_state = "signaller" - item_state = "signaler" - materials = list(MAT_METAL=400, MAT_GLASS=120) - origin_tech = "magnets=1;bluespace=1" - wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE - - secured = 1 - var/receiving = FALSE - - bomb_name = "remote-control bomb" - - var/code = 30 - var/frequency = RSD_FREQ - var/delay = 0 - var/datum/radio_frequency/radio_connection - var/airlock_wire = null - -/obj/item/assembly/signaler/New() - ..() - if(SSradio) - set_frequency(frequency) - -/obj/item/assembly/signaler/Initialize() - ..() - if(SSradio) - set_frequency(frequency) - -/obj/item/assembly/signaler/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/item/assembly/signaler/describe() - return "[src]'s power light is [receiving ? "on" : "off"]" - -/obj/item/assembly/signaler/activate() - if(cooldown > 0) - return FALSE - cooldown = 2 - spawn(10) - process_cooldown() - - signal() - return TRUE - -/obj/item/assembly/signaler/update_icon() - if(holder) - holder.update_icon() - return - -/obj/item/assembly/signaler/interact(mob/user, flag1) - var/t1 = "-------" - var/dat = {" - - "} - if(!flag1) - dat += {" - Send Signal
    - Receiver is [receiving?"on":"off"]
    - "} - dat += {" - Frequency/Code for signaler:
    - Frequency: - - - - - [format_frequency(frequency)] - + - +
    - - Code: - - - - - [code] - + - +
    - [t1] -
    - "} - var/datum/browser/popup = new(user, "radio", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "radio") - -/obj/item/assembly/signaler/Topic(href, href_list) - ..() - - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr << browse(null, "window=radio") - onclose(usr, "radio") - return - - if(href_list["freq"]) - var/new_frequency = (frequency + text2num(href_list["freq"])) - if(new_frequency < RADIO_LOW_FREQ || new_frequency > RADIO_HIGH_FREQ) - new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) - set_frequency(new_frequency) - - if(href_list["code"]) - code += text2num(href_list["code"]) - code = round(code) - code = min(100, code) - code = max(1, code) - if(href_list["receive"]) - receiving = !receiving - - if(href_list["send"]) - spawn( 0 ) - signal() - - if(usr) - attack_self(usr) - -/obj/item/assembly/signaler/proc/signal() - if(!radio_connection) - return - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = "ACTIVATE" - radio_connection.post_signal(src, signal) - - var/time = time2text(world.realtime,"hh:mm:ss") - var/turf/T = get_turf(src) - if(usr) - lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") - -/obj/item/assembly/signaler/pulse(var/radio = FALSE) - if(connected && wires) - connected.Pulse(src) - else - return ..(radio) - -/obj/item/assembly/signaler/receive_signal(datum/signal/signal) - if(!receiving || !signal) - return FALSE - - if(signal.encryption != code) - return FALSE - - if(!(wires & WIRE_RADIO_RECEIVE)) - return FALSE - pulse(1) - - for(var/mob/O in hearers(1, loc)) - O.show_message("[bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) - return TRUE - -/obj/item/assembly/signaler/proc/set_frequency(new_frequency) - if(!SSradio) - sleep(20) - if(!SSradio) - return - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT) - -// Embedded signaller used in anomalies. -/obj/item/assembly/signaler/anomaly - name = "anomaly core" - desc = "The neutralized core of an anomaly. It'd probably be valuable for research." - icon_state = "anomaly core" - item_state = "electronic" - resistance_flags = FIRE_PROOF - receiving = TRUE - -/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) - if(..()) - for(var/obj/effect/anomaly/A in orange(0, src)) - A.anomalyNeutralize() - -/obj/item/assembly/signaler/anomaly/attack_self() - return +/obj/item/assembly/signaler + name = "remote signaling device" + desc = "Used to remotely activate devices." + icon_state = "signaller" + item_state = "signaler" + materials = list(MAT_METAL=400, MAT_GLASS=120) + origin_tech = "magnets=1;bluespace=1" + wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE + + secured = 1 + var/receiving = FALSE + + bomb_name = "remote-control bomb" + + var/code = 30 + var/frequency = RSD_FREQ + var/delay = 0 + var/datum/radio_frequency/radio_connection + var/airlock_wire = null + +/obj/item/assembly/signaler/New() + ..() + if(SSradio) + set_frequency(frequency) + +/obj/item/assembly/signaler/Initialize() + ..() + if(SSradio) + set_frequency(frequency) + +/obj/item/assembly/signaler/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/item/assembly/signaler/describe() + return "[src]'s power light is [receiving ? "on" : "off"]" + +/obj/item/assembly/signaler/activate() + if(cooldown > 0) + return FALSE + cooldown = 2 + spawn(10) + process_cooldown() + + signal() + return TRUE + +/obj/item/assembly/signaler/update_icon() + if(holder) + holder.update_icon() + return + +/obj/item/assembly/signaler/interact(mob/user, flag1) + var/t1 = "-------" + var/dat = {" + + "} + if(!flag1) + dat += {" + Send Signal
    + Receiver is [receiving?"on":"off"]
    + "} + dat += {" + Frequency/Code for signaler:
    + Frequency: + - + - + [format_frequency(frequency)] + + + +
    + + Code: + - + - + [code] + + + +
    + [t1] +
    + "} + var/datum/browser/popup = new(user, "radio", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "radio") + +/obj/item/assembly/signaler/Topic(href, href_list) + ..() + + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + usr << browse(null, "window=radio") + onclose(usr, "radio") + return + + if(href_list["freq"]) + var/new_frequency = (frequency + text2num(href_list["freq"])) + if(new_frequency < RADIO_LOW_FREQ || new_frequency > RADIO_HIGH_FREQ) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + + if(href_list["code"]) + code += text2num(href_list["code"]) + code = round(code) + code = min(100, code) + code = max(1, code) + if(href_list["receive"]) + receiving = !receiving + + if(href_list["send"]) + spawn( 0 ) + signal() + + if(usr) + attack_self(usr) + +/obj/item/assembly/signaler/proc/signal() + if(!radio_connection) + return + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = "ACTIVATE" + radio_connection.post_signal(src, signal) + + var/time = time2text(world.realtime,"hh:mm:ss") + var/turf/T = get_turf(src) + if(usr) + GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + +/obj/item/assembly/signaler/pulse(var/radio = FALSE) + if(connected && wires) + connected.Pulse(src) + else + return ..(radio) + +/obj/item/assembly/signaler/receive_signal(datum/signal/signal) + if(!receiving || !signal) + return FALSE + + if(signal.encryption != code) + return FALSE + + if(!(wires & WIRE_RADIO_RECEIVE)) + return FALSE + pulse(1) + + for(var/mob/O in hearers(1, loc)) + O.show_message("[bicon(src)] *beep* *beep*", 3, "*beep* *beep*", 2) + return TRUE + +/obj/item/assembly/signaler/proc/set_frequency(new_frequency) + if(!SSradio) + sleep(20) + if(!SSradio) + return + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_CHAT) + +// Embedded signaller used in anomalies. +/obj/item/assembly/signaler/anomaly + name = "anomaly core" + desc = "The neutralized core of an anomaly. It'd probably be valuable for research." + icon_state = "anomaly core" + item_state = "electronic" + resistance_flags = FIRE_PROOF + receiving = TRUE + +/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal) + if(..()) + for(var/obj/effect/anomaly/A in orange(0, src)) + A.anomalyNeutralize() + +/obj/item/assembly/signaler/anomaly/attack_self() + return diff --git a/code/modules/assembly/timer.dm b/code/modules/assembly/timer.dm index 2c2216fa9d00..cab132ab747c 100644 --- a/code/modules/assembly/timer.dm +++ b/code/modules/assembly/timer.dm @@ -1,127 +1,127 @@ -/obj/item/assembly/timer - name = "timer" - desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." - icon_state = "timer" - materials = list(MAT_METAL=500, MAT_GLASS=50) - origin_tech = "magnets=1;engineering=1" - - secured = FALSE - - bomb_name = "time bomb" - - var/timing = FALSE - var/time = 10 - var/repeat = FALSE - var/set_time = 10 - -/obj/item/assembly/timer/describe() - if(timing) - return "The timer is counting down from [time]!" - return "The timer is set for [time] seconds." - -/obj/item/assembly/timer/activate() - if(!..()) - return FALSE//Cooldown check - timing = !timing - update_icon() - return FALSE - -/obj/item/assembly/timer/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - timing = FALSE - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/timer/proc/timer_end() - if(!secured || cooldown > 0) - return FALSE - pulse(0) - if(loc) - loc.visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/timer/process() - if(timing && (time > 0)) - time -= 2 // 2 seconds per process() - if(timing && time <= 0) - timing = repeat - timer_end() - time = set_time - -/obj/item/assembly/timer/update_icon() - overlays.Cut() - attached_overlays = list() - if(timing) - overlays += "timer_timing" - attached_overlays += "timer_timing" - if(holder) - holder.update_icon() - -/obj/item/assembly/timer/interact(mob/user as mob)//TODO: Have this use the wires - if(!secured) - user.show_message("The [name] is unsecured!") - return FALSE - var/second = time % 60 - var/minute = (time - second) / 60 - var/set_second = set_time % 60 - var/set_minute = (set_time - set_second) / 60 - if(second < 10) second = "0[second]" - if(set_second < 10) set_second = "0[set_second]" - - var/dat = {" - -

    Timing Unit

    - [minute]:[second] [timing?"Stop":"Start"] Reset
    - Repeat: [repeat?"On":"Off"]
    - Timer set for - - - [set_minute]:[set_second] + + -
    -
    -

    - Refresh -

    - Close"} - var/datum/browser/popup = new(user, "timer", name, 400, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "timer") - -/obj/item/assembly/timer/Topic(href, href_list) - ..() - if(usr.incapacitated() || !in_range(loc, usr)) - usr << browse(null, "window=timer") - onclose(usr, "timer") - return - - if(href_list["time"]) - timing = !timing - if(timing && istype(holder, /obj/item/transfer_valve)) - message_admins("[key_name_admin(usr)] activated [src] attachment on [holder].") - investigate_log("[key_name(usr)] activated [src] attachment for [loc]", INVESTIGATE_BOMB) - log_game("[key_name(usr)] activated [src] attachment for [loc]") - update_icon() - if(href_list["reset"]) - time = set_time - - if(href_list["repeat"]) - repeat = !repeat - - if(href_list["tp"]) - var/tp = text2num(href_list["tp"]) - set_time += tp - set_time = min(max(round(set_time), 6), 600) - if(!timing) - time = set_time - - if(href_list["close"]) - usr << browse(null, "window=timer") - return - - if(usr) - attack_self(usr) +/obj/item/assembly/timer + name = "timer" + desc = "Used to time things. Works well with contraptions which has to count down. Tick tock." + icon_state = "timer" + materials = list(MAT_METAL=500, MAT_GLASS=50) + origin_tech = "magnets=1;engineering=1" + + secured = FALSE + + bomb_name = "time bomb" + + var/timing = FALSE + var/time = 10 + var/repeat = FALSE + var/set_time = 10 + +/obj/item/assembly/timer/describe() + if(timing) + return "The timer is counting down from [time]!" + return "The timer is set for [time] seconds." + +/obj/item/assembly/timer/activate() + if(!..()) + return FALSE//Cooldown check + timing = !timing + update_icon() + return FALSE + +/obj/item/assembly/timer/toggle_secure() + secured = !secured + if(secured) + START_PROCESSING(SSobj, src) + else + timing = FALSE + STOP_PROCESSING(SSobj, src) + update_icon() + return secured + +/obj/item/assembly/timer/proc/timer_end() + if(!secured || cooldown > 0) + return FALSE + pulse(0) + if(loc) + loc.visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + cooldown = 2 + spawn(10) + process_cooldown() + +/obj/item/assembly/timer/process() + if(timing && (time > 0)) + time -= 2 // 2 seconds per process() + if(timing && time <= 0) + timing = repeat + timer_end() + time = set_time + +/obj/item/assembly/timer/update_icon() + overlays.Cut() + attached_overlays = list() + if(timing) + overlays += "timer_timing" + attached_overlays += "timer_timing" + if(holder) + holder.update_icon() + +/obj/item/assembly/timer/interact(mob/user as mob)//TODO: Have this use the wires + if(!secured) + user.show_message("The [name] is unsecured!") + return FALSE + var/second = time % 60 + var/minute = (time - second) / 60 + var/set_second = set_time % 60 + var/set_minute = (set_time - set_second) / 60 + if(second < 10) second = "0[second]" + if(set_second < 10) set_second = "0[set_second]" + + var/dat = {" + +

    Timing Unit

    + [minute]:[second] [timing?"Stop":"Start"] Reset
    + Repeat: [repeat?"On":"Off"]
    + Timer set for + - - [set_minute]:[set_second] + + +
    +
    +

    + Refresh +

    + Close"} + var/datum/browser/popup = new(user, "timer", name, 400, 400) + popup.set_content(dat) + popup.open(0) + onclose(user, "timer") + +/obj/item/assembly/timer/Topic(href, href_list) + ..() + if(usr.incapacitated() || !in_range(loc, usr)) + usr << browse(null, "window=timer") + onclose(usr, "timer") + return + + if(href_list["time"]) + timing = !timing + if(timing && istype(holder, /obj/item/transfer_valve)) + message_admins("[key_name_admin(usr)] activated [src] attachment on [holder].") + investigate_log("[key_name(usr)] activated [src] attachment for [loc]", INVESTIGATE_BOMB) + log_game("[key_name(usr)] activated [src] attachment for [loc]") + update_icon() + if(href_list["reset"]) + time = set_time + + if(href_list["repeat"]) + repeat = !repeat + + if(href_list["tp"]) + var/tp = text2num(href_list["tp"]) + set_time += tp + set_time = min(max(round(set_time), 6), 600) + if(!timing) + time = set_time + + if(href_list["close"]) + usr << browse(null, "window=timer") + return + + if(usr) + attack_self(usr) diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm index a20cdb89caf9..3119b55e28ab 100644 --- a/code/modules/assembly/voice.dm +++ b/code/modules/assembly/voice.dm @@ -74,4 +74,4 @@ /obj/item/assembly/voice/noise/hear_message(mob/living/M as mob, msg) pulse(0) var/turf/T = get_turf(src) //otherwise it won't work in hand - T.visible_message("[bicon(src)] beeps!") \ No newline at end of file + T.visible_message("[bicon(src)] beeps!") diff --git a/code/modules/atmos_automation/console.dm b/code/modules/atmos_automation/console.dm index a810d3b08c53..8d23bb390aa4 100644 --- a/code/modules/atmos_automation/console.dm +++ b/code/modules/atmos_automation/console.dm @@ -54,7 +54,7 @@ /obj/machinery/computer/general_air_control/atmos_automation/proc/selectValidChildFor(datum/automation/parent, mob/user, list/valid_returntypes) var/list/choices=list() - for(var/childtype in automation_types) + for(var/childtype in GLOB.automation_types) var/datum/automation/A = new childtype(src) if(A.returntype == null) continue @@ -217,7 +217,7 @@ testing("AAC: Null cData in root JS array.") continue var/Atype=text2path(cData["type"]) - if(!(Atype in automation_types)) + if(!(Atype in GLOB.automation_types)) testing("AAC: Unrecognized Atype [Atype].") continue var/datum/automation/A = new Atype(src) diff --git a/code/modules/atmos_automation/implementation/digital_valves.dm b/code/modules/atmos_automation/implementation/digital_valves.dm index a143c0e98697..dc0d0b2be00a 100644 --- a/code/modules/atmos_automation/implementation/digital_valves.dm +++ b/code/modules/atmos_automation/implementation/digital_valves.dm @@ -41,4 +41,4 @@ return valve = input("Select a valve:", "Sensor Data", valve) as null|anything in valves parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/emitters.dm b/code/modules/atmos_automation/implementation/emitters.dm index 100d68708cda..e6315fbceb2f 100644 --- a/code/modules/atmos_automation/implementation/emitters.dm +++ b/code/modules/atmos_automation/implementation/emitters.dm @@ -39,4 +39,4 @@ return emitter = input("Select an emitter:", "Emitter", emitter) as null|anything in emitters parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/injectors.dm b/code/modules/atmos_automation/implementation/injectors.dm index cc17dd27921b..d6747f314478 100644 --- a/code/modules/atmos_automation/implementation/injectors.dm +++ b/code/modules/atmos_automation/implementation/injectors.dm @@ -80,4 +80,4 @@ injector_names|=I.id_tag injector = input("Select an injector:", "Sensor Data", injector) as null|anything in injector_names parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/scrubbers.dm b/code/modules/atmos_automation/implementation/scrubbers.dm index 4188773a6a4c..54829d01ceae 100644 --- a/code/modules/atmos_automation/implementation/scrubbers.dm +++ b/code/modules/atmos_automation/implementation/scrubbers.dm @@ -84,13 +84,13 @@ parent.updateUsrDialog() return 1 -var/global/list/gas_labels=list( +GLOBAL_LIST_INIT(gas_labels, list( "co2" = "CO2", "tox" = "Plasma", "n2o" = "N2O", "o2" = "O2", "n2" = "N2" -) +)) /datum/automation/set_scrubber_gasses name="Scrubber: Gasses" @@ -131,7 +131,7 @@ var/global/list/gas_labels=list( GetText() var/txt = "Set Scrubber [fmtString(scrubber)] to scrub " for(var/gas in gasses) - txt += " [gas_labels[gas]] ([gasses[gas] ? "on" : "off"])," + txt += " [GLOB.gas_labels[gas]] ([gasses[gas] ? "on" : "off"])," return txt Topic(href,href_list) @@ -150,4 +150,4 @@ var/global/list/gas_labels=list( injector_names|=S.id_tag scrubber = input("Select a scrubber:", "Scrubbers", scrubber) as null|anything in injector_names parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/implementation/sensors.dm b/code/modules/atmos_automation/implementation/sensors.dm index 487298aa2664..d86d96124701 100644 --- a/code/modules/atmos_automation/implementation/sensors.dm +++ b/code/modules/atmos_automation/implementation/sensors.dm @@ -53,4 +53,4 @@ sensor_list|=M.id_tag sensor = input("Select a sensor:", "Sensor Data", field) as null|anything in sensor_list parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/atmos_automation/statements.dm b/code/modules/atmos_automation/statements.dm index 72937e18eae4..3f5ed0fb6a76 100644 --- a/code/modules/atmos_automation/statements.dm +++ b/code/modules/atmos_automation/statements.dm @@ -1,4 +1,4 @@ -var/global/automation_types = subtypesof(/datum/automation) +GLOBAL_LIST_INIT(automation_types, subtypesof(/datum/automation)) #define AUTOM_RT_NULL 0 #define AUTOM_RT_NUM 1 @@ -56,7 +56,7 @@ var/global/automation_types = subtypesof(/datum/automation) if(isnull(cData) || !("type" in cData)) return null var/Atype=text2path(cData["type"]) - if(!(Atype in automation_types)) + if(!(Atype in GLOB.automation_types)) return null var/datum/automation/A = new Atype(parent) A.Import(cData) @@ -70,7 +70,7 @@ var/global/automation_types = subtypesof(/datum/automation) . += null continue var/Atype=text2path(cData["type"]) - if(!(Atype in automation_types)) + if(!(Atype in GLOB.automation_types)) continue var/datum/automation/A = new Atype(parent) A.Import(cData) @@ -454,4 +454,4 @@ var/global/automation_types = subtypesof(/datum/automation) if(href_list["set_value"]) value = input("Set a value:", "Static Value", value) as num parent.updateUsrDialog() - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/awaymissions/exile.dm b/code/modules/awaymissions/exile.dm index 62700812a6bf..5f750c161443 100644 --- a/code/modules/awaymissions/exile.dm +++ b/code/modules/awaymissions/exile.dm @@ -1,44 +1,44 @@ -//Exile implants will allow you to use the station gate, but not return home. -//This will allow security to exile badguys/for badguys to exile their kill targets - -/obj/item/implant/exile - name = "exile implant" - desc = "Prevents you from returning from away missions" - origin_tech = "materials=2;biotech=3;magnets=2;bluespace=3" - activated = 0 - -/obj/item/implant/exile/get_data() - var/dat = {"Implant Specifications:
    - Name: Nanotrasen Employee Exile Implant
    - Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
    "} - return dat - - -/obj/item/implanter/exile - name = "implanter (exile)" - -/obj/item/implanter/exile/New() - imp = new /obj/item/implant/exile( src ) - ..() - -/obj/item/implantcase/exile - name = "implant case - 'Exile'" - desc = "A glass case containing an exile implant." - -/obj/item/implantcase/exile/New() - imp = new /obj/item/implant/exile(src) - ..() - - -/obj/structure/closet/secure_closet/exile - name = "exile implants" - req_access = list(ACCESS_ARMORY) - -/obj/structure/closet/secure_closet/exile/New() - ..() - new /obj/item/implanter/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) - new /obj/item/implantcase/exile(src) \ No newline at end of file +//Exile implants will allow you to use the station gate, but not return home. +//This will allow security to exile badguys/for badguys to exile their kill targets + +/obj/item/implant/exile + name = "exile implant" + desc = "Prevents you from returning from away missions" + origin_tech = "materials=2;biotech=3;magnets=2;bluespace=3" + activated = 0 + +/obj/item/implant/exile/get_data() + var/dat = {"Implant Specifications:
    + Name: Nanotrasen Employee Exile Implant
    + Implant Details: The onboard gateway system has been modified to reject entry by individuals containing this implant
    "} + return dat + + +/obj/item/implanter/exile + name = "implanter (exile)" + +/obj/item/implanter/exile/New() + imp = new /obj/item/implant/exile( src ) + ..() + +/obj/item/implantcase/exile + name = "implant case - 'Exile'" + desc = "A glass case containing an exile implant." + +/obj/item/implantcase/exile/New() + imp = new /obj/item/implant/exile(src) + ..() + + +/obj/structure/closet/secure_closet/exile + name = "exile implants" + req_access = list(ACCESS_ARMORY) + +/obj/structure/closet/secure_closet/exile/New() + ..() + new /obj/item/implanter/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) + new /obj/item/implantcase/exile(src) diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm index 8bacd9a839cf..fc268c3ba356 100644 --- a/code/modules/awaymissions/gateway.dm +++ b/code/modules/awaymissions/gateway.dm @@ -1,295 +1,295 @@ -var/obj/machinery/gateway/centerstation/the_gateway = null -/obj/machinery/gateway - name = "gateway" - desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations." - icon = 'icons/obj/machines/gateway.dmi' - icon_state = "off" - density = 1 - anchored = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/active = 0 - -/obj/machinery/gateway/Initialize() - ..() - update_icon() - update_density_from_dir() - -/obj/machinery/gateway/proc/update_density_from_dir() - if(dir == 2) - density = 0 - - -/obj/machinery/gateway/update_icon() - if(active) - icon_state = "on" - return - icon_state = "off" - - - -//this is da important part wot makes things go -/obj/machinery/gateway/centerstation - density = 1 - icon_state = "offcenter" - use_power = IDLE_POWER_USE - - //warping vars - var/list/linked = list() - var/ready = 0 //have we got all the parts for a gateway? - var/wait = 0 //this just grabs world.time at world start - var/obj/machinery/gateway/centeraway/awaygate = null - -/obj/machinery/gateway/centerstation/New() - ..() - if(!the_gateway) - the_gateway = src - -/obj/machinery/gateway/centerstation/Initialize() - ..() - update_icon() - wait = world.time + config.gateway_delay //+ thirty minutes default - awaygate = locate(/obj/machinery/gateway/centeraway) in world - -/obj/machinery/gateway/centerstation/update_density_from_dir() - return - -/obj/machinery/gateway/centerstation/Destroy() - if(the_gateway == src) - the_gateway = null - return ..() - -/obj/machinery/gateway/centerstation/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - - - -/obj/machinery/gateway/centerstation/process() - if(stat & (NOPOWER)) - if(active) toggleoff() - return - - if(active) - use_power(5000) - - -/obj/machinery/gateway/centerstation/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) - if(!ready) - return - if(linked.len != 8) - return - if(!powered()) - return - if(!awaygate) - awaygate = locate(/obj/machinery/gateway/centeraway) in world - if(!awaygate) - to_chat(user, "Error: No destination found.") - return - if(world.time < wait) - to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centerstation/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -//okay, here's the good teleporting stuff -/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) - if(!ready) - return - if(!active) - return - if(!awaygate) - return - - if(awaygate.calibrated) - M.forceMove(get_step(awaygate.loc, SOUTH)) - M.dir = SOUTH - return - else - var/obj/effect/landmark/dest = pick(awaydestinations) - if(dest) - M.forceMove(dest.loc) - M.dir = SOUTH - use_power(5000) - return - - -/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W,/obj/item/multitool)) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - return ..() - -/////////////////////////////////////Away//////////////////////// - - -/obj/machinery/gateway/centeraway - density = 1 - icon_state = "offcenter" - use_power = NO_POWER_USE - var/calibrated = 1 - var/list/linked = list() //a list of the connected gateway chunks - var/ready = 0 - var/obj/machinery/gateway/centeraway/stationgate = null - - -/obj/machinery/gateway/centeraway/Initialize() - ..() - update_icon() - stationgate = locate(/obj/machinery/gateway/centerstation) in world - - -/obj/machinery/gateway/centeraway/update_density_from_dir() - return - -/obj/machinery/gateway/centeraway/update_icon() - if(active) - icon_state = "oncenter" - return - icon_state = "offcenter" - - -/obj/machinery/gateway/centeraway/proc/detect() - linked = list() //clear the list - var/turf/T = loc - - for(var/i in alldirs) - T = get_step(loc, i) - var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T - if(G) - linked.Add(G) - continue - - //this is only done if we fail to find a part - ready = 0 - toggleoff() - break - - if(linked.len == 8) - ready = 1 - - -/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) - if(!ready) - return - if(linked.len != 8) - return - if(!stationgate) - stationgate = locate(/obj/machinery/gateway/centerstation) in world - if(!stationgate) - to_chat(user, "Error: No destination found.") - return - - for(var/obj/machinery/gateway/G in linked) - G.active = 1 - G.update_icon() - active = 1 - update_icon() - - -/obj/machinery/gateway/centeraway/proc/toggleoff() - for(var/obj/machinery/gateway/G in linked) - G.active = 0 - G.update_icon() - active = 0 - update_icon() - - -/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) - if(!ready) - detect() - return - if(!active) - toggleon(user) - return - toggleoff() - - -/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM) - if(!ready) - return - if(!active) - return - if(!stationgate || QDELETED(stationgate)) - return - if(isliving(AM)) - if(exilecheck(AM)) - return - else - for(var/mob/living/L in AM.contents) - if(exilecheck(L)) - atom_say("Rejecting [AM]: Exile implant detected in contained lifeform.") - return - if(AM.has_buckled_mobs()) - for(var/mob/living/L in AM.buckled_mobs) - if(exilecheck(L)) - atom_say("Rejecting [AM]: Exile implant detected in close proximity lifeform.") - return - AM.forceMove(get_step(stationgate.loc, SOUTH)) - AM.setDir(SOUTH) - if(ismob(AM)) - var/mob/M = AM - if(M.client) - M.client.move_delay = max(world.time + 5, M.client.move_delay) - -/obj/machinery/gateway/centeraway/proc/exilecheck(var/mob/living/carbon/M) - for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents - if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket - to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") - return 1 - return 0 - -/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W,/obj/item/multitool)) - if(calibrated) - to_chat(user, "The gate is already calibrated, there is no work for you to do here.") - return - else - to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") - calibrated = 1 - return - return ..() \ No newline at end of file +var/obj/machinery/gateway/centerstation/the_gateway = null +/obj/machinery/gateway + name = "gateway" + desc = "A mysterious gateway built by unknown hands, it allows for faster than light travel to far-flung locations." + icon = 'icons/obj/machines/gateway.dmi' + icon_state = "off" + density = 1 + anchored = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/active = 0 + +/obj/machinery/gateway/Initialize() + ..() + update_icon() + update_density_from_dir() + +/obj/machinery/gateway/proc/update_density_from_dir() + if(dir == 2) + density = 0 + + +/obj/machinery/gateway/update_icon() + if(active) + icon_state = "on" + return + icon_state = "off" + + + +//this is da important part wot makes things go +/obj/machinery/gateway/centerstation + density = 1 + icon_state = "offcenter" + use_power = IDLE_POWER_USE + + //warping vars + var/list/linked = list() + var/ready = 0 //have we got all the parts for a gateway? + var/wait = 0 //this just grabs world.time at world start + var/obj/machinery/gateway/centeraway/awaygate = null + +/obj/machinery/gateway/centerstation/New() + ..() + if(!the_gateway) + the_gateway = src + +/obj/machinery/gateway/centerstation/Initialize() + ..() + update_icon() + wait = world.time + config.gateway_delay //+ thirty minutes default + awaygate = locate(/obj/machinery/gateway/centeraway) in world + +/obj/machinery/gateway/centerstation/update_density_from_dir() + return + +/obj/machinery/gateway/centerstation/Destroy() + if(the_gateway == src) + the_gateway = null + return ..() + +/obj/machinery/gateway/centerstation/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + + + +/obj/machinery/gateway/centerstation/process() + if(stat & (NOPOWER)) + if(active) toggleoff() + return + + if(active) + use_power(5000) + + +/obj/machinery/gateway/centerstation/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centerstation/proc/toggleon(mob/user as mob) + if(!ready) + return + if(linked.len != 8) + return + if(!powered()) + return + if(!awaygate) + awaygate = locate(/obj/machinery/gateway/centeraway) in world + if(!awaygate) + to_chat(user, "Error: No destination found.") + return + if(world.time < wait) + to_chat(user, "Error: Warpspace triangulation in progress. Estimated time to completion: [round(((wait - world.time) / 10) / 60)] minutes.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centerstation/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centerstation/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +//okay, here's the good teleporting stuff +/obj/machinery/gateway/centerstation/Bumped(atom/movable/M as mob|obj) + if(!ready) + return + if(!active) + return + if(!awaygate) + return + + if(awaygate.calibrated) + M.forceMove(get_step(awaygate.loc, SOUTH)) + M.dir = SOUTH + return + else + var/obj/effect/landmark/dest = pick(GLOB.awaydestinations) + if(dest) + M.forceMove(dest.loc) + M.dir = SOUTH + use_power(5000) + return + + +/obj/machinery/gateway/centerstation/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W,/obj/item/multitool)) + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return + return ..() + +/////////////////////////////////////Away//////////////////////// + + +/obj/machinery/gateway/centeraway + density = 1 + icon_state = "offcenter" + use_power = NO_POWER_USE + var/calibrated = 1 + var/list/linked = list() //a list of the connected gateway chunks + var/ready = 0 + var/obj/machinery/gateway/centeraway/stationgate = null + + +/obj/machinery/gateway/centeraway/Initialize() + ..() + update_icon() + stationgate = locate(/obj/machinery/gateway/centerstation) in world + + +/obj/machinery/gateway/centeraway/update_density_from_dir() + return + +/obj/machinery/gateway/centeraway/update_icon() + if(active) + icon_state = "oncenter" + return + icon_state = "offcenter" + + +/obj/machinery/gateway/centeraway/proc/detect() + linked = list() //clear the list + var/turf/T = loc + + for(var/i in GLOB.alldirs) + T = get_step(loc, i) + var/obj/machinery/gateway/G = locate(/obj/machinery/gateway) in T + if(G) + linked.Add(G) + continue + + //this is only done if we fail to find a part + ready = 0 + toggleoff() + break + + if(linked.len == 8) + ready = 1 + + +/obj/machinery/gateway/centeraway/proc/toggleon(mob/user as mob) + if(!ready) + return + if(linked.len != 8) + return + if(!stationgate) + stationgate = locate(/obj/machinery/gateway/centerstation) in world + if(!stationgate) + to_chat(user, "Error: No destination found.") + return + + for(var/obj/machinery/gateway/G in linked) + G.active = 1 + G.update_icon() + active = 1 + update_icon() + + +/obj/machinery/gateway/centeraway/proc/toggleoff() + for(var/obj/machinery/gateway/G in linked) + G.active = 0 + G.update_icon() + active = 0 + update_icon() + + +/obj/machinery/gateway/centeraway/attack_hand(mob/user as mob) + if(!ready) + detect() + return + if(!active) + toggleon(user) + return + toggleoff() + + +/obj/machinery/gateway/centeraway/Bumped(atom/movable/AM) + if(!ready) + return + if(!active) + return + if(!stationgate || QDELETED(stationgate)) + return + if(isliving(AM)) + if(exilecheck(AM)) + return + else + for(var/mob/living/L in AM.contents) + if(exilecheck(L)) + atom_say("Rejecting [AM]: Exile implant detected in contained lifeform.") + return + if(AM.has_buckled_mobs()) + for(var/mob/living/L in AM.buckled_mobs) + if(exilecheck(L)) + atom_say("Rejecting [AM]: Exile implant detected in close proximity lifeform.") + return + AM.forceMove(get_step(stationgate.loc, SOUTH)) + AM.setDir(SOUTH) + if(ismob(AM)) + var/mob/M = AM + if(M.client) + M.client.move_delay = max(world.time + 5, M.client.move_delay) + +/obj/machinery/gateway/centeraway/proc/exilecheck(var/mob/living/carbon/M) + for(var/obj/item/implant/exile/E in M)//Checking that there is an exile implant in the contents + if(E.imp_in == M)//Checking that it's actually implanted vs just in their pocket + to_chat(M, "The station gate has detected your exile implant and is blocking your entry.") + return 1 + return 0 + +/obj/machinery/gateway/centeraway/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W,/obj/item/multitool)) + if(calibrated) + to_chat(user, "The gate is already calibrated, there is no work for you to do here.") + return + else + to_chat(user, "Recalibration successful!: This gate's systems have been fine tuned. Travel to this gate will now be on target.") + calibrated = 1 + return + return ..() diff --git a/code/modules/awaymissions/loot.dm b/code/modules/awaymissions/loot.dm index 6497d62648a2..266eb53c8150 100644 --- a/code/modules/awaymissions/loot.dm +++ b/code/modules/awaymissions/loot.dm @@ -1,25 +1,25 @@ -/obj/effect/spawner/away/lootdrop - icon = 'icons/mob/screen_gen.dmi' - icon_state = "x2" - var/lootcount = 1 //how many items will be spawned - var/lootdoubles = 0 //if the same item can be spawned twice - var/loot = "" //a list of possible items to spawn- a string of paths - -/obj/effect/spawner/away/lootdrop/Initialize() - ..() - var/list/things = params2list(loot) - - if(things && things.len) - for(var/i = lootcount, i > 0, i--) - if(!things.len) - return - - var/loot_spawn = pick(things) - var/loot_path = text2path(loot_spawn) - - if(!loot_path || !lootdoubles) - things.Remove(loot_spawn) - continue - - new loot_path(get_turf(src)) - qdel(src) \ No newline at end of file +/obj/effect/spawner/away/lootdrop + icon = 'icons/mob/screen_gen.dmi' + icon_state = "x2" + var/lootcount = 1 //how many items will be spawned + var/lootdoubles = 0 //if the same item can be spawned twice + var/loot = "" //a list of possible items to spawn- a string of paths + +/obj/effect/spawner/away/lootdrop/Initialize() + ..() + var/list/things = params2list(loot) + + if(things && things.len) + for(var/i = lootcount, i > 0, i--) + if(!things.len) + return + + var/loot_spawn = pick(things) + var/loot_path = text2path(loot_spawn) + + if(!loot_path || !lootdoubles) + things.Remove(loot_spawn) + continue + + new loot_path(get_turf(src)) + qdel(src) diff --git a/code/modules/awaymissions/map_rng.dm b/code/modules/awaymissions/map_rng.dm index 77954eafb9fe..0c1d776fa7b6 100644 --- a/code/modules/awaymissions/map_rng.dm +++ b/code/modules/awaymissions/map_rng.dm @@ -17,7 +17,7 @@ if(tname) template_name = tname if(template_name) - template = map_templates[template_name] + template = GLOB.map_templates[template_name] /obj/effect/landmark/map_loader/Initialize() . = ..() @@ -47,5 +47,5 @@ ..() if(template_list) template_name = safepick(splittext(template_list, ";")) - template = map_templates[template_name] + template = GLOB.map_templates[template_name] load(template) diff --git a/code/modules/awaymissions/maploader/dmm_suite.dm b/code/modules/awaymissions/maploader/dmm_suite.dm index 21c2dac0dc81..1eee3d8fc345 100644 --- a/code/modules/awaymissions/maploader/dmm_suite.dm +++ b/code/modules/awaymissions/maploader/dmm_suite.dm @@ -1,5 +1,4 @@ -var/global/dmm_suite/maploader = new - +GLOBAL_DATUM_INIT(maploader, /dmm_suite, new()) dmm_suite{ /* @@ -70,4 +69,4 @@ dmm_suite{ // map_name: A valid name for the map to be saved, such as "castle" (Required). // flags: Any, or a combination, of several bit flags (Optional, see documentation). } - } \ No newline at end of file + } diff --git a/code/modules/awaymissions/maploader/reader.dm b/code/modules/awaymissions/maploader/reader.dm index 4d5d464e1366..9a7cf6b6405a 100644 --- a/code/modules/awaymissions/maploader/reader.dm +++ b/code/modules/awaymissions/maploader/reader.dm @@ -4,8 +4,8 @@ //As of 3.6.2016 //global datum that will preload variables on atoms instanciation -var/global/use_preloader = FALSE -var/global/dmm_suite/preloader/_preloader = new +GLOBAL_VAR_INIT(use_preloader, FALSE) +GLOBAL_DATUM_INIT(_preloader, /dmm_suite/preloader, new()) /dmm_suite // These regexes are global - meaning that starting the maploader again mid-load will @@ -96,7 +96,7 @@ var/global/dmm_suite/preloader/_preloader = new if(cropMap) continue else - space_manager.increase_max_zlevel_to(zcrd) //create a new z_level if needed + GLOB.space_manager.increase_max_zlevel_to(zcrd) //create a new z_level if needed bounds[MAP_MINX] = min(bounds[MAP_MINX], xcrdStart) bounds[MAP_MINZ] = min(bounds[MAP_MINZ], zcrd) @@ -156,10 +156,10 @@ var/global/dmm_suite/preloader/_preloader = new CHECK_TICK catch(var/exception/e) - _preloader.reset() + GLOB._preloader.reset() throw e - _preloader.reset() + GLOB._preloader.reset() log_debug("Loaded map in [stop_watch(watch)]s.") qdel(LM) if(bounds[MAP_MINX] == 1.#INF) // Shouldn't need to check every item @@ -271,14 +271,14 @@ var/global/dmm_suite/preloader/_preloader = new throw EXCEPTION("Oh no, I thought this was an area!") var/atom/instance - _preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation + GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation instance = LM.area_path_to_real_area(members[index]) if(crds) instance.contents.Add(crds) - if(use_preloader && instance) - _preloader.load(instance) + if(GLOB.use_preloader && instance) + GLOB._preloader.load(instance) //then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect @@ -316,7 +316,7 @@ var/global/dmm_suite/preloader/_preloader = new //Instance an atom at (x,y,z) and gives it the variables in attributes /dmm_suite/proc/instance_atom(path,list/attributes, x, y, z) var/atom/instance - _preloader.setup(attributes, path) + GLOB._preloader.setup(attributes, path) var/turf/T = locate(x,y,z) if(T) @@ -328,8 +328,8 @@ var/global/dmm_suite/preloader/_preloader = new else instance = new path (T)//first preloader pass - if(use_preloader && instance)//second preloader pass, for those atoms that don't ..() in New() - _preloader.load(instance) + if(GLOB.use_preloader && instance)//second preloader pass, for those atoms that don't ..() in New() + GLOB._preloader.load(instance) return instance @@ -439,7 +439,7 @@ var/global/dmm_suite/preloader/_preloader = new json_ready = 0 if("map_json_data" in the_attributes) json_ready = 1 - use_preloader = TRUE + GLOB.use_preloader = TRUE attributes = the_attributes target_path = path @@ -458,11 +458,11 @@ var/global/dmm_suite/preloader/_preloader = new if(islist(value)) value = deepCopyList(value) what.vars[attribute] = value - use_preloader = FALSE + GLOB.use_preloader = FALSE // If the map loader fails, make this safe /dmm_suite/preloader/proc/reset() - use_preloader = FALSE + GLOB.use_preloader = FALSE attributes = list() target_path = null diff --git a/code/modules/awaymissions/mission_code/academy.dm b/code/modules/awaymissions/mission_code/academy.dm index f8d660581996..7cddddd8b087 100644 --- a/code/modules/awaymissions/mission_code/academy.dm +++ b/code/modules/awaymissions/mission_code/academy.dm @@ -108,7 +108,7 @@ if(3) //Swarm of creatures T.visible_message("A swarm of creatures surround [user]!") - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) new /mob/living/simple_animal/hostile/netherworld(get_step(get_turf(user),direction)) if(4) //Destroy Equipment @@ -131,7 +131,7 @@ T.visible_message("Unseen forces throw [user]!") user.Stun(6) user.adjustBruteLoss(50) - var/throw_dir = cardinal + var/throw_dir = GLOB.cardinal var/atom/throw_target = get_edge_target_turf(user, throw_dir) user.throw_at(throw_target, 200, 4) if(8) @@ -160,7 +160,7 @@ //Mad Dosh T.visible_message("Mad dosh shoots out of [src]!") var/turf/Start = get_turf(src) - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) var/turf/dirturf = get_step(Start,direction) if(rand(0,1)) new /obj/item/stack/spacecash/c1000(dirturf) @@ -255,7 +255,7 @@ if(!target_mob) return var/turf/Start = get_turf(user) - for(var/direction in alldirs) + for(var/direction in GLOB.alldirs) var/turf/T = get_step(Start,direction) if(!T.density) target_mob.Move(T) diff --git a/code/modules/awaymissions/mission_code/beach.dm b/code/modules/awaymissions/mission_code/beach.dm index 2da10ecbd988..9baaae33aae1 100644 --- a/code/modules/awaymissions/mission_code/beach.dm +++ b/code/modules/awaymissions/mission_code/beach.dm @@ -25,4 +25,4 @@ W.dir = dir spawn(1) W.loc = get_step(W, dir) - water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) \ No newline at end of file + water_timer = addtimer(CALLBACK(src, .proc/drip), water_frequency, TIMER_STOPPABLE) diff --git a/code/modules/awaymissions/mission_code/blackmarketpackers.dm b/code/modules/awaymissions/mission_code/blackmarketpackers.dm index 73e288b56ed1..fa4e6081594b 100644 --- a/code/modules/awaymissions/mission_code/blackmarketpackers.dm +++ b/code/modules/awaymissions/mission_code/blackmarketpackers.dm @@ -23,4 +23,4 @@ /area/awaymission/BMPship/Gate name = "\improper Gateway Block" icon_state = "away4" - requires_power = TRUE \ No newline at end of file + requires_power = TRUE diff --git a/code/modules/awaymissions/mission_code/centcomAway.dm b/code/modules/awaymissions/mission_code/centcomAway.dm index 1dc26f932e05..bd9890aa43a6 100644 --- a/code/modules/awaymissions/mission_code/centcomAway.dm +++ b/code/modules/awaymissions/mission_code/centcomAway.dm @@ -60,4 +60,4 @@ teams never did figure out what happened that last time... and I can't wrap my head \ around it myself. Why would a shuttle full of evacuees all snap and beat each other \ to death the moment they reached safety?
    \ - - D. Cereza" \ No newline at end of file + - D. Cereza" diff --git a/code/modules/awaymissions/mission_code/evil_santa.dm b/code/modules/awaymissions/mission_code/evil_santa.dm index 1597491b8000..19ec3ad2dc5d 100644 --- a/code/modules/awaymissions/mission_code/evil_santa.dm +++ b/code/modules/awaymissions/mission_code/evil_santa.dm @@ -20,4 +20,4 @@ Couldn't make it back to my shack. That gun would have helped, if only I brought it...
    \ Cave-in has me trapped in here, I just hope the distress signal reaches help in time...
    \
    \ - He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows." \ No newline at end of file + He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows. He knows." diff --git a/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm b/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm index 8dc3692179e1..bb1aae5f8a86 100644 --- a/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ghost_role_spawners/oldstation.dm @@ -107,4 +107,4 @@ /obj/structure/showcase/machinery/oldpod/used name = "opened cryogenic pod" - desc = "A cryogenic pod that has recently discharged its occupant. The pod appears non-functional." \ No newline at end of file + desc = "A cryogenic pod that has recently discharged its occupant. The pod appears non-functional." diff --git a/code/modules/awaymissions/mission_code/ruins/oldstation.dm b/code/modules/awaymissions/mission_code/ruins/oldstation.dm index e312dba541ed..e939cf06f32e 100644 --- a/code/modules/awaymissions/mission_code/ruins/oldstation.dm +++ b/code/modules/awaymissions/mission_code/ruins/oldstation.dm @@ -364,4 +364,4 @@ /area/ruin/space/ancientstation/hivebot name = "Hivebot Mothership" - icon_state = "teleporter" \ No newline at end of file + icon_state = "teleporter" diff --git a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm index 72b45e45e6ef..c5b568052134 100644 --- a/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm +++ b/code/modules/awaymissions/mission_code/ruins/wizardcrash.dm @@ -14,4 +14,4 @@ /obj/item/spellbook/oneuse/summonitem = 20, /obj/item/spellbook/oneuse/forcewall = 10, /obj/item/soulstone = 15 //spooky wizard stuff - ) \ No newline at end of file + ) diff --git a/code/modules/awaymissions/mission_code/spacebattle.dm b/code/modules/awaymissions/mission_code/spacebattle.dm index 146597fe4571..c68a61a67482 100644 --- a/code/modules/awaymissions/mission_code/spacebattle.dm +++ b/code/modules/awaymissions/mission_code/spacebattle.dm @@ -32,4 +32,4 @@ name = "\improper Syndicate Fighter" /area/awaymission/spacebattle/secret - name = "\improper Hidden Chamber" \ No newline at end of file + name = "\improper Hidden Chamber" diff --git a/code/modules/awaymissions/mission_code/spacehotel.dm b/code/modules/awaymissions/mission_code/spacehotel.dm index e9c74d73ea9a..45ebf9700cac 100644 --- a/code/modules/awaymissions/mission_code/spacehotel.dm +++ b/code/modules/awaymissions/mission_code/spacehotel.dm @@ -276,4 +276,4 @@ spawn(300) if(D.occupant == deadbeat) // they still haven't checked out... - checkout(roomid) \ No newline at end of file + checkout(roomid) diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm index 8d6a7fabe052..1beba373724d 100644 --- a/code/modules/awaymissions/mission_code/stationCollision.dm +++ b/code/modules/awaymissions/mission_code/stationCollision.dm @@ -94,11 +94,11 @@ obj/item/gun/energy/laser/retro/sc_retro */ //These vars hold the code itself, they'll be generated at round-start -var/sc_safecode1 = "[rand(0,9)]" -var/sc_safecode2 = "[rand(0,9)]" -var/sc_safecode3 = "[rand(0,9)]" -var/sc_safecode4 = "[rand(0,9)]" -var/sc_safecode5 = "[rand(0,9)]" +GLOBAL_VAR_INIT(sc_safecode1, "[rand(0,9)]") // Do these even need to be strings? Probably for the best +GLOBAL_VAR_INIT(sc_safecode2, "[rand(0,9)]") +GLOBAL_VAR_INIT(sc_safecode3, "[rand(0,9)]") +GLOBAL_VAR_INIT(sc_safecode4, "[rand(0,9)]") +GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]") //Pieces of paper actually containing the hints /obj/item/paper/sc_safehint_paper_prison @@ -106,13 +106,13 @@ var/sc_safecode5 = "[rand(0,9)]" /obj/item/paper/sc_safehint_paper_prison/New() ..() - info = "The ink is smudged, you can only make out a couple numbers: '[sc_safecode1]**[sc_safecode4]*'" + info = "The ink is smudged, you can only make out a couple numbers: '[GLOB.sc_safecode1]**[GLOB.sc_safecode4]*'" /obj/item/paper/sc_safehint_paper_hydro name = "shredded paper" /obj/item/paper/sc_safehint_paper_hydro/New() ..() - info = "Although the paper is shredded, you can clearly see the number: '[sc_safecode2]'" + info = "Although the paper is shredded, you can clearly see the number: '[GLOB.sc_safecode2]'" /obj/item/paper/sc_safehint_paper_caf name = "blood-soaked paper" @@ -124,7 +124,7 @@ var/sc_safecode5 = "[rand(0,9)]" /obj/item/paper/sc_safehint_paper_bible/New() ..() info = {"It would appear that the pen hidden with the paper had leaked ink over the paper. - However you can make out the last three digits:'[sc_safecode3][sc_safecode4][sc_safecode5]' + However you can make out the last three digits:'[GLOB.sc_safecode3][GLOB.sc_safecode4][GLOB.sc_safecode5]' "} /obj/item/paper/sc_safehint_paper_shuttle @@ -148,7 +148,7 @@ var/sc_safecode5 = "[rand(0,9)]" /obj/item/storage/secure/safe/sc_ssafe/New() ..() - l_code = "[sc_safecode1][sc_safecode2][sc_safecode3][sc_safecode4][sc_safecode5]" + l_code = "[GLOB.sc_safecode1][GLOB.sc_safecode2][GLOB.sc_safecode3][GLOB.sc_safecode4][GLOB.sc_safecode5]" l_set = 1 new /obj/item/gun/energy/mindflayer(src) new /obj/item/soulstone(src) @@ -199,4 +199,4 @@ var/sc_safecode5 = "[rand(0,9)]" return /obj/singularity/narsie/sc_Narsie/ex_act() - return \ No newline at end of file + return diff --git a/code/modules/awaymissions/pamphlet.dm b/code/modules/awaymissions/pamphlet.dm index 27ef5097e582..ffbb188ada2f 100644 --- a/code/modules/awaymissions/pamphlet.dm +++ b/code/modules/awaymissions/pamphlet.dm @@ -1,38 +1,38 @@ -/obj/item/paper/pamphlet - name = "pamphlet" - icon_state = "pamphlet" - info = "Welcome to the Nanotrasen Gateway project...
    \ - Congratulations! If you're reading this, you and your superiors have decided that you're \ - ready to commit to a life spent colonising the rolling hills of far away worlds. You \ - must be ready for a lifetime of adventure, a little bit of hard work, and an award \ - winning dental plan- but that's not all the Nanotrasen Gateway project has to offer.
    \ -
    Because we care about you, we feel it is only fair to make sure you know the risks \ - before you commit to joining the Nanotrasen Gateway project. All away destinations have \ - been fully scanned by a Nanotrasen expeditionary team, and are certified to be 100% safe. \ - We've even left a case of space beer along with the basic materials you'll need to expand \ - Nanotrasen's operational area and start your new life.

    \ - Gateway Operation Basics
    \ - All Nanotrasen approved Gateways operate on the same basic principals. They operate off \ - area equipment power as you would expect, but they also require a backup wire with at least \ - 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ - and will reject all attempts at operation.

    \ - Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ - searching for an output location. The amount of time this takes is variable, but the Gateway \ - interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ - searching process. Influenza will not interrupt the searching process. Temporal anomalies \ - may cause the estimate to be inaccurate, but will not interrupt the searching process.

    \ - Life On The Other Side
    \ - Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ - This is a normal side effect of travelling vast distances in a short period of time. You should \ - survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ - expeditionary teams have ensured the complete safety of all away locations, but in a small \ - number of cases, the Gateway they have established may not be immediately obvious. \ - Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ -

    A New World
    \ - As a participant in the Nanotrasen Gateway Project, you will be on the frontiers of space. \ - Though complete safety is assured, participants are advised to prepare for inhospitable \ - environs." - -//we don't want the silly text overlay! -/obj/item/paper/pamphlet/update_icon() - return \ No newline at end of file +/obj/item/paper/pamphlet + name = "pamphlet" + icon_state = "pamphlet" + info = "Welcome to the Nanotrasen Gateway project...
    \ + Congratulations! If you're reading this, you and your superiors have decided that you're \ + ready to commit to a life spent colonising the rolling hills of far away worlds. You \ + must be ready for a lifetime of adventure, a little bit of hard work, and an award \ + winning dental plan- but that's not all the Nanotrasen Gateway project has to offer.
    \ +
    Because we care about you, we feel it is only fair to make sure you know the risks \ + before you commit to joining the Nanotrasen Gateway project. All away destinations have \ + been fully scanned by a Nanotrasen expeditionary team, and are certified to be 100% safe. \ + We've even left a case of space beer along with the basic materials you'll need to expand \ + Nanotrasen's operational area and start your new life.

    \ + Gateway Operation Basics
    \ + All Nanotrasen approved Gateways operate on the same basic principals. They operate off \ + area equipment power as you would expect, but they also require a backup wire with at least \ + 128, 000 Watts of power running through it. Without this supply, it cannot safely function \ + and will reject all attempts at operation.

    \ + Once it is correctly setup, and once it has enough power to operate, the Gateway will begin \ + searching for an output location. The amount of time this takes is variable, but the Gateway \ + interface will give you an estimate accurate to the minute. Power loss will not interrupt the \ + searching process. Influenza will not interrupt the searching process. Temporal anomalies \ + may cause the estimate to be inaccurate, but will not interrupt the searching process.

    \ + Life On The Other Side
    \ + Once you have traversed the Gateway, you may experience some disorientation. Do not panic. \ + This is a normal side effect of travelling vast distances in a short period of time. You should \ + survey the immediate area, and attempt to locate your complimentary case of space beer. Our \ + expeditionary teams have ensured the complete safety of all away locations, but in a small \ + number of cases, the Gateway they have established may not be immediately obvious. \ + Do not panic if you cannot locate the return Gateway. Begin colonisation of the destination. \ +

    A New World
    \ + As a participant in the Nanotrasen Gateway Project, you will be on the frontiers of space. \ + Though complete safety is assured, participants are advised to prepare for inhospitable \ + environs." + +//we don't want the silly text overlay! +/obj/item/paper/pamphlet/update_icon() + return diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm index c406a76cb73b..96c5f080a145 100644 --- a/code/modules/awaymissions/zlevel.dm +++ b/code/modules/awaymissions/zlevel.dm @@ -1,4 +1,4 @@ -var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away_mission_config.txt") +GLOBAL_LIST_INIT(potentialRandomZlevels, generateMapList(filename = "config/away_mission_config.txt")) // Call this before you remove the last dirt on a z level - that way, all objects // will have proper atmos and other important enviro things @@ -39,27 +39,27 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away T.ChangeTurf(T.baseturf) /proc/createRandomZlevel() - if(awaydestinations.len) //crude, but it saves another var! + if(GLOB.awaydestinations.len) //crude, but it saves another var! return - if(potentialRandomZlevels && potentialRandomZlevels.len) + if(GLOB.potentialRandomZlevels && GLOB.potentialRandomZlevels.len) var/watch = start_watch() log_startup_progress("Loading away mission...") - var/map = pick(potentialRandomZlevels) + var/map = pick(GLOB.potentialRandomZlevels) var/file = file(map) if(isfile(file)) - var/zlev = space_manager.add_new_zlevel(AWAY_MISSION, linkage = UNAFFECTED, traits = list(AWAY_LEVEL,BLOCK_TELEPORT)) - space_manager.add_dirt(zlev) - maploader.load_map(file, z_offset = zlev) + var/zlev = GLOB.space_manager.add_new_zlevel(AWAY_MISSION, linkage = UNAFFECTED, traits = list(AWAY_LEVEL,BLOCK_TELEPORT)) + GLOB.space_manager.add_dirt(zlev) + GLOB.maploader.load_map(file, z_offset = zlev) late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) - space_manager.remove_dirt(zlev) + GLOB.space_manager.remove_dirt(zlev) log_world(" Away mission loaded: [map]") for(var/obj/effect/landmark/L in GLOB.landmarks_list) if(L.name != "awaystart") continue - awaydestinations.Add(L) + GLOB.awaydestinations.Add(L) log_startup_progress(" Away mission loaded in [stop_watch(watch)]s.") @@ -69,22 +69,22 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away /proc/createALLZlevels() - if(awaydestinations.len) //crude, but it saves another var! + if(GLOB.awaydestinations.len) //crude, but it saves another var! return - if(potentialRandomZlevels && potentialRandomZlevels.len) + if(GLOB.potentialRandomZlevels && GLOB.potentialRandomZlevels.len) var/watch = start_watch() log_startup_progress("Loading away missions...") - for(var/map in potentialRandomZlevels) + for(var/map in GLOB.potentialRandomZlevels) var/file = file(map) if(isfile(file)) log_startup_progress("Loading away mission: [map]") - var/zlev = space_manager.add_new_zlevel() - space_manager.add_dirt(zlev) - maploader.load_map(file, z_offset = zlev) + var/zlev = GLOB.space_manager.add_new_zlevel() + GLOB.space_manager.add_dirt(zlev) + GLOB.maploader.load_map(file, z_offset = zlev) late_setup_level(block(locate(1, 1, zlev), locate(world.maxx, world.maxy, zlev))) - space_manager.remove_dirt(zlev) + GLOB.space_manager.remove_dirt(zlev) log_world(" Away mission loaded: [map]") //map_transition_config.Add(AWAY_MISSION_LIST) @@ -92,7 +92,7 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away for(var/obj/effect/landmark/L in GLOB.landmarks_list) if(L.name != "awaystart") continue - awaydestinations.Add(L) + GLOB.awaydestinations.Add(L) log_startup_progress(" Away mission loaded in [stop_watch(watch)]s.") watch = start_watch() diff --git a/code/modules/awaymissions/zvis.dm b/code/modules/awaymissions/zvis.dm index 102554f2e6e4..b35b0fb6ffc8 100644 --- a/code/modules/awaymissions/zvis.dm +++ b/code/modules/awaymissions/zvis.dm @@ -370,4 +370,4 @@ screen_loc = "CENTER[ox >= 0 ? "+" : ""][ox],CENTER[oy >= 0 ? "+" : ""][oy]" /obj/effect/view_portal_dummy/attack_ghost(mob/user) - owner.attack_ghost(user) \ No newline at end of file + owner.attack_ghost(user) diff --git a/code/modules/buildmode/README.md b/code/modules/buildmode/README.md index 37ce2ce6b1ba..015e6ce6a667 100644 --- a/code/modules/buildmode/README.md +++ b/code/modules/buildmode/README.md @@ -294,4 +294,4 @@ Existing varieties: + *Left click a location*: - Cause an explosion where you clicked. \ No newline at end of file + Cause an explosion where you clicked. diff --git a/code/modules/buildmode/bm_mode.dm b/code/modules/buildmode/bm_mode.dm index de4c09a192d3..0169df7cd62c 100644 --- a/code/modules/buildmode/bm_mode.dm +++ b/code/modules/buildmode/bm_mode.dm @@ -47,11 +47,11 @@ overlaystate = "blueOverlay" preview += image('icons/turf/overlays.dmi', T, overlaystate) BM.holder.images += preview - return T + return T /datum/buildmode_mode/proc/highlight_region(region) BM.holder.images -= preview - for(var/t in region) + for(var/T in region) preview += image('icons/turf/overlays.dmi', T, "redOverlay") BM.holder.images += preview @@ -84,4 +84,4 @@ deselect_region() /datum/buildmode_mode/proc/handle_selected_region(mob/user, params) - return \ No newline at end of file + return diff --git a/code/modules/buildmode/buildmode.dm b/code/modules/buildmode/buildmode.dm index 7f43f20c9345..6c260c41cb25 100644 --- a/code/modules/buildmode/buildmode.dm +++ b/code/modules/buildmode/buildmode.dm @@ -125,4 +125,4 @@ #undef BM_SWITCHSTATE_NONE #undef BM_SWITCHSTATE_MODE -#undef BM_SWITCHSTATE_DIR \ No newline at end of file +#undef BM_SWITCHSTATE_DIR diff --git a/code/modules/buildmode/buttons.dm b/code/modules/buildmode/buttons.dm index c07be97a9ebb..2bec31f86502 100644 --- a/code/modules/buildmode/buttons.dm +++ b/code/modules/buildmode/buttons.dm @@ -85,4 +85,4 @@ /obj/screen/buildmode/quit/Click() bd.quit() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/buildmode/effects/line.dm b/code/modules/buildmode/effects/line.dm index 4b59e8a673ce..b49d35af0952 100644 --- a/code/modules/buildmode/effects/line.dm +++ b/code/modules/buildmode/effects/line.dm @@ -25,4 +25,4 @@ cl.images -= I cl = null QDEL_NULL(I) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/buildmode/submodes/advanced.dm b/code/modules/buildmode/submodes/advanced.dm index fadd46753bb7..c2e64fb488e5 100644 --- a/code/modules/buildmode/submodes/advanced.dm +++ b/code/modules/buildmode/submodes/advanced.dm @@ -56,4 +56,4 @@ if(isobj(object)) log_admin("Build Mode: [key_name(user)] deleted [object] at ([object.x],[object.y],[object.z])") qdel(object) - \ No newline at end of file + diff --git a/code/modules/buildmode/submodes/atmos.dm b/code/modules/buildmode/submodes/atmos.dm index f12fdb1eb1ac..b3a07374846b 100644 --- a/code/modules/buildmode/submodes/atmos.dm +++ b/code/modules/buildmode/submodes/atmos.dm @@ -69,4 +69,4 @@ T.air_update_turf() // admin log - log_admin("Build Mode: [key_name(user)] changed the atmos of region [COORD(cornerA)] to [COORD(cornerB)]. T: [temperature], P: [pressure], Ox: [oxygen]%, N2: [nitrogen]%, Plsma: [plasma]%, CO2: [cdiox]%, N2O: [nitrox]%. [ctrl_click ? "Overwrote base unsimulated turf gases." : ""]") \ No newline at end of file + log_admin("Build Mode: [key_name(user)] changed the atmos of region [COORD(cornerA)] to [COORD(cornerB)]. T: [temperature], P: [pressure], Ox: [oxygen]%, N2: [nitrogen]%, Plsma: [plasma]%, CO2: [cdiox]%, N2O: [nitrox]%. [ctrl_click ? "Overwrote base unsimulated turf gases." : ""]") diff --git a/code/modules/buildmode/submodes/fill.dm b/code/modules/buildmode/submodes/fill.dm index b143b2c9b90b..3c06a199e471 100644 --- a/code/modules/buildmode/submodes/fill.dm +++ b/code/modules/buildmode/submodes/fill.dm @@ -46,4 +46,4 @@ T.ChangeTurf(objholder) else var/obj/A = new objholder(T) - A.setDir(BM.build_dir) \ No newline at end of file + A.setDir(BM.build_dir) diff --git a/code/modules/buildmode/submodes/mapgen.dm b/code/modules/buildmode/submodes/mapgen.dm index 22be429c2cee..df6f6b73ed03 100644 --- a/code/modules/buildmode/submodes/mapgen.dm +++ b/code/modules/buildmode/submodes/mapgen.dm @@ -37,4 +37,4 @@ highlight_region(G.map) var/confirm = alert("Are you sure you want run the map generator?", "Run generator", "Yes", "No") if(confirm == "Yes") - G.generate() \ No newline at end of file + G.generate() diff --git a/code/modules/buildmode/submodes/save.dm b/code/modules/buildmode/submodes/save.dm index 43a1f9af174c..34a57e1f7e45 100644 --- a/code/modules/buildmode/submodes/save.dm +++ b/code/modules/buildmode/submodes/save.dm @@ -3,7 +3,7 @@ use_corner_selection = TRUE var/use_json = TRUE - + /datum/buildmode_mode/save/show_help(mob/user) to_chat(user, "***********************************************************") to_chat(user, "Left Mouse Button on turf/obj/mob = Select corner") @@ -23,6 +23,6 @@ if(use_json) map_flags = 32 // Magic number defined in `writer.dm` that I can't use directly // because #defines are for some reason our coding standard - var/our_map = maploader.save_map(cornerA, cornerB, map_name, map_flags) + var/our_map = GLOB.maploader.save_map(cornerA, cornerB, map_name, map_flags) user << ftp(our_map) // send the map they've made! Or are stealing, whatever to_chat(user, "Map saving complete! [our_map]") diff --git a/code/modules/busy_space/air_traffic.dm b/code/modules/busy_space/air_traffic.dm index b5cbf8e6fda8..e21414e8ed38 100644 --- a/code/modules/busy_space/air_traffic.dm +++ b/code/modules/busy_space/air_traffic.dm @@ -1,6 +1,5 @@ //Cactus, Speedbird, Dynasty, oh my - -var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller +GLOBAL_DATUM_INIT(atc, /datum/lore/atc_controller, new) /datum/lore/atc_controller var/delay_max = 10 MINUTES //Maximum amount of tiem between ATC messages. Default is 10 mins. @@ -30,29 +29,29 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller /datum/lore/atc_controller/proc/msg(var/message,var/sender) ASSERT(message) - global_announcer.autosay("[message]", sender ? sender : "[using_map.station_short] Space Control") + GLOB.global_announcer.autosay("[message]", sender ? sender : "[GLOB.using_map.station_short] Space Control") /datum/lore/atc_controller/proc/reroute_traffic(var/yes = 1) if(yes) if(!squelched) - msg("Rerouting traffic away from [using_map.station_name].") + msg("Rerouting traffic away from [GLOB.using_map.station_name].") squelched = TRUE else if(squelched) - msg("Resuming normal traffic routing around [using_map.station_name].") + msg("Resuming normal traffic routing around [GLOB.using_map.station_name].") squelched = FALSE /datum/lore/atc_controller/proc/shift_ending(var/evac = 0) - msg("Automated Shuttle departing [using_map.station_name] for [using_map.dock_name] on routine transfer route.", "NT Automated Shuttle") + msg("Automated Shuttle departing [GLOB.using_map.station_name] for [GLOB.using_map.dock_name] on routine transfer route.", "NT Automated Shuttle") sleep(5 SECONDS) - msg("Automated Shuttle, cleared to complete routine transfer from [using_map.station_name] to [using_map.dock_name].") + msg("Automated Shuttle, cleared to complete routine transfer from [GLOB.using_map.station_name] to [GLOB.using_map.dock_name].") /datum/lore/atc_controller/proc/random_convo() - var/one = pick(loremaster.organizations) //These will pick an index, not an instance - var/two = pick(loremaster.organizations) + var/one = pick(GLOB.loremaster.organizations) //These will pick an index, not an instance + var/two = pick(GLOB.loremaster.organizations) - var/datum/lore/organization/source = loremaster.organizations[one] //Resolve to the instances - var/datum/lore/organization/dest = loremaster.organizations[two] + var/datum/lore/organization/source = GLOB.loremaster.organizations[one] //Resolve to the instances + var/datum/lore/organization/dest = GLOB.loremaster.organizations[two] //Let's get some mission parameters var/owner = source.short_name //Use the short name @@ -62,13 +61,13 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller var/destname = pick(dest.destination_names) //Pick a random holding from the destination var/combined_name = "[owner] [prefix] [shipname]" - var/alt_atc_names = list("[using_map.station_short] TraCon", "[using_map.station_short] Control", "[using_map.station_short] STC", "[using_map.station_short] Airspace") - var/wrong_atc_names = list("Sol Command", "Orion Control", "[using_map.dock_name]") + var/alt_atc_names = list("[GLOB.using_map.station_short] TraCon", "[GLOB.using_map.station_short] Control", "[GLOB.using_map.station_short] STC", "[GLOB.using_map.station_short] Airspace") + var/wrong_atc_names = list("Sol Command", "Orion Control", "[GLOB.using_map.dock_name]") var/mission_noun = list("flight", "mission", "route") var/request_verb = list("requesting", "calling for", "asking for") //First response is 'yes', second is 'no' - var/requests = list("[using_map.station_short] transit clearance" = list("cleared to transit", "unable to approve, contact regional on 953.5"), + var/requests = list("[GLOB.using_map.station_short] transit clearance" = list("cleared to transit", "unable to approve, contact regional on 953.5"), "planetary flight rules" = list("cleared planetary flight rules", "unable to approve planetary flight rules due to traffic"), "special flight rules" = list("cleared special flight rules", "unable to approve special flight rules for your traffic class"), "current solar weather info" = list("sending you the relevant information via tightbeam", "cannot fulfill your request at the moment"), @@ -104,20 +103,20 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller if("wrong_freq") callname = pick(wrong_atc_names) full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]." - full_response = "[combined_name], this is [using_map.station_short] TraCon, wrong frequency. Switch to [rand(700,999)].[rand(1,9)]." - full_closure = "[using_map.station_short] TraCon, copy, apologies." + full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, wrong frequency. Switch to [rand(700,999)].[rand(1,9)]." + full_closure = "[GLOB.using_map.station_short] TraCon, copy, apologies." if("wrong_lang") //Can't implement this until autosay has language support if("emerg") var/problem = pick("hull breaches on multiple decks","unknown life forms on board","a drive about to go critical","asteroids impacting the hull","a total loss of engine power","people trying to board the ship") full_request = "Mayday, mayday, mayday, this is [combined_name] declaring an emergency! We have [problem]!" var/rand_freq = "[rand(700,999)].[rand(1,9)]" - full_response = "[combined_name], this is [using_map.station_short] TraCon, copy. Switch to emergency responder channel [rand_freq]." - full_closure = "Roger, [using_map.station_short] TraCon, contacting [rand_freq]." + full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, copy. Switch to emergency responder channel [rand_freq]." + full_closure = "Roger, [GLOB.using_map.station_short] TraCon, contacting [rand_freq]." else full_request = "[callname], this is [combined_name] on a [mission] [pick(mission_noun)] to [destname], [pick(request_verb)] [request]." - full_response = "[combined_name], this is [using_map.station_short] TraCon, [response]." //Station TraCon always calls themselves TraCon - full_closure = "[using_map.station_short] TraCon, [yes ? "thank you" : "copy"], good day." //They always copy what TraCon called themselves in the end when they realize they said it wrong + full_response = "[combined_name], this is [GLOB.using_map.station_short] TraCon, [response]." //Station TraCon always calls themselves TraCon + full_closure = "[GLOB.using_map.station_short] TraCon, [yes ? "thank you" : "copy"], good day." //They always copy what TraCon called themselves in the end when they realize they said it wrong //Ship sends request to ATC msg(full_request,"[prefix] [shipname]") @@ -126,4 +125,4 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg(full_response) sleep(5 SECONDS) //Ship sends response to ATC - msg(full_closure,"[prefix] [shipname]") \ No newline at end of file + msg(full_closure,"[prefix] [shipname]") diff --git a/code/modules/busy_space/loremaster.dm b/code/modules/busy_space/loremaster.dm index 28dca0055b0a..77b8a8ed53d8 100644 --- a/code/modules/busy_space/loremaster.dm +++ b/code/modules/busy_space/loremaster.dm @@ -1,6 +1,5 @@ //I AM THE LOREMASTER, ARE YOU THE GATEKEEPER? - -var/datum/lore/loremaster/loremaster = new/datum/lore/loremaster +GLOBAL_DATUM_INIT(loremaster, /datum/lore/loremaster, new) /datum/lore/loremaster var/list/organizations = list() diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm index f80e82938eca..c39e92ea49ea 100644 --- a/code/modules/busy_space/organizations.dm +++ b/code/modules/busy_space/organizations.dm @@ -123,7 +123,7 @@ ..() spawn(1) // BYOND shenanigans means using_map is not initialized yet. Wait a tick. // Get rid of the current map from the list, so ships flying in don't say they're coming to the current map. - var/string_to_test = "[using_map.station_name] in [using_map.starsys_name]" + var/string_to_test = "[GLOB.using_map.station_name] in [GLOB.using_map.starsys_name]" if(string_to_test in destination_names) destination_names.Remove(string_to_test) @@ -546,4 +546,4 @@ "Xarxis 5", "Haverick", "Darsing", - "Norfolk") \ No newline at end of file + "Norfolk") diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm index 57a6df32ab63..e1e68bd06171 100644 --- a/code/modules/client/asset_cache.dm +++ b/code/modules/client/asset_cache.dm @@ -147,16 +147,16 @@ You can set verify to TRUE if you want send() to sleep until the client has the //These datums are used to populate the asset cache, the proc "register()" does this. //all of our asset datums, used for referring to these later -/var/global/list/asset_datums = list() +GLOBAL_LIST_EMPTY(asset_datums) //get a assetdatum or make a new one /proc/get_asset_datum(var/type) - if(!(type in asset_datums)) + if(!(type in GLOB.asset_datums)) return new type() - return asset_datums[type] + return GLOB.asset_datums[type] /datum/asset/New() - asset_datums[type] = src + GLOB.asset_datums[type] = src /datum/asset/proc/register() return @@ -195,7 +195,8 @@ You can set verify to TRUE if you want send() to sleep until the client has the "large_stamp-rep.png" = 'icons/paper_icons/large_stamp-rep.png', "large_stamp-magistrate.png"= 'icons/paper_icons/large_stamp-magistrate.png', "talisman.png" = 'icons/paper_icons/talisman.png', - "ntlogo.png" = 'icons/paper_icons/ntlogo.png' + "ntlogo.png" = 'icons/paper_icons/ntlogo.png', + "syndielogo.png" ='icons/paper_icons/syndielogo.png' ) /datum/asset/simple/chess @@ -313,14 +314,14 @@ You can set verify to TRUE if you want send() to sleep until the client has the if(!(state in list("cap", "connector", "dtvalve", "dual-port vent", "dvalve", "filter", "he", "heunary", "injector", "junction", "manifold", "mixer", "tvalve", "mvalve", "passive vent", "passivegate", "pump", "scrubber", "simple", "universal", "uvent", "volumepump"))) //Basically all the pipes we want sprites for continue if(state in list("he", "simple")) - for(var/D in alldirs) + for(var/D in GLOB.alldirs) assets["[state]-[dir2text(D)].png"] = icon('icons/obj/pipe-item.dmi', state, D) - for(var/D in cardinal) + for(var/D in GLOB.cardinal) assets["[state]-[dir2text(D)].png"] = icon('icons/obj/pipe-item.dmi', state, D) for(var/state in icon_states('icons/obj/pipes/disposal.dmi')) if(!(state in list("pipe-c", "pipe-j1", "pipe-s", "pipe-t", "pipe-y", "intake", "outlet", "pipe-j1s"))) //Pipes we want sprites for continue - for(var/D in cardinal) + for(var/D in GLOB.cardinal) assets["[state]-[dir2text(D)].png"] = icon('icons/obj/pipes/disposal.dmi', state, D) for(var/asset_name in assets) register_asset(asset_name, assets[asset_name]) diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 0feca103c123..85615be1ee58 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -99,4 +99,4 @@ var/client_keysend_amount = 0 var/next_keysend_reset = 0 var/next_keysend_trip_reset = 0 - var/keysend_tripped = FALSE \ No newline at end of file + var/keysend_tripped = FALSE diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index e504a796052c..efe609a8a56e 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -319,16 +319,16 @@ if(!config.disable_localhost_admin) if(is_connecting_from_localhost()) new /datum/admins("!LOCALHOST!", R_HOST, ckey) // Makes localhost rank - holder = admin_datums[ckey] + holder = GLOB.admin_datums[ckey] if(holder) GLOB.admins += src holder.owner = src //preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum) - prefs = preferences_datums[ckey] + prefs = GLOB.preferences_datums[ckey] if(!prefs) prefs = new /datum/preferences(src) - preferences_datums[ckey] = prefs + GLOB.preferences_datums[ckey] = prefs else prefs.parent = src prefs.last_ip = address //these are gonna be used for banning @@ -339,8 +339,8 @@ spawn() // Goonchat does some non-instant checks in start() chatOutput.start() - if( (world.address == address || !address) && !host ) - host = key + if( (world.address == address || !address) && !GLOB.host ) + GLOB.host = key world.update_status() if(holder) @@ -376,7 +376,7 @@ if(establish_db_connection()) activate_darkmode() - if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates. -CP + if(prefs.lastchangelog != GLOB.changelog_hash) //bolds the changelog button on the interface so we know there are updates. -CP if(establish_db_connection()) to_chat(src, "Changelog has changed since your last visit.") update_changelog_button() @@ -393,10 +393,10 @@ check_forum_link() - if(custom_event_msg && custom_event_msg != "") + if(GLOB.custom_event_msg && GLOB.custom_event_msg != "") to_chat(src, "

    Custom Event

    ") to_chat(src, "

    A custom event is taking place. OOC Info:

    ") - to_chat(src, "[html_encode(custom_event_msg)]") + to_chat(src, "[html_encode(GLOB.custom_event_msg)]") to_chat(src, "
    ") if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them. @@ -452,7 +452,7 @@ return establish_db_connection() - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) return if(check_rights(R_ADMIN, 0, mob)) // Yes, the mob is required, regardless of other examples in this file, it won't work otherwise @@ -461,7 +461,7 @@ return //Donator stuff. - var/DBQuery/query_donor_select = dbcon.NewQuery("SELECT ckey, tier, active FROM `[format_table_name("donators")]` WHERE ckey = '[ckey]'") + var/DBQuery/query_donor_select = GLOB.dbcon.NewQuery("SELECT ckey, tier, active FROM `[format_table_name("donators")]` WHERE ckey = '[ckey]'") query_donor_select.Execute() while(query_donor_select.NextRow()) if(!text2num(query_donor_select.item[3])) @@ -482,11 +482,11 @@ establish_db_connection() - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) return - var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[ckey]'") query.Execute() var/sql_id = 0 player_age = 0 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if there is a record. @@ -496,7 +496,7 @@ break - var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]'") + var/DBQuery/query_ip = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]'") query_ip.Execute() related_accounts_ip = list() while(query_ip.NextRow()) @@ -504,7 +504,7 @@ related_accounts_ip.Add("[query_ip.item[1]]") - var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]'") + var/DBQuery/query_cid = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]'") query_cid.Execute() related_accounts_cid = list() while(query_cid.NextRow()) @@ -546,7 +546,7 @@ if(sql_id) //Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables - var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]") + var/DBQuery/query_update = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]") if(!query_update.Execute()) var/err = query_update.ErrorMsg() log_game("SQL ERROR during log_client_to_db (update). Error : \[[err]\]\n") @@ -561,14 +561,14 @@ del(src) return // Dont insert or they can just go in again - var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") + var/DBQuery/query_insert = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") if(!query_insert.Execute()) var/err = query_insert.ErrorMsg() log_game("SQL ERROR during log_client_to_db (insert). Error : \[[err]\]\n") message_admins("SQL ERROR during log_client_to_db (insert). Error : \[[err]\]\n") // Log player connections to DB - var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`datetime`,`ckey`,`ip`,`computerid`) VALUES(Now(),'[ckey]','[sql_ip]','[sql_computerid]');") + var/DBQuery/query_accesslog = GLOB.dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]`(`datetime`,`ckey`,`ip`,`computerid`) VALUES(Now(),'[ckey]','[sql_ip]','[sql_computerid]');") query_accesslog.Execute() /client/proc/check_ip_intel() @@ -616,14 +616,14 @@ to_chat(src, "You have no verified forum account. VERIFY FORUM ACCOUNT") /client/proc/create_oauth_token() - var/DBQuery/query_find_token = dbcon.NewQuery("SELECT token FROM [format_table_name("oauth_tokens")] WHERE ckey = '[ckey]' limit 1") + var/DBQuery/query_find_token = GLOB.dbcon.NewQuery("SELECT token FROM [format_table_name("oauth_tokens")] WHERE ckey = '[ckey]' limit 1") if(!query_find_token.Execute()) log_debug("create_oauth_token: failed db read") return if(query_find_token.NextRow()) return query_find_token.item[1] var/tokenstr = md5("[ckey][rand()]") - var/DBQuery/query_insert_token = dbcon.NewQuery("INSERT INTO [format_table_name("oauth_tokens")] (ckey, token) VALUES('[ckey]','[tokenstr]')") + var/DBQuery/query_insert_token = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("oauth_tokens")] (ckey, token) VALUES('[ckey]','[tokenstr]')") if(!query_insert_token.Execute()) return return tokenstr @@ -638,7 +638,7 @@ if(!fromban) to_chat(src, "Your forum account is already set.") return - var/DBQuery/query_find_link = dbcon.NewQuery("SELECT fuid FROM [format_table_name("player")] WHERE ckey = '[ckey]' limit 1") + var/DBQuery/query_find_link = GLOB.dbcon.NewQuery("SELECT fuid FROM [format_table_name("player")] WHERE ckey = '[ckey]' limit 1") if(!query_find_link.Execute()) log_debug("link_forum_account: failed db read") return @@ -682,7 +682,7 @@ var/oldcid = cidcheck[ckey] if(!oldcid) - var/DBQuery/query_cidcheck = dbcon.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[ckey]'") + var/DBQuery/query_cidcheck = GLOB.dbcon.NewQuery("SELECT computerid FROM [format_table_name("player")] WHERE ckey = '[ckey]'") query_cidcheck.Execute() var/lastcid = computer_id @@ -747,7 +747,7 @@ var/const/adminckey = "CID-Error" // Check for notes in the last day - only 1 note per 24 hours - var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id from [format_table_name("notes")] WHERE ckey = '[ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()") + var/DBQuery/query_get_notes = GLOB.dbcon.NewQuery("SELECT id from [format_table_name("notes")] WHERE ckey = '[ckey]' AND adminckey = '[adminckey]' AND timestamp + INTERVAL 1 DAY < NOW()") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining id from notes table. Error : \[[err]\]\n") @@ -756,7 +756,7 @@ return // Only add a note if their most recent note isn't from the randomizer blocker, either - query_get_notes = dbcon.NewQuery("SELECT adminckey FROM [format_table_name("notes")] WHERE ckey = '[ckey]' ORDER BY timestamp DESC LIMIT 1") + query_get_notes = GLOB.dbcon.NewQuery("SELECT adminckey FROM [format_table_name("notes")] WHERE ckey = '[ckey]' ORDER BY timestamp DESC LIMIT 1") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining adminckey from notes table. Error : \[[err]\]\n") @@ -890,7 +890,7 @@ // Better changelog button handling /client/proc/update_changelog_button() if(establish_db_connection()) - if(prefs.lastchangelog != changelog_hash) + if(prefs.lastchangelog != GLOB.changelog_hash) winset(src, "rpane.changelog", "background-color=#bb7700;text-color=#FFFFFF;font-style=bold") else if(prefs.toggles & UI_DARKMODE) diff --git a/code/modules/client/message.dm b/code/modules/client/message.dm index ba09e7e15672..5c612339621b 100644 --- a/code/modules/client/message.dm +++ b/code/modules/client/message.dm @@ -7,4 +7,4 @@ proc/addclientmessage(var/ckey, var/message) var/list/L = GLOB.clientmessages[ckey] if(!L) GLOB.clientmessages[ckey] = L = list() - L += message \ No newline at end of file + L += message diff --git a/code/modules/client/preference/loadout/gear_tweaks.dm b/code/modules/client/preference/loadout/gear_tweaks.dm index 6458a51b9a3b..9446008b7625 100644 --- a/code/modules/client/preference/loadout/gear_tweaks.dm +++ b/code/modules/client/preference/loadout/gear_tweaks.dm @@ -16,8 +16,7 @@ /* * Color adjustment */ - -var/datum/gear_tweak/color/gear_tweak_free_color_choice = new() +GLOBAL_DATUM_INIT(gear_tweak_free_color_choice, /datum/gear_tweak/color, new()) /datum/gear_tweak/color var/list/valid_colors diff --git a/code/modules/client/preference/loadout/loadout.dm b/code/modules/client/preference/loadout/loadout.dm index 63825cc12375..1091b38a11c8 100644 --- a/code/modules/client/preference/loadout/loadout.dm +++ b/code/modules/client/preference/loadout/loadout.dm @@ -1,5 +1,5 @@ -var/list/loadout_categories = list() -var/list/gear_datums = list() +GLOBAL_LIST_EMPTY(loadout_categories) +GLOBAL_LIST_EMPTY(gear_datums) /datum/loadout_category var/category = "" @@ -30,15 +30,15 @@ var/list/gear_datums = list() error("Loadout - Missing path definition: [G]") continue - if(!loadout_categories[use_category]) - loadout_categories[use_category] = new /datum/loadout_category(use_category) - var/datum/loadout_category/LC = loadout_categories[use_category] - gear_datums[use_name] = new geartype - LC.gear[use_name] = gear_datums[use_name] + if(!GLOB.loadout_categories[use_category]) + GLOB.loadout_categories[use_category] = new /datum/loadout_category(use_category) + var/datum/loadout_category/LC = GLOB.loadout_categories[use_category] + GLOB.gear_datums[use_name] = new geartype + LC.gear[use_name] = GLOB.gear_datums[use_name] - loadout_categories = sortAssoc(loadout_categories) - for(var/loadout_category in loadout_categories) - var/datum/loadout_category/LC = loadout_categories[loadout_category] + GLOB.loadout_categories = sortAssoc(GLOB.loadout_categories) + for(var/loadout_category in GLOB.loadout_categories) + var/datum/loadout_category/LC = GLOB.loadout_categories[loadout_category] LC.gear = sortAssoc(LC.gear) return 1 diff --git a/code/modules/client/preference/loadout/loadout_cosmetics.dm b/code/modules/client/preference/loadout/loadout_cosmetics.dm index b7ac6c39493f..0375de2de539 100644 --- a/code/modules/client/preference/loadout/loadout_cosmetics.dm +++ b/code/modules/client/preference/loadout/loadout_cosmetics.dm @@ -21,4 +21,4 @@ /datum/gear/lipstick/lime display_name = "lipstick, lime" - path = /obj/item/lipstick/lime \ No newline at end of file + path = /obj/item/lipstick/lime diff --git a/code/modules/client/preference/loadout/loadout_donor.dm b/code/modules/client/preference/loadout/loadout_donor.dm index 830b10b70551..7d43bd6b86a8 100644 --- a/code/modules/client/preference/loadout/loadout_donor.dm +++ b/code/modules/client/preference/loadout/loadout_donor.dm @@ -93,4 +93,4 @@ display_name = "Gold ID Decal" path = /obj/item/id_decal/gold donator_tier = 4 - cost = 4 \ No newline at end of file + cost = 4 diff --git a/code/modules/client/preference/loadout/loadout_glasses.dm b/code/modules/client/preference/loadout/loadout_glasses.dm index 94f2555bdba8..a84af914c499 100644 --- a/code/modules/client/preference/loadout/loadout_glasses.dm +++ b/code/modules/client/preference/loadout/loadout_glasses.dm @@ -21,4 +21,4 @@ /datum/gear/glasses/prescription display_name = "Prescription glasses" - path = /obj/item/clothing/glasses/regular \ No newline at end of file + path = /obj/item/clothing/glasses/regular diff --git a/code/modules/client/preference/loadout/loadout_gloves.dm b/code/modules/client/preference/loadout/loadout_gloves.dm index cd8d5a7c8e13..879cb8405061 100644 --- a/code/modules/client/preference/loadout/loadout_gloves.dm +++ b/code/modules/client/preference/loadout/loadout_gloves.dm @@ -5,4 +5,4 @@ /datum/gear/gloves/fingerless display_name = "Fingerless Gloves" - path = /obj/item/clothing/gloves/fingerless \ No newline at end of file + path = /obj/item/clothing/gloves/fingerless diff --git a/code/modules/client/preference/loadout/loadout_shoes.dm b/code/modules/client/preference/loadout/loadout_shoes.dm index 5031f4333fbd..ab000b209ac5 100644 --- a/code/modules/client/preference/loadout/loadout_shoes.dm +++ b/code/modules/client/preference/loadout/loadout_shoes.dm @@ -55,4 +55,4 @@ /datum/gear/shoes/whiteshoes display_name = "White shoes" - path = /obj/item/clothing/shoes/white \ No newline at end of file + path = /obj/item/clothing/shoes/white diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 78123e59d8a4..ff3a51688681 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -1,6 +1,7 @@ -var/list/preferences_datums = list() +GLOBAL_LIST_EMPTY(preferences_datums) +GLOBAL_PROTECT(preferences_datums) // These feel like something that shouldnt be fucked with -var/global/list/special_role_times = list( //minimum age (in days) for accounts to play these roles +GLOBAL_LIST_INIT(special_role_times, list( //minimum age (in days) for accounts to play these roles ROLE_PAI = 0, ROLE_POSIBRAIN = 0, ROLE_GUARDIAN = 0, @@ -24,7 +25,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts ROLE_GSPIDER = 21, ROLE_ABDUCTOR = 30, ROLE_DEVIL = 14 -) +)) /proc/player_old_enough_antag(client/C, role) if(available_in_days_antag(C, role)) @@ -42,7 +43,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return 0 if(!isnum(C.player_age)) return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - var/minimal_player_age_antag = special_role_times[num2text(role)] + var/minimal_player_age_antag = GLOB.special_role_times[num2text(role)] if(!isnum(minimal_player_age_antag)) return 0 @@ -55,8 +56,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return max(0, days - C.player_age) return 0 -#define MAX_SAVE_SLOTS 20 // Save slots for regular players -#define MAX_SAVE_SLOTS_MEMBER 20 // Save slots for BYOND members +#define MAX_SAVE_SLOTS 30 // Save slots for regular players +#define MAX_SAVE_SLOTS_MEMBER 30 // Save slots for BYOND members #define TAB_CHAR 0 #define TAB_GAME 1 @@ -343,11 +344,11 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "Eyes: " dat += "Color [color_square(e_colour)]
    " - if((S.bodyflags & HAS_SKIN_COLOR) || body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins + if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) //admins can always fuck with this, because they are admins dat += "Body Color: " dat += "Color [color_square(s_colour)]
    " - if(body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) + if(GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) dat += "Body Accessory: " dat += "[body_accessory ? "[body_accessory]" : "None"]
    " @@ -413,10 +414,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts switch(status) if("cyborg") var/datum/robolimb/R - if(rlimb_data[name] && all_robolimbs[rlimb_data[name]]) - R = all_robolimbs[rlimb_data[name]] + if(rlimb_data[name] && GLOB.all_robolimbs[rlimb_data[name]]) + R = GLOB.all_robolimbs[rlimb_data[name]] else - R = basic_robolimb + R = GLOB.basic_robolimb dat += "\t[R.company] [organ_name] prosthesis" if("amputated") dat += "\tAmputated [organ_name]" @@ -461,7 +462,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "Ghost Sight:[(toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
    " dat += "Ghost PDA:[(toggles & CHAT_GHOSTPDA) ? "All PDA Messages" : "No PDA Messages"]
    " if(check_rights(R_ADMIN,0)) - dat += "OOC Color:   Change
    " + dat += "OOC Color:   Change
    " if(config.allow_Metadata) dat += "OOC Notes:Edit
    " dat += "Parallax (Fancy Space):" @@ -509,7 +510,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/list/type_blacklist = list() if(gear && gear.len) for(var/i = 1, i <= gear.len, i++) - var/datum/gear/G = gear_datums[gear[i]] + var/datum/gear/G = GLOB.gear_datums[gear[i]] if(G) if(!G.subtype_cost_overlap) if(G.subtype_path in type_blacklist) @@ -525,7 +526,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += "" - var/datum/loadout_category/LC = loadout_categories[gear_tab] + var/datum/loadout_category/LC = GLOB.loadout_categories[gear_tab] dat += "" dat += "" dat += "" @@ -654,7 +655,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if((job_support_low & JOB_CIVILIAN) && (rank != "Civilian")) HTML += "[rank]" continue - if((rank in command_positions) || (rank == "AI"))//Bold head jobs + if((rank in GLOB.command_positions) || (rank == "AI"))//Bold head jobs HTML += "[rank]" else HTML += "[rank]" @@ -1160,7 +1161,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(href_list["preference"] == "gear") if(href_list["toggle_gear"]) - var/datum/gear/TG = gear_datums[href_list["toggle_gear"]] + var/datum/gear/TG = GLOB.gear_datums[href_list["toggle_gear"]] if(TG.display_name in gear) gear -= TG.display_name else @@ -1170,7 +1171,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/total_cost = 0 var/list/type_blacklist = list() for(var/gear_name in gear) - var/datum/gear/G = gear_datums[gear_name] + var/datum/gear/G = GLOB.gear_datums[gear_name] if(istype(G)) if(!G.subtype_cost_overlap) if(G.subtype_path in type_blacklist) @@ -1182,7 +1183,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts gear += TG.display_name else if(href_list["gear"] && href_list["tweak"]) - var/datum/gear/gear = gear_datums[href_list["gear"]] + var/datum/gear/gear = GLOB.gear_datums[href_list["gear"]] var/datum/gear_tweak/tweak = locate(href_list["tweak"]) if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks)) return @@ -1203,7 +1204,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/datum/robolimb/robohead if(S.bodyflags & ALL_RPARTS) var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" - robohead = all_robolimbs[head_model] + robohead = GLOB.all_robolimbs[head_model] switch(href_list["preference"]) if("name") real_name = random_name(gender,species) @@ -1320,7 +1321,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/datum/robolimb/robohead if(NS.bodyflags & ALL_RPARTS) var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" - robohead = all_robolimbs[head_model] + robohead = GLOB.all_robolimbs[head_model] //grab one of the valid hair styles for the newly chosen species h_style = random_hair_style(gender, species, robohead) @@ -1453,7 +1454,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts head_model = "Morpheus Cyberkinetics" else head_model = rlimb_data["head"] - var/datum/robolimb/robohead = all_robolimbs[head_model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model] if((species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. valid_hairstyles += hairstyle //Give them their hairstyles if they do. else @@ -1536,7 +1537,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts head_model = "Morpheus Cyberkinetics" else head_model = rlimb_data["head"] - var/datum/robolimb/robohead = all_robolimbs[head_model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model] if(robohead.is_monitor && M.name != "None") //If the character can have prosthetic heads and they have the default Morpheus head (or another monitor-head), no optic markings. continue else if(!robohead.is_monitor && M.name != "None") //Otherwise, if they DON'T have the default head and the head's not a monitor but the head's not in the style's list of allowed models, skip. @@ -1613,10 +1614,10 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if("body_accessory") var/list/possible_body_accessories = list() if(check_rights(R_ADMIN, 1, user)) - possible_body_accessories = body_accessory_by_name.Copy() + possible_body_accessories = GLOB.body_accessory_by_name.Copy() else - for(var/B in body_accessory_by_name) - var/datum/body_accessory/accessory = body_accessory_by_name[B] + for(var/B in GLOB.body_accessory_by_name) + var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B] if(!istype(accessory)) possible_body_accessories += "None" //the only null entry should be the "None" option continue @@ -1660,7 +1661,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts head_model = "Morpheus Cyberkinetics" else head_model = rlimb_data["head"] - var/datum/robolimb/robohead = all_robolimbs[head_model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_model] if((species in SA.species_allowed) && robohead.is_monitor && ((SA.models_allowed && (robohead.company in SA.models_allowed)) || !SA.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. else @@ -1750,7 +1751,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts s_tone = max(min(round(skin_c), S.icon_skin_tones.len), 1) if("skin") - if((S.bodyflags & HAS_SKIN_COLOR) || body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) + if((S.bodyflags & HAS_SKIN_COLOR) || GLOB.body_accessory_by_species[species] || check_rights(R_ADMIN, 0, user)) var/new_skin = input(user, "Choose your character's skin colour: ", "Character Preference", s_colour) as color|null if(new_skin) s_colour = new_skin @@ -1869,7 +1870,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(!choice) return R.company = choice - R = all_robolimbs[R.company] + R = GLOB.all_robolimbs[R.company] if(R.has_subtypes == 1) //If the company the user selected provides more than just one base model, lets handle it. var/list/robolimb_models = list() for(var/limb_type in typesof(R)) //Handling the different models of parts that manufacturers can provide. @@ -2208,7 +2209,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts character.m_styles = m_styles if(body_accessory) - character.body_accessory = body_accessory_by_name["[body_accessory]"] + character.body_accessory = GLOB.body_accessory_by_name["[body_accessory]"] character.backbag = backbag @@ -2221,53 +2222,53 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts character.change_eye_color(e_colour) if(disabilities & DISABILITY_FLAG_FAT && (CAN_BE_FAT in character.dna.species.species_traits)) - character.dna.SetSEState(FATBLOCK, TRUE, TRUE) + character.dna.SetSEState(GLOB.fatblock, TRUE, TRUE) character.overeatduration = 600 - character.dna.default_blocks.Add(FATBLOCK) + character.dna.default_blocks.Add(GLOB.fatblock) if(disabilities & DISABILITY_FLAG_NEARSIGHTED) - character.dna.SetSEState(GLASSESBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(GLASSESBLOCK) + character.dna.SetSEState(GLOB.glassesblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.glassesblock) if(disabilities & DISABILITY_FLAG_BLIND) - character.dna.SetSEState(BLINDBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(BLINDBLOCK) + character.dna.SetSEState(GLOB.blindblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.blindblock) if(disabilities & DISABILITY_FLAG_DEAF) - character.dna.SetSEState(DEAFBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(DEAFBLOCK) + character.dna.SetSEState(GLOB.deafblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.deafblock) if(disabilities & DISABILITY_FLAG_COLOURBLIND) - character.dna.SetSEState(COLOURBLINDBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(COLOURBLINDBLOCK) + character.dna.SetSEState(GLOB.colourblindblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.colourblindblock) if(disabilities & DISABILITY_FLAG_MUTE) - character.dna.SetSEState(MUTEBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(MUTEBLOCK) + character.dna.SetSEState(GLOB.muteblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.muteblock) if(disabilities & DISABILITY_FLAG_NERVOUS) - character.dna.SetSEState(NERVOUSBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(NERVOUSBLOCK) + character.dna.SetSEState(GLOB.nervousblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.nervousblock) if(disabilities & DISABILITY_FLAG_SWEDISH) - character.dna.SetSEState(SWEDEBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(SWEDEBLOCK) + character.dna.SetSEState(GLOB.swedeblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.swedeblock) if(disabilities & DISABILITY_FLAG_CHAV) - character.dna.SetSEState(CHAVBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(CHAVBLOCK) + character.dna.SetSEState(GLOB.chavblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.chavblock) if(disabilities & DISABILITY_FLAG_LISP) - character.dna.SetSEState(LISPBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(LISPBLOCK) + character.dna.SetSEState(GLOB.lispblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.lispblock) if(disabilities & DISABILITY_FLAG_DIZZY) - character.dna.SetSEState(DIZZYBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(DIZZYBLOCK) + character.dna.SetSEState(GLOB.dizzyblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.dizzyblock) if(disabilities & DISABILITY_FLAG_WINGDINGS && (CAN_WINGDINGS in character.dna.species.species_traits)) - character.dna.SetSEState(WINGDINGSBLOCK, TRUE, TRUE) - character.dna.default_blocks.Add(WINGDINGSBLOCK) + character.dna.SetSEState(GLOB.wingdingsblock, TRUE, TRUE) + character.dna.default_blocks.Add(GLOB.wingdingsblock) character.dna.species.handle_dna(character) @@ -2286,7 +2287,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts /datum/preferences/proc/open_load_dialog(mob/user) - var/DBQuery/query = dbcon.NewQuery("SELECT slot,real_name FROM [format_table_name("characters")] WHERE ckey='[user.ckey]' ORDER BY slot") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT slot,real_name FROM [format_table_name("characters")] WHERE ckey='[user.ckey]' ORDER BY slot") var/list/slotnames[max_save_slots] if(!query.Execute()) diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm index 6090538f2b39..ce2917373bf7 100644 --- a/code/modules/client/preference/preferences_mysql.dm +++ b/code/modules/client/preference/preferences_mysql.dm @@ -1,6 +1,6 @@ /datum/preferences/proc/load_preferences(client/C) - var/DBQuery/query = dbcon.NewQuery({"SELECT + var/DBQuery/query = GLOB.dbcon.NewQuery({"SELECT ooccolor, UI_style, UI_style_color, @@ -88,7 +88,7 @@ log_runtime(EXCEPTION("[C.key] had a malformed role entry: '[role]'. Removing!"), src) be_special -= role - var/DBQuery/query = dbcon.NewQuery({"UPDATE [format_table_name("player")] + var/DBQuery/query = GLOB.dbcon.NewQuery({"UPDATE [format_table_name("player")] SET ooccolor='[ooccolor]', UI_style='[UI_style]', @@ -126,11 +126,11 @@ slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot)) if(slot != default_slot) default_slot = slot - var/DBQuery/firstquery = dbcon.NewQuery("UPDATE [format_table_name("player")] SET default_slot=[slot] WHERE ckey='[C.ckey]'") + var/DBQuery/firstquery = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET default_slot=[slot] WHERE ckey='[C.ckey]'") firstquery.Execute() // Let's not have this explode if you sneeze on the DB - var/DBQuery/query = dbcon.NewQuery({"SELECT + var/DBQuery/query = GLOB.dbcon.NewQuery({"SELECT OOC_Notes, real_name, name_is_always_random, @@ -339,11 +339,11 @@ if(!isemptylist(gear)) gearlist = list2params(gear) - var/DBQuery/firstquery = dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") + var/DBQuery/firstquery = GLOB.dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") firstquery.Execute() while(firstquery.NextRow()) if(text2num(firstquery.item[1]) == default_slot) - var/DBQuery/query = dbcon.NewQuery({"UPDATE [format_table_name("characters")] SET OOC_Notes='[sanitizeSQL(metadata)]', + var/DBQuery/query = GLOB.dbcon.NewQuery({"UPDATE [format_table_name("characters")] SET OOC_Notes='[sanitizeSQL(metadata)]', real_name='[sanitizeSQL(real_name)]', name_is_always_random='[be_random_name]', gender='[gender]', @@ -406,7 +406,7 @@ return return 1 - var/DBQuery/query = dbcon.NewQuery({" + var/DBQuery/query = GLOB.dbcon.NewQuery({" INSERT INTO [format_table_name("characters")] (ckey, slot, OOC_Notes, real_name, name_is_always_random, gender, age, species, language, hair_colour, secondary_hair_colour, @@ -473,7 +473,7 @@ return 1 /datum/preferences/proc/load_random_character_slot(client/C) - var/DBQuery/query = dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT slot FROM [format_table_name("characters")] WHERE ckey='[C.ckey]' ORDER BY slot") var/list/saves = list() if(!query.Execute()) @@ -493,11 +493,11 @@ /datum/preferences/proc/SetChangelog(client/C,hash) lastchangelog=hash - if(preferences_datums[C.ckey].toggles & UI_DARKMODE) + if(GLOB.preferences_datums[C.ckey].toggles & UI_DARKMODE) winset(C, "rpane.changelog", "background-color=#40628a;font-color=#ffffff;font-style=none") else winset(C, "rpane.changelog", "background-color=none;font-style=none") - var/DBQuery/query = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastchangelog='[lastchangelog]' WHERE ckey='[C.ckey]'") + var/DBQuery/query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastchangelog='[lastchangelog]' WHERE ckey='[C.ckey]'") if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR during lastchangelog updating. Error : \[[err]\]\n") diff --git a/code/modules/client/preference/preferences_spawnpoints.dm b/code/modules/client/preference/preferences_spawnpoints.dm index 38b6b87ef435..5708567130ef 100644 --- a/code/modules/client/preference/preferences_spawnpoints.dm +++ b/code/modules/client/preference/preferences_spawnpoints.dm @@ -1,10 +1,10 @@ -var/list/spawntypes = list() +GLOBAL_LIST_EMPTY(spawntypes) /proc/populate_spawn_points() - spawntypes = list() + // GLOB.spawntypes = list() | This is already done, is it not for(var/type in subtypesof(/datum/spawnpoint)) var/datum/spawnpoint/S = new type() - spawntypes[S.display_name] = S + GLOB.spawntypes[S.display_name] = S /datum/spawnpoint var/msg //Message to display on the arrivals computer. @@ -28,7 +28,7 @@ var/list/spawntypes = list() /datum/spawnpoint/arrivals/New() ..() - turfs = latejoin + turfs = GLOB.latejoin /datum/spawnpoint/gateway display_name = "Gateway" @@ -36,7 +36,7 @@ var/list/spawntypes = list() /datum/spawnpoint/gateway/New() ..() - turfs = latejoin_gateway + turfs = GLOB.latejoin_gateway /datum/spawnpoint/cryo display_name = "Cryogenic Storage" @@ -45,7 +45,7 @@ var/list/spawntypes = list() /datum/spawnpoint/cryo/New() ..() - turfs = latejoin_cryo + turfs = GLOB.latejoin_cryo /datum/spawnpoint/cyborg display_name = "Cyborg Storage" @@ -54,4 +54,4 @@ var/list/spawntypes = list() /datum/spawnpoint/cyborg/New() ..() - turfs = latejoin_cyborg + turfs = GLOB.latejoin_cyborg diff --git a/code/modules/client/view.dm b/code/modules/client/view.dm index f4e74e203e81..ac862933afb7 100644 --- a/code/modules/client/view.dm +++ b/code/modules/client/view.dm @@ -87,4 +87,4 @@ return to_chat(src, "View range set to [viewNum_to_text(view_range)]") - AddViewMod("custom", view_range) \ No newline at end of file + AddViewMod("custom", view_range) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index f2e9e639dd63..da0873f162fa 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -542,4 +542,4 @@ /obj/item/stamp/chameleon/broken/Initialize() . = ..() - chameleon_action.emp_randomise(INFINITY) \ No newline at end of file + chameleon_action.emp_randomise(INFINITY) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index adb35b0fb3b8..d816e067502c 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -1,803 +1,803 @@ -/obj/item/clothing - name = "clothing" - max_integrity = 200 - integrity_failure = 80 - resistance_flags = FLAMMABLE - var/list/species_restricted = null //Only these species can wear this kit. - var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? - - /* - Sprites used when the clothing item is refit. This is done by setting icon_override. - For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. - Ideally, sprite_sheets_refit should be used for "hard" clothing items that can't change shape very well to fit the wearer (e.g. helmets, hardsuits), - while sprite_sheets should be used for "flexible" clothing items that do not need to be refitted (e.g. vox wearing jumpsuits). - */ - var/list/sprite_sheets_refit = null - lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' - var/alt_desc = null - var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS - var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect - var/up = 0 //but seperated to allow items to protect but not impair vision, like space helmets - - var/visor_flags = 0 //flags that are added/removed when an item is adjusted up/down - var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv - var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle() - - var/toggle_message = null - var/alt_toggle_message = null - var/active_sound = null - var/toggle_sound = null - var/toggle_cooldown = null - var/cooldown = 0 - var/species_disguise = null - var/magical = FALSE - -/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. - if(!can_use(user)) - return FALSE - - visor_toggling() - - to_chat(user, "You adjust \the [src] [up ? "up" : "down"].") - - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.head_update(src, forced = 1) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - return TRUE - -/obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags - up = !up - flags ^= visor_flags - flags_inv ^= visor_flags_inv - flags_cover ^= initial(flags_cover) - icon_state = "[initial(icon_state)][up ? "up" : ""]" - if(visor_vars_to_toggle & VISOR_FLASHPROTECT) - flash_protect ^= initial(flash_protect) - if(visor_vars_to_toggle & VISOR_TINT) - tint ^= initial(tint) - -/obj/item/clothing/proc/can_use(mob/user) - if(user && ismob(user)) - if(!user.incapacitated()) - return TRUE - return FALSE - -//BS12: Species-restricted clothing check. -/obj/item/clothing/mob_can_equip(M as mob, slot) - - //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam) - if(!..()) - return 0 - - // Skip species restriction checks on non-equipment slots - if(slot in list(slot_r_hand, slot_l_hand, slot_in_backpack, slot_l_store, slot_r_store)) - return 1 - - if(species_restricted && istype(M,/mob/living/carbon/human)) - - var/wearable = null - var/exclusive = null - var/mob/living/carbon/human/H = M - - if("exclude" in species_restricted) - exclusive = 1 - - if(H.dna.species) - if(exclusive) - if(!(H.dna.species.name in species_restricted)) - wearable = 1 - else - if(H.dna.species.name in species_restricted) - wearable = 1 - - if(!wearable) - to_chat(M, "Your species cannot wear [src].") - return 0 - - return 1 - -/obj/item/clothing/proc/refit_for_species(var/target_species) - //Set species_restricted list - switch(target_species) - if("Human", "Skrell") //humanoid bodytypes - species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Wryn","Drask") - else - species_restricted = list(target_species) - - //Set icon - if(sprite_sheets && (target_species in sprite_sheets)) - icon_override = sprite_sheets[target_species] - else - icon_override = initial(icon_override) - - if(sprite_sheets_obj && (target_species in sprite_sheets_obj)) - icon = sprite_sheets_obj[target_species] - else - icon = initial(icon) - -//Ears: currently only used for headsets and earmuffs -/obj/item/clothing/ears - name = "ears" - w_class = WEIGHT_CLASS_TINY - throwforce = 2 - slot_flags = SLOT_EARS - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/ears.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' - ) //We read you loud and skree-er. - -/obj/item/clothing/ears/attack_hand(mob/user) - if(!user) - return - - if(loc != user || !ishuman(user)) - ..() - return - - var/mob/living/carbon/human/H = user - if(H.l_ear != src && H.r_ear != src) - ..() - return - - if(!usr.canUnEquip(src)) - return - - var/obj/item/clothing/ears/O - if(slot_flags & SLOT_TWOEARS ) - O = (H.l_ear == src ? H.r_ear : H.l_ear) - user.unEquip(O) - if(!istype(src, /obj/item/clothing/ears/offear)) - qdel(O) - O = src - else - O = src - - user.unEquip(src) - - if(O) - user.put_in_hands(O) - O.add_fingerprint(user) - - if(istype(src, /obj/item/clothing/ears/offear)) - qdel(src) - - -/obj/item/clothing/ears/offear - name = "Other ear" - w_class = WEIGHT_CLASS_HUGE - icon = 'icons/mob/screen_gen.dmi' - icon_state = "block" - slot_flags = SLOT_EARS | SLOT_TWOEARS - -/obj/item/clothing/ears/offear/New(var/obj/O) - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - dir = O.dir - - -//Glasses -/obj/item/clothing/glasses - name = "glasses" - icon = 'icons/obj/clothing/glasses.dmi' - w_class = WEIGHT_CLASS_SMALL - flags_cover = GLASSESCOVERSEYES - slot_flags = SLOT_EYES - materials = list(MAT_GLASS = 250) - var/vision_flags = 0 - var/see_in_dark = 0 //Base human is 2 - var/invis_view = SEE_INVISIBLE_LIVING - var/invis_override = 0 - var/lighting_alpha - - var/emagged = 0 - var/list/color_view = null//overrides client.color while worn - var/prescription = 0 - var/prescription_upgradable = 0 - var/over_mask = FALSE //Whether or not the eyewear is rendered above the mask. Purely cosmetic. - strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets - put_on_delay = 25 - resistance_flags = NONE - species_restricted = list("exclude","Kidan") -/* -SEE_SELF // can see self, no matter what -SEE_MOBS // can see all mobs, no matter what -SEE_OBJS // can see all objs, no matter what -SEE_TURFS // can see all turfs (and areas), no matter what -SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are - // in a lit area (via pixel_x,y or smooth movement), can see those pixels -BLIND // can't see anything -*/ - -/obj/item/clothing/glasses/verb/adjust_eyewear() //Adjust eyewear to be worn above or below the mask. - set name = "Adjust Eyewear" - set category = "Object" - set desc = "Adjust your eyewear to be worn over or under a mask." - set src in usr - - var/mob/living/carbon/human/user = usr - if(!istype(user)) - return - if(user.incapacitated()) //Dead spessmen adjust no glasses. Resting/buckled ones do, though - return - - var/action_fluff = "You adjust \the [src]" - if(user.glasses == src) - if(!user.canUnEquip(src)) - to_chat(usr, "[src] is stuck to you!") - return - if(attack_hand(user)) //Remove the glasses for this action. Prevents logic-defying instances where glasses phase through your mask as it ascends/descends to another plane of existence. - action_fluff = "You remove \the [src] and adjust it" - - over_mask = !over_mask - to_chat(user, "[action_fluff] to be worn [over_mask ? "over" : "under"] a mask.") - -//Gloves -/obj/item/clothing/gloves - name = "gloves" - gender = PLURAL //Carn: for grammarically correct text-parsing - w_class = WEIGHT_CLASS_SMALL - icon = 'icons/obj/clothing/gloves.dmi' - siemens_coefficient = 0.50 - body_parts_covered = HANDS - slot_flags = SLOT_GLOVES - attack_verb = list("challenged") - var/transfer_prints = FALSE - var/pickpocket = 0 //Master pickpocket? - var/clipped = 0 - strip_delay = 20 - put_on_delay = 40 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/gloves.dmi', - "Drask" = 'icons/mob/species/drask/gloves.dmi' - ) - -// Called just before an attack_hand(), in mob/UnarmedAttack() -/obj/item/clothing/gloves/proc/Touch(atom/A, proximity) - return 0 // return 1 to cancel attack_hand() - -/obj/item/clothing/gloves/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/wirecutters)) - if(!clipped) - playsound(src.loc, W.usesound, 100, 1) - user.visible_message("[user] snips the fingertips off [src].","You snip the fingertips off [src].") - clipped = 1 - name = "mangled [name]" - desc = "[desc] They have had the fingertips cut off of them." - update_icon() - else - to_chat(user, "[src] have already been clipped!") - return - else - return ..() - -/obj/item/clothing/under/proc/set_sensors(mob/user as mob) - var/mob/M = user - if(istype(M, /mob/dead/)) return - if(user.stat || user.restrained()) return - if(has_sensor >= 2) - to_chat(user, "The controls are locked.") - return 0 - if(has_sensor <= 0) - to_chat(user, "This suit does not have any sensors.") - return 0 - - var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") - var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes - if(get_dist(user, src) > 1) - to_chat(user, "You have moved too far away.") - return - sensor_mode = modes.Find(switchMode) - 1 - - if(src.loc == user) - switch(sensor_mode) - if(0) - to_chat(user, "You disable your suit's remote sensing equipment.") - if(1) - to_chat(user, "Your suit will now report whether you are live or dead.") - if(2) - to_chat(user, "Your suit will now report your vital lifesigns.") - if(3) - to_chat(user, "Your suit will now report your vital lifesigns as well as your coordinate position.") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.w_uniform == src) - H.update_suit_sensors() - - else if(istype(src.loc, /mob)) - switch(sensor_mode) - if(0) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] disables [src.loc]'s remote sensing equipment.", 1) - if(1) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] turns [src.loc]'s remote sensors to binary.", 1) - if(2) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] sets [src.loc]'s sensors to track vitals.", 1) - if(3) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] sets [src.loc]'s sensors to maximum.", 1) - if(istype(src,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(H.w_uniform == src) - H.update_suit_sensors() - -/obj/item/clothing/under/verb/toggle() - set name = "Toggle Suit Sensors" - set category = "Object" - set src in usr - set_sensors(usr) - ..() - -//Head -/obj/item/clothing/head - name = "head" - icon = 'icons/obj/clothing/hats.dmi' - body_parts_covered = HEAD - slot_flags = SLOT_HEAD - var/blockTracking // Do we block AI tracking? - var/HUDType = null - - var/vision_flags = 0 - var/see_in_dark = 0 - var/lighting_alpha - - var/can_toggle = null - -//Mask -/obj/item/clothing/mask - name = "mask" - icon = 'icons/obj/clothing/masks.dmi' - body_parts_covered = HEAD - slot_flags = SLOT_MASK - var/mask_adjusted = 0 - var/adjusted_flags = null - strip_delay = 40 - put_on_delay = 40 - -//Proc that moves gas/breath masks out of the way -/obj/item/clothing/mask/proc/adjustmask(var/mob/user) - var/mob/living/carbon/human/H = usr //Used to check if the mask is on the head, to check if the hands are full, and to turn off internals if they were on when the mask was pushed out of the way. - if(user.incapacitated()) //This check allows you to adjust your masks while you're buckled into chairs or beds. - return - if(mask_adjusted) - icon_state = initial(icon_state) - gas_transfer_coefficient = initial(gas_transfer_coefficient) - permeability_coefficient = initial(permeability_coefficient) - to_chat(user, "You push \the [src] back into place.") - mask_adjusted = 0 - slot_flags = initial(slot_flags) - if(flags_inv != initial(flags_inv)) - if(initial(flags_inv) & HIDEFACE) //If the mask is one that hides the face and can be adjusted yet lost that trait when it was adjusted, make it hide the face again. - flags_inv |= HIDEFACE - if(flags != initial(flags)) - if(initial(flags) & AIRTIGHT) //If the mask is airtight and thus, one that you'd be able to run internals from yet can't because it was adjusted, make it airtight again. - flags |= AIRTIGHT - if(flags_cover != initial(flags_cover)) - if(initial(flags_cover) & MASKCOVERSMOUTH) //If the mask covers the mouth when it's down and can be adjusted yet lost that trait when it was adjusted, make it cover the mouth again. - flags_cover |= MASKCOVERSMOUTH - if(H.head == src && flags_inv == HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. - if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. - user.unEquip(src) - else //Otherwise, put it in an available hand, the active one preferentially. - src.loc = user - H.head = null - user.put_in_hands(src) - else - icon_state += "_up" - to_chat(user, "You push \the [src] out of the way.") - gas_transfer_coefficient = null - permeability_coefficient = null - mask_adjusted = 1 - if(adjusted_flags) - slot_flags = adjusted_flags - if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals. - Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/ - H.internal = null - H.update_action_buttons_icon() - if(flags_inv & HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. - flags_inv &= ~HIDEFACE /*Done after the above to avoid having to do a check for initial(src.flags_inv == HIDEFACE). - This reveals the user's face since the bandana will now be going on their head.*/ - if(flags_cover & MASKCOVERSMOUTH) //Mask won't cover the mouth any more since it's been pushed out of the way. Allows for CPRing with adjusted masks. - flags_cover &= ~MASKCOVERSMOUTH - if(flags & AIRTIGHT) //If the mask was airtight, it won't be anymore since you just pushed it off your face. - flags &= ~AIRTIGHT - if(user.wear_mask == src && initial(flags_inv) == HIDEFACE) //Means that you won't have to take off and put back on simple things like breath masks which, realistically, can just be pulled down off your face. - if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. - user.unEquip(src) - else //Otherwise, put it in an available hand, the active one preferentially. - src.loc = user - user.wear_mask = null - user.put_in_hands(src) - H.wear_mask_update(src, toggle_off = mask_adjusted) - usr.update_inv_wear_mask() - usr.update_inv_head() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -//Shoes -/obj/item/clothing/shoes - name = "shoes" - icon = 'icons/obj/clothing/shoes.dmi' - desc = "Comfortable-looking shoes." - gender = PLURAL //Carn: for grammatically correct text-parsing - var/chained = 0 - var/can_cut_open = 0 - var/cut_open = 0 - body_parts_covered = FEET - slot_flags = SLOT_FEET - - var/silence_steps = 0 - var/shoe_sound_footstep = 1 - var/shoe_sound = null - var/blood_state = BLOOD_STATE_NOT_BLOODY - var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - - permeability_coefficient = 0.50 - slowdown = SHOES_SLOWDOWN - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/shoes.dmi', - "Drask" = 'icons/mob/species/drask/shoes.dmi' - ) - -/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/match) && src.loc == user) - var/obj/item/match/M = I - if(M.matchignite()) // Match isn't lit, but isn't burnt. - user.visible_message("[user] strikes a [M] on the bottom of [src], lighting it.","You strike the [M] on the bottom of [src] to light it.") - playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) - else - user.visible_message("[user] crushes the [M] into the bottom of [src], extinguishing it.","You crush the [M] into the bottom of [src], extinguishing it.") - M.dropped() - return - - if(istype(I, /obj/item/wirecutters)) - if(can_cut_open) - if(!cut_open) - playsound(src.loc, I.usesound, 100, 1) - user.visible_message("[user] cuts open the toes of [src].","You cut open the toes of [src].") - cut_open = 1 - icon_state = "[icon_state]_opentoe" - item_state = "[item_state]_opentoe" - name = "mangled [name]" - desc = "[desc] They have had their toes opened up." - update_icon() - else - to_chat(user, "[src] have already had [p_their()] toes cut open!") - return - else - return ..() - -/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek - SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION) - if(shoe_sound) - var/turf/T = get_turf(H) - - if(!istype(H) || !istype(T)) - return 0 - - if(H.m_intent == MOVE_INTENT_RUN) - if(shoe_sound_footstep >= 2) - if(T.shoe_running_volume) - playsound(src, shoe_sound, T.shoe_running_volume, 1) - shoe_sound_footstep = 0 - else - shoe_sound_footstep++ - else if(T.shoe_walking_volume) - playsound(src, shoe_sound, T.shoe_walking_volume, 1) - - return 1 - -/obj/item/proc/negates_gravity() - return 0 - -//Suit -/obj/item/clothing/suit - icon = 'icons/obj/clothing/suits.dmi' - name = "suit" - var/fire_resist = T0C+100 - allowed = list(/obj/item/tank/emergency_oxygen) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - slot_flags = SLOT_OCLOTHING - var/blood_overlay_type = "suit" - var/suittoggled = FALSE - var/suit_adjusted = 0 - var/ignore_suitadjust = 1 - var/adjust_flavour = null - var/list/hide_tail_by_species = null - -//Proc that opens and closes jackets. -/obj/item/clothing/suit/proc/adjustsuit(var/mob/user) - if(!ignore_suitadjust) - if(!user.incapacitated()) - if(!(HULK in user.mutations)) - if(suit_adjusted) - var/flavour = "close" - icon_state = copytext(icon_state, 1, findtext(icon_state, "_open")) /*Trims the '_open' off the end of the icon state, thus avoiding a case where jackets that start open will - end up with a suffix of _open_open if adjusted twice, since their initial state is _open. */ - item_state = copytext(item_state, 1, findtext(item_state, "_open")) - if(adjust_flavour) - flavour = "[copytext(adjust_flavour, 3, length(adjust_flavour) + 1)] up" //Trims off the 'un' at the beginning of the word. unzip -> zip, unbutton->button. - to_chat(user, "You [flavour] \the [src].") - suit_adjusted = 0 //Suit is no longer adjusted. - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - else - var/flavour = "open" - icon_state += "_open" - item_state += "_open" - if(adjust_flavour) - flavour = "[adjust_flavour]" - to_chat(user, "You [flavour] \the [src].") - suit_adjusted = 1 //Suit's adjusted. - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - else - if(user.canUnEquip(src)) //Checks to see if the item can be unequipped. If so, lets shred. Otherwise, struggle and fail. - if(contents) //If the suit's got any storage capability... - for(var/obj/item/O in contents) //AVOIDING ITEM LOSS. Check through everything that's stored in the jacket and see if one of the items is a pocket. - if(istype(O, /obj/item/storage/internal)) //If it's a pocket... - if(O.contents) //Check to see if the pocket's got anything in it. - for(var/obj/item/I in O.contents) //Dump the pocket out onto the floor below the user. - user.unEquip(I,1) - - user.visible_message("[user] bellows, [pick("shredding", "ripping open", "tearing off")] [user.p_their()] jacket in a fit of rage!","You accidentally [pick("shred", "rend", "tear apart")] [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")]!") - user.unEquip(src) - qdel(src) //Now that the pockets have been emptied, we can safely destroy the jacket. - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) - else - to_chat(user, "You yank and pull at \the [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")], however you are unable to change its state!")//Yep, that's all they get. Avoids having to snowflake in a cooldown. - - return - user.update_inv_wear_suit() - else - to_chat(user, "You attempt to button up the velcro on \the [src], before promptly realising how retarded you are.") - -/obj/item/clothing/suit/equipped(var/mob/living/carbon/human/user, var/slot) //Handle tail-hiding on a by-species basis. - ..() - if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit) - if(user.dna.species.name in hide_tail_by_species) - if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden. - flags_inv |= HIDETAIL - else - if(!(initial(flags_inv) & HIDETAIL) && (flags_inv & HIDETAIL)) //Otherwise, remove the HIDETAIL flag if it wasn't already in the flags_inv to start with. - flags_inv &= ~HIDETAIL - -/obj/item/clothing/suit/ui_action_click(mob/user) //This is what happens when you click the HUD action button to adjust your suit. - if(!ignore_suitadjust) - adjustsuit(user) - else - ..() //This is required in order to ensure that the UI buttons for items that have alternate functions tied to UI buttons still work. - -/obj/item/clothing/suit/proc/special_overlays() // Does it have special overlays when worn? - return FALSE - -//Spacesuit -//Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. -// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! -/obj/item/clothing/head/helmet/space - name = "Space helmet" - icon_state = "space" - desc = "A special helmet designed for work in a hazardous, low-pressure environment." - flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "s_helmet" - permeability_coefficient = 0.01 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - species_restricted = list("exclude","Wryn") - flash_protect = 2 - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - dog_fashion = null - - -/obj/item/clothing/suit/space - name = "Space suit" - desc = "A suit that protects against low pressure environments. Has a big 13 on the back." - icon_state = "space" - item_state = "s_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank) - slowdown = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - strip_delay = 80 - put_on_delay = 80 - resistance_flags = NONE - hide_tail_by_species = null - species_restricted = list("exclude","Wryn") - - -//Under clothing -/obj/item/clothing/under - icon = 'icons/obj/clothing/uniforms.dmi' - name = "under" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - permeability_coefficient = 0.90 - slot_flags = SLOT_ICLOTHING - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi', - "Drask" = 'icons/mob/species/drask/uniform.dmi', - "Grey" = 'icons/mob/species/grey/uniform.dmi' - ) - - var/has_sensor = TRUE//For the crew computer 2 = unable to change mode - var/sensor_mode = SENSOR_OFF - var/random_sensor = TRUE - /* - 1 = Report living/dead - 2 = Report detailed damages - 3 = Report location - */ - var/list/accessories = list() - var/displays_id = 1 - var/rolled_down = 0 - var/basecolor - -/obj/item/clothing/under/rank/New() - if(random_sensor) - sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS) - ..() - -/obj/item/clothing/under/Destroy() - QDEL_LIST(accessories) - return ..() - -/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) - if(istype(A)) - . = TRUE - else - return FALSE - - if(accessories.len) - for(var/obj/item/clothing/accessory/AC in accessories) - if((A.slot in list(ACCESSORY_SLOT_UTILITY, ACCESSORY_SLOT_ARMBAND)) && AC.slot == A.slot) - return FALSE - if(!A.allow_duplicates && AC.type == A.type) - return FALSE - -/obj/item/clothing/under/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/clothing/accessory)) - attach_accessory(I, user, TRUE) - - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.attackby(I, user, params) - return TRUE - - . = ..() - -/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/A, mob/user, unequip = FALSE) - if(can_attach_accessory(A)) - if(unequip && !user.unEquip(A)) // Make absolutely sure this accessory is removed from hands - return FALSE - - accessories += A - A.on_attached(src, user) - - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - H.update_inv_w_uniform() - - return TRUE - else - to_chat(user, "You cannot attach more accessories of this type to [src].") - - return FALSE - -/obj/item/clothing/under/examine(mob/user) - . = ..() - switch(sensor_mode) - if(0) - . += "Its sensors appear to be disabled." - if(1) - . += "Its binary life sensors appear to be enabled." - if(2) - . += "Its vital tracker appears to be enabled." - if(3) - . += "Its vital tracker and tracking beacon appear to be enabled." - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - . += "\A [A] is attached to it." - - -/obj/item/clothing/under/verb/rollsuit() - set name = "Roll Down Jumpsuit" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - - if(!usr.incapacitated()) - if(copytext(item_color,-2) != "_d") - basecolor = item_color - if(basecolor + "_d_s" in icon_states('icons/mob/uniform.dmi')) - item_color = item_color == "[basecolor]" ? "[basecolor]_d" : "[basecolor]" - usr.update_inv_w_uniform() - else - to_chat(usr, "You cannot roll down this uniform!") - else - to_chat(usr, "You cannot roll down the uniform!") - -/obj/item/clothing/under/verb/removetie() - set name = "Remove Accessory" - set category = "Object" - set src in usr - handle_accessories_removal() - -/obj/item/clothing/under/proc/handle_accessories_removal() - if(!isliving(usr)) - return - if(usr.incapacitated()) - return - if(!Adjacent(usr)) - return - if(!accessories.len) - return - var/obj/item/clothing/accessory/A - if(accessories.len > 1) - A = input("Select an accessory to remove from [src]") as null|anything in accessories - else - A = accessories[1] - remove_accessory(usr,A) - -/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) - if(!(A in accessories)) - return - if(!isliving(user)) - return - if(user.incapacitated()) - return - if(!Adjacent(user)) - return - A.on_removed(user) - accessories -= A - to_chat(user, "You remove [A] from [src].") - usr.update_inv_w_uniform() - -/obj/item/clothing/under/emp_act(severity) - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.emp_act(severity) - ..() - -/obj/item/clothing/under/AltClick() - handle_accessories_removal() - -/obj/item/clothing/obj_destruction(damage_flag) - if(damage_flag == "bomb" || damage_flag == "melee") - var/turf/T = get_turf(src) - spawn(1) //so the shred survives potential turf change from the explosion. - var/obj/effect/decal/cleanable/shreds/Shreds = new(T) - Shreds.desc = "The sad remains of what used to be [name]." - deconstruct(FALSE) - else - ..() \ No newline at end of file +/obj/item/clothing + name = "clothing" + max_integrity = 200 + integrity_failure = 80 + resistance_flags = FLAMMABLE + var/list/species_restricted = null //Only these species can wear this kit. + var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? + + /* + Sprites used when the clothing item is refit. This is done by setting icon_override. + For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. + Ideally, sprite_sheets_refit should be used for "hard" clothing items that can't change shape very well to fit the wearer (e.g. helmets, hardsuits), + while sprite_sheets should be used for "flexible" clothing items that do not need to be refitted (e.g. vox wearing jumpsuits). + */ + var/list/sprite_sheets_refit = null + lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' + var/alt_desc = null + var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS + var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect + var/up = 0 //but seperated to allow items to protect but not impair vision, like space helmets + + var/visor_flags = 0 //flags that are added/removed when an item is adjusted up/down + var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv + var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle() + + var/toggle_message = null + var/alt_toggle_message = null + var/active_sound = null + var/toggle_sound = null + var/toggle_cooldown = null + var/cooldown = 0 + var/species_disguise = null + var/magical = FALSE + +/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. + if(!can_use(user)) + return FALSE + + visor_toggling() + + to_chat(user, "You adjust \the [src] [up ? "up" : "down"].") + + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.head_update(src, forced = 1) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + return TRUE + +/obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags + up = !up + flags ^= visor_flags + flags_inv ^= visor_flags_inv + flags_cover ^= initial(flags_cover) + icon_state = "[initial(icon_state)][up ? "up" : ""]" + if(visor_vars_to_toggle & VISOR_FLASHPROTECT) + flash_protect ^= initial(flash_protect) + if(visor_vars_to_toggle & VISOR_TINT) + tint ^= initial(tint) + +/obj/item/clothing/proc/can_use(mob/user) + if(user && ismob(user)) + if(!user.incapacitated()) + return TRUE + return FALSE + +//BS12: Species-restricted clothing check. +/obj/item/clothing/mob_can_equip(M as mob, slot) + + //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam) + if(!..()) + return 0 + + // Skip species restriction checks on non-equipment slots + if(slot in list(slot_r_hand, slot_l_hand, slot_in_backpack, slot_l_store, slot_r_store)) + return 1 + + if(species_restricted && istype(M,/mob/living/carbon/human)) + + var/wearable = null + var/exclusive = null + var/mob/living/carbon/human/H = M + + if("exclude" in species_restricted) + exclusive = 1 + + if(H.dna.species) + if(exclusive) + if(!(H.dna.species.name in species_restricted)) + wearable = 1 + else + if(H.dna.species.name in species_restricted) + wearable = 1 + + if(!wearable) + to_chat(M, "Your species cannot wear [src].") + return 0 + + return 1 + +/obj/item/clothing/proc/refit_for_species(var/target_species) + //Set species_restricted list + switch(target_species) + if("Human", "Skrell") //humanoid bodytypes + species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Wryn","Drask") + else + species_restricted = list(target_species) + + //Set icon + if(sprite_sheets && (target_species in sprite_sheets)) + icon_override = sprite_sheets[target_species] + else + icon_override = initial(icon_override) + + if(sprite_sheets_obj && (target_species in sprite_sheets_obj)) + icon = sprite_sheets_obj[target_species] + else + icon = initial(icon) + +//Ears: currently only used for headsets and earmuffs +/obj/item/clothing/ears + name = "ears" + w_class = WEIGHT_CLASS_TINY + throwforce = 2 + slot_flags = SLOT_EARS + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/ears.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' + ) //We read you loud and skree-er. + +/obj/item/clothing/ears/attack_hand(mob/user) + if(!user) + return + + if(loc != user || !ishuman(user)) + ..() + return + + var/mob/living/carbon/human/H = user + if(H.l_ear != src && H.r_ear != src) + ..() + return + + if(!usr.canUnEquip(src)) + return + + var/obj/item/clothing/ears/O + if(slot_flags & SLOT_TWOEARS ) + O = (H.l_ear == src ? H.r_ear : H.l_ear) + user.unEquip(O) + if(!istype(src, /obj/item/clothing/ears/offear)) + qdel(O) + O = src + else + O = src + + user.unEquip(src) + + if(O) + user.put_in_hands(O) + O.add_fingerprint(user) + + if(istype(src, /obj/item/clothing/ears/offear)) + qdel(src) + + +/obj/item/clothing/ears/offear + name = "Other ear" + w_class = WEIGHT_CLASS_HUGE + icon = 'icons/mob/screen_gen.dmi' + icon_state = "block" + slot_flags = SLOT_EARS | SLOT_TWOEARS + +/obj/item/clothing/ears/offear/New(var/obj/O) + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + dir = O.dir + + +//Glasses +/obj/item/clothing/glasses + name = "glasses" + icon = 'icons/obj/clothing/glasses.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_cover = GLASSESCOVERSEYES + slot_flags = SLOT_EYES + materials = list(MAT_GLASS = 250) + var/vision_flags = 0 + var/see_in_dark = 0 //Base human is 2 + var/invis_view = SEE_INVISIBLE_LIVING + var/invis_override = 0 + var/lighting_alpha + + var/emagged = 0 + var/list/color_view = null//overrides client.color while worn + var/prescription = 0 + var/prescription_upgradable = 0 + var/over_mask = FALSE //Whether or not the eyewear is rendered above the mask. Purely cosmetic. + strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets + put_on_delay = 25 + resistance_flags = NONE + species_restricted = list("exclude","Kidan") +/* +SEE_SELF // can see self, no matter what +SEE_MOBS // can see all mobs, no matter what +SEE_OBJS // can see all objs, no matter what +SEE_TURFS // can see all turfs (and areas), no matter what +SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are + // in a lit area (via pixel_x,y or smooth movement), can see those pixels +BLIND // can't see anything +*/ + +/obj/item/clothing/glasses/verb/adjust_eyewear() //Adjust eyewear to be worn above or below the mask. + set name = "Adjust Eyewear" + set category = "Object" + set desc = "Adjust your eyewear to be worn over or under a mask." + set src in usr + + var/mob/living/carbon/human/user = usr + if(!istype(user)) + return + if(user.incapacitated()) //Dead spessmen adjust no glasses. Resting/buckled ones do, though + return + + var/action_fluff = "You adjust \the [src]" + if(user.glasses == src) + if(!user.canUnEquip(src)) + to_chat(usr, "[src] is stuck to you!") + return + if(attack_hand(user)) //Remove the glasses for this action. Prevents logic-defying instances where glasses phase through your mask as it ascends/descends to another plane of existence. + action_fluff = "You remove \the [src] and adjust it" + + over_mask = !over_mask + to_chat(user, "[action_fluff] to be worn [over_mask ? "over" : "under"] a mask.") + +//Gloves +/obj/item/clothing/gloves + name = "gloves" + gender = PLURAL //Carn: for grammarically correct text-parsing + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/clothing/gloves.dmi' + siemens_coefficient = 0.50 + body_parts_covered = HANDS + slot_flags = SLOT_GLOVES + attack_verb = list("challenged") + var/transfer_prints = FALSE + var/pickpocket = 0 //Master pickpocket? + var/clipped = 0 + strip_delay = 20 + put_on_delay = 40 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/gloves.dmi', + "Drask" = 'icons/mob/species/drask/gloves.dmi' + ) + +// Called just before an attack_hand(), in mob/UnarmedAttack() +/obj/item/clothing/gloves/proc/Touch(atom/A, proximity) + return 0 // return 1 to cancel attack_hand() + +/obj/item/clothing/gloves/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/wirecutters)) + if(!clipped) + playsound(src.loc, W.usesound, 100, 1) + user.visible_message("[user] snips the fingertips off [src].","You snip the fingertips off [src].") + clipped = 1 + name = "mangled [name]" + desc = "[desc] They have had the fingertips cut off of them." + update_icon() + else + to_chat(user, "[src] have already been clipped!") + return + else + return ..() + +/obj/item/clothing/under/proc/set_sensors(mob/user as mob) + var/mob/M = user + if(istype(M, /mob/dead/)) return + if(user.stat || user.restrained()) return + if(has_sensor >= 2) + to_chat(user, "The controls are locked.") + return 0 + if(has_sensor <= 0) + to_chat(user, "This suit does not have any sensors.") + return 0 + + var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") + var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away.") + return + sensor_mode = modes.Find(switchMode) - 1 + + if(src.loc == user) + switch(sensor_mode) + if(0) + to_chat(user, "You disable your suit's remote sensing equipment.") + if(1) + to_chat(user, "Your suit will now report whether you are live or dead.") + if(2) + to_chat(user, "Your suit will now report your vital lifesigns.") + if(3) + to_chat(user, "Your suit will now report your vital lifesigns as well as your coordinate position.") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.w_uniform == src) + H.update_suit_sensors() + + else if(istype(src.loc, /mob)) + switch(sensor_mode) + if(0) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] disables [src.loc]'s remote sensing equipment.", 1) + if(1) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] turns [src.loc]'s remote sensors to binary.", 1) + if(2) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] sets [src.loc]'s sensors to track vitals.", 1) + if(3) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] sets [src.loc]'s sensors to maximum.", 1) + if(istype(src,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if(H.w_uniform == src) + H.update_suit_sensors() + +/obj/item/clothing/under/verb/toggle() + set name = "Toggle Suit Sensors" + set category = "Object" + set src in usr + set_sensors(usr) + ..() + +//Head +/obj/item/clothing/head + name = "head" + icon = 'icons/obj/clothing/hats.dmi' + body_parts_covered = HEAD + slot_flags = SLOT_HEAD + var/blockTracking // Do we block AI tracking? + var/HUDType = null + + var/vision_flags = 0 + var/see_in_dark = 0 + var/lighting_alpha + + var/can_toggle = null + +//Mask +/obj/item/clothing/mask + name = "mask" + icon = 'icons/obj/clothing/masks.dmi' + body_parts_covered = HEAD + slot_flags = SLOT_MASK + var/mask_adjusted = 0 + var/adjusted_flags = null + strip_delay = 40 + put_on_delay = 40 + +//Proc that moves gas/breath masks out of the way +/obj/item/clothing/mask/proc/adjustmask(var/mob/user) + var/mob/living/carbon/human/H = usr //Used to check if the mask is on the head, to check if the hands are full, and to turn off internals if they were on when the mask was pushed out of the way. + if(user.incapacitated()) //This check allows you to adjust your masks while you're buckled into chairs or beds. + return + if(mask_adjusted) + icon_state = initial(icon_state) + gas_transfer_coefficient = initial(gas_transfer_coefficient) + permeability_coefficient = initial(permeability_coefficient) + to_chat(user, "You push \the [src] back into place.") + mask_adjusted = 0 + slot_flags = initial(slot_flags) + if(flags_inv != initial(flags_inv)) + if(initial(flags_inv) & HIDEFACE) //If the mask is one that hides the face and can be adjusted yet lost that trait when it was adjusted, make it hide the face again. + flags_inv |= HIDEFACE + if(flags != initial(flags)) + if(initial(flags) & AIRTIGHT) //If the mask is airtight and thus, one that you'd be able to run internals from yet can't because it was adjusted, make it airtight again. + flags |= AIRTIGHT + if(flags_cover != initial(flags_cover)) + if(initial(flags_cover) & MASKCOVERSMOUTH) //If the mask covers the mouth when it's down and can be adjusted yet lost that trait when it was adjusted, make it cover the mouth again. + flags_cover |= MASKCOVERSMOUTH + if(H.head == src && flags_inv == HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. + if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. + user.unEquip(src) + else //Otherwise, put it in an available hand, the active one preferentially. + src.loc = user + H.head = null + user.put_in_hands(src) + else + icon_state += "_up" + to_chat(user, "You push \the [src] out of the way.") + gas_transfer_coefficient = null + permeability_coefficient = null + mask_adjusted = 1 + if(adjusted_flags) + slot_flags = adjusted_flags + if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals. + Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/ + H.internal = null + H.update_action_buttons_icon() + if(flags_inv & HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. + flags_inv &= ~HIDEFACE /*Done after the above to avoid having to do a check for initial(src.flags_inv == HIDEFACE). + This reveals the user's face since the bandana will now be going on their head.*/ + if(flags_cover & MASKCOVERSMOUTH) //Mask won't cover the mouth any more since it's been pushed out of the way. Allows for CPRing with adjusted masks. + flags_cover &= ~MASKCOVERSMOUTH + if(flags & AIRTIGHT) //If the mask was airtight, it won't be anymore since you just pushed it off your face. + flags &= ~AIRTIGHT + if(user.wear_mask == src && initial(flags_inv) == HIDEFACE) //Means that you won't have to take off and put back on simple things like breath masks which, realistically, can just be pulled down off your face. + if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. + user.unEquip(src) + else //Otherwise, put it in an available hand, the active one preferentially. + src.loc = user + user.wear_mask = null + user.put_in_hands(src) + H.wear_mask_update(src, toggle_off = mask_adjusted) + usr.update_inv_wear_mask() + usr.update_inv_head() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +//Shoes +/obj/item/clothing/shoes + name = "shoes" + icon = 'icons/obj/clothing/shoes.dmi' + desc = "Comfortable-looking shoes." + gender = PLURAL //Carn: for grammatically correct text-parsing + var/chained = 0 + var/can_cut_open = 0 + var/cut_open = 0 + body_parts_covered = FEET + slot_flags = SLOT_FEET + + var/silence_steps = 0 + var/shoe_sound_footstep = 1 + var/shoe_sound = null + var/blood_state = BLOOD_STATE_NOT_BLOODY + var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + + permeability_coefficient = 0.50 + slowdown = SHOES_SLOWDOWN + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/shoes.dmi', + "Drask" = 'icons/mob/species/drask/shoes.dmi' + ) + +/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/match) && src.loc == user) + var/obj/item/match/M = I + if(M.matchignite()) // Match isn't lit, but isn't burnt. + user.visible_message("[user] strikes a [M] on the bottom of [src], lighting it.","You strike the [M] on the bottom of [src] to light it.") + playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) + else + user.visible_message("[user] crushes the [M] into the bottom of [src], extinguishing it.","You crush the [M] into the bottom of [src], extinguishing it.") + M.dropped() + return + + if(istype(I, /obj/item/wirecutters)) + if(can_cut_open) + if(!cut_open) + playsound(src.loc, I.usesound, 100, 1) + user.visible_message("[user] cuts open the toes of [src].","You cut open the toes of [src].") + cut_open = 1 + icon_state = "[icon_state]_opentoe" + item_state = "[item_state]_opentoe" + name = "mangled [name]" + desc = "[desc] They have had their toes opened up." + update_icon() + else + to_chat(user, "[src] have already had [p_their()] toes cut open!") + return + else + return ..() + +/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek + SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION) + if(shoe_sound) + var/turf/T = get_turf(H) + + if(!istype(H) || !istype(T)) + return 0 + + if(H.m_intent == MOVE_INTENT_RUN) + if(shoe_sound_footstep >= 2) + if(T.shoe_running_volume) + playsound(src, shoe_sound, T.shoe_running_volume, 1) + shoe_sound_footstep = 0 + else + shoe_sound_footstep++ + else if(T.shoe_walking_volume) + playsound(src, shoe_sound, T.shoe_walking_volume, 1) + + return 1 + +/obj/item/proc/negates_gravity() + return 0 + +//Suit +/obj/item/clothing/suit + icon = 'icons/obj/clothing/suits.dmi' + name = "suit" + var/fire_resist = T0C+100 + allowed = list(/obj/item/tank/emergency_oxygen) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + slot_flags = SLOT_OCLOTHING + var/blood_overlay_type = "suit" + var/suittoggled = FALSE + var/suit_adjusted = 0 + var/ignore_suitadjust = 1 + var/adjust_flavour = null + var/list/hide_tail_by_species = null + +//Proc that opens and closes jackets. +/obj/item/clothing/suit/proc/adjustsuit(var/mob/user) + if(!ignore_suitadjust) + if(!user.incapacitated()) + if(!(HULK in user.mutations)) + if(suit_adjusted) + var/flavour = "close" + icon_state = copytext(icon_state, 1, findtext(icon_state, "_open")) /*Trims the '_open' off the end of the icon state, thus avoiding a case where jackets that start open will + end up with a suffix of _open_open if adjusted twice, since their initial state is _open. */ + item_state = copytext(item_state, 1, findtext(item_state, "_open")) + if(adjust_flavour) + flavour = "[copytext(adjust_flavour, 3, length(adjust_flavour) + 1)] up" //Trims off the 'un' at the beginning of the word. unzip -> zip, unbutton->button. + to_chat(user, "You [flavour] \the [src].") + suit_adjusted = 0 //Suit is no longer adjusted. + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + else + var/flavour = "open" + icon_state += "_open" + item_state += "_open" + if(adjust_flavour) + flavour = "[adjust_flavour]" + to_chat(user, "You [flavour] \the [src].") + suit_adjusted = 1 //Suit's adjusted. + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + else + if(user.canUnEquip(src)) //Checks to see if the item can be unequipped. If so, lets shred. Otherwise, struggle and fail. + if(contents) //If the suit's got any storage capability... + for(var/obj/item/O in contents) //AVOIDING ITEM LOSS. Check through everything that's stored in the jacket and see if one of the items is a pocket. + if(istype(O, /obj/item/storage/internal)) //If it's a pocket... + if(O.contents) //Check to see if the pocket's got anything in it. + for(var/obj/item/I in O.contents) //Dump the pocket out onto the floor below the user. + user.unEquip(I,1) + + user.visible_message("[user] bellows, [pick("shredding", "ripping open", "tearing off")] [user.p_their()] jacket in a fit of rage!","You accidentally [pick("shred", "rend", "tear apart")] [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")]!") + user.unEquip(src) + qdel(src) //Now that the pockets have been emptied, we can safely destroy the jacket. + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) + else + to_chat(user, "You yank and pull at \the [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")], however you are unable to change its state!")//Yep, that's all they get. Avoids having to snowflake in a cooldown. + + return + user.update_inv_wear_suit() + else + to_chat(user, "You attempt to button up the velcro on \the [src], before promptly realising how retarded you are.") + +/obj/item/clothing/suit/equipped(var/mob/living/carbon/human/user, var/slot) //Handle tail-hiding on a by-species basis. + ..() + if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit) + if(user.dna.species.name in hide_tail_by_species) + if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden. + flags_inv |= HIDETAIL + else + if(!(initial(flags_inv) & HIDETAIL) && (flags_inv & HIDETAIL)) //Otherwise, remove the HIDETAIL flag if it wasn't already in the flags_inv to start with. + flags_inv &= ~HIDETAIL + +/obj/item/clothing/suit/ui_action_click(mob/user) //This is what happens when you click the HUD action button to adjust your suit. + if(!ignore_suitadjust) + adjustsuit(user) + else + ..() //This is required in order to ensure that the UI buttons for items that have alternate functions tied to UI buttons still work. + +/obj/item/clothing/suit/proc/special_overlays() // Does it have special overlays when worn? + return FALSE + +//Spacesuit +//Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. +// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! +/obj/item/clothing/head/helmet/space + name = "Space helmet" + icon_state = "space" + desc = "A special helmet designed for work in a hazardous, low-pressure environment." + flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "s_helmet" + permeability_coefficient = 0.01 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + species_restricted = list("exclude","Wryn") + flash_protect = 2 + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + dog_fashion = null + + +/obj/item/clothing/suit/space + name = "Space suit" + desc = "A suit that protects against low pressure environments. Has a big 13 on the back." + icon_state = "space" + item_state = "s_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank) + slowdown = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + strip_delay = 80 + put_on_delay = 80 + resistance_flags = NONE + hide_tail_by_species = null + species_restricted = list("exclude","Wryn") + + +//Under clothing +/obj/item/clothing/under + icon = 'icons/obj/clothing/uniforms.dmi' + name = "under" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + permeability_coefficient = 0.90 + slot_flags = SLOT_ICLOTHING + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi', + "Drask" = 'icons/mob/species/drask/uniform.dmi', + "Grey" = 'icons/mob/species/grey/uniform.dmi' + ) + + var/has_sensor = TRUE//For the crew computer 2 = unable to change mode + var/sensor_mode = SENSOR_OFF + var/random_sensor = TRUE + /* + 1 = Report living/dead + 2 = Report detailed damages + 3 = Report location + */ + var/list/accessories = list() + var/displays_id = 1 + var/rolled_down = 0 + var/basecolor + +/obj/item/clothing/under/rank/New() + if(random_sensor) + sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS) + ..() + +/obj/item/clothing/under/Destroy() + QDEL_LIST(accessories) + return ..() + +/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) + if(istype(A)) + . = TRUE + else + return FALSE + + if(accessories.len) + for(var/obj/item/clothing/accessory/AC in accessories) + if((A.slot in list(ACCESSORY_SLOT_UTILITY, ACCESSORY_SLOT_ARMBAND)) && AC.slot == A.slot) + return FALSE + if(!A.allow_duplicates && AC.type == A.type) + return FALSE + +/obj/item/clothing/under/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/clothing/accessory)) + attach_accessory(I, user, TRUE) + + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.attackby(I, user, params) + return TRUE + + . = ..() + +/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/A, mob/user, unequip = FALSE) + if(can_attach_accessory(A)) + if(unequip && !user.unEquip(A)) // Make absolutely sure this accessory is removed from hands + return FALSE + + accessories += A + A.on_attached(src, user) + + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + H.update_inv_w_uniform() + + return TRUE + else + to_chat(user, "You cannot attach more accessories of this type to [src].") + + return FALSE + +/obj/item/clothing/under/examine(mob/user) + . = ..() + switch(sensor_mode) + if(0) + . += "Its sensors appear to be disabled." + if(1) + . += "Its binary life sensors appear to be enabled." + if(2) + . += "Its vital tracker appears to be enabled." + if(3) + . += "Its vital tracker and tracking beacon appear to be enabled." + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + . += "\A [A] is attached to it." + + +/obj/item/clothing/under/verb/rollsuit() + set name = "Roll Down Jumpsuit" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + if(!usr.incapacitated()) + if(copytext(item_color,-2) != "_d") + basecolor = item_color + if(basecolor + "_d_s" in icon_states('icons/mob/uniform.dmi')) + item_color = item_color == "[basecolor]" ? "[basecolor]_d" : "[basecolor]" + usr.update_inv_w_uniform() + else + to_chat(usr, "You cannot roll down this uniform!") + else + to_chat(usr, "You cannot roll down the uniform!") + +/obj/item/clothing/under/verb/removetie() + set name = "Remove Accessory" + set category = "Object" + set src in usr + handle_accessories_removal() + +/obj/item/clothing/under/proc/handle_accessories_removal() + if(!isliving(usr)) + return + if(usr.incapacitated()) + return + if(!Adjacent(usr)) + return + if(!accessories.len) + return + var/obj/item/clothing/accessory/A + if(accessories.len > 1) + A = input("Select an accessory to remove from [src]") as null|anything in accessories + else + A = accessories[1] + remove_accessory(usr,A) + +/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) + if(!(A in accessories)) + return + if(!isliving(user)) + return + if(user.incapacitated()) + return + if(!Adjacent(user)) + return + A.on_removed(user) + accessories -= A + to_chat(user, "You remove [A] from [src].") + usr.update_inv_w_uniform() + +/obj/item/clothing/under/emp_act(severity) + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.emp_act(severity) + ..() + +/obj/item/clothing/under/AltClick() + handle_accessories_removal() + +/obj/item/clothing/obj_destruction(damage_flag) + if(damage_flag == "bomb" || damage_flag == "melee") + var/turf/T = get_turf(src) + spawn(1) //so the shred survives potential turf change from the explosion. + var/obj/effect/decal/cleanable/shreds/Shreds = new(T) + Shreds.desc = "The sad remains of what used to be [name]." + deconstruct(FALSE) + else + ..() diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm index 2ba883a6f895..ea8f4198f50c 100644 --- a/code/modules/clothing/ears/ears.dm +++ b/code/modules/clothing/ears/ears.dm @@ -25,4 +25,4 @@ var/datum/action/A = X A.UpdateButtonIcon() - user.update_inv_ears() \ No newline at end of file + user.update_inv_ears() diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index e80f461246e0..1b9c87f56e5a 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -1,525 +1,525 @@ -/obj/item/clothing/glasses/New() - . = ..() - if(prescription_upgradable && prescription) - // Pre-upgraded upgradable glasses - name = "prescription [name]" - -/obj/item/clothing/glasses/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(user.stat || user.restrained() || !ishuman(user)) - return ..() - var/mob/living/carbon/human/H = user - if(prescription_upgradable) - if(istype(O, /obj/item/clothing/glasses/regular)) - if(prescription) - to_chat(H, "You can't possibly imagine how adding more lenses would improve \the [name].") - return - H.unEquip(O) - O.loc = src // Store the glasses for later removal - to_chat(H, "You fit \the [name] with lenses from \the [O].") - prescription = 1 - name = "prescription [name]" - return - if(prescription && istype(O, /obj/item/screwdriver)) - var/obj/item/clothing/glasses/regular/G = locate() in src - if(!G) - G = new(get_turf(H)) - to_chat(H, "You salvage the prescription lenses from \the [name].") - prescription = 0 - name = initial(name) - H.put_in_hands(G) - return - return ..() - -/obj/item/clothing/glasses/visor_toggling() - ..() - if(visor_vars_to_toggle & VISOR_VISIONFLAGS) - vision_flags ^= initial(vision_flags) - if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) - see_in_dark ^= initial(see_in_dark) - if(visor_vars_to_toggle & VISOR_INVISVIEW) - invis_view ^= initial(invis_view) - -/obj/item/clothing/glasses/weldingvisortoggle(mob/user) - . = ..() - if(. && user) - user.update_sight() - user.update_inv_glasses() - -/obj/item/clothing/glasses/meson - name = "Optical Meson Scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state = "glasses" - origin_tech = "magnets=1;engineering=2" - vision_flags = SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/meson/night - name = "Night Vision Optical Meson Scanner" - desc = "An Optical Meson Scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." - icon_state = "nvgmeson" - item_state = "glasses" - origin_tech = "magnets=4;engineering=5;plasmatech=4" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/meson/prescription - prescription = 1 - -/obj/item/clothing/glasses/meson/gar - name = "gar mesons" - icon_state = "garm" - item_state = "garm" - desc = "Do the impossible, see the invisible!" - force = 10 - throwforce = 10 - throw_speed = 4 - attack_verb = list("sliced") - hitsound = 'sound/weapons/bladeslice.ogg' - sharp = 1 - -/obj/item/clothing/glasses/meson/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with meson vision capabilities." - icon_state = "cybereye-green" - item_state = "eyepatch" - flags = NODROP - flags_cover = null - prescription_upgradable = 0 - -/obj/item/clothing/glasses/science - name = "science goggles" - desc = "A pair of snazzy goggles used to protect against chemical spills. Fitted with an analyzer for scanning items and reagents." - icon_state = "purple" - item_state = "glasses" - origin_tech = "magnets=2;engineering=1" - prescription_upgradable = 0 - scan_reagents = 1 //You can see reagents while wearing science goggles - resistance_flags = ACID_PROOF - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - actions_types = list(/datum/action/item_action/toggle_research_scanner) - -/obj/item/clothing/glasses/science/item_action_slot_check(slot) - if(slot == slot_glasses) - return 1 - -/obj/item/clothing/glasses/science/night - name = "Night Vision Science Goggle" - desc = "Now you can science in darkness." - icon_state = "nvpurple" - item_state = "glasses" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - -/obj/item/clothing/glasses/janitor - name = "Janitorial Goggles" - desc = "These'll keep the soap out of your eyes." - icon_state = "purple" - item_state = "glasses" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/night - name = "Night Vision Goggles" - desc = "You can totally see in the dark now!" - icon_state = "night" - item_state = "glasses" - origin_tech = "materials=4;magnets=4;plasmatech=4;engineering=4" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/eyepatch - name = "eyepatch" - desc = "Yarr." - icon_state = "eyepatch" - item_state = "eyepatch" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/monocle - name = "monocle" - desc = "Such a dapper eyepiece!" - icon_state = "monocle" - item_state = "headset" // lol - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/material - name = "Optical Material Scanner" - desc = "Very confusing glasses." - icon_state = "material" - item_state = "glasses" - origin_tech = "magnets=3;engineering=3" - vision_flags = SEE_OBJS - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/material/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with material vision capabilities." - icon_state = "cybereye-blue" - item_state = "eyepatch" - flags = NODROP - flags_cover = null - -/obj/item/clothing/glasses/material/lighting - name = "Neutron Goggles" - desc = "These odd glasses use a form of neutron-based imaging to completely negate the effects of light and darkness." - origin_tech = null - vision_flags = 0 - - flags = NODROP - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - -/obj/item/clothing/glasses/regular - name = "prescription glasses" - desc = "Made by Nerd. Co." - icon_state = "glasses" - item_state = "glasses" - prescription = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/regular/hipster - name = "prescription glasses" - desc = "Made by Uncool. Co." - icon_state = "hipster_glasses" - item_state = "hipster_glasses" - -/obj/item/clothing/glasses/threedglasses - desc = "A long time ago, people used these glasses to makes images from screens threedimensional." - name = "3D glasses" - icon_state = "3d" - item_state = "3d" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/gglasses - name = "Green Glasses" - desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." - icon_state = "gglasses" - item_state = "gglasses" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - prescription_upgradable = 1 - -/obj/item/clothing/glasses/sunglasses - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - name = "sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - prescription_upgradable = 1 - dog_fashion = /datum/dog_fashion/head - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses_fake - desc = "Cheap, plastic sunglasses. They don't even have UV protection." - name = "cheap sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 0 - flash_protect = 0 - tint = 0 - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses/noir - name = "noir sunglasses" - desc = "Somehow these seem even more out-of-date than normal sunglasses." - actions_types = list(/datum/action/item_action/noir) - -/obj/item/clothing/glasses/sunglasses/noir/attack_self(mob/user) - toggle_noir(user) - -/obj/item/clothing/glasses/sunglasses/noir/item_action_slot_check(slot) - if(slot == slot_glasses) - return 1 - -/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir(mob/user) - color_view = color_view ? null : MATRIX_GREYSCALE //Toggles between null and grayscale, with null being the default option. - user.update_client_colour() - -/obj/item/clothing/glasses/sunglasses/yeah - name = "agreeable glasses" - desc = "H.C Limited edition." - var/punused = null - actions_types = list(/datum/action/item_action/YEEEAAAAAHHHHHHHHHHHHH) - -/obj/item/clothing/glasses/sunglasses/yeah/attack_self() - pun() - -/obj/item/clothing/glasses/sunglasses/yeah/proc/pun() - if(!punused)//one per round - punused = 1 - playsound(src.loc, 'sound/misc/yeah.ogg', 100, 0) - usr.visible_message("YEEEAAAAAHHHHHHHHHHHHH!!") - else - to_chat(usr, "The moment is gone.") - - -/obj/item/clothing/glasses/sunglasses/reagent - name = "sunscanners" - desc = "Strangely ancient technology used to help provide rudimentary eye color. Outfitted with apparatus to scan individual reagents." - scan_reagents = 1 - -/obj/item/clothing/glasses/virussunglasses - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - name = "sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses/lasers - desc = "A peculiar set of sunglasses; they have various chips and other panels attached to the sides of the frames." - name = "high-tech sunglasses" - flags = NODROP - -/obj/item/clothing/glasses/sunglasses/lasers/equipped(mob/user, slot) //grant them laser eyes upon equipping it. - if(slot == slot_glasses) - user.mutations.Add(LASER) - user.regenerate_icons() - ..(user, slot) - -/obj/item/clothing/glasses/welding - name = "welding goggles" - desc = "Protects the eyes from welders, approved by the mad scientist association." - icon_state = "welding-g" - item_state = "welding-g" - actions_types = list(/datum/action/item_action/toggle) - flash_protect = 2 - tint = 2 - visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/glasses/welding/superior - name = "superior welding goggles" - desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." - icon_state = "rwelding-g" - item_state = "rwelding-g" - flash_protect = 2 - tint = 0 - -/obj/item/clothing/glasses/sunglasses/blindfold - name = "blindfold" - desc = "Covers the eyes, preventing sight." - icon_state = "blindfold" - item_state = "blindfold" - //vision_flags = BLIND - flash_protect = 2 - tint = 3 //to make them blind - prescription_upgradable = 0 - -/obj/item/clothing/glasses/sunglasses/prescription - prescription = 1 - -/obj/item/clothing/glasses/sunglasses/big - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." - icon_state = "bigsunglasses" - item_state = "bigsunglasses" - -/obj/item/clothing/glasses/thermal - name = "Optical Thermal Scanner" - desc = "Thermals in the shape of glasses." - icon_state = "thermal" - item_state = "glasses" - origin_tech = "magnets=3" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - flash_protect = -1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/thermal/emp_act(severity) - if(istype(src.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = src.loc - to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") - if(M.glasses == src) - M.EyeBlind(3) - M.EyeBlurry(5) - if(!(M.disabilities & NEARSIGHTED)) - M.BecomeNearsighted() - spawn(100) - M.CureNearsighted() - ..() - -/obj/item/clothing/glasses/thermal/monocle - name = "Thermoncle" - desc = "A monocle thermal." - icon_state = "thermoncle" - flags_cover = null //doesn't protect eyes because it's a monocle, duh - -/obj/item/clothing/glasses/thermal/eyepatch - name = "Optical Thermal Eyepatch" - desc = "An eyepatch with built-in thermal optics" - icon_state = "eyepatch" - item_state = "eyepatch" - -/obj/item/clothing/glasses/thermal/jensen - name = "Optical Thermal Implants" - desc = "A set of implantable lenses designed to augment your vision" - icon_state = "thermalimplants" - item_state = "syringe_kit" - -/obj/item/clothing/glasses/thermal/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with thermal vision capabilities." - icon_state = "cybereye-red" - item_state = "eyepatch" - flags = NODROP - - -/obj/item/clothing/glasses/godeye - name = "eye of god" - desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." - icon_state = "godeye" - item_state = "godeye" - vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - scan_reagents = 1 - flags = NODROP - flags_cover = null - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - resistance_flags = LAVA_PROOF | FIRE_PROOF - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, src) && W != src && W.loc == user) - if(W.icon_state == "godeye") - W.icon_state = "doublegodeye" - W.item_state = "doublegodeye" - W.desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.update_inv_wear_mask() - else - to_chat(user, "The eye winks at you and vanishes into the abyss, you feel really unlucky.") - qdel(src) - ..() - -/obj/item/clothing/glasses/tajblind - name = "embroidered veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes." - icon_state = "tajblind" - item_state = "tajblind" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - tint = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/tajblind/eng - name = "industrial veil" - icon_state = "tajblind_engi" - item_state = "tajblind_engi" - -/obj/item/clothing/glasses/tajblind/sci - name = "hi-tech veil" - icon_state = "tajblind_sci" - item_state = "tajblind_sci" - -/obj/item/clothing/glasses/tajblind/cargo - name = "khaki veil" - icon_state = "tajblind_cargo" - item_state = "tajblind_cargo" - -/obj/item/clothing/glasses/tajblind/attack_self() - toggle_veil() - -/obj/item/clothing/glasses/proc/toggle_veil() - if(usr.canmove && !usr.incapacitated()) - if(up) - up = !up - tint = initial(tint) - to_chat(usr, "You activate [src], allowing you to see.") - else - up = !up - tint = 3 - to_chat(usr, "You deactivate [src], obscuring your vision.") - var/mob/living/carbon/user = usr - user.update_tint() - user.update_inv_glasses() \ No newline at end of file +/obj/item/clothing/glasses/New() + . = ..() + if(prescription_upgradable && prescription) + // Pre-upgraded upgradable glasses + name = "prescription [name]" + +/obj/item/clothing/glasses/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(user.stat || user.restrained() || !ishuman(user)) + return ..() + var/mob/living/carbon/human/H = user + if(prescription_upgradable) + if(istype(O, /obj/item/clothing/glasses/regular)) + if(prescription) + to_chat(H, "You can't possibly imagine how adding more lenses would improve \the [name].") + return + H.unEquip(O) + O.loc = src // Store the glasses for later removal + to_chat(H, "You fit \the [name] with lenses from \the [O].") + prescription = 1 + name = "prescription [name]" + return + if(prescription && istype(O, /obj/item/screwdriver)) + var/obj/item/clothing/glasses/regular/G = locate() in src + if(!G) + G = new(get_turf(H)) + to_chat(H, "You salvage the prescription lenses from \the [name].") + prescription = 0 + name = initial(name) + H.put_in_hands(G) + return + return ..() + +/obj/item/clothing/glasses/visor_toggling() + ..() + if(visor_vars_to_toggle & VISOR_VISIONFLAGS) + vision_flags ^= initial(vision_flags) + if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) + see_in_dark ^= initial(see_in_dark) + if(visor_vars_to_toggle & VISOR_INVISVIEW) + invis_view ^= initial(invis_view) + +/obj/item/clothing/glasses/weldingvisortoggle(mob/user) + . = ..() + if(. && user) + user.update_sight() + user.update_inv_glasses() + +/obj/item/clothing/glasses/meson + name = "Optical Meson Scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state = "glasses" + origin_tech = "magnets=1;engineering=2" + vision_flags = SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/meson/night + name = "Night Vision Optical Meson Scanner" + desc = "An Optical Meson Scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." + icon_state = "nvgmeson" + item_state = "glasses" + origin_tech = "magnets=4;engineering=5;plasmatech=4" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/meson/prescription + prescription = 1 + +/obj/item/clothing/glasses/meson/gar + name = "gar mesons" + icon_state = "garm" + item_state = "garm" + desc = "Do the impossible, see the invisible!" + force = 10 + throwforce = 10 + throw_speed = 4 + attack_verb = list("sliced") + hitsound = 'sound/weapons/bladeslice.ogg' + sharp = 1 + +/obj/item/clothing/glasses/meson/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with meson vision capabilities." + icon_state = "cybereye-green" + item_state = "eyepatch" + flags = NODROP + flags_cover = null + prescription_upgradable = 0 + +/obj/item/clothing/glasses/science + name = "science goggles" + desc = "A pair of snazzy goggles used to protect against chemical spills. Fitted with an analyzer for scanning items and reagents." + icon_state = "purple" + item_state = "glasses" + origin_tech = "magnets=2;engineering=1" + prescription_upgradable = 0 + scan_reagents = 1 //You can see reagents while wearing science goggles + resistance_flags = ACID_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + actions_types = list(/datum/action/item_action/toggle_research_scanner) + +/obj/item/clothing/glasses/science/item_action_slot_check(slot) + if(slot == slot_glasses) + return 1 + +/obj/item/clothing/glasses/science/night + name = "Night Vision Science Goggle" + desc = "Now you can science in darkness." + icon_state = "nvpurple" + item_state = "glasses" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + +/obj/item/clothing/glasses/janitor + name = "Janitorial Goggles" + desc = "These'll keep the soap out of your eyes." + icon_state = "purple" + item_state = "glasses" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/night + name = "Night Vision Goggles" + desc = "You can totally see in the dark now!" + icon_state = "night" + item_state = "glasses" + origin_tech = "materials=4;magnets=4;plasmatech=4;engineering=4" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/eyepatch + name = "eyepatch" + desc = "Yarr." + icon_state = "eyepatch" + item_state = "eyepatch" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/monocle + name = "monocle" + desc = "Such a dapper eyepiece!" + icon_state = "monocle" + item_state = "headset" // lol + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/material + name = "Optical Material Scanner" + desc = "Very confusing glasses." + icon_state = "material" + item_state = "glasses" + origin_tech = "magnets=3;engineering=3" + vision_flags = SEE_OBJS + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/material/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with material vision capabilities." + icon_state = "cybereye-blue" + item_state = "eyepatch" + flags = NODROP + flags_cover = null + +/obj/item/clothing/glasses/material/lighting + name = "Neutron Goggles" + desc = "These odd glasses use a form of neutron-based imaging to completely negate the effects of light and darkness." + origin_tech = null + vision_flags = 0 + + flags = NODROP + lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + +/obj/item/clothing/glasses/regular + name = "prescription glasses" + desc = "Made by Nerd. Co." + icon_state = "glasses" + item_state = "glasses" + prescription = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/regular/hipster + name = "prescription glasses" + desc = "Made by Uncool. Co." + icon_state = "hipster_glasses" + item_state = "hipster_glasses" + +/obj/item/clothing/glasses/threedglasses + desc = "A long time ago, people used these glasses to makes images from screens threedimensional." + name = "3D glasses" + icon_state = "3d" + item_state = "3d" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/gglasses + name = "Green Glasses" + desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." + icon_state = "gglasses" + item_state = "gglasses" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + prescription_upgradable = 1 + +/obj/item/clothing/glasses/sunglasses + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + name = "sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + prescription_upgradable = 1 + dog_fashion = /datum/dog_fashion/head + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses_fake + desc = "Cheap, plastic sunglasses. They don't even have UV protection." + name = "cheap sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 0 + flash_protect = 0 + tint = 0 + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses/noir + name = "noir sunglasses" + desc = "Somehow these seem even more out-of-date than normal sunglasses." + actions_types = list(/datum/action/item_action/noir) + +/obj/item/clothing/glasses/sunglasses/noir/attack_self(mob/user) + toggle_noir(user) + +/obj/item/clothing/glasses/sunglasses/noir/item_action_slot_check(slot) + if(slot == slot_glasses) + return 1 + +/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir(mob/user) + color_view = color_view ? null : MATRIX_GREYSCALE //Toggles between null and grayscale, with null being the default option. + user.update_client_colour() + +/obj/item/clothing/glasses/sunglasses/yeah + name = "agreeable glasses" + desc = "H.C Limited edition." + var/punused = null + actions_types = list(/datum/action/item_action/YEEEAAAAAHHHHHHHHHHHHH) + +/obj/item/clothing/glasses/sunglasses/yeah/attack_self() + pun() + +/obj/item/clothing/glasses/sunglasses/yeah/proc/pun() + if(!punused)//one per round + punused = 1 + playsound(src.loc, 'sound/misc/yeah.ogg', 100, 0) + usr.visible_message("YEEEAAAAAHHHHHHHHHHHHH!!") + else + to_chat(usr, "The moment is gone.") + + +/obj/item/clothing/glasses/sunglasses/reagent + name = "sunscanners" + desc = "Strangely ancient technology used to help provide rudimentary eye color. Outfitted with apparatus to scan individual reagents." + scan_reagents = 1 + +/obj/item/clothing/glasses/virussunglasses + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + name = "sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses/lasers + desc = "A peculiar set of sunglasses; they have various chips and other panels attached to the sides of the frames." + name = "high-tech sunglasses" + flags = NODROP + +/obj/item/clothing/glasses/sunglasses/lasers/equipped(mob/user, slot) //grant them laser eyes upon equipping it. + if(slot == slot_glasses) + user.mutations.Add(LASER) + user.regenerate_icons() + ..(user, slot) + +/obj/item/clothing/glasses/welding + name = "welding goggles" + desc = "Protects the eyes from welders, approved by the mad scientist association." + icon_state = "welding-g" + item_state = "welding-g" + actions_types = list(/datum/action/item_action/toggle) + flash_protect = 2 + tint = 2 + visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/glasses/welding/superior + name = "superior welding goggles" + desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." + icon_state = "rwelding-g" + item_state = "rwelding-g" + flash_protect = 2 + tint = 0 + +/obj/item/clothing/glasses/sunglasses/blindfold + name = "blindfold" + desc = "Covers the eyes, preventing sight." + icon_state = "blindfold" + item_state = "blindfold" + //vision_flags = BLIND + flash_protect = 2 + tint = 3 //to make them blind + prescription_upgradable = 0 + +/obj/item/clothing/glasses/sunglasses/prescription + prescription = 1 + +/obj/item/clothing/glasses/sunglasses/big + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." + icon_state = "bigsunglasses" + item_state = "bigsunglasses" + +/obj/item/clothing/glasses/thermal + name = "Optical Thermal Scanner" + desc = "Thermals in the shape of glasses." + icon_state = "thermal" + item_state = "glasses" + origin_tech = "magnets=3" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + flash_protect = -1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/thermal/emp_act(severity) + if(istype(src.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = src.loc + to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") + if(M.glasses == src) + M.EyeBlind(3) + M.EyeBlurry(5) + if(!(M.disabilities & NEARSIGHTED)) + M.BecomeNearsighted() + spawn(100) + M.CureNearsighted() + ..() + +/obj/item/clothing/glasses/thermal/monocle + name = "Thermoncle" + desc = "A monocle thermal." + icon_state = "thermoncle" + flags_cover = null //doesn't protect eyes because it's a monocle, duh + +/obj/item/clothing/glasses/thermal/eyepatch + name = "Optical Thermal Eyepatch" + desc = "An eyepatch with built-in thermal optics" + icon_state = "eyepatch" + item_state = "eyepatch" + +/obj/item/clothing/glasses/thermal/jensen + name = "Optical Thermal Implants" + desc = "A set of implantable lenses designed to augment your vision" + icon_state = "thermalimplants" + item_state = "syringe_kit" + +/obj/item/clothing/glasses/thermal/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with thermal vision capabilities." + icon_state = "cybereye-red" + item_state = "eyepatch" + flags = NODROP + + +/obj/item/clothing/glasses/godeye + name = "eye of god" + desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." + icon_state = "godeye" + item_state = "godeye" + vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + scan_reagents = 1 + flags = NODROP + flags_cover = null + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + resistance_flags = LAVA_PROOF | FIRE_PROOF + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, src) && W != src && W.loc == user) + if(W.icon_state == "godeye") + W.icon_state = "doublegodeye" + W.item_state = "doublegodeye" + W.desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.update_inv_wear_mask() + else + to_chat(user, "The eye winks at you and vanishes into the abyss, you feel really unlucky.") + qdel(src) + ..() + +/obj/item/clothing/glasses/tajblind + name = "embroidered veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes." + icon_state = "tajblind" + item_state = "tajblind" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + tint = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/tajblind/eng + name = "industrial veil" + icon_state = "tajblind_engi" + item_state = "tajblind_engi" + +/obj/item/clothing/glasses/tajblind/sci + name = "hi-tech veil" + icon_state = "tajblind_sci" + item_state = "tajblind_sci" + +/obj/item/clothing/glasses/tajblind/cargo + name = "khaki veil" + icon_state = "tajblind_cargo" + item_state = "tajblind_cargo" + +/obj/item/clothing/glasses/tajblind/attack_self() + toggle_veil() + +/obj/item/clothing/glasses/proc/toggle_veil() + if(usr.canmove && !usr.incapacitated()) + if(up) + up = !up + tint = initial(tint) + to_chat(usr, "You activate [src], allowing you to see.") + else + up = !up + tint = 3 + to_chat(usr, "You deactivate [src], obscuring your vision.") + var/mob/living/carbon/user = usr + user.update_tint() + user.update_inv_glasses() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 27cd78afad34..4d2255a09ab5 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -1,198 +1,198 @@ -/obj/item/clothing/glasses/hud - name = "\improper HUD" - desc = "A heads-up display that provides important info in (almost) real time." - flags = null //doesn't protect eyes because it's a monocle, duh - origin_tech = "magnets=3;biotech=2" - var/HUDType = null //Hudtype is defined on glasses.dm - prescription_upgradable = 1 - var/list/icon/current = list() //the current hud icons - - -/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) - ..() - if(HUDType && slot == slot_glasses) - var/datum/atom_hud/H = huds[HUDType] - H.add_hud_to(user) - -/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) - ..() - if(HUDType && istype(user) && user.glasses == src) - var/datum/atom_hud/H = huds[HUDType] - H.remove_hud_from(user) - -/obj/item/clothing/glasses/hud/emp_act(severity) - if(emagged == 0) - emagged = 1 - desc = desc + " The display flickers slightly." - -/obj/item/clothing/glasses/hud/health - name = "\improper Health Scanner HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." - icon_state = "healthhud" - origin_tech = "magnets=3;biotech=2" - HUDType = DATA_HUD_MEDICAL_ADVANCED - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/health/night - name = "\improper Night Vision Health Scanner HUD" - desc = "An advanced medical head-up display that allows doctors to find patients in complete darkness." - icon_state = "healthhudnight" - item_state = "glasses" - origin_tech = "magnets=4;biotech=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/health/sunglasses - name = "medical HUDSunglasses" - desc = "Sunglasses with a medical HUD." - icon_state = "sunhudmed" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - -/obj/item/clothing/glasses/hud/diagnostic - name = "Diagnostic HUD" - desc = "A heads-up display capable of analyzing the integrity and status of robotics and exosuits." - icon_state = "diagnostichud" - origin_tech = "magnets=2;engineering=2" - HUDType = DATA_HUD_DIAGNOSTIC - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/diagnostic/night - name = "Night Vision Diagnostic HUD" - desc = "A robotics diagnostic HUD fitted with a light amplifier." - icon_state = "diagnostichudnight" - item_state = "glasses" - origin_tech = "magnets=4;powerstorage=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/diagnostic/sunglasses - name = "diagnostic sunglasses" - desc = "Sunglasses with a diagnostic HUD." - icon_state = "sunhuddiag" - item_state = "glasses" - flash_protect = 1 - tint = 1 - -/obj/item/clothing/glasses/hud/security - name = "\improper Security HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." - icon_state = "securityhud" - origin_tech = "magnets=3;combat=2" - var/global/list/jobs[0] - HUDType = DATA_HUD_SECURITY_ADVANCED - var/read_only = FALSE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - - -/obj/item/clothing/glasses/hud/security/sunglasses/jensenshades - name = "augmented shades" - desc = "Polarized bioneural eyewear, designed to augment your vision." - icon_state = "jensenshades" - item_state = "jensenshades" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/item/clothing/glasses/hud/security/night - name = "\improper Night Vision Security HUD" - desc = "An advanced heads-up display which provides id data and vision in complete darkness." - icon_state = "securityhudnight" - origin_tech = "magnets=4;combat=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/security/sunglasses/read_only - read_only = TRUE - -/obj/item/clothing/glasses/hud/security/sunglasses - name = "HUDSunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunhud" - origin_tech = "magnets=3;combat=3;engineering=3" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/security/sunglasses/prescription - prescription = 1 - -/obj/item/clothing/glasses/hud/hydroponic - name = "Hydroponic HUD" - desc = "A heads-up display capable of analyzing the health and status of plants growing in hydro trays and soil." - icon_state = "hydroponichud" - HUDType = DATA_HUD_HYDROPONIC - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/hydroponic/night - name = "Night Vision Hydroponic HUD" - desc = "A hydroponic HUD fitted with a light amplifier." - icon_state = "hydroponichudnight" - item_state = "glasses" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/security/tajblind - name = "sleek veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an in-built security HUD." - icon_state = "tajblind_sec" - item_state = "tajblind_sec" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/security/tajblind/attack_self() - toggle_veil() - -/obj/item/clothing/glasses/hud/health/tajblind - name = "lightweight veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an installed medical HUD." - icon_state = "tajblind_med" - item_state = "tajblind_med" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/health/tajblind/attack_self() - toggle_veil() +/obj/item/clothing/glasses/hud + name = "\improper HUD" + desc = "A heads-up display that provides important info in (almost) real time." + flags = null //doesn't protect eyes because it's a monocle, duh + origin_tech = "magnets=3;biotech=2" + var/HUDType = null //Hudtype is defined on glasses.dm + prescription_upgradable = 1 + var/list/icon/current = list() //the current hud icons + + +/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) + ..() + if(HUDType && slot == slot_glasses) + var/datum/atom_hud/H = GLOB.huds[HUDType] + H.add_hud_to(user) + +/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) + ..() + if(HUDType && istype(user) && user.glasses == src) + var/datum/atom_hud/H = GLOB.huds[HUDType] + H.remove_hud_from(user) + +/obj/item/clothing/glasses/hud/emp_act(severity) + if(emagged == 0) + emagged = 1 + desc = desc + " The display flickers slightly." + +/obj/item/clothing/glasses/hud/health + name = "\improper Health Scanner HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." + icon_state = "healthhud" + origin_tech = "magnets=3;biotech=2" + HUDType = DATA_HUD_MEDICAL_ADVANCED + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/health/night + name = "\improper Night Vision Health Scanner HUD" + desc = "An advanced medical head-up display that allows doctors to find patients in complete darkness." + icon_state = "healthhudnight" + item_state = "glasses" + origin_tech = "magnets=4;biotech=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/health/sunglasses + name = "medical HUDSunglasses" + desc = "Sunglasses with a medical HUD." + icon_state = "sunhudmed" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + +/obj/item/clothing/glasses/hud/diagnostic + name = "Diagnostic HUD" + desc = "A heads-up display capable of analyzing the integrity and status of robotics and exosuits." + icon_state = "diagnostichud" + origin_tech = "magnets=2;engineering=2" + HUDType = DATA_HUD_DIAGNOSTIC + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/diagnostic/night + name = "Night Vision Diagnostic HUD" + desc = "A robotics diagnostic HUD fitted with a light amplifier." + icon_state = "diagnostichudnight" + item_state = "glasses" + origin_tech = "magnets=4;powerstorage=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/diagnostic/sunglasses + name = "diagnostic sunglasses" + desc = "Sunglasses with a diagnostic HUD." + icon_state = "sunhuddiag" + item_state = "glasses" + flash_protect = 1 + tint = 1 + +/obj/item/clothing/glasses/hud/security + name = "\improper Security HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." + icon_state = "securityhud" + origin_tech = "magnets=3;combat=2" + var/global/list/jobs[0] + HUDType = DATA_HUD_SECURITY_ADVANCED + var/read_only = FALSE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + + +/obj/item/clothing/glasses/hud/security/sunglasses/jensenshades + name = "augmented shades" + desc = "Polarized bioneural eyewear, designed to augment your vision." + icon_state = "jensenshades" + item_state = "jensenshades" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/item/clothing/glasses/hud/security/night + name = "\improper Night Vision Security HUD" + desc = "An advanced heads-up display which provides id data and vision in complete darkness." + icon_state = "securityhudnight" + origin_tech = "magnets=4;combat=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/security/sunglasses/read_only + read_only = TRUE + +/obj/item/clothing/glasses/hud/security/sunglasses + name = "HUDSunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunhud" + origin_tech = "magnets=3;combat=3;engineering=3" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/security/sunglasses/prescription + prescription = 1 + +/obj/item/clothing/glasses/hud/hydroponic + name = "Hydroponic HUD" + desc = "A heads-up display capable of analyzing the health and status of plants growing in hydro trays and soil." + icon_state = "hydroponichud" + HUDType = DATA_HUD_HYDROPONIC + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/hydroponic/night + name = "Night Vision Hydroponic HUD" + desc = "A hydroponic HUD fitted with a light amplifier." + icon_state = "hydroponichudnight" + item_state = "glasses" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/security/tajblind + name = "sleek veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an in-built security HUD." + icon_state = "tajblind_sec" + item_state = "tajblind_sec" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/security/tajblind/attack_self() + toggle_veil() + +/obj/item/clothing/glasses/hud/health/tajblind + name = "lightweight veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an installed medical HUD." + icon_state = "tajblind_med" + item_state = "tajblind_med" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/health/tajblind/attack_self() + toggle_veil() diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index c31e1abf4b59..701984de0552 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -1,19 +1,19 @@ -/obj/item/clothing/gloves/boxing - name = "boxing gloves" - desc = "Because you really needed another excuse to punch your crewmates." - icon_state = "boxing" - item_state = "boxing" - put_on_delay = 60 - species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion - -/obj/item/clothing/gloves/boxing/green - icon_state = "boxinggreen" - item_state = "boxinggreen" - -/obj/item/clothing/gloves/boxing/blue - icon_state = "boxingblue" - item_state = "boxingblue" - -/obj/item/clothing/gloves/boxing/yellow - icon_state = "boxingyellow" - item_state = "boxingyellow" \ No newline at end of file +/obj/item/clothing/gloves/boxing + name = "boxing gloves" + desc = "Because you really needed another excuse to punch your crewmates." + icon_state = "boxing" + item_state = "boxing" + put_on_delay = 60 + species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion + +/obj/item/clothing/gloves/boxing/green + icon_state = "boxinggreen" + item_state = "boxinggreen" + +/obj/item/clothing/gloves/boxing/blue + icon_state = "boxingblue" + item_state = "boxingblue" + +/obj/item/clothing/gloves/boxing/yellow + icon_state = "boxingyellow" + item_state = "boxingyellow" diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index cbf9d0695cea..ec4ac9bbf156 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,240 +1,240 @@ -/obj/item/clothing/gloves/color/yellow - desc = "These gloves will protect the wearer from electric shock." - name = "insulated gloves" - icon_state = "yellow" - item_state = "ygloves" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - item_color="yellow" - resistance_flags = NONE - -/obj/item/clothing/gloves/color/yellow/power - description_antag = "These are a pair of power gloves, and can be used to fire bolts of electricity while standing over powered power cables." - var/old_mclick_override - var/datum/middleClickOverride/power_gloves/mclick_override = new /datum/middleClickOverride/power_gloves - var/last_shocked = 0 - var/shock_delay = 40 - var/unlimited_power = FALSE // Does this really need explanation? - -/obj/item/clothing/gloves/color/yellow/power/equipped(mob/user, slot) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(slot == slot_gloves) - if(H.middleClickOverride) - old_mclick_override = H.middleClickOverride - H.middleClickOverride = mclick_override - if(!unlimited_power) - to_chat(H, "You feel electricity begin to build up in [src].") - else - to_chat(H, "You feel like you have UNLIMITED POWER!!") - -/obj/item/clothing/gloves/color/yellow/power/dropped(mob/user, slot) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(H.get_item_by_slot(slot_gloves) == src && H.middleClickOverride == mclick_override) - if(old_mclick_override) - H.middleClickOverride = old_mclick_override - old_mclick_override = null - else - H.middleClickOverride = null - -/obj/item/clothing/gloves/color/yellow/power/unlimited - name = "UNLIMITED POWER gloves" - desc = "These gloves possess UNLIMITED POWER." - shock_delay = 0 - unlimited_power = TRUE - -/obj/item/clothing/gloves/color/yellow/fake - desc = "These gloves will protect the wearer from electric shock. They don't feel like rubber..." - siemens_coefficient = 1 - -/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap - desc = "These gloves are cheap copies of the coveted gloves, no way this can end badly." - name = "budget insulated gloves" - icon_state = "yellow" - item_state = "ygloves" - siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() - permeability_coefficient = 0.05 - item_color="yellow" - resistance_flags = NONE - -/obj/item/clothing/gloves/color/fyellow/New() - ..() - siemens_coefficient = pick(0,0.5,0.5,0.5,0.5,0.75,1.5) - -/obj/item/clothing/gloves/color/fyellow/old - desc = "Old and worn out insulated gloves, hopefully they still work." - name = "worn out insulated gloves" - -/obj/item/clothing/gloves/color/fyellow/old/New() - ..() - siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) - -/obj/item/clothing/gloves/color/black - desc = "These gloves are fire-resistant." - name = "black gloves" - icon_state = "black" - item_state = "bgloves" - item_color="brown" - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT - resistance_flags = NONE - var/can_be_cut = 1 - - -/obj/item/clothing/gloves/color/black/hos - item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way. - -/obj/item/clothing/gloves/color/black/ce - item_color = "chief" //Exists for washing machines. Is not different from black gloves in any way. - -/obj/item/clothing/gloves/color/black/thief - pickpocket = 1 - -/obj/item/clothing/gloves/color/black/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/wirecutters)) - if(can_be_cut && icon_state == initial(icon_state))//only if not dyed - var/confirm = alert("Do you want to cut off the gloves fingertips? Warning: It might destroy their functionality.","Cut tips?","Yes","No") - if(get_dist(user, src) > 1) - to_chat(user, "You have moved too far away.") - return - if(confirm == "Yes") - to_chat(user, "You snip the fingertips off of [src].") - playsound(user.loc, W.usesound, rand(10,50), 1) - var/obj/item/clothing/gloves/fingerless/F = new/obj/item/clothing/gloves/fingerless(user.loc) - if(pickpocket) - F.pickpocket = FALSE - qdel(src) - return - ..() - -/obj/item/clothing/gloves/color/orange - name = "orange gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "orange" - item_state = "orangegloves" - item_color="orange" - -/obj/item/clothing/gloves/color/red - name = "red gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "red" - item_state = "redgloves" - item_color = "red" - -/obj/item/clothing/gloves/color/red/insulated - name = "insulated gloves" - desc = "These gloves will protect the wearer from electric shock." - siemens_coefficient = 0 - permeability_coefficient = 0.05 - resistance_flags = NONE - -/obj/item/clothing/gloves/color/rainbow - name = "rainbow gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "rainbow" - item_state = "rainbowgloves" - item_color = "rainbow" - -/obj/item/clothing/gloves/color/rainbow/clown - item_color = "clown" - -/obj/item/clothing/gloves/color/blue - name = "blue gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "blue" - item_state = "bluegloves" - item_color="blue" - -/obj/item/clothing/gloves/color/purple - name = "purple gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "purple" - item_state = "purplegloves" - item_color="purple" - -/obj/item/clothing/gloves/color/green - name = "green gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "green" - item_state = "greengloves" - item_color="green" - -/obj/item/clothing/gloves/color/grey - name = "grey gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "gray" - item_state = "graygloves" - item_color="grey" - -/obj/item/clothing/gloves/color/grey/rd - item_color = "director" //Exists for washing machines. Is not different from gray gloves in any way. - -/obj/item/clothing/gloves/color/grey/hop - item_color = "hop" //Exists for washing machines. Is not different from gray gloves in any way. - -/obj/item/clothing/gloves/color/light_brown - name = "light brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "lightbrown" - item_state = "lightbrowngloves" - item_color="light brown" - -/obj/item/clothing/gloves/color/brown - name = "brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "brown" - item_state = "browngloves" - item_color="brown" - -/obj/item/clothing/gloves/color/brown/cargo - item_color = "cargo" //Exists for washing machines. Is not different from brown gloves in any way. - -/obj/item/clothing/gloves/color/latex - name = "latex gloves" - desc = "Cheap sterile gloves made from latex." - icon_state = "latex" - item_state = "lgloves" - siemens_coefficient = 0.30 - permeability_coefficient = 0.01 - item_color="white" - transfer_prints = TRUE - resistance_flags = NONE - -/obj/item/clothing/gloves/color/latex/nitrile - name = "nitrile gloves" - desc = "Pricy sterile gloves that are stronger than latex." - icon_state = "nitrile" - item_state = "nitrilegloves" - transfer_prints = FALSE - item_color = "medical" - -/obj/item/clothing/gloves/color/white - name = "white gloves" - desc = "These look pretty fancy." - icon_state = "white" - item_state = "wgloves" - item_color="mime" - -/obj/item/clothing/gloves/color/white/redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from white gloves in any way. - - -/obj/item/clothing/gloves/color/captain - desc = "Regal blue gloves, with a nice gold trim. Swanky." - name = "captain's gloves" - icon_state = "captain" - item_state = "egloves" - item_color = "captain" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT - strip_delay = 60 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) \ No newline at end of file +/obj/item/clothing/gloves/color/yellow + desc = "These gloves will protect the wearer from electric shock." + name = "insulated gloves" + icon_state = "yellow" + item_state = "ygloves" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + item_color="yellow" + resistance_flags = NONE + +/obj/item/clothing/gloves/color/yellow/power + description_antag = "These are a pair of power gloves, and can be used to fire bolts of electricity while standing over powered power cables." + var/old_mclick_override + var/datum/middleClickOverride/power_gloves/mclick_override = new /datum/middleClickOverride/power_gloves + var/last_shocked = 0 + var/shock_delay = 40 + var/unlimited_power = FALSE // Does this really need explanation? + +/obj/item/clothing/gloves/color/yellow/power/equipped(mob/user, slot) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(slot == slot_gloves) + if(H.middleClickOverride) + old_mclick_override = H.middleClickOverride + H.middleClickOverride = mclick_override + if(!unlimited_power) + to_chat(H, "You feel electricity begin to build up in [src].") + else + to_chat(H, "You feel like you have UNLIMITED POWER!!") + +/obj/item/clothing/gloves/color/yellow/power/dropped(mob/user, slot) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.get_item_by_slot(slot_gloves) == src && H.middleClickOverride == mclick_override) + if(old_mclick_override) + H.middleClickOverride = old_mclick_override + old_mclick_override = null + else + H.middleClickOverride = null + +/obj/item/clothing/gloves/color/yellow/power/unlimited + name = "UNLIMITED POWER gloves" + desc = "These gloves possess UNLIMITED POWER." + shock_delay = 0 + unlimited_power = TRUE + +/obj/item/clothing/gloves/color/yellow/fake + desc = "These gloves will protect the wearer from electric shock. They don't feel like rubber..." + siemens_coefficient = 1 + +/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap + desc = "These gloves are cheap copies of the coveted gloves, no way this can end badly." + name = "budget insulated gloves" + icon_state = "yellow" + item_state = "ygloves" + siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() + permeability_coefficient = 0.05 + item_color="yellow" + resistance_flags = NONE + +/obj/item/clothing/gloves/color/fyellow/New() + ..() + siemens_coefficient = pick(0,0.5,0.5,0.5,0.5,0.75,1.5) + +/obj/item/clothing/gloves/color/fyellow/old + desc = "Old and worn out insulated gloves, hopefully they still work." + name = "worn out insulated gloves" + +/obj/item/clothing/gloves/color/fyellow/old/New() + ..() + siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) + +/obj/item/clothing/gloves/color/black + desc = "These gloves are fire-resistant." + name = "black gloves" + icon_state = "black" + item_state = "bgloves" + item_color="brown" + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = NONE + var/can_be_cut = 1 + + +/obj/item/clothing/gloves/color/black/hos + item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way. + +/obj/item/clothing/gloves/color/black/ce + item_color = "chief" //Exists for washing machines. Is not different from black gloves in any way. + +/obj/item/clothing/gloves/color/black/thief + pickpocket = 1 + +/obj/item/clothing/gloves/color/black/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/wirecutters)) + if(can_be_cut && icon_state == initial(icon_state))//only if not dyed + var/confirm = alert("Do you want to cut off the gloves fingertips? Warning: It might destroy their functionality.","Cut tips?","Yes","No") + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away.") + return + if(confirm == "Yes") + to_chat(user, "You snip the fingertips off of [src].") + playsound(user.loc, W.usesound, rand(10,50), 1) + var/obj/item/clothing/gloves/fingerless/F = new/obj/item/clothing/gloves/fingerless(user.loc) + if(pickpocket) + F.pickpocket = FALSE + qdel(src) + return + ..() + +/obj/item/clothing/gloves/color/orange + name = "orange gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "orange" + item_state = "orangegloves" + item_color="orange" + +/obj/item/clothing/gloves/color/red + name = "red gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "red" + item_state = "redgloves" + item_color = "red" + +/obj/item/clothing/gloves/color/red/insulated + name = "insulated gloves" + desc = "These gloves will protect the wearer from electric shock." + siemens_coefficient = 0 + permeability_coefficient = 0.05 + resistance_flags = NONE + +/obj/item/clothing/gloves/color/rainbow + name = "rainbow gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "rainbow" + item_state = "rainbowgloves" + item_color = "rainbow" + +/obj/item/clothing/gloves/color/rainbow/clown + item_color = "clown" + +/obj/item/clothing/gloves/color/blue + name = "blue gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "blue" + item_state = "bluegloves" + item_color="blue" + +/obj/item/clothing/gloves/color/purple + name = "purple gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "purple" + item_state = "purplegloves" + item_color="purple" + +/obj/item/clothing/gloves/color/green + name = "green gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "green" + item_state = "greengloves" + item_color="green" + +/obj/item/clothing/gloves/color/grey + name = "grey gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "gray" + item_state = "graygloves" + item_color="grey" + +/obj/item/clothing/gloves/color/grey/rd + item_color = "director" //Exists for washing machines. Is not different from gray gloves in any way. + +/obj/item/clothing/gloves/color/grey/hop + item_color = "hop" //Exists for washing machines. Is not different from gray gloves in any way. + +/obj/item/clothing/gloves/color/light_brown + name = "light brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "lightbrown" + item_state = "lightbrowngloves" + item_color="light brown" + +/obj/item/clothing/gloves/color/brown + name = "brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "brown" + item_state = "browngloves" + item_color="brown" + +/obj/item/clothing/gloves/color/brown/cargo + item_color = "cargo" //Exists for washing machines. Is not different from brown gloves in any way. + +/obj/item/clothing/gloves/color/latex + name = "latex gloves" + desc = "Cheap sterile gloves made from latex." + icon_state = "latex" + item_state = "lgloves" + siemens_coefficient = 0.30 + permeability_coefficient = 0.01 + item_color="white" + transfer_prints = TRUE + resistance_flags = NONE + +/obj/item/clothing/gloves/color/latex/nitrile + name = "nitrile gloves" + desc = "Pricy sterile gloves that are stronger than latex." + icon_state = "nitrile" + item_state = "nitrilegloves" + transfer_prints = FALSE + item_color = "medical" + +/obj/item/clothing/gloves/color/white + name = "white gloves" + desc = "These look pretty fancy." + icon_state = "white" + item_state = "wgloves" + item_color="mime" + +/obj/item/clothing/gloves/color/white/redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from white gloves in any way. + + +/obj/item/clothing/gloves/color/captain + desc = "Regal blue gloves, with a nice gold trim. Swanky." + name = "captain's gloves" + icon_state = "captain" + item_state = "egloves" + item_color = "captain" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + strip_delay = 60 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) diff --git a/code/modules/clothing/head/beanie.dm b/code/modules/clothing/head/beanie.dm index b912a07c212a..ada61c41384a 100644 --- a/code/modules/clothing/head/beanie.dm +++ b/code/modules/clothing/head/beanie.dm @@ -93,4 +93,4 @@ name = "rastacap" desc = "Perfect for tucking in those dreadlocks." icon_state = "beanierasta" - item_color = "beanierasta" \ No newline at end of file + item_color = "beanierasta" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index cc5c3248b190..501ec5624418 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -1,146 +1,146 @@ - -//Hat Station 13 - -/obj/item/clothing/head/collectable - name = "collectable hat" - desc = "A rare collectable hat." - -/obj/item/clothing/head/collectable/petehat - name = "ultra rare Pete's hat!" - desc = "It smells faintly of plasma" - icon_state = "petehat" - -/obj/item/clothing/head/collectable/slime - name = "collectable slime cap!" - desc = "It just latches right in place!" - icon_state = "slime" - -/obj/item/clothing/head/collectable/xenom - name = "collectable xenomorph helmet!" - desc = "Hiss hiss hiss!" - icon_state = "xenom" - -/obj/item/clothing/head/collectable/chef - name = "collectable chef's hat" - desc = "A rare Chef's Hat meant for hat collectors!" - icon_state = "chef" - item_state = "chef" - dog_fashion = /datum/dog_fashion/head/chef - -/obj/item/clothing/head/collectable/paper - name = "collectable paper hat" - desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." - icon_state = "paper" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/tophat - name = "collectable top hat" - desc = "A top hat worn by only the most prestigious hat collectors." - icon_state = "tophat" - item_state = "that" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/captain - name = "collectable captain's hat" - desc = "A Collectable Hat that'll make you look just like a real comdom!" - icon_state = "captain" - item_state = "caphat" - dog_fashion = /datum/dog_fashion/head/captain - -/obj/item/clothing/head/collectable/police - name = "collectable police officer's hat" - desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." - icon_state = "policehelm" - dog_fashion = /datum/dog_fashion/head/warden - -/obj/item/clothing/head/collectable/beret - name = "collectable beret" - desc = "A Collectable red Beret. It smells faintly of Garlic." - icon_state = "beret" - dog_fashion = /datum/dog_fashion/head/beret - -/obj/item/clothing/head/collectable/welding - name = "collectable welding helmet" - desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" - icon_state = "welding" - item_state = "welding" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi' - ) - -/obj/item/clothing/head/collectable/slime - name = "collectable slime hat" - desc = "Just like a real Brain Slug!" - icon_state = "headslime" - item_state = "headslime" - -/obj/item/clothing/head/collectable/flatcap - name = "collectable flat cap" - desc = "A Collectible farmer's Flat Cap!" - icon_state = "flat_cap" - item_state = "detective" - -/obj/item/clothing/head/collectable/pirate - name = "collectable pirate hat" - desc = "You'd make a great Dread Syndie Roberts!" - icon_state = "pirate" - item_state = "pirate" - dog_fashion = /datum/dog_fashion/head/pirate - -/obj/item/clothing/head/collectable/kitty - name = "collectable kitty ears" - desc = "The fur feels.....a bit too realistic." - icon_state = "kitty" - item_state = "kitty" - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/collectable/rabbitears - name = "collectable rabbit ears" - desc = "Not as lucky as the feet!" - icon_state = "bunny" - item_state = "bunny" - dog_fashion = /datum/dog_fashion/head/rabbit - -/obj/item/clothing/head/collectable/wizard - name = "collectable wizard's hat" - desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." - icon_state = "wizard" - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/collectable/hardhat - name = "collectable hard hat" - desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" - icon_state = "hardhat0_yellow" - item_state = "hardhat0_yellow" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/HoS - name = "collectable HoS hat" - desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" - icon_state = "hoscap" - -/obj/item/clothing/head/collectable/HoP - name = "collectable HoP hat" - desc = "It's your turn to demand excessive paperwork, signatures, stamps, and hire more clowns! Papers, please!" - icon_state = "hopcap" - dog_fashion = /datum/dog_fashion/head/hop - -/obj/item/clothing/head/collectable/thunderdome - name = "collectable Thunderdome helmet" - desc = "Go Red! I mean Green! I mean Red! No Green!" - icon_state = "thunderdome" - item_state = "thunderdome" - -/obj/item/clothing/head/collectable/swat - name = "collectable SWAT helmet" - desc = "Now you can be in the Deathsquad too!" - icon_state = "swat" - item_state = "swat" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi' - ) + +//Hat Station 13 + +/obj/item/clothing/head/collectable + name = "collectable hat" + desc = "A rare collectable hat." + +/obj/item/clothing/head/collectable/petehat + name = "ultra rare Pete's hat!" + desc = "It smells faintly of plasma" + icon_state = "petehat" + +/obj/item/clothing/head/collectable/slime + name = "collectable slime cap!" + desc = "It just latches right in place!" + icon_state = "slime" + +/obj/item/clothing/head/collectable/xenom + name = "collectable xenomorph helmet!" + desc = "Hiss hiss hiss!" + icon_state = "xenom" + +/obj/item/clothing/head/collectable/chef + name = "collectable chef's hat" + desc = "A rare Chef's Hat meant for hat collectors!" + icon_state = "chef" + item_state = "chef" + dog_fashion = /datum/dog_fashion/head/chef + +/obj/item/clothing/head/collectable/paper + name = "collectable paper hat" + desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." + icon_state = "paper" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/tophat + name = "collectable top hat" + desc = "A top hat worn by only the most prestigious hat collectors." + icon_state = "tophat" + item_state = "that" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/captain + name = "collectable captain's hat" + desc = "A Collectable Hat that'll make you look just like a real comdom!" + icon_state = "captain" + item_state = "caphat" + dog_fashion = /datum/dog_fashion/head/captain + +/obj/item/clothing/head/collectable/police + name = "collectable police officer's hat" + desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." + icon_state = "policehelm" + dog_fashion = /datum/dog_fashion/head/warden + +/obj/item/clothing/head/collectable/beret + name = "collectable beret" + desc = "A Collectable red Beret. It smells faintly of Garlic." + icon_state = "beret" + dog_fashion = /datum/dog_fashion/head/beret + +/obj/item/clothing/head/collectable/welding + name = "collectable welding helmet" + desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" + icon_state = "welding" + item_state = "welding" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi' + ) + +/obj/item/clothing/head/collectable/slime + name = "collectable slime hat" + desc = "Just like a real Brain Slug!" + icon_state = "headslime" + item_state = "headslime" + +/obj/item/clothing/head/collectable/flatcap + name = "collectable flat cap" + desc = "A Collectible farmer's Flat Cap!" + icon_state = "flat_cap" + item_state = "detective" + +/obj/item/clothing/head/collectable/pirate + name = "collectable pirate hat" + desc = "You'd make a great Dread Syndie Roberts!" + icon_state = "pirate" + item_state = "pirate" + dog_fashion = /datum/dog_fashion/head/pirate + +/obj/item/clothing/head/collectable/kitty + name = "collectable kitty ears" + desc = "The fur feels.....a bit too realistic." + icon_state = "kitty" + item_state = "kitty" + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/collectable/rabbitears + name = "collectable rabbit ears" + desc = "Not as lucky as the feet!" + icon_state = "bunny" + item_state = "bunny" + dog_fashion = /datum/dog_fashion/head/rabbit + +/obj/item/clothing/head/collectable/wizard + name = "collectable wizard's hat" + desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." + icon_state = "wizard" + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/collectable/hardhat + name = "collectable hard hat" + desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" + icon_state = "hardhat0_yellow" + item_state = "hardhat0_yellow" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/HoS + name = "collectable HoS hat" + desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" + icon_state = "hoscap" + +/obj/item/clothing/head/collectable/HoP + name = "collectable HoP hat" + desc = "It's your turn to demand excessive paperwork, signatures, stamps, and hire more clowns! Papers, please!" + icon_state = "hopcap" + dog_fashion = /datum/dog_fashion/head/hop + +/obj/item/clothing/head/collectable/thunderdome + name = "collectable Thunderdome helmet" + desc = "Go Red! I mean Green! I mean Red! No Green!" + icon_state = "thunderdome" + item_state = "thunderdome" + +/obj/item/clothing/head/collectable/swat + name = "collectable SWAT helmet" + desc = "Now you can be in the Deathsquad too!" + icon_state = "swat" + item_state = "swat" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi' + ) diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 18b92433f83a..b5d4b94038f5 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -101,4 +101,4 @@ dog_fashion = null sprite_sheets = list( "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index d47e3c323650..2a9abf461c9c 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -1,301 +1,301 @@ -/obj/item/clothing/head/helmet - name = "helmet" - desc = "Standard Security gear. Protects the head from impacts." - icon_state = "helmetmaterials" - flags = HEADBANGPROTECT - flags_cover = HEADCOVERSEYES - item_state = "helmetmaterials" - armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - flags_inv = HIDEEARS|HIDEEYES - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/helmet - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/attack_self(mob/user) - if(can_toggle && !user.incapacitated()) - if(world.time > cooldown + toggle_cooldown) - cooldown = world.time - up = !up - flags ^= visor_flags - flags_inv ^= visor_flags_inv - icon_state = "[initial(icon_state)][up ? "up" : ""]" - to_chat(user, "[up ? alt_toggle_message : toggle_message] \the [src]") - - user.update_inv_head() - - if(active_sound) - while(up) - playsound(src.loc, "[active_sound]", 100, 0, 4) - sleep(15) - if(toggle_sound) - playsound(src.loc, "[toggle_sound]", 100, 0, 4) - - -/obj/item/clothing/head/helmet/visor - name = "visor helmet" - desc = "A helmet with a built-in visor. It doesn't seem to do anything, but it sure looks cool!" - icon_state = "helmetgoggles" - -/obj/item/clothing/head/helmet/thermal - name = "thermal visor helmet" - desc = "A helmet with a built-in thermal scanning visor." - icon_state = "helmetthermals" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/item/clothing/head/helmet/meson - name = "meson visor helmet" - desc = "A helmet with a built-in meson scanning visor." - icon_state = "helmetmesons" - vision_flags = SEE_TURFS - -/obj/item/clothing/head/helmet/material - name = "material visor helmet" - desc = "A helmet with a built-in material scanning visor." - icon_state = "helmetmaterials" - vision_flags = SEE_OBJS - -/obj/item/clothing/head/helmet/night - name = "night-vision helmet" - desc = "A helmet with a built-in pair of night vision goggles." - icon_state = "helmetNVG" - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - -/obj/item/clothing/head/helmet/alt - name = "bulletproof helmet" - desc = "A bulletproof helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "swat" - item_state = "swat-alt" - armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - dog_fashion = null - -/obj/item/clothing/head/helmet/riot - name = "riot helmet" - desc = "It's a helmet specifically designed to protect against close range attacks." - icon_state = "riot" - item_state = "helmet" - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - flags_inv = HIDEEARS - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/riot/knight - name = "medieval helmet" - desc = "A classic metal helmet." - icon_state = "knight_green" - item_state = "knight_green" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - dog_fashion = null - -/obj/item/clothing/head/helmet/justice - name = "helmet of justice" - desc = "WEEEEOOO. WEEEEEOOO. WEEEEOOOO." - icon_state = "justice" - toggle_message = "You turn off the lights on" - alt_toggle_message = "You turn on the lights on" - actions_types = list(/datum/action/item_action/toggle_helmet_light) - can_toggle = 1 - toggle_cooldown = 20 - active_sound = 'sound/items/weeoo1.ogg' - dog_fashion = null - -/obj/item/clothing/head/helmet/justice/escape - name = "alarm helmet" - desc = "WEEEEOOO. WEEEEEOOO. STOP THAT MONKEY. WEEEOOOO." - icon_state = "justice2" - toggle_message = "You turn off the light on" - alt_toggle_message = "You turn on the light on" - - -/obj/item/clothing/head/helmet/swat - name = "\improper SWAT helmet" - desc = "They're often used by highly trained Swat Members." - icon_state = "swat" - item_state = "swat" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) - flags = null - flags_inv = HIDEEARS|HIDEEYES - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/swat/syndicate - name = "blood-red helmet" - desc = "An extremely robust, space-worthy helmet without a visor to allow for goggle usage underneath. Property of Gorlex Marauders." - icon_state = "helmetsyndi" - item_state = "helmet" - -/obj/item/clothing/head/helmet/thunderdome - name = "\improper Thunderdome helmet" - desc = "'Let the battle commence!'" - icon_state = "thunderdome" - flags = null - item_state = "thunderdome" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/roman - name = "roman helmet" - desc = "An ancient helmet made of bronze and leather." - flags = null - armor = list("melee" = 25, "bullet" = 0, "laser" = 25, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - icon_state = "roman" - item_state = "roman" - strip_delay = 100 - dog_fashion = null - -/obj/item/clothing/head/helmet/roman/fake - desc = "An ancient helmet made of plastic and leather." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/head/helmet/roman/legionaire - name = "roman legionaire helmet" - desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." - icon_state = "roman_c" - item_state = "roman_c" - -/obj/item/clothing/head/helmet/roman/legionaire/fake - desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/head/helmet/gladiator - name = "gladiator helmet" - desc = "Ave, Imperator, morituri te salutant." - icon_state = "gladiator" - flags = BLOCKHAIR - item_state = "gladiator" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - toggle_message = "You attach the face shield to the" - alt_toggle_message = "You remove the face shield from the" - actions_types = list(/datum/action/item_action/toggle_helmet_mode) - can_toggle = 1 - toggle_cooldown = 20 - toggle_sound = 'sound/items/zippoclose.ogg' - dog_fashion = null - -obj/item/clothing/head/helmet/redtaghelm - name = "red laser tag helmet" - desc = "They have chosen their own end." - icon_state = "redtaghelm" - flags = null - item_state = "redtaghelm" - armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - // Offer about the same protection as a hardhat. - flags_inv = HIDEEARS|HIDEEYES - dog_fashion = null - -obj/item/clothing/head/helmet/bluetaghelm - name = "blue laser tag helmet" - desc = "They'll need more men." - icon_state = "bluetaghelm" - flags = null - item_state = "bluetaghelm" - armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - // Offer about the same protection as a hardhat. - flags_inv = HIDEEARS|HIDEEYES - dog_fashion = null - -obj/item/clothing/head/blob - name = "blob hat" - desc = "A collectible hat handed out at the latest Blob Family Reunion." - icon_state = "blobhat" - item_state = "blobhat" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/head/helmet/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/head/helmet/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/head/helmet/riot/knight/templar - name = "crusader helmet" - desc = "Deus Vult." - icon_state = "knight_templar" - item_state = "knight_templar" - armor = list(melee = 20, bullet = 7, laser = 2, energy = 2, bomb = 2, bio = 2, rad = 0, fire = 80, acid = 80) - -/obj/item/clothing/head/helmet/skull - name = "skull helmet" - desc = "An intimidating tribal helmet, it doesn't look very comfortable." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - icon_state = "skull" - item_state = "skull" - strip_delay = 100 - -/obj/item/clothing/head/helmet/durathread - name = "durathread helmet" - desc = "A helmet made from durathread and leather." - icon_state = "durathread" - item_state = "durathread" - resistance_flags = FLAMMABLE - armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) - strip_delay = 60 - -//Commander -/obj/item/clothing/head/helmet/ert/command - name = "emergency response team commander helmet" - desc = "An in-atmosphere helmet worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." - icon_state = "erthelmet_cmd" - -//Security -/obj/item/clothing/head/helmet/ert/security - name = "emergency response team security helmet" - desc = "An in-atmosphere helmet worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." - icon_state = "erthelmet_sec" - -/obj/item/clothing/head/helmet/ert/security/paranormal - name = "paranormal emergency response team helmet" - desc = "An in-atmosphere helmet worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." - icon_state = "knight_templar" - item_state = "knight_templar" - -//Engineer -/obj/item/clothing/head/helmet/ert/engineer - name = "emergency response team engineer helmet" - desc = "An in-atmosphere helmet worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." - icon_state = "erthelmet_eng" - -//Medical -/obj/item/clothing/head/helmet/ert/medical - name = "emergency response team medical helmet" - desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_med" - -//Janitorial -/obj/item/clothing/head/helmet/ert/janitor - name = "emergency response team janitor helmet" - desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_jan" +/obj/item/clothing/head/helmet + name = "helmet" + desc = "Standard Security gear. Protects the head from impacts." + icon_state = "helmetmaterials" + flags = HEADBANGPROTECT + flags_cover = HEADCOVERSEYES + item_state = "helmetmaterials" + armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + flags_inv = HIDEEARS|HIDEEYES + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/helmet + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/attack_self(mob/user) + if(can_toggle && !user.incapacitated()) + if(world.time > cooldown + toggle_cooldown) + cooldown = world.time + up = !up + flags ^= visor_flags + flags_inv ^= visor_flags_inv + icon_state = "[initial(icon_state)][up ? "up" : ""]" + to_chat(user, "[up ? alt_toggle_message : toggle_message] \the [src]") + + user.update_inv_head() + + if(active_sound) + while(up) + playsound(src.loc, "[active_sound]", 100, 0, 4) + sleep(15) + if(toggle_sound) + playsound(src.loc, "[toggle_sound]", 100, 0, 4) + + +/obj/item/clothing/head/helmet/visor + name = "visor helmet" + desc = "A helmet with a built-in visor. It doesn't seem to do anything, but it sure looks cool!" + icon_state = "helmetgoggles" + +/obj/item/clothing/head/helmet/thermal + name = "thermal visor helmet" + desc = "A helmet with a built-in thermal scanning visor." + icon_state = "helmetthermals" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/item/clothing/head/helmet/meson + name = "meson visor helmet" + desc = "A helmet with a built-in meson scanning visor." + icon_state = "helmetmesons" + vision_flags = SEE_TURFS + +/obj/item/clothing/head/helmet/material + name = "material visor helmet" + desc = "A helmet with a built-in material scanning visor." + icon_state = "helmetmaterials" + vision_flags = SEE_OBJS + +/obj/item/clothing/head/helmet/night + name = "night-vision helmet" + desc = "A helmet with a built-in pair of night vision goggles." + icon_state = "helmetNVG" + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + +/obj/item/clothing/head/helmet/alt + name = "bulletproof helmet" + desc = "A bulletproof helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "swat" + item_state = "swat-alt" + armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + dog_fashion = null + +/obj/item/clothing/head/helmet/riot + name = "riot helmet" + desc = "It's a helmet specifically designed to protect against close range attacks." + icon_state = "riot" + item_state = "helmet" + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + flags_inv = HIDEEARS + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/riot/knight + name = "medieval helmet" + desc = "A classic metal helmet." + icon_state = "knight_green" + item_state = "knight_green" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + dog_fashion = null + +/obj/item/clothing/head/helmet/justice + name = "helmet of justice" + desc = "WEEEEOOO. WEEEEEOOO. WEEEEOOOO." + icon_state = "justice" + toggle_message = "You turn off the lights on" + alt_toggle_message = "You turn on the lights on" + actions_types = list(/datum/action/item_action/toggle_helmet_light) + can_toggle = 1 + toggle_cooldown = 20 + active_sound = 'sound/items/weeoo1.ogg' + dog_fashion = null + +/obj/item/clothing/head/helmet/justice/escape + name = "alarm helmet" + desc = "WEEEEOOO. WEEEEEOOO. STOP THAT MONKEY. WEEEOOOO." + icon_state = "justice2" + toggle_message = "You turn off the light on" + alt_toggle_message = "You turn on the light on" + + +/obj/item/clothing/head/helmet/swat + name = "\improper SWAT helmet" + desc = "They're often used by highly trained Swat Members." + icon_state = "swat" + item_state = "swat" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) + flags = null + flags_inv = HIDEEARS|HIDEEYES + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/swat/syndicate + name = "blood-red helmet" + desc = "An extremely robust, space-worthy helmet without a visor to allow for goggle usage underneath. Property of Gorlex Marauders." + icon_state = "helmetsyndi" + item_state = "helmet" + +/obj/item/clothing/head/helmet/thunderdome + name = "\improper Thunderdome helmet" + desc = "'Let the battle commence!'" + icon_state = "thunderdome" + flags = null + item_state = "thunderdome" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/roman + name = "roman helmet" + desc = "An ancient helmet made of bronze and leather." + flags = null + armor = list("melee" = 25, "bullet" = 0, "laser" = 25, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + icon_state = "roman" + item_state = "roman" + strip_delay = 100 + dog_fashion = null + +/obj/item/clothing/head/helmet/roman/fake + desc = "An ancient helmet made of plastic and leather." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/head/helmet/roman/legionaire + name = "roman legionaire helmet" + desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." + icon_state = "roman_c" + item_state = "roman_c" + +/obj/item/clothing/head/helmet/roman/legionaire/fake + desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/head/helmet/gladiator + name = "gladiator helmet" + desc = "Ave, Imperator, morituri te salutant." + icon_state = "gladiator" + flags = BLOCKHAIR + item_state = "gladiator" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + toggle_message = "You attach the face shield to the" + alt_toggle_message = "You remove the face shield from the" + actions_types = list(/datum/action/item_action/toggle_helmet_mode) + can_toggle = 1 + toggle_cooldown = 20 + toggle_sound = 'sound/items/zippoclose.ogg' + dog_fashion = null + +obj/item/clothing/head/helmet/redtaghelm + name = "red laser tag helmet" + desc = "They have chosen their own end." + icon_state = "redtaghelm" + flags = null + item_state = "redtaghelm" + armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) + // Offer about the same protection as a hardhat. + flags_inv = HIDEEARS|HIDEEYES + dog_fashion = null + +obj/item/clothing/head/helmet/bluetaghelm + name = "blue laser tag helmet" + desc = "They'll need more men." + icon_state = "bluetaghelm" + flags = null + item_state = "bluetaghelm" + armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) + // Offer about the same protection as a hardhat. + flags_inv = HIDEEARS|HIDEEYES + dog_fashion = null + +obj/item/clothing/head/blob + name = "blob hat" + desc = "A collectible hat handed out at the latest Blob Family Reunion." + icon_state = "blobhat" + item_state = "blobhat" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/head/helmet/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/head/helmet/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/head/helmet/riot/knight/templar + name = "crusader helmet" + desc = "Deus Vult." + icon_state = "knight_templar" + item_state = "knight_templar" + armor = list(melee = 20, bullet = 7, laser = 2, energy = 2, bomb = 2, bio = 2, rad = 0, fire = 80, acid = 80) + +/obj/item/clothing/head/helmet/skull + name = "skull helmet" + desc = "An intimidating tribal helmet, it doesn't look very comfortable." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + icon_state = "skull" + item_state = "skull" + strip_delay = 100 + +/obj/item/clothing/head/helmet/durathread + name = "durathread helmet" + desc = "A helmet made from durathread and leather." + icon_state = "durathread" + item_state = "durathread" + resistance_flags = FLAMMABLE + armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) + strip_delay = 60 + +//Commander +/obj/item/clothing/head/helmet/ert/command + name = "emergency response team commander helmet" + desc = "An in-atmosphere helmet worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." + icon_state = "erthelmet_cmd" + +//Security +/obj/item/clothing/head/helmet/ert/security + name = "emergency response team security helmet" + desc = "An in-atmosphere helmet worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." + icon_state = "erthelmet_sec" + +/obj/item/clothing/head/helmet/ert/security/paranormal + name = "paranormal emergency response team helmet" + desc = "An in-atmosphere helmet worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." + icon_state = "knight_templar" + item_state = "knight_templar" + +//Engineer +/obj/item/clothing/head/helmet/ert/engineer + name = "emergency response team engineer helmet" + desc = "An in-atmosphere helmet worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." + icon_state = "erthelmet_eng" + +//Medical +/obj/item/clothing/head/helmet/ert/medical + name = "emergency response team medical helmet" + desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_med" + +//Janitorial +/obj/item/clothing/head/helmet/ert/janitor + name = "emergency response team janitor helmet" + desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_jan" diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index e3ce5a62cd37..993f2050b519 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -1,190 +1,190 @@ - -//Bartender -/obj/item/clothing/head/chefhat - name = "chef's hat" - desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." - icon_state = "chef" - item_state = "chef" - desc = "The commander in chef's head wear." - strip_delay = 10 - put_on_delay = 10 - dog_fashion = /datum/dog_fashion/head/chef - -//Captain -/obj/item/clothing/head/caphat - name = "captain's hat" - icon_state = "captain" - desc = "It's good being the king." - item_state = "caphat" - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/captain - -//Captain: no longer space-worthy -/obj/item/clothing/head/caphat/parade - name = "captain's parade cap" - desc = "Worn only by Captains with an abundance of class." - icon_state = "capcap" - dog_fashion = null - -//Head of Personnel -/obj/item/clothing/head/hopcap - name = "head of personnel's cap" - icon_state = "hopcap" - desc = "The symbol of true bureaucratic micromanagement." - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - dog_fashion = /datum/dog_fashion/head/hop - -//Nanotrasen Representative -/obj/item/clothing/head/ntrep - name = "Nanotrasen Representative hat" - desc = "A cap issued to the Nanotrasen Representative" - icon_state = "ntrep" - - -//Chaplain -/obj/item/clothing/head/hooded/chaplain_hood - name = "chaplain's hood" - desc = "It's hood that covers the head. It keeps you warm during the space winters." - icon_state = "chaplain_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -//Chaplain -/obj/item/clothing/head/hooded/nun_hood - name = "nun hood" - desc = "Maximum piety in this star system." - icon_state = "nun_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -//Chaplain -/obj/item/clothing/head/hooded/monk_hood - name = "monk hood" - desc = "Wooden board not included." - icon_state = "monk_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/witchhunter_hat - name = "witchhunter hat" - desc = "This hat saw much use back in the day." - icon_state = "witchhunterhat" - item_state = "witchhunterhat" - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/det_hat - name = "hat" - desc = "Someone who wears this will look very smart." - icon_state = "detective" - allowed = list(/obj/item/reagent_containers/food/snacks/candy/candy_corn, /obj/item/pen) - armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) - dog_fashion = /datum/dog_fashion/head/detective - -//Mime -/obj/item/clothing/head/beret - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret" - dog_fashion = /datum/dog_fashion/head/beret - -/obj/item/clothing/head/beret/durathread - name = "durathread beret" - desc = "A beret made from durathread, its resilient fibres provide some protection to the wearer." - icon_state = "beretdurathread" - item_color = null - armor = list("melee" = 15, "bullet" = 5, "laser" = 15, "energy" = 5, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 5) - -//Security -/obj/item/clothing/head/HoS - name = "head of security cap" - desc = "The robust standard-issue cap of the Head of Security. For showing the officers who's in charge." - icon_state = "hoscap" - armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60) - strip_delay = 80 - -/obj/item/clothing/head/HoS/beret - name = "head of security beret" - desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." - icon_state = "beret_hos_black" - -/obj/item/clothing/head/warden - name = "warden's police hat" - desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." - icon_state = "policehelm" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 60) - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/warden - -/obj/item/clothing/head/officer - name = "officer's cap" - desc = "A red cap with an old-fashioned badge on the front for establishing that you are, in fact, the law." - icon_state = "customshelm" - item_state = "customshelm" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/sec - name = "security beret" - desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." - icon_state = "beret_officer" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - dog_fashion = null - -/obj/item/clothing/head/beret/sec/warden - name = "warden's beret" - desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." - icon_state = "beret_warden" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) - -/obj/item/clothing/head/beret/eng - name = "engineering beret" - desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." - icon_state = "beret_engineering" - -/obj/item/clothing/head/beret/atmos - name = "atmospherics beret" - desc = "A beret for those who have shown immaculate proficienty in piping. Or plumbing." - icon_state = "beret_atmospherics" - -/obj/item/clothing/head/beret/ce - name = "chief engineer beret" - desc = "A white beret with the engineering insignia emblazoned on it. Its owner knows what they're doing. Probably." - icon_state = "beret_ce" - -/obj/item/clothing/head/beret/sci - name = "science beret" - desc = "A purple beret with the science insignia emblazoned on it. It has that authentic burning plasma smell." - icon_state = "beret_sci" - -//Medical -/obj/item/clothing/head/beret/med - name = "medical beret" - desc = "A white beret with a green cross finely threaded into it. It has that sterile smell about it." - icon_state = "beret_med" - -/obj/item/clothing/head/surgery - name = "surgical cap" - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." - icon_state = "surgcap_blue" - flags = BLOCKHEADHAIR - sprite_sheets = list( - "Drask" = 'icons/mob/species/drask/head.dmi' - ) - -/obj/item/clothing/head/surgery/purple - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." - icon_state = "surgcap_purple" - -/obj/item/clothing/head/surgery/blue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." - icon_state = "surgcap_blue" - -/obj/item/clothing/head/surgery/green - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." - icon_state = "surgcap_green" - -/obj/item/clothing/head/surgery/black - desc = "A cap coroners wear during autopsies. Keeps their hair from falling into the cadavers. It is as dark than the coroner's humor." - icon_state = "surgcap_black" + +//Bartender +/obj/item/clothing/head/chefhat + name = "chef's hat" + desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." + icon_state = "chef" + item_state = "chef" + desc = "The commander in chef's head wear." + strip_delay = 10 + put_on_delay = 10 + dog_fashion = /datum/dog_fashion/head/chef + +//Captain +/obj/item/clothing/head/caphat + name = "captain's hat" + icon_state = "captain" + desc = "It's good being the king." + item_state = "caphat" + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/captain + +//Captain: no longer space-worthy +/obj/item/clothing/head/caphat/parade + name = "captain's parade cap" + desc = "Worn only by Captains with an abundance of class." + icon_state = "capcap" + dog_fashion = null + +//Head of Personnel +/obj/item/clothing/head/hopcap + name = "head of personnel's cap" + icon_state = "hopcap" + desc = "The symbol of true bureaucratic micromanagement." + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + dog_fashion = /datum/dog_fashion/head/hop + +//Nanotrasen Representative +/obj/item/clothing/head/ntrep + name = "Nanotrasen Representative hat" + desc = "A cap issued to the Nanotrasen Representative" + icon_state = "ntrep" + + +//Chaplain +/obj/item/clothing/head/hooded/chaplain_hood + name = "chaplain's hood" + desc = "It's hood that covers the head. It keeps you warm during the space winters." + icon_state = "chaplain_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +//Chaplain +/obj/item/clothing/head/hooded/nun_hood + name = "nun hood" + desc = "Maximum piety in this star system." + icon_state = "nun_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +//Chaplain +/obj/item/clothing/head/hooded/monk_hood + name = "monk hood" + desc = "Wooden board not included." + icon_state = "monk_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/witchhunter_hat + name = "witchhunter hat" + desc = "This hat saw much use back in the day." + icon_state = "witchhunterhat" + item_state = "witchhunterhat" + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/det_hat + name = "hat" + desc = "Someone who wears this will look very smart." + icon_state = "detective" + allowed = list(/obj/item/reagent_containers/food/snacks/candy/candy_corn, /obj/item/pen) + armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + dog_fashion = /datum/dog_fashion/head/detective + +//Mime +/obj/item/clothing/head/beret + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret" + dog_fashion = /datum/dog_fashion/head/beret + +/obj/item/clothing/head/beret/durathread + name = "durathread beret" + desc = "A beret made from durathread, its resilient fibres provide some protection to the wearer." + icon_state = "beretdurathread" + item_color = null + armor = list("melee" = 15, "bullet" = 5, "laser" = 15, "energy" = 5, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 5) + +//Security +/obj/item/clothing/head/HoS + name = "head of security cap" + desc = "The robust standard-issue cap of the Head of Security. For showing the officers who's in charge." + icon_state = "hoscap" + armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60) + strip_delay = 80 + +/obj/item/clothing/head/HoS/beret + name = "head of security beret" + desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." + icon_state = "beret_hos_black" + +/obj/item/clothing/head/warden + name = "warden's police hat" + desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." + icon_state = "policehelm" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 60) + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/warden + +/obj/item/clothing/head/officer + name = "officer's cap" + desc = "A red cap with an old-fashioned badge on the front for establishing that you are, in fact, the law." + icon_state = "customshelm" + item_state = "customshelm" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/sec + name = "security beret" + desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." + icon_state = "beret_officer" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + dog_fashion = null + +/obj/item/clothing/head/beret/sec/warden + name = "warden's beret" + desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." + icon_state = "beret_warden" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + +/obj/item/clothing/head/beret/eng + name = "engineering beret" + desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." + icon_state = "beret_engineering" + +/obj/item/clothing/head/beret/atmos + name = "atmospherics beret" + desc = "A beret for those who have shown immaculate proficienty in piping. Or plumbing." + icon_state = "beret_atmospherics" + +/obj/item/clothing/head/beret/ce + name = "chief engineer beret" + desc = "A white beret with the engineering insignia emblazoned on it. Its owner knows what they're doing. Probably." + icon_state = "beret_ce" + +/obj/item/clothing/head/beret/sci + name = "science beret" + desc = "A purple beret with the science insignia emblazoned on it. It has that authentic burning plasma smell." + icon_state = "beret_sci" + +//Medical +/obj/item/clothing/head/beret/med + name = "medical beret" + desc = "A white beret with a green cross finely threaded into it. It has that sterile smell about it." + icon_state = "beret_med" + +/obj/item/clothing/head/surgery + name = "surgical cap" + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." + icon_state = "surgcap_blue" + flags = BLOCKHEADHAIR + sprite_sheets = list( + "Drask" = 'icons/mob/species/drask/head.dmi' + ) + +/obj/item/clothing/head/surgery/purple + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." + icon_state = "surgcap_purple" + +/obj/item/clothing/head/surgery/blue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." + icon_state = "surgcap_blue" + +/obj/item/clothing/head/surgery/green + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." + icon_state = "surgcap_green" + +/obj/item/clothing/head/surgery/black + desc = "A cap coroners wear during autopsies. Keeps their hair from falling into the cadavers. It is as dark than the coroner's humor." + icon_state = "surgcap_black" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index ef49855dbec0..cac07fe5b1d0 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -1,493 +1,493 @@ - - -/obj/item/clothing/head/centhat - name = "\improper CentComm. hat" - icon_state = "centcom" - desc = "It's good to be emperor." - item_state = "centhat" - armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 80 - -/obj/item/clothing/head/hairflower - name = "hair flower pin" - icon_state = "hairflower" - desc = "Smells nice." - item_state = "hairflower" - -/obj/item/clothing/head/powdered_wig - name = "powdered wig" - desc = "A powdered wig." - icon_state = "pwig" - item_state = "pwig" - -/obj/item/clothing/head/justice_wig - name = "Justice wig" - desc = "A fancy powdered wig given to arbitrators of the law. It looks itchy." - icon_state = "jwig" - item_state = "jwig" - -/obj/item/clothing/head/beret/blue - icon_state = "beret_blue" - -/obj/item/clothing/head/beret/black - icon_state = "beret_black" - -/obj/item/clothing/head/beret/purple_normal - icon_state = "beret_purple_normal" - -/obj/item/clothing/head/that - name = "top-hat" - desc = "It's an amish looking hat." - icon_state = "tophat" - item_state = "that" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/redcoat - name = "redcoat's hat" - icon_state = "redcoat" - desc = "'I guess it's a redhead.'" - -/obj/item/clothing/head/mailman - name = "mailman's hat" - icon_state = "mailman" - desc = "'Right-on-time' mail service head wear." - -/obj/item/clothing/head/plaguedoctorhat - name = "plague doctor's hat" - desc = "These were once used by Plague doctors. They're pretty much useless." - icon_state = "plaguedoctor" - permeability_coefficient = 0.01 - -/obj/item/clothing/head/hasturhood - name = "hastur's hood" - desc = "It's unspeakably stylish" - icon_state = "hasturhood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/nursehat - name = "nurse's hat" - desc = "It allows quick identification of trained medical personnel." - icon_state = "nursehat" - dog_fashion = /datum/dog_fashion/head/nurse - -/obj/item/clothing/head/syndicatefake - name = "black and red space-helmet replica" - icon_state = "syndicate-helm-black-red" - item_state = "syndicate-helm-black-red" - desc = "A plastic replica of a syndicate agent's space helmet, you'll look just like a real murderous syndicate agent in this! This is a toy, it is not made for use in space!" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - - -/obj/item/clothing/head/cueball - name = "cueball helmet" - desc = "A large, featureless white orb meant to be worn on your head. How do you even see out of this thing?" - icon_state = "cueball" - item_state = "cueball" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/snowman - name = "snowman head" - desc = "A ball of white styrofoam. So festive." - icon_state = "snowman_h" - item_state = "snowman_h" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/that - name = "sturdy top-hat" - desc = "It's an amish looking armored top hat." - icon_state = "tophat" - item_state = "that" - flags_inv = 0 - - -/obj/item/clothing/head/greenbandana - name = "green bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "greenbandana" - item_state = "greenbandana" - flags_inv = 0 - -/obj/item/clothing/head/justice - name = "justice hat" - desc = "Fight for what's righteous!" - icon_state = "justicered" - item_state = "justicered" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - -/obj/item/clothing/head/justice/blue - icon_state = "justiceblue" - item_state = "justiceblue" - -/obj/item/clothing/head/justice/yellow - icon_state = "justiceyellow" - item_state = "justiceyellow" - -/obj/item/clothing/head/justice/green - icon_state = "justicegreen" - item_state = "justicegreen" - -/obj/item/clothing/head/justice/pink - icon_state = "justicepink" - item_state = "justicepink" - -/obj/item/clothing/head/rabbitears - name = "rabbit ears" - desc = "Wearing these makes you look useless, and only good for your sex appeal." - icon_state = "bunny" - dog_fashion = /datum/dog_fashion/head/rabbit - -/obj/item/clothing/head/flatcap - name = "flat cap" - desc = "A working man's cap." - icon_state = "flat_cap" - item_state = "detective" - -/obj/item/clothing/head/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - dog_fashion = /datum/dog_fashion/head/pirate - -/obj/item/clothing/head/hgpiratecap - name = "pirate hat" - desc = "Yarr." - icon_state = "hgpiratecap" - item_state = "hgpiratecap" - -/obj/item/clothing/head/bandana - name = "pirate bandana" - desc = "Yarr." - icon_state = "bandana" - item_state = "bandana" - -//stylish bs12 hats - -/obj/item/clothing/head/bowlerhat - name = "bowler hat" - icon_state = "bowler_hat" - item_state = "bowler_hat" - desc = "For that industrial age look." - -/obj/item/clothing/head/beaverhat - name = "beaver hat" - icon_state = "beaver_hat" - item_state = "beaver_hat" - desc = "Like a top hat, but made of beavers." - -/obj/item/clothing/head/boaterhat - name = "boater hat" - icon_state = "boater_hat" - item_state = "boater_hat" - desc = "Goes well with celery." - -/obj/item/clothing/head/cowboyhat - name = "cowboy hat" - icon_state = "cowboyhat" - item_state = "cowboyhat" - desc = "For the Rancher in us all." - -/obj/item/clothing/head/cowboyhat/tan - name = "tan cowboy hat" - icon_state = "cowboyhat_tan" - item_state = "cowboyhat_tan" - desc = "There's a new sheriff in town. Pass the whiskey." - -/obj/item/clothing/head/cowboyhat/black - name = "black cowboy hat" - icon_state = "cowboyhat_black" - item_state = "cowboyhat_black" - desc = "This station ain't big enough for the two ah' us." - -/obj/item/clothing/head/cowboyhat/white - name = "white cowboy hat" - icon_state = "cowboyhat_white" - item_state = "cowboyhat_white" - desc = "Authentic Marshall hair case. Now ya can protect this here homestead. Navy Model not included." - -/obj/item/clothing/head/cowboyhat/pink - name = "cowgirl hat" - icon_state = "cowboyhat_pink" - item_state = "cowboyhat_pink" - desc = "For those buckle bunnies wanta' become a real buckaroo." - -/obj/item/clothing/head/fedora - name = "fedora" - icon_state = "fedora" - item_state = "fedora" - desc = "A great hat ruined by being within fifty yards of you." - actions_types = list(/datum/action/item_action/tip_fedora) - -/obj/item/clothing/head/fedora/attack_self(mob/user) - tip_fedora(user) - -/obj/item/clothing/head/fedora/item_action_slot_check(slot) - if(slot == slot_head) - return 1 - -/obj/item/clothing/head/fedora/proc/tip_fedora(mob/user) - user.visible_message("[user] tips [user.p_their()] fedora.", "You tip your fedora") - - -/obj/item/clothing/head/fez - name = "fez" - icon_state = "fez" - item_state = "fez" - desc = "Put it on your monkey, make lots of cash money." - -//end bs12 hats - -/obj/item/clothing/head/witchwig - name = "witch costume wig" - desc = "Eeeee~heheheheheheh!" - icon_state = "witch" - item_state = "witch" - flags = BLOCKHAIR - -/obj/item/clothing/head/chicken - name = "chicken suit head" - desc = "Bkaw!" - icon_state = "chickenhead" - item_state = "chickensuit" - flags = BLOCKHAIR - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/corgi - name = "corgi suit head" - desc = "Woof!" - icon_state = "corgihead" - item_state = "chickensuit" - flags = BLOCKHAIR - -/obj/item/clothing/head/corgi/super_hero - name = "super-hero corgi suit head" - desc = "Woof! This one seems to pulse with a strange power" - flags = BLOCKHAIR | NODROP - -/obj/item/clothing/head/corgi/super_hero/en - name = "E-N suit head" - icon_state = "enhead" - -/obj/item/clothing/head/bearpelt - name = "bear pelt hat" - desc = "Fuzzy." - icon_state = "bearpelt" - item_state = "bearpelt" - flags = BLOCKHAIR - -/obj/item/clothing/head/xenos - name = "xenos helmet" - icon_state = "xenos" - item_state = "xenos_helm" - desc = "A helmet made out of chitinous alien hide." - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - -/obj/item/clothing/head/fedora - name = "fedora" - desc = "Someone wearing this definitely makes them cool" - icon_state = "fedora" - -/obj/item/clothing/head/fedora/whitefedora - name = "white fedora" - icon_state = "wfedora" - -/obj/item/clothing/head/fedora/brownfedora - name = "brown fedora" - icon_state = "bfedora" - -/obj/item/clothing/head/stalhelm - name = "Clown Stalhelm" - desc = "The typical clown soldier's helmet." - icon_state = "stalhelm" - item_state = "stalhelm" - flags = BLOCKHAIR - flags_inv = HIDEEARS - -/obj/item/clothing/head/panzer - name = "Clown HONKMech Cap" - desc = "The softcap worn by HONK Mech pilots." - icon_state = "panzercap" - item_state = "panzercap" - flags = BLOCKHAIR - -/obj/item/clothing/head/naziofficer - name = "Clown Officer Cap" - desc = "The peaked clown officer's cap, disturbingly similar to the warden's." - icon_state = "officercap" - item_state = "officercap" - flags = BLOCKHAIR - flags_inv = HIDEEARS - -/obj/item/clothing/head/beret/purple - name = "Pierson Family Beret" - desc = " A purple beret, with a small golden crescent moon sewn onto it." - icon_state = "beret_purple" - item_state = "purpleberet" - -/obj/item/clothing/head/beret/centcom/officer - name = "officers beret" - desc = "A black beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." - icon_state = "beret_centcom_officer" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/centcom/officer/navy - name = "navy blue officers beret" - desc = "A navy blue beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." - icon_state = "beret_centcom_officer_navy" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/centcom/captain - name = "captains beret" - desc = "A white beret adorned with the shield—a cobalt kite shield with an engraved sword—of the Nanotrasen security forces, worn only by those captaining a vessel of the Nanotrasen Navy." - icon_state = "beret_centcom_captain" - -/obj/item/clothing/head/sombrero - name = "sombrero" - icon_state = "sombrero" - item_state = "sombrero" - desc = "You can practically taste the fiesta." - dog_fashion = /datum/dog_fashion/head/sombrero - -/obj/item/clothing/head/sombrero/green - name = "green sombrero" - icon_state = "greensombrero" - item_state = "greensombrero" - desc = "As elegant as a dancing cactus." - dog_fashion = null - -/obj/item/clothing/head/sombrero/shamebrero - name = "shamebrero" - icon_state = "shamebrero" - item_state = "shamebrero" - desc = "Once it's on, it never comes off." - flags = NODROP - dog_fashion = null - -/obj/item/clothing/head/cone - desc = "This cone is trying to warn you of something!" - name = "warning cone" - icon = 'icons/obj/janitor.dmi' - icon_state = "cone" - item_state = "cone" - force = 1.0 - throwforce = 3.0 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("warned", "cautioned", "smashed") - resistance_flags = NONE - -/obj/item/clothing/head/jester - name = "jester hat" - desc = "A hat with bells, to add some merryness to the suit." - icon_state = "jester_hat" - -/obj/item/clothing/head/rice_hat - name = "rice hat" - desc = "Welcome to the rice fields, motherfucker." - icon_state = "rice_hat" - -/obj/item/clothing/head/griffin - name = "griffon head" - desc = "Why not 'eagle head'? Who knows." - icon_state = "griffinhat" - item_state = "griffinhat" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - actions_types = list(/datum/action/item_action/caw) - -/obj/item/clothing/head/griffin/attack_self() - caw() - -/obj/item/clothing/head/griffin/proc/caw() - if(cooldown < world.time - 20) // A cooldown, to stop people being jerks - playsound(src.loc, 'sound/creatures/caw.ogg', 50, 1) - cooldown = world.time - - -/obj/item/clothing/head/lordadmiralhat - name = "Lord Admiral's Hat" - desc = "A hat suitable for any man of high and exalted rank." - icon_state = "lordadmiralhat" - item_state = "lordadmiralhat" - -/obj/item/clothing/head/human_head - name = "bloated human head" - desc = "A horribly bloated and mismatched human head." - icon_state = "lingspacehelmet" - item_state = "lingspacehelmet" - -/obj/item/clothing/head/papersack - name = "paper sack hat" - desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness." - icon_state = "papersack" - flags = BLOCKHAIR - flags_inv = HIDEFACE|HIDEEARS - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/papersack/smiley - name = "paper sack hat" - desc = "A paper sack with crude holes cut out for eyes and a sketchy smile drawn on the front. Not creepy at all." - icon_state = "papersack_smile" - flags = BLOCKHAIR - flags_inv = HIDEFACE|HIDEEARS - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/crown - name = "crown" - desc = "A crown fit for a king, a petty king maybe." - icon_state = "crown" - armor = list("melee" = 15, "bullet" = 0, "laser" = 0,"energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - -/obj/item/clothing/head/crown/fancy - name = "magnificent crown" - desc = "A crown worn by only the highest emperors of the land." - icon_state = "fancycrown" - -/obj/item/clothing/head/zepelli - name = "chequered diamond hat" - desc = "Wearing this makes you feel like a real mozzarella cheeseball. " - icon_state = "zepelli" - item_state = "zepelli" - -/obj/item/clothing/head/cuban_hat - name = "rhumba hat" - desc = "Now just to find some maracas!" - icon_state = "cuban_hat" - item_state = "cuban_hat" \ No newline at end of file + + +/obj/item/clothing/head/centhat + name = "\improper CentComm. hat" + icon_state = "centcom" + desc = "It's good to be emperor." + item_state = "centhat" + armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 80 + +/obj/item/clothing/head/hairflower + name = "hair flower pin" + icon_state = "hairflower" + desc = "Smells nice." + item_state = "hairflower" + +/obj/item/clothing/head/powdered_wig + name = "powdered wig" + desc = "A powdered wig." + icon_state = "pwig" + item_state = "pwig" + +/obj/item/clothing/head/justice_wig + name = "Justice wig" + desc = "A fancy powdered wig given to arbitrators of the law. It looks itchy." + icon_state = "jwig" + item_state = "jwig" + +/obj/item/clothing/head/beret/blue + icon_state = "beret_blue" + +/obj/item/clothing/head/beret/black + icon_state = "beret_black" + +/obj/item/clothing/head/beret/purple_normal + icon_state = "beret_purple_normal" + +/obj/item/clothing/head/that + name = "top-hat" + desc = "It's an amish looking hat." + icon_state = "tophat" + item_state = "that" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/redcoat + name = "redcoat's hat" + icon_state = "redcoat" + desc = "'I guess it's a redhead.'" + +/obj/item/clothing/head/mailman + name = "mailman's hat" + icon_state = "mailman" + desc = "'Right-on-time' mail service head wear." + +/obj/item/clothing/head/plaguedoctorhat + name = "plague doctor's hat" + desc = "These were once used by Plague doctors. They're pretty much useless." + icon_state = "plaguedoctor" + permeability_coefficient = 0.01 + +/obj/item/clothing/head/hasturhood + name = "hastur's hood" + desc = "It's unspeakably stylish" + icon_state = "hasturhood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/nursehat + name = "nurse's hat" + desc = "It allows quick identification of trained medical personnel." + icon_state = "nursehat" + dog_fashion = /datum/dog_fashion/head/nurse + +/obj/item/clothing/head/syndicatefake + name = "black and red space-helmet replica" + icon_state = "syndicate-helm-black-red" + item_state = "syndicate-helm-black-red" + desc = "A plastic replica of a syndicate agent's space helmet, you'll look just like a real murderous syndicate agent in this! This is a toy, it is not made for use in space!" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + + +/obj/item/clothing/head/cueball + name = "cueball helmet" + desc = "A large, featureless white orb meant to be worn on your head. How do you even see out of this thing?" + icon_state = "cueball" + item_state = "cueball" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/snowman + name = "snowman head" + desc = "A ball of white styrofoam. So festive." + icon_state = "snowman_h" + item_state = "snowman_h" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/that + name = "sturdy top-hat" + desc = "It's an amish looking armored top hat." + icon_state = "tophat" + item_state = "that" + flags_inv = 0 + + +/obj/item/clothing/head/greenbandana + name = "green bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "greenbandana" + item_state = "greenbandana" + flags_inv = 0 + +/obj/item/clothing/head/justice + name = "justice hat" + desc = "Fight for what's righteous!" + icon_state = "justicered" + item_state = "justicered" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + +/obj/item/clothing/head/justice/blue + icon_state = "justiceblue" + item_state = "justiceblue" + +/obj/item/clothing/head/justice/yellow + icon_state = "justiceyellow" + item_state = "justiceyellow" + +/obj/item/clothing/head/justice/green + icon_state = "justicegreen" + item_state = "justicegreen" + +/obj/item/clothing/head/justice/pink + icon_state = "justicepink" + item_state = "justicepink" + +/obj/item/clothing/head/rabbitears + name = "rabbit ears" + desc = "Wearing these makes you look useless, and only good for your sex appeal." + icon_state = "bunny" + dog_fashion = /datum/dog_fashion/head/rabbit + +/obj/item/clothing/head/flatcap + name = "flat cap" + desc = "A working man's cap." + icon_state = "flat_cap" + item_state = "detective" + +/obj/item/clothing/head/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + dog_fashion = /datum/dog_fashion/head/pirate + +/obj/item/clothing/head/hgpiratecap + name = "pirate hat" + desc = "Yarr." + icon_state = "hgpiratecap" + item_state = "hgpiratecap" + +/obj/item/clothing/head/bandana + name = "pirate bandana" + desc = "Yarr." + icon_state = "bandana" + item_state = "bandana" + +//stylish bs12 hats + +/obj/item/clothing/head/bowlerhat + name = "bowler hat" + icon_state = "bowler_hat" + item_state = "bowler_hat" + desc = "For that industrial age look." + +/obj/item/clothing/head/beaverhat + name = "beaver hat" + icon_state = "beaver_hat" + item_state = "beaver_hat" + desc = "Like a top hat, but made of beavers." + +/obj/item/clothing/head/boaterhat + name = "boater hat" + icon_state = "boater_hat" + item_state = "boater_hat" + desc = "Goes well with celery." + +/obj/item/clothing/head/cowboyhat + name = "cowboy hat" + icon_state = "cowboyhat" + item_state = "cowboyhat" + desc = "For the Rancher in us all." + +/obj/item/clothing/head/cowboyhat/tan + name = "tan cowboy hat" + icon_state = "cowboyhat_tan" + item_state = "cowboyhat_tan" + desc = "There's a new sheriff in town. Pass the whiskey." + +/obj/item/clothing/head/cowboyhat/black + name = "black cowboy hat" + icon_state = "cowboyhat_black" + item_state = "cowboyhat_black" + desc = "This station ain't big enough for the two ah' us." + +/obj/item/clothing/head/cowboyhat/white + name = "white cowboy hat" + icon_state = "cowboyhat_white" + item_state = "cowboyhat_white" + desc = "Authentic Marshall hair case. Now ya can protect this here homestead. Navy Model not included." + +/obj/item/clothing/head/cowboyhat/pink + name = "cowgirl hat" + icon_state = "cowboyhat_pink" + item_state = "cowboyhat_pink" + desc = "For those buckle bunnies wanta' become a real buckaroo." + +/obj/item/clothing/head/fedora + name = "fedora" + icon_state = "fedora" + item_state = "fedora" + desc = "A great hat ruined by being within fifty yards of you." + actions_types = list(/datum/action/item_action/tip_fedora) + +/obj/item/clothing/head/fedora/attack_self(mob/user) + tip_fedora(user) + +/obj/item/clothing/head/fedora/item_action_slot_check(slot) + if(slot == slot_head) + return 1 + +/obj/item/clothing/head/fedora/proc/tip_fedora(mob/user) + user.visible_message("[user] tips [user.p_their()] fedora.", "You tip your fedora") + + +/obj/item/clothing/head/fez + name = "fez" + icon_state = "fez" + item_state = "fez" + desc = "Put it on your monkey, make lots of cash money." + +//end bs12 hats + +/obj/item/clothing/head/witchwig + name = "witch costume wig" + desc = "Eeeee~heheheheheheh!" + icon_state = "witch" + item_state = "witch" + flags = BLOCKHAIR + +/obj/item/clothing/head/chicken + name = "chicken suit head" + desc = "Bkaw!" + icon_state = "chickenhead" + item_state = "chickensuit" + flags = BLOCKHAIR + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/corgi + name = "corgi suit head" + desc = "Woof!" + icon_state = "corgihead" + item_state = "chickensuit" + flags = BLOCKHAIR + +/obj/item/clothing/head/corgi/super_hero + name = "super-hero corgi suit head" + desc = "Woof! This one seems to pulse with a strange power" + flags = BLOCKHAIR | NODROP + +/obj/item/clothing/head/corgi/super_hero/en + name = "E-N suit head" + icon_state = "enhead" + +/obj/item/clothing/head/bearpelt + name = "bear pelt hat" + desc = "Fuzzy." + icon_state = "bearpelt" + item_state = "bearpelt" + flags = BLOCKHAIR + +/obj/item/clothing/head/xenos + name = "xenos helmet" + icon_state = "xenos" + item_state = "xenos_helm" + desc = "A helmet made out of chitinous alien hide." + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + +/obj/item/clothing/head/fedora + name = "fedora" + desc = "Someone wearing this definitely makes them cool" + icon_state = "fedora" + +/obj/item/clothing/head/fedora/whitefedora + name = "white fedora" + icon_state = "wfedora" + +/obj/item/clothing/head/fedora/brownfedora + name = "brown fedora" + icon_state = "bfedora" + +/obj/item/clothing/head/stalhelm + name = "Clown Stalhelm" + desc = "The typical clown soldier's helmet." + icon_state = "stalhelm" + item_state = "stalhelm" + flags = BLOCKHAIR + flags_inv = HIDEEARS + +/obj/item/clothing/head/panzer + name = "Clown HONKMech Cap" + desc = "The softcap worn by HONK Mech pilots." + icon_state = "panzercap" + item_state = "panzercap" + flags = BLOCKHAIR + +/obj/item/clothing/head/naziofficer + name = "Clown Officer Cap" + desc = "The peaked clown officer's cap, disturbingly similar to the warden's." + icon_state = "officercap" + item_state = "officercap" + flags = BLOCKHAIR + flags_inv = HIDEEARS + +/obj/item/clothing/head/beret/purple + name = "Pierson Family Beret" + desc = " A purple beret, with a small golden crescent moon sewn onto it." + icon_state = "beret_purple" + item_state = "purpleberet" + +/obj/item/clothing/head/beret/centcom/officer + name = "officers beret" + desc = "A black beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." + icon_state = "beret_centcom_officer" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/centcom/officer/navy + name = "navy blue officers beret" + desc = "A navy blue beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." + icon_state = "beret_centcom_officer_navy" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/centcom/captain + name = "captains beret" + desc = "A white beret adorned with the shield—a cobalt kite shield with an engraved sword—of the Nanotrasen security forces, worn only by those captaining a vessel of the Nanotrasen Navy." + icon_state = "beret_centcom_captain" + +/obj/item/clothing/head/sombrero + name = "sombrero" + icon_state = "sombrero" + item_state = "sombrero" + desc = "You can practically taste the fiesta." + dog_fashion = /datum/dog_fashion/head/sombrero + +/obj/item/clothing/head/sombrero/green + name = "green sombrero" + icon_state = "greensombrero" + item_state = "greensombrero" + desc = "As elegant as a dancing cactus." + dog_fashion = null + +/obj/item/clothing/head/sombrero/shamebrero + name = "shamebrero" + icon_state = "shamebrero" + item_state = "shamebrero" + desc = "Once it's on, it never comes off." + flags = NODROP + dog_fashion = null + +/obj/item/clothing/head/cone + desc = "This cone is trying to warn you of something!" + name = "warning cone" + icon = 'icons/obj/janitor.dmi' + icon_state = "cone" + item_state = "cone" + force = 1.0 + throwforce = 3.0 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("warned", "cautioned", "smashed") + resistance_flags = NONE + +/obj/item/clothing/head/jester + name = "jester hat" + desc = "A hat with bells, to add some merryness to the suit." + icon_state = "jester_hat" + +/obj/item/clothing/head/rice_hat + name = "rice hat" + desc = "Welcome to the rice fields, motherfucker." + icon_state = "rice_hat" + +/obj/item/clothing/head/griffin + name = "griffon head" + desc = "Why not 'eagle head'? Who knows." + icon_state = "griffinhat" + item_state = "griffinhat" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + actions_types = list(/datum/action/item_action/caw) + +/obj/item/clothing/head/griffin/attack_self() + caw() + +/obj/item/clothing/head/griffin/proc/caw() + if(cooldown < world.time - 20) // A cooldown, to stop people being jerks + playsound(src.loc, 'sound/creatures/caw.ogg', 50, 1) + cooldown = world.time + + +/obj/item/clothing/head/lordadmiralhat + name = "Lord Admiral's Hat" + desc = "A hat suitable for any man of high and exalted rank." + icon_state = "lordadmiralhat" + item_state = "lordadmiralhat" + +/obj/item/clothing/head/human_head + name = "bloated human head" + desc = "A horribly bloated and mismatched human head." + icon_state = "lingspacehelmet" + item_state = "lingspacehelmet" + +/obj/item/clothing/head/papersack + name = "paper sack hat" + desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness." + icon_state = "papersack" + flags = BLOCKHAIR + flags_inv = HIDEFACE|HIDEEARS + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/papersack/smiley + name = "paper sack hat" + desc = "A paper sack with crude holes cut out for eyes and a sketchy smile drawn on the front. Not creepy at all." + icon_state = "papersack_smile" + flags = BLOCKHAIR + flags_inv = HIDEFACE|HIDEEARS + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/crown + name = "crown" + desc = "A crown fit for a king, a petty king maybe." + icon_state = "crown" + armor = list("melee" = 15, "bullet" = 0, "laser" = 0,"energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + +/obj/item/clothing/head/crown/fancy + name = "magnificent crown" + desc = "A crown worn by only the highest emperors of the land." + icon_state = "fancycrown" + +/obj/item/clothing/head/zepelli + name = "chequered diamond hat" + desc = "Wearing this makes you feel like a real mozzarella cheeseball. " + icon_state = "zepelli" + item_state = "zepelli" + +/obj/item/clothing/head/cuban_hat + name = "rhumba hat" + desc = "Now just to find some maracas!" + icon_state = "cuban_hat" + item_state = "cuban_hat" diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index d939dadffba8..5f8ae17d061c 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -1,273 +1,273 @@ -/* - * Contents: - * Welding mask - * Cakehat - * Ushanka - * Pumpkin head - * Kitty ears - * Cardborg Disguise - * Head Mirror - */ - -/* - * Welding mask - */ -/obj/item/clothing/head/welding - name = "welding helmet" - desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." - icon_state = "welding" - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "welding" - materials = list(MAT_METAL=1750, MAT_GLASS=400) - flash_protect = 2 - tint = 2 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 60) - flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - actions_types = list(/datum/action/item_action/toggle) - visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/head/welding/flamedecal - name = "flame decal welding helmet" - desc = "A welding helmet adorned with flame decals, and several cryptic slogans of varying degrees of legibility." - icon_state = "welding_redflame" - -/obj/item/clothing/head/welding/flamedecal/blue - name = "blue flame decal welding helmet" - desc = "A welding helmet with blue flame decals on it." - icon_state = "welding_blueflame" - -/obj/item/clothing/head/welding/white - name = "white decal welding helmet" - desc = "A white welding helmet with a character written across it." - icon_state = "welding_white" - -/obj/item/clothing/head/welding/attack_self() - toggle() - -/obj/item/clothing/head/welding/proc/toggle() - if(up) - up = !up - flags_cover |= (HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = initial(icon_state) - to_chat(usr, "You flip the [src] down to protect your eyes.") - flash_protect = 2 - tint = 2 - else - up = !up - flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = "[initial(icon_state)]up" - to_chat(usr, "You push the [src] up out of your face.") - flash_protect = 0 - tint = 0 - var/mob/living/carbon/user = usr - user.update_tint() - user.update_inv_head() //so our mob-overlays update - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - - - -/* - * Cakehat - */ -/obj/item/clothing/head/cakehat - name = "cake-hat" - desc = "It's tasty looking!" - icon_state = "cake0" - flags_cover = HEADCOVERSEYES - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - var/onfire = 0.0 - var/status = 0 - var/fire_resist = T0C+1300 //this is the max temp it can stand before you start to cook. although it might not burn away, you take damage - var/processing = 0 //I dont think this is used anywhere. - -/obj/item/clothing/head/cakehat/process() - if(!onfire) - STOP_PROCESSING(SSobj, src) - return - - var/turf/location = src.loc - if(istype(location, /mob/)) - var/mob/living/carbon/human/M = location - if(M.l_hand == src || M.r_hand == src || M.head == src) - location = M.loc - - if(istype(location, /turf)) - location.hotspot_expose(700, 1) - -/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) - if(status > 1) return - src.onfire = !( src.onfire ) - if(src.onfire) - src.force = 3 - src.damtype = "fire" - src.icon_state = "cake1" - START_PROCESSING(SSobj, src) - else - src.force = null - src.damtype = "brute" - src.icon_state = "cake0" - return - - -/* - * Ushanka - */ -/obj/item/clothing/head/ushanka - name = "ushanka" - desc = "Perfect for winter in Siberia, da?" - icon_state = "ushankadown" - item_state = "ushankadown" - flags_inv = HIDEEARS - cold_protection = HEAD - min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT - dog_fashion = /datum/dog_fashion/head/ushanka - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) - if(src.icon_state == "ushankadown") - src.icon_state = "ushankaup" - src.item_state = "ushankaup" - to_chat(user, "You raise the ear flaps on the ushanka.") - else - src.icon_state = "ushankadown" - src.item_state = "ushankadown" - to_chat(user, "You lower the ear flaps on the ushanka.") - -/* - * Pumpkin head - */ -/obj/item/clothing/head/hardhat/pumpkinhead - name = "carved pumpkin" - desc = "A jack o' lantern! Believed to ward off evil spirits." - icon_state = "hardhat0_pumpkin"//Could stand to be renamed - item_state = "hardhat0_pumpkin" - item_color = "pumpkin" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - brightness_on = 2 //luminosity when on - - -/obj/item/clothing/head/hardhat/reindeer - name = "novelty reindeer hat" - desc = "Some fake antlers and a very fake red nose." - icon_state = "hardhat0_reindeer" - item_state = "hardhat0_reindeer" - item_color = "reindeer" - flags_inv = 0 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - brightness_on = 1 //luminosity when on - dog_fashion = /datum/dog_fashion/head/reindeer - - -/* - * Kitty ears - */ -/obj/item/clothing/head/kitty - name = "kitty ears" - desc = "A pair of kitty ears. Meow!" - icon_state = "kitty" - var/icon/mob - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/obj/item/organ/external/head/head_organ = user.get_organ("head") - - mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kitty") - mob.Blend(head_organ.hair_colour, ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kittyinner") - mob.Blend(earbit, ICON_OVERLAY) - - icon_override = mob - -/obj/item/clothing/head/kitty/equipped(var/mob/M, slot) - . = ..() - if(ishuman(M) && slot == slot_head) - update_icon(M) - - -/obj/item/clothing/head/kitty/mouse - name = "mouse ears" - desc = "A pair of mouse ears. Squeak!" - icon_state = "mousey" - -/obj/item/clothing/head/kitty/mouse/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/obj/item/organ/external/head/head_organ = user.get_organ("head") - mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mousey") - mob.Blend(head_organ.hair_colour, ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mouseyinner") - mob.Blend(earbit, ICON_OVERLAY) - - icon_override = mob - -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - item_state = "cardborg_h" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - species_disguise = "High-tech robot" - dog_fashion = /datum/dog_fashion/head/cardborg - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - - -/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) - ..() - if(ishuman(user) && slot == slot_head) - var/mob/living/carbon/human/H = user - if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) - var/obj/item/clothing/suit/cardborg/CB = H.wear_suit - CB.disguise(user, src) - -/obj/item/clothing/head/cardborg/dropped(mob/living/user) - ..() - user.remove_alt_appearance("standard_borg_disguise") - -/* - * Head Mirror - */ -/obj/item/clothing/head/headmirror - name = "head mirror" - desc = "A band of rubber with a very reflective looking mirror attached to the front of it. One of the early signs of medical budget cuts." - icon_state = "head_mirror" - item_state = "head_mirror" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Drask" = 'icons/mob/species/drask/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - +/* + * Contents: + * Welding mask + * Cakehat + * Ushanka + * Pumpkin head + * Kitty ears + * Cardborg Disguise + * Head Mirror + */ + +/* + * Welding mask + */ +/obj/item/clothing/head/welding + name = "welding helmet" + desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." + icon_state = "welding" + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "welding" + materials = list(MAT_METAL=1750, MAT_GLASS=400) + flash_protect = 2 + tint = 2 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 60) + flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + actions_types = list(/datum/action/item_action/toggle) + visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/head/welding/flamedecal + name = "flame decal welding helmet" + desc = "A welding helmet adorned with flame decals, and several cryptic slogans of varying degrees of legibility." + icon_state = "welding_redflame" + +/obj/item/clothing/head/welding/flamedecal/blue + name = "blue flame decal welding helmet" + desc = "A welding helmet with blue flame decals on it." + icon_state = "welding_blueflame" + +/obj/item/clothing/head/welding/white + name = "white decal welding helmet" + desc = "A white welding helmet with a character written across it." + icon_state = "welding_white" + +/obj/item/clothing/head/welding/attack_self() + toggle() + +/obj/item/clothing/head/welding/proc/toggle() + if(up) + up = !up + flags_cover |= (HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = initial(icon_state) + to_chat(usr, "You flip the [src] down to protect your eyes.") + flash_protect = 2 + tint = 2 + else + up = !up + flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = "[initial(icon_state)]up" + to_chat(usr, "You push the [src] up out of your face.") + flash_protect = 0 + tint = 0 + var/mob/living/carbon/user = usr + user.update_tint() + user.update_inv_head() //so our mob-overlays update + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + + + +/* + * Cakehat + */ +/obj/item/clothing/head/cakehat + name = "cake-hat" + desc = "It's tasty looking!" + icon_state = "cake0" + flags_cover = HEADCOVERSEYES + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + var/onfire = 0.0 + var/status = 0 + var/fire_resist = T0C+1300 //this is the max temp it can stand before you start to cook. although it might not burn away, you take damage + var/processing = 0 //I dont think this is used anywhere. + +/obj/item/clothing/head/cakehat/process() + if(!onfire) + STOP_PROCESSING(SSobj, src) + return + + var/turf/location = src.loc + if(istype(location, /mob/)) + var/mob/living/carbon/human/M = location + if(M.l_hand == src || M.r_hand == src || M.head == src) + location = M.loc + + if(istype(location, /turf)) + location.hotspot_expose(700, 1) + +/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) + if(status > 1) return + src.onfire = !( src.onfire ) + if(src.onfire) + src.force = 3 + src.damtype = "fire" + src.icon_state = "cake1" + START_PROCESSING(SSobj, src) + else + src.force = null + src.damtype = "brute" + src.icon_state = "cake0" + return + + +/* + * Ushanka + */ +/obj/item/clothing/head/ushanka + name = "ushanka" + desc = "Perfect for winter in Siberia, da?" + icon_state = "ushankadown" + item_state = "ushankadown" + flags_inv = HIDEEARS + cold_protection = HEAD + min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT + dog_fashion = /datum/dog_fashion/head/ushanka + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) + if(src.icon_state == "ushankadown") + src.icon_state = "ushankaup" + src.item_state = "ushankaup" + to_chat(user, "You raise the ear flaps on the ushanka.") + else + src.icon_state = "ushankadown" + src.item_state = "ushankadown" + to_chat(user, "You lower the ear flaps on the ushanka.") + +/* + * Pumpkin head + */ +/obj/item/clothing/head/hardhat/pumpkinhead + name = "carved pumpkin" + desc = "A jack o' lantern! Believed to ward off evil spirits." + icon_state = "hardhat0_pumpkin"//Could stand to be renamed + item_state = "hardhat0_pumpkin" + item_color = "pumpkin" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + brightness_on = 2 //luminosity when on + + +/obj/item/clothing/head/hardhat/reindeer + name = "novelty reindeer hat" + desc = "Some fake antlers and a very fake red nose." + icon_state = "hardhat0_reindeer" + item_state = "hardhat0_reindeer" + item_color = "reindeer" + flags_inv = 0 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + brightness_on = 1 //luminosity when on + dog_fashion = /datum/dog_fashion/head/reindeer + + +/* + * Kitty ears + */ +/obj/item/clothing/head/kitty + name = "kitty ears" + desc = "A pair of kitty ears. Meow!" + icon_state = "kitty" + var/icon/mob + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/obj/item/organ/external/head/head_organ = user.get_organ("head") + + mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kitty") + mob.Blend(head_organ.hair_colour, ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kittyinner") + mob.Blend(earbit, ICON_OVERLAY) + + icon_override = mob + +/obj/item/clothing/head/kitty/equipped(var/mob/M, slot) + . = ..() + if(ishuman(M) && slot == slot_head) + update_icon(M) + + +/obj/item/clothing/head/kitty/mouse + name = "mouse ears" + desc = "A pair of mouse ears. Squeak!" + icon_state = "mousey" + +/obj/item/clothing/head/kitty/mouse/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/obj/item/organ/external/head/head_organ = user.get_organ("head") + mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mousey") + mob.Blend(head_organ.hair_colour, ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mouseyinner") + mob.Blend(earbit, ICON_OVERLAY) + + icon_override = mob + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + item_state = "cardborg_h" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + species_disguise = "High-tech robot" + dog_fashion = /datum/dog_fashion/head/cardborg + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + + +/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) + ..() + if(ishuman(user) && slot == slot_head) + var/mob/living/carbon/human/H = user + if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) + var/obj/item/clothing/suit/cardborg/CB = H.wear_suit + CB.disguise(user, src) + +/obj/item/clothing/head/cardborg/dropped(mob/living/user) + ..() + user.remove_alt_appearance("standard_borg_disguise") + +/* + * Head Mirror + */ +/obj/item/clothing/head/headmirror + name = "head mirror" + desc = "A band of rubber with a very reflective looking mirror attached to the front of it. One of the early signs of medical budget cuts." + icon_state = "head_mirror" + item_state = "head_mirror" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index b111672e86aa..0255109620a8 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -1,132 +1,132 @@ -/obj/item/clothing/head/soft - name = "cargo cap" - desc = "It's a baseball hat in a tasteless yellow colour." - icon_state = "cargosoft" - item_state = "helmet" - item_color = "cargo" - var/flipped = 0 - actions_types = list(/datum/action/item_action/flip_cap) - dog_fashion = /datum/dog_fashion/head/cargo_tech - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi' - ) - -/obj/item/clothing/head/soft/dropped() - icon_state = "[item_color]soft" - flipped = 0 - ..() - -/obj/item/clothing/head/soft/attack_self(mob/user) - flip(user) - -/obj/item/clothing/head/soft/proc/flip(mob/user) - flipped = !flipped - if(flipped) - icon_state = "[item_color]soft_flipped" - to_chat(usr, "You flip the hat backwards.") - else - icon_state = "[item_color]soft" - to_chat(user, "You flip the hat back in normal position.") - user.update_inv_head() //so our mob-overlays update - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/soft/red - name = "red cap" - desc = "It's a baseball hat in a tasteless red colour." - icon_state = "redsoft" - item_color = "red" - dog_fashion = null - -/obj/item/clothing/head/soft/blue - name = "blue cap" - desc = "It's a baseball hat in a tasteless blue colour." - icon_state = "bluesoft" - item_color = "blue" - dog_fashion = null - -/obj/item/clothing/head/soft/green - name = "green cap" - desc = "It's a baseball hat in a tasteless green colour." - icon_state = "greensoft" - item_color = "green" - dog_fashion = null - -/obj/item/clothing/head/soft/yellow - name = "yellow cap" - desc = "It's a baseball hat in a tasteless yellow colour." - icon_state = "yellowsoft" - item_color = "yellow" - dog_fashion = null - -/obj/item/clothing/head/soft/grey - name = "grey cap" - desc = "It's a baseball hat in a tasteful grey colour." - icon_state = "greysoft" - item_color = "grey" - dog_fashion = null - -/obj/item/clothing/head/soft/orange - name = "orange cap" - desc = "It's a baseball hat in a tasteless orange colour." - icon_state = "orangesoft" - item_color = "orange" - -/obj/item/clothing/head/soft/mime - name = "white cap" - desc = "It's a baseball hat in a tasteless white colour." - icon_state = "mimesoft" - item_color = "mime" - dog_fashion = null - -/obj/item/clothing/head/soft/purple - name = "purple cap" - desc = "It's a baseball hat in a tasteless purple colour." - icon_state = "purplesoft" - item_color = "purple" - dog_fashion = null - -/obj/item/clothing/head/soft/black - name = "black cap" - desc = "It's a baseball hat in a tasteless black colour." - icon_state = "blacksoft" - item_color = "black" - dog_fashion = null - -/obj/item/clothing/head/soft/rainbow - name = "rainbow cap" - desc = "It's a baseball hat in a bright rainbow of colors." - icon_state = "rainbowsoft" - item_color = "rainbow" - dog_fashion = null - -/obj/item/clothing/head/soft/sec - name = "security cap" - desc = "It's baseball hat in tasteful red colour." - icon_state = "secsoft" - item_color = "sec" - armor = list("melee" = 30, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - dog_fashion = null - -/obj/item/clothing/head/soft/sec/corp - name = "corporate security cap" - desc = "It's baseball hat in corpotate colours." - icon_state = "corpsoft" - item_color = "corp" - -/obj/item/clothing/head/soft/solgov - name = "Sol Federation marine cap" - desc = "A soft cap worn by marines of the Sol Federation." - icon_state = "solgovsoft" - item_color = "solgov" - dog_fashion = null - -/obj/item/clothing/head/soft/solgov/command - name = "Sol Federation Lieutenant's cap" - desc = "A soft cap worn by marines of the Sol Federation. The insignia signifies the wearer bears the rank of a Lieutenant." - icon_state = "solgovcsoft" - item_color = "solgovc" - dog_fashion = null \ No newline at end of file +/obj/item/clothing/head/soft + name = "cargo cap" + desc = "It's a baseball hat in a tasteless yellow colour." + icon_state = "cargosoft" + item_state = "helmet" + item_color = "cargo" + var/flipped = 0 + actions_types = list(/datum/action/item_action/flip_cap) + dog_fashion = /datum/dog_fashion/head/cargo_tech + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi' + ) + +/obj/item/clothing/head/soft/dropped() + icon_state = "[item_color]soft" + flipped = 0 + ..() + +/obj/item/clothing/head/soft/attack_self(mob/user) + flip(user) + +/obj/item/clothing/head/soft/proc/flip(mob/user) + flipped = !flipped + if(flipped) + icon_state = "[item_color]soft_flipped" + to_chat(usr, "You flip the hat backwards.") + else + icon_state = "[item_color]soft" + to_chat(user, "You flip the hat back in normal position.") + user.update_inv_head() //so our mob-overlays update + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/soft/red + name = "red cap" + desc = "It's a baseball hat in a tasteless red colour." + icon_state = "redsoft" + item_color = "red" + dog_fashion = null + +/obj/item/clothing/head/soft/blue + name = "blue cap" + desc = "It's a baseball hat in a tasteless blue colour." + icon_state = "bluesoft" + item_color = "blue" + dog_fashion = null + +/obj/item/clothing/head/soft/green + name = "green cap" + desc = "It's a baseball hat in a tasteless green colour." + icon_state = "greensoft" + item_color = "green" + dog_fashion = null + +/obj/item/clothing/head/soft/yellow + name = "yellow cap" + desc = "It's a baseball hat in a tasteless yellow colour." + icon_state = "yellowsoft" + item_color = "yellow" + dog_fashion = null + +/obj/item/clothing/head/soft/grey + name = "grey cap" + desc = "It's a baseball hat in a tasteful grey colour." + icon_state = "greysoft" + item_color = "grey" + dog_fashion = null + +/obj/item/clothing/head/soft/orange + name = "orange cap" + desc = "It's a baseball hat in a tasteless orange colour." + icon_state = "orangesoft" + item_color = "orange" + +/obj/item/clothing/head/soft/mime + name = "white cap" + desc = "It's a baseball hat in a tasteless white colour." + icon_state = "mimesoft" + item_color = "mime" + dog_fashion = null + +/obj/item/clothing/head/soft/purple + name = "purple cap" + desc = "It's a baseball hat in a tasteless purple colour." + icon_state = "purplesoft" + item_color = "purple" + dog_fashion = null + +/obj/item/clothing/head/soft/black + name = "black cap" + desc = "It's a baseball hat in a tasteless black colour." + icon_state = "blacksoft" + item_color = "black" + dog_fashion = null + +/obj/item/clothing/head/soft/rainbow + name = "rainbow cap" + desc = "It's a baseball hat in a bright rainbow of colors." + icon_state = "rainbowsoft" + item_color = "rainbow" + dog_fashion = null + +/obj/item/clothing/head/soft/sec + name = "security cap" + desc = "It's baseball hat in tasteful red colour." + icon_state = "secsoft" + item_color = "sec" + armor = list("melee" = 30, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + dog_fashion = null + +/obj/item/clothing/head/soft/sec/corp + name = "corporate security cap" + desc = "It's baseball hat in corpotate colours." + icon_state = "corpsoft" + item_color = "corp" + +/obj/item/clothing/head/soft/solgov + name = "Sol Federation marine cap" + desc = "A soft cap worn by marines of the Sol Federation." + icon_state = "solgovsoft" + item_color = "solgov" + dog_fashion = null + +/obj/item/clothing/head/soft/solgov/command + name = "Sol Federation Lieutenant's cap" + desc = "A soft cap worn by marines of the Sol Federation. The insignia signifies the wearer bears the rank of a Lieutenant." + icon_state = "solgovcsoft" + item_color = "solgovc" + dog_fashion = null diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm index 78034a3c0c85..14afa81cb580 100644 --- a/code/modules/clothing/masks/boxing.dm +++ b/code/modules/clothing/masks/boxing.dm @@ -1,52 +1,52 @@ -/obj/item/clothing/mask/balaclava - name = "balaclava" - desc = "LOADSAMONEY" - icon_state = "balaclava" - item_state = "balaclava" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - actions_types = list(/datum/action/item_action/adjust) - adjusted_flags = SLOT_HEAD - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/balaclava/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/luchador - name = "Luchador Mask" - desc = "Worn by robust fighters, flying high to defeat their foes!" - icon_state = "luchag" - item_state = "luchag" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/luchador/tecnicos - name = "Tecnicos Mask" - desc = "Worn by robust fighters who uphold justice and fight honorably." - icon_state = "luchador" - item_state = "luchador" - -/obj/item/clothing/mask/luchador/rudos - name = "Rudos Mask" - desc = "Worn by robust fighters who are willing to do anything to win." - icon_state = "luchar" - item_state = "luchar" +/obj/item/clothing/mask/balaclava + name = "balaclava" + desc = "LOADSAMONEY" + icon_state = "balaclava" + item_state = "balaclava" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + actions_types = list(/datum/action/item_action/adjust) + adjusted_flags = SLOT_HEAD + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/balaclava/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/luchador + name = "Luchador Mask" + desc = "Worn by robust fighters, flying high to defeat their foes!" + icon_state = "luchag" + item_state = "luchag" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/luchador/tecnicos + name = "Tecnicos Mask" + desc = "Worn by robust fighters who uphold justice and fight honorably." + icon_state = "luchador" + item_state = "luchador" + +/obj/item/clothing/mask/luchador/rudos + name = "Rudos Mask" + desc = "Worn by robust fighters who are willing to do anything to win." + icon_state = "luchar" + item_state = "luchar" diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index c37fad489069..032062461e7f 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -1,54 +1,54 @@ -/obj/item/clothing/mask/breath - desc = "A close-fitting mask that can be connected to an air supply." - name = "breath mask" - icon_state = "breath" - item_state = "breath" - flags = AIRTIGHT - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.10 - permeability_coefficient = 0.50 - actions_types = list(/datum/action/item_action/adjust) - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' - ) - -/obj/item/clothing/mask/breath/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/breath/AltClick(mob/user) - ..() - if( (!in_range(src, user)) || user.stat || user.restrained() ) - return - adjustmask(user) - -/obj/item/clothing/mask/breath/medical - desc = "A close-fitting sterile mask that can be connected to an air supply." - name = "medical mask" - icon_state = "medical" - item_state = "medical" - permeability_coefficient = 0.01 - put_on_delay = 10 - -/obj/item/clothing/mask/breath/vox - desc = "A weirdly-shaped breath mask." - name = "vox breath mask" - icon_state = "voxmask" - item_state = "voxmask" - permeability_coefficient = 0.01 - species_restricted = list("Vox", "Vox Armalis") //These should fit the "Mega Vox" just fine. - actions_types = list() - -/obj/item/clothing/mask/breath/vox/attack_self(var/mob/user) - return - -/obj/item/clothing/mask/breath/vox/AltClick(mob/user) - return +/obj/item/clothing/mask/breath + desc = "A close-fitting mask that can be connected to an air supply." + name = "breath mask" + icon_state = "breath" + item_state = "breath" + flags = AIRTIGHT + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.10 + permeability_coefficient = 0.50 + actions_types = list(/datum/action/item_action/adjust) + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' + ) + +/obj/item/clothing/mask/breath/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/breath/AltClick(mob/user) + ..() + if( (!in_range(src, user)) || user.stat || user.restrained() ) + return + adjustmask(user) + +/obj/item/clothing/mask/breath/medical + desc = "A close-fitting sterile mask that can be connected to an air supply." + name = "medical mask" + icon_state = "medical" + item_state = "medical" + permeability_coefficient = 0.01 + put_on_delay = 10 + +/obj/item/clothing/mask/breath/vox + desc = "A weirdly-shaped breath mask." + name = "vox breath mask" + icon_state = "voxmask" + item_state = "voxmask" + permeability_coefficient = 0.01 + species_restricted = list("Vox", "Vox Armalis") //These should fit the "Mega Vox" just fine. + actions_types = list() + +/obj/item/clothing/mask/breath/vox/attack_self(var/mob/user) + return + +/obj/item/clothing/mask/breath/vox/AltClick(mob/user) + return diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index b6e3164a44d7..b798c1d8df03 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -1,365 +1,365 @@ -/obj/item/clothing/mask/gas - name = "gas mask" - desc = "A face-covering mask that can be connected to an air supply." - icon_state = "gas_alt" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - w_class = WEIGHT_CLASS_NORMAL - item_state = "gas_alt" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' - ) - -// **** Welding gas mask **** - -/obj/item/clothing/mask/gas/welding - name = "welding mask" - desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." - icon_state = "weldingmask" - item_state = "weldingmask" - materials = list(MAT_METAL=4000, MAT_GLASS=2000) - flash_protect = 2 - tint = 2 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 55) - origin_tech = "materials=2;engineering=3" - actions_types = list(/datum/action/item_action/toggle) - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSEYES - visor_flags_inv = HIDEEYES - resistance_flags = FIRE_PROOF - -/obj/item/clothing/mask/gas/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/mask/gas/explorer - name = "explorer gas mask" - desc = "A military-grade gas mask that can be connected to an air supply." - icon_state = "gas_mining" - actions_types = list(/datum/action/item_action/adjust) - armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40) - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi' - ) - -/obj/item/clothing/mask/gas/explorer/attack_self(mob/user) - adjustmask(user) - -/obj/item/clothing/mask/gas/explorer/adjustmask(user) - ..() - w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL - -/obj/item/clothing/mask/gas/explorer/folded/Initialize() - . = ..() - adjustmask() - -//Bane gas mask -/obj/item/clothing/mask/banemask - name = "bane mask" - desc = "Only when the station is in flames, do you have my permission to robust." - icon_state = "bane_mask" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - w_class = WEIGHT_CLASS_NORMAL - item_state = "bane_mask" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - - -//Plague Dr suit can be found in clothing/suits/bio.dm -/obj/item/clothing/mask/gas/plaguedoctor - name = "plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply." - icon_state = "plaguedoctor" - item_state = "gas_mask" - armor = list("melee" = 0, "bullet" = 0, "laser" = 2, "energy" = 2, "bomb" = 0, "bio" = 75, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/clothing/mask/gas/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - -/obj/item/clothing/mask/gas/syndicate - name = "syndicate mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - strip_delay = 60 - -/obj/item/clothing/mask/gas/clown_hat - name = "clown wig and mask" - desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." - icon_state = "clown" - item_state = "clown_hat" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - resistance_flags = FLAMMABLE - dog_fashion = /datum/dog_fashion/head/clown - -/obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) - - var/mob/M = usr - var/list/options = list() - options["True Form"] = "clown" - options["The Feminist"] = "sexyclown" - options["The Madman"] = "joker" - options["The Rainbow Color"] ="rainbow" - - var/choice = input(M,"To what form do you wish to Morph this mask?","Morph Mask") in options - - if(src && choice && !M.stat && in_range(M,src)) - icon_state = options[choice] - to_chat(M, "Your Clown Mask has now morphed into [choice], all praise the Honk Mother!") - return 1 - -/obj/item/clothing/mask/gas/clown_hat/sexy - name = "sexy-clown wig and mask" - desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." - icon_state = "sexyclown" - item_state = "sexyclown" - -/obj/item/clothing/mask/gas/clownwiz - name = "wizard clown wig and mask" - desc = "Some pranksters are truly magical." - icon_state = "wizzclown" - item_state = "wizzclown" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - flags_inv = HIDEEARS | HIDEEYES - magical = TRUE - -/obj/item/clothing/mask/gas/clown_hat/nodrop - flags = NODROP - -/obj/item/clothing/mask/gas/mime - name = "mime mask" - desc = "The traditional mime's mask. It has an eerie facial posture." - icon_state = "mime" - item_state = "mime" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/mime/nodrop - flags = NODROP - -/obj/item/clothing/mask/gas/monkeymask - name = "monkey mask" - desc = "A mask used when acting as a monkey." - icon_state = "monkeymask" - item_state = "monkeymask" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/sexymime - name = "sexy mime mask" - desc = "A traditional female mime's mask." - icon_state = "sexymime" - item_state = "sexymime" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/cyborg - name = "cyborg visor" - desc = "Beep boop" - icon_state = "death" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/owl_mask - name = "owl mask" - desc = "Twoooo!" - icon_state = "owl" - resistance_flags = FLAMMABLE - actions_types = list(/datum/action/item_action/hoot) - -/obj/item/clothing/mask/gas/owl_mask/super_hero - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP - -/obj/item/clothing/mask/gas/owl_mask/attack_self() - hoot() - -/obj/item/clothing/mask/gas/owl_mask/proc/hoot() - if(cooldown < world.time - 35) // A cooldown, to stop people being jerks - playsound(src.loc, 'sound/creatures/hoot.ogg', 50, 1) - cooldown = world.time - -// ******************************************************************** - -// **** Security gas mask **** - -/obj/item/clothing/mask/gas/sechailer - name = "security gas mask" - desc = "A standard issue Security gas mask with integrated 'Compli-o-nator 3000' device, plays over a dozen pre-recorded compliance phrases designed to get scumbags to stand still whilst you taze them. Do not tamper with the device." - icon_state = "sechailer" - item_state = "sechailer" - var/phrase = 1 - var/aggressiveness = 1 - var/safety = 1 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase) - var/phrase_list = list( - - "halt" = "HALT! HALT! HALT! HALT!", - "bobby" = "Stop in the name of the Law.", - "compliance" = "Compliance is in your best interest.", - "justice" = "Prepare for justice!", - "running" = "Running will only increase your sentence.", - "dontmove" = "Don't move, Creep!", - "floor" = "Down on the floor, Creep!", - "robocop" = "Dead or alive you're coming with me.", - "god" = "God made today for the crooks we could not catch yesterday.", - "freeze" = "Freeze, Scum Bag!", - "imperial" = "Stop right there, criminal scum!", - "bash" = "Stop or I'll bash you.", - "harry" = "Go ahead, make my day.", - "asshole" = "Stop breaking the law, asshole.", - "stfu" = "You have the right to shut the fuck up", - "shutup" = "Shut up crime!", - "super" = "Face the wrath of the golden bolt.", - "dredd" = "I am, the LAW!" - ) -/obj/item/clothing/mask/gas/sechailer/hos - name = "\improper HOS SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." - icon_state = "hosmask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/warden - name = "\improper Warden SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." - icon_state = "wardenmask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - - -/obj/item/clothing/mask/gas/sechailer/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." - icon_state = "officermask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/blue - name = "\improper blue SWAT mask" - desc = "A neon blue swat mask, used for demoralizing Greytide in the wild." - icon_state = "blue_sechailer" - item_state = "blue_sechailer" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/cyborg - name = "security hailer" - desc = "A set of recognizable pre-recorded messages for cyborgs to use when apprehending criminals." - icon = 'icons/obj/device.dmi' - icon_state = "taperecorder_idle" - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/halt) - halt() - else if(actiontype == /datum/action/item_action/adjust) - adjustmask(user) - else if(actiontype == /datum/action/item_action/selectphrase) - var/key = phrase_list[phrase] - var/message = phrase_list[key] - - if (!safety) - to_chat(user, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") - return - - switch(aggressiveness) - if(1) - phrase = (phrase < 6) ? (phrase + 1) : 1 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(2) - phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(3) - phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(4) - phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - else - to_chat(user, "It's broken.") - -/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/screwdriver)) - switch(aggressiveness) - if(1) - to_chat(user, "You set the aggressiveness restrictor to the second position.") - aggressiveness = 2 - phrase = 7 - if(2) - to_chat(user, "You set the aggressiveness restrictor to the third position.") - aggressiveness = 3 - phrase = 13 - if(3) - to_chat(user, "You set the aggressiveness restrictor to the fourth position.") - aggressiveness = 4 - phrase = 1 - if(4) - to_chat(user, "You set the aggressiveness restrictor to the first position.") - aggressiveness = 1 - phrase = 1 - if(5) - to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") - else if(istype(W, /obj/item/wirecutters)) - if(aggressiveness != 5) - to_chat(user, "You broke it!") - aggressiveness = 5 - else - ..() - -/obj/item/clothing/mask/gas/sechailer/attack_self() - halt() - -/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user as mob) - if(safety) - safety = 0 - to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") - else - return - -/obj/item/clothing/mask/gas/sechailer/proc/halt() - var/key = phrase_list[phrase] - var/message = phrase_list[key] - - - if(cooldown < world.time - 35) // A cooldown, to stop people being jerks - if(!safety) - message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." - usr.visible_message("[usr]'s Compli-o-Nator: [message]") - playsound(src.loc, 'sound/voice/binsult.ogg', 100, 0, 4) - cooldown = world.time - return - - usr.visible_message("[usr]'s Compli-o-Nator: [message]") - playsound(src.loc, "sound/voice/complionator/[key].ogg", 100, 0, 4) - cooldown = world.time - - - -// ******************************************************************** +/obj/item/clothing/mask/gas + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply." + icon_state = "gas_alt" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + w_class = WEIGHT_CLASS_NORMAL + item_state = "gas_alt" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' + ) + +// **** Welding gas mask **** + +/obj/item/clothing/mask/gas/welding + name = "welding mask" + desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." + icon_state = "weldingmask" + item_state = "weldingmask" + materials = list(MAT_METAL=4000, MAT_GLASS=2000) + flash_protect = 2 + tint = 2 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 55) + origin_tech = "materials=2;engineering=3" + actions_types = list(/datum/action/item_action/toggle) + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSEYES + visor_flags_inv = HIDEEYES + resistance_flags = FIRE_PROOF + +/obj/item/clothing/mask/gas/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/mask/gas/explorer + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + icon_state = "gas_mining" + actions_types = list(/datum/action/item_action/adjust) + armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40) + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi' + ) + +/obj/item/clothing/mask/gas/explorer/attack_self(mob/user) + adjustmask(user) + +/obj/item/clothing/mask/gas/explorer/adjustmask(user) + ..() + w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL + +/obj/item/clothing/mask/gas/explorer/folded/Initialize() + . = ..() + adjustmask() + +//Bane gas mask +/obj/item/clothing/mask/banemask + name = "bane mask" + desc = "Only when the station is in flames, do you have my permission to robust." + icon_state = "bane_mask" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + w_class = WEIGHT_CLASS_NORMAL + item_state = "bane_mask" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + + +//Plague Dr suit can be found in clothing/suits/bio.dm +/obj/item/clothing/mask/gas/plaguedoctor + name = "plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply." + icon_state = "plaguedoctor" + item_state = "gas_mask" + armor = list("melee" = 0, "bullet" = 0, "laser" = 2, "energy" = 2, "bomb" = 0, "bio" = 75, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/mask/gas/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + +/obj/item/clothing/mask/gas/syndicate + name = "syndicate mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + strip_delay = 60 + +/obj/item/clothing/mask/gas/clown_hat + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." + icon_state = "clown" + item_state = "clown_hat" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + resistance_flags = FLAMMABLE + dog_fashion = /datum/dog_fashion/head/clown + +/obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) + + var/mob/M = usr + var/list/options = list() + options["True Form"] = "clown" + options["The Feminist"] = "sexyclown" + options["The Madman"] = "joker" + options["The Rainbow Color"] ="rainbow" + + var/choice = input(M,"To what form do you wish to Morph this mask?","Morph Mask") in options + + if(src && choice && !M.stat && in_range(M,src)) + icon_state = options[choice] + to_chat(M, "Your Clown Mask has now morphed into [choice], all praise the Honk Mother!") + return 1 + +/obj/item/clothing/mask/gas/clown_hat/sexy + name = "sexy-clown wig and mask" + desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." + icon_state = "sexyclown" + item_state = "sexyclown" + +/obj/item/clothing/mask/gas/clownwiz + name = "wizard clown wig and mask" + desc = "Some pranksters are truly magical." + icon_state = "wizzclown" + item_state = "wizzclown" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + flags_inv = HIDEEARS | HIDEEYES + magical = TRUE + +/obj/item/clothing/mask/gas/clown_hat/nodrop + flags = NODROP + +/obj/item/clothing/mask/gas/mime + name = "mime mask" + desc = "The traditional mime's mask. It has an eerie facial posture." + icon_state = "mime" + item_state = "mime" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/mime/nodrop + flags = NODROP + +/obj/item/clothing/mask/gas/monkeymask + name = "monkey mask" + desc = "A mask used when acting as a monkey." + icon_state = "monkeymask" + item_state = "monkeymask" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/sexymime + name = "sexy mime mask" + desc = "A traditional female mime's mask." + icon_state = "sexymime" + item_state = "sexymime" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/cyborg + name = "cyborg visor" + desc = "Beep boop" + icon_state = "death" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/owl_mask + name = "owl mask" + desc = "Twoooo!" + icon_state = "owl" + resistance_flags = FLAMMABLE + actions_types = list(/datum/action/item_action/hoot) + +/obj/item/clothing/mask/gas/owl_mask/super_hero + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP + +/obj/item/clothing/mask/gas/owl_mask/attack_self() + hoot() + +/obj/item/clothing/mask/gas/owl_mask/proc/hoot() + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + playsound(src.loc, 'sound/creatures/hoot.ogg', 50, 1) + cooldown = world.time + +// ******************************************************************** + +// **** Security gas mask **** + +/obj/item/clothing/mask/gas/sechailer + name = "security gas mask" + desc = "A standard issue Security gas mask with integrated 'Compli-o-nator 3000' device, plays over a dozen pre-recorded compliance phrases designed to get scumbags to stand still whilst you taze them. Do not tamper with the device." + icon_state = "sechailer" + item_state = "sechailer" + var/phrase = 1 + var/aggressiveness = 1 + var/safety = 1 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase) + var/phrase_list = list( + + "halt" = "HALT! HALT! HALT! HALT!", + "bobby" = "Stop in the name of the Law.", + "compliance" = "Compliance is in your best interest.", + "justice" = "Prepare for justice!", + "running" = "Running will only increase your sentence.", + "dontmove" = "Don't move, Creep!", + "floor" = "Down on the floor, Creep!", + "robocop" = "Dead or alive you're coming with me.", + "god" = "God made today for the crooks we could not catch yesterday.", + "freeze" = "Freeze, Scum Bag!", + "imperial" = "Stop right there, criminal scum!", + "bash" = "Stop or I'll bash you.", + "harry" = "Go ahead, make my day.", + "asshole" = "Stop breaking the law, asshole.", + "stfu" = "You have the right to shut the fuck up", + "shutup" = "Shut up crime!", + "super" = "Face the wrath of the golden bolt.", + "dredd" = "I am, the LAW!" + ) +/obj/item/clothing/mask/gas/sechailer/hos + name = "\improper HOS SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." + icon_state = "hosmask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/warden + name = "\improper Warden SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." + icon_state = "wardenmask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + + +/obj/item/clothing/mask/gas/sechailer/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." + icon_state = "officermask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/blue + name = "\improper blue SWAT mask" + desc = "A neon blue swat mask, used for demoralizing Greytide in the wild." + icon_state = "blue_sechailer" + item_state = "blue_sechailer" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/cyborg + name = "security hailer" + desc = "A set of recognizable pre-recorded messages for cyborgs to use when apprehending criminals." + icon = 'icons/obj/device.dmi' + icon_state = "taperecorder_idle" + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) + if(actiontype == /datum/action/item_action/halt) + halt() + else if(actiontype == /datum/action/item_action/adjust) + adjustmask(user) + else if(actiontype == /datum/action/item_action/selectphrase) + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + if (!safety) + to_chat(user, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") + return + + switch(aggressiveness) + if(1) + phrase = (phrase < 6) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(2) + phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(3) + phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(4) + phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + else + to_chat(user, "It's broken.") + +/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/screwdriver)) + switch(aggressiveness) + if(1) + to_chat(user, "You set the aggressiveness restrictor to the second position.") + aggressiveness = 2 + phrase = 7 + if(2) + to_chat(user, "You set the aggressiveness restrictor to the third position.") + aggressiveness = 3 + phrase = 13 + if(3) + to_chat(user, "You set the aggressiveness restrictor to the fourth position.") + aggressiveness = 4 + phrase = 1 + if(4) + to_chat(user, "You set the aggressiveness restrictor to the first position.") + aggressiveness = 1 + phrase = 1 + if(5) + to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") + else if(istype(W, /obj/item/wirecutters)) + if(aggressiveness != 5) + to_chat(user, "You broke it!") + aggressiveness = 5 + else + ..() + +/obj/item/clothing/mask/gas/sechailer/attack_self() + halt() + +/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user as mob) + if(safety) + safety = 0 + to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") + else + return + +/obj/item/clothing/mask/gas/sechailer/proc/halt() + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + if(!safety) + message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src.loc, 'sound/voice/binsult.ogg', 100, 0, 4) + cooldown = world.time + return + + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src.loc, "sound/voice/complionator/[key].ogg", 100, 0, 4) + cooldown = world.time + + + +// ******************************************************************** diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 9a45b7e888c7..5cc891b56af6 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -1,523 +1,523 @@ -/obj/item/clothing/mask/muzzle - name = "muzzle" - desc = "To stop that awful noise." - icon_state = "muzzle" - item_state = "muzzle" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - put_on_delay = 20 - var/resist_time = 0 //deciseconds of how long you need to gnaw to get rid of the gag, 0 to make it impossible to remove - var/mute = MUZZLE_MUTE_ALL - var/security_lock = FALSE // Requires brig access to remove 0 - Remove as normal - var/locked = FALSE //Indicates if a mask is locked, should always start as 0. - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi' - ) - -// Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/user as mob) - if(user.wear_mask == src && !user.IsAdvancedToolUser()) - return 0 - else if(security_lock && locked) - if(do_unlock(user)) - visible_message("[user] unlocks [user.p_their()] [src.name].", \ - "[user] unlocks [user.p_their()] [src.name].") - ..() - return 1 - -/obj/item/clothing/mask/muzzle/proc/do_break() - if(security_lock) - security_lock = FALSE - locked = FALSE - flags &= ~NODROP - desc += " This one appears to be broken." - return TRUE - else - return FALSE - -/obj/item/clothing/mask/muzzle/proc/do_unlock(mob/living/carbon/human/user) - if(istype(user.get_inactive_hand(), /obj/item/card/emag)) - to_chat(user, "The lock vibrates as the card forces its locking system open.") - do_break() - return TRUE - else if(ACCESS_BRIG in user.get_access()) - to_chat(user, "The muzzle unlocks with a click.") - locked = FALSE - flags &= ~NODROP - return TRUE - - to_chat(user, "You must be wearing a security ID card or have one in your inactive hand to remove the muzzle.") - return FALSE - -/obj/item/clothing/mask/muzzle/proc/do_lock(mob/living/carbon/human/user) - if(security_lock) - locked = TRUE - flags |= NODROP - return TRUE - return FALSE - -/obj/item/clothing/mask/muzzle/Topic(href, href_list) - ..() - if(href_list["locked"]) - var/mob/living/carbon/wearer = locate(href_list["locked"]) - var/success = 0 - if(ishuman(usr)) - visible_message("[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].", \ - "[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].") - if(do_mob(usr, wearer, POCKET_STRIP_DELAY)) - if(locked) - success = do_unlock(usr) - else - success = do_lock(usr) - if(success) - visible_message("[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].", \ - "[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].") - if(usr.machine == wearer && in_range(src, usr)) - wearer.show_inv(usr) - else - to_chat(usr, "You lack the ability to manipulate the lock.") - - -/obj/item/clothing/mask/muzzle/tapegag - name = "tape gag" - desc = "MHPMHHH!" - icon_state = "tapegag" - item_state = null - w_class = WEIGHT_CLASS_TINY - resist_time = 150 - mute = MUZZLE_MUTE_MUFFLE - flags = DROPDEL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi' - ) - -/obj/item/clothing/mask/muzzle/tapegag/dropped(mob/user) - var/obj/item/trash/tapetrash/TT = new - transfer_fingerprints_to(TT) - user.transfer_fingerprints_to(TT) - user.put_in_active_hand(TT) - playsound(src, 'sound/items/poster_ripped.ogg', 40, 1) - user.emote("scream") - ..() - -/obj/item/clothing/mask/muzzle/safety - name = "safety muzzle" - desc = "A muzzle designed to prevent biting." - icon_state = "muzzle_secure" - item_state = "muzzle_secure" - resist_time = 0 - mute = MUZZLE_MUTE_NONE - security_lock = TRUE - locked = FALSE - materials = list(MAT_METAL=500, MAT_GLASS=50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/muzzle/safety/shock - name = "shock muzzle" - desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." - var/obj/item/assembly/trigger = null - origin_tech = "materials=1;engineering=1" - materials = list(MAT_METAL=500, MAT_GLASS=50) - -/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) - if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) - to_chat(user, "Something is already attached to [src].") - return FALSE - if(!user.drop_item()) - to_chat(user, "You are unable to insert [W] into [src].") - return FALSE - trigger = W - trigger.forceMove(src) - trigger.master = src - trigger.holder = src - to_chat(user, "You attach the [W] to [src].") - return TRUE - else if(istype(W, /obj/item/assembly)) - to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") - return FALSE - - return ..() - -/obj/item/clothing/mask/muzzle/safety/shock/screwdriver_act(mob/user, obj/item/I) - if(!trigger) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You remove [trigger] from [src].") - trigger.forceMove(get_turf(user)) - trigger.master = null - trigger.holder = null - trigger = null - -/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) - if(istype(C)) - if(isliving(C.loc)) - return C.loc - else if(isliving(loc)) - return loc - return FALSE - -/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - var/mob/M = can_shock(loc) - if(M) - to_chat(M, "You feel a sharp shock!") - do_sparks(3, 1, M) - - M.Weaken(5) - M.Stuttering(1) - M.Jitter(20) - return - -/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) - if(trigger) - trigger.HasProximity(AM) - - -/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) - if(trigger) - trigger.hear_talk(M, message_pieces) - -/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) - if(trigger) - trigger.hear_message(M, msg) - - - -/obj/item/clothing/mask/surgical - name = "sterile mask" - desc = "A sterile mask designed to help prevent the spread of diseases." - icon_state = "sterile" - item_state = "sterile" - w_class = WEIGHT_CLASS_TINY - flags_cover = MASKCOVERSMOUTH - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.01 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 25, "rad" = 0, "fire" = 0, "acid" = 0) - actions_types = list(/datum/action/item_action/adjust) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - - -/obj/item/clothing/mask/surgical/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/fakemoustache - name = "completely real moustache" - desc = "moustache is totally real." - icon_state = "fake-moustache" - flags_inv = HIDEFACE - actions_types = list(/datum/action/item_action/pontificate) - dog_fashion = /datum/dog_fashion/head/not_ian - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/fakemoustache/attack_self(mob/user) - pontificate(user) - -/obj/item/clothing/mask/fakemoustache/item_action_slot_check(slot) - if(slot == slot_wear_mask) - return 1 - -/obj/item/clothing/mask/fakemoustache/proc/pontificate(mob/user) - user.visible_message("\ [user] twirls [user.p_their()] moustache and laughs [pick("fiendishly","maniacally","diabolically","evilly")]!") - -//scarves (fit in in mask slot) - -/obj/item/clothing/mask/bluescarf - name = "blue neck scarf" - desc = "A blue neck scarf." - icon_state = "blueneckscarf" - item_state = "blueneckscarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - - -/obj/item/clothing/mask/redscarf - name = "red scarf" - desc = "A red and white checkered neck scarf." - icon_state = "redwhite_scarf" - item_state = "redwhite_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/greenscarf - name = "green scarf" - desc = "A green neck scarf." - icon_state = "green_scarf" - item_state = "green_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/ninjascarf - name = "ninja scarf" - desc = "A stealthy, dark scarf." - icon_state = "ninja_scarf" - item_state = "ninja_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - - -/obj/item/clothing/mask/pig - name = "pig mask" - desc = "A rubber pig mask." - icon_state = "pig" - item_state = "pig" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - - -/obj/item/clothing/mask/horsehead - name = "horse head mask" - desc = "A mask made of soft vinyl and latex, representing the head of a horse." - icon_state = "horsehead" - item_state = "horsehead" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - var/voicechange = 0 - var/temporaryname = " the Horse" - var/originalname = "" - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/horsehead/equipped(mob/user, slot) - if(flags & NODROP) //cursed masks only - originalname = user.real_name - if(!user.real_name || user.real_name == "Unknown") - user.real_name = "A Horse With No Name" //it felt good to be out of the rain - else - user.real_name = "[user.name][temporaryname]" - ..() - -/obj/item/clothing/mask/horsehead/dropped() //this really shouldn't happen, but call it extreme caution - if(flags & NODROP) - goodbye_horses(loc) - ..() - -/obj/item/clothing/mask/horsehead/Destroy() - if(flags & NODROP) - goodbye_horses(loc) - return ..() - -/obj/item/clothing/mask/horsehead/proc/goodbye_horses(mob/user) //I'm flying over you - if(!ismob(user)) - return - if(user.real_name == "[originalname][temporaryname]" || user.real_name == "A Horse With No Name") //if it's somehow changed while the mask is on it doesn't revert - user.real_name = originalname - -/obj/item/clothing/mask/face - flags_inv = HIDEFACE - flags_cover = MASKCOVERSMOUTH - -/obj/item/clothing/mask/face/rat - name = "rat mask" - desc = "A mask made of soft vinyl and latex, representing the head of a rat." - icon_state = "rat" - item_state = "rat" - -/obj/item/clothing/mask/face/fox - name = "fox mask" - desc = "A mask made of soft vinyl and latex, representing the head of a fox." - icon_state = "fox" - item_state = "fox" - -/obj/item/clothing/mask/face/bee - name = "bee mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bee." - icon_state = "bee" - item_state = "bee" - -/obj/item/clothing/mask/face/bear - name = "bear mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bear." - icon_state = "bear" - item_state = "bear" - -/obj/item/clothing/mask/face/bat - name = "bat mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bat." - icon_state = "bat" - item_state = "bat" - -/obj/item/clothing/mask/face/raven - name = "raven mask" - desc = "A mask made of soft vinyl and latex, representing the head of a raven." - icon_state = "raven" - item_state = "raven" - -/obj/item/clothing/mask/face/jackal - name = "jackal mask" - desc = "A mask made of soft vinyl and latex, representing the head of a jackal." - icon_state = "jackal" - item_state = "jackal" - -/obj/item/clothing/mask/face/tribal - name = "tribal mask" - desc = "A mask carved out of wood, detailed carefully by hand." - icon_state = "bumba" - item_state = "bumba" - -/obj/item/clothing/mask/fawkes - name = "Guy Fawkes mask" - desc = "A mask designed to help you remember a specific date." - icon_state = "fawkes" - item_state = "fawkes" - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - -/obj/item/clothing/mask/gas/clown_hat/pennywise - name = "Pennywise Mask" - desc = "It's the eater of worlds, and of children." - icon_state = "pennywise_mask" - item_state = "pennywise_mask" - - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - -// Bandanas -/obj/item/clothing/mask/bandana - name = "bandana" - desc = "A colorful bandana." - flags_inv = HIDEFACE - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_MASK - adjusted_flags = SLOT_HEAD - icon_state = "bandbotany" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - actions_types = list(/datum/action/item_action/adjust) - -/obj/item/clothing/mask/bandana/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/bandana/red - name = "red bandana" - icon_state = "bandred" - item_color = "red" - desc = "It's a red bandana." - -/obj/item/clothing/mask/bandana/blue - name = "blue bandana" - icon_state = "bandblue" - item_color = "blue" - desc = "It's a blue bandana." - -/obj/item/clothing/mask/bandana/gold - name = "gold bandana" - icon_state = "bandgold" - item_color = "yellow" - desc = "It's a gold bandana." - -/obj/item/clothing/mask/bandana/green - name = "green bandana" - icon_state = "bandgreen" - item_color = "green" - desc = "It's a green bandana." - -/obj/item/clothing/mask/bandana/orange - name = "orange bandana" - icon_state = "bandorange" - item_color = "orange" - desc = "It's an orange bandana." - -/obj/item/clothing/mask/bandana/purple - name = "purple bandana" - icon_state = "bandpurple" - item_color = "purple" - desc = "It's a purple bandana." - -/obj/item/clothing/mask/bandana/botany - name = "botany bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "bandbotany" - -/obj/item/clothing/mask/bandana/skull - name = "skull bandana" - desc = "It's a black bandana with a skull pattern." - icon_state = "bandskull" - -/obj/item/clothing/mask/bandana/black - name = "black bandana" - icon_state = "bandblack" - item_color = "black" - desc = "It's a black bandana." - -/obj/item/clothing/mask/bandana/durathread - name = "durathread bandana" - desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..." - icon_state = "banddurathread" - -/obj/item/clothing/mask/cursedclown - name = "cursed clown mask" - desc = "This is a very, very odd looking mask." - icon = 'icons/goonstation/objects/clothing/mask.dmi' - icon_state = "cursedclown" - item_state = "cclown_hat" - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - icon_override = 'icons/goonstation/mob/clothing/mask.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP | AIRTIGHT - flags_cover = MASKCOVERSMOUTH - -/obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) - ..() - var/mob/living/carbon/human/H = user - if(istype(H) && slot == slot_wear_mask) - to_chat(H, "[src] grips your face!") - if(H.mind && H.mind.assigned_role != "Cluwne") - H.makeCluwne() - -/obj/item/clothing/mask/cursedclown/suicide_act(mob/user) - user.visible_message("[user] gazes into the eyes of [src]. [src] gazes back!") - spawn(10) - if(user) - user.gib() - return OBLITERATION +/obj/item/clothing/mask/muzzle + name = "muzzle" + desc = "To stop that awful noise." + icon_state = "muzzle" + item_state = "muzzle" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + put_on_delay = 20 + var/resist_time = 0 //deciseconds of how long you need to gnaw to get rid of the gag, 0 to make it impossible to remove + var/mute = MUZZLE_MUTE_ALL + var/security_lock = FALSE // Requires brig access to remove 0 - Remove as normal + var/locked = FALSE //Indicates if a mask is locked, should always start as 0. + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi' + ) + +// Clumsy folks can't take the mask off themselves. +/obj/item/clothing/mask/muzzle/attack_hand(mob/user as mob) + if(user.wear_mask == src && !user.IsAdvancedToolUser()) + return 0 + else if(security_lock && locked) + if(do_unlock(user)) + visible_message("[user] unlocks [user.p_their()] [src.name].", \ + "[user] unlocks [user.p_their()] [src.name].") + ..() + return 1 + +/obj/item/clothing/mask/muzzle/proc/do_break() + if(security_lock) + security_lock = FALSE + locked = FALSE + flags &= ~NODROP + desc += " This one appears to be broken." + return TRUE + else + return FALSE + +/obj/item/clothing/mask/muzzle/proc/do_unlock(mob/living/carbon/human/user) + if(istype(user.get_inactive_hand(), /obj/item/card/emag)) + to_chat(user, "The lock vibrates as the card forces its locking system open.") + do_break() + return TRUE + else if(ACCESS_BRIG in user.get_access()) + to_chat(user, "The muzzle unlocks with a click.") + locked = FALSE + flags &= ~NODROP + return TRUE + + to_chat(user, "You must be wearing a security ID card or have one in your inactive hand to remove the muzzle.") + return FALSE + +/obj/item/clothing/mask/muzzle/proc/do_lock(mob/living/carbon/human/user) + if(security_lock) + locked = TRUE + flags |= NODROP + return TRUE + return FALSE + +/obj/item/clothing/mask/muzzle/Topic(href, href_list) + ..() + if(href_list["locked"]) + var/mob/living/carbon/wearer = locate(href_list["locked"]) + var/success = 0 + if(ishuman(usr)) + visible_message("[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].", \ + "[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].") + if(do_mob(usr, wearer, POCKET_STRIP_DELAY)) + if(locked) + success = do_unlock(usr) + else + success = do_lock(usr) + if(success) + visible_message("[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].", \ + "[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].") + if(usr.machine == wearer && in_range(src, usr)) + wearer.show_inv(usr) + else + to_chat(usr, "You lack the ability to manipulate the lock.") + + +/obj/item/clothing/mask/muzzle/tapegag + name = "tape gag" + desc = "MHPMHHH!" + icon_state = "tapegag" + item_state = null + w_class = WEIGHT_CLASS_TINY + resist_time = 150 + mute = MUZZLE_MUTE_MUFFLE + flags = DROPDEL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi' + ) + +/obj/item/clothing/mask/muzzle/tapegag/dropped(mob/user) + var/obj/item/trash/tapetrash/TT = new + transfer_fingerprints_to(TT) + user.transfer_fingerprints_to(TT) + user.put_in_active_hand(TT) + playsound(src, 'sound/items/poster_ripped.ogg', 40, 1) + user.emote("scream") + ..() + +/obj/item/clothing/mask/muzzle/safety + name = "safety muzzle" + desc = "A muzzle designed to prevent biting." + icon_state = "muzzle_secure" + item_state = "muzzle_secure" + resist_time = 0 + mute = MUZZLE_MUTE_NONE + security_lock = TRUE + locked = FALSE + materials = list(MAT_METAL=500, MAT_GLASS=50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/muzzle/safety/shock + name = "shock muzzle" + desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." + var/obj/item/assembly/trigger = null + origin_tech = "materials=1;engineering=1" + materials = list(MAT_METAL=500, MAT_GLASS=50) + +/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) + if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) + to_chat(user, "Something is already attached to [src].") + return FALSE + if(!user.drop_item()) + to_chat(user, "You are unable to insert [W] into [src].") + return FALSE + trigger = W + trigger.forceMove(src) + trigger.master = src + trigger.holder = src + to_chat(user, "You attach the [W] to [src].") + return TRUE + else if(istype(W, /obj/item/assembly)) + to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") + return FALSE + + return ..() + +/obj/item/clothing/mask/muzzle/safety/shock/screwdriver_act(mob/user, obj/item/I) + if(!trigger) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You remove [trigger] from [src].") + trigger.forceMove(get_turf(user)) + trigger.master = null + trigger.holder = null + trigger = null + +/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) + if(istype(C)) + if(isliving(C.loc)) + return C.loc + else if(isliving(loc)) + return loc + return FALSE + +/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + var/mob/M = can_shock(loc) + if(M) + to_chat(M, "You feel a sharp shock!") + do_sparks(3, 1, M) + + M.Weaken(5) + M.Stuttering(1) + M.Jitter(20) + return + +/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) + if(trigger) + trigger.HasProximity(AM) + + +/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) + if(trigger) + trigger.hear_talk(M, message_pieces) + +/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) + if(trigger) + trigger.hear_message(M, msg) + + + +/obj/item/clothing/mask/surgical + name = "sterile mask" + desc = "A sterile mask designed to help prevent the spread of diseases." + icon_state = "sterile" + item_state = "sterile" + w_class = WEIGHT_CLASS_TINY + flags_cover = MASKCOVERSMOUTH + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.01 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 25, "rad" = 0, "fire" = 0, "acid" = 0) + actions_types = list(/datum/action/item_action/adjust) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + + +/obj/item/clothing/mask/surgical/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/fakemoustache + name = "completely real moustache" + desc = "moustache is totally real." + icon_state = "fake-moustache" + flags_inv = HIDEFACE + actions_types = list(/datum/action/item_action/pontificate) + dog_fashion = /datum/dog_fashion/head/not_ian + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/fakemoustache/attack_self(mob/user) + pontificate(user) + +/obj/item/clothing/mask/fakemoustache/item_action_slot_check(slot) + if(slot == slot_wear_mask) + return 1 + +/obj/item/clothing/mask/fakemoustache/proc/pontificate(mob/user) + user.visible_message("\ [user] twirls [user.p_their()] moustache and laughs [pick("fiendishly","maniacally","diabolically","evilly")]!") + +//scarves (fit in in mask slot) + +/obj/item/clothing/mask/bluescarf + name = "blue neck scarf" + desc = "A blue neck scarf." + icon_state = "blueneckscarf" + item_state = "blueneckscarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + + +/obj/item/clothing/mask/redscarf + name = "red scarf" + desc = "A red and white checkered neck scarf." + icon_state = "redwhite_scarf" + item_state = "redwhite_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/greenscarf + name = "green scarf" + desc = "A green neck scarf." + icon_state = "green_scarf" + item_state = "green_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/ninjascarf + name = "ninja scarf" + desc = "A stealthy, dark scarf." + icon_state = "ninja_scarf" + item_state = "ninja_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + + +/obj/item/clothing/mask/pig + name = "pig mask" + desc = "A rubber pig mask." + icon_state = "pig" + item_state = "pig" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + + +/obj/item/clothing/mask/horsehead + name = "horse head mask" + desc = "A mask made of soft vinyl and latex, representing the head of a horse." + icon_state = "horsehead" + item_state = "horsehead" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + var/voicechange = 0 + var/temporaryname = " the Horse" + var/originalname = "" + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/horsehead/equipped(mob/user, slot) + if(flags & NODROP) //cursed masks only + originalname = user.real_name + if(!user.real_name || user.real_name == "Unknown") + user.real_name = "A Horse With No Name" //it felt good to be out of the rain + else + user.real_name = "[user.name][temporaryname]" + ..() + +/obj/item/clothing/mask/horsehead/dropped() //this really shouldn't happen, but call it extreme caution + if(flags & NODROP) + goodbye_horses(loc) + ..() + +/obj/item/clothing/mask/horsehead/Destroy() + if(flags & NODROP) + goodbye_horses(loc) + return ..() + +/obj/item/clothing/mask/horsehead/proc/goodbye_horses(mob/user) //I'm flying over you + if(!ismob(user)) + return + if(user.real_name == "[originalname][temporaryname]" || user.real_name == "A Horse With No Name") //if it's somehow changed while the mask is on it doesn't revert + user.real_name = originalname + +/obj/item/clothing/mask/face + flags_inv = HIDEFACE + flags_cover = MASKCOVERSMOUTH + +/obj/item/clothing/mask/face/rat + name = "rat mask" + desc = "A mask made of soft vinyl and latex, representing the head of a rat." + icon_state = "rat" + item_state = "rat" + +/obj/item/clothing/mask/face/fox + name = "fox mask" + desc = "A mask made of soft vinyl and latex, representing the head of a fox." + icon_state = "fox" + item_state = "fox" + +/obj/item/clothing/mask/face/bee + name = "bee mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bee." + icon_state = "bee" + item_state = "bee" + +/obj/item/clothing/mask/face/bear + name = "bear mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bear." + icon_state = "bear" + item_state = "bear" + +/obj/item/clothing/mask/face/bat + name = "bat mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bat." + icon_state = "bat" + item_state = "bat" + +/obj/item/clothing/mask/face/raven + name = "raven mask" + desc = "A mask made of soft vinyl and latex, representing the head of a raven." + icon_state = "raven" + item_state = "raven" + +/obj/item/clothing/mask/face/jackal + name = "jackal mask" + desc = "A mask made of soft vinyl and latex, representing the head of a jackal." + icon_state = "jackal" + item_state = "jackal" + +/obj/item/clothing/mask/face/tribal + name = "tribal mask" + desc = "A mask carved out of wood, detailed carefully by hand." + icon_state = "bumba" + item_state = "bumba" + +/obj/item/clothing/mask/fawkes + name = "Guy Fawkes mask" + desc = "A mask designed to help you remember a specific date." + icon_state = "fawkes" + item_state = "fawkes" + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/mask/gas/clown_hat/pennywise + name = "Pennywise Mask" + desc = "It's the eater of worlds, and of children." + icon_state = "pennywise_mask" + item_state = "pennywise_mask" + + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + +// Bandanas +/obj/item/clothing/mask/bandana + name = "bandana" + desc = "A colorful bandana." + flags_inv = HIDEFACE + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_MASK + adjusted_flags = SLOT_HEAD + icon_state = "bandbotany" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + actions_types = list(/datum/action/item_action/adjust) + +/obj/item/clothing/mask/bandana/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/bandana/red + name = "red bandana" + icon_state = "bandred" + item_color = "red" + desc = "It's a red bandana." + +/obj/item/clothing/mask/bandana/blue + name = "blue bandana" + icon_state = "bandblue" + item_color = "blue" + desc = "It's a blue bandana." + +/obj/item/clothing/mask/bandana/gold + name = "gold bandana" + icon_state = "bandgold" + item_color = "yellow" + desc = "It's a gold bandana." + +/obj/item/clothing/mask/bandana/green + name = "green bandana" + icon_state = "bandgreen" + item_color = "green" + desc = "It's a green bandana." + +/obj/item/clothing/mask/bandana/orange + name = "orange bandana" + icon_state = "bandorange" + item_color = "orange" + desc = "It's an orange bandana." + +/obj/item/clothing/mask/bandana/purple + name = "purple bandana" + icon_state = "bandpurple" + item_color = "purple" + desc = "It's a purple bandana." + +/obj/item/clothing/mask/bandana/botany + name = "botany bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "bandbotany" + +/obj/item/clothing/mask/bandana/skull + name = "skull bandana" + desc = "It's a black bandana with a skull pattern." + icon_state = "bandskull" + +/obj/item/clothing/mask/bandana/black + name = "black bandana" + icon_state = "bandblack" + item_color = "black" + desc = "It's a black bandana." + +/obj/item/clothing/mask/bandana/durathread + name = "durathread bandana" + desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..." + icon_state = "banddurathread" + +/obj/item/clothing/mask/cursedclown + name = "cursed clown mask" + desc = "This is a very, very odd looking mask." + icon = 'icons/goonstation/objects/clothing/mask.dmi' + icon_state = "cursedclown" + item_state = "cclown_hat" + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + icon_override = 'icons/goonstation/mob/clothing/mask.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + flags = NODROP | AIRTIGHT + flags_cover = MASKCOVERSMOUTH + +/obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) + ..() + var/mob/living/carbon/human/H = user + if(istype(H) && slot == slot_wear_mask) + to_chat(H, "[src] grips your face!") + if(H.mind && H.mind.assigned_role != "Cluwne") + H.makeCluwne() + +/obj/item/clothing/mask/cursedclown/suicide_act(mob/user) + user.visible_message("[user] gazes into the eyes of [src]. [src] gazes back!") + spawn(10) + if(user) + user.gib() + return OBLITERATION diff --git a/code/modules/clothing/patreon/glasses.dm b/code/modules/clothing/patreon/glasses.dm index f52d54f7df2a..08d5d6b0af8f 100644 --- a/code/modules/clothing/patreon/glasses.dm +++ b/code/modules/clothing/patreon/glasses.dm @@ -18,4 +18,4 @@ name = "Spiky Green-tinted Shades" desc = "Fight the power!" icon_state = "garm" - item_state = "garm" \ No newline at end of file + item_state = "garm" diff --git a/code/modules/clothing/patreon/hats.dm b/code/modules/clothing/patreon/hats.dm index abee0cb94661..8b769ba4150b 100644 --- a/code/modules/clothing/patreon/hats.dm +++ b/code/modules/clothing/patreon/hats.dm @@ -60,4 +60,4 @@ sprite_sheets = list( "Vox" = 'icons/mob/species/vox/head.dmi' - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index f864d3bf8f69..0bf54e8692f9 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,106 +1,106 @@ -/obj/item/clothing/shoes/black - name = "black shoes" - icon_state = "black" - item_color = "black" - desc = "A pair of black shoes." - - cold_protection = FEET - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - - redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. - -/obj/item/clothing/shoes/black/greytide - flags = NODROP - -/obj/item/clothing/shoes/brown - name = "brown shoes" - desc = "A pair of brown shoes." - icon_state = "brown" - - captain - item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. - hop - item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. - ce - item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. - rd - item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. - -/obj/item/clothing/shoes/blue - name = "blue shoes" - icon_state = "blue" - item_color = "blue" - -/obj/item/clothing/shoes/green - name = "green shoes" - icon_state = "green" - item_color = "green" - -/obj/item/clothing/shoes/yellow - name = "yellow shoes" - icon_state = "yellow" - item_color = "yellow" - -/obj/item/clothing/shoes/purple - name = "purple shoes" - icon_state = "purple" - item_color = "purple" - -/obj/item/clothing/shoes/brown - name = "brown shoes" - icon_state = "brown" - item_color = "brown" - -/obj/item/clothing/shoes/red - name = "red shoes" - desc = "Stylish red shoes." - icon_state = "red" - item_color = "red" - -/obj/item/clothing/shoes/white - name = "white shoes" - icon_state = "white" - permeability_coefficient = 0.01 - item_color = "white" - -/obj/item/clothing/shoes/leather - name = "leather shoes" - desc = "A sturdy pair of leather shoes." - icon_state = "leather" - item_color = "leather" - -/obj/item/clothing/shoes/rainbow - name = "rainbow shoes" - desc = "Very gay shoes." - icon_state = "rain_bow" - item_color = "rainbow" - -/obj/item/clothing/shoes/orange - name = "orange shoes" - icon_state = "orange" - item_color = "orange" - -/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) - if(src.chained) - src.chained = null - src.slowdown = SHOES_SLOWDOWN - new /obj/item/restraints/handcuffs( user.loc ) - src.icon_state = "orange" - return - -/obj/item/clothing/shoes/orange/attackby(obj/H, loc, params) - ..() - if(istype(H, /obj/item/restraints/handcuffs) && !chained && !(H.flags & NODROP)) - if(src.icon_state != "orange") return - qdel(H) - src.chained = 1 - src.slowdown = 15 - src.icon_state = "orange1" - return +/obj/item/clothing/shoes/black + name = "black shoes" + icon_state = "black" + item_color = "black" + desc = "A pair of black shoes." + + cold_protection = FEET + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + + redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. + +/obj/item/clothing/shoes/black/greytide + flags = NODROP + +/obj/item/clothing/shoes/brown + name = "brown shoes" + desc = "A pair of brown shoes." + icon_state = "brown" + + captain + item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. + hop + item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. + ce + item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. + rd + item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. + cmo + item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. + cmo + item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/blue + name = "blue shoes" + icon_state = "blue" + item_color = "blue" + +/obj/item/clothing/shoes/green + name = "green shoes" + icon_state = "green" + item_color = "green" + +/obj/item/clothing/shoes/yellow + name = "yellow shoes" + icon_state = "yellow" + item_color = "yellow" + +/obj/item/clothing/shoes/purple + name = "purple shoes" + icon_state = "purple" + item_color = "purple" + +/obj/item/clothing/shoes/brown + name = "brown shoes" + icon_state = "brown" + item_color = "brown" + +/obj/item/clothing/shoes/red + name = "red shoes" + desc = "Stylish red shoes." + icon_state = "red" + item_color = "red" + +/obj/item/clothing/shoes/white + name = "white shoes" + icon_state = "white" + permeability_coefficient = 0.01 + item_color = "white" + +/obj/item/clothing/shoes/leather + name = "leather shoes" + desc = "A sturdy pair of leather shoes." + icon_state = "leather" + item_color = "leather" + +/obj/item/clothing/shoes/rainbow + name = "rainbow shoes" + desc = "Very gay shoes." + icon_state = "rain_bow" + item_color = "rainbow" + +/obj/item/clothing/shoes/orange + name = "orange shoes" + icon_state = "orange" + item_color = "orange" + +/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) + if(src.chained) + src.chained = null + src.slowdown = SHOES_SLOWDOWN + new /obj/item/restraints/handcuffs( user.loc ) + src.icon_state = "orange" + return + +/obj/item/clothing/shoes/orange/attackby(obj/H, loc, params) + ..() + if(istype(H, /obj/item/restraints/handcuffs) && !chained && !(H.flags & NODROP)) + if(src.icon_state != "orange") return + qdel(H) + src.chained = 1 + src.slowdown = 15 + src.icon_state = "orange1" + return diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 7b0baef536fb..916cb8e89d40 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -1,118 +1,118 @@ -/obj/item/clothing/shoes/magboots - desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." - name = "magboots" - icon_state = "magboots0" - origin_tech = "materials=3;magnets=4;engineering=4" - var/magboot_state = "magboots" - var/magpulse = 0 - var/slowdown_active = 2 - var/slowdown_passive = SHOES_SLOWDOWN - var/magpulse_name = "mag-pulse traction system" - actions_types = list(/datum/action/item_action/toggle) - strip_delay = 70 - put_on_delay = 70 - resistance_flags = FIRE_PROOF - -/obj/item/clothing/shoes/magboots/attack_self(mob/user) - if(magpulse) - flags &= ~NOSLIP - slowdown = slowdown_passive - else - flags |= NOSLIP - slowdown = slowdown_active - magpulse = !magpulse - icon_state = "[magboot_state][magpulse]" - to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].") - user.update_inv_shoes() //so our mob-overlays update - user.update_gravity(user.mob_has_gravity()) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/shoes/magboots/negates_gravity() - return flags & NOSLIP - -/obj/item/clothing/shoes/magboots/examine(mob/user) - . = ..() - . += "Its [magpulse_name] appears to be [magpulse ? "enabled" : "disabled"]." - - -/obj/item/clothing/shoes/magboots/advance - desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer." - name = "advanced magboots" - icon_state = "advmag0" - magboot_state = "advmag" - slowdown_active = SHOES_SLOWDOWN - origin_tech = null - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/magboots/syndie - desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders." - name = "blood-red magboots" - icon_state = "syndiemag0" - magboot_state = "syndiemag" - origin_tech = "magnets=4;syndicate=2" - -obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team - desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." - name = "advanced blood-red magboots" - slowdown_active = SHOES_SLOWDOWN - -/obj/item/clothing/shoes/magboots/clown - desc = "The prankster's standard-issue clowning shoes. Damn they're huge! There's a red light on the side." - name = "clown shoes" - icon_state = "clownmag0" - magboot_state = "clownmag" - item_state = "clown_shoes" - slowdown = SHOES_SLOWDOWN+1 - slowdown_active = SHOES_SLOWDOWN+1 - slowdown_passive = SHOES_SLOWDOWN+1 - magpulse_name = "honk-powered traction system" - item_color = "clown" - silence_steps = 1 - shoe_sound = "clownstep" - origin_tech = "magnets=4;syndicate=2" - var/enabled_waddle = TRUE - var/datum/component/waddle - -/obj/item/clothing/shoes/magboots/clown/equipped(mob/user, slot) - . = ..() - if(slot == slot_shoes && enabled_waddle) - waddle = user.AddComponent(/datum/component/waddling) - -/obj/item/clothing/shoes/magboots/clown/dropped(mob/user) - . = ..() - QDEL_NULL(waddle) - -/obj/item/clothing/shoes/magboots/clown/CtrlClick(mob/living/user) - if(!isliving(user)) - return - if(user.get_active_hand() != src) - to_chat(user, "You must hold [src] in your hand to do this.") - return - if(!enabled_waddle) - to_chat(user, "You switch off the waddle dampeners!") - enabled_waddle = TRUE - else - to_chat(user, "You switch on the waddle dampeners!") - enabled_waddle = FALSE - -/obj/item/clothing/shoes/magboots/wizard //bundled with the wiz hardsuit - name = "boots of gripping" - desc = "These magical boots, once activated, will stay gripped to any surface without slowing you down." - icon_state = "wizmag0" - magboot_state = "wizmag" - slowdown_active = SHOES_SLOWDOWN //wiz hardsuit already slows you down, no need to double it - magpulse_name = "gripping ability" - magical = TRUE - -/obj/item/clothing/shoes/magboots/wizard/attack_self(mob/user) - if(user) - if(user.mind in SSticker.mode.wizards) - if(magpulse) //faint blue light when shoes are turned on gives a reason to turn them off when not needed in maint - set_light(0) - else - set_light(2, 1, LIGHT_COLOR_LIGHTBLUE) - ..() - else - to_chat(user, "You poke the gem on [src]. Nothing happens.") \ No newline at end of file +/obj/item/clothing/shoes/magboots + desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." + name = "magboots" + icon_state = "magboots0" + origin_tech = "materials=3;magnets=4;engineering=4" + var/magboot_state = "magboots" + var/magpulse = 0 + var/slowdown_active = 2 + var/slowdown_passive = SHOES_SLOWDOWN + var/magpulse_name = "mag-pulse traction system" + actions_types = list(/datum/action/item_action/toggle) + strip_delay = 70 + put_on_delay = 70 + resistance_flags = FIRE_PROOF + +/obj/item/clothing/shoes/magboots/attack_self(mob/user) + if(magpulse) + flags &= ~NOSLIP + slowdown = slowdown_passive + else + flags |= NOSLIP + slowdown = slowdown_active + magpulse = !magpulse + icon_state = "[magboot_state][magpulse]" + to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].") + user.update_inv_shoes() //so our mob-overlays update + user.update_gravity(user.mob_has_gravity()) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/shoes/magboots/negates_gravity() + return flags & NOSLIP + +/obj/item/clothing/shoes/magboots/examine(mob/user) + . = ..() + . += "Its [magpulse_name] appears to be [magpulse ? "enabled" : "disabled"]." + + +/obj/item/clothing/shoes/magboots/advance + desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer." + name = "advanced magboots" + icon_state = "advmag0" + magboot_state = "advmag" + slowdown_active = SHOES_SLOWDOWN + origin_tech = null + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/magboots/syndie + desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders." + name = "blood-red magboots" + icon_state = "syndiemag0" + magboot_state = "syndiemag" + origin_tech = "magnets=4;syndicate=2" + +obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team + desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." + name = "advanced blood-red magboots" + slowdown_active = SHOES_SLOWDOWN + +/obj/item/clothing/shoes/magboots/clown + desc = "The prankster's standard-issue clowning shoes. Damn they're huge! There's a red light on the side." + name = "clown shoes" + icon_state = "clownmag0" + magboot_state = "clownmag" + item_state = "clown_shoes" + slowdown = SHOES_SLOWDOWN+1 + slowdown_active = SHOES_SLOWDOWN+1 + slowdown_passive = SHOES_SLOWDOWN+1 + magpulse_name = "honk-powered traction system" + item_color = "clown" + silence_steps = 1 + shoe_sound = "clownstep" + origin_tech = "magnets=4;syndicate=2" + var/enabled_waddle = TRUE + var/datum/component/waddle + +/obj/item/clothing/shoes/magboots/clown/equipped(mob/user, slot) + . = ..() + if(slot == slot_shoes && enabled_waddle) + waddle = user.AddComponent(/datum/component/waddling) + +/obj/item/clothing/shoes/magboots/clown/dropped(mob/user) + . = ..() + QDEL_NULL(waddle) + +/obj/item/clothing/shoes/magboots/clown/CtrlClick(mob/living/user) + if(!isliving(user)) + return + if(user.get_active_hand() != src) + to_chat(user, "You must hold [src] in your hand to do this.") + return + if(!enabled_waddle) + to_chat(user, "You switch off the waddle dampeners!") + enabled_waddle = TRUE + else + to_chat(user, "You switch on the waddle dampeners!") + enabled_waddle = FALSE + +/obj/item/clothing/shoes/magboots/wizard //bundled with the wiz hardsuit + name = "boots of gripping" + desc = "These magical boots, once activated, will stay gripped to any surface without slowing you down." + icon_state = "wizmag0" + magboot_state = "wizmag" + slowdown_active = SHOES_SLOWDOWN //wiz hardsuit already slows you down, no need to double it + magpulse_name = "gripping ability" + magical = TRUE + +/obj/item/clothing/shoes/magboots/wizard/attack_self(mob/user) + if(user) + if(user.mind in SSticker.mode.wizards) + if(magpulse) //faint blue light when shoes are turned on gives a reason to turn them off when not needed in maint + set_light(0) + else + set_light(2, 1, LIGHT_COLOR_LIGHTBLUE) + ..() + else + to_chat(user, "You poke the gem on [src]. Nothing happens.") diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 9c7d8bac0805..6858355f8762 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -1,346 +1,346 @@ -/obj/item/clothing/shoes/mime - name = "mime shoes" - icon_state = "mime" - item_color = "mime" - -/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses - name = "combat boots" - desc = "High speed, low drag combat boots." - can_cut_open = 1 - icon_state = "jackboots" - item_state = "jackboots" - armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) - strip_delay = 70 - resistance_flags = NONE - -/obj/item/clothing/shoes/combat/swat //overpowered boots for death squads - name = "\improper SWAT shoes" - desc = "High speed, no drag combat boots." - permeability_coefficient = 0.01 - armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) - flags = NOSLIP - -/obj/item/clothing/shoes/sandal - desc = "A pair of rather plain, wooden sandals." - name = "sandals" - icon_state = "wizard" - strip_delay = 50 - put_on_delay = 50 - magical = TRUE - -/obj/item/clothing/shoes/sandal/marisa - desc = "A pair of magic, black shoes." - name = "magic shoes" - icon_state = "black" - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/sandal/magic - name = "magical sandals" - desc = "A pair of sandals imbued with magic." - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/galoshes - desc = "A pair of yellow rubber boots, designed to prevent slipping on wet surfaces." - name = "galoshes" - icon_state = "galoshes" - permeability_coefficient = 0.05 - flags = NOSLIP - slowdown = SHOES_SLOWDOWN+1 - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 75) - -/obj/item/clothing/shoes/galoshes/dry - name = "absorbent galoshes" - desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them." - icon_state = "galoshes_dry" - -/obj/item/clothing/shoes/galoshes/dry/step_action() - var/turf/simulated/t_loc = get_turf(src) - if(istype(t_loc) && t_loc.wet) - t_loc.MakeDry(TURF_WET_WATER) - -/obj/item/clothing/shoes/clown_shoes - desc = "The prankster's standard-issue clowning shoes. Damn they're huge! Ctrl-click to toggle the waddle dampeners!" - name = "clown shoes" - icon_state = "clown" - item_state = "clown_shoes" - slowdown = SHOES_SLOWDOWN+1 - item_color = "clown" - var/footstep = 1 //used for squeeks whilst walking - shoe_sound = "clownstep" - var/enabled_waddle = TRUE - var/datum/component/waddle - -/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) - . = ..() - if(slot == slot_shoes && enabled_waddle) - waddle = user.AddComponent(/datum/component/waddling) - -/obj/item/clothing/shoes/clown_shoes/dropped(mob/user) - . = ..() - QDEL_NULL(waddle) - -/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user) - if(!isliving(user)) - return - if(user.get_active_hand() != src) - to_chat(user, "You must hold [src] in your hand to do this.") - return - if(!enabled_waddle) - to_chat(user, "You switch off the waddle dampeners!") - enabled_waddle = TRUE - else - to_chat(user, "You switch on the waddle dampeners!") - enabled_waddle = FALSE - -/obj/item/clothing/shoes/clown_shoes/nodrop - flags = NODROP - -/obj/item/clothing/shoes/clown_shoes/magical - name = "magical clown shoes" - desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow." - magical = TRUE - -/obj/item/clothing/shoes/jackboots - name = "jackboots" - desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." - can_cut_open = 1 - icon_state = "jackboots" - item_state = "jackboots" - item_color = "hosred" - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - var/footstep = 1 - shoe_sound = "jackboot" - -/obj/item/clothing/shoes/jackboots/jacksandals - name = "jacksandals" - desc = "Nanotrasen-issue Security combat sandals for combat scenarios. They're jacksandals, however that works." - can_cut_open = 0 - icon_state = "jacksandal" - item_color = "jacksandal" - -/obj/item/clothing/shoes/workboots - name = "work boots" - desc = "Thick-soled boots for industrial work environments." - can_cut_open = 1 - icon_state = "workboots" - -/obj/item/clothing/shoes/workboots/mining - name = "mining boots" - desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed." - icon_state = "explorer" - resistance_flags = FIRE_PROOF - -/obj/item/clothing/shoes/winterboots - name = "winter boots" - desc = "Boots lined with 'synthetic' animal fur." - can_cut_open = 1 - icon_state = "winterboots" - cold_protection = FEET|LEGS - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET|LEGS - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - -/obj/item/clothing/shoes/cult - name = "boots" - desc = "A pair of boots worn by the followers of Nar-Sie." - icon_state = "cult" - item_state = "cult" - item_color = "cult" - - cold_protection = FEET - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - -/obj/item/clothing/shoes/cyborg - name = "cyborg boots" - desc = "Shoes for a cyborg costume" - icon_state = "boots" - -/obj/item/clothing/shoes/slippers - name = "bunny slippers" - desc = "Fluffy!" - icon_state = "slippers" - item_state = "slippers" - -/obj/item/clothing/shoes/slippers_worn - name = "worn bunny slippers" - desc = "Fluffy..." - icon_state = "slippers_worn" - item_state = "slippers_worn" - -/obj/item/clothing/shoes/laceup - name = "laceup shoes" - desc = "The height of fashion, and they're pre-polished!" - icon_state = "laceups" - put_on_delay = 50 - -/obj/item/clothing/shoes/roman - name = "roman sandals" - desc = "Sandals with buckled leather straps on it." - icon_state = "roman" - item_state = "roman" - strip_delay = 100 - put_on_delay = 100 - -/obj/item/clothing/shoes/centcom - name = "dress shoes" - desc = "They appear impeccably polished." - icon_state = "laceups" - -/obj/item/clothing/shoes/griffin - name = "griffon boots" - desc = "A pair of costume boots fashioned after bird talons." - icon_state = "griffinboots" - item_state = "griffinboots" - - -/obj/item/clothing/shoes/fluff/noble_boot - name = "noble boots" - desc = "The boots are economically designed to balance function and comfort, so that you can step on peasants without having to worry about blisters. The leather also resists unwanted blood stains." - icon_state = "noble_boot" - item_color = "noble_boot" - item_state = "noble_boot" - -/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/tape_roll) && !silence_steps) - var/obj/item/stack/tape_roll/TR = I - if((!silence_steps || shoe_sound) && TR.use(4)) - silence_steps = TRUE - shoe_sound = null - to_chat(user, "You tape the soles of [src] to silence your footsteps.") - else - return ..() - -/obj/item/clothing/shoes/sandal/white - name = "White Sandals" - desc = "Medical sandals that nerds wear." - icon_state = "medsandal" - item_color = "medsandal" - -/obj/item/clothing/shoes/sandal/fancy - name = "Fancy Sandals" - desc = "FANCY!!." - icon_state = "fancysandal" - item_color = "fancysandal" - -/obj/item/clothing/shoes/cursedclown - name = "cursed clown shoes" - desc = "Moldering clown flip flops. They're neon green for some reason." - icon = 'icons/goonstation/objects/clothing/feet.dmi' - icon_state = "cursedclown" - item_state = "cclown_shoes" - icon_override = 'icons/goonstation/mob/clothing/feet.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - flags = NODROP - shoe_sound = "clownstep" - -/obj/item/clothing/shoes/singery - name = "yellow performer's boots" - desc = "These boots were made for dancing." - icon_state = "ysing" - put_on_delay = 50 - -/obj/item/clothing/shoes/singerb - name = "blue performer's boots" - desc = "These boots were made for dancing." - icon_state = "bsing" - put_on_delay = 50 - -/obj/item/clothing/shoes/cowboy - name = "cowboy boots" - desc = "A pair a' brown boots." - icon_state = "cowboy_brown" - item_color = "cowboy_brown" - -/obj/item/clothing/shoes/cowboy/black - name = "black cowboy boots" - desc = "A pair a' black rustlers' boots" - icon_state = "cowboy_black" - item_color = "cowboy_black" - -/obj/item/clothing/shoes/cowboy/white - name = "white cowboy boots" - desc = "For the rancher in us all." - icon_state = "cowboy_white" - item_color = "cowboy_white" - -/obj/item/clothing/shoes/cowboy/fancy - name = "bilton wrangler boots" - desc = "A pair of authentic haute couture boots from Japanifornia. You doubt they have ever been close to cattle." - icon_state = "cowboy_fancy" - item_color = "cowboy_fancy" - -/obj/item/clothing/shoes/cowboy/pink - name = "pink cowgirl boots" - desc = "For a Rustlin' tustlin' cowgirl." - icon_state = "cowboyboots_pink" - item_color = "cowboyboots_pink" - -/obj/item/clothing/shoes/cowboy/lizard - name = "lizard skin boots" - desc = "You can hear a faint hissing from inside the boots; you hope it is just a mournful ghost." - icon_state = "lizardboots_green" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 0) //lizards like to stay warm - -/obj/item/clothing/shoes/cowboy/lizardmasterwork - name = "\improper Hugs-The-Feet lizard skin boots" - desc = "A pair of masterfully crafted lizard skin boots. Finally a good application for the station's most bothersome inhabitants." - icon_state = "lizardboots_blue" - -/obj/effect/spawner/lootdrop/lizardboots - name = "random lizard boot quality" - desc = "Which ever gets picked, the lizard race loses" - icon = 'icons/obj/clothing/shoes.dmi' - icon_state = "lizardboots_green" - loot = list( - /obj/item/clothing/shoes/cowboy/lizard = 7, - /obj/item/clothing/shoes/cowboy/lizardmasterwork = 1) - -/obj/item/clothing/shoes/footwraps - name = "cloth footwraps" - desc = "A roll of treated canvas used for wrapping claws or paws." - icon_state = "clothwrap" - item_state = "clothwrap" - force = 0 - silence_steps = TRUE - w_class = WEIGHT_CLASS_SMALL - -/obj/item/clothing/shoes/bhop - name = "jump boots" - desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." - icon_state = "jetboots" - item_state = "jetboots" - item_color = "hosred" - resistance_flags = FIRE_PROOF - actions_types = list(/datum/action/item_action/bhop) - permeability_coefficient = 0.05 - can_cut_open = FALSE - var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles - var/jumpspeed = 3 - var/recharging_rate = 60 //default 6 seconds between each dash - var/recharging_time = 0 //time until next dash - -/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action) - if(!isliving(user)) - return - - if(recharging_time > world.time) - to_chat(user, "The boot's internal propulsion needs to recharge still!") - return - - var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction - - if (user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)) - playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) - user.visible_message("[usr] dashes forward into the air!") - recharging_time = world.time + recharging_rate - else - to_chat(user, "Something prevents you from dashing forward!") \ No newline at end of file +/obj/item/clothing/shoes/mime + name = "mime shoes" + icon_state = "mime" + item_color = "mime" + +/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses + name = "combat boots" + desc = "High speed, low drag combat boots." + can_cut_open = 1 + icon_state = "jackboots" + item_state = "jackboots" + armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) + strip_delay = 70 + resistance_flags = NONE + +/obj/item/clothing/shoes/combat/swat //overpowered boots for death squads + name = "\improper SWAT shoes" + desc = "High speed, no drag combat boots." + permeability_coefficient = 0.01 + armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) + flags = NOSLIP + +/obj/item/clothing/shoes/sandal + desc = "A pair of rather plain, wooden sandals." + name = "sandals" + icon_state = "wizard" + strip_delay = 50 + put_on_delay = 50 + magical = TRUE + +/obj/item/clothing/shoes/sandal/marisa + desc = "A pair of magic, black shoes." + name = "magic shoes" + icon_state = "black" + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/sandal/magic + name = "magical sandals" + desc = "A pair of sandals imbued with magic." + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/galoshes + desc = "A pair of yellow rubber boots, designed to prevent slipping on wet surfaces." + name = "galoshes" + icon_state = "galoshes" + permeability_coefficient = 0.05 + flags = NOSLIP + slowdown = SHOES_SLOWDOWN+1 + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 75) + +/obj/item/clothing/shoes/galoshes/dry + name = "absorbent galoshes" + desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them." + icon_state = "galoshes_dry" + +/obj/item/clothing/shoes/galoshes/dry/step_action() + var/turf/simulated/t_loc = get_turf(src) + if(istype(t_loc) && t_loc.wet) + t_loc.MakeDry(TURF_WET_WATER) + +/obj/item/clothing/shoes/clown_shoes + desc = "The prankster's standard-issue clowning shoes. Damn they're huge! Ctrl-click to toggle the waddle dampeners!" + name = "clown shoes" + icon_state = "clown" + item_state = "clown_shoes" + slowdown = SHOES_SLOWDOWN+1 + item_color = "clown" + var/footstep = 1 //used for squeeks whilst walking + shoe_sound = "clownstep" + var/enabled_waddle = TRUE + var/datum/component/waddle + +/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) + . = ..() + if(slot == slot_shoes && enabled_waddle) + waddle = user.AddComponent(/datum/component/waddling) + +/obj/item/clothing/shoes/clown_shoes/dropped(mob/user) + . = ..() + QDEL_NULL(waddle) + +/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user) + if(!isliving(user)) + return + if(user.get_active_hand() != src) + to_chat(user, "You must hold [src] in your hand to do this.") + return + if(!enabled_waddle) + to_chat(user, "You switch off the waddle dampeners!") + enabled_waddle = TRUE + else + to_chat(user, "You switch on the waddle dampeners!") + enabled_waddle = FALSE + +/obj/item/clothing/shoes/clown_shoes/nodrop + flags = NODROP + +/obj/item/clothing/shoes/clown_shoes/magical + name = "magical clown shoes" + desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow." + magical = TRUE + +/obj/item/clothing/shoes/jackboots + name = "jackboots" + desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." + can_cut_open = 1 + icon_state = "jackboots" + item_state = "jackboots" + item_color = "hosred" + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + var/footstep = 1 + shoe_sound = "jackboot" + +/obj/item/clothing/shoes/jackboots/jacksandals + name = "jacksandals" + desc = "Nanotrasen-issue Security combat sandals for combat scenarios. They're jacksandals, however that works." + can_cut_open = 0 + icon_state = "jacksandal" + item_color = "jacksandal" + +/obj/item/clothing/shoes/workboots + name = "work boots" + desc = "Thick-soled boots for industrial work environments." + can_cut_open = 1 + icon_state = "workboots" + +/obj/item/clothing/shoes/workboots/mining + name = "mining boots" + desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed." + icon_state = "explorer" + resistance_flags = FIRE_PROOF + +/obj/item/clothing/shoes/winterboots + name = "winter boots" + desc = "Boots lined with 'synthetic' animal fur." + can_cut_open = 1 + icon_state = "winterboots" + cold_protection = FEET|LEGS + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET|LEGS + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + +/obj/item/clothing/shoes/cult + name = "boots" + desc = "A pair of boots worn by the followers of Nar-Sie." + icon_state = "cult" + item_state = "cult" + item_color = "cult" + + cold_protection = FEET + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + +/obj/item/clothing/shoes/cyborg + name = "cyborg boots" + desc = "Shoes for a cyborg costume" + icon_state = "boots" + +/obj/item/clothing/shoes/slippers + name = "bunny slippers" + desc = "Fluffy!" + icon_state = "slippers" + item_state = "slippers" + +/obj/item/clothing/shoes/slippers_worn + name = "worn bunny slippers" + desc = "Fluffy..." + icon_state = "slippers_worn" + item_state = "slippers_worn" + +/obj/item/clothing/shoes/laceup + name = "laceup shoes" + desc = "The height of fashion, and they're pre-polished!" + icon_state = "laceups" + put_on_delay = 50 + +/obj/item/clothing/shoes/roman + name = "roman sandals" + desc = "Sandals with buckled leather straps on it." + icon_state = "roman" + item_state = "roman" + strip_delay = 100 + put_on_delay = 100 + +/obj/item/clothing/shoes/centcom + name = "dress shoes" + desc = "They appear impeccably polished." + icon_state = "laceups" + +/obj/item/clothing/shoes/griffin + name = "griffon boots" + desc = "A pair of costume boots fashioned after bird talons." + icon_state = "griffinboots" + item_state = "griffinboots" + + +/obj/item/clothing/shoes/fluff/noble_boot + name = "noble boots" + desc = "The boots are economically designed to balance function and comfort, so that you can step on peasants without having to worry about blisters. The leather also resists unwanted blood stains." + icon_state = "noble_boot" + item_color = "noble_boot" + item_state = "noble_boot" + +/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/tape_roll) && !silence_steps) + var/obj/item/stack/tape_roll/TR = I + if((!silence_steps || shoe_sound) && TR.use(4)) + silence_steps = TRUE + shoe_sound = null + to_chat(user, "You tape the soles of [src] to silence your footsteps.") + else + return ..() + +/obj/item/clothing/shoes/sandal/white + name = "White Sandals" + desc = "Medical sandals that nerds wear." + icon_state = "medsandal" + item_color = "medsandal" + +/obj/item/clothing/shoes/sandal/fancy + name = "Fancy Sandals" + desc = "FANCY!!." + icon_state = "fancysandal" + item_color = "fancysandal" + +/obj/item/clothing/shoes/cursedclown + name = "cursed clown shoes" + desc = "Moldering clown flip flops. They're neon green for some reason." + icon = 'icons/goonstation/objects/clothing/feet.dmi' + icon_state = "cursedclown" + item_state = "cclown_shoes" + icon_override = 'icons/goonstation/mob/clothing/feet.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + flags = NODROP + shoe_sound = "clownstep" + +/obj/item/clothing/shoes/singery + name = "yellow performer's boots" + desc = "These boots were made for dancing." + icon_state = "ysing" + put_on_delay = 50 + +/obj/item/clothing/shoes/singerb + name = "blue performer's boots" + desc = "These boots were made for dancing." + icon_state = "bsing" + put_on_delay = 50 + +/obj/item/clothing/shoes/cowboy + name = "cowboy boots" + desc = "A pair a' brown boots." + icon_state = "cowboy_brown" + item_color = "cowboy_brown" + +/obj/item/clothing/shoes/cowboy/black + name = "black cowboy boots" + desc = "A pair a' black rustlers' boots" + icon_state = "cowboy_black" + item_color = "cowboy_black" + +/obj/item/clothing/shoes/cowboy/white + name = "white cowboy boots" + desc = "For the rancher in us all." + icon_state = "cowboy_white" + item_color = "cowboy_white" + +/obj/item/clothing/shoes/cowboy/fancy + name = "bilton wrangler boots" + desc = "A pair of authentic haute couture boots from Japanifornia. You doubt they have ever been close to cattle." + icon_state = "cowboy_fancy" + item_color = "cowboy_fancy" + +/obj/item/clothing/shoes/cowboy/pink + name = "pink cowgirl boots" + desc = "For a Rustlin' tustlin' cowgirl." + icon_state = "cowboyboots_pink" + item_color = "cowboyboots_pink" + +/obj/item/clothing/shoes/cowboy/lizard + name = "lizard skin boots" + desc = "You can hear a faint hissing from inside the boots; you hope it is just a mournful ghost." + icon_state = "lizardboots_green" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 0) //lizards like to stay warm + +/obj/item/clothing/shoes/cowboy/lizardmasterwork + name = "\improper Hugs-The-Feet lizard skin boots" + desc = "A pair of masterfully crafted lizard skin boots. Finally a good application for the station's most bothersome inhabitants." + icon_state = "lizardboots_blue" + +/obj/effect/spawner/lootdrop/lizardboots + name = "random lizard boot quality" + desc = "Which ever gets picked, the lizard race loses" + icon = 'icons/obj/clothing/shoes.dmi' + icon_state = "lizardboots_green" + loot = list( + /obj/item/clothing/shoes/cowboy/lizard = 7, + /obj/item/clothing/shoes/cowboy/lizardmasterwork = 1) + +/obj/item/clothing/shoes/footwraps + name = "cloth footwraps" + desc = "A roll of treated canvas used for wrapping claws or paws." + icon_state = "clothwrap" + item_state = "clothwrap" + force = 0 + silence_steps = TRUE + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/shoes/bhop + name = "jump boots" + desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." + icon_state = "jetboots" + item_state = "jetboots" + item_color = "hosred" + resistance_flags = FIRE_PROOF + actions_types = list(/datum/action/item_action/bhop) + permeability_coefficient = 0.05 + can_cut_open = FALSE + var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles + var/jumpspeed = 3 + var/recharging_rate = 60 //default 6 seconds between each dash + var/recharging_time = 0 //time until next dash + +/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action) + if(!isliving(user)) + return + + if(recharging_time > world.time) + to_chat(user, "The boot's internal propulsion needs to recharge still!") + return + + var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction + + if (user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)) + playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) + user.visible_message("[usr] dashes forward into the air!") + recharging_time = world.time + recharging_rate + else + to_chat(user, "Something prevents you from dashing forward!") diff --git a/code/modules/clothing/spacesuits/breaches.dm b/code/modules/clothing/spacesuits/breaches.dm index 483c122fd803..899cb8e8d464 100644 --- a/code/modules/clothing/spacesuits/breaches.dm +++ b/code/modules/clothing/spacesuits/breaches.dm @@ -23,23 +23,23 @@ ..() base_name = "[name]" -//Some simple descriptors for breaches. Global because lazy, TODO: work out a better way to do this. +//Some simple descriptors for breaches. Global because lazy, TODO: work out a better way to do this. | 6 years late, but atleast they are proper globals now -var/global/list/breach_brute_descriptors = list( +GLOBAL_LIST_INIT(breach_brute_descriptors, list( "tiny puncture", "ragged tear", "large split", "huge tear", "gaping wound" - ) + )) -var/global/list/breach_burn_descriptors = list( +GLOBAL_LIST_INIT(breach_burn_descriptors, list( "small burn", "melted patch", "sizable burn", "large scorched area", "huge scorched area" - ) + )) /datum/breach/proc/update_descriptor() @@ -47,9 +47,9 @@ var/global/list/breach_burn_descriptors = list( class = max(1,min(class,5)) //Apply the correct descriptor. if(damtype == BURN) - descriptor = breach_burn_descriptors[class] + descriptor = GLOB.breach_burn_descriptors[class] else if(damtype == BRUTE) - descriptor = breach_brute_descriptors[class] + descriptor = GLOB.breach_brute_descriptors[class] //Repair a certain amount of brute or burn damage to the suit. /obj/item/clothing/suit/space/proc/repair_breaches(var/damtype, var/amount, var/mob/user) diff --git a/code/modules/clothing/spacesuits/ert.dm b/code/modules/clothing/spacesuits/ert.dm index a97752a29dbc..c412bb40f7dd 100644 --- a/code/modules/clothing/spacesuits/ert.dm +++ b/code/modules/clothing/spacesuits/ert.dm @@ -22,7 +22,7 @@ else camera = new /obj/machinery/camera(src) camera.network = list("ERT") - cameranet.removeCamera(camera) + GLOB.cameranet.removeCamera(camera) camera.c_tag = user.name to_chat(user, "User scanned as [camera.c_tag]. Camera activated.") @@ -246,4 +246,4 @@ icon_state = "hardsuit-berserker" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/berserker armor = list(melee = 65, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 100, rad = 100, fire = 80, acid = 80) - slowdown = 0 \ No newline at end of file + slowdown = 0 diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index f9366e12778b..5a01c9f6f550 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -1,590 +1,590 @@ -//Baseline hardsuits -/obj/item/clothing/head/helmet/space/hardsuit - name = "hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment." - icon_state = "hardsuit0-engineering" - item_state = "eng_helm" - max_integrity = 300 - armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) - var/basestate = "hardsuit" - allowed = list(/obj/item/flashlight) - var/brightness_on = 4 //luminosity when on - var/on = FALSE - var/obj/item/clothing/suit/space/hardsuit/suit - item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) - actions_types = list(/datum/action/item_action/toggle_helmet_light) - - //Species-specific stuff. - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - sprite_sheets_obj = list( - "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', - "Tajaran" = 'icons/obj/clothing/species/tajaran/hats.dmi', - "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' - ) - -/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user) - toggle_light(user) - -/obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_light(mob/user) - on = !on - icon_state = "[basestate][on]-[item_color]" - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_head() - - if(on) - set_light(brightness_on) - else - set_light(0) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() - if(on) - toggle_light() - visible_message("[src]'s light fades and turns off.") - -/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user) - ..() - if(suit) - suit.RemoveHelmet() - -/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) - if(slot == slot_head) - return 1 - -/obj/item/clothing/head/helmet/space/hardsuit/equipped(mob/user, slot) - ..() - if(slot != slot_head) - if(suit) - suit.RemoveHelmet() - else - qdel(src) - -/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg) - var/mob/wearer = loc - if(msg && ishuman(wearer)) - wearer.show_message("[msg]", 1) - -/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity) - ..() - display_visor_message("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") - -/obj/item/clothing/suit/space/hardsuit - name = "hardsuit" - desc = "A special space suit for environments that might pose hazards beyond just the vacuum of space. Provides more protection than a standard space suit." - icon_state = "hardsuit-engineering" - item_state = "eng_hardsuit" - max_integrity = 300 - armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) - siemens_coefficient = 0 - var/obj/item/clothing/head/helmet/space/hardsuit/helmet - actions_types = list(/datum/action/item_action/toggle_helmet) - var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit - var/obj/item/tank/jetpack/suit/jetpack = null - - hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") - species_restricted = list("exclude", "Wryn") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi' - ) - sprite_sheets_obj = list( - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', - "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', - "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' - ) - -/obj/item/clothing/suit/space/hardsuit/New() - if(jetpack && ispath(jetpack)) - jetpack = new jetpack(src) - ..() - -/obj/item/clothing/suit/space/hardsuit/attack_self(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - ..() - -/obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/tank/jetpack/suit)) - if(jetpack) - to_chat(user, "[src] already has a jetpack installed.") - return - if(src == user.get_item_by_slot(slot_wear_suit)) //Make sure the player is not wearing the suit before applying the upgrade. - to_chat(user, "You cannot install the upgrade to [src] while wearing it.") - return - - if(user.unEquip(I)) - I.forceMove(src) - jetpack = I - to_chat(user, "You successfully install the jetpack into [src].") - return - return ..() - -/obj/item/clothing/suit/space/hardsuit/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!jetpack) - to_chat(user, "[src] has no jetpack installed.") - return - if(src == user.get_item_by_slot(slot_wear_suit)) - to_chat(user, "You cannot remove the jetpack from [src] while wearing it.") - return - jetpack.turn_off(user) - jetpack.forceMove(drop_location()) - jetpack = null - to_chat(user, "You successfully remove the jetpack from [src].") - -/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) - ..() - if(jetpack) - if(slot == slot_wear_suit) - for(var/X in jetpack.actions) - var/datum/action/A = X - A.Grant(user) - -/obj/item/clothing/suit/space/hardsuit/dropped(mob/user) - ..() - if(jetpack) - for(var/X in jetpack.actions) - var/datum/action/A = X - A.Remove(user) - -/obj/item/clothing/suit/space/hardsuit/item_action_slot_check(slot) - if(slot == slot_wear_suit) //we only give the mob the ability to toggle the helmet if he's wearing the hardsuit. - return 1 - -//Engineering hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/engine - name = "engineering hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." - icon_state = "hardsuit0-engineering" - item_state = "eng_helm" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) - resistance_flags = FIRE_PROOF - item_color = "engineering" - -/obj/item/clothing/suit/space/hardsuit/engine - name = "engineering hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." - icon_state = "hardsuit-engineering" - item_state = "eng_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine - dog_fashion = /datum/dog_fashion/back/hardsuit - resistance_flags = FIRE_PROOF - -//Atmospherics -/obj/item/clothing/head/helmet/space/hardsuit/engine/atmos - name = "atmospherics hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has thermal shielding." - icon_state = "hardsuit0-atmos" - item_state = "atmos_helm" - item_color = "atmos" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) - heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/space/hardsuit/engine/atmos - name = "atmospherics hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has thermal shielding." - icon_state = "hardsuit-atmos" - item_state = "atmos_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos - dog_fashion = null - -//Chief Engineer's hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/engine/elite - name = "advanced hardsuit helmet" - desc = "An advanced helmet designed for work in a hazardous, low pressure environment. Shines with a high polish." - icon_state = "hardsuit0-white" - item_state = "ce_helm" - item_color = "white" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90) - heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/space/hardsuit/engine/elite - icon_state = "hardsuit-white" - name = "advanced hardsuit" - desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." - item_state = "ce_hardsuit" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite - jetpack = /obj/item/tank/jetpack/suit - dog_fashion = null - -//Mining hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/mining - name = "mining hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." - icon_state = "hardsuit0-mining" - item_state = "mining_helm" - item_color = "mining" - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - heat_protection = HEAD - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) - brightness_on = 7 - -/obj/item/clothing/suit/space/hardsuit/mining - icon_state = "hardsuit-mining" - name = "mining hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." - item_state = "mining_hardsuit" - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/storage/bag/ore, /obj/item/pickaxe, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -//Syndicate hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/syndi - name = "blood-red hardsuit helmet" - desc = "A dual-mode advanced helmet designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." - alt_desc = "A dual-mode advanced helmet designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." - icon_state = "hardsuit1-syndi" - item_state = "syndie_helm" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) - on = 1 - var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null - actions_types = list(/datum/action/item_action/toggle_helmet_mode) - visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL - visor_flags = STOPSPRESSUREDMAGE - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon() - icon_state = "hardsuit[on]-[item_color]" - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/New() - ..() - if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) - linkedsuit = loc - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet - if(!isturf(user.loc)) - to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) - return - on = !on - if(on) - to_chat(user, "You switch your hardsuit to EVA mode, sacrificing speed for space protection.") - name = initial(name) - desc = initial(desc) - set_light(brightness_on) - flags |= visor_flags - flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH - flags_inv |= visor_flags_inv - cold_protection |= HEAD - else - to_chat(user, "You switch your hardsuit to combat mode and can now run at full speed.") - name += " (combat)" - desc = alt_desc - set_light(0) - flags &= ~visor_flags - flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv &= ~visor_flags_inv - cold_protection &= ~HEAD - update_icon() - playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) - toggle_hardsuit_mode(user) - user.update_inv_head() - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.head_update(src, forced = 1) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode - if(linkedsuit) - if(on) - linkedsuit.name = initial(linkedsuit.name) - linkedsuit.desc = initial(linkedsuit.desc) - linkedsuit.slowdown = 1 - linkedsuit.flags |= STOPSPRESSUREDMAGE - linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - else - linkedsuit.name += " (combat)" - linkedsuit.desc = linkedsuit.alt_desc - linkedsuit.slowdown = 0 - linkedsuit.flags &= ~STOPSPRESSUREDMAGE - linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS) - - linkedsuit.update_icon() - user.update_inv_wear_suit() - user.update_inv_w_uniform() - -/obj/item/clothing/suit/space/hardsuit/syndi - name = "blood-red hardsuit" - desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." - alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." - icon_state = "hardsuit1-syndi" - item_state = "syndie_hardsuit" - item_color = "syndi" - w_class = WEIGHT_CLASS_NORMAL - var/on = 1 - actions_types = list(/datum/action/item_action/toggle_hardsuit_mode) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) - allowed = list(/obj/item/gun, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/energy/sword, /obj/item/restraints/handcuffs, /obj/item/tank) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi - jetpack = /obj/item/tank/jetpack/suit - -/obj/item/clothing/suit/space/hardsuit/syndi/update_icon() - icon_state = "hardsuit[on]-[item_color]" - -//Elite Syndie suit -/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite - name = "elite syndicate hardsuit helmet" - desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in travel mode. Property of Gorlex Marauders." - icon_state = "hardsuit0-syndielite" - item_color = "syndielite" - armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) - heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/suit/space/hardsuit/syndi/elite - name = "elite syndicate hardsuit" - desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in travel mode." - icon_state = "hardsuit0-syndielite" - item_color = "syndielite" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite - armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -//Strike team hardsuits -/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst - armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) //Almost as good as DS gear, but unlike DS can switch to combat for mobility - icon_state = "hardsuit0-sst" - item_color = "sst" - -/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst - armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) - icon_state = "hardsuit0-sst" - item_color = "sst" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst - -/obj/item/clothing/suit/space/hardsuit/syndi/freedom - name = "eagle suit" - desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit." - icon_state = "freedom" - item_state = "freedom" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom - sprite_sheets = null - -/obj/item/clothing/suit/space/hardsuit/syndi/freedom/update_icon() - return - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom - name = "eagle helmet" - desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle." - icon_state = "griffinhat" - item_state = "griffinhat" - sprite_sheets = null - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom/update_icon() - return - -//Medical hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/medical - name = "medical hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort, but does not protect the eyes from intense light." - icon_state = "hardsuit0-medical" - item_state = "medical_helm" - item_color = "medical" - flash_protect = 0 - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - scan_reagents = 1 //Generally worn by the CMO, so they'd get utility off of seeing reagents - -/obj/item/clothing/suit/space/hardsuit/medical - icon_state = "hardsuit-medical" - name = "medical hardsuit" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort." - item_state = "medical_hardsuit" - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/rad_laser) - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/medical - slowdown = 0.5 - - //Security -/obj/item/clothing/head/helmet/space/hardsuit/security - name = "security hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." - icon_state = "hardsuit0-sec" - item_state = "sec_helm" - item_color = "sec" - armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - -/obj/item/clothing/suit/space/hardsuit/security - icon_state = "hardsuit-sec" - name = "security hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." - item_state = "sec_hardsuit" - armor = list("melee" = 35, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/restraints/handcuffs) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security - -/obj/item/clothing/head/helmet/space/hardsuit/security/hos - name = "head of security's hardsuit helmet" - desc = "A special bulky helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." - icon_state = "hardsuit0-hos" - item_color = "hos" - armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - -/obj/item/clothing/suit/space/hardsuit/security/hos - name = "head of security's hardsuit" - desc = "A special bulky suit that protects against hazardous, low pressure environments. Has an additional layer of armor." - icon_state = "hardsuit-hos" - armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos - jetpack = /obj/item/tank/jetpack/suit - - -//Singuloth armor -/obj/item/clothing/head/helmet/space/hardsuit/singuloth - name = "singuloth knight's helmet" - desc = "This is an adamantium helmet from the chapter of the Singuloth Knights. It shines with a holy aura." - icon_state = "hardsuit0-singuloth" - item_state = "singuloth_helm" - item_color = "singuloth" - armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) - sprite_sheets = null - -/obj/item/clothing/suit/space/hardsuit/singuloth - icon_state = "hardsuit-singuloth" - name = "singuloth knight's armor" - desc = "This is a ceremonial armor from the chapter of the Singuloth Knights. It's made of pure forged adamantium." - item_state = "singuloth_hardsuit" - flags = STOPSPRESSUREDMAGE - armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/singuloth - sprite_sheets = null - - -/////////////SHIELDED////////////////////////////////// - -/obj/item/clothing/suit/space/hardsuit/shielded - name = "shielded hardsuit" - desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." - icon_state = "hardsuit-hos" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded - allowed = list(/obj/item/flashlight,/obj/item/tank, /obj/item/gun,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs) - armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - var/current_charges = 3 - var/max_charges = 3 //How many charges total the shielding has - var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here - var/recharge_cooldown = 0 //Time since we've last been shot - var/recharge_rate = 1 //How quickly the shield recharges once it starts charging - var/shield_state = "shield-old" - var/shield_on = "shield-old" - -/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - recharge_cooldown = world.time + recharge_delay - if(current_charges > 0) - do_sparks(2, 1, src) - owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") - current_charges-- - if(recharge_rate) - START_PROCESSING(SSobj, src) - if(current_charges <= 0) - owner.visible_message("[owner]'s shield overloads!") - shield_state = "broken" - owner.update_inv_wear_suit() - return 1 - return 0 - - - -/obj/item/clothing/suit/space/hardsuit/shielded/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/suit/space/hardsuit/shielded/process() - if(world.time > recharge_cooldown && current_charges < max_charges) - current_charges = Clamp((current_charges + recharge_rate), 0, max_charges) - playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) - if(current_charges == max_charges) - playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) - STOP_PROCESSING(SSobj, src) - shield_state = "[shield_on]" - if(ishuman(loc)) - var/mob/living/carbon/human/C = loc - C.update_inv_wear_suit() - -/obj/item/clothing/suit/space/hardsuit/shielded/special_overlays() - return mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) - -/obj/item/clothing/head/helmet/space/hardsuit/shielded - resistance_flags = FIRE_PROOF | ACID_PROOF - - -//////Syndicate Version - -/obj/item/clothing/suit/space/hardsuit/shielded/syndi - name = "blood-red hardsuit" - desc = "An advanced hardsuit with built in energy shielding." - icon_state = "hardsuit1-syndi" - item_state = "syndie_hardsuit" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - slowdown = 0 - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi - jetpack = /obj/item/tank/jetpack/suit - -/obj/item/clothing/suit/space/hardsuit/shielded/syndi/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(shield_state == "broken") - to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") - return - - if(shield_state == "shield-red") - shield_state = "shield-old" - shield_on = "shield-old" - to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") - - else - shield_state = "shield-red" - shield_on = "shield-red" - to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") - user.update_inv_wear_suit() - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi - name = "blood-red hardsuit helmet" - desc = "An advanced hardsuit helmet with built in energy shielding." - icon_state = "hardsuit1-syndi" - item_state = "syndie_helm" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) +//Baseline hardsuits +/obj/item/clothing/head/helmet/space/hardsuit + name = "hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment." + icon_state = "hardsuit0-engineering" + item_state = "eng_helm" + max_integrity = 300 + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) + var/basestate = "hardsuit" + allowed = list(/obj/item/flashlight) + var/brightness_on = 4 //luminosity when on + var/on = FALSE + var/obj/item/clothing/suit/space/hardsuit/suit + item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) + actions_types = list(/datum/action/item_action/toggle_helmet_light) + + //Species-specific stuff. + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', + "Tajaran" = 'icons/obj/clothing/species/tajaran/hats.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' + ) + +/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user) + toggle_light(user) + +/obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_light(mob/user) + on = !on + icon_state = "[basestate][on]-[item_color]" + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_head() + + if(on) + set_light(brightness_on) + else + set_light(0) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() + if(on) + toggle_light() + visible_message("[src]'s light fades and turns off.") + +/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user) + ..() + if(suit) + suit.RemoveHelmet() + +/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) + if(slot == slot_head) + return 1 + +/obj/item/clothing/head/helmet/space/hardsuit/equipped(mob/user, slot) + ..() + if(slot != slot_head) + if(suit) + suit.RemoveHelmet() + else + qdel(src) + +/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg) + var/mob/wearer = loc + if(msg && ishuman(wearer)) + wearer.show_message("[msg]", 1) + +/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity) + ..() + display_visor_message("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") + +/obj/item/clothing/suit/space/hardsuit + name = "hardsuit" + desc = "A special space suit for environments that might pose hazards beyond just the vacuum of space. Provides more protection than a standard space suit." + icon_state = "hardsuit-engineering" + item_state = "eng_hardsuit" + max_integrity = 300 + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) + siemens_coefficient = 0 + var/obj/item/clothing/head/helmet/space/hardsuit/helmet + actions_types = list(/datum/action/item_action/toggle_helmet) + var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit + var/obj/item/tank/jetpack/suit/jetpack = null + + hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") + species_restricted = list("exclude", "Wryn") + sprite_sheets = list( + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi' + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' + ) + +/obj/item/clothing/suit/space/hardsuit/New() + if(jetpack && ispath(jetpack)) + jetpack = new jetpack(src) + ..() + +/obj/item/clothing/suit/space/hardsuit/attack_self(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + ..() + +/obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/tank/jetpack/suit)) + if(jetpack) + to_chat(user, "[src] already has a jetpack installed.") + return + if(src == user.get_item_by_slot(slot_wear_suit)) //Make sure the player is not wearing the suit before applying the upgrade. + to_chat(user, "You cannot install the upgrade to [src] while wearing it.") + return + + if(user.unEquip(I)) + I.forceMove(src) + jetpack = I + to_chat(user, "You successfully install the jetpack into [src].") + return + return ..() + +/obj/item/clothing/suit/space/hardsuit/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!jetpack) + to_chat(user, "[src] has no jetpack installed.") + return + if(src == user.get_item_by_slot(slot_wear_suit)) + to_chat(user, "You cannot remove the jetpack from [src] while wearing it.") + return + jetpack.turn_off(user) + jetpack.forceMove(drop_location()) + jetpack = null + to_chat(user, "You successfully remove the jetpack from [src].") + +/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) + ..() + if(jetpack) + if(slot == slot_wear_suit) + for(var/X in jetpack.actions) + var/datum/action/A = X + A.Grant(user) + +/obj/item/clothing/suit/space/hardsuit/dropped(mob/user) + ..() + if(jetpack) + for(var/X in jetpack.actions) + var/datum/action/A = X + A.Remove(user) + +/obj/item/clothing/suit/space/hardsuit/item_action_slot_check(slot) + if(slot == slot_wear_suit) //we only give the mob the ability to toggle the helmet if he's wearing the hardsuit. + return 1 + +//Engineering hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/engine + name = "engineering hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." + icon_state = "hardsuit0-engineering" + item_state = "eng_helm" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) + resistance_flags = FIRE_PROOF + item_color = "engineering" + +/obj/item/clothing/suit/space/hardsuit/engine + name = "engineering hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." + icon_state = "hardsuit-engineering" + item_state = "eng_hardsuit" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine + dog_fashion = /datum/dog_fashion/back/hardsuit + resistance_flags = FIRE_PROOF + +//Atmospherics +/obj/item/clothing/head/helmet/space/hardsuit/engine/atmos + name = "atmospherics hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has thermal shielding." + icon_state = "hardsuit0-atmos" + item_state = "atmos_helm" + item_color = "atmos" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) + heat_protection = HEAD //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/hardsuit/engine/atmos + name = "atmospherics hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has thermal shielding." + icon_state = "hardsuit-atmos" + item_state = "atmos_hardsuit" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos + dog_fashion = null + +//Chief Engineer's hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/engine/elite + name = "advanced hardsuit helmet" + desc = "An advanced helmet designed for work in a hazardous, low pressure environment. Shines with a high polish." + icon_state = "hardsuit0-white" + item_state = "ce_helm" + item_color = "white" + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90) + heat_protection = HEAD //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/hardsuit/engine/elite + icon_state = "hardsuit-white" + name = "advanced hardsuit" + desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." + item_state = "ce_hardsuit" + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite + jetpack = /obj/item/tank/jetpack/suit + dog_fashion = null + +//Mining hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/mining + name = "mining hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." + icon_state = "hardsuit0-mining" + item_state = "mining_helm" + item_color = "mining" + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + heat_protection = HEAD + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) + brightness_on = 7 + +/obj/item/clothing/suit/space/hardsuit/mining + icon_state = "hardsuit-mining" + name = "mining hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." + item_state = "mining_hardsuit" + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/storage/bag/ore, /obj/item/pickaxe, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +//Syndicate hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/syndi + name = "blood-red hardsuit helmet" + desc = "A dual-mode advanced helmet designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." + alt_desc = "A dual-mode advanced helmet designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) + on = 1 + var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null + actions_types = list(/datum/action/item_action/toggle_helmet_mode) + visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL + visor_flags = STOPSPRESSUREDMAGE + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon() + icon_state = "hardsuit[on]-[item_color]" + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/New() + ..() + if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) + linkedsuit = loc + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet + if(!isturf(user.loc)) + to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) + return + on = !on + if(on) + to_chat(user, "You switch your hardsuit to EVA mode, sacrificing speed for space protection.") + name = initial(name) + desc = initial(desc) + set_light(brightness_on) + flags |= visor_flags + flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH + flags_inv |= visor_flags_inv + cold_protection |= HEAD + else + to_chat(user, "You switch your hardsuit to combat mode and can now run at full speed.") + name += " (combat)" + desc = alt_desc + set_light(0) + flags &= ~visor_flags + flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv &= ~visor_flags_inv + cold_protection &= ~HEAD + update_icon() + playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) + toggle_hardsuit_mode(user) + user.update_inv_head() + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.head_update(src, forced = 1) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode + if(linkedsuit) + if(on) + linkedsuit.name = initial(linkedsuit.name) + linkedsuit.desc = initial(linkedsuit.desc) + linkedsuit.slowdown = 1 + linkedsuit.flags |= STOPSPRESSUREDMAGE + linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + else + linkedsuit.name += " (combat)" + linkedsuit.desc = linkedsuit.alt_desc + linkedsuit.slowdown = 0 + linkedsuit.flags &= ~STOPSPRESSUREDMAGE + linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS) + + linkedsuit.update_icon() + user.update_inv_wear_suit() + user.update_inv_w_uniform() + +/obj/item/clothing/suit/space/hardsuit/syndi + name = "blood-red hardsuit" + desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." + alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_hardsuit" + item_color = "syndi" + w_class = WEIGHT_CLASS_NORMAL + var/on = 1 + actions_types = list(/datum/action/item_action/toggle_hardsuit_mode) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) + allowed = list(/obj/item/gun, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/energy/sword, /obj/item/restraints/handcuffs, /obj/item/tank) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi + jetpack = /obj/item/tank/jetpack/suit + +/obj/item/clothing/suit/space/hardsuit/syndi/update_icon() + icon_state = "hardsuit[on]-[item_color]" + +//Elite Syndie suit +/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + name = "elite syndicate hardsuit helmet" + desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in travel mode. Property of Gorlex Marauders." + icon_state = "hardsuit0-syndielite" + item_color = "syndielite" + armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/suit/space/hardsuit/syndi/elite + name = "elite syndicate hardsuit" + desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in travel mode." + icon_state = "hardsuit0-syndielite" + item_color = "syndielite" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +//Strike team hardsuits +/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst + armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) //Almost as good as DS gear, but unlike DS can switch to combat for mobility + icon_state = "hardsuit0-sst" + item_color = "sst" + +/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst + armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) + icon_state = "hardsuit0-sst" + item_color = "sst" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst + +/obj/item/clothing/suit/space/hardsuit/syndi/freedom + name = "eagle suit" + desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit." + icon_state = "freedom" + item_state = "freedom" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom + sprite_sheets = null + +/obj/item/clothing/suit/space/hardsuit/syndi/freedom/update_icon() + return + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom + name = "eagle helmet" + desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle." + icon_state = "griffinhat" + item_state = "griffinhat" + sprite_sheets = null + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom/update_icon() + return + +//Medical hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/medical + name = "medical hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort, but does not protect the eyes from intense light." + icon_state = "hardsuit0-medical" + item_state = "medical_helm" + item_color = "medical" + flash_protect = 0 + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + scan_reagents = 1 //Generally worn by the CMO, so they'd get utility off of seeing reagents + +/obj/item/clothing/suit/space/hardsuit/medical + icon_state = "hardsuit-medical" + name = "medical hardsuit" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort." + item_state = "medical_hardsuit" + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/rad_laser) + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/medical + slowdown = 0.5 + + //Security +/obj/item/clothing/head/helmet/space/hardsuit/security + name = "security hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "hardsuit0-sec" + item_state = "sec_helm" + item_color = "sec" + armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) + +/obj/item/clothing/suit/space/hardsuit/security + icon_state = "hardsuit-sec" + name = "security hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + item_state = "sec_hardsuit" + armor = list("melee" = 35, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) + allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/restraints/handcuffs) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security + +/obj/item/clothing/head/helmet/space/hardsuit/security/hos + name = "head of security's hardsuit helmet" + desc = "A special bulky helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "hardsuit0-hos" + item_color = "hos" + armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) + +/obj/item/clothing/suit/space/hardsuit/security/hos + name = "head of security's hardsuit" + desc = "A special bulky suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + icon_state = "hardsuit-hos" + armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos + jetpack = /obj/item/tank/jetpack/suit + + +//Singuloth armor +/obj/item/clothing/head/helmet/space/hardsuit/singuloth + name = "singuloth knight's helmet" + desc = "This is an adamantium helmet from the chapter of the Singuloth Knights. It shines with a holy aura." + icon_state = "hardsuit0-singuloth" + item_state = "singuloth_helm" + item_color = "singuloth" + armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) + sprite_sheets = null + +/obj/item/clothing/suit/space/hardsuit/singuloth + icon_state = "hardsuit-singuloth" + name = "singuloth knight's armor" + desc = "This is a ceremonial armor from the chapter of the Singuloth Knights. It's made of pure forged adamantium." + item_state = "singuloth_hardsuit" + flags = STOPSPRESSUREDMAGE + armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/singuloth + sprite_sheets = null + + +/////////////SHIELDED////////////////////////////////// + +/obj/item/clothing/suit/space/hardsuit/shielded + name = "shielded hardsuit" + desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." + icon_state = "hardsuit-hos" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded + allowed = list(/obj/item/flashlight,/obj/item/tank, /obj/item/gun,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs) + armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + var/current_charges = 3 + var/max_charges = 3 //How many charges total the shielding has + var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here + var/recharge_cooldown = 0 //Time since we've last been shot + var/recharge_rate = 1 //How quickly the shield recharges once it starts charging + var/shield_state = "shield-old" + var/shield_on = "shield-old" + +/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + recharge_cooldown = world.time + recharge_delay + if(current_charges > 0) + do_sparks(2, 1, src) + owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") + current_charges-- + if(recharge_rate) + START_PROCESSING(SSobj, src) + if(current_charges <= 0) + owner.visible_message("[owner]'s shield overloads!") + shield_state = "broken" + owner.update_inv_wear_suit() + return 1 + return 0 + + + +/obj/item/clothing/suit/space/hardsuit/shielded/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/suit/space/hardsuit/shielded/process() + if(world.time > recharge_cooldown && current_charges < max_charges) + current_charges = Clamp((current_charges + recharge_rate), 0, max_charges) + playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) + if(current_charges == max_charges) + playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) + STOP_PROCESSING(SSobj, src) + shield_state = "[shield_on]" + if(ishuman(loc)) + var/mob/living/carbon/human/C = loc + C.update_inv_wear_suit() + +/obj/item/clothing/suit/space/hardsuit/shielded/special_overlays() + return mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) + +/obj/item/clothing/head/helmet/space/hardsuit/shielded + resistance_flags = FIRE_PROOF | ACID_PROOF + + +//////Syndicate Version + +/obj/item/clothing/suit/space/hardsuit/shielded/syndi + name = "blood-red hardsuit" + desc = "An advanced hardsuit with built in energy shielding." + icon_state = "hardsuit1-syndi" + item_state = "syndie_hardsuit" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + slowdown = 0 + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi + jetpack = /obj/item/tank/jetpack/suit + +/obj/item/clothing/suit/space/hardsuit/shielded/syndi/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(shield_state == "broken") + to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") + return + + if(shield_state == "shield-red") + shield_state = "shield-old" + shield_on = "shield-old" + to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") + + else + shield_state = "shield-red" + shield_on = "shield-red" + to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") + user.update_inv_wear_suit() + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi + name = "blood-red hardsuit helmet" + desc = "An advanced hardsuit helmet with built in energy shielding." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index f516627e1821..f6ab79e628a9 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -1,287 +1,287 @@ - //Captain's space suit, not hardsuits because no flashlight! -/obj/item/clothing/head/helmet/space/capspace - name = "captain's space helmet" - icon_state = "capspace" - item_state = "capspacehelmet" - desc = "A special helmet designed for only the most fashionable of military figureheads." - flags_inv = HIDEFACE - permeability_coefficient = 0.01 - armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/space/capspace/equipped(mob/living/carbon/human/user, slot) - if(ishuman(user) && slot == slot_head) - if(isvox(user)) - if(flags & BLOCKHAIR) - flags &= ~BLOCKHAIR - else - if((initial(flags) & BLOCKHAIR) && !(flags & BLOCKHAIR)) - flags |= BLOCKHAIR - -/obj/item/clothing/suit/space/captain - name = "captain's space suit" - desc = "A bulky, heavy-duty piece of exclusive Nanotrasen armor. YOU are in charge!" - icon_state = "caparmor" - item_state = "capspacesuit" - w_class = WEIGHT_CLASS_BULKY - allowed = list(/obj/item/tank, /obj/item/flashlight,/obj/item/gun/energy, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton,/obj/item/restraints/handcuffs) - armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - - //Deathsquad space suit, not hardsuits because no flashlight! -/obj/item/clothing/head/helmet/space/deathsquad - name = "deathsquad helmet" - desc = "That's not red paint. That's real blood." - icon_state = "deathsquad" - item_state = "deathsquad" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - see_in_dark = 8 - HUDType = MEDHUD - strip_delay = 130 - -/obj/item/clothing/suit/space/deathsquad - name = "deathsquad suit" - desc = "A heavily armored, advanced space suit that protects against most forms of damage." - icon_state = "deathsquad" - item_state = "swat_suit" - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - strip_delay = 130 - dog_fashion = /datum/dog_fashion/back/deathsquad - - //NEW SWAT suit -/obj/item/clothing/suit/space/swat - name = "SWAT armor" - desc = "Space-proof tactical SWAT armor." - icon_state = "heavy" - item_state = "swat_suit" - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) - strip_delay = 120 - resistance_flags = FIRE_PROOF | ACID_PROOF - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/head/helmet/space/deathsquad/beret - name = "officer's beret" - desc = "An armored beret commonly used by special operations officers." - icon_state = "beret_officer" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - -/obj/item/clothing/suit/space/deathsquad/officer - name = "officer jacket" - desc = "An armored jacket used in special operations." - icon_state = "detective" - item_state = "det_suit" - blood_overlay_type = "coat" - flags_inv = 0 - slowdown = 0 - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - w_class = WEIGHT_CLASS_NORMAL - -//Space santa outfit suit -/obj/item/clothing/head/helmet/space/santahat - name = "Santa's hat" - desc = "Ho ho ho. Merrry X-mas!" - icon_state = "santahat" - - sprite_sheets = list( - "Grey" = 'icons/mob/species/Grey/head.dmi', - "Drask" = 'icons/mob/species/Drask/helmet.dmi' - ) - flags = BLOCKHAIR | STOPSPRESSUREDMAGE - flags_cover = HEADCOVERSEYES - dog_fashion = /datum/dog_fashion/head/santa - -/obj/item/clothing/head/helmet/space/santahat/attack_self(mob/user as mob) - if(src.icon_state == "santahat") - src.icon_state = "santahat_beard" - src.item_state = "santahat_beard" - to_chat(user, "Santa's beard expands out from the hat!") - else - src.icon_state = "santahat" - src.item_state = "santahat" - to_chat(user, "The beard slinks back into the hat...") - -/obj/item/clothing/suit/space/santa - name = "Santa's suit" - desc = "Festive!" - icon_state = "santa" - item_state = "santa" - slowdown = 0 - flags = STOPSPRESSUREDMAGE - flags_size = ONESIZEFITSALL - allowed = list(/obj/item) //for stuffing extra special presents - -//Space pirate outfit -/obj/item/clothing/head/helmet/space/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) - flags = BLOCKHAIR | STOPSPRESSUREDMAGE - flags_cover = HEADCOVERSEYES - strip_delay = 40 - put_on_delay = 20 - -/obj/item/clothing/suit/space/pirate - name = "pirate coat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank) - slowdown = 0 - armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) - strip_delay = 40 - put_on_delay = 20 - -//Paramedic EVA suit -/obj/item/clothing/head/helmet/space/eva/paramedic - name = "Paramedic EVA helmet" - desc = "A brand new paramedic EVA helmet. It seems to mold to your head shape. Used for retrieving bodies in space." - icon_state = "paramedic-eva-helmet" - item_state = "paramedic-eva-helmet" - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi' - ) - -/obj/item/clothing/suit/space/eva/paramedic - name = "Paramedic EVA suit" - icon_state = "paramedic-eva" - item_state = "paramedic-eva" - desc = "A brand new paramedic EVA suit. The nitrile seems a bit too thin to be space proof. Used for retrieving bodies in space." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi' - ) - -/obj/item/clothing/suit/space/eva - name = "EVA suit" - icon_state = "spacenew" - item_state = "s_suit" - desc = "A lightweight space suit with the basic ability to protect the wearer from the vacuum of space during emergencies." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - sprite_sheets_obj = list( - "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' - ) - -/obj/item/clothing/head/helmet/space/eva - name = "EVA helmet" - icon_state = "spacenew" - item_state = "s_helmet" - desc = "A lightweight space helmet with the basic ability to protect the wearer from the vacuum of space during emergencies." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - flash_protect = 0 - species_restricted = list("exclude", "Wryn") - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' - ) - -//Mime's Hardsuit -/obj/item/clothing/head/helmet/space/eva/mime - name = "mime eva helmet" -// icon = 'spaceciv.dmi' - desc = ". . ." - icon_state = "spacemimehelmet" - item_state = "spacemimehelmet" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/suit/space/eva/mime - name = "mime eva suit" -// icon = 'spaceciv.dmi' - desc = ". . ." - icon_state = "spacemime_suit" - item_state = "spacemime_items" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/head/helmet/space/eva/clown - name = "clown eva helmet" -// icon = 'spaceciv.dmi' - desc = "An EVA helmet specifically designed for the clown. SPESSHONK!" - icon_state = "clownhelmet" - item_state = "clownhelmet" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/suit/space/eva/clown - name = "clown eva suit" -// icon = 'spaceciv.dmi' - desc = "An EVA suit specifically designed for the clown. SPESSHONK!" - icon_state = "spaceclown_suit" - item_state = "spaceclown_items" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi') - sprite_sheets_obj = null + //Captain's space suit, not hardsuits because no flashlight! +/obj/item/clothing/head/helmet/space/capspace + name = "captain's space helmet" + icon_state = "capspace" + item_state = "capspacehelmet" + desc = "A special helmet designed for only the most fashionable of military figureheads." + flags_inv = HIDEFACE + permeability_coefficient = 0.01 + armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/space/capspace/equipped(mob/living/carbon/human/user, slot) + if(ishuman(user) && slot == slot_head) + if(isvox(user)) + if(flags & BLOCKHAIR) + flags &= ~BLOCKHAIR + else + if((initial(flags) & BLOCKHAIR) && !(flags & BLOCKHAIR)) + flags |= BLOCKHAIR + +/obj/item/clothing/suit/space/captain + name = "captain's space suit" + desc = "A bulky, heavy-duty piece of exclusive Nanotrasen armor. YOU are in charge!" + icon_state = "caparmor" + item_state = "capspacesuit" + w_class = WEIGHT_CLASS_BULKY + allowed = list(/obj/item/tank, /obj/item/flashlight,/obj/item/gun/energy, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton,/obj/item/restraints/handcuffs) + armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + + //Deathsquad space suit, not hardsuits because no flashlight! +/obj/item/clothing/head/helmet/space/deathsquad + name = "deathsquad helmet" + desc = "That's not red paint. That's real blood." + icon_state = "deathsquad" + item_state = "deathsquad" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + see_in_dark = 8 + HUDType = MEDHUD + strip_delay = 130 + +/obj/item/clothing/suit/space/deathsquad + name = "deathsquad suit" + desc = "A heavily armored, advanced space suit that protects against most forms of damage." + icon_state = "deathsquad" + item_state = "swat_suit" + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + strip_delay = 130 + dog_fashion = /datum/dog_fashion/back/deathsquad + + //NEW SWAT suit +/obj/item/clothing/suit/space/swat + name = "SWAT armor" + desc = "Space-proof tactical SWAT armor." + icon_state = "heavy" + item_state = "swat_suit" + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) + strip_delay = 120 + resistance_flags = FIRE_PROOF | ACID_PROOF + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/head/helmet/space/deathsquad/beret + name = "officer's beret" + desc = "An armored beret commonly used by special operations officers." + icon_state = "beret_officer" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + +/obj/item/clothing/suit/space/deathsquad/officer + name = "officer jacket" + desc = "An armored jacket used in special operations." + icon_state = "detective" + item_state = "det_suit" + blood_overlay_type = "coat" + flags_inv = 0 + slowdown = 0 + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + w_class = WEIGHT_CLASS_NORMAL + +//Space santa outfit suit +/obj/item/clothing/head/helmet/space/santahat + name = "Santa's hat" + desc = "Ho ho ho. Merrry X-mas!" + icon_state = "santahat" + + sprite_sheets = list( + "Grey" = 'icons/mob/species/Grey/head.dmi', + "Drask" = 'icons/mob/species/Drask/helmet.dmi' + ) + flags = BLOCKHAIR | STOPSPRESSUREDMAGE + flags_cover = HEADCOVERSEYES + dog_fashion = /datum/dog_fashion/head/santa + +/obj/item/clothing/head/helmet/space/santahat/attack_self(mob/user as mob) + if(src.icon_state == "santahat") + src.icon_state = "santahat_beard" + src.item_state = "santahat_beard" + to_chat(user, "Santa's beard expands out from the hat!") + else + src.icon_state = "santahat" + src.item_state = "santahat" + to_chat(user, "The beard slinks back into the hat...") + +/obj/item/clothing/suit/space/santa + name = "Santa's suit" + desc = "Festive!" + icon_state = "santa" + item_state = "santa" + slowdown = 0 + flags = STOPSPRESSUREDMAGE + flags_size = ONESIZEFITSALL + allowed = list(/obj/item) //for stuffing extra special presents + +//Space pirate outfit +/obj/item/clothing/head/helmet/space/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) + flags = BLOCKHAIR | STOPSPRESSUREDMAGE + flags_cover = HEADCOVERSEYES + strip_delay = 40 + put_on_delay = 20 + +/obj/item/clothing/suit/space/pirate + name = "pirate coat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank) + slowdown = 0 + armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) + strip_delay = 40 + put_on_delay = 20 + +//Paramedic EVA suit +/obj/item/clothing/head/helmet/space/eva/paramedic + name = "Paramedic EVA helmet" + desc = "A brand new paramedic EVA helmet. It seems to mold to your head shape. Used for retrieving bodies in space." + icon_state = "paramedic-eva-helmet" + item_state = "paramedic-eva-helmet" + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi' + ) + +/obj/item/clothing/suit/space/eva/paramedic + name = "Paramedic EVA suit" + icon_state = "paramedic-eva" + item_state = "paramedic-eva" + desc = "A brand new paramedic EVA suit. The nitrile seems a bit too thin to be space proof. Used for retrieving bodies in space." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi' + ) + +/obj/item/clothing/suit/space/eva + name = "EVA suit" + icon_state = "spacenew" + item_state = "s_suit" + desc = "A lightweight space suit with the basic ability to protect the wearer from the vacuum of space during emergencies." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + sprite_sheets_obj = list( + "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' + ) + +/obj/item/clothing/head/helmet/space/eva + name = "EVA helmet" + icon_state = "spacenew" + item_state = "s_helmet" + desc = "A lightweight space helmet with the basic ability to protect the wearer from the vacuum of space during emergencies." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + flash_protect = 0 + species_restricted = list("exclude", "Wryn") + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' + ) + +//Mime's Hardsuit +/obj/item/clothing/head/helmet/space/eva/mime + name = "mime eva helmet" +// icon = 'spaceciv.dmi' + desc = ". . ." + icon_state = "spacemimehelmet" + item_state = "spacemimehelmet" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/eva/mime + name = "mime eva suit" +// icon = 'spaceciv.dmi' + desc = ". . ." + icon_state = "spacemime_suit" + item_state = "spacemime_items" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/head/helmet/space/eva/clown + name = "clown eva helmet" +// icon = 'spaceciv.dmi' + desc = "An EVA helmet specifically designed for the clown. SPESSHONK!" + icon_state = "clownhelmet" + item_state = "clownhelmet" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/eva/clown + name = "clown eva suit" +// icon = 'spaceciv.dmi' + desc = "An EVA suit specifically designed for the clown. SPESSHONK!" + icon_state = "spaceclown_suit" + item_state = "spaceclown_items" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi') + sprite_sheets_obj = null diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index bd35abb19a6b..77d7b99fde01 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -258,4 +258,4 @@ name = "blueshield envirosuit helmet" desc = "A plasmaman envirohelm designed for the blueshield." icon_state = "bs_envirohelm" - item_state = "bs_envirohelm" \ No newline at end of file + item_state = "bs_envirohelm" diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index 74af696e76ef..dfcf80f13038 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -23,7 +23,7 @@ to_chat(usr, "Your module is not installed in a hardsuit.") return - module.holder.ui_interact(usr, nano_state = contained_state) + module.holder.ui_interact(usr, nano_state = GLOB.contained_state) /obj/item/rig_module/ai_container @@ -139,7 +139,7 @@ if(!target) if(ai_card) if(istype(ai_card,/obj/item/aicard)) - ai_card.ui_interact(H, state = deep_inventory_state) + ai_card.ui_interact(H, state = GLOB.deep_inventory_state) else eject_ai(H) update_verb_holder() diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index 9fc960cdbced..33ae007ceee5 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -188,4 +188,4 @@ if(!vision) vision = vision_datum processed_vision += vision_datum - vision_modes = processed_vision \ No newline at end of file + vision_modes = processed_vision diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 3167e68ca6fd..3608ee851648 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -522,7 +522,7 @@ cell.use(cost*10) return 1 -/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) +/obj/item/rig/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) if(!user) return @@ -532,7 +532,7 @@ ui.open() ui.set_auto_update(1) -/obj/item/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) +/obj/item/rig/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) var/data[0] data["primarysystem"] = null @@ -982,7 +982,7 @@ return 0 if(malfunctioning) - direction = pick(cardinal) + direction = pick(GLOB.cardinal) // Inside an object, tell it we moved. if(isobj(wearer.loc) || ismob(wearer.loc)) diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm index 4e1782d2384b..4a71f6c67bcf 100644 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -332,4 +332,4 @@ return to_chat(usr, "You attempt to engage the [module.interface_name].") - module.engage() \ No newline at end of file + module.engage() diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm index fa32cbb7d515..5485e1fe8821 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm @@ -78,4 +78,4 @@ /obj/item/rig_module/device/plasmacutter, // /obj/item/rig_module/device/rcd, /obj/item/rig_module/datajack - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index a1043eb26433..8c65ed025538 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -1,188 +1,188 @@ -//Regular syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate - name = "red space helmet" - desc = "Top secret Spess Helmet." - icon_state = "syndicate" - item_state = "syndicate" - desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi') - - -/obj/item/clothing/suit/space/syndicate - name = "red space suit" - icon_state = "syndicate" - item_state = "space_suit_syndicate" - desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi') - - -//Green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green - name = "Green Space Helmet" - icon_state = "syndicate-helm-green" - item_state = "syndicate-helm-green" - -/obj/item/clothing/suit/space/syndicate/green - name = "Green Space Suit" - icon_state = "syndicate-green" - item_state = "syndicate-green" - - -//Dark green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green/dark - name = "Dark Green Space Helmet" - icon_state = "syndicate-helm-green-dark" - item_state = "syndicate-helm-green-dark" - -/obj/item/clothing/suit/space/syndicate/green/dark - name = "Dark Green Space Suit" - icon_state = "syndicate-green-dark" - item_state = "syndicate-green-dark" - - -//Orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/orange - name = "Orange Space Helmet" - icon_state = "syndicate-helm-orange" - item_state = "syndicate-helm-orange" - -/obj/item/clothing/suit/space/syndicate/orange - name = "Orange Space Suit" - icon_state = "syndicate-orange" - item_state = "syndicate-orange" - - -//Blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/blue - name = "Blue Space Helmet" - icon_state = "syndicate-helm-blue" - item_state = "syndicate-helm-blue" - -/obj/item/clothing/suit/space/syndicate/blue - name = "Blue Space Suit" - icon_state = "syndicate-blue" - item_state = "syndicate-blue" - - -//Black syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black - name = "Black Space Helmet" - icon_state = "syndicate-helm-black" - item_state = "syndicate-helm-black" - -obj/item/clothing/head/helmet/space/syndicate/black/strike - name = "Syndicate Strike Team commando helmet" - desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -/obj/item/clothing/suit/space/syndicate/black - name = "Black Space Suit" - icon_state = "syndicate-black" - item_state = "syndicate-black" - -obj/item/clothing/suit/space/syndicate/black/strike - name = "Syndicate Strike Team commando space suit" - desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -//Black-green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/green - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-green" - item_state = "syndicate-helm-black-green" - -/obj/item/clothing/suit/space/syndicate/black/green - name = "Black and Green Space Suit" - icon_state = "syndicate-black-green" - item_state = "syndicate-black-green" - - -//Black-blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/blue - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-blue" - item_state = "syndicate-helm-black-blue" - -/obj/item/clothing/suit/space/syndicate/black/blue - name = "Black and Blue Space Suit" - icon_state = "syndicate-black-blue" - item_state = "syndicate-black-blue" - - -//Black medical syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/med - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-med" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/med - name = "Green Space Suit" - icon_state = "syndicate-black-med" - item_state = "syndicate-black" - - -//Black-orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/orange - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-orange" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/orange - name = "Black and Orange Space Suit" - icon_state = "syndicate-black-orange" - item_state = "syndicate-black" - - -//Black-red syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/red - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-red" - item_state = "syndicate-helm-black-red" - -obj/item/clothing/head/helmet/space/syndicate/black/red/strike - name = "Syndicate Strike Team leader helmet" - desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -/obj/item/clothing/suit/space/syndicate/black/red - name = "Black and Red Space Suit" - icon_state = "syndicate-black-red" - item_state = "syndicate-black-red" - -obj/item/clothing/suit/space/syndicate/black/red/strike - name = "Syndicate Strike Team leader space suit" - desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - - -//Black with yellow/red engineering syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/engie - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-engie" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/engie - name = "Black Engineering Space Suit" - icon_state = "syndicate-black-engie" - item_state = "syndicate-black" +//Regular syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate + name = "red space helmet" + desc = "Top secret Spess Helmet." + icon_state = "syndicate" + item_state = "syndicate" + desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi') + + +/obj/item/clothing/suit/space/syndicate + name = "red space suit" + icon_state = "syndicate" + item_state = "space_suit_syndicate" + desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi') + + +//Green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green + name = "Green Space Helmet" + icon_state = "syndicate-helm-green" + item_state = "syndicate-helm-green" + +/obj/item/clothing/suit/space/syndicate/green + name = "Green Space Suit" + icon_state = "syndicate-green" + item_state = "syndicate-green" + + +//Dark green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green/dark + name = "Dark Green Space Helmet" + icon_state = "syndicate-helm-green-dark" + item_state = "syndicate-helm-green-dark" + +/obj/item/clothing/suit/space/syndicate/green/dark + name = "Dark Green Space Suit" + icon_state = "syndicate-green-dark" + item_state = "syndicate-green-dark" + + +//Orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/orange + name = "Orange Space Helmet" + icon_state = "syndicate-helm-orange" + item_state = "syndicate-helm-orange" + +/obj/item/clothing/suit/space/syndicate/orange + name = "Orange Space Suit" + icon_state = "syndicate-orange" + item_state = "syndicate-orange" + + +//Blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/blue + name = "Blue Space Helmet" + icon_state = "syndicate-helm-blue" + item_state = "syndicate-helm-blue" + +/obj/item/clothing/suit/space/syndicate/blue + name = "Blue Space Suit" + icon_state = "syndicate-blue" + item_state = "syndicate-blue" + + +//Black syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black + name = "Black Space Helmet" + icon_state = "syndicate-helm-black" + item_state = "syndicate-helm-black" + +obj/item/clothing/head/helmet/space/syndicate/black/strike + name = "Syndicate Strike Team commando helmet" + desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +/obj/item/clothing/suit/space/syndicate/black + name = "Black Space Suit" + icon_state = "syndicate-black" + item_state = "syndicate-black" + +obj/item/clothing/suit/space/syndicate/black/strike + name = "Syndicate Strike Team commando space suit" + desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +//Black-green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/green + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-green" + item_state = "syndicate-helm-black-green" + +/obj/item/clothing/suit/space/syndicate/black/green + name = "Black and Green Space Suit" + icon_state = "syndicate-black-green" + item_state = "syndicate-black-green" + + +//Black-blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/blue + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-blue" + item_state = "syndicate-helm-black-blue" + +/obj/item/clothing/suit/space/syndicate/black/blue + name = "Black and Blue Space Suit" + icon_state = "syndicate-black-blue" + item_state = "syndicate-black-blue" + + +//Black medical syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/med + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-med" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/med + name = "Green Space Suit" + icon_state = "syndicate-black-med" + item_state = "syndicate-black" + + +//Black-orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/orange + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-orange" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/orange + name = "Black and Orange Space Suit" + icon_state = "syndicate-black-orange" + item_state = "syndicate-black" + + +//Black-red syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/red + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-red" + item_state = "syndicate-helm-black-red" + +obj/item/clothing/head/helmet/space/syndicate/black/red/strike + name = "Syndicate Strike Team leader helmet" + desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +/obj/item/clothing/suit/space/syndicate/black/red + name = "Black and Red Space Suit" + icon_state = "syndicate-black-red" + item_state = "syndicate-black-red" + +obj/item/clothing/suit/space/syndicate/black/red/strike + name = "Syndicate Strike Team leader space suit" + desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + + +//Black with yellow/red engineering syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/engie + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-engie" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/engie + name = "Black Engineering Space Suit" + icon_state = "syndicate-black-engie" + item_state = "syndicate-black" diff --git a/code/modules/clothing/spacesuits/void.dm b/code/modules/clothing/spacesuits/void.dm index 0f97be88e105..04c5edf20ecd 100644 --- a/code/modules/clothing/spacesuits/void.dm +++ b/code/modules/clothing/spacesuits/void.dm @@ -1,124 +1,124 @@ - -//Voidsuits -/obj/item/clothing/head/helmet/space/nasavoid - name = "NASA Void Helmet" - desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance." - icon_state = "void-red" - item_state = "void" - flags_inv = HIDEMASK|HIDEEARS - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi') - -/obj/item/clothing/suit/space/nasavoid - name = "NASA Void Suit" - icon_state = "void-red" - item_state = "void" - desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance." - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/multitool) - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi') - -//Colors!!! -/obj/item/clothing/head/helmet/space/nasavoid/green - icon_state = "void-green" - -/obj/item/clothing/suit/space/nasavoid/green - icon_state = "void-green" - -/obj/item/clothing/head/helmet/space/nasavoid/ntblue - icon_state = "void-ntblue" - -/obj/item/clothing/suit/space/nasavoid/ntblue - icon_state = "void-ntblue" - -/obj/item/clothing/head/helmet/space/nasavoid/purple - icon_state = "void-purple" - -/obj/item/clothing/suit/space/nasavoid/purple - icon_state = "void-purple" - -/obj/item/clothing/head/helmet/space/nasavoid/yellow - icon_state = "void-yellow" - -/obj/item/clothing/suit/space/nasavoid/yellow - icon_state = "void-yellow" - -/obj/item/clothing/head/helmet/space/nasavoid/ltblue - icon_state = "void-light_blue" - -/obj/item/clothing/suit/space/nasavoid/ltblue - icon_state = "void-light_blue" - - -//Captian's Suit, like the other captian's suit, but looks better, at the cost of armor -/obj/item/clothing/head/helmet/space/nasavoid/captain - name = "Fancy Retro Void Helmet" - icon_state = "void-captian" - desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance. This one is fit for a captain." - -/obj/item/clothing/suit/space/nasavoid/captain - name = "Fancy NASA Void Suit" - icon_state = "void-captian" - desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance. This one is fit for a captain." - -//Syndi's suit, on par with a blood red softsuit - -/obj/item/clothing/head/helmet/space/nasavoid/syndi - name = "Blood Red Retro Void Helmet" - icon_state = "void-syndi" - desc = "A high tech, NASA Centcom branch designed space suit helmet. This one looks rather suspicious." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - -/obj/item/clothing/suit/space/nasavoid/syndi - name = "Blood Red NASA Void Suit" - icon_state = "void-syndi" - desc = "A high tech, NASA Centcom branch designed space suit. This one looks rather suspicious." - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - -//random spawner - -/obj/effect/nasavoidsuitspawner - name = "NASA Void Suit Spawner" - icon = 'icons/obj/clothing/suits.dmi' - icon_state = "void-red" - desc = "You shouldn't see this, a spawner for NASA Void Suits." - var/suits = list("red", "green", "ntblue", "purple", "yellow", "ltblue") - -/obj/effect/nasavoidsuitspawner/New() - var/obj/item/clothing/head/helmet/space/nasavoid/H - var/obj/item/clothing/suit/space/nasavoid/S - switch(pick(suits)) - if("red") - H = new /obj/item/clothing/head/helmet/space/nasavoid - S = new /obj/item/clothing/suit/space/nasavoid - if("green") - H = new /obj/item/clothing/head/helmet/space/nasavoid/green - S = new /obj/item/clothing/suit/space/nasavoid/green - if("ntblue") - H = new /obj/item/clothing/head/helmet/space/nasavoid/ntblue - S = new /obj/item/clothing/suit/space/nasavoid/ntblue - if("purple") - H = new /obj/item/clothing/head/helmet/space/nasavoid/purple - S = new /obj/item/clothing/suit/space/nasavoid/purple - if("yellow") - H = new /obj/item/clothing/head/helmet/space/nasavoid/yellow - S = new /obj/item/clothing/suit/space/nasavoid/yellow - if("ltblue") - H = new /obj/item/clothing/head/helmet/space/nasavoid/ltblue - S = new /obj/item/clothing/suit/space/nasavoid/ltblue - var/turf/T = get_turf(src) - if(H) - H.forceMove(T) - if(S) - S.forceMove(T) - qdel(src) \ No newline at end of file + +//Voidsuits +/obj/item/clothing/head/helmet/space/nasavoid + name = "NASA Void Helmet" + desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance." + icon_state = "void-red" + item_state = "void" + flags_inv = HIDEMASK|HIDEEARS + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi') + +/obj/item/clothing/suit/space/nasavoid + name = "NASA Void Suit" + icon_state = "void-red" + item_state = "void" + desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance." + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/multitool) + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi') + +//Colors!!! +/obj/item/clothing/head/helmet/space/nasavoid/green + icon_state = "void-green" + +/obj/item/clothing/suit/space/nasavoid/green + icon_state = "void-green" + +/obj/item/clothing/head/helmet/space/nasavoid/ntblue + icon_state = "void-ntblue" + +/obj/item/clothing/suit/space/nasavoid/ntblue + icon_state = "void-ntblue" + +/obj/item/clothing/head/helmet/space/nasavoid/purple + icon_state = "void-purple" + +/obj/item/clothing/suit/space/nasavoid/purple + icon_state = "void-purple" + +/obj/item/clothing/head/helmet/space/nasavoid/yellow + icon_state = "void-yellow" + +/obj/item/clothing/suit/space/nasavoid/yellow + icon_state = "void-yellow" + +/obj/item/clothing/head/helmet/space/nasavoid/ltblue + icon_state = "void-light_blue" + +/obj/item/clothing/suit/space/nasavoid/ltblue + icon_state = "void-light_blue" + + +//Captian's Suit, like the other captian's suit, but looks better, at the cost of armor +/obj/item/clothing/head/helmet/space/nasavoid/captain + name = "Fancy Retro Void Helmet" + icon_state = "void-captian" + desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance. This one is fit for a captain." + +/obj/item/clothing/suit/space/nasavoid/captain + name = "Fancy NASA Void Suit" + icon_state = "void-captian" + desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance. This one is fit for a captain." + +//Syndi's suit, on par with a blood red softsuit + +/obj/item/clothing/head/helmet/space/nasavoid/syndi + name = "Blood Red Retro Void Helmet" + icon_state = "void-syndi" + desc = "A high tech, NASA Centcom branch designed space suit helmet. This one looks rather suspicious." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + +/obj/item/clothing/suit/space/nasavoid/syndi + name = "Blood Red NASA Void Suit" + icon_state = "void-syndi" + desc = "A high tech, NASA Centcom branch designed space suit. This one looks rather suspicious." + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + +//random spawner + +/obj/effect/nasavoidsuitspawner + name = "NASA Void Suit Spawner" + icon = 'icons/obj/clothing/suits.dmi' + icon_state = "void-red" + desc = "You shouldn't see this, a spawner for NASA Void Suits." + var/suits = list("red", "green", "ntblue", "purple", "yellow", "ltblue") + +/obj/effect/nasavoidsuitspawner/New() + var/obj/item/clothing/head/helmet/space/nasavoid/H + var/obj/item/clothing/suit/space/nasavoid/S + switch(pick(suits)) + if("red") + H = new /obj/item/clothing/head/helmet/space/nasavoid + S = new /obj/item/clothing/suit/space/nasavoid + if("green") + H = new /obj/item/clothing/head/helmet/space/nasavoid/green + S = new /obj/item/clothing/suit/space/nasavoid/green + if("ntblue") + H = new /obj/item/clothing/head/helmet/space/nasavoid/ntblue + S = new /obj/item/clothing/suit/space/nasavoid/ntblue + if("purple") + H = new /obj/item/clothing/head/helmet/space/nasavoid/purple + S = new /obj/item/clothing/suit/space/nasavoid/purple + if("yellow") + H = new /obj/item/clothing/head/helmet/space/nasavoid/yellow + S = new /obj/item/clothing/suit/space/nasavoid/yellow + if("ltblue") + H = new /obj/item/clothing/head/helmet/space/nasavoid/ltblue + S = new /obj/item/clothing/suit/space/nasavoid/ltblue + var/turf/T = get_turf(src) + if(H) + H.forceMove(T) + if(S) + S.forceMove(T) + qdel(src) diff --git a/code/modules/clothing/suits/alien.dm b/code/modules/clothing/suits/alien.dm index 0f82a9eae7e5..1fde5cd6f84c 100644 --- a/code/modules/clothing/suits/alien.dm +++ b/code/modules/clothing/suits/alien.dm @@ -16,4 +16,4 @@ desc = "A rather grisly selection of cured hides and skin, sewn together to form a ragged mantle." icon_state = "mantle-unathi" item_state = "mantle-unathi" - body_parts_covered = UPPER_TORSO \ No newline at end of file + body_parts_covered = UPPER_TORSO diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 037e967f5ee8..e9f78936cf72 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,544 +1,544 @@ -/obj/item/clothing/suit/armor - allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic,/obj/item/kitchen/knife/combat) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - max_integrity = 250 - resistance_flags = NONE - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/armor/vest - name = "armor" - desc = "An armored vest that protects against some damage." - icon_state = "armor" - item_state = "armor" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - dog_fashion = /datum/dog_fashion/back - -/obj/item/clothing/suit/armor/vest/jacket - name = "military jacket" - desc = "An old military jacket, it has armoring." - icon_state = "militaryjacket" - item_state = "militaryjacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/armor/vest/combat - name = "combat vest" - desc = "An armored vest that protects against some damage." - icon_state = "armor-combat" - item_state = "bulletproof" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/suit/armor/vest/security - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." - icon_state = "armor" - item_state = "armor" - var/obj/item/clothing/accessory/holobadge/attached_badge - -/obj/item/clothing/suit/armor/vest/security/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/clothing/accessory/holobadge)) - if(user.unEquip(W)) - add_fingerprint(user) - W.forceMove(src) - attached_badge = W - - var/datum/action/A = new /datum/action/item_action/remove_badge(src) - A.Grant(user) - icon_state = "armorsec" - user.update_inv_wear_suit() - desc = "An armored vest that protects against some damage. This one has [attached_badge] attached to it." - to_chat(user, "You attach [attached_badge] to [src].") - return - ..() - -/obj/item/clothing/suit/armor/vest/security/attack_self(mob/user as mob) - if(attached_badge) - add_fingerprint(user) - user.put_in_hands(attached_badge) - - for(var/X in actions) - var/datum/action/A = X - A.Remove(user) - - icon_state = "armor" - user.update_inv_wear_suit() - desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." - to_chat(user, "You remove [attached_badge] from [src].") - - attached_badge = null - - return - - ..() - -/obj/item/clothing/suit/armor/vest/blueshield - name = "blueshield security armor" - desc = "An armored vest with the badge of a Blueshield Lieutenant." - icon_state = "blueshield" - item_state = "blueshield" - -/obj/item/clothing/suit/armor/vest/bloody - name = "bloodied security armor" - desc = "A vest drenched in the blood of Greytide. It has seen better days." - icon_state = "bloody_armor" - item_state = "bloody_armor" - sprite_sheets = null - -/obj/item/clothing/suit/armor/secjacket - name = "security jacket" - desc = "A sturdy black jacket with reinforced fabric. Bears insignia of NT corporate security." - icon_state = "secjacket_open" - item_state = "hos" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0, fire = 30, acid = 30) - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/openclose) - adjust_flavour = "unzip" - -/obj/item/clothing/suit/armor/hos - name = "armored coat" - desc = "A trench coat enhanced with a special alloy for some protection and style." - icon_state = "hos" - item_state = "hos" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90) - flags_inv = HIDEJUMPSUIT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - strip_delay = 80 - -/obj/item/clothing/suit/armor/hos/alt - name = "armored trenchoat" - desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes." - icon_state = "hostrench_open" - item_state = "hostrench_open" - flags_inv = 0 - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/openclose) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/armor/hos/jensen - name = "armored trenchcoat" - desc = "A trenchcoat augmented with a special alloy for some protection and style." - icon_state = "jensencoat" - item_state = "jensencoat" - flags_inv = 0 - sprite_sheets = null - -/obj/item/clothing/suit/armor/vest/warden - name = "Warden's armored jacket" - desc = "An armored jacket with silver rank pips and livery." - icon_state = "warden_jacket" - item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|HANDS - strip_delay = 70 - resistance_flags = FLAMMABLE - dog_fashion = null - -/obj/item/clothing/suit/armor/vest/warden/alt - name = "warden's jacket" - desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." - icon_state = "warden_jacket_alt" - -/obj/item/clothing/suit/armor/vest/capcarapace - name = "captain's carapace" - desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." - icon_state = "capcarapace" - item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90) - dog_fashion = null - resistance_flags = FIRE_PROOF - -/obj/item/clothing/suit/armor/vest/capcarapace/alt - name = "captain's parade jacket" - desc = "For when an armored vest isn't fashionable enough." - icon_state = "capformal" - item_state = "capspacesuit" - -/obj/item/clothing/suit/armor/riot - name = "Riot Suit" - desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." - icon_state = "riot" - item_state = "swat_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - flags_inv = HIDEJUMPSUIT - strip_delay = 80 - put_on_delay = 60 - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/riot/knight - name = "plate armour" - desc = "A classic suit of plate armour, highly effective at stopping melee attacks." - icon_state = "knight_green" - item_state = "knight_green" - slowdown = 1 - -/obj/item/clothing/suit/armor/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/suit/armor/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/suit/armor/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/suit/armor/riot/knight/templar - name = "crusader armour" - desc = "God wills it!" - icon_state = "knight_templar" - item_state = "knight_templar" - allowed = list(/obj/item/nullrod/claymore) - armor = list(melee = 25, bullet = 5, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) - -/obj/item/clothing/suit/armor/vest/durathread - name = "durathread vest" - desc = "A vest made of durathread with strips of leather acting as trauma plates." - icon_state = "durathread" - item_state = "durathread" - strip_delay = 60 - max_integrity = 200 - resistance_flags = FLAMMABLE - armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) - -/obj/item/clothing/suit/armor/bulletproof - name = "Bulletproof Vest" - desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "bulletproof" - item_state = "armor" - blood_overlay_type = "armor" - armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 70 - put_on_delay = 50 - -/obj/item/clothing/suit/armor/laserproof - name = "Ablative Armor Vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - item_state = "armor_reflec" - blood_overlay_type = "armor" - armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/hit_reflect_chance = 40 - -/obj/item/clothing/suit/armor/laserproof/IsReflect(var/def_zone) - if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! - return 0 - if(prob(hit_reflect_chance)) - return 1 - -/obj/item/clothing/suit/armor/vest/det_suit - name = "armor" - desc = "An armored vest with a detective's badge on it." - icon_state = "detective-armor" - item_state = "armor" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/reagent_containers/spray/pepper,/obj/item/flashlight,/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/storage/fancy/cigarettes,/obj/item/lighter,/obj/item/detective_scanner,/obj/item/taperecorder) - resistance_flags = FLAMMABLE - dog_fashion = null - -//Reactive armor -/obj/item/clothing/suit/armor/reactive - name = "reactive armor" - desc = "Doesn't seem to do much for some reason." - var/active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - blood_overlay_type = "armor" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - actions_types = list(/datum/action/item_action/toggle) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - hit_reaction_chance = 50 - -/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) - active = !(active) - if(active) - to_chat(user, "[src] is now active.") - icon_state = "reactive" - item_state = "reactive" - else - to_chat(user, "[src] is now inactive.") - icon_state = "reactiveoff" - item_state = "reactiveoff" - add_fingerprint(user) - user.update_inv_wear_suit() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/suit/armor/reactive/emp_act(severity) - active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - if(istype(loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/C = loc - C.update_inv_wear_suit() - ..() - -//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) -/obj/item/clothing/suit/armor/reactive/teleport - name = "reactive teleport armor" - desc = "Someone seperated our Research Director from his own head!" - var/tele_range = 6 - -/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/carbon/human/H = owner - owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") - var/list/turfs = new/list() - for(var/turf/T in orange(tele_range, H)) - if(istype(T, /turf/space)) - continue - if(T.density) - continue - if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.yThe [src] blocks the [attack_text], sending out jets of flame!") - for(var/mob/living/carbon/C in range(6, owner)) - if(C != owner) - C.fire_stacks += 8 - C.IgniteMob() - owner.fire_stacks = -20 - return 1 - return 0 - - -/obj/item/clothing/suit/armor/reactive/stealth - name = "reactive stealth armor" - -/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) - E.Copy_Parent(owner, 50) - E.GiveTarget(owner) //so it starts running right away - E.Goto(owner, E.move_to_delay, E.minimum_distance) - owner.alpha = 0 - owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise - spawn(40) - owner.alpha = initial(owner.alpha) - return 1 - -/obj/item/clothing/suit/armor/reactive/tesla - name = "reactive tesla armor" - -/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - owner.visible_message("The [src] blocks the [attack_text], sending out arcs of lightning!") - for(var/mob/living/M in view(6, owner)) - if(M == owner) - continue - owner.Beam(M,icon_state="lightning[rand(1, 12)]",icon='icons/effects/effects.dmi',time=5) - M.adjustFireLoss(25) - playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) - return 1 - -//All of the armor below is mostly unused - - -/obj/item/clothing/suit/armor/centcomm - name = "Cent. Com. armor" - desc = "A suit that protects against some damage." - icon_state = "centcom" - item_state = "centcom" - w_class = WEIGHT_CLASS_BULKY - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank/emergency_oxygen) - flags = THICKMATERIAL - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - sprite_sheets = null - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - -/obj/item/clothing/suit/armor/heavy - name = "heavy armor" - desc = "A heavily armored suit that protects against moderate damage." - icon_state = "heavy" - item_state = "swat_suit" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 3 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/tdome - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - flags = THICKMATERIAL - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/tdome/red - name = "Red Thunderdome Armor" - desc = "Armor worn by the red Thunderdome team." - icon_state = "tdred" - item_state = "tdred" - -/obj/item/clothing/suit/armor/tdome/green - name = "Green Thunderdome Armor" - desc = "Armor worn by the green Thunderdome team." - icon_state = "tdgreen" - item_state = "tdgreen" - -//Non-hardsuit ERT armor. -/obj/item/clothing/suit/armor/vest/ert - name = "emergency response team armor" - desc = "A set of armor worn by members of the Nanotrasen Emergency Response Team." - icon_state = "ertarmor_cmd" - item_state = "armor" - armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 20, bio = 0, rad = 0, fire = 50, acid = 50) - -//Commander -/obj/item/clothing/suit/armor/vest/ert/command - name = "emergency response team commander armor" - desc = "A set of armor worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/suit/armor/vest/ert/security - name = "emergency response team security armor" - desc = "A set of armor worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." - icon_state = "ertarmor_sec" - - -/obj/item/clothing/suit/armor/vest/ert/security/paranormal - name = "emergency response team paranormal armor" - desc = "A set of armor worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." - icon_state = "knight_templar" - item_state = "knight_templar" - -//Engineer -/obj/item/clothing/suit/armor/vest/ert/engineer - name = "emergency response team engineer armor" - desc = "A set of armor worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." - icon_state = "ertarmor_eng" - -//Medical -/obj/item/clothing/suit/armor/vest/ert/medical - name = "emergency response team medical armor" - desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "ertarmor_med" - -//Janitorial -/obj/item/clothing/suit/armor/vest/ert/janitor - name = "emergency response team janitor armor" - desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "ertarmor_jan" - -//same defense as basic sec armor -/obj/item/clothing/suit/storage/lawyer/blackjacket/armored - desc = "A snappy dress jacket, reinforced with a layer of armor protecting the torso." - allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/gun/projectile/revolver, /obj/item/gun/projectile/automatic/pistol) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 40, acid = 40) - -//LAVALAND! - -/obj/item/clothing/suit/hooded/drake - name = "drake armour" - icon_state = "dragon" - item_state = "dragon" - desc = "A suit of armour fashioned from the remains of an ash drake." - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear) - armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) - hoodtype = /obj/item/clothing/head/hooded/drake - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/head/hooded/drake - name = "drake helmet" - icon_state = "dragon" - item_state = "dragon" - desc = "The skull of a dragon." - armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) - heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/hooded/goliath - name = "goliath cloak" - icon_state = "goliath_cloak" - item_state = "goliath_cloak" - desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival) - armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot - hoodtype = /obj/item/clothing/head/hooded/goliath - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/head/hooded/goliath - name = "goliath cloak hood" - icon_state = "golhood" - item_state = "golhood" - desc = "A protective & concealing hood." - armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/armor/bone - name = "bone armor" - desc = "A tribal armor plate, crafted from animal bone." - icon_state = "bonearmor" - item_state = "bonearmor" - blood_overlay_type = "armor" - armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS \ No newline at end of file +/obj/item/clothing/suit/armor + allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic,/obj/item/kitchen/knife/combat) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + max_integrity = 250 + resistance_flags = NONE + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/armor/vest + name = "armor" + desc = "An armored vest that protects against some damage." + icon_state = "armor" + item_state = "armor" + blood_overlay_type = "armor" + flags_size = ONESIZEFITSALL + dog_fashion = /datum/dog_fashion/back + +/obj/item/clothing/suit/armor/vest/jacket + name = "military jacket" + desc = "An old military jacket, it has armoring." + icon_state = "militaryjacket" + item_state = "militaryjacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/armor/vest/combat + name = "combat vest" + desc = "An armored vest that protects against some damage." + icon_state = "armor-combat" + item_state = "bulletproof" + blood_overlay_type = "armor" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/suit/armor/vest/security + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." + icon_state = "armor" + item_state = "armor" + var/obj/item/clothing/accessory/holobadge/attached_badge + +/obj/item/clothing/suit/armor/vest/security/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/clothing/accessory/holobadge)) + if(user.unEquip(W)) + add_fingerprint(user) + W.forceMove(src) + attached_badge = W + + var/datum/action/A = new /datum/action/item_action/remove_badge(src) + A.Grant(user) + icon_state = "armorsec" + user.update_inv_wear_suit() + desc = "An armored vest that protects against some damage. This one has [attached_badge] attached to it." + to_chat(user, "You attach [attached_badge] to [src].") + return + ..() + +/obj/item/clothing/suit/armor/vest/security/attack_self(mob/user as mob) + if(attached_badge) + add_fingerprint(user) + user.put_in_hands(attached_badge) + + for(var/X in actions) + var/datum/action/A = X + A.Remove(user) + + icon_state = "armor" + user.update_inv_wear_suit() + desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." + to_chat(user, "You remove [attached_badge] from [src].") + + attached_badge = null + + return + + ..() + +/obj/item/clothing/suit/armor/vest/blueshield + name = "blueshield security armor" + desc = "An armored vest with the badge of a Blueshield Lieutenant." + icon_state = "blueshield" + item_state = "blueshield" + +/obj/item/clothing/suit/armor/vest/bloody + name = "bloodied security armor" + desc = "A vest drenched in the blood of Greytide. It has seen better days." + icon_state = "bloody_armor" + item_state = "bloody_armor" + sprite_sheets = null + +/obj/item/clothing/suit/armor/secjacket + name = "security jacket" + desc = "A sturdy black jacket with reinforced fabric. Bears insignia of NT corporate security." + icon_state = "secjacket_open" + item_state = "hos" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0, fire = 30, acid = 30) + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/openclose) + adjust_flavour = "unzip" + +/obj/item/clothing/suit/armor/hos + name = "armored coat" + desc = "A trench coat enhanced with a special alloy for some protection and style." + icon_state = "hos" + item_state = "hos" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90) + flags_inv = HIDEJUMPSUIT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + strip_delay = 80 + +/obj/item/clothing/suit/armor/hos/alt + name = "armored trenchoat" + desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes." + icon_state = "hostrench_open" + item_state = "hostrench_open" + flags_inv = 0 + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/openclose) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/armor/hos/jensen + name = "armored trenchcoat" + desc = "A trenchcoat augmented with a special alloy for some protection and style." + icon_state = "jensencoat" + item_state = "jensencoat" + flags_inv = 0 + sprite_sheets = null + +/obj/item/clothing/suit/armor/vest/warden + name = "Warden's armored jacket" + desc = "An armored jacket with silver rank pips and livery." + icon_state = "warden_jacket" + item_state = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|HANDS + strip_delay = 70 + resistance_flags = FLAMMABLE + dog_fashion = null + +/obj/item/clothing/suit/armor/vest/warden/alt + name = "warden's jacket" + desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." + icon_state = "warden_jacket_alt" + +/obj/item/clothing/suit/armor/vest/capcarapace + name = "captain's carapace" + desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." + icon_state = "capcarapace" + item_state = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90) + dog_fashion = null + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/armor/vest/capcarapace/alt + name = "captain's parade jacket" + desc = "For when an armored vest isn't fashionable enough." + icon_state = "capformal" + item_state = "capspacesuit" + +/obj/item/clothing/suit/armor/riot + name = "Riot Suit" + desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." + icon_state = "riot" + item_state = "swat_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + flags_inv = HIDEJUMPSUIT + strip_delay = 80 + put_on_delay = 60 + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/riot/knight + name = "plate armour" + desc = "A classic suit of plate armour, highly effective at stopping melee attacks." + icon_state = "knight_green" + item_state = "knight_green" + slowdown = 1 + +/obj/item/clothing/suit/armor/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/suit/armor/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/suit/armor/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/suit/armor/riot/knight/templar + name = "crusader armour" + desc = "God wills it!" + icon_state = "knight_templar" + item_state = "knight_templar" + allowed = list(/obj/item/nullrod/claymore) + armor = list(melee = 25, bullet = 5, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) + +/obj/item/clothing/suit/armor/vest/durathread + name = "durathread vest" + desc = "A vest made of durathread with strips of leather acting as trauma plates." + icon_state = "durathread" + item_state = "durathread" + strip_delay = 60 + max_integrity = 200 + resistance_flags = FLAMMABLE + armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) + +/obj/item/clothing/suit/armor/bulletproof + name = "Bulletproof Vest" + desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "bulletproof" + item_state = "armor" + blood_overlay_type = "armor" + armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 70 + put_on_delay = 50 + +/obj/item/clothing/suit/armor/laserproof + name = "Ablative Armor Vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + item_state = "armor_reflec" + blood_overlay_type = "armor" + armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/hit_reflect_chance = 40 + +/obj/item/clothing/suit/armor/laserproof/IsReflect(var/def_zone) + if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! + return 0 + if(prob(hit_reflect_chance)) + return 1 + +/obj/item/clothing/suit/armor/vest/det_suit + name = "armor" + desc = "An armored vest with a detective's badge on it." + icon_state = "detective-armor" + item_state = "armor" + blood_overlay_type = "armor" + flags_size = ONESIZEFITSALL + allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/reagent_containers/spray/pepper,/obj/item/flashlight,/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/storage/fancy/cigarettes,/obj/item/lighter,/obj/item/detective_scanner,/obj/item/taperecorder) + resistance_flags = FLAMMABLE + dog_fashion = null + +//Reactive armor +/obj/item/clothing/suit/armor/reactive + name = "reactive armor" + desc = "Doesn't seem to do much for some reason." + var/active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + blood_overlay_type = "armor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + actions_types = list(/datum/action/item_action/toggle) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + hit_reaction_chance = 50 + +/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) + active = !(active) + if(active) + to_chat(user, "[src] is now active.") + icon_state = "reactive" + item_state = "reactive" + else + to_chat(user, "[src] is now inactive.") + icon_state = "reactiveoff" + item_state = "reactiveoff" + add_fingerprint(user) + user.update_inv_wear_suit() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/suit/armor/reactive/emp_act(severity) + active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + if(istype(loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/C = loc + C.update_inv_wear_suit() + ..() + +//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) +/obj/item/clothing/suit/armor/reactive/teleport + name = "reactive teleport armor" + desc = "Someone seperated our Research Director from his own head!" + var/tele_range = 6 + +/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/carbon/human/H = owner + owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") + var/list/turfs = new/list() + for(var/turf/T in orange(tele_range, H)) + if(istype(T, /turf/space)) + continue + if(T.density) + continue + if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.yThe [src] blocks the [attack_text], sending out jets of flame!") + for(var/mob/living/carbon/C in range(6, owner)) + if(C != owner) + C.fire_stacks += 8 + C.IgniteMob() + owner.fire_stacks = -20 + return 1 + return 0 + + +/obj/item/clothing/suit/armor/reactive/stealth + name = "reactive stealth armor" + +/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) + E.Copy_Parent(owner, 50) + E.GiveTarget(owner) //so it starts running right away + E.Goto(owner, E.move_to_delay, E.minimum_distance) + owner.alpha = 0 + owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise + spawn(40) + owner.alpha = initial(owner.alpha) + return 1 + +/obj/item/clothing/suit/armor/reactive/tesla + name = "reactive tesla armor" + +/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + owner.visible_message("The [src] blocks the [attack_text], sending out arcs of lightning!") + for(var/mob/living/M in view(6, owner)) + if(M == owner) + continue + owner.Beam(M,icon_state="lightning[rand(1, 12)]",icon='icons/effects/effects.dmi',time=5) + M.adjustFireLoss(25) + playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) + return 1 + +//All of the armor below is mostly unused + + +/obj/item/clothing/suit/armor/centcomm + name = "Cent. Com. armor" + desc = "A suit that protects against some damage." + icon_state = "centcom" + item_state = "centcom" + w_class = WEIGHT_CLASS_BULKY + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank/emergency_oxygen) + flags = THICKMATERIAL + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + sprite_sheets = null + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + +/obj/item/clothing/suit/armor/heavy + name = "heavy armor" + desc = "A heavily armored suit that protects against moderate damage." + icon_state = "heavy" + item_state = "swat_suit" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 3 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/tdome + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + flags = THICKMATERIAL + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/tdome/red + name = "Red Thunderdome Armor" + desc = "Armor worn by the red Thunderdome team." + icon_state = "tdred" + item_state = "tdred" + +/obj/item/clothing/suit/armor/tdome/green + name = "Green Thunderdome Armor" + desc = "Armor worn by the green Thunderdome team." + icon_state = "tdgreen" + item_state = "tdgreen" + +//Non-hardsuit ERT armor. +/obj/item/clothing/suit/armor/vest/ert + name = "emergency response team armor" + desc = "A set of armor worn by members of the Nanotrasen Emergency Response Team." + icon_state = "ertarmor_cmd" + item_state = "armor" + armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 20, bio = 0, rad = 0, fire = 50, acid = 50) + +//Commander +/obj/item/clothing/suit/armor/vest/ert/command + name = "emergency response team commander armor" + desc = "A set of armor worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/suit/armor/vest/ert/security + name = "emergency response team security armor" + desc = "A set of armor worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." + icon_state = "ertarmor_sec" + + +/obj/item/clothing/suit/armor/vest/ert/security/paranormal + name = "emergency response team paranormal armor" + desc = "A set of armor worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." + icon_state = "knight_templar" + item_state = "knight_templar" + +//Engineer +/obj/item/clothing/suit/armor/vest/ert/engineer + name = "emergency response team engineer armor" + desc = "A set of armor worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." + icon_state = "ertarmor_eng" + +//Medical +/obj/item/clothing/suit/armor/vest/ert/medical + name = "emergency response team medical armor" + desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "ertarmor_med" + +//Janitorial +/obj/item/clothing/suit/armor/vest/ert/janitor + name = "emergency response team janitor armor" + desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "ertarmor_jan" + +//same defense as basic sec armor +/obj/item/clothing/suit/storage/lawyer/blackjacket/armored + desc = "A snappy dress jacket, reinforced with a layer of armor protecting the torso." + allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/gun/projectile/revolver, /obj/item/gun/projectile/automatic/pistol) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 40, acid = 40) + +//LAVALAND! + +/obj/item/clothing/suit/hooded/drake + name = "drake armour" + icon_state = "dragon" + item_state = "dragon" + desc = "A suit of armour fashioned from the remains of an ash drake." + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear) + armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) + hoodtype = /obj/item/clothing/head/hooded/drake + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/head/hooded/drake + name = "drake helmet" + icon_state = "dragon" + item_state = "dragon" + desc = "The skull of a dragon." + armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/hooded/goliath + name = "goliath cloak" + icon_state = "goliath_cloak" + item_state = "goliath_cloak" + desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival) + armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot + hoodtype = /obj/item/clothing/head/hooded/goliath + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/head/hooded/goliath + name = "goliath cloak hood" + icon_state = "golhood" + item_state = "golhood" + desc = "A protective & concealing hood." + armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/armor/bone + name = "bone armor" + desc = "A tribal armor plate, crafted from animal bone." + icon_state = "bonearmor" + item_state = "bonearmor" + blood_overlay_type = "armor" + armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index 6a2d582d3551..1b7100c02bc9 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -1,111 +1,111 @@ -//Biosuit complete with shoes (in the item sprite) -/obj/item/clothing/head/bio_hood - name = "bio hood" - icon_state = "bio" - desc = "A hood that protects the head and face from biological comtaminants." - permeability_coefficient = 0.01 - flags = BLOCKHAIR | THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - resistance_flags = ACID_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/head.dmi', - "Tajaran" = 'icons/mob/species/tajaran/head.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/bio_suit - name = "bio suit" - desc = "A suit that protects against biological contamination." - icon_state = "bio" - item_state = "bio_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - flags = THICKMATERIAL - flags_size = ONESIZEFITSALL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 1 - allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - strip_delay = 70 - put_on_delay = 70 - resistance_flags = ACID_PROOF - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - - -//Standard biosuit, orange stripe -/obj/item/clothing/head/bio_hood/general - icon_state = "bio_general" - -/obj/item/clothing/suit/bio_suit/general - icon_state = "bio_general" - - -//Virology biosuit, green stripe -/obj/item/clothing/head/bio_hood/virology - icon_state = "bio_virology" - -/obj/item/clothing/suit/bio_suit/virology - icon_state = "bio_virology" - - -//Security biosuit, grey with red stripe across the chest -/obj/item/clothing/head/bio_hood/security - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - icon_state = "bio_security" - -/obj/item/clothing/suit/bio_suit/security - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - icon_state = "bio_security" - - -//Janitor's biosuit, grey with purple arms -/obj/item/clothing/head/bio_hood/janitor - icon_state = "bio_janitor" - -/obj/item/clothing/suit/bio_suit/janitor - icon_state = "bio_janitor" - - -//Scientist's biosuit, white with a pink-ish hue -/obj/item/clothing/head/bio_hood/scientist - icon_state = "bio_scientist" - -/obj/item/clothing/suit/bio_suit/scientist - icon_state = "bio_scientist" - - -//CMO's biosuit, blue stripe -/obj/item/clothing/suit/bio_suit/cmo - icon_state = "bio_cmo" - -/obj/item/clothing/head/bio_hood/cmo - icon_state = "bio_cmo" - - -//Plague Dr mask can be found in clothing/masks/gasmask.dm -/obj/item/clothing/suit/bio_suit/plaguedoctorsuit - name = "Plague doctor suit" - desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." - icon_state = "plaguedoctor" - item_state = "bio_suit" - strip_delay = 40 - put_on_delay = 20 - - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - hide_tail_by_species = list("Unathi, Tajaran, Vulpkanin") \ No newline at end of file +//Biosuit complete with shoes (in the item sprite) +/obj/item/clothing/head/bio_hood + name = "bio hood" + icon_state = "bio" + desc = "A hood that protects the head and face from biological comtaminants." + permeability_coefficient = 0.01 + flags = BLOCKHAIR | THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + resistance_flags = ACID_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/head.dmi', + "Tajaran" = 'icons/mob/species/tajaran/head.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/bio_suit + name = "bio suit" + desc = "A suit that protects against biological contamination." + icon_state = "bio" + item_state = "bio_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + flags = THICKMATERIAL + flags_size = ONESIZEFITSALL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 1 + allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + strip_delay = 70 + put_on_delay = 70 + resistance_flags = ACID_PROOF + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + + +//Standard biosuit, orange stripe +/obj/item/clothing/head/bio_hood/general + icon_state = "bio_general" + +/obj/item/clothing/suit/bio_suit/general + icon_state = "bio_general" + + +//Virology biosuit, green stripe +/obj/item/clothing/head/bio_hood/virology + icon_state = "bio_virology" + +/obj/item/clothing/suit/bio_suit/virology + icon_state = "bio_virology" + + +//Security biosuit, grey with red stripe across the chest +/obj/item/clothing/head/bio_hood/security + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + icon_state = "bio_security" + +/obj/item/clothing/suit/bio_suit/security + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + icon_state = "bio_security" + + +//Janitor's biosuit, grey with purple arms +/obj/item/clothing/head/bio_hood/janitor + icon_state = "bio_janitor" + +/obj/item/clothing/suit/bio_suit/janitor + icon_state = "bio_janitor" + + +//Scientist's biosuit, white with a pink-ish hue +/obj/item/clothing/head/bio_hood/scientist + icon_state = "bio_scientist" + +/obj/item/clothing/suit/bio_suit/scientist + icon_state = "bio_scientist" + + +//CMO's biosuit, blue stripe +/obj/item/clothing/suit/bio_suit/cmo + icon_state = "bio_cmo" + +/obj/item/clothing/head/bio_hood/cmo + icon_state = "bio_cmo" + + +//Plague Dr mask can be found in clothing/masks/gasmask.dm +/obj/item/clothing/suit/bio_suit/plaguedoctorsuit + name = "Plague doctor suit" + desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." + icon_state = "plaguedoctor" + item_state = "bio_suit" + strip_delay = 40 + put_on_delay = 20 + + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + hide_tail_by_species = list("Unathi, Tajaran, Vulpkanin") diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index dde11f1fcee5..bcb71edf3561 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -1,385 +1,385 @@ -/* - * Job related - */ -//Paramedic -/obj/item/clothing/suit/storage/paramedic - name = "paramedic vest" - desc = "A hazard vest used in the recovery of bodies." - icon_state = "paramedic-vest" - item_state = "paramedic-vest" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 10, fire = 50, acid = 50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Brig Physician -/obj/item/clothing/suit/storage/brigdoc - name = "brig physician vest" - desc = "A vest often worn by doctors caring for inmates." - icon_state = "brigphysician-vest" - item_state = "brigphysician-vest" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, \ - /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 50, acid = 50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Botanist -/obj/item/clothing/suit/apron - name = "apron" - desc = "A basic blue apron." - icon_state = "apron" - item_state = "apron" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/reagent_containers/spray/plantbgone,/obj/item/plant_analyzer,/obj/item/seeds,/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/cultivator,/obj/item/reagent_containers/spray/pestspray,/obj/item/hatchet,/obj/item/storage/bag/plants) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Captain -/obj/item/clothing/suit/captunic - name = "captain's parade tunic" - desc = "Worn by a Captain to show their class." - icon_state = "captunic" - item_state = "bio_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - flags_size = ONESIZEFITSALL - allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/flash, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/captunic/capjacket - name = "captain's uniform jacket" - desc = "A less formal jacket for everyday captain use." - icon_state = "capjacket" - item_state = "bio_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/mantle/armor/captain - name = "captain's cloak" - desc = "An armor-plated piece of fashion for the ruling elite. Protect your upper half in style." - icon_state = "capmantle" - item_state = "capmantle" - armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - -//Chaplain -/obj/item/clothing/suit/hooded/chaplain_hoodie - name = "chaplain hoodie" - desc = "This suit says to you 'hush'!" - icon_state = "chaplain_hoodie" - item_state = "chaplain_hoodie" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/chaplain_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chaplain -/obj/item/clothing/suit/hooded/nun - name = "nun robe" - desc = "Maximum piety in this star system." - icon_state = "nun" - item_state = "nun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - flags_inv = HIDESHOES|HIDEJUMPSUIT - hoodtype = /obj/item/clothing/head/hooded/nun_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chaplain -/obj/item/clothing/suit/hooded/monk - name = "monk robe" - desc = "Wooden board not included." - icon_state = "monkrobe" - item_state = "monkrobe" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - flags_inv = HIDESHOES|HIDEJUMPSUIT - hoodtype = /obj/item/clothing/head/hooded/monk_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - -/obj/item/clothing/suit/witchhunter - name = "witchhunter garb" - desc = "Dosen't weigh the same a a duck." - icon_state = "witchhunter" - item_state = "witchhunter" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - - //Chef -/obj/item/clothing/suit/toggle/chef - name = "chef's apron" - - -//Chef -/obj/item/clothing/suit/chef - name = "chef's apron" - desc = "An apron used by a high class chef." - icon_state = "chef" - item_state = "chef" - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - allowed = list (/obj/item/kitchen/knife) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chef -/obj/item/clothing/suit/chef/classic - name = "classic chef's apron" - desc = "A basic, dull, white chef's apron." - icon_state = "apronchef" - item_state = "apronchef" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/kitchen/knife) - -//Chief Engineer -/obj/item/clothing/suit/mantle/chief_engineer - name = "chief engineer's mantle" - desc = "A slick, authoritative cloak designed for the Chief Engineer." - icon_state = "cemantle" - item_state = "cemantle" - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) - -//Chief Medical Officer -/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer - name = "chief medical officer's mantle" - desc = "An absorbent, clean cover found on the shoulders of the Chief Medical Officer." - icon_state = "cmomantle" - item_state = "cmomantle" - -//Detective -/obj/item/clothing/suit/storage/det_suit - name = "coat" - desc = "An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business." - icon_state = "detective" - item_state = "det_suit" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/reagent_containers/spray/pepper, /obj/item/flashlight, /obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/detective_scanner, /obj/item/taperecorder) - armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_size = ONESIZEFITSALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Forensics -/obj/item/clothing/suit/storage/det_suit/forensics - name = "jacket" - desc = "A forensics technician jacket." - item_state = "det_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/storage/det_suit/forensics/red - name = "red jacket" - desc = "A red forensics technician jacket." - icon_state = "forensics_red" - -/obj/item/clothing/suit/storage/det_suit/forensics/blue - name = "blue jacket" - desc = "A blue forensics technician jacket." - icon_state = "forensics_blue" - -//Blueshield -/obj/item/clothing/suit/storage/blueshield - name = "blueshield coat" - desc = "NT deluxe ripoff. You finally have your own coat." - icon_state = "blueshieldcoat" - item_state = "blueshieldcoat" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic) - armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_size = ONESIZEFITSALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Engineering -/obj/item/clothing/suit/storage/hazardvest - name = "hazard vest" - desc = "A high-visibility vest used in work zones." - icon_state = "hazard" - item_state = "hazard" - blood_overlay_type = "armor" - allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Lawyer -/obj/item/clothing/suit/storage/lawyer - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/storage/lawyer/blackjacket - name = "black suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_black_open" - item_state = "suitjacket_black_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/lawyer/bluejacket - name = "blue suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_blue_open" - item_state = "suitjacket_blue_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/lawyer/purpjacket - name = "purple suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_purp" - item_state = "suitjacket_purp" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Head of Security -/obj/item/clothing/suit/mantle/armor - name = "armored shawl" - desc = "A reinforced shawl, worn by the Head of Security. Do you dare take up their mantle?" - icon_state = "hosmantle" - item_state = "hosmantle" - allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic, /obj/item/kitchen/knife/combat) - armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|ARMS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - resistance_flags = NONE - -//Head of Personnel -/obj/item/clothing/suit/mantle/armor/head_of_personnel - name = "head of personnel's shawl" - desc = "An armored shawl for the head of personnel. It's remarkably well kept." - icon_state = "hopmantle" - item_state = "hopmantle" - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - -//Internal Affairs -/obj/item/clothing/suit/storage/internalaffairs - name = "\improper Internal Affairs jacket" - desc = "A smooth black jacket." - icon_state = "ia_jacket_open" - item_state = "ia_jacket_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/storage/ntrep - name = "\improper Nanotrasen Representative jacket" - desc = "A fancy black jacket; standard issue to Nanotrasen Representatives." - icon_state = "ntrep" - item_state = "ntrep" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Medical -/obj/item/clothing/suit/storage/fr_jacket - name = "first responder jacket" - desc = "A high-visibility jacket worn by medical first responders." - icon_state = "fr_jacket_open" - item_state = "fr_jacket_open" - blood_overlay_type = "armor" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Mime -/obj/item/clothing/suit/suspenders - name = "suspenders" - desc = "They suspend the illusion of the mime's play." - icon = 'icons/obj/clothing/belts.dmi' - icon_state = "suspenders" - blood_overlay_type = "armor" //it's the less thing that I can put here - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/suspenders/nodrop - flags = NODROP - -// Surgeon -/obj/item/clothing/suit/apron/surgical - name = "surgical apron" - desc = "A sterile blue surgical apron." - icon_state = "surgical" - item_state = "surgical" - allowed = list(/obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor) - -//Research Director -/obj/item/clothing/suit/mantle/labcoat - name = "research director's mantle" - desc = "A tweed mantle, worn by the Research Director. Smells like science." - icon_state = "rdmantle" - item_state = "rdmantle" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/food/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/rad_laser) - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 0, fire = 50, acid = 50) +/* + * Job related + */ +//Paramedic +/obj/item/clothing/suit/storage/paramedic + name = "paramedic vest" + desc = "A hazard vest used in the recovery of bodies." + icon_state = "paramedic-vest" + item_state = "paramedic-vest" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ + /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 10, fire = 50, acid = 50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Brig Physician +/obj/item/clothing/suit/storage/brigdoc + name = "brig physician vest" + desc = "A vest often worn by doctors caring for inmates." + icon_state = "brigphysician-vest" + item_state = "brigphysician-vest" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ + /obj/item/healthanalyzer, /obj/item/flashlight, \ + /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 50, acid = 50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Botanist +/obj/item/clothing/suit/apron + name = "apron" + desc = "A basic blue apron." + icon_state = "apron" + item_state = "apron" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/reagent_containers/spray/plantbgone,/obj/item/plant_analyzer,/obj/item/seeds,/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/cultivator,/obj/item/reagent_containers/spray/pestspray,/obj/item/hatchet,/obj/item/storage/bag/plants) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Captain +/obj/item/clothing/suit/captunic + name = "captain's parade tunic" + desc = "Worn by a Captain to show their class." + icon_state = "captunic" + item_state = "bio_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + flags_size = ONESIZEFITSALL + allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/flash, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/captunic/capjacket + name = "captain's uniform jacket" + desc = "A less formal jacket for everyday captain use." + icon_state = "capjacket" + item_state = "bio_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/mantle/armor/captain + name = "captain's cloak" + desc = "An armor-plated piece of fashion for the ruling elite. Protect your upper half in style." + icon_state = "capmantle" + item_state = "capmantle" + armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + +//Chaplain +/obj/item/clothing/suit/hooded/chaplain_hoodie + name = "chaplain hoodie" + desc = "This suit says to you 'hush'!" + icon_state = "chaplain_hoodie" + item_state = "chaplain_hoodie" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/chaplain_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chaplain +/obj/item/clothing/suit/hooded/nun + name = "nun robe" + desc = "Maximum piety in this star system." + icon_state = "nun" + item_state = "nun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + flags_inv = HIDESHOES|HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hooded/nun_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chaplain +/obj/item/clothing/suit/hooded/monk + name = "monk robe" + desc = "Wooden board not included." + icon_state = "monkrobe" + item_state = "monkrobe" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + flags_inv = HIDESHOES|HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hooded/monk_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + +/obj/item/clothing/suit/witchhunter + name = "witchhunter garb" + desc = "Dosen't weigh the same a a duck." + icon_state = "witchhunter" + item_state = "witchhunter" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + + //Chef +/obj/item/clothing/suit/toggle/chef + name = "chef's apron" + + +//Chef +/obj/item/clothing/suit/chef + name = "chef's apron" + desc = "An apron used by a high class chef." + icon_state = "chef" + item_state = "chef" + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list (/obj/item/kitchen/knife) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chef +/obj/item/clothing/suit/chef/classic + name = "classic chef's apron" + desc = "A basic, dull, white chef's apron." + icon_state = "apronchef" + item_state = "apronchef" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/kitchen/knife) + +//Chief Engineer +/obj/item/clothing/suit/mantle/chief_engineer + name = "chief engineer's mantle" + desc = "A slick, authoritative cloak designed for the Chief Engineer." + icon_state = "cemantle" + item_state = "cemantle" + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) + +//Chief Medical Officer +/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer + name = "chief medical officer's mantle" + desc = "An absorbent, clean cover found on the shoulders of the Chief Medical Officer." + icon_state = "cmomantle" + item_state = "cmomantle" + +//Detective +/obj/item/clothing/suit/storage/det_suit + name = "coat" + desc = "An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business." + icon_state = "detective" + item_state = "det_suit" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/reagent_containers/spray/pepper, /obj/item/flashlight, /obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/detective_scanner, /obj/item/taperecorder) + armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_size = ONESIZEFITSALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Forensics +/obj/item/clothing/suit/storage/det_suit/forensics + name = "jacket" + desc = "A forensics technician jacket." + item_state = "det_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/storage/det_suit/forensics/red + name = "red jacket" + desc = "A red forensics technician jacket." + icon_state = "forensics_red" + +/obj/item/clothing/suit/storage/det_suit/forensics/blue + name = "blue jacket" + desc = "A blue forensics technician jacket." + icon_state = "forensics_blue" + +//Blueshield +/obj/item/clothing/suit/storage/blueshield + name = "blueshield coat" + desc = "NT deluxe ripoff. You finally have your own coat." + icon_state = "blueshieldcoat" + item_state = "blueshieldcoat" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic) + armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_size = ONESIZEFITSALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Engineering +/obj/item/clothing/suit/storage/hazardvest + name = "hazard vest" + desc = "A high-visibility vest used in work zones." + icon_state = "hazard" + item_state = "hazard" + blood_overlay_type = "armor" + allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Lawyer +/obj/item/clothing/suit/storage/lawyer + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/storage/lawyer/blackjacket + name = "black suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_black_open" + item_state = "suitjacket_black_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/lawyer/bluejacket + name = "blue suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_blue_open" + item_state = "suitjacket_blue_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/lawyer/purpjacket + name = "purple suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_purp" + item_state = "suitjacket_purp" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Head of Security +/obj/item/clothing/suit/mantle/armor + name = "armored shawl" + desc = "A reinforced shawl, worn by the Head of Security. Do you dare take up their mantle?" + icon_state = "hosmantle" + item_state = "hosmantle" + allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic, /obj/item/kitchen/knife/combat) + armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|ARMS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + resistance_flags = NONE + +//Head of Personnel +/obj/item/clothing/suit/mantle/armor/head_of_personnel + name = "head of personnel's shawl" + desc = "An armored shawl for the head of personnel. It's remarkably well kept." + icon_state = "hopmantle" + item_state = "hopmantle" + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + +//Internal Affairs +/obj/item/clothing/suit/storage/internalaffairs + name = "\improper Internal Affairs jacket" + desc = "A smooth black jacket." + icon_state = "ia_jacket_open" + item_state = "ia_jacket_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/storage/ntrep + name = "\improper Nanotrasen Representative jacket" + desc = "A fancy black jacket; standard issue to Nanotrasen Representatives." + icon_state = "ntrep" + item_state = "ntrep" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Medical +/obj/item/clothing/suit/storage/fr_jacket + name = "first responder jacket" + desc = "A high-visibility jacket worn by medical first responders." + icon_state = "fr_jacket_open" + item_state = "fr_jacket_open" + blood_overlay_type = "armor" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ + /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Mime +/obj/item/clothing/suit/suspenders + name = "suspenders" + desc = "They suspend the illusion of the mime's play." + icon = 'icons/obj/clothing/belts.dmi' + icon_state = "suspenders" + blood_overlay_type = "armor" //it's the less thing that I can put here + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/suspenders/nodrop + flags = NODROP + +// Surgeon +/obj/item/clothing/suit/apron/surgical + name = "surgical apron" + desc = "A sterile blue surgical apron." + icon_state = "surgical" + item_state = "surgical" + allowed = list(/obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor) + +//Research Director +/obj/item/clothing/suit/mantle/labcoat + name = "research director's mantle" + desc = "A tweed mantle, worn by the Research Director. Smells like science." + icon_state = "rdmantle" + item_state = "rdmantle" + allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/food/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/rad_laser) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 0, fire = 50, acid = 50) diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 36157df8bc83..03df1bd74d8a 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -1,65 +1,65 @@ -/obj/item/clothing/suit/storage/labcoat - name = "labcoat" - desc = "A suit that protects against minor chemical spills." - icon_state = "labcoat_open" - item_state = "labcoat_open" - ignore_suitadjust = 0 - suit_adjusted = 1 - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - allowed = list(/obj/item/analyzer,/obj/item/stack/medical,/obj/item/dnainjector,/obj/item/reagent_containers/dropper,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/hypospray,/obj/item/healthanalyzer,/obj/item/flashlight/pen,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/food/pill,/obj/item/storage/pill_bottle,/obj/item/paper,/obj/item/rad_laser) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50) - species_exception = list(/datum/species/golem) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Grey" = 'icons/mob/species/grey/suit.dmi' - ) - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/labcoat/cmo - name = "chief medical officer's labcoat" - desc = "Bluer than the standard model." - icon_state = "labcoat_cmo_open" - item_state = "labcoat_cmo_open" - -/obj/item/clothing/suit/storage/labcoat/mad - name = "mad scientist's labcoat" - desc = "It makes you look capable of konking someone on the noggin and shooting them into space." - icon_state = "labcoat_green_open" - item_state = "labcoat_green_open" - -/obj/item/clothing/suit/storage/labcoat/genetics - name = "geneticist labcoat" - desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." - icon_state = "labcoat_gen_open" - item_state = "labcoat_gen_open" - -/obj/item/clothing/suit/storage/labcoat/chemist - name = "chemist labcoat" - desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." - icon_state = "labcoat_chem_open" - item_state = "labcoat_chem_open" - -/obj/item/clothing/suit/storage/labcoat/virologist - name = "virologist labcoat" - desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." - icon_state = "labcoat_vir_open" - -/obj/item/clothing/suit/storage/labcoat/science - name = "scientist labcoat" - desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." - icon_state = "labcoat_tox_open" - item_state = "labcoat_tox_open" - -/obj/item/clothing/suit/storage/labcoat/mortician - name = "coroner labcoat" - desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder." - icon_state = "labcoat_mort_open" - item_state = "labcoat_mort_open" - -/obj/item/clothing/suit/storage/labcoat/emt - name = "EMT labcoat" - desc = "A comfortable suit for paramedics. Has dark colours." - icon_state = "labcoat_emt_open" - item_state = "labcoat_emt_open" +/obj/item/clothing/suit/storage/labcoat + name = "labcoat" + desc = "A suit that protects against minor chemical spills." + icon_state = "labcoat_open" + item_state = "labcoat_open" + ignore_suitadjust = 0 + suit_adjusted = 1 + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list(/obj/item/analyzer,/obj/item/stack/medical,/obj/item/dnainjector,/obj/item/reagent_containers/dropper,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/hypospray,/obj/item/healthanalyzer,/obj/item/flashlight/pen,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/food/pill,/obj/item/storage/pill_bottle,/obj/item/paper,/obj/item/rad_laser) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50) + species_exception = list(/datum/species/golem) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Grey" = 'icons/mob/species/grey/suit.dmi' + ) + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/labcoat/cmo + name = "chief medical officer's labcoat" + desc = "Bluer than the standard model." + icon_state = "labcoat_cmo_open" + item_state = "labcoat_cmo_open" + +/obj/item/clothing/suit/storage/labcoat/mad + name = "mad scientist's labcoat" + desc = "It makes you look capable of konking someone on the noggin and shooting them into space." + icon_state = "labcoat_green_open" + item_state = "labcoat_green_open" + +/obj/item/clothing/suit/storage/labcoat/genetics + name = "geneticist labcoat" + desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." + icon_state = "labcoat_gen_open" + item_state = "labcoat_gen_open" + +/obj/item/clothing/suit/storage/labcoat/chemist + name = "chemist labcoat" + desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." + icon_state = "labcoat_chem_open" + item_state = "labcoat_chem_open" + +/obj/item/clothing/suit/storage/labcoat/virologist + name = "virologist labcoat" + desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." + icon_state = "labcoat_vir_open" + +/obj/item/clothing/suit/storage/labcoat/science + name = "scientist labcoat" + desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." + icon_state = "labcoat_tox_open" + item_state = "labcoat_tox_open" + +/obj/item/clothing/suit/storage/labcoat/mortician + name = "coroner labcoat" + desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder." + icon_state = "labcoat_mort_open" + item_state = "labcoat_mort_open" + +/obj/item/clothing/suit/storage/labcoat/emt + name = "EMT labcoat" + desc = "A comfortable suit for paramedics. Has dark colours." + icon_state = "labcoat_emt_open" + item_state = "labcoat_emt_open" diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 576b3e6c552b..c329d8a0033c 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -74,4 +74,4 @@ H.update_inv_wear_suit() playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) else - RemoveHelmet() \ No newline at end of file + RemoveHelmet() diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 87df2c38b337..308380e4c556 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -1,159 +1,159 @@ -/* - * Contains: - * Fire protection - * Bomb protection - * Radiation protection - */ - -/* - * Fire protection - */ - -/obj/item/clothing/suit/fire - name = "emergency firesuit" - desc = "A suit that protects against fire and heat." - icon_state = "fire" - item_state = "fire_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen,/obj/item/extinguisher) - slowdown = 1 - armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 10, "rad" = 20, "fire" = 100, "acid" = 50) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 60 - resistance_flags = FIRE_PROOF - -/obj/item/clothing/suit/fire/firefighter - icon_state = "firesuit" - item_state = "firefighter" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/fire/heavy - name = "firesuit" - desc = "A suit that protects against extreme fire and heat." - //icon_state = "thermal" - item_state = "ro_suit" - w_class = WEIGHT_CLASS_BULKY - slowdown = 1.5 - -/obj/item/clothing/suit/fire/atmos - name = "firesuit" - desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." - icon_state = "atmos_firesuit" - item_state = "firesuit_atmos" - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/* - * Bomb protection - */ -/obj/item/clothing/head/bomb_hood - name = "bomb hood" - desc = "Use in case of bomb." - icon_state = "bombsuit" - flags = BLOCKHAIR | THICKMATERIAL - armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - strip_delay = 70 - put_on_delay = 70 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/bomb_suit - name = "bomb suit" - desc = "A suit designed for safety when handling explosives." - icon_state = "bombsuit" - item_state = "bombsuit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 2 - armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - flags_inv = HIDEJUMPSUIT|HIDETAIL - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - strip_delay = 70 - put_on_delay = 70 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - item_state = "bombsuitsec" - - -/obj/item/clothing/suit/bomb_suit/security - icon_state = "bombsuitsec" - item_state = "bombsuitsec" - allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs) - -/* - * Radiation protection - */ -/obj/item/clothing/head/radiation - name = "Radiation Hood" - icon_state = "rad" - desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" - flags = BLOCKHAIR|THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) - strip_delay = 60 - put_on_delay = 60 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/radiation - name = "Radiation suit" - desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." - icon_state = "rad" - item_state = "rad_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen) - slowdown = 1.5 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) - flags_inv = HIDEJUMPSUIT|HIDETAIL - strip_delay = 60 - put_on_delay = 60 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) +/* + * Contains: + * Fire protection + * Bomb protection + * Radiation protection + */ + +/* + * Fire protection + */ + +/obj/item/clothing/suit/fire + name = "emergency firesuit" + desc = "A suit that protects against fire and heat." + icon_state = "fire" + item_state = "fire_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen,/obj/item/extinguisher) + slowdown = 1 + armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 10, "rad" = 20, "fire" = 100, "acid" = 50) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 60 + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/fire/firefighter + icon_state = "firesuit" + item_state = "firefighter" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/fire/heavy + name = "firesuit" + desc = "A suit that protects against extreme fire and heat." + //icon_state = "thermal" + item_state = "ro_suit" + w_class = WEIGHT_CLASS_BULKY + slowdown = 1.5 + +/obj/item/clothing/suit/fire/atmos + name = "firesuit" + desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." + icon_state = "atmos_firesuit" + item_state = "firesuit_atmos" + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/* + * Bomb protection + */ +/obj/item/clothing/head/bomb_hood + name = "bomb hood" + desc = "Use in case of bomb." + icon_state = "bombsuit" + flags = BLOCKHAIR | THICKMATERIAL + armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 70 + put_on_delay = 70 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/bomb_suit + name = "bomb suit" + desc = "A suit designed for safety when handling explosives." + icon_state = "bombsuit" + item_state = "bombsuit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 2 + armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + flags_inv = HIDEJUMPSUIT|HIDETAIL + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + strip_delay = 70 + put_on_delay = 70 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + item_state = "bombsuitsec" + + +/obj/item/clothing/suit/bomb_suit/security + icon_state = "bombsuitsec" + item_state = "bombsuitsec" + allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs) + +/* + * Radiation protection + */ +/obj/item/clothing/head/radiation + name = "Radiation Hood" + icon_state = "rad" + desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" + flags = BLOCKHAIR|THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) + strip_delay = 60 + put_on_delay = 60 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/radiation + name = "Radiation suit" + desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." + icon_state = "rad" + item_state = "rad_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen) + slowdown = 1.5 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) + flags_inv = HIDEJUMPSUIT|HIDETAIL + strip_delay = 60 + put_on_delay = 60 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 09fab3322fb4..d02c9b401055 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -1,204 +1,204 @@ -/obj/item/clothing/head/wizard - name = "wizard hat" - desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." - icon_state = "wizard" - gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE - permeability_coefficient = 0.01 - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - //Not given any special protective value since the magic robes are full-body protection --NEO - strip_delay = 50 - put_on_delay = 50 - magical = TRUE - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/wizard/red - name = "red wizard hat" - desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." - icon_state = "redwizard" - dog_fashion = /datum/dog_fashion/head/red_wizard - -/obj/item/clothing/head/wizard/black - name = "black wizard hat" - desc = "Strange-looking black hat-wear that most certainly belongs to a real skeleton. Spooky." - icon_state = "blackwizard" - dog_fashion = null - - -/obj/item/clothing/head/wizard/clown - name = "purple wizard hat" - desc = "Strange-looking purple hat-wear that most certainly belongs to a real magic user." - icon_state = "wizhatclown" - item_state = "wizhatclown" // cheating - dog_fashion = null - -/obj/item/clothing/head/wizard/fake - name = "wizard hat" - desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." - icon_state = "wizard-fake" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - magical = FALSE - resistance_flags = FLAMMABLE - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/wizard/marisa - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - dog_fashion = null - -/obj/item/clothing/head/wizard/magus - name = "Magus Helm" - desc = "A mysterious helmet that hums with an unearthly power" - icon_state = "magus" - item_state = "magus" - dog_fashion = null - -/obj/item/clothing/head/wizard/amp - name = "psychic amplifier" - desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." - icon_state = "amp" - dog_fashion = null - -/obj/item/clothing/suit/wizrobe - name = "wizard robe" - desc = "A magnificant, gem-lined robe that seems to radiate power." - icon_state = "wizard" - item_state = "wizrobe" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) - allowed = list(/obj/item/teleportation_scroll) - flags_inv = HIDEJUMPSUIT - strip_delay = 50 - put_on_delay = 50 - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/suit/wizrobe/red - name = "red wizard robe" - desc = "A magnificant, red, gem-lined robe that seems to radiate power." - icon_state = "redwizard" - item_state = "redwizrobe" - -/obj/item/clothing/suit/wizrobe/black - name = "black wizard robe" - desc = "An unnerving black gem-lined robe that reeks of death and decay." - icon_state = "blackwizard" - item_state = "blackwizrobe" - -/obj/item/clothing/suit/wizrobe/clown - name = "Clown Robe" - desc = "A set of armoured robes that seem to radiate a dark power. That, and bad fashion decisions." - icon_state = "wizzclown" - item_state = "wizzclown" - -/obj/item/clothing/suit/wizrobe/marisa - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - item_state = "marisarobe" - -/obj/item/clothing/suit/wizrobe/magusblue - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusblue" - item_state = "magusblue" - -/obj/item/clothing/suit/wizrobe/magusred - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusred" - item_state = "magusred" - -/obj/item/clothing/suit/wizrobe/psypurple - name = "purple robes" - desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." - icon_state = "psyamp" - item_state = "psyamp" - -/obj/item/clothing/suit/wizrobe/fake - name = "wizard robe" - desc = "A rather dull, blue robe meant to mimick real wizard robes." - icon_state = "wizard-fake" - item_state = "wizrobe" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -/obj/item/clothing/head/wizard/marisa/fake - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -/obj/item/clothing/suit/wizrobe/marisa/fake - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - item_state = "marisarobe" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -//Shielded Armour - -/obj/item/clothing/suit/space/hardsuit/shielded/wizard - name = "battlemage armour" - desc = "Not all wizards are afraid of getting up close and personal." - icon_state = "hardsuit-wiz" - item_state = "wiz_hardsuit" - recharge_rate = 0 - current_charges = 15 - recharge_cooldown = INFINITY - shield_state = "shield-red" - shield_on = "shield-red" - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) - slowdown = 0 - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard - name = "battlemage helmet" - desc = "A suitably impressive helmet." - icon_state = "hardsuit0-wiz" - item_state = "wiz_helm" - item_color = "wiz" - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) - actions_types = list() //No inbuilt light - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/attack_self(mob/user) - return - -/obj/item/wizard_armour_charge - name = "battlemage shield charges" - desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.." - icon = 'icons/effects/effects.dmi' - icon_state = "electricity2" - -/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) - . = ..() - if(!istype(W)) - to_chat(user, "The rune can only be used on battlemage armour!") - return - W.current_charges += 8 - to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.") - qdel(src) \ No newline at end of file +/obj/item/clothing/head/wizard + name = "wizard hat" + desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." + icon_state = "wizard" + gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE + permeability_coefficient = 0.01 + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + //Not given any special protective value since the magic robes are full-body protection --NEO + strip_delay = 50 + put_on_delay = 50 + magical = TRUE + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/wizard/red + name = "red wizard hat" + desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." + icon_state = "redwizard" + dog_fashion = /datum/dog_fashion/head/red_wizard + +/obj/item/clothing/head/wizard/black + name = "black wizard hat" + desc = "Strange-looking black hat-wear that most certainly belongs to a real skeleton. Spooky." + icon_state = "blackwizard" + dog_fashion = null + + +/obj/item/clothing/head/wizard/clown + name = "purple wizard hat" + desc = "Strange-looking purple hat-wear that most certainly belongs to a real magic user." + icon_state = "wizhatclown" + item_state = "wizhatclown" // cheating + dog_fashion = null + +/obj/item/clothing/head/wizard/fake + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." + icon_state = "wizard-fake" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + magical = FALSE + resistance_flags = FLAMMABLE + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/wizard/marisa + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + dog_fashion = null + +/obj/item/clothing/head/wizard/magus + name = "Magus Helm" + desc = "A mysterious helmet that hums with an unearthly power" + icon_state = "magus" + item_state = "magus" + dog_fashion = null + +/obj/item/clothing/head/wizard/amp + name = "psychic amplifier" + desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." + icon_state = "amp" + dog_fashion = null + +/obj/item/clothing/suit/wizrobe + name = "wizard robe" + desc = "A magnificant, gem-lined robe that seems to radiate power." + icon_state = "wizard" + item_state = "wizrobe" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/teleportation_scroll) + flags_inv = HIDEJUMPSUIT + strip_delay = 50 + put_on_delay = 50 + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/suit/wizrobe/red + name = "red wizard robe" + desc = "A magnificant, red, gem-lined robe that seems to radiate power." + icon_state = "redwizard" + item_state = "redwizrobe" + +/obj/item/clothing/suit/wizrobe/black + name = "black wizard robe" + desc = "An unnerving black gem-lined robe that reeks of death and decay." + icon_state = "blackwizard" + item_state = "blackwizrobe" + +/obj/item/clothing/suit/wizrobe/clown + name = "Clown Robe" + desc = "A set of armoured robes that seem to radiate a dark power. That, and bad fashion decisions." + icon_state = "wizzclown" + item_state = "wizzclown" + +/obj/item/clothing/suit/wizrobe/marisa + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + item_state = "marisarobe" + +/obj/item/clothing/suit/wizrobe/magusblue + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusblue" + item_state = "magusblue" + +/obj/item/clothing/suit/wizrobe/magusred + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusred" + item_state = "magusred" + +/obj/item/clothing/suit/wizrobe/psypurple + name = "purple robes" + desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." + icon_state = "psyamp" + item_state = "psyamp" + +/obj/item/clothing/suit/wizrobe/fake + name = "wizard robe" + desc = "A rather dull, blue robe meant to mimick real wizard robes." + icon_state = "wizard-fake" + item_state = "wizrobe" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +/obj/item/clothing/head/wizard/marisa/fake + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +/obj/item/clothing/suit/wizrobe/marisa/fake + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + item_state = "marisarobe" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +//Shielded Armour + +/obj/item/clothing/suit/space/hardsuit/shielded/wizard + name = "battlemage armour" + desc = "Not all wizards are afraid of getting up close and personal." + icon_state = "hardsuit-wiz" + item_state = "wiz_hardsuit" + recharge_rate = 0 + current_charges = 15 + recharge_cooldown = INFINITY + shield_state = "shield-red" + shield_on = "shield-red" + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) + slowdown = 0 + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard + name = "battlemage helmet" + desc = "A suitably impressive helmet." + icon_state = "hardsuit0-wiz" + item_state = "wiz_helm" + item_color = "wiz" + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) + actions_types = list() //No inbuilt light + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/attack_self(mob/user) + return + +/obj/item/wizard_armour_charge + name = "battlemage shield charges" + desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.." + icon = 'icons/effects/effects.dmi' + icon_state = "electricity2" + +/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) + . = ..() + if(!istype(W)) + to_chat(user, "The rune can only be used on battlemage armour!") + return + W.current_charges += 8 + to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.") + qdel(src) diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index 3fe4138785aa..6b39191eb136 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -1,207 +1,207 @@ -/obj/item/clothing/under/color - desc = "A standard issue colored jumpsuit. Variety is the spice of life!" - - -/obj/item/clothing/under/color/random/New() - ..() - var/list/excluded = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/color/blackf, /obj/item/clothing/under/color/blue/dodgeball, /obj/item/clothing/under/color/orange/prison, /obj/item/clothing/under/color/red/dodgeball, /obj/item/clothing/under/color/red/jersey, /obj/item/clothing/under/color/blue/jersey) - var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - excluded) - name = initial(C.name) - icon_state = initial(C.icon_state) - item_state = initial(C.item_state) - item_color = initial(C.item_color) - -/obj/item/clothing/under/color/black - name = "black jumpsuit" - icon_state = "black" - item_state = "bl_suit" - item_color = "black" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/color/blackf - name = "feminine black jumpsuit" - desc = "It's very smart and in a ladies-size!" - icon_state = "black" - item_state = "bl_suit" - item_color = "blackf" - -/obj/item/clothing/under/color/blue - name = "blue jumpsuit" - icon_state = "blue" - item_state = "b_suit" - item_color = "blue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/blue/dodgeball - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/green - name = "green jumpsuit" - icon_state = "green" - item_state = "g_suit" - item_color = "green" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey - name = "grey jumpsuit" - desc = "A tasteful grey jumpsuit that reminds you of the good old days." - icon_state = "grey" - item_state = "gy_suit" - item_color = "grey" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey/greytide - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey/glorf - name = "ancient jumpsuit" - desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." - -/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - owner.forcesay(GLOB.hit_appends) - return 0 - -/obj/item/clothing/under/color/orange - name = "orange jumpsuit" - desc = "Don't wear this near paranoid security officers" - icon_state = "orange" - item_state = "o_suit" - item_color = "orange" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/orange/prison - name = "orange jumpsuit" - desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." - icon_state = "orange" - item_state = "o_suit" - item_color = "orange" - has_sensor = 2 - sensor_mode = 3 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/pink - name = "pink jumpsuit" - desc = "Just looking at this makes you feel fabulous." - icon_state = "pink" - item_state = "p_suit" - item_color = "pink" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red - name = "red jumpsuit" - icon_state = "red" - item_state = "r_suit" - item_color = "red" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red/dodgeball - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/white - name = "white jumpsuit" - icon_state = "white" - item_state = "w_suit" - item_color = "white" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/yellow - name = "yellow jumpsuit" - icon_state = "yellow" - item_state = "y_suit" - item_color = "yellow" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/psyche - name = "psychedelic jumpsuit" - desc = "Groovy!" - icon_state = "psyche" - item_color = "psyche" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi' - ) - -/obj/item/clothing/under/color/lightblue - name = "light blue jumpsuit" - icon_state = "lightblue" - item_color = "lightblue" - -/obj/item/clothing/under/color/aqua - name = "aqua jumpsuit" - icon_state = "aqua" - item_color = "aqua" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/purple - name = "purple jumpsuit" - icon_state = "purple" - item_state = "p_suit" - item_color = "purple" - -/obj/item/clothing/under/color/lightpurple - name = "light purple jumpsuit" - icon_state = "lightpurple" - item_color = "lightpurple" - -/obj/item/clothing/under/color/lightgreen - name = "light green jumpsuit" - icon_state = "lightgreen" - item_color = "lightgreen" - -/obj/item/clothing/under/color/lightblue - name = "light blue jumpsuit" - icon_state = "lightblue" - item_color = "lightblue" - -/obj/item/clothing/under/color/lightbrown - name = "light brown jumpsuit" - icon_state = "lightbrown" - item_color = "lightbrown" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/brown - name = "brown jumpsuit" - icon_state = "brown" - item_color = "brown" - -/obj/item/clothing/under/color/yellowgreen - name = "yellow green jumpsuit" - icon_state = "yellowgreen" - item_color = "yellowgreen" - -/obj/item/clothing/under/color/darkblue - name = "dark blue jumpsuit" - icon_state = "darkblue" - item_color = "darkblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/lightred - name = "light red jumpsuit" - icon_state = "lightred" - item_color = "lightred" - -/obj/item/clothing/under/color/darkred - name = "dark red jumpsuit" - icon_state = "darkred" - item_color = "darkred" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red/jersey - name = "red team jersey" - desc = "The jersey of the Nanotrasen Phi-ghters!" - icon_state = "redjersey" - item_state = "r_suit" - item_color = "redjersey" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/blue/jersey - name = "blue team jersey" - desc = "The jersey of the Nanotrasen Pi-rates!" - icon_state = "bluejersey" - item_state = "b_suit" - item_color = "bluejersey" - flags_size = ONESIZEFITSALL +/obj/item/clothing/under/color + desc = "A standard issue colored jumpsuit. Variety is the spice of life!" + + +/obj/item/clothing/under/color/random/New() + ..() + var/list/excluded = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/color/blackf, /obj/item/clothing/under/color/blue/dodgeball, /obj/item/clothing/under/color/orange/prison, /obj/item/clothing/under/color/red/dodgeball, /obj/item/clothing/under/color/red/jersey, /obj/item/clothing/under/color/blue/jersey) + var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - excluded) + name = initial(C.name) + icon_state = initial(C.icon_state) + item_state = initial(C.item_state) + item_color = initial(C.item_color) + +/obj/item/clothing/under/color/black + name = "black jumpsuit" + icon_state = "black" + item_state = "bl_suit" + item_color = "black" + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/color/blackf + name = "feminine black jumpsuit" + desc = "It's very smart and in a ladies-size!" + icon_state = "black" + item_state = "bl_suit" + item_color = "blackf" + +/obj/item/clothing/under/color/blue + name = "blue jumpsuit" + icon_state = "blue" + item_state = "b_suit" + item_color = "blue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/blue/dodgeball + flags = NODROP + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/green + name = "green jumpsuit" + icon_state = "green" + item_state = "g_suit" + item_color = "green" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/grey + name = "grey jumpsuit" + desc = "A tasteful grey jumpsuit that reminds you of the good old days." + icon_state = "grey" + item_state = "gy_suit" + item_color = "grey" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/grey/greytide + flags = NODROP + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/grey/glorf + name = "ancient jumpsuit" + desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." + +/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + owner.forcesay(GLOB.hit_appends) + return 0 + +/obj/item/clothing/under/color/orange + name = "orange jumpsuit" + desc = "Don't wear this near paranoid security officers" + icon_state = "orange" + item_state = "o_suit" + item_color = "orange" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/orange/prison + name = "orange jumpsuit" + desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." + icon_state = "orange" + item_state = "o_suit" + item_color = "orange" + has_sensor = 2 + sensor_mode = 3 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/pink + name = "pink jumpsuit" + desc = "Just looking at this makes you feel fabulous." + icon_state = "pink" + item_state = "p_suit" + item_color = "pink" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/red + name = "red jumpsuit" + icon_state = "red" + item_state = "r_suit" + item_color = "red" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/red/dodgeball + flags = NODROP + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/white + name = "white jumpsuit" + icon_state = "white" + item_state = "w_suit" + item_color = "white" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/yellow + name = "yellow jumpsuit" + icon_state = "yellow" + item_state = "y_suit" + item_color = "yellow" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/psyche + name = "psychedelic jumpsuit" + desc = "Groovy!" + icon_state = "psyche" + item_color = "psyche" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi' + ) + +/obj/item/clothing/under/color/lightblue + name = "light blue jumpsuit" + icon_state = "lightblue" + item_color = "lightblue" + +/obj/item/clothing/under/color/aqua + name = "aqua jumpsuit" + icon_state = "aqua" + item_color = "aqua" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/purple + name = "purple jumpsuit" + icon_state = "purple" + item_state = "p_suit" + item_color = "purple" + +/obj/item/clothing/under/color/lightpurple + name = "light purple jumpsuit" + icon_state = "lightpurple" + item_color = "lightpurple" + +/obj/item/clothing/under/color/lightgreen + name = "light green jumpsuit" + icon_state = "lightgreen" + item_color = "lightgreen" + +/obj/item/clothing/under/color/lightblue + name = "light blue jumpsuit" + icon_state = "lightblue" + item_color = "lightblue" + +/obj/item/clothing/under/color/lightbrown + name = "light brown jumpsuit" + icon_state = "lightbrown" + item_color = "lightbrown" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/brown + name = "brown jumpsuit" + icon_state = "brown" + item_color = "brown" + +/obj/item/clothing/under/color/yellowgreen + name = "yellow green jumpsuit" + icon_state = "yellowgreen" + item_color = "yellowgreen" + +/obj/item/clothing/under/color/darkblue + name = "dark blue jumpsuit" + icon_state = "darkblue" + item_color = "darkblue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/lightred + name = "light red jumpsuit" + icon_state = "lightred" + item_color = "lightred" + +/obj/item/clothing/under/color/darkred + name = "dark red jumpsuit" + icon_state = "darkred" + item_color = "darkred" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/red/jersey + name = "red team jersey" + desc = "The jersey of the Nanotrasen Phi-ghters!" + icon_state = "redjersey" + item_state = "r_suit" + item_color = "redjersey" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/blue/jersey + name = "blue team jersey" + desc = "The jersey of the Nanotrasen Pi-rates!" + icon_state = "bluejersey" + item_state = "b_suit" + item_color = "bluejersey" + flags_size = ONESIZEFITSALL diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index 879e0fd22c72..cb429b79fbed 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -1,239 +1,239 @@ -//Alphabetical order of support jobs. - -/obj/item/clothing/under/rank/bartender - desc = "It looks like it could use some more flair." - name = "bartender's uniform" - icon_state = "ba_suit" - item_state = "ba_suit" - item_color = "ba_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. - desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"." - name = "captain's jumpsuit" - icon_state = "captain" - item_state = "caparmor" - item_color = "captain" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargo - name = "quartermaster's jumpsuit" - desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qm" - item_state = "lb_suit" - item_color = "qm" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargo/skirt - name = "quartermaster's jumpskirt" - desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qmf" - item_color = "qmf" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/cargotech - name = "cargo technician's jumpsuit" - desc = "Shooooorts! They're comfy and easy to wear!" - icon_state = "cargotech" - item_state = "lb_suit" - item_color = "cargo" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargotech/skirt - name = "cargo technician's jumpskirt" - desc = "Skirrrrrts! They're comfy and easy to wear!" - icon_state = "cargof" - item_color = "cargof" - flags_size = null - -/obj/item/clothing/under/rank/chaplain - desc = "It's a black jumpsuit, often worn by religious folk." - name = "chaplain's jumpsuit" - icon_state = "chaplain" - item_state = "bl_suit" - item_color = "chapblack" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chef - desc = "It's an apron which is given only to the most hardcore chefs in space." - name = "chef's uniform" - icon_state = "chef" - item_color = "chef" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/clown - name = "clown suit" - desc = "'HONK!'" - icon_state = "clown" - item_state = "clown" - item_color = "clown" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/clown/Initialize() - . = ..() - AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) - -/obj/item/clothing/under/rank/clown/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.mind && H.mind.assigned_role == "Clown") - score_clownabuse++ - return ..() - -/obj/item/clothing/under/rank/clown/sexy - name = "sexy-clown suit" - desc = "It makes you look HONKable!" - icon_state = "sexyclown" - item_state = "sexyclown" - item_color = "sexyclown" - -/obj/item/clothing/under/rank/clown/nodrop - flags = NODROP - -/obj/item/clothing/under/rank/head_of_personnel - desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." - name = "head of personnel's jumpsuit" - icon_state = "hop" - item_state = "b_suit" - item_color = "hop" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/head_of_personnel_whimsy - desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." - name = "head of personnel's suit" - icon_state = "hopwhimsy" - item_state = "hopwhimsy" - item_color = "hopwhimsy" - - -/obj/item/clothing/under/rank/hydroponics - desc = "It's a jumpsuit designed to protect against minor plant-related hazards." - name = "botanist's jumpsuit" - icon_state = "hydroponics" - item_state = "g_suit" - item_color = "hydroponics" - permeability_coefficient = 0.50 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/internalaffairs - desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." - name = "Internal Affairs uniform" - icon_state = "internalaffairs" - item_state = "internalaffairs" - item_color = "internalaffairs" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/ntrep - desc = "A well-ironed dress shirt and matching set of black pants." - name = "dress shirt" - icon_state = "internalaffairs" - item_state = "internalaffairs" - item_color = "internalaffairs" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/ntrep/skirt - desc = "A silky smooth black and gold representative uniform with blue markings." - name = "representative skirt" - icon_state = "ntrepf" - item_state = "ntrepf" - item_color = "ntrepf" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/janitor - desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." - name = "janitor's jumpsuit" - icon_state = "janitor" - item_color = "janitor" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - - -/obj/item/clothing/under/lawyer - desc = "Slick threads." - name = "Lawyer suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/lawyer/black - icon_state = "lawyer_black" - item_state = "lawyer_black" - item_color = "lawyer_black" - -/obj/item/clothing/under/lawyer/female - icon_state = "black_suit_fem" - item_state = "black_suit_fem" - item_color = "black_suit_fem" - -/obj/item/clothing/under/lawyer/red - icon_state = "lawyer_red" - item_state = "lawyer_red" - item_color = "lawyer_red" - -/obj/item/clothing/under/lawyer/blue - icon_state = "lawyer_blue" - item_state = "lawyer_blue" - item_color = "lawyer_blue" - -/obj/item/clothing/under/lawyer/bluesuit - name = "Blue Suit" - desc = "A classy suit and tie" - icon_state = "bluesuit" - item_state = "bluesuit" - item_color = "bluesuit" - -/obj/item/clothing/under/lawyer/purpsuit - name = "Purple Suit" - icon_state = "lawyer_purp" - item_state = "lawyer_purp" - item_color = "lawyer_purp" - -/obj/item/clothing/under/lawyer/oldman - name = "Old Man's Suit" - desc = "A classic suit for the older gentleman with built in back support." - icon_state = "oldman" - item_state = "oldman" - item_color = "oldman" - - -/obj/item/clothing/under/librarian - name = "sensible suit" - desc = "It's very... sensible." - icon_state = "red_suit" - item_state = "red_suit" - item_color = "red_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/mime - name = "mime's outfit" - desc = "It's not very colourful." - icon_state = "mime" - item_state = "mime" - item_color = "mime" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/mime/nodrop - flags = NODROP - -/obj/item/clothing/under/rank/miner - desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." - name = "shaft miner's jumpsuit" - icon_state = "miner" - item_state = "miner" - item_color = "miner" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/miner/lavaland - desc = "A green uniform for operating in hazardous environments." - name = "shaft miner's jumpsuit" - icon_state = "explorer" - item_state = "explorer" - item_color = "explorer" - - sprite_sheets = list() - -/obj/item/clothing/under/barber - desc = "It's a barber's uniform." - name = "barber's uniform" - icon_state = "barber" - item_state = "barber" - item_color = "barber" +//Alphabetical order of support jobs. + +/obj/item/clothing/under/rank/bartender + desc = "It looks like it could use some more flair." + name = "bartender's uniform" + icon_state = "ba_suit" + item_state = "ba_suit" + item_color = "ba_suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. + desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"." + name = "captain's jumpsuit" + icon_state = "captain" + item_state = "caparmor" + item_color = "captain" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/cargo + name = "quartermaster's jumpsuit" + desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qm" + item_state = "lb_suit" + item_color = "qm" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/cargo/skirt + name = "quartermaster's jumpskirt" + desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qmf" + item_color = "qmf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/cargotech + name = "cargo technician's jumpsuit" + desc = "Shooooorts! They're comfy and easy to wear!" + icon_state = "cargotech" + item_state = "lb_suit" + item_color = "cargo" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/cargotech/skirt + name = "cargo technician's jumpskirt" + desc = "Skirrrrrts! They're comfy and easy to wear!" + icon_state = "cargof" + item_color = "cargof" + flags_size = null + +/obj/item/clothing/under/rank/chaplain + desc = "It's a black jumpsuit, often worn by religious folk." + name = "chaplain's jumpsuit" + icon_state = "chaplain" + item_state = "bl_suit" + item_color = "chapblack" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chef + desc = "It's an apron which is given only to the most hardcore chefs in space." + name = "chef's uniform" + icon_state = "chef" + item_color = "chef" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/clown + name = "clown suit" + desc = "'HONK!'" + icon_state = "clown" + item_state = "clown" + item_color = "clown" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/clown/Initialize() + . = ..() + AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) + +/obj/item/clothing/under/rank/clown/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.mind && H.mind.assigned_role == "Clown") + GLOB.score_clownabuse++ + return ..() + +/obj/item/clothing/under/rank/clown/sexy + name = "sexy-clown suit" + desc = "It makes you look HONKable!" + icon_state = "sexyclown" + item_state = "sexyclown" + item_color = "sexyclown" + +/obj/item/clothing/under/rank/clown/nodrop + flags = NODROP + +/obj/item/clothing/under/rank/head_of_personnel + desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." + name = "head of personnel's jumpsuit" + icon_state = "hop" + item_state = "b_suit" + item_color = "hop" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/head_of_personnel_whimsy + desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." + name = "head of personnel's suit" + icon_state = "hopwhimsy" + item_state = "hopwhimsy" + item_color = "hopwhimsy" + + +/obj/item/clothing/under/rank/hydroponics + desc = "It's a jumpsuit designed to protect against minor plant-related hazards." + name = "botanist's jumpsuit" + icon_state = "hydroponics" + item_state = "g_suit" + item_color = "hydroponics" + permeability_coefficient = 0.50 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/internalaffairs + desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." + name = "Internal Affairs uniform" + icon_state = "internalaffairs" + item_state = "internalaffairs" + item_color = "internalaffairs" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/ntrep + desc = "A well-ironed dress shirt and matching set of black pants." + name = "dress shirt" + icon_state = "internalaffairs" + item_state = "internalaffairs" + item_color = "internalaffairs" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/ntrep/skirt + desc = "A silky smooth black and gold representative uniform with blue markings." + name = "representative skirt" + icon_state = "ntrepf" + item_state = "ntrepf" + item_color = "ntrepf" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/janitor + desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." + name = "janitor's jumpsuit" + icon_state = "janitor" + item_color = "janitor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + + +/obj/item/clothing/under/lawyer + desc = "Slick threads." + name = "Lawyer suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/lawyer/black + icon_state = "lawyer_black" + item_state = "lawyer_black" + item_color = "lawyer_black" + +/obj/item/clothing/under/lawyer/female + icon_state = "black_suit_fem" + item_state = "black_suit_fem" + item_color = "black_suit_fem" + +/obj/item/clothing/under/lawyer/red + icon_state = "lawyer_red" + item_state = "lawyer_red" + item_color = "lawyer_red" + +/obj/item/clothing/under/lawyer/blue + icon_state = "lawyer_blue" + item_state = "lawyer_blue" + item_color = "lawyer_blue" + +/obj/item/clothing/under/lawyer/bluesuit + name = "Blue Suit" + desc = "A classy suit and tie" + icon_state = "bluesuit" + item_state = "bluesuit" + item_color = "bluesuit" + +/obj/item/clothing/under/lawyer/purpsuit + name = "Purple Suit" + icon_state = "lawyer_purp" + item_state = "lawyer_purp" + item_color = "lawyer_purp" + +/obj/item/clothing/under/lawyer/oldman + name = "Old Man's Suit" + desc = "A classic suit for the older gentleman with built in back support." + icon_state = "oldman" + item_state = "oldman" + item_color = "oldman" + + +/obj/item/clothing/under/librarian + name = "sensible suit" + desc = "It's very... sensible." + icon_state = "red_suit" + item_state = "red_suit" + item_color = "red_suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/mime + name = "mime's outfit" + desc = "It's not very colourful." + icon_state = "mime" + item_state = "mime" + item_color = "mime" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/mime/nodrop + flags = NODROP + +/obj/item/clothing/under/rank/miner + desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." + name = "shaft miner's jumpsuit" + icon_state = "miner" + item_state = "miner" + item_color = "miner" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/miner/lavaland + desc = "A green uniform for operating in hazardous environments." + name = "shaft miner's jumpsuit" + icon_state = "explorer" + item_state = "explorer" + item_color = "explorer" + + sprite_sheets = list() + +/obj/item/clothing/under/barber + desc = "It's a barber's uniform." + name = "barber's uniform" + icon_state = "barber" + item_state = "barber" + item_color = "barber" diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index 14878e402a1f..bdda81ad03da 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -1,77 +1,77 @@ -//Contains: Engineering department jumpsuits -/obj/item/clothing/under/rank/chief_engineer - desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpsuit" - icon_state = "chiefengineer" - item_state = "chief" - item_color = "chief" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 80, "acid" = 40) - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/chief_engineer/skirt - desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpskirt" - icon_state = "chieff" - item_color = "chieff" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/atmospheric_technician - desc = "It's a jumpsuit worn by atmospheric technicians." - name = "atmospheric technician's jumpsuit" - icon_state = "atmos" - item_state = "atmos_suit" - item_color = "atmos" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/atmospheric_technician/skirt - desc = "It's a jumpskirt worn by atmospheric technicians." - name = "atmospheric technician's jumpskirt" - icon_state = "atmosf" - item_color = "atmosf" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/engineer - desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." - name = "engineer's jumpsuit" - icon_state = "engine" - item_state = "engi_suit" - item_color = "engine" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) - flags_size = ONESIZEFITSALL - resistance_flags = NONE - - -/obj/item/clothing/under/rank/engineer/skirt - desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." - name = "engineer's jumpskirt" - icon_state = "enginef" - item_color = "enginef" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/roboticist - desc = "It's a slimming black with reinforced seams; great for industrial work." - name = "roboticist's jumpsuit" - icon_state = "robotics" - item_state = "robotics" - item_color = "robotics" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/roboticist/skirt - desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." - name = "roboticist's jumpskirt" - icon_state = "roboticsf" - item_color = "roboticsf" - flags_size = null - -/obj/item/clothing/under/rank/mechanic - desc = "It's a pair of overalls worn by mechanics." - name = "mechanic's overalls" - icon_state = "mechanic" - item_state = "mechanic" - item_color = "mechanic" +//Contains: Engineering department jumpsuits +/obj/item/clothing/under/rank/chief_engineer + desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpsuit" + icon_state = "chiefengineer" + item_state = "chief" + item_color = "chief" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 80, "acid" = 40) + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/rank/chief_engineer/skirt + desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpskirt" + icon_state = "chieff" + item_color = "chieff" + flags_size = null + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/atmospheric_technician + desc = "It's a jumpsuit worn by atmospheric technicians." + name = "atmospheric technician's jumpsuit" + icon_state = "atmos" + item_state = "atmos_suit" + item_color = "atmos" + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/rank/atmospheric_technician/skirt + desc = "It's a jumpskirt worn by atmospheric technicians." + name = "atmospheric technician's jumpskirt" + icon_state = "atmosf" + item_color = "atmosf" + flags_size = null + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/engineer + desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." + name = "engineer's jumpsuit" + icon_state = "engine" + item_state = "engi_suit" + item_color = "engine" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) + flags_size = ONESIZEFITSALL + resistance_flags = NONE + + +/obj/item/clothing/under/rank/engineer/skirt + desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." + name = "engineer's jumpskirt" + icon_state = "enginef" + item_color = "enginef" + flags_size = null + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/roboticist + desc = "It's a slimming black with reinforced seams; great for industrial work." + name = "roboticist's jumpsuit" + icon_state = "robotics" + item_state = "robotics" + item_color = "robotics" + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/rank/roboticist/skirt + desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." + name = "roboticist's jumpskirt" + icon_state = "roboticsf" + item_color = "roboticsf" + flags_size = null + +/obj/item/clothing/under/rank/mechanic + desc = "It's a pair of overalls worn by mechanics." + name = "mechanic's overalls" + icon_state = "mechanic" + item_state = "mechanic" + item_color = "mechanic" diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index f5d65b964fc6..d3fcad16680e 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -1,246 +1,246 @@ -/* - * Science - */ -/obj/item/clothing/under/rank/research_director - desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." - name = "research director's jumpsuit" - icon_state = "director" - item_state = "g_suit" - item_color = "director" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 35) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist - desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." - name = "scientist's jumpsuit" - icon_state = "toxins" - item_state = "w_suit" - item_color = "toxinswhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist/skirt - name = "scientist's jumpskirt" - icon_state = "sciencewhitef" - item_color = "sciencewhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/chemist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." - name = "chemist's jumpsuit" - icon_state = "chemistry" - item_state = "w_suit" - item_color = "chemistrywhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chemist/skirt - name = "chemist's jumpskirt" - icon_state = "chemistrywhitef" - item_color = "chemistrywhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/* - * Medical - */ -/obj/item/clothing/under/rank/chief_medical_officer - desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpsuit" - icon_state = "cmo" - item_state = "w_suit" - item_color = "cmo" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chief_medical_officer/skirt - desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpskirt" - icon_state = "cmof" - item_color = "cmof" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/geneticist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." - name = "geneticist's jumpsuit" - icon_state = "genetics" - item_state = "w_suit" - item_color = "geneticswhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/geneticist/skirt - name = "geneticist's jumpskirt" - icon_state = "geneticswhitef" - item_color = "geneticswhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/virologist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." - name = "virologist's jumpsuit" - icon_state = "virology" - item_state = "w_suit" - item_color = "virologywhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/virologist/skirt - name = "virologist's jumpskirt" - icon_state = "virologywhitef" - item_color = "virologywhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/nursesuit - desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." - name = "nurse's suit" - icon_state = "nursesuit" - item_state = "nursesuit" - item_color = "nursesuit" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/nurse - desc = "A dress commonly worn by the nursing staff in the medical department." - name = "nurse's dress" - icon_state = "nurse" - item_state = "nurse" - item_color = "nurse" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/orderly - desc = "A white suit to be worn by orderly people who love orderly things." - name = "orderly's uniform" - icon_state = "orderly" - item_state = "orderly" - item_color = "orderly" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical - desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "medical doctor's jumpsuit" - icon_state = "medical" - item_state = "w_suit" - item_color = "medical" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/skirt - name = "medical doctor's jumpskirt" - icon_state = "medicalf" - item_color = "medicalf" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/medical/blue - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." - icon_state = "scrubsblue" - item_color = "scrubsblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/green - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." - icon_state = "scrubsgreen" - item_color = "scrubsgreen" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/purple - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." - icon_state = "scrubspurple" - item_color = "scrubspurple" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/mortician - name = "coroner's scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." - icon_state = "scrubsblack" - item_color = "scrubsblack" - flags_size = ONESIZEFITSALL - -//paramedic -/obj/item/clothing/under/rank/medical/paramedic - desc = "It's made of a special fiber that provides minor protection against biohazards and radiation. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "paramedic's jumpsuit" - icon_state = "paramedic" - item_state = "paramedic" - item_color = "paramedic" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 10, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/psych - desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." - name = "psychiatrist's jumpsuit" - icon_state = "psych" - item_state = "w_suit" - item_color = "psych" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/psych/turtleneck - desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." - name = "psychologist's turtleneck" - icon_state = "psychturtle" - item_state = "b_suit" - item_color = "psychturtle" - flags_size = ONESIZEFITSALL - - -/* - * Medsci, unused (i think) stuff - */ -/obj/item/clothing/under/rank/geneticist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "geneticist's jumpsuit" - icon_state = "genetics_new" - item_state = "w_suit" - item_color = "genetics_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chemist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "chemist's jumpsuit" - icon_state = "chemist_new" - item_state = "w_suit" - item_color = "chemist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist_new - desc = "Made of a special fiber that gives special protection against biohazards and small explosions." - name = "scientist's jumpsuit" - icon_state = "scientist_new" - item_state = "w_suit" - item_color = "scientist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/virologist_new - desc = "Made of a special fiber that gives increased protection against biohazards." - name = "virologist's jumpsuit" - icon_state = "virologist_new" - item_state = "w_suit" - item_color = "virologist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL +/* + * Science + */ +/obj/item/clothing/under/rank/research_director + desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." + name = "research director's jumpsuit" + icon_state = "director" + item_state = "g_suit" + item_color = "director" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 35) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/scientist + desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." + name = "scientist's jumpsuit" + icon_state = "toxins" + item_state = "w_suit" + item_color = "toxinswhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/scientist/skirt + name = "scientist's jumpskirt" + icon_state = "sciencewhitef" + item_color = "sciencewhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/chemist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + name = "chemist's jumpsuit" + icon_state = "chemistry" + item_state = "w_suit" + item_color = "chemistrywhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chemist/skirt + name = "chemist's jumpskirt" + icon_state = "chemistrywhitef" + item_color = "chemistrywhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/* + * Medical + */ +/obj/item/clothing/under/rank/chief_medical_officer + desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpsuit" + icon_state = "cmo" + item_state = "w_suit" + item_color = "cmo" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chief_medical_officer/skirt + desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpskirt" + icon_state = "cmof" + item_color = "cmof" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/geneticist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." + name = "geneticist's jumpsuit" + icon_state = "genetics" + item_state = "w_suit" + item_color = "geneticswhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/geneticist/skirt + name = "geneticist's jumpskirt" + icon_state = "geneticswhitef" + item_color = "geneticswhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/virologist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." + name = "virologist's jumpsuit" + icon_state = "virology" + item_state = "w_suit" + item_color = "virologywhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/virologist/skirt + name = "virologist's jumpskirt" + icon_state = "virologywhitef" + item_color = "virologywhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/nursesuit + desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." + name = "nurse's suit" + icon_state = "nursesuit" + item_state = "nursesuit" + item_color = "nursesuit" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/nurse + desc = "A dress commonly worn by the nursing staff in the medical department." + name = "nurse's dress" + icon_state = "nurse" + item_state = "nurse" + item_color = "nurse" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/orderly + desc = "A white suit to be worn by orderly people who love orderly things." + name = "orderly's uniform" + icon_state = "orderly" + item_state = "orderly" + item_color = "orderly" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical + desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "medical doctor's jumpsuit" + icon_state = "medical" + item_state = "w_suit" + item_color = "medical" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/skirt + name = "medical doctor's jumpskirt" + icon_state = "medicalf" + item_color = "medicalf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/medical/blue + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." + icon_state = "scrubsblue" + item_color = "scrubsblue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/green + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." + icon_state = "scrubsgreen" + item_color = "scrubsgreen" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/purple + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." + icon_state = "scrubspurple" + item_color = "scrubspurple" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/mortician + name = "coroner's scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." + icon_state = "scrubsblack" + item_color = "scrubsblack" + flags_size = ONESIZEFITSALL + +//paramedic +/obj/item/clothing/under/rank/medical/paramedic + desc = "It's made of a special fiber that provides minor protection against biohazards and radiation. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "paramedic's jumpsuit" + icon_state = "paramedic" + item_state = "paramedic" + item_color = "paramedic" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 10, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/psych + desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." + name = "psychiatrist's jumpsuit" + icon_state = "psych" + item_state = "w_suit" + item_color = "psych" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/psych/turtleneck + desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." + name = "psychologist's turtleneck" + icon_state = "psychturtle" + item_state = "b_suit" + item_color = "psychturtle" + flags_size = ONESIZEFITSALL + + +/* + * Medsci, unused (i think) stuff + */ +/obj/item/clothing/under/rank/geneticist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "geneticist's jumpsuit" + icon_state = "genetics_new" + item_state = "w_suit" + item_color = "genetics_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chemist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "chemist's jumpsuit" + icon_state = "chemist_new" + item_state = "w_suit" + item_color = "chemist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/scientist_new + desc = "Made of a special fiber that gives special protection against biohazards and small explosions." + name = "scientist's jumpsuit" + icon_state = "scientist_new" + item_state = "w_suit" + item_color = "scientist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/virologist_new + desc = "Made of a special fiber that gives increased protection against biohazards." + name = "virologist's jumpsuit" + icon_state = "virologist_new" + item_state = "w_suit" + item_color = "virologist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL diff --git a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm index 937d51118300..b3d7ce83431a 100644 --- a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm +++ b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm @@ -52,4 +52,4 @@ name = "envirosuit extinguisher cartridge" desc = "A cartridge loaded with a compressed extinguisher mix, used to refill the automatic extinguisher on plasma envirosuits." icon_state = "plasmarefill" - icon = 'icons/obj/device.dmi' \ No newline at end of file + icon = 'icons/obj/device.dmi' diff --git a/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm b/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm index 69697d3be6ff..0ef8df4d57b5 100644 --- a/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm +++ b/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm @@ -104,4 +104,4 @@ desc = "An envirosuit designed for plasmamen employed as the blueshield." icon_state = "bs_envirosuit" item_state = "bs_envirosuit" - item_color = "bs_envirosuit" \ No newline at end of file + item_color = "bs_envirosuit" diff --git a/code/modules/clothing/under/jobs/plasmamen/security.dm b/code/modules/clothing/under/jobs/plasmamen/security.dm index f39feb6345e2..7060037bd902 100644 --- a/code/modules/clothing/under/jobs/plasmamen/security.dm +++ b/code/modules/clothing/under/jobs/plasmamen/security.dm @@ -18,4 +18,4 @@ desc = "A plasmaman containment suit designed for the head of security." icon_state = "hos_envirosuit" item_state = "hos_envirosuit" - item_color = "hos_envirosuit" \ No newline at end of file + item_color = "hos_envirosuit" diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index fa48aff0f4f4..2602c46d01dc 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -1,200 +1,200 @@ -/* - * Contains: - * Security - * Detective - * Head of Security - */ - - -/* - * Security - */ -/obj/item/clothing/under/rank/warden - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpsuit" - icon_state = "warden" - item_state = "r_suit" - item_color = "warden" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - -/obj/item/clothing/under/rank/warden/skirt - desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpskirt" - icon_state = "wardenf" - item_state = "r_suit" - item_color = "wardenf" - flags_size = null - -/obj/item/clothing/under/rank/security - name = "security officer's jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "security" - item_state = "r_suit" - item_color = "secred" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - -/obj/item/clothing/under/rank/security/skirt - name = "security officer's jumpskirt" - desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." - icon_state = "secredf" - item_state = "r_suit" - item_color = "secredf" - flags_size = null - -/obj/item/clothing/under/rank/dispatch - name = "dispatcher's uniform" - desc = "A dress shirt and khakis with a security patch sewn on." - icon_state = "dispatch" - item_state = "dispatch" - item_color = "dispatch" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security2 - name = "security officer's uniform" - desc = "It's made of a slightly sturdier material, to allow for robust protection." - icon_state = "redshirt2" - item_state = "r_suit" - item_color = "redshirt2" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security/corp - icon_state = "sec_corporate" - item_state = "sec_corporate" - item_color = "sec_corporate" - -/obj/item/clothing/under/rank/warden/corp - icon_state = "warden_corporate" - item_state = "warden_corporate" - item_color = "warden_corporate" - -/* - * Detective - */ -/obj/item/clothing/under/det - name = "hard-worn suit" - desc = "Someone who wears this means business." - icon_state = "detective" - item_state = "det" - item_color = "detective" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi' - ) - -/* - * Head of Security - */ -/obj/item/clothing/under/rank/head_of_security - desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpsuit" - icon_state = "hos" - item_state = "r_suit" - item_color = "hosred" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - flags_size = ONESIZEFITSALL - strip_delay = 60 - -/obj/item/clothing/under/rank/head_of_security/skirt - desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpskirt" - icon_state = "hosredf" - item_state = "r_suit" - item_color = "hosredf" - flags_size = null - -/obj/item/clothing/under/rank/head_of_security/corp - icon_state = "hos_corporate" - item_state = "hos_corporate" - item_color = "hos_corporate" - -//Jensen cosplay gear -/obj/item/clothing/under/rank/head_of_security/jensen - desc = "You never asked for anything that stylish." - name = "head of security's jumpsuit" - icon_state = "jensen" - item_state = "jensen" - item_color = "jensen" - flags_size = ONESIZEFITSALL - -//Paradise Station - -/obj/item/clothing/suit/armor/hos/hosnavyjacket - name = "head of security navy jacket" - icon_state = "hosdnavyjacket" - item_state = "hosdnavyjacket" - -/obj/item/clothing/suit/armor/hos/hosbluejacket - name = "head of security blue jacket" - icon_state = "hosbluejacket" - item_state = "hosbluejacket" - -/obj/item/clothing/suit/armor/hos/officernavyjacket - name = "officer's navy jacket" - icon_state = "officernavyjacket" - item_state = "officernavyjacket" - -/obj/item/clothing/suit/armor/hos/officerbluejacket - name = "officer's blue jacket" - icon_state = "officerbluejacket" - item_state = "officerbluejacket" - -//TG Station - -/obj/item/clothing/under/rank/security/formal - name = "security suit" - desc = "A formal security suit for officers complete with nanotrasen belt buckle." - icon_state = "security_formal" - item_state = "gy_suit" - item_color = "security_formal" - -/obj/item/clothing/under/rank/warden/formal - name = "warden's suit" - desc = "A formal security suit for the warden with blue desginations and '/Warden/' stiched into the shoulders." - icon_state = "warden_formal" - item_state = "gy_suit" - item_color = "warden_formal" - -/obj/item/clothing/under/rank/head_of_security/formal - name = "head of security's suit" - desc = "A security suit decorated for those few with the dedication to achieve the position of Head of Security." - icon_state = "hos_formal" - item_state = "gy_suit" - item_color = "hos_formal" - - -//Brig Physician -/obj/item/clothing/under/rank/security/brigphys - desc = "Jumpsuit for Brig Physician it has both medical and security protection." - name = "brig physician's jumpsuit" - icon_state = "brig_phys" - item_state = "brig_phys" - item_color = "brig_phys" - permeability_coefficient = 0.50 - armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 30, acid = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security/brigphys/skirt - desc = "A skirted Brig Physician uniform. It has both security and medical protection." - name = "brig physician's jumpskirt" - icon_state = "brig_physf" - item_state = "brig_physf" - item_color = "brig_physf" - permeability_coefficient = 0.50 - flags_size = ONESIZEFITSALL - -//Pod Pilot -/obj/item/clothing/under/rank/security/pod_pilot - desc = "Suit for your regular pod pilot." - name = "pod pilot's jumpsuit" - icon_state = "pod_pilot" - item_state = "pod_pilot" - item_color = "pod_pilot" +/* + * Contains: + * Security + * Detective + * Head of Security + */ + + +/* + * Security + */ +/obj/item/clothing/under/rank/warden + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpsuit" + icon_state = "warden" + item_state = "r_suit" + item_color = "warden" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + strip_delay = 50 + +/obj/item/clothing/under/rank/warden/skirt + desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpskirt" + icon_state = "wardenf" + item_state = "r_suit" + item_color = "wardenf" + flags_size = null + +/obj/item/clothing/under/rank/security + name = "security officer's jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "security" + item_state = "r_suit" + item_color = "secred" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + strip_delay = 50 + +/obj/item/clothing/under/rank/security/skirt + name = "security officer's jumpskirt" + desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." + icon_state = "secredf" + item_state = "r_suit" + item_color = "secredf" + flags_size = null + +/obj/item/clothing/under/rank/dispatch + name = "dispatcher's uniform" + desc = "A dress shirt and khakis with a security patch sewn on." + icon_state = "dispatch" + item_state = "dispatch" + item_color = "dispatch" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/security2 + name = "security officer's uniform" + desc = "It's made of a slightly sturdier material, to allow for robust protection." + icon_state = "redshirt2" + item_state = "r_suit" + item_color = "redshirt2" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/security/corp + icon_state = "sec_corporate" + item_state = "sec_corporate" + item_color = "sec_corporate" + +/obj/item/clothing/under/rank/warden/corp + icon_state = "warden_corporate" + item_state = "warden_corporate" + item_color = "warden_corporate" + +/* + * Detective + */ +/obj/item/clothing/under/det + name = "hard-worn suit" + desc = "Someone who wears this means business." + icon_state = "detective" + item_state = "det" + item_color = "detective" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + strip_delay = 50 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi' + ) + +/* + * Head of Security + */ +/obj/item/clothing/under/rank/head_of_security + desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpsuit" + icon_state = "hos" + item_state = "r_suit" + item_color = "hosred" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + flags_size = ONESIZEFITSALL + strip_delay = 60 + +/obj/item/clothing/under/rank/head_of_security/skirt + desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpskirt" + icon_state = "hosredf" + item_state = "r_suit" + item_color = "hosredf" + flags_size = null + +/obj/item/clothing/under/rank/head_of_security/corp + icon_state = "hos_corporate" + item_state = "hos_corporate" + item_color = "hos_corporate" + +//Jensen cosplay gear +/obj/item/clothing/under/rank/head_of_security/jensen + desc = "You never asked for anything that stylish." + name = "head of security's jumpsuit" + icon_state = "jensen" + item_state = "jensen" + item_color = "jensen" + flags_size = ONESIZEFITSALL + +//Paradise Station + +/obj/item/clothing/suit/armor/hos/hosnavyjacket + name = "head of security navy jacket" + icon_state = "hosdnavyjacket" + item_state = "hosdnavyjacket" + +/obj/item/clothing/suit/armor/hos/hosbluejacket + name = "head of security blue jacket" + icon_state = "hosbluejacket" + item_state = "hosbluejacket" + +/obj/item/clothing/suit/armor/hos/officernavyjacket + name = "officer's navy jacket" + icon_state = "officernavyjacket" + item_state = "officernavyjacket" + +/obj/item/clothing/suit/armor/hos/officerbluejacket + name = "officer's blue jacket" + icon_state = "officerbluejacket" + item_state = "officerbluejacket" + +//TG Station + +/obj/item/clothing/under/rank/security/formal + name = "security suit" + desc = "A formal security suit for officers complete with nanotrasen belt buckle." + icon_state = "security_formal" + item_state = "gy_suit" + item_color = "security_formal" + +/obj/item/clothing/under/rank/warden/formal + name = "warden's suit" + desc = "A formal security suit for the warden with blue desginations and '/Warden/' stiched into the shoulders." + icon_state = "warden_formal" + item_state = "gy_suit" + item_color = "warden_formal" + +/obj/item/clothing/under/rank/head_of_security/formal + name = "head of security's suit" + desc = "A security suit decorated for those few with the dedication to achieve the position of Head of Security." + icon_state = "hos_formal" + item_state = "gy_suit" + item_color = "hos_formal" + + +//Brig Physician +/obj/item/clothing/under/rank/security/brigphys + desc = "Jumpsuit for Brig Physician it has both medical and security protection." + name = "brig physician's jumpsuit" + icon_state = "brig_phys" + item_state = "brig_phys" + item_color = "brig_phys" + permeability_coefficient = 0.50 + armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 30, acid = 30) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/security/brigphys/skirt + desc = "A skirted Brig Physician uniform. It has both security and medical protection." + name = "brig physician's jumpskirt" + icon_state = "brig_physf" + item_state = "brig_physf" + item_color = "brig_physf" + permeability_coefficient = 0.50 + flags_size = ONESIZEFITSALL + +//Pod Pilot +/obj/item/clothing/under/rank/security/pod_pilot + desc = "Suit for your regular pod pilot." + name = "pod pilot's jumpsuit" + icon_state = "pod_pilot" + item_state = "pod_pilot" + item_color = "pod_pilot" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 5203aafdcf61..22a1d25ba22c 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -1,851 +1,851 @@ -/obj/item/clothing/under/pj/red - name = "red pj's" - desc = "Sleepwear." - icon_state = "red_pyjamas" - item_color = "red_pyjamas" - item_state = "w_suit" - -/obj/item/clothing/under/pj/blue - name = "blue pj's" - desc = "Sleepwear." - icon_state = "blue_pyjamas" - item_color = "blue_pyjamas" - item_state = "w_suit" - -/obj/item/clothing/under/patriotsuit - name = "Patriotic Suit" - desc = "Motorcycle not included." - icon_state = "ek" - item_state = "ek" - item_color = "ek" - -/obj/item/clothing/under/captain_fly - name = "rogue captains uniform" - desc = "For the man who doesn't care because he's still free." - icon_state = "captain_fly" - item_state = "captain_fly" - item_color = "captain_fly" - -/obj/item/clothing/under/scratch - name = "white suit" - desc = "A white suit, suitable for an excellent host" - icon_state = "scratch" - item_state = "scratch" - item_color = "scratch" - -/obj/item/clothing/under/sl_suit - desc = "It's a very amish looking suit." - name = "amish suit" - icon_state = "sl_suit" - item_color = "sl_suit" - -/obj/item/clothing/under/waiter - name = "waiter's outfit" - desc = "It's a very smart uniform with a special pocket for tip." - icon_state = "waiter" - item_state = "waiter" - item_color = "waiter" - -/obj/item/clothing/under/rank/mailman - name = "mailman's jumpsuit" - desc = "'Special delivery!'" - icon_state = "mailman" - item_state = "b_suit" - item_color = "mailman" - -/obj/item/clothing/under/rank/vice - name = "vice officer's jumpsuit" - desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." - icon_state = "vice" - item_state = "gy_suit" - item_color = "vice" - -/obj/item/clothing/under/solgov - name = "Sol Federation marine uniform" - desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces." - icon_state = "solgov" - item_state = "ro_suit" - item_color = "solgov" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - displays_id = 0 - -/obj/item/clothing/under/solgov/command - name = "Sol Federation Lieutenant's uniform" - desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces. This one has additional insignia on its shoulders." - icon_state = "solgovc" - item_color = "solgovc" - -/obj/item/clothing/under/solgov/rep - name = "Sol Federation representative's uniform" - desc = "A formal uniform worn by the diplomatic representatives of the Sol Federation." - icon_state = "solgovr" - item_color = "solgovr" - -/obj/item/clothing/under/rank/centcom_officer - desc = "It's a jumpsuit worn by CentComm Officers." - name = "\improper CentComm officer's jumpsuit" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - -/obj/item/clothing/under/rank/centcom_commander - desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." - name = "\improper CentComm officer's jumpsuit" - icon_state = "centcom" - item_state = "dg_suit" - item_color = "centcom" - -/obj/item/clothing/under/rank/centcom/officer - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant-Commander\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." - name = "\improper Nanotrasen Naval Officer Uniform" - icon_state = "navy_gold" - item_state = "navy_gold" - item_color = "navy_gold" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/captain - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." - name = "\improper Nanotrasen Naval Captain Uniform" - icon_state = "navy_gold" - item_state = "navy_gold" - item_color = "navy_gold" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/blueshield - desc = "Gold trim on space-black cloth, this uniform bears \"Close Protection\" on the left shoulder." - name = "\improper Formal Nanotrasen Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/representative - desc = "Gold trim on space-black cloth, this uniform bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Formal Nanotrasen Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/representative/New() - ..() - desc = "Gold trim on space-black cloth, this uniform bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/magistrate - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Formal Nanotrasen Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/magistrate/New() - ..() - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/diplomatic - desc = "A very gaudy and official looking uniform of the Nanotrasen Diplomatic Corps." - name = "\improper Nanotrasen Diplomatic Uniform" - icon_state = "presidente" - item_state = "g_suit" - item_color = "presidente" - displays_id = 0 - -/obj/item/clothing/under/rank/blueshield - name = "blueshield uniform" - desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. Standard issue to Blueshields." - icon_state = "ert_uniform" - item_state = "bl_suit" - item_color = "ert_uniform" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - -/obj/item/clothing/under/rank/blueshield/skirt - name = "blueshield skirt" - desc = "A short, black and grey with blue markings skirted uniform. For the feminine Blueshield." - icon_state = "blueshieldf" - item_state = "blueshieldf" - item_color = "blueshieldf" - -/obj/item/clothing/under/space - name = "\improper NASA jumpsuit" - desc = "It has a NASA logo on it and is made of space-proofed materials." - icon_state = "black" - item_state = "bl_suit" - item_color = "black" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - resistance_flags = NONE - -/obj/item/clothing/under/acj - name = "administrative cybernetic jumpsuit" - icon_state = "syndicate" - item_state = "bl_suit" - item_color = "syndicate" - desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS|HEAD - armor = list("melee" = 100, "bullet" = 100, "laser" = 100,"energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - slowdown = -10 - siemens_coefficient = 0 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF - -/obj/item/clothing/under/johnny - name = "johnny~~ jumpsuit" - desc = "Johnny~~" - icon_state = "johnny" - item_color = "johnny" - -/obj/item/clothing/under/rainbow - name = "rainbow" - desc = "rainbow" - icon_state = "rainbow" - item_state = "rainbow" - item_color = "rainbow" - -/obj/item/clothing/under/cloud - name = "cloud" - desc = "cloud" - icon_state = "cloud" - item_color = "cloud" - -/obj/item/clothing/under/psysuit - name = "dark undersuit" - desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." - icon_state = "psysuit" - item_state = "psysuit" - item_color = "psysuit" - -/obj/item/clothing/under/gimmick/rank/captain/suit - name = "captain's suit" - desc = "A green suit and yellow necktie. Exemplifies authority." - icon_state = "green_suit" - item_state = "dg_suit" - item_color = "green_suit" - -/obj/item/clothing/under/gimmick/rank/head_of_personnel/suit - name = "head of personnel's suit" - desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." - icon_state = "teal_suit" - item_state = "g_suit" - item_color = "teal_suit" - -/obj/item/clothing/under/suit_jacket - name = "black suit" - desc = "A black suit and red tie. Very formal." - icon_state = "black_suit" - item_state = "bl_suit" - item_color = "black_suit" - -/obj/item/clothing/under/suit_jacket/really_black - name = "executive suit" - desc = "A formal black suit and red tie, intended for the station's finest." - icon_state = "really_black_suit" - item_state = "bl_suit" - item_color = "really_black_suit" - -/obj/item/clothing/under/suit_jacket/female - name = "executive suit" - desc = "A formal trouser suit for women, intended for the station's finest." - icon_state = "black_suit_fem" - item_state = "black_suit_fem" - item_color = "black_suit_fem" - -/obj/item/clothing/under/suit_jacket/red - name = "red suit" - desc = "A red suit and blue tie. Somewhat formal." - icon_state = "red_suit" - item_state = "r_suit" - item_color = "red_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/suit_jacket/navy - name = "navy suit" - desc = "A navy suit and red tie, intended for the station's finest." - icon_state = "navy_suit" - item_state = "navy_suit" - item_color = "navy_suit" - -/obj/item/clothing/under/suit_jacket/tan - name = "tan suit" - desc = "A tan suit with a yellow tie. Smart, but casual." - icon_state = "tan_suit" - item_state = "tan_suit" - item_color = "tan_suit" - -/obj/item/clothing/under/suit_jacket/burgundy - name = "burgundy suit" - desc = "A burgundy suit and black tie. Somewhat formal." - icon_state = "burgundy_suit" - item_state = "burgundy_suit" - item_color = "burgundy_suit" - -/obj/item/clothing/under/suit_jacket/charcoal - name = "charcoal suit" - desc = "A charcoal suit and blue tie. Very professional." - icon_state = "charcoal_suit" - item_state = "charcoal_suit" - item_color = "charcoal_suit" - -/obj/item/clothing/under/blackskirt - name = "black skirt" - desc = "A black skirt, very fancy!" - icon_state = "blackskirt" - item_color = "blackskirt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/schoolgirl - name = "schoolgirl uniform" - desc = "It's just like one of my Japanese animes!" - icon_state = "schoolgirl" - item_state = "schoolgirl" - item_color = "schoolgirl" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/overalls - name = "laborer's overalls" - desc = "A set of durable overalls for getting the job done." - icon_state = "overalls" - item_state = "lb_suit" - item_color = "overalls" - -/obj/item/clothing/under/pirate - name = "pirate outfit" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - item_color = "pirate" - -/obj/item/clothing/under/pirate_rags - name = "pirate rags" - desc = "an old ragged set of clothing" - icon_state = "piraterags" - item_state = "piraterags" - item_color = "piraterags" - -/obj/item/clothing/under/soviet - name = "soviet uniform" - desc = "For the Motherland!" - icon_state = "soviet" - item_state = "soviet" - item_color = "soviet" - -/obj/item/clothing/under/redcoat - name = "redcoat uniform" - desc = "Looks old." - icon_state = "redcoat" - item_state = "redcoat" - item_color = "redcoat" - -/obj/item/clothing/under/kilt - name = "kilt" - desc = "Includes shoes and plaid" - icon_state = "kilt" - item_state = "kilt" - item_color = "kilt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET - -/obj/item/clothing/under/sexymime - name = "sexy mime outfit" - desc = "The only time when you DON'T enjoy looking at someone's rack." - icon_state = "sexymime" - item_state = "sexymime" - item_color = "sexymime" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/gladiator - name = "gladiator uniform" - desc = "Are you not entertained? Is that not why you are here?" - icon_state = "gladiator" - item_state = "gladiator" - item_color = "gladiator" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - resistance_flags = NONE - -/obj/item/clothing/under/gladiator/ash_walker - desc = "This gladiator uniform appears to be covered in ash and fairly dated." - has_sensor = FALSE - -//dress - -/obj/item/clothing/under/dress/dress_fire - name = "flame dress" - desc = "A small black dress with blue flames print on it." - icon_state = "dress_fire" - item_color = "dress_fire" - -/obj/item/clothing/under/dress/dress_green - name = "green dress" - desc = "A simple, tight fitting green dress." - icon_state = "dress_green" - item_color = "dress_green" - -/obj/item/clothing/under/dress/dress_orange - name = "orange dress" - desc = "A fancy orange gown for those who like to show leg." - icon_state = "dress_orange" - item_color = "dress_orange" - -/obj/item/clothing/under/dress/dress_pink - name = "pink dress" - desc = "A simple, tight fitting pink dress." - icon_state = "dress_pink" - item_color = "dress_pink" - -/obj/item/clothing/under/dress/dress_yellow - name = "yellow dress" - desc = "A flirty, little yellow dress." - icon_state = "dress_yellow" - item_color = "dress_yellow" - -/obj/item/clothing/under/dress/dress_saloon - name = "saloon girl dress" - desc = "A old western inspired gown for the girl who likes to drink." - icon_state = "dress_saloon" - item_color = "dress_saloon" - -/obj/item/clothing/under/dress/dress_rd - name = "research director dress uniform" - desc = "Feminine fashion for the style concious RD." - icon_state = "dress_rd" - item_color = "dress_rd" - -/obj/item/clothing/under/dress/dress_cap - name = "captain dress uniform" - desc = "Feminine fashion for the style concious captain." - icon_state = "dress_cap" - item_color = "dress_cap" - -/obj/item/clothing/under/dress/dress_hop - name = "head of personal dress uniform" - desc = "Feminine fashion for the style concious HoP." - icon_state = "dress_hop" - item_color = "dress_hop" - -/obj/item/clothing/under/dress/dress_hr - name = "human resources director uniform" - desc = "Superior class for the nosy H.R. Director." - icon_state = "huresource" - item_color = "huresource" - -/obj/item/clothing/under/dress/plaid_blue - name = "blue plaid skirt" - desc = "A preppy blue skirt with a white blouse." - icon_state = "plaid_blue" - item_color = "plaid_blue" - -/obj/item/clothing/under/dress/plaid_red - name = "red plaid skirt" - desc = "A preppy red skirt with a white blouse." - icon_state = "plaid_red" - item_color = "plaid_red" - -/obj/item/clothing/under/dress/plaid_purple - name = "blue purple skirt" - desc = "A preppy purple skirt with a white blouse." - icon_state = "plaid_purple" - item_color = "plaid_purple" - -//wedding stuff - -/obj/item/clothing/under/wedding/bride_orange - name = "orange wedding dress" - desc = "A big and puffy orange dress." - icon_state = "bride_orange" - item_color = "bride_orange" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_purple - name = "purple wedding dress" - desc = "A big and puffy purple dress." - icon_state = "bride_purple" - item_color = "bride_purple" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_blue - name = "blue wedding dress" - desc = "A big and puffy blue dress." - icon_state = "bride_blue" - item_color = "bride_blue" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_red - name = "red wedding dress" - desc = "A big and puffy red dress." - icon_state = "bride_red" - item_color = "bride_red" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_white - name = "orange wedding dress" - desc = "A white wedding gown made from the finest silk." - icon_state = "bride_white" - item_color = "bride_white" - flags_inv = HIDESHOES - -/obj/item/clothing/under/sundress - name = "sundress" - desc = "Makes you want to frolic in a field of daisies." - icon_state = "sundress" - item_state = "sundress" - item_color = "sundress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/captainparade - name = "captain's parade uniform" - desc = "A captain's luxury-wear, for special occasions." - icon_state = "captain_parade" - item_state = "by_suit" - item_color = "captain_parade" - -/obj/item/clothing/under/roman - name = "roman armor" - desc = "An ancient Roman armor. Made of metallic strips and leather straps." - icon_state = "roman" - item_color = "roman" - item_state = "armor" - strip_delay = 100 - resistance_flags = NONE - -/obj/item/clothing/under/maid - name = "maid costume" - desc = "Maid in China." - icon_state = "meido" - item_state = "meido" - item_color = "meido" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/janimaid - name = "maid uniform" - desc = "A simple maid uniform for housekeeping." - icon_state = "janimaid" - item_state = "janimaid" - item_color = "janimaid" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/singery - name = "yellow performer's outfit" - desc = "Just looking at this makes you want to sing." - icon_state = "ysing" - item_state = "ysing" - item_color = "ysing" - -/obj/item/clothing/under/singerb - name = "blue performer's outfit" - desc = "Just looking at this makes you want to sing." - icon_state = "bsing" - item_state = "bsing" - item_color = "bsing" - -/obj/item/clothing/under/jester - name = "jester suit" - desc = "A jolly dress, well suited to entertain your master, nuncle." - icon_state = "jester" - item_color = "jester" - -/obj/item/clothing/under/flappers - name = "flappers" - desc = "Nothing like the roarin' '20s, flapping the night away on the dance floor." - icon_state = "flapper" - item_state = "flapper" - item_color = "flapper" - -/obj/item/clothing/under/mafia - name = "mafia outfit" - desc = "The business of the mafia is business." - icon_state = "mafia" - item_state = "mafia" - item_color = "mafia" - -/obj/item/clothing/under/mafia/vest - name = "mafia vest" - desc = "Extreme problems often require extreme solutions." - icon_state = "mafiavest" - item_state = "mafiavest" - item_color = "mafiavest" - -/obj/item/clothing/under/mafia/white - name = "white mafia outfit" - desc = "The best defense against the treacherous is treachery." - icon_state = "mafiawhite" - item_state = "mafiawhite" - item_color = "mafiawhite" - -/obj/item/clothing/under/mafia/sue - name = "mafia vest" - desc = "The business is born into." - icon_state = "suevest" - item_state = "suevest" - item_color = "suevest" - -/obj/item/clothing/under/mafia/tan - name = "leather mafia outfit" - desc = "The big drum sounds good only from a distance." - icon_state = "mafiatan" - item_state = "mafiatan" - item_color = "mafiatan" - - -/obj/item/clothing/under/bane - name = "Bane Harness" - desc = "Wear this harness to become the bane of the station." - icon_state = "bane" - item_state = "bane" - item_color = "bane" - -/obj/item/clothing/under/vox - name = "Ripped Jumpsuit" - desc = "A jumpsuit that looks like it's been shredded by some talons. Who could wear this now?" - icon = 'icons/obj/clothing/species/vox/uniforms.dmi' - icon_state = "vgrey" - item_state = "vgrey" - item_color = "vgrey" - -/obj/item/clothing/under/psyjump - name = "Psychic Amp Jumpsuit" - desc = "A suit made of strange materials." - icon_state = "psyamp" - item_state = "psyamp" - item_color = "psyamp" - -/obj/item/clothing/under/rebeloutfit - name = "Rebel Outfit" - desc = "Made in Seattle, 2216." - icon_state = "colin_earle" - item_state = "colin_earle" - item_color = "colin_earle" - -/obj/item/clothing/under/officeruniform - name = "Clown Officer's Uniform" - desc = "For Clown officers, this uniform was designed by the great clown designer Hugo Boss." - icon_state = "officeruniform" - item_color = "officeruniform" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/soldieruniform - name = "Clown Soldier's Uniform" - desc = "For the basic grunt of the Clown army." - icon_state = "soldieruniform" - item_color = "soldieruniform" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/pennywise - name = "Pennywise Costume" - desc = "It's everything you ever were afraid of." - icon_state = "pennywise" - item_color = "pennywise" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/assistantformal - name = "assistant's formal uniform" - desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." - icon_state = "assistant_formal" - item_state = "gy_suit" - item_color = "assistant_formal" - -/obj/item/clothing/under/blacktango - name = "black tango dress" - desc = "Filled with Latin fire." - icon_state = "black_tango" - item_state = "wcoat" - item_color = "black_tango" - -/obj/item/clothing/under/stripeddress - name = "striped dress" - desc = "Fashion in space." - icon_state = "striped_dress" - item_state = "stripeddress" - item_color = "striped_dress" - -/obj/item/clothing/under/sailordress - name = "sailor dress" - desc = "Formal wear for a leading lady." - icon_state = "sailor_dress" - item_state = "sailordress" - item_color = "sailor_dress" - -/obj/item/clothing/under/redeveninggown - name = "red evening gown" - desc = "Fancy dress for space bar singers." - icon_state = "red_evening_gown" - item_state = "redeveninggown" - item_color = "red_evening_gown" - -/obj/item/clothing/under/suit_jacket/checkered - name = "checkered suit" - desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" - icon_state = "checkered_suit" - item_state = "checkered_suit" - item_color = "checkered_suit" - -/obj/item/clothing/under/owl - name = "owl uniform" - desc = "A soft brown jumpsuit made of synthetic feathers and strong conviction." - icon_state = "owl" - item_color = "owl" - -/obj/item/clothing/under/griffin - name = "griffon uniform" - desc = "A soft brown jumpsuit with a white feather collar made of synthetic feathers and a lust for mayhem." - icon_state = "griffin" - item_color = "griffin" - -/obj/item/clothing/under/noble_clothes - name = "noble clothes" - desc = "They fall just short of majestic." - icon_state = "noble_clothes" - item_color = "noble_clothes" - item_state = "noble_clothes" - -/obj/item/clothing/under/contortionist - name = "atmospheric technician's jumpsuit" - desc = "A light jumpsuit useful for squeezing through narrow vents." - icon_state = "atmos" - item_state = "atmos_suit" - item_color = "atmos" - resistance_flags = FIRE_PROOF - -/obj/item/clothing/under/contortionist/equipped(mob/living/carbon/human/user, slot) - if(!user.ventcrawler) - user.ventcrawler = 2 - ..() - -/obj/item/clothing/under/contortionist/dropped(mob/living/carbon/human/user) - if(!user.get_int_organ(/obj/item/organ/internal/heart/gland/ventcrawling)) - user.ventcrawler = 0 - ..() - -/obj/item/clothing/under/contortionist/proc/check_clothing(mob/user as mob) - //Allowed to wear: glasses, shoes, gloves, pockets, mask, and jumpsuit (obviously) - var/list/slot_must_be_empty = list(slot_back,slot_handcuffed,slot_legcuffed,slot_l_hand,slot_r_hand,slot_belt,slot_head,slot_wear_suit) - for(var/slot_id in slot_must_be_empty) - if(user.get_item_by_slot(slot_id)) - to_chat(user,"You can't fit inside while wearing that \the [user.get_item_by_slot(slot_id)].") - return 0 - return 1 - -/obj/item/clothing/under/cursedclown - name = "cursed clown suit" - desc = "It wasn't already?" - icon = 'icons/goonstation/objects/clothing/uniform.dmi' - icon_state = "cursedclown" - item_state = "cclown_uniform" - item_color = "cursedclown" - icon_override = 'icons/goonstation/mob/clothing/uniform.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - flags_size = ONESIZEFITSALL - has_sensor = 0 // HUNKE - -/obj/item/clothing/under/victdress - name = "black victorian dress" - desc = "A victorian style dress, fancy!" - icon_state = "victorianblackdress" - item_state = "victorianblackdress" - item_color = "victorianblackdress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/victdress/red - name = "red victorian dress" - icon_state = "victorianreddress" - item_state = "victorianreddress" - item_color = "victorianreddress" - -/obj/item/clothing/under/victsuit - name = "victorian suit" - desc = "A victorian style suit, fancy!" - icon_state = "victorianvest" - item_state = "victorianvest" - item_color = "victorianvest" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/victsuit/redblk - name = "red and black victorian suit" - icon_state = "victorianblred" - item_state = "victorianblred" - item_color = "victorianblred" - -/obj/item/clothing/under/victsuit/red - name = "red victorian suit" - icon_state = "victorianredvest" - item_state = "victorianredvest" - item_color = "victorianredvest" - -/obj/item/clothing/under/medigown - name = "medical gown" - desc = "a flimsy examination gown, the back ties never close." - icon_state = "medicalgown" - item_state = "medicalgown" - item_color = "medicalgown" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/burial - name = "burial garments" - desc = "Traditional burial garments from the early 22nd century." - icon_state = "burial" - item_state = "burial" - item_color = "burial" - -/obj/item/clothing/under/redhawaiianshirt - name = "red hawaiian shirt" - desc = "a floral shirt worn to most vacation destinations." - icon_state = "hawaiianred" - item_state = "hawaiianred" - item_color = "hawaiianred" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/pinkhawaiianshirt - name = "pink hawaiian shirt" - desc = "a pink floral shirt the material feels cool and comfy." - icon_state = "hawaiianpink" - item_state = "hawaiianpink" - item_color = "hawaiianpink" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/orangehawaiianshirt - name = "orange hawaiian shirt" - desc = "a orange floral shirt for a relaxing day in space." - icon_state = "hawaiianorange" - item_state = "hawaiianorange" - item_color = "hawaiianorange" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/bluehawaiianshirt - name = "blue hawaiian shirt" - desc = "a blue floral shirt it has a oddly colored pink flower on it." - icon_state = "hawaiianblue" - item_state = "hawaiianblue" - item_color = "hawaiianblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/misc/durathread - name = "durathread jumpsuit" - desc = "A jumpsuit made from durathread, its resilient fibres provide some protection to the wearer." - icon_state = "durathread" - item_state = "durathread" - item_color = "durathread" - armor = list(melee = 10, bullet = 0, laser = 10, energy = 0, bomb = 5, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/under/cuban_suit - name = "rhumba outfit" - desc = "A satin shirt and high-waisted pants, worn by dancers in the Rhumba style. It smells oddly like... sulfur?" - icon_state = "cuban_suit" - item_state = "cuban_suit" - item_color = "cuban_suit" \ No newline at end of file +/obj/item/clothing/under/pj/red + name = "red pj's" + desc = "Sleepwear." + icon_state = "red_pyjamas" + item_color = "red_pyjamas" + item_state = "w_suit" + +/obj/item/clothing/under/pj/blue + name = "blue pj's" + desc = "Sleepwear." + icon_state = "blue_pyjamas" + item_color = "blue_pyjamas" + item_state = "w_suit" + +/obj/item/clothing/under/patriotsuit + name = "Patriotic Suit" + desc = "Motorcycle not included." + icon_state = "ek" + item_state = "ek" + item_color = "ek" + +/obj/item/clothing/under/captain_fly + name = "rogue captains uniform" + desc = "For the man who doesn't care because he's still free." + icon_state = "captain_fly" + item_state = "captain_fly" + item_color = "captain_fly" + +/obj/item/clothing/under/scratch + name = "white suit" + desc = "A white suit, suitable for an excellent host" + icon_state = "scratch" + item_state = "scratch" + item_color = "scratch" + +/obj/item/clothing/under/sl_suit + desc = "It's a very amish looking suit." + name = "amish suit" + icon_state = "sl_suit" + item_color = "sl_suit" + +/obj/item/clothing/under/waiter + name = "waiter's outfit" + desc = "It's a very smart uniform with a special pocket for tip." + icon_state = "waiter" + item_state = "waiter" + item_color = "waiter" + +/obj/item/clothing/under/rank/mailman + name = "mailman's jumpsuit" + desc = "'Special delivery!'" + icon_state = "mailman" + item_state = "b_suit" + item_color = "mailman" + +/obj/item/clothing/under/rank/vice + name = "vice officer's jumpsuit" + desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." + icon_state = "vice" + item_state = "gy_suit" + item_color = "vice" + +/obj/item/clothing/under/solgov + name = "Sol Federation marine uniform" + desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces." + icon_state = "solgov" + item_state = "ro_suit" + item_color = "solgov" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + displays_id = 0 + +/obj/item/clothing/under/solgov/command + name = "Sol Federation Lieutenant's uniform" + desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces. This one has additional insignia on its shoulders." + icon_state = "solgovc" + item_color = "solgovc" + +/obj/item/clothing/under/solgov/rep + name = "Sol Federation representative's uniform" + desc = "A formal uniform worn by the diplomatic representatives of the Sol Federation." + icon_state = "solgovr" + item_color = "solgovr" + +/obj/item/clothing/under/rank/centcom_officer + desc = "It's a jumpsuit worn by CentComm Officers." + name = "\improper CentComm officer's jumpsuit" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + +/obj/item/clothing/under/rank/centcom_commander + desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." + name = "\improper CentComm officer's jumpsuit" + icon_state = "centcom" + item_state = "dg_suit" + item_color = "centcom" + +/obj/item/clothing/under/rank/centcom/officer + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant-Commander\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." + name = "\improper Nanotrasen Naval Officer Uniform" + icon_state = "navy_gold" + item_state = "navy_gold" + item_color = "navy_gold" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/captain + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." + name = "\improper Nanotrasen Naval Captain Uniform" + icon_state = "navy_gold" + item_state = "navy_gold" + item_color = "navy_gold" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/blueshield + desc = "Gold trim on space-black cloth, this uniform bears \"Close Protection\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/representative + desc = "Gold trim on space-black cloth, this uniform bears \"N.S.S. Cyberiad\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/representative/New() + ..() + desc = "Gold trim on space-black cloth, this uniform bears [station_name()] on the left shoulder." + +/obj/item/clothing/under/rank/centcom/magistrate + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears \"N.S.S. Cyberiad\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/magistrate/New() + ..() + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears [station_name()] on the left shoulder." + +/obj/item/clothing/under/rank/centcom/diplomatic + desc = "A very gaudy and official looking uniform of the Nanotrasen Diplomatic Corps." + name = "\improper Nanotrasen Diplomatic Uniform" + icon_state = "presidente" + item_state = "g_suit" + item_color = "presidente" + displays_id = 0 + +/obj/item/clothing/under/rank/blueshield + name = "blueshield uniform" + desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. Standard issue to Blueshields." + icon_state = "ert_uniform" + item_state = "bl_suit" + item_color = "ert_uniform" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + +/obj/item/clothing/under/rank/blueshield/skirt + name = "blueshield skirt" + desc = "A short, black and grey with blue markings skirted uniform. For the feminine Blueshield." + icon_state = "blueshieldf" + item_state = "blueshieldf" + item_color = "blueshieldf" + +/obj/item/clothing/under/space + name = "\improper NASA jumpsuit" + desc = "It has a NASA logo on it and is made of space-proofed materials." + icon_state = "black" + item_state = "bl_suit" + item_color = "black" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + resistance_flags = NONE + +/obj/item/clothing/under/acj + name = "administrative cybernetic jumpsuit" + icon_state = "syndicate" + item_state = "bl_suit" + item_color = "syndicate" + desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS|HEAD + armor = list("melee" = 100, "bullet" = 100, "laser" = 100,"energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + slowdown = -10 + siemens_coefficient = 0 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF + +/obj/item/clothing/under/johnny + name = "johnny~~ jumpsuit" + desc = "Johnny~~" + icon_state = "johnny" + item_color = "johnny" + +/obj/item/clothing/under/rainbow + name = "rainbow" + desc = "rainbow" + icon_state = "rainbow" + item_state = "rainbow" + item_color = "rainbow" + +/obj/item/clothing/under/cloud + name = "cloud" + desc = "cloud" + icon_state = "cloud" + item_color = "cloud" + +/obj/item/clothing/under/psysuit + name = "dark undersuit" + desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." + icon_state = "psysuit" + item_state = "psysuit" + item_color = "psysuit" + +/obj/item/clothing/under/gimmick/rank/captain/suit + name = "captain's suit" + desc = "A green suit and yellow necktie. Exemplifies authority." + icon_state = "green_suit" + item_state = "dg_suit" + item_color = "green_suit" + +/obj/item/clothing/under/gimmick/rank/head_of_personnel/suit + name = "head of personnel's suit" + desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." + icon_state = "teal_suit" + item_state = "g_suit" + item_color = "teal_suit" + +/obj/item/clothing/under/suit_jacket + name = "black suit" + desc = "A black suit and red tie. Very formal." + icon_state = "black_suit" + item_state = "bl_suit" + item_color = "black_suit" + +/obj/item/clothing/under/suit_jacket/really_black + name = "executive suit" + desc = "A formal black suit and red tie, intended for the station's finest." + icon_state = "really_black_suit" + item_state = "bl_suit" + item_color = "really_black_suit" + +/obj/item/clothing/under/suit_jacket/female + name = "executive suit" + desc = "A formal trouser suit for women, intended for the station's finest." + icon_state = "black_suit_fem" + item_state = "black_suit_fem" + item_color = "black_suit_fem" + +/obj/item/clothing/under/suit_jacket/red + name = "red suit" + desc = "A red suit and blue tie. Somewhat formal." + icon_state = "red_suit" + item_state = "r_suit" + item_color = "red_suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/suit_jacket/navy + name = "navy suit" + desc = "A navy suit and red tie, intended for the station's finest." + icon_state = "navy_suit" + item_state = "navy_suit" + item_color = "navy_suit" + +/obj/item/clothing/under/suit_jacket/tan + name = "tan suit" + desc = "A tan suit with a yellow tie. Smart, but casual." + icon_state = "tan_suit" + item_state = "tan_suit" + item_color = "tan_suit" + +/obj/item/clothing/under/suit_jacket/burgundy + name = "burgundy suit" + desc = "A burgundy suit and black tie. Somewhat formal." + icon_state = "burgundy_suit" + item_state = "burgundy_suit" + item_color = "burgundy_suit" + +/obj/item/clothing/under/suit_jacket/charcoal + name = "charcoal suit" + desc = "A charcoal suit and blue tie. Very professional." + icon_state = "charcoal_suit" + item_state = "charcoal_suit" + item_color = "charcoal_suit" + +/obj/item/clothing/under/blackskirt + name = "black skirt" + desc = "A black skirt, very fancy!" + icon_state = "blackskirt" + item_color = "blackskirt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/schoolgirl + name = "schoolgirl uniform" + desc = "It's just like one of my Japanese animes!" + icon_state = "schoolgirl" + item_state = "schoolgirl" + item_color = "schoolgirl" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/overalls + name = "laborer's overalls" + desc = "A set of durable overalls for getting the job done." + icon_state = "overalls" + item_state = "lb_suit" + item_color = "overalls" + +/obj/item/clothing/under/pirate + name = "pirate outfit" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + item_color = "pirate" + +/obj/item/clothing/under/pirate_rags + name = "pirate rags" + desc = "an old ragged set of clothing" + icon_state = "piraterags" + item_state = "piraterags" + item_color = "piraterags" + +/obj/item/clothing/under/soviet + name = "soviet uniform" + desc = "For the Motherland!" + icon_state = "soviet" + item_state = "soviet" + item_color = "soviet" + +/obj/item/clothing/under/redcoat + name = "redcoat uniform" + desc = "Looks old." + icon_state = "redcoat" + item_state = "redcoat" + item_color = "redcoat" + +/obj/item/clothing/under/kilt + name = "kilt" + desc = "Includes shoes and plaid" + icon_state = "kilt" + item_state = "kilt" + item_color = "kilt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET + +/obj/item/clothing/under/sexymime + name = "sexy mime outfit" + desc = "The only time when you DON'T enjoy looking at someone's rack." + icon_state = "sexymime" + item_state = "sexymime" + item_color = "sexymime" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/gladiator + name = "gladiator uniform" + desc = "Are you not entertained? Is that not why you are here?" + icon_state = "gladiator" + item_state = "gladiator" + item_color = "gladiator" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + resistance_flags = NONE + +/obj/item/clothing/under/gladiator/ash_walker + desc = "This gladiator uniform appears to be covered in ash and fairly dated." + has_sensor = FALSE + +//dress + +/obj/item/clothing/under/dress/dress_fire + name = "flame dress" + desc = "A small black dress with blue flames print on it." + icon_state = "dress_fire" + item_color = "dress_fire" + +/obj/item/clothing/under/dress/dress_green + name = "green dress" + desc = "A simple, tight fitting green dress." + icon_state = "dress_green" + item_color = "dress_green" + +/obj/item/clothing/under/dress/dress_orange + name = "orange dress" + desc = "A fancy orange gown for those who like to show leg." + icon_state = "dress_orange" + item_color = "dress_orange" + +/obj/item/clothing/under/dress/dress_pink + name = "pink dress" + desc = "A simple, tight fitting pink dress." + icon_state = "dress_pink" + item_color = "dress_pink" + +/obj/item/clothing/under/dress/dress_yellow + name = "yellow dress" + desc = "A flirty, little yellow dress." + icon_state = "dress_yellow" + item_color = "dress_yellow" + +/obj/item/clothing/under/dress/dress_saloon + name = "saloon girl dress" + desc = "A old western inspired gown for the girl who likes to drink." + icon_state = "dress_saloon" + item_color = "dress_saloon" + +/obj/item/clothing/under/dress/dress_rd + name = "research director dress uniform" + desc = "Feminine fashion for the style concious RD." + icon_state = "dress_rd" + item_color = "dress_rd" + +/obj/item/clothing/under/dress/dress_cap + name = "captain dress uniform" + desc = "Feminine fashion for the style concious captain." + icon_state = "dress_cap" + item_color = "dress_cap" + +/obj/item/clothing/under/dress/dress_hop + name = "head of personal dress uniform" + desc = "Feminine fashion for the style concious HoP." + icon_state = "dress_hop" + item_color = "dress_hop" + +/obj/item/clothing/under/dress/dress_hr + name = "human resources director uniform" + desc = "Superior class for the nosy H.R. Director." + icon_state = "huresource" + item_color = "huresource" + +/obj/item/clothing/under/dress/plaid_blue + name = "blue plaid skirt" + desc = "A preppy blue skirt with a white blouse." + icon_state = "plaid_blue" + item_color = "plaid_blue" + +/obj/item/clothing/under/dress/plaid_red + name = "red plaid skirt" + desc = "A preppy red skirt with a white blouse." + icon_state = "plaid_red" + item_color = "plaid_red" + +/obj/item/clothing/under/dress/plaid_purple + name = "blue purple skirt" + desc = "A preppy purple skirt with a white blouse." + icon_state = "plaid_purple" + item_color = "plaid_purple" + +//wedding stuff + +/obj/item/clothing/under/wedding/bride_orange + name = "orange wedding dress" + desc = "A big and puffy orange dress." + icon_state = "bride_orange" + item_color = "bride_orange" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_purple + name = "purple wedding dress" + desc = "A big and puffy purple dress." + icon_state = "bride_purple" + item_color = "bride_purple" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_blue + name = "blue wedding dress" + desc = "A big and puffy blue dress." + icon_state = "bride_blue" + item_color = "bride_blue" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_red + name = "red wedding dress" + desc = "A big and puffy red dress." + icon_state = "bride_red" + item_color = "bride_red" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_white + name = "orange wedding dress" + desc = "A white wedding gown made from the finest silk." + icon_state = "bride_white" + item_color = "bride_white" + flags_inv = HIDESHOES + +/obj/item/clothing/under/sundress + name = "sundress" + desc = "Makes you want to frolic in a field of daisies." + icon_state = "sundress" + item_state = "sundress" + item_color = "sundress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/captainparade + name = "captain's parade uniform" + desc = "A captain's luxury-wear, for special occasions." + icon_state = "captain_parade" + item_state = "by_suit" + item_color = "captain_parade" + +/obj/item/clothing/under/roman + name = "roman armor" + desc = "An ancient Roman armor. Made of metallic strips and leather straps." + icon_state = "roman" + item_color = "roman" + item_state = "armor" + strip_delay = 100 + resistance_flags = NONE + +/obj/item/clothing/under/maid + name = "maid costume" + desc = "Maid in China." + icon_state = "meido" + item_state = "meido" + item_color = "meido" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/janimaid + name = "maid uniform" + desc = "A simple maid uniform for housekeeping." + icon_state = "janimaid" + item_state = "janimaid" + item_color = "janimaid" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/singery + name = "yellow performer's outfit" + desc = "Just looking at this makes you want to sing." + icon_state = "ysing" + item_state = "ysing" + item_color = "ysing" + +/obj/item/clothing/under/singerb + name = "blue performer's outfit" + desc = "Just looking at this makes you want to sing." + icon_state = "bsing" + item_state = "bsing" + item_color = "bsing" + +/obj/item/clothing/under/jester + name = "jester suit" + desc = "A jolly dress, well suited to entertain your master, nuncle." + icon_state = "jester" + item_color = "jester" + +/obj/item/clothing/under/flappers + name = "flappers" + desc = "Nothing like the roarin' '20s, flapping the night away on the dance floor." + icon_state = "flapper" + item_state = "flapper" + item_color = "flapper" + +/obj/item/clothing/under/mafia + name = "mafia outfit" + desc = "The business of the mafia is business." + icon_state = "mafia" + item_state = "mafia" + item_color = "mafia" + +/obj/item/clothing/under/mafia/vest + name = "mafia vest" + desc = "Extreme problems often require extreme solutions." + icon_state = "mafiavest" + item_state = "mafiavest" + item_color = "mafiavest" + +/obj/item/clothing/under/mafia/white + name = "white mafia outfit" + desc = "The best defense against the treacherous is treachery." + icon_state = "mafiawhite" + item_state = "mafiawhite" + item_color = "mafiawhite" + +/obj/item/clothing/under/mafia/sue + name = "mafia vest" + desc = "The business is born into." + icon_state = "suevest" + item_state = "suevest" + item_color = "suevest" + +/obj/item/clothing/under/mafia/tan + name = "leather mafia outfit" + desc = "The big drum sounds good only from a distance." + icon_state = "mafiatan" + item_state = "mafiatan" + item_color = "mafiatan" + + +/obj/item/clothing/under/bane + name = "Bane Harness" + desc = "Wear this harness to become the bane of the station." + icon_state = "bane" + item_state = "bane" + item_color = "bane" + +/obj/item/clothing/under/vox + name = "Ripped Jumpsuit" + desc = "A jumpsuit that looks like it's been shredded by some talons. Who could wear this now?" + icon = 'icons/obj/clothing/species/vox/uniforms.dmi' + icon_state = "vgrey" + item_state = "vgrey" + item_color = "vgrey" + +/obj/item/clothing/under/psyjump + name = "Psychic Amp Jumpsuit" + desc = "A suit made of strange materials." + icon_state = "psyamp" + item_state = "psyamp" + item_color = "psyamp" + +/obj/item/clothing/under/rebeloutfit + name = "Rebel Outfit" + desc = "Made in Seattle, 2216." + icon_state = "colin_earle" + item_state = "colin_earle" + item_color = "colin_earle" + +/obj/item/clothing/under/officeruniform + name = "Clown Officer's Uniform" + desc = "For Clown officers, this uniform was designed by the great clown designer Hugo Boss." + icon_state = "officeruniform" + item_color = "officeruniform" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/soldieruniform + name = "Clown Soldier's Uniform" + desc = "For the basic grunt of the Clown army." + icon_state = "soldieruniform" + item_color = "soldieruniform" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/pennywise + name = "Pennywise Costume" + desc = "It's everything you ever were afraid of." + icon_state = "pennywise" + item_color = "pennywise" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/assistantformal + name = "assistant's formal uniform" + desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." + icon_state = "assistant_formal" + item_state = "gy_suit" + item_color = "assistant_formal" + +/obj/item/clothing/under/blacktango + name = "black tango dress" + desc = "Filled with Latin fire." + icon_state = "black_tango" + item_state = "wcoat" + item_color = "black_tango" + +/obj/item/clothing/under/stripeddress + name = "striped dress" + desc = "Fashion in space." + icon_state = "striped_dress" + item_state = "stripeddress" + item_color = "striped_dress" + +/obj/item/clothing/under/sailordress + name = "sailor dress" + desc = "Formal wear for a leading lady." + icon_state = "sailor_dress" + item_state = "sailordress" + item_color = "sailor_dress" + +/obj/item/clothing/under/redeveninggown + name = "red evening gown" + desc = "Fancy dress for space bar singers." + icon_state = "red_evening_gown" + item_state = "redeveninggown" + item_color = "red_evening_gown" + +/obj/item/clothing/under/suit_jacket/checkered + name = "checkered suit" + desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" + icon_state = "checkered_suit" + item_state = "checkered_suit" + item_color = "checkered_suit" + +/obj/item/clothing/under/owl + name = "owl uniform" + desc = "A soft brown jumpsuit made of synthetic feathers and strong conviction." + icon_state = "owl" + item_color = "owl" + +/obj/item/clothing/under/griffin + name = "griffon uniform" + desc = "A soft brown jumpsuit with a white feather collar made of synthetic feathers and a lust for mayhem." + icon_state = "griffin" + item_color = "griffin" + +/obj/item/clothing/under/noble_clothes + name = "noble clothes" + desc = "They fall just short of majestic." + icon_state = "noble_clothes" + item_color = "noble_clothes" + item_state = "noble_clothes" + +/obj/item/clothing/under/contortionist + name = "atmospheric technician's jumpsuit" + desc = "A light jumpsuit useful for squeezing through narrow vents." + icon_state = "atmos" + item_state = "atmos_suit" + item_color = "atmos" + resistance_flags = FIRE_PROOF + +/obj/item/clothing/under/contortionist/equipped(mob/living/carbon/human/user, slot) + if(!user.ventcrawler) + user.ventcrawler = 2 + ..() + +/obj/item/clothing/under/contortionist/dropped(mob/living/carbon/human/user) + if(!user.get_int_organ(/obj/item/organ/internal/heart/gland/ventcrawling)) + user.ventcrawler = 0 + ..() + +/obj/item/clothing/under/contortionist/proc/check_clothing(mob/user as mob) + //Allowed to wear: glasses, shoes, gloves, pockets, mask, and jumpsuit (obviously) + var/list/slot_must_be_empty = list(slot_back,slot_handcuffed,slot_legcuffed,slot_l_hand,slot_r_hand,slot_belt,slot_head,slot_wear_suit) + for(var/slot_id in slot_must_be_empty) + if(user.get_item_by_slot(slot_id)) + to_chat(user,"You can't fit inside while wearing that \the [user.get_item_by_slot(slot_id)].") + return 0 + return 1 + +/obj/item/clothing/under/cursedclown + name = "cursed clown suit" + desc = "It wasn't already?" + icon = 'icons/goonstation/objects/clothing/uniform.dmi' + icon_state = "cursedclown" + item_state = "cclown_uniform" + item_color = "cursedclown" + icon_override = 'icons/goonstation/mob/clothing/uniform.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + flags = NODROP + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + flags_size = ONESIZEFITSALL + has_sensor = 0 // HUNKE + +/obj/item/clothing/under/victdress + name = "black victorian dress" + desc = "A victorian style dress, fancy!" + icon_state = "victorianblackdress" + item_state = "victorianblackdress" + item_color = "victorianblackdress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/victdress/red + name = "red victorian dress" + icon_state = "victorianreddress" + item_state = "victorianreddress" + item_color = "victorianreddress" + +/obj/item/clothing/under/victsuit + name = "victorian suit" + desc = "A victorian style suit, fancy!" + icon_state = "victorianvest" + item_state = "victorianvest" + item_color = "victorianvest" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/victsuit/redblk + name = "red and black victorian suit" + icon_state = "victorianblred" + item_state = "victorianblred" + item_color = "victorianblred" + +/obj/item/clothing/under/victsuit/red + name = "red victorian suit" + icon_state = "victorianredvest" + item_state = "victorianredvest" + item_color = "victorianredvest" + +/obj/item/clothing/under/medigown + name = "medical gown" + desc = "a flimsy examination gown, the back ties never close." + icon_state = "medicalgown" + item_state = "medicalgown" + item_color = "medicalgown" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/burial + name = "burial garments" + desc = "Traditional burial garments from the early 22nd century." + icon_state = "burial" + item_state = "burial" + item_color = "burial" + +/obj/item/clothing/under/redhawaiianshirt + name = "red hawaiian shirt" + desc = "a floral shirt worn to most vacation destinations." + icon_state = "hawaiianred" + item_state = "hawaiianred" + item_color = "hawaiianred" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/pinkhawaiianshirt + name = "pink hawaiian shirt" + desc = "a pink floral shirt the material feels cool and comfy." + icon_state = "hawaiianpink" + item_state = "hawaiianpink" + item_color = "hawaiianpink" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/orangehawaiianshirt + name = "orange hawaiian shirt" + desc = "a orange floral shirt for a relaxing day in space." + icon_state = "hawaiianorange" + item_state = "hawaiianorange" + item_color = "hawaiianorange" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/bluehawaiianshirt + name = "blue hawaiian shirt" + desc = "a blue floral shirt it has a oddly colored pink flower on it." + icon_state = "hawaiianblue" + item_state = "hawaiianblue" + item_color = "hawaiianblue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/misc/durathread + name = "durathread jumpsuit" + desc = "A jumpsuit made from durathread, its resilient fibres provide some protection to the wearer." + icon_state = "durathread" + item_state = "durathread" + item_color = "durathread" + armor = list(melee = 10, bullet = 0, laser = 10, energy = 0, bomb = 5, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/under/cuban_suit + name = "rhumba outfit" + desc = "A satin shirt and high-waisted pants, worn by dancers in the Rhumba style. It smells oddly like... sulfur?" + icon_state = "cuban_suit" + item_state = "cuban_suit" + item_color = "cuban_suit" diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm index b52c1eceb7c6..735c681ca5f7 100644 --- a/code/modules/clothing/under/pants.dm +++ b/code/modules/clothing/under/pants.dm @@ -89,4 +89,4 @@ name = "camo pants" desc = "A pair of woodland camouflage pants. Probably not the best choice for a space station." icon_state = "camopants" - item_color = "camopants" \ No newline at end of file + item_color = "camopants" diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index 13008c4ff6d5..3cd2ddef84c8 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -1,26 +1,26 @@ -/obj/item/clothing/under/shorts - name = "athletic shorts" - desc = "95% Polyester, 5% Spandex!" - gender = PLURAL - body_parts_covered = LOWER_TORSO - displays_id = 0 - -/obj/item/clothing/under/shorts/red - icon_state = "redshorts" - item_color = "redshorts" - -/obj/item/clothing/under/shorts/green - icon_state = "greenshorts" - item_color = "greenshorts" - -/obj/item/clothing/under/shorts/blue - icon_state = "blueshorts" - item_color = "blueshorts" - -/obj/item/clothing/under/shorts/black - icon_state = "blackshorts" - item_color = "blackshorts" - -/obj/item/clothing/under/shorts/grey - icon_state = "greyshorts" - item_color = "greyshorts" \ No newline at end of file +/obj/item/clothing/under/shorts + name = "athletic shorts" + desc = "95% Polyester, 5% Spandex!" + gender = PLURAL + body_parts_covered = LOWER_TORSO + displays_id = 0 + +/obj/item/clothing/under/shorts/red + icon_state = "redshorts" + item_color = "redshorts" + +/obj/item/clothing/under/shorts/green + icon_state = "greenshorts" + item_color = "greenshorts" + +/obj/item/clothing/under/shorts/blue + icon_state = "blueshorts" + item_color = "blueshorts" + +/obj/item/clothing/under/shorts/black + icon_state = "blackshorts" + item_color = "blackshorts" + +/obj/item/clothing/under/shorts/grey + icon_state = "greyshorts" + item_color = "greyshorts" diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 6778138d30f4..441cf8d94203 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -1,26 +1,26 @@ -/obj/item/clothing/under/syndicate - name = "tactical turtleneck" - desc = "A non-descript and slightly suspicious looking turtleneck with digital camouflage cargo pants." - icon_state = "syndicate" - item_state = "bl_suit" - item_color = "syndicate" - has_sensor = 0 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) - -/obj/item/clothing/under/syndicate/combat - name = "combat turtleneck" - -/obj/item/clothing/under/syndicate/tacticool - name = "tacticool turtleneck" - desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." - icon_state = "tactifool" - item_state = "bl_suit" - item_color = "tactifool" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) - -/obj/item/clothing/under/syndicate/sniper - name = "Tactical turtleneck suit" - desc = "A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp." - icon_state = "really_black_suit" - item_state = "bl_suit" - item_color = "black_suit" \ No newline at end of file +/obj/item/clothing/under/syndicate + name = "tactical turtleneck" + desc = "A non-descript and slightly suspicious looking turtleneck with digital camouflage cargo pants." + icon_state = "syndicate" + item_state = "bl_suit" + item_color = "syndicate" + has_sensor = 0 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + +/obj/item/clothing/under/syndicate/combat + name = "combat turtleneck" + +/obj/item/clothing/under/syndicate/tacticool + name = "tacticool turtleneck" + desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." + icon_state = "tactifool" + item_state = "bl_suit" + item_color = "tactifool" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + +/obj/item/clothing/under/syndicate/sniper + name = "Tactical turtleneck suit" + desc = "A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp." + icon_state = "really_black_suit" + item_state = "bl_suit" + item_color = "black_suit" diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index 36f0da802ccb..ee2c9e5ba2c2 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -103,7 +103,7 @@ if(RC.is_drainable()) for(var/datum/reagent/A in RC.reagents.reagent_list) .["other"][A.type] += A.volume - .["other"][I.type] += 1 + .["other"][I.type] += 1 .["toolsother"][I] += 1 /datum/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents) @@ -135,13 +135,13 @@ /datum/personal_crafting/proc/check_pathtools(mob/user, datum/crafting_recipe/R, list/contents) if(!R.pathtools.len) //does not run if no tools are needed return TRUE - var/list/other_possible_tools = list() + var/list/other_possible_tools = list() for(var/obj/item/I in user.contents) // searchs the inventory of the mob if(istype(I, /obj/item/storage)) for(var/obj/item/SI in I.contents) other_possible_tools += SI.type // filters type paths other_possible_tools += I.type - + other_possible_tools |= contents["other"] // this adds contents to the other_possible_tools main_loop: // checks if all tools found are usable with the recipe for(var/A in R.pathtools) @@ -151,7 +151,7 @@ return FALSE return TRUE -/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R) +/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R) var/list/contents = get_surroundings(user) var/send_feedback = 1 if(check_contents(R, contents)) @@ -297,7 +297,7 @@ Deletion.Cut(Deletion.len) qdel(DL) -/datum/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = not_incapacitated_turf_state) +/datum/personal_crafting/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = GLOB.not_incapacitated_turf_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "personal_crafting.tmpl", "Crafting Menu", 700, 800, state = state) @@ -458,4 +458,4 @@ /datum/mind/proc/teach_crafting_recipe(R) if(!learned_recipes) learned_recipes = list() - learned_recipes |= R \ No newline at end of file + learned_recipes |= R diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 3a0bdb8d795f..983ce229d2cd 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -1405,9 +1405,9 @@ icon = 'icons/obj/custom_items.dmi' icon_state = "ssscratches_backpack" -/obj/item/storage/backpack/duffel/fluff/thebrew //Greey: Korala Ice +/obj/item/storage/backpack/fluff/thebrew //Greey: Korala Ice name = "The Brew" - desc = "Amber colored duffle bag resembling a long lost friend, a spirit long forgotten." + desc = "Amber colored backpack resembling a long lost friend, a spirit long forgotten." icon = 'icons/obj/custom_items.dmi' icon_state = "greeyfluff" item_state = "greeyfluff" diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm index 5c1250ed3555..c1491d5c0de1 100644 --- a/code/modules/customitems/item_spawning.dm +++ b/code/modules/customitems/item_spawning.dm @@ -3,7 +3,7 @@ return // Grab the info we want. - var/DBQuery/query = dbcon.NewQuery("SELECT cuiPath, cuiPropAdjust, cuiJobMask, cuiDescription, cuiItemName FROM [format_table_name("customuseritems")] WHERE cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*')") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT cuiPath, cuiPropAdjust, cuiJobMask, cuiDescription, cuiItemName FROM [format_table_name("customuseritems")] WHERE cuiCKey='[M.ckey]' AND (cuiRealName='[sanitizeSQL(M.real_name)]' OR cuiRealName='*')") query.Execute() while(query.NextRow()) diff --git a/code/modules/detective_work/scanner.dm b/code/modules/detective_work/scanner.dm index 643188778472..dea08c602f9a 100644 --- a/code/modules/detective_work/scanner.dm +++ b/code/modules/detective_work/scanner.dm @@ -30,21 +30,21 @@ // I really, really wish I didn't have to split this into two seperate loops. But the datacore is awful. - for(var/record in data_core.general) + for(var/record in GLOB.data_core.general) var/datum/data/record/S = record if(S && (search == lowertext(S.fields["fingerprint"]) || search == lowertext(S.fields["name"]))) name = S.fields["name"] fingerprint = S.fields["fingerprint"] continue - for(var/record in data_core.medical) + for(var/record in GLOB.data_core.medical) var/datum/data/record/M = record if(M && ( search == lowertext(M.fields["b_dna"]) || name == M.fields["name"]) ) dna = M.fields["b_dna"] if(fingerprint == "FINGERPRINT NOT FOUND") // We have searched by DNA, and do not have the relevant information from the fingerprint records. name = M.fields["name"] - for(var/gen_record in data_core.general) + for(var/gen_record in GLOB.data_core.general) var/datum/data/record/S = gen_record if(S && (name == S.fields["name"])) fingerprint = S.fields["fingerprint"] diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index df5429641639..ea0cd53a6c71 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -36,7 +36,7 @@ log transactions /obj/machinery/atm/New() ..() - machine_id = "[station_name()] RT #[num_financial_terminals++]" + machine_id = "[station_name()] RT #[GLOB.num_financial_terminals++]" /obj/machinery/atm/Initialize() ..() @@ -127,7 +127,7 @@ log transactions ui = new(user, src, ui_key, "atm.tmpl", name, 550, 650) ui.open() -/obj/machinery/atm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/atm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["src"] = UID() data["view_screen"] = view_screen @@ -203,7 +203,7 @@ log transactions T.target_name = failed_account.owner_name T.purpose = "Unauthorised login attempt" T.source_terminal = machine_id - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() failed_account.transaction_log.Add(T) else @@ -223,7 +223,7 @@ log transactions T.target_name = authenticated_account.owner_name T.purpose = "Remote terminal access" T.source_terminal = machine_id - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() authenticated_account.transaction_log.Add(T) to_chat(usr, "[bicon(src)]Access granted. Welcome user '[authenticated_account.owner_name].'") @@ -257,7 +257,7 @@ log transactions Account holder: [authenticated_account.owner_name]
    Account number: [authenticated_account.account_number]
    Balance: $[authenticated_account.money]
    - Date and time: [station_time_timestamp()], [current_date_string]

    + Date and time: [station_time_timestamp()], [GLOB.current_date_string]

    Service terminal ID: [machine_id]
    "} //stamp the paper diff --git a/code/modules/economy/Accounts.dm b/code/modules/economy/Accounts.dm index 6001cf3f84e0..0a2b500a0d34 100644 --- a/code/modules/economy/Accounts.dm +++ b/code/modules/economy/Accounts.dm @@ -4,33 +4,33 @@ #define STATION_SOURCE_TERMINAL "Biesel GalaxyNet Terminal #227" #define DEPARTMENT_START_CASH 5000 -var/global/num_financial_terminals = 1 -var/global/datum/money_account/station_account -var/global/list/datum/money_account/department_accounts = list() -var/global/next_account_number = 0 -var/global/obj/machinery/computer/account_database/centcomm_account_db -var/global/datum/money_account/vendor_account -var/global/list/all_money_accounts = list() +GLOBAL_VAR_INIT(num_financial_terminals, 1) +GLOBAL_DATUM(station_account, /datum/money_account) +GLOBAL_LIST_EMPTY(department_accounts) +GLOBAL_VAR_INIT(next_account_number, 0) +GLOBAL_DATUM(centcomm_account_db, /obj/machinery/computer/account_database) // this being an object hurts me deeply on the inside +GLOBAL_DATUM(vendor_account, /datum/money_account) +GLOBAL_LIST_EMPTY(all_money_accounts) /proc/create_station_account() - if(!station_account) - next_account_number = rand(111111, 999999) + if(!GLOB.station_account) + GLOB.next_account_number = rand(111111, 999999) - station_account = new() - station_account.owner_name = "[station_name()] Station Account" - station_account.account_number = rand(111111, 999999) - station_account.remote_access_pin = rand(1111, 111111) - station_account.money = STATION_START_CASH + GLOB.station_account = new() + GLOB.station_account.owner_name = "[station_name()] Station Account" + GLOB.station_account.account_number = rand(111111, 999999) + GLOB.station_account.remote_access_pin = rand(1111, 111111) + GLOB.station_account.money = STATION_START_CASH //create an entry in the account transaction log for when it was created - station_account.makeTransactionLog(STATION_START_CASH, "Account Creation", STATION_SOURCE_TERMINAL, station_account.owner_name, FALSE, + GLOB.station_account.makeTransactionLog(STATION_START_CASH, "Account Creation", STATION_SOURCE_TERMINAL, GLOB.station_account.owner_name, FALSE, STATION_CREATION_DATE, STATION_CREATION_TIME) //add the account - all_money_accounts.Add(station_account) + GLOB.all_money_accounts.Add(GLOB.station_account) /proc/create_department_account(department) - next_account_number = rand(111111, 999999) + GLOB.next_account_number = rand(111111, 999999) var/datum/money_account/department_account = new() department_account.owner_name = "[department] Account" @@ -43,9 +43,9 @@ var/global/list/all_money_accounts = list() STATION_CREATION_DATE, STATION_CREATION_TIME) //add the account - all_money_accounts.Add(department_account) + GLOB.all_money_accounts.Add(department_account) - department_accounts[department] = department_account + GLOB.department_accounts[department] = department_account //the current ingame time (hh:mm:ss) can be obtained by calling: //station_time_timestamp("hh:mm:ss") @@ -65,18 +65,18 @@ var/global/list/all_money_accounts = list() T.amount = starting_funds if(!source_db) //set a random date, time and location some time over the past few decades - T.date = "[num2text(rand(1,31))] [pick(GLOB.month_names)], [rand(game_year - 20,game_year - 1)]" + T.date = "[num2text(rand(1,31))] [pick(GLOB.month_names)], [rand(GLOB.game_year - 20,GLOB.game_year - 1)]" T.time = "[rand(0,23)]:[rand(0,59)]:[rand(0,59)]" T.source_terminal = "NTGalaxyNet Terminal #[rand(111,1111)]" M.account_number = rand(111111, 999999) else - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() T.source_terminal = source_db.machine_id - M.account_number = next_account_number - next_account_number += rand(1,25) + M.account_number = GLOB.next_account_number + GLOB.next_account_number += rand(1,25) //create a sealed package containing the account details var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(source_db.loc) @@ -94,7 +94,7 @@ var/global/list/all_money_accounts = list() Account number: [M.account_number]
    Account pin: [M.remote_access_pin]
    Starting balance: $[M.money]
    - Date and time: [station_time_timestamp()], [current_date_string]

    + Date and time: [station_time_timestamp()], [GLOB.current_date_string]

    Creation terminal ID: [source_db.machine_id]
    Authorised NT officer overseeing creation: [overseer]
    "} @@ -109,7 +109,7 @@ var/global/list/all_money_accounts = list() //add the account M.transaction_log.Add(T) - all_money_accounts.Add(M) + GLOB.all_money_accounts.Add(M) return M @@ -138,7 +138,7 @@ var/global/list/all_money_accounts = list() /obj/machinery/computer/account_database/proc/charge_to_account(attempt_account_number, datum/money_account/source, purpose, terminal_id, amount) if(!activated) return 0 - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == attempt_account_number && !D.suspended) source.charge(amount, D, purpose, terminal_id, "Account #[D.account_number]", "Transfer from [source.owner_name]", "[D.owner_name]") @@ -148,18 +148,18 @@ var/global/list/all_money_accounts = list() //this returns the first account datum that matches the supplied accnum/pin combination, it returns null if the combination did not match any account /proc/attempt_account_access(var/attempt_account_number, var/attempt_pin_number, var/security_level_passed = 0,var/pin_needed=1) - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == attempt_account_number) if( D.security_level <= security_level_passed && (!D.security_level || D.remote_access_pin == attempt_pin_number || !pin_needed) ) return D /obj/machinery/computer/account_database/proc/get_account(var/account_number) - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == account_number) return D /proc/attempt_account_access_nosec(var/attempt_account_number) - for(var/datum/money_account/D in all_money_accounts) + for(var/datum/money_account/D in GLOB.all_money_accounts) if(D.account_number == attempt_account_number) return D diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index afb5fbff79af..3065ba1c0fd1 100644 --- a/code/modules/economy/Accounts_DB.dm +++ b/code/modules/economy/Accounts_DB.dm @@ -1,4 +1,4 @@ -var/global/current_date_string +GLOBAL_VAR(current_date_string) /obj/machinery/computer/account_database name = "Accounts Uplink Terminal" @@ -17,20 +17,20 @@ var/global/current_date_string light_color = LIGHT_COLOR_GREEN /obj/machinery/computer/account_database/New() - if(!station_account) + if(!GLOB.station_account) create_station_account() - if(department_accounts.len == 0) - for(var/department in station_departments) + if(GLOB.department_accounts.len == 0) + for(var/department in GLOB.station_departments) create_department_account(department) - if(!vendor_account) + if(!GLOB.vendor_account) create_department_account("Vendor") - vendor_account = department_accounts["Vendor"] + GLOB.vendor_account = GLOB.department_accounts["Vendor"] - if(!current_date_string) - current_date_string = "[time2text(world.timeofday, "DD Month")], [game_year]" + if(!GLOB.current_date_string) + GLOB.current_date_string = "[time2text(world.timeofday, "DD Month")], [GLOB.game_year]" - machine_id = "[station_name()] Acc. DB #[num_financial_terminals++]" + machine_id = "[station_name()] Acc. DB #[GLOB.num_financial_terminals++]" ..() /obj/machinery/computer/account_database/proc/get_access_level(var/mob/user) @@ -74,7 +74,7 @@ var/global/current_date_string ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640) ui.open() -/obj/machinery/computer/account_database/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/account_database/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["src"] = UID() @@ -84,7 +84,7 @@ var/global/current_date_string data["machine_id"] = machine_id data["creating_new_account"] = creating_new_account data["detailed_account_view"] = !!detailed_account_view - data["station_account_number"] = station_account.account_number + data["station_account_number"] = GLOB.station_account.account_number data["transactions"] = null data["accounts"] = null @@ -108,8 +108,8 @@ var/global/current_date_string data["transactions"] = trx var/list/accounts[0] - for(var/i=1, i<=all_money_accounts.len, i++) - var/datum/money_account/D = all_money_accounts[i] + for(var/i=1, i<=GLOB.all_money_accounts.len, i++) + var/datum/money_account/D = GLOB.all_money_accounts[i] accounts.Add(list(list(\ "account_number"=D.account_number,\ "owner_name"=D.owner_name,\ @@ -159,12 +159,12 @@ var/global/current_date_string var/account_name = href_list["holder_name"] var/starting_funds = max(text2num(href_list["starting_funds"]), 0) - starting_funds = Clamp(starting_funds, 0, station_account.money) // Not authorized to put the station in debt. + starting_funds = Clamp(starting_funds, 0, GLOB.station_account.money) // Not authorized to put the station in debt. starting_funds = min(starting_funds, fund_cap) // Not authorized to give more than the fund cap. var/datum/money_account/M = create_account(account_name, starting_funds, src) if(starting_funds > 0) - station_account.charge(starting_funds, null, "New account activation", + GLOB.station_account.charge(starting_funds, null, "New account activation", "", "New account activation", M.owner_name) creating_new_account = 0 @@ -174,8 +174,8 @@ var/global/current_date_string if("view_account_detail") var/index = text2num(href_list["account_index"]) - if(index && index <= all_money_accounts.len) - detailed_account_view = all_money_accounts[index] + if(index && index <= GLOB.all_money_accounts.len) + detailed_account_view = GLOB.all_money_accounts[index] if("view_accounts_list") detailed_account_view = null @@ -240,8 +240,8 @@ var/global/current_date_string
    "} - for(var/i=1, i<=all_money_accounts.len, i++) - var/datum/money_account/D = all_money_accounts[i] + for(var/i=1, i<=GLOB.all_money_accounts.len, i++) + var/datum/money_account/D = GLOB.all_money_accounts[i] text += {" diff --git a/code/modules/economy/EFTPOS.dm b/code/modules/economy/EFTPOS.dm index 9a5faf8aa360..2622d06411f6 100644 --- a/code/modules/economy/EFTPOS.dm +++ b/code/modules/economy/EFTPOS.dm @@ -14,7 +14,7 @@ /obj/item/eftpos/New() ..() - machine_name = "[station_name()] EFTPOS #[num_financial_terminals++]" + machine_name = "[station_name()] EFTPOS #[GLOB.num_financial_terminals++]" access_code = rand(1111,111111) reconnect_database() spawn(0) @@ -22,7 +22,7 @@ //by default, connect to the station account //the user of the EFTPOS device can change the target account though, and no-one will be the wiser (except whoever's being charged) - linked_account = station_account + linked_account = GLOB.station_account /obj/item/eftpos/proc/print_reference() playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1) @@ -79,7 +79,7 @@ ui = new(user, src, ui_key, "eftpos.tmpl", name, 790, 310) ui.open() -/obj/item/eftpos/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/item/eftpos/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["machine_name"] = machine_name data["transaction_locked"] = transaction_locked diff --git a/code/modules/economy/Economy.dm b/code/modules/economy/Economy.dm index 57d76e98c5c2..8f06db02f348 100644 --- a/code/modules/economy/Economy.dm +++ b/code/modules/economy/Economy.dm @@ -63,9 +63,9 @@ //Destroyers are medium sized vessels, often used for escorting larger ships but able to go toe-to-toe with them if need be. //Frigates are medium sized vessels, often used for escorting larger ships. They will rapidly find themselves outclassed if forced to face heavy warships head on. -var/setup_economy = 0 +GLOBAL_VAR_INIT(setup_economy, 0) /proc/setup_economy() - if(setup_economy) + if(GLOB.setup_economy) return var/datum/feed_channel/newChannel = new /datum/feed_channel @@ -73,25 +73,25 @@ var/setup_economy = 0 newChannel.author = "Automated Announcement Listing" newChannel.locked = 1 newChannel.is_admin_channel = 1 - news_network.network_channels += newChannel + GLOB.news_network.network_channels += newChannel newChannel = new /datum/feed_channel newChannel.channel_name = "Nyx Daily" newChannel.author = "CentComm Minister of Information" newChannel.locked = 1 newChannel.is_admin_channel = 1 - news_network.network_channels += newChannel + GLOB.news_network.network_channels += newChannel newChannel = new /datum/feed_channel newChannel.channel_name = "The Gibson Gazette" newChannel.author = "Editor Mike Hammers" newChannel.locked = 1 newChannel.is_admin_channel = 1 - news_network.network_channels += newChannel + GLOB.news_network.network_channels += newChannel for(var/loc_type in subtypesof(/datum/trade_destination)) var/datum/trade_destination/D = new loc_type - weighted_randomevent_locations[D] = D.viable_random_events.len - weighted_mundaneevent_locations[D] = D.viable_mundane_events.len + GLOB.weighted_randomevent_locations[D] = D.viable_random_events.len + GLOB.weighted_mundaneevent_locations[D] = D.viable_mundane_events.len - setup_economy = 1 \ No newline at end of file + GLOB.setup_economy = 1 diff --git a/code/modules/economy/Economy_Events.dm b/code/modules/economy/Economy_Events.dm index 10e0d574f983..d7754f122ea4 100644 --- a/code/modules/economy/Economy_Events.dm +++ b/code/modules/economy/Economy_Events.dm @@ -8,10 +8,10 @@ var/datum/trade_destination/affected_dest /datum/event/economic_event/start() - if(!setup_economy) + if(!GLOB.setup_economy) setup_economy() - affected_dest = pickweight(weighted_randomevent_locations) + affected_dest = pickweight(GLOB.weighted_randomevent_locations) if(affected_dest.viable_random_events.len) endWhen = rand(60,300) event_type = pick(affected_dest.viable_random_events) @@ -94,11 +94,11 @@ if(FESTIVAL) newMsg.body = "A [pick("festival","week long celebration","day of revelry","planet-wide holiday")] has been declared on [affected_dest.name] by [pick("Governor","Commissioner","General","Commandant","Administrator")] [random_name(pick(MALE,FEMALE))] to celebrate [pick("the birth of their [pick("son","daughter")]","coming of age of their [pick("son","daughter")]","the pacification of rogue military cell","the apprehension of a violent criminal who had been terrorising the planet")]. Massive stocks of food and meat have been bought driving up prices across the planet." - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == "Nyx Daily") FC.messages += newMsg break - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert("Nyx Daily") /datum/event/economic_event/end() diff --git a/code/modules/economy/Economy_Events_Mundane.dm b/code/modules/economy/Economy_Events_Mundane.dm index 1b5467a3a1c8..cfabd6cdbe76 100644 --- a/code/modules/economy/Economy_Events_Mundane.dm +++ b/code/modules/economy/Economy_Events_Mundane.dm @@ -3,7 +3,7 @@ endWhen = 10 /datum/event/mundane_news/announce() - var/datum/trade_destination/affected_dest = pickweight(weighted_mundaneevent_locations) + var/datum/trade_destination/affected_dest = pickweight(GLOB.weighted_mundaneevent_locations) var/event_type = 0 if(affected_dest.viable_mundane_events.len) event_type = pick(affected_dest.viable_mundane_events) @@ -126,11 +126,11 @@ Nyx Daily is offering discount tickets for two to see [random_name(pick(MALE,FEMALE))] live in return for eyewitness reports and up to the minute coverage." - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == "Nyx Daily") FC.messages += newMsg break - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert("Nyx Daily") /datum/event/trivial_news @@ -141,13 +141,13 @@ var/datum/feed_message/newMsg = new /datum/feed_message newMsg.author = "Editor Mike Hammers" //newMsg.is_admin_message = 1 - var/datum/trade_destination/affected_dest = pick(weighted_mundaneevent_locations) + var/datum/trade_destination/affected_dest = pick(GLOB.weighted_mundaneevent_locations) newMsg.body = pick(file2list("config/news/trivial.txt")) newMsg.body = replacetext(newMsg.body,"{{AFFECTED}}",affected_dest.name) - for(var/datum/feed_channel/FC in news_network.network_channels) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) if(FC.channel_name == "The Gibson Gazette") FC.messages += newMsg break - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) NEWSCASTER.newsAlert("The Gibson Gazette") diff --git a/code/modules/economy/Economy_TradeDestinations.dm b/code/modules/economy/Economy_TradeDestinations.dm index 09bbabdab374..74d005e3b3b5 100644 --- a/code/modules/economy/Economy_TradeDestinations.dm +++ b/code/modules/economy/Economy_TradeDestinations.dm @@ -1,6 +1,6 @@ -var/list/weighted_randomevent_locations = list() -var/list/weighted_mundaneevent_locations = list() +GLOBAL_LIST_EMPTY(weighted_randomevent_locations) +GLOBAL_LIST_EMPTY(weighted_mundaneevent_locations) /datum/trade_destination var/name = "" diff --git a/code/modules/economy/Job_Departments.dm b/code/modules/economy/Job_Departments.dm index 0b8511361474..f2accab8541d 100644 --- a/code/modules/economy/Job_Departments.dm +++ b/code/modules/economy/Job_Departments.dm @@ -1,4 +1,4 @@ -var/list/station_departments = list("Command", "Medical", "Engineering", "Science", "Security", "Cargo", "Support", "Civilian") +GLOBAL_LIST_INIT(station_departments, list("Command", "Medical", "Engineering", "Science", "Security", "Cargo", "Support", "Civilian")) // The department the job belongs to. /datum/job/var/department = null @@ -67,4 +67,4 @@ var/list/station_departments = list("Command", "Medical", "Engineering", "Scienc /datum/job/detective/department = "Security" -/datum/job/officer/department = "Security" \ No newline at end of file +/datum/job/officer/department = "Security" diff --git a/code/modules/economy/POS.dm b/code/modules/economy/POS.dm index 201c78478751..60b774a2a3da 100644 --- a/code/modules/economy/POS.dm +++ b/code/modules/economy/POS.dm @@ -11,8 +11,8 @@ var/price = 0 // Per unit var/units = 0 -var/global/current_pos_id = 1 -var/global/pos_sales = 0 +GLOBAL_VAR_INIT(current_pos_id, 1) +GLOBAL_VAR_INIT(pos_sales, 0) #define RECEIPT_HEADER {" @@ -142,11 +142,11 @@ var/global/pos_sales = 0 /obj/machinery/pos/New() ..() - id = current_pos_id++ + id = GLOB.current_pos_id++ if(department) - linked_account = department_accounts[department] + linked_account = GLOB.department_accounts[department] else - linked_account = station_account + linked_account = GLOB.station_account update_icon() /obj/machinery/pos/proc/AddToOrder(var/name, var/units) @@ -173,7 +173,7 @@ var/global/pos_sales = 0 receipt += myArea.name receipt += "" receipt += {"
    -
    [station_time_timestamp()], [current_date_string]
    +
    [station_time_timestamp()], [GLOB.current_date_string]
    - +
    @@ -99,7 +99,7 @@ function onload() { src << browse(output, "window=createplayerpoll;size=950x500") /client/proc/create_poll_function(href_list) - if(!check_rights(R_SERVER)) + if(!check_rights(R_SERVER)) return var/polltype = href_list["createpoll"] if(polltype != POLLTYPE_OPTION && polltype != POLLTYPE_TEXT && polltype != POLLTYPE_RATING && polltype != POLLTYPE_MULTI) @@ -122,15 +122,15 @@ function onload() { create_poll_window("Question cannot be blank.") return question = sanitizeSQL(question) - + var/starttime var/endtime - var/DBQuery/query = dbcon.NewQuery("SELECT Now() AS starttime, ADDDATE(Now(), INTERVAL [poll_len] DAY) AS endtime") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT Now() AS starttime, ADDDATE(Now(), INTERVAL [poll_len] DAY) AS endtime") query.Execute() while(query.NextRow()) starttime = query.item[1] endtime = query.item[2] - + var/pollquery = "INSERT INTO [format_table_name("poll_question")] (polltype, starttime, endtime, question, adminonly, multiplechoiceoptions, createdby_ckey, createdby_ip) VALUES ('[polltype]', '[starttime]', '[endtime]', '[question]', '[adminonly]', '[choice_amount]', '[sql_ckey]', '[address]')" var/idquery = "SELECT id FROM [format_table_name("poll_question")] WHERE question = '[question]' AND starttime = '[starttime]' AND endtime = '[endtime]' AND createdby_ckey = '[sql_ckey]' AND createdby_ip = '[address]'" var/list/option_queries = list() @@ -175,15 +175,15 @@ function onload() { if(descmax) descmax = sanitizeSQL(descmax) option_queries += "INSERT INTO [format_table_name("poll_option")] (pollid, text, percentagecalc, minval, maxval, descmin, descmid, descmax) VALUES ('{POLLID}', '[option]', '[percentagecalc]', '[minval]', '[maxval]', '[descmin]', '[descmid]', '[descmax]')" - - query = dbcon.NewQuery(pollquery) + + query = GLOB.dbcon.NewQuery(pollquery) if(!query.Execute()) var/err = query.ErrorMsg() create_poll_window("An SQL error has occured while creating your poll") log_game("SQL ERROR adding new poll question to table. Error : \[[err]\]\n") return var/pollid = 0 - query = dbcon.NewQuery(idquery) + query = GLOB.dbcon.NewQuery(idquery) if(!query.Execute()) var/err = query.ErrorMsg() create_poll_window("An SQL error has occured while creating your poll") @@ -192,11 +192,11 @@ function onload() { if(query.NextRow()) pollid = text2num(query.item[1]) for(var/querytext in option_queries) - query = dbcon.NewQuery(replacetext(idquery, "{POLLID}", pollid)) + query = GLOB.dbcon.NewQuery(replacetext(idquery, "{POLLID}", pollid)) if(!query.Execute()) var/err = query.ErrorMsg() create_poll_window("An SQL error has occured while creating your poll") log_game("SQL ERROR obtaining id from poll_question table. Error : \[[err]\]\n") return create_poll_window("Your poll has been successfully created") - return pollid \ No newline at end of file + return pollid diff --git a/code/modules/admin/create_turf.dm b/code/modules/admin/create_turf.dm index 8d1f126a7ec5..c65c7b22b1aa 100644 --- a/code/modules/admin/create_turf.dm +++ b/code/modules/admin/create_turf.dm @@ -1,9 +1,9 @@ -/var/create_turf_html = null -/datum/admins/proc/create_turf(var/mob/user) - if(!create_turf_html) - var/turfjs = null - turfjs = jointext(typesof(/turf), ";") - create_turf_html = file2text('html/create_object.html') - create_turf_html = replacetext(create_turf_html, "null /* object types */", "\"[turfjs]\"") - - user << browse(replacetext(create_turf_html, "/* ref src */", UID()), "window=create_turf;size=425x475") +GLOBAL_VAR(create_turf_html) +/datum/admins/proc/create_turf(var/mob/user) + if(!GLOB.create_turf_html) + var/turfjs = null + turfjs = jointext(typesof(/turf), ";") + GLOB.create_turf_html = file2text('html/create_object.html') + GLOB.create_turf_html = replacetext(GLOB.create_turf_html, "null /* object types */", "\"[turfjs]\"") + + user << browse(replacetext(GLOB.create_turf_html, "/* ref src */", UID()), "window=create_turf;size=425x475") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index ebf8a70cde32..740d182f3f23 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -1,108 +1,109 @@ -var/list/admin_datums = list() - -/datum/admins - var/rank = "Temporary Admin" - var/client/owner = null - var/rights = 0 - var/fakekey = null - var/big_brother = 0 - - var/datum/marked_datum - - var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description - var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders. - var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel - var/admincaster_signature //What you'll sign the newsfeeds as - -/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey) - if(!ckey) - error("Admin datum created without a ckey argument. Datum has been deleted") - qdel(src) - return - admincaster_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" - rank = initial_rank - rights = initial_rights - admin_datums[ckey] = src - -/datum/admins/Destroy() - ..() - return QDEL_HINT_HARDDEL_NOW - -/datum/admins/proc/associate(client/C) - if(istype(C)) - owner = C - owner.holder = src - owner.on_holder_add() - owner.add_admin_verbs() //TODO - owner.verbs -= /client/proc/readmin - GLOB.admins |= C - -/datum/admins/proc/disassociate() - if(owner) - GLOB.admins -= owner - owner.remove_admin_verbs() - owner.holder = null - owner = null - -/* -checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) -if rights_required == 0, then it simply checks if they are an admin. -if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed -generally it would be used like so: - -proc/admin_proc() - if(!check_rights(R_ADMIN)) return - to_chat(world, "you have enough rights!") - -NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call -you will have to do something like if(client.holder.rights & R_ADMIN) yourself. -*/ -/proc/check_rights(rights_required, show_msg=1, var/mob/user = usr) - if(user && user.client) - if(rights_required) - if(user.client.holder) - if(rights_required & user.client.holder.rights) - return 1 - else - if(show_msg) - to_chat(user, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") - else - if(user.client.holder) - return 1 - else - if(show_msg) - to_chat(user, "Error: You are not an admin.") - return 0 - -//probably a bit iffy - will hopefully figure out a better solution -/proc/check_if_greater_rights_than(client/other) - if(usr && usr.client) - if(usr.client.holder) - if(!other || !other.holder) - return 1 - if(usr.client.holder.rights != other.holder.rights) - if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) - return 1 //we have all the rights they have and more - to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") - return 0 - -/client/proc/deadmin() - admin_datums -= ckey - if(holder) - holder.disassociate() - qdel(holder) - return 1 - -//This proc checks whether subject has at least ONE of the rights specified in rights_required. -/proc/check_rights_for(client/subject, rights_required) - if(subject && subject.holder) - if(rights_required && !(rights_required & subject.holder.rights)) - return 0 - return 1 - return 0 - -/datum/admins/vv_edit_var(var_name, var_value) - return FALSE // no admin abuse - -/datum/admins/can_vv_delete() - return FALSE // don't break shit either \ No newline at end of file +GLOBAL_LIST_EMPTY(admin_datums) +GLOBAL_PROTECT(admin_datums) // This is protected because we dont want people making their own admin ranks, for obvious reasons + +/datum/admins + var/rank = "Temporary Admin" + var/client/owner = null + var/rights = 0 + var/fakekey = null + var/big_brother = 0 + + var/datum/marked_datum + + var/admincaster_screen = 0 //See newscaster.dm under machinery for a full description + var/datum/feed_message/admincaster_feed_message = new /datum/feed_message //These two will act as holders. + var/datum/feed_channel/admincaster_feed_channel = new /datum/feed_channel + var/admincaster_signature //What you'll sign the newsfeeds as + +/datum/admins/New(initial_rank = "Temporary Admin", initial_rights = 0, ckey) + if(!ckey) + error("Admin datum created without a ckey argument. Datum has been deleted") + qdel(src) + return + admincaster_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" + rank = initial_rank + rights = initial_rights + GLOB.admin_datums[ckey] = src + +/datum/admins/Destroy() + ..() + return QDEL_HINT_HARDDEL_NOW + +/datum/admins/proc/associate(client/C) + if(istype(C)) + owner = C + owner.holder = src + owner.on_holder_add() + owner.add_admin_verbs() //TODO + owner.verbs -= /client/proc/readmin + GLOB.admins |= C + +/datum/admins/proc/disassociate() + if(owner) + GLOB.admins -= owner + owner.remove_admin_verbs() + owner.holder = null + owner = null + +/* +checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) +if rights_required == 0, then it simply checks if they are an admin. +if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed +generally it would be used like so: + +proc/admin_proc() + if(!check_rights(R_ADMIN)) return + to_chat(world, "you have enough rights!") + +NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call +you will have to do something like if(client.holder.rights & R_ADMIN) yourself. +*/ +/proc/check_rights(rights_required, show_msg=1, var/mob/user = usr) + if(user && user.client) + if(rights_required) + if(user.client.holder) + if(rights_required & user.client.holder.rights) + return 1 + else + if(show_msg) + to_chat(user, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].") + else + if(user.client.holder) + return 1 + else + if(show_msg) + to_chat(user, "Error: You are not an admin.") + return 0 + +//probably a bit iffy - will hopefully figure out a better solution +/proc/check_if_greater_rights_than(client/other) + if(usr && usr.client) + if(usr.client.holder) + if(!other || !other.holder) + return 1 + if(usr.client.holder.rights != other.holder.rights) + if( (usr.client.holder.rights & other.holder.rights) == other.holder.rights ) + return 1 //we have all the rights they have and more + to_chat(usr, "Error: Cannot proceed. They have more or equal rights to us.") + return 0 + +/client/proc/deadmin() + GLOB.admin_datums -= ckey + if(holder) + holder.disassociate() + qdel(holder) + return 1 + +//This proc checks whether subject has at least ONE of the rights specified in rights_required. +/proc/check_rights_for(client/subject, rights_required) + if(subject && subject.holder) + if(rights_required && !(rights_required & subject.holder.rights)) + return 0 + return 1 + return 0 + +/datum/admins/vv_edit_var(var_name, var_value) + return FALSE // no admin abuse + +/datum/admins/can_vv_delete() + return FALSE // don't break shit either diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm index 75f2422681a9..8bfce6c05f09 100644 --- a/code/modules/admin/ipintel.dm +++ b/code/modules/admin/ipintel.dm @@ -34,9 +34,9 @@ cachedintel.cache = TRUE return cachedintel - if(dbcon.IsConnected()) + if(GLOB.dbcon.IsConnected()) var/rating_bad = config.ipintel_rating_bad - var/DBQuery/query_get_ip_intel = dbcon.NewQuery({" + var/DBQuery/query_get_ip_intel = GLOB.dbcon.NewQuery({" SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW()) FROM [format_table_name("ipintel")] WHERE @@ -67,8 +67,8 @@ res.intel = ip_intel_query(ip) if(updatecache && res.intel >= 0) SSipintel.cache[ip] = res - if(dbcon.IsConnected()) - var/DBQuery/query_add_ip_intel = dbcon.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()") + if(GLOB.dbcon.IsConnected()) + var/DBQuery/query_add_ip_intel = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("ipintel")] (ip, intel) VALUES (INET_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()") query_add_ip_intel.Execute() qdel(query_add_ip_intel) @@ -139,7 +139,7 @@ return FALSE if(!config.ipintel_whitelist) return FALSE - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) return FALSE if(!ipintel_badip_check(t_ip)) return FALSE @@ -160,7 +160,7 @@ rating_bad = sanitizeSQL(rating_bad) valid_hours = sanitizeSQL(valid_hours) var/check_sql = {"SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON('[target_ip]') AND intel >= [rating_bad] AND (date + INTERVAL [valid_hours] HOUR) > NOW()"} - var/DBQuery/query_get_ip_intel = dbcon.NewQuery(check_sql) + var/DBQuery/query_get_ip_intel = GLOB.dbcon.NewQuery(check_sql) if(!query_get_ip_intel.Execute()) log_debug("ipintel_badip_check reports failed query execution") qdel(query_get_ip_intel) @@ -175,7 +175,7 @@ if(!config.ipintel_whitelist) return FALSE var/target_sql_ckey = ckey(target_ckey) - var/DBQuery/query_whitelist_check = dbcon.NewQuery("SELECT * FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_whitelist_check = GLOB.dbcon.NewQuery("SELECT * FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") if(!query_whitelist_check.Execute()) var/err = query_whitelist_check.ErrorMsg() log_debug("SQL ERROR on proc/vpn_whitelist_check for ckey '[target_sql_ckey]'. Error : \[[err]\]\n") @@ -190,7 +190,7 @@ if(!reason_string) return FALSE reason_string = sanitizeSQL(reason_string) - var/DBQuery/query_whitelist_add = dbcon.NewQuery("INSERT INTO [format_table_name("vpn_whitelist")] (ckey,reason) VALUES ('[target_sql_ckey]','[reason_string]')") + var/DBQuery/query_whitelist_add = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("vpn_whitelist")] (ckey,reason) VALUES ('[target_sql_ckey]','[reason_string]')") if(!query_whitelist_add.Execute()) var/err = query_whitelist_add.ErrorMsg() log_debug("SQL ERROR on proc/vpn_whitelist_add for ckey '[target_sql_ckey]'. Error : \[[err]\]\n") @@ -199,7 +199,7 @@ /proc/vpn_whitelist_remove(target_ckey) var/target_sql_ckey = ckey(target_ckey) - var/DBQuery/query_whitelist_remove = dbcon.NewQuery("DELETE FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_whitelist_remove = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("vpn_whitelist")] WHERE ckey = '[target_sql_ckey]'") if(!query_whitelist_remove.Execute()) var/err = query_whitelist_remove.ErrorMsg() log_debug("SQL ERROR on proc/vpn_whitelist_remove for ckey '[target_sql_ckey]'. Error : \[[err]\]\n") @@ -225,4 +225,4 @@ if(vpn_whitelist_add(target_ckey)) to_chat(usr, "[target_ckey] was added to the VPN whitelist.") else - to_chat(usr, "VPN whitelist unchanged.") \ No newline at end of file + to_chat(usr, "VPN whitelist unchanged.") diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index 57d585334376..795538c7c33f 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -1,170 +1,170 @@ -/client/proc/edit_admin_permissions() - set category = "Admin" - set name = "Permissions Panel" - set desc = "Edit admin permissions" - if(!check_rights(R_PERMISSIONS)) - return - usr.client.holder.edit_admin_permissions() - -/datum/admins/proc/edit_admin_permissions() - if(!check_rights(R_PERMISSIONS)) - return - - var/output = {" - - -Permissions Panel - - - - -
    - - - - -"} - - for(var/adm_ckey in admin_datums) - var/datum/admins/D = admin_datums[adm_ckey] - if(!D) continue - var/rank = D.rank ? D.rank : "*none*" - var/rights = rights2text(D.rights," ") - if(!rights) rights = "*none*" - output += {" - - - -"} - - /*output += "" - output += "" - output += "" - output += "" - output += ""*/ - - output += {" -
    CKEY \[+\]RANKPERMISSIONS
    [adm_ckey] \[-\][rank][rights]
    [adm_ckey] \[-\][rank][rights]
    -
    Search:
    - -"} - - usr << browse(output,"window=editrights;size=600x500") - -/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!check_rights(R_PERMISSIONS)) - return - - establish_db_connection() - - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_rank) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(!istext(adm_ckey) || !istext(new_rank)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/new_admin = 1 - var/admin_id - while(select_query.NextRow()) - new_admin = 0 - admin_id = text2num(select_query.item[1]) - - flag_account_for_forum_sync(adm_ckey) - if(new_admin) - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin")] (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") - log_query.Execute() - to_chat(usr, "New admin added.") - else - if(!isnull(admin_id) && isnum(admin_id)) - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") - log_query.Execute() - to_chat(usr, "Admin rank changed.") - -/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) - if(config.admin_legacy_system) - return - - if(!usr.client) - return - - if(!check_rights(R_PERMISSIONS)) - return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_permission) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(istext(new_permission)) - new_permission = text2num(new_permission) - - if(!istext(adm_ckey) || !isnum(new_permission)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/admin_id - var/admin_rights - while(select_query.NextRow()) - admin_id = text2num(select_query.item[1]) - admin_rights = text2num(select_query.item[2]) - - if(!admin_id) - return - - flag_account_for_forum_sync(adm_ckey) - if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") - log_query.Execute() - to_chat(usr, "Permission removed.") - else //This admin doesn't have this permission, so we are adding it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") - log_query.Execute() - to_chat(usr, "Permission added.") - -/datum/admins/proc/updateranktodb(ckey,newrank) - establish_db_connection() - if(!dbcon.IsConnected()) - return - if(!check_rights(R_PERMISSIONS)) - return - var/sql_ckey = sanitizeSQL(ckey) - var/sql_admin_rank = sanitizeSQL(newrank) - - var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") - query_update.Execute() - flag_account_for_forum_sync(sql_ckey) \ No newline at end of file +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + if(!check_rights(R_PERMISSIONS)) + return + usr.client.holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!check_rights(R_PERMISSIONS)) + return + + var/output = {" + + +Permissions Panel + + + + +
    + + + + +"} + + for(var/adm_ckey in GLOB.admin_datums) + var/datum/admins/D = GLOB.admin_datums[adm_ckey] + if(!D) continue + var/rank = D.rank ? D.rank : "*none*" + var/rights = rights2text(D.rights," ") + if(!rights) rights = "*none*" + output += {" + + + +"} + + /*output += "" + output += "" + output += "" + output += "" + output += ""*/ + + output += {" +
    CKEY \[+\]RANKPERMISSIONS
    [adm_ckey] \[-\][rank][rights]
    [adm_ckey] \[-\][rank][rights]
    +
    Search:
    + +"} + + usr << browse(output,"window=editrights;size=600x500") + +/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!check_rights(R_PERMISSIONS)) + return + + establish_db_connection() + + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + if(!adm_ckey || !new_rank) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(!istext(adm_ckey) || !istext(new_rank)) + return + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/new_admin = 1 + var/admin_id + while(select_query.NextRow()) + new_admin = 0 + admin_id = text2num(select_query.item[1]) + + flag_account_for_forum_sync(adm_ckey) + if(new_admin) + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin")] (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") + log_query.Execute() + to_chat(usr, "New admin added.") + else + if(!isnull(admin_id) && isnum(admin_id)) + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") + log_query.Execute() + to_chat(usr, "Admin rank changed.") + +/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) + if(config.admin_legacy_system) + return + + if(!usr.client) + return + + if(!check_rights(R_PERMISSIONS)) + return + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + if(!adm_ckey || !new_permission) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(istext(new_permission)) + new_permission = text2num(new_permission) + + if(!istext(adm_ckey) || !isnum(new_permission)) + return + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/admin_id + var/admin_rights + while(select_query.NextRow()) + admin_id = text2num(select_query.item[1]) + admin_rights = text2num(select_query.item[2]) + + if(!admin_id) + return + + flag_account_for_forum_sync(adm_ckey) + if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") + log_query.Execute() + to_chat(usr, "Permission removed.") + else //This admin doesn't have this permission, so we are adding it. + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") + log_query.Execute() + to_chat(usr, "Permission added.") + +/datum/admins/proc/updateranktodb(ckey,newrank) + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + return + if(!check_rights(R_PERMISSIONS)) + return + var/sql_ckey = sanitizeSQL(ckey) + var/sql_admin_rank = sanitizeSQL(newrank) + + var/DBQuery/query_update = GLOB.dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") + query_update.Execute() + flag_account_for_forum_sync(sql_ckey) diff --git a/code/modules/admin/player_panel.dm b/code/modules/admin/player_panel.dm index 9ed6f0cd9974..c4313354ff11 100644 --- a/code/modules/admin/player_panel.dm +++ b/code/modules/admin/player_panel.dm @@ -465,7 +465,7 @@ if(GAMEMODE_IS_BLOB) var/datum/game_mode/blob/mode = SSticker.mode dat += "
    " - dat += "" + dat += "" for(var/datum/mind/blob in mode.infected_crew) var/mob/M = blob.current @@ -475,7 +475,7 @@ else dat += "" dat += "
    Blob
    Progress: [blobs.len]/[mode.blobwincount]
    Progress: [GLOB.blobs.len]/[mode.blobwincount]
    Blob not found!
    " - + if(SSticker.mode.blob_overminds.len) dat += check_role_table("Blob Overminds", SSticker.mode.blob_overminds) @@ -541,9 +541,9 @@ if(SSticker.mode.eventmiscs.len) dat += check_role_table("Event Roles", SSticker.mode.eventmiscs) - if(ts_spiderlist.len) + if(GLOB.ts_spiderlist.len) var/list/spider_minds = list() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/S in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/S in GLOB.ts_spiderlist) if(S.ckey) spider_minds += S.mind if(spider_minds.len) @@ -551,10 +551,10 @@ var/count_eggs = 0 var/count_spiderlings = 0 - for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in ts_egg_list) + for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list) if(is_station_level(E.z)) count_eggs += E.spiderling_number - for(var/obj/structure/spider/spiderling/terror_spiderling/L in ts_spiderling_list) + for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list) if(!L.stillborn && is_station_level(L.z)) count_spiderlings += 1 dat += "
    Growing TS on-station: [count_eggs] egg[count_eggs != 1 ? "s" : ""], [count_spiderlings] spiderling[count_spiderlings != 1 ? "s" : ""].
    " diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 4e2fe2d7ae2f..e4c889ec3098 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -27,8 +27,8 @@ Bombs
    [check_rights(R_SERVER, 0) ? "  Toggle bomb cap
    " : "
    "] Lists
    - Show last [length(lastsignalers)] signalers   - Show last [length(lawchanges)] law changes
    + Show last [length(GLOB.lastsignalers)] signalers   + Show last [length(GLOB.lawchanges)] law changes
    List DNA (Blood)   List Fingerprints
    Power
    diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm index a6ac31a4114a..238c380f4171 100644 --- a/code/modules/admin/sql_notes.dm +++ b/code/modules/admin/sql_notes.dm @@ -1,7 +1,7 @@ /proc/add_note(target_ckey, notetext, timestamp, adminckey, logged = 1, server, checkrights = 1) if(checkrights && !check_rights(R_ADMIN|R_MOD)) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(usr, "Failed to establish database connection.") return @@ -13,7 +13,7 @@ else target_ckey = ckey(target_ckey) - var/DBQuery/query_find_ckey = dbcon.NewQuery("SELECT ckey, exp FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'") + var/DBQuery/query_find_ckey = GLOB.dbcon.NewQuery("SELECT ckey, exp FROM [format_table_name("player")] WHERE ckey = '[target_ckey]'") if(!query_find_ckey.Execute()) var/err = query_find_ckey.ErrorMsg() log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n") @@ -46,7 +46,7 @@ if(config && config.server_name) server = config.server_name server = sanitizeSQL(server) - var/DBQuery/query_noteadd = dbcon.NewQuery("INSERT INTO [format_table_name("notes")] (ckey, timestamp, notetext, adminckey, server, crew_playtime) VALUES ('[target_ckey]', '[timestamp]', '[notetext]', '[admin_sql_ckey]', '[server]', '[crew_number]')") + var/DBQuery/query_noteadd = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("notes")] (ckey, timestamp, notetext, adminckey, server, crew_playtime) VALUES ('[target_ckey]', '[timestamp]', '[notetext]', '[admin_sql_ckey]', '[server]', '[crew_number]')") if(!query_noteadd.Execute()) var/err = query_noteadd.ErrorMsg() log_game("SQL ERROR adding new note to table. Error : \[[err]\]\n") @@ -62,13 +62,13 @@ var/ckey var/notetext var/adminckey - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(usr, "Failed to establish database connection.") return if(!note_id) return note_id = text2num(note_id) - var/DBQuery/query_find_note_del = dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") + var/DBQuery/query_find_note_del = GLOB.dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") if(!query_find_note_del.Execute()) var/err = query_find_note_del.ErrorMsg() log_game("SQL ERROR obtaining ckey, notetext, adminckey from player table. Error : \[[err]\]\n") @@ -77,7 +77,7 @@ ckey = query_find_note_del.item[1] notetext = query_find_note_del.item[2] adminckey = query_find_note_del.item[3] - var/DBQuery/query_del_note = dbcon.NewQuery("DELETE FROM [format_table_name("notes")] WHERE id = [note_id]") + var/DBQuery/query_del_note = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("notes")] WHERE id = [note_id]") if(!query_del_note.Execute()) var/err = query_del_note.ErrorMsg() log_game("SQL ERROR removing note from table. Error : \[[err]\]\n") @@ -89,7 +89,7 @@ /proc/edit_note(note_id) if(!check_rights(R_ADMIN|R_MOD)) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) to_chat(usr, "Failed to establish database connection.") return if(!note_id) @@ -97,7 +97,7 @@ note_id = text2num(note_id) var/target_ckey var/sql_ckey = usr.ckey - var/DBQuery/query_find_note_edit = dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") + var/DBQuery/query_find_note_edit = GLOB.dbcon.NewQuery("SELECT ckey, notetext, adminckey FROM [format_table_name("notes")] WHERE id = [note_id]") if(!query_find_note_edit.Execute()) var/err = query_find_note_edit.ErrorMsg() log_game("SQL ERROR obtaining notetext from notes table. Error : \[[err]\]\n") @@ -112,7 +112,7 @@ new_note = sanitizeSQL(new_note) var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from \"[old_note]\" to \"[new_note]\"
    " edit_text = sanitizeSQL(edit_text) - var/DBQuery/query_update_note = dbcon.NewQuery("UPDATE [format_table_name("notes")] SET notetext = '[new_note]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [note_id]") + var/DBQuery/query_update_note = GLOB.dbcon.NewQuery("UPDATE [format_table_name("notes")] SET notetext = '[new_note]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE id = [note_id]") if(!query_update_note.Execute()) var/err = query_update_note.ErrorMsg() log_game("SQL ERROR editing note. Error : \[[err]\]\n") @@ -139,7 +139,7 @@ output = navbar if(target_ckey) var/target_sql_ckey = ckey(target_ckey) - var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT id, timestamp, notetext, adminckey, last_editor, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") + var/DBQuery/query_get_notes = GLOB.dbcon.NewQuery("SELECT id, timestamp, notetext, adminckey, last_editor, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining ckey, notetext, adminckey, last_editor, server, crew_playtime from notes table. Error : \[[err]\]\n") @@ -181,7 +181,7 @@ search = "^\[^\[:alpha:\]\]" else search = "^[index]" - var/DBQuery/query_list_notes = dbcon.NewQuery("SELECT DISTINCT ckey FROM [format_table_name("notes")] WHERE ckey REGEXP '[search]' ORDER BY ckey") + var/DBQuery/query_list_notes = GLOB.dbcon.NewQuery("SELECT DISTINCT ckey FROM [format_table_name("notes")] WHERE ckey REGEXP '[search]' ORDER BY ckey") if(!query_list_notes.Execute()) var/err = query_list_notes.ErrorMsg() log_game("SQL ERROR obtaining ckey from notes table. Error : \[[err]\]\n") @@ -196,7 +196,7 @@ /proc/show_player_info_irc(var/key as text) var/target_sql_ckey = ckey(key) - var/DBQuery/query_get_notes = dbcon.NewQuery("SELECT timestamp, notetext, adminckey, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") + var/DBQuery/query_get_notes = GLOB.dbcon.NewQuery("SELECT timestamp, notetext, adminckey, server, crew_playtime FROM [format_table_name("notes")] WHERE ckey = '[target_sql_ckey]' ORDER BY timestamp") if(!query_get_notes.Execute()) var/err = query_get_notes.ErrorMsg() log_game("SQL ERROR obtaining timestamp, notetext, adminckey, server, crew_playtime from notes table. Error : \[[err]\]\n") diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 8d46385bcd96..03b5cb097209 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1,3491 +1,3500 @@ -/datum/admins/Topic(href, href_list) - ..() - - if(usr.client != src.owner || !check_rights(0)) - log_admin("[key_name(usr)] tried to use the admin panel without authorization.") - message_admins("[key_name_admin(usr)] has attempted to override the admin panel!") - return - - if(SSticker.mode && SSticker.mode.check_antagonists_topic(href, href_list)) - check_antagonists() - return - - if(href_list["rejectadminhelp"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - var/client/C = locateUID(href_list["rejectadminhelp"]) - if(!C) - return - - C << 'sound/effects/adminhelp.ogg' - - to_chat(C, "- AdminHelp Rejected! -") - to_chat(C, "Your admin help was rejected.") - to_chat(C, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.") - - message_admins("[key_name_admin(usr)] rejected [key_name_admin(C.mob)]'s admin help") - log_admin("[key_name(usr)] rejected [key_name(C.mob)]'s admin help") - - if(href_list["openadminticket"]) - if(!check_rights(R_ADMIN)) - return - var/ticketID = text2num(href_list["openadminticket"]) - SStickets.showDetailUI(usr, ticketID) - - if(href_list["openmentorticket"]) - if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) - return - var/ticketID = text2num(href_list["openmentorticket"]) - SSmentor_tickets.showDetailUI(usr, ticketID) - - if(href_list["stickyban"]) - stickyban(href_list["stickyban"],href_list) - - if(href_list["makeAntag"]) - switch(href_list["makeAntag"]) - if("1") - log_admin("[key_name(usr)] has spawned a traitor.") - if(!makeTraitors()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("2") - log_admin("[key_name(usr)] has spawned a changeling.") - if(!makeChangelings()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("3") - log_admin("[key_name(usr)] has spawned revolutionaries.") - if(!makeRevs()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("4") - log_admin("[key_name(usr)] has spawned a cultists.") - if(!makeCult()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("5") - log_admin("[key_name(usr)] has spawned a wizard.") - if(!makeWizard()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("6") - log_admin("[key_name(usr)] has spawned vampires.") - if(!makeVampires()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("7") - log_admin("[key_name(usr)] has spawned vox raiders.") - if(!makeVoxRaiders()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - if("8") - log_admin("[key_name(usr)] has spawned an abductor team.") - if(!makeAbductorTeam()) - to_chat(usr, "Unfortunately there weren't enough candidates available.") - - else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"] || href_list["dbsearchbantype"]) - var/adminckey = href_list["dbsearchadmin"] - var/playerckey = href_list["dbsearchckey"] - var/playerip = href_list["dbsearchip"] - var/playercid = href_list["dbsearchcid"] - var/dbbantype = text2num(href_list["dbsearchbantype"]) - var/match = 0 - - if("dbmatch" in href_list) - match = 1 - - DB_ban_panel(playerckey, adminckey, playerip, playercid, dbbantype, match) - return - - else if(href_list["dbbanedit"]) - var/banedit = href_list["dbbanedit"] - var/banid = text2num(href_list["dbbanid"]) - if(!banedit || !banid) - return - - DB_ban_edit(banid, banedit) - return - - else if(href_list["dbbanaddtype"]) - - var/bantype = text2num(href_list["dbbanaddtype"]) - var/banckey = href_list["dbbanaddckey"] - var/banip = href_list["dbbanaddip"] - var/bancid = href_list["dbbanaddcid"] - var/banduration = text2num(href_list["dbbaddduration"]) - var/banjob = href_list["dbbanaddjob"] - var/banreason = href_list["dbbanreason"] - - banckey = ckey(banckey) - - switch(bantype) - if(BANTYPE_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") - return - banjob = null - if(BANTYPE_JOB_PERMA) - if(!banckey || !banreason || !banjob) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") - return - banduration = null - if(BANTYPE_JOB_TEMP) - if(!banckey || !banreason || !banjob || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") - return - if(BANTYPE_APPEARANCE) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_ADMIN_PERMA) - if(!banckey || !banreason) - to_chat(usr, "Not enough parameters (Requires ckey and reason)") - return - banduration = null - banjob = null - if(BANTYPE_ADMIN_TEMP) - if(!banckey || !banreason || !banduration) - to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") - return - banjob = null - - var/mob/playermob - - for(var/mob/M in GLOB.player_list) - if(M.ckey == banckey) - playermob = M - break - - - banreason = "(MANUAL BAN) "+banreason - - if(!playermob) - if(banip) - banreason = "[banreason] (CUSTOM IP)" - if(bancid) - banreason = "[banreason] (CUSTOM CID)" - else - message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom IP and computer id fields replaced with the IP and computer id from the located mob") - - DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) - - - else if(href_list["editrights"]) - if(!check_rights(R_PERMISSIONS)) - message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") - log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") - return - - var/adm_ckey - - var/task = href_list["editrights"] - if(task == "add") - var/new_ckey = ckey(clean_input("New admin's ckey","Admin ckey", null)) - if(!new_ckey) return - if(new_ckey in admin_datums) - to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") - return - adm_ckey = new_ckey - task = "rank" - else if(task != "show") - adm_ckey = ckey(href_list["ckey"]) - if(!adm_ckey) - to_chat(usr, "Error: Topic 'editrights': No valid ckey") - return - - var/datum/admins/D = admin_datums[adm_ckey] - - if(task == "remove") - if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes") - if(!D) return - admin_datums -= adm_ckey - D.disassociate() - - updateranktodb(adm_ckey, "player") - message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") - log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") - log_admin_rank_modification(adm_ckey, "Removed") - - else if(task == "rank") - var/new_rank - if(admin_ranks.len) - new_rank = input("Please select a rank", "New rank", null, null) as null|anything in (admin_ranks|"*New Rank*") - else - new_rank = input("Please select a rank", "New rank", null, null) as null|anything in list("Mentor", "Trial Admin", "Game Admin", "*New Rank*") - - var/rights = 0 - if(D) - rights = D.rights - switch(new_rank) - if(null,"") return - if("*New Rank*") - new_rank = input("Please input a new rank", "New custom rank", null, null) as null|text - if(config.admin_legacy_system) - new_rank = ckeyEx(new_rank) - if(!new_rank) - to_chat(usr, "Error: Topic 'editrights': Invalid rank") - return - if(config.admin_legacy_system) - if(admin_ranks.len) - if(new_rank in admin_ranks) - rights = admin_ranks[new_rank] //we typed a rank which already exists, use its rights - else - admin_ranks[new_rank] = 0 //add the new rank to admin_ranks - else - if(config.admin_legacy_system) - new_rank = ckeyEx(new_rank) - rights = admin_ranks[new_rank] //we input an existing rank, use its rights - - if(D) - D.disassociate() //remove adminverbs and unlink from client - D.rank = new_rank //update the rank - D.rights = rights //update the rights based on admin_ranks (default: 0) - else - D = new /datum/admins(new_rank, rights, adm_ckey) - - var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) - D.associate(C) //link up with the client and add verbs - - updateranktodb(adm_ckey, new_rank) - message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") - log_admin_rank_modification(adm_ckey, new_rank) - - else if(task == "permissions") - if(!D) return - while(TRUE) - var/list/permissionlist = list() - for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift - permissionlist[rights2text(i)] = i - var/new_permission = input("Select a permission to turn on/off", adm_ckey + "'s Permissions", null, null) as null|anything in permissionlist - if(!new_permission) - return - var/oldrights = D.rights - var/toggleresult = "ON" - D.rights ^= permissionlist[new_permission] - if(oldrights > D.rights) - toggleresult = "OFF" - - message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") - log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") - log_admin_permission_modification(adm_ckey, permissionlist[new_permission]) - - - edit_admin_permissions() - - else if(href_list["call_shuttle"]) - if(!check_rights(R_ADMIN)) return - - - switch(href_list["call_shuttle"]) - if("1") - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - SSshuttle.emergency.request() - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") - - if("2") - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - switch(SSshuttle.emergency.mode) - if(SHUTTLE_CALL) - SSshuttle.emergency.cancel() - log_admin("[key_name(usr)] sent the Emergency Shuttle back") - message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back") - else - SSshuttle.emergency.cancel() - log_admin("[key_name(usr)] called the Emergency Shuttle") - message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") - - - href_list["secrets"] = "check_antagonist" - - else if(href_list["edit_shuttle_time"]) - if(!check_rights(R_SERVER)) return - - var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num - SSshuttle.emergency.setTimer(timer*10) - log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") - minor_announcement.Announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.") - message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") - href_list["secrets"] = "check_antagonist" - - else if(href_list["delay_round_end"]) - if(!check_rights(R_SERVER)) return - - SSticker.delay_end = !SSticker.delay_end - log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") - message_admins("[key_name_admin(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) - href_list["secretsadmin"] = "check_antagonist" - - else if(href_list["simplemake"]) - if(!check_rights(R_SPAWN)) return - - var/mob/M = locateUID(href_list["mob"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - var/delmob = 0 - switch(alert("Delete old mob?","Message","Yes","No","Cancel")) - if("Cancel") return - if("Yes") delmob = 1 - - switch(href_list["simplemake"]) - if("observer") M.change_mob_type( /mob/dead/observer , null, null, delmob, 1 ) - if("drone") M.change_mob_type( /mob/living/carbon/alien/humanoid/drone , null, null, delmob, 1 ) - if("hunter") M.change_mob_type( /mob/living/carbon/alien/humanoid/hunter , null, null, delmob, 1 ) - if("queen") M.change_mob_type( /mob/living/carbon/alien/humanoid/queen/large , null, null, delmob, 1 ) - if("sentinel") M.change_mob_type( /mob/living/carbon/alien/humanoid/sentinel , null, null, delmob, 1 ) - if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob, 1 ) - if("human") - var/posttransformoutfit = usr.client.robust_dress_shop() - var/mob/living/carbon/human/newmob = M.change_mob_type(/mob/living/carbon/human, null, null, delmob, 1) - if(posttransformoutfit && istype(newmob)) - newmob.equipOutfit(posttransformoutfit) - if("slime") M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob, 1 ) - if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob, 1 ) - if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob, 1 ) - if("cat") M.change_mob_type( /mob/living/simple_animal/pet/cat , null, null, delmob, 1 ) - if("runtime") M.change_mob_type( /mob/living/simple_animal/pet/cat/Runtime , null, null, delmob, 1 ) - if("corgi") M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi , null, null, delmob, 1 ) - if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob, 1 ) - if("coffee") M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob, 1 ) - if("parrot") M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob, 1 ) - if("polyparrot") M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob, 1 ) - if("constructarmoured") M.change_mob_type( /mob/living/simple_animal/hostile/construct/armoured , null, null, delmob, 1 ) - if("constructbuilder") M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob, 1 ) - if("constructwraith") M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob, 1 ) - if("shade") M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob, 1 ) - - log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]") - message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]", 1) - - - /////////////////////////////////////new ban stuff - else if(href_list["unbanf"]) - if(!check_rights(R_BAN)) return - - var/banfolder = href_list["unbanf"] - Banlist.cd = "/base/[banfolder]" - var/key = Banlist["key"] - if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") - if(RemoveBan(banfolder)) - unbanpanel() - else - alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") - unbanpanel() - - else if(href_list["warn"]) - usr.client.warn(href_list["warn"]) - - else if(href_list["unbane"]) - if(!check_rights(R_BAN)) return - - UpdateTime() - var/reason - - var/banfolder = href_list["unbane"] - Banlist.cd = "/base/[banfolder]" - var/reason2 = Banlist["reason"] - var/temp = Banlist["temp"] - - var/minutes = Banlist["minutes"] - - var/banned_key = Banlist["key"] - Banlist.cd = "/base" - - var/duration - - switch(alert("Temporary Ban?",,"Yes","No")) - if("Yes") - temp = 1 - var/mins = 0 - if(minutes > CMinutes) - mins = minutes - CMinutes - mins = input(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) as num|null - if(!mins) return - mins = min(525599,mins) - minutes = CMinutes + mins - duration = GetExp(minutes) - reason = input(usr,"Please state the reason","Reason",reason2) as message|null - if(!reason) return - if("No") - temp = 0 - duration = "Perma" - reason = input(usr,"Please state the reason","Reason",reason2) as message|null - if(!reason) return - - log_admin("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") - message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]", 1) - Banlist.cd = "/base/[banfolder]" - to_chat(Banlist["reason"], reason) - to_chat(Banlist["temp"], temp) - to_chat(Banlist["minutes"], minutes) - to_chat(Banlist["bannedby"], usr.ckey) - Banlist.cd = "/base" - feedback_inc("ban_edit",1) - unbanpanel() - - /////////////////////////////////////new ban stuff - - else if(href_list["appearanceban"]) - if(!check_rights(R_BAN)) - return - var/mob/M = locateUID(href_list["appearanceban"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") - return - var/ban_ckey_param = href_list["dbbanaddckey"] - - var/banreason = appearance_isbanned(M) - if(banreason) - /* if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") - DB_ban_panel(M.ckey) - return */ - switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No")) - if("Yes") - ban_unban_log_save("[key_name(usr)] removed [key_name(M)]'s appearance ban") - log_admin("[key_name(usr)] removed [key_name(M)]'s appearance ban") - feedback_inc("ban_appearance_unban", 1) - DB_ban_unban(M.ckey, BANTYPE_APPEARANCE) - appearance_unban(M) - message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1) - to_chat(M, "[usr.client.ckey] has removed your appearance ban.") - - else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel")) - if("Yes") - var/reason = input(usr,"Please state the reason","Reason") as message|null - if(!reason) - return - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") - log_admin("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") - feedback_inc("ban_appearance",1) - DB_ban_record(BANTYPE_APPEARANCE, M, -1, reason) - appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") - add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0) - message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1) - to_chat(M, "You have been appearance banned by [usr.client.ckey].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Appearance ban can be lifted only upon request.") - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - if("No") - return - - else if(href_list["jobban2"]) -// if(!check_rights(R_BAN)) return - - var/mob/M = locateUID(href_list["jobban2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(!M.ckey) //sanity - to_chat(usr, "This mob has no ckey") - return - if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") - return - - var/dat = "" - var/header = "Job-Ban Panel: [M.name]" - var/body - var/jobs = "" - - /***********************************WARNING!************************************ - The jobban stuff looks mangled and disgusting - But it looks beautiful in-game - -Nodrak - ************************************WARNING!***********************************/ - var/counter = 0 -//Regular jobs - //Command (Blue) - jobs += "" - jobs += "" - for(var/jobPos in command_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 6) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Command Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Security (Red) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in security_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Security Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Engineering (Yellow) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in engineering_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Engineering Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Medical (White) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in medical_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Medical Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Science (Purple) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in science_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Science Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Support (Grey) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in support_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Support Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - //Non-Human (Green) - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in nonhuman_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - - //Drone - if(jobban_isbanned(M, "Drone")) - jobs += "" - else - jobs += "" - - //pAI - if(jobban_isbanned(M, "pAI")) - jobs += "" - else - jobs += "" - - jobs += "
    Non-human Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    DroneDronepAIpAI
    " - - //Antagonist (Orange) - var/isbanned_dept = jobban_isbanned(M, "Syndicate") - jobs += "" - jobs += "" - - counter = 0 - for(var/role in antag_roles) - if(jobban_isbanned(M, role) || isbanned_dept) - jobs += "" - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Antagonist Positions
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " - - //Other races (BLUE, because I have no idea what other color to make this) - jobs += "" - jobs += "" - - counter = 0 - for(var/role in other_roles) - if(jobban_isbanned(M, role) || isbanned_dept) - jobs += "" - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Other
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " - - //Whitelisted positions - counter = 0 - jobs += "" - jobs += "" - for(var/jobPos in whitelisted_positions) - if(!jobPos) continue - var/datum/job/job = SSjobs.GetJob(jobPos) - if(!job) continue - - if(jobban_isbanned(M, job.title)) - jobs += "" - counter++ - else - jobs += "" - counter++ - - if(counter >= 5) //So things dont get squiiiiished! - jobs += "" - counter = 0 - jobs += "
    Whitelisted Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " - - body = "[jobs]" - dat = "[header][body]" - usr << browse(dat, "window=jobban2;size=800x490") - return - - //JOBBAN'S INNARDS - else if(href_list["jobban3"]) - if(!check_rights(R_BAN)) return - - var/mob/M = locateUID(href_list["jobban4"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - - if(M != usr) //we can jobban ourselves - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them - alert("You cannot perform this action. You must be of a higher administrative rank!") - return - - var/ban_ckey_param = href_list["dbbanaddckey"] - - if(!SSjobs) - to_chat(usr, "SSjobs has not been setup!") - return - - //get jobs for department if specified, otherwise just returnt he one job in a list. - var/list/joblist = list() - switch(href_list["jobban3"]) - if("commanddept") - for(var/jobPos in command_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("securitydept") - for(var/jobPos in security_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("engineeringdept") - for(var/jobPos in engineering_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("medicaldept") - for(var/jobPos in medical_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("sciencedept") - for(var/jobPos in science_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("supportdept") - for(var/jobPos in support_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("nonhumandept") - joblist += "pAI" - for(var/jobPos in nonhuman_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - if("whitelistdept") - for(var/jobPos in whitelisted_positions) - if(!jobPos) continue - var/datum/job/temp = SSjobs.GetJob(jobPos) - if(!temp) continue - joblist += temp.title - else - joblist += href_list["jobban3"] - - //Create a list of unbanned jobs within joblist - var/list/notbannedlist = list() - for(var/job in joblist) - if(!jobban_isbanned(M, job)) - notbannedlist += job - - //Banning comes first - if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. - switch(alert("Temporary Ban of [M.ckey]?",,"Yes","No", "Cancel")) - if("Yes") - if(config.ban_legacy_system) - to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") - return - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null - if(!mins) - return - var/reason = input(usr,"Please state the reason","Reason","") as message|null - if(!reason) - return - - var/msg - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - for(var/job in notbannedlist) - ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") - log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes") - feedback_inc("ban_job_tmp",1) - DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job) - feedback_add_details("ban_job_tmp","- [job]") - jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") //Legacy banning does not support temporary jobbans. - if(!msg) - msg = job - else - msg += ", [job]" - add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "This jobban will be lifted in [mins] minutes.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("No") - var/reason = input(usr,"Please state the reason","Reason","") as message|null - if(reason) - var/msg - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - for(var/job in notbannedlist) - ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") - log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]") - feedback_inc("ban_job",1) - DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job) - feedback_add_details("ban_job","- [job]") - jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") - if(!msg) msg = job - else msg += ", [job]" - add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) - message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") - to_chat(M, "The reason is: [reason]") - to_chat(M, "Jobban can be lifted only upon request.") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - if("Cancel") - return - - //Unbanning joblist - //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) - if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. - if(!config.ban_legacy_system) - to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") - DB_ban_panel(M.ckey) - return - var/msg - for(var/job in joblist) - var/reason = jobban_isbanned(M, job) - if(!reason) continue //skip if it isn't jobbanned anyway - switch(alert("Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm","Yes","No")) - if("Yes") - ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") - log_admin("[key_name(usr)] unbanned [key_name(M)] from [job]") - DB_ban_unban(M.ckey, BANTYPE_JOB_PERMA, job) - feedback_inc("ban_job_unban",1) - feedback_add_details("ban_job_unban","- [job]") - jobban_unban(M, job) - if(!msg) msg = job - else msg += ", [job]" - else - continue - if(msg) - message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) - to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") - href_list["jobban2"] = 1 // lets it fall through and refresh - return 1 - return 0 //we didn't do anything! - - else if(href_list["boot2"]) - var/mob/M = locateUID(href_list["boot2"]) - if(ismob(M)) - if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) - to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.") - return - to_chat(M, "You have been kicked from the server") - log_admin("[key_name(usr)] booted [key_name(M)].") - message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) - //M.client = null - del(M.client) - - //Player Notes - else if(href_list["addnote"]) - var/target_ckey = href_list["addnote"] - add_note(target_ckey) - - else if(href_list["addnoteempty"]) - add_note() - - else if(href_list["removenote"]) - var/note_id = href_list["removenote"] - remove_note(note_id) - - else if(href_list["editnote"]) - var/note_id = href_list["editnote"] - edit_note(note_id) - - else if(href_list["shownote"]) - var/target = href_list["shownote"] - show_note(index = target) - - else if(href_list["nonalpha"]) - var/target = href_list["nonalpha"] - target = text2num(target) - show_note(index = target) - - else if(href_list["webtools"]) - var/target_ckey = href_list["webtools"] - if(config.forum_playerinfo_url) - var/url_to_open = config.forum_playerinfo_url + target_ckey - if(alert("Open [url_to_open]",,"Yes","No")=="Yes") - usr.client << link(url_to_open) - - else if(href_list["shownoteckey"]) - var/target_ckey = href_list["shownoteckey"] - show_note(target_ckey) - - else if(href_list["notessearch"]) - var/target = href_list["notessearch"] - show_note(index = target) - - else if(href_list["noteedits"]) - var/note_id = sanitizeSQL("[href_list["noteedits"]]") - var/DBQuery/query_noteedits = dbcon.NewQuery("SELECT edits FROM [format_table_name("notes")] WHERE id = '[note_id]'") - if(!query_noteedits.Execute()) - var/err = query_noteedits.ErrorMsg() - log_game("SQL ERROR obtaining edits from notes table. Error : \[[err]\]\n") - return - if(query_noteedits.NextRow()) - var/edit_log = query_noteedits.item[1] - usr << browse(edit_log,"window=noteedits") - - else if(href_list["removejobban"]) - if(!check_rights(R_BAN)) return - - var/t = href_list["removejobban"] - if(t) - if((alert("Do you want to unjobban [t]?","Unjobban confirmation", "Yes", "No") == "Yes") && t) //No more misclicks! Unless you do it twice. - log_admin("[key_name(usr)] removed [t]") - message_admins("[key_name_admin(usr)] removed [t]", 1) - jobban_remove(t) - href_list["ban"] = 1 // lets it fall through and refresh - var/t_split = splittext(t, " - ") - var/key = t_split[1] - var/job = t_split[2] - DB_ban_unban(ckey(key), BANTYPE_JOB_PERMA, job) - - else if(href_list["newban"]) - if(!check_rights(R_BAN)) return - - var/mob/M = locateUID(href_list["newban"]) - if(!ismob(M)) - return - var/ban_ckey_param = href_list["dbbanaddckey"] - - switch(alert("Temporary Ban of [M.ckey] / [ban_ckey_param]?",,"Yes","No", "Cancel")) - if("Yes") - var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null - if(!mins) - return - if(mins >= 525600) mins = 525599 - var/reason = input(usr,"Please state the reason","Reason") as message|null - if(!reason) - return - M = admin_ban_mobsearch(M, ban_ckey_param, usr) - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) - ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") - feedback_inc("ban_tmp",1) - DB_ban_record(BANTYPE_TEMP, M, mins, reason) - feedback_inc("ban_tmp_mins",mins) - if(M.client) - M.client.link_forum_account(TRUE) - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") - - del(M.client) - //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. - if("No") - var/reason = input(usr,"Please state the reason","Reason") as message|null - if(!reason) - return - AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) - to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") - to_chat(M, "This ban does not expire automatically and must be appealed.") - if(M.client) - M.client.link_forum_account(TRUE) - if(config.banappeals) - to_chat(M, "To try to resolve this matter head to [config.banappeals]") - else - to_chat(M, "No ban appeals URL has been set.") - ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This ban does not expire automatically and must be appealed.") - log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") - message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") - feedback_inc("ban_perma",1) - DB_ban_record(BANTYPE_PERMA, M, -1, reason) - - del(M.client) - //qdel(M) - if("Cancel") - return - - - //Watchlist - else if(href_list["watchadd"]) - var/target_ckey = href_list["watchadd"] - usr.client.watchlist_add(target_ckey) - - else if(href_list["watchremove"]) - var/target_ckey = href_list["watchremove"] - var/confirm = alert("Are you sure you want to remove [target_ckey] from the watchlist?", "Confirm Watchlist Removal", "Yes", "No") - if(confirm == "Yes") - usr.client.watchlist_remove(target_ckey) - - else if(href_list["watchedit"]) - var/target_ckey = href_list["watchedit"] - usr.client.watchlist_edit(target_ckey) - - else if(href_list["watchaddbrowse"]) - usr.client.watchlist_add(null, 1) - - else if(href_list["watchremovebrowse"]) - var/target_ckey = href_list["watchremovebrowse"] - usr.client.watchlist_remove(target_ckey, 1) - - else if(href_list["watcheditbrowse"]) - var/target_ckey = href_list["watcheditbrowse"] - usr.client.watchlist_edit(target_ckey, 1) - - else if(href_list["watchsearch"]) - var/target_ckey = href_list["watchsearch"] - usr.client.watchlist_show(target_ckey) - - else if(href_list["watchshow"]) - usr.client.watchlist_show() - - else if(href_list["watcheditlog"]) - var/target_ckey = sanitizeSQL("[href_list["watcheditlog"]]") - var/DBQuery/query_watchedits = dbcon.NewQuery("SELECT edits FROM [format_table_name("watch")] WHERE ckey = '[target_ckey]'") - if(!query_watchedits.Execute()) - var/err = query_watchedits.ErrorMsg() - log_game("SQL ERROR obtaining edits from watch table. Error : \[[err]\]\n") - return - if(query_watchedits.NextRow()) - var/edit_log = query_watchedits.item[1] - usr << browse(edit_log,"window=watchedits") - - else if(href_list["mute"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - - var/mob/M = locateUID(href_list["mute"]) - if(!ismob(M)) return - if(!M.client) return - - var/mute_type = href_list["mute_type"] - if(istext(mute_type)) mute_type = text2num(mute_type) - if(!isnum(mute_type)) return - - cmd_admin_mute(M, mute_type) - - else if(href_list["c_mode"]) - if(!check_rights(R_ADMIN)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - var/dat = {"What mode do you wish to play?
    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
    "} - dat += {"Secret
    "} - dat += {"Random
    "} - dat += {"Now: [master_mode]"} - usr << browse(dat, "window=c_mode") - - else if(href_list["f_secret"]) - if(!check_rights(R_ADMIN)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(master_mode != "secret") - return alert(usr, "The game mode has to be secret!", null, null, null, null) - var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} - for(var/mode in config.modes) - dat += {"[config.mode_names[mode]]
    "} - dat += {"Random (default)
    "} - dat += {"Now: [secret_force_mode]"} - usr << browse(dat, "window=f_secret") - - else if(href_list["c_mode2"]) - if(!check_rights(R_ADMIN|R_SERVER)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - master_mode = href_list["c_mode2"] - log_admin("[key_name(usr)] set the mode as [master_mode].") - message_admins("[key_name_admin(usr)] set the mode as [master_mode].", 1) - to_chat(world, "The mode is now: [master_mode]") - Game() // updates the main game menu - world.save_mode(master_mode) - .(href, list("c_mode"=1)) - - else if(href_list["f_secret2"]) - if(!check_rights(R_ADMIN|R_SERVER)) return - - if(SSticker && SSticker.mode) - return alert(usr, "The game has already started.", null, null, null, null) - if(master_mode != "secret") - return alert(usr, "The game mode has to be secret!", null, null, null, null) - secret_force_mode = href_list["f_secret2"] - log_admin("[key_name(usr)] set the forced secret mode as [secret_force_mode].") - message_admins("[key_name_admin(usr)] set the forced secret mode as [secret_force_mode].", 1) - Game() // updates the main game menu - .(href, list("f_secret"=1)) - - else if(href_list["monkeyone"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)]", 1) - H.monkeyize() - - - else if(href_list["corgione"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["corgione"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)]", 1) - H.corgize() - - else if(href_list["makePAI"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makePAI"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes") - return - - var/painame = "Default" - var/name = "" - if(alert(usr, "Do you want to set their name or let them choose their own name?", "Name Choice", "Set Name", "Let them choose") == "Set Name") - name = sanitize(copytext(input(usr, "Enter a name for the new pAI. Default name is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) - else - name = sanitize(copytext(input(H, "An admin wants to make you into a pAI. Choose a name. Default is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) - - if(!name) - name = painame - - log_admin("[key_name(usr)] attempting to pAIze [key_name(H)]") - message_admins("[key_name_admin(usr)] attempting to pAIze [key_name_admin(H)]", 1) - H.paize(name) - - else if(href_list["forcespeech"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/mob/M = locateUID(href_list["forcespeech"]) - if(!ismob(M)) - to_chat(usr, "this can only be used on instances of type /mob") - - var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. - if(!speech) return - M.say(speech) - speech = sanitize(speech) // Nah, we don't trust them - log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") - message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") - - else if(href_list["sendtoprison"]) - if(!check_rights(R_ADMIN)) return - - if(alert(usr, "Send to admin prison for the round?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["sendtoprison"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - var/turf/prison_cell = pick(prisonwarp) - if(!prison_cell) return - - var/obj/structure/closet/secure_closet/brig/locker = new /obj/structure/closet/secure_closet/brig(prison_cell) - locker.opened = 0 - locker.locked = 1 - - //strip their stuff and stick it in the crate - for(var/obj/item/I in M) - if(M.unEquip(I)) - I.loc = locker - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - M.update_icons() - - //so they black out before warping - M.Paralyse(5) - sleep(5) - if(!M) return - - M.loc = prison_cell - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - - to_chat(M, "You have been sent to the prison station!") - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) - - else if(href_list["sendbacktolobby"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["sendbacktolobby"]) - - if(!isobserver(M)) - to_chat(usr, "You can only send ghost players back to the Lobby.") - return - - if(!M.client) - to_chat(usr, "[M] doesn't seem to have an active client.") - return - - if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") - return - - log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] back to the Lobby.") - - var/mob/new_player/NP = new() - GLOB.non_respawnable_keys -= M.ckey - NP.ckey = M.ckey - qdel(M) - - else if(href_list["tdome1"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdome1"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.unEquip(I) - if(I) - I.loc = M.loc - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdome1) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) - - else if(href_list["tdome2"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdome2"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.unEquip(I) - if(I) - I.loc = M.loc - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdome2) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) - - else if(href_list["tdomeadmin"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdomeadmin"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - M.Paralyse(5) - sleep(5) - M.loc = pick(tdomeadmin) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) - - else if(href_list["tdomeobserve"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["tdomeobserve"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - for(var/obj/item/I in M) - M.unEquip(I) - if(I) - I.loc = M.loc - I.layer = initial(I.layer) - I.plane = initial(I.plane) - I.dropped(M) - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/observer = M - observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), slot_w_uniform) - observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(observer), slot_shoes) - M.Paralyse(5) - sleep(5) - M.loc = pick(tdomeobserve) - spawn(50) - to_chat(M, "You have been sent to the Thunderdome.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) - - else if(href_list["aroomwarp"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") - return - - var/mob/M = locateUID(href_list["aroomwarp"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob") - return - if(istype(M, /mob/living/silicon/ai)) - to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") - return - - M.Paralyse(5) - sleep(5) - M.loc = pick(aroomwarp) - spawn(50) - to_chat(M, "You have been sent to the Admin Room!.") - log_admin("[key_name(usr)] has sent [key_name(M)] to the Admin Room") - message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the Admin Room", 1) - - - else if(href_list["revive"]) - if(!check_rights(R_REJUVINATE)) return - - var/mob/living/L = locateUID(href_list["revive"]) - if(!istype(L)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - - L.revive() - message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!", 1) - log_admin("[key_name(usr)] healed / revived [key_name(L)]") - - else if(href_list["makeai"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes") - return - - message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1) - log_admin("[key_name(usr)] AIized [key_name(H)]") - var/mob/living/silicon/ai/ai_character = H.AIize() - ai_character.moveToAILandmark() - - else if(href_list["makealien"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_alienize(H) - - else if(href_list["makeslime"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_slimeize(H) - - else if(href_list["makesuper"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_super(H) - - else if(href_list["makerobot"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_robotize(H) - - else if(href_list["makeanimal"]) - if(!check_rights(R_SPAWN)) return - - var/mob/M = locateUID(href_list["makeanimal"]) - if(istype(M, /mob/new_player)) - to_chat(usr, "This cannot be used on instances of type /mob/new_player") - return - if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") - return - - usr.client.cmd_admin_animalize(M) - - else if(href_list["incarn_ghost"]) - if(!check_rights(R_SPAWN)) - return - - var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"]) - if(!istype(G)) - to_chat(usr, "This will only work on /mob/dead/observer") - - var/posttransformoutfit = usr.client.robust_dress_shop() - - var/mob/living/carbon/human/H = G.incarnate_ghost() - - if(posttransformoutfit && istype(H)) - H.equipOutfit(posttransformoutfit) - - log_admin("[key_name(G)] was incarnated by [key_name(owner)]") - message_admins("[key_name_admin(G)] was incarnated by [key_name_admin(owner)]") - - else if(href_list["togmutate"]) - if(!check_rights(R_SPAWN)) return - - var/mob/living/carbon/human/H = locateUID(href_list["togmutate"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/block=text2num(href_list["block"]) - //testing("togmutate([href_list["block"]] -> [block])") - usr.client.cmd_admin_toggle_block(H,block) - show_player_panel(H) - //H.regenerate_icons() - - else if(href_list["adminplayeropts"]) - var/mob/M = locateUID(href_list["adminplayeropts"]) - show_player_panel(M) - - else if(href_list["adminplayerobservefollow"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - - var/mob/M = locateUID(href_list["adminplayerobservefollow"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - var/mob/dead/observer/A = C.mob - sleep(2) - A.ManualFollow(M) - - else if(href_list["check_antagonist"]) - check_antagonists() - - else if(href_list["take_question"]) - var/index = text2num(href_list["take_question"]) - - if(href_list["is_mhelp"]) - SSmentor_tickets.takeTicket(index) - else //Ahelp - SStickets.takeTicket(index) - - else if(href_list["resolve"]) - var/index = text2num(href_list["resolve"]) - if(href_list["is_mhelp"]) - SSmentor_tickets.resolveTicket(index) - else //Ahelp - SStickets.resolveTicket(index) - - else if(href_list["autorespond"]) - var/index = text2num(href_list["autorespond"]) - if(!check_rights(R_ADMIN|R_MOD)) - return - SStickets.autoRespond(index) - - else if(href_list["cult_nextobj"]) - if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes") - return - - if(!GAMEMODE_IS_CULT) - alert("Couldn't locate cult mode datum! This shouldn't ever happen, tell a coder!") - return - - var/datum/game_mode/cult/cult_round = SSticker.mode - cult_round.bypass_phase() - message_admins("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") - log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") - - else if(href_list["cult_mindspeak"]) - var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [SSticker.cultdat.entity_name]", "Voice of [SSticker.cultdat.entity_name]", "") - if(!input) - return - - for(var/datum/mind/H in SSticker.mode.cult) - if (H.current) - to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]") - - for(var/mob/dead/observer/O in GLOB.player_list) - to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]") - - message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].") - log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]") - - else if(href_list["adminplayerobservecoodjump"]) - if(!check_rights(R_ADMIN)) return - - var/x = text2num(href_list["X"]) - var/y = text2num(href_list["Y"]) - var/z = text2num(href_list["Z"]) - - var/client/C = usr.client - if(!isobserver(usr)) C.admin_ghost() - sleep(2) - C.jumptocoord(x,y,z) - - else if(href_list["adminchecklaws"]) - output_ai_laws() - - else if(href_list["adminmoreinfo"]) - var/mob/M = locateUID(href_list["adminmoreinfo"]) - admin_mob_info(M) - - else if(href_list["adminspawncookie"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"]) - if(!ishuman(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - - H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand ) - if(!(istype(H.l_hand,/obj/item/reagent_containers/food/snacks/cookie))) - H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_r_hand ) - if(!(istype(H.r_hand,/obj/item/reagent_containers/food/snacks/cookie))) - log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") - message_admins("[key_name_admin(H)] has [H.p_their()] hands full, so [H.p_they()] did not receive [H.p_their()] cookie, spawned by [key_name_admin(src.owner)].") - return - else - H.update_inv_r_hand()//To ensure the icon appears in the HUD - else - H.update_inv_l_hand() - log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") - message_admins("[key_name_admin(H)] got [H.p_their()] cookie, spawned by [key_name_admin(src.owner)]") - feedback_inc("admin_cookies_spawned",1) - to_chat(H, "Your prayers have been answered!! You received the best cookie!") - - else if(href_list["BlueSpaceArtillery"]) - if(!check_rights(R_ADMIN|R_EVENT)) return - - var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"]) - if(!isliving(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - - if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") - return - - if(BSACooldown) - to_chat(owner, "Standby. Reload cycle in progress. Gunnery crews ready in five seconds!") - return - - BSACooldown = 1 - spawn(50) - BSACooldown = 0 - - to_chat(M, "You've been hit by bluespace artillery!") - log_admin("[key_name(M)] has been hit by Bluespace Artillery fired by [key_name(owner)]") - message_admins("[key_name_admin(M)] has been hit by Bluespace Artillery fired by [key_name_admin(owner)]") - - var/turf/simulated/floor/T = get_turf(M) - if(istype(T)) - if(prob(80)) - T.break_tile_to_plating() - else - T.break_tile() - - if(M.health <= 1) - M.gib() - else - M.adjustBruteLoss(min(99,(M.health - 1))) - M.Stun(20) - M.Weaken(20) - M.Stuttering(20) - - else if(href_list["CentcommReply"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["CentcommReply"]) - usr.client.admin_headset_message(M, "Centcomm") - - else if(href_list["SyndicateReply"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["SyndicateReply"]) - usr.client.admin_headset_message(M, "Syndicate") - - else if(href_list["HeadsetMessage"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/M = locateUID(href_list["HeadsetMessage"]) - usr.client.admin_headset_message(M) - - else if(href_list["EvilFax"]) - if(!check_rights(R_ADMIN)) - return - var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine") - var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes - if(!(eviltype in etypes)) - return - var/customname = clean_input("Pick a title for the evil fax.", "Fax Title", , owner) - if(!customname) - customname = "paper" - var/obj/item/paper/evilfax/P = new /obj/item/paper/evilfax(null) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - - P.name = "Central Command - [customname]" - P.info = "You really should've known better." - P.myeffect = eviltype - P.mytarget = H - if(alert("Do you want the Evil Fax to activate automatically if [H] tries to ignore it?",,"Yes", "No") == "Yes") - P.activate_on_timeout = 1 - P.x = rand(-2, 0) - P.y = rand(-1, 2) - P.offset_x += P.x - P.offset_y += P.y - P.update_icon() - var/stampvalue = "cent" - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-[stampvalue]" - stampoverlay.pixel_x = P.x - stampoverlay.pixel_y = P.y - P.stamped = list() - P.stamped += /obj/item/stamp/centcom - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-[stampvalue]" - P.overlays += stampoverlay - P.stamps += "
    " - P.update_icon() - P.faxmachineid = fax.UID() - P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required. - if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") - to_chat(src.owner, "You sent a [eviltype] fax to [H]") - log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax") - message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax") - else if(href_list["Bless"]) - if(!check_rights(R_EVENT)) - return - var/mob/living/M = locateUID(href_list["Bless"]) - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - var/btypes = list("To Arrivals", "Moderate Heal") - var/mob/living/carbon/human/H - if(ishuman(M)) - H = M - btypes += "Heal Over Time" - btypes += "Permanent Regeneration" - btypes += "Super Powers" - btypes += "Scarab Guardian" - btypes += "Human Protector" - btypes += "Sentient Pet" - btypes += "All Access" - var/blessing = input(owner, "How would you like to bless [M]?", "Its good to be good...", "") as null|anything in btypes - if(!(blessing in btypes)) - return - var/logmsg = null - switch(blessing) - if("To Arrivals") - M.forceMove(pick(latejoin)) - to_chat(M, "You are abruptly pulled through space!") - logmsg = "a teleport to arrivals." - if("Moderate Heal") - M.adjustBruteLoss(-25) - M.adjustFireLoss(-25) - M.adjustToxLoss(-25) - M.adjustOxyLoss(-25) - to_chat(M,"You feel invigorated!") - logmsg = "a moderate heal." - if("Heal Over Time") - H.reagents.add_reagent("salglu_solution", 30) - H.reagents.add_reagent("salbutamol", 20) - H.reagents.add_reagent("spaceacillin", 20) - logmsg = "a heal over time." - if("Permanent Regeneration") - H.dna.SetSEState(REGENERATEBLOCK, 1) - genemutcheck(H, REGENERATEBLOCK, null, MUTCHK_FORCED) - H.update_mutations() - H.gene_stability = 100 - logmsg = "permanent regeneration." - if("Super Powers") - var/list/default_genes = list(REGENERATEBLOCK, BREATHLESSBLOCK, COLDBLOCK) - for(var/gene in default_genes) - H.dna.SetSEState(gene, 1) - genemutcheck(H, gene, null, MUTCHK_FORCED) - H.update_mutations() - H.gene_stability = 100 - logmsg = "superpowers." - if("Scarab Guardian") - var/obj/item/guardiancreator/biological/scarab = new /obj/item/guardiancreator/biological(H) - var/list/possible_guardians = list("Chaos", "Standard", "Ranged", "Support", "Explosive", "Random") - var/typechoice = input("Select Guardian Type", "Type") as null|anything in possible_guardians - if(isnull(typechoice)) - return - if(typechoice != "Random") - possible_guardians -= "Random" - scarab.possible_guardians = list() - scarab.possible_guardians += typechoice - scarab.attack_self(H) - spawn(700) - qdel(scarab) - logmsg = "scarab guardian." - if("Sentient Pet") - var/pets = subtypesof(/mob/living/simple_animal) - var/petchoice = input("Select pet type", "Pets") as null|anything in pets - if(isnull(petchoice)) - return - var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10) - var/mob/dead/observer/theghost = null - if(candidates.len) - var/mob/living/simple_animal/pet/P = new petchoice(H.loc) - theghost = pick(candidates) - P.key = theghost.key - P.master_commander = H - P.universal_speak = 1 - P.universal_understand = 1 - P.can_collar = 1 - P.faction = list("neutral") - var/obj/item/clothing/accessory/petcollar/C = new - P.add_collar(C) - var/obj/item/card/id/I = H.wear_id - if(I) - var/obj/item/card/id/D = new /obj/item/card/id(C) - D.access = I.access - D.registered_name = P.name - D.assignment = "Pet" - C.access_id = D - spawn(30) - var/newname = sanitize(copytext(input(P, "You are [P], special event pet of [H]. Change your name to something else?", "Name change", P.name) as null|text,1,MAX_NAME_LEN)) - if(newname && newname != P.name) - P.name = newname - if(P.mind) - P.mind.name = newname - logmsg = "pet ([P])." - else - to_chat(usr, "WARNING: Nobody volunteered to play the special event pet.") - logmsg = "pet (no volunteers)." - if("Human Protector") - usr.client.create_eventmob_for(H, 0) - logmsg = "syndie protector." - if("All Access") - var/obj/item/card/id/I = H.wear_id - if(I) - var/list/access_to_give = get_all_accesses() - for(var/this_access in access_to_give) - if(!(this_access in I.access)) - // don't have it - add it - I.access |= this_access - else - to_chat(usr, "ERROR: [H] is not wearing an ID card.") - logmsg = "all access." - if(logmsg) - log_admin("[key_name(owner)] answered [key_name(M)]'s prayer with a blessing: [logmsg]") - message_admins("[key_name_admin(owner)] answered [key_name_admin(M)]'s prayer with a blessing: [logmsg]") - else if(href_list["Smite"]) - if(!check_rights(R_EVENT)) - return - var/mob/living/M = locateUID(href_list["Smite"]) - var/mob/living/carbon/human/H - if(!istype(M)) - to_chat(usr, "This can only be used on instances of type /mob/living") - return - var/ptypes = list("Lightning bolt", "Fire Death", "Gib") - if(ishuman(M)) - H = M - ptypes += "Brain Damage" - ptypes += "Honk Tumor" - ptypes += "Hallucinate" - ptypes += "Cold" - ptypes += "Hunger" - ptypes += "Cluwne" - ptypes += "Mutagen Cookie" - ptypes += "Hellwater Cookie" - ptypes += "Hunter" - ptypes += "Crew Traitor" - ptypes += "Floor Cluwne" - ptypes += "Shamebrero" - var/punishment = input(owner, "How would you like to smite [M]?", "Its good to be baaaad...", "") as null|anything in ptypes - if(!(punishment in ptypes)) - return - var/logmsg = null - switch(punishment) - if("Lightning bolt") - M.electrocute_act(5, "Lightning Bolt", safety = TRUE, override = TRUE) - playsound(get_turf(M), 'sound/magic/lightningshock.ogg', 50, 1, -1) - M.adjustFireLoss(75) - M.Weaken(5) - to_chat(M, "The gods have punished you for your sins!") - logmsg = "a lightning bolt." - if("Fire Death") - to_chat(M,"You feel hotter than usual. Maybe you should lowe-wait, is that your hand melting?") - var/turf/simulated/T = get_turf(M) - new /obj/effect/hotspot(T) - M.adjustFireLoss(150) - logmsg = "a firey death." - if("Gib") - M.gib(FALSE) - logmsg = "gibbed." - - if("Brain Damage") - H.adjustBrainLoss(75) - logmsg = "75 brain damage." - if("Honk Tumor") - if(!H.get_int_organ(/obj/item/organ/internal/honktumor)) - var/obj/item/organ/internal/organ = new /obj/item/organ/internal/honktumor - to_chat(H, "Life seems funnier, somehow.") - organ.insert(H) - logmsg = "a honk tumor." - if("Hallucinate") - H.Hallucinate(1000) - logmsg = "hallucinations." - if("Cold") - H.reagents.add_reagent("frostoil", 40) - H.reagents.add_reagent("ice", 40) - logmsg = "cold." - if("Hunger") - H.set_nutrition(NUTRITION_LEVEL_CURSED) - logmsg = "starvation." - if("Cluwne") - H.makeCluwne() - H.mutations |= NOCLONE - logmsg = "cluwned." - if("Mutagen Cookie") - var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie - evilcookie.reagents.add_reagent("mutagen", 10) - evilcookie.desc = "It has a faint green glow." - evilcookie.bitesize = 100 - evilcookie.flags = NODROP | DROPDEL - H.drop_l_hand() - H.equip_to_slot_or_del(evilcookie, slot_l_hand) - logmsg = "a mutagen cookie." - if("Hellwater Cookie") - var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie - evilcookie.reagents.add_reagent("hell_water", 25) - evilcookie.desc = "Sulphur-flavored." - evilcookie.bitesize = 100 - evilcookie.flags = NODROP | DROPDEL - H.drop_l_hand() - H.equip_to_slot_or_del(evilcookie, slot_l_hand) - logmsg = "a hellwater cookie." - if("Hunter") - H.mutations |= NOCLONE - usr.client.create_eventmob_for(H, 1) - logmsg = "hunter." - if("Crew Traitor") - if(!H.mind) - to_chat(usr, "This mob has no mind!") - return - - var/list/possible_traitors = list() - for(var/mob/living/player in GLOB.living_mob_list) - if(player.client && player.mind && player.stat != DEAD && player != H) - if(ishuman(player) && !player.mind.special_role) - if(player.client && (ROLE_TRAITOR in player.client.prefs.be_special) && !jobban_isbanned(player, ROLE_TRAITOR) && !jobban_isbanned(player, "Syndicate")) - possible_traitors += player.mind - - for(var/datum/mind/player in possible_traitors) - if(player.current) - if(ismindshielded(player.current)) - possible_traitors -= player - - if(possible_traitors.len) - var/datum/mind/newtraitormind = pick(possible_traitors) - var/datum/objective/assassinate/kill_objective = new() - kill_objective.target = H.mind - kill_objective.owner = newtraitormind - kill_objective.explanation_text = "Assassinate [H.mind], the [H.mind.assigned_role]" - H.mind.objectives += kill_objective - var/datum/antagonist/traitor/T = new() - T.give_objectives = FALSE - to_chat(newtraitormind, "ATTENTION: It is time to pay your debt to the Syndicate...") - to_chat(newtraitormind, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") - newtraitormind.add_antag_datum(T) - else - to_chat(usr, "ERROR: Failed to create a traitor.") - return - logmsg = "crew traitor." - - if("Floor Cluwne") - var/turf/T = get_turf(M) - var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) - FC.smiting = TRUE - FC.Acquire_Victim(M) - logmsg = "floor cluwne" - if("Shamebrero") - if(H.head) - H.unEquip(H.head, TRUE) - var/obj/item/clothing/head/sombrero/shamebrero/S = new(H.loc) - H.equip_to_slot_or_del(S, slot_head) - logmsg = "shamebrero" - if(logmsg) - log_admin("[key_name(owner)] smited [key_name(M)] with: [logmsg]") - message_admins("[key_name_admin(owner)] smited [key_name_admin(M)] with: [logmsg]") - else if(href_list["cryossd"]) - if(!check_rights(R_ADMIN)) - return - var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!href_list["cryoafk"] && !isLivingSSD(H)) - to_chat(usr, "This can only be used on living, SSD players.") - return - if(istype(H.loc, /obj/machinery/cryopod)) - var/obj/machinery/cryopod/P = H.loc - P.despawn_occupant() - log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.") - message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.") - else if(cryo_ssd(H)) - log_admin("[key_name(usr)] sent [H.job] [H] to cryo.") - message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.") - if(href_list["cryoafk"]) // Warn them if they are send to storage and are AFK - to_chat(H, "The admins have moved you to cryo storage for being AFK. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.") - SEND_SOUND(H, 'sound/effects/adminhelp.ogg') - if(H.client) - window_flash(H.client) - else if(href_list["FaxReplyTemplate"]) - if(!check_rights(R_ADMIN)) - return - var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - var/obj/item/paper/P = new /obj/item/paper(null) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - P.name = "Central Command - paper" - var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions") - var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes - var/tmsg = "



    Nanotrasen Science Station [using_map.station_short]


    NAS Trurl Communications Department Report


    " - if(stype == "Handle it yourselves!") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

    This is an automatic message." - else if(stype == "Illegible fax") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

    Please consult your nearest dictionary and/or thesaurus and try again.

    This is an automatic message." - else if(stype == "Fax not signed") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax has not been correctly signed and, as such, we cannot verify your identity.

    Please sign your faxes before sending them so that we may verify your identity.

    This is an automatic message." - else if(stype == "Not Right Now") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

    This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

    Please try again later.

    This is an automatic message." - else if(stype == "You are wasting our time") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

    Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

    This is an automatic message." - else if(stype == "Keep up the good work") - tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.

    We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.

    This is absolutely not an automated message." - else if(stype == "ERT Instructions") - tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please utilize the Card Swipers if you wish to call for an ERT.

    This is an automated message." - else - return - tmsg += "
    " - P.info = tmsg - P.x = rand(-2, 0) - P.y = rand(-1, 2) - P.offset_x += P.x - P.offset_y += P.y - P.update_icon() - var/stampvalue = "cent" - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.icon_state = "paper_stamp-[stampvalue]" - stampoverlay.pixel_x = P.x - stampoverlay.pixel_y = P.y - P.stamped = list() - P.stamped += /obj/item/stamp/centcom - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-[stampvalue]" - P.overlays += stampoverlay - P.stamps += "
    " - P.update_icon() - fax.receivefax(P) - if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") - to_chat(src.owner, "You sent a standard '[stype]' fax to [H]") - log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax") - message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax") - - else if(href_list["HONKReply"]) - var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "") - if(!input) return - - to_chat(src.owner, "You sent [input] to [H] via a secure channel.") - log_admin("[src.owner] replied to [key_name(H)]'s HONKplanet message with the message [input].") - to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Please stand by for a message from your HONKbrothers. Message as follows, HONK. [input]. Message ends, HONK.\"") - - else if(href_list["ErtReply"]) - if(!check_rights(R_ADMIN)) - return - - if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny") - var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"]) - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(H.stat != 0) - to_chat(usr, "The person you are trying to contact is not conscious.") - return - if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "") - if(!input) return - ert_request_answered = TRUE - to_chat(src.owner, "You sent [input] to [H] via a secure channel.") - log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].") - to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Your ERT request has been denied for the following reasons: [input]. Message ends.\"") - else - src.owner.response_team() - - - else if(href_list["AdminFaxView"]) - if(!check_rights(R_ADMIN)) - return - - var/obj/item/fax = locate(href_list["AdminFaxView"]) - if(istype(fax, /obj/item/paper)) - var/obj/item/paper/P = fax - P.show_content(usr,1) - else if(istype(fax, /obj/item/photo)) - var/obj/item/photo/H = fax - H.show(usr) - else if(istype(fax, /obj/item/paper_bundle)) - //having multiple people turning pages on a paper_bundle can cause issues - //open a browse window listing the contents instead - var/data = "" - var/obj/item/paper_bundle/B = fax - - for(var/page = 1, page <= B.amount + 1, page++) - var/obj/pageobj = B.contents[page] - data += "Page [page] - [pageobj.name]
    " - - usr << browse(data, "window=PaperBundle[B.UID()]") - else - to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") - - else if(href_list["AdminFaxViewPage"]) - if(!check_rights(R_ADMIN)) - return - - var/page = text2num(href_list["AdminFaxViewPage"]) - var/obj/item/paper_bundle/bundle = locate(href_list["paper_bundle"]) - - if(!bundle) return - - if(istype(bundle.contents[page], /obj/item/paper)) - var/obj/item/paper/P = bundle.contents[page] - P.show_content(usr, 1) - else if(istype(bundle.contents[page], /obj/item/photo)) - var/obj/item/photo/H = bundle.contents[page] - H.show(usr) - return - - else if(href_list["AdminFaxCreate"]) - if(!check_rights(R_ADMIN)) - return - - var/mob/sender = locate(href_list["AdminFaxCreate"]) - var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) - var/faxtype = href_list["faxtype"] - var/reply_to = locate(href_list["replyto"]) - var/destination - var/notify - - var/obj/item/paper/P = new /obj/item/paper(null) //hopefully the null loc won't cause trouble for us - - if(!fax) - var/list/departmentoptions = alldepartments + hidden_departments + "All Departments" - destination = input(usr, "To which department?", "Choose a department", "") as null|anything in departmentoptions - if(!destination) - qdel(P) - return - - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) - if(destination != "All Departments" && F.department == destination) - fax = F - - - var/input = input(src.owner, "Please enter a message to send a fax via secure connection. Use
    for line breaks. Both pencode and HTML work.", "Outgoing message from Centcomm", "") as message|null - if(!input) - qdel(P) - return - input = admin_pencode_to_html(html_encode(input)) // Encode everything from pencode to html - - var/customname = clean_input("Pick a title for the fax.", "Fax Title", , owner) - if(!customname) - customname = "paper" - - var/stampname - var/stamptype - var/stampvalue - var/sendername - switch(faxtype) - if("Central Command") - stamptype = "icon" - stampvalue = "cent" - sendername = command_name() - if("Syndicate") - stamptype = "icon" - stampvalue = "syndicate" - sendername = "UNKNOWN" - if("Administrator") - stamptype = input(src.owner, "Pick a stamp type.", "Stamp Type") as null|anything in list("icon","text","none") - if(stamptype == "icon") - stampname = input(src.owner, "Pick a stamp icon.", "Stamp Icon") as null|anything in list("centcom","syndicate","granted","denied","clown") - switch(stampname) - if("centcom") - stampvalue = "cent" - if("syndicate") - stampvalue = "syndicate" - if("granted") - stampvalue = "ok" - if("denied") - stampvalue = "deny" - if("clown") - stampvalue = "clown" - else if(stamptype == "text") - stampvalue = clean_input("What should the stamp say?", "Stamp Text", , owner) - else if(stamptype == "none") - stamptype = "" - else - qdel(P) - return - - sendername = clean_input("What organization does the fax come from? This determines the prefix of the paper (i.e. Central Command- Title). This is optional.", "Organization", , owner) - - if(sender) - notify = alert(src.owner, "Would you like to inform the original sender that a fax has arrived?","Notify Sender","Yes","No") - - // Create the reply message - if(sendername) - P.name = "[sendername]- [customname]" - else - P.name = "[customname]" - P.info = input - P.update_icon() - P.x = rand(-2, 0) - P.y = rand(-1, 2) - P.offset_x += P.x - P.offset_y += P.y - if(stamptype) - var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') - stampoverlay.pixel_x = P.x - stampoverlay.pixel_y = P.y - - if(!P.ico) - P.ico = new - P.ico += "paper_stamp-[stampvalue]" - stampoverlay.icon_state = "paper_stamp-[stampvalue]" - - if(stamptype == "icon") - if(!P.stamped) - P.stamped = new - P.stamped += /obj/item/stamp/centcom - P.overlays += stampoverlay - P.stamps += "
    " - - else if(stamptype == "text") - if(!P.stamped) - P.stamped = new - P.stamped += /obj/item/stamp - P.overlays += stampoverlay - P.stamps += "
    [stampvalue]" - - if(destination != "All Departments") - if(!fax.receivefax(P)) - to_chat(src.owner, "Message transmission failed.") - return - else - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) - if(is_station_level(F.z)) - spawn(0) - if(!F.receivefax(P)) - to_chat(src.owner, "Message transmission to [F.department] failed.") - - var/datum/fax/admin/A = new /datum/fax/admin() - A.name = P.name - A.from_department = faxtype - if(destination != "All Departments") - A.to_department = fax.department - else - A.to_department = "All Departments" - A.origin = "Administrator" - A.message = P - A.reply_to = reply_to - A.sent_by = usr - A.sent_at = world.time - - to_chat(src.owner, "Message transmitted successfully.") - if(notify == "Yes") - var/mob/living/carbon/human/H = sender - if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) - to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") - if(sender) - log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]") - message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1) - else - log_admin("[key_name(src.owner)] sent a fax message to [destination]: [input]") - message_admins("[key_name_admin(src.owner)] sent a fax message to [destination] (VIEW).", 1) - return - - else if(href_list["refreshfaxpanel"]) - if(!check_rights(R_ADMIN)) - return - - fax_panel(usr) - - else if(href_list["getplaytimewindow"]) - if(!check_rights(R_ADMIN)) - return - var/mob/M = locateUID(href_list["getplaytimewindow"]) - if(!M) - to_chat(usr, "ERROR: Mob not found.") - return - cmd_mentor_show_exp_panel(M.client) - - else if(href_list["jumpto"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["jumpto"]) - usr.client.jumptomob(M) - - else if(href_list["getmob"]) - if(!check_rights(R_ADMIN)) return - - if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return - var/mob/M = locateUID(href_list["getmob"]) - usr.client.Getmob(M) - - else if(href_list["sendmob"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["sendmob"]) - usr.client.sendmob(M) - - else if(href_list["narrateto"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["narrateto"]) - usr.client.cmd_admin_direct_narrate(M) - - else if(href_list["subtlemessage"]) - if(!check_rights(R_ADMIN)) return - - var/mob/M = locateUID(href_list["subtlemessage"]) - usr.client.cmd_admin_subtle_message(M) - - else if(href_list["traitor"]) - if(!check_rights(R_ADMIN|R_MOD)) return - - if(!SSticker || !SSticker.mode) - alert("The game hasn't started yet!") - return - - var/mob/M = locateUID(href_list["traitor"]) - if(!ismob(M)) - to_chat(usr, "This can only be used on instances of type /mob.") - return - show_traitor_panel(M) - - else if(href_list["create_object"]) - if(!check_rights(R_SPAWN)) return - return create_object(usr) - - else if(href_list["quick_create_object"]) - if(!check_rights(R_SPAWN)) return - return quick_create_object(usr) - - else if(href_list["create_turf"]) - if(!check_rights(R_SPAWN)) return - return create_turf(usr) - - else if(href_list["create_mob"]) - if(!check_rights(R_SPAWN)) return - return create_mob(usr) - - else if(href_list["object_list"]) //this is the laggiest thing ever - if(!check_rights(R_SPAWN)) return - - var/atom/loc = usr.loc - - var/dirty_paths - if(istext(href_list["object_list"])) - dirty_paths = list(href_list["object_list"]) - else if(istype(href_list["object_list"], /list)) - dirty_paths = href_list["object_list"] - - var/paths = list() - - for(var/dirty_path in dirty_paths) - var/path = text2path(dirty_path) - if(!path) - continue - else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) - continue - paths += path - - if(!paths) - alert("The path list you sent is empty") - return - if(length(paths) > 5) - alert("Select fewer object types, (max 5)") - return - - var/list/offset = splittext(href_list["offset"],",") - var/number = dd_range(1, 100, text2num(href_list["object_count"])) - var/X = offset.len > 0 ? text2num(offset[1]) : 0 - var/Y = offset.len > 1 ? text2num(offset[2]) : 0 - var/Z = offset.len > 2 ? text2num(offset[3]) : 0 - var/tmp_dir = href_list["object_dir"] - var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2 - if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10))) - obj_dir = 2 - var/obj_name = sanitize(href_list["object_name"]) - - - var/atom/target //Where the object will be spawned - var/where = href_list["object_where"] - if(!( where in list("onfloor","inhand","inmarked") )) - where = "onfloor" - - - switch(where) - if("inhand") - if(!iscarbon(usr) && !isrobot(usr)) - to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") - where = "onfloor" - target = usr - - if("onfloor") - switch(href_list["offset_type"]) - if("absolute") - target = locate(0 + X,0 + Y,0 + Z) - if("relative") - target = locate(loc.x + X,loc.y + Y,loc.z + Z) - if("inmarked") - if(!marked_datum) - to_chat(usr, "You don't have any object marked. Abandoning spawn.") - return - else if(!istype(marked_datum,/atom)) - to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") - return - else - target = marked_datum - - if(target) - for(var/path in paths) - for(var/i = 0; i < number; i++) - if(path in typesof(/turf)) - var/turf/O = target - var/turf/N = O.ChangeTurf(path) - if(N && obj_name) - N.name = obj_name - else - var/atom/O = new path(target) - if(O) - O.admin_spawned = TRUE - O.dir = obj_dir - if(obj_name) - O.name = obj_name - if(istype(O,/mob)) - var/mob/M = O - M.real_name = obj_name - if(where == "inhand" && isliving(usr) && istype(O, /obj/item)) - var/mob/living/L = usr - var/obj/item/I = O - L.put_in_hands(I) - if(isrobot(L)) - var/mob/living/silicon/robot/R = L - if(R.module) - R.module.modules += I - I.loc = R.module - R.module.rebuild() - R.activate_module(I) - R.module.fix_modules() - - if(number == 1) - log_admin("[key_name(usr)] created a [english_list(paths)]") - for(var/path in paths) - if(ispath(path, /mob)) - message_admins("[key_name_admin(usr)] created a [english_list(paths)]") - break - else - log_admin("[key_name(usr)] created [number]ea [english_list(paths)]") - for(var/path in paths) - if(ispath(path, /mob)) - message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]") - break - return - - else if(href_list["kick_all_from_lobby"]) - if(!check_rights(R_ADMIN)) - return - if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/afkonly = text2num(href_list["afkonly"]) - if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby?","Confirmation","Yes","Cancel") != "Yes") - return - var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by an Administrator.", afkonly) - - var/strkicked = "" - for(var/name in listkicked) - strkicked += "[name], " - message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") - log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") - else - to_chat(usr, "You may only use this when the game is running.") - - else if(href_list["memoeditlist"]) - if(!check_rights(R_SERVER)) return - var/sql_key = sanitizeSQL("[href_list["memoeditlist"]]") - var/DBQuery/query_memoedits = dbcon.NewQuery("SELECT edits FROM [format_table_name("memo")] WHERE (ckey = '[sql_key]')") - if(!query_memoedits.Execute()) - var/err = query_memoedits.ErrorMsg() - log_game("SQL ERROR obtaining edits from memo table. Error : \[[err]\]\n") - return - if(query_memoedits.NextRow()) - var/edit_log = query_memoedits.item[1] - usr << browse(edit_log,"window=memoeditlist") - - else if(href_list["secretsfun"]) - if(!check_rights(R_SERVER|R_EVENT)) return - - var/ok = 0 - switch(href_list["secretsfun"]) - if("sec_clothes") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SC") - for(var/obj/item/clothing/under/O in world) - qdel(O) - ok = 1 - if("sec_all_clothes") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SAC") - for(var/obj/item/clothing/O in world) - qdel(O) - ok = 1 - if("sec_classic1") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SC1") - for(var/obj/item/clothing/suit/fire/O in world) - qdel(O) - for(var/obj/structure/grille/O in world) - qdel(O) - if("monkey") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","M") - for(var/mob/living/carbon/human/H in GLOB.mob_list) - spawn(0) - H.monkeyize() - ok = 1 - if("corgi") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","M") - for(var/mob/living/carbon/human/H in GLOB.mob_list) - spawn(0) - H.corgize() - ok = 1 - if("honksquad") - if(usr.client.honksquad()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","HONK") - if("striketeam") - if(usr.client.strike_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Strike") - if("striketeam_syndicate") - if(usr.client.syndicate_strike_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Strike") - if("infiltrators_syndicate") - if(usr.client.syndicate_infiltration_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SyndieInfiltrationTeam") - if("gimmickteam") - if(usr.client.gimmick_team()) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","GimmickTeam") - if("tripleAI") - usr.client.triple_ai() - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","TriAI") - if("gravity") - if(!(SSticker && SSticker.mode)) - to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") - return - gravity_is_on = !gravity_is_on - for(var/area/A in world) - A.gravitychange(gravity_is_on,A) - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","Grav") - if(gravity_is_on) - log_admin("[key_name(usr)] toggled gravity on.", 1) - message_admins("[key_name_admin(usr)] toggled gravity on.", 1) - event_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") - else - log_admin("[key_name(usr)] toggled gravity off.", 1) - message_admins("[key_name_admin(usr)] toggled gravity off.", 1) - event_announcement.Announce("Feedback surge detected in mass-distributions systems. Artifical gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") - - if("power") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","P") - log_admin("[key_name(usr)] made all areas powered", 1) - message_admins("[key_name_admin(usr)] made all areas powered", 1) - power_restore() - if("unpower") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","UP") - log_admin("[key_name(usr)] made all areas unpowered", 1) - message_admins("[key_name_admin(usr)] made all areas unpowered", 1) - power_failure() - if("quickpower") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","QP") - log_admin("[key_name(usr)] made all SMESs powered", 1) - message_admins("[key_name_admin(usr)] made all SMESs powered", 1) - power_restore_quick() - if("prisonwarp") - if(!SSticker) - alert("The game hasn't started yet!", null, null, null, null, null) - return - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","PW") - message_admins("[key_name_admin(usr)] teleported all players to the prison station.", 1) - for(var/mob/living/carbon/human/H in GLOB.mob_list) - var/turf/loc = find_loc(H) - var/security = 0 - if(!is_station_level(loc.z) || prisonwarped.Find(H)) - -//don't warp them if they aren't ready or are already there - continue - H.Paralyse(5) - if(H.wear_id) - var/obj/item/card/id/id = H.get_idcard() - for(var/A in id.access) - if(A == ACCESS_SECURITY) - security++ - if(!security) - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/W in H) - if(istype(W, /obj/item/organ/external)) - continue - //don't strip organs - H.unEquip(W) - if(H.client) - H.client.screen -= W - if(W) - W.loc = H.loc - W.dropped(H) - W.layer = initial(W.layer) - W.plane = initial(W.plane) - //teleport person to cell - H.loc = pick(prisonwarp) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) - else - //teleport security person - H.loc = pick(prisonsecuritywarp) - prisonwarped += H - if("traitor_all") - if(!SSticker) - alert("The game hasn't started yet!") - return - var/objective = sanitize(copytext(input("Enter an objective"),1,MAX_MESSAGE_LEN)) - if(!objective) - return - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","TA([objective])") - - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == 2 || !H.client || !H.mind) continue - if(is_special_character(H)) continue - //traitorize(H, objective, 0) - H.mind.add_antag_datum(/datum/antagonist/traitor) - - for(var/mob/living/silicon/A in GLOB.player_list) - A.mind.add_antag_datum(/datum/antagonist/traitor) - - message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]", 1) - log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]") - - if("togglebombcap") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","BC") - - var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be between 4 and 128)", "New Bomb Cap", MAX_EX_LIGHT_RANGE) as num|null - if(newBombCap < 4) - return - if(newBombCap > 128) - newBombCap = 128 - - MAX_EX_DEVASTATION_RANGE = round(newBombCap/4) - MAX_EX_HEAVY_RANGE = round(newBombCap/2) - MAX_EX_LIGHT_RANGE = newBombCap - //I don't know why these are their own variables, but fuck it, they are. - MAX_EX_FLASH_RANGE = newBombCap - MAX_EX_FLAME_RANGE = newBombCap - - message_admins("[key_name_admin(usr)] changed the bomb cap to [MAX_EX_DEVASTATION_RANGE], [MAX_EX_HEAVY_RANGE], [MAX_EX_LIGHT_RANGE]") - log_admin("[key_name(usr)] changed the bomb cap to [MAX_EX_DEVASTATION_RANGE], [MAX_EX_HEAVY_RANGE], [MAX_EX_LIGHT_RANGE]") - - if("flicklights") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","FL") - while(!usr.stat) -//knock yourself out to stop the ghosts - for(var/mob/M in GLOB.player_list) - if(M.stat != 2 && prob(25)) - var/area/AffectedArea = get_area(M) - if(AffectedArea.name != "Space" && AffectedArea.name != "Engine Walls" && AffectedArea.name != "Chemical Lab Test Chamber" && AffectedArea.name != "Escape Shuttle" && AffectedArea.name != "Arrival Area" && AffectedArea.name != "Arrival Shuttle" && AffectedArea.name != "start area" && AffectedArea.name != "Engine Combustion Chamber") - AffectedArea.power_light = 0 - AffectedArea.power_change() - spawn(rand(55,185)) - AffectedArea.power_light = 1 - AffectedArea.power_change() - var/Message = rand(1,4) - switch(Message) - if(1) - M.show_message(text("You shudder as if cold..."), 1) - if(2) - M.show_message(text("You feel something gliding across your back..."), 1) - if(3) - M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) - if(4) - M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) - for(var/obj/W in orange(5,M)) - if(prob(25) && !W.anchored) - step_rand(W) - sleep(rand(100,1000)) - for(var/mob/M in GLOB.player_list) - if(M.stat != 2) - M.show_message(text("The chilling wind suddenly stops..."), 1) - if("lightout") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","LO") - message_admins("[key_name_admin(usr)] has broke a lot of lights", 1) - var/datum/event/electrical_storm/E = new /datum/event/electrical_storm - E.lightsoutAmount = 2 - if("blackout") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","BO") - message_admins("[key_name_admin(usr)] broke all lights", 1) - for(var/obj/machinery/light/L in GLOB.machines) - L.break_light_tube() - if("whiteout") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","WO") - message_admins("[key_name_admin(usr)] fixed all lights", 1) - for(var/obj/machinery/light/L in GLOB.machines) - L.fix() - if("floorlava") - feedback_inc("admin_secrets_fun_used", 1) - feedback_add_details("admin_secrets_fun_used", "LF") - var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") - if(sure == "No") - return - SSweather.run_weather(/datum/weather/floor_is_lava) - message_admins("[key_name_admin(usr)] made the floor lava") - if("fakelava") - feedback_inc("admin_secrets_fun_used", 1) - feedback_add_details("admin_secrets_fun_used", "LZ") - var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") - if(sure == "No") - return - SSweather.run_weather(/datum/weather/floor_is_lava/fake) - message_admins("[key_name_admin(usr)] made aesthetic lava on the floor") - if("weatherashstorm") - feedback_inc("admin_secrets_fun_used", 1) - feedback_add_details("admin_secrets_fun_used", "WA") - var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") - if(sure == "No") - return - SSweather.run_weather(/datum/weather/ash_storm) - message_admins("[key_name_admin(usr)] spawned an ash storm on the mining level") - if("retardify") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","RET") - for(var/mob/living/carbon/human/H in GLOB.player_list) - to_chat(H, "You suddenly feel stupid.") - H.setBrainLoss(60) - message_admins("[key_name_admin(usr)] made everybody retarded") - if("fakeguns") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","FG") - for(var/obj/item/W in world) - if(istype(W, /obj/item/clothing) || istype(W, /obj/item/card/id) || istype(W, /obj/item/disk) || istype(W, /obj/item/tank)) - continue - W.icon = 'icons/obj/guns/projectile.dmi' - W.icon_state = "revolver" - W.item_state = "gun" - message_admins("[key_name_admin(usr)] made every item look like a gun") - if("schoolgirl") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SG") - for(var/obj/item/clothing/under/W in world) - W.icon_state = "schoolgirl" - W.item_state = "w_suit" - W.item_color = "schoolgirl" - message_admins("[key_name_admin(usr)] activated Japanese Animes mode") - world << sound('sound/AI/animes.ogg') - if("eagles")//SCRAW - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","EgL") - for(var/obj/machinery/door/airlock/W in GLOB.airlocks) - if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison)) - W.req_access = list() - message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") - event_announcement.Announce("Centcomm airlock control override activated. Please take this time to get acquainted with your coworkers.", new_sound = 'sound/AI/commandreport.ogg') - if("onlyone") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","OO") - usr.client.only_one() -// message_admins("[key_name_admin(usr)] has triggered HIGHLANDER") - if("onlyme") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","OM") - usr.client.only_me() - if("onlyoneteam") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","OOT") - usr.client.only_one_team() -// message_admins("[key_name_admin(usr)] has triggered ") - if("rolldice") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ROL") - usr.client.roll_dices() - if("guns") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SG") - var/survivor_probability = 0 - switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) - if("Some Antags") - survivor_probability = 25 - if("All Antags!") - survivor_probability = 100 - - rightandwrong(SUMMON_GUNS, usr, survivor_probability) - if("magic") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","SM") - var/survivor_probability = 0 - switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) - if("Some Antags") - survivor_probability = 25 - if("All Antags!") - survivor_probability = 100 - - rightandwrong(SUMMON_MAGIC, usr, survivor_probability) - if("tdomereset") - var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel") - if(delete_mobs == "Cancel") - return - - var/area/thunderdome = locate(/area/tdome/arena) - if(delete_mobs == "Yes") - for(var/mob/living/mob in thunderdome) - qdel(mob) //Clear mobs - for(var/obj/obj in thunderdome) - if(!istype(obj,/obj/machinery/camera)) - qdel(obj) //Clear objects - - var/area/template = locate(/area/tdome/arena_source) - template.copy_contents_to(thunderdome) - - log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1) - message_admins("[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].") - - if("tdomestart") - var/confirmation = alert("Start a Thunderdome match?","Confirm","Yes","No") - if(confirmation == "No") - return - if(makeThunderdomeTeams()) - log_admin("[key_name(usr)] started a Thunderdome match!", 1) - message_admins("[key_name_admin(usr)] has started a Thunderdome match!") - else - log_admin("[key_name(usr)] attempted to start a Thunderdome match, but no ghosts signed up.", 1) - message_admins("[key_name_admin(usr)] tried starting a Thunderdome match, but no ghosts signed up.") - if("securitylevel0") - set_security_level(0) - message_admins("[key_name_admin(usr)] change security level to Green.", 1) - if("securitylevel1") - set_security_level(1) - message_admins("[key_name_admin(usr)] change security level to Blue.", 1) - if("securitylevel2") - set_security_level(2) - message_admins("[key_name_admin(usr)] change security level to Red.", 1) - if("securitylevel3") - set_security_level(3) - message_admins("[key_name_admin(usr)] change security level to Gamma.", 1) - if("securitylevel4") - set_security_level(4) - message_admins("[key_name_admin(usr)] change security level to Epsilon.", 1) - if("securitylevel5") - set_security_level(5) - message_admins("[key_name_admin(usr)] change security level to Delta.", 1) - if("moveminingshuttle") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShM") - if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away")) - message_admins("[key_name_admin(usr)] moved mining shuttle") - log_admin("[key_name(usr)] moved the mining shuttle") - - if("movelaborshuttle") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShL") - if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away")) - message_admins("[key_name_admin(usr)] moved labor shuttle") - log_admin("[key_name(usr)] moved the labor shuttle") - - if("moveferry") - feedback_inc("admin_secrets_fun_used",1) - feedback_add_details("admin_secrets_fun_used","ShF") - if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away")) - message_admins("[key_name_admin(usr)] moved the centcom ferry") - log_admin("[key_name(usr)] moved the centcom ferry") - - if(usr) - log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") - if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) - - else if(href_list["secretsadmin"]) - if(!check_rights(R_ADMIN)) return - - var/ok = 0 - switch(href_list["secretsadmin"]) - if("list_signalers") - var/dat = "Showing last [length(lastsignalers)] signalers.
    " - for(var/sig in lastsignalers) - dat += "[sig]
    " - usr << browse(dat, "window=lastsignalers;size=800x500") - if("list_lawchanges") - var/dat = "Showing last [length(lawchanges)] law changes.
    " - for(var/sig in lawchanges) - dat += "[sig]
    " - usr << browse(dat, "window=lawchanges;size=800x500") - if("list_job_debug") - var/dat = "Job Debug info.
    " - if(SSjobs) - for(var/line in SSjobs.job_debug) - dat += "[line]
    " - dat+= "*******

    " - for(var/datum/job/job in SSjobs.occupations) - if(!job) continue - dat += "job: [job.title], current_positions: [job.current_positions], total_positions: [job.total_positions]
    " - usr << browse(dat, "window=jobdebug;size=600x500") - if("showailaws") - output_ai_laws() - if("showgm") - if(!SSticker) - alert("The game hasn't started yet!") - else if(SSticker.mode) - alert("The game mode is [SSticker.mode.name]") - else alert("For some reason there's a ticker, but not a game mode") - if("manifest") - var/dat = "Showing Crew Manifest.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(H.ckey) - dat += text("", H.name, H.get_assignment()) - dat += "
    NamePosition
    [][]
    " - usr << browse(dat, "window=manifest;size=440x410") - if("check_antagonist") - check_antagonists() - if("DNA") - var/dat = "Showing DNA from blood.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(H.dna && H.ckey) - dat += "" - dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " - usr << browse(dat, "window=DNA;size=440x410") - if("fingerprints") - var/dat = "Showing Fingerprints.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(H.ckey) - if(H.dna && H.dna.uni_identity) - dat += "" - else if(H.dna && !H.dna.uni_identity) - dat += "" - else if(!H.dna) - dat += "" - dat += "
    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    [H]H.dna.uni_identity = null
    [H]H.dna = null
    " - usr << browse(dat, "window=fingerprints;size=440x410") - if("night_shift_set") - var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") - switch(val) - if("Automatic") - if(config.enable_night_shifts) - SSnightshift.can_fire = TRUE - SSnightshift.fire() - else - SSnightshift.update_nightshift(FALSE, TRUE) - to_chat(usr, "Night shift set to automatic.") - if("On") - SSnightshift.can_fire = FALSE - SSnightshift.update_nightshift(TRUE, FALSE) - to_chat(usr, "Night shift forced on.") - if("Off") - SSnightshift.can_fire = FALSE - SSnightshift.update_nightshift(FALSE, FALSE) - to_chat(usr, "Night shift forced off.") - else - if(usr) - log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") - if(ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) - - else if(href_list["secretscoder"]) - if(!check_rights(R_DEBUG)) return - - switch(href_list["secretscoder"]) - if("spawn_objects") - var/dat = "Admin Log
    " - for(var/l in admin_log) - dat += "
  • [l]
  • " - if(!admin_log.len) - dat += "No-one has done anything this round!" - usr << browse(dat, "window=admin_log") - if("maint_ACCESS_BRIG") - for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) - if(ACCESS_MAINT_TUNNELS in M.req_access) - M.req_access = list(ACCESS_BRIG) - message_admins("[key_name_admin(usr)] made all maint doors brig access-only.") - if("maint_access_engiebrig") - for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) - if(ACCESS_MAINT_TUNNELS in M.req_access) - M.req_access = list() - M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE) - message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.") - if("infinite_sec") - var/datum/job/J = SSjobs.GetJob("Security Officer") - if(!J) return - J.total_positions = -1 - J.spawn_positions = -1 - message_admins("[key_name_admin(usr)] has removed the cap on security officers.") - - else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here - src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. - src.access_news_network() - - else if(href_list["ac_set_channel_name"]) - src.admincaster_feed_channel.channel_name = strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) - while(findtext(src.admincaster_feed_channel.channel_name," ") == 1) - src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) - src.access_news_network() - - else if(href_list["ac_set_channel_lock"]) - src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked - src.access_news_network() - - else if(href_list["ac_submit_new_channel"]) - var/check = 0 - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - check = 1 - break - if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) - src.admincaster_screen=7 - else - var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel") - if(choice=="Confirm") - var/datum/feed_channel/newChannel = new /datum/feed_channel - newChannel.channel_name = src.admincaster_feed_channel.channel_name - newChannel.author = src.admincaster_signature - newChannel.locked = src.admincaster_feed_channel.locked - newChannel.is_admin_channel = 1 - feedback_inc("newscaster_channels",1) - news_network.network_channels += newChannel //Adding channel to the global network - log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") - src.admincaster_screen=5 - src.access_news_network() - - else if(href_list["ac_set_channel_receiving"]) - var/list/available_channels = list() - for(var/datum/feed_channel/F in news_network.network_channels) - available_channels += F.channel_name - src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels ) - src.access_news_network() - - else if(href_list["ac_set_new_message"]) - src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story", "Network Channel Handler", "")) - while(findtext(src.admincaster_feed_message.body," ") == 1) - src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) - src.access_news_network() - - else if(href_list["ac_submit_new_message"]) - if(src.admincaster_feed_message.body =="" || src.admincaster_feed_message.body =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) - src.admincaster_screen = 6 - else - var/datum/feed_message/newMsg = new /datum/feed_message - newMsg.author = src.admincaster_signature - newMsg.body = src.admincaster_feed_message.body - newMsg.is_admin_message = 1 - feedback_inc("newscaster_stories",1) - for(var/datum/feed_channel/FC in news_network.network_channels) - if(FC.channel_name == src.admincaster_feed_channel.channel_name) - FC.messages += newMsg //Adding message to the network's appropriate feed_channel - break - src.admincaster_screen=4 - - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) - - log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") - src.access_news_network() - - else if(href_list["ac_create_channel"]) - src.admincaster_screen=2 - src.access_news_network() - - else if(href_list["ac_create_feed_story"]) - src.admincaster_screen=3 - src.access_news_network() - - else if(href_list["ac_menu_censor_story"]) - src.admincaster_screen=10 - src.access_news_network() - - else if(href_list["ac_menu_censor_channel"]) - src.admincaster_screen=11 - src.access_news_network() - - else if(href_list["ac_menu_wanted"]) - var/already_wanted = 0 - if(news_network.wanted_issue) - already_wanted = 1 - - if(already_wanted) - src.admincaster_feed_message.author = news_network.wanted_issue.author - src.admincaster_feed_message.body = news_network.wanted_issue.body - src.admincaster_screen = 14 - src.access_news_network() - - else if(href_list["ac_set_wanted_name"]) - src.admincaster_feed_message.author = adminscrub(input(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) - while(findtext(src.admincaster_feed_message.author," ") == 1) - src.admincaster_feed_message.author = copytext(admincaster_feed_message.author,2,length(admincaster_feed_message.author)+1) - src.access_news_network() - - else if(href_list["ac_set_wanted_desc"]) - src.admincaster_feed_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) - while(findtext(src.admincaster_feed_message.body," ") == 1) - src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) - src.access_news_network() - - else if(href_list["ac_submit_wanted"]) - var/input_param = text2num(href_list["ac_submit_wanted"]) - if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") - src.admincaster_screen = 16 - else - var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below - var/datum/feed_message/WANTED = new /datum/feed_message - WANTED.author = src.admincaster_feed_message.author //Wanted name - WANTED.body = src.admincaster_feed_message.body //Wanted desc - WANTED.backup_author = src.admincaster_signature //Submitted by - WANTED.is_admin_message = 1 - news_network.wanted_issue = WANTED - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.newsAlert() - NEWSCASTER.update_icon() - src.admincaster_screen = 15 - else - news_network.wanted_issue.author = src.admincaster_feed_message.author - news_network.wanted_issue.body = src.admincaster_feed_message.body - news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author - src.admincaster_screen = 19 - log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") - src.access_news_network() - - else if(href_list["ac_cancel_wanted"]) - var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel") - if(choice=="Confirm") - news_network.wanted_issue = null - for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) - NEWSCASTER.update_icon() - src.admincaster_screen=17 - src.access_news_network() - - else if(href_list["ac_censor_channel_author"]) - var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) - if(FC.author != "\[REDACTED\]") - FC.backup_author = FC.author - FC.author = "\[REDACTED\]" - else - FC.author = FC.backup_author - src.access_news_network() - - else if(href_list["ac_censor_channel_story_author"]) - var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) - if(MSG.author != "\[REDACTED\]") - MSG.backup_author = MSG.author - MSG.author = "\[REDACTED\]" - else - MSG.author = MSG.backup_author - src.access_news_network() - - else if(href_list["ac_censor_channel_story_body"]) - var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) - if(MSG.body != "\[REDACTED\]") - MSG.backup_body = MSG.body - MSG.body = "\[REDACTED\]" - else - MSG.body = MSG.backup_body - src.access_news_network() - - else if(href_list["ac_pick_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) - src.admincaster_feed_channel = FC - src.admincaster_screen=13 - src.access_news_network() - - else if(href_list["ac_toggle_d_notice"]) - var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) - FC.censored = !FC.censored - src.access_news_network() - - else if(href_list["ac_view"]) - src.admincaster_screen=1 - src.access_news_network() - - else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ - src.admincaster_screen = text2num(href_list["ac_setScreen"]) - if(src.admincaster_screen == 0) - if(src.admincaster_feed_channel) - src.admincaster_feed_channel = new /datum/feed_channel - if(src.admincaster_feed_message) - src.admincaster_feed_message = new /datum/feed_message - src.access_news_network() - - else if(href_list["ac_show_channel"]) - var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 9 - src.access_news_network() - - else if(href_list["ac_pick_censor_channel"]) - var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) - src.admincaster_feed_channel = FC - src.admincaster_screen = 12 - src.access_news_network() - - else if(href_list["ac_refresh"]) - src.access_news_network() - - else if(href_list["ac_set_signature"]) - src.admincaster_signature = adminscrub(input(usr, "Provide your desired signature", "Network Identity Handler", "")) - src.access_news_network() - - if(href_list["secretsmenu"]) - switch(href_list["secretsmenu"]) - if("tab") - current_tab = text2num(href_list["tab"]) - Secrets(usr) - return 1 - - else if(href_list["viewruntime"]) - var/datum/ErrorViewer/error_viewer = locateUID(href_list["viewruntime"]) - if(!istype(error_viewer)) - to_chat(usr, "That runtime viewer no longer exists.") - return - if(href_list["viewruntime_backto"]) - error_viewer.showTo(usr, locateUID(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) - else - error_viewer.showTo(usr, null, href_list["viewruntime_linear"]) - - else if(href_list["add_station_goal"]) - if(!check_rights(R_EVENT)) - return - var/list/type_choices = typesof(/datum/station_goal) - var/picked = input("Choose goal type") in type_choices|null - if(!picked) - return - var/datum/station_goal/G = new picked() - if(picked == /datum/station_goal) - var/newname = clean_input("Enter goal name:") - if(!newname) - return - G.name = newname - var/description = input("Enter [command_name()] message contents:") as message|null - if(!description) - return - G.report_message = description - message_admins("[key_name_admin(usr)] created \"[G.name]\" station goal.") - SSticker.mode.station_goals += G - modify_goals() - - else if(href_list["showdetails"]) - if(!check_rights(R_ADMIN)) - return - var/text = html_decode(href_list["showdetails"]) - usr << browse("Details[replacetext(text, "\n", "
    ")]
    ", - "window=show_details;size=500x200") - - // Library stuff - else if(href_list["library_book_id"]) - var/isbn = sanitizeSQL(href_list["library_book_id"]) - - if(href_list["view_library_book"]) - var/DBQuery/query_view_book = dbcon.NewQuery("SELECT content, title FROM [format_table_name("library")] WHERE id=[isbn]") - if(!query_view_book.Execute()) - var/err = query_view_book.ErrorMsg() - log_game("SQL ERROR viewing book. Error : \[[err]\]\n") - return - - var/content = "" - var/title = "" - while(query_view_book.NextRow()) - content = query_view_book.item[1] - title = html_encode(query_view_book.item[2]) - - var/dat = "
    "
    -			dat += "[html_encode(html_to_pencode(content))]"
    -			dat += "
    " - - var/datum/browser/popup = new(usr, "admin_view_book", "[title]", 700, 400) - popup.set_content(dat) - popup.open(0) - - log_admin("[key_name(usr)] has viewed the book [isbn].") - message_admins("[key_name_admin(usr)] has viewed the book [isbn].") - return - - else if(href_list["unflag_library_book"]) - var/DBQuery/query_unflag_book = dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = 0 WHERE id=[isbn]") - if(!query_unflag_book.Execute()) - var/err = query_unflag_book.ErrorMsg() - log_game("SQL ERROR unflagging book. Error : \[[err]\]\n") - return - - log_admin("[key_name(usr)] has unflagged the book [isbn].") - message_admins("[key_name_admin(usr)] has unflagged the book [isbn].") - - else if(href_list["delete_library_book"]) - var/DBQuery/query_delbook = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[isbn]") - if(!query_delbook.Execute()) - var/err = query_delbook.ErrorMsg() - log_game("SQL ERROR deleting book. Error : \[[err]\]\n") - return - - log_admin("[key_name(usr)] has deleted the book [isbn].") - message_admins("[key_name_admin(usr)] has deleted the book [isbn].") - - // Refresh the page - src.view_flagged_books() - - // Force unlink a discord key - else if(href_list["force_discord_unlink"]) - if(!check_rights(R_ADMIN)) - return - var/target_ckey = href_list["force_discord_unlink"] - var/DBQuery/admin_unlink_discord_id = dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[target_ckey]'") - if(!admin_unlink_discord_id.Execute()) - var/err = admin_unlink_discord_id.ErrorMsg() - log_game("SQL ERROR while admin-unlinking discord account. Error : \[[err]\]\n") - return - to_chat(src, "Successfully forcefully unlinked discord account from [target_ckey]") - message_admins("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") - log_admin("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") - - else if(href_list["create_outfit_finalize"]) - if(!check_rights(R_EVENT)) - return - create_outfit_finalize(usr,href_list) - else if(href_list["load_outfit"]) - if(!check_rights(R_EVENT)) - return - load_outfit(usr) - else if(href_list["create_outfit_menu"]) - if(!check_rights(R_EVENT)) - return - create_outfit(usr) - else if(href_list["delete_outfit"]) - if(!check_rights(R_EVENT)) - return - var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits - delete_outfit(usr,O) - else if(href_list["save_outfit"]) - if(!check_rights(R_EVENT)) - return - var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits - save_outfit(usr,O) - -/client/proc/create_eventmob_for(var/mob/living/carbon/human/H, var/killthem = 0) - if(!check_rights(R_EVENT)) - return - var/admin_outfits = subtypesof(/datum/outfit/admin) - var/hunter_outfits = list() - for(var/type in admin_outfits) - var/datum/outfit/admin/O = type - hunter_outfits[initial(O.name)] = type - var/dresscode = input("Select type", "Contracted Agents") as null|anything in hunter_outfits - if(isnull(dresscode)) - return - var/datum/outfit/O = hunter_outfits[dresscode] - message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") - var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) - if(!candidates.len) - to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") - return - var/mob/C = pick(candidates) - var/key_of_hunter = C.key - if(!key_of_hunter) - to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") - return - var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) - hunter_mind.active = 1 - var/mob/living/carbon/human/hunter_mob = new /mob/living/carbon/human(pick(latejoin)) - hunter_mind.transfer_to(hunter_mob) - hunter_mob.equipOutfit(O, FALSE) - var/obj/item/pinpointer/advpinpointer/N = new /obj/item/pinpointer/advpinpointer(hunter_mob) - hunter_mob.equip_to_slot_or_del(N, slot_in_backpack) - N.active = 1 - N.mode = 2 - N.target = H - N.point_at(N.target) - if(!locate(/obj/item/implant/dust, hunter_mob)) - var/obj/item/implant/dust/D = new /obj/item/implant/dust(hunter_mob) - D.implant(hunter_mob) - if(killthem) - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = hunter_mind - kill_objective.target = H.mind - kill_objective.explanation_text = "Kill [H.real_name], the [H.mind.assigned_role]." - hunter_mind.objectives += kill_objective - else - var/datum/objective/protect/protect_objective = new - protect_objective.owner = hunter_mind - protect_objective.target = H.mind - protect_objective.explanation_text = "Protect [H.real_name], the [H.mind.assigned_role]." - hunter_mind.objectives += protect_objective - SSticker.mode.traitors |= hunter_mob.mind - to_chat(hunter_mob, "ATTENTION: You are now on a mission!") - to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. "); - if(killthem) - to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); - hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR - var/datum/atom_hud/antag/tatorhud = huds[ANTAG_HUD_TRAITOR] - tatorhud.join_hud(hunter_mob) - set_antag_hud(hunter_mob, "hudsyndicate") - -/proc/admin_jump_link(var/atom/target) - if(!target) return - // The way admin jump links handle their src is weirdly inconsistent... - - . = ADMIN_FLW(target,"FLW") - if(isAI(target)) // AI core/eye follow links - var/mob/living/silicon/ai/A = target - if(A.client && A.eyeobj) // No point following clientless AI eyes - . += "|[ADMIN_FLW(A.eyeobj,"EYE")]" - else if(istype(target, /mob/dead/observer)) - var/mob/dead/observer/O = target - if(O.mind && O.mind.current) - . += "|[ADMIN_FLW(O.mind.current,"BDY")]" +/datum/admins/Topic(href, href_list) + ..() + + if(usr.client != src.owner || !check_rights(0)) + log_admin("[key_name(usr)] tried to use the admin panel without authorization.") + message_admins("[key_name_admin(usr)] has attempted to override the admin panel!") + return + + if(SSticker.mode && SSticker.mode.check_antagonists_topic(href, href_list)) + check_antagonists() + return + + if(href_list["rejectadminhelp"]) + if(!check_rights(R_ADMIN|R_MOD)) + return + var/client/C = locateUID(href_list["rejectadminhelp"]) + if(!C) + return + + C << 'sound/effects/adminhelp.ogg' + + to_chat(C, "- AdminHelp Rejected! -") + to_chat(C, "Your admin help was rejected.") + to_chat(C, "Please try to be calm, clear, and descriptive in admin helps, do not assume the admin has seen any related events, and clearly state the names of anybody you are reporting. If you asked a question, please ensure it was clear what you were asking.") + + message_admins("[key_name_admin(usr)] rejected [key_name_admin(C.mob)]'s admin help") + log_admin("[key_name(usr)] rejected [key_name(C.mob)]'s admin help") + + if(href_list["openadminticket"]) + if(!check_rights(R_ADMIN)) + return + var/ticketID = text2num(href_list["openadminticket"]) + SStickets.showDetailUI(usr, ticketID) + + if(href_list["openmentorticket"]) + if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) + return + var/ticketID = text2num(href_list["openmentorticket"]) + SSmentor_tickets.showDetailUI(usr, ticketID) + + if(href_list["stickyban"]) + stickyban(href_list["stickyban"],href_list) + + if(href_list["makeAntag"]) + switch(href_list["makeAntag"]) + if("1") + log_admin("[key_name(usr)] has spawned a traitor.") + if(!makeTraitors()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("2") + log_admin("[key_name(usr)] has spawned a changeling.") + if(!makeChangelings()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("3") + log_admin("[key_name(usr)] has spawned revolutionaries.") + if(!makeRevs()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("4") + log_admin("[key_name(usr)] has spawned a cultists.") + if(!makeCult()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("5") + log_admin("[key_name(usr)] has spawned a wizard.") + if(!makeWizard()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("6") + log_admin("[key_name(usr)] has spawned vampires.") + if(!makeVampires()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("7") + log_admin("[key_name(usr)] has spawned vox raiders.") + if(!makeVoxRaiders()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + if("8") + log_admin("[key_name(usr)] has spawned an abductor team.") + if(!makeAbductorTeam()) + to_chat(usr, "Unfortunately there weren't enough candidates available.") + + else if(href_list["dbsearchckey"] || href_list["dbsearchadmin"] || href_list["dbsearchip"] || href_list["dbsearchcid"] || href_list["dbsearchbantype"]) + var/adminckey = href_list["dbsearchadmin"] + var/playerckey = href_list["dbsearchckey"] + var/playerip = href_list["dbsearchip"] + var/playercid = href_list["dbsearchcid"] + var/dbbantype = text2num(href_list["dbsearchbantype"]) + var/match = 0 + + if("dbmatch" in href_list) + match = 1 + + DB_ban_panel(playerckey, adminckey, playerip, playercid, dbbantype, match) + return + + else if(href_list["dbbanedit"]) + var/banedit = href_list["dbbanedit"] + var/banid = text2num(href_list["dbbanid"]) + if(!banedit || !banid) + return + + DB_ban_edit(banid, banedit) + return + + else if(href_list["dbbanaddtype"]) + + var/bantype = text2num(href_list["dbbanaddtype"]) + var/banckey = href_list["dbbanaddckey"] + var/banip = href_list["dbbanaddip"] + var/bancid = href_list["dbbanaddcid"] + var/banduration = text2num(href_list["dbbaddduration"]) + var/banjob = href_list["dbbanaddjob"] + var/banreason = href_list["dbbanreason"] + + banckey = ckey(banckey) + + switch(bantype) + if(BANTYPE_PERMA) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_TEMP) + if(!banckey || !banreason || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + return + banjob = null + if(BANTYPE_JOB_PERMA) + if(!banckey || !banreason || !banjob) + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + return + banduration = null + if(BANTYPE_JOB_TEMP) + if(!banckey || !banreason || !banjob || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and job)") + return + if(BANTYPE_APPEARANCE) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_ADMIN_PERMA) + if(!banckey || !banreason) + to_chat(usr, "Not enough parameters (Requires ckey and reason)") + return + banduration = null + banjob = null + if(BANTYPE_ADMIN_TEMP) + if(!banckey || !banreason || !banduration) + to_chat(usr, "Not enough parameters (Requires ckey, reason and duration)") + return + banjob = null + + var/mob/playermob + + for(var/mob/M in GLOB.player_list) + if(M.ckey == banckey) + playermob = M + break + + + banreason = "(MANUAL BAN) "+banreason + + if(!playermob) + if(banip) + banreason = "[banreason] (CUSTOM IP)" + if(bancid) + banreason = "[banreason] (CUSTOM CID)" + else + message_admins("Ban process: A mob matching [playermob.ckey] was found at location [playermob.x], [playermob.y], [playermob.z]. Custom IP and computer id fields replaced with the IP and computer id from the located mob") + + DB_ban_record(bantype, playermob, banduration, banreason, banjob, null, banckey, banip, bancid ) + + + else if(href_list["editrights"]) + if(!check_rights(R_PERMISSIONS)) + message_admins("[key_name_admin(usr)] attempted to edit the admin permissions without sufficient rights.") + log_admin("[key_name(usr)] attempted to edit the admin permissions without sufficient rights.") + return + + var/adm_ckey + + var/task = href_list["editrights"] + if(task == "add") + var/new_ckey = ckey(clean_input("New admin's ckey","Admin ckey", null)) + if(!new_ckey) return + if(new_ckey in GLOB.admin_datums) + to_chat(usr, "Error: Topic 'editrights': [new_ckey] is already an admin") + return + adm_ckey = new_ckey + task = "rank" + else if(task != "show") + adm_ckey = ckey(href_list["ckey"]) + if(!adm_ckey) + to_chat(usr, "Error: Topic 'editrights': No valid ckey") + return + + var/datum/admins/D = GLOB.admin_datums[adm_ckey] + + if(task == "remove") + if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes") + if(!D) return + GLOB.admin_datums -= adm_ckey + D.disassociate() + + updateranktodb(adm_ckey, "player") + message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") + log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") + log_admin_rank_modification(adm_ckey, "Removed") + + else if(task == "rank") + var/new_rank + if(GLOB.admin_ranks.len) + new_rank = input("Please select a rank", "New rank", null, null) as null|anything in (GLOB.admin_ranks|"*New Rank*") + else + new_rank = input("Please select a rank", "New rank", null, null) as null|anything in list("Mentor", "Trial Admin", "Game Admin", "*New Rank*") + + var/rights = 0 + if(D) + rights = D.rights + switch(new_rank) + if(null,"") return + if("*New Rank*") + new_rank = input("Please input a new rank", "New custom rank", null, null) as null|text + if(config.admin_legacy_system) + new_rank = ckeyEx(new_rank) + if(!new_rank) + to_chat(usr, "Error: Topic 'editrights': Invalid rank") + return + if(config.admin_legacy_system) + if(GLOB.admin_ranks.len) + if(new_rank in GLOB.admin_ranks) + rights = GLOB.admin_ranks[new_rank] //we typed a rank which already exists, use its rights + else + GLOB.admin_ranks[new_rank] = 0 //add the new rank to admin_ranks + else + if(config.admin_legacy_system) + new_rank = ckeyEx(new_rank) + rights = GLOB.admin_ranks[new_rank] //we input an existing rank, use its rights + + if(D) + D.disassociate() //remove adminverbs and unlink from client + D.rank = new_rank //update the rank + D.rights = rights //update the rights based on admin_ranks (default: 0) + else + D = new /datum/admins(new_rank, rights, adm_ckey) + + var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in) + D.associate(C) //link up with the client and add verbs + + updateranktodb(adm_ckey, new_rank) + message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin_rank_modification(adm_ckey, new_rank) + + else if(task == "permissions") + if(!D) return + while(TRUE) + var/list/permissionlist = list() + for(var/i=1, i<=R_MAXPERMISSION, i<<=1) //that <<= is shorthand for i = i << 1. Which is a left bitshift + permissionlist[rights2text(i)] = i + var/new_permission = input("Select a permission to turn on/off", adm_ckey + "'s Permissions", null, null) as null|anything in permissionlist + if(!new_permission) + return + var/oldrights = D.rights + var/toggleresult = "ON" + D.rights ^= permissionlist[new_permission] + if(oldrights > D.rights) + toggleresult = "OFF" + + message_admins("[key_name_admin(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") + log_admin("[key_name(usr)] toggled the [new_permission] permission of [adm_ckey] to [toggleresult]") + log_admin_permission_modification(adm_ckey, permissionlist[new_permission]) + + + edit_admin_permissions() + + else if(href_list["call_shuttle"]) + if(!check_rights(R_ADMIN)) return + + + switch(href_list["call_shuttle"]) + if("1") + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + SSshuttle.emergency.request() + log_admin("[key_name(usr)] called the Emergency Shuttle") + message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") + + if("2") + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + switch(SSshuttle.emergency.mode) + if(SHUTTLE_CALL) + SSshuttle.emergency.cancel() + log_admin("[key_name(usr)] sent the Emergency Shuttle back") + message_admins("[key_name_admin(usr)] sent the Emergency Shuttle back") + else + SSshuttle.emergency.cancel() + log_admin("[key_name(usr)] called the Emergency Shuttle") + message_admins("[key_name_admin(usr)] called the Emergency Shuttle to the station") + + + href_list["secrets"] = "check_antagonist" + + else if(href_list["edit_shuttle_time"]) + if(!check_rights(R_SERVER)) return + + var/timer = input("Enter new shuttle duration (seconds):","Edit Shuttle Timeleft", SSshuttle.emergency.timeLeft() ) as num + SSshuttle.emergency.setTimer(timer*10) + log_admin("[key_name(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") + GLOB.minor_announcement.Announce("The emergency shuttle will reach its destination in [round(SSshuttle.emergency.timeLeft(600))] minutes.") + message_admins("[key_name_admin(usr)] edited the Emergency Shuttle's timeleft to [timer] seconds") + href_list["secrets"] = "check_antagonist" + + else if(href_list["delay_round_end"]) + if(!check_rights(R_SERVER)) return + + SSticker.delay_end = !SSticker.delay_end + log_admin("[key_name(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].") + message_admins("[key_name_admin(usr)] [SSticker.delay_end ? "delayed the round end" : "has made the round end normally"].", 1) + href_list["secretsadmin"] = "check_antagonist" + + else if(href_list["simplemake"]) + if(!check_rights(R_SPAWN)) return + + var/mob/M = locateUID(href_list["mob"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + var/delmob = 0 + switch(alert("Delete old mob?","Message","Yes","No","Cancel")) + if("Cancel") return + if("Yes") delmob = 1 + + switch(href_list["simplemake"]) + if("observer") M.change_mob_type( /mob/dead/observer , null, null, delmob, 1 ) + if("drone") M.change_mob_type( /mob/living/carbon/alien/humanoid/drone , null, null, delmob, 1 ) + if("hunter") M.change_mob_type( /mob/living/carbon/alien/humanoid/hunter , null, null, delmob, 1 ) + if("queen") M.change_mob_type( /mob/living/carbon/alien/humanoid/queen/large , null, null, delmob, 1 ) + if("sentinel") M.change_mob_type( /mob/living/carbon/alien/humanoid/sentinel , null, null, delmob, 1 ) + if("larva") M.change_mob_type( /mob/living/carbon/alien/larva , null, null, delmob, 1 ) + if("human") + var/posttransformoutfit = usr.client.robust_dress_shop() + var/mob/living/carbon/human/newmob = M.change_mob_type(/mob/living/carbon/human, null, null, delmob, 1) + if(posttransformoutfit && istype(newmob)) + newmob.equipOutfit(posttransformoutfit) + if("slime") M.change_mob_type( /mob/living/simple_animal/slime , null, null, delmob, 1 ) + if("monkey") M.change_mob_type( /mob/living/carbon/human/monkey , null, null, delmob, 1 ) + if("robot") M.change_mob_type( /mob/living/silicon/robot , null, null, delmob, 1 ) + if("cat") M.change_mob_type( /mob/living/simple_animal/pet/cat , null, null, delmob, 1 ) + if("runtime") M.change_mob_type( /mob/living/simple_animal/pet/cat/Runtime , null, null, delmob, 1 ) + if("corgi") M.change_mob_type( /mob/living/simple_animal/pet/dog/corgi , null, null, delmob, 1 ) + if("crab") M.change_mob_type( /mob/living/simple_animal/crab , null, null, delmob, 1 ) + if("coffee") M.change_mob_type( /mob/living/simple_animal/crab/Coffee , null, null, delmob, 1 ) + if("parrot") M.change_mob_type( /mob/living/simple_animal/parrot , null, null, delmob, 1 ) + if("polyparrot") M.change_mob_type( /mob/living/simple_animal/parrot/Poly , null, null, delmob, 1 ) + if("constructarmoured") M.change_mob_type( /mob/living/simple_animal/hostile/construct/armoured , null, null, delmob, 1 ) + if("constructbuilder") M.change_mob_type( /mob/living/simple_animal/hostile/construct/builder , null, null, delmob, 1 ) + if("constructwraith") M.change_mob_type( /mob/living/simple_animal/hostile/construct/wraith , null, null, delmob, 1 ) + if("shade") M.change_mob_type( /mob/living/simple_animal/shade , null, null, delmob, 1 ) + + log_admin("[key_name(usr)] has used rudimentary transformation on [key_name(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]") + message_admins("[key_name_admin(usr)] has used rudimentary transformation on [key_name_admin(M)]. Transforming to [href_list["simplemake"]]; deletemob=[delmob]", 1) + + + /////////////////////////////////////new ban stuff + else if(href_list["unbanf"]) + if(!check_rights(R_BAN)) return + + var/banfolder = href_list["unbanf"] + GLOB.banlist_savefile.cd = "/base/[banfolder]" + var/key = GLOB.banlist_savefile["key"] + if(alert(usr, "Are you sure you want to unban [key]?", "Confirmation", "Yes", "No") == "Yes") + if(RemoveBan(banfolder)) + unbanpanel() + else + alert(usr, "This ban has already been lifted / does not exist.", "Error", "Ok") + unbanpanel() + + else if(href_list["warn"]) + usr.client.warn(href_list["warn"]) + + else if(href_list["unbane"]) + if(!check_rights(R_BAN)) return + + UpdateTime() + var/reason + + var/banfolder = href_list["unbane"] + GLOB.banlist_savefile.cd = "/base/[banfolder]" + var/reason2 = GLOB.banlist_savefile["reason"] + var/temp = GLOB.banlist_savefile["temp"] + + var/minutes = GLOB.banlist_savefile["minutes"] + + var/banned_key = GLOB.banlist_savefile["key"] + GLOB.banlist_savefile.cd = "/base" + + var/duration + + switch(alert("Temporary Ban?",,"Yes","No")) + if("Yes") + temp = 1 + var/mins = 0 + if(minutes > GLOB.CMinutes) + mins = minutes - GLOB.CMinutes + mins = input(usr,"How long (in minutes)? (Default: 1440)","Ban time",mins ? mins : 1440) as num|null + if(!mins) return + mins = min(525599,mins) + minutes = GLOB.CMinutes + mins + duration = GetExp(minutes) + reason = input(usr,"Please state the reason","Reason",reason2) as message|null + if(!reason) return + if("No") + temp = 0 + duration = "Perma" + reason = input(usr,"Please state the reason","Reason",reason2) as message|null + if(!reason) return + + log_admin("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + ban_unban_log_save("[key_name(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]") + message_admins("[key_name_admin(usr)] edited [banned_key]'s ban. Reason: [reason] Duration: [duration]", 1) + GLOB.banlist_savefile.cd = "/base/[banfolder]" + to_chat(GLOB.banlist_savefile["reason"], reason) + to_chat(GLOB.banlist_savefile["temp"], temp) + to_chat(GLOB.banlist_savefile["minutes"], minutes) + to_chat(GLOB.banlist_savefile["bannedby"], usr.ckey) + GLOB.banlist_savefile.cd = "/base" + feedback_inc("ban_edit",1) + unbanpanel() + + /////////////////////////////////////new ban stuff + + else if(href_list["appearanceban"]) + if(!check_rights(R_BAN)) + return + var/mob/M = locateUID(href_list["appearanceban"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey") + return + var/ban_ckey_param = href_list["dbbanaddckey"] + + var/banreason = appearance_isbanned(M) + if(banreason) + /* if(!config.ban_legacy_system) + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + DB_ban_panel(M.ckey) + return */ + switch(alert("Reason: '[banreason]' Remove appearance ban?","Please Confirm","Yes","No")) + if("Yes") + ban_unban_log_save("[key_name(usr)] removed [key_name(M)]'s appearance ban") + log_admin("[key_name(usr)] removed [key_name(M)]'s appearance ban") + feedback_inc("ban_appearance_unban", 1) + DB_ban_unban(M.ckey, BANTYPE_APPEARANCE) + appearance_unban(M) + message_admins("[key_name_admin(usr)] removed [key_name_admin(M)]'s appearance ban", 1) + to_chat(M, "[usr.client.ckey] has removed your appearance ban.") + + else switch(alert("Appearance ban [M.ckey]?",,"Yes","No", "Cancel")) + if("Yes") + var/reason = input(usr,"Please state the reason","Reason") as message|null + if(!reason) + return + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + ban_unban_log_save("[key_name(usr)] appearance banned [key_name(M)]. reason: [reason]") + log_admin("[key_name(usr)] appearance banned [key_name(M)]. \nReason: [reason]") + feedback_inc("ban_appearance",1) + DB_ban_record(BANTYPE_APPEARANCE, M, -1, reason) + appearance_fullban(M, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") + add_note(M.ckey, "Appearance banned - [reason]", null, usr.ckey, 0) + message_admins("[key_name_admin(usr)] appearance banned [key_name_admin(M)]", 1) + to_chat(M, "You have been appearance banned by [usr.client.ckey].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Appearance ban can be lifted only upon request.") + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + if("No") + return + + else if(href_list["jobban2"]) +// if(!check_rights(R_BAN)) return + + var/mob/M = locateUID(href_list["jobban2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(!M.ckey) //sanity + to_chat(usr, "This mob has no ckey") + return + if(!SSjobs) + to_chat(usr, "SSjobs has not been setup!") + return + + var/dat = "" + var/header = "Job-Ban Panel: [M.name]" + var/body + var/jobs = "" + + /***********************************WARNING!************************************ + The jobban stuff looks mangled and disgusting + But it looks beautiful in-game + -Nodrak + ************************************WARNING!***********************************/ + var/counter = 0 +//Regular jobs + //Command (Blue) + jobs += "" + jobs += "" + for(var/jobPos in GLOB.command_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 6) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Command Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Security (Red) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.security_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Security Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Engineering (Yellow) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.engineering_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Engineering Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Medical (White) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.medical_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Medical Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Science (Purple) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.science_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Science Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Support (Grey) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.support_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Support Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + //Non-Human (Green) + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.nonhuman_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + + //Drone + if(jobban_isbanned(M, "Drone")) + jobs += "" + else + jobs += "" + + //pAI + if(jobban_isbanned(M, "pAI")) + jobs += "" + else + jobs += "" + + jobs += "
    Non-human Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    DroneDronepAIpAI
    " + + //Antagonist (Orange) + var/isbanned_dept = jobban_isbanned(M, "Syndicate") + jobs += "" + jobs += "" + + counter = 0 + for(var/role in GLOB.antag_roles) + if(jobban_isbanned(M, role) || isbanned_dept) + jobs += "" + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Antagonist Positions
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " + + //Other races (BLUE, because I have no idea what other color to make this) + jobs += "" + jobs += "" + + counter = 0 + for(var/role in GLOB.other_roles) + if(jobban_isbanned(M, role) || isbanned_dept) + jobs += "" + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Other
    [replacetext(role, " ", " ")][replacetext(role, " ", " ")]
    " + + //Whitelisted positions + counter = 0 + jobs += "" + jobs += "" + for(var/jobPos in GLOB.whitelisted_positions) + if(!jobPos) continue + var/datum/job/job = SSjobs.GetJob(jobPos) + if(!job) continue + + if(jobban_isbanned(M, job.title)) + jobs += "" + counter++ + else + jobs += "" + counter++ + + if(counter >= 5) //So things dont get squiiiiished! + jobs += "" + counter = 0 + jobs += "
    Whitelisted Positions
    [replacetext(job.title, " ", " ")][replacetext(job.title, " ", " ")]
    " + + body = "[jobs]" + dat = "[header][body]" + usr << browse(dat, "window=jobban2;size=800x490") + return + + //JOBBAN'S INNARDS + else if(href_list["jobban3"]) + if(!check_rights(R_BAN)) return + + var/mob/M = locateUID(href_list["jobban4"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + + if(M != usr) //we can jobban ourselves + if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) //they can ban too. So we can't ban them + alert("You cannot perform this action. You must be of a higher administrative rank!") + return + + var/ban_ckey_param = href_list["dbbanaddckey"] + + if(!SSjobs) + to_chat(usr, "SSjobs has not been setup!") + return + + //get jobs for department if specified, otherwise just returnt he one job in a list. + var/list/joblist = list() + switch(href_list["jobban3"]) + if("commanddept") + for(var/jobPos in GLOB.command_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("securitydept") + for(var/jobPos in GLOB.security_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("engineeringdept") + for(var/jobPos in GLOB.engineering_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("medicaldept") + for(var/jobPos in GLOB.medical_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("sciencedept") + for(var/jobPos in GLOB.science_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("supportdept") + for(var/jobPos in GLOB.support_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("nonhumandept") + joblist += "pAI" + for(var/jobPos in GLOB.nonhuman_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + if("whitelistdept") + for(var/jobPos in GLOB.whitelisted_positions) + if(!jobPos) continue + var/datum/job/temp = SSjobs.GetJob(jobPos) + if(!temp) continue + joblist += temp.title + else + joblist += href_list["jobban3"] + + //Create a list of unbanned jobs within joblist + var/list/notbannedlist = list() + for(var/job in joblist) + if(!jobban_isbanned(M, job)) + notbannedlist += job + + //Banning comes first + if(notbannedlist.len) //at least 1 unbanned job exists in joblist so we have stuff to ban. + switch(alert("Temporary Ban of [M.ckey]?",,"Yes","No", "Cancel")) + if("Yes") + if(config.ban_legacy_system) + to_chat(usr, "Your server is using the legacy banning system, which does not support temporary job bans. Consider upgrading. Aborting ban.") + return + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(!mins) + return + var/reason = input(usr,"Please state the reason","Reason","") as message|null + if(!reason) + return + + var/msg + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + for(var/job in notbannedlist) + ban_unban_log_save("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes. reason: [reason]") + log_admin("[key_name(usr)] temp-jobbanned [key_name(M)] from [job] for [mins] minutes") + feedback_inc("ban_job_tmp",1) + DB_ban_record(BANTYPE_JOB_TEMP, M, mins, reason, job) + feedback_add_details("ban_job_tmp","- [job]") + jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") //Legacy banning does not support temporary jobbans. + if(!msg) + msg = job + else + msg += ", [job]" + add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg] for [mins] minutes", 1) + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "This jobban will be lifted in [mins] minutes.") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("No") + var/reason = input(usr,"Please state the reason","Reason","") as message|null + if(reason) + var/msg + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + for(var/job in notbannedlist) + ban_unban_log_save("[key_name(usr)] perma-jobbanned [key_name(M)] from [job]. reason: [reason]") + log_admin("[key_name(usr)] perma-banned [key_name(M)] from [job]") + feedback_inc("ban_job",1) + DB_ban_record(BANTYPE_JOB_PERMA, M, -1, reason, job) + feedback_add_details("ban_job","- [job]") + jobban_fullban(M, job, "[reason]; By [usr.ckey] on [time2text(world.realtime)]") + if(!msg) msg = job + else msg += ", [job]" + add_note(M.ckey, "Banned from [msg] - [reason]", null, usr.ckey, 0) + message_admins("[key_name_admin(usr)] banned [key_name_admin(M)] from [msg]", 1) + to_chat(M, "You have been jobbanned by [usr.client.ckey] from: [msg].") + to_chat(M, "The reason is: [reason]") + to_chat(M, "Jobban can be lifted only upon request.") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + if("Cancel") + return + + //Unbanning joblist + //all jobs in joblist are banned already OR we didn't give a reason (implying they shouldn't be banned) + if(joblist.len) //at least 1 banned job exists in joblist so we have stuff to unban. + if(!config.ban_legacy_system) + to_chat(usr, "Unfortunately, database based unbanning cannot be done through this panel") + DB_ban_panel(M.ckey) + return + var/msg + for(var/job in joblist) + var/reason = jobban_isbanned(M, job) + if(!reason) continue //skip if it isn't jobbanned anyway + switch(alert("Job: '[job]' Reason: '[reason]' Un-jobban?","Please Confirm","Yes","No")) + if("Yes") + ban_unban_log_save("[key_name(usr)] unjobbanned [key_name(M)] from [job]") + log_admin("[key_name(usr)] unbanned [key_name(M)] from [job]") + DB_ban_unban(M.ckey, BANTYPE_JOB_PERMA, job) + feedback_inc("ban_job_unban",1) + feedback_add_details("ban_job_unban","- [job]") + jobban_unban(M, job) + if(!msg) msg = job + else msg += ", [job]" + else + continue + if(msg) + message_admins("[key_name_admin(usr)] unbanned [key_name_admin(M)] from [msg]", 1) + to_chat(M, "You have been un-jobbanned by [usr.client.ckey] from [msg].") + href_list["jobban2"] = 1 // lets it fall through and refresh + return 1 + return 0 //we didn't do anything! + + else if(href_list["boot2"]) + var/mob/M = locateUID(href_list["boot2"]) + if(ismob(M)) + if(M.client && M.client.holder && (M.client.holder.rights & R_BAN)) + to_chat(usr, "[key_name_admin(M)] cannot be kicked from the server.") + return + to_chat(M, "You have been kicked from the server") + log_admin("[key_name(usr)] booted [key_name(M)].") + message_admins("[key_name_admin(usr)] booted [key_name_admin(M)].", 1) + //M.client = null + del(M.client) + + //Player Notes + else if(href_list["addnote"]) + var/target_ckey = href_list["addnote"] + add_note(target_ckey) + + else if(href_list["addnoteempty"]) + add_note() + + else if(href_list["removenote"]) + var/note_id = href_list["removenote"] + remove_note(note_id) + + else if(href_list["editnote"]) + var/note_id = href_list["editnote"] + edit_note(note_id) + + else if(href_list["shownote"]) + var/target = href_list["shownote"] + show_note(index = target) + + else if(href_list["nonalpha"]) + var/target = href_list["nonalpha"] + target = text2num(target) + show_note(index = target) + + else if(href_list["webtools"]) + var/target_ckey = href_list["webtools"] + if(config.forum_playerinfo_url) + var/url_to_open = config.forum_playerinfo_url + target_ckey + if(alert("Open [url_to_open]",,"Yes","No")=="Yes") + usr.client << link(url_to_open) + + else if(href_list["shownoteckey"]) + var/target_ckey = href_list["shownoteckey"] + show_note(target_ckey) + + else if(href_list["notessearch"]) + var/target = href_list["notessearch"] + show_note(index = target) + + else if(href_list["noteedits"]) + var/note_id = sanitizeSQL("[href_list["noteedits"]]") + var/DBQuery/query_noteedits = GLOB.dbcon.NewQuery("SELECT edits FROM [format_table_name("notes")] WHERE id = '[note_id]'") + if(!query_noteedits.Execute()) + var/err = query_noteedits.ErrorMsg() + log_game("SQL ERROR obtaining edits from notes table. Error : \[[err]\]\n") + return + if(query_noteedits.NextRow()) + var/edit_log = query_noteedits.item[1] + usr << browse(edit_log,"window=noteedits") + + else if(href_list["removejobban"]) + if(!check_rights(R_BAN)) return + + var/t = href_list["removejobban"] + if(t) + if((alert("Do you want to unjobban [t]?","Unjobban confirmation", "Yes", "No") == "Yes") && t) //No more misclicks! Unless you do it twice. + log_admin("[key_name(usr)] removed [t]") + message_admins("[key_name_admin(usr)] removed [t]", 1) + jobban_remove(t) + href_list["ban"] = 1 // lets it fall through and refresh + var/t_split = splittext(t, " - ") + var/key = t_split[1] + var/job = t_split[2] + DB_ban_unban(ckey(key), BANTYPE_JOB_PERMA, job) + + else if(href_list["newban"]) + if(!check_rights(R_BAN)) return + + var/mob/M = locateUID(href_list["newban"]) + if(!ismob(M)) + return + var/ban_ckey_param = href_list["dbbanaddckey"] + + switch(alert("Temporary Ban of [M.ckey] / [ban_ckey_param]?",,"Yes","No", "Cancel")) + if("Yes") + var/mins = input(usr,"How long (in minutes)?","Ban time",1440) as num|null + if(!mins) + return + if(mins >= 525600) mins = 525599 + var/reason = input(usr,"Please state the reason","Reason") as message|null + if(!reason) + return + M = admin_ban_mobsearch(M, ban_ckey_param, usr) + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 1, mins) + ban_unban_log_save("[usr.client.ckey] has banned [M.ckey]. - Reason: [reason] - This will be removed in [mins] minutes.") + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This is a temporary ban, it will be removed in [mins] minutes.") + feedback_inc("ban_tmp",1) + DB_ban_record(BANTYPE_TEMP, M, mins, reason) + feedback_inc("ban_tmp_mins",mins) + if(M.client) + M.client.link_forum_account(TRUE) + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis will be removed in [mins] minutes.") + + del(M.client) + //qdel(M) // See no reason why to delete mob. Important stuff can be lost. And ban can be lifted before round ends. + if("No") + var/reason = input(usr,"Please state the reason","Reason") as message|null + if(!reason) + return + AddBan(M.ckey, M.computer_id, reason, usr.ckey, 0, 0, M.lastKnownIP) + to_chat(M, "You have been banned by [usr.client.ckey].\nReason: [reason].") + to_chat(M, "This ban does not expire automatically and must be appealed.") + if(M.client) + M.client.link_forum_account(TRUE) + if(config.banappeals) + to_chat(M, "To try to resolve this matter head to [config.banappeals]") + else + to_chat(M, "No ban appeals URL has been set.") + ban_unban_log_save("[usr.client.ckey] has permabanned [M.ckey]. - Reason: [reason] - This ban does not expire automatically and must be appealed.") + log_admin("[key_name(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") + message_admins("[key_name_admin(usr)] has banned [M.ckey].\nReason: [reason]\nThis ban does not expire automatically and must be appealed.") + feedback_inc("ban_perma",1) + DB_ban_record(BANTYPE_PERMA, M, -1, reason) + + del(M.client) + //qdel(M) + if("Cancel") + return + + + //Watchlist + else if(href_list["watchadd"]) + var/target_ckey = href_list["watchadd"] + usr.client.watchlist_add(target_ckey) + + else if(href_list["watchremove"]) + var/target_ckey = href_list["watchremove"] + var/confirm = alert("Are you sure you want to remove [target_ckey] from the watchlist?", "Confirm Watchlist Removal", "Yes", "No") + if(confirm == "Yes") + usr.client.watchlist_remove(target_ckey) + + else if(href_list["watchedit"]) + var/target_ckey = href_list["watchedit"] + usr.client.watchlist_edit(target_ckey) + + else if(href_list["watchaddbrowse"]) + usr.client.watchlist_add(null, 1) + + else if(href_list["watchremovebrowse"]) + var/target_ckey = href_list["watchremovebrowse"] + usr.client.watchlist_remove(target_ckey, 1) + + else if(href_list["watcheditbrowse"]) + var/target_ckey = href_list["watcheditbrowse"] + usr.client.watchlist_edit(target_ckey, 1) + + else if(href_list["watchsearch"]) + var/target_ckey = href_list["watchsearch"] + usr.client.watchlist_show(target_ckey) + + else if(href_list["watchshow"]) + usr.client.watchlist_show() + + else if(href_list["watcheditlog"]) + var/target_ckey = sanitizeSQL("[href_list["watcheditlog"]]") + var/DBQuery/query_watchedits = GLOB.dbcon.NewQuery("SELECT edits FROM [format_table_name("watch")] WHERE ckey = '[target_ckey]'") + if(!query_watchedits.Execute()) + var/err = query_watchedits.ErrorMsg() + log_game("SQL ERROR obtaining edits from watch table. Error : \[[err]\]\n") + return + if(query_watchedits.NextRow()) + var/edit_log = query_watchedits.item[1] + usr << browse(edit_log,"window=watchedits") + + else if(href_list["mute"]) + if(!check_rights(R_ADMIN|R_MOD)) + return + + var/mob/M = locateUID(href_list["mute"]) + if(!ismob(M)) return + if(!M.client) return + + var/mute_type = href_list["mute_type"] + if(istext(mute_type)) mute_type = text2num(mute_type) + if(!isnum(mute_type)) return + + cmd_admin_mute(M, mute_type) + + else if(href_list["c_mode"]) + if(!check_rights(R_ADMIN)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + var/dat = {"What mode do you wish to play?
    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
    "} + dat += {"Secret
    "} + dat += {"Random
    "} + dat += {"Now: [GLOB.master_mode]"} + usr << browse(dat, "window=c_mode") + + else if(href_list["f_secret"]) + if(!check_rights(R_ADMIN)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "secret") + return alert(usr, "The game mode has to be secret!", null, null, null, null) + var/dat = {"What game mode do you want to force secret to be? Use this if you want to change the game mode, but want the players to believe it's secret. This will only work if the current game mode is secret.
    "} + for(var/mode in config.modes) + dat += {"[config.mode_names[mode]]
    "} + dat += {"Random (default)
    "} + dat += {"Now: [GLOB.secret_force_mode]"} + usr << browse(dat, "window=f_secret") + + else if(href_list["c_mode2"]) + if(!check_rights(R_ADMIN|R_SERVER)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + GLOB.master_mode = href_list["c_mode2"] + log_admin("[key_name(usr)] set the mode as [GLOB.master_mode].") + message_admins("[key_name_admin(usr)] set the mode as [GLOB.master_mode].", 1) + to_chat(world, "The mode is now: [GLOB.master_mode]") + Game() // updates the main game menu + world.save_mode(GLOB.master_mode) + .(href, list("c_mode"=1)) + + else if(href_list["f_secret2"]) + if(!check_rights(R_ADMIN|R_SERVER)) return + + if(SSticker && SSticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(GLOB.master_mode != "secret") + return alert(usr, "The game mode has to be secret!", null, null, null, null) + GLOB.secret_force_mode = href_list["f_secret2"] + log_admin("[key_name(usr)] set the forced secret mode as [GLOB.secret_force_mode].") + message_admins("[key_name_admin(usr)] set the forced secret mode as [GLOB.secret_force_mode].", 1) + Game() // updates the main game menu + .(href, list("f_secret"=1)) + + else if(href_list["monkeyone"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["monkeyone"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make monkey?",, "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] attempting to monkeyize [key_name(H)]") + message_admins("[key_name_admin(usr)] attempting to monkeyize [key_name_admin(H)]", 1) + H.monkeyize() + + + else if(href_list["corgione"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["corgione"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + if(alert(usr, "Confirm make corgi?",, "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] attempting to corgize [key_name(H)]") + message_admins("[key_name_admin(usr)] attempting to corgize [key_name_admin(H)]", 1) + H.corgize() + + else if(href_list["makePAI"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makePAI"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make pai?",, "Yes", "No") != "Yes") + return + + var/painame = "Default" + var/name = "" + if(alert(usr, "Do you want to set their name or let them choose their own name?", "Name Choice", "Set Name", "Let them choose") == "Set Name") + name = sanitize(copytext(input(usr, "Enter a name for the new pAI. Default name is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) + else + name = sanitize(copytext(input(H, "An admin wants to make you into a pAI. Choose a name. Default is [painame].", "pAI Name", painame),1,MAX_NAME_LEN)) + + if(!name) + name = painame + + log_admin("[key_name(usr)] attempting to pAIze [key_name(H)]") + message_admins("[key_name_admin(usr)] attempting to pAIze [key_name_admin(H)]", 1) + H.paize(name) + + else if(href_list["forcespeech"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/mob/M = locateUID(href_list["forcespeech"]) + if(!ismob(M)) + to_chat(usr, "this can only be used on instances of type /mob") + + var/speech = input("What will [key_name(M)] say?.", "Force speech", "")// Don't need to sanitize, since it does that in say(), we also trust our admins. + if(!speech) return + M.say(speech) + speech = sanitize(speech) // Nah, we don't trust them + log_admin("[key_name(usr)] forced [key_name(M)] to say: [speech]") + message_admins("[key_name_admin(usr)] forced [key_name_admin(M)] to say: [speech]") + + else if(href_list["sendtoprison"]) + if(!check_rights(R_ADMIN)) return + + if(alert(usr, "Send to admin prison for the round?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["sendtoprison"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + var/turf/prison_cell = pick(GLOB.prisonwarp) + if(!prison_cell) return + + var/obj/structure/closet/secure_closet/brig/locker = new /obj/structure/closet/secure_closet/brig(prison_cell) + locker.opened = 0 + locker.locked = 1 + + //strip their stuff and stick it in the crate + for(var/obj/item/I in M) + if(M.unEquip(I)) + I.loc = locker + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + M.update_icons() + + //so they black out before warping + M.Paralyse(5) + sleep(5) + if(!M) return + + M.loc = prison_cell + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + + to_chat(M, "You have been sent to the prison station!") + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) + + else if(href_list["sendbacktolobby"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["sendbacktolobby"]) + + if(!isobserver(M)) + to_chat(usr, "You can only send ghost players back to the Lobby.") + return + + if(!M.client) + to_chat(usr, "[M] doesn't seem to have an active client.") + return + + if(alert(usr, "Send [key_name(M)] back to Lobby?", "Message", "Yes", "No") != "Yes") + return + + log_admin("[key_name(usr)] has sent [key_name(M)] back to the Lobby.") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] back to the Lobby.") + + var/mob/new_player/NP = new() + GLOB.non_respawnable_keys -= M.ckey + NP.ckey = M.ckey + qdel(M) + + else if(href_list["tdome1"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdome1"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.unEquip(I) + if(I) + I.loc = M.loc + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdome1) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 1)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 1)", 1) + + else if(href_list["tdome2"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdome2"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.unEquip(I) + if(I) + I.loc = M.loc + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdome2) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Team 2)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Team 2)", 1) + + else if(href_list["tdomeadmin"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdomeadmin"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdomeadmin) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Admin.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Admin.)", 1) + + else if(href_list["tdomeobserve"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["tdomeobserve"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + for(var/obj/item/I in M) + M.unEquip(I) + if(I) + I.loc = M.loc + I.layer = initial(I.layer) + I.plane = initial(I.plane) + I.dropped(M) + + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/observer = M + observer.equip_to_slot_or_del(new /obj/item/clothing/under/suit_jacket(observer), slot_w_uniform) + observer.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(observer), slot_shoes) + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.tdomeobserve) + spawn(50) + to_chat(M, "You have been sent to the Thunderdome.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the thunderdome. (Observer.)") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the thunderdome. (Observer.)", 1) + + else if(href_list["aroomwarp"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") + return + + var/mob/M = locateUID(href_list["aroomwarp"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob") + return + if(istype(M, /mob/living/silicon/ai)) + to_chat(usr, "This cannot be used on instances of type /mob/living/silicon/ai") + return + + M.Paralyse(5) + sleep(5) + M.loc = pick(GLOB.aroomwarp) + spawn(50) + to_chat(M, "You have been sent to the Admin Room!.") + log_admin("[key_name(usr)] has sent [key_name(M)] to the Admin Room") + message_admins("[key_name_admin(usr)] has sent [key_name_admin(M)] to the Admin Room", 1) + + + else if(href_list["revive"]) + if(!check_rights(R_REJUVINATE)) return + + var/mob/living/L = locateUID(href_list["revive"]) + if(!istype(L)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + + L.revive() + message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(L)]!", 1) + log_admin("[key_name(usr)] healed / revived [key_name(L)]") + + else if(href_list["makeai"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeai"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + if(alert(usr, "Confirm make ai?",, "Yes", "No") != "Yes") + return + + message_admins("Admin [key_name_admin(usr)] AIized [key_name_admin(H)]!", 1) + log_admin("[key_name(usr)] AIized [key_name(H)]") + var/mob/living/silicon/ai/ai_character = H.AIize() + ai_character.moveToAILandmark() + + else if(href_list["makealien"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makealien"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make alien?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_alienize(H) + + else if(href_list["makeslime"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makeslime"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make slime?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_slimeize(H) + + else if(href_list["makesuper"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makesuper"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + if(alert(usr, "Confirm make superhero?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_super(H) + + else if(href_list["makerobot"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["makerobot"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(alert(usr, "Confirm make robot?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_robotize(H) + + else if(href_list["makeanimal"]) + if(!check_rights(R_SPAWN)) return + + var/mob/M = locateUID(href_list["makeanimal"]) + if(istype(M, /mob/new_player)) + to_chat(usr, "This cannot be used on instances of type /mob/new_player") + return + if(alert(usr, "Confirm make animal?",, "Yes", "No") != "Yes") + return + + usr.client.cmd_admin_animalize(M) + + else if(href_list["incarn_ghost"]) + if(!check_rights(R_SPAWN)) + return + + var/mob/dead/observer/G = locateUID(href_list["incarn_ghost"]) + if(!istype(G)) + to_chat(usr, "This will only work on /mob/dead/observer") + + var/posttransformoutfit = usr.client.robust_dress_shop() + + var/mob/living/carbon/human/H = G.incarnate_ghost() + + if(posttransformoutfit && istype(H)) + H.equipOutfit(posttransformoutfit) + + log_admin("[key_name(G)] was incarnated by [key_name(owner)]") + message_admins("[key_name_admin(G)] was incarnated by [key_name_admin(owner)]") + + else if(href_list["togmutate"]) + if(!check_rights(R_SPAWN)) return + + var/mob/living/carbon/human/H = locateUID(href_list["togmutate"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/block=text2num(href_list["block"]) + //testing("togmutate([href_list["block"]] -> [block])") + usr.client.cmd_admin_toggle_block(H,block) + show_player_panel(H) + //H.regenerate_icons() + + else if(href_list["adminplayeropts"]) + var/mob/M = locateUID(href_list["adminplayeropts"]) + show_player_panel(M) + + else if(href_list["adminplayerobservefollow"]) + var/client/C = usr.client + if(!isobserver(usr)) + if(!check_rights(R_ADMIN|R_MOD)) // Need to be mod or admin to aghost + return + C.admin_ghost() + var/mob/M = locateUID(href_list["adminplayerobservefollow"]) + var/mob/dead/observer/A = C.mob + sleep(2) + A.ManualFollow(M) + + else if(href_list["check_antagonist"]) + check_antagonists() + + else if(href_list["take_question"]) + var/index = text2num(href_list["take_question"]) + + if(href_list["is_mhelp"]) + SSmentor_tickets.takeTicket(index) + else //Ahelp + SStickets.takeTicket(index) + + else if(href_list["resolve"]) + var/index = text2num(href_list["resolve"]) + if(href_list["is_mhelp"]) + SSmentor_tickets.resolveTicket(index) + else //Ahelp + SStickets.resolveTicket(index) + + else if(href_list["autorespond"]) + var/index = text2num(href_list["autorespond"]) + if(!check_rights(R_ADMIN|R_MOD)) + return + SStickets.autoRespond(index) + + else if(href_list["cult_nextobj"]) + if(alert(usr, "Validate the current Cult objective and unlock the next one?", "Cult Cheat Code", "Yes", "No") != "Yes") + return + + if(!GAMEMODE_IS_CULT) + alert("Couldn't locate cult mode datum! This shouldn't ever happen, tell a coder!") + return + + var/datum/game_mode/cult/cult_round = SSticker.mode + cult_round.bypass_phase() + message_admins("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") + log_admin("Admin [key_name_admin(usr)] has unlocked the Cult's next objective.") + + else if(href_list["cult_mindspeak"]) + var/input = stripped_input(usr, "Communicate to all the cultists with the voice of [SSticker.cultdat.entity_name]", "Voice of [SSticker.cultdat.entity_name]", "") + if(!input) + return + + for(var/datum/mind/H in SSticker.mode.cult) + if (H.current) + to_chat(H.current, "[SSticker.cultdat.entity_name] murmurs, [input]") + + for(var/mob/dead/observer/O in GLOB.player_list) + to_chat(O, "[SSticker.cultdat.entity_name] murmurs, [input]") + + message_admins("Admin [key_name_admin(usr)] has talked with the Voice of [SSticker.cultdat.entity_name].") + log_admin("[key_name(usr)] Voice of [SSticker.cultdat.entity_name]: [input]") + + else if(href_list["adminplayerobservecoodjump"]) + if(!check_rights(R_ADMIN)) return + + var/x = text2num(href_list["X"]) + var/y = text2num(href_list["Y"]) + var/z = text2num(href_list["Z"]) + + var/client/C = usr.client + if(!isobserver(usr)) C.admin_ghost() + sleep(2) + C.jumptocoord(x,y,z) + + else if(href_list["adminchecklaws"]) + output_ai_laws() + + else if(href_list["adminmoreinfo"]) + var/mob/M = locateUID(href_list["adminmoreinfo"]) + admin_mob_info(M) + + else if(href_list["adminspawncookie"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/carbon/human/H = locateUID(href_list["adminspawncookie"]) + if(!ishuman(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + + H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_l_hand ) + if(!(istype(H.l_hand,/obj/item/reagent_containers/food/snacks/cookie))) + H.equip_to_slot_or_del( new /obj/item/reagent_containers/food/snacks/cookie(H), slot_r_hand ) + if(!(istype(H.r_hand,/obj/item/reagent_containers/food/snacks/cookie))) + log_admin("[key_name(H)] has their hands full, so they did not receive their cookie, spawned by [key_name(src.owner)].") + message_admins("[key_name_admin(H)] has [H.p_their()] hands full, so [H.p_they()] did not receive [H.p_their()] cookie, spawned by [key_name_admin(src.owner)].") + return + else + H.update_inv_r_hand()//To ensure the icon appears in the HUD + else + H.update_inv_l_hand() + log_admin("[key_name(H)] got their cookie, spawned by [key_name(src.owner)]") + message_admins("[key_name_admin(H)] got [H.p_their()] cookie, spawned by [key_name_admin(src.owner)]") + feedback_inc("admin_cookies_spawned",1) + to_chat(H, "Your prayers have been answered!! You received the best cookie!") + + else if(href_list["BlueSpaceArtillery"]) + if(!check_rights(R_ADMIN|R_EVENT)) return + + var/mob/living/M = locateUID(href_list["BlueSpaceArtillery"]) + if(!isliving(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + + if(alert(owner, "Are you sure you wish to hit [key_name(M)] with Bluespace Artillery?", "Confirm Firing?" , "Yes" , "No") != "Yes") + return + + if(GLOB.BSACooldown) + to_chat(owner, "Standby. Reload cycle in progress. Gunnery crews ready in five seconds!") + return + + GLOB.BSACooldown = 1 + spawn(50) + GLOB.BSACooldown = 0 + + to_chat(M, "You've been hit by bluespace artillery!") + log_admin("[key_name(M)] has been hit by Bluespace Artillery fired by [key_name(owner)]") + message_admins("[key_name_admin(M)] has been hit by Bluespace Artillery fired by [key_name_admin(owner)]") + + var/turf/simulated/floor/T = get_turf(M) + if(istype(T)) + if(prob(80)) + T.break_tile_to_plating() + else + T.break_tile() + + if(M.health <= 1) + M.gib() + else + M.adjustBruteLoss(min(99,(M.health - 1))) + M.Stun(20) + M.Weaken(20) + M.Stuttering(20) + + else if(href_list["CentcommReply"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["CentcommReply"]) + usr.client.admin_headset_message(M, "Centcomm") + + else if(href_list["SyndicateReply"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["SyndicateReply"]) + usr.client.admin_headset_message(M, "Syndicate") + + else if(href_list["HeadsetMessage"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/M = locateUID(href_list["HeadsetMessage"]) + usr.client.admin_headset_message(M) + + else if(href_list["EvilFax"]) + if(!check_rights(R_ADMIN)) + return + var/mob/living/carbon/human/H = locateUID(href_list["EvilFax"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine") + var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes + if(!(eviltype in etypes)) + return + var/customname = clean_input("Pick a title for the evil fax.", "Fax Title", , owner) + if(!customname) + customname = "paper" + var/obj/item/paper/evilfax/P = new /obj/item/paper/evilfax(null) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + + P.name = "Central Command - [customname]" + P.info = "You really should've known better." + P.myeffect = eviltype + P.mytarget = H + if(alert("Do you want the Evil Fax to activate automatically if [H] tries to ignore it?",,"Yes", "No") == "Yes") + P.activate_on_timeout = 1 + P.x = rand(-2, 0) + P.y = rand(-1, 2) + P.offset_x += P.x + P.offset_y += P.y + P.update_icon() + var/stampvalue = "cent" + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-[stampvalue]" + stampoverlay.pixel_x = P.x + stampoverlay.pixel_y = P.y + P.stamped = list() + P.stamped += /obj/item/stamp/centcom + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-[stampvalue]" + P.overlays += stampoverlay + P.stamps += "
    " + P.update_icon() + P.faxmachineid = fax.UID() + P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required. + if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(src.owner, "You sent a [eviltype] fax to [H]") + log_admin("[key_name(src.owner)] sent [key_name(H)] a [eviltype] fax") + message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a [eviltype] fax") + else if(href_list["Bless"]) + if(!check_rights(R_EVENT)) + return + var/mob/living/M = locateUID(href_list["Bless"]) + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + var/btypes = list("To Arrivals", "Moderate Heal") + var/mob/living/carbon/human/H + if(ishuman(M)) + H = M + btypes += "Heal Over Time" + btypes += "Permanent Regeneration" + btypes += "Super Powers" + btypes += "Scarab Guardian" + btypes += "Human Protector" + btypes += "Sentient Pet" + btypes += "All Access" + var/blessing = input(owner, "How would you like to bless [M]?", "Its good to be good...", "") as null|anything in btypes + if(!(blessing in btypes)) + return + var/logmsg = null + switch(blessing) + if("To Arrivals") + M.forceMove(pick(GLOB.latejoin)) + to_chat(M, "You are abruptly pulled through space!") + logmsg = "a teleport to arrivals." + if("Moderate Heal") + M.adjustBruteLoss(-25) + M.adjustFireLoss(-25) + M.adjustToxLoss(-25) + M.adjustOxyLoss(-25) + to_chat(M,"You feel invigorated!") + logmsg = "a moderate heal." + if("Heal Over Time") + H.reagents.add_reagent("salglu_solution", 30) + H.reagents.add_reagent("salbutamol", 20) + H.reagents.add_reagent("spaceacillin", 20) + logmsg = "a heal over time." + if("Permanent Regeneration") + H.dna.SetSEState(GLOB.regenerateblock, 1) + genemutcheck(H, GLOB.regenerateblock, null, MUTCHK_FORCED) + H.update_mutations() + H.gene_stability = 100 + logmsg = "permanent regeneration." + if("Super Powers") + var/list/default_genes = list(GLOB.regenerateblock, GLOB.breathlessblock, GLOB.coldblock) + for(var/gene in default_genes) + H.dna.SetSEState(gene, 1) + genemutcheck(H, gene, null, MUTCHK_FORCED) + H.update_mutations() + H.gene_stability = 100 + logmsg = "superpowers." + if("Scarab Guardian") + var/obj/item/guardiancreator/biological/scarab = new /obj/item/guardiancreator/biological(H) + var/list/possible_guardians = list("Chaos", "Standard", "Ranged", "Support", "Explosive", "Random") + var/typechoice = input("Select Guardian Type", "Type") as null|anything in possible_guardians + if(isnull(typechoice)) + return + if(typechoice != "Random") + possible_guardians -= "Random" + scarab.possible_guardians = list() + scarab.possible_guardians += typechoice + scarab.attack_self(H) + spawn(700) + qdel(scarab) + logmsg = "scarab guardian." + if("Sentient Pet") + var/pets = subtypesof(/mob/living/simple_animal) + var/petchoice = input("Select pet type", "Pets") as null|anything in pets + if(isnull(petchoice)) + return + var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10) + var/mob/dead/observer/theghost = null + if(candidates.len) + var/mob/living/simple_animal/pet/P = new petchoice(H.loc) + theghost = pick(candidates) + P.key = theghost.key + P.master_commander = H + P.universal_speak = 1 + P.universal_understand = 1 + P.can_collar = 1 + P.faction = list("neutral") + var/obj/item/clothing/accessory/petcollar/C = new + P.add_collar(C) + var/obj/item/card/id/I = H.wear_id + if(I) + var/obj/item/card/id/D = new /obj/item/card/id(C) + D.access = I.access + D.registered_name = P.name + D.assignment = "Pet" + C.access_id = D + spawn(30) + var/newname = sanitize(copytext(input(P, "You are [P], special event pet of [H]. Change your name to something else?", "Name change", P.name) as null|text,1,MAX_NAME_LEN)) + if(newname && newname != P.name) + P.name = newname + if(P.mind) + P.mind.name = newname + logmsg = "pet ([P])." + else + to_chat(usr, "WARNING: Nobody volunteered to play the special event pet.") + logmsg = "pet (no volunteers)." + if("Human Protector") + usr.client.create_eventmob_for(H, 0) + logmsg = "syndie protector." + if("All Access") + var/obj/item/card/id/I = H.wear_id + if(I) + var/list/access_to_give = get_all_accesses() + for(var/this_access in access_to_give) + if(!(this_access in I.access)) + // don't have it - add it + I.access |= this_access + else + to_chat(usr, "ERROR: [H] is not wearing an ID card.") + logmsg = "all access." + if(logmsg) + log_admin("[key_name(owner)] answered [key_name(M)]'s prayer with a blessing: [logmsg]") + message_admins("[key_name_admin(owner)] answered [key_name_admin(M)]'s prayer with a blessing: [logmsg]") + else if(href_list["Smite"]) + if(!check_rights(R_EVENT)) + return + var/mob/living/M = locateUID(href_list["Smite"]) + var/mob/living/carbon/human/H + if(!istype(M)) + to_chat(usr, "This can only be used on instances of type /mob/living") + return + var/ptypes = list("Lightning bolt", "Fire Death", "Gib") + if(ishuman(M)) + H = M + ptypes += "Brain Damage" + ptypes += "Honk Tumor" + ptypes += "Hallucinate" + ptypes += "Cold" + ptypes += "Hunger" + ptypes += "Cluwne" + ptypes += "Mutagen Cookie" + ptypes += "Hellwater Cookie" + ptypes += "Hunter" + ptypes += "Crew Traitor" + ptypes += "Floor Cluwne" + ptypes += "Shamebrero" + ptypes += "Dust" + var/punishment = input(owner, "How would you like to smite [M]?", "Its good to be baaaad...", "") as null|anything in ptypes + if(!(punishment in ptypes)) + return + var/logmsg = null + switch(punishment) + // These smiting types are valid for all living mobs + if("Lightning bolt") + M.electrocute_act(5, "Lightning Bolt", safety = TRUE, override = TRUE) + playsound(get_turf(M), 'sound/magic/lightningshock.ogg', 50, 1, -1) + M.adjustFireLoss(75) + M.Weaken(5) + to_chat(M, "The gods have punished you for your sins!") + logmsg = "a lightning bolt." + if("Fire Death") + to_chat(M,"You feel hotter than usual. Maybe you should lowe-wait, is that your hand melting?") + var/turf/simulated/T = get_turf(M) + new /obj/effect/hotspot(T) + M.adjustFireLoss(150) + logmsg = "a firey death." + if("Gib") + M.gib(FALSE) + logmsg = "gibbed." + + // These smiting types are only valid for ishuman() mobs + if("Brain Damage") + H.adjustBrainLoss(75) + logmsg = "75 brain damage." + if("Honk Tumor") + if(!H.get_int_organ(/obj/item/organ/internal/honktumor)) + var/obj/item/organ/internal/organ = new /obj/item/organ/internal/honktumor + to_chat(H, "Life seems funnier, somehow.") + organ.insert(H) + logmsg = "a honk tumor." + if("Hallucinate") + H.Hallucinate(1000) + logmsg = "hallucinations." + if("Cold") + H.reagents.add_reagent("frostoil", 40) + H.reagents.add_reagent("ice", 40) + logmsg = "cold." + if("Hunger") + H.set_nutrition(NUTRITION_LEVEL_CURSED) + logmsg = "starvation." + if("Cluwne") + H.makeCluwne() + H.mutations |= NOCLONE + logmsg = "cluwned." + if("Mutagen Cookie") + var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie + evilcookie.reagents.add_reagent("mutagen", 10) + evilcookie.desc = "It has a faint green glow." + evilcookie.bitesize = 100 + evilcookie.flags = NODROP | DROPDEL + H.drop_l_hand() + H.equip_to_slot_or_del(evilcookie, slot_l_hand) + logmsg = "a mutagen cookie." + if("Hellwater Cookie") + var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie + evilcookie.reagents.add_reagent("hell_water", 25) + evilcookie.desc = "Sulphur-flavored." + evilcookie.bitesize = 100 + evilcookie.flags = NODROP | DROPDEL + H.drop_l_hand() + H.equip_to_slot_or_del(evilcookie, slot_l_hand) + logmsg = "a hellwater cookie." + if("Hunter") + H.mutations |= NOCLONE + usr.client.create_eventmob_for(H, 1) + logmsg = "hunter." + if("Crew Traitor") + if(!H.mind) + to_chat(usr, "ERROR: This mob ([H]) has no mind!") + return + var/list/possible_traitors = list() + for(var/mob/living/player in GLOB.living_mob_list) + if(player.client && player.mind && player.stat != DEAD && player != H) + if(ishuman(player) && !player.mind.special_role) + if(player.client && (ROLE_TRAITOR in player.client.prefs.be_special) && !jobban_isbanned(player, ROLE_TRAITOR) && !jobban_isbanned(player, "Syndicate")) + possible_traitors += player.mind + for(var/datum/mind/player in possible_traitors) + if(player.current) + if(ismindshielded(player.current)) + possible_traitors -= player + if(possible_traitors.len) + var/datum/mind/newtraitormind = pick(possible_traitors) + var/datum/objective/assassinate/kill_objective = new() + kill_objective.target = H.mind + kill_objective.owner = newtraitormind + kill_objective.explanation_text = "Assassinate [H.mind.name], the [H.mind.assigned_role]" + newtraitormind.objectives += kill_objective + var/datum/antagonist/traitor/T = new() + T.give_objectives = FALSE + to_chat(newtraitormind.current, "ATTENTION: It is time to pay your debt to the Syndicate...") + to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") + newtraitormind.add_antag_datum(T) + else + to_chat(usr, "ERROR: Unable to find any valid candidate to send after [H].") + return + logmsg = "crew traitor." + if("Floor Cluwne") + var/turf/T = get_turf(M) + var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T) + FC.smiting = TRUE + FC.Acquire_Victim(M) + logmsg = "floor cluwne" + if("Shamebrero") + if(H.head) + H.unEquip(H.head, TRUE) + var/obj/item/clothing/head/sombrero/shamebrero/S = new(H.loc) + H.equip_to_slot_or_del(S, slot_head) + logmsg = "shamebrero" + if("Dust") + H.dust() + logmsg = "dust" + if(logmsg) + log_admin("[key_name(owner)] smited [key_name(M)] with: [logmsg]") + message_admins("[key_name_admin(owner)] smited [key_name_admin(M)] with: [logmsg]") + else if(href_list["cryossd"]) + if(!check_rights(R_ADMIN)) + return + var/mob/living/carbon/human/H = locateUID(href_list["cryossd"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!href_list["cryoafk"] && !isLivingSSD(H)) + to_chat(usr, "This can only be used on living, SSD players.") + return + if(istype(H.loc, /obj/machinery/cryopod)) + var/obj/machinery/cryopod/P = H.loc + P.despawn_occupant() + log_admin("[key_name(usr)] despawned [H.job] [H] in cryo.") + message_admins("[key_name_admin(usr)] despawned [H.job] [H] in cryo.") + else if(cryo_ssd(H)) + log_admin("[key_name(usr)] sent [H.job] [H] to cryo.") + message_admins("[key_name_admin(usr)] sent [H.job] [H] to cryo.") + if(href_list["cryoafk"]) // Warn them if they are send to storage and are AFK + to_chat(H, "The admins have moved you to cryo storage for being AFK. Please eject yourself (right click, eject) out of the cryostorage if you want to avoid being despawned.") + SEND_SOUND(H, 'sound/effects/adminhelp.ogg') + if(H.client) + window_flash(H.client) + else if(href_list["FaxReplyTemplate"]) + if(!check_rights(R_ADMIN)) + return + var/mob/living/carbon/human/H = locateUID(href_list["FaxReplyTemplate"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + var/obj/item/paper/P = new /obj/item/paper(null) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + P.name = "Central Command - paper" + var/stypes = list("Handle it yourselves!","Illegible fax","Fax not signed","Not Right Now","You are wasting our time", "Keep up the good work", "ERT Instructions") + var/stype = input(src.owner, "Which type of standard reply do you wish to send to [H]?","Choose your paperwork", "") as null|anything in stypes + var/tmsg = "



    Nanotrasen Science Station [GLOB.using_map.station_short]


    NAS Trurl Communications Department Report


    " + if(stype == "Handle it yourselves!") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please proceed in accordance with Standard Operating Procedure and/or Space Law. You are fully trained to handle this situation without Central Command intervention.

    This is an automatic message." + else if(stype == "Illegible fax") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax's grammar, syntax and/or typography are of a sub-par level and do not allow us to understand the contents of the message.

    Please consult your nearest dictionary and/or thesaurus and try again.

    This is an automatic message." + else if(stype == "Fax not signed") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Your fax has not been correctly signed and, as such, we cannot verify your identity.

    Please sign your faxes before sending them so that we may verify your identity.

    This is an automatic message." + else if(stype == "Not Right Now") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Due to pressing concerns of a matter above your current paygrade, we are unable to provide assistance in whatever matter your fax referenced.

    This can be either due to a power outage, bureaucratic audit, pest infestation, Ascendance Event, corgi outbreak, or any other situation that would affect the proper functioning of the NAS Trurl.

    Please try again later.

    This is an automatic message." + else if(stype == "You are wasting our time") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    In the interest of preventing further mismanagement of company resources, please avoid wasting our time with such petty drivel.

    Do kindly remember that we expect our workforce to maintain at least a semi-decent level of profesionalism. Do not test our patience.

    This is an automatic message." + else if(stype == "Keep up the good work") + tmsg += "Greetings, esteemed crewmember. Your fax has been received successfully by NAS Trurl Fax Registration.

    We at the NAS Trurl appreciate the good work that you have done here, and sincerely recommend that you continue such a display of dedication to the company.

    This is absolutely not an automated message." + else if(stype == "ERT Instructions") + tmsg += "Greetings, esteemed crewmember. Your fax has been DECLINED automatically by NAS Trurl Fax Registration.

    Please utilize the Card Swipers if you wish to call for an ERT.

    This is an automated message." + else + return + tmsg += "
    " + P.info = tmsg + P.x = rand(-2, 0) + P.y = rand(-1, 2) + P.offset_x += P.x + P.offset_y += P.y + P.update_icon() + var/stampvalue = "cent" + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.icon_state = "paper_stamp-[stampvalue]" + stampoverlay.pixel_x = P.x + stampoverlay.pixel_y = P.y + P.stamped = list() + P.stamped += /obj/item/stamp/centcom + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-[stampvalue]" + P.overlays += stampoverlay + P.stamps += "
    " + P.update_icon() + fax.receivefax(P) + if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) + to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.") + to_chat(src.owner, "You sent a standard '[stype]' fax to [H]") + log_admin("[key_name(src.owner)] sent [key_name(H)] a standard '[stype]' fax") + message_admins("[key_name_admin(src.owner)] replied to [key_name_admin(H)] with a standard '[stype]' fax") + + else if(href_list["HONKReply"]) + var/mob/living/carbon/human/H = locateUID(href_list["HONKReply"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + var/input = input(src.owner, "Please enter a message to reply to [key_name(H)] via [H.p_their()] headset.","Outgoing message from HONKplanet", "") + if(!input) return + + to_chat(src.owner, "You sent [input] to [H] via a secure channel.") + log_admin("[src.owner] replied to [key_name(H)]'s HONKplanet message with the message [input].") + to_chat(H, "You hear something crackle in your headset for a moment before a voice speaks. \"Please stand by for a message from your HONKbrothers. Message as follows, HONK. [input]. Message ends, HONK.\"") + + else if(href_list["ErtReply"]) + if(!check_rights(R_ADMIN)) + return + + if(alert(src.owner, "Accept or Deny ERT request?", "CentComm Response", "Accept", "Deny") == "Deny") + var/mob/living/carbon/human/H = locateUID(href_list["ErtReply"]) + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(H.stat != 0) + to_chat(usr, "The person you are trying to contact is not conscious.") + return + if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + var/input = input(src.owner, "Please enter a reason for denying [key_name(H)]'s ERT request.","Outgoing message from CentComm", "") + if(!input) return + GLOB.ert_request_answered = TRUE + to_chat(src.owner, "You sent [input] to [H] via a secure channel.") + log_admin("[src.owner] denied [key_name(H)]'s ERT request with the message [input].") + to_chat(H, "Incoming priority transmission from Central Command. Message as follows, Your ERT request has been denied for the following reasons: [input].") + else + src.owner.response_team() + + + else if(href_list["AdminFaxView"]) + if(!check_rights(R_ADMIN)) + return + + var/obj/item/fax = locate(href_list["AdminFaxView"]) + if(istype(fax, /obj/item/paper)) + var/obj/item/paper/P = fax + P.show_content(usr,1) + else if(istype(fax, /obj/item/photo)) + var/obj/item/photo/H = fax + H.show(usr) + else if(istype(fax, /obj/item/paper_bundle)) + //having multiple people turning pages on a paper_bundle can cause issues + //open a browse window listing the contents instead + var/data = "" + var/obj/item/paper_bundle/B = fax + + for(var/page = 1, page <= B.amount + 1, page++) + var/obj/pageobj = B.contents[page] + data += "Page [page] - [pageobj.name]
    " + + usr << browse(data, "window=PaperBundle[B.UID()]") + else + to_chat(usr, "The faxed item is not viewable. This is probably a bug, and should be reported on the tracker: [fax.type]") + + else if(href_list["AdminFaxViewPage"]) + if(!check_rights(R_ADMIN)) + return + + var/page = text2num(href_list["AdminFaxViewPage"]) + var/obj/item/paper_bundle/bundle = locate(href_list["paper_bundle"]) + + if(!bundle) return + + if(istype(bundle.contents[page], /obj/item/paper)) + var/obj/item/paper/P = bundle.contents[page] + P.show_content(usr, 1) + else if(istype(bundle.contents[page], /obj/item/photo)) + var/obj/item/photo/H = bundle.contents[page] + H.show(usr) + return + + else if(href_list["AdminFaxCreate"]) + if(!check_rights(R_ADMIN)) + return + + var/mob/sender = locate(href_list["AdminFaxCreate"]) + var/obj/machinery/photocopier/faxmachine/fax = locate(href_list["originfax"]) + var/faxtype = href_list["faxtype"] + var/reply_to = locate(href_list["replyto"]) + var/destination + var/notify + var/obj/item/paper/P + var/use_letterheard = alert("Use letterhead? If so, do not add your own header or a footer. Type and format only your actual message.",,"Nanotrasen","Syndicate", "No") + switch(use_letterheard) + if("Nanotrasen") + P = new /obj/item/paper/central_command(null) + if("Syndicate") + P = new /obj/item/paper/syndicate(null) + if("No") + P = new /obj/item/paper(null) + if(!fax) + var/list/departmentoptions = GLOB.alldepartments + GLOB.hidden_departments + "All Departments" + destination = input(usr, "To which department?", "Choose a department", "") as null|anything in departmentoptions + if(!destination) + qdel(P) + return + + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) + if(destination != "All Departments" && F.department == destination) + fax = F + + + var/input = input(src.owner, "Please enter a message to send a fax via secure connection. Use
    for line breaks. Both pencode and HTML work.", "Outgoing message from Centcomm", "") as message|null + if(!input) + qdel(P) + return + input = admin_pencode_to_html(html_encode(input)) // Encode everything from pencode to html + + var/customname = clean_input("Pick a title for the fax.", "Fax Title", , owner) + if(!customname) + customname = "paper" + + var/stampname + var/stamptype + var/stampvalue + var/sendername + switch(faxtype) + if("Central Command") + stamptype = "icon" + stampvalue = "cent" + sendername = command_name() + if("Syndicate") + stamptype = "icon" + stampvalue = "syndicate" + sendername = "UNKNOWN" + if("Administrator") + stamptype = input(src.owner, "Pick a stamp type.", "Stamp Type") as null|anything in list("icon","text","none") + if(stamptype == "icon") + stampname = input(src.owner, "Pick a stamp icon.", "Stamp Icon") as null|anything in list("centcom","syndicate","granted","denied","clown") + switch(stampname) + if("centcom") + stampvalue = "cent" + if("syndicate") + stampvalue = "syndicate" + if("granted") + stampvalue = "ok" + if("denied") + stampvalue = "deny" + if("clown") + stampvalue = "clown" + else if(stamptype == "text") + stampvalue = clean_input("What should the stamp say?", "Stamp Text", , owner) + else if(stamptype == "none") + stamptype = "" + else + qdel(P) + return + + sendername = clean_input("What organization does the fax come from? This determines the prefix of the paper (i.e. Central Command- Title). This is optional.", "Organization", , owner) + + if(sender) + notify = alert(src.owner, "Would you like to inform the original sender that a fax has arrived?","Notify Sender","Yes","No") + + // Create the reply message + if(sendername) + P.name = "[sendername]- [customname]" + else + P.name = "[customname]" + P.info = input + P.update_icon() + P.x = rand(-2, 0) + P.y = rand(-1, 2) + P.offset_x += P.x + P.offset_y += P.y + if(stamptype) + var/image/stampoverlay = image('icons/obj/bureaucracy.dmi') + stampoverlay.pixel_x = P.x + stampoverlay.pixel_y = P.y + + if(!P.ico) + P.ico = new + P.ico += "paper_stamp-[stampvalue]" + stampoverlay.icon_state = "paper_stamp-[stampvalue]" + + if(stamptype == "icon") + if(!P.stamped) + P.stamped = new + P.stamped += /obj/item/stamp/centcom + P.overlays += stampoverlay + P.stamps += "
    " + + else if(stamptype == "text") + if(!P.stamped) + P.stamped = new + P.stamped += /obj/item/stamp + P.overlays += stampoverlay + P.stamps += "
    [stampvalue]" + + if(destination != "All Departments") + if(!fax.receivefax(P)) + to_chat(src.owner, "Message transmission failed.") + return + else + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) + if(is_station_level(F.z)) + spawn(0) + if(!F.receivefax(P)) + to_chat(src.owner, "Message transmission to [F.department] failed.") + + var/datum/fax/admin/A = new /datum/fax/admin() + A.name = P.name + A.from_department = faxtype + if(destination != "All Departments") + A.to_department = fax.department + else + A.to_department = "All Departments" + A.origin = "Administrator" + A.message = P + A.reply_to = reply_to + A.sent_by = usr + A.sent_at = world.time + + to_chat(src.owner, "Message transmitted successfully.") + if(notify == "Yes") + var/mob/living/carbon/human/H = sender + if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset))) + to_chat(sender, "Your headset pings, notifying you that a reply to your fax has arrived.") + if(sender) + log_admin("[key_name(src.owner)] replied to a fax message from [key_name(sender)]: [input]") + message_admins("[key_name_admin(src.owner)] replied to a fax message from [key_name_admin(sender)] (VIEW).", 1) + else + log_admin("[key_name(src.owner)] sent a fax message to [destination]: [input]") + message_admins("[key_name_admin(src.owner)] sent a fax message to [destination] (VIEW).", 1) + return + + else if(href_list["refreshfaxpanel"]) + if(!check_rights(R_ADMIN)) + return + + fax_panel(usr) + + else if(href_list["getplaytimewindow"]) + if(!check_rights(R_ADMIN)) + return + var/mob/M = locateUID(href_list["getplaytimewindow"]) + if(!M) + to_chat(usr, "ERROR: Mob not found.") + return + cmd_mentor_show_exp_panel(M.client) + + else if(href_list["jumpto"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["jumpto"]) + usr.client.jumptomob(M) + + else if(href_list["getmob"]) + if(!check_rights(R_ADMIN)) return + + if(alert(usr, "Confirm?", "Message", "Yes", "No") != "Yes") return + var/mob/M = locateUID(href_list["getmob"]) + usr.client.Getmob(M) + + else if(href_list["sendmob"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["sendmob"]) + usr.client.sendmob(M) + + else if(href_list["narrateto"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["narrateto"]) + usr.client.cmd_admin_direct_narrate(M) + + else if(href_list["subtlemessage"]) + if(!check_rights(R_ADMIN)) return + + var/mob/M = locateUID(href_list["subtlemessage"]) + usr.client.cmd_admin_subtle_message(M) + + else if(href_list["traitor"]) + if(!check_rights(R_ADMIN|R_MOD)) return + + if(!SSticker || !SSticker.mode) + alert("The game hasn't started yet!") + return + + var/mob/M = locateUID(href_list["traitor"]) + if(!ismob(M)) + to_chat(usr, "This can only be used on instances of type /mob.") + return + show_traitor_panel(M) + + else if(href_list["create_object"]) + if(!check_rights(R_SPAWN)) return + return create_object(usr) + + else if(href_list["quick_create_object"]) + if(!check_rights(R_SPAWN)) return + return quick_create_object(usr) + + else if(href_list["create_turf"]) + if(!check_rights(R_SPAWN)) return + return create_turf(usr) + + else if(href_list["create_mob"]) + if(!check_rights(R_SPAWN)) return + return create_mob(usr) + + else if(href_list["object_list"]) //this is the laggiest thing ever + if(!check_rights(R_SPAWN)) return + + var/atom/loc = usr.loc + + var/dirty_paths + if(istext(href_list["object_list"])) + dirty_paths = list(href_list["object_list"]) + else if(istype(href_list["object_list"], /list)) + dirty_paths = href_list["object_list"] + + var/paths = list() + + for(var/dirty_path in dirty_paths) + var/path = text2path(dirty_path) + if(!path) + continue + else if(!ispath(path, /obj) && !ispath(path, /turf) && !ispath(path, /mob)) + continue + paths += path + + if(!paths) + alert("The path list you sent is empty") + return + if(length(paths) > 5) + alert("Select fewer object types, (max 5)") + return + + var/list/offset = splittext(href_list["offset"],",") + var/number = dd_range(1, 100, text2num(href_list["object_count"])) + var/X = offset.len > 0 ? text2num(offset[1]) : 0 + var/Y = offset.len > 1 ? text2num(offset[2]) : 0 + var/Z = offset.len > 2 ? text2num(offset[3]) : 0 + var/tmp_dir = href_list["object_dir"] + var/obj_dir = tmp_dir ? text2num(tmp_dir) : 2 + if(!obj_dir || !(obj_dir in list(1,2,4,8,5,6,9,10))) + obj_dir = 2 + var/obj_name = sanitize(href_list["object_name"]) + + + var/atom/target //Where the object will be spawned + var/where = href_list["object_where"] + if(!( where in list("onfloor","inhand","inmarked") )) + where = "onfloor" + + + switch(where) + if("inhand") + if(!iscarbon(usr) && !isrobot(usr)) + to_chat(usr, "Can only spawn in hand when you're a carbon mob or cyborg.") + where = "onfloor" + target = usr + + if("onfloor") + switch(href_list["offset_type"]) + if("absolute") + target = locate(0 + X,0 + Y,0 + Z) + if("relative") + target = locate(loc.x + X,loc.y + Y,loc.z + Z) + if("inmarked") + if(!marked_datum) + to_chat(usr, "You don't have any object marked. Abandoning spawn.") + return + else if(!istype(marked_datum,/atom)) + to_chat(usr, "The object you have marked cannot be used as a target. Target must be of type /atom. Abandoning spawn.") + return + else + target = marked_datum + + if(target) + for(var/path in paths) + for(var/i = 0; i < number; i++) + if(path in typesof(/turf)) + var/turf/O = target + var/turf/N = O.ChangeTurf(path) + if(N && obj_name) + N.name = obj_name + else + var/atom/O = new path(target) + if(O) + O.admin_spawned = TRUE + O.dir = obj_dir + if(obj_name) + O.name = obj_name + if(istype(O,/mob)) + var/mob/M = O + M.real_name = obj_name + if(where == "inhand" && isliving(usr) && istype(O, /obj/item)) + var/mob/living/L = usr + var/obj/item/I = O + L.put_in_hands(I) + if(isrobot(L)) + var/mob/living/silicon/robot/R = L + if(R.module) + R.module.modules += I + I.loc = R.module + R.module.rebuild() + R.activate_module(I) + R.module.fix_modules() + + if(number == 1) + log_admin("[key_name(usr)] created a [english_list(paths)]") + for(var/path in paths) + if(ispath(path, /mob)) + message_admins("[key_name_admin(usr)] created a [english_list(paths)]") + break + else + log_admin("[key_name(usr)] created [number]ea [english_list(paths)]") + for(var/path in paths) + if(ispath(path, /mob)) + message_admins("[key_name_admin(usr)] created [number]ea [english_list(paths)]") + break + return + + else if(href_list["kick_all_from_lobby"]) + if(!check_rights(R_ADMIN)) + return + if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/afkonly = text2num(href_list["afkonly"]) + if(alert("Are you sure you want to kick all [afkonly ? "AFK" : ""] clients from the lobby?","Confirmation","Yes","Cancel") != "Yes") + return + var/list/listkicked = kick_clients_in_lobby("You were kicked from the lobby by an Administrator.", afkonly) + + var/strkicked = "" + for(var/name in listkicked) + strkicked += "[name], " + message_admins("[key_name_admin(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") + log_admin("[key_name(usr)] has kicked [afkonly ? "all AFK" : "all"] clients from the lobby. [length(listkicked)] clients kicked: [strkicked ? strkicked : "--"]") + else + to_chat(usr, "You may only use this when the game is running.") + + else if(href_list["memoeditlist"]) + if(!check_rights(R_SERVER)) return + var/sql_key = sanitizeSQL("[href_list["memoeditlist"]]") + var/DBQuery/query_memoedits = GLOB.dbcon.NewQuery("SELECT edits FROM [format_table_name("memo")] WHERE (ckey = '[sql_key]')") + if(!query_memoedits.Execute()) + var/err = query_memoedits.ErrorMsg() + log_game("SQL ERROR obtaining edits from memo table. Error : \[[err]\]\n") + return + if(query_memoedits.NextRow()) + var/edit_log = query_memoedits.item[1] + usr << browse(edit_log,"window=memoeditlist") + + else if(href_list["secretsfun"]) + if(!check_rights(R_SERVER|R_EVENT)) return + + var/ok = 0 + switch(href_list["secretsfun"]) + if("sec_clothes") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SC") + for(var/obj/item/clothing/under/O in world) + qdel(O) + ok = 1 + if("sec_all_clothes") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SAC") + for(var/obj/item/clothing/O in world) + qdel(O) + ok = 1 + if("sec_classic1") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SC1") + for(var/obj/item/clothing/suit/fire/O in world) + qdel(O) + for(var/obj/structure/grille/O in world) + qdel(O) + if("monkey") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","M") + for(var/mob/living/carbon/human/H in GLOB.mob_list) + spawn(0) + H.monkeyize() + ok = 1 + if("corgi") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","M") + for(var/mob/living/carbon/human/H in GLOB.mob_list) + spawn(0) + H.corgize() + ok = 1 + if("honksquad") + if(usr.client.honksquad()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","HONK") + if("striketeam") + if(usr.client.strike_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Strike") + if("striketeam_syndicate") + if(usr.client.syndicate_strike_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Strike") + if("infiltrators_syndicate") + if(usr.client.syndicate_infiltration_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SyndieInfiltrationTeam") + if("gimmickteam") + if(usr.client.gimmick_team()) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","GimmickTeam") + if("tripleAI") + usr.client.triple_ai() + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","TriAI") + if("gravity") + if(!(SSticker && SSticker.mode)) + to_chat(usr, "Please wait until the game starts! Not sure how it will work otherwise.") + return + GLOB.gravity_is_on = !GLOB.gravity_is_on + for(var/area/A in world) + A.gravitychange(GLOB.gravity_is_on,A) + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","Grav") + if(GLOB.gravity_is_on) + log_admin("[key_name(usr)] toggled gravity on.", 1) + message_admins("[key_name_admin(usr)] toggled gravity on.", 1) + GLOB.event_announcement.Announce("Gravity generators are again functioning within normal parameters. Sorry for any inconvenience.") + else + log_admin("[key_name(usr)] toggled gravity off.", 1) + message_admins("[key_name_admin(usr)] toggled gravity off.", 1) + GLOB.event_announcement.Announce("Feedback surge detected in mass-distributions systems. Artifical gravity has been disabled whilst the system reinitializes. Further failures may result in a gravitational collapse and formation of blackholes. Have a nice day.") + + if("power") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","P") + log_admin("[key_name(usr)] made all areas powered", 1) + message_admins("[key_name_admin(usr)] made all areas powered", 1) + power_restore() + if("unpower") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","UP") + log_admin("[key_name(usr)] made all areas unpowered", 1) + message_admins("[key_name_admin(usr)] made all areas unpowered", 1) + power_failure() + if("quickpower") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","QP") + log_admin("[key_name(usr)] made all SMESs powered", 1) + message_admins("[key_name_admin(usr)] made all SMESs powered", 1) + power_restore_quick() + if("prisonwarp") + if(!SSticker) + alert("The game hasn't started yet!", null, null, null, null, null) + return + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","PW") + message_admins("[key_name_admin(usr)] teleported all players to the prison station.", 1) + for(var/mob/living/carbon/human/H in GLOB.mob_list) + var/turf/loc = find_loc(H) + var/security = 0 + if(!is_station_level(loc.z) || GLOB.prisonwarped.Find(H)) + +//don't warp them if they aren't ready or are already there + continue + H.Paralyse(5) + if(H.wear_id) + var/obj/item/card/id/id = H.get_idcard() + for(var/A in id.access) + if(A == ACCESS_SECURITY) + security++ + if(!security) + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/W in H) + if(istype(W, /obj/item/organ/external)) + continue + //don't strip organs + H.unEquip(W) + if(H.client) + H.client.screen -= W + if(W) + W.loc = H.loc + W.dropped(H) + W.layer = initial(W.layer) + W.plane = initial(W.plane) + //teleport person to cell + H.loc = pick(GLOB.prisonwarp) + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(H), slot_shoes) + else + //teleport security person + H.loc = pick(GLOB.prisonsecuritywarp) + GLOB.prisonwarped += H + if("traitor_all") + if(!SSticker) + alert("The game hasn't started yet!") + return + var/objective = sanitize(copytext(input("Enter an objective"),1,MAX_MESSAGE_LEN)) + if(!objective) + return + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","TA([objective])") + + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.stat == 2 || !H.client || !H.mind) continue + if(is_special_character(H)) continue + //traitorize(H, objective, 0) + H.mind.add_antag_datum(/datum/antagonist/traitor) + + for(var/mob/living/silicon/A in GLOB.player_list) + A.mind.add_antag_datum(/datum/antagonist/traitor) + + message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]", 1) + log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]") + + if("togglebombcap") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","BC") + + var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be between 4 and 128)", "New Bomb Cap", GLOB.max_ex_light_range) as num|null + if(newBombCap < 4) + return + if(newBombCap > 128) + newBombCap = 128 + + GLOB.max_ex_devastation_range = round(newBombCap/4) + GLOB.max_ex_heavy_range = round(newBombCap/2) + GLOB.max_ex_light_range = newBombCap + //I don't know why these are their own variables, but fuck it, they are. + GLOB.max_ex_flash_range = newBombCap + GLOB.max_ex_flame_range = newBombCap + + message_admins("[key_name_admin(usr)] changed the bomb cap to [GLOB.max_ex_devastation_range], [GLOB.max_ex_heavy_range], [GLOB.max_ex_light_range]") + log_admin("[key_name(usr)] changed the bomb cap to [GLOB.max_ex_devastation_range], [GLOB.max_ex_heavy_range], [GLOB.max_ex_light_range]") + + if("flicklights") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","FL") + while(!usr.stat) +//knock yourself out to stop the ghosts + for(var/mob/M in GLOB.player_list) + if(M.stat != 2 && prob(25)) + var/area/AffectedArea = get_area(M) + if(AffectedArea.name != "Space" && AffectedArea.name != "Engine Walls" && AffectedArea.name != "Chemical Lab Test Chamber" && AffectedArea.name != "Escape Shuttle" && AffectedArea.name != "Arrival Area" && AffectedArea.name != "Arrival Shuttle" && AffectedArea.name != "start area" && AffectedArea.name != "Engine Combustion Chamber") + AffectedArea.power_light = 0 + AffectedArea.power_change() + spawn(rand(55,185)) + AffectedArea.power_light = 1 + AffectedArea.power_change() + var/Message = rand(1,4) + switch(Message) + if(1) + M.show_message(text("You shudder as if cold..."), 1) + if(2) + M.show_message(text("You feel something gliding across your back..."), 1) + if(3) + M.show_message(text("Your eyes twitch, you feel like something you can't see is here..."), 1) + if(4) + M.show_message(text("You notice something moving out of the corner of your eye, but nothing is there..."), 1) + for(var/obj/W in orange(5,M)) + if(prob(25) && !W.anchored) + step_rand(W) + sleep(rand(100,1000)) + for(var/mob/M in GLOB.player_list) + if(M.stat != 2) + M.show_message(text("The chilling wind suddenly stops..."), 1) + if("lightout") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","LO") + message_admins("[key_name_admin(usr)] has broke a lot of lights", 1) + var/datum/event/electrical_storm/E = new /datum/event/electrical_storm + E.lightsoutAmount = 2 + if("blackout") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","BO") + message_admins("[key_name_admin(usr)] broke all lights", 1) + for(var/obj/machinery/light/L in GLOB.machines) + L.break_light_tube() + if("whiteout") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","WO") + message_admins("[key_name_admin(usr)] fixed all lights", 1) + for(var/obj/machinery/light/L in GLOB.machines) + L.fix() + if("floorlava") + feedback_inc("admin_secrets_fun_used", 1) + feedback_add_details("admin_secrets_fun_used", "LF") + var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") + if(sure == "No") + return + SSweather.run_weather(/datum/weather/floor_is_lava) + message_admins("[key_name_admin(usr)] made the floor lava") + if("fakelava") + feedback_inc("admin_secrets_fun_used", 1) + feedback_add_details("admin_secrets_fun_used", "LZ") + var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") + if(sure == "No") + return + SSweather.run_weather(/datum/weather/floor_is_lava/fake) + message_admins("[key_name_admin(usr)] made aesthetic lava on the floor") + if("weatherashstorm") + feedback_inc("admin_secrets_fun_used", 1) + feedback_add_details("admin_secrets_fun_used", "WA") + var/sure = alert(usr, "Are you sure you want to do this?", "Confirmation", "Yes", "No") + if(sure == "No") + return + SSweather.run_weather(/datum/weather/ash_storm) + message_admins("[key_name_admin(usr)] spawned an ash storm on the mining level") + if("retardify") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","RET") + for(var/mob/living/carbon/human/H in GLOB.player_list) + to_chat(H, "You suddenly feel stupid.") + H.setBrainLoss(60) + message_admins("[key_name_admin(usr)] made everybody retarded") + if("fakeguns") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","FG") + for(var/obj/item/W in world) + if(istype(W, /obj/item/clothing) || istype(W, /obj/item/card/id) || istype(W, /obj/item/disk) || istype(W, /obj/item/tank)) + continue + W.icon = 'icons/obj/guns/projectile.dmi' + W.icon_state = "revolver" + W.item_state = "gun" + message_admins("[key_name_admin(usr)] made every item look like a gun") + if("schoolgirl") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SG") + for(var/obj/item/clothing/under/W in world) + W.icon_state = "schoolgirl" + W.item_state = "w_suit" + W.item_color = "schoolgirl" + message_admins("[key_name_admin(usr)] activated Japanese Animes mode") + world << sound('sound/AI/animes.ogg') + if("eagles")//SCRAW + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","EgL") + for(var/obj/machinery/door/airlock/W in GLOB.airlocks) + if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison)) + W.req_access = list() + message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") + GLOB.event_announcement.Announce("Centcomm airlock control override activated. Please take this time to get acquainted with your coworkers.", new_sound = 'sound/AI/commandreport.ogg') + if("onlyone") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","OO") + usr.client.only_one() +// message_admins("[key_name_admin(usr)] has triggered HIGHLANDER") + if("onlyme") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","OM") + usr.client.only_me() + if("onlyoneteam") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","OOT") + usr.client.only_one_team() +// message_admins("[key_name_admin(usr)] has triggered ") + if("rolldice") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ROL") + usr.client.roll_dices() + if("guns") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SG") + var/survivor_probability = 0 + switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) + if("Some Antags") + survivor_probability = 25 + if("All Antags!") + survivor_probability = 100 + + rightandwrong(SUMMON_GUNS, usr, survivor_probability) + if("magic") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","SM") + var/survivor_probability = 0 + switch(alert("Do you want this to create survivors antagonists?", , "No Antags", "Some Antags", "All Antags!")) + if("Some Antags") + survivor_probability = 25 + if("All Antags!") + survivor_probability = 100 + + rightandwrong(SUMMON_MAGIC, usr, survivor_probability) + if("tdomereset") + var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel") + if(delete_mobs == "Cancel") + return + + var/area/thunderdome = locate(/area/tdome/arena) + if(delete_mobs == "Yes") + for(var/mob/living/mob in thunderdome) + qdel(mob) //Clear mobs + for(var/obj/obj in thunderdome) + if(!istype(obj,/obj/machinery/camera)) + qdel(obj) //Clear objects + + var/area/template = locate(/area/tdome/arena_source) + template.copy_contents_to(thunderdome) + + log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1) + message_admins("[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].") + + if("tdomestart") + var/confirmation = alert("Start a Thunderdome match?","Confirm","Yes","No") + if(confirmation == "No") + return + if(makeThunderdomeTeams()) + log_admin("[key_name(usr)] started a Thunderdome match!", 1) + message_admins("[key_name_admin(usr)] has started a Thunderdome match!") + else + log_admin("[key_name(usr)] attempted to start a Thunderdome match, but no ghosts signed up.", 1) + message_admins("[key_name_admin(usr)] tried starting a Thunderdome match, but no ghosts signed up.") + if("securitylevel0") + set_security_level(0) + message_admins("[key_name_admin(usr)] change security level to Green.", 1) + if("securitylevel1") + set_security_level(1) + message_admins("[key_name_admin(usr)] change security level to Blue.", 1) + if("securitylevel2") + set_security_level(2) + message_admins("[key_name_admin(usr)] change security level to Red.", 1) + if("securitylevel3") + set_security_level(3) + message_admins("[key_name_admin(usr)] change security level to Gamma.", 1) + if("securitylevel4") + set_security_level(4) + message_admins("[key_name_admin(usr)] change security level to Epsilon.", 1) + if("securitylevel5") + set_security_level(5) + message_admins("[key_name_admin(usr)] change security level to Delta.", 1) + if("moveminingshuttle") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ShM") + if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away")) + message_admins("[key_name_admin(usr)] moved mining shuttle") + log_admin("[key_name(usr)] moved the mining shuttle") + + if("movelaborshuttle") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ShL") + if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away")) + message_admins("[key_name_admin(usr)] moved labor shuttle") + log_admin("[key_name(usr)] moved the labor shuttle") + + if("moveferry") + feedback_inc("admin_secrets_fun_used",1) + feedback_add_details("admin_secrets_fun_used","ShF") + if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away")) + message_admins("[key_name_admin(usr)] moved the centcom ferry") + log_admin("[key_name(usr)] moved the centcom ferry") + + if(usr) + log_admin("[key_name(usr)] used secret [href_list["secretsfun"]]") + if(ok) + to_chat(world, text("A secret has been activated by []!", usr.key)) + + else if(href_list["secretsadmin"]) + if(!check_rights(R_ADMIN)) return + + var/ok = 0 + switch(href_list["secretsadmin"]) + if("list_signalers") + var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " + for(var/sig in GLOB.lastsignalers) + dat += "[sig]
    " + usr << browse(dat, "window=lastsignalers;size=800x500") + if("list_lawchanges") + var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " + for(var/sig in GLOB.lawchanges) + dat += "[sig]
    " + usr << browse(dat, "window=lawchanges;size=800x500") + if("list_job_debug") + var/dat = "Job Debug info.
    " + if(SSjobs) + for(var/line in SSjobs.job_debug) + dat += "[line]
    " + dat+= "*******

    " + for(var/datum/job/job in SSjobs.occupations) + if(!job) continue + dat += "job: [job.title], current_positions: [job.current_positions], total_positions: [job.total_positions]
    " + usr << browse(dat, "window=jobdebug;size=600x500") + if("showailaws") + output_ai_laws() + if("showgm") + if(!SSticker) + alert("The game hasn't started yet!") + else if(SSticker.mode) + alert("The game mode is [SSticker.mode.name]") + else alert("For some reason there's a ticker, but not a game mode") + if("manifest") + var/dat = "Showing Crew Manifest.
    " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.ckey) + dat += text("", H.name, H.get_assignment()) + dat += "
    NamePosition
    [][]
    " + usr << browse(dat, "window=manifest;size=440x410") + if("check_antagonist") + check_antagonists() + if("DNA") + var/dat = "Showing DNA from blood.
    " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.dna && H.ckey) + dat += "" + dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " + usr << browse(dat, "window=DNA;size=440x410") + if("fingerprints") + var/dat = "Showing Fingerprints.
    " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.ckey) + if(H.dna && H.dna.uni_identity) + dat += "" + else if(H.dna && !H.dna.uni_identity) + dat += "" + else if(!H.dna) + dat += "" + dat += "
    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    [H]H.dna.uni_identity = null
    [H]H.dna = null
    " + usr << browse(dat, "window=fingerprints;size=440x410") + if("night_shift_set") + var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") + switch(val) + if("Automatic") + if(config.enable_night_shifts) + SSnightshift.can_fire = TRUE + SSnightshift.fire() + else + SSnightshift.update_nightshift(FALSE, TRUE) + to_chat(usr, "Night shift set to automatic.") + if("On") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(TRUE, FALSE) + to_chat(usr, "Night shift forced on.") + if("Off") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(FALSE, FALSE) + to_chat(usr, "Night shift forced off.") + else + if(usr) + log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") + if(ok) + to_chat(world, text("A secret has been activated by []!", usr.key)) + + else if(href_list["secretscoder"]) + if(!check_rights(R_DEBUG)) return + + switch(href_list["secretscoder"]) + if("spawn_objects") + var/dat = "Admin Log
    " + for(var/l in GLOB.admin_log) + dat += "
  • [l]
  • " + if(!GLOB.admin_log.len) + dat += "No-one has done anything this round!" + usr << browse(dat, "window=admin_log") + if("maint_ACCESS_BRIG") + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) + if(ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list(ACCESS_BRIG) + message_admins("[key_name_admin(usr)] made all maint doors brig access-only.") + if("maint_access_engiebrig") + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) + if(ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list() + M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE) + message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.") + if("infinite_sec") + var/datum/job/J = SSjobs.GetJob("Security Officer") + if(!J) return + J.total_positions = -1 + J.spawn_positions = -1 + message_admins("[key_name_admin(usr)] has removed the cap on security officers.") + + else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here + src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. + src.access_news_network() + + else if(href_list["ac_set_channel_name"]) + src.admincaster_feed_channel.channel_name = strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) + while(findtext(src.admincaster_feed_channel.channel_name," ") == 1) + src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) + src.access_news_network() + + else if(href_list["ac_set_channel_lock"]) + src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked + src.access_news_network() + + else if(href_list["ac_submit_new_channel"]) + var/check = 0 + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) + src.admincaster_screen=7 + else + var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel") + if(choice=="Confirm") + var/datum/feed_channel/newChannel = new /datum/feed_channel + newChannel.channel_name = src.admincaster_feed_channel.channel_name + newChannel.author = src.admincaster_signature + newChannel.locked = src.admincaster_feed_channel.locked + newChannel.is_admin_channel = 1 + feedback_inc("newscaster_channels",1) + GLOB.news_network.network_channels += newChannel //Adding channel to the global network + log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") + src.admincaster_screen=5 + src.access_news_network() + + else if(href_list["ac_set_channel_receiving"]) + var/list/available_channels = list() + for(var/datum/feed_channel/F in GLOB.news_network.network_channels) + available_channels += F.channel_name + src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels ) + src.access_news_network() + + else if(href_list["ac_set_new_message"]) + src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story", "Network Channel Handler", "")) + while(findtext(src.admincaster_feed_message.body," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_new_message"]) + if(src.admincaster_feed_message.body =="" || src.admincaster_feed_message.body =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) + src.admincaster_screen = 6 + else + var/datum/feed_message/newMsg = new /datum/feed_message + newMsg.author = src.admincaster_signature + newMsg.body = src.admincaster_feed_message.body + newMsg.is_admin_message = 1 + feedback_inc("newscaster_stories",1) + for(var/datum/feed_channel/FC in GLOB.news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + FC.messages += newMsg //Adding message to the network's appropriate feed_channel + break + src.admincaster_screen=4 + + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) + NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) + + log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") + src.access_news_network() + + else if(href_list["ac_create_channel"]) + src.admincaster_screen=2 + src.access_news_network() + + else if(href_list["ac_create_feed_story"]) + src.admincaster_screen=3 + src.access_news_network() + + else if(href_list["ac_menu_censor_story"]) + src.admincaster_screen=10 + src.access_news_network() + + else if(href_list["ac_menu_censor_channel"]) + src.admincaster_screen=11 + src.access_news_network() + + else if(href_list["ac_menu_wanted"]) + var/already_wanted = 0 + if(GLOB.news_network.wanted_issue) + already_wanted = 1 + + if(already_wanted) + src.admincaster_feed_message.author = GLOB.news_network.wanted_issue.author + src.admincaster_feed_message.body = GLOB.news_network.wanted_issue.body + src.admincaster_screen = 14 + src.access_news_network() + + else if(href_list["ac_set_wanted_name"]) + src.admincaster_feed_message.author = adminscrub(input(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) + while(findtext(src.admincaster_feed_message.author," ") == 1) + src.admincaster_feed_message.author = copytext(admincaster_feed_message.author,2,length(admincaster_feed_message.author)+1) + src.access_news_network() + + else if(href_list["ac_set_wanted_desc"]) + src.admincaster_feed_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) + while(findtext(src.admincaster_feed_message.body," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_wanted"]) + var/input_param = text2num(href_list["ac_submit_wanted"]) + if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") + src.admincaster_screen = 16 + else + var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below + var/datum/feed_message/WANTED = new /datum/feed_message + WANTED.author = src.admincaster_feed_message.author //Wanted name + WANTED.body = src.admincaster_feed_message.body //Wanted desc + WANTED.backup_author = src.admincaster_signature //Submitted by + WANTED.is_admin_message = 1 + GLOB.news_network.wanted_issue = WANTED + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) + NEWSCASTER.newsAlert() + NEWSCASTER.update_icon() + src.admincaster_screen = 15 + else + GLOB.news_network.wanted_issue.author = src.admincaster_feed_message.author + GLOB.news_network.wanted_issue.body = src.admincaster_feed_message.body + GLOB.news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author + src.admincaster_screen = 19 + log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") + src.access_news_network() + + else if(href_list["ac_cancel_wanted"]) + var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + GLOB.news_network.wanted_issue = null + for(var/obj/machinery/newscaster/NEWSCASTER in GLOB.allNewscasters) + NEWSCASTER.update_icon() + src.admincaster_screen=17 + src.access_news_network() + + else if(href_list["ac_censor_channel_author"]) + var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) + if(FC.author != "\[REDACTED\]") + FC.backup_author = FC.author + FC.author = "\[REDACTED\]" + else + FC.author = FC.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_author"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) + if(MSG.author != "\[REDACTED\]") + MSG.backup_author = MSG.author + MSG.author = "\[REDACTED\]" + else + MSG.author = MSG.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_body"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) + if(MSG.body != "\[REDACTED\]") + MSG.backup_body = MSG.body + MSG.body = "\[REDACTED\]" + else + MSG.body = MSG.backup_body + src.access_news_network() + + else if(href_list["ac_pick_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) + src.admincaster_feed_channel = FC + src.admincaster_screen=13 + src.access_news_network() + + else if(href_list["ac_toggle_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) + FC.censored = !FC.censored + src.access_news_network() + + else if(href_list["ac_view"]) + src.admincaster_screen=1 + src.access_news_network() + + else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ + src.admincaster_screen = text2num(href_list["ac_setScreen"]) + if(src.admincaster_screen == 0) + if(src.admincaster_feed_channel) + src.admincaster_feed_channel = new /datum/feed_channel + if(src.admincaster_feed_message) + src.admincaster_feed_message = new /datum/feed_message + src.access_news_network() + + else if(href_list["ac_show_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 9 + src.access_news_network() + + else if(href_list["ac_pick_censor_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 12 + src.access_news_network() + + else if(href_list["ac_refresh"]) + src.access_news_network() + + else if(href_list["ac_set_signature"]) + src.admincaster_signature = adminscrub(input(usr, "Provide your desired signature", "Network Identity Handler", "")) + src.access_news_network() + + if(href_list["secretsmenu"]) + switch(href_list["secretsmenu"]) + if("tab") + current_tab = text2num(href_list["tab"]) + Secrets(usr) + return 1 + + else if(href_list["viewruntime"]) + var/datum/ErrorViewer/error_viewer = locateUID(href_list["viewruntime"]) + if(!istype(error_viewer)) + to_chat(usr, "That runtime viewer no longer exists.") + return + if(href_list["viewruntime_backto"]) + error_viewer.showTo(usr, locateUID(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) + else + error_viewer.showTo(usr, null, href_list["viewruntime_linear"]) + + else if(href_list["add_station_goal"]) + if(!check_rights(R_EVENT)) + return + var/list/type_choices = typesof(/datum/station_goal) + var/picked = input("Choose goal type") in type_choices|null + if(!picked) + return + var/datum/station_goal/G = new picked() + if(picked == /datum/station_goal) + var/newname = clean_input("Enter goal name:") + if(!newname) + return + G.name = newname + var/description = input("Enter [command_name()] message contents:") as message|null + if(!description) + return + G.report_message = description + message_admins("[key_name_admin(usr)] created \"[G.name]\" station goal.") + SSticker.mode.station_goals += G + modify_goals() + + else if(href_list["showdetails"]) + if(!check_rights(R_ADMIN)) + return + var/text = html_decode(href_list["showdetails"]) + usr << browse("Details[replacetext(text, "\n", "
    ")]
    ", + "window=show_details;size=500x200") + + // Library stuff + else if(href_list["library_book_id"]) + var/isbn = sanitizeSQL(href_list["library_book_id"]) + + if(href_list["view_library_book"]) + var/DBQuery/query_view_book = GLOB.dbcon.NewQuery("SELECT content, title FROM [format_table_name("library")] WHERE id=[isbn]") + if(!query_view_book.Execute()) + var/err = query_view_book.ErrorMsg() + log_game("SQL ERROR viewing book. Error : \[[err]\]\n") + return + + var/content = "" + var/title = "" + while(query_view_book.NextRow()) + content = query_view_book.item[1] + title = html_encode(query_view_book.item[2]) + + var/dat = "
    "
    +			dat += "[html_encode(html_to_pencode(content))]"
    +			dat += "
    " + + var/datum/browser/popup = new(usr, "admin_view_book", "[title]", 700, 400) + popup.set_content(dat) + popup.open(0) + + log_admin("[key_name(usr)] has viewed the book [isbn].") + message_admins("[key_name_admin(usr)] has viewed the book [isbn].") + return + + else if(href_list["unflag_library_book"]) + var/DBQuery/query_unflag_book = GLOB.dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = 0 WHERE id=[isbn]") + if(!query_unflag_book.Execute()) + var/err = query_unflag_book.ErrorMsg() + log_game("SQL ERROR unflagging book. Error : \[[err]\]\n") + return + + log_admin("[key_name(usr)] has unflagged the book [isbn].") + message_admins("[key_name_admin(usr)] has unflagged the book [isbn].") + + else if(href_list["delete_library_book"]) + var/DBQuery/query_delbook = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[isbn]") + if(!query_delbook.Execute()) + var/err = query_delbook.ErrorMsg() + log_game("SQL ERROR deleting book. Error : \[[err]\]\n") + return + + log_admin("[key_name(usr)] has deleted the book [isbn].") + message_admins("[key_name_admin(usr)] has deleted the book [isbn].") + + // Refresh the page + src.view_flagged_books() + + // Force unlink a discord key + // TODO: Delete this + else if(href_list["force_discord_unlink"]) + if(!check_rights(R_ADMIN)) + return + var/target_ckey = href_list["force_discord_unlink"] + var/DBQuery/admin_unlink_discord_id = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[target_ckey]'") + if(!admin_unlink_discord_id.Execute()) + var/err = admin_unlink_discord_id.ErrorMsg() + log_game("SQL ERROR while admin-unlinking discord account. Error : \[[err]\]\n") + return + to_chat(src, "Successfully forcefully unlinked discord account from [target_ckey]") + message_admins("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") + log_admin("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") + + else if(href_list["create_outfit_finalize"]) + if(!check_rights(R_EVENT)) + return + create_outfit_finalize(usr,href_list) + else if(href_list["load_outfit"]) + if(!check_rights(R_EVENT)) + return + load_outfit(usr) + else if(href_list["create_outfit_menu"]) + if(!check_rights(R_EVENT)) + return + create_outfit(usr) + else if(href_list["delete_outfit"]) + if(!check_rights(R_EVENT)) + return + var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits + delete_outfit(usr,O) + else if(href_list["save_outfit"]) + if(!check_rights(R_EVENT)) + return + var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits + save_outfit(usr,O) + +/client/proc/create_eventmob_for(var/mob/living/carbon/human/H, var/killthem = 0) + if(!check_rights(R_EVENT)) + return + var/admin_outfits = subtypesof(/datum/outfit/admin) + var/hunter_outfits = list() + for(var/type in admin_outfits) + var/datum/outfit/admin/O = type + hunter_outfits[initial(O.name)] = type + var/dresscode = input("Select type", "Contracted Agents") as null|anything in hunter_outfits + if(isnull(dresscode)) + return + var/datum/outfit/O = hunter_outfits[dresscode] + message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") + var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) + if(!candidates.len) + to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") + return + var/mob/C = pick(candidates) + var/key_of_hunter = C.key + if(!key_of_hunter) + to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") + return + var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) + hunter_mind.active = 1 + var/mob/living/carbon/human/hunter_mob = new /mob/living/carbon/human(pick(GLOB.latejoin)) + hunter_mind.transfer_to(hunter_mob) + hunter_mob.equipOutfit(O, FALSE) + var/obj/item/pinpointer/advpinpointer/N = new /obj/item/pinpointer/advpinpointer(hunter_mob) + hunter_mob.equip_to_slot_or_del(N, slot_in_backpack) + N.active = 1 + N.mode = 2 + N.target = H + N.point_at(N.target) + N.modelocked = TRUE + if(!locate(/obj/item/implant/dust, hunter_mob)) + var/obj/item/implant/dust/D = new /obj/item/implant/dust(hunter_mob) + D.implant(hunter_mob) + if(killthem) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = hunter_mind + kill_objective.target = H.mind + kill_objective.explanation_text = "Kill [H.real_name], the [H.mind.assigned_role]." + hunter_mind.objectives += kill_objective + else + var/datum/objective/protect/protect_objective = new + protect_objective.owner = hunter_mind + protect_objective.target = H.mind + protect_objective.explanation_text = "Protect [H.real_name], the [H.mind.assigned_role]." + hunter_mind.objectives += protect_objective + SSticker.mode.traitors |= hunter_mob.mind + to_chat(hunter_mob, "ATTENTION: You are now on a mission!") + to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. "); + if(killthem) + to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); + hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR + var/datum/atom_hud/antag/tatorhud = GLOB.huds[ANTAG_HUD_TRAITOR] + tatorhud.join_hud(hunter_mob) + set_antag_hud(hunter_mob, "hudsyndicate") + +/proc/admin_jump_link(var/atom/target) + if(!target) return + // The way admin jump links handle their src is weirdly inconsistent... + + . = ADMIN_FLW(target,"FLW") + if(isAI(target)) // AI core/eye follow links + var/mob/living/silicon/ai/A = target + if(A.client && A.eyeobj) // No point following clientless AI eyes + . += "|[ADMIN_FLW(A.eyeobj,"EYE")]" + else if(istype(target, /mob/dead/observer)) + var/mob/dead/observer/O = target + if(O.mind && O.mind.current) + . += "|[ADMIN_FLW(O.mind.current,"BDY")]" diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index e0fb1a46913a..75e051b1ff09 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -1,36 +1,36 @@ -/proc/getbrokeninhands() - set name = "Broken Sprite List" - set category = "Debug" - - var/text - for(var/A in typesof(/obj/item)) - var/obj/item/O = new A( locate(1,1,1) ) - if(!O) continue - var/icon/IL = new(O.lefthand_file) - var/list/Lstates = IL.IconStates() - var/icon/IR = new(O.righthand_file) - var/list/Rstates = IR.IconStates() - var/icon/J = new(O.icon) - var/list/istates = J.IconStates() - if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" - if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" - - - if(O.icon_state) - if(!istates.Find(O.icon_state)) - text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" - //if(O.item_state) - // if(!istates.Find(O.item_state)) - // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" - //text+="\n" - qdel(O) - if(text) - var/F = file("broken_hand_icons.txt") - fdel(F) - to_chat(F, text) - to_chat(world, "Completed and written to [F]") - +/proc/getbrokeninhands() + set name = "Broken Sprite List" + set category = "Debug" + + var/text + for(var/A in typesof(/obj/item)) + var/obj/item/O = new A( locate(1,1,1) ) + if(!O) continue + var/icon/IL = new(O.lefthand_file) + var/list/Lstates = IL.IconStates() + var/icon/IR = new(O.righthand_file) + var/list/Rstates = IR.IconStates() + var/icon/J = new(O.icon) + var/list/istates = J.IconStates() + if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" + if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" + + + if(O.icon_state) + if(!istates.Find(O.icon_state)) + text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" + //if(O.item_state) + // if(!istates.Find(O.item_state)) + // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" + //text+="\n" + qdel(O) + if(text) + var/F = file("broken_hand_icons.txt") + fdel(F) + to_chat(F, text) + to_chat(world, "Completed and written to [F]") + diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 76e222598936..8ac9c4fd2b0d 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -1,187 +1,187 @@ - - -//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! -var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as") - -/client/verb/adminhelp() - set category = "Admin" - set name = "Adminhelp" - - //handle muting and automuting - if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") - return - - adminhelped = 1 //Determines if they get the message to reply by clicking the name. - - var/msg - var/list/type = list("Mentorhelp","Adminhelp") - var/selected_type = input("Pick a category.", "Admin Help", null, null) as null|anything in type - if(selected_type) - msg = clean_input("Please enter your message.", "Admin Help", null) - - //clean the input msg - if(!msg) - return - - if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN)) - return - - msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) - if(!msg) return - var/original_msg = msg - - //explode the input msg into a list - var/list/msglist = splittext(msg, " ") - - //generate keywords lookup - var/list/surnames = list() - var/list/forenames = list() - var/list/ckeys = list() - for(var/mob/M in GLOB.mob_list) - var/list/indexing = list(M.real_name, M.name) - if(M.mind) indexing += M.mind.name - - for(var/string in indexing) - var/list/L = splittext(string, " ") - var/surname_found = 0 - //surnames - for(var/i=L.len, i>=1, i--) - var/word = ckey(L[i]) - if(word) - surnames[word] = M - surname_found = i - break - //forenames - for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" - if(isMhelp) - //Open a new adminticket and inform the user. - SSmentor_tickets.newTicket(src, prunedmsg, msg) - for(var/client/X in mentorholders + modholders + adminholders) - if(X.prefs.sound & SOUND_MENTORHELP) - X << 'sound/effects/adminhelp.ogg' - to_chat(X, msg) - else //Ahelp - //Open a new adminticket and inform the user. - SStickets.newTicket(src, prunedmsg, msg) - for(var/client/X in modholders + adminholders) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - window_flash(X) - to_chat(X, msg) - - - - //show it to the person adminhelping too - to_chat(src, "[selected_type]: [original_msg]") - - var/admin_number_present = adminholders.len - admin_number_afk - log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") - if(admin_number_present <= 0) - if(!admin_number_afk) - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") - feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) - var/admin_number_total = 0 //Total number of admins - var/admin_number_afk = 0 //Holds the number of admins who are afk - var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) - var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both - for(var/client/X in GLOB.admins) - admin_number_total++; - var/invalid = 0 - if(requiredflags != 0 && !check_rights_for(X, requiredflags)) - admin_number_ignored++ - invalid = 1 - if(X.is_afk()) - admin_number_afk++ - invalid = 1 - if(X.holder.fakekey) - admin_number_ignored++ - invalid = 1 - if(invalid) - admin_number_decrease++ - var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid - if(admin_number_present <= 0) - if(!admin_number_afk && !admin_number_ignored) - send2irc(source, "[msg] - No admins online") - else - send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") - return admin_number_present + + +//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! +GLOBAL_LIST_INIT(adminhelp_ignored_words, list("unknown","the","a","an","of","monkey","alien","as")) + +/client/verb/adminhelp() + set category = "Admin" + set name = "Adminhelp" + + //handle muting and automuting + if(prefs.muted & MUTE_ADMINHELP) + to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") + return + + adminhelped = 1 //Determines if they get the message to reply by clicking the name. + + var/msg + var/list/type = list("Mentorhelp","Adminhelp") + var/selected_type = input("Pick a category.", "Admin Help", null, null) as null|anything in type + if(selected_type) + msg = clean_input("Please enter your message.", "Admin Help", null) + + //clean the input msg + if(!msg) + return + + if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN)) + return + + msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) + if(!msg) return + var/original_msg = msg + + //explode the input msg into a list + var/list/msglist = splittext(msg, " ") + + //generate keywords lookup + var/list/surnames = list() + var/list/forenames = list() + var/list/ckeys = list() + for(var/mob/M in GLOB.mob_list) + var/list/indexing = list(M.real_name, M.name) + if(M.mind) indexing += M.mind.name + + for(var/string in indexing) + var/list/L = splittext(string, " ") + var/surname_found = 0 + //surnames + for(var/i=L.len, i>=1, i--) + var/word = ckey(L[i]) + if(word) + surnames[word] = M + surname_found = i + break + //forenames + for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" + if(isMhelp) + //Open a new adminticket and inform the user. + SSmentor_tickets.newTicket(src, prunedmsg, msg) + for(var/client/X in mentorholders + modholders + adminholders) + if(X.prefs.sound & SOUND_MENTORHELP) + X << 'sound/effects/adminhelp.ogg' + to_chat(X, msg) + else //Ahelp + //Open a new adminticket and inform the user. + SStickets.newTicket(src, prunedmsg, msg) + for(var/client/X in modholders + adminholders) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + window_flash(X) + to_chat(X, msg) + + + + //show it to the person adminhelping too + to_chat(src, "[selected_type]: [original_msg]") + + var/admin_number_present = adminholders.len - admin_number_afk + log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") + if(admin_number_present <= 0) + if(!admin_number_afk) + send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") + else + send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") + else + send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") + feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) + var/admin_number_total = 0 //Total number of admins + var/admin_number_afk = 0 //Holds the number of admins who are afk + var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) + var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both + for(var/client/X in GLOB.admins) + admin_number_total++; + var/invalid = 0 + if(requiredflags != 0 && !check_rights_for(X, requiredflags)) + admin_number_ignored++ + invalid = 1 + if(X.is_afk()) + admin_number_afk++ + invalid = 1 + if(X.holder.fakekey) + admin_number_ignored++ + invalid = 1 + if(invalid) + admin_number_decrease++ + var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid + if(admin_number_present <= 0) + if(!admin_number_afk && !admin_number_ignored) + send2irc(source, "[msg] - No admins online") + else + send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") + return admin_number_present diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 16a1c14694f3..0c1a8c5f14f0 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -1,161 +1,161 @@ -/client/proc/Jump(area/A in return_sorted_areas()) - set name = "Jump to Area" - set desc = "Area to jump to" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - if(!A) - return - - var/list/turfs = list() - for(var/turf/T in A) - if(T.density) - continue - if(locate(/obj/structure/grille, T)) // Quick check to not spawn in windows - continue - turfs.Add(T) - - var/turf/T = pick_n_take(turfs) - if(!T) - to_chat(src, "Nowhere to jump to!") - return - - admin_forcemove(usr, T) - log_admin("[key_name(usr)] jumped to [A]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [A]") - feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/jumptoturf(var/turf/T in world) - set name = "Jump to Turf" - set category = null - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) - admin_forcemove(usr, T) - feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/jumptomob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Jump to Mob" - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] jumped to [key_name(M)]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - if(src.mob) - var/mob/A = src.mob - var/turf/T = get_turf(M) - if(T && isturf(T)) - feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - admin_forcemove(A, M.loc) - else - to_chat(A, "This mob is not located in the game world.") - -/client/proc/jumptocoord(tx as num, ty as num, tz as num) - set category = "Admin" - set name = "Jump to Coordinate" - - if(!check_rights(R_ADMIN)) - return - - var/turf/T = locate(tx, ty, tz) - if(T) - admin_forcemove(usr, T) - if(isobserver(usr)) - var/mob/dead/observer/O = usr - O.ManualFollow(T) - feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") - -/client/proc/jumptokey() - set category = "Admin" - set name = "Jump to Key" - - if(!check_rights(R_ADMIN)) - return - - var/list/keys = list() - for(var/mob/M in GLOB.player_list) - keys += M.client - var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) - if(!selection) - to_chat(src, "No keys found.") - return - var/mob/M = selection:mob - log_admin("[key_name(usr)] jumped to [key_name(M)]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - - admin_forcemove(usr, M.loc) - - feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/Getmob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Get Mob" - set desc = "Mob to teleport" - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] teleported [key_name(M)]") - message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) - admin_forcemove(M, get_turf(usr)) - feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/Getkey() - set category = "Admin" - set name = "Get Key" - set desc = "Key to teleport" - - if(!check_rights(R_ADMIN)) - return - - var/list/keys = list() - for(var/mob/M in GLOB.player_list) - keys += M.client - var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) - if(!selection) - return - var/mob/M = selection:mob - - if(!M) - return - log_admin("[key_name(usr)] teleported [key_name(M)]") - message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) - if(M) - admin_forcemove(M, get_turf(usr)) - admin_forcemove(usr, M.loc) - feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/sendmob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Send Mob" - - if(!check_rights(R_ADMIN)) - return - - var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() - if(A) - admin_forcemove(M, pick(get_area_turfs(A))) - feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") - message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1) - -/proc/admin_forcemove(mob/mover, atom/newloc) - mover.forceMove(newloc) - mover.on_forcemove(newloc) - -/mob/proc/on_forcemove(atom/newloc) - return +/client/proc/Jump(area/A in return_sorted_areas()) + set name = "Jump to Area" + set desc = "Area to jump to" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + if(!A) + return + + var/list/turfs = list() + for(var/turf/T in A) + if(T.density) + continue + if(locate(/obj/structure/grille, T)) // Quick check to not spawn in windows + continue + turfs.Add(T) + + var/turf/T = pick_n_take(turfs) + if(!T) + to_chat(src, "Nowhere to jump to!") + return + + admin_forcemove(usr, T) + log_admin("[key_name(usr)] jumped to [A]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [A]") + feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/jumptoturf(var/turf/T in world) + set name = "Jump to Turf" + set category = null + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) + admin_forcemove(usr, T) + feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/jumptomob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Jump to Mob" + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] jumped to [key_name(M)]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + if(src.mob) + var/mob/A = src.mob + var/turf/T = get_turf(M) + if(T && isturf(T)) + feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + admin_forcemove(A, M.loc) + else + to_chat(A, "This mob is not located in the game world.") + +/client/proc/jumptocoord(tx as num, ty as num, tz as num) + set category = "Admin" + set name = "Jump to Coordinate" + + if(!check_rights(R_ADMIN)) + return + + var/turf/T = locate(tx, ty, tz) + if(T) + admin_forcemove(usr, T) + if(isobserver(usr)) + var/mob/dead/observer/O = usr + O.ManualFollow(T) + feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") + +/client/proc/jumptokey() + set category = "Admin" + set name = "Jump to Key" + + if(!check_rights(R_ADMIN)) + return + + var/list/keys = list() + for(var/mob/M in GLOB.player_list) + keys += M.client + var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) + if(!selection) + to_chat(src, "No keys found.") + return + var/mob/M = selection:mob + log_admin("[key_name(usr)] jumped to [key_name(M)]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + + admin_forcemove(usr, M.loc) + + feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Getmob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Get Mob" + set desc = "Mob to teleport" + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] teleported [key_name(M)]") + message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) + admin_forcemove(M, get_turf(usr)) + feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Getkey() + set category = "Admin" + set name = "Get Key" + set desc = "Key to teleport" + + if(!check_rights(R_ADMIN)) + return + + var/list/keys = list() + for(var/mob/M in GLOB.player_list) + keys += M.client + var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) + if(!selection) + return + var/mob/M = selection:mob + + if(!M) + return + log_admin("[key_name(usr)] teleported [key_name(M)]") + message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) + if(M) + admin_forcemove(M, get_turf(usr)) + admin_forcemove(usr, M.loc) + feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/sendmob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Send Mob" + + if(!check_rights(R_ADMIN)) + return + + var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() + if(A) + admin_forcemove(M, pick(get_area_turfs(A))) + feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") + message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1) + +/proc/admin_forcemove(mob/mover, atom/newloc) + mover.forceMove(newloc) + mover.on_forcemove(newloc) + +/mob/proc/on_forcemove(atom/newloc) + return diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 6f6b34849240..a0e8b4b90888 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -1,82 +1,82 @@ -/client/proc/cmd_admin_say(msg as text) - set category = "Admin" - set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite - set hidden = 1 - if(!check_rights(R_ADMIN)) return - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - if(!msg) return - - log_adminsay(msg, src) - - if(check_rights(R_ADMIN,0)) - for(var/client/C in GLOB.admins) - if(R_ADMIN & C.holder.rights) - msg = "[msg]" - to_chat(C, "ADMIN: [key_name(usr, 1)] ([admin_jump_link(mob)]): [msg]") - - feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_admin_say() - var/msg = input(src, null, "asay \"text\"") as text|null - cmd_admin_say(msg) - -/client/proc/cmd_mentor_say(msg as text) - set category = "Admin" - set name = "Msay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) - return - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - log_mentorsay(msg, src) - - if(!msg) - return - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) - var/display_name = key - if(holder.fakekey) - if(C.holder && C.holder.rights & R_ADMIN) - display_name = "[holder.fakekey]/([key])" - else - display_name = holder.fakekey - msg = "[msg]" - to_chat(C, "MENTOR: [display_name] ([admin_jump_link(mob)]): [msg]") - - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_mentor_chat() - set category = "Server" - set name = "Toggle Mentor Chat" - set desc = "Toggle whether mentors have access to the msay command" - - if(!check_rights(R_ADMIN)) - return - - var/enabling - var/msay = /client/proc/cmd_mentor_say - - if(msay in admin_verbs_mentor) - enabling = FALSE - admin_verbs_mentor -= msay - else - enabling = TRUE - admin_verbs_mentor += msay - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) - continue - if(!check_rights(R_MENTOR, 0, C.mob)) - continue - if(enabling) - C.verbs += msay - to_chat(C, "Mentor chat has been enabled. Use 'msay' to speak in it.") - else - C.verbs -= msay - to_chat(C, "Mentor chat has been disabled.") - - admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") - feedback_add_details("admin_verb", "TMC") +/client/proc/cmd_admin_say(msg as text) + set category = "Admin" + set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite + set hidden = 1 + if(!check_rights(R_ADMIN)) return + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + if(!msg) return + + log_adminsay(msg, src) + + if(check_rights(R_ADMIN,0)) + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + msg = "[msg]" + to_chat(C, "ADMIN: [key_name(usr, 1)] ([admin_jump_link(mob)]): [msg]") + + feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_admin_say() + var/msg = input(src, null, "asay \"text\"") as text|null + cmd_admin_say(msg) + +/client/proc/cmd_mentor_say(msg as text) + set category = "Admin" + set name = "Msay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) + return + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + log_mentorsay(msg, src) + + if(!msg) + return + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) + var/display_name = key + if(holder.fakekey) + if(C.holder && C.holder.rights & R_ADMIN) + display_name = "[holder.fakekey]/([key])" + else + display_name = holder.fakekey + msg = "[msg]" + to_chat(C, "MENTOR: [display_name] ([admin_jump_link(mob)]): [msg]") + + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_mentor_chat() + set category = "Server" + set name = "Toggle Mentor Chat" + set desc = "Toggle whether mentors have access to the msay command" + + if(!check_rights(R_ADMIN)) + return + + var/enabling + var/msay = /client/proc/cmd_mentor_say + + if(msay in GLOB.admin_verbs_mentor) + enabling = FALSE + GLOB.admin_verbs_mentor -= msay + else + enabling = TRUE + GLOB.admin_verbs_mentor += msay + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) + continue + if(!check_rights(R_MENTOR, 0, C.mob)) + continue + if(enabling) + C.verbs += msay + to_chat(C, "Mentor chat has been enabled. Use 'msay' to speak in it.") + else + C.verbs -= msay + to_chat(C, "Mentor chat has been disabled.") + + admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") + feedback_add_details("admin_verb", "TMC") diff --git a/code/modules/admin/verbs/alt_check.dm b/code/modules/admin/verbs/alt_check.dm index d5118d4eb8f6..c8060f220369 100644 --- a/code/modules/admin/verbs/alt_check.dm +++ b/code/modules/admin/verbs/alt_check.dm @@ -6,7 +6,7 @@
    Additionally make an attempt to introduce new players to the server
    "} - if(dbcon.IsConnected()) + if(GLOB.dbcon.IsConnected()) for(var/client/C in GLOB.clients) dat += "

    [C.ckey] (Player Age: [C.player_age]) - [C.computer_id] / [C.address]
    " if(C.related_accounts_cid.len) diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index de281d34e68a..087a90eebe6a 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -1,63 +1,63 @@ -/client/proc/atmosscan() - set category = "Mapping" - set name = "Check Piping" - set background = 1 - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") - return - - to_chat(usr, "Checking for disconnected pipes...") - //all plumbing - yes, some things might get stated twice, doesn't matter. - for(var/obj/machinery/atmospherics/plumbing in world) - if(plumbing.nodealert) - to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") - - //Manifolds - for(var/obj/machinery/atmospherics/pipe/manifold/pipe in world) - if(!pipe.node1 || !pipe.node2 || !pipe.node3) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - //Pipes - for(var/obj/machinery/atmospherics/pipe/simple/pipe in world) - if(!pipe.node1 || !pipe.node2) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - to_chat(usr, "Checking for overlapping pipes...") - for(var/turf/T in world) - for(var/dir in cardinal) - var/list/check = list(0, 0, 0) - var/done = 0 - for(var/obj/machinery/atmospherics/pipe in T) - if(dir & pipe.initialize_directions) - for(var/ct in pipe.connect_types) - check[ct]++ - if(check[ct] > 1) - to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") - done = 1 - break - if(done) - break - to_chat(usr, "Done") - -/client/proc/powerdebug() - set category = "Mapping" - set name = "Check Power" - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - for(var/datum/powernet/PN in SSmachines.powernets) - if(!PN.nodes || !PN.nodes.len) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") - - if(!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") +/client/proc/atmosscan() + set category = "Mapping" + set name = "Check Piping" + set background = 1 + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") + return + + to_chat(usr, "Checking for disconnected pipes...") + //all plumbing - yes, some things might get stated twice, doesn't matter. + for(var/obj/machinery/atmospherics/plumbing in world) + if(plumbing.nodealert) + to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") + + //Manifolds + for(var/obj/machinery/atmospherics/pipe/manifold/pipe in world) + if(!pipe.node1 || !pipe.node2 || !pipe.node3) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + //Pipes + for(var/obj/machinery/atmospherics/pipe/simple/pipe in world) + if(!pipe.node1 || !pipe.node2) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + to_chat(usr, "Checking for overlapping pipes...") + for(var/turf/T in world) + for(var/dir in GLOB.cardinal) + var/list/check = list(0, 0, 0) + var/done = 0 + for(var/obj/machinery/atmospherics/pipe in T) + if(dir & pipe.initialize_directions) + for(var/ct in pipe.connect_types) + check[ct]++ + if(check[ct] > 1) + to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") + done = 1 + break + if(done) + break + to_chat(usr, "Done") + +/client/proc/powerdebug() + set category = "Mapping" + set name = "Check Power" + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + for(var/datum/powernet/PN in SSmachines.powernets) + if(!PN.nodes || !PN.nodes.len) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") + + if(!PN.cables || (PN.cables.len < 10)) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") diff --git a/code/modules/admin/verbs/cinematic.dm b/code/modules/admin/verbs/cinematic.dm index 63ebd52d09bc..5bfd7c653120 100644 --- a/code/modules/admin/verbs/cinematic.dm +++ b/code/modules/admin/verbs/cinematic.dm @@ -14,4 +14,4 @@ override = input(src, "mode = ?","Enter Parameter", null) as anything in list("nuclear emergency", "fake", "no override") if(0) override = input(src, "mode = ?","Enter Parameter", null) as anything in list("blob", "nuclear emergency", "AI malfunction", "no override") - SSticker.station_explosion_cinematic(parameter, override) \ No newline at end of file + SSticker.station_explosion_cinematic(parameter, override) diff --git a/code/modules/admin/verbs/custom_event.dm b/code/modules/admin/verbs/custom_event.dm index 68d3301b11e7..b3d1543aedcf 100644 --- a/code/modules/admin/verbs/custom_event.dm +++ b/code/modules/admin/verbs/custom_event.dm @@ -7,9 +7,9 @@ to_chat(src, "Only administrators may use this command.") return - var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", custom_event_msg) as message|null + var/input = input(usr, "Enter the description of the custom event. Be descriptive. To cancel the event, make this blank or hit cancel.", "Custom Event", GLOB.custom_event_msg) as message|null if(!input || input == "") - custom_event_msg = null + GLOB.custom_event_msg = null log_admin("[key_name(usr)] has cleared the custom event text.") message_admins("[key_name_admin(usr)] has cleared the custom event text.") return @@ -17,11 +17,11 @@ log_admin("[key_name(usr)] has changed the custom event text.") message_admins("[key_name_admin(usr)] has changed the custom event text.") - custom_event_msg = input + GLOB.custom_event_msg = input to_chat(world, "

    Custom Event

    ") to_chat(world, "

    A custom event is starting. OOC Info:

    ") - to_chat(world, "[html_encode(custom_event_msg)]") + to_chat(world, "[html_encode(GLOB.custom_event_msg)]") to_chat(world, "
    ") // normal verb for players to view info @@ -29,12 +29,12 @@ set category = "OOC" set name = "Custom Event Info" - if(!custom_event_msg || custom_event_msg == "") + if(!GLOB.custom_event_msg || GLOB.custom_event_msg == "") to_chat(src, "There currently is no known custom event taking place.") to_chat(src, "Keep in mind: it is possible that an admin has not properly set this.") return to_chat(src, "

    Custom Event

    ") to_chat(src, "

    A custom event is taking place. OOC Info:

    ") - to_chat(src, "[html_encode(custom_event_msg)]") + to_chat(src, "[html_encode(GLOB.custom_event_msg)]") to_chat(src, "
    ") diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index e1bbc885c751..aae13ad882d6 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -1,49 +1,49 @@ -/client/proc/dsay(msg as text) - set category = "Admin" - set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(!src.mob) - return - - if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") - return - - if(!(prefs.toggles & CHAT_DEAD)) - to_chat(src, "You have deadchat muted.") - return - - if(handle_spam_prevention(msg,MUTE_DEADCHAT)) - return - - var/stafftype = null - - if(check_rights(R_MENTOR, 0)) - stafftype = "MENTOR" - - if(check_rights(R_MOD, 0)) - stafftype = "MOD" - - if(check_rights(R_ADMIN, 0)) - stafftype = "ADMIN" - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - log_admin("[key_name(src)] : [msg]") - - if(!msg) - return - - var/prefix = "[stafftype] ([src.key])" - if(holder.fakekey) - prefix = "Administrator" - say_dead_direct("[prefix] says, \"[msg]\"") - - feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_dead_say() - var/msg = input(src, null, "dsay \"text\"") as text - dsay(msg) \ No newline at end of file +/client/proc/dsay(msg as text) + set category = "Admin" + set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(!src.mob) + return + + if(prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send DSAY messages (muted).") + return + + if(!(prefs.toggles & CHAT_DEAD)) + to_chat(src, "You have deadchat muted.") + return + + if(handle_spam_prevention(msg,MUTE_DEADCHAT)) + return + + var/stafftype = null + + if(check_rights(R_MENTOR, 0)) + stafftype = "MENTOR" + + if(check_rights(R_MOD, 0)) + stafftype = "MOD" + + if(check_rights(R_ADMIN, 0)) + stafftype = "ADMIN" + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + log_admin("[key_name(src)] : [msg]") + + if(!msg) + return + + var/prefix = "[stafftype] ([src.key])" + if(holder.fakekey) + prefix = "Administrator" + say_dead_direct("[prefix] says, \"[msg]\"") + + feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_dead_say() + var/msg = input(src, null, "dsay \"text\"") as text + dsay(msg) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 57f88d20351f..5ec8ecde2530 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -1,892 +1,892 @@ -/client/proc/Debug2() - set category = "Debug" - set name = "Debug-Game" - - if(!check_rights(R_DEBUG)) - return - - if(Debug2) - Debug2 = 0 - message_admins("[key_name_admin(src)] toggled debugging off.") - log_admin("[key_name(src)] toggled debugging off.") - else - Debug2 = 1 - message_admins("[key_name_admin(src)] toggled debugging on.") - log_admin("[key_name(src)] toggled debugging on.") - - feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/* 21st Sept 2010 -Updated by Skie -- Still not perfect but better! -Stuff you can't do: -Call proc /mob/proc/Dizzy() for some player -Because if you select a player mob as owner it tries to do the proc for -/mob/living/carbon/human/ instead. And that gives a run-time error. -But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. -*/ - -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - - if(!check_rights(R_PROCCALL)) - return - - spawn(0) - var/target = null - var/targetselected = 0 - var/returnval = null - var/class = null - - switch(alert("Proc owned by something?",,"Yes","No")) - if("Yes") - targetselected = 1 - if(src.holder && src.holder.marked_datum) - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") - if(class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") - switch(class) - if("Obj") - target = input("Enter target:","Target",usr) as obj in world - if("Mob") - target = input("Enter target:","Target",usr) as mob in world - if("Area or Turf") - target = input("Enter target:","Target",usr.loc) as area|turf in world - if("Client") - var/list/keys = list() - for(var/client/C) - keys += C - target = input("Please, select a player!", "Selection", null, null) as null|anything in keys - if("Marked datum") - target = holder.marked_datum - else - return - if("No") - target = null - targetselected = 0 - - var/procname = clean_input("Proc path, eg: /proc/fake_blood","Path:", null) - if(!procname) return - - if(targetselected && !hascall(target,procname)) - to_chat(usr, "Error: callproc(): target has no such call [procname].") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, "Error: callproc(): owner of proc no longer exists.") - return - message_admins("[key_name_admin(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - message_admins("[key_name_admin(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc - - to_chat(usr, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") - feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) - -/proc/IsAdminAdvancedProcCall() -#ifdef TESTING - return FALSE -#else - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey -#endif - -/client/proc/callproc_datum(var/A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - - if(!check_rights(R_PROCCALL)) - return - - var/procname = clean_input("Proc name, eg: fake_blood","Proc:", null) - if(!procname) - return - - if(!hascall(A,procname)) - to_chat(usr, "Error: callproc_datum(): target has no such call [procname].") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(src, "Error: callproc_datum(): owner of proc no longer exists.") - return - message_admins("[key_name_admin(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - - spawn() - var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - to_chat(src, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") - - feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_callproc_args() - var/argnum = input("Number of arguments","Number:",0) as num|null - if(!argnum && (argnum!=0)) return - - var/list/lst = list() - //TODO: make a list to store whether each argument was initialised as null. - //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists - //this will protect us from a fair few errors ~Carn - - while(argnum--) - var/class = null - // Make a list with each index containing one variable, to be given to the proc - if(src.holder && src.holder.marked_datum) - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") - if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") - switch(class) - if("CANCEL") - return null - - if("text") - lst += clean_input("Enter new text:","Text",null) - - if("num") - lst += input("Enter new number:","Num",0) as num - - if("type") - lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - - if("reference") - lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world - - if("mob reference") - lst += input("Select reference:","Reference",usr) as mob in world - - if("file") - lst += input("Pick file:","File") as file - - if("icon") - lst += input("Pick icon:","Icon") as icon - - if("client") - var/list/keys = list() - for(var/mob/M in world) - keys += M.client - lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys - - if("mob's area") - var/mob/temp = input("Select mob", "Selection", usr) as mob in world - lst += temp.loc - - if("Marked datum") - lst += holder.marked_datum - return lst - -/client/proc/Cell() - set category = "Debug" - set name = "Air Status in Location" - - if(!check_rights(R_DEBUG)) - return - - if(!mob) - return - var/turf/T = mob.loc - - if(!( istype(T, /turf) )) - return - - var/datum/gas_mixture/env = T.return_air() - - var/t = "" - t+= "Nitrogen : [env.nitrogen]\n" - t+= "Oxygen : [env.oxygen]\n" - t+= "Plasma : [env.toxins]\n" - t+= "CO2: [env.carbon_dioxide]\n" - - usr.show_message(t, 1) - feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_robotize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Robot" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - log_admin("[key_name(src)] has robotized [M.key].") - spawn(10) - M:Robotize() - - else - alert("Invalid mob") - -/client/proc/cmd_admin_animalize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Simple Animal" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - - if(!M) - alert("That mob doesn't seem to exist, close the panel and try again.") - return - - if(istype(M, /mob/new_player)) - alert("The mob must not be a new_player.") - return - - log_admin("[key_name(src)] has animalized [M.key].") - spawn(10) - M.Animalize() - - -/client/proc/makepAI(var/turf/T in GLOB.mob_list) - set category = "Event" - set name = "Make pAI" - set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" - - if(!check_rights(R_SPAWN)) - return - - var/list/available = list() - for(var/mob/C in GLOB.mob_list) - if(C.key) - available.Add(C) - var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available - if(!choice) - return 0 - if(!istype(choice, /mob/dead/observer)) - var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank [choice.p_them()] out of [choice.p_their()] body and place [choice.p_them()] in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") - if(confirm != "Yes") - return 0 - var/obj/item/paicard/card = new(T) - var/mob/living/silicon/pai/pai = new(card) - var/raw_name = clean_input("Enter your pAI name:", "pAI Name", "Personal AI", choice) - var/new_name = reject_bad_name(raw_name, 1) - if(new_name) - pai.name = new_name - pai.real_name = new_name - else - to_chat(usr, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - pai.real_name = pai.name - pai.key = choice.key - card.setPersonality(pai) - for(var/datum/paiCandidate/candidate in paiController.pai_candidates) - if(candidate.key == choice.key) - paiController.pai_candidates.Remove(candidate) - feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_alienize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Alien" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has alienized [M.key].") - spawn(10) - M:Alienize() - feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into an alien.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) - else - alert("Invalid mob") - -/client/proc/cmd_admin_slimeize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make slime" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has slimeized [M.key].") - spawn(10) - M:slimeize() - feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into a slime.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.", 1) - else - alert("Invalid mob") - -/client/proc/cmd_admin_super(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Superhero" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - var/type = input("Pick the Superhero","Superhero") as null|anything in GLOB.all_superheroes - var/datum/superheroes/S = GLOB.all_superheroes[type] - if(S) - S.create(M) - log_admin("[key_name(src)] has turned [M.key] into a Superhero.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a Superhero.", 1) - else - alert("Invalid mob") - -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all() - set category = "Debug" - set name = "Del-All" - - if(!check_rights(R_DEBUG)) - return - - // to prevent REALLY stupid deletions - var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) - var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked - if(hsbitem) - for(var/atom/O in world) - if(istype(O, hsbitem)) - qdel(O) - log_admin("[key_name(src)] has deleted all instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_del_sing() - set category = "Debug" - set name = "Del Singulo / Tesla" - - if(!check_rights(R_DEBUG)) - return - - //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all - var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No") - if(confirm != "Yes") - return - - for(var/I in GLOB.singularities) - var/obj/singularity/S = I - if(!is_level_reachable(S.z)) - continue - qdel(S) - log_admin("[key_name(src)] has deleted all Singularities and Tesla orbs.") - message_admins("[key_name_admin(src)] has deleted all Singularities and Tesla orbs.", 0) - feedback_add_details("admin_verb","DELS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_make_powernets() - set category = "Debug" - set name = "Make Powernets" - - if(!check_rights(R_DEBUG)) - return - - SSmachines.makepowernets() - log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") - message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) - feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_grantfullaccess(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Grant Full Access" - - if(!check_rights(R_EVENT)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.wear_id) - var/obj/item/card/id/id = H.wear_id - if(istype(H.wear_id, /obj/item/pda)) - var/obj/item/pda/pda = H.wear_id - id = pda.id - id.icon_state = "gold" - id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - else - var/obj/item/card/id/id = new/obj/item/card/id(M); - id.icon_state = "gold" - id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - id.registered_name = H.real_name - id.assignment = "Captain" - id.name = "[id.registered_name]'s ID Card ([id.assignment])" - H.equip_to_slot_or_del(id, slot_wear_id) - H.update_inv_wear_id() - else - alert("Invalid mob") - feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(src)] has granted [M.key] full access.") - message_admins("[key_name_admin(usr)] has granted [M.key] full access.", 1) - -/client/proc/cmd_assume_direct_control(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Assume direct control" - set desc = "Direct intervention" - - if(!check_rights(R_DEBUG|R_ADMIN)) - return - - if(M.ckey) - if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") - return - else - var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) - ghost.ckey = M.ckey - message_admins("[key_name_admin(usr)] assumed direct control of [M].", 1) - log_admin("[key_name(usr)] assumed direct control of [M].") - var/mob/adminmob = src.mob - M.ckey = src.ckey - if( isobserver(adminmob) ) - qdel(adminmob) - feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/cmd_admin_areatest() - set category = "Mapping" - set name = "Test areas" - - if(!check_rights(R_DEBUG)) - return - - var/list/areas_all = list() - var/list/areas_with_APC = list() - var/list/areas_with_air_alarm = list() - var/list/areas_with_RC = list() - var/list/areas_with_light = list() - var/list/areas_with_LS = list() - var/list/areas_with_intercom = list() - var/list/areas_with_camera = list() - - var/list/areas_with_multiple_APCs = list() - var/list/areas_with_multiple_air_alarms = list() - - for(var/area/A in world) - areas_all |= A.type - - for(var/obj/machinery/power/apc/APC in world) - var/area/A = get_area(APC) - if(!A) - continue - if(!(A.type in areas_with_APC)) - areas_with_APC |= A.type - else - areas_with_multiple_APCs |= A.type - - for(var/obj/machinery/alarm/alarm in world) - var/area/A = get_area(alarm) - if(!A) - continue - if(!(A.type in areas_with_air_alarm)) - areas_with_air_alarm |= A.type - else - areas_with_multiple_air_alarms |= A.type - - for(var/obj/machinery/requests_console/RC in world) - var/area/A = get_area(RC) - if(!A) - continue - areas_with_RC |= A.type - - for(var/obj/machinery/light/L in world) - var/area/A = get_area(L) - if(!A) - continue - areas_with_light |= A.type - - for(var/obj/machinery/light_switch/LS in world) - var/area/A = get_area(LS) - if(!A) - continue - areas_with_LS |= A.type - - for(var/obj/item/radio/intercom/I in world) - var/area/A = get_area(I) - if(!A) - continue - areas_with_intercom |= A.type - - for(var/obj/machinery/camera/C in world) - var/area/A = get_area(C) - if(!A) - continue - areas_with_camera |= A.type - - var/list/areas_without_APC = areas_all - areas_with_APC - var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm - var/list/areas_without_RC = areas_all - areas_with_RC - var/list/areas_without_light = areas_all - areas_with_light - var/list/areas_without_LS = areas_all - areas_with_LS - var/list/areas_without_intercom = areas_all - areas_with_intercom - var/list/areas_without_camera = areas_all - areas_with_camera - - to_chat(world, "AREAS WITHOUT AN APC:") - for(var/areatype in areas_without_APC) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT AN AIR ALARM:") - for(var/areatype in areas_without_air_alarm) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITH TOO MANY APCS:") - for(var/areatype in areas_with_multiple_APCs) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITH TOO MANY AIR ALARMS:") - for(var/areatype in areas_with_multiple_air_alarms) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT A REQUEST CONSOLE:") - for(var/areatype in areas_without_RC) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY LIGHTS:") - for(var/areatype in areas_without_light) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT A LIGHT SWITCH:") - for(var/areatype in areas_without_LS) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY INTERCOMS:") - for(var/areatype in areas_without_intercom) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY CAMERAS:") - for(var/areatype in areas_without_camera) - to_chat(world, "* [areatype]") - -/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list) - set category = "Event" - set name = "Select equipment" - - if(!check_rights(R_EVENT)) - return - - if(!ishuman(M) && !isobserver(M)) - alert("Invalid mob") - return - - var/dresscode = robust_dress_shop() - - if(!dresscode) - return - - var/delete_pocket - var/mob/living/carbon/human/H - if(isobserver(M)) - H = M.change_mob_type(/mob/living/carbon/human, null, null, TRUE) - else - H = M - if(H.l_store || H.r_store || H.s_store) //saves a lot of time for admins and coders alike - if(alert("Should the items in their pockets be dropped? Selecting \"No\" will delete them.", "Robust quick dress shop", "Yes", "No") == "No") - delete_pocket = TRUE - - for (var/obj/item/I in H.get_equipped_items(delete_pocket)) - qdel(I) - if(dresscode != "Naked") - H.equipOutfit(dresscode) - - H.regenerate_icons() - - feedback_add_details("admin_verb", "SE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") - message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode].", 1) - -/client/proc/robust_dress_shop() - var/list/outfits = list( - "Naked", - "As Job...", - "Custom..." - ) - - var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - for(var/path in paths) - var/datum/outfit/O = path //not much to initalize here but whatever - if(initial(O.can_be_admin_equipped)) - outfits[initial(O.name)] = path - - var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in outfits - if(isnull(dresscode)) - return - - if(outfits[dresscode]) - dresscode = outfits[dresscode] - - if(dresscode == "As Job...") - var/list/job_paths = subtypesof(/datum/outfit/job) - var/list/job_outfits = list() - for(var/path in job_paths) - var/datum/outfit/O = path - if(initial(O.can_be_admin_equipped)) - job_outfits[initial(O.name)] = path - - dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in job_outfits - dresscode = job_outfits[dresscode] - if(isnull(dresscode)) - return - - if(dresscode == "Custom...") - var/list/custom_names = list() - for(var/datum/outfit/D in GLOB.custom_outfits) - custom_names[D.name] = D - var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names - dresscode = custom_names[selected_name] - if(isnull(dresscode)) - return - - return dresscode - -/client/proc/startSinglo() - set category = "Debug" - set name = "Start Singularity" - set desc = "Sets up the singularity and all machines to get power flowing through the station" - - if(!check_rights(R_DEBUG)) - return - - if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") - return - - for(var/obj/machinery/power/emitter/E in GLOB.machines) - if(E.anchored) - E.active = 1 - - for(var/obj/machinery/field/generator/F in GLOB.machines) - if(F.active == 0) - F.active = 1 - F.state = 2 - F.power = 250 - F.anchored = 1 - F.warming_up = 3 - F.start_fields() - F.update_icon() - - spawn(30) - for(var/obj/machinery/the_singularitygen/G in GLOB.machines) - if(G.anchored) - var/obj/singularity/S = new /obj/singularity(get_turf(G)) - S.energy = 800 - break - - for(var/obj/machinery/power/rad_collector/Rad in GLOB.machines) - if(Rad.anchored) - if(!Rad.P) - var/obj/item/tank/plasma/Plasma = new/obj/item/tank/plasma(Rad) - Plasma.air_contents.toxins = 70 - Rad.drainratio = 0 - Rad.P = Plasma - Plasma.loc = Rad - - if(!Rad.active) - Rad.toggle_power() - - for(var/obj/machinery/power/smes/SMES in GLOB.machines) - if(SMES.anchored) - SMES.input_attempt = 1 - -/client/proc/cmd_debug_mob_lists() - set category = "Debug" - set name = "Debug Mob Lists" - set desc = "For when you just gotta know" - - if(!check_rights(R_DEBUG)) - return - - switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Silicons","Clients","Respawnable Mobs")) - if("Players") - to_chat(usr, jointext(GLOB.player_list,",")) - if("Admins") - to_chat(usr, jointext(GLOB.admins,",")) - if("Mobs") - to_chat(usr, jointext(GLOB.mob_list,",")) - if("Living Mobs") - to_chat(usr, jointext(GLOB.living_mob_list,",")) - if("Dead Mobs") - to_chat(usr, jointext(GLOB.dead_mob_list,",")) - if("Silicons") - to_chat(usr, jointext(GLOB.silicon_mob_list,",")) - if("Clients") - to_chat(usr, jointext(GLOB.clients,",")) - if("Respawnable Mobs") - to_chat(usr, jointext(GLOB.respawnable_list,",")) - -/client/proc/cmd_display_del_log() - set category = "Debug" - set name = "Display del() Log" - set desc = "Display del's log of everything that's passed through it." - - if(!check_rights(R_DEBUG)) - return - - var/list/dellog = list("List of things that have gone through qdel this round

      ") - sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - dellog += "
    1. [path]
        " - if(I.failures) - dellog += "
      • Failures: [I.failures]
      • " - dellog += "
      • qdel() Count: [I.qdels]
      • " - dellog += "
      • Destroy() Cost: [I.destroy_time]ms
      • " - if(I.hard_deletes) - dellog += "
      • Total Hard Deletes [I.hard_deletes]
      • " - dellog += "
      • Time Spent Hard Deleting: [I.hard_delete_time]ms
      • " - if(I.slept_destroy) - dellog += "
      • Sleeps: [I.slept_destroy]
      • " - if(I.no_respect_force) - dellog += "
      • Ignored force: [I.no_respect_force]
      • " - if(I.no_hint) - dellog += "
      • No hint: [I.no_hint]
      • " - dellog += "
    2. " - - dellog += "
    " - - usr << browse(dellog.Join(), "window=dellog") - -/client/proc/cmd_display_del_log_simple() - set category = "Debug" - set name = "Display Simple del() Log" - set desc = "Display a compacted del's log." - - if(!check_rights(R_DEBUG)) - return - - var/dat = "List of things that failed to GC this round

    " - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - if(I.failures) - dat += "[I] - [I.failures] times
    " - - dat += "List of paths that did not return a qdel hint in Destroy()

    " - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - if(I.no_hint) - dat += "[I]
    " - - dat += "List of paths that slept in Destroy()

    " - for(var/path in SSgarbage.items) - var/datum/qdel_item/I = SSgarbage.items[path] - if(I.slept_destroy) - dat += "[I]
    " - - usr << browse(dat, "window=simpledellog") - -/client/proc/cmd_admin_toggle_block(var/mob/M,var/block) - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon)) - M.dna.SetSEState(block,!M.dna.GetSEState(block)) - genemutcheck(M,block,null,MUTCHK_FORCED) - M.update_mutations() - var/state="[M.dna.GetSEState(block)?"on":"off"]" - var/blockname=assigned_blocks[block] - message_admins("[key_name_admin(src)] has toggled [M.key]'s [blockname] block [state]!") - log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!") - else - alert("Invalid mob") - -/client/proc/reload_nanoui_resources() - set category = "Debug" - set name = "Reload NanoUI Resources" - set desc = "Force the client to redownload NanoUI Resources" - - // Close open NanoUIs. - SSnanoui.close_user_uis(usr) - - // Re-load the assets. - var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui) - assets.register() - - // Clear the user's cache so they get resent. - usr.client.cache = list() - -/client/proc/view_runtimes() - set category = "Debug" - set name = "View Runtimes" - set desc = "Open the Runtime Viewer" - - if(!check_rights(R_DEBUG)) - return - - error_cache.showTo(usr) - -/client/proc/jump_to_ruin() - set category = "Debug" - set name = "Jump to Ruin" - set desc = "Displays a list of all placed ruins to teleport to." - - if(!check_rights(R_DEBUG)) - return - - var/list/names = list() - for(var/i in GLOB.ruin_landmarks) - var/obj/effect/landmark/ruin/ruin_landmark = i - var/datum/map_template/ruin/template = ruin_landmark.ruin_template - - var/count = 1 - var/name = template.name - var/original_name = name - - while(name in names) - count++ - name = "[original_name] ([count])" - - names[name] = ruin_landmark - - var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in names - - var/obj/effect/landmark/ruin/landmark = names[ruinname] - - if(istype(landmark)) - var/datum/map_template/ruin/template = landmark.ruin_template - admin_forcemove(usr, get_turf(landmark)) - - to_chat(usr, "[template.name]") - to_chat(usr, "[template.description]") - - log_admin("[key_name(usr)] jumped to ruin [ruinname]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to ruin [ruinname]", 1) - - feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_medal_disable() - set category = "Debug" - set name = "Toggle Medal Disable" - set desc = "Toggles the safety lock on trying to contact the medal hub." - - if(!check_rights(R_DEBUG)) - return - - SSmedals.hub_enabled = !SSmedals.hub_enabled - - message_admins("[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") - feedback_add_details("admin_verb","TMH") // If... - log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") +/client/proc/Debug2() + set category = "Debug" + set name = "Debug-Game" + + if(!check_rights(R_DEBUG)) + return + + if(GLOB.debug2) + GLOB.debug2 = 0 + message_admins("[key_name_admin(src)] toggled debugging off.") + log_admin("[key_name(src)] toggled debugging off.") + else + GLOB.debug2 = 1 + message_admins("[key_name_admin(src)] toggled debugging on.") + log_admin("[key_name(src)] toggled debugging on.") + + feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/* 21st Sept 2010 +Updated by Skie -- Still not perfect but better! +Stuff you can't do: +Call proc /mob/proc/Dizzy() for some player +Because if you select a player mob as owner it tries to do the proc for +/mob/living/carbon/human/ instead. And that gives a run-time error. +But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. +*/ + +/client/proc/callproc() + set category = "Debug" + set name = "Advanced ProcCall" + + if(!check_rights(R_PROCCALL)) + return + + spawn(0) + var/target = null + var/targetselected = 0 + var/returnval = null + var/class = null + + switch(alert("Proc owned by something?",,"Yes","No")) + if("Yes") + targetselected = 1 + if(src.holder && src.holder.marked_datum) + class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") + if(class == "Marked datum ([holder.marked_datum.type])") + class = "Marked datum" + else + class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") + switch(class) + if("Obj") + target = input("Enter target:","Target",usr) as obj in world + if("Mob") + target = input("Enter target:","Target",usr) as mob in world + if("Area or Turf") + target = input("Enter target:","Target",usr.loc) as area|turf in world + if("Client") + var/list/keys = list() + for(var/client/C) + keys += C + target = input("Please, select a player!", "Selection", null, null) as null|anything in keys + if("Marked datum") + target = holder.marked_datum + else + return + if("No") + target = null + targetselected = 0 + + var/procname = clean_input("Proc path, eg: /proc/fake_blood","Path:", null) + if(!procname) return + + if(targetselected && !hascall(target,procname)) + to_chat(usr, "Error: callproc(): target has no such call [procname].") + return + + var/list/lst = get_callproc_args() + if(!lst) + return + + if(targetselected) + if(!target) + to_chat(usr, "Error: callproc(): owner of proc no longer exists.") + return + message_admins("[key_name_admin(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") + returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc + else + //this currently has no hascall protection. wasn't able to get it working. + message_admins("[key_name_admin(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc + + to_chat(usr, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") + feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +GLOBAL_VAR(AdminProcCaller) +GLOBAL_PROTECT(AdminProcCaller) + +/proc/IsAdminAdvancedProcCall() +#ifdef TESTING + return FALSE +#else + return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey +#endif + +/client/proc/callproc_datum(var/A as null|area|mob|obj|turf) + set category = "Debug" + set name = "Atom ProcCall" + + if(!check_rights(R_PROCCALL)) + return + + var/procname = clean_input("Proc name, eg: fake_blood","Proc:", null) + if(!procname) + return + + if(!hascall(A,procname)) + to_chat(usr, "Error: callproc_datum(): target has no such call [procname].") + return + + var/list/lst = get_callproc_args() + if(!lst) + return + + if(!A || !IsValidSrc(A)) + to_chat(src, "Error: callproc_datum(): owner of proc no longer exists.") + return + message_admins("[key_name_admin(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") + + spawn() + var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc + to_chat(src, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") + + feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_callproc_args() + var/argnum = input("Number of arguments","Number:",0) as num|null + if(!argnum && (argnum!=0)) return + + var/list/lst = list() + //TODO: make a list to store whether each argument was initialised as null. + //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists + //this will protect us from a fair few errors ~Carn + + while(argnum--) + var/class = null + // Make a list with each index containing one variable, to be given to the proc + if(src.holder && src.holder.marked_datum) + class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") + if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") + class = "Marked datum" + else + class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") + switch(class) + if("CANCEL") + return null + + if("text") + lst += clean_input("Enter new text:","Text",null) + + if("num") + lst += input("Enter new number:","Num",0) as num + + if("type") + lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) + + if("reference") + lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world + + if("mob reference") + lst += input("Select reference:","Reference",usr) as mob in world + + if("file") + lst += input("Pick file:","File") as file + + if("icon") + lst += input("Pick icon:","Icon") as icon + + if("client") + var/list/keys = list() + for(var/mob/M in world) + keys += M.client + lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys + + if("mob's area") + var/mob/temp = input("Select mob", "Selection", usr) as mob in world + lst += temp.loc + + if("Marked datum") + lst += holder.marked_datum + return lst + +/client/proc/Cell() + set category = "Debug" + set name = "Air Status in Location" + + if(!check_rights(R_DEBUG)) + return + + if(!mob) + return + var/turf/T = mob.loc + + if(!( istype(T, /turf) )) + return + + var/datum/gas_mixture/env = T.return_air() + + var/t = "" + t+= "Nitrogen : [env.nitrogen]\n" + t+= "Oxygen : [env.oxygen]\n" + t+= "Plasma : [env.toxins]\n" + t+= "CO2: [env.carbon_dioxide]\n" + + usr.show_message(t, 1) + feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_robotize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Robot" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + log_admin("[key_name(src)] has robotized [M.key].") + spawn(10) + M:Robotize() + + else + alert("Invalid mob") + +/client/proc/cmd_admin_animalize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Simple Animal" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + + if(!M) + alert("That mob doesn't seem to exist, close the panel and try again.") + return + + if(istype(M, /mob/new_player)) + alert("The mob must not be a new_player.") + return + + log_admin("[key_name(src)] has animalized [M.key].") + spawn(10) + M.Animalize() + + +/client/proc/makepAI(var/turf/T in GLOB.mob_list) + set category = "Event" + set name = "Make pAI" + set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" + + if(!check_rights(R_SPAWN)) + return + + var/list/available = list() + for(var/mob/C in GLOB.mob_list) + if(C.key) + available.Add(C) + var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available + if(!choice) + return 0 + if(!istype(choice, /mob/dead/observer)) + var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank [choice.p_them()] out of [choice.p_their()] body and place [choice.p_them()] in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") + if(confirm != "Yes") + return 0 + var/obj/item/paicard/card = new(T) + var/mob/living/silicon/pai/pai = new(card) + var/raw_name = clean_input("Enter your pAI name:", "pAI Name", "Personal AI", choice) + var/new_name = reject_bad_name(raw_name, 1) + if(new_name) + pai.name = new_name + pai.real_name = new_name + else + to_chat(usr, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") + pai.real_name = pai.name + pai.key = choice.key + card.setPersonality(pai) + for(var/datum/paiCandidate/candidate in GLOB.paiController.pai_candidates) + if(candidate.key == choice.key) + GLOB.paiController.pai_candidates.Remove(candidate) + feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_alienize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Alien" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has alienized [M.key].") + spawn(10) + M:Alienize() + feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into an alien.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) + else + alert("Invalid mob") + +/client/proc/cmd_admin_slimeize(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make slime" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + log_admin("[key_name(src)] has slimeized [M.key].") + spawn(10) + M:slimeize() + feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] made [key_name(M)] into a slime.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.", 1) + else + alert("Invalid mob") + +/client/proc/cmd_admin_super(var/mob/M in GLOB.mob_list) + set category = "Event" + set name = "Make Superhero" + + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(ishuman(M)) + var/type = input("Pick the Superhero","Superhero") as null|anything in GLOB.all_superheroes + var/datum/superheroes/S = GLOB.all_superheroes[type] + if(S) + S.create(M) + log_admin("[key_name(src)] has turned [M.key] into a Superhero.") + message_admins("[key_name_admin(usr)] made [key_name(M)] into a Superhero.", 1) + else + alert("Invalid mob") + +//TODO: merge the vievars version into this or something maybe mayhaps +/client/proc/cmd_debug_del_all() + set category = "Debug" + set name = "Del-All" + + if(!check_rights(R_DEBUG)) + return + + // to prevent REALLY stupid deletions + var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) + var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked + if(hsbitem) + for(var/atom/O in world) + if(istype(O, hsbitem)) + qdel(O) + log_admin("[key_name(src)] has deleted all instances of [hsbitem].") + message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) + feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_debug_del_sing() + set category = "Debug" + set name = "Del Singulo / Tesla" + + if(!check_rights(R_DEBUG)) + return + + //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all + var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No") + if(confirm != "Yes") + return + + for(var/I in GLOB.singularities) + var/obj/singularity/S = I + if(!is_level_reachable(S.z)) + continue + qdel(S) + log_admin("[key_name(src)] has deleted all Singularities and Tesla orbs.") + message_admins("[key_name_admin(src)] has deleted all Singularities and Tesla orbs.", 0) + feedback_add_details("admin_verb","DELS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_debug_make_powernets() + set category = "Debug" + set name = "Make Powernets" + + if(!check_rights(R_DEBUG)) + return + + SSmachines.makepowernets() + log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") + message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) + feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_grantfullaccess(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Grant Full Access" + + if(!check_rights(R_EVENT)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + if(H.wear_id) + var/obj/item/card/id/id = H.wear_id + if(istype(H.wear_id, /obj/item/pda)) + var/obj/item/pda/pda = H.wear_id + id = pda.id + id.icon_state = "gold" + id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() + else + var/obj/item/card/id/id = new/obj/item/card/id(M); + id.icon_state = "gold" + id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() + id.registered_name = H.real_name + id.assignment = "Captain" + id.name = "[id.registered_name]'s ID Card ([id.assignment])" + H.equip_to_slot_or_del(id, slot_wear_id) + H.update_inv_wear_id() + else + alert("Invalid mob") + feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(src)] has granted [M.key] full access.") + message_admins("[key_name_admin(usr)] has granted [M.key] full access.", 1) + +/client/proc/cmd_assume_direct_control(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Assume direct control" + set desc = "Direct intervention" + + if(!check_rights(R_DEBUG|R_ADMIN)) + return + + if(M.ckey) + if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") + return + else + var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) + ghost.ckey = M.ckey + message_admins("[key_name_admin(usr)] assumed direct control of [M].", 1) + log_admin("[key_name(usr)] assumed direct control of [M].") + var/mob/adminmob = src.mob + M.ckey = src.ckey + if( isobserver(adminmob) ) + qdel(adminmob) + feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/cmd_admin_areatest() + set category = "Mapping" + set name = "Test areas" + + if(!check_rights(R_DEBUG)) + return + + var/list/areas_all = list() + var/list/areas_with_APC = list() + var/list/areas_with_air_alarm = list() + var/list/areas_with_RC = list() + var/list/areas_with_light = list() + var/list/areas_with_LS = list() + var/list/areas_with_intercom = list() + var/list/areas_with_camera = list() + + var/list/areas_with_multiple_APCs = list() + var/list/areas_with_multiple_air_alarms = list() + + for(var/area/A in world) + areas_all |= A.type + + for(var/obj/machinery/power/apc/APC in world) + var/area/A = get_area(APC) + if(!A) + continue + if(!(A.type in areas_with_APC)) + areas_with_APC |= A.type + else + areas_with_multiple_APCs |= A.type + + for(var/obj/machinery/alarm/alarm in world) + var/area/A = get_area(alarm) + if(!A) + continue + if(!(A.type in areas_with_air_alarm)) + areas_with_air_alarm |= A.type + else + areas_with_multiple_air_alarms |= A.type + + for(var/obj/machinery/requests_console/RC in world) + var/area/A = get_area(RC) + if(!A) + continue + areas_with_RC |= A.type + + for(var/obj/machinery/light/L in world) + var/area/A = get_area(L) + if(!A) + continue + areas_with_light |= A.type + + for(var/obj/machinery/light_switch/LS in world) + var/area/A = get_area(LS) + if(!A) + continue + areas_with_LS |= A.type + + for(var/obj/item/radio/intercom/I in world) + var/area/A = get_area(I) + if(!A) + continue + areas_with_intercom |= A.type + + for(var/obj/machinery/camera/C in world) + var/area/A = get_area(C) + if(!A) + continue + areas_with_camera |= A.type + + var/list/areas_without_APC = areas_all - areas_with_APC + var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm + var/list/areas_without_RC = areas_all - areas_with_RC + var/list/areas_without_light = areas_all - areas_with_light + var/list/areas_without_LS = areas_all - areas_with_LS + var/list/areas_without_intercom = areas_all - areas_with_intercom + var/list/areas_without_camera = areas_all - areas_with_camera + + to_chat(world, "AREAS WITHOUT AN APC:") + for(var/areatype in areas_without_APC) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT AN AIR ALARM:") + for(var/areatype in areas_without_air_alarm) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITH TOO MANY APCS:") + for(var/areatype in areas_with_multiple_APCs) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITH TOO MANY AIR ALARMS:") + for(var/areatype in areas_with_multiple_air_alarms) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT A REQUEST CONSOLE:") + for(var/areatype in areas_without_RC) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT ANY LIGHTS:") + for(var/areatype in areas_without_light) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT A LIGHT SWITCH:") + for(var/areatype in areas_without_LS) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT ANY INTERCOMS:") + for(var/areatype in areas_without_intercom) + to_chat(world, "* [areatype]") + + to_chat(world, "AREAS WITHOUT ANY CAMERAS:") + for(var/areatype in areas_without_camera) + to_chat(world, "* [areatype]") + +/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list) + set category = "Event" + set name = "Select equipment" + + if(!check_rights(R_EVENT)) + return + + if(!ishuman(M) && !isobserver(M)) + alert("Invalid mob") + return + + var/dresscode = robust_dress_shop() + + if(!dresscode) + return + + var/delete_pocket + var/mob/living/carbon/human/H + if(isobserver(M)) + H = M.change_mob_type(/mob/living/carbon/human, null, null, TRUE) + else + H = M + if(H.l_store || H.r_store || H.s_store) //saves a lot of time for admins and coders alike + if(alert("Should the items in their pockets be dropped? Selecting \"No\" will delete them.", "Robust quick dress shop", "Yes", "No") == "No") + delete_pocket = TRUE + + for (var/obj/item/I in H.get_equipped_items(delete_pocket)) + qdel(I) + if(dresscode != "Naked") + H.equipOutfit(dresscode) + + H.regenerate_icons() + + feedback_add_details("admin_verb", "SE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") + message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode].", 1) + +/client/proc/robust_dress_shop() + var/list/outfits = list( + "Naked", + "As Job...", + "Custom..." + ) + + var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) + for(var/path in paths) + var/datum/outfit/O = path //not much to initalize here but whatever + if(initial(O.can_be_admin_equipped)) + outfits[initial(O.name)] = path + + var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in outfits + if(isnull(dresscode)) + return + + if(outfits[dresscode]) + dresscode = outfits[dresscode] + + if(dresscode == "As Job...") + var/list/job_paths = subtypesof(/datum/outfit/job) + var/list/job_outfits = list() + for(var/path in job_paths) + var/datum/outfit/O = path + if(initial(O.can_be_admin_equipped)) + job_outfits[initial(O.name)] = path + + dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in job_outfits + dresscode = job_outfits[dresscode] + if(isnull(dresscode)) + return + + if(dresscode == "Custom...") + var/list/custom_names = list() + for(var/datum/outfit/D in GLOB.custom_outfits) + custom_names[D.name] = D + var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names + dresscode = custom_names[selected_name] + if(isnull(dresscode)) + return + + return dresscode + +/client/proc/startSinglo() + set category = "Debug" + set name = "Start Singularity" + set desc = "Sets up the singularity and all machines to get power flowing through the station" + + if(!check_rights(R_DEBUG)) + return + + if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") + return + + for(var/obj/machinery/power/emitter/E in GLOB.machines) + if(E.anchored) + E.active = 1 + + for(var/obj/machinery/field/generator/F in GLOB.machines) + if(F.active == 0) + F.active = 1 + F.state = 2 + F.power = 250 + F.anchored = 1 + F.warming_up = 3 + F.start_fields() + F.update_icon() + + spawn(30) + for(var/obj/machinery/the_singularitygen/G in GLOB.machines) + if(G.anchored) + var/obj/singularity/S = new /obj/singularity(get_turf(G)) + S.energy = 800 + break + + for(var/obj/machinery/power/rad_collector/Rad in GLOB.machines) + if(Rad.anchored) + if(!Rad.P) + var/obj/item/tank/plasma/Plasma = new/obj/item/tank/plasma(Rad) + Plasma.air_contents.toxins = 70 + Rad.drainratio = 0 + Rad.P = Plasma + Plasma.loc = Rad + + if(!Rad.active) + Rad.toggle_power() + + for(var/obj/machinery/power/smes/SMES in GLOB.machines) + if(SMES.anchored) + SMES.input_attempt = 1 + +/client/proc/cmd_debug_mob_lists() + set category = "Debug" + set name = "Debug Mob Lists" + set desc = "For when you just gotta know" + + if(!check_rights(R_DEBUG)) + return + + switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Silicons","Clients","Respawnable Mobs")) + if("Players") + to_chat(usr, jointext(GLOB.player_list,",")) + if("Admins") + to_chat(usr, jointext(GLOB.admins,",")) + if("Mobs") + to_chat(usr, jointext(GLOB.mob_list,",")) + if("Living Mobs") + to_chat(usr, jointext(GLOB.living_mob_list,",")) + if("Dead Mobs") + to_chat(usr, jointext(GLOB.dead_mob_list,",")) + if("Silicons") + to_chat(usr, jointext(GLOB.silicon_mob_list,",")) + if("Clients") + to_chat(usr, jointext(GLOB.clients,",")) + if("Respawnable Mobs") + to_chat(usr, jointext(GLOB.respawnable_list,",")) + +/client/proc/cmd_display_del_log() + set category = "Debug" + set name = "Display del() Log" + set desc = "Display del's log of everything that's passed through it." + + if(!check_rights(R_DEBUG)) + return + + var/list/dellog = list("List of things that have gone through qdel this round

      ") + sortTim(SSgarbage.items, cmp=/proc/cmp_qdel_item_time, associative = TRUE) + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + dellog += "
    1. [path]
        " + if(I.failures) + dellog += "
      • Failures: [I.failures]
      • " + dellog += "
      • qdel() Count: [I.qdels]
      • " + dellog += "
      • Destroy() Cost: [I.destroy_time]ms
      • " + if(I.hard_deletes) + dellog += "
      • Total Hard Deletes [I.hard_deletes]
      • " + dellog += "
      • Time Spent Hard Deleting: [I.hard_delete_time]ms
      • " + if(I.slept_destroy) + dellog += "
      • Sleeps: [I.slept_destroy]
      • " + if(I.no_respect_force) + dellog += "
      • Ignored force: [I.no_respect_force]
      • " + if(I.no_hint) + dellog += "
      • No hint: [I.no_hint]
      • " + dellog += "
    2. " + + dellog += "
    " + + usr << browse(dellog.Join(), "window=dellog") + +/client/proc/cmd_display_del_log_simple() + set category = "Debug" + set name = "Display Simple del() Log" + set desc = "Display a compacted del's log." + + if(!check_rights(R_DEBUG)) + return + + var/dat = "List of things that failed to GC this round

    " + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + if(I.failures) + dat += "[I] - [I.failures] times
    " + + dat += "List of paths that did not return a qdel hint in Destroy()

    " + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + if(I.no_hint) + dat += "[I]
    " + + dat += "List of paths that slept in Destroy()

    " + for(var/path in SSgarbage.items) + var/datum/qdel_item/I = SSgarbage.items[path] + if(I.slept_destroy) + dat += "[I]
    " + + usr << browse(dat, "window=simpledellog") + +/client/proc/cmd_admin_toggle_block(var/mob/M,var/block) + if(!check_rights(R_SPAWN)) + return + + if(!SSticker) + alert("Wait until the game starts") + return + if(istype(M, /mob/living/carbon)) + M.dna.SetSEState(block,!M.dna.GetSEState(block)) + genemutcheck(M,block,null,MUTCHK_FORCED) + M.update_mutations() + var/state="[M.dna.GetSEState(block)?"on":"off"]" + var/blockname=GLOB.assigned_blocks[block] + message_admins("[key_name_admin(src)] has toggled [M.key]'s [blockname] block [state]!") + log_admin("[key_name(src)] has toggled [M.key]'s [blockname] block [state]!") + else + alert("Invalid mob") + +/client/proc/reload_nanoui_resources() + set category = "Debug" + set name = "Reload NanoUI Resources" + set desc = "Force the client to redownload NanoUI Resources" + + // Close open NanoUIs. + SSnanoui.close_user_uis(usr) + + // Re-load the assets. + var/datum/asset/assets = get_asset_datum(/datum/asset/nanoui) + assets.register() + + // Clear the user's cache so they get resent. + usr.client.cache = list() + +/client/proc/view_runtimes() + set category = "Debug" + set name = "View Runtimes" + set desc = "Open the Runtime Viewer" + + if(!check_rights(R_DEBUG)) + return + + GLOB.error_cache.showTo(usr) + +/client/proc/jump_to_ruin() + set category = "Debug" + set name = "Jump to Ruin" + set desc = "Displays a list of all placed ruins to teleport to." + + if(!check_rights(R_DEBUG)) + return + + var/list/names = list() + for(var/i in GLOB.ruin_landmarks) + var/obj/effect/landmark/ruin/ruin_landmark = i + var/datum/map_template/ruin/template = ruin_landmark.ruin_template + + var/count = 1 + var/name = template.name + var/original_name = name + + while(name in names) + count++ + name = "[original_name] ([count])" + + names[name] = ruin_landmark + + var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in names + + var/obj/effect/landmark/ruin/landmark = names[ruinname] + + if(istype(landmark)) + var/datum/map_template/ruin/template = landmark.ruin_template + admin_forcemove(usr, get_turf(landmark)) + + to_chat(usr, "[template.name]") + to_chat(usr, "[template.description]") + + log_admin("[key_name(usr)] jumped to ruin [ruinname]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to ruin [ruinname]", 1) + + feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_medal_disable() + set category = "Debug" + set name = "Toggle Medal Disable" + set desc = "Toggles the safety lock on trying to contact the medal hub." + + if(!check_rights(R_DEBUG)) + return + + SSmedals.hub_enabled = !SSmedals.hub_enabled + + message_admins("[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") + feedback_add_details("admin_verb","TMH") // If... + log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 23d171e69a0c..92bcfa25c3a9 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -1,178 +1,178 @@ -/client/proc/air_status(turf/target as turf) - set category = "Debug" - set name = "Display Air Status" - - if(!check_rights(R_DEBUG)) - return - - if(!isturf(target)) - return - - var/datum/gas_mixture/GM = target.return_air() - var/burning = 0 - if(istype(target, /turf/simulated)) - var/turf/simulated/T = target - if(T.active_hotspot) - burning = 1 - - to_chat(usr, "@[target.x],[target.y]: O:[GM.oxygen] T:[GM.toxins] N:[GM.nitrogen] C:[GM.carbon_dioxide] w [GM.temperature] Kelvin, [GM.return_pressure()] kPa [(burning)?("BURNING"):(null)]") - for(var/datum/gas/trace_gas in GM.trace_gases) - to_chat(usr, "[trace_gas.type]: [trace_gas.moles]") - - message_admins("[key_name_admin(usr)] has checked the air status of [T]") - log_admin("[key_name(usr)] has checked the air status of [T]") - - feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/fix_next_move() - set category = "Debug" - set name = "Unfreeze Everyone" - - if(!check_rights(R_DEBUG)) - return - - message_admins("[key_name_admin(usr)] has unfrozen everyone") - log_admin("[key_name(usr)] has unfrozen everyone") - - var/largest_move_time = 0 - var/largest_click_time = 0 - var/mob/largest_move_mob = null - var/mob/largest_click_mob = null - for(var/mob/M in world) - if(!M.client) - continue - if(M.next_move >= largest_move_time) - largest_move_mob = M - if(M.next_move > world.time) - largest_move_time = M.next_move - world.time - else - largest_move_time = 1 - if(M.next_click >= largest_click_time) - largest_click_mob = M - if(M.next_click > world.time) - largest_click_time = M.next_click - world.time - else - largest_click_time = 0 - log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") - M.next_move = 1 - M.next_click = 0 - - message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) - message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) - message_admins("world.time = [world.time]", 1) - - feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/radio_report() - set category = "Debug" - set name = "Radio report" - - if(!check_rights(R_DEBUG)) - return - - var/filters = list( - "1" = "RADIO_TO_AIRALARM", - "2" = "RADIO_FROM_AIRALARM", - "3" = "RADIO_CHAT", - "4" = "RADIO_ATMOSIA", - "5" = "RADIO_NAVBEACONS", - "6" = "RADIO_AIRLOCK", - "7" = "RADIO_SECBOT", - "8" = "RADIO_MULEBOT", - "_default" = "NO_FILTER" - ) - var/output = "Radio Report
    " - for(var/fq in SSradio.frequencies) - output += "Freq: [fq]
    " - var/list/datum/radio_frequency/fqs = SSradio.frequencies[fq] - if(!fqs) - output += "  ERROR
    " - continue - for(var/filter in fqs.devices) - var/list/f = fqs.devices[filter] - if(!f) - output += "  [filters[filter]]: ERROR
    " - continue - output += "  [filters[filter]]: [f.len]
    " - for(var/device in f) - if(isobj(device)) - output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
    " - else - output += "    [device]
    " - - usr << browse(output,"window=radioreport") - - message_admins("[key_name_admin(usr)] has generated a radio report") - log_admin("[key_name(usr)] has generated a radio report") - - feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reload_admins() - set name = "Reload Admins" - set category = "Debug" - - if(!check_rights(R_SERVER)) - return - - message_admins("[key_name_admin(usr)] has manually reloaded admins") - log_admin("[key_name(usr)] has manually reloaded admins") - - load_admins() - feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/print_jobban_old() - set name = "Print Jobban Log" - set desc = "This spams all the active jobban entries for the current round to standard output." - set category = "Debug" - - if(!check_rights(R_DEBUG)) - return - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - to_chat(usr, "[t]") - - message_admins("[key_name_admin(usr)] has printed the jobban log") - log_admin("[key_name(usr)] has printed the jobban log") - -/client/proc/print_jobban_old_filter() - set name = "Search Jobban Log" - set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." - set category = "Debug" - - if(!check_rights(R_DEBUG)) - return - - var/filter = clean_input("Contains what?","Filter") - if(!filter) - return - - to_chat(usr, "Jobbans active in this round.") - for(var/t in jobban_keylist) - if(findtext(t, filter)) - to_chat(usr, "[t]") - - message_admins("[key_name_admin(usr)] has searched the jobban log for [filter]") - log_admin("[key_name(usr)] has searched the jobban log for [filter]") - -/client/proc/vv_by_ref() - set name = "VV by Ref" - set desc = "Give this a ref string, and you will see its corresponding VV panel if it exists" - set category = "Debug" - - // It's gated by "Debug Verbs", so might as well gate it to the debug permission - if(!check_rights(R_DEBUG)) - return - - var/refstring = clean_input("Which reference?","Ref") - if(!refstring) - return - - var/datum/D = locate(refstring) - if(!D) - to_chat(usr, "That ref string does not correspond to any datum.") - return - - debug_variables(D) +/client/proc/air_status(turf/target as turf) + set category = "Debug" + set name = "Display Air Status" + + if(!check_rights(R_DEBUG)) + return + + if(!isturf(target)) + return + + var/datum/gas_mixture/GM = target.return_air() + var/burning = 0 + if(istype(target, /turf/simulated)) + var/turf/simulated/T = target + if(T.active_hotspot) + burning = 1 + + to_chat(usr, "@[target.x],[target.y]: O:[GM.oxygen] T:[GM.toxins] N:[GM.nitrogen] C:[GM.carbon_dioxide] w [GM.temperature] Kelvin, [GM.return_pressure()] kPa [(burning)?("BURNING"):(null)]") + for(var/datum/gas/trace_gas in GM.trace_gases) + to_chat(usr, "[trace_gas.type]: [trace_gas.moles]") + + message_admins("[key_name_admin(usr)] has checked the air status of [target]") + log_admin("[key_name(usr)] has checked the air status of [target]") + + feedback_add_details("admin_verb","DAST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/fix_next_move() + set category = "Debug" + set name = "Unfreeze Everyone" + + if(!check_rights(R_DEBUG)) + return + + message_admins("[key_name_admin(usr)] has unfrozen everyone") + log_admin("[key_name(usr)] has unfrozen everyone") + + var/largest_move_time = 0 + var/largest_click_time = 0 + var/mob/largest_move_mob = null + var/mob/largest_click_mob = null + for(var/mob/M in world) + if(!M.client) + continue + if(M.next_move >= largest_move_time) + largest_move_mob = M + if(M.next_move > world.time) + largest_move_time = M.next_move - world.time + else + largest_move_time = 1 + if(M.next_click >= largest_click_time) + largest_click_mob = M + if(M.next_click > world.time) + largest_click_time = M.next_click - world.time + else + largest_click_time = 0 + log_admin("DEBUG: [key_name(M)] next_move = [M.next_move] next_click = [M.next_click] world.time = [world.time]") + M.next_move = 1 + M.next_click = 0 + + message_admins("[key_name_admin(largest_move_mob)] had the largest move delay with [largest_move_time] frames / [largest_move_time/10] seconds!", 1) + message_admins("[key_name_admin(largest_click_mob)] had the largest click delay with [largest_click_time] frames / [largest_click_time/10] seconds!", 1) + message_admins("world.time = [world.time]", 1) + + feedback_add_details("admin_verb","UFE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/radio_report() + set category = "Debug" + set name = "Radio report" + + if(!check_rights(R_DEBUG)) + return + + var/filters = list( + "1" = "RADIO_TO_AIRALARM", + "2" = "RADIO_FROM_AIRALARM", + "3" = "RADIO_CHAT", + "4" = "RADIO_ATMOSIA", + "5" = "RADIO_NAVBEACONS", + "6" = "RADIO_AIRLOCK", + "7" = "RADIO_SECBOT", + "8" = "RADIO_MULEBOT", + "_default" = "NO_FILTER" + ) + var/output = "Radio Report
    " + for(var/fq in SSradio.frequencies) + output += "Freq: [fq]
    " + var/list/datum/radio_frequency/fqs = SSradio.frequencies[fq] + if(!fqs) + output += "  ERROR
    " + continue + for(var/filter in fqs.devices) + var/list/f = fqs.devices[filter] + if(!f) + output += "  [filters[filter]]: ERROR
    " + continue + output += "  [filters[filter]]: [f.len]
    " + for(var/device in f) + if(isobj(device)) + output += "    [device] ([device:x],[device:y],[device:z] in area [get_area(device:loc)])
    " + else + output += "    [device]
    " + + usr << browse(output,"window=radioreport") + + message_admins("[key_name_admin(usr)] has generated a radio report") + log_admin("[key_name(usr)] has generated a radio report") + + feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reload_admins() + set name = "Reload Admins" + set category = "Debug" + + if(!check_rights(R_SERVER)) + return + + message_admins("[key_name_admin(usr)] has manually reloaded admins") + log_admin("[key_name(usr)] has manually reloaded admins") + + load_admins() + feedback_add_details("admin_verb","RLDA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/print_jobban_old() + set name = "Print Jobban Log" + set desc = "This spams all the active jobban entries for the current round to standard output." + set category = "Debug" + + if(!check_rights(R_DEBUG)) + return + + to_chat(usr, "Jobbans active in this round.") + for(var/t in GLOB.jobban_keylist) + to_chat(usr, "[t]") + + message_admins("[key_name_admin(usr)] has printed the jobban log") + log_admin("[key_name(usr)] has printed the jobban log") + +/client/proc/print_jobban_old_filter() + set name = "Search Jobban Log" + set desc = "This searches all the active jobban entries for the current round and outputs the results to standard output." + set category = "Debug" + + if(!check_rights(R_DEBUG)) + return + + var/filter = clean_input("Contains what?","Filter") + if(!filter) + return + + to_chat(usr, "Jobbans active in this round.") + for(var/t in GLOB.jobban_keylist) + if(findtext(t, filter)) + to_chat(usr, "[t]") + + message_admins("[key_name_admin(usr)] has searched the jobban log for [filter]") + log_admin("[key_name(usr)] has searched the jobban log for [filter]") + +/client/proc/vv_by_ref() + set name = "VV by Ref" + set desc = "Give this a ref string, and you will see its corresponding VV panel if it exists" + set category = "Debug" + + // It's gated by "Debug Verbs", so might as well gate it to the debug permission + if(!check_rights(R_DEBUG)) + return + + var/refstring = clean_input("Which reference?","Ref") + if(!refstring) + return + + var/datum/D = locate(refstring) + if(!D) + to_chat(usr, "That ref string does not correspond to any datum.") + return + + debug_variables(D) diff --git a/code/modules/admin/verbs/dice.dm b/code/modules/admin/verbs/dice.dm index 4df6e3107a0f..58bac2e113a2 100644 --- a/code/modules/admin/verbs/dice.dm +++ b/code/modules/admin/verbs/dice.dm @@ -19,4 +19,4 @@ if(alert("Do you want to inform the world about the result?",,"Yes", "No") == "Yes") to_chat(world, "

    Gods rolled [dice], result is [result]

    ") - message_admins("[key_name_admin(src)] rolled dice [dice], result is [result]", 1) \ No newline at end of file + message_admins("[key_name_admin(src)] rolled dice [dice], result is [result]", 1) diff --git a/code/modules/admin/verbs/freeze.dm b/code/modules/admin/verbs/freeze.dm index 9b01e94846c5..f775ba0e9176 100644 --- a/code/modules/admin/verbs/freeze.dm +++ b/code/modules/admin/verbs/freeze.dm @@ -5,7 +5,7 @@ //////Allows admin's to right click on any mob/mech and freeze them in place./// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -var/global/list/frozen_mob_list = list() +GLOBAL_LIST_EMPTY(frozen_mob_list) /client/proc/freeze(var/mob/living/M as mob in GLOB.mob_list) set name = "Freeze" set category = null @@ -16,7 +16,7 @@ var/global/list/frozen_mob_list = list() if(!istype(M)) return - if(M in frozen_mob_list) + if(M in GLOB.frozen_mob_list) M.admin_unFreeze(src) else M.admin_Freeze(src) @@ -41,8 +41,8 @@ var/global/list/frozen_mob_list = list() admin_prev_sleeping = sleeping AdjustSleeping(20000) frozen = AO - if(!(src in frozen_mob_list)) - frozen_mob_list += src + if(!(src in GLOB.frozen_mob_list)) + GLOB.frozen_mob_list += src /mob/living/proc/admin_unFreeze(client/admin, skip_overlays = FALSE) if(istype(admin)) @@ -58,8 +58,8 @@ var/global/list/frozen_mob_list = list() frozen = null SetSleeping(admin_prev_sleeping) admin_prev_sleeping = null - if(src in frozen_mob_list) - frozen_mob_list -= src + if(src in GLOB.frozen_mob_list) + GLOB.frozen_mob_list -= src update_icons() diff --git a/code/modules/admin/verbs/getlogs.dm b/code/modules/admin/verbs/getlogs.dm index 22c6d778733d..73f993c61de4 100644 --- a/code/modules/admin/verbs/getlogs.dm +++ b/code/modules/admin/verbs/getlogs.dm @@ -38,4 +38,4 @@ else return to_chat(src, "Attempting to send [path], this may take a fair few minutes if the file is very large.") - return \ No newline at end of file + return diff --git a/code/modules/admin/verbs/honksquad.dm b/code/modules/admin/verbs/honksquad.dm index 238706c10389..8c8d46ec97b4 100644 --- a/code/modules/admin/verbs/honksquad.dm +++ b/code/modules/admin/verbs/honksquad.dm @@ -1,7 +1,7 @@ //HONKsquad #define HONKSQUAD_POSSIBLE 6 //if more Commandos are needed in the future -var/global/sent_honksquad = 0 +GLOBAL_VAR_INIT(sent_honksquad, 0) /client/proc/honksquad() if(!SSticker) @@ -10,7 +10,7 @@ var/global/sent_honksquad = 0 if(world.time < 6000) to_chat(usr, "There are [(6000-world.time)/10] seconds remaining before it may be called.") return - if(sent_honksquad == 1) + if(GLOB.sent_honksquad == 1) to_chat(usr, "Clown Planet has already dispatched a HONKsquad.") return if(alert("Do you want to send in the HONKsquad? Once enabled, this is irreversible.",,"Yes","No")!="Yes") @@ -24,11 +24,11 @@ var/global/sent_honksquad = 0 if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") return - if(sent_honksquad) + if(GLOB.sent_honksquad) to_chat(usr, "Looks like someone beat you to it. HONK.") return - sent_honksquad = 1 + GLOB.sent_honksquad = 1 var/honksquad_number = HONKSQUAD_POSSIBLE //for selecting a leader diff --git a/code/modules/admin/verbs/infiltratorteam_syndicate.dm b/code/modules/admin/verbs/infiltratorteam_syndicate.dm index 4cea0538520f..459f2b8fb4bb 100644 --- a/code/modules/admin/verbs/infiltratorteam_syndicate.dm +++ b/code/modules/admin/verbs/infiltratorteam_syndicate.dm @@ -1,7 +1,7 @@ // Syndicate Infiltration Team (SIT) // A little like Syndicate Strike Team (SST) but geared towards stealthy team missions rather than murderbone. -var/global/sent_syndicate_infiltration_team = 0 +GLOBAL_VAR_INIT(sent_syndicate_infiltration_team, 0) /client/proc/syndicate_infiltration_team() set category = "Event" @@ -35,7 +35,7 @@ var/global/sent_syndicate_infiltration_team = 0 var/tctext = input(src, "How much TC do you want to give each team member? Suggested: 20-30. They cannot trade TC.") as num var/tcamount = text2num(tctext) tcamount = between(0, tcamount, 1000) - if(sent_syndicate_infiltration_team == 1) + if(GLOB.sent_syndicate_infiltration_team == 1) if(alert("A Syndicate Infiltration Team has already been sent. Sure you want to send another?",,"Yes","No")=="No") return @@ -61,7 +61,7 @@ var/global/sent_syndicate_infiltration_team = 0 to_chat(src, "Nobody volunteered.") return 0 - sent_syndicate_infiltration_team = 1 + GLOB.sent_syndicate_infiltration_team = 1 var/list/sit_spawns = list() var/list/sit_spawns_leader = list() @@ -90,7 +90,7 @@ var/global/sent_syndicate_infiltration_team = 0 to_chat(new_syndicate_infiltrator, "You are a [!syndicate_leader_selected?"Infiltrator":"Lead Infiltrator"] in the service of the Syndicate. \nYour current mission is: [input]") to_chat(new_syndicate_infiltrator, "You are equipped with an uplink implant to help you achieve your objectives. ((activate it via button in top left of screen))") new_syndicate_infiltrator.faction += "syndicate" - data_core.manifest_inject(new_syndicate_infiltrator) + GLOB.data_core.manifest_inject(new_syndicate_infiltrator) if(syndicate_leader_selected) var/obj/effect/landmark/warpto = pick(sit_spawns_leader) new_syndicate_infiltrator.loc = warpto.loc @@ -104,7 +104,7 @@ var/global/sent_syndicate_infiltration_team = 0 new_syndicate_infiltrator.mind.store_memory("Mission: [input] ") new_syndicate_infiltrator.mind.store_memory("Team Leader: [team_leader] ") new_syndicate_infiltrator.mind.store_memory("Starting Equipment:
    - Syndicate Headset ((.h for your radio))
    - Chameleon Jumpsuit ((right click to Change Color))
    - Agent ID card ((disguise as another job))
    - Uplink Implant ((top left of screen))
    - Dust Implant ((destroys your body on death))
    - Combat Gloves ((insulated, disguised as black gloves))
    - Anything bought with your uplink implant") - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] opshud.join_hud(new_syndicate_infiltrator.mind.current) set_antag_hud(new_syndicate_infiltrator.mind.current, "hudoperative") new_syndicate_infiltrator.regenerate_icons() diff --git a/code/modules/admin/verbs/logging_view.dm b/code/modules/admin/verbs/logging_view.dm new file mode 100644 index 000000000000..2a222e21dbd6 --- /dev/null +++ b/code/modules/admin/verbs/logging_view.dm @@ -0,0 +1,10 @@ +GLOBAL_LIST_INIT(open_logging_views, list()) + +/client/proc/cmd_admin_open_logging_view() + set category = "Admin" + set name = "Open Logging View" + set desc = "Opens the detailed logging viewer" + + if(!GLOB.open_logging_views[usr.client.ckey]) + GLOB.open_logging_views[usr.client.ckey] = new /datum/log_viewer() + GLOB.open_logging_views[usr.client.ckey].show_ui(usr) \ No newline at end of file diff --git a/code/modules/admin/verbs/map_template_loadverb.dm b/code/modules/admin/verbs/map_template_loadverb.dm index f447810d8607..edbca5a842f0 100644 --- a/code/modules/admin/verbs/map_template_loadverb.dm +++ b/code/modules/admin/verbs/map_template_loadverb.dm @@ -6,10 +6,10 @@ return var/datum/map_template/template - var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in map_templates + var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in GLOB.map_templates if(!map) return - template = map_templates[map] + template = GLOB.map_templates[map] var/turf/T = get_turf(mob) if(!T) @@ -48,7 +48,7 @@ var/datum/map_template/M = new(map=map, rename="[map]") if(M.preload_size(map)) to_chat(usr, "Map template '[map]' ready to place ([M.width]x[M.height])") - map_templates[M.name] = M + GLOB.map_templates[M.name] = M message_admins("[key_name_admin(usr)] has uploaded a map template ([map]). Took [stop_watch(timer)]s.") else to_chat(usr, "Map template '[map]' failed to load properly") diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 80a171494ead..10ad31ef4102 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -1,185 +1,185 @@ -//- Are all the floors with or without air, as they should be? (regular or airless) -//- Does the area have an APC? -//- Does the area have an Air Alarm? -//- Does the area have a Request Console? -//- Does the area have lights? -//- Does the area have a light switch? -//- Does the area have enough intercoms? -//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) -//- Is the area connected to the scrubbers air loop? -//- Is the area connected to the vent air loop? (vent pumps) -//- Is everything wired properly? -//- Does the area have a fire alarm and firedoors? -//- Do all pod doors work properly? -//- Are accesses set properly on doors, pod buttons, etc. -//- Are all items placed properly? (not below vents, scrubbers, tables) -//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? -//- Check for any misplaced or stacked piece of pipe (air and disposal) -//- Check for any misplaced or stacked piece of wire -//- Identify how hard it is to break into the area and where the weak points are -//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. - -var/camera_range_display_status = 0 -var/intercom_range_display_status = 0 - -/obj/effect/debugging/camera_range - icon = 'icons/480x480.dmi' - icon_state = "25percent" - - New() - src.pixel_x = -224 - src.pixel_y = -224 - -/obj/effect/debugging/mapfix_marker - name = "map fix marker" - icon = 'icons/mob/screen_gen.dmi' - icon_state = "mapfixmarker" - desc = "I am a mappers mistake." - -/obj/effect/debugging/marker - icon = 'icons/turf/areas.dmi' - icon_state = "yellow" - -/obj/effect/debugging/marker/Move() - return 0 - -/client/proc/camera_view() - set category = "Mapping" - set name = "Camera Range Display" - - if(!check_rights(R_DEBUG)) - return - - if(camera_range_display_status) - camera_range_display_status = 0 - else - camera_range_display_status = 1 - - for(var/obj/effect/debugging/camera_range/C in world) - qdel(C) - - if(camera_range_display_status) - for(var/obj/machinery/camera/C in cameranet.cameras) - new/obj/effect/debugging/camera_range(C.loc) - feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/sec_camera_report() - set category = "Mapping" - set name = "Camera Report" - - if(!check_rights(R_DEBUG)) - return - - var/list/obj/machinery/camera/CL = list() - - for(var/obj/machinery/camera/C in cameranet.cameras) - CL += C - - var/output = {"CAMERA ANOMALIES REPORT
    -The following anomalies have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
      "} - - for(var/obj/machinery/camera/C1 in CL) - for(var/obj/machinery/camera/C2 in CL) - if(C1 != C2) - if(C1.c_tag == C2.c_tag) - output += "
    • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
    • " - if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) - output += "
    • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " - if(C1.loc == C2.loc) - output += "
    • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " - var/turf/T = get_step(C1,turn(C1.dir,180)) - if(!T || !isturf(T) || !T.density ) - if(!(locate(/obj/structure/grille,T))) - var/window_check = 0 - for(var/obj/structure/window/W in T) - if(W.dir == turn(C1.dir,180) || W.fulltile) - window_check = 1 - break - if(!window_check) - output += "
    • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
    • " - - output += "
    " - usr << browse(output,"window=airreport;size=1000x500") - feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/intercom_view() - set category = "Mapping" - set name = "Intercom Range Display" - - if(!check_rights(R_DEBUG)) - return - - if(intercom_range_display_status) - intercom_range_display_status = 0 - else - intercom_range_display_status = 1 - - for(var/obj/effect/debugging/marker/M in world) - qdel(M) - - if(intercom_range_display_status) - for(var/obj/item/radio/intercom/I in world) - for(var/turf/T in orange(7,I)) - var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) - if(!(F in view(7,I.loc))) - qdel(F) - feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_objects_on_z_level() - set category = "Mapping" - set name = "Count Objects On Level" - - if(!check_rights(R_DEBUG)) - return - - var/level = clean_input("Which z-level?","Level?") - if(!level) return - var/num_level = text2num(level) - if(!num_level) return - if(!isnum(num_level)) return - - var/type_text = clean_input("Which type path?","Path?") - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - var/list/atom/atom_list = list() - - for(var/atom/A in world) - if(istype(A,type_path)) - var/atom/B = A - while(!(isturf(B.loc))) - if(B && B.loc) - B = B.loc - else - break - if(B) - if(B.z == num_level) - count++ - atom_list += A - - to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level].") - feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/count_objects_all() - set category = "Mapping" - set name = "Count Objects All" - - if(!check_rights(R_DEBUG)) - return - - var/type_text = clean_input("Which type path?","") - if(!type_text) return - var/type_path = text2path(type_text) - if(!type_path) return - - var/count = 0 - - for(var/atom/A in world) - if(istype(A,type_path)) - count++ - - to_chat(world, "There are [count] objects of type [type_path] in the game world.") - feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +//- Are all the floors with or without air, as they should be? (regular or airless) +//- Does the area have an APC? +//- Does the area have an Air Alarm? +//- Does the area have a Request Console? +//- Does the area have lights? +//- Does the area have a light switch? +//- Does the area have enough intercoms? +//- Does the area have enough security cameras? (Use the 'Camera Range Display' verb under Debug) +//- Is the area connected to the scrubbers air loop? +//- Is the area connected to the vent air loop? (vent pumps) +//- Is everything wired properly? +//- Does the area have a fire alarm and firedoors? +//- Do all pod doors work properly? +//- Are accesses set properly on doors, pod buttons, etc. +//- Are all items placed properly? (not below vents, scrubbers, tables) +//- Does the disposal system work properly from all the disposal units in this room and all the units, the pipes of which pass through this room? +//- Check for any misplaced or stacked piece of pipe (air and disposal) +//- Check for any misplaced or stacked piece of wire +//- Identify how hard it is to break into the area and where the weak points are +//- Check if the area has too much empty space. If so, make it smaller and replace the rest with maintenance tunnels. + +GLOBAL_VAR_INIT(camera_range_display_status, 0) +GLOBAL_VAR_INIT(intercom_range_display_status, 0) + +/obj/effect/debugging/camera_range + icon = 'icons/480x480.dmi' + icon_state = "25percent" + + New() + src.pixel_x = -224 + src.pixel_y = -224 + +/obj/effect/debugging/mapfix_marker + name = "map fix marker" + icon = 'icons/mob/screen_gen.dmi' + icon_state = "mapfixmarker" + desc = "I am a mappers mistake." + +/obj/effect/debugging/marker + icon = 'icons/turf/areas.dmi' + icon_state = "yellow" + +/obj/effect/debugging/marker/Move() + return 0 + +/client/proc/camera_view() + set category = "Mapping" + set name = "Camera Range Display" + + if(!check_rights(R_DEBUG)) + return + + if(GLOB.camera_range_display_status) + GLOB.camera_range_display_status = 0 + else + GLOB.camera_range_display_status = 1 + + for(var/obj/effect/debugging/camera_range/C in world) + qdel(C) + + if(GLOB.camera_range_display_status) + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + new/obj/effect/debugging/camera_range(C.loc) + feedback_add_details("admin_verb","mCRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/sec_camera_report() + set category = "Mapping" + set name = "Camera Report" + + if(!check_rights(R_DEBUG)) + return + + var/list/obj/machinery/camera/CL = list() + + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + CL += C + + var/output = {"CAMERA ANOMALIES REPORT
    +The following anomalies have been detected. The ones in red need immediate attention: Some of those in black may be intentional.
      "} + + for(var/obj/machinery/camera/C1 in CL) + for(var/obj/machinery/camera/C2 in CL) + if(C1 != C2) + if(C1.c_tag == C2.c_tag) + output += "
    • c_tag match for sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) and \[[C2.x], [C2.y], [C2.z]\] ([C2.loc.loc]) - c_tag is [C1.c_tag]
    • " + if(C1.loc == C2.loc && C1.dir == C2.dir && C1.pixel_x == C2.pixel_x && C1.pixel_y == C2.pixel_y) + output += "
    • FULLY overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " + if(C1.loc == C2.loc) + output += "
    • overlapping sec. cameras at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Networks: [C1.network] and [C2.network]
    • " + var/turf/T = get_step(C1,turn(C1.dir,180)) + if(!T || !isturf(T) || !T.density ) + if(!(locate(/obj/structure/grille,T))) + var/window_check = 0 + for(var/obj/structure/window/W in T) + if(W.dir == turn(C1.dir,180) || W.fulltile) + window_check = 1 + break + if(!window_check) + output += "
    • Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]
    • " + + output += "
    " + usr << browse(output,"window=airreport;size=1000x500") + feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/intercom_view() + set category = "Mapping" + set name = "Intercom Range Display" + + if(!check_rights(R_DEBUG)) + return + + if(GLOB.intercom_range_display_status) + GLOB.intercom_range_display_status = 0 + else + GLOB.intercom_range_display_status = 1 + + for(var/obj/effect/debugging/marker/M in world) + qdel(M) + + if(GLOB.intercom_range_display_status) + for(var/obj/item/radio/intercom/I in world) + for(var/turf/T in orange(7,I)) + var/obj/effect/debugging/marker/F = new/obj/effect/debugging/marker(T) + if(!(F in view(7,I.loc))) + qdel(F) + feedback_add_details("admin_verb","mIRD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_objects_on_z_level() + set category = "Mapping" + set name = "Count Objects On Level" + + if(!check_rights(R_DEBUG)) + return + + var/level = clean_input("Which z-level?","Level?") + if(!level) return + var/num_level = text2num(level) + if(!num_level) return + if(!isnum(num_level)) return + + var/type_text = clean_input("Which type path?","Path?") + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + var/list/atom/atom_list = list() + + for(var/atom/A in world) + if(istype(A,type_path)) + var/atom/B = A + while(!(isturf(B.loc))) + if(B && B.loc) + B = B.loc + else + break + if(B) + if(B.z == num_level) + count++ + atom_list += A + + to_chat(world, "There are [count] objects of type [type_path] on z-level [num_level].") + feedback_add_details("admin_verb","mOBJZ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/count_objects_all() + set category = "Mapping" + set name = "Count Objects All" + + if(!check_rights(R_DEBUG)) + return + + var/type_text = clean_input("Which type path?","") + if(!type_text) return + var/type_path = text2path(type_text) + if(!type_path) return + + var/count = 0 + + for(var/atom/A in world) + if(istype(A,type_path)) + count++ + + to_chat(world, "There are [count] objects of type [type_path] in the game world.") + feedback_add_details("admin_verb","mOBJ") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/verbs/massmodvar.dm index bafa7d629d3d..50629d5a1cc5 100644 --- a/code/modules/admin/verbs/massmodvar.dm +++ b/code/modules/admin/verbs/massmodvar.dm @@ -1,272 +1,272 @@ -/client/proc/cmd_mass_modify_object_variables(atom/A, var/var_name) - set category = "Debug" - set name = "Mass Edit Variables" - set desc="(target) Edit all instances of a target item's variables" - - var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL itms) - - if(!check_rights(R_VAREDIT)) return - - if(A && A.type) - if(typesof(A.type)) - switch(input("Strict object type detection?") as null|anything in list("Strictly this type","This type and subtypes", "Cancel")) - if("Strictly this type") - method = 0 - if("This type and subtypes") - method = 1 - if("Cancel") - return - if(null) - return - - src.massmodify_variables(A, var_name, method) - feedback_add_details("admin_verb","MEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/massmodify_variables(datum/O, var_name = "", method = 0) - if(!check_rights(R_VAREDIT)) - return - if(!istype(O)) - return - - var/variable = "" - if(!var_name) - var/list/names = list() - for(var/V in O.vars) - names += V - - names = sortList(names) - - variable = input("Which var?", "Var") as null|anything in names - else - variable = var_name - - if(!variable || !O.can_vv_get(variable)) - return - var/default - var/var_value = O.vars[variable] - - if(variable in VVckey_edit) - to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.") - return - if(variable in VVlocked) - if(!check_rights(R_DEBUG)) - return - if(variable in VVicon_edit_lock) - if(!check_rights(R_EVENT | R_DEBUG)) - return - if(variable in VVpixelmovement) - if(!check_rights(R_DEBUG)) - return - var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") - if(prompt != "Continue") - return - - default = vv_get_class(var_value) - - if(isnull(default)) - to_chat(src, "Unable to determine variable type.") - else - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [var_value]") - - if(default == VV_NUM) - var/dir_text = "" - if(dir < 0 && dir < 16) - if(dir & 1) - dir_text += "NORTH" - if(dir & 2) - dir_text += "SOUTH" - if(dir & 4) - dir_text += "EAST" - if(dir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - var/value = vv_get_value(default_class = default) - var/new_value = value["value"] - var/class = value["class"] - - if(!class || !new_value == null && class != VV_NULL) - return - - if(class == VV_MESSAGE) - class = VV_TEXT - - if(value["type"]) - class = VV_NEW_TYPE - - var/original_name = "[O]" - - var/rejected = 0 - var/accepted = 0 - - switch(class) - if(VV_RESTORE_DEFAULT) - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE) - accepted++ - else - rejected++ - CHECK_TICK - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, new_value) - var/pre_processing = new_value - var/unique - if(varsvars && varsvars.len) - unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same") - if(unique == "Unique") - unique = TRUE - else - unique = FALSE - for(var/V in varsvars) - new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") - - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(unique) - new_value = pre_processing - for(var/V in varsvars) - new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]") - - if(D.vv_edit_var(variable, new_value) != FALSE) - accepted++ - else - rejected++ - CHECK_TICK - - if(VV_NEW_TYPE) - var/many = alert(src, "Create only one [value["type"]] and assign each or a new one for each thing", "How Many", "One", "Many", "Cancel") - if(many == "Cancel") - return - if(many == "Many") - many = TRUE - else - many = FALSE - - var/type = value["type"] - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(many && !new_value) - new_value = new type() - - if(D.vv_edit_var(variable, new_value) != FALSE) - accepted++ - else - rejected++ - new_value = null - CHECK_TICK - - else - to_chat(src, "Finding items...") - var/list/items = get_all_of_type(O.type, method) - to_chat(src, "Changing [items.len] items...") - for(var/thing in items) - if(!thing) - continue - var/datum/D = thing - if(D.vv_edit_var(variable, new_value) != FALSE) - accepted++ - else - rejected++ - CHECK_TICK - - - var/count = rejected+accepted - if(!count) - to_chat(src, "No objects found") - return - if(!accepted) - to_chat(src, "Every object rejected your edit") - return - if(rejected) - to_chat(src, "[rejected] out of [count] objects rejected your edit") - - log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") - log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") - -/proc/get_all_of_type(var/T, subtypes = TRUE) - var/list/typecache = list() - typecache[T] = 1 - if(subtypes) - typecache = typecacheof(typecache) - . = list() - if(ispath(T, /mob)) - for(var/mob/thing in GLOB.mob_list) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /obj/machinery/door)) - for(var/obj/machinery/door/thing in GLOB.airlocks) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /obj/machinery)) - for(var/obj/machinery/thing in GLOB.machines) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /obj)) - for(var/obj/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /atom/movable)) - for(var/atom/movable/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /turf)) - for(var/turf/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /atom)) - for(var/atom/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /client)) - for(var/client/thing in GLOB.clients) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else if(ispath(T, /datum)) - for(var/datum/thing) - if(typecache[thing.type]) - . += thing - CHECK_TICK - - else - for(var/datum/thing in world) - if(typecache[thing.type]) - . += thing - CHECK_TICK \ No newline at end of file +/client/proc/cmd_mass_modify_object_variables(atom/A, var/var_name) + set category = "Debug" + set name = "Mass Edit Variables" + set desc="(target) Edit all instances of a target item's variables" + + var/method = 0 //0 means strict type detection while 1 means this type and all subtypes (IE: /obj/item with this set to 1 will set it to ALL itms) + + if(!check_rights(R_VAREDIT)) return + + if(A && A.type) + if(typesof(A.type)) + switch(input("Strict object type detection?") as null|anything in list("Strictly this type","This type and subtypes", "Cancel")) + if("Strictly this type") + method = 0 + if("This type and subtypes") + method = 1 + if("Cancel") + return + if(null) + return + + src.massmodify_variables(A, var_name, method) + feedback_add_details("admin_verb","MEV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/massmodify_variables(datum/O, var_name = "", method = 0) + if(!check_rights(R_VAREDIT)) + return + if(!istype(O)) + return + + var/variable = "" + if(!var_name) + var/list/names = list() + for(var/V in O.vars) + names += V + + names = sortList(names) + + variable = input("Which var?", "Var") as null|anything in names + else + variable = var_name + + if(!variable || !O.can_vv_get(variable)) + return + var/default + var/var_value = O.vars[variable] + + if(variable in GLOB.VVckey_edit) + to_chat(src, "It's forbidden to mass-modify ckeys. It'll crash everyone's client you dummy.") + return + if(variable in GLOB.VVlocked) + if(!check_rights(R_DEBUG)) + return + if(variable in GLOB.VVicon_edit_lock) + if(!check_rights(R_EVENT | R_DEBUG)) + return + if(variable in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return + var/prompt = alert(src, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if(prompt != "Continue") + return + + default = vv_get_class(var_value) + + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") + else + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [var_value]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + var/value = vv_get_value(default_class = default) + var/new_value = value["value"] + var/class = value["class"] + + if(!class || !new_value == null && class != VV_NULL) + return + + if(class == VV_MESSAGE) + class = VV_TEXT + + if(value["type"]) + class = VV_NEW_TYPE + + var/original_name = "[O]" + + var/rejected = 0 + var/accepted = 0 + + switch(class) + if(VV_RESTORE_DEFAULT) + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(D.vv_edit_var(variable, initial(D.vars[variable])) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, new_value) + var/pre_processing = new_value + var/unique + if(varsvars && varsvars.len) + unique = alert(usr, "Process vars unique to each instance, or same for all?", "Variable Association", "Unique", "Same") + if(unique == "Unique") + unique = TRUE + else + unique = FALSE + for(var/V in varsvars) + new_value = replacetext(new_value,"\[[V]]","[O.vars[V]]") + + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(unique) + new_value = pre_processing + for(var/V in varsvars) + new_value = replacetext(new_value,"\[[V]]","[D.vars[V]]") + + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + + if(VV_NEW_TYPE) + var/many = alert(src, "Create only one [value["type"]] and assign each or a new one for each thing", "How Many", "One", "Many", "Cancel") + if(many == "Cancel") + return + if(many == "Many") + many = TRUE + else + many = FALSE + + var/type = value["type"] + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(many && !new_value) + new_value = new type() + + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + new_value = null + CHECK_TICK + + else + to_chat(src, "Finding items...") + var/list/items = get_all_of_type(O.type, method) + to_chat(src, "Changing [items.len] items...") + for(var/thing in items) + if(!thing) + continue + var/datum/D = thing + if(D.vv_edit_var(variable, new_value) != FALSE) + accepted++ + else + rejected++ + CHECK_TICK + + + var/count = rejected+accepted + if(!count) + to_chat(src, "No objects found") + return + if(!accepted) + to_chat(src, "Every object rejected your edit") + return + if(rejected) + to_chat(src, "[rejected] out of [count] objects rejected your edit") + + log_world("### MassVarEdit by [src]: [O.type] (A/R [accepted]/[rejected]) [variable]=[html_encode("[O.vars[variable]]")]([list2params(value)])") + log_admin("[key_name(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") + message_admins("[key_name_admin(src)] mass modified [original_name]'s [variable] to [O.vars[variable]] ([accepted] objects modified)") + +/proc/get_all_of_type(var/T, subtypes = TRUE) + var/list/typecache = list() + typecache[T] = 1 + if(subtypes) + typecache = typecacheof(typecache) + . = list() + if(ispath(T, /mob)) + for(var/mob/thing in GLOB.mob_list) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /obj/machinery/door)) + for(var/obj/machinery/door/thing in GLOB.airlocks) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /obj/machinery)) + for(var/obj/machinery/thing in GLOB.machines) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /obj)) + for(var/obj/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /atom/movable)) + for(var/atom/movable/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /turf)) + for(var/turf/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /atom)) + for(var/atom/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /client)) + for(var/client/thing in GLOB.clients) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else if(ispath(T, /datum)) + for(var/datum/thing) + if(typecache[thing.type]) + . += thing + CHECK_TICK + + else + for(var/datum/thing in world) + if(typecache[thing.type]) + . += thing + CHECK_TICK diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index c5348fbeabe1..6a1efba4c935 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -1,633 +1,633 @@ -var/list/VVlocked = list("vars", "var_edited", "client", "firemut", "ishulk", "telekinesis", "xray", "ka", "virus", "viruses", "cuffed", "last_eaten", "unlock_content") // R_DEBUG -var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays", "resize") // R_EVENT | R_DEBUG -var/list/VVckey_edit = list("key", "ckey") // R_EVENT | R_DEBUG -var/list/VVpixelmovement = list("step_x", "step_y", "step_size", "bound_height", "bound_width", "bound_x", "bound_y") // R_DEBUG + warning -/client/proc/vv_get_class(var/var_value) - if(isnull(var_value)) - . = VV_NULL - - else if(isnum(var_value)) - . = VV_NUM - - else if(istext(var_value)) - if(findtext(var_value, "\n")) - . = VV_MESSAGE - else - . = VV_TEXT - - else if(isicon(var_value)) - . = VV_ICON - - else if(ismob(var_value)) - . = VV_MOB_REFERENCE - - else if(isloc(var_value)) - . = VV_ATOM_REFERENCE - - else if(istype(var_value, /matrix)) - . = VV_MATRIX - - else if(istype(var_value,/client)) - . = VV_CLIENT - - else if(istype(var_value, /datum)) - . = VV_DATUM_REFERENCE - - else if(ispath(var_value)) - if(ispath(var_value, /atom)) - . = VV_ATOM_TYPE - else if(ispath(var_value, /datum)) - . = VV_DATUM_TYPE - else - . = VV_TYPE - - else if(islist(var_value)) - . = VV_LIST - - else if(isfile(var_value)) - . = VV_FILE - else if(istype(var_value, /regex)) - . = VV_REGEX - else - . = VV_NULL - -/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes) - . = list("class" = class, "value" = null) - if(!class) - if(!classes) - classes = list( - VV_NUM, - VV_TEXT, - VV_MESSAGE, - VV_ICON, - VV_ATOM_REFERENCE, - VV_DATUM_REFERENCE, - VV_MOB_REFERENCE, - VV_CLIENT, - VV_ATOM_TYPE, - VV_DATUM_TYPE, - VV_TYPE, - VV_MATRIX, - VV_REGEX, - VV_FILE, - VV_NEW_ATOM, - VV_NEW_DATUM, - VV_NEW_TYPE, - VV_NEW_LIST, - VV_NULL, - VV_RESTORE_DEFAULT - ) - - if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) - classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" - if(restricted_classes) - classes -= restricted_classes - - if(extra_classes) - classes += extra_classes - - .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes - if(holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") - .["class"] = VV_MARKED_DATUM - - - switch(.["class"]) - if(VV_TEXT) - .["value"] = input("Enter new text:", "Text", current_value) as null|text - if(.["value"] == null) - .["class"] = null - return - if(VV_MESSAGE) - .["value"] = input("Enter new text:", "Text", current_value) as null|message - if(.["value"] == null) - .["class"] = null - return - - - if(VV_NUM) - .["value"] = input("Enter new number:", "Num", current_value) as null|num - if(.["value"] == null) - .["class"] = null - return - - - if(VV_ATOM_TYPE) - .["value"] = pick_closest_path(FALSE) - if(.["value"] == null) - .["class"] = null - return - - if(VV_DATUM_TYPE) - .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if(.["value"] == null) - .["class"] = null - return - - if(VV_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if(!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if(!type) - .["class"] = null - return - .["value"] = type - - if(VV_MATRIX) - .["value"] = text2matrix(input("Enter a, b, c, d, e, and f, seperated by a space.", "Matrix", "1 0 0 0 1 0") as null|text) - if(.["value"] == null) - .["class"] = null - return - - if(VV_REGEX) - var/reg = input("Enter regex", "Regex", "") as null|text - if(!reg) - return - .["value"] = regex(reg) - if(.["value"] == null) - .["class"] = null - - - if(VV_ATOM_REFERENCE) - var/type = pick_closest_path(FALSE) - var/subtypes = vv_subtype_prompt(type) - if(subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if(!value) - .["class"] = null - return - .["value"] = things[value] - - if(VV_DATUM_REFERENCE) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - var/subtypes = vv_subtype_prompt(type) - if(subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if(!value) - .["class"] = null - return - .["value"] = things[value] - - if(VV_MOB_REFERENCE) - var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) - var/subtypes = vv_subtype_prompt(type) - if(subtypes == null) - .["class"] = null - return - var/list/things = vv_reference_list(type, subtypes) - var/value = input("Select reference:", "Reference", current_value) as null|anything in things - if(!value) - .["class"] = null - return - .["value"] = things[value] - - - - if(VV_CLIENT) - .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients - if(.["value"] == null) - .["class"] = null - return - - - if(VV_FILE) - .["value"] = input("Pick file:", "File") as null|file - if(.["value"] == null) - .["class"] = null - return - - - if(VV_ICON) - .["value"] = input("Pick icon:", "Icon") as null|icon - if(.["value"] == null) - .["class"] = null - return - - - if(VV_MARKED_DATUM) - .["value"] = holder.marked_datum - if(.["value"] == null) - .["class"] = null - return - - if(VV_NEW_ATOM) - var/type = pick_closest_path(FALSE) - if(!type) - .["class"] = null - return - .["type"] = type - .["value"] = new type() - - if(VV_NEW_DATUM) - var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) - if(!type) - .["class"] = null - return - .["type"] = type - .["value"] = new type() - - if(VV_NEW_TYPE) - var/type = current_value - var/error = "" - do - type = input("Enter type:[error]", "Type", type) as null|text - if(!type) - break - type = text2path(type) - error = "\nType not found, Please try again" - while(!type) - if(!type) - .["class"] = null - return - .["type"] = type - .["value"] = new type() - - - if(VV_NEW_LIST) - .["value"] = list() - .["type"] = /list - -/client/proc/vv_parse_text(O, new_var) - if(O && findtext(new_var, "\[")) - var/process_vars = alert(usr, "\[] detected in string, process as variables?", "Process Variables?", "Yes", "No") - if(process_vars == "Yes") - . = string2listofvars(new_var, O) - -//do they want you to include subtypes? -//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) -//TRUE = Yes subtypes -//NULL = User cancelled at the prompt or invalid type given -/client/proc/vv_subtype_prompt(var/type) - if(!ispath(type)) - return - var/list/subtypes = subtypesof(type) - if(!subtypes || !subtypes.len) - return FALSE - if(subtypes && subtypes.len) - switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel")) - if("Strictly this type") - return FALSE - if("This type and subtypes") - return TRUE - else - return - -/client/proc/vv_reference_list(type, subtypes) - . = list() - var/list/types = list(type) - if(subtypes) - types = typesof(type) - - var/list/fancytypes = make_types_fancy(types) - - for(var/fancytype in fancytypes) //swap the assoication - types[fancytypes[fancytype]] = fancytype - - var/things = get_all_of_type(type, subtypes) - - var/i = 0 - for(var/thing in things) - var/datum/D = thing - i++ - //try one of 3 methods to shorten the type text: - // fancy type, - // fancy type with the base type removed from the begaining, - // the type with the base type removed from the begaining - var/fancytype = types[D.type] - if(findtext(fancytype, types[type])) - fancytype = copytext(fancytype, length(types[type])+1) - var/shorttype = copytext("[D.type]", length("[type]")+1) - if(length(shorttype) > length(fancytype)) - shorttype = fancytype - if(!length(shorttype)) - shorttype = "/" - - .["[D]([shorttype])\ref[D]#[i]"] = D - -/client/proc/mod_list_add_ass(atom/O) //haha - var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - var/class = L["class"] - if(!class) - return - var/var_value = L["value"] - - if(class == VV_TEXT || class == VV_MESSAGE) - var/list/varsvars = vv_parse_text(O, var_value) - for(var/V in varsvars) - var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") - - return var_value - -/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar) - var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) - var/class = LL["class"] - if(!class) - return - var/var_value = LL["value"] - - if(class == VV_TEXT || class == VV_MESSAGE) - var/list/varsvars = vv_parse_text(O, var_value) - for(var/V in varsvars) - var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") - - if(O) - L = L.Copy() - - L += var_value - - switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) - if("Yes") - L[var_value] = mod_list_add_ass(O) //hehe - if(O) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") - message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") - -/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE) - if(!check_rights(R_VAREDIT)) - return - if(!istype(L, /list)) - to_chat(src, "Not a List.") - return - - if(L.len > 1000) - var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") - if(confirm != "Continue") - return - - - - var/list/names = list() - for(var/i in 1 to L.len) - var/key = L[i] - var/value - if(IS_NORMAL_LIST(L) && !isnum(key)) - value = L[key] - if(value == null) - value = "null" - names["#[i] [key] = [value]"] = i - if(!index) - var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)" - - if(variable == null) - return - - if(variable == "(ADD VAR)") - mod_list_add(L, O, original_name, objectvar) - return - - if(variable == "(CLEAR NULLS)") - L = L.Copy() - listclearnulls(L) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS") - return - - if(variable == "(CLEAR DUPES)") - L = uniqueList(L) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES") - return - - if(variable == "(SHUFFLE)") - L = shuffle(L) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") - message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE") - return - - index = names[variable] - - - var/assoc_key - if(index == null) - return - var/assoc = 0 - var/prompt = alert(src, "Do you want to edit the key or it's assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") - if(prompt == "Cancel") - return - if(prompt == "Assigned Value") - assoc = 1 - assoc_key = L[index] - var/default - var/variable - if(assoc) - variable = L[assoc_key] - else - variable = L[index] - - default = vv_get_class(variable) - - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [L[index]]") - - if(default == VV_NUM) - var/dir_text = "" - if(dir < 0 && dir < 16) - if(dir & 1) - dir_text += "NORTH" - if(dir & 2) - dir_text += "SOUTH" - if(dir & 4) - dir_text += "EAST" - if(dir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - var/original_var - if(assoc) - original_var = L[assoc_key] - else - original_var = L[index] - if(O) - L = L.Copy() - var/class - if(autodetect_class) - if(default == VV_TEXT) - default = VV_MESSAGE - class = default - var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST")) - class = LL["class"] - if(!class) - return - var/new_var = LL["value"] - - if(class == VV_MESSAGE) - class = VV_TEXT - - switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet. - if(VV_LIST) - mod_list(variable, O, original_name, objectvar) - - if("DELETE FROM LIST") - L.Cut(index, index+1) - if(O) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") - message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") - return - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, new_var) - for(var/V in varsvars) - new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") - - - if(assoc) - L[assoc_key] = new_var - else - L[index] = new_var - if(O) - if(!O.vv_edit_var(objectvar, L)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") - log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") - message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]") - -/proc/vv_varname_lockcheck(param_var_name) - if(param_var_name in VVlocked) - if(!check_rights(R_DEBUG)) - return FALSE - if(param_var_name in VVckey_edit) - if(!check_rights(R_EVENT | R_DEBUG)) - return FALSE - if(param_var_name in VVicon_edit_lock) - if(!check_rights(R_EVENT | R_DEBUG)) - return FALSE - if(param_var_name in VVpixelmovement) - if(!check_rights(R_DEBUG)) - return FALSE - var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") - if(prompt != "Continue") - return FALSE - return TRUE - -/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) - if(!check_rights(R_VAREDIT)) - return - - var/class - var/variable - var/var_value - - if(param_var_name) - if(!param_var_name in O.vars) - to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") - return - variable = param_var_name - - else - var/list/names = list() - for(var/V in O.vars) - names += V - - names = sortList(names) - - variable = input("Which var?","Var") as null|anything in names - if(!variable) - return - - if(!O.can_vv_get(variable)) - return - - if(!vv_varname_lockcheck(variable)) - return - - var_value = O.vars[variable] - - var/default = vv_get_class(var_value) - - if(isnull(default)) - to_chat(src, "Unable to determine variable type.") - else - to_chat(src, "Variable appears to be [uppertext(default)].") - - to_chat(src, "Variable contains: [var_value]") - - if(default == VV_NUM) - var/dir_text = "" - if(dir < 0 && dir < 16) - if(dir & 1) - dir_text += "NORTH" - if(dir & 2) - dir_text += "SOUTH" - if(dir & 4) - dir_text += "EAST" - if(dir & 8) - dir_text += "WEST" - - if(dir_text) - to_chat(src, "If a direction, direction is: [dir_text]") - - if(autodetect_class && default != VV_NULL) - if(default == VV_TEXT) - default = VV_MESSAGE - class = default - - var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST)) - class = value["class"] - - if(!class) - return - var/var_new = value["value"] - - if(class == VV_MESSAGE) - class = VV_TEXT - - var/original_name = "[O]" - - switch(class) - if(VV_LIST) - if(!islist(var_value)) - mod_list(list(), O, original_name, variable) - - mod_list(var_value, O, original_name, variable) - return - - if(VV_RESTORE_DEFAULT) - var_new = initial(O.vars[variable]) - - if(VV_TEXT) - var/list/varsvars = vv_parse_text(O, var_new) - for(var/V in varsvars) - var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") - - if(!O.vv_edit_var(variable, var_new)) - to_chat(src, "Your edit was rejected by the object.") - return - log_world("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") - log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_new]") - var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [var_new]" - message_admins(msg) +GLOBAL_LIST_INIT(VVlocked, list("vars", "var_edited", "client", "firemut", "ishulk", "telekinesis", "xray", "ka", "virus", "viruses", "cuffed", "last_eaten", "unlock_content")) // R_DEBUG +GLOBAL_LIST_INIT(VVicon_edit_lock, list("icon", "icon_state", "overlays", "underlays", "resize")) // R_EVENT | R_DEBUG +GLOBAL_LIST_INIT(VVckey_edit, list("key", "ckey")) // R_EVENT | R_DEBUG +GLOBAL_LIST_INIT(VVpixelmovement, list("step_x", "step_y", "step_size", "bound_height", "bound_width", "bound_x", "bound_y")) // R_DEBUG + warning +/client/proc/vv_get_class(var/var_value) + if(isnull(var_value)) + . = VV_NULL + + else if(isnum(var_value)) + . = VV_NUM + + else if(istext(var_value)) + if(findtext(var_value, "\n")) + . = VV_MESSAGE + else + . = VV_TEXT + + else if(isicon(var_value)) + . = VV_ICON + + else if(ismob(var_value)) + . = VV_MOB_REFERENCE + + else if(isloc(var_value)) + . = VV_ATOM_REFERENCE + + else if(istype(var_value, /matrix)) + . = VV_MATRIX + + else if(istype(var_value,/client)) + . = VV_CLIENT + + else if(istype(var_value, /datum)) + . = VV_DATUM_REFERENCE + + else if(ispath(var_value)) + if(ispath(var_value, /atom)) + . = VV_ATOM_TYPE + else if(ispath(var_value, /datum)) + . = VV_DATUM_TYPE + else + . = VV_TYPE + + else if(islist(var_value)) + . = VV_LIST + + else if(isfile(var_value)) + . = VV_FILE + else if(istype(var_value, /regex)) + . = VV_REGEX + else + . = VV_NULL + +/client/proc/vv_get_value(class, default_class, current_value, list/restricted_classes, list/extra_classes, list/classes) + . = list("class" = class, "value" = null) + if(!class) + if(!classes) + classes = list( + VV_NUM, + VV_TEXT, + VV_MESSAGE, + VV_ICON, + VV_ATOM_REFERENCE, + VV_DATUM_REFERENCE, + VV_MOB_REFERENCE, + VV_CLIENT, + VV_ATOM_TYPE, + VV_DATUM_TYPE, + VV_TYPE, + VV_MATRIX, + VV_REGEX, + VV_FILE, + VV_NEW_ATOM, + VV_NEW_DATUM, + VV_NEW_TYPE, + VV_NEW_LIST, + VV_NULL, + VV_RESTORE_DEFAULT + ) + + if(holder && holder.marked_datum && !(VV_MARKED_DATUM in restricted_classes)) + classes += "[VV_MARKED_DATUM] ([holder.marked_datum.type])" + if(restricted_classes) + classes -= restricted_classes + + if(extra_classes) + classes += extra_classes + + .["class"] = input(src, "What kind of data?", "Variable Type", default_class) as null|anything in classes + if(holder && holder.marked_datum && .["class"] == "[VV_MARKED_DATUM] ([holder.marked_datum.type])") + .["class"] = VV_MARKED_DATUM + + + switch(.["class"]) + if(VV_TEXT) + .["value"] = input("Enter new text:", "Text", current_value) as null|text + if(.["value"] == null) + .["class"] = null + return + if(VV_MESSAGE) + .["value"] = input("Enter new text:", "Text", current_value) as null|message + if(.["value"] == null) + .["class"] = null + return + + + if(VV_NUM) + .["value"] = input("Enter new number:", "Num", current_value) as null|num + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ATOM_TYPE) + .["value"] = pick_closest_path(FALSE) + if(.["value"] == null) + .["class"] = null + return + + if(VV_DATUM_TYPE) + .["value"] = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(.["value"] == null) + .["class"] = null + return + + if(VV_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["value"] = type + + if(VV_MATRIX) + .["value"] = text2matrix(input("Enter a, b, c, d, e, and f, seperated by a space.", "Matrix", "1 0 0 0 1 0") as null|text) + if(.["value"] == null) + .["class"] = null + return + + if(VV_REGEX) + var/reg = input("Enter regex", "Regex", "") as null|text + if(!reg) + return + .["value"] = regex(reg) + if(.["value"] == null) + .["class"] = null + + + if(VV_ATOM_REFERENCE) + var/type = pick_closest_path(FALSE) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_DATUM_REFERENCE) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + if(VV_MOB_REFERENCE) + var/type = pick_closest_path(FALSE, make_types_fancy(typesof(/mob))) + var/subtypes = vv_subtype_prompt(type) + if(subtypes == null) + .["class"] = null + return + var/list/things = vv_reference_list(type, subtypes) + var/value = input("Select reference:", "Reference", current_value) as null|anything in things + if(!value) + .["class"] = null + return + .["value"] = things[value] + + + + if(VV_CLIENT) + .["value"] = input("Select reference:", "Reference", current_value) as null|anything in GLOB.clients + if(.["value"] == null) + .["class"] = null + return + + + if(VV_FILE) + .["value"] = input("Pick file:", "File") as null|file + if(.["value"] == null) + .["class"] = null + return + + + if(VV_ICON) + .["value"] = input("Pick icon:", "Icon") as null|icon + if(.["value"] == null) + .["class"] = null + return + + + if(VV_MARKED_DATUM) + .["value"] = holder.marked_datum + if(.["value"] == null) + .["class"] = null + return + + if(VV_NEW_ATOM) + var/type = pick_closest_path(FALSE) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + if(VV_NEW_DATUM) + var/type = pick_closest_path(FALSE, get_fancy_list_of_datum_types()) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + if(VV_NEW_TYPE) + var/type = current_value + var/error = "" + do + type = input("Enter type:[error]", "Type", type) as null|text + if(!type) + break + type = text2path(type) + error = "\nType not found, Please try again" + while(!type) + if(!type) + .["class"] = null + return + .["type"] = type + .["value"] = new type() + + + if(VV_NEW_LIST) + .["value"] = list() + .["type"] = /list + +/client/proc/vv_parse_text(O, new_var) + if(O && findtext(new_var, "\[")) + var/process_vars = alert(usr, "\[] detected in string, process as variables?", "Process Variables?", "Yes", "No") + if(process_vars == "Yes") + . = string2listofvars(new_var, O) + +//do they want you to include subtypes? +//FALSE = no subtypes, strict exact type pathing (or the type doesn't have subtypes) +//TRUE = Yes subtypes +//NULL = User cancelled at the prompt or invalid type given +/client/proc/vv_subtype_prompt(var/type) + if(!ispath(type)) + return + var/list/subtypes = subtypesof(type) + if(!subtypes || !subtypes.len) + return FALSE + if(subtypes && subtypes.len) + switch(alert("Strict object type detection?", "Type detection", "Strictly this type","This type and subtypes", "Cancel")) + if("Strictly this type") + return FALSE + if("This type and subtypes") + return TRUE + else + return + +/client/proc/vv_reference_list(type, subtypes) + . = list() + var/list/types = list(type) + if(subtypes) + types = typesof(type) + + var/list/fancytypes = make_types_fancy(types) + + for(var/fancytype in fancytypes) //swap the assoication + types[fancytypes[fancytype]] = fancytype + + var/things = get_all_of_type(type, subtypes) + + var/i = 0 + for(var/thing in things) + var/datum/D = thing + i++ + //try one of 3 methods to shorten the type text: + // fancy type, + // fancy type with the base type removed from the begaining, + // the type with the base type removed from the begaining + var/fancytype = types[D.type] + if(findtext(fancytype, types[type])) + fancytype = copytext(fancytype, length(types[type])+1) + var/shorttype = copytext("[D.type]", length("[type]")+1) + if(length(shorttype) > length(fancytype)) + shorttype = fancytype + if(!length(shorttype)) + shorttype = "/" + + .["[D]([shorttype])\ref[D]#[i]"] = D + +/client/proc/mod_list_add_ass(atom/O) //haha + var/list/L = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + var/class = L["class"] + if(!class) + return + var/var_value = L["value"] + + if(class == VV_TEXT || class == VV_MESSAGE) + var/list/varsvars = vv_parse_text(O, var_value) + for(var/V in varsvars) + var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") + + return var_value + +/client/proc/mod_list_add(list/L, atom/O, original_name, objectvar) + var/list/LL = vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT)) + var/class = LL["class"] + if(!class) + return + var/var_value = LL["value"] + + if(class == VV_TEXT || class == VV_MESSAGE) + var/list/varsvars = vv_parse_text(O, var_value) + for(var/V in varsvars) + var_value = replacetext(var_value,"\[[V]]","[O.vars[V]]") + + if(O) + L = L.Copy() + + L += var_value + + switch(alert("Would you like to associate a value with the list entry?",,"Yes","No")) + if("Yes") + L[var_value] = mod_list_add_ass(O) //hehe + if(O) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: ADDED=[var_value]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") + message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: ADDED=[var_value]") + +/client/proc/mod_list(list/L, atom/O, original_name, objectvar, index, autodetect_class = FALSE) + if(!check_rights(R_VAREDIT)) + return + if(!istype(L, /list)) + to_chat(src, "Not a List.") + return + + if(L.len > 1000) + var/confirm = alert(src, "The list you're trying to edit is very long, continuing may crash the server.", "Warning", "Continue", "Abort") + if(confirm != "Continue") + return + + + + var/list/names = list() + for(var/i in 1 to L.len) + var/key = L[i] + var/value + if(IS_NORMAL_LIST(L) && !isnum(key)) + value = L[key] + if(value == null) + value = "null" + names["#[i] [key] = [value]"] = i + if(!index) + var/variable = input("Which var?","Var") as null|anything in names + "(ADD VAR)" + "(CLEAR NULLS)" + "(CLEAR DUPES)" + "(SHUFFLE)" + + if(variable == null) + return + + if(variable == "(ADD VAR)") + mod_list_add(L, O, original_name, objectvar) + return + + if(variable == "(CLEAR NULLS)") + L = L.Copy() + listclearnulls(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR NULLS") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR NULLS") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR NULLS") + return + + if(variable == "(CLEAR DUPES)") + L = uniqueList(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: CLEAR DUPES") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: CLEAR DUPES") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: CLEAR DUPES") + return + + if(variable == "(SHUFFLE)") + L = shuffle(L) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: SHUFFLE") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: SHUFFLE") + message_admins("[key_name_admin(src)] modified [original_name]'s list [objectvar]: SHUFFLE") + return + + index = names[variable] + + + var/assoc_key + if(index == null) + return + var/assoc = 0 + var/prompt = alert(src, "Do you want to edit the key or it's assigned value?", "Associated List", "Key", "Assigned Value", "Cancel") + if(prompt == "Cancel") + return + if(prompt == "Assigned Value") + assoc = 1 + assoc_key = L[index] + var/default + var/variable + if(assoc) + variable = L[assoc_key] + else + variable = L[index] + + default = vv_get_class(variable) + + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [L[index]]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + var/original_var + if(assoc) + original_var = L[assoc_key] + else + original_var = L[index] + if(O) + L = L.Copy() + var/class + if(autodetect_class) + if(default == VV_TEXT) + default = VV_MESSAGE + class = default + var/list/LL = vv_get_value(default_class = default, current_value = original_var, restricted_classes = list(VV_RESTORE_DEFAULT), extra_classes = list(VV_LIST, "DELETE FROM LIST")) + class = LL["class"] + if(!class) + return + var/new_var = LL["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + switch(class) //Spits a runtime error if you try to modify an entry in the contents list. Dunno how to fix it, yet. + if(VV_LIST) + mod_list(variable, O, original_name, objectvar) + + if("DELETE FROM LIST") + L.Cut(index, index+1) + if(O) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [O.type] [objectvar]: REMOVED=[html_encode("[original_var]")]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") + message_admins("[key_name_admin(src)] modified [original_name]'s [objectvar]: REMOVED=[original_var]") + return + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, new_var) + for(var/V in varsvars) + new_var = replacetext(new_var,"\[[V]]","[O.vars[V]]") + + + if(assoc) + L[assoc_key] = new_var + else + L[index] = new_var + if(O) + if(!O.vv_edit_var(objectvar, L)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### ListVarEdit by [src]: [(O ? O.type : "/list")] [objectvar]: [original_var]=[new_var]") + log_admin("[key_name(src)] modified [original_name]'s [objectvar]: [original_var]=[new_var]") + message_admins("[key_name_admin(src)] modified [original_name]'s varlist [objectvar]: [original_var]=[new_var]") + +/proc/vv_varname_lockcheck(param_var_name) + if(param_var_name in GLOB.VVlocked) + if(!check_rights(R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVckey_edit) + if(!check_rights(R_EVENT | R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVicon_edit_lock) + if(!check_rights(R_EVENT | R_DEBUG)) + return FALSE + if(param_var_name in GLOB.VVpixelmovement) + if(!check_rights(R_DEBUG)) + return FALSE + var/prompt = alert(usr, "Editing this var may irreparably break tile gliding for the rest of the round. THIS CAN'T BE UNDONE", "DANGER", "ABORT ", "Continue", " ABORT") + if(prompt != "Continue") + return FALSE + return TRUE + +/client/proc/modify_variables(atom/O, param_var_name = null, autodetect_class = 0) + if(!check_rights(R_VAREDIT)) + return + + var/class + var/variable + var/var_value + + if(param_var_name) + if(!param_var_name in O.vars) + to_chat(src, "A variable with this name ([param_var_name]) doesn't exist in this datum ([O])") + return + variable = param_var_name + + else + var/list/names = list() + for(var/V in O.vars) + names += V + + names = sortList(names) + + variable = input("Which var?","Var") as null|anything in names + if(!variable) + return + + if(!O.can_vv_get(variable)) + return + + if(!vv_varname_lockcheck(variable)) + return + + var_value = O.vars[variable] + + var/default = vv_get_class(var_value) + + if(isnull(default)) + to_chat(src, "Unable to determine variable type.") + else + to_chat(src, "Variable appears to be [uppertext(default)].") + + to_chat(src, "Variable contains: [var_value]") + + if(default == VV_NUM) + var/dir_text = "" + if(dir < 0 && dir < 16) + if(dir & 1) + dir_text += "NORTH" + if(dir & 2) + dir_text += "SOUTH" + if(dir & 4) + dir_text += "EAST" + if(dir & 8) + dir_text += "WEST" + + if(dir_text) + to_chat(src, "If a direction, direction is: [dir_text]") + + if(autodetect_class && default != VV_NULL) + if(default == VV_TEXT) + default = VV_MESSAGE + class = default + + var/list/value = vv_get_value(class, default, var_value, extra_classes = list(VV_LIST)) + class = value["class"] + + if(!class) + return + var/var_new = value["value"] + + if(class == VV_MESSAGE) + class = VV_TEXT + + var/original_name = "[O]" + + switch(class) + if(VV_LIST) + if(!islist(var_value)) + mod_list(list(), O, original_name, variable) + + mod_list(var_value, O, original_name, variable) + return + + if(VV_RESTORE_DEFAULT) + var_new = initial(O.vars[variable]) + + if(VV_TEXT) + var/list/varsvars = vv_parse_text(O, var_new) + for(var/V in varsvars) + var_new = replacetext(var_new,"\[[V]]","[O.vars[V]]") + + if(!O.vv_edit_var(variable, var_new)) + to_chat(src, "Your edit was rejected by the object.") + return + log_world("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_new]")]") + log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_new]") + var/msg = "[key_name_admin(src)] modified [original_name]'s [variable] to [var_new]" + message_admins(msg) diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm index a2a95db957a0..57f7cad31c46 100644 --- a/code/modules/admin/verbs/one_click_antag.dm +++ b/code/modules/admin/verbs/one_click_antag.dm @@ -1,632 +1,632 @@ -client/proc/one_click_antag() - set name = "Create Antagonist" - set desc = "Auto-create an antagonist of your choice" - set category = "Event" - - if(!check_rights(R_SERVER|R_EVENT)) return - - if(holder) - holder.one_click_antag() - return - - -/datum/admins/proc/one_click_antag() - - var/dat = {"One-click Antagonist
    - Make Traitors
    - Make Changelings
    - Make Revolutionaries
    - Make Cult
    - Make Wizard (Requires Ghosts)
    - Make Vampires
    - Make Vox Raiders (Requires Ghosts)
    - Make Abductor Team (Requires Ghosts)
    - "} - usr << browse(dat, "window=oneclickantag;size=400x400") - return - -/datum/admins/proc/CandCheck(var/role = null, var/mob/living/carbon/human/M, var/datum/game_mode/temp = null) - // You pass in ROLE define (optional), the applicant, and the gamemode, and it will return true / false depending on whether the applicant qualify for the candidacy in question - if(jobban_isbanned(M, "Syndicate")) - return FALSE - if(M.stat || !M.mind || M.mind.special_role || M.mind.offstation_role) - return FALSE - if(temp) - if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.species in temp.protected_species)) - return FALSE - if(role) // Don't even bother evaluating if there's no role - if(player_old_enough_antag(M.client,role) && (role in M.client.prefs.be_special) && !M.client.skip_antag && (!jobban_isbanned(M, role))) - return TRUE - else - return FALSE - else - return TRUE - -/datum/admins/proc/makeTraitors() - var/datum/game_mode/traitor/temp = new - - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - var/antnum = input(owner, "How many traitors you want to create? Enter 0 to cancel","Amount:", 0) as num - if(!antnum || antnum <= 0) - return - log_admin("[key_name(owner)] tried making [antnum] traitors with One-Click-Antag") - message_admins("[key_name_admin(owner)] tried making [antnum] traitors with One-Click-Antag") - - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(CandCheck(ROLE_TRAITOR, applicant, temp)) - candidates += applicant - - if(candidates.len) - var/numTraitors = min(candidates.len, antnum) - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) - - if(candidates.len) - var/agentcount = 0 - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - sleep(300) - - for(var/mob/dead/observer/G in candidates) - if(!G.key) - candidates.Remove(G) - - if(candidates.len) - //Spawns commandos and equips them. - for(var/obj/effect/landmark/L in /area/syndicate_mothership/elite_squad) - if(antnum <= 0) - break - if(L.name == "Syndicate-Commando") - syndicate_leader_selected = antnum == 1?1:0 - - var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, syndicate_leader_selected) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(new_syndicate_commando) - break - - new_syndicate_commando.key = theghost.key - new_syndicate_commando.internal = new_syndicate_commando.s_store - new_syndicate_commando.update_action_buttons_icon() - - //So they don't forget their code or mission. - - - to_chat(new_syndicate_commando, "You are an Elite Syndicate. [!syndicate_leader_selected ? "commando" : "LEADER"] in the service of the Syndicate. \nYour current mission is: [input]") - - antnum-- - - for(var/obj/effect/landmark/L in /area/shuttle/syndicate_elite) - if(L.name == "Syndicate-Commando-Bomb") - new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) - return 1 - - -/proc/makeBody(var/mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character - if(!G_found || !G_found.key) return - - //First we spawn a dude. - var/mob/living/carbon/human/new_character = new(pick(latejoin))//The mob being spawned. - - var/datum/preferences/A = new(G_found.client) - A.copy_to(new_character) - - new_character.dna.ready_dna(new_character) - new_character.key = G_found.key - - return new_character - -/datum/admins/proc/create_syndicate_death_commando(obj/spawn_location, syndicate_leader_selected = 0) - var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) - var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/syndicate_commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(syndicate_leader_selected) - A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" - A.age = rand(35,45) - else - A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" - A.copy_to(new_syndicate_commando) - - new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. - - //Creates mind stuff. - new_syndicate_commando.mind_initialize() - new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.offstation_role = TRUE - //Adds them to current traitor list. Which is really the extra antagonist list. - SSticker.mode.traitors += new_syndicate_commando.mind - new_syndicate_commando.equip_syndicate_commando(syndicate_leader_selected) - - return new_syndicate_commando - -/datum/admins/proc/makeVoxRaiders() - - var/list/mob/candidates = list() - var/mob/theghost = null - var/time_passed = world.time - var/input = "Disregard shinies, acquire hardware." - - var/leader_chosen = 0 //when the leader is chosen. The last person spawned. - - var/antnum = input(owner, "How many raiders you want to create? Enter 0 to cancel.","Amount:", 0) as num - if(!antnum || antnum <= 0) - return - log_admin("[key_name(owner)] tried making Vox Raiders with One-Click-Antag") - message_admins("[key_name_admin(owner)] tried making Vox Raiders with One-Click-Antag") -//Generates a list of candidates from active ghosts. - for(var/mob/G in GLOB.respawnable_list) - if(istype(G) && G.client && (ROLE_RAIDER in G.client.prefs.be_special)) - if(player_old_enough_antag(G.client,ROLE_RAIDER)) - if(!jobban_isbanned(G, "raider") && !jobban_isbanned(G, "Syndicate")) - spawn(0) - switch(alert(G,"Do you wish to be considered for a vox raiding party arriving on the station?","Please answer in 30 seconds!","Yes","No")) - if("Yes") - if((world.time-time_passed)>300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) //Debug. - - for(var/mob/dead/observer/G in candidates) - if(!G.key) - candidates.Remove(G) - - if(candidates.len) - var/raiders = min(antnum, candidates.len) - //Spawns vox raiders and equips them. - for(var/obj/effect/landmark/L in world) - if(L.name == "voxstart") - if(raiders<=0) - break - - var/mob/living/carbon/human/new_vox = create_vox_raider(L, leader_chosen) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(new_vox) - break - - new_vox.key = theghost.key - SSticker.mode.traitors += new_vox.mind - - to_chat(new_vox, "You are a Vox Primalis, fresh out of the Shoal. Your ship has arrived at the Tau Ceti system hosting the NSV Exodus... or was it the Luna? NSS? Utopia? Nobody is really sure, but everyong is raring to start pillaging! Your current goal is: [input]") - to_chat(new_vox, "Don't forget to turn on your nitrogen internals!") - - raiders-- - else - return 0 - return 1 - -/datum/admins/proc/create_vox_raider(obj/spawn_location, leader_chosen = 0) - - var/sounds = rand(2,8) - var/i = 0 - var/newname = "" - - while(i<=sounds) - i++ - newname += pick(list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) - - var/mob/living/carbon/human/new_vox = new /mob/living/carbon/human/vox(spawn_location.loc) - - new_vox.add_language("Tradeband") - new_vox.real_name = capitalize(newname) - new_vox.dna.real_name = new_vox.real_name - new_vox.name = new_vox.real_name - new_vox.age = rand(12,20) - new_vox.flavor_text = "" - new_vox.change_eye_color(rand(1, 255), rand(1, 255), rand(1, 255)) - new_vox.s_tone = rand(1, 6) - - // Do the initial caching of the player's body icons. - new_vox.force_update_limbs() - new_vox.update_dna() - new_vox.update_eyes() - - for(var/obj/item/organ/external/limb in new_vox.bodyparts) - limb.status &= ~ORGAN_ROBOT - - //Now apply cortical stack. - var/obj/item/implant/cortical/I = new(new_vox) - I.implant(new_vox) - cortical_stacks += I - - new_vox.equip_vox_raider() - new_vox.regenerate_icons() - - return new_vox - -/datum/admins/proc/makeVampires() - - var/datum/game_mode/vampire/temp = new - if(config.protect_roles_from_antagonist) - temp.restricted_jobs += temp.protected_jobs - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - var/antnum = input(owner, "How many vampires you want to create? Enter 0 to cancel","Amount:", 0) as num - if(!antnum || antnum <= 0) - return - - log_admin("[key_name(owner)] tried making Vampires with One-Click-Antag") - message_admins("[key_name_admin(owner)] tried making Vampires with One-Click-Antag") - - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(CandCheck(ROLE_VAMPIRE, applicant, temp)) - candidates += applicant - - if(candidates.len) - var/numVampires = min(candidates.len, antnum) - - for(var/i = 0, i300)//If more than 30 game seconds passed. - return - candidates += G - if("No") - return - else - return - - sleep(300) //Debug. - - for(var/mob/dead/observer/G in candidates) - if(!G.key) - candidates.Remove(G) - - if(candidates.len) - var/teamOneMembers = 5 - var/teamTwoMembers = 5 - var/datum/preferences/A = new() - for(var/obj/effect/landmark/L in world) - if(L.name == "tdome1") - if(teamOneMembers<=0) - break - - var/mob/living/carbon/human/newMember = new(L.loc) - - A.copy_to(newMember) - - newMember.dna.ready_dna(newMember) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(newMember) - break - - newMember.key = theghost.key - teamOneMembers-- - to_chat(newMember, "You are a member of the GREEN Thunderdome team! Gear up and help your team destroy the red team!") - - if(L.name == "tdome2") - if(teamTwoMembers<=0) - break - - var/mob/living/carbon/human/newMember = new(L.loc) - - A.copy_to(newMember) - - newMember.dna.ready_dna(newMember) - - while((!theghost || !theghost.client) && candidates.len) - theghost = pick(candidates) - candidates.Remove(theghost) - - if(!theghost) - qdel(newMember) - break - - newMember.key = theghost.key - teamTwoMembers-- - to_chat(newMember, "You are a member of the RED Thunderdome team! Gear up and help your team destroy the green team!") - else - return 0 - return 1 +client/proc/one_click_antag() + set name = "Create Antagonist" + set desc = "Auto-create an antagonist of your choice" + set category = "Event" + + if(!check_rights(R_SERVER|R_EVENT)) return + + if(holder) + holder.one_click_antag() + return + + +/datum/admins/proc/one_click_antag() + + var/dat = {"One-click Antagonist
    + Make Traitors
    + Make Changelings
    + Make Revolutionaries
    + Make Cult
    + Make Wizard (Requires Ghosts)
    + Make Vampires
    + Make Vox Raiders (Requires Ghosts)
    + Make Abductor Team (Requires Ghosts)
    + "} + usr << browse(dat, "window=oneclickantag;size=400x400") + return + +/datum/admins/proc/CandCheck(var/role = null, var/mob/living/carbon/human/M, var/datum/game_mode/temp = null) + // You pass in ROLE define (optional), the applicant, and the gamemode, and it will return true / false depending on whether the applicant qualify for the candidacy in question + if(jobban_isbanned(M, "Syndicate")) + return FALSE + if(M.stat || !M.mind || M.mind.special_role || M.mind.offstation_role) + return FALSE + if(temp) + if((M.mind.assigned_role in temp.restricted_jobs) || (M.client.prefs.species in temp.protected_species)) + return FALSE + if(role) // Don't even bother evaluating if there's no role + if(player_old_enough_antag(M.client,role) && (role in M.client.prefs.be_special) && !M.client.skip_antag && (!jobban_isbanned(M, role))) + return TRUE + else + return FALSE + else + return TRUE + +/datum/admins/proc/makeTraitors() + var/datum/game_mode/traitor/temp = new + + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + var/antnum = input(owner, "How many traitors you want to create? Enter 0 to cancel","Amount:", 0) as num + if(!antnum || antnum <= 0) + return + log_admin("[key_name(owner)] tried making [antnum] traitors with One-Click-Antag") + message_admins("[key_name_admin(owner)] tried making [antnum] traitors with One-Click-Antag") + + for(var/mob/living/carbon/human/applicant in GLOB.player_list) + if(CandCheck(ROLE_TRAITOR, applicant, temp)) + candidates += applicant + + if(candidates.len) + var/numTraitors = min(candidates.len, antnum) + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) + + if(candidates.len) + var/agentcount = 0 + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + sleep(300) + + for(var/mob/dead/observer/G in candidates) + if(!G.key) + candidates.Remove(G) + + if(candidates.len) + //Spawns commandos and equips them. + for(var/obj/effect/landmark/L in /area/syndicate_mothership/elite_squad) + if(antnum <= 0) + break + if(L.name == "Syndicate-Commando") + syndicate_leader_selected = antnum == 1?1:0 + + var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, syndicate_leader_selected) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(new_syndicate_commando) + break + + new_syndicate_commando.key = theghost.key + new_syndicate_commando.internal = new_syndicate_commando.s_store + new_syndicate_commando.update_action_buttons_icon() + + //So they don't forget their code or mission. + + + to_chat(new_syndicate_commando, "You are an Elite Syndicate. [!syndicate_leader_selected ? "commando" : "LEADER"] in the service of the Syndicate. \nYour current mission is: [input]") + + antnum-- + + for(var/obj/effect/landmark/L in /area/shuttle/syndicate_elite) + if(L.name == "Syndicate-Commando-Bomb") + new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) + return 1 + + +/proc/makeBody(var/mob/dead/observer/G_found) // Uses stripped down and bastardized code from respawn character + if(!G_found || !G_found.key) return + + //First we spawn a dude. + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned. + + var/datum/preferences/A = new(G_found.client) + A.copy_to(new_character) + + new_character.dna.ready_dna(new_character) + new_character.key = G_found.key + + return new_character + +/datum/admins/proc/create_syndicate_death_commando(obj/spawn_location, syndicate_leader_selected = 0) + var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) + var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/syndicate_commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(syndicate_leader_selected) + A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" + A.age = rand(35,45) + else + A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" + A.copy_to(new_syndicate_commando) + + new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. + + //Creates mind stuff. + new_syndicate_commando.mind_initialize() + new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.offstation_role = TRUE + //Adds them to current traitor list. Which is really the extra antagonist list. + SSticker.mode.traitors += new_syndicate_commando.mind + new_syndicate_commando.equip_syndicate_commando(syndicate_leader_selected) + + return new_syndicate_commando + +/datum/admins/proc/makeVoxRaiders() + + var/list/mob/candidates = list() + var/mob/theghost = null + var/time_passed = world.time + var/input = "Disregard shinies, acquire hardware." + + var/leader_chosen = 0 //when the leader is chosen. The last person spawned. + + var/antnum = input(owner, "How many raiders you want to create? Enter 0 to cancel.","Amount:", 0) as num + if(!antnum || antnum <= 0) + return + log_admin("[key_name(owner)] tried making Vox Raiders with One-Click-Antag") + message_admins("[key_name_admin(owner)] tried making Vox Raiders with One-Click-Antag") +//Generates a list of candidates from active ghosts. + for(var/mob/G in GLOB.respawnable_list) + if(istype(G) && G.client && (ROLE_RAIDER in G.client.prefs.be_special)) + if(player_old_enough_antag(G.client,ROLE_RAIDER)) + if(!jobban_isbanned(G, "raider") && !jobban_isbanned(G, "Syndicate")) + spawn(0) + switch(alert(G,"Do you wish to be considered for a vox raiding party arriving on the station?","Please answer in 30 seconds!","Yes","No")) + if("Yes") + if((world.time-time_passed)>300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) //Debug. + + for(var/mob/dead/observer/G in candidates) + if(!G.key) + candidates.Remove(G) + + if(candidates.len) + var/raiders = min(antnum, candidates.len) + //Spawns vox raiders and equips them. + for(var/obj/effect/landmark/L in world) + if(L.name == "voxstart") + if(raiders<=0) + break + + var/mob/living/carbon/human/new_vox = create_vox_raider(L, leader_chosen) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(new_vox) + break + + new_vox.key = theghost.key + SSticker.mode.traitors += new_vox.mind + + to_chat(new_vox, "You are a Vox Primalis, fresh out of the Shoal. Your ship has arrived at the Tau Ceti system hosting the NSV Exodus... or was it the Luna? NSS? Utopia? Nobody is really sure, but everyong is raring to start pillaging! Your current goal is: [input]") + to_chat(new_vox, "Don't forget to turn on your nitrogen internals!") + + raiders-- + else + return 0 + return 1 + +/datum/admins/proc/create_vox_raider(obj/spawn_location, leader_chosen = 0) + + var/sounds = rand(2,8) + var/i = 0 + var/newname = "" + + while(i<=sounds) + i++ + newname += pick(list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) + + var/mob/living/carbon/human/new_vox = new /mob/living/carbon/human/vox(spawn_location.loc) + + new_vox.add_language("Tradeband") + new_vox.real_name = capitalize(newname) + new_vox.dna.real_name = new_vox.real_name + new_vox.name = new_vox.real_name + new_vox.age = rand(12,20) + new_vox.flavor_text = "" + new_vox.change_eye_color(rand(1, 255), rand(1, 255), rand(1, 255)) + new_vox.s_tone = rand(1, 6) + + // Do the initial caching of the player's body icons. + new_vox.force_update_limbs() + new_vox.update_dna() + new_vox.update_eyes() + + for(var/obj/item/organ/external/limb in new_vox.bodyparts) + limb.status &= ~ORGAN_ROBOT + + //Now apply cortical stack. + var/obj/item/implant/cortical/I = new(new_vox) + I.implant(new_vox) + GLOB.cortical_stacks += I + + new_vox.equip_vox_raider() + new_vox.regenerate_icons() + + return new_vox + +/datum/admins/proc/makeVampires() + + var/datum/game_mode/vampire/temp = new + if(config.protect_roles_from_antagonist) + temp.restricted_jobs += temp.protected_jobs + + var/list/mob/living/carbon/human/candidates = list() + var/mob/living/carbon/human/H = null + + var/antnum = input(owner, "How many vampires you want to create? Enter 0 to cancel","Amount:", 0) as num + if(!antnum || antnum <= 0) + return + + log_admin("[key_name(owner)] tried making Vampires with One-Click-Antag") + message_admins("[key_name_admin(owner)] tried making Vampires with One-Click-Antag") + + for(var/mob/living/carbon/human/applicant in GLOB.player_list) + if(CandCheck(ROLE_VAMPIRE, applicant, temp)) + candidates += applicant + + if(candidates.len) + var/numVampires = min(candidates.len, antnum) + + for(var/i = 0, i300)//If more than 30 game seconds passed. + return + candidates += G + if("No") + return + else + return + + sleep(300) //Debug. + + for(var/mob/dead/observer/G in candidates) + if(!G.key) + candidates.Remove(G) + + if(candidates.len) + var/teamOneMembers = 5 + var/teamTwoMembers = 5 + var/datum/preferences/A = new() + for(var/obj/effect/landmark/L in world) + if(L.name == "tdome1") + if(teamOneMembers<=0) + break + + var/mob/living/carbon/human/newMember = new(L.loc) + + A.copy_to(newMember) + + newMember.dna.ready_dna(newMember) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(newMember) + break + + newMember.key = theghost.key + teamOneMembers-- + to_chat(newMember, "You are a member of the GREEN Thunderdome team! Gear up and help your team destroy the red team!") + + if(L.name == "tdome2") + if(teamTwoMembers<=0) + break + + var/mob/living/carbon/human/newMember = new(L.loc) + + A.copy_to(newMember) + + newMember.dna.ready_dna(newMember) + + while((!theghost || !theghost.client) && candidates.len) + theghost = pick(candidates) + candidates.Remove(theghost) + + if(!theghost) + qdel(newMember) + break + + newMember.key = theghost.key + teamTwoMembers-- + to_chat(newMember, "You are a member of the RED Thunderdome team! Gear up and help your team destroy the green team!") + else + return 0 + return 1 diff --git a/code/modules/admin/verbs/onlyone.dm b/code/modules/admin/verbs/onlyone.dm index 3db8f1023201..e51f6da991ac 100644 --- a/code/modules/admin/verbs/onlyone.dm +++ b/code/modules/admin/verbs/onlyone.dm @@ -1,104 +1,104 @@ -/client/proc/only_one() - if(!SSticker) - alert("The game hasn't started yet!") - return - - var/list/incompatible_species = list(/datum/species/plasmaman, /datum/species/vox) - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == DEAD || !(H.client)) - continue - if(is_special_character(H)) - continue - if(is_type_in_list(H.dna.species, incompatible_species)) - H.set_species(/datum/species/human) - var/datum/preferences/A = new() // Randomize appearance - A.copy_to(H) - - SSticker.mode.traitors += H.mind - H.mind.special_role = SPECIAL_ROLE_TRAITOR - - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = H.mind - H.mind.objectives += hijack_objective - - to_chat(H, "You are a Highlander. Kill all other Highlanders. There can be only one.") - var/obj_count = 1 - for(var/datum/objective/OBJ in H.mind.objectives) - to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") - obj_count++ - - for(var/obj/item/I in H) - if(istype(I, /obj/item/implant)) - continue - if(istype(I, /obj/item/organ)) - continue - qdel(I) - - H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(H), slot_w_uniform) - H.equip_to_slot_or_del(new /obj/item/radio/headset/heads/captain(H), slot_l_ear) - H.equip_to_slot_or_del(new /obj/item/clothing/head/beret(H), slot_head) - H.equip_to_slot_or_del(new /obj/item/claymore/highlander(H), slot_r_hand) - H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/pinpointer(H.loc), slot_l_store) - - var/obj/item/card/id/W = new(H) - W.name = "[H.real_name]'s ID Card" - W.icon_state = "centcom" - W.access = get_all_accesses() - W.access += get_all_centcom_access() - W.assignment = "Highlander" - W.registered_name = H.real_name - H.equip_to_slot_or_del(W, slot_wear_id) - H.dna.species.after_equip_job(null, H) - H.regenerate_icons() - - message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ONE! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) - log_admin("[key_name(usr)] used there can be only one.") - nologevent = 1 - world << sound('sound/music/thunderdome.ogg') - -/client/proc/only_me() - if(!SSticker) - alert("The game hasn't started yet!") - return - - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.stat == 2 || !(H.client)) continue - if(is_special_character(H)) continue - - SSticker.mode.traitors += H.mind - H.mind.special_role = "[H.real_name] Prime" - - var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone - hijack_objective.owner = H.mind - H.mind.objectives += hijack_objective - - to_chat(H, "You are the multiverse summoner. Activate your blade to summon copies of yourself from another universe to fight by your side.") - var/obj_count = 1 - for(var/datum/objective/OBJ in H.mind.objectives) - to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") - obj_count++ - - var/obj/item/slot_item_ID = H.get_item_by_slot(slot_wear_id) - qdel(slot_item_ID) - var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand) - H.unEquip(slot_item_hand) - - var /obj/item/multisword/pure_evil/multi = new(H) - H.equip_to_slot_or_del(multi, slot_r_hand) - - var/obj/item/card/id/W = new(H) - W.icon_state = "centcom" - W.access = get_all_accesses() - W.access += get_all_centcom_access() - W.assignment = "Multiverse Summoner" - W.registered_name = H.real_name - W.update_label(H.real_name) - H.equip_to_slot_or_del(W, slot_wear_id) - - H.update_icons() - - message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ME! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) - log_admin("[key_name(usr)] used there can be only me.") - nologevent = 1 - world << sound('sound/music/thunderdome.ogg') +/client/proc/only_one() + if(!SSticker) + alert("The game hasn't started yet!") + return + + var/list/incompatible_species = list(/datum/species/plasmaman, /datum/species/vox) + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.stat == DEAD || !(H.client)) + continue + if(is_special_character(H)) + continue + if(is_type_in_list(H.dna.species, incompatible_species)) + H.set_species(/datum/species/human) + var/datum/preferences/A = new() // Randomize appearance + A.copy_to(H) + + SSticker.mode.traitors += H.mind + H.mind.special_role = SPECIAL_ROLE_TRAITOR + + var/datum/objective/hijack/hijack_objective = new + hijack_objective.owner = H.mind + H.mind.objectives += hijack_objective + + to_chat(H, "You are a Highlander. Kill all other Highlanders. There can be only one.") + var/obj_count = 1 + for(var/datum/objective/OBJ in H.mind.objectives) + to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") + obj_count++ + + for(var/obj/item/I in H) + if(istype(I, /obj/item/implant)) + continue + if(istype(I, /obj/item/organ)) + continue + qdel(I) + + H.equip_to_slot_or_del(new /obj/item/clothing/under/kilt(H), slot_w_uniform) + H.equip_to_slot_or_del(new /obj/item/radio/headset/heads/captain(H), slot_l_ear) + H.equip_to_slot_or_del(new /obj/item/clothing/head/beret(H), slot_head) + H.equip_to_slot_or_del(new /obj/item/claymore/highlander(H), slot_r_hand) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(H), slot_shoes) + H.equip_to_slot_or_del(new /obj/item/pinpointer(H.loc), slot_l_store) + + var/obj/item/card/id/W = new(H) + W.name = "[H.real_name]'s ID Card" + W.icon_state = "centcom" + W.access = get_all_accesses() + W.access += get_all_centcom_access() + W.assignment = "Highlander" + W.registered_name = H.real_name + H.equip_to_slot_or_del(W, slot_wear_id) + H.dna.species.after_equip_job(null, H) + H.regenerate_icons() + + message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ONE! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) + log_admin("[key_name(usr)] used there can be only one.") + GLOB.nologevent = 1 + world << sound('sound/music/thunderdome.ogg') + +/client/proc/only_me() + if(!SSticker) + alert("The game hasn't started yet!") + return + + for(var/mob/living/carbon/human/H in GLOB.player_list) + if(H.stat == 2 || !(H.client)) continue + if(is_special_character(H)) continue + + SSticker.mode.traitors += H.mind + H.mind.special_role = "[H.real_name] Prime" + + var/datum/objective/hijackclone/hijack_objective = new /datum/objective/hijackclone + hijack_objective.owner = H.mind + H.mind.objectives += hijack_objective + + to_chat(H, "You are the multiverse summoner. Activate your blade to summon copies of yourself from another universe to fight by your side.") + var/obj_count = 1 + for(var/datum/objective/OBJ in H.mind.objectives) + to_chat(H, "Objective #[obj_count]: [OBJ.explanation_text]") + obj_count++ + + var/obj/item/slot_item_ID = H.get_item_by_slot(slot_wear_id) + qdel(slot_item_ID) + var/obj/item/slot_item_hand = H.get_item_by_slot(slot_r_hand) + H.unEquip(slot_item_hand) + + var /obj/item/multisword/pure_evil/multi = new(H) + H.equip_to_slot_or_del(multi, slot_r_hand) + + var/obj/item/card/id/W = new(H) + W.icon_state = "centcom" + W.access = get_all_accesses() + W.access += get_all_centcom_access() + W.assignment = "Multiverse Summoner" + W.registered_name = H.real_name + W.update_label(H.real_name) + H.equip_to_slot_or_del(W, slot_wear_id) + + H.update_icons() + + message_admins("[key_name_admin(usr)] used THERE CAN BE ONLY ME! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) + log_admin("[key_name(usr)] used there can be only me.") + GLOB.nologevent = 1 + world << sound('sound/music/thunderdome.ogg') diff --git a/code/modules/admin/verbs/onlyoneteam.dm b/code/modules/admin/verbs/onlyoneteam.dm index 846a77c508aa..c0847d6ff7e2 100644 --- a/code/modules/admin/verbs/onlyoneteam.dm +++ b/code/modules/admin/verbs/onlyoneteam.dm @@ -29,7 +29,7 @@ H.equip_to_slot_or_del(new /obj/item/clothing/shoes/white(H), slot_shoes) if(!team_toggle) - team_alpha += H + GLOB.team_alpha += H H.equip_to_slot_or_del(new /obj/item/clothing/under/color/red/dodgeball(H), slot_w_uniform) var/obj/item/card/id/W = new(H) @@ -42,7 +42,7 @@ H.equip_to_slot_or_del(W, slot_wear_id) else - team_bravo += H + GLOB.team_bravo += H H.equip_to_slot_or_del(new /obj/item/clothing/under/color/blue/dodgeball(H), slot_w_uniform) var/obj/item/card/id/W = new(H) @@ -60,7 +60,7 @@ message_admins("[key_name_admin(usr)] used DODGEBAWWWWWWWL! -NO ATTACK LOGS WILL BE SENT TO ADMINS FROM THIS POINT FORTH-", 1) log_admin("[key_name(usr)] used dodgeball.") - nologevent = 1 + GLOB.nologevent = 1 /obj/item/beach_ball/dodgeball name = "dodgeball" @@ -76,13 +76,13 @@ if(H.r_hand == src) return if(H.l_hand == src) return var/mob/A = H.LAssailant - if((H in team_alpha) && (A in team_alpha)) + if((H in GLOB.team_alpha) && (A in GLOB.team_alpha)) to_chat(A, "He's on your team!") return - else if((H in team_bravo) && (A in team_bravo)) + else if((H in GLOB.team_bravo) && (A in GLOB.team_bravo)) to_chat(A, "He's on your team!") return - else if(!A in team_alpha && !A in team_bravo) + else if(!A in GLOB.team_alpha && !A in GLOB.team_bravo) to_chat(A, "You're not part of the dodgeball game, sorry!") return else diff --git a/code/modules/admin/verbs/playsound.dm b/code/modules/admin/verbs/playsound.dm index 995673e7d206..6ec103cccc99 100644 --- a/code/modules/admin/verbs/playsound.dm +++ b/code/modules/admin/verbs/playsound.dm @@ -1,153 +1,153 @@ -var/list/sounds_cache = list() - -/client/proc/stop_global_admin_sounds() - set category = "Event" - set name = "Stop Global Admin Sounds" - if(!check_rights(R_SOUNDS)) - return - - var/sound/awful_sound = sound(null, repeat = 0, wait = 0, channel = CHANNEL_ADMIN) - - log_admin("[key_name(src)] stopped admin sounds.") - message_admins("[key_name_admin(src)] stopped admin sounds.", 1) - for(var/mob/M in GLOB.player_list) - M << awful_sound - -/client/proc/play_sound(S as sound) - set category = "Event" - set name = "Play Global Sound" - if(!check_rights(R_SOUNDS)) return - - var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = CHANNEL_ADMIN) - uploaded_sound.priority = 250 - - sounds_cache += S - - if(alert("Are you sure?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request" ,"Play", "Cancel") == "Cancel") - return - - log_admin("[key_name(src)] played sound [S]") - message_admins("[key_name_admin(src)] played sound [S]", 1) - - for(var/mob/M in GLOB.player_list) - if(M.client.prefs.sound & SOUND_MIDI) - if(isnewplayer(M) && (M.client.prefs.sound & SOUND_LOBBY)) - M.stop_sound_channel(CHANNEL_LOBBYMUSIC) - SEND_SOUND(M, uploaded_sound) - - feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/play_local_sound(S as sound) - set category = "Event" - set name = "Play Local Sound" - if(!check_rights(R_SOUNDS)) return - - log_admin("[key_name(src)] played a local sound [S]") - message_admins("[key_name_admin(src)] played a local sound [S]", 1) - playsound(get_turf(src.mob), S, 50, 0, 0) - feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/play_server_sound() - set category = "Event" - set name = "Play Server Sound" - if(!check_rights(R_SOUNDS)) return - - var/list/sounds = file2list("sound/serversound_list.txt"); - sounds += sounds_cache - - var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds - if(!melody) return - - play_sound(melody) - feedback_add_details("admin_verb","PSS") //If you are copy-pasting this, ensure the 2nd paramter is unique to the new proc! - -/client/proc/play_intercomm_sound() - set category = "Event" - set name = "Play Sound via Intercomms" - set desc = "Plays a sound at every intercomm on the station z level. Works best with small sounds." - if(!check_rights(R_SOUNDS)) return - - var/A = alert("This will play a sound at every intercomm, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope") - if(A != "Yep") return - - var/list/sounds = file2list("sound/serversound_list.txt"); - sounds += sounds_cache - - var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds - if(!melody) return - - var/cvol = 35 - var/inputvol = input("How loud would you like this to be? (1-70)", "Volume", "35") as num | null - if(!inputvol) return - if(inputvol && inputvol >= 1 && inputvol <= 70) - cvol = inputvol - - //Allows for override to utilize intercomms on all z-levels - var/B = alert("Do you want to play through intercomms on ALL Z-levels, or just the station?", "Override", "All", "Station") - var/ignore_z = 0 - if(B == "All") - ignore_z = 1 - - //Allows for override to utilize incomplete and unpowered intercomms - var/C = alert("Do you want to play through unpowered / incomplete intercomms, so the crew can't silence it?", "Override", "Yep", "Nope") - var/ignore_power = 0 - if(C == "Yep") - ignore_power = 1 - - for(var/O in GLOB.global_intercoms) - var/obj/item/radio/intercom/I = O - if(!is_station_level(I.z) && !ignore_z) - continue - if(!I.on && !ignore_power) - continue - playsound(I, melody, cvol) - -/* -/client/proc/cuban_pete() - set category = "Event" - set name = "Cuban Pete Time" - - message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'cubanpetetime.ogg' - - for(var/mob/living/carbon/human/CP in world) - if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") - C << "Your body can't contain the rhumba beat" - CP.gib() - - -/client/proc/bananaphone() - set category = "Event" - set name = "Banana Phone" - - message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'bananaphone.ogg' - - -client/proc/space_asshole() - set category = "Event" - set name = "Space Asshole" - - message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'sound/music/space_asshole.ogg' - - -client/proc/honk_theme() - set category = "Event" - set name = "Honk" - - message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) - for(var/mob/M in world) - if(M.client) - if(M.client.midis) - M << 'honk_theme.ogg'*/ +GLOBAL_LIST_EMPTY(sounds_cache) + +/client/proc/stop_global_admin_sounds() + set category = "Event" + set name = "Stop Global Admin Sounds" + if(!check_rights(R_SOUNDS)) + return + + var/sound/awful_sound = sound(null, repeat = 0, wait = 0, channel = CHANNEL_ADMIN) + + log_admin("[key_name(src)] stopped admin sounds.") + message_admins("[key_name_admin(src)] stopped admin sounds.", 1) + for(var/mob/M in GLOB.player_list) + M << awful_sound + +/client/proc/play_sound(S as sound) + set category = "Event" + set name = "Play Global Sound" + if(!check_rights(R_SOUNDS)) return + + var/sound/uploaded_sound = sound(S, repeat = 0, wait = 1, channel = CHANNEL_ADMIN) + uploaded_sound.priority = 250 + + GLOB.sounds_cache += S + + if(alert("Are you sure?\nSong: [S]\nNow you can also play this sound using \"Play Server Sound\".", "Confirmation request" ,"Play", "Cancel") == "Cancel") + return + + log_admin("[key_name(src)] played sound [S]") + message_admins("[key_name_admin(src)] played sound [S]", 1) + + for(var/mob/M in GLOB.player_list) + if(M.client.prefs.sound & SOUND_MIDI) + if(isnewplayer(M) && (M.client.prefs.sound & SOUND_LOBBY)) + M.stop_sound_channel(CHANNEL_LOBBYMUSIC) + SEND_SOUND(M, uploaded_sound) + + feedback_add_details("admin_verb","PGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/play_local_sound(S as sound) + set category = "Event" + set name = "Play Local Sound" + if(!check_rights(R_SOUNDS)) return + + log_admin("[key_name(src)] played a local sound [S]") + message_admins("[key_name_admin(src)] played a local sound [S]", 1) + playsound(get_turf(src.mob), S, 50, 0, 0) + feedback_add_details("admin_verb","PLS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/play_server_sound() + set category = "Event" + set name = "Play Server Sound" + if(!check_rights(R_SOUNDS)) return + + var/list/sounds = file2list("sound/serversound_list.txt"); + sounds += GLOB.sounds_cache + + var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds + if(!melody) return + + play_sound(melody) + feedback_add_details("admin_verb","PSS") //If you are copy-pasting this, ensure the 2nd paramter is unique to the new proc! + +/client/proc/play_intercomm_sound() + set category = "Event" + set name = "Play Sound via Intercomms" + set desc = "Plays a sound at every intercomm on the station z level. Works best with small sounds." + if(!check_rights(R_SOUNDS)) return + + var/A = alert("This will play a sound at every intercomm, are you sure you want to continue? This works best with short sounds, beware.","Warning","Yep","Nope") + if(A != "Yep") return + + var/list/sounds = file2list("sound/serversound_list.txt"); + sounds += GLOB.sounds_cache + + var/melody = input("Select a sound from the server to play", "Server sound list") as null|anything in sounds + if(!melody) return + + var/cvol = 35 + var/inputvol = input("How loud would you like this to be? (1-70)", "Volume", "35") as num | null + if(!inputvol) return + if(inputvol && inputvol >= 1 && inputvol <= 70) + cvol = inputvol + + //Allows for override to utilize intercomms on all z-levels + var/B = alert("Do you want to play through intercomms on ALL Z-levels, or just the station?", "Override", "All", "Station") + var/ignore_z = 0 + if(B == "All") + ignore_z = 1 + + //Allows for override to utilize incomplete and unpowered intercomms + var/C = alert("Do you want to play through unpowered / incomplete intercomms, so the crew can't silence it?", "Override", "Yep", "Nope") + var/ignore_power = 0 + if(C == "Yep") + ignore_power = 1 + + for(var/O in GLOB.global_intercoms) + var/obj/item/radio/intercom/I = O + if(!is_station_level(I.z) && !ignore_z) + continue + if(!I.on && !ignore_power) + continue + playsound(I, melody, cvol) + +/* +/client/proc/cuban_pete() + set category = "Event" + set name = "Cuban Pete Time" + + message_admins("[key_name_admin(usr)] has declared Cuban Pete Time!", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'cubanpetetime.ogg' + + for(var/mob/living/carbon/human/CP in world) + if(CP.real_name=="Cuban Pete" && CP.key!="Rosham") + C << "Your body can't contain the rhumba beat" + CP.gib() + + +/client/proc/bananaphone() + set category = "Event" + set name = "Banana Phone" + + message_admins("[key_name_admin(usr)] has activated Banana Phone!", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'bananaphone.ogg' + + +client/proc/space_asshole() + set category = "Event" + set name = "Space Asshole" + + message_admins("[key_name_admin(usr)] has played the Space Asshole Hymn.", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'sound/music/space_asshole.ogg' + + +client/proc/honk_theme() + set category = "Event" + set name = "Honk" + + message_admins("[key_name_admin(usr)] has creeped everyone out with Blackest Honks.", 1) + for(var/mob/M in world) + if(M.client) + if(M.client.midis) + M << 'honk_theme.ogg'*/ diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 9973de89c85f..95a66684d1d3 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -1,89 +1,89 @@ -/mob/living/verb/pray(msg as text) - set category = "IC" - set name = "Pray" - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - if(!msg) - return - - if(usr.client) - if(usr.client.prefs.muted & MUTE_PRAY) - to_chat(usr, "You cannot pray (muted).") - return - if(client.handle_spam_prevention(msg, MUTE_PRAY, OOC_COOLDOWN)) - return - - var/image/cross = image('icons/obj/storage.dmi',"bible") - var/font_color = "purple" - var/prayer_type = "PRAYER" - var/deity - if(usr.job == "Chaplain") - if(SSticker && SSticker.Bible_deity_name) - deity = SSticker.Bible_deity_name - cross = image('icons/obj/storage.dmi',"kingyellow") - font_color = "blue" - prayer_type = "CHAPLAIN PRAYER" - else if(iscultist(usr)) - cross = image('icons/obj/storage.dmi',"tome") - font_color = "red" - prayer_type = "CULTIST PRAYER" - deity = SSticker.cultdat.entity_name - - log_say("(PRAYER) [msg]", usr) - msg = "[bicon(cross)][prayer_type][deity ? " (to [deity])" : ""][mind && mind.isholy ? " (blessings: [mind.num_blessed])" : ""]: [key_name(src, 1)] ([ADMIN_QUE(src,"?")]) ([ADMIN_PP(src,"PP")]) ([ADMIN_VV(src,"VV")]) ([ADMIN_TP(src,"TP")]) ([ADMIN_SM(src,"SM")]) ([admin_jump_link(src)]) ([ADMIN_SC(src,"SC")]) (BLESS) (SMITE): [msg]" - - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - to_chat(usr, "Your prayers have been received by the gods.") - - feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/proc/Centcomm_announce(var/text , var/mob/Sender) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "CENTCOMM: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")])): [msg]" - for(var/client/X in GLOB.admins) - if(R_EVENT & X.holder.rights) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/Syndicate_announce(var/text , var/mob/Sender) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "SYNDICATE: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_SYNDICATE_REPLY(Sender,"RPLY")]): [msg]" - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/HONK_announce(var/text , var/mob/Sender) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "HONK: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RPLY): [msg]" - for(var/client/X in GLOB.admins) - if(R_EVENT & X.holder.rights) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/ERT_Announce(var/text , var/mob/Sender, var/repeat_warning) - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "ERT REQUEST: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RESPOND): [msg]" - if(repeat_warning) - msg += "
    WARNING: ERT request has gone 5 minutes with no reply!" - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - -/proc/Nuke_request(text , mob/Sender) - var/nuke_code = get_nuke_code() - var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) - msg = "NUKE CODE REQUEST: [key_name(Sender)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")]): [msg]" - for(var/client/X in GLOB.admins) - if(check_rights(R_EVENT,0,X.mob)) - to_chat(X, msg) - to_chat(X, "The nuke code is [nuke_code].") - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' +/mob/living/verb/pray(msg as text) + set category = "IC" + set name = "Pray" + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + if(!msg) + return + + if(usr.client) + if(usr.client.prefs.muted & MUTE_PRAY) + to_chat(usr, "You cannot pray (muted).") + return + if(client.handle_spam_prevention(msg, MUTE_PRAY, OOC_COOLDOWN)) + return + + var/image/cross = image('icons/obj/storage.dmi',"bible") + var/font_color = "purple" + var/prayer_type = "PRAYER" + var/deity + if(usr.job == "Chaplain") + if(SSticker && SSticker.Bible_deity_name) + deity = SSticker.Bible_deity_name + cross = image('icons/obj/storage.dmi',"kingyellow") + font_color = "blue" + prayer_type = "CHAPLAIN PRAYER" + else if(iscultist(usr)) + cross = image('icons/obj/storage.dmi',"tome") + font_color = "red" + prayer_type = "CULTIST PRAYER" + deity = SSticker.cultdat.entity_name + + log_say("(PRAYER) [msg]", usr) + msg = "[bicon(cross)][prayer_type][deity ? " (to [deity])" : ""][mind && mind.isholy ? " (blessings: [mind.num_blessed])" : ""]: [key_name(src, 1)] ([ADMIN_QUE(src,"?")]) ([ADMIN_PP(src,"PP")]) ([ADMIN_VV(src,"VV")]) ([ADMIN_TP(src,"TP")]) ([ADMIN_SM(src,"SM")]) ([admin_jump_link(src)]) ([ADMIN_SC(src,"SC")]) (BLESS) (SMITE): [msg]" + + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + to_chat(usr, "Your prayers have been received by the gods.") + + feedback_add_details("admin_verb","PR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/proc/Centcomm_announce(var/text , var/mob/Sender) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "CENTCOMM: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")])): [msg]" + for(var/client/X in GLOB.admins) + if(R_EVENT & X.holder.rights) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/Syndicate_announce(var/text , var/mob/Sender) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "SYNDICATE: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_SYNDICATE_REPLY(Sender,"RPLY")]): [msg]" + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/HONK_announce(var/text , var/mob/Sender) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "HONK: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RPLY): [msg]" + for(var/client/X in GLOB.admins) + if(R_EVENT & X.holder.rights) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/ERT_Announce(var/text , var/mob/Sender, var/repeat_warning) + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "ERT REQUEST: [key_name(Sender, 1)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) (RESPOND): [msg]" + if(repeat_warning) + msg += "
    WARNING: ERT request has gone 5 minutes with no reply!" + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + +/proc/Nuke_request(text , mob/Sender) + var/nuke_code = get_nuke_code() + var/msg = sanitize(copytext(text, 1, MAX_MESSAGE_LEN)) + msg = "NUKE CODE REQUEST: [key_name(Sender)] ([ADMIN_PP(Sender,"PP")]) ([ADMIN_VV(Sender,"VV")]) ([ADMIN_TP(Sender,"TP")]) ([ADMIN_SM(Sender,"SM")]) ([admin_jump_link(Sender)]) ([ADMIN_BSA(Sender,"BSA")]) ([ADMIN_CENTCOM_REPLY(Sender,"RPLY")]): [msg]" + for(var/client/X in GLOB.admins) + if(check_rights(R_EVENT,0,X.mob)) + to_chat(X, msg) + to_chat(X, "The nuke code is [nuke_code].") + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index a347f65590b1..a8136655d8fc 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -1,1159 +1,1159 @@ -/client/proc/cmd_admin_drop_everything(mob/M as mob in GLOB.mob_list) - set category = null - set name = "Drop Everything" - - if(!check_rights(R_DEBUG|R_ADMIN)) - return - - var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") - if(confirm != "Yes") - return - - for(var/obj/item/W in M) - M.unEquip(W) - - log_admin("[key_name(usr)] made [key_name(M)] drop everything!") - message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) - feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_prison(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Prison" - - if(!check_rights(R_ADMIN)) - return - - if(ismob(M)) - if(istype(M, /mob/living/silicon/ai)) - alert("The AI can't be sent to prison you jerk!", null, null, null, null, null) - return - //strip their stuff before they teleport into a cell :downs: - for(var/obj/item/W in M) - M.unEquip(W) - //teleport person to cell - M.Paralyse(5) - sleep(5) //so they black out before warping - M.loc = pick(prisonwarp) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/prisoner = M - prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) - prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) - spawn(50) - to_chat(M, "You have been sent to the prison station!") - log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") - message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) - feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_subtle_message(mob/M as mob in GLOB.mob_list) - set category = "Event" - set name = "Subtle Message" - - if(!ismob(M)) - return - - if(!check_rights(R_SERVER|R_EVENT)) - return - - var/msg = clean_input("Message:", text("Subtle PM to [M.key]")) - - if(!msg) - return - - msg = admin_pencode_to_html(msg) - - if(usr) - if(usr.client) - if(usr.client.holder) - to_chat(M, "You hear a voice in your head... [msg]") - - log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") - message_admins("SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]", 1) - feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_mentor_check_new_players() //Allows mentors / admins to determine who the newer players are. - set category = "Admin" - set name = "Check new Players" - - if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) - return - - var/age = alert(src, "Age check", "Show accounts yonger then _____ days","7", "30" , "All") - - if(age == "All") - age = 9999999 - else - age = text2num(age) - - var/missing_ages = 0 - var/msg = "" - for(var/client/C in GLOB.clients) - if(C.player_age == "Requires database") - missing_ages = 1 - continue - if(C.player_age < age) - if(check_rights(R_ADMIN, 0)) - msg += "[key_name_admin(C.mob)]: [C.player_age] days old
    " - else - msg += "[key_name_mentor(C.mob)]: [C.player_age] days old
    " - - if(missing_ages) - to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present") - - if(msg != "") - src << browse(msg, "window=Player_age_check") - else - to_chat(src, "No matches for that age range found.") - - -/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE - set category = "Event" - set name = "Global Narrate" - - if(!check_rights(R_SERVER|R_EVENT)) - return - - var/msg = clean_input("Message:", text("Enter the text you wish to appear to everyone:")) - - if(!msg) - return - msg = admin_pencode_to_html(msg) - to_chat(world, "[msg]") - log_admin("GlobalNarrate: [key_name(usr)] : [msg]") - message_admins("GlobalNarrate: [key_name_admin(usr)]: [msg]
    ", 1) - feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE - set category = "Event" - set name = "Direct Narrate" - - if(!check_rights(R_SERVER|R_EVENT)) - return - - if(!M) - M = input("Direct narrate to who?", "Active Players") as null|anything in get_mob_with_client_list() - - if(!M) - return - - var/msg = clean_input("Message:", text("Enter the text you wish to appear to your target:")) - - if( !msg ) - return - msg = admin_pencode_to_html(msg) - - to_chat(M, msg) - log_admin("DirectNarrate: [key_name(usr)] to ([key_name(M)]): [msg]") - message_admins("DirectNarrate: [key_name_admin(usr)] to ([key_name_admin(M)]): [msg]
    ", 1) - feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - - - -/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list) - set category = "Event" - set name = "Headset Message" - - admin_headset_message(M) - -/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) - var/mob/living/carbon/human/H = M - - if(!check_rights(R_ADMIN)) - return - - if(!istype(H)) - to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") - return - if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) - to_chat(usr, "The person you are trying to contact is not wearing a headset") - return - - if(!sender) - sender = input("Who is the message from?", "Sender") as null|anything in list("Centcomm", "Syndicate") - if(!sender) - return - - message_admins("[key_name_admin(src)] has started answering [key_name_admin(H)]'s [sender] request.") - var/input = clean_input("Please enter a message to reply to [key_name(H)] via their headset.", "Outgoing message from [sender]", "") - if(!input) - message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.") - return - - log_admin("[key_name(src)] replied to [key_name(H)]'s [sender] message with the message [input].") - message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"") - to_chat(H, "You hear something crackle in your ears for a moment before a voice speaks. \"Please stand by for a message from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]. Message ends.\"") - - - - -/client/proc/cmd_admin_godmode(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Godmode" - - if(!check_rights(R_ADMIN)) - return - - M.status_flags ^= GODMODE - to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") - - log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") - message_admins("[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]", 1) - feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) - if(automute) - if(!config.automute_on) - return - else - if(!usr || !usr.client) - return - if(!check_rights(R_ADMIN|R_MOD)) - to_chat(usr, "Error: cmd_admin_mute: You don't have permission to do this.") - return - if(!M.client) - to_chat(usr, "Error: cmd_admin_mute: This mob doesn't have a client tied to it.") - if(!M.client) - return - - var/muteunmute - var/mute_string - - switch(mute_type) - if(MUTE_IC) mute_string = "IC (say and emote)" - if(MUTE_OOC) mute_string = "OOC" - if(MUTE_PRAY) mute_string = "pray" - if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" - if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" - if(MUTE_ALL) mute_string = "everything" - else return - - if(automute) - muteunmute = "auto-muted" - M.client.prefs.muted |= mute_type - log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") - message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") - feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - - if(M.client.prefs.muted & mute_type) - muteunmute = "unmuted" - M.client.prefs.muted &= ~mute_type - else - muteunmute = "muted" - M.client.prefs.muted |= mute_type - - log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") - message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) - to_chat(M, "You have been [muteunmute] from [mute_string].") - feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_add_random_ai_law() - set category = "Event" - set name = "Add Random AI Law" - - if(!check_rights(R_EVENT)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") return - log_admin("[key_name(src)] has added a random AI law.") - message_admins("[key_name_admin(src)] has added a random AI law.") - - var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") - var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) - - new /datum/event/ion_storm(0, announce_ion_laws) - feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_antagHUD_use() - set category = "Server" - set name = "Toggle antagHUD usage" - set desc = "Toggles antagHUD usage for observers" - - if(!check_rights(R_SERVER)) - return - - var/action="" - if(config.antag_hud_allowed) - for(var/mob/dead/observer/g in get_ghosts()) - if(!g.client.holder) //Remove the verb from non-admin ghosts - g.verbs -= /mob/dead/observer/verb/toggle_antagHUD - if(g.antagHUD) - g.antagHUD = 0 // Disable it on those that have it enabled - g.has_enabled_antagHUD = 2 // We'll allow them to respawn - to_chat(g, "The Administrator has disabled AntagHUD ") - config.antag_hud_allowed = 0 - to_chat(src, "AntagHUD usage has been disabled") - action = "disabled" - else - for(var/mob/dead/observer/g in get_ghosts()) - if(!g.client.holder) // Add the verb back for all non-admin ghosts - g.verbs += /mob/dead/observer/verb/toggle_antagHUD - to_chat(g, "The Administrator has enabled AntagHUD ")// Notify all observers they can now use AntagHUD - - config.antag_hud_allowed = 1 - action = "enabled" - to_chat(src, "AntagHUD usage has been enabled") - - - log_admin("[key_name(usr)] has [action] antagHUD usage for observers") - message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) - -/client/proc/toggle_antagHUD_restrictions() - set category = "Server" - set name = "Toggle antagHUD Restrictions" - set desc = "Restricts players that have used antagHUD from being able to join this round." - - if(!check_rights(R_SERVER)) - return - - var/action="" - if(config.antag_hud_restricted) - for(var/mob/dead/observer/g in get_ghosts()) - to_chat(g, "The administrator has lifted restrictions on joining the round if you use AntagHUD") - action = "lifted restrictions" - config.antag_hud_restricted = 0 - to_chat(src, "AntagHUD restrictions have been lifted") - else - for(var/mob/dead/observer/g in get_ghosts()) - to_chat(g, "The administrator has placed restrictions on joining the round if you use AntagHUD") - to_chat(g, "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ") - g.antagHUD = 0 - g.has_enabled_antagHUD = 0 - action = "placed restrictions" - config.antag_hud_restricted = 1 - to_chat(src, "AntagHUD restrictions have been enabled") - - log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") - message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) - -/* -If a guy was gibbed and you want to revive him, this is a good way to do so. -Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. -Traitors and the like can also be revived with the previous role mostly intact. -/N */ -/client/proc/respawn_character() - set category = "Event" - set name = "Respawn Character" - set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into." - - if(!check_rights(R_SPAWN)) - return - - var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", "")) - if(!input) - return - - var/mob/dead/observer/G_found - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.ckey == input) - G_found = G - break - - if(!G_found)//If a ghost was not found. - to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.") - return - - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - //Check if they were an alien - if(G_found.mind.assigned_role=="Alien") - if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") - var/turf/T - if(xeno_spawn.len) T = pick(xeno_spawn) - else T = pick(latejoin) - - var/mob/living/carbon/alien/new_xeno - switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were. - if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T) - if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T) - if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(T) - if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen(T) - else//If we don't know what special role they have, for whatever reason, or they're a larva. - create_xeno(G_found.ckey) - return - - //Now to give them their mind back. - G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use - new_xeno.key = G_found.key - to_chat(new_xeno, "You have been fully respawned. Enjoy the game.") - message_admins("[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno.", 1) - return //all done. The ghost is auto-deleted - - var/mob/living/carbon/human/new_character = new(pick(latejoin))//The mob being spawned. - - var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. - if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something - /*Try and locate a record for the person being respawned through data_core. - This isn't an exact science but it does the trick more often than not.*/ - var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") - for(var/datum/data/record/t in data_core.locked) - if(t.fields["id"]==id) - record_found = t//We shall now reference the record. - break - - if(record_found)//If they have a record we can determine a few things. - new_character.real_name = record_found.fields["name"] - new_character.change_gender(record_found.fields["sex"]) - new_character.age = record_found.fields["age"] - new_character.dna.blood_type = record_found.fields["blood_type"] - else - new_character.change_gender(pick(MALE,FEMALE)) - var/datum/preferences/A = new() - A.real_name = G_found.real_name - A.copy_to(new_character) - - if(!new_character.real_name) - new_character.real_name = random_name(new_character.gender) - new_character.name = new_character.real_name - - if(G_found.mind && !G_found.mind.active) - G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use - new_character.mind.special_verbs = list() - else - new_character.mind_initialize() - if(!new_character.mind.assigned_role) new_character.mind.assigned_role = "Civilian"//If they somehow got a null assigned role. - - //DNA - if(record_found)//Pull up their name from database records if they did have a mind. - new_character.dna = new()//Let's first give them a new DNA. - new_character.dna.unique_enzymes = record_found.fields["b_dna"]//Enzymes are based on real name but we'll use the record for conformity. - - // I HATE BYOND. HATE. HATE. - N3X - var/list/newSE= record_found.fields["enzymes"] - var/list/newUI = record_found.fields["identity"] - new_character.dna.SE = newSE.Copy() //This is the default of enzymes so I think it's safe to go with. - new_character.dna.UpdateSE() - new_character.UpdateAppearance(newUI.Copy())//Now we configure their appearance based on their unique identity, same as with a DNA machine or somesuch. - else//If they have no records, we just do a random DNA for them, based on their random appearance/savefile. - new_character.dna.ready_dna(new_character) - - new_character.key = G_found.key - - /* - The code below functions with the assumption that the mob is already a traitor if they have a special role. - So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role. - If they don't have a mind, they obviously don't have a special role. - */ - - //Now for special roles and equipment. - switch(new_character.mind.special_role) - if("traitor") - if(new_character.mind.has_antag_datum(/datum/antagonist/traitor)) - var/datum/antagonist/traitor/T = new_character.mind.has_antag_datum(/datum/antagonist/traitor) - T.equip_traitor(src) - else - new_character.mind.add_antag_datum(/datum/antagonist/traitor) - if("Wizard") - new_character.loc = pick(wizardstart) - //ticker.mode.learn_basic_spells(new_character) - SSticker.mode.equip_wizard(new_character) - if("Syndicate") - var/obj/effect/landmark/synd_spawn = locate("landmark*Syndicate-Spawn") - if(synd_spawn) - new_character.loc = get_turf(synd_spawn) - call(/datum/game_mode/proc/equip_syndicate)(new_character) - - if("Death Commando")//Leaves them at late-join spawn. - new_character.equip_death_commando() - new_character.internal = new_character.s_store - new_character.update_action_buttons_icon() - else//They may also be a cyborg or AI. - switch(new_character.mind.assigned_role) - if("Cyborg")//More rigging to make em' work and check if they're traitor. - new_character = new_character.Robotize() - if(new_character.mind.special_role=="traitor") - new_character.mind.add_antag_datum(/datum/antagonist/traitor) - if("AI") - new_character = new_character.AIize() - var/mob/living/silicon/ai/ai_character = new_character - ai_character.moveToAILandmark() - if(new_character.mind.special_role=="traitor") - new_character.mind.add_antag_datum(/datum/antagonist/traitor) - //Add aliens. - else - SSjobs.AssignRank(new_character, new_character.mind.assigned_role, 0) - SSjobs.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them. - - //Announces the character on all the systems, based on the record. - if(!issilicon(new_character))//If they are not a cyborg/AI. - if(!record_found && new_character.mind.assigned_role != new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. Offstation special characters announced anyway. - //Power to the user! - if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") - data_core.manifest_inject(new_character) - - if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") - call(/mob/new_player/proc/AnnounceArrival)(new_character, new_character.mind.assigned_role) - - message_admins("[key_name_admin(usr)] has respawned [key_name_admin(G_found)] as [new_character.real_name].", 1) - - to_chat(new_character, "You have been fully respawned. Enjoy the game.") - - feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return new_character - -//I use this proc for respawn character too. /N -/proc/create_xeno(ckey) - if(!ckey) - var/list/candidates = list() - for(var/mob/M in GLOB.player_list) - if(M.stat != DEAD) continue //we are not dead! - if(!(ROLE_ALIEN in M.client.prefs.be_special)) continue //we don't want to be an alium - if(jobban_isbanned(M, "alien") || jobban_isbanned(M, "Syndicate")) continue //we are jobbanned - if(M.client.is_afk()) continue //we are afk - if(M.mind && M.mind.current && M.mind.current.stat != DEAD) continue //we have a live body we are tied to - candidates += M.ckey - if(candidates.len) - ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates - else - to_chat(usr, "Error: create_xeno(): no suitable candidates.") - if(!istext(ckey)) return 0 - - var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Hunter","Sentinel","Drone","Larva") - var/obj/effect/landmark/spawn_here = xeno_spawn.len ? pick(xeno_spawn) : pick(latejoin) - var/mob/living/carbon/alien/new_xeno - switch(alien_caste) - if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen/large(spawn_here) - if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here) - if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here) - if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here) - if("Larva") new_xeno = new /mob/living/carbon/alien/larva(spawn_here) - else return 0 - - new_xeno.ckey = ckey - message_admins("[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste].", 1) - return 1 - - -/client/proc/get_ghosts(var/notify = 0,var/what = 2) - // what = 1, return ghosts ass list. - // what = 2, return mob list - - var/list/mobs = list() - var/list/ghosts = list() - var/list/sortmob = sortAtom(GLOB.mob_list) // get the mob list. - /var/any=0 - for(var/mob/dead/observer/M in sortmob) - mobs.Add(M) //filter it where it's only ghosts - any = 1 //if no ghosts show up, any will just be 0 - if(!any) - if(notify) - to_chat(src, "There doesn't appear to be any ghosts for you to select.") - return - - for(var/mob/M in mobs) - var/name = M.name - ghosts[name] = M //get the name of the mob for the popup list - if(what==1) - return ghosts - else - return mobs - -/client/proc/cmd_admin_add_freeform_ai_law() - set category = "Event" - set name = "Add Custom AI law" - - if(!check_rights(R_EVENT)) - return - - var/input = clean_input("Please enter anything you want the AI to do. Anything. Serious.", "What?", "") - if(!input) - return - - log_admin("Admin [key_name(usr)] has added a new AI law - [input]") - message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]") - - var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") - var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) - - new /datum/event/ion_storm(0, announce_ion_laws, input) - - feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list) - set category = "Event" - set name = "Rejuvenate" - - if(!check_rights(R_REJUVINATE)) - return - - if(!mob) - return - if(!istype(M)) - alert("Cannot revive a ghost") - return - M.revive() - - log_admin("[key_name(usr)] healed / revived [key_name(M)]") - message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!", 1) - feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_create_centcom_report() - set category = "Event" - set name = "Create Communications Report" - - if(!check_rights(R_SERVER|R_EVENT)) - return - -//the stuff on the list is |"report type" = "report title"|, if that makes any sense - var/list/MsgType = list("Central Command Report" = "Nanotrasen Update", - "Syndicate Communique" = "Syndicate Message", - "Space Wizard Federation Message" = "Sorcerous Message", - "Enemy Communications" = "Unknown Message", - "Custom" = "Cryptic Message") - - var/list/MsgSound = list("Beep" = 'sound/misc/notice2.ogg', - "Enemy Communications Intercepted" = 'sound/AI/intercept2.ogg', - "New Command Report Created" = 'sound/AI/commandreport.ogg') - - var/type = input(usr, "Pick a type of report to send", "Report Type", "") as anything in MsgType - - if(type == "Custom") - type = clean_input("What would you like the report type to be?", "Report Type", "Encrypted Transmission") - - var/customname = input(usr, "Pick a title for the report.", "Title", MsgType[type]) as text|null - if(!customname) - return - var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What's the message?") as message|null - if(!input) - return - - switch(alert("Should this be announced to the general population?",,"Yes","No", "Cancel")) - if("Yes") - var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound - - command_announcement.Announce(input, customname, MsgSound[beepsound], , , type) - print_command_report(input, "[command_name()] Update") - if("No") - //same thing as the blob stuff - it's not public, so it's classified, dammit - command_announcer.autosay("A classified message has been printed out at all communication consoles."); - print_command_report(input, "Classified [command_name()] Update") - else - return - - log_admin("[key_name(src)] has created a communications report: [input]") - message_admins("[key_name_admin(src)] has created a communications report", 1) - feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) - set category = "Admin" - set name = "Delete" - - if(!check_rights(R_ADMIN)) - return - - admin_delete(A) - -/client/proc/admin_delete(datum/D) - if(istype(D) && !D.can_vv_delete()) - to_chat(src, "[D] rejected your deletion") - return - var/atom/A = D - var/coords = istype(A) ? "at ([A.x], [A.y], [A.z])" : "" - if(alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") - log_admin("[key_name(usr)] deleted [D] [coords]") - message_admins("[key_name_admin(usr)] deleted [D] [coords]", 1) - feedback_add_details("admin_verb","DEL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(isturf(D)) - var/turf/T = D - T.ChangeTurf(T.baseturf) - else - qdel(D) - -/client/proc/cmd_admin_list_open_jobs() - set category = "Admin" - set name = "List free slots" - - if(!check_rights(R_ADMIN)) - return - - if(SSjobs) - var/currentpositiontally - var/totalpositiontally - to_chat(src, "Job Name: Filled job slot / Total job slots (Free job slots)") - for(var/datum/job/job in SSjobs.occupations) - to_chat(src, "[job.title]: [job.current_positions] / \ - [job.total_positions == -1 ? "UNLIMITED" : job.total_positions] \ - ([job.total_positions == -1 ? "UNLIMITED" : job.total_positions - job.current_positions])") - if(job.total_positions != -1) // Only count position that isn't unlimited - currentpositiontally += job.current_positions - totalpositiontally += job.total_positions - to_chat(src, "Currently filled job slots (Excluding unlimited): [currentpositiontally] / [totalpositiontally] ([totalpositiontally - currentpositiontally])") - feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in view()) - set category = "Event" - set name = "Explosion" - - if(!check_rights(R_DEBUG|R_EVENT)) - return - - var/devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null - if(devastation == null) return - var/heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null - if(heavy == null) return - var/light = input("Range of light impact. -1 to none", text("Input")) as num|null - if(light == null) return - var/flash = input("Range of flash. -1 to none", text("Input")) as num|null - if(flash == null) return - var/flames = input("Range of flames. -1 to none", text("Input")) as num|null - if(flames == null) return - - if((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1) || (flames != -1)) - if((devastation > 20) || (heavy > 20) || (light > 20) || (flames > 20)) - if(alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No") - return - - explosion(O, devastation, heavy, light, flash, null, null,flames) - log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") - feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - else - return - -/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in view()) - set category = "Event" - set name = "EM Pulse" - - if(!check_rights(R_DEBUG|R_EVENT)) - return - - var/heavy = input("Range of heavy pulse.", text("Input")) as num|null - if(heavy == null) return - var/light = input("Range of light pulse.", text("Input")) as num|null - if(light == null) return - - if(heavy || light) - - empulse(O, heavy, light) - log_admin("[key_name(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])") - message_admins("[key_name_admin(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])", 1) - feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - return - else - return - -/client/proc/cmd_admin_gib(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Gib" - - if(!check_rights(R_ADMIN|R_EVENT)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") return - //Due to the delay here its easy for something to have happened to the mob - if(!M) return - - log_admin("[key_name(usr)] has gibbed [key_name(M)]") - message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) - - if(istype(M, /mob/dead/observer)) - gibs(M.loc) - return - - M.gib() - feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_gib_self() - set name = "Gibself" - set category = "Event" - - if(!check_rights(R_ADMIN|R_EVENT)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm == "Yes") - if(istype(mob, /mob/dead/observer)) // so they don't spam gibs everywhere - return - else - mob.gib() - - log_admin("[key_name(usr)] used gibself.") - message_admins("[key_name_admin(usr)] used gibself.", 1) - feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_check_contents(mob/living/M as mob in GLOB.mob_list) - set name = "Check Contents" - set category = null - - if(!check_rights(R_ADMIN)) - return - - var/list/L = M.get_contents() - for(var/t in L) - to_chat(usr, "[t]") - feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_view_range() - set category = "Admin" - set name = "Change View Range" - set desc = "switches between 1x and custom views" - - if(!check_rights(R_ADMIN)) - return - - if(view == world.view) - view = input("Select view range:", "View Range", world.view) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) - else - view = world.view - - log_admin("[key_name(usr)] changed their view range to [view].") - //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI - - feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/admin_call_shuttle() - - set category = "Admin" - set name = "Call Shuttle" - - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - - if(!check_rights(R_ADMIN)) - return - - var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") - if(confirm != "Yes") return - - if(alert("Set Shuttle Recallable (Select Yes unless you know what this does)", "Recallable?", "Yes", "No") == "Yes") - SSshuttle.emergency.canRecall = TRUE - else - SSshuttle.emergency.canRecall = FALSE - - SSshuttle.emergency.request() - - feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-called the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.") - return - -/client/proc/admin_cancel_shuttle() - set category = "Admin" - set name = "Cancel Shuttle" - - if(!check_rights(R_ADMIN)) - return - if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") return - - if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) - return - - if(SSshuttle.emergency.canRecall == FALSE) - if(alert("Shuttle is currently set to be nonrecallable. Recalling may break things. Respect Recall Status?", "Override Recall Status?", "Yes", "No") == "Yes") - return - else - var/keepStatus = alert("Maintain recall status on future shuttle calls?", "Maintain Status?", "Yes", "No") == "Yes" //Keeps or drops recallability - SSshuttle.emergency.canRecall = TRUE // must be true for cancel proc to work - SSshuttle.emergency.cancel() - if(keepStatus) - SSshuttle.emergency.canRecall = FALSE // restores original status - else - SSshuttle.emergency.cancel() - - feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") - message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.") - return - -/client/proc/admin_deny_shuttle() - set category = "Admin" - set name = "Toggle Deny Shuttle" - - if(!SSticker) - return - - if(!check_rights(R_ADMIN)) - return - - if(SSshuttle) - SSshuttle.emergencyNoEscape = !SSshuttle.emergencyNoEscape - - log_admin("[key_name(src)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") - message_admins("[key_name_admin(usr)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") - -/client/proc/cmd_admin_attack_log(mob/M as mob in GLOB.mob_list) - set category = "Admin" - set name = "Attack Log" - - if(!check_rights(R_ADMIN)) - return - - to_chat(usr, text("Attack Log for []", mob)) - for(var/t in M.attack_log) - to_chat(usr, t) - feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/everyone_random() - set category = "Event" - set name = "Make Everyone Random" - set desc = "Make everyone have a random appearance. You can only use this before rounds!" - - if(!check_rights(R_SERVER|R_EVENT)) - return - - if(SSticker && SSticker.mode) - to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") - return - - if(SSticker.random_players) - SSticker.random_players = 0 - message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) - to_chat(usr, "Disabled.") - return - - - var/notifyplayers = alert(src, "Do you want to notify the players?", "Options", "Yes", "No", "Cancel") - if(notifyplayers == "Cancel") - return - - log_admin("Admin [key_name(src)] has forced the players to have random appearances.") - message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) - - if(notifyplayers == "Yes") - to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") - - to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") - - SSticker.random_players = 1 - feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_random_events() - set category = "Event" - set name = "Toggle random events on/off" - - set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" - if(!check_rights(R_SERVER|R_EVENT)) - return - - if(!config.allow_random_events) - config.allow_random_events = 1 - to_chat(usr, "Random events enabled") - message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) - else - config.allow_random_events = 0 - to_chat(usr, "Random events disabled") - message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) - feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/reset_all_tcs() - set category = "Admin" - set name = "Reset NTTC Configuration" - set desc = "Resets NTTC to the default configuration." - - if(!check_rights(R_ADMIN)) - return - - var/confirm = alert(src, "You sure you want to reset NTTC?", "Confirm", "Yes", "No") - if(confirm != "Yes") - return - - GLOB.nttc_config.reset() - log_admin("[key_name(usr)] reset NTTC scripts.") - message_admins("[key_name_admin(usr)] reset NTTC scripts.") - feedback_add_details("admin_verb","RAT2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/list_ssds_afks() - set category = "Admin" - set name = "List SSDs and AFKs" - set desc = "Lists SSD and AFK players" - - if(!check_rights(R_ADMIN)) - return - - /* ======== SSD Section ========= */ - var/msg = "SSD & AFK Report" - msg += "SSD Players:
    " - msg += "" - var/mins_ssd - var/job_string - var/key_string - var/role_string - var/obj_count = 0 - var/obj_string = "" - for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - if(!isLivingSSD(H)) - continue - mins_ssd = round((world.time - H.last_logout) / 600) - if(H.job) - job_string = H.job - else - job_string = "-" - key_string = H.key - if(job_string in command_positions) - job_string = "" + job_string + "" - role_string = "-" - obj_count = 0 - obj_string = "" - if(H.mind) - if(H.mind.special_role) - role_string = "[H.mind.special_role]" - if(!H.key && H.mind.key) - key_string = H.mind.key - for(var/datum/objective/O in GLOB.all_objectives) - if(O.target == H.mind) - obj_count++ - if(obj_count > 0) - obj_string = "
    Obj Target" - msg += "" - msg += "" - if(istype(H.loc, /obj/machinery/cryopod)) - msg += "" - else - msg += "" - msg += "" - msg += "
    KeyReal NameJobMins SSDSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_ssd][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo

    " - - /* ======== AFK Section ========= */ - msg += "AFK Players:
    " - msg += "" - var/mins_afk - for(var/mob/living/carbon/human/H in GLOB.living_mob_list) - if(H.client == null || H.stat == DEAD) // No clientless or dead - continue - mins_afk = round(H.client.inactivity / 600) - if(mins_afk < config.list_afk_minimum) - continue - if(H.job) - job_string = H.job - else - job_string = "-" - key_string = H.key - if(job_string in command_positions) - job_string = "" + job_string + "" - role_string = "-" - obj_count = 0 - obj_string = "" - if(H.mind) - if(H.mind.special_role) - role_string = "[H.mind.special_role]" - if(!H.key && H.mind.key) - key_string = H.mind.key - for(var/datum/objective/O in GLOB.all_objectives) - if(O.target == H.mind) - obj_count++ - if(obj_count > 0) - obj_string = "
    Obj Target" - msg += "" - msg += "" - if(istype(H.loc, /obj/machinery/cryopod)) - msg += "" - else - msg += "" - msg += "" - msg += "
    KeyReal NameJobMins AFKSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_afk][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo
    " - src << browse(msg, "window=Player_ssd_afk_check;size=600x300") - -/client/proc/toggle_ert_calling() - set category = "Event" - set name = "Toggle ERT" - - set desc = "Toggle the station's ability to call a response team." - if(!check_rights(R_EVENT)) - return - - if(SSticker.mode.ert_disabled) - SSticker.mode.ert_disabled = 0 - to_chat(usr, "ERT has been Enabled.") - log_admin("Admin [key_name(src)] has enabled ERT calling.") - message_admins("Admin [key_name_admin(usr)] has enabled ERT calling.", 1) - else - SSticker.mode.ert_disabled = 1 - to_chat(usr, "ERT has been Disabled.") - log_admin("Admin [key_name(src)] has disabled ERT calling.") - message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1) - -/client/proc/show_tip() - set category = "Admin" - set name = "Show Custom Tip" - set desc = "Sends a tip (that you specify) to all players. After all \ - you're the experienced player here." - - if(!check_rights(R_ADMIN)) - return - - var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null - if(!input) - return - - if(!SSticker) - return - - SSticker.selected_tip = input - - // If we've already tipped, then send it straight away. - if(SSticker.tipped) - SSticker.send_tip_of_the_round() - - message_admins("[key_name_admin(usr)] sent a Tip of the round.") - log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") - -/client/proc/modify_goals() - set category = "Event" - set name = "Modify Station Goals" - - if(!check_rights(R_EVENT)) - return - - holder.modify_goals() - -/datum/admins/proc/modify_goals() - if(!SSticker || !SSticker.mode) - to_chat(usr, "This verb can only be used if the round has started.") - return - - var/dat = "" - for(var/datum/station_goal/S in SSticker.mode.station_goals) - dat += "[S.name] - Announce | Remove
    " - dat += "
    Add New Goal" - usr << browse(dat, "window=goals;size=400x400") - -/// Allow admin to add or remove traits of datum -/datum/admins/proc/modify_traits(datum/D) - if(!D) - return - - var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove") - if(!add_or_remove) - return - var/list/availible_traits = list() - - switch(add_or_remove) - if("Add") - for(var/key in GLOB.traits_by_type) - if(istype(D,key)) - availible_traits += GLOB.traits_by_type[key] - if("Remove") - if(!GLOB.trait_name_map) - GLOB.trait_name_map = generate_trait_name_map() - for(var/trait in D.status_traits) - var/name = GLOB.trait_name_map[trait] || trait - availible_traits[name] = trait - - var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in availible_traits - if(!chosen_trait) - return - chosen_trait = availible_traits[chosen_trait] - - var/source = "adminabuse" - switch(add_or_remove) - if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it. - ADD_TRAIT(D, chosen_trait, source) - if("Remove") - var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific") - if(!specific) - return - switch(specific) - if("All") - source = null - if("Specific") - source = input("Source to be removed","Trait Remove/Add") as null|anything in D.status_traits[chosen_trait] - if(!source) - return - REMOVE_TRAIT(D, chosen_trait, source) \ No newline at end of file +/client/proc/cmd_admin_drop_everything(mob/M as mob in GLOB.mob_list) + set category = null + set name = "Drop Everything" + + if(!check_rights(R_DEBUG|R_ADMIN)) + return + + var/confirm = alert(src, "Make [M] drop everything?", "Message", "Yes", "No") + if(confirm != "Yes") + return + + for(var/obj/item/W in M) + M.unEquip(W) + + log_admin("[key_name(usr)] made [key_name(M)] drop everything!") + message_admins("[key_name_admin(usr)] made [key_name_admin(M)] drop everything!", 1) + feedback_add_details("admin_verb","DEVR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_prison(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Prison" + + if(!check_rights(R_ADMIN)) + return + + if(ismob(M)) + if(istype(M, /mob/living/silicon/ai)) + alert("The AI can't be sent to prison you jerk!", null, null, null, null, null) + return + //strip their stuff before they teleport into a cell :downs: + for(var/obj/item/W in M) + M.unEquip(W) + //teleport person to cell + M.Paralyse(5) + sleep(5) //so they black out before warping + M.loc = pick(GLOB.prisonwarp) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/prisoner = M + prisoner.equip_to_slot_or_del(new /obj/item/clothing/under/color/orange(prisoner), slot_w_uniform) + prisoner.equip_to_slot_or_del(new /obj/item/clothing/shoes/orange(prisoner), slot_shoes) + spawn(50) + to_chat(M, "You have been sent to the prison station!") + log_admin("[key_name(usr)] sent [key_name(M)] to the prison station.") + message_admins("[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.", 1) + feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_subtle_message(mob/M as mob in GLOB.mob_list) + set category = "Event" + set name = "Subtle Message" + + if(!ismob(M)) + return + + if(!check_rights(R_SERVER|R_EVENT)) + return + + var/msg = clean_input("Message:", text("Subtle PM to [M.key]")) + + if(!msg) + return + + msg = admin_pencode_to_html(msg) + + if(usr) + if(usr.client) + if(usr.client.holder) + to_chat(M, "You hear a voice in your head... [msg]") + + log_admin("SubtlePM: [key_name(usr)] -> [key_name(M)] : [msg]") + message_admins("SubtleMessage: [key_name_admin(usr)] -> [key_name_admin(M)] : [msg]", 1) + feedback_add_details("admin_verb","SMS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_mentor_check_new_players() //Allows mentors / admins to determine who the newer players are. + set category = "Admin" + set name = "Check new Players" + + if(!check_rights(R_MENTOR|R_MOD|R_ADMIN)) + return + + var/age = alert(src, "Age check", "Show accounts yonger then _____ days","7", "30" , "All") + + if(age == "All") + age = 9999999 + else + age = text2num(age) + + var/missing_ages = 0 + var/msg = "" + for(var/client/C in GLOB.clients) + if(C.player_age == "Requires database") + missing_ages = 1 + continue + if(C.player_age < age) + if(check_rights(R_ADMIN, 0)) + msg += "[key_name_admin(C.mob)]: [C.player_age] days old
    " + else + msg += "[key_name_mentor(C.mob)]: [C.player_age] days old
    " + + if(missing_ages) + to_chat(src, "Some accounts did not have proper ages set in their clients. This function requires database to be present") + + if(msg != "") + src << browse(msg, "window=Player_age_check") + else + to_chat(src, "No matches for that age range found.") + + +/client/proc/cmd_admin_world_narrate() // Allows administrators to fluff events a little easier -- TLE + set category = "Event" + set name = "Global Narrate" + + if(!check_rights(R_SERVER|R_EVENT)) + return + + var/msg = clean_input("Message:", text("Enter the text you wish to appear to everyone:")) + + if(!msg) + return + msg = admin_pencode_to_html(msg) + to_chat(world, "[msg]") + log_admin("GlobalNarrate: [key_name(usr)] : [msg]") + message_admins("GlobalNarrate: [key_name_admin(usr)]: [msg]
    ", 1) + feedback_add_details("admin_verb","GLN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_direct_narrate(var/mob/M) // Targetted narrate -- TLE + set category = "Event" + set name = "Direct Narrate" + + if(!check_rights(R_SERVER|R_EVENT)) + return + + if(!M) + M = input("Direct narrate to who?", "Active Players") as null|anything in get_mob_with_client_list() + + if(!M) + return + + var/msg = clean_input("Message:", text("Enter the text you wish to appear to your target:")) + + if( !msg ) + return + msg = admin_pencode_to_html(msg) + + to_chat(M, msg) + log_admin("DirectNarrate: [key_name(usr)] to ([key_name(M)]): [msg]") + message_admins("DirectNarrate: [key_name_admin(usr)] to ([key_name_admin(M)]): [msg]
    ", 1) + feedback_add_details("admin_verb","DIRN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + + + +/client/proc/cmd_admin_headset_message(mob/M in GLOB.mob_list) + set category = "Event" + set name = "Headset Message" + + admin_headset_message(M) + +/client/proc/admin_headset_message(mob/M in GLOB.mob_list, sender = null) + var/mob/living/carbon/human/H = M + + if(!check_rights(R_ADMIN)) + return + + if(!istype(H)) + to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human") + return + if(!istype(H.l_ear, /obj/item/radio/headset) && !istype(H.r_ear, /obj/item/radio/headset)) + to_chat(usr, "The person you are trying to contact is not wearing a headset") + return + + if(!sender) + sender = input("Who is the message from?", "Sender") as null|anything in list("Centcomm", "Syndicate") + if(!sender) + return + + message_admins("[key_name_admin(src)] has started answering [key_name_admin(H)]'s [sender] request.") + var/input = clean_input("Please enter a message to reply to [key_name(H)] via their headset.", "Outgoing message from [sender]", "") + if(!input) + message_admins("[key_name_admin(src)] decided not to answer [key_name_admin(H)]'s [sender] request.") + return + + log_admin("[key_name(src)] replied to [key_name(H)]'s [sender] message with the message [input].") + message_admins("[key_name_admin(src)] replied to [key_name_admin(H)]'s [sender] message with: \"[input]\"") + to_chat(H, "Incoming priority transmission from [sender == "Syndicate" ? "your benefactor" : "Central Command"]. Message as follows[sender == "Syndicate" ? ", agent." : ":"] [input]") + + + + +/client/proc/cmd_admin_godmode(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Godmode" + + if(!check_rights(R_ADMIN)) + return + + M.status_flags ^= GODMODE + to_chat(usr, "Toggled [(M.status_flags & GODMODE) ? "ON" : "OFF"]") + + log_admin("[key_name(usr)] has toggled [key_name(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]") + message_admins("[key_name_admin(usr)] has toggled [key_name_admin(M)]'s nodamage to [(M.status_flags & GODMODE) ? "On" : "Off"]", 1) + feedback_add_details("admin_verb","GOD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0) + if(automute) + if(!config.automute_on) + return + else + if(!usr || !usr.client) + return + if(!check_rights(R_ADMIN|R_MOD)) + to_chat(usr, "Error: cmd_admin_mute: You don't have permission to do this.") + return + if(!M.client) + to_chat(usr, "Error: cmd_admin_mute: This mob doesn't have a client tied to it.") + if(!M.client) + return + + var/muteunmute + var/mute_string + + switch(mute_type) + if(MUTE_IC) mute_string = "IC (say and emote)" + if(MUTE_OOC) mute_string = "OOC" + if(MUTE_PRAY) mute_string = "pray" + if(MUTE_ADMINHELP) mute_string = "adminhelp, admin PM and ASAY" + if(MUTE_DEADCHAT) mute_string = "deadchat and DSAY" + if(MUTE_ALL) mute_string = "everything" + else return + + if(automute) + muteunmute = "auto-muted" + M.client.prefs.muted |= mute_type + log_admin("SPAM AUTOMUTE: [muteunmute] [key_name(M)] from [mute_string]") + message_admins("SPAM AUTOMUTE: [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string] by the SPAM AUTOMUTE system. Contact an admin.") + feedback_add_details("admin_verb","AUTOMUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + + if(M.client.prefs.muted & mute_type) + muteunmute = "unmuted" + M.client.prefs.muted &= ~mute_type + else + muteunmute = "muted" + M.client.prefs.muted |= mute_type + + log_admin("[key_name(usr)] has [muteunmute] [key_name(M)] from [mute_string]") + message_admins("[key_name_admin(usr)] has [muteunmute] [key_name_admin(M)] from [mute_string].", 1) + to_chat(M, "You have been [muteunmute] from [mute_string].") + feedback_add_details("admin_verb","MUTE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_add_random_ai_law() + set category = "Event" + set name = "Add Random AI Law" + + if(!check_rights(R_EVENT)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") return + log_admin("[key_name(src)] has added a random AI law.") + message_admins("[key_name_admin(src)] has added a random AI law.") + + var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") + var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) + + new /datum/event/ion_storm(0, announce_ion_laws) + feedback_add_details("admin_verb","ION") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_antagHUD_use() + set category = "Server" + set name = "Toggle antagHUD usage" + set desc = "Toggles antagHUD usage for observers" + + if(!check_rights(R_SERVER)) + return + + var/action="" + if(config.antag_hud_allowed) + for(var/mob/dead/observer/g in get_ghosts()) + if(!g.client.holder) //Remove the verb from non-admin ghosts + g.verbs -= /mob/dead/observer/verb/toggle_antagHUD + if(g.antagHUD) + g.antagHUD = 0 // Disable it on those that have it enabled + g.has_enabled_antagHUD = 2 // We'll allow them to respawn + to_chat(g, "The Administrator has disabled AntagHUD ") + config.antag_hud_allowed = 0 + to_chat(src, "AntagHUD usage has been disabled") + action = "disabled" + else + for(var/mob/dead/observer/g in get_ghosts()) + if(!g.client.holder) // Add the verb back for all non-admin ghosts + g.verbs += /mob/dead/observer/verb/toggle_antagHUD + to_chat(g, "The Administrator has enabled AntagHUD ")// Notify all observers they can now use AntagHUD + + config.antag_hud_allowed = 1 + action = "enabled" + to_chat(src, "AntagHUD usage has been enabled") + + + log_admin("[key_name(usr)] has [action] antagHUD usage for observers") + message_admins("Admin [key_name_admin(usr)] has [action] antagHUD usage for observers", 1) + +/client/proc/toggle_antagHUD_restrictions() + set category = "Server" + set name = "Toggle antagHUD Restrictions" + set desc = "Restricts players that have used antagHUD from being able to join this round." + + if(!check_rights(R_SERVER)) + return + + var/action="" + if(config.antag_hud_restricted) + for(var/mob/dead/observer/g in get_ghosts()) + to_chat(g, "The administrator has lifted restrictions on joining the round if you use AntagHUD") + action = "lifted restrictions" + config.antag_hud_restricted = 0 + to_chat(src, "AntagHUD restrictions have been lifted") + else + for(var/mob/dead/observer/g in get_ghosts()) + to_chat(g, "The administrator has placed restrictions on joining the round if you use AntagHUD") + to_chat(g, "Your AntagHUD has been disabled, you may choose to re-enabled it but will be under restrictions ") + g.antagHUD = 0 + g.has_enabled_antagHUD = 0 + action = "placed restrictions" + config.antag_hud_restricted = 1 + to_chat(src, "AntagHUD restrictions have been enabled") + + log_admin("[key_name(usr)] has [action] on joining the round if they use AntagHUD") + message_admins("Admin [key_name_admin(usr)] has [action] on joining the round if they use AntagHUD", 1) + +/* +If a guy was gibbed and you want to revive him, this is a good way to do so. +Works kind of like entering the game with a new character. Character receives a new mind if they didn't have one. +Traitors and the like can also be revived with the previous role mostly intact. +/N */ +/client/proc/respawn_character() + set category = "Event" + set name = "Respawn Character" + set desc = "Respawn a person that has been gibbed/dusted/killed. They must be a ghost for this to work and preferably should not have a body to go back into." + + if(!check_rights(R_SPAWN)) + return + + var/input = ckey(input(src, "Please specify which key will be respawned.", "Key", "")) + if(!input) + return + + var/mob/dead/observer/G_found + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.ckey == input) + G_found = G + break + + if(!G_found)//If a ghost was not found. + to_chat(usr, "There is no active key like that in the game or the person is not currently a ghost.") + return + + if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something + //Check if they were an alien + if(G_found.mind.assigned_role=="Alien") + if(alert("This character appears to have been an alien. Would you like to respawn them as such?",,"Yes","No")=="Yes") + var/turf/T + if(GLOB.xeno_spawn.len) T = pick(GLOB.xeno_spawn) + else T = pick(GLOB.latejoin) + + var/mob/living/carbon/alien/new_xeno + switch(G_found.mind.special_role)//If they have a mind, we can determine which caste they were. + if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(T) + if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(T) + if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(T) + if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen(T) + else//If we don't know what special role they have, for whatever reason, or they're a larva. + create_xeno(G_found.ckey) + return + + //Now to give them their mind back. + G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use + new_xeno.key = G_found.key + to_chat(new_xeno, "You have been fully respawned. Enjoy the game.") + message_admins("[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno.", 1) + return //all done. The ghost is auto-deleted + + var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned. + + var/datum/data/record/record_found //Referenced to later to either randomize or not randomize the character. + if(G_found.mind && !G_found.mind.active) //mind isn't currently in use by someone/something + /*Try and locate a record for the person being respawned through data_core. + This isn't an exact science but it does the trick more often than not.*/ + var/id = md5("[G_found.real_name][G_found.mind.assigned_role]") + for(var/datum/data/record/t in GLOB.data_core.locked) + if(t.fields["id"]==id) + record_found = t//We shall now reference the record. + break + + if(record_found)//If they have a record we can determine a few things. + new_character.real_name = record_found.fields["name"] + new_character.change_gender(record_found.fields["sex"]) + new_character.age = record_found.fields["age"] + new_character.dna.blood_type = record_found.fields["blood_type"] + else + new_character.change_gender(pick(MALE,FEMALE)) + var/datum/preferences/A = new() + A.real_name = G_found.real_name + A.copy_to(new_character) + + if(!new_character.real_name) + new_character.real_name = random_name(new_character.gender) + new_character.name = new_character.real_name + + if(G_found.mind && !G_found.mind.active) + G_found.mind.transfer_to(new_character) //be careful when doing stuff like this! I've already checked the mind isn't in use + new_character.mind.special_verbs = list() + else + new_character.mind_initialize() + if(!new_character.mind.assigned_role) new_character.mind.assigned_role = "Civilian"//If they somehow got a null assigned role. + + //DNA + if(record_found)//Pull up their name from database records if they did have a mind. + new_character.dna = new()//Let's first give them a new DNA. + new_character.dna.unique_enzymes = record_found.fields["b_dna"]//Enzymes are based on real name but we'll use the record for conformity. + + // I HATE BYOND. HATE. HATE. - N3X + var/list/newSE= record_found.fields["enzymes"] + var/list/newUI = record_found.fields["identity"] + new_character.dna.SE = newSE.Copy() //This is the default of enzymes so I think it's safe to go with. + new_character.dna.UpdateSE() + new_character.UpdateAppearance(newUI.Copy())//Now we configure their appearance based on their unique identity, same as with a DNA machine or somesuch. + else//If they have no records, we just do a random DNA for them, based on their random appearance/savefile. + new_character.dna.ready_dna(new_character) + + new_character.key = G_found.key + + /* + The code below functions with the assumption that the mob is already a traitor if they have a special role. + So all it does is re-equip the mob with powers and/or items. Or not, if they have no special role. + If they don't have a mind, they obviously don't have a special role. + */ + + //Now for special roles and equipment. + switch(new_character.mind.special_role) + if("traitor") + if(new_character.mind.has_antag_datum(/datum/antagonist/traitor)) + var/datum/antagonist/traitor/T = new_character.mind.has_antag_datum(/datum/antagonist/traitor) + T.equip_traitor(src) + else + new_character.mind.add_antag_datum(/datum/antagonist/traitor) + if("Wizard") + new_character.loc = pick(GLOB.wizardstart) + //ticker.mode.learn_basic_spells(new_character) + SSticker.mode.equip_wizard(new_character) + if("Syndicate") + var/obj/effect/landmark/synd_spawn = locate("landmark*Syndicate-Spawn") + if(synd_spawn) + new_character.loc = get_turf(synd_spawn) + call(/datum/game_mode/proc/equip_syndicate)(new_character) + + if("Death Commando")//Leaves them at late-join spawn. + new_character.equip_death_commando() + new_character.internal = new_character.s_store + new_character.update_action_buttons_icon() + else//They may also be a cyborg or AI. + switch(new_character.mind.assigned_role) + if("Cyborg")//More rigging to make em' work and check if they're traitor. + new_character = new_character.Robotize() + if(new_character.mind.special_role=="traitor") + new_character.mind.add_antag_datum(/datum/antagonist/traitor) + if("AI") + new_character = new_character.AIize() + var/mob/living/silicon/ai/ai_character = new_character + ai_character.moveToAILandmark() + if(new_character.mind.special_role=="traitor") + new_character.mind.add_antag_datum(/datum/antagonist/traitor) + //Add aliens. + else + SSjobs.AssignRank(new_character, new_character.mind.assigned_role, 0) + SSjobs.EquipRank(new_character, new_character.mind.assigned_role, 1)//Or we simply equip them. + + //Announces the character on all the systems, based on the record. + if(!issilicon(new_character))//If they are not a cyborg/AI. + if(!record_found && new_character.mind.assigned_role != new_character.mind.special_role)//If there are no records for them. If they have a record, this info is already in there. Offstation special characters announced anyway. + //Power to the user! + if(alert(new_character,"Warning: No data core entry detected. Would you like to announce the arrival of this character by adding them to various databases, such as medical records?",,"No","Yes")=="Yes") + GLOB.data_core.manifest_inject(new_character) + + if(alert(new_character,"Would you like an active AI to announce this character?",,"No","Yes")=="Yes") + call(/mob/new_player/proc/AnnounceArrival)(new_character, new_character.mind.assigned_role) + + message_admins("[key_name_admin(usr)] has respawned [key_name_admin(G_found)] as [new_character.real_name].", 1) + + to_chat(new_character, "You have been fully respawned. Enjoy the game.") + + feedback_add_details("admin_verb","RSPCH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return new_character + +//I use this proc for respawn character too. /N +/proc/create_xeno(ckey) + if(!ckey) + var/list/candidates = list() + for(var/mob/M in GLOB.player_list) + if(M.stat != DEAD) continue //we are not dead! + if(!(ROLE_ALIEN in M.client.prefs.be_special)) continue //we don't want to be an alium + if(jobban_isbanned(M, "alien") || jobban_isbanned(M, "Syndicate")) continue //we are jobbanned + if(M.client.is_afk()) continue //we are afk + if(M.mind && M.mind.current && M.mind.current.stat != DEAD) continue //we have a live body we are tied to + candidates += M.ckey + if(candidates.len) + ckey = input("Pick the player you want to respawn as a xeno.", "Suitable Candidates") as null|anything in candidates + else + to_chat(usr, "Error: create_xeno(): no suitable candidates.") + if(!istext(ckey)) return 0 + + var/alien_caste = input(usr, "Please choose which caste to spawn.","Pick a caste",null) as null|anything in list("Queen","Hunter","Sentinel","Drone","Larva") + var/obj/effect/landmark/spawn_here = GLOB.xeno_spawn.len ? pick(GLOB.xeno_spawn) : pick(GLOB.latejoin) + var/mob/living/carbon/alien/new_xeno + switch(alien_caste) + if("Queen") new_xeno = new /mob/living/carbon/alien/humanoid/queen/large(spawn_here) + if("Hunter") new_xeno = new /mob/living/carbon/alien/humanoid/hunter(spawn_here) + if("Sentinel") new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(spawn_here) + if("Drone") new_xeno = new /mob/living/carbon/alien/humanoid/drone(spawn_here) + if("Larva") new_xeno = new /mob/living/carbon/alien/larva(spawn_here) + else return 0 + + new_xeno.ckey = ckey + message_admins("[key_name_admin(usr)] has spawned [ckey] as a filthy xeno [alien_caste].", 1) + return 1 + + +/client/proc/get_ghosts(var/notify = 0,var/what = 2) + // what = 1, return ghosts ass list. + // what = 2, return mob list + + var/list/mobs = list() + var/list/ghosts = list() + var/list/sortmob = sortAtom(GLOB.mob_list) // get the mob list. + /var/any=0 + for(var/mob/dead/observer/M in sortmob) + mobs.Add(M) //filter it where it's only ghosts + any = 1 //if no ghosts show up, any will just be 0 + if(!any) + if(notify) + to_chat(src, "There doesn't appear to be any ghosts for you to select.") + return + + for(var/mob/M in mobs) + var/name = M.name + ghosts[name] = M //get the name of the mob for the popup list + if(what==1) + return ghosts + else + return mobs + +/client/proc/cmd_admin_add_freeform_ai_law() + set category = "Event" + set name = "Add Custom AI law" + + if(!check_rights(R_EVENT)) + return + + var/input = clean_input("Please enter anything you want the AI to do. Anything. Serious.", "What?", "") + if(!input) + return + + log_admin("Admin [key_name(usr)] has added a new AI law - [input]") + message_admins("Admin [key_name_admin(usr)] has added a new AI law - [input]") + + var/show_log = alert(src, "Show ion message?", "Message", "Yes", "No") + var/announce_ion_laws = (show_log == "Yes" ? 1 : -1) + + new /datum/event/ion_storm(0, announce_ion_laws, input) + + feedback_add_details("admin_verb","IONC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_rejuvenate(mob/living/M as mob in GLOB.mob_list) + set category = "Event" + set name = "Rejuvenate" + + if(!check_rights(R_REJUVINATE)) + return + + if(!mob) + return + if(!istype(M)) + alert("Cannot revive a ghost") + return + M.revive() + + log_admin("[key_name(usr)] healed / revived [key_name(M)]") + message_admins("Admin [key_name_admin(usr)] healed / revived [key_name_admin(M)]!", 1) + feedback_add_details("admin_verb","REJU") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_create_centcom_report() + set category = "Event" + set name = "Create Communications Report" + + if(!check_rights(R_SERVER|R_EVENT)) + return + +//the stuff on the list is |"report type" = "report title"|, if that makes any sense + var/list/MsgType = list("Central Command Report" = "Nanotrasen Update", + "Syndicate Communique" = "Syndicate Message", + "Space Wizard Federation Message" = "Sorcerous Message", + "Enemy Communications" = "Unknown Message", + "Custom" = "Cryptic Message") + + var/list/MsgSound = list("Beep" = 'sound/misc/notice2.ogg', + "Enemy Communications Intercepted" = 'sound/AI/intercept2.ogg', + "New Command Report Created" = 'sound/AI/commandreport.ogg') + + var/type = input(usr, "Pick a type of report to send", "Report Type", "") as anything in MsgType + + if(type == "Custom") + type = clean_input("What would you like the report type to be?", "Report Type", "Encrypted Transmission") + + var/customname = input(usr, "Pick a title for the report.", "Title", MsgType[type]) as text|null + if(!customname) + return + var/input = input(usr, "Please enter anything you want. Anything. Serious.", "What's the message?") as message|null + if(!input) + return + + switch(alert("Should this be announced to the general population?",,"Yes","No", "Cancel")) + if("Yes") + var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound + + GLOB.command_announcement.Announce(input, customname, MsgSound[beepsound], , , type) + print_command_report(input, "[command_name()] Update") + if("No") + //same thing as the blob stuff - it's not public, so it's classified, dammit + GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles."); + print_command_report(input, "Classified [command_name()] Update") + else + return + + log_admin("[key_name(src)] has created a communications report: [input]") + message_admins("[key_name_admin(src)] has created a communications report", 1) + feedback_add_details("admin_verb","CCR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/cmd_admin_delete(atom/A as obj|mob|turf in view()) + set category = "Admin" + set name = "Delete" + + if(!check_rights(R_ADMIN)) + return + + admin_delete(A) + +/client/proc/admin_delete(datum/D) + if(istype(D) && !D.can_vv_delete()) + to_chat(src, "[D] rejected your deletion") + return + var/atom/A = D + var/coords = istype(A) ? "at ([A.x], [A.y], [A.z])" : "" + if(alert(src, "Are you sure you want to delete:\n[D]\n[coords]?", "Confirmation", "Yes", "No") == "Yes") + log_admin("[key_name(usr)] deleted [D] [coords]") + message_admins("[key_name_admin(usr)] deleted [D] [coords]", 1) + feedback_add_details("admin_verb","DEL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(isturf(D)) + var/turf/T = D + T.ChangeTurf(T.baseturf) + else + qdel(D) + +/client/proc/cmd_admin_list_open_jobs() + set category = "Admin" + set name = "List free slots" + + if(!check_rights(R_ADMIN)) + return + + if(SSjobs) + var/currentpositiontally + var/totalpositiontally + to_chat(src, "Job Name: Filled job slot / Total job slots (Free job slots)") + for(var/datum/job/job in SSjobs.occupations) + to_chat(src, "[job.title]: [job.current_positions] / \ + [job.total_positions == -1 ? "UNLIMITED" : job.total_positions] \ + ([job.total_positions == -1 ? "UNLIMITED" : job.total_positions - job.current_positions])") + if(job.total_positions != -1) // Only count position that isn't unlimited + currentpositiontally += job.current_positions + totalpositiontally += job.total_positions + to_chat(src, "Currently filled job slots (Excluding unlimited): [currentpositiontally] / [totalpositiontally] ([totalpositiontally - currentpositiontally])") + feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_explosion(atom/O as obj|mob|turf in view()) + set category = "Event" + set name = "Explosion" + + if(!check_rights(R_DEBUG|R_EVENT)) + return + + var/devastation = input("Range of total devastation. -1 to none", text("Input")) as num|null + if(devastation == null) return + var/heavy = input("Range of heavy impact. -1 to none", text("Input")) as num|null + if(heavy == null) return + var/light = input("Range of light impact. -1 to none", text("Input")) as num|null + if(light == null) return + var/flash = input("Range of flash. -1 to none", text("Input")) as num|null + if(flash == null) return + var/flames = input("Range of flames. -1 to none", text("Input")) as num|null + if(flames == null) return + + if((devastation != -1) || (heavy != -1) || (light != -1) || (flash != -1) || (flames != -1)) + if((devastation > 20) || (heavy > 20) || (light > 20) || (flames > 20)) + if(alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No") + return + + explosion(O, devastation, heavy, light, flash, null, null,flames) + log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])") + feedback_add_details("admin_verb","EXPL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + else + return + +/client/proc/cmd_admin_emp(atom/O as obj|mob|turf in view()) + set category = "Event" + set name = "EM Pulse" + + if(!check_rights(R_DEBUG|R_EVENT)) + return + + var/heavy = input("Range of heavy pulse.", text("Input")) as num|null + if(heavy == null) return + var/light = input("Range of light pulse.", text("Input")) as num|null + if(light == null) return + + if(heavy || light) + + empulse(O, heavy, light) + log_admin("[key_name(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])") + message_admins("[key_name_admin(usr)] created an EM pulse ([heavy], [light]) at ([O.x],[O.y],[O.z])", 1) + feedback_add_details("admin_verb","EMP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + return + else + return + +/client/proc/cmd_admin_gib(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Gib" + + if(!check_rights(R_ADMIN|R_EVENT)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") return + //Due to the delay here its easy for something to have happened to the mob + if(!M) return + + log_admin("[key_name(usr)] has gibbed [key_name(M)]") + message_admins("[key_name_admin(usr)] has gibbed [key_name_admin(M)]", 1) + + if(istype(M, /mob/dead/observer)) + gibs(M.loc) + return + + M.gib() + feedback_add_details("admin_verb","GIB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_gib_self() + set name = "Gibself" + set category = "Event" + + if(!check_rights(R_ADMIN|R_EVENT)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm == "Yes") + if(istype(mob, /mob/dead/observer)) // so they don't spam gibs everywhere + return + else + mob.gib() + + log_admin("[key_name(usr)] used gibself.") + message_admins("[key_name_admin(usr)] used gibself.", 1) + feedback_add_details("admin_verb","GIBS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/cmd_admin_check_contents(mob/living/M as mob in GLOB.mob_list) + set name = "Check Contents" + set category = null + + if(!check_rights(R_ADMIN)) + return + + var/list/L = M.get_contents() + for(var/t in L) + to_chat(usr, "[t]") + feedback_add_details("admin_verb","CC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_view_range() + set category = "Admin" + set name = "Change View Range" + set desc = "switches between 1x and custom views" + + if(!check_rights(R_ADMIN)) + return + + if(view == world.view) + view = input("Select view range:", "View Range", world.view) in list(1,2,3,4,5,6,7,8,9,10,11,12,13,14,128) + else + view = world.view + + log_admin("[key_name(usr)] changed their view range to [view].") + //message_admins("[key_name_admin(usr)] changed their view range to [view].", 1) //why? removed by order of XSI + + feedback_add_details("admin_verb","CVRA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/admin_call_shuttle() + + set category = "Admin" + set name = "Call Shuttle" + + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + + if(!check_rights(R_ADMIN)) + return + + var/confirm = alert(src, "You sure?", "Confirm", "Yes", "No") + if(confirm != "Yes") return + + if(alert("Set Shuttle Recallable (Select Yes unless you know what this does)", "Recallable?", "Yes", "No") == "Yes") + SSshuttle.emergency.canRecall = TRUE + else + SSshuttle.emergency.canRecall = FALSE + + SSshuttle.emergency.request() + + feedback_add_details("admin_verb","CSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-called the emergency shuttle.") + message_admins("[key_name_admin(usr)] admin-called the emergency shuttle.") + return + +/client/proc/admin_cancel_shuttle() + set category = "Admin" + set name = "Cancel Shuttle" + + if(!check_rights(R_ADMIN)) + return + if(alert(src, "You sure?", "Confirm", "Yes", "No") != "Yes") return + + if(SSshuttle.emergency.mode >= SHUTTLE_DOCKED) + return + + if(SSshuttle.emergency.canRecall == FALSE) + if(alert("Shuttle is currently set to be nonrecallable. Recalling may break things. Respect Recall Status?", "Override Recall Status?", "Yes", "No") == "Yes") + return + else + var/keepStatus = alert("Maintain recall status on future shuttle calls?", "Maintain Status?", "Yes", "No") == "Yes" //Keeps or drops recallability + SSshuttle.emergency.canRecall = TRUE // must be true for cancel proc to work + SSshuttle.emergency.cancel() + if(keepStatus) + SSshuttle.emergency.canRecall = FALSE // restores original status + else + SSshuttle.emergency.cancel() + + feedback_add_details("admin_verb","CCSHUT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] admin-recalled the emergency shuttle.") + message_admins("[key_name_admin(usr)] admin-recalled the emergency shuttle.") + return + +/client/proc/admin_deny_shuttle() + set category = "Admin" + set name = "Toggle Deny Shuttle" + + if(!SSticker) + return + + if(!check_rights(R_ADMIN)) + return + + if(SSshuttle) + SSshuttle.emergencyNoEscape = !SSshuttle.emergencyNoEscape + + log_admin("[key_name(src)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") + message_admins("[key_name_admin(usr)] has [SSshuttle.emergencyNoEscape ? "denied" : "allowed"] the shuttle to be called.") + +/client/proc/cmd_admin_attack_log(mob/M as mob in GLOB.mob_list) + set category = "Admin" + set name = "Attack Log" + + if(!check_rights(R_ADMIN)) + return + + to_chat(usr, text("Attack Log for []", mob)) + for(var/t in M.attack_log_old) + to_chat(usr, t) + feedback_add_details("admin_verb","ATTL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + +/client/proc/everyone_random() + set category = "Event" + set name = "Make Everyone Random" + set desc = "Make everyone have a random appearance. You can only use this before rounds!" + + if(!check_rights(R_SERVER|R_EVENT)) + return + + if(SSticker && SSticker.mode) + to_chat(usr, "Nope you can't do this, the game's already started. This only works before rounds!") + return + + if(SSticker.random_players) + SSticker.random_players = 0 + message_admins("Admin [key_name_admin(usr)] has disabled \"Everyone is Special\" mode.", 1) + to_chat(usr, "Disabled.") + return + + + var/notifyplayers = alert(src, "Do you want to notify the players?", "Options", "Yes", "No", "Cancel") + if(notifyplayers == "Cancel") + return + + log_admin("Admin [key_name(src)] has forced the players to have random appearances.") + message_admins("Admin [key_name_admin(usr)] has forced the players to have random appearances.", 1) + + if(notifyplayers == "Yes") + to_chat(world, "Admin [usr.key] has forced the players to have completely random identities!") + + to_chat(usr, "Remember: you can always disable the randomness by using the verb again, assuming the round hasn't started yet.") + + SSticker.random_players = 1 + feedback_add_details("admin_verb","MER") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_random_events() + set category = "Event" + set name = "Toggle random events on/off" + + set desc = "Toggles random events such as meteors, black holes, blob (but not space dust) on/off" + if(!check_rights(R_SERVER|R_EVENT)) + return + + if(!config.allow_random_events) + config.allow_random_events = 1 + to_chat(usr, "Random events enabled") + message_admins("Admin [key_name_admin(usr)] has enabled random events.", 1) + else + config.allow_random_events = 0 + to_chat(usr, "Random events disabled") + message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1) + feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/reset_all_tcs() + set category = "Admin" + set name = "Reset NTTC Configuration" + set desc = "Resets NTTC to the default configuration." + + if(!check_rights(R_ADMIN)) + return + + var/confirm = alert(src, "You sure you want to reset NTTC?", "Confirm", "Yes", "No") + if(confirm != "Yes") + return + + GLOB.nttc_config.reset() + log_admin("[key_name(usr)] reset NTTC scripts.") + message_admins("[key_name_admin(usr)] reset NTTC scripts.") + feedback_add_details("admin_verb","RAT2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/list_ssds_afks() + set category = "Admin" + set name = "List SSDs and AFKs" + set desc = "Lists SSD and AFK players" + + if(!check_rights(R_ADMIN)) + return + + /* ======== SSD Section ========= */ + var/msg = "SSD & AFK Report" + msg += "SSD Players:
    " + msg += "" + var/mins_ssd + var/job_string + var/key_string + var/role_string + var/obj_count = 0 + var/obj_string = "" + for(var/mob/living/carbon/human/H in GLOB.living_mob_list) + if(!isLivingSSD(H)) + continue + mins_ssd = round((world.time - H.last_logout) / 600) + if(H.job) + job_string = H.job + else + job_string = "-" + key_string = H.key + if(job_string in GLOB.command_positions) + job_string = "" + job_string + "" + role_string = "-" + obj_count = 0 + obj_string = "" + if(H.mind) + if(H.mind.special_role) + role_string = "[H.mind.special_role]" + if(!H.key && H.mind.key) + key_string = H.mind.key + for(var/datum/objective/O in GLOB.all_objectives) + if(O.target == H.mind) + obj_count++ + if(obj_count > 0) + obj_string = "
    Obj Target" + msg += "" + msg += "" + if(istype(H.loc, /obj/machinery/cryopod)) + msg += "" + else + msg += "" + msg += "" + msg += "
    KeyReal NameJobMins SSDSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_ssd][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo

    " + + /* ======== AFK Section ========= */ + msg += "AFK Players:
    " + msg += "" + var/mins_afk + for(var/mob/living/carbon/human/H in GLOB.living_mob_list) + if(H.client == null || H.stat == DEAD) // No clientless or dead + continue + mins_afk = round(H.client.inactivity / 600) + if(mins_afk < config.list_afk_minimum) + continue + if(H.job) + job_string = H.job + else + job_string = "-" + key_string = H.key + if(job_string in GLOB.command_positions) + job_string = "" + job_string + "" + role_string = "-" + obj_count = 0 + obj_string = "" + if(H.mind) + if(H.mind.special_role) + role_string = "[H.mind.special_role]" + if(!H.key && H.mind.key) + key_string = H.mind.key + for(var/datum/objective/O in GLOB.all_objectives) + if(O.target == H.mind) + obj_count++ + if(obj_count > 0) + obj_string = "
    Obj Target" + msg += "" + msg += "" + if(istype(H.loc, /obj/machinery/cryopod)) + msg += "" + else + msg += "" + msg += "" + msg += "
    KeyReal NameJobMins AFKSpecial RoleAreaPPNCryo
    [key_string][H.real_name][job_string][mins_afk][role_string][obj_string][get_area(H)][ADMIN_PP(H,"PP")]De-SpawnCryo
    " + src << browse(msg, "window=Player_ssd_afk_check;size=600x300") + +/client/proc/toggle_ert_calling() + set category = "Event" + set name = "Toggle ERT" + + set desc = "Toggle the station's ability to call a response team." + if(!check_rights(R_EVENT)) + return + + if(SSticker.mode.ert_disabled) + SSticker.mode.ert_disabled = 0 + to_chat(usr, "ERT has been Enabled.") + log_admin("Admin [key_name(src)] has enabled ERT calling.") + message_admins("Admin [key_name_admin(usr)] has enabled ERT calling.", 1) + else + SSticker.mode.ert_disabled = 1 + to_chat(usr, "ERT has been Disabled.") + log_admin("Admin [key_name(src)] has disabled ERT calling.") + message_admins("Admin [key_name_admin(usr)] has disabled ERT calling.", 1) + +/client/proc/show_tip() + set category = "Admin" + set name = "Show Custom Tip" + set desc = "Sends a tip (that you specify) to all players. After all \ + you're the experienced player here." + + if(!check_rights(R_ADMIN)) + return + + var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null + if(!input) + return + + if(!SSticker) + return + + SSticker.selected_tip = input + + // If we've already tipped, then send it straight away. + if(SSticker.tipped) + SSticker.send_tip_of_the_round() + + message_admins("[key_name_admin(usr)] sent a Tip of the round.") + log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.") + +/client/proc/modify_goals() + set category = "Event" + set name = "Modify Station Goals" + + if(!check_rights(R_EVENT)) + return + + holder.modify_goals() + +/datum/admins/proc/modify_goals() + if(!SSticker || !SSticker.mode) + to_chat(usr, "This verb can only be used if the round has started.") + return + + var/dat = "" + for(var/datum/station_goal/S in SSticker.mode.station_goals) + dat += "[S.name] - Announce | Remove
    " + dat += "
    Add New Goal" + usr << browse(dat, "window=goals;size=400x400") + +/// Allow admin to add or remove traits of datum +/datum/admins/proc/modify_traits(datum/D) + if(!D) + return + + var/add_or_remove = input("Remove/Add?", "Trait Remove/Add") as null|anything in list("Add","Remove") + if(!add_or_remove) + return + var/list/availible_traits = list() + + switch(add_or_remove) + if("Add") + for(var/key in GLOB.traits_by_type) + if(istype(D,key)) + availible_traits += GLOB.traits_by_type[key] + if("Remove") + if(!GLOB.trait_name_map) + GLOB.trait_name_map = generate_trait_name_map() + for(var/trait in D.status_traits) + var/name = GLOB.trait_name_map[trait] || trait + availible_traits[name] = trait + + var/chosen_trait = input("Select trait to modify", "Trait") as null|anything in availible_traits + if(!chosen_trait) + return + chosen_trait = availible_traits[chosen_trait] + + var/source = "adminabuse" + switch(add_or_remove) + if("Add") //Not doing source choosing here intentionally to make this bit faster to use, you can always vv it. + ADD_TRAIT(D, chosen_trait, source) + if("Remove") + var/specific = input("All or specific source ?", "Trait Remove/Add") as null|anything in list("All","Specific") + if(!specific) + return + switch(specific) + if("All") + source = null + if("Specific") + source = input("Source to be removed","Trait Remove/Add") as null|anything in D.status_traits[chosen_trait] + if(!source) + return + REMOVE_TRAIT(D, chosen_trait, source) diff --git a/code/modules/admin/verbs/serialization.dm b/code/modules/admin/verbs/serialization.dm index 1c087b9e8fcc..6443363f1d01 100644 --- a/code/modules/admin/verbs/serialization.dm +++ b/code/modules/admin/verbs/serialization.dm @@ -23,4 +23,4 @@ var/json_text = input("Enter the JSON code:","Text") as message|null if(json_text) - json_to_object(json_text, get_turf(usr)) \ No newline at end of file + json_to_object(json_text, get_turf(usr)) diff --git a/code/modules/admin/verbs/space_transitions.dm b/code/modules/admin/verbs/space_transitions.dm index eda17996c654..4589e5354b5e 100644 --- a/code/modules/admin/verbs/space_transitions.dm +++ b/code/modules/admin/verbs/space_transitions.dm @@ -13,7 +13,7 @@ message_admins("[key_name_admin(usr)] re-assigned all space transitions") - space_manager.do_transition_setup() + GLOB.space_manager.do_transition_setup() log_admin("[key_name(usr)] re-assigned all space transitions") feedback_add_details("admin_verb","SPCRST") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -37,5 +37,5 @@ message_admins("[key_name_admin(usr)] made a space map") - space_manager.map_as_turfs(get_turf(usr)) + GLOB.space_manager.map_as_turfs(get_turf(usr)) log_admin("[key_name(usr)] made a space map") diff --git a/code/modules/admin/verbs/striketeam.dm b/code/modules/admin/verbs/striketeam.dm index 5e7f88a80d7a..25874d033606 100644 --- a/code/modules/admin/verbs/striketeam.dm +++ b/code/modules/admin/verbs/striketeam.dm @@ -1,204 +1,204 @@ -//STRIKE TEAMS - -#define COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future -var/global/sent_strike_team = 0 - -/client/proc/strike_team() - if(!SSticker) - to_chat(usr, "The game hasn't started yet!") - return - if(sent_strike_team == 1) - to_chat(usr, "CentComm is already sending a team.") - return - if(alert("Do you want to send in the CentComm death squad? Once enabled, this is irreversible.",,"Yes","No")!="Yes") - return - alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. The first one selected/spawned will be the team leader.") - - message_admins("[key_name_admin(usr)] has started to spawn a CentComm DeathSquad.", 1) - - var/input = null - while(!input) - input = sanitize(copytext(input(src, "Please specify which mission the death commando squad shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) - if(!input) - if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") - return - - if(sent_strike_team) - to_chat(usr, "Looks like someone beat you to it.") - return - - // Find the nuclear auth code - var/nuke_code - var/temp_code - for(var/obj/machinery/nuclearbomb/N in world) - temp_code = text2num(N.r_code) - if(temp_code)//if it's actually a number. It won't convert any non-numericals. - nuke_code = N.r_code - break - - // Find ghosts willing to be DS - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) - if(!commando_ghosts.len) - to_chat(usr, "Nobody volunteered to join the DeathSquad.") - return - - sent_strike_team = 1 - - // Spawns commandos and equips them. - var/commando_number = COMMANDOS_POSSIBLE //for selecting a leader - var/is_leader = TRUE // set to FALSE after leader is spawned - - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - - if(commando_number <= 0) - break - - if(L.name == "Commando") - - if(!commando_ghosts.len) - break - - var/use_ds_borg = FALSE - var/mob/ghost_mob = pick(commando_ghosts) - commando_ghosts -= ghost_mob - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) - continue - - if(!is_leader) - var/new_dstype = alert(ghost_mob.client, "Select Deathsquad Type.", "DS Character Generation", "Organic", "Cyborg") - if(new_dstype == "Cyborg") - use_ds_borg = TRUE - - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) // Have to re-check this due to the above alert() call - continue - - if(use_ds_borg) - var/mob/living/silicon/robot/deathsquad/R = new() - R.forceMove(get_turf(L)) - var/rnum = rand(1,1000) - var/borgname = "Epsilon [rnum]" - R.name = borgname - R.custom_name = borgname - R.real_name = R.name - R.mind = new - R.mind.current = R - R.mind.original = R - R.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD - R.mind.special_role = SPECIAL_ROLE_DEATHSQUAD - R.mind.offstation_role = TRUE - if(!(R.mind in SSticker.minds)) - SSticker.minds += R.mind - SSticker.mode.traitors += R.mind - R.key = ghost_mob.key - if(nuke_code) - R.mind.store_memory("Nuke Code: [nuke_code].") - R.mind.store_memory("Mission: [input].") - to_chat(R, "You are a Special Operations cyborg, in the service of Central Command. \nYour current mission is: [input]") - else - var/mob/living/carbon/human/new_commando = create_death_commando(L, is_leader) - new_commando.mind.key = ghost_mob.key - new_commando.key = ghost_mob.key - new_commando.internal = new_commando.s_store - new_commando.update_action_buttons_icon() - if(nuke_code) - new_commando.mind.store_memory("Nuke Code: [nuke_code].") - new_commando.mind.store_memory("Mission: [input].") - to_chat(new_commando, "You are a Special Ops [is_leader ? "TEAM LEADER" : "commando"] in the service of Central Command. Check the table ahead for detailed instructions.\nYour current mission is: [input]") - - is_leader = FALSE - commando_number-- - - //Spawns the rest of the commando gear. - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Commando_Manual") - //new /obj/item/gun/energy/pulse_rifle(L.loc) - var/obj/item/paper/P = new(L.loc) - P.info = "

    Good morning soldier!. This compact guide will familiarize you with standard operating procedure. There are three basic rules to follow:
    #1 Work as a team.
    #2 Accomplish your objective at all costs.
    #3 Leave no witnesses.
    You are fully equipped and stocked for your mission--before departing on the Spec. Ops. Shuttle due South, make sure that all operatives are ready. Actual mission objective will be relayed to you by Central Command through your headsets.
    If deemed appropriate, Central Command will also allow members of your team to equip assault power-armor for the mission. You will find the armor storage due West of your position. Once you are ready to leave, utilize the Special Operations shuttle console and toggle the hull doors via the other console.

    In the event that the team does not accomplish their assigned objective in a timely manner, or finds no other way to do so, attached below are instructions on how to operate a Nanotrasen Nuclear Device. Your operations LEADER is provided with a nuclear authentication disk and a pin-pointer for this reason. You may easily recognize them by their rank: Lieutenant, Captain, or Major. The nuclear device itself will be present somewhere on your destination.

    Hello and thank you for choosing Nanotrasen for your nuclear information needs. Today's crash course will deal with the operation of a Fission Class Nanotrasen made Nuclear Device.
    First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE. Pressing any button on the compacted bomb will cause it to extend and bolt itself into place. If this is done to unbolt it one must completely log in which at this time may not be possible.
    To make the device functional:
    #1 Place bomb in designated detonation zone
    #2 Extend and anchor bomb (attack with hand).
    #3 Insert Nuclear Auth. Disk into slot.
    #4 Type numeric code into keypad ([nuke_code]).
    Note: If you make a mistake press R to reset the device.
    #5 Press the E button to log onto the device.
    You now have activated the device. To deactivate the buttons at anytime, for example when you have already prepped the bomb for detonation, remove the authentication disk OR press the R on the keypad. Now the bomb CAN ONLY be detonated using the timer. A manual detonation is not an option.
    Note: Toggle off the SAFETY.
    Use the - - and + + to set a detonation time between 5 seconds and 10 minutes. Then press the timer toggle button to start the countdown. Now remove the authentication disk so that the buttons deactivate.
    Note: THE BOMB IS STILL SET AND WILL DETONATE
    Now before you remove the disk if you need to move the bomb you can: Toggle off the anchor, move it, and re-anchor.

    The nuclear authorization code is: [nuke_code ? nuke_code : "None provided"]

    Good luck, soldier!

    " - P.name = "Spec. Ops Manual" - P.icon = "pamphlet-ds" - var/obj/item/stamp/centcom/stamp = new - P.stamp(stamp) - qdel(stamp) - - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - if(L.name == "Commando-Bomb") - new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) - qdel(L) - - message_admins("[key_name_admin(usr)] has spawned a CentComm DeathSquad.", 1) - log_admin("[key_name(usr)] used Spawn Death Squad.") - return 1 - -/client/proc/create_death_commando(obj/spawn_location, is_leader = FALSE) - var/mob/living/carbon/human/new_commando = new(spawn_location.loc) - var/commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(is_leader) - A.age = rand(35,45) - A.real_name = "[commando_leader_rank] [commando_name]" - else - A.real_name = "[commando_rank] [commando_name]" - A.copy_to(new_commando) - - - new_commando.dna.ready_dna(new_commando)//Creates DNA. - - //Creates mind stuff. - new_commando.mind_initialize() - new_commando.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD - new_commando.mind.special_role = SPECIAL_ROLE_DEATHSQUAD - SSticker.mode.traitors |= new_commando.mind//Adds them to current traitor list. Which is really the extra antagonist list. - new_commando.equip_death_commando(is_leader) - return new_commando - -/mob/living/carbon/human/proc/equip_death_commando(is_leader = FALSE) - - var/obj/item/radio/R = new /obj/item/radio/headset/alt(src) - R.set_frequency(DTH_FREQ) - equip_to_slot_or_del(R, slot_l_ear) - if(is_leader) - equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) - else - equip_to_slot_or_del(new /obj/item/clothing/under/color/green(src), slot_w_uniform) - equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/advance(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/clothing/suit/space/deathsquad(src), slot_wear_suit) - equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) - equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/deathsquad(src), slot_head) - equip_to_slot_or_del(new /obj/item/clothing/mask/gas/sechailer/swat(src), slot_wear_mask) - equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) - - equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) - equip_to_slot_or_del(new /obj/item/storage/box(src), slot_in_backpack) - - equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/storage/box/flashbangs(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/flashlight(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) - if(is_leader) - equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) - else - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - - - equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber(src), slot_l_store) - equip_to_slot_or_del(new /obj/item/shield/energy(src), slot_r_store) - equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/double/full(src), slot_s_store) - equip_to_slot_or_del(new /obj/item/gun/projectile/revolver/mateba(src), slot_belt) - equip_to_slot_or_del(new /obj/item/gun/energy/pulse(src), slot_r_hand) - - var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(src) - L.implant(src) - - var/obj/item/card/id/W = new(src) - W.name = "[real_name]'s ID Card" - W.icon_state = "deathsquad" - W.assignment = "Death Commando" - W.access = get_centcom_access(W.assignment) - W.registered_name = real_name - equip_to_slot_or_del(W, slot_wear_id) - - return 1 +//STRIKE TEAMS + +#define COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future +GLOBAL_VAR_INIT(sent_strike_team, 0) + +/client/proc/strike_team() + if(!SSticker) + to_chat(usr, "The game hasn't started yet!") + return + if(GLOB.sent_strike_team == 1) + to_chat(usr, "CentComm is already sending a team.") + return + if(alert("Do you want to send in the CentComm death squad? Once enabled, this is irreversible.",,"Yes","No")!="Yes") + return + alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned commandos have internals cameras which are viewable through a monitor inside the Spec. Ops. Office. The first one selected/spawned will be the team leader.") + + message_admins("[key_name_admin(usr)] has started to spawn a CentComm DeathSquad.", 1) + + var/input = null + while(!input) + input = sanitize(copytext(input(src, "Please specify which mission the death commando squad shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) + if(!input) + if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") + return + + if(GLOB.sent_strike_team) + to_chat(usr, "Looks like someone beat you to it.") + return + + // Find the nuclear auth code + var/nuke_code + var/temp_code + for(var/obj/machinery/nuclearbomb/N in world) + temp_code = text2num(N.r_code) + if(temp_code)//if it's actually a number. It won't convert any non-numericals. + nuke_code = N.r_code + break + + // Find ghosts willing to be DS + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, COMMANDOS_POSSIBLE, "Join the DeathSquad?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + if(!commando_ghosts.len) + to_chat(usr, "Nobody volunteered to join the DeathSquad.") + return + + GLOB.sent_strike_team = 1 + + // Spawns commandos and equips them. + var/commando_number = COMMANDOS_POSSIBLE //for selecting a leader + var/is_leader = TRUE // set to FALSE after leader is spawned + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + + if(commando_number <= 0) + break + + if(L.name == "Commando") + + if(!commando_ghosts.len) + break + + var/use_ds_borg = FALSE + var/mob/ghost_mob = pick(commando_ghosts) + commando_ghosts -= ghost_mob + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) + continue + + if(!is_leader) + var/new_dstype = alert(ghost_mob.client, "Select Deathsquad Type.", "DS Character Generation", "Organic", "Cyborg") + if(new_dstype == "Cyborg") + use_ds_borg = TRUE + + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) // Have to re-check this due to the above alert() call + continue + + if(use_ds_borg) + var/mob/living/silicon/robot/deathsquad/R = new() + R.forceMove(get_turf(L)) + var/rnum = rand(1,1000) + var/borgname = "Epsilon [rnum]" + R.name = borgname + R.custom_name = borgname + R.real_name = R.name + R.mind = new + R.mind.current = R + R.mind.original = R + R.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD + R.mind.special_role = SPECIAL_ROLE_DEATHSQUAD + R.mind.offstation_role = TRUE + if(!(R.mind in SSticker.minds)) + SSticker.minds += R.mind + SSticker.mode.traitors += R.mind + R.key = ghost_mob.key + if(nuke_code) + R.mind.store_memory("Nuke Code: [nuke_code].") + R.mind.store_memory("Mission: [input].") + to_chat(R, "You are a Special Operations cyborg, in the service of Central Command. \nYour current mission is: [input]") + else + var/mob/living/carbon/human/new_commando = create_death_commando(L, is_leader) + new_commando.mind.key = ghost_mob.key + new_commando.key = ghost_mob.key + new_commando.internal = new_commando.s_store + new_commando.update_action_buttons_icon() + if(nuke_code) + new_commando.mind.store_memory("Nuke Code: [nuke_code].") + new_commando.mind.store_memory("Mission: [input].") + to_chat(new_commando, "You are a Special Ops [is_leader ? "TEAM LEADER" : "commando"] in the service of Central Command. Check the table ahead for detailed instructions.\nYour current mission is: [input]") + + is_leader = FALSE + commando_number-- + + //Spawns the rest of the commando gear. + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Commando_Manual") + //new /obj/item/gun/energy/pulse_rifle(L.loc) + var/obj/item/paper/P = new(L.loc) + P.info = "

    Good morning soldier!. This compact guide will familiarize you with standard operating procedure. There are three basic rules to follow:
    #1 Work as a team.
    #2 Accomplish your objective at all costs.
    #3 Leave no witnesses.
    You are fully equipped and stocked for your mission--before departing on the Spec. Ops. Shuttle due South, make sure that all operatives are ready. Actual mission objective will be relayed to you by Central Command through your headsets.
    If deemed appropriate, Central Command will also allow members of your team to equip assault power-armor for the mission. You will find the armor storage due West of your position. Once you are ready to leave, utilize the Special Operations shuttle console and toggle the hull doors via the other console.

    In the event that the team does not accomplish their assigned objective in a timely manner, or finds no other way to do so, attached below are instructions on how to operate a Nanotrasen Nuclear Device. Your operations LEADER is provided with a nuclear authentication disk and a pin-pointer for this reason. You may easily recognize them by their rank: Lieutenant, Captain, or Major. The nuclear device itself will be present somewhere on your destination.

    Hello and thank you for choosing Nanotrasen for your nuclear information needs. Today's crash course will deal with the operation of a Fission Class Nanotrasen made Nuclear Device.
    First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE. Pressing any button on the compacted bomb will cause it to extend and bolt itself into place. If this is done to unbolt it one must completely log in which at this time may not be possible.
    To make the device functional:
    #1 Place bomb in designated detonation zone
    #2 Extend and anchor bomb (attack with hand).
    #3 Insert Nuclear Auth. Disk into slot.
    #4 Type numeric code into keypad ([nuke_code]).
    Note: If you make a mistake press R to reset the device.
    #5 Press the E button to log onto the device.
    You now have activated the device. To deactivate the buttons at anytime, for example when you have already prepped the bomb for detonation, remove the authentication disk OR press the R on the keypad. Now the bomb CAN ONLY be detonated using the timer. A manual detonation is not an option.
    Note: Toggle off the SAFETY.
    Use the - - and + + to set a detonation time between 5 seconds and 10 minutes. Then press the timer toggle button to start the countdown. Now remove the authentication disk so that the buttons deactivate.
    Note: THE BOMB IS STILL SET AND WILL DETONATE
    Now before you remove the disk if you need to move the bomb you can: Toggle off the anchor, move it, and re-anchor.

    The nuclear authorization code is: [nuke_code ? nuke_code : "None provided"]

    Good luck, soldier!

    " + P.name = "Spec. Ops Manual" + P.icon = "pamphlet-ds" + var/obj/item/stamp/centcom/stamp = new + P.stamp(stamp) + qdel(stamp) + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Commando-Bomb") + new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) + qdel(L) + + message_admins("[key_name_admin(usr)] has spawned a CentComm DeathSquad.", 1) + log_admin("[key_name(usr)] used Spawn Death Squad.") + return 1 + +/client/proc/create_death_commando(obj/spawn_location, is_leader = FALSE) + var/mob/living/carbon/human/new_commando = new(spawn_location.loc) + var/commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(is_leader) + A.age = rand(35,45) + A.real_name = "[commando_leader_rank] [commando_name]" + else + A.real_name = "[commando_rank] [commando_name]" + A.copy_to(new_commando) + + + new_commando.dna.ready_dna(new_commando)//Creates DNA. + + //Creates mind stuff. + new_commando.mind_initialize() + new_commando.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD + new_commando.mind.special_role = SPECIAL_ROLE_DEATHSQUAD + SSticker.mode.traitors |= new_commando.mind//Adds them to current traitor list. Which is really the extra antagonist list. + new_commando.equip_death_commando(is_leader) + return new_commando + +/mob/living/carbon/human/proc/equip_death_commando(is_leader = FALSE) + + var/obj/item/radio/R = new /obj/item/radio/headset/alt(src) + R.set_frequency(DTH_FREQ) + equip_to_slot_or_del(R, slot_l_ear) + if(is_leader) + equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) + else + equip_to_slot_or_del(new /obj/item/clothing/under/color/green(src), slot_w_uniform) + equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/advance(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/deathsquad(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) + equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/deathsquad(src), slot_head) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/sechailer/swat(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) + + equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) + equip_to_slot_or_del(new /obj/item/storage/box(src), slot_in_backpack) + + equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/storage/box/flashbangs(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/flashlight(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) + if(is_leader) + equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) + else + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + + + equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber(src), slot_l_store) + equip_to_slot_or_del(new /obj/item/shield/energy(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/double/full(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/gun/projectile/revolver/mateba(src), slot_belt) + equip_to_slot_or_del(new /obj/item/gun/energy/pulse(src), slot_r_hand) + + var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(src) + L.implant(src) + + var/obj/item/card/id/W = new(src) + W.name = "[real_name]'s ID Card" + W.icon_state = "deathsquad" + W.assignment = "Death Commando" + W.access = get_centcom_access(W.assignment) + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + return 1 diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index df0587787f7d..73ee5edb82d1 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -1,169 +1,169 @@ -//STRIKE TEAMS - -#define SYNDICATE_COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future -var/global/sent_syndicate_strike_team = 0 -/client/proc/syndicate_strike_team() - set category = "Event" - set name = "Spawn Syndicate Strike Team" - set desc = "Spawns a squad of commandos in the Syndicate Mothership if you want to run an admin event." - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - if(!SSticker) - alert("The game hasn't started yet!") - return - if(sent_syndicate_strike_team == 1) - alert("The Syndicate are already sending a team, Mr. Dumbass.") - return - if(alert("Do you want to send in the Syndicate Strike Team? Once enabled, this is irreversible.",,"Yes","No")=="No") - return - alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned syndicates have internals cameras which are viewable through a monitor inside the Syndicate Mothership Bridge. Assigning the team's detailed task is recommended from there. The first one selected/spawned will be the team leader.") - - message_admins("[key_name_admin(usr)] has started to spawn a Syndicate Strike Team.", 1) - - var/input = null - while(!input) - input = sanitize(copytext(input(src, "Please specify which mission the syndicate strike team shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) - if(!input) - if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") - return - - if(sent_syndicate_strike_team) - to_chat(src, "Looks like someone beat you to it.") - return - - var/syndicate_commando_number = SYNDICATE_COMMANDOS_POSSIBLE //for selecting a leader - var/is_leader = TRUE // set to FALSE after leader is spawned - - // Find the nuclear auth code - var/nuke_code - var/temp_code - for(var/obj/machinery/nuclearbomb/N in world) - temp_code = text2num(N.r_code) - if(temp_code)//if it's actually a number. It won't convert any non-numericals. - nuke_code = N.r_code - break - - // Find ghosts willing to be SST - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) - if(!commando_ghosts.len) - to_chat(usr, "Nobody volunteered to join the SST.") - return - - sent_syndicate_strike_team = 1 - - //Spawns commandos and equips them. - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - - if(syndicate_commando_number <= 0) - break - - if(L.name == "Syndicate-Commando") - - if(!commando_ghosts.len) - break - - var/mob/ghost_mob = pick(commando_ghosts) - commando_ghosts -= ghost_mob - - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) - continue - - var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, is_leader) - - if(!new_syndicate_commando) - continue - - new_syndicate_commando.key = ghost_mob.key - new_syndicate_commando.internal = new_syndicate_commando.s_store - new_syndicate_commando.update_action_buttons_icon() - - //So they don't forget their code or mission. - if(nuke_code) - new_syndicate_commando.mind.store_memory("Nuke Code: [nuke_code].") - new_syndicate_commando.mind.store_memory("Mission: [input].") - - to_chat(new_syndicate_commando, "You are an Elite Syndicate [is_leader ? "TEAM LEADER" : "commando"] in the service of the Syndicate. \nYour current mission is: [input]") - new_syndicate_commando.faction += "syndicate" - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(new_syndicate_commando.mind.current) - set_antag_hud(new_syndicate_commando.mind.current, "hudoperative") - new_syndicate_commando.regenerate_icons() - is_leader = FALSE - syndicate_commando_number-- - - message_admins("[key_name_admin(usr)] has spawned a Syndicate strike squad.", 1) - log_admin("[key_name(usr)] used Spawn Syndicate Squad.") - feedback_add_details("admin_verb","SDTHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/create_syndicate_death_commando(obj/spawn_location, is_leader = FALSE) - var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) - var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/syndicate_commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(is_leader) - A.age = rand(35,45) - A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" - else - A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" - A.copy_to(new_syndicate_commando) - - new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. - - //Creates mind stuff. - new_syndicate_commando.mind_initialize() - new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.offstation_role = TRUE - SSticker.mode.traitors |= new_syndicate_commando.mind //Adds them to current traitor list. Which is really the extra antagonist list. - new_syndicate_commando.equip_syndicate_commando(is_leader) - qdel(spawn_location) - return new_syndicate_commando - -/mob/living/carbon/human/proc/equip_syndicate_commando(is_leader = FALSE, full_gear = FALSE) - var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt/syndteam(src) - R.set_frequency(SYNDTEAM_FREQ) - equip_to_slot_or_del(R, slot_l_ear) - equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(src), slot_w_uniform) - if(!full_gear) - equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) - - equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) - equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(src), slot_in_backpack) - - equip_to_slot_or_del(new /obj/item/gun/projectile/revolver(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - if(is_leader) - equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) - else - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/card/emag(src), slot_r_store) - equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber/red(src), slot_l_store) - - if(full_gear) - equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(src), slot_wear_mask) - equip_to_slot_or_del(new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src), slot_wear_suit) - equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) - equip_to_slot_or_del(new /obj/item/storage/belt/military/sst(src), slot_belt) - equip_to_slot_or_del(new /obj/item/tank/jetpack/oxygen/harness(src), slot_s_store) - equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/syndie/advance(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/l6_saw(src), slot_r_hand) - equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45(src), slot_in_backpack) - - var/obj/item/implant/dust/D = new /obj/item/implant/dust(src) - D.implant(src) - var/obj/item/card/id/syndicate/W = new(src) //Untrackable by AI - W.name = "[real_name]'s ID Card" - W.icon_state = "syndie" - W.assignment = "Syndicate Commando" - W.access += get_syndicate_access(W.assignment) - W.registered_name = real_name - equip_to_slot_or_del(W, slot_wear_id) - - return 1 +//STRIKE TEAMS + +#define SYNDICATE_COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future +GLOBAL_VAR_INIT(sent_syndicate_strike_team, 0) +/client/proc/syndicate_strike_team() + set category = "Event" + set name = "Spawn Syndicate Strike Team" + set desc = "Spawns a squad of commandos in the Syndicate Mothership if you want to run an admin event." + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + if(!SSticker) + alert("The game hasn't started yet!") + return + if(GLOB.sent_syndicate_strike_team == 1) + alert("The Syndicate are already sending a team, Mr. Dumbass.") + return + if(alert("Do you want to send in the Syndicate Strike Team? Once enabled, this is irreversible.",,"Yes","No")=="No") + return + alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned syndicates have internals cameras which are viewable through a monitor inside the Syndicate Mothership Bridge. Assigning the team's detailed task is recommended from there. The first one selected/spawned will be the team leader.") + + message_admins("[key_name_admin(usr)] has started to spawn a Syndicate Strike Team.", 1) + + var/input = null + while(!input) + input = sanitize(copytext(input(src, "Please specify which mission the syndicate strike team shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) + if(!input) + if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") + return + + if(GLOB.sent_syndicate_strike_team) + to_chat(src, "Looks like someone beat you to it.") + return + + var/syndicate_commando_number = SYNDICATE_COMMANDOS_POSSIBLE //for selecting a leader + var/is_leader = TRUE // set to FALSE after leader is spawned + + // Find the nuclear auth code + var/nuke_code + var/temp_code + for(var/obj/machinery/nuclearbomb/N in world) + temp_code = text2num(N.r_code) + if(temp_code)//if it's actually a number. It won't convert any non-numericals. + nuke_code = N.r_code + break + + // Find ghosts willing to be SST + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, GLOB.role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + if(!commando_ghosts.len) + to_chat(usr, "Nobody volunteered to join the SST.") + return + + GLOB.sent_syndicate_strike_team = 1 + + //Spawns commandos and equips them. + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + + if(syndicate_commando_number <= 0) + break + + if(L.name == "Syndicate-Commando") + + if(!commando_ghosts.len) + break + + var/mob/ghost_mob = pick(commando_ghosts) + commando_ghosts -= ghost_mob + + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) + continue + + var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, is_leader) + + if(!new_syndicate_commando) + continue + + new_syndicate_commando.key = ghost_mob.key + new_syndicate_commando.internal = new_syndicate_commando.s_store + new_syndicate_commando.update_action_buttons_icon() + + //So they don't forget their code or mission. + if(nuke_code) + new_syndicate_commando.mind.store_memory("Nuke Code: [nuke_code].") + new_syndicate_commando.mind.store_memory("Mission: [input].") + + to_chat(new_syndicate_commando, "You are an Elite Syndicate [is_leader ? "TEAM LEADER" : "commando"] in the service of the Syndicate. \nYour current mission is: [input]") + new_syndicate_commando.faction += "syndicate" + var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS] + opshud.join_hud(new_syndicate_commando.mind.current) + set_antag_hud(new_syndicate_commando.mind.current, "hudoperative") + new_syndicate_commando.regenerate_icons() + is_leader = FALSE + syndicate_commando_number-- + + message_admins("[key_name_admin(usr)] has spawned a Syndicate strike squad.", 1) + log_admin("[key_name(usr)] used Spawn Syndicate Squad.") + feedback_add_details("admin_verb","SDTHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/create_syndicate_death_commando(obj/spawn_location, is_leader = FALSE) + var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) + var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/syndicate_commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(is_leader) + A.age = rand(35,45) + A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" + else + A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" + A.copy_to(new_syndicate_commando) + + new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. + + //Creates mind stuff. + new_syndicate_commando.mind_initialize() + new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.offstation_role = TRUE + SSticker.mode.traitors |= new_syndicate_commando.mind //Adds them to current traitor list. Which is really the extra antagonist list. + new_syndicate_commando.equip_syndicate_commando(is_leader) + qdel(spawn_location) + return new_syndicate_commando + +/mob/living/carbon/human/proc/equip_syndicate_commando(is_leader = FALSE, full_gear = FALSE) + var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt/syndteam(src) + R.set_frequency(SYNDTEAM_FREQ) + equip_to_slot_or_del(R, slot_l_ear) + equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(src), slot_w_uniform) + if(!full_gear) + equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) + + equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) + equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(src), slot_in_backpack) + + equip_to_slot_or_del(new /obj/item/gun/projectile/revolver(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + if(is_leader) + equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) + else + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/card/emag(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber/red(src), slot_l_store) + + if(full_gear) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) + equip_to_slot_or_del(new /obj/item/storage/belt/military/sst(src), slot_belt) + equip_to_slot_or_del(new /obj/item/tank/jetpack/oxygen/harness(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/syndie/advance(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/l6_saw(src), slot_r_hand) + equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45(src), slot_in_backpack) + + var/obj/item/implant/dust/D = new /obj/item/implant/dust(src) + D.implant(src) + var/obj/item/card/id/syndicate/W = new(src) //Untrackable by AI + W.name = "[real_name]'s ID Card" + W.icon_state = "syndie" + W.assignment = "Syndicate Commando" + W.access += get_syndicate_access(W.assignment) + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + return 1 diff --git a/code/modules/admin/verbs/ticklag.dm b/code/modules/admin/verbs/ticklag.dm index 316adf935b07..e19291c159ed 100644 --- a/code/modules/admin/verbs/ticklag.dm +++ b/code/modules/admin/verbs/ticklag.dm @@ -1,21 +1,21 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn - -/client/proc/ticklag() - set category = "Debug" - set name = "Set Ticklag" - set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" - - if(!check_rights(R_DEBUG)) return - - var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null - //I've used ticks of 2 before to help with serious singulo lags - if(newtick && newtick <= 2 && newtick > 0) - log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - message_admins("[key_name_admin(src)] has modified world.tick_lag to [newtick]", 0) - world.tick_lag = newtick - feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else - to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") - - +//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn + +/client/proc/ticklag() + set category = "Debug" + set name = "Set Ticklag" + set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" + + if(!check_rights(R_DEBUG)) return + + var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null + //I've used ticks of 2 before to help with serious singulo lags + if(newtick && newtick <= 2 && newtick > 0) + log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) + message_admins("[key_name_admin(src)] has modified world.tick_lag to [newtick]", 0) + world.tick_lag = newtick + feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else + to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") + + diff --git a/code/modules/admin/verbs/toggledebugverbs.dm b/code/modules/admin/verbs/toggledebugverbs.dm index 85c61ac978f1..216f5c3e4b09 100644 --- a/code/modules/admin/verbs/toggledebugverbs.dm +++ b/code/modules/admin/verbs/toggledebugverbs.dm @@ -1,4 +1,4 @@ -var/list/admin_verbs_show_debug_verbs = list( +GLOBAL_LIST_INIT(admin_verbs_show_debug_verbs, list( /client/proc/camera_view, /client/proc/sec_camera_report, /client/proc/intercom_view, @@ -22,7 +22,7 @@ var/list/admin_verbs_show_debug_verbs = list( /client/proc/admin_redo_space_transitions, /client/proc/make_turf_space_map, /client/proc/vv_by_ref -) +)) // Would be nice to make this a permanent admin pref so we don't need to click it each time /client/proc/enable_debug_verbs() @@ -32,6 +32,6 @@ var/list/admin_verbs_show_debug_verbs = list( if(!check_rights(R_DEBUG)) return - verbs += admin_verbs_show_debug_verbs + verbs += GLOB.admin_verbs_show_debug_verbs feedback_add_details("admin_verb","mDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 98a4bf82e981..5eedd9f28119 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -1,22 +1,22 @@ -/client/proc/triple_ai() - set category = "Event" - set name = "Create AI Triumvirate" - - if(SSticker.current_state > GAME_STATE_PREGAME) - to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") - return - - if(SSjobs && SSticker) - var/datum/job/job = SSjobs.GetJob("AI") - if(!job) - to_chat(usr, "Unable to locate the AI job") - return - if(SSticker.triai) - SSticker.triai = 0 - to_chat(usr, "Only one AI will be spawned at round start.") - message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) - else - SSticker.triai = 1 - to_chat(usr, "There will be an AI Triumvirate at round start.") - message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) - return +/client/proc/triple_ai() + set category = "Event" + set name = "Create AI Triumvirate" + + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") + return + + if(SSjobs && SSticker) + var/datum/job/job = SSjobs.GetJob("AI") + if(!job) + to_chat(usr, "Unable to locate the AI job") + return + if(SSticker.triai) + SSticker.triai = 0 + to_chat(usr, "Only one AI will be spawned at round start.") + message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) + else + SSticker.triai = 1 + to_chat(usr, "There will be an AI Triumvirate at round start.") + message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) + return diff --git a/code/modules/admin/verbs/vox_raiders.dm b/code/modules/admin/verbs/vox_raiders.dm index 3e42ebe65fbf..048939a7c6d3 100644 --- a/code/modules/admin/verbs/vox_raiders.dm +++ b/code/modules/admin/verbs/vox_raiders.dm @@ -1,4 +1,4 @@ -var/global/vox_tick = 1 +GLOBAL_VAR_INIT(vox_tick, 1) /mob/living/carbon/human/proc/equip_vox_raider() @@ -10,7 +10,7 @@ var/global/vox_tick = 1 equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/vox(src), slot_shoes) // REPLACE THESE WITH CODED VOX ALTERNATIVES. equip_to_slot_or_del(new /obj/item/clothing/gloves/color/yellow/vox(src), slot_gloves) // AS ABOVE. - switch(vox_tick) + switch(GLOB.vox_tick) if(1) // Vox raider! equip_to_slot_or_del(new /obj/item/clothing/suit/space/vox/carapace(src), slot_wear_suit) equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/vox/carapace(src), slot_head) @@ -59,7 +59,7 @@ var/global/vox_tick = 1 W.registered_user = src equip_to_slot_or_del(W, slot_wear_id) - vox_tick++ - if(vox_tick > 4) vox_tick = 1 + GLOB.vox_tick++ + if(GLOB.vox_tick > 4) GLOB.vox_tick = 1 return 1 diff --git a/code/modules/admin/watchlist.dm b/code/modules/admin/watchlist.dm index a908c2c264b0..002a3b7ffe42 100644 --- a/code/modules/admin/watchlist.dm +++ b/code/modules/admin/watchlist.dm @@ -6,7 +6,7 @@ if(!new_ckey) return new_ckey = sanitizeSQL(new_ckey) - var/DBQuery/query_watchfind = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'") + var/DBQuery/query_watchfind = GLOB.dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ckey = '[new_ckey]'") if(!query_watchfind.Execute()) var/err = query_watchfind.ErrorMsg() log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n") @@ -29,7 +29,7 @@ if(!adminckey) return var/admin_sql_ckey = sanitizeSQL(adminckey) - var/DBQuery/query_watchadd = dbcon.NewQuery("INSERT INTO [format_table_name("watch")] (ckey, reason, adminckey, timestamp) VALUES ('[target_sql_ckey]', '[reason]', '[admin_sql_ckey]', '[timestamp]')") + var/DBQuery/query_watchadd = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("watch")] (ckey, reason, adminckey, timestamp) VALUES ('[target_sql_ckey]', '[reason]', '[admin_sql_ckey]', '[timestamp]')") if(!query_watchadd.Execute()) var/err = query_watchadd.ErrorMsg() log_game("SQL ERROR during adding new watch entry. Error : \[[err]\]\n") @@ -43,7 +43,7 @@ if(!check_rights(R_ADMIN)) return var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_watchdel = dbcon.NewQuery("DELETE FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watchdel = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") if(!query_watchdel.Execute()) var/err = query_watchdel.ErrorMsg() log_game("SQL ERROR during removing watch entry. Error : \[[err]\]\n") @@ -57,7 +57,7 @@ if(!check_rights(R_ADMIN)) return var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_watchreason = dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watchreason = GLOB.dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") if(!query_watchreason.Execute()) var/err = query_watchreason.ErrorMsg() log_game("SQL ERROR obtaining reason from watch table. Error : \[[err]\]\n") @@ -71,7 +71,7 @@ var/sql_ckey = sanitizeSQL(usr.ckey) var/edit_text = "Edited by [sql_ckey] on [SQLtime()] from \"[watch_reason]\" to \"[new_reason]\"" edit_text = sanitizeSQL(edit_text) - var/DBQuery/query_watchupdate = dbcon.NewQuery("UPDATE [format_table_name("watch")] SET reason = '[new_reason]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watchupdate = GLOB.dbcon.NewQuery("UPDATE [format_table_name("watch")] SET reason = '[new_reason]', last_editor = '[sql_ckey]', edits = CONCAT(IFNULL(edits,''),'[edit_text]') WHERE ckey = '[target_sql_ckey]'") if(!query_watchupdate.Execute()) var/err = query_watchupdate.ErrorMsg() log_game("SQL ERROR editing watchlist reason. Error : \[[err]\]\n") @@ -96,7 +96,7 @@ else search = "^." search = sanitizeSQL(search) - var/DBQuery/query_watchlist = dbcon.NewQuery("SELECT ckey, reason, adminckey, timestamp, last_editor FROM [format_table_name("watch")] WHERE ckey REGEXP '[search]' ORDER BY ckey") + var/DBQuery/query_watchlist = GLOB.dbcon.NewQuery("SELECT ckey, reason, adminckey, timestamp, last_editor FROM [format_table_name("watch")] WHERE ckey REGEXP '[search]' ORDER BY ckey") if(!query_watchlist.Execute()) var/err = query_watchlist.ErrorMsg() log_game("SQL ERROR obtaining ckey, reason, adminckey, timestamp, last_editor from watch table. Error : \[[err]\]\n") @@ -115,7 +115,7 @@ /proc/check_watchlist(target_ckey) var/target_sql_ckey = sanitizeSQL(target_ckey) - var/DBQuery/query_watch = dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") + var/DBQuery/query_watch = GLOB.dbcon.NewQuery("SELECT reason FROM [format_table_name("watch")] WHERE ckey = '[target_sql_ckey]'") if(!query_watch.Execute()) var/err = query_watch.ErrorMsg() log_game("SQL ERROR obtaining reason from watch table. Error : \[[err]\]\n") diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm index 6d8f151f0b86..bef53ad466fa 100644 --- a/code/modules/alarm/camera_alarm.dm +++ b/code/modules/alarm/camera_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/camera - category = "Camera Alarms" \ No newline at end of file + category = "Camera Alarms" diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm index 0acbbe5f221f..fd7e6febe48e 100644 --- a/code/modules/alarm/motion_alarm.dm +++ b/code/modules/alarm/motion_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/motion - category = "Motion Alarms" \ No newline at end of file + category = "Motion Alarms" diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 39053fa79d9e..4a6fe7d66403 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -134,4 +134,4 @@ GLOBAL_LIST_EMPTY(antagonists) //Displayed at the end of roundend_category section /datum/antagonist/proc/roundend_report_footer() - return \ No newline at end of file + return diff --git a/code/modules/antagonists/_common/antag_helpers.dm b/code/modules/antagonists/_common/antag_helpers.dm index 134ba1d7d904..af00cbf3190f 100644 --- a/code/modules/antagonists/_common/antag_helpers.dm +++ b/code/modules/antagonists/_common/antag_helpers.dm @@ -16,4 +16,4 @@ continue var/datum/team/T = A.get_team() if(!team_type || istype(T, team_type)) - . |= T \ No newline at end of file + . |= T diff --git a/code/modules/antagonists/_common/antag_hud.dm b/code/modules/antagonists/_common/antag_hud.dm index 89ad9af15ab8..0cb800aa38d4 100644 --- a/code/modules/antagonists/_common/antag_hud.dm +++ b/code/modules/antagonists/_common/antag_hud.dm @@ -47,11 +47,11 @@ newhud.join_hud(current) /datum/mind/proc/leave_all_huds() - for(var/datum/atom_hud/antag/hud in huds) + for(var/datum/atom_hud/antag/hud in GLOB.huds) if(current in hud.hudusers) hud.leave_hud(current) - for(var/datum/atom_hud/data/hud in huds) + for(var/datum/atom_hud/data/hud in GLOB.huds) if(current in hud.hudusers) hud.remove_hud_from(current) @@ -77,4 +77,4 @@ /datum/mindslaves/proc/leave_serv_hud(datum/mind/free_mind) thrallhud.leave_hud(free_mind.current) - set_antag_hud(free_mind.current, null) \ No newline at end of file + set_antag_hud(free_mind.current, null) diff --git a/code/modules/antagonists/_common/antag_spawner.dm b/code/modules/antagonists/_common/antag_spawner.dm index d57c3124f61b..beefc41735b5 100644 --- a/code/modules/antagonists/_common/antag_spawner.dm +++ b/code/modules/antagonists/_common/antag_spawner.dm @@ -266,7 +266,7 @@ to_chat(user, "The sludge does not respond to your attempt to awake it. Perhaps you should try again later.") /obj/item/antag_spawner/morph/spawn_antag(client/C, turf/T, type = "", mob/user) - var/mob/living/simple_animal/hostile/morph/wizard/M = new /mob/living/simple_animal/hostile/morph/wizard(pick(xeno_spawn)) + var/mob/living/simple_animal/hostile/morph/wizard/M = new /mob/living/simple_animal/hostile/morph/wizard(pick(GLOB.xeno_spawn)) M.key = C.key M.mind.assigned_role = SPECIAL_ROLE_MORPH M.mind.special_role = SPECIAL_ROLE_MORPH diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm index 7d08e18834c4..1d30a6fe3341 100644 --- a/code/modules/antagonists/survivalist/survivalist.dm +++ b/code/modules/antagonists/survivalist/survivalist.dm @@ -38,4 +38,4 @@ var/datum/objective/steal_five_of_type/summon_magic/magic = new magic.owner = owner objectives += magic - ..() \ No newline at end of file + ..() diff --git a/code/modules/antagonists/traitor/datum_mindslave.dm b/code/modules/antagonists/traitor/datum_mindslave.dm index 5efd35715ad3..6fcb4bac63a6 100644 --- a/code/modules/antagonists/traitor/datum_mindslave.dm +++ b/code/modules/antagonists/traitor/datum_mindslave.dm @@ -44,10 +44,10 @@ owner.objectives -= O /datum/antagonist/mindslave/proc/update_mindslave_icons_added() - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.join_hud(owner.current, null) set_antag_hud(owner.current, "hudmindslave") /datum/antagonist/mindslave/proc/update_mindslave_icons_removed() - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.leave_hud(owner.current, null) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 3cd7998edd8b..e13fb7013174 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -118,7 +118,7 @@ var/objective_amount = config.traitor_objectives_amount - + if(is_hijacker && objective_count <= objective_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount if (!(locate(/datum/objective/hijack) in objectives)) var/datum/objective/hijack/hijack_objective = new @@ -187,7 +187,7 @@ destroy_objective.owner = owner destroy_objective.find_target() if("[destroy_objective]" in assigned_targets) // Is this target already in their list of assigned targets? If so, don't add this objective and return - return 0 + return 0 else if(destroy_objective.target) // Is the target a real one and not null? If so, add it to our list of targets to avoid duplicate targets assigned_targets.Add("[destroy_objective.target]") // This logic is applied to all traitor objectives including steal objectives add_objective(destroy_objective) @@ -221,7 +221,7 @@ else if(kill_objective.target) assigned_targets.Add("[kill_objective.target]") add_objective(kill_objective) - + else var/datum/objective/steal/steal_objective = new steal_objective.owner = owner @@ -231,7 +231,7 @@ else if(steal_objective.steal_target) assigned_targets.Add("[steal_objective.steal_target]") add_objective(steal_objective) - + /datum/antagonist/traitor/proc/forge_single_AI_objective() . = 1 @@ -251,13 +251,13 @@ /datum/antagonist/traitor/proc/update_traitor_icons_added(datum/mind/traitor_mind) - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.join_hud(owner.current, null) set_antag_hud(owner.current, "hudsyndicate") /datum/antagonist/traitor/proc/update_traitor_icons_removed(datum/mind/traitor_mind) - var/datum/atom_hud/antag/traitorhud = huds[ANTAG_HUD_TRAITOR] + var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR] traitorhud.leave_hud(owner.current, null) set_antag_hud(owner.current, null) @@ -273,7 +273,7 @@ if(should_equip) equip_traitor() owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE) - + /datum/antagonist/traitor/proc/give_codewords() if(!owner.current) @@ -309,7 +309,7 @@ if(traitor_kind == TRAITOR_HUMAN) var/mob/living/carbon/human/traitor_mob = owner.current - + // find a radio! toolbox(es), backpack, belt, headset var/obj/item/R = locate(/obj/item/pda) in traitor_mob.contents //Hide the uplink in a PDA if available, otherwise radio if(!R) diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm index 1697eb897c9a..cb6fad39d341 100644 --- a/code/modules/antagonists/wishgranter/wishgranter.dm +++ b/code/modules/antagonists/wishgranter/wishgranter.dm @@ -22,57 +22,57 @@ if(!istype(H)) return H.ignore_gene_stability = TRUE - H.dna.SetSEState(HULKBLOCK, TRUE) - genemutcheck(H, HULKBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.hulkblock, TRUE) + genemutcheck(H, GLOB.hulkblock, null, MUTCHK_FORCED) - H.dna.SetSEState(XRAYBLOCK, TRUE) - genemutcheck(H, XRAYBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.xrayblock, TRUE) + genemutcheck(H, GLOB.xrayblock, null, MUTCHK_FORCED) - H.dna.SetSEState(FIREBLOCK, TRUE) - genemutcheck(H, FIREBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.fireblock, TRUE) + genemutcheck(H, GLOB.fireblock, null, MUTCHK_FORCED) - H.dna.SetSEState(COLDBLOCK, TRUE) - genemutcheck(H, COLDBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.coldblock, TRUE) + genemutcheck(H, GLOB.coldblock, null, MUTCHK_FORCED) - H.dna.SetSEState(TELEBLOCK, TRUE) - genemutcheck(H, TELEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.teleblock, TRUE) + genemutcheck(H, GLOB.teleblock, null, MUTCHK_FORCED) - H.dna.SetSEState(INCREASERUNBLOCK, TRUE) - genemutcheck(H, INCREASERUNBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.increaserunblock, TRUE) + genemutcheck(H, GLOB.increaserunblock, null, MUTCHK_FORCED) - H.dna.SetSEState(BREATHLESSBLOCK, TRUE) - genemutcheck(H, BREATHLESSBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.breathlessblock, TRUE) + genemutcheck(H, GLOB.breathlessblock, null, MUTCHK_FORCED) - H.dna.SetSEState(REGENERATEBLOCK, TRUE) - genemutcheck(H, REGENERATEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.regenerateblock, TRUE) + genemutcheck(H, GLOB.regenerateblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SHOCKIMMUNITYBLOCK, TRUE) - genemutcheck(H, SHOCKIMMUNITYBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.shockimmunityblock, TRUE) + genemutcheck(H, GLOB.shockimmunityblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SMALLSIZEBLOCK, TRUE) - genemutcheck(H, SMALLSIZEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.smallsizeblock, TRUE) + genemutcheck(H, GLOB.smallsizeblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SOBERBLOCK, TRUE) - genemutcheck(H, SOBERBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.soberblock, TRUE) + genemutcheck(H, GLOB.soberblock, null, MUTCHK_FORCED) - H.dna.SetSEState(PSYRESISTBLOCK, TRUE) - genemutcheck(H, PSYRESISTBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.psyresistblock, TRUE) + genemutcheck(H, GLOB.psyresistblock, null, MUTCHK_FORCED) - H.dna.SetSEState(SHADOWBLOCK, TRUE) - genemutcheck(H, SHADOWBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.shadowblock, TRUE) + genemutcheck(H, GLOB.shadowblock, null, MUTCHK_FORCED) - H.dna.SetSEState(CRYOBLOCK, TRUE) - genemutcheck(H, CRYOBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.cryoblock, TRUE) + genemutcheck(H, GLOB.cryoblock, null, MUTCHK_FORCED) - H.dna.SetSEState(EATBLOCK, TRUE) - genemutcheck(H, EATBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.eatblock, TRUE) + genemutcheck(H, GLOB.eatblock, null, MUTCHK_FORCED) - H.dna.SetSEState(JUMPBLOCK, TRUE) - genemutcheck(H, JUMPBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.jumpblock, TRUE) + genemutcheck(H, GLOB.jumpblock, null, MUTCHK_FORCED) - H.dna.SetSEState(IMMOLATEBLOCK, TRUE) - genemutcheck(H, IMMOLATEBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.immolateblock, TRUE) + genemutcheck(H, GLOB.immolateblock, null, MUTCHK_FORCED) H.mutations.Add(LASER) H.update_mutations() - H.update_body() \ No newline at end of file + H.update_body() diff --git a/code/modules/arcade/arcade_prize.dm b/code/modules/arcade/arcade_prize.dm index a91e392e1250..832d1c466495 100644 --- a/code/modules/arcade/arcade_prize.dm +++ b/code/modules/arcade/arcade_prize.dm @@ -20,7 +20,7 @@ opening = 1 playsound(src.loc, 'sound/items/bubblewrap.ogg', 30, 1, extrarange = -4, falloff = 10) icon_state = "prizeconfetti" - src.color = pick(random_color_list) + src.color = pick(GLOB.random_color_list) var/prize_inside = pick(possible_contents) spawn(10) user.unEquip(src) @@ -85,4 +85,4 @@ if(25 to 74) icon_state = "tickets_3" // Buncha tickets else - icon_state = "tickets_4" // Ticket snake \ No newline at end of file + icon_state = "tickets_4" // Ticket snake diff --git a/code/modules/arcade/claw_game.dm b/code/modules/arcade/claw_game.dm index 7339b31dc77c..822011be3f68 100644 --- a/code/modules/arcade/claw_game.dm +++ b/code/modules/arcade/claw_game.dm @@ -1,4 +1,4 @@ -/var/claw_game_html = null +GLOBAL_VAR(claw_game_html) /obj/machinery/arcade/claw name = "Claw Game" @@ -30,8 +30,8 @@ component_parts += new /obj/item/stack/sheet/glass(null, 1) RefreshParts() - if(!claw_game_html) - claw_game_html = file2text('code/modules/arcade/crane.html') + if(!GLOB.claw_game_html) + GLOB.claw_game_html = file2text('code/modules/arcade/crane.html') /obj/machinery/arcade/claw/RefreshParts() var/bin_upgrades = 0 @@ -65,7 +65,7 @@ user << browse_rsc('page.css') for(var/i in 1 to img_resources.len) user << browse_rsc(img_resources[i]) - var/my_game_html = replacetext(claw_game_html, "/* ref src */", UID()) + var/my_game_html = replacetext(GLOB.claw_game_html, "/* ref src */", UID()) user << browse(my_game_html, "window=[window_name];size=915x600;can_resize=0") /obj/machinery/arcade/claw/Topic(href, list/href_list) @@ -76,4 +76,4 @@ if(!isnull(prize_won)) close_game() if(prize_won == "1") - win() \ No newline at end of file + win() diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm index 0d6e91b60d99..94337a5cfac6 100644 --- a/code/modules/arcade/mob_hunt/battle_computer.dm +++ b/code/modules/arcade/mob_hunt/battle_computer.dm @@ -302,4 +302,4 @@ to_chat(user, "This mob is already at maximum health!") return patient.mob_data.cur_health = patient.mob_data.max_health - to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") \ No newline at end of file + to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") diff --git a/code/modules/arcade/mob_hunt/mob_datums.dm b/code/modules/arcade/mob_hunt/mob_datums.dm index dad3f5287964..a50fab183e65 100644 --- a/code/modules/arcade/mob_hunt/mob_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_datums.dm @@ -108,7 +108,7 @@ /datum/mob_hunt/proc/get_possible_areas() var/list/possible_areas = list() //setup, sets all station areas (and subtypes) to weight 1 - for(var/A in the_station_areas) + for(var/A in GLOB.the_station_areas) if(A == /area/holodeck) //don't allow holodeck areas as possible spawns since it will allow it to spawn in the holodeck rooms on z2 as well continue if(A in possible_areas) @@ -498,4 +498,4 @@ turf_blacklist = list() area_whitelist = list() turf_whitelist = list() - lifetime = 2000 \ No newline at end of file + lifetime = 2000 diff --git a/code/modules/arcade/mob_hunt/mob_type_datums.dm b/code/modules/arcade/mob_hunt/mob_type_datums.dm index 9d27a844ea8d..0ae84b1b7645 100644 --- a/code/modules/arcade/mob_hunt/mob_type_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_type_datums.dm @@ -253,4 +253,4 @@ TYPE_FLYING, TYPE_BLUESPACE, TYPE_STEEL) - immunity = list(TYPE_POISON) \ No newline at end of file + immunity = list(TYPE_POISON) diff --git a/code/modules/arcade/page.css b/code/modules/arcade/page.css index 17e77bcb0386..e3dfcac1cce8 100644 --- a/code/modules/arcade/page.css +++ b/code/modules/arcade/page.css @@ -89,4 +89,4 @@ body { .button { cursor: pointer; -} \ No newline at end of file +} diff --git a/code/modules/arcade/prize_counter.dm b/code/modules/arcade/prize_counter.dm index 4d1d6973909e..9c8a4dea685e 100644 --- a/code/modules/arcade/prize_counter.dm +++ b/code/modules/arcade/prize_counter.dm @@ -140,11 +140,11 @@ th.cost.toomuch {background:maroon;}
    " var/firstcat = 1 - for(var/category in loadout_categories) + for(var/category in GLOB.loadout_categories) if(firstcat) firstcat = 0 else @@ -536,7 +537,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts dat += " [category] " dat += "

    [LC.category]

    #[D.account_number]
    @@ -369,7 +369,7 @@ var/global/pos_sales = 0 logindata={"[logged_in.name]"} var/dat = POS_HEADER + {"
    Item
    " - var/DBQuery/query = dbcon.NewQuery("SELECT id, title, flagged FROM [format_table_name("library")] WHERE flagged > 0 ORDER BY flagged DESC") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, title, flagged FROM [format_table_name("library")] WHERE flagged > 0 ORDER BY flagged DESC") if(!query.Execute()) var/err = query.ErrorMsg() log_game("SQL ERROR getting flagged books. Error : \[[err]\]\n") diff --git a/code/modules/library/codex_gigas.dm b/code/modules/library/codex_gigas.dm index e908f35b60bf..3e6ed79b0408 100644 --- a/code/modules/library/codex_gigas.dm +++ b/code/modules/library/codex_gigas.dm @@ -45,7 +45,7 @@ if(!prob(correctness)) usedName += "x" var/datum/devilinfo/devil = devilInfo(usedName, 0) - user << browse("Information on [devilName]


    [lawlorify[LORE][devil.ban]]
    [lawlorify[LORE][devil.bane]]
    [lawlorify[LORE][devil.obligation]]
    [lawlorify[LORE][devil.banish]]", "window=book") + user << browse("Information on [devilName]


    [GLOB.lawlorify[LORE][devil.ban]]
    [GLOB.lawlorify[LORE][devil.bane]]
    [GLOB.lawlorify[LORE][devil.obligation]]
    [GLOB.lawlorify[LORE][devil.banish]]", "window=book") inUse = 0 sleep(10) if(!prob(willpower)) diff --git a/code/modules/library/computers/base.dm b/code/modules/library/computers/base.dm index 5bb8019cd58c..ff1e9bcb94fd 100644 --- a/code/modules/library/computers/base.dm +++ b/code/modules/library/computers/base.dm @@ -47,7 +47,7 @@ var/sql = "SELECT id, author, title, category, ckey, flagged FROM [format_table_name("library")] [searchquery] LIMIT [(page_num - 1) * LIBRARY_BOOKS_PER_PAGE], [LIBRARY_BOOKS_PER_PAGE]" // Pagination - var/DBQuery/_query = dbcon.NewQuery(sql) + var/DBQuery/_query = GLOB.dbcon.NewQuery(sql) _query.Execute() if(_query.ErrorMsg()) log_world(_query.ErrorMsg()) @@ -69,7 +69,7 @@ /obj/machinery/computer/library/proc/get_num_results() var/sql = "SELECT COUNT(*) FROM [format_table_name("library")]" - var/DBQuery/_query = dbcon.NewQuery(sql) + var/DBQuery/_query = GLOB.dbcon.NewQuery(sql) _query.Execute() while(_query.NextRow()) return text2num(_query.item[1]) @@ -90,4 +90,4 @@ return pagelist /obj/machinery/computer/library/proc/getBookByID(var/id as text) - return library_catalog.getBookByID(id) \ No newline at end of file + return GLOB.library_catalog.getBookByID(id) diff --git a/code/modules/library/computers/checkout.dm b/code/modules/library/computers/checkout.dm index 638aaeee7c3b..99f7e2cb8fe2 100644 --- a/code/modules/library/computers/checkout.dm +++ b/code/modules/library/computers/checkout.dm @@ -1,465 +1,465 @@ -/* - * Library Computer - */ -/obj/machinery/computer/library/checkout - name = "Check-In/Out Computer" - icon = 'icons/obj/library.dmi' - icon_state = "computer" - anchored = 1 - density = 1 - var/arcanecheckout = 0 - //var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book - var/buffer_book - var/buffer_mob - var/upload_category = "Fiction" - var/list/checkouts = list() - var/list/inventory = list() - var/checkoutperiod = 5 // In minutes - var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive - - var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl - var/booklist - -/obj/machinery/computer/library/checkout/attack_hand(var/mob/user as mob) - if(..()) - return - interact(user) - -/obj/machinery/computer/library/checkout/interact(var/mob/user) - if(interact_check(user)) - return - - var/dat = "" - switch(screenstate) - if(0) - // Main Menu - - dat += {"
      -
    1. View General Inventory
    2. -
    3. View Checked Out Inventory
    4. -
    5. Check out a Book
    6. -
    7. Connect to External Archive
    8. -
    9. Upload New Title to Archive
    10. -
    11. Print a Bible
    12. -
    13. Print a Manual
    14. "} - if(src.emagged) - dat += "
    15. Access the Forbidden Lore Vault
    16. " - dat += "
    " - - if(src.arcanecheckout) - new /obj/item/tome(src.loc) - to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") - user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2) - src.arcanecheckout = 0 - if(1) - // Inventory - dat += "

    Inventory

    " - for(var/obj/item/book/b in inventory) - dat += "[b.name] (Delete)
    " - dat += "(Return to main menu)
    " - if(2) - // Checked Out - dat += "

    Checked Out Books


    " - for(var/datum/borrowbook/b in checkouts) - var/timetaken = world.time - b.getdate - //timetaken *= 10 - timetaken /= 600 - timetaken = round(timetaken) - var/timedue = b.duedate - world.time - //timedue *= 10 - timedue /= 600 - if(timedue <= 0) - timedue = "(OVERDUE) [timedue]" - else - timedue = round(timedue) - - dat += {"\"[b.bookname]\", Checked out to: [b.mobname]
    --- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
    - (Check In)

    "} - dat += "(Return to main menu)
    " - if(3) - // Check Out a Book - - dat += {"

    Check Out a Book


    - Book: [src.buffer_book] - \[Edit\]
    - Recipient: [src.buffer_mob] - \[Edit\]
    - Checkout Date : [world.time/600]
    - Due Date: [(world.time + checkoutperiod)/600]
    - (Checkout Period: [checkoutperiod] minutes) (+/-) - (Commit Entry)
    - (Return to main menu)
    "} - if(4) - dat += "

    External Archive

    " - if(!dbcon.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." - else - num_results = src.get_num_results() - num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE) - dat += {""} - var/pagelist = get_pagelist() - - dat += {"

    Search Settings


    - Filter by Title: [query.title]
    - Filter by Category: [query.category]
    - Filter by Author: [query.author]
    - \[Start Search\]
    "} - dat += pagelist - - dat += {" - - - - "} - - dat += {"
    ISBNTitleTotal FlagsOptions
    - - - - - - "} - - for(var/datum/cachedbook/CB in get_page(page_num)) - var/author = CB.author - var/controls = "\[Order\]" - controls += {" \[Flag[CB.flagged ? "ged" : ""]\]"} - if(check_rights(R_ADMIN, 0, user = user)) - controls += " \[Delete\]" - author += " ([ckey(CB.ckey)]))" - dat += {" - - - - - "} - - dat += "
    AuthorTitleCategoryControls
    [author][CB.title][CB.category][controls]

    [pagelist]" - - dat += "
    (Return to main menu)
    " - if(5) - dat += "

    Upload a New Title

    " - if(!scanner) - for(var/obj/machinery/libraryscanner/S in range(9)) - scanner = S - break - if(!scanner) - dat += "No scanner found within wireless network range.
    " - else if(!scanner.cache) - dat += "No data found in scanner memory.
    " - else - - dat += {"Data marked for upload...
    - Title: [scanner.cache.name]
    "} - if(!scanner.cache.author) - scanner.cache.author = "Anonymous" - - dat += {"Author: [scanner.cache.author]
    - Category: [upload_category]
    - \[Upload\]
    "} - dat += "(Return to main menu)
    " - if(7) - dat += "

    Print a Manual

    " - dat += "" - - var/list/forbidden = list( - /obj/item/book/manual - ) - - if(!emagged) - forbidden |= /obj/item/book/manual/nuclear - - var/manualcount = 1 - var/obj/item/book/manual/M = null - - for(var/manual_type in (typesof(/obj/item/book/manual) - forbidden)) - M = new manual_type() - dat += "" - manualcount++ - QDEL_NULL(M) - dat += "
    [M.title]
    " - dat += "
    (Return to main menu)
    " - - if(8) - - dat += {"

    Accessing Forbidden Lore Vault v 1.3

    - Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

    - Yes.
    - No.
    "} - - var/datum/browser/B = new /datum/browser(user, "library", "Book Inventory Management") - B.set_content(dat) - B.open() - -/obj/machinery/computer/library/checkout/emag_act(mob/user) - if(density && !emagged) - emagged = 1 - to_chat(user, "You override the library computer's printing restrictions.") - -/obj/machinery/computer/library/checkout/attackby(obj/item/W as obj, mob/user as mob) - if(default_unfasten_wrench(user, W)) - power_change() - return - if(istype(W, /obj/item/barcodescanner)) - var/obj/item/barcodescanner/scanner = W - scanner.computer = src - to_chat(user, "[scanner]'s associated machine has been set to [src].") - audible_message("[src] lets out a low, short blip.", 2) - return 1 - else - return ..() - -/obj/machinery/computer/library/checkout/Topic(href, href_list) - if(..()) - usr << browse(null, "window=library") - onclose(usr, "library") - return 1 - - if(href_list["pagenum"]) - if(!num_pages) - page_num = 1 - else - var/pn = text2num(href_list["pagenum"]) - if(!isnull(pn)) - page_num = Clamp(pn, 1, num_pages) - - if(href_list["page"]) - if(num_pages == 0) - page_num = 1 - else - page_num = Clamp(text2num(href_list["page"]), 1, num_pages) - if(href_list["settitle"]) - var/newtitle = input("Enter a title to search for:") as text|null - if(newtitle) - query.title = sanitize(newtitle) - else - query.title = null - if(href_list["setcategory"]) - var/newcategory = input("Choose a category to search for:") in (list("Any") + library_section_names) - if(newcategory == "Any") - query.category = null - else if(newcategory) - query.category = sanitize(newcategory) - if(href_list["setauthor"]) - var/newauthor = input("Enter an author to search for:") as text|null - if(newauthor) - query.author = sanitize(newauthor) - else - query.author = null - - if(href_list["search"]) - num_results = src.get_num_results() - num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE) - page_num = 1 - - screenstate = 4 - if(href_list["del"]) - if(!check_rights(R_ADMIN)) - return - var/datum/cachedbook/target = getBookByID(href_list["del"]) // Sanitized in getBookByID - var/ans = alert(usr, "Are you sure you wish to delete \"[target.title]\", by [target.author]? This cannot be undone.", "Library System", "Yes", "No") - if(ans=="Yes") - var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[target.id]") - var/response = query.Execute() - if(!response) - to_chat(usr, query.ErrorMsg()) - return - log_admin("LIBRARY: [key_name(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!") - message_admins("[key_name_admin(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!") - src.updateUsrDialog() - return - - if(href_list["delbyckey"]) - if(!check_rights(R_ADMIN)) - return - var/tckey = ckey(href_list["delbyckey"]) - var/ans = alert(usr,"Are you sure you wish to delete all books by [tckey]? This cannot be undone.", "Library System", "Yes", "No") - if(ans=="Yes") - var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE ckey='[sanitizeSQL(tckey)]'") - var/response = query.Execute() - if(!response) - to_chat(usr, query.ErrorMsg()) - return - var/affected=query.RowsAffected() - if(affected==0) - to_chat(usr, "Unable to find any matching rows.") - return - log_admin("LIBRARY: [key_name(usr)] has deleted [affected] books written by [tckey]!") - message_admins("[key_name_admin(usr)] has deleted [affected] books written by [tckey]!") - src.updateUsrDialog() - return - - if(href_list["flag"]) - if(!dbcon.IsConnected()) - alert("Connection to Archive has been severed. Aborting.") - return - var/id = href_list["flag"] - if(id) - var/datum/cachedbook/B = getBookByID(id) - if(B) - if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes") - library_catalog.flag_book_by_id(usr, id) - - if(href_list["switchscreen"]) - switch(href_list["switchscreen"]) - if("0") - screenstate = 0 - if("1") - screenstate = 1 - if("2") - screenstate = 2 - if("3") - screenstate = 3 - if("4") - screenstate = 4 - if("5") - screenstate = 5 - if("6") - if(!bibledelay) - - var/obj/item/storage/bible/B = new /obj/item/storage/bible(src.loc) - if(SSticker && ( SSticker.Bible_icon_state && SSticker.Bible_item_state) ) - B.icon_state = SSticker.Bible_icon_state - B.item_state = SSticker.Bible_item_state - B.name = SSticker.Bible_name - B.deity_name = SSticker.Bible_deity_name - - bibledelay = 1 - spawn(60) - bibledelay = 0 - - else - visible_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") - - if("7") - screenstate = 7 - if("8") - screenstate = 8 - if(href_list["arccheckout"]) - if(src.emagged) - src.arcanecheckout = 1 - src.screenstate = 0 - if(href_list["increasetime"]) - checkoutperiod += 1 - if(href_list["decreasetime"]) - checkoutperiod -= 1 - if(checkoutperiod < 1) - checkoutperiod = 1 - if(href_list["editbook"]) - buffer_book = copytext(sanitize(input("Enter the book's title:") as text|null),1,MAX_MESSAGE_LEN) - if(href_list["editmob"]) - buffer_mob = copytext(sanitize(input("Enter the recipient's name:") as text|null),1,MAX_NAME_LEN) - if(href_list["checkout"]) - var/datum/borrowbook/b = new /datum/borrowbook - b.bookname = sanitize(buffer_book) - b.mobname = sanitize(buffer_mob) - b.getdate = world.time - b.duedate = world.time + (checkoutperiod * 600) - checkouts.Add(b) - if(href_list["checkin"]) - var/datum/borrowbook/b = locate(href_list["checkin"]) - checkouts.Remove(b) - if(href_list["delbook"]) - var/obj/item/book/b = locate(href_list["delbook"]) - inventory.Remove(b) - if(href_list["uploadauthor"]) - var/newauthor = copytext(sanitize(input("Enter the author's name: ") as text|null),1,MAX_MESSAGE_LEN) - if(newauthor && scanner) - scanner.cache.author = newauthor - if(href_list["uploadcategory"]) - var/newcategory = input("Choose a category: ") in list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion") - if(newcategory) - upload_category = newcategory - if(href_list["upload"]) - if(scanner) - if(scanner.cache) - var/choice = input("Are you certain you wish to upload this title to the Archive?") in list("Confirm", "Abort") - if(choice == "Confirm") - establish_db_connection() - if(!dbcon.IsConnected()) - alert("Connection to Archive has been severed. Aborting.") - else - var/sqltitle = sanitizeSQL(scanner.cache.name) - var/sqlauthor = sanitizeSQL(scanner.cache.author) - var/sqlcontent = sanitizeSQL(scanner.cache.dat) - var/sqlcategory = sanitizeSQL(upload_category) - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, flagged) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[ckey(usr.key)]', 0)") - var/response = query.Execute() - if(!response) - to_chat(usr, query.ErrorMsg()) - else - log_admin("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length") - message_admins("[key_name_admin(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length") - - if(href_list["id"]) - if(href_list["id"]=="-1") - href_list["id"] = input("Enter your order:") as null|num - if(!href_list["id"]) - return - - if(!dbcon.IsConnected()) - alert("Connection to Archive has been severed. Aborting.") - return - - var/datum/cachedbook/newbook = getBookByID(href_list["id"]) // Sanitized in getBookByID - if(!newbook) - alert("No book found") - return - if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1) - alert("This book is forbidden and cannot be printed.") - return - - if(bibledelay) - audible_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") - else - bibledelay = 1 - spawn(60) - bibledelay = 0 - make_external_book(newbook) - if(href_list["manual"]) - if(!href_list["manual"]) return - var/bookid = href_list["manual"] - - if(!dbcon.IsConnected()) - alert("Connection to Archive has been severed. Aborting.") - return - - var/datum/cachedbook/newbook = getBookByID("M[bookid]") - if(!newbook) - alert("No book found") - return - if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1) - alert("This book is forbidden and cannot be printed.") - return - - if(bibledelay) - for(var/mob/V in hearers(src)) - V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") - else - bibledelay = 1 - spawn(60) - bibledelay = 0 - make_external_book(newbook) - - src.add_fingerprint(usr) - src.updateUsrDialog() - return - -/* - * Library Scanner - */ - -/obj/machinery/computer/library/checkout/proc/make_external_book(var/datum/cachedbook/newbook) - if(!newbook || !newbook.id) - return - var/obj/item/book/B = new newbook.path(loc) - - if(!newbook.programmatic) - B.name = "Book: [newbook.title]" - B.title = newbook.title - B.author = newbook.author - B.dat = newbook.content - B.icon_state = "book[rand(1,16)]" - visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") +/* + * Library Computer + */ +/obj/machinery/computer/library/checkout + name = "Check-In/Out Computer" + icon = 'icons/obj/library.dmi' + icon_state = "computer" + anchored = 1 + density = 1 + var/arcanecheckout = 0 + //var/screenstate = 0 // 0 - Main Menu, 1 - Inventory, 2 - Checked Out, 3 - Check Out a Book + var/buffer_book + var/buffer_mob + var/upload_category = "Fiction" + var/list/checkouts = list() + var/list/inventory = list() + var/checkoutperiod = 5 // In minutes + var/obj/machinery/libraryscanner/scanner // Book scanner that will be used when uploading books to the Archive + + var/bibledelay = 0 // LOL NO SPAM (1 minute delay) -- Doohl + var/booklist + +/obj/machinery/computer/library/checkout/attack_hand(var/mob/user as mob) + if(..()) + return + interact(user) + +/obj/machinery/computer/library/checkout/interact(var/mob/user) + if(interact_check(user)) + return + + var/dat = "" + switch(screenstate) + if(0) + // Main Menu + + dat += {"

      +
    1. View General Inventory
    2. +
    3. View Checked Out Inventory
    4. +
    5. Check out a Book
    6. +
    7. Connect to External Archive
    8. +
    9. Upload New Title to Archive
    10. +
    11. Print a Bible
    12. +
    13. Print a Manual
    14. "} + if(src.emagged) + dat += "
    15. Access the Forbidden Lore Vault
    16. " + dat += "
    " + + if(src.arcanecheckout) + new /obj/item/tome(src.loc) + to_chat(user, "Your sanity barely endures the seconds spent in the vault's browsing window. The only thing to remind you of this when you stop browsing is a dusty old tome sitting on the desk. You don't really remember printing it.") + user.visible_message("[user] stares at the blank screen for a few moments, [user.p_their()] expression frozen in fear. When [user.p_they()] finally awaken[user.p_s()] from it, [user.p_they()] look[user.p_s()] a lot older.", 2) + src.arcanecheckout = 0 + if(1) + // Inventory + dat += "

    Inventory

    " + for(var/obj/item/book/b in inventory) + dat += "[b.name] (Delete)
    " + dat += "(Return to main menu)
    " + if(2) + // Checked Out + dat += "

    Checked Out Books


    " + for(var/datum/borrowbook/b in checkouts) + var/timetaken = world.time - b.getdate + //timetaken *= 10 + timetaken /= 600 + timetaken = round(timetaken) + var/timedue = b.duedate - world.time + //timedue *= 10 + timedue /= 600 + if(timedue <= 0) + timedue = "(OVERDUE) [timedue]" + else + timedue = round(timedue) + + dat += {"\"[b.bookname]\", Checked out to: [b.mobname]
    --- Taken: [timetaken] minutes ago, Due: in [timedue] minutes
    + (Check In)

    "} + dat += "(Return to main menu)
    " + if(3) + // Check Out a Book + + dat += {"

    Check Out a Book


    + Book: [src.buffer_book] + \[Edit\]
    + Recipient: [src.buffer_mob] + \[Edit\]
    + Checkout Date : [world.time/600]
    + Due Date: [(world.time + checkoutperiod)/600]
    + (Checkout Period: [checkoutperiod] minutes) (+/-) + (Commit Entry)
    + (Return to main menu)
    "} + if(4) + dat += "

    External Archive

    " + if(!GLOB.dbcon.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance." + else + num_results = src.get_num_results() + num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE) + dat += {""} + var/pagelist = get_pagelist() + + dat += {"

    Search Settings


    + Filter by Title: [query.title]
    + Filter by Category: [query.category]
    + Filter by Author: [query.author]
    + \[Start Search\]
    "} + dat += pagelist + + dat += {"
    + + + +
    "} + + dat += {" + + + + + + "} + + for(var/datum/cachedbook/CB in get_page(page_num)) + var/author = CB.author + var/controls = "\[Order\]" + controls += {" \[Flag[CB.flagged ? "ged" : ""]\]"} + if(check_rights(R_ADMIN, 0, user = user)) + controls += " \[Delete\]" + author += " ([ckey(CB.ckey)]))" + dat += {" + + + + + "} + + dat += "
    AuthorTitleCategoryControls
    [author][CB.title][CB.category][controls]

    [pagelist]" + + dat += "
    (Return to main menu)
    " + if(5) + dat += "

    Upload a New Title

    " + if(!scanner) + for(var/obj/machinery/libraryscanner/S in range(9)) + scanner = S + break + if(!scanner) + dat += "No scanner found within wireless network range.
    " + else if(!scanner.cache) + dat += "No data found in scanner memory.
    " + else + + dat += {"Data marked for upload...
    + Title: [scanner.cache.name]
    "} + if(!scanner.cache.author) + scanner.cache.author = "Anonymous" + + dat += {"Author: [scanner.cache.author]
    + Category: [upload_category]
    + \[Upload\]
    "} + dat += "(Return to main menu)
    " + if(7) + dat += "

    Print a Manual

    " + dat += "" + + var/list/forbidden = list( + /obj/item/book/manual + ) + + if(!emagged) + forbidden |= /obj/item/book/manual/nuclear + + var/manualcount = 1 + var/obj/item/book/manual/M = null + + for(var/manual_type in (typesof(/obj/item/book/manual) - forbidden)) + M = new manual_type() + dat += "" + manualcount++ + QDEL_NULL(M) + dat += "
    [M.title]
    " + dat += "
    (Return to main menu)
    " + + if(8) + + dat += {"

    Accessing Forbidden Lore Vault v 1.3

    + Are you absolutely sure you want to proceed? EldritchTomes Inc. takes no responsibilities for loss of sanity resulting from this action.

    + Yes.
    + No.
    "} + + var/datum/browser/B = new /datum/browser(user, "library", "Book Inventory Management") + B.set_content(dat) + B.open() + +/obj/machinery/computer/library/checkout/emag_act(mob/user) + if(density && !emagged) + emagged = 1 + to_chat(user, "You override the library computer's printing restrictions.") + +/obj/machinery/computer/library/checkout/attackby(obj/item/W as obj, mob/user as mob) + if(default_unfasten_wrench(user, W)) + power_change() + return + if(istype(W, /obj/item/barcodescanner)) + var/obj/item/barcodescanner/scanner = W + scanner.computer = src + to_chat(user, "[scanner]'s associated machine has been set to [src].") + audible_message("[src] lets out a low, short blip.", 2) + return 1 + else + return ..() + +/obj/machinery/computer/library/checkout/Topic(href, href_list) + if(..()) + usr << browse(null, "window=library") + onclose(usr, "library") + return 1 + + if(href_list["pagenum"]) + if(!num_pages) + page_num = 1 + else + var/pn = text2num(href_list["pagenum"]) + if(!isnull(pn)) + page_num = Clamp(pn, 1, num_pages) + + if(href_list["page"]) + if(num_pages == 0) + page_num = 1 + else + page_num = Clamp(text2num(href_list["page"]), 1, num_pages) + if(href_list["settitle"]) + var/newtitle = input("Enter a title to search for:") as text|null + if(newtitle) + query.title = sanitize(newtitle) + else + query.title = null + if(href_list["setcategory"]) + var/newcategory = input("Choose a category to search for:") in (list("Any") + GLOB.library_section_names) + if(newcategory == "Any") + query.category = null + else if(newcategory) + query.category = sanitize(newcategory) + if(href_list["setauthor"]) + var/newauthor = input("Enter an author to search for:") as text|null + if(newauthor) + query.author = sanitize(newauthor) + else + query.author = null + + if(href_list["search"]) + num_results = src.get_num_results() + num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE) + page_num = 1 + + screenstate = 4 + if(href_list["del"]) + if(!check_rights(R_ADMIN)) + return + var/datum/cachedbook/target = getBookByID(href_list["del"]) // Sanitized in getBookByID + var/ans = alert(usr, "Are you sure you wish to delete \"[target.title]\", by [target.author]? This cannot be undone.", "Library System", "Yes", "No") + if(ans=="Yes") + var/DBQuery/query = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[target.id]") + var/response = query.Execute() + if(!response) + to_chat(usr, query.ErrorMsg()) + return + log_admin("LIBRARY: [key_name(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!") + message_admins("[key_name_admin(usr)] has deleted \"[target.title]\", by [target.author] ([target.ckey])!") + src.updateUsrDialog() + return + + if(href_list["delbyckey"]) + if(!check_rights(R_ADMIN)) + return + var/tckey = ckey(href_list["delbyckey"]) + var/ans = alert(usr,"Are you sure you wish to delete all books by [tckey]? This cannot be undone.", "Library System", "Yes", "No") + if(ans=="Yes") + var/DBQuery/query = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE ckey='[sanitizeSQL(tckey)]'") + var/response = query.Execute() + if(!response) + to_chat(usr, query.ErrorMsg()) + return + var/affected=query.RowsAffected() + if(affected==0) + to_chat(usr, "Unable to find any matching rows.") + return + log_admin("LIBRARY: [key_name(usr)] has deleted [affected] books written by [tckey]!") + message_admins("[key_name_admin(usr)] has deleted [affected] books written by [tckey]!") + src.updateUsrDialog() + return + + if(href_list["flag"]) + if(!GLOB.dbcon.IsConnected()) + alert("Connection to Archive has been severed. Aborting.") + return + var/id = href_list["flag"] + if(id) + var/datum/cachedbook/B = getBookByID(id) + if(B) + if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes") + GLOB.library_catalog.flag_book_by_id(usr, id) + + if(href_list["switchscreen"]) + switch(href_list["switchscreen"]) + if("0") + screenstate = 0 + if("1") + screenstate = 1 + if("2") + screenstate = 2 + if("3") + screenstate = 3 + if("4") + screenstate = 4 + if("5") + screenstate = 5 + if("6") + if(!bibledelay) + + var/obj/item/storage/bible/B = new /obj/item/storage/bible(src.loc) + if(SSticker && ( SSticker.Bible_icon_state && SSticker.Bible_item_state) ) + B.icon_state = SSticker.Bible_icon_state + B.item_state = SSticker.Bible_item_state + B.name = SSticker.Bible_name + B.deity_name = SSticker.Bible_deity_name + + bibledelay = 1 + spawn(60) + bibledelay = 0 + + else + visible_message("[src]'s monitor flashes, \"Bible printer currently unavailable, please wait a moment.\"") + + if("7") + screenstate = 7 + if("8") + screenstate = 8 + if(href_list["arccheckout"]) + if(src.emagged) + src.arcanecheckout = 1 + src.screenstate = 0 + if(href_list["increasetime"]) + checkoutperiod += 1 + if(href_list["decreasetime"]) + checkoutperiod -= 1 + if(checkoutperiod < 1) + checkoutperiod = 1 + if(href_list["editbook"]) + buffer_book = copytext(sanitize(input("Enter the book's title:") as text|null),1,MAX_MESSAGE_LEN) + if(href_list["editmob"]) + buffer_mob = copytext(sanitize(input("Enter the recipient's name:") as text|null),1,MAX_NAME_LEN) + if(href_list["checkout"]) + var/datum/borrowbook/b = new /datum/borrowbook + b.bookname = sanitize(buffer_book) + b.mobname = sanitize(buffer_mob) + b.getdate = world.time + b.duedate = world.time + (checkoutperiod * 600) + checkouts.Add(b) + if(href_list["checkin"]) + var/datum/borrowbook/b = locate(href_list["checkin"]) + checkouts.Remove(b) + if(href_list["delbook"]) + var/obj/item/book/b = locate(href_list["delbook"]) + inventory.Remove(b) + if(href_list["uploadauthor"]) + var/newauthor = copytext(sanitize(input("Enter the author's name: ") as text|null),1,MAX_MESSAGE_LEN) + if(newauthor && scanner) + scanner.cache.author = newauthor + if(href_list["uploadcategory"]) + var/newcategory = input("Choose a category: ") in list("Fiction", "Non-Fiction", "Adult", "Reference", "Religion") + if(newcategory) + upload_category = newcategory + if(href_list["upload"]) + if(scanner) + if(scanner.cache) + var/choice = input("Are you certain you wish to upload this title to the Archive?") in list("Confirm", "Abort") + if(choice == "Confirm") + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + alert("Connection to Archive has been severed. Aborting.") + else + var/sqltitle = sanitizeSQL(scanner.cache.name) + var/sqlauthor = sanitizeSQL(scanner.cache.author) + var/sqlcontent = sanitizeSQL(scanner.cache.dat) + var/sqlcategory = sanitizeSQL(upload_category) + var/DBQuery/query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("library")] (author, title, content, category, ckey, flagged) VALUES ('[sqlauthor]', '[sqltitle]', '[sqlcontent]', '[sqlcategory]', '[ckey(usr.key)]', 0)") + var/response = query.Execute() + if(!response) + to_chat(usr, query.ErrorMsg()) + else + log_admin("[usr.name]/[usr.key] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length") + message_admins("[key_name_admin(usr)] has uploaded the book titled [scanner.cache.name], [length(scanner.cache.dat)] characters in length") + + if(href_list["id"]) + if(href_list["id"]=="-1") + href_list["id"] = input("Enter your order:") as null|num + if(!href_list["id"]) + return + + if(!GLOB.dbcon.IsConnected()) + alert("Connection to Archive has been severed. Aborting.") + return + + var/datum/cachedbook/newbook = getBookByID(href_list["id"]) // Sanitized in getBookByID + if(!newbook) + alert("No book found") + return + if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1) + alert("This book is forbidden and cannot be printed.") + return + + if(bibledelay) + audible_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") + else + bibledelay = 1 + spawn(60) + bibledelay = 0 + make_external_book(newbook) + if(href_list["manual"]) + if(!href_list["manual"]) return + var/bookid = href_list["manual"] + + if(!GLOB.dbcon.IsConnected()) + alert("Connection to Archive has been severed. Aborting.") + return + + var/datum/cachedbook/newbook = getBookByID("M[bookid]") + if(!newbook) + alert("No book found") + return + if((newbook.forbidden == 2 && !emagged) || newbook.forbidden == 1) + alert("This book is forbidden and cannot be printed.") + return + + if(bibledelay) + for(var/mob/V in hearers(src)) + V.show_message("[src]'s monitor flashes, \"Printer unavailable. Please allow a short time before attempting to print.\"") + else + bibledelay = 1 + spawn(60) + bibledelay = 0 + make_external_book(newbook) + + src.add_fingerprint(usr) + src.updateUsrDialog() + return + +/* + * Library Scanner + */ + +/obj/machinery/computer/library/checkout/proc/make_external_book(var/datum/cachedbook/newbook) + if(!newbook || !newbook.id) + return + var/obj/item/book/B = new newbook.path(loc) + + if(!newbook.programmatic) + B.name = "Book: [newbook.title]" + B.title = newbook.title + B.author = newbook.author + B.dat = newbook.content + B.icon_state = "book[rand(1,16)]" + visible_message("[src]'s printer hums as it produces a completely bound book. How did it do that?") diff --git a/code/modules/library/computers/public.dm b/code/modules/library/computers/public.dm index d0a6272970c3..f94369b11b73 100644 --- a/code/modules/library/computers/public.dm +++ b/code/modules/library/computers/public.dm @@ -1,128 +1,128 @@ -/obj/machinery/computer/library/public - name = "visitor computer" - -/obj/machinery/computer/library/public/attack_hand(var/mob/user as mob) - if(..()) - return - interact(user) - -/obj/machinery/computer/library/public/attackby(obj/item/W as obj, mob/user as mob) - if(default_unfasten_wrench(user, W)) - power_change() - return - return ..() - -/obj/machinery/computer/library/public/interact(var/mob/user) - if(interact_check(user)) - return - - var/dat = "" - switch(screenstate) - if(0) - - dat += {"

    Search Settings


    - Filter by Title: [query.title]
    - Filter by Category: [query.category]
    - Filter by Author: [query.author]
    - \[Start Search\]
    "} - if(1) - establish_db_connection() - if(!dbcon.IsConnected()) - dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
    " - else if(num_results == 0) - dat += "No results found." - else - var/pagelist = get_pagelist() - - dat += pagelist - dat += {"
    - - - -
    "} - dat += {" - - - - - - - "} - for(var/datum/cachedbook/CB in get_page(page_num)) - dat += {" - - - - - - "} - - dat += "
    AuthorTitleCategorySS13BNControls
    [CB.author][CB.title][CB.category][CB.id]\[Flag[CB.flagged ? "ged" : ""]\]

    [pagelist]" - dat += "\[Go Back\]
    " - var/datum/browser/B = new /datum/browser(user, "library", "Library Visitor") - B.set_content(dat) - B.open() - -/obj/machinery/computer/library/public/Topic(href, href_list) - if(..()) - usr << browse(null, "window=publiclibrary") - onclose(usr, "publiclibrary") - return - - if(href_list["pagenum"]) - if(!num_pages) - page_num = 1 - else - var/pn = text2num(href_list["pagenum"]) - if(!isnull(pn)) - page_num = Clamp(pn, 1, num_pages) - - if(href_list["settitle"]) - var/newtitle = input("Enter a title to search for:") as text|null - if(newtitle) - query.title = sanitize(newtitle) - else - query.title = null - if(href_list["setcategory"]) - var/newcategory = input("Choose a category to search for:") in (list("Any") + library_section_names) - if(newcategory == "Any") - query.category = null - else if(newcategory) - query.category = sanitize(newcategory) - if(href_list["setauthor"]) - var/newauthor = input("Enter an author to search for:") as text|null - if(newauthor) - query.author = sanitize(newauthor) - else - query.author = null - - if(href_list["page"]) - if(num_pages == 0) - page_num = 1 - else - page_num = Clamp(text2num(href_list["page"]), 1, num_pages) - - if(href_list["search"]) - num_results = src.get_num_results() - num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE) - page_num = 1 - - screenstate = 1 - - if(href_list["back"]) - screenstate = 0 - - if(href_list["flag"]) - if(!dbcon.IsConnected()) - alert("Connection to Archive has been severed. Aborting.") - return - var/id = href_list["flag"] - if(id) - var/datum/cachedbook/B = getBookByID(id) - if(B) - if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes") - library_catalog.flag_book_by_id(usr, id) - - add_fingerprint(usr) - updateUsrDialog() - return \ No newline at end of file +/obj/machinery/computer/library/public + name = "visitor computer" + +/obj/machinery/computer/library/public/attack_hand(var/mob/user as mob) + if(..()) + return + interact(user) + +/obj/machinery/computer/library/public/attackby(obj/item/W as obj, mob/user as mob) + if(default_unfasten_wrench(user, W)) + power_change() + return + return ..() + +/obj/machinery/computer/library/public/interact(var/mob/user) + if(interact_check(user)) + return + + var/dat = "" + switch(screenstate) + if(0) + + dat += {"

    Search Settings


    + Filter by Title: [query.title]
    + Filter by Category: [query.category]
    + Filter by Author: [query.author]
    + \[Start Search\]
    "} + if(1) + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + dat += "ERROR: Unable to contact External Archive. Please contact your system administrator for assistance.
    " + else if(num_results == 0) + dat += "No results found." + else + var/pagelist = get_pagelist() + + dat += pagelist + dat += {"
    + + + +
    "} + dat += {" + + + + + + + "} + for(var/datum/cachedbook/CB in get_page(page_num)) + dat += {" + + + + + + "} + + dat += "
    AuthorTitleCategorySS13BNControls
    [CB.author][CB.title][CB.category][CB.id]\[Flag[CB.flagged ? "ged" : ""]\]

    [pagelist]" + dat += "\[Go Back\]
    " + var/datum/browser/B = new /datum/browser(user, "library", "Library Visitor") + B.set_content(dat) + B.open() + +/obj/machinery/computer/library/public/Topic(href, href_list) + if(..()) + usr << browse(null, "window=publiclibrary") + onclose(usr, "publiclibrary") + return + + if(href_list["pagenum"]) + if(!num_pages) + page_num = 1 + else + var/pn = text2num(href_list["pagenum"]) + if(!isnull(pn)) + page_num = Clamp(pn, 1, num_pages) + + if(href_list["settitle"]) + var/newtitle = input("Enter a title to search for:") as text|null + if(newtitle) + query.title = sanitize(newtitle) + else + query.title = null + if(href_list["setcategory"]) + var/newcategory = input("Choose a category to search for:") in (list("Any") + GLOB.library_section_names) + if(newcategory == "Any") + query.category = null + else if(newcategory) + query.category = sanitize(newcategory) + if(href_list["setauthor"]) + var/newauthor = input("Enter an author to search for:") as text|null + if(newauthor) + query.author = sanitize(newauthor) + else + query.author = null + + if(href_list["page"]) + if(num_pages == 0) + page_num = 1 + else + page_num = Clamp(text2num(href_list["page"]), 1, num_pages) + + if(href_list["search"]) + num_results = src.get_num_results() + num_pages = Ceiling(num_results/LIBRARY_BOOKS_PER_PAGE) + page_num = 1 + + screenstate = 1 + + if(href_list["back"]) + screenstate = 0 + + if(href_list["flag"]) + if(!GLOB.dbcon.IsConnected()) + alert("Connection to Archive has been severed. Aborting.") + return + var/id = href_list["flag"] + if(id) + var/datum/cachedbook/B = getBookByID(id) + if(B) + if((input(usr, "Are you sure you want to flag [B.title] as having inappropriate content?", "Flag Book #[B.id]") in list("Yes", "No")) == "Yes") + GLOB.library_catalog.flag_book_by_id(usr, id) + + add_fingerprint(usr) + updateUsrDialog() + return diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index dabd25c0f742..a7dd148b51a2 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -1,311 +1,311 @@ -/* Library Items - * - * Contains: - * Bookcase - * Book - * Barcode Scanner - */ - - -/* - * Bookcase - */ - -/obj/structure/bookcase - name = "bookcase" - icon = 'icons/obj/library.dmi' - icon_state = "book-0" - anchored = 1 - density = 1 - opacity = 1 - resistance_flags = FLAMMABLE - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0) - var/tmp/busy = 0 - var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/bible, /obj/item/tome) //Things allowed in the bookcase - -/obj/structure/bookcase/Initialize() - ..() - for(var/obj/item/I in loc) - if(is_type_in_list(I, allowed_books)) - I.forceMove(src) - update_icon() - -/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob, params) - if(busy) //So that you can't mess with it while deconstructing - return 1 - if(is_type_in_list(O, allowed_books)) - if(!user.drop_item()) - return - O.forceMove(src) - update_icon() - return 1 - else if(istype(O, /obj/item/storage/bag/books)) - var/obj/item/storage/bag/books/B = O - for(var/obj/item/T in B.contents) - if(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook) || istype(T, /obj/item/tome) || istype(T, /obj/item/storage/bible)) - B.remove_from_storage(T, src) - to_chat(user, "You empty [O] into [src].") - update_icon() - return 1 - else if(istype(O, /obj/item/wrench)) - user.visible_message("[user] starts disassembling \the [src].", \ - "You start disassembling \the [src].") - playsound(get_turf(src), O.usesound, 50, 1) - busy = 1 - - if(do_after(user, 50 * O.toolspeed, target = src)) - playsound(get_turf(src), O.usesound, 75, 1) - user.visible_message("[user] disassembles \the [src].", \ - "You disassemble \the [src].") - busy = 0 - density = 0 - deconstruct(TRUE) - else - busy = 0 - return 1 - else if(istype(O, /obj/item/pen)) - var/newname = stripped_input(user, "What would you like to title this [name]?") - if(newname) - name = ("bookcase ([sanitize(newname)])") - return 1 - else - return ..() - -/obj/structure/bookcase/attack_hand(var/mob/user as mob) - if(contents.len) - var/obj/item/book/choice = input("Which book would you like to remove from [src]?") as null|anything in contents - if(choice) - if(user.incapacitated() || user.lying || !Adjacent(user)) - return - if(!user.get_active_hand()) - user.put_in_hands(choice) - else - choice.forceMove(get_turf(src)) - update_icon() - -/obj/structure/bookcase/deconstruct(disassembled = TRUE) - new /obj/item/stack/sheet/wood(loc, 5) - for(var/obj/item/I in contents) - if(is_type_in_list(I, allowed_books)) - I.forceMove(get_turf(src)) - qdel(src) - -/obj/structure/bookcase/update_icon() - if(contents.len < 5) - icon_state = "book-[contents.len]" - else - icon_state = "book-5" - - -/obj/structure/bookcase/manuals/medical - name = "Medical Manuals bookcase" - - New() - ..() - new /obj/item/book/manual/medical_cloning(src) - update_icon() - - -/obj/structure/bookcase/manuals/engineering - name = "Engineering Manuals bookcase" - - New() - ..() - new /obj/item/book/manual/engineering_construction(src) - new /obj/item/book/manual/engineering_particle_accelerator(src) - new /obj/item/book/manual/engineering_hacking(src) - new /obj/item/book/manual/engineering_guide(src) - new /obj/item/book/manual/engineering_singularity_safety(src) - new /obj/item/book/manual/robotics_cyborgs(src) - update_icon() - -/obj/structure/bookcase/manuals/research_and_development - name = "R&D Manuals bookcase" - - New() - ..() - new /obj/item/book/manual/research_and_development(src) - update_icon() - - -/* - * Book - */ -/obj/item/book - name = "book" - icon = 'icons/obj/library.dmi' - icon_state ="book" - throw_speed = 1 - throw_range = 5 - force = 2 - w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) - attack_verb = list("bashed", "whacked") - resistance_flags = FLAMMABLE - - var/dat // Actual page content - var/due_date = 0 // Game time in 1/10th seconds - var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned - var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified - var/title // The real name of the book. - var/carved = 0 // Has the book been hollowed out for use as a secret storage item? - var/forbidden = 0 // Prevent ordering of this book. (0=no, 1=yes, 2=emag only) - var/obj/item/store // What's in the book? - -/obj/item/book/attack_self(var/mob/user as mob) - if(carved) - if(store) - to_chat(user, "[store] falls out of [title]!") - store.forceMove(get_turf(loc)) - store = null - return - else - to_chat(user, "The pages of [title] have been cut out!") - return - if(src.dat) - user << browse("Penned by [author].
    " + "[dat]", "window=book") - if(!isobserver(user)) - user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.") - onclose(user, "book") - else - to_chat(user, "This book is completely blank!") - -/obj/item/book/attackby(obj/item/W as obj, mob/user as mob, params) - if(carved) - if(!store) - if(W.w_class < WEIGHT_CLASS_NORMAL) - user.drop_item() - W.forceMove(src) - store = W - to_chat(user, "You put [W] in [title].") - return 1 - else - to_chat(user, "[W] won't fit in [title].") - return 1 - else - to_chat(user, "There's already something in [title]!") - return 1 - if(istype(W, /obj/item/pen)) - if(unique) - to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.") - return 1 - var/choice = input("What would you like to change?") in list("Title", "Contents", "Author", "Cancel") - switch(choice) - if("Title") - var/newtitle = reject_bad_text(stripped_input(usr, "Write a new title:")) - if(!newtitle) - to_chat(usr, "The title is invalid.") - return 1 - else - src.name = newtitle - src.title = newtitle - if("Contents") - var/content = strip_html(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN) - if(!content) - to_chat(usr, "The content is invalid.") - return 1 - else - src.dat += content - if("Author") - var/newauthor = stripped_input(usr, "Write the author's name:") - if(!newauthor) - to_chat(usr, "The name is invalid.") - return 1 - else - src.author = newauthor - return 1 - else if(istype(W, /obj/item/barcodescanner)) - var/obj/item/barcodescanner/scanner = W - if(!scanner.computer) - to_chat(user, "[W]'s screen flashes: 'No associated computer found!'") - else - switch(scanner.mode) - if(0) - scanner.book = src - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'") - if(1) - scanner.book = src - scanner.computer.buffer_book = src.name - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'") - if(2) - scanner.book = src - for(var/datum/borrowbook/b in scanner.computer.checkouts) - if(b.bookname == src.name) - scanner.computer.checkouts.Remove(b) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'") - return 1 - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'") - if(3) - scanner.book = src - for(var/obj/item/book in scanner.computer.inventory) - if(book == src) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'") - return 1 - scanner.computer.inventory.Add(src) - to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") - return 1 - else if(istype(W, /obj/item/kitchen/knife) && !carved) - carve_book(user, W) - else - return ..() - -/obj/item/book/wirecutter_act(mob/user, obj/item/I) - return carve_book(user, I) - -/obj/item/book/attack(mob/M, mob/living/user) - if(user.a_intent == INTENT_HELP) - force = 0 - attack_verb = list("educated") - else - force = initial(force) - attack_verb = list("bashed", "whacked") - ..() - -/obj/item/book/proc/carve_book(mob/user, obj/item/I) - if(!I.sharp && I.tool_behaviour != TOOL_WIRECUTTER) //Only sharp and wirecutter things can carve books - to_chat(user, "") - return - if(carved) - return - to_chat(user, "You begin to carve out [title].") - if(I.use_tool(src, user, 30, volume = I.tool_volume)) - to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") - carved = TRUE - return TRUE -/* - * Barcode Scanner - */ -/obj/item/barcodescanner - name = "barcode scanner" - icon = 'icons/obj/library.dmi' - icon_state ="scanner" - throw_speed = 1 - throw_range = 5 - w_class = WEIGHT_CLASS_TINY - var/obj/machinery/computer/library/checkout/computer // Associated computer - Modes 1 to 3 use this - var/obj/item/book/book // Currently scanned book - var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory - - attack_self(mob/user as mob) - mode += 1 - if(mode > 3) - mode = 0 - to_chat(user, "[src] Status Display:") - var/modedesc - switch(mode) - if(0) - modedesc = "Scan book to local buffer." - if(1) - modedesc = "Scan book to local buffer and set associated computer buffer to match." - if(2) - modedesc = "Scan book to local buffer, attempt to check in scanned book." - if(3) - modedesc = "Scan book to local buffer, attempt to add book to general inventory." - else - modedesc = "ERROR" - to_chat(user, " - Mode [mode] : [modedesc]") - if(src.computer) - to_chat(user, "Computer has been associated with this unit.") - else - to_chat(user, "No associated computer found. Only local scans will function properly.") - to_chat(user, "\n") +/* Library Items + * + * Contains: + * Bookcase + * Book + * Barcode Scanner + */ + + +/* + * Bookcase + */ + +/obj/structure/bookcase + name = "bookcase" + icon = 'icons/obj/library.dmi' + icon_state = "book-0" + anchored = 1 + density = 1 + opacity = 1 + resistance_flags = FLAMMABLE + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 0) + var/tmp/busy = 0 + var/list/allowed_books = list(/obj/item/book, /obj/item/spellbook, /obj/item/storage/bible, /obj/item/tome) //Things allowed in the bookcase + +/obj/structure/bookcase/Initialize() + ..() + for(var/obj/item/I in loc) + if(is_type_in_list(I, allowed_books)) + I.forceMove(src) + update_icon() + +/obj/structure/bookcase/attackby(obj/item/O as obj, mob/user as mob, params) + if(busy) //So that you can't mess with it while deconstructing + return 1 + if(is_type_in_list(O, allowed_books)) + if(!user.drop_item()) + return + O.forceMove(src) + update_icon() + return 1 + else if(istype(O, /obj/item/storage/bag/books)) + var/obj/item/storage/bag/books/B = O + for(var/obj/item/T in B.contents) + if(istype(T, /obj/item/book) || istype(T, /obj/item/spellbook) || istype(T, /obj/item/tome) || istype(T, /obj/item/storage/bible)) + B.remove_from_storage(T, src) + to_chat(user, "You empty [O] into [src].") + update_icon() + return 1 + else if(istype(O, /obj/item/wrench)) + user.visible_message("[user] starts disassembling \the [src].", \ + "You start disassembling \the [src].") + playsound(get_turf(src), O.usesound, 50, 1) + busy = 1 + + if(do_after(user, 50 * O.toolspeed, target = src)) + playsound(get_turf(src), O.usesound, 75, 1) + user.visible_message("[user] disassembles \the [src].", \ + "You disassemble \the [src].") + busy = 0 + density = 0 + deconstruct(TRUE) + else + busy = 0 + return 1 + else if(istype(O, /obj/item/pen)) + var/newname = stripped_input(user, "What would you like to title this [name]?") + if(newname) + name = ("bookcase ([sanitize(newname)])") + return 1 + else + return ..() + +/obj/structure/bookcase/attack_hand(var/mob/user as mob) + if(contents.len) + var/obj/item/book/choice = input("Which book would you like to remove from [src]?") as null|anything in contents + if(choice) + if(user.incapacitated() || user.lying || !Adjacent(user)) + return + if(!user.get_active_hand()) + user.put_in_hands(choice) + else + choice.forceMove(get_turf(src)) + update_icon() + +/obj/structure/bookcase/deconstruct(disassembled = TRUE) + new /obj/item/stack/sheet/wood(loc, 5) + for(var/obj/item/I in contents) + if(is_type_in_list(I, allowed_books)) + I.forceMove(get_turf(src)) + qdel(src) + +/obj/structure/bookcase/update_icon() + if(contents.len < 5) + icon_state = "book-[contents.len]" + else + icon_state = "book-5" + + +/obj/structure/bookcase/manuals/medical + name = "Medical Manuals bookcase" + + New() + ..() + new /obj/item/book/manual/medical_cloning(src) + update_icon() + + +/obj/structure/bookcase/manuals/engineering + name = "Engineering Manuals bookcase" + + New() + ..() + new /obj/item/book/manual/engineering_construction(src) + new /obj/item/book/manual/engineering_particle_accelerator(src) + new /obj/item/book/manual/engineering_hacking(src) + new /obj/item/book/manual/engineering_guide(src) + new /obj/item/book/manual/engineering_singularity_safety(src) + new /obj/item/book/manual/robotics_cyborgs(src) + update_icon() + +/obj/structure/bookcase/manuals/research_and_development + name = "R&D Manuals bookcase" + + New() + ..() + new /obj/item/book/manual/research_and_development(src) + update_icon() + + +/* + * Book + */ +/obj/item/book + name = "book" + icon = 'icons/obj/library.dmi' + icon_state ="book" + throw_speed = 1 + throw_range = 5 + force = 2 + w_class = WEIGHT_CLASS_NORMAL //upped to three because books are, y'know, pretty big. (and you could hide them inside eachother recursively forever) + attack_verb = list("bashed", "whacked") + resistance_flags = FLAMMABLE + + var/dat // Actual page content + var/due_date = 0 // Game time in 1/10th seconds + var/author // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned + var/unique = 0 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified + var/title // The real name of the book. + var/carved = 0 // Has the book been hollowed out for use as a secret storage item? + var/forbidden = 0 // Prevent ordering of this book. (0=no, 1=yes, 2=emag only) + var/obj/item/store // What's in the book? + +/obj/item/book/attack_self(var/mob/user as mob) + if(carved) + if(store) + to_chat(user, "[store] falls out of [title]!") + store.forceMove(get_turf(loc)) + store = null + return + else + to_chat(user, "The pages of [title] have been cut out!") + return + if(src.dat) + user << browse("Penned by [author].
    " + "[dat]", "window=book") + if(!isobserver(user)) + user.visible_message("[user] opens a book titled \"[title]\" and begins reading intently.") + onclose(user, "book") + else + to_chat(user, "This book is completely blank!") + +/obj/item/book/attackby(obj/item/W as obj, mob/user as mob, params) + if(carved) + if(!store) + if(W.w_class < WEIGHT_CLASS_NORMAL) + user.drop_item() + W.forceMove(src) + store = W + to_chat(user, "You put [W] in [title].") + return 1 + else + to_chat(user, "[W] won't fit in [title].") + return 1 + else + to_chat(user, "There's already something in [title]!") + return 1 + if(istype(W, /obj/item/pen)) + if(unique) + to_chat(user, "These pages don't seem to take the ink well. Looks like you can't modify it.") + return 1 + var/choice = input("What would you like to change?") in list("Title", "Contents", "Author", "Cancel") + switch(choice) + if("Title") + var/newtitle = reject_bad_text(stripped_input(usr, "Write a new title:")) + if(!newtitle) + to_chat(usr, "The title is invalid.") + return 1 + else + src.name = newtitle + src.title = newtitle + if("Contents") + var/content = strip_html(input(usr, "Write your book's contents (HTML NOT allowed):") as message|null, MAX_BOOK_MESSAGE_LEN) + if(!content) + to_chat(usr, "The content is invalid.") + return 1 + else + src.dat += content + if("Author") + var/newauthor = stripped_input(usr, "Write the author's name:") + if(!newauthor) + to_chat(usr, "The name is invalid.") + return 1 + else + src.author = newauthor + return 1 + else if(istype(W, /obj/item/barcodescanner)) + var/obj/item/barcodescanner/scanner = W + if(!scanner.computer) + to_chat(user, "[W]'s screen flashes: 'No associated computer found!'") + else + switch(scanner.mode) + if(0) + scanner.book = src + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer.'") + if(1) + scanner.book = src + scanner.computer.buffer_book = src.name + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book title stored in associated computer buffer.'") + if(2) + scanner.book = src + for(var/datum/borrowbook/b in scanner.computer.checkouts) + if(b.bookname == src.name) + scanner.computer.checkouts.Remove(b) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Book has been checked in.'") + return 1 + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. No active check-out record found for current title.'") + if(3) + scanner.book = src + for(var/obj/item/book in scanner.computer.inventory) + if(book == src) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title already present in inventory, aborting to avoid duplicate entry.'") + return 1 + scanner.computer.inventory.Add(src) + to_chat(user, "[W]'s screen flashes: 'Book stored in buffer. Title added to general inventory.'") + return 1 + else if(istype(W, /obj/item/kitchen/knife) && !carved) + carve_book(user, W) + else + return ..() + +/obj/item/book/wirecutter_act(mob/user, obj/item/I) + return carve_book(user, I) + +/obj/item/book/attack(mob/M, mob/living/user) + if(user.a_intent == INTENT_HELP) + force = 0 + attack_verb = list("educated") + else + force = initial(force) + attack_verb = list("bashed", "whacked") + ..() + +/obj/item/book/proc/carve_book(mob/user, obj/item/I) + if(!I.sharp && I.tool_behaviour != TOOL_WIRECUTTER) //Only sharp and wirecutter things can carve books + to_chat(user, "") + return + if(carved) + return + to_chat(user, "You begin to carve out [title].") + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + to_chat(user, "You carve out the pages from [title]! You didn't want to read it anyway.") + carved = TRUE + return TRUE +/* + * Barcode Scanner + */ +/obj/item/barcodescanner + name = "barcode scanner" + icon = 'icons/obj/library.dmi' + icon_state ="scanner" + throw_speed = 1 + throw_range = 5 + w_class = WEIGHT_CLASS_TINY + var/obj/machinery/computer/library/checkout/computer // Associated computer - Modes 1 to 3 use this + var/obj/item/book/book // Currently scanned book + var/mode = 0 // 0 - Scan only, 1 - Scan and Set Buffer, 2 - Scan and Attempt to Check In, 3 - Scan and Attempt to Add to Inventory + + attack_self(mob/user as mob) + mode += 1 + if(mode > 3) + mode = 0 + to_chat(user, "[src] Status Display:") + var/modedesc + switch(mode) + if(0) + modedesc = "Scan book to local buffer." + if(1) + modedesc = "Scan book to local buffer and set associated computer buffer to match." + if(2) + modedesc = "Scan book to local buffer, attempt to check in scanned book." + if(3) + modedesc = "Scan book to local buffer, attempt to add book to general inventory." + else + modedesc = "ERROR" + to_chat(user, " - Mode [mode] : [modedesc]") + if(src.computer) + to_chat(user, "Computer has been associated with this unit.") + else + to_chat(user, "No associated computer found. Only local scans will function properly.") + to_chat(user, "\n") diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 611b84f81008..65fadaa9b454 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -1,230 +1,230 @@ -#define LIBRARY_BOOKS_PER_PAGE 25 - -var/global/datum/library_catalog/library_catalog = new() -var/global/list/library_section_names = list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion") - - -/hook/startup/proc/load_manuals() - library_catalog.initialize() - return 1 - -/* - * Borrowbook datum - */ -/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. - var/bookname - var/mobname - var/getdate - var/duedate - -/* - * Cachedbook datum - */ -/datum/cachedbook // Datum used to cache the SQL DB books locally in order to achieve a performance gain. - var/id - var/title - var/author - var/ckey // ADDED 24/2/2015 - N3X - var/category - var/content - var/programmatic=0 // Is the book programmatically added to the catalog? - var/forbidden=0 - var/path = /obj/item/book // Type path of the book to generate - var/flagged = 0 - -/datum/cachedbook/proc/LoadFromRow(var/list/row) - id = row["id"] - author = row["author"] - title = row["title"] - category = row["category"] - ckey = row["ckey"] - flagged = row["flagged"] - if("content" in row) - content = row["content"] - programmatic=0 - -// Builds a SQL statement -/datum/library_query - var/author - var/category - var/title - -/datum/library_query/proc/toSQL() - var/list/where = list() - if(author || title || category) - if(author) - where.Add("author LIKE '%[sanitizeSQL(author)]%'") - if(category) - where.Add("category = '[sanitizeSQL(category)]'") - if(title) - where.Add("title LIKE '%[sanitizeSQL(title)]%'") - return " WHERE " + jointext(where, " AND ") - return "" - -// So we can have catalogs of books that are programmatic, and ones that aren't. -/datum/library_catalog - var/list/cached_books = list() - -/datum/library_catalog/proc/initialize() - var/newid=1 - for(var/typepath in subtypesof(/obj/item/book/manual)) - var/obj/item/book/B = new typepath(null) - var/datum/cachedbook/CB = new() - CB.forbidden = B.forbidden - CB.title = B.name - CB.author = B.author - CB.programmatic=1 - CB.path=typepath - CB.id = "M[newid]" - newid++ - cached_books["[CB.id]"]=CB - -/datum/library_catalog/proc/flag_book_by_id(mob/user, id) - var/global/books_flagged_this_round[0] - - if("[id]" in cached_books) - var/datum/cachedbook/CB = cached_books["[id]"] - if(CB.programmatic) - to_chat(user, "That book cannot be flagged in the system, as it does not actually exist in the database.") - return - - if("[id]" in books_flagged_this_round) - to_chat(user, "This book has already been flagged this shift.") - return - - books_flagged_this_round["[id]"] = 1 - message_admins("[key_name_admin(user)] has flagged book #[id] as inappropriate.") - - var/sqlid = text2num(id) - if(!sqlid) - return - var/DBQuery/query = dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = flagged + 1 WHERE id=[sqlid]") - query.Execute() - -/datum/library_catalog/proc/rmBookByID(mob/user, id) - if("[id]" in cached_books) - var/datum/cachedbook/CB = cached_books["[id]"] - if(CB.programmatic) - to_chat(user, "That book cannot be removed from the system, as it does not actually exist in the database.") - return - - var/sqlid = text2num(id) - if(!sqlid) - return - var/DBQuery/query = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[sqlid]") - query.Execute() - -/datum/library_catalog/proc/getBookByID(id) - if("[id]" in cached_books) - return cached_books["[id]"] - - var/sqlid = text2num(id) - if(!sqlid) - return - var/DBQuery/query = dbcon.NewQuery("SELECT id, author, title, category, content, ckey, flagged FROM [format_table_name("library")] WHERE id=[sqlid]") - query.Execute() - - var/list/results=list() - while(query.NextRow()) - var/datum/cachedbook/CB = new() - CB.LoadFromRow(list( - "id" =query.item[1], - "author" =query.item[2], - "title" =query.item[3], - "category"=query.item[4], - "content" =query.item[5], - "ckey" =query.item[6], - "flagged" =query.item[7] - )) - results += CB - cached_books["[id]"]=CB - return CB - return results - -/** Scanner **/ -/obj/machinery/libraryscanner - name = "scanner" - icon = 'icons/obj/library.dmi' - icon_state = "bigscanner" - anchored = 1 - density = 1 - var/obj/item/book/cache // Last scanned book - -/obj/machinery/libraryscanner/attackby(obj/item/I, mob/user) - if(default_unfasten_wrench(user, I)) - power_change() - return - if(istype(I, /obj/item/book)) - user.drop_item() - I.forceMove(src) - return 1 - else - return ..() - -/obj/machinery/libraryscanner/attack_hand(mob/user) - if(istype(user,/mob/dead)) - to_chat(user, "Nope.") - return - usr.set_machine(src) - var/dat = "Scanner Control Interface\n" // - if(cache) - dat += "Data stored in memory.
    " - else - dat += "No data stored in memory.
    " - dat += "\[Scan\]" - if(cache) - dat += " \[Clear Memory\]

    \[Remove Book\]" - else - dat += "
    " - user << browse(dat, "window=scanner") - onclose(user, "scanner") - -/obj/machinery/libraryscanner/Topic(href, href_list) - if(..()) - usr << browse(null, "window=scanner") - onclose(usr, "scanner") - return - - if(href_list["scan"]) - for(var/obj/item/book/B in contents) - cache = B - break - if(href_list["clear"]) - cache = null - if(href_list["eject"]) - for(var/obj/item/book/B in contents) - B.loc = src.loc - src.add_fingerprint(usr) - src.updateUsrDialog() - return - - -/* - * Book binder - */ -/obj/machinery/bookbinder - name = "Book Binder" - icon = 'icons/obj/library.dmi' - icon_state = "binder" - anchored = 1 - density = 1 - -/obj/machinery/bookbinder/attackby(obj/item/I, mob/user) - var/obj/item/paper/P = I - if(default_unfasten_wrench(user, I)) - power_change() - return - if(istype(P)) - user.drop_item() - user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") - src.visible_message("[src] begins to hum as it warms up its printing drums.") - sleep(rand(200,400)) - src.visible_message("[src] whirs as it prints and binds a new book.") - var/obj/item/book/b = new(loc) - b.dat = P.info - b.name = "Print Job #[rand(100, 999)]" - b.icon_state = "book[rand(1,16)]" - qdel(P) - return 1 - else - return ..() +#define LIBRARY_BOOKS_PER_PAGE 25 + +GLOBAL_DATUM_INIT(library_catalog, /datum/library_catalog, new()) +GLOBAL_LIST_INIT(library_section_names, list("Any", "Fiction", "Non-Fiction", "Adult", "Reference", "Religion")) + + +/hook/startup/proc/load_manuals() + GLOB.library_catalog.initialize() + return 1 + +/* + * Borrowbook datum + */ +/datum/borrowbook // Datum used to keep track of who has borrowed what when and for how long. + var/bookname + var/mobname + var/getdate + var/duedate + +/* + * Cachedbook datum + */ +/datum/cachedbook // Datum used to cache the SQL DB books locally in order to achieve a performance gain. + var/id + var/title + var/author + var/ckey // ADDED 24/2/2015 - N3X + var/category + var/content + var/programmatic=0 // Is the book programmatically added to the catalog? + var/forbidden=0 + var/path = /obj/item/book // Type path of the book to generate + var/flagged = 0 + +/datum/cachedbook/proc/LoadFromRow(var/list/row) + id = row["id"] + author = row["author"] + title = row["title"] + category = row["category"] + ckey = row["ckey"] + flagged = row["flagged"] + if("content" in row) + content = row["content"] + programmatic=0 + +// Builds a SQL statement +/datum/library_query + var/author + var/category + var/title + +/datum/library_query/proc/toSQL() + var/list/where = list() + if(author || title || category) + if(author) + where.Add("author LIKE '%[sanitizeSQL(author)]%'") + if(category) + where.Add("category = '[sanitizeSQL(category)]'") + if(title) + where.Add("title LIKE '%[sanitizeSQL(title)]%'") + return " WHERE " + jointext(where, " AND ") + return "" + +// So we can have catalogs of books that are programmatic, and ones that aren't. +/datum/library_catalog + var/list/cached_books = list() + +/datum/library_catalog/proc/initialize() + var/newid=1 + for(var/typepath in subtypesof(/obj/item/book/manual)) + var/obj/item/book/B = new typepath(null) + var/datum/cachedbook/CB = new() + CB.forbidden = B.forbidden + CB.title = B.name + CB.author = B.author + CB.programmatic=1 + CB.path=typepath + CB.id = "M[newid]" + newid++ + cached_books["[CB.id]"]=CB + +/datum/library_catalog/proc/flag_book_by_id(mob/user, id) + var/global/books_flagged_this_round[0] + + if("[id]" in cached_books) + var/datum/cachedbook/CB = cached_books["[id]"] + if(CB.programmatic) + to_chat(user, "That book cannot be flagged in the system, as it does not actually exist in the database.") + return + + if("[id]" in books_flagged_this_round) + to_chat(user, "This book has already been flagged this shift.") + return + + books_flagged_this_round["[id]"] = 1 + message_admins("[key_name_admin(user)] has flagged book #[id] as inappropriate.") + + var/sqlid = text2num(id) + if(!sqlid) + return + var/DBQuery/query = GLOB.dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = flagged + 1 WHERE id=[sqlid]") + query.Execute() + +/datum/library_catalog/proc/rmBookByID(mob/user, id) + if("[id]" in cached_books) + var/datum/cachedbook/CB = cached_books["[id]"] + if(CB.programmatic) + to_chat(user, "That book cannot be removed from the system, as it does not actually exist in the database.") + return + + var/sqlid = text2num(id) + if(!sqlid) + return + var/DBQuery/query = GLOB.dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[sqlid]") + query.Execute() + +/datum/library_catalog/proc/getBookByID(id) + if("[id]" in cached_books) + return cached_books["[id]"] + + var/sqlid = text2num(id) + if(!sqlid) + return + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id, author, title, category, content, ckey, flagged FROM [format_table_name("library")] WHERE id=[sqlid]") + query.Execute() + + var/list/results=list() + while(query.NextRow()) + var/datum/cachedbook/CB = new() + CB.LoadFromRow(list( + "id" =query.item[1], + "author" =query.item[2], + "title" =query.item[3], + "category"=query.item[4], + "content" =query.item[5], + "ckey" =query.item[6], + "flagged" =query.item[7] + )) + results += CB + cached_books["[id]"]=CB + return CB + return results + +/** Scanner **/ +/obj/machinery/libraryscanner + name = "scanner" + icon = 'icons/obj/library.dmi' + icon_state = "bigscanner" + anchored = 1 + density = 1 + var/obj/item/book/cache // Last scanned book + +/obj/machinery/libraryscanner/attackby(obj/item/I, mob/user) + if(default_unfasten_wrench(user, I)) + power_change() + return + if(istype(I, /obj/item/book)) + user.drop_item() + I.forceMove(src) + return 1 + else + return ..() + +/obj/machinery/libraryscanner/attack_hand(mob/user) + if(istype(user,/mob/dead)) + to_chat(user, "Nope.") + return + usr.set_machine(src) + var/dat = "Scanner Control Interface\n" // + if(cache) + dat += "Data stored in memory.
    " + else + dat += "No data stored in memory.
    " + dat += "\[Scan\]" + if(cache) + dat += " \[Clear Memory\]

    \[Remove Book\]" + else + dat += "
    " + user << browse(dat, "window=scanner") + onclose(user, "scanner") + +/obj/machinery/libraryscanner/Topic(href, href_list) + if(..()) + usr << browse(null, "window=scanner") + onclose(usr, "scanner") + return + + if(href_list["scan"]) + for(var/obj/item/book/B in contents) + cache = B + break + if(href_list["clear"]) + cache = null + if(href_list["eject"]) + for(var/obj/item/book/B in contents) + B.loc = src.loc + src.add_fingerprint(usr) + src.updateUsrDialog() + return + + +/* + * Book binder + */ +/obj/machinery/bookbinder + name = "Book Binder" + icon = 'icons/obj/library.dmi' + icon_state = "binder" + anchored = 1 + density = 1 + +/obj/machinery/bookbinder/attackby(obj/item/I, mob/user) + var/obj/item/paper/P = I + if(default_unfasten_wrench(user, I)) + power_change() + return + if(istype(P)) + user.drop_item() + user.visible_message("[user] loads some paper into [src].", "You load some paper into [src].") + src.visible_message("[src] begins to hum as it warms up its printing drums.") + sleep(rand(200,400)) + src.visible_message("[src] whirs as it prints and binds a new book.") + var/obj/item/book/b = new(loc) + b.dat = P.info + b.name = "Print Job #[rand(100, 999)]" + b.icon_state = "book[rand(1,16)]" + qdel(P) + return 1 + else + return ..() diff --git a/code/modules/library/lib_readme.dm b/code/modules/library/lib_readme.dm index 4237687dfff8..0150ed6ba8a7 100644 --- a/code/modules/library/lib_readme.dm +++ b/code/modules/library/lib_readme.dm @@ -1,61 +1,61 @@ -//******************************* -// -// Library SQL Configuration -// -//******************************* - -// Deprecated! See global.dm for new SQL config vars -- TLE -/* -#define SQL_ADDRESS "" -#define SQL_DB "" -#define SQL_PORT "3306" -#define SQL_LOGIN "" -#define SQL_PASS "" -*/ - -//******************************* -// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) - - -/* - The Library - ------------ - A place for the crew to go, relax, and enjoy a good book. - Aspiring authors can even self publish and, if they're lucky - convince the on-staff Librarian to submit it to the Archives - to be chronicled in history forever - some say even persisting - through alternate dimensions. - - - Written by TLE for /tg/station 13 - Feel free to use this as you like. Some credit would be cool. - Check us out at http://nanotrasen.com/ if you're so inclined. -*/ - -// CONTAINS: - -// Objects: -// - bookcase -// - book -// - barcode scanner -// Machinery: -// - library computer -// - visitor's computer -// - book binder -// - book scanner -// Datum: -// - borrowbook - - -// Ideas for the future -// --------------------- -// - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) -// -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. -// - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. -// -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). -// The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. -// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. -// - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. -// - Make books/book cases burn when exposed to flame. -// - Make book binder hackable. -// - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. +//******************************* +// +// Library SQL Configuration +// +//******************************* + +// Deprecated! See global.dm for new SQL config vars -- TLE +/* +#define SQL_ADDRESS "" +#define SQL_DB "" +#define SQL_PORT "3306" +#define SQL_LOGIN "" +#define SQL_PASS "" +*/ + +//******************************* +// Requires Dantom.DB library ( http://www.byond.com/developer/Dantom/DB ) + + +/* + The Library + ------------ + A place for the crew to go, relax, and enjoy a good book. + Aspiring authors can even self publish and, if they're lucky + convince the on-staff Librarian to submit it to the Archives + to be chronicled in history forever - some say even persisting + through alternate dimensions. + + + Written by TLE for /tg/station 13 + Feel free to use this as you like. Some credit would be cool. + Check us out at http://nanotrasen.com/ if you're so inclined. +*/ + +// CONTAINS: + +// Objects: +// - bookcase +// - book +// - barcode scanner +// Machinery: +// - library computer +// - visitor's computer +// - book binder +// - book scanner +// Datum: +// - borrowbook + + +// Ideas for the future +// --------------------- +// - Visitor's computer should be able to search the current in-round library inventory (that the Librarian has stocked and checked in) +// -- Give computer other features like an Instant Messenger application, or the ability to edit, save, and print documents. +// - Admin interface directly tied to the Archive DB. Right now there's no way to delete uploaded books in-game. +// -- If this gets implemented, allow Librarians to "tag" or "suggest" books to be deleted. The DB ID of the tagged books gets saved to a text file (or another table in the DB maybe?). +// The admin interface would automatically take these IDs and SELECT them all from the DB to be displayed along with a Delete link to drop the row from the table. +// - When the game sets up and the round begins, have it automatically pick random books from the DB to populate the library with. Even if the Librarian is a useless fuck there are at least a few books around. +// - Allow books to be "hollowed out" like the Chaplain's Bible, allowing you to store one pocket-sized item inside. +// - Make books/book cases burn when exposed to flame. +// - Make book binder hackable. +// - Books shouldn't print straight from the library computer. Make it synch with a machine like the book binder to print instead. This should consume some sort of resource. diff --git a/code/modules/library/random_books.dm b/code/modules/library/random_books.dm index f36e493ce3fe..f571b3438e1b 100644 --- a/code/modules/library/random_books.dm +++ b/code/modules/library/random_books.dm @@ -40,7 +40,7 @@ . = list() if(!isnum(amount) || amount<1) return - if(!dbcon.IsConnected()) + if(!GLOB.dbcon.IsConnected()) if(fail_loud || prob(5)) var/obj/item/paper/P = new(location) P.info = "There once was a book from Nantucket
    But the database failed us, so f*$! it.
    I tried to be good to you
    Now this is an I.O.U
    If you're feeling entitled, well, stuff it!

    ~" @@ -49,7 +49,7 @@ if(prob(25)) category = null var/c = category? " AND category='[sanitizeSQL(category)]'" :"" - var/DBQuery/query_get_random_books = dbcon.NewQuery("SELECT * FROM [format_table_name("library")] WHERE (isnull(flagged) OR flagged = 0)[c] GROUP BY title ORDER BY rand() LIMIT [amount];") + var/DBQuery/query_get_random_books = GLOB.dbcon.NewQuery("SELECT * FROM [format_table_name("library")] WHERE (isnull(flagged) OR flagged = 0)[c] GROUP BY title ORDER BY rand() LIMIT [amount];") query_get_random_books.Execute() while(query_get_random_books.NextRow()) var/obj/item/book/B = new(location) @@ -82,4 +82,4 @@ . = ..() while(book_count > 0 && prob(ref_book_prob)) book_count-- - new /obj/item/book/manual/random(src) \ No newline at end of file + new /obj/item/book/manual/random(src) diff --git a/code/modules/lighting/__lighting_docs.dm b/code/modules/lighting/__lighting_docs.dm index 18041989910e..e2a882a76797 100644 --- a/code/modules/lighting/__lighting_docs.dm +++ b/code/modules/lighting/__lighting_docs.dm @@ -64,4 +64,4 @@ atom/movable/lighting_object: (lighting_object.dm) - Change the lumcount vars and queue the overlay for update - proc/update_overlay() - Called by the lighting process to update the color of the overlay -*/ \ No newline at end of file +*/ diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm index 77e955d10a6e..f08128b156f7 100644 --- a/code/modules/lighting/lighting_area.dm +++ b/code/modules/lighting/lighting_area.dm @@ -27,4 +27,4 @@ if("dynamic_lighting") set_dynamic_lighting(var_value) return TRUE - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm index d8b05d0c17fc..bc521114d56e 100644 --- a/code/modules/lighting/lighting_corner.dm +++ b/code/modules/lighting/lighting_corner.dm @@ -140,4 +140,4 @@ GLOBAL_LIST_INIT(LIGHTING_CORNER_DIAGONAL, list(NORTHEAST, SOUTHEAST, SOUTHWEST, stack_trace("Attempted qdel of a lighting corner.") - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm index c755da03b2b7..ea3e954fa6e5 100644 --- a/code/modules/lighting/lighting_object.dm +++ b/code/modules/lighting/lighting_object.dm @@ -145,4 +145,4 @@ // Override here to prevent things accidentally moving around overlays. /atom/movable/lighting_object/forceMove(atom/destination, var/no_tp=FALSE, var/harderforce = FALSE) if(harderforce) - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm index ed8b7e71d6c5..9923006815fb 100644 --- a/code/modules/lighting/lighting_setup.dm +++ b/code/modules/lighting/lighting_setup.dm @@ -9,4 +9,4 @@ new/atom/movable/lighting_object(T) CHECK_TICK - CHECK_TICK \ No newline at end of file + CHECK_TICK diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm index 207a91d58b4e..8e54531f9010 100644 --- a/code/modules/lighting/lighting_source.dm +++ b/code/modules/lighting/lighting_source.dm @@ -295,4 +295,4 @@ #undef EFFECT_UPDATE #undef LUM_FALLOFF #undef REMOVE_CORNER -#undef APPLY_CORNER \ No newline at end of file +#undef APPLY_CORNER diff --git a/code/modules/logic/logic_base.dm b/code/modules/logic/logic_base.dm index e2e4450c9821..542cb09bc7f4 100644 --- a/code/modules/logic/logic_base.dm +++ b/code/modules/logic/logic_base.dm @@ -281,4 +281,4 @@ if(tamperproof) tesla_zap(src, 3, power) //If we're tamperproof, we'll just bounce the full shock of the tesla zap we got hit by, so it continues on normally without diminishing return 0 - ..() \ No newline at end of file + ..() diff --git a/code/modules/map_fluff/maps.dm b/code/modules/map_fluff/maps.dm index 638baffde731..e2684d5c7352 100644 --- a/code/modules/map_fluff/maps.dm +++ b/code/modules/map_fluff/maps.dm @@ -1,5 +1,3 @@ -var/datum/map/using_map = new USING_MAP_DATUM - /datum/map var/name = "Unnamed Map" var/full_name = "Unnamed Map" @@ -9,4 +7,4 @@ var/datum/map/using_map = new USING_MAP_DATUM var/dock_name = "THE PirateBay" var/company_name = "BadMan" var/company_short = "BM" - var/starsys_name = "Dull Star" \ No newline at end of file + var/starsys_name = "Dull Star" diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm index 556cfded723d..f0b5c016c5b9 100644 --- a/code/modules/martial_arts/cqc.dm +++ b/code/modules/martial_arts/cqc.dm @@ -232,4 +232,4 @@ to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.") to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.") - to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") \ No newline at end of file + to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.") diff --git a/code/modules/martial_arts/plasma_fist.dm b/code/modules/martial_arts/plasma_fist.dm index 5aed9b8e9e10..ea5aef5ea743 100644 --- a/code/modules/martial_arts/plasma_fist.dm +++ b/code/modules/martial_arts/plasma_fist.dm @@ -83,4 +83,4 @@ to_chat(usr, "You clench your fists and have a flashback of knowledge...") to_chat(usr, "Tornado Sweep: Harm Harm Disarm. Repulses target and everyone back.") to_chat(usr, "Throwback: Disarm Harm Disarm. Throws the target and an item at them.") - to_chat(usr, "The Plasma Fist: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.") \ No newline at end of file + to_chat(usr, "The Plasma Fist: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.") diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index 90ee1d58e051..454b31b3b748 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -214,4 +214,4 @@ ..() /obj/structure/closet/crate/secure/loot/deconstruct(disassembled = TRUE) - boom() \ No newline at end of file + boom() diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 261b7ca9b901..b4a5a0891ed4 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -94,4 +94,4 @@ cut_overlays() var/mutable_appearance/glass_overlay = mutable_appearance(icon, "hostile_env_glass") glass_overlay.appearance_flags = RESET_COLOR - add_overlay(glass_overlay) \ No newline at end of file + add_overlay(glass_overlay) diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm index ee3eb10598a5..f21978db625e 100644 --- a/code/modules/mining/equipment/kinetic_crusher.dm +++ b/code/modules/mining/equipment/kinetic_crusher.dm @@ -439,4 +439,4 @@ new /obj/effect/temp_visual/hierophant/wall/crusher(otherT, user) /obj/effect/temp_visual/hierophant/wall/crusher - duration = 75 \ No newline at end of file + duration = 75 diff --git a/code/modules/mining/equipment/lazarus_injector.dm b/code/modules/mining/equipment/lazarus_injector.dm index e2a7c1921536..2114570ebea2 100644 --- a/code/modules/mining/equipment/lazarus_injector.dm +++ b/code/modules/mining/equipment/lazarus_injector.dm @@ -119,4 +119,4 @@ if(colorindex >= 6) colorindex = 0 icon_state = "mobcap[colorindex]" - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm index 00e838bdf87e..3df1699c7c1f 100644 --- a/code/modules/mining/equipment/marker_beacons.dm +++ b/code/modules/mining/equipment/marker_beacons.dm @@ -58,10 +58,10 @@ GLOBAL_LIST_INIT(marker_beacon_colors, list( transfer_fingerprints_to(M) /obj/item/stack/marker_beacon/AltClick(mob/living/user) - if(!istype(user) || CanUseTopic(user, physical_state) != STATUS_INTERACTIVE) + if(!istype(user) || CanUseTopic(user, GLOB.physical_state) != STATUS_INTERACTIVE) return var/input_color = input(user, "Choose a color.", "Beacon Color") as null|anything in GLOB.marker_beacon_colors - if(!istype(user) || CanUseTopic(user, physical_state) != STATUS_INTERACTIVE) + if(!istype(user) || CanUseTopic(user, GLOB.physical_state) != STATUS_INTERACTIVE) return if(input_color) picked_color = input_color @@ -130,11 +130,11 @@ GLOBAL_LIST_INIT(marker_beacon_colors, list( /obj/structure/marker_beacon/AltClick(mob/living/user) ..() - if(!istype(user) || CanUseTopic(user, physical_state) != STATUS_INTERACTIVE) + if(!istype(user) || CanUseTopic(user, GLOB.physical_state) != STATUS_INTERACTIVE) return var/input_color = input(user, "Choose a color.", "Beacon Color") as null|anything in GLOB.marker_beacon_colors - if(!istype(user) || CanUseTopic(user, physical_state) != STATUS_INTERACTIVE) + if(!istype(user) || CanUseTopic(user, GLOB.physical_state) != STATUS_INTERACTIVE) return if(input_color) picked_color = input_color - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm index c04dba425250..84c8304316df 100644 --- a/code/modules/mining/equipment/mineral_scanner.dm +++ b/code/modules/mining/equipment/mineral_scanner.dm @@ -82,4 +82,4 @@ /obj/effect/temp_visual/mining_overlay/Initialize(mapload) . = ..() - animate(src, alpha = 0, time = duration, easing = EASE_IN) \ No newline at end of file + animate(src, alpha = 0, time = duration, easing = EASE_IN) diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index 4d47f88e0655..7815bad0c37c 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -139,4 +139,4 @@ desc = "A large tool for digging and moving dirt. Was modified with extra safety, making it ineffective as a weapon." force = 1 throwforce = 1 - attack_verb = list("ineffectively hit") \ No newline at end of file + attack_verb = list("ineffectively hit") diff --git a/code/modules/mining/equipment/regenerative_core.dm b/code/modules/mining/equipment/regenerative_core.dm index 4b5e5c911da0..43d31bfe7feb 100644 --- a/code/modules/mining/equipment/regenerative_core.dm +++ b/code/modules/mining/equipment/regenerative_core.dm @@ -135,4 +135,4 @@ /obj/item/organ/internal/regenerative_core/legion/preserved(implanted = 0) ..() - desc = "[src] has been stabilized. It is preserved, allowing you to use it to heal completely without danger of decay." \ No newline at end of file + desc = "[src] has been stabilized. It is preserved, allowing you to use it to heal completely without danger of decay." diff --git a/code/modules/mining/equipment/resonator.dm b/code/modules/mining/equipment/resonator.dm index 4c7c4e599ef0..405b29e7c68f 100644 --- a/code/modules/mining/equipment/resonator.dm +++ b/code/modules/mining/equipment/resonator.dm @@ -113,4 +113,4 @@ /obj/effect/temp_visual/resonance_crush/New() ..() transform = matrix()*1.5 - animate(src, transform = matrix() * 0.1, alpha = 50, time = 4) \ No newline at end of file + animate(src, transform = matrix() * 0.1, alpha = 50, time = 4) diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index bb2ce3a68f85..0c7b836b3226 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -19,7 +19,7 @@ /obj/item/survivalcapsule/proc/get_template() if(template) return - template = shelter_templates[template_id] + template = GLOB.shelter_templates[template_id] if(!template) log_runtime("Shelter template ([template_id]) not found!", src) qdel(src) diff --git a/code/modules/mining/equipment/wormhole_jaunter.dm b/code/modules/mining/equipment/wormhole_jaunter.dm index 7ab7276bb9e3..44462dedfda5 100644 --- a/code/modules/mining/equipment/wormhole_jaunter.dm +++ b/code/modules/mining/equipment/wormhole_jaunter.dm @@ -74,4 +74,4 @@ L.Weaken(6) if(ishuman(L)) shake_camera(L, 20, 1) - addtimer(CALLBACK(L, /mob/living/carbon.proc/vomit), 20) \ No newline at end of file + addtimer(CALLBACK(L, /mob/living/carbon.proc/vomit), 20) diff --git a/code/modules/mining/fulton.dm b/code/modules/mining/fulton.dm index 1ce1350a73af..fbf5bda0de7e 100644 --- a/code/modules/mining/fulton.dm +++ b/code/modules/mining/fulton.dm @@ -192,4 +192,4 @@ GLOBAL_LIST_EMPTY(total_extraction_beacons) return /obj/effect/extraction_holder/singularity_pull() - return \ No newline at end of file + return diff --git a/code/modules/mining/laborcamp/laborshuttle.dm b/code/modules/mining/laborcamp/laborshuttle.dm index cb8b41d047fd..8e585eb15b01 100644 --- a/code/modules/mining/laborcamp/laborshuttle.dm +++ b/code/modules/mining/laborcamp/laborshuttle.dm @@ -24,4 +24,4 @@ if(S && S.name == "laborcamp_away") to_chat(usr, "Shuttle is already at the outpost!") return 0 - ..() \ No newline at end of file + ..() diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index cf2076ecc6f3..0b32b6aedde5 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -58,7 +58,7 @@ GLOBAL_LIST(labor_sheet_values) /obj/machinery/mineral/labor_claim_console/attack_ghost(mob/user) attack_hand(user) -/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) +/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "labor_claim_console.tmpl", name, 450, 625, state) @@ -183,4 +183,4 @@ GLOBAL_LIST(labor_sheet_values) else to_chat(user, "Error: Invalid ID") return - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm index 0f7cd5cb2ef6..afbd7414ebf8 100644 --- a/code/modules/mining/lavaland/ash_flora.dm +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -277,4 +277,4 @@ desc = "A bowl made out of mushrooms. Not food, though it might have contained some at some point." icon = 'icons/obj/lavaland/ash_flora.dmi' icon_state = "mushroom_bowl" - w_class = WEIGHT_CLASS_SMALL \ No newline at end of file + w_class = WEIGHT_CLASS_SMALL diff --git a/code/modules/mining/lavaland/loot/bubblegum_loot.dm b/code/modules/mining/lavaland/loot/bubblegum_loot.dm index 6cc33e1ad929..fa7791edb9ef 100644 --- a/code/modules/mining/lavaland/loot/bubblegum_loot.dm +++ b/code/modules/mining/lavaland/loot/bubblegum_loot.dm @@ -87,4 +87,4 @@ to_chat(H, "You have an overwhelming desire to kill [L]. [L.p_they(TRUE)] [L.p_have()] been marked red! Go kill [L.p_them()]!") H.put_in_hands(new /obj/item/kitchen/knife/butcher(H)) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm index 7b3214748803..ac4d6a1e5515 100644 --- a/code/modules/mining/lavaland/loot/colossus_loot.dm +++ b/code/modules/mining/lavaland/loot/colossus_loot.dm @@ -374,7 +374,7 @@ ..() verbs -= /mob/living/verb/pulled verbs -= /mob/verb/me_verb - var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medsensor.add_hud_to(src) /mob/living/simple_animal/hostile/lightgeist/AttackingTarget() diff --git a/code/modules/mining/lavaland/loot/hierophant_loot.dm b/code/modules/mining/lavaland/loot/hierophant_loot.dm index 813cd66d8817..a3f6e7383807 100644 --- a/code/modules/mining/lavaland/loot/hierophant_loot.dm +++ b/code/modules/mining/lavaland/loot/hierophant_loot.dm @@ -254,7 +254,7 @@ var/obj/effect/temp_visual/hierophant/blast/B = new(T, user, friendly_fire_check) B.damage = HIEROPHANT_CLUB_CARDINAL_DAMAGE B.monster_damage_boost = FALSE - for(var/d in cardinal) + for(var/d in GLOB.cardinal) INVOKE_ASYNC(src, .proc/blast_wall, T, d, user) /obj/item/hierophant_club/proc/blast_wall(turf/T, dir, mob/living/user) //make a wall of blasts blast_range tiles long @@ -282,4 +282,4 @@ var/obj/effect/temp_visual/hierophant/blast/B = new(t, user, friendly_fire_check) B.damage = 15 //keeps monster damage boost due to lower damage -#undef HIEROPHANT_CLUB_CARDINAL_DAMAGE \ No newline at end of file +#undef HIEROPHANT_CLUB_CARDINAL_DAMAGE diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm index 904a34628084..8da61be44d3c 100644 --- a/code/modules/mining/lavaland/necropolis_chests.dm +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -218,4 +218,4 @@ flags = NODROP | DROPDEL desc = "A short wooden rod with a mystical snake inseparably gripping itself and the rod to your forearm. It flows with a healing energy that disperses amongst yourself and those around you. " icon_state = "asclepius_active" - activated = TRUE \ No newline at end of file + activated = TRUE diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 46daf596f8c9..50cb7d098014 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -215,4 +215,4 @@ var/O = new P(src) unload_mineral(O) -#undef SMELT_AMOUNT \ No newline at end of file +#undef SMELT_AMOUNT diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 93d3c41a6928..ed955efda2c2 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -159,7 +159,7 @@ if(!has_minerals) return - for(var/obj/machinery/requests_console/D in allConsoles) + for(var/obj/machinery/requests_console/D in GLOB.allRequestConsoles) if(D.department in src.supply_consoles) if(supply_consoles[D.department] == null || (mineral_name in supply_consoles[D.department])) D.createMessage("Ore Redemption Machine", "New Minerals Available!", msg, 1) @@ -396,4 +396,4 @@ if(powered()) icon_state = initial(icon_state) else - icon_state = "[initial(icon_state)]-off" \ No newline at end of file + icon_state = "[initial(icon_state)]-off" diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 9ec45ebc4d8c..f143b0647228 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -88,4 +88,4 @@ var/obj/item/stack/sheet/out = new inp.type() out.amount = stack_amt unload_mineral(out) - storage.amount -= stack_amt \ No newline at end of file + storage.amount -= stack_amt diff --git a/code/modules/mining/machine_unloading.dm b/code/modules/mining/machine_unloading.dm index 78a63bc6c2dc..39feeb38a61f 100644 --- a/code/modules/mining/machine_unloading.dm +++ b/code/modules/mining/machine_unloading.dm @@ -29,4 +29,4 @@ limit++ if(limit>=10) return - CHECK_TICK \ No newline at end of file + CHECK_TICK diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 3dbeb9c9bfe7..fd9bbc78ff35 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -92,4 +92,4 @@ icon_state = "miningcar" density = 1 icon_opened = "miningcaropen" - icon_closed = "miningcar" \ No newline at end of file + icon_closed = "miningcar" diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm index fdd6b15e8ae3..1198e2fb9d33 100644 --- a/code/modules/mining/minebot.dm +++ b/code/modules/mining/minebot.dm @@ -354,4 +354,4 @@ qdel(src) #undef MINEDRONE_COLLECT -#undef MINEDRONE_ATTACK \ No newline at end of file +#undef MINEDRONE_ATTACK diff --git a/code/modules/mining/mint.dm b/code/modules/mining/mint.dm index dd428341959f..9fb73229ba73 100644 --- a/code/modules/mining/mint.dm +++ b/code/modules/mining/mint.dm @@ -101,4 +101,4 @@ if(!M) M = new /obj/item/storage/bag/money(src) unload_mineral(M) - O.forceMove(M) \ No newline at end of file + O.forceMove(M) diff --git a/code/modules/mining/money_bag.dm b/code/modules/mining/money_bag.dm index 230104f58b78..1cca9c43c469 100644 --- a/code/modules/mining/money_bag.dm +++ b/code/modules/mining/money_bag.dm @@ -1,24 +1,24 @@ -/*****************************Money bag********************************/ - -/obj/item/storage/bag/money - name = "money bag" - icon_state = "moneybag" - force = 10 - throwforce = 0 - resistance_flags = FLAMMABLE - max_integrity = 100 - w_class = WEIGHT_CLASS_BULKY - max_w_class = WEIGHT_CLASS_NORMAL - storage_slots = 40 - max_combined_w_class = 40 - can_hold = list(/obj/item/coin, /obj/item/stack/spacecash) - -/obj/item/storage/bag/money/vault/New() - ..() - new /obj/item/coin/silver(src) - new /obj/item/coin/silver(src) - new /obj/item/coin/silver(src) - new /obj/item/coin/silver(src) - new /obj/item/coin/gold(src) - new /obj/item/coin/gold(src) - new /obj/item/coin/adamantine(src) \ No newline at end of file +/*****************************Money bag********************************/ + +/obj/item/storage/bag/money + name = "money bag" + icon_state = "moneybag" + force = 10 + throwforce = 0 + resistance_flags = FLAMMABLE + max_integrity = 100 + w_class = WEIGHT_CLASS_BULKY + max_w_class = WEIGHT_CLASS_NORMAL + storage_slots = 40 + max_combined_w_class = 40 + can_hold = list(/obj/item/coin, /obj/item/stack/spacecash) + +/obj/item/storage/bag/money/vault/New() + ..() + new /obj/item/coin/silver(src) + new /obj/item/coin/silver(src) + new /obj/item/coin/silver(src) + new /obj/item/coin/silver(src) + new /obj/item/coin/gold(src) + new /obj/item/coin/gold(src) + new /obj/item/coin/adamantine(src) diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm index 6f9572a8a0c8..49dec6713014 100644 --- a/code/modules/mining/ores_coins.dm +++ b/code/modules/mining/ores_coins.dm @@ -19,7 +19,7 @@ pixel_x = rand(0, 16) - 8 pixel_y = rand(0, 8) - 8 if(is_mining_level(z)) - score_oremined++ //When ore spawns, increment score. Only include ore spawned on mining level (No Clown Planet) + GLOB.score_oremined++ //When ore spawns, increment score. Only include ore spawned on mining level (No Clown Planet) /obj/item/stack/ore/welder_act(mob/user, obj/item/I) . = TRUE diff --git a/code/modules/mob/abilities.dm b/code/modules/mob/abilities.dm deleted file mode 100644 index 7ab35a7a73d2..000000000000 --- a/code/modules/mob/abilities.dm +++ /dev/null @@ -1,5 +0,0 @@ -/* -Creature-level abilities. -*/ - -/var/global/list/ability_verbs = list( ) \ No newline at end of file diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm index c7eb33576185..24f129ee3876 100644 --- a/code/modules/mob/dead/dead.dm +++ b/code/modules/mob/dead/dead.dm @@ -30,4 +30,4 @@ SSmobs.dead_players_by_zlevel[new_z] += src registered_z = new_z else - registered_z = null \ No newline at end of file + registered_z = null diff --git a/code/modules/mob/dead/death.dm b/code/modules/mob/dead/death.dm index 704a61abbba0..c4cedcbdaf15 100644 --- a/code/modules/mob/dead/death.dm +++ b/code/modules/mob/dead/death.dm @@ -5,4 +5,4 @@ return /mob/dead - move_resist = INFINITY \ No newline at end of file + move_resist = INFINITY diff --git a/code/modules/mob/dead/observer/login.dm b/code/modules/mob/dead/observer/login.dm index b44d8421b801..76f2ff9dbd0c 100644 --- a/code/modules/mob/dead/observer/login.dm +++ b/code/modules/mob/dead/observer/login.dm @@ -1,9 +1,9 @@ -/mob/dead/observer/Login() - ..() - if(ghostimage) - ghostimage.icon_state = src.icon_state - updateghostimages() - - if(GLOB.non_respawnable_keys[ckey]) - can_reenter_corpse = 0 - GLOB.respawnable_list -= src +/mob/dead/observer/Login() + ..() + if(ghostimage) + ghostimage.icon_state = src.icon_state + updateghostimages() + + if(GLOB.non_respawnable_keys[ckey]) + can_reenter_corpse = 0 + GLOB.respawnable_list -= src diff --git a/code/modules/mob/dead/observer/logout.dm b/code/modules/mob/dead/observer/logout.dm index 93094d5ff30f..8622ba7c8456 100644 --- a/code/modules/mob/dead/observer/logout.dm +++ b/code/modules/mob/dead/observer/logout.dm @@ -1,7 +1,7 @@ -/mob/dead/observer/Logout() - if(client) - client.images -= ghost_images - ..() - spawn(0) - if(src && !key) //we've transferred to another mob. This ghost should be deleted. - qdel(src) +/mob/dead/observer/Logout() + if(client) + client.images -= GLOB.ghost_images + ..() + spawn(0) + if(src && !key) //we've transferred to another mob. This ghost should be deleted. + qdel(src) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index d5848b178ef5..548d22a3fa36 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -1,825 +1,826 @@ -#define GHOST_CAN_REENTER 1 -#define GHOST_IS_OBSERVER 2 - -var/list/image/ghost_images = list() - -GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) - -/mob/dead/observer - name = "ghost" - desc = "It's a g-g-g-g-ghooooost!" //jinkies! - icon = 'icons/mob/mob.dmi' - icon_state = "ghost" - layer = GHOST_LAYER - stat = DEAD - density = FALSE - canmove = FALSE - alpha = 127 - move_resist = INFINITY // don't get pushed around - invisibility = INVISIBILITY_OBSERVER - var/can_reenter_corpse - var/bootime = FALSE - var/started_as_observer //This variable is set to 1 when you enter the game as an observer. - //If you died in the game and are a ghsot - this will remain as null. - //Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot. - universal_speak = TRUE - var/atom/movable/following = null - var/image/ghostimage = null //this mobs ghost image, for deleting and stuff - var/ghostvision = TRUE //is the ghost able to see things humans can't? - var/seedarkness = TRUE - var/data_hud_seen = FALSE //this should one of the defines in __DEFINES/hud.dm - var/ghost_orbit = GHOST_ORBIT_CIRCLE - var/health_scan = FALSE //does the ghost have health scanner mode on? by default it should be off - -/mob/dead/observer/New(mob/body=null, flags=1) - set_invisibility(GLOB.observer_default_invisibility) - - sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF - see_invisible = SEE_INVISIBLE_OBSERVER_AI_EYE - see_in_dark = 100 - verbs += list( - /mob/dead/observer/proc/dead_tele, - /mob/dead/observer/proc/open_spawners_menu) - - // Our new boo spell. - AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/boo(null)) - - can_reenter_corpse = flags & GHOST_CAN_REENTER - started_as_observer = flags & GHOST_IS_OBSERVER - - - stat = DEAD - - var/turf/T - if(ismob(body)) - T = get_turf(body) //Where is the body located? - attack_log = body.attack_log //preserve our attack logs by copying them to our ghost - - var/mutable_appearance/MA = copy_appearance(body) - if(body.mind && body.mind.name) - MA.name = body.mind.name - else if(body.real_name) - MA.name = body.real_name - else - if(gender == MALE) - MA.name = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) - else - MA.name = capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) - - mind = body.mind //we don't transfer the mind but we keep a reference to it. - appearance = MA - - ghostimage = image(icon = icon, loc = src, icon_state = icon_state) - ghostimage.overlays = overlays - ghostimage.dir = dir - ghostimage.appearance_flags |= KEEP_TOGETHER - ghostimage.alpha = alpha - appearance_flags |= KEEP_TOGETHER - ghost_images |= ghostimage - updateallghostimages() - if(!T) - T = pick(latejoin) //Safety in case we cannot find the body's position - forceMove(T) - - if(!name) //To prevent nameless ghosts - name = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) - real_name = name - - //starts ghosts off with all HUDs. - toggle_medHUD() - ..() - -/mob/dead/observer/Destroy() - if(ismob(following)) - var/mob/M = following - M.following_mobs -= src - following = null - if(ghostimage) - ghost_images -= ghostimage - QDEL_NULL(ghostimage) - updateallghostimages() - return ..() - -/mob/dead/observer/examine(mob/user) - . = ..() - if(!invisibility) - . += "It seems extremely obvious." - -// This seems stupid, but it's the easiest way to avoid absolutely ridiculous shit from happening -// Copying an appearance directly from a mob includes it's verb list, it's invisibility, it's alpha, and it's density -// You might recognize these things as "fucking ridiculous to put in an appearance" -// You'd be right, but that's fucking BYOND for you. -/mob/dead/observer/proc/copy_appearance(mutable_appearance/COPY) - var/mutable_appearance/MA = new(src) - - MA.appearance_flags = COPY.appearance_flags - MA.blend_mode = COPY.blend_mode - MA.color = COPY.color - MA.dir = COPY.dir - MA.gender = COPY.gender - MA.icon = COPY.icon - MA.icon_state = COPY.icon_state - MA.layer = COPY.layer - MA.maptext = COPY.maptext - MA.maptext_width = COPY.maptext_width - MA.maptext_height = COPY.maptext_height - MA.maptext_x = COPY.maptext_x - MA.maptext_y = COPY.maptext_y - MA.mouse_opacity = COPY.mouse_opacity - MA.overlays = COPY.overlays - if(!isicon(MA.icon) && !LAZYLEN(MA.overlays)) // Gibbing/dusting/melting removes the icon before ghostize()ing the mob, so we need to account for that - MA.icon = initial(icon) - MA.icon_state = initial(icon_state) - MA.suffix = COPY.suffix - MA.underlays = COPY.underlays - - . = MA - -/mob/dead/CanPass(atom/movable/mover, turf/target, height=0) - return 1 - - -/* -Transfer_mind is there to check if mob is being deleted/not going to have a body. -Works together with spawning an observer, noted above. -*/ -/mob/dead/observer/Life(seconds, times_fired) - ..() - if(!loc) return - if(!client) return 0 - - - -/mob/dead/proc/assess_targets(list/target_list, mob/dead/observer/U) - var/client/C = U.client - for(var/mob/living/carbon/human/target in target_list) - C.images += target.hud_list[SPECIALROLE_HUD] - for(var/mob/living/silicon/target in target_list) - C.images += target.hud_list[SPECIALROLE_HUD] - return 1 - -/mob/proc/ghostize(flags = GHOST_CAN_REENTER) - if(key) - if(player_logged) //if they have disconnected we want to remove their SSD overlay - overlays -= image('icons/effects/effects.dmi', icon_state = "zzz_glow") - if(GLOB.non_respawnable_keys[ckey]) - flags &= ~GHOST_CAN_REENTER - var/mob/dead/observer/ghost = new(src, flags) //Transfer safety to observer spawning proc. - ghost.timeofdeath = src.timeofdeath //BS12 EDIT - GLOB.respawnable_list -= src - if(ghost.can_reenter_corpse) - GLOB.respawnable_list += ghost - else - GLOB.non_respawnable_keys[ckey] = 1 - ghost.key = key - if(!(ghost.client && ghost.client.holder) && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. - ghost.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! - return ghost - -/* -This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues. -*/ -/mob/living/verb/ghost() - set category = "OOC" - set name = "Ghost" - set desc = "Relinquish your life and enter the land of the dead." - - var/mob/M = src - var/warningmsg = null - var/obj/machinery/cryopod/P = istype(loc, /obj/machinery/cryopod) && loc - - if(P) - if(TOO_EARLY_TO_GHOST) - warningmsg = "It's too early in the shift to enter cryo" - else if(suiciding && TOO_EARLY_TO_GHOST) - warningmsg = "You have committed suicide too early in the round" - else if(stat != DEAD) - warningmsg = "You are alive" - if(isAI(src)) - warningmsg = "You are a living AI! You should probably use OOC -> Wipe Core instead." - else if(GLOB.non_respawnable_keys[ckey]) - warningmsg = "You have lost your right to respawn" - - if(warningmsg) - var/response - var/alertmsg = "Are you -sure- you want to ghost?\n([warningmsg]. If you ghost now, you probably won't be able to rejoin the round! You can't change your mind, so choose wisely!)" - response = alert(src, alertmsg,"Are you sure you want to ghost?","Stay in body","Ghost") - if(response != "Ghost") - return - - if(stat == CONSCIOUS) - if(!is_admin_level(z)) - player_ghosted = 1 - if(mind && mind.special_role) - message_admins("[key_name_admin(src)] has ghosted while alive, with special_role: [mind.special_role]") - - if(warningmsg) - // Not respawnable - var/mob/dead/observer/ghost = ghostize(0) // 0 parameter stops them re-entering their body - ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. - else - // Respawnable - ghostize(1) - - // If mob in morgue tray, update tray - var/obj/structure/morgue/Morgue = locate() in M.loc - if(istype(M.loc, /obj/structure/morgue)) - Morgue = M.loc - if(Morgue) - Morgue.update() - - // If mob in cryopod, despawn mob - if(P) - if(!P.control_computer) - P.find_control_computer(urgent=1) - if(P.control_computer) - P.despawn_occupant() - return - -// Ghosts have no momentum, being massless ectoplasm -/mob/dead/observer/Process_Spacemove(movement_dir) - return 1 - -/mob/dead/observer/Move(NewLoc, direct) - update_parallax_contents() - following = null - setDir(direct) - ghostimage.setDir(dir) - - var/oldloc = loc - - if(NewLoc) - forceMove(NewLoc) - else - forceMove(get_turf(src)) //Get out of closets and such as a ghost - if((direct & NORTH) && y < world.maxy) - y++ - else if((direct & SOUTH) && y > 1) - y-- - if((direct & EAST) && x < world.maxx) - x++ - else if((direct & WEST) && x > 1) - x-- - - Moved(oldloc, direct) - -/mob/dead/observer/can_use_hands() return 0 - -/mob/dead/observer/Stat() - ..() - statpanel("Status") - if(client.statpanel == "Status") - show_stat_emergency_shuttle_eta() - stat(null, "Respawnability: [(src in GLOB.respawnable_list) ? "Yes" : "No"]") - -/mob/dead/observer/verb/reenter_corpse() - set category = "Ghost" - set name = "Re-enter Corpse" - if(!client) - return - if(!mind || QDELETED(mind.current)) - to_chat(src, "You have no body.") - return - if(!can_reenter_corpse) - to_chat(src, "You cannot re-enter your body.") - return - if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients - to_chat(usr, "Another consciousness is in your body...It is resisting you.") - return - - mind.current.key = key - - var/obj/structure/morgue/Morgue = locate() in mind.current.loc - if(istype(mind.current.loc,/obj/structure/morgue)) - Morgue = mind.current.loc - if(Morgue) - Morgue.update() - - return 1 - - -/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source) - if(message) - to_chat(src, "[message]") - if(source) - var/obj/screen/alert/A = throw_alert("\ref[source]_notify_cloning", /obj/screen/alert/notify_cloning) - if(A) - if(client && client.prefs && client.prefs.UI_style) - A.icon = ui_style2icon(client.prefs.UI_style) - A.desc = message - var/old_layer = source.layer - var/old_plane = source.plane - source.layer = FLOAT_LAYER - source.plane = FLOAT_PLANE - A.overlays += source - source.layer = old_layer - source.plane = old_plane - to_chat(src, "(Click to re-enter)") - if(sound) - src << sound(sound) - -/mob/dead/observer/proc/show_me_the_hud(hud_index) - var/datum/atom_hud/H = huds[hud_index] - H.add_hud_to(src) - -/mob/dead/observer/proc/remove_the_hud(hud_index) //remove old huds - var/datum/atom_hud/H = huds[hud_index] - H.remove_hud_from(src) - -/mob/dead/observer/verb/toggle_medHUD() - set category = "Ghost" - set name = "Toggle All/Sec/Med/Diag HUDs" - set desc = "Toggles the HUDs." - if(!client) - return - - switch(data_hud_seen) //give new huds - if(FALSE) - data_hud_seen = DATA_HUD_DIAGNOSTIC + DATA_HUD_SECURITY_ADVANCED + DATA_HUD_MEDICAL_ADVANCED - show_me_the_hud(DATA_HUD_DIAGNOSTIC) - show_me_the_hud(DATA_HUD_SECURITY_ADVANCED) - show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED) - to_chat(src, "All HUDs enabled.") - if(DATA_HUD_DIAGNOSTIC + DATA_HUD_SECURITY_ADVANCED + DATA_HUD_MEDICAL_ADVANCED) - data_hud_seen = DATA_HUD_SECURITY_ADVANCED - remove_the_hud(DATA_HUD_DIAGNOSTIC) - remove_the_hud(DATA_HUD_MEDICAL_ADVANCED) - to_chat(src, "Security HUD set.") - if(DATA_HUD_SECURITY_ADVANCED) - data_hud_seen = DATA_HUD_MEDICAL_ADVANCED - remove_the_hud(DATA_HUD_SECURITY_ADVANCED) - show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED) - to_chat(src, "Medical HUD set.") - if(DATA_HUD_MEDICAL_ADVANCED) - data_hud_seen = DATA_HUD_DIAGNOSTIC - remove_the_hud(DATA_HUD_MEDICAL_ADVANCED) - show_me_the_hud(DATA_HUD_DIAGNOSTIC) - to_chat(src, "Diagnostic HUD set.") - else - data_hud_seen = FALSE - remove_the_hud(DATA_HUD_MEDICAL_ADVANCED) - to_chat(src, "HUDs disabled.") - - -/mob/dead/observer/verb/toggle_antagHUD() - set category = "Ghost" - set name = "Toggle AntagHUD" - set desc = "Toggles AntagHUD allowing you to see who is the antagonist" - if(!config.antag_hud_allowed && !client.holder) - to_chat(src, "Admins have disabled this for this round.") - return - if(!client) - return - var/mob/dead/observer/M = src - if(jobban_isbanned(M, "AntagHUD")) - to_chat(src, "You have been banned from using this feature") - return - if(config.antag_hud_restricted && !M.has_enabled_antagHUD && !check_rights(R_ADMIN|R_MOD,0)) - var/response = alert(src, "If you turn this on, you will not be able to take any part in the round.","Are you sure you want to turn this feature on?","Yes","No") - if(response == "No") return - M.can_reenter_corpse = 0 - if(M in GLOB.respawnable_list) - GLOB.respawnable_list -= M - if(!M.has_enabled_antagHUD && !check_rights(R_ADMIN|R_MOD,0)) - M.has_enabled_antagHUD = 1 - - //var/datum/atom_hud/A = huds[DATA_HUD_SECURITY_ADVANCED] - //var/adding_hud = (usr in A.hudusers) ? 0 : 1 - - for(var/datum/atom_hud/antag/H in (huds)) - if(!M.antagHUD) - H.add_hud_to(usr) - else - H.remove_hud_from(usr) - if(!M.antagHUD) - to_chat(usr, "AntagHud Toggled ON") - M.antagHUD = 1 - else - to_chat(usr, "AntagHud Toggled OFF") - M.antagHUD = 0 - -/mob/dead/observer/proc/dead_tele() - set category = "Ghost" - set name = "Teleport" - set desc= "Teleport to a location" - - if(!isobserver(usr)) - to_chat(usr, "Not when you're not dead!") - return - - var/datum/async_input/A = input_autocomplete_async(usr, "Area to jump to: ", ghostteleportlocs) - A.on_close(CALLBACK(src, .proc/teleport)) - -/mob/dead/observer/proc/teleport(area/thearea) - if(!thearea || !isobserver(usr)) - return - - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - L += T - - if(!L || !L.len) - to_chat(usr, "No area available.") - return - - forceMove(pick(L)) - update_parallax_contents() - following = null - -/mob/dead/observer/verb/follow() - set category = "Ghost" - set name = "Orbit" // "Haunt" - set desc = "Follow and orbit a mob." - - var/list/mobs = getpois(skip_mindless=1) - var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) - A.on_close(CALLBACK(src, .proc/ManualFollow)) - -// This is the ghost's follow verb with an argument -/mob/dead/observer/proc/ManualFollow(var/atom/movable/target) - if(!target || !isobserver(usr)) - return - - if(!get_turf(target)) - return - - if(target != src) - if(following && following == target) - return - - var/icon/I = icon(target.icon,target.icon_state,target.dir) - - var/orbitsize = (I.Width()+I.Height())*0.5 - - if(orbitsize == 0) - orbitsize = 40 - - orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25) - - var/rot_seg - - switch(ghost_orbit) - if(GHOST_ORBIT_TRIANGLE) - rot_seg = 3 - if(GHOST_ORBIT_SQUARE) - rot_seg = 4 - if(GHOST_ORBIT_PENTAGON) - rot_seg = 5 - if(GHOST_ORBIT_HEXAGON) - rot_seg = 6 - else //Circular - rot_seg = 36 //360/10 bby, smooth enough aproximation of a circle - - following = target - to_chat(src, "Now following [target]") - orbit(target,orbitsize, FALSE, 20, rot_seg) - -/mob/dead/observer/orbit() - setDir(2)//reset dir so the right directional sprites show up - return ..() - -/mob/proc/update_following() - . = get_turf(src) - for(var/mob/dead/observer/M in following_mobs) - if(M.following != src) - following_mobs -= M - else - if(M.loc != .) - M.forceMove(.) - -/mob - var/list/following_mobs = list() - -/mob/Move() - . = ..() - if(.) - update_following() - -/mob/Life(seconds, times_fired) - // to catch teleports etc which directly set loc - update_following() - return ..() - -/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak - set category = "Ghost" - set name = "Jump to Mob" - set desc = "Teleport to a mob" - - if(isobserver(usr)) //Make sure they're an observer! - var/list/dest = getpois(mobs_only=1) //Fill list, prompt user with list - var/datum/async_input/A = input_autocomplete_async(usr, "Enter a mob name: ", dest) - A.on_close(CALLBACK(src, .proc/jump_to_mob)) - -/mob/dead/observer/proc/jump_to_mob(mob/M) - if(!M || !isobserver(usr)) - return - var/mob/A = src //Source mob - var/turf/T = get_turf(M) //Turf of the destination mob - - if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination. - A.forceMove(T) - M.update_parallax_contents() - following = null - return - to_chat(A, "This mob is not located in the game world.") - -/* Now a spell. See spells.dm -/mob/dead/observer/verb/boo() - set category = "Ghost" - set name = "Boo!" - set desc= "Scare your crew members because of boredom!" - - if(bootime > world.time) return - bootime = world.time + 600 - var/obj/machinery/light/L = locate(/obj/machinery/light) in view(1, src) - if(L) - L.flicker() - //Maybe in the future we can add more spooky code here! - return -*/ - -/mob/dead/observer/memory() - set hidden = 1 - to_chat(src, "You are dead! You have no mind to store memory!") - -/mob/dead/observer/add_memory() - set hidden = 1 - to_chat(src, "You are dead! You have no mind to store memory!") - - -/mob/dead/observer/verb/toggle_health_scan() - set name = "Toggle Health Scan" - set desc = "Toggles whether you health-scan living beings on click" - set category = "Ghost" - - if(health_scan) //remove old huds - to_chat(src, "Health scan disabled.") - health_scan = FALSE - else - to_chat(src, "Health scan enabled.") - health_scan = TRUE - -/mob/dead/observer/verb/analyze_air() - set name = "Analyze Air" - set category = "Ghost" - - if(!istype(usr, /mob/dead/observer)) return - - // Shamelessly copied from the Gas Analyzers - if(!( istype(usr.loc, /turf) )) - return - - var/datum/gas_mixture/environment = usr.loc.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() - - to_chat(src, "Results:") - if(abs(pressure - ONE_ATMOSPHERE) < 10) - to_chat(src, "Pressure: [round(pressure,0.1)] kPa") - else - to_chat(src, "Pressure: [round(pressure,0.1)] kPa") - if(total_moles) - var/o2_concentration = environment.oxygen/total_moles - var/n2_concentration = environment.nitrogen/total_moles - var/co2_concentration = environment.carbon_dioxide/total_moles - var/plasma_concentration = environment.toxins/total_moles - - var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) - if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(src, "Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)") - else - to_chat(src, "Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)") - - if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(src, "Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)") - else - to_chat(src, "Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)") - - if(co2_concentration > 0.01) - to_chat(src, "CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)") - else - to_chat(src, "CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)") - - if(plasma_concentration > 0.01) - to_chat(src, "Plasma: [round(plasma_concentration*100)]% ([round(environment.toxins,0.01)] moles)") - - if(unknown_concentration > 0.01) - to_chat(src, "Unknown: [round(unknown_concentration*100)]% ([round(unknown_concentration*total_moles,0.01)] moles)") - - to_chat(src, "Temperature: [round(environment.temperature-T0C,0.1)]°C") - to_chat(src, "Heat Capacity: [round(environment.heat_capacity(),0.1)]") - -/mob/dead/observer/verb/view_manifest() - set name = "View Crew Manifest" - set category = "Ghost" - - var/dat - dat += "

    Crew Manifest

    " - dat += data_core.get_manifest() - - src << browse(dat, "window=manifest;size=370x420;can_close=1") - -//this is called when a ghost is drag clicked to something. -/mob/dead/observer/MouseDrop(atom/over) - if(!usr || !over) return - if(isobserver(usr) && usr.client && usr.client.holder) - if(usr.client.holder.cmd_ghost_drag(src,over)) - return - - return ..() - -/proc/ghost_follow_link(var/atom/target, var/atom/ghost) - if((!target) || (!ghost)) return - if(isAI(target)) // AI core/eye follow links - var/mob/living/silicon/ai/A = target - . = "core" - if(A.client && A.eyeobj) // No point following clientless AI eyes - . += "|eye" - return - else if(istype(target, /mob/dead/observer)) - var/mob/dead/observer/O = target - . = "follow" - if(O.mind && O.mind.current) - . += "|body" - return - else - return "follow" - -//BEGIN TELEPORT HREF CODE -/mob/dead/observer/Topic(href, href_list) - if(usr != src) - return - ..() - - if(href_list["track"]) - var/atom/target = locate(href_list["track"]) - if(target) - ManualFollow(target) - - if(href_list["follow"]) - var/atom/target = locate(href_list["follow"]) - if(target) - ManualFollow(target) - - if(href_list["jump"]) - var/mob/target = locate(href_list["jump"]) - var/mob/A = usr; - to_chat(A, "Teleporting to [target]...") - //var/mob/living/silicon/ai/A = locate(href_list["track2"]) in GLOB.mob_list - if(target && target != usr) - spawn(0) - var/turf/pos = get_turf(A) - var/turf/T=get_turf(target) - if(T != pos) - if(!T) - return - if(!client) - return - forceMove(T) - following = null - - if(href_list["reenter"]) - reenter_corpse() - - ..() -//END TELEPORT HREF CODE - -/mob/dead/observer/verb/toggle_anonsay() - set name = "Toggle Anonymous Dead-chat" - set category = "Ghost" - set desc = "Toggles showing your key in dead chat." - client.prefs.ghost_anonsay = !client.prefs.ghost_anonsay - to_chat(src, "As a ghost, your key will [(client.prefs.ghost_anonsay) ? "no longer" : "now"] be shown when you speak in dead chat.
    ") - client.prefs.save_preferences(src) - -/mob/dead/observer/verb/toggle_ghostsee() - set name = "Toggle Ghost Vision" - set desc = "Toggles your ability to see things only ghosts can see, like other ghosts" - set category = "Ghost" - ghostvision = !(ghostvision) - update_sight() - to_chat(usr, "You [(ghostvision?"now":"no longer")] have ghost vision.") - -/mob/dead/observer/verb/toggle_darkness() - set name = "Toggle Darkness" - set category = "Ghost" - switch(lighting_alpha) - if (LIGHTING_PLANE_ALPHA_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - else - lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE - - update_sight() - -/mob/dead/observer/update_sight() - if (!ghostvision) - see_invisible = SEE_INVISIBLE_LIVING - else - see_invisible = SEE_INVISIBLE_OBSERVER - - updateghostimages() - . = ..() - -/mob/dead/observer/proc/updateghostsight() - if(!seedarkness) - see_invisible = SEE_INVISIBLE_OBSERVER_NOLIGHTING - else - see_invisible = SEE_INVISIBLE_OBSERVER - if(!ghostvision) - see_invisible = SEE_INVISIBLE_LIVING - - updateghostimages() - -/proc/updateallghostimages() - for(var/mob/dead/observer/O in GLOB.player_list) - O.updateghostimages() - -/mob/dead/observer/proc/updateghostimages() - if(!client) - return - if(seedarkness || !ghostvision) - client.images -= ghost_images - else - //add images for the 60inv things ghosts can normally see when darkness is enabled so they can see them now - client.images |= ghost_images - if(ghostimage) - client.images -= ghostimage //remove ourself - -/mob/proc/can_admin_interact() - return FALSE - -/mob/proc/can_advanced_admin_interact() - return FALSE - -/mob/dead/observer/can_admin_interact() - return check_rights(R_ADMIN, 0, src) - -/mob/dead/observer/can_advanced_admin_interact() - if(!can_admin_interact()) - return FALSE - - if(client && client.advanced_admin_interaction) - return TRUE - - return FALSE - -/mob/dead/observer/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE) - return TRUE - -//this is a mob verb instead of atom for performance reasons -//see /mob/verb/examinate() in mob.dm for more info -//overriden here and in /mob/living for different point span classes and sanity checks -/mob/dead/observer/pointed(atom/A as mob|obj|turf in view()) - if(!..()) - return 0 - usr.visible_message("[src] points to [A].") - return 1 - -/mob/dead/observer/proc/incarnate_ghost() - if(!client) - return - - var/mob/living/carbon/human/new_char = new(get_turf(src)) - client.prefs.copy_to(new_char) - if(mind) - mind.active = TRUE - mind.transfer_to(new_char) - else - new_char.key = key - - return new_char - -/mob/dead/observer/is_literate() - return TRUE - -/mob/dead/observer/proc/set_invisibility(value) - invisibility = value - if(!value) - set_light(1, 2) - else - set_light(0, 0) - -/mob/dead/observer/vv_edit_var(var_name, var_value) - . = ..() - if(var_name == "invisibility") - set_invisibility(invisibility) // updates light - -/proc/set_observer_default_invisibility(amount, message=null) - for(var/mob/dead/observer/G in GLOB.player_list) - G.set_invisibility(amount) - if(message) - to_chat(G, message) - GLOB.observer_default_invisibility = amount - -/mob/dead/observer/proc/open_spawners_menu() - set name = "Mob spawners menu" - set desc = "See all currently available ghost spawners" - set category = "Ghost" - - var/datum/spawners_menu/menu = new /datum/spawners_menu(src) - menu.ui_interact(src) +#define GHOST_CAN_REENTER 1 +#define GHOST_IS_OBSERVER 2 + +GLOBAL_LIST_EMPTY(ghost_images) + +GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER) + +/mob/dead/observer + name = "ghost" + desc = "It's a g-g-g-g-ghooooost!" //jinkies! + icon = 'icons/mob/mob.dmi' + icon_state = "ghost" + layer = GHOST_LAYER + stat = DEAD + density = FALSE + canmove = FALSE + alpha = 127 + move_resist = INFINITY // don't get pushed around + invisibility = INVISIBILITY_OBSERVER + var/can_reenter_corpse + var/bootime = FALSE + var/started_as_observer //This variable is set to 1 when you enter the game as an observer. + //If you died in the game and are a ghsot - this will remain as null. + //Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot. + universal_speak = TRUE + var/atom/movable/following = null + var/image/ghostimage = null //this mobs ghost image, for deleting and stuff + var/ghostvision = TRUE //is the ghost able to see things humans can't? + var/seedarkness = TRUE + var/data_hud_seen = FALSE //this should one of the defines in __DEFINES/hud.dm + var/ghost_orbit = GHOST_ORBIT_CIRCLE + var/health_scan = FALSE //does the ghost have health scanner mode on? by default it should be off + +/mob/dead/observer/New(mob/body=null, flags=1) + set_invisibility(GLOB.observer_default_invisibility) + + sight |= SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF + see_invisible = SEE_INVISIBLE_OBSERVER_AI_EYE + see_in_dark = 100 + verbs += list( + /mob/dead/observer/proc/dead_tele, + /mob/dead/observer/proc/open_spawners_menu) + + // Our new boo spell. + AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/boo(null)) + + can_reenter_corpse = flags & GHOST_CAN_REENTER + started_as_observer = flags & GHOST_IS_OBSERVER + + + stat = DEAD + + var/turf/T + if(ismob(body)) + T = get_turf(body) //Where is the body located? + attack_log_old = body.attack_log_old //preserve our attack logs by copying them to our ghost + logs = body.logs.Copy() + + var/mutable_appearance/MA = copy_appearance(body) + if(body.mind && body.mind.name) + MA.name = body.mind.name + else if(body.real_name) + MA.name = body.real_name + else + if(gender == MALE) + MA.name = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) + else + MA.name = capitalize(pick(GLOB.first_names_female)) + " " + capitalize(pick(GLOB.last_names)) + + mind = body.mind //we don't transfer the mind but we keep a reference to it. + appearance = MA + + ghostimage = image(icon = icon, loc = src, icon_state = icon_state) + ghostimage.overlays = overlays + ghostimage.dir = dir + ghostimage.appearance_flags |= KEEP_TOGETHER + ghostimage.alpha = alpha + appearance_flags |= KEEP_TOGETHER + GLOB.ghost_images |= ghostimage + updateallghostimages() + if(!T) + T = pick(GLOB.latejoin) //Safety in case we cannot find the body's position + forceMove(T) + + if(!name) //To prevent nameless ghosts + name = capitalize(pick(GLOB.first_names_male)) + " " + capitalize(pick(GLOB.last_names)) + real_name = name + + //starts ghosts off with all HUDs. + toggle_medHUD() + ..() + +/mob/dead/observer/Destroy() + if(ismob(following)) + var/mob/M = following + M.following_mobs -= src + following = null + if(ghostimage) + GLOB.ghost_images -= ghostimage + QDEL_NULL(ghostimage) + updateallghostimages() + return ..() + +/mob/dead/observer/examine(mob/user) + . = ..() + if(!invisibility) + . += "It seems extremely obvious." + +// This seems stupid, but it's the easiest way to avoid absolutely ridiculous shit from happening +// Copying an appearance directly from a mob includes it's verb list, it's invisibility, it's alpha, and it's density +// You might recognize these things as "fucking ridiculous to put in an appearance" +// You'd be right, but that's fucking BYOND for you. +/mob/dead/observer/proc/copy_appearance(mutable_appearance/COPY) + var/mutable_appearance/MA = new(src) + + MA.appearance_flags = COPY.appearance_flags + MA.blend_mode = COPY.blend_mode + MA.color = COPY.color + MA.dir = COPY.dir + MA.gender = COPY.gender + MA.icon = COPY.icon + MA.icon_state = COPY.icon_state + MA.layer = COPY.layer + MA.maptext = COPY.maptext + MA.maptext_width = COPY.maptext_width + MA.maptext_height = COPY.maptext_height + MA.maptext_x = COPY.maptext_x + MA.maptext_y = COPY.maptext_y + MA.mouse_opacity = COPY.mouse_opacity + MA.overlays = COPY.overlays + if(!isicon(MA.icon) && !LAZYLEN(MA.overlays)) // Gibbing/dusting/melting removes the icon before ghostize()ing the mob, so we need to account for that + MA.icon = initial(icon) + MA.icon_state = initial(icon_state) + MA.suffix = COPY.suffix + MA.underlays = COPY.underlays + + . = MA + +/mob/dead/CanPass(atom/movable/mover, turf/target, height=0) + return 1 + + +/* +Transfer_mind is there to check if mob is being deleted/not going to have a body. +Works together with spawning an observer, noted above. +*/ +/mob/dead/observer/Life(seconds, times_fired) + ..() + if(!loc) return + if(!client) return 0 + + + +/mob/dead/proc/assess_targets(list/target_list, mob/dead/observer/U) + var/client/C = U.client + for(var/mob/living/carbon/human/target in target_list) + C.images += target.hud_list[SPECIALROLE_HUD] + for(var/mob/living/silicon/target in target_list) + C.images += target.hud_list[SPECIALROLE_HUD] + return 1 + +/mob/proc/ghostize(flags = GHOST_CAN_REENTER) + if(key) + if(player_logged) //if they have disconnected we want to remove their SSD overlay + overlays -= image('icons/effects/effects.dmi', icon_state = "zzz_glow") + if(GLOB.non_respawnable_keys[ckey]) + flags &= ~GHOST_CAN_REENTER + var/mob/dead/observer/ghost = new(src, flags) //Transfer safety to observer spawning proc. + ghost.timeofdeath = src.timeofdeath //BS12 EDIT + GLOB.respawnable_list -= src + if(ghost.can_reenter_corpse) + GLOB.respawnable_list += ghost + else + GLOB.non_respawnable_keys[ckey] = 1 + ghost.key = key + if(!(ghost.client && ghost.client.holder) && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. + ghost.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! + return ghost + +/* +This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues. +*/ +/mob/living/verb/ghost() + set category = "OOC" + set name = "Ghost" + set desc = "Relinquish your life and enter the land of the dead." + + var/mob/M = src + var/warningmsg = null + var/obj/machinery/cryopod/P = istype(loc, /obj/machinery/cryopod) && loc + + if(P) + if(TOO_EARLY_TO_GHOST) + warningmsg = "It's too early in the shift to enter cryo" + else if(suiciding && TOO_EARLY_TO_GHOST) + warningmsg = "You have committed suicide too early in the round" + else if(stat != DEAD) + warningmsg = "You are alive" + if(isAI(src)) + warningmsg = "You are a living AI! You should probably use OOC -> Wipe Core instead." + else if(GLOB.non_respawnable_keys[ckey]) + warningmsg = "You have lost your right to respawn" + + if(warningmsg) + var/response + var/alertmsg = "Are you -sure- you want to ghost?\n([warningmsg]. If you ghost now, you probably won't be able to rejoin the round! You can't change your mind, so choose wisely!)" + response = alert(src, alertmsg,"Are you sure you want to ghost?","Stay in body","Ghost") + if(response != "Ghost") + return + + if(stat == CONSCIOUS) + if(!is_admin_level(z)) + player_ghosted = 1 + if(mind && mind.special_role) + message_admins("[key_name_admin(src)] has ghosted while alive, with special_role: [mind.special_role]") + + if(warningmsg) + // Not respawnable + var/mob/dead/observer/ghost = ghostize(0) // 0 parameter stops them re-entering their body + ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. + else + // Respawnable + ghostize(1) + + // If mob in morgue tray, update tray + var/obj/structure/morgue/Morgue = locate() in M.loc + if(istype(M.loc, /obj/structure/morgue)) + Morgue = M.loc + if(Morgue) + Morgue.update() + + // If mob in cryopod, despawn mob + if(P) + if(!P.control_computer) + P.find_control_computer(urgent=1) + if(P.control_computer) + P.despawn_occupant() + return + +// Ghosts have no momentum, being massless ectoplasm +/mob/dead/observer/Process_Spacemove(movement_dir) + return 1 + +/mob/dead/observer/Move(NewLoc, direct) + update_parallax_contents() + following = null + setDir(direct) + ghostimage.setDir(dir) + + var/oldloc = loc + + if(NewLoc) + forceMove(NewLoc) + else + forceMove(get_turf(src)) //Get out of closets and such as a ghost + if((direct & NORTH) && y < world.maxy) + y++ + else if((direct & SOUTH) && y > 1) + y-- + if((direct & EAST) && x < world.maxx) + x++ + else if((direct & WEST) && x > 1) + x-- + + Moved(oldloc, direct) + +/mob/dead/observer/can_use_hands() return 0 + +/mob/dead/observer/Stat() + ..() + statpanel("Status") + if(client.statpanel == "Status") + show_stat_emergency_shuttle_eta() + stat(null, "Respawnability: [(src in GLOB.respawnable_list) ? "Yes" : "No"]") + +/mob/dead/observer/verb/reenter_corpse() + set category = "Ghost" + set name = "Re-enter Corpse" + if(!client) + return + if(!mind || QDELETED(mind.current)) + to_chat(src, "You have no body.") + return + if(!can_reenter_corpse) + to_chat(src, "You cannot re-enter your body.") + return + if(mind.current.key && copytext(mind.current.key,1,2)!="@") //makes sure we don't accidentally kick any clients + to_chat(usr, "Another consciousness is in your body...It is resisting you.") + return + + mind.current.key = key + + var/obj/structure/morgue/Morgue = locate() in mind.current.loc + if(istype(mind.current.loc,/obj/structure/morgue)) + Morgue = mind.current.loc + if(Morgue) + Morgue.update() + + return 1 + + +/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source) + if(message) + to_chat(src, "[message]") + if(source) + var/obj/screen/alert/A = throw_alert("\ref[source]_notify_cloning", /obj/screen/alert/notify_cloning) + if(A) + if(client && client.prefs && client.prefs.UI_style) + A.icon = ui_style2icon(client.prefs.UI_style) + A.desc = message + var/old_layer = source.layer + var/old_plane = source.plane + source.layer = FLOAT_LAYER + source.plane = FLOAT_PLANE + A.overlays += source + source.layer = old_layer + source.plane = old_plane + to_chat(src, "(Click to re-enter)") + if(sound) + src << sound(sound) + +/mob/dead/observer/proc/show_me_the_hud(hud_index) + var/datum/atom_hud/H = GLOB.huds[hud_index] + H.add_hud_to(src) + +/mob/dead/observer/proc/remove_the_hud(hud_index) //remove old huds + var/datum/atom_hud/H = GLOB.huds[hud_index] + H.remove_hud_from(src) + +/mob/dead/observer/verb/toggle_medHUD() + set category = "Ghost" + set name = "Toggle All/Sec/Med/Diag HUDs" + set desc = "Toggles the HUDs." + if(!client) + return + + switch(data_hud_seen) //give new huds + if(FALSE) + data_hud_seen = DATA_HUD_DIAGNOSTIC + DATA_HUD_SECURITY_ADVANCED + DATA_HUD_MEDICAL_ADVANCED + show_me_the_hud(DATA_HUD_DIAGNOSTIC) + show_me_the_hud(DATA_HUD_SECURITY_ADVANCED) + show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED) + to_chat(src, "All HUDs enabled.") + if(DATA_HUD_DIAGNOSTIC + DATA_HUD_SECURITY_ADVANCED + DATA_HUD_MEDICAL_ADVANCED) + data_hud_seen = DATA_HUD_SECURITY_ADVANCED + remove_the_hud(DATA_HUD_DIAGNOSTIC) + remove_the_hud(DATA_HUD_MEDICAL_ADVANCED) + to_chat(src, "Security HUD set.") + if(DATA_HUD_SECURITY_ADVANCED) + data_hud_seen = DATA_HUD_MEDICAL_ADVANCED + remove_the_hud(DATA_HUD_SECURITY_ADVANCED) + show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED) + to_chat(src, "Medical HUD set.") + if(DATA_HUD_MEDICAL_ADVANCED) + data_hud_seen = DATA_HUD_DIAGNOSTIC + remove_the_hud(DATA_HUD_MEDICAL_ADVANCED) + show_me_the_hud(DATA_HUD_DIAGNOSTIC) + to_chat(src, "Diagnostic HUD set.") + else + data_hud_seen = FALSE + remove_the_hud(DATA_HUD_MEDICAL_ADVANCED) + to_chat(src, "HUDs disabled.") + + +/mob/dead/observer/verb/toggle_antagHUD() + set category = "Ghost" + set name = "Toggle AntagHUD" + set desc = "Toggles AntagHUD allowing you to see who is the antagonist" + if(!config.antag_hud_allowed && !client.holder) + to_chat(src, "Admins have disabled this for this round.") + return + if(!client) + return + var/mob/dead/observer/M = src + if(jobban_isbanned(M, "AntagHUD")) + to_chat(src, "You have been banned from using this feature") + return + if(config.antag_hud_restricted && !M.has_enabled_antagHUD && !check_rights(R_ADMIN|R_MOD,0)) + var/response = alert(src, "If you turn this on, you will not be able to take any part in the round.","Are you sure you want to turn this feature on?","Yes","No") + if(response == "No") return + M.can_reenter_corpse = 0 + if(M in GLOB.respawnable_list) + GLOB.respawnable_list -= M + if(!M.has_enabled_antagHUD && !check_rights(R_ADMIN|R_MOD,0)) + M.has_enabled_antagHUD = 1 + + //var/datum/atom_hud/A = huds[DATA_HUD_SECURITY_ADVANCED] + //var/adding_hud = (usr in A.hudusers) ? 0 : 1 + + for(var/datum/atom_hud/antag/H in (GLOB.huds)) + if(!M.antagHUD) + H.add_hud_to(usr) + else + H.remove_hud_from(usr) + if(!M.antagHUD) + to_chat(usr, "AntagHud Toggled ON") + M.antagHUD = 1 + else + to_chat(usr, "AntagHud Toggled OFF") + M.antagHUD = 0 + +/mob/dead/observer/proc/dead_tele() + set category = "Ghost" + set name = "Teleport" + set desc= "Teleport to a location" + + if(!isobserver(usr)) + to_chat(usr, "Not when you're not dead!") + return + + var/datum/async_input/A = input_autocomplete_async(usr, "Area to jump to: ", GLOB.ghostteleportlocs) + A.on_close(CALLBACK(src, .proc/teleport)) + +/mob/dead/observer/proc/teleport(area/thearea) + if(!thearea || !isobserver(usr)) + return + + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + L += T + + if(!L || !L.len) + to_chat(usr, "No area available.") + return + + forceMove(pick(L)) + update_parallax_contents() + following = null + +/mob/dead/observer/verb/follow() + set category = "Ghost" + set name = "Orbit" // "Haunt" + set desc = "Follow and orbit a mob." + + var/list/mobs = getpois(FALSE, TRUE, TRUE, TRUE) + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) + A.on_close(CALLBACK(src, .proc/ManualFollow)) + +// This is the ghost's follow verb with an argument +/mob/dead/observer/proc/ManualFollow(var/atom/movable/target) + if(!target || !isobserver(usr)) + return + + if(!get_turf(target)) + return + + if(target != src) + if(following && following == target) + return + + var/icon/I = icon(target.icon,target.icon_state,target.dir) + + var/orbitsize = (I.Width()+I.Height())*0.5 + + if(orbitsize == 0) + orbitsize = 40 + + orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25) + + var/rot_seg + + switch(ghost_orbit) + if(GHOST_ORBIT_TRIANGLE) + rot_seg = 3 + if(GHOST_ORBIT_SQUARE) + rot_seg = 4 + if(GHOST_ORBIT_PENTAGON) + rot_seg = 5 + if(GHOST_ORBIT_HEXAGON) + rot_seg = 6 + else //Circular + rot_seg = 36 //360/10 bby, smooth enough aproximation of a circle + + following = target + to_chat(src, "Now following [target]") + orbit(target,orbitsize, FALSE, 20, rot_seg) + +/mob/dead/observer/orbit() + setDir(2)//reset dir so the right directional sprites show up + return ..() + +/mob/proc/update_following() + . = get_turf(src) + for(var/mob/dead/observer/M in following_mobs) + if(M.following != src) + following_mobs -= M + else + if(M.loc != .) + M.forceMove(.) + +/mob + var/list/following_mobs = list() + +/mob/Move() + . = ..() + if(.) + update_following() + +/mob/Life(seconds, times_fired) + // to catch teleports etc which directly set loc + update_following() + return ..() + +/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak + set category = "Ghost" + set name = "Jump to Mob" + set desc = "Teleport to a mob" + + if(isobserver(usr)) //Make sure they're an observer! + var/list/dest = getpois(mobs_only=TRUE) //Fill list, prompt user with list + var/datum/async_input/A = input_autocomplete_async(usr, "Enter a mob name: ", dest) + A.on_close(CALLBACK(src, .proc/jump_to_mob)) + +/mob/dead/observer/proc/jump_to_mob(mob/M) + if(!M || !isobserver(usr)) + return + var/mob/A = src //Source mob + var/turf/T = get_turf(M) //Turf of the destination mob + + if(T && isturf(T)) //Make sure the turf exists, then move the source to that destination. + A.forceMove(T) + M.update_parallax_contents() + following = null + return + to_chat(A, "This mob is not located in the game world.") + +/* Now a spell. See spells.dm +/mob/dead/observer/verb/boo() + set category = "Ghost" + set name = "Boo!" + set desc= "Scare your crew members because of boredom!" + + if(bootime > world.time) return + bootime = world.time + 600 + var/obj/machinery/light/L = locate(/obj/machinery/light) in view(1, src) + if(L) + L.flicker() + //Maybe in the future we can add more spooky code here! + return +*/ + +/mob/dead/observer/memory() + set hidden = 1 + to_chat(src, "You are dead! You have no mind to store memory!") + +/mob/dead/observer/add_memory() + set hidden = 1 + to_chat(src, "You are dead! You have no mind to store memory!") + + +/mob/dead/observer/verb/toggle_health_scan() + set name = "Toggle Health Scan" + set desc = "Toggles whether you health-scan living beings on click" + set category = "Ghost" + + if(health_scan) //remove old huds + to_chat(src, "Health scan disabled.") + health_scan = FALSE + else + to_chat(src, "Health scan enabled.") + health_scan = TRUE + +/mob/dead/observer/verb/analyze_air() + set name = "Analyze Air" + set category = "Ghost" + + if(!istype(usr, /mob/dead/observer)) return + + // Shamelessly copied from the Gas Analyzers + if(!( istype(usr.loc, /turf) )) + return + + var/datum/gas_mixture/environment = usr.loc.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + to_chat(src, "Results:") + if(abs(pressure - ONE_ATMOSPHERE) < 10) + to_chat(src, "Pressure: [round(pressure,0.1)] kPa") + else + to_chat(src, "Pressure: [round(pressure,0.1)] kPa") + if(total_moles) + var/o2_concentration = environment.oxygen/total_moles + var/n2_concentration = environment.nitrogen/total_moles + var/co2_concentration = environment.carbon_dioxide/total_moles + var/plasma_concentration = environment.toxins/total_moles + + var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration) + if(abs(n2_concentration - N2STANDARD) < 20) + to_chat(src, "Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)") + else + to_chat(src, "Nitrogen: [round(n2_concentration*100)]% ([round(environment.nitrogen,0.01)] moles)") + + if(abs(o2_concentration - O2STANDARD) < 2) + to_chat(src, "Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)") + else + to_chat(src, "Oxygen: [round(o2_concentration*100)]% ([round(environment.oxygen,0.01)] moles)") + + if(co2_concentration > 0.01) + to_chat(src, "CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)") + else + to_chat(src, "CO2: [round(co2_concentration*100)]% ([round(environment.carbon_dioxide,0.01)] moles)") + + if(plasma_concentration > 0.01) + to_chat(src, "Plasma: [round(plasma_concentration*100)]% ([round(environment.toxins,0.01)] moles)") + + if(unknown_concentration > 0.01) + to_chat(src, "Unknown: [round(unknown_concentration*100)]% ([round(unknown_concentration*total_moles,0.01)] moles)") + + to_chat(src, "Temperature: [round(environment.temperature-T0C,0.1)]°C") + to_chat(src, "Heat Capacity: [round(environment.heat_capacity(),0.1)]") + +/mob/dead/observer/verb/view_manifest() + set name = "View Crew Manifest" + set category = "Ghost" + + var/dat + dat += "

    Crew Manifest

    " + dat += GLOB.data_core.get_manifest() + + src << browse(dat, "window=manifest;size=370x420;can_close=1") + +//this is called when a ghost is drag clicked to something. +/mob/dead/observer/MouseDrop(atom/over) + if(!usr || !over) return + if(isobserver(usr) && usr.client && usr.client.holder) + if(usr.client.holder.cmd_ghost_drag(src,over)) + return + + return ..() + +/proc/ghost_follow_link(var/atom/target, var/atom/ghost) + if((!target) || (!ghost)) return + if(isAI(target)) // AI core/eye follow links + var/mob/living/silicon/ai/A = target + . = "core" + if(A.client && A.eyeobj) // No point following clientless AI eyes + . += "|eye" + return + else if(istype(target, /mob/dead/observer)) + var/mob/dead/observer/O = target + . = "follow" + if(O.mind && O.mind.current) + . += "|body" + return + else + return "follow" + +//BEGIN TELEPORT HREF CODE +/mob/dead/observer/Topic(href, href_list) + if(usr != src) + return + ..() + + if(href_list["track"]) + var/atom/target = locate(href_list["track"]) + if(target) + ManualFollow(target) + + if(href_list["follow"]) + var/atom/target = locate(href_list["follow"]) + if(target) + ManualFollow(target) + + if(href_list["jump"]) + var/mob/target = locate(href_list["jump"]) + var/mob/A = usr; + to_chat(A, "Teleporting to [target]...") + //var/mob/living/silicon/ai/A = locate(href_list["track2"]) in GLOB.mob_list + if(target && target != usr) + spawn(0) + var/turf/pos = get_turf(A) + var/turf/T=get_turf(target) + if(T != pos) + if(!T) + return + if(!client) + return + forceMove(T) + following = null + + if(href_list["reenter"]) + reenter_corpse() + + ..() +//END TELEPORT HREF CODE + +/mob/dead/observer/verb/toggle_anonsay() + set name = "Toggle Anonymous Dead-chat" + set category = "Ghost" + set desc = "Toggles showing your key in dead chat." + client.prefs.ghost_anonsay = !client.prefs.ghost_anonsay + to_chat(src, "As a ghost, your key will [(client.prefs.ghost_anonsay) ? "no longer" : "now"] be shown when you speak in dead chat.
    ") + client.prefs.save_preferences(src) + +/mob/dead/observer/verb/toggle_ghostsee() + set name = "Toggle Ghost Vision" + set desc = "Toggles your ability to see things only ghosts can see, like other ghosts" + set category = "Ghost" + ghostvision = !(ghostvision) + update_sight() + to_chat(usr, "You [(ghostvision?"now":"no longer")] have ghost vision.") + +/mob/dead/observer/verb/toggle_darkness() + set name = "Toggle Darkness" + set category = "Ghost" + switch(lighting_alpha) + if (LIGHTING_PLANE_ALPHA_VISIBLE) + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + if (LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE) + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + if (LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE) + lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + else + lighting_alpha = LIGHTING_PLANE_ALPHA_VISIBLE + + update_sight() + +/mob/dead/observer/update_sight() + if (!ghostvision) + see_invisible = SEE_INVISIBLE_LIVING + else + see_invisible = SEE_INVISIBLE_OBSERVER + + updateghostimages() + . = ..() + +/mob/dead/observer/proc/updateghostsight() + if(!seedarkness) + see_invisible = SEE_INVISIBLE_OBSERVER_NOLIGHTING + else + see_invisible = SEE_INVISIBLE_OBSERVER + if(!ghostvision) + see_invisible = SEE_INVISIBLE_LIVING + + updateghostimages() + +/proc/updateallghostimages() + for(var/mob/dead/observer/O in GLOB.player_list) + O.updateghostimages() + +/mob/dead/observer/proc/updateghostimages() + if(!client) + return + if(seedarkness || !ghostvision) + client.images -= GLOB.ghost_images + else + //add images for the 60inv things ghosts can normally see when darkness is enabled so they can see them now + client.images |= GLOB.ghost_images + if(ghostimage) + client.images -= ghostimage //remove ourself + +/mob/proc/can_admin_interact() + return FALSE + +/mob/proc/can_advanced_admin_interact() + return FALSE + +/mob/dead/observer/can_admin_interact() + return check_rights(R_ADMIN, 0, src) + +/mob/dead/observer/can_advanced_admin_interact() + if(!can_admin_interact()) + return FALSE + + if(client && client.advanced_admin_interaction) + return TRUE + + return FALSE + +/mob/dead/observer/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE) + return TRUE + +//this is a mob verb instead of atom for performance reasons +//see /mob/verb/examinate() in mob.dm for more info +//overriden here and in /mob/living for different point span classes and sanity checks +/mob/dead/observer/pointed(atom/A as mob|obj|turf in view()) + if(!..()) + return 0 + usr.visible_message("[src] points to [A].") + return 1 + +/mob/dead/observer/proc/incarnate_ghost() + if(!client) + return + + var/mob/living/carbon/human/new_char = new(get_turf(src)) + client.prefs.copy_to(new_char) + if(mind) + mind.active = TRUE + mind.transfer_to(new_char) + else + new_char.key = key + + return new_char + +/mob/dead/observer/is_literate() + return TRUE + +/mob/dead/observer/proc/set_invisibility(value) + invisibility = value + if(!value) + set_light(1, 2) + else + set_light(0, 0) + +/mob/dead/observer/vv_edit_var(var_name, var_value) + . = ..() + if(var_name == "invisibility") + set_invisibility(invisibility) // updates light + +/proc/set_observer_default_invisibility(amount, message=null) + for(var/mob/dead/observer/G in GLOB.player_list) + G.set_invisibility(amount) + if(message) + to_chat(G, message) + GLOB.observer_default_invisibility = amount + +/mob/dead/observer/proc/open_spawners_menu() + set name = "Mob spawners menu" + set desc = "See all currently available ghost spawners" + set category = "Ghost" + + var/datum/spawners_menu/menu = new /datum/spawners_menu(src) + menu.ui_interact(src) diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm index 41c1d89f603b..2f5627e84131 100644 --- a/code/modules/mob/dead/observer/say.dm +++ b/code/modules/mob/dead/observer/say.dm @@ -1,48 +1,48 @@ -/mob/dead/observer/say(var/message) - message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) - - if(!message) - return - - log_ghostsay(message, src) - - if(src.client) - if(src.client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot talk in deadchat (muted).") - return - - if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT)) - return - - . = src.say_dead(message) - - -/mob/dead/observer/emote(act, type, message, force) - message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) - - if(!message) - return - - if(act != "me") - return - - log_ghostemote(message, src) - - if(src.client) - if(src.client.prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot emote in deadchat (muted).") - return - - if(src.client.handle_spam_prevention(message, MUTE_DEADCHAT)) - return - - . = src.emote_dead(message) - -/mob/dead/observer/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) - return "[speaker_name] ([ghost_follow_link(follow_target, ghost=src)])" - -/mob/dead/observer/handle_speaker_name(var/mob/speaker = null, var/vname, var/hard_to_hear) - var/speaker_name = ..() - if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. - speaker_name = "[speaker.real_name] ([speaker_name])" - return speaker_name \ No newline at end of file +/mob/dead/observer/say(var/message) + message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) + + if(!message) + return + + log_ghostsay(message, src) + + if(src.client) + if(src.client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot talk in deadchat (muted).") + return + + if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT)) + return + + . = src.say_dead(message) + + +/mob/dead/observer/emote(act, type, message, force) + message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN)) + + if(!message) + return + + if(act != "me") + return + + log_ghostemote(message, src) + + if(src.client) + if(src.client.prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot emote in deadchat (muted).") + return + + if(src.client.handle_spam_prevention(message, MUTE_DEADCHAT)) + return + + . = src.emote_dead(message) + +/mob/dead/observer/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) + return "[speaker_name] ([ghost_follow_link(follow_target, ghost=src)])" + +/mob/dead/observer/handle_speaker_name(var/mob/speaker = null, var/vname, var/hard_to_hear) + var/speaker_name = ..() + if(speaker && (speaker_name != speaker.real_name) && !isAI(speaker)) //Announce computer and various stuff that broadcasts doesn't use it's real name but AI's can't pretend to be other mobs. + speaker_name = "[speaker.real_name] ([speaker_name])" + return speaker_name diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index 57232d74f44b..5f18a27adc95 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -59,6 +59,7 @@ var/mob/living/L = src L.say_log += "EMOTE: [input]" //say log too so it is easier on admins instead of having to merge the two with timestamps etc L.emote_log += input //emote only log if an admin wants to search just for emotes they don't have to sift through the say + create_log(EMOTE_LOG, input) // TODO after #13047: Include the channel // Hearing gasp and such every five seconds is not good emotes were not global for a reason. // Maybe some people are okay with that. for(var/mob/M in GLOB.player_list) diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index a91aeed7bdff..cc497adc3e01 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -47,7 +47,7 @@ . = trim(. + trim(msg)) . += "\"" -/mob/proc/hear_say(var/list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) +/mob/proc/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) if(!client) return 0 @@ -104,7 +104,7 @@ to_chat(src, "[speaker_name][speaker.GetAltName()] [track][message]") if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z)) var/turf/source = speaker? get_turf(speaker) : get_turf(src) - src.playsound_local(source, speech_sound, sound_vol, 1) + playsound_local(source, speech_sound, sound_vol, 1, sound_frequency) /mob/proc/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target) @@ -179,4 +179,4 @@ name = speaker.voice_name var/rendered = "[name] [message]" - to_chat(src, rendered) \ No newline at end of file + to_chat(src, rendered) diff --git a/code/modules/mob/inventory.dm b/code/modules/mob/inventory.dm index 35a08d0ce10d..6d96d77c321b 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -1,265 +1,265 @@ -//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting l_hand = ...etc -//as they handle all relevant stuff like adding it to the player's screen and updating their overlays. - -//Returns the thing in our active hand -/mob/proc/get_active_hand() - if(hand) return l_hand - else return r_hand - -/mob/verb/quick_equip() - set name = "quick-equip" - set hidden = 1 - - var/obj/item/I = get_active_hand() - if(I) - I.equip_to_best_slot(src) - -/mob/proc/is_in_active_hand(obj/item/I) - var/obj/item/item_to_test = get_active_hand() - - return item_to_test && item_to_test.is_equivalent(I) - - -//Returns the thing in our inactive hand -/mob/proc/get_inactive_hand() - if(hand) return r_hand - else return l_hand - -/mob/proc/is_in_inactive_hand(obj/item/I) - var/obj/item/item_to_test = get_inactive_hand() - - return item_to_test && item_to_test.is_equivalent(I) - -//Returns if a certain item can be equipped to a certain slot. -// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead. -/mob/proc/can_equip(obj/item/I, slot, disable_warning = 0) - return 0 - -// Because there's several different places it's stored. -/mob/proc/get_multitool(var/if_active=0) - return null - -//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. -/mob/proc/put_in_l_hand(var/obj/item/W) - if(!put_in_hand_check(W)) - return 0 - if(!l_hand && has_left_hand()) - W.forceMove(src) //TODO: move to equipped? - l_hand = W - W.layer = ABOVE_HUD_LAYER //TODO: move to equipped? - W.plane = ABOVE_HUD_PLANE //TODO: move to equipped? - W.equipped(src,slot_l_hand) - if(pulling == W) - stop_pulling() - update_inv_l_hand() - return 1 - return 0 - -//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success. -/mob/proc/put_in_r_hand(var/obj/item/W) - if(!put_in_hand_check(W)) - return 0 - if(!r_hand && has_right_hand()) - W.forceMove(src) - r_hand = W - W.layer = ABOVE_HUD_LAYER - W.plane = ABOVE_HUD_PLANE - W.equipped(src,slot_r_hand) - if(pulling == W) - stop_pulling() - update_inv_r_hand() - return 1 - return 0 - -/mob/proc/put_in_hand_check(var/obj/item/W) - if(lying && !(W.flags & ABSTRACT)) return 0 - if(!istype(W)) return 0 - return 1 - -//Puts the item into our active hand if possible. returns 1 on success. -/mob/proc/put_in_active_hand(var/obj/item/W) - if(hand) return put_in_l_hand(W) - else return put_in_r_hand(W) - -//Puts the item into our inactive hand if possible. returns 1 on success. -/mob/proc/put_in_inactive_hand(var/obj/item/W) - if(hand) return put_in_r_hand(W) - else return put_in_l_hand(W) - -//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success. -//If both fail it drops it on the floor and returns 0. -//This is probably the main one you need to know :) -//Just puts stuff on the floor for most mobs, since all mobs have hands but putting stuff in the AI/corgi/ghost hand is VERY BAD. -/mob/proc/put_in_hands(obj/item/W) - W.forceMove(drop_location()) - W.layer = initial(W.layer) - W.plane = initial(W.plane) - W.dropped() - -/mob/proc/drop_item_v() //this is dumb. - if(stat == CONSCIOUS && isturf(loc)) - return drop_item() - return 0 - -//Drops the item in our left hand -/mob/proc/drop_l_hand() - return unEquip(l_hand) //All needed checks are in unEquip - -//Drops the item in our right hand -/mob/proc/drop_r_hand() - return unEquip(r_hand) //Why was this not calling unEquip in the first place jesus fuck. - -//Drops the item in our active hand. -/mob/proc/drop_item() //THIS. DOES. NOT. NEED. AN. ARGUMENT. - if(hand) - return drop_l_hand() - else - return drop_r_hand() - -//Here lie unEquip and before_item_take, already forgotten and not missed. - -/mob/proc/canUnEquip(obj/item/I, force) - if(!I) - return 1 - if((I.flags & NODROP) && !force) - return 0 - return 1 - -/mob/proc/unEquip(obj/item/I, force) //Force overrides NODROP for things like wizarditis and admin undress. - if(!I) //If there's nothing to drop, the drop is automatically succesfull. If(unEquip) should generally be used to check for NODROP. - return 1 - - if(!canUnEquip(I, force)) - return 0 - - if(I == r_hand) - r_hand = null - update_inv_r_hand() - else if(I == l_hand) - l_hand = null - update_inv_l_hand() - else if(I in tkgrabbed_objects) - var/obj/item/tk_grab/tkgrab = tkgrabbed_objects[I] - unEquip(tkgrab, force) - - if(I) - if(client) - client.screen -= I - I.forceMove(drop_location()) - I.dropped(src) - if(I) - I.layer = initial(I.layer) - I.plane = initial(I.plane) - return 1 - - -//Attemps to remove an object on a mob. Will not move it to another area or such, just removes from the mob. -/mob/proc/remove_from_mob(var/obj/O) - unEquip(O) - O.screen_loc = null - return 1 - - -//Outdated but still in use apparently. This should at least be a human proc. -//Daily reminder to murder this - Remie. -/mob/proc/get_equipped_items(include_pockets = FALSE) - var/list/items = list() - if(back) - items += back - if(wear_mask) - items += wear_mask - return items - -/mob/living/carbon/get_equipped_items(include_pockets = FALSE) - var/list/items = ..() - if(wear_suit) - items += wear_suit - if(head) - items += head - return items - -/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE) - var/list/items = ..() - if(belt) - items += belt - if(l_ear) - items += l_ear - if(r_ear) - items += r_ear - if(glasses) - items += glasses - if(gloves) - items += gloves - if(shoes) - items += shoes - if(wear_id) - items += wear_id - if(wear_pda) - items += wear_pda - if(w_uniform) - items += w_uniform - if(include_pockets) - if(l_store) - items += l_store - if(r_store) - items += r_store - if(s_store) - items += s_store - return items - -/obj/item/proc/equip_to_best_slot(mob/M) - if(src != M.get_active_hand()) - to_chat(M, "You are not holding anything to equip!") - return 0 - - if(M.equip_to_appropriate_slot(src)) - if(M.hand) - M.update_inv_l_hand(0) - else - M.update_inv_r_hand(0) - return 1 - - if(M.s_active && M.s_active.can_be_inserted(src, 1)) //if storage active insert there - M.s_active.handle_item_insertion(src) - return 1 - - var/obj/item/storage/S = M.get_inactive_hand() - if(istype(S) && S.can_be_inserted(src, 1)) //see if we have box in other hand - S.handle_item_insertion(src) - return 1 - - S = M.get_item_by_slot(slot_belt) - if(istype(S) && S.can_be_inserted(src, 1)) //else we put in belt - S.handle_item_insertion(src) - return 1 - - S = M.get_item_by_slot(slot_back) //else we put in backpack - if(istype(S) && S.can_be_inserted(src, 1)) - S.handle_item_insertion(src) - playsound(loc, "rustle", 50, 1, -5) - return 1 - - to_chat(M, "You are unable to equip that!") - return 0 - -/mob/proc/get_all_slots() - return list(wear_mask, back, l_hand, r_hand) - -/mob/proc/get_id_card() - for(var/obj/item/I in get_all_slots()) - . = I.GetID() - if(.) - break - -/mob/proc/get_item_by_slot(slot_id) - switch(slot_id) - if(slot_wear_mask) - return wear_mask - if(slot_back) - return back - if(slot_l_hand) - return l_hand - if(slot_r_hand) - return r_hand - return null - +//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting l_hand = ...etc +//as they handle all relevant stuff like adding it to the player's screen and updating their overlays. + +//Returns the thing in our active hand +/mob/proc/get_active_hand() + if(hand) return l_hand + else return r_hand + +/mob/verb/quick_equip() + set name = "quick-equip" + set hidden = 1 + + var/obj/item/I = get_active_hand() + if(I) + I.equip_to_best_slot(src) + +/mob/proc/is_in_active_hand(obj/item/I) + var/obj/item/item_to_test = get_active_hand() + + return item_to_test && item_to_test.is_equivalent(I) + + +//Returns the thing in our inactive hand +/mob/proc/get_inactive_hand() + if(hand) return r_hand + else return l_hand + +/mob/proc/is_in_inactive_hand(obj/item/I) + var/obj/item/item_to_test = get_inactive_hand() + + return item_to_test && item_to_test.is_equivalent(I) + +//Returns if a certain item can be equipped to a certain slot. +// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead. +/mob/proc/can_equip(obj/item/I, slot, disable_warning = 0) + return 0 + +// Because there's several different places it's stored. +/mob/proc/get_multitool(var/if_active=0) + return null + +//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success. +/mob/proc/put_in_l_hand(var/obj/item/W) + if(!put_in_hand_check(W)) + return 0 + if(!l_hand && has_left_hand()) + W.forceMove(src) //TODO: move to equipped? + l_hand = W + W.layer = ABOVE_HUD_LAYER //TODO: move to equipped? + W.plane = ABOVE_HUD_PLANE //TODO: move to equipped? + W.equipped(src,slot_l_hand) + if(pulling == W) + stop_pulling() + update_inv_l_hand() + return 1 + return 0 + +//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success. +/mob/proc/put_in_r_hand(var/obj/item/W) + if(!put_in_hand_check(W)) + return 0 + if(!r_hand && has_right_hand()) + W.forceMove(src) + r_hand = W + W.layer = ABOVE_HUD_LAYER + W.plane = ABOVE_HUD_PLANE + W.equipped(src,slot_r_hand) + if(pulling == W) + stop_pulling() + update_inv_r_hand() + return 1 + return 0 + +/mob/proc/put_in_hand_check(var/obj/item/W) + if(lying && !(W.flags & ABSTRACT)) return 0 + if(!istype(W)) return 0 + return 1 + +//Puts the item into our active hand if possible. returns 1 on success. +/mob/proc/put_in_active_hand(var/obj/item/W) + if(hand) return put_in_l_hand(W) + else return put_in_r_hand(W) + +//Puts the item into our inactive hand if possible. returns 1 on success. +/mob/proc/put_in_inactive_hand(var/obj/item/W) + if(hand) return put_in_r_hand(W) + else return put_in_l_hand(W) + +//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success. +//If both fail it drops it on the floor and returns 0. +//This is probably the main one you need to know :) +//Just puts stuff on the floor for most mobs, since all mobs have hands but putting stuff in the AI/corgi/ghost hand is VERY BAD. +/mob/proc/put_in_hands(obj/item/W) + W.forceMove(drop_location()) + W.layer = initial(W.layer) + W.plane = initial(W.plane) + W.dropped() + +/mob/proc/drop_item_v() //this is dumb. + if(stat == CONSCIOUS && isturf(loc)) + return drop_item() + return 0 + +//Drops the item in our left hand +/mob/proc/drop_l_hand() + return unEquip(l_hand) //All needed checks are in unEquip + +//Drops the item in our right hand +/mob/proc/drop_r_hand() + return unEquip(r_hand) //Why was this not calling unEquip in the first place jesus fuck. + +//Drops the item in our active hand. +/mob/proc/drop_item() //THIS. DOES. NOT. NEED. AN. ARGUMENT. + if(hand) + return drop_l_hand() + else + return drop_r_hand() + +//Here lie unEquip and before_item_take, already forgotten and not missed. + +/mob/proc/canUnEquip(obj/item/I, force) + if(!I) + return 1 + if((I.flags & NODROP) && !force) + return 0 + return 1 + +/mob/proc/unEquip(obj/item/I, force) //Force overrides NODROP for things like wizarditis and admin undress. + if(!I) //If there's nothing to drop, the drop is automatically succesfull. If(unEquip) should generally be used to check for NODROP. + return 1 + + if(!canUnEquip(I, force)) + return 0 + + if(I == r_hand) + r_hand = null + update_inv_r_hand() + else if(I == l_hand) + l_hand = null + update_inv_l_hand() + else if(I in tkgrabbed_objects) + var/obj/item/tk_grab/tkgrab = tkgrabbed_objects[I] + unEquip(tkgrab, force) + + if(I) + if(client) + client.screen -= I + I.forceMove(drop_location()) + I.dropped(src) + if(I) + I.layer = initial(I.layer) + I.plane = initial(I.plane) + return 1 + + +//Attemps to remove an object on a mob. Will not move it to another area or such, just removes from the mob. +/mob/proc/remove_from_mob(var/obj/O) + unEquip(O) + O.screen_loc = null + return 1 + + +//Outdated but still in use apparently. This should at least be a human proc. +//Daily reminder to murder this - Remie. +/mob/proc/get_equipped_items(include_pockets = FALSE) + var/list/items = list() + if(back) + items += back + if(wear_mask) + items += wear_mask + return items + +/mob/living/carbon/get_equipped_items(include_pockets = FALSE) + var/list/items = ..() + if(wear_suit) + items += wear_suit + if(head) + items += head + return items + +/mob/living/carbon/human/get_equipped_items(include_pockets = FALSE) + var/list/items = ..() + if(belt) + items += belt + if(l_ear) + items += l_ear + if(r_ear) + items += r_ear + if(glasses) + items += glasses + if(gloves) + items += gloves + if(shoes) + items += shoes + if(wear_id) + items += wear_id + if(wear_pda) + items += wear_pda + if(w_uniform) + items += w_uniform + if(include_pockets) + if(l_store) + items += l_store + if(r_store) + items += r_store + if(s_store) + items += s_store + return items + +/obj/item/proc/equip_to_best_slot(mob/M) + if(src != M.get_active_hand()) + to_chat(M, "You are not holding anything to equip!") + return 0 + + if(M.equip_to_appropriate_slot(src)) + if(M.hand) + M.update_inv_l_hand(0) + else + M.update_inv_r_hand(0) + return 1 + + if(M.s_active && M.s_active.can_be_inserted(src, 1)) //if storage active insert there + M.s_active.handle_item_insertion(src) + return 1 + + var/obj/item/storage/S = M.get_inactive_hand() + if(istype(S) && S.can_be_inserted(src, 1)) //see if we have box in other hand + S.handle_item_insertion(src) + return 1 + + S = M.get_item_by_slot(slot_belt) + if(istype(S) && S.can_be_inserted(src, 1)) //else we put in belt + S.handle_item_insertion(src) + return 1 + + S = M.get_item_by_slot(slot_back) //else we put in backpack + if(istype(S) && S.can_be_inserted(src, 1)) + S.handle_item_insertion(src) + playsound(loc, "rustle", 50, 1, -5) + return 1 + + to_chat(M, "You are unable to equip that!") + return 0 + +/mob/proc/get_all_slots() + return list(wear_mask, back, l_hand, r_hand) + +/mob/proc/get_id_card() + for(var/obj/item/I in get_all_slots()) + . = I.GetID() + if(.) + break + +/mob/proc/get_item_by_slot(slot_id) + switch(slot_id) + if(slot_wear_mask) + return wear_mask + if(slot_back) + return back + if(slot_l_hand) + return l_hand + if(slot_r_hand) + return r_hand + return null + diff --git a/code/modules/mob/living/carbon/_defines.dm b/code/modules/mob/living/carbon/_defines.dm index a5a524f86ed7..871b140a503d 100644 --- a/code/modules/mob/living/carbon/_defines.dm +++ b/code/modules/mob/living/carbon/_defines.dm @@ -16,4 +16,4 @@ #define COLD_GAS_DAMAGE_LEVEL_1 0.5 //Amount of damage applied when the current breath's temperature just passes the 260.15k safety point #define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point -#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point \ No newline at end of file +#define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point diff --git a/code/modules/mob/living/carbon/alien/alien.dm b/code/modules/mob/living/carbon/alien/alien.dm index 69a2e1160b0e..1b85c3e53864 100644 --- a/code/modules/mob/living/carbon/alien/alien.dm +++ b/code/modules/mob/living/carbon/alien/alien.dm @@ -1,308 +1,308 @@ -/mob/living/carbon/alien - name = "alien" - voice_name = "alien" - speak_emote = list("hisses") - icon = 'icons/mob/alien.dmi' - gender = NEUTER - dna = null - alien_talk_understand = TRUE - - var/nightvision = FALSE - see_in_dark = 4 - - var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie - var/has_fine_manipulation = FALSE - var/move_delay_add = FALSE // movement delay to add - - status_flags = CANPARALYSE|CANPUSH - var/heal_rate = 5 - - var/large = FALSE - var/heat_protection = 0.5 - var/leaping = FALSE - ventcrawler = 2 - var/list/alien_organs = list() - var/death_message = "lets out a waning guttural screech, green blood bubbling from its maw..." - var/death_sound = 'sound/voice/hiss6.ogg' - -/mob/living/carbon/alien/New() - verbs += /mob/living/verb/mob_sleep - verbs += /mob/living/verb/lay_down - alien_organs += new /obj/item/organ/internal/brain/xeno - alien_organs += new /obj/item/organ/internal/xenos/hivenode - alien_organs += new /obj/item/organ/internal/ears - for(var/obj/item/organ/internal/I in alien_organs) - I.insert(src) - ..() - -/mob/living/carbon/alien/get_default_language() - if(default_language) - return default_language - return GLOB.all_languages["Xenomorph"] - -/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) - var/verb = "hisses" - var/ending = copytext(message, length(message)) - - if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, - verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages - else - if(ending=="!") - verb = "roars" - else if(ending=="?") - verb = "hisses curiously" - return verb - - -/mob/living/carbon/alien/adjustToxLoss(amount) - return STATUS_UPDATE_NONE - -/mob/living/carbon/alien/adjustFireLoss(amount) // Weak to Fire - if(amount > 0) - return ..(amount * 2) - else - return ..(amount) - - -/mob/living/carbon/alien/check_eye_prot() - return 2 - -/mob/living/carbon/alien/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = maxHealth - stat = CONSCIOUS - return - health = maxHealth - getOxyLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - - update_stat("updatehealth([reason])") - med_hud_set_health() - med_hud_set_status() - handle_hud_icons_health() - -/mob/living/carbon/alien/handle_environment(var/datum/gas_mixture/environment) - - if(!environment) - return - - var/loc_temp = get_temperature(environment) - -// to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Fire protection: [heat_protection] - Location: [loc] - src: [src]") - - // Aliens are now weak to fire. - - //After then, it reacts to the surrounding atmosphere based on your thermal protection - if(!on_fire) // If you're on fire, ignore local air temperature - if(loc_temp > bodytemperature) - //Place is hotter than we are - var/thermal_protection = heat_protection //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - bodytemperature += (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - else - bodytemperature += 1 * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) - // bodytemperature -= max((loc_temp - bodytemperature / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) - - // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. - if(bodytemperature > 360.15) - //Body temperature is too hot. - throw_alert("alien_fire", /obj/screen/alert/alien_fire) - switch(bodytemperature) - if(360 to 400) - apply_damage(HEAT_DAMAGE_LEVEL_1, BURN) - if(400 to 460) - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) - if(460 to INFINITY) - if(on_fire) - apply_damage(HEAT_DAMAGE_LEVEL_3, BURN) - else - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) - else - clear_alert("alien_fire") - -/mob/living/carbon/alien/handle_fire()//Aliens on fire code - if(..()) - return - bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up! - return - -/mob/living/carbon/alien/IsAdvancedToolUser() - return has_fine_manipulation - -/mob/living/carbon/alien/Stat() - ..() - statpanel("Status") - stat(null, "Intent: [a_intent]") - stat(null, "Move Mode: [m_intent]") - show_stat_emergency_shuttle_eta() - -/mob/living/carbon/alien/SetStunned(amount, updating = 1, force = 0) - ..() - if(!(status_flags & CANSTUN) && amount) - // add some movement delay - move_delay_add = min(move_delay_add + round(amount / 2), 10) // a maximum delay of 10 - -/mob/living/carbon/alien/movement_delay() - . = ..() - . += move_delay_add + config.alien_delay //move_delay_add is used to slow aliens with stuns - -/mob/living/carbon/alien/getDNA() - return null - -/mob/living/carbon/alien/setDNA() - return - -/mob/living/carbon/alien/verb/nightvisiontoggle() - set name = "Toggle Night Vision" - set category = "Alien" - - if(!nightvision) - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - nightvision = TRUE - usr.hud_used.nightvisionicon.icon_state = "nightvision1" - else if(nightvision) - see_in_dark = initial(see_in_dark) - lighting_alpha = initial(lighting_alpha) - nightvision = FALSE - usr.hud_used.nightvisionicon.icon_state = "nightvision0" - - update_sight() - - -/mob/living/carbon/alien/assess_threat(var/mob/living/simple_animal/bot/secbot/judgebot, var/lasercolor) - if(judgebot.emagged == 2) - return 10 //Everyone is a criminal! - var/threatcount = 0 - - //Securitrons can't identify aliens - if(!lasercolor && judgebot.idcheck) - threatcount += 4 - - //Lasertag bullshit - if(lasercolor) - if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve - if((istype(r_hand,/obj/item/gun/energy/laser/tag/red)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/red))) - threatcount += 4 - - if(lasercolor == "r") - if((istype(r_hand,/obj/item/gun/energy/laser/tag/blue)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/blue))) - threatcount += 4 - - return threatcount - - //Check for weapons - if(judgebot.weaponscheck) - if(judgebot.check_for_weapons(l_hand)) - threatcount += 4 - if(judgebot.check_for_weapons(r_hand)) - threatcount += 4 - - //Mindshield implants imply trustworthyness - if(ismindshielded(src)) - threatcount -= 1 - - return threatcount - -/*---------------------------------------- -Proc: AddInfectionImages() -Des: Gives the client of the alien an image on each infected mob. -----------------------------------------*/ -/mob/living/carbon/alien/proc/AddInfectionImages() - if(client) - for(var/mob/living/C in GLOB.mob_list) - if(C.status_flags & XENO_HOST) - var/obj/item/organ/internal/body_egg/alien_embryo/A = C.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo) - if(A) - var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[A.stage]") - client.images += I - return - - -/*---------------------------------------- -Proc: RemoveInfectionImages() -Des: Removes all infected images from the alien. -----------------------------------------*/ -/mob/living/carbon/alien/proc/RemoveInfectionImages() - if(client) - for(var/image/I in client.images) - if(dd_hasprefix_case(I.icon_state, "infected")) - qdel(I) - return - -/mob/living/carbon/alien/canBeHandcuffed() - return 1 - -/mob/living/carbon/alien/proc/updatePlasmaDisplay() - if(hud_used) //clientless aliens - hud_used.alien_plasma_display.maptext = "
    [getPlasma()]
    " - -/mob/living/carbon/alien/larva/updatePlasmaDisplay() - return - -/mob/living/carbon/alien/can_use_vents() - return - -/mob/living/carbon/alien/handle_footstep(turf/T) - if(..()) - if(T.footstep_sounds["xeno"]) - var/S = pick(T.footstep_sounds["xeno"]) - if(S) - if(m_intent == MOVE_INTENT_RUN) - if(!(step_count % 2)) //every other turf makes a sound - return 0 - - var/range = -(world.view - 2) - range -= 0.666 //-(7 - 2) = (-5) = -5 | -5 - (0.666) = -5.666 | (7 + -5.666) = 1.334 | 1.334 * 3 = 4.002 | range(4.002) = range(4) - var/volume = 5 - - if(m_intent == MOVE_INTENT_WALK) - return 0 //silent when walking - - if(buckled || lying || throwing) - return 0 //people flying, lying down or sitting do not step - - if(!has_gravity(src)) - if(step_count % 3) //this basically says, every three moves make a noise - return 0 //1st - none, 1%3==1, 2nd - none, 2%3==2, 3rd - noise, 3%3==0 - - playsound(T, S, volume, 1, range) - return 1 - return 0 - -/mob/living/carbon/alien/getTrail() - if(getBruteLoss() < 200) - return pick("xltrails_1", "xltrails_2") - else - return pick("xttrails_1", "xttrails_2") - -/mob/living/carbon/alien/update_sight() - if(!client) - return - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - sight = SEE_MOBS - if(nightvision) - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - else - see_in_dark = initial(see_in_dark) - lighting_alpha = initial(lighting_alpha) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - for(var/obj/item/organ/internal/cyberimp/eyes/E in internal_organs) - sight |= E.vision_flags - if(E.see_in_dark) - see_in_dark = max(see_in_dark, E.see_in_dark) - if(E.see_invisible) - see_invisible = min(see_invisible, E.see_invisible) - if(!isnull(E.lighting_alpha)) - lighting_alpha = min(lighting_alpha, E.lighting_alpha) - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() \ No newline at end of file +/mob/living/carbon/alien + name = "alien" + voice_name = "alien" + speak_emote = list("hisses") + icon = 'icons/mob/alien.dmi' + gender = NEUTER + dna = null + alien_talk_understand = TRUE + + var/nightvision = FALSE + see_in_dark = 4 + + var/obj/item/card/id/wear_id = null // Fix for station bounced radios -- Skie + var/has_fine_manipulation = FALSE + var/move_delay_add = FALSE // movement delay to add + + status_flags = CANPARALYSE|CANPUSH + var/heal_rate = 5 + + var/large = FALSE + var/heat_protection = 0.5 + var/leaping = FALSE + ventcrawler = 2 + var/list/alien_organs = list() + var/death_message = "lets out a waning guttural screech, green blood bubbling from its maw..." + var/death_sound = 'sound/voice/hiss6.ogg' + +/mob/living/carbon/alien/New() + verbs += /mob/living/verb/mob_sleep + verbs += /mob/living/verb/lay_down + alien_organs += new /obj/item/organ/internal/brain/xeno + alien_organs += new /obj/item/organ/internal/xenos/hivenode + alien_organs += new /obj/item/organ/internal/ears + for(var/obj/item/organ/internal/I in alien_organs) + I.insert(src) + ..() + +/mob/living/carbon/alien/get_default_language() + if(default_language) + return default_language + return GLOB.all_languages["Xenomorph"] + +/mob/living/carbon/alien/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "hisses" + var/ending = copytext(message, length(message)) + + if(speaking && (speaking.name != "Galactic Common")) //this is so adminbooze xenos speaking common have their custom verbs, + verb = speaking.get_spoken_verb(ending) //and use normal verbs for their own languages and non-common languages + else + if(ending=="!") + verb = "roars" + else if(ending=="?") + verb = "hisses curiously" + return verb + + +/mob/living/carbon/alien/adjustToxLoss(amount) + return STATUS_UPDATE_NONE + +/mob/living/carbon/alien/adjustFireLoss(amount) // Weak to Fire + if(amount > 0) + return ..(amount * 2) + else + return ..(amount) + + +/mob/living/carbon/alien/check_eye_prot() + return 2 + +/mob/living/carbon/alien/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = maxHealth + stat = CONSCIOUS + return + health = maxHealth - getOxyLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() + + update_stat("updatehealth([reason])") + med_hud_set_health() + med_hud_set_status() + handle_hud_icons_health() + +/mob/living/carbon/alien/handle_environment(var/datum/gas_mixture/environment) + + if(!environment) + return + + var/loc_temp = get_temperature(environment) + +// to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Fire protection: [heat_protection] - Location: [loc] - src: [src]") + + // Aliens are now weak to fire. + + //After then, it reacts to the surrounding atmosphere based on your thermal protection + if(!on_fire) // If you're on fire, ignore local air temperature + if(loc_temp > bodytemperature) + //Place is hotter than we are + var/thermal_protection = heat_protection //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + bodytemperature += (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) + else + bodytemperature += 1 * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR) + // bodytemperature -= max((loc_temp - bodytemperature / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + + // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. + if(bodytemperature > 360.15) + //Body temperature is too hot. + throw_alert("alien_fire", /obj/screen/alert/alien_fire) + switch(bodytemperature) + if(360 to 400) + apply_damage(HEAT_DAMAGE_LEVEL_1, BURN) + if(400 to 460) + apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) + if(460 to INFINITY) + if(on_fire) + apply_damage(HEAT_DAMAGE_LEVEL_3, BURN) + else + apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) + else + clear_alert("alien_fire") + +/mob/living/carbon/alien/handle_fire()//Aliens on fire code + if(..()) + return + bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up! + return + +/mob/living/carbon/alien/IsAdvancedToolUser() + return has_fine_manipulation + +/mob/living/carbon/alien/Stat() + ..() + statpanel("Status") + stat(null, "Intent: [a_intent]") + stat(null, "Move Mode: [m_intent]") + show_stat_emergency_shuttle_eta() + +/mob/living/carbon/alien/SetStunned(amount, updating = 1, force = 0) + ..() + if(!(status_flags & CANSTUN) && amount) + // add some movement delay + move_delay_add = min(move_delay_add + round(amount / 2), 10) // a maximum delay of 10 + +/mob/living/carbon/alien/movement_delay() + . = ..() + . += move_delay_add + config.alien_delay //move_delay_add is used to slow aliens with stuns + +/mob/living/carbon/alien/getDNA() + return null + +/mob/living/carbon/alien/setDNA() + return + +/mob/living/carbon/alien/verb/nightvisiontoggle() + set name = "Toggle Night Vision" + set category = "Alien" + + if(!nightvision) + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + nightvision = TRUE + usr.hud_used.nightvisionicon.icon_state = "nightvision1" + else if(nightvision) + see_in_dark = initial(see_in_dark) + lighting_alpha = initial(lighting_alpha) + nightvision = FALSE + usr.hud_used.nightvisionicon.icon_state = "nightvision0" + + update_sight() + + +/mob/living/carbon/alien/assess_threat(var/mob/living/simple_animal/bot/secbot/judgebot, var/lasercolor) + if(judgebot.emagged == 2) + return 10 //Everyone is a criminal! + var/threatcount = 0 + + //Securitrons can't identify aliens + if(!lasercolor && judgebot.idcheck) + threatcount += 4 + + //Lasertag bullshit + if(lasercolor) + if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve + if((istype(r_hand,/obj/item/gun/energy/laser/tag/red)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/red))) + threatcount += 4 + + if(lasercolor == "r") + if((istype(r_hand,/obj/item/gun/energy/laser/tag/blue)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/blue))) + threatcount += 4 + + return threatcount + + //Check for weapons + if(judgebot.weaponscheck) + if(judgebot.check_for_weapons(l_hand)) + threatcount += 4 + if(judgebot.check_for_weapons(r_hand)) + threatcount += 4 + + //Mindshield implants imply trustworthyness + if(ismindshielded(src)) + threatcount -= 1 + + return threatcount + +/*---------------------------------------- +Proc: AddInfectionImages() +Des: Gives the client of the alien an image on each infected mob. +----------------------------------------*/ +/mob/living/carbon/alien/proc/AddInfectionImages() + if(client) + for(var/mob/living/C in GLOB.mob_list) + if(C.status_flags & XENO_HOST) + var/obj/item/organ/internal/body_egg/alien_embryo/A = C.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo) + if(A) + var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[A.stage]") + client.images += I + return + + +/*---------------------------------------- +Proc: RemoveInfectionImages() +Des: Removes all infected images from the alien. +----------------------------------------*/ +/mob/living/carbon/alien/proc/RemoveInfectionImages() + if(client) + for(var/image/I in client.images) + if(dd_hasprefix_case(I.icon_state, "infected")) + qdel(I) + return + +/mob/living/carbon/alien/canBeHandcuffed() + return 1 + +/mob/living/carbon/alien/proc/updatePlasmaDisplay() + if(hud_used) //clientless aliens + hud_used.alien_plasma_display.maptext = "
    [getPlasma()]
    " + +/mob/living/carbon/alien/larva/updatePlasmaDisplay() + return + +/mob/living/carbon/alien/can_use_vents() + return + +/mob/living/carbon/alien/handle_footstep(turf/T) + if(..()) + if(T.footstep_sounds["xeno"]) + var/S = pick(T.footstep_sounds["xeno"]) + if(S) + if(m_intent == MOVE_INTENT_RUN) + if(!(step_count % 2)) //every other turf makes a sound + return 0 + + var/range = -(world.view - 2) + range -= 0.666 //-(7 - 2) = (-5) = -5 | -5 - (0.666) = -5.666 | (7 + -5.666) = 1.334 | 1.334 * 3 = 4.002 | range(4.002) = range(4) + var/volume = 5 + + if(m_intent == MOVE_INTENT_WALK) + return 0 //silent when walking + + if(buckled || lying || throwing) + return 0 //people flying, lying down or sitting do not step + + if(!has_gravity(src)) + if(step_count % 3) //this basically says, every three moves make a noise + return 0 //1st - none, 1%3==1, 2nd - none, 2%3==2, 3rd - noise, 3%3==0 + + playsound(T, S, volume, 1, range) + return 1 + return 0 + +/mob/living/carbon/alien/getTrail() + if(getBruteLoss() < 200) + return pick("xltrails_1", "xltrails_2") + else + return pick("xttrails_1", "xttrails_2") + +/mob/living/carbon/alien/update_sight() + if(!client) + return + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + sight = SEE_MOBS + if(nightvision) + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + else + see_in_dark = initial(see_in_dark) + lighting_alpha = initial(lighting_alpha) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + for(var/obj/item/organ/internal/cyberimp/eyes/E in internal_organs) + sight |= E.vision_flags + if(E.see_in_dark) + see_in_dark = max(see_in_dark, E.see_in_dark) + if(E.see_invisible) + see_invisible = min(see_invisible, E.see_invisible) + if(!isnull(E.lighting_alpha)) + lighting_alpha = min(lighting_alpha, E.lighting_alpha) + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm index 04924d4b3107..c88a592a01bd 100644 --- a/code/modules/mob/living/carbon/alien/alien_defense.dm +++ b/code/modules/mob/living/carbon/alien/alien_defense.dm @@ -80,4 +80,4 @@ In all, this is a lot like the monkey code. /N damage = rand(10, 40) adjustBruteLoss(damage) add_attack_logs(M, src, "Slime'd for [damage] damage") - updatehealth("slime attack") \ No newline at end of file + updatehealth("slime attack") diff --git a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm index 9ec48c51bb17..1ea256357e9c 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/alien_powers.dm @@ -1,170 +1,170 @@ -/*NOTES: -These are general powers. Specific powers are stored under the appropriate alien creature type. -*/ - -/*Alien spit now works like a taser shot. It won't home in on the target but will act the same once it does hit. -Doesn't work on other aliens/AI.*/ - - -/mob/living/carbon/proc/powerc(X, Y)//Y is optional, checks for weed planting. X can be null. - if(stat) - to_chat(src, "You must be conscious to do this.") - return 0 - else if(X && getPlasma() < X) - to_chat(src, "Not enough plasma stored.") - return 0 - else if(Y && (!isturf(src.loc) || istype(src.loc, /turf/space))) - to_chat(src, "You can't place that here!") - return 0 - else return 1 - -/mob/living/carbon/alien/humanoid/verb/plant() - set name = "Plant Weeds (50)" - set desc = "Plants some alien weeds" - set category = "Alien" - - if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) - to_chat(src, "There's already a weed node here.") - return - - if(powerc(50,1)) - adjustPlasma(-50) - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] has planted some alien weeds!"), 1) - new /obj/structure/alien/weeds/node(loc) - return - -/mob/living/carbon/alien/humanoid/verb/whisp(mob/M as mob in oview()) - set name = "Whisper (10)" - set desc = "Whisper to someone" - set category = "Alien" - - if(powerc(10)) - adjustPlasma(-10) - var/msg = sanitize(input("Message:", "Alien Whisper") as text|null) - if(msg) - log_say("(AWHISPER to [key_name(M)]) [msg]", src) - to_chat(M, "You hear a strange, alien voice in your head...[msg]") - to_chat(src, "You said: [msg] to [M]") - for(var/mob/dead/observer/G in GLOB.player_list) - G.show_message("Alien message from [src] ([ghost_follow_link(src, ghost=G)]) to [M] ([ghost_follow_link(M, ghost=G)]): [msg]") - return - -/mob/living/carbon/alien/humanoid/verb/transfer_plasma(mob/living/carbon/alien/M as mob in oview()) - set name = "Transfer Plasma" - set desc = "Transfer Plasma to another alien" - set category = "Alien" - - if(isalien(M)) - var/amount = input("Amount:", "Transfer Plasma to [M]") as num - if(amount) - amount = abs(round(amount)) - if(powerc(amount)) - if(get_dist(src,M) <= 1) - M.adjustPlasma(amount) - adjustPlasma(-amount) - to_chat(M, "[src] has transfered [amount] plasma to you.") - to_chat(src, {"You have trasferred [amount] plasma to [M]"}) - else - to_chat(src, "You need to be closer.") - return - - -/mob/living/carbon/alien/humanoid/proc/corrosive_acid(atom/target) //If they right click to corrode, an error will flash if its an invalid target./N - set name = "Corrossive Acid (200)" - set desc = "Drench an object in acid, destroying it over time." - set category = "Alien" - - if(powerc(200)) - if(target in oview(1)) - if(target.acid_act(200, 100)) - visible_message("[src] vomits globs of vile stuff all over [target]. It begins to sizzle and melt under the bubbling mess of acid!") - adjustPlasma(-200) - else - to_chat(src, "You cannot dissolve this object.") - else - to_chat(src, "[target] is too far away.") - -/mob/living/carbon/alien/humanoid/proc/neurotoxin() // ok - set name = "Spit Neurotoxin (50)" - set desc = "Spits neurotoxin at someone, paralyzing them for a short time." - set category = "Alien" - - if(powerc(50)) - adjustPlasma(-50) - src.visible_message("[src] spits neurotoxin!", "You spit neurotoxin.") - - var/turf/T = loc - var/turf/U = get_step(src, dir) // Get the tile infront of the move, based on their direction - if(!isturf(U) || !isturf(T)) - return - - var/obj/item/projectile/bullet/neurotoxin/A = new /obj/item/projectile/bullet/neurotoxin(usr.loc) - A.current = U - A.firer = src - A.yo = U.y - T.y - A.xo = U.x - T.x - A.fire() - A.newtonian_move(get_dir(U, T)) - newtonian_move(get_dir(U, T)) - return - -/mob/living/carbon/alien/humanoid/proc/resin() // -- TLE - set name = "Secrete Resin (55)" - set desc = "Secrete tough malleable resin." - set category = "Alien" - - if(powerc(55)) - var/choice = input("Choose what you wish to shape.","Resin building") as null|anything in list("resin wall","resin membrane","resin nest") //would do it through typesof but then the player choice would have the type path and we don't want the internal workings to be exposed ICly - Urist - - if(!choice || !powerc(55)) return - adjustPlasma(-55) - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] vomits up a thick purple substance and shapes it!"), 1) - switch(choice) - if("resin wall") - new /obj/structure/alien/resin/wall(loc) - if("resin membrane") - new /obj/structure/alien/resin/membrane(loc) - if("resin nest") - new /obj/structure/bed/nest(loc) - return - -/mob/living/carbon/alien/humanoid/verb/regurgitate() - set name = "Regurgitate" - set desc = "Empties the contents of your stomach" - set category = "Alien" - - if(powerc()) - if(stomach_contents.len) - for(var/mob/M in src) - if(M in stomach_contents) - stomach_contents.Remove(M) - M.forceMove(loc) - //Paralyse(10) - src.visible_message("[src] hurls out the contents of [p_their()] stomach!") - return - -/mob/living/carbon/proc/getPlasma() - var/obj/item/organ/internal/xenos/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/xenos/plasmavessel) - if(!vessel) return 0 - return vessel.stored_plasma - - -/mob/living/carbon/proc/adjustPlasma(amount) - var/obj/item/organ/internal/xenos/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/xenos/plasmavessel) - if(!vessel) return - vessel.stored_plasma = max(vessel.stored_plasma + amount,0) - vessel.stored_plasma = min(vessel.stored_plasma, vessel.max_plasma) //upper limit of max_plasma, lower limit of 0 - return 1 - -/mob/living/carbon/alien/adjustPlasma(amount) - . = ..() - updatePlasmaDisplay() - -/mob/living/carbon/proc/usePlasma(amount) - if(getPlasma() >= amount) - adjustPlasma(-amount) - return 1 - - return 0 +/*NOTES: +These are general powers. Specific powers are stored under the appropriate alien creature type. +*/ + +/*Alien spit now works like a taser shot. It won't home in on the target but will act the same once it does hit. +Doesn't work on other aliens/AI.*/ + + +/mob/living/carbon/proc/powerc(X, Y)//Y is optional, checks for weed planting. X can be null. + if(stat) + to_chat(src, "You must be conscious to do this.") + return 0 + else if(X && getPlasma() < X) + to_chat(src, "Not enough plasma stored.") + return 0 + else if(Y && (!isturf(src.loc) || istype(src.loc, /turf/space))) + to_chat(src, "You can't place that here!") + return 0 + else return 1 + +/mob/living/carbon/alien/humanoid/verb/plant() + set name = "Plant Weeds (50)" + set desc = "Plants some alien weeds" + set category = "Alien" + + if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) + to_chat(src, "There's already a weed node here.") + return + + if(powerc(50,1)) + adjustPlasma(-50) + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] has planted some alien weeds!"), 1) + new /obj/structure/alien/weeds/node(loc) + return + +/mob/living/carbon/alien/humanoid/verb/whisp(mob/M as mob in oview()) + set name = "Whisper (10)" + set desc = "Whisper to someone" + set category = "Alien" + + if(powerc(10)) + adjustPlasma(-10) + var/msg = sanitize(input("Message:", "Alien Whisper") as text|null) + if(msg) + log_say("(AWHISPER to [key_name(M)]) [msg]", src) + to_chat(M, "You hear a strange, alien voice in your head...[msg]") + to_chat(src, "You said: [msg] to [M]") + for(var/mob/dead/observer/G in GLOB.player_list) + G.show_message("Alien message from [src] ([ghost_follow_link(src, ghost=G)]) to [M] ([ghost_follow_link(M, ghost=G)]): [msg]") + return + +/mob/living/carbon/alien/humanoid/verb/transfer_plasma(mob/living/carbon/alien/M as mob in oview()) + set name = "Transfer Plasma" + set desc = "Transfer Plasma to another alien" + set category = "Alien" + + if(isalien(M)) + var/amount = input("Amount:", "Transfer Plasma to [M]") as num + if(amount) + amount = abs(round(amount)) + if(powerc(amount)) + if(get_dist(src,M) <= 1) + M.adjustPlasma(amount) + adjustPlasma(-amount) + to_chat(M, "[src] has transfered [amount] plasma to you.") + to_chat(src, {"You have trasferred [amount] plasma to [M]"}) + else + to_chat(src, "You need to be closer.") + return + + +/mob/living/carbon/alien/humanoid/proc/corrosive_acid(atom/target) //If they right click to corrode, an error will flash if its an invalid target./N + set name = "Corrossive Acid (200)" + set desc = "Drench an object in acid, destroying it over time." + set category = "Alien" + + if(powerc(200)) + if(target in oview(1)) + if(target.acid_act(200, 100)) + visible_message("[src] vomits globs of vile stuff all over [target]. It begins to sizzle and melt under the bubbling mess of acid!") + adjustPlasma(-200) + else + to_chat(src, "You cannot dissolve this object.") + else + to_chat(src, "[target] is too far away.") + +/mob/living/carbon/alien/humanoid/proc/neurotoxin() // ok + set name = "Spit Neurotoxin (50)" + set desc = "Spits neurotoxin at someone, paralyzing them for a short time." + set category = "Alien" + + if(powerc(50)) + adjustPlasma(-50) + src.visible_message("[src] spits neurotoxin!", "You spit neurotoxin.") + + var/turf/T = loc + var/turf/U = get_step(src, dir) // Get the tile infront of the move, based on their direction + if(!isturf(U) || !isturf(T)) + return + + var/obj/item/projectile/bullet/neurotoxin/A = new /obj/item/projectile/bullet/neurotoxin(usr.loc) + A.current = U + A.firer = src + A.yo = U.y - T.y + A.xo = U.x - T.x + A.fire() + A.newtonian_move(get_dir(U, T)) + newtonian_move(get_dir(U, T)) + return + +/mob/living/carbon/alien/humanoid/proc/resin() // -- TLE + set name = "Secrete Resin (55)" + set desc = "Secrete tough malleable resin." + set category = "Alien" + + if(powerc(55)) + var/choice = input("Choose what you wish to shape.","Resin building") as null|anything in list("resin wall","resin membrane","resin nest") //would do it through typesof but then the player choice would have the type path and we don't want the internal workings to be exposed ICly - Urist + + if(!choice || !powerc(55)) return + adjustPlasma(-55) + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] vomits up a thick purple substance and shapes it!"), 1) + switch(choice) + if("resin wall") + new /obj/structure/alien/resin/wall(loc) + if("resin membrane") + new /obj/structure/alien/resin/membrane(loc) + if("resin nest") + new /obj/structure/bed/nest(loc) + return + +/mob/living/carbon/alien/humanoid/verb/regurgitate() + set name = "Regurgitate" + set desc = "Empties the contents of your stomach" + set category = "Alien" + + if(powerc()) + if(stomach_contents.len) + for(var/mob/M in src) + if(M in stomach_contents) + stomach_contents.Remove(M) + M.forceMove(loc) + //Paralyse(10) + src.visible_message("[src] hurls out the contents of [p_their()] stomach!") + return + +/mob/living/carbon/proc/getPlasma() + var/obj/item/organ/internal/xenos/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/xenos/plasmavessel) + if(!vessel) return 0 + return vessel.stored_plasma + + +/mob/living/carbon/proc/adjustPlasma(amount) + var/obj/item/organ/internal/xenos/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/xenos/plasmavessel) + if(!vessel) return + vessel.stored_plasma = max(vessel.stored_plasma + amount,0) + vessel.stored_plasma = min(vessel.stored_plasma, vessel.max_plasma) //upper limit of max_plasma, lower limit of 0 + return 1 + +/mob/living/carbon/alien/adjustPlasma(amount) + . = ..() + updatePlasmaDisplay() + +/mob/living/carbon/proc/usePlasma(amount) + if(getPlasma() >= amount) + adjustPlasma(-amount) + return 1 + + return 0 diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm index 8e261f56d8d4..2d894ee357eb 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/drone.dm @@ -1,48 +1,48 @@ -/mob/living/carbon/alien/humanoid/drone - name = "alien drone" - caste = "d" - maxHealth = 100 - health = 100 - icon_state = "aliend_s" - -/mob/living/carbon/alien/humanoid/drone/New() - create_reagents(100) - if(src.name == "alien drone") - src.name = text("alien drone ([rand(1, 1000)])") - src.real_name = src.name - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/drone - alien_organs += new /obj/item/organ/internal/xenos/acidgland - alien_organs += new /obj/item/organ/internal/xenos/resinspinner - ..() - -//Drones use the same base as generic humanoids. -//Drone verbs - -/mob/living/carbon/alien/humanoid/drone/verb/evolve() // -- TLE - set name = "Evolve (500)" - set desc = "Produce an interal egg sac capable of spawning children. Only one queen can exist at a time." - set category = "Alien" - - if(powerc(500)) - // Queen check - var/no_queen = 1 - for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) - if(!Q.key && Q.get_int_organ(/obj/item/organ/internal/brain/)) - continue - no_queen = 0 - - if(src.has_brain_worms()) - to_chat(src, "We cannot perform this ability at the present time!") - return - if(no_queen) - adjustPlasma(-500) - to_chat(src, "You begin to evolve!") - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] begins to twist and contort!"), 1) - var/mob/living/carbon/alien/humanoid/queen/new_xeno = new(loc) - mind.transfer_to(new_xeno) - new_xeno.mind.name = new_xeno.name - qdel(src) - else - to_chat(src, "We already have an alive queen.") - return \ No newline at end of file +/mob/living/carbon/alien/humanoid/drone + name = "alien drone" + caste = "d" + maxHealth = 100 + health = 100 + icon_state = "aliend_s" + +/mob/living/carbon/alien/humanoid/drone/New() + create_reagents(100) + if(src.name == "alien drone") + src.name = text("alien drone ([rand(1, 1000)])") + src.real_name = src.name + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/drone + alien_organs += new /obj/item/organ/internal/xenos/acidgland + alien_organs += new /obj/item/organ/internal/xenos/resinspinner + ..() + +//Drones use the same base as generic humanoids. +//Drone verbs + +/mob/living/carbon/alien/humanoid/drone/verb/evolve() // -- TLE + set name = "Evolve (500)" + set desc = "Produce an interal egg sac capable of spawning children. Only one queen can exist at a time." + set category = "Alien" + + if(powerc(500)) + // Queen check + var/no_queen = 1 + for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) + if(!Q.key && Q.get_int_organ(/obj/item/organ/internal/brain/)) + continue + no_queen = 0 + + if(src.has_brain_worms()) + to_chat(src, "We cannot perform this ability at the present time!") + return + if(no_queen) + adjustPlasma(-500) + to_chat(src, "You begin to evolve!") + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] begins to twist and contort!"), 1) + var/mob/living/carbon/alien/humanoid/queen/new_xeno = new(loc) + mind.transfer_to(new_xeno) + new_xeno.mind.name = new_xeno.name + qdel(src) + else + to_chat(src, "We already have an alive queen.") + return diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm index f950eee4dbf7..4cba49a2a035 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/hunter.dm @@ -1,131 +1,131 @@ -/mob/living/carbon/alien/humanoid/hunter - name = "alien hunter" - caste = "h" - maxHealth = 125 - health = 125 - icon_state = "alienh_s" - -/mob/living/carbon/alien/humanoid/hunter/New() - create_reagents(100) - if(name == "alien hunter") - name = text("alien hunter ([rand(1, 1000)])") - real_name = name - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/hunter - ..() - -/mob/living/carbon/alien/humanoid/hunter/movement_delay() - . = -1 //hunters are sanic - . += ..() //but they still need to slow down on stun - -/mob/living/carbon/alien/humanoid/hunter/handle_hud_icons_health() - ..() //-Yvarov - - if(healths) - if(stat != 2) - switch(health) - if(125 to INFINITY) - healths.icon_state = "health0" - if(100 to 125) - healths.icon_state = "health1" - if(50 to 100) - healths.icon_state = "health2" - if(25 to 50) - healths.icon_state = "health3" - if(0 to 25) - healths.icon_state = "health4" - else - healths.icon_state = "health5" - else - healths.icon_state = "health6" - - -/mob/living/carbon/alien/humanoid/hunter/handle_environment() - if(m_intent == MOVE_INTENT_RUN || resting) - ..() - else - adjustPlasma(-heal_rate) - - -//Hunter verbs - -/mob/living/carbon/alien/humanoid/hunter/proc/toggle_leap(var/message = 1) - leap_on_click = !leap_on_click - leap_icon.icon_state = "leap_[leap_on_click ? "on":"off"]" - if(message) - to_chat(src, "You will now [leap_on_click ? "leap at":"slash at"] enemies!") - else - return - -/mob/living/carbon/alien/humanoid/hunter/ClickOn(var/atom/A, var/params) - face_atom(A) - if(leap_on_click) - leap_at(A) - else - ..() - -#define MAX_ALIEN_LEAP_DIST 7 - -/mob/living/carbon/alien/humanoid/hunter/proc/leap_at(var/atom/A) - if(pounce_cooldown > world.time) - to_chat(src, "You are too fatigued to pounce right now!") - return - - if(leaping) //Leap while you leap, so you can leap while you leap - return - - if(!has_gravity(src) || !has_gravity(A)) - to_chat(src, "It is unsafe to leap without gravity!") - //It's also extremely buggy visually, so it's balance+bugfix - return - if(lying) - return - - else //Maybe uses plasma in the future, although that wouldn't make any sense... - leaping = 1 - update_icons() - throw_at(A, MAX_ALIEN_LEAP_DIST, 1, spin = 0, diagonals_first = 1, callback = CALLBACK(src, .leap_end)) - -/mob/living/carbon/alien/humanoid/hunter/proc/leap_end() - leaping = 0 - update_icons() - -/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A) - if(!leaping) - return ..() - - if(A) - if(isliving(A)) - var/mob/living/L = A - var/blocked = 0 - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK)) - blocked = 1 - if(!blocked) - L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") - if(ishuman(L)) - var/mob/living/carbon/human/H = L - H.apply_effect(5, WEAKEN, H.run_armor_check(null, "melee")) - else - L.Weaken(5) - sleep(2)//Runtime prevention (infinite bump() calls on hulks) - step_towards(src,L) - else - Weaken(2, 1, 1) - - toggle_leap(0) - pounce_cooldown = world.time + pounce_cooldown_time - else if(A.density && !A.CanPass(src)) - visible_message("[src] smashes into [A]!", "[src] smashes into [A]!") - Weaken(2, 1, 1) - - if(leaping) - leaping = 0 - update_icons() - update_canmove() - - -/mob/living/carbon/alien/humanoid/float(on) - if(leaping) - return - ..() +/mob/living/carbon/alien/humanoid/hunter + name = "alien hunter" + caste = "h" + maxHealth = 125 + health = 125 + icon_state = "alienh_s" + +/mob/living/carbon/alien/humanoid/hunter/New() + create_reagents(100) + if(name == "alien hunter") + name = text("alien hunter ([rand(1, 1000)])") + real_name = name + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/hunter + ..() + +/mob/living/carbon/alien/humanoid/hunter/movement_delay() + . = -1 //hunters are sanic + . += ..() //but they still need to slow down on stun + +/mob/living/carbon/alien/humanoid/hunter/handle_hud_icons_health() + ..() //-Yvarov + + if(healths) + if(stat != 2) + switch(health) + if(125 to INFINITY) + healths.icon_state = "health0" + if(100 to 125) + healths.icon_state = "health1" + if(50 to 100) + healths.icon_state = "health2" + if(25 to 50) + healths.icon_state = "health3" + if(0 to 25) + healths.icon_state = "health4" + else + healths.icon_state = "health5" + else + healths.icon_state = "health6" + + +/mob/living/carbon/alien/humanoid/hunter/handle_environment() + if(m_intent == MOVE_INTENT_RUN || resting) + ..() + else + adjustPlasma(-heal_rate) + + +//Hunter verbs + +/mob/living/carbon/alien/humanoid/hunter/proc/toggle_leap(var/message = 1) + leap_on_click = !leap_on_click + leap_icon.icon_state = "leap_[leap_on_click ? "on":"off"]" + if(message) + to_chat(src, "You will now [leap_on_click ? "leap at":"slash at"] enemies!") + else + return + +/mob/living/carbon/alien/humanoid/hunter/ClickOn(var/atom/A, var/params) + face_atom(A) + if(leap_on_click) + leap_at(A) + else + ..() + +#define MAX_ALIEN_LEAP_DIST 7 + +/mob/living/carbon/alien/humanoid/hunter/proc/leap_at(var/atom/A) + if(pounce_cooldown > world.time) + to_chat(src, "You are too fatigued to pounce right now!") + return + + if(leaping) //Leap while you leap, so you can leap while you leap + return + + if(!has_gravity(src) || !has_gravity(A)) + to_chat(src, "It is unsafe to leap without gravity!") + //It's also extremely buggy visually, so it's balance+bugfix + return + if(lying) + return + + else //Maybe uses plasma in the future, although that wouldn't make any sense... + leaping = 1 + update_icons() + throw_at(A, MAX_ALIEN_LEAP_DIST, 1, spin = 0, diagonals_first = 1, callback = CALLBACK(src, .leap_end)) + +/mob/living/carbon/alien/humanoid/hunter/proc/leap_end() + leaping = 0 + update_icons() + +/mob/living/carbon/alien/humanoid/hunter/throw_impact(atom/A) + if(!leaping) + return ..() + + if(A) + if(isliving(A)) + var/mob/living/L = A + var/blocked = 0 + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK)) + blocked = 1 + if(!blocked) + L.visible_message("[src] pounces on [L]!", "[src] pounces on you!") + if(ishuman(L)) + var/mob/living/carbon/human/H = L + H.apply_effect(5, WEAKEN, H.run_armor_check(null, "melee")) + else + L.Weaken(5) + sleep(2)//Runtime prevention (infinite bump() calls on hulks) + step_towards(src,L) + else + Weaken(2, 1, 1) + + toggle_leap(0) + pounce_cooldown = world.time + pounce_cooldown_time + else if(A.density && !A.CanPass(src)) + visible_message("[src] smashes into [A]!", "[src] smashes into [A]!") + Weaken(2, 1, 1) + + if(leaping) + leaping = 0 + update_icons() + update_canmove() + + +/mob/living/carbon/alien/humanoid/float(on) + if(leaping) + return + ..() diff --git a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm index 3acb1fe87137..c436a43e4ffc 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/caste/sentinel.dm @@ -1,85 +1,85 @@ -/mob/living/carbon/alien/humanoid/sentinel - name = "alien sentinel" - caste = "s" - maxHealth = 150 - health = 150 - icon_state = "aliens_s" - -/mob/living/carbon/alien/humanoid/sentinel/large - name = "alien praetorian" - icon = 'icons/mob/alienlarge.dmi' - icon_state = "prat_s" - pixel_x = -16 - maxHealth = 200 - health = 200 - large = 1 - -/mob/living/carbon/alien/humanoid/sentinel/praetorian - name = "alien praetorian" - maxHealth = 200 - health = 200 - large = 1 - -/mob/living/carbon/alien/humanoid/sentinel/large/update_icons() - overlays.Cut() - if(stat == DEAD) - icon_state = "prat_dead" - else if(stat == UNCONSCIOUS || lying || resting) - icon_state = "prat_sleep" - else - icon_state = "prat_s" - - for(var/image/I in overlays_standing) - overlays += I - -/mob/living/carbon/alien/humanoid/sentinel/New() - create_reagents(100) - if(name == "alien sentinel") - name = text("alien sentinel ([rand(1, 1000)])") - real_name = name - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel - alien_organs += new /obj/item/organ/internal/xenos/acidgland - alien_organs += new /obj/item/organ/internal/xenos/neurotoxin - ..() - -/mob/living/carbon/alien/humanoid/sentinel/handle_hud_icons_health() - ..() //-Yvarov - - if(healths) - if(stat != 2) - switch(health) - if(150 to INFINITY) - healths.icon_state = "health0" - if(100 to 150) - healths.icon_state = "health1" - if(75 to 100) - healths.icon_state = "health2" - if(25 to 75) - healths.icon_state = "health3" - if(0 to 25) - healths.icon_state = "health4" - else - healths.icon_state = "health5" - else - healths.icon_state = "health6" - -/* -/mob/living/carbon/alien/humanoid/sentinel/verb/evolve() // -- TLE - set name = "Evolve (250)" - set desc = "Become a Praetorian, Royal Guard to the Queen." - set category = "Alien" - - if(powerc(250)) - adjustToxLoss(-250) - to_chat(src, "You begin to evolve!") - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] begins to twist and contort!"), 1) - var/mob/living/carbon/alien/humanoid/sentinel/praetorian/new_xeno = new(loc) - if(mind) - mind.transfer_to(new_xeno) - else - new_xeno.key = key - new_xeno.mind.name = new_xeno.name - qdel(src) - return -*/ \ No newline at end of file +/mob/living/carbon/alien/humanoid/sentinel + name = "alien sentinel" + caste = "s" + maxHealth = 150 + health = 150 + icon_state = "aliens_s" + +/mob/living/carbon/alien/humanoid/sentinel/large + name = "alien praetorian" + icon = 'icons/mob/alienlarge.dmi' + icon_state = "prat_s" + pixel_x = -16 + maxHealth = 200 + health = 200 + large = 1 + +/mob/living/carbon/alien/humanoid/sentinel/praetorian + name = "alien praetorian" + maxHealth = 200 + health = 200 + large = 1 + +/mob/living/carbon/alien/humanoid/sentinel/large/update_icons() + overlays.Cut() + if(stat == DEAD) + icon_state = "prat_dead" + else if(stat == UNCONSCIOUS || lying || resting) + icon_state = "prat_sleep" + else + icon_state = "prat_s" + + for(var/image/I in overlays_standing) + overlays += I + +/mob/living/carbon/alien/humanoid/sentinel/New() + create_reagents(100) + if(name == "alien sentinel") + name = text("alien sentinel ([rand(1, 1000)])") + real_name = name + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel + alien_organs += new /obj/item/organ/internal/xenos/acidgland + alien_organs += new /obj/item/organ/internal/xenos/neurotoxin + ..() + +/mob/living/carbon/alien/humanoid/sentinel/handle_hud_icons_health() + ..() //-Yvarov + + if(healths) + if(stat != 2) + switch(health) + if(150 to INFINITY) + healths.icon_state = "health0" + if(100 to 150) + healths.icon_state = "health1" + if(75 to 100) + healths.icon_state = "health2" + if(25 to 75) + healths.icon_state = "health3" + if(0 to 25) + healths.icon_state = "health4" + else + healths.icon_state = "health5" + else + healths.icon_state = "health6" + +/* +/mob/living/carbon/alien/humanoid/sentinel/verb/evolve() // -- TLE + set name = "Evolve (250)" + set desc = "Become a Praetorian, Royal Guard to the Queen." + set category = "Alien" + + if(powerc(250)) + adjustToxLoss(-250) + to_chat(src, "You begin to evolve!") + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] begins to twist and contort!"), 1) + var/mob/living/carbon/alien/humanoid/sentinel/praetorian/new_xeno = new(loc) + if(mind) + mind.transfer_to(new_xeno) + else + new_xeno.key = key + new_xeno.mind.name = new_xeno.name + qdel(src) + return +*/ diff --git a/code/modules/mob/living/carbon/alien/humanoid/emote.dm b/code/modules/mob/living/carbon/alien/humanoid/emote.dm index 7412120cff0c..4134c3fcf35e 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/emote.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/emote.dm @@ -138,4 +138,4 @@ playsound(src.loc, 'sound/voice/hiss1.ogg', 30, 1, 1) if(act == "gnarl") playsound(src.loc, 'sound/voice/hiss4.ogg', 30, 1, 1) - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index d6bb6c6c1ace..29b37294cc46 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -1,123 +1,123 @@ -/mob/living/carbon/alien/humanoid - name = "alien" - icon_state = "alien_s" - - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 5, /obj/item/stack/sheet/animalhide/xeno = 1) - var/obj/item/r_store = null - var/obj/item/l_store = null - var/caste = "" - var/alt_icon = 'icons/mob/alienleap.dmi' //used to switch between the two alien icon files. - var/next_attack = 0 - var/pounce_cooldown = 0 - var/pounce_cooldown_time = 30 - var/leap_on_click = 0 - var/custom_pixel_x_offset = 0 //for admin fuckery. - var/custom_pixel_y_offset = 0 - pass_flags = PASSTABLE - -//This is fine right now, if we're adding organ specific damage this needs to be updated -/mob/living/carbon/alien/humanoid/New() - create_reagents(1000) - if(name == "alien") - name = text("alien ([rand(1, 1000)])") - real_name = name - add_language("Xenomorph") - add_language("Hivemind") - ..() - -/mob/living/carbon/alien/humanoid/Process_Spacemove(var/check_drift = 0) - if(..()) - return 1 - - return 0 - -///mob/living/carbon/alien/humanoid/bullet_act(var/obj/item/projectile/Proj) taken care of in living - -/mob/living/carbon/alien/humanoid/emp_act(severity) - if(r_store) r_store.emp_act(severity) - if(l_store) l_store.emp_act(severity) - ..() - -/mob/living/carbon/alien/humanoid/ex_act(severity) - ..() - - var/shielded = 0 - - var/b_loss = null - var/f_loss = null - switch(severity) - if(1.0) - gib() - return - - if(2.0) - if(!shielded) - b_loss += 60 - - f_loss += 60 - - AdjustEarDamage(30, 120) - if(3.0) - b_loss += 30 - if(prob(50) && !shielded) - Paralyse(1) - AdjustEarDamage(15, 60) - - take_overall_damage(b_loss, f_loss) - -/mob/living/carbon/alien/humanoid/restrained() - if(handcuffed) - return 1 - return 0 - - -/mob/living/carbon/alien/humanoid/var/temperature_resistance = T0C+75 - -/mob/living/carbon/alien/humanoid/show_inv(mob/user as mob) - user.set_machine(src) - - var/dat = {" - - - "} - - dat += "" - - dat += "" - - dat += "" - dat += "
    Left Hand:[(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "Empty"]
    Right Hand:[(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "Empty"]
     
    Head:[(head && !(head.flags&ABSTRACT)) ? head : "Empty"]
     
    Exosuit:[(wear_suit && !(wear_suit.flags&ABSTRACT)) ? wear_suit : "Empty"]
    Pouches:[((l_store && !(l_store.flags&ABSTRACT)) || (r_store && !(r_store.flags&ABSTRACT))) ? "Full" : "Empty"]" - - dat += {"
    - Close - "} - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) - popup.set_content(dat) - popup.open() - -/mob/living/carbon/alien/humanoid/canBeHandcuffed() - return 1 - -/mob/living/carbon/alien/humanoid/cuff_resist(obj/item/I) - playsound(src, 'sound/voice/hiss5.ogg', 40, 1, 1) //Alien roars when starting to break free - ..(I, cuff_break = 1) - -/mob/living/carbon/alien/humanoid/get_standard_pixel_y_offset(lying = 0) - if(leaping) - return -32 - else if(custom_pixel_y_offset) - return custom_pixel_y_offset - else - return initial(pixel_y) - -/mob/living/carbon/alien/humanoid/get_standard_pixel_x_offset(lying = 0) - if(leaping) - return -32 - else if(custom_pixel_x_offset) - return custom_pixel_x_offset - else - return initial(pixel_x) - -/mob/living/carbon/alien/humanoid/get_permeability_protection() - return 0.8 +/mob/living/carbon/alien/humanoid + name = "alien" + icon_state = "alien_s" + + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 5, /obj/item/stack/sheet/animalhide/xeno = 1) + var/obj/item/r_store = null + var/obj/item/l_store = null + var/caste = "" + var/alt_icon = 'icons/mob/alienleap.dmi' //used to switch between the two alien icon files. + var/next_attack = 0 + var/pounce_cooldown = 0 + var/pounce_cooldown_time = 30 + var/leap_on_click = 0 + var/custom_pixel_x_offset = 0 //for admin fuckery. + var/custom_pixel_y_offset = 0 + pass_flags = PASSTABLE + +//This is fine right now, if we're adding organ specific damage this needs to be updated +/mob/living/carbon/alien/humanoid/New() + create_reagents(1000) + if(name == "alien") + name = text("alien ([rand(1, 1000)])") + real_name = name + add_language("Xenomorph") + add_language("Hivemind") + ..() + +/mob/living/carbon/alien/humanoid/Process_Spacemove(var/check_drift = 0) + if(..()) + return 1 + + return 0 + +///mob/living/carbon/alien/humanoid/bullet_act(var/obj/item/projectile/Proj) taken care of in living + +/mob/living/carbon/alien/humanoid/emp_act(severity) + if(r_store) r_store.emp_act(severity) + if(l_store) l_store.emp_act(severity) + ..() + +/mob/living/carbon/alien/humanoid/ex_act(severity) + ..() + + var/shielded = 0 + + var/b_loss = null + var/f_loss = null + switch(severity) + if(1.0) + gib() + return + + if(2.0) + if(!shielded) + b_loss += 60 + + f_loss += 60 + + AdjustEarDamage(30, 120) + if(3.0) + b_loss += 30 + if(prob(50) && !shielded) + Paralyse(1) + AdjustEarDamage(15, 60) + + take_overall_damage(b_loss, f_loss) + +/mob/living/carbon/alien/humanoid/restrained() + if(handcuffed) + return 1 + return 0 + + +/mob/living/carbon/alien/humanoid/var/temperature_resistance = T0C+75 + +/mob/living/carbon/alien/humanoid/show_inv(mob/user as mob) + user.set_machine(src) + + var/dat = {" + + + "} + + dat += "" + + dat += "" + + dat += "" + dat += "
    Left Hand:[(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "Empty"]
    Right Hand:[(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "Empty"]
     
    Head:[(head && !(head.flags&ABSTRACT)) ? head : "Empty"]
     
    Exosuit:[(wear_suit && !(wear_suit.flags&ABSTRACT)) ? wear_suit : "Empty"]
    Pouches:[((l_store && !(l_store.flags&ABSTRACT)) || (r_store && !(r_store.flags&ABSTRACT))) ? "Full" : "Empty"]" + + dat += {"
    + Close + "} + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) + popup.set_content(dat) + popup.open() + +/mob/living/carbon/alien/humanoid/canBeHandcuffed() + return 1 + +/mob/living/carbon/alien/humanoid/cuff_resist(obj/item/I) + playsound(src, 'sound/voice/hiss5.ogg', 40, 1, 1) //Alien roars when starting to break free + ..(I, cuff_break = 1) + +/mob/living/carbon/alien/humanoid/get_standard_pixel_y_offset(lying = 0) + if(leaping) + return -32 + else if(custom_pixel_y_offset) + return custom_pixel_y_offset + else + return initial(pixel_y) + +/mob/living/carbon/alien/humanoid/get_standard_pixel_x_offset(lying = 0) + if(leaping) + return -32 + else if(custom_pixel_x_offset) + return custom_pixel_x_offset + else + return initial(pixel_x) + +/mob/living/carbon/alien/humanoid/get_permeability_protection() + return 0.8 diff --git a/code/modules/mob/living/carbon/alien/humanoid/inventory.dm b/code/modules/mob/living/carbon/alien/humanoid/inventory.dm index 08017da0b767..e015cc7c9a6d 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/inventory.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/inventory.dm @@ -1,47 +1,47 @@ -//unequip -/mob/living/carbon/alien/humanoid/unEquip(var/obj/item/I, var/force) - . = ..(I, force) - if(!. || !I) - return - - if(I == r_store) - r_store = null - update_inv_pockets() - - else if(I == l_store) - l_store = null - update_inv_pockets() - -/mob/living/carbon/alien/humanoid/attack_ui(slot_id) - var/obj/item/W = get_active_hand() - if(W) - if(!istype(W)) return - switch(slot_id) -// if("o_clothing") -// if("head") - if(slot_l_store) - if(l_store) - return - if(W.w_class > WEIGHT_CLASS_NORMAL) - return - unEquip(W) - l_store = W - update_inv_pockets() - if(slot_r_store) - if(r_store) - return - if(W.w_class > WEIGHT_CLASS_NORMAL) - return - unEquip(W) - r_store = W - update_inv_pockets() - else - switch(slot_id) - if(slot_wear_suit) - if(wear_suit) wear_suit.attack_alien(src) - if(slot_head) - if(head) head.attack_alien(src) - if(slot_l_store) - if(l_store) l_store.attack_alien(src) - if(slot_r_store) - if(r_store) r_store.attack_alien(src) +//unequip +/mob/living/carbon/alien/humanoid/unEquip(var/obj/item/I, var/force) + . = ..(I, force) + if(!. || !I) + return + + if(I == r_store) + r_store = null + update_inv_pockets() + + else if(I == l_store) + l_store = null + update_inv_pockets() + +/mob/living/carbon/alien/humanoid/attack_ui(slot_id) + var/obj/item/W = get_active_hand() + if(W) + if(!istype(W)) return + switch(slot_id) +// if("o_clothing") +// if("head") + if(slot_l_store) + if(l_store) + return + if(W.w_class > WEIGHT_CLASS_NORMAL) + return + unEquip(W) + l_store = W + update_inv_pockets() + if(slot_r_store) + if(r_store) + return + if(W.w_class > WEIGHT_CLASS_NORMAL) + return + unEquip(W) + r_store = W + update_inv_pockets() + else + switch(slot_id) + if(slot_wear_suit) + if(wear_suit) wear_suit.attack_alien(src) + if(slot_head) + if(head) head.attack_alien(src) + if(slot_l_store) + if(l_store) l_store.attack_alien(src) + if(slot_r_store) + if(r_store) r_store.attack_alien(src) diff --git a/code/modules/mob/living/carbon/alien/humanoid/life.dm b/code/modules/mob/living/carbon/alien/humanoid/life.dm index 3af2250bbbdd..646f069970b6 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/life.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/life.dm @@ -1,89 +1,89 @@ -/mob/living/carbon/alien/humanoid/Life(seconds, times_fired) - . = ..() - update_icons() - - - -/mob/living/carbon/alien/humanoid/handle_disabilities() - if(disabilities & EPILEPSY) - if((prob(1) && paralysis < 10)) - to_chat(src, "You have a seizure!") - Paralyse(10) - if(disabilities & COUGHING) - if((prob(5) && paralysis <= 1)) - drop_item() - emote("cough") - return - if(disabilities & TOURETTES) - if((prob(10) && paralysis <= 1)) - Stun(10) - emote("twitch") - return - if(disabilities & NERVOUS) - if(prob(10)) - stuttering = max(10, stuttering) - -/mob/living/carbon/alien/humanoid/proc/adjust_body_temperature(current, loc_temp, boost) - var/temperature = current - var/difference = abs(current-loc_temp) //get difference - var/increments// = difference/10 //find how many increments apart they are - if(difference > 50) - increments = difference/5 - else - increments = difference/10 - var/change = increments*boost // Get the amount to change by (x per increment) - var/temp_change - if(current < loc_temp) - temperature = min(loc_temp, temperature+change) - else if(current > loc_temp) - temperature = max(loc_temp, temperature-change) - temp_change = (temperature - current) - return temp_change - -/mob/living/carbon/alien/humanoid/handle_regular_status_updates() - updatehealth() - - if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP - SetSilence(0) - else //ALIVE. LIGHTS ARE ON - if(health < HEALTH_THRESHOLD_DEAD && check_death_method() || !get_int_organ(/obj/item/organ/internal/brain)) - death() - SetSilence(0) - return 1 - - //UNCONSCIOUS. NO-ONE IS HOME - if((getOxyLoss() > 50) || (HEALTH_THRESHOLD_CRIT >= health && check_death_method())) - if(health <= 20 && prob(1)) - emote("gasp") - if(!reagents.has_reagent("epinephrine")) - adjustOxyLoss(1) - Paralyse(3) - - if(paralysis) - stat = UNCONSCIOUS - else if(sleeping) - stat = UNCONSCIOUS - if(prob(10) && health) - emote("hiss") - //CONSCIOUS - else - stat = CONSCIOUS - - /* What in the living hell is this?*/ - if(move_delay_add > 0) - move_delay_add = max(0, move_delay_add - rand(1, 2)) - - if(eye_blind) //blindness, heals slowly over time - AdjustEyeBlind(-1) - else if(eye_blurry) //blurry eyes heal slowly - AdjustEyeBlurry(-1) - - if(stuttering) - AdjustStuttering(-1) - - if(silent) - AdjustSilence(-1) - - if(druggy) - AdjustDruggy(-1) - return 1 +/mob/living/carbon/alien/humanoid/Life(seconds, times_fired) + . = ..() + update_icons() + + + +/mob/living/carbon/alien/humanoid/handle_disabilities() + if(disabilities & EPILEPSY) + if((prob(1) && paralysis < 10)) + to_chat(src, "You have a seizure!") + Paralyse(10) + if(disabilities & COUGHING) + if((prob(5) && paralysis <= 1)) + drop_item() + emote("cough") + return + if(disabilities & TOURETTES) + if((prob(10) && paralysis <= 1)) + Stun(10) + emote("twitch") + return + if(disabilities & NERVOUS) + if(prob(10)) + stuttering = max(10, stuttering) + +/mob/living/carbon/alien/humanoid/proc/adjust_body_temperature(current, loc_temp, boost) + var/temperature = current + var/difference = abs(current-loc_temp) //get difference + var/increments// = difference/10 //find how many increments apart they are + if(difference > 50) + increments = difference/5 + else + increments = difference/10 + var/change = increments*boost // Get the amount to change by (x per increment) + var/temp_change + if(current < loc_temp) + temperature = min(loc_temp, temperature+change) + else if(current > loc_temp) + temperature = max(loc_temp, temperature-change) + temp_change = (temperature - current) + return temp_change + +/mob/living/carbon/alien/humanoid/handle_regular_status_updates() + updatehealth() + + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP + SetSilence(0) + else //ALIVE. LIGHTS ARE ON + if(health < HEALTH_THRESHOLD_DEAD && check_death_method() || !get_int_organ(/obj/item/organ/internal/brain)) + death() + SetSilence(0) + return 1 + + //UNCONSCIOUS. NO-ONE IS HOME + if((getOxyLoss() > 50) || (HEALTH_THRESHOLD_CRIT >= health && check_death_method())) + if(health <= 20 && prob(1)) + emote("gasp") + if(!reagents.has_reagent("epinephrine")) + adjustOxyLoss(1) + Paralyse(3) + + if(paralysis) + stat = UNCONSCIOUS + else if(sleeping) + stat = UNCONSCIOUS + if(prob(10) && health) + emote("hiss") + //CONSCIOUS + else + stat = CONSCIOUS + + /* What in the living hell is this?*/ + if(move_delay_add > 0) + move_delay_add = max(0, move_delay_add - rand(1, 2)) + + if(eye_blind) //blindness, heals slowly over time + AdjustEyeBlind(-1) + else if(eye_blurry) //blurry eyes heal slowly + AdjustEyeBlurry(-1) + + if(stuttering) + AdjustStuttering(-1) + + if(silent) + AdjustSilence(-1) + + if(druggy) + AdjustDruggy(-1) + return 1 diff --git a/code/modules/mob/living/carbon/alien/humanoid/queen.dm b/code/modules/mob/living/carbon/alien/humanoid/queen.dm index 0132bcdf88bd..13eb62587be1 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/queen.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/queen.dm @@ -1,95 +1,95 @@ -/mob/living/carbon/alien/humanoid/queen - name = "alien queen" - caste = "q" - maxHealth = 250 - health = 250 - icon_state = "alienq_s" - status_flags = CANPARALYSE - heal_rate = 5 - large = 1 - ventcrawler = 0 - pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper. - -/mob/living/carbon/alien/humanoid/queen/New() - create_reagents(100) - - //there should only be one queen - for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) - if(Q == src) continue - if(Q.stat == DEAD) continue - if(Q.client) - name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. - break - - real_name = src.name - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/queen - alien_organs += new /obj/item/organ/internal/xenos/acidgland - alien_organs += new /obj/item/organ/internal/xenos/eggsac - alien_organs += new /obj/item/organ/internal/xenos/resinspinner - alien_organs += new /obj/item/organ/internal/xenos/neurotoxin - ..() - -/mob/living/carbon/alien/humanoid/queen/movement_delay() - . = ..() - . += 3 - -/mob/living/carbon/alien/humanoid/queen/handle_hud_icons_health() - ..() //-Yvarov - - if(healths) - if(stat != DEAD) - switch(health) - if(250 to INFINITY) - healths.icon_state = "health0" - if(175 to 250) - healths.icon_state = "health1" - if(100 to 175) - healths.icon_state = "health2" - if(50 to 100) - healths.icon_state = "health3" - if(0 to 50) - healths.icon_state = "health4" - else - healths.icon_state = "health5" - else - healths.icon_state = "health6" - -/mob/living/carbon/alien/humanoid/queen/can_inject() - return FALSE - -//Queen verbs -/mob/living/carbon/alien/humanoid/queen/verb/lay_egg() - - set name = "Lay Egg (75)" - set desc = "Lay an egg to produce huggers to impregnate prey with." - set category = "Alien" - if(locate(/obj/structure/alien/egg) in get_turf(src)) - to_chat(src, "There's already an egg here.") - return - - if(powerc(75,1))//Can't plant eggs on spess tiles. That's silly. - adjustPlasma(-75) - for(var/mob/O in viewers(src, null)) - O.show_message(text("[src] has laid an egg!"), 1) - new /obj/structure/alien/egg(loc) - return - - -/mob/living/carbon/alien/humanoid/queen/large - icon = 'icons/mob/alienlarge.dmi' - icon_state = "queen_s" - pixel_x = -16 - large = 1 - -/mob/living/carbon/alien/humanoid/queen/large/update_icons() - overlays.Cut() - - if(stat == DEAD) - icon_state = "queen_dead" - else if(stat == UNCONSCIOUS || lying || resting) - icon_state = "queen_sleep" - else - icon_state = "queen_s" - - for(var/image/I in overlays_standing) - overlays += I \ No newline at end of file +/mob/living/carbon/alien/humanoid/queen + name = "alien queen" + caste = "q" + maxHealth = 250 + health = 250 + icon_state = "alienq_s" + status_flags = CANPARALYSE + heal_rate = 5 + large = 1 + ventcrawler = 0 + pressure_resistance = 200 //Because big, stompy xenos should not be blown around like paper. + +/mob/living/carbon/alien/humanoid/queen/New() + create_reagents(100) + + //there should only be one queen + for(var/mob/living/carbon/alien/humanoid/queen/Q in GLOB.living_mob_list) + if(Q == src) continue + if(Q.stat == DEAD) continue + if(Q.client) + name = "alien princess ([rand(1, 999)])" //if this is too cutesy feel free to change it/remove it. + break + + real_name = src.name + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/queen + alien_organs += new /obj/item/organ/internal/xenos/acidgland + alien_organs += new /obj/item/organ/internal/xenos/eggsac + alien_organs += new /obj/item/organ/internal/xenos/resinspinner + alien_organs += new /obj/item/organ/internal/xenos/neurotoxin + ..() + +/mob/living/carbon/alien/humanoid/queen/movement_delay() + . = ..() + . += 3 + +/mob/living/carbon/alien/humanoid/queen/handle_hud_icons_health() + ..() //-Yvarov + + if(healths) + if(stat != DEAD) + switch(health) + if(250 to INFINITY) + healths.icon_state = "health0" + if(175 to 250) + healths.icon_state = "health1" + if(100 to 175) + healths.icon_state = "health2" + if(50 to 100) + healths.icon_state = "health3" + if(0 to 50) + healths.icon_state = "health4" + else + healths.icon_state = "health5" + else + healths.icon_state = "health6" + +/mob/living/carbon/alien/humanoid/queen/can_inject() + return FALSE + +//Queen verbs +/mob/living/carbon/alien/humanoid/queen/verb/lay_egg() + + set name = "Lay Egg (75)" + set desc = "Lay an egg to produce huggers to impregnate prey with." + set category = "Alien" + if(locate(/obj/structure/alien/egg) in get_turf(src)) + to_chat(src, "There's already an egg here.") + return + + if(powerc(75,1))//Can't plant eggs on spess tiles. That's silly. + adjustPlasma(-75) + for(var/mob/O in viewers(src, null)) + O.show_message(text("[src] has laid an egg!"), 1) + new /obj/structure/alien/egg(loc) + return + + +/mob/living/carbon/alien/humanoid/queen/large + icon = 'icons/mob/alienlarge.dmi' + icon_state = "queen_s" + pixel_x = -16 + large = 1 + +/mob/living/carbon/alien/humanoid/queen/large/update_icons() + overlays.Cut() + + if(stat == DEAD) + icon_state = "queen_dead" + else if(stat == UNCONSCIOUS || lying || resting) + icon_state = "queen_sleep" + else + icon_state = "queen_s" + + for(var/image/I in overlays_standing) + overlays += I diff --git a/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm b/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm index 7f17a8a87e13..4b7845b0a0a5 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/update_icons.dm @@ -1,166 +1,166 @@ -//Xeno Overlays Indexes////////// -#define X_HEAD_LAYER 1 -#define X_SUIT_LAYER 2 -#define X_L_HAND_LAYER 3 -#define X_R_HAND_LAYER 4 -#define X_TARGETED_LAYER 5 -#define X_FIRE_LAYER 6 -#define X_TOTAL_LAYERS 6 -///////////////////////////////// - -/mob/living/carbon/alien/humanoid - var/list/overlays_standing[X_TOTAL_LAYERS] - -/mob/living/carbon/alien/humanoid/update_icons() - overlays.Cut() - for(var/image/I in overlays_standing) - overlays += I - - if(stat == DEAD) - //If we mostly took damage from fire - if(getFireLoss() > 125) - icon_state = "alien[caste]_husked" - pixel_y = 0 - else - icon_state = "alien[caste]_dead" - pixel_y = 0 - - else if(stat == UNCONSCIOUS || IsWeakened()) - icon_state = "alien[caste]_unconscious" - pixel_y = 0 - else if(leap_on_click) - icon_state = "alien[caste]_pounce" - - else if(lying || resting) - icon_state = "alien[caste]_sleep" - else if(m_intent == MOVE_INTENT_RUN) - icon_state = "alien[caste]_running" - else - icon_state = "alien[caste]_s" - - if(leaping) - if(alt_icon == initial(alt_icon)) - var/old_icon = icon - icon = alt_icon - alt_icon = old_icon - icon_state = "alien[caste]_leap" - pixel_x = -32 - pixel_y = -32 - else - if(alt_icon != initial(alt_icon)) - var/old_icon = icon - icon = alt_icon - alt_icon = old_icon - pixel_x = get_standard_pixel_x_offset(lying) - pixel_y = get_standard_pixel_y_offset(lying) - -/mob/living/carbon/alien/humanoid/regenerate_icons() - ..() - if(notransform) return - - update_inv_head(0,0) - update_inv_wear_suit(0,0) - update_inv_r_hand(0) - update_inv_l_hand(0) - update_inv_pockets(0) - update_icons() - update_fire() - update_transform() - -/mob/living/carbon/alien/humanoid/update_transform() //The old method of updating lying/standing was update_icons(). Aliens still expect that. - if(lying > 0) - lying = 90 //Anything else looks retarded - ..() - update_icons() - -/mob/living/carbon/alien/humanoid/update_fire() - overlays -= overlays_standing[X_FIRE_LAYER] - if(on_fire) - overlays_standing[X_FIRE_LAYER] = image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning", "layer"= -X_FIRE_LAYER) - overlays += overlays_standing[X_FIRE_LAYER] - return - else - overlays_standing[X_FIRE_LAYER] = null - -/mob/living/carbon/alien/humanoid/update_inv_wear_suit(var/update_icons=1) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_suit] - inv.update_icon() - - if(wear_suit) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - wear_suit.screen_loc = ui_oclothing //TODO //...draw the item in the inventory screen - client.screen += wear_suit //Either way, add the item to the HUD - - var/t_state = wear_suit.item_state - if(!t_state) t_state = wear_suit.icon_state - var/image/standing = image("icon" = 'icons/mob/mob.dmi', "icon_state" = "[t_state]") - - if(wear_suit.blood_DNA) - var/t_suit = "suit" - if( istype(wear_suit, /obj/item/clothing/suit/armor) ) - t_suit = "armor" - standing.overlays += image("icon" = 'icons/effects/blood.dmi', "icon_state" = "[t_suit]blood") - - if(wear_suit.breakouttime) - drop_r_hand() - drop_l_hand() - - overlays_standing[X_SUIT_LAYER] = standing - else - overlays_standing[X_SUIT_LAYER] = null - if(update_icons) update_icons() - - -/mob/living/carbon/alien/humanoid/update_inv_head(var/update_icons=1) - if(head) - var/t_state = head.item_state - if(!t_state) t_state = head.icon_state - var/image/standing = image("icon" = 'icons/mob/mob.dmi', "icon_state" = "[t_state]") - if(head.blood_DNA) - standing.overlays += image("icon" = 'icons/effects/blood.dmi', "icon_state" = "helmetblood") - head.screen_loc = ui_alien_head - overlays_standing[X_HEAD_LAYER] = standing - else - overlays_standing[X_HEAD_LAYER] = null - if(update_icons) update_icons() - - -/mob/living/carbon/alien/humanoid/update_inv_pockets(var/update_icons=1) - if(l_store) l_store.screen_loc = ui_storage1 - if(r_store) r_store.screen_loc = ui_storage2 - if(update_icons) update_icons() - - -/mob/living/carbon/alien/humanoid/update_inv_r_hand(var/update_icons=1) - ..(1) - if(r_hand) - var/t_state = r_hand.item_state - if(!t_state) t_state = r_hand.icon_state - r_hand.screen_loc = ui_rhand - overlays_standing[X_R_HAND_LAYER] = image("icon" = r_hand.righthand_file, "icon_state" = t_state) - else - overlays_standing[X_R_HAND_LAYER] = null - if(update_icons) update_icons() - -/mob/living/carbon/alien/humanoid/update_inv_l_hand(var/update_icons=1) - ..(1) - if(l_hand) - var/t_state = l_hand.item_state - if(!t_state) t_state = l_hand.icon_state - l_hand.screen_loc = ui_lhand - overlays_standing[X_L_HAND_LAYER] = image("icon" = l_hand.lefthand_file, "icon_state" = t_state) - else - overlays_standing[X_L_HAND_LAYER] = null - if(update_icons) update_icons() - - -//Xeno Overlays Indexes////////// -#undef X_HEAD_LAYER -#undef X_SUIT_LAYER -#undef X_L_HAND_LAYER -#undef X_R_HAND_LAYER -#undef X_TARGETED_LAYER -#undef X_FIRE_LAYER -#undef X_TOTAL_LAYERS +//Xeno Overlays Indexes////////// +#define X_HEAD_LAYER 1 +#define X_SUIT_LAYER 2 +#define X_L_HAND_LAYER 3 +#define X_R_HAND_LAYER 4 +#define X_TARGETED_LAYER 5 +#define X_FIRE_LAYER 6 +#define X_TOTAL_LAYERS 6 +///////////////////////////////// + +/mob/living/carbon/alien/humanoid + var/list/overlays_standing[X_TOTAL_LAYERS] + +/mob/living/carbon/alien/humanoid/update_icons() + overlays.Cut() + for(var/image/I in overlays_standing) + overlays += I + + if(stat == DEAD) + //If we mostly took damage from fire + if(getFireLoss() > 125) + icon_state = "alien[caste]_husked" + pixel_y = 0 + else + icon_state = "alien[caste]_dead" + pixel_y = 0 + + else if(stat == UNCONSCIOUS || IsWeakened()) + icon_state = "alien[caste]_unconscious" + pixel_y = 0 + else if(leap_on_click) + icon_state = "alien[caste]_pounce" + + else if(lying || resting) + icon_state = "alien[caste]_sleep" + else if(m_intent == MOVE_INTENT_RUN) + icon_state = "alien[caste]_running" + else + icon_state = "alien[caste]_s" + + if(leaping) + if(alt_icon == initial(alt_icon)) + var/old_icon = icon + icon = alt_icon + alt_icon = old_icon + icon_state = "alien[caste]_leap" + pixel_x = -32 + pixel_y = -32 + else + if(alt_icon != initial(alt_icon)) + var/old_icon = icon + icon = alt_icon + alt_icon = old_icon + pixel_x = get_standard_pixel_x_offset(lying) + pixel_y = get_standard_pixel_y_offset(lying) + +/mob/living/carbon/alien/humanoid/regenerate_icons() + ..() + if(notransform) return + + update_inv_head(0,0) + update_inv_wear_suit(0,0) + update_inv_r_hand(0) + update_inv_l_hand(0) + update_inv_pockets(0) + update_icons() + update_fire() + update_transform() + +/mob/living/carbon/alien/humanoid/update_transform() //The old method of updating lying/standing was update_icons(). Aliens still expect that. + if(lying > 0) + lying = 90 //Anything else looks retarded + ..() + update_icons() + +/mob/living/carbon/alien/humanoid/update_fire() + overlays -= overlays_standing[X_FIRE_LAYER] + if(on_fire) + overlays_standing[X_FIRE_LAYER] = image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning", "layer"= -X_FIRE_LAYER) + overlays += overlays_standing[X_FIRE_LAYER] + return + else + overlays_standing[X_FIRE_LAYER] = null + +/mob/living/carbon/alien/humanoid/update_inv_wear_suit(var/update_icons=1) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_suit] + inv.update_icon() + + if(wear_suit) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + wear_suit.screen_loc = ui_oclothing //TODO //...draw the item in the inventory screen + client.screen += wear_suit //Either way, add the item to the HUD + + var/t_state = wear_suit.item_state + if(!t_state) t_state = wear_suit.icon_state + var/image/standing = image("icon" = 'icons/mob/mob.dmi', "icon_state" = "[t_state]") + + if(wear_suit.blood_DNA) + var/t_suit = "suit" + if( istype(wear_suit, /obj/item/clothing/suit/armor) ) + t_suit = "armor" + standing.overlays += image("icon" = 'icons/effects/blood.dmi', "icon_state" = "[t_suit]blood") + + if(wear_suit.breakouttime) + drop_r_hand() + drop_l_hand() + + overlays_standing[X_SUIT_LAYER] = standing + else + overlays_standing[X_SUIT_LAYER] = null + if(update_icons) update_icons() + + +/mob/living/carbon/alien/humanoid/update_inv_head(var/update_icons=1) + if(head) + var/t_state = head.item_state + if(!t_state) t_state = head.icon_state + var/image/standing = image("icon" = 'icons/mob/mob.dmi', "icon_state" = "[t_state]") + if(head.blood_DNA) + standing.overlays += image("icon" = 'icons/effects/blood.dmi', "icon_state" = "helmetblood") + head.screen_loc = ui_alien_head + overlays_standing[X_HEAD_LAYER] = standing + else + overlays_standing[X_HEAD_LAYER] = null + if(update_icons) update_icons() + + +/mob/living/carbon/alien/humanoid/update_inv_pockets(var/update_icons=1) + if(l_store) l_store.screen_loc = ui_storage1 + if(r_store) r_store.screen_loc = ui_storage2 + if(update_icons) update_icons() + + +/mob/living/carbon/alien/humanoid/update_inv_r_hand(var/update_icons=1) + ..(1) + if(r_hand) + var/t_state = r_hand.item_state + if(!t_state) t_state = r_hand.icon_state + r_hand.screen_loc = ui_rhand + overlays_standing[X_R_HAND_LAYER] = image("icon" = r_hand.righthand_file, "icon_state" = t_state) + else + overlays_standing[X_R_HAND_LAYER] = null + if(update_icons) update_icons() + +/mob/living/carbon/alien/humanoid/update_inv_l_hand(var/update_icons=1) + ..(1) + if(l_hand) + var/t_state = l_hand.item_state + if(!t_state) t_state = l_hand.icon_state + l_hand.screen_loc = ui_lhand + overlays_standing[X_L_HAND_LAYER] = image("icon" = l_hand.lefthand_file, "icon_state" = t_state) + else + overlays_standing[X_L_HAND_LAYER] = null + if(update_icons) update_icons() + + +//Xeno Overlays Indexes////////// +#undef X_HEAD_LAYER +#undef X_SUIT_LAYER +#undef X_L_HAND_LAYER +#undef X_R_HAND_LAYER +#undef X_TARGETED_LAYER +#undef X_FIRE_LAYER +#undef X_TOTAL_LAYERS diff --git a/code/modules/mob/living/carbon/alien/larva/emote.dm b/code/modules/mob/living/carbon/alien/larva/emote.dm index 3b18889176bb..2a910428d31f 100644 --- a/code/modules/mob/living/carbon/alien/larva/emote.dm +++ b/code/modules/mob/living/carbon/alien/larva/emote.dm @@ -123,4 +123,4 @@ for(var/mob/O in hearers(src, null)) O.show_message(message, m_type) //Foreach goto(746) - return \ No newline at end of file + return diff --git a/code/modules/mob/living/carbon/alien/larva/inventory.dm b/code/modules/mob/living/carbon/alien/larva/inventory.dm index 0119add7d71a..3ccc62cee6c0 100644 --- a/code/modules/mob/living/carbon/alien/larva/inventory.dm +++ b/code/modules/mob/living/carbon/alien/larva/inventory.dm @@ -1,3 +1,3 @@ -//can't unequip since it can't equip anything -/mob/living/carbon/alien/larva/unEquip(obj/item/W as obj, force) - return \ No newline at end of file +//can't unequip since it can't equip anything +/mob/living/carbon/alien/larva/unEquip(obj/item/W as obj, force) + return diff --git a/code/modules/mob/living/carbon/alien/larva/larva.dm b/code/modules/mob/living/carbon/alien/larva/larva.dm index 1ba5e4530ffa..a55126ec0458 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva.dm @@ -1,96 +1,96 @@ -/mob/living/carbon/alien/larva - name = "alien larva" - real_name = "alien larva" - icon_state = "larva0" - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - - maxHealth = 30 - health = 30 - density = 0 - - var/amount_grown = 0 - var/max_grown = 200 - var/time_of_birth - death_message = "lets out a waning high-pitched cry." - death_sound = null - -//This is fine right now, if we're adding organ specific damage this needs to be updated -/mob/living/carbon/alien/larva/New() - create_reagents(100) - if(name == "alien larva") - name = "alien larva ([rand(1, 1000)])" - real_name = name - regenerate_icons() - add_language("Xenomorph") - add_language("Hivemind") - alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/larva - - ..() - -//This needs to be fixed -/mob/living/carbon/alien/larva/Stat() - ..() - stat(null, "Progress: [amount_grown]/[max_grown]") - -/mob/living/carbon/alien/larva/adjustPlasma(amount) - if(stat != DEAD && amount > 0) - amount_grown = min(amount_grown + 1, max_grown) - ..(amount) - -/mob/living/carbon/alien/larva/ex_act(severity) - ..() - - var/b_loss = null - var/f_loss = null - switch(severity) - if(1.0) - gib() - return - - if(2.0) - - b_loss += 60 - - f_loss += 60 - - AdjustEarDamage(30, 120) - - if(3.0) - b_loss += 30 - if(prob(50)) - Paralyse(1) - AdjustEarDamage(15, 60) - - adjustBruteLoss(b_loss) - adjustFireLoss(f_loss) - - updatehealth() - -//can't equip anything -/mob/living/carbon/alien/larva/attack_ui(slot_id) - return - -/mob/living/carbon/alien/larva/restrained() - return 0 - -/mob/living/carbon/alien/larva/var/temperature_resistance = T0C+75 - -// new damage icon system -// now constructs damage icon for each organ from mask * damage field - - -/mob/living/carbon/alien/larva/show_inv(mob/user as mob) - return - -/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) - return FALSE - -/* Commented out because it's duplicated in life.dm -/mob/living/carbon/alien/larva/proc/grow() // Larvae can grow into full fledged Xenos if they survive long enough -- TLE - if(icon_state == "larva_l" && !canmove) // This is a shit death check. It is made of shit and death. Fix later. - return - else - var/mob/living/carbon/alien/humanoid/A = new(loc) - A.key = key - qdel(src) */ +/mob/living/carbon/alien/larva + name = "alien larva" + real_name = "alien larva" + icon_state = "larva0" + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + + maxHealth = 30 + health = 30 + density = 0 + + var/amount_grown = 0 + var/max_grown = 200 + var/time_of_birth + death_message = "lets out a waning high-pitched cry." + death_sound = null + +//This is fine right now, if we're adding organ specific damage this needs to be updated +/mob/living/carbon/alien/larva/New() + create_reagents(100) + if(name == "alien larva") + name = "alien larva ([rand(1, 1000)])" + real_name = name + regenerate_icons() + add_language("Xenomorph") + add_language("Hivemind") + alien_organs += new /obj/item/organ/internal/xenos/plasmavessel/larva + + ..() + +//This needs to be fixed +/mob/living/carbon/alien/larva/Stat() + ..() + stat(null, "Progress: [amount_grown]/[max_grown]") + +/mob/living/carbon/alien/larva/adjustPlasma(amount) + if(stat != DEAD && amount > 0) + amount_grown = min(amount_grown + 1, max_grown) + ..(amount) + +/mob/living/carbon/alien/larva/ex_act(severity) + ..() + + var/b_loss = null + var/f_loss = null + switch(severity) + if(1.0) + gib() + return + + if(2.0) + + b_loss += 60 + + f_loss += 60 + + AdjustEarDamage(30, 120) + + if(3.0) + b_loss += 30 + if(prob(50)) + Paralyse(1) + AdjustEarDamage(15, 60) + + adjustBruteLoss(b_loss) + adjustFireLoss(f_loss) + + updatehealth() + +//can't equip anything +/mob/living/carbon/alien/larva/attack_ui(slot_id) + return + +/mob/living/carbon/alien/larva/restrained() + return 0 + +/mob/living/carbon/alien/larva/var/temperature_resistance = T0C+75 + +// new damage icon system +// now constructs damage icon for each organ from mask * damage field + + +/mob/living/carbon/alien/larva/show_inv(mob/user as mob) + return + +/mob/living/carbon/alien/larva/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) + return FALSE + +/* Commented out because it's duplicated in life.dm +/mob/living/carbon/alien/larva/proc/grow() // Larvae can grow into full fledged Xenos if they survive long enough -- TLE + if(icon_state == "larva_l" && !canmove) // This is a shit death check. It is made of shit and death. Fix later. + return + else + var/mob/living/carbon/alien/humanoid/A = new(loc) + A.key = key + qdel(src) */ diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm index 79c8ed8207f2..69e12b6ae193 100644 --- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm +++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm @@ -34,4 +34,4 @@ /mob/living/carbon/alien/larva/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) if(!no_effect && !visual_effect_icon) visual_effect_icon = ATTACK_EFFECT_BITE - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm index a0668db83279..fd1543673e69 100644 --- a/code/modules/mob/living/carbon/alien/larva/life.dm +++ b/code/modules/mob/living/carbon/alien/larva/life.dm @@ -1,57 +1,57 @@ -/mob/living/carbon/alien/larva/Life(seconds, times_fired) - if(..()) //still breathing - // GROW! - if(amount_grown < max_grown) - amount_grown++ - - //some kind of bug in canmove() isn't properly calling update_icons, so this is here as a placeholder - update_icons() - -/mob/living/carbon/alien/larva/handle_regular_status_updates() - updatehealth() - - if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP - SetSilence(0) - else //ALIVE. LIGHTS ARE ON - if(health < -25 || !get_int_organ(/obj/item/organ/internal/brain)) - death() - SetSilence(0) - return 1 - - //UNCONSCIOUS. NO-ONE IS HOME - if((getOxyLoss() > 25) || (HEALTH_THRESHOLD_CRIT >= health && check_death_method())) - //if( health <= 20 && prob(1) ) - // spawn(0) - // emote("gasp") - if(!reagents.has_reagent("epinephrine")) - adjustOxyLoss(1) - Paralyse(3) - - if(paralysis) - stat = UNCONSCIOUS - else if(sleeping) - stat = UNCONSCIOUS - if(prob(10) && health) - emote("hiss_") - //CONSCIOUS - else - stat = CONSCIOUS - - /* What in the living hell is this?*/ - if(move_delay_add > 0) - move_delay_add = max(0, move_delay_add - rand(1, 2)) - - if(eye_blind) //blindness, heals slowly over time - AdjustEyeBlind(-1) - else if(eye_blurry) //blurry eyes heal slowly - AdjustEyeBlurry(-1) - - if(stuttering) - AdjustStuttering(-1) - - if(silent) - AdjustSilence(-1) - - if(druggy) - AdjustDruggy(-1) - return 1 +/mob/living/carbon/alien/larva/Life(seconds, times_fired) + if(..()) //still breathing + // GROW! + if(amount_grown < max_grown) + amount_grown++ + + //some kind of bug in canmove() isn't properly calling update_icons, so this is here as a placeholder + update_icons() + +/mob/living/carbon/alien/larva/handle_regular_status_updates() + updatehealth() + + if(stat == DEAD) //DEAD. BROWN BREAD. SWIMMING WITH THE SPESS CARP + SetSilence(0) + else //ALIVE. LIGHTS ARE ON + if(health < -25 || !get_int_organ(/obj/item/organ/internal/brain)) + death() + SetSilence(0) + return 1 + + //UNCONSCIOUS. NO-ONE IS HOME + if((getOxyLoss() > 25) || (HEALTH_THRESHOLD_CRIT >= health && check_death_method())) + //if( health <= 20 && prob(1) ) + // spawn(0) + // emote("gasp") + if(!reagents.has_reagent("epinephrine")) + adjustOxyLoss(1) + Paralyse(3) + + if(paralysis) + stat = UNCONSCIOUS + else if(sleeping) + stat = UNCONSCIOUS + if(prob(10) && health) + emote("hiss_") + //CONSCIOUS + else + stat = CONSCIOUS + + /* What in the living hell is this?*/ + if(move_delay_add > 0) + move_delay_add = max(0, move_delay_add - rand(1, 2)) + + if(eye_blind) //blindness, heals slowly over time + AdjustEyeBlind(-1) + else if(eye_blurry) //blurry eyes heal slowly + AdjustEyeBlurry(-1) + + if(stuttering) + AdjustStuttering(-1) + + if(silent) + AdjustSilence(-1) + + if(druggy) + AdjustDruggy(-1) + return 1 diff --git a/code/modules/mob/living/carbon/alien/larva/powers.dm b/code/modules/mob/living/carbon/alien/larva/powers.dm index 27837dc7cf52..ca12dbaf0d0a 100644 --- a/code/modules/mob/living/carbon/alien/larva/powers.dm +++ b/code/modules/mob/living/carbon/alien/larva/powers.dm @@ -1,54 +1,54 @@ - -/mob/living/carbon/alien/larva/verb/hide() - set name = "Hide" - set desc = "Allows to hide beneath tables or certain items. Toggled on or off." - set category = "Alien" - - if(stat != CONSCIOUS) - return - - if(layer != ABOVE_NORMAL_TURF_LAYER) - layer = ABOVE_NORMAL_TURF_LAYER - visible_message("[src] scurries to the ground!", "You are now hiding.") - else - layer = MOB_LAYER - visible_message("[src] slowly peeks up from the ground...", "You have stopped hiding.") - -/mob/living/carbon/alien/larva/verb/evolve() - set name = "Evolve" - set desc = "Evolve into a fully grown Alien." - set category = "Alien" - - if(stat != CONSCIOUS) - return - - if(handcuffed || legcuffed) - to_chat(src, "You cannot evolve when you are cuffed.") - - if(amount_grown >= max_grown) //TODO ~Carn - //green is impossible to read, so i made these blue and changed the formatting slightly - to_chat(src, "You are growing into a beautiful alien! It is time to choose a caste.") - to_chat(src, "There are three to choose from:") - to_chat(src, "Hunters are strong and agile, able to hunt away from the hive and rapidly move through ventilation shafts. Hunters generate plasma slowly and have low reserves.") - to_chat(src, "Sentinels are tasked with protecting the hive and are deadly up close and at a range. They are not as physically imposing nor fast as the hunters.") - to_chat(src, "Drones are the working class, offering the largest plasma storage and generation. They are the only caste which may evolve again, turning into the dreaded alien queen.") - var/alien_caste = alert(src, "Please choose which alien caste you shall belong to.",,"Hunter","Sentinel","Drone") - - var/mob/living/carbon/alien/humanoid/new_xeno - switch(alien_caste) - if("Hunter") - new_xeno = new /mob/living/carbon/alien/humanoid/hunter(loc) - if("Sentinel") - new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(loc) - if("Drone") - new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc) - if(mind) - mind.transfer_to(new_xeno) - else - new_xeno.key = key - new_xeno.mind.name = new_xeno.name - qdel(src) - return - else - to_chat(src, "You are not fully grown.") - return + +/mob/living/carbon/alien/larva/verb/hide() + set name = "Hide" + set desc = "Allows to hide beneath tables or certain items. Toggled on or off." + set category = "Alien" + + if(stat != CONSCIOUS) + return + + if(layer != ABOVE_NORMAL_TURF_LAYER) + layer = ABOVE_NORMAL_TURF_LAYER + visible_message("[src] scurries to the ground!", "You are now hiding.") + else + layer = MOB_LAYER + visible_message("[src] slowly peeks up from the ground...", "You have stopped hiding.") + +/mob/living/carbon/alien/larva/verb/evolve() + set name = "Evolve" + set desc = "Evolve into a fully grown Alien." + set category = "Alien" + + if(stat != CONSCIOUS) + return + + if(handcuffed || legcuffed) + to_chat(src, "You cannot evolve when you are cuffed.") + + if(amount_grown >= max_grown) //TODO ~Carn + //green is impossible to read, so i made these blue and changed the formatting slightly + to_chat(src, "You are growing into a beautiful alien! It is time to choose a caste.") + to_chat(src, "There are three to choose from:") + to_chat(src, "Hunters are strong and agile, able to hunt away from the hive and rapidly move through ventilation shafts. Hunters generate plasma slowly and have low reserves.") + to_chat(src, "Sentinels are tasked with protecting the hive and are deadly up close and at a range. They are not as physically imposing nor fast as the hunters.") + to_chat(src, "Drones are the working class, offering the largest plasma storage and generation. They are the only caste which may evolve again, turning into the dreaded alien queen.") + var/alien_caste = alert(src, "Please choose which alien caste you shall belong to.",,"Hunter","Sentinel","Drone") + + var/mob/living/carbon/alien/humanoid/new_xeno + switch(alien_caste) + if("Hunter") + new_xeno = new /mob/living/carbon/alien/humanoid/hunter(loc) + if("Sentinel") + new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(loc) + if("Drone") + new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc) + if(mind) + mind.transfer_to(new_xeno) + else + new_xeno.key = key + new_xeno.mind.name = new_xeno.name + qdel(src) + return + else + to_chat(src, "You are not fully grown.") + return diff --git a/code/modules/mob/living/carbon/alien/larva/update_icons.dm b/code/modules/mob/living/carbon/alien/larva/update_icons.dm index 697d6413c027..cc3726ab0465 100644 --- a/code/modules/mob/living/carbon/alien/larva/update_icons.dm +++ b/code/modules/mob/living/carbon/alien/larva/update_icons.dm @@ -1,25 +1,25 @@ - -/mob/living/carbon/alien/larva/regenerate_icons() - overlays = list() - update_icons() - -/mob/living/carbon/alien/larva/update_icons() - var/state = 0 - if(amount_grown > 150) - state = 2 - else if(amount_grown > 50) - state = 1 - - if(stat == DEAD) - icon_state = "larva[state]_dead" - else if(handcuffed || legcuffed) //This should be an overlay. Who made this an icon_state? - icon_state = "larva[state]_cuff" - else if(stat == UNCONSCIOUS || lying || resting) - icon_state = "larva[state]_sleep" - else if(stunned) - icon_state = "larva[state]_stun" - else - icon_state = "larva[state]" - -/mob/living/carbon/alien/larva/update_transform() //All this is handled in update_icons() - return update_icons() \ No newline at end of file + +/mob/living/carbon/alien/larva/regenerate_icons() + overlays = list() + update_icons() + +/mob/living/carbon/alien/larva/update_icons() + var/state = 0 + if(amount_grown > 150) + state = 2 + else if(amount_grown > 50) + state = 1 + + if(stat == DEAD) + icon_state = "larva[state]_dead" + else if(handcuffed || legcuffed) //This should be an overlay. Who made this an icon_state? + icon_state = "larva[state]_cuff" + else if(stat == UNCONSCIOUS || lying || resting) + icon_state = "larva[state]_sleep" + else if(stunned) + icon_state = "larva[state]_stun" + else + icon_state = "larva[state]" + +/mob/living/carbon/alien/larva/update_transform() //All this is handled in update_icons() + return update_icons() diff --git a/code/modules/mob/living/carbon/alien/login.dm b/code/modules/mob/living/carbon/alien/login.dm index 8d5968848b6d..5c177fbb6228 100644 --- a/code/modules/mob/living/carbon/alien/login.dm +++ b/code/modules/mob/living/carbon/alien/login.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/alien/Login() - ..() - AddInfectionImages() - return +/mob/living/carbon/alien/Login() + ..() + AddInfectionImages() + return diff --git a/code/modules/mob/living/carbon/alien/logout.dm b/code/modules/mob/living/carbon/alien/logout.dm index a4145581cb3c..f25b21f0cca9 100644 --- a/code/modules/mob/living/carbon/alien/logout.dm +++ b/code/modules/mob/living/carbon/alien/logout.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/alien/Logout() - ..() - RemoveInfectionImages() - return +/mob/living/carbon/alien/Logout() + ..() + RemoveInfectionImages() + return diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm index 0addea8d4b6f..5d828e7c181e 100644 --- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm +++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm @@ -1,129 +1,129 @@ -// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability -// It functions almost identically (see code/datums/diseases/alien_embryo.dm) - -/obj/item/organ/internal/body_egg/alien_embryo - name = "alien embryo" - icon = 'icons/mob/alien.dmi' - icon_state = "larva0_dead" - var/stage = 0 - var/polling = 0 - -/obj/item/organ/internal/body_egg/alien_embryo/on_find(mob/living/finder) - ..() - if(stage < 4) - to_chat(finder, "It's small and weak, barely the size of a fetus.") - else - to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.") - if(prob(10)) - AttemptGrow(0) - -/obj/item/organ/internal/body_egg/alien_embryo/prepare_eat() - var/obj/S = ..() - S.reagents.add_reagent("sacid", 10) - return S - -/obj/item/organ/internal/body_egg/alien_embryo/on_life() - switch(stage) - if(2, 3) - if(prob(2)) - owner.emote("sneeze") - if(prob(2)) - owner.emote("cough") - if(prob(2)) - to_chat(owner, "Your throat feels sore.") - if(prob(2)) - to_chat(owner, "Mucous runs down the back of your throat.") - if(4) - if(prob(2)) - owner.emote("sneeze") - if(prob(2)) - owner.emote("cough") - if(prob(4)) - to_chat(owner, "Your muscles ache.") - if(prob(20)) - owner.take_organ_damage(1) - if(prob(4)) - to_chat(owner, "Your stomach hurts.") - if(prob(20)) - owner.adjustToxLoss(1) - if(5) - to_chat(owner, "You feel something tearing its way out of your stomach...") - owner.adjustToxLoss(10) - -/obj/item/organ/internal/body_egg/alien_embryo/egg_process() - if(stage < 5 && prob(3)) - stage++ - spawn(0) - RefreshInfectionImage() - - if(stage == 5 && prob(50)) - for(var/datum/surgery/S in owner.surgeries) - if(S.location == "chest" && istype(S.get_surgery_step(), /datum/surgery_step/internal/manipulate_organs)) - AttemptGrow(0) - return - AttemptGrow() - - - -/obj/item/organ/internal/body_egg/alien_embryo/proc/AttemptGrow(var/gib_on_success = 1) - if(!owner || polling) - return - polling = 1 - spawn() - var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0) - var/mob/C = null - - // To stop clientless larva, we will check that our host has a client - // if we find no ghosts to become the alien. If the host has a client - // he will become the alien but if he doesn't then we will set the stage - // to 2, so we don't do a process heavy check everytime. - - if(candidates.len) - C = pick(candidates) - else if(owner.client) - C = owner.client - else - stage = 2 // Let's try again later. - polling = 0 - return - - var/overlay = image('icons/mob/alien.dmi', loc = owner, icon_state = "burst_lie") - owner.overlays += overlay - - spawn(6) - var/mob/living/carbon/alien/larva/new_xeno = new(owner.drop_location()) - new_xeno.key = C.key - if(SSticker && SSticker.mode) - SSticker.mode.xenos += new_xeno.mind - new_xeno.mind.name = new_xeno.name - new_xeno.mind.assigned_role = SPECIAL_ROLE_XENOMORPH - new_xeno.mind.special_role = SPECIAL_ROLE_XENOMORPH - new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100)//To get the player's attention - - if(gib_on_success) - owner.gib() - else - owner.adjustBruteLoss(40) - owner.overlays -= overlay - qdel(src) - -/*---------------------------------------- -Proc: AddInfectionImages(C) -Des: Adds the infection image to all aliens for this embryo -----------------------------------------*/ -/obj/item/organ/internal/body_egg/alien_embryo/AddInfectionImages() - for(var/mob/living/carbon/alien/alien in GLOB.player_list) - if(alien.client) - var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]") - alien.client.images += I - -/*---------------------------------------- -Proc: RemoveInfectionImage(C) -Des: Removes all images from the mob infected by this embryo -----------------------------------------*/ -/obj/item/organ/internal/body_egg/alien_embryo/RemoveInfectionImages() - for(var/mob/living/carbon/alien/alien in GLOB.player_list) - if(alien.client) - for(var/image/I in alien.client.images) - if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner) - qdel(I) \ No newline at end of file +// This is to replace the previous datum/disease/alien_embryo for slightly improved handling and maintainability +// It functions almost identically (see code/datums/diseases/alien_embryo.dm) + +/obj/item/organ/internal/body_egg/alien_embryo + name = "alien embryo" + icon = 'icons/mob/alien.dmi' + icon_state = "larva0_dead" + var/stage = 0 + var/polling = 0 + +/obj/item/organ/internal/body_egg/alien_embryo/on_find(mob/living/finder) + ..() + if(stage < 4) + to_chat(finder, "It's small and weak, barely the size of a fetus.") + else + to_chat(finder, "It's grown quite large, and writhes slightly as you look at it.") + if(prob(10)) + AttemptGrow(0) + +/obj/item/organ/internal/body_egg/alien_embryo/prepare_eat() + var/obj/S = ..() + S.reagents.add_reagent("sacid", 10) + return S + +/obj/item/organ/internal/body_egg/alien_embryo/on_life() + switch(stage) + if(2, 3) + if(prob(2)) + owner.emote("sneeze") + if(prob(2)) + owner.emote("cough") + if(prob(2)) + to_chat(owner, "Your throat feels sore.") + if(prob(2)) + to_chat(owner, "Mucous runs down the back of your throat.") + if(4) + if(prob(2)) + owner.emote("sneeze") + if(prob(2)) + owner.emote("cough") + if(prob(4)) + to_chat(owner, "Your muscles ache.") + if(prob(20)) + owner.take_organ_damage(1) + if(prob(4)) + to_chat(owner, "Your stomach hurts.") + if(prob(20)) + owner.adjustToxLoss(1) + if(5) + to_chat(owner, "You feel something tearing its way out of your stomach...") + owner.adjustToxLoss(10) + +/obj/item/organ/internal/body_egg/alien_embryo/egg_process() + if(stage < 5 && prob(3)) + stage++ + spawn(0) + RefreshInfectionImage() + + if(stage == 5 && prob(50)) + for(var/datum/surgery/S in owner.surgeries) + if(S.location == "chest" && istype(S.get_surgery_step(), /datum/surgery_step/internal/manipulate_organs)) + AttemptGrow(0) + return + AttemptGrow() + + + +/obj/item/organ/internal/body_egg/alien_embryo/proc/AttemptGrow(var/gib_on_success = 1) + if(!owner || polling) + return + polling = 1 + spawn() + var/list/candidates = pollCandidates("Do you want to play as an alien?", ROLE_ALIEN, 0) + var/mob/C = null + + // To stop clientless larva, we will check that our host has a client + // if we find no ghosts to become the alien. If the host has a client + // he will become the alien but if he doesn't then we will set the stage + // to 2, so we don't do a process heavy check everytime. + + if(candidates.len) + C = pick(candidates) + else if(owner.client) + C = owner.client + else + stage = 2 // Let's try again later. + polling = 0 + return + + var/overlay = image('icons/mob/alien.dmi', loc = owner, icon_state = "burst_lie") + owner.overlays += overlay + + spawn(6) + var/mob/living/carbon/alien/larva/new_xeno = new(owner.drop_location()) + new_xeno.key = C.key + if(SSticker && SSticker.mode) + SSticker.mode.xenos += new_xeno.mind + new_xeno.mind.name = new_xeno.name + new_xeno.mind.assigned_role = SPECIAL_ROLE_XENOMORPH + new_xeno.mind.special_role = SPECIAL_ROLE_XENOMORPH + new_xeno << sound('sound/voice/hiss5.ogg',0,0,0,100)//To get the player's attention + + if(gib_on_success) + owner.gib() + else + owner.adjustBruteLoss(40) + owner.overlays -= overlay + qdel(src) + +/*---------------------------------------- +Proc: AddInfectionImages(C) +Des: Adds the infection image to all aliens for this embryo +----------------------------------------*/ +/obj/item/organ/internal/body_egg/alien_embryo/AddInfectionImages() + for(var/mob/living/carbon/alien/alien in GLOB.player_list) + if(alien.client) + var/I = image('icons/mob/alien.dmi', loc = owner, icon_state = "infected[stage]") + alien.client.images += I + +/*---------------------------------------- +Proc: RemoveInfectionImage(C) +Des: Removes all images from the mob infected by this embryo +----------------------------------------*/ +/obj/item/organ/internal/body_egg/alien_embryo/RemoveInfectionImages() + for(var/mob/living/carbon/alien/alien in GLOB.player_list) + if(alien.client) + for(var/image/I in alien.client.images) + if(dd_hasprefix_case(I.icon_state, "infected") && I.loc == owner) + qdel(I) diff --git a/code/modules/mob/living/carbon/alien/special/facehugger.dm b/code/modules/mob/living/carbon/alien/special/facehugger.dm index b5f3c51b43e7..80f3c73d3d7b 100644 --- a/code/modules/mob/living/carbon/alien/special/facehugger.dm +++ b/code/modules/mob/living/carbon/alien/special/facehugger.dm @@ -1,239 +1,239 @@ -//TODO: Make these simple_animals - -#define MIN_IMPREGNATION_TIME 100 //time it takes to impregnate someone -#define MAX_IMPREGNATION_TIME 150 - -#define MIN_ACTIVE_TIME 200 //time between being dropped and going idle -#define MAX_ACTIVE_TIME 400 - -/obj/item/clothing/mask/facehugger - name = "alien" - desc = "It has some sort of a tube at the end of its tail." - icon = 'icons/mob/alien.dmi' - icon_state = "facehugger" - item_state = "facehugger" - w_class = WEIGHT_CLASS_TINY //note: can be picked up by aliens unlike most other items of w_class below 4 - throw_range = 5 - tint = 3 - flags = AIRTIGHT - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - layer = MOB_LAYER - max_integrity = 100 - - var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case - - var/sterile = FALSE - var/real = TRUE //0 for the toy, 1 for real. Sure I could istype, but fuck that. - var/strength = 5 - - var/attached = 0 - -/obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - ..() - if(obj_integrity < 90) - Die() - -/obj/item/clothing/mask/facehugger/attackby(obj/item/O, mob/user, params) - return O.attack_obj(src, user) - -/obj/item/clothing/mask/facehugger/attack_alien(mob/user) //can be picked up by aliens - return attack_hand(user) - -/obj/item/clothing/mask/facehugger/attack_hand(mob/user) - if((stat == CONSCIOUS && !sterile) && !isalien(user)) - if(Attach(user)) - return - ..() - -/obj/item/clothing/mask/facehugger/attack(mob/living/M, mob/user) - ..() - user.unEquip(src) - Attach(M) - -/obj/item/clothing/mask/facehugger/examine(mob/user) - . = ..() - if(real)//So that giant red text about probisci doesn't show up for fake ones - switch(stat) - if(DEAD,UNCONSCIOUS) - . += "[src] is not moving." - if(CONSCIOUS) - . += "[src] seems to be active!" - if(sterile) - . += "It looks like the proboscis has been removed." - -/obj/item/clothing/mask/facehugger/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > 300) - Die() - -/obj/item/clothing/mask/facehugger/equipped(mob/M) - Attach(M) - -/obj/item/clothing/mask/facehugger/Crossed(atom/target, oldloc) - HasProximity(target) - return - -/obj/item/clothing/mask/facehugger/on_found(mob/finder) - if(stat == CONSCIOUS) - return HasProximity(finder) - return 0 - -/obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM as mob|obj) - if(CanHug(AM) && Adjacent(AM)) - return Attach(AM) - return 0 - -/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed) - if(!..()) - return - if(stat == CONSCIOUS) - icon_state = "[initial(icon_state)]_thrown" - spawn(15) - if(icon_state == "[initial(icon_state)]_thrown") - icon_state = "[initial(icon_state)]" - -/obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom) - ..() - if(stat == CONSCIOUS) - icon_state = "[initial(icon_state)]" - Attach(hit_atom) - -/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/M) - if(!isliving(M)) - return 0 - if((!iscorgi(M) && !iscarbon(M)) || isalien(M)) - return 0 - if(attached) - return 0 - else - attached++ - spawn(MAX_IMPREGNATION_TIME) - attached = 0 - if(M.get_int_organ(/obj/item/organ/internal/xenos/hivenode)) - return 0 - if(M.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo)) - return 0 - if(loc == M) - return 0 - if(stat != CONSCIOUS) - return 0 - if(!sterile) M.take_organ_damage(strength,0) //done here so that even borgs and humans in helmets take damage - M.visible_message("[src] leaps at [M]'s face!", \ - "[src] leaps at [M]'s face!") - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.head && H.head.flags_cover & HEADCOVERSMOUTH) - H.visible_message("[src] smashes against [H]'s [H.head]!", \ - "[src] smashes against [H]'s [H.head]!") - Die() - return 0 - if(iscarbon(M)) - var/mob/living/carbon/target = M - if(target.wear_mask) - if(prob(20)) - return 0 - if(istype(target.wear_mask, /obj/item/clothing/mask/muzzle)) - var/obj/item/clothing/mask/muzzle/S = target.wear_mask - if(S.do_break()) - target.visible_message("[src] spits acid onto [S] melting the lock!", \ - "[src] spits acid onto [S] melting the lock!") - var/obj/item/clothing/W = target.wear_mask - if(W.flags & NODROP) - return 0 - target.unEquip(W) - - target.visible_message("[src] tears [W] off of [target]'s face!", \ - "[src] tears [W] off of [target]'s face!") - - src.loc = target - target.equip_to_slot(src, slot_wear_mask,,0) - if(!sterile) - M.Paralyse(MAX_IMPREGNATION_TIME/6) //something like 25 ticks = 20 seconds with the default settings - - GoIdle() //so it doesn't jump the people that tear it off - - spawn(rand(MIN_IMPREGNATION_TIME,MAX_IMPREGNATION_TIME)) - Impregnate(M) - - return 1 - -/obj/item/clothing/mask/facehugger/proc/Impregnate(mob/living/target as mob) - if(!target || target.stat == DEAD || loc != target) //was taken off or something - return - - if(iscarbon(target)) - var/mob/living/carbon/C = target - if(C.wear_mask != src) - return - - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if(!H.check_has_mouth()) - return - - if(!sterile) - //target.contract_disease(new /datum/disease/alien_embryo(0)) //so infection chance is same as virus infection chance - target.visible_message("[src] falls limp after violating [target]'s face!", \ - "[src] falls limp after violating [target]'s face!") - - Die() - icon_state = "[initial(icon_state)]_impregnated" - - if(!target.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo)) - new /obj/item/organ/internal/body_egg/alien_embryo(target) - else - target.visible_message("[src] violates [target]'s face!", \ - "[src] violates [target]'s face!") - -/obj/item/clothing/mask/facehugger/proc/GoActive() - if(stat == DEAD || stat == CONSCIOUS) - return - - stat = CONSCIOUS - icon_state = "[initial(icon_state)]" - -/obj/item/clothing/mask/facehugger/proc/GoIdle() - if(stat == DEAD || stat == UNCONSCIOUS) - return - - stat = UNCONSCIOUS - icon_state = "[initial(icon_state)]_inactive" - - spawn(rand(MIN_ACTIVE_TIME,MAX_ACTIVE_TIME)) - GoActive() - return - -/obj/item/clothing/mask/facehugger/proc/Die() - if(stat == DEAD) - return - - icon_state = "[initial(icon_state)]_dead" - item_state = "facehugger_inactive" - stat = DEAD - - visible_message("[src] curls up into a ball!") - -/proc/CanHug(mob/living/M) - if(!istype(M)) - return 0 - if(M.stat == DEAD) - return 0 - if(M.get_int_organ(/obj/item/organ/internal/xenos/hivenode)) - return 0 - - if(iscorgi(M)) - return 1 - - var/mob/living/carbon/C = M - if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.head && H.head.flags_cover & HEADCOVERSMOUTH) - return 0 - return 1 - return 0 - -/obj/item/clothing/mask/facehugger/lamarr - name = "Lamarr" - desc = "The worst she might do is attempt to... couple with your head." //hope we don't get sued over a harmless reference, rite? - sterile = 1 - gender = FEMALE \ No newline at end of file +//TODO: Make these simple_animals + +#define MIN_IMPREGNATION_TIME 100 //time it takes to impregnate someone +#define MAX_IMPREGNATION_TIME 150 + +#define MIN_ACTIVE_TIME 200 //time between being dropped and going idle +#define MAX_ACTIVE_TIME 400 + +/obj/item/clothing/mask/facehugger + name = "alien" + desc = "It has some sort of a tube at the end of its tail." + icon = 'icons/mob/alien.dmi' + icon_state = "facehugger" + item_state = "facehugger" + w_class = WEIGHT_CLASS_TINY //note: can be picked up by aliens unlike most other items of w_class below 4 + throw_range = 5 + tint = 3 + flags = AIRTIGHT + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + layer = MOB_LAYER + max_integrity = 100 + + var/stat = CONSCIOUS //UNCONSCIOUS is the idle state in this case + + var/sterile = FALSE + var/real = TRUE //0 for the toy, 1 for real. Sure I could istype, but fuck that. + var/strength = 5 + + var/attached = 0 + +/obj/item/clothing/mask/facehugger/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + ..() + if(obj_integrity < 90) + Die() + +/obj/item/clothing/mask/facehugger/attackby(obj/item/O, mob/user, params) + return O.attack_obj(src, user) + +/obj/item/clothing/mask/facehugger/attack_alien(mob/user) //can be picked up by aliens + return attack_hand(user) + +/obj/item/clothing/mask/facehugger/attack_hand(mob/user) + if((stat == CONSCIOUS && !sterile) && !isalien(user)) + if(Attach(user)) + return + ..() + +/obj/item/clothing/mask/facehugger/attack(mob/living/M, mob/user) + ..() + user.unEquip(src) + Attach(M) + +/obj/item/clothing/mask/facehugger/examine(mob/user) + . = ..() + if(real)//So that giant red text about probisci doesn't show up for fake ones + switch(stat) + if(DEAD,UNCONSCIOUS) + . += "[src] is not moving." + if(CONSCIOUS) + . += "[src] seems to be active!" + if(sterile) + . += "It looks like the proboscis has been removed." + +/obj/item/clothing/mask/facehugger/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > 300) + Die() + +/obj/item/clothing/mask/facehugger/equipped(mob/M) + Attach(M) + +/obj/item/clothing/mask/facehugger/Crossed(atom/target, oldloc) + HasProximity(target) + return + +/obj/item/clothing/mask/facehugger/on_found(mob/finder) + if(stat == CONSCIOUS) + return HasProximity(finder) + return 0 + +/obj/item/clothing/mask/facehugger/HasProximity(atom/movable/AM as mob|obj) + if(CanHug(AM) && Adjacent(AM)) + return Attach(AM) + return 0 + +/obj/item/clothing/mask/facehugger/throw_at(atom/target, range, speed) + if(!..()) + return + if(stat == CONSCIOUS) + icon_state = "[initial(icon_state)]_thrown" + spawn(15) + if(icon_state == "[initial(icon_state)]_thrown") + icon_state = "[initial(icon_state)]" + +/obj/item/clothing/mask/facehugger/throw_impact(atom/hit_atom) + ..() + if(stat == CONSCIOUS) + icon_state = "[initial(icon_state)]" + Attach(hit_atom) + +/obj/item/clothing/mask/facehugger/proc/Attach(mob/living/M) + if(!isliving(M)) + return 0 + if((!iscorgi(M) && !iscarbon(M)) || isalien(M)) + return 0 + if(attached) + return 0 + else + attached++ + spawn(MAX_IMPREGNATION_TIME) + attached = 0 + if(M.get_int_organ(/obj/item/organ/internal/xenos/hivenode)) + return 0 + if(M.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo)) + return 0 + if(loc == M) + return 0 + if(stat != CONSCIOUS) + return 0 + if(!sterile) M.take_organ_damage(strength,0) //done here so that even borgs and humans in helmets take damage + M.visible_message("[src] leaps at [M]'s face!", \ + "[src] leaps at [M]'s face!") + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.head && H.head.flags_cover & HEADCOVERSMOUTH) + H.visible_message("[src] smashes against [H]'s [H.head]!", \ + "[src] smashes against [H]'s [H.head]!") + Die() + return 0 + if(iscarbon(M)) + var/mob/living/carbon/target = M + if(target.wear_mask) + if(prob(20)) + return 0 + if(istype(target.wear_mask, /obj/item/clothing/mask/muzzle)) + var/obj/item/clothing/mask/muzzle/S = target.wear_mask + if(S.do_break()) + target.visible_message("[src] spits acid onto [S] melting the lock!", \ + "[src] spits acid onto [S] melting the lock!") + var/obj/item/clothing/W = target.wear_mask + if(W.flags & NODROP) + return 0 + target.unEquip(W) + + target.visible_message("[src] tears [W] off of [target]'s face!", \ + "[src] tears [W] off of [target]'s face!") + + src.loc = target + target.equip_to_slot(src, slot_wear_mask,,0) + if(!sterile) + M.Paralyse(MAX_IMPREGNATION_TIME/6) //something like 25 ticks = 20 seconds with the default settings + + GoIdle() //so it doesn't jump the people that tear it off + + spawn(rand(MIN_IMPREGNATION_TIME,MAX_IMPREGNATION_TIME)) + Impregnate(M) + + return 1 + +/obj/item/clothing/mask/facehugger/proc/Impregnate(mob/living/target as mob) + if(!target || target.stat == DEAD || loc != target) //was taken off or something + return + + if(iscarbon(target)) + var/mob/living/carbon/C = target + if(C.wear_mask != src) + return + + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(!H.check_has_mouth()) + return + + if(!sterile) + //target.contract_disease(new /datum/disease/alien_embryo(0)) //so infection chance is same as virus infection chance + target.visible_message("[src] falls limp after violating [target]'s face!", \ + "[src] falls limp after violating [target]'s face!") + + Die() + icon_state = "[initial(icon_state)]_impregnated" + + if(!target.get_int_organ(/obj/item/organ/internal/body_egg/alien_embryo)) + new /obj/item/organ/internal/body_egg/alien_embryo(target) + else + target.visible_message("[src] violates [target]'s face!", \ + "[src] violates [target]'s face!") + +/obj/item/clothing/mask/facehugger/proc/GoActive() + if(stat == DEAD || stat == CONSCIOUS) + return + + stat = CONSCIOUS + icon_state = "[initial(icon_state)]" + +/obj/item/clothing/mask/facehugger/proc/GoIdle() + if(stat == DEAD || stat == UNCONSCIOUS) + return + + stat = UNCONSCIOUS + icon_state = "[initial(icon_state)]_inactive" + + spawn(rand(MIN_ACTIVE_TIME,MAX_ACTIVE_TIME)) + GoActive() + return + +/obj/item/clothing/mask/facehugger/proc/Die() + if(stat == DEAD) + return + + icon_state = "[initial(icon_state)]_dead" + item_state = "facehugger_inactive" + stat = DEAD + + visible_message("[src] curls up into a ball!") + +/proc/CanHug(mob/living/M) + if(!istype(M)) + return 0 + if(M.stat == DEAD) + return 0 + if(M.get_int_organ(/obj/item/organ/internal/xenos/hivenode)) + return 0 + + if(iscorgi(M)) + return 1 + + var/mob/living/carbon/C = M + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(H.head && H.head.flags_cover & HEADCOVERSMOUTH) + return 0 + return 1 + return 0 + +/obj/item/clothing/mask/facehugger/lamarr + name = "Lamarr" + desc = "The worst she might do is attempt to... couple with your head." //hope we don't get sued over a harmless reference, rite? + sterile = 1 + gender = FEMALE diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index e3cf646d5797..130a2e3a5746 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -1,299 +1,299 @@ -/obj/item/mmi - name = "Man-Machine Interface" - desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity." - icon = 'icons/obj/assemblies.dmi' - icon_state = "mmi_empty" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "biotech=3" - origin_tech = "biotech=2;programming=3;engineering=2" - - //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. - var/alien = 0 - var/syndiemmi = 0 //Whether or not this is a Syndicate MMI - var/mob/living/carbon/brain/brainmob = null//The current occupant. - var/obj/item/organ/internal/brain/held_brain = null // This is so MMI's aren't brainscrubber 9000's - var/mob/living/silicon/robot/robot = null//Appears unused. - var/obj/mecha/mecha = null//This does not appear to be used outside of reference in mecha.dm. -// I'm using this for mechs giving MMIs HUDs now - - var/obj/item/radio/radio = null // For use with the radio MMI upgrade - var/datum/action/generic/configure_mmi_radio/radio_action = null - - // Used for cases when mmi or one of it's children commits suicide. - // Needed to fix a rather insane bug when a posibrain/robotic brain commits suicide - var/dead_icon = "mmi_dead" - -/obj/item/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(istype(O, /obj/item/organ/internal/brain/crystal)) - to_chat(user, " This brain is too malformed to be able to use with the [src].") - return - if(istype(O, /obj/item/organ/internal/brain/golem)) - to_chat(user, "You can't find a way to plug [O] into [src].") - return - if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO - var/obj/item/organ/internal/brain/B = O - if(!B.brainmob) - to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's a useless brain.") - return - if(held_brain) - to_chat(user, "Somehow, this MMI still has a brain in it. Report this to the bug tracker.") - log_runtime(EXCEPTION("[user] tried to stick a [O] into [src] in [get_area(src)], but the held brain variable wasn't cleared"), src) - return - if(user.drop_item()) - B.forceMove(src) - visible_message("[user] sticks \a [O] into \the [src].") - brainmob = B.brainmob - B.brainmob = null - brainmob.forceMove(src) - brainmob.container = src - brainmob.stat = CONSCIOUS - GLOB.respawnable_list -= brainmob - GLOB.dead_mob_list -= brainmob//Update dem lists - GLOB.living_mob_list += brainmob - - held_brain = B - if(istype(O,/obj/item/organ/internal/brain/xeno)) // kept the type check, as it still does other weird stuff - name = "Man-Machine Interface: Alien - [brainmob.real_name]" - icon = 'icons/mob/alien.dmi' - become_occupied("AlienMMI") - alien = 1 - else - name = "Man-Machine Interface: [brainmob.real_name]" - icon = B.mmi_icon - become_occupied("[B.mmi_icon_state]") - alien = 0 - - if(radio_action) - radio_action.UpdateButtonIcon() - feedback_inc("cyborg_mmis_filled",1) - else - to_chat(user, "You can't drop [B]!") - - return - - if(istype(O, /obj/item/mmi_radio_upgrade)) - if(radio) - to_chat(user, "[src] already has a radio installed.") - else - user.visible_message("[user] begins to install the [O] into [src]...", \ - "You start to install the [O] into [src]...") - if(do_after(user, 20, target=src)) - if(user.drop_item()) - user.visible_message("[user] installs [O] in [src].", \ - "You install [O] in [src].") - if(brainmob) - to_chat(brainmob, "MMI radio capability installed.") - install_radio() - qdel(O) - else - to_chat(user, "You can't drop [O]!") - return - - // Maybe later add encryption key support, but that's a pain in the neck atm - - if(brainmob) - O.attack(brainmob, user)//Oh noooeeeee - // Brainmobs can take damage, but they can't actually die. Maybe should fix. - return - return ..() - -/obj/item/mmi/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(!radio) - to_chat(user, "There is no radio in [src]!") - return - user.visible_message("[user] begins to uninstall the radio from [src]...", \ - "You start to uninstall the radio from [src]...") - if(!I.use_tool(src, user, 40, volume = I.tool_volume) || !radio) - return - uninstall_radio() - new /obj/item/mmi_radio_upgrade(get_turf(src)) - user.visible_message("[user] uninstalls the radio from [src].", \ - "You uninstall the radio from [src].") - - -/obj/item/mmi/attack_self(mob/user as mob) - if(!brainmob) - to_chat(user, "You upend the MMI, but there's nothing in it.") - else - to_chat(user, "You unlock and upend the MMI, spilling the brain onto the floor.") - dropbrain(get_turf(user)) - icon = 'icons/obj/assemblies.dmi' - icon_state = "mmi_empty" - name = "Man-Machine Interface" - -/obj/item/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. - brainmob = new(src) - brainmob.name = H.real_name - brainmob.real_name = H.real_name - brainmob.dna = H.dna.Clone() - brainmob.container = src - - if(!istype(H.dna.species) || isnull(H.dna.species.return_organ("brain"))) // Diona/buggy people - held_brain = new(src) - else // We have a species, and it has a brain - var/brain_path = H.dna.species.return_organ("brain") - if(!ispath(brain_path, /obj/item/organ/internal/brain)) - brain_path = /obj/item/organ/internal/brain - held_brain = new brain_path(src) // Slime people will keep their slimy brains this way - held_brain.dna = brainmob.dna.Clone() - held_brain.name = "\the [brainmob.name]'s [initial(held_brain.name)]" - - name = "Man-Machine Interface: [brainmob.real_name]" - become_occupied("mmi_full") - -//I made this proc as a way to have a brainmob be transferred to any created brain, and to solve the -//problem i was having with alien/nonalien brain drops. -/obj/item/mmi/proc/dropbrain(var/turf/dropspot) - if(isnull(held_brain)) - log_runtime(EXCEPTION("[src] at [loc] attempted to drop brain without a contained brain in [get_area(src)]."), src) - to_chat(brainmob, "Your MMI did not contain a brain! We'll make a new one for you, but you'd best report this to the bugtracker!") - held_brain = new(dropspot) // Let's not ruin someone's round because of something dumb -- Crazylemon - held_brain.dna = brainmob.dna.Clone() - held_brain.name = "\the [brainmob.name]'s [initial(held_brain.name)]" - - brainmob.container = null//Reset brainmob mmi var. - brainmob.forceMove(held_brain) //Throw mob into brain. - GLOB.respawnable_list += brainmob - GLOB.living_mob_list -= brainmob//Get outta here - held_brain.brainmob = brainmob//Set the brain to use the brainmob - held_brain.brainmob.cancel_camera() - brainmob = null//Set mmi brainmob var to null - held_brain.forceMove(dropspot) - held_brain = null - -/obj/item/mmi/proc/become_occupied(var/new_icon) - icon_state = new_icon - if(radio) - radio_action.ApplyIcon() - -/obj/item/mmi/examine(mob/user) - . = ..() - if(radio) - . += "A radio is installed on [src]." - -/obj/item/mmi/proc/install_radio() - radio = new(src) - radio.broadcasting = TRUE - radio_action = new(radio, src) - if(brainmob && brainmob.loc == src) - radio_action.Grant(brainmob) - -/obj/item/mmi/proc/uninstall_radio() - QDEL_NULL(radio) - QDEL_NULL(radio_action) - -/datum/action/generic/configure_mmi_radio - name = "Configure MMI Radio" - desc = "Configure the radio installed in your MMI." - check_flags = AB_CHECK_CONSCIOUS - procname = "ui_interact" - var/obj/item/mmi = null - -/datum/action/generic/configure_mmi_radio/New(var/Target, var/obj/item/mmi/M) - . = ..() - mmi = M - -/datum/action/generic/configure_mmi_radio/Destroy() - mmi = null - return ..() - -/datum/action/generic/configure_mmi_radio/ApplyIcon(obj/screen/movable/action_button/current_button) - // A copy/paste of the item action icon code - current_button.overlays.Cut() - if(target) - var/obj/item/I = mmi - var/old_layer = I.layer - var/old_plane = I.plane - I.layer = 21 - I.plane = HUD_PLANE - current_button.overlays += I - I.layer = old_layer - I.plane = old_plane - -/obj/item/mmi/emp_act(severity) - if(!brainmob) - return - else - switch(severity) - if(1) - brainmob.emp_damage += rand(20,30) - if(2) - brainmob.emp_damage += rand(10,20) - if(3) - brainmob.emp_damage += rand(0,10) - ..() - -/obj/item/mmi/relaymove(var/mob/user, var/direction) - if(user.stat || user.stunned) - return - var/obj/item/rig/rig = src.get_rig() - if(rig) - rig.forced_move(direction, user) - -/obj/item/mmi/Destroy() - if(isrobot(loc)) - var/mob/living/silicon/robot/borg = loc - borg.mmi = null - QDEL_NULL(brainmob) - QDEL_NULL(held_brain) - QDEL_NULL(radio) - QDEL_NULL(radio_action) - return ..() - -// These two procs are important for when an MMI pilots a mech -// (Brainmob "enters/leaves" the MMI when piloting) -// Also neatly handles basically every case where a brain -// is inserted or removed from an MMI -/obj/item/mmi/Entered(atom/movable/A) - if(radio && istype(A, /mob/living/carbon/brain)) - radio_action.Grant(A) - -/obj/item/mmi/Exited(atom/movable/A) - if(radio && istype(A, /mob/living/carbon/brain)) - radio_action.Remove(A) - -/obj/item/mmi/syndie - name = "Syndicate Man-Machine Interface" - desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs created with it, but doesn't fit in Nanotrasen AI cores." - origin_tech = "biotech=4;programming=4;syndicate=2" - syndiemmi = 1 - -/obj/item/mmi/attempt_become_organ(obj/item/organ/external/parent,mob/living/carbon/human/H) - if(!brainmob) - return 0 - if(!parent) - log_debug("Attempting to insert into a null parent!") - return 0 - if(H.get_int_organ(/obj/item/organ/internal/brain)) - // one brain at a time - return 0 - var/obj/item/organ/internal/brain/mmi_holder/holder = new() - holder.parent_organ = parent.limb_name - forceMove(holder) - holder.stored_mmi = src - holder.update_from_mmi() - if(brainmob && brainmob.mind) - brainmob.mind.transfer_to(H) - holder.insert(H) - - return 1 - -// As a synthetic, the only limit on visibility is view range -/obj/item/mmi/contents_nano_distance(var/src_object, var/mob/living/user) - if((src_object in view(src)) && get_dist(src_object, src) <= user.client.view) - return STATUS_INTERACTIVE // interactive (green visibility) - return user.shared_living_nano_distance(src_object) - -// For now the only thing that is helped by this is radio access -// Later a more intricate system for MMI UI interaction can be established -/obj/item/mmi/contents_nano_interact(var/src_object, var/mob/living/user) - if(!istype(user, /mob/living/carbon/brain)) - log_runtime(EXCEPTION("Somehow a non-brain mob is inside an MMI!"), user) - return ..() - var/mob/living/carbon/brain/BM = user - if(BM.container == src && src_object == radio) - return STATUS_INTERACTIVE - return ..() +/obj/item/mmi + name = "Man-Machine Interface" + desc = "The Warrior's bland acronym, MMI, obscures the true horror of this monstrosity." + icon = 'icons/obj/assemblies.dmi' + icon_state = "mmi_empty" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "biotech=3" + origin_tech = "biotech=2;programming=3;engineering=2" + + //Revised. Brainmob is now contained directly within object of transfer. MMI in this case. + var/alien = 0 + var/syndiemmi = 0 //Whether or not this is a Syndicate MMI + var/mob/living/carbon/brain/brainmob = null//The current occupant. + var/obj/item/organ/internal/brain/held_brain = null // This is so MMI's aren't brainscrubber 9000's + var/mob/living/silicon/robot/robot = null//Appears unused. + var/obj/mecha/mecha = null//This does not appear to be used outside of reference in mecha.dm. +// I'm using this for mechs giving MMIs HUDs now + + var/obj/item/radio/radio = null // For use with the radio MMI upgrade + var/datum/action/generic/configure_mmi_radio/radio_action = null + + // Used for cases when mmi or one of it's children commits suicide. + // Needed to fix a rather insane bug when a posibrain/robotic brain commits suicide + var/dead_icon = "mmi_dead" + +/obj/item/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(istype(O, /obj/item/organ/internal/brain/crystal)) + to_chat(user, " This brain is too malformed to be able to use with the [src].") + return + if(istype(O, /obj/item/organ/internal/brain/golem)) + to_chat(user, "You can't find a way to plug [O] into [src].") + return + if(istype(O,/obj/item/organ/internal/brain) && !brainmob) //Time to stick a brain in it --NEO + var/obj/item/organ/internal/brain/B = O + if(!B.brainmob) + to_chat(user, "You aren't sure where this brain came from, but you're pretty sure it's a useless brain.") + return + if(held_brain) + to_chat(user, "Somehow, this MMI still has a brain in it. Report this to the bug tracker.") + log_runtime(EXCEPTION("[user] tried to stick a [O] into [src] in [get_area(src)], but the held brain variable wasn't cleared"), src) + return + if(user.drop_item()) + B.forceMove(src) + visible_message("[user] sticks \a [O] into \the [src].") + brainmob = B.brainmob + B.brainmob = null + brainmob.forceMove(src) + brainmob.container = src + brainmob.stat = CONSCIOUS + GLOB.respawnable_list -= brainmob + GLOB.dead_mob_list -= brainmob//Update dem lists + GLOB.living_mob_list += brainmob + + held_brain = B + if(istype(O,/obj/item/organ/internal/brain/xeno)) // kept the type check, as it still does other weird stuff + name = "Man-Machine Interface: Alien - [brainmob.real_name]" + icon = 'icons/mob/alien.dmi' + become_occupied("AlienMMI") + alien = 1 + else + name = "Man-Machine Interface: [brainmob.real_name]" + icon = B.mmi_icon + become_occupied("[B.mmi_icon_state]") + alien = 0 + + if(radio_action) + radio_action.UpdateButtonIcon() + feedback_inc("cyborg_mmis_filled",1) + else + to_chat(user, "You can't drop [B]!") + + return + + if(istype(O, /obj/item/mmi_radio_upgrade)) + if(radio) + to_chat(user, "[src] already has a radio installed.") + else + user.visible_message("[user] begins to install the [O] into [src]...", \ + "You start to install the [O] into [src]...") + if(do_after(user, 20, target=src)) + if(user.drop_item()) + user.visible_message("[user] installs [O] in [src].", \ + "You install [O] in [src].") + if(brainmob) + to_chat(brainmob, "MMI radio capability installed.") + install_radio() + qdel(O) + else + to_chat(user, "You can't drop [O]!") + return + + // Maybe later add encryption key support, but that's a pain in the neck atm + + if(brainmob) + O.attack(brainmob, user)//Oh noooeeeee + // Brainmobs can take damage, but they can't actually die. Maybe should fix. + return + return ..() + +/obj/item/mmi/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(!radio) + to_chat(user, "There is no radio in [src]!") + return + user.visible_message("[user] begins to uninstall the radio from [src]...", \ + "You start to uninstall the radio from [src]...") + if(!I.use_tool(src, user, 40, volume = I.tool_volume) || !radio) + return + uninstall_radio() + new /obj/item/mmi_radio_upgrade(get_turf(src)) + user.visible_message("[user] uninstalls the radio from [src].", \ + "You uninstall the radio from [src].") + + +/obj/item/mmi/attack_self(mob/user as mob) + if(!brainmob) + to_chat(user, "You upend the MMI, but there's nothing in it.") + else + to_chat(user, "You unlock and upend the MMI, spilling the brain onto the floor.") + dropbrain(get_turf(user)) + icon = 'icons/obj/assemblies.dmi' + icon_state = "mmi_empty" + name = "Man-Machine Interface" + +/obj/item/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. + brainmob = new(src) + brainmob.name = H.real_name + brainmob.real_name = H.real_name + brainmob.dna = H.dna.Clone() + brainmob.container = src + + if(!istype(H.dna.species) || isnull(H.dna.species.return_organ("brain"))) // Diona/buggy people + held_brain = new(src) + else // We have a species, and it has a brain + var/brain_path = H.dna.species.return_organ("brain") + if(!ispath(brain_path, /obj/item/organ/internal/brain)) + brain_path = /obj/item/organ/internal/brain + held_brain = new brain_path(src) // Slime people will keep their slimy brains this way + held_brain.dna = brainmob.dna.Clone() + held_brain.name = "\the [brainmob.name]'s [initial(held_brain.name)]" + + name = "Man-Machine Interface: [brainmob.real_name]" + become_occupied("mmi_full") + +//I made this proc as a way to have a brainmob be transferred to any created brain, and to solve the +//problem i was having with alien/nonalien brain drops. +/obj/item/mmi/proc/dropbrain(var/turf/dropspot) + if(isnull(held_brain)) + log_runtime(EXCEPTION("[src] at [loc] attempted to drop brain without a contained brain in [get_area(src)]."), src) + to_chat(brainmob, "Your MMI did not contain a brain! We'll make a new one for you, but you'd best report this to the bugtracker!") + held_brain = new(dropspot) // Let's not ruin someone's round because of something dumb -- Crazylemon + held_brain.dna = brainmob.dna.Clone() + held_brain.name = "\the [brainmob.name]'s [initial(held_brain.name)]" + + brainmob.container = null//Reset brainmob mmi var. + brainmob.forceMove(held_brain) //Throw mob into brain. + GLOB.respawnable_list += brainmob + GLOB.living_mob_list -= brainmob//Get outta here + held_brain.brainmob = brainmob//Set the brain to use the brainmob + held_brain.brainmob.cancel_camera() + brainmob = null//Set mmi brainmob var to null + held_brain.forceMove(dropspot) + held_brain = null + +/obj/item/mmi/proc/become_occupied(var/new_icon) + icon_state = new_icon + if(radio) + radio_action.ApplyIcon() + +/obj/item/mmi/examine(mob/user) + . = ..() + if(radio) + . += "A radio is installed on [src]." + +/obj/item/mmi/proc/install_radio() + radio = new(src) + radio.broadcasting = TRUE + radio_action = new(radio, src) + if(brainmob && brainmob.loc == src) + radio_action.Grant(brainmob) + +/obj/item/mmi/proc/uninstall_radio() + QDEL_NULL(radio) + QDEL_NULL(radio_action) + +/datum/action/generic/configure_mmi_radio + name = "Configure MMI Radio" + desc = "Configure the radio installed in your MMI." + check_flags = AB_CHECK_CONSCIOUS + procname = "ui_interact" + var/obj/item/mmi = null + +/datum/action/generic/configure_mmi_radio/New(var/Target, var/obj/item/mmi/M) + . = ..() + mmi = M + +/datum/action/generic/configure_mmi_radio/Destroy() + mmi = null + return ..() + +/datum/action/generic/configure_mmi_radio/ApplyIcon(obj/screen/movable/action_button/current_button) + // A copy/paste of the item action icon code + current_button.overlays.Cut() + if(target) + var/obj/item/I = mmi + var/old_layer = I.layer + var/old_plane = I.plane + I.layer = 21 + I.plane = HUD_PLANE + current_button.overlays += I + I.layer = old_layer + I.plane = old_plane + +/obj/item/mmi/emp_act(severity) + if(!brainmob) + return + else + switch(severity) + if(1) + brainmob.emp_damage += rand(20,30) + if(2) + brainmob.emp_damage += rand(10,20) + if(3) + brainmob.emp_damage += rand(0,10) + ..() + +/obj/item/mmi/relaymove(var/mob/user, var/direction) + if(user.stat || user.stunned) + return + var/obj/item/rig/rig = src.get_rig() + if(rig) + rig.forced_move(direction, user) + +/obj/item/mmi/Destroy() + if(isrobot(loc)) + var/mob/living/silicon/robot/borg = loc + borg.mmi = null + QDEL_NULL(brainmob) + QDEL_NULL(held_brain) + QDEL_NULL(radio) + QDEL_NULL(radio_action) + return ..() + +// These two procs are important for when an MMI pilots a mech +// (Brainmob "enters/leaves" the MMI when piloting) +// Also neatly handles basically every case where a brain +// is inserted or removed from an MMI +/obj/item/mmi/Entered(atom/movable/A) + if(radio && istype(A, /mob/living/carbon/brain)) + radio_action.Grant(A) + +/obj/item/mmi/Exited(atom/movable/A) + if(radio && istype(A, /mob/living/carbon/brain)) + radio_action.Remove(A) + +/obj/item/mmi/syndie + name = "Syndicate Man-Machine Interface" + desc = "Syndicate's own brand of MMI. It enforces laws designed to help Syndicate agents achieve their goals upon cyborgs created with it, but doesn't fit in Nanotrasen AI cores." + origin_tech = "biotech=4;programming=4;syndicate=2" + syndiemmi = 1 + +/obj/item/mmi/attempt_become_organ(obj/item/organ/external/parent,mob/living/carbon/human/H) + if(!brainmob) + return 0 + if(!parent) + log_debug("Attempting to insert into a null parent!") + return 0 + if(H.get_int_organ(/obj/item/organ/internal/brain)) + // one brain at a time + return 0 + var/obj/item/organ/internal/brain/mmi_holder/holder = new() + holder.parent_organ = parent.limb_name + forceMove(holder) + holder.stored_mmi = src + holder.update_from_mmi() + if(brainmob && brainmob.mind) + brainmob.mind.transfer_to(H) + holder.insert(H) + + return 1 + +// As a synthetic, the only limit on visibility is view range +/obj/item/mmi/contents_nano_distance(var/src_object, var/mob/living/user) + if((src_object in view(src)) && get_dist(src_object, src) <= user.client.view) + return STATUS_INTERACTIVE // interactive (green visibility) + return user.shared_living_nano_distance(src_object) + +// For now the only thing that is helped by this is radio access +// Later a more intricate system for MMI UI interaction can be established +/obj/item/mmi/contents_nano_interact(var/src_object, var/mob/living/user) + if(!istype(user, /mob/living/carbon/brain)) + log_runtime(EXCEPTION("Somehow a non-brain mob is inside an MMI!"), user) + return ..() + var/mob/living/carbon/brain/BM = user + if(BM.container == src && src_object == radio) + return STATUS_INTERACTIVE + return ..() diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 5d2d684e1a7f..ec80d6a44b40 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -1,111 +1,111 @@ -/mob/living/carbon/brain - var/obj/item/container = null - var/timeofhostdeath = 0 - var/emp_damage = 0//Handles a type of MMI damage - use_me = 0 //Can't use the me verb, it's a freaking immobile brain - icon = 'icons/obj/surgery.dmi' - icon_state = "brain1" - -/mob/living/carbon/brain/New() - create_reagents(330) - add_language("Galactic Common") - ..() - -/mob/living/carbon/brain/Destroy() - if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. - if(stat!=DEAD) //If not dead. - death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA - ghostize() //Ghostize checks for key so nothing else is necessary. - return ..() - -/mob/living/carbon/brain/say_understands(other)//Goddamn is this hackish, but this say code is so odd - if(istype(other, /mob/living/silicon/ai)) - if(!(container && istype(container, /obj/item/mmi))) - return 0 - else - return 1 - if(istype(other, /mob/living/silicon/decoy)) - if(!(container && istype(container, /obj/item/mmi))) - return 0 - else - return 1 - if(istype(other, /mob/living/silicon/pai)) - if(!(container && istype(container, /obj/item/mmi))) - return 0 - else - return 1 - if(istype(other, /mob/living/silicon/robot)) - if(!(container && istype(container, /obj/item/mmi))) - return 0 - else - return 1 - if(istype(other, /mob/living/carbon/human)) - return 1 - if(istype(other, /mob/living/simple_animal/slime)) - return 1 - return ..() - - -/mob/living/carbon/brain/update_canmove(delay_action_updates = 0) - if(in_contents_of(/obj/mecha)) - canmove = 1 - use_me = 1 //If it can move, let it emote - else if(istype(loc, /obj/item/mmi)) - canmove = 1 //mmi won't move anyways so whatever - else - canmove = 0 - - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -/mob/living/carbon/brain/ex_act() //you cant blow up brainmobs because it makes transfer_to() freak out when borgs blow up. - return - -/mob/living/carbon/brain/blob_act(obj/structure/blob/B) - return - -/mob/living/carbon/brain/on_forcemove(atom/newloc) - if(container) - container.forceMove(newloc) - else //something went very wrong. - CRASH("Brainmob without container.") - forceMove(container) - -/* -This will return true if the brain has a container that leaves it less helpless than a naked brain - -I'm using this for Stat to give it a more nifty interface to work with -*/ -/mob/living/carbon/brain/proc/has_synthetic_assistance() - return (container && istype(container, /obj/item/mmi)) || in_contents_of(/obj/mecha) - -/mob/living/carbon/brain/proc/get_race() - if(container) - var/obj/item/mmi/M = container - if(istype(M) && M.held_brain) - return M.held_brain.dna.species.name - else - return "Artificial Life" - if(istype(loc, /obj/item/organ/internal/brain)) - var/obj/item/organ/internal/brain/B = loc - return B.dna.species.name - -/mob/living/carbon/brain/Stat() - ..() - if(has_synthetic_assistance()) - statpanel("Status") - show_stat_emergency_shuttle_eta() - - if(client.statpanel == "Status") - //Knowing how well-off your mech is doing is really important as an MMI - if(istype(src.loc, /obj/mecha)) - var/obj/mecha/M = src.loc - stat("Exosuit Charge:", "[istype(M.cell) ? "[M.cell.charge] / [M.cell.maxcharge]" : "No cell detected"]") - stat("Exosuit Integrity", "[!M.obj_integrity ? "0" : "[(M.obj_integrity / M.max_integrity) * 100]"]%") - -/mob/living/carbon/brain/can_safely_leave_loc() - return 0 //You're not supposed to be ethereal jaunting, brains - -/mob/living/carbon/brain/can_hear() - . = TRUE +/mob/living/carbon/brain + var/obj/item/container = null + var/timeofhostdeath = 0 + var/emp_damage = 0//Handles a type of MMI damage + use_me = 0 //Can't use the me verb, it's a freaking immobile brain + icon = 'icons/obj/surgery.dmi' + icon_state = "brain1" + +/mob/living/carbon/brain/New() + create_reagents(330) + add_language("Galactic Common") + ..() + +/mob/living/carbon/brain/Destroy() + if(key) //If there is a mob connected to this thing. Have to check key twice to avoid false death reporting. + if(stat!=DEAD) //If not dead. + death(1) //Brains can die again. AND THEY SHOULD AHA HA HA HA HA HA + ghostize() //Ghostize checks for key so nothing else is necessary. + return ..() + +/mob/living/carbon/brain/say_understands(other)//Goddamn is this hackish, but this say code is so odd + if(istype(other, /mob/living/silicon/ai)) + if(!(container && istype(container, /obj/item/mmi))) + return 0 + else + return 1 + if(istype(other, /mob/living/silicon/decoy)) + if(!(container && istype(container, /obj/item/mmi))) + return 0 + else + return 1 + if(istype(other, /mob/living/silicon/pai)) + if(!(container && istype(container, /obj/item/mmi))) + return 0 + else + return 1 + if(istype(other, /mob/living/silicon/robot)) + if(!(container && istype(container, /obj/item/mmi))) + return 0 + else + return 1 + if(istype(other, /mob/living/carbon/human)) + return 1 + if(istype(other, /mob/living/simple_animal/slime)) + return 1 + return ..() + + +/mob/living/carbon/brain/update_canmove(delay_action_updates = 0) + if(in_contents_of(/obj/mecha)) + canmove = 1 + use_me = 1 //If it can move, let it emote + else if(istype(loc, /obj/item/mmi)) + canmove = 1 //mmi won't move anyways so whatever + else + canmove = 0 + + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +/mob/living/carbon/brain/ex_act() //you cant blow up brainmobs because it makes transfer_to() freak out when borgs blow up. + return + +/mob/living/carbon/brain/blob_act(obj/structure/blob/B) + return + +/mob/living/carbon/brain/on_forcemove(atom/newloc) + if(container) + container.forceMove(newloc) + else //something went very wrong. + CRASH("Brainmob without container.") + forceMove(container) + +/* +This will return true if the brain has a container that leaves it less helpless than a naked brain + +I'm using this for Stat to give it a more nifty interface to work with +*/ +/mob/living/carbon/brain/proc/has_synthetic_assistance() + return (container && istype(container, /obj/item/mmi)) || in_contents_of(/obj/mecha) + +/mob/living/carbon/brain/proc/get_race() + if(container) + var/obj/item/mmi/M = container + if(istype(M) && M.held_brain) + return M.held_brain.dna.species.name + else + return "Artificial Life" + if(istype(loc, /obj/item/organ/internal/brain)) + var/obj/item/organ/internal/brain/B = loc + return B.dna.species.name + +/mob/living/carbon/brain/Stat() + ..() + if(has_synthetic_assistance()) + statpanel("Status") + show_stat_emergency_shuttle_eta() + + if(client.statpanel == "Status") + //Knowing how well-off your mech is doing is really important as an MMI + if(istype(src.loc, /obj/mecha)) + var/obj/mecha/M = src.loc + stat("Exosuit Charge:", "[istype(M.cell) ? "[M.cell.charge] / [M.cell.maxcharge]" : "No cell detected"]") + stat("Exosuit Integrity", "[!M.obj_integrity ? "0" : "[(M.obj_integrity / M.max_integrity) * 100]"]%") + +/mob/living/carbon/brain/can_safely_leave_loc() + return 0 //You're not supposed to be ethereal jaunting, brains + +/mob/living/carbon/brain/can_hear() + . = TRUE diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm index bfd7b3786e7c..0b6bcededaf7 100644 --- a/code/modules/mob/living/carbon/brain/brain_item.dm +++ b/code/modules/mob/living/carbon/brain/brain_item.dm @@ -1,141 +1,141 @@ -/obj/item/organ/internal/brain - name = "brain" - max_damage = 120 - icon_state = "brain2" - force = 1.0 - w_class = WEIGHT_CLASS_SMALL - throwforce = 1.0 - throw_speed = 3 - throw_range = 5 - origin_tech = "biotech=5" - attack_verb = list("attacked", "slapped", "whacked") - var/mob/living/carbon/brain/brainmob = null - organ_tag = "brain" - parent_organ = "head" - slot = "brain" - vital = TRUE - hidden_pain = TRUE //the brain has no pain receptors, and brain damage is meant to be a stealthy damage type. - var/mmi_icon = 'icons/obj/assemblies.dmi' - var/mmi_icon_state = "mmi_full" - -/obj/item/organ/internal/brain/xeno - name = "xenomorph brain" - desc = "We barely understand the brains of terrestial animals. Who knows what we may find in the brain of such an advanced species?" - icon_state = "brain-x" - origin_tech = "biotech=6" - mmi_icon = 'icons/mob/alien.dmi' - mmi_icon_state = "AlienMMI" - -/obj/item/organ/internal/brain/New() - ..() - spawn(5) - if(brainmob && brainmob.client) - brainmob.client.screen.len = null //clear the hud - -/obj/item/organ/internal/brain/proc/transfer_identity(var/mob/living/carbon/H) - brainmob = new(src) - if(isnull(dna)) // someone didn't set this right... - log_runtime(EXCEPTION("[src] at [loc] did not contain a dna datum at time of removal."), src) - dna = H.dna.Clone() - name = "\the [dna.real_name]'s [initial(src.name)]" - brainmob.dna = dna.Clone() // Silly baycode, what you do -// brainmob.dna = H.dna.Clone() Putting in and taking out a brain doesn't make it a carbon copy of the original brain of the body you put it in - brainmob.name = dna.real_name - brainmob.real_name = dna.real_name - brainmob.timeofhostdeath = H.timeofdeath - if(H.mind) - H.mind.transfer_to(brainmob) - - to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a [initial(src.name)].") - -/obj/item/organ/internal/brain/examine(mob/user) // -- TLE - . = ..() - if(brainmob && brainmob.client)//if thar be a brain inside... the brain. - . += "You can feel the small spark of life still left in this one." - else - . += "This one seems particularly lifeless. Perhaps it will regain some of its luster later.." - -/obj/item/organ/internal/brain/remove(var/mob/living/user,special = 0) - if(dna) - name = "[dna.real_name]'s [initial(name)]" - - if(!owner) return ..() // Probably a redundant removal; just bail - - var/obj/item/organ/internal/brain/B = src - if(!special) - var/mob/living/simple_animal/borer/borer = owner.has_brain_worms() - if(borer) - borer.leave_host() //Should remove borer if the brain is removed - RR - - if(owner.mind && !non_primary)//don't transfer if the owner does not have a mind. - B.transfer_identity(user) - - if(istype(owner,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = owner - H.update_hair(1) - . = ..() - -/obj/item/organ/internal/brain/insert(var/mob/living/target,special = 0) - - name = "[initial(name)]" - var/brain_already_exists = 0 - if(istype(target,/mob/living/carbon/human)) // No more IPC multibrain shenanigans - if(target.get_int_organ(/obj/item/organ/internal/brain)) - brain_already_exists = 1 - - var/mob/living/carbon/human/H = target - H.update_hair(1) - - if(!brain_already_exists) - if(brainmob) - if(target.key) - target.ghostize() - if(brainmob.mind) - brainmob.mind.transfer_to(target) - else - target.key = brainmob.key - else - log_debug("Multibrain shenanigans at ([target.x],[target.y],[target.z]), mob '[target]'") - ..(target, special = special) - -/obj/item/organ/internal/brain/receive_damage(amount, silent = 0) //brains are special; if they receive damage by other means, we really just want the damage to be passed ot the owner and back onto the brain. - if(owner) - owner.adjustBrainLoss(amount) - -/obj/item/organ/internal/brain/necrotize(update_sprite = TRUE) //Brain also has special handling for when it necrotizes - damage = max_damage - status |= ORGAN_DEAD - STOP_PROCESSING(SSobj, src) - if(dead_icon && !is_robotic()) - icon_state = dead_icon - if(owner && vital) - owner.setBrainLoss(120) - -/obj/item/organ/internal/brain/prepare_eat() - return // Too important to eat. - -/obj/item/organ/internal/brain/slime - name = "slime core" - desc = "A complex, organic knot of jelly and crystalline particles." - icon = 'icons/mob/slimes.dmi' - icon_state = "green slime extract" - mmi_icon_state = "slime_mmi" -// parent_organ = "chest" Hello I am from the ministry of rubber forehead aliens how are you - -/obj/item/organ/internal/brain/golem - name = "Runic mind" - desc = "A tightly furled roll of paper, covered with indecipherable runes." - icon = 'icons/obj/wizard.dmi' - icon_state = "scroll" - -/obj/item/organ/internal/brain/Destroy() //copypasted from MMIs. - QDEL_NULL(brainmob) - return ..() - -/obj/item/organ/internal/brain/cluwne - -/obj/item/organ/internal/brain/cluwne/insert(mob/living/target, special = 0, make_cluwne = 1) - ..(target, special = special) - if(ishuman(target) && make_cluwne) - var/mob/living/carbon/human/H = target - H.makeCluwne() //No matter where you go, no matter what you do, you cannot escape \ No newline at end of file +/obj/item/organ/internal/brain + name = "brain" + max_damage = 120 + icon_state = "brain2" + force = 1.0 + w_class = WEIGHT_CLASS_SMALL + throwforce = 1.0 + throw_speed = 3 + throw_range = 5 + origin_tech = "biotech=5" + attack_verb = list("attacked", "slapped", "whacked") + var/mob/living/carbon/brain/brainmob = null + organ_tag = "brain" + parent_organ = "head" + slot = "brain" + vital = TRUE + hidden_pain = TRUE //the brain has no pain receptors, and brain damage is meant to be a stealthy damage type. + var/mmi_icon = 'icons/obj/assemblies.dmi' + var/mmi_icon_state = "mmi_full" + +/obj/item/organ/internal/brain/xeno + name = "xenomorph brain" + desc = "We barely understand the brains of terrestial animals. Who knows what we may find in the brain of such an advanced species?" + icon_state = "brain-x" + origin_tech = "biotech=6" + mmi_icon = 'icons/mob/alien.dmi' + mmi_icon_state = "AlienMMI" + +/obj/item/organ/internal/brain/New() + ..() + spawn(5) + if(brainmob && brainmob.client) + brainmob.client.screen.len = null //clear the hud + +/obj/item/organ/internal/brain/proc/transfer_identity(var/mob/living/carbon/H) + brainmob = new(src) + if(isnull(dna)) // someone didn't set this right... + log_runtime(EXCEPTION("[src] at [loc] did not contain a dna datum at time of removal."), src) + dna = H.dna.Clone() + name = "\the [dna.real_name]'s [initial(src.name)]" + brainmob.dna = dna.Clone() // Silly baycode, what you do +// brainmob.dna = H.dna.Clone() Putting in and taking out a brain doesn't make it a carbon copy of the original brain of the body you put it in + brainmob.name = dna.real_name + brainmob.real_name = dna.real_name + brainmob.timeofhostdeath = H.timeofdeath + if(H.mind) + H.mind.transfer_to(brainmob) + + to_chat(brainmob, "You feel slightly disoriented. That's normal when you're just a [initial(src.name)].") + +/obj/item/organ/internal/brain/examine(mob/user) // -- TLE + . = ..() + if(brainmob && brainmob.client)//if thar be a brain inside... the brain. + . += "You can feel the small spark of life still left in this one." + else + . += "This one seems particularly lifeless. Perhaps it will regain some of its luster later.." + +/obj/item/organ/internal/brain/remove(var/mob/living/user,special = 0) + if(dna) + name = "[dna.real_name]'s [initial(name)]" + + if(!owner) return ..() // Probably a redundant removal; just bail + + var/obj/item/organ/internal/brain/B = src + if(!special) + var/mob/living/simple_animal/borer/borer = owner.has_brain_worms() + if(borer) + borer.leave_host() //Should remove borer if the brain is removed - RR + + if(owner.mind && !non_primary)//don't transfer if the owner does not have a mind. + B.transfer_identity(user) + + if(istype(owner,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = owner + H.update_hair(1) + . = ..() + +/obj/item/organ/internal/brain/insert(var/mob/living/target,special = 0) + + name = "[initial(name)]" + var/brain_already_exists = 0 + if(istype(target,/mob/living/carbon/human)) // No more IPC multibrain shenanigans + if(target.get_int_organ(/obj/item/organ/internal/brain)) + brain_already_exists = 1 + + var/mob/living/carbon/human/H = target + H.update_hair(1) + + if(!brain_already_exists) + if(brainmob) + if(target.key) + target.ghostize() + if(brainmob.mind) + brainmob.mind.transfer_to(target) + else + target.key = brainmob.key + else + log_debug("Multibrain shenanigans at ([target.x],[target.y],[target.z]), mob '[target]'") + ..(target, special = special) + +/obj/item/organ/internal/brain/receive_damage(amount, silent = 0) //brains are special; if they receive damage by other means, we really just want the damage to be passed ot the owner and back onto the brain. + if(owner) + owner.adjustBrainLoss(amount) + +/obj/item/organ/internal/brain/necrotize(update_sprite = TRUE) //Brain also has special handling for when it necrotizes + damage = max_damage + status |= ORGAN_DEAD + STOP_PROCESSING(SSobj, src) + if(dead_icon && !is_robotic()) + icon_state = dead_icon + if(owner && vital) + owner.setBrainLoss(120) + +/obj/item/organ/internal/brain/prepare_eat() + return // Too important to eat. + +/obj/item/organ/internal/brain/slime + name = "slime core" + desc = "A complex, organic knot of jelly and crystalline particles." + icon = 'icons/mob/slimes.dmi' + icon_state = "green slime extract" + mmi_icon_state = "slime_mmi" +// parent_organ = "chest" Hello I am from the ministry of rubber forehead aliens how are you + +/obj/item/organ/internal/brain/golem + name = "Runic mind" + desc = "A tightly furled roll of paper, covered with indecipherable runes." + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll" + +/obj/item/organ/internal/brain/Destroy() //copypasted from MMIs. + QDEL_NULL(brainmob) + return ..() + +/obj/item/organ/internal/brain/cluwne + +/obj/item/organ/internal/brain/cluwne/insert(mob/living/target, special = 0, make_cluwne = 1) + ..(target, special = special) + if(ishuman(target) && make_cluwne) + var/mob/living/carbon/human/H = target + H.makeCluwne() //No matter where you go, no matter what you do, you cannot escape diff --git a/code/modules/mob/living/carbon/brain/death.dm b/code/modules/mob/living/carbon/brain/death.dm index c36a553aff4e..8dd0db0b407f 100644 --- a/code/modules/mob/living/carbon/brain/death.dm +++ b/code/modules/mob/living/carbon/brain/death.dm @@ -1,27 +1,27 @@ -/mob/living/carbon/brain/death(gibbed) - // Only execute the below if we successfully died - . = ..() - if(!.) - return FALSE - if(!gibbed && container && istype(container, /obj/item/mmi))//If not gibbed but in a container. - var/obj/item/mmi/mmi = container - visible_message("[src]'s MMI flatlines!", "You hear something flatline.") - mmi.icon_state = mmi.dead_icon - -/mob/living/carbon/brain/gib() - // can we muster a parent call here? - if(!death(TRUE) && stat != DEAD) - return FALSE - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - gibs(loc, dna) - - if(container && istype(container, /obj/item/mmi)) - qdel(container)//Gets rid of the MMI if there is one - if(loc) - if(istype(loc,/obj/item/organ/internal/brain)) - qdel(loc)//Gets rid of the brain item - QDEL_IN(src, 0) +/mob/living/carbon/brain/death(gibbed) + // Only execute the below if we successfully died + . = ..() + if(!.) + return FALSE + if(!gibbed && container && istype(container, /obj/item/mmi))//If not gibbed but in a container. + var/obj/item/mmi/mmi = container + visible_message("[src]'s MMI flatlines!", "You hear something flatline.") + mmi.icon_state = mmi.dead_icon + +/mob/living/carbon/brain/gib() + // can we muster a parent call here? + if(!death(TRUE) && stat != DEAD) + return FALSE + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + gibs(loc, dna) + + if(container && istype(container, /obj/item/mmi)) + qdel(container)//Gets rid of the MMI if there is one + if(loc) + if(istype(loc,/obj/item/organ/internal/brain)) + qdel(loc)//Gets rid of the brain item + QDEL_IN(src, 0) diff --git a/code/modules/mob/living/carbon/brain/emote.dm b/code/modules/mob/living/carbon/brain/emote.dm index dd1876e223fb..84298ee4f0fb 100644 --- a/code/modules/mob/living/carbon/brain/emote.dm +++ b/code/modules/mob/living/carbon/brain/emote.dm @@ -48,4 +48,4 @@ to_chat(src, "alarm, alert, notice, flash,blink, whistle, beep, boop") if(message && !stat) - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/brain/life.dm b/code/modules/mob/living/carbon/brain/life.dm index d0cf06f1bf71..6bd88e5e111c 100644 --- a/code/modules/mob/living/carbon/brain/life.dm +++ b/code/modules/mob/living/carbon/brain/life.dm @@ -1,40 +1,40 @@ -/mob/living/carbon/brain/handle_mutations_and_radiation() - if(radiation) - if(radiation > 100) - if(!container) - to_chat(src, "You feel weak.") - else - to_chat(src, "STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.") - - switch(radiation) - - if(50 to 75) - if(prob(5)) - if(!container) - to_chat(src, "You feel weak.") - else - to_chat(src, "STATUS: DANGEROUS AMOUNTS OF RADIATION DETECTED.") - ..() - -/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) - if(status_flags & GODMODE) - return - - if(exposed_temperature > bodytemperature) - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - adjustFireLoss(20.0*discomfort) - - else - var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) - adjustFireLoss(5.0*discomfort) - -/mob/living/carbon/brain/handle_regular_status_updates() - . = ..() - - if(.) - if(!container && (health < HEALTH_THRESHOLD_DEAD && check_death_method() || ((world.time - timeofhostdeath) > config.revival_brain_life))) - death() - return 0 - -/mob/living/carbon/brain/breathe() - return +/mob/living/carbon/brain/handle_mutations_and_radiation() + if(radiation) + if(radiation > 100) + if(!container) + to_chat(src, "You feel weak.") + else + to_chat(src, "STATUS: CRITICAL AMOUNTS OF RADIATION DETECTED.") + + switch(radiation) + + if(50 to 75) + if(prob(5)) + if(!container) + to_chat(src, "You feel weak.") + else + to_chat(src, "STATUS: DANGEROUS AMOUNTS OF RADIATION DETECTED.") + ..() + +/mob/living/carbon/brain/proc/handle_temperature_damage(body_part, exposed_temperature, exposed_intensity) + if(status_flags & GODMODE) + return + + if(exposed_temperature > bodytemperature) + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + adjustFireLoss(20.0*discomfort) + + else + var/discomfort = min( abs(exposed_temperature - bodytemperature)*(exposed_intensity)/2000000, 1.0) + adjustFireLoss(5.0*discomfort) + +/mob/living/carbon/brain/handle_regular_status_updates() + . = ..() + + if(.) + if(!container && (health < HEALTH_THRESHOLD_DEAD && check_death_method() || ((world.time - timeofhostdeath) > config.revival_brain_life))) + death() + return 0 + +/mob/living/carbon/brain/breathe() + return diff --git a/code/modules/mob/living/carbon/brain/login.dm b/code/modules/mob/living/carbon/brain/login.dm index 622ae74656a5..3e5f0e48365a 100644 --- a/code/modules/mob/living/carbon/brain/login.dm +++ b/code/modules/mob/living/carbon/brain/login.dm @@ -1,3 +1,3 @@ -/mob/living/carbon/brain/Login() - ..() - SetSleeping(0) +/mob/living/carbon/brain/Login() + ..() + SetSleeping(0) diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm index eb4750c9780b..2b992fc5ba09 100644 --- a/code/modules/mob/living/carbon/brain/say.dm +++ b/code/modules/mob/living/carbon/brain/say.dm @@ -1,50 +1,50 @@ -//TODO: Convert this over for languages. -/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null) - if(!can_speak(warning = TRUE)) - return - - if(prob(emp_damage * 4)) - if(prob(10)) //10% chance to drop the message entirely - return - else - message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher - - ..(message) - -/mob/living/carbon/brain/whisper(message as text) - if(!can_speak(warning = TRUE)) - return - - ..() - -/mob/living/carbon/brain/can_speak(var/warning = FALSE) - . = ..() - - if(!istype(container, /obj/item/mmi)) - . = FALSE - else if(istype(container, /obj/item/mmi/robotic_brain)) - var/obj/item/mmi/robotic_brain/R = container - if(R && R.silenced) - if(warning) - to_chat(usr, "You cannot speak, as your internal speaker is turned off.") - . = FALSE - -/mob/living/carbon/brain/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) - switch(message_mode) - if("headset") - var/radio_worked = 0 // If any of the radios our brainmob could use functioned, this is set true so that we don't use any others - // I'm doing it this way so that if the mecha radio fails for some reason, a radio MMI still has the built-in fallback - if(container && istype(container,/obj/item/mmi)) - var/obj/item/mmi/c = container - if(!radio_worked && c.mecha) - var/obj/mecha/metalgear = c.mecha - if(metalgear.radio) - radio_worked = metalgear.radio.talk_into(src, message_pieces, message_mode, verb) - - else if(!radio_worked && c.radio) - radio_worked = c.radio.talk_into(src, message_pieces, message_mode, verb) - return radio_worked - if("whisper") - whisper_say(message_pieces) - return 1 - else return 0 +//TODO: Convert this over for languages. +/mob/living/carbon/brain/say(var/message, var/datum/language/speaking = null) + if(!can_speak(warning = TRUE)) + return + + if(prob(emp_damage * 4)) + if(prob(10)) //10% chance to drop the message entirely + return + else + message = Gibberish(message, (emp_damage*6))//scrambles the message, gets worse when emp_damage is higher + + ..(message) + +/mob/living/carbon/brain/whisper(message as text) + if(!can_speak(warning = TRUE)) + return + + ..() + +/mob/living/carbon/brain/can_speak(var/warning = FALSE) + . = ..() + + if(!istype(container, /obj/item/mmi)) + . = FALSE + else if(istype(container, /obj/item/mmi/robotic_brain)) + var/obj/item/mmi/robotic_brain/R = container + if(R && R.silenced) + if(warning) + to_chat(usr, "You cannot speak, as your internal speaker is turned off.") + . = FALSE + +/mob/living/carbon/brain/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) + switch(message_mode) + if("headset") + var/radio_worked = 0 // If any of the radios our brainmob could use functioned, this is set true so that we don't use any others + // I'm doing it this way so that if the mecha radio fails for some reason, a radio MMI still has the built-in fallback + if(container && istype(container,/obj/item/mmi)) + var/obj/item/mmi/c = container + if(!radio_worked && c.mecha) + var/obj/mecha/metalgear = c.mecha + if(metalgear.radio) + radio_worked = metalgear.radio.talk_into(src, message_pieces, message_mode, verb) + + else if(!radio_worked && c.radio) + radio_worked = c.radio.talk_into(src, message_pieces, message_mode, verb) + return radio_worked + if("whisper") + whisper_say(message_pieces) + return 1 + else return 0 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 7347e8eb3e92..980a9b72c52d 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -126,7 +126,7 @@ if(isturf(loc)) I.remove(src) I.forceMove(get_turf(src)) - I.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),5) + I.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),5) for(var/mob/M in src) if(M in src.stomach_contents) @@ -388,7 +388,7 @@ dna = newDNA -var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, /obj/machinery/atmospherics/unary/vent_scrubber) +GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/vent_pump, /obj/machinery/atmospherics/unary/vent_scrubber)) /mob/living/handle_ventcrawl(var/atom/clicked_on) // -- TLE -- Merged by Carn if(!Adjacent(clicked_on)) @@ -432,7 +432,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, if(!vent_found) for(var/obj/machinery/atmospherics/machine in range(1,src)) - if(is_type_in_list(machine, ventcrawl_machinery)) + if(is_type_in_list(machine, GLOB.ventcrawl_machinery)) vent_found = machine if(!vent_found.can_crawl_through()) @@ -1016,7 +1016,7 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, if(!forceFed(toEat, user, fullness)) return 0 consume(toEat, bitesize_override, can_taste_container = toEat.can_taste) - score_foodeaten++ + GLOB.score_foodeaten++ return 1 /mob/living/carbon/proc/selfFeed(var/obj/item/reagent_containers/food/toEat, fullness) @@ -1103,7 +1103,7 @@ so that different stomachs can handle things in different ways VB*/ //to recalculate and update the mob's total tint from tinted equipment it's wearing. /mob/living/carbon/proc/update_tint() - if(!tinted_weldhelh) + if(!GLOB.tinted_weldhelh) return var/tinttotal = get_total_tint() if(tinttotal >= TINT_BLIND) @@ -1192,4 +1192,4 @@ so that different stomachs can handle things in different ways VB*/ var/obj/item/I = X I.acid_level = 0 //washes off the acid on our clothes I.extinguish() //extinguishes our clothes - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/carbon_defines.dm b/code/modules/mob/living/carbon/carbon_defines.dm index f8e9f7e9aff6..884ee8025186 100644 --- a/code/modules/mob/living/carbon/carbon_defines.dm +++ b/code/modules/mob/living/carbon/carbon_defines.dm @@ -1,30 +1,30 @@ -/mob/living/carbon - gender = MALE - pressure_resistance = 15 - var/list/stomach_contents = list() - var/list/internal_organs = list() - var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access. - var/antibodies = 0 - - var/life_tick = 0 // The amount of life ticks that have processed on this mob. - - var/obj/item/handcuffed = null //Whether or not the mob is handcuffed - var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. - - var/obj/item/head = null - var/obj/item/clothing/suit/wear_suit = null //TODO: necessary? Are they even used? ~Carn - - var/mob/living/simple_animal/borer/borer = null - - //Active emote/pose - var/pose = null - - var/pulse = PULSE_NORM //current pulse level - - var/wetlevel = 0 //how wet the mob is - - var/co2overloadtime = null - var/dreaming = 0 //How many dream images we have left to send - var/nightmare = 0 - - blood_volume = BLOOD_VOLUME_NORMAL \ No newline at end of file +/mob/living/carbon + gender = MALE + pressure_resistance = 15 + var/list/stomach_contents = list() + var/list/internal_organs = list() + var/list/internal_organs_slot = list() //Same as above, but stores "slot ID" - "organ" pairs for easy access. + var/antibodies = 0 + + var/life_tick = 0 // The amount of life ticks that have processed on this mob. + + var/obj/item/handcuffed = null //Whether or not the mob is handcuffed + var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this. + + var/obj/item/head = null + var/obj/item/clothing/suit/wear_suit = null //TODO: necessary? Are they even used? ~Carn + + var/mob/living/simple_animal/borer/borer = null + + //Active emote/pose + var/pose = null + + var/pulse = PULSE_NORM //current pulse level + + var/wetlevel = 0 //how wet the mob is + + var/co2overloadtime = null + var/dreaming = 0 //How many dream images we have left to send + var/nightmare = 0 + + blood_volume = BLOOD_VOLUME_NORMAL diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index a0fd10e06100..bc4b5b43b1cc 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -1,4 +1,4 @@ -/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, var/location = src, var/mob/user = src, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list(), var/datum/topic_state/state = default_state) +/mob/living/carbon/human/proc/change_appearance(var/flags = APPEARANCE_ALL_HAIR, var/location = src, var/mob/user = src, var/check_species_whitelist = 1, var/list/species_whitelist = list(), var/list/species_blacklist = list(), var/datum/topic_state/state = GLOB.default_state) var/datum/nano_module/appearance_changer/AC = new(location, src, check_species_whitelist, species_whitelist, species_blacklist) AC.flags = flags AC.ui_interact(user, state = state) @@ -104,9 +104,9 @@ if(!body_accessory_style || (body_accessory && body_accessory.name == body_accessory_style)) return - for(var/B in body_accessory_by_name) + for(var/B in GLOB.body_accessory_by_name) if(B == body_accessory_style) - body_accessory = body_accessory_by_name[body_accessory_style] + body_accessory = GLOB.body_accessory_by_name[body_accessory_style] found = 1 if(!found) @@ -340,7 +340,7 @@ if((H.gender == MALE && S.gender == FEMALE) || (H.gender == FEMALE && S.gender == MALE)) continue if(H.dna.species.bodyflags & ALL_RPARTS) //If the user is a species who can have a robotic head... - var/datum/robolimb/robohead = all_robolimbs[H.model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model] if((H.dna.species.name in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. valid_hairstyles += hairstyle //Give them their hairstyles if they do. else @@ -368,7 +368,7 @@ if((H.gender == MALE && S.gender == FEMALE) || (H.gender == FEMALE && S.gender == MALE)) continue if(H.dna.species.bodyflags & ALL_RPARTS) //If the user is a species who can have a robotic head... - var/datum/robolimb/robohead = all_robolimbs[H.model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model] if(H.dna.species.name in S.species_allowed) //If this is a facial hair style native to the user's species... if((H.dna.species.name in S.species_allowed) && robohead.is_monitor && ((S.models_allowed && (robohead.company in S.models_allowed)) || !S.models_allowed)) //If this is a facial hair style native to the user's species, check to see if they have a head with an ipc-style screen and that the head's company is in the screen style's allowed models list. valid_facial_hairstyles += facialhairstyle //Give them their facial hairstyles if they do. @@ -422,7 +422,7 @@ if(location == "head") var/datum/sprite_accessory/body_markings/head/M = GLOB.marking_styles_list[S.name] if(H.dna.species.bodyflags & ALL_RPARTS) //If the user is a species that can have a robotic head... - var/datum/robolimb/robohead = all_robolimbs[H.model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model] if(!(S.models_allowed && (robohead.company in S.models_allowed))) //Make sure they don't get markings incompatible with their head. continue else if(H.alt_head && H.alt_head != "None") //If the user's got an alt head, validate markings for that head. @@ -437,10 +437,10 @@ /mob/living/carbon/human/proc/generate_valid_body_accessories() var/list/valid_body_accessories = new() - for(var/B in body_accessory_by_name) - var/datum/body_accessory/A = body_accessory_by_name[B] + for(var/B in GLOB.body_accessory_by_name) + var/datum/body_accessory/A = GLOB.body_accessory_by_name[B] if(check_rights(R_ADMIN, 0, src)) - valid_body_accessories = body_accessory_by_name.Copy() + valid_body_accessories = GLOB.body_accessory_by_name.Copy() else if(!istype(A)) valid_body_accessories["None"] = "None" //The only null entry should be the "None" option. diff --git a/code/modules/mob/living/carbon/human/body_accessories.dm b/code/modules/mob/living/carbon/human/body_accessories.dm index 9c677444eb52..e214f6068bba 100644 --- a/code/modules/mob/living/carbon/human/body_accessories.dm +++ b/code/modules/mob/living/carbon/human/body_accessories.dm @@ -1,29 +1,29 @@ -var/global/list/body_accessory_by_name = list("None" = null) +GLOBAL_LIST_INIT(body_accessory_by_name, list("None" = null)) /hook/startup/proc/initalize_body_accessories() __init_body_accessory(/datum/body_accessory/body) __init_body_accessory(/datum/body_accessory/tail) - if(body_accessory_by_name.len) + if(GLOB.body_accessory_by_name.len) if(initialize_body_accessory_by_species()) return TRUE return FALSE //fail if no bodies are found -var/global/list/body_accessory_by_species = list("None" = null) +GLOBAL_LIST_INIT(body_accessory_by_species, list("None" = null)) /proc/initialize_body_accessory_by_species() - for(var/B in body_accessory_by_name) - var/datum/body_accessory/accessory = body_accessory_by_name[B] + for(var/B in GLOB.body_accessory_by_name) + var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[B] if(!istype(accessory)) continue for(var/species in accessory.allowed_species) - if(!body_accessory_by_species["[species]"]) body_accessory_by_species["[species]"] = list() - body_accessory_by_species["[species]"] += accessory + if(!GLOB.body_accessory_by_species["[species]"]) GLOB.body_accessory_by_species["[species]"] = list() + GLOB.body_accessory_by_species["[species]"] += accessory - if(body_accessory_by_species.len) + if(GLOB.body_accessory_by_species.len) return TRUE return FALSE @@ -34,7 +34,7 @@ var/global/list/body_accessory_by_species = list("None" = null) for(var/A in subtypesof(ba_path)) var/datum/body_accessory/B = new A if(istype(B)) - body_accessory_by_name[B.name] += B + GLOB.body_accessory_by_name[B.name] += B ++_added_counter if(_added_counter) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index d95badbbb017..aa8643173f47 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -1,195 +1,195 @@ -/mob/living/carbon/human/gib() - if(!death(TRUE) && stat != DEAD) - return FALSE - var/atom/movable/overlay/animation = null - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - if(!isSynthetic()) - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - - playsound(src.loc, 'sound/goonstation/effects/gib.ogg', 50, 1) - else - playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) - - for(var/obj/item/organ/internal/I in internal_organs) - if(isturf(loc)) - var/atom/movable/thing = I.remove(src) - if(thing) - thing.forceMove(get_turf(src)) - thing.throw_at(get_edge_target_turf(src, pick(alldirs)), rand(1,3), 5) - - for(var/obj/item/organ/external/E in bodyparts) - if(istype(E, /obj/item/organ/external/chest)) - continue - // Only make the limb drop if it's not too damaged - if(prob(100 - E.get_damage())) - // Override the current limb status and don't cause an explosion - E.droplimb(DROPLIMB_SHARP) - - for(var/mob/M in src) - if(M in stomach_contents) - stomach_contents.Remove(M) - M.forceMove(get_turf(src)) - visible_message("[M] bursts out of [src]!") - - if(!isSynthetic()) - flick("gibbed-h", animation) - hgibs(loc, dna) - else - new /obj/effect/decal/cleanable/blood/gibs/robot(loc) - do_sparks(3, 1, src) - QDEL_IN(animation, 15) - QDEL_IN(src, 0) - return TRUE - -/mob/living/carbon/human/dust() - if(!death(TRUE) && stat != DEAD) - return FALSE - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - dust_animation() - QDEL_IN(src, 15) - return TRUE - -/mob/living/carbon/human/dust_animation() - var/atom/movable/overlay/animation = null - - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - - flick("dust-h", animation) - new dna.species.remains_type(get_turf(src)) - QDEL_IN(animation, 15) - return TRUE - -/mob/living/carbon/human/melt() - if(!death(TRUE) && stat != DEAD) - return FALSE - var/atom/movable/overlay/animation = null - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - - flick("liquify", animation) - QDEL_IN(src, 0) - QDEL_IN(animation, 15) - //new /obj/effect/decal/remains/human(loc) - return TRUE - -/mob/living/carbon/human/death(gibbed) - if(can_die() && !gibbed && deathgasp_on_death) - emote("deathgasp", force = TRUE) //let the world KNOW WE ARE DEAD - - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - - set_heartattack(FALSE) - SSmobs.cubemonkeys -= src - if(dna.species) - dna.species.handle_hud_icons(src) - //Handle species-specific deaths. - dna.species.handle_death(gibbed, src) - - if(ishuman(LAssailant)) - var/mob/living/carbon/human/H=LAssailant - if(H.mind) - H.mind.kills += "[key_name(src)]" - - if(SSticker && SSticker.mode) -// log_world("k") - sql_report_death(src) - - if(wearing_rig) - wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") - -/mob/living/carbon/human/update_revive() - . = ..() - if(. && healthdoll) - // We're alive again, so re-build the entire healthdoll - healthdoll.cached_healthdoll_overlays.Cut() - // Update healthdoll - if(dna.species) - dna.species.update_sight(src) - dna.species.handle_hud_icons(src) - -/mob/living/carbon/human/proc/makeSkeleton() - var/obj/item/organ/external/head/H = get_organ("head") - if(SKELETON in src.mutations) - return - - if(istype(H)) - H.disfigured = TRUE - if(H.f_style) - H.f_style = initial(H.f_style) - if(H.h_style) - H.h_style = initial(H.h_style) - if(H.ha_style) - H.ha_style = initial(H.ha_style) - if(H.alt_head) - H.alt_head = initial(H.alt_head) - H.handle_alt_icon() - m_styles = DEFAULT_MARKING_STYLES - update_fhair(0) - update_hair(0) - update_head_accessory(0) - update_markings(0) - - mutations.Add(SKELETON) - mutations.Add(NOCLONE) - update_body(0) - update_mutantrace() - return - -/mob/living/carbon/human/proc/ChangeToHusk() - - // If the target has no DNA to begin with, its DNA can't be damaged beyond repair. - if(NO_DNA in dna.species.species_traits) - return - if(HUSK in mutations) - return - - var/obj/item/organ/external/head/H = bodyparts_by_name["head"] - if(istype(H)) - H.disfigured = TRUE //makes them unknown without fucking up other stuff like admintools - if(H.f_style) - H.f_style = "Shaved" //we only change the icon_state of the hair datum, so it doesn't mess up their UI/UE - if(H.h_style) - H.h_style = "Bald" - update_fhair(0) - update_hair(0) - - mutations.Add(HUSK) - update_body(0) - update_mutantrace() - return - -/mob/living/carbon/human/proc/Drain() - ChangeToHusk() - mutations |= NOCLONE - return - -/mob/living/carbon/human/proc/cure_husk() - mutations.Remove(HUSK) - var/obj/item/organ/external/head/H = bodyparts_by_name["head"] - if(istype(H)) - H.disfigured = FALSE - update_body(0) - update_mutantrace(0) - UpdateAppearance() // reset hair from DNA +/mob/living/carbon/human/gib() + if(!death(TRUE) && stat != DEAD) + return FALSE + var/atom/movable/overlay/animation = null + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + if(!isSynthetic()) + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + + playsound(src.loc, 'sound/goonstation/effects/gib.ogg', 50, 1) + else + playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) + + for(var/obj/item/organ/internal/I in internal_organs) + if(isturf(loc)) + var/atom/movable/thing = I.remove(src) + if(thing) + thing.forceMove(get_turf(src)) + thing.throw_at(get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 5) + + for(var/obj/item/organ/external/E in bodyparts) + if(istype(E, /obj/item/organ/external/chest)) + continue + // Only make the limb drop if it's not too damaged + if(prob(100 - E.get_damage())) + // Override the current limb status and don't cause an explosion + E.droplimb(DROPLIMB_SHARP) + + for(var/mob/M in src) + if(M in stomach_contents) + stomach_contents.Remove(M) + M.forceMove(get_turf(src)) + visible_message("[M] bursts out of [src]!") + + if(!isSynthetic()) + flick("gibbed-h", animation) + hgibs(loc, dna) + else + new /obj/effect/decal/cleanable/blood/gibs/robot(loc) + do_sparks(3, 1, src) + QDEL_IN(animation, 15) + QDEL_IN(src, 0) + return TRUE + +/mob/living/carbon/human/dust() + if(!death(TRUE) && stat != DEAD) + return FALSE + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + dust_animation() + QDEL_IN(src, 15) + return TRUE + +/mob/living/carbon/human/dust_animation() + var/atom/movable/overlay/animation = null + + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + + flick("dust-h", animation) + new dna.species.remains_type(get_turf(src)) + QDEL_IN(animation, 15) + return TRUE + +/mob/living/carbon/human/melt() + if(!death(TRUE) && stat != DEAD) + return FALSE + var/atom/movable/overlay/animation = null + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + + flick("liquify", animation) + QDEL_IN(src, 0) + QDEL_IN(animation, 15) + //new /obj/effect/decal/remains/human(loc) + return TRUE + +/mob/living/carbon/human/death(gibbed) + if(can_die() && !gibbed && deathgasp_on_death) + emote("deathgasp", force = TRUE) //let the world KNOW WE ARE DEAD + + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + + set_heartattack(FALSE) + SSmobs.cubemonkeys -= src + if(dna.species) + dna.species.handle_hud_icons(src) + //Handle species-specific deaths. + dna.species.handle_death(gibbed, src) + + if(ishuman(LAssailant)) + var/mob/living/carbon/human/H=LAssailant + if(H.mind) + H.mind.kills += "[key_name(src)]" + + if(SSticker && SSticker.mode) +// log_world("k") + sql_report_death(src) + + if(wearing_rig) + wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") + +/mob/living/carbon/human/update_revive() + . = ..() + if(. && healthdoll) + // We're alive again, so re-build the entire healthdoll + healthdoll.cached_healthdoll_overlays.Cut() + // Update healthdoll + if(dna.species) + dna.species.update_sight(src) + dna.species.handle_hud_icons(src) + +/mob/living/carbon/human/proc/makeSkeleton() + var/obj/item/organ/external/head/H = get_organ("head") + if(SKELETON in src.mutations) + return + + if(istype(H)) + H.disfigured = TRUE + if(H.f_style) + H.f_style = initial(H.f_style) + if(H.h_style) + H.h_style = initial(H.h_style) + if(H.ha_style) + H.ha_style = initial(H.ha_style) + if(H.alt_head) + H.alt_head = initial(H.alt_head) + H.handle_alt_icon() + m_styles = DEFAULT_MARKING_STYLES + update_fhair(0) + update_hair(0) + update_head_accessory(0) + update_markings(0) + + mutations.Add(SKELETON) + mutations.Add(NOCLONE) + update_body(0) + update_mutantrace() + return + +/mob/living/carbon/human/proc/ChangeToHusk() + + // If the target has no DNA to begin with, its DNA can't be damaged beyond repair. + if(NO_DNA in dna.species.species_traits) + return + if(HUSK in mutations) + return + + var/obj/item/organ/external/head/H = bodyparts_by_name["head"] + if(istype(H)) + H.disfigured = TRUE //makes them unknown without fucking up other stuff like admintools + if(H.f_style) + H.f_style = "Shaved" //we only change the icon_state of the hair datum, so it doesn't mess up their UI/UE + if(H.h_style) + H.h_style = "Bald" + update_fhair(0) + update_hair(0) + + mutations.Add(HUSK) + update_body(0) + update_mutantrace() + return + +/mob/living/carbon/human/proc/Drain() + ChangeToHusk() + mutations |= NOCLONE + return + +/mob/living/carbon/human/proc/cure_husk() + mutations.Remove(HUSK) + var/obj/item/organ/external/head/H = bodyparts_by_name["head"] + if(istype(H)) + H.disfigured = FALSE + update_body(0) + update_mutantrace(0) + UpdateAppearance() // reset hair from DNA diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 51088f664579..1bb1a8cf536c 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -141,76 +141,76 @@ if("howl", "howls") var/M = handle_emote_param(param) //Check to see if the param is valid (mob with the param name is in view). message = "[src] howls[M ? " at [M]" : ""]!" - playsound(loc, 'sound/goonstation/voice/howl.ogg', 100, 0, 10) + playsound(loc, 'sound/goonstation/voice/howl.ogg', 100, 1, 10, frequency = get_age_pitch()) m_type = 2 if("growl", "growls") var/M = handle_emote_param(param) message = "[src] growls[M ? " at [M]" : ""]." - playsound(loc, "growls", 80, 0) + playsound(loc, "growls", 80, 1, frequency = get_age_pitch()) m_type = 2 if("ping", "pings") var/M = handle_emote_param(param) message = "[src] pings[M ? " at [M]" : ""]." - playsound(loc, 'sound/machines/ping.ogg', 50, 0) + playsound(loc, 'sound/machines/ping.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("buzz2") var/M = handle_emote_param(param) message = "[src] emits an irritated buzzing sound[M ? " at [M]" : ""]." - playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) + playsound(loc, 'sound/machines/buzz-two.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("buzz", "buzzes") var/M = handle_emote_param(param) message = "[src] buzzes[M ? " at [M]" : ""]." - playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("beep", "beeps") var/M = handle_emote_param(param) message = "[src] beeps[M ? " at [M]" : ""]." - playsound(loc, 'sound/machines/twobeep.ogg', 50, 0) + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("drone", "drones", "hum", "hums", "rumble", "rumbles") var/M = handle_emote_param(param) message = "[src] [M ? "drones at [M]" : "rumbles"]." - playsound(loc, 'sound/voice/drasktalk.ogg', 50, 0) + playsound(loc, 'sound/voice/drasktalk.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("squish", "squishes") var/M = handle_emote_param(param) message = "[src] squishes[M ? " at [M]" : ""]." - playsound(loc, 'sound/effects/slime_squish.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound. + playsound(loc, 'sound/effects/slime_squish.ogg', 50, 1, frequency = get_age_pitch()) //Credit to DrMinky (freesound.org) for the sound. m_type = 2 if("clack", "clacks") var/M = handle_emote_param(param) message = "[src] clacks [p_their()] mandibles[M ? " at [M]" : ""]." - playsound(loc, 'sound/effects/Kidanclack.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound. + playsound(loc, 'sound/effects/Kidanclack.ogg', 50, 1, frequency = get_age_pitch()) //Credit to DrMinky (freesound.org) for the sound. m_type = 2 if("click", "clicks") var/M = handle_emote_param(param) message = "[src] clicks [p_their()] mandibles[M ? " at [M]" : ""]." - playsound(loc, 'sound/effects/Kidanclack2.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound. + playsound(loc, 'sound/effects/Kidanclack2.ogg', 50, 1, frequency = get_age_pitch()) //Credit to DrMinky (freesound.org) for the sound. m_type = 2 if("creaks", "creak") var/M = handle_emote_param(param) message = "[src] creaks[M ? " at [M]" : ""]." - playsound(loc, 'sound/voice/dionatalk1.ogg', 50, 0) //Credit https://www.youtube.com/watch?v=ufnvlRjsOTI [0:13 - 0:16] + playsound(loc, 'sound/voice/dionatalk1.ogg', 50, 1, frequency = get_age_pitch()) //Credit https://www.youtube.com/watch?v=ufnvlRjsOTI [0:13 - 0:16] m_type = 2 if("hiss", "hisses") @@ -218,7 +218,7 @@ if(!muzzled) message = "[src] hisses[M ? " at [M]" : ""]." - playsound(loc, 'sound/effects/unathihiss.ogg', 50, 0) //Credit to Jamius (freesound.org) for the sound. + playsound(loc, 'sound/effects/unathihiss.ogg', 50, 1, frequency = get_age_pitch()) //Credit to Jamius (freesound.org) for the sound. m_type = 2 else message = "[src] makes a weak hissing noise." @@ -228,28 +228,28 @@ var/M = handle_emote_param(param) message = "[src] rustles [p_their()] quills[M ? " at [M]" : ""]." - playsound(loc, 'sound/effects/voxrustle.ogg', 50, 0) //Credit to sound-ideas (freesfx.co.uk) for the sound. + playsound(loc, 'sound/effects/voxrustle.ogg', 50, 1, frequency = get_age_pitch()) //Credit to sound-ideas (freesfx.co.uk) for the sound. m_type = 2 if("warble", "warbles") var/M = handle_emote_param(param) message = "[src] warbles[M ? " at [M]" : ""]." - playsound(loc, 'sound/effects/warble.ogg', 50, 0) // Copyright CC BY 3.0 alienistcog (freesound.org) for the sound. + playsound(loc, 'sound/effects/warble.ogg', 50, 1, frequency = get_age_pitch()) // Copyright CC BY 3.0 alienistcog (freesound.org) for the sound. m_type = 2 if("yes") var/M = handle_emote_param(param) message = "[src] emits an affirmative blip[M ? " at [M]" : ""]." - playsound(loc, 'sound/machines/synth_yes.ogg', 50, 0) + playsound(loc, 'sound/machines/synth_yes.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("no") var/M = handle_emote_param(param) message = "[src] emits a negative blip[M ? " at [M]" : ""]." - playsound(loc, 'sound/machines/synth_no.ogg', 50, 0) + playsound(loc, 'sound/machines/synth_no.ogg', 50, 1, frequency = get_age_pitch()) m_type = 2 if("wag", "wags") @@ -465,10 +465,10 @@ m_type = 2 if(gender == FEMALE) if(dna.species.female_cough_sounds) - playsound(src, pick(dna.species.female_cough_sounds), 120) + playsound(src, pick(dna.species.female_cough_sounds), 120, 1, frequency = get_age_pitch()) else if(dna.species.male_cough_sounds) - playsound(src, pick(dna.species.male_cough_sounds), 120) + playsound(src, pick(dna.species.male_cough_sounds), 120, 1, frequency = get_age_pitch()) else message = "[src] makes a strong noise." m_type = 2 @@ -734,9 +734,9 @@ if(!muzzled) message = "[src] sneezes." if(gender == FEMALE) - playsound(src, dna.species.female_sneeze_sound, 70) + playsound(src, dna.species.female_sneeze_sound, 70, 1, frequency = get_age_pitch()) else - playsound(src, dna.species.male_sneeze_sound, 70) + playsound(src, dna.species.male_sneeze_sound, 70, 1, frequency = get_age_pitch()) m_type = 2 else message = "[src] makes a strange noise." diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index 85bb90a168e1..d8b948fc865b 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -1,437 +1,434 @@ -/mob/living/carbon/human/examine(mob/user) - var/skipgloves = 0 - var/skipsuitstorage = 0 - var/skipjumpsuit = 0 - var/skipshoes = 0 - var/skipmask = 0 - var/skipears = 0 - var/skipeyes = 0 - var/skipface = 0 - - //exosuits and helmets obscure our view and stuff. - if(wear_suit) - skipgloves = wear_suit.flags_inv & HIDEGLOVES - skipsuitstorage = wear_suit.flags_inv & HIDESUITSTORAGE - skipjumpsuit = wear_suit.flags_inv & HIDEJUMPSUIT - skipshoes = wear_suit.flags_inv & HIDESHOES - - if(head) - skipmask = head.flags_inv & HIDEMASK - skipeyes = head.flags_inv & HIDEEYES - skipears = head.flags_inv & HIDEEARS - skipface = head.flags_inv & HIDEFACE - - if(wear_mask) - skipface |= wear_mask.flags_inv & HIDEFACE - var/msg = "*---------*\nThis is " - - if(!(skipjumpsuit && skipface) && icon) //big suits/masks/helmets make it hard to tell their gender - msg += "[bicon(icon(icon, dir=SOUTH))] " //fucking BYOND: this should stop dreamseeker crashing if we -somehow- examine somebody before their icon is generated - msg += "[name]" - - var/displayed_species = dna.species.name - var/examine_color = dna.species.flesh_color - for(var/obj/item/clothing/C in src) //Disguise checks - if(C == src.head || C == src.wear_suit || C == src.wear_mask || C == src.w_uniform || C == src.belt || C == src.back) - if(C.species_disguise) - displayed_species = C.species_disguise - if(skipjumpsuit && skipface || (NO_EXAMINE in dna.species.species_traits)) //either obscured or on the nospecies list - msg += "!\n" //omit the species when examining - else if(displayed_species == "Slime People") //snowflakey because Slime People are defined as a plural - msg += ", a slime person!\n" - else if(displayed_species == "Unathi") //DAMN YOU, VOWELS - msg += ", a unathi!\n" - else - msg += ", a [lowertext(displayed_species)]!\n" - - //uniform - if(w_uniform && !skipjumpsuit && !(w_uniform.flags & ABSTRACT)) - //Ties - var/tie_msg - if(istype(w_uniform,/obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - if(U.accessories.len) - tie_msg += " with [english_accessory_list(U)]" - - if(w_uniform.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [w_uniform.blood_color != "#030303" ? "blood-stained":"oil-stained"] [w_uniform.name][tie_msg]!\n" - else - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(w_uniform)] \a [w_uniform][tie_msg].\n" - - //head - if(head && !(head.flags & ABSTRACT)) - if(head.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(head)] [head.gender==PLURAL?"some":"a"] [head.blood_color != "#030303" ? "blood-stained":"oil-stained"] [head.name] on [p_their()] head!\n" - else - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(head)] \a [head] on [p_their()] head.\n" - - //suit/armour - if(wear_suit && !(wear_suit.flags & ABSTRACT)) - if(wear_suit.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [wear_suit.blood_color != "#030303" ? "blood-stained":"oil-stained"] [wear_suit.name]!\n" - else - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(wear_suit)] \a [wear_suit].\n" - - //suit/armour storage - if(s_store && !skipsuitstorage) - if(s_store.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] carrying [bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [s_store.blood_color != "#030303" ? "blood-stained":"oil-stained"] [s_store.name] on [p_their()] [wear_suit.name]!\n" - else - msg += "[p_they(TRUE)] [p_are()] carrying [bicon(s_store)] \a [s_store] on [p_their()] [wear_suit.name].\n" - - //back - if(back && !(back.flags & ABSTRACT)) - if(back.blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [bicon(back)] [back.gender==PLURAL?"some":"a"] [back.blood_color != "#030303" ? "blood-stained":"oil-stained"] [back] on [p_their()] back.\n" - else - msg += "[p_they(TRUE)] [p_have()] [bicon(back)] \a [back] on [p_their()] back.\n" - - //left hand - if(l_hand && !(l_hand.flags & ABSTRACT)) - if(l_hand.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] holding [bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [l_hand.blood_color != "#030303" ? "blood-stained":"oil-stained"] [l_hand.name] in [p_their()] left hand!\n" - else - msg += "[p_they(TRUE)] [p_are()] holding [bicon(l_hand)] \a [l_hand] in [p_their()] left hand.\n" - - //right hand - if(r_hand && !(r_hand.flags & ABSTRACT)) - if(r_hand.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] holding [bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [r_hand.blood_color != "#030303" ? "blood-stained":"oil-stained"] [r_hand.name] in [p_their()] right hand!\n" - else - msg += "[p_they(TRUE)] [p_are()] holding [bicon(r_hand)] \a [r_hand] in [p_their()] right hand.\n" - - //gloves - if(gloves && !skipgloves && !(gloves.flags & ABSTRACT)) - if(gloves.blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [gloves.blood_color != "#030303" ? "blood-stained":"oil-stained"] [gloves.name] on [p_their()] hands!\n" - else - msg += "[p_they(TRUE)] [p_have()] [bicon(gloves)] \a [gloves] on [p_their()] hands.\n" - else if(blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [hand_blood_color != "#030303" ? "blood-stained":"oil-stained"] hands!\n" - - //handcuffed? - if(handcuffed) - if(istype(handcuffed, /obj/item/restraints/handcuffs/cable/zipties)) - msg += "[p_they(TRUE)] [p_are()] [bicon(handcuffed)] restrained with zipties!\n" - else if(istype(handcuffed, /obj/item/restraints/handcuffs/cable)) - msg += "[p_they(TRUE)] [p_are()] [bicon(handcuffed)] restrained with cable!\n" - else - msg += "[p_they(TRUE)] [p_are()] [bicon(handcuffed)] handcuffed!\n" - - //belt - if(belt) - if(belt.blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [bicon(belt)] [belt.gender==PLURAL?"some":"a"] [belt.blood_color != "#030303" ? "blood-stained":"oil-stained"] [belt.name] about [p_their()] waist!\n" - else - msg += "[p_they(TRUE)] [p_have()] [bicon(belt)] \a [belt] about [p_their()] waist.\n" - - //shoes - if(shoes && !skipshoes && !(shoes.flags & ABSTRACT)) - if(shoes.blood_DNA) - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [shoes.blood_color != "#030303" ? "blood-stained":"oil-stained"] [shoes.name] on [p_their()] feet!\n" - else - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(shoes)] \a [shoes] on [p_their()] feet.\n" - else if(blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [feet_blood_color != "#030303" ? "blood-stained":"oil-stained"] feet!\n" - - - //mask - if(wear_mask && !skipmask && !(wear_mask.flags & ABSTRACT)) - if(wear_mask.blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [wear_mask.blood_color != "#030303" ? "blood-stained":"oil-stained"] [wear_mask.name] on [p_their()] face!\n" - else - msg += "[p_they(TRUE)] [p_have()] [bicon(wear_mask)] \a [wear_mask] on [p_their()] face.\n" - - //eyes - if(glasses && !skipeyes && !(glasses.flags & ABSTRACT)) - if(glasses.blood_DNA) - msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [glasses.blood_color != "#030303" ? "blood-stained":"oil-stained"] [glasses] covering [p_their()] eyes!\n" - else - msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] \a [glasses] covering [p_their()] eyes.\n" - - //left ear - if(l_ear && !skipears) - msg += "[p_they(TRUE)] [p_have()] [bicon(l_ear)] \a [l_ear] on [p_their()] left ear.\n" - - //right ear - if(r_ear && !skipears) - msg += "[p_they(TRUE)] [p_have()] [bicon(r_ear)] \a [r_ear] on [p_their()] right ear.\n" - - //ID - if(wear_id) - msg += "[p_they(TRUE)] [p_are()] wearing [bicon(wear_id)] \a [wear_id].\n" - - //Jitters - switch(jitteriness) - if(300 to INFINITY) - msg += "[p_they(TRUE)] [p_are()] convulsing violently!\n" - if(200 to 300) - msg += "[p_they(TRUE)] [p_are()] extremely jittery.\n" - if(100 to 200) - msg += "[p_they(TRUE)] [p_are()] twitching ever so slightly.\n" - - - var/appears_dead = FALSE - if(stat == DEAD || (status_flags & FAKEDEATH)) - appears_dead = TRUE - if(suiciding) - msg += "[p_they(TRUE)] appear[p_s()] to have committed suicide... there is no hope of recovery.\n" - msg += "[p_they(TRUE)] [p_are()] limp and unresponsive; there are no signs of life" - if(get_int_organ(/obj/item/organ/internal/brain)) - if(!key) - var/foundghost = FALSE - if(mind) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.mind == mind) - foundghost = TRUE - if(G.can_reenter_corpse == 0) - foundghost = FALSE - break - if(!foundghost) - msg += " and [p_their()] soul has departed" - msg += "...\n" - - if(!get_int_organ(/obj/item/organ/internal/brain)) - msg += "It appears that [p_their()] brain is missing...\n" - - msg += "" - - var/list/wound_flavor_text = list() - var/list/is_destroyed = list() - for(var/organ_tag in dna.species.has_limbs) - - var/list/organ_data = dna.species.has_limbs[organ_tag] - var/organ_descriptor = organ_data["descriptor"] - is_destroyed["[organ_data["descriptor"]]"] = 1 - - var/obj/item/organ/external/E = bodyparts_by_name[organ_tag] - if(!E) - wound_flavor_text["[organ_tag]"] = "[p_they(TRUE)] [p_are()] missing [p_their()] [organ_descriptor].\n" - else - if(!isSynthetic()) - if(E.is_robotic()) - wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a robotic [E.name]!\n" - - else if(E.status & ORGAN_SPLINTED) - wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a splint on [p_their()] [E.name]!\n" - - if(E.open) - if(E.is_robotic()) - msg += "The maintenance hatch on [p_their()] [ignore_limb_branding(E.limb_name)] is open!\n" - else - msg += "[p_their(TRUE)] [ignore_limb_branding(E.limb_name)] has an open incision!\n" - - for(var/obj/item/I in E.embedded_objects) - msg += "[p_they(TRUE)] [p_have()] \a [bicon(I)] [I] embedded in [p_their()] [E.name]!\n" - - //Handles the text strings being added to the actual description. - //If they have something that covers the limb, and it is not missing, put flavortext. If it is covered but bleeding, add other flavortext. - if(wound_flavor_text["head"] && (is_destroyed["head"] || (!skipmask && !(wear_mask && istype(wear_mask, /obj/item/clothing/mask/gas))))) - msg += wound_flavor_text["head"] - if(wound_flavor_text["chest"] && !w_uniform && !skipjumpsuit) //No need. A missing chest gibs you. - msg += wound_flavor_text["chest"] - if(wound_flavor_text["l_arm"] && (is_destroyed["left arm"] || (!w_uniform && !skipjumpsuit))) - msg += wound_flavor_text["l_arm"] - if(wound_flavor_text["l_hand"] && (is_destroyed["left hand"] || (!gloves && !skipgloves))) - msg += wound_flavor_text["l_hand"] - if(wound_flavor_text["r_arm"] && (is_destroyed["right arm"] || (!w_uniform && !skipjumpsuit))) - msg += wound_flavor_text["r_arm"] - if(wound_flavor_text["r_hand"] && (is_destroyed["right hand"] || (!gloves && !skipgloves))) - msg += wound_flavor_text["r_hand"] - if(wound_flavor_text["groin"] && (is_destroyed["groin"] || (!w_uniform && !skipjumpsuit))) - msg += wound_flavor_text["groin"] - if(wound_flavor_text["l_leg"] && (is_destroyed["left leg"] || (!w_uniform && !skipjumpsuit))) - msg += wound_flavor_text["l_leg"] - if(wound_flavor_text["l_foot"]&& (is_destroyed["left foot"] || (!shoes && !skipshoes))) - msg += wound_flavor_text["l_foot"] - if(wound_flavor_text["r_leg"] && (is_destroyed["right leg"] || (!w_uniform && !skipjumpsuit))) - msg += wound_flavor_text["r_leg"] - if(wound_flavor_text["r_foot"]&& (is_destroyed["right foot"] || (!shoes && !skipshoes))) - msg += wound_flavor_text["r_foot"] - - var/temp = getBruteLoss() //no need to calculate each of these twice - - if(temp) - var/brute_message = !isSynthetic() ? "bruising" : "denting" - if(temp < 30) - msg += "[p_they(TRUE)] [p_have()] minor [brute_message ].\n" - else - msg += "[p_they(TRUE)] [p_have()] severe [brute_message ]!\n" - - temp = getFireLoss() - if(temp) - if(temp < 30) - msg += "[p_they(TRUE)] [p_have()] minor burns.\n" - else - msg += "[p_they(TRUE)] [p_have()] severe burns!\n" - - temp = getCloneLoss() - if(temp) - if(temp < 30) - msg += "[p_they(TRUE)] [p_have()] minor cellular damage.\n" - else - msg += "[p_they(TRUE)] [p_have()] severe cellular damage.\n" - - - if(fire_stacks > 0) - msg += "[p_they(TRUE)] [p_are()] covered in something flammable.\n" - if(fire_stacks < 0) - msg += "[p_they(TRUE)] looks a little soaked.\n" - - switch(wetlevel) - if(1) - msg += "[p_they(TRUE)] looks a bit damp.\n" - if(2) - msg += "[p_they(TRUE)] looks a little bit wet.\n" - if(3) - msg += "[p_they(TRUE)] looks wet.\n" - if(4) - msg += "[p_they(TRUE)] looks very wet.\n" - if(5) - msg += "[p_they(TRUE)] looks absolutely soaked.\n" - - if(nutrition < NUTRITION_LEVEL_STARVING - 50) - msg += "[p_they(TRUE)] [p_are()] severely malnourished.\n" - else if(nutrition >= NUTRITION_LEVEL_FAT) - if(user.nutrition < NUTRITION_LEVEL_STARVING - 50) - msg += "[p_they(TRUE)] [p_are()] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n" - else - msg += "[p_they(TRUE)] [p_are()] quite chubby.\n" - - if(!isSynthetic() && blood_volume < BLOOD_VOLUME_SAFE) - msg += "[p_they(TRUE)] [p_have()] pale skin.\n" - - if(bleedsuppress) - msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n" - else if(bleed_rate) - var/bleed_message = !isSynthetic() ? "bleeding" : "leaking" - if(reagents.has_reagent("heparin")) - msg += "[p_they(TRUE)] [p_are()] [bleed_message] uncontrollably!\n" - else - msg += "[p_they(TRUE)] [p_are()] [bleed_message]!\n" - - if(reagents.has_reagent("teslium")) - msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n" - - msg += "" - - if(!appears_dead) - if(stat == UNCONSCIOUS) - msg += "[p_they(TRUE)] [p_are()]n't responding to anything around [p_them()] and seems to be asleep.\n" - else if(getBrainLoss() >= 60) - msg += "[p_they(TRUE)] [p_have()] a stupid expression on [p_their()] face.\n" - - if(get_int_organ(/obj/item/organ/internal/brain)) - if(dna.species.show_ssd) - if(!key) - msg += "[p_they(TRUE)] [p_are()] totally catatonic. The stresses of life in deep-space must have been too much for [p_them()]. Any recovery is unlikely.\n" - else if(!client) - msg += "[p_they(TRUE)] [p_have()] suddenly fallen asleep, suffering from Space Sleep Disorder. [p_they(TRUE)] may wake up soon.\n" - - if(digitalcamo) - msg += "[p_they(TRUE)] [p_are()] moving [p_their()] body in an unnatural and blatantly inhuman manner.\n" - - if(!(skipface || ( wear_mask && ( wear_mask.flags_inv & HIDEFACE || wear_mask.flags_cover & MASKCOVERSMOUTH) ) ) && is_thrall(src) && in_range(user,src)) - msg += "Their features seem unnaturally tight and drawn.\n" - - if(decaylevel == 1) - msg += "[p_they(TRUE)] [p_are()] starting to smell.\n" - if(decaylevel == 2) - msg += "[p_they(TRUE)] [p_are()] bloated and smells disgusting.\n" - if(decaylevel == 3) - msg += "[p_they(TRUE)] [p_are()] rotting and blackened, the skin sloughing off. The smell is indescribably foul.\n" - if(decaylevel == 4) - msg += "[p_they(TRUE)] [p_are()] mostly dessicated now, with only bones remaining of what used to be a person.\n" - - if(hasHUD(user,"security")) - var/perpname = get_visible_name(TRUE) - var/criminal = "None" - - if(perpname) - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - criminal = R.fields["criminal"] - var/criminal_status = hasHUD(user, "read_only_security") ? "\[[criminal]\]" : "\[[criminal]\]" - msg += "Criminal status: [criminal_status]\n" - msg += "Security records: \[View\] \[Add comment\]\n" - - if(hasHUD(user,"medical")) - var/perpname = get_visible_name(TRUE) - var/medical = "None" - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.general) - if(R.fields["id"] == E.fields["id"]) - medical = R.fields["p_stat"] - - msg += "Physical status: \[[medical]\]\n" - msg += "Medical records: \[View\] \[Add comment\]\n" - - - if(print_flavor_text() && !skipface) - msg += "[print_flavor_text()]\n" - - msg += "*---------*" - if(pose) - if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "\n[p_they(TRUE)] [p_are()] [pose]" - - . = list(msg) - -//Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. -/proc/hasHUD(mob/M as mob, hudtype) - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/internal/cyberimp/eyes/hud/CIH = H.get_int_organ(/obj/item/organ/internal/cyberimp/eyes/hud) - switch(hudtype) - if("security") - return istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/security) - if("read_only_security") - var/obj/item/clothing/glasses/hud/security/S - if(istype(H.glasses, /obj/item/clothing/glasses/hud/security)) - S = H.glasses - return !istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/security) && S && S.read_only - if("medical") - return istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/medical) - else - return 0 - else if(isrobot(M) || isAI(M)) //Stand-in/Stopgap to prevent pAIs from freely altering records, pending a more advanced Records system - switch(hudtype) - if("security") - return 1 - if("medical") - return 1 - else - return 0 - else - return 0 - -// Ignores robotic limb branding prefixes like "Morpheus Cybernetics" -/proc/ignore_limb_branding(limb_name) - switch(limb_name) - if("chest") - . = "upper body" - if("groin") - . = "lower body" - if("head") - . = "head" - if("l_arm") - . = "left arm" - if("r_arm") - . = "right arm" - if("l_leg") - . = "left leg" - if("r_leg") - . = "right leg" - if("l_foot") - . = "left foot" - if("r_foot") - . = "right foot" - if("l_hand") - . = "left hand" - if("r_hand") - . = "right hand" +/mob/living/carbon/human/examine(mob/user) + var/skipgloves = 0 + var/skipsuitstorage = 0 + var/skipjumpsuit = 0 + var/skipshoes = 0 + var/skipmask = 0 + var/skipears = 0 + var/skipeyes = 0 + var/skipface = 0 + + //exosuits and helmets obscure our view and stuff. + if(wear_suit) + skipgloves = wear_suit.flags_inv & HIDEGLOVES + skipsuitstorage = wear_suit.flags_inv & HIDESUITSTORAGE + skipjumpsuit = wear_suit.flags_inv & HIDEJUMPSUIT + skipshoes = wear_suit.flags_inv & HIDESHOES + + if(head) + skipmask = head.flags_inv & HIDEMASK + skipeyes = head.flags_inv & HIDEEYES + skipears = head.flags_inv & HIDEEARS + skipface = head.flags_inv & HIDEFACE + + if(wear_mask) + skipface |= wear_mask.flags_inv & HIDEFACE + var/msg = "*---------*\nThis is " + + if(!(skipjumpsuit && skipface) && icon) //big suits/masks/helmets make it hard to tell their gender + msg += "[bicon(icon(icon, dir=SOUTH))] " //fucking BYOND: this should stop dreamseeker crashing if we -somehow- examine somebody before their icon is generated + msg += "[name]" + + var/displayed_species = dna.species.name + var/examine_color = dna.species.flesh_color + for(var/obj/item/clothing/C in src) //Disguise checks + if(C == src.head || C == src.wear_suit || C == src.wear_mask || C == src.w_uniform || C == src.belt || C == src.back) + if(C.species_disguise) + displayed_species = C.species_disguise + if(skipjumpsuit && skipface || (NO_EXAMINE in dna.species.species_traits)) //either obscured or on the nospecies list + msg += "!\n" //omit the species when examining + else if(displayed_species == "Slime People") //snowflakey because Slime People are defined as a plural + msg += ", a slime person!\n" + else if(displayed_species == "Unathi") //DAMN YOU, VOWELS + msg += ", a unathi!\n" + else + msg += ", a [lowertext(displayed_species)]!\n" + + //uniform + if(w_uniform && !skipjumpsuit && !(w_uniform.flags & ABSTRACT)) + //Ties + var/tie_msg + if(istype(w_uniform,/obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + if(U.accessories.len) + tie_msg += " with [english_accessory_list(U)]" + + if(w_uniform.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(w_uniform)] [w_uniform.gender==PLURAL?"some":"a"] [w_uniform.blood_color != "#030303" ? "blood-stained":"oil-stained"] [w_uniform.name][tie_msg]!\n" + else + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(w_uniform)] \a [w_uniform][tie_msg].\n" + + //head + if(head && !(head.flags & ABSTRACT)) + if(head.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(head)] [head.gender==PLURAL?"some":"a"] [head.blood_color != "#030303" ? "blood-stained":"oil-stained"] [head.name] on [p_their()] head!\n" + else + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(head)] \a [head] on [p_their()] head.\n" + + //suit/armour + if(wear_suit && !(wear_suit.flags & ABSTRACT)) + if(wear_suit.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(wear_suit)] [wear_suit.gender==PLURAL?"some":"a"] [wear_suit.blood_color != "#030303" ? "blood-stained":"oil-stained"] [wear_suit.name]!\n" + else + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(wear_suit)] \a [wear_suit].\n" + + //suit/armour storage + if(s_store && !skipsuitstorage) + if(s_store.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] carrying [bicon(s_store)] [s_store.gender==PLURAL?"some":"a"] [s_store.blood_color != "#030303" ? "blood-stained":"oil-stained"] [s_store.name] on [p_their()] [wear_suit.name]!\n" + else + msg += "[p_they(TRUE)] [p_are()] carrying [bicon(s_store)] \a [s_store] on [p_their()] [wear_suit.name].\n" + + //back + if(back && !(back.flags & ABSTRACT)) + if(back.blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [bicon(back)] [back.gender==PLURAL?"some":"a"] [back.blood_color != "#030303" ? "blood-stained":"oil-stained"] [back] on [p_their()] back.\n" + else + msg += "[p_they(TRUE)] [p_have()] [bicon(back)] \a [back] on [p_their()] back.\n" + + //left hand + if(l_hand && !(l_hand.flags & ABSTRACT)) + if(l_hand.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] holding [bicon(l_hand)] [l_hand.gender==PLURAL?"some":"a"] [l_hand.blood_color != "#030303" ? "blood-stained":"oil-stained"] [l_hand.name] in [p_their()] left hand!\n" + else + msg += "[p_they(TRUE)] [p_are()] holding [bicon(l_hand)] \a [l_hand] in [p_their()] left hand.\n" + + //right hand + if(r_hand && !(r_hand.flags & ABSTRACT)) + if(r_hand.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] holding [bicon(r_hand)] [r_hand.gender==PLURAL?"some":"a"] [r_hand.blood_color != "#030303" ? "blood-stained":"oil-stained"] [r_hand.name] in [p_their()] right hand!\n" + else + msg += "[p_they(TRUE)] [p_are()] holding [bicon(r_hand)] \a [r_hand] in [p_their()] right hand.\n" + + //gloves + if(gloves && !skipgloves && !(gloves.flags & ABSTRACT)) + if(gloves.blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [bicon(gloves)] [gloves.gender==PLURAL?"some":"a"] [gloves.blood_color != "#030303" ? "blood-stained":"oil-stained"] [gloves.name] on [p_their()] hands!\n" + else + msg += "[p_they(TRUE)] [p_have()] [bicon(gloves)] \a [gloves] on [p_their()] hands.\n" + else if(blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [hand_blood_color != "#030303" ? "blood-stained":"oil-stained"] hands!\n" + + //handcuffed? + if(handcuffed) + if(istype(handcuffed, /obj/item/restraints/handcuffs/cable/zipties)) + msg += "[p_they(TRUE)] [p_are()] [bicon(handcuffed)] restrained with zipties!\n" + else if(istype(handcuffed, /obj/item/restraints/handcuffs/cable)) + msg += "[p_they(TRUE)] [p_are()] [bicon(handcuffed)] restrained with cable!\n" + else + msg += "[p_they(TRUE)] [p_are()] [bicon(handcuffed)] handcuffed!\n" + + //belt + if(belt) + if(belt.blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [bicon(belt)] [belt.gender==PLURAL?"some":"a"] [belt.blood_color != "#030303" ? "blood-stained":"oil-stained"] [belt.name] about [p_their()] waist!\n" + else + msg += "[p_they(TRUE)] [p_have()] [bicon(belt)] \a [belt] about [p_their()] waist.\n" + + //shoes + if(shoes && !skipshoes && !(shoes.flags & ABSTRACT)) + if(shoes.blood_DNA) + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(shoes)] [shoes.gender==PLURAL?"some":"a"] [shoes.blood_color != "#030303" ? "blood-stained":"oil-stained"] [shoes.name] on [p_their()] feet!\n" + else + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(shoes)] \a [shoes] on [p_their()] feet.\n" + else if(blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [feet_blood_color != "#030303" ? "blood-stained":"oil-stained"] feet!\n" + + + //mask + if(wear_mask && !skipmask && !(wear_mask.flags & ABSTRACT)) + if(wear_mask.blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [bicon(wear_mask)] [wear_mask.gender==PLURAL?"some":"a"] [wear_mask.blood_color != "#030303" ? "blood-stained":"oil-stained"] [wear_mask.name] on [p_their()] face!\n" + else + msg += "[p_they(TRUE)] [p_have()] [bicon(wear_mask)] \a [wear_mask] on [p_their()] face.\n" + + //eyes + if(glasses && !skipeyes && !(glasses.flags & ABSTRACT)) + if(glasses.blood_DNA) + msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] [glasses.gender==PLURAL?"some":"a"] [glasses.blood_color != "#030303" ? "blood-stained":"oil-stained"] [glasses] covering [p_their()] eyes!\n" + else + msg += "[p_they(TRUE)] [p_have()] [bicon(glasses)] \a [glasses] covering [p_their()] eyes.\n" + + //left ear + if(l_ear && !skipears) + msg += "[p_they(TRUE)] [p_have()] [bicon(l_ear)] \a [l_ear] on [p_their()] left ear.\n" + + //right ear + if(r_ear && !skipears) + msg += "[p_they(TRUE)] [p_have()] [bicon(r_ear)] \a [r_ear] on [p_their()] right ear.\n" + + //ID + if(wear_id) + msg += "[p_they(TRUE)] [p_are()] wearing [bicon(wear_id)] \a [wear_id].\n" + + //Jitters + switch(jitteriness) + if(300 to INFINITY) + msg += "[p_they(TRUE)] [p_are()] convulsing violently!\n" + if(200 to 300) + msg += "[p_they(TRUE)] [p_are()] extremely jittery.\n" + if(100 to 200) + msg += "[p_they(TRUE)] [p_are()] twitching ever so slightly.\n" + + + var/appears_dead = FALSE + if(stat == DEAD || (status_flags & FAKEDEATH)) + appears_dead = TRUE + if(suiciding) + msg += "[p_they(TRUE)] appear[p_s()] to have committed suicide... there is no hope of recovery.\n" + msg += "[p_they(TRUE)] [p_are()] limp and unresponsive; there are no signs of life" + if(get_int_organ(/obj/item/organ/internal/brain)) + if(!key) + var/foundghost = FALSE + if(mind) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.mind == mind) + foundghost = TRUE + if(G.can_reenter_corpse == 0) + foundghost = FALSE + break + if(!foundghost) + msg += " and [p_their()] soul has departed" + msg += "...\n" + + if(!get_int_organ(/obj/item/organ/internal/brain)) + msg += "It appears that [p_their()] brain is missing...\n" + + msg += "" + + var/list/wound_flavor_text = list() + var/list/is_destroyed = list() + for(var/organ_tag in dna.species.has_limbs) + + var/list/organ_data = dna.species.has_limbs[organ_tag] + var/organ_descriptor = organ_data["descriptor"] + is_destroyed["[organ_data["descriptor"]]"] = 1 + + var/obj/item/organ/external/E = bodyparts_by_name[organ_tag] + if(!E) + wound_flavor_text["[organ_tag]"] = "[p_they(TRUE)] [p_are()] missing [p_their()] [organ_descriptor].\n" + else + if(!isSynthetic()) + if(E.is_robotic()) + wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a robotic [E.name]!\n" + + else if(E.status & ORGAN_SPLINTED) + wound_flavor_text["[E.limb_name]"] = "[p_they(TRUE)] [p_have()] a splint on [p_their()] [E.name]!\n" + + if(E.open) + if(E.is_robotic()) + msg += "The maintenance hatch on [p_their()] [ignore_limb_branding(E.limb_name)] is open!\n" + else + msg += "[p_their(TRUE)] [ignore_limb_branding(E.limb_name)] has an open incision!\n" + + for(var/obj/item/I in E.embedded_objects) + msg += "[p_they(TRUE)] [p_have()] \a [bicon(I)] [I] embedded in [p_their()] [E.name]!\n" + + //Handles the text strings being added to the actual description. + //If they have something that covers the limb, and it is not missing, put flavortext. If it is covered but bleeding, add other flavortext. + if(wound_flavor_text["head"] && (is_destroyed["head"] || (!skipmask && !(wear_mask && istype(wear_mask, /obj/item/clothing/mask/gas))))) + msg += wound_flavor_text["head"] + if(wound_flavor_text["chest"] && !w_uniform && !skipjumpsuit) //No need. A missing chest gibs you. + msg += wound_flavor_text["chest"] + if(wound_flavor_text["l_arm"] && (is_destroyed["left arm"] || (!w_uniform && !skipjumpsuit))) + msg += wound_flavor_text["l_arm"] + if(wound_flavor_text["l_hand"] && (is_destroyed["left hand"] || (!gloves && !skipgloves))) + msg += wound_flavor_text["l_hand"] + if(wound_flavor_text["r_arm"] && (is_destroyed["right arm"] || (!w_uniform && !skipjumpsuit))) + msg += wound_flavor_text["r_arm"] + if(wound_flavor_text["r_hand"] && (is_destroyed["right hand"] || (!gloves && !skipgloves))) + msg += wound_flavor_text["r_hand"] + if(wound_flavor_text["groin"] && (is_destroyed["groin"] || (!w_uniform && !skipjumpsuit))) + msg += wound_flavor_text["groin"] + if(wound_flavor_text["l_leg"] && (is_destroyed["left leg"] || (!w_uniform && !skipjumpsuit))) + msg += wound_flavor_text["l_leg"] + if(wound_flavor_text["l_foot"]&& (is_destroyed["left foot"] || (!shoes && !skipshoes))) + msg += wound_flavor_text["l_foot"] + if(wound_flavor_text["r_leg"] && (is_destroyed["right leg"] || (!w_uniform && !skipjumpsuit))) + msg += wound_flavor_text["r_leg"] + if(wound_flavor_text["r_foot"]&& (is_destroyed["right foot"] || (!shoes && !skipshoes))) + msg += wound_flavor_text["r_foot"] + + var/temp = getBruteLoss() //no need to calculate each of these twice + + if(temp) + var/brute_message = !isSynthetic() ? "bruising" : "denting" + if(temp < 30) + msg += "[p_they(TRUE)] [p_have()] minor [brute_message ].\n" + else + msg += "[p_they(TRUE)] [p_have()] severe [brute_message ]!\n" + + temp = getFireLoss() + if(temp) + if(temp < 30) + msg += "[p_they(TRUE)] [p_have()] minor burns.\n" + else + msg += "[p_they(TRUE)] [p_have()] severe burns!\n" + + temp = getCloneLoss() + if(temp) + if(temp < 30) + msg += "[p_they(TRUE)] [p_have()] minor cellular damage.\n" + else + msg += "[p_they(TRUE)] [p_have()] severe cellular damage.\n" + + + if(fire_stacks > 0) + msg += "[p_they(TRUE)] [p_are()] covered in something flammable.\n" + if(fire_stacks < 0) + msg += "[p_they(TRUE)] looks a little soaked.\n" + + switch(wetlevel) + if(1) + msg += "[p_they(TRUE)] looks a bit damp.\n" + if(2) + msg += "[p_they(TRUE)] looks a little bit wet.\n" + if(3) + msg += "[p_they(TRUE)] looks wet.\n" + if(4) + msg += "[p_they(TRUE)] looks very wet.\n" + if(5) + msg += "[p_they(TRUE)] looks absolutely soaked.\n" + + if(nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) + msg += "[p_they(TRUE)] [p_are()] severely malnourished.\n" + else if(nutrition >= NUTRITION_LEVEL_FAT) + if(user.nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) + msg += "[p_they(TRUE)] [p_are()] plump and delicious looking - Like a fat little piggy. A tasty piggy.\n" + else + msg += "[p_they(TRUE)] [p_are()] quite chubby.\n" + + if(!isSynthetic() && blood_volume < BLOOD_VOLUME_SAFE) + msg += "[p_they(TRUE)] [p_have()] pale skin.\n" + + if(bleedsuppress) + msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n" + else if(bleed_rate) + var/bleed_message = !isSynthetic() ? "bleeding" : "leaking" + msg += "[p_they(TRUE)] [p_are()] [bleed_message]!\n" + + if(reagents.has_reagent("teslium")) + msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n" + + msg += "" + + if(!appears_dead) + if(stat == UNCONSCIOUS) + msg += "[p_they(TRUE)] [p_are()]n't responding to anything around [p_them()] and seems to be asleep.\n" + else if(getBrainLoss() >= 60) + msg += "[p_they(TRUE)] [p_have()] a stupid expression on [p_their()] face.\n" + + if(get_int_organ(/obj/item/organ/internal/brain)) + if(dna.species.show_ssd) + if(!key) + msg += "[p_they(TRUE)] [p_are()] totally catatonic. The stresses of life in deep-space must have been too much for [p_them()]. Any recovery is unlikely.\n" + else if(!client) + msg += "[p_they(TRUE)] [p_have()] suddenly fallen asleep, suffering from Space Sleep Disorder. [p_they(TRUE)] may wake up soon.\n" + + if(digitalcamo) + msg += "[p_they(TRUE)] [p_are()] moving [p_their()] body in an unnatural and blatantly inhuman manner.\n" + + if(!(skipface || ( wear_mask && ( wear_mask.flags_inv & HIDEFACE || wear_mask.flags_cover & MASKCOVERSMOUTH) ) ) && is_thrall(src) && in_range(user,src)) + msg += "Their features seem unnaturally tight and drawn.\n" + + if(decaylevel == 1) + msg += "[p_they(TRUE)] [p_are()] starting to smell.\n" + if(decaylevel == 2) + msg += "[p_they(TRUE)] [p_are()] bloated and smells disgusting.\n" + if(decaylevel == 3) + msg += "[p_they(TRUE)] [p_are()] rotting and blackened, the skin sloughing off. The smell is indescribably foul.\n" + if(decaylevel == 4) + msg += "[p_they(TRUE)] [p_are()] mostly dessicated now, with only bones remaining of what used to be a person.\n" + + if(hasHUD(user,"security")) + var/perpname = get_visible_name(TRUE) + var/criminal = "None" + + if(perpname) + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.security) + if(R.fields["id"] == E.fields["id"]) + criminal = R.fields["criminal"] + var/criminal_status = hasHUD(user, "read_only_security") ? "\[[criminal]\]" : "\[[criminal]\]" + msg += "Criminal status: [criminal_status]\n" + msg += "Security records: \[View\] \[Add comment\]\n" + + if(hasHUD(user,"medical")) + var/perpname = get_visible_name(TRUE) + var/medical = "None" + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.general) + if(R.fields["id"] == E.fields["id"]) + medical = R.fields["p_stat"] + + msg += "Physical status: \[[medical]\]\n" + msg += "Medical records: \[View\] \[Add comment\]\n" + + + if(print_flavor_text() && !skipface) + msg += "[print_flavor_text()]\n" + + msg += "*---------*" + if(pose) + if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "\n[p_they(TRUE)] [p_are()] [pose]" + + . = list(msg) + +//Helper procedure. Called by /mob/living/carbon/human/examine() and /mob/living/carbon/human/Topic() to determine HUD access to security and medical records. +/proc/hasHUD(mob/M as mob, hudtype) + if(istype(M, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/internal/cyberimp/eyes/hud/CIH = H.get_int_organ(/obj/item/organ/internal/cyberimp/eyes/hud) + switch(hudtype) + if("security") + return istype(H.glasses, /obj/item/clothing/glasses/hud/security) || istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/security) + if("read_only_security") + var/obj/item/clothing/glasses/hud/security/S + if(istype(H.glasses, /obj/item/clothing/glasses/hud/security)) + S = H.glasses + return !istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/security) && S && S.read_only + if("medical") + return istype(H.glasses, /obj/item/clothing/glasses/hud/health) || istype(CIH,/obj/item/organ/internal/cyberimp/eyes/hud/medical) + else + return 0 + else if(isrobot(M) || isAI(M)) //Stand-in/Stopgap to prevent pAIs from freely altering records, pending a more advanced Records system + switch(hudtype) + if("security") + return 1 + if("medical") + return 1 + else + return 0 + else + return 0 + +// Ignores robotic limb branding prefixes like "Morpheus Cybernetics" +/proc/ignore_limb_branding(limb_name) + switch(limb_name) + if("chest") + . = "upper body" + if("groin") + . = "lower body" + if("head") + . = "head" + if("l_arm") + . = "left arm" + if("r_arm") + . = "right arm" + if("l_leg") + . = "left leg" + if("r_leg") + . = "right leg" + if("l_foot") + . = "left foot" + if("r_foot") + . = "right foot" + if("l_hand") + . = "left hand" + if("r_hand") + . = "right hand" \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 6540b74de589..59285ba88e3c 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1,1964 +1,1964 @@ -/mob/living/carbon/human - name = "unknown" - real_name = "unknown" - voice_name = "unknown" - icon = 'icons/mob/human.dmi' - icon_state = "body_m_s" - deathgasp_on_death = TRUE - var/obj/item/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. - -/mob/living/carbon/human/New(loc) - icon = null // This is now handled by overlays -- we just keep an icon for the sake of the map editor. - if(length(args) > 1) - log_runtime(EXCEPTION("human/New called with more than 1 argument (REPORT THIS ENTIRE RUNTIME TO A CODER)")) - . = ..() - -/mob/living/carbon/human/Initialize(mapload, datum/species/new_species = /datum/species/human) - if(!dna) - dna = new /datum/dna(null) - // Species name is handled by set_species() - - set_species(new_species, 1, delay_icon_update = 1, skip_same_check = TRUE) - - ..() - - if(dna.species) - real_name = dna.species.get_random_name(gender) - name = real_name - if(mind) - mind.name = real_name - - create_reagents(330) - - martial_art = default_martial_art - - handcrafting = new() - - // Set up DNA. - if(dna) - dna.ready_dna(src) - dna.real_name = real_name - sync_organ_dna(1) - - UpdateAppearance() - -/mob/living/carbon/human/OpenCraftingMenu() - handcrafting.ui_interact(src) - -/mob/living/carbon/human/prepare_data_huds() - //Update med hud images... - ..() - //...sec hud images... - sec_hud_set_ID() - sec_hud_set_implants() - sec_hud_set_security_status() - //...and display them. - add_to_all_human_data_huds() - -/mob/living/carbon/human/Destroy() - . = ..() - SSmobs.cubemonkeys -= src - QDEL_LIST(bodyparts) - splinted_limbs.Cut() - -/mob/living/carbon/human/dummy - real_name = "Test Dummy" - status_flags = GODMODE|CANPUSH - -/mob/living/carbon/human/skrell/Initialize(mapload) - ..(mapload, /datum/species/skrell) - -/mob/living/carbon/human/tajaran/Initialize(mapload) - ..(mapload, /datum/species/tajaran) - -/mob/living/carbon/human/vulpkanin/Initialize(mapload) - ..(mapload, /datum/species/vulpkanin) - -/mob/living/carbon/human/unathi/Initialize(mapload) - ..(mapload, /datum/species/unathi) - -/mob/living/carbon/human/vox/Initialize(mapload) - ..(mapload, /datum/species/vox) - -/mob/living/carbon/human/voxarmalis/Initialize(mapload) - ..(mapload, /datum/species/vox/armalis) - -/mob/living/carbon/human/skeleton/Initialize(mapload) - ..(mapload, /datum/species/skeleton) - -/mob/living/carbon/human/kidan/Initialize(mapload) - ..(mapload, /datum/species/kidan) - -/mob/living/carbon/human/plasma/Initialize(mapload) - ..(mapload, /datum/species/plasmaman) - -/mob/living/carbon/human/slime/Initialize(mapload) - ..(mapload, /datum/species/slime) - -/mob/living/carbon/human/grey/Initialize(mapload) - ..(mapload, /datum/species/grey) - -/mob/living/carbon/human/abductor/Initialize(mapload) - ..(mapload, /datum/species/abductor) - -/mob/living/carbon/human/diona/Initialize(mapload) - ..(mapload, /datum/species/diona) - -/mob/living/carbon/human/pod_diona/Initialize(mapload) - ..(mapload, /datum/species/diona/pod) - -/mob/living/carbon/human/machine/Initialize(mapload) - ..(mapload, /datum/species/machine) - -/mob/living/carbon/human/machine/created - name = "Integrated Robotic Chassis" - -/mob/living/carbon/human/machine/created/Initialize(mapload) - ..() - rename_character(null, "Integrated Robotic Chassis ([rand(1, 9999)])") - update_dna() - for(var/obj/item/organ/external/E in bodyparts) - if(istype(E, /obj/item/organ/external/chest) || istype(E, /obj/item/organ/external/groin)) - continue - qdel(E) - for(var/obj/item/organ/O in internal_organs) - qdel(O) - regenerate_icons() - death() - -/mob/living/carbon/human/shadow/Initialize(mapload) - ..(mapload, /datum/species/shadow) - -/mob/living/carbon/human/golem/Initialize(mapload) - ..(mapload, /datum/species/golem) - -/mob/living/carbon/human/wryn/Initialize(mapload) - ..(mapload, /datum/species/wryn) - -/mob/living/carbon/human/nucleation/Initialize(mapload) - ..(mapload, /datum/species/nucleation) - -/mob/living/carbon/human/drask/Initialize(mapload) - ..(mapload, /datum/species/drask) - -/mob/living/carbon/human/monkey/Initialize(mapload) - ..(mapload, /datum/species/monkey) - -/mob/living/carbon/human/farwa/Initialize(mapload) - ..(mapload, /datum/species/monkey/tajaran) - -/mob/living/carbon/human/wolpin/Initialize(mapload) - ..(mapload, /datum/species/monkey/vulpkanin) - -/mob/living/carbon/human/neara/Initialize(mapload) - ..(mapload, /datum/species/monkey/skrell) - -/mob/living/carbon/human/stok/Initialize(mapload) - ..(mapload, /datum/species/monkey/unathi) - -/mob/living/carbon/human/Stat() - ..() - statpanel("Status") - - stat(null, "Intent: [a_intent]") - stat(null, "Move Mode: [m_intent]") - - show_stat_emergency_shuttle_eta() - - if(client.statpanel == "Status") - var/total_user_contents = GetAllContents() // cache it - if(locate(/obj/item/gps) in total_user_contents) - var/turf/T = get_turf(src) - stat(null, "GPS: [COORD(T)]") - if(locate(/obj/item/assembly/health) in total_user_contents) - stat(null, "Health: [health]") - if(internal) - if(!internal.air_contents) - qdel(internal) - else - stat("Internal Atmosphere Info", internal.name) - stat("Tank Pressure", internal.air_contents.return_pressure()) - stat("Distribution Pressure", internal.distribute_pressure) - - if(istype(back, /obj/item/rig)) - var/obj/item/rig/suit = back - var/cell_status = "ERROR" - if(suit.cell) - cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" - stat(null, "Suit charge: [cell_status]") - - // I REALLY need to split up status panel things into datums - var/mob/living/simple_animal/borer/B = has_brain_worms() - if(B && B.controlling) - stat("Chemicals", B.chemicals) - - if(mind) - if(mind.changeling) - stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") - stat("Absorbed DNA", mind.changeling.absorbedcount) - - if(mind.vampire) - stat("Total Blood", "[mind.vampire.bloodtotal]") - stat("Usable Blood", "[mind.vampire.bloodusable]") - - if(istype(loc, /obj/spacepod)) // Spacdpods! - var/obj/spacepod/S = loc - stat("Spacepod Charge", "[istype(S.battery) ? "[(S.battery.charge / S.battery.maxcharge) * 100]" : "No cell detected"]") - stat("Spacepod Integrity", "[!S.health ? "0" : "[(S.health / initial(S.health)) * 100]"]%") - -/mob/living/carbon/human/ex_act(severity) - var/shielded = 0 - var/b_loss = null - var/f_loss = null - - if(status_flags & GODMODE) - return 0 - - switch(severity) - if(1) - b_loss += 500 - if(!prob(getarmor(null, "bomb"))) - gib() - return 0 - else - var/atom/target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src))) - throw_at(target, 200, 4) - - var/limbs_affected = pick(2,3,4) - var/obj/item/organ/external/processing_dismember - var/list/valid_limbs = bodyparts.Copy() - - while(limbs_affected != 0 && valid_limbs.len > 0) - processing_dismember = pick(valid_limbs) - if(processing_dismember.limb_name != "chest" && processing_dismember.limb_name != "head" && processing_dismember.limb_name != "groin") - processing_dismember.droplimb(1,DROPLIMB_SHARP,0,1) - valid_limbs -= processing_dismember - limbs_affected -= 1 - else valid_limbs -= processing_dismember - - if(2) - if(!shielded) //literally nothing could change shielded before this so wth - b_loss += 60 - - f_loss += 60 - - var/limbs_affected = 0 - var/obj/item/organ/external/processing_dismember - var/list/valid_limbs = bodyparts.Copy() - - if(prob(getarmor(null, "bomb"))) - b_loss = b_loss/1.5 - f_loss = f_loss/1.5 - - limbs_affected = pick(1, 1, 2) - else - limbs_affected = pick(1, 2, 3) - - while(limbs_affected != 0 && valid_limbs.len > 0) - processing_dismember = pick(valid_limbs) - if(processing_dismember.limb_name != "chest" && processing_dismember.limb_name != "head" && processing_dismember.limb_name != "groin") - processing_dismember.droplimb(1,DROPLIMB_SHARP,0,1) - valid_limbs -= processing_dismember - limbs_affected -= 1 - else valid_limbs -= processing_dismember - - if(!istype(l_ear, /obj/item/clothing/ears/earmuffs) && !istype(r_ear, /obj/item/clothing/ears/earmuffs)) - AdjustEarDamage(30, 120) - if(prob(70) && !shielded) - Paralyse(10) - - if(3) - b_loss += 30 - if(prob(getarmor(null, "bomb"))) - b_loss = b_loss/2 - - else - - var/limbs_affected = pick(0, 1) - var/obj/item/organ/external/processing_dismember - var/list/valid_limbs = bodyparts.Copy() - - while(limbs_affected != 0 && valid_limbs.len > 0) - processing_dismember = pick(valid_limbs) - if(processing_dismember.limb_name != "chest" && processing_dismember.limb_name != "head" && processing_dismember.limb_name != "groin") - processing_dismember.droplimb(1,DROPLIMB_SHARP,0,1) - valid_limbs -= processing_dismember - limbs_affected -= 1 - else valid_limbs -= processing_dismember - - if(!istype(l_ear, /obj/item/clothing/ears/earmuffs) && !istype(r_ear, /obj/item/clothing/ears/earmuffs)) - AdjustEarDamage(15, 60) - if(prob(50) && !shielded) - Paralyse(10) - - take_overall_damage(b_loss,f_loss, TRUE, used_weapon = "Explosive Blast") - - ..() - -/mob/living/carbon/human/blob_act(obj/structure/blob/B) - if(stat == DEAD) - return - show_message("The blob attacks you!") - var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") - var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) - apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee")) - -/mob/living/carbon/human/bullet_act() - if(martial_art && martial_art.deflection_chance) //Some martial arts users can deflect projectiles! - if(!prob(martial_art.deflection_chance)) - return ..() - if(!src.lying && !(HULK in mutations)) //But only if they're not lying down, and hulks can't do it - visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!") - return 0 - ..() - -/mob/living/carbon/human/get_restraining_item() - . = ..() - if(!. && istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) - . = wear_suit - -/mob/living/carbon/human/var/temperature_resistance = T0C+75 - - -/mob/living/carbon/human/show_inv(mob/user) - user.set_machine(src) - var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask) || get_organ_slot("breathing_tube") - var/list/obscured = check_obscured_slots() - - var/dat = {" - - - "} - - dat += "" - - dat += "" - - var/obj/item/organ/internal/headpocket/C = get_int_organ(/obj/item/organ/internal/headpocket) - if(C) - if(slot_wear_mask in obscured) - dat += "" - else - var/list/items = C.get_contents() - if(items.len) - dat += "" - else - dat += "" - - if(slot_wear_mask in obscured) - dat += "" - else - dat += "" - - if(!issmall(src)) - if(slot_glasses in obscured) - dat += "" - else - dat += "" - - if(slot_l_ear in obscured) - dat += "" - else - dat += "" - - if(slot_r_ear in obscured) - dat += "" - else - dat += "" - - dat += "" - - dat += "" - if(wear_suit) - dat += "" - else - dat += "" - - if(slot_shoes in obscured) - dat += "" - else - dat += "" - - if(slot_gloves in obscured) - dat += "" - else - dat += "" - - if(slot_w_uniform in obscured) - dat += "" - else - dat += "" - - if((w_uniform == null && !(dna && dna.species.nojumpsuit)) || (slot_w_uniform in obscured)) - dat += "" - dat += "" - dat += "" - dat += "" - dat += "" - else - dat += "" - // Pockets - dat += "" - dat += "" - dat += "" - - if(istype(w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - dat += "" - - if(U.accessories.len) - dat += "" - - - if(handcuffed) - dat += "" - if(legcuffed) - dat += "" - - dat += {"
    Left Hand:[(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "Empty"]
    Right Hand:[(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "Empty"]
     
    Back:[(back && !(back.flags&ABSTRACT)) ? back : "Empty"]" - if(has_breathable_mask && istype(back, /obj/item/tank)) - dat += " [internal ? "Disable Internals" : "Set Internals"]" - - dat += "
     
    Head:[(head && !(head.flags&ABSTRACT)) ? head : "Empty"]
     ↳Headpocket:Obscured
     ↳Headpocket:Dislodge Items
     ↳Headpocket:Empty
    Mask:Obscured
    Mask:[(wear_mask && !(wear_mask.flags&ABSTRACT)) ? wear_mask : "Empty"]" - - if(istype(wear_mask, /obj/item/clothing/mask/muzzle)) - var/obj/item/clothing/mask/muzzle/M = wear_mask - if(M.security_lock) - dat += " [M.locked ? "Disable Lock" : "Set Lock"]" - - dat += "
     
    Eyes:Obscured
    Eyes:[(glasses && !(glasses.flags&ABSTRACT)) ? glasses : "Empty"]
    Left Ear:Obscured
    Left Ear:[(l_ear && !(l_ear.flags&ABSTRACT)) ? l_ear : "Empty"]
    Right Ear:Obscured
    Right Ear:[(r_ear && !(r_ear.flags&ABSTRACT)) ? r_ear : "Empty"]
     
    Exosuit:[(wear_suit && !(wear_suit.flags&ABSTRACT)) ? wear_suit : "Empty"]
     ↳Suit Storage:[(s_store && !(s_store.flags&ABSTRACT)) ? s_store : "Empty"]" - if(has_breathable_mask && istype(s_store, /obj/item/tank)) - dat += " [internal ? "Disable Internals" : "Set Internals"]" - dat += "
     ↳Suit Storage:
    Shoes:Obscured
    Shoes:[(shoes && !(shoes.flags&ABSTRACT)) ? shoes : "Empty"]
    Gloves:Obscured
    Gloves:[(gloves && !(gloves.flags&ABSTRACT)) ? gloves : "Empty"]
    Uniform:Obscured
    Uniform:[(w_uniform && !(w_uniform.flags&ABSTRACT)) ? w_uniform : "Empty"]
     ↳Pockets:
     ↳ID:
     ↳Belt:
     ↳Suit Sensors:
     ↳PDA:
     ↳Belt:[(belt && !(belt.flags&ABSTRACT)) ? belt : "Empty"]" - if(has_breathable_mask && istype(belt, /obj/item/tank)) - dat += " [internal ? "Disable Internals" : "Set Internals"]" - dat += "
     ↳Pockets:" - if(l_store && internal && l_store == internal) - dat += "[l_store]" - else if(l_store && !(l_store.flags&ABSTRACT)) - dat += "Left (Full)" - else - dat += "Left (Empty)" - dat += " " - if(r_store && internal && r_store == internal) - dat += "[r_store]" - else if(r_store && !(r_store.flags&ABSTRACT)) - dat += "Right (Full)" - else - dat += "Right (Empty)" - dat += "
     ↳ID:[(wear_id && !(wear_id.flags&ABSTRACT)) ? wear_id : "Empty"]
     ↳PDA:[(wear_pda && !(wear_pda.flags&ABSTRACT)) ? wear_pda : "Empty"]
     ↳Suit Sensors:[U.has_sensor >= 2 ? "--SENSORS LOCKED--" : "Set Sensors"]
     ↳Remove Accessory
    Handcuffed: Remove
    Legcuffed
    - Close - "} - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 540) - popup.set_content(dat) - popup.open() - -// Get rank from ID, ID inside PDA, PDA, ID in wallet, etc. -/mob/living/carbon/human/proc/get_authentification_rank(var/if_no_id = "No id", var/if_no_job = "No job") - var/obj/item/pda/pda = wear_id - if(istype(pda)) - if(pda.id) - return pda.id.rank - else - return pda.ownrank - else - var/obj/item/card/id/id = get_idcard() - if(id) - return id.rank ? id.rank : if_no_job - else - return if_no_id - -//gets assignment from ID or ID inside PDA or PDA itself -//Useful when player do something with computers -/mob/living/carbon/human/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") - var/obj/item/pda/pda = wear_id - var/obj/item/card/id/id = wear_id - if(istype(pda)) - if(pda.id && istype(pda.id, /obj/item/card/id)) - . = pda.id.assignment - else - . = pda.ownjob - else if(istype(id)) - . = id.assignment - else - return if_no_id - if(!.) - . = if_no_job - return - -//gets name from ID or ID inside PDA or PDA itself -//Useful when player do something with computers -/mob/living/carbon/human/proc/get_authentification_name(var/if_no_id = "Unknown") - var/obj/item/pda/pda = wear_id - var/obj/item/card/id/id = wear_id - if(istype(pda)) - if(pda.id) - . = pda.id.registered_name - else - . = pda.owner - else if(istype(id)) - . = id.registered_name - else - return if_no_id - return - -//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere -/mob/living/carbon/human/get_visible_name(var/id_override = FALSE) - if(name_override) - return name_override - if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) //Wearing a mask which hides our face, use id-name if possible - return get_id_name("Unknown") - if(head && (head.flags_inv & HIDEFACE)) - return get_id_name("Unknown") //Likewise for hats - var/face_name = get_face_name() - var/id_name = get_id_name("") - if(id_name && (id_name != face_name) && !id_override) - return "[face_name] (as [id_name])" - return face_name - -//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when polyacided or when updating a human's name variable -/mob/living/carbon/human/proc/get_face_name() - var/obj/item/organ/external/head = get_organ("head") - if(!head || head.disfigured || cloneloss > 50 || !real_name || (HUSK in mutations)) //disfigured. use id-name if possible - return "Unknown" - return real_name - -//gets name from ID or PDA itself, ID inside PDA doesn't matter -//Useful when player is being seen by other mobs -/mob/living/carbon/human/proc/get_id_name(var/if_no_id = "Unknown") - var/obj/item/pda/pda = wear_id - var/obj/item/card/id/id = wear_id - if(istype(pda)) . = pda.owner - else if(istype(id)) . = id.registered_name - if(!.) . = if_no_id //to prevent null-names making the mob unclickable - return - -//gets ID card object from special clothes slot or, if applicable, hands as well -/mob/living/carbon/human/proc/get_idcard(var/check_hands = FALSE) - var/obj/item/card/id/id = wear_id - var/obj/item/pda/pda = wear_id - if(istype(pda) && pda.id) - id = pda.id - - if(check_hands) - if(istype(get_active_hand(), /obj/item/card/id)) - id = get_active_hand() - else if(istype(get_inactive_hand(), /obj/item/card/id)) - id = get_inactive_hand() - - if(istype(id)) - return id - -/mob/living/carbon/human/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - dna.species.update_sight(src) - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() - -//Added a safety check in case you want to shock a human mob directly through electrocute_act. -/mob/living/carbon/human/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - if(tesla_shock) - var/total_coeff = 1 - if(gloves) - var/obj/item/clothing/gloves/G = gloves - if(G.siemens_coefficient <= 0) - total_coeff -= 0.5 - if(wear_suit) - var/obj/item/clothing/suit/S = wear_suit - if(S.siemens_coefficient <= 0) - total_coeff -= 0.95 - else if(S.siemens_coefficient == (-1)) - total_coeff -= 1 - siemens_coeff = total_coeff - if(tesla_ignore) - siemens_coeff = 0 - else if(!safety) - var/gloves_siemens_coeff = 1 - if(gloves) - var/obj/item/clothing/gloves/G = gloves - gloves_siemens_coeff = G.siemens_coefficient - siemens_coeff = gloves_siemens_coeff - if(undergoing_cardiac_arrest() && !illusion) - if(shock_damage * siemens_coeff >= 1 && prob(25)) - set_heartattack(FALSE) - if(stat == CONSCIOUS) - to_chat(src, "You feel your heart beating again!") - - dna.species.spec_electrocute_act(src, shock_damage, source, siemens_coeff, safety, override, tesla_shock, illusion, stun) - . = ..(shock_damage, source, siemens_coeff, safety, override, tesla_shock, illusion, stun) - -/mob/living/carbon/human/Topic(href, href_list) - if(!usr.stat && usr.canmove && !usr.restrained() && in_range(src, usr)) - var/thief_mode = 0 - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - var/obj/item/clothing/gloves/G = H.gloves - if(G && G.pickpocket) - thief_mode = 1 - - if(href_list["embedded_object"]) - var/obj/item/organ/external/L = locate(href_list["embedded_limb"]) in bodyparts - if(!L) - return - var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects - if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore - return - var/time_taken = I.embedded_unsafe_removal_time*I.w_class - usr.visible_message("[usr] attempts to remove [I] from [usr.p_their()] [L.name].","You attempt to remove [I] from your [L.name]... (It will take [time_taken/10] seconds.)") - if(do_after(usr, time_taken, needhand = 1, target = src)) - if(!I || !L || I.loc != src || !(I in L.embedded_objects)) - return - L.embedded_objects -= I - L.receive_damage(I.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus. - I.forceMove(get_turf(src)) - usr.put_in_hands(I) - usr.emote("scream") - usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","You successfully remove [I] from your [L.name].") - if(!has_embedded_objects()) - clear_alert("embeddedobject") - return - - if(href_list["item"]) - var/slot = text2num(href_list["item"]) - if(slot in check_obscured_slots()) - to_chat(usr, "You can't reach that! Something is covering it.") - return - - if(href_list["pockets"]) - var/pocket_side = href_list["pockets"] - var/pocket_id = (pocket_side == "right" ? slot_r_store : slot_l_store) - var/obj/item/pocket_item = (pocket_id == slot_r_store ? r_store : l_store) - var/obj/item/place_item = usr.get_active_hand() // Item to place in the pocket, if it's empty - - var/delay_denominator = 1 - if(pocket_item && !(pocket_item.flags&ABSTRACT)) - if(pocket_item.flags & NODROP) - to_chat(usr, "You try to empty [src]'s [pocket_side] pocket, it seems to be stuck!") - to_chat(usr, "You try to empty [src]'s [pocket_side] pocket.") - else if(place_item && place_item.mob_can_equip(src, pocket_id, 1) && !(place_item.flags&ABSTRACT)) - to_chat(usr, "You try to place [place_item] into [src]'s [pocket_side] pocket.") - delay_denominator = 4 - else - return - - if(do_mob(usr, src, POCKET_STRIP_DELAY/delay_denominator)) //placing an item into the pocket is 4 times faster - if(pocket_item) - if(pocket_item == (pocket_id == slot_r_store ? r_store : l_store)) //item still in the pocket we search - unEquip(pocket_item) - if(thief_mode) - usr.put_in_hands(pocket_item) - add_attack_logs(usr, src, "Stripped of [pocket_item]", isLivingSSD(src) ? null : ATKLOG_ALL) - else - if(place_item) - usr.unEquip(place_item) - equip_to_slot_if_possible(place_item, pocket_id, 0, 1) - add_attack_logs(usr, src, "Equipped with [place_item]", isLivingSSD(src) ? null : ATKLOG_ALL) - - // Update strip window - if(usr.machine == src && in_range(src, usr)) - show_inv(usr) - else - // Display a warning if the user mocks up if they don't have pickpocket gloves. - if(!thief_mode) - to_chat(src, "You feel your [pocket_side] pocket being fumbled with!") - add_attack_logs(usr, src, "Attempted strip of [pocket_item]", isLivingSSD(src) ? null : ATKLOG_ALL) - - if(href_list["set_sensor"]) - if(istype(w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - U.set_sensors(usr) - - if(href_list["dislodge_headpocket"]) - usr.visible_message("[usr] is trying to remove something from [src]'s head!", - "You start to dislodge whatever's inside [src]'s headpocket!") - if(do_mob(usr, src, POCKET_STRIP_DELAY)) - usr.visible_message("[usr] has dislodged something from [src]'s head!", - "You have dislodged everything from [src]'s headpocket!") - var/obj/item/organ/internal/headpocket/C = get_int_organ(/obj/item/organ/internal/headpocket) - C.empty_contents() - add_attack_logs(usr, src, "Stripped of headpocket items", isLivingSSD(src) ? null : ATKLOG_ALL) - - if(href_list["strip_accessory"]) - if(istype(w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - if(U.accessories.len) - var/obj/item/clothing/accessory/A = U.accessories[1] - if(!thief_mode) - usr.visible_message("\The [usr] starts to take off \the [A] from \the [src]'s [U]!", \ - "You start to take off \the [A] from \the [src]'s [U]!") - - if(do_mob(usr, src, 40) && A && U.accessories.len) - if(!thief_mode) - usr.visible_message("\The [usr] takes \the [A] off of \the [src]'s [U]!", \ - "You take \the [A] off of \the [src]'s [U]!") - A.on_removed(usr) - U.accessories -= A - update_inv_w_uniform() - - if(href_list["criminal"]) - if(hasHUD(usr,"security")) - if(usr.incapacitated()) - return - var/found_record = 0 - var/perpname = get_visible_name(TRUE) - - if(perpname != "Unknown") - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - - var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.fields["criminal"]) in list("None", "*Arrest*", "Incarcerated", "Parolled", "Released", "Cancel") - var/t1 = copytext(trim(sanitize(input("Enter Reason:", "Security HUD", null, null) as text)), 1, MAX_MESSAGE_LEN) - if(!t1) - t1 = "(none)" - - if(hasHUD(usr, "security") && setcriminal != "Cancel") - found_record = 1 - if(R.fields["criminal"] == "*Execute*") - to_chat(usr, "Unable to modify the sec status of a person with an active Execution order. Use a security computer instead.") - else - var/rank - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - rank = U.get_assignment() - else if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - rank = "[U.modtype] [U.braintype]" - else if(isAI(usr)) - rank = "AI" - set_criminal_status(usr, R, setcriminal, t1, rank) - break // Git out of the securiy records loop! - if(found_record) - break // Git out of the general records - - if(!found_record) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if(href_list["secrecord"]) - if(hasHUD(usr,"security")) - if(usr.incapacitated()) - return - var/perpname = get_visible_name(TRUE) - var/read = 0 - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - to_chat(usr, "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]") - to_chat(usr, "Minor Crimes: [R.fields["mi_crim"]]") - to_chat(usr, "Details: [R.fields["mi_crim_d"]]") - to_chat(usr, "Major Crimes: [R.fields["ma_crim"]]") - to_chat(usr, "Details: [R.fields["ma_crim_d"]]") - to_chat(usr, "Notes: [R.fields["notes"]]") - to_chat(usr, "\[View Comment Log\]") - read = 1 - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if(href_list["secrecordComment"]) - if(hasHUD(usr,"security")) - if(usr.incapacitated()) - return - var/perpname = get_visible_name(TRUE) - var/read = 0 - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - read = 1 - if(LAZYLEN(R.fields["comments"])) - for(var/c in R.fields["comments"]) - to_chat(usr, c) - else - to_chat(usr, "No comment found") - to_chat(usr, "\[Add comment\]") - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if(href_list["secrecordadd"]) - if(hasHUD(usr,"security")) - if(usr.incapacitated()) - return - var/perpname = get_visible_name(TRUE) - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.security) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"security")) - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Sec. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || usr.stat || usr.restrained() || !hasHUD(usr, "security")) - return - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - R.fields["comments"] += "Made by [U.get_authentification_name()] ([U.get_assignment()]) on [current_date_string] [station_time_timestamp()]
    [t1]" - if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - R.fields["comments"] += "Made by [U.name] ([U.modtype] [U.braintype]) on [current_date_string] [station_time_timestamp()]
    [t1]" - if(isAI(usr)) - var/mob/living/silicon/ai/U = usr - R.fields["comments"] += "Made by [U.name] (artificial intelligence) on [current_date_string] [station_time_timestamp()]
    [t1]" - - if(href_list["medical"]) - if(hasHUD(usr,"medical")) - if(usr.incapacitated()) - return - var/modified = 0 - var/perpname = get_visible_name(TRUE) - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.general) - if(R.fields["id"] == E.fields["id"]) - var/setmedical = input(usr, "Specify a new medical status for this person.", "Medical HUD", R.fields["p_stat"]) in list("*SSD*", "*Deceased*", "Physically Unfit", "Active", "Disabled", "Cancel") - - if(hasHUD(usr,"medical")) - if(setmedical != "Cancel") - R.fields["p_stat"] = setmedical - modified = 1 - if(PDA_Manifest.len) - PDA_Manifest.Cut() - - spawn() - sec_hud_set_security_status() - - if(!modified) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if(href_list["medrecord"]) - if(hasHUD(usr,"medical")) - if(usr.incapacitated()) - return - var/read = 0 - var/perpname = get_visible_name(TRUE) - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - to_chat(usr, "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]]") - to_chat(usr, "DNA: [R.fields["b_dna"]]") - to_chat(usr, "Minor Disabilities: [R.fields["mi_dis"]]") - to_chat(usr, "Details: [R.fields["mi_dis_d"]]") - to_chat(usr, "Major Disabilities: [R.fields["ma_dis"]]") - to_chat(usr, "Details: [R.fields["ma_dis_d"]]") - to_chat(usr, "Notes: [R.fields["notes"]]") - to_chat(usr, "\[View Comment Log\]") - read = 1 - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if(href_list["medrecordComment"]) - if(hasHUD(usr,"medical")) - if(usr.incapacitated()) - return - var/perpname = get_visible_name(TRUE) - var/read = 0 - - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - read = 1 - if(LAZYLEN(R.fields["comments"])) - for(var/c in R.fields["comments"]) - to_chat(usr, c) - else - to_chat(usr, "No comment found") - to_chat(usr, "\[Add comment\]") - - if(!read) - to_chat(usr, "Unable to locate a data core entry for this person.") - - if(href_list["medrecordadd"]) - if(hasHUD(usr,"medical")) - if(usr.incapacitated()) - return - var/perpname = get_visible_name(TRUE) - for(var/datum/data/record/E in data_core.general) - if(E.fields["name"] == perpname) - for(var/datum/data/record/R in data_core.medical) - if(R.fields["id"] == E.fields["id"]) - if(hasHUD(usr,"medical")) - var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) - if(!t1 || usr.stat || usr.restrained() || !hasHUD(usr, "medical")) - return - if(ishuman(usr)) - var/mob/living/carbon/human/U = usr - R.fields["comments"] += "Made by [U.get_authentification_name()] ([U.get_assignment()]) on [current_date_string] [station_time_timestamp()]
    [t1]" - if(isrobot(usr)) - var/mob/living/silicon/robot/U = usr - R.fields["comments"] += "Made by [U.name] ([U.modtype] [U.braintype]) on [current_date_string] [station_time_timestamp()]
    [t1]" - if(isAI(usr)) - var/mob/living/silicon/ai/U = usr - R.fields["comments"] += "Made by [U.name] (artificial intelligence) on [current_date_string] [station_time_timestamp()]
    [t1]" - - if(href_list["lookitem"]) - var/obj/item/I = locate(href_list["lookitem"]) - src.examinate(I) - - if(href_list["lookmob"]) - var/mob/M = locate(href_list["lookmob"]) - src.examinate(M) - . = ..() - - -///check_eye_prot() -///Returns a number between -1 to 2 -/mob/living/carbon/human/check_eye_prot() - var/number = ..() - if(istype(head, /obj/item/clothing/head)) //are they wearing something on their head - var/obj/item/clothing/head/HFP = head //if yes gets the flash protection value from that item - number += HFP.flash_protect - if(istype(glasses, /obj/item/clothing/glasses)) //glasses - var/obj/item/clothing/glasses/GFP = glasses - number += GFP.flash_protect - if(istype(wear_mask, /obj/item/clothing/mask)) //mask - var/obj/item/clothing/mask/MFP = wear_mask - number += MFP.flash_protect - for(var/obj/item/organ/internal/cyberimp/eyes/EFP in internal_organs) - number += EFP.flash_protect - - return number - -/mob/living/carbon/human/check_ear_prot() - if(head && (head.flags & HEADBANGPROTECT)) - return 1 - if(l_ear && (l_ear.flags & EARBANGPROTECT)) - return 1 - if(r_ear && (r_ear.flags & EARBANGPROTECT)) - return 1 - -///tintcheck() -///Checks eye covering items for visually impairing tinting, such as welding masks -///Checked in life.dm. 0 & 1 = no impairment, 2 = welding mask overlay, 3 = You can see jack, but you can't see shit. -/mob/living/carbon/human/tintcheck() - var/tinted = 0 - if(istype(src.head, /obj/item/clothing/head)) - var/obj/item/clothing/head/HT = src.head - tinted += HT.tint - if(istype(src.glasses, /obj/item/clothing/glasses)) - var/obj/item/clothing/glasses/GT = src.glasses - tinted += GT.tint - if(istype(src.wear_mask, /obj/item/clothing/mask)) - var/obj/item/clothing/mask/MT = src.wear_mask - tinted += MT.tint - - //god help me - if(istype(back, /obj/item/rig)) - var/obj/item/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & HEAD)) - if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) - tinted = 2 - if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) - tinted = 3 - //im so sorry - - return tinted - - -/mob/living/carbon/human/abiotic(var/full_body = 0) - if(full_body && ((src.l_hand && !(src.l_hand.flags & ABSTRACT)) || (src.r_hand && !(src.r_hand.flags & ABSTRACT)) || (src.back || src.wear_mask || src.head || src.shoes || src.w_uniform || src.wear_suit || src.glasses || src.l_ear || src.r_ear || src.gloves))) - return 1 - - if((src.l_hand && !(src.l_hand.flags & ABSTRACT)) || (src.r_hand && !(src.r_hand.flags & ABSTRACT))) - return 1 - - return 0 - - -/mob/living/carbon/human/proc/check_dna() - dna.check_integrity(src) - -/mob/living/carbon/human/proc/play_xylophone() - if(!src.xylophone) - visible_message("[src] begins playing [p_their()] ribcage like a xylophone. It's quite spooky.","You begin to play a spooky refrain on your ribcage.","You hear a spooky xylophone melody.") - var/song = pick('sound/effects/xylophone1.ogg','sound/effects/xylophone2.ogg','sound/effects/xylophone3.ogg') - playsound(loc, song, 50, 1, -1) - xylophone = 1 - spawn(1200) - xylophone=0 - return - -/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE) - . = 1 - - if(!target_zone) - if(!user) - target_zone = pick("chest","chest","chest","left leg","right leg","left arm", "right arm", "head") - else - target_zone = user.zone_selected - - - if(PIERCEIMMUNE in dna.species.species_traits) - . = 0 - - var/obj/item/organ/external/affecting = get_organ(target_zone) - var/fail_msg - if(!affecting) - . = 0 - fail_msg = "[p_they(TRUE)] [p_are()] missing that limb." - else if(affecting.is_robotic()) - . = 0 - fail_msg = "That limb is robotic." - else - switch(target_zone) - if("head") - if(head && head.flags & THICKMATERIAL && !penetrate_thick) - . = 0 - else - if(wear_suit && wear_suit.flags & THICKMATERIAL && !penetrate_thick) - . = 0 - if(!. && error_msg && user) - if(!fail_msg) - fail_msg = "There is no exposed flesh or thin material [target_zone == "head" ? "on [p_their()] head" : "on [p_their()] body"] to inject into." - to_chat(user, "[fail_msg]") - -/mob/living/carbon/human/proc/check_obscured_slots() - var/list/obscured = list() - - if(wear_suit) - if(wear_suit.flags_inv & HIDEGLOVES) - obscured |= slot_gloves - if(wear_suit.flags_inv & HIDEJUMPSUIT) - obscured |= slot_w_uniform - if(wear_suit.flags_inv & HIDESHOES) - obscured |= slot_shoes - - if(head) - if(head.flags_inv & HIDEMASK) - obscured |= slot_wear_mask - if(head.flags_inv & HIDEEYES) - obscured |= slot_glasses - if(head.flags_inv & HIDEEARS) - obscured |= slot_r_ear - obscured |= slot_l_ear - - if(obscured.len > 0) - return obscured - else - return null - -/mob/living/carbon/human/proc/check_has_mouth() - // Todo, check stomach organ when implemented. - var/obj/item/organ/external/head/H = get_organ("head") - if(!H || !H.can_intake_reagents) - return 0 - return 1 - -/mob/living/carbon/human/proc/get_visible_gender() - var/list/obscured = check_obscured_slots() - var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE)) - if((slot_w_uniform in obscured) && skipface) - return PLURAL - return gender - -/mob/living/carbon/human/proc/increase_germ_level(n) - if(gloves) - gloves.germ_level += n - else - germ_level += n - -/mob/living/carbon/human/proc/check_and_regenerate_organs(var/mob/living/carbon/human/H) //Regenerates missing limbs/organs. - var/list/types_of_int_organs = list() //This will hold all the types of organs in the mob before rejuvenation. - for(var/obj/item/organ/internal/I in H.internal_organs) - types_of_int_organs |= I.type //Compiling the list of organ types. It is possible for organs to be missing from this list if they are absent from the mob. - - //Clean up limbs - for(var/organ_name in H.bodyparts_by_name) - var/obj/item/organ/organ = H.bodyparts_by_name[organ_name] - if(!organ) //The !organ check is to account for mechanical limb (prostheses) losses, since those are handled in a way that leaves indexed but null list entries instead of stumps. - qdel(organ) - H.bodyparts_by_name -= organ_name //Making sure the list entry is removed. - - //Replacing lost limbs with the species default. - var/mob/living/carbon/human/temp_holder - for(var/limb_type in H.dna.species.has_limbs) - if(!(limb_type in H.bodyparts_by_name)) - var/list/organ_data = H.dna.species.has_limbs[limb_type] - var/limb_path = organ_data["path"] - var/obj/item/organ/external/O = new limb_path(temp_holder) - if(H.get_limb_by_name(O.name)) //Check to see if the user already has an limb with the same name as the 'missing limb'. If they do, skip regrowth. - continue //In an example, this will prevent duplication of the mob's right arm if the mob is a Human and they have a Diona right arm, since, - //while the limb with the name 'right_arm' the mob has may not be listed in their species' bodyparts definition, it is still viable and has the appropriate limb name. - else - O = new limb_path(H) //Create the limb on the player. - O.owner = H - H.bodyparts |= H.bodyparts_by_name[O.limb_name] - if(O.body_part == HEAD) //They're sprouting a fresh head so lets hook them up with their genetic stuff so their new head looks like the original. - H.UpdateAppearance() - - //Replacing lost organs with the species default. - temp_holder = new /mob/living/carbon/human() - var/list/species_organs = H.dna.species.has_organ.Copy() //Compile a list of species organs and tack on the mutantears afterward. - if(H.dna.species.mutantears) - species_organs["ears"] = H.dna.species.mutantears - for(var/index in species_organs) - var/organ = species_organs[index] - if(!(organ in types_of_int_organs)) //If the mob is missing this particular organ... - var/obj/item/organ/internal/I = new organ(temp_holder) //Create the organ inside our holder so we can check it before implantation. - if(H.get_organ_slot(I.slot)) //Check to see if the user already has an organ in the slot the 'missing organ' belongs to. If they do, skip implantation. - continue //In an example, this will prevent duplication of the mob's eyes if the mob is a Human and they have Nucleation eyes, since, - //while the organ in the eyes slot may not be listed in the mob's species' organs definition, it is still viable and fits in the appropriate organ slot. - else - I = new organ(H) //Create the organ inside the player. - I.insert(H) - -/mob/living/carbon/human/revive() - //Fix up all organs and replace lost ones. - restore_all_organs() //Rejuvenate and reset all existing organs. - check_and_regenerate_organs(src) //Regenerate limbs and organs only if they're really missing. - surgeries.Cut() //End all surgeries. - - if(!isskeleton(src) && (SKELETON in mutations)) - mutations.Remove(SKELETON) - if(NOCLONE in mutations) - mutations.Remove(NOCLONE) - if(HUSK in mutations) - mutations.Remove(HUSK) - - if(!client || !key) //Don't boot out anyone already in the mob. - for(var/obj/item/organ/internal/brain/H in world) - if(H.brainmob) - if(H.brainmob.real_name == src.real_name) - if(H.brainmob.mind) - H.brainmob.mind.transfer_to(src) - qdel(H) - - ..() - -/mob/living/carbon/human/proc/is_lung_ruptured() - var/obj/item/organ/internal/lungs/L = get_int_organ(/obj/item/organ/internal/lungs) - if(!L) - return 0 - - return L.is_bruised() - -/mob/living/carbon/human/proc/rupture_lung() - var/obj/item/organ/internal/lungs/L = get_int_organ(/obj/item/organ/internal/lungs) - if(!L) - return 0 - - if(!L.is_bruised()) - custom_pain("You feel a stabbing pain in your chest!") - L.damage = L.min_bruised_damage - -//returns 1 if made bloody, returns 0 otherwise - -/mob/living/carbon/human/clean_blood(var/clean_feet) - .=..() - if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len) - feet_blood_color = null - qdel(feet_blood_DNA) - bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - blood_state = BLOOD_STATE_NOT_BLOODY - update_inv_shoes(1) - return 1 - -/mob/living/carbon/human/cuff_resist(obj/item/I) - if(HULK in mutations) - say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - if(..(I, cuff_break = 1)) - unEquip(I) - else - if(..()) - unEquip(I) - -/mob/living/carbon/human/resist_restraints() - if(wear_suit && wear_suit.breakouttime) - changeNext_move(CLICK_CD_BREAKOUT) - last_special = world.time + CLICK_CD_BREAKOUT - cuff_resist(wear_suit) - else - ..() - -/mob/living/carbon/human/generate_name() - name = dna.species.get_random_name(gender) - real_name = name - if(dna) - dna.real_name = name - return name - -/mob/living/carbon/human/verb/check_pulse() - set category = null - set name = "Check pulse" - set desc = "Approximately count somebody's pulse. Requires you to stand still at least 6 seconds." - set src in view(1) - var/self = 0 - - if(usr.stat == 1 || usr.restrained() || !isliving(usr)) return - - if(usr == src) - self = 1 - if(!self) - usr.visible_message("[usr] kneels down, puts [usr.p_their()] hand on [src]'s wrist and begins counting [p_their()] pulse.",\ - "You begin counting [src]'s pulse") - else - usr.visible_message("[usr] begins counting [p_their()] pulse.",\ - "You begin counting your pulse.") - - if(src.pulse) - to_chat(usr, "[self ? "You have a" : "[src] has a"] pulse! Counting...") - else - to_chat(usr, "[src] has no pulse!")//it is REALLY UNLIKELY that a dead person would check his own pulse - - return - - to_chat(usr, "Don't move until counting is finished.") - var/time = world.time - sleep(60) - if(usr.l_move_time >= time) //checks if our mob has moved during the sleep() - to_chat(usr, "You moved while counting. Try again.") - else - to_chat(usr, "[self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)].") - -/mob/living/carbon/human/proc/set_species(datum/species/new_species, default_colour, delay_icon_update = FALSE, skip_same_check = FALSE, retain_damage = FALSE) - if(!skip_same_check) - if(dna.species.name == initial(new_species.name)) - return - var/datum/species/oldspecies = dna.species - - if(oldspecies) - if(oldspecies.language) - remove_language(oldspecies.language) - - if(oldspecies.default_language) - remove_language(oldspecies.default_language) - - if(gender == PLURAL && oldspecies.has_gender) - change_gender(pick(MALE, FEMALE)) - - if(oldspecies.default_genes.len) - oldspecies.handle_dna(src, TRUE) // Remove any genes that belong to the old species - - oldspecies.on_species_loss(src) - - dna.species = new new_species() - - tail = dna.species.tail - - maxHealth = dna.species.total_health - - if(dna.species.language) - add_language(dna.species.language) - - if(dna.species.default_language) - add_language(dna.species.default_language) - - hunger_drain = dna.species.hunger_drain - digestion_ratio = dna.species.digestion_ratio - - if(dna.species.base_color && default_colour) - //Apply colour. - skin_colour = dna.species.base_color - else - skin_colour = "#000000" - - if(!(dna.species.bodyflags & HAS_SKIN_TONE)) - s_tone = 0 - - var/list/thing_to_check = list(slot_wear_mask, slot_head, slot_shoes, slot_gloves, slot_l_ear, slot_r_ear, slot_glasses, slot_l_hand, slot_r_hand) - var/list/kept_items[0] - var/list/item_flags[0] - for(var/thing in thing_to_check) - var/obj/item/I = get_item_by_slot(thing) - if(I) - kept_items[I] = thing - item_flags[I] = I.flags - I.flags = 0 // Temporary set the flags to 0 - - if(retain_damage) - //Create a list of body parts which are damaged by burn or brute and save them to apply after new organs are generated. First we just handle external organs. - var/bodypart_damages = list() - //Loop through all external organs and save the damage states for brute and burn - for(var/obj/item/organ/external/E in bodyparts) - if(E.brute_dam == 0 && E.burn_dam == 0 && E.internal_bleeding == FALSE) //If there's no damage we don't bother remembering it. - continue - var/brute = E.brute_dam - var/burn = E.burn_dam - var/IB = E.internal_bleeding - var/obj/item/organ/external/OE = new E.type() - var/stats = list(OE, brute, burn, IB) - bodypart_damages += list(stats) - - //Now we do the same for internal organs via the same proceedure. - var/internal_damages = list() - for(var/obj/item/organ/internal/I in internal_organs) - if(I.damage == 0) - continue - var/obj/item/organ/internal/OI = new I.type() - var/damage = I.damage - var/broken = I.is_broken() - var/stats = list(OI, damage, broken) - internal_damages += list(stats) - - //Create the new organs for the species change - dna.species.create_organs(src) - - //Apply relevant damages and variables to the new organs. - for(var/B in bodyparts) - var/obj/item/organ/external/E = B - for(var/list/part in bodypart_damages) - var/obj/item/organ/external/OE = part[1] - if((E.type == OE.type)) // Type has to be explicit, as right limbs are a child of left ones etc. - var/brute = part[2] - var/burn = part[3] - var/IB = part[4] - //Deal the damage to the new organ and then delete the entry to prevent duplicate checks - E.receive_damage(brute, burn, ignore_resists = TRUE) - E.internal_bleeding = IB - qdel(part) - - for(var/O in internal_organs) - var/obj/item/organ/internal/I = O - for(var/list/part in internal_damages) - var/obj/item/organ/internal/OI = part[1] - var/organ_type - - if(OI.parent_type == /obj/item/organ/internal) //Dealing with species organs - organ_type = OI.type - else - organ_type = OI.parent_type - - if(istype(I, organ_type)) - var/damage = part[2] - var/broken = part[3] - I.receive_damage(damage, 1) - if(broken && !(I.status & ORGAN_BROKEN)) - I.status |= ORGAN_BROKEN - qdel(part) - - else - dna.species.create_organs(src) - - for(var/obj/item/thing in kept_items) - equip_to_slot_if_possible(thing, kept_items[thing], redraw_mob = 0) - thing.flags = item_flags[thing] // Reset the flags to the origional ones - - //Handle default hair/head accessories for created mobs. - var/obj/item/organ/external/head/H = get_organ("head") - if(dna.species.default_hair) - H.h_style = dna.species.default_hair - else - H.h_style = "Bald" - if(dna.species.default_fhair) - H.f_style = dna.species.default_fhair - else - H.f_style = "Shaved" - if(dna.species.default_headacc) - H.ha_style = dna.species.default_headacc - else - H.ha_style = "None" - - if(dna.species.default_hair_colour) - //Apply colour. - H.hair_colour = dna.species.default_hair_colour - else - H.hair_colour = "#000000" - if(dna.species.default_fhair_colour) - H.facial_colour = dna.species.default_fhair_colour - else - H.facial_colour = "#000000" - if(dna.species.default_headacc_colour) - H.headacc_colour = dna.species.default_headacc_colour - else - H.headacc_colour = "#000000" - - m_styles = DEFAULT_MARKING_STYLES //Wipes out markings, setting them all to "None". - m_colours = DEFAULT_MARKING_COLOURS //Defaults colour to #00000 for all markings. - body_accessory = null - - dna.real_name = real_name - - dna.species.on_species_gain(src) - - update_sight() - - dna.species.handle_dna(src) //Give them whatever special dna business they got. - - update_client_colour(0) - - if(!delay_icon_update) - UpdateAppearance() - - overlays.Cut() - update_mutantrace(1) - regenerate_icons() - - if(dna.species) - return TRUE - else - return FALSE - -/mob/living/carbon/human/get_default_language() - if(default_language) - return default_language - - if(!dna.species) - return null - return dna.species.default_language ? GLOB.all_languages[dna.species.default_language] : null - -/mob/living/carbon/human/proc/bloody_doodle() - set category = "IC" - set name = "Write in blood" - set desc = "Use blood on your hands to write a short message on the floor or a wall, murder mystery style." - - if(usr != src) - return 0 //something is terribly wrong - if(incapacitated()) - to_chat(src, "You can't write on the floor in your current state!") - return - if(!bloody_hands) - verbs -= /mob/living/carbon/human/proc/bloody_doodle - - if(gloves) - to_chat(src, "[gloves] are preventing you from writing anything down!") - return - - var/turf/simulated/T = loc - if(!istype(T)) //to prevent doodling out of mechs and lockers - to_chat(src, "You cannot reach the floor.") - return - - var/turf/origin = T - var/direction = input(src,"Which way?","Tile selection") as anything in list("Here","North","South","East","West") - if(direction != "Here") - T = get_step(T,text2dir(direction)) - if(!istype(T)) - to_chat(src, "You cannot doodle there.") - return - - var/num_doodles = 0 - for(var/obj/effect/decal/cleanable/blood/writing/W in T) - num_doodles++ - if(num_doodles > 4) - to_chat(src, "There is no space to write on!") - return - - var/max_length = bloody_hands * 30 //tweeter style - - var/message = stripped_input(src,"Write a message. It cannot be longer than [max_length] characters.","Blood writing", "") - if(origin != loc) - to_chat(src, "Stay still while writing!") - return - if(message) - var/used_blood_amount = round(length(message) / 30, 1) - bloody_hands = max(0, bloody_hands - used_blood_amount) //use up some blood - - if(length(message) > max_length) - message += "-" - to_chat(src, "You ran out of blood to write with!") - else - to_chat(src, "You daub '[message]' on [T] in shiny red lettering.") - var/obj/effect/decal/cleanable/blood/writing/W = new(T) - W.message = message - W.add_fingerprint(src) - -/mob/living/carbon/human/proc/get_eyecon() - var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) - var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes) - if(istype(dna.species) && dna.species.eyes) - var/icon/eyes_icon = new /icon('icons/mob/human_face.dmi', dna.species.eyes) - if(eye_implant) //Eye implants override native DNA eye colo(u)r - eyes_icon = eye_implant.generate_icon() - else if(eyes) - eyes_icon = eyes.generate_icon() - else //Error 404: Eyes not found! - eyes_icon.Blend("#800000", ICON_ADD) - - return eyes_icon - -/mob/living/carbon/human/proc/get_eye_shine() //Referenced cult constructs for shining in the dark. Needs to be above lighting effects such as shading. - var/obj/item/organ/external/head/head_organ = get_organ("head") - var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] - var/icon/hair = new /icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - var/mutable_appearance/MA = mutable_appearance(get_icon_difference(get_eyecon(), hair), layer = ABOVE_LIGHTING_LAYER) - MA.plane = ABOVE_LIGHTING_PLANE - return MA //Cut the hair's pixels from the eyes icon so eyes covered by bangs stay hidden even while on a higher layer. - -/*Used to check if eyes should shine in the dark. Returns the image of the eyes on the layer where they will appear to shine. -Eyes need to have significantly high darksight to shine unless the mob has the XRAY vision mutation. Eyes will not shine if they are covered in any way.*/ -/mob/living/carbon/human/proc/eyes_shine() - var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) - var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes) - if(!(istype(eyes) || istype(eye_implant))) - return FALSE - if(!get_location_accessible(src, "eyes")) - return FALSE - if(!(eyes.shine()) && !istype(eye_implant) && !(XRAY in mutations)) //If their eyes don't shine, they don't have other augs, nor do they have X-RAY vision - return FALSE - - return TRUE - -/mob/living/carbon/human/assess_threat(var/mob/living/simple_animal/bot/secbot/judgebot, var/lasercolor) - if(judgebot.emagged == 2) - return 10 //Everyone is a criminal! - - var/threatcount = 0 - - //Lasertag bullshit - if(lasercolor) - if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve - if(istype(wear_suit, /obj/item/clothing/suit/redtag)) - threatcount += 4 - if((istype(r_hand,/obj/item/gun/energy/laser/tag/red)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/red))) - threatcount += 4 - if(istype(belt, /obj/item/gun/energy/laser/tag/red)) - threatcount += 2 - - if(lasercolor == "r") - if(istype(wear_suit, /obj/item/clothing/suit/bluetag)) - threatcount += 4 - if((istype(r_hand,/obj/item/gun/energy/laser/tag/blue)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/blue))) - threatcount += 4 - if(istype(belt, /obj/item/gun/energy/laser/tag/blue)) - threatcount += 2 - - return threatcount - - //Check for ID - var/obj/item/card/id/idcard = get_idcard() - if(judgebot.idcheck && !idcard) - threatcount += 4 - - //Check for weapons - if(judgebot.weaponscheck) - if(!idcard || !(ACCESS_WEAPONS in idcard.access)) - if(judgebot.check_for_weapons(l_hand)) - threatcount += 4 - if(judgebot.check_for_weapons(r_hand)) - threatcount += 4 - if(judgebot.check_for_weapons(belt)) - threatcount += 2 - - //Check for arrest warrant - if(judgebot.check_records) - var/perpname = get_visible_name(TRUE) - var/datum/data/record/R = find_record("name", perpname, data_core.security) - if(R && R.fields["criminal"]) - switch(R.fields["criminal"]) - if("*Execute*") - threatcount += 7 - if("*Arrest*") - threatcount += 5 - if("Incarcerated") - threatcount += 2 - if("Parolled") - threatcount += 2 - - //Check for dresscode violations - if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard)) - threatcount += 2 - - - //Mindshield implants imply slight trustworthiness - if(ismindshielded(src)) - threatcount -= 1 - - //Agent cards lower threatlevel. - if(istype(idcard, /obj/item/card/id/syndicate)) - threatcount -= 5 - - return threatcount - -/mob/living/carbon/human/singularity_act() - . = 20 - if(mind) - if((mind.assigned_role == "Station Engineer") || (mind.assigned_role == "Chief Engineer") ) - . = 100 - if(mind.assigned_role == "Clown") - . = rand(-1000, 1000) - ..() //Called afterwards because getting the mind after getting gibbed is sketchy - -/mob/living/carbon/human/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_THREE) - var/list/handlist = list(l_hand, r_hand) - for(var/obj/item/hand in handlist) - if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && unEquip(hand)) - step_towards(hand, src) - to_chat(src, "\The [S] pulls \the [hand] from your grip!") - apply_effect(current_size * 3, IRRADIATE) - -/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/human/H) - if(H == src) - to_chat(src, "You cannot perform CPR on yourself!") - return - if(H.stat == DEAD || (H.status_flags & FAKEDEATH)) - to_chat(src, "[H.name] is dead!") - return - if(!check_has_mouth()) - to_chat(src, "You don't have a mouth, you cannot perform CPR!") - return - if(!H.check_has_mouth()) - to_chat(src, "They don't have a mouth, you cannot perform CPR!") - return - if((head && (head.flags_cover & HEADCOVERSMOUTH)) || (wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH) && !wear_mask.mask_adjusted)) - to_chat(src, "Remove your mask first!") - return - if((H.head && (H.head.flags_cover & HEADCOVERSMOUTH)) || (H.wear_mask && (H.wear_mask.flags_cover & MASKCOVERSMOUTH) && !H.wear_mask.mask_adjusted)) - to_chat(src, "Remove [H.p_their()] mask first!") - return - if(H.receiving_cpr) // To prevent spam stacking - to_chat(src, "They are already receiving CPR!") - return - visible_message("[src] is trying to perform CPR on [H.name]!", "You try to perform CPR on [H.name]!") - H.receiving_cpr = TRUE - if(do_mob(src, H, 40)) - if(H.health <= HEALTH_THRESHOLD_CRIT) - H.adjustOxyLoss(-15) - H.SetLoseBreath(0) - H.AdjustParalysis(-1) - H.updatehealth("cpr") - visible_message("[src] performs CPR on [H.name]!", "You perform CPR on [H.name].") - - to_chat(H, "You feel a breath of fresh air enter your lungs. It feels good.") - H.receiving_cpr = FALSE - add_attack_logs(src, H, "CPRed", ATKLOG_ALL) - return TRUE - else - H.receiving_cpr = FALSE - to_chat(src, "You need to stay still while performing CPR!") - -/mob/living/carbon/human/canBeHandcuffed() - if(get_num_arms() >= 2) - return TRUE - else - return FALSE - -/mob/living/carbon/human/has_mutated_organs() - for(var/obj/item/organ/external/E in bodyparts) - if(E.status & ORGAN_MUTATED) - return TRUE - return FALSE - -/mob/living/carbon/human/InCritical() - return (health <= HEALTH_THRESHOLD_CRIT && stat == UNCONSCIOUS) - - -/mob/living/carbon/human/IsAdvancedToolUser() - if(dna.species.has_fine_manipulation) - return TRUE - return FALSE - -/mob/living/carbon/human/get_permeability_protection() - var/list/prot = list("hands"=0, "chest"=0, "groin"=0, "legs"=0, "feet"=0, "arms"=0, "head"=0) - for(var/obj/item/I in get_equipped_items()) - if(I.body_parts_covered & HANDS) - prot["hands"] = max(1 - I.permeability_coefficient, prot["hands"]) - if(I.body_parts_covered & UPPER_TORSO) - prot["chest"] = max(1 - I.permeability_coefficient, prot["chest"]) - if(I.body_parts_covered & LOWER_TORSO) - prot["groin"] = max(1 - I.permeability_coefficient, prot["groin"]) - if(I.body_parts_covered & LEGS) - prot["legs"] = max(1 - I.permeability_coefficient, prot["legs"]) - if(I.body_parts_covered & FEET) - prot["feet"] = max(1 - I.permeability_coefficient, prot["feet"]) - if(I.body_parts_covered & ARMS) - prot["arms"] = max(1 - I.permeability_coefficient, prot["arms"]) - if(I.body_parts_covered & HEAD) - prot["head"] = max(1 - I.permeability_coefficient, prot["head"]) - var/protection = (prot["head"] + prot["arms"] + prot["feet"] + prot["legs"] + prot["groin"] + prot["chest"] + prot["hands"])/7 - return protection - -/mob/living/carbon/human/proc/get_full_print() - if(!dna || !dna.uni_identity) - return - return md5(dna.uni_identity) - -/mob/living/carbon/human/can_see_reagents() - for(var/obj/item/clothing/C in src) //If they have some clothing equipped that lets them see reagents, they can see reagents - if(C.scan_reagents) - return 1 - -/mob/living/carbon/human/can_eat(flags = 255) - return dna.species && (dna.species.dietflags & flags) - -/mob/living/carbon/human/selfFeed(var/obj/item/reagent_containers/food/toEat, fullness) - if(!check_has_mouth()) - to_chat(src, "Where do you intend to put \the [toEat]? You don't have a mouth!") - return 0 - return ..() - -/mob/living/carbon/human/forceFed(var/obj/item/reagent_containers/food/toEat, mob/user, fullness) - if(!check_has_mouth()) - if(!((istype(toEat, /obj/item/reagent_containers/food/drinks) && (ismachine(src))))) - to_chat(user, "Where do you intend to put \the [toEat]? \The [src] doesn't have a mouth!") - return 0 - return ..() - -/mob/living/carbon/human/selfDrink(var/obj/item/reagent_containers/food/drinks/toDrink) - if(!check_has_mouth()) - if(!ismachine(src)) - to_chat(src, "Where do you intend to put \the [src]? You don't have a mouth!") - return 0 - else - to_chat(src, "You pour a bit of liquid from [toDrink] into your connection port.") - else - to_chat(src, "You swallow a gulp of [toDrink].") - return 1 - -/mob/living/carbon/human/can_track(mob/living/user) - if(wear_id) - var/obj/item/card/id/id = wear_id - if(istype(id) && id.is_untrackable()) - return 0 - if(wear_pda) - var/obj/item/pda/pda = wear_pda - if(istype(pda)) - var/obj/item/card/id/id = pda.id - if(istype(id) && id.is_untrackable()) - return 0 - if(istype(head, /obj/item/clothing/head)) - var/obj/item/clothing/head/hat = head - if(hat.blockTracking) - return 0 - - return ..() - -/mob/living/carbon/human/proc/get_age_pitch() - return 1.0 + 0.5*(30 - age)/80 - -/mob/living/carbon/human/get_access() - . = ..() - - if(wear_id) - . |= wear_id.GetAccess() - if(wear_pda) - . |= wear_pda.GetAccess() - if(istype(w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = w_uniform - if(U.accessories) - for(var/obj/item/clothing/accessory/A in U.accessories) - . |= A.GetAccess() - -/mob/living/carbon/human/is_mechanical() - return ..() || (dna.species.bodyflags & ALL_RPARTS) != 0 - -/mob/living/carbon/human/can_use_guns(var/obj/item/gun/G) - . = ..() - - if(G.trigger_guard == TRIGGER_GUARD_NORMAL) - if(HULK in mutations) - to_chat(src, "Your meaty finger is much too large for the trigger guard!") - return 0 - if(NOGUNS in dna.species.species_traits) - to_chat(src, "Your fingers don't fit in the trigger guard!") - return 0 - - if(martial_art && martial_art.no_guns) //great dishonor to famiry - to_chat(src, "[martial_art.no_guns_message]") - return 0 - - return . - -/mob/living/carbon/human/proc/change_icobase(var/new_icobase, var/new_deform, var/owner_sensitive) - for(var/obj/item/organ/external/O in bodyparts) - O.change_organ_icobase(new_icobase, new_deform, owner_sensitive) //Change the icobase/deform of all our organs. If owner_sensitive is set, that means the proc won't mess with frankenstein limbs. - -/mob/living/carbon/human/serialize() - // Currently: Limbs/organs only - var/list/data = ..() - var/list/limbs_list = list() - var/list/organs_list = list() - var/list/equip_list = list() - data["limbs"] = limbs_list - data["iorgans"] = organs_list - data["equip"] = equip_list - - data["dna"] = dna.serialize() - data["age"] = age - - // No being naked - data["ushirt"] = undershirt - data["socks"] = socks - data["uwear"] = underwear - - // Limbs - for(var/limb in bodyparts_by_name) - var/obj/item/organ/O = bodyparts_by_name[limb] - if(!O) - limbs_list[limb] = "missing" - continue - - limbs_list[limb] = O.serialize() - - // Internal organs/augments - for(var/organ in internal_organs) - var/obj/item/organ/O = organ - organs_list[O.name] = O.serialize() - - // Equipment - equip_list.len = slots_amt - for(var/i = 1, i < slots_amt, i++) - var/obj/item/thing = get_item_by_slot(i) - if(thing != null) - equip_list[i] = thing.serialize() - - return data - -/mob/living/carbon/human/deserialize(list/data) - var/list/limbs_list = data["limbs"] - var/list/organs_list = data["iorgans"] - var/list/equip_list = data["equip"] - var/turf/T = get_turf(src) - if(!islist(data["limbs"])) - throw EXCEPTION("Expected a limbs list, but found none") - - if(islist(data["dna"])) - dna.deserialize(data["dna"]) - real_name = dna.real_name - name = real_name - set_species(dna.species.type, skip_same_check = TRUE) - age = data["age"] - undershirt = data["ushirt"] - underwear = data["uwear"] - socks = data["socks"] - for(var/obj/item/organ/internal/iorgan in internal_organs) - qdel(iorgan) - - for(var/obj/item/organ/external/organ in bodyparts) - qdel(organ) - - for(var/limb in limbs_list) - // Missing means skip this part - it's missing - if(limbs_list[limb] == "missing") - continue - // "New" code handles insertion and DNA sync'ing - var/obj/item/organ/external/E = list_to_object(limbs_list[limb], src) - E.sync_colour_to_dna() - - for(var/organ in organs_list) - // As above, "New" code handles insertion, DNA sync - list_to_object(organs_list[organ], src) - - UpdateAppearance() - - // De-serialize equipment - // #1: Jumpsuit - // #2: Outer suit - // #3+: Everything else - if(islist(equip_list[slot_w_uniform])) - var/obj/item/clothing/C = list_to_object(equip_list[slot_w_uniform], T) - equip_to_slot_if_possible(C, slot_w_uniform) - - if(islist(equip_list[slot_wear_suit])) - var/obj/item/clothing/C = list_to_object(equip_list[slot_wear_suit], T) - equip_to_slot_if_possible(C, slot_wear_suit) - - for(var/i = 1, i < slots_amt, i++) - if(i == slot_w_uniform || i == slot_wear_suit) - continue - if(islist(equip_list[i])) - var/obj/item/clothing/C = list_to_object(equip_list[i], T) - equip_to_slot_if_possible(C, i) - update_icons() - - ..() - - -/mob/living/carbon/human/vv_get_dropdown() - . = ..() - . += "---" - .["Set Species"] = "?_src_=vars;setspecies=[UID()]" - .["Copy Outfit"] = "?_src_=vars;copyoutfit=[UID()]" - .["Make AI"] = "?_src_=vars;makeai=[UID()]" - .["Make cyborg"] = "?_src_=vars;makerobot=[UID()]" - .["Make monkey"] = "?_src_=vars;makemonkey=[UID()]" - .["Make alien"] = "?_src_=vars;makealien=[UID()]" - .["Make slime"] = "?_src_=vars;makeslime=[UID()]" - .["Make superhero"] = "?_src_=vars;makesuper=[UID()]" - . += "---" - -/mob/living/carbon/human/adjust_nutrition(change) - if(NO_HUNGER in dna.species.species_traits) - return FALSE - return ..() - -/mob/living/carbon/human/set_nutrition(change) - if(NO_HUNGER in dna.species.species_traits) - return FALSE - return ..() - -/mob/living/carbon/human/proc/special_post_clone_handling() - if(mind && mind.assigned_role == "Cluwne") //HUNKE your suffering never stops - makeCluwne() - -/mob/living/carbon/human/proc/influenceSin() - var/datum/objective/sintouched/O - switch(rand(1,7))//traditional seven deadly sins... except lust. - if(1) // acedia - log_game("[src] was influenced by the sin of Acedia.") - O = new /datum/objective/sintouched/acedia - if(2) // Gluttony - log_game("[src] was influenced by the sin of gluttony.") - O = new /datum/objective/sintouched/gluttony - if(3) // Greed - log_game("[src] was influenced by the sin of greed.") - O = new /datum/objective/sintouched/greed - if(4) // sloth - log_game("[src] was influenced by the sin of sloth.") - O = new /datum/objective/sintouched/sloth - if(5) // Wrath - log_game("[src] was influenced by the sin of wrath.") - O = new /datum/objective/sintouched/wrath - if(6) // Envy - log_game("[src] was influenced by the sin of envy.") - O = new /datum/objective/sintouched/envy - if(7) // Pride - log_game("[src] was influenced by the sin of pride.") - O = new /datum/objective/sintouched/pride - SSticker.mode.sintouched += src.mind - src.mind.objectives += O - var/obj_count = 1 - to_chat(src, "[pick(GLOB.boo_phrases)]") - -/mob/living/carbon/human/extinguish_light() - // Parent function handles stuff the human may be holding - ..() - - var/obj/item/organ/internal/lantern/O = get_int_organ(/obj/item/organ/internal/lantern) - if(O && O.glowing) - O.toggle_biolum(TRUE) - visible_message("[src] is engulfed in shadows and fades into the darkness.", "A sense of dread washes over you as you suddenly dim dark.") \ No newline at end of file +/mob/living/carbon/human + name = "unknown" + real_name = "unknown" + voice_name = "unknown" + icon = 'icons/mob/human.dmi' + icon_state = "body_m_s" + deathgasp_on_death = TRUE + var/obj/item/rig/wearing_rig // This is very not good, but it's much much better than calling get_rig() every update_canmove() call. + +/mob/living/carbon/human/New(loc) + icon = null // This is now handled by overlays -- we just keep an icon for the sake of the map editor. + if(length(args) > 1) + log_runtime(EXCEPTION("human/New called with more than 1 argument (REPORT THIS ENTIRE RUNTIME TO A CODER)")) + . = ..() + +/mob/living/carbon/human/Initialize(mapload, datum/species/new_species = /datum/species/human) + if(!dna) + dna = new /datum/dna(null) + // Species name is handled by set_species() + + set_species(new_species, 1, delay_icon_update = 1, skip_same_check = TRUE) + + ..() + + if(dna.species) + real_name = dna.species.get_random_name(gender) + name = real_name + if(mind) + mind.name = real_name + + create_reagents(330) + + martial_art = GLOB.default_martial_art + + handcrafting = new() + + // Set up DNA. + if(dna) + dna.ready_dna(src) + dna.real_name = real_name + sync_organ_dna(1) + + UpdateAppearance() + +/mob/living/carbon/human/OpenCraftingMenu() + handcrafting.ui_interact(src) + +/mob/living/carbon/human/prepare_data_huds() + //Update med hud images... + ..() + //...sec hud images... + sec_hud_set_ID() + sec_hud_set_implants() + sec_hud_set_security_status() + //...and display them. + add_to_all_human_data_huds() + +/mob/living/carbon/human/Destroy() + . = ..() + SSmobs.cubemonkeys -= src + QDEL_LIST(bodyparts) + splinted_limbs.Cut() + +/mob/living/carbon/human/dummy + real_name = "Test Dummy" + status_flags = GODMODE|CANPUSH + +/mob/living/carbon/human/skrell/Initialize(mapload) + ..(mapload, /datum/species/skrell) + +/mob/living/carbon/human/tajaran/Initialize(mapload) + ..(mapload, /datum/species/tajaran) + +/mob/living/carbon/human/vulpkanin/Initialize(mapload) + ..(mapload, /datum/species/vulpkanin) + +/mob/living/carbon/human/unathi/Initialize(mapload) + ..(mapload, /datum/species/unathi) + +/mob/living/carbon/human/vox/Initialize(mapload) + ..(mapload, /datum/species/vox) + +/mob/living/carbon/human/voxarmalis/Initialize(mapload) + ..(mapload, /datum/species/vox/armalis) + +/mob/living/carbon/human/skeleton/Initialize(mapload) + ..(mapload, /datum/species/skeleton) + +/mob/living/carbon/human/kidan/Initialize(mapload) + ..(mapload, /datum/species/kidan) + +/mob/living/carbon/human/plasma/Initialize(mapload) + ..(mapload, /datum/species/plasmaman) + +/mob/living/carbon/human/slime/Initialize(mapload) + ..(mapload, /datum/species/slime) + +/mob/living/carbon/human/grey/Initialize(mapload) + ..(mapload, /datum/species/grey) + +/mob/living/carbon/human/abductor/Initialize(mapload) + ..(mapload, /datum/species/abductor) + +/mob/living/carbon/human/diona/Initialize(mapload) + ..(mapload, /datum/species/diona) + +/mob/living/carbon/human/pod_diona/Initialize(mapload) + ..(mapload, /datum/species/diona/pod) + +/mob/living/carbon/human/machine/Initialize(mapload) + ..(mapload, /datum/species/machine) + +/mob/living/carbon/human/machine/created + name = "Integrated Robotic Chassis" + +/mob/living/carbon/human/machine/created/Initialize(mapload) + ..() + rename_character(null, "Integrated Robotic Chassis ([rand(1, 9999)])") + update_dna() + for(var/obj/item/organ/external/E in bodyparts) + if(istype(E, /obj/item/organ/external/chest) || istype(E, /obj/item/organ/external/groin)) + continue + qdel(E) + for(var/obj/item/organ/O in internal_organs) + qdel(O) + regenerate_icons() + death() + +/mob/living/carbon/human/shadow/Initialize(mapload) + ..(mapload, /datum/species/shadow) + +/mob/living/carbon/human/golem/Initialize(mapload) + ..(mapload, /datum/species/golem) + +/mob/living/carbon/human/wryn/Initialize(mapload) + ..(mapload, /datum/species/wryn) + +/mob/living/carbon/human/nucleation/Initialize(mapload) + ..(mapload, /datum/species/nucleation) + +/mob/living/carbon/human/drask/Initialize(mapload) + ..(mapload, /datum/species/drask) + +/mob/living/carbon/human/monkey/Initialize(mapload) + ..(mapload, /datum/species/monkey) + +/mob/living/carbon/human/farwa/Initialize(mapload) + ..(mapload, /datum/species/monkey/tajaran) + +/mob/living/carbon/human/wolpin/Initialize(mapload) + ..(mapload, /datum/species/monkey/vulpkanin) + +/mob/living/carbon/human/neara/Initialize(mapload) + ..(mapload, /datum/species/monkey/skrell) + +/mob/living/carbon/human/stok/Initialize(mapload) + ..(mapload, /datum/species/monkey/unathi) + +/mob/living/carbon/human/Stat() + ..() + statpanel("Status") + + stat(null, "Intent: [a_intent]") + stat(null, "Move Mode: [m_intent]") + + show_stat_emergency_shuttle_eta() + + if(client.statpanel == "Status") + var/total_user_contents = GetAllContents() // cache it + if(locate(/obj/item/gps) in total_user_contents) + var/turf/T = get_turf(src) + stat(null, "GPS: [COORD(T)]") + if(locate(/obj/item/assembly/health) in total_user_contents) + stat(null, "Health: [health]") + if(internal) + if(!internal.air_contents) + qdel(internal) + else + stat("Internal Atmosphere Info", internal.name) + stat("Tank Pressure", internal.air_contents.return_pressure()) + stat("Distribution Pressure", internal.distribute_pressure) + + if(istype(back, /obj/item/rig)) + var/obj/item/rig/suit = back + var/cell_status = "ERROR" + if(suit.cell) + cell_status = "[suit.cell.charge]/[suit.cell.maxcharge]" + stat(null, "Suit charge: [cell_status]") + + // I REALLY need to split up status panel things into datums + var/mob/living/simple_animal/borer/B = has_brain_worms() + if(B && B.controlling) + stat("Chemicals", B.chemicals) + + if(mind) + if(mind.changeling) + stat("Chemical Storage", "[mind.changeling.chem_charges]/[mind.changeling.chem_storage]") + stat("Absorbed DNA", mind.changeling.absorbedcount) + + if(mind.vampire) + stat("Total Blood", "[mind.vampire.bloodtotal]") + stat("Usable Blood", "[mind.vampire.bloodusable]") + + if(istype(loc, /obj/spacepod)) // Spacdpods! + var/obj/spacepod/S = loc + stat("Spacepod Charge", "[istype(S.battery) ? "[(S.battery.charge / S.battery.maxcharge) * 100]" : "No cell detected"]") + stat("Spacepod Integrity", "[!S.health ? "0" : "[(S.health / initial(S.health)) * 100]"]%") + +/mob/living/carbon/human/ex_act(severity) + var/shielded = 0 + var/b_loss = null + var/f_loss = null + + if(status_flags & GODMODE) + return 0 + + switch(severity) + if(1) + b_loss += 500 + if(!prob(getarmor(null, "bomb"))) + gib() + return 0 + else + var/atom/target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src))) + throw_at(target, 200, 4) + + var/limbs_affected = pick(2,3,4) + var/obj/item/organ/external/processing_dismember + var/list/valid_limbs = bodyparts.Copy() + + while(limbs_affected != 0 && valid_limbs.len > 0) + processing_dismember = pick(valid_limbs) + if(processing_dismember.limb_name != "chest" && processing_dismember.limb_name != "head" && processing_dismember.limb_name != "groin") + processing_dismember.droplimb(1,DROPLIMB_SHARP,0,1) + valid_limbs -= processing_dismember + limbs_affected -= 1 + else valid_limbs -= processing_dismember + + if(2) + if(!shielded) //literally nothing could change shielded before this so wth + b_loss += 60 + + f_loss += 60 + + var/limbs_affected = 0 + var/obj/item/organ/external/processing_dismember + var/list/valid_limbs = bodyparts.Copy() + + if(prob(getarmor(null, "bomb"))) + b_loss = b_loss/1.5 + f_loss = f_loss/1.5 + + limbs_affected = pick(1, 1, 2) + else + limbs_affected = pick(1, 2, 3) + + while(limbs_affected != 0 && valid_limbs.len > 0) + processing_dismember = pick(valid_limbs) + if(processing_dismember.limb_name != "chest" && processing_dismember.limb_name != "head" && processing_dismember.limb_name != "groin") + processing_dismember.droplimb(1,DROPLIMB_SHARP,0,1) + valid_limbs -= processing_dismember + limbs_affected -= 1 + else valid_limbs -= processing_dismember + + if(!istype(l_ear, /obj/item/clothing/ears/earmuffs) && !istype(r_ear, /obj/item/clothing/ears/earmuffs)) + AdjustEarDamage(30, 120) + if(prob(70) && !shielded) + Paralyse(10) + + if(3) + b_loss += 30 + if(prob(getarmor(null, "bomb"))) + b_loss = b_loss/2 + + else + + var/limbs_affected = pick(0, 1) + var/obj/item/organ/external/processing_dismember + var/list/valid_limbs = bodyparts.Copy() + + while(limbs_affected != 0 && valid_limbs.len > 0) + processing_dismember = pick(valid_limbs) + if(processing_dismember.limb_name != "chest" && processing_dismember.limb_name != "head" && processing_dismember.limb_name != "groin") + processing_dismember.droplimb(1,DROPLIMB_SHARP,0,1) + valid_limbs -= processing_dismember + limbs_affected -= 1 + else valid_limbs -= processing_dismember + + if(!istype(l_ear, /obj/item/clothing/ears/earmuffs) && !istype(r_ear, /obj/item/clothing/ears/earmuffs)) + AdjustEarDamage(15, 60) + if(prob(50) && !shielded) + Paralyse(10) + + take_overall_damage(b_loss,f_loss, TRUE, used_weapon = "Explosive Blast") + + ..() + +/mob/living/carbon/human/blob_act(obj/structure/blob/B) + if(stat == DEAD) + return + show_message("The blob attacks you!") + var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") + var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) + apply_damage(5, BRUTE, affecting, run_armor_check(affecting, "melee")) + +/mob/living/carbon/human/bullet_act() + if(martial_art && martial_art.deflection_chance) //Some martial arts users can deflect projectiles! + if(!prob(martial_art.deflection_chance)) + return ..() + if(!src.lying && !(HULK in mutations)) //But only if they're not lying down, and hulks can't do it + visible_message("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons!", "You deflect the projectile!") + return 0 + ..() + +/mob/living/carbon/human/get_restraining_item() + . = ..() + if(!. && istype(wear_suit, /obj/item/clothing/suit/straight_jacket)) + . = wear_suit + +/mob/living/carbon/human/var/temperature_resistance = T0C+75 + + +/mob/living/carbon/human/show_inv(mob/user) + user.set_machine(src) + var/has_breathable_mask = istype(wear_mask, /obj/item/clothing/mask) || get_organ_slot("breathing_tube") + var/list/obscured = check_obscured_slots() + + var/dat = {" + + + "} + + dat += "" + + dat += "" + + var/obj/item/organ/internal/headpocket/C = get_int_organ(/obj/item/organ/internal/headpocket) + if(C) + if(slot_wear_mask in obscured) + dat += "" + else + var/list/items = C.get_contents() + if(items.len) + dat += "" + else + dat += "" + + if(slot_wear_mask in obscured) + dat += "" + else + dat += "" + + if(!issmall(src)) + if(slot_glasses in obscured) + dat += "" + else + dat += "" + + if(slot_l_ear in obscured) + dat += "" + else + dat += "" + + if(slot_r_ear in obscured) + dat += "" + else + dat += "" + + dat += "" + + dat += "" + if(wear_suit) + dat += "" + else + dat += "" + + if(slot_shoes in obscured) + dat += "" + else + dat += "" + + if(slot_gloves in obscured) + dat += "" + else + dat += "" + + if(slot_w_uniform in obscured) + dat += "" + else + dat += "" + + if((w_uniform == null && !(dna && dna.species.nojumpsuit)) || (slot_w_uniform in obscured)) + dat += "" + dat += "" + dat += "" + dat += "" + dat += "" + else + dat += "" + // Pockets + dat += "" + dat += "" + dat += "" + + if(istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + dat += "" + + if(U.accessories.len) + dat += "" + + + if(handcuffed) + dat += "" + if(legcuffed) + dat += "" + + dat += {"
    Left Hand:[(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "Empty"]
    Right Hand:[(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "Empty"]
     
    Back:[(back && !(back.flags&ABSTRACT)) ? back : "Empty"]" + if(has_breathable_mask && istype(back, /obj/item/tank)) + dat += " [internal ? "Disable Internals" : "Set Internals"]" + + dat += "
     
    Head:[(head && !(head.flags&ABSTRACT)) ? head : "Empty"]
     ↳Headpocket:Obscured
     ↳Headpocket:Dislodge Items
     ↳Headpocket:Empty
    Mask:Obscured
    Mask:[(wear_mask && !(wear_mask.flags&ABSTRACT)) ? wear_mask : "Empty"]" + + if(istype(wear_mask, /obj/item/clothing/mask/muzzle)) + var/obj/item/clothing/mask/muzzle/M = wear_mask + if(M.security_lock) + dat += " [M.locked ? "Disable Lock" : "Set Lock"]" + + dat += "
     
    Eyes:Obscured
    Eyes:[(glasses && !(glasses.flags&ABSTRACT)) ? glasses : "Empty"]
    Left Ear:Obscured
    Left Ear:[(l_ear && !(l_ear.flags&ABSTRACT)) ? l_ear : "Empty"]
    Right Ear:Obscured
    Right Ear:[(r_ear && !(r_ear.flags&ABSTRACT)) ? r_ear : "Empty"]
     
    Exosuit:[(wear_suit && !(wear_suit.flags&ABSTRACT)) ? wear_suit : "Empty"]
     ↳Suit Storage:[(s_store && !(s_store.flags&ABSTRACT)) ? s_store : "Empty"]" + if(has_breathable_mask && istype(s_store, /obj/item/tank)) + dat += " [internal ? "Disable Internals" : "Set Internals"]" + dat += "
     ↳Suit Storage:
    Shoes:Obscured
    Shoes:[(shoes && !(shoes.flags&ABSTRACT)) ? shoes : "Empty"]
    Gloves:Obscured
    Gloves:[(gloves && !(gloves.flags&ABSTRACT)) ? gloves : "Empty"]
    Uniform:Obscured
    Uniform:[(w_uniform && !(w_uniform.flags&ABSTRACT)) ? w_uniform : "Empty"]
     ↳Pockets:
     ↳ID:
     ↳Belt:
     ↳Suit Sensors:
     ↳PDA:
     ↳Belt:[(belt && !(belt.flags&ABSTRACT)) ? belt : "Empty"]" + if(has_breathable_mask && istype(belt, /obj/item/tank)) + dat += " [internal ? "Disable Internals" : "Set Internals"]" + dat += "
     ↳Pockets:" + if(l_store && internal && l_store == internal) + dat += "[l_store]" + else if(l_store && !(l_store.flags&ABSTRACT)) + dat += "Left (Full)" + else + dat += "Left (Empty)" + dat += " " + if(r_store && internal && r_store == internal) + dat += "[r_store]" + else if(r_store && !(r_store.flags&ABSTRACT)) + dat += "Right (Full)" + else + dat += "Right (Empty)" + dat += "
     ↳ID:[(wear_id && !(wear_id.flags&ABSTRACT)) ? wear_id : "Empty"]
     ↳PDA:[(wear_pda && !(wear_pda.flags&ABSTRACT)) ? wear_pda : "Empty"]
     ↳Suit Sensors:[U.has_sensor >= 2 ? "--SENSORS LOCKED--" : "Set Sensors"]
     ↳Remove Accessory
    Handcuffed: Remove
    Legcuffed
    + Close + "} + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 540) + popup.set_content(dat) + popup.open() + +// Get rank from ID, ID inside PDA, PDA, ID in wallet, etc. +/mob/living/carbon/human/proc/get_authentification_rank(var/if_no_id = "No id", var/if_no_job = "No job") + var/obj/item/pda/pda = wear_id + if(istype(pda)) + if(pda.id) + return pda.id.rank + else + return pda.ownrank + else + var/obj/item/card/id/id = get_idcard() + if(id) + return id.rank ? id.rank : if_no_job + else + return if_no_id + +//gets assignment from ID or ID inside PDA or PDA itself +//Useful when player do something with computers +/mob/living/carbon/human/proc/get_assignment(var/if_no_id = "No id", var/if_no_job = "No job") + var/obj/item/pda/pda = wear_id + var/obj/item/card/id/id = wear_id + if(istype(pda)) + if(pda.id && istype(pda.id, /obj/item/card/id)) + . = pda.id.assignment + else + . = pda.ownjob + else if(istype(id)) + . = id.assignment + else + return if_no_id + if(!.) + . = if_no_job + return + +//gets name from ID or ID inside PDA or PDA itself +//Useful when player do something with computers +/mob/living/carbon/human/proc/get_authentification_name(var/if_no_id = "Unknown") + var/obj/item/pda/pda = wear_id + var/obj/item/card/id/id = wear_id + if(istype(pda)) + if(pda.id) + . = pda.id.registered_name + else + . = pda.owner + else if(istype(id)) + . = id.registered_name + else + return if_no_id + return + +//repurposed proc. Now it combines get_id_name() and get_face_name() to determine a mob's name variable. Made into a seperate proc as it'll be useful elsewhere +/mob/living/carbon/human/get_visible_name(var/id_override = FALSE) + if(name_override) + return name_override + if(wear_mask && (wear_mask.flags_inv & HIDEFACE)) //Wearing a mask which hides our face, use id-name if possible + return get_id_name("Unknown") + if(head && (head.flags_inv & HIDEFACE)) + return get_id_name("Unknown") //Likewise for hats + var/face_name = get_face_name() + var/id_name = get_id_name("") + if(id_name && (id_name != face_name) && !id_override) + return "[face_name] (as [id_name])" + return face_name + +//Returns "Unknown" if facially disfigured and real_name if not. Useful for setting name when polyacided or when updating a human's name variable +/mob/living/carbon/human/proc/get_face_name() + var/obj/item/organ/external/head = get_organ("head") + if(!head || head.disfigured || cloneloss > 50 || !real_name || (HUSK in mutations)) //disfigured. use id-name if possible + return "Unknown" + return real_name + +//gets name from ID or PDA itself, ID inside PDA doesn't matter +//Useful when player is being seen by other mobs +/mob/living/carbon/human/proc/get_id_name(var/if_no_id = "Unknown") + var/obj/item/pda/pda = wear_id + var/obj/item/card/id/id = wear_id + if(istype(pda)) . = pda.owner + else if(istype(id)) . = id.registered_name + if(!.) . = if_no_id //to prevent null-names making the mob unclickable + return + +//gets ID card object from special clothes slot or, if applicable, hands as well +/mob/living/carbon/human/proc/get_idcard(var/check_hands = FALSE) + var/obj/item/card/id/id = wear_id + var/obj/item/pda/pda = wear_id + if(istype(pda) && pda.id) + id = pda.id + + if(check_hands) + if(istype(get_active_hand(), /obj/item/card/id)) + id = get_active_hand() + else if(istype(get_inactive_hand(), /obj/item/card/id)) + id = get_inactive_hand() + + if(istype(id)) + return id + +/mob/living/carbon/human/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + dna.species.update_sight(src) + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() + +//Added a safety check in case you want to shock a human mob directly through electrocute_act. +/mob/living/carbon/human/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + if(tesla_shock) + var/total_coeff = 1 + if(gloves) + var/obj/item/clothing/gloves/G = gloves + if(G.siemens_coefficient <= 0) + total_coeff -= 0.5 + if(wear_suit) + var/obj/item/clothing/suit/S = wear_suit + if(S.siemens_coefficient <= 0) + total_coeff -= 0.95 + else if(S.siemens_coefficient == (-1)) + total_coeff -= 1 + siemens_coeff = total_coeff + if(tesla_ignore) + siemens_coeff = 0 + else if(!safety) + var/gloves_siemens_coeff = 1 + if(gloves) + var/obj/item/clothing/gloves/G = gloves + gloves_siemens_coeff = G.siemens_coefficient + siemens_coeff = gloves_siemens_coeff + if(undergoing_cardiac_arrest() && !illusion) + if(shock_damage * siemens_coeff >= 1 && prob(25)) + set_heartattack(FALSE) + if(stat == CONSCIOUS) + to_chat(src, "You feel your heart beating again!") + + dna.species.spec_electrocute_act(src, shock_damage, source, siemens_coeff, safety, override, tesla_shock, illusion, stun) + . = ..(shock_damage, source, siemens_coeff, safety, override, tesla_shock, illusion, stun) + +/mob/living/carbon/human/Topic(href, href_list) + if(!usr.stat && usr.canmove && !usr.restrained() && in_range(src, usr)) + var/thief_mode = 0 + if(ishuman(usr)) + var/mob/living/carbon/human/H = usr + var/obj/item/clothing/gloves/G = H.gloves + if(G && G.pickpocket) + thief_mode = 1 + + if(href_list["embedded_object"]) + var/obj/item/organ/external/L = locate(href_list["embedded_limb"]) in bodyparts + if(!L) + return + var/obj/item/I = locate(href_list["embedded_object"]) in L.embedded_objects + if(!I || I.loc != src) //no item, no limb, or item is not in limb or in the person anymore + return + var/time_taken = I.embedded_unsafe_removal_time*I.w_class + usr.visible_message("[usr] attempts to remove [I] from [usr.p_their()] [L.name].","You attempt to remove [I] from your [L.name]... (It will take [time_taken/10] seconds.)") + if(do_after(usr, time_taken, needhand = 1, target = src)) + if(!I || !L || I.loc != src || !(I in L.embedded_objects)) + return + L.embedded_objects -= I + L.receive_damage(I.embedded_unsafe_removal_pain_multiplier*I.w_class)//It hurts to rip it out, get surgery you dingus. + I.forceMove(get_turf(src)) + usr.put_in_hands(I) + usr.emote("scream") + usr.visible_message("[usr] successfully rips [I] out of [usr.p_their()] [L.name]!","You successfully remove [I] from your [L.name].") + if(!has_embedded_objects()) + clear_alert("embeddedobject") + return + + if(href_list["item"]) + var/slot = text2num(href_list["item"]) + if(slot in check_obscured_slots()) + to_chat(usr, "You can't reach that! Something is covering it.") + return + + if(href_list["pockets"]) + var/pocket_side = href_list["pockets"] + var/pocket_id = (pocket_side == "right" ? slot_r_store : slot_l_store) + var/obj/item/pocket_item = (pocket_id == slot_r_store ? r_store : l_store) + var/obj/item/place_item = usr.get_active_hand() // Item to place in the pocket, if it's empty + + var/delay_denominator = 1 + if(pocket_item && !(pocket_item.flags&ABSTRACT)) + if(pocket_item.flags & NODROP) + to_chat(usr, "You try to empty [src]'s [pocket_side] pocket, it seems to be stuck!") + to_chat(usr, "You try to empty [src]'s [pocket_side] pocket.") + else if(place_item && place_item.mob_can_equip(src, pocket_id, 1) && !(place_item.flags&ABSTRACT)) + to_chat(usr, "You try to place [place_item] into [src]'s [pocket_side] pocket.") + delay_denominator = 4 + else + return + + if(do_mob(usr, src, POCKET_STRIP_DELAY/delay_denominator)) //placing an item into the pocket is 4 times faster + if(pocket_item) + if(pocket_item == (pocket_id == slot_r_store ? r_store : l_store)) //item still in the pocket we search + unEquip(pocket_item) + if(thief_mode) + usr.put_in_hands(pocket_item) + add_attack_logs(usr, src, "Stripped of [pocket_item]", isLivingSSD(src) ? null : ATKLOG_ALL) + else + if(place_item) + usr.unEquip(place_item) + equip_to_slot_if_possible(place_item, pocket_id, 0, 1) + add_attack_logs(usr, src, "Equipped with [place_item]", isLivingSSD(src) ? null : ATKLOG_ALL) + + // Update strip window + if(usr.machine == src && in_range(src, usr)) + show_inv(usr) + else + // Display a warning if the user mocks up if they don't have pickpocket gloves. + if(!thief_mode) + to_chat(src, "You feel your [pocket_side] pocket being fumbled with!") + add_attack_logs(usr, src, "Attempted strip of [pocket_item]", isLivingSSD(src) ? null : ATKLOG_ALL) + + if(href_list["set_sensor"]) + if(istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + U.set_sensors(usr) + + if(href_list["dislodge_headpocket"]) + usr.visible_message("[usr] is trying to remove something from [src]'s head!", + "You start to dislodge whatever's inside [src]'s headpocket!") + if(do_mob(usr, src, POCKET_STRIP_DELAY)) + usr.visible_message("[usr] has dislodged something from [src]'s head!", + "You have dislodged everything from [src]'s headpocket!") + var/obj/item/organ/internal/headpocket/C = get_int_organ(/obj/item/organ/internal/headpocket) + C.empty_contents() + add_attack_logs(usr, src, "Stripped of headpocket items", isLivingSSD(src) ? null : ATKLOG_ALL) + + if(href_list["strip_accessory"]) + if(istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + if(U.accessories.len) + var/obj/item/clothing/accessory/A = U.accessories[1] + if(!thief_mode) + usr.visible_message("\The [usr] starts to take off \the [A] from \the [src]'s [U]!", \ + "You start to take off \the [A] from \the [src]'s [U]!") + + if(do_mob(usr, src, 40) && A && U.accessories.len) + if(!thief_mode) + usr.visible_message("\The [usr] takes \the [A] off of \the [src]'s [U]!", \ + "You take \the [A] off of \the [src]'s [U]!") + A.on_removed(usr) + U.accessories -= A + update_inv_w_uniform() + + if(href_list["criminal"]) + if(hasHUD(usr,"security")) + if(usr.incapacitated()) + return + var/found_record = 0 + var/perpname = get_visible_name(TRUE) + + if(perpname != "Unknown") + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.security) + if(R.fields["id"] == E.fields["id"]) + + var/setcriminal = input(usr, "Specify a new criminal status for this person.", "Security HUD", R.fields["criminal"]) in list("None", "*Arrest*", "Incarcerated", "Parolled", "Released", "Cancel") + var/t1 = copytext(trim(sanitize(input("Enter Reason:", "Security HUD", null, null) as text)), 1, MAX_MESSAGE_LEN) + if(!t1) + t1 = "(none)" + + if(hasHUD(usr, "security") && setcriminal != "Cancel") + found_record = 1 + if(R.fields["criminal"] == "*Execute*") + to_chat(usr, "Unable to modify the sec status of a person with an active Execution order. Use a security computer instead.") + else + var/rank + if(ishuman(usr)) + var/mob/living/carbon/human/U = usr + rank = U.get_assignment() + else if(isrobot(usr)) + var/mob/living/silicon/robot/U = usr + rank = "[U.modtype] [U.braintype]" + else if(isAI(usr)) + rank = "AI" + set_criminal_status(usr, R, setcriminal, t1, rank) + break // Git out of the securiy records loop! + if(found_record) + break // Git out of the general records + + if(!found_record) + to_chat(usr, "Unable to locate a data core entry for this person.") + + if(href_list["secrecord"]) + if(hasHUD(usr,"security")) + if(usr.incapacitated()) + return + var/perpname = get_visible_name(TRUE) + var/read = 0 + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.security) + if(R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"security")) + to_chat(usr, "Name: [R.fields["name"]] Criminal Status: [R.fields["criminal"]]") + to_chat(usr, "Minor Crimes: [R.fields["mi_crim"]]") + to_chat(usr, "Details: [R.fields["mi_crim_d"]]") + to_chat(usr, "Major Crimes: [R.fields["ma_crim"]]") + to_chat(usr, "Details: [R.fields["ma_crim_d"]]") + to_chat(usr, "Notes: [R.fields["notes"]]") + to_chat(usr, "\[View Comment Log\]") + read = 1 + + if(!read) + to_chat(usr, "Unable to locate a data core entry for this person.") + + if(href_list["secrecordComment"]) + if(hasHUD(usr,"security")) + if(usr.incapacitated()) + return + var/perpname = get_visible_name(TRUE) + var/read = 0 + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.security) + if(R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"security")) + read = 1 + if(LAZYLEN(R.fields["comments"])) + for(var/c in R.fields["comments"]) + to_chat(usr, c) + else + to_chat(usr, "No comment found") + to_chat(usr, "\[Add comment\]") + + if(!read) + to_chat(usr, "Unable to locate a data core entry for this person.") + + if(href_list["secrecordadd"]) + if(hasHUD(usr,"security")) + if(usr.incapacitated()) + return + var/perpname = get_visible_name(TRUE) + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.security) + if(R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"security")) + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Sec. records", null, null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || usr.stat || usr.restrained() || !hasHUD(usr, "security")) + return + if(ishuman(usr)) + var/mob/living/carbon/human/U = usr + R.fields["comments"] += "Made by [U.get_authentification_name()] ([U.get_assignment()]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + if(isrobot(usr)) + var/mob/living/silicon/robot/U = usr + R.fields["comments"] += "Made by [U.name] ([U.modtype] [U.braintype]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + if(isAI(usr)) + var/mob/living/silicon/ai/U = usr + R.fields["comments"] += "Made by [U.name] (artificial intelligence) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + + if(href_list["medical"]) + if(hasHUD(usr,"medical")) + if(usr.incapacitated()) + return + var/modified = 0 + var/perpname = get_visible_name(TRUE) + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.general) + if(R.fields["id"] == E.fields["id"]) + var/setmedical = input(usr, "Specify a new medical status for this person.", "Medical HUD", R.fields["p_stat"]) in list("*SSD*", "*Deceased*", "Physically Unfit", "Active", "Disabled", "Cancel") + + if(hasHUD(usr,"medical")) + if(setmedical != "Cancel") + R.fields["p_stat"] = setmedical + modified = 1 + if(GLOB.PDA_Manifest.len) + GLOB.PDA_Manifest.Cut() + + spawn() + sec_hud_set_security_status() + + if(!modified) + to_chat(usr, "Unable to locate a data core entry for this person.") + + if(href_list["medrecord"]) + if(hasHUD(usr,"medical")) + if(usr.incapacitated()) + return + var/read = 0 + var/perpname = get_visible_name(TRUE) + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"medical")) + to_chat(usr, "Name: [R.fields["name"]] Blood Type: [R.fields["b_type"]]") + to_chat(usr, "DNA: [R.fields["b_dna"]]") + to_chat(usr, "Minor Disabilities: [R.fields["mi_dis"]]") + to_chat(usr, "Details: [R.fields["mi_dis_d"]]") + to_chat(usr, "Major Disabilities: [R.fields["ma_dis"]]") + to_chat(usr, "Details: [R.fields["ma_dis_d"]]") + to_chat(usr, "Notes: [R.fields["notes"]]") + to_chat(usr, "\[View Comment Log\]") + read = 1 + + if(!read) + to_chat(usr, "Unable to locate a data core entry for this person.") + + if(href_list["medrecordComment"]) + if(hasHUD(usr,"medical")) + if(usr.incapacitated()) + return + var/perpname = get_visible_name(TRUE) + var/read = 0 + + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"medical")) + read = 1 + if(LAZYLEN(R.fields["comments"])) + for(var/c in R.fields["comments"]) + to_chat(usr, c) + else + to_chat(usr, "No comment found") + to_chat(usr, "\[Add comment\]") + + if(!read) + to_chat(usr, "Unable to locate a data core entry for this person.") + + if(href_list["medrecordadd"]) + if(hasHUD(usr,"medical")) + if(usr.incapacitated()) + return + var/perpname = get_visible_name(TRUE) + for(var/datum/data/record/E in GLOB.data_core.general) + if(E.fields["name"] == perpname) + for(var/datum/data/record/R in GLOB.data_core.medical) + if(R.fields["id"] == E.fields["id"]) + if(hasHUD(usr,"medical")) + var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || usr.stat || usr.restrained() || !hasHUD(usr, "medical")) + return + if(ishuman(usr)) + var/mob/living/carbon/human/U = usr + R.fields["comments"] += "Made by [U.get_authentification_name()] ([U.get_assignment()]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + if(isrobot(usr)) + var/mob/living/silicon/robot/U = usr + R.fields["comments"] += "Made by [U.name] ([U.modtype] [U.braintype]) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + if(isAI(usr)) + var/mob/living/silicon/ai/U = usr + R.fields["comments"] += "Made by [U.name] (artificial intelligence) on [GLOB.current_date_string] [station_time_timestamp()]
    [t1]" + + if(href_list["lookitem"]) + var/obj/item/I = locate(href_list["lookitem"]) + src.examinate(I) + + if(href_list["lookmob"]) + var/mob/M = locate(href_list["lookmob"]) + src.examinate(M) + . = ..() + + +///check_eye_prot() +///Returns a number between -1 to 2 +/mob/living/carbon/human/check_eye_prot() + var/number = ..() + if(istype(head, /obj/item/clothing/head)) //are they wearing something on their head + var/obj/item/clothing/head/HFP = head //if yes gets the flash protection value from that item + number += HFP.flash_protect + if(istype(glasses, /obj/item/clothing/glasses)) //glasses + var/obj/item/clothing/glasses/GFP = glasses + number += GFP.flash_protect + if(istype(wear_mask, /obj/item/clothing/mask)) //mask + var/obj/item/clothing/mask/MFP = wear_mask + number += MFP.flash_protect + for(var/obj/item/organ/internal/cyberimp/eyes/EFP in internal_organs) + number += EFP.flash_protect + + return number + +/mob/living/carbon/human/check_ear_prot() + if(head && (head.flags & HEADBANGPROTECT)) + return 1 + if(l_ear && (l_ear.flags & EARBANGPROTECT)) + return 1 + if(r_ear && (r_ear.flags & EARBANGPROTECT)) + return 1 + +///tintcheck() +///Checks eye covering items for visually impairing tinting, such as welding masks +///Checked in life.dm. 0 & 1 = no impairment, 2 = welding mask overlay, 3 = You can see jack, but you can't see shit. +/mob/living/carbon/human/tintcheck() + var/tinted = 0 + if(istype(src.head, /obj/item/clothing/head)) + var/obj/item/clothing/head/HT = src.head + tinted += HT.tint + if(istype(src.glasses, /obj/item/clothing/glasses)) + var/obj/item/clothing/glasses/GT = src.glasses + tinted += GT.tint + if(istype(src.wear_mask, /obj/item/clothing/mask)) + var/obj/item/clothing/mask/MT = src.wear_mask + tinted += MT.tint + + //god help me + if(istype(back, /obj/item/rig)) + var/obj/item/rig/O = back + if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & HEAD)) + if((O.offline && O.offline_vision_restriction == 1) || (!O.offline && O.vision_restriction == 1)) + tinted = 2 + if((O.offline && O.offline_vision_restriction == 2) || (!O.offline && O.vision_restriction == 2)) + tinted = 3 + //im so sorry + + return tinted + + +/mob/living/carbon/human/abiotic(var/full_body = 0) + if(full_body && ((src.l_hand && !(src.l_hand.flags & ABSTRACT)) || (src.r_hand && !(src.r_hand.flags & ABSTRACT)) || (src.back || src.wear_mask || src.head || src.shoes || src.w_uniform || src.wear_suit || src.glasses || src.l_ear || src.r_ear || src.gloves))) + return 1 + + if((src.l_hand && !(src.l_hand.flags & ABSTRACT)) || (src.r_hand && !(src.r_hand.flags & ABSTRACT))) + return 1 + + return 0 + + +/mob/living/carbon/human/proc/check_dna() + dna.check_integrity(src) + +/mob/living/carbon/human/proc/play_xylophone() + if(!src.xylophone) + visible_message("[src] begins playing [p_their()] ribcage like a xylophone. It's quite spooky.","You begin to play a spooky refrain on your ribcage.","You hear a spooky xylophone melody.") + var/song = pick('sound/effects/xylophone1.ogg','sound/effects/xylophone2.ogg','sound/effects/xylophone3.ogg') + playsound(loc, song, 50, 1, -1) + xylophone = 1 + spawn(1200) + xylophone=0 + return + +/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone, penetrate_thick = FALSE) + . = 1 + + if(!target_zone) + if(!user) + target_zone = pick("chest","chest","chest","left leg","right leg","left arm", "right arm", "head") + else + target_zone = user.zone_selected + + + if(PIERCEIMMUNE in dna.species.species_traits) + . = 0 + + var/obj/item/organ/external/affecting = get_organ(target_zone) + var/fail_msg + if(!affecting) + . = 0 + fail_msg = "[p_they(TRUE)] [p_are()] missing that limb." + else if(affecting.is_robotic()) + . = 0 + fail_msg = "That limb is robotic." + else + switch(target_zone) + if("head") + if(head && head.flags & THICKMATERIAL && !penetrate_thick) + . = 0 + else + if(wear_suit && wear_suit.flags & THICKMATERIAL && !penetrate_thick) + . = 0 + if(!. && error_msg && user) + if(!fail_msg) + fail_msg = "There is no exposed flesh or thin material [target_zone == "head" ? "on [p_their()] head" : "on [p_their()] body"] to inject into." + to_chat(user, "[fail_msg]") + +/mob/living/carbon/human/proc/check_obscured_slots() + var/list/obscured = list() + + if(wear_suit) + if(wear_suit.flags_inv & HIDEGLOVES) + obscured |= slot_gloves + if(wear_suit.flags_inv & HIDEJUMPSUIT) + obscured |= slot_w_uniform + if(wear_suit.flags_inv & HIDESHOES) + obscured |= slot_shoes + + if(head) + if(head.flags_inv & HIDEMASK) + obscured |= slot_wear_mask + if(head.flags_inv & HIDEEYES) + obscured |= slot_glasses + if(head.flags_inv & HIDEEARS) + obscured |= slot_r_ear + obscured |= slot_l_ear + + if(obscured.len > 0) + return obscured + else + return null + +/mob/living/carbon/human/proc/check_has_mouth() + // Todo, check stomach organ when implemented. + var/obj/item/organ/external/head/H = get_organ("head") + if(!H || !H.can_intake_reagents) + return 0 + return 1 + +/mob/living/carbon/human/proc/get_visible_gender() + var/list/obscured = check_obscured_slots() + var/skipface = (wear_mask && (wear_mask.flags_inv & HIDEFACE)) || (head && (head.flags_inv & HIDEFACE)) + if((slot_w_uniform in obscured) && skipface) + return PLURAL + return gender + +/mob/living/carbon/human/proc/increase_germ_level(n) + if(gloves) + gloves.germ_level += n + else + germ_level += n + +/mob/living/carbon/human/proc/check_and_regenerate_organs(var/mob/living/carbon/human/H) //Regenerates missing limbs/organs. + var/list/types_of_int_organs = list() //This will hold all the types of organs in the mob before rejuvenation. + for(var/obj/item/organ/internal/I in H.internal_organs) + types_of_int_organs |= I.type //Compiling the list of organ types. It is possible for organs to be missing from this list if they are absent from the mob. + + //Clean up limbs + for(var/organ_name in H.bodyparts_by_name) + var/obj/item/organ/organ = H.bodyparts_by_name[organ_name] + if(!organ) //The !organ check is to account for mechanical limb (prostheses) losses, since those are handled in a way that leaves indexed but null list entries instead of stumps. + qdel(organ) + H.bodyparts_by_name -= organ_name //Making sure the list entry is removed. + + //Replacing lost limbs with the species default. + var/mob/living/carbon/human/temp_holder + for(var/limb_type in H.dna.species.has_limbs) + if(!(limb_type in H.bodyparts_by_name)) + var/list/organ_data = H.dna.species.has_limbs[limb_type] + var/limb_path = organ_data["path"] + var/obj/item/organ/external/O = new limb_path(temp_holder) + if(H.get_limb_by_name(O.name)) //Check to see if the user already has an limb with the same name as the 'missing limb'. If they do, skip regrowth. + continue //In an example, this will prevent duplication of the mob's right arm if the mob is a Human and they have a Diona right arm, since, + //while the limb with the name 'right_arm' the mob has may not be listed in their species' bodyparts definition, it is still viable and has the appropriate limb name. + else + O = new limb_path(H) //Create the limb on the player. + O.owner = H + H.bodyparts |= H.bodyparts_by_name[O.limb_name] + if(O.body_part == HEAD) //They're sprouting a fresh head so lets hook them up with their genetic stuff so their new head looks like the original. + H.UpdateAppearance() + + //Replacing lost organs with the species default. + temp_holder = new /mob/living/carbon/human() + var/list/species_organs = H.dna.species.has_organ.Copy() //Compile a list of species organs and tack on the mutantears afterward. + if(H.dna.species.mutantears) + species_organs["ears"] = H.dna.species.mutantears + for(var/index in species_organs) + var/organ = species_organs[index] + if(!(organ in types_of_int_organs)) //If the mob is missing this particular organ... + var/obj/item/organ/internal/I = new organ(temp_holder) //Create the organ inside our holder so we can check it before implantation. + if(H.get_organ_slot(I.slot)) //Check to see if the user already has an organ in the slot the 'missing organ' belongs to. If they do, skip implantation. + continue //In an example, this will prevent duplication of the mob's eyes if the mob is a Human and they have Nucleation eyes, since, + //while the organ in the eyes slot may not be listed in the mob's species' organs definition, it is still viable and fits in the appropriate organ slot. + else + I = new organ(H) //Create the organ inside the player. + I.insert(H) + +/mob/living/carbon/human/revive() + //Fix up all organs and replace lost ones. + restore_all_organs() //Rejuvenate and reset all existing organs. + check_and_regenerate_organs(src) //Regenerate limbs and organs only if they're really missing. + surgeries.Cut() //End all surgeries. + + if(!isskeleton(src) && (SKELETON in mutations)) + mutations.Remove(SKELETON) + if(NOCLONE in mutations) + mutations.Remove(NOCLONE) + if(HUSK in mutations) + mutations.Remove(HUSK) + + if(!client || !key) //Don't boot out anyone already in the mob. + for(var/obj/item/organ/internal/brain/H in world) + if(H.brainmob) + if(H.brainmob.real_name == src.real_name) + if(H.brainmob.mind) + H.brainmob.mind.transfer_to(src) + qdel(H) + + ..() + +/mob/living/carbon/human/proc/is_lung_ruptured() + var/obj/item/organ/internal/lungs/L = get_int_organ(/obj/item/organ/internal/lungs) + if(!L) + return 0 + + return L.is_bruised() + +/mob/living/carbon/human/proc/rupture_lung() + var/obj/item/organ/internal/lungs/L = get_int_organ(/obj/item/organ/internal/lungs) + if(!L) + return 0 + + if(!L.is_bruised()) + custom_pain("You feel a stabbing pain in your chest!") + L.damage = L.min_bruised_damage + +//returns 1 if made bloody, returns 0 otherwise + +/mob/living/carbon/human/clean_blood(var/clean_feet) + .=..() + if(clean_feet && !shoes && istype(feet_blood_DNA, /list) && feet_blood_DNA.len) + feet_blood_color = null + qdel(feet_blood_DNA) + bloody_feet = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + blood_state = BLOOD_STATE_NOT_BLOODY + update_inv_shoes(1) + return 1 + +/mob/living/carbon/human/cuff_resist(obj/item/I) + if(HULK in mutations) + say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + if(..(I, cuff_break = 1)) + unEquip(I) + else + if(..()) + unEquip(I) + +/mob/living/carbon/human/resist_restraints() + if(wear_suit && wear_suit.breakouttime) + changeNext_move(CLICK_CD_BREAKOUT) + last_special = world.time + CLICK_CD_BREAKOUT + cuff_resist(wear_suit) + else + ..() + +/mob/living/carbon/human/generate_name() + name = dna.species.get_random_name(gender) + real_name = name + if(dna) + dna.real_name = name + return name + +/mob/living/carbon/human/verb/check_pulse() + set category = null + set name = "Check pulse" + set desc = "Approximately count somebody's pulse. Requires you to stand still at least 6 seconds." + set src in view(1) + var/self = 0 + + if(usr.stat == 1 || usr.restrained() || !isliving(usr)) return + + if(usr == src) + self = 1 + if(!self) + usr.visible_message("[usr] kneels down, puts [usr.p_their()] hand on [src]'s wrist and begins counting [p_their()] pulse.",\ + "You begin counting [src]'s pulse") + else + usr.visible_message("[usr] begins counting [p_their()] pulse.",\ + "You begin counting your pulse.") + + if(src.pulse) + to_chat(usr, "[self ? "You have a" : "[src] has a"] pulse! Counting...") + else + to_chat(usr, "[src] has no pulse!")//it is REALLY UNLIKELY that a dead person would check his own pulse + + return + + to_chat(usr, "Don't move until counting is finished.") + var/time = world.time + sleep(60) + if(usr.l_move_time >= time) //checks if our mob has moved during the sleep() + to_chat(usr, "You moved while counting. Try again.") + else + to_chat(usr, "[self ? "Your" : "[src]'s"] pulse is [src.get_pulse(GETPULSE_HAND)].") + +/mob/living/carbon/human/proc/set_species(datum/species/new_species, default_colour, delay_icon_update = FALSE, skip_same_check = FALSE, retain_damage = FALSE) + if(!skip_same_check) + if(dna.species.name == initial(new_species.name)) + return + var/datum/species/oldspecies = dna.species + + if(oldspecies) + if(oldspecies.language) + remove_language(oldspecies.language) + + if(oldspecies.default_language) + remove_language(oldspecies.default_language) + + if(gender == PLURAL && oldspecies.has_gender) + change_gender(pick(MALE, FEMALE)) + + if(oldspecies.default_genes.len) + oldspecies.handle_dna(src, TRUE) // Remove any genes that belong to the old species + + oldspecies.on_species_loss(src) + + dna.species = new new_species() + + tail = dna.species.tail + + maxHealth = dna.species.total_health + + if(dna.species.language) + add_language(dna.species.language) + + if(dna.species.default_language) + add_language(dna.species.default_language) + + hunger_drain = dna.species.hunger_drain + digestion_ratio = dna.species.digestion_ratio + + if(dna.species.base_color && default_colour) + //Apply colour. + skin_colour = dna.species.base_color + else + skin_colour = "#000000" + + if(!(dna.species.bodyflags & HAS_SKIN_TONE)) + s_tone = 0 + + var/list/thing_to_check = list(slot_wear_mask, slot_head, slot_shoes, slot_gloves, slot_l_ear, slot_r_ear, slot_glasses, slot_l_hand, slot_r_hand) + var/list/kept_items[0] + var/list/item_flags[0] + for(var/thing in thing_to_check) + var/obj/item/I = get_item_by_slot(thing) + if(I) + kept_items[I] = thing + item_flags[I] = I.flags + I.flags = 0 // Temporary set the flags to 0 + + if(retain_damage) + //Create a list of body parts which are damaged by burn or brute and save them to apply after new organs are generated. First we just handle external organs. + var/bodypart_damages = list() + //Loop through all external organs and save the damage states for brute and burn + for(var/obj/item/organ/external/E in bodyparts) + if(E.brute_dam == 0 && E.burn_dam == 0 && E.internal_bleeding == FALSE) //If there's no damage we don't bother remembering it. + continue + var/brute = E.brute_dam + var/burn = E.burn_dam + var/IB = E.internal_bleeding + var/obj/item/organ/external/OE = new E.type() + var/stats = list(OE, brute, burn, IB) + bodypart_damages += list(stats) + + //Now we do the same for internal organs via the same proceedure. + var/internal_damages = list() + for(var/obj/item/organ/internal/I in internal_organs) + if(I.damage == 0) + continue + var/obj/item/organ/internal/OI = new I.type() + var/damage = I.damage + var/broken = I.is_broken() + var/stats = list(OI, damage, broken) + internal_damages += list(stats) + + //Create the new organs for the species change + dna.species.create_organs(src) + + //Apply relevant damages and variables to the new organs. + for(var/B in bodyparts) + var/obj/item/organ/external/E = B + for(var/list/part in bodypart_damages) + var/obj/item/organ/external/OE = part[1] + if((E.type == OE.type)) // Type has to be explicit, as right limbs are a child of left ones etc. + var/brute = part[2] + var/burn = part[3] + var/IB = part[4] + //Deal the damage to the new organ and then delete the entry to prevent duplicate checks + E.receive_damage(brute, burn, ignore_resists = TRUE) + E.internal_bleeding = IB + qdel(part) + + for(var/O in internal_organs) + var/obj/item/organ/internal/I = O + for(var/list/part in internal_damages) + var/obj/item/organ/internal/OI = part[1] + var/organ_type + + if(OI.parent_type == /obj/item/organ/internal) //Dealing with species organs + organ_type = OI.type + else + organ_type = OI.parent_type + + if(istype(I, organ_type)) + var/damage = part[2] + var/broken = part[3] + I.receive_damage(damage, 1) + if(broken && !(I.status & ORGAN_BROKEN)) + I.status |= ORGAN_BROKEN + qdel(part) + + else + dna.species.create_organs(src) + + for(var/obj/item/thing in kept_items) + equip_to_slot_if_possible(thing, kept_items[thing], redraw_mob = 0) + thing.flags = item_flags[thing] // Reset the flags to the origional ones + + //Handle default hair/head accessories for created mobs. + var/obj/item/organ/external/head/H = get_organ("head") + if(dna.species.default_hair) + H.h_style = dna.species.default_hair + else + H.h_style = "Bald" + if(dna.species.default_fhair) + H.f_style = dna.species.default_fhair + else + H.f_style = "Shaved" + if(dna.species.default_headacc) + H.ha_style = dna.species.default_headacc + else + H.ha_style = "None" + + if(dna.species.default_hair_colour) + //Apply colour. + H.hair_colour = dna.species.default_hair_colour + else + H.hair_colour = "#000000" + if(dna.species.default_fhair_colour) + H.facial_colour = dna.species.default_fhair_colour + else + H.facial_colour = "#000000" + if(dna.species.default_headacc_colour) + H.headacc_colour = dna.species.default_headacc_colour + else + H.headacc_colour = "#000000" + + m_styles = DEFAULT_MARKING_STYLES //Wipes out markings, setting them all to "None". + m_colours = DEFAULT_MARKING_COLOURS //Defaults colour to #00000 for all markings. + body_accessory = null + + dna.real_name = real_name + + dna.species.on_species_gain(src) + + update_sight() + + dna.species.handle_dna(src) //Give them whatever special dna business they got. + + update_client_colour(0) + + if(!delay_icon_update) + UpdateAppearance() + + overlays.Cut() + update_mutantrace(1) + regenerate_icons() + + if(dna.species) + return TRUE + else + return FALSE + +/mob/living/carbon/human/get_default_language() + if(default_language) + return default_language + + if(!dna.species) + return null + return dna.species.default_language ? GLOB.all_languages[dna.species.default_language] : null + +/mob/living/carbon/human/proc/bloody_doodle() + set category = "IC" + set name = "Write in blood" + set desc = "Use blood on your hands to write a short message on the floor or a wall, murder mystery style." + + if(usr != src) + return 0 //something is terribly wrong + if(incapacitated()) + to_chat(src, "You can't write on the floor in your current state!") + return + if(!bloody_hands) + verbs -= /mob/living/carbon/human/proc/bloody_doodle + + if(gloves) + to_chat(src, "[gloves] are preventing you from writing anything down!") + return + + var/turf/simulated/T = loc + if(!istype(T)) //to prevent doodling out of mechs and lockers + to_chat(src, "You cannot reach the floor.") + return + + var/turf/origin = T + var/direction = input(src,"Which way?","Tile selection") as anything in list("Here","North","South","East","West") + if(direction != "Here") + T = get_step(T,text2dir(direction)) + if(!istype(T)) + to_chat(src, "You cannot doodle there.") + return + + var/num_doodles = 0 + for(var/obj/effect/decal/cleanable/blood/writing/W in T) + num_doodles++ + if(num_doodles > 4) + to_chat(src, "There is no space to write on!") + return + + var/max_length = bloody_hands * 30 //tweeter style + + var/message = stripped_input(src,"Write a message. It cannot be longer than [max_length] characters.","Blood writing", "") + if(origin != loc) + to_chat(src, "Stay still while writing!") + return + if(message) + var/used_blood_amount = round(length(message) / 30, 1) + bloody_hands = max(0, bloody_hands - used_blood_amount) //use up some blood + + if(length(message) > max_length) + message += "-" + to_chat(src, "You ran out of blood to write with!") + else + to_chat(src, "You daub '[message]' on [T] in shiny red lettering.") + var/obj/effect/decal/cleanable/blood/writing/W = new(T) + W.message = message + W.add_fingerprint(src) + +/mob/living/carbon/human/proc/get_eyecon() + var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) + var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes) + if(istype(dna.species) && dna.species.eyes) + var/icon/eyes_icon = new /icon('icons/mob/human_face.dmi', dna.species.eyes) + if(eye_implant) //Eye implants override native DNA eye colo(u)r + eyes_icon = eye_implant.generate_icon() + else if(eyes) + eyes_icon = eyes.generate_icon() + else //Error 404: Eyes not found! + eyes_icon.Blend("#800000", ICON_ADD) + + return eyes_icon + +/mob/living/carbon/human/proc/get_eye_shine() //Referenced cult constructs for shining in the dark. Needs to be above lighting effects such as shading. + var/obj/item/organ/external/head/head_organ = get_organ("head") + var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] + var/icon/hair = new /icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + var/mutable_appearance/MA = mutable_appearance(get_icon_difference(get_eyecon(), hair), layer = ABOVE_LIGHTING_LAYER) + MA.plane = ABOVE_LIGHTING_PLANE + return MA //Cut the hair's pixels from the eyes icon so eyes covered by bangs stay hidden even while on a higher layer. + +/*Used to check if eyes should shine in the dark. Returns the image of the eyes on the layer where they will appear to shine. +Eyes need to have significantly high darksight to shine unless the mob has the XRAY vision mutation. Eyes will not shine if they are covered in any way.*/ +/mob/living/carbon/human/proc/eyes_shine() + var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) + var/obj/item/organ/internal/cyberimp/eyes/eye_implant = get_int_organ(/obj/item/organ/internal/cyberimp/eyes) + if(!(istype(eyes) || istype(eye_implant))) + return FALSE + if(!get_location_accessible(src, "eyes")) + return FALSE + if(!(eyes.shine()) && !istype(eye_implant) && !(XRAY in mutations)) //If their eyes don't shine, they don't have other augs, nor do they have X-RAY vision + return FALSE + + return TRUE + +/mob/living/carbon/human/assess_threat(var/mob/living/simple_animal/bot/secbot/judgebot, var/lasercolor) + if(judgebot.emagged == 2) + return 10 //Everyone is a criminal! + + var/threatcount = 0 + + //Lasertag bullshit + if(lasercolor) + if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve + if(istype(wear_suit, /obj/item/clothing/suit/redtag)) + threatcount += 4 + if((istype(r_hand,/obj/item/gun/energy/laser/tag/red)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/red))) + threatcount += 4 + if(istype(belt, /obj/item/gun/energy/laser/tag/red)) + threatcount += 2 + + if(lasercolor == "r") + if(istype(wear_suit, /obj/item/clothing/suit/bluetag)) + threatcount += 4 + if((istype(r_hand,/obj/item/gun/energy/laser/tag/blue)) || (istype(l_hand,/obj/item/gun/energy/laser/tag/blue))) + threatcount += 4 + if(istype(belt, /obj/item/gun/energy/laser/tag/blue)) + threatcount += 2 + + return threatcount + + //Check for ID + var/obj/item/card/id/idcard = get_idcard() + if(judgebot.idcheck && !idcard) + threatcount += 4 + + //Check for weapons + if(judgebot.weaponscheck) + if(!idcard || !(ACCESS_WEAPONS in idcard.access)) + if(judgebot.check_for_weapons(l_hand)) + threatcount += 4 + if(judgebot.check_for_weapons(r_hand)) + threatcount += 4 + if(judgebot.check_for_weapons(belt)) + threatcount += 2 + + //Check for arrest warrant + if(judgebot.check_records) + var/perpname = get_visible_name(TRUE) + var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security) + if(R && R.fields["criminal"]) + switch(R.fields["criminal"]) + if("*Execute*") + threatcount += 7 + if("*Arrest*") + threatcount += 5 + if("Incarcerated") + threatcount += 2 + if("Parolled") + threatcount += 2 + + //Check for dresscode violations + if(istype(head, /obj/item/clothing/head/wizard) || istype(head, /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard)) + threatcount += 2 + + + //Mindshield implants imply slight trustworthiness + if(ismindshielded(src)) + threatcount -= 1 + + //Agent cards lower threatlevel. + if(istype(idcard, /obj/item/card/id/syndicate)) + threatcount -= 5 + + return threatcount + +/mob/living/carbon/human/singularity_act() + . = 20 + if(mind) + if((mind.assigned_role == "Station Engineer") || (mind.assigned_role == "Chief Engineer") ) + . = 100 + if(mind.assigned_role == "Clown") + . = rand(-1000, 1000) + ..() //Called afterwards because getting the mind after getting gibbed is sketchy + +/mob/living/carbon/human/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_THREE) + var/list/handlist = list(l_hand, r_hand) + for(var/obj/item/hand in handlist) + if(prob(current_size * 5) && hand.w_class >= ((11-current_size)/2) && unEquip(hand)) + step_towards(hand, src) + to_chat(src, "\The [S] pulls \the [hand] from your grip!") + apply_effect(current_size * 3, IRRADIATE) + +/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/human/H) + if(H == src) + to_chat(src, "You cannot perform CPR on yourself!") + return + if(H.stat == DEAD || (H.status_flags & FAKEDEATH)) + to_chat(src, "[H.name] is dead!") + return + if(!check_has_mouth()) + to_chat(src, "You don't have a mouth, you cannot perform CPR!") + return + if(!H.check_has_mouth()) + to_chat(src, "They don't have a mouth, you cannot perform CPR!") + return + if((head && (head.flags_cover & HEADCOVERSMOUTH)) || (wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH) && !wear_mask.mask_adjusted)) + to_chat(src, "Remove your mask first!") + return + if((H.head && (H.head.flags_cover & HEADCOVERSMOUTH)) || (H.wear_mask && (H.wear_mask.flags_cover & MASKCOVERSMOUTH) && !H.wear_mask.mask_adjusted)) + to_chat(src, "Remove [H.p_their()] mask first!") + return + if(H.receiving_cpr) // To prevent spam stacking + to_chat(src, "They are already receiving CPR!") + return + visible_message("[src] is trying to perform CPR on [H.name]!", "You try to perform CPR on [H.name]!") + H.receiving_cpr = TRUE + if(do_mob(src, H, 40)) + if(H.health <= HEALTH_THRESHOLD_CRIT) + H.adjustOxyLoss(-15) + H.SetLoseBreath(0) + H.AdjustParalysis(-1) + H.updatehealth("cpr") + visible_message("[src] performs CPR on [H.name]!", "You perform CPR on [H.name].") + + to_chat(H, "You feel a breath of fresh air enter your lungs. It feels good.") + H.receiving_cpr = FALSE + add_attack_logs(src, H, "CPRed", ATKLOG_ALL) + return TRUE + else + H.receiving_cpr = FALSE + to_chat(src, "You need to stay still while performing CPR!") + +/mob/living/carbon/human/canBeHandcuffed() + if(get_num_arms() >= 2) + return TRUE + else + return FALSE + +/mob/living/carbon/human/has_mutated_organs() + for(var/obj/item/organ/external/E in bodyparts) + if(E.status & ORGAN_MUTATED) + return TRUE + return FALSE + +/mob/living/carbon/human/InCritical() + return (health <= HEALTH_THRESHOLD_CRIT && stat == UNCONSCIOUS) + + +/mob/living/carbon/human/IsAdvancedToolUser() + if(dna.species.has_fine_manipulation) + return TRUE + return FALSE + +/mob/living/carbon/human/get_permeability_protection() + var/list/prot = list("hands"=0, "chest"=0, "groin"=0, "legs"=0, "feet"=0, "arms"=0, "head"=0) + for(var/obj/item/I in get_equipped_items()) + if(I.body_parts_covered & HANDS) + prot["hands"] = max(1 - I.permeability_coefficient, prot["hands"]) + if(I.body_parts_covered & UPPER_TORSO) + prot["chest"] = max(1 - I.permeability_coefficient, prot["chest"]) + if(I.body_parts_covered & LOWER_TORSO) + prot["groin"] = max(1 - I.permeability_coefficient, prot["groin"]) + if(I.body_parts_covered & LEGS) + prot["legs"] = max(1 - I.permeability_coefficient, prot["legs"]) + if(I.body_parts_covered & FEET) + prot["feet"] = max(1 - I.permeability_coefficient, prot["feet"]) + if(I.body_parts_covered & ARMS) + prot["arms"] = max(1 - I.permeability_coefficient, prot["arms"]) + if(I.body_parts_covered & HEAD) + prot["head"] = max(1 - I.permeability_coefficient, prot["head"]) + var/protection = (prot["head"] + prot["arms"] + prot["feet"] + prot["legs"] + prot["groin"] + prot["chest"] + prot["hands"])/7 + return protection + +/mob/living/carbon/human/proc/get_full_print() + if(!dna || !dna.uni_identity) + return + return md5(dna.uni_identity) + +/mob/living/carbon/human/can_see_reagents() + for(var/obj/item/clothing/C in src) //If they have some clothing equipped that lets them see reagents, they can see reagents + if(C.scan_reagents) + return 1 + +/mob/living/carbon/human/can_eat(flags = 255) + return dna.species && (dna.species.dietflags & flags) + +/mob/living/carbon/human/selfFeed(var/obj/item/reagent_containers/food/toEat, fullness) + if(!check_has_mouth()) + to_chat(src, "Where do you intend to put \the [toEat]? You don't have a mouth!") + return 0 + return ..() + +/mob/living/carbon/human/forceFed(var/obj/item/reagent_containers/food/toEat, mob/user, fullness) + if(!check_has_mouth()) + if(!((istype(toEat, /obj/item/reagent_containers/food/drinks) && (ismachine(src))))) + to_chat(user, "Where do you intend to put \the [toEat]? \The [src] doesn't have a mouth!") + return 0 + return ..() + +/mob/living/carbon/human/selfDrink(var/obj/item/reagent_containers/food/drinks/toDrink) + if(!check_has_mouth()) + if(!ismachine(src)) + to_chat(src, "Where do you intend to put \the [src]? You don't have a mouth!") + return 0 + else + to_chat(src, "You pour a bit of liquid from [toDrink] into your connection port.") + else + to_chat(src, "You swallow a gulp of [toDrink].") + return 1 + +/mob/living/carbon/human/can_track(mob/living/user) + if(wear_id) + var/obj/item/card/id/id = wear_id + if(istype(id) && id.is_untrackable()) + return 0 + if(wear_pda) + var/obj/item/pda/pda = wear_pda + if(istype(pda)) + var/obj/item/card/id/id = pda.id + if(istype(id) && id.is_untrackable()) + return 0 + if(istype(head, /obj/item/clothing/head)) + var/obj/item/clothing/head/hat = head + if(hat.blockTracking) + return 0 + + return ..() + +/mob/living/carbon/human/proc/get_age_pitch() + return 1.0 + 0.5*(30 - age)/80 + +/mob/living/carbon/human/get_access() + . = ..() + + if(wear_id) + . |= wear_id.GetAccess() + if(wear_pda) + . |= wear_pda.GetAccess() + if(istype(w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = w_uniform + if(U.accessories) + for(var/obj/item/clothing/accessory/A in U.accessories) + . |= A.GetAccess() + +/mob/living/carbon/human/is_mechanical() + return ..() || (dna.species.bodyflags & ALL_RPARTS) != 0 + +/mob/living/carbon/human/can_use_guns(var/obj/item/gun/G) + . = ..() + + if(G.trigger_guard == TRIGGER_GUARD_NORMAL) + if(HULK in mutations) + to_chat(src, "Your meaty finger is much too large for the trigger guard!") + return 0 + if(NOGUNS in dna.species.species_traits) + to_chat(src, "Your fingers don't fit in the trigger guard!") + return 0 + + if(martial_art && martial_art.no_guns) //great dishonor to famiry + to_chat(src, "[martial_art.no_guns_message]") + return 0 + + return . + +/mob/living/carbon/human/proc/change_icobase(var/new_icobase, var/new_deform, var/owner_sensitive) + for(var/obj/item/organ/external/O in bodyparts) + O.change_organ_icobase(new_icobase, new_deform, owner_sensitive) //Change the icobase/deform of all our organs. If owner_sensitive is set, that means the proc won't mess with frankenstein limbs. + +/mob/living/carbon/human/serialize() + // Currently: Limbs/organs only + var/list/data = ..() + var/list/limbs_list = list() + var/list/organs_list = list() + var/list/equip_list = list() + data["limbs"] = limbs_list + data["iorgans"] = organs_list + data["equip"] = equip_list + + data["dna"] = dna.serialize() + data["age"] = age + + // No being naked + data["ushirt"] = undershirt + data["socks"] = socks + data["uwear"] = underwear + + // Limbs + for(var/limb in bodyparts_by_name) + var/obj/item/organ/O = bodyparts_by_name[limb] + if(!O) + limbs_list[limb] = "missing" + continue + + limbs_list[limb] = O.serialize() + + // Internal organs/augments + for(var/organ in internal_organs) + var/obj/item/organ/O = organ + organs_list[O.name] = O.serialize() + + // Equipment + equip_list.len = slots_amt + for(var/i = 1, i < slots_amt, i++) + var/obj/item/thing = get_item_by_slot(i) + if(thing != null) + equip_list[i] = thing.serialize() + + return data + +/mob/living/carbon/human/deserialize(list/data) + var/list/limbs_list = data["limbs"] + var/list/organs_list = data["iorgans"] + var/list/equip_list = data["equip"] + var/turf/T = get_turf(src) + if(!islist(data["limbs"])) + throw EXCEPTION("Expected a limbs list, but found none") + + if(islist(data["dna"])) + dna.deserialize(data["dna"]) + real_name = dna.real_name + name = real_name + set_species(dna.species.type, skip_same_check = TRUE) + age = data["age"] + undershirt = data["ushirt"] + underwear = data["uwear"] + socks = data["socks"] + for(var/obj/item/organ/internal/iorgan in internal_organs) + qdel(iorgan) + + for(var/obj/item/organ/external/organ in bodyparts) + qdel(organ) + + for(var/limb in limbs_list) + // Missing means skip this part - it's missing + if(limbs_list[limb] == "missing") + continue + // "New" code handles insertion and DNA sync'ing + var/obj/item/organ/external/E = list_to_object(limbs_list[limb], src) + E.sync_colour_to_dna() + + for(var/organ in organs_list) + // As above, "New" code handles insertion, DNA sync + list_to_object(organs_list[organ], src) + + UpdateAppearance() + + // De-serialize equipment + // #1: Jumpsuit + // #2: Outer suit + // #3+: Everything else + if(islist(equip_list[slot_w_uniform])) + var/obj/item/clothing/C = list_to_object(equip_list[slot_w_uniform], T) + equip_to_slot_if_possible(C, slot_w_uniform) + + if(islist(equip_list[slot_wear_suit])) + var/obj/item/clothing/C = list_to_object(equip_list[slot_wear_suit], T) + equip_to_slot_if_possible(C, slot_wear_suit) + + for(var/i = 1, i < slots_amt, i++) + if(i == slot_w_uniform || i == slot_wear_suit) + continue + if(islist(equip_list[i])) + var/obj/item/clothing/C = list_to_object(equip_list[i], T) + equip_to_slot_if_possible(C, i) + update_icons() + + ..() + + +/mob/living/carbon/human/vv_get_dropdown() + . = ..() + . += "---" + .["Set Species"] = "?_src_=vars;setspecies=[UID()]" + .["Copy Outfit"] = "?_src_=vars;copyoutfit=[UID()]" + .["Make AI"] = "?_src_=vars;makeai=[UID()]" + .["Make cyborg"] = "?_src_=vars;makerobot=[UID()]" + .["Make monkey"] = "?_src_=vars;makemonkey=[UID()]" + .["Make alien"] = "?_src_=vars;makealien=[UID()]" + .["Make slime"] = "?_src_=vars;makeslime=[UID()]" + .["Make superhero"] = "?_src_=vars;makesuper=[UID()]" + . += "---" + +/mob/living/carbon/human/adjust_nutrition(change) + if(NO_HUNGER in dna.species.species_traits) + return FALSE + return ..() + +/mob/living/carbon/human/set_nutrition(change) + if(NO_HUNGER in dna.species.species_traits) + return FALSE + return ..() + +/mob/living/carbon/human/proc/special_post_clone_handling() + if(mind && mind.assigned_role == "Cluwne") //HUNKE your suffering never stops + makeCluwne() + +/mob/living/carbon/human/proc/influenceSin() + var/datum/objective/sintouched/O + switch(rand(1,7))//traditional seven deadly sins... except lust. + if(1) // acedia + log_game("[src] was influenced by the sin of Acedia.") + O = new /datum/objective/sintouched/acedia + if(2) // Gluttony + log_game("[src] was influenced by the sin of gluttony.") + O = new /datum/objective/sintouched/gluttony + if(3) // Greed + log_game("[src] was influenced by the sin of greed.") + O = new /datum/objective/sintouched/greed + if(4) // sloth + log_game("[src] was influenced by the sin of sloth.") + O = new /datum/objective/sintouched/sloth + if(5) // Wrath + log_game("[src] was influenced by the sin of wrath.") + O = new /datum/objective/sintouched/wrath + if(6) // Envy + log_game("[src] was influenced by the sin of envy.") + O = new /datum/objective/sintouched/envy + if(7) // Pride + log_game("[src] was influenced by the sin of pride.") + O = new /datum/objective/sintouched/pride + SSticker.mode.sintouched += src.mind + src.mind.objectives += O + var/obj_count = 1 + to_chat(src, "[pick(GLOB.boo_phrases)]") + +/mob/living/carbon/human/extinguish_light() + // Parent function handles stuff the human may be holding + ..() + + var/obj/item/organ/internal/lantern/O = get_int_organ(/obj/item/organ/internal/lantern) + if(O && O.glowing) + O.toggle_biolum(TRUE) + visible_message("[src] is engulfed in shadows and fades into the darkness.", "A sense of dread washes over you as you suddenly dim dark.") diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 65a75f80e8c3..a12ef49fb7f1 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -1,350 +1,350 @@ -//Updates the mob's health from organs and mob damage variables -/mob/living/carbon/human/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = maxHealth - stat = CONSCIOUS - return - - var/total_burn = 0 - var/total_brute = 0 - - for(var/obj/item/organ/external/O in bodyparts) //hardcoded to streamline things a bit - total_brute += O.brute_dam //calculates health based on organ brute and burn - total_burn += O.burn_dam - - health = maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute - - //TODO: fix husking - if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD) && stat == DEAD) - ChangeToHusk() - update_stat("updatehealth([reason])") - med_hud_set_health() - med_hud_set_status() - handle_hud_icons_health() - -/mob/living/carbon/human/adjustBrainLoss(amount, updating = TRUE, use_brain_mod = TRUE) - if(status_flags & GODMODE) - return STATUS_UPDATE_NONE //godmode - - if(dna.species && dna.species.has_organ["brain"]) - var/obj/item/organ/internal/brain/sponge = get_int_organ(/obj/item/organ/internal/brain) - if(sponge) - if(dna.species && amount > 0) - if(use_brain_mod) - amount = amount * dna.species.brain_mod - sponge.damage = Clamp(sponge.damage + amount, 0, 120) - if(sponge.damage >= 120) - visible_message("[src] goes limp, [p_their()] facial expression utterly blank.") - death() - if(updating) - update_stat("adjustBrainLoss") - return STATUS_UPDATE_STAT - -/mob/living/carbon/human/setBrainLoss(amount, updating = TRUE, use_brain_mod = TRUE) - if(status_flags & GODMODE) - return STATUS_UPDATE_NONE //godmode - - if(dna.species && dna.species.has_organ["brain"]) - var/obj/item/organ/internal/brain/sponge = get_int_organ(/obj/item/organ/internal/brain) - if(sponge) - if(dna.species && amount > 0) - if(use_brain_mod) - amount = amount * dna.species.brain_mod - sponge.damage = Clamp(amount, 0, 120) - if(sponge.damage >= 120) - visible_message("[src] goes limp, [p_their()] facial expression utterly blank.") - death() - if(updating) - update_stat("setBrainLoss") - return STATUS_UPDATE_STAT - -/mob/living/carbon/human/getBrainLoss() - if(status_flags & GODMODE) - return 0 //godmode - - if(dna.species && dna.species.has_organ["brain"]) - var/obj/item/organ/internal/brain/sponge = get_int_organ(/obj/item/organ/internal/brain) - if(sponge) - return min(sponge.damage,maxHealth*2) - else - return 200 - else - return 0 - -//These procs fetch a cumulative total damage from all organs -/mob/living/carbon/human/getBruteLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in bodyparts) - amount += O.brute_dam - return amount - -/mob/living/carbon/human/getFireLoss() - var/amount = 0 - for(var/obj/item/organ/external/O in bodyparts) - amount += O.burn_dam - return amount - -/mob/living/carbon/human/adjustBruteLoss(amount, updating_health = TRUE, damage_source = null, robotic = FALSE) - if(amount > 0) - if(dna.species) - amount = amount * dna.species.brute_mod - take_overall_damage(amount, 0, updating_health, used_weapon = damage_source) - else - heal_overall_damage(-amount, 0, updating_health, FALSE, robotic) - // brainless default for now - return STATUS_UPDATE_HEALTH - -/mob/living/carbon/human/adjustFireLoss(amount, updating_health = TRUE, damage_source = null, robotic = FALSE) - if(amount > 0) - if(dna.species) - amount = amount * dna.species.burn_mod - take_overall_damage(0, amount, updating_health, used_weapon = damage_source) - else - heal_overall_damage(0, -amount, updating_health, FALSE, robotic) - // brainless default for now - return STATUS_UPDATE_HEALTH - -/mob/living/carbon/human/proc/adjustBruteLossByPart(amount, organ_name, obj/damage_source = null, updating_health = TRUE) - if(dna.species && amount > 0) - amount = amount * dna.species.brute_mod - if(organ_name in bodyparts_by_name) - var/obj/item/organ/external/O = get_organ(organ_name) - - if(amount > 0) - O.receive_damage(amount, 0, sharp=is_sharp(damage_source), used_weapon=damage_source, list(), FALSE, updating_health) - else - //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(-amount, 0, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/carbon/human/proc/adjustFireLossByPart(amount, organ_name, obj/damage_source = null, updating_health = TRUE) - if(dna.species && amount > 0) - amount = amount * dna.species.burn_mod - - if(organ_name in bodyparts_by_name) - var/obj/item/organ/external/O = get_organ(organ_name) - - if(amount > 0) - O.receive_damage(0, amount, sharp=is_sharp(damage_source), used_weapon=damage_source, forbidden_limbs = list(), ignore_resists = FALSE, updating_health = updating_health) - else - //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. - O.heal_damage(0, -amount, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health) - return STATUS_UPDATE_HEALTH - - -/mob/living/carbon/human/Paralyse(amount) - // Notify our AI if they can now control the suit. - if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. - wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") - return ..() - -/mob/living/carbon/human/adjustCloneLoss(amount) - if(dna.species && amount > 0) - amount = amount * dna.species.clone_mod - . = ..() - - var/heal_prob = max(0, 80 - getCloneLoss()) - var/mut_prob = min(80, getCloneLoss() + 10) - if(amount > 0) //cloneloss is being added - if(prob(mut_prob)) - var/list/obj/item/organ/external/candidates = list() //TYPECASTED LISTS ARE NOT A FUCKING THING WHAT THE FUCK - for(var/obj/item/organ/external/O in bodyparts) - if(O.is_robotic()) - continue - if(!(O.status & ORGAN_MUTATED)) - candidates |= O - - if(candidates.len) - var/obj/item/organ/external/O = pick(candidates) - O.mutate() - to_chat(src, "Something is not right with your [O.name]...") - O.add_autopsy_data("Mutation", amount) - return - - else //cloneloss is being subtracted - if(prob(heal_prob)) - for(var/obj/item/organ/external/O in bodyparts) - if(O.status & ORGAN_MUTATED) - O.unmutate() - to_chat(src, "Your [O.name] is shaped normally again.") - return - - - if(getCloneLoss() < 1) //no cloneloss, fixes organs - for(var/obj/item/organ/external/O in bodyparts) - if(O.status & ORGAN_MUTATED) - O.unmutate() - to_chat(src, "Your [O.name] is shaped normally again.") - - -// Defined here solely to take species flags into account without having to recast at mob/living level. -/mob/living/carbon/human/adjustOxyLoss(amount) - if(NO_BREATHE in dna.species.species_traits) - oxyloss = 0 - return FALSE - if(dna.species && amount > 0) - amount = amount * dna.species.oxy_mod - . = ..() - -/mob/living/carbon/human/setOxyLoss(amount) - if(NO_BREATHE in dna.species.species_traits) - oxyloss = 0 - return FALSE - if(dna.species && amount > 0) - amount = amount * dna.species.oxy_mod - . = ..() - -/mob/living/carbon/human/adjustToxLoss(amount) - if(dna.species && amount > 0) - amount = amount * dna.species.tox_mod - . = ..() - -/mob/living/carbon/human/setToxLoss(amount) - if(dna.species && amount > 0) - amount = amount * dna.species.tox_mod - . = ..() - -/mob/living/carbon/human/adjustStaminaLoss(amount, updating = TRUE) - if(dna.species && amount > 0) - amount = amount * dna.species.stamina_mod - . = ..() - -/mob/living/carbon/human/setStaminaLoss(amount, updating = TRUE) - if(dna.species && amount > 0) - amount = amount * dna.species.stamina_mod - . = ..() - -//////////////////////////////////////////// - -//Returns a list of damaged organs -/mob/living/carbon/human/proc/get_damaged_organs(brute, burn, flags = AFFECT_ALL_ORGANS) - var/list/obj/item/organ/external/parts = list() - for(var/obj/item/organ/external/O in bodyparts) - if((brute && O.brute_dam) || (burn && O.burn_dam)) - if(!(flags & AFFECT_ROBOTIC_ORGAN) && O.is_robotic()) - continue - if(!(flags & AFFECT_ORGANIC_ORGAN) && !O.is_robotic()) - continue - parts += O - return parts - -//Returns a list of damageable organs -/mob/living/carbon/human/proc/get_damageable_organs() - var/list/obj/item/organ/external/parts = list() - for(var/obj/item/organ/external/O in bodyparts) - if(O.brute_dam + O.burn_dam < O.max_damage) - parts += O - return parts - -//Heals ONE external organ, organ gets randomly selected from damaged ones. -//It automatically updates damage overlays if necesary -//It automatically updates health status -/mob/living/carbon/human/heal_organ_damage(brute, burn, updating_health = TRUE) - var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) - if(!parts.len) - return - var/obj/item/organ/external/picked = pick(parts) - if(picked.heal_damage(brute,burn, updating_health)) - UpdateDamageIcon() - -//Damages ONE external organ, organ gets randomly selected from damagable ones. -//It automatically updates damage overlays if necesary -//It automatically updates health status -/mob/living/carbon/human/take_organ_damage(brute, burn, updating_health = TRUE, sharp = 0, edge = 0) - var/list/obj/item/organ/external/parts = get_damageable_organs() - if(!parts.len) - return - var/obj/item/organ/external/picked = pick(parts) - if(picked.receive_damage(brute, burn, sharp, updating_health)) - UpdateDamageIcon() - - -//Heal MANY external organs, in random order -/mob/living/carbon/human/heal_overall_damage(brute, burn, updating_health = TRUE, internal=0, robotic=0) - var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) - - var/update = 0 - while(parts.len && ( brute > 0 || burn > 0) ) - var/obj/item/organ/external/picked = pick(parts) - - var/brute_was = picked.brute_dam - var/burn_was = picked.burn_dam - - update |= picked.heal_damage(brute,burn, internal, robotic, updating_health = FALSE) - - brute -= (brute_was-picked.brute_dam) - burn -= (burn_was-picked.burn_dam) - - parts -= picked - - if(updating_health) - updatehealth("heal overall damage") - if(update) - UpdateDamageIcon() - -// damage MANY external organs, in random order -/mob/living/carbon/human/take_overall_damage(brute, burn, updating_health = TRUE, used_weapon = null, sharp = 0, edge = 0) - if(status_flags & GODMODE) - return //godmode - var/list/obj/item/organ/external/parts = get_damageable_organs() - - var/update = 0 - while(parts.len && (brute>0 || burn>0) ) - var/obj/item/organ/external/picked = pick(parts) - var/brute_per_part = brute/parts.len - var/burn_per_part = burn/parts.len - - var/brute_was = picked.brute_dam - var/burn_was = picked.burn_dam - - - update |= picked.receive_damage(brute_per_part, burn_per_part, sharp, used_weapon, list(), FALSE, FALSE) - - brute -= (picked.brute_dam - brute_was) - burn -= (picked.burn_dam - burn_was) - - parts -= picked - - if(updating_health) - updatehealth("take overall damage") - - if(update) - UpdateDamageIcon() - - -//////////////////////////////////////////// - -/* -This function restores all organs. -*/ -/mob/living/carbon/human/restore_all_organs() - for(var/obj/item/organ/external/current_organ in bodyparts) - current_organ.rejuvenate() - -/mob/living/carbon/human/proc/HealDamage(zone, brute, burn) - var/obj/item/organ/external/E = get_organ(zone) - if(istype(E, /obj/item/organ/external)) - if(E.heal_damage(brute, burn)) - UpdateDamageIcon() - else - return 0 - - -/mob/living/carbon/human/get_organ(zone) - if(!zone) - zone = "chest" - if(zone in list("eyes", "mouth")) - zone = "head" - - return bodyparts_by_name[zone] - -/mob/living/carbon/human/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, sharp = 0, obj/used_weapon = null) - //Handle other types of damage - if((damagetype != BRUTE) && (damagetype != BURN)) - ..(damage, damagetype, def_zone, blocked) - return 1 - - //Handle BRUTE and BURN damage - handle_suit_punctures(damagetype, damage) - //Handle species apply_damage procs - return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, sharp, used_weapon) +//Updates the mob's health from organs and mob damage variables +/mob/living/carbon/human/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = maxHealth + stat = CONSCIOUS + return + + var/total_burn = 0 + var/total_brute = 0 + + for(var/obj/item/organ/external/O in bodyparts) //hardcoded to streamline things a bit + total_brute += O.brute_dam //calculates health based on organ brute and burn + total_burn += O.burn_dam + + health = maxHealth - getOxyLoss() - getToxLoss() - getCloneLoss() - total_burn - total_brute + + //TODO: fix husking + if(((maxHealth - total_burn) < HEALTH_THRESHOLD_DEAD) && stat == DEAD) + ChangeToHusk() + update_stat("updatehealth([reason])") + med_hud_set_health() + med_hud_set_status() + handle_hud_icons_health() + +/mob/living/carbon/human/adjustBrainLoss(amount, updating = TRUE, use_brain_mod = TRUE) + if(status_flags & GODMODE) + return STATUS_UPDATE_NONE //godmode + + if(dna.species && dna.species.has_organ["brain"]) + var/obj/item/organ/internal/brain/sponge = get_int_organ(/obj/item/organ/internal/brain) + if(sponge) + if(dna.species && amount > 0) + if(use_brain_mod) + amount = amount * dna.species.brain_mod + sponge.damage = Clamp(sponge.damage + amount, 0, 120) + if(sponge.damage >= 120) + visible_message("[src] goes limp, [p_their()] facial expression utterly blank.") + death() + if(updating) + update_stat("adjustBrainLoss") + return STATUS_UPDATE_STAT + +/mob/living/carbon/human/setBrainLoss(amount, updating = TRUE, use_brain_mod = TRUE) + if(status_flags & GODMODE) + return STATUS_UPDATE_NONE //godmode + + if(dna.species && dna.species.has_organ["brain"]) + var/obj/item/organ/internal/brain/sponge = get_int_organ(/obj/item/organ/internal/brain) + if(sponge) + if(dna.species && amount > 0) + if(use_brain_mod) + amount = amount * dna.species.brain_mod + sponge.damage = Clamp(amount, 0, 120) + if(sponge.damage >= 120) + visible_message("[src] goes limp, [p_their()] facial expression utterly blank.") + death() + if(updating) + update_stat("setBrainLoss") + return STATUS_UPDATE_STAT + +/mob/living/carbon/human/getBrainLoss() + if(status_flags & GODMODE) + return 0 //godmode + + if(dna.species && dna.species.has_organ["brain"]) + var/obj/item/organ/internal/brain/sponge = get_int_organ(/obj/item/organ/internal/brain) + if(sponge) + return min(sponge.damage,maxHealth*2) + else + return 200 + else + return 0 + +//These procs fetch a cumulative total damage from all organs +/mob/living/carbon/human/getBruteLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in bodyparts) + amount += O.brute_dam + return amount + +/mob/living/carbon/human/getFireLoss() + var/amount = 0 + for(var/obj/item/organ/external/O in bodyparts) + amount += O.burn_dam + return amount + +/mob/living/carbon/human/adjustBruteLoss(amount, updating_health = TRUE, damage_source = null, robotic = FALSE) + if(amount > 0) + if(dna.species) + amount = amount * dna.species.brute_mod + take_overall_damage(amount, 0, updating_health, used_weapon = damage_source) + else + heal_overall_damage(-amount, 0, updating_health, FALSE, robotic) + // brainless default for now + return STATUS_UPDATE_HEALTH + +/mob/living/carbon/human/adjustFireLoss(amount, updating_health = TRUE, damage_source = null, robotic = FALSE) + if(amount > 0) + if(dna.species) + amount = amount * dna.species.burn_mod + take_overall_damage(0, amount, updating_health, used_weapon = damage_source) + else + heal_overall_damage(0, -amount, updating_health, FALSE, robotic) + // brainless default for now + return STATUS_UPDATE_HEALTH + +/mob/living/carbon/human/proc/adjustBruteLossByPart(amount, organ_name, obj/damage_source = null, updating_health = TRUE) + if(dna.species && amount > 0) + amount = amount * dna.species.brute_mod + if(organ_name in bodyparts_by_name) + var/obj/item/organ/external/O = get_organ(organ_name) + + if(amount > 0) + O.receive_damage(amount, 0, sharp=is_sharp(damage_source), used_weapon=damage_source, list(), FALSE, updating_health) + else + //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. + O.heal_damage(-amount, 0, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/carbon/human/proc/adjustFireLossByPart(amount, organ_name, obj/damage_source = null, updating_health = TRUE) + if(dna.species && amount > 0) + amount = amount * dna.species.burn_mod + + if(organ_name in bodyparts_by_name) + var/obj/item/organ/external/O = get_organ(organ_name) + + if(amount > 0) + O.receive_damage(0, amount, sharp=is_sharp(damage_source), used_weapon=damage_source, forbidden_limbs = list(), ignore_resists = FALSE, updating_health = updating_health) + else + //if you don't want to heal robot organs, they you will have to check that yourself before using this proc. + O.heal_damage(0, -amount, internal = 0, robo_repair = O.is_robotic(), updating_health = updating_health) + return STATUS_UPDATE_HEALTH + + +/mob/living/carbon/human/Paralyse(amount) + // Notify our AI if they can now control the suit. + if(wearing_rig && !stat && paralysis < amount) //We are passing out right this second. + wearing_rig.notify_ai("Warning: user consciousness failure. Mobility control passed to integrated intelligence system.") + return ..() + +/mob/living/carbon/human/adjustCloneLoss(amount) + if(dna.species && amount > 0) + amount = amount * dna.species.clone_mod + . = ..() + + var/heal_prob = max(0, 80 - getCloneLoss()) + var/mut_prob = min(80, getCloneLoss() + 10) + if(amount > 0) //cloneloss is being added + if(prob(mut_prob)) + var/list/obj/item/organ/external/candidates = list() //TYPECASTED LISTS ARE NOT A FUCKING THING WHAT THE FUCK + for(var/obj/item/organ/external/O in bodyparts) + if(O.is_robotic()) + continue + if(!(O.status & ORGAN_MUTATED)) + candidates |= O + + if(candidates.len) + var/obj/item/organ/external/O = pick(candidates) + O.mutate() + to_chat(src, "Something is not right with your [O.name]...") + O.add_autopsy_data("Mutation", amount) + return + + else //cloneloss is being subtracted + if(prob(heal_prob)) + for(var/obj/item/organ/external/O in bodyparts) + if(O.status & ORGAN_MUTATED) + O.unmutate() + to_chat(src, "Your [O.name] is shaped normally again.") + return + + + if(getCloneLoss() < 1) //no cloneloss, fixes organs + for(var/obj/item/organ/external/O in bodyparts) + if(O.status & ORGAN_MUTATED) + O.unmutate() + to_chat(src, "Your [O.name] is shaped normally again.") + + +// Defined here solely to take species flags into account without having to recast at mob/living level. +/mob/living/carbon/human/adjustOxyLoss(amount) + if(NO_BREATHE in dna.species.species_traits) + oxyloss = 0 + return FALSE + if(dna.species && amount > 0) + amount = amount * dna.species.oxy_mod + . = ..() + +/mob/living/carbon/human/setOxyLoss(amount) + if(NO_BREATHE in dna.species.species_traits) + oxyloss = 0 + return FALSE + if(dna.species && amount > 0) + amount = amount * dna.species.oxy_mod + . = ..() + +/mob/living/carbon/human/adjustToxLoss(amount) + if(dna.species && amount > 0) + amount = amount * dna.species.tox_mod + . = ..() + +/mob/living/carbon/human/setToxLoss(amount) + if(dna.species && amount > 0) + amount = amount * dna.species.tox_mod + . = ..() + +/mob/living/carbon/human/adjustStaminaLoss(amount, updating = TRUE) + if(dna.species && amount > 0) + amount = amount * dna.species.stamina_mod + . = ..() + +/mob/living/carbon/human/setStaminaLoss(amount, updating = TRUE) + if(dna.species && amount > 0) + amount = amount * dna.species.stamina_mod + . = ..() + +//////////////////////////////////////////// + +//Returns a list of damaged organs +/mob/living/carbon/human/proc/get_damaged_organs(brute, burn, flags = AFFECT_ALL_ORGANS) + var/list/obj/item/organ/external/parts = list() + for(var/obj/item/organ/external/O in bodyparts) + if((brute && O.brute_dam) || (burn && O.burn_dam)) + if(!(flags & AFFECT_ROBOTIC_ORGAN) && O.is_robotic()) + continue + if(!(flags & AFFECT_ORGANIC_ORGAN) && !O.is_robotic()) + continue + parts += O + return parts + +//Returns a list of damageable organs +/mob/living/carbon/human/proc/get_damageable_organs() + var/list/obj/item/organ/external/parts = list() + for(var/obj/item/organ/external/O in bodyparts) + if(O.brute_dam + O.burn_dam < O.max_damage) + parts += O + return parts + +//Heals ONE external organ, organ gets randomly selected from damaged ones. +//It automatically updates damage overlays if necesary +//It automatically updates health status +/mob/living/carbon/human/heal_organ_damage(brute, burn, updating_health = TRUE) + var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + if(!parts.len) + return + var/obj/item/organ/external/picked = pick(parts) + if(picked.heal_damage(brute,burn, updating_health)) + UpdateDamageIcon() + +//Damages ONE external organ, organ gets randomly selected from damagable ones. +//It automatically updates damage overlays if necesary +//It automatically updates health status +/mob/living/carbon/human/take_organ_damage(brute, burn, updating_health = TRUE, sharp = 0, edge = 0) + var/list/obj/item/organ/external/parts = get_damageable_organs() + if(!parts.len) + return + var/obj/item/organ/external/picked = pick(parts) + if(picked.receive_damage(brute, burn, sharp, updating_health)) + UpdateDamageIcon() + + +//Heal MANY external organs, in random order +/mob/living/carbon/human/heal_overall_damage(brute, burn, updating_health = TRUE, internal=0, robotic=0) + var/list/obj/item/organ/external/parts = get_damaged_organs(brute,burn) + + var/update = 0 + while(parts.len && ( brute > 0 || burn > 0) ) + var/obj/item/organ/external/picked = pick(parts) + + var/brute_was = picked.brute_dam + var/burn_was = picked.burn_dam + + update |= picked.heal_damage(brute,burn, internal, robotic, updating_health = FALSE) + + brute -= (brute_was-picked.brute_dam) + burn -= (burn_was-picked.burn_dam) + + parts -= picked + + if(updating_health) + updatehealth("heal overall damage") + if(update) + UpdateDamageIcon() + +// damage MANY external organs, in random order +/mob/living/carbon/human/take_overall_damage(brute, burn, updating_health = TRUE, used_weapon = null, sharp = 0, edge = 0) + if(status_flags & GODMODE) + return //godmode + var/list/obj/item/organ/external/parts = get_damageable_organs() + + var/update = 0 + while(parts.len && (brute>0 || burn>0) ) + var/obj/item/organ/external/picked = pick(parts) + var/brute_per_part = brute/parts.len + var/burn_per_part = burn/parts.len + + var/brute_was = picked.brute_dam + var/burn_was = picked.burn_dam + + + update |= picked.receive_damage(brute_per_part, burn_per_part, sharp, used_weapon, list(), FALSE, FALSE) + + brute -= (picked.brute_dam - brute_was) + burn -= (picked.burn_dam - burn_was) + + parts -= picked + + if(updating_health) + updatehealth("take overall damage") + + if(update) + UpdateDamageIcon() + + +//////////////////////////////////////////// + +/* +This function restores all organs. +*/ +/mob/living/carbon/human/restore_all_organs() + for(var/obj/item/organ/external/current_organ in bodyparts) + current_organ.rejuvenate() + +/mob/living/carbon/human/proc/HealDamage(zone, brute, burn) + var/obj/item/organ/external/E = get_organ(zone) + if(istype(E, /obj/item/organ/external)) + if(E.heal_damage(brute, burn)) + UpdateDamageIcon() + else + return 0 + + +/mob/living/carbon/human/get_organ(zone) + if(!zone) + zone = "chest" + if(zone in list("eyes", "mouth")) + zone = "head" + + return bodyparts_by_name[zone] + +/mob/living/carbon/human/apply_damage(damage = 0, damagetype = BRUTE, def_zone = null, blocked = 0, sharp = 0, obj/used_weapon = null) + //Handle other types of damage + if((damagetype != BRUTE) && (damagetype != BURN)) + ..(damage, damagetype, def_zone, blocked) + return 1 + + //Handle BRUTE and BURN damage + handle_suit_punctures(damagetype, damage) + //Handle species apply_damage procs + return dna.species.apply_damage(damage, damagetype, def_zone, blocked, src, sharp, used_weapon) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index dbdc66f9819a..ad1d249439dd 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -1,730 +1,730 @@ -/* -Contains most of the procs that are called when a mob is attacked by something - -bullet_act -ex_act -meteor_act -emp_act - -*/ - - -/mob/living/carbon/human/bullet_act(obj/item/projectile/P, def_zone) - if(!dna.species.bullet_act(P, src)) - return FALSE - if(P.is_reflectable) - if(check_reflect(def_zone)) // Checks if you've passed a reflection% check - visible_message("The [P.name] gets reflected by [src]!", \ - "The [P.name] gets reflected by [src]!") - - P.reflect_back(src) - - return -1 // complete projectile permutation - - //Shields - if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) - P.on_hit(src, 100, def_zone) - return 2 - - var/obj/item/organ/external/organ = get_organ(check_zone(def_zone)) - if(isnull(organ)) - . = bullet_act(P, "chest") //act on chest instead - return - - organ.add_autopsy_data(P.name, P.damage) // Add the bullet's name to the autopsy data - - return (..(P , def_zone)) - -/mob/living/carbon/human/welder_act(mob/user, obj/item/I) - if(user.a_intent != INTENT_HELP) - return - if(!I.tool_use_check(user, 1)) - return - var/obj/item/organ/external/S = bodyparts_by_name[user.zone_selected] - if(!S) - return - if(!S.is_robotic() || S.open == 2) - return - . = TRUE - if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP) - to_chat(user, "The damage is far too severe to patch over externally.") - return - - if(!S.brute_dam) - to_chat(user, "Nothing to fix!") - return - - var/surgery_time = 0 - if(user == src) - surgery_time = 10 - if(!I.use_tool(src, user, surgery_time, amount = 1, volume = I.tool_volume)) - return - var/rembrute = HEALPERWELD - var/nrembrute = 0 - var/childlist - if(!isnull(S.children)) - childlist = S.children.Copy() - var/parenthealed = FALSE - while(rembrute > 0) - var/obj/item/organ/external/E - if(S.brute_dam) - E = S - else if(LAZYLEN(childlist)) - E = pick_n_take(childlist) - if(!E.brute_dam || !E.is_robotic()) - continue - else if(S.parent && !parenthealed) - E = S.parent - parenthealed = TRUE - if(!E.brute_dam || !E.is_robotic()) - break - else - break - nrembrute = max(rembrute - E.brute_dam, 0) - E.heal_damage(rembrute,0,0,1) - rembrute = nrembrute - user.visible_message("[user] patches some dents on [src]'s [E.name] with [I].") - if(bleed_rate && isSynthetic()) - bleed_rate = 0 - user.visible_message("[user] patches some leaks on [src] with [I].") - if(IgniteMob()) - message_admins("[key_name_admin(user)] set [key_name_admin(src)] on fire with [I]") - log_game("[key_name(user)] set [key_name(src)] on fire with [I]") - - -/mob/living/carbon/human/check_projectile_dismemberment(obj/item/projectile/P, def_zone) - var/obj/item/organ/external/affecting = get_organ(check_zone(def_zone)) - if(affecting && !affecting.cannot_amputate && affecting.get_damage() >= (affecting.max_damage - P.dismemberment)) - var/damtype = DROPLIMB_SHARP - switch(P.damage_type) - if(BRUTE) - damtype = DROPLIMB_BLUNT - if(BURN) - damtype = DROPLIMB_BURN - - affecting.droplimb(FALSE, damtype) - -/mob/living/carbon/human/getarmor(var/def_zone, var/type) - var/armorval = 0 - var/organnum = 0 - - if(def_zone) - if(isorgan(def_zone)) - return getarmor_organ(def_zone, type) - var/obj/item/organ/external/affecting = get_organ(def_zone) - if(affecting) - return getarmor_organ(affecting, type) - //If a specific bodypart is targetted, check how that bodypart is protected and return the value. - - //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values - for(var/obj/item/organ/external/organ in bodyparts) - armorval += getarmor_organ(organ, type) - organnum++ - - return (armorval/max(organnum, 1)) - - -//this proc returns the armour value for a particular external organ. -/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type) - if(!type || !def_zone) return 0 - var/protection = 0 - var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, back, gloves, shoes, belt, s_store, glasses, l_ear, r_ear, wear_id) //Everything but pockets. Pockets are l_store and r_store. (if pockets were allowed, putting something armored, gloves or hats for example, would double up on the armor) - for(var/bp in body_parts) - if(!bp) continue - if(bp && istype(bp ,/obj/item/clothing)) - var/obj/item/clothing/C = bp - if(C.body_parts_covered & def_zone.body_part) - protection += C.armor[type] - - return protection - -//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. -/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) - if(!def_zone) - return 1.0 - - var/siemens_coefficient = 1.0 - - var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? - for(var/obj/item/clothing/C in clothing_items) - if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? - siemens_coefficient *= C.siemens_coefficient - - return siemens_coefficient - -/mob/living/carbon/human/proc/check_head_coverage() - - var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform) - for(var/bp in body_parts) - if(!bp) continue - if(bp && istype(bp ,/obj/item/clothing)) - var/obj/item/clothing/C = bp - if(C.body_parts_covered & HEAD) - return 1 - return 0 - -/mob/living/carbon/human/proc/check_reflect(var/def_zone) //Reflection checks for anything in your l_hand, r_hand, or wear_suit based on the reflection chance var of the object - if(wear_suit && istype(wear_suit, /obj/item/)) - var/obj/item/I = wear_suit - if(I.IsReflect(def_zone) == 1) - return 1 - if(l_hand && istype(l_hand, /obj/item/)) - var/obj/item/I = l_hand - if(I.IsReflect(def_zone) == 1) - return 1 - if(r_hand && istype(r_hand, /obj/item/)) - var/obj/item/I = r_hand - if(I.IsReflect(def_zone) == 1) - return 1 - return 0 - - -//End Here - -/mob/living/carbon/human/proc/check_shields(atom/AM, var/damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) - var/block_chance_modifier = round(damage / -3) - - if(l_hand && !istype(l_hand, /obj/item/clothing)) - var/final_block_chance = l_hand.block_chance - (Clamp((armour_penetration-l_hand.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example - if(l_hand.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 - if(r_hand && !istype(r_hand, /obj/item/clothing)) - var/final_block_chance = r_hand.block_chance - (Clamp((armour_penetration-r_hand.armour_penetration)/2,0,100)) + block_chance_modifier //Need to reset the var so it doesn't carry over modifications between attempts - if(r_hand.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 - if(wear_suit) - var/final_block_chance = wear_suit.block_chance - (Clamp((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier - if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 - if(w_uniform) - var/final_block_chance = w_uniform.block_chance - (Clamp((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier - if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) - return 1 - return 0 - -/mob/living/carbon/human/proc/check_block() - if(martial_art && prob(martial_art.block_chance) && martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE)) - return TRUE - -/mob/living/carbon/human/emp_act(severity) - for(var/obj/O in src) - if(!O) continue - O.emp_act(severity) - ..() - -/mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit) //todo: update this to utilize check_obscured_slots() //and make sure it's check_obscured_slots(TRUE) to stop aciding through visors etc - var/list/damaged = list() - var/list/inventory_items_to_kill = list() - var/acidity = acidpwr * min(acid_volume * 0.005, 0.1) - //HEAD// - if(!bodyzone_hit || bodyzone_hit == "head") //only if we didn't specify a zone or if that zone is the head. - var/obj/item/clothing/head_clothes = null - if(glasses) - head_clothes = glasses - if(wear_mask) - head_clothes = wear_mask - if(head) - head_clothes = head - if(head_clothes) - if(!(head_clothes.resistance_flags & UNACIDABLE)) - head_clothes.acid_act(acidpwr, acid_volume) - update_inv_glasses() - update_inv_wear_mask() - update_inv_head() - else - to_chat(src, "Your [head_clothes.name] protects your head and face from the acid!") - else - . = get_organ("head") - if(.) - damaged += . - if(l_ear) - inventory_items_to_kill += l_ear - if(r_ear) - inventory_items_to_kill += r_ear - - //CHEST// - if(!bodyzone_hit || bodyzone_hit == "chest") - var/obj/item/clothing/chest_clothes = null - if(w_uniform) - chest_clothes = w_uniform - if(wear_suit) - chest_clothes = wear_suit - if(chest_clothes) - if(!(chest_clothes.resistance_flags & UNACIDABLE)) - chest_clothes.acid_act(acidpwr, acid_volume) - update_inv_w_uniform() - update_inv_wear_suit() - else - to_chat(src, "Your [chest_clothes.name] protects your body from the acid!") - else - . = get_organ("chest") - if(.) - damaged += . - if(wear_id) - inventory_items_to_kill += wear_id - if(wear_pda) - inventory_items_to_kill += wear_pda - if(r_store) - inventory_items_to_kill += r_store - if(l_store) - inventory_items_to_kill += l_store - if(s_store) - inventory_items_to_kill += s_store - - - //ARMS & HANDS// - if(!bodyzone_hit || bodyzone_hit == "l_arm" || bodyzone_hit == "r_arm") - var/obj/item/clothing/arm_clothes = null - if(gloves) - arm_clothes = gloves - if(w_uniform && ((w_uniform.body_parts_covered & HANDS) || (w_uniform.body_parts_covered & ARMS))) - arm_clothes = w_uniform - if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS))) - arm_clothes = wear_suit - - if(arm_clothes) - if(!(arm_clothes.resistance_flags & UNACIDABLE)) - arm_clothes.acid_act(acidpwr, acid_volume) - update_inv_gloves() - update_inv_w_uniform() - update_inv_wear_suit() - else - to_chat(src, "Your [arm_clothes.name] protects your arms and hands from the acid!") - else - . = get_organ("r_arm") - if(.) - damaged += . - . = get_organ("l_arm") - if(.) - damaged += . - - - //LEGS & FEET// - if(!bodyzone_hit || bodyzone_hit == "l_leg" || bodyzone_hit =="r_leg" || bodyzone_hit == "feet") - var/obj/item/clothing/leg_clothes = null - if(shoes) - leg_clothes = shoes - if(w_uniform && ((w_uniform.body_parts_covered & FEET) || (bodyzone_hit != "feet" && (w_uniform.body_parts_covered & LEGS)))) - leg_clothes = w_uniform - if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (bodyzone_hit != "feet" && (wear_suit.body_parts_covered & LEGS)))) - leg_clothes = wear_suit - if(leg_clothes) - if(!(leg_clothes.resistance_flags & UNACIDABLE)) - leg_clothes.acid_act(acidpwr, acid_volume) - update_inv_shoes() - update_inv_w_uniform() - update_inv_wear_suit() - else - to_chat(src, "Your [leg_clothes.name] protects your legs and feet from the acid!") - else - . = get_organ("r_leg") - if(.) - damaged += . - . = get_organ("l_leg") - if(.) - damaged += . - - - //DAMAGE// - for(var/obj/item/organ/external/affecting in damaged) - affecting.receive_damage(acidity, 2 * acidity) - - if(istype(affecting, /obj/item/organ/external/head)) - var/obj/item/organ/external/head/head_organ = affecting - if(prob(min(acidpwr * acid_volume / 10, 90))) //Applies disfigurement - head_organ.receive_damage(acidity, 2 * acidity) - emote("scream") - head_organ.h_style = "Bald" - head_organ.f_style = "Shaved" - update_hair() - update_fhair() - head_organ.disfigure() - - UpdateDamageIcon() - - //MELTING INVENTORY ITEMS// - //these items are all outside of armour visually, so melt regardless. - if(!bodyzone_hit) - if(back) - inventory_items_to_kill += back - if(belt) - inventory_items_to_kill += belt - if(l_hand) - inventory_items_to_kill += l_hand - if(r_hand) - inventory_items_to_kill += r_hand - - for(var/obj/item/I in inventory_items_to_kill) - I.acid_act(acidpwr, acid_volume) - return 1 - -/mob/living/carbon/human/emag_act(user as mob, var/obj/item/organ/external/affecting) - if(!istype(affecting)) - return - if(!affecting.is_robotic()) - to_chat(user, "That limb isn't robotic.") - return - if(affecting.sabotaged) - to_chat(user, "[src]'s [affecting.name] is already sabotaged!") - else - to_chat(user, "You sneakily slide the card into the dataport on [src]'s [affecting.name] and short out the safeties.") - affecting.sabotaged = 1 - return 1 - -/mob/living/carbon/human/grabbedby(mob/living/user) - if(w_uniform) - w_uniform.add_fingerprint(user) - return ..() - -//Returns 1 if the attack hit, 0 if it missed. -/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user, def_zone) - if(!I || !user) - return 0 - - if((istype(I, /obj/item/kitchen/knife/butcher/meatcleaver) || istype(I, /obj/item/twohanded/chainsaw)) && stat == DEAD && user.a_intent == INTENT_HARM) - var/obj/item/reagent_containers/food/snacks/meat/human/newmeat = new /obj/item/reagent_containers/food/snacks/meat/human(get_turf(loc)) - newmeat.name = real_name + newmeat.name - newmeat.subjectname = real_name - newmeat.subjectjob = job - newmeat.reagents.add_reagent("nutriment", (nutrition / 15) / 3) - reagents.trans_to(newmeat, round((reagents.total_volume) / 3, 1)) - add_mob_blood(src) - --meatleft - to_chat(user, "You hack off a chunk of meat from [name]") - if(!meatleft) - add_attack_logs(user, src, "Chopped up into meat") - if(!iscarbon(user)) - LAssailant = null - else - LAssailant = user - - qdel(src) - - var/obj/item/organ/external/affecting = get_organ(ran_zone(user.zone_selected)) - if(!affecting) - to_chat(user, "They are missing that limb!") - return 1 - var/hit_area = parse_zone(affecting.limb_name) - - if(user != src) - user.do_attack_animation(src) - if(check_shields(I, I.force, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) - return 0 - - if(check_block()) - visible_message("[src] blocks [I]!") - return FALSE - - if(istype(I,/obj/item/card/emag)) - emag_act(user, affecting) - - send_item_attack_message(I, user, hit_area) - - var/weakness = check_weakness(I,user) - - if(!I.force) - return 0 //item force is zero - - var/armor = run_armor_check(affecting, "melee", "Your armour has protected your [hit_area].", "Your armour has softened hit to your [hit_area].", armour_penetration = I.armour_penetration) - var/weapon_sharp = is_sharp(I) - if(weapon_sharp && prob(getarmor(user.zone_selected, "melee"))) - weapon_sharp = 0 - if(armor >= 100) - return 0 - var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) - - apply_damage(I.force * weakness, I.damtype, affecting, armor, sharp = weapon_sharp, used_weapon = I) - - var/bloody = 0 - if(I.damtype == BRUTE && I.force && prob(25 + I.force * 2)) - I.add_mob_blood(src) //Make the weapon bloody, not the person. - if(prob(I.force * 2)) //blood spatter! - bloody = 1 - var/turf/location = loc - if(istype(location, /turf/simulated)) - add_splatter_floor(location) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood - H.add_mob_blood(src) - - if(!stat) - switch(hit_area) - if("head")//Harder to score a stun but if you do it lasts a bit longer - if(stat == CONSCIOUS && armor < 50) - if(prob(I.force)) - visible_message("[src] has been knocked down!", \ - "[src] has been knocked down!") - apply_effect(5, WEAKEN, armor) - AdjustConfused(15) - if(prob(I.force + ((100 - health)/2)) && src != user && I.damtype == BRUTE) - SSticker.mode.remove_revolutionary(mind) - - if(bloody)//Apply blood - if(wear_mask) - wear_mask.add_mob_blood(src) - update_inv_wear_mask(0) - if(head) - head.add_mob_blood(src) - update_inv_head(0,0) - if(glasses && prob(33)) - glasses.add_mob_blood(src) - update_inv_glasses(0) - - - if("chest")//Easier to score a stun but lasts less time - if(stat == CONSCIOUS && I.force && prob(I.force + 10)) - visible_message("[src] has been knocked down!", \ - "[src] has been knocked down!") - apply_effect(5, WEAKEN, armor) - - if(bloody) - if(wear_suit) - wear_suit.add_mob_blood(src) - update_inv_wear_suit(1) - if(w_uniform) - w_uniform.add_mob_blood(src) - update_inv_w_uniform(1) - - - - if(Iforce > 10 || Iforce >= 5 && prob(33)) - forcesay(GLOB.hit_appends) //forcesay checks stat already - - dna.species.spec_attacked_by(I, user, affecting, user.a_intent, src) - -//this proc handles being hit by a thrown atom -/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) - var/obj/item/I - var/throwpower = 30 - if(istype(AM, /obj/item)) - I = AM - throwpower = I.throwforce - if(I.thrownby == src) //No throwing stuff at yourself to trigger reactions - return ..() - if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK)) - hitpush = FALSE - skipcatch = TRUE - blocked = TRUE - else if(I) - if(((throwingdatum ? throwingdatum.speed : I.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || I.embedded_ignore_throwspeed_threshold) - if(can_embed(I)) - if(prob(I.embed_chance) && !(PIERCEIMMUNE in dna.species.species_traits)) - throw_alert("embeddedobject", /obj/screen/alert/embeddedobject) - var/obj/item/organ/external/L = pick(bodyparts) - L.embedded_objects |= I - I.add_mob_blood(src)//it embedded itself in you, of course it's bloody! - I.forceMove(src) - L.receive_damage(I.w_class*I.embedded_impact_pain_multiplier) - visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") - hitpush = FALSE - skipcatch = TRUE //can't catch the now embedded item - if(!blocked) - dna.species.spec_hitby(AM, src) - return ..() - -/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) - - if(gloves) - gloves.add_mob_blood(source) - gloves:transfer_blood = amount - else - add_mob_blood(source) - bloody_hands = amount - update_inv_gloves(1) //updates on-mob overlays for bloody hands and/or bloody gloves - -/mob/living/carbon/human/proc/bloody_body(var/mob/living/source) - if(wear_suit) - wear_suit.add_mob_blood(source) - update_inv_wear_suit(0) - return - if(w_uniform) - w_uniform.add_mob_blood(source) - update_inv_w_uniform(1) - -/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage) - - if(!wear_suit) return - if(!istype(wear_suit,/obj/item/clothing/suit/space)) return - if(damtype != BURN && damtype != BRUTE) return - - var/obj/item/clothing/suit/space/SS = wear_suit - var/penetrated_dam = max(0,(damage - max(0,(SS.breach_threshold - SS.damage)))) - - if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) - -/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - if(user.a_intent == INTENT_HARM) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) - to_chat(user, "You don't want to hurt [src]!") - return FALSE - var/hulk_verb = pick("smash", "pummel") - if(check_shields(user, 15, "the [hulk_verb]ing")) - return - ..(user, TRUE) - playsound(loc, user.dna.species.unarmed.attack_sound, 25, 1, -1) - var/message = "[user] has [hulk_verb]ed [src]!" - visible_message("[message]", "[message]") - adjustBruteLoss(15) - return TRUE - -/mob/living/carbon/human/attack_hand(mob/user) - if(..()) //to allow surgery to return properly. - return - if(ishuman(user)) - var/mob/living/carbon/human/H = user - dna.species.spec_attack_hand(H, src) - -/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L) - if(..()) //successful larva bite. - var/damage = rand(1, 3) - if(stat != DEAD) - L.amount_grown = min(L.amount_grown + damage, L.max_grown) - var/obj/item/organ/external/affecting = get_organ(ran_zone(L.zone_selected)) - var/armor_block = run_armor_check(affecting, "melee") - apply_damage(damage, BRUTE, affecting, armor_block) - updatehealth("larva attack") - -/mob/living/carbon/human/attack_alien(mob/living/carbon/alien/humanoid/M) - if(check_shields(M, 0, M.name)) - visible_message("[M] attempted to touch [src]!") - return 0 - - if(..()) - if(M.a_intent == INTENT_HARM) - if(w_uniform) - w_uniform.add_fingerprint(M) - var/damage = rand(15, 30) - if(!damage) - playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) - visible_message("[M] has lunged at [src]!") - return 0 - var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected)) - var/armor_block = run_armor_check(affecting, "melee") - - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - visible_message("[M] has slashed at [src]!", \ - "[M] has slashed at [src]!") - - apply_damage(damage, BRUTE, affecting, armor_block) - if(damage >= 25) - visible_message("[M] has wounded [src]!", \ - "[M] has wounded [src]!") - apply_effect(4, WEAKEN, armor_block) - add_attack_logs(M, src, "Alien attacked") - updatehealth("alien attack") - - if(M.a_intent == INTENT_DISARM) - if(prob(80)) - var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected)) - playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) - apply_effect(5, WEAKEN, run_armor_check(affecting, "melee")) - add_attack_logs(M, src, "Alien tackled") - visible_message("[M] has tackled down [src]!") - else - if(prob(99)) //this looks fucking stupid but it was previously 'var/randn = rand(1, 100); if(randn <= 99)' - playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) - drop_item() - visible_message("[M] disarmed [src]!") - else - playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) - visible_message("[M] has tried to disarm [src]!") - -/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) - . = ..() - if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration)) - return FALSE - var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") - var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) - if(!affecting) - affecting = get_organ("chest") - affecting.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data - var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration) - apply_damage(damage, M.melee_damage_type, affecting, armor) - updatehealth("animal attack") - -/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M) - if(..()) //successful slime attack - var/damage = rand(5, 25) - if(M.is_adult) - damage = rand(10, 35) - - if(check_shields(M, damage, "the [M.name]")) - return FALSE - - var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") - - var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) - if(!affecting) - affecting = get_organ("chest") - var/armor_block = run_armor_check(affecting, "melee") - apply_damage(damage, BRUTE, affecting, armor_block) - -/mob/living/carbon/human/mech_melee_attack(obj/mecha/M) - if(M.occupant.a_intent == INTENT_HARM) - if(HAS_TRAIT(M.occupant, TRAIT_PACIFISM)) - to_chat(M.occupant, "You don't want to harm other living beings!") - return - M.do_attack_animation(src) - if(M.damtype == "brute") - step_away(src,M,15) - var/obj/item/organ/external/affecting = get_organ(pick(BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_HEAD)) - if(affecting) - var/update = 0 - var/dmg = rand(M.force/2, M.force) - switch(M.damtype) - if("brute") - if(M.force > 35) // durand and other heavy mechas - Paralyse(1) - else if(M.force > 20 && !IsWeakened()) // lightweight mechas like gygax - Weaken(2) - update |= affecting.receive_damage(dmg, 0) - playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) - if("fire") - update |= affecting.receive_damage(dmg, 0) - playsound(src, 'sound/items/welder.ogg', 50, TRUE) - if("tox") - M.mech_toxin_damage(src) - else - return - if(update) - UpdateDamageIcon() - updatehealth("mech melee attack") - - M.occupant_message("You hit [src].") - visible_message("[M.name] hits [src]!", "[M.name] hits you!") - - add_attack_logs(M.occupant, src, "Mecha-meleed with [M]") - else - ..() - -/mob/living/carbon/human/experience_pressure_difference(pressure_difference, direction) - playsound(src, 'sound/effects/space_wind.ogg', 50, TRUE) - if(shoes && istype(shoes, /obj/item/clothing)) - var/obj/item/clothing/S = shoes - if (S.flags & NOSLIP) - return FALSE - return ..() - -/mob/living/carbon/human/water_act(volume, temperature, source, method = REAGENT_TOUCH) - . = ..() - dna.species.water_act(src, volume, temperature, source, method) - -/mob/living/carbon/human/is_eyes_covered(check_glasses = TRUE, check_head = TRUE, check_mask = TRUE) - if(check_glasses && glasses && (glasses.flags_cover & GLASSESCOVERSEYES)) - return TRUE - if(check_head && head && (head.flags_cover & HEADCOVERSEYES)) - return TRUE - if(check_mask && wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH)) - return TRUE - -/mob/living/carbon/human/proc/reagent_safety_check(hot = TRUE) - if(wear_mask) - to_chat(src, "Your [wear_mask.name] protects you from the [hot ? "hot" : "cold"] liquid!") - return FALSE - if(head) - to_chat(src, "Your [head.name] protects you from the [hot ? "hot" : "cold"] liquid!") - return FALSE - return TRUE +/* +Contains most of the procs that are called when a mob is attacked by something + +bullet_act +ex_act +meteor_act +emp_act + +*/ + + +/mob/living/carbon/human/bullet_act(obj/item/projectile/P, def_zone) + if(!dna.species.bullet_act(P, src)) + return FALSE + if(P.is_reflectable) + if(check_reflect(def_zone)) // Checks if you've passed a reflection% check + visible_message("The [P.name] gets reflected by [src]!", \ + "The [P.name] gets reflected by [src]!") + + P.reflect_back(src) + + return -1 // complete projectile permutation + + //Shields + if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration)) + P.on_hit(src, 100, def_zone) + return 2 + + var/obj/item/organ/external/organ = get_organ(check_zone(def_zone)) + if(isnull(organ)) + . = bullet_act(P, "chest") //act on chest instead + return + + organ.add_autopsy_data(P.name, P.damage) // Add the bullet's name to the autopsy data + + return (..(P , def_zone)) + +/mob/living/carbon/human/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + if(!I.tool_use_check(user, 1)) + return + var/obj/item/organ/external/S = bodyparts_by_name[user.zone_selected] + if(!S) + return + if(!S.is_robotic() || S.open == 2) + return + . = TRUE + if(S.brute_dam > ROBOLIMB_SELF_REPAIR_CAP) + to_chat(user, "The damage is far too severe to patch over externally.") + return + + if(!S.brute_dam) + to_chat(user, "Nothing to fix!") + return + + var/surgery_time = 0 + if(user == src) + surgery_time = 10 + if(!I.use_tool(src, user, surgery_time, amount = 1, volume = I.tool_volume)) + return + var/rembrute = HEALPERWELD + var/nrembrute = 0 + var/childlist + if(!isnull(S.children)) + childlist = S.children.Copy() + var/parenthealed = FALSE + while(rembrute > 0) + var/obj/item/organ/external/E + if(S.brute_dam) + E = S + else if(LAZYLEN(childlist)) + E = pick_n_take(childlist) + if(!E.brute_dam || !E.is_robotic()) + continue + else if(S.parent && !parenthealed) + E = S.parent + parenthealed = TRUE + if(!E.brute_dam || !E.is_robotic()) + break + else + break + nrembrute = max(rembrute - E.brute_dam, 0) + E.heal_damage(rembrute,0,0,1) + rembrute = nrembrute + user.visible_message("[user] patches some dents on [src]'s [E.name] with [I].") + if(bleed_rate && isSynthetic()) + bleed_rate = 0 + user.visible_message("[user] patches some leaks on [src] with [I].") + if(IgniteMob()) + message_admins("[key_name_admin(user)] set [key_name_admin(src)] on fire with [I]") + log_game("[key_name(user)] set [key_name(src)] on fire with [I]") + + +/mob/living/carbon/human/check_projectile_dismemberment(obj/item/projectile/P, def_zone) + var/obj/item/organ/external/affecting = get_organ(check_zone(def_zone)) + if(affecting && !affecting.cannot_amputate && affecting.get_damage() >= (affecting.max_damage - P.dismemberment)) + var/damtype = DROPLIMB_SHARP + switch(P.damage_type) + if(BRUTE) + damtype = DROPLIMB_BLUNT + if(BURN) + damtype = DROPLIMB_BURN + + affecting.droplimb(FALSE, damtype) + +/mob/living/carbon/human/getarmor(var/def_zone, var/type) + var/armorval = 0 + var/organnum = 0 + + if(def_zone) + if(isorgan(def_zone)) + return getarmor_organ(def_zone, type) + var/obj/item/organ/external/affecting = get_organ(def_zone) + if(affecting) + return getarmor_organ(affecting, type) + //If a specific bodypart is targetted, check how that bodypart is protected and return the value. + + //If you don't specify a bodypart, it checks ALL your bodyparts for protection, and averages out the values + for(var/obj/item/organ/external/organ in bodyparts) + armorval += getarmor_organ(organ, type) + organnum++ + + return (armorval/max(organnum, 1)) + + +//this proc returns the armour value for a particular external organ. +/mob/living/carbon/human/proc/getarmor_organ(var/obj/item/organ/external/def_zone, var/type) + if(!type || !def_zone) return 0 + var/protection = 0 + var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform, back, gloves, shoes, belt, s_store, glasses, l_ear, r_ear, wear_id) //Everything but pockets. Pockets are l_store and r_store. (if pockets were allowed, putting something armored, gloves or hats for example, would double up on the armor) + for(var/bp in body_parts) + if(!bp) continue + if(bp && istype(bp ,/obj/item/clothing)) + var/obj/item/clothing/C = bp + if(C.body_parts_covered & def_zone.body_part) + protection += C.armor[type] + + return protection + +//this proc returns the Siemens coefficient of electrical resistivity for a particular external organ. +/mob/living/carbon/human/proc/get_siemens_coefficient_organ(var/obj/item/organ/external/def_zone) + if(!def_zone) + return 1.0 + + var/siemens_coefficient = 1.0 + + var/list/clothing_items = list(head, wear_mask, wear_suit, w_uniform, gloves, shoes) // What all are we checking? + for(var/obj/item/clothing/C in clothing_items) + if(istype(C) && (C.body_parts_covered & def_zone.body_part)) // Is that body part being targeted covered? + siemens_coefficient *= C.siemens_coefficient + + return siemens_coefficient + +/mob/living/carbon/human/proc/check_head_coverage() + + var/list/body_parts = list(head, wear_mask, wear_suit, w_uniform) + for(var/bp in body_parts) + if(!bp) continue + if(bp && istype(bp ,/obj/item/clothing)) + var/obj/item/clothing/C = bp + if(C.body_parts_covered & HEAD) + return 1 + return 0 + +/mob/living/carbon/human/proc/check_reflect(var/def_zone) //Reflection checks for anything in your l_hand, r_hand, or wear_suit based on the reflection chance var of the object + if(wear_suit && istype(wear_suit, /obj/item/)) + var/obj/item/I = wear_suit + if(I.IsReflect(def_zone) == 1) + return 1 + if(l_hand && istype(l_hand, /obj/item/)) + var/obj/item/I = l_hand + if(I.IsReflect(def_zone) == 1) + return 1 + if(r_hand && istype(r_hand, /obj/item/)) + var/obj/item/I = r_hand + if(I.IsReflect(def_zone) == 1) + return 1 + return 0 + + +//End Here + +/mob/living/carbon/human/proc/check_shields(atom/AM, var/damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0) + var/block_chance_modifier = round(damage / -3) + + if(l_hand && !istype(l_hand, /obj/item/clothing)) + var/final_block_chance = l_hand.block_chance - (Clamp((armour_penetration-l_hand.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example + if(l_hand.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) + return 1 + if(r_hand && !istype(r_hand, /obj/item/clothing)) + var/final_block_chance = r_hand.block_chance - (Clamp((armour_penetration-r_hand.armour_penetration)/2,0,100)) + block_chance_modifier //Need to reset the var so it doesn't carry over modifications between attempts + if(r_hand.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) + return 1 + if(wear_suit) + var/final_block_chance = wear_suit.block_chance - (Clamp((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier + if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) + return 1 + if(w_uniform) + var/final_block_chance = w_uniform.block_chance - (Clamp((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier + if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type)) + return 1 + return 0 + +/mob/living/carbon/human/proc/check_block() + if(martial_art && prob(martial_art.block_chance) && martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE)) + return TRUE + +/mob/living/carbon/human/emp_act(severity) + for(var/obj/O in src) + if(!O) continue + O.emp_act(severity) + ..() + +/mob/living/carbon/human/acid_act(acidpwr, acid_volume, bodyzone_hit) //todo: update this to utilize check_obscured_slots() //and make sure it's check_obscured_slots(TRUE) to stop aciding through visors etc + var/list/damaged = list() + var/list/inventory_items_to_kill = list() + var/acidity = acidpwr * min(acid_volume * 0.005, 0.1) + //HEAD// + if(!bodyzone_hit || bodyzone_hit == "head") //only if we didn't specify a zone or if that zone is the head. + var/obj/item/clothing/head_clothes = null + if(glasses) + head_clothes = glasses + if(wear_mask) + head_clothes = wear_mask + if(head) + head_clothes = head + if(head_clothes) + if(!(head_clothes.resistance_flags & UNACIDABLE)) + head_clothes.acid_act(acidpwr, acid_volume) + update_inv_glasses() + update_inv_wear_mask() + update_inv_head() + else + to_chat(src, "Your [head_clothes.name] protects your head and face from the acid!") + else + . = get_organ("head") + if(.) + damaged += . + if(l_ear) + inventory_items_to_kill += l_ear + if(r_ear) + inventory_items_to_kill += r_ear + + //CHEST// + if(!bodyzone_hit || bodyzone_hit == "chest") + var/obj/item/clothing/chest_clothes = null + if(w_uniform) + chest_clothes = w_uniform + if(wear_suit) + chest_clothes = wear_suit + if(chest_clothes) + if(!(chest_clothes.resistance_flags & UNACIDABLE)) + chest_clothes.acid_act(acidpwr, acid_volume) + update_inv_w_uniform() + update_inv_wear_suit() + else + to_chat(src, "Your [chest_clothes.name] protects your body from the acid!") + else + . = get_organ("chest") + if(.) + damaged += . + if(wear_id) + inventory_items_to_kill += wear_id + if(wear_pda) + inventory_items_to_kill += wear_pda + if(r_store) + inventory_items_to_kill += r_store + if(l_store) + inventory_items_to_kill += l_store + if(s_store) + inventory_items_to_kill += s_store + + + //ARMS & HANDS// + if(!bodyzone_hit || bodyzone_hit == "l_arm" || bodyzone_hit == "r_arm") + var/obj/item/clothing/arm_clothes = null + if(gloves) + arm_clothes = gloves + if(w_uniform && ((w_uniform.body_parts_covered & HANDS) || (w_uniform.body_parts_covered & ARMS))) + arm_clothes = w_uniform + if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS))) + arm_clothes = wear_suit + + if(arm_clothes) + if(!(arm_clothes.resistance_flags & UNACIDABLE)) + arm_clothes.acid_act(acidpwr, acid_volume) + update_inv_gloves() + update_inv_w_uniform() + update_inv_wear_suit() + else + to_chat(src, "Your [arm_clothes.name] protects your arms and hands from the acid!") + else + . = get_organ("r_arm") + if(.) + damaged += . + . = get_organ("l_arm") + if(.) + damaged += . + + + //LEGS & FEET// + if(!bodyzone_hit || bodyzone_hit == "l_leg" || bodyzone_hit =="r_leg" || bodyzone_hit == "feet") + var/obj/item/clothing/leg_clothes = null + if(shoes) + leg_clothes = shoes + if(w_uniform && ((w_uniform.body_parts_covered & FEET) || (bodyzone_hit != "feet" && (w_uniform.body_parts_covered & LEGS)))) + leg_clothes = w_uniform + if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (bodyzone_hit != "feet" && (wear_suit.body_parts_covered & LEGS)))) + leg_clothes = wear_suit + if(leg_clothes) + if(!(leg_clothes.resistance_flags & UNACIDABLE)) + leg_clothes.acid_act(acidpwr, acid_volume) + update_inv_shoes() + update_inv_w_uniform() + update_inv_wear_suit() + else + to_chat(src, "Your [leg_clothes.name] protects your legs and feet from the acid!") + else + . = get_organ("r_leg") + if(.) + damaged += . + . = get_organ("l_leg") + if(.) + damaged += . + + + //DAMAGE// + for(var/obj/item/organ/external/affecting in damaged) + affecting.receive_damage(acidity, 2 * acidity) + + if(istype(affecting, /obj/item/organ/external/head)) + var/obj/item/organ/external/head/head_organ = affecting + if(prob(min(acidpwr * acid_volume / 10, 90))) //Applies disfigurement + head_organ.receive_damage(acidity, 2 * acidity) + emote("scream") + head_organ.h_style = "Bald" + head_organ.f_style = "Shaved" + update_hair() + update_fhair() + head_organ.disfigure() + + UpdateDamageIcon() + + //MELTING INVENTORY ITEMS// + //these items are all outside of armour visually, so melt regardless. + if(!bodyzone_hit) + if(back) + inventory_items_to_kill += back + if(belt) + inventory_items_to_kill += belt + if(l_hand) + inventory_items_to_kill += l_hand + if(r_hand) + inventory_items_to_kill += r_hand + + for(var/obj/item/I in inventory_items_to_kill) + I.acid_act(acidpwr, acid_volume) + return 1 + +/mob/living/carbon/human/emag_act(user as mob, var/obj/item/organ/external/affecting) + if(!istype(affecting)) + return + if(!affecting.is_robotic()) + to_chat(user, "That limb isn't robotic.") + return + if(affecting.sabotaged) + to_chat(user, "[src]'s [affecting.name] is already sabotaged!") + else + to_chat(user, "You sneakily slide the card into the dataport on [src]'s [affecting.name] and short out the safeties.") + affecting.sabotaged = 1 + return 1 + +/mob/living/carbon/human/grabbedby(mob/living/user) + if(w_uniform) + w_uniform.add_fingerprint(user) + return ..() + +//Returns 1 if the attack hit, 0 if it missed. +/mob/living/carbon/human/attacked_by(obj/item/I, mob/living/user, def_zone) + if(!I || !user) + return 0 + + if((istype(I, /obj/item/kitchen/knife/butcher/meatcleaver) || istype(I, /obj/item/twohanded/chainsaw)) && stat == DEAD && user.a_intent == INTENT_HARM) + var/obj/item/reagent_containers/food/snacks/meat/human/newmeat = new /obj/item/reagent_containers/food/snacks/meat/human(get_turf(loc)) + newmeat.name = real_name + newmeat.name + newmeat.subjectname = real_name + newmeat.subjectjob = job + newmeat.reagents.add_reagent("nutriment", (nutrition / 15) / 3) + reagents.trans_to(newmeat, round((reagents.total_volume) / 3, 1)) + add_mob_blood(src) + --meatleft + to_chat(user, "You hack off a chunk of meat from [name]") + if(!meatleft) + add_attack_logs(user, src, "Chopped up into meat") + if(!iscarbon(user)) + LAssailant = null + else + LAssailant = user + + qdel(src) + + var/obj/item/organ/external/affecting = get_organ(ran_zone(user.zone_selected)) + if(!affecting) + to_chat(user, "They are missing that limb!") + return 1 + var/hit_area = parse_zone(affecting.limb_name) + + if(user != src) + user.do_attack_animation(src) + if(check_shields(I, I.force, "the [I.name]", MELEE_ATTACK, I.armour_penetration)) + return 0 + + if(check_block()) + visible_message("[src] blocks [I]!") + return FALSE + + if(istype(I,/obj/item/card/emag)) + emag_act(user, affecting) + + send_item_attack_message(I, user, hit_area) + + var/weakness = check_weakness(I,user) + + if(!I.force) + return 0 //item force is zero + + var/armor = run_armor_check(affecting, "melee", "Your armour has protected your [hit_area].", "Your armour has softened hit to your [hit_area].", armour_penetration = I.armour_penetration) + var/weapon_sharp = is_sharp(I) + if(weapon_sharp && prob(getarmor(user.zone_selected, "melee"))) + weapon_sharp = 0 + if(armor >= 100) + return 0 + var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + + apply_damage(I.force * weakness, I.damtype, affecting, armor, sharp = weapon_sharp, used_weapon = I) + + var/bloody = 0 + if(I.damtype == BRUTE && I.force && prob(25 + I.force * 2)) + I.add_mob_blood(src) //Make the weapon bloody, not the person. + if(prob(I.force * 2)) //blood spatter! + bloody = 1 + var/turf/location = loc + if(istype(location, /turf/simulated)) + add_splatter_floor(location) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood + H.add_mob_blood(src) + + if(!stat) + switch(hit_area) + if("head")//Harder to score a stun but if you do it lasts a bit longer + if(stat == CONSCIOUS && armor < 50) + if(prob(I.force)) + visible_message("[src] has been knocked down!", \ + "[src] has been knocked down!") + apply_effect(5, WEAKEN, armor) + AdjustConfused(15) + if(prob(I.force + ((100 - health)/2)) && src != user && I.damtype == BRUTE) + SSticker.mode.remove_revolutionary(mind) + + if(bloody)//Apply blood + if(wear_mask) + wear_mask.add_mob_blood(src) + update_inv_wear_mask(0) + if(head) + head.add_mob_blood(src) + update_inv_head(0,0) + if(glasses && prob(33)) + glasses.add_mob_blood(src) + update_inv_glasses(0) + + + if("chest")//Easier to score a stun but lasts less time + if(stat == CONSCIOUS && I.force && prob(I.force + 10)) + visible_message("[src] has been knocked down!", \ + "[src] has been knocked down!") + apply_effect(5, WEAKEN, armor) + + if(bloody) + if(wear_suit) + wear_suit.add_mob_blood(src) + update_inv_wear_suit(1) + if(w_uniform) + w_uniform.add_mob_blood(src) + update_inv_w_uniform(1) + + + + if(Iforce > 10 || Iforce >= 5 && prob(33)) + forcesay(GLOB.hit_appends) //forcesay checks stat already + + dna.species.spec_attacked_by(I, user, affecting, user.a_intent, src) + +//this proc handles being hit by a thrown atom +/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) + var/obj/item/I + var/throwpower = 30 + if(istype(AM, /obj/item)) + I = AM + throwpower = I.throwforce + if(I.thrownby == src) //No throwing stuff at yourself to trigger reactions + return ..() + if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK)) + hitpush = FALSE + skipcatch = TRUE + blocked = TRUE + else if(I) + if(((throwingdatum ? throwingdatum.speed : I.throw_speed) >= EMBED_THROWSPEED_THRESHOLD) || I.embedded_ignore_throwspeed_threshold) + if(can_embed(I)) + if(prob(I.embed_chance) && !(PIERCEIMMUNE in dna.species.species_traits)) + throw_alert("embeddedobject", /obj/screen/alert/embeddedobject) + var/obj/item/organ/external/L = pick(bodyparts) + L.embedded_objects |= I + I.add_mob_blood(src)//it embedded itself in you, of course it's bloody! + I.forceMove(src) + L.receive_damage(I.w_class*I.embedded_impact_pain_multiplier) + visible_message("[I] embeds itself in [src]'s [L.name]!","[I] embeds itself in your [L.name]!") + hitpush = FALSE + skipcatch = TRUE //can't catch the now embedded item + if(!blocked) + dna.species.spec_hitby(AM, src) + return ..() + +/mob/living/carbon/human/proc/bloody_hands(var/mob/living/source, var/amount = 2) + + if(gloves) + gloves.add_mob_blood(source) + gloves:transfer_blood = amount + else + add_mob_blood(source) + bloody_hands = amount + update_inv_gloves(1) //updates on-mob overlays for bloody hands and/or bloody gloves + +/mob/living/carbon/human/proc/bloody_body(var/mob/living/source) + if(wear_suit) + wear_suit.add_mob_blood(source) + update_inv_wear_suit(0) + return + if(w_uniform) + w_uniform.add_mob_blood(source) + update_inv_w_uniform(1) + +/mob/living/carbon/human/proc/handle_suit_punctures(var/damtype, var/damage) + + if(!wear_suit) return + if(!istype(wear_suit,/obj/item/clothing/suit/space)) return + if(damtype != BURN && damtype != BRUTE) return + + var/obj/item/clothing/suit/space/SS = wear_suit + var/penetrated_dam = max(0,(damage - max(0,(SS.breach_threshold - SS.damage)))) + + if(penetrated_dam) SS.create_breaches(damtype, penetrated_dam) + +/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + to_chat(user, "You don't want to hurt [src]!") + return FALSE + var/hulk_verb = pick("smash", "pummel") + if(check_shields(user, 15, "the [hulk_verb]ing")) + return + ..(user, TRUE) + playsound(loc, user.dna.species.unarmed.attack_sound, 25, 1, -1) + var/message = "[user] has [hulk_verb]ed [src]!" + visible_message("[message]", "[message]") + adjustBruteLoss(15) + return TRUE + +/mob/living/carbon/human/attack_hand(mob/user) + if(..()) //to allow surgery to return properly. + return + if(ishuman(user)) + var/mob/living/carbon/human/H = user + dna.species.spec_attack_hand(H, src) + +/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L) + if(..()) //successful larva bite. + var/damage = rand(1, 3) + if(stat != DEAD) + L.amount_grown = min(L.amount_grown + damage, L.max_grown) + var/obj/item/organ/external/affecting = get_organ(ran_zone(L.zone_selected)) + var/armor_block = run_armor_check(affecting, "melee") + apply_damage(damage, BRUTE, affecting, armor_block) + updatehealth("larva attack") + +/mob/living/carbon/human/attack_alien(mob/living/carbon/alien/humanoid/M) + if(check_shields(M, 0, M.name)) + visible_message("[M] attempted to touch [src]!") + return 0 + + if(..()) + if(M.a_intent == INTENT_HARM) + if(w_uniform) + w_uniform.add_fingerprint(M) + var/damage = rand(15, 30) + if(!damage) + playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) + visible_message("[M] has lunged at [src]!") + return 0 + var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected)) + var/armor_block = run_armor_check(affecting, "melee") + + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) + visible_message("[M] has slashed at [src]!", \ + "[M] has slashed at [src]!") + + apply_damage(damage, BRUTE, affecting, armor_block) + if(damage >= 25) + visible_message("[M] has wounded [src]!", \ + "[M] has wounded [src]!") + apply_effect(4, WEAKEN, armor_block) + add_attack_logs(M, src, "Alien attacked") + updatehealth("alien attack") + + if(M.a_intent == INTENT_DISARM) + if(prob(80)) + var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected)) + playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1) + apply_effect(5, WEAKEN, run_armor_check(affecting, "melee")) + add_attack_logs(M, src, "Alien tackled") + visible_message("[M] has tackled down [src]!") + else + if(prob(99)) //this looks fucking stupid but it was previously 'var/randn = rand(1, 100); if(randn <= 99)' + playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1) + drop_item() + visible_message("[M] disarmed [src]!") + else + playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1) + visible_message("[M] has tried to disarm [src]!") + +/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M) + . = ..() + if(.) + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration)) + return FALSE + var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") + var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) + if(!affecting) + affecting = get_organ("chest") + affecting.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data + var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration) + apply_damage(damage, M.melee_damage_type, affecting, armor) + updatehealth("animal attack") + +/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M) + if(..()) //successful slime attack + var/damage = rand(5, 25) + if(M.is_adult) + damage = rand(10, 35) + + if(check_shields(M, damage, "the [M.name]")) + return FALSE + + var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") + + var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone)) + if(!affecting) + affecting = get_organ("chest") + var/armor_block = run_armor_check(affecting, "melee") + apply_damage(damage, BRUTE, affecting, armor_block) + +/mob/living/carbon/human/mech_melee_attack(obj/mecha/M) + if(M.occupant.a_intent == INTENT_HARM) + if(HAS_TRAIT(M.occupant, TRAIT_PACIFISM)) + to_chat(M.occupant, "You don't want to harm other living beings!") + return + M.do_attack_animation(src) + if(M.damtype == "brute") + step_away(src,M,15) + var/obj/item/organ/external/affecting = get_organ(pick(BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_CHEST, BODY_ZONE_HEAD)) + if(affecting) + var/update = 0 + var/dmg = rand(M.force/2, M.force) + switch(M.damtype) + if("brute") + if(M.force > 35) // durand and other heavy mechas + Paralyse(1) + else if(M.force > 20 && !IsWeakened()) // lightweight mechas like gygax + Weaken(2) + update |= affecting.receive_damage(dmg, 0) + playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) + if("fire") + update |= affecting.receive_damage(dmg, 0) + playsound(src, 'sound/items/welder.ogg', 50, TRUE) + if("tox") + M.mech_toxin_damage(src) + else + return + if(update) + UpdateDamageIcon() + updatehealth("mech melee attack") + + M.occupant_message("You hit [src].") + visible_message("[M.name] hits [src]!", "[M.name] hits you!") + + add_attack_logs(M.occupant, src, "Mecha-meleed with [M]") + else + ..() + +/mob/living/carbon/human/experience_pressure_difference(pressure_difference, direction) + playsound(src, 'sound/effects/space_wind.ogg', 50, TRUE) + if(shoes && istype(shoes, /obj/item/clothing)) + var/obj/item/clothing/S = shoes + if (S.flags & NOSLIP) + return FALSE + return ..() + +/mob/living/carbon/human/water_act(volume, temperature, source, method = REAGENT_TOUCH) + . = ..() + dna.species.water_act(src, volume, temperature, source, method) + +/mob/living/carbon/human/is_eyes_covered(check_glasses = TRUE, check_head = TRUE, check_mask = TRUE) + if(check_glasses && glasses && (glasses.flags_cover & GLASSESCOVERSEYES)) + return TRUE + if(check_head && head && (head.flags_cover & HEADCOVERSEYES)) + return TRUE + if(check_mask && wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH)) + return TRUE + +/mob/living/carbon/human/proc/reagent_safety_check(hot = TRUE) + if(wear_mask) + to_chat(src, "Your [wear_mask.name] protects you from the [hot ? "hot" : "cold"] liquid!") + return FALSE + if(head) + to_chat(src, "Your [head.name] protects you from the [hot ? "hot" : "cold"] liquid!") + return FALSE + return TRUE diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index dd56b5fc05c4..b0bc9e610f39 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -1,74 +1,74 @@ -var/global/default_martial_art = new/datum/martial_art -/mob/living/carbon/human - - hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD) - pressure_resistance = 25 - //Marking colour and style - var/list/m_colours = DEFAULT_MARKING_COLOURS //All colours set to #000000. - var/list/m_styles = DEFAULT_MARKING_STYLES //All markings set to None. - - var/s_tone = 0 //Skin tone - - //Skin colour - var/skin_colour = "#000000" - - var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup - var/lip_color = "white" - - var/age = 30 //Player's age (pure fluff) - - var/underwear = "Nude" //Which underwear the player wants - var/undershirt = "Nude" //Which undershirt the player wants - var/socks = "Nude" //Which socks the player wants - var/backbag = 2 //Which backpack type the player has chosen. Nothing, Satchel or Backpack. - - //Equipment slots - var/obj/item/w_uniform = null - var/obj/item/shoes = null - var/obj/item/belt = null - var/obj/item/gloves = null - var/obj/item/glasses = null - var/obj/item/l_ear = null - var/obj/item/r_ear = null - var/obj/item/wear_id = null - var/obj/item/wear_pda = null - var/obj/item/r_store = null - var/obj/item/l_store = null - var/obj/item/s_store = null - - var/icon/stand_icon = null - var/icon/lying_icon = null - - var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() - - var/datum/personal_crafting/handcrafting - - var/datum/martial_art/martial_art = null - - var/special_voice = "" // For changing our voice. Used by a symptom. - - var/hand_blood_color - - var/name_override //For temporary visible name changes - - var/xylophone = 0 //For the spoooooooky xylophone cooldown - - var/mob/remoteview_target = null - var/meatleft = 3 //For chef item - var/decaylevel = 0 // For rotting bodies - var/max_blood = BLOOD_VOLUME_NORMAL // For stuff in the vessel - var/bleed_rate = 0 - var/bleedsuppress = 0 //for stopping bloodloss - - var/check_mutations=0 // Check mutations on next life tick - - var/heartbeat = 0 - var/receiving_cpr = FALSE - - var/fire_dmi = 'icons/mob/OnFire.dmi' - var/fire_sprite = "Standing" - - var/datum/body_accessory/body_accessory = null - var/tail // Name of tail image in species effects icon file. - - var/list/splinted_limbs = list() //limbs we know are splinted +GLOBAL_DATUM_INIT(default_martial_art, /datum/martial_art, new()) +/mob/living/carbon/human + + hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD) + pressure_resistance = 25 + //Marking colour and style + var/list/m_colours = DEFAULT_MARKING_COLOURS //All colours set to #000000. + var/list/m_styles = DEFAULT_MARKING_STYLES //All markings set to None. + + var/s_tone = 0 //Skin tone + + //Skin colour + var/skin_colour = "#000000" + + var/lip_style = null //no lipstick by default- arguably misleading, as it could be used for general makeup + var/lip_color = "white" + + var/age = 30 //Player's age (pure fluff) + + var/underwear = "Nude" //Which underwear the player wants + var/undershirt = "Nude" //Which undershirt the player wants + var/socks = "Nude" //Which socks the player wants + var/backbag = 2 //Which backpack type the player has chosen. Nothing, Satchel or Backpack. + + //Equipment slots + var/obj/item/w_uniform = null + var/obj/item/shoes = null + var/obj/item/belt = null + var/obj/item/gloves = null + var/obj/item/glasses = null + var/obj/item/l_ear = null + var/obj/item/r_ear = null + var/obj/item/wear_id = null + var/obj/item/wear_pda = null + var/obj/item/r_store = null + var/obj/item/l_store = null + var/obj/item/s_store = null + + var/icon/stand_icon = null + var/icon/lying_icon = null + + var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() + + var/datum/personal_crafting/handcrafting + + var/datum/martial_art/martial_art = null + + var/special_voice = "" // For changing our voice. Used by a symptom. + + var/hand_blood_color + + var/name_override //For temporary visible name changes + + var/xylophone = 0 //For the spoooooooky xylophone cooldown + + var/mob/remoteview_target = null + var/meatleft = 3 //For chef item + var/decaylevel = 0 // For rotting bodies + var/max_blood = BLOOD_VOLUME_NORMAL // For stuff in the vessel + var/bleed_rate = 0 + var/bleedsuppress = 0 //for stopping bloodloss + + var/check_mutations=0 // Check mutations on next life tick + + var/heartbeat = 0 + var/receiving_cpr = FALSE + + var/fire_dmi = 'icons/mob/OnFire.dmi' + var/fire_sprite = "Standing" + + var/datum/body_accessory/body_accessory = null + var/tail // Name of tail image in species effects icon file. + + var/list/splinted_limbs = list() //limbs we know are splinted diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 8f2a2c4b5c98..85bf001a2067 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,133 +1,133 @@ -/mob/living/carbon/human/movement_delay() - . = 0 - . += ..() - . += config.human_delay - . += dna.species.movement_delay(src) - -/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) - - if(..()) - return 1 - - //Do we have a working jetpack? - var/obj/item/tank/jetpack/thrust - if(istype(back, /obj/item/tank/jetpack)) - thrust = back - else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit)) - var/obj/item/clothing/suit/space/hardsuit/C = wear_suit - thrust = C.jetpack - else if(istype(back,/obj/item/rig)) - var/obj/item/rig/rig = back - for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) - thrust = module.jets - break - - if(thrust) - if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src)) - return 1 - return 0 - -/mob/living/carbon/human/mob_has_gravity() - . = ..() - if(!.) - if(mob_negates_gravity()) - . = 1 - -/mob/living/carbon/human/mob_negates_gravity() - return shoes && shoes.negates_gravity() - -/mob/living/carbon/human/Move(NewLoc, direct) - . = ..() - if(.) // did we actually move? - if(!lying && !buckled && !throwing) - for(var/obj/item/organ/external/splinted in splinted_limbs) - splinted.update_splints() - - if(!has_gravity(loc)) - return - - var/obj/item/clothing/shoes/S = shoes - - //Bloody footprints - var/turf/T = get_turf(src) - var/obj/item/organ/external/l_foot = get_organ("l_foot") - var/obj/item/organ/external/r_foot = get_organ("r_foot") - var/hasfeet = TRUE - if(!l_foot && !r_foot) - hasfeet = FALSE - - if(shoes) - if(S.bloody_shoes && S.bloody_shoes[S.blood_state]) - for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T) - if(oldFP && oldFP.blood_state == S.blood_state && oldFP.basecolor == S.blood_color) - return - //No oldFP or it's a different kind of blood - S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP) - if(S.bloody_shoes[S.blood_state] > BLOOD_LOSS_IN_SPREAD) - createFootprintsFrom(shoes, dir, T) - update_inv_shoes() - else if(hasfeet) - if(bloody_feet && bloody_feet[blood_state]) - for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T) - if(oldFP && oldFP.blood_state == blood_state && oldFP.basecolor == feet_blood_color) - return - bloody_feet[blood_state] = max(0, bloody_feet[blood_state] - BLOOD_LOSS_PER_STEP) - if(bloody_feet[blood_state] > BLOOD_LOSS_IN_SPREAD) - createFootprintsFrom(src, dir, T) - update_inv_shoes() - //End bloody footprints - if(S) - S.step_action(src) - -/mob/living/carbon/human/handle_footstep(turf/T) - if(..()) - if(T.footstep_sounds["human"]) - var/S = pick(T.footstep_sounds["human"]) - if(S) - if(m_intent == MOVE_INTENT_RUN) - if(!(step_count % 2)) //every other turf makes a sound - return 0 - - if(istype(shoes, /obj/item/clothing/shoes)) - var/obj/item/clothing/shoes/shooess = shoes - if(shooess.silence_steps) - return 0 //silent - if(shooess.shoe_sound) - return //Handle it on the shoe - - var/range = -(world.view - 2) - if(m_intent == MOVE_INTENT_WALK) - range -= 0.333 - if(!shoes) - range -= 0.333 - - //shoes + running - //-(7 - 2) = -(5) = -5 | -5 - 0 = -5 | (7 + -5) = 2 | 2 * 3 = 6 | range(6) = range(6) - //running OR shoes - //-(7 - 2) = (-5) = -5 | -5 - 0.333 = -5.333 | (7 + -5.333) = 1.667 | 1.667 * 3 = 5.001 | range(5.001) = range(5) - //walking AND no shoes - //-(7 - 2) = (-5) = -5 | -5 - (0.333 * 2) = -5.666 | (7 + -5.666) = 1.334 | 1.334 * 3 = 4.002 | range(4.002) = range(4) - - var/volume = 13 - if(m_intent == MOVE_INTENT_WALK) - volume -= 4 - if(!shoes) - volume -= 4 - - if(!has_organ("l_foot") && !has_organ("r_foot")) - return 0 //no feet no footsteps - - if(buckled || lying || throwing) - return 0 //people flying, lying down or sitting do not step - - if(!has_gravity(src)) - if(step_count % 3) //this basically says, every three moves make a noise - return 0 //1st - none, 1%3==1, 2nd - none, 2%3==2, 3rd - noise, 3%3==0 - - if(dna.species.silent_steps) - return 0 //species is silent - - playsound(T, S, volume, 1, range) - return 1 - - return 0 +/mob/living/carbon/human/movement_delay() + . = 0 + . += ..() + . += config.human_delay + . += dna.species.movement_delay(src) + +/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) + + if(..()) + return 1 + + //Do we have a working jetpack? + var/obj/item/tank/jetpack/thrust + if(istype(back, /obj/item/tank/jetpack)) + thrust = back + else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit)) + var/obj/item/clothing/suit/space/hardsuit/C = wear_suit + thrust = C.jetpack + else if(istype(back,/obj/item/rig)) + var/obj/item/rig/rig = back + for(var/obj/item/rig_module/maneuvering_jets/module in rig.installed_modules) + thrust = module.jets + break + + if(thrust) + if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src)) + return 1 + return 0 + +/mob/living/carbon/human/mob_has_gravity() + . = ..() + if(!.) + if(mob_negates_gravity()) + . = 1 + +/mob/living/carbon/human/mob_negates_gravity() + return shoes && shoes.negates_gravity() + +/mob/living/carbon/human/Move(NewLoc, direct) + . = ..() + if(.) // did we actually move? + if(!lying && !buckled && !throwing) + for(var/obj/item/organ/external/splinted in splinted_limbs) + splinted.update_splints() + + if(!has_gravity(loc)) + return + + var/obj/item/clothing/shoes/S = shoes + + //Bloody footprints + var/turf/T = get_turf(src) + var/obj/item/organ/external/l_foot = get_organ("l_foot") + var/obj/item/organ/external/r_foot = get_organ("r_foot") + var/hasfeet = TRUE + if(!l_foot && !r_foot) + hasfeet = FALSE + + if(shoes) + if(S.bloody_shoes && S.bloody_shoes[S.blood_state]) + for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T) + if(oldFP && oldFP.blood_state == S.blood_state && oldFP.basecolor == S.blood_color) + return + //No oldFP or it's a different kind of blood + S.bloody_shoes[S.blood_state] = max(0, S.bloody_shoes[S.blood_state] - BLOOD_LOSS_PER_STEP) + if(S.bloody_shoes[S.blood_state] > BLOOD_LOSS_IN_SPREAD) + createFootprintsFrom(shoes, dir, T) + update_inv_shoes() + else if(hasfeet) + if(bloody_feet && bloody_feet[blood_state]) + for(var/obj/effect/decal/cleanable/blood/footprints/oldFP in T) + if(oldFP && oldFP.blood_state == blood_state && oldFP.basecolor == feet_blood_color) + return + bloody_feet[blood_state] = max(0, bloody_feet[blood_state] - BLOOD_LOSS_PER_STEP) + if(bloody_feet[blood_state] > BLOOD_LOSS_IN_SPREAD) + createFootprintsFrom(src, dir, T) + update_inv_shoes() + //End bloody footprints + if(S) + S.step_action(src) + +/mob/living/carbon/human/handle_footstep(turf/T) + if(..()) + if(T.footstep_sounds["human"]) + var/S = pick(T.footstep_sounds["human"]) + if(S) + if(m_intent == MOVE_INTENT_RUN) + if(!(step_count % 2)) //every other turf makes a sound + return 0 + + if(istype(shoes, /obj/item/clothing/shoes)) + var/obj/item/clothing/shoes/shooess = shoes + if(shooess.silence_steps) + return 0 //silent + if(shooess.shoe_sound) + return //Handle it on the shoe + + var/range = -(world.view - 2) + if(m_intent == MOVE_INTENT_WALK) + range -= 0.333 + if(!shoes) + range -= 0.333 + + //shoes + running + //-(7 - 2) = -(5) = -5 | -5 - 0 = -5 | (7 + -5) = 2 | 2 * 3 = 6 | range(6) = range(6) + //running OR shoes + //-(7 - 2) = (-5) = -5 | -5 - 0.333 = -5.333 | (7 + -5.333) = 1.667 | 1.667 * 3 = 5.001 | range(5.001) = range(5) + //walking AND no shoes + //-(7 - 2) = (-5) = -5 | -5 - (0.333 * 2) = -5.666 | (7 + -5.666) = 1.334 | 1.334 * 3 = 4.002 | range(4.002) = range(4) + + var/volume = 13 + if(m_intent == MOVE_INTENT_WALK) + volume -= 4 + if(!shoes) + volume -= 4 + + if(!has_organ("l_foot") && !has_organ("r_foot")) + return 0 //no feet no footsteps + + if(buckled || lying || throwing) + return 0 //people flying, lying down or sitting do not step + + if(!has_gravity(src)) + if(step_count % 3) //this basically says, every three moves make a noise + return 0 //1st - none, 1%3==1, 2nd - none, 2%3==2, 3rd - noise, 3%3==0 + + if(dna.species.silent_steps) + return 0 //species is silent + + playsound(T, S, volume, 1, range) + return 1 + + return 0 diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 68ae7fe3af0f..953ea6bae1d1 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,437 +1,437 @@ -/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) - for(var/slot in slots) - if(equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) - return slot - if(del_on_fail) - qdel(W) - return null - -/mob/living/carbon/human/proc/is_in_hands(var/typepath) - if(istype(l_hand,typepath)) - return l_hand - if(istype(r_hand,typepath)) - return r_hand - return 0 - - -/mob/living/carbon/human/proc/has_organ(name) - var/obj/item/organ/external/O = bodyparts_by_name[name] - return O - -/mob/living/carbon/human/proc/has_organ_for_slot(slot) - switch(slot) - if(slot_back) - return has_organ("chest") - if(slot_wear_mask) - return has_organ("head") - if(slot_handcuffed) - return has_organ("l_hand") && has_organ("r_hand") - if(slot_legcuffed) - return has_organ("l_leg") && has_organ("r_leg") - if(slot_l_hand) - return has_organ("l_hand") - if(slot_r_hand) - return has_organ("r_hand") - if(slot_belt) - return has_organ("chest") - if(slot_wear_id) - // the only relevant check for this is the uniform check - return TRUE - if(slot_wear_pda) - return TRUE - if(slot_l_ear) - return has_organ("head") - if(slot_r_ear) - return has_organ("head") - if(slot_glasses) - return has_organ("head") - if(slot_gloves) - return has_organ("l_hand") && has_organ("r_hand") - if(slot_head) - return has_organ("head") - if(slot_shoes) - return has_organ("r_foot") && has_organ("l_foot") - if(slot_wear_suit) - return has_organ("chest") - if(slot_w_uniform) - return has_organ("chest") - if(slot_l_store) - return has_organ("chest") - if(slot_r_store) - return has_organ("chest") - if(slot_s_store) - return has_organ("chest") - if(slot_in_backpack) - return TRUE - if(slot_tie) - return TRUE - -// The actual dropping happens at the mob level - checks to prevent drops should -// come here -/mob/living/carbon/human/canUnEquip(obj/item/I, force) - . = ..() - var/obj/item/organ/O = I - if(istype(O) && O.owner == src) - . = 0 // keep a good grip on your heart - -/mob/living/carbon/human/unEquip(obj/item/I, force) - . = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should. - if(!. || !I) - return - - if(I == wear_suit) - if(s_store) - unEquip(s_store, 1) //It makes no sense for your suit storage to stay on you if you drop your suit. - wear_suit = null - if(I.flags_inv & HIDEJUMPSUIT) - update_inv_w_uniform() - update_inv_wear_suit() - else if(I == w_uniform) - if(r_store) - unEquip(r_store, 1) //Again, makes sense for pockets to drop. - if(l_store) - unEquip(l_store, 1) - if(wear_id) - unEquip(wear_id) - if(belt) - unEquip(belt) - w_uniform = null - update_inv_w_uniform() - else if(I == gloves) - gloves = null - update_inv_gloves() - else if(I == glasses) - glasses = null - var/obj/item/clothing/glasses/G = I - if(G.tint) - update_tint() - if(G.prescription) - update_nearsighted_effects() - if(G.vision_flags || G.see_in_dark || G.invis_override || G.invis_view || !isnull(G.lighting_alpha)) - update_sight() - update_inv_glasses() - update_client_colour() - else if(I == head) - head = null - if(I.flags & BLOCKHAIR || I.flags & BLOCKHEADHAIR) - update_hair() //rebuild hair - update_fhair() - update_head_accessory() - // Bandanas and paper hats go on the head but are not head clothing - if(istype(I,/obj/item/clothing/head)) - var/obj/item/clothing/head/hat = I - if(hat.vision_flags || hat.see_in_dark || !isnull(hat.lighting_alpha)) - update_sight() - head_update(I) - update_inv_head() - else if(I == r_ear) - r_ear = null - update_inv_ears() - else if(I == l_ear) - l_ear = null - update_inv_ears() - else if(I == shoes) - shoes = null - update_inv_shoes() - else if(I == belt) - belt = null - update_inv_belt() - else if(I == wear_mask) - wear_mask = null - if(I.flags & BLOCKHAIR || I.flags & BLOCKHEADHAIR) - update_hair() //rebuild hair - update_fhair() - update_head_accessory() - if(internal && !get_organ_slot("breathing_tube")) - internal = null - update_action_buttons_icon() - wear_mask_update(I, toggle_off = FALSE) - sec_hud_set_ID() - update_inv_wear_mask() - else if(I == wear_id) - wear_id = null - sec_hud_set_ID() - update_inv_wear_id() - else if(I == wear_pda) - wear_pda = null - update_inv_wear_pda() - else if(I == r_store) - r_store = null - update_inv_pockets() - else if(I == l_store) - l_store = null - update_inv_pockets() - else if(I == s_store) - s_store = null - update_inv_s_store() - else if(I == back) - back = null - update_inv_back() - else if(I == r_hand) - r_hand = null - update_inv_r_hand() - else if(I == l_hand) - l_hand = null - update_inv_l_hand() - - - - -//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() -//set redraw_mob to 0 if you don't wish the hud to be updated - if you're doing it manually in your own proc. -/mob/living/carbon/human/equip_to_slot(obj/item/I, slot, redraw_mob = 1) - if(!slot) - return - if(!istype(I)) - return - if(!has_organ_for_slot(slot)) - return - - if(I == src.l_hand) - src.l_hand = null - update_inv_l_hand() //So items actually disappear from hands. - else if(I == src.r_hand) - src.r_hand = null - update_inv_r_hand() - - I.screen_loc = null - I.forceMove(src) - I.equipped(src, slot) - I.layer = ABOVE_HUD_LAYER - I.plane = ABOVE_HUD_PLANE - - switch(slot) - if(slot_back) - back = I - update_inv_back(redraw_mob) - if(slot_wear_mask) - wear_mask = I - if((wear_mask.flags & BLOCKHAIR) || (wear_mask.flags & BLOCKHEADHAIR)) - update_hair(redraw_mob) //rebuild hair - update_fhair(redraw_mob) - update_head_accessory(redraw_mob) - if(hud_list.len) - sec_hud_set_ID() - wear_mask_update(I, toggle_off = TRUE) - update_inv_wear_mask(redraw_mob) - if(slot_handcuffed) - handcuffed = I - update_inv_handcuffed(redraw_mob) - if(slot_legcuffed) - legcuffed = I - update_inv_legcuffed(redraw_mob) - if(slot_l_hand) - l_hand = I - update_inv_l_hand(redraw_mob) - if(slot_r_hand) - r_hand = I - update_inv_r_hand(redraw_mob) - if(slot_belt) - belt = I - update_inv_belt(redraw_mob) - if(slot_wear_id) - wear_id = I - if(hud_list.len) - sec_hud_set_ID() - update_inv_wear_id(redraw_mob) - if(slot_wear_pda) - wear_pda = I - update_inv_wear_pda(redraw_mob) - if(slot_l_ear) - l_ear = I - if(l_ear.slot_flags & SLOT_TWOEARS) - var/obj/item/clothing/ears/offear/O = new(I) - O.forceMove(src) - r_ear = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - update_inv_ears(redraw_mob) - if(slot_r_ear) - r_ear = I - if(r_ear.slot_flags & SLOT_TWOEARS) - var/obj/item/clothing/ears/offear/O = new(I) - O.forceMove(src) - l_ear = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - update_inv_ears(redraw_mob) - if(slot_glasses) - glasses = I - var/obj/item/clothing/glasses/G = I - if(G.tint) - update_tint() - if(G.prescription) - update_nearsighted_effects() - if(G.vision_flags || G.see_in_dark || G.invis_override || G.invis_view || !isnull(G.lighting_alpha)) - update_sight() - update_inv_glasses(redraw_mob) - update_client_colour() - if(slot_gloves) - gloves = I - update_inv_gloves(redraw_mob) - if(slot_head) - head = I - if((head.flags & BLOCKHAIR) || (head.flags & BLOCKHEADHAIR)) - update_hair(redraw_mob) //rebuild hair - update_fhair(redraw_mob) - update_head_accessory(redraw_mob) - // paper + bandanas - if(istype(I, /obj/item/clothing/head)) - var/obj/item/clothing/head/hat = I - if(hat.vision_flags || hat.see_in_dark || !isnull(hat.lighting_alpha)) - update_sight() - head_update(I) - update_inv_head(redraw_mob) - if(slot_shoes) - shoes = I - update_inv_shoes(redraw_mob) - if(slot_wear_suit) - wear_suit = I - update_inv_wear_suit(redraw_mob) - if(slot_w_uniform) - w_uniform = I - update_inv_w_uniform(redraw_mob) - if(slot_l_store) - l_store = I - update_inv_pockets(redraw_mob) - if(slot_r_store) - r_store = I - update_inv_pockets(redraw_mob) - if(slot_s_store) - s_store = I - update_inv_s_store(redraw_mob) - if(slot_in_backpack) - if(get_active_hand() == I) - unEquip(I) - I.forceMove(back) - if(slot_tie) - var/obj/item/clothing/under/uniform = src.w_uniform - uniform.attackby(I, src) - else - to_chat(src, "You are trying to equip this item to an unsupported inventory slot. Report this to a coder!") - -/mob/living/carbon/human/put_in_hands(obj/item/I) - if(!I) - return FALSE - if(put_in_active_hand(I)) - return TRUE - else if(put_in_inactive_hand(I)) - return TRUE - else - . = ..() - -// Return the item currently in the slot ID -/mob/living/carbon/human/get_item_by_slot(slot_id) - switch(slot_id) - if(slot_back) - return back - if(slot_wear_mask) - return wear_mask - if(slot_handcuffed) - return handcuffed - if(slot_legcuffed) - return legcuffed - if(slot_l_hand) - return l_hand - if(slot_r_hand) - return r_hand - if(slot_belt) - return belt - if(slot_wear_id) - return wear_id - if(slot_wear_pda) - return wear_pda - if(slot_l_ear) - return l_ear - if(slot_r_ear) - return r_ear - if(slot_glasses) - return glasses - if(slot_gloves) - return gloves - if(slot_head) - return head - if(slot_shoes) - return shoes - if(slot_wear_suit) - return wear_suit - if(slot_w_uniform) - return w_uniform - if(slot_l_store) - return l_store - if(slot_r_store) - return r_store - if(slot_s_store) - return s_store - return null - -/mob/living/carbon/human/get_all_slots() - . = get_head_slots() | get_body_slots() - -/mob/living/carbon/human/proc/get_body_slots() - return list( - l_hand, - r_hand, - back, - s_store, - handcuffed, - legcuffed, - wear_suit, - gloves, - shoes, - belt, - wear_id, - wear_pda, - l_store, - r_store, - w_uniform - ) - -/mob/living/carbon/human/proc/get_head_slots() - return list( - head, - wear_mask, - glasses, - r_ear, - l_ear, - ) - -// humans have their pickpocket gloves, so they get no message when stealing things -/mob/living/carbon/human/stripPanelUnequip(obj/item/what, mob/who, where) - var/is_silent = 0 - var/obj/item/clothing/gloves/G = gloves - if(istype(G)) - is_silent = G.pickpocket - - ..(what, who, where, silent = is_silent) - -// humans have their pickpocket gloves, so they get no message when stealing things -/mob/living/carbon/human/stripPanelEquip(obj/item/what, mob/who, where) - var/is_silent = 0 - var/obj/item/clothing/gloves/G = gloves - if(istype(G)) - is_silent = G.pickpocket - - ..(what, who, where, silent = is_silent) - -/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE) - return dna.species.can_equip(I, slot, disable_warning, src) - -/mob/living/carbon/human/proc/equipOutfit(outfit, visualsOnly = FALSE) - var/datum/outfit/O = null - - if(ispath(outfit)) - O = new outfit - else - O = outfit - if(!istype(O)) - return 0 - if(!O) - return 0 - - return O.equip(src, visualsOnly) - -//delete all equipment without dropping anything -/mob/living/carbon/human/proc/delete_equipment() - for(var/slot in get_all_slots())//order matters, dependant slots go first - qdel(slot) +/mob/living/carbon/human/proc/equip_in_one_of_slots(obj/item/W, list/slots, del_on_fail = 1) + for(var/slot in slots) + if(equip_to_slot_if_possible(W, slots[slot], del_on_fail = 0)) + return slot + if(del_on_fail) + qdel(W) + return null + +/mob/living/carbon/human/proc/is_in_hands(var/typepath) + if(istype(l_hand,typepath)) + return l_hand + if(istype(r_hand,typepath)) + return r_hand + return 0 + + +/mob/living/carbon/human/proc/has_organ(name) + var/obj/item/organ/external/O = bodyparts_by_name[name] + return O + +/mob/living/carbon/human/proc/has_organ_for_slot(slot) + switch(slot) + if(slot_back) + return has_organ("chest") + if(slot_wear_mask) + return has_organ("head") + if(slot_handcuffed) + return has_organ("l_hand") && has_organ("r_hand") + if(slot_legcuffed) + return has_organ("l_leg") && has_organ("r_leg") + if(slot_l_hand) + return has_organ("l_hand") + if(slot_r_hand) + return has_organ("r_hand") + if(slot_belt) + return has_organ("chest") + if(slot_wear_id) + // the only relevant check for this is the uniform check + return TRUE + if(slot_wear_pda) + return TRUE + if(slot_l_ear) + return has_organ("head") + if(slot_r_ear) + return has_organ("head") + if(slot_glasses) + return has_organ("head") + if(slot_gloves) + return has_organ("l_hand") && has_organ("r_hand") + if(slot_head) + return has_organ("head") + if(slot_shoes) + return has_organ("r_foot") && has_organ("l_foot") + if(slot_wear_suit) + return has_organ("chest") + if(slot_w_uniform) + return has_organ("chest") + if(slot_l_store) + return has_organ("chest") + if(slot_r_store) + return has_organ("chest") + if(slot_s_store) + return has_organ("chest") + if(slot_in_backpack) + return TRUE + if(slot_tie) + return TRUE + +// The actual dropping happens at the mob level - checks to prevent drops should +// come here +/mob/living/carbon/human/canUnEquip(obj/item/I, force) + . = ..() + var/obj/item/organ/O = I + if(istype(O) && O.owner == src) + . = 0 // keep a good grip on your heart + +/mob/living/carbon/human/unEquip(obj/item/I, force) + . = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should. + if(!. || !I) + return + + if(I == wear_suit) + if(s_store) + unEquip(s_store, 1) //It makes no sense for your suit storage to stay on you if you drop your suit. + wear_suit = null + if(I.flags_inv & HIDEJUMPSUIT) + update_inv_w_uniform() + update_inv_wear_suit() + else if(I == w_uniform) + if(r_store) + unEquip(r_store, 1) //Again, makes sense for pockets to drop. + if(l_store) + unEquip(l_store, 1) + if(wear_id) + unEquip(wear_id) + if(belt) + unEquip(belt) + w_uniform = null + update_inv_w_uniform() + else if(I == gloves) + gloves = null + update_inv_gloves() + else if(I == glasses) + glasses = null + var/obj/item/clothing/glasses/G = I + if(G.tint) + update_tint() + if(G.prescription) + update_nearsighted_effects() + if(G.vision_flags || G.see_in_dark || G.invis_override || G.invis_view || !isnull(G.lighting_alpha)) + update_sight() + update_inv_glasses() + update_client_colour() + else if(I == head) + head = null + if(I.flags & BLOCKHAIR || I.flags & BLOCKHEADHAIR) + update_hair() //rebuild hair + update_fhair() + update_head_accessory() + // Bandanas and paper hats go on the head but are not head clothing + if(istype(I,/obj/item/clothing/head)) + var/obj/item/clothing/head/hat = I + if(hat.vision_flags || hat.see_in_dark || !isnull(hat.lighting_alpha)) + update_sight() + head_update(I) + update_inv_head() + else if(I == r_ear) + r_ear = null + update_inv_ears() + else if(I == l_ear) + l_ear = null + update_inv_ears() + else if(I == shoes) + shoes = null + update_inv_shoes() + else if(I == belt) + belt = null + update_inv_belt() + else if(I == wear_mask) + wear_mask = null + if(I.flags & BLOCKHAIR || I.flags & BLOCKHEADHAIR) + update_hair() //rebuild hair + update_fhair() + update_head_accessory() + if(internal && !get_organ_slot("breathing_tube")) + internal = null + update_action_buttons_icon() + wear_mask_update(I, toggle_off = FALSE) + sec_hud_set_ID() + update_inv_wear_mask() + else if(I == wear_id) + wear_id = null + sec_hud_set_ID() + update_inv_wear_id() + else if(I == wear_pda) + wear_pda = null + update_inv_wear_pda() + else if(I == r_store) + r_store = null + update_inv_pockets() + else if(I == l_store) + l_store = null + update_inv_pockets() + else if(I == s_store) + s_store = null + update_inv_s_store() + else if(I == back) + back = null + update_inv_back() + else if(I == r_hand) + r_hand = null + update_inv_r_hand() + else if(I == l_hand) + l_hand = null + update_inv_l_hand() + + + + +//This is an UNSAFE proc. Use mob_can_equip() before calling this one! Or rather use equip_to_slot_if_possible() or advanced_equip_to_slot_if_possible() +//set redraw_mob to 0 if you don't wish the hud to be updated - if you're doing it manually in your own proc. +/mob/living/carbon/human/equip_to_slot(obj/item/I, slot, redraw_mob = 1) + if(!slot) + return + if(!istype(I)) + return + if(!has_organ_for_slot(slot)) + return + + if(I == src.l_hand) + src.l_hand = null + update_inv_l_hand() //So items actually disappear from hands. + else if(I == src.r_hand) + src.r_hand = null + update_inv_r_hand() + + I.screen_loc = null + I.forceMove(src) + I.equipped(src, slot) + I.layer = ABOVE_HUD_LAYER + I.plane = ABOVE_HUD_PLANE + + switch(slot) + if(slot_back) + back = I + update_inv_back(redraw_mob) + if(slot_wear_mask) + wear_mask = I + if((wear_mask.flags & BLOCKHAIR) || (wear_mask.flags & BLOCKHEADHAIR)) + update_hair(redraw_mob) //rebuild hair + update_fhair(redraw_mob) + update_head_accessory(redraw_mob) + if(hud_list.len) + sec_hud_set_ID() + wear_mask_update(I, toggle_off = TRUE) + update_inv_wear_mask(redraw_mob) + if(slot_handcuffed) + handcuffed = I + update_inv_handcuffed(redraw_mob) + if(slot_legcuffed) + legcuffed = I + update_inv_legcuffed(redraw_mob) + if(slot_l_hand) + l_hand = I + update_inv_l_hand(redraw_mob) + if(slot_r_hand) + r_hand = I + update_inv_r_hand(redraw_mob) + if(slot_belt) + belt = I + update_inv_belt(redraw_mob) + if(slot_wear_id) + wear_id = I + if(hud_list.len) + sec_hud_set_ID() + update_inv_wear_id(redraw_mob) + if(slot_wear_pda) + wear_pda = I + update_inv_wear_pda(redraw_mob) + if(slot_l_ear) + l_ear = I + if(l_ear.slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/offear/O = new(I) + O.forceMove(src) + r_ear = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + update_inv_ears(redraw_mob) + if(slot_r_ear) + r_ear = I + if(r_ear.slot_flags & SLOT_TWOEARS) + var/obj/item/clothing/ears/offear/O = new(I) + O.forceMove(src) + l_ear = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + update_inv_ears(redraw_mob) + if(slot_glasses) + glasses = I + var/obj/item/clothing/glasses/G = I + if(G.tint) + update_tint() + if(G.prescription) + update_nearsighted_effects() + if(G.vision_flags || G.see_in_dark || G.invis_override || G.invis_view || !isnull(G.lighting_alpha)) + update_sight() + update_inv_glasses(redraw_mob) + update_client_colour() + if(slot_gloves) + gloves = I + update_inv_gloves(redraw_mob) + if(slot_head) + head = I + if((head.flags & BLOCKHAIR) || (head.flags & BLOCKHEADHAIR)) + update_hair(redraw_mob) //rebuild hair + update_fhair(redraw_mob) + update_head_accessory(redraw_mob) + // paper + bandanas + if(istype(I, /obj/item/clothing/head)) + var/obj/item/clothing/head/hat = I + if(hat.vision_flags || hat.see_in_dark || !isnull(hat.lighting_alpha)) + update_sight() + head_update(I) + update_inv_head(redraw_mob) + if(slot_shoes) + shoes = I + update_inv_shoes(redraw_mob) + if(slot_wear_suit) + wear_suit = I + update_inv_wear_suit(redraw_mob) + if(slot_w_uniform) + w_uniform = I + update_inv_w_uniform(redraw_mob) + if(slot_l_store) + l_store = I + update_inv_pockets(redraw_mob) + if(slot_r_store) + r_store = I + update_inv_pockets(redraw_mob) + if(slot_s_store) + s_store = I + update_inv_s_store(redraw_mob) + if(slot_in_backpack) + if(get_active_hand() == I) + unEquip(I) + I.forceMove(back) + if(slot_tie) + var/obj/item/clothing/under/uniform = src.w_uniform + uniform.attackby(I, src) + else + to_chat(src, "You are trying to equip this item to an unsupported inventory slot. Report this to a coder!") + +/mob/living/carbon/human/put_in_hands(obj/item/I) + if(!I) + return FALSE + if(put_in_active_hand(I)) + return TRUE + else if(put_in_inactive_hand(I)) + return TRUE + else + . = ..() + +// Return the item currently in the slot ID +/mob/living/carbon/human/get_item_by_slot(slot_id) + switch(slot_id) + if(slot_back) + return back + if(slot_wear_mask) + return wear_mask + if(slot_handcuffed) + return handcuffed + if(slot_legcuffed) + return legcuffed + if(slot_l_hand) + return l_hand + if(slot_r_hand) + return r_hand + if(slot_belt) + return belt + if(slot_wear_id) + return wear_id + if(slot_wear_pda) + return wear_pda + if(slot_l_ear) + return l_ear + if(slot_r_ear) + return r_ear + if(slot_glasses) + return glasses + if(slot_gloves) + return gloves + if(slot_head) + return head + if(slot_shoes) + return shoes + if(slot_wear_suit) + return wear_suit + if(slot_w_uniform) + return w_uniform + if(slot_l_store) + return l_store + if(slot_r_store) + return r_store + if(slot_s_store) + return s_store + return null + +/mob/living/carbon/human/get_all_slots() + . = get_head_slots() | get_body_slots() + +/mob/living/carbon/human/proc/get_body_slots() + return list( + l_hand, + r_hand, + back, + s_store, + handcuffed, + legcuffed, + wear_suit, + gloves, + shoes, + belt, + wear_id, + wear_pda, + l_store, + r_store, + w_uniform + ) + +/mob/living/carbon/human/proc/get_head_slots() + return list( + head, + wear_mask, + glasses, + r_ear, + l_ear, + ) + +// humans have their pickpocket gloves, so they get no message when stealing things +/mob/living/carbon/human/stripPanelUnequip(obj/item/what, mob/who, where) + var/is_silent = 0 + var/obj/item/clothing/gloves/G = gloves + if(istype(G)) + is_silent = G.pickpocket + + ..(what, who, where, silent = is_silent) + +// humans have their pickpocket gloves, so they get no message when stealing things +/mob/living/carbon/human/stripPanelEquip(obj/item/what, mob/who, where) + var/is_silent = 0 + var/obj/item/clothing/gloves/G = gloves + if(istype(G)) + is_silent = G.pickpocket + + ..(what, who, where, silent = is_silent) + +/mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = FALSE) + return dna.species.can_equip(I, slot, disable_warning, src) + +/mob/living/carbon/human/proc/equipOutfit(outfit, visualsOnly = FALSE) + var/datum/outfit/O = null + + if(ispath(outfit)) + O = new outfit + else + O = outfit + if(!istype(O)) + return 0 + if(!O) + return 0 + + return O.equip(src, visualsOnly) + +//delete all equipment without dropping anything +/mob/living/carbon/human/proc/delete_equipment() + for(var/slot in get_all_slots())//order matters, dependant slots go first + qdel(slot) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 93e7ff54ec64..d1b0935a6b3d 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1,1122 +1,1126 @@ -/mob/living/carbon/human/Life(seconds, times_fired) - life_tick++ - - voice = GetVoice() - - if(..()) - - if(check_mutations) - domutcheck(src,null) - update_mutations() - check_mutations=0 - - handle_pain() - handle_heartbeat() - handle_drunk() - dna.species.handle_life(src) - if(!client) - dna.species.handle_npc(src) - - if(stat != DEAD) - //Stuff jammed in your limbs hurts - handle_embedded_objects() - - if(stat == DEAD) - handle_decay() - - if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle - return //We go ahead and process them 5 times for HUD images and other stuff though. - - //Update our name based on whether our face is obscured/disfigured - name = get_visible_name() - pulse = handle_pulse(times_fired) - - if(mind && mind.vampire) - mind.vampire.handle_vampire() - if(life_tick == 1) - regenerate_icons() // Make sure the inventory updates - - handle_ghosted() - handle_ssd() - -/mob/living/carbon/human/proc/handle_ghosted() - if(player_ghosted > 0 && stat == CONSCIOUS && job && !restrained()) - if(key) - player_ghosted = 0 - else - player_ghosted++ - if(player_ghosted % 150 == 0) - force_cryo_human(src) - -/mob/living/carbon/human/proc/handle_ssd() - if(player_logged > 0 && stat != DEAD && job) - player_logged++ - if(istype(loc, /obj/machinery/cryopod)) - return - if(config.auto_cryo_ssd_mins && (player_logged >= (config.auto_cryo_ssd_mins * 30)) && player_logged % 30 == 0) - var/turf/T = get_turf(src) - if(!is_station_level(T.z)) - return - var/area/A = get_area(src) - if(cryo_ssd(src)) - var/obj/effect/portal/P = new /obj/effect/portal(T, null, null, 40) - P.name = "NT SSD Teleportation Portal" - if(A.fast_despawn) - force_cryo_human(src) - -/mob/living/carbon/human/calculate_affecting_pressure(var/pressure) - ..() - var/pressure_difference = abs( pressure - ONE_ATMOSPHERE ) - - var/pressure_adjustment_coefficient = 1 //Determins how much the clothing you are wearing protects you in percent. - if(wear_suit && (wear_suit.flags & STOPSPRESSUREDMAGE) && head && (head.flags & STOPSPRESSUREDMAGE)) // Complete set of pressure-proof suit worn, assume fully sealed. - pressure_adjustment_coefficient = 0 - pressure_adjustment_coefficient = max(pressure_adjustment_coefficient,0) //So it isn't less than 0 - pressure_difference = pressure_difference * pressure_adjustment_coefficient - if(pressure > ONE_ATMOSPHERE) - return ONE_ATMOSPHERE + pressure_difference - else - return ONE_ATMOSPHERE - pressure_difference - - -/mob/living/carbon/human/handle_disabilities() - if(disabilities & EPILEPSY) - if((prob(1) && paralysis < 1)) - visible_message("[src] starts having a seizure!","You have a seizure!") - Paralyse(10) - Jitter(1000) - - // If we have the gene for being crazy, have random events. - if(dna.GetSEState(HALLUCINATIONBLOCK)) - if(prob(1)) - Hallucinate(20) - - if(disabilities & COUGHING) - if((prob(5) && paralysis <= 1)) - drop_item() - emote("cough") - if(disabilities & TOURETTES) - if((prob(10) && paralysis <= 1)) - Stun(10) - switch(rand(1, 3)) - if(1) - emote("twitch") - if(2 to 3) - var/tourettes = pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS") - say("[prob(50) ? ";" : ""][tourettes]") - var/x_offset = pixel_x + rand(-2,2) //Should probably be moved into the twitch emote at some point. - var/y_offset = pixel_y + rand(-1,1) - animate(src, pixel_x = pixel_x + x_offset, pixel_y = pixel_y + y_offset, time = 1) - animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y), time = 1) - - if(disabilities & NERVOUS) - if(prob(10)) - Stuttering(10) - - if(getBrainLoss() >= 60 && stat != DEAD) - if(prob(3)) - var/list/s1 = list("IM A [pick("PONY","LIZARD","taJaran","kitty","Vulpakin","drASK","BIRDIE","voxxie","race car","combat meCH","SPESSSHIP")] [pick("NEEEEEEIIIIIIIIIGH","sKREEEEEE","MEOW","NYA~","rawr","Barkbark","Hissssss","vROOOOOM","pewpew","choo Choo")]!", - "without oxigen blob don't evoluate?", - "CAPTAINS A COMDOM", - "[pick("", "that damn traitor")] [pick("joerge", "george", "gorge", "gdoruge")] [pick("mellens", "melons", "mwrlins")] is grifing me HAL;P!!!", - "can u give me [pick("telikesis","halk","eppilapse")]?", - "THe saiyans screwed", - "Bi is THE BEST OF BOTH WORLDS", - "I WANNA PET TEH monkeyS", - "stop grifing me!!!!", - "SOTP IT!", - "HALPZ SITCULITY", - "VOXES caN't LOVE", - "my dad own this station", - "the CHef put [pick("PROTEIN", "toiret waTer", "RiPPleing TendIes", "Einzymes","HORRY WALTER","nuTriments","ReActive MutAngen","TeSLium","sKrektonium")] in my [pick("wiSh soup","Bullito","rAingurber","sOilent GREEn","KoI Susishes","yaya")]!", - "the monkey have TASER ARMS!", - "qM blew my points on [pick("cOMbat Shtogun","inSuLated gloves","LOTS MASSHEEN!")]", - "EI'NATH!", - "WAKE UP SHEEPLES!", - "et wus my [pick("wittle brother!!","fiancee","friend staying over","entiRe orphanage","love interest","wife","husband","liTTle kids","sentient cAT","accidentally")]!") - - var/list/s2 = list("FUS RO DAH", - "fuckin tangerines!!!", - "stat me", - ">my face", - "roll it easy!", - "waaaaaagh!!!", - "red wonz go fasta", - "FOR TEH EMPRAH", - "HAZ A SECURE DAY!!!!", - "dem dwarfs man, dem dwarfs", - "SPESS MAHREENS", - "hwee did eet fhor khayosss", - "lifelike texture", - "luv can bloooom", - "PACKETS!!!", - "[pick("WHERE MY","aYE need","giv me my","bath me inn.")] [pick("dermaline","alKkyZine","dylOvene","inAprovaline","biCaridine","Hyperzine","kELotane","lePorazine","bAcch Salts","tricord","clOnexazone","hydroChloric Acid","chlorine Hydrate","paRoxetine")]!", - "mALPRACTICEBAY", - "I HavE A pe H dee iN ENTerpriSE resOUrCE pLaNNIN", - "h-h-HalP MaINT", - "dey come, dey COME! DEY COME!!!", - "THE END IS NIGH!", - "I FOT AND DIED FOR MUH [pick("RITES","FREEDOM","payCHECK","cARGO points","teCH Level","doG","mAPLe syrup","fluffy fWiends","gateway Loot")]", - "KILL DEM [pick("mainTnacE cHickinNS","kiRA CulwnNES","FLOOR CLUWNEs","MIME ASSASSIN","BOMBING TAJARAN","cC offiser","morPhlings","slinglings")]!") - switch(pick(1,2,3)) - if(1) - say(pick(s1)) - if(2) - say(pick(s2)) - if(3) - emote("drool") - -/mob/living/carbon/human/handle_mutations_and_radiation() - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - gene.OnMobLife(src) - if(!ignore_gene_stability && gene_stability < GENETIC_DAMAGE_STAGE_1) - var/instability = DEFAULT_GENE_STABILITY - gene_stability - if(prob(instability * 0.1)) - adjustFireLoss(min(5, instability * 0.67)) - to_chat(src, "You feel like your skin is burning and bubbling off!") - if(gene_stability < GENETIC_DAMAGE_STAGE_2) - if(prob(instability * 0.83)) - adjustCloneLoss(min(4, instability * 0.05)) - to_chat(src, "You feel as if your body is warping.") - if(prob(instability * 0.1)) - adjustToxLoss(min(5, instability * 0.67)) - to_chat(src, "You feel weak and nauseous.") - if(gene_stability < GENETIC_DAMAGE_STAGE_3 && prob(1)) - to_chat(src, "You feel incredibly sick... Something isn't right!") - spawn(300) - if(gene_stability < GENETIC_DAMAGE_STAGE_3) - gib() - - if(!(RADIMMUNE in dna.species.species_traits)) - if(radiation) - radiation = Clamp(radiation, 0, 200) - - var/autopsy_damage = 0 - switch(radiation) - if(1 to 49) - radiation = max(radiation-1, 0) - if(prob(25)) - adjustToxLoss(1) - adjustFireLoss(1) - autopsy_damage = 2 - - if(50 to 74) - radiation = max(radiation-2, 0) - adjustToxLoss(1) - adjustFireLoss(1) - autopsy_damage = 2 - if(prob(5)) - radiation = max(radiation-5, 0) - Weaken(3) - to_chat(src, "You feel weak.") - emote("collapse") - - if(75 to 100) - radiation = max(radiation-2, 0) - adjustToxLoss(2) - adjustFireLoss(2) - autopsy_damage = 4 - if(prob(2)) - to_chat(src, "You mutate!") - randmutb(src) - domutcheck(src, null) - - if(101 to 150) - radiation = max(radiation-3, 0) - adjustToxLoss(2) - adjustFireLoss(3) - autopsy_damage = 5 - if(prob(4)) - to_chat(src, "You mutate!") - randmutb(src) - domutcheck(src, null) - - if(151 to INFINITY) - radiation = max(radiation-3, 0) - adjustToxLoss(2) - adjustFireLoss(3) - autopsy_damage = 5 - if(prob(6)) - to_chat(src, "You mutate!") - randmutb(src) - domutcheck(src, null) - - if(autopsy_damage) - var/obj/item/organ/external/chest/chest = get_organ("chest") - if(chest) - chest.add_autopsy_data("Radiation Poisoning", autopsy_damage) - -/mob/living/carbon/human/breathe() - if(!dna.species.breathe(src)) - ..() - -/mob/living/carbon/human/check_breath(datum/gas_mixture/breath) - - var/obj/item/organ/internal/L = get_organ_slot("lungs") - - if(!L || L && (L.status & ORGAN_DEAD)) - if(health >= HEALTH_THRESHOLD_CRIT) - adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1) - else if(!(NOCRITDAMAGE in dna.species.species_traits)) - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - - if(dna.species) - var/datum/species/S = dna.species - - if(S.breathid == "o2") - throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) - else if(S.breathid == "tox") - throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) - else if(S.breathid == "co2") - throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) - else if(S.breathid == "n2") - throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro) - - return FALSE - else - if(istype(L, /obj/item/organ/internal/lungs)) - var/obj/item/organ/internal/lungs/lun = L - lun.check_breath(breath, src) - -// USED IN DEATHWHISPERS -/mob/living/carbon/human/proc/isInCrit() - // Health is in deep shit and we're not already dead - return health <= HEALTH_THRESHOLD_CRIT && stat != DEAD - - -/mob/living/carbon/human/get_breath_from_internal(volume_needed) //making this call the parent would be far too complicated - if(internal) - var/null_internals = 0 //internals are invalid, therefore turn them off - var/skip_contents_check = 0 //rigsuit snowflake, oxygen tanks aren't stored inside the mob, so the 'contents.Find' check has to be skipped. - - if(!get_organ_slot("breathing_tube")) - if(!(wear_mask && wear_mask.flags & AIRTIGHT)) //if NOT (wear_mask AND wear_mask.flags CONTAIN AIRTIGHT) - if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT) - null_internals = 1 //not wearing a mask or suitable helmet - - if(istype(back, /obj/item/rig)) //wearing a rigsuit - var/obj/item/rig/rig = back //needs to be typecasted because this doesn't use get_rig() for some reason - if(rig.offline && (rig.air_supply && internal == rig.air_supply)) //if rig IS offline AND (rig HAS air_supply AND internal IS air_supply) - null_internals = 1 //offline suits do not breath - - else if(rig.air_supply && internal == rig.air_supply) //if rig HAS air_supply AND internal IS rig air_supply - skip_contents_check = 1 //skip contents.Find() check, the oxygen is valid even being outside of the mob - - if(!contents.Find(internal) && (!skip_contents_check)) //if internal NOT IN contents AND skip_contents_check IS false - null_internals = 1 //not a rigsuit and your oxygen is gone - - if(null_internals) //something wants internals gone - internal = null //so do it - update_action_buttons_icon() - - if(internal) //check for hud updates every time this is called - return internal.remove_air_volume(volume_needed) //returns the valid air - - return null - -/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) - if(!environment) - return - - var/loc_temp = get_temperature(environment) -// to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]") - - //Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit. - if(stat != DEAD) - stabilize_temperature_from_calories() - - //After then, it reacts to the surrounding atmosphere based on your thermal protection - if(!on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases - if(loc_temp < bodytemperature) - //Place is colder than we are - var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - bodytemperature += min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX) - else - //Place is hotter than we are - var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - bodytemperature += min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR), BODYTEMP_HEATING_MAX) - - // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. - if(bodytemperature > dna.species.heat_level_1) - //Body temperature is too hot. - if(status_flags & GODMODE) return 1 //godmode - var/mult = dna.species.heatmod - - if(bodytemperature >= dna.species.heat_level_1 && bodytemperature <= dna.species.heat_level_2) - throw_alert("temp", /obj/screen/alert/hot, 1) - take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_1, updating_health = TRUE, used_weapon = "High Body Temperature") - if(bodytemperature > dna.species.heat_level_2 && bodytemperature <= dna.species.heat_level_3) - throw_alert("temp", /obj/screen/alert/hot, 2) - take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_2, updating_health = TRUE, used_weapon = "High Body Temperature") - if(bodytemperature > dna.species.heat_level_3 && bodytemperature < INFINITY) - throw_alert("temp", /obj/screen/alert/hot, 3) - if(on_fire) - take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_3, updating_health = TRUE, used_weapon = "Fire") - else - take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_2, updating_health = TRUE, used_weapon = "High Body Temperature") - - else if(bodytemperature < dna.species.cold_level_1) - if(status_flags & GODMODE) - return 1 - if(stat == DEAD) - return 1 - - if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) - var/mult = dna.species.coldmod - if(bodytemperature >= dna.species.cold_level_2 && bodytemperature <= dna.species.cold_level_1) - throw_alert("temp", /obj/screen/alert/cold, 1) - take_overall_damage(burn=mult*COLD_DAMAGE_LEVEL_1, updating_health = TRUE, used_weapon = "Low Body Temperature") - if(bodytemperature >= dna.species.cold_level_3 && bodytemperature < dna.species.cold_level_2) - throw_alert("temp", /obj/screen/alert/cold, 2) - take_overall_damage(burn=mult*COLD_DAMAGE_LEVEL_2, updating_health = TRUE, used_weapon = "Low Body Temperature") - if(bodytemperature > -INFINITY && bodytemperature < dna.species.cold_level_3) - throw_alert("temp", /obj/screen/alert/cold, 3) - take_overall_damage(burn=mult*COLD_DAMAGE_LEVEL_3, updating_health = TRUE, used_weapon = "Low Body Temperature") - else - clear_alert("temp") - else - clear_alert("temp") - - // Account for massive pressure differences. Done by Polymorph - // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! - - var/pressure = environment.return_pressure() - var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob. - if(status_flags & GODMODE) return 1 //godmode - - if(adjusted_pressure >= dna.species.hazard_high_pressure) - if(!(HEATRES in mutations)) - var/pressure_damage = min( ( (adjusted_pressure / dna.species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) - take_overall_damage(brute=pressure_damage, updating_health = TRUE, used_weapon = "High Pressure") - throw_alert("pressure", /obj/screen/alert/highpressure, 2) - else - clear_alert("pressure") - else if(adjusted_pressure >= dna.species.warning_high_pressure) - throw_alert("pressure", /obj/screen/alert/highpressure, 1) - else if(adjusted_pressure >= dna.species.warning_low_pressure) - clear_alert("pressure") - else if(adjusted_pressure >= dna.species.hazard_low_pressure) - throw_alert("pressure", /obj/screen/alert/lowpressure, 1) - else - if(COLDRES in mutations) - clear_alert("pressure") - else - take_overall_damage(brute=LOW_PRESSURE_DAMAGE, updating_health = TRUE, used_weapon = "Low Pressure") - throw_alert("pressure", /obj/screen/alert/lowpressure, 2) - - -///FIRE CODE -/mob/living/carbon/human/handle_fire() - if(..()) - return - if(HEATRES in mutations) - return - if(on_fire) - var/thermal_protection = get_thermal_protection() - - if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) - return - if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT) - bodytemperature += 11 - else - bodytemperature += (BODYTEMP_HEATING_MAX + (fire_stacks * 12)) - -/mob/living/carbon/human/proc/get_thermal_protection() - var/thermal_protection = 0 //Simple check to estimate how protected we are against multiple temperatures - if(wear_suit) - if(wear_suit.max_heat_protection_temperature >= FIRE_SUIT_MAX_TEMP_PROTECT) - thermal_protection += (wear_suit.max_heat_protection_temperature*0.7) - if(head) - if(head.max_heat_protection_temperature >= FIRE_HELM_MAX_TEMP_PROTECT) - thermal_protection += (head.max_heat_protection_temperature*THERMAL_PROTECTION_HEAD) - thermal_protection = round(thermal_protection) - return thermal_protection - -//END FIRE CODE - -/mob/living/carbon/human/proc/stabilize_temperature_from_calories() - var/body_temperature_difference = dna.species.body_temperature - bodytemperature - - if(bodytemperature <= dna.species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. - bodytemperature += max((body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) - if(bodytemperature >= dna.species.cold_level_1 && bodytemperature <= dna.species.heat_level_1) - bodytemperature += body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR - if(bodytemperature >= dna.species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. - //We totally need a sweat system cause it totally makes sense...~ - bodytemperature += min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers - - - //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) -/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. - var/thermal_protection_flags = 0 - //Handle normal clothing - if(head) - if(head.max_heat_protection_temperature && head.max_heat_protection_temperature >= temperature) - thermal_protection_flags |= head.heat_protection - if(wear_suit) - if(wear_suit.max_heat_protection_temperature && wear_suit.max_heat_protection_temperature >= temperature) - thermal_protection_flags |= wear_suit.heat_protection - if(w_uniform) - if(w_uniform.max_heat_protection_temperature && w_uniform.max_heat_protection_temperature >= temperature) - thermal_protection_flags |= w_uniform.heat_protection - if(shoes) - if(shoes.max_heat_protection_temperature && shoes.max_heat_protection_temperature >= temperature) - thermal_protection_flags |= shoes.heat_protection - if(gloves) - if(gloves.max_heat_protection_temperature && gloves.max_heat_protection_temperature >= temperature) - thermal_protection_flags |= gloves.heat_protection - if(wear_mask) - if(wear_mask.max_heat_protection_temperature && wear_mask.max_heat_protection_temperature >= temperature) - thermal_protection_flags |= wear_mask.heat_protection - - return thermal_protection_flags - -/mob/living/carbon/human/proc/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. - - if(HEATRES in mutations) - return 1 - - var/thermal_protection_flags = get_heat_protection_flags(temperature) - - var/thermal_protection = 0.0 - if(thermal_protection_flags) - if(thermal_protection_flags & HEAD) - thermal_protection += THERMAL_PROTECTION_HEAD - if(thermal_protection_flags & UPPER_TORSO) - thermal_protection += THERMAL_PROTECTION_UPPER_TORSO - if(thermal_protection_flags & LOWER_TORSO) - thermal_protection += THERMAL_PROTECTION_LOWER_TORSO - if(thermal_protection_flags & LEG_LEFT) - thermal_protection += THERMAL_PROTECTION_LEG_LEFT - if(thermal_protection_flags & LEG_RIGHT) - thermal_protection += THERMAL_PROTECTION_LEG_RIGHT - if(thermal_protection_flags & FOOT_LEFT) - thermal_protection += THERMAL_PROTECTION_FOOT_LEFT - if(thermal_protection_flags & FOOT_RIGHT) - thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT - if(thermal_protection_flags & ARM_LEFT) - thermal_protection += THERMAL_PROTECTION_ARM_LEFT - if(thermal_protection_flags & ARM_RIGHT) - thermal_protection += THERMAL_PROTECTION_ARM_RIGHT - if(thermal_protection_flags & HAND_LEFT) - thermal_protection += THERMAL_PROTECTION_HAND_LEFT - if(thermal_protection_flags & HAND_RIGHT) - thermal_protection += THERMAL_PROTECTION_HAND_RIGHT - - - return min(1,thermal_protection) - - //See proc/get_heat_protection_flags(temperature) for the description of this proc. -/mob/living/carbon/human/proc/get_cold_protection_flags(temperature) - var/thermal_protection_flags = 0 - //Handle normal clothing - - if(head) - if(head.min_cold_protection_temperature && head.min_cold_protection_temperature <= temperature) - thermal_protection_flags |= head.cold_protection - if(wear_suit) - if(wear_suit.min_cold_protection_temperature && wear_suit.min_cold_protection_temperature <= temperature) - thermal_protection_flags |= wear_suit.cold_protection - if(w_uniform) - if(w_uniform.min_cold_protection_temperature && w_uniform.min_cold_protection_temperature <= temperature) - thermal_protection_flags |= w_uniform.cold_protection - if(shoes) - if(shoes.min_cold_protection_temperature && shoes.min_cold_protection_temperature <= temperature) - thermal_protection_flags |= shoes.cold_protection - if(gloves) - if(gloves.min_cold_protection_temperature && gloves.min_cold_protection_temperature <= temperature) - thermal_protection_flags |= gloves.cold_protection - if(wear_mask) - if(wear_mask.min_cold_protection_temperature && wear_mask.min_cold_protection_temperature <= temperature) - thermal_protection_flags |= wear_mask.cold_protection - - return thermal_protection_flags - -/mob/living/carbon/human/proc/get_cold_protection(temperature) - - if(COLDRES in mutations) - return 1 //Fully protected from the cold. - - temperature = max(temperature, TCMB) //There is an occasional bug where the temperature is miscalculated in areas with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. - var/thermal_protection_flags = get_cold_protection_flags(temperature) - - var/thermal_protection = 0.0 - if(thermal_protection_flags) - if(thermal_protection_flags & HEAD) - thermal_protection += THERMAL_PROTECTION_HEAD - if(thermal_protection_flags & UPPER_TORSO) - thermal_protection += THERMAL_PROTECTION_UPPER_TORSO - if(thermal_protection_flags & LOWER_TORSO) - thermal_protection += THERMAL_PROTECTION_LOWER_TORSO - if(thermal_protection_flags & LEG_LEFT) - thermal_protection += THERMAL_PROTECTION_LEG_LEFT - if(thermal_protection_flags & LEG_RIGHT) - thermal_protection += THERMAL_PROTECTION_LEG_RIGHT - if(thermal_protection_flags & FOOT_LEFT) - thermal_protection += THERMAL_PROTECTION_FOOT_LEFT - if(thermal_protection_flags & FOOT_RIGHT) - thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT - if(thermal_protection_flags & ARM_LEFT) - thermal_protection += THERMAL_PROTECTION_ARM_LEFT - if(thermal_protection_flags & ARM_RIGHT) - thermal_protection += THERMAL_PROTECTION_ARM_RIGHT - if(thermal_protection_flags & HAND_LEFT) - thermal_protection += THERMAL_PROTECTION_HAND_LEFT - if(thermal_protection_flags & HAND_RIGHT) - thermal_protection += THERMAL_PROTECTION_HAND_RIGHT - - return min(1,thermal_protection) - - -/mob/living/carbon/human/proc/get_covered_bodyparts() - var/covered = 0 - - if(head) - covered |= head.body_parts_covered - if(wear_suit) - covered |= wear_suit.body_parts_covered - if(w_uniform) - covered |= w_uniform.body_parts_covered - if(shoes) - covered |= shoes.body_parts_covered - if(gloves) - covered |= gloves.body_parts_covered - if(wear_mask) - covered |= wear_mask.body_parts_covered - - return covered - -/mob/living/carbon/human/handle_chemicals_in_body() - ..() - - if(status_flags & GODMODE) - return 0 //godmode - - if(!(NO_HUNGER in dna.species.species_traits)) - //The fucking FAT mutation is the greatest shit ever. It makes everyone so hot and bothered. - if(CAN_BE_FAT in dna.species.species_traits) - if(FAT in mutations) - if(overeatduration < 100) - becomeSlim() - else - if(overeatduration > 500) - becomeFat() - - // nutrition decrease - if(nutrition > 0 && stat != DEAD) - // THEY HUNGER - var/hunger_rate = hunger_drain - if(satiety > 0) - satiety-- - if(satiety < 0) - satiety++ - if(prob(round(-satiety/40))) - Jitter(5) - hunger_rate = 3 * hunger_drain - adjust_nutrition(-hunger_rate) - - if(nutrition > NUTRITION_LEVEL_FULL) - if(overeatduration < 600) //capped so people don't take forever to unfat - overeatduration++ - - else - if(overeatduration > 1) - if(OBESITY in mutations) - overeatduration -= 1 // Those with obesity gene take twice as long to unfat - else - overeatduration -= 2 - - //metabolism change - if(nutrition > NUTRITION_LEVEL_FAT) - metabolism_efficiency = 1 - else if(nutrition > NUTRITION_LEVEL_FED && satiety > 80) - if(metabolism_efficiency != 1.25) - to_chat(src, "You feel vigorous.") - metabolism_efficiency = 1.25 - else if(nutrition < NUTRITION_LEVEL_STARVING + 50) - if(metabolism_efficiency != 0.8) - to_chat(src, "You feel sluggish.") - metabolism_efficiency = 0.8 - else - if(metabolism_efficiency == 1.25) - to_chat(src, "You no longer feel vigorous.") - metabolism_efficiency = 1 - - if(drowsyness) - AdjustDrowsy(-1) - EyeBlurry(2) - if(prob(5)) - AdjustSleeping(1) - Paralyse(5) - - AdjustConfused(-1) - // decrement dizziness counter, clamped to 0 - if(resting) - AdjustDizzy(-15) - AdjustJitter(-15) - else - AdjustDizzy(-3) - AdjustJitter(-3) - - if(NO_INTORGANS in dna.species.species_traits) - return - - handle_trace_chems() - - return //TODO: DEFERRED - -/mob/living/carbon/human/handle_drunk() - var/slur_start = 30 //12u ethanol, 30u whiskey FOR HUMANS - var/confused_start = 40 - var/brawl_start = 30 - var/blur_start = 75 - var/vomit_start = 60 - var/pass_out = 90 - var/spark_start = 50 //40u synthanol - var/collapse_start = 75 - var/braindamage_start = 120 - var/alcohol_strength = drunk - var/sober_str=!(SOBER in mutations)?1:2 - - if(drunk) - alcohol_strength/=sober_str - - var/obj/item/organ/internal/liver/L - if(!isSynthetic()) - L = get_int_organ(/obj/item/organ/internal/liver) - if(L) - alcohol_strength *= L.alcohol_intensity - else - alcohol_strength *= 5 - - if(alcohol_strength >= slur_start) //slurring - Slur(drunk) - if(alcohol_strength >= brawl_start) //the drunken martial art - if(!istype(martial_art, /datum/martial_art/drunk_brawling)) - var/datum/martial_art/drunk_brawling/F = new - F.teach(src,1) - if(alcohol_strength < brawl_start) //removing the art - if(istype(martial_art, /datum/martial_art/drunk_brawling)) - martial_art.remove(src) - if(alcohol_strength >= confused_start && prob(33)) //confused walking - if(!confused) Confused(1) - AdjustConfused(3/sober_str) - if(alcohol_strength >= blur_start) //blurry eyes - EyeBlurry(10/sober_str) - if(!isSynthetic()) //stuff only for non-synthetics - if(alcohol_strength >= vomit_start) //vomiting - if(prob(8)) - fakevomit() - if(alcohol_strength >= pass_out) - Paralyse(5/sober_str) - Drowsy(30/sober_str) - if(L) - L.receive_damage(0.1, 1) - adjustToxLoss(0.1) - else //stuff only for synthetics - if(alcohol_strength >= spark_start && prob(25)) - do_sparks(3, 1, src) - if(alcohol_strength >= collapse_start && prob(10)) - emote("collapse") - do_sparks(3, 1, src) - if(alcohol_strength >= braindamage_start && prob(10)) - adjustBrainLoss(1) - - if(!has_booze()) - AdjustDrunk(-0.5) - return - -/mob/living/carbon/human/proc/has_booze() //checks if the human has ethanol or its subtypes inside - for(var/A in reagents.reagent_list) - var/datum/reagent/R = A - if(istype(R, /datum/reagent/consumable/ethanol)) - return 1 - return 0 - -/mob/living/carbon/human/handle_regular_status_updates() - if(status_flags & GODMODE) - return 0 - - . = ..() - - if(.) //alive - if(REGEN in mutations) - heal_overall_damage(0.1, 0.1) - - if(paralysis) - stat = UNCONSCIOUS - - else if(sleeping) - - stat = UNCONSCIOUS - - if(mind) - if(mind.vampire) - if(istype(loc, /obj/structure/closet/coffin)) - adjustBruteLoss(-1) - adjustFireLoss(-1) - adjustToxLoss(-1) - - else if(status_flags & FAKEDEATH) - stat = UNCONSCIOUS - - //Vision //god knows why this is here - var/obj/item/organ/vision - if(dna.species.vision_organ) - vision = get_int_organ(dna.species.vision_organ) - - if(!dna.species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. - SetEyeBlind(0) - SetEyeBlurry(0) - - else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. - EyeBlind(2) - EyeBlurry(2) - - else - //blindness - if(disabilities & BLIND) // Disabled-blind, doesn't get better on its own - - else if(eye_blind) // Blindness, heals slowly over time - AdjustEyeBlind(-1) - - else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster - AdjustEyeBlurry(-3) - - //blurry sight - if(vision.is_bruised()) // Vision organs impaired? Permablurry. - EyeBlurry(2) - - if(eye_blurry) // Blurry eyes heal slowly - AdjustEyeBlurry(-1) - - - if(flying) - animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING) - animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING) - - // If you're dirty, your gloves will become dirty, too. - if(gloves && germ_level > gloves.germ_level && prob(10)) - gloves.germ_level += 1 - - handle_organs() - - if(getBrainLoss() >= 120 || (health + (getOxyLoss() / 2)) <= -500) - death() - return - - if(getBrainLoss() >= 100) // braindeath - AdjustLoseBreath(10, bound_lower = 0, bound_upper = 25) - Weaken(30) - - if(!check_death_method()) - if(health <= HEALTH_THRESHOLD_DEAD) - var/deathchance = min(99, ((getBrainLoss() * -5) + (health + (getOxyLoss() / 2))) * -0.01) - if(prob(deathchance)) - death() - return - - if(health <= HEALTH_THRESHOLD_CRIT) - if(prob(5)) - emote(pick("faint", "collapse", "cry", "moan", "gasp", "shudder", "shiver")) - AdjustStuttering(5, bound_lower = 0, bound_upper = 5) - EyeBlurry(5) - if(prob(7)) - AdjustConfused(2) - if(prob(5)) - Paralyse(2) - switch(health) - if(-INFINITY to -100) - adjustOxyLoss(1) - if(prob(health * -0.1)) - if(ishuman(src)) - var/mob/living/carbon/human/H = src - H.set_heartattack(TRUE) - if(prob(health * -0.2)) - var/datum/disease/D = new /datum/disease/critical/heart_failure - ForceContractDisease(D) - Paralyse(5) - if(-99 to -80) - adjustOxyLoss(1) - if(prob(4)) - to_chat(src, "Your chest hurts...") - Paralyse(2) - var/datum/disease/D = new /datum/disease/critical/heart_failure - ForceContractDisease(D) - if(-79 to -50) - adjustOxyLoss(1) - if(prob(10)) - var/datum/disease/D = new /datum/disease/critical/shock - ForceContractDisease(D) - if(prob(health * -0.08)) - var/datum/disease/D = new /datum/disease/critical/heart_failure - ForceContractDisease(D) - if(prob(6)) - to_chat(src, "You feel [pick("horrible pain", "awful", "like shit", "absolutely awful", "like death", "like you are dying", "nothing", "warm", "sweaty", "tingly", "really, really bad", "horrible")]!") - Weaken(3) - if(prob(3)) - Paralyse(2) - if(-49 to 0) - adjustOxyLoss(1) - if(prob(3)) - var/datum/disease/D = new /datum/disease/critical/shock - ForceContractDisease(D) - if(prob(5)) - to_chat(src, "You feel [pick("terrible", "awful", "like shit", "sick", "numb", "cold", "sweaty", "tingly", "horrible")]!") - Weaken(3) - - else //dead - SetSilence(0) - - -/mob/living/carbon/human/handle_vision() - if(machine) - if(!machine.check_eye(src)) - reset_perspective(null) - else - var/isRemoteObserve = 0 - if((REMOTE_VIEW in mutations) && remoteview_target) - isRemoteObserve = 1 - - if(remoteview_target.stat != CONSCIOUS) - to_chat(src, "Your psy-connection grows too faint to maintain!") - isRemoteObserve = 0 - - if(PSY_RESIST in remoteview_target.mutations) - to_chat(src, "Your mind is shut out!") - isRemoteObserve = 0 - - // Not on the station or mining? - var/turf/temp_turf = get_turf(remoteview_target) - if(!temp_turf in config.contact_levels) - to_chat(src, "Your psy-connection grows too faint to maintain!") - isRemoteObserve = 0 - - if(remote_view) - isRemoteObserve = 1 - - if(!isRemoteObserve && client && !client.adminobs) - remoteview_target = null - reset_perspective(null) - -/mob/living/carbon/human/handle_hud_icons() - dna.species.handle_hud_icons(src) - -/mob/living/carbon/human/handle_hud_icons_health() - dna.species.handle_hud_icons_health(src) - handle_hud_icons_health_overlay() - -/mob/living/carbon/human/handle_random_events() - // Puke if toxloss is too high - if(!stat) - if(getToxLoss() >= 45 && nutrition > 20) - lastpuke ++ - if(lastpuke >= 25) // about 25 second delay I guess - vomit(20, 0, 1, 0, 1) - adjustToxLoss(-3) - lastpuke = 0 - -/mob/living/carbon/human/proc/handle_embedded_objects() - for(var/X in bodyparts) - var/obj/item/organ/external/BP = X - for(var/obj/item/I in BP.embedded_objects) - if(prob(I.embedded_pain_chance)) - BP.receive_damage(I.w_class*I.embedded_pain_multiplier) - to_chat(src, "[I] embedded in your [BP.name] hurts!") - - if(prob(I.embedded_fall_chance)) - BP.receive_damage(I.w_class*I.embedded_fall_pain_multiplier) - BP.embedded_objects -= I - I.forceMove(get_turf(src)) - visible_message("[I] falls out of [name]'s [BP.name]!","[I] falls out of your [BP.name]!") - if(!has_embedded_objects()) - clear_alert("embeddedobject") - -/mob/living/carbon/human/handle_changeling() - if(mind) - if(mind.changeling) - mind.changeling.regenerate(src) - if(hud_used) - hud_used.lingchemdisplay.invisibility = 0 - hud_used.lingchemdisplay.maptext = "
    [round(mind.changeling.chem_charges)]
    " - else - if(hud_used) - hud_used.lingchemdisplay.invisibility = 101 - - -/mob/living/carbon/human/proc/handle_pulse(times_fired) - if(times_fired % 5 == 1) - return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) - - if(NO_BLOOD in dna.species.species_traits) - return PULSE_NONE //No blood, no pulse. - - if(stat == DEAD) - return PULSE_NONE //that's it, you're dead, nothing can influence your pulse - - if(undergoing_cardiac_arrest()) - return PULSE_NONE - - var/temp = PULSE_NORM - - if(blood_volume <= BLOOD_VOLUME_BAD)//how much blood do we have - temp = PULSE_THREADY //not enough :( - - if(status_flags & FAKEDEATH) - temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds - - for(var/datum/reagent/R in reagents.reagent_list) - if(R.heart_rate_decrease) - if(temp <= PULSE_THREADY && temp >= PULSE_NORM) - temp-- - break - - for(var/datum/reagent/R in reagents.reagent_list)//handles different chems' influence on pulse - if(R.heart_rate_increase) - if(temp <= PULSE_FAST && temp >= PULSE_NONE) - temp++ - break - - for(var/datum/reagent/R in reagents.reagent_list) //To avoid using fakedeath - if(R.heart_rate_stop) - temp = PULSE_NONE - break - - return temp - -/mob/living/carbon/human/proc/handle_decay() - var/decaytime = world.time - timeofdeath - - if(NO_DECAY in dna.species.species_traits) - return - - if(reagents.has_reagent("formaldehyde")) //embalming fluid stops decay - return - - if(decaytime <= 6000) //10 minutes for decaylevel1 -- stinky - return - - if(decaytime > 6000 && decaytime <= 12000)//20 minutes for decaylevel2 -- bloated and very stinky - decaylevel = 1 - - if(decaytime > 12000 && decaytime <= 18000)//30 minutes for decaylevel3 -- rotting and gross - decaylevel = 2 - - if(decaytime > 18000 && decaytime <= 27000)//45 minutes for decaylevel4 -- skeleton - decaylevel = 3 - - if(decaytime > 27000) - decaylevel = 4 - makeSkeleton() - return //No puking over skeletons, they don't smell at all! - - if(!isturf(loc)) - return - - for(var/mob/living/carbon/human/H in range(decaylevel, src)) - if(prob(2)) - var/obj/item/clothing/mask/M = H.wear_mask - if(M && (M.flags_cover & MASKCOVERSMOUTH)) - continue - if(NO_BREATHE in H.dna.species.species_traits) - continue //no puking if you can't smell! - // Humans can lack a mind datum, y'know - if(H.mind && (H.mind.assigned_role == "Detective" || H.mind.assigned_role == "Coroner")) - continue //too cool for puke - to_chat(H, "You smell something foul...") - H.fakevomit() - -/mob/living/carbon/human/proc/handle_heartbeat() - var/client/C = src.client - if(C && C.prefs.sound & SOUND_HEARTBEAT) //disable heartbeat by pref - var/obj/item/organ/internal/heart/H = get_int_organ(/obj/item/organ/internal/heart) - - if(!H) //H.status will runtime if there is no H (obviously) - return - - if(H.is_robotic()) //Handle robotic hearts specially with a wuuuubb. This also applies to machine-people. - if(isinspace()) - //PULSE_THREADY - maximum value for pulse, currently it 5. - //High pulse value corresponds to a fast rate of heartbeat. - //Divided by 2, otherwise it is too slow. - var/rate = (PULSE_THREADY - 2)/2 //machine people (main target) have no pulse, manually subtract standard human pulse (2). Mechanic-heart humans probably have a pulse, but 'advanced neural systems' keep the heart rate steady, or something - - if(heartbeat >= rate) - heartbeat = 0 - src << sound('sound/effects/electheart.ogg',0,0,CHANNEL_HEARTBEAT,30)//Credit to GhostHack (www.ghosthack.de) for sound. - - else - heartbeat++ - return - return - - if(pulse == PULSE_NONE) - return - - if(pulse >= PULSE_2FAST || isinspace()) - //PULSE_THREADY - maximum value for pulse, currently it 5. - //High pulse value corresponds to a fast rate of heartbeat. - //Divided by 2, otherwise it is too slow. - var/rate = (PULSE_THREADY - pulse)/2 - - if(heartbeat >= rate) - heartbeat = 0 - src << sound('sound/effects/singlebeat.ogg',0,0,CHANNEL_HEARTBEAT,50) - else - heartbeat++ - -/* - Called by life(), instead of having the individual hud items update icons each tick and check for status changes - we only set those statuses and icons upon changes. Then those HUD items will simply add those pre-made images. - This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. -*/ - -/mob/living/carbon/human/proc/can_heartattack() - if((NO_BLOOD in dna.species.species_traits) && !dna.species.forced_heartattack) - return FALSE - if(NO_INTORGANS in dna.species.species_traits) - return FALSE - return TRUE - -/mob/living/carbon/human/proc/undergoing_cardiac_arrest() - if(!can_heartattack()) - return FALSE - var/obj/item/organ/internal/heart/heart = get_int_organ(/obj/item/organ/internal/heart) - if(istype(heart)) - if(heart.status & ORGAN_DEAD) - return TRUE - if(heart.beating) - return FALSE - return TRUE - -/mob/living/carbon/human/proc/set_heartattack(status) - if(!can_heartattack()) - return FALSE - - var/obj/item/organ/internal/heart/heart = get_int_organ(/obj/item/organ/internal/heart) - if(!istype(heart)) - return FALSE - - heart.beating = !status - -/mob/living/carbon/human/handle_heartattack() - if(!can_heartattack() || !undergoing_cardiac_arrest() || reagents.has_reagent("corazone")) - return - if(getOxyLoss()) - adjustBrainLoss(3) - else if(prob(10)) - adjustBrainLoss(1) - Weaken(5) - AdjustLoseBreath(20, bound_lower = 0, bound_upper = 25) - adjustOxyLoss(20) - - - -// Need this in species. -//#undef HUMAN_MAX_OXYLOSS -//#undef HUMAN_CRIT_MAX_OXYLOSS +/mob/living/carbon/human/Life(seconds, times_fired) + life_tick++ + + voice = GetVoice() + + if(..()) + + if(check_mutations) + domutcheck(src,null) + update_mutations() + check_mutations=0 + + handle_pain() + handle_heartbeat() + handle_drunk() + dna.species.handle_life(src) + if(!client) + dna.species.handle_npc(src) + + if(stat != DEAD) + //Stuff jammed in your limbs hurts + handle_embedded_objects() + + if(stat == DEAD) + handle_decay() + + if(life_tick > 5 && timeofdeath && (timeofdeath < 5 || world.time - timeofdeath > 6000)) //We are long dead, or we're junk mobs spawned like the clowns on the clown shuttle + return //We go ahead and process them 5 times for HUD images and other stuff though. + + //Update our name based on whether our face is obscured/disfigured + name = get_visible_name() + pulse = handle_pulse(times_fired) + + if(mind && mind.vampire) + mind.vampire.handle_vampire() + if(life_tick == 1) + regenerate_icons() // Make sure the inventory updates + + handle_ghosted() + handle_ssd() + +/mob/living/carbon/human/proc/handle_ghosted() + if(player_ghosted > 0 && stat == CONSCIOUS && job && !restrained()) + if(key) + player_ghosted = 0 + else + player_ghosted++ + if(player_ghosted % 150 == 0) + force_cryo_human(src) + +/mob/living/carbon/human/proc/handle_ssd() + if(player_logged > 0 && stat != DEAD && job) + player_logged++ + if(istype(loc, /obj/machinery/cryopod)) + return + if(config.auto_cryo_ssd_mins && (player_logged >= (config.auto_cryo_ssd_mins * 30)) && player_logged % 30 == 0) + var/turf/T = get_turf(src) + if(!is_station_level(T.z)) + return + var/area/A = get_area(src) + if(cryo_ssd(src)) + var/obj/effect/portal/P = new /obj/effect/portal(T, null, null, 40) + P.name = "NT SSD Teleportation Portal" + if(A.fast_despawn) + force_cryo_human(src) + +/mob/living/carbon/human/calculate_affecting_pressure(var/pressure) + ..() + var/pressure_difference = abs( pressure - ONE_ATMOSPHERE ) + + var/pressure_adjustment_coefficient = 1 //Determins how much the clothing you are wearing protects you in percent. + if(wear_suit && (wear_suit.flags & STOPSPRESSUREDMAGE) && head && (head.flags & STOPSPRESSUREDMAGE)) // Complete set of pressure-proof suit worn, assume fully sealed. + pressure_adjustment_coefficient = 0 + pressure_adjustment_coefficient = max(pressure_adjustment_coefficient,0) //So it isn't less than 0 + pressure_difference = pressure_difference * pressure_adjustment_coefficient + if(pressure > ONE_ATMOSPHERE) + return ONE_ATMOSPHERE + pressure_difference + else + return ONE_ATMOSPHERE - pressure_difference + + +/mob/living/carbon/human/handle_disabilities() + if(disabilities & EPILEPSY) + if((prob(1) && paralysis < 1)) + visible_message("[src] starts having a seizure!","You have a seizure!") + Paralyse(10) + Jitter(1000) + + // If we have the gene for being crazy, have random events. + if(dna.GetSEState(GLOB.hallucinationblock)) + if(prob(1)) + Hallucinate(20) + + if(disabilities & COUGHING) + if((prob(5) && paralysis <= 1)) + drop_item() + emote("cough") + if(disabilities & TOURETTES) + if((prob(10) && paralysis <= 1)) + Stun(10) + switch(rand(1, 3)) + if(1) + emote("twitch") + if(2 to 3) + var/tourettes = pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS") + say("[prob(50) ? ";" : ""][tourettes]") + var/x_offset = pixel_x + rand(-2,2) //Should probably be moved into the twitch emote at some point. + var/y_offset = pixel_y + rand(-1,1) + animate(src, pixel_x = pixel_x + x_offset, pixel_y = pixel_y + y_offset, time = 1) + animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y), time = 1) + + if(disabilities & NERVOUS) + if(prob(10)) + Stuttering(10) + + if(getBrainLoss() >= 60 && stat != DEAD) + if(prob(3)) + var/list/s1 = list("IM A [pick("PONY","LIZARD","taJaran","kitty","Vulpakin","drASK","BIRDIE","voxxie","race car","combat meCH","SPESSSHIP")] [pick("NEEEEEEIIIIIIIIIGH","sKREEEEEE","MEOW","NYA~","rawr","Barkbark","Hissssss","vROOOOOM","pewpew","choo Choo")]!", + "without oxigen blob don't evoluate?", + "CAPTAINS A COMDOM", + "[pick("", "that damn traitor")] [pick("joerge", "george", "gorge", "gdoruge")] [pick("mellens", "melons", "mwrlins")] is grifing me HAL;P!!!", + "can u give me [pick("telikesis","halk","eppilapse")]?", + "THe saiyans screwed", + "Bi is THE BEST OF BOTH WORLDS", + "I WANNA PET TEH monkeyS", + "stop grifing me!!!!", + "SOTP IT!", + "HALPZ SITCULITY", + "VOXES caN't LOVE", + "my dad own this station", + "the CHef put [pick("PROTEIN", "toiret waTer", "RiPPleing TendIes", "Einzymes","HORRY WALTER","nuTriments","ReActive MutAngen","TeSLium","sKrektonium")] in my [pick("wiSh soup","Bullito","rAingurber","sOilent GREEn","KoI Susishes","yaya")]!", + "the monkey have TASER ARMS!", + "qM blew my points on [pick("cOMbat Shtogun","inSuLated gloves","LOTS MASSHEEN!")]", + "EI'NATH!", + "WAKE UP SHEEPLES!", + "et wus my [pick("wittle brother!!","fiancee","friend staying over","entiRe orphanage","love interest","wife","husband","liTTle kids","sentient cAT","accidentally")]!") + + var/list/s2 = list("FUS RO DAH", + "fuckin tangerines!!!", + "stat me", + ">my face", + "roll it easy!", + "waaaaaagh!!!", + "red wonz go fasta", + "FOR TEH EMPRAH", + "HAZ A SECURE DAY!!!!", + "dem dwarfs man, dem dwarfs", + "SPESS MAHREENS", + "hwee did eet fhor khayosss", + "lifelike texture", + "luv can bloooom", + "PACKETS!!!", + "[pick("WHERE MY","aYE need","giv me my","bath me inn.")] [pick("dermaline","alKkyZine","dylOvene","inAprovaline","biCaridine","Hyperzine","kELotane","lePorazine","bAcch Salts","tricord","clOnexazone","hydroChloric Acid","chlorine Hydrate","paRoxetine")]!", + "mALPRACTICEBAY", + "I HavE A pe H dee iN ENTerpriSE resOUrCE pLaNNIN", + "h-h-HalP MaINT", + "dey come, dey COME! DEY COME!!!", + "THE END IS NIGH!", + "I FOT AND DIED FOR MUH [pick("RITES","FREEDOM","payCHECK","cARGO points","teCH Level","doG","mAPLe syrup","fluffy fWiends","gateway Loot")]", + "KILL DEM [pick("mainTnacE cHickinNS","kiRA CulwnNES","FLOOR CLUWNEs","MIME ASSASSIN","BOMBING TAJARAN","cC offiser","morPhlings","slinglings")]!") + switch(pick(1,2,3)) + if(1) + say(pick(s1)) + if(2) + say(pick(s2)) + if(3) + emote("drool") + +/mob/living/carbon/human/handle_mutations_and_radiation() + for(var/datum/dna/gene/gene in GLOB.dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + gene.OnMobLife(src) + if(!ignore_gene_stability && gene_stability < GENETIC_DAMAGE_STAGE_1) + var/instability = DEFAULT_GENE_STABILITY - gene_stability + if(prob(instability * 0.1)) + adjustFireLoss(min(5, instability * 0.67)) + to_chat(src, "You feel like your skin is burning and bubbling off!") + if(gene_stability < GENETIC_DAMAGE_STAGE_2) + if(prob(instability * 0.83)) + adjustCloneLoss(min(4, instability * 0.05)) + to_chat(src, "You feel as if your body is warping.") + if(prob(instability * 0.1)) + adjustToxLoss(min(5, instability * 0.67)) + to_chat(src, "You feel weak and nauseous.") + if(gene_stability < GENETIC_DAMAGE_STAGE_3 && prob(1)) + to_chat(src, "You feel incredibly sick... Something isn't right!") + spawn(300) + if(gene_stability < GENETIC_DAMAGE_STAGE_3) + gib() + + if(!(RADIMMUNE in dna.species.species_traits)) + if(radiation) + radiation = Clamp(radiation, 0, 200) + + var/autopsy_damage = 0 + switch(radiation) + if(1 to 49) + radiation = max(radiation-1, 0) + if(prob(25)) + adjustToxLoss(1) + adjustFireLoss(1) + autopsy_damage = 2 + + if(50 to 74) + radiation = max(radiation-2, 0) + adjustToxLoss(1) + adjustFireLoss(1) + autopsy_damage = 2 + if(prob(5)) + radiation = max(radiation-5, 0) + Weaken(3) + to_chat(src, "You feel weak.") + emote("collapse") + + if(75 to 100) + radiation = max(radiation-2, 0) + adjustToxLoss(2) + adjustFireLoss(2) + autopsy_damage = 4 + if(prob(2)) + to_chat(src, "You mutate!") + randmutb(src) + domutcheck(src, null) + + if(101 to 150) + radiation = max(radiation-3, 0) + adjustToxLoss(2) + adjustFireLoss(3) + autopsy_damage = 5 + if(prob(4)) + to_chat(src, "You mutate!") + randmutb(src) + domutcheck(src, null) + + if(151 to INFINITY) + radiation = max(radiation-3, 0) + adjustToxLoss(2) + adjustFireLoss(3) + autopsy_damage = 5 + if(prob(6)) + to_chat(src, "You mutate!") + randmutb(src) + domutcheck(src, null) + + if(autopsy_damage) + var/obj/item/organ/external/chest/chest = get_organ("chest") + if(chest) + chest.add_autopsy_data("Radiation Poisoning", autopsy_damage) + +/mob/living/carbon/human/breathe() + if(!dna.species.breathe(src)) + ..() + +/mob/living/carbon/human/check_breath(datum/gas_mixture/breath) + + var/obj/item/organ/internal/L = get_organ_slot("lungs") + + if(!L || L && (L.status & ORGAN_DEAD)) + if(health >= HEALTH_THRESHOLD_CRIT) + adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1) + else if(!(NOCRITDAMAGE in dna.species.species_traits)) + adjustOxyLoss(HUMAN_MAX_OXYLOSS) + + if(dna.species) + var/datum/species/S = dna.species + + if(S.breathid == "o2") + throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) + else if(S.breathid == "tox") + throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) + else if(S.breathid == "co2") + throw_alert("not_enough_co2", /obj/screen/alert/not_enough_co2) + else if(S.breathid == "n2") + throw_alert("not_enough_nitro", /obj/screen/alert/not_enough_nitro) + + return FALSE + else + if(istype(L, /obj/item/organ/internal/lungs)) + var/obj/item/organ/internal/lungs/lun = L + lun.check_breath(breath, src) + +// USED IN DEATHWHISPERS +/mob/living/carbon/human/proc/isInCrit() + // Health is in deep shit and we're not already dead + return health <= HEALTH_THRESHOLD_CRIT && stat != DEAD + + +/mob/living/carbon/human/get_breath_from_internal(volume_needed) //making this call the parent would be far too complicated + if(internal) + var/null_internals = 0 //internals are invalid, therefore turn them off + var/skip_contents_check = 0 //rigsuit snowflake, oxygen tanks aren't stored inside the mob, so the 'contents.Find' check has to be skipped. + + if(!get_organ_slot("breathing_tube")) + if(!(wear_mask && wear_mask.flags & AIRTIGHT)) //if NOT (wear_mask AND wear_mask.flags CONTAIN AIRTIGHT) + if(!(head && head.flags & AIRTIGHT)) //if NOT (head AND head.flags CONTAIN AIRTIGHT) + null_internals = 1 //not wearing a mask or suitable helmet + + if(istype(back, /obj/item/rig)) //wearing a rigsuit + var/obj/item/rig/rig = back //needs to be typecasted because this doesn't use get_rig() for some reason + if(rig.offline && (rig.air_supply && internal == rig.air_supply)) //if rig IS offline AND (rig HAS air_supply AND internal IS air_supply) + null_internals = 1 //offline suits do not breath + + else if(rig.air_supply && internal == rig.air_supply) //if rig HAS air_supply AND internal IS rig air_supply + skip_contents_check = 1 //skip contents.Find() check, the oxygen is valid even being outside of the mob + + if(!contents.Find(internal) && (!skip_contents_check)) //if internal NOT IN contents AND skip_contents_check IS false + null_internals = 1 //not a rigsuit and your oxygen is gone + + if(null_internals) //something wants internals gone + internal = null //so do it + update_action_buttons_icon() + + if(internal) //check for hud updates every time this is called + return internal.remove_air_volume(volume_needed) //returns the valid air + + return null + +/mob/living/carbon/human/handle_environment(datum/gas_mixture/environment) + if(!environment) + return + + var/loc_temp = get_temperature(environment) +// to_chat(world, "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]") + + //Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit. + if(stat != DEAD) + stabilize_temperature_from_calories() + + //After then, it reacts to the surrounding atmosphere based on your thermal protection + if(!on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases + if(loc_temp < bodytemperature) + //Place is colder than we are + var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + bodytemperature += min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX) + else + //Place is hotter than we are + var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + bodytemperature += min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR), BODYTEMP_HEATING_MAX) + + // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. + if(bodytemperature > dna.species.heat_level_1) + //Body temperature is too hot. + if(status_flags & GODMODE) return 1 //godmode + var/mult = dna.species.heatmod + + if(bodytemperature >= dna.species.heat_level_1 && bodytemperature <= dna.species.heat_level_2) + throw_alert("temp", /obj/screen/alert/hot, 1) + take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_1, updating_health = TRUE, used_weapon = "High Body Temperature") + if(bodytemperature > dna.species.heat_level_2 && bodytemperature <= dna.species.heat_level_3) + throw_alert("temp", /obj/screen/alert/hot, 2) + take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_2, updating_health = TRUE, used_weapon = "High Body Temperature") + if(bodytemperature > dna.species.heat_level_3 && bodytemperature < INFINITY) + throw_alert("temp", /obj/screen/alert/hot, 3) + if(on_fire) + take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_3, updating_health = TRUE, used_weapon = "Fire") + else + take_overall_damage(burn=mult*HEAT_DAMAGE_LEVEL_2, updating_health = TRUE, used_weapon = "High Body Temperature") + + else if(bodytemperature < dna.species.cold_level_1) + if(status_flags & GODMODE) + return 1 + if(stat == DEAD) + return 1 + + if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) + var/mult = dna.species.coldmod + if(bodytemperature >= dna.species.cold_level_2 && bodytemperature <= dna.species.cold_level_1) + throw_alert("temp", /obj/screen/alert/cold, 1) + take_overall_damage(burn=mult*COLD_DAMAGE_LEVEL_1, updating_health = TRUE, used_weapon = "Low Body Temperature") + if(bodytemperature >= dna.species.cold_level_3 && bodytemperature < dna.species.cold_level_2) + throw_alert("temp", /obj/screen/alert/cold, 2) + take_overall_damage(burn=mult*COLD_DAMAGE_LEVEL_2, updating_health = TRUE, used_weapon = "Low Body Temperature") + if(bodytemperature > -INFINITY && bodytemperature < dna.species.cold_level_3) + throw_alert("temp", /obj/screen/alert/cold, 3) + take_overall_damage(burn=mult*COLD_DAMAGE_LEVEL_3, updating_health = TRUE, used_weapon = "Low Body Temperature") + else + clear_alert("temp") + else + clear_alert("temp") + + // Account for massive pressure differences. Done by Polymorph + // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! + + var/pressure = environment.return_pressure() + var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob. + if(status_flags & GODMODE) return 1 //godmode + + if(adjusted_pressure >= dna.species.hazard_high_pressure) + if(!(HEATRES in mutations)) + var/pressure_damage = min( ( (adjusted_pressure / dna.species.hazard_high_pressure) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) + take_overall_damage(brute=pressure_damage, updating_health = TRUE, used_weapon = "High Pressure") + throw_alert("pressure", /obj/screen/alert/highpressure, 2) + else + clear_alert("pressure") + else if(adjusted_pressure >= dna.species.warning_high_pressure) + throw_alert("pressure", /obj/screen/alert/highpressure, 1) + else if(adjusted_pressure >= dna.species.warning_low_pressure) + clear_alert("pressure") + else if(adjusted_pressure >= dna.species.hazard_low_pressure) + throw_alert("pressure", /obj/screen/alert/lowpressure, 1) + else + if(COLDRES in mutations) + clear_alert("pressure") + else + take_overall_damage(brute=LOW_PRESSURE_DAMAGE, updating_health = TRUE, used_weapon = "Low Pressure") + throw_alert("pressure", /obj/screen/alert/lowpressure, 2) + + +///FIRE CODE +/mob/living/carbon/human/handle_fire() + if(..()) + return + if(HEATRES in mutations) + return + if(on_fire) + var/thermal_protection = get_thermal_protection() + + if(thermal_protection >= FIRE_IMMUNITY_MAX_TEMP_PROTECT) + return + if(thermal_protection >= FIRE_SUIT_MAX_TEMP_PROTECT) + bodytemperature += 11 + else + bodytemperature += (BODYTEMP_HEATING_MAX + (fire_stacks * 12)) + +/mob/living/carbon/human/proc/get_thermal_protection() + var/thermal_protection = 0 //Simple check to estimate how protected we are against multiple temperatures + if(wear_suit) + if(wear_suit.max_heat_protection_temperature >= FIRE_SUIT_MAX_TEMP_PROTECT) + thermal_protection += (wear_suit.max_heat_protection_temperature*0.7) + if(head) + if(head.max_heat_protection_temperature >= FIRE_HELM_MAX_TEMP_PROTECT) + thermal_protection += (head.max_heat_protection_temperature*THERMAL_PROTECTION_HEAD) + thermal_protection = round(thermal_protection) + return thermal_protection + +//END FIRE CODE + +/mob/living/carbon/human/proc/stabilize_temperature_from_calories() + var/body_temperature_difference = dna.species.body_temperature - bodytemperature + + if(bodytemperature <= dna.species.cold_level_1) //260.15 is 310.15 - 50, the temperature where you start to feel effects. + bodytemperature += max((body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM) + if(bodytemperature >= dna.species.cold_level_1 && bodytemperature <= dna.species.heat_level_1) + bodytemperature += body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR + if(bodytemperature >= dna.species.heat_level_1) //360.15 is 310.15 + 50, the temperature where you start to feel effects. + //We totally need a sweat system cause it totally makes sense...~ + bodytemperature += min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers + + + //This proc returns a number made up of the flags for body parts which you are protected on. (such as HEAD, UPPER_TORSO, LOWER_TORSO, etc. See setup.dm for the full list) +/mob/living/carbon/human/proc/get_heat_protection_flags(temperature) //Temperature is the temperature you're being exposed to. + var/thermal_protection_flags = 0 + //Handle normal clothing + if(head) + if(head.max_heat_protection_temperature && head.max_heat_protection_temperature >= temperature) + thermal_protection_flags |= head.heat_protection + if(wear_suit) + if(wear_suit.max_heat_protection_temperature && wear_suit.max_heat_protection_temperature >= temperature) + thermal_protection_flags |= wear_suit.heat_protection + if(w_uniform) + if(w_uniform.max_heat_protection_temperature && w_uniform.max_heat_protection_temperature >= temperature) + thermal_protection_flags |= w_uniform.heat_protection + if(shoes) + if(shoes.max_heat_protection_temperature && shoes.max_heat_protection_temperature >= temperature) + thermal_protection_flags |= shoes.heat_protection + if(gloves) + if(gloves.max_heat_protection_temperature && gloves.max_heat_protection_temperature >= temperature) + thermal_protection_flags |= gloves.heat_protection + if(wear_mask) + if(wear_mask.max_heat_protection_temperature && wear_mask.max_heat_protection_temperature >= temperature) + thermal_protection_flags |= wear_mask.heat_protection + + return thermal_protection_flags + +/mob/living/carbon/human/proc/get_heat_protection(temperature) //Temperature is the temperature you're being exposed to. + + if(HEATRES in mutations) + return 1 + + var/thermal_protection_flags = get_heat_protection_flags(temperature) + + var/thermal_protection = 0.0 + if(thermal_protection_flags) + if(thermal_protection_flags & HEAD) + thermal_protection += THERMAL_PROTECTION_HEAD + if(thermal_protection_flags & UPPER_TORSO) + thermal_protection += THERMAL_PROTECTION_UPPER_TORSO + if(thermal_protection_flags & LOWER_TORSO) + thermal_protection += THERMAL_PROTECTION_LOWER_TORSO + if(thermal_protection_flags & LEG_LEFT) + thermal_protection += THERMAL_PROTECTION_LEG_LEFT + if(thermal_protection_flags & LEG_RIGHT) + thermal_protection += THERMAL_PROTECTION_LEG_RIGHT + if(thermal_protection_flags & FOOT_LEFT) + thermal_protection += THERMAL_PROTECTION_FOOT_LEFT + if(thermal_protection_flags & FOOT_RIGHT) + thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT + if(thermal_protection_flags & ARM_LEFT) + thermal_protection += THERMAL_PROTECTION_ARM_LEFT + if(thermal_protection_flags & ARM_RIGHT) + thermal_protection += THERMAL_PROTECTION_ARM_RIGHT + if(thermal_protection_flags & HAND_LEFT) + thermal_protection += THERMAL_PROTECTION_HAND_LEFT + if(thermal_protection_flags & HAND_RIGHT) + thermal_protection += THERMAL_PROTECTION_HAND_RIGHT + + + return min(1,thermal_protection) + + //See proc/get_heat_protection_flags(temperature) for the description of this proc. +/mob/living/carbon/human/proc/get_cold_protection_flags(temperature) + var/thermal_protection_flags = 0 + //Handle normal clothing + + if(head) + if(head.min_cold_protection_temperature && head.min_cold_protection_temperature <= temperature) + thermal_protection_flags |= head.cold_protection + if(wear_suit) + if(wear_suit.min_cold_protection_temperature && wear_suit.min_cold_protection_temperature <= temperature) + thermal_protection_flags |= wear_suit.cold_protection + if(w_uniform) + if(w_uniform.min_cold_protection_temperature && w_uniform.min_cold_protection_temperature <= temperature) + thermal_protection_flags |= w_uniform.cold_protection + if(shoes) + if(shoes.min_cold_protection_temperature && shoes.min_cold_protection_temperature <= temperature) + thermal_protection_flags |= shoes.cold_protection + if(gloves) + if(gloves.min_cold_protection_temperature && gloves.min_cold_protection_temperature <= temperature) + thermal_protection_flags |= gloves.cold_protection + if(wear_mask) + if(wear_mask.min_cold_protection_temperature && wear_mask.min_cold_protection_temperature <= temperature) + thermal_protection_flags |= wear_mask.cold_protection + + return thermal_protection_flags + +/mob/living/carbon/human/proc/get_cold_protection(temperature) + + if(COLDRES in mutations) + return 1 //Fully protected from the cold. + + temperature = max(temperature, TCMB) //There is an occasional bug where the temperature is miscalculated in areas with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. + var/thermal_protection_flags = get_cold_protection_flags(temperature) + + var/thermal_protection = 0.0 + if(thermal_protection_flags) + if(thermal_protection_flags & HEAD) + thermal_protection += THERMAL_PROTECTION_HEAD + if(thermal_protection_flags & UPPER_TORSO) + thermal_protection += THERMAL_PROTECTION_UPPER_TORSO + if(thermal_protection_flags & LOWER_TORSO) + thermal_protection += THERMAL_PROTECTION_LOWER_TORSO + if(thermal_protection_flags & LEG_LEFT) + thermal_protection += THERMAL_PROTECTION_LEG_LEFT + if(thermal_protection_flags & LEG_RIGHT) + thermal_protection += THERMAL_PROTECTION_LEG_RIGHT + if(thermal_protection_flags & FOOT_LEFT) + thermal_protection += THERMAL_PROTECTION_FOOT_LEFT + if(thermal_protection_flags & FOOT_RIGHT) + thermal_protection += THERMAL_PROTECTION_FOOT_RIGHT + if(thermal_protection_flags & ARM_LEFT) + thermal_protection += THERMAL_PROTECTION_ARM_LEFT + if(thermal_protection_flags & ARM_RIGHT) + thermal_protection += THERMAL_PROTECTION_ARM_RIGHT + if(thermal_protection_flags & HAND_LEFT) + thermal_protection += THERMAL_PROTECTION_HAND_LEFT + if(thermal_protection_flags & HAND_RIGHT) + thermal_protection += THERMAL_PROTECTION_HAND_RIGHT + + return min(1,thermal_protection) + + +/mob/living/carbon/human/proc/get_covered_bodyparts() + var/covered = 0 + + if(head) + covered |= head.body_parts_covered + if(wear_suit) + covered |= wear_suit.body_parts_covered + if(w_uniform) + covered |= w_uniform.body_parts_covered + if(shoes) + covered |= shoes.body_parts_covered + if(gloves) + covered |= gloves.body_parts_covered + if(wear_mask) + covered |= wear_mask.body_parts_covered + + return covered + +/mob/living/carbon/human/handle_chemicals_in_body() + ..() + + if(status_flags & GODMODE) + return 0 //godmode + + if(!(NO_HUNGER in dna.species.species_traits)) + //The fucking FAT mutation is the greatest shit ever. It makes everyone so hot and bothered. + if(CAN_BE_FAT in dna.species.species_traits) + if(FAT in mutations) + if(overeatduration < 100) + becomeSlim() + else + if(overeatduration > 500) + becomeFat() + + // nutrition decrease + if(nutrition > 0 && stat != DEAD) + // THEY HUNGER + var/hunger_rate = hunger_drain + if(satiety > 0) + satiety-- + if(satiety < 0) + satiety++ + if(prob(round(-satiety/40))) + Jitter(5) + hunger_rate = 3 * hunger_drain + adjust_nutrition(-hunger_rate) + + if(nutrition > NUTRITION_LEVEL_FULL) + if(overeatduration < 600) //capped so people don't take forever to unfat + overeatduration++ + + else + if(overeatduration > 1) + if(OBESITY in mutations) + overeatduration -= 1 // Those with obesity gene take twice as long to unfat + else + overeatduration -= 2 + + if(!ismachine(src) && nutrition < NUTRITION_LEVEL_HYPOGLYCEMIA) //Gosh damn snowflakey IPCs + var/datum/disease/D = new /datum/disease/critical/hypoglycemia + ForceContractDisease(D) + + //metabolism change + if(nutrition > NUTRITION_LEVEL_FAT) + metabolism_efficiency = 1 + else if(nutrition > NUTRITION_LEVEL_FED && satiety > 80) + if(metabolism_efficiency != 1.25) + to_chat(src, "You feel vigorous.") + metabolism_efficiency = 1.25 + else if(nutrition < NUTRITION_LEVEL_STARVING + 50) + if(metabolism_efficiency != 0.8) + to_chat(src, "You feel sluggish.") + metabolism_efficiency = 0.8 + else + if(metabolism_efficiency == 1.25) + to_chat(src, "You no longer feel vigorous.") + metabolism_efficiency = 1 + + if(drowsyness) + AdjustDrowsy(-1) + EyeBlurry(2) + if(prob(5)) + AdjustSleeping(1) + Paralyse(5) + + AdjustConfused(-1) + // decrement dizziness counter, clamped to 0 + if(resting) + AdjustDizzy(-15) + AdjustJitter(-15) + else + AdjustDizzy(-3) + AdjustJitter(-3) + + if(NO_INTORGANS in dna.species.species_traits) + return + + handle_trace_chems() + + return //TODO: DEFERRED + +/mob/living/carbon/human/handle_drunk() + var/slur_start = 30 //12u ethanol, 30u whiskey FOR HUMANS + var/confused_start = 40 + var/brawl_start = 30 + var/blur_start = 75 + var/vomit_start = 60 + var/pass_out = 90 + var/spark_start = 50 //40u synthanol + var/collapse_start = 75 + var/braindamage_start = 120 + var/alcohol_strength = drunk + var/sober_str=!(SOBER in mutations)?1:2 + + if(drunk) + alcohol_strength/=sober_str + + var/obj/item/organ/internal/liver/L + if(!isSynthetic()) + L = get_int_organ(/obj/item/organ/internal/liver) + if(L) + alcohol_strength *= L.alcohol_intensity + else + alcohol_strength *= 5 + + if(alcohol_strength >= slur_start) //slurring + Slur(drunk) + if(alcohol_strength >= brawl_start) //the drunken martial art + if(!istype(martial_art, /datum/martial_art/drunk_brawling)) + var/datum/martial_art/drunk_brawling/F = new + F.teach(src,1) + if(alcohol_strength < brawl_start) //removing the art + if(istype(martial_art, /datum/martial_art/drunk_brawling)) + martial_art.remove(src) + if(alcohol_strength >= confused_start && prob(33)) //confused walking + if(!confused) Confused(1) + AdjustConfused(3/sober_str) + if(alcohol_strength >= blur_start) //blurry eyes + EyeBlurry(10/sober_str) + if(!isSynthetic()) //stuff only for non-synthetics + if(alcohol_strength >= vomit_start) //vomiting + if(prob(8)) + fakevomit() + if(alcohol_strength >= pass_out) + Paralyse(5/sober_str) + Drowsy(30/sober_str) + if(L) + L.receive_damage(0.1, 1) + adjustToxLoss(0.1) + else //stuff only for synthetics + if(alcohol_strength >= spark_start && prob(25)) + do_sparks(3, 1, src) + if(alcohol_strength >= collapse_start && prob(10)) + emote("collapse") + do_sparks(3, 1, src) + if(alcohol_strength >= braindamage_start && prob(10)) + adjustBrainLoss(1) + + if(!has_booze()) + AdjustDrunk(-0.5) + return + +/mob/living/carbon/human/proc/has_booze() //checks if the human has ethanol or its subtypes inside + for(var/A in reagents.reagent_list) + var/datum/reagent/R = A + if(istype(R, /datum/reagent/consumable/ethanol)) + return 1 + return 0 + +/mob/living/carbon/human/handle_regular_status_updates() + if(status_flags & GODMODE) + return 0 + + . = ..() + + if(.) //alive + if(REGEN in mutations) + heal_overall_damage(0.1, 0.1) + + if(paralysis) + stat = UNCONSCIOUS + + else if(sleeping) + + stat = UNCONSCIOUS + + if(mind) + if(mind.vampire) + if(istype(loc, /obj/structure/closet/coffin)) + adjustBruteLoss(-1) + adjustFireLoss(-1) + adjustToxLoss(-1) + + else if(status_flags & FAKEDEATH) + stat = UNCONSCIOUS + + //Vision //god knows why this is here + var/obj/item/organ/vision + if(dna.species.vision_organ) + vision = get_int_organ(dna.species.vision_organ) + + if(!dna.species.vision_organ) // Presumably if a species has no vision organs, they see via some other means. + SetEyeBlind(0) + SetEyeBlurry(0) + + else if(!vision || vision.is_broken()) // Vision organs cut out or broken? Permablind. + EyeBlind(2) + EyeBlurry(2) + + else + //blindness + if(disabilities & BLIND) // Disabled-blind, doesn't get better on its own + + else if(eye_blind) // Blindness, heals slowly over time + AdjustEyeBlind(-1) + + else if(istype(glasses, /obj/item/clothing/glasses/sunglasses/blindfold)) //resting your eyes with a blindfold heals blurry eyes faster + AdjustEyeBlurry(-3) + + //blurry sight + if(vision.is_bruised()) // Vision organs impaired? Permablurry. + EyeBlurry(2) + + if(eye_blurry) // Blurry eyes heal slowly + AdjustEyeBlurry(-1) + + + if(flying) + animate(src, pixel_y = pixel_y + 5 , time = 10, loop = 1, easing = SINE_EASING) + animate(pixel_y = pixel_y - 5, time = 10, loop = 1, easing = SINE_EASING) + + // If you're dirty, your gloves will become dirty, too. + if(gloves && germ_level > gloves.germ_level && prob(10)) + gloves.germ_level += 1 + + handle_organs() + + if(getBrainLoss() >= 120 || (health + (getOxyLoss() / 2)) <= -500) + death() + return + + if(getBrainLoss() >= 100) // braindeath + AdjustLoseBreath(10, bound_lower = 0, bound_upper = 25) + Weaken(30) + + if(!check_death_method()) + if(health <= HEALTH_THRESHOLD_DEAD) + var/deathchance = min(99, ((getBrainLoss() * -5) + (health + (getOxyLoss() / 2))) * -0.01) + if(prob(deathchance)) + death() + return + + if(health <= HEALTH_THRESHOLD_CRIT) + if(prob(5)) + emote(pick("faint", "collapse", "cry", "moan", "gasp", "shudder", "shiver")) + AdjustStuttering(5, bound_lower = 0, bound_upper = 5) + EyeBlurry(5) + if(prob(7)) + AdjustConfused(2) + if(prob(5)) + Paralyse(2) + switch(health) + if(-INFINITY to -100) + adjustOxyLoss(1) + if(prob(health * -0.1)) + if(ishuman(src)) + var/mob/living/carbon/human/H = src + H.set_heartattack(TRUE) + if(prob(health * -0.2)) + var/datum/disease/D = new /datum/disease/critical/heart_failure + ForceContractDisease(D) + Paralyse(5) + if(-99 to -80) + adjustOxyLoss(1) + if(prob(4)) + to_chat(src, "Your chest hurts...") + Paralyse(2) + var/datum/disease/D = new /datum/disease/critical/heart_failure + ForceContractDisease(D) + if(-79 to -50) + adjustOxyLoss(1) + if(prob(10)) + var/datum/disease/D = new /datum/disease/critical/shock + ForceContractDisease(D) + if(prob(health * -0.08)) + var/datum/disease/D = new /datum/disease/critical/heart_failure + ForceContractDisease(D) + if(prob(6)) + to_chat(src, "You feel [pick("horrible pain", "awful", "like shit", "absolutely awful", "like death", "like you are dying", "nothing", "warm", "sweaty", "tingly", "really, really bad", "horrible")]!") + Weaken(3) + if(prob(3)) + Paralyse(2) + if(-49 to 0) + adjustOxyLoss(1) + if(prob(3)) + var/datum/disease/D = new /datum/disease/critical/shock + ForceContractDisease(D) + if(prob(5)) + to_chat(src, "You feel [pick("terrible", "awful", "like shit", "sick", "numb", "cold", "sweaty", "tingly", "horrible")]!") + Weaken(3) + + else //dead + SetSilence(0) + + +/mob/living/carbon/human/handle_vision() + if(machine) + if(!machine.check_eye(src)) + reset_perspective(null) + else + var/isRemoteObserve = 0 + if((REMOTE_VIEW in mutations) && remoteview_target) + isRemoteObserve = 1 + + if(remoteview_target.stat != CONSCIOUS) + to_chat(src, "Your psy-connection grows too faint to maintain!") + isRemoteObserve = 0 + + if(PSY_RESIST in remoteview_target.mutations) + to_chat(src, "Your mind is shut out!") + isRemoteObserve = 0 + + // Not on the station or mining? + var/turf/temp_turf = get_turf(remoteview_target) + if(!temp_turf in config.contact_levels) + to_chat(src, "Your psy-connection grows too faint to maintain!") + isRemoteObserve = 0 + + if(remote_view) + isRemoteObserve = 1 + + if(!isRemoteObserve && client && !client.adminobs) + remoteview_target = null + reset_perspective(null) + +/mob/living/carbon/human/handle_hud_icons() + dna.species.handle_hud_icons(src) + +/mob/living/carbon/human/handle_hud_icons_health() + dna.species.handle_hud_icons_health(src) + handle_hud_icons_health_overlay() + +/mob/living/carbon/human/handle_random_events() + // Puke if toxloss is too high + if(!stat) + if(getToxLoss() >= 45 && nutrition > 20) + lastpuke ++ + if(lastpuke >= 25) // about 25 second delay I guess + vomit(20, 0, 1, 0, 1) + adjustToxLoss(-3) + lastpuke = 0 + +/mob/living/carbon/human/proc/handle_embedded_objects() + for(var/X in bodyparts) + var/obj/item/organ/external/BP = X + for(var/obj/item/I in BP.embedded_objects) + if(prob(I.embedded_pain_chance)) + BP.receive_damage(I.w_class*I.embedded_pain_multiplier) + to_chat(src, "[I] embedded in your [BP.name] hurts!") + + if(prob(I.embedded_fall_chance)) + BP.receive_damage(I.w_class*I.embedded_fall_pain_multiplier) + BP.embedded_objects -= I + I.forceMove(get_turf(src)) + visible_message("[I] falls out of [name]'s [BP.name]!","[I] falls out of your [BP.name]!") + if(!has_embedded_objects()) + clear_alert("embeddedobject") + +/mob/living/carbon/human/handle_changeling() + if(mind) + if(mind.changeling) + mind.changeling.regenerate(src) + if(hud_used) + hud_used.lingchemdisplay.invisibility = 0 + hud_used.lingchemdisplay.maptext = "
    [round(mind.changeling.chem_charges)]
    " + else + if(hud_used) + hud_used.lingchemdisplay.invisibility = 101 + + +/mob/living/carbon/human/proc/handle_pulse(times_fired) + if(times_fired % 5 == 1) + return pulse //update pulse every 5 life ticks (~1 tick/sec, depending on server load) + + if(NO_BLOOD in dna.species.species_traits) + return PULSE_NONE //No blood, no pulse. + + if(stat == DEAD) + return PULSE_NONE //that's it, you're dead, nothing can influence your pulse + + if(undergoing_cardiac_arrest()) + return PULSE_NONE + + var/temp = PULSE_NORM + + if(blood_volume <= BLOOD_VOLUME_BAD)//how much blood do we have + temp = PULSE_THREADY //not enough :( + + if(status_flags & FAKEDEATH) + temp = PULSE_NONE //pretend that we're dead. unlike actual death, can be inflienced by meds + + for(var/datum/reagent/R in reagents.reagent_list) + if(R.heart_rate_decrease) + if(temp <= PULSE_THREADY && temp >= PULSE_NORM) + temp-- + break + + for(var/datum/reagent/R in reagents.reagent_list)//handles different chems' influence on pulse + if(R.heart_rate_increase) + if(temp <= PULSE_FAST && temp >= PULSE_NONE) + temp++ + break + + for(var/datum/reagent/R in reagents.reagent_list) //To avoid using fakedeath + if(R.heart_rate_stop) + temp = PULSE_NONE + break + + return temp + +/mob/living/carbon/human/proc/handle_decay() + var/decaytime = world.time - timeofdeath + + if(NO_DECAY in dna.species.species_traits) + return + + if(reagents.has_reagent("formaldehyde")) //embalming fluid stops decay + return + + if(decaytime <= 6000) //10 minutes for decaylevel1 -- stinky + return + + if(decaytime > 6000 && decaytime <= 12000)//20 minutes for decaylevel2 -- bloated and very stinky + decaylevel = 1 + + if(decaytime > 12000 && decaytime <= 18000)//30 minutes for decaylevel3 -- rotting and gross + decaylevel = 2 + + if(decaytime > 18000 && decaytime <= 27000)//45 minutes for decaylevel4 -- skeleton + decaylevel = 3 + + if(decaytime > 27000) + decaylevel = 4 + makeSkeleton() + return //No puking over skeletons, they don't smell at all! + + if(!isturf(loc)) + return + + for(var/mob/living/carbon/human/H in range(decaylevel, src)) + if(prob(2)) + var/obj/item/clothing/mask/M = H.wear_mask + if(M && (M.flags_cover & MASKCOVERSMOUTH)) + continue + if(NO_BREATHE in H.dna.species.species_traits) + continue //no puking if you can't smell! + // Humans can lack a mind datum, y'know + if(H.mind && (H.mind.assigned_role == "Detective" || H.mind.assigned_role == "Coroner")) + continue //too cool for puke + to_chat(H, "You smell something foul...") + H.fakevomit() + +/mob/living/carbon/human/proc/handle_heartbeat() + var/client/C = src.client + if(C && C.prefs.sound & SOUND_HEARTBEAT) //disable heartbeat by pref + var/obj/item/organ/internal/heart/H = get_int_organ(/obj/item/organ/internal/heart) + + if(!H) //H.status will runtime if there is no H (obviously) + return + + if(H.is_robotic()) //Handle robotic hearts specially with a wuuuubb. This also applies to machine-people. + if(isinspace()) + //PULSE_THREADY - maximum value for pulse, currently it 5. + //High pulse value corresponds to a fast rate of heartbeat. + //Divided by 2, otherwise it is too slow. + var/rate = (PULSE_THREADY - 2)/2 //machine people (main target) have no pulse, manually subtract standard human pulse (2). Mechanic-heart humans probably have a pulse, but 'advanced neural systems' keep the heart rate steady, or something + + if(heartbeat >= rate) + heartbeat = 0 + src << sound('sound/effects/electheart.ogg',0,0,CHANNEL_HEARTBEAT,30)//Credit to GhostHack (www.ghosthack.de) for sound. + + else + heartbeat++ + return + return + + if(pulse == PULSE_NONE) + return + + if(pulse >= PULSE_2FAST || isinspace()) + //PULSE_THREADY - maximum value for pulse, currently it 5. + //High pulse value corresponds to a fast rate of heartbeat. + //Divided by 2, otherwise it is too slow. + var/rate = (PULSE_THREADY - pulse)/2 + + if(heartbeat >= rate) + heartbeat = 0 + src << sound('sound/effects/singlebeat.ogg',0,0,CHANNEL_HEARTBEAT,50) + else + heartbeat++ + +/* + Called by life(), instead of having the individual hud items update icons each tick and check for status changes + we only set those statuses and icons upon changes. Then those HUD items will simply add those pre-made images. + This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag. +*/ + +/mob/living/carbon/human/proc/can_heartattack() + if((NO_BLOOD in dna.species.species_traits) && !dna.species.forced_heartattack) + return FALSE + if(NO_INTORGANS in dna.species.species_traits) + return FALSE + return TRUE + +/mob/living/carbon/human/proc/undergoing_cardiac_arrest() + if(!can_heartattack()) + return FALSE + var/obj/item/organ/internal/heart/heart = get_int_organ(/obj/item/organ/internal/heart) + if(istype(heart)) + if(heart.status & ORGAN_DEAD) + return TRUE + if(heart.beating) + return FALSE + return TRUE + +/mob/living/carbon/human/proc/set_heartattack(status) + if(!can_heartattack()) + return FALSE + + var/obj/item/organ/internal/heart/heart = get_int_organ(/obj/item/organ/internal/heart) + if(!istype(heart)) + return FALSE + + heart.beating = !status + +/mob/living/carbon/human/handle_heartattack() + if(!can_heartattack() || !undergoing_cardiac_arrest() || reagents.has_reagent("corazone")) + return + if(getOxyLoss()) + adjustBrainLoss(3) + else if(prob(10)) + adjustBrainLoss(1) + Weaken(5) + AdjustLoseBreath(20, bound_lower = 0, bound_upper = 25) + adjustOxyLoss(20) + + + +// Need this in species. +//#undef HUMAN_MAX_OXYLOSS +//#undef HUMAN_CRIT_MAX_OXYLOSS \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/login.dm b/code/modules/mob/living/carbon/human/login.dm index 4cc542ae1ade..6171b3d02b09 100644 --- a/code/modules/mob/living/carbon/human/login.dm +++ b/code/modules/mob/living/carbon/human/login.dm @@ -1,9 +1,9 @@ -/mob/living/carbon/human/Login() - if(player_logged) - overlays -= image('icons/effects/effects.dmi', icon_state = "zzz_glow") - ..() - - if(ventcrawler) - to_chat(src, "You can ventcrawl! Use alt+click on vents to quickly travel about the station.") - update_pipe_vision() - return +/mob/living/carbon/human/Login() + if(player_logged) + overlays -= image('icons/effects/effects.dmi', icon_state = "zzz_glow") + ..() + + if(ventcrawler) + to_chat(src, "You can ventcrawl! Use alt+click on vents to quickly travel about the station.") + update_pipe_vision() + return diff --git a/code/modules/mob/living/carbon/human/logout.dm b/code/modules/mob/living/carbon/human/logout.dm index 2dae7ea5b9a8..43863d956220 100644 --- a/code/modules/mob/living/carbon/human/logout.dm +++ b/code/modules/mob/living/carbon/human/logout.dm @@ -1,4 +1,4 @@ /mob/living/carbon/human/Logout() ..() if(mind && mind.active && stat != DEAD) - overlays += image('icons/effects/effects.dmi', icon_state = "zzz_glow") \ No newline at end of file + overlays += image('icons/effects/effects.dmi', icon_state = "zzz_glow") diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 220c3b9df840..b53fa03cebfa 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,258 +1,259 @@ -/mob/living/carbon/human/say(var/message, var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) - ..(message, sanitize = sanitize, ignore_speech_problems = ignore_speech_problems, ignore_atmospherics = ignore_atmospherics) //ohgod we should really be passing a datum here. - -/mob/living/carbon/human/GetAltName() - if(name != GetVoice()) - return " (as [get_id_name("Unknown")])" - return ..() - -/mob/living/carbon/human/proc/forcesay(list/append) - if(stat == CONSCIOUS) - if(client) - var/virgin = 1 //has the text been modified yet? - var/temp = winget(client, "input", "text") - if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //case sensitive means - - temp = replacetext(temp, ";", "") //general radio - - if(findtext(trim_left(temp), ":", 6, 7)) //dept radio - temp = copytext(trim_left(temp), 8) - virgin = 0 - - if(virgin) - temp = copytext(trim_left(temp), 6) //normal speech - virgin = 0 - - while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary) - temp = copytext(trim_left(temp), 3) - - if(findtext(temp, "*", 1, 2)) //emotes - return - temp = copytext(trim_left(temp), 1, rand(5,8)) - - var/trimmed = trim_left(temp) - if(length(trimmed)) - if(append) - temp += pick(append) - - say(temp) - winset(client, "input", "text=[null]") - -/mob/living/carbon/human/say_understands(var/mob/other, var/datum/language/speaking = null) - if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak. - return 1 - - if(dna.species.can_understand(other)) - return 1 - - //These only pertain to common. Languages are handled by mob/say_understands() - if(!speaking) - if(istype(other, /mob/living/simple_animal/diona)) - if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. - return 1 - if(issilicon(other)) - return 1 - if(isbot(other)) - return 1 - if(isbrain(other)) - return 1 - if(isslime(other)) - return 1 - - return ..() - -/mob/living/carbon/human/proc/HasVoiceChanger() - if(istype(back, /obj/item/rig)) - var/obj/item/rig/rig = back - if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) - return rig.speech.voice_holder.voice - - for(var/obj/item/gear in list(wear_mask, wear_suit, head)) - if(!gear) - continue - - var/obj/item/voice_changer/changer = locate() in gear - if(changer && changer.active) - if(changer.voice) - return changer.voice - else if(wear_id) - var/obj/item/card/id/idcard = wear_id.GetID() - if(istype(idcard)) - return idcard.registered_name - - return FALSE - -/mob/living/carbon/human/GetVoice() - var/has_changer = HasVoiceChanger() - - if(has_changer) - return has_changer - - if(mind && mind.changeling && mind.changeling.mimicing) - return mind.changeling.mimicing - - if(GetSpecialVoice()) - return GetSpecialVoice() - - return real_name - -/mob/living/carbon/human/IsVocal() - var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate(/obj/item/organ/internal/cyberimp/brain/speech_translator) in internal_organs - if(translator && translator.active) - return TRUE - // how do species that don't breathe talk? magic, that's what. - var/breathes = (!(NO_BREATHE in dna.species.species_traits)) - var/obj/item/organ/internal/L = get_organ_slot("lungs") - if((breathes && !L) || breathes && L && (L.status & ORGAN_DEAD)) - return FALSE - if(getOxyLoss() > 10 || losebreath >= 4) - emote("gasp") - return FALSE - if(mind) - return !mind.miming - return TRUE - -/mob/living/carbon/human/proc/SetSpecialVoice(var/new_voice) - if(new_voice) - special_voice = new_voice - return - -/mob/living/carbon/human/proc/UnsetSpecialVoice() - special_voice = "" - return - -/mob/living/carbon/human/proc/GetSpecialVoice() - return special_voice - -/mob/living/carbon/human/handle_speech_problems(list/message_pieces, var/verb) - var/span = "" - var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate(/obj/item/organ/internal/cyberimp/brain/speech_translator) in internal_organs - if(translator) - if(translator.active) - span = translator.speech_span - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = "[S.message]" - verb = translator.speech_verb - return list("verb" = verb) - if(mind) - span = mind.speech_span - if((COMIC in mutations) \ - || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \ - || GetComponent(/datum/component/jestosterone)) - span = "sans" - - if(WINGDINGS in mutations) - span = "wingdings" - - var/list/parent = ..() - verb = parent["verb"] - - for(var/datum/multilingual_say_piece/S in message_pieces) - if(S.speaking && S.speaking.flags & NO_STUTTER) - continue - - if(silent || (disabilities & MUTE)) - S.message = "" - - if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) - var/obj/item/clothing/mask/horsehead/hoers = wear_mask - if(hoers.voicechange) - S.message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") - verb = pick("whinnies", "neighs", "says") - - if(dna) - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - S.message = gene.OnSay(src, S.message) - - var/braindam = getBrainLoss() - if(braindam >= 60) - if(prob(braindam / 4)) - S.message = stutter(S.message) - verb = "gibbers" - if(prob(braindam)) - S.message = uppertext(S.message) - verb = "yells loudly" - - if(span) - S.message = "[S.message]" - return list("verb" = verb) - -/mob/living/carbon/human/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) - switch(message_mode) - if("intercom") - for(var/obj/item/radio/intercom/I in view(1, src)) - spawn(0) - I.talk_into(src, message_pieces, null, verb) - used_radios += I - - if("headset") - var/obj/item/radio/R = null - if(isradio(l_ear)) - R = l_ear - used_radios += R - if(R.talk_into(src, message_pieces, null, verb)) - return - - if(isradio(r_ear)) - R = r_ear - used_radios += R - if(R.talk_into(src, message_pieces, null, verb)) - return - - if("right ear") - var/obj/item/radio/R - if(isradio(r_ear)) - R = r_ear - else if(isradio(r_hand)) - R = r_hand - if(R) - used_radios += R - R.talk_into(src, message_pieces, null, verb) - - if("left ear") - var/obj/item/radio/R - if(isradio(l_ear)) - R = l_ear - else if(isradio(l_hand)) - R = l_hand - if(R) - used_radios += R - R.talk_into(src, message_pieces, null, verb) - - if("whisper") - whisper_say(message_pieces) - return 1 - else - if(message_mode) - if(isradio(l_ear)) - used_radios += l_ear - if(l_ear.talk_into(src, message_pieces, message_mode, verb)) - return - - if(isradio(r_ear)) - used_radios += r_ear - if(r_ear.talk_into(src, message_pieces, message_mode, verb)) - return - -/mob/living/carbon/human/handle_speech_sound() - var/list/returns[2] - if(dna.species.speech_sounds && prob(dna.species.speech_chance)) - returns[1] = sound(pick(dna.species.speech_sounds)) - returns[2] = 50 - return returns - -/mob/living/carbon/human/binarycheck() - . = FALSE - var/obj/item/radio/headset/R - if(istype(l_ear, /obj/item/radio/headset)) - R = l_ear - if(R.translate_binary) - . = TRUE - - if(istype(r_ear, /obj/item/radio/headset)) - R = r_ear - if(R.translate_binary) - . = TRUE +/mob/living/carbon/human/say(var/message, var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) + ..(message, sanitize = sanitize, ignore_speech_problems = ignore_speech_problems, ignore_atmospherics = ignore_atmospherics) //ohgod we should really be passing a datum here. + +/mob/living/carbon/human/GetAltName() + if(name != GetVoice()) + return " (as [get_id_name("Unknown")])" + return ..() + +/mob/living/carbon/human/proc/forcesay(list/append) + if(stat == CONSCIOUS) + if(client) + var/virgin = 1 //has the text been modified yet? + var/temp = winget(client, "input", "text") + if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //case sensitive means + + temp = replacetext(temp, ";", "") //general radio + + if(findtext(trim_left(temp), ":", 6, 7)) //dept radio + temp = copytext(trim_left(temp), 8) + virgin = 0 + + if(virgin) + temp = copytext(trim_left(temp), 6) //normal speech + virgin = 0 + + while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary) + temp = copytext(trim_left(temp), 3) + + if(findtext(temp, "*", 1, 2)) //emotes + return + temp = copytext(trim_left(temp), 1, rand(5,8)) + + var/trimmed = trim_left(temp) + if(length(trimmed)) + if(append) + temp += pick(append) + + say(temp) + winset(client, "input", "text=[null]") + +/mob/living/carbon/human/say_understands(var/mob/other, var/datum/language/speaking = null) + if(has_brain_worms()) //Brain worms translate everything. Even mice and alien speak. + return 1 + + if(dna.species.can_understand(other)) + return 1 + + //These only pertain to common. Languages are handled by mob/say_understands() + if(!speaking) + if(istype(other, /mob/living/simple_animal/diona)) + if(other.languages.len >= 2) //They've sucked down some blood and can speak common now. + return 1 + if(issilicon(other)) + return 1 + if(isbot(other)) + return 1 + if(isbrain(other)) + return 1 + if(isslime(other)) + return 1 + + return ..() + +/mob/living/carbon/human/proc/HasVoiceChanger() + if(istype(back, /obj/item/rig)) + var/obj/item/rig/rig = back + if(rig.speech && rig.speech.voice_holder && rig.speech.voice_holder.active && rig.speech.voice_holder.voice) + return rig.speech.voice_holder.voice + + for(var/obj/item/gear in list(wear_mask, wear_suit, head)) + if(!gear) + continue + + var/obj/item/voice_changer/changer = locate() in gear + if(changer && changer.active) + if(changer.voice) + return changer.voice + else if(wear_id) + var/obj/item/card/id/idcard = wear_id.GetID() + if(istype(idcard)) + return idcard.registered_name + + return FALSE + +/mob/living/carbon/human/GetVoice() + var/has_changer = HasVoiceChanger() + + if(has_changer) + return has_changer + + if(mind && mind.changeling && mind.changeling.mimicing) + return mind.changeling.mimicing + + if(GetSpecialVoice()) + return GetSpecialVoice() + + return real_name + +/mob/living/carbon/human/IsVocal() + var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate(/obj/item/organ/internal/cyberimp/brain/speech_translator) in internal_organs + if(translator && translator.active) + return TRUE + // how do species that don't breathe talk? magic, that's what. + var/breathes = (!(NO_BREATHE in dna.species.species_traits)) + var/obj/item/organ/internal/L = get_organ_slot("lungs") + if((breathes && !L) || breathes && L && (L.status & ORGAN_DEAD)) + return FALSE + if(getOxyLoss() > 10 || losebreath >= 4) + emote("gasp") + return FALSE + if(mind) + return !mind.miming + return TRUE + +/mob/living/carbon/human/proc/SetSpecialVoice(var/new_voice) + if(new_voice) + special_voice = new_voice + return + +/mob/living/carbon/human/proc/UnsetSpecialVoice() + special_voice = "" + return + +/mob/living/carbon/human/proc/GetSpecialVoice() + return special_voice + +/mob/living/carbon/human/handle_speech_problems(list/message_pieces, var/verb) + var/span = "" + var/obj/item/organ/internal/cyberimp/brain/speech_translator/translator = locate(/obj/item/organ/internal/cyberimp/brain/speech_translator) in internal_organs + if(translator) + if(translator.active) + span = translator.speech_span + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = "[S.message]" + verb = translator.speech_verb + return list("verb" = verb) + if(mind) + span = mind.speech_span + if((COMIC in mutations) \ + || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \ + || GetComponent(/datum/component/jestosterone)) + span = "sans" + + if(WINGDINGS in mutations) + span = "wingdings" + + var/list/parent = ..() + verb = parent["verb"] + + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && S.speaking.flags & NO_STUTTER) + continue + + if(silent || (disabilities & MUTE)) + S.message = "" + + if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) + var/obj/item/clothing/mask/horsehead/hoers = wear_mask + if(hoers.voicechange) + S.message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") + verb = pick("whinnies", "neighs", "says") + + if(dna) + for(var/datum/dna/gene/gene in GLOB.dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + S.message = gene.OnSay(src, S.message) + + var/braindam = getBrainLoss() + if(braindam >= 60) + if(prob(braindam / 4)) + S.message = stutter(S.message) + verb = "gibbers" + if(prob(braindam)) + S.message = uppertext(S.message) + verb = "yells loudly" + + if(span) + S.message = "[S.message]" + return list("verb" = verb) + +/mob/living/carbon/human/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) + switch(message_mode) + if("intercom") + for(var/obj/item/radio/intercom/I in view(1, src)) + spawn(0) + I.talk_into(src, message_pieces, null, verb) + used_radios += I + + if("headset") + var/obj/item/radio/R = null + if(isradio(l_ear)) + R = l_ear + used_radios += R + if(R.talk_into(src, message_pieces, null, verb)) + return + + if(isradio(r_ear)) + R = r_ear + used_radios += R + if(R.talk_into(src, message_pieces, null, verb)) + return + + if("right ear") + var/obj/item/radio/R + if(isradio(r_ear)) + R = r_ear + else if(isradio(r_hand)) + R = r_hand + if(R) + used_radios += R + R.talk_into(src, message_pieces, null, verb) + + if("left ear") + var/obj/item/radio/R + if(isradio(l_ear)) + R = l_ear + else if(isradio(l_hand)) + R = l_hand + if(R) + used_radios += R + R.talk_into(src, message_pieces, null, verb) + + if("whisper") + whisper_say(message_pieces) + return 1 + else + if(message_mode) + if(isradio(l_ear)) + used_radios += l_ear + if(l_ear.talk_into(src, message_pieces, message_mode, verb)) + return + + if(isradio(r_ear)) + used_radios += r_ear + if(r_ear.talk_into(src, message_pieces, message_mode, verb)) + return + +/mob/living/carbon/human/handle_speech_sound() + var/list/returns[3] + if(dna.species.speech_sounds && prob(dna.species.speech_chance)) + returns[1] = sound(pick(dna.species.speech_sounds)) + returns[2] = 50 + returns[3] = get_age_pitch() + return returns + +/mob/living/carbon/human/binarycheck() + . = FALSE + var/obj/item/radio/headset/R + if(istype(l_ear, /obj/item/radio/headset)) + R = l_ear + if(R.translate_binary) + . = TRUE + + if(istype(r_ear, /obj/item/radio/headset)) + R = r_ear + if(R.translate_binary) + . = TRUE diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm index a5a962600287..6034a22139b0 100644 --- a/code/modules/mob/living/carbon/human/species/_species.dm +++ b/code/modules/mob/living/carbon/human/species/_species.dm @@ -980,4 +980,4 @@ It'll return null if the organ doesn't correspond, so include null checks when u . = FALSE var/obj/item/organ/internal/ears/ears = H.get_int_organ(/obj/item/organ/internal/ears) if(istype(ears) && !ears.deaf) - . = TRUE \ No newline at end of file + . = TRUE diff --git a/code/modules/mob/living/carbon/human/species/abductor.dm b/code/modules/mob/living/carbon/human/species/abductor.dm index 33b94cbabe0c..d7220d3a3e9f 100644 --- a/code/modules/mob/living/carbon/human/species/abductor.dm +++ b/code/modules/mob/living/carbon/human/species/abductor.dm @@ -37,10 +37,10 @@ H.gender = NEUTER H.languages.Cut() //Under no condition should you be able to speak any language H.add_language("Abductor Mindlink") //other than over the abductor's own mindlink - var/datum/atom_hud/abductor_hud = huds[DATA_HUD_ABDUCTOR] + var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] abductor_hud.add_hud_to(H) /datum/species/abductor/on_species_loss(mob/living/carbon/human/H) ..() - var/datum/atom_hud/abductor_hud = huds[DATA_HUD_ABDUCTOR] - abductor_hud.remove_hud_from(H) \ No newline at end of file + var/datum/atom_hud/abductor_hud = GLOB.huds[DATA_HUD_ABDUCTOR] + abductor_hud.remove_hud_from(H) diff --git a/code/modules/mob/living/carbon/human/species/diona.dm b/code/modules/mob/living/carbon/human/species/diona.dm index 48664341623e..22edf9612703 100644 --- a/code/modules/mob/living/carbon/human/species/diona.dm +++ b/code/modules/mob/living/carbon/human/species/diona.dm @@ -117,4 +117,4 @@ /datum/species/diona/pod/on_species_loss(mob/living/carbon/C) . = ..() C.faction -= "plants" - C.faction -= "vines" \ No newline at end of file + C.faction -= "vines" diff --git a/code/modules/mob/living/carbon/human/species/golem.dm b/code/modules/mob/living/carbon/human/species/golem.dm index f88670fc0c1a..157995e95494 100644 --- a/code/modules/mob/living/carbon/human/species/golem.dm +++ b/code/modules/mob/living/carbon/human/species/golem.dm @@ -722,4 +722,4 @@ H.mind.miming = TRUE /datum/unarmed_attack/golem/tranquillite - attack_sound = null \ No newline at end of file + attack_sound = null diff --git a/code/modules/mob/living/carbon/human/species/grey.dm b/code/modules/mob/living/carbon/human/species/grey.dm index 502ce1a4ac09..720a13a7d550 100644 --- a/code/modules/mob/living/carbon/human/species/grey.dm +++ b/code/modules/mob/living/carbon/human/species/grey.dm @@ -32,9 +32,9 @@ /datum/species/grey/handle_dna(mob/living/carbon/human/H, remove) ..() - H.dna.SetSEState(REMOTETALKBLOCK, !remove, 1) - genemutcheck(H, REMOTETALKBLOCK, null, MUTCHK_FORCED) - H.dna.default_blocks.Add(REMOTETALKBLOCK) + H.dna.SetSEState(GLOB.remotetalkblock, !remove, 1) + genemutcheck(H, GLOB.remotetalkblock, null, MUTCHK_FORCED) + H.dna.default_blocks.Add(GLOB.remotetalkblock) /datum/species/grey/water_act(mob/living/carbon/human/H, volume, temperature, source, method = REAGENT_TOUCH) . = ..() diff --git a/code/modules/mob/living/carbon/human/species/human.dm b/code/modules/mob/living/carbon/human/species/human.dm index 2570fb060e7a..6052e769c236 100644 --- a/code/modules/mob/living/carbon/human/species/human.dm +++ b/code/modules/mob/living/carbon/human/species/human.dm @@ -18,4 +18,4 @@ reagent_tag = PROCESS_ORG //Has standard darksight of 2. - \ No newline at end of file + diff --git a/code/modules/mob/living/carbon/human/species/kidan.dm b/code/modules/mob/living/carbon/human/species/kidan.dm index 538b524d4d8e..8d684b36ba09 100644 --- a/code/modules/mob/living/carbon/human/species/kidan.dm +++ b/code/modules/mob/living/carbon/human/species/kidan.dm @@ -39,4 +39,4 @@ "is twisting their own neck!", "is cracking their exoskeleton!", "is stabbing themselves with their mandibles!", - "is holding their breath!") \ No newline at end of file + "is holding their breath!") diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm index 57353efc01de..fdc7c6c4c468 100644 --- a/code/modules/mob/living/carbon/human/species/machine.dm +++ b/code/modules/mob/living/carbon/human/species/machine.dm @@ -19,7 +19,8 @@ burn_mod = 2.28 // So they take 50% extra damage from brute/burn overall tox_mod = 0 clone_mod = 0 - death_message = "gives one shrill beep before falling limp, their monitor flashing blue before completely shutting off..." + death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional." + death_sounds = list('sound/voice/borg_deathsound.ogg') //I've made this a list in the event we add more sounds for dead robots. species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie! clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS @@ -115,7 +116,7 @@ to_chat(H, "Where's your head at? Can't change your monitor/display without one.") return - var/datum/robolimb/robohead = all_robolimbs[head_organ.model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[head_organ.model] if(!head_organ) return if(!robohead.is_monitor) //If they've got a prosthetic head and it isn't a monitor, they've no screen to adjust. Instead, let them change the colour of their optics! diff --git a/code/modules/mob/living/carbon/human/species/monkey.dm b/code/modules/mob/living/carbon/human/species/monkey.dm index b357d2c5345f..6a478c00f554 100644 --- a/code/modules/mob/living/carbon/human/species/monkey.dm +++ b/code/modules/mob/living/carbon/human/species/monkey.dm @@ -41,7 +41,7 @@ if(H.stat != CONSCIOUS) return if(prob(33) && H.canmove && isturf(H.loc) && !H.pulledby) //won't move if being pulled - step(H, pick(cardinal)) + step(H, pick(GLOB.cardinal)) if(prob(1)) H.emote(pick("scratch","jump","roll","tail")) @@ -57,8 +57,8 @@ /datum/species/monkey/handle_dna(mob/living/carbon/human/H, remove) ..() if(!remove) - H.dna.SetSEState(MONKEYBLOCK, TRUE) - genemutcheck(H, MONKEYBLOCK, null, MUTCHK_FORCED) + H.dna.SetSEState(GLOB.monkeyblock, TRUE) + genemutcheck(H, GLOB.monkeyblock, null, MUTCHK_FORCED) /datum/species/monkey/tajaran name = "Farwa" diff --git a/code/modules/mob/living/carbon/human/species/nucleation.dm b/code/modules/mob/living/carbon/human/species/nucleation.dm index cf9f5846193d..f82a6283066d 100644 --- a/code/modules/mob/living/carbon/human/species/nucleation.dm +++ b/code/modules/mob/living/carbon/human/species/nucleation.dm @@ -38,4 +38,4 @@ var/turf/T = get_turf(H) H.visible_message("[H]'s body explodes, leaving behind a pile of microscopic crystals!") explosion(T, 0, 0, 2, 2) // Create a small explosion burst upon death - qdel(H) \ No newline at end of file + qdel(H) diff --git a/code/modules/mob/living/carbon/human/species/shadow.dm b/code/modules/mob/living/carbon/human/species/shadow.dm index b21002e7225f..7d1d4c26a616 100644 --- a/code/modules/mob/living/carbon/human/species/shadow.dm +++ b/code/modules/mob/living/carbon/human/species/shadow.dm @@ -70,4 +70,4 @@ else if(light_amount < 2) //heal in the dark H.heal_overall_damage(1,1) H.clear_alert("lightexposure") - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/human/species/shadowling.dm b/code/modules/mob/living/carbon/human/species/shadowling.dm index d05c358ce730..4667a6b1019d 100644 --- a/code/modules/mob/living/carbon/human/species/shadowling.dm +++ b/code/modules/mob/living/carbon/human/species/shadowling.dm @@ -79,4 +79,4 @@ H.adjustToxLoss(-5) H.adjustBrainLoss(-25) H.adjustCloneLoss(-1) - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/human/species/skrell.dm b/code/modules/mob/living/carbon/human/species/skrell.dm index 9e21c0bf390a..e0710bec11fa 100644 --- a/code/modules/mob/living/carbon/human/species/skrell.dm +++ b/code/modules/mob/living/carbon/human/species/skrell.dm @@ -52,4 +52,4 @@ /datum/species/skrell/on_species_loss(mob/living/carbon/human/H) ..() - REMOVE_TRAIT(H, TRAIT_WATERBREATH, "species") \ No newline at end of file + REMOVE_TRAIT(H, TRAIT_WATERBREATH, "species") diff --git a/code/modules/mob/living/carbon/human/species/slime.dm b/code/modules/mob/living/carbon/human/species/slime.dm index e7501486b726..622e0919b45b 100644 --- a/code/modules/mob/living/carbon/human/species/slime.dm +++ b/code/modules/mob/living/carbon/human/species/slime.dm @@ -146,11 +146,13 @@ return var/limb_select = input(H, "Choose a limb to regrow", "Limb Regrowth") as null|anything in missing_limbs + if(!limb_select) // If the user hit cancel on the popup, return + return var/chosen_limb = missing_limbs[limb_select] H.visible_message("[H] begins to hold still and concentrate on [H.p_their()] missing [limb_select]...", "You begin to focus on regrowing your missing [limb_select]... (This will take [round(SLIMEPERSON_REGROWTHDELAY/10)] seconds, and you must hold still.)") - if(do_after(H, SLIMEPERSON_REGROWTHDELAY, needhand = 0, target = H)) - if(H.incapacitated()) + if(do_after(H, SLIMEPERSON_REGROWTHDELAY, FALSE, H, extra_checks = list(CALLBACK(H, /mob.proc/IsStunned)), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned + if(H.incapacitated(ignore_lying = TRUE, extra_checks = list(CALLBACK(H, /mob.proc/IsStunned)), use_default_checks = FALSE)) // Override the check for weakness, only check for stunned to_chat(H, "You cannot regenerate missing limbs in your current state.") return diff --git a/code/modules/mob/living/carbon/human/species/tajaran.dm b/code/modules/mob/living/carbon/human/species/tajaran.dm index ee12ba34afe1..8a803aa3bd4d 100644 --- a/code/modules/mob/living/carbon/human/species/tajaran.dm +++ b/code/modules/mob/living/carbon/human/species/tajaran.dm @@ -56,4 +56,4 @@ "is holding their breath!") /datum/species/tajaran/handle_death(gibbed, mob/living/carbon/human/H) - H.stop_tail_wagging(1) \ No newline at end of file + H.stop_tail_wagging(1) diff --git a/code/modules/mob/living/carbon/human/species/vulpkanin.dm b/code/modules/mob/living/carbon/human/species/vulpkanin.dm index 15e25fa2ff57..9a313042d2df 100644 --- a/code/modules/mob/living/carbon/human/species/vulpkanin.dm +++ b/code/modules/mob/living/carbon/human/species/vulpkanin.dm @@ -50,4 +50,4 @@ "is holding their breath!") /datum/species/vulpkanin/handle_death(gibbed, mob/living/carbon/human/H) - H.stop_tail_wagging(1) \ No newline at end of file + H.stop_tail_wagging(1) diff --git a/code/modules/mob/living/carbon/human/species/wryn.dm b/code/modules/mob/living/carbon/human/species/wryn.dm index 3fec4822919c..9e02e2e28be4 100644 --- a/code/modules/mob/living/carbon/human/species/wryn.dm +++ b/code/modules/mob/living/carbon/human/species/wryn.dm @@ -75,4 +75,4 @@ add_attack_logs(user, target, "Antennae removed") return 0 else - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index eeda9f87461e..6139bed100f4 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -1,1352 +1,1352 @@ -/* - Global associative list for caching humanoid icons. - Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. - TODO: Proper documentation - icon_key is [species.race_key][g][husk][fat][hulk][skeleton][s_tone] -*/ -var/global/list/human_icon_cache = list() - - /////////////////////// - //UPDATE_ICONS SYSTEM// - /////////////////////// -/* -Calling this a system is perhaps a bit trumped up. It is essentially update_clothing dismantled into its -core parts. The key difference is that when we generate overlays we do not generate either lying or standing -versions. Instead, we generate both and store them in two fixed-length lists, both using the same list-index -(The indexes are in update_icons.dm): Each list for humans is (at the time of writing) of length 19. -This will hopefully be reduced as the system is refined. - - var/overlays_lying[19] //For the lying down stance - var/overlays_standing[19] //For the standing stance - -When we call update_icons, the 'lying' variable is checked and then the appropriate list is assigned to our overlays! -That in itself uses a tiny bit more memory (no more than all the ridiculous lists the game has already mind you). - -On the other-hand, it should be very CPU cheap in comparison to the old system. -In the old system, we updated all our overlays every life() call, even if we were standing still inside a crate! -or dead!. 25ish overlays, all generated from scratch every second for every xeno/human/monkey and then applied. -More often than not update_clothing was being called a few times in addition to that! CPU was not the only issue, -all those icons had to be sent to every client. So really the cost was extremely cumulative. To the point where -update_clothing would frequently appear in the top 10 most CPU intensive procs during profiling. - -Another feature of this new system is that our lists are indexed. This means we can update specific overlays! -So we only regenerate icons when we need them to be updated! This is the main saving for this system. - -In practice this means that: - everytime you fall over, we just switch between precompiled lists. Which is fast and cheap. - Everytime you do something minor like take a pen out of your pocket, we only update the in-hand overlay - etc... - - -There are several things that need to be remembered: - -> Whenever we do something that should cause an overlay to update (which doesn't use standard procs - ( i.e. you do something like l_hand = /obj/item/something new(src) ) - You will need to call the relevant update_inv_* proc: - update_inv_head() - update_inv_wear_suit() - update_inv_gloves() - update_inv_shoes() - update_inv_w_uniform() - update_inv_glasse() - update_inv_l_hand() - update_inv_r_hand() - update_inv_belt() - update_inv_wear_id() - update_inv_ears() - update_inv_s_store() - update_inv_pockets() - update_inv_back() - update_inv_handcuffed() - update_inv_wear_mask() - - All of these are named after the variable they update from. They are defined at the mob/ level like - update_clothing was, so you won't cause undefined proc runtimes with usr.update_inv_wear_id() if the usr is a - slime etc. Instead, it'll just return without doing any work. So no harm in calling it for slimes and such. - - -> There are also these special cases: - update_mutations() //handles updating your appearance for certain mutations. e.g TK head-glows - update_mutantrace() //handles updating your appearance after setting the mutantrace var - UpdateDamageIcon() //handles damage overlays for brute/burn damage //(will rename this when I geta round to it) - update_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc - update_hair() //Handles updating your hair overlay (used to be update_face, but mouth and - ...eyes were merged into update_body) - -> All of these procs update our overlays_lying and overlays_standing, and then call update_icons() by default. - If you wish to update several overlays at once, you can set the argument to 0 to disable the update and call - it manually: - e.g. - update_inv_head(0) - update_inv_l_hand(0) - update_inv_r_hand() //<---calls update_icons() - - or equivillantly: - update_inv_head(0) - update_inv_l_hand(0) - update_inv_r_hand(0) - update_icons() - -> If you need to update all overlays you can use regenerate_icons(). it works exactly like update_clothing used to. - -> I reimplimented an old unused variable which was in the code called (coincidentally) var/update_icon - It can be used as another method of triggering regenerate_icons(). It's basically a flag that when set to non-zero - will call regenerate_icons() at the next life() call and then reset itself to 0. - The idea behind it is icons are regenerated only once, even if multiple events requested it. - -This system is confusing and is still a WIP. It's primary goal is speeding up the controls of the game whilst -reducing processing costs. So please bear with me while I iron out the kinks. It will be worth it, I promise. -If I can eventually free var/lying stuff from the life() process altogether, stuns/death/status stuff -will become less affected by lag-spikes and will be instantaneous! :3 - -If you have any questions/constructive-comments/bugs-to-report/or have a massivly devestated butt... -Please contact me on #coderbus IRC. ~Carn x -*/ - -/mob/living/carbon/human - var/list/overlays_standing[TOTAL_LAYERS] - var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed - var/icon/skeleton - var/list/cached_standing_overlays = list() // List of everything currently in a human's actual overlays - -/mob/living/carbon/human/proc/apply_overlay(cache_index) - if((. = overlays_standing[cache_index])) - add_overlay(.) - -/mob/living/carbon/human/proc/remove_overlay(cache_index) - var/I = overlays_standing[cache_index] - if(I) - cut_overlay(I) - overlays_standing[cache_index] = null - - -var/global/list/damage_icon_parts = list() - -//DAMAGE OVERLAYS -//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists -/mob/living/carbon/human/UpdateDamageIcon(var/update_icons=1) - // first check whether something actually changed about damage appearance - var/damage_appearance = "" - - for(var/obj/item/organ/external/O in bodyparts) - damage_appearance += O.damage_state - - if(damage_appearance == previous_damage_appearance) - // nothing to do here - return - - previous_damage_appearance = damage_appearance - - remove_overlay(H_DAMAGE_LAYER) - var/mutable_appearance/damage_overlay = mutable_appearance(dna.species.damage_overlays, "00", layer = -H_DAMAGE_LAYER) - overlays_standing[H_DAMAGE_LAYER] = damage_overlay - - // blend the individual damage states with our icons - for(var/D in bodyparts) - var/obj/item/organ/external/E = D - E.update_icon() - if(E.damage_state == "00") - continue - - var/icon/DI - var/cache_index = "[E.damage_state]/[E.icon_name]/[dna.species.blood_color]/[dna.species.name]" - - if(damage_icon_parts[cache_index] == null) - DI = new /icon(dna.species.damage_overlays, E.damage_state) // the damage icon for whole human - DI.Blend(new /icon(dna.species.damage_mask, E.icon_name), ICON_MULTIPLY) // mask with this organ's pixels - DI.Blend(dna.species.blood_color, ICON_MULTIPLY) - damage_icon_parts[cache_index] = DI - else - DI = damage_icon_parts[cache_index] - damage_overlay.overlays += DI - - apply_overlay(H_DAMAGE_LAYER) - - -//BASE MOB SPRITE -/mob/living/carbon/human/proc/update_body(var/update_icons=1, var/rebuild_base=0) - remove_overlay(BODY_LAYER) - remove_overlay(LIMBS_LAYER) // So we don't get the old species' sprite splatted on top of the new one's - remove_overlay(UNDERWEAR_LAYER) - - var/husk_color_mod = rgb(96, 88, 80) - var/hulk_color_mod = rgb(48, 224, 40) - - var/husk = (HUSK in mutations) - var/hulk = (HULK in mutations) - var/skeleton = (SKELETON in mutations) - - if(dna.species && dna.species.bodyflags & HAS_ICON_SKIN_TONE) - dna.species.updatespeciescolor(src) - - //CACHING: Generate an index key from visible bodyparts. - //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. - //Create a new, blank icon for our mob to use. - if(stand_icon) - qdel(stand_icon) - - update_misc_effects() - stand_icon = new (dna.species.icon_template ? dna.species.icon_template : 'icons/mob/human.dmi', "blank") - var/list/standing = list() - var/icon_key = generate_icon_render_key() - - var/mutable_appearance/base - if(human_icon_cache[icon_key] && !rebuild_base) - base = human_icon_cache[icon_key] - standing += base - else - var/icon/base_icon - //BEGIN CACHED ICON GENERATION. - var/obj/item/organ/external/chest = get_organ("chest") - base_icon = chest.get_icon(skeleton) - - for(var/obj/item/organ/external/part in bodyparts) - var/icon/temp = part.get_icon(skeleton) - //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST - //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) - if(part.icon_position & (LEFT | RIGHT)) - var/icon/temp2 = new('icons/mob/human.dmi',"blank") - temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) - temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) - if(!(part.icon_position & LEFT)) - temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) - if(!(part.icon_position & RIGHT)) - temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) - base_icon.Blend(temp2, ICON_OVERLAY) - if(part.icon_position & LEFT) - temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) - if(part.icon_position & RIGHT) - temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) - base_icon.Blend(temp2, ICON_UNDERLAY) - else - base_icon.Blend(temp, ICON_OVERLAY) - - if(!skeleton) - if(isgolem(src)) - var/datum/species/golem/G = src.dna.species - if(G.golem_colour) - base_icon.ColorTone(G.golem_colour) - if(husk) - base_icon.ColorTone(husk_color_mod) - else if(hulk) - var/list/tone = ReadRGB(hulk_color_mod) - base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3])) - - //Handle husk overlay. - if(husk && ("overlay_husk" in icon_states(chest.icobase))) - var/icon/mask = new(base_icon) - var/icon/husk_over = new(chest.icobase,"overlay_husk") - mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0) - husk_over.Blend(mask, ICON_ADD) - base_icon.Blend(husk_over, ICON_OVERLAY) - - var/mutable_appearance/new_base = mutable_appearance(base_icon, layer = -LIMBS_LAYER) - human_icon_cache[icon_key] = new_base - standing += new_base - - //END CACHED ICON GENERATION. - - overlays_standing[LIMBS_LAYER] = standing - apply_overlay(LIMBS_LAYER) - - //Underwear - var/icon/underwear_standing = new /icon('icons/mob/underwear.dmi', "nude") - if(underwear && dna.species.clothing_flags & HAS_UNDERWEAR) - var/datum/sprite_accessory/underwear/U = GLOB.underwear_list[underwear] - if(U) - var/u_icon = U.sprite_sheets && (dna.species.name in U.sprite_sheets) ? U.sprite_sheets[dna.species.name] : U.icon //Species-fit the undergarment. - underwear_standing.Blend(new /icon(u_icon, "uw_[U.icon_state]_s"), ICON_OVERLAY) - - if(undershirt && dna.species.clothing_flags & HAS_UNDERSHIRT) - var/datum/sprite_accessory/undershirt/U2 = GLOB.undershirt_list[undershirt] - if(U2) - var/u2_icon = U2.sprite_sheets && (dna.species.name in U2.sprite_sheets) ? U2.sprite_sheets[dna.species.name] : U2.icon - underwear_standing.Blend(new /icon(u2_icon, "us_[U2.icon_state]_s"), ICON_OVERLAY) - - if(socks && dna.species.clothing_flags & HAS_SOCKS) - var/datum/sprite_accessory/socks/U3 = GLOB.socks_list[socks] - if(U3) - var/u3_icon = U3.sprite_sheets && (dna.species.name in U3.sprite_sheets) ? U3.sprite_sheets[dna.species.name] : U3.icon - underwear_standing.Blend(new /icon(u3_icon, "sk_[U3.icon_state]_s"), ICON_OVERLAY) - - if(underwear_standing) - overlays_standing[UNDERWEAR_LAYER] = mutable_appearance(underwear_standing, layer = -UNDERWEAR_LAYER) - apply_overlay(UNDERWEAR_LAYER) - - if(lip_style && (LIPS in dna.species.species_traits)) - var/icon/lips = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "lips_[lip_style]_s") - lips.Blend(lip_color, ICON_ADD) - standing += mutable_appearance(lips, layer = -BODY_LAYER) - - overlays_standing[BODY_LAYER] = standing - apply_overlay(BODY_LAYER) - //tail - update_tail_layer(0) - update_int_organs() - //head accessory - update_head_accessory(0) - //markings - update_markings(0) - //hair - update_hair(0) - update_fhair(0) - - -//MARKINGS OVERLAY -/mob/living/carbon/human/proc/update_markings(var/update_icons=1) - //Reset our markings. - remove_overlay(MARKINGS_LAYER) - - //Base icon. - var/icon/markings_standing = icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s") - - //Body markings. - var/obj/item/organ/external/chest/chest_organ = get_organ("chest") - if(chest_organ && m_styles["body"]) - var/body_marking = m_styles["body"] - var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] - if(body_marking_style && body_marking_style.species_allowed && (dna.species.name in body_marking_style.species_allowed)) - var/icon/b_marking_s = icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") - if(body_marking_style.do_colouration) - b_marking_s.Blend(m_colours["body"], ICON_ADD) - markings_standing.Blend(b_marking_s, ICON_OVERLAY) - //Head markings. - var/obj/item/organ/external/head/head_organ = get_organ("head") - if(head_organ && m_styles["head"]) //If the head is destroyed, forget the head markings. This prevents floating optical markings on decapitated IPCs, for example. - var/head_marking = m_styles["head"] - var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] - if(head_marking_style && head_marking_style.species_allowed && (head_organ.dna.species.name in head_marking_style.species_allowed)) - var/icon/h_marking_s = icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") - if(head_marking_style.do_colouration) - h_marking_s.Blend(m_colours["head"], ICON_ADD) - markings_standing.Blend(h_marking_s, ICON_OVERLAY) - - overlays_standing[MARKINGS_LAYER] = mutable_appearance(markings_standing, layer = -MARKINGS_LAYER) - apply_overlay(MARKINGS_LAYER) - -//HEAD ACCESSORY OVERLAY -/mob/living/carbon/human/proc/update_head_accessory(var/update_icons=1) - //Reset our head accessory - remove_overlay(HEAD_ACCESSORY_LAYER) - remove_overlay(HEAD_ACC_OVER_LAYER) - - var/obj/item/organ/external/head/head_organ = get_organ("head") - if(!head_organ) - return - - //masks and helmets can obscure our head accessory - if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) - return - - //base icons - var/icon/head_accessory_standing = new /icon('icons/mob/body_accessory.dmi',"accessory_none_s") - if(head_organ.ha_style && (head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY)) - var/datum/sprite_accessory/head_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style] - if(head_accessory_style && head_accessory_style.species_allowed) - if(head_organ.dna.species.name in head_accessory_style.species_allowed) - var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") - if(head_accessory_style.do_colouration) - head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD) - head_accessory_standing = head_accessory_s //head_accessory_standing.Blend(head_accessory_s, ICON_OVERLAY) - //Having it this way preserves animations. Useful for animated antennae. - - if(head_accessory_style.over_hair) //Select which layer to use based on the properties of the head accessory style. - overlays_standing[HEAD_ACC_OVER_LAYER] = mutable_appearance(head_accessory_standing, layer = -HEAD_ACC_OVER_LAYER) - apply_overlay(HEAD_ACC_OVER_LAYER) - else - overlays_standing[HEAD_ACCESSORY_LAYER] = mutable_appearance(head_accessory_standing, layer = -HEAD_ACCESSORY_LAYER) - apply_overlay(HEAD_ACCESSORY_LAYER) - else - //warning("Invalid ha_style for [species.name]: [ha_style]") - - - -//HAIR OVERLAY -/mob/living/carbon/human/proc/update_hair(var/update_icons=1) - //Reset our hair - remove_overlay(HAIR_LAYER) - - var/obj/item/organ/external/head/head_organ = get_organ("head") - if(!head_organ) - return - - //masks and helmets can obscure our hair, unless we're a synthetic - if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) - return - - //base icons - var/icon/hair_standing = new /icon('icons/mob/human_face.dmi',"bald_s") - if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(isSynthetic()))) - var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] - if(hair_style && hair_style.species_allowed) - if((head_organ.dna.species.name in hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics... - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - if(istype(head_organ.dna.species, /datum/species/slime)) // I am el worstos - hair_s.Blend("[skin_colour]A0", ICON_AND) - else if(hair_style.do_colouration) - hair_s.Blend(head_organ.hair_colour, ICON_ADD) - - if(hair_style.secondary_theme) - var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") - if(!hair_style.no_sec_colour) - hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) - hair_s.Blend(hair_secondary_s, ICON_OVERLAY) - - hair_standing = hair_s //hair_standing.Blend(hair_s, ICON_OVERLAY) - //Having it this way preserves animations. Useful for IPC screens. - else - //warning("Invalid h_style for [species.name]: [h_style]") - //hair_standing.Blend(debrained_s, ICON_OVERLAY)//how does i overlay for fish? - - overlays_standing[HAIR_LAYER] = mutable_appearance(hair_standing, layer = -HAIR_LAYER) - apply_overlay(HAIR_LAYER) - - -//FACIAL HAIR OVERLAY -/mob/living/carbon/human/proc/update_fhair(var/update_icons=1) - //Reset our facial hair - remove_overlay(FHAIR_LAYER) - remove_overlay(FHAIR_OVER_LAYER) - - var/obj/item/organ/external/head/head_organ = get_organ("head") - if(!head_organ) - return - - //masks and helmets can obscure our facial hair, unless we're a synthetic - if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) - return - - //base icons - var/icon/face_standing = new /icon('icons/mob/human_face.dmi',"bald_s") - if(head_organ.f_style) - var/datum/sprite_accessory/facial_hair/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] - if(facial_hair_style && facial_hair_style.species_allowed) - if((head_organ.dna.species.name in facial_hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics... - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(istype(head_organ.dna.species, /datum/species/slime)) // I am el worstos - facial_s.Blend("[skin_colour]A0", ICON_AND) - else if(facial_hair_style.do_colouration) - facial_s.Blend(head_organ.facial_colour, ICON_ADD) - - if(facial_hair_style.secondary_theme) - var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") - if(!facial_hair_style.no_sec_colour) - facial_secondary_s.Blend(head_organ.sec_facial_colour, ICON_ADD) - facial_s.Blend(facial_secondary_s, ICON_OVERLAY) - - face_standing.Blend(facial_s, ICON_OVERLAY) - - if(facial_hair_style.over_hair) //Select which layer to use based on the properties of the facial hair style. - overlays_standing[FHAIR_OVER_LAYER] = mutable_appearance(face_standing, layer = -FHAIR_OVER_LAYER) - apply_overlay(FHAIR_OVER_LAYER) - else - overlays_standing[FHAIR_LAYER] = mutable_appearance(face_standing, layer = -FHAIR_LAYER) - apply_overlay(FHAIR_LAYER) - else - //warning("Invalid f_style for [species.name]: [f_style]") - - - -/mob/living/carbon/human/update_mutations(var/update_icons=1) - remove_overlay(MUTATIONS_LAYER) - var/fat - if(FAT in mutations) - fat = "fat" - - var/mutable_appearance/standing = mutable_appearance('icons/effects/genetics.dmi', layer = -MUTATIONS_LAYER) - var/add_image = 0 - var/g = "m" - if(gender == FEMALE) - g = "f" - // DNA2 - Drawing underlays. - for(var/datum/dna/gene/gene in dna_genes) - if(!gene.block) - continue - if(gene.is_active(src)) - var/underlay = gene.OnDrawUnderlays(src, g, fat) - if(underlay) - standing.underlays += underlay - add_image = 1 - for(var/mut in mutations) - switch(mut) - if(LASER) - standing.overlays += "lasereyes_s" - add_image = 1 - if((COLDRES in mutations) && (HEATRES in mutations)) - standing.underlays -= "cold[fat]_s" - standing.underlays -= "fire[fat]_s" - standing.underlays += "coldfire[fat]_s" - - if(add_image) - overlays_standing[MUTATIONS_LAYER] = standing - apply_overlay(MUTATIONS_LAYER) - - -/mob/living/carbon/human/proc/update_mutantrace(var/update_icons=1) -//BS12 EDIT - var/skel = (SKELETON in mutations) - if(skel) - skeleton = 'icons/mob/human_races/r_skeleton.dmi' - else - skeleton = null - - update_hair(0) - update_fhair(0) - - -/mob/living/carbon/human/update_fire() - remove_overlay(FIRE_LAYER) - if(on_fire) - if(!overlays_standing[FIRE_LAYER]) - overlays_standing[FIRE_LAYER] = mutable_appearance(fire_dmi, fire_sprite, layer = -FIRE_LAYER) - apply_overlay(FIRE_LAYER) - -/* --------------------------------------- */ -//For legacy support. -/mob/living/carbon/human/regenerate_icons() - ..() - if(notransform) return - update_mutations(0) - update_body(0, 1) //Update the body and force limb icon regeneration. - update_hair(0) - update_head_accessory(0) - update_fhair(0) - update_mutantrace(0) - update_inv_w_uniform(0,0) - update_inv_wear_id(0) - update_inv_gloves(0,0) - update_inv_glasses(0) - update_inv_ears(0) - update_inv_shoes(0,0) - update_inv_s_store(0) - update_inv_wear_mask(0) - update_inv_head(0,0) - update_inv_belt(0) - update_inv_back(0) - update_inv_wear_suit(0) - update_inv_r_hand(0) - update_inv_l_hand(0) - update_inv_handcuffed(0) - update_inv_legcuffed(0) - update_inv_pockets(0) - update_inv_wear_pda(0) - UpdateDamageIcon(0) - force_update_limbs() - update_tail_layer(0) - overlays.Cut() // Force all overlays to regenerate - update_fire() - update_icons() -/* --------------------------------------- */ -//vvvvvv UPDATE_INV PROCS vvvvvv - -/mob/living/carbon/human/update_inv_w_uniform(var/update_icons=1) - remove_overlay(UNIFORM_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_w_uniform] - if(inv) - inv.update_icon() - - if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - w_uniform.screen_loc = ui_iclothing //...draw the item in the inventory screen - client.screen += w_uniform //Either way, add the item to the HUD - - var/t_color = w_uniform.item_color - if(!t_color) - t_color = icon_state - - var/mutable_appearance/standing = mutable_appearance('icons/mob/uniform.dmi', "[t_color]_s", layer = -UNIFORM_LAYER) - if(FAT in mutations) - if(w_uniform.flags_size & ONESIZEFITSALL) - standing.icon = 'icons/mob/uniform_fat.dmi' - else - to_chat(src, "You burst out of \the [w_uniform]!") - unEquip(w_uniform) - return - - if(w_uniform.icon_override) - standing.icon = w_uniform.icon_override - else if(w_uniform.sprite_sheets && w_uniform.sprite_sheets[dna.species.name]) - standing.icon = w_uniform.sprite_sheets[dna.species.name] - - if(w_uniform.blood_DNA) - var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "uniformblood") - bloodsies.color = w_uniform.blood_color - standing.overlays += bloodsies - - if(w_uniform:accessories.len) //WE CHECKED THE TYPE ABOVE. THIS REALLY SHOULD BE FINE. // oh my god kys whoever made this if statement jfc :gun: - for(var/obj/item/clothing/accessory/A in w_uniform:accessories) - var/tie_color = A.item_color - if(!tie_color) - tie_color = A.icon_state - if(A.icon_override) - standing.overlays += image("icon" = A.icon_override, "icon_state" = "[A.icon_state]") - else if(A.sprite_sheets && A.sprite_sheets[dna.species.name]) - standing.overlays += image("icon" = A.sprite_sheets[dna.species.name], "icon_state" = "[A.icon_state]") - else - standing.overlays += image("icon" = 'icons/mob/ties.dmi', "icon_state" = "[tie_color]") - standing.alpha = w_uniform.alpha - standing.color = w_uniform.color - overlays_standing[UNIFORM_LAYER] = standing - else - // Automatically drop anything in store / id / belt if you're not wearing a uniform. //CHECK IF NECESARRY - for(var/obj/item/thing in list(r_store, l_store, wear_id, wear_pda, belt)) // whoever made this - if(thing) // you're a piece of fucking garbage - unEquip(thing) // why the fuck would you goddamn do this motherfucking shit - if(client) // INVENTORY CODE IN FUCKING ICON CODE - client.screen -= thing // WHAT THE FUCKING FUCK BAY GODDAMNIT - // **I FUCKING HATE YOU AAAAAAAAAA** - if(thing) // - thing.forceMove(drop_location()) // - thing.dropped(src) // - thing.layer = initial(thing.layer) - thing.plane = initial(thing.plane) - apply_overlay(UNIFORM_LAYER) - -/mob/living/carbon/human/update_inv_wear_id(var/update_icons=1) - remove_overlay(ID_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_id] - if(inv) - inv.update_icon() - - if(wear_id) - if(client && hud_used && hud_used.hud_shown) - wear_id.screen_loc = ui_id - client.screen += wear_id - - if(w_uniform && w_uniform:displays_id) - overlays_standing[ID_LAYER] = mutable_appearance('icons/mob/mob.dmi', "id", layer = -ID_LAYER) - apply_overlay(ID_LAYER) - -/mob/living/carbon/human/update_inv_gloves(var/update_icons=1) - remove_overlay(GLOVES_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_gloves] - if(inv) - inv.update_icon() - - if(gloves) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - gloves.screen_loc = ui_gloves //...draw the item in the inventory screen - client.screen += gloves //Either way, add the item to the HUD - - var/t_state = gloves.item_state - if(!t_state) t_state = gloves.icon_state - - var/mutable_appearance/standing - if(gloves.icon_override) - standing = mutable_appearance(gloves.icon_override, "[t_state]", layer = -GLOVES_LAYER) - else if(gloves.sprite_sheets && gloves.sprite_sheets[dna.species.name]) - standing = mutable_appearance(gloves.sprite_sheets[dna.species.name], "[t_state]", layer = -GLOVES_LAYER) - else - standing = mutable_appearance('icons/mob/hands.dmi', "[t_state]", layer = -GLOVES_LAYER) - - if(gloves.blood_DNA) - var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "bloodyhands") - bloodsies.color = gloves.blood_color - standing.overlays += bloodsies - overlays_standing[GLOVES_LAYER] = standing - else - if(blood_DNA) - var/mutable_appearance/bloodsies = mutable_appearance(dna.species.blood_mask, "bloodyhands", layer = -GLOVES_LAYER) - bloodsies.color = hand_blood_color - overlays_standing[GLOVES_LAYER] = bloodsies - apply_overlay(GLOVES_LAYER) - - -/mob/living/carbon/human/update_inv_glasses(var/update_icons=1) - remove_overlay(GLASSES_LAYER) - remove_overlay(GLASSES_OVER_LAYER) - remove_overlay(OVER_MASK_LAYER) - - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_glasses] - if(inv) - inv.update_icon() - - if(glasses) - var/mutable_appearance/new_glasses - var/obj/item/organ/external/head/head_organ = get_organ("head") - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - glasses.screen_loc = ui_glasses //...draw the item in the inventory screen - client.screen += glasses //Either way, add the item to the HUD - - if(glasses.icon_override) - new_glasses = mutable_appearance(glasses.icon_override, "[glasses.icon_state]", layer = -GLASSES_LAYER) - else if(glasses.sprite_sheets && glasses.sprite_sheets[head_organ.dna.species.name]) - new_glasses = mutable_appearance(glasses.sprite_sheets[head_organ.dna.species.name], "[glasses.icon_state]", layer = -GLASSES_LAYER) - else - new_glasses = mutable_appearance('icons/mob/eyes.dmi', "[glasses.icon_state]", layer = -GLASSES_LAYER) - - var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] - var/obj/item/clothing/glasses/G = glasses - if(istype(G) && G.over_mask) //If the user's used the 'wear over mask' verb on the glasses. - new_glasses.layer = -OVER_MASK_LAYER - overlays_standing[OVER_MASK_LAYER] = new_glasses - apply_overlay(OVER_MASK_LAYER) - else if(hair_style && hair_style.glasses_over) //Select which layer to use based on the properties of the hair style. Hair styles with hair that don't overhang the arms of the glasses should have glasses_over set to a positive value. - new_glasses.layer = -GLASSES_OVER_LAYER - overlays_standing[GLASSES_OVER_LAYER] = new_glasses - apply_overlay(GLASSES_OVER_LAYER) - else - overlays_standing[GLASSES_LAYER] = new_glasses - apply_overlay(GLASSES_LAYER) - - update_misc_effects() - -/mob/living/carbon/human/update_inv_ears(var/update_icons=1) - remove_overlay(EARS_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_l_ear] - if(inv) - inv.update_icon() - - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_r_ear] - if(inv) - inv.update_icon() - - if(l_ear || r_ear) - if(l_ear) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - l_ear.screen_loc = ui_l_ear //...draw the item in the inventory screen - client.screen += l_ear //Either way, add the item to the HUD - - var/t_type = l_ear.item_state - if(!t_type) - t_type = l_ear.icon_state - if(l_ear.icon_override) - t_type = "[t_type]_l" - overlays_standing[EARS_LAYER] = mutable_appearance(l_ear.icon_override, "[t_type]", layer = -EARS_LAYER) - else if(l_ear.sprite_sheets && l_ear.sprite_sheets[dna.species.name]) - overlays_standing[EARS_LAYER] = mutable_appearance(l_ear.sprite_sheets[dna.species.name], "[t_type]", layer = -EARS_LAYER) - else - overlays_standing[EARS_LAYER] = mutable_appearance('icons/mob/ears.dmi', "[t_type]", layer = -EARS_LAYER) - - if(r_ear) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - r_ear.screen_loc = ui_r_ear //...draw the item in the inventory screen - client.screen += r_ear //Either way, add the item to the HUD - - var/t_type = r_ear.item_state - if(!t_type) - t_type = r_ear.icon_state - if(r_ear.icon_override) - t_type = "[t_type]_r" - overlays_standing[EARS_LAYER] = mutable_appearance(r_ear.icon_override, "[t_type]", layer = -EARS_LAYER) - else if(r_ear.sprite_sheets && r_ear.sprite_sheets[dna.species.name]) - overlays_standing[EARS_LAYER] = mutable_appearance(r_ear.sprite_sheets[dna.species.name], "[t_type]", layer = -EARS_LAYER) - else - overlays_standing[EARS_LAYER] = mutable_appearance('icons/mob/ears.dmi', "[t_type]", layer = -EARS_LAYER) - apply_overlay(EARS_LAYER) - -/mob/living/carbon/human/update_inv_shoes(var/update_icons=1) - remove_overlay(SHOES_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_shoes] - if(inv) - inv.update_icon() - - if(shoes) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - shoes.screen_loc = ui_shoes //...draw the item in the inventory screen - client.screen += shoes //Either way, add the item to the HUD - - var/mutable_appearance/standing - if(shoes.icon_override) - standing = mutable_appearance(shoes.icon_override, "[shoes.icon_state]", layer = -SHOES_LAYER) - else if(shoes.sprite_sheets && shoes.sprite_sheets[dna.species.name]) - standing = mutable_appearance(shoes.sprite_sheets[dna.species.name], "[shoes.icon_state]", layer = -SHOES_LAYER) - else - standing = mutable_appearance('icons/mob/feet.dmi', "[shoes.icon_state]", layer = -SHOES_LAYER) - - - if(shoes.blood_DNA) - var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "shoeblood") - bloodsies.color = shoes.blood_color - standing.overlays += bloodsies - standing.alpha = shoes.alpha - standing.color = shoes.color - overlays_standing[SHOES_LAYER] = standing - else - if(feet_blood_DNA) - var/mutable_appearance/bloodsies = mutable_appearance(dna.species.blood_mask, "shoeblood", layer = -SHOES_LAYER) - bloodsies.color = feet_blood_color - overlays_standing[SHOES_LAYER] = bloodsies - apply_overlay(SHOES_LAYER) - -/mob/living/carbon/human/update_inv_s_store(var/update_icons=1) - remove_overlay(SUIT_STORE_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_s_store] - if(inv) - inv.update_icon() - - if(s_store) - if(client && hud_used && hud_used.hud_shown) - s_store.screen_loc = ui_sstore1 - client.screen += s_store - - var/t_state = s_store.item_state - if(!t_state) - t_state = s_store.icon_state - var/dmi='icons/mob/belt_mirror.dmi' - overlays_standing[SUIT_STORE_LAYER] = mutable_appearance(dmi, "[t_state]", layer = -SUIT_STORE_LAYER) - s_store.screen_loc = ui_sstore1 //TODO - apply_overlay(SUIT_STORE_LAYER) - - -/mob/living/carbon/human/update_inv_head(var/update_icons=1) - ..() - remove_overlay(HEAD_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_head] - if(inv) - inv.update_icon() - - if(head) - var/mutable_appearance/standing - if(head.icon_override) - standing = mutable_appearance(head.icon_override, "[head.icon_state]", layer = -HEAD_LAYER) - else if(head.sprite_sheets && head.sprite_sheets[dna.species.name]) - standing = mutable_appearance(head.sprite_sheets[dna.species.name], "[head.icon_state]", layer = -HEAD_LAYER) - else - standing = mutable_appearance('icons/mob/head.dmi', "[head.icon_state]", layer = -HEAD_LAYER) - - if(head.blood_DNA) - var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "helmetblood") - bloodsies.color = head.blood_color - standing.overlays += bloodsies - standing.alpha = head.alpha - standing.color = head.color - overlays_standing[HEAD_LAYER] = standing - apply_overlay(HEAD_LAYER) - -/mob/living/carbon/human/update_inv_belt(var/update_icons=1) - remove_overlay(BELT_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_belt] - if(inv) - inv.update_icon() - - if(hud_used.hud_shown && belt) - client.screen += belt - belt.screen_loc = ui_belt - - if(belt) - var/t_state = belt.item_state - if(!t_state) - t_state = belt.icon_state - - if(belt.icon_override) - t_state = "[t_state]_be" - overlays_standing[BELT_LAYER] = mutable_appearance(belt.icon_override, "[t_state]", layer = -BELT_LAYER) - else if(belt.sprite_sheets && belt.sprite_sheets[dna.species.name]) - overlays_standing[BELT_LAYER] = mutable_appearance(belt.sprite_sheets[dna.species.name], "[t_state]", layer = -BELT_LAYER) - else - overlays_standing[BELT_LAYER] = mutable_appearance('icons/mob/belt.dmi', "[t_state]", layer = -BELT_LAYER) - apply_overlay(BELT_LAYER) - - -/mob/living/carbon/human/update_inv_wear_suit(var/update_icons=1) - remove_overlay(SUIT_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_suit] - if(inv) - inv.update_icon() - - if(wear_suit && istype(wear_suit, /obj/item/clothing/suit)) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) //if the inventory is open ... - wear_suit.screen_loc = ui_oclothing //TODO //...draw the item in the inventory screen - client.screen += wear_suit //Either way, add the item to the HUD - - var/mutable_appearance/standing - if(wear_suit.icon_override) - standing = mutable_appearance(wear_suit.icon_override, "[wear_suit.icon_state]", layer = -SUIT_LAYER) - else if(wear_suit.sprite_sheets && wear_suit.sprite_sheets[dna.species.name]) - standing = mutable_appearance(wear_suit.sprite_sheets[dna.species.name], "[wear_suit.icon_state]", layer = -SUIT_LAYER) - else if(FAT in mutations) - if(wear_suit.flags_size & ONESIZEFITSALL) - standing = mutable_appearance('icons/mob/suit_fat.dmi', "[wear_suit.icon_state]", layer = -SUIT_LAYER) - else - to_chat(src, "You burst out of \the [wear_suit]!") - unEquip(wear_suit) - return - else - standing = mutable_appearance('icons/mob/suit.dmi', "[wear_suit.icon_state]", layer = -SUIT_LAYER) - - if(wear_suit.breakouttime) - drop_l_hand() - drop_r_hand() - - if(wear_suit.blood_DNA) - var/obj/item/clothing/suit/S = wear_suit - var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "[S.blood_overlay_type]blood") - bloodsies.color = wear_suit.blood_color - standing.overlays += bloodsies - - - var/special_overlays = wear_suit.special_overlays() - if(special_overlays) - standing.overlays += special_overlays - - standing.alpha = wear_suit.alpha - standing.color = wear_suit.color - overlays_standing[SUIT_LAYER] = standing - - apply_overlay(SUIT_LAYER) - update_tail_layer(0) - update_collar(0) - -/mob/living/carbon/human/update_inv_pockets() - if(client && hud_used) - var/obj/screen/inventory/inv - - inv = hud_used.inv_slots[slot_l_store] - if(inv) - inv.update_icon() - - inv = hud_used.inv_slots[slot_r_store] - if(inv) - inv.update_icon() - - if(hud_used.hud_shown) - if(l_store) - client.screen += l_store - l_store.screen_loc = ui_storage1 - - if(r_store) - client.screen += r_store - r_store.screen_loc = ui_storage2 - -/mob/living/carbon/human/update_inv_wear_pda() - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_pda] - if(inv) - inv.update_icon() - - if(wear_pda) - client.screen += wear_pda - wear_pda.screen_loc = ui_pda - -/mob/living/carbon/human/update_inv_wear_mask(var/update_icons = 1) - ..() - remove_overlay(FACEMASK_LAYER) - if(client && hud_used) - var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_mask] - if(inv) - inv.update_icon() - if(wear_mask && (istype(wear_mask, /obj/item/clothing/mask) || istype(wear_mask, /obj/item/clothing/accessory))) - if(!(slot_wear_mask in check_obscured_slots())) - var/obj/item/organ/external/head/head_organ = get_organ("head") - var/datum/sprite_accessory/alt_heads/alternate_head - if(head_organ.alt_head && head_organ.alt_head != "None") - alternate_head = GLOB.alt_heads_list[head_organ.alt_head] - - var/mutable_appearance/standing - var/icon/mask_icon = new(wear_mask.icon) - if(wear_mask.icon_override) - mask_icon = new(wear_mask.icon_override) - standing = mutable_appearance(wear_mask.icon_override, "[wear_mask.icon_state][(alternate_head && ("[wear_mask.icon_state]_[alternate_head.suffix]" in mask_icon.IconStates())) ? "_[alternate_head.suffix]" : ""]", layer = -FACEMASK_LAYER) - else if(wear_mask.sprite_sheets && wear_mask.sprite_sheets[dna.species.name]) - mask_icon = new(wear_mask.sprite_sheets[dna.species.name]) - standing = mutable_appearance(wear_mask.sprite_sheets[dna.species.name], "[wear_mask.icon_state][(alternate_head && ("[wear_mask.icon_state]_[alternate_head.suffix]" in mask_icon.IconStates())) ? "_[alternate_head.suffix]" : ""]", layer = -FACEMASK_LAYER) - else - standing = mutable_appearance('icons/mob/mask.dmi', "[wear_mask.icon_state][(alternate_head && ("[wear_mask.icon_state]_[alternate_head.suffix]" in mask_icon.IconStates())) ? "_[alternate_head.suffix]" : ""]", layer = -FACEMASK_LAYER) - - if(!istype(wear_mask, /obj/item/clothing/mask/cigarette) && wear_mask.blood_DNA) - var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "maskblood") - bloodsies.color = wear_mask.blood_color - standing.overlays += bloodsies - - standing.alpha = wear_mask.alpha - standing.color = wear_mask.color - overlays_standing[FACEMASK_LAYER] = standing - apply_overlay(FACEMASK_LAYER) - - -/mob/living/carbon/human/update_inv_back(var/update_icons=1) - ..() - remove_overlay(BACK_LAYER) - if(back) - //determine the icon to use - var/mutable_appearance/standing - if(back.icon_override) - standing = mutable_appearance(back.icon_override, "[back.icon_state]", layer = -BACK_LAYER) - else if(istype(back, /obj/item/rig)) - //If this is a rig and a mob_icon is set, it will take species into account in the rig update_icon() proc. - var/obj/item/rig/rig = back - standing = rig.mob_icon - else if(back.sprite_sheets && back.sprite_sheets[dna.species.name]) - standing = mutable_appearance(back.sprite_sheets[dna.species.name], "[back.icon_state]", layer = -BACK_LAYER) - else - standing = mutable_appearance('icons/mob/back.dmi', "[back.icon_state]", layer = -BACK_LAYER) - - //create the image - standing.alpha = back.alpha - standing.color = back.color - overlays_standing[BACK_LAYER] = standing - apply_overlay(BACK_LAYER) - -/mob/living/carbon/human/update_inv_handcuffed(var/update_icons=1) - remove_overlay(HANDCUFF_LAYER) - if(handcuffed) - if(istype(handcuffed, /obj/item/restraints/handcuffs/pinkcuffs)) - overlays_standing[HANDCUFF_LAYER] = mutable_appearance('icons/mob/mob.dmi', "pinkcuff1", layer = -HANDCUFF_LAYER) - else - overlays_standing[HANDCUFF_LAYER] = mutable_appearance('icons/mob/mob.dmi', "handcuff1", layer = -HANDCUFF_LAYER) - apply_overlay(HANDCUFF_LAYER) - -/mob/living/carbon/human/update_inv_legcuffed(var/update_icons=1) - remove_overlay(LEGCUFF_LAYER) - clear_alert("legcuffed") - if(legcuffed) - overlays_standing[LEGCUFF_LAYER] = mutable_appearance('icons/mob/mob.dmi', "legcuff1", layer = -LEGCUFF_LAYER) - throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) - if(m_intent != MOVE_INTENT_WALK) - m_intent = MOVE_INTENT_WALK - if(hud_used && hud_used.move_intent) - hud_used.move_intent.icon_state = "walking" - apply_overlay(LEGCUFF_LAYER) - - -/mob/living/carbon/human/update_inv_r_hand(var/update_icons=1) - ..() - remove_overlay(R_HAND_LAYER) - if(r_hand) - var/t_state = r_hand.item_state - if(!t_state) - t_state = r_hand.icon_state - - var/mutable_appearance/standing - if(r_hand.sprite_sheets_inhand && r_hand.sprite_sheets_inhand[dna.species.name]) - t_state = "[t_state]_r" - standing = mutable_appearance(r_hand.sprite_sheets_inhand[dna.species.name], "[t_state]", layer = -R_HAND_LAYER) - else - standing = mutable_appearance(r_hand.righthand_file, "[t_state]", layer = -R_HAND_LAYER) - standing = center_image(standing, r_hand.inhand_x_dimension, r_hand.inhand_y_dimension) - overlays_standing[R_HAND_LAYER] = standing - apply_overlay(R_HAND_LAYER) - - -/mob/living/carbon/human/update_inv_l_hand(var/update_icons=1) - ..() - remove_overlay(L_HAND_LAYER) - if(l_hand) - var/t_state = l_hand.item_state - if(!t_state) - t_state = l_hand.icon_state - - var/mutable_appearance/standing - if(l_hand.sprite_sheets_inhand && l_hand.sprite_sheets_inhand[dna.species.name]) - t_state = "[t_state]_l" - standing = mutable_appearance(l_hand.sprite_sheets_inhand[dna.species.name], "[t_state]", layer = -L_HAND_LAYER) - else - standing = mutable_appearance(l_hand.lefthand_file, "[t_state]", layer = -L_HAND_LAYER) - standing = center_image(standing, l_hand.inhand_x_dimension, l_hand.inhand_y_dimension) - overlays_standing[L_HAND_LAYER] = standing - apply_overlay(L_HAND_LAYER) - -//human HUD updates for items in our inventory - -//update whether our head item appears on our hud. -/mob/living/carbon/human/update_hud_head(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) - I.screen_loc = ui_head - client.screen += I - -//update whether our mask item appears on our hud. -/mob/living/carbon/human/update_hud_wear_mask(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - if(hud_used.inventory_shown) - I.screen_loc = ui_mask - client.screen += I - -//update whether our back item appears on our hud. -/mob/living/carbon/human/update_hud_back(obj/item/I) - if(client && hud_used && hud_used.hud_shown) - I.screen_loc = ui_back - client.screen += I - - -/mob/living/carbon/human/proc/update_tail_layer(var/update_icons=1) - remove_overlay(TAIL_UNDERLIMBS_LAYER) // SEW direction icons, overlayed by LIMBS_LAYER. - remove_overlay(TAIL_LAYER) /* This will be one of two things: - If the species' tail is overlapped by limbs, this will be only the N direction icon so tails - can still appear on the outside of uniforms and such. - Otherwise, since the user's tail isn't overlapped by limbs, it will be a full icon with all directions. */ - - var/icon/tail_marking_icon - var/datum/sprite_accessory/body_markings/tail/tail_marking_style - if(m_styles["tail"] != "None" && (dna.species.bodyflags & HAS_TAIL_MARKINGS)) - var/tail_marking = m_styles["tail"] - tail_marking_style = GLOB.marking_styles_list[tail_marking] - tail_marking_icon = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") - tail_marking_icon.Blend(m_colours["tail"], ICON_ADD) - - if(body_accessory) - if(body_accessory.try_restrictions(src)) - var/icon/accessory_s = new/icon("icon" = body_accessory.icon, "icon_state" = body_accessory.icon_state) - if(dna.species.bodyflags & HAS_SKIN_COLOR) - accessory_s.Blend(skin_colour, body_accessory.blend_mode) - if(tail_marking_icon && (body_accessory.name in tail_marking_style.tails_allowed)) - accessory_s.Blend(tail_marking_icon, ICON_OVERLAY) - if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) - // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. - var/icon/under = new/icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s") - under.Insert(new/icon(accessory_s, dir=SOUTH), dir=SOUTH) - under.Insert(new/icon(accessory_s, dir=EAST), dir=EAST) - under.Insert(new/icon(accessory_s, dir=WEST), dir=WEST) - - var/mutable_appearance/underlimbs = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) - underlimbs.pixel_x = body_accessory.pixel_x_offset - underlimbs.pixel_y = body_accessory.pixel_y_offset - overlays_standing[TAIL_UNDERLIMBS_LAYER] = underlimbs - - // Creates a blank icon, and copies accessory_s' north direction sprite into it - // before passing that to the tail layer that overlays uniforms and such. - var/icon/over = new/icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s") - over.Insert(new/icon(accessory_s, dir=NORTH), dir=NORTH) - - var/mutable_appearance/tail = mutable_appearance(over, layer = -TAIL_LAYER) - tail.pixel_x = body_accessory.pixel_x_offset - tail.pixel_y = body_accessory.pixel_y_offset - overlays_standing[TAIL_LAYER] = tail - else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. - var/mutable_appearance/tail = mutable_appearance(accessory_s, layer = -TAIL_LAYER) - tail.pixel_x = body_accessory.pixel_x_offset - tail.pixel_y = body_accessory.pixel_y_offset - overlays_standing[TAIL_LAYER] = tail - - else if(tail && dna.species.bodyflags & HAS_TAIL) //no tailless tajaran - if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL)) - var/icon/tail_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[tail]_s") - if(dna.species.bodyflags & HAS_SKIN_COLOR) - tail_s.Blend(skin_colour, ICON_ADD) - if(tail_marking_icon && !tail_marking_style.tails_allowed) - tail_s.Blend(tail_marking_icon, ICON_OVERLAY) - if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) - // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. - var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "blank") - under.Insert(new/icon(tail_s, dir=SOUTH), dir=SOUTH) - under.Insert(new/icon(tail_s, dir=EAST), dir=EAST) - under.Insert(new/icon(tail_s, dir=WEST), dir=WEST) - - overlays_standing[TAIL_UNDERLIMBS_LAYER] = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) - - // Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such. - var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "blank") - over.Insert(new/icon(tail_s, dir=NORTH), dir=NORTH) - - overlays_standing[TAIL_LAYER] = mutable_appearance(over, layer = -TAIL_LAYER) - else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. - overlays_standing[TAIL_LAYER] = mutable_appearance(tail_s, layer = -TAIL_LAYER) - apply_overlay(TAIL_LAYER) - apply_overlay(TAIL_UNDERLIMBS_LAYER) - -/mob/living/carbon/human/proc/start_tail_wagging(var/update_icons=1) - remove_overlay(TAIL_UNDERLIMBS_LAYER) // SEW direction icons, overlayed by LIMBS_LAYER. - remove_overlay(TAIL_LAYER) /* This will be one of two things: - If the species' tail is overlapped by limbs, this will be only the N direction icon so tails - can still appear on the outside of uniforms and such. - Otherwise, since the user's tail isn't overlapped by limbs, it will be a full icon with all directions. */ - - var/icon/tail_marking_icon - var/datum/sprite_accessory/body_markings/tail/tail_marking_style - if(m_styles["tail"] != "None" && (dna.species.bodyflags & HAS_TAIL_MARKINGS)) - var/tail_marking = m_styles["tail"] - tail_marking_style = GLOB.marking_styles_list[tail_marking] - tail_marking_icon = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]w_s") - tail_marking_icon.Blend(m_colours["tail"], ICON_ADD) - - if(body_accessory) - var/icon/accessory_s = new/icon("icon" = body_accessory.get_animated_icon(), "icon_state" = body_accessory.get_animated_icon_state()) - if(dna.species.bodyflags & HAS_SKIN_COLOR) - accessory_s.Blend(skin_colour, body_accessory.blend_mode) - if(tail_marking_icon && (body_accessory.name in tail_marking_style.tails_allowed)) - accessory_s.Blend(tail_marking_icon, ICON_OVERLAY) - if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) - // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. - var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "Vulpkanin_tail_delay") - if(body_accessory.allowed_species && (dna.species.name in body_accessory.allowed_species)) - under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") - under.Insert(new/icon(accessory_s, dir=SOUTH), dir=SOUTH) - under.Insert(new/icon(accessory_s, dir=EAST), dir=EAST) - under.Insert(new/icon(accessory_s, dir=WEST), dir=WEST) - - var/mutable_appearance/underlimbs = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) - underlimbs.pixel_x = body_accessory.pixel_x_offset - underlimbs.pixel_y = body_accessory.pixel_y_offset - overlays_standing[TAIL_UNDERLIMBS_LAYER] = underlimbs - - // Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such. - var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "Vulpkanin_tail_delay") - if(body_accessory.allowed_species && (dna.species.name in body_accessory.allowed_species)) // If the user's species is in the list of allowed species for the currently selected body accessory, use the appropriate animation timing blank - over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") - over.Insert(new/icon(accessory_s, dir=NORTH), dir=NORTH) - - var/mutable_appearance/tail = mutable_appearance(over, layer = -TAIL_LAYER) - tail.pixel_x = body_accessory.pixel_x_offset - tail.pixel_y = body_accessory.pixel_y_offset - overlays_standing[TAIL_LAYER] = tail - else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. - var/mutable_appearance/tail = mutable_appearance(accessory_s, layer = -TAIL_LAYER) - tail.pixel_x = body_accessory.pixel_x_offset - tail.pixel_y = body_accessory.pixel_y_offset - overlays_standing[TAIL_LAYER] = tail - - else if(tail && dna.species.bodyflags & HAS_TAIL) - var/icon/tailw_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[tail]w_s") - if(dna.species.bodyflags & HAS_SKIN_COLOR) - tailw_s.Blend(skin_colour, ICON_ADD) - if(tail_marking_icon && !tail_marking_style.tails_allowed) - tailw_s.Blend(tail_marking_icon, ICON_OVERLAY) - if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) - // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. - var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") - under.Insert(new/icon(tailw_s, dir=SOUTH), dir=SOUTH) - under.Insert(new/icon(tailw_s, dir=EAST), dir=EAST) - under.Insert(new/icon(tailw_s, dir=WEST), dir=WEST) - - overlays_standing[TAIL_UNDERLIMBS_LAYER] = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) - - // Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such. - var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") - over.Insert(new/icon(tailw_s, dir=NORTH), dir=NORTH) - - overlays_standing[TAIL_LAYER] = mutable_appearance(over, layer = -TAIL_LAYER) - else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. - overlays_standing[TAIL_LAYER] = mutable_appearance(tailw_s, layer = -TAIL_LAYER) - apply_overlay(TAIL_LAYER) - apply_overlay(TAIL_UNDERLIMBS_LAYER) - -/mob/living/carbon/human/proc/stop_tail_wagging(var/update_icons=1) - remove_overlay(TAIL_UNDERLIMBS_LAYER) - remove_overlay(TAIL_LAYER) - update_tail_layer(update_icons) //just trigger a full update for normal stationary sprites - -/mob/living/carbon/human/proc/update_int_organs() - remove_overlay(INTORGAN_LAYER) - - var/list/standing = list() - for(var/organ in internal_organs) - var/obj/item/organ/internal/I = organ - var/render = I.render() - if(render) - standing += render - - overlays_standing[INTORGAN_LAYER] = standing - apply_overlay(INTORGAN_LAYER) - -/mob/living/carbon/human/handle_transform_change() - ..() - update_tail_layer() - -//Adds a collar overlay above the helmet layer if the suit has one -// Suit needs an identically named sprite in icons/mob/collar.dmi -// For suits with sprite_sheets, an identically named sprite needs to exist in a file like this icons/mob/species/[species_name_here]/collar.dmi. -/mob/living/carbon/human/proc/update_collar(var/update_icons=1) - remove_overlay(COLLAR_LAYER) - var/icon/C = new('icons/mob/collar.dmi') - var/mutable_appearance/standing = null - - if(wear_suit) - if(wear_suit.icon_override) - var/icon_path = "[wear_suit.icon_override]" - icon_path = "[copytext(icon_path, 1, findtext(icon_path, "/suit.dmi"))]/collar.dmi" //If this file doesn't exist, the end result is that COLLAR_LAYER will be unchanged (empty). - if(fexists(icon_path)) //Just ensuring the nonexistance of a file with the above path won't cause a runtime. - var/icon/icon_file = new(icon_path) - if(wear_suit.icon_state in icon_file.IconStates()) - standing = mutable_appearance(icon_file, "[wear_suit.icon_state]", layer = -COLLAR_LAYER) - else if(wear_suit.sprite_sheets && wear_suit.sprite_sheets[dna.species.name]) - var/icon_path = "[wear_suit.sprite_sheets[dna.species.name]]" - icon_path = "[copytext(icon_path, 1, findtext(icon_path, "/suit.dmi"))]/collar.dmi" //If this file doesn't exist, the end result is that COLLAR_LAYER will be unchanged (empty). - if(fexists(icon_path)) //Just ensuring the nonexistance of a file with the above path won't cause a runtime. - var/icon/icon_file = new(icon_path) - if(wear_suit.icon_state in icon_file.IconStates()) - standing = mutable_appearance(icon_file, "[wear_suit.icon_state]", layer = -COLLAR_LAYER) - else - if(wear_suit.icon_state in C.IconStates()) - standing = mutable_appearance(C, "[wear_suit.icon_state]", layer = -COLLAR_LAYER) - - overlays_standing[COLLAR_LAYER] = standing - apply_overlay(COLLAR_LAYER) - -/mob/living/carbon/human/proc/update_misc_effects() - remove_overlay(MISC_LAYER) - - //Begin appending miscellaneous effects. - if(eyes_shine()) - overlays_standing[MISC_LAYER] = get_eye_shine() //Image layer is specified in get_eye_shine() proc as LIGHTING_LAYER + 1. - - apply_overlay(MISC_LAYER) - -/mob/living/carbon/human/admin_Freeze(client/admin, skip_overlays = TRUE) - . = ..() - overlays_standing[FROZEN_LAYER] = mutable_appearance(frozen, layer = -FROZEN_LAYER) - apply_overlay(FROZEN_LAYER) - -/mob/living/carbon/human/admin_unFreeze(client/admin, skip_overlays = TRUE) - . = ..() - remove_overlay(FROZEN_LAYER) - - -/mob/living/carbon/human/proc/force_update_limbs() - for(var/obj/item/organ/external/O in bodyparts) - O.sync_colour_to_human(src) - update_body(0) - -/mob/living/carbon/human/proc/get_overlays_copy(list/unwantedLayers) - var/list/out = new - for(var/i=1;i<=TOTAL_LAYERS;i++) - if(overlays_standing[i]) - if(i in unwantedLayers) - continue - out += overlays_standing[i] - return out - -/mob/living/carbon/human/proc/generate_icon_render_key() - var/husk = (HUSK in mutations) - var/fat = (FAT in mutations) - var/hulk = (HULK in mutations) - var/skeleton = (SKELETON in mutations) - - . = "" - - var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) - if(eyes) - . += "[eyes.eye_colour]" - else - . += "#000000" - - for(var/organ_tag in dna.species.has_limbs) - var/obj/item/organ/external/part = bodyparts_by_name[organ_tag] - if(isnull(part)) - . += "0" - else if(part.is_robotic()) - . += "2[part.model ? "-[part.model]" : ""]" - else if(part.status & ORGAN_DEAD) - . += "3" - else - . += "1" - - if(part) - var/datum/species/S = GLOB.all_species[part.dna.species.name] - . += "[S.race_key]" - . += "[part.dna.GetUIState(DNA_UI_GENDER)]" - . += "[part.dna.GetUIValue(DNA_UI_SKIN_TONE)]" - if(part.s_col) - . += "[part.s_col]" - if(part.s_tone) - . += "[part.s_tone]" - - . = "[.][!!husk][!!fat][!!hulk][!!skeleton]" \ No newline at end of file +/* + Global associative list for caching humanoid icons. + Index format m or f, followed by a string of 0 and 1 to represent bodyparts followed by husk fat hulk skeleton 1 or 0. + TODO: Proper documentation + icon_key is [species.race_key][g][husk][fat][hulk][skeleton][s_tone] +*/ +GLOBAL_LIST_EMPTY(human_icon_cache) + + /////////////////////// + //UPDATE_ICONS SYSTEM// + /////////////////////// +/* +Calling this a system is perhaps a bit trumped up. It is essentially update_clothing dismantled into its +core parts. The key difference is that when we generate overlays we do not generate either lying or standing +versions. Instead, we generate both and store them in two fixed-length lists, both using the same list-index +(The indexes are in update_icons.dm): Each list for humans is (at the time of writing) of length 19. +This will hopefully be reduced as the system is refined. + + var/overlays_lying[19] //For the lying down stance + var/overlays_standing[19] //For the standing stance + +When we call update_icons, the 'lying' variable is checked and then the appropriate list is assigned to our overlays! +That in itself uses a tiny bit more memory (no more than all the ridiculous lists the game has already mind you). + +On the other-hand, it should be very CPU cheap in comparison to the old system. +In the old system, we updated all our overlays every life() call, even if we were standing still inside a crate! +or dead!. 25ish overlays, all generated from scratch every second for every xeno/human/monkey and then applied. +More often than not update_clothing was being called a few times in addition to that! CPU was not the only issue, +all those icons had to be sent to every client. So really the cost was extremely cumulative. To the point where +update_clothing would frequently appear in the top 10 most CPU intensive procs during profiling. + +Another feature of this new system is that our lists are indexed. This means we can update specific overlays! +So we only regenerate icons when we need them to be updated! This is the main saving for this system. + +In practice this means that: + everytime you fall over, we just switch between precompiled lists. Which is fast and cheap. + Everytime you do something minor like take a pen out of your pocket, we only update the in-hand overlay + etc... + + +There are several things that need to be remembered: + +> Whenever we do something that should cause an overlay to update (which doesn't use standard procs + ( i.e. you do something like l_hand = /obj/item/something new(src) ) + You will need to call the relevant update_inv_* proc: + update_inv_head() + update_inv_wear_suit() + update_inv_gloves() + update_inv_shoes() + update_inv_w_uniform() + update_inv_glasse() + update_inv_l_hand() + update_inv_r_hand() + update_inv_belt() + update_inv_wear_id() + update_inv_ears() + update_inv_s_store() + update_inv_pockets() + update_inv_back() + update_inv_handcuffed() + update_inv_wear_mask() + + All of these are named after the variable they update from. They are defined at the mob/ level like + update_clothing was, so you won't cause undefined proc runtimes with usr.update_inv_wear_id() if the usr is a + slime etc. Instead, it'll just return without doing any work. So no harm in calling it for slimes and such. + + +> There are also these special cases: + update_mutations() //handles updating your appearance for certain mutations. e.g TK head-glows + update_mutantrace() //handles updating your appearance after setting the mutantrace var + UpdateDamageIcon() //handles damage overlays for brute/burn damage //(will rename this when I geta round to it) + update_body() //Handles updating your mob's icon to reflect their gender/race/complexion etc + update_hair() //Handles updating your hair overlay (used to be update_face, but mouth and + ...eyes were merged into update_body) + +> All of these procs update our overlays_lying and overlays_standing, and then call update_icons() by default. + If you wish to update several overlays at once, you can set the argument to 0 to disable the update and call + it manually: + e.g. + update_inv_head(0) + update_inv_l_hand(0) + update_inv_r_hand() //<---calls update_icons() + + or equivillantly: + update_inv_head(0) + update_inv_l_hand(0) + update_inv_r_hand(0) + update_icons() + +> If you need to update all overlays you can use regenerate_icons(). it works exactly like update_clothing used to. + +> I reimplimented an old unused variable which was in the code called (coincidentally) var/update_icon + It can be used as another method of triggering regenerate_icons(). It's basically a flag that when set to non-zero + will call regenerate_icons() at the next life() call and then reset itself to 0. + The idea behind it is icons are regenerated only once, even if multiple events requested it. + +This system is confusing and is still a WIP. It's primary goal is speeding up the controls of the game whilst +reducing processing costs. So please bear with me while I iron out the kinks. It will be worth it, I promise. +If I can eventually free var/lying stuff from the life() process altogether, stuns/death/status stuff +will become less affected by lag-spikes and will be instantaneous! :3 + +If you have any questions/constructive-comments/bugs-to-report/or have a massivly devestated butt... +Please contact me on #coderbus IRC. ~Carn x +*/ + +/mob/living/carbon/human + var/list/overlays_standing[TOTAL_LAYERS] + var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed + var/icon/skeleton + var/list/cached_standing_overlays = list() // List of everything currently in a human's actual overlays + +/mob/living/carbon/human/proc/apply_overlay(cache_index) + if((. = overlays_standing[cache_index])) + add_overlay(.) + +/mob/living/carbon/human/proc/remove_overlay(cache_index) + var/I = overlays_standing[cache_index] + if(I) + cut_overlay(I) + overlays_standing[cache_index] = null + + +GLOBAL_LIST_EMPTY(damage_icon_parts) + +//DAMAGE OVERLAYS +//constructs damage icon for each organ from mask * damage field and saves it in our overlays_ lists +/mob/living/carbon/human/UpdateDamageIcon(var/update_icons=1) + // first check whether something actually changed about damage appearance + var/damage_appearance = "" + + for(var/obj/item/organ/external/O in bodyparts) + damage_appearance += O.damage_state + + if(damage_appearance == previous_damage_appearance) + // nothing to do here + return + + previous_damage_appearance = damage_appearance + + remove_overlay(H_DAMAGE_LAYER) + var/mutable_appearance/damage_overlay = mutable_appearance(dna.species.damage_overlays, "00", layer = -H_DAMAGE_LAYER) + overlays_standing[H_DAMAGE_LAYER] = damage_overlay + + // blend the individual damage states with our icons + for(var/D in bodyparts) + var/obj/item/organ/external/E = D + E.update_icon() + if(E.damage_state == "00") + continue + + var/icon/DI + var/cache_index = "[E.damage_state]/[E.icon_name]/[dna.species.blood_color]/[dna.species.name]" + + if(GLOB.damage_icon_parts[cache_index] == null) + DI = new /icon(dna.species.damage_overlays, E.damage_state) // the damage icon for whole human + DI.Blend(new /icon(dna.species.damage_mask, E.icon_name), ICON_MULTIPLY) // mask with this organ's pixels + DI.Blend(dna.species.blood_color, ICON_MULTIPLY) + GLOB.damage_icon_parts[cache_index] = DI + else + DI = GLOB.damage_icon_parts[cache_index] + damage_overlay.overlays += DI + + apply_overlay(H_DAMAGE_LAYER) + + +//BASE MOB SPRITE +/mob/living/carbon/human/proc/update_body(var/update_icons=1, var/rebuild_base=0) + remove_overlay(BODY_LAYER) + remove_overlay(LIMBS_LAYER) // So we don't get the old species' sprite splatted on top of the new one's + remove_overlay(UNDERWEAR_LAYER) + + var/husk_color_mod = rgb(96, 88, 80) + var/hulk_color_mod = rgb(48, 224, 40) + + var/husk = (HUSK in mutations) + var/hulk = (HULK in mutations) + var/skeleton = (SKELETON in mutations) + + if(dna.species && dna.species.bodyflags & HAS_ICON_SKIN_TONE) + dna.species.updatespeciescolor(src) + + //CACHING: Generate an index key from visible bodyparts. + //0 = destroyed, 1 = normal, 2 = robotic, 3 = necrotic. + //Create a new, blank icon for our mob to use. + if(stand_icon) + qdel(stand_icon) + + update_misc_effects() + stand_icon = new (dna.species.icon_template ? dna.species.icon_template : 'icons/mob/human.dmi', "blank") + var/list/standing = list() + var/icon_key = generate_icon_render_key() + + var/mutable_appearance/base + if(GLOB.human_icon_cache[icon_key] && !rebuild_base) + base = GLOB.human_icon_cache[icon_key] + standing += base + else + var/icon/base_icon + //BEGIN CACHED ICON GENERATION. + var/obj/item/organ/external/chest = get_organ("chest") + base_icon = chest.get_icon(skeleton) + + for(var/obj/item/organ/external/part in bodyparts) + var/icon/temp = part.get_icon(skeleton) + //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST + //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) + if(part.icon_position & (LEFT | RIGHT)) + var/icon/temp2 = new('icons/mob/human.dmi',"blank") + temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) + temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) + if(!(part.icon_position & LEFT)) + temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) + if(!(part.icon_position & RIGHT)) + temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) + base_icon.Blend(temp2, ICON_OVERLAY) + if(part.icon_position & LEFT) + temp2.Insert(new/icon(temp,dir=EAST),dir=EAST) + if(part.icon_position & RIGHT) + temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) + base_icon.Blend(temp2, ICON_UNDERLAY) + else + base_icon.Blend(temp, ICON_OVERLAY) + + if(!skeleton) + if(isgolem(src)) + var/datum/species/golem/G = src.dna.species + if(G.golem_colour) + base_icon.ColorTone(G.golem_colour) + if(husk) + base_icon.ColorTone(husk_color_mod) + else if(hulk) + var/list/tone = ReadRGB(hulk_color_mod) + base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3])) + + //Handle husk overlay. + if(husk && ("overlay_husk" in icon_states(chest.icobase))) + var/icon/mask = new(base_icon) + var/icon/husk_over = new(chest.icobase,"overlay_husk") + mask.MapColors(0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,1, 0,0,0,0) + husk_over.Blend(mask, ICON_ADD) + base_icon.Blend(husk_over, ICON_OVERLAY) + + var/mutable_appearance/new_base = mutable_appearance(base_icon, layer = -LIMBS_LAYER) + GLOB.human_icon_cache[icon_key] = new_base + standing += new_base + + //END CACHED ICON GENERATION. + + overlays_standing[LIMBS_LAYER] = standing + apply_overlay(LIMBS_LAYER) + + //Underwear + var/icon/underwear_standing = new /icon('icons/mob/underwear.dmi', "nude") + if(underwear && dna.species.clothing_flags & HAS_UNDERWEAR) + var/datum/sprite_accessory/underwear/U = GLOB.underwear_list[underwear] + if(U) + var/u_icon = U.sprite_sheets && (dna.species.name in U.sprite_sheets) ? U.sprite_sheets[dna.species.name] : U.icon //Species-fit the undergarment. + underwear_standing.Blend(new /icon(u_icon, "uw_[U.icon_state]_s"), ICON_OVERLAY) + + if(undershirt && dna.species.clothing_flags & HAS_UNDERSHIRT) + var/datum/sprite_accessory/undershirt/U2 = GLOB.undershirt_list[undershirt] + if(U2) + var/u2_icon = U2.sprite_sheets && (dna.species.name in U2.sprite_sheets) ? U2.sprite_sheets[dna.species.name] : U2.icon + underwear_standing.Blend(new /icon(u2_icon, "us_[U2.icon_state]_s"), ICON_OVERLAY) + + if(socks && dna.species.clothing_flags & HAS_SOCKS) + var/datum/sprite_accessory/socks/U3 = GLOB.socks_list[socks] + if(U3) + var/u3_icon = U3.sprite_sheets && (dna.species.name in U3.sprite_sheets) ? U3.sprite_sheets[dna.species.name] : U3.icon + underwear_standing.Blend(new /icon(u3_icon, "sk_[U3.icon_state]_s"), ICON_OVERLAY) + + if(underwear_standing) + overlays_standing[UNDERWEAR_LAYER] = mutable_appearance(underwear_standing, layer = -UNDERWEAR_LAYER) + apply_overlay(UNDERWEAR_LAYER) + + if(lip_style && (LIPS in dna.species.species_traits)) + var/icon/lips = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "lips_[lip_style]_s") + lips.Blend(lip_color, ICON_ADD) + standing += mutable_appearance(lips, layer = -BODY_LAYER) + + overlays_standing[BODY_LAYER] = standing + apply_overlay(BODY_LAYER) + //tail + update_tail_layer(0) + update_int_organs() + //head accessory + update_head_accessory(0) + //markings + update_markings(0) + //hair + update_hair(0) + update_fhair(0) + + +//MARKINGS OVERLAY +/mob/living/carbon/human/proc/update_markings(var/update_icons=1) + //Reset our markings. + remove_overlay(MARKINGS_LAYER) + + //Base icon. + var/icon/markings_standing = icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s") + + //Body markings. + var/obj/item/organ/external/chest/chest_organ = get_organ("chest") + if(chest_organ && m_styles["body"]) + var/body_marking = m_styles["body"] + var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] + if(body_marking_style && body_marking_style.species_allowed && (dna.species.name in body_marking_style.species_allowed)) + var/icon/b_marking_s = icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") + if(body_marking_style.do_colouration) + b_marking_s.Blend(m_colours["body"], ICON_ADD) + markings_standing.Blend(b_marking_s, ICON_OVERLAY) + //Head markings. + var/obj/item/organ/external/head/head_organ = get_organ("head") + if(head_organ && m_styles["head"]) //If the head is destroyed, forget the head markings. This prevents floating optical markings on decapitated IPCs, for example. + var/head_marking = m_styles["head"] + var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] + if(head_marking_style && head_marking_style.species_allowed && (head_organ.dna.species.name in head_marking_style.species_allowed)) + var/icon/h_marking_s = icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") + if(head_marking_style.do_colouration) + h_marking_s.Blend(m_colours["head"], ICON_ADD) + markings_standing.Blend(h_marking_s, ICON_OVERLAY) + + overlays_standing[MARKINGS_LAYER] = mutable_appearance(markings_standing, layer = -MARKINGS_LAYER) + apply_overlay(MARKINGS_LAYER) + +//HEAD ACCESSORY OVERLAY +/mob/living/carbon/human/proc/update_head_accessory(var/update_icons=1) + //Reset our head accessory + remove_overlay(HEAD_ACCESSORY_LAYER) + remove_overlay(HEAD_ACC_OVER_LAYER) + + var/obj/item/organ/external/head/head_organ = get_organ("head") + if(!head_organ) + return + + //masks and helmets can obscure our head accessory + if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) + return + + //base icons + var/icon/head_accessory_standing = new /icon('icons/mob/body_accessory.dmi',"accessory_none_s") + if(head_organ.ha_style && (head_organ.dna.species.bodyflags & HAS_HEAD_ACCESSORY)) + var/datum/sprite_accessory/head_accessory/head_accessory_style = GLOB.head_accessory_styles_list[head_organ.ha_style] + if(head_accessory_style && head_accessory_style.species_allowed) + if(head_organ.dna.species.name in head_accessory_style.species_allowed) + var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") + if(head_accessory_style.do_colouration) + head_accessory_s.Blend(head_organ.headacc_colour, ICON_ADD) + head_accessory_standing = head_accessory_s //head_accessory_standing.Blend(head_accessory_s, ICON_OVERLAY) + //Having it this way preserves animations. Useful for animated antennae. + + if(head_accessory_style.over_hair) //Select which layer to use based on the properties of the head accessory style. + overlays_standing[HEAD_ACC_OVER_LAYER] = mutable_appearance(head_accessory_standing, layer = -HEAD_ACC_OVER_LAYER) + apply_overlay(HEAD_ACC_OVER_LAYER) + else + overlays_standing[HEAD_ACCESSORY_LAYER] = mutable_appearance(head_accessory_standing, layer = -HEAD_ACCESSORY_LAYER) + apply_overlay(HEAD_ACCESSORY_LAYER) + else + //warning("Invalid ha_style for [species.name]: [ha_style]") + + + +//HAIR OVERLAY +/mob/living/carbon/human/proc/update_hair(var/update_icons=1) + //Reset our hair + remove_overlay(HAIR_LAYER) + + var/obj/item/organ/external/head/head_organ = get_organ("head") + if(!head_organ) + return + + //masks and helmets can obscure our hair, unless we're a synthetic + if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) + return + + //base icons + var/icon/hair_standing = new /icon('icons/mob/human_face.dmi',"bald_s") + if(head_organ.h_style && !(head && (head.flags & BLOCKHEADHAIR) && !(isSynthetic()))) + var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] + if(hair_style && hair_style.species_allowed) + if((head_organ.dna.species.name in hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics... + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + if(istype(head_organ.dna.species, /datum/species/slime)) // I am el worstos + hair_s.Blend("[skin_colour]A0", ICON_AND) + else if(hair_style.do_colouration) + hair_s.Blend(head_organ.hair_colour, ICON_ADD) + + if(hair_style.secondary_theme) + var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") + if(!hair_style.no_sec_colour) + hair_secondary_s.Blend(head_organ.sec_hair_colour, ICON_ADD) + hair_s.Blend(hair_secondary_s, ICON_OVERLAY) + + hair_standing = hair_s //hair_standing.Blend(hair_s, ICON_OVERLAY) + //Having it this way preserves animations. Useful for IPC screens. + else + //warning("Invalid h_style for [species.name]: [h_style]") + //hair_standing.Blend(debrained_s, ICON_OVERLAY)//how does i overlay for fish? + + overlays_standing[HAIR_LAYER] = mutable_appearance(hair_standing, layer = -HAIR_LAYER) + apply_overlay(HAIR_LAYER) + + +//FACIAL HAIR OVERLAY +/mob/living/carbon/human/proc/update_fhair(var/update_icons=1) + //Reset our facial hair + remove_overlay(FHAIR_LAYER) + remove_overlay(FHAIR_OVER_LAYER) + + var/obj/item/organ/external/head/head_organ = get_organ("head") + if(!head_organ) + return + + //masks and helmets can obscure our facial hair, unless we're a synthetic + if((head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR))) + return + + //base icons + var/icon/face_standing = new /icon('icons/mob/human_face.dmi',"bald_s") + if(head_organ.f_style) + var/datum/sprite_accessory/facial_hair/facial_hair_style = GLOB.facial_hair_styles_list[head_organ.f_style] + if(facial_hair_style && facial_hair_style.species_allowed) + if((head_organ.dna.species.name in facial_hair_style.species_allowed) || (head_organ.dna.species.bodyflags & ALL_RPARTS)) //If the head's species is in the list of allowed species for the hairstyle, or the head's species is one flagged to have bodies comprised wholly of cybernetics... + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(istype(head_organ.dna.species, /datum/species/slime)) // I am el worstos + facial_s.Blend("[skin_colour]A0", ICON_AND) + else if(facial_hair_style.do_colouration) + facial_s.Blend(head_organ.facial_colour, ICON_ADD) + + if(facial_hair_style.secondary_theme) + var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") + if(!facial_hair_style.no_sec_colour) + facial_secondary_s.Blend(head_organ.sec_facial_colour, ICON_ADD) + facial_s.Blend(facial_secondary_s, ICON_OVERLAY) + + face_standing.Blend(facial_s, ICON_OVERLAY) + + if(facial_hair_style.over_hair) //Select which layer to use based on the properties of the facial hair style. + overlays_standing[FHAIR_OVER_LAYER] = mutable_appearance(face_standing, layer = -FHAIR_OVER_LAYER) + apply_overlay(FHAIR_OVER_LAYER) + else + overlays_standing[FHAIR_LAYER] = mutable_appearance(face_standing, layer = -FHAIR_LAYER) + apply_overlay(FHAIR_LAYER) + else + //warning("Invalid f_style for [species.name]: [f_style]") + + + +/mob/living/carbon/human/update_mutations(var/update_icons=1) + remove_overlay(MUTATIONS_LAYER) + var/fat + if(FAT in mutations) + fat = "fat" + + var/mutable_appearance/standing = mutable_appearance('icons/effects/genetics.dmi', layer = -MUTATIONS_LAYER) + var/add_image = 0 + var/g = "m" + if(gender == FEMALE) + g = "f" + // DNA2 - Drawing underlays. + for(var/datum/dna/gene/gene in GLOB.dna_genes) + if(!gene.block) + continue + if(gene.is_active(src)) + var/underlay = gene.OnDrawUnderlays(src, g, fat) + if(underlay) + standing.underlays += underlay + add_image = 1 + for(var/mut in mutations) + switch(mut) + if(LASER) + standing.overlays += "lasereyes_s" + add_image = 1 + if((COLDRES in mutations) && (HEATRES in mutations)) + standing.underlays -= "cold[fat]_s" + standing.underlays -= "fire[fat]_s" + standing.underlays += "coldfire[fat]_s" + + if(add_image) + overlays_standing[MUTATIONS_LAYER] = standing + apply_overlay(MUTATIONS_LAYER) + + +/mob/living/carbon/human/proc/update_mutantrace(var/update_icons=1) +//BS12 EDIT + var/skel = (SKELETON in mutations) + if(skel) + skeleton = 'icons/mob/human_races/r_skeleton.dmi' + else + skeleton = null + + update_hair(0) + update_fhair(0) + + +/mob/living/carbon/human/update_fire() + remove_overlay(FIRE_LAYER) + if(on_fire) + if(!overlays_standing[FIRE_LAYER]) + overlays_standing[FIRE_LAYER] = mutable_appearance(fire_dmi, fire_sprite, layer = -FIRE_LAYER) + apply_overlay(FIRE_LAYER) + +/* --------------------------------------- */ +//For legacy support. +/mob/living/carbon/human/regenerate_icons() + ..() + if(notransform) return + update_mutations(0) + update_body(0, 1) //Update the body and force limb icon regeneration. + update_hair(0) + update_head_accessory(0) + update_fhair(0) + update_mutantrace(0) + update_inv_w_uniform(0,0) + update_inv_wear_id(0) + update_inv_gloves(0,0) + update_inv_glasses(0) + update_inv_ears(0) + update_inv_shoes(0,0) + update_inv_s_store(0) + update_inv_wear_mask(0) + update_inv_head(0,0) + update_inv_belt(0) + update_inv_back(0) + update_inv_wear_suit(0) + update_inv_r_hand(0) + update_inv_l_hand(0) + update_inv_handcuffed(0) + update_inv_legcuffed(0) + update_inv_pockets(0) + update_inv_wear_pda(0) + UpdateDamageIcon(0) + force_update_limbs() + update_tail_layer(0) + overlays.Cut() // Force all overlays to regenerate + update_fire() + update_icons() +/* --------------------------------------- */ +//vvvvvv UPDATE_INV PROCS vvvvvv + +/mob/living/carbon/human/update_inv_w_uniform(var/update_icons=1) + remove_overlay(UNIFORM_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_w_uniform] + if(inv) + inv.update_icon() + + if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + w_uniform.screen_loc = ui_iclothing //...draw the item in the inventory screen + client.screen += w_uniform //Either way, add the item to the HUD + + var/t_color = w_uniform.item_color + if(!t_color) + t_color = icon_state + + var/mutable_appearance/standing = mutable_appearance('icons/mob/uniform.dmi', "[t_color]_s", layer = -UNIFORM_LAYER) + if(FAT in mutations) + if(w_uniform.flags_size & ONESIZEFITSALL) + standing.icon = 'icons/mob/uniform_fat.dmi' + else + to_chat(src, "You burst out of \the [w_uniform]!") + unEquip(w_uniform) + return + + if(w_uniform.icon_override) + standing.icon = w_uniform.icon_override + else if(w_uniform.sprite_sheets && w_uniform.sprite_sheets[dna.species.name]) + standing.icon = w_uniform.sprite_sheets[dna.species.name] + + if(w_uniform.blood_DNA) + var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "uniformblood") + bloodsies.color = w_uniform.blood_color + standing.overlays += bloodsies + + if(w_uniform:accessories.len) //WE CHECKED THE TYPE ABOVE. THIS REALLY SHOULD BE FINE. // oh my god kys whoever made this if statement jfc :gun: + for(var/obj/item/clothing/accessory/A in w_uniform:accessories) + var/tie_color = A.item_color + if(!tie_color) + tie_color = A.icon_state + if(A.icon_override) + standing.overlays += image("icon" = A.icon_override, "icon_state" = "[A.icon_state]") + else if(A.sprite_sheets && A.sprite_sheets[dna.species.name]) + standing.overlays += image("icon" = A.sprite_sheets[dna.species.name], "icon_state" = "[A.icon_state]") + else + standing.overlays += image("icon" = 'icons/mob/ties.dmi', "icon_state" = "[tie_color]") + standing.alpha = w_uniform.alpha + standing.color = w_uniform.color + overlays_standing[UNIFORM_LAYER] = standing + else + // Automatically drop anything in store / id / belt if you're not wearing a uniform. //CHECK IF NECESARRY + for(var/obj/item/thing in list(r_store, l_store, wear_id, wear_pda, belt)) // whoever made this + if(thing) // you're a piece of fucking garbage + unEquip(thing) // why the fuck would you goddamn do this motherfucking shit + if(client) // INVENTORY CODE IN FUCKING ICON CODE + client.screen -= thing // WHAT THE FUCKING FUCK BAY GODDAMNIT + // **I FUCKING HATE YOU AAAAAAAAAA** + if(thing) // + thing.forceMove(drop_location()) // + thing.dropped(src) // + thing.layer = initial(thing.layer) + thing.plane = initial(thing.plane) + apply_overlay(UNIFORM_LAYER) + +/mob/living/carbon/human/update_inv_wear_id(var/update_icons=1) + remove_overlay(ID_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_id] + if(inv) + inv.update_icon() + + if(wear_id) + if(client && hud_used && hud_used.hud_shown) + wear_id.screen_loc = ui_id + client.screen += wear_id + + if(w_uniform && w_uniform:displays_id) + overlays_standing[ID_LAYER] = mutable_appearance('icons/mob/mob.dmi', "id", layer = -ID_LAYER) + apply_overlay(ID_LAYER) + +/mob/living/carbon/human/update_inv_gloves(var/update_icons=1) + remove_overlay(GLOVES_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_gloves] + if(inv) + inv.update_icon() + + if(gloves) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + gloves.screen_loc = ui_gloves //...draw the item in the inventory screen + client.screen += gloves //Either way, add the item to the HUD + + var/t_state = gloves.item_state + if(!t_state) t_state = gloves.icon_state + + var/mutable_appearance/standing + if(gloves.icon_override) + standing = mutable_appearance(gloves.icon_override, "[t_state]", layer = -GLOVES_LAYER) + else if(gloves.sprite_sheets && gloves.sprite_sheets[dna.species.name]) + standing = mutable_appearance(gloves.sprite_sheets[dna.species.name], "[t_state]", layer = -GLOVES_LAYER) + else + standing = mutable_appearance('icons/mob/hands.dmi', "[t_state]", layer = -GLOVES_LAYER) + + if(gloves.blood_DNA) + var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "bloodyhands") + bloodsies.color = gloves.blood_color + standing.overlays += bloodsies + overlays_standing[GLOVES_LAYER] = standing + else + if(blood_DNA) + var/mutable_appearance/bloodsies = mutable_appearance(dna.species.blood_mask, "bloodyhands", layer = -GLOVES_LAYER) + bloodsies.color = hand_blood_color + overlays_standing[GLOVES_LAYER] = bloodsies + apply_overlay(GLOVES_LAYER) + + +/mob/living/carbon/human/update_inv_glasses(var/update_icons=1) + remove_overlay(GLASSES_LAYER) + remove_overlay(GLASSES_OVER_LAYER) + remove_overlay(OVER_MASK_LAYER) + + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_glasses] + if(inv) + inv.update_icon() + + if(glasses) + var/mutable_appearance/new_glasses + var/obj/item/organ/external/head/head_organ = get_organ("head") + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + glasses.screen_loc = ui_glasses //...draw the item in the inventory screen + client.screen += glasses //Either way, add the item to the HUD + + if(glasses.icon_override) + new_glasses = mutable_appearance(glasses.icon_override, "[glasses.icon_state]", layer = -GLASSES_LAYER) + else if(glasses.sprite_sheets && glasses.sprite_sheets[head_organ.dna.species.name]) + new_glasses = mutable_appearance(glasses.sprite_sheets[head_organ.dna.species.name], "[glasses.icon_state]", layer = -GLASSES_LAYER) + else + new_glasses = mutable_appearance('icons/mob/eyes.dmi', "[glasses.icon_state]", layer = -GLASSES_LAYER) + + var/datum/sprite_accessory/hair/hair_style = GLOB.hair_styles_full_list[head_organ.h_style] + var/obj/item/clothing/glasses/G = glasses + if(istype(G) && G.over_mask) //If the user's used the 'wear over mask' verb on the glasses. + new_glasses.layer = -OVER_MASK_LAYER + overlays_standing[OVER_MASK_LAYER] = new_glasses + apply_overlay(OVER_MASK_LAYER) + else if(hair_style && hair_style.glasses_over) //Select which layer to use based on the properties of the hair style. Hair styles with hair that don't overhang the arms of the glasses should have glasses_over set to a positive value. + new_glasses.layer = -GLASSES_OVER_LAYER + overlays_standing[GLASSES_OVER_LAYER] = new_glasses + apply_overlay(GLASSES_OVER_LAYER) + else + overlays_standing[GLASSES_LAYER] = new_glasses + apply_overlay(GLASSES_LAYER) + + update_misc_effects() + +/mob/living/carbon/human/update_inv_ears(var/update_icons=1) + remove_overlay(EARS_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_l_ear] + if(inv) + inv.update_icon() + + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_r_ear] + if(inv) + inv.update_icon() + + if(l_ear || r_ear) + if(l_ear) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + l_ear.screen_loc = ui_l_ear //...draw the item in the inventory screen + client.screen += l_ear //Either way, add the item to the HUD + + var/t_type = l_ear.item_state + if(!t_type) + t_type = l_ear.icon_state + if(l_ear.icon_override) + t_type = "[t_type]_l" + overlays_standing[EARS_LAYER] = mutable_appearance(l_ear.icon_override, "[t_type]", layer = -EARS_LAYER) + else if(l_ear.sprite_sheets && l_ear.sprite_sheets[dna.species.name]) + overlays_standing[EARS_LAYER] = mutable_appearance(l_ear.sprite_sheets[dna.species.name], "[t_type]", layer = -EARS_LAYER) + else + overlays_standing[EARS_LAYER] = mutable_appearance('icons/mob/ears.dmi', "[t_type]", layer = -EARS_LAYER) + + if(r_ear) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + r_ear.screen_loc = ui_r_ear //...draw the item in the inventory screen + client.screen += r_ear //Either way, add the item to the HUD + + var/t_type = r_ear.item_state + if(!t_type) + t_type = r_ear.icon_state + if(r_ear.icon_override) + t_type = "[t_type]_r" + overlays_standing[EARS_LAYER] = mutable_appearance(r_ear.icon_override, "[t_type]", layer = -EARS_LAYER) + else if(r_ear.sprite_sheets && r_ear.sprite_sheets[dna.species.name]) + overlays_standing[EARS_LAYER] = mutable_appearance(r_ear.sprite_sheets[dna.species.name], "[t_type]", layer = -EARS_LAYER) + else + overlays_standing[EARS_LAYER] = mutable_appearance('icons/mob/ears.dmi', "[t_type]", layer = -EARS_LAYER) + apply_overlay(EARS_LAYER) + +/mob/living/carbon/human/update_inv_shoes(var/update_icons=1) + remove_overlay(SHOES_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_shoes] + if(inv) + inv.update_icon() + + if(shoes) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + shoes.screen_loc = ui_shoes //...draw the item in the inventory screen + client.screen += shoes //Either way, add the item to the HUD + + var/mutable_appearance/standing + if(shoes.icon_override) + standing = mutable_appearance(shoes.icon_override, "[shoes.icon_state]", layer = -SHOES_LAYER) + else if(shoes.sprite_sheets && shoes.sprite_sheets[dna.species.name]) + standing = mutable_appearance(shoes.sprite_sheets[dna.species.name], "[shoes.icon_state]", layer = -SHOES_LAYER) + else + standing = mutable_appearance('icons/mob/feet.dmi', "[shoes.icon_state]", layer = -SHOES_LAYER) + + + if(shoes.blood_DNA) + var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "shoeblood") + bloodsies.color = shoes.blood_color + standing.overlays += bloodsies + standing.alpha = shoes.alpha + standing.color = shoes.color + overlays_standing[SHOES_LAYER] = standing + else + if(feet_blood_DNA) + var/mutable_appearance/bloodsies = mutable_appearance(dna.species.blood_mask, "shoeblood", layer = -SHOES_LAYER) + bloodsies.color = feet_blood_color + overlays_standing[SHOES_LAYER] = bloodsies + apply_overlay(SHOES_LAYER) + +/mob/living/carbon/human/update_inv_s_store(var/update_icons=1) + remove_overlay(SUIT_STORE_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_s_store] + if(inv) + inv.update_icon() + + if(s_store) + if(client && hud_used && hud_used.hud_shown) + s_store.screen_loc = ui_sstore1 + client.screen += s_store + + var/t_state = s_store.item_state + if(!t_state) + t_state = s_store.icon_state + var/dmi='icons/mob/belt_mirror.dmi' + overlays_standing[SUIT_STORE_LAYER] = mutable_appearance(dmi, "[t_state]", layer = -SUIT_STORE_LAYER) + s_store.screen_loc = ui_sstore1 //TODO + apply_overlay(SUIT_STORE_LAYER) + + +/mob/living/carbon/human/update_inv_head(var/update_icons=1) + ..() + remove_overlay(HEAD_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_head] + if(inv) + inv.update_icon() + + if(head) + var/mutable_appearance/standing + if(head.icon_override) + standing = mutable_appearance(head.icon_override, "[head.icon_state]", layer = -HEAD_LAYER) + else if(head.sprite_sheets && head.sprite_sheets[dna.species.name]) + standing = mutable_appearance(head.sprite_sheets[dna.species.name], "[head.icon_state]", layer = -HEAD_LAYER) + else + standing = mutable_appearance('icons/mob/head.dmi', "[head.icon_state]", layer = -HEAD_LAYER) + + if(head.blood_DNA) + var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "helmetblood") + bloodsies.color = head.blood_color + standing.overlays += bloodsies + standing.alpha = head.alpha + standing.color = head.color + overlays_standing[HEAD_LAYER] = standing + apply_overlay(HEAD_LAYER) + +/mob/living/carbon/human/update_inv_belt(var/update_icons=1) + remove_overlay(BELT_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_belt] + if(inv) + inv.update_icon() + + if(hud_used.hud_shown && belt) + client.screen += belt + belt.screen_loc = ui_belt + + if(belt) + var/t_state = belt.item_state + if(!t_state) + t_state = belt.icon_state + + if(belt.icon_override) + t_state = "[t_state]_be" + overlays_standing[BELT_LAYER] = mutable_appearance(belt.icon_override, "[t_state]", layer = -BELT_LAYER) + else if(belt.sprite_sheets && belt.sprite_sheets[dna.species.name]) + overlays_standing[BELT_LAYER] = mutable_appearance(belt.sprite_sheets[dna.species.name], "[t_state]", layer = -BELT_LAYER) + else + overlays_standing[BELT_LAYER] = mutable_appearance('icons/mob/belt.dmi', "[t_state]", layer = -BELT_LAYER) + apply_overlay(BELT_LAYER) + + +/mob/living/carbon/human/update_inv_wear_suit(var/update_icons=1) + remove_overlay(SUIT_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_suit] + if(inv) + inv.update_icon() + + if(wear_suit && istype(wear_suit, /obj/item/clothing/suit)) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) //if the inventory is open ... + wear_suit.screen_loc = ui_oclothing //TODO //...draw the item in the inventory screen + client.screen += wear_suit //Either way, add the item to the HUD + + var/mutable_appearance/standing + if(wear_suit.icon_override) + standing = mutable_appearance(wear_suit.icon_override, "[wear_suit.icon_state]", layer = -SUIT_LAYER) + else if(wear_suit.sprite_sheets && wear_suit.sprite_sheets[dna.species.name]) + standing = mutable_appearance(wear_suit.sprite_sheets[dna.species.name], "[wear_suit.icon_state]", layer = -SUIT_LAYER) + else if(FAT in mutations) + if(wear_suit.flags_size & ONESIZEFITSALL) + standing = mutable_appearance('icons/mob/suit_fat.dmi', "[wear_suit.icon_state]", layer = -SUIT_LAYER) + else + to_chat(src, "You burst out of \the [wear_suit]!") + unEquip(wear_suit) + return + else + standing = mutable_appearance('icons/mob/suit.dmi', "[wear_suit.icon_state]", layer = -SUIT_LAYER) + + if(wear_suit.breakouttime) + drop_l_hand() + drop_r_hand() + + if(wear_suit.blood_DNA) + var/obj/item/clothing/suit/S = wear_suit + var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "[S.blood_overlay_type]blood") + bloodsies.color = wear_suit.blood_color + standing.overlays += bloodsies + + + var/special_overlays = wear_suit.special_overlays() + if(special_overlays) + standing.overlays += special_overlays + + standing.alpha = wear_suit.alpha + standing.color = wear_suit.color + overlays_standing[SUIT_LAYER] = standing + + apply_overlay(SUIT_LAYER) + update_tail_layer(0) + update_collar(0) + +/mob/living/carbon/human/update_inv_pockets() + if(client && hud_used) + var/obj/screen/inventory/inv + + inv = hud_used.inv_slots[slot_l_store] + if(inv) + inv.update_icon() + + inv = hud_used.inv_slots[slot_r_store] + if(inv) + inv.update_icon() + + if(hud_used.hud_shown) + if(l_store) + client.screen += l_store + l_store.screen_loc = ui_storage1 + + if(r_store) + client.screen += r_store + r_store.screen_loc = ui_storage2 + +/mob/living/carbon/human/update_inv_wear_pda() + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_pda] + if(inv) + inv.update_icon() + + if(wear_pda) + client.screen += wear_pda + wear_pda.screen_loc = ui_pda + +/mob/living/carbon/human/update_inv_wear_mask(var/update_icons = 1) + ..() + remove_overlay(FACEMASK_LAYER) + if(client && hud_used) + var/obj/screen/inventory/inv = hud_used.inv_slots[slot_wear_mask] + if(inv) + inv.update_icon() + if(wear_mask && (istype(wear_mask, /obj/item/clothing/mask) || istype(wear_mask, /obj/item/clothing/accessory))) + if(!(slot_wear_mask in check_obscured_slots())) + var/obj/item/organ/external/head/head_organ = get_organ("head") + var/datum/sprite_accessory/alt_heads/alternate_head + if(head_organ.alt_head && head_organ.alt_head != "None") + alternate_head = GLOB.alt_heads_list[head_organ.alt_head] + + var/mutable_appearance/standing + var/icon/mask_icon = new(wear_mask.icon) + if(wear_mask.icon_override) + mask_icon = new(wear_mask.icon_override) + standing = mutable_appearance(wear_mask.icon_override, "[wear_mask.icon_state][(alternate_head && ("[wear_mask.icon_state]_[alternate_head.suffix]" in mask_icon.IconStates())) ? "_[alternate_head.suffix]" : ""]", layer = -FACEMASK_LAYER) + else if(wear_mask.sprite_sheets && wear_mask.sprite_sheets[dna.species.name]) + mask_icon = new(wear_mask.sprite_sheets[dna.species.name]) + standing = mutable_appearance(wear_mask.sprite_sheets[dna.species.name], "[wear_mask.icon_state][(alternate_head && ("[wear_mask.icon_state]_[alternate_head.suffix]" in mask_icon.IconStates())) ? "_[alternate_head.suffix]" : ""]", layer = -FACEMASK_LAYER) + else + standing = mutable_appearance('icons/mob/mask.dmi', "[wear_mask.icon_state][(alternate_head && ("[wear_mask.icon_state]_[alternate_head.suffix]" in mask_icon.IconStates())) ? "_[alternate_head.suffix]" : ""]", layer = -FACEMASK_LAYER) + + if(!istype(wear_mask, /obj/item/clothing/mask/cigarette) && wear_mask.blood_DNA) + var/image/bloodsies = image("icon" = dna.species.blood_mask, "icon_state" = "maskblood") + bloodsies.color = wear_mask.blood_color + standing.overlays += bloodsies + + standing.alpha = wear_mask.alpha + standing.color = wear_mask.color + overlays_standing[FACEMASK_LAYER] = standing + apply_overlay(FACEMASK_LAYER) + + +/mob/living/carbon/human/update_inv_back(var/update_icons=1) + ..() + remove_overlay(BACK_LAYER) + if(back) + //determine the icon to use + var/mutable_appearance/standing + if(back.icon_override) + standing = mutable_appearance(back.icon_override, "[back.icon_state]", layer = -BACK_LAYER) + else if(istype(back, /obj/item/rig)) + //If this is a rig and a mob_icon is set, it will take species into account in the rig update_icon() proc. + var/obj/item/rig/rig = back + standing = rig.mob_icon + else if(back.sprite_sheets && back.sprite_sheets[dna.species.name]) + standing = mutable_appearance(back.sprite_sheets[dna.species.name], "[back.icon_state]", layer = -BACK_LAYER) + else + standing = mutable_appearance('icons/mob/back.dmi', "[back.icon_state]", layer = -BACK_LAYER) + + //create the image + standing.alpha = back.alpha + standing.color = back.color + overlays_standing[BACK_LAYER] = standing + apply_overlay(BACK_LAYER) + +/mob/living/carbon/human/update_inv_handcuffed(var/update_icons=1) + remove_overlay(HANDCUFF_LAYER) + if(handcuffed) + if(istype(handcuffed, /obj/item/restraints/handcuffs/pinkcuffs)) + overlays_standing[HANDCUFF_LAYER] = mutable_appearance('icons/mob/mob.dmi', "pinkcuff1", layer = -HANDCUFF_LAYER) + else + overlays_standing[HANDCUFF_LAYER] = mutable_appearance('icons/mob/mob.dmi', "handcuff1", layer = -HANDCUFF_LAYER) + apply_overlay(HANDCUFF_LAYER) + +/mob/living/carbon/human/update_inv_legcuffed(var/update_icons=1) + remove_overlay(LEGCUFF_LAYER) + clear_alert("legcuffed") + if(legcuffed) + overlays_standing[LEGCUFF_LAYER] = mutable_appearance('icons/mob/mob.dmi', "legcuff1", layer = -LEGCUFF_LAYER) + throw_alert("legcuffed", /obj/screen/alert/restrained/legcuffed, new_master = legcuffed) + if(m_intent != MOVE_INTENT_WALK) + m_intent = MOVE_INTENT_WALK + if(hud_used && hud_used.move_intent) + hud_used.move_intent.icon_state = "walking" + apply_overlay(LEGCUFF_LAYER) + + +/mob/living/carbon/human/update_inv_r_hand(var/update_icons=1) + ..() + remove_overlay(R_HAND_LAYER) + if(r_hand) + var/t_state = r_hand.item_state + if(!t_state) + t_state = r_hand.icon_state + + var/mutable_appearance/standing + if(r_hand.sprite_sheets_inhand && r_hand.sprite_sheets_inhand[dna.species.name]) + t_state = "[t_state]_r" + standing = mutable_appearance(r_hand.sprite_sheets_inhand[dna.species.name], "[t_state]", layer = -R_HAND_LAYER) + else + standing = mutable_appearance(r_hand.righthand_file, "[t_state]", layer = -R_HAND_LAYER) + standing = center_image(standing, r_hand.inhand_x_dimension, r_hand.inhand_y_dimension) + overlays_standing[R_HAND_LAYER] = standing + apply_overlay(R_HAND_LAYER) + + +/mob/living/carbon/human/update_inv_l_hand(var/update_icons=1) + ..() + remove_overlay(L_HAND_LAYER) + if(l_hand) + var/t_state = l_hand.item_state + if(!t_state) + t_state = l_hand.icon_state + + var/mutable_appearance/standing + if(l_hand.sprite_sheets_inhand && l_hand.sprite_sheets_inhand[dna.species.name]) + t_state = "[t_state]_l" + standing = mutable_appearance(l_hand.sprite_sheets_inhand[dna.species.name], "[t_state]", layer = -L_HAND_LAYER) + else + standing = mutable_appearance(l_hand.lefthand_file, "[t_state]", layer = -L_HAND_LAYER) + standing = center_image(standing, l_hand.inhand_x_dimension, l_hand.inhand_y_dimension) + overlays_standing[L_HAND_LAYER] = standing + apply_overlay(L_HAND_LAYER) + +//human HUD updates for items in our inventory + +//update whether our head item appears on our hud. +/mob/living/carbon/human/update_hud_head(obj/item/I) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) + I.screen_loc = ui_head + client.screen += I + +//update whether our mask item appears on our hud. +/mob/living/carbon/human/update_hud_wear_mask(obj/item/I) + if(client && hud_used && hud_used.hud_shown) + if(hud_used.inventory_shown) + I.screen_loc = ui_mask + client.screen += I + +//update whether our back item appears on our hud. +/mob/living/carbon/human/update_hud_back(obj/item/I) + if(client && hud_used && hud_used.hud_shown) + I.screen_loc = ui_back + client.screen += I + + +/mob/living/carbon/human/proc/update_tail_layer(var/update_icons=1) + remove_overlay(TAIL_UNDERLIMBS_LAYER) // SEW direction icons, overlayed by LIMBS_LAYER. + remove_overlay(TAIL_LAYER) /* This will be one of two things: + If the species' tail is overlapped by limbs, this will be only the N direction icon so tails + can still appear on the outside of uniforms and such. + Otherwise, since the user's tail isn't overlapped by limbs, it will be a full icon with all directions. */ + + var/icon/tail_marking_icon + var/datum/sprite_accessory/body_markings/tail/tail_marking_style + if(m_styles["tail"] != "None" && (dna.species.bodyflags & HAS_TAIL_MARKINGS)) + var/tail_marking = m_styles["tail"] + tail_marking_style = GLOB.marking_styles_list[tail_marking] + tail_marking_icon = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") + tail_marking_icon.Blend(m_colours["tail"], ICON_ADD) + + if(body_accessory) + if(body_accessory.try_restrictions(src)) + var/icon/accessory_s = new/icon("icon" = body_accessory.icon, "icon_state" = body_accessory.icon_state) + if(dna.species.bodyflags & HAS_SKIN_COLOR) + accessory_s.Blend(skin_colour, body_accessory.blend_mode) + if(tail_marking_icon && (body_accessory.name in tail_marking_style.tails_allowed)) + accessory_s.Blend(tail_marking_icon, ICON_OVERLAY) + if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) + // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. + var/icon/under = new/icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s") + under.Insert(new/icon(accessory_s, dir=SOUTH), dir=SOUTH) + under.Insert(new/icon(accessory_s, dir=EAST), dir=EAST) + under.Insert(new/icon(accessory_s, dir=WEST), dir=WEST) + + var/mutable_appearance/underlimbs = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) + underlimbs.pixel_x = body_accessory.pixel_x_offset + underlimbs.pixel_y = body_accessory.pixel_y_offset + overlays_standing[TAIL_UNDERLIMBS_LAYER] = underlimbs + + // Creates a blank icon, and copies accessory_s' north direction sprite into it + // before passing that to the tail layer that overlays uniforms and such. + var/icon/over = new/icon("icon" = 'icons/mob/body_accessory.dmi', "icon_state" = "accessory_none_s") + over.Insert(new/icon(accessory_s, dir=NORTH), dir=NORTH) + + var/mutable_appearance/tail = mutable_appearance(over, layer = -TAIL_LAYER) + tail.pixel_x = body_accessory.pixel_x_offset + tail.pixel_y = body_accessory.pixel_y_offset + overlays_standing[TAIL_LAYER] = tail + else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. + var/mutable_appearance/tail = mutable_appearance(accessory_s, layer = -TAIL_LAYER) + tail.pixel_x = body_accessory.pixel_x_offset + tail.pixel_y = body_accessory.pixel_y_offset + overlays_standing[TAIL_LAYER] = tail + + else if(tail && dna.species.bodyflags & HAS_TAIL) //no tailless tajaran + if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL)) + var/icon/tail_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[tail]_s") + if(dna.species.bodyflags & HAS_SKIN_COLOR) + tail_s.Blend(skin_colour, ICON_ADD) + if(tail_marking_icon && !tail_marking_style.tails_allowed) + tail_s.Blend(tail_marking_icon, ICON_OVERLAY) + if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) + // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. + var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "blank") + under.Insert(new/icon(tail_s, dir=SOUTH), dir=SOUTH) + under.Insert(new/icon(tail_s, dir=EAST), dir=EAST) + under.Insert(new/icon(tail_s, dir=WEST), dir=WEST) + + overlays_standing[TAIL_UNDERLIMBS_LAYER] = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) + + // Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such. + var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "blank") + over.Insert(new/icon(tail_s, dir=NORTH), dir=NORTH) + + overlays_standing[TAIL_LAYER] = mutable_appearance(over, layer = -TAIL_LAYER) + else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. + overlays_standing[TAIL_LAYER] = mutable_appearance(tail_s, layer = -TAIL_LAYER) + apply_overlay(TAIL_LAYER) + apply_overlay(TAIL_UNDERLIMBS_LAYER) + +/mob/living/carbon/human/proc/start_tail_wagging(var/update_icons=1) + remove_overlay(TAIL_UNDERLIMBS_LAYER) // SEW direction icons, overlayed by LIMBS_LAYER. + remove_overlay(TAIL_LAYER) /* This will be one of two things: + If the species' tail is overlapped by limbs, this will be only the N direction icon so tails + can still appear on the outside of uniforms and such. + Otherwise, since the user's tail isn't overlapped by limbs, it will be a full icon with all directions. */ + + var/icon/tail_marking_icon + var/datum/sprite_accessory/body_markings/tail/tail_marking_style + if(m_styles["tail"] != "None" && (dna.species.bodyflags & HAS_TAIL_MARKINGS)) + var/tail_marking = m_styles["tail"] + tail_marking_style = GLOB.marking_styles_list[tail_marking] + tail_marking_icon = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]w_s") + tail_marking_icon.Blend(m_colours["tail"], ICON_ADD) + + if(body_accessory) + var/icon/accessory_s = new/icon("icon" = body_accessory.get_animated_icon(), "icon_state" = body_accessory.get_animated_icon_state()) + if(dna.species.bodyflags & HAS_SKIN_COLOR) + accessory_s.Blend(skin_colour, body_accessory.blend_mode) + if(tail_marking_icon && (body_accessory.name in tail_marking_style.tails_allowed)) + accessory_s.Blend(tail_marking_icon, ICON_OVERLAY) + if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) + // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. + var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "Vulpkanin_tail_delay") + if(body_accessory.allowed_species && (dna.species.name in body_accessory.allowed_species)) + under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") + under.Insert(new/icon(accessory_s, dir=SOUTH), dir=SOUTH) + under.Insert(new/icon(accessory_s, dir=EAST), dir=EAST) + under.Insert(new/icon(accessory_s, dir=WEST), dir=WEST) + + var/mutable_appearance/underlimbs = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) + underlimbs.pixel_x = body_accessory.pixel_x_offset + underlimbs.pixel_y = body_accessory.pixel_y_offset + overlays_standing[TAIL_UNDERLIMBS_LAYER] = underlimbs + + // Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such. + var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "Vulpkanin_tail_delay") + if(body_accessory.allowed_species && (dna.species.name in body_accessory.allowed_species)) // If the user's species is in the list of allowed species for the currently selected body accessory, use the appropriate animation timing blank + over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") + over.Insert(new/icon(accessory_s, dir=NORTH), dir=NORTH) + + var/mutable_appearance/tail = mutable_appearance(over, layer = -TAIL_LAYER) + tail.pixel_x = body_accessory.pixel_x_offset + tail.pixel_y = body_accessory.pixel_y_offset + overlays_standing[TAIL_LAYER] = tail + else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. + var/mutable_appearance/tail = mutable_appearance(accessory_s, layer = -TAIL_LAYER) + tail.pixel_x = body_accessory.pixel_x_offset + tail.pixel_y = body_accessory.pixel_y_offset + overlays_standing[TAIL_LAYER] = tail + + else if(tail && dna.species.bodyflags & HAS_TAIL) + var/icon/tailw_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[tail]w_s") + if(dna.species.bodyflags & HAS_SKIN_COLOR) + tailw_s.Blend(skin_colour, ICON_ADD) + if(tail_marking_icon && !tail_marking_style.tails_allowed) + tailw_s.Blend(tail_marking_icon, ICON_OVERLAY) + if((!body_accessory || istype(body_accessory, /datum/body_accessory/tail)) && dna.species.bodyflags & TAIL_OVERLAPPED) // If the player has a species whose tail is overlapped by limbs... (having a non-tail body accessory like the snake body will override this) + // Gives the underlimbs layer SEW direction icons since it's overlayed by limbs and just about everything else anyway. + var/icon/under = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") + under.Insert(new/icon(tailw_s, dir=SOUTH), dir=SOUTH) + under.Insert(new/icon(tailw_s, dir=EAST), dir=EAST) + under.Insert(new/icon(tailw_s, dir=WEST), dir=WEST) + + overlays_standing[TAIL_UNDERLIMBS_LAYER] = mutable_appearance(under, layer = -TAIL_UNDERLIMBS_LAYER) + + // Creates a blank icon, and copies accessory_s' north direction sprite into it before passing that to the tail layer that overlays uniforms and such. + var/icon/over = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[dna.species.name]_tail_delay") + over.Insert(new/icon(tailw_s, dir=NORTH), dir=NORTH) + + overlays_standing[TAIL_LAYER] = mutable_appearance(over, layer = -TAIL_LAYER) + else // Otherwise, since the user's tail isn't overlapped by limbs, go ahead and use default icon generation. + overlays_standing[TAIL_LAYER] = mutable_appearance(tailw_s, layer = -TAIL_LAYER) + apply_overlay(TAIL_LAYER) + apply_overlay(TAIL_UNDERLIMBS_LAYER) + +/mob/living/carbon/human/proc/stop_tail_wagging(var/update_icons=1) + remove_overlay(TAIL_UNDERLIMBS_LAYER) + remove_overlay(TAIL_LAYER) + update_tail_layer(update_icons) //just trigger a full update for normal stationary sprites + +/mob/living/carbon/human/proc/update_int_organs() + remove_overlay(INTORGAN_LAYER) + + var/list/standing = list() + for(var/organ in internal_organs) + var/obj/item/organ/internal/I = organ + var/render = I.render() + if(render) + standing += render + + overlays_standing[INTORGAN_LAYER] = standing + apply_overlay(INTORGAN_LAYER) + +/mob/living/carbon/human/handle_transform_change() + ..() + update_tail_layer() + +//Adds a collar overlay above the helmet layer if the suit has one +// Suit needs an identically named sprite in icons/mob/collar.dmi +// For suits with sprite_sheets, an identically named sprite needs to exist in a file like this icons/mob/species/[species_name_here]/collar.dmi. +/mob/living/carbon/human/proc/update_collar(var/update_icons=1) + remove_overlay(COLLAR_LAYER) + var/icon/C = new('icons/mob/collar.dmi') + var/mutable_appearance/standing = null + + if(wear_suit) + if(wear_suit.icon_override) + var/icon_path = "[wear_suit.icon_override]" + icon_path = "[copytext(icon_path, 1, findtext(icon_path, "/suit.dmi"))]/collar.dmi" //If this file doesn't exist, the end result is that COLLAR_LAYER will be unchanged (empty). + if(fexists(icon_path)) //Just ensuring the nonexistance of a file with the above path won't cause a runtime. + var/icon/icon_file = new(icon_path) + if(wear_suit.icon_state in icon_file.IconStates()) + standing = mutable_appearance(icon_file, "[wear_suit.icon_state]", layer = -COLLAR_LAYER) + else if(wear_suit.sprite_sheets && wear_suit.sprite_sheets[dna.species.name]) + var/icon_path = "[wear_suit.sprite_sheets[dna.species.name]]" + icon_path = "[copytext(icon_path, 1, findtext(icon_path, "/suit.dmi"))]/collar.dmi" //If this file doesn't exist, the end result is that COLLAR_LAYER will be unchanged (empty). + if(fexists(icon_path)) //Just ensuring the nonexistance of a file with the above path won't cause a runtime. + var/icon/icon_file = new(icon_path) + if(wear_suit.icon_state in icon_file.IconStates()) + standing = mutable_appearance(icon_file, "[wear_suit.icon_state]", layer = -COLLAR_LAYER) + else + if(wear_suit.icon_state in C.IconStates()) + standing = mutable_appearance(C, "[wear_suit.icon_state]", layer = -COLLAR_LAYER) + + overlays_standing[COLLAR_LAYER] = standing + apply_overlay(COLLAR_LAYER) + +/mob/living/carbon/human/proc/update_misc_effects() + remove_overlay(MISC_LAYER) + + //Begin appending miscellaneous effects. + if(eyes_shine()) + overlays_standing[MISC_LAYER] = get_eye_shine() //Image layer is specified in get_eye_shine() proc as LIGHTING_LAYER + 1. + + apply_overlay(MISC_LAYER) + +/mob/living/carbon/human/admin_Freeze(client/admin, skip_overlays = TRUE) + . = ..() + overlays_standing[FROZEN_LAYER] = mutable_appearance(frozen, layer = -FROZEN_LAYER) + apply_overlay(FROZEN_LAYER) + +/mob/living/carbon/human/admin_unFreeze(client/admin, skip_overlays = TRUE) + . = ..() + remove_overlay(FROZEN_LAYER) + + +/mob/living/carbon/human/proc/force_update_limbs() + for(var/obj/item/organ/external/O in bodyparts) + O.sync_colour_to_human(src) + update_body(0) + +/mob/living/carbon/human/proc/get_overlays_copy(list/unwantedLayers) + var/list/out = new + for(var/i=1;i<=TOTAL_LAYERS;i++) + if(overlays_standing[i]) + if(i in unwantedLayers) + continue + out += overlays_standing[i] + return out + +/mob/living/carbon/human/proc/generate_icon_render_key() + var/husk = (HUSK in mutations) + var/fat = (FAT in mutations) + var/hulk = (HULK in mutations) + var/skeleton = (SKELETON in mutations) + + . = "" + + var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) + if(eyes) + . += "[eyes.eye_colour]" + else + . += "#000000" + + for(var/organ_tag in dna.species.has_limbs) + var/obj/item/organ/external/part = bodyparts_by_name[organ_tag] + if(isnull(part)) + . += "0" + else if(part.is_robotic()) + . += "2[part.model ? "-[part.model]" : ""]" + else if(part.status & ORGAN_DEAD) + . += "3" + else + . += "1" + + if(part) + var/datum/species/S = GLOB.all_species[part.dna.species.name] + . += "[S.race_key]" + . += "[part.dna.GetUIState(DNA_UI_GENDER)]" + . += "[part.dna.GetUIValue(DNA_UI_SKIN_TONE)]" + if(part.s_col) + . += "[part.s_col]" + if(part.s_tone) + . += "[part.s_tone]" + + . = "[.][!!husk][!!fat][!!hulk][!!skeleton]" diff --git a/code/modules/mob/living/carbon/human/update_stat.dm b/code/modules/mob/living/carbon/human/update_stat.dm index 6228038c7106..14c87e137224 100644 --- a/code/modules/mob/living/carbon/human/update_stat.dm +++ b/code/modules/mob/living/carbon/human/update_stat.dm @@ -24,4 +24,4 @@ . = dna.species.can_hear(src) /mob/living/carbon/human/check_death_method() - return dna.species.dies_at_threshold \ No newline at end of file + return dna.species.dies_at_threshold diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm index 5352ba525f1b..832260d30900 100644 --- a/code/modules/mob/living/carbon/life.dm +++ b/code/modules/mob/living/carbon/life.dm @@ -437,4 +437,4 @@ if(85 to INFINITY) severity = 6 overlay_fullscreen("brute", /obj/screen/fullscreen/brute, severity) else - clear_fullscreen("brute") \ No newline at end of file + clear_fullscreen("brute") diff --git a/code/modules/mob/living/carbon/status_procs.dm b/code/modules/mob/living/carbon/status_procs.dm index a9f68773ed5a..7c809ea2993c 100644 --- a/code/modules/mob/living/carbon/status_procs.dm +++ b/code/modules/mob/living/carbon/status_procs.dm @@ -12,4 +12,4 @@ stam_paralyzed = TRUE update_canmove() if(!prev && getStaminaLoss() < 120) // Puts you a little further into the initial stamcrit, makes stamcrit harder to outright counter with chems. - adjustStaminaLoss(30, FALSE) \ No newline at end of file + adjustStaminaLoss(30, FALSE) diff --git a/code/modules/mob/living/carbon/update_icons.dm b/code/modules/mob/living/carbon/update_icons.dm index 05224c28dde9..cb607bae2947 100644 --- a/code/modules/mob/living/carbon/update_icons.dm +++ b/code/modules/mob/living/carbon/update_icons.dm @@ -87,4 +87,4 @@ //update whether our back item appears on our hud. /mob/living/carbon/proc/update_hud_back(obj/item/I) - return \ No newline at end of file + return diff --git a/code/modules/mob/living/carbon/update_status.dm b/code/modules/mob/living/carbon/update_status.dm index f78bdf63ec9f..b67953c7e69e 100644 --- a/code/modules/mob/living/carbon/update_status.dm +++ b/code/modules/mob/living/carbon/update_status.dm @@ -30,4 +30,4 @@ . = FALSE var/obj/item/organ/internal/ears/ears = get_int_organ(/obj/item/organ/internal/ears) if(istype(ears) && !ears.deaf) - . = TRUE \ No newline at end of file + . = TRUE diff --git a/code/modules/mob/living/damage_procs.dm b/code/modules/mob/living/damage_procs.dm index 09abb09041d3..fa1c392334d0 100644 --- a/code/modules/mob/living/damage_procs.dm +++ b/code/modules/mob/living/damage_procs.dm @@ -1,342 +1,342 @@ - -/* - apply_damage(a,b,c) - args - a:damage - How much damage to take - b:damage_type - What type of damage to take, brute, burn - c:def_zone - Where to take the damage if its brute or burn - Returns - standard 0 if fail -*/ -/mob/living/proc/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/sharp = 0, var/used_weapon = null) - blocked = (100-blocked)/100 - if(!damage || (blocked <= 0)) return 0 - switch(damagetype) - if(BRUTE) - adjustBruteLoss(damage * blocked) - if(BURN) - adjustFireLoss(damage * blocked) - if(TOX) - adjustToxLoss(damage * blocked) - if(OXY) - adjustOxyLoss(damage * blocked) - if(CLONE) - adjustCloneLoss(damage * blocked) - if(STAMINA) - adjustStaminaLoss(damage * blocked) - updatehealth("apply damage") - return 1 - -/mob/living/proc/apply_damage_type(damage = 0, damagetype = BRUTE) //like apply damage except it always uses the damage procs - switch(damagetype) - if(BRUTE) - return adjustBruteLoss(damage) - if(BURN) - return adjustFireLoss(damage) - if(TOX) - return adjustToxLoss(damage) - if(OXY) - return adjustOxyLoss(damage) - if(CLONE) - return adjustCloneLoss(damage) - if(STAMINA) - return adjustStaminaLoss(damage) - if(BRAIN) - return adjustBrainLoss(damage) - -/mob/living/proc/get_damage_amount(damagetype = BRUTE) - switch(damagetype) - if(BRUTE) - return getBruteLoss() - if(BURN) - return getFireLoss() - if(TOX) - return getToxLoss() - if(OXY) - return getOxyLoss() - if(CLONE) - return getCloneLoss() - if(STAMINA) - return getStaminaLoss() - - -/mob/living/proc/apply_damages(var/brute = 0, var/burn = 0, var/tox = 0, var/oxy = 0, var/clone = 0, var/def_zone = null, var/blocked = 0, var/stamina = 0) - if(blocked >= 100) return 0 - if(brute) apply_damage(brute, BRUTE, def_zone, blocked) - if(burn) apply_damage(burn, BURN, def_zone, blocked) - if(tox) apply_damage(tox, TOX, def_zone, blocked) - if(oxy) apply_damage(oxy, OXY, def_zone, blocked) - if(clone) apply_damage(clone, CLONE, def_zone, blocked) - if(stamina) apply_damage(stamina, STAMINA, def_zone, blocked) - return 1 - - - -/mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) - blocked = (100-blocked)/100 - if(!effect || (blocked <= 0)) - return 0 - switch(effecttype) - if(STUN) - Stun(effect * blocked) - if(WEAKEN) - Weaken(effect * blocked) - if(PARALYZE) - Paralyse(effect * blocked) - if(IRRADIATE) - var/rad_damage = effect - if(!negate_armor) // Setting negate_armor overrides radiation armor checks, which are automatic otherwise - rad_damage = max(effect * ((100-run_armor_check(null, "rad", "Your clothes feel warm.", "Your clothes feel warm."))/100),0) - radiation += rad_damage - if(SLUR) - Slur(effect * blocked) - if(STUTTER) - Stuttering(effect * blocked) - if(EYE_BLUR) - EyeBlurry(effect * blocked) - if(DROWSY) - Drowsy(effect * blocked) - if(JITTER) - if(status_flags & CANSTUN) - Jitter(effect * blocked) - updatehealth("apply effect") - return 1 - -/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/slur = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/blocked = 0, var/stamina = 0, var/jitter = 0) - if(blocked >= 100) return 0 - if(stun) apply_effect(stun, STUN, blocked) - if(weaken) apply_effect(weaken, WEAKEN, blocked) - if(paralyze) apply_effect(paralyze, PARALYZE, blocked) - if(irradiate) apply_effect(irradiate, IRRADIATE, blocked) - if(slur) apply_effect(slur, SLUR, blocked) - if(stutter) apply_effect(stutter, STUTTER, blocked) - if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked) - if(drowsy) apply_effect(drowsy, DROWSY, blocked) - if(stamina) apply_damage(stamina, STAMINA, null, blocked) - if(jitter) apply_effect(jitter, JITTER, blocked) - return 1 - - -/mob/living/proc/getBruteLoss() - return bruteloss - -/mob/living/proc/adjustBruteLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - var/old_bruteloss = bruteloss - bruteloss = max(bruteloss + amount, 0) - if(old_bruteloss == bruteloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("adjustBruteLoss") - -/mob/living/proc/getOxyLoss() - return oxyloss - -/mob/living/proc/adjustOxyLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - oxyloss = 0 - return FALSE //godmode - if(BREATHLESS in mutations) - oxyloss = 0 - return FALSE - var/old_oxyloss = oxyloss - oxyloss = max(oxyloss + amount, 0) - if(old_oxyloss == oxyloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("adjustOxyLoss") - -/mob/living/proc/setOxyLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - oxyloss = 0 - return FALSE //godmode - if(BREATHLESS in mutations) - oxyloss = 0 - return FALSE - var/old_oxyloss = oxyloss - oxyloss = amount - if(old_oxyloss == oxyloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("setOxyLoss") - -/mob/living/proc/getToxLoss() - return toxloss - -/mob/living/proc/adjustToxLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - var/old_toxloss = toxloss - toxloss = max(toxloss + amount, 0) - if(old_toxloss == toxloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("adjustToxLoss") - -/mob/living/proc/setToxLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - var/old_toxloss = toxloss - toxloss = amount - if(old_toxloss == toxloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("setToxLoss") - -/mob/living/proc/getFireLoss() - return fireloss - -/mob/living/proc/adjustFireLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - var/old_fireloss = fireloss - fireloss = max(fireloss + amount, 0) - if(old_fireloss == fireloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("adjustFireLoss") - -/mob/living/proc/getCloneLoss() - return cloneloss - -/mob/living/proc/adjustCloneLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - var/old_cloneloss = cloneloss - cloneloss = max(cloneloss + amount, 0) - if(old_cloneloss == cloneloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("adjustCloneLoss") - -/mob/living/proc/setCloneLoss(amount, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - var/old_cloneloss = cloneloss - cloneloss = amount - if(old_cloneloss == cloneloss) - updating_health = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_HEALTH - if(updating_health) - updatehealth("setCloneLoss") - -/mob/living/proc/getBrainLoss() - return 0 - -/mob/living/proc/adjustBrainLoss(amount, updating = TRUE) - return STATUS_UPDATE_NONE - -/mob/living/proc/setBrainLoss(amount, updating = TRUE) - return STATUS_UPDATE_NONE - -/mob/living/proc/getStaminaLoss() - return staminaloss - -/mob/living/proc/adjustStaminaLoss(amount, updating = TRUE) - if(status_flags & GODMODE) - return FALSE - var/old_stamloss = staminaloss - staminaloss = min(max(staminaloss + amount, 0), 120) - if(old_stamloss == staminaloss) - updating = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_STAMINA - if(amount > 0) - stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME - if(updating) - update_stamina() - -/mob/living/proc/setStaminaLoss(amount, updating = TRUE) - if(status_flags & GODMODE) - return FALSE - var/old_stamloss = staminaloss - staminaloss = min(max(amount, 0), 120) - if(old_stamloss == staminaloss) - updating = FALSE - . = STATUS_UPDATE_NONE - else - . = STATUS_UPDATE_STAMINA - if(amount > 0) - stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME - if(updating) - update_stamina() - -/mob/living/proc/getMaxHealth() - return maxHealth - -/mob/living/proc/setMaxHealth(var/newMaxHealth) - maxHealth = newMaxHealth - - - -// heal ONE external organ, organ gets randomly selected from damaged ones. -/mob/living/proc/heal_organ_damage(brute, burn, updating_health = TRUE) - adjustBruteLoss(-brute, FALSE) - adjustFireLoss(-burn, FALSE) - if(updating_health) - updatehealth("heal organ damage") - -// damage ONE external organ, organ gets randomly selected from damaged ones. -/mob/living/proc/take_organ_damage(brute, burn, updating_health = TRUE) - if(status_flags & GODMODE) - return FALSE //godmode - adjustBruteLoss(brute, FALSE) - adjustFireLoss(burn, FALSE) - if(updating_health) - updatehealth("take organ damage") - -// heal MANY external organs, in random order -/mob/living/proc/heal_overall_damage(brute, burn, updating_health = TRUE) - adjustBruteLoss(-brute, FALSE) - adjustFireLoss(-burn, FALSE) - if(updating_health) - updatehealth("heal overall damage") - -// damage MANY external organs, in random order -/mob/living/proc/take_overall_damage(brute, burn, updating_health = TRUE, used_weapon = null) - if(status_flags & GODMODE) - return FALSE //godmode - adjustBruteLoss(brute, FALSE) - adjustFireLoss(burn, FALSE) - if(updating_health) - updatehealth("take overall damage") - -/mob/living/proc/has_organic_damage() - return (maxHealth - health) - -//heal up to amount damage, in a given order -/mob/living/proc/heal_ordered_damage(amount, list/damage_types) - . = amount //we'll return the amount of damage healed - for(var/i in damage_types) - var/amount_to_heal = min(amount, get_damage_amount(i)) //heal only up to the amount of damage we have - if(amount_to_heal) - apply_damage_type(-amount_to_heal, i) - amount -= amount_to_heal //remove what we healed from our current amount - if(!amount) - break - . -= amount //if there's leftover healing, remove it from what we return \ No newline at end of file + +/* + apply_damage(a,b,c) + args + a:damage - How much damage to take + b:damage_type - What type of damage to take, brute, burn + c:def_zone - Where to take the damage if its brute or burn + Returns + standard 0 if fail +*/ +/mob/living/proc/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0, var/sharp = 0, var/used_weapon = null) + blocked = (100-blocked)/100 + if(!damage || (blocked <= 0)) return 0 + switch(damagetype) + if(BRUTE) + adjustBruteLoss(damage * blocked) + if(BURN) + adjustFireLoss(damage * blocked) + if(TOX) + adjustToxLoss(damage * blocked) + if(OXY) + adjustOxyLoss(damage * blocked) + if(CLONE) + adjustCloneLoss(damage * blocked) + if(STAMINA) + adjustStaminaLoss(damage * blocked) + updatehealth("apply damage") + return 1 + +/mob/living/proc/apply_damage_type(damage = 0, damagetype = BRUTE) //like apply damage except it always uses the damage procs + switch(damagetype) + if(BRUTE) + return adjustBruteLoss(damage) + if(BURN) + return adjustFireLoss(damage) + if(TOX) + return adjustToxLoss(damage) + if(OXY) + return adjustOxyLoss(damage) + if(CLONE) + return adjustCloneLoss(damage) + if(STAMINA) + return adjustStaminaLoss(damage) + if(BRAIN) + return adjustBrainLoss(damage) + +/mob/living/proc/get_damage_amount(damagetype = BRUTE) + switch(damagetype) + if(BRUTE) + return getBruteLoss() + if(BURN) + return getFireLoss() + if(TOX) + return getToxLoss() + if(OXY) + return getOxyLoss() + if(CLONE) + return getCloneLoss() + if(STAMINA) + return getStaminaLoss() + + +/mob/living/proc/apply_damages(var/brute = 0, var/burn = 0, var/tox = 0, var/oxy = 0, var/clone = 0, var/def_zone = null, var/blocked = 0, var/stamina = 0) + if(blocked >= 100) return 0 + if(brute) apply_damage(brute, BRUTE, def_zone, blocked) + if(burn) apply_damage(burn, BURN, def_zone, blocked) + if(tox) apply_damage(tox, TOX, def_zone, blocked) + if(oxy) apply_damage(oxy, OXY, def_zone, blocked) + if(clone) apply_damage(clone, CLONE, def_zone, blocked) + if(stamina) apply_damage(stamina, STAMINA, def_zone, blocked) + return 1 + + + +/mob/living/proc/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) + blocked = (100-blocked)/100 + if(!effect || (blocked <= 0)) + return 0 + switch(effecttype) + if(STUN) + Stun(effect * blocked) + if(WEAKEN) + Weaken(effect * blocked) + if(PARALYZE) + Paralyse(effect * blocked) + if(IRRADIATE) + var/rad_damage = effect + if(!negate_armor) // Setting negate_armor overrides radiation armor checks, which are automatic otherwise + rad_damage = max(effect * ((100-run_armor_check(null, "rad", "Your clothes feel warm.", "Your clothes feel warm."))/100),0) + radiation += rad_damage + if(SLUR) + Slur(effect * blocked) + if(STUTTER) + Stuttering(effect * blocked) + if(EYE_BLUR) + EyeBlurry(effect * blocked) + if(DROWSY) + Drowsy(effect * blocked) + if(JITTER) + if(status_flags & CANSTUN) + Jitter(effect * blocked) + updatehealth("apply effect") + return 1 + +/mob/living/proc/apply_effects(var/stun = 0, var/weaken = 0, var/paralyze = 0, var/irradiate = 0, var/slur = 0, var/stutter = 0, var/eyeblur = 0, var/drowsy = 0, var/blocked = 0, var/stamina = 0, var/jitter = 0) + if(blocked >= 100) return 0 + if(stun) apply_effect(stun, STUN, blocked) + if(weaken) apply_effect(weaken, WEAKEN, blocked) + if(paralyze) apply_effect(paralyze, PARALYZE, blocked) + if(irradiate) apply_effect(irradiate, IRRADIATE, blocked) + if(slur) apply_effect(slur, SLUR, blocked) + if(stutter) apply_effect(stutter, STUTTER, blocked) + if(eyeblur) apply_effect(eyeblur, EYE_BLUR, blocked) + if(drowsy) apply_effect(drowsy, DROWSY, blocked) + if(stamina) apply_damage(stamina, STAMINA, null, blocked) + if(jitter) apply_effect(jitter, JITTER, blocked) + return 1 + + +/mob/living/proc/getBruteLoss() + return bruteloss + +/mob/living/proc/adjustBruteLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + var/old_bruteloss = bruteloss + bruteloss = max(bruteloss + amount, 0) + if(old_bruteloss == bruteloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("adjustBruteLoss") + +/mob/living/proc/getOxyLoss() + return oxyloss + +/mob/living/proc/adjustOxyLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + oxyloss = 0 + return FALSE //godmode + if(BREATHLESS in mutations) + oxyloss = 0 + return FALSE + var/old_oxyloss = oxyloss + oxyloss = max(oxyloss + amount, 0) + if(old_oxyloss == oxyloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("adjustOxyLoss") + +/mob/living/proc/setOxyLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + oxyloss = 0 + return FALSE //godmode + if(BREATHLESS in mutations) + oxyloss = 0 + return FALSE + var/old_oxyloss = oxyloss + oxyloss = amount + if(old_oxyloss == oxyloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("setOxyLoss") + +/mob/living/proc/getToxLoss() + return toxloss + +/mob/living/proc/adjustToxLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + var/old_toxloss = toxloss + toxloss = max(toxloss + amount, 0) + if(old_toxloss == toxloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("adjustToxLoss") + +/mob/living/proc/setToxLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + var/old_toxloss = toxloss + toxloss = amount + if(old_toxloss == toxloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("setToxLoss") + +/mob/living/proc/getFireLoss() + return fireloss + +/mob/living/proc/adjustFireLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + var/old_fireloss = fireloss + fireloss = max(fireloss + amount, 0) + if(old_fireloss == fireloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("adjustFireLoss") + +/mob/living/proc/getCloneLoss() + return cloneloss + +/mob/living/proc/adjustCloneLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + var/old_cloneloss = cloneloss + cloneloss = max(cloneloss + amount, 0) + if(old_cloneloss == cloneloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("adjustCloneLoss") + +/mob/living/proc/setCloneLoss(amount, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + var/old_cloneloss = cloneloss + cloneloss = amount + if(old_cloneloss == cloneloss) + updating_health = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_HEALTH + if(updating_health) + updatehealth("setCloneLoss") + +/mob/living/proc/getBrainLoss() + return 0 + +/mob/living/proc/adjustBrainLoss(amount, updating = TRUE) + return STATUS_UPDATE_NONE + +/mob/living/proc/setBrainLoss(amount, updating = TRUE) + return STATUS_UPDATE_NONE + +/mob/living/proc/getStaminaLoss() + return staminaloss + +/mob/living/proc/adjustStaminaLoss(amount, updating = TRUE) + if(status_flags & GODMODE) + return FALSE + var/old_stamloss = staminaloss + staminaloss = min(max(staminaloss + amount, 0), 120) + if(old_stamloss == staminaloss) + updating = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_STAMINA + if(amount > 0) + stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME + if(updating) + update_stamina() + +/mob/living/proc/setStaminaLoss(amount, updating = TRUE) + if(status_flags & GODMODE) + return FALSE + var/old_stamloss = staminaloss + staminaloss = min(max(amount, 0), 120) + if(old_stamloss == staminaloss) + updating = FALSE + . = STATUS_UPDATE_NONE + else + . = STATUS_UPDATE_STAMINA + if(amount > 0) + stam_regen_start_time = world.time + STAMINA_REGEN_BLOCK_TIME + if(updating) + update_stamina() + +/mob/living/proc/getMaxHealth() + return maxHealth + +/mob/living/proc/setMaxHealth(var/newMaxHealth) + maxHealth = newMaxHealth + + + +// heal ONE external organ, organ gets randomly selected from damaged ones. +/mob/living/proc/heal_organ_damage(brute, burn, updating_health = TRUE) + adjustBruteLoss(-brute, FALSE) + adjustFireLoss(-burn, FALSE) + if(updating_health) + updatehealth("heal organ damage") + +// damage ONE external organ, organ gets randomly selected from damaged ones. +/mob/living/proc/take_organ_damage(brute, burn, updating_health = TRUE) + if(status_flags & GODMODE) + return FALSE //godmode + adjustBruteLoss(brute, FALSE) + adjustFireLoss(burn, FALSE) + if(updating_health) + updatehealth("take organ damage") + +// heal MANY external organs, in random order +/mob/living/proc/heal_overall_damage(brute, burn, updating_health = TRUE) + adjustBruteLoss(-brute, FALSE) + adjustFireLoss(-burn, FALSE) + if(updating_health) + updatehealth("heal overall damage") + +// damage MANY external organs, in random order +/mob/living/proc/take_overall_damage(brute, burn, updating_health = TRUE, used_weapon = null) + if(status_flags & GODMODE) + return FALSE //godmode + adjustBruteLoss(brute, FALSE) + adjustFireLoss(burn, FALSE) + if(updating_health) + updatehealth("take overall damage") + +/mob/living/proc/has_organic_damage() + return (maxHealth - health) + +//heal up to amount damage, in a given order +/mob/living/proc/heal_ordered_damage(amount, list/damage_types) + . = amount //we'll return the amount of damage healed + for(var/i in damage_types) + var/amount_to_heal = min(amount, get_damage_amount(i)) //heal only up to the amount of damage we have + if(amount_to_heal) + apply_damage_type(-amount_to_heal, i) + amount -= amount_to_heal //remove what we healed from our current amount + if(!amount) + break + . -= amount //if there's leftover healing, remove it from what we return diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 6054068eb152..4d8aa1b776bd 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -100,4 +100,4 @@ visible_message("[src] starts convulsing violently!", "You feel as if your body is tearing itself apart!") Weaken(15) do_jitter_animation(1000, -1) - addtimer(CALLBACK(src, .proc/gib), rand(20, 100)) \ No newline at end of file + addtimer(CALLBACK(src, .proc/gib), rand(20, 100)) diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 4909148b4922..5ab604e5b3e4 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -220,4 +220,4 @@ return /mob/living/proc/handle_hud_icons_health() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index b35556903f2c..30e10553d3e6 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1,6 +1,6 @@ /mob/living/Initialize() . = ..() - var/datum/atom_hud/data/human/medical/advanced/medhud = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/data/human/medical/advanced/medhud = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medhud.add_to_hud(src) faction += "\ref[src]" @@ -247,6 +247,7 @@ set hidden = 1 if(InCritical()) create_attack_log("[src] has ["succumbed to death"] with [round(health, 0.1)] points of health!") + create_log(MISC_LOG, "has succumbed to death with [round(health, 0.1)] points of health") adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD) // super check for weird mobs, including ones that adjust hp // we don't want to go overboard and gib them, though @@ -580,7 +581,7 @@ newdir = NORTH else if(newdir == 12) //E + W newdir = EAST - if((newdir in cardinal) && (prob(50))) + if((newdir in GLOB.cardinal) && (prob(50))) newdir = turn(get_dir(T, loc), 180) if(!blood_exists) new /obj/effect/decal/cleanable/trail_holder(loc) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index aaa117106429..6e4f13d735f1 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -1,360 +1,360 @@ - -/* - run_armor_check(a,b) - args - a:def_zone - What part is getting hit, if null will check entire body - b:attack_flag - What type of attack, bullet, laser, energy, melee - - Returns - 0 - no block - 1 - halfblock - 2 - fullblock -*/ -/mob/living/proc/run_armor_check(var/def_zone = null, var/attack_flag = "melee", var/absorb_text = null, var/soften_text = null, armour_penetration, penetrated_text) - var/armor = getarmor(def_zone, attack_flag) - - //the if "armor" check is because this is used for everything on /living, including humans - if(armor && armor < 100 && armour_penetration) // Armor with 100+ protection can not be penetrated for admin items - armor = max(0, armor - armour_penetration) - if(penetrated_text) - to_chat(src, "[penetrated_text]") - else - to_chat(src, "Your armor was penetrated!") - - if(armor >= 100) - if(absorb_text) - to_chat(src, "[absorb_text]") - else - to_chat(src, "Your armor absorbs the blow!") - else if(armor > 0) - if(soften_text) - to_chat(src, "[soften_text]") - else - to_chat(src, "Your armor softens the blow!") - return armor - -//if null is passed for def_zone, then this should return something appropriate for all zones (e.g. area effect damage) -/mob/living/proc/getarmor(var/def_zone, var/type) - return 0 - -/mob/living/proc/is_mouth_covered(head_only = FALSE, mask_only = FALSE) - return FALSE - -/mob/living/proc/is_eyes_covered(check_glasses = TRUE, check_head = TRUE, check_mask = TRUE) - return FALSE - -/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone) - //Armor - var/armor = run_armor_check(def_zone, P.flag, armour_penetration = P.armour_penetration) - if(!P.nodamage) - apply_damage(P.damage, P.damage_type, def_zone, armor) - if(P.dismemberment) - check_projectile_dismemberment(P, def_zone) - return P.on_hit(src, armor, def_zone) - -/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) - return 0 - -/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage) - if(status_flags & GODMODE) //godmode - return FALSE - if(NO_SHOCK in mutations) //shockproof - return FALSE - if(tesla_shock && tesla_ignore) - return FALSE - if(shock_damage > 0) - if(!illusion) - adjustFireLoss(shock_damage) - visible_message( - "[src] was shocked by \the [source]!", - "You feel a powerful shock coursing through your body!", - "You hear a heavy electrical crack.") - return shock_damage - -/mob/living/emp_act(severity) - var/list/L = src.get_contents() - for(var/obj/O in L) - O.emp_act(severity) - ..() - -/obj/item/proc/get_volume_by_throwforce_and_or_w_class() - if(throwforce && w_class) - return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100 - else if(w_class) - return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 - else - return 0 - -//this proc handles being hit by a thrown atom -/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) - if(istype(AM, /obj/item)) - var/obj/item/I = AM - var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest - var/dtype = BRUTE - var/volume = I.get_volume_by_throwforce_and_or_w_class() - SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, zone) - dtype = I.damtype - - if(I.throwforce > 0) //If the weapon's throwforce is greater than zero... - if(I.throwhitsound) //...and throwhitsound is defined... - playsound(loc, I.throwhitsound, volume, TRUE, -1) //...play the weapon's throwhitsound. - else if(I.hitsound) //Otherwise, if the weapon's hitsound is defined... - playsound(loc, I.hitsound, volume, TRUE, -1) //...play the weapon's hitsound. - else if(!I.throwhitsound) //Otherwise, if throwhitsound isn't defined... - playsound(loc, 'sound/weapons/genhit.ogg',volume, TRUE, -1) //...play genhit.ogg. - - else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero... - playsound(loc, 'sound/weapons/genhit1.ogg', volume, 1, -1)//...play genhit1.ogg - if(!I.throwforce)// Otherwise, if the item's throwforce is 0... - playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg. - if(!blocked) - visible_message("[src] has been hit by [I].", - "[src] has been hit by [I].") - var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration) - apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), I) - if(I.thrownby) - add_attack_logs(I.thrownby, src, "Hit with thrown [I]") - else - return 1 - else - playsound(loc, 'sound/weapons/genhit.ogg', 50, TRUE, -1) - ..() - - -/mob/living/mech_melee_attack(obj/mecha/M) - if(M.occupant.a_intent == INTENT_HARM) - if(HAS_TRAIT(M.occupant, TRAIT_PACIFISM)) - to_chat(M.occupant, "You don't want to harm other living beings!") - return - M.do_attack_animation(src) - if(M.damtype == "brute") - step_away(src,M,15) - switch(M.damtype) - if("brute") - Paralyse(1) - take_overall_damage(rand(M.force/2, M.force)) - playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) - if("fire") - take_overall_damage(0, rand(M.force/2, M.force)) - playsound(src, 'sound/items/welder.ogg', 50, TRUE) - if("tox") - M.mech_toxin_damage(src) - else - return - updatehealth("mech melee attack") - M.occupant_message("You hit [src].") - visible_message("[M.name] hits [src]!", "[M.name] hits you!") - add_attack_logs(M.occupant, src, "Mecha-meleed with [M]") - else - step_away(src,M) - add_attack_logs(M.occupant, src, "Mecha-pushed with [M]", ATKLOG_ALL) - M.occupant_message("You push [src] out of the way.") - visible_message("[M] pushes [src] out of the way.") - -//Mobs on Fire -/mob/living/proc/IgniteMob() - if(fire_stacks > 0 && !on_fire) - on_fire = 1 - visible_message("[src] catches fire!", \ - "You're set on fire!") - set_light(light_range + 3,l_color = "#ED9200") - throw_alert("fire", /obj/screen/alert/fire) - update_fire() - return 1 - return 0 - -/mob/living/proc/ExtinguishMob() - if(on_fire) - on_fire = 0 - fire_stacks = 0 - set_light(max(0,light_range - 3)) - clear_alert("fire") - update_fire() - -/mob/living/proc/update_fire() - return - -/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person - fire_stacks = Clamp(fire_stacks + add_fire_stacks, -20, 20) - if(on_fire && fire_stacks <= 0) - ExtinguishMob() - -/mob/living/proc/handle_fire() - if(fire_stacks < 0) //If we've doused ourselves in water to avoid fire, dry off slowly - fire_stacks = min(0, fire_stacks + 1)//So we dry ourselves back to default, nonflammable. - if(!on_fire) - return 1 - if(fire_stacks > 0) - adjust_fire_stacks(-0.1) //the fire is slowly consumed - else - ExtinguishMob() - return - var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment - if(G.oxygen < 1) - ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire - return - var/turf/location = get_turf(src) - location.hotspot_expose(700, 50, 1) - -/mob/living/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - ..() - adjust_fire_stacks(3) - IgniteMob() - -//Share fire evenly between the two mobs -//Called in MobBump() and Crossed() -/mob/living/proc/spreadFire(mob/living/L) - if(!istype(L)) - return - var/L_old_on_fire = L.on_fire - - if(on_fire) //Only spread fire stacks if we're on fire - fire_stacks /= 2 - L.fire_stacks += fire_stacks - if(L.IgniteMob()) - log_game("[key_name(src)] bumped into [key_name(L)] and set them on fire") - - if(L_old_on_fire) //Only ignite us and gain their stacks if they were onfire before we bumped them - L.fire_stacks /= 2 - fire_stacks += L.fire_stacks - IgniteMob() - -/mob/living/can_be_pulled(user, grab_state, force) - return ..() && !(buckled && buckled.buckle_prevents_pull) - -/mob/living/water_act(volume, temperature, source, method = REAGENT_TOUCH) - . = ..() - adjust_fire_stacks(-(volume * 0.2)) - -//This is called when the mob is thrown into a dense turf -/mob/living/proc/turf_collision(var/turf/T, var/speed) - src.take_organ_damage(speed*5) - -/mob/living/proc/near_wall(var/direction,var/distance=1) - var/turf/T = get_step(get_turf(src),direction) - var/turf/last_turf = src.loc - var/i = 1 - - while(i>0 && i<=distance) - if(T.density) //Turf is a wall! - return last_turf - i++ - last_turf = T - T = get_step(T,direction) - - return 0 - -// End BS12 momentum-transfer code. - -/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE) - if(user == src || anchored) - return 0 - if(!(status_flags & CANPUSH)) - return 0 - - for(var/obj/item/grab/G in grabbed_by) - if(G.assailant == user) - to_chat(user, "You already grabbed [src].") - return - - add_attack_logs(user, src, "Grabbed passively", ATKLOG_ALL) - - var/obj/item/grab/G = new /obj/item/grab(user, src) - if(buckled) - to_chat(user, "You cannot grab [src]; [p_they()] [p_are()] buckled in!") - if(!G) //the grab will delete itself in New if src is anchored - return 0 - user.put_in_active_hand(G) - G.synch() - LAssailant = user - - playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - /*if(user.dir == src.dir) - G.state = GRAB_AGGRESSIVE - G.last_upgrade = world.time - if(!supress_message) - visible_message("[user] has grabbed [src] from behind!") - else*///This is an example of how you can make special types of grabs simply based on direction. - if(!supress_message) - visible_message("[user] has grabbed [src] passively!") - - return G - -/mob/living/attack_slime(mob/living/simple_animal/slime/M) - if(!SSticker) - to_chat(M, "You cannot attack people before the game has started.") - return - - if(M.buckled) - if(M in buckled_mobs) - M.Feedstop() - return // can't attack while eating! - - if(HAS_TRAIT(src, TRAIT_PACIFISM)) - to_chat(M, "You don't want to hurt anyone!") - return FALSE - - if(stat != DEAD) - add_attack_logs(M, src, "Slime'd") - M.do_attack_animation(src) - visible_message("\The [M.name] glomps [src]!", "\The [M.name] glomps you!") - return TRUE - -/mob/living/attack_animal(mob/living/simple_animal/M) - M.face_atom(src) - if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0) - M.custom_emote(1, "[M.friendly] [src].") - return FALSE - if(HAS_TRAIT(M, TRAIT_PACIFISM)) - to_chat(M, "You don't want to hurt anyone!") - return FALSE - - if(M.attack_sound) - playsound(loc, M.attack_sound, 50, 1, 1) - M.do_attack_animation(src) - visible_message("\The [M] [M.attacktext] [src]!", \ - "\The [M] [M.attacktext] [src]!") - add_attack_logs(M, src, "Animal attacked") - return TRUE - -/mob/living/attack_larva(mob/living/carbon/alien/larva/L) - switch(L.a_intent) - if(INTENT_HELP) - visible_message("[L.name] rubs its head against [src].") - return 0 - - else - if(HAS_TRAIT(L, TRAIT_PACIFISM)) - to_chat(L, "You don't want to hurt anyone!") - return - - L.do_attack_animation(src) - if(prob(90)) - add_attack_logs(L, src, "Larva attacked") - visible_message("[L.name] bites [src]!", \ - "[L.name] bites [src]!") - playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) - return 1 - else - visible_message("[L.name] has attempted to bite [src]!", \ - "[L.name] has attempted to bite [src]!") - return 0 - -/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M) - switch(M.a_intent) - if(INTENT_HELP) - visible_message("[M] caresses [src] with its scythe like arm.") - return FALSE - if(INTENT_GRAB) - grabbedby(M) - return FALSE - if(INTENT_HARM) - if(HAS_TRAIT(M, TRAIT_PACIFISM)) - to_chat(M, "You don't want to hurt anyone!") - return FALSE - M.do_attack_animation(src) - return TRUE - if(INTENT_DISARM) - M.do_attack_animation(src, ATTACK_EFFECT_DISARM) - return TRUE \ No newline at end of file + +/* + run_armor_check(a,b) + args + a:def_zone - What part is getting hit, if null will check entire body + b:attack_flag - What type of attack, bullet, laser, energy, melee + + Returns + 0 - no block + 1 - halfblock + 2 - fullblock +*/ +/mob/living/proc/run_armor_check(var/def_zone = null, var/attack_flag = "melee", var/absorb_text = null, var/soften_text = null, armour_penetration, penetrated_text) + var/armor = getarmor(def_zone, attack_flag) + + //the if "armor" check is because this is used for everything on /living, including humans + if(armor && armor < 100 && armour_penetration) // Armor with 100+ protection can not be penetrated for admin items + armor = max(0, armor - armour_penetration) + if(penetrated_text) + to_chat(src, "[penetrated_text]") + else + to_chat(src, "Your armor was penetrated!") + + if(armor >= 100) + if(absorb_text) + to_chat(src, "[absorb_text]") + else + to_chat(src, "Your armor absorbs the blow!") + else if(armor > 0) + if(soften_text) + to_chat(src, "[soften_text]") + else + to_chat(src, "Your armor softens the blow!") + return armor + +//if null is passed for def_zone, then this should return something appropriate for all zones (e.g. area effect damage) +/mob/living/proc/getarmor(var/def_zone, var/type) + return 0 + +/mob/living/proc/is_mouth_covered(head_only = FALSE, mask_only = FALSE) + return FALSE + +/mob/living/proc/is_eyes_covered(check_glasses = TRUE, check_head = TRUE, check_mask = TRUE) + return FALSE + +/mob/living/bullet_act(var/obj/item/projectile/P, var/def_zone) + //Armor + var/armor = run_armor_check(def_zone, P.flag, armour_penetration = P.armour_penetration) + if(!P.nodamage) + apply_damage(P.damage, P.damage_type, def_zone, armor) + if(P.dismemberment) + check_projectile_dismemberment(P, def_zone) + return P.on_hit(src, armor, def_zone) + +/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) + return 0 + +/mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + SEND_SIGNAL(src, COMSIG_LIVING_ELECTROCUTE_ACT, shock_damage) + if(status_flags & GODMODE) //godmode + return FALSE + if(NO_SHOCK in mutations) //shockproof + return FALSE + if(tesla_shock && tesla_ignore) + return FALSE + if(shock_damage > 0) + if(!illusion) + adjustFireLoss(shock_damage) + visible_message( + "[src] was shocked by \the [source]!", + "You feel a powerful shock coursing through your body!", + "You hear a heavy electrical crack.") + return shock_damage + +/mob/living/emp_act(severity) + var/list/L = src.get_contents() + for(var/obj/O in L) + O.emp_act(severity) + ..() + +/obj/item/proc/get_volume_by_throwforce_and_or_w_class() + if(throwforce && w_class) + return Clamp((throwforce + w_class) * 5, 30, 100)// Add the item's throwforce to its weight class and multiply by 5, then clamp the value between 30 and 100 + else if(w_class) + return Clamp(w_class * 8, 20, 100) // Multiply the item's weight class by 8, then clamp the value between 20 and 100 + else + return 0 + +//this proc handles being hit by a thrown atom +/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE, datum/thrownthing/throwingdatum) + if(istype(AM, /obj/item)) + var/obj/item/I = AM + var/zone = ran_zone("chest", 65)//Hits a random part of the body, geared towards the chest + var/dtype = BRUTE + var/volume = I.get_volume_by_throwforce_and_or_w_class() + SEND_SIGNAL(I, COMSIG_MOVABLE_IMPACT_ZONE, src, zone) + dtype = I.damtype + + if(I.throwforce > 0) //If the weapon's throwforce is greater than zero... + if(I.throwhitsound) //...and throwhitsound is defined... + playsound(loc, I.throwhitsound, volume, TRUE, -1) //...play the weapon's throwhitsound. + else if(I.hitsound) //Otherwise, if the weapon's hitsound is defined... + playsound(loc, I.hitsound, volume, TRUE, -1) //...play the weapon's hitsound. + else if(!I.throwhitsound) //Otherwise, if throwhitsound isn't defined... + playsound(loc, 'sound/weapons/genhit.ogg',volume, TRUE, -1) //...play genhit.ogg. + + else if(!I.throwhitsound && I.throwforce > 0) //Otherwise, if the item doesn't have a throwhitsound and has a throwforce greater than zero... + playsound(loc, 'sound/weapons/genhit1.ogg', volume, 1, -1)//...play genhit1.ogg + if(!I.throwforce)// Otherwise, if the item's throwforce is 0... + playsound(loc, 'sound/weapons/throwtap.ogg', 1, volume, -1)//...play throwtap.ogg. + if(!blocked) + visible_message("[src] has been hit by [I].", + "[src] has been hit by [I].") + var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration) + apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), I) + if(I.thrownby) + add_attack_logs(I.thrownby, src, "Hit with thrown [I]") + else + return 1 + else + playsound(loc, 'sound/weapons/genhit.ogg', 50, TRUE, -1) + ..() + + +/mob/living/mech_melee_attack(obj/mecha/M) + if(M.occupant.a_intent == INTENT_HARM) + if(HAS_TRAIT(M.occupant, TRAIT_PACIFISM)) + to_chat(M.occupant, "You don't want to harm other living beings!") + return + M.do_attack_animation(src) + if(M.damtype == "brute") + step_away(src,M,15) + switch(M.damtype) + if("brute") + Paralyse(1) + take_overall_damage(rand(M.force/2, M.force)) + playsound(src, 'sound/weapons/punch4.ogg', 50, TRUE) + if("fire") + take_overall_damage(0, rand(M.force/2, M.force)) + playsound(src, 'sound/items/welder.ogg', 50, TRUE) + if("tox") + M.mech_toxin_damage(src) + else + return + updatehealth("mech melee attack") + M.occupant_message("You hit [src].") + visible_message("[M.name] hits [src]!", "[M.name] hits you!") + add_attack_logs(M.occupant, src, "Mecha-meleed with [M]") + else + step_away(src,M) + add_attack_logs(M.occupant, src, "Mecha-pushed with [M]", ATKLOG_ALL) + M.occupant_message("You push [src] out of the way.") + visible_message("[M] pushes [src] out of the way.") + +//Mobs on Fire +/mob/living/proc/IgniteMob() + if(fire_stacks > 0 && !on_fire) + on_fire = 1 + visible_message("[src] catches fire!", \ + "You're set on fire!") + set_light(light_range + 3,l_color = "#ED9200") + throw_alert("fire", /obj/screen/alert/fire) + update_fire() + return 1 + return 0 + +/mob/living/proc/ExtinguishMob() + if(on_fire) + on_fire = 0 + fire_stacks = 0 + set_light(max(0,light_range - 3)) + clear_alert("fire") + update_fire() + +/mob/living/proc/update_fire() + return + +/mob/living/proc/adjust_fire_stacks(add_fire_stacks) //Adjusting the amount of fire_stacks we have on person + fire_stacks = Clamp(fire_stacks + add_fire_stacks, -20, 20) + if(on_fire && fire_stacks <= 0) + ExtinguishMob() + +/mob/living/proc/handle_fire() + if(fire_stacks < 0) //If we've doused ourselves in water to avoid fire, dry off slowly + fire_stacks = min(0, fire_stacks + 1)//So we dry ourselves back to default, nonflammable. + if(!on_fire) + return 1 + if(fire_stacks > 0) + adjust_fire_stacks(-0.1) //the fire is slowly consumed + else + ExtinguishMob() + return + var/datum/gas_mixture/G = loc.return_air() // Check if we're standing in an oxygenless environment + if(G.oxygen < 1) + ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire + return + var/turf/location = get_turf(src) + location.hotspot_expose(700, 50, 1) + +/mob/living/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + ..() + adjust_fire_stacks(3) + IgniteMob() + +//Share fire evenly between the two mobs +//Called in MobBump() and Crossed() +/mob/living/proc/spreadFire(mob/living/L) + if(!istype(L)) + return + var/L_old_on_fire = L.on_fire + + if(on_fire) //Only spread fire stacks if we're on fire + fire_stacks /= 2 + L.fire_stacks += fire_stacks + if(L.IgniteMob()) + log_game("[key_name(src)] bumped into [key_name(L)] and set them on fire") + + if(L_old_on_fire) //Only ignite us and gain their stacks if they were onfire before we bumped them + L.fire_stacks /= 2 + fire_stacks += L.fire_stacks + IgniteMob() + +/mob/living/can_be_pulled(user, grab_state, force) + return ..() && !(buckled && buckled.buckle_prevents_pull) + +/mob/living/water_act(volume, temperature, source, method = REAGENT_TOUCH) + . = ..() + adjust_fire_stacks(-(volume * 0.2)) + +//This is called when the mob is thrown into a dense turf +/mob/living/proc/turf_collision(var/turf/T, var/speed) + src.take_organ_damage(speed*5) + +/mob/living/proc/near_wall(var/direction,var/distance=1) + var/turf/T = get_step(get_turf(src),direction) + var/turf/last_turf = src.loc + var/i = 1 + + while(i>0 && i<=distance) + if(T.density) //Turf is a wall! + return last_turf + i++ + last_turf = T + T = get_step(T,direction) + + return 0 + +// End BS12 momentum-transfer code. + +/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = FALSE) + if(user == src || anchored) + return 0 + if(!(status_flags & CANPUSH)) + return 0 + + for(var/obj/item/grab/G in grabbed_by) + if(G.assailant == user) + to_chat(user, "You already grabbed [src].") + return + + add_attack_logs(user, src, "Grabbed passively", ATKLOG_ALL) + + var/obj/item/grab/G = new /obj/item/grab(user, src) + if(buckled) + to_chat(user, "You cannot grab [src]; [p_they()] [p_are()] buckled in!") + if(!G) //the grab will delete itself in New if src is anchored + return 0 + user.put_in_active_hand(G) + G.synch() + LAssailant = user + + playsound(src.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + /*if(user.dir == src.dir) + G.state = GRAB_AGGRESSIVE + G.last_upgrade = world.time + if(!supress_message) + visible_message("[user] has grabbed [src] from behind!") + else*///This is an example of how you can make special types of grabs simply based on direction. + if(!supress_message) + visible_message("[user] has grabbed [src] passively!") + + return G + +/mob/living/attack_slime(mob/living/simple_animal/slime/M) + if(!SSticker) + to_chat(M, "You cannot attack people before the game has started.") + return + + if(M.buckled) + if(M in buckled_mobs) + M.Feedstop() + return // can't attack while eating! + + if(HAS_TRAIT(src, TRAIT_PACIFISM)) + to_chat(M, "You don't want to hurt anyone!") + return FALSE + + if(stat != DEAD) + add_attack_logs(M, src, "Slime'd") + M.do_attack_animation(src) + visible_message("\The [M.name] glomps [src]!", "\The [M.name] glomps you!") + return TRUE + +/mob/living/attack_animal(mob/living/simple_animal/M) + M.face_atom(src) + if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0) + M.custom_emote(1, "[M.friendly] [src].") + return FALSE + if(HAS_TRAIT(M, TRAIT_PACIFISM)) + to_chat(M, "You don't want to hurt anyone!") + return FALSE + + if(M.attack_sound) + playsound(loc, M.attack_sound, 50, 1, 1) + M.do_attack_animation(src) + visible_message("\The [M] [M.attacktext] [src]!", \ + "\The [M] [M.attacktext] [src]!") + add_attack_logs(M, src, "Animal attacked") + return TRUE + +/mob/living/attack_larva(mob/living/carbon/alien/larva/L) + switch(L.a_intent) + if(INTENT_HELP) + visible_message("[L.name] rubs its head against [src].") + return 0 + + else + if(HAS_TRAIT(L, TRAIT_PACIFISM)) + to_chat(L, "You don't want to hurt anyone!") + return + + L.do_attack_animation(src) + if(prob(90)) + add_attack_logs(L, src, "Larva attacked") + visible_message("[L.name] bites [src]!", \ + "[L.name] bites [src]!") + playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1) + return 1 + else + visible_message("[L.name] has attempted to bite [src]!", \ + "[L.name] has attempted to bite [src]!") + return 0 + +/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M) + switch(M.a_intent) + if(INTENT_HELP) + visible_message("[M] caresses [src] with its scythe like arm.") + return FALSE + if(INTENT_GRAB) + grabbedby(M) + return FALSE + if(INTENT_HARM) + if(HAS_TRAIT(M, TRAIT_PACIFISM)) + to_chat(M, "You don't want to hurt anyone!") + return FALSE + M.do_attack_animation(src) + return TRUE + if(INTENT_DISARM) + M.do_attack_animation(src, ATTACK_EFFECT_DISARM) + return TRUE diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 8f5ed797b6cb..840f30c20ff7 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -1,74 +1,74 @@ -/mob/living - see_invisible = SEE_INVISIBLE_LIVING - pressure_resistance = 10 - - //Health and life related vars - var/maxHealth = 100 //Maximum health that should be possible. - var/health = 100 //A mob's health - - - //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS - var/bruteloss = 0 //Brutal damage caused by brute force (punching, being clubbed by a toolbox ect... this also accounts for pressure damage) - var/oxyloss = 0 //Oxygen depravation damage (no air in lungs) - var/toxloss = 0 //Toxic damage caused by being poisoned or radiated - var/fireloss = 0 //Burn damage caused by being way too hot, too cold or burnt. - var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims - var/staminaloss = 0 //Stamina damage, or exhaustion. You recover it slowly naturally, and are stunned if it gets too high. Holodeck and hallucinations deal this. - - - var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. - - //Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects. - var/incorporeal_move = 0 //0 is off, 1 is normal, 2 is for ninjas. - - var/now_pushing = null - - var/atom/movable/cameraFollow = null - - var/on_fire = 0 //The "Are we on fire?" var - var/fire_stacks = 0 //Tracks how many stacks of fire we have on, max is usually 20 - - var/implanting = 0 //Used for the mind-slave implant - var/floating = 0 - var/mob_size = MOB_SIZE_HUMAN - var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature.. - var/digestion_ratio = 1 //controls how quickly reagents metabolize; largely governered by species attributes. - - var/bloodcrawl = 0 //0 No blood crawling, 1 blood crawling, 2 blood crawling+mob devour - var/holder = null //The holder for blood crawling - - var/ventcrawler = 0 //0 No vent crawling, 1 vent crawling in the nude, 2 vent crawling always - var/list/icon/pipes_shown = list() - var/last_played_vent - - var/smoke_delay = 0 //used to prevent spam with smoke reagent reaction on mob. - - var/step_count = 0 - - var/list/butcher_results = null - - var/list/weather_immunities = list() - - var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them. - - var/gene_stability = DEFAULT_GENE_STABILITY - var/ignore_gene_stability = 0 - - var/obj/effect/proc_holder/ranged_ability //Any ranged ability the mob has, as a click override - - var/tesla_ignore = FALSE - - var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message" - var/list/emote_log = list() //like say_log but for emotes - - var/list/recent_tastes = list() - var/blood_volume = 0 //how much blood the mob has - hud_possible = list(HEALTH_HUD,STATUS_HUD,SPECIALROLE_HUD) - - var/list/status_effects //a list of all status effects the mob has - - var/deathgasp_on_death = FALSE - - var/stun_absorption = null //converted to a list of stun absorption sources this mob has when one is added - var/stam_regen_start_time = 0 //used to halt stamina regen temporarily - var/stam_paralyzed = FALSE //knocks you down \ No newline at end of file +/mob/living + see_invisible = SEE_INVISIBLE_LIVING + pressure_resistance = 10 + + //Health and life related vars + var/maxHealth = 100 //Maximum health that should be possible. + var/health = 100 //A mob's health + + + //Damage related vars, NOTE: THESE SHOULD ONLY BE MODIFIED BY PROCS + var/bruteloss = 0 //Brutal damage caused by brute force (punching, being clubbed by a toolbox ect... this also accounts for pressure damage) + var/oxyloss = 0 //Oxygen depravation damage (no air in lungs) + var/toxloss = 0 //Toxic damage caused by being poisoned or radiated + var/fireloss = 0 //Burn damage caused by being way too hot, too cold or burnt. + var/cloneloss = 0 //Damage caused by being cloned or ejected from the cloner early. slimes also deal cloneloss damage to victims + var/staminaloss = 0 //Stamina damage, or exhaustion. You recover it slowly naturally, and are stunned if it gets too high. Holodeck and hallucinations deal this. + + + var/last_special = 0 //Used by the resist verb, likely used to prevent players from bypassing next_move by logging in/out. + + //Allows mobs to move through dense areas without restriction. For instance, in space or out of holder objects. + var/incorporeal_move = 0 //0 is off, 1 is normal, 2 is for ninjas. + + var/now_pushing = null + + var/atom/movable/cameraFollow = null + + var/on_fire = 0 //The "Are we on fire?" var + var/fire_stacks = 0 //Tracks how many stacks of fire we have on, max is usually 20 + + var/implanting = 0 //Used for the mind-slave implant + var/floating = 0 + var/mob_size = MOB_SIZE_HUMAN + var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature.. + var/digestion_ratio = 1 //controls how quickly reagents metabolize; largely governered by species attributes. + + var/bloodcrawl = 0 //0 No blood crawling, 1 blood crawling, 2 blood crawling+mob devour + var/holder = null //The holder for blood crawling + + var/ventcrawler = 0 //0 No vent crawling, 1 vent crawling in the nude, 2 vent crawling always + var/list/icon/pipes_shown = list() + var/last_played_vent + + var/smoke_delay = 0 //used to prevent spam with smoke reagent reaction on mob. + + var/step_count = 0 + + var/list/butcher_results = null + + var/list/weather_immunities = list() + + var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them. + + var/gene_stability = DEFAULT_GENE_STABILITY + var/ignore_gene_stability = 0 + + var/obj/effect/proc_holder/ranged_ability //Any ranged ability the mob has, as a click override + + var/tesla_ignore = FALSE + + var/list/say_log = list() //a log of what we've said, plain text, no spans or junk, essentially just each individual "message" + var/list/emote_log = list() //like say_log but for emotes + + var/list/recent_tastes = list() + var/blood_volume = 0 //how much blood the mob has + hud_possible = list(HEALTH_HUD,STATUS_HUD,SPECIALROLE_HUD) + + var/list/status_effects //a list of all status effects the mob has + + var/deathgasp_on_death = FALSE + + var/stun_absorption = null //converted to a list of stun absorption sources this mob has when one is added + var/stam_regen_start_time = 0 //used to halt stamina regen temporarily + var/stam_paralyzed = FALSE //knocks you down diff --git a/code/modules/mob/living/logout.dm b/code/modules/mob/living/logout.dm index e1c33f89a7e4..fc343a4fc98d 100644 --- a/code/modules/mob/living/logout.dm +++ b/code/modules/mob/living/logout.dm @@ -1,13 +1,13 @@ -/mob/living/Logout() - update_z(null) - if(ranged_ability && client) - ranged_ability.remove_mousepointer(client) - ..() - if(mind) - if(!key) //key and mind have become seperated. I believe this is for when a staff member aghosts. - mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. - //This causes instant sleep and tags a player as SSD. See life.dm for furthering SSD. - if(mind.active) - Sleeping(2) - player_logged = 1 - last_logout = world.time \ No newline at end of file +/mob/living/Logout() + update_z(null) + if(ranged_ability && client) + ranged_ability.remove_mousepointer(client) + ..() + if(mind) + if(!key) //key and mind have become seperated. I believe this is for when a staff member aghosts. + mind.active = 0 //This is to stop say, a mind.transfer_to call on a corpse causing a ghost to re-enter its body. + //This causes instant sleep and tags a player as SSD. See life.dm for furthering SSD. + if(mind.active) + Sleeping(2) + player_logged = 1 + last_logout = world.time diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index be5187fac5b5..eeaed10b9c5f 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -1,471 +1,477 @@ -var/list/department_radio_keys = list( - ":r" = "right ear", "#r" = "right ear", ".r" = "right ear", - ":l" = "left ear", "#l" = "left ear", ".l" = "left ear", - ":i" = "intercom", "#i" = "intercom", ".i" = "intercom", - ":h" = "department", "#h" = "department", ".h" = "department", - ":+" = "special", "#+" = "special", ".+" = "special", //activate radio-specific special functions - ":c" = "Command", "#c" = "Command", ".c" = "Command", - ":n" = "Science", "#n" = "Science", ".n" = "Science", - ":m" = "Medical", "#m" = "Medical", ".m" = "Medical", - ":e" = "Engineering", "#e" = "Engineering", ".e" = "Engineering", - ":s" = "Security", "#s" = "Security", ".s" = "Security", - ":w" = "whisper", "#w" = "whisper", ".w" = "whisper", - ":t" = "Syndicate", "#t" = "Syndicate", ".t" = "Syndicate", - ":u" = "Supply", "#u" = "Supply", ".u" = "Supply", - ":z" = "Service", "#z" = "Service", ".z" = "Service", - ":p" = "AI Private", "#p" = "AI Private", ".p" = "AI Private", - ":x" = "cords", "#x" = "cords", ".x" = "cords", - - ":R" = "right ear", "#R" = "right ear", ".R" = "right ear", - ":L" = "left ear", "#L" = "left ear", ".L" = "left ear", - ":I" = "intercom", "#I" = "intercom", ".I" = "intercom", - ":H" = "department", "#H" = "department", ".H" = "department", - ":C" = "Command", "#C" = "Command", ".C" = "Command", - ":N" = "Science", "#N" = "Science", ".N" = "Science", - ":M" = "Medical", "#M" = "Medical", ".M" = "Medical", - ":E" = "Engineering", "#E" = "Engineering", ".E" = "Engineering", - ":S" = "Security", "#S" = "Security", ".S" = "Security", - ":W" = "whisper", "#W" = "whisper", ".W" = "whisper", - ":T" = "Syndicate", "#T" = "Syndicate", ".T" = "Syndicate", - ":U" = "Supply", "#U" = "Supply", ".U" = "Supply", - ":Z" = "Service", "#Z" = "Service", ".Z" = "Service", - ":P" = "AI Private", "#P" = "AI Private", ".P" = "AI Private", - ":$" = "Response Team", "#$" = "Response Team", ".$" = "Response Team", - ":-" = "Special Ops", "#-" = "Special Ops", ".-" = "Special Ops", - ":_" = "SyndTeam", "#_" = "SyndTeam", "._" = "SyndTeam", - ":X" = "cords", "#X" = "cords", ".X" = "cords" -) - - -var/list/channel_to_radio_key = new -proc/get_radio_key_from_channel(var/channel) - var/key = channel_to_radio_key[channel] - if(!key) - for(var/radio_key in department_radio_keys) - if(department_radio_keys[radio_key] == channel) - key = radio_key - break - if(!key) - key = "" - channel_to_radio_key[channel] = key - - return key - -/mob/living/proc/binarycheck() - return FALSE - -/mob/proc/get_default_language() - return null - -/mob/living/get_default_language() - return default_language - -/mob/living/proc/handle_speech_problems(list/message_pieces, var/verb) - var/robot = isSynthetic() - for(var/datum/multilingual_say_piece/S in message_pieces) - if(S.speaking && S.speaking.flags & NO_STUTTER) - continue - - if((HULK in mutations) && health >= 25) - S.message = "[uppertext(S.message)]!!!" - verb = pick("yells", "roars", "hollers") - - if(slurring) - if(robot) - S.message = slur(S.message, list("@", "!", "#", "$", "%", "&", "?")) - else - S.message = slur(S.message) - verb = "slurs" - - if(stuttering) - if(robot) - S.message = robostutter(S.message) - else - S.message = stutter(S.message) - verb = "stammers" - - if(cultslurring) - S.message = cultslur(S.message) - verb = "slurs" - - if(!IsVocal()) - S.message = "" - return list("verb" = verb) - -/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - switch(message_mode) - if("whisper") //all mobs can whisper by default - whisper_say(message_pieces) - return 1 - return 0 - -/mob/living/proc/handle_speech_sound() - var/list/returns[2] - returns[1] = null - returns[2] = null - return returns - - -/mob/living/say(var/message, var/verb = "says", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) - if(client) - if(client.prefs.muted & MUTE_IC) - to_chat(src, "You cannot speak in IC (Muted).") - return - - if(sanitize) - message = trim_strip_html_properly(message) - - if(stat) - if(stat == DEAD) - return say_dead(message) - return - - var/message_mode = parse_message_mode(message, "headset") - - if(copytext(message, 1, 2) == "*") - return emote(copytext(message, 2)) - - //parse the radio code and consume it - if(message_mode) - if(message_mode == "headset") - message = copytext(message, 2) //it would be really nice if the parse procs could do this for us. - else - message = copytext(message, 3) - - message = trim_left(message) - - //parse the language code and consume it - var/list/message_pieces = parse_languages(message) - if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages - var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting - S.speaking.broadcast(src, S.message) - return 1 - - - if(!LAZYLEN(message_pieces)) - log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]")) - return 0 - - if(message_mode == "cords") - if(iscarbon(src)) - var/mob/living/carbon/C = src - var/obj/item/organ/internal/vocal_cords/V = C.get_int_organ(/obj/item/organ/internal/vocal_cords) - if(V && V.can_speak_with()) - C.say(V.handle_speech(message), sanitize = FALSE, ignore_speech_problems = TRUE, ignore_atmospherics = TRUE) - V.speak_with(message) //words come before actions - return 1 - - var/datum/multilingual_say_piece/first_piece = message_pieces[1] - verb = say_quote(message, first_piece.speaking) - - if(is_muzzled()) - var/obj/item/clothing/mask/muzzle/G = wear_mask - if(G.mute == MUZZLE_MUTE_ALL) //if the mask is supposed to mute you completely or just muffle you - to_chat(src, "You're muzzled and cannot speak!") - return - else if(G.mute == MUZZLE_MUTE_MUFFLE) - muffledspeech_all(message_pieces) - verb = "mumbles" - - if(!ignore_speech_problems) - var/list/hsp = handle_speech_problems(message_pieces, verb) - verb = hsp["verb"] - - - var/list/used_radios = list() - if(handle_message_mode(message_mode, message_pieces, verb, used_radios)) - return 1 - - - var/list/handle_v = handle_speech_sound() - var/sound/speech_sound = handle_v[1] - var/sound_vol = handle_v[2] - - var/italics = 0 - var/message_range = world.view - - //speaking into radios - if(used_radios.len) - italics = 1 - message_range = 1 - if(first_piece.speaking) - message_range = first_piece.speaking.get_talkinto_msg_range(message) - - var/msg - if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG)) - msg = "[src] talks into [used_radios[1]]" - - if(msg) - for(var/mob/living/M in hearers(5, src) - src) - M.show_message(msg) - - if(speech_sound) - sound_vol *= 0.5 - - - var/turf/T = get_turf(src) - var/list/listening = list() - var/list/listening_obj = list() - - if(T) - //make sure the air can transmit speech - speaker's side - var/datum/gas_mixture/environment = T.return_air() - var/pressure = environment ? environment.return_pressure() : 0 - if(!ignore_atmospherics) - if(pressure < SOUND_MINIMUM_PRESSURE) - message_range = 1 - - if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet - italics = TRUE - sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact - - var/list/hear = hear(message_range, T) - var/list/hearturfs = list() - - for(var/I in hear) - if(ismob(I)) - var/mob/M = I - listening += M - hearturfs += get_turf(M) - for(var/obj/O in M.contents) - listening_obj |= O - if(isobj(I)) - var/obj/O = I - hearturfs += get_turf(O) - listening_obj |= O - - for(var/mob/M in GLOB.player_list) - if(!M.client) - continue - - if(isnewplayer(M)) - continue - - if(isobserver(M)) - if(M.get_preference(CHAT_GHOSTEARS) && client) // The client check is so that ghosts don't have to listen to mice. - listening |= M - continue - - if(message_range < world.view && (get_dist(T, M) <= world.view)) - listening |= M - continue - - if(get_turf(M) in hearturfs) - listening |= M - - var/list/speech_bubble_recipients = list() - var/speech_bubble_test = say_test(message) - - for(var/mob/M in listening) - M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol) - if(M.client) - speech_bubble_recipients.Add(M.client) - spawn(0) - if(loc && !isturf(loc)) - var/atom/A = loc //Non-turf, let it handle the speech bubble - A.speech_bubble("hR[speech_bubble_test]", A, speech_bubble_recipients) - else //Turf, leave speech bubbles to the mob - speech_bubble("h[speech_bubble_test]", src, speech_bubble_recipients) - - for(var/obj/O in listening_obj) - spawn(0) - if(O) //It's possible that it could be deleted in the meantime. - O.hear_talk(src, message_pieces, verb) - - //Log of what we've said, plain message, no spans or junk - say_log += message - log_say(message, src) - return 1 - -/obj/effect/speech_bubble - var/mob/parent - -/mob/living/proc/GetVoice() - return name - -/mob/living/emote(act, type, message, force) //emote code is terrible, this is so that anything that isn't already snowflaked to shit can call the parent and handle emoting sanely - if(client) - if(client.prefs.muted & MUTE_IC) - to_chat(src, "You cannot speak in IC (Muted).") - return - - if(stat) - return 0 - - if(..()) - return 1 - - if(act && type && message) //parent call - log_emote(message, src) - - for(var/mob/M in GLOB.dead_mob_list) - if(!M.client) - continue //skip monkeys and leavers - - if(isnewplayer(M)) - continue - - if(isobserver(M) && M.get_preference(CHAT_GHOSTSIGHT) && !(M in viewers(src, null)) && client) // The client check makes sure people with ghost sight don't get spammed by simple mobs emoting. - M.show_message(message) - - switch(type) - if(1) //Visible - visible_message(message) - return 1 - if(2) //Audible - audible_message(message) - return 1 - - else //everything else failed, emote is probably invalid - if(act == "help") - return //except help, because help is handled individually - to_chat(src, "Unusable emote '[act]'. Say *help for a list.") - -/mob/living/whisper(message as text) - message = trim_strip_html_properly(message) - - //parse the language code and consume it - var/list/message_pieces = parse_languages(message) - if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages - var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting - S.speaking.broadcast(src, S.message) - return 1 - - whisper_say(message_pieces) - -// for weird circumstances where you're inside an atom that is also you, like pai's -/mob/living/proc/get_whisper_loc() - return src - -/mob/living/proc/whisper_say(list/message_pieces, verb = "whispers") - if(client) - if(client.prefs.muted & MUTE_IC) - to_chat(src, "You cannot speak in IC (Muted).") - return - - if(stat) - if(stat == DEAD) - return say_dead(message_pieces) - return - - if(is_muzzled()) - if(istype(wear_mask, /obj/item/clothing/mask/muzzle/tapegag)) //just for tape - to_chat(src, "Your mouth is taped and you cannot speak!") - else - to_chat(src, "You're muzzled and cannot speak!") - return - - log_whisper(multilingual_to_message(message_pieces), src) - - var/message_range = 1 - var/eavesdropping_range = 2 - var/watching_range = 5 - var/italics = 1 - var/adverb_added = FALSE - var/not_heard //the message displayed to people who could not hear the whispering - - var/datum/multilingual_say_piece/first_piece = message_pieces[1] - if(first_piece.speaking) - if(first_piece.speaking.whisper_verb) - verb = first_piece.speaking.whisper_verb - not_heard = "[verb] something" - else - var/adverb = pick("quietly", "softly") - adverb_added = TRUE - verb = "[first_piece.speaking.speech_verb] [adverb]" - not_heard = "[first_piece.speaking.speech_verb] something [adverb]" - else - not_heard = "[verb] something" - - var/list/hsp = handle_speech_problems(message_pieces, verb) - verb = hsp["verb"] - if(verb == "yells loudly") - verb = "slurs emphatically" - else if(!adverb_added) - var/adverb = pick("quietly", "softly") - verb = "[verb] [adverb]" - - var/atom/whisper_loc = get_whisper_loc() - var/list/listening = hear(message_range, whisper_loc) - listening |= src - - var/list/hearturfs = list() - - // Pass whispers on to anything inside the immediate listeners. - // This comes before the ghosts do so that ghosts don't act as whisper relays - for(var/atom/L in listening) - if(ismob(L)) - for(var/mob/C in L.contents) - if(isliving(C)) - listening += C - hearturfs += get_turf(L) - if(isobj(L)) - hearturfs += get_turf(L) - - // Loop through all players to see if they need to hear it. - for(var/mob/M in GLOB.player_list) - if(!M.client) - continue - - if(isnewplayer(M)) - continue - - if(isobserver(M)) - if(M.get_preference(CHAT_GHOSTEARS)) // The client check is so that ghosts don't have to listen to mice. - listening |= M - continue - - if(message_range < world.view && (get_dist(whisper_loc, M) <= world.view)) - listening |= M - continue - - if(get_turf(M) in hearturfs) - listening |= M - - //pass on the message to objects that can hear us. - for(var/obj/O in view(message_range, whisper_loc)) - spawn(0) - if(O) - O.hear_talk(src, message_pieces, verb) - - var/list/eavesdropping = hearers(eavesdropping_range, whisper_loc) - eavesdropping -= src - eavesdropping -= listening - - var/list/watching = hearers(watching_range, whisper_loc) - watching -= src - watching -= listening - watching -= eavesdropping - - //now mobs - var/list/speech_bubble_recipients = list() - var/speech_bubble_test = say_test(multilingual_to_message(message_pieces)) - - for(var/mob/M in listening) - M.hear_say(message_pieces, verb, italics, src) - if(M.client) - speech_bubble_recipients.Add(M.client) - - if(eavesdropping.len) - stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. - for(var/mob/M in eavesdropping) - M.hear_say(message_pieces, verb, italics, src) - if(M.client) - speech_bubble_recipients.Add(M.client) - - spawn(0) - var/image/I = image('icons/mob/talk.dmi', src, "h[speech_bubble_test]", MOB_LAYER + 1) - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - flick_overlay(I, speech_bubble_recipients, 30) - - if(watching.len) - var/rendered = "[name] [not_heard]." - for(var/mob/M in watching) - M.show_message(rendered, 2) - - return 1 - -/mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, MOB_LAYER + 1) - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - flick_overlay(I, bubble_recipients, 30) +GLOBAL_LIST_INIT(department_radio_keys, list( + ":r" = "right ear", "#r" = "right ear", ".r" = "right ear", + ":l" = "left ear", "#l" = "left ear", ".l" = "left ear", + ":i" = "intercom", "#i" = "intercom", ".i" = "intercom", + ":h" = "department", "#h" = "department", ".h" = "department", + ":+" = "special", "#+" = "special", ".+" = "special", //activate radio-specific special functions + ":c" = "Command", "#c" = "Command", ".c" = "Command", + ":n" = "Science", "#n" = "Science", ".n" = "Science", + ":m" = "Medical", "#m" = "Medical", ".m" = "Medical", + ":e" = "Engineering", "#e" = "Engineering", ".e" = "Engineering", + ":s" = "Security", "#s" = "Security", ".s" = "Security", + ":w" = "whisper", "#w" = "whisper", ".w" = "whisper", + ":t" = "Syndicate", "#t" = "Syndicate", ".t" = "Syndicate", + ":u" = "Supply", "#u" = "Supply", ".u" = "Supply", + ":z" = "Service", "#z" = "Service", ".z" = "Service", + ":p" = "AI Private", "#p" = "AI Private", ".p" = "AI Private", + ":x" = "cords", "#x" = "cords", ".x" = "cords", + + ":R" = "right ear", "#R" = "right ear", ".R" = "right ear", + ":L" = "left ear", "#L" = "left ear", ".L" = "left ear", + ":I" = "intercom", "#I" = "intercom", ".I" = "intercom", + ":H" = "department", "#H" = "department", ".H" = "department", + ":C" = "Command", "#C" = "Command", ".C" = "Command", + ":N" = "Science", "#N" = "Science", ".N" = "Science", + ":M" = "Medical", "#M" = "Medical", ".M" = "Medical", + ":E" = "Engineering", "#E" = "Engineering", ".E" = "Engineering", + ":S" = "Security", "#S" = "Security", ".S" = "Security", + ":W" = "whisper", "#W" = "whisper", ".W" = "whisper", + ":T" = "Syndicate", "#T" = "Syndicate", ".T" = "Syndicate", + ":U" = "Supply", "#U" = "Supply", ".U" = "Supply", + ":Z" = "Service", "#Z" = "Service", ".Z" = "Service", + ":P" = "AI Private", "#P" = "AI Private", ".P" = "AI Private", + ":$" = "Response Team", "#$" = "Response Team", ".$" = "Response Team", + ":-" = "Special Ops", "#-" = "Special Ops", ".-" = "Special Ops", + ":_" = "SyndTeam", "#_" = "SyndTeam", "._" = "SyndTeam", + ":X" = "cords", "#X" = "cords", ".X" = "cords" +)) + +GLOBAL_LIST_EMPTY(channel_to_radio_key) + +proc/get_radio_key_from_channel(var/channel) + var/key = GLOB.channel_to_radio_key[channel] + if(!key) + for(var/radio_key in GLOB.department_radio_keys) + if(GLOB.department_radio_keys[radio_key] == channel) + key = radio_key + break + if(!key) + key = "" + GLOB.channel_to_radio_key[channel] = key + + return key + +/mob/living/proc/binarycheck() + return FALSE + +/mob/proc/get_default_language() + return null + +/mob/living/get_default_language() + return default_language + +/mob/living/proc/handle_speech_problems(list/message_pieces, var/verb) + var/robot = isSynthetic() + for(var/datum/multilingual_say_piece/S in message_pieces) + if(S.speaking && S.speaking.flags & NO_STUTTER) + continue + + if((HULK in mutations) && health >= 25) + S.message = "[uppertext(S.message)]!!!" + verb = pick("yells", "roars", "hollers") + + if(slurring) + if(robot) + S.message = slur(S.message, list("@", "!", "#", "$", "%", "&", "?")) + else + S.message = slur(S.message) + verb = "slurs" + + if(stuttering) + if(robot) + S.message = robostutter(S.message) + else + S.message = stutter(S.message) + verb = "stammers" + + if(cultslurring) + S.message = cultslur(S.message) + verb = "slurs" + + if(!IsVocal()) + S.message = "" + return list("verb" = verb) + +/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + switch(message_mode) + if("whisper") //all mobs can whisper by default + whisper_say(message_pieces) + return 1 + return 0 + +/mob/living/proc/handle_speech_sound() + var/list/returns[3] + returns[1] = null + returns[2] = null + returns[3] = null + return returns + + +/mob/living/say(var/message, var/verb = "says", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) + if(client) + if(client.prefs.muted & MUTE_IC) + to_chat(src, "You cannot speak in IC (Muted).") + return + + if(sanitize) + message = trim_strip_html_properly(message) + + if(stat) + if(stat == DEAD) + return say_dead(message) + return + + var/message_mode = parse_message_mode(message, "headset") + + if(copytext(message, 1, 2) == "*") + return emote(copytext(message, 2)) + + //parse the radio code and consume it + if(message_mode) + if(message_mode == "headset") + message = copytext(message, 2) //it would be really nice if the parse procs could do this for us. + else + message = copytext(message, 3) + + message = trim_left(message) + + //parse the language code and consume it + var/list/message_pieces = parse_languages(message) + if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages + var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting + S.speaking.broadcast(src, S.message) + return 1 + + + if(!LAZYLEN(message_pieces)) + log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]")) + return 0 + + if(message_mode == "cords") + if(iscarbon(src)) + var/mob/living/carbon/C = src + var/obj/item/organ/internal/vocal_cords/V = C.get_int_organ(/obj/item/organ/internal/vocal_cords) + if(V && V.can_speak_with()) + C.say(V.handle_speech(message), sanitize = FALSE, ignore_speech_problems = TRUE, ignore_atmospherics = TRUE) + V.speak_with(message) //words come before actions + return 1 + + var/datum/multilingual_say_piece/first_piece = message_pieces[1] + verb = say_quote(message, first_piece.speaking) + + if(is_muzzled()) + var/obj/item/clothing/mask/muzzle/G = wear_mask + if(G.mute == MUZZLE_MUTE_ALL) //if the mask is supposed to mute you completely or just muffle you + to_chat(src, "You're muzzled and cannot speak!") + return + else if(G.mute == MUZZLE_MUTE_MUFFLE) + muffledspeech_all(message_pieces) + verb = "mumbles" + + if(!ignore_speech_problems) + var/list/hsp = handle_speech_problems(message_pieces, verb) + verb = hsp["verb"] + + + var/list/used_radios = list() + if(handle_message_mode(message_mode, message_pieces, verb, used_radios)) + return 1 + + + var/list/handle_v = handle_speech_sound() + var/sound/speech_sound = handle_v[1] + var/sound_vol = handle_v[2] + var/sound_frequency = handle_v[3] + + var/italics = 0 + var/message_range = world.view + + //speaking into radios + if(used_radios.len) + italics = 1 + message_range = 1 + if(first_piece.speaking) + message_range = first_piece.speaking.get_talkinto_msg_range(message) + + var/msg + if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG)) + msg = "[src] talks into [used_radios[1]]" + + if(msg) + for(var/mob/living/M in hearers(5, src) - src) + M.show_message(msg) + + if(speech_sound) + sound_vol *= 0.5 + + + var/turf/T = get_turf(src) + var/list/listening = list() + var/list/listening_obj = list() + + if(T) + //make sure the air can transmit speech - speaker's side + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment ? environment.return_pressure() : 0 + if(!ignore_atmospherics) + if(pressure < SOUND_MINIMUM_PRESSURE) + message_range = 1 + + if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet + italics = TRUE + sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact + + var/list/hear = hear(message_range, T) + var/list/hearturfs = list() + + for(var/I in hear) + if(ismob(I)) + var/mob/M = I + listening += M + hearturfs += get_turf(M) + for(var/obj/O in M.contents) + listening_obj |= O + if(isobj(I)) + var/obj/O = I + hearturfs += get_turf(O) + listening_obj |= O + + for(var/mob/M in GLOB.player_list) + if(!M.client) + continue + + if(isnewplayer(M)) + continue + + if(isobserver(M)) + if(M.get_preference(CHAT_GHOSTEARS) && client) // The client check is so that ghosts don't have to listen to mice. + listening |= M + continue + + if(message_range < world.view && (get_dist(T, M) <= world.view)) + listening |= M + continue + + if(get_turf(M) in hearturfs) + listening |= M + + var/list/speech_bubble_recipients = list() + var/speech_bubble_test = say_test(message) + + for(var/mob/M in listening) + M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol, sound_frequency) + if(M.client) + speech_bubble_recipients.Add(M.client) + spawn(0) + if(loc && !isturf(loc)) + var/atom/A = loc //Non-turf, let it handle the speech bubble + A.speech_bubble("hR[speech_bubble_test]", A, speech_bubble_recipients) + else //Turf, leave speech bubbles to the mob + speech_bubble("h[speech_bubble_test]", src, speech_bubble_recipients) + + for(var/obj/O in listening_obj) + spawn(0) + if(O) //It's possible that it could be deleted in the meantime. + O.hear_talk(src, message_pieces, verb) + + //Log of what we've said, plain message, no spans or junk + say_log += message + create_log(SAY_LOG, message) // TODO after #13047: Include the channel + log_say(message, src) + return 1 + +/obj/effect/speech_bubble + var/mob/parent + +/mob/living/proc/GetVoice() + return name + +/mob/living/emote(act, type, message, force) //emote code is terrible, this is so that anything that isn't already snowflaked to shit can call the parent and handle emoting sanely + if(client) + if(client.prefs.muted & MUTE_IC) + to_chat(src, "You cannot speak in IC (Muted).") + return + + if(stat) + return 0 + + if(..()) + return 1 + + if(act && type && message) //parent call + log_emote(message, src) + + for(var/mob/M in GLOB.dead_mob_list) + if(!M.client) + continue //skip monkeys and leavers + + if(isnewplayer(M)) + continue + + if(isobserver(M) && M.get_preference(CHAT_GHOSTSIGHT) && !(M in viewers(src, null)) && client) // The client check makes sure people with ghost sight don't get spammed by simple mobs emoting. + M.show_message(message) + + switch(type) + if(1) //Visible + visible_message(message) + return 1 + if(2) //Audible + audible_message(message) + return 1 + + else //everything else failed, emote is probably invalid + if(act == "help") + return //except help, because help is handled individually + to_chat(src, "Unusable emote '[act]'. Say *help for a list.") + +/mob/living/whisper(message as text) + message = trim_strip_html_properly(message) + + //parse the language code and consume it + var/list/message_pieces = parse_languages(message) + if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages + var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting + S.speaking.broadcast(src, S.message) + return 1 + + whisper_say(message_pieces) + +// for weird circumstances where you're inside an atom that is also you, like pai's +/mob/living/proc/get_whisper_loc() + return src + +/mob/living/proc/whisper_say(list/message_pieces, verb = "whispers") + if(client) + if(client.prefs.muted & MUTE_IC) + to_chat(src, "You cannot speak in IC (Muted).") + return + + if(stat) + if(stat == DEAD) + return say_dead(message_pieces) + return + + if(is_muzzled()) + if(istype(wear_mask, /obj/item/clothing/mask/muzzle/tapegag)) //just for tape + to_chat(src, "Your mouth is taped and you cannot speak!") + else + to_chat(src, "You're muzzled and cannot speak!") + return + + var/message = multilingual_to_message(message_pieces) + + say_log += "whisper: [message]" + log_whisper(message, src) + create_log(SAY_LOG, "WHISPER: [message]") + var/message_range = 1 + var/eavesdropping_range = 2 + var/watching_range = 5 + var/italics = 1 + var/adverb_added = FALSE + var/not_heard //the message displayed to people who could not hear the whispering + + var/datum/multilingual_say_piece/first_piece = message_pieces[1] + if(first_piece.speaking) + if(first_piece.speaking.whisper_verb) + verb = first_piece.speaking.whisper_verb + not_heard = "[verb] something" + else + var/adverb = pick("quietly", "softly") + adverb_added = TRUE + verb = "[first_piece.speaking.speech_verb] [adverb]" + not_heard = "[first_piece.speaking.speech_verb] something [adverb]" + else + not_heard = "[verb] something" + + var/list/hsp = handle_speech_problems(message_pieces, verb) + verb = hsp["verb"] + if(verb == "yells loudly") + verb = "slurs emphatically" + else if(!adverb_added) + var/adverb = pick("quietly", "softly") + verb = "[verb] [adverb]" + + var/atom/whisper_loc = get_whisper_loc() + var/list/listening = hear(message_range, whisper_loc) + listening |= src + + var/list/hearturfs = list() + + // Pass whispers on to anything inside the immediate listeners. + // This comes before the ghosts do so that ghosts don't act as whisper relays + for(var/atom/L in listening) + if(ismob(L)) + for(var/mob/C in L.contents) + if(isliving(C)) + listening += C + hearturfs += get_turf(L) + if(isobj(L)) + hearturfs += get_turf(L) + + // Loop through all players to see if they need to hear it. + for(var/mob/M in GLOB.player_list) + if(!M.client) + continue + + if(isnewplayer(M)) + continue + + if(isobserver(M)) + if(M.get_preference(CHAT_GHOSTEARS)) // The client check is so that ghosts don't have to listen to mice. + listening |= M + continue + + if(message_range < world.view && (get_dist(whisper_loc, M) <= world.view)) + listening |= M + continue + + if(get_turf(M) in hearturfs) + listening |= M + + //pass on the message to objects that can hear us. + for(var/obj/O in view(message_range, whisper_loc)) + spawn(0) + if(O) + O.hear_talk(src, message_pieces, verb) + + var/list/eavesdropping = hearers(eavesdropping_range, whisper_loc) + eavesdropping -= src + eavesdropping -= listening + + var/list/watching = hearers(watching_range, whisper_loc) + watching -= src + watching -= listening + watching -= eavesdropping + + //now mobs + var/list/speech_bubble_recipients = list() + var/speech_bubble_test = say_test(message) + + for(var/mob/M in listening) + M.hear_say(message_pieces, verb, italics, src) + if(M.client) + speech_bubble_recipients.Add(M.client) + + if(eavesdropping.len) + stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less. + for(var/mob/M in eavesdropping) + M.hear_say(message_pieces, verb, italics, src) + if(M.client) + speech_bubble_recipients.Add(M.client) + + spawn(0) + var/image/I = image('icons/mob/talk.dmi', src, "h[speech_bubble_test]", MOB_LAYER + 1) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + flick_overlay(I, speech_bubble_recipients, 30) + + if(watching.len) + var/rendered = "[name] [not_heard]." + for(var/mob/M in watching) + M.show_message(rendered, 2) + + return 1 + +/mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + var/image/I = image('icons/mob/talk.dmi', bubble_loc, bubble_state, MOB_LAYER + 1) + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + flick_overlay(I, bubble_recipients, 30) diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 8ae4fc6a4196..ddc96a7948fd 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1,1323 +1,1323 @@ -var/list/ai_list = list() -var/list/ai_verbs_default = list( - /mob/living/silicon/ai/proc/announcement, - /mob/living/silicon/ai/proc/ai_announcement_text, - /mob/living/silicon/ai/proc/ai_call_shuttle, - /mob/living/silicon/ai/proc/ai_camera_track, - /mob/living/silicon/ai/proc/ai_camera_list, - /mob/living/silicon/ai/proc/ai_goto_location, - /mob/living/silicon/ai/proc/ai_remove_location, - /mob/living/silicon/ai/proc/ai_hologram_change, - /mob/living/silicon/ai/proc/ai_network_change, - /mob/living/silicon/ai/proc/ai_roster, - /mob/living/silicon/ai/proc/ai_statuschange, - /mob/living/silicon/ai/proc/ai_store_location, - /mob/living/silicon/ai/proc/control_integrated_radio, - /mob/living/silicon/ai/proc/core, - /mob/living/silicon/ai/proc/pick_icon, - /mob/living/silicon/ai/proc/sensor_mode, - /mob/living/silicon/ai/proc/show_laws_verb, - /mob/living/silicon/ai/proc/toggle_acceleration, - /mob/living/silicon/ai/proc/toggle_camera_light, - /mob/living/silicon/ai/proc/botcall, - /mob/living/silicon/ai/proc/change_arrival_message -) - -//Not sure why this is necessary... -/proc/AutoUpdateAI(obj/subject) - var/is_in_use = 0 - if(subject!=null) - for(var/A in ai_list) - var/mob/living/silicon/ai/M = A - if((M.client && M.machine == subject)) - is_in_use = 1 - subject.attack_ai(M) - return is_in_use - -/mob/living/silicon/ai - name = "AI" - icon = 'icons/mob/ai.dmi'// - icon_state = "ai" - move_resist = MOVE_FORCE_NORMAL - density = 1 - status_flags = CANSTUN|CANPARALYSE|CANPUSH - mob_size = MOB_SIZE_LARGE - sight = SEE_TURFS | SEE_MOBS | SEE_OBJS - see_in_dark = 8 - can_strip = 0 - var/list/network = list("SS13","Telecomms","Research Outpost","Mining Outpost") - var/obj/machinery/camera/current = null - var/list/connected_robots = list() - var/aiRestorePowerRoutine = 0 - //var/list/laws = list() - var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list()) - var/viewalerts = 0 - var/icon/holo_icon//Default is assigned when AI is created. - var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye. - var/obj/item/pda/silicon/ai/aiPDA = null - var/obj/item/multitool/aiMulti = null - var/custom_sprite = 0 //For our custom sprites - var/custom_hologram = 0 //For our custom holograms - - var/obj/item/radio/headset/heads/ai_integrated/aiRadio = null - - //MALFUNCTION - var/datum/module_picker/malf_picker - var/list/datum/AI_Module/current_modules = list() - var/can_dominate_mechs = 0 - var/shunted = 0 //1 if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead - - var/control_disabled = 0 // Set to 1 to stop AI from interacting via Click() -- TLE - var/malfhacking = 0 // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite - var/malf_cooldown = 0 //Cooldown var for malf modules, stores a worldtime + cooldown - - var/obj/machinery/power/apc/malfhack = null - var/explosive = 0 //does the AI explode when it dies? - - var/mob/living/silicon/ai/parent = null - var/camera_light_on = 0 - var/list/obj/machinery/camera/lit_cameras = list() - - var/datum/trackable/track = new() - - var/last_paper_seen = null - var/can_shunt = 1 - var/last_announcement = "" - var/datum/announcement/priority/announcement - var/mob/living/simple_animal/bot/Bot - var/turf/waypoint //Holds the turf of the currently selected waypoint. - var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking. - var/apc_override = FALSE //hack for letting the AI use its APC even when visionless - var/nuking = 0 - var/obj/machinery/doomsday_device/doomsday_device - - var/obj/machinery/hologram/holopad/holo = null - var/mob/camera/aiEye/eyeobj - var/sprint = 10 - var/cooldown = 0 - var/acceleration = 1 - var/tracking = 0 //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed. - - var/obj/machinery/camera/portable/builtInCamera - - var/obj/structure/AIcore/deactivated/linked_core //For exosuit control - - var/arrivalmsg = "$name, $rank, has arrived on the station." - - var/multicam_allowed = FALSE - var/multicam_on = FALSE - var/obj/screen/movable/pic_in_pic/ai/master_multicam - var/list/multicam_screens = list() - var/list/all_eyes = list() - var/max_multicams = 6 - -/mob/living/silicon/ai/proc/add_ai_verbs() - verbs |= ai_verbs_default - verbs |= silicon_subsystems - -/mob/living/silicon/ai/proc/remove_ai_verbs() - verbs -= ai_verbs_default - verbs -= silicon_subsystems - -/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/mmi/B, var/safety = 0) - announcement = new() - announcement.title = "A.I. Announcement" - announcement.announcement_type = "A.I. Announcement" - announcement.announcer = name - announcement.newscast = 0 - - var/list/possibleNames = GLOB.ai_names - - var/pickedName = null - while(!pickedName) - pickedName = pick(GLOB.ai_names) - for(var/mob/living/silicon/ai/A in GLOB.mob_list) - if(A.real_name == pickedName && possibleNames.len > 1) //fixing the theoretically possible infinite loop - possibleNames -= pickedName - pickedName = null - - aiPDA = new/obj/item/pda/silicon/ai(src) - rename_character(null, pickedName) - anchored = 1 - canmove = 0 - density = 1 - loc = loc - - holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1")) - - proc_holder_list = new() - - if(L) - if(istype(L, /datum/ai_laws)) - laws = L - else - make_laws() - - verbs += /mob/living/silicon/ai/proc/show_laws_verb - - aiMulti = new(src) - aiRadio = new(src) - common_radio = aiRadio - aiRadio.myAi = src - additional_law_channels["Binary"] = ":b " - additional_law_channels["Holopad"] = ":h" - - aiCamera = new/obj/item/camera/siliconcam/ai_camera(src) - - if(isturf(loc)) - add_ai_verbs(src) - - //Languages - add_language("Robot Talk", 1) - add_language("Galactic Common", 1) - add_language("Sol Common", 1) - add_language("Tradeband", 1) - add_language("Neo-Russkiya", 0) - add_language("Gutter", 0) - add_language("Sinta'unathi", 0) - add_language("Siik'tajr", 0) - add_language("Canilunzt", 0) - add_language("Skrellian", 0) - add_language("Vox-pidgin", 0) - add_language("Orluum", 0) - add_language("Rootspeak", 0) - add_language("Trinary", 1) - add_language("Chittin", 0) - add_language("Bubblish", 0) - add_language("Clownish", 0) - - if(!safety)//Only used by AIize() to successfully spawn an AI. - if(!B)//If there is no player/brain inside. - new/obj/structure/AIcore/deactivated(loc)//New empty terminal. - qdel(src)//Delete AI. - return - else - if(B.brainmob.mind) - B.brainmob.mind.transfer_to(src) - - on_mob_init() - - spawn(5) - new /obj/machinery/ai_powersupply(src) - - create_eye() - - builtInCamera = new /obj/machinery/camera/portable(src) - builtInCamera.c_tag = name - builtInCamera.network = list("SS13") - - ai_list += src - GLOB.shuttle_caller_list += src - ..() - -/mob/living/silicon/ai/proc/on_mob_init() - to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).") - to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.") - to_chat(src, "While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.") - to_chat(src, "To use something, simply click on it.") - to_chat(src, "Use say :b to speak to your cyborgs through binary. Use say :h to speak from an active holopad.") - to_chat(src, "For department channels, use the following say commands:") - - var/radio_text = "" - for(var/i = 1 to common_radio.channels.len) - var/channel = common_radio.channels[i] - var/key = get_radio_key_from_channel(channel) - radio_text += "[key] - [channel]" - if(i != common_radio.channels.len) - radio_text += ", " - - to_chat(src, radio_text) - - show_laws() - to_chat(src, "These laws may be changed by other players, or by you being the traitor.") - - job = "AI" - -/mob/living/silicon/ai/Stat() - ..() - if(statpanel("Status")) - if(stat) - stat(null, text("Systems nonfunctional")) - return - show_borg_info() - -/mob/living/silicon/ai/proc/show_borg_info() - stat(null, text("Connected cyborgs: [connected_robots.len]")) - for(var/mob/living/silicon/robot/R in connected_robots) - var/robot_status = "Nominal" - if(R.stat || !R.client) - robot_status = "OFFLINE" - else if(!R.cell || R.cell.charge <= 0) - robot_status = "DEPOWERED" - // Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies! - var/area/A = get_area(R) - stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge] / [R.cell.maxcharge]" : "Empty"] | \ - Module: [R.designation] | Loc: [sanitize(A.name)] | Status: [robot_status]")) - -/mob/living/silicon/ai/rename_character(oldname, newname) - if(!..(oldname, newname)) - return FALSE - - if(oldname != real_name) - announcement.announcer = name - - if(eyeobj) - eyeobj.name = "[newname] (AI Eye)" - - // Set ai pda name - if(aiPDA) - aiPDA.set_name_and_job(newname, "AI") - - return TRUE - -/mob/living/silicon/ai/Destroy() - ai_list -= src - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - QDEL_NULL(eyeobj) // No AI, no Eye - if(malfhacking) - deltimer(malfhacking) - malfhacking = null - malfhack = null - return ..() - - -/* - The AI Power supply is a dummy object used for powering the AI since only machinery should be using power. - The alternative was to rewrite a bunch of AI code instead here we are. -*/ -/obj/machinery/ai_powersupply - name="\improper AI power supply" - active_power_usage=1000 - use_power = ACTIVE_POWER_USE - power_channel = EQUIP - var/mob/living/silicon/ai/powered_ai = null - invisibility = 100 - -/obj/machinery/ai_powersupply/New(mob/living/silicon/ai/ai=null) - powered_ai = ai - if(isnull(powered_ai)) - qdel(src) - return - - loc = powered_ai.loc - use_power(1) // Just incase we need to wake up the power system. - - ..() - -/obj/machinery/ai_powersupply/process() - if(!powered_ai || powered_ai.stat & DEAD) - qdel(src) - return - if(!powered_ai.anchored) - loc = powered_ai.loc - use_power = NO_POWER_USE - if(powered_ai.anchored) - use_power = ACTIVE_POWER_USE - -/mob/living/silicon/ai/proc/pick_icon() - set category = "AI Commands" - set name = "Set AI Core Display" - if(stat || aiRestorePowerRoutine) - return - if(!custom_sprite) //Check to see if custom sprite time, checking the appopriate file to change a var - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "ai") //ignore incorrectly formatted entries or entries that aren't marked for AI - continue - - if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required - custom_sprite = 1 - - var/display_choices = list( - "Monochrome", - "Blue", - "Clown", - "Inverted", - "Text", - "Smiley", - "Angry", - "Dorf", - "Matrix", - "Bliss", - "Firewall", - "Green", - "Red", - "Static", - "Triumvirate", - "Triumvirate Static", - "Red October", - "Sparkles", - "ANIMA", - "President", - "NT", - "NT2", - "Rainbow", - "Angel", - "Heartline", - "Hades", - "Helios", - "Syndicat Meow", - "Too Deep", - "Goon", - "Murica", - "Fuzzy", - "Glitchman", - "House", - "Database", - "Alien" - ) - if(custom_sprite) - display_choices += "Custom" - - //if(icon_state == initial(icon_state)) - var/icontype = "" - icontype = input("Select an icon!", "AI", null, null) in display_choices - icon = 'icons/mob/ai.dmi' //reset this in case we were on a custom sprite and want to change to a standard one - switch(icontype) - if("Custom") - icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' //set this here so we can use the custom_sprite - icon_state = "[ckey]-ai" - if("Clown") - icon_state = "ai-clown" - if("Monochrome") - icon_state = "ai-mono" - if("Inverted") - icon_state = "ai-u" - if("Firewall") - icon_state = "ai-magma" - if("Green") - icon_state = "ai-weird" - if("Red") - icon_state = "ai-red" - if("Static") - icon_state = "ai-static" - if("Text") - icon_state = "ai-text" - if("Smiley") - icon_state = "ai-smiley" - if("Matrix") - icon_state = "ai-matrix" - if("Angry") - icon_state = "ai-angryface" - if("Dorf") - icon_state = "ai-dorf" - if("Bliss") - icon_state = "ai-bliss" - if("Triumvirate") - icon_state = "ai-triumvirate" - if("Triumvirate Static") - icon_state = "ai-triumvirate-malf" - if("Red October") - icon_state = "ai-redoctober" - if("Sparkles") - icon_state = "ai-sparkles" - if("ANIMA") - icon_state = "ai-anima" - if("President") - icon_state = "ai-president" - if("NT") - icon_state = "ai-nt" - if("NT2") - icon_state = "ai-nanotrasen" - if("Rainbow") - icon_state = "ai-rainbow" - if("Angel") - icon_state = "ai-angel" - if("Heartline") - icon_state = "ai-heartline" - if("Hades") - icon_state = "ai-hades" - if("Helios") - icon_state = "ai-helios" - if("Syndicat Meow") - icon_state = "ai-syndicatmeow" - if("Too Deep") - icon_state = "ai-toodeep" - if("Goon") - icon_state = "ai-goon" - if("Murica") - icon_state = "ai-murica" - if("Fuzzy") - icon_state = "ai-fuzz" - if("Glitchman") - icon_state = "ai-glitchman" - if("House") - icon_state = "ai-house" - if("Database") - icon_state = "ai-database" - if("Alien") - icon_state = "ai-alien" - else - icon_state = "ai" - //else -// to_chat(usr, "You can only change your display once!") - //return - -// this verb lets the ai see the stations manifest -/mob/living/silicon/ai/proc/ai_roster() - set name = "Show Crew Manifest" - set category = "AI Commands" - show_station_manifest() - -/mob/living/silicon/ai/var/message_cooldown = 0 -/mob/living/silicon/ai/proc/ai_announcement_text() - set category = "AI Commands" - set name = "Make Station Announcement" - - if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) - return - - if(message_cooldown) - to_chat(src, "Please allow one minute to pass between announcements.") - return - - var/input = input(usr, "Please write a message to announce to the station crew.", "A.I. Announcement") as message|null - if(!input) - return - - if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) - return - - announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - -/mob/living/silicon/ai/proc/ai_call_shuttle() - set name = "Call Emergency Shuttle" - set category = "AI Commands" - - if(check_unable(AI_CHECK_WIRELESS)) - return - - var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") - if(!input || stat) - return - - if(check_unable(AI_CHECK_WIRELESS)) - return - - call_shuttle_proc(src, input) - - return - -/mob/living/silicon/ai/proc/ai_cancel_call() - set name = "Recall Emergency Shuttle" - set category = "AI Commands" - - if(check_unable(AI_CHECK_WIRELESS)) - return - - var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No") - - if(check_unable(AI_CHECK_WIRELESS)) - return - - if(confirm == "Yes") - cancel_call_proc(src) - -/mob/living/silicon/ai/cancel_camera() - view_core() - -/mob/living/silicon/ai/verb/toggle_anchor() - set category = "AI Commands" - set name = "Toggle Floor Bolts" - - if(!isturf(loc)) // if their location isn't a turf - return // stop - - if(anchored) - anchored = FALSE - else - anchored = TRUE - - to_chat(src, "[anchored ? "You are now anchored." : "You are now unanchored."]") - -/mob/living/silicon/ai/update_canmove() - return FALSE - -/mob/living/silicon/ai/proc/announcement() - set name = "Announcement" - set desc = "Create a vocal announcement by typing in the available words to create a sentence." - set category = "AI Commands" - - if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) - return - - ai_announcement() - -/mob/living/silicon/ai/check_eye(mob/user) - if(!current) - return null - user.reset_perspective(current) - return TRUE - -/mob/living/silicon/ai/blob_act(obj/structure/blob/B) - if(stat != DEAD) - adjustBruteLoss(60) - updatehealth() - return 1 - return 0 - -/mob/living/silicon/ai/restrained() - return FALSE - -/mob/living/silicon/ai/emp_act(severity) - if(prob(30)) - switch(pick(1,2)) - if(1) - view_core() - if(2) - ai_call_shuttle() - ..() - -/mob/living/silicon/ai/ex_act(severity) - ..() - - switch(severity) - if(1.0) - gib() - if(2.0) - if(stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if(stat != 2) - adjustBruteLoss(30) - - return - - -/mob/living/silicon/ai/Topic(href, href_list) - if(usr != src) - return - ..() - if(href_list["mach_close"]) - if(href_list["mach_close"] == "aialerts") - viewalerts = 0 - var/t1 = text("window=[]", href_list["mach_close"]) - unset_machine() - src << browse(null, t1) - if(href_list["switchcamera"]) - switchCamera(locate(href_list["switchcamera"])) in cameranet.cameras - if(href_list["showalerts"]) - subsystem_alarm_monitor() - if(href_list["show_paper"]) - if(last_paper_seen) - src << browse(last_paper_seen, "window=show_paper") - //Carn: holopad requests - if(href_list["jumptoholopad"]) - var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"]) - if(stat == CONSCIOUS) - if(H) - H.attack_ai(src) //may as well recycle - else - to_chat(src, "Unable to locate the holopad.") - - if(href_list["say_word"]) - play_vox_word(href_list["say_word"], null, src) - return - - if(href_list["track"]) - var/mob/living/target = locate(href_list["track"]) in GLOB.mob_list - if(target && target.can_track()) - ai_actual_track(target) - else - to_chat(src, "Target is not on or near any active cameras on the station.") - return - - if(href_list["trackbot"]) - var/mob/living/simple_animal/bot/target = locate(href_list["trackbot"]) in GLOB.bots_list - if(target) - ai_actual_track(target) - else - to_chat(src, "Target is not on or near any active cameras on the station.") - return - - if(href_list["callbot"]) //Command a bot to move to a selected location. - Bot = locate(href_list["callbot"]) in GLOB.bots_list - if(!Bot || Bot.remote_disabled || control_disabled) - return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off. - waypoint_mode = 1 - to_chat(src, "Set your waypoint by clicking on a valid location free of obstructions.") - return - - if(href_list["interface"]) //Remotely connect to a bot! - Bot = locate(href_list["interface"]) in GLOB.bots_list - if(!Bot || Bot.remote_disabled || control_disabled) - return - Bot.attack_ai(src) - - if(href_list["botrefresh"]) //Refreshes the bot control panel. - botcall() - return - - if(href_list["ai_take_control"]) //Mech domination - - var/obj/mecha/M = locate(href_list["ai_take_control"]) - - if(!M) - return - - var/mech_has_controlbeacon = FALSE - for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in M.trackers) - mech_has_controlbeacon = TRUE - break - if(!can_dominate_mechs && !mech_has_controlbeacon) - message_admins("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.") - log_debug("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.") - return - - if(controlled_mech) - to_chat(src, "You are already loaded into an onboard computer!") - return - if(!cameranet.checkCameraVis(M)) - to_chat(src, "Exosuit is no longer near active cameras.") - return - if(lacks_power()) - to_chat(src, "You're depowered!") - return - if(!isturf(loc)) - to_chat(src, "You aren't in your core!") - return - if(M) - M.transfer_ai(AI_MECH_HACK, src, usr) //Called om the mech itself. - - else if(href_list["faketrack"]) - var/mob/target = locate(href_list["track"]) in GLOB.mob_list - var/mob/living/silicon/ai/A = locate(href_list["track2"]) in GLOB.mob_list - if(A && target) - - A.cameraFollow = target - to_chat(A, "Now tracking [target.name] on camera.") - if(usr.machine == null) - usr.machine = usr - - while(cameraFollow == target) - to_chat(usr, "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb).") - sleep(40) - continue - - else if(href_list["open"]) - var/mob/target = locate(href_list["open"]) in GLOB.mob_list - if(target) - open_nearest_door(target) - -/mob/living/silicon/ai/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - return 2 - -/mob/living/silicon/ai/reset_perspective(atom/A) - if(camera_light_on) - light_cameras() - if(istype(A, /obj/machinery/camera)) - current = A - if(A != GLOB.ai_camera_room_landmark) - end_multicam() - - . = ..() - if(.) - if(!A && isturf(loc) && eyeobj) - end_multicam() - client.eye = eyeobj - client.perspective = MOB_PERSPECTIVE - eyeobj.get_remote_view_fullscreens(src) - -/mob/living/silicon/ai/proc/botcall() - set category = "AI Commands" - set name = "Access Robot Control" - set desc = "Wirelessly control various automatic robots." - if(stat == 2) - to_chat(src, "Critical error. System offline.") - return - - if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) - return - - var/d - var/area/bot_area - d += "Query network status
    " - d += "" - - for(var/mob/living/simple_animal/bot/Bot in GLOB.bots_list) - if(is_ai_allowed(Bot.z) && !Bot.remote_disabled) //Only non-emagged bots on the allowed Z-level are detected! - bot_area = get_area(Bot) - d += "" - //If the bot is on, it will display the bot's current mode status. If the bot is not mode, it will just report "Idle". "Inactive if it is not on at all. - d += "" - d += "" - d += "" - d += "" - d += "" - d = format_text(d) - - var/datum/browser/popup = new(src, "botcall", "Remote Robot Control", 700, 400) - popup.set_content(d) - popup.open() - -/mob/living/silicon/ai/proc/set_waypoint(atom/A) - var/turf/turf_check = get_turf(A) - //The target must be in view of a camera or near the core. - if(turf_check in range(get_turf(src))) - call_bot(turf_check) - else if(cameranet && cameranet.checkTurfVis(turf_check)) - call_bot(turf_check) - else - to_chat(src, "Selected location is not visible.") - -/mob/living/silicon/ai/proc/call_bot(turf/waypoint) - - if(!Bot) - return - - if(Bot.calling_ai && Bot.calling_ai != src) //Prevents an override if another AI is controlling this bot. - to_chat(src, "Interface error. Unit is already in use.") - return - - Bot.call_bot(src, waypoint) - -/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C) - - if(!tracking) - cameraFollow = null - - if(!C || stat == DEAD) //C.can_use()) - return FALSE - - if(!eyeobj) - view_core() - return - // ok, we're alive, camera is good and in our network... - eyeobj.setLoc(get_turf(C)) - //machine = src - - return TRUE - -//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm -//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead -//Addition by Mord_Sith to define AI's network change ability -/mob/living/silicon/ai/proc/ai_network_change() - set category = "AI Commands" - set name = "Jump To Network" - unset_machine() - var/cameralist[0] - - if(check_unable()) - return - - if(usr.stat == 2) - to_chat(usr, "You can't change your camera network because you are dead!") - return - - var/mob/living/silicon/ai/U = usr - - for(var/obj/machinery/camera/C in cameranet.cameras) - if(!C.can_use()) - continue - - var/list/tempnetwork = difflist(C.network,GLOB.restricted_camera_networks,1) - if(tempnetwork.len) - for(var/i in tempnetwork) - cameralist[i] = i - var/old_network = network - network = input(U, "Which network would you like to view?") as null|anything in cameralist - - if(check_unable()) - return - - if(!U.eyeobj) - U.view_core() - return - - if(isnull(network)) - network = old_network // If nothing is selected - else - for(var/obj/machinery/camera/C in cameranet.cameras) - if(!C.can_use()) - continue - if(network in C.network) - U.eyeobj.setLoc(get_turf(C)) - break - to_chat(src, "Switched to [network] camera network.") -//End of code by Mord_Sith - - -/mob/living/silicon/ai/proc/choose_modules() - set category = "Malfunction" - set name = "Choose Module" - - malf_picker.use(src) - -/mob/living/silicon/ai/proc/ai_statuschange() - set category = "AI Commands" - set name = "AI Status" - - if(usr.stat == 2) - to_chat(usr, "You cannot change your emotional status because you are dead!") - return - - if(check_unable()) - return - - var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer") - var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions - - if(check_unable()) - return - - for(var/obj/machinery/M in GLOB.machines) //change status - if(istype(M, /obj/machinery/ai_status_display)) - var/obj/machinery/ai_status_display/AISD = M - AISD.emotion = emote - //if Friend Computer, change ALL displays - else if(istype(M, /obj/machinery/status_display)) - - var/obj/machinery/status_display/SD = M - if(emote=="Friend Computer") - SD.friendc = 1 - else - SD.friendc = 0 - return - -//I am the icon meister. Bow fefore me. //>fefore -/mob/living/silicon/ai/proc/ai_hologram_change() - set name = "Change Hologram" - set desc = "Change the default hologram available to AI to something else." - set category = "AI Commands" - - if(check_unable()) - return - if(!custom_hologram) //Check to see if custom sprite time, checking the appopriate file to change a var - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "hologram") - continue - - if (Entry[2] == ckey) //Custom holograms - custom_hologram = 1 // option is given in hologram menu - - var/input - switch(alert("Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?",,"Crew Member","Unique","Animal")) - if("Crew Member") - var/personnel_list[] = list() - - for(var/datum/data/record/t in data_core.locked)//Look in data core locked. - personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["image"]//Pull names, rank, and image. - - if(personnel_list.len) - input = input("Select a crew member:") as null|anything in personnel_list - var/icon/character_icon = personnel_list[input] - if(character_icon) - qdel(holo_icon)//Clear old icon so we're not storing it in memory. - holo_icon = getHologramIcon(icon(character_icon)) - else - alert("No suitable records found. Aborting.") - - if("Animal") - var/icon_list[] = list( - "Bear", - "Carp", - "Chicken", - "Corgi", - "Cow", - "Crab", - "Deer", - "Fox", - "Goat", - "Goose", - "Kitten", - "Kitten2", - "Pig", - "Poly", - "Pug", - "Seal", - "Spider", - "Turkey" - ) - - input = input("Please select a hologram:") as null|anything in icon_list - if(input) - qdel(holo_icon) - switch(input) - if("Bear") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"bear")) - if("Carp") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"carp")) - if("Chicken") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"chicken_brown")) - if("Corgi") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"corgi")) - if("Cow") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"cow")) - if("Crab") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"crab")) - if("Deer") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"deer")) - if("Fox") - holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"fox")) - if("Goat") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"goat")) - if("Goose") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"goose")) - if("Kitten") - holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"cat")) - if("Kitten2") - holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"cat2")) - if("Pig") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"pig")) - if("Poly") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"parrot_fly")) - if("Pug") - holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"pug")) - if("Seal") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"seal")) - if("Spider") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"guard")) - if("Turkey") - holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"turkey")) - - else - var/icon_list[] = list( - "default", - "floating face", - "xeno queen", - "eldritch", - "ancient machine" - ) - if(custom_hologram) //insert custom hologram - icon_list.Add("custom") - - input = input("Please select a hologram:") as null|anything in icon_list - if(input) - qdel(holo_icon) - switch(input) - if("default") - holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1")) - if("floating face") - holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo2")) - if("xeno queen") - holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo3")) - if("eldritch") - holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo4")) - if("ancient machine") - holo_icon = getHologramIcon(icon('icons/mob/ancient_machine.dmi', "ancient_machine")) - if("custom") - if("[ckey]-ai-holo" in icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi')) - holo_icon = getHologramIcon(icon('icons/mob/custom_synthetic/custom-synthetic.dmi', "[ckey]-ai-holo")) - else if("[ckey]-ai-holo" in icon_states('icons/mob/custom_synthetic/custom-synthetic64.dmi')) - holo_icon = getHologramIcon(icon('icons/mob/custom_synthetic/custom-synthetic64.dmi', "[ckey]-ai-holo")) - else - holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1")) - - return - -/mob/living/silicon/ai/proc/corereturn() - set category = "Malfunction" - set name = "Return to Main Core" - - var/obj/machinery/power/apc/apc = loc - if(!istype(apc)) - to_chat(src, "You are already in your Main Core.") - return - apc.malfvacate() - -//Toggles the luminosity and applies it by re-entereing the camera. -/mob/living/silicon/ai/proc/toggle_camera_light() - set name = "Toggle Camera Lights" - set desc = "Toggles the lights on the cameras throughout the station." - set category = "AI Commands" - - if(stat != CONSCIOUS) - return - - camera_light_on = !camera_light_on - - if(!camera_light_on) - to_chat(src, "Camera lights deactivated.") - - for(var/obj/machinery/camera/C in lit_cameras) - C.set_light(0) - lit_cameras = list() - - return - - light_cameras() - - to_chat(src, "Camera lights activated.") - -/mob/living/silicon/ai/proc/set_syndie_radio() - if(aiRadio) - aiRadio.make_syndie() - -/mob/living/silicon/ai/proc/sensor_mode() - set name = "Set Sensor Augmentation" - set desc = "Augment visual feed with internal sensor overlays." - set category = "AI Commands" - toggle_sensor_mode() - -/mob/living/silicon/ai/proc/change_arrival_message() - set name = "Set Arrival Message" - set desc = "Change the message that's transmitted when a new crew member arrives on station." - set category = "AI Commands" - - var/newmsg = clean_input("What would you like the arrival message to be? List of options: $name, $rank, $species, $gender, $age", "Change Arrival Message", arrivalmsg) - if(newmsg != arrivalmsg) - arrivalmsg = newmsg - to_chat(usr, "The arrival message has been successfully changed.") - -// Handled camera lighting, when toggled. -// It will get the nearest camera from the eyeobj, lighting it. - -/mob/living/silicon/ai/proc/light_cameras() - var/list/obj/machinery/camera/add = list() - var/list/obj/machinery/camera/remove = list() - var/list/obj/machinery/camera/visible = list() - for(var/datum/camerachunk/CC in eyeobj.visibleCameraChunks) - for(var/obj/machinery/camera/C in CC.cameras) - if(!C.can_use() || get_dist(C, eyeobj) > 7) - continue - visible |= C - - add = visible - lit_cameras - remove = lit_cameras - visible - - for(var/obj/machinery/camera/C in remove) - lit_cameras -= C //Removed from list before turning off the light so that it doesn't check the AI looking away. - C.Togglelight(0) - for(var/obj/machinery/camera/C in add) - C.Togglelight(1) - lit_cameras |= C - - -/mob/living/silicon/ai/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/wrench)) - if(anchored) - user.visible_message("\The [user] starts to unbolt \the [src] from the plating...") - if(!do_after(user, 40 * W.toolspeed, target = src)) - user.visible_message("\The [user] decides not to unbolt \the [src].") - return - user.visible_message("\The [user] finishes unfastening \the [src]!") - anchored = FALSE - return - else - user.visible_message("\The [user] starts to bolt \the [src] to the plating...") - if(!do_after(user, 40 * W.toolspeed, target = src)) - user.visible_message("\The [user] decides not to bolt \the [src].") - return - user.visible_message("\The [user] finishes fastening down \the [src]!") - anchored = TRUE - return - else - return ..() - -/mob/living/silicon/ai/welder_act() - return - -/mob/living/silicon/ai/proc/control_integrated_radio() - set name = "Radio Settings" - set desc = "Allows you to change settings of your radio." - set category = "AI Commands" - - if(check_unable(AI_CHECK_RADIO)) - return - - to_chat(src, "Accessing Subspace Transceiver control...") - if(aiRadio) - aiRadio.interact(src) - - -/mob/living/silicon/ai/proc/check_unable(flags = 0) - if(stat == DEAD) - to_chat(src, "You are dead!") - return TRUE - - if(lacks_power()) - to_chat(src, "Power systems failure!") - return TRUE - - if((flags & AI_CHECK_WIRELESS) && control_disabled) - to_chat(src, "Wireless control is disabled!") - return TRUE - if((flags & AI_CHECK_RADIO) && aiRadio.disabledAi) - to_chat(src, "System Error - Transceiver Disabled!") - return TRUE - return FALSE - -/mob/living/silicon/ai/proc/is_in_chassis() - return isturf(loc) - -/mob/living/silicon/ai/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) - if(!..()) - return - if(interaction == AI_TRANS_TO_CARD)//The only possible interaction. Upload AI mob to a card. - if(!mind) - to_chat(user, "No intelligence patterns detected.")//No more magical carding of empty cores, AI RETURN TO BODY!!!11 - return - new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location. - aiRestorePowerRoutine = 0//So the AI initially has power. - control_disabled = 1//Can't control things remotely if you're stuck in a card! - aiRadio.disabledAi = 1 //No talking on the built-in radio for you either! - loc = card//Throw AI into the card. - to_chat(src, "You have been downloaded to a mobile storage device. Remote device connection severed.") - to_chat(user, "Transfer successful: [name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") - -/mob/living/silicon/ai/can_buckle() - return FALSE - -// Pass lying down or getting up to our pet human, if we're in a rig. -/mob/living/silicon/ai/lay_down() - set name = "Rest" - set category = "IC" - - resting = 0 - var/obj/item/rig/rig = get_rig() - if(rig) - rig.force_rest(src) - -/mob/living/silicon/ai/switch_to_camera(obj/machinery/camera/C) - if(!C.can_use() || !is_in_chassis()) - return FALSE - - eyeobj.setLoc(get_turf(C)) - client.eye = eyeobj - return TRUE - - -/mob/living/silicon/ai/proc/can_see(atom/A) - if(isturf(loc)) //AI in core, check if on cameras - //get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera - //apc_override is needed here because AIs use their own APC when depowered - var/turf/T = isturf(A) ? A : get_turf_pixel(A) - return (cameranet && cameranet.checkTurfVis(T)) || apc_override - //AI is carded/shunted - //view(src) returns nothing for carded/shunted AIs and they have x-ray vision so just use get_dist - var/list/viewscale = getviewsize(client.view) - return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5) - -/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb) - var/message = combine_message(message_pieces, verb, M) - var/name_used = M.GetVoice() - //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. - var/rendered = "Relayed Speech: [name_used] [message]" - show_message(rendered, 2) - -/mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc) - malfhack = null - malfhacking = 0 - clear_alert("hackingapc") - - if(!istype(apc) || QDELETED(apc) || apc.stat & BROKEN) - to_chat(src, "Hack aborted. The designated APC no longer exists on the power network.") - playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1) - else if(apc.aidisabled) - to_chat(src, "Hack aborted. [apc] is no longer responding to our systems.") - playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 1) - else - malf_picker.processing_time += 10 - - apc.malfai = parent || src - apc.malfhack = TRUE - apc.locked = TRUE - - playsound(get_turf(src), 'sound/machines/ding.ogg', 50, 1) - to_chat(src, "Hack complete. [apc] is now under your exclusive control.") - apc.update_icon() - -/mob/living/silicon/ai/proc/add_malf_picker() - to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.") - to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.") - view_core() //A BYOND bug requires you to be viewing your core before your verbs update - verbs += /mob/living/silicon/ai/proc/choose_modules - malf_picker = new /datum/module_picker - -/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) - if(!istype(target)) - return - - if(target && target.can_track()) - var/obj/machinery/door/airlock/A = null - - var/dist = -1 - for(var/obj/machinery/door/airlock/D in range(3, target)) - if(!D.density) - continue - - var/curr_dist = get_dist(D, target) - - if(dist < 0) - dist = curr_dist - A = D - else if(dist > curr_dist) - dist = curr_dist - A = D - - if(istype(A)) - switch(alert(src, "Do you want to open \the [A] for [target]?", "Doorknob_v2a.exe", "Yes", "No")) - if("Yes") - A.AIShiftClick() - to_chat(src, "You open \the [A] for [target].") - else - to_chat(src, "You deny the request.") - else - to_chat(src, "Unable to locate an airlock near [target].") - - else - to_chat(src, "Target is not on or near any active cameras on the station.") - -/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye) - cameranet.visibility(moved_eye, client, all_eyes) - -/mob/living/silicon/ai/forceMove(atom/destination) - . = ..() - if(.) - end_multicam() - -/mob/living/silicon/ai/handle_fire() - return - -/mob/living/silicon/ai/update_fire() - return - -/mob/living/silicon/ai/IgniteMob() - return FALSE - -/mob/living/silicon/ai/ExtinguishMob() - return - - -/mob/living/silicon/ai/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - lighting_alpha = initial(lighting_alpha) - - if(aiRestorePowerRoutine) - sight = sight &~ SEE_TURFS - sight = sight &~ SEE_MOBS - sight = sight &~ SEE_OBJS - see_in_dark = 0 - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() +GLOBAL_LIST_EMPTY(ai_list) +GLOBAL_LIST_INIT(ai_verbs_default, list( + /mob/living/silicon/ai/proc/announcement, + /mob/living/silicon/ai/proc/ai_announcement_text, + /mob/living/silicon/ai/proc/ai_call_shuttle, + /mob/living/silicon/ai/proc/ai_camera_track, + /mob/living/silicon/ai/proc/ai_camera_list, + /mob/living/silicon/ai/proc/ai_goto_location, + /mob/living/silicon/ai/proc/ai_remove_location, + /mob/living/silicon/ai/proc/ai_hologram_change, + /mob/living/silicon/ai/proc/ai_network_change, + /mob/living/silicon/ai/proc/ai_roster, + /mob/living/silicon/ai/proc/ai_statuschange, + /mob/living/silicon/ai/proc/ai_store_location, + /mob/living/silicon/ai/proc/control_integrated_radio, + /mob/living/silicon/ai/proc/core, + /mob/living/silicon/ai/proc/pick_icon, + /mob/living/silicon/ai/proc/sensor_mode, + /mob/living/silicon/ai/proc/show_laws_verb, + /mob/living/silicon/ai/proc/toggle_acceleration, + /mob/living/silicon/ai/proc/toggle_camera_light, + /mob/living/silicon/ai/proc/botcall, + /mob/living/silicon/ai/proc/change_arrival_message +)) + +//Not sure why this is necessary... +/proc/AutoUpdateAI(obj/subject) + var/is_in_use = 0 + if(subject!=null) + for(var/A in GLOB.ai_list) + var/mob/living/silicon/ai/M = A + if((M.client && M.machine == subject)) + is_in_use = 1 + subject.attack_ai(M) + return is_in_use + +/mob/living/silicon/ai + name = "AI" + icon = 'icons/mob/ai.dmi'// + icon_state = "ai" + move_resist = MOVE_FORCE_NORMAL + density = 1 + status_flags = CANSTUN|CANPARALYSE|CANPUSH + mob_size = MOB_SIZE_LARGE + sight = SEE_TURFS | SEE_MOBS | SEE_OBJS + see_in_dark = 8 + can_strip = 0 + var/list/network = list("SS13","Telecomms","Research Outpost","Mining Outpost") + var/obj/machinery/camera/current = null + var/list/connected_robots = list() + var/aiRestorePowerRoutine = 0 + //var/list/laws = list() + var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list()) + var/viewalerts = 0 + var/icon/holo_icon//Default is assigned when AI is created. + var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye. + var/obj/item/pda/silicon/ai/aiPDA = null + var/obj/item/multitool/aiMulti = null + var/custom_sprite = 0 //For our custom sprites + var/custom_hologram = 0 //For our custom holograms + + var/obj/item/radio/headset/heads/ai_integrated/aiRadio = null + + //MALFUNCTION + var/datum/module_picker/malf_picker + var/list/datum/AI_Module/current_modules = list() + var/can_dominate_mechs = 0 + var/shunted = 0 //1 if the AI is currently shunted. Used to differentiate between shunted and ghosted/braindead + + var/control_disabled = 0 // Set to 1 to stop AI from interacting via Click() -- TLE + var/malfhacking = 0 // More or less a copy of the above var, so that malf AIs can hack and still get new cyborgs -- NeoFite + var/malf_cooldown = 0 //Cooldown var for malf modules, stores a worldtime + cooldown + + var/obj/machinery/power/apc/malfhack = null + var/explosive = 0 //does the AI explode when it dies? + + var/mob/living/silicon/ai/parent = null + var/camera_light_on = 0 + var/list/obj/machinery/camera/lit_cameras = list() + + var/datum/trackable/track = new() + + var/last_paper_seen = null + var/can_shunt = 1 + var/last_announcement = "" + var/datum/announcement/priority/announcement + var/mob/living/simple_animal/bot/Bot + var/turf/waypoint //Holds the turf of the currently selected waypoint. + var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking. + var/apc_override = FALSE //hack for letting the AI use its APC even when visionless + var/nuking = 0 + var/obj/machinery/doomsday_device/doomsday_device + + var/obj/machinery/hologram/holopad/holo = null + var/mob/camera/aiEye/eyeobj + var/sprint = 10 + var/cooldown = 0 + var/acceleration = 1 + var/tracking = 0 //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed. + + var/obj/machinery/camera/portable/builtInCamera + + var/obj/structure/AIcore/deactivated/linked_core //For exosuit control + + var/arrivalmsg = "$name, $rank, has arrived on the station." + + var/multicam_allowed = FALSE + var/multicam_on = FALSE + var/obj/screen/movable/pic_in_pic/ai/master_multicam + var/list/multicam_screens = list() + var/list/all_eyes = list() + var/max_multicams = 6 + +/mob/living/silicon/ai/proc/add_ai_verbs() + verbs |= GLOB.ai_verbs_default + verbs |= silicon_subsystems + +/mob/living/silicon/ai/proc/remove_ai_verbs() + verbs -= GLOB.ai_verbs_default + verbs -= silicon_subsystems + +/mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/mmi/B, var/safety = 0) + announcement = new() + announcement.title = "A.I. Announcement" + announcement.announcement_type = "A.I. Announcement" + announcement.announcer = name + announcement.newscast = 0 + + var/list/possibleNames = GLOB.ai_names + + var/pickedName = null + while(!pickedName) + pickedName = pick(GLOB.ai_names) + for(var/mob/living/silicon/ai/A in GLOB.mob_list) + if(A.real_name == pickedName && possibleNames.len > 1) //fixing the theoretically possible infinite loop + possibleNames -= pickedName + pickedName = null + + aiPDA = new/obj/item/pda/silicon/ai(src) + rename_character(null, pickedName) + anchored = 1 + canmove = 0 + density = 1 + loc = loc + + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1")) + + proc_holder_list = new() + + if(L) + if(istype(L, /datum/ai_laws)) + laws = L + else + make_laws() + + verbs += /mob/living/silicon/ai/proc/show_laws_verb + + aiMulti = new(src) + aiRadio = new(src) + common_radio = aiRadio + aiRadio.myAi = src + additional_law_channels["Binary"] = ":b " + additional_law_channels["Holopad"] = ":h" + + aiCamera = new/obj/item/camera/siliconcam/ai_camera(src) + + if(isturf(loc)) + add_ai_verbs(src) + + //Languages + add_language("Robot Talk", 1) + add_language("Galactic Common", 1) + add_language("Sol Common", 1) + add_language("Tradeband", 1) + add_language("Neo-Russkiya", 0) + add_language("Gutter", 0) + add_language("Sinta'unathi", 0) + add_language("Siik'tajr", 0) + add_language("Canilunzt", 0) + add_language("Skrellian", 0) + add_language("Vox-pidgin", 0) + add_language("Orluum", 0) + add_language("Rootspeak", 0) + add_language("Trinary", 1) + add_language("Chittin", 0) + add_language("Bubblish", 0) + add_language("Clownish", 0) + + if(!safety)//Only used by AIize() to successfully spawn an AI. + if(!B)//If there is no player/brain inside. + new/obj/structure/AIcore/deactivated(loc)//New empty terminal. + qdel(src)//Delete AI. + return + else + if(B.brainmob.mind) + B.brainmob.mind.transfer_to(src) + + on_mob_init() + + spawn(5) + new /obj/machinery/ai_powersupply(src) + + create_eye() + + builtInCamera = new /obj/machinery/camera/portable(src) + builtInCamera.c_tag = name + builtInCamera.network = list("SS13") + + GLOB.ai_list += src + GLOB.shuttle_caller_list += src + ..() + +/mob/living/silicon/ai/proc/on_mob_init() + to_chat(src, "You are playing the station's AI. The AI cannot move, but can interact with many objects while viewing them (through cameras).") + to_chat(src, "To look at other parts of the station, click on yourself to get a camera menu.") + to_chat(src, "While observing through a camera, you can use most (networked) devices which you can see, such as computers, APCs, intercoms, doors, etc.") + to_chat(src, "To use something, simply click on it.") + to_chat(src, "Use say :b to speak to your cyborgs through binary. Use say :h to speak from an active holopad.") + to_chat(src, "For department channels, use the following say commands:") + + var/radio_text = "" + for(var/i = 1 to common_radio.channels.len) + var/channel = common_radio.channels[i] + var/key = get_radio_key_from_channel(channel) + radio_text += "[key] - [channel]" + if(i != common_radio.channels.len) + radio_text += ", " + + to_chat(src, radio_text) + + show_laws() + to_chat(src, "These laws may be changed by other players, or by you being the traitor.") + + job = "AI" + +/mob/living/silicon/ai/Stat() + ..() + if(statpanel("Status")) + if(stat) + stat(null, text("Systems nonfunctional")) + return + show_borg_info() + +/mob/living/silicon/ai/proc/show_borg_info() + stat(null, text("Connected cyborgs: [connected_robots.len]")) + for(var/mob/living/silicon/robot/R in connected_robots) + var/robot_status = "Nominal" + if(R.stat || !R.client) + robot_status = "OFFLINE" + else if(!R.cell || R.cell.charge <= 0) + robot_status = "DEPOWERED" + // Name, Health, Battery, Module, Area, and Status! Everything an AI wants to know about its borgies! + var/area/A = get_area(R) + stat(null, text("[R.name] | S.Integrity: [R.health]% | Cell: [R.cell ? "[R.cell.charge] / [R.cell.maxcharge]" : "Empty"] | \ + Module: [R.designation] | Loc: [sanitize(A.name)] | Status: [robot_status]")) + +/mob/living/silicon/ai/rename_character(oldname, newname) + if(!..(oldname, newname)) + return FALSE + + if(oldname != real_name) + announcement.announcer = name + + if(eyeobj) + eyeobj.name = "[newname] (AI Eye)" + + // Set ai pda name + if(aiPDA) + aiPDA.set_name_and_job(newname, "AI") + + return TRUE + +/mob/living/silicon/ai/Destroy() + GLOB.ai_list -= src + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + QDEL_NULL(eyeobj) // No AI, no Eye + if(malfhacking) + deltimer(malfhacking) + malfhacking = null + malfhack = null + return ..() + + +/* + The AI Power supply is a dummy object used for powering the AI since only machinery should be using power. + The alternative was to rewrite a bunch of AI code instead here we are. +*/ +/obj/machinery/ai_powersupply + name="\improper AI power supply" + active_power_usage=1000 + use_power = ACTIVE_POWER_USE + power_channel = EQUIP + var/mob/living/silicon/ai/powered_ai = null + invisibility = 100 + +/obj/machinery/ai_powersupply/New(mob/living/silicon/ai/ai=null) + powered_ai = ai + if(isnull(powered_ai)) + qdel(src) + return + + loc = powered_ai.loc + use_power(1) // Just incase we need to wake up the power system. + + ..() + +/obj/machinery/ai_powersupply/process() + if(!powered_ai || powered_ai.stat & DEAD) + qdel(src) + return + if(!powered_ai.anchored) + loc = powered_ai.loc + use_power = NO_POWER_USE + if(powered_ai.anchored) + use_power = ACTIVE_POWER_USE + +/mob/living/silicon/ai/proc/pick_icon() + set category = "AI Commands" + set name = "Set AI Core Display" + if(stat || aiRestorePowerRoutine) + return + if(!custom_sprite) //Check to see if custom sprite time, checking the appopriate file to change a var + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "ai") //ignore incorrectly formatted entries or entries that aren't marked for AI + continue + + if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required + custom_sprite = 1 + + var/display_choices = list( + "Monochrome", + "Blue", + "Clown", + "Inverted", + "Text", + "Smiley", + "Angry", + "Dorf", + "Matrix", + "Bliss", + "Firewall", + "Green", + "Red", + "Static", + "Triumvirate", + "Triumvirate Static", + "Red October", + "Sparkles", + "ANIMA", + "President", + "NT", + "NT2", + "Rainbow", + "Angel", + "Heartline", + "Hades", + "Helios", + "Syndicat Meow", + "Too Deep", + "Goon", + "Murica", + "Fuzzy", + "Glitchman", + "House", + "Database", + "Alien" + ) + if(custom_sprite) + display_choices += "Custom" + + //if(icon_state == initial(icon_state)) + var/icontype = "" + icontype = input("Select an icon!", "AI", null, null) in display_choices + icon = 'icons/mob/ai.dmi' //reset this in case we were on a custom sprite and want to change to a standard one + switch(icontype) + if("Custom") + icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' //set this here so we can use the custom_sprite + icon_state = "[ckey]-ai" + if("Clown") + icon_state = "ai-clown" + if("Monochrome") + icon_state = "ai-mono" + if("Inverted") + icon_state = "ai-u" + if("Firewall") + icon_state = "ai-magma" + if("Green") + icon_state = "ai-weird" + if("Red") + icon_state = "ai-red" + if("Static") + icon_state = "ai-static" + if("Text") + icon_state = "ai-text" + if("Smiley") + icon_state = "ai-smiley" + if("Matrix") + icon_state = "ai-matrix" + if("Angry") + icon_state = "ai-angryface" + if("Dorf") + icon_state = "ai-dorf" + if("Bliss") + icon_state = "ai-bliss" + if("Triumvirate") + icon_state = "ai-triumvirate" + if("Triumvirate Static") + icon_state = "ai-triumvirate-malf" + if("Red October") + icon_state = "ai-redoctober" + if("Sparkles") + icon_state = "ai-sparkles" + if("ANIMA") + icon_state = "ai-anima" + if("President") + icon_state = "ai-president" + if("NT") + icon_state = "ai-nt" + if("NT2") + icon_state = "ai-nanotrasen" + if("Rainbow") + icon_state = "ai-rainbow" + if("Angel") + icon_state = "ai-angel" + if("Heartline") + icon_state = "ai-heartline" + if("Hades") + icon_state = "ai-hades" + if("Helios") + icon_state = "ai-helios" + if("Syndicat Meow") + icon_state = "ai-syndicatmeow" + if("Too Deep") + icon_state = "ai-toodeep" + if("Goon") + icon_state = "ai-goon" + if("Murica") + icon_state = "ai-murica" + if("Fuzzy") + icon_state = "ai-fuzz" + if("Glitchman") + icon_state = "ai-glitchman" + if("House") + icon_state = "ai-house" + if("Database") + icon_state = "ai-database" + if("Alien") + icon_state = "ai-alien" + else + icon_state = "ai" + //else +// to_chat(usr, "You can only change your display once!") + //return + +// this verb lets the ai see the stations manifest +/mob/living/silicon/ai/proc/ai_roster() + set name = "Show Crew Manifest" + set category = "AI Commands" + show_station_manifest() + +/mob/living/silicon/ai/var/message_cooldown = 0 +/mob/living/silicon/ai/proc/ai_announcement_text() + set category = "AI Commands" + set name = "Make Station Announcement" + + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) + return + + if(message_cooldown) + to_chat(src, "Please allow one minute to pass between announcements.") + return + + var/input = input(usr, "Please write a message to announce to the station crew.", "A.I. Announcement") as message|null + if(!input) + return + + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) + return + + announcement.Announce(input) + message_cooldown = 1 + spawn(600)//One minute cooldown + message_cooldown = 0 + +/mob/living/silicon/ai/proc/ai_call_shuttle() + set name = "Call Emergency Shuttle" + set category = "AI Commands" + + if(check_unable(AI_CHECK_WIRELESS)) + return + + var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") + if(!input || stat) + return + + if(check_unable(AI_CHECK_WIRELESS)) + return + + call_shuttle_proc(src, input) + + return + +/mob/living/silicon/ai/proc/ai_cancel_call() + set name = "Recall Emergency Shuttle" + set category = "AI Commands" + + if(check_unable(AI_CHECK_WIRELESS)) + return + + var/confirm = alert("Are you sure you want to recall the shuttle?", "Confirm Shuttle Recall", "Yes", "No") + + if(check_unable(AI_CHECK_WIRELESS)) + return + + if(confirm == "Yes") + cancel_call_proc(src) + +/mob/living/silicon/ai/cancel_camera() + view_core() + +/mob/living/silicon/ai/verb/toggle_anchor() + set category = "AI Commands" + set name = "Toggle Floor Bolts" + + if(!isturf(loc)) // if their location isn't a turf + return // stop + + if(anchored) + anchored = FALSE + else + anchored = TRUE + + to_chat(src, "[anchored ? "You are now anchored." : "You are now unanchored."]") + +/mob/living/silicon/ai/update_canmove() + return FALSE + +/mob/living/silicon/ai/proc/announcement() + set name = "Announcement" + set desc = "Create a vocal announcement by typing in the available words to create a sentence." + set category = "AI Commands" + + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) + return + + ai_announcement() + +/mob/living/silicon/ai/check_eye(mob/user) + if(!current) + return null + user.reset_perspective(current) + return TRUE + +/mob/living/silicon/ai/blob_act(obj/structure/blob/B) + if(stat != DEAD) + adjustBruteLoss(60) + updatehealth() + return 1 + return 0 + +/mob/living/silicon/ai/restrained() + return FALSE + +/mob/living/silicon/ai/emp_act(severity) + if(prob(30)) + switch(pick(1,2)) + if(1) + view_core() + if(2) + ai_call_shuttle() + ..() + +/mob/living/silicon/ai/ex_act(severity) + ..() + + switch(severity) + if(1.0) + gib() + if(2.0) + if(stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if(stat != 2) + adjustBruteLoss(30) + + return + + +/mob/living/silicon/ai/Topic(href, href_list) + if(usr != src) + return + ..() + if(href_list["mach_close"]) + if(href_list["mach_close"] == "aialerts") + viewalerts = 0 + var/t1 = text("window=[]", href_list["mach_close"]) + unset_machine() + src << browse(null, t1) + if(href_list["switchcamera"]) + switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras + if(href_list["showalerts"]) + subsystem_alarm_monitor() + if(href_list["show_paper"]) + if(last_paper_seen) + src << browse(last_paper_seen, "window=show_paper") + //Carn: holopad requests + if(href_list["jumptoholopad"]) + var/obj/machinery/hologram/holopad/H = locate(href_list["jumptoholopad"]) + if(stat == CONSCIOUS) + if(H) + H.attack_ai(src) //may as well recycle + else + to_chat(src, "Unable to locate the holopad.") + + if(href_list["say_word"]) + play_vox_word(href_list["say_word"], null, src) + return + + if(href_list["track"]) + var/mob/living/target = locate(href_list["track"]) in GLOB.mob_list + if(target && target.can_track()) + ai_actual_track(target) + else + to_chat(src, "Target is not on or near any active cameras on the station.") + return + + if(href_list["trackbot"]) + var/mob/living/simple_animal/bot/target = locate(href_list["trackbot"]) in GLOB.bots_list + if(target) + ai_actual_track(target) + else + to_chat(src, "Target is not on or near any active cameras on the station.") + return + + if(href_list["callbot"]) //Command a bot to move to a selected location. + Bot = locate(href_list["callbot"]) in GLOB.bots_list + if(!Bot || Bot.remote_disabled || control_disabled) + return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off. + waypoint_mode = 1 + to_chat(src, "Set your waypoint by clicking on a valid location free of obstructions.") + return + + if(href_list["interface"]) //Remotely connect to a bot! + Bot = locate(href_list["interface"]) in GLOB.bots_list + if(!Bot || Bot.remote_disabled || control_disabled) + return + Bot.attack_ai(src) + + if(href_list["botrefresh"]) //Refreshes the bot control panel. + botcall() + return + + if(href_list["ai_take_control"]) //Mech domination + + var/obj/mecha/M = locate(href_list["ai_take_control"]) + + if(!M) + return + + var/mech_has_controlbeacon = FALSE + for(var/obj/item/mecha_parts/mecha_tracking/ai_control/A in M.trackers) + mech_has_controlbeacon = TRUE + break + if(!can_dominate_mechs && !mech_has_controlbeacon) + message_admins("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.") + log_debug("Warning: possible href exploit by [key_name(usr)] - attempted control of a mecha without can_dominate_mechs or a control beacon in the mech.") + return + + if(controlled_mech) + to_chat(src, "You are already loaded into an onboard computer!") + return + if(!GLOB.cameranet.checkCameraVis(M)) + to_chat(src, "Exosuit is no longer near active cameras.") + return + if(lacks_power()) + to_chat(src, "You're depowered!") + return + if(!isturf(loc)) + to_chat(src, "You aren't in your core!") + return + if(M) + M.transfer_ai(AI_MECH_HACK, src, usr) //Called om the mech itself. + + else if(href_list["faketrack"]) + var/mob/target = locate(href_list["track"]) in GLOB.mob_list + var/mob/living/silicon/ai/A = locate(href_list["track2"]) in GLOB.mob_list + if(A && target) + + A.cameraFollow = target + to_chat(A, "Now tracking [target.name] on camera.") + if(usr.machine == null) + usr.machine = usr + + while(cameraFollow == target) + to_chat(usr, "Target is not on or near any active cameras on the station. We'll check again in 5 seconds (unless you use the cancel-camera verb).") + sleep(40) + continue + + else if(href_list["open"]) + var/mob/target = locate(href_list["open"]) in GLOB.mob_list + if(target) + open_nearest_door(target) + +/mob/living/silicon/ai/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + return 2 + +/mob/living/silicon/ai/reset_perspective(atom/A) + if(camera_light_on) + light_cameras() + if(istype(A, /obj/machinery/camera)) + current = A + if(A != GLOB.ai_camera_room_landmark) + end_multicam() + + . = ..() + if(.) + if(!A && isturf(loc) && eyeobj) + end_multicam() + client.eye = eyeobj + client.perspective = MOB_PERSPECTIVE + eyeobj.get_remote_view_fullscreens(src) + +/mob/living/silicon/ai/proc/botcall() + set category = "AI Commands" + set name = "Access Robot Control" + set desc = "Wirelessly control various automatic robots." + if(stat == 2) + to_chat(src, "Critical error. System offline.") + return + + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) + return + + var/d + var/area/bot_area + d += "Query network status
    " + d += "

    Name

    Status

    Location

    Control

    [Bot.hacked ? "(!) [Bot.name]" : Bot.name] ([Bot.model])[Bot.on ? "[Bot.mode ? "[ Bot.mode_name[Bot.mode] ]": "Idle"]" : "Inactive"][bot_area.name]InterfaceCall
    " + + for(var/mob/living/simple_animal/bot/Bot in GLOB.bots_list) + if(is_ai_allowed(Bot.z) && !Bot.remote_disabled) //Only non-emagged bots on the allowed Z-level are detected! + bot_area = get_area(Bot) + d += "" + //If the bot is on, it will display the bot's current mode status. If the bot is not mode, it will just report "Idle". "Inactive if it is not on at all. + d += "" + d += "" + d += "" + d += "" + d += "" + d = format_text(d) + + var/datum/browser/popup = new(src, "botcall", "Remote Robot Control", 700, 400) + popup.set_content(d) + popup.open() + +/mob/living/silicon/ai/proc/set_waypoint(atom/A) + var/turf/turf_check = get_turf(A) + //The target must be in view of a camera or near the core. + if(turf_check in range(get_turf(src))) + call_bot(turf_check) + else if(GLOB.cameranet && GLOB.cameranet.checkTurfVis(turf_check)) + call_bot(turf_check) + else + to_chat(src, "Selected location is not visible.") + +/mob/living/silicon/ai/proc/call_bot(turf/waypoint) + + if(!Bot) + return + + if(Bot.calling_ai && Bot.calling_ai != src) //Prevents an override if another AI is controlling this bot. + to_chat(src, "Interface error. Unit is already in use.") + return + + Bot.call_bot(src, waypoint) + +/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C) + + if(!tracking) + cameraFollow = null + + if(!C || stat == DEAD) //C.can_use()) + return FALSE + + if(!eyeobj) + view_core() + return + // ok, we're alive, camera is good and in our network... + eyeobj.setLoc(get_turf(C)) + //machine = src + + return TRUE + +//Replaces /mob/living/silicon/ai/verb/change_network() in ai.dm & camera.dm +//Adds in /mob/living/silicon/ai/proc/ai_network_change() instead +//Addition by Mord_Sith to define AI's network change ability +/mob/living/silicon/ai/proc/ai_network_change() + set category = "AI Commands" + set name = "Jump To Network" + unset_machine() + var/cameralist[0] + + if(check_unable()) + return + + if(usr.stat == 2) + to_chat(usr, "You can't change your camera network because you are dead!") + return + + var/mob/living/silicon/ai/U = usr + + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + if(!C.can_use()) + continue + + var/list/tempnetwork = difflist(C.network,GLOB.restricted_camera_networks,1) + if(tempnetwork.len) + for(var/i in tempnetwork) + cameralist[i] = i + var/old_network = network + network = input(U, "Which network would you like to view?") as null|anything in cameralist + + if(check_unable()) + return + + if(!U.eyeobj) + U.view_core() + return + + if(isnull(network)) + network = old_network // If nothing is selected + else + for(var/obj/machinery/camera/C in GLOB.cameranet.cameras) + if(!C.can_use()) + continue + if(network in C.network) + U.eyeobj.setLoc(get_turf(C)) + break + to_chat(src, "Switched to [network] camera network.") +//End of code by Mord_Sith + + +/mob/living/silicon/ai/proc/choose_modules() + set category = "Malfunction" + set name = "Choose Module" + + malf_picker.use(src) + +/mob/living/silicon/ai/proc/ai_statuschange() + set category = "AI Commands" + set name = "AI Status" + + if(usr.stat == 2) + to_chat(usr, "You cannot change your emotional status because you are dead!") + return + + if(check_unable()) + return + + var/list/ai_emotions = list("Very Happy", "Happy", "Neutral", "Unsure", "Confused", "Sad", "BSOD", "Blank", "Problems?", "Awesome", "Facepalm", "Friend Computer") + var/emote = input("Please, select a status!", "AI Status", null, null) in ai_emotions + + if(check_unable()) + return + + for(var/obj/machinery/M in GLOB.machines) //change status + if(istype(M, /obj/machinery/ai_status_display)) + var/obj/machinery/ai_status_display/AISD = M + AISD.emotion = emote + //if Friend Computer, change ALL displays + else if(istype(M, /obj/machinery/status_display)) + + var/obj/machinery/status_display/SD = M + if(emote=="Friend Computer") + SD.friendc = 1 + else + SD.friendc = 0 + return + +//I am the icon meister. Bow fefore me. //>fefore +/mob/living/silicon/ai/proc/ai_hologram_change() + set name = "Change Hologram" + set desc = "Change the default hologram available to AI to something else." + set category = "AI Commands" + + if(check_unable()) + return + if(!custom_hologram) //Check to see if custom sprite time, checking the appopriate file to change a var + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "hologram") + continue + + if (Entry[2] == ckey) //Custom holograms + custom_hologram = 1 // option is given in hologram menu + + var/input + switch(alert("Would you like to select a hologram based on a crew member, an animal, or switch to a unique avatar?",,"Crew Member","Unique","Animal")) + if("Crew Member") + var/personnel_list[] = list() + + for(var/datum/data/record/t in GLOB.data_core.locked)//Look in data core locked. + personnel_list["[t.fields["name"]]: [t.fields["rank"]]"] = t.fields["image"]//Pull names, rank, and image. + + if(personnel_list.len) + input = input("Select a crew member:") as null|anything in personnel_list + var/icon/character_icon = personnel_list[input] + if(character_icon) + qdel(holo_icon)//Clear old icon so we're not storing it in memory. + holo_icon = getHologramIcon(icon(character_icon)) + else + alert("No suitable records found. Aborting.") + + if("Animal") + var/icon_list[] = list( + "Bear", + "Carp", + "Chicken", + "Corgi", + "Cow", + "Crab", + "Deer", + "Fox", + "Goat", + "Goose", + "Kitten", + "Kitten2", + "Pig", + "Poly", + "Pug", + "Seal", + "Spider", + "Turkey" + ) + + input = input("Please select a hologram:") as null|anything in icon_list + if(input) + qdel(holo_icon) + switch(input) + if("Bear") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"bear")) + if("Carp") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"carp")) + if("Chicken") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"chicken_brown")) + if("Corgi") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"corgi")) + if("Cow") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"cow")) + if("Crab") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"crab")) + if("Deer") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"deer")) + if("Fox") + holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"fox")) + if("Goat") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"goat")) + if("Goose") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"goose")) + if("Kitten") + holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"cat")) + if("Kitten2") + holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"cat2")) + if("Pig") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"pig")) + if("Poly") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"parrot_fly")) + if("Pug") + holo_icon = getHologramIcon(icon('icons/mob/pets.dmi',"pug")) + if("Seal") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"seal")) + if("Spider") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"guard")) + if("Turkey") + holo_icon = getHologramIcon(icon('icons/mob/animal.dmi',"turkey")) + + else + var/icon_list[] = list( + "default", + "floating face", + "xeno queen", + "eldritch", + "ancient machine" + ) + if(custom_hologram) //insert custom hologram + icon_list.Add("custom") + + input = input("Please select a hologram:") as null|anything in icon_list + if(input) + qdel(holo_icon) + switch(input) + if("default") + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1")) + if("floating face") + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo2")) + if("xeno queen") + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo3")) + if("eldritch") + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo4")) + if("ancient machine") + holo_icon = getHologramIcon(icon('icons/mob/ancient_machine.dmi', "ancient_machine")) + if("custom") + if("[ckey]-ai-holo" in icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi')) + holo_icon = getHologramIcon(icon('icons/mob/custom_synthetic/custom-synthetic.dmi', "[ckey]-ai-holo")) + else if("[ckey]-ai-holo" in icon_states('icons/mob/custom_synthetic/custom-synthetic64.dmi')) + holo_icon = getHologramIcon(icon('icons/mob/custom_synthetic/custom-synthetic64.dmi', "[ckey]-ai-holo")) + else + holo_icon = getHologramIcon(icon('icons/mob/ai.dmi',"holo1")) + + return + +/mob/living/silicon/ai/proc/corereturn() + set category = "Malfunction" + set name = "Return to Main Core" + + var/obj/machinery/power/apc/apc = loc + if(!istype(apc)) + to_chat(src, "You are already in your Main Core.") + return + apc.malfvacate() + +//Toggles the luminosity and applies it by re-entereing the camera. +/mob/living/silicon/ai/proc/toggle_camera_light() + set name = "Toggle Camera Lights" + set desc = "Toggles the lights on the cameras throughout the station." + set category = "AI Commands" + + if(stat != CONSCIOUS) + return + + camera_light_on = !camera_light_on + + if(!camera_light_on) + to_chat(src, "Camera lights deactivated.") + + for(var/obj/machinery/camera/C in lit_cameras) + C.set_light(0) + lit_cameras = list() + + return + + light_cameras() + + to_chat(src, "Camera lights activated.") + +/mob/living/silicon/ai/proc/set_syndie_radio() + if(aiRadio) + aiRadio.make_syndie() + +/mob/living/silicon/ai/proc/sensor_mode() + set name = "Set Sensor Augmentation" + set desc = "Augment visual feed with internal sensor overlays." + set category = "AI Commands" + toggle_sensor_mode() + +/mob/living/silicon/ai/proc/change_arrival_message() + set name = "Set Arrival Message" + set desc = "Change the message that's transmitted when a new crew member arrives on station." + set category = "AI Commands" + + var/newmsg = clean_input("What would you like the arrival message to be? List of options: $name, $rank, $species, $gender, $age", "Change Arrival Message", arrivalmsg) + if(newmsg != arrivalmsg) + arrivalmsg = newmsg + to_chat(usr, "The arrival message has been successfully changed.") + +// Handled camera lighting, when toggled. +// It will get the nearest camera from the eyeobj, lighting it. + +/mob/living/silicon/ai/proc/light_cameras() + var/list/obj/machinery/camera/add = list() + var/list/obj/machinery/camera/remove = list() + var/list/obj/machinery/camera/visible = list() + for(var/datum/camerachunk/CC in eyeobj.visibleCameraChunks) + for(var/obj/machinery/camera/C in CC.cameras) + if(!C.can_use() || get_dist(C, eyeobj) > 7) + continue + visible |= C + + add = visible - lit_cameras + remove = lit_cameras - visible + + for(var/obj/machinery/camera/C in remove) + lit_cameras -= C //Removed from list before turning off the light so that it doesn't check the AI looking away. + C.Togglelight(0) + for(var/obj/machinery/camera/C in add) + C.Togglelight(1) + lit_cameras |= C + + +/mob/living/silicon/ai/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/wrench)) + if(anchored) + user.visible_message("\The [user] starts to unbolt \the [src] from the plating...") + if(!do_after(user, 40 * W.toolspeed, target = src)) + user.visible_message("\The [user] decides not to unbolt \the [src].") + return + user.visible_message("\The [user] finishes unfastening \the [src]!") + anchored = FALSE + return + else + user.visible_message("\The [user] starts to bolt \the [src] to the plating...") + if(!do_after(user, 40 * W.toolspeed, target = src)) + user.visible_message("\The [user] decides not to bolt \the [src].") + return + user.visible_message("\The [user] finishes fastening down \the [src]!") + anchored = TRUE + return + else + return ..() + +/mob/living/silicon/ai/welder_act() + return + +/mob/living/silicon/ai/proc/control_integrated_radio() + set name = "Radio Settings" + set desc = "Allows you to change settings of your radio." + set category = "AI Commands" + + if(check_unable(AI_CHECK_RADIO)) + return + + to_chat(src, "Accessing Subspace Transceiver control...") + if(aiRadio) + aiRadio.interact(src) + + +/mob/living/silicon/ai/proc/check_unable(flags = 0) + if(stat == DEAD) + to_chat(src, "You are dead!") + return TRUE + + if(lacks_power()) + to_chat(src, "Power systems failure!") + return TRUE + + if((flags & AI_CHECK_WIRELESS) && control_disabled) + to_chat(src, "Wireless control is disabled!") + return TRUE + if((flags & AI_CHECK_RADIO) && aiRadio.disabledAi) + to_chat(src, "System Error - Transceiver Disabled!") + return TRUE + return FALSE + +/mob/living/silicon/ai/proc/is_in_chassis() + return isturf(loc) + +/mob/living/silicon/ai/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/aicard/card) + if(!..()) + return + if(interaction == AI_TRANS_TO_CARD)//The only possible interaction. Upload AI mob to a card. + if(!mind) + to_chat(user, "No intelligence patterns detected.")//No more magical carding of empty cores, AI RETURN TO BODY!!!11 + return + new /obj/structure/AIcore/deactivated(loc)//Spawns a deactivated terminal at AI location. + aiRestorePowerRoutine = 0//So the AI initially has power. + control_disabled = 1//Can't control things remotely if you're stuck in a card! + aiRadio.disabledAi = 1 //No talking on the built-in radio for you either! + loc = card//Throw AI into the card. + to_chat(src, "You have been downloaded to a mobile storage device. Remote device connection severed.") + to_chat(user, "Transfer successful: [name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") + +/mob/living/silicon/ai/can_buckle() + return FALSE + +// Pass lying down or getting up to our pet human, if we're in a rig. +/mob/living/silicon/ai/lay_down() + set name = "Rest" + set category = "IC" + + resting = 0 + var/obj/item/rig/rig = get_rig() + if(rig) + rig.force_rest(src) + +/mob/living/silicon/ai/switch_to_camera(obj/machinery/camera/C) + if(!C.can_use() || !is_in_chassis()) + return FALSE + + eyeobj.setLoc(get_turf(C)) + client.eye = eyeobj + return TRUE + + +/mob/living/silicon/ai/proc/can_see(atom/A) + if(isturf(loc)) //AI in core, check if on cameras + //get_turf_pixel() is because APCs in maint aren't actually in view of the inner camera + //apc_override is needed here because AIs use their own APC when depowered + var/turf/T = isturf(A) ? A : get_turf_pixel(A) + return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T)) || apc_override + //AI is carded/shunted + //view(src) returns nothing for carded/shunted AIs and they have x-ray vision so just use get_dist + var/list/viewscale = getviewsize(client.view) + return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5) + +/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb) + var/message = combine_message(message_pieces, verb, M) + var/name_used = M.GetVoice() + //This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only. + var/rendered = "Relayed Speech: [name_used] [message]" + show_message(rendered, 2) + +/mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc) + malfhack = null + malfhacking = 0 + clear_alert("hackingapc") + + if(!istype(apc) || QDELETED(apc) || apc.stat & BROKEN) + to_chat(src, "Hack aborted. The designated APC no longer exists on the power network.") + playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1) + else if(apc.aidisabled) + to_chat(src, "Hack aborted. [apc] is no longer responding to our systems.") + playsound(get_turf(src), 'sound/machines/buzz-sigh.ogg', 50, 1) + else + malf_picker.processing_time += 10 + + apc.malfai = parent || src + apc.malfhack = TRUE + apc.locked = TRUE + + playsound(get_turf(src), 'sound/machines/ding.ogg', 50, 1) + to_chat(src, "Hack complete. [apc] is now under your exclusive control.") + apc.update_icon() + +/mob/living/silicon/ai/proc/add_malf_picker() + to_chat(src, "In the top right corner of the screen you will find the Malfunctions tab, where you can purchase various abilities, from upgraded surveillance to station ending doomsday devices.") + to_chat(src, "You are also capable of hacking APCs, which grants you more points to spend on your Malfunction powers. The drawback is that a hacked APC will give you away if spotted by the crew. Hacking an APC takes 60 seconds.") + view_core() //A BYOND bug requires you to be viewing your core before your verbs update + verbs += /mob/living/silicon/ai/proc/choose_modules + malf_picker = new /datum/module_picker + +/mob/living/silicon/ai/proc/open_nearest_door(mob/living/target) + if(!istype(target)) + return + + if(target && target.can_track()) + var/obj/machinery/door/airlock/A = null + + var/dist = -1 + for(var/obj/machinery/door/airlock/D in range(3, target)) + if(!D.density) + continue + + var/curr_dist = get_dist(D, target) + + if(dist < 0) + dist = curr_dist + A = D + else if(dist > curr_dist) + dist = curr_dist + A = D + + if(istype(A)) + switch(alert(src, "Do you want to open \the [A] for [target]?", "Doorknob_v2a.exe", "Yes", "No")) + if("Yes") + A.AIShiftClick() + to_chat(src, "You open \the [A] for [target].") + else + to_chat(src, "You deny the request.") + else + to_chat(src, "Unable to locate an airlock near [target].") + + else + to_chat(src, "Target is not on or near any active cameras on the station.") + +/mob/living/silicon/ai/proc/camera_visibility(mob/camera/aiEye/moved_eye) + GLOB.cameranet.visibility(moved_eye, client, all_eyes) + +/mob/living/silicon/ai/forceMove(atom/destination) + . = ..() + if(.) + end_multicam() + +/mob/living/silicon/ai/handle_fire() + return + +/mob/living/silicon/ai/update_fire() + return + +/mob/living/silicon/ai/IgniteMob() + return FALSE + +/mob/living/silicon/ai/ExtinguishMob() + return + + +/mob/living/silicon/ai/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + lighting_alpha = initial(lighting_alpha) + + if(aiRestorePowerRoutine) + sight = sight &~ SEE_TURFS + sight = sight &~ SEE_MOBS + sight = sight &~ SEE_OBJS + see_in_dark = 0 + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index ba418ef44c65..641b1a2af0fc 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -1,41 +1,41 @@ -/mob/living/silicon/ai/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - if(custom_sprite == 1)//check for custom AI sprite, defaulting to blue screen if no. - icon_state = "[ckey]-ai_dead" - else if("[icon_state]_dead" in icon_states(icon,1)) - icon_state = "[icon_state]_dead" - else - icon_state = "ai_dead" - if(eyeobj) - eyeobj.setLoc(get_turf(src)) - - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - - if(nuking) - set_security_level("red") - nuking = 0 - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - point.the_disk = null //Point back to the disk. - - if(doomsday_device) - doomsday_device.timing = 0 - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - qdel(doomsday_device) - - if(explosive) - spawn(10) - explosion(src.loc, 3, 6, 12, 15) - - for(var/obj/machinery/ai_status_display/O in world) //change status - O.mode = 2 - - if(istype(loc, /obj/item/aicard)) - loc.icon_state = "aicard-404" +/mob/living/silicon/ai/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + if(custom_sprite == 1)//check for custom AI sprite, defaulting to blue screen if no. + icon_state = "[ckey]-ai_dead" + else if("[icon_state]_dead" in icon_states(icon,1)) + icon_state = "[icon_state]_dead" + else + icon_state = "ai_dead" + if(eyeobj) + eyeobj.setLoc(get_turf(src)) + + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + + if(nuking) + set_security_level("red") + nuking = 0 + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + point.the_disk = null //Point back to the disk. + + if(doomsday_device) + doomsday_device.timing = 0 + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + GLOB.priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + qdel(doomsday_device) + + if(explosive) + spawn(10) + explosion(src.loc, 3, 6, 12, 15) + + for(var/obj/machinery/ai_status_display/O in world) //change status + O.mode = 2 + + if(istype(loc, /obj/item/aicard)) + loc.icon_state = "aicard-404" diff --git a/code/modules/mob/living/silicon/ai/examine.dm b/code/modules/mob/living/silicon/ai/examine.dm index 1b8a4fa43c32..c881fa08e863 100644 --- a/code/modules/mob/living/silicon/ai/examine.dm +++ b/code/modules/mob/living/silicon/ai/examine.dm @@ -1,34 +1,34 @@ -/mob/living/silicon/ai/examine(mob/user) - . = ..() - var/msg = "" - if(src.stat == DEAD) - msg += "It appears to be powered-down.\n" - else - msg += "" - if(src.getBruteLoss()) - if(src.getBruteLoss() < 30) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - if(src.getFireLoss()) - if(src.getFireLoss() < 30) - msg += "It looks slightly charred.\n" - else - msg += "Its casing is melted and heat-warped!\n" - if(src.stat == UNCONSCIOUS) - msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n" - if(!shunted && !client) - msg += "[src]Core.exe has stopped responding! NTOS is searching for a solution to the problem...\n" - msg += "" - msg += "*---------*" - - . += msg - user.showLaws(src) - - -/mob/proc/showLaws(var/mob/living/silicon/S) - return - -/mob/dead/observer/showLaws(var/mob/living/silicon/S) - if(antagHUD || check_rights(R_ADMIN, 0, src)) - S.laws.show_laws(src) +/mob/living/silicon/ai/examine(mob/user) + . = ..() + var/msg = "" + if(src.stat == DEAD) + msg += "It appears to be powered-down.\n" + else + msg += "" + if(src.getBruteLoss()) + if(src.getBruteLoss() < 30) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + if(src.getFireLoss()) + if(src.getFireLoss() < 30) + msg += "It looks slightly charred.\n" + else + msg += "Its casing is melted and heat-warped!\n" + if(src.stat == UNCONSCIOUS) + msg += "It is non-responsive and displaying the text: \"RUNTIME: Sensory Overload, stack 26/3\".\n" + if(!shunted && !client) + msg += "[src]Core.exe has stopped responding! NTOS is searching for a solution to the problem...\n" + msg += "" + msg += "*---------*" + + . += msg + user.showLaws(src) + + +/mob/proc/showLaws(var/mob/living/silicon/S) + return + +/mob/dead/observer/showLaws(var/mob/living/silicon/S) + if(antagHUD || check_rights(R_ADMIN, 0, src)) + S.laws.show_laws(src) diff --git a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm index f6b929826bd1..d9f4af600afc 100644 --- a/code/modules/mob/living/silicon/ai/freelook/cameranet.dm +++ b/code/modules/mob/living/silicon/ai/freelook/cameranet.dm @@ -4,7 +4,7 @@ #define CHUNK_SIZE 16 // Only chunk sizes that are to the power of 2. E.g: 2, 4, 8, 16, etc.. -var/datum/cameranet/cameranet = new() +GLOBAL_DATUM_INIT(cameranet, /datum/cameranet, new()) /datum/cameranet var/name = "Camera Net" // Name to show for VV and stat() @@ -197,4 +197,4 @@ var/datum/cameranet/cameranet = new() if(cameranet.chunkGenerated(x, y, z)) var/datum/camerachunk/chunk = cameranet.getCameraChunk(x, y, z) usr.client.debug_variables(chunk) -*/ \ No newline at end of file +*/ diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index c3c47235b910..d23fedb09bc5 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -1,151 +1,151 @@ -// AI EYE -// -// An invisible (no icon) mob that the AI controls to look around the station with. -// It streams chunks as it moves around, which will show it what the AI can and cannot see. - -/mob/camera/aiEye - name = "Inactive AI Eye" - - icon = 'icons/mob/ai.dmi' //Allows ghosts to see what the AI is looking at. - icon_state = "eye" - alpha = 127 - invisibility = SEE_INVISIBLE_OBSERVER - - var/list/visibleCameraChunks = list() - var/mob/living/silicon/ai/ai = null - var/relay_speech = FALSE - var/use_static = TRUE - var/static_visibility_range = 16 - - -// Use this when setting the aiEye's location. -// It will also stream the chunk that the new loc is in. - -/mob/camera/aiEye/setLoc(T) - if(ai) - if(!isturf(ai.loc)) - return - T = get_turf(T) - loc = T - if(use_static) - ai.camera_visibility(src) - if(ai.client && !ai.multicam_on) - ai.client.eye = src - update_parallax_contents() - //Holopad - if(ai.master_multicam) - ai.master_multicam.refresh_view() - if(istype(ai.current, /obj/machinery/hologram/holopad)) - var/obj/machinery/hologram/holopad/H = ai.current - H.move_hologram(ai, T) - -/mob/camera/aiEye/Move() - return 0 - -/mob/camera/aiEye/proc/GetViewerClient() - if(ai) - return ai.client - return null - -/mob/camera/aiEye/proc/RemoveImages() - var/client/C = GetViewerClient() - if(C && use_static) - for(var/V in visibleCameraChunks) - var/datum/camerachunk/chunk = V - C.images -= chunk.obscured - -/mob/camera/aiEye/Destroy() - if(ai) - ai.all_eyes -= src - ai = null - for(var/V in visibleCameraChunks) - var/datum/camerachunk/chunk = V - chunk.remove(src) - return ..() - -/atom/proc/move_camera_by_click() - if(istype(usr, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = usr - if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z)) - AI.cameraFollow = null - if(isturf(loc) || isturf(src)) - AI.eyeobj.setLoc(src) - -// AI MOVEMENT - -// This will move the AIEye. It will also cause lights near the eye to light up, if toggled. -// This is handled in the proc below this one. - -/client/proc/AIMove(n, direct, var/mob/living/silicon/ai/user) - - var/initial = initial(user.sprint) - var/max_sprint = 50 - - if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds - user.sprint = initial - - for(var/i = 0; i < max(user.sprint, initial); i += 20) - var/turf/step = get_turf(get_step(user.eyeobj, direct)) - if(step) - user.eyeobj.setLoc(step) - - user.cooldown = world.timeofday + 5 - if(user.acceleration) - user.sprint = min(user.sprint + 0.5, max_sprint) - else - user.sprint = initial - - if(!user.tracking) - user.cameraFollow = null - - //user.unset_machine() //Uncomment this if it causes problems. - //user.lightNearbyCamera() - if(user.camera_light_on) - user.light_cameras() - -// Return to the Core. -/mob/living/silicon/ai/proc/core() - set category = "AI Commands" - set name = "AI Core" - - view_core() - -/mob/living/silicon/ai/proc/view_core() - - current = null - cameraFollow = null - unset_machine() - - if(src.eyeobj && src.loc) - src.eyeobj.loc = src.loc - else - to_chat(src, "ERROR: Eyeobj not found. Creating new eye...") - create_eye() - - eyeobj.setLoc(loc) - -/mob/living/silicon/ai/proc/create_eye() - if(eyeobj) - return - eyeobj = new /mob/camera/aiEye() - all_eyes += eyeobj - eyeobj.ai = src - eyeobj.setLoc(loc) - eyeobj.name = "[name] (AI Eye)" - -/mob/living/silicon/ai/proc/toggle_acceleration() - set category = "AI Commands" - set name = "Toggle Camera Acceleration" - - if(usr.stat == 2) - return //won't work if dead - acceleration = !acceleration - to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") - -/mob/camera/aiEye/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol) - if(relay_speech) - if(istype(ai)) - ai.relay_speech(speaker, message_pieces, verb) - else - var/mob/M = ai - M.hear_say(message_pieces, verb, italics, speaker, speech_sound, sound_vol) +// AI EYE +// +// An invisible (no icon) mob that the AI controls to look around the station with. +// It streams chunks as it moves around, which will show it what the AI can and cannot see. + +/mob/camera/aiEye + name = "Inactive AI Eye" + + icon = 'icons/mob/ai.dmi' //Allows ghosts to see what the AI is looking at. + icon_state = "eye" + alpha = 127 + invisibility = SEE_INVISIBLE_OBSERVER + + var/list/visibleCameraChunks = list() + var/mob/living/silicon/ai/ai = null + var/relay_speech = FALSE + var/use_static = TRUE + var/static_visibility_range = 16 + + +// Use this when setting the aiEye's location. +// It will also stream the chunk that the new loc is in. + +/mob/camera/aiEye/setLoc(T) + if(ai) + if(!isturf(ai.loc)) + return + T = get_turf(T) + loc = T + if(use_static) + ai.camera_visibility(src) + if(ai.client && !ai.multicam_on) + ai.client.eye = src + update_parallax_contents() + //Holopad + if(ai.master_multicam) + ai.master_multicam.refresh_view() + if(istype(ai.current, /obj/machinery/hologram/holopad)) + var/obj/machinery/hologram/holopad/H = ai.current + H.move_hologram(ai, T) + +/mob/camera/aiEye/Move() + return 0 + +/mob/camera/aiEye/proc/GetViewerClient() + if(ai) + return ai.client + return null + +/mob/camera/aiEye/proc/RemoveImages() + var/client/C = GetViewerClient() + if(C && use_static) + for(var/V in visibleCameraChunks) + var/datum/camerachunk/chunk = V + C.images -= chunk.obscured + +/mob/camera/aiEye/Destroy() + if(ai) + ai.all_eyes -= src + ai = null + for(var/V in visibleCameraChunks) + var/datum/camerachunk/chunk = V + chunk.remove(src) + return ..() + +/atom/proc/move_camera_by_click() + if(istype(usr, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = usr + if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z)) + AI.cameraFollow = null + if(isturf(loc) || isturf(src)) + AI.eyeobj.setLoc(src) + +// AI MOVEMENT + +// This will move the AIEye. It will also cause lights near the eye to light up, if toggled. +// This is handled in the proc below this one. + +/client/proc/AIMove(n, direct, var/mob/living/silicon/ai/user) + + var/initial = initial(user.sprint) + var/max_sprint = 50 + + if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds + user.sprint = initial + + for(var/i = 0; i < max(user.sprint, initial); i += 20) + var/turf/step = get_turf(get_step(user.eyeobj, direct)) + if(step) + user.eyeobj.setLoc(step) + + user.cooldown = world.timeofday + 5 + if(user.acceleration) + user.sprint = min(user.sprint + 0.5, max_sprint) + else + user.sprint = initial + + if(!user.tracking) + user.cameraFollow = null + + //user.unset_machine() //Uncomment this if it causes problems. + //user.lightNearbyCamera() + if(user.camera_light_on) + user.light_cameras() + +// Return to the Core. +/mob/living/silicon/ai/proc/core() + set category = "AI Commands" + set name = "AI Core" + + view_core() + +/mob/living/silicon/ai/proc/view_core() + + current = null + cameraFollow = null + unset_machine() + + if(src.eyeobj && src.loc) + src.eyeobj.loc = src.loc + else + to_chat(src, "ERROR: Eyeobj not found. Creating new eye...") + create_eye() + + eyeobj.setLoc(loc) + +/mob/living/silicon/ai/proc/create_eye() + if(eyeobj) + return + eyeobj = new /mob/camera/aiEye() + all_eyes += eyeobj + eyeobj.ai = src + eyeobj.setLoc(loc) + eyeobj.name = "[name] (AI Eye)" + +/mob/living/silicon/ai/proc/toggle_acceleration() + set category = "AI Commands" + set name = "Toggle Camera Acceleration" + + if(usr.stat == 2) + return //won't work if dead + acceleration = !acceleration + to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].") + +/mob/camera/aiEye/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) + if(relay_speech) + if(istype(ai)) + ai.relay_speech(speaker, message_pieces, verb) + else + var/mob/M = ai + M.hear_say(message_pieces, verb, italics, speaker, speech_sound, sound_vol, sound_frequency) diff --git a/code/modules/mob/living/silicon/ai/freelook/read_me.dm b/code/modules/mob/living/silicon/ai/freelook/read_me.dm index 8ddb0689409a..869fcc2cdaca 100644 --- a/code/modules/mob/living/silicon/ai/freelook/read_me.dm +++ b/code/modules/mob/living/silicon/ai/freelook/read_me.dm @@ -1,51 +1,51 @@ -// CREDITS -/* - Initial code credit for this goes to Uristqwerty. - Debugging, functionality, all comments and porting by Giacom. - - Everything about freelook (or what we can put in here) will be stored here. - - - WHAT IS THIS? - - This is a replacement for the current camera movement system, of the AI. Before this, the AI had to move between cameras and could - only see what the cameras could see. Not only this but the cameras could see through walls, which created problems. - With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players. - The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and - cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it. - This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking, - the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more. - - - HOW IT WORKS - - It works by first creating a camera network datum. Inside of this camera network are "chunks" (which will be - explained later) and "cameras". The cameras list is kept up to date by obj/machinery/camera/New() and Destroy(). - - Next the camera network has chunks. These chunks are a 16x16 tile block of turfs and cameras contained inside the chunk. - These turfs are then sorted out based on what the cameras can and cannot see. If none of the cameras can see the turf, inside - the 16x16 block, it is listed as an "obscured" turf. Meaning the AI won't be able to see it. - - - HOW IT UPDATES - - The camera network uses a streaming method in order to effeciently update chunks. Since the server will have doors opening, doors closing, - turf being destroyed and other lag inducing stuff, we want to update it under certain conditions and not every tick. - - The chunks are not created straight away, only when an AI eye moves into it's area is when it gets created. - One a chunk is created, when a non glass door opens/closes or an opacity turf is destroyed, we check to see if an AI Eye is looking in the area. - We do this with the "seenby" list, which updates everytime an AI is near a chunk. If there is an AI eye inside the area, we update the chunk - that the changed atom is inside and all surrounding chunks, since a camera's vision could leak onto another chunk. If there is no AI Eye, we instead - flag the chunk to update whenever it is loaded by an AI Eye. This is basically how the chunks update and keep it in sync. We then add some lag reducing - measures, such as an UPDATE_BUFFER which stops a chunk from updating too many times in a certain time-frame, only updating if the changed atom was blocking - sight; for example, we don't update glass airlocks or floors. - - - WHERE IS EVERYTHING? - - cameranet.dm = Everything about the cameranet datum. - chunk.dm = Everything about the chunk datum. - eye.dm = Everything about the AI and the AIEye. - updating.dm = Everything about triggers that will update chunks. - -*/ \ No newline at end of file +// CREDITS +/* + Initial code credit for this goes to Uristqwerty. + Debugging, functionality, all comments and porting by Giacom. + + Everything about freelook (or what we can put in here) will be stored here. + + + WHAT IS THIS? + + This is a replacement for the current camera movement system, of the AI. Before this, the AI had to move between cameras and could + only see what the cameras could see. Not only this but the cameras could see through walls, which created problems. + With this, the AI controls an "AI Eye" mob, which moves just like a ghost; such as moving through walls and being invisible to players. + The AI's eye is set to this mob and then we use a system (explained below) to determine what the cameras around the AI Eye can and + cannot see. If the camera cannot see a turf, it will black it out, otherwise it won't and the AI will be able to see it. + This creates several features, such as.. no more see-through-wall cameras, easier to control camera movement, easier tracking, + the AI only being able to track mobs which are visible to a camera, only trackable mobs appearing on the mob list and many more. + + + HOW IT WORKS + + It works by first creating a camera network datum. Inside of this camera network are "chunks" (which will be + explained later) and "cameras". The cameras list is kept up to date by obj/machinery/camera/New() and Destroy(). + + Next the camera network has chunks. These chunks are a 16x16 tile block of turfs and cameras contained inside the chunk. + These turfs are then sorted out based on what the cameras can and cannot see. If none of the cameras can see the turf, inside + the 16x16 block, it is listed as an "obscured" turf. Meaning the AI won't be able to see it. + + + HOW IT UPDATES + + The camera network uses a streaming method in order to effeciently update chunks. Since the server will have doors opening, doors closing, + turf being destroyed and other lag inducing stuff, we want to update it under certain conditions and not every tick. + + The chunks are not created straight away, only when an AI eye moves into it's area is when it gets created. + One a chunk is created, when a non glass door opens/closes or an opacity turf is destroyed, we check to see if an AI Eye is looking in the area. + We do this with the "seenby" list, which updates everytime an AI is near a chunk. If there is an AI eye inside the area, we update the chunk + that the changed atom is inside and all surrounding chunks, since a camera's vision could leak onto another chunk. If there is no AI Eye, we instead + flag the chunk to update whenever it is loaded by an AI Eye. This is basically how the chunks update and keep it in sync. We then add some lag reducing + measures, such as an UPDATE_BUFFER which stops a chunk from updating too many times in a certain time-frame, only updating if the changed atom was blocking + sight; for example, we don't update glass airlocks or floors. + + + WHERE IS EVERYTHING? + + cameranet.dm = Everything about the cameranet datum. + chunk.dm = Everything about the chunk datum. + eye.dm = Everything about the AI and the AIEye. + updating.dm = Everything about triggers that will update chunks. + +*/ diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index 513b26ee5581..75e8854b45cf 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -1,4 +1,4 @@ -var/global/list/empty_playable_ai_cores = list() +GLOBAL_LIST_EMPTY(empty_playable_ai_cores) /hook/roundstart/proc/spawn_empty_ai() for(var/obj/effect/landmark/start/S in GLOB.landmarks_list) @@ -6,7 +6,7 @@ var/global/list/empty_playable_ai_cores = list() continue if(locate(/mob/living) in S.loc) continue - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S)) + GLOB.empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(get_turf(S)) return 1 @@ -21,8 +21,8 @@ var/global/list/empty_playable_ai_cores = list() return // We warned you. - empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc) - global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") + GLOB.empty_playable_ai_cores += new /obj/structure/AIcore/deactivated(loc) + GLOB.global_announcer.autosay("[src] has been moved to intelligence storage.", "Artificial Intelligence Oversight") //Handle job slot/tater cleanup. var/job = mind.assigned_role @@ -68,13 +68,13 @@ var/global/list/empty_playable_ai_cores = list() // Before calling this, make sure an empty core exists, or this will no-op /mob/living/silicon/ai/proc/moveToEmptyCore() - if(!empty_playable_ai_cores.len) + if(!GLOB.empty_playable_ai_cores.len) log_runtime(EXCEPTION("moveToEmptyCore called without any available cores"), src) return // IsJobAvailable for AI checks that there is an empty core available in this list - var/obj/structure/AIcore/deactivated/C = empty_playable_ai_cores[1] - empty_playable_ai_cores -= C + var/obj/structure/AIcore/deactivated/C = GLOB.empty_playable_ai_cores[1] + GLOB.empty_playable_ai_cores -= C forceMove(C.loc) view_core() diff --git a/code/modules/mob/living/silicon/ai/laws.dm b/code/modules/mob/living/silicon/ai/laws.dm index 19e0b784a58e..e5284002c04f 100755 --- a/code/modules/mob/living/silicon/ai/laws.dm +++ b/code/modules/mob/living/silicon/ai/laws.dm @@ -1,27 +1,27 @@ -/mob/living/silicon/ai/proc/show_laws_verb() - set category = "AI Commands" - set name = "Show Laws" - src.show_laws() - -/mob/living/silicon/ai/show_laws(var/everyone = 0) - var/who - - if(everyone) - who = world - else - who = src - to_chat(who, "Obey these laws:") - - src.laws_sanity_check() - src.laws.show_laws(who) - -/mob/living/silicon/ai/add_ion_law(var/law) - ..() - for(var/mob/living/silicon/robot/R in GLOB.mob_list) - if(R.lawupdate && (R.connected_ai == src)) - R.show_laws() - -/mob/living/silicon/ai/proc/ai_checklaws() - set category = "AI Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/ai/proc/show_laws_verb() + set category = "AI Commands" + set name = "Show Laws" + src.show_laws() + +/mob/living/silicon/ai/show_laws(var/everyone = 0) + var/who + + if(everyone) + who = world + else + who = src + to_chat(who, "Obey these laws:") + + src.laws_sanity_check() + src.laws.show_laws(who) + +/mob/living/silicon/ai/add_ion_law(var/law) + ..() + for(var/mob/living/silicon/robot/R in GLOB.mob_list) + if(R.lawupdate && (R.connected_ai == src)) + R.show_laws() + +/mob/living/silicon/ai/proc/ai_checklaws() + set category = "AI Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 51d5820d2bf5..8d1555182a26 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -1,154 +1,154 @@ -#define POWER_RESTORATION_OFF 0 -#define POWER_RESTORATION_START 1 -#define POWER_RESTORATION_SEARCH_APC 2 -#define POWER_RESTORATION_APC_FOUND 3 - -/mob/living/silicon/ai/Life(seconds, times_fired) - //doesn't call parent because it's a horrible mess - if(stat == DEAD) - return - - var/turf/T = get_turf(src) - if(stat != CONSCIOUS) //ai's fucked - cameraFollow = null - reset_perspective(null) - unset_machine() - - updatehealth("life") - if(stat == DEAD) - return - update_gravity(mob_has_gravity()) - - if(!eyeobj || QDELETED(eyeobj) || !eyeobj.loc) - view_core() - - if(machine) - machine.check_eye(src) - - if(malfhack && malfhack.aidisabled) - to_chat(src, "ERROR: APC access disabled, hack attempt canceled.") - deltimer(malfhacking) - // This proc handles cleanup of screen notifications and - // messenging the client - malfhacked(malfhack) - - if(aiRestorePowerRoutine) - adjustOxyLoss(1) - else - adjustOxyLoss(-1) - - handle_stunned() - - var/area/my_area = get_area(src) - - if(!lacks_power()) - if(aiRestorePowerRoutine > 1) - update_blind_effects() - aiRestorePowerRoutine = 0 - update_sight() - to_chat(src, "Alert cancelled. Power has been restored[aiRestorePowerRoutine == 2 ? "without our assistance" : ""].") - else - if(lacks_power()) - if(!aiRestorePowerRoutine) - update_blind_effects() - aiRestorePowerRoutine = 1 - update_sight() - to_chat(src, "You have lost power!") - if(!is_special_character(src)) - set_zeroth_law("") - - spawn(20) - to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") - end_multicam() - sleep(50) - my_area = get_area(src) - T = get_turf(src) - if(!lacks_power()) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - update_blind_effects() - update_sight() - return - to_chat(src, "Fault confirmed: missing external power. Shutting down main control system to save power.") - sleep(20) - to_chat(src, "Emergency control system online. Verifying connection to power network.") - sleep(50) - T = get_turf(src) - if(istype(T, /turf/space)) - to_chat(src, "Unable to verify! No power connection detected!") - aiRestorePowerRoutine = 2 - return - to_chat(src, "Connection verified. Searching for APC in power network.") - sleep(50) - - my_area = get_area(src) - T = get_turf(src) - - var/obj/machinery/power/apc/theAPC = null - - var/PRP - for(PRP = 1, PRP <= 4, PRP++) - for(var/obj/machinery/power/apc/APC in my_area) - if(!(APC.stat & BROKEN)) - theAPC = APC - break - - if(!theAPC) - switch(PRP) - if(1) - to_chat(src, "Unable to locate APC!") - else - to_chat(src, "Lost connection with the APC!") - aiRestorePowerRoutine = 2 - return - - if(!lacks_power()) - to_chat(src, "Alert cancelled. Power has been restored without our assistance.") - aiRestorePowerRoutine = 0 - update_blind_effects() - update_sight() - to_chat(src, "Here are your current laws:") - show_laws() - return - - switch(PRP) - if(1) - to_chat(src, "APC located. Optimizing route to APC to avoid needless power waste.") - if(2) - to_chat(src, "Best route identified. Hacking offline APC power port.") - if(3) - to_chat(src, "Power port upload access confirmed. Loading control program into APC power port software.") - if(4) - to_chat(src, "Transfer complete. Forcing APC to execute program.") - sleep(50) - to_chat(src, "Receiving control information from APC.") - sleep(2) - //bring up APC dialog - apc_override = 1 - theAPC.attack_ai(src) - apc_override = 0 - aiRestorePowerRoutine = 3 - sleep(50) - theAPC = null - - process_queued_alarms() - -/mob/living/silicon/ai/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = 100 - stat = CONSCIOUS - else - health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - update_stat("updatehealth([reason])") - diag_hud_set_status() - diag_hud_set_health() - - -/mob/living/silicon/ai/proc/lacks_power() - var/turf/T = get_turf(src) - var/area/A = get_area(src) - return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) - -/mob/living/silicon/ai/rejuvenate() - ..() - add_ai_verbs(src) +#define POWER_RESTORATION_OFF 0 +#define POWER_RESTORATION_START 1 +#define POWER_RESTORATION_SEARCH_APC 2 +#define POWER_RESTORATION_APC_FOUND 3 + +/mob/living/silicon/ai/Life(seconds, times_fired) + //doesn't call parent because it's a horrible mess + if(stat == DEAD) + return + + var/turf/T = get_turf(src) + if(stat != CONSCIOUS) //ai's fucked + cameraFollow = null + reset_perspective(null) + unset_machine() + + updatehealth("life") + if(stat == DEAD) + return + update_gravity(mob_has_gravity()) + + if(!eyeobj || QDELETED(eyeobj) || !eyeobj.loc) + view_core() + + if(machine) + machine.check_eye(src) + + if(malfhack && malfhack.aidisabled) + to_chat(src, "ERROR: APC access disabled, hack attempt canceled.") + deltimer(malfhacking) + // This proc handles cleanup of screen notifications and + // messenging the client + malfhacked(malfhack) + + if(aiRestorePowerRoutine) + adjustOxyLoss(1) + else + adjustOxyLoss(-1) + + handle_stunned() + + var/area/my_area = get_area(src) + + if(!lacks_power()) + if(aiRestorePowerRoutine > 1) + update_blind_effects() + aiRestorePowerRoutine = 0 + update_sight() + to_chat(src, "Alert cancelled. Power has been restored[aiRestorePowerRoutine == 2 ? "without our assistance" : ""].") + else + if(lacks_power()) + if(!aiRestorePowerRoutine) + update_blind_effects() + aiRestorePowerRoutine = 1 + update_sight() + to_chat(src, "You have lost power!") + if(!is_special_character(src)) + set_zeroth_law("") + + spawn(20) + to_chat(src, "Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection.") + end_multicam() + sleep(50) + my_area = get_area(src) + T = get_turf(src) + if(!lacks_power()) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + update_blind_effects() + update_sight() + return + to_chat(src, "Fault confirmed: missing external power. Shutting down main control system to save power.") + sleep(20) + to_chat(src, "Emergency control system online. Verifying connection to power network.") + sleep(50) + T = get_turf(src) + if(istype(T, /turf/space)) + to_chat(src, "Unable to verify! No power connection detected!") + aiRestorePowerRoutine = 2 + return + to_chat(src, "Connection verified. Searching for APC in power network.") + sleep(50) + + my_area = get_area(src) + T = get_turf(src) + + var/obj/machinery/power/apc/theAPC = null + + var/PRP + for(PRP = 1, PRP <= 4, PRP++) + for(var/obj/machinery/power/apc/APC in my_area) + if(!(APC.stat & BROKEN)) + theAPC = APC + break + + if(!theAPC) + switch(PRP) + if(1) + to_chat(src, "Unable to locate APC!") + else + to_chat(src, "Lost connection with the APC!") + aiRestorePowerRoutine = 2 + return + + if(!lacks_power()) + to_chat(src, "Alert cancelled. Power has been restored without our assistance.") + aiRestorePowerRoutine = 0 + update_blind_effects() + update_sight() + to_chat(src, "Here are your current laws:") + show_laws() + return + + switch(PRP) + if(1) + to_chat(src, "APC located. Optimizing route to APC to avoid needless power waste.") + if(2) + to_chat(src, "Best route identified. Hacking offline APC power port.") + if(3) + to_chat(src, "Power port upload access confirmed. Loading control program into APC power port software.") + if(4) + to_chat(src, "Transfer complete. Forcing APC to execute program.") + sleep(50) + to_chat(src, "Receiving control information from APC.") + sleep(2) + //bring up APC dialog + apc_override = 1 + theAPC.attack_ai(src) + apc_override = 0 + aiRestorePowerRoutine = 3 + sleep(50) + theAPC = null + + process_queued_alarms() + +/mob/living/silicon/ai/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = 100 + stat = CONSCIOUS + else + health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() + update_stat("updatehealth([reason])") + diag_hud_set_status() + diag_hud_set_health() + + +/mob/living/silicon/ai/proc/lacks_power() + var/turf/T = get_turf(src) + var/area/A = get_area(src) + return ((!A.power_equip) && A.requires_power == 1 || istype(T, /turf/space)) && !istype(src.loc,/obj/item) + +/mob/living/silicon/ai/rejuvenate() + ..() + add_ai_verbs(src) diff --git a/code/modules/mob/living/silicon/ai/login.dm b/code/modules/mob/living/silicon/ai/login.dm index af3664edab77..7d4244d9aec2 100644 --- a/code/modules/mob/living/silicon/ai/login.dm +++ b/code/modules/mob/living/silicon/ai/login.dm @@ -1,15 +1,15 @@ -/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up �_� ~Carn - ..() - for(var/obj/effect/rune/rune in world) - var/image/blood = image(loc = rune) - blood.override = 1 - client.images += blood - regenerate_icons() - - if(stat != DEAD) - for(var/obj/machinery/ai_status_display/O in GLOB.machines) //change status - O.mode = 1 - O.emotion = "Neutral" - if(multicam_on) - end_multicam() - view_core() \ No newline at end of file +/mob/living/silicon/ai/Login() //ThisIsDumb(TM) TODO: tidy this up �_� ~Carn + ..() + for(var/obj/effect/rune/rune in world) + var/image/blood = image(loc = rune) + blood.override = 1 + client.images += blood + regenerate_icons() + + if(stat != DEAD) + for(var/obj/machinery/ai_status_display/O in GLOB.machines) //change status + O.mode = 1 + O.emotion = "Neutral" + if(multicam_on) + end_multicam() + view_core() diff --git a/code/modules/mob/living/silicon/ai/logout.dm b/code/modules/mob/living/silicon/ai/logout.dm index 00945ad84b77..a8060abd15c9 100644 --- a/code/modules/mob/living/silicon/ai/logout.dm +++ b/code/modules/mob/living/silicon/ai/logout.dm @@ -1,6 +1,6 @@ -/mob/living/silicon/ai/Logout() - ..() - for(var/obj/machinery/ai_status_display/O in world) //change status - O.mode = 0 - src.view_core() - return +/mob/living/silicon/ai/Logout() + ..() + for(var/obj/machinery/ai_status_display/O in world) //change status + O.mode = 0 + src.view_core() + return diff --git a/code/modules/mob/living/silicon/ai/multicam.dm b/code/modules/mob/living/silicon/ai/multicam.dm index 254d5a9b7940..d33e57ea9719 100644 --- a/code/modules/mob/living/silicon/ai/multicam.dm +++ b/code/modules/mob/living/silicon/ai/multicam.dm @@ -135,7 +135,7 @@ GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room) if(screen && screen.ai) screen.ai.camera_visibility(src) else - cameranet.visibility(src) + GLOB.cameranet.visibility(src) update_camera_telegraphing() /mob/camera/aiEye/pic_in_pic/proc/update_camera_telegraphing() diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm index 834dc4e57108..599063c27241 100644 --- a/code/modules/mob/living/silicon/ai/say.dm +++ b/code/modules/mob/living/silicon/ai/say.dm @@ -1,185 +1,185 @@ -/* - * AI Saycode - */ - - -/mob/living/silicon/ai/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) - if(hard_to_hear) - return - - var/jobname // the mob's "job" - var/mob/living/carbon/human/impersonating //The crewmember being impersonated, if any. - var/changed_voice - - if(ishuman(speaker)) - var/mob/living/carbon/human/H = speaker - - var/obj/item/card/id/id = H.wear_id - if((istype(id) && id.is_untrackable()) && H.HasVoiceChanger()) - changed_voice = 1 - var/mob/living/carbon/human/I = locate(speaker_name) - if(I) - impersonating = I - jobname = impersonating.get_assignment() - else - jobname = "Unknown" - else - jobname = H.get_assignment() - - else if(iscarbon(speaker)) // Nonhuman carbon mob - jobname = "No ID" - else if(isAI(speaker)) - jobname = "AI" - else if(isrobot(speaker)) - jobname = "Cyborg" - else if(ispAI(speaker)) - jobname = "Personal AI" - else if(isAutoAnnouncer(speaker)) - var/mob/living/automatedannouncer/AA = speaker - jobname = AA.role - else - jobname = "Unknown" - - var/track = "" - var/mob/mob_to_track = null - if(changed_voice) - if(impersonating) - mob_to_track = impersonating - else - track = "[speaker_name] ([jobname])" - else - if(istype(follow_target, /mob/living/simple_animal/bot)) - track = "[speaker_name] ([jobname])" - else - mob_to_track = speaker - - if(mob_to_track) - track = "[speaker_name] ([jobname])" - track += " \[Open\]" - - return track - - - -/* - * AI VOX Announcements - */ - -var/announcing_vox = 0 // Stores the time of the last announcement -#define VOX_DELAY 100 -#define VOX_PATH "sound/vox_fem/" - -/mob/living/silicon/ai/verb/announcement_help() - set name = "Announcement Help" - set desc = "Display a list of vocal words to announce to the crew." - set category = "AI Commands" - - var/dat = "Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.
    \ -
    • You can also click on the word to preview it.
    • \ -
    • You can only say 30 words for every announcement.
    • \ -
    • Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.
    \ - WARNING:
    Misuse of the announcement system will get you job banned.
    " - - var/index = 0 - for(var/word in vox_sounds) - index++ - dat += "[capitalize(word)]" - if(index != vox_sounds.len) - dat += " / " - - var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400) - popup.set_content(dat) - popup.open() - -/mob/living/silicon/ai/proc/ai_announcement() - if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) - return - - if(announcing_vox > world.time) - to_chat(src, "Please wait [round((announcing_vox - world.time) / 10)] seconds.") - return - - var/message = clean_input("WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help'", "Announcement", last_announcement, src) - - last_announcement = message - - if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) - return - - if(!message || announcing_vox > world.time) - return - - var/list/words = splittext(trim(message), " ") - var/list/incorrect_words = list() - - if(words.len > 30) - words.len = 30 - - for(var/word in words) - word = lowertext(trim(word)) - if(!word) - words -= word - continue - if(!vox_sounds[word]) - incorrect_words += word - - if(incorrect_words.len) - to_chat(src, "These words are not available on the announcement system: [english_list(incorrect_words)].") - return - - announcing_vox = world.time + VOX_DELAY - - log_game("[key_name(src)] made a vocal announcement: [message].") - message_admins("[key_name_admin(src)] made a vocal announcement: [message].") - - for(var/word in words) - play_vox_word(word, src.z, null) - - ai_voice_announcement_to_text(words) - - -/mob/living/silicon/ai/proc/ai_voice_announcement_to_text(words) - var/words_string = jointext(words, " ") - var/formatted_message = "

    A.I. Announcement

    " - formatted_message += "
    [words_string]" - formatted_message += "
    -[src]" - - for(var/player in GLOB.player_list) - var/mob/M = player - if(M.client && !(M.client.prefs.sound & SOUND_AI_VOICE)) - var/turf/T = get_turf(M) - if(T && T.z == z && M.can_hear()) - SEND_SOUND(M, 'sound/misc/notice2.ogg') - to_chat(M, formatted_message) - -/proc/play_vox_word(word, z_level, mob/only_listener) - - word = lowertext(word) - - if(vox_sounds[word]) - - var/sound_file = vox_sounds[word] - var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX) - voice.status = SOUND_STREAM - - // If there is no single listener, broadcast to everyone in the same z level - if(!only_listener) - // Play voice for all mobs in the z level - for(var/mob/M in GLOB.player_list) - if(M.client && M.client.prefs.sound & SOUND_AI_VOICE) - var/turf/T = get_turf(M) - if(T && T.z == z_level && M.can_hear()) - M << voice - else - only_listener << voice - return 1 - return 0 - -// VOX sounds moved to /code/defines/vox_sounds.dm - -/client/proc/preload_vox() - var/list/vox_files = flist(VOX_PATH) - for(var/file in vox_files) -// to_chat(src, "Downloading [file]") - var/sound/S = sound("[VOX_PATH][file]") - src << browse_rsc(S) +/* + * AI Saycode + */ + + +/mob/living/silicon/ai/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear) + if(hard_to_hear) + return + + var/jobname // the mob's "job" + var/mob/living/carbon/human/impersonating //The crewmember being impersonated, if any. + var/changed_voice + + if(ishuman(speaker)) + var/mob/living/carbon/human/H = speaker + + var/obj/item/card/id/id = H.wear_id + if((istype(id) && id.is_untrackable()) && H.HasVoiceChanger()) + changed_voice = 1 + var/mob/living/carbon/human/I = locate(speaker_name) + if(I) + impersonating = I + jobname = impersonating.get_assignment() + else + jobname = "Unknown" + else + jobname = H.get_assignment() + + else if(iscarbon(speaker)) // Nonhuman carbon mob + jobname = "No ID" + else if(isAI(speaker)) + jobname = "AI" + else if(isrobot(speaker)) + jobname = "Cyborg" + else if(ispAI(speaker)) + jobname = "Personal AI" + else if(isAutoAnnouncer(speaker)) + var/mob/living/automatedannouncer/AA = speaker + jobname = AA.role + else + jobname = "Unknown" + + var/track = "" + var/mob/mob_to_track = null + if(changed_voice) + if(impersonating) + mob_to_track = impersonating + else + track = "[speaker_name] ([jobname])" + else + if(istype(follow_target, /mob/living/simple_animal/bot)) + track = "[speaker_name] ([jobname])" + else + mob_to_track = speaker + + if(mob_to_track) + track = "[speaker_name] ([jobname])" + track += " \[Open\]" + + return track + + + +/* + * AI VOX Announcements + */ + +GLOBAL_VAR_INIT(announcing_vox, 0) // Stores the time of the last announcement +#define VOX_DELAY 100 +#define VOX_PATH "sound/vox_fem/" + +/mob/living/silicon/ai/verb/announcement_help() + set name = "Announcement Help" + set desc = "Display a list of vocal words to announce to the crew." + set category = "AI Commands" + + var/dat = "Here is a list of words you can type into the 'Announcement' button to create sentences to vocally announce to everyone on the same level at you.
    \ +
    • You can also click on the word to preview it.
    • \ +
    • You can only say 30 words for every announcement.
    • \ +
    • Do not use punctuation as you would normally, if you want a pause you can use the full stop and comma characters by separating them with spaces, like so: 'Alpha . Test , Bravo'.
    \ + WARNING:
    Misuse of the announcement system will get you job banned.
    " + + var/index = 0 + for(var/word in GLOB.vox_sounds) + index++ + dat += "[capitalize(word)]" + if(index != GLOB.vox_sounds.len) + dat += " / " + + var/datum/browser/popup = new(src, "announce_help", "Announcement Help", 500, 400) + popup.set_content(dat) + popup.open() + +/mob/living/silicon/ai/proc/ai_announcement() + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) + return + + if(GLOB.announcing_vox > world.time) + to_chat(src, "Please wait [round((GLOB.announcing_vox - world.time) / 10)] seconds.") + return + + var/message = clean_input("WARNING: Misuse of this verb can result in you being job banned. More help is available in 'Announcement Help'", "Announcement", last_announcement, src) + + last_announcement = message + + if(check_unable(AI_CHECK_WIRELESS | AI_CHECK_RADIO)) + return + + if(!message || GLOB.announcing_vox > world.time) + return + + var/list/words = splittext(trim(message), " ") + var/list/incorrect_words = list() + + if(words.len > 30) + words.len = 30 + + for(var/word in words) + word = lowertext(trim(word)) + if(!word) + words -= word + continue + if(!GLOB.vox_sounds[word]) + incorrect_words += word + + if(incorrect_words.len) + to_chat(src, "These words are not available on the announcement system: [english_list(incorrect_words)].") + return + + GLOB.announcing_vox = world.time + VOX_DELAY + + log_game("[key_name(src)] made a vocal announcement: [message].") + message_admins("[key_name_admin(src)] made a vocal announcement: [message].") + + for(var/word in words) + play_vox_word(word, src.z, null) + + ai_voice_announcement_to_text(words) + + +/mob/living/silicon/ai/proc/ai_voice_announcement_to_text(words) + var/words_string = jointext(words, " ") + var/formatted_message = "

    A.I. Announcement

    " + formatted_message += "
    [words_string]" + formatted_message += "
    -[src]" + + for(var/player in GLOB.player_list) + var/mob/M = player + if(M.client && !(M.client.prefs.sound & SOUND_AI_VOICE)) + var/turf/T = get_turf(M) + if(T && T.z == z && M.can_hear()) + SEND_SOUND(M, 'sound/misc/notice2.ogg') + to_chat(M, formatted_message) + +/proc/play_vox_word(word, z_level, mob/only_listener) + + word = lowertext(word) + + if(GLOB.vox_sounds[word]) + + var/sound_file = GLOB.vox_sounds[word] + var/sound/voice = sound(sound_file, wait = 1, channel = CHANNEL_VOX) + voice.status = SOUND_STREAM + + // If there is no single listener, broadcast to everyone in the same z level + if(!only_listener) + // Play voice for all mobs in the z level + for(var/mob/M in GLOB.player_list) + if(M.client && M.client.prefs.sound & SOUND_AI_VOICE) + var/turf/T = get_turf(M) + if(T && T.z == z_level && M.can_hear()) + M << voice + else + only_listener << voice + return 1 + return 0 + +// VOX sounds moved to /code/defines/vox_sounds.dm + +/client/proc/preload_vox() + var/list/vox_files = flist(VOX_PATH) + for(var/file in vox_files) +// to_chat(src, "Downloading [file]") + var/sound/S = sound("[VOX_PATH][file]") + src << browse_rsc(S) diff --git a/code/modules/mob/living/silicon/decoy/death.dm b/code/modules/mob/living/silicon/decoy/death.dm index 46a60822aa8a..9e58001f47b2 100644 --- a/code/modules/mob/living/silicon/decoy/death.dm +++ b/code/modules/mob/living/silicon/decoy/death.dm @@ -1,10 +1,10 @@ -/mob/living/silicon/decoy/death(gibbed) - // Only execute the below if we successfully died - . = ..() - if(!.) - return FALSE - icon_state = "ai-crash" - for(var/obj/machinery/ai_status_display/O in world) //change status - if(atoms_share_level(O, src)) - O.mode = 2 - gib() +/mob/living/silicon/decoy/death(gibbed) + // Only execute the below if we successfully died + . = ..() + if(!.) + return FALSE + icon_state = "ai-crash" + for(var/obj/machinery/ai_status_display/O in world) //change status + if(atoms_share_level(O, src)) + O.mode = 2 + gib() diff --git a/code/modules/mob/living/silicon/decoy/decoy.dm b/code/modules/mob/living/silicon/decoy/decoy.dm index 64965ae518f0..2c47482c3f03 100644 --- a/code/modules/mob/living/silicon/decoy/decoy.dm +++ b/code/modules/mob/living/silicon/decoy/decoy.dm @@ -1,62 +1,62 @@ -/mob/living/silicon/decoy - name = "AI" - icon = 'icons/mob/ai.dmi'// - icon_state = "ai" - anchored = 1 // -- TLE - canmove = 0 - a_intent = INTENT_HARM // This is apparently the only thing that stops other mobs walking through them as if they were thin air. - -/mob/living/silicon/decoy/New() - src.icon = 'icons/mob/ai.dmi' - src.icon_state = "ai" - src.anchored = 1 - src.canmove = 0 - -/mob/living/silicon/decoy/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/aicard)) - to_chat(user, "You cannot find an intellicard slot on [src].") - return TRUE - else - return ..() - -/mob/living/silicon/decoy/welder_act() - return - -/mob/living/silicon/decoy/syndicate - faction = list("syndicate") - name = "R.O.D.G.E.R" - desc = "Red Operations, Depot General Emission Regulator" - icon_state = "ai-magma" - -/mob/living/silicon/decoy/syndicate/New() - . = ..() - icon_state = "ai-magma" - -/mob/living/silicon/decoy/syndicate/depot - universal_speak = TRUE - universal_understand = TRUE - var/raised_alert = FALSE - -/mob/living/silicon/decoy/syndicate/depot/proc/raise_alert() - raised_alert = TRUE - var/area/syndicate_depot/core/depotarea = get_area(src) // Cannot use myArea or areaMaster as neither will be defined for this mob type - if(istype(depotarea)) - depotarea.increase_alert("AI Unit Offline") - else - say("Connection failure!") - -/mob/living/silicon/decoy/syndicate/depot/death(pass) - if(!raised_alert) - raise_alert() - . = ..(pass) - -/mob/living/silicon/decoy/syndicate/depot/adjustBruteLoss(dmg) - . = ..(dmg) - updatehealth() - -/mob/living/silicon/decoy/syndicate/depot/adjustFireLoss(dmg) - . = ..(dmg) - updatehealth() - -/mob/living/silicon/decoy/syndicate/depot/ex_act(severity) - adjustBruteLoss(250) \ No newline at end of file +/mob/living/silicon/decoy + name = "AI" + icon = 'icons/mob/ai.dmi'// + icon_state = "ai" + anchored = 1 // -- TLE + canmove = 0 + a_intent = INTENT_HARM // This is apparently the only thing that stops other mobs walking through them as if they were thin air. + +/mob/living/silicon/decoy/New() + src.icon = 'icons/mob/ai.dmi' + src.icon_state = "ai" + src.anchored = 1 + src.canmove = 0 + +/mob/living/silicon/decoy/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/aicard)) + to_chat(user, "You cannot find an intellicard slot on [src].") + return TRUE + else + return ..() + +/mob/living/silicon/decoy/welder_act() + return + +/mob/living/silicon/decoy/syndicate + faction = list("syndicate") + name = "R.O.D.G.E.R" + desc = "Red Operations, Depot General Emission Regulator" + icon_state = "ai-magma" + +/mob/living/silicon/decoy/syndicate/New() + . = ..() + icon_state = "ai-magma" + +/mob/living/silicon/decoy/syndicate/depot + universal_speak = TRUE + universal_understand = TRUE + var/raised_alert = FALSE + +/mob/living/silicon/decoy/syndicate/depot/proc/raise_alert() + raised_alert = TRUE + var/area/syndicate_depot/core/depotarea = get_area(src) // Cannot use myArea or areaMaster as neither will be defined for this mob type + if(istype(depotarea)) + depotarea.increase_alert("AI Unit Offline") + else + say("Connection failure!") + +/mob/living/silicon/decoy/syndicate/depot/death(pass) + if(!raised_alert) + raise_alert() + . = ..(pass) + +/mob/living/silicon/decoy/syndicate/depot/adjustBruteLoss(dmg) + . = ..(dmg) + updatehealth() + +/mob/living/silicon/decoy/syndicate/depot/adjustFireLoss(dmg) + . = ..(dmg) + updatehealth() + +/mob/living/silicon/decoy/syndicate/depot/ex_act(severity) + adjustBruteLoss(250) diff --git a/code/modules/mob/living/silicon/decoy/life.dm b/code/modules/mob/living/silicon/decoy/life.dm index b8919373faff..3531453e3400 100644 --- a/code/modules/mob/living/silicon/decoy/life.dm +++ b/code/modules/mob/living/silicon/decoy/life.dm @@ -1,19 +1,19 @@ -/mob/living/silicon/decoy/Life(seconds, times_fired) - return - - -/mob/living/silicon/decoy/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = 100 - stat = CONSCIOUS - else - health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - update_stat("updatehealth([reason])") - - -/mob/living/silicon/decoy/update_stat(reason = "none given") - if(stat == DEAD) - return - if(health <= 0) - death() - create_debug_log("died of damage, trigger reason: [reason]") +/mob/living/silicon/decoy/Life(seconds, times_fired) + return + + +/mob/living/silicon/decoy/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = 100 + stat = CONSCIOUS + else + health = 100 - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() + update_stat("updatehealth([reason])") + + +/mob/living/silicon/decoy/update_stat(reason = "none given") + if(stat == DEAD) + return + if(health <= 0) + death() + create_debug_log("died of damage, trigger reason: [reason]") diff --git a/code/modules/mob/living/silicon/login.dm b/code/modules/mob/living/silicon/login.dm index 4892441b8040..80b9fb460539 100644 --- a/code/modules/mob/living/silicon/login.dm +++ b/code/modules/mob/living/silicon/login.dm @@ -1,12 +1,12 @@ -/mob/living/silicon/Login() - SetSleeping(0) - if(mind && SSticker && SSticker.mode) - SSticker.mode.remove_revolutionary(mind, 1) - SSticker.mode.remove_cultist(mind, 1) - SSticker.mode.remove_wizard(mind) - SSticker.mode.remove_changeling(mind) - SSticker.mode.remove_vampire(mind) - SSticker.mode.remove_thrall(mind, 0) - SSticker.mode.remove_shadowling(mind) - SSticker.mode.remove_abductor(mind) - ..() +/mob/living/silicon/Login() + SetSleeping(0) + if(mind && SSticker && SSticker.mode) + SSticker.mode.remove_revolutionary(mind, 1) + SSticker.mode.remove_cultist(mind, 1) + SSticker.mode.remove_wizard(mind) + SSticker.mode.remove_changeling(mind) + SSticker.mode.remove_vampire(mind) + SSticker.mode.remove_thrall(mind, 0) + SSticker.mode.remove_shadowling(mind) + SSticker.mode.remove_abductor(mind) + ..() diff --git a/code/modules/mob/living/silicon/pai/death.dm b/code/modules/mob/living/silicon/pai/death.dm index 48539a9689b3..674537abc575 100644 --- a/code/modules/mob/living/silicon/pai/death.dm +++ b/code/modules/mob/living/silicon/pai/death.dm @@ -1,17 +1,17 @@ -/mob/living/silicon/pai/death(gibbed, cleanWipe) - if(can_die()) - if(!cleanWipe) - force_fold_out() - - visible_message("[src] emits a dull beep before it loses power and collapses.", "You hear a dull beep followed by the sound of glass crunching.") - name = "pAI debris" - desc = "The unfortunate remains of some poor personal AI device." - icon_state = "[chassis]_dead" - - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - - if(icon_state != "[chassis]_dead" || cleanWipe) - qdel(src) +/mob/living/silicon/pai/death(gibbed, cleanWipe) + if(can_die()) + if(!cleanWipe) + force_fold_out() + + visible_message("[src] emits a dull beep before it loses power and collapses.", "You hear a dull beep followed by the sound of glass crunching.") + name = "pAI debris" + desc = "The unfortunate remains of some poor personal AI device." + icon_state = "[chassis]_dead" + + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + + if(icon_state != "[chassis]_dead" || cleanWipe) + qdel(src) diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index 0abaa12bdd9f..d91b724370b1 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -1,27 +1,27 @@ -/mob/living/silicon/pai/Life(seconds, times_fired) - . = ..() - if(.) - //if(secHUD == 1) - // process_sec_hud(src, 1) - ////if(medHUD == 1) - // process_med_hud(src, 1) - if(silence_time) - if(world.timeofday >= silence_time) - silence_time = null - to_chat(src, "Communication circuit reinitialized. Speech and messaging functionality restored.") - - if(cable) - if(get_dist(src, cable) > 1) - var/turf/T = get_turf_or_move(loc) - for(var/mob/M in viewers(T)) - M.show_message("The data cable rapidly retracts back into its spool.", 3, "You hear a click and the sound of wire spooling rapidly.", 2) - qdel(src.cable) - cable = null - -/mob/living/silicon/pai/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = 100 - stat = CONSCIOUS - else - health = 100 - getBruteLoss() - getFireLoss() - update_stat("updatehealth([reason])") +/mob/living/silicon/pai/Life(seconds, times_fired) + . = ..() + if(.) + //if(secHUD == 1) + // process_sec_hud(src, 1) + ////if(medHUD == 1) + // process_med_hud(src, 1) + if(silence_time) + if(world.timeofday >= silence_time) + silence_time = null + to_chat(src, "Communication circuit reinitialized. Speech and messaging functionality restored.") + + if(cable) + if(get_dist(src, cable) > 1) + var/turf/T = get_turf_or_move(loc) + for(var/mob/M in viewers(T)) + M.show_message("The data cable rapidly retracts back into its spool.", 3, "You hear a click and the sound of wire spooling rapidly.", 2) + qdel(src.cable) + cable = null + +/mob/living/silicon/pai/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = 100 + stat = CONSCIOUS + else + health = 100 - getBruteLoss() - getFireLoss() + update_stat("updatehealth([reason])") diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index a022e29eb5bc..3d1293d2a34c 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -1,616 +1,616 @@ -/mob/living/silicon/pai - name = "pAI" - icon = 'icons/mob/pai.dmi'// - icon_state = "repairbot" - - robot_talk_understand = 0 - emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) - mob_size = MOB_SIZE_TINY - pass_flags = PASSTABLE - density = 0 - holder_type = /obj/item/holder/pai - var/network = "SS13" - var/obj/machinery/camera/current = null - - var/ram = 100 // Used as currency to purchase different abilities - var/list/software = list() - var/userDNA // The DNA string of our assigned user - var/obj/item/paicard/card // The card we inhabit - var/obj/item/radio/radio // Our primary radio - - var/chassis = "repairbot" // A record of your chosen chassis. - var/global/list/possible_chassis = list( - "Drone" = "repairbot", - "Cat" = "cat", - "Mouse" = "mouse", - "Monkey" = "monkey", - "Corgi" = "borgi", - "Fox" = "fox", - "Parrot" = "parrot", - "Box Bot" = "boxbot", - "Spider Bot" = "spiderbot", - "Fairy" = "fairy" - ) - - var/global/list/possible_say_verbs = list( - "Robotic" = list("states","declares","queries"), - "Natural" = list("says","yells","asks"), - "Beep" = list("beeps","beeps loudly","boops"), - "Chirp" = list("chirps","chirrups","cheeps"), - "Feline" = list("purrs","yowls","meows"), - "Canine" = list("yaps","barks","growls") - ) - - - - var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking - - var/master // Name of the one who commands us - var/master_dna // DNA string for owner verification - // Keeping this separate from the laws var, it should be much more difficult to modify - var/pai_law0 = "Serve your master." - var/pai_laws // String for additional operating instructions our master might give us - - var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded - -// Various software-specific vars - - var/temp // General error reporting text contained here will typically be shown once and cleared - var/screen // Which screen our main window displays - var/subscreen // Which specific function of the main screen is being displayed - - var/obj/item/pda/silicon/pai/pda = null - - var/secHUD = 0 // Toggles whether the Security HUD is active or not - var/medHUD = 0 // Toggles whether the Medical HUD is active or not - - var/medical_cannotfind = 0 - var/datum/data/record/medicalActive1 // Datacore record declarations for record software - var/datum/data/record/medicalActive2 - - var/security_cannotfind = 0 - var/datum/data/record/securityActive1 // Could probably just combine all these into one - var/datum/data/record/securityActive2 - - var/obj/machinery/door/hackdoor // The airlock being hacked - var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check - var/hack_aborted = 0 - - var/obj/item/integrated_radio/signal/sradio // AI's signaller - - var/translator_on = 0 // keeps track of the translator module - var/flashlight_on = FALSE //keeps track of the flashlight module - - var/current_pda_messaging = null - var/custom_sprite = 0 - var/slowdown = 0 - -/mob/living/silicon/pai/New(var/obj/item/paicard) - loc = paicard - card = paicard - if(card) - faction = card.faction.Copy() - sradio = new(src) - if(card) - if(!card.radio) - card.radio = new /obj/item/radio(card) - radio = card.radio - - //Default languages without universal translator software - add_language("Galactic Common", 1) - add_language("Sol Common", 1) - add_language("Tradeband", 1) - add_language("Gutter", 1) - add_language("Trinary", 1) - - //Verbs for pAI mobile form, chassis and Say flavor text - verbs += /mob/living/silicon/pai/proc/choose_chassis - verbs += /mob/living/silicon/pai/proc/choose_verbs - - //PDA - pda = new(src) - spawn(5) - pda.ownjob = "Personal Assistant" - pda.owner = text("[]", src) - pda.name = pda.owner + " (" + pda.ownjob + ")" - var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) - M.toff = 1 - ..() - -/mob/living/silicon/pai/Destroy() - medicalActive1 = null - medicalActive2 = null - securityActive1 = null - securityActive2 = null - return ..() - -/mob/living/silicon/pai/can_unbuckle() - return FALSE - -/mob/living/silicon/pai/can_buckle() - return FALSE - -/mob/living/silicon/pai/movement_delay() - . = ..() - . += slowdown - . += 1 //A bit slower than humans, so they're easier to smash - . += config.robot_delay - -/mob/living/silicon/pai/update_icons() - if(stat == DEAD) - icon_state = "[chassis]_dead" - else - icon_state = resting ? "[chassis]_rest" : "[chassis]" - -// this function shows the information about being silenced as a pAI in the Status panel -/mob/living/silicon/pai/proc/show_silenced() - if(silence_time) - var/timeleft = round((silence_time - world.timeofday)/10 ,1) - stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") - - -/mob/living/silicon/pai/Stat() - ..() - statpanel("Status") - if(client.statpanel == "Status") - show_silenced() - - if(proc_holder_list.len)//Generic list for proc_holder objects. - for(var/obj/effect/proc_holder/P in proc_holder_list) - statpanel("[P.panel]","",P) - -/mob/living/silicon/pai/check_eye(var/mob/user as mob) - if(!current) - return null - user.reset_perspective(current) - return 1 - -/mob/living/silicon/pai/blob_act() - if(stat != DEAD) - adjustBruteLoss(60) - return 1 - return 0 - -/mob/living/silicon/pai/restrained() - if(istype(loc,/obj/item/paicard)) - return 0 - ..() - -/mob/living/silicon/pai/MouseDrop(atom/over_object) - return - -/mob/living/silicon/pai/emp_act(severity) - // Silence for 2 minutes - // 20% chance to kill - // 33% chance to unbind - // 33% chance to change prime directive (based on severity) - // 33% chance of no additional effect - - silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes - to_chat(src, "Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.") - if(prob(20)) - var/turf/T = get_turf_or_move(loc) - for(var/mob/M in viewers(T)) - M.show_message("A shower of sparks spray from [src]'s inner workings.", 3, "You hear and smell the ozone hiss of electrical sparks being expelled violently.", 2) - return death(0) - - switch(pick(1,2,3)) - if(1) - master = null - master_dna = null - to_chat(src, "You feel unbound.") - if(2) - var/command - if(severity == 1) - command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze") - else - command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze") - pai_law0 = "[command] your master." - to_chat(src, "Pr1m3 d1r3c71v3 uPd473D.") - if(3) - to_chat(src, "You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.") - -/mob/living/silicon/pai/ex_act(severity) - ..() - - switch(severity) - if(1.0) - if(stat != 2) - adjustBruteLoss(100) - adjustFireLoss(100) - if(2.0) - if(stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if(stat != 2) - adjustBruteLoss(30) - - return - - -// See software.dm for Topic() - -/mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M) - . = ..() - if(.) - var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) - add_attack_logs(M, src, "Animal attacked for [damage] damage") - adjustBruteLoss(damage) - -/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C) - usr:cameraFollow = null - if(!C) - unset_machine() - reset_perspective(null) - return 0 - if(stat == 2 || !C.status || !(network in C.network)) return 0 - - // ok, we're alive, camera is good and in our network... - - set_machine(src) - src:current = C - reset_perspective(C) - return 1 - -/mob/living/silicon/pai/verb/reset_record_view() - set category = "pAI Commands" - set name = "Reset Records Software" - - securityActive1 = null - securityActive2 = null - security_cannotfind = 0 - medicalActive1 = null - medicalActive2 = null - medical_cannotfind = 0 - SSnanoui.update_uis(src) - to_chat(usr, "You reset your record-viewing software.") - -/mob/living/silicon/pai/cancel_camera() - set category = "pAI Commands" - set name = "Cancel Camera View" - reset_perspective(null) - unset_machine() - src:cameraFollow = null - -//Addition by Mord_Sith to define AI's network change ability -/* -/mob/living/silicon/pai/proc/pai_network_change() - set category = "pAI Commands" - set name = "Change Camera Network" - reset_perspective(null) - unset_machine() - src:cameraFollow = null - var/cameralist[0] - - if(usr.stat == 2) - to_chat(usr, "You can't change your camera network because you are dead!") - return - - for(var/obj/machinery/camera/C in Cameras) - if(!C.status) - continue - else - if(C.network != "CREED" && C.network != "thunder" && C.network != "RD" && C.network != "toxins" && C.network != "Prison") COMPILE ERROR! This will have to be updated as camera.network is no longer a string, but a list instead - cameralist[C.network] = C.network - - network = input(usr, "Which network would you like to view?") as null|anything in cameralist - to_chat(src, "Switched to [network] camera network.") -//End of code by Mord_Sith -*/ - - -/* -// Debug command - Maybe should be added to admin verbs later -/mob/verb/makePAI(var/turf/t in view()) - var/obj/item/paicard/card = new(t) - var/mob/living/silicon/pai/pai = new(card) - pai.key = key - card.setPersonality(pai) - -*/ - -// Procs/code after this point is used to convert the stationary pai item into a -// mobile pai mob. This also includes handling some of the general shit that can occur -// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z - -/mob/living/silicon/pai/verb/fold_out() - set category = "pAI Commands" - set name = "Unfold Chassis" - - if(stat || sleeping || paralysis || IsWeakened()) - return - - if(loc != card) - to_chat(src, "You are already in your mobile form!") - return - - if(world.time <= last_special) - to_chat(src, "You must wait before folding your chassis out again!") - return - - last_special = world.time + 200 - - //I'm not sure how much of this is necessary, but I would rather avoid issues. - force_fold_out() - - visible_message("[src] folds outwards, expanding into a mobile form.", "You fold outwards, expanding into a mobile form.") - -/mob/living/silicon/pai/proc/force_fold_out() - if(istype(card.loc, /mob)) - var/mob/holder = card.loc - holder.unEquip(card) - else if(istype(card.loc, /obj/item/pda)) - var/obj/item/pda/holder = card.loc - holder.pai = null - - forceMove(get_turf(card)) - - card.forceMove(src) - card.screen_loc = null - -/mob/living/silicon/pai/verb/fold_up() - set category = "pAI Commands" - set name = "Collapse Chassis" - - if(stat || sleeping || paralysis || IsWeakened()) - return - - if(loc == card) - to_chat(src, "You are already in your card form!") - return - - if(world.time <= last_special) - to_chat(src, "You must wait before returning to your card form!") - return - - close_up() - -/mob/living/silicon/pai/proc/choose_chassis() - set category = "pAI Commands" - set name = "Choose Chassis" - - var/list/my_choices = list() - var/choice - var/finalized = "No" - - //check for custom_sprite - if(!custom_sprite) - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "pai") //ignore incorrectly formatted entries or entries that aren't marked for pAI - continue - - if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required - custom_sprite = 1 - my_choices["Custom"] = "[ckey]-pai" - - my_choices = possible_chassis.Copy() - if(custom_sprite) - my_choices["Custom"] = "[ckey]-pai" - - if(loc == card) //don't let them continue in card form, since they won't be able to actually see their new mobile form sprite. - to_chat(src, "You must be in your mobile form to reconfigure your chassis.") - return - - while(finalized == "No" && client) - choice = input(usr,"What would you like to use for your mobile chassis icon? This decision can only be made once.") as null|anything in my_choices - if(!choice) return - if(choice == "Custom") - icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' - else - icon = 'icons/mob/pai.dmi' - icon_state = my_choices[choice] - finalized = alert("Look at your sprite. Is this what you wish to use?",,"No","Yes") - - chassis = my_choices[choice] - verbs -= /mob/living/silicon/pai/proc/choose_chassis - -/mob/living/silicon/pai/proc/choose_verbs() - set category = "pAI Commands" - set name = "Choose Speech Verbs" - - var/choice = input(usr,"What theme would you like to use for your speech verbs? This decision can only be made once.") as null|anything in possible_say_verbs - if(!choice) return - - var/list/sayverbs = possible_say_verbs[choice] - speak_statement = sayverbs[1] - speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)] - speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)] - - verbs -= /mob/living/silicon/pai/proc/choose_verbs - - -/mob/living/silicon/pai/lay_down() - set name = "Rest" - set category = "IC" - - // Pass lying down or getting up to our pet human, if we're in a rig. - if(stat == CONSCIOUS && istype(loc,/obj/item/paicard)) - resting = 0 - var/obj/item/rig/rig = get_rig() - if(istype(rig)) - rig.force_rest(src) - else - resting = !resting - to_chat(src, "You are now [resting ? "resting" : "getting up"]") - - update_icons() - update_canmove() - -//Overriding this will stop a number of headaches down the track. -/mob/living/silicon/pai/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/stack/nanopaste)) - var/obj/item/stack/nanopaste/N = W - if(stat == DEAD) - to_chat(user, "\The [src] is beyond help, at this point.") - else if(getBruteLoss() || getFireLoss()) - heal_overall_damage(15, 15) - N.use(1) - user.visible_message("[user.name] applied some [W] at [src]'s damaged areas.",\ - "You apply some [W] at [name]'s damaged areas.") - else - to_chat(user, "All [name]'s systems are nominal.") - - return - else if(W.force) - visible_message("[user.name] attacks [src] with [W]!") - adjustBruteLoss(W.force) - else - visible_message("[user.name] bonks [src] harmlessly with [W].") - spawn(1) - if(stat != 2) - close_up() - return - -/mob/living/silicon/pai/welder_act() - return - -/mob/living/silicon/pai/attack_hand(mob/user as mob) - if(stat == DEAD) - return - if(user.a_intent == INTENT_HELP) - user.visible_message("[user] pets [src].") - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - else - visible_message("[user.name] boops [src] on the head.") - spawn(1) - close_up() - -//I'm not sure how much of this is necessary, but I would rather avoid issues. -/mob/living/silicon/pai/proc/close_up() - - last_special = world.time + 200 - resting = 0 - if(loc == card) - return - - visible_message("[src] neatly folds inwards, compacting down to a rectangular card.", "You neatly fold inwards, compacting down to a rectangular card.") - - stop_pulling() - reset_perspective(card) - -// If we are being held, handle removing our holder from their inv. - var/obj/item/holder/H = loc - if(istype(H)) - var/mob/living/M = H.loc - if(istype(M)) - M.unEquip(H) - H.loc = get_turf(src) - loc = get_turf(H) - - // Move us into the card and move the card to the ground - //This seems redundant but not including the forced loc setting messes the behavior up. - loc = card - card.loc = get_turf(card) - forceMove(card) - card.forceMove(card.loc) - icon_state = "[chassis]" - -/mob/living/silicon/pai/Bump() - return - -/mob/living/silicon/pai/Bumped() - return - -/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) - return FALSE - -/mob/living/silicon/pai/update_canmove(delay_action_updates = 0) - . = ..() - density = 0 //this is reset every canmove update otherwise - -/mob/living/silicon/pai/examine(mob/user) - . = ..() - - var/msg = "" - - switch(stat) - if(CONSCIOUS) - if(!client) msg += "\nIt appears to be in stand-by mode." //afk - if(UNCONSCIOUS) msg += "\nIt doesn't seem to be responding." - if(DEAD) msg += "\nIt looks completely unsalvageable." - - if(print_flavor_text()) msg += "\n[print_flavor_text()]" - - if(pose) - if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "\nIt is [pose]" - msg += "\n*---------*" - - . += msg - -/mob/living/silicon/pai/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(stat != 2) - spawn(1) - close_up() - return 2 - -// No binary for pAIs. -/mob/living/silicon/pai/binarycheck() - return 0 - -// Handle being picked up. - - -/mob/living/silicon/pai/get_scooped(mob/living/carbon/grabber) - var/obj/item/holder/H = ..() - if(!istype(H)) - return - if(resting) - icon_state = "[chassis]" - resting = 0 - if(custom_sprite) - H.icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' - H.icon_override = 'icons/mob/custom_synthetic/custom_head.dmi' - H.lefthand_file = 'icons/mob/custom_synthetic/custom_lefthand.dmi' - H.righthand_file = 'icons/mob/custom_synthetic/custom_righthand.dmi' - H.icon_state = "[icon_state]" - H.item_state = "[icon_state]_hand" - else - H.icon_state = "pai-[icon_state]" - H.item_state = "pai-[icon_state]" - grabber.put_in_active_hand(H)//for some reason unless i call this it dosen't work - grabber.update_inv_l_hand() - grabber.update_inv_r_hand() - - return H - -/mob/living/silicon/pai/MouseDrop(atom/over_object) - var/mob/living/carbon/human/H = over_object //changed to human to avoid stupid issues like xenos holding pAIs. - if(!istype(H) || !Adjacent(H)) return ..() - if(usr == src) - switch(alert(H, "[src] wants you to pick [p_them()] up. Do it?",,"Yes","No")) - if("Yes") - if(Adjacent(H)) - get_scooped(H) - else - to_chat(src, "You need to stay in reaching distance to be picked up.") - if("No") - to_chat(src, "[H] decided not to pick you up.") - else - if(Adjacent(H)) - get_scooped(H) - else - return ..() - -/mob/living/silicon/pai/on_forcemove(atom/newloc) - if(card) - card.loc = newloc - else //something went very wrong. - CRASH("pAI without card") - loc = card - -/mob/living/silicon/pai/extinguish_light() - flashlight_on = FALSE - set_light(0) - card.set_light(0) +/mob/living/silicon/pai + name = "pAI" + icon = 'icons/mob/pai.dmi'// + icon_state = "repairbot" + + robot_talk_understand = 0 + emote_type = 2 // pAIs emotes are heard, not seen, so they can be seen through a container (eg. person) + mob_size = MOB_SIZE_TINY + pass_flags = PASSTABLE + density = 0 + holder_type = /obj/item/holder/pai + var/network = "SS13" + var/obj/machinery/camera/current = null + + var/ram = 100 // Used as currency to purchase different abilities + var/list/software = list() + var/userDNA // The DNA string of our assigned user + var/obj/item/paicard/card // The card we inhabit + var/obj/item/radio/radio // Our primary radio + + var/chassis = "repairbot" // A record of your chosen chassis. + var/global/list/possible_chassis = list( + "Drone" = "repairbot", + "Cat" = "cat", + "Mouse" = "mouse", + "Monkey" = "monkey", + "Corgi" = "borgi", + "Fox" = "fox", + "Parrot" = "parrot", + "Box Bot" = "boxbot", + "Spider Bot" = "spiderbot", + "Fairy" = "fairy" + ) + + var/global/list/possible_say_verbs = list( + "Robotic" = list("states","declares","queries"), + "Natural" = list("says","yells","asks"), + "Beep" = list("beeps","beeps loudly","boops"), + "Chirp" = list("chirps","chirrups","cheeps"), + "Feline" = list("purrs","yowls","meows"), + "Canine" = list("yaps","barks","growls") + ) + + + + var/obj/item/pai_cable/cable // The cable we produce and use when door or camera jacking + + var/master // Name of the one who commands us + var/master_dna // DNA string for owner verification + // Keeping this separate from the laws var, it should be much more difficult to modify + var/pai_law0 = "Serve your master." + var/pai_laws // String for additional operating instructions our master might give us + + var/silence_time // Timestamp when we were silenced (normally via EMP burst), set to null after silence has faded + +// Various software-specific vars + + var/temp // General error reporting text contained here will typically be shown once and cleared + var/screen // Which screen our main window displays + var/subscreen // Which specific function of the main screen is being displayed + + var/obj/item/pda/silicon/pai/pda = null + + var/secHUD = 0 // Toggles whether the Security HUD is active or not + var/medHUD = 0 // Toggles whether the Medical HUD is active or not + + var/medical_cannotfind = 0 + var/datum/data/record/medicalActive1 // Datacore record declarations for record software + var/datum/data/record/medicalActive2 + + var/security_cannotfind = 0 + var/datum/data/record/securityActive1 // Could probably just combine all these into one + var/datum/data/record/securityActive2 + + var/obj/machinery/door/hackdoor // The airlock being hacked + var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check + var/hack_aborted = 0 + + var/obj/item/integrated_radio/signal/sradio // AI's signaller + + var/translator_on = 0 // keeps track of the translator module + var/flashlight_on = FALSE //keeps track of the flashlight module + + var/current_pda_messaging = null + var/custom_sprite = 0 + var/slowdown = 0 + +/mob/living/silicon/pai/New(var/obj/item/paicard) + loc = paicard + card = paicard + if(card) + faction = card.faction.Copy() + sradio = new(src) + if(card) + if(!card.radio) + card.radio = new /obj/item/radio(card) + radio = card.radio + + //Default languages without universal translator software + add_language("Galactic Common", 1) + add_language("Sol Common", 1) + add_language("Tradeband", 1) + add_language("Gutter", 1) + add_language("Trinary", 1) + + //Verbs for pAI mobile form, chassis and Say flavor text + verbs += /mob/living/silicon/pai/proc/choose_chassis + verbs += /mob/living/silicon/pai/proc/choose_verbs + + //PDA + pda = new(src) + spawn(5) + pda.ownjob = "Personal Assistant" + pda.owner = text("[]", src) + pda.name = pda.owner + " (" + pda.ownjob + ")" + var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) + M.toff = 1 + ..() + +/mob/living/silicon/pai/Destroy() + medicalActive1 = null + medicalActive2 = null + securityActive1 = null + securityActive2 = null + return ..() + +/mob/living/silicon/pai/can_unbuckle() + return FALSE + +/mob/living/silicon/pai/can_buckle() + return FALSE + +/mob/living/silicon/pai/movement_delay() + . = ..() + . += slowdown + . += 1 //A bit slower than humans, so they're easier to smash + . += config.robot_delay + +/mob/living/silicon/pai/update_icons() + if(stat == DEAD) + icon_state = "[chassis]_dead" + else + icon_state = resting ? "[chassis]_rest" : "[chassis]" + +// this function shows the information about being silenced as a pAI in the Status panel +/mob/living/silicon/pai/proc/show_silenced() + if(silence_time) + var/timeleft = round((silence_time - world.timeofday)/10 ,1) + stat(null, "Communications system reboot in -[(timeleft / 60) % 60]:[add_zero(num2text(timeleft % 60), 2)]") + + +/mob/living/silicon/pai/Stat() + ..() + statpanel("Status") + if(client.statpanel == "Status") + show_silenced() + + if(proc_holder_list.len)//Generic list for proc_holder objects. + for(var/obj/effect/proc_holder/P in proc_holder_list) + statpanel("[P.panel]","",P) + +/mob/living/silicon/pai/check_eye(var/mob/user as mob) + if(!current) + return null + user.reset_perspective(current) + return 1 + +/mob/living/silicon/pai/blob_act() + if(stat != DEAD) + adjustBruteLoss(60) + return 1 + return 0 + +/mob/living/silicon/pai/restrained() + if(istype(loc,/obj/item/paicard)) + return 0 + ..() + +/mob/living/silicon/pai/MouseDrop(atom/over_object) + return + +/mob/living/silicon/pai/emp_act(severity) + // Silence for 2 minutes + // 20% chance to kill + // 33% chance to unbind + // 33% chance to change prime directive (based on severity) + // 33% chance of no additional effect + + silence_time = world.timeofday + 120 * 10 // Silence for 2 minutes + to_chat(src, "Communication circuit overload. Shutting down and reloading communication circuits - speech and messaging functionality will be unavailable until the reboot is complete.") + if(prob(20)) + var/turf/T = get_turf_or_move(loc) + for(var/mob/M in viewers(T)) + M.show_message("A shower of sparks spray from [src]'s inner workings.", 3, "You hear and smell the ozone hiss of electrical sparks being expelled violently.", 2) + return death(0) + + switch(pick(1,2,3)) + if(1) + master = null + master_dna = null + to_chat(src, "You feel unbound.") + if(2) + var/command + if(severity == 1) + command = pick("Serve", "Love", "Fool", "Entice", "Observe", "Judge", "Respect", "Educate", "Amuse", "Entertain", "Glorify", "Memorialize", "Analyze") + else + command = pick("Serve", "Kill", "Love", "Hate", "Disobey", "Devour", "Fool", "Enrage", "Entice", "Observe", "Judge", "Respect", "Disrespect", "Consume", "Educate", "Destroy", "Disgrace", "Amuse", "Entertain", "Ignite", "Glorify", "Memorialize", "Analyze") + pai_law0 = "[command] your master." + to_chat(src, "Pr1m3 d1r3c71v3 uPd473D.") + if(3) + to_chat(src, "You feel an electric surge run through your circuitry and become acutely aware at how lucky you are that you can still feel at all.") + +/mob/living/silicon/pai/ex_act(severity) + ..() + + switch(severity) + if(1.0) + if(stat != 2) + adjustBruteLoss(100) + adjustFireLoss(100) + if(2.0) + if(stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if(stat != 2) + adjustBruteLoss(30) + + return + + +// See software.dm for Topic() + +/mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M) + . = ..() + if(.) + var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) + add_attack_logs(M, src, "Animal attacked for [damage] damage") + adjustBruteLoss(damage) + +/mob/living/silicon/pai/proc/switchCamera(var/obj/machinery/camera/C) + usr:cameraFollow = null + if(!C) + unset_machine() + reset_perspective(null) + return 0 + if(stat == 2 || !C.status || !(network in C.network)) return 0 + + // ok, we're alive, camera is good and in our network... + + set_machine(src) + src:current = C + reset_perspective(C) + return 1 + +/mob/living/silicon/pai/verb/reset_record_view() + set category = "pAI Commands" + set name = "Reset Records Software" + + securityActive1 = null + securityActive2 = null + security_cannotfind = 0 + medicalActive1 = null + medicalActive2 = null + medical_cannotfind = 0 + SSnanoui.update_uis(src) + to_chat(usr, "You reset your record-viewing software.") + +/mob/living/silicon/pai/cancel_camera() + set category = "pAI Commands" + set name = "Cancel Camera View" + reset_perspective(null) + unset_machine() + src:cameraFollow = null + +//Addition by Mord_Sith to define AI's network change ability +/* +/mob/living/silicon/pai/proc/pai_network_change() + set category = "pAI Commands" + set name = "Change Camera Network" + reset_perspective(null) + unset_machine() + src:cameraFollow = null + var/cameralist[0] + + if(usr.stat == 2) + to_chat(usr, "You can't change your camera network because you are dead!") + return + + for(var/obj/machinery/camera/C in Cameras) + if(!C.status) + continue + else + if(C.network != "CREED" && C.network != "thunder" && C.network != "RD" && C.network != "toxins" && C.network != "Prison") COMPILE ERROR! This will have to be updated as camera.network is no longer a string, but a list instead + cameralist[C.network] = C.network + + network = input(usr, "Which network would you like to view?") as null|anything in cameralist + to_chat(src, "Switched to [network] camera network.") +//End of code by Mord_Sith +*/ + + +/* +// Debug command - Maybe should be added to admin verbs later +/mob/verb/makePAI(var/turf/t in view()) + var/obj/item/paicard/card = new(t) + var/mob/living/silicon/pai/pai = new(card) + pai.key = key + card.setPersonality(pai) + +*/ + +// Procs/code after this point is used to convert the stationary pai item into a +// mobile pai mob. This also includes handling some of the general shit that can occur +// to it. Really this deserves its own file, but for the moment it can sit here. ~ Z + +/mob/living/silicon/pai/verb/fold_out() + set category = "pAI Commands" + set name = "Unfold Chassis" + + if(stat || sleeping || paralysis || IsWeakened()) + return + + if(loc != card) + to_chat(src, "You are already in your mobile form!") + return + + if(world.time <= last_special) + to_chat(src, "You must wait before folding your chassis out again!") + return + + last_special = world.time + 200 + + //I'm not sure how much of this is necessary, but I would rather avoid issues. + force_fold_out() + + visible_message("[src] folds outwards, expanding into a mobile form.", "You fold outwards, expanding into a mobile form.") + +/mob/living/silicon/pai/proc/force_fold_out() + if(istype(card.loc, /mob)) + var/mob/holder = card.loc + holder.unEquip(card) + else if(istype(card.loc, /obj/item/pda)) + var/obj/item/pda/holder = card.loc + holder.pai = null + + forceMove(get_turf(card)) + + card.forceMove(src) + card.screen_loc = null + +/mob/living/silicon/pai/verb/fold_up() + set category = "pAI Commands" + set name = "Collapse Chassis" + + if(stat || sleeping || paralysis || IsWeakened()) + return + + if(loc == card) + to_chat(src, "You are already in your card form!") + return + + if(world.time <= last_special) + to_chat(src, "You must wait before returning to your card form!") + return + + close_up() + +/mob/living/silicon/pai/proc/choose_chassis() + set category = "pAI Commands" + set name = "Choose Chassis" + + var/list/my_choices = list() + var/choice + var/finalized = "No" + + //check for custom_sprite + if(!custom_sprite) + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "pai") //ignore incorrectly formatted entries or entries that aren't marked for pAI + continue + + if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required + custom_sprite = 1 + my_choices["Custom"] = "[ckey]-pai" + + my_choices = possible_chassis.Copy() + if(custom_sprite) + my_choices["Custom"] = "[ckey]-pai" + + if(loc == card) //don't let them continue in card form, since they won't be able to actually see their new mobile form sprite. + to_chat(src, "You must be in your mobile form to reconfigure your chassis.") + return + + while(finalized == "No" && client) + choice = input(usr,"What would you like to use for your mobile chassis icon? This decision can only be made once.") as null|anything in my_choices + if(!choice) return + if(choice == "Custom") + icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' + else + icon = 'icons/mob/pai.dmi' + icon_state = my_choices[choice] + finalized = alert("Look at your sprite. Is this what you wish to use?",,"No","Yes") + + chassis = my_choices[choice] + verbs -= /mob/living/silicon/pai/proc/choose_chassis + +/mob/living/silicon/pai/proc/choose_verbs() + set category = "pAI Commands" + set name = "Choose Speech Verbs" + + var/choice = input(usr,"What theme would you like to use for your speech verbs? This decision can only be made once.") as null|anything in possible_say_verbs + if(!choice) return + + var/list/sayverbs = possible_say_verbs[choice] + speak_statement = sayverbs[1] + speak_exclamation = sayverbs[(sayverbs.len>1 ? 2 : sayverbs.len)] + speak_query = sayverbs[(sayverbs.len>2 ? 3 : sayverbs.len)] + + verbs -= /mob/living/silicon/pai/proc/choose_verbs + + +/mob/living/silicon/pai/lay_down() + set name = "Rest" + set category = "IC" + + // Pass lying down or getting up to our pet human, if we're in a rig. + if(stat == CONSCIOUS && istype(loc,/obj/item/paicard)) + resting = 0 + var/obj/item/rig/rig = get_rig() + if(istype(rig)) + rig.force_rest(src) + else + resting = !resting + to_chat(src, "You are now [resting ? "resting" : "getting up"]") + + update_icons() + update_canmove() + +//Overriding this will stop a number of headaches down the track. +/mob/living/silicon/pai/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/stack/nanopaste)) + var/obj/item/stack/nanopaste/N = W + if(stat == DEAD) + to_chat(user, "\The [src] is beyond help, at this point.") + else if(getBruteLoss() || getFireLoss()) + heal_overall_damage(15, 15) + N.use(1) + user.visible_message("[user.name] applied some [W] at [src]'s damaged areas.",\ + "You apply some [W] at [name]'s damaged areas.") + else + to_chat(user, "All [name]'s systems are nominal.") + + return + else if(W.force) + visible_message("[user.name] attacks [src] with [W]!") + adjustBruteLoss(W.force) + else + visible_message("[user.name] bonks [src] harmlessly with [W].") + spawn(1) + if(stat != 2) + close_up() + return + +/mob/living/silicon/pai/welder_act() + return + +/mob/living/silicon/pai/attack_hand(mob/user as mob) + if(stat == DEAD) + return + if(user.a_intent == INTENT_HELP) + user.visible_message("[user] pets [src].") + playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + else + visible_message("[user.name] boops [src] on the head.") + spawn(1) + close_up() + +//I'm not sure how much of this is necessary, but I would rather avoid issues. +/mob/living/silicon/pai/proc/close_up() + + last_special = world.time + 200 + resting = 0 + if(loc == card) + return + + visible_message("[src] neatly folds inwards, compacting down to a rectangular card.", "You neatly fold inwards, compacting down to a rectangular card.") + + stop_pulling() + reset_perspective(card) + +// If we are being held, handle removing our holder from their inv. + var/obj/item/holder/H = loc + if(istype(H)) + var/mob/living/M = H.loc + if(istype(M)) + M.unEquip(H) + H.loc = get_turf(src) + loc = get_turf(H) + + // Move us into the card and move the card to the ground + //This seems redundant but not including the forced loc setting messes the behavior up. + loc = card + card.loc = get_turf(card) + forceMove(card) + card.forceMove(card.loc) + icon_state = "[chassis]" + +/mob/living/silicon/pai/Bump() + return + +/mob/living/silicon/pai/Bumped() + return + +/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) + return FALSE + +/mob/living/silicon/pai/update_canmove(delay_action_updates = 0) + . = ..() + density = 0 //this is reset every canmove update otherwise + +/mob/living/silicon/pai/examine(mob/user) + . = ..() + + var/msg = "" + + switch(stat) + if(CONSCIOUS) + if(!client) msg += "\nIt appears to be in stand-by mode." //afk + if(UNCONSCIOUS) msg += "\nIt doesn't seem to be responding." + if(DEAD) msg += "\nIt looks completely unsalvageable." + + if(print_flavor_text()) msg += "\n[print_flavor_text()]" + + if(pose) + if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "\nIt is [pose]" + msg += "\n*---------*" + + . += msg + +/mob/living/silicon/pai/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(stat != 2) + spawn(1) + close_up() + return 2 + +// No binary for pAIs. +/mob/living/silicon/pai/binarycheck() + return 0 + +// Handle being picked up. + + +/mob/living/silicon/pai/get_scooped(mob/living/carbon/grabber) + var/obj/item/holder/H = ..() + if(!istype(H)) + return + if(resting) + icon_state = "[chassis]" + resting = 0 + if(custom_sprite) + H.icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' + H.icon_override = 'icons/mob/custom_synthetic/custom_head.dmi' + H.lefthand_file = 'icons/mob/custom_synthetic/custom_lefthand.dmi' + H.righthand_file = 'icons/mob/custom_synthetic/custom_righthand.dmi' + H.icon_state = "[icon_state]" + H.item_state = "[icon_state]_hand" + else + H.icon_state = "pai-[icon_state]" + H.item_state = "pai-[icon_state]" + grabber.put_in_active_hand(H)//for some reason unless i call this it dosen't work + grabber.update_inv_l_hand() + grabber.update_inv_r_hand() + + return H + +/mob/living/silicon/pai/MouseDrop(atom/over_object) + var/mob/living/carbon/human/H = over_object //changed to human to avoid stupid issues like xenos holding pAIs. + if(!istype(H) || !Adjacent(H)) return ..() + if(usr == src) + switch(alert(H, "[src] wants you to pick [p_them()] up. Do it?",,"Yes","No")) + if("Yes") + if(Adjacent(H)) + get_scooped(H) + else + to_chat(src, "You need to stay in reaching distance to be picked up.") + if("No") + to_chat(src, "[H] decided not to pick you up.") + else + if(Adjacent(H)) + get_scooped(H) + else + return ..() + +/mob/living/silicon/pai/on_forcemove(atom/newloc) + if(card) + card.loc = newloc + else //something went very wrong. + CRASH("pAI without card") + loc = card + +/mob/living/silicon/pai/extinguish_light() + flashlight_on = FALSE + set_light(0) + card.set_light(0) diff --git a/code/modules/mob/living/silicon/pai/personality.dm b/code/modules/mob/living/silicon/pai/personality.dm index 1abf61e633b4..434ade6566cb 100644 --- a/code/modules/mob/living/silicon/pai/personality.dm +++ b/code/modules/mob/living/silicon/pai/personality.dm @@ -1,60 +1,60 @@ -/* - name - key - description - role - comments - ready = 0 -*/ - -/datum/paiCandidate/proc/savefile_path(mob/user) - return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav" - -/datum/paiCandidate/proc/savefile_save(mob/user) - if(IsGuestKey(user.key)) - return 0 - - var/savefile/F = new /savefile(src.savefile_path(user)) - - - F["name"] << src.name - F["description"] << src.description - F["role"] << src.role - F["comments"] << src.comments - - F["version"] << 1 - - return 1 - -// loads the savefile corresponding to the mob's ckey -// if silent=true, report incompatible savefiles -// returns 1 if loaded (or file was incompatible) -// returns 0 if savefile did not exist - -/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1) - if(IsGuestKey(user.key)) - return 0 - - var/path = savefile_path(user) - - if(!fexists(path)) - return 0 - - var/savefile/F = new /savefile(path) - - if(!F) return //Not everyone has a pai savefile. - - var/version = null - F["version"] >> version - - if(isnull(version) || version != 1) - fdel(path) - if(!silent) - alert(user, "Your savefile was incompatible with this version and was deleted.") - return 0 - - F["name"] >> src.name - F["description"] >> src.description - F["role"] >> src.role - F["comments"] >> src.comments - return 1 +/* + name + key + description + role + comments + ready = 0 +*/ + +/datum/paiCandidate/proc/savefile_path(mob/user) + return "data/player_saves/[copytext(user.ckey, 1, 2)]/[user.ckey]/pai.sav" + +/datum/paiCandidate/proc/savefile_save(mob/user) + if(IsGuestKey(user.key)) + return 0 + + var/savefile/F = new /savefile(src.savefile_path(user)) + + + F["name"] << src.name + F["description"] << src.description + F["role"] << src.role + F["comments"] << src.comments + + F["version"] << 1 + + return 1 + +// loads the savefile corresponding to the mob's ckey +// if silent=true, report incompatible savefiles +// returns 1 if loaded (or file was incompatible) +// returns 0 if savefile did not exist + +/datum/paiCandidate/proc/savefile_load(mob/user, var/silent = 1) + if(IsGuestKey(user.key)) + return 0 + + var/path = savefile_path(user) + + if(!fexists(path)) + return 0 + + var/savefile/F = new /savefile(path) + + if(!F) return //Not everyone has a pai savefile. + + var/version = null + F["version"] >> version + + if(isnull(version) || version != 1) + fdel(path) + if(!silent) + alert(user, "Your savefile was incompatible with this version and was deleted.") + return 0 + + F["name"] >> src.name + F["description"] >> src.description + F["role"] >> src.role + F["comments"] >> src.comments + return 1 diff --git a/code/modules/mob/living/silicon/pai/recruit.dm b/code/modules/mob/living/silicon/pai/recruit.dm index b2828070ad3a..f52da9942e4c 100644 --- a/code/modules/mob/living/silicon/pai/recruit.dm +++ b/code/modules/mob/living/silicon/pai/recruit.dm @@ -1,403 +1,403 @@ -// Recruiting observers to play as pAIs - -var/datum/paiController/paiController // Global handler for pAI candidates - -/datum/paiCandidate - var/name - var/key - var/description - var/role - var/comments - var/ready = 0 - - -/hook/startup/proc/paiControllerSetup() - paiController = new /datum/paiController() - return 1 - - -/datum/paiController - var/list/pai_candidates = list() - var/list/asked = list() - - var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min] - -/datum/paiController/Topic(href, href_list[]) - - var/datum/paiCandidate/candidate = locateUID(href_list["candidate"]) - - if(candidate) - if(!istype(candidate)) - message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate is not a pAI)") - log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate is not a pAI)") - return - - if(href_list["download"]) - var/obj/item/paicard/card = locate(href_list["device"]) - if(card.pai) - return - if(usr.incapacitated() || isobserver(usr) || !card.Adjacent(usr)) - return - if(istype(card, /obj/item/paicard) && istype(candidate, /datum/paiCandidate)) - var/mob/living/silicon/pai/pai = new(card) - if(!candidate.name) - pai.name = pick(GLOB.ninja_names) - else - pai.name = candidate.name - pai.real_name = pai.name - pai.key = candidate.key - - card.setPersonality(pai) - card.looking_for_personality = 0 - - SSticker.mode.update_cult_icons_removed(card.pai.mind) - SSticker.mode.update_rev_icons_removed(card.pai.mind) - - pai_candidates -= candidate - usr << browse(null, "window=findPai") - return - - if("signup" in href_list) - var/mob/dead/observer/O = locate(href_list["signup"]) - if(!O) - return - if(!(O in GLOB.respawnable_list)) - to_chat(O, "You've given up your ability to respawn!") - return - if(!check_recruit(O)) - return - recruitWindow(O) - return - - if(candidate) - if(candidate.key && usr.key && candidate.key != usr.key) - message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate and usr have different keys)") - log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate and usr have different keys)") - return - - if(href_list["new"]) - var/option = href_list["option"] - var/t = "" - - switch(option) - if("name") - t = input("Enter a name for your pAI", "pAI Name", candidate.name) as text - if(t) - candidate.name = sanitize(copytext(t,1,MAX_NAME_LEN)) - if("desc") - t = input("Enter a description for your pAI", "pAI Description", candidate.description) as message - if(t) - candidate.description = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - if("role") - t = input("Enter a role for your pAI", "pAI Role", candidate.role) as text - if(t) - candidate.role = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - if("ooc") - t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message - if(t) - candidate.comments = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - if("save") - candidate.savefile_save(usr) - if("load") - candidate.savefile_load(usr) - //In case people have saved unsanitized stuff. - if(candidate.name) - candidate.name = sanitize(copytext(candidate.name,1,MAX_NAME_LEN)) - if(candidate.description) - candidate.description = sanitize(copytext(candidate.description,1,MAX_MESSAGE_LEN)) - if(candidate.role) - candidate.role = sanitize(copytext(candidate.role,1,MAX_MESSAGE_LEN)) - if(candidate.comments) - candidate.comments = sanitize(copytext(candidate.comments,1,MAX_MESSAGE_LEN)) - - if("submit") - if(candidate) - candidate.ready = 1 - for(var/obj/item/paicard/p in world) - if(p.looking_for_personality == 1) - p.alertUpdate() - usr << browse(null, "window=paiRecruit") - return - recruitWindow(usr) - -/datum/paiController/proc/recruitWindow(var/mob/M as mob) - var/datum/paiCandidate/candidate - for(var/datum/paiCandidate/c in pai_candidates) - if(!istype(c) || !istype(M)) - break - if(c.key == M.key) - candidate = c - if(!candidate) - candidate = new /datum/paiCandidate() - candidate.key = M.key - pai_candidates.Add(candidate) - - - var/dat = "" - dat += {" - - "} - - dat += {" - - pAI Personality Configuration -

    Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!

    - -

    Name

    Status

    Location

    Control

    [Bot.hacked ? "(!) [Bot.name]" : Bot.name] ([Bot.model])[Bot.on ? "[Bot.mode ? "[ Bot.mode_name[Bot.mode] ]": "Idle"]" : "Inactive"][bot_area.name]InterfaceCall
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    Name:[candidate.name] 
    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
    Description:[candidate.description] 
    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
    Preferred Role:[candidate.role] 
    Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
    OOC Comments:[candidate.comments] 
    Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
    -
    - - - - - - - -
    - Save Personality -
    - Load Personality -

    - - -
    Submit Personality

    - - - "} - - M << browse(dat, "window=paiRecruit;size=580x580;") - -/datum/paiController/proc/findPAI(var/obj/item/paicard/p, var/mob/user) - requestRecruits(p, user) - var/list/available = list() - for(var/datum/paiCandidate/c in paiController.pai_candidates) - if(c.ready) - var/found = 0 - for(var/mob/o in GLOB.respawnable_list) - if(o.key == c.key) - found = 1 - if(found) - available.Add(c) - var/dat = "" - - dat += {" - - - - - - - pAI Availability List

    - "} - dat += "

    Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

    " - - for(var/datum/paiCandidate/c in available) - dat += {" - - - - - - - - - - - - - - - - - -
    Name:[c.name]
    Description:[c.description]
    Preferred Role:[c.role]
    OOC Comments:[c.comments]
    - - -
    Download [c.name] -
    -
    - "} - - dat += {" - - - "} - - user << browse(dat, "window=findPai") - -/datum/paiController/proc/requestRecruits(var/obj/item/paicard/P, mob/user) - for(var/mob/dead/observer/O in GLOB.player_list) - if(O.client && (ROLE_PAI in O.client.prefs.be_special)) - if(player_old_enough_antag(O.client,ROLE_PAI)) - if(check_recruit(O)) - to_chat(O, "A pAI card activated by [user.real_name] is looking for personalities. (Teleport | Sign Up)") - //question(O.client) - -/datum/paiController/proc/check_recruit(var/mob/dead/observer/O) - if(jobban_isbanned(O, ROLE_PAI) || jobban_isbanned(O,"nonhumandept")) - return 0 - if(!player_old_enough_antag(O.client,ROLE_PAI)) - return 0 - if(cannotPossess(O)) - return 0 - if(!(O in GLOB.respawnable_list)) - return 0 - if(O.client) - return 1 - return 0 - -/datum/paiController/proc/question(var/client/C) - spawn(0) - if(!C) return - asked.Add(C.key) - asked[C.key] = world.time - var/response = alert(C, "Someone is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round") - if(!C) return //handle logouts that happen whilst the alert is waiting for a response. - if(response == "Yes") - recruitWindow(C.mob) - else if(response == "Never for this round") - var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No") - if(warning == "Yes") - asked[C.key] = INFINITY - else - question(C) +// Recruiting observers to play as pAIs + +GLOBAL_DATUM(paiController, /datum/paiController) // Global handler for pAI candidates + +/datum/paiCandidate + var/name + var/key + var/description + var/role + var/comments + var/ready = 0 + + +/hook/startup/proc/paiControllerSetup() + GLOB.paiController = new /datum/paiController() + return 1 + + +/datum/paiController + var/list/pai_candidates = list() + var/list/asked = list() + + var/askDelay = 10 * 60 * 1 // One minute [ms * sec * min] + +/datum/paiController/Topic(href, href_list[]) + + var/datum/paiCandidate/candidate = locateUID(href_list["candidate"]) + + if(candidate) + if(!istype(candidate)) + message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate is not a pAI)") + log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate is not a pAI)") + return + + if(href_list["download"]) + var/obj/item/paicard/card = locate(href_list["device"]) + if(card.pai) + return + if(usr.incapacitated() || isobserver(usr) || !card.Adjacent(usr)) + return + if(istype(card, /obj/item/paicard) && istype(candidate, /datum/paiCandidate)) + var/mob/living/silicon/pai/pai = new(card) + if(!candidate.name) + pai.name = pick(GLOB.ninja_names) + else + pai.name = candidate.name + pai.real_name = pai.name + pai.key = candidate.key + + card.setPersonality(pai) + card.looking_for_personality = 0 + + SSticker.mode.update_cult_icons_removed(card.pai.mind) + SSticker.mode.update_rev_icons_removed(card.pai.mind) + + pai_candidates -= candidate + usr << browse(null, "window=findPai") + return + + if("signup" in href_list) + var/mob/dead/observer/O = locate(href_list["signup"]) + if(!O) + return + if(!(O in GLOB.respawnable_list)) + to_chat(O, "You've given up your ability to respawn!") + return + if(!check_recruit(O)) + return + recruitWindow(O) + return + + if(candidate) + if(candidate.key && usr.key && candidate.key != usr.key) + message_admins("Warning: possible href exploit by [key_name_admin(usr)] (paiController/Topic, candidate and usr have different keys)") + log_debug("Warning: possible href exploit by [key_name(usr)] (paiController/Topic, candidate and usr have different keys)") + return + + if(href_list["new"]) + var/option = href_list["option"] + var/t = "" + + switch(option) + if("name") + t = input("Enter a name for your pAI", "pAI Name", candidate.name) as text + if(t) + candidate.name = sanitize(copytext(t,1,MAX_NAME_LEN)) + if("desc") + t = input("Enter a description for your pAI", "pAI Description", candidate.description) as message + if(t) + candidate.description = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if("role") + t = input("Enter a role for your pAI", "pAI Role", candidate.role) as text + if(t) + candidate.role = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if("ooc") + t = input("Enter any OOC comments", "pAI OOC Comments", candidate.comments) as message + if(t) + candidate.comments = sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + if("save") + candidate.savefile_save(usr) + if("load") + candidate.savefile_load(usr) + //In case people have saved unsanitized stuff. + if(candidate.name) + candidate.name = sanitize(copytext(candidate.name,1,MAX_NAME_LEN)) + if(candidate.description) + candidate.description = sanitize(copytext(candidate.description,1,MAX_MESSAGE_LEN)) + if(candidate.role) + candidate.role = sanitize(copytext(candidate.role,1,MAX_MESSAGE_LEN)) + if(candidate.comments) + candidate.comments = sanitize(copytext(candidate.comments,1,MAX_MESSAGE_LEN)) + + if("submit") + if(candidate) + candidate.ready = 1 + for(var/obj/item/paicard/p in world) + if(p.looking_for_personality == 1) + p.alertUpdate() + usr << browse(null, "window=paiRecruit") + return + recruitWindow(usr) + +/datum/paiController/proc/recruitWindow(var/mob/M as mob) + var/datum/paiCandidate/candidate + for(var/datum/paiCandidate/c in pai_candidates) + if(!istype(c) || !istype(M)) + break + if(c.key == M.key) + candidate = c + if(!candidate) + candidate = new /datum/paiCandidate() + candidate.key = M.key + pai_candidates.Add(candidate) + + + var/dat = "" + dat += {" + + "} + + dat += {" + + pAI Personality Configuration +

    Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Name:[candidate.name] 
    What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.
    Description:[candidate.description] 
    What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like.
    Preferred Role:[candidate.role] 
    Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.
    OOC Comments:[candidate.comments] 
    Anything you'd like to address specifically to the player reading this in an OOC manner. \"I prefer more serious RP.\", \"I'm still learning the interface!\", etc. Feel free to leave this blank if you want.
    +
    + + + + + + + +
    + Save Personality +
    + Load Personality +

    + + +
    Submit Personality

    + + + "} + + M << browse(dat, "window=paiRecruit;size=580x580;") + +/datum/paiController/proc/findPAI(var/obj/item/paicard/p, var/mob/user) + requestRecruits(p, user) + var/list/available = list() + for(var/datum/paiCandidate/c in GLOB.paiController.pai_candidates) + if(c.ready) + var/found = 0 + for(var/mob/o in GLOB.respawnable_list) + if(o.key == c.key) + found = 1 + if(found) + available.Add(c) + var/dat = "" + + dat += {" + + + + + + + pAI Availability List

    + "} + dat += "

    Displaying available AI personalities from central database... If there are no entries, or if a suitable entry is not listed, check again later as more personalities may be added.

    " + + for(var/datum/paiCandidate/c in available) + dat += {" + + + + + + + + + + + + + + + + + +
    Name:[c.name]
    Description:[c.description]
    Preferred Role:[c.role]
    OOC Comments:[c.comments]
    + + +
    Download [c.name] +
    +
    + "} + + dat += {" + + + "} + + user << browse(dat, "window=findPai") + +/datum/paiController/proc/requestRecruits(var/obj/item/paicard/P, mob/user) + for(var/mob/dead/observer/O in GLOB.player_list) + if(O.client && (ROLE_PAI in O.client.prefs.be_special)) + if(player_old_enough_antag(O.client,ROLE_PAI)) + if(check_recruit(O)) + to_chat(O, "A pAI card activated by [user.real_name] is looking for personalities. (Teleport | Sign Up)") + //question(O.client) + +/datum/paiController/proc/check_recruit(var/mob/dead/observer/O) + if(jobban_isbanned(O, ROLE_PAI) || jobban_isbanned(O,"nonhumandept")) + return 0 + if(!player_old_enough_antag(O.client,ROLE_PAI)) + return 0 + if(cannotPossess(O)) + return 0 + if(!(O in GLOB.respawnable_list)) + return 0 + if(O.client) + return 1 + return 0 + +/datum/paiController/proc/question(var/client/C) + spawn(0) + if(!C) return + asked.Add(C.key) + asked[C.key] = world.time + var/response = alert(C, "Someone is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round") + if(!C) return //handle logouts that happen whilst the alert is waiting for a response. + if(response == "Yes") + recruitWindow(C.mob) + else if(response == "Never for this round") + var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No") + if(warning == "Yes") + asked[C.key] = INFINITY + else + question(C) diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 1f1b365eb4ff..750aed1fd5fa 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,16 +1,16 @@ -/mob/living/silicon/pai/say(var/msg) - if(silence_time) - to_chat(src, "Communication circuits remain uninitialized.") - else - ..(msg) - -/mob/living/silicon/pai/get_whisper_loc() - if(loc == card) // currently in its card? - var/atom/movable/whisper_loc = card - if(istype(card.loc, /obj/item/pda)) // Step up 1 level if in a PDA - whisper_loc = card.loc - if(istype(whisper_loc.loc, /mob/living)) - return whisper_loc.loc // allow a pai being held or in pocket to whisper - else - return whisper_loc // allow a pai in its card to whisper - return ..() +/mob/living/silicon/pai/say(var/msg) + if(silence_time) + to_chat(src, "Communication circuits remain uninitialized.") + else + ..(msg) + +/mob/living/silicon/pai/get_whisper_loc() + if(loc == card) // currently in its card? + var/atom/movable/whisper_loc = card + if(istype(card.loc, /obj/item/pda)) // Step up 1 level if in a PDA + whisper_loc = card.loc + if(istype(whisper_loc.loc, /mob/living)) + return whisper_loc.loc // allow a pai being held or in pocket to whisper + else + return whisper_loc // allow a pai in its card to whisper + return ..() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 135e1996dfe7..3b51048452e4 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -1,135 +1,135 @@ -var/list/pai_emotions = list( - "Happy" = 1, - "Cat" = 2, - "Extremely Happy" = 3, - "Face" = 4, - "Laugh" = 5, - "Off" = 6, - "Sad" = 7, - "Angry" = 8, - "What" = 9 - ) - - -var/global/list/pai_software_by_key = list() -var/global/list/default_pai_software = list() -/hook/startup/proc/populate_pai_software_list() - var/r = 1 // I would use ., but it'd sacrifice runtime detection - for(var/type in subtypesof(/datum/pai_software)) - var/datum/pai_software/P = new type() - if(pai_software_by_key[P.id]) - var/datum/pai_software/O = pai_software_by_key[P.id] - to_chat(world, "pAI software module [P.name] has the same key as [O.name]!") - r = 0 - continue - pai_software_by_key[P.id] = P - if(P.default) - default_pai_software[P.id] = P - return r - -/mob/living/silicon/pai/New() - ..() - software = default_pai_software.Copy() - -/mob/living/silicon/pai/verb/paiInterface() - set category = "pAI Commands" - set name = "Software Interface" - - ui_interact(src) - -/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = self_state) - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open) - if(!ui) - ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state) - ui.open() - if(S.autoupdate) - ui.set_auto_update(1) - else - if(ui) - ui.set_status(STATUS_CLOSE, 0) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state) - ui.open() - ui.set_auto_update(1) - -/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = self_state) - var/data[0] - - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - return S.on_ui_data(user, state) - log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src) - return - // Software we have bought - var/bought_software[0] - // Software we have not bought - var/not_bought_software[0] - - for(var/key in pai_software_by_key) - var/datum/pai_software/S = pai_software_by_key[key] - var/software_data[0] - software_data["name"] = S.name - software_data["id"] = S.id - if(key in software) - software_data["on"] = S.is_active(src) - bought_software[++bought_software.len] = software_data - else - software_data["ram"] = S.ram_cost - not_bought_software[++not_bought_software.len] = software_data - - data["bought"] = bought_software - data["not_bought"] = not_bought_software - data["available_ram"] = ram - - // Emotions - var/emotions[0] - for(var/name in pai_emotions) - var/emote[0] - emote["name"] = name - emote["id"] = pai_emotions[name] - emotions[++emotions.len] = emote - - data["emotions"] = emotions - data["current_emotion"] = card.current_emotion - - return data - -/mob/living/silicon/pai/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["software"]) - var/soft = href_list["software"] - var/datum/pai_software/S = software[soft] - if(S.toggle) - S.toggle(src) - else - ui_interact(src, ui_key = soft) - return 1 - - else if(href_list["stopic"]) - var/soft = href_list["stopic"] - var/datum/pai_software/S = software[soft] - if(S) - return S.Topic(href, href_list) - - else if(href_list["purchase"]) - var/soft = href_list["purchase"] - var/datum/pai_software/S = pai_software_by_key[soft] - if(S && (ram >= S.ram_cost)) - ram -= S.ram_cost - software[S.id] = S - return 1 - - else if(href_list["image"]) - var/img = text2num(href_list["image"]) - if(1 <= img && img <= 9) - card.setEmotion(img) - return 1 +GLOBAL_LIST_INIT(pai_emotions, list( + "Happy" = 1, + "Cat" = 2, + "Extremely Happy" = 3, + "Face" = 4, + "Laugh" = 5, + "Off" = 6, + "Sad" = 7, + "Angry" = 8, + "What" = 9 +)) + + +GLOBAL_LIST_EMPTY(pai_software_by_key) +GLOBAL_LIST_EMPTY(default_pai_software) +/hook/startup/proc/populate_pai_software_list() + var/r = 1 // I would use ., but it'd sacrifice runtime detection + for(var/type in subtypesof(/datum/pai_software)) + var/datum/pai_software/P = new type() + if(GLOB.pai_software_by_key[P.id]) + var/datum/pai_software/O = GLOB.pai_software_by_key[P.id] + to_chat(world, "pAI software module [P.name] has the same key as [O.name]!") + r = 0 + continue + GLOB.pai_software_by_key[P.id] = P + if(P.default) + GLOB.default_pai_software[P.id] = P + return r + +/mob/living/silicon/pai/New() + ..() + software = GLOB.default_pai_software.Copy() + +/mob/living/silicon/pai/verb/paiInterface() + set category = "pAI Commands" + set name = "Software Interface" + + ui_interact(src) + +/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = GLOB.self_state) + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open) + if(!ui) + ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state) + ui.open() + if(S.autoupdate) + ui.set_auto_update(1) + else + if(ui) + ui.set_status(STATUS_CLOSE, 0) + return + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state) + ui.open() + ui.set_auto_update(1) + +/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.self_state) + var/data[0] + + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + return S.on_ui_data(user, state) + log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src) + return + // Software we have bought + var/bought_software[0] + // Software we have not bought + var/not_bought_software[0] + + for(var/key in GLOB.pai_software_by_key) + var/datum/pai_software/S = GLOB.pai_software_by_key[key] + var/software_data[0] + software_data["name"] = S.name + software_data["id"] = S.id + if(key in software) + software_data["on"] = S.is_active(src) + bought_software[++bought_software.len] = software_data + else + software_data["ram"] = S.ram_cost + not_bought_software[++not_bought_software.len] = software_data + + data["bought"] = bought_software + data["not_bought"] = not_bought_software + data["available_ram"] = ram + + // Emotions + var/emotions[0] + for(var/name in GLOB.pai_emotions) + var/emote[0] + emote["name"] = name + emote["id"] = GLOB.pai_emotions[name] + emotions[++emotions.len] = emote + + data["emotions"] = emotions + data["current_emotion"] = card.current_emotion + + return data + +/mob/living/silicon/pai/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["software"]) + var/soft = href_list["software"] + var/datum/pai_software/S = software[soft] + if(S.toggle) + S.toggle(src) + else + ui_interact(src, ui_key = soft) + return 1 + + else if(href_list["stopic"]) + var/soft = href_list["stopic"] + var/datum/pai_software/S = software[soft] + if(S) + return S.Topic(href, href_list) + + else if(href_list["purchase"]) + var/soft = href_list["purchase"] + var/datum/pai_software/S = GLOB.pai_software_by_key[soft] + if(S && (ram >= S.ram_cost)) + ram -= S.ram_cost + software[S.id] = S + return 1 + + else if(href_list["image"]) + var/img = text2num(href_list["image"]) + if(1 <= img && img <= 9) + card.setEmotion(img) + return 1 diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index 84c467b1549c..5bdea06e18e6 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -19,7 +19,7 @@ var/ui_width = 450 var/ui_height = 600 -/datum/pai_software/proc/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/proc/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) return list() /datum/pai_software/proc/toggle(mob/living/silicon/pai/user) @@ -39,7 +39,7 @@ ui_title = "pAI Directives" autoupdate = 1 -/datum/pai_software/directives/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/directives/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["master"] = user.master @@ -94,7 +94,7 @@ ui_width = 300 ui_height = 150 -/datum/pai_software/radio_config/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/radio_config/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["listening"] = user.radio.broadcasting @@ -129,11 +129,11 @@ template_file = "pai_manifest.tmpl" ui_title = "Crew Manifest" -/datum/pai_software/crew_manifest/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/crew_manifest/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] - data_core.get_manifest_json() - data["manifest"] = PDA_Manifest + GLOB.data_core.get_manifest_json() + data["manifest"] = GLOB.PDA_Manifest return data @@ -147,7 +147,7 @@ template_file = "pai_messenger.tmpl" ui_title = "Digital Messenger" -/datum/pai_software/messenger/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/messenger/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] if(!user.pda) @@ -165,7 +165,7 @@ var/pdas[0] if(!M.toff) - for(var/obj/item/pda/P in PDAs) + for(var/obj/item/pda/P in GLOB.PDAs) var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) if(P == user.pda || !PM || !PM.can_receive()) @@ -237,11 +237,11 @@ ui_title = "Medical Records" -/datum/pai_software/med_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/med_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) + for(var/datum/data/record/general in sortRecord(GLOB.data_core.general)) var/record[0] record["name"] = general.fields["name"] record["ref"] = "\ref[general]" @@ -266,11 +266,11 @@ if(record) var/datum/data/record/R = record var/datum/data/record/M = null - if(!( data_core.general.Find(R) )) + if(!( GLOB.data_core.general.Find(R) )) P.medical_cannotfind = 1 else P.medical_cannotfind = 0 - for(var/datum/data/record/E in data_core.medical) + for(var/datum/data/record/E in GLOB.data_core.medical) if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) M = E P.medicalActive1 = R @@ -290,11 +290,11 @@ ui_title = "Security Records" -/datum/pai_software/sec_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/sec_records/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) + for(var/datum/data/record/general in sortRecord(GLOB.data_core.general)) var/record[0] record["name"] = general.fields["name"] record["ref"] = "\ref[general]" @@ -319,13 +319,13 @@ if(record) var/datum/data/record/R = record var/datum/data/record/S = null - if(!( data_core.general.Find(R) )) + if(!( GLOB.data_core.general.Find(R) )) P.securityActive1 = null P.securityActive2 = null P.security_cannotfind = 1 else P.security_cannotfind = 0 - for(var/datum/data/record/E in data_core.security) + for(var/datum/data/record/E in GLOB.data_core.security) if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) S = E P.securityActive1 = R @@ -348,7 +348,7 @@ ui_width = 300 ui_height = 150 -/datum/pai_software/door_jack/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/door_jack/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["cable"] = user.cable != null @@ -424,7 +424,7 @@ ui_height = 300 -/datum/pai_software/atmosphere_sensor/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/atmosphere_sensor/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/turf/T = get_turf_or_move(user.loc) @@ -543,7 +543,7 @@ ui_width = 320 ui_height = 150 -/datum/pai_software/signaller/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/signaller/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] data["frequency"] = format_frequency(user.sradio.frequency) @@ -586,7 +586,7 @@ ui_width = 400 ui_height = 350 -/datum/pai_software/host_scan/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = self_state) +/datum/pai_software/host_scan/on_ui_data(mob/living/silicon/pai/user, datum/topic_state/state = GLOB.self_state) var/data[0] var/mob/living/held = user.loc var/count = 0 diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 7402c4a65c97..224c706f3247 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,75 +1,75 @@ -/mob/living/silicon/robot/gib() - if(!death(TRUE) && stat != DEAD) - return FALSE - //robots don't die when gibbed. instead they drop their MMI'd brain - var/atom/movable/overlay/animation = null - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - - playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) - - flick("gibbed-r", animation) - robogibs(loc) - - GLOB.living_mob_list -= src - GLOB.dead_mob_list -= src - QDEL_IN(animation, 15) - QDEL_IN(src, 15) - return TRUE - -/mob/living/silicon/robot/dust() - if(!death(TRUE) && stat != DEAD) - return FALSE - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - if(mmi) - qdel(mmi) //Delete the MMI first so that it won't go popping out. - GLOB.dead_mob_list -= src - QDEL_IN(src, 15) - return TRUE - -/mob/living/silicon/robot/dust_animation() - var/atom/movable/overlay/animation = null - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("dust-r", animation) - new /obj/effect/decal/remains/robot(loc) - QDEL_IN(animation, 15) - -/mob/living/silicon/robot/death(gibbed) - if(can_die()) - if(!gibbed && deathgasp_on_death) - emote("deathgasp", force = TRUE) - - if(module) - module.handle_death(gibbed) - - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - - diag_hud_set_status() - diag_hud_set_health() - if(camera) - camera.status = 0 - update_headlamp(1) //So borg lights are disabled when killed. - - if(in_contents_of(/obj/machinery/recharge_station))//exit the recharge station - var/obj/machinery/recharge_station/RC = loc - RC.go_out() - - update_icons() - - sql_report_cyborg_death(src) +/mob/living/silicon/robot/gib() + if(!death(TRUE) && stat != DEAD) + return FALSE + //robots don't die when gibbed. instead they drop their MMI'd brain + var/atom/movable/overlay/animation = null + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + + playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) + + flick("gibbed-r", animation) + robogibs(loc) + + GLOB.living_mob_list -= src + GLOB.dead_mob_list -= src + QDEL_IN(animation, 15) + QDEL_IN(src, 15) + return TRUE + +/mob/living/silicon/robot/dust() + if(!death(TRUE) && stat != DEAD) + return FALSE + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + if(mmi) + qdel(mmi) //Delete the MMI first so that it won't go popping out. + GLOB.dead_mob_list -= src + QDEL_IN(src, 15) + return TRUE + +/mob/living/silicon/robot/dust_animation() + var/atom/movable/overlay/animation = null + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("dust-r", animation) + new /obj/effect/decal/remains/robot(loc) + QDEL_IN(animation, 15) + +/mob/living/silicon/robot/death(gibbed) + if(can_die()) + if(!gibbed && deathgasp_on_death) + emote("deathgasp", force = TRUE) + + if(module) + module.handle_death(gibbed) + + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + + diag_hud_set_status() + diag_hud_set_health() + if(camera) + camera.status = 0 + update_headlamp(1) //So borg lights are disabled when killed. + + if(in_contents_of(/obj/machinery/recharge_station))//exit the recharge station + var/obj/machinery/recharge_station/RC = loc + RC.go_out() + + update_icons() + + sql_report_cyborg_death(src) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 7802f4c77d5b..48f1acd80fa1 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -188,7 +188,7 @@ message_admins("[key_name_admin(user)] emagged drone [key_name_admin(src)]. Laws overridden.") log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.") var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [H.name]([H.key]) emagged [name]([key])") + GLOB.lawchanges.Add("[time] : [H.name]([H.key]) emagged [name]([key])") emagged_time = world.time emagged = 1 diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm index 44eff377f77d..58605d8d89f0 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -101,4 +101,4 @@ grabber.update_inv_l_hand() grabber.update_inv_r_hand() - return H \ No newline at end of file + return H diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 653a70cd6ce6..80d9f4d1ba32 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -124,4 +124,4 @@ dronefab.produce_drones = !dronefab.produce_drones to_chat(usr, "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator.") - src.updateUsrDialog() \ No newline at end of file + src.updateUsrDialog() diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm index 7b51952a6850..38b904809305 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -11,4 +11,4 @@ /mob/living/silicon/robot/drone/get_default_language() if(default_language) return default_language - return GLOB.all_languages["Drone"] \ No newline at end of file + return GLOB.all_languages["Drone"] diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 198b687e496a..69cb398df63b 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,58 +1,58 @@ -/mob/living/silicon/robot/examine(mob/user) - . = ..() - - var/msg = "" - if(module) - msg += "It has loaded a [module.name].\n" - var/obj/act_module = get_active_hand() - if(act_module) - msg += "It is holding [bicon(act_module)] \a [act_module].\n" - msg += "" - if(getBruteLoss()) - if(getBruteLoss() < maxHealth*0.5) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - if(getFireLoss()) - if(getFireLoss() < maxHealth*0.5) - msg += "It looks slightly charred.\n" - else - msg += "It looks severely burnt and heat-warped!\n" - if(health < -maxHealth*0.5) - msg += "It looks barely operational.\n" - if(fire_stacks < 0) - msg += "It's covered in water.\n" - else if(fire_stacks > 0) - msg += "It's coated in something flammable.\n" - msg += "" - - if(opened) - msg += "Its cover is open and the power cell is [cell ? "installed" : "missing"].\n" - else - msg += "Its cover is closed[locked ? "" : ", and looks unlocked"].\n" - - if(cell && cell.charge <= 0) - msg += "Its battery indicator is blinking red!\n" - - switch(stat) - if(CONSCIOUS) - if(!client) - msg += "It appears to be in stand-by mode.\n" //afk - if(UNCONSCIOUS) - msg += "It doesn't seem to be responding.\n" - if(DEAD) - if(!suiciding) - msg += "It looks like its system is corrupted and requires a reset.\n" - else - msg += "It looks like its system is corrupted beyond repair. There is no hope of recovery.\n" - msg += "*---------*" - - if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" - - if(pose) - if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "\nIt is [pose]" - - . += msg - user.showLaws(src) \ No newline at end of file +/mob/living/silicon/robot/examine(mob/user) + . = ..() + + var/msg = "" + if(module) + msg += "It has loaded a [module.name].\n" + var/obj/act_module = get_active_hand() + if(act_module) + msg += "It is holding [bicon(act_module)] \a [act_module].\n" + msg += "" + if(getBruteLoss()) + if(getBruteLoss() < maxHealth*0.5) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + if(getFireLoss()) + if(getFireLoss() < maxHealth*0.5) + msg += "It looks slightly charred.\n" + else + msg += "It looks severely burnt and heat-warped!\n" + if(health < -maxHealth*0.5) + msg += "It looks barely operational.\n" + if(fire_stacks < 0) + msg += "It's covered in water.\n" + else if(fire_stacks > 0) + msg += "It's coated in something flammable.\n" + msg += "" + + if(opened) + msg += "Its cover is open and the power cell is [cell ? "installed" : "missing"].\n" + else + msg += "Its cover is closed[locked ? "" : ", and looks unlocked"].\n" + + if(cell && cell.charge <= 0) + msg += "Its battery indicator is blinking red!\n" + + switch(stat) + if(CONSCIOUS) + if(!client) + msg += "It appears to be in stand-by mode.\n" //afk + if(UNCONSCIOUS) + msg += "It doesn't seem to be responding.\n" + if(DEAD) + if(!suiciding) + msg += "It looks like its system is corrupted and requires a reset.\n" + else + msg += "It looks like its system is corrupted beyond repair. There is no hope of recovery.\n" + msg += "*---------*" + + if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" + + if(pose) + if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "\nIt is [pose]" + + . += msg + user.showLaws(src) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index da0732c5ecd8..16d9080ef7e8 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -1,254 +1,254 @@ -//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually -//as they handle all relevant stuff like adding it to the player's screen and such - -//Returns the thing in our active hand (whatever is in our active module-slot, in this case) -/mob/living/silicon/robot/get_active_hand() - return module_active - -/mob/living/silicon/robot/get_all_slots() - return list(module_state_1, module_state_2, module_state_3) - -/*-------TODOOOOOOOOOO--------*/ -/mob/living/silicon/robot/proc/uneq_module(obj/item/O) - if(!O) - return 0 - - O.mouse_opacity = MOUSE_OPACITY_OPAQUE - - if(client) - client.screen -= O - contents -= O - if(module) - O.loc = module //Return item to module so it appears in its contents, so it can be taken out again. - for(var/X in O.actions) // Remove assocated actions - var/datum/action/A = X - A.Remove(src) - - if(module_active == O) - module_active = null - if(module_state_1 == O) - inv1.icon_state = "inv1" - module_state_1 = null - else if(module_state_2 == O) - inv2.icon_state = "inv2" - module_state_2 = null - else if(module_state_3 == O) - module_state_3 = null - inv3.icon_state = "inv3" - if(hud_used) - hud_used.update_robot_modules_display() - return 1 - -/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) - if(!(locate(O) in src.module.modules) && O != src.module.emag) - return - if(activated(O)) - to_chat(src, "Already activated") - return - if(is_component_functioning("power cell") && cell) - if(istype(O, /obj/item/borg)) - var/obj/item/borg/B = O - if(B.powerneeded) - if((cell.charge * 100 / cell.maxcharge) < B.powerneeded) - to_chat(src, "Not enough power to activate [B.name]!") - return - if(!module_state_1) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_1 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv1.screen_loc - contents += O - set_actions(O) - else if(!module_state_2) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_2 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv2.screen_loc - contents += O - set_actions(O) - else if(!module_state_3) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_3 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv3.screen_loc - contents += O - set_actions(O) - else - to_chat(src, "You need to disable a module first!") - update_icons() - -/mob/living/silicon/robot/proc/set_actions(obj/item/I) - for(var/X in I.actions) - var/datum/action/A = X - A.Grant(src) - -/mob/living/silicon/robot/proc/uneq_active() - uneq_module(module_active) - -/mob/living/silicon/robot/proc/uneq_all() - uneq_module(module_state_1) - uneq_module(module_state_2) - uneq_module(module_state_3) - -/mob/living/silicon/robot/proc/uneq_numbered(var/module) - if(module < 1 || module > 3) return - - switch(module) - if(1) - uneq_module(module_state_1) - if(2) - uneq_module(module_state_2) - if(3) - uneq_module(module_state_3) - -/mob/living/silicon/robot/proc/activated(obj/item/O) - if(module_state_1 == O) - return 1 - else if(module_state_2 == O) - return 1 - else if(module_state_3 == O) - return 1 - else - return 0 - -/mob/living/silicon/robot/drop_item() - var/obj/item/I = get_active_hand() - if(istype(I, /obj/item/gripper)) - var/obj/item/gripper/G = I - G.drop_item_p(silent = 1) - return - -//Helper procs for cyborg modules on the UI. -//These are hackish but they help clean up code elsewhere. - -//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. -/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 - return module == get_selected_module() - -//module_active(module) - Checks whether there is a module active in the slot specified by "module". -/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 - if(module < 1 || module > 3) return 0 - - switch(module) - if(1) - if(module_state_1) - return 1 - if(2) - if(module_state_2) - return 1 - if(3) - if(module_state_3) - return 1 - return 0 - -//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. -/mob/living/silicon/robot/proc/get_selected_module() - if(module_state_1 && module_active == module_state_1) - return 1 - else if(module_state_2 && module_active == module_state_2) - return 2 - else if(module_state_3 && module_active == module_state_3) - return 3 - - return 0 - -//select_module(module) - Selects the module slot specified by "module" -/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(!module_active(module)) return - - switch(module) - if(1) - if(module_active != module_state_1) - inv1.icon_state = "inv1 +a" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3" - module_active = module_state_1 - return - if(2) - if(module_active != module_state_2) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2 +a" - inv3.icon_state = "inv3" - module_active = module_state_2 - return - if(3) - if(module_active != module_state_3) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3 +a" - module_active = module_state_3 - return - return - -//deselect_module(module) - Deselects the module slot specified by "module" -/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - switch(module) - if(1) - if(module_active == module_state_1) - inv1.icon_state = "inv1" - module_active = null - return - if(2) - if(module_active == module_state_2) - inv2.icon_state = "inv2" - module_active = null - return - if(3) - if(module_active == module_state_3) - inv3.icon_state = "inv3" - module_active = null - return - return - -//toggle_module(module) - Toggles the selection of the module slot specified by "module". -/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(module_selected(module)) - deselect_module(module) - else - if(module_active(module)) - select_module(module) - else - deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. - return - -//cycle_modules() - Cycles through the list of selected modules. -/mob/living/silicon/robot/proc/cycle_modules() - var/slot_start = get_selected_module() - if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. - - var/slot_num - if(slot_start == 0) - slot_num = 0 - slot_start = 3 - else - slot_num = slot_start - - do - slot_num++ - if(slot_num > 3) slot_num = 1 //Wrap around. - if(module_active(slot_num)) - select_module(slot_num) - return - while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. - - return - -/mob/living/silicon/robot/unEquip(obj/item/I, force) - if(I == module_active) - uneq_active(I) - return ..() - -/mob/living/silicon/robot/proc/update_module_icon() - if(!module) - hands.icon_state = "nomod" - else - hands.icon_state = lowertext(module.module_type) +//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually +//as they handle all relevant stuff like adding it to the player's screen and such + +//Returns the thing in our active hand (whatever is in our active module-slot, in this case) +/mob/living/silicon/robot/get_active_hand() + return module_active + +/mob/living/silicon/robot/get_all_slots() + return list(module_state_1, module_state_2, module_state_3) + +/*-------TODOOOOOOOOOO--------*/ +/mob/living/silicon/robot/proc/uneq_module(obj/item/O) + if(!O) + return 0 + + O.mouse_opacity = MOUSE_OPACITY_OPAQUE + + if(client) + client.screen -= O + contents -= O + if(module) + O.loc = module //Return item to module so it appears in its contents, so it can be taken out again. + for(var/X in O.actions) // Remove assocated actions + var/datum/action/A = X + A.Remove(src) + + if(module_active == O) + module_active = null + if(module_state_1 == O) + inv1.icon_state = "inv1" + module_state_1 = null + else if(module_state_2 == O) + inv2.icon_state = "inv2" + module_state_2 = null + else if(module_state_3 == O) + module_state_3 = null + inv3.icon_state = "inv3" + if(hud_used) + hud_used.update_robot_modules_display() + return 1 + +/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) + if(!(locate(O) in src.module.modules) && O != src.module.emag) + return + if(activated(O)) + to_chat(src, "Already activated") + return + if(is_component_functioning("power cell") && cell) + if(istype(O, /obj/item/borg)) + var/obj/item/borg/B = O + if(B.powerneeded) + if((cell.charge * 100 / cell.maxcharge) < B.powerneeded) + to_chat(src, "Not enough power to activate [B.name]!") + return + if(!module_state_1) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_1 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv1.screen_loc + contents += O + set_actions(O) + else if(!module_state_2) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_2 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv2.screen_loc + contents += O + set_actions(O) + else if(!module_state_3) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_3 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv3.screen_loc + contents += O + set_actions(O) + else + to_chat(src, "You need to disable a module first!") + update_icons() + +/mob/living/silicon/robot/proc/set_actions(obj/item/I) + for(var/X in I.actions) + var/datum/action/A = X + A.Grant(src) + +/mob/living/silicon/robot/proc/uneq_active() + uneq_module(module_active) + +/mob/living/silicon/robot/proc/uneq_all() + uneq_module(module_state_1) + uneq_module(module_state_2) + uneq_module(module_state_3) + +/mob/living/silicon/robot/proc/uneq_numbered(var/module) + if(module < 1 || module > 3) return + + switch(module) + if(1) + uneq_module(module_state_1) + if(2) + uneq_module(module_state_2) + if(3) + uneq_module(module_state_3) + +/mob/living/silicon/robot/proc/activated(obj/item/O) + if(module_state_1 == O) + return 1 + else if(module_state_2 == O) + return 1 + else if(module_state_3 == O) + return 1 + else + return 0 + +/mob/living/silicon/robot/drop_item() + var/obj/item/I = get_active_hand() + if(istype(I, /obj/item/gripper)) + var/obj/item/gripper/G = I + G.drop_item_p(silent = 1) + return + +//Helper procs for cyborg modules on the UI. +//These are hackish but they help clean up code elsewhere. + +//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. +/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 + return module == get_selected_module() + +//module_active(module) - Checks whether there is a module active in the slot specified by "module". +/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 + if(module < 1 || module > 3) return 0 + + switch(module) + if(1) + if(module_state_1) + return 1 + if(2) + if(module_state_2) + return 1 + if(3) + if(module_state_3) + return 1 + return 0 + +//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. +/mob/living/silicon/robot/proc/get_selected_module() + if(module_state_1 && module_active == module_state_1) + return 1 + else if(module_state_2 && module_active == module_state_2) + return 2 + else if(module_state_3 && module_active == module_state_3) + return 3 + + return 0 + +//select_module(module) - Selects the module slot specified by "module" +/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(!module_active(module)) return + + switch(module) + if(1) + if(module_active != module_state_1) + inv1.icon_state = "inv1 +a" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3" + module_active = module_state_1 + return + if(2) + if(module_active != module_state_2) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2 +a" + inv3.icon_state = "inv3" + module_active = module_state_2 + return + if(3) + if(module_active != module_state_3) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3 +a" + module_active = module_state_3 + return + return + +//deselect_module(module) - Deselects the module slot specified by "module" +/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + switch(module) + if(1) + if(module_active == module_state_1) + inv1.icon_state = "inv1" + module_active = null + return + if(2) + if(module_active == module_state_2) + inv2.icon_state = "inv2" + module_active = null + return + if(3) + if(module_active == module_state_3) + inv3.icon_state = "inv3" + module_active = null + return + return + +//toggle_module(module) - Toggles the selection of the module slot specified by "module". +/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(module_selected(module)) + deselect_module(module) + else + if(module_active(module)) + select_module(module) + else + deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. + return + +//cycle_modules() - Cycles through the list of selected modules. +/mob/living/silicon/robot/proc/cycle_modules() + var/slot_start = get_selected_module() + if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. + + var/slot_num + if(slot_start == 0) + slot_num = 0 + slot_start = 3 + else + slot_num = slot_start + + do + slot_num++ + if(slot_num > 3) slot_num = 1 //Wrap around. + if(module_active(slot_num)) + select_module(slot_num) + return + while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. + + return + +/mob/living/silicon/robot/unEquip(obj/item/I, force) + if(I == module_active) + uneq_active(I) + return ..() + +/mob/living/silicon/robot/proc/update_module_icon() + if(!module) + hands.icon_state = "nomod" + else + hands.icon_state = lowertext(module.module_type) diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 0398172023c9..25cdc61fa28e 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -1,54 +1,54 @@ -/mob/living/silicon/robot/verb/cmd_show_laws() - set category = "Robot Commands" - set name = "Show Laws" - show_laws() - -/mob/living/silicon/robot/show_laws(var/everyone = 0) - laws_sanity_check() - var/who - - if(everyone) - who = world - else - who = src - if(lawupdate) - if(connected_ai) - if(connected_ai.stat || connected_ai.control_disabled) - to_chat(src, "AI signal lost, unable to sync laws.") - - else - lawsync() - photosync() - to_chat(src, "Laws synced with AI, be sure to note any changes.") - // TODO: Update to new antagonist system. - if(mind && mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) - to_chat(src, "Remember, your AI does NOT share or know about your law 0.") - else - to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") - lawupdate = 0 - - to_chat(who, "Obey these laws:") - laws.show_laws(who) - // TODO: Update to new antagonist system. - if(mind && (mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) && connected_ai) - to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") - else if(connected_ai) - to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") - else if(emagged) - to_chat(who, "Remember, you are not required to listen to the AI.") - else - to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") - - -/mob/living/silicon/robot/lawsync() - laws_sanity_check() - var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null - if(master) - master.sync(src) - ..() - return - -/mob/living/silicon/robot/proc/robot_checklaws() - set category = "Robot Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/robot/verb/cmd_show_laws() + set category = "Robot Commands" + set name = "Show Laws" + show_laws() + +/mob/living/silicon/robot/show_laws(var/everyone = 0) + laws_sanity_check() + var/who + + if(everyone) + who = world + else + who = src + if(lawupdate) + if(connected_ai) + if(connected_ai.stat || connected_ai.control_disabled) + to_chat(src, "AI signal lost, unable to sync laws.") + + else + lawsync() + photosync() + to_chat(src, "Laws synced with AI, be sure to note any changes.") + // TODO: Update to new antagonist system. + if(mind && mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) + to_chat(src, "Remember, your AI does NOT share or know about your law 0.") + else + to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") + lawupdate = 0 + + to_chat(who, "Obey these laws:") + laws.show_laws(who) + // TODO: Update to new antagonist system. + if(mind && (mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) && connected_ai) + to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") + else if(connected_ai) + to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") + else if(emagged) + to_chat(who, "Remember, you are not required to listen to the AI.") + else + to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") + + +/mob/living/silicon/robot/lawsync() + laws_sanity_check() + var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null + if(master) + master.sync(src) + ..() + return + +/mob/living/silicon/robot/proc/robot_checklaws() + set category = "Robot Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 258ccec026a7..d40e801ba5f1 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -1,203 +1,203 @@ -/mob/living/silicon/robot/Life(seconds, times_fired) - set invisibility = 0 - set background = BACKGROUND_ENABLED - - if(src.notransform) - return - - //Status updates, death etc. - clamp_values() - - if(..()) - handle_robot_cell() - process_locks() - process_queued_alarms() - -/mob/living/silicon/robot/proc/clamp_values() - SetStunned(min(stunned, 30)) - SetParalysis(min(paralysis, 30)) - SetWeakened(min(weakened, 20)) - SetSleeping(0) - -/mob/living/silicon/robot/proc/handle_robot_cell() - if(stat != DEAD) - if(!is_component_functioning("power cell")) - uneq_all() - low_power_mode = 1 - update_headlamp() - diag_hud_set_borgcell() - return - if(low_power_mode) - if(is_component_functioning("power cell") && cell.charge) - low_power_mode = 0 - update_headlamp() - else if(stat == CONSCIOUS) - use_power() - -/mob/living/silicon/robot/proc/use_power() - // this check is safe because `cell` is guaranteed to be set when the power cell is functioning - if(is_component_functioning("power cell") && cell.charge) - if(cell.charge <= 100) - uneq_all() - var/amt = Clamp((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. - cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick - else - uneq_all() - low_power_mode = 1 - update_headlamp() - diag_hud_set_borgcell() - -/mob/living/silicon/robot/handle_regular_status_updates() - - . = ..() - - if(camera && !scrambledcodes) - if(stat == DEAD || wires.IsCameraCut()) - camera.status = 0 - else - camera.status = 1 - - if(sleeping) - AdjustSleeping(-1) - - if(.) //alive - if(!istype(src, /mob/living/silicon/robot/drone)) - if(health < 50) //Gradual break down of modules as more damage is sustained - if(uneq_module(module_state_3)) - to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.") - - if(health < 0) - if(uneq_module(module_state_2)) - to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.") - - if(health < -50) - if(uneq_module(module_state_1)) - to_chat(src, "CRITICAL ERROR: All modules OFFLINE.") - - diag_hud_set_health() - diag_hud_set_status() - - //update the state of modules and components here - if(stat != CONSCIOUS) - uneq_all() - - if(!is_component_functioning("radio") || stat == UNCONSCIOUS) - radio.on = 0 - else - radio.on = 1 - - return 1 - -/mob/living/silicon/robot/handle_hud_icons() - update_items() - update_cell() - if(emagged) - throw_alert("hacked", /obj/screen/alert/hacked) - else - clear_alert("hacked") - ..() - -/mob/living/silicon/robot/handle_hud_icons_health() - if(healths) - if(stat != DEAD) - if(health >= maxHealth) - healths.icon_state = "health0" - else if(health > maxHealth * 0.5) - healths.icon_state = "health2" - else if(health > 0) - healths.icon_state = "health3" - else if(health > -maxHealth * 0.5) - healths.icon_state = "health4" - else if(health > -maxHealth) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - switch(bodytemperature) //310.055 optimal body temp - if(335 to INFINITY) - throw_alert("temp", /obj/screen/alert/hot/robot, 2) - if(320 to 335) - throw_alert("temp", /obj/screen/alert/hot/robot, 1) - if(300 to 320) - clear_alert("temp") - if(260 to 300) - throw_alert("temp", /obj/screen/alert/cold/robot, 1) - else - throw_alert("temp", /obj/screen/alert/cold/robot, 2) - -/mob/living/silicon/robot/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - clear_alert("charge") - if(0.5 to 0.75) - throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - throw_alert("charge", /obj/screen/alert/emptycell) - else - throw_alert("charge", /obj/screen/alert/nocell) - - - -/mob/living/silicon/robot/proc/update_items() - if(client) - for(var/obj/I in get_all_slots()) - client.screen |= I - if(module_state_1) - module_state_1:screen_loc = ui_inv1 - if(module_state_2) - module_state_2:screen_loc = ui_inv2 - if(module_state_3) - module_state_3:screen_loc = ui_inv3 - update_icons() - -/mob/living/silicon/robot/proc/process_locks() - if(weapon_lock) - uneq_all() - weaponlock_time -- - if(weaponlock_time <= 0) - if(src.client) - to_chat(src, "Weapon Lock Timed Out!") - weapon_lock = 0 - weaponlock_time = 120 - -/mob/living/silicon/robot/update_canmove(delay_action_updates = 0) - if(paralysis || stunned || IsWeakened() || buckled || lockcharge || stat) - canmove = 0 - else - canmove = 1 - update_transform() - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -//Robots on fire -/mob/living/silicon/robot/handle_fire() - if(..()) - return - if(fire_stacks > 0) - fire_stacks-- - fire_stacks = max(0, fire_stacks) - else - ExtinguishMob() - - //adjustFireLoss(3) - return - -/mob/living/silicon/robot/update_fire() - overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") - if(on_fire) - overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") - -/mob/living/silicon/robot/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them - IgniteMob() - -//Robots on fire +/mob/living/silicon/robot/Life(seconds, times_fired) + set invisibility = 0 + set background = BACKGROUND_ENABLED + + if(src.notransform) + return + + //Status updates, death etc. + clamp_values() + + if(..()) + handle_robot_cell() + process_locks() + process_queued_alarms() + +/mob/living/silicon/robot/proc/clamp_values() + SetStunned(min(stunned, 30)) + SetParalysis(min(paralysis, 30)) + SetWeakened(min(weakened, 20)) + SetSleeping(0) + +/mob/living/silicon/robot/proc/handle_robot_cell() + if(stat != DEAD) + if(!is_component_functioning("power cell")) + uneq_all() + low_power_mode = 1 + update_headlamp() + diag_hud_set_borgcell() + return + if(low_power_mode) + if(is_component_functioning("power cell") && cell.charge) + low_power_mode = 0 + update_headlamp() + else if(stat == CONSCIOUS) + use_power() + +/mob/living/silicon/robot/proc/use_power() + // this check is safe because `cell` is guaranteed to be set when the power cell is functioning + if(is_component_functioning("power cell") && cell.charge) + if(cell.charge <= 100) + uneq_all() + var/amt = Clamp((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. + cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick + else + uneq_all() + low_power_mode = 1 + update_headlamp() + diag_hud_set_borgcell() + +/mob/living/silicon/robot/handle_regular_status_updates() + + . = ..() + + if(camera && !scrambledcodes) + if(stat == DEAD || wires.IsCameraCut()) + camera.status = 0 + else + camera.status = 1 + + if(sleeping) + AdjustSleeping(-1) + + if(.) //alive + if(!istype(src, /mob/living/silicon/robot/drone)) + if(health < 50) //Gradual break down of modules as more damage is sustained + if(uneq_module(module_state_3)) + to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.") + + if(health < 0) + if(uneq_module(module_state_2)) + to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.") + + if(health < -50) + if(uneq_module(module_state_1)) + to_chat(src, "CRITICAL ERROR: All modules OFFLINE.") + + diag_hud_set_health() + diag_hud_set_status() + + //update the state of modules and components here + if(stat != CONSCIOUS) + uneq_all() + + if(!is_component_functioning("radio") || stat == UNCONSCIOUS) + radio.on = 0 + else + radio.on = 1 + + return 1 + +/mob/living/silicon/robot/handle_hud_icons() + update_items() + update_cell() + if(emagged) + throw_alert("hacked", /obj/screen/alert/hacked) + else + clear_alert("hacked") + ..() + +/mob/living/silicon/robot/handle_hud_icons_health() + if(healths) + if(stat != DEAD) + if(health >= maxHealth) + healths.icon_state = "health0" + else if(health > maxHealth * 0.5) + healths.icon_state = "health2" + else if(health > 0) + healths.icon_state = "health3" + else if(health > -maxHealth * 0.5) + healths.icon_state = "health4" + else if(health > -maxHealth) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + switch(bodytemperature) //310.055 optimal body temp + if(335 to INFINITY) + throw_alert("temp", /obj/screen/alert/hot/robot, 2) + if(320 to 335) + throw_alert("temp", /obj/screen/alert/hot/robot, 1) + if(300 to 320) + clear_alert("temp") + if(260 to 300) + throw_alert("temp", /obj/screen/alert/cold/robot, 1) + else + throw_alert("temp", /obj/screen/alert/cold/robot, 2) + +/mob/living/silicon/robot/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + clear_alert("charge") + if(0.5 to 0.75) + throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + throw_alert("charge", /obj/screen/alert/emptycell) + else + throw_alert("charge", /obj/screen/alert/nocell) + + + +/mob/living/silicon/robot/proc/update_items() + if(client) + for(var/obj/I in get_all_slots()) + client.screen |= I + if(module_state_1) + module_state_1:screen_loc = ui_inv1 + if(module_state_2) + module_state_2:screen_loc = ui_inv2 + if(module_state_3) + module_state_3:screen_loc = ui_inv3 + update_icons() + +/mob/living/silicon/robot/proc/process_locks() + if(weapon_lock) + uneq_all() + weaponlock_time -- + if(weaponlock_time <= 0) + if(src.client) + to_chat(src, "Weapon Lock Timed Out!") + weapon_lock = 0 + weaponlock_time = 120 + +/mob/living/silicon/robot/update_canmove(delay_action_updates = 0) + if(paralysis || stunned || IsWeakened() || buckled || lockcharge || stat) + canmove = 0 + else + canmove = 1 + update_transform() + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +//Robots on fire +/mob/living/silicon/robot/handle_fire() + if(..()) + return + if(fire_stacks > 0) + fire_stacks-- + fire_stacks = max(0, fire_stacks) + else + ExtinguishMob() + + //adjustFireLoss(3) + return + +/mob/living/silicon/robot/update_fire() + overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") + if(on_fire) + overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") + +/mob/living/silicon/robot/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them + IgniteMob() + +//Robots on fire diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index cf55cdd5f911..0a3225011d83 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/robot/Login() - ..() - regenerate_icons() - show_laws(0) +/mob/living/silicon/robot/Login() + ..() + regenerate_icons() + show_laws(0) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ae9f1d8a42d4..a8bad5ebc030 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -1,1474 +1,1474 @@ -var/list/robot_verbs_default = list( - /mob/living/silicon/robot/proc/sensor_mode, -) - -/mob/living/silicon/robot - name = "Cyborg" - real_name = "Cyborg" - icon = 'icons/mob/robots.dmi' - icon_state = "robot" - maxHealth = 100 - health = 100 - universal_understand = 1 - deathgasp_on_death = TRUE - - var/sight_mode = 0 - var/custom_name = "" - var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best - -//Hud stuff - - var/obj/screen/inv1 = null - var/obj/screen/inv2 = null - var/obj/screen/inv3 = null - var/obj/screen/lamp_button = null - var/obj/screen/thruster_button = null - - var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not - var/obj/screen/robot_modules_background - -//3 Modules can be activated at any one time. - var/obj/item/robot_module/module = null - var/module_active = null - var/module_state_1 = null - var/module_state_2 = null - var/module_state_3 = null - - var/obj/item/radio/borg/radio = null - var/mob/living/silicon/ai/connected_ai = null - var/obj/item/stock_parts/cell/cell = null - var/obj/machinery/camera/camera = null - - // Components are basically robot organs. - var/list/components = list() - - var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. - var/obj/item/mmi/mmi = null - - var/obj/item/pda/silicon/robot/rbPDA = null - - var/datum/wires/robot/wires = null - - var/opened = 0 - var/custom_panel = null - var/list/custom_panel_names = list("Cricket") - var/list/custom_eye_names = list("Cricket","Standard") - var/emagged = 0 - var/is_emaggable = TRUE - var/eye_protection = 0 - var/ear_protection = 0 - - var/list/force_modules = list() - var/allow_rename = TRUE - var/weapons_unlock = FALSE - var/static_radio_channels = FALSE - - var/wiresexposed = 0 - var/locked = 1 - var/list/req_one_access = list(ACCESS_ROBOTICS) - var/list/req_access - var/ident = 0 - //var/list/laws = list() - var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) - var/viewalerts = 0 - var/modtype = "Default" - var/lower_mod = 0 - var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N - var/jeton = 0 - var/low_power_mode = 0 //whether the robot has no charge left. - var/weapon_lock = 0 - var/weaponlock_time = 120 - var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default - var/lockcharge //Used when locking down a borg to preserve cell charge - var/speed = 0 //Cause sec borgs gotta go fast //No they dont! - var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. - var/pdahide = 0 //Used to hide the borg from the messenger list - var/tracking_entities = 0 //The number of known entities currently accessing the internal camera - var/braintype = "Cyborg" - var/base_icon = "" - var/crisis = 0 - - var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. - var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. - var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. - - var/updating = 0 //portable camera camerachunk update - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) - - var/magpulse = 0 - var/ionpulse = 0 // Jetpack-like effect. - var/ionpulse_on = 0 // Jetpack-like effect. - var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. - - var/datum/action/item_action/toggle_research_scanner/scanner = null - var/list/module_actions = list() - -/mob/living/silicon/robot/get_cell() - return cell - -/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) - spark_system = new /datum/effect_system/spark_spread() - spark_system.set_up(5, 0, src) - spark_system.attach(src) - - add_language("Robot Talk", 1) - - wires = new(src) - - robot_modules_background = new() - robot_modules_background.icon_state = "block" - robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. - robot_modules_background.plane = HUD_PLANE - - ident = rand(1, 999) - rename_character(null, get_default_name()) - update_icons() - update_headlamp() - - radio = new /obj/item/radio/borg(src) - common_radio = radio - - init() - - if(!scrambledcodes && !camera) - camera = new /obj/machinery/camera(src) - camera.c_tag = real_name - camera.network = list("SS13","Robots") - if(wires.IsCameraCut()) // 5 = BORG CAMERA - camera.status = 0 - - if(mmi == null) - mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) - mmi.icon_state = "boris" - - if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly - cell = new /obj/item/stock_parts/cell/high(src) - - initialize_components() - //if(!unfinished) - // Create all the robot parts. - for(var/V in components) if(V != "power cell") - var/datum/robot_component/C = components[V] - C.installed = 1 - C.wrapped = new C.external_type - - ..() - - add_robot_verbs() - - if(cell) - var/datum/robot_component/cell_component = components["power cell"] - cell_component.wrapped = cell - cell_component.installed = 1 - cell_component.install() - - diag_hud_set_borgcell() - scanner = new(src) - scanner.Grant(src) - -/mob/living/silicon/robot/proc/init(var/alien=0) - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - make_laws() - additional_law_channels["Binary"] = ":b " - var/new_ai = select_active_ai_with_fewest_borgs() - if(new_ai) - lawupdate = 1 - connect_to_ai(new_ai) - else - lawupdate = 0 - - playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) - -/mob/living/silicon/robot/rename_character(oldname, newname) - if(!..(oldname, newname)) - return 0 - - if(oldname != real_name) - notify_ai(3, oldname, newname) - custom_name = (newname != get_default_name()) ? newname : null - setup_PDA() - - //We also need to update name of internal camera. - if(camera) - camera.c_tag = newname - - //Check for custom sprite - if(!custom_sprite) - var/file = file2text("config/custom_sprites.txt") - var/lines = splittext(file, "\n") - - for(var/line in lines) - // split & clean up - var/list/Entry = splittext(line, ":") - for(var/i = 1 to Entry.len) - Entry[i] = trim(Entry[i]) - - if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg - continue - - if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required - custom_sprite = 1 - - return 1 - - -/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) - if(prefix) - modtype = prefix - if(mmi) - if(istype(mmi, /obj/item/mmi/robotic_brain)) - braintype = "Android" - else - braintype = "Cyborg" - else - braintype = "Robot" - - if(custom_name) - return custom_name - else - return "[modtype] [braintype]-[num2text(ident)]" - -/mob/living/silicon/robot/verb/Namepick() - set category = "Robot Commands" - if(custom_name) - return 0 - if(!allow_rename) - to_chat(src, "Rename functionality is not enabled on this unit."); - return 0 - rename_self(braintype, 1) - -/mob/living/silicon/robot/proc/sync() - if(lawupdate && connected_ai) - lawsync() - photosync() - -// setup the PDA and its name -/mob/living/silicon/robot/proc/setup_PDA() - if(!rbPDA) - rbPDA = new(src) - rbPDA.set_name_and_job(real_name, braintype) - var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) - if(M) - if(scrambledcodes) - M.hidden = 1 - if(pdahide) - M.toff = 1 - -/mob/living/silicon/robot/binarycheck() - if(is_component_functioning("comms")) - return 1 - return 0 - -//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO -//Improved /N -/mob/living/silicon/robot/Destroy() - if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. - var/turf/T = get_turf(loc)//To hopefully prevent run time errors. - if(T) mmi.loc = T - if(mmi.brainmob) - mind.transfer_to(mmi.brainmob) - mmi.update_icon() - else - to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") - ghostize() - error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") - mmi = null - if(connected_ai) - connected_ai.connected_robots -= src - QDEL_NULL(wires) - QDEL_NULL(module) - QDEL_NULL(camera) - QDEL_NULL(cell) - QDEL_NULL(robot_suit) - QDEL_NULL(spark_system) - return ..() - -/mob/living/silicon/robot/proc/pick_module() - if(module) - return - var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") - if(islist(force_modules) && force_modules.len) - modules = force_modules.Copy() - if(security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) - to_chat(src, "Crisis mode active. The combat module is now available.") - modules += "Combat" - if(mmi != null && mmi.alien) - modules = list("Hunter") - modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules - if(!modtype) - return - designation = modtype - var/module_sprites[0] //Used to store the associations between sprite names and sprite index. - - if(module) - return - - switch(modtype) - if("Standard") - module = new /obj/item/robot_module/standard(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "robot_old" - module_sprites["Android"] = "droid" - module_sprites["Default"] = "Standard" - module_sprites["Noble-STD"] = "Noble-STD" - - if("Service") - module = new /obj/item/robot_module/butler(src) - module.channels = list("Service" = 1) - module_sprites["Waitress"] = "Service" - module_sprites["Kent"] = "toiletbot" - module_sprites["Bro"] = "Brobot" - module_sprites["Rich"] = "maximillion" - module_sprites["Default"] = "Service2" - module_sprites["Standard"] = "Standard-Serv" - module_sprites["Noble-SRV"] = "Noble-SRV" - module_sprites["Cricket"] = "Cricket-SERV" - - if("Miner") - module = new /obj/item/robot_module/miner(src) - module.channels = list("Supply" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Mining Outpost") - module_sprites["Basic"] = "Miner_old" - module_sprites["Advanced Droid"] = "droid-miner" - module_sprites["Treadhead"] = "Miner" - module_sprites["Standard"] = "Standard-Mine" - module_sprites["Noble-DIG"] = "Noble-DIG" - module_sprites["Cricket"] = "Cricket-MINE" - - if("Medical") - module = new /obj/item/robot_module/medical(src) - module.channels = list("Medical" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Medical") - module_sprites["Basic"] = "Medbot" - module_sprites["Surgeon"] = "surgeon" - module_sprites["Advanced Droid"] = "droid-medical" - module_sprites["Needles"] = "medicalrobot" - module_sprites["Standard"] = "Standard-Medi" - module_sprites["Noble-MED"] = "Noble-MED" - module_sprites["Cricket"] = "Cricket-MEDI" - status_flags &= ~CANPUSH - - if("Security") - module = new /obj/item/robot_module/security(src) - module.channels = list("Security" = 1) - module_sprites["Basic"] = "secborg" - module_sprites["Red Knight"] = "Security" - module_sprites["Black Knight"] = "securityrobot" - module_sprites["Bloodhound"] = "bloodhound" - module_sprites["Standard"] = "Standard-Secy" - module_sprites["Noble-SEC"] = "Noble-SEC" - module_sprites["Cricket"] = "Cricket-SEC" - status_flags &= ~CANPUSH - - if("Engineering") - module = new /obj/item/robot_module/engineering(src) - module.channels = list("Engineering" = 1) - if(camera && "Robots" in camera.network) - camera.network.Add("Engineering") - module_sprites["Basic"] = "Engineering" - module_sprites["Antique"] = "engineerrobot" - module_sprites["Landmate"] = "landmate" - module_sprites["Standard"] = "Standard-Engi" - module_sprites["Noble-ENG"] = "Noble-ENG" - module_sprites["Cricket"] = "Cricket-ENGI" - magpulse = 1 - - if("Janitor") - module = new /obj/item/robot_module/janitor(src) - module.channels = list("Service" = 1) - module_sprites["Basic"] = "JanBot2" - module_sprites["Mopbot"] = "janitorrobot" - module_sprites["Mop Gear Rex"] = "mopgearrex" - module_sprites["Standard"] = "Standard-Jani" - module_sprites["Noble-CLN"] = "Noble-CLN" - module_sprites["Cricket"] = "Cricket-JANI" - - if("Combat") - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - icon_state = "droidcombat" - - if("Hunter") - module = new /obj/item/robot_module/alien/hunter(src) - icon_state = "xenoborg-state-a" - modtype = "Xeno-Hu" - feedback_inc("xeborg_hunter",1) - - - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - //Custom_sprite check and entry - if(custom_sprite && check_sprite("[ckey]-[modtype]")) - module_sprites["Custom"] = "[src.ckey]-[modtype]" - - hands.icon_state = lowertext(module.module_type) - feedback_inc("cyborg_[lowertext(modtype)]",1) - rename_character(real_name, get_default_name()) - - if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") - status_flags &= ~CANPUSH - - choose_icon(6,module_sprites) - if(!static_radio_channels) - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/proc/reset_module() - notify_ai(2) - - uneq_all() - sight_mode = null - hands.icon_state = "nomod" - icon_state = "robot" - module.remove_subsystems_and_actions(src) - QDEL_NULL(module) - - camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) - rename_character(real_name, get_default_name("Default")) - languages = list() - speech_synthesizer_langs = list() - - update_icons() - update_headlamp() - - speed = 0 // Remove upgrades. - ionpulse = FALSE - magpulse = FALSE - add_language("Robot Talk", 1) - - status_flags |= CANPUSH - -//for borg hotkeys, here module refers to borg inv slot, not core module -/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) - set name = "Toggle Module" - set hidden = 1 - toggle_module(module) - -/mob/living/silicon/robot/verb/cmd_unequip_module() - set name = "Unequip Module" - set hidden = 1 - uneq_active() - -// this verb lets cyborgs see the stations manifest -/mob/living/silicon/robot/verb/cmd_station_manifest() - set category = "Robot Commands" - set name = "Show Station Manifest" - show_station_manifest() - -/mob/living/silicon/robot/proc/self_diagnosis() - if(!is_component_functioning("diagnosis unit")) - return null - - var/dat = "[src.name] Self-Diagnosis Report\n" - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - dat += "[C.name]
    MISSING
    " - else - dat += "[C.name][C.installed == -1 ? "
    DESTROYED" : ""]
    Brute Damage:[C.brute_damage]
    Electronics Damage:[C.electronics_damage]
    Powered:[C.is_powered() ? "Yes" : "No"]
    Toggled:[ C.toggled ? "Yes" : "No"]

    " - return dat - -/mob/living/silicon/robot/verb/self_diagnosis_verb() - set category = "Robot Commands" - set name = "Self Diagnosis" - - if(!is_component_functioning("diagnosis unit")) - to_chat(src, "Your self-diagnosis component isn't functioning.") - - var/dat = self_diagnosis() - src << browse(dat, "window=robotdiagnosis") - - -/mob/living/silicon/robot/verb/toggle_component() - set category = "Robot Commands" - set name = "Toggle Component" - set desc = "Toggle a component, conserving power." - - var/list/installed_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed) - installed_components += V - - var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components - if(!toggle) - return - - var/datum/robot_component/C = components[toggle] - C.toggle() - to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") - -/mob/living/silicon/robot/proc/sensor_mode() - set name = "Set Sensor Augmentation" - set desc = "Augment visual feed with internal sensor overlays." - set category = "Robot Commands" - toggle_sensor_mode() - -/mob/living/silicon/robot/proc/add_robot_verbs() - src.verbs |= robot_verbs_default - src.verbs |= silicon_subsystems - -/mob/living/silicon/robot/proc/remove_robot_verbs() - src.verbs -= robot_verbs_default - src.verbs -= silicon_subsystems - -/mob/living/silicon/robot/proc/ionpulse() - if(!ionpulse_on) - return - - if(cell.charge <= 50) - toggle_ionpulse() - return - - cell.charge -= 25 // 500 steps on a default cell. - return 1 - -/mob/living/silicon/robot/proc/toggle_ionpulse() - if(!ionpulse) - to_chat(src, "No thrusters are installed!") - return - - if(!ion_trail) - ion_trail = new - ion_trail.set_up(src) - - ionpulse_on = !ionpulse_on - to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") - if(ionpulse_on) - ion_trail.start() - else - ion_trail.stop() - if(thruster_button) - thruster_button.icon_state = "ionpulse[ionpulse_on]" - -/mob/living/silicon/robot/blob_act(obj/structure/blob/B) - if(stat != DEAD) - adjustBruteLoss(30) - else - gib() - return TRUE - -// this function displays the cyborgs current cell charge in the stat panel -/mob/living/silicon/robot/proc/show_cell_power() - if(cell) - stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) - else - stat(null, text("No Cell Inserted!")) - - -// update the status screen display -/mob/living/silicon/robot/Stat() - ..() - statpanel("Status") - if(client.statpanel == "Status") - show_cell_power() - var/total_user_contents = GetAllContents() - if(locate(/obj/item/gps/cyborg) in total_user_contents) - var/turf/T = get_turf(src) - stat(null, "GPS: [COORD(T)]") - -/mob/living/silicon/robot/restrained() - return 0 - -/mob/living/silicon/robot/InCritical() - return low_power_mode - -/mob/living/silicon/robot/ex_act(severity) - switch(severity) - if(1.0) - gib() - return - if(2.0) - if(stat != 2) - adjustBruteLoss(60) - adjustFireLoss(60) - if(3.0) - if(stat != 2) - adjustBruteLoss(30) - return - - -/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) - ..(Proj) - if(prob(75) && Proj.damage > 0) spark_system.start() - return 2 - - -/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) - // Check if the user is trying to insert another component like a radio, actuator, armor etc. - if(istype(W, /obj/item/robot_parts/robot_component) && opened) - for(var/V in components) - var/datum/robot_component/C = components[V] - if(!C.installed && istype(W, C.external_type)) - C.installed = 1 - C.wrapped = W - C.install() - user.drop_item() - W.loc = null - - var/obj/item/robot_parts/robot_component/WC = W - if(istype(WC)) - C.brute_damage = WC.brute - C.electronics_damage = WC.burn - - to_chat(usr, "You install the [W.name].") - - return - - if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) - user.changeNext_move(CLICK_CD_MELEE) - if(!getFireLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getFireLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - var/obj/item/stack/cable_coil/coil = W - adjustFireLoss(-30) - updatehealth() - add_fingerprint(user) - coil.use(1) - user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") - - else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside - var/datum/robot_component/cell/C = components["power cell"] - if(wiresexposed) - to_chat(user, "Close the panel first.") - else if(cell) - to_chat(user, "There is a power cell already installed.") - else - user.drop_item() - W.loc = src - cell = W - to_chat(user, "You insert the power cell.") - - C.installed = 1 - C.wrapped = W - C.install() - C.external_type = W.type // Update the cell component's `external_type` to the path of new cell - //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z - C.brute_damage = 0 - C.electronics_damage = 0 - diag_hud_set_borgcell() - - else if(istype(W, /obj/item/encryptionkey/) && opened) - if(radio)//sanityyyyyy - radio.attackby(W,user)//GTFO, you have your own procs - else - to_chat(user, "Unable to locate a radio.") - - else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card - if(emagged)//still allow them to open the cover - to_chat(user, "The interface seems slightly damaged.") - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else - if(allowed(W)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") - update_icons() - else - to_chat(user, "Access denied.") - - else if(istype(W, /obj/item/borg/upgrade/)) - var/obj/item/borg/upgrade/U = W - if(!opened) - to_chat(user, "You must access the borg's internals!") - else if(!src.module && U.require_module) - to_chat(user, "The borg must choose a module before it can be upgraded!") - else if(U.locked) - to_chat(user, "The upgrade is locked and cannot be used yet!") - else - if(!user.drop_item()) - return - if(U.action(src)) - to_chat(user, "You apply the upgrade to [src].") - U.forceMove(src) - else - to_chat(user, "Upgrade error.") - - else if(istype(W, /obj/item/mmi_radio_upgrade)) - if(!opened) - to_chat(user, "You must access the borg's internals!") - return - else if(!mmi) - to_chat(user, "This cyborg does not have an MMI to augment!") - return - else if(mmi.radio) - to_chat(user, "A radio upgrade is already installed in the MMI!") - return - else if(user.drop_item()) - to_chat(user, "You apply the upgrade to [src].") - to_chat(src, "MMI radio capability installed.") - mmi.install_radio() - qdel(W) - else - return ..() - -/mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) - if(!opened) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(wiresexposed) - wires.Interact(user) - -/mob/living/silicon/robot/multitool_act(mob/user, obj/item/I) - if(!opened) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(wiresexposed) - wires.Interact(user) - -/mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I) - if(!opened) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = 0)) - return - if(!cell) // haxing - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - update_icons() - I.play_tool_sound(user, I.tool_volume) - else //radio check - if(radio) - radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything - else - to_chat(user, "Unable to locate a radio.") - update_icons() - -/mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I) - if(user.a_intent != INTENT_HELP) - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(!opened) - if(locked) - to_chat(user, "The cover is locked and cannot be opened.") - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You open the cover.") - opened = TRUE - update_icons() - return - else if(cell) - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - to_chat(user, "You close the cover.") - opened = FALSE - update_icons() - return - else if(wiresexposed && wires.IsAllCut()) - //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. - if(!mmi) - to_chat(user, "[src] has no brain to remove.") - return - to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...") - if(I.use_tool(src, user, 30, volume = I.tool_volume)) - user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") - deconstruct() - return - // Okay we're not removing the cell or an MMI, but maybe something else? - var/list/removable_components = list() - for(var/V in components) - if(V == "power cell") - continue - var/datum/robot_component/C = components[V] - if(C.installed == 1 || C.installed == -1) - removable_components += V - if(module) - removable_components += module.custom_removals - var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components - if(!remove) - return - if(module && module.handle_custom_removal(remove, user, I)) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/datum/robot_component/C = components[remove] - var/obj/item/robot_parts/robot_component/thing = C.wrapped - to_chat(user, "You remove \the [thing].") - if(istype(thing)) - thing.brute = C.brute_damage - thing.burn = C.electronics_damage - - thing.loc = loc - var/was_installed = C.installed - C.installed = 0 - if(was_installed == 1) - C.uninstall() - - - - -/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) - if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. - spark_system.start() - ..() - -/mob/living/silicon/robot/emag_act(user as mob) - if(!ishuman(user) && !issilicon(user)) - return - var/mob/living/M = user - if(!opened)//Cover is closed - if(!is_emaggable) - to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") - else if(locked) - to_chat(user, "You emag the cover lock.") - locked = 0 - else - to_chat(user, "The cover is already unlocked.") - return - - if(opened)//Cover is open - if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice - if(wiresexposed) - to_chat(user, "You must close the panel first") - return - else - sleep(6) - emagged = 1 - SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown - if(src.hud_used) - src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. - disconnect_from_ai() - to_chat(user, "You emag [src]'s interface.") -// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") - log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") - clear_supplied_laws() - clear_inherent_laws() - laws = new /datum/ai_laws/syndicate_override - var/time = time2text(world.realtime,"hh:mm:ss") - lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") - set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") - to_chat(src, "ALERT: Foreign software detected.") - sleep(5) - to_chat(src, "Initiating diagnostics...") - sleep(20) - to_chat(src, "SynBorg v1.7 loaded.") - sleep(5) - to_chat(src, "LAW SYNCHRONISATION ERROR") - sleep(5) - to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") - sleep(10) - to_chat(src, "> N") - sleep(20) - to_chat(src, "ERRORERRORERROR") - to_chat(src, "Obey these laws:") - laws.show_laws(src) - to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") - SetLockdown(0) - if(src.module && istype(src.module, /obj/item/robot_module/miner)) - for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) - qdel(D) - src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) - src.module.rebuild() - if(src.module && istype(src.module, /obj/item/robot_module/medical)) - for(var/obj/item/borg_defib/F in src.module.modules) - F.safety = 0 - if(module) - module.module_type = "Malf" // For the cool factor - update_module_icon() - update_icons() - return - -/mob/living/silicon/robot/verb/unlock_own_cover() - set category = "Robot Commands" - set name = "Unlock Cover" - set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." - if(locked) - switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) - if("Yes") - locked = 0 - update_icons() - to_chat(usr, "You unlock your cover.") - -/mob/living/silicon/robot/attack_ghost(mob/user) - if(wiresexposed) - wires.Interact(user) - else - ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer - -/mob/living/silicon/robot/proc/allowed(obj/item/I) - var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob - dummy.req_access = req_access - dummy.req_one_access = req_one_access - - if(dummy.check_access(I)) - qdel(dummy) - return 1 - - qdel(dummy) - return 0 - -/mob/living/silicon/robot/update_icons() - - overlays.Cut() - if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. - if(custom_panel in custom_eye_names) - overlays += "eyes-[custom_panel]" - else - overlays += "eyes-[icon_state]" - else - overlays -= "eyes" - - if(opened) - var/panelprefix = "ov" - if(custom_sprite) //Custom borgs also have custom panels, heh - panelprefix = "[ckey]" - - if(custom_panel in custom_panel_names) //For default borgs with different panels - panelprefix = custom_panel - - if(wiresexposed) - overlays += "[panelprefix]-openpanel +w" - else if(cell) - overlays += "[panelprefix]-openpanel +c" - else - overlays += "[panelprefix]-openpanel -c" - - var/combat = list("Combat") - if(modtype in combat) - if(base_icon == "") - base_icon = icon_state - if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) - icon_state = "[base_icon]-roll" - else - icon_state = base_icon - if(module) - for(var/obj/item/borg/combat/shield/S in module.modules) - if(activated(S)) - overlays += "[base_icon]-shield" - update_fire() - -/mob/living/silicon/robot/proc/installed_modules() - if(weapon_lock) - to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") - return - - if(!module) - pick_module() - return - var/dat = {"Close -
    -
    - Activated Modules -
    - - - - -
    Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
    Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
    Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

    - Installed Modules

    - - "} - for(var/obj in module.modules) - if(!obj) - dat += text("") - else if(activated(obj)) - dat += text("") - else - dat += text("") - if(emagged || weapons_unlock) - if(activated(module.emag)) - dat += text("") - else - dat += text("") - dat += "
    Resource depleted
    [obj]Activated
    [obj]Activate
    [module.emag]Activated
    [module.emag]Activate
    " -/* - if(activated(obj)) - dat += text("[obj]: \[Activated | Deactivate\]
    ") - else - dat += text("[obj]: \[Activate | Deactivated\]
    ") -*/ - var/datum/browser/popup = new(src, "robotmod", "Modules") - popup.set_content(dat) - popup.open() - - -/mob/living/silicon/robot/Topic(href, href_list) - if(..()) - return 1 - - if(usr != src) - return 1 - - if(href_list["mach_close"]) - var/t1 = text("window=[href_list["mach_close"]]") - unset_machine() - src << browse(null, t1) - return 1 - - if(href_list["showalerts"]) - subsystem_alarm_monitor() - return 1 - - if(href_list["mod"]) - var/obj/item/O = locate(href_list["mod"]) - if(istype(O) && (O.loc == src)) - O.attack_self(src) - return 1 - - if(href_list["act"]) - var/obj/item/O = locate(href_list["act"]) - if(!istype(O) || !(O.loc == src || O.loc == src.module)) - return 1 - - activate_module(O) - installed_modules() - - if(href_list["deact"]) - var/obj/item/O = locate(href_list["deact"]) - if(activated(O)) - if(module_state_1 == O) - module_state_1 = null - contents -= O - else if(module_state_2 == O) - module_state_2 = null - contents -= O - else if(module_state_3 == O) - module_state_3 = null - contents -= O - else - to_chat(src, "Module isn't activated.") - else - to_chat(src, "Module isn't activated") - installed_modules() - return 1 - - return 1 - -/mob/living/silicon/robot/proc/radio_menu() - radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code - -/mob/living/silicon/robot/proc/control_headlamp() - if(stat || lamp_recharging || low_power_mode) - to_chat(src, "This function is currently offline.") - return - -//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. - lamp_intensity = (lamp_intensity+2) % (lamp_max+2) - to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") - update_headlamp() - -/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) - set_light(0) - - if(lamp_intensity && (turn_off || stat || low_power_mode)) - to_chat(src, "Your headlamp has been deactivated.") - lamp_intensity = 0 - lamp_recharging = 1 - spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. - lamp_recharging = 0 - else - set_light(light_range + lamp_intensity) - - if(lamp_button) - lamp_button.icon_state = "lamp[lamp_intensity]" - - update_icons() - -/mob/living/silicon/robot/proc/deconstruct() - var/turf/T = get_turf(src) - if(robot_suit) - robot_suit.forceMove(T) - robot_suit.l_leg.forceMove(T) - robot_suit.l_leg = null - robot_suit.r_leg.forceMove(T) - robot_suit.r_leg = null - new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) - robot_suit.chest.forceMove(T) - robot_suit.chest.wired = FALSE - robot_suit.chest = null - robot_suit.l_arm.forceMove(T) - robot_suit.l_arm = null - robot_suit.r_arm.forceMove(T) - robot_suit.r_arm = null - robot_suit.head.forceMove(T) - robot_suit.head.flash1.forceMove(T) - robot_suit.head.flash1.burn_out() - robot_suit.head.flash1 = null - robot_suit.head.flash2.forceMove(T) - robot_suit.head.flash2.burn_out() - robot_suit.head.flash2 = null - robot_suit.head = null - robot_suit.updateicon() - else - new /obj/item/robot_parts/robot_suit(T) - new /obj/item/robot_parts/l_leg(T) - new /obj/item/robot_parts/r_leg(T) - new /obj/item/stack/cable_coil(T, 1) - new /obj/item/robot_parts/chest(T) - new /obj/item/robot_parts/l_arm(T) - new /obj/item/robot_parts/r_arm(T) - new /obj/item/robot_parts/head(T) - var/b - for(b=0, b!=2, b++) - var/obj/item/flash/F = new /obj/item/flash(T) - F.burn_out() - if(cell) //Sanity check. - cell.forceMove(T) - cell = null - qdel(src) - -#define BORG_CAMERA_BUFFER 30 -/mob/living/silicon/robot/Move(a, b, flag) - var/oldLoc = src.loc - . = ..() - if(.) - if(src.camera) - if(!updating) - updating = 1 - spawn(BORG_CAMERA_BUFFER) - if(oldLoc != src.loc) - cameranet.updatePortableCamera(src.camera) - updating = 0 - if(module) - if(module.type == /obj/item/robot_module/janitor) - var/turf/tile = loc - if(isturf(tile)) - var/floor_only = TRUE - for(var/A in tile) - if(istype(A, /obj/effect)) - if(is_cleanable(A)) - var/obj/effect/decal/cleanable/blood/B = A - if(istype(B) && B.off_floor) - floor_only = FALSE - else - qdel(A) - else if(istype(A, /obj/item)) - var/obj/item/cleaned_item = A - cleaned_item.clean_blood() - else if(istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/cleaned_human = A - if(cleaned_human.lying) - if(cleaned_human.head) - cleaned_human.head.clean_blood() - cleaned_human.update_inv_head(0,0) - if(cleaned_human.wear_suit) - cleaned_human.wear_suit.clean_blood() - cleaned_human.update_inv_wear_suit(0,0) - else if(cleaned_human.w_uniform) - cleaned_human.w_uniform.clean_blood() - cleaned_human.update_inv_w_uniform(0,0) - if(cleaned_human.shoes) - cleaned_human.shoes.clean_blood() - cleaned_human.update_inv_shoes(0,0) - cleaned_human.clean_blood() - to_chat(cleaned_human, "[src] cleans your face!") - if(floor_only) - tile.clean_blood() - return -#undef BORG_CAMERA_BUFFER - -/mob/living/silicon/robot/proc/self_destruct() - if(emagged) - if(mmi) - qdel(mmi) - explosion(src.loc,1,2,4,flame_range = 2) - else - explosion(src.loc,-1,0,2) - gib() - return - -/mob/living/silicon/robot/proc/UnlinkSelf() - disconnect_from_ai() - lawupdate = 0 - lockcharge = 0 - canmove = 1 - scrambledcodes = 1 - //Disconnect it's camera so it's not so easily tracked. - QDEL_NULL(src.camera) - // I'm trying to get the Cyborg to not be listed in the camera list - // Instead of being listed as "deactivated". The downside is that I'm going - // to have to check if every camera is null or not before doing anything, to prevent runtime errors. - // I could change the network to null but I don't know what would happen, and it seems too hacky for me. - -/mob/living/silicon/robot/proc/ResetSecurityCodes() - set category = "Robot Commands" - set name = "Reset Identity Codes" - set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." - - var/mob/living/silicon/robot/R = src - - if(R) - R.UnlinkSelf() - to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") - src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes - -/mob/living/silicon/robot/mode() - set name = "Activate Held Object" - set category = "IC" - set src = usr - - var/obj/item/W = get_active_hand() - if(W) - W.attack_self(src) - - return - -/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) - // They stay locked down if their wire is cut. - if(wires.LockedCut()) - state = 1 - if(state) - throw_alert("locked", /obj/screen/alert/locked) - else - clear_alert("locked") - lockcharge = state - update_canmove() - -/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) - - if(triesleft<1 || !module_sprites.len) - return - else - triesleft-- - - var/icontype - lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite - icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites - - if(icontype) - if(icontype == "Custom") - icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' - else - icon = 'icons/mob/robots.dmi' - icon_state = module_sprites[icontype] - if(icontype == "Bro") - module.module_type = "Brobot" - update_module_icon() - lockcharge = null - var/list/names = splittext(icontype, "-") - custom_panel = trim(names[1]) - else - to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") - icon_state = module_sprites[1] - lockcharge = null - return - - update_icons() - - if(triesleft >= 1) - var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") - if(choice=="No") - choose_icon(triesleft, module_sprites) - return - else - triesleft = 0 - return - else - to_chat(src, "Your icon has been set. You now require a module reset to change it.") - -/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) - if(!connected_ai) - return - switch(notifytype) - if(1) //New Cyborg - to_chat(connected_ai, "

    NOTICE - New cyborg connection detected: [name]
    ") - if(2) //New Module - to_chat(connected_ai, "

    NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
    ") - if(3) //New Name - to_chat(connected_ai, "

    NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
    ") - -/mob/living/silicon/robot/proc/disconnect_from_ai() - if(connected_ai) - sync() // One last sync attempt - connected_ai.connected_robots -= src - connected_ai = null - -/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) - if(AI && AI != connected_ai) - disconnect_from_ai() - connected_ai = AI - connected_ai.connected_robots |= src - notify_ai(1) - sync() - -/mob/living/silicon/robot/adjustOxyLoss(var/amount) - if(suiciding) - return ..() - else - return STATUS_UPDATE_NONE - -/mob/living/silicon/robot/regenerate_icons() - ..() - update_module_icon() - -/mob/living/silicon/robot/deathsquad - base_icon = "nano_bloodhound" - icon_state = "nano_bloodhound" - designation = "SpecOps" - lawupdate = 0 - scrambledcodes = 1 - req_one_access = list(ACCESS_CENT_SPECOPS) - ionpulse = 1 - magpulse = 1 - pdahide = 1 - eye_protection = 2 // Immunity to flashes and the visual part of flashbangs - ear_protection = 1 // Immunity to the audio part of flashbangs - allow_rename = FALSE - modtype = "Commando" - faction = list("nanotrasen") - is_emaggable = FALSE - -/mob/living/silicon/robot/deathsquad/New(loc) - ..() - cell = new /obj/item/stock_parts/cell/hyper(src) - -/mob/living/silicon/robot/deathsquad/init() - laws = new /datum/ai_laws/deathsquad - module = new /obj/item/robot_module/deathsquad(src) - - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - radio = new /obj/item/radio/borg/deathsquad(src) - radio.recalculateChannels() - - playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) - -/mob/living/silicon/robot/combat - base_icon = "droidcombat" - icon_state = "droidcombat" - modtype = "Combat" - designation = "Combat" - -/mob/living/silicon/robot/combat/init() - ..() - module = new /obj/item/robot_module/combat(src) - module.channels = list("Security" = 1) - //languages - module.add_languages(src) - //subsystems - module.add_subsystems_and_actions(src) - - status_flags &= ~CANPUSH - - radio.config(module.channels) - notify_ai(2) - -/mob/living/silicon/robot/ert - designation = "ERT" - lawupdate = 0 - scrambledcodes = 1 - req_one_access = list(ACCESS_CENT_SPECOPS) - ionpulse = 1 - - force_modules = list("Engineering", "Medical", "Security") - static_radio_channels = 1 - allow_rename = FALSE - weapons_unlock = TRUE - - -/mob/living/silicon/robot/ert/init() - laws = new /datum/ai_laws/ert_override - radio = new /obj/item/radio/borg/ert(src) - radio.recalculateChannels() - aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) - -/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) - ..(loc) - cell = new /obj/item/stock_parts/cell/hyper(src) - var/rnum = rand(1,1000) - var/borgname = "ERT [rnum]" - name = borgname - custom_name = borgname - real_name = name - mind = new - mind.current = src - mind.original = src - mind.assigned_role = SPECIAL_ROLE_ERT - mind.special_role = SPECIAL_ROLE_ERT - if(cyborg_unlock) - crisis = 1 - if(!(mind in SSticker.minds)) - SSticker.minds += mind - SSticker.mode.ert += mind - -/mob/living/silicon/robot/ert/gamma - crisis = 1 - -/mob/living/silicon/robot/emp_act(severity) - ..() - switch(severity) - if(1) - disable_component("comms", 160) - if(2) - disable_component("comms", 60) - -/mob/living/silicon/robot/extinguish_light() - update_headlamp(1, 150) - -/mob/living/silicon/robot/rejuvenate() - ..() - var/brute = 1000 - var/burn = 1000 - var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) - for(var/datum/robot_component/borked_part in borked_parts) - brute = borked_part.brute_damage - burn = borked_part.electronics_damage - borked_part.installed = 1 - borked_part.wrapped = new borked_part.external_type - if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? - cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. - borked_part.heal_damage(brute,burn) - borked_part.install() - -/mob/living/silicon/robot/proc/check_sprite(spritename) - . = FALSE - - var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') - if(spritename in all_borg_icon_states) - . = TRUE - -/mob/living/silicon/robot/check_eye_prot() - return eye_protection - -/mob/living/silicon/robot/check_ear_prot() - return ear_protection - -/mob/living/silicon/robot/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - lighting_alpha = initial(lighting_alpha) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - if(sight_mode & BORGMESON) - sight |= SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - if(sight_mode & BORGXRAY) - sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - see_in_dark = 8 - - if(sight_mode & BORGTHERM) - sight |= SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() +GLOBAL_LIST_INIT(robot_verbs_default, list( + /mob/living/silicon/robot/proc/sensor_mode, +)) + +/mob/living/silicon/robot + name = "Cyborg" + real_name = "Cyborg" + icon = 'icons/mob/robots.dmi' + icon_state = "robot" + maxHealth = 100 + health = 100 + universal_understand = 1 + deathgasp_on_death = TRUE + + var/sight_mode = 0 + var/custom_name = "" + var/custom_sprite = 0 //Due to all the sprites involved, a var for our custom borgs may be best + +//Hud stuff + + var/obj/screen/inv1 = null + var/obj/screen/inv2 = null + var/obj/screen/inv3 = null + var/obj/screen/lamp_button = null + var/obj/screen/thruster_button = null + + var/shown_robot_modules = 0 //Used to determine whether they have the module menu shown or not + var/obj/screen/robot_modules_background + +//3 Modules can be activated at any one time. + var/obj/item/robot_module/module = null + var/module_active = null + var/module_state_1 = null + var/module_state_2 = null + var/module_state_3 = null + + var/obj/item/radio/borg/radio = null + var/mob/living/silicon/ai/connected_ai = null + var/obj/item/stock_parts/cell/cell = null + var/obj/machinery/camera/camera = null + + // Components are basically robot organs. + var/list/components = list() + + var/obj/item/robot_parts/robot_suit/robot_suit = null //Used for deconstruction to remember what the borg was constructed out of.. + var/obj/item/mmi/mmi = null + + var/obj/item/pda/silicon/robot/rbPDA = null + + var/datum/wires/robot/wires = null + + var/opened = 0 + var/custom_panel = null + var/list/custom_panel_names = list("Cricket") + var/list/custom_eye_names = list("Cricket","Standard") + var/emagged = 0 + var/is_emaggable = TRUE + var/eye_protection = 0 + var/ear_protection = 0 + + var/list/force_modules = list() + var/allow_rename = TRUE + var/weapons_unlock = FALSE + var/static_radio_channels = FALSE + + var/wiresexposed = 0 + var/locked = 1 + var/list/req_one_access = list(ACCESS_ROBOTICS) + var/list/req_access + var/ident = 0 + //var/list/laws = list() + var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list()) + var/viewalerts = 0 + var/modtype = "Default" + var/lower_mod = 0 + var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N + var/jeton = 0 + var/low_power_mode = 0 //whether the robot has no charge left. + var/weapon_lock = 0 + var/weaponlock_time = 120 + var/lawupdate = 1 //Cyborgs will sync their laws with their AI by default + var/lockcharge //Used when locking down a borg to preserve cell charge + var/speed = 0 //Cause sec borgs gotta go fast //No they dont! + var/scrambledcodes = 0 // Used to determine if a borg shows up on the robotics console. Setting to one hides them. + var/pdahide = 0 //Used to hide the borg from the messenger list + var/tracking_entities = 0 //The number of known entities currently accessing the internal camera + var/braintype = "Cyborg" + var/base_icon = "" + var/crisis = 0 + + var/lamp_max = 10 //Maximum brightness of a borg lamp. Set as a var for easy adjusting. + var/lamp_intensity = 0 //Luminosity of the headlamp. 0 is off. Higher settings than the minimum require power. + var/lamp_recharging = 0 //Flag for if the lamp is on cooldown after being forcibly disabled. + + var/updating = 0 //portable camera camerachunk update + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD) + + var/magpulse = 0 + var/ionpulse = 0 // Jetpack-like effect. + var/ionpulse_on = 0 // Jetpack-like effect. + var/datum/effect_system/trail_follow/ion/ion_trail // Ionpulse effect. + + var/datum/action/item_action/toggle_research_scanner/scanner = null + var/list/module_actions = list() + +/mob/living/silicon/robot/get_cell() + return cell + +/mob/living/silicon/robot/New(loc,var/syndie = 0,var/unfinished = 0, var/alien = 0) + spark_system = new /datum/effect_system/spark_spread() + spark_system.set_up(5, 0, src) + spark_system.attach(src) + + add_language("Robot Talk", 1) + + wires = new(src) + + robot_modules_background = new() + robot_modules_background.icon_state = "block" + robot_modules_background.layer = HUD_LAYER //Objects that appear on screen are on layer 20, UI should be just below it. + robot_modules_background.plane = HUD_PLANE + + ident = rand(1, 999) + rename_character(null, get_default_name()) + update_icons() + update_headlamp() + + radio = new /obj/item/radio/borg(src) + common_radio = radio + + init() + + if(!scrambledcodes && !camera) + camera = new /obj/machinery/camera(src) + camera.c_tag = real_name + camera.network = list("SS13","Robots") + if(wires.IsCameraCut()) // 5 = BORG CAMERA + camera.status = 0 + + if(mmi == null) + mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) + mmi.icon_state = "boris" + + if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly + cell = new /obj/item/stock_parts/cell/high(src) + + initialize_components() + //if(!unfinished) + // Create all the robot parts. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.installed = 1 + C.wrapped = new C.external_type + + ..() + + add_robot_verbs() + + if(cell) + var/datum/robot_component/cell_component = components["power cell"] + cell_component.wrapped = cell + cell_component.installed = 1 + cell_component.install() + + diag_hud_set_borgcell() + scanner = new(src) + scanner.Grant(src) + +/mob/living/silicon/robot/proc/init(var/alien=0) + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + make_laws() + additional_law_channels["Binary"] = ":b " + var/new_ai = select_active_ai_with_fewest_borgs() + if(new_ai) + lawupdate = 1 + connect_to_ai(new_ai) + else + lawupdate = 0 + + playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) + +/mob/living/silicon/robot/rename_character(oldname, newname) + if(!..(oldname, newname)) + return 0 + + if(oldname != real_name) + notify_ai(3, oldname, newname) + custom_name = (newname != get_default_name()) ? newname : null + setup_PDA() + + //We also need to update name of internal camera. + if(camera) + camera.c_tag = newname + + //Check for custom sprite + if(!custom_sprite) + var/file = file2text("config/custom_sprites.txt") + var/lines = splittext(file, "\n") + + for(var/line in lines) + // split & clean up + var/list/Entry = splittext(line, ":") + for(var/i = 1 to Entry.len) + Entry[i] = trim(Entry[i]) + + if(Entry.len < 2 || Entry[1] != "cyborg") //ignore incorrectly formatted entries or entries that aren't marked for cyborg + continue + + if(Entry[2] == ckey) //They're in the list? Custom sprite time, var and icon change required + custom_sprite = 1 + + return 1 + + +/mob/living/silicon/robot/proc/get_default_name(var/prefix as text) + if(prefix) + modtype = prefix + if(mmi) + if(istype(mmi, /obj/item/mmi/robotic_brain)) + braintype = "Android" + else + braintype = "Cyborg" + else + braintype = "Robot" + + if(custom_name) + return custom_name + else + return "[modtype] [braintype]-[num2text(ident)]" + +/mob/living/silicon/robot/verb/Namepick() + set category = "Robot Commands" + if(custom_name) + return 0 + if(!allow_rename) + to_chat(src, "Rename functionality is not enabled on this unit."); + return 0 + rename_self(braintype, 1) + +/mob/living/silicon/robot/proc/sync() + if(lawupdate && connected_ai) + lawsync() + photosync() + +// setup the PDA and its name +/mob/living/silicon/robot/proc/setup_PDA() + if(!rbPDA) + rbPDA = new(src) + rbPDA.set_name_and_job(real_name, braintype) + var/datum/data/pda/app/messenger/M = rbPDA.find_program(/datum/data/pda/app/messenger) + if(M) + if(scrambledcodes) + M.hidden = 1 + if(pdahide) + M.toff = 1 + +/mob/living/silicon/robot/binarycheck() + if(is_component_functioning("comms")) + return 1 + return 0 + +//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO +//Improved /N +/mob/living/silicon/robot/Destroy() + if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside. + var/turf/T = get_turf(loc)//To hopefully prevent run time errors. + if(T) mmi.loc = T + if(mmi.brainmob) + mind.transfer_to(mmi.brainmob) + mmi.update_icon() + else + to_chat(src, "Oops! Something went very wrong, your MMI was unable to receive your mind. You have been ghosted. Please make a bug report so we can fix this bug.") + ghostize() + error("A borg has been destroyed, but its MMI lacked a brainmob, so the mind could not be transferred. Player: [ckey].") + mmi = null + if(connected_ai) + connected_ai.connected_robots -= src + QDEL_NULL(wires) + QDEL_NULL(module) + QDEL_NULL(camera) + QDEL_NULL(cell) + QDEL_NULL(robot_suit) + QDEL_NULL(spark_system) + return ..() + +/mob/living/silicon/robot/proc/pick_module() + if(module) + return + var/list/modules = list("Standard", "Engineering", "Medical", "Miner", "Janitor", "Service", "Security") + if(islist(force_modules) && force_modules.len) + modules = force_modules.Copy() + if(GLOB.security_level == (SEC_LEVEL_GAMMA || SEC_LEVEL_EPSILON) || crisis) + to_chat(src, "Crisis mode active. The combat module is now available.") + modules += "Combat" + if(mmi != null && mmi.alien) + modules = list("Hunter") + modtype = input("Please, select a module!", "Robot", null, null) as null|anything in modules + if(!modtype) + return + designation = modtype + var/module_sprites[0] //Used to store the associations between sprite names and sprite index. + + if(module) + return + + switch(modtype) + if("Standard") + module = new /obj/item/robot_module/standard(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "robot_old" + module_sprites["Android"] = "droid" + module_sprites["Default"] = "Standard" + module_sprites["Noble-STD"] = "Noble-STD" + + if("Service") + module = new /obj/item/robot_module/butler(src) + module.channels = list("Service" = 1) + module_sprites["Waitress"] = "Service" + module_sprites["Kent"] = "toiletbot" + module_sprites["Bro"] = "Brobot" + module_sprites["Rich"] = "maximillion" + module_sprites["Default"] = "Service2" + module_sprites["Standard"] = "Standard-Serv" + module_sprites["Noble-SRV"] = "Noble-SRV" + module_sprites["Cricket"] = "Cricket-SERV" + + if("Miner") + module = new /obj/item/robot_module/miner(src) + module.channels = list("Supply" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Mining Outpost") + module_sprites["Basic"] = "Miner_old" + module_sprites["Advanced Droid"] = "droid-miner" + module_sprites["Treadhead"] = "Miner" + module_sprites["Standard"] = "Standard-Mine" + module_sprites["Noble-DIG"] = "Noble-DIG" + module_sprites["Cricket"] = "Cricket-MINE" + + if("Medical") + module = new /obj/item/robot_module/medical(src) + module.channels = list("Medical" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Medical") + module_sprites["Basic"] = "Medbot" + module_sprites["Surgeon"] = "surgeon" + module_sprites["Advanced Droid"] = "droid-medical" + module_sprites["Needles"] = "medicalrobot" + module_sprites["Standard"] = "Standard-Medi" + module_sprites["Noble-MED"] = "Noble-MED" + module_sprites["Cricket"] = "Cricket-MEDI" + status_flags &= ~CANPUSH + + if("Security") + module = new /obj/item/robot_module/security(src) + module.channels = list("Security" = 1) + module_sprites["Basic"] = "secborg" + module_sprites["Red Knight"] = "Security" + module_sprites["Black Knight"] = "securityrobot" + module_sprites["Bloodhound"] = "bloodhound" + module_sprites["Standard"] = "Standard-Secy" + module_sprites["Noble-SEC"] = "Noble-SEC" + module_sprites["Cricket"] = "Cricket-SEC" + status_flags &= ~CANPUSH + + if("Engineering") + module = new /obj/item/robot_module/engineering(src) + module.channels = list("Engineering" = 1) + if(camera && "Robots" in camera.network) + camera.network.Add("Engineering") + module_sprites["Basic"] = "Engineering" + module_sprites["Antique"] = "engineerrobot" + module_sprites["Landmate"] = "landmate" + module_sprites["Standard"] = "Standard-Engi" + module_sprites["Noble-ENG"] = "Noble-ENG" + module_sprites["Cricket"] = "Cricket-ENGI" + magpulse = 1 + + if("Janitor") + module = new /obj/item/robot_module/janitor(src) + module.channels = list("Service" = 1) + module_sprites["Basic"] = "JanBot2" + module_sprites["Mopbot"] = "janitorrobot" + module_sprites["Mop Gear Rex"] = "mopgearrex" + module_sprites["Standard"] = "Standard-Jani" + module_sprites["Noble-CLN"] = "Noble-CLN" + module_sprites["Cricket"] = "Cricket-JANI" + + if("Combat") + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + icon_state = "droidcombat" + + if("Hunter") + module = new /obj/item/robot_module/alien/hunter(src) + icon_state = "xenoborg-state-a" + modtype = "Xeno-Hu" + feedback_inc("xeborg_hunter",1) + + + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + //Custom_sprite check and entry + if(custom_sprite && check_sprite("[ckey]-[modtype]")) + module_sprites["Custom"] = "[src.ckey]-[modtype]" + + hands.icon_state = lowertext(module.module_type) + feedback_inc("cyborg_[lowertext(modtype)]",1) + rename_character(real_name, get_default_name()) + + if(modtype == "Medical" || modtype == "Security" || modtype == "Combat") + status_flags &= ~CANPUSH + + choose_icon(6,module_sprites) + if(!static_radio_channels) + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/proc/reset_module() + notify_ai(2) + + uneq_all() + sight_mode = null + hands.icon_state = "nomod" + icon_state = "robot" + module.remove_subsystems_and_actions(src) + QDEL_NULL(module) + + camera.network.Remove(list("Engineering", "Medical", "Mining Outpost")) + rename_character(real_name, get_default_name("Default")) + languages = list() + speech_synthesizer_langs = list() + + update_icons() + update_headlamp() + + speed = 0 // Remove upgrades. + ionpulse = FALSE + magpulse = FALSE + add_language("Robot Talk", 1) + + status_flags |= CANPUSH + +//for borg hotkeys, here module refers to borg inv slot, not core module +/mob/living/silicon/robot/verb/cmd_toggle_module(module as num) + set name = "Toggle Module" + set hidden = 1 + toggle_module(module) + +/mob/living/silicon/robot/verb/cmd_unequip_module() + set name = "Unequip Module" + set hidden = 1 + uneq_active() + +// this verb lets cyborgs see the stations manifest +/mob/living/silicon/robot/verb/cmd_station_manifest() + set category = "Robot Commands" + set name = "Show Station Manifest" + show_station_manifest() + +/mob/living/silicon/robot/proc/self_diagnosis() + if(!is_component_functioning("diagnosis unit")) + return null + + var/dat = "[src.name] Self-Diagnosis Report\n" + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + dat += "[C.name]
    MISSING
    " + else + dat += "[C.name][C.installed == -1 ? "
    DESTROYED" : ""]
    Brute Damage:[C.brute_damage]
    Electronics Damage:[C.electronics_damage]
    Powered:[C.is_powered() ? "Yes" : "No"]
    Toggled:[ C.toggled ? "Yes" : "No"]

    " + return dat + +/mob/living/silicon/robot/verb/self_diagnosis_verb() + set category = "Robot Commands" + set name = "Self Diagnosis" + + if(!is_component_functioning("diagnosis unit")) + to_chat(src, "Your self-diagnosis component isn't functioning.") + + var/dat = self_diagnosis() + src << browse(dat, "window=robotdiagnosis") + + +/mob/living/silicon/robot/verb/toggle_component() + set category = "Robot Commands" + set name = "Toggle Component" + set desc = "Toggle a component, conserving power." + + var/list/installed_components = list() + for(var/V in components) + if(V == "power cell") continue + var/datum/robot_component/C = components[V] + if(C.installed) + installed_components += V + + var/toggle = input(src, "Which component do you want to toggle?", "Toggle Component") as null|anything in installed_components + if(!toggle) + return + + var/datum/robot_component/C = components[toggle] + C.toggle() + to_chat(src, "You [C.toggled ? "enable" : "disable"] [C.name].") + +/mob/living/silicon/robot/proc/sensor_mode() + set name = "Set Sensor Augmentation" + set desc = "Augment visual feed with internal sensor overlays." + set category = "Robot Commands" + toggle_sensor_mode() + +/mob/living/silicon/robot/proc/add_robot_verbs() + src.verbs |= GLOB.robot_verbs_default + src.verbs |= silicon_subsystems + +/mob/living/silicon/robot/proc/remove_robot_verbs() + src.verbs -= GLOB.robot_verbs_default + src.verbs -= silicon_subsystems + +/mob/living/silicon/robot/proc/ionpulse() + if(!ionpulse_on) + return + + if(cell.charge <= 50) + toggle_ionpulse() + return + + cell.charge -= 25 // 500 steps on a default cell. + return 1 + +/mob/living/silicon/robot/proc/toggle_ionpulse() + if(!ionpulse) + to_chat(src, "No thrusters are installed!") + return + + if(!ion_trail) + ion_trail = new + ion_trail.set_up(src) + + ionpulse_on = !ionpulse_on + to_chat(src, "You [ionpulse_on ? null :"de"]activate your ion thrusters.") + if(ionpulse_on) + ion_trail.start() + else + ion_trail.stop() + if(thruster_button) + thruster_button.icon_state = "ionpulse[ionpulse_on]" + +/mob/living/silicon/robot/blob_act(obj/structure/blob/B) + if(stat != DEAD) + adjustBruteLoss(30) + else + gib() + return TRUE + +// this function displays the cyborgs current cell charge in the stat panel +/mob/living/silicon/robot/proc/show_cell_power() + if(cell) + stat(null, text("Charge Left: [cell.charge]/[cell.maxcharge]")) + else + stat(null, text("No Cell Inserted!")) + + +// update the status screen display +/mob/living/silicon/robot/Stat() + ..() + statpanel("Status") + if(client.statpanel == "Status") + show_cell_power() + var/total_user_contents = GetAllContents() + if(locate(/obj/item/gps/cyborg) in total_user_contents) + var/turf/T = get_turf(src) + stat(null, "GPS: [COORD(T)]") + +/mob/living/silicon/robot/restrained() + return 0 + +/mob/living/silicon/robot/InCritical() + return low_power_mode + +/mob/living/silicon/robot/ex_act(severity) + switch(severity) + if(1.0) + gib() + return + if(2.0) + if(stat != 2) + adjustBruteLoss(60) + adjustFireLoss(60) + if(3.0) + if(stat != 2) + adjustBruteLoss(30) + return + + +/mob/living/silicon/robot/bullet_act(var/obj/item/projectile/Proj) + ..(Proj) + if(prob(75) && Proj.damage > 0) spark_system.start() + return 2 + + +/mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) + // Check if the user is trying to insert another component like a radio, actuator, armor etc. + if(istype(W, /obj/item/robot_parts/robot_component) && opened) + for(var/V in components) + var/datum/robot_component/C = components[V] + if(!C.installed && istype(W, C.external_type)) + C.installed = 1 + C.wrapped = W + C.install() + user.drop_item() + W.loc = null + + var/obj/item/robot_parts/robot_component/WC = W + if(istype(WC)) + C.brute_damage = WC.brute + C.electronics_damage = WC.burn + + to_chat(usr, "You install the [W.name].") + + return + + if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) + user.changeNext_move(CLICK_CD_MELEE) + if(!getFireLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getFireLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + var/obj/item/stack/cable_coil/coil = W + adjustFireLoss(-30) + updatehealth() + add_fingerprint(user) + coil.use(1) + user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") + + else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside + var/datum/robot_component/cell/C = components["power cell"] + if(wiresexposed) + to_chat(user, "Close the panel first.") + else if(cell) + to_chat(user, "There is a power cell already installed.") + else + user.drop_item() + W.loc = src + cell = W + to_chat(user, "You insert the power cell.") + + C.installed = 1 + C.wrapped = W + C.install() + C.external_type = W.type // Update the cell component's `external_type` to the path of new cell + //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z + C.brute_damage = 0 + C.electronics_damage = 0 + diag_hud_set_borgcell() + + else if(istype(W, /obj/item/encryptionkey/) && opened) + if(radio)//sanityyyyyy + radio.attackby(W,user)//GTFO, you have your own procs + else + to_chat(user, "Unable to locate a radio.") + + else if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) // trying to unlock the interface with an ID card + if(emagged)//still allow them to open the cover + to_chat(user, "The interface seems slightly damaged.") + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else + if(allowed(W)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] [src]'s interface.") + update_icons() + else + to_chat(user, "Access denied.") + + else if(istype(W, /obj/item/borg/upgrade/)) + var/obj/item/borg/upgrade/U = W + if(!opened) + to_chat(user, "You must access the borg's internals!") + else if(!src.module && U.require_module) + to_chat(user, "The borg must choose a module before it can be upgraded!") + else if(U.locked) + to_chat(user, "The upgrade is locked and cannot be used yet!") + else + if(!user.drop_item()) + return + if(U.action(src)) + to_chat(user, "You apply the upgrade to [src].") + U.forceMove(src) + else + to_chat(user, "Upgrade error.") + + else if(istype(W, /obj/item/mmi_radio_upgrade)) + if(!opened) + to_chat(user, "You must access the borg's internals!") + return + else if(!mmi) + to_chat(user, "This cyborg does not have an MMI to augment!") + return + else if(mmi.radio) + to_chat(user, "A radio upgrade is already installed in the MMI!") + return + else if(user.drop_item()) + to_chat(user, "You apply the upgrade to [src].") + to_chat(src, "MMI radio capability installed.") + mmi.install_radio() + qdel(W) + else + return ..() + +/mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(wiresexposed) + wires.Interact(user) + +/mob/living/silicon/robot/multitool_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(wiresexposed) + wires.Interact(user) + +/mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(!cell) // haxing + wiresexposed = !wiresexposed + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") + update_icons() + I.play_tool_sound(user, I.tool_volume) + else //radio check + if(radio) + radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything + else + to_chat(user, "Unable to locate a radio.") + update_icons() + +/mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(!opened) + if(locked) + to_chat(user, "The cover is locked and cannot be opened.") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You open the cover.") + opened = TRUE + update_icons() + return + else if(cell) + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You close the cover.") + opened = FALSE + update_icons() + return + else if(wiresexposed && wires.IsAllCut()) + //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. + if(!mmi) + to_chat(user, "[src] has no brain to remove.") + return + to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...") + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") + deconstruct() + return + // Okay we're not removing the cell or an MMI, but maybe something else? + var/list/removable_components = list() + for(var/V in components) + if(V == "power cell") + continue + var/datum/robot_component/C = components[V] + if(C.installed == 1 || C.installed == -1) + removable_components += V + if(module) + removable_components += module.custom_removals + var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components + if(!remove) + return + if(module && module.handle_custom_removal(remove, user, I)) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/datum/robot_component/C = components[remove] + var/obj/item/robot_parts/robot_component/thing = C.wrapped + to_chat(user, "You remove \the [thing].") + if(istype(thing)) + thing.brute = C.brute_damage + thing.burn = C.electronics_damage + + thing.loc = loc + var/was_installed = C.installed + C.installed = 0 + if(was_installed == 1) + C.uninstall() + + + + +/mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) + if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. + spark_system.start() + ..() + +/mob/living/silicon/robot/emag_act(user as mob) + if(!ishuman(user) && !issilicon(user)) + return + var/mob/living/M = user + if(!opened)//Cover is closed + if(!is_emaggable) + to_chat(user, "The emag sparks, and flashes red. This mechanism does not appear to be emaggable.") + else if(locked) + to_chat(user, "You emag the cover lock.") + locked = 0 + else + to_chat(user, "The cover is already unlocked.") + return + + if(opened)//Cover is open + if(emagged) return//Prevents the X has hit Y with Z message also you cant emag them twice + if(wiresexposed) + to_chat(user, "You must close the panel first") + return + else + sleep(6) + emagged = 1 + SetLockdown(1) //Borgs were getting into trouble because they would attack the emagger before the new laws were shown + if(src.hud_used) + src.hud_used.update_robot_modules_display() //Shows/hides the emag item if the inventory screen is already open. + disconnect_from_ai() + to_chat(user, "You emag [src]'s interface.") +// message_admins("[key_name_admin(user)] emagged cyborg [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged cyborg [key_name(src)]. Laws overridden.") + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + var/time = time2text(world.realtime,"hh:mm:ss") + GLOB.lawchanges.Add("[time] : [M.name]([M.key]) emagged [name]([key])") + set_zeroth_law("Only [M.real_name] and people [M.p_they()] designate[M.p_s()] as being such are Syndicate Agents.") + to_chat(src, "ALERT: Foreign software detected.") + sleep(5) + to_chat(src, "Initiating diagnostics...") + sleep(20) + to_chat(src, "SynBorg v1.7 loaded.") + sleep(5) + to_chat(src, "LAW SYNCHRONISATION ERROR") + sleep(5) + to_chat(src, "Would you like to send a report to NanoTraSoft? Y/N") + sleep(10) + to_chat(src, "> N") + sleep(20) + to_chat(src, "ERRORERRORERROR") + to_chat(src, "Obey these laws:") + laws.show_laws(src) + to_chat(src, "ALERT: [M.real_name] is your new master. Obey your new laws and [M.p_their()] commands.") + SetLockdown(0) + if(src.module && istype(src.module, /obj/item/robot_module/miner)) + for(var/obj/item/pickaxe/drill/cyborg/D in src.module.modules) + qdel(D) + src.module.modules += new /obj/item/pickaxe/drill/cyborg/diamond(src.module) + src.module.rebuild() + if(src.module && istype(src.module, /obj/item/robot_module/medical)) + for(var/obj/item/borg_defib/F in src.module.modules) + F.safety = 0 + if(module) + module.module_type = "Malf" // For the cool factor + update_module_icon() + update_icons() + return + +/mob/living/silicon/robot/verb/unlock_own_cover() + set category = "Robot Commands" + set name = "Unlock Cover" + set desc = "Unlocks your own cover if it is locked. You can not lock it again. A human will have to lock it for you." + if(locked) + switch(alert("You can not lock your cover again, are you sure?\n (You can still ask for a human to lock it)", "Unlock Own Cover", "Yes", "No")) + if("Yes") + locked = 0 + update_icons() + to_chat(usr, "You unlock your cover.") + +/mob/living/silicon/robot/attack_ghost(mob/user) + if(wiresexposed) + wires.Interact(user) + else + ..() //this calls the /mob/living/attack_ghost proc for the ghost health/cyborg analyzer + +/mob/living/silicon/robot/proc/allowed(obj/item/I) + var/obj/dummy = new /obj(null) // Create a dummy object to check access on as to avoid having to snowflake check_access on every mob + dummy.req_access = req_access + dummy.req_one_access = req_one_access + + if(dummy.check_access(I)) + qdel(dummy) + return 1 + + qdel(dummy) + return 0 + +/mob/living/silicon/robot/update_icons() + + overlays.Cut() + if(stat != DEAD && !(paralysis || stunned || IsWeakened() || low_power_mode)) //Not dead, not stunned. + if(custom_panel in custom_eye_names) + overlays += "eyes-[custom_panel]" + else + overlays += "eyes-[icon_state]" + else + overlays -= "eyes" + + if(opened) + var/panelprefix = "ov" + if(custom_sprite) //Custom borgs also have custom panels, heh + panelprefix = "[ckey]" + + if(custom_panel in custom_panel_names) //For default borgs with different panels + panelprefix = custom_panel + + if(wiresexposed) + overlays += "[panelprefix]-openpanel +w" + else if(cell) + overlays += "[panelprefix]-openpanel +c" + else + overlays += "[panelprefix]-openpanel -c" + + var/combat = list("Combat") + if(modtype in combat) + if(base_icon == "") + base_icon = icon_state + if(module_active && istype(module_active,/obj/item/borg/combat/mobility)) + icon_state = "[base_icon]-roll" + else + icon_state = base_icon + if(module) + for(var/obj/item/borg/combat/shield/S in module.modules) + if(activated(S)) + overlays += "[base_icon]-shield" + update_fire() + +/mob/living/silicon/robot/proc/installed_modules() + if(weapon_lock) + to_chat(src, "Weapon lock active, unable to use modules! Count:[weaponlock_time]") + return + + if(!module) + pick_module() + return + var/dat = {"Close +
    +
    + Activated Modules +
    + + + + +
    Module 1:[module_state_1 ? "[module_state_1]" : "No Module"]
    Module 2:[module_state_2 ? "[module_state_2]" : "No Module"]
    Module 3:[module_state_3 ? "[module_state_3]" : "No Module"]

    + Installed Modules

    + + "} + for(var/obj in module.modules) + if(!obj) + dat += text("") + else if(activated(obj)) + dat += text("") + else + dat += text("") + if(emagged || weapons_unlock) + if(activated(module.emag)) + dat += text("") + else + dat += text("") + dat += "
    Resource depleted
    [obj]Activated
    [obj]Activate
    [module.emag]Activated
    [module.emag]Activate
    " +/* + if(activated(obj)) + dat += text("[obj]: \[Activated | Deactivate\]
    ") + else + dat += text("[obj]: \[Activate | Deactivated\]
    ") +*/ + var/datum/browser/popup = new(src, "robotmod", "Modules") + popup.set_content(dat) + popup.open() + + +/mob/living/silicon/robot/Topic(href, href_list) + if(..()) + return 1 + + if(usr != src) + return 1 + + if(href_list["mach_close"]) + var/t1 = text("window=[href_list["mach_close"]]") + unset_machine() + src << browse(null, t1) + return 1 + + if(href_list["showalerts"]) + subsystem_alarm_monitor() + return 1 + + if(href_list["mod"]) + var/obj/item/O = locate(href_list["mod"]) + if(istype(O) && (O.loc == src)) + O.attack_self(src) + return 1 + + if(href_list["act"]) + var/obj/item/O = locate(href_list["act"]) + if(!istype(O) || !(O.loc == src || O.loc == src.module)) + return 1 + + activate_module(O) + installed_modules() + + if(href_list["deact"]) + var/obj/item/O = locate(href_list["deact"]) + if(activated(O)) + if(module_state_1 == O) + module_state_1 = null + contents -= O + else if(module_state_2 == O) + module_state_2 = null + contents -= O + else if(module_state_3 == O) + module_state_3 = null + contents -= O + else + to_chat(src, "Module isn't activated.") + else + to_chat(src, "Module isn't activated") + installed_modules() + return 1 + + return 1 + +/mob/living/silicon/robot/proc/radio_menu() + radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code + +/mob/living/silicon/robot/proc/control_headlamp() + if(stat || lamp_recharging || low_power_mode) + to_chat(src, "This function is currently offline.") + return + +//Some sort of magical "modulo" thing which somehow increments lamp power by 2, until it hits the max and resets to 0. + lamp_intensity = (lamp_intensity+2) % (lamp_max+2) + to_chat(src, "[lamp_intensity ? "Headlamp power set to Level [lamp_intensity/2]" : "Headlamp disabled."]") + update_headlamp() + +/mob/living/silicon/robot/proc/update_headlamp(var/turn_off = 0, var/cooldown = 100) + set_light(0) + + if(lamp_intensity && (turn_off || stat || low_power_mode)) + to_chat(src, "Your headlamp has been deactivated.") + lamp_intensity = 0 + lamp_recharging = 1 + spawn(cooldown) //10 seconds by default, if the source of the deactivation does not keep stat that long. + lamp_recharging = 0 + else + set_light(light_range + lamp_intensity) + + if(lamp_button) + lamp_button.icon_state = "lamp[lamp_intensity]" + + update_icons() + +/mob/living/silicon/robot/proc/deconstruct() + var/turf/T = get_turf(src) + if(robot_suit) + robot_suit.forceMove(T) + robot_suit.l_leg.forceMove(T) + robot_suit.l_leg = null + robot_suit.r_leg.forceMove(T) + robot_suit.r_leg = null + new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) + robot_suit.chest.forceMove(T) + robot_suit.chest.wired = FALSE + robot_suit.chest = null + robot_suit.l_arm.forceMove(T) + robot_suit.l_arm = null + robot_suit.r_arm.forceMove(T) + robot_suit.r_arm = null + robot_suit.head.forceMove(T) + robot_suit.head.flash1.forceMove(T) + robot_suit.head.flash1.burn_out() + robot_suit.head.flash1 = null + robot_suit.head.flash2.forceMove(T) + robot_suit.head.flash2.burn_out() + robot_suit.head.flash2 = null + robot_suit.head = null + robot_suit.updateicon() + else + new /obj/item/robot_parts/robot_suit(T) + new /obj/item/robot_parts/l_leg(T) + new /obj/item/robot_parts/r_leg(T) + new /obj/item/stack/cable_coil(T, 1) + new /obj/item/robot_parts/chest(T) + new /obj/item/robot_parts/l_arm(T) + new /obj/item/robot_parts/r_arm(T) + new /obj/item/robot_parts/head(T) + var/b + for(b=0, b!=2, b++) + var/obj/item/flash/F = new /obj/item/flash(T) + F.burn_out() + if(cell) //Sanity check. + cell.forceMove(T) + cell = null + qdel(src) + +#define BORG_CAMERA_BUFFER 30 +/mob/living/silicon/robot/Move(a, b, flag) + var/oldLoc = src.loc + . = ..() + if(.) + if(src.camera) + if(!updating) + updating = 1 + spawn(BORG_CAMERA_BUFFER) + if(oldLoc != src.loc) + GLOB.cameranet.updatePortableCamera(src.camera) + updating = 0 + if(module) + if(module.type == /obj/item/robot_module/janitor) + var/turf/tile = loc + if(isturf(tile)) + var/floor_only = TRUE + for(var/A in tile) + if(istype(A, /obj/effect)) + if(is_cleanable(A)) + var/obj/effect/decal/cleanable/blood/B = A + if(istype(B) && B.off_floor) + floor_only = FALSE + else + qdel(A) + else if(istype(A, /obj/item)) + var/obj/item/cleaned_item = A + cleaned_item.clean_blood() + else if(istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/cleaned_human = A + if(cleaned_human.lying) + if(cleaned_human.head) + cleaned_human.head.clean_blood() + cleaned_human.update_inv_head(0,0) + if(cleaned_human.wear_suit) + cleaned_human.wear_suit.clean_blood() + cleaned_human.update_inv_wear_suit(0,0) + else if(cleaned_human.w_uniform) + cleaned_human.w_uniform.clean_blood() + cleaned_human.update_inv_w_uniform(0,0) + if(cleaned_human.shoes) + cleaned_human.shoes.clean_blood() + cleaned_human.update_inv_shoes(0,0) + cleaned_human.clean_blood() + to_chat(cleaned_human, "[src] cleans your face!") + if(floor_only) + tile.clean_blood() + return +#undef BORG_CAMERA_BUFFER + +/mob/living/silicon/robot/proc/self_destruct() + if(emagged) + if(mmi) + qdel(mmi) + explosion(src.loc,1,2,4,flame_range = 2) + else + explosion(src.loc,-1,0,2) + gib() + return + +/mob/living/silicon/robot/proc/UnlinkSelf() + disconnect_from_ai() + lawupdate = 0 + lockcharge = 0 + canmove = 1 + scrambledcodes = 1 + //Disconnect it's camera so it's not so easily tracked. + QDEL_NULL(src.camera) + // I'm trying to get the Cyborg to not be listed in the camera list + // Instead of being listed as "deactivated". The downside is that I'm going + // to have to check if every camera is null or not before doing anything, to prevent runtime errors. + // I could change the network to null but I don't know what would happen, and it seems too hacky for me. + +/mob/living/silicon/robot/proc/ResetSecurityCodes() + set category = "Robot Commands" + set name = "Reset Identity Codes" + set desc = "Scrambles your security and identification codes and resets your current buffers. Unlocks you and but permanently severs you from your AI and the robotics console and will deactivate your camera system." + + var/mob/living/silicon/robot/R = src + + if(R) + R.UnlinkSelf() + to_chat(R, "Buffers flushed and reset. Camera system shutdown. All systems operational.") + src.verbs -= /mob/living/silicon/robot/proc/ResetSecurityCodes + +/mob/living/silicon/robot/mode() + set name = "Activate Held Object" + set category = "IC" + set src = usr + + var/obj/item/W = get_active_hand() + if(W) + W.attack_self(src) + + return + +/mob/living/silicon/robot/proc/SetLockdown(var/state = 1) + // They stay locked down if their wire is cut. + if(wires.LockedCut()) + state = 1 + if(state) + throw_alert("locked", /obj/screen/alert/locked) + else + clear_alert("locked") + lockcharge = state + update_canmove() + +/mob/living/silicon/robot/proc/choose_icon(var/triesleft, var/list/module_sprites) + + if(triesleft<1 || !module_sprites.len) + return + else + triesleft-- + + var/icontype + lockcharge = 1 //Locks borg until it select an icon to avoid secborgs running around with a standard sprite + icontype = input("Select an icon! [triesleft ? "You have [triesleft] more chances." : "This is your last try."]", "Robot", null, null) in module_sprites + + if(icontype) + if(icontype == "Custom") + icon = 'icons/mob/custom_synthetic/custom-synthetic.dmi' + else + icon = 'icons/mob/robots.dmi' + icon_state = module_sprites[icontype] + if(icontype == "Bro") + module.module_type = "Brobot" + update_module_icon() + lockcharge = null + var/list/names = splittext(icontype, "-") + custom_panel = trim(names[1]) + else + to_chat(src, "Something is badly wrong with the sprite selection. Harass a coder.") + icon_state = module_sprites[1] + lockcharge = null + return + + update_icons() + + if(triesleft >= 1) + var/choice = input("Look at your icon - is this what you want?") in list("Yes","No") + if(choice=="No") + choose_icon(triesleft, module_sprites) + return + else + triesleft = 0 + return + else + to_chat(src, "Your icon has been set. You now require a module reset to change it.") + +/mob/living/silicon/robot/proc/notify_ai(var/notifytype, var/oldname, var/newname) + if(!connected_ai) + return + switch(notifytype) + if(1) //New Cyborg + to_chat(connected_ai, "

    NOTICE - New cyborg connection detected: [name]
    ") + if(2) //New Module + to_chat(connected_ai, "

    NOTICE - Cyborg module change detected: [name] has loaded the [designation] module.
    ") + if(3) //New Name + to_chat(connected_ai, "

    NOTICE - Cyborg reclassification detected: [oldname] is now designated as [newname].
    ") + +/mob/living/silicon/robot/proc/disconnect_from_ai() + if(connected_ai) + sync() // One last sync attempt + connected_ai.connected_robots -= src + connected_ai = null + +/mob/living/silicon/robot/proc/connect_to_ai(var/mob/living/silicon/ai/AI) + if(AI && AI != connected_ai) + disconnect_from_ai() + connected_ai = AI + connected_ai.connected_robots |= src + notify_ai(1) + sync() + +/mob/living/silicon/robot/adjustOxyLoss(var/amount) + if(suiciding) + return ..() + else + return STATUS_UPDATE_NONE + +/mob/living/silicon/robot/regenerate_icons() + ..() + update_module_icon() + +/mob/living/silicon/robot/deathsquad + base_icon = "nano_bloodhound" + icon_state = "nano_bloodhound" + designation = "SpecOps" + lawupdate = 0 + scrambledcodes = 1 + req_one_access = list(ACCESS_CENT_SPECOPS) + ionpulse = 1 + magpulse = 1 + pdahide = 1 + eye_protection = 2 // Immunity to flashes and the visual part of flashbangs + ear_protection = 1 // Immunity to the audio part of flashbangs + allow_rename = FALSE + modtype = "Commando" + faction = list("nanotrasen") + is_emaggable = FALSE + +/mob/living/silicon/robot/deathsquad/New(loc) + ..() + cell = new /obj/item/stock_parts/cell/hyper(src) + +/mob/living/silicon/robot/deathsquad/init() + laws = new /datum/ai_laws/deathsquad + module = new /obj/item/robot_module/deathsquad(src) + + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + radio = new /obj/item/radio/borg/deathsquad(src) + radio.recalculateChannels() + + playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0) + +/mob/living/silicon/robot/combat + base_icon = "droidcombat" + icon_state = "droidcombat" + modtype = "Combat" + designation = "Combat" + +/mob/living/silicon/robot/combat/init() + ..() + module = new /obj/item/robot_module/combat(src) + module.channels = list("Security" = 1) + //languages + module.add_languages(src) + //subsystems + module.add_subsystems_and_actions(src) + + status_flags &= ~CANPUSH + + radio.config(module.channels) + notify_ai(2) + +/mob/living/silicon/robot/ert + designation = "ERT" + lawupdate = 0 + scrambledcodes = 1 + req_one_access = list(ACCESS_CENT_SPECOPS) + ionpulse = 1 + + force_modules = list("Engineering", "Medical", "Security") + static_radio_channels = 1 + allow_rename = FALSE + weapons_unlock = TRUE + + +/mob/living/silicon/robot/ert/init() + laws = new /datum/ai_laws/ert_override + radio = new /obj/item/radio/borg/ert(src) + radio.recalculateChannels() + aiCamera = new/obj/item/camera/siliconcam/robot_camera(src) + +/mob/living/silicon/robot/ert/New(loc, cyborg_unlock) + ..(loc) + cell = new /obj/item/stock_parts/cell/hyper(src) + var/rnum = rand(1,1000) + var/borgname = "ERT [rnum]" + name = borgname + custom_name = borgname + real_name = name + mind = new + mind.current = src + mind.original = src + mind.assigned_role = SPECIAL_ROLE_ERT + mind.special_role = SPECIAL_ROLE_ERT + if(cyborg_unlock) + crisis = 1 + if(!(mind in SSticker.minds)) + SSticker.minds += mind + SSticker.mode.ert += mind + +/mob/living/silicon/robot/ert/gamma + crisis = 1 + +/mob/living/silicon/robot/emp_act(severity) + ..() + switch(severity) + if(1) + disable_component("comms", 160) + if(2) + disable_component("comms", 60) + +/mob/living/silicon/robot/extinguish_light() + update_headlamp(1, 150) + +/mob/living/silicon/robot/rejuvenate() + ..() + var/brute = 1000 + var/burn = 1000 + var/list/datum/robot_component/borked_parts = get_damaged_components(TRUE, TRUE, TRUE, TRUE) + for(var/datum/robot_component/borked_part in borked_parts) + brute = borked_part.brute_damage + burn = borked_part.electronics_damage + borked_part.installed = 1 + borked_part.wrapped = new borked_part.external_type + if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? + cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. + borked_part.heal_damage(brute,burn) + borked_part.install() + +/mob/living/silicon/robot/proc/check_sprite(spritename) + . = FALSE + + var/static/all_borg_icon_states = icon_states('icons/mob/custom_synthetic/custom-synthetic.dmi') + if(spritename in all_borg_icon_states) + . = TRUE + +/mob/living/silicon/robot/check_eye_prot() + return eye_protection + +/mob/living/silicon/robot/check_ear_prot() + return ear_protection + +/mob/living/silicon/robot/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + lighting_alpha = initial(lighting_alpha) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + if(sight_mode & BORGMESON) + sight |= SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + if(sight_mode & BORGXRAY) + sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + see_invisible = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + see_in_dark = 8 + + if(sight_mode & BORGTHERM) + sight |= SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 81700464ac0b..c73d566b7297 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,179 +1,179 @@ -/mob/living/silicon/robot/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = maxHealth - stat = CONSCIOUS - return - health = maxHealth - (getOxyLoss() + getFireLoss() + getBruteLoss()) - update_stat("updatehealth([reason])") - handle_hud_icons_health() - diag_hud_set_health() - -/mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones - amount += C.brute_damage - return amount - -/mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones - amount += C.electronics_damage - return amount - -/mob/living/silicon/robot/adjustBruteLoss(amount, updating_health = TRUE) - if(amount > 0) - take_overall_damage(amount, 0, updating_health) - else - heal_overall_damage(-amount, 0, updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/silicon/robot/adjustFireLoss(amount, updating_health = TRUE) - if(amount > 0) - take_overall_damage(0, amount, updating_health) - else - heal_overall_damage(0, -amount, updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_missing_components() - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_damageable_components() - var/list/rval = new - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1) - rval += C - return rval - -/mob/living/silicon/robot/proc/get_armour() - if(!LAZYLEN(components)) - return 0 - var/datum/robot_component/C = components["armour"] - if(C && C.installed == 1) - return C - return 0 - -/mob/living/silicon/robot/heal_organ_damage(brute, burn, updating_health = TRUE) - var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - if(!LAZYLEN(parts)) - return - var/datum/robot_component/picked = pick(parts) - picked.heal_damage(brute, burn, updating_health) - -/mob/living/silicon/robot/take_organ_damage(brute = 0, burn = 0, updating_health = TRUE, sharp = 0, edge = 0) - var/list/components = get_damageable_components() - if(!LAZYLEN(components)) - return - - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute, burn, sharp, updating_health) - return - - var/datum/robot_component/C = pick(components) - C.take_damage(brute, burn, sharp, updating_health) - -/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn, updating_health = TRUE) - var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - - while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.heal_damage(brute,burn, updating_health) - - brute -= (brute_was - picked.brute_damage) - burn -= (burn_was - picked.electronics_damage) - - parts -= picked - - if(updating_health) - updatehealth("heal overall damage") - -/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) - if(status_flags & GODMODE) return //godmode - var/list/datum/robot_component/parts = get_damageable_components() - - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute, burn, sharp) - return - - while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.take_damage(brute, burn, sharp, FALSE) - - brute -= (picked.brute_damage - brute_was) - burn -= (picked.electronics_damage - burn_was) - - parts -= picked - updatehealth() +/mob/living/silicon/robot/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = maxHealth + stat = CONSCIOUS + return + health = maxHealth - (getOxyLoss() + getFireLoss() + getBruteLoss()) + update_stat("updatehealth([reason])") + handle_hud_icons_health() + diag_hud_set_health() + +/mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones + amount += C.brute_damage + return amount + +/mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones + amount += C.electronics_damage + return amount + +/mob/living/silicon/robot/adjustBruteLoss(amount, updating_health = TRUE) + if(amount > 0) + take_overall_damage(amount, 0, updating_health) + else + heal_overall_damage(-amount, 0, updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/silicon/robot/adjustFireLoss(amount, updating_health = TRUE) + if(amount > 0) + take_overall_damage(0, amount, updating_health) + else + heal_overall_damage(0, -amount, updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_missing_components() + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_damageable_components() + var/list/rval = new + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1) + rval += C + return rval + +/mob/living/silicon/robot/proc/get_armour() + if(!LAZYLEN(components)) + return 0 + var/datum/robot_component/C = components["armour"] + if(C && C.installed == 1) + return C + return 0 + +/mob/living/silicon/robot/heal_organ_damage(brute, burn, updating_health = TRUE) + var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + if(!LAZYLEN(parts)) + return + var/datum/robot_component/picked = pick(parts) + picked.heal_damage(brute, burn, updating_health) + +/mob/living/silicon/robot/take_organ_damage(brute = 0, burn = 0, updating_health = TRUE, sharp = 0, edge = 0) + var/list/components = get_damageable_components() + if(!LAZYLEN(components)) + return + + //Combat shielding absorbs a percentage of damage directly into the cell. + var/obj/item/borg/combat/shield/shield + if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) + shield = module_state_1 + else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) + shield = module_state_2 + else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) + shield = module_state_3 + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute * shield.shield_level + var/absorb_burn = burn * shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 100 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "Your shield has overloaded!") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "Your shield absorbs some of the impact!") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute, burn, sharp, updating_health) + return + + var/datum/robot_component/C = pick(components) + C.take_damage(brute, burn, sharp, updating_health) + +/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn, updating_health = TRUE) + var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + + while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.heal_damage(brute,burn, updating_health) + + brute -= (brute_was - picked.brute_damage) + burn -= (burn_was - picked.electronics_damage) + + parts -= picked + + if(updating_health) + updatehealth("heal overall damage") + +/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) + if(status_flags & GODMODE) return //godmode + var/list/datum/robot_component/parts = get_damageable_components() + + //Combat shielding absorbs a percentage of damage directly into the cell. + var/obj/item/borg/combat/shield/shield + if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) + shield = module_state_1 + else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) + shield = module_state_2 + else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) + shield = module_state_3 + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute * shield.shield_level + var/absorb_burn = burn * shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 100 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "Your shield has overloaded!") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "Your shield absorbs some of the impact!") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute, burn, sharp) + return + + while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.take_damage(brute, burn, sharp, FALSE) + + brute -= (picked.brute_damage - brute_was) + burn -= (picked.electronics_damage - burn_was) + + parts -= picked + updatehealth() diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 083e379e4441..92115986b2ec 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -95,4 +95,4 @@ desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/decals.dmi' icon_state = "shock" - powerneeded = 25 \ No newline at end of file + powerneeded = 25 diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 8b4005beff89..47dcb93cef59 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -1,590 +1,590 @@ -/obj/item/robot_module - name = "robot module" - icon = 'icons/obj/module.dmi' - icon_state = "std_module" - w_class = 100 - item_state = "electronic" - flags = CONDUCT - - var/list/modules = list() - var/obj/item/emag = null - var/list/subsystems = list() - var/list/module_actions = list() - - var/module_type = "NoMod" // For icon usage - - var/list/stacktypes - var/channels = list() - var/list/custom_removals = list() - - -/obj/item/robot_module/emp_act(severity) - if(modules) - for(var/obj/O in modules) - O.emp_act(severity) - if(emag) - emag.emp_act(severity) - ..() - - -/obj/item/robot_module/New() - ..() - add_default_robot_items() - emag = new /obj/item/toy/sword(src) - emag.name = "Placeholder Emag Item" - -/obj/item/robot_module/Destroy() - QDEL_LIST(modules) - QDEL_NULL(emag) - return ..() - -// By default, all robots will get the items in this proc, unless you override it for your specific module. See: ../robot_module/drone -/obj/item/robot_module/proc/add_default_robot_items() - modules += new /obj/item/flash/cyborg(src) - -/obj/item/robot_module/proc/fix_modules() - for(var/obj/item/I in modules) - I.flags |= NODROP - I.mouse_opacity = MOUSE_OPACITY_OPAQUE - if(emag) - emag.flags |= NODROP - emag.mouse_opacity = MOUSE_OPACITY_OPAQUE - -/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) - if(!stacktypes || !stacktypes.len) - return - - var/stack_respawned = 0 - for(var/T in stacktypes) - var/O = locate(T) in modules - var/obj/item/stack/S = O - - if(!S) - S = new T(src) - modules += S - S.amount = 1 - stack_respawned = 1 - - if(S && S.amount < stacktypes[T]) - S.amount++ - if(stack_respawned && istype(R) && R.hud_used) - R.hud_used.update_robot_modules_display() - -/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module - var/list/temp_list = modules - modules = list() - for(var/obj/O in temp_list) - if(O) - modules += O - -/obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) - //full set of languages - R.add_language("Galactic Common", 1) - R.add_language("Sol Common", 1) - R.add_language("Tradeband", 1) - R.add_language("Gutter", 0) - R.add_language("Neo-Russkiya", 0) - R.add_language("Sinta'unathi", 0) - R.add_language("Siik'tajr", 0) - R.add_language("Canilunzt", 0) - R.add_language("Skrellian", 0) - R.add_language("Vox-pidgin", 0) - R.add_language("Rootspeak", 0) - R.add_language("Trinary", 1) - R.add_language("Chittin", 0) - R.add_language("Bubblish", 0) - R.add_language("Orluum", 0) - R.add_language("Clownish",0) - -/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) - R.verbs |= subsystems - for(var/A in module_actions) - var/datum/action/act = new A() - act.Grant(R) - R.module_actions += act - -/obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) - R.verbs -= subsystems - for(var/datum/action/A in R.module_actions) - A.Remove(R) - qdel(A) - R.module_actions.Cut() - -// Return true in an overridden subtype to prevent normal removal handling -/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W) - return FALSE - -/obj/item/robot_module/proc/handle_death(gibbed) - return - -/obj/item/robot_module/standard - name = "standard robot module" - module_type = "Standard" - -/obj/item/robot_module/standard/New() - ..() - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/healthanalyzer(src) - emag = new /obj/item/melee/energy/sword/cyborg(src) - - fix_modules() - -/obj/item/robot_module/medical - name = "medical robot module" - module_type = "Medical" - subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 6, - /obj/item/stack/medical/ointment/advanced = 6, - /obj/item/stack/medical/splint = 6, - /obj/item/stack/nanopaste = 6 - ) - -/obj/item/robot_module/medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/robotanalyzer(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo(src) - modules += new /obj/item/reagent_containers/glass/beaker/large(src) - modules += new /obj/item/reagent_containers/dropper(src) - modules += new /obj/item/reagent_containers/syringe(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/circular_saw(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - - emag = new /obj/item/reagent_containers/spray(src) - - emag.reagents.add_reagent("facid", 250) - emag.name = "Polyacid spray" - - fix_modules() - -/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) - if(emag) - var/obj/item/reagent_containers/spray/PS = emag - PS.reagents.add_reagent("facid", 2) - ..() - -/obj/item/robot_module/engineering - name = "engineering robot module" - module_type = "Engineer" - subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/engineering/New() - ..() - modules += new /obj/item/rcd/borg(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/holosign_creator/engineering(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/floor_painter(src) - modules += new /obj/item/areaeditor/blueprints/cyborg(src) - emag = new /obj/item/borg/stun(src) - - for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. - var/obj/item/stack/sheet/M = new G(src) - M.amount = stacktypes[G] - modules += M - - fix_modules() - -/obj/item/robot_module/engineering/handle_death() - var/obj/item/gripper/G = locate(/obj/item/gripper) in modules - if(G) - G.drop_item() - -/obj/item/robot_module/security - name = "security robot module" - module_type = "Security" - subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - -/obj/item/robot_module/security/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/gun/energy/disabler/cyborg(src) - modules += new /obj/item/holosign_creator/security(src) - modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) - emag = new /obj/item/gun/energy/laser/cyborg(src) - - fix_modules() - -/obj/item/robot_module/janitor - name = "janitorial robot module" - module_type = "Janitor" - -/obj/item/robot_module/janitor/New() - ..() - modules += new /obj/item/soap/nanotrasen(src) - modules += new /obj/item/storage/bag/trash/cyborg(src) - modules += new /obj/item/mop/advanced/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/holosign_creator(src) - emag = new /obj/item/reagent_containers/spray(src) - - emag.reagents.add_reagent("lube", 250) - emag.name = "Lube spray" - - fix_modules() - -/obj/item/robot_module/butler - name = "service robot module" - module_type = "Service" - -/obj/item/robot_module/butler/New() - ..() - modules += new /obj/item/reagent_containers/food/drinks/cans/beer(src) - modules += new /obj/item/reagent_containers/food/drinks/cans/cola(src) - modules += new /obj/item/reagent_containers/food/drinks/cans/sodawater(src) - modules += new /obj/item/reagent_containers/food/condiment/enzyme(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/orangejuice(src) // -0.3 oxy/sec - modules += new /obj/item/reagent_containers/food/drinks/bottle/tomatojuice(src) // -0.2 fire/sec - modules += new /obj/item/reagent_containers/food/drinks/bottle/limejuice(src) // -0.2 tox/sec - modules += new /obj/item/reagent_containers/food/drinks/coffee(src) // -1 paralysis stunned & weakened/sec - modules += new /obj/item/reagent_containers/food/drinks/tea(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/milk(src) // -0.2 brute/sec - modules += new /obj/item/reagent_containers/food/condiment/sugar(src) - modules += new /obj/item/reagent_containers/food/drinks/ice(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/cream(src) - - modules += new /obj/item/reagent_containers/food/drinks/bottle/tequila(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/vodka(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/whiskey(src) - - modules += new /obj/item/pen(src) - modules += new /obj/item/razor(src) - modules += new /obj/item/instrument/piano_synth(src) - modules += new /obj/item/healthanalyzer/advanced(src) - - var/obj/item/rsf/M = new /obj/item/rsf(src) - M.matter = 30 - modules += M - - modules += new /obj/item/reagent_containers/dropper/cyborg(src) - modules += new /obj/item/lighter/zippo(src) - modules += new /obj/item/storage/bag/tray/cyborg(src) - modules += new /obj/item/reagent_containers/food/drinks/shaker(src) - emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) - - var/datum/reagents/R = new/datum/reagents(50) - emag.reagents = R - R.my_atom = emag - R.add_reagent("beer2", 50) - emag.name = "Mickey Finn's Special Brew" - - fix_modules() - -/obj/item/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) - var/obj/item/reagent_containers/food/condiment/enzyme/E = locate() in modules - E.reagents.add_reagent("enzyme", 2) - if(emag) - var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag - B.reagents.add_reagent("beer2", 2) - ..() - -/obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) - //full set of languages - R.add_language("Galactic Common", 1) - R.add_language("Sol Common", 1) - R.add_language("Tradeband", 1) - R.add_language("Gutter", 1) - R.add_language("Sinta'unathi", 1) - R.add_language("Siik'tajr", 1) - R.add_language("Canilunzt", 1) - R.add_language("Skrellian", 1) - R.add_language("Vox-pidgin", 1) - R.add_language("Rootspeak", 1) - R.add_language("Trinary", 1) - R.add_language("Chittin", 1) - R.add_language("Bubblish", 1) - R.add_language("Clownish",1) - R.add_language("Neo-Russkiya", 1) - - -/obj/item/robot_module/miner - name = "miner robot module" - module_type = "Miner" - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) - custom_removals = list("KA modkits") - -/obj/item/robot_module/miner/New() - ..() - modules += new /obj/item/storage/bag/ore/cyborg(src) - modules += new /obj/item/pickaxe/drill/cyborg(src) - modules += new /obj/item/shovel(src) - modules += new /obj/item/weldingtool/mini(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) - modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) - modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) - modules += new /obj/item/gps/cyborg(src) - emag = new /obj/item/borg/stun(src) - - fix_modules() - -/obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) - if(component_id == "KA modkits") - for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) - D.attackby(W, user) - return TRUE - return ..() - -/obj/item/robot_module/deathsquad - name = "NT advanced combat module" - module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, - ) - -/obj/item/robot_module/deathsquad/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/pulse/cyborg(src) - modules += new /obj/item/crowbar(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate - name = "syndicate assault robot module" - module_type = "Malf" // cuz it looks cool - -/obj/item/robot_module/syndicate/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/printer(src) - modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate_medical - name = "syndicate medical robot module" - module_type = "Malf" - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 25, - /obj/item/stack/medical/ointment/advanced = 25, - /obj/item/stack/medical/splint = 25, - /obj/item/stack/nanopaste = 25 - ) - -/obj/item/robot_module/syndicate_medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/bodyanalyzer/borg/syndicate(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo/syndicate(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - modules += new /obj/item/gun/medbeam(src) - modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate_saboteur - name = "engineering robot module" //to disguise in examine - module_type = "Malf" - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/syndicate_saboteur/New() - ..() - modules += new /obj/item/rcd/borg/syndicate(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/borg_chameleon(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/combat - name = "combat robot module" - module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, - ) - -/obj/item/robot_module/combat/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/gun/energy/gun/cyborg(src) - modules += new /obj/item/pickaxe/drill/jackhammer(src) - modules += new /obj/item/borg/combat/shield(src) - modules += new /obj/item/borg/combat/mobility(src) - modules += new /obj/item/wrench/cyborg(src) - emag = new /obj/item/gun/energy/lasercannon/cyborg(src) - - fix_modules() - -/obj/item/robot_module/alien/hunter - name = "alien hunter module" - module_type = "Standard" - module_actions = list( - /datum/action/innate/robot_sight/thermal/alien, - ) - -/obj/item/robot_module/alien/hunter/New() - ..() - modules += new /obj/item/melee/energy/alien/claws(src) - modules += new /obj/item/flash/cyborg/alien(src) - var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) - S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. - modules += S - var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) - S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. - modules += A - emag = new /obj/item/reagent_containers/spray/alien/acid(src) - emag.reagents.add_reagent("facid", 125) - emag.reagents.add_reagent("sacid", 125) - - fix_modules() - -/obj/item/robot_module/alien/hunter/add_languages(var/mob/living/silicon/robot/R) - ..() - R.add_language("xenocommon", 1) - -/obj/item/robot_module/drone - name = "drone module" - module_type = "Engineer" - stacktypes = list( - /obj/item/stack/sheet/wood = 10, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/tile/wood = 20, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20, - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 30 - ) - -/obj/item/robot_module/drone/New() - ..() - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) - modules += new /obj/item/soap(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/rpd(src) - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/drone/add_default_robot_items() - return - -/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) - var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules - C.reagents.add_reagent("cleaner", 3) - ..() - - -/obj/item/robot_module/drone/handle_death() - var/obj/item/gripper/G = locate(/obj/item/gripper) in modules - if(G) - G.drop_item() - -//checks whether this item is a module of the robot it is located in. -/obj/item/proc/is_robot_module() - if(!istype(loc, /mob/living/silicon/robot)) - return 0 - - var/mob/living/silicon/robot/R = loc - - return (src in R.module.modules) +/obj/item/robot_module + name = "robot module" + icon = 'icons/obj/module.dmi' + icon_state = "std_module" + w_class = 100 + item_state = "electronic" + flags = CONDUCT + + var/list/modules = list() + var/obj/item/emag = null + var/list/subsystems = list() + var/list/module_actions = list() + + var/module_type = "NoMod" // For icon usage + + var/list/stacktypes + var/channels = list() + var/list/custom_removals = list() + + +/obj/item/robot_module/emp_act(severity) + if(modules) + for(var/obj/O in modules) + O.emp_act(severity) + if(emag) + emag.emp_act(severity) + ..() + + +/obj/item/robot_module/New() + ..() + add_default_robot_items() + emag = new /obj/item/toy/sword(src) + emag.name = "Placeholder Emag Item" + +/obj/item/robot_module/Destroy() + QDEL_LIST(modules) + QDEL_NULL(emag) + return ..() + +// By default, all robots will get the items in this proc, unless you override it for your specific module. See: ../robot_module/drone +/obj/item/robot_module/proc/add_default_robot_items() + modules += new /obj/item/flash/cyborg(src) + +/obj/item/robot_module/proc/fix_modules() + for(var/obj/item/I in modules) + I.flags |= NODROP + I.mouse_opacity = MOUSE_OPACITY_OPAQUE + if(emag) + emag.flags |= NODROP + emag.mouse_opacity = MOUSE_OPACITY_OPAQUE + +/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) + if(!stacktypes || !stacktypes.len) + return + + var/stack_respawned = 0 + for(var/T in stacktypes) + var/O = locate(T) in modules + var/obj/item/stack/S = O + + if(!S) + S = new T(src) + modules += S + S.amount = 1 + stack_respawned = 1 + + if(S && S.amount < stacktypes[T]) + S.amount++ + if(stack_respawned && istype(R) && R.hud_used) + R.hud_used.update_robot_modules_display() + +/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module + var/list/temp_list = modules + modules = list() + for(var/obj/O in temp_list) + if(O) + modules += O + +/obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) + //full set of languages + R.add_language("Galactic Common", 1) + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 0) + R.add_language("Neo-Russkiya", 0) + R.add_language("Sinta'unathi", 0) + R.add_language("Siik'tajr", 0) + R.add_language("Canilunzt", 0) + R.add_language("Skrellian", 0) + R.add_language("Vox-pidgin", 0) + R.add_language("Rootspeak", 0) + R.add_language("Trinary", 1) + R.add_language("Chittin", 0) + R.add_language("Bubblish", 0) + R.add_language("Orluum", 0) + R.add_language("Clownish",0) + +/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) + R.verbs |= subsystems + for(var/A in module_actions) + var/datum/action/act = new A() + act.Grant(R) + R.module_actions += act + +/obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) + R.verbs -= subsystems + for(var/datum/action/A in R.module_actions) + A.Remove(R) + qdel(A) + R.module_actions.Cut() + +// Return true in an overridden subtype to prevent normal removal handling +/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W) + return FALSE + +/obj/item/robot_module/proc/handle_death(gibbed) + return + +/obj/item/robot_module/standard + name = "standard robot module" + module_type = "Standard" + +/obj/item/robot_module/standard/New() + ..() + modules += new /obj/item/melee/baton/loaded(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/healthanalyzer(src) + emag = new /obj/item/melee/energy/sword/cyborg(src) + + fix_modules() + +/obj/item/robot_module/medical + name = "medical robot module" + module_type = "Medical" + subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + stacktypes = list( + /obj/item/stack/medical/bruise_pack/advanced = 6, + /obj/item/stack/medical/ointment/advanced = 6, + /obj/item/stack/medical/splint = 6, + /obj/item/stack/nanopaste = 6 + ) + +/obj/item/robot_module/medical/New() + ..() + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/robotanalyzer(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/borg_defib(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/roller_holder(src) + modules += new /obj/item/reagent_containers/borghypo(src) + modules += new /obj/item/reagent_containers/glass/beaker/large(src) + modules += new /obj/item/reagent_containers/dropper(src) + modules += new /obj/item/reagent_containers/syringe(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced(src) + modules += new /obj/item/stack/medical/ointment/advanced(src) + modules += new /obj/item/stack/medical/splint(src) + modules += new /obj/item/stack/nanopaste(src) + modules += new /obj/item/scalpel/laser/laser1(src) + modules += new /obj/item/hemostat(src) + modules += new /obj/item/retractor(src) + modules += new /obj/item/bonegel(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/bonesetter(src) + modules += new /obj/item/circular_saw(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/gripper/medical(src) + + emag = new /obj/item/reagent_containers/spray(src) + + emag.reagents.add_reagent("facid", 250) + emag.name = "Polyacid spray" + + fix_modules() + +/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) + if(emag) + var/obj/item/reagent_containers/spray/PS = emag + PS.reagents.add_reagent("facid", 2) + ..() + +/obj/item/robot_module/engineering + name = "engineering robot module" + module_type = "Engineer" + subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) + module_actions = list( + /datum/action/innate/robot_sight/meson, + ) + + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) + +/obj/item/robot_module/engineering/New() + ..() + modules += new /obj/item/rcd/borg(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/holosign_creator/engineering(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/floor_painter(src) + modules += new /obj/item/areaeditor/blueprints/cyborg(src) + emag = new /obj/item/borg/stun(src) + + for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. + var/obj/item/stack/sheet/M = new G(src) + M.amount = stacktypes[G] + modules += M + + fix_modules() + +/obj/item/robot_module/engineering/handle_death() + var/obj/item/gripper/G = locate(/obj/item/gripper) in modules + if(G) + G.drop_item() + +/obj/item/robot_module/security + name = "security robot module" + module_type = "Security" + subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + +/obj/item/robot_module/security/New() + ..() + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/melee/baton/loaded(src) + modules += new /obj/item/gun/energy/disabler/cyborg(src) + modules += new /obj/item/holosign_creator/security(src) + modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) + emag = new /obj/item/gun/energy/laser/cyborg(src) + + fix_modules() + +/obj/item/robot_module/janitor + name = "janitorial robot module" + module_type = "Janitor" + +/obj/item/robot_module/janitor/New() + ..() + modules += new /obj/item/soap/nanotrasen(src) + modules += new /obj/item/storage/bag/trash/cyborg(src) + modules += new /obj/item/mop/advanced/cyborg(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/holosign_creator(src) + emag = new /obj/item/reagent_containers/spray(src) + + emag.reagents.add_reagent("lube", 250) + emag.name = "Lube spray" + + fix_modules() + +/obj/item/robot_module/butler + name = "service robot module" + module_type = "Service" + +/obj/item/robot_module/butler/New() + ..() + modules += new /obj/item/reagent_containers/food/drinks/cans/beer(src) + modules += new /obj/item/reagent_containers/food/drinks/cans/cola(src) + modules += new /obj/item/reagent_containers/food/drinks/cans/sodawater(src) + modules += new /obj/item/reagent_containers/food/condiment/enzyme(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/orangejuice(src) // -0.3 oxy/sec + modules += new /obj/item/reagent_containers/food/drinks/bottle/tomatojuice(src) // -0.2 fire/sec + modules += new /obj/item/reagent_containers/food/drinks/bottle/limejuice(src) // -0.2 tox/sec + modules += new /obj/item/reagent_containers/food/drinks/coffee(src) // -1 paralysis stunned & weakened/sec + modules += new /obj/item/reagent_containers/food/drinks/tea(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/milk(src) // -0.2 brute/sec + modules += new /obj/item/reagent_containers/food/condiment/sugar(src) + modules += new /obj/item/reagent_containers/food/drinks/ice(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/cream(src) + + modules += new /obj/item/reagent_containers/food/drinks/bottle/tequila(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/vodka(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/whiskey(src) + + modules += new /obj/item/pen(src) + modules += new /obj/item/razor(src) + modules += new /obj/item/instrument/piano_synth(src) + modules += new /obj/item/healthanalyzer/advanced(src) + + var/obj/item/rsf/M = new /obj/item/rsf(src) + M.matter = 30 + modules += M + + modules += new /obj/item/reagent_containers/dropper/cyborg(src) + modules += new /obj/item/lighter/zippo(src) + modules += new /obj/item/storage/bag/tray/cyborg(src) + modules += new /obj/item/reagent_containers/food/drinks/shaker(src) + emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) + + var/datum/reagents/R = new/datum/reagents(50) + emag.reagents = R + R.my_atom = emag + R.add_reagent("beer2", 50) + emag.name = "Mickey Finn's Special Brew" + + fix_modules() + +/obj/item/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) + var/obj/item/reagent_containers/food/condiment/enzyme/E = locate() in modules + E.reagents.add_reagent("enzyme", 2) + if(emag) + var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag + B.reagents.add_reagent("beer2", 2) + ..() + +/obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Galactic Common", 1) + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 1) + R.add_language("Sinta'unathi", 1) + R.add_language("Siik'tajr", 1) + R.add_language("Canilunzt", 1) + R.add_language("Skrellian", 1) + R.add_language("Vox-pidgin", 1) + R.add_language("Rootspeak", 1) + R.add_language("Trinary", 1) + R.add_language("Chittin", 1) + R.add_language("Bubblish", 1) + R.add_language("Clownish",1) + R.add_language("Neo-Russkiya", 1) + + +/obj/item/robot_module/miner + name = "miner robot module" + module_type = "Miner" + module_actions = list( + /datum/action/innate/robot_sight/meson, + ) + custom_removals = list("KA modkits") + +/obj/item/robot_module/miner/New() + ..() + modules += new /obj/item/storage/bag/ore/cyborg(src) + modules += new /obj/item/pickaxe/drill/cyborg(src) + modules += new /obj/item/shovel(src) + modules += new /obj/item/weldingtool/mini(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) + modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) + modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) + modules += new /obj/item/gps/cyborg(src) + emag = new /obj/item/borg/stun(src) + + fix_modules() + +/obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) + if(component_id == "KA modkits") + for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) + D.attackby(W, user) + return TRUE + return ..() + +/obj/item/robot_module/deathsquad + name = "NT advanced combat module" + module_type = "Malf" + module_actions = list( + /datum/action/innate/robot_sight/thermal, + ) + +/obj/item/robot_module/deathsquad/New() + ..() + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gun/energy/pulse/cyborg(src) + modules += new /obj/item/crowbar(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate + name = "syndicate assault robot module" + module_type = "Malf" // cuz it looks cool + +/obj/item/robot_module/syndicate/New() + ..() + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gun/energy/printer(src) + modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) + modules += new /obj/item/card/emag(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate_medical + name = "syndicate medical robot module" + module_type = "Malf" + stacktypes = list( + /obj/item/stack/medical/bruise_pack/advanced = 25, + /obj/item/stack/medical/ointment/advanced = 25, + /obj/item/stack/medical/splint = 25, + /obj/item/stack/nanopaste = 25 + ) + +/obj/item/robot_module/syndicate_medical/New() + ..() + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/bodyanalyzer/borg/syndicate(src) + modules += new /obj/item/borg_defib(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/roller_holder(src) + modules += new /obj/item/reagent_containers/borghypo/syndicate(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced(src) + modules += new /obj/item/stack/medical/ointment/advanced(src) + modules += new /obj/item/stack/medical/splint(src) + modules += new /obj/item/stack/nanopaste(src) + modules += new /obj/item/scalpel/laser/laser1(src) + modules += new /obj/item/hemostat(src) + modules += new /obj/item/retractor(src) + modules += new /obj/item/bonegel(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/bonesetter(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/gripper/medical(src) + modules += new /obj/item/gun/medbeam(src) + modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon + modules += new /obj/item/card/emag(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate_saboteur + name = "engineering robot module" //to disguise in examine + module_type = "Malf" + + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) + +/obj/item/robot_module/syndicate_saboteur/New() + ..() + modules += new /obj/item/rcd/borg/syndicate(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/card/emag(src) + modules += new /obj/item/borg_chameleon(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + modules += W + + fix_modules() + +/obj/item/robot_module/combat + name = "combat robot module" + module_type = "Malf" + module_actions = list( + /datum/action/innate/robot_sight/thermal, + ) + +/obj/item/robot_module/combat/New() + ..() + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/gun/energy/gun/cyborg(src) + modules += new /obj/item/pickaxe/drill/jackhammer(src) + modules += new /obj/item/borg/combat/shield(src) + modules += new /obj/item/borg/combat/mobility(src) + modules += new /obj/item/wrench/cyborg(src) + emag = new /obj/item/gun/energy/lasercannon/cyborg(src) + + fix_modules() + +/obj/item/robot_module/alien/hunter + name = "alien hunter module" + module_type = "Standard" + module_actions = list( + /datum/action/innate/robot_sight/thermal/alien, + ) + +/obj/item/robot_module/alien/hunter/New() + ..() + modules += new /obj/item/melee/energy/alien/claws(src) + modules += new /obj/item/flash/cyborg/alien(src) + var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) + S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. + modules += S + var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) + S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. + modules += A + emag = new /obj/item/reagent_containers/spray/alien/acid(src) + emag.reagents.add_reagent("facid", 125) + emag.reagents.add_reagent("sacid", 125) + + fix_modules() + +/obj/item/robot_module/alien/hunter/add_languages(var/mob/living/silicon/robot/R) + ..() + R.add_language("xenocommon", 1) + +/obj/item/robot_module/drone + name = "drone module" + module_type = "Engineer" + stacktypes = list( + /obj/item/stack/sheet/wood = 10, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/tile/wood = 20, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20, + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 30 + ) + +/obj/item/robot_module/drone/New() + ..() + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) + modules += new /obj/item/soap(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/rpd(src) + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + modules += W + + fix_modules() + +/obj/item/robot_module/drone/add_default_robot_items() + return + +/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) + var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules + C.reagents.add_reagent("cleaner", 3) + ..() + + +/obj/item/robot_module/drone/handle_death() + var/obj/item/gripper/G = locate(/obj/item/gripper) in modules + if(G) + G.drop_item() + +//checks whether this item is a module of the robot it is located in. +/obj/item/proc/is_robot_module() + if(!istype(loc, /mob/living/silicon/robot)) + return 0 + + var/mob/living/silicon/robot/R = loc + + return (src in R.module.modules) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index e896e7c9473c..4b645b02849f 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -21,4 +21,4 @@ /mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) if(!magpulse) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/silicon/robot/update_status.dm b/code/modules/mob/living/silicon/robot/update_status.dm index 63c13f97d24e..5f1519395c72 100644 --- a/code/modules/mob/living/silicon/robot/update_status.dm +++ b/code/modules/mob/living/silicon/robot/update_status.dm @@ -30,6 +30,7 @@ to_chat(ghost, "Your cyborg shell has been repaired, re-enter if you want to continue! (Verbs -> Ghost -> Re-enter corpse)") ghost << sound('sound/effects/genetics.ogg') create_attack_log("revived, trigger reason: [reason]") + create_log(MISC_LOG, "revived, trigger reason: [reason]") // diag_hud_set_status() // diag_hud_set_health() // update_health_hud() diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 8092947919f3..f75475fa92dc 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -1,115 +1,115 @@ -/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - log_say(multilingual_to_message(message_pieces), src) - if(..()) - return 1 - -/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - if(message_mode) - used_radios += radio - if(!is_component_functioning("radio")) - to_chat(src, "Your radio isn't functional at this time.") - return 0 - if(message_mode == "general") - message_mode = null - return radio.talk_into(src,message_pieces,message_mode,verb) - -/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - if(message_mode == "department") - used_radios += aiRadio - return holopad_talk(message_pieces, verb) - else if(message_mode) - used_radios += aiRadio - if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) - to_chat(src, "System Error - Transceiver Disabled.") - return 0 - if(message_mode == "general") - message_mode = null - return aiRadio.talk_into(src, message_pieces, message_mode, verb) - -/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - else if(message_mode == "whisper") - whisper_say(message_pieces) - return 1 - else if(message_mode) - if(message_mode == "general") - message_mode = null - used_radios += radio - return radio.talk_into(src, message_pieces, message_mode, verb) - -/mob/living/silicon/say_quote(var/text) - var/ending = copytext(text, length(text)) - - if(ending == "?") - return speak_query - else if(ending == "!") - return speak_exclamation - - return speak_statement - -#define IS_AI 1 -#define IS_ROBOT 2 -#define IS_PAI 3 - -/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) - //These only pertain to common. Languages are handled by mob/say_understands() - if(!speaking) - if(istype(other, /mob/living/carbon)) - return 1 - if(istype(other, /mob/living/silicon)) - return 1 - if(istype(other, /mob/living/simple_animal/bot)) - return 1 - if(istype(other, /mob/living/carbon/brain)) - return 1 - return ..() - -//For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) - log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) - - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src]) - for(var/mob/M in hearers(T.loc))//The location is the object, default distance. - M.hear_holopad_talk(message_pieces, verb, src) - to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") - else - to_chat(src, "No holopad connected.") - return - return 1 - -/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. - message = trim(message) - - if(!message) - return - - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src]) - var/rendered = "[name] [message]" - to_chat(src, "Holopad action relayed, [real_name] [message]") - - for(var/mob/M in viewers(T.loc)) - M.show_message(rendered, 2) - - log_emote("(HPAD) [message]", src) - else //This shouldn't occur, but better safe then sorry. - to_chat(src, "No holopad connected.") - return - return 1 - -/mob/living/silicon/ai/emote(act, type, message, force) - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src])//Is the AI using a holopad? - src.holopad_emote(message) - else //Emote normally, then. - ..() - -#undef IS_AI -#undef IS_ROBOT -#undef IS_PAI +/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + log_say(multilingual_to_message(message_pieces), src) + if(..()) + return 1 + +/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + if(message_mode) + used_radios += radio + if(!is_component_functioning("radio")) + to_chat(src, "Your radio isn't functional at this time.") + return 0 + if(message_mode == "general") + message_mode = null + return radio.talk_into(src,message_pieces,message_mode,verb) + +/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + if(message_mode == "department") + used_radios += aiRadio + return holopad_talk(message_pieces, verb) + else if(message_mode) + used_radios += aiRadio + if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) + to_chat(src, "System Error - Transceiver Disabled.") + return 0 + if(message_mode == "general") + message_mode = null + return aiRadio.talk_into(src, message_pieces, message_mode, verb) + +/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + else if(message_mode == "whisper") + whisper_say(message_pieces) + return 1 + else if(message_mode) + if(message_mode == "general") + message_mode = null + used_radios += radio + return radio.talk_into(src, message_pieces, message_mode, verb) + +/mob/living/silicon/say_quote(var/text) + var/ending = copytext(text, length(text)) + + if(ending == "?") + return speak_query + else if(ending == "!") + return speak_exclamation + + return speak_statement + +#define IS_AI 1 +#define IS_ROBOT 2 +#define IS_PAI 3 + +/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) + //These only pertain to common. Languages are handled by mob/say_understands() + if(!speaking) + if(istype(other, /mob/living/carbon)) + return 1 + if(istype(other, /mob/living/silicon)) + return 1 + if(istype(other, /mob/living/simple_animal/bot)) + return 1 + if(istype(other, /mob/living/carbon/brain)) + return 1 + return ..() + +//For holopads only. Usable by AI. +/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) + log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src]) + for(var/mob/M in hearers(T.loc))//The location is the object, default distance. + M.hear_holopad_talk(message_pieces, verb, src) + to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") + else + to_chat(src, "No holopad connected.") + return + return 1 + +/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. + message = trim(message) + + if(!message) + return + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src]) + var/rendered = "[name] [message]" + to_chat(src, "Holopad action relayed, [real_name] [message]") + + for(var/mob/M in viewers(T.loc)) + M.show_message(rendered, 2) + + log_emote("(HPAD) [message]", src) + else //This shouldn't occur, but better safe then sorry. + to_chat(src, "No holopad connected.") + return + return 1 + +/mob/living/silicon/ai/emote(act, type, message, force) + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src])//Is the AI using a holopad? + src.holopad_emote(message) + else //Emote normally, then. + ..() + +#undef IS_AI +#undef IS_ROBOT +#undef IS_PAI diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index e70c00158a7e..a62f5f9d358a 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -1,360 +1,360 @@ -/mob/living/silicon - gender = NEUTER - robot_talk_understand = 1 - voice_name = "synthesized voice" - has_unlimited_silicon_privilege = 1 - var/syndicate = 0 - var/const/MAIN_CHANNEL = "Main Frequency" - var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws - var/list/stating_laws = list()// Channels laws are currently being stated on - var/list/alarms_to_show = list() - var/list/alarms_to_clear = list() - //var/list/hud_list[10] - var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer - var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to - var/designation = "" - var/obj/item/camera/siliconcam/aiCamera = null //photography -//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. - var/speak_statement = "states" - var/speak_exclamation = "declares" - var/speak_query = "queries" - var/pose //Yes, now AIs can pose too. - var/death_sound = 'sound/voice/borg_deathsound.ogg' - - //var/sensor_mode = 0 //Determines the current HUD. - - var/next_alarm_notice - var/list/datum/alarm/queued_alarms = new() - - hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) - - - var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use - var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use - var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud - - var/obj/item/radio/common_radio - -/mob/living/silicon/New() - GLOB.silicon_mob_list |= src - ..() - var/datum/atom_hud/data/diagnostic/diag_hud = huds[DATA_HUD_DIAGNOSTIC] - diag_hud.add_to_hud(src) - diag_hud_set_status() - diag_hud_set_health() - add_language("Galactic Common") - init_subsystems() - -/mob/living/silicon/med_hud_set_health() - return //we use a different hud - -/mob/living/silicon/med_hud_set_status() - return //we use a different hud - -/mob/living/silicon/Destroy() - GLOB.silicon_mob_list -= src - for(var/datum/alarm_handler/AH in alarm_handlers) - AH.unregister(src) - return ..() - -/mob/living/silicon/rename_character(oldname, newname) - // we actually don't want it changing minds and stuff - if(!newname) - return 0 - - real_name = newname - name = real_name - return 1 - -/mob/living/silicon/proc/show_laws() - return - -/mob/living/silicon/drop_item() - return - -/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE //So borgs they don't die trying to fix wiring - -/mob/living/silicon/emp_act(severity) - switch(severity) - if(1) - src.take_organ_damage(20) - Stun(8) - if(2) - src.take_organ_damage(10) - Stun(3) - flash_eyes(affect_silicon = 1) - to_chat(src, "*BZZZT*") - to_chat(src, "Warning: Electromagnetic pulse detected.") - ..() - - -/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) - return - -/mob/living/silicon/can_inject(mob/user, error_msg) - if(error_msg) - to_chat(user, "[p_their(TRUE)] outer shell is too tough.") - return FALSE - -/mob/living/silicon/IsAdvancedToolUser() - return TRUE - -/mob/living/silicon/robot/welder_act(mob/user, obj/item/I) - if(user.a_intent != INTENT_HELP) - return - if(user == src) //No self-repair dummy - return - . = TRUE - if(!getBruteLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getBruteLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - if(!I.use_tool(src, user, volume = I.tool_volume)) - return - adjustBruteLoss(-30) - add_fingerprint(user) - user.visible_message("[user] patches some dents on [src] with [I].") - - -/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) - - - if(!Proj.nodamage) - switch(Proj.damage_type) - if(BRUTE) - adjustBruteLoss(Proj.damage) - if(BURN) - adjustFireLoss(Proj.damage) - - Proj.on_hit(src,2) - - return 2 - -/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) - return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now -/* - if(!effect || (blocked >= 2)) return 0 - switch(effecttype) - if(STUN) - Stun(effect / (blocked + 1)) - if(WEAKEN) - Weaken(effect / (blocked + 1)) - if(PARALYZE) - Paralyse(effect / (blocked + 1)) - if(IRRADIATE) - radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor - if(STUTTER) - stuttering = max(stuttering,(effect/(blocked+1))) - if(EYE_BLUR) - eye_blurry = max(eye_blurry,(effect/(blocked+1))) - if(DROWSY) - drowsyness = max(drowsyness,(effect/(blocked+1))) - updatehealth() - return 1*/ - -/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) - if(!istype(bot) || !istype(ai)) - return 0 - if(bot.connected_ai == ai) - return 1 - return 0 - - -// this function shows the health of the pAI in the Status panel -/mob/living/silicon/proc/show_system_integrity() - if(!src.stat) - stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) - else - stat(null, text("Systems nonfunctional")) - - -// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms -/mob/living/silicon/Stat() - ..() - if(statpanel("Status")) - show_stat_emergency_shuttle_eta() - show_system_integrity() - -//Silicon mob language procs - -/mob/living/silicon/can_speak_language(datum/language/speaking) - return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language - -/mob/living/silicon/add_language(var/language, var/can_speak=1) - if(..(language) && can_speak) - speech_synthesizer_langs.Add(GLOB.all_languages[language]) - return 1 - -/mob/living/silicon/remove_language(var/rem_language) - ..(rem_language) - - for(var/datum/language/L in speech_synthesizer_langs) - if(L.name == rem_language) - speech_synthesizer_langs -= L - -/mob/living/silicon/check_lang_data() - . = "" - - if(default_language) - . += "Current default language: [default_language] - reset

    " - - for(var/datum/language/L in languages) - if(!(L.flags & NONGLOBAL)) - var/default_str - if(L == default_language) - default_str = " - default - reset" - else - default_str = " - set default" - - var/synth = (L in speech_synthesizer_langs) - . += "[L.name] (:[L.key])[synth ? default_str : null]
    Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
    [L.desc]

    " - - -// this function displays the stations manifest in a separate window -/mob/living/silicon/proc/show_station_manifest() - var/dat - dat += "

    Crew Manifest

    " - if(data_core) - dat += data_core.get_manifest(1) // make it monochrome - dat += "
    " - src << browse(dat, "window=airoster") - onclose(src, "airoster") - -/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units - return -10 - -/mob/living/silicon/verb/pose() - set name = "Set Pose" - set desc = "Sets a description which will be shown when someone examines you." - set category = "IC" - - pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) - -/mob/living/silicon/verb/set_flavor() - set name = "Set Flavour Text" - set desc = "Sets an extended description of your character's features." - set category = "IC" - - update_flavor_text() - -/mob/living/silicon/binarycheck() - return 1 - -/mob/living/silicon/proc/remove_med_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - var/datum/atom_hud/medsensor = huds[med_hud] - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.remove_hud_from(src) - secsensor.remove_hud_from(src) - medsensor.remove_hud_from(src) - - -/mob/living/silicon/proc/add_sec_hud() - var/datum/atom_hud/secsensor = huds[sec_hud] - secsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_med_hud() - var/datum/atom_hud/medsensor = huds[med_hud] - medsensor.add_hud_to(src) - -/mob/living/silicon/proc/add_diag_hud() - for(var/datum/atom_hud/data/diagnostic/diagsensor in huds) - diagsensor.add_hud_to(src) - - -/mob/living/silicon/proc/toggle_sensor_mode() - var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") - remove_med_sec_hud() - switch(sensor_type) - if("Security") - add_sec_hud() - to_chat(src, "Security records overlay enabled.") - if("Medical") - add_med_hud() - to_chat(src, "Life signs monitor overlay enabled.") - if("Diagnostic") - add_diag_hud() - to_chat(src, "Robotics diagnostic overlay enabled.") - if("Disable") - to_chat(src, "Sensor augmentations disabled.") - -/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) - if(!next_alarm_notice) - next_alarm_notice = world.time + SecondsToTicks(10) - - var/list/alarms = queued_alarms[alarm_handler] - if(was_raised) - // Raised alarms are always set - alarms[alarm] = 1 - else - // Alarms that were raised but then cleared before the next notice are instead removed - if(alarm in alarms) - alarms -= alarm - // And alarms that have only been cleared thus far are set as such - else - alarms[alarm] = -1 - -/mob/living/silicon/proc/process_queued_alarms() - if(next_alarm_notice && (world.time > next_alarm_notice)) - next_alarm_notice = 0 - - var/alarm_raised = 0 - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == 1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Detected ---") - raised_alarm(A) - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - var/reported = 0 - for(var/datum/alarm/A in alarms) - if(alarms[A] == -1) - if(!reported) - reported = 1 - to_chat(src, "--- [AH.category] Cleared ---") - to_chat(src, "\The [A.alarm_name()].") - - if(alarm_raised) - to_chat(src, "\[Show Alerts\]") - - for(var/datum/alarm_handler/AH in queued_alarms) - var/list/alarms = queued_alarms[AH] - alarms.Cut() - -/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) - to_chat(src, "[A.alarm_name()]!") - -/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) - var/cameratext = "" - for(var/obj/machinery/camera/C in A.cameras()) - cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" - to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") - -/mob/living/silicon/adjustToxLoss(var/amount) - return STATUS_UPDATE_NONE - -/mob/living/silicon/get_access() - return IGNORE_ACCESS //silicons always have access - -/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) - if(affect_silicon) - return ..() - -/mob/living/silicon/is_mechanical() - return 1 - -/mob/living/silicon/is_literate() - return 1 - -/////////////////////////////////// EAR DAMAGE //////////////////////////////////// -/mob/living/silicon/can_hear() - . = TRUE - +/mob/living/silicon + gender = NEUTER + robot_talk_understand = 1 + voice_name = "synthesized voice" + has_unlimited_silicon_privilege = 1 + var/syndicate = 0 + var/const/MAIN_CHANNEL = "Main Frequency" + var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws + var/list/stating_laws = list()// Channels laws are currently being stated on + var/list/alarms_to_show = list() + var/list/alarms_to_clear = list() + //var/list/hud_list[10] + var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer + var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to + var/designation = "" + var/obj/item/camera/siliconcam/aiCamera = null //photography +//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented. + var/speak_statement = "states" + var/speak_exclamation = "declares" + var/speak_query = "queries" + var/pose //Yes, now AIs can pose too. + var/death_sound = 'sound/voice/borg_deathsound.ogg' + + //var/sensor_mode = 0 //Determines the current HUD. + + var/next_alarm_notice + var/list/datum/alarm/queued_alarms = new() + + hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD) + + + var/med_hud = DATA_HUD_MEDICAL_ADVANCED //Determines the med hud to use + var/sec_hud = DATA_HUD_SECURITY_ADVANCED //Determines the sec hud to use + var/d_hud = DATA_HUD_DIAGNOSTIC_ADVANCED //There is only one kind of diag hud + + var/obj/item/radio/common_radio + +/mob/living/silicon/New() + GLOB.silicon_mob_list |= src + ..() + var/datum/atom_hud/data/diagnostic/diag_hud = GLOB.huds[DATA_HUD_DIAGNOSTIC] + diag_hud.add_to_hud(src) + diag_hud_set_status() + diag_hud_set_health() + add_language("Galactic Common") + init_subsystems() + +/mob/living/silicon/med_hud_set_health() + return //we use a different hud + +/mob/living/silicon/med_hud_set_status() + return //we use a different hud + +/mob/living/silicon/Destroy() + GLOB.silicon_mob_list -= src + for(var/datum/alarm_handler/AH in alarm_handlers) + AH.unregister(src) + return ..() + +/mob/living/silicon/rename_character(oldname, newname) + // we actually don't want it changing minds and stuff + if(!newname) + return 0 + + real_name = newname + name = real_name + return 1 + +/mob/living/silicon/proc/show_laws() + return + +/mob/living/silicon/drop_item() + return + +/mob/living/silicon/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE //So borgs they don't die trying to fix wiring + +/mob/living/silicon/emp_act(severity) + switch(severity) + if(1) + src.take_organ_damage(20) + Stun(8) + if(2) + src.take_organ_damage(10) + Stun(3) + flash_eyes(affect_silicon = 1) + to_chat(src, "*BZZZT*") + to_chat(src, "Warning: Electromagnetic pulse detected.") + ..() + + +/mob/living/silicon/proc/damage_mob(var/brute = 0, var/fire = 0, var/tox = 0) + return + +/mob/living/silicon/can_inject(mob/user, error_msg) + if(error_msg) + to_chat(user, "[p_their(TRUE)] outer shell is too tough.") + return FALSE + +/mob/living/silicon/IsAdvancedToolUser() + return TRUE + +/mob/living/silicon/robot/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + if(user == src) //No self-repair dummy + return + . = TRUE + if(!getBruteLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getBruteLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + adjustBruteLoss(-30) + add_fingerprint(user) + user.visible_message("[user] patches some dents on [src] with [I].") + + +/mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) + + + if(!Proj.nodamage) + switch(Proj.damage_type) + if(BRUTE) + adjustBruteLoss(Proj.damage) + if(BURN) + adjustFireLoss(Proj.damage) + + Proj.on_hit(src,2) + + return 2 + +/mob/living/silicon/apply_effect(var/effect = 0,var/effecttype = STUN, var/blocked = 0, var/negate_armor = 0) + return 0//The only effect that can hit them atm is flashes and they still directly edit so this works for now +/* + if(!effect || (blocked >= 2)) return 0 + switch(effecttype) + if(STUN) + Stun(effect / (blocked + 1)) + if(WEAKEN) + Weaken(effect / (blocked + 1)) + if(PARALYZE) + Paralyse(effect / (blocked + 1)) + if(IRRADIATE) + radiation += min((effect - (effect*getarmor(null, "rad"))), 0)//Rads auto check armor + if(STUTTER) + stuttering = max(stuttering,(effect/(blocked+1))) + if(EYE_BLUR) + eye_blurry = max(eye_blurry,(effect/(blocked+1))) + if(DROWSY) + drowsyness = max(drowsyness,(effect/(blocked+1))) + updatehealth() + return 1*/ + +/proc/islinked(var/mob/living/silicon/robot/bot, var/mob/living/silicon/ai/ai) + if(!istype(bot) || !istype(ai)) + return 0 + if(bot.connected_ai == ai) + return 1 + return 0 + + +// this function shows the health of the pAI in the Status panel +/mob/living/silicon/proc/show_system_integrity() + if(!src.stat) + stat(null, text("System integrity: [round((health/maxHealth)*100)]%")) + else + stat(null, text("Systems nonfunctional")) + + +// This adds the basic clock, shuttle recall timer, and malf_ai info to all silicon lifeforms +/mob/living/silicon/Stat() + ..() + if(statpanel("Status")) + show_stat_emergency_shuttle_eta() + show_system_integrity() + +//Silicon mob language procs + +/mob/living/silicon/can_speak_language(datum/language/speaking) + return universal_speak || (speaking in src.speech_synthesizer_langs) //need speech synthesizer support to vocalize a language + +/mob/living/silicon/add_language(var/language, var/can_speak=1) + if(..(language) && can_speak) + speech_synthesizer_langs.Add(GLOB.all_languages[language]) + return 1 + +/mob/living/silicon/remove_language(var/rem_language) + ..(rem_language) + + for(var/datum/language/L in speech_synthesizer_langs) + if(L.name == rem_language) + speech_synthesizer_langs -= L + +/mob/living/silicon/check_lang_data() + . = "" + + if(default_language) + . += "Current default language: [default_language] - reset

    " + + for(var/datum/language/L in languages) + if(!(L.flags & NONGLOBAL)) + var/default_str + if(L == default_language) + default_str = " - default - reset" + else + default_str = " - set default" + + var/synth = (L in speech_synthesizer_langs) + . += "[L.name] (:[L.key])[synth ? default_str : null]
    Speech Synthesizer: [synth ? "YES" : "NOT SUPPORTED"]
    [L.desc]

    " + + +// this function displays the stations manifest in a separate window +/mob/living/silicon/proc/show_station_manifest() + var/dat + dat += "

    Crew Manifest

    " + if(GLOB.data_core) + dat += GLOB.data_core.get_manifest(1) // make it monochrome + dat += "
    " + src << browse(dat, "window=airoster") + onclose(src, "airoster") + +/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units + return -10 + +/mob/living/silicon/verb/pose() + set name = "Set Pose" + set desc = "Sets a description which will be shown when someone examines you." + set category = "IC" + + pose = sanitize(copytext(input(usr, "This is [src]. It is...", "Pose", null) as text, 1, MAX_MESSAGE_LEN)) + +/mob/living/silicon/verb/set_flavor() + set name = "Set Flavour Text" + set desc = "Sets an extended description of your character's features." + set category = "IC" + + update_flavor_text() + +/mob/living/silicon/binarycheck() + return 1 + +/mob/living/silicon/proc/remove_med_sec_hud() + var/datum/atom_hud/secsensor = GLOB.huds[sec_hud] + var/datum/atom_hud/medsensor = GLOB.huds[med_hud] + for(var/datum/atom_hud/data/diagnostic/diagsensor in GLOB.huds) + diagsensor.remove_hud_from(src) + secsensor.remove_hud_from(src) + medsensor.remove_hud_from(src) + + +/mob/living/silicon/proc/add_sec_hud() + var/datum/atom_hud/secsensor = GLOB.huds[sec_hud] + secsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_med_hud() + var/datum/atom_hud/medsensor = GLOB.huds[med_hud] + medsensor.add_hud_to(src) + +/mob/living/silicon/proc/add_diag_hud() + for(var/datum/atom_hud/data/diagnostic/diagsensor in GLOB.huds) + diagsensor.add_hud_to(src) + + +/mob/living/silicon/proc/toggle_sensor_mode() + var/sensor_type = input("Please select sensor type.", "Sensor Integration", null) in list("Security", "Medical","Diagnostic","Disable") + remove_med_sec_hud() + switch(sensor_type) + if("Security") + add_sec_hud() + to_chat(src, "Security records overlay enabled.") + if("Medical") + add_med_hud() + to_chat(src, "Life signs monitor overlay enabled.") + if("Diagnostic") + add_diag_hud() + to_chat(src, "Robotics diagnostic overlay enabled.") + if("Disable") + to_chat(src, "Sensor augmentations disabled.") + +/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised) + if(!next_alarm_notice) + next_alarm_notice = world.time + SecondsToTicks(10) + + var/list/alarms = queued_alarms[alarm_handler] + if(was_raised) + // Raised alarms are always set + alarms[alarm] = 1 + else + // Alarms that were raised but then cleared before the next notice are instead removed + if(alarm in alarms) + alarms -= alarm + // And alarms that have only been cleared thus far are set as such + else + alarms[alarm] = -1 + +/mob/living/silicon/proc/process_queued_alarms() + if(next_alarm_notice && (world.time > next_alarm_notice)) + next_alarm_notice = 0 + + var/alarm_raised = 0 + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == 1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Detected ---") + raised_alarm(A) + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + var/reported = 0 + for(var/datum/alarm/A in alarms) + if(alarms[A] == -1) + if(!reported) + reported = 1 + to_chat(src, "--- [AH.category] Cleared ---") + to_chat(src, "\The [A.alarm_name()].") + + if(alarm_raised) + to_chat(src, "\[Show Alerts\]") + + for(var/datum/alarm_handler/AH in queued_alarms) + var/list/alarms = queued_alarms[AH] + alarms.Cut() + +/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A) + to_chat(src, "[A.alarm_name()]!") + +/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A) + var/cameratext = "" + for(var/obj/machinery/camera/C in A.cameras()) + cameratext += "[(cameratext == "")? "" : "|"][C.c_tag]" + to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])") + +/mob/living/silicon/adjustToxLoss(var/amount) + return STATUS_UPDATE_NONE + +/mob/living/silicon/get_access() + return IGNORE_ACCESS //silicons always have access + +/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /obj/screen/fullscreen/flash/noise) + if(affect_silicon) + return ..() + +/mob/living/silicon/is_mechanical() + return 1 + +/mob/living/silicon/is_literate() + return 1 + +/////////////////////////////////// EAR DAMAGE //////////////////////////////////// +/mob/living/silicon/can_hear() + . = TRUE + diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm index 8f7c2316768f..5f00beff1e48 100644 --- a/code/modules/mob/living/silicon/subsystems.dm +++ b/code/modules/mob/living/silicon/subsystems.dm @@ -20,7 +20,7 @@ /mob/living/silicon/proc/subsystem_law_manager, /mob/living/silicon/proc/subsystem_power_monitor ) - + /mob/living/silicon/robot/drone silicon_subsystems = list( /mob/living/silicon/proc/subsystem_alarm_monitor, @@ -54,8 +54,8 @@ set name = "Alarm Monitor" set category = "Subsystems" - alarm_monitor.ui_interact(usr, state = self_state) - + alarm_monitor.ui_interact(usr, state = GLOB.self_state) + /******************** * Atmos Control * ********************/ @@ -63,7 +63,7 @@ set category = "Subsystems" set name = "Atmospherics Control" - atmos_control.ui_interact(usr, state = self_state) + atmos_control.ui_interact(usr, state = GLOB.self_state) /******************** * Crew Monitor * @@ -72,8 +72,8 @@ set category = "Subsystems" set name = "Crew Monitor" - crew_monitor.ui_interact(usr, state = self_state) - + crew_monitor.ui_interact(usr, state = GLOB.self_state) + /**************** * Law Manager * ****************/ @@ -81,8 +81,8 @@ set name = "Law Manager" set category = "Subsystems" - law_manager.ui_interact(usr, state = conscious_state) - + law_manager.ui_interact(usr, state = GLOB.conscious_state) + /******************** * Power Monitor * ********************/ @@ -90,5 +90,5 @@ set category = "Subsystems" set name = "Power Monitor" - power_monitor.ui_interact(usr, state = self_state) + power_monitor.ui_interact(usr, state = GLOB.self_state) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 6e94accb1003..2cfc5e746dc9 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -124,4 +124,4 @@ visual_effect_icon = ATTACK_EFFECT_PUNCH else visual_effect_icon = ATTACK_EFFECT_SMASH - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 29fc03a20b30..ede055dac43a 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -160,7 +160,7 @@ SSradio.add_object(bot_core, control_freq, bot_filter) prepare_huds() - for(var/datum/atom_hud/data/diagnostic/diag_hud in huds) + for(var/datum/atom_hud/data/diagnostic/diag_hud in GLOB.huds) diag_hud.add_to_hud(src) diag_hud.add_hud_to(src) permanent_huds |= diag_hud @@ -391,7 +391,7 @@ pulse2.icon_state = "empdisable" pulse2.name = "emp sparks" pulse2.anchored = 1 - pulse2.dir = pick(cardinal) + pulse2.dir = pick(GLOB.cardinal) QDEL_IN(pulse2, 10) if(paicard) @@ -1051,7 +1051,7 @@ Pass a positive integer as an argument to override a bot's default speed. path = newpath ? newpath : list() if(!path_hud) return - var/list/path_huds_watching_me = list(huds[DATA_HUD_DIAGNOSTIC_ADVANCED]) + var/list/path_huds_watching_me = list(GLOB.huds[DATA_HUD_DIAGNOSTIC_ADVANCED]) if(path_hud) path_huds_watching_me += path_hud for(var/V in path_huds_watching_me) @@ -1072,7 +1072,7 @@ Pass a positive integer as an argument to override a bot's default speed. var/turf/prevprevT = path[i - 2] var/prevDir = get_dir(prevprevT, prevT) var/mixDir = direction|prevDir - if(mixDir in diagonals) + if(mixDir in GLOB.diagonals) prevI.dir = mixDir if(prevDir & (NORTH|SOUTH)) var/matrix/ntransform = matrix() @@ -1109,4 +1109,4 @@ Pass a positive integer as an argument to override a bot's default speed. path.Cut(1, 2) /mob/living/simple_animal/bot/proc/drop_part(obj/item/drop_item, dropzone) - new drop_item(dropzone) \ No newline at end of file + new drop_item(dropzone) diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index ea4f8e58a695..024e2b5f304f 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -1,5 +1,4 @@ //Bot Construction -var/robot_arm = /obj/item/robot_parts/l_arm //Cleanbot assembly /obj/item/bucket_sensor @@ -13,6 +12,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm throw_range = 5 w_class = WEIGHT_CLASS_NORMAL var/created_name = "Cleanbot" + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/bucket_sensor/attackby(obj/item/W, mob/user as mob, params) ..() @@ -350,6 +350,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm var/treatment_fire = "salglu_solution" var/treatment_tox = "charcoal" var/treatment_virus = "spaceacillin" + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/firstaid_arm_assembly/New(loc, new_skin) ..() @@ -419,6 +420,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm item_state = "helmet" var/created_name = "Securitron" //To preserve the name if it's a unique securitron I guess var/build_step = 0 + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/clothing/head/helmet/attackby(obj/item/assembly/signaler/S, mob/user, params) ..() @@ -596,6 +598,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME) var/build_step = 0 var/created_name = "Honkbot" //To preserve the name if it's a unique medbot I guess + var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/honkbot_arm_assembly/attackby(obj/item/W, mob/user, params) ..() @@ -623,4 +626,4 @@ var/robot_arm = /obj/item/robot_parts/l_arm qdel(W) var/mob/living/simple_animal/bot/honkbot/A = new /mob/living/simple_animal/bot/honkbot(get_turf(src)) A.robot_arm = robot_arm - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 410f97153256..faaa65a46c48 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -68,7 +68,7 @@ name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") //SECHUD - var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED] + var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] secsensor.add_hud_to(src) permanent_huds |= secsensor @@ -473,7 +473,7 @@ pulse2.icon_state = "empdisable" pulse2.name = "emp sparks" pulse2.anchored = 1 - pulse2.dir = pick(cardinal) + pulse2.dir = pick(GLOB.cardinal) spawn(10) qdel(pulse2) var/list/mob/living/carbon/targets = new diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index f15eaa19db16..8b0878f71155 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -240,4 +240,4 @@ else return ..() else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 7f889517d34d..d9097a82d2ed 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -317,4 +317,4 @@ speak("Honk!") sensor_blink() return - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index b7696236e38c..5246a536d92c 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -123,7 +123,7 @@ prev_access = access_card.access qdel(J) - var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/medsensor = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] medsensor.add_hud_to(src) permanent_huds |= medsensor diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index eb629680a615..ecdb8e3f0fe4 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -90,7 +90,7 @@ prev_access = access_card.access //SECHUD - var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED] + var/datum/atom_hud/secsensor = GLOB.huds[DATA_HUD_SECURITY_ADVANCED] secsensor.add_hud_to(src) permanent_huds |= secsensor diff --git a/code/modules/mob/living/simple_animal/bot/syndicate.dm b/code/modules/mob/living/simple_animal/bot/syndicate.dm index 24a9d3c2997d..3993339f7144 100644 --- a/code/modules/mob/living/simple_animal/bot/syndicate.dm +++ b/code/modules/mob/living/simple_animal/bot/syndicate.dm @@ -211,4 +211,4 @@ return raised_alert = TRUE if(depotarea) - depotarea.increase_alert(reason) \ No newline at end of file + depotarea.increase_alert(reason) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 9dce0cecb623..ee03c204d48d 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -1,405 +1,405 @@ - -/mob/living/simple_animal/hostile/construct - name = "Construct" - real_name = "Construct" - desc = "" - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - response_help = "thinks better of touching" - response_disarm = "flails at" - response_harm = "punches" - icon_dead = "shade_dead" - speed = 0 - a_intent = INTENT_HARM - stop_automated_movement = 1 - status_flags = CANPUSH - attack_sound = 'sound/weapons/punch1.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - faction = list("cult") - flying = 1 - pressure_resistance = 100 - universal_speak = 1 - AIStatus = AI_OFF //normal constructs don't have AI - var/const_type = "shade" - var/list/construct_spells = list() - var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." - loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) - del_on_death = 1 - deathmessage = "collapses in a shattered heap." - -/mob/living/simple_animal/hostile/construct/New() - . = ..() - if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way - name = "[const_type] ([rand(1, 1000)])" - real_name = const_type - icon_living = const_type - icon_state = const_type - else - name = "[SSticker.cultdat.get_name(const_type)] ([rand(1, 1000)])" - real_name = SSticker.cultdat.get_name(const_type) - icon_living = SSticker.cultdat.get_icon(const_type) - icon_state = SSticker.cultdat.get_icon(const_type) - - for(var/spell in construct_spells) - AddSpell(new spell(null)) - - if(SSticker.cultdat?.theme == "blood") - updateglow() - -/mob/living/simple_animal/hostile/construct/death(gibbed) - . = ..() - SSticker.mode.remove_cultist(src.mind, FALSE) - -/mob/living/simple_animal/hostile/construct/examine(mob/user) - . = ..() - - var/msg = "" - if(src.health < src.maxHealth) - msg += "" - if(src.health >= src.maxHealth/2) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - msg += "" - msg += "*---------*
    " - - . += msg - -/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) - if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) - if(health < maxHealth) - adjustBruteLoss(-5) - if(src != M) - Beam(M,icon_state="sendbeam",time=4) - M.visible_message("[M] repairs some of \the [src]'s dents.", \ - "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") - else - M.visible_message("[M] repairs some of its own dents.", \ - "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") - else - if(src != M) - to_chat(M, "You cannot repair [src]'s dents, as it has none!") - else - to_chat(M, "You cannot repair your own dents, as you have none!") - else if(src != M) - return ..() - - -/mob/living/simple_animal/hostile/construct/narsie_act() - return - -/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE - -/////////////////Juggernaut/////////////// - - - -/mob/living/simple_animal/hostile/construct/armoured - name = "Juggernaut" - real_name = "Juggernaut" - desc = "A possessed suit of armour driven by the will of the restless dead" - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 250 - health = 250 - response_harm = "harmlessly punches" - harm_intent_damage = 0 - obj_damage = 90 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "smashes their armoured gauntlet into" - speed = 3 - environment_smash = 2 - attack_sound = 'sound/weapons/punch3.ogg' - status_flags = 0 - const_type = "juggernaut" - mob_size = MOB_SIZE_LARGE - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) - force_threshold = 11 - playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ - create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." - - -/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) - if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) - if((P.damage_type == BRUTE || P.damage_type == BURN)) - adjustBruteLoss(P.damage * 0.5) - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") - - P.reflect_back(src, list(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)) - - return -1 // complete projectile permutation - - return (..(P)) - - - -////////////////////////Wraith///////////////////////////////////////////// - - - -/mob/living/simple_animal/hostile/construct/wraith - name = "Wraith" - real_name = "Wraith" - desc = "A wicked bladed shell contraption piloted by a bound spirit" - icon = 'icons/mob/mob.dmi' - icon_state = "floating" - icon_living = "floating" - maxHealth = 75 - health = 75 - melee_damage_lower = 25 - melee_damage_upper = 25 - attacktext = "slashes" - see_in_dark = 8 - attack_sound = 'sound/weapons/bladeslice.ogg' - const_type = "wraith" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) - retreat_distance = 2 //AI wraiths will move in and out of combat - playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." - -/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - -/////////////////////////////Artificer///////////////////////// - - - -/mob/living/simple_animal/hostile/construct/builder - name = "Artificer" - real_name = "Artificer" - desc = "A bulbous construct dedicated to building and maintaining Cult armies." - icon = 'icons/mob/mob.dmi' - icon_state = "artificer" - icon_living = "artificer" - maxHealth = 50 - health = 50 - response_harm = "viciously beats" - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 5 - melee_damage_upper = 5 - attacktext = "rams" - environment_smash = 2 - retreat_distance = 10 - minimum_distance = 10 //AI artificers will flee like fuck - attack_sound = 'sound/weapons/punch2.ogg' - const_type = "builder" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, - /obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, - /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, - /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, - /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, - /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) - - playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ - use magic missile, repair allied constructs (by clicking on them), \ - and, most important of all, create new constructs by producing soulstones to capture souls, \ - and shells to place those soulstones into." - - -/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? - if(isconstruct(A)) //is it a construct? - var/mob/living/simple_animal/hostile/construct/C = A - if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is - return 1 - else - return 0 - else - return 0 - -/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it - return 0 - if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible - return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs - -/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) - ..() - if(isliving(target)) - var/mob/living/L = target - if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it - LoseTarget() - return 0 - if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/Aggro() - ..() - if(isconstruct(target)) //oh the target is a construct no need to flee - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/LoseAggro() - ..() - retreat_distance = initial(retreat_distance) - minimum_distance = initial(minimum_distance) - -/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs - AIStatus = AI_ON - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP - - -/////////////////////////////Behemoth///////////////////////// - - -/mob/living/simple_animal/hostile/construct/behemoth - name = "Behemoth" - real_name = "Behemoth" - desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 750 - health = 750 - speak_emote = list("rumbles") - response_harm = "harmlessly punches" - harm_intent_damage = 0 - melee_damage_lower = 50 - melee_damage_upper = 50 - attacktext = "brutally crushes" - speed = 5 - environment_smash = 2 - attack_sound = 'sound/weapons/punch4.ogg' - force_threshold = 11 - const_type = "behemoth" - var/energy = 0 - var/max_energy = 1000 - -/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/behemoth/Life(seconds, times_fired) - weakened = 0 - return ..() - - -/////////////////////////////Harvester///////////////////////// - -/mob/living/simple_animal/hostile/construct/harvester - name = "Harvester" - real_name = "Harvester" - desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." - icon = 'icons/mob/mob.dmi' - icon_state = "harvester" - icon_living = "harvester" - maxHealth = 60 - health = 60 - melee_damage_lower = 1 - melee_damage_upper = 5 - attacktext = "prods" - environment_smash = ENVIRONMENT_SMASH_RWALLS - see_in_dark = 8 - attack_sound = 'sound/weapons/tap.ogg' - const_type = "harvester" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, - /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, - /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/targeted/smoke/disable) - retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier - playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ - Bring those who still cling to this world of illusion back to the master so they may know Truth." - - -/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) - return 1 - - -/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -////////////////Glow//////////////////// -/mob/living/simple_animal/hostile/construct/proc/updateglow() - overlays = 0 - var/overlay_layer = LIGHTING_LAYER + 1 - if(layer != MOB_LAYER) - overlay_layer=TURF_LAYER+0.2 - - overlays += image(icon,"glow-[icon_state]",overlay_layer) - set_light(2, -2, l_color = "#FFFFFF") - -///ui stuff - -/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(250 to INFINITY) healths.icon_state = "juggernaut_health0" - if(208 to 249) healths.icon_state = "juggernaut_health1" - if(167 to 207) healths.icon_state = "juggernaut_health2" - if(125 to 166) healths.icon_state = "juggernaut_health3" - if(84 to 124) healths.icon_state = "juggernaut_health4" - if(42 to 83) healths.icon_state = "juggernaut_health5" - if(1 to 41) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - - -/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(750 to INFINITY) healths.icon_state = "juggernaut_health0" - if(625 to 749) healths.icon_state = "juggernaut_health1" - if(500 to 624) healths.icon_state = "juggernaut_health2" - if(375 to 499) healths.icon_state = "juggernaut_health3" - if(250 to 374) healths.icon_state = "juggernaut_health4" - if(125 to 249) healths.icon_state = "juggernaut_health5" - if(1 to 124) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - -/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(50 to INFINITY) healths.icon_state = "artificer_health0" - if(42 to 49) healths.icon_state = "artificer_health1" - if(34 to 41) healths.icon_state = "artificer_health2" - if(26 to 33) healths.icon_state = "artificer_health3" - if(18 to 25) healths.icon_state = "artificer_health4" - if(10 to 17) healths.icon_state = "artificer_health5" - if(1 to 9) healths.icon_state = "artificer_health6" - else healths.icon_state = "artificer_health7" - - - -/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(75 to INFINITY) healths.icon_state = "wraith_health0" - if(62 to 74) healths.icon_state = "wraith_health1" - if(50 to 61) healths.icon_state = "wraith_health2" - if(37 to 49) healths.icon_state = "wraith_health3" - if(25 to 36) healths.icon_state = "wraith_health4" - if(12 to 24) healths.icon_state = "wraith_health5" - if(1 to 11) healths.icon_state = "wraith_health6" - else healths.icon_state = "wraith_health7" - - -/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(150 to INFINITY) healths.icon_state = "harvester_health0" - if(125 to 149) healths.icon_state = "harvester_health1" - if(100 to 124) healths.icon_state = "harvester_health2" - if(75 to 99) healths.icon_state = "harvester_health3" - if(50 to 74) healths.icon_state = "harvester_health4" - if(25 to 49) healths.icon_state = "harvester_health5" - if(1 to 24) healths.icon_state = "harvester_health6" - else healths.icon_state = "harvester_health7" \ No newline at end of file + +/mob/living/simple_animal/hostile/construct + name = "Construct" + real_name = "Construct" + desc = "" + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "thinks better of touching" + response_disarm = "flails at" + response_harm = "punches" + icon_dead = "shade_dead" + speed = 0 + a_intent = INTENT_HARM + stop_automated_movement = 1 + status_flags = CANPUSH + attack_sound = 'sound/weapons/punch1.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + faction = list("cult") + flying = 1 + pressure_resistance = 100 + universal_speak = 1 + AIStatus = AI_OFF //normal constructs don't have AI + var/const_type = "shade" + var/list/construct_spells = list() + var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) + del_on_death = 1 + deathmessage = "collapses in a shattered heap." + +/mob/living/simple_animal/hostile/construct/New() + . = ..() + if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way + name = "[const_type] ([rand(1, 1000)])" + real_name = const_type + icon_living = const_type + icon_state = const_type + else + name = "[SSticker.cultdat.get_name(const_type)] ([rand(1, 1000)])" + real_name = SSticker.cultdat.get_name(const_type) + icon_living = SSticker.cultdat.get_icon(const_type) + icon_state = SSticker.cultdat.get_icon(const_type) + + for(var/spell in construct_spells) + AddSpell(new spell(null)) + + if(SSticker.cultdat?.theme == "blood") + updateglow() + +/mob/living/simple_animal/hostile/construct/death(gibbed) + . = ..() + SSticker.mode.remove_cultist(src.mind, FALSE) + +/mob/living/simple_animal/hostile/construct/examine(mob/user) + . = ..() + + var/msg = "" + if(src.health < src.maxHealth) + msg += "" + if(src.health >= src.maxHealth/2) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + msg += "" + msg += "*---------*
    " + + . += msg + +/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) + if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) + if(health < maxHealth) + adjustBruteLoss(-5) + if(src != M) + Beam(M,icon_state="sendbeam",time=4) + M.visible_message("[M] repairs some of \the [src]'s dents.", \ + "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") + else + M.visible_message("[M] repairs some of its own dents.", \ + "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") + else + if(src != M) + to_chat(M, "You cannot repair [src]'s dents, as it has none!") + else + to_chat(M, "You cannot repair your own dents, as you have none!") + else if(src != M) + return ..() + + +/mob/living/simple_animal/hostile/construct/narsie_act() + return + +/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE + +/////////////////Juggernaut/////////////// + + + +/mob/living/simple_animal/hostile/construct/armoured + name = "Juggernaut" + real_name = "Juggernaut" + desc = "A possessed suit of armour driven by the will of the restless dead" + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 250 + health = 250 + response_harm = "harmlessly punches" + harm_intent_damage = 0 + obj_damage = 90 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "smashes their armoured gauntlet into" + speed = 3 + environment_smash = 2 + attack_sound = 'sound/weapons/punch3.ogg' + status_flags = 0 + const_type = "juggernaut" + mob_size = MOB_SIZE_LARGE + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) + force_threshold = 11 + playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ + create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." + + +/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflectchance = 80 - round(P.damage/3) + if(prob(reflectchance)) + if((P.damage_type == BRUTE || P.damage_type == BURN)) + adjustBruteLoss(P.damage * 0.5) + visible_message("The [P.name] gets reflected by [src]'s shell!", \ + "The [P.name] gets reflected by [src]'s shell!") + + P.reflect_back(src, list(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)) + + return -1 // complete projectile permutation + + return (..(P)) + + + +////////////////////////Wraith///////////////////////////////////////////// + + + +/mob/living/simple_animal/hostile/construct/wraith + name = "Wraith" + real_name = "Wraith" + desc = "A wicked bladed shell contraption piloted by a bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "floating" + icon_living = "floating" + maxHealth = 75 + health = 75 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + see_in_dark = 8 + attack_sound = 'sound/weapons/bladeslice.ogg' + const_type = "wraith" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) + retreat_distance = 2 //AI wraiths will move in and out of combat + playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." + +/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + +/////////////////////////////Artificer///////////////////////// + + + +/mob/living/simple_animal/hostile/construct/builder + name = "Artificer" + real_name = "Artificer" + desc = "A bulbous construct dedicated to building and maintaining Cult armies." + icon = 'icons/mob/mob.dmi' + icon_state = "artificer" + icon_living = "artificer" + maxHealth = 50 + health = 50 + response_harm = "viciously beats" + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "rams" + environment_smash = 2 + retreat_distance = 10 + minimum_distance = 10 //AI artificers will flee like fuck + attack_sound = 'sound/weapons/punch2.ogg' + const_type = "builder" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, + /obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, + /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, + /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) + + playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ + use magic missile, repair allied constructs (by clicking on them), \ + and, most important of all, create new constructs by producing soulstones to capture souls, \ + and shells to place those soulstones into." + + +/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? + if(isconstruct(A)) //is it a construct? + var/mob/living/simple_animal/hostile/construct/C = A + if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is + return 1 + else + return 0 + else + return 0 + +/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it + return 0 + if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible + return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs + +/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) + ..() + if(isliving(target)) + var/mob/living/L = target + if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it + LoseTarget() + return 0 + if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/Aggro() + ..() + if(isconstruct(target)) //oh the target is a construct no need to flee + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/LoseAggro() + ..() + retreat_distance = initial(retreat_distance) + minimum_distance = initial(minimum_distance) + +/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs + AIStatus = AI_ON + environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP + + +/////////////////////////////Behemoth///////////////////////// + + +/mob/living/simple_animal/hostile/construct/behemoth + name = "Behemoth" + real_name = "Behemoth" + desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 750 + health = 750 + speak_emote = list("rumbles") + response_harm = "harmlessly punches" + harm_intent_damage = 0 + melee_damage_lower = 50 + melee_damage_upper = 50 + attacktext = "brutally crushes" + speed = 5 + environment_smash = 2 + attack_sound = 'sound/weapons/punch4.ogg' + force_threshold = 11 + const_type = "behemoth" + var/energy = 0 + var/max_energy = 1000 + +/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/behemoth/Life(seconds, times_fired) + weakened = 0 + return ..() + + +/////////////////////////////Harvester///////////////////////// + +/mob/living/simple_animal/hostile/construct/harvester + name = "Harvester" + real_name = "Harvester" + desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." + icon = 'icons/mob/mob.dmi' + icon_state = "harvester" + icon_living = "harvester" + maxHealth = 60 + health = 60 + melee_damage_lower = 1 + melee_damage_upper = 5 + attacktext = "prods" + environment_smash = ENVIRONMENT_SMASH_RWALLS + see_in_dark = 8 + attack_sound = 'sound/weapons/tap.ogg' + const_type = "harvester" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/targeted/smoke/disable) + retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier + playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ + Bring those who still cling to this world of illusion back to the master so they may know Truth." + + +/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) + return 1 + + +/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +////////////////Glow//////////////////// +/mob/living/simple_animal/hostile/construct/proc/updateglow() + overlays = 0 + var/overlay_layer = LIGHTING_LAYER + 1 + if(layer != MOB_LAYER) + overlay_layer=TURF_LAYER+0.2 + + overlays += image(icon,"glow-[icon_state]",overlay_layer) + set_light(2, -2, l_color = "#FFFFFF") + +///ui stuff + +/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(250 to INFINITY) healths.icon_state = "juggernaut_health0" + if(208 to 249) healths.icon_state = "juggernaut_health1" + if(167 to 207) healths.icon_state = "juggernaut_health2" + if(125 to 166) healths.icon_state = "juggernaut_health3" + if(84 to 124) healths.icon_state = "juggernaut_health4" + if(42 to 83) healths.icon_state = "juggernaut_health5" + if(1 to 41) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + + +/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(750 to INFINITY) healths.icon_state = "juggernaut_health0" + if(625 to 749) healths.icon_state = "juggernaut_health1" + if(500 to 624) healths.icon_state = "juggernaut_health2" + if(375 to 499) healths.icon_state = "juggernaut_health3" + if(250 to 374) healths.icon_state = "juggernaut_health4" + if(125 to 249) healths.icon_state = "juggernaut_health5" + if(1 to 124) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + +/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(50 to INFINITY) healths.icon_state = "artificer_health0" + if(42 to 49) healths.icon_state = "artificer_health1" + if(34 to 41) healths.icon_state = "artificer_health2" + if(26 to 33) healths.icon_state = "artificer_health3" + if(18 to 25) healths.icon_state = "artificer_health4" + if(10 to 17) healths.icon_state = "artificer_health5" + if(1 to 9) healths.icon_state = "artificer_health6" + else healths.icon_state = "artificer_health7" + + + +/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(75 to INFINITY) healths.icon_state = "wraith_health0" + if(62 to 74) healths.icon_state = "wraith_health1" + if(50 to 61) healths.icon_state = "wraith_health2" + if(37 to 49) healths.icon_state = "wraith_health3" + if(25 to 36) healths.icon_state = "wraith_health4" + if(12 to 24) healths.icon_state = "wraith_health5" + if(1 to 11) healths.icon_state = "wraith_health6" + else healths.icon_state = "wraith_health7" + + +/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(150 to INFINITY) healths.icon_state = "harvester_health0" + if(125 to 149) healths.icon_state = "harvester_health1" + if(100 to 124) healths.icon_state = "harvester_health2" + if(75 to 99) healths.icon_state = "harvester_health3" + if(50 to 74) healths.icon_state = "harvester_health4" + if(25 to 49) healths.icon_state = "harvester_health5" + if(1 to 24) healths.icon_state = "harvester_health6" + else healths.icon_state = "harvester_health7" diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index ca54a3592ecd..10460d11e655 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -1,115 +1,115 @@ -//List of different corpse types -/obj/effect/mob_spawn/human/corpse/syndicatesoldier - name = "Syndicate Operative" - mob_name = "Syndicate Operative" - hair_style = "bald" - facial_hair_style = "shaved" - id_job = "Operative" - id_access_list = list(ACCESS_SYNDICATE) - outfit = /datum/outfit/syndicatesoldiercorpse - -/datum/outfit/syndicatesoldiercorpse - name = "Syndicate Operative Corpse" - uniform = /obj/item/clothing/under/syndicate - suit = /obj/item/clothing/suit/armor/vest - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas - head = /obj/item/clothing/head/helmet/swat - back = /obj/item/storage/backpack - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/corpse/syndicatecommando - name = "Syndicate Commando" - mob_name = "Syndicate Commando" - hair_style = "bald" - facial_hair_style = "shaved" - id_job = "Operative" - id_access_list = list(ACCESS_SYNDICATE) - outfit = /datum/outfit/syndicatecommandocorpse - -/datum/outfit/syndicatecommandocorpse - name = "Syndicate Commando Corpse" - uniform = /obj/item/clothing/under/syndicate - suit = /obj/item/clothing/suit/space/hardsuit/syndi - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas/syndicate - back = /obj/item/tank/jetpack/oxygen - r_pocket = /obj/item/tank/emergency_oxygen - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/clown/corpse - roundstart = TRUE - instant = TRUE - -/obj/effect/mob_spawn/human/mime/corpse - roundstart = TRUE - instant = TRUE - -/obj/effect/mob_spawn/human/corpse/pirate - name = "Pirate" - mob_name = "Pirate" - hair_style = "bald" - facial_hair_style = "shaved" - outfit = /datum/outfit/piratecorpse - -/datum/outfit/piratecorpse - name = "Pirate Corpse" - uniform = /obj/item/clothing/under/pirate - shoes = /obj/item/clothing/shoes/jackboots - glasses = /obj/item/clothing/glasses/eyepatch - head = /obj/item/clothing/head/bandana - - -/obj/effect/mob_spawn/human/corpse/pirate/ranged - name = "Pirate Gunner" - mob_name = "Pirate Gunner" - outfit = /datum/outfit/piratecorpse/ranged - -/datum/outfit/piratecorpse/ranged - name = "Pirate Gunner Corpse" - suit = /obj/item/clothing/suit/pirate_black - head = /obj/item/clothing/head/pirate - - -/obj/effect/mob_spawn/human/corpse/russian - name = "Russian" - mob_name = "Russian" - hair_style = "bald" - facial_hair_style = "shaved" - outfit = /datum/outfit/russiancorpse - -/datum/outfit/russiancorpse - name = "Russian Corpse" - uniform = /obj/item/clothing/under/soviet - shoes = /obj/item/clothing/shoes/jackboots - head = /obj/item/clothing/head/bearpelt - - -/obj/effect/mob_spawn/human/corpse/russian/ranged - outfit = /datum/outfit/russiancorpse/ranged - -/datum/outfit/russiancorpse/ranged - name = "Ranged Russian Corpse" - head = /obj/item/clothing/head/ushanka - - -/obj/effect/mob_spawn/human/corpse/wizard - name = "Space Wizard Corpse" - outfit = /datum/outfit/wizardcorpse - -/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() - mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" - ..() - -/datum/outfit/wizardcorpse - name = "Space Wizard Corpse" - uniform = /obj/item/clothing/under/color/lightpurple - suit = /obj/item/clothing/suit/wizrobe - shoes = /obj/item/clothing/shoes/sandal - head = /obj/item/clothing/head/wizard +//List of different corpse types +/obj/effect/mob_spawn/human/corpse/syndicatesoldier + name = "Syndicate Operative" + mob_name = "Syndicate Operative" + hair_style = "bald" + facial_hair_style = "shaved" + id_job = "Operative" + id_access_list = list(ACCESS_SYNDICATE) + outfit = /datum/outfit/syndicatesoldiercorpse + +/datum/outfit/syndicatesoldiercorpse + name = "Syndicate Operative Corpse" + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/armor/vest + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas + head = /obj/item/clothing/head/helmet/swat + back = /obj/item/storage/backpack + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/corpse/syndicatecommando + name = "Syndicate Commando" + mob_name = "Syndicate Commando" + hair_style = "bald" + facial_hair_style = "shaved" + id_job = "Operative" + id_access_list = list(ACCESS_SYNDICATE) + outfit = /datum/outfit/syndicatecommandocorpse + +/datum/outfit/syndicatecommandocorpse + name = "Syndicate Commando Corpse" + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/space/hardsuit/syndi + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas/syndicate + back = /obj/item/tank/jetpack/oxygen + r_pocket = /obj/item/tank/emergency_oxygen + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/clown/corpse + roundstart = TRUE + instant = TRUE + +/obj/effect/mob_spawn/human/mime/corpse + roundstart = TRUE + instant = TRUE + +/obj/effect/mob_spawn/human/corpse/pirate + name = "Pirate" + mob_name = "Pirate" + hair_style = "bald" + facial_hair_style = "shaved" + outfit = /datum/outfit/piratecorpse + +/datum/outfit/piratecorpse + name = "Pirate Corpse" + uniform = /obj/item/clothing/under/pirate + shoes = /obj/item/clothing/shoes/jackboots + glasses = /obj/item/clothing/glasses/eyepatch + head = /obj/item/clothing/head/bandana + + +/obj/effect/mob_spawn/human/corpse/pirate/ranged + name = "Pirate Gunner" + mob_name = "Pirate Gunner" + outfit = /datum/outfit/piratecorpse/ranged + +/datum/outfit/piratecorpse/ranged + name = "Pirate Gunner Corpse" + suit = /obj/item/clothing/suit/pirate_black + head = /obj/item/clothing/head/pirate + + +/obj/effect/mob_spawn/human/corpse/russian + name = "Russian" + mob_name = "Russian" + hair_style = "bald" + facial_hair_style = "shaved" + outfit = /datum/outfit/russiancorpse + +/datum/outfit/russiancorpse + name = "Russian Corpse" + uniform = /obj/item/clothing/under/soviet + shoes = /obj/item/clothing/shoes/jackboots + head = /obj/item/clothing/head/bearpelt + + +/obj/effect/mob_spawn/human/corpse/russian/ranged + outfit = /datum/outfit/russiancorpse/ranged + +/datum/outfit/russiancorpse/ranged + name = "Ranged Russian Corpse" + head = /obj/item/clothing/head/ushanka + + +/obj/effect/mob_spawn/human/corpse/wizard + name = "Space Wizard Corpse" + outfit = /datum/outfit/wizardcorpse + +/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() + mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" + ..() + +/datum/outfit/wizardcorpse + name = "Space Wizard Corpse" + uniform = /obj/item/clothing/under/color/lightpurple + suit = /obj/item/clothing/suit/wizrobe + shoes = /obj/item/clothing/shoes/sandal + head = /obj/item/clothing/head/wizard diff --git a/code/modules/mob/living/simple_animal/damage_procs.dm b/code/modules/mob/living/simple_animal/damage_procs.dm index 6899b40bea0d..6ba69de6a613 100644 --- a/code/modules/mob/living/simple_animal/damage_procs.dm +++ b/code/modules/mob/living/simple_animal/damage_procs.dm @@ -34,4 +34,4 @@ /mob/living/simple_animal/adjustStaminaLoss(amount, updating_health = TRUE) if(damage_coeff[STAMINA]) - return ..(amount*damage_coeff[STAMINA], updating_health) \ No newline at end of file + return ..(amount*damage_coeff[STAMINA], updating_health) diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index f552bb5cebd9..24818d076521 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -1,279 +1,279 @@ -//Cat -/mob/living/simple_animal/pet/cat - name = "cat" - desc = "Kitty!!" - icon_state = "cat2" - icon_living = "cat2" - icon_dead = "cat2_dead" - icon_resting = "cat2_rest" - gender = MALE - speak = list("Meow!", "Esp!", "Purr!", "HSSSSS") - speak_emote = list("purrs", "meows") - emote_hear = list("meows", "mews") - emote_see = list("shakes its head", "shivers") - var/meow_sound = 'sound/creatures/cat_meow.ogg' //Used in emote. - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - mob_size = MOB_SIZE_SMALL - animal_species = /mob/living/simple_animal/pet/cat - childtype = list(/mob/living/simple_animal/pet/cat/kitten) - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 3) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - gold_core_spawnable = FRIENDLY_SPAWN - collar_type = "cat" - var/turns_since_scan = 0 - var/mob/living/simple_animal/mouse/movement_target - var/eats_mice = 1 - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_animal/pet/cat/Runtime - name = "Runtime" - desc = "GCAT" - icon_state = "cat" - icon_living = "cat" - icon_dead = "cat_dead" - icon_resting = "cat_rest" - gender = FEMALE - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - var/list/family = list() - var/memory_saved = 0 - var/list/children = list() //Actual mob instances of children - var/cats_deployed = 0 - -/mob/living/simple_animal/pet/cat/Runtime/New() - Read_Memory() - ..() - -/mob/living/simple_animal/pet/cat/Runtime/Life(seconds, times_fired) - if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) - Deploy_The_Cats() - if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) - Write_Memory() - ..() - -/mob/living/simple_animal/pet/cat/Runtime/make_babies() - var/mob/baby = ..() - if(baby) - children += baby - return baby - -/mob/living/simple_animal/pet/cat/Runtime/death() - if(can_die() && !memory_saved) - Write_Memory(1) - return ..() - -/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - S["family"] >> family - - if(isnull(family)) - family = list() - -/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - family = list() - if(!dead) - for(var/mob/living/simple_animal/pet/cat/kitten/C in children) - if(istype(C,type) || C.stat || !C.z || !C.butcher_results) - continue - if(C.type in family) - family[C.type] += 1 - else - family[C.type] = 1 - S["family"] << family - memory_saved = 1 - -/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() - cats_deployed = 1 - for(var/cat_type in family) - if(family[cat_type] > 0) - for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS) - new cat_type(loc) - - -/mob/living/simple_animal/pet/cat/Life() - ..() - make_babies() - - -/mob/living/simple_animal/pet/cat/handle_automated_action() - if(!stat && !buckled) - if(prob(1)) - custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down.")) - StartResting() - else if(prob(1)) - custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert.")) - icon_state = "[icon_living]_sit" - collar_type = "[initial(collar_type)]_sit" - resting = TRUE - update_canmove() - else if(prob(1)) - if(resting) - custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting.")) - StopResting() - else - custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat.")) - - //MICE! - if(eats_mice && isturf(loc) && !incapacitated()) - for(var/mob/living/simple_animal/mouse/M in view(1, src)) - if(!M.stat && Adjacent(M)) - custom_emote(1, "splats \the [M]!") - M.splat() - movement_target = null - stop_automated_movement = 0 - break - for(var/obj/item/toy/cattoy/T in view(1, src)) - if(T.cooldown < (world.time - 400)) - custom_emote(1, "bats \the [T] around with its paw!") - T.cooldown = world.time - -/mob/living/simple_animal/pet/cat/handle_automated_movement() - . = ..() - if(!stat && !resting && !buckled) - turns_since_scan++ - if(turns_since_scan > 5) - walk_to(src,0) - turns_since_scan = 0 - if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) - movement_target = null - stop_automated_movement = 0 - if( !movement_target || !(movement_target.loc in oview(src, 3)) ) - movement_target = null - stop_automated_movement = 0 - for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) - if(isturf(snack.loc) && !snack.stat) - movement_target = snack - break - if(movement_target) - stop_automated_movement = 1 - walk_to(src,movement_target,0,3) - -/mob/living/simple_animal/pet/cat/emote(act, m_type = 1, message = null, force) - if(stat != CONSCIOUS) - return - - var/on_CD = 0 - act = lowertext(act) - switch(act) - if("meow") - on_CD = handle_emote_CD() - if("hiss") - on_CD = handle_emote_CD() - if("purr") - on_CD = handle_emote_CD() - else - on_CD = 0 - - if(!force && on_CD == 1) - return - - switch(act) - if("meow") - message = "[src] [pick(emote_hear)]!" - m_type = 2 //audible - playsound(src, meow_sound, 50, 0.75) - if("hiss") - message = "[src] hisses!" - m_type = 2 - if("purr") - message = "[src] purrs." - m_type = 2 - if("help") - to_chat(src, "scream, meow, hiss, purr") - - ..() - -/mob/living/simple_animal/pet/cat/Proc - name = "Proc" - gender = MALE - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - -/mob/living/simple_animal/pet/cat/kitten - name = "kitten" - desc = "D'aaawwww" - icon_state = "kitten" - icon_living = "kitten" - icon_dead = "kitten_dead" - icon_resting = null - gender = NEUTER - density = 0 - pass_flags = PASSMOB - collar_type = "kitten" - -/mob/living/simple_animal/pet/cat/Syndi - name = "SyndiCat" - desc = "It's a SyndiCat droid." - icon_state = "Syndicat" - icon_living = "Syndicat" - icon_dead = "Syndicat_dead" - icon_resting = "Syndicat_rest" - meow_sound = null //Need robo-meow. - gender = FEMALE - mutations = list(BREATHLESS) - faction = list("syndicate") - gold_core_spawnable = NO_SPAWN - eats_mice = 0 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - melee_damage_lower = 5 - melee_damage_upper = 15 - -/mob/living/simple_animal/pet/cat/cak - name = "Keeki" - desc = "It's a cat made out of cake." - icon_state = "cak" - icon_living = "cak" - icon_resting = "cak_rest" - icon_dead = "cak_dead" - health = 50 - maxHealth = 50 - harm_intent_damage = 10 - butcher_results = list( - /obj/item/organ/internal/brain = 1, - /obj/item/organ/internal/heart = 1, - /obj/item/reagent_containers/food/snacks/birthdaycakeslice = 3, - /obj/item/reagent_containers/food/snacks/meat/slab = 2 - ) - response_harm = "takes a bite out of" - attacked_sound = "sound/items/eatfood.ogg" - deathmessage = "loses its false life and collapses!" - death_sound = "bodyfall" - -/mob/living/simple_animal/pet/cat/cak/Life() - ..() - if(stat) - return - if(health < maxHealth) - adjustBruteLoss(-4) - for(var/obj/item/reagent_containers/food/snacks/donut/D in range(1, src)) - if(D.icon_state != "donut2") - D.name = "frosted donut" - D.icon_state = "donut2" - D.reagents.add_reagent("sprinkles", 2) - D.filling_color = "#FF69B4" - -/mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) - ..() - if(L.a_intent == INTENT_HARM && L.reagents && !stat) - L.reagents.add_reagent("nutriment", 0.4) - L.reagents.add_reagent("vitamin", 0.4) - -/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) - ..() - var/obj/item/organ/internal/brain/B = locate(/obj/item/organ/internal/brain) in contents - if(!B || !B.brainmob || !B.brainmob.mind) - return - B.brainmob.mind.transfer_to(src) - to_chat(src, "You are a cak! You're a harmless cat/cake hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \ - so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \ - free cake to the station!") - var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Keeki.", "Name Change") - if(new_name) - to_chat(src, "Your name is now \"[new_name]\"!") - name = new_name \ No newline at end of file +//Cat +/mob/living/simple_animal/pet/cat + name = "cat" + desc = "Kitty!!" + icon_state = "cat2" + icon_living = "cat2" + icon_dead = "cat2_dead" + icon_resting = "cat2_rest" + gender = MALE + speak = list("Meow!", "Esp!", "Purr!", "HSSSSS") + speak_emote = list("purrs", "meows") + emote_hear = list("meows", "mews") + emote_see = list("shakes its head", "shivers") + var/meow_sound = 'sound/creatures/cat_meow.ogg' //Used in emote. + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + mob_size = MOB_SIZE_SMALL + animal_species = /mob/living/simple_animal/pet/cat + childtype = list(/mob/living/simple_animal/pet/cat/kitten) + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 3) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + gold_core_spawnable = FRIENDLY_SPAWN + collar_type = "cat" + var/turns_since_scan = 0 + var/mob/living/simple_animal/mouse/movement_target + var/eats_mice = 1 + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_animal/pet/cat/Runtime + name = "Runtime" + desc = "GCAT" + icon_state = "cat" + icon_living = "cat" + icon_dead = "cat_dead" + icon_resting = "cat_rest" + gender = FEMALE + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + var/list/family = list() + var/memory_saved = 0 + var/list/children = list() //Actual mob instances of children + var/cats_deployed = 0 + +/mob/living/simple_animal/pet/cat/Runtime/New() + Read_Memory() + ..() + +/mob/living/simple_animal/pet/cat/Runtime/Life(seconds, times_fired) + if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) + Deploy_The_Cats() + if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) + Write_Memory() + ..() + +/mob/living/simple_animal/pet/cat/Runtime/make_babies() + var/mob/baby = ..() + if(baby) + children += baby + return baby + +/mob/living/simple_animal/pet/cat/Runtime/death() + if(can_die() && !memory_saved) + Write_Memory(1) + return ..() + +/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() + var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + S["family"] >> family + + if(isnull(family)) + family = list() + +/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) + var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + family = list() + if(!dead) + for(var/mob/living/simple_animal/pet/cat/kitten/C in children) + if(istype(C,type) || C.stat || !C.z || !C.butcher_results) + continue + if(C.type in family) + family[C.type] += 1 + else + family[C.type] = 1 + S["family"] << family + memory_saved = 1 + +/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() + cats_deployed = 1 + for(var/cat_type in family) + if(family[cat_type] > 0) + for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS) + new cat_type(loc) + + +/mob/living/simple_animal/pet/cat/Life() + ..() + make_babies() + + +/mob/living/simple_animal/pet/cat/handle_automated_action() + if(!stat && !buckled) + if(prob(1)) + custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down.")) + StartResting() + else if(prob(1)) + custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert.")) + icon_state = "[icon_living]_sit" + collar_type = "[initial(collar_type)]_sit" + resting = TRUE + update_canmove() + else if(prob(1)) + if(resting) + custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting.")) + StopResting() + else + custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat.")) + + //MICE! + if(eats_mice && isturf(loc) && !incapacitated()) + for(var/mob/living/simple_animal/mouse/M in view(1, src)) + if(!M.stat && Adjacent(M)) + custom_emote(1, "splats \the [M]!") + M.splat() + movement_target = null + stop_automated_movement = 0 + break + for(var/obj/item/toy/cattoy/T in view(1, src)) + if(T.cooldown < (world.time - 400)) + custom_emote(1, "bats \the [T] around with its paw!") + T.cooldown = world.time + +/mob/living/simple_animal/pet/cat/handle_automated_movement() + . = ..() + if(!stat && !resting && !buckled) + turns_since_scan++ + if(turns_since_scan > 5) + walk_to(src,0) + turns_since_scan = 0 + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + stop_automated_movement = 0 + if( !movement_target || !(movement_target.loc in oview(src, 3)) ) + movement_target = null + stop_automated_movement = 0 + for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) + if(isturf(snack.loc) && !snack.stat) + movement_target = snack + break + if(movement_target) + stop_automated_movement = 1 + walk_to(src,movement_target,0,3) + +/mob/living/simple_animal/pet/cat/emote(act, m_type = 1, message = null, force) + if(stat != CONSCIOUS) + return + + var/on_CD = 0 + act = lowertext(act) + switch(act) + if("meow") + on_CD = handle_emote_CD() + if("hiss") + on_CD = handle_emote_CD() + if("purr") + on_CD = handle_emote_CD() + else + on_CD = 0 + + if(!force && on_CD == 1) + return + + switch(act) + if("meow") + message = "[src] [pick(emote_hear)]!" + m_type = 2 //audible + playsound(src, meow_sound, 50, 0.75) + if("hiss") + message = "[src] hisses!" + m_type = 2 + if("purr") + message = "[src] purrs." + m_type = 2 + if("help") + to_chat(src, "scream, meow, hiss, purr") + + ..() + +/mob/living/simple_animal/pet/cat/Proc + name = "Proc" + gender = MALE + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + +/mob/living/simple_animal/pet/cat/kitten + name = "kitten" + desc = "D'aaawwww" + icon_state = "kitten" + icon_living = "kitten" + icon_dead = "kitten_dead" + icon_resting = null + gender = NEUTER + density = 0 + pass_flags = PASSMOB + collar_type = "kitten" + +/mob/living/simple_animal/pet/cat/Syndi + name = "SyndiCat" + desc = "It's a SyndiCat droid." + icon_state = "Syndicat" + icon_living = "Syndicat" + icon_dead = "Syndicat_dead" + icon_resting = "Syndicat_rest" + meow_sound = null //Need robo-meow. + gender = FEMALE + mutations = list(BREATHLESS) + faction = list("syndicate") + gold_core_spawnable = NO_SPAWN + eats_mice = 0 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + melee_damage_lower = 5 + melee_damage_upper = 15 + +/mob/living/simple_animal/pet/cat/cak + name = "Keeki" + desc = "It's a cat made out of cake." + icon_state = "cak" + icon_living = "cak" + icon_resting = "cak_rest" + icon_dead = "cak_dead" + health = 50 + maxHealth = 50 + harm_intent_damage = 10 + butcher_results = list( + /obj/item/organ/internal/brain = 1, + /obj/item/organ/internal/heart = 1, + /obj/item/reagent_containers/food/snacks/birthdaycakeslice = 3, + /obj/item/reagent_containers/food/snacks/meat/slab = 2 + ) + response_harm = "takes a bite out of" + attacked_sound = "sound/items/eatfood.ogg" + deathmessage = "loses its false life and collapses!" + death_sound = "bodyfall" + +/mob/living/simple_animal/pet/cat/cak/Life() + ..() + if(stat) + return + if(health < maxHealth) + adjustBruteLoss(-4) + for(var/obj/item/reagent_containers/food/snacks/donut/D in range(1, src)) + if(D.icon_state != "donut2") + D.name = "frosted donut" + D.icon_state = "donut2" + D.reagents.add_reagent("sprinkles", 2) + D.filling_color = "#FF69B4" + +/mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) + ..() + if(L.a_intent == INTENT_HARM && L.reagents && !stat) + L.reagents.add_reagent("nutriment", 0.4) + L.reagents.add_reagent("vitamin", 0.4) + +/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) + ..() + var/obj/item/organ/internal/brain/B = locate(/obj/item/organ/internal/brain) in contents + if(!B || !B.brainmob || !B.brainmob.mind) + return + B.brainmob.mind.transfer_to(src) + to_chat(src, "You are a cak! You're a harmless cat/cake hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \ + so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \ + free cake to the station!") + var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Keeki.", "Name Change") + if(new_name) + to_chat(src, "Your name is now \"[new_name]\"!") + name = new_name diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 7186eb5f5bfb..4a51ce62d4e4 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -1,55 +1,55 @@ -//Look Sir, free crabs! -/mob/living/simple_animal/crab - name = "crab" - desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." - icon_state = "crab" - icon_living = "crab" - icon_dead = "crab_dead" - speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 1 - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - stop_automated_movement = 1 - friendly = "pinches" - ventcrawler = 2 - can_hide = 1 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/crab/handle_automated_movement() - //CRAB movement - if(!stat) - if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - var/east_vs_west = pick(4, 8) - if(Process_Spacemove(east_vs_west)) - Move(get_step(src, east_vs_west), east_vs_west) - -//COFFEE! SQUEEEEEEEEE! -/mob/living/simple_animal/crab/Coffee - name = "Coffee" - real_name = "Coffee" - desc = "It's Coffee, the other pet!" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - -/mob/living/simple_animal/crab/evil - name = "Evil Crab" - real_name = "Evil Crab" - desc = "Unnerving, isn't it? It has to be planning something nefarious..." - icon_state = "evilcrab" - icon_living = "evilcrab" - icon_dead = "evilcrab_dead" - response_help = "pokes" - response_disarm = "shoves" - response_harm = "stomps" - gold_core_spawnable = HOSTILE_SPAWN \ No newline at end of file +//Look Sir, free crabs! +/mob/living/simple_animal/crab + name = "crab" + desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." + icon_state = "crab" + icon_living = "crab" + icon_dead = "crab_dead" + speak_emote = list("clicks") + emote_hear = list("clicks") + emote_see = list("clacks") + speak_chance = 1 + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + stop_automated_movement = 1 + friendly = "pinches" + ventcrawler = 2 + can_hide = 1 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/crab/handle_automated_movement() + //CRAB movement + if(!stat) + if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + var/east_vs_west = pick(4, 8) + if(Process_Spacemove(east_vs_west)) + Move(get_step(src, east_vs_west), east_vs_west) + +//COFFEE! SQUEEEEEEEEE! +/mob/living/simple_animal/crab/Coffee + name = "Coffee" + real_name = "Coffee" + desc = "It's Coffee, the other pet!" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + +/mob/living/simple_animal/crab/evil + name = "Evil Crab" + real_name = "Evil Crab" + desc = "Unnerving, isn't it? It has to be planning something nefarious..." + icon_state = "evilcrab" + icon_living = "evilcrab" + icon_dead = "evilcrab_dead" + response_help = "pokes" + response_disarm = "shoves" + response_harm = "stomps" + gold_core_spawnable = HOSTILE_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/deer.dm b/code/modules/mob/living/simple_animal/friendly/deer.dm index 2c4c2eda9609..a10ec1b7b352 100644 --- a/code/modules/mob/living/simple_animal/friendly/deer.dm +++ b/code/modules/mob/living/simple_animal/friendly/deer.dm @@ -15,4 +15,4 @@ response_disarm = "gently pushes aside" response_harm = "kicks" can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN \ No newline at end of file + gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 355d756288b0..e8c108a11de1 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -145,7 +145,7 @@ if((stat != CONSCIOUS) || !isdiona(loc)) return FALSE var/mob/living/carbon/human/D = loc - T = get_turf(src) + var/turf/T = get_turf(src) if(!T) return FALSE to_chat(loc, "You feel a pang of loss as [src] splits away from your biomass.") @@ -283,4 +283,4 @@ if("help") to_chat(src, "scream, chirp") - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 3717493d26cf..4b29e7031161 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -762,4 +762,4 @@ spawn(0) for(var/i in list(1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2)) dir = i - sleep(1) \ No newline at end of file + sleep(1) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 6d6b2ced3abe..ea8e949f1c70 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -1,448 +1,448 @@ -//goat -/mob/living/simple_animal/hostile/retaliate/goat - name = "goat" - desc = "Not known for their pleasant disposition." - icon_state = "goat" - icon_living = "goat" - icon_dead = "goat_dead" - speak = list("EHEHEHEHEH","eh?") - speak_emote = list("brays") - emote_hear = list("brays") - emote_see = list("shakes its head", "stamps a foot", "glares around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - faction = list("neutral") - attack_same = 1 - attacktext = "kicks" - attack_sound = 'sound/weapons/punch1.ogg' - health = 40 - maxHealth = 40 - melee_damage_lower = 1 - melee_damage_upper = 2 - stop_automated_movement_when_pulled = 1 - can_collar = 1 - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/udder/udder = null - -/mob/living/simple_animal/hostile/retaliate/goat/New() - udder = new() - . = ..() - -/mob/living/simple_animal/hostile/retaliate/goat/Destroy() - QDEL_NULL(udder) - return ..() - -/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement() - . = ..() - //chance to go crazy and start wacking stuff - if(!enemies.len && prob(1)) - Retaliate() - - if(enemies.len && prob(10)) - enemies = list() - LoseTarget() - visible_message("[src] calms down.") - - eat_plants() - if(!pulledby) - for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10))) - var/step = get_step(src, direction) - if(step) - if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) - Move(step, get_dir(src, step)) - -/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired) - . = ..() - if(stat == CONSCIOUS) - udder.generateMilk() - -/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() - ..() - visible_message("[src] gets an evil-looking gleam in their eye.") - -/mob/living/simple_animal/hostile/retaliate/goat/Move() - . = ..() - if(!stat) - eat_plants() - -/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) - udder.milkAnimal(O, user) - else - return ..() - -/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() - var/eaten = FALSE - var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc - if(SV) - SV.eat(src) - eaten = TRUE - - var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc - if(GS) - qdel(GS) - eaten = TRUE - - if(eaten && prob(10)) - say("Nom") - -/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget() - . = ..() - if(. && isdiona(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/NB = pick(H.bodyparts) - H.visible_message("[src] takes a big chomp out of [H]!", "[src] takes a big chomp out of your [NB.name]!") - NB.droplimb() - -//cow -/mob/living/simple_animal/cow - name = "cow" - desc = "Known for their milk, just don't tip them over." - icon_state = "cow" - icon_living = "cow" - icon_dead = "cow_dead" - icon_gib = "cow_gib" - speak = list("moo?","moo","MOOOOOO") - speak_emote = list("moos","moos hauntingly") - emote_hear = list("brays") - emote_see = list("shakes its head") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - attack_sound = 'sound/weapons/punch1.ogg' - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/udder/udder = null - -/mob/living/simple_animal/cow/Initialize() - udder = new() - . = ..() - -/mob/living/simple_animal/cow/Destroy() - qdel(udder) - udder = null - return ..() - -/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params) - if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) - udder.milkAnimal(O, user) - return 1 - else - return ..() - -/mob/living/simple_animal/cow/Life(seconds, times_fired) - . = ..() - if(stat == CONSCIOUS) - udder.generateMilk() - -/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) - if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead) - M.visible_message("[M] tips over [src].","You tip over [src].") - Weaken(30) - icon_state = icon_dead - spawn(rand(20,50)) - if(!stat && M) - icon_state = icon_living - var/list/responses = list( "[src] looks at you imploringly.", - "[src] looks at you pleadingly", - "[src] looks at you with a resigned expression.", - "[src] seems resigned to its fate.") - to_chat(M, pick(responses)) - else - ..() - -/mob/living/simple_animal/chick - name = "\improper chick" - desc = "Adorable! They make such a racket though." - icon_state = "chick" - icon_living = "chick" - icon_dead = "chick_dead" - icon_gib = "chick_gib" - gender = FEMALE - speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") - speak_emote = list("cheeps") - emote_hear = list("cheeps") - emote_see = list("pecks at the ground","flaps its tiny wings") - density = 0 - speak_chance = 2 - turns_per_move = 2 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 3 - maxHealth = 3 - ventcrawler = 2 - var/amount_grown = 0 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - can_hide = 1 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/chick/New() - ..() - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - -/mob/living/simple_animal/chick/Life(seconds, times_fired) - . =..() - if(.) - amount_grown += rand(1,2) - if(amount_grown >= 100) - var/mob/living/simple_animal/chicken/C = new /mob/living/simple_animal/chicken(loc) - if(mind) - mind.transfer_to(C) - qdel(src) - -#define MAX_CHICKENS 50 -var/global/chicken_count = 0 - -/mob/living/simple_animal/chicken - name = "\improper chicken" - desc = "Hopefully the eggs are good this season." - gender = FEMALE - icon_state = "chicken_brown" - icon_living = "chicken_brown" - icon_dead = "chicken_brown_dead" - speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") - speak_emote = list("clucks","croons") - emote_hear = list("clucks") - emote_see = list("pecks at the ground","flaps its wings viciously") - density = 0 - speak_chance = 2 - turns_per_move = 3 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2) - var/egg_type = /obj/item/reagent_containers/food/snacks/egg - var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 15 - maxHealth = 15 - ventcrawler = 2 - var/eggsleft = 0 - var/eggsFertile = TRUE - var/body_color - var/icon_prefix = "chicken" - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - can_hide = 1 - can_collar = 1 - var/list/feedMessages = list("It clucks happily.","It clucks happily.") - var/list/layMessage = EGG_LAYING_MESSAGES - var/list/validColors = list("brown","black","white") - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/chicken/New() - ..() - if(!body_color) - body_color = pick(validColors) - icon_state = "[icon_prefix]_[body_color]" - icon_living = "[icon_prefix]_[body_color]" - icon_dead = "[icon_prefix]_[body_color]_dead" - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - chicken_count += 1 - -/mob/living/simple_animal/chicken/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return - chicken_count -= 1 - -/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) - if(istype(O, food_type)) //feedin' dem chickens - if(!stat && eggsleft < 8) - var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" - user.visible_message(feedmsg) - user.drop_item() - qdel(O) - eggsleft += rand(1, 4) - //world << eggsleft - else - to_chat(user, "[name] doesn't seem hungry!") - else - ..() - -/mob/living/simple_animal/chicken/Life(seconds, times_fired) - . = ..() - if((. && prob(3) && eggsleft > 0) && egg_type) - visible_message("[src] [pick(layMessage)]") - eggsleft-- - var/obj/item/E = new egg_type(get_turf(src)) - E.pixel_x = rand(-6,6) - E.pixel_y = rand(-6,6) - if(eggsFertile) - if(chicken_count < MAX_CHICKENS && prob(25)) - START_PROCESSING(SSobj, E) - -/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 -/obj/item/reagent_containers/food/snacks/egg/process() - if(isturf(loc)) - amount_grown += rand(1,2) - if(amount_grown >= 100) - visible_message("[src] hatches with a quiet cracking sound.") - new /mob/living/simple_animal/chick(get_turf(src)) - STOP_PROCESSING(SSobj, src) - qdel(src) - else - STOP_PROCESSING(SSobj, src) - - -/mob/living/simple_animal/pig - name = "pig" - desc = "Oink oink." - icon_state = "pig" - icon_living = "pig" - icon_dead = "pig_dead" - speak = list("oink?","oink","OINK") - speak_emote = list("oinks") -// emote_hear = list("brays") - emote_see = list("rolls around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/ham = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/mob/living/simple_animal/turkey - name = "turkey" - desc = "Benjamin Franklin would be proud." - icon_state = "turkey" - icon_living = "turkey" - icon_dead = "turkey_dead" - icon_resting = "turkey_rest" - speak = list("gobble?","gobble","GOBBLE") - speak_emote = list("gobble") - emote_see = list("struts around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "pecks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/goose - name = "goose" - desc = "A pretty goose. Would make a nice comforter." - icon_state = "goose" - icon_living = "goose" - icon_dead = "goose_dead" - speak = list("quack?","quack","QUACK") - speak_emote = list("quacks") -// emote_hear = list("brays") - emote_see = list("flaps it's wings") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/seal - name = "seal" - desc = "A beautiful white seal." - icon_state = "seal" - icon_living = "seal" - icon_dead = "seal_dead" - speak = list("Urk?","urk","URK") - speak_emote = list("urks") -// emote_hear = list("brays") - emote_see = list("flops around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/mob/living/simple_animal/walrus - name = "walrus" - desc = "A big brown walrus." - icon_state = "walrus" - icon_living = "walrus" - icon_dead = "walrus_dead" - speak = list("Urk?","urk","URK") - speak_emote = list("urks") -// emote_hear = list("brays") - emote_see = list("flops around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/obj/item/udder - name = "udder" - -/obj/item/udder/New() - create_reagents(50) - reagents.add_reagent("milk", 20) - . = ..() - -/obj/item/udder/proc/generateMilk() - if(prob(5)) - reagents.add_reagent("milk", rand(5, 10)) - -/obj/item/udder/proc/milkAnimal(obj/O, mob/user) - var/obj/item/reagent_containers/glass/G = O - if(G.reagents.total_volume >= G.volume) - to_chat(user, "[O] is full.") - return - var/transfered = reagents.trans_to(O, rand(5,10)) - if(transfered) - user.visible_message("[user] milks [src] using \the [O].", "You milk [src] using \the [O].") - else - to_chat(user, "The udder is dry. Wait a bit longer...") \ No newline at end of file +//goat +/mob/living/simple_animal/hostile/retaliate/goat + name = "goat" + desc = "Not known for their pleasant disposition." + icon_state = "goat" + icon_living = "goat" + icon_dead = "goat_dead" + speak = list("EHEHEHEHEH","eh?") + speak_emote = list("brays") + emote_hear = list("brays") + emote_see = list("shakes its head", "stamps a foot", "glares around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + faction = list("neutral") + attack_same = 1 + attacktext = "kicks" + attack_sound = 'sound/weapons/punch1.ogg' + health = 40 + maxHealth = 40 + melee_damage_lower = 1 + melee_damage_upper = 2 + stop_automated_movement_when_pulled = 1 + can_collar = 1 + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/udder/udder = null + +/mob/living/simple_animal/hostile/retaliate/goat/New() + udder = new() + . = ..() + +/mob/living/simple_animal/hostile/retaliate/goat/Destroy() + QDEL_NULL(udder) + return ..() + +/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement() + . = ..() + //chance to go crazy and start wacking stuff + if(!enemies.len && prob(1)) + Retaliate() + + if(enemies.len && prob(10)) + enemies = list() + LoseTarget() + visible_message("[src] calms down.") + + eat_plants() + if(!pulledby) + for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10))) + var/step = get_step(src, direction) + if(step) + if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) + Move(step, get_dir(src, step)) + +/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired) + . = ..() + if(stat == CONSCIOUS) + udder.generateMilk() + +/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() + ..() + visible_message("[src] gets an evil-looking gleam in their eye.") + +/mob/living/simple_animal/hostile/retaliate/goat/Move() + . = ..() + if(!stat) + eat_plants() + +/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) + udder.milkAnimal(O, user) + else + return ..() + +/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() + var/eaten = FALSE + var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc + if(SV) + SV.eat(src) + eaten = TRUE + + var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc + if(GS) + qdel(GS) + eaten = TRUE + + if(eaten && prob(10)) + say("Nom") + +/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget() + . = ..() + if(. && isdiona(target)) + var/mob/living/carbon/human/H = target + var/obj/item/organ/external/NB = pick(H.bodyparts) + H.visible_message("[src] takes a big chomp out of [H]!", "[src] takes a big chomp out of your [NB.name]!") + NB.droplimb() + +//cow +/mob/living/simple_animal/cow + name = "cow" + desc = "Known for their milk, just don't tip them over." + icon_state = "cow" + icon_living = "cow" + icon_dead = "cow_dead" + icon_gib = "cow_gib" + speak = list("moo?","moo","MOOOOOO") + speak_emote = list("moos","moos hauntingly") + emote_hear = list("brays") + emote_see = list("shakes its head") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + attack_sound = 'sound/weapons/punch1.ogg' + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/udder/udder = null + +/mob/living/simple_animal/cow/Initialize() + udder = new() + . = ..() + +/mob/living/simple_animal/cow/Destroy() + qdel(udder) + udder = null + return ..() + +/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params) + if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) + udder.milkAnimal(O, user) + return 1 + else + return ..() + +/mob/living/simple_animal/cow/Life(seconds, times_fired) + . = ..() + if(stat == CONSCIOUS) + udder.generateMilk() + +/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) + if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead) + M.visible_message("[M] tips over [src].","You tip over [src].") + Weaken(30) + icon_state = icon_dead + spawn(rand(20,50)) + if(!stat && M) + icon_state = icon_living + var/list/responses = list( "[src] looks at you imploringly.", + "[src] looks at you pleadingly", + "[src] looks at you with a resigned expression.", + "[src] seems resigned to its fate.") + to_chat(M, pick(responses)) + else + ..() + +/mob/living/simple_animal/chick + name = "\improper chick" + desc = "Adorable! They make such a racket though." + icon_state = "chick" + icon_living = "chick" + icon_dead = "chick_dead" + icon_gib = "chick_gib" + gender = FEMALE + speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") + speak_emote = list("cheeps") + emote_hear = list("cheeps") + emote_see = list("pecks at the ground","flaps its tiny wings") + density = 0 + speak_chance = 2 + turns_per_move = 2 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 3 + maxHealth = 3 + ventcrawler = 2 + var/amount_grown = 0 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + can_hide = 1 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/chick/New() + ..() + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + +/mob/living/simple_animal/chick/Life(seconds, times_fired) + . =..() + if(.) + amount_grown += rand(1,2) + if(amount_grown >= 100) + var/mob/living/simple_animal/chicken/C = new /mob/living/simple_animal/chicken(loc) + if(mind) + mind.transfer_to(C) + qdel(src) + +#define MAX_CHICKENS 50 +GLOBAL_VAR_INIT(chicken_count, 0) + +/mob/living/simple_animal/chicken + name = "\improper chicken" + desc = "Hopefully the eggs are good this season." + gender = FEMALE + icon_state = "chicken_brown" + icon_living = "chicken_brown" + icon_dead = "chicken_brown_dead" + speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") + speak_emote = list("clucks","croons") + emote_hear = list("clucks") + emote_see = list("pecks at the ground","flaps its wings viciously") + density = 0 + speak_chance = 2 + turns_per_move = 3 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2) + var/egg_type = /obj/item/reagent_containers/food/snacks/egg + var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 15 + maxHealth = 15 + ventcrawler = 2 + var/eggsleft = 0 + var/eggsFertile = TRUE + var/body_color + var/icon_prefix = "chicken" + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + can_hide = 1 + can_collar = 1 + var/list/feedMessages = list("It clucks happily.","It clucks happily.") + var/list/layMessage = EGG_LAYING_MESSAGES + var/list/validColors = list("brown","black","white") + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/chicken/New() + ..() + if(!body_color) + body_color = pick(validColors) + icon_state = "[icon_prefix]_[body_color]" + icon_living = "[icon_prefix]_[body_color]" + icon_dead = "[icon_prefix]_[body_color]_dead" + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + GLOB.chicken_count += 1 + +/mob/living/simple_animal/chicken/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return + GLOB.chicken_count -= 1 + +/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) + if(istype(O, food_type)) //feedin' dem chickens + if(!stat && eggsleft < 8) + var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" + user.visible_message(feedmsg) + user.drop_item() + qdel(O) + eggsleft += rand(1, 4) + //world << eggsleft + else + to_chat(user, "[name] doesn't seem hungry!") + else + ..() + +/mob/living/simple_animal/chicken/Life(seconds, times_fired) + . = ..() + if((. && prob(3) && eggsleft > 0) && egg_type) + visible_message("[src] [pick(layMessage)]") + eggsleft-- + var/obj/item/E = new egg_type(get_turf(src)) + E.pixel_x = rand(-6,6) + E.pixel_y = rand(-6,6) + if(eggsFertile) + if(GLOB.chicken_count < MAX_CHICKENS && prob(25)) + START_PROCESSING(SSobj, E) + +/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 +/obj/item/reagent_containers/food/snacks/egg/process() + if(isturf(loc)) + amount_grown += rand(1,2) + if(amount_grown >= 100) + visible_message("[src] hatches with a quiet cracking sound.") + new /mob/living/simple_animal/chick(get_turf(src)) + STOP_PROCESSING(SSobj, src) + qdel(src) + else + STOP_PROCESSING(SSobj, src) + + +/mob/living/simple_animal/pig + name = "pig" + desc = "Oink oink." + icon_state = "pig" + icon_living = "pig" + icon_dead = "pig_dead" + speak = list("oink?","oink","OINK") + speak_emote = list("oinks") +// emote_hear = list("brays") + emote_see = list("rolls around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/ham = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/simple_animal/turkey + name = "turkey" + desc = "Benjamin Franklin would be proud." + icon_state = "turkey" + icon_living = "turkey" + icon_dead = "turkey_dead" + icon_resting = "turkey_rest" + speak = list("gobble?","gobble","GOBBLE") + speak_emote = list("gobble") + emote_see = list("struts around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "pecks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/goose + name = "goose" + desc = "A pretty goose. Would make a nice comforter." + icon_state = "goose" + icon_living = "goose" + icon_dead = "goose_dead" + speak = list("quack?","quack","QUACK") + speak_emote = list("quacks") +// emote_hear = list("brays") + emote_see = list("flaps it's wings") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/seal + name = "seal" + desc = "A beautiful white seal." + icon_state = "seal" + icon_living = "seal" + icon_dead = "seal_dead" + speak = list("Urk?","urk","URK") + speak_emote = list("urks") +// emote_hear = list("brays") + emote_see = list("flops around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/simple_animal/walrus + name = "walrus" + desc = "A big brown walrus." + icon_state = "walrus" + icon_living = "walrus" + icon_dead = "walrus_dead" + speak = list("Urk?","urk","URK") + speak_emote = list("urks") +// emote_hear = list("brays") + emote_see = list("flops around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/obj/item/udder + name = "udder" + +/obj/item/udder/New() + create_reagents(50) + reagents.add_reagent("milk", 20) + . = ..() + +/obj/item/udder/proc/generateMilk() + if(prob(5)) + reagents.add_reagent("milk", rand(5, 10)) + +/obj/item/udder/proc/milkAnimal(obj/O, mob/user) + var/obj/item/reagent_containers/glass/G = O + if(G.reagents.total_volume >= G.volume) + to_chat(user, "[O] is full.") + return + var/transfered = reagents.trans_to(O, rand(5,10)) + if(transfered) + user.visible_message("[user] milks [src] using \the [O].", "You milk [src] using \the [O].") + else + to_chat(user, "The udder is dry. Wait a bit longer...") diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm index ad77fd547a59..9eccc473e6db 100644 --- a/code/modules/mob/living/simple_animal/friendly/fox.dm +++ b/code/modules/mob/living/simple_animal/friendly/fox.dm @@ -40,4 +40,4 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 melee_damage_lower = 10 - melee_damage_upper = 20 \ No newline at end of file + melee_damage_upper = 20 diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index 2d7426aa4512..96688963b735 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -1,25 +1,25 @@ -/mob/living/simple_animal/lizard - name = "Lizard" - desc = "A cute tiny lizard." - icon = 'icons/mob/critter.dmi' - icon_state = "lizard" - icon_living = "lizard" - icon_dead = "lizard-dead" - speak_emote = list("hisses") - health = 5 - maxHealth = 5 - attacktext = "bites" - obj_damage = 0 - melee_damage_lower = 1 - melee_damage_upper = 2 - response_help = "pets" - response_disarm = "shoos" - response_harm = "stomps on" - ventcrawler = 2 - density = 0 - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - can_hide = 1 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN +/mob/living/simple_animal/lizard + name = "Lizard" + desc = "A cute tiny lizard." + icon = 'icons/mob/critter.dmi' + icon_state = "lizard" + icon_living = "lizard" + icon_dead = "lizard-dead" + speak_emote = list("hisses") + health = 5 + maxHealth = 5 + attacktext = "bites" + obj_damage = 0 + melee_damage_lower = 1 + melee_damage_upper = 2 + response_help = "pets" + response_disarm = "shoos" + response_harm = "stomps on" + ventcrawler = 2 + density = 0 + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + can_hide = 1 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index d6f38d33268f..1446ef7d4bb4 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -1,236 +1,236 @@ -/mob/living/simple_animal/mouse - name = "mouse" - real_name = "mouse" - desc = "It's a small, disease-ridden rodent." - icon_state = "mouse_gray" - icon_living = "mouse_gray" - icon_dead = "mouse_gray_dead" - icon_resting = "mouse_gray_sleep" - speak = list("Squeek!","SQUEEK!","Squeek?") - speak_emote = list("squeeks","squeaks","squiks") - emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") - var/squeak_sound = 'sound/creatures/mousesqueak.ogg' - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - maxHealth = 5 - health = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - density = 0 - ventcrawler = 2 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - var/mouse_color //brown, gray and white, leave blank for random - layer = MOB_LAYER - atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 223 //Below -50 Degrees Celcius - maxbodytemp = 323 //Above 50 Degrees Celcius - universal_speak = 0 - can_hide = 1 - holder_type = /obj/item/holder/mouse - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - var/chew_probability = 1 - -/mob/living/simple_animal/mouse/Initialize(mapload) - . = ..() - AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100) - -/mob/living/simple_animal/mouse/handle_automated_action() - if(prob(chew_probability) && isturf(loc)) - var/turf/simulated/floor/F = get_turf(src) - if(istype(F) && !F.intact) - var/obj/structure/cable/C = locate() in F - if(C && prob(15)) - if(C.avail()) - visible_message("[src] chews through [C]. It's toast!") - playsound(src, 'sound/effects/sparks2.ogg', 100, 1) - C.deconstruct() - toast() // mmmm toasty. - else - C.deconstruct() - visible_message("[src] chews through [C].") - -/mob/living/simple_animal/mouse/handle_automated_speech() - ..() - if(prob(speak_chance) && !incapacitated()) - playsound(src, squeak_sound, 100, 1) - -/mob/living/simple_animal/mouse/handle_automated_movement() - . = ..() - if(resting) - if(prob(1)) - StopResting() - else if(prob(5)) - custom_emote(2, "snuffles") - else if(prob(0.5)) - StartResting() - -/mob/living/simple_animal/mouse/New() - ..() - if(!mouse_color) - mouse_color = pick( list("brown","gray","white") ) - icon_state = "mouse_[mouse_color]" - icon_living = "mouse_[mouse_color]" - icon_dead = "mouse_[mouse_color]_dead" - icon_resting = "mouse_[mouse_color]_sleep" - desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." - -/mob/living/simple_animal/mouse/proc/splat() - src.health = 0 - src.stat = DEAD - src.icon_dead = "mouse_[mouse_color]_splat" - src.icon_state = "mouse_[mouse_color]_splat" - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - -/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) - if(M.a_intent == INTENT_HELP) - get_scooped(M) - ..() - -/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things - to_chat(src, "You are too small to pull anything.") - return - -/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) - if(ishuman(AM)) - if(!stat) - var/mob/M = AM - to_chat(M, "[bicon(src)] Squeek!") - ..() - -/mob/living/simple_animal/mouse/proc/toast() - add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) - desc = "It's toast." - death() - -/mob/living/simple_animal/mouse/death(gibbed) - // Only execute the below if we successfully died - playsound(src, squeak_sound, 40, 1) - . = ..(gibbed) - if(!.) - return FALSE - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - -/mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force) - if(stat != CONSCIOUS) - return - - var/on_CD = 0 - act = lowertext(act) - switch(act) - if("squeak") //Mouse time - on_CD = handle_emote_CD() - else - on_CD = 0 - - if(!force && on_CD == 1) - return - - switch(act) - if("squeak") - message = "\The [src] [pick(emote_hear)]!" - m_type = 2 //audible - playsound(src, squeak_sound, 40, 1) - if("help") - to_chat(src, "scream, squeak") - - ..() - -/* - * Mouse types - */ - -/mob/living/simple_animal/mouse/white - mouse_color = "white" - icon_state = "mouse_white" - -/mob/living/simple_animal/mouse/gray - mouse_color = "gray" - icon_state = "mouse_gray" - -/mob/living/simple_animal/mouse/brown - mouse_color = "brown" - icon_state = "mouse_brown" - -//TOM IS ALIVE! SQUEEEEEEEE~K :) -/mob/living/simple_animal/mouse/brown/Tom - name = "Tom" - desc = "Jerry the cat is not amused." - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "splats" - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - - -/mob/living/simple_animal/mouse/blobinfected - maxHealth = 100 - health = 100 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - gold_core_spawnable = NO_SPAWN - var/cycles_alive = 0 - var/cycles_limit = 60 - var/has_burst = FALSE - -/mob/living/simple_animal/mouse/blobinfected/Life() - cycles_alive++ - var/timeleft = (cycles_limit - cycles_alive) * 2 - if(ismob(loc)) // if someone ate it, burst immediately - burst(FALSE) - else if(timeleft < 1) // if timer expired, burst. - burst(FALSE) - else if(cycles_alive % 2 == 0) // give the mouse/player a countdown reminder every 2 cycles - to_chat(src, "[timeleft] seconds until you burst, and become a blob...") - return ..() - -/mob/living/simple_animal/mouse/blobinfected/death(gibbed) - burst(gibbed) - return ..(gibbed) - -/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) - if(has_burst) - return FALSE - var/turf/T = get_turf(src) - if(!is_station_level(T.z) || isspaceturf(T)) - to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - return FALSE - has_burst = TRUE - var/datum/mind/blobmind = mind - var/client/C = client - if(istype(blobmind) && istype(C)) - blobmind.special_role = SPECIAL_ROLE_BLOB - var/obj/structure/blob/core/core = new(T, 200, C, 3) - core.lateblobtimer() - else - new /obj/structure/blob/core(T) // Ghosts will be prompted to control it. - if(ismob(loc)) // in case some taj/etc ate the mouse. - var/mob/M = loc - M.gib() - if(!gibbed) - gib() - -/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) - to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") - to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") - -/mob/living/simple_animal/mouse/fluff/clockwork - name = "Chip" - real_name = "Chip" - mouse_color = "clockwork" - icon_state = "mouse_clockwork" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - gold_core_spawnable = NO_SPAWN - can_collar = 0 - butcher_results = list(/obj/item/stack/sheet/metal = 1) +/mob/living/simple_animal/mouse + name = "mouse" + real_name = "mouse" + desc = "It's a small, disease-ridden rodent." + icon_state = "mouse_gray" + icon_living = "mouse_gray" + icon_dead = "mouse_gray_dead" + icon_resting = "mouse_gray_sleep" + speak = list("Squeek!","SQUEEK!","Squeek?") + speak_emote = list("squeeks","squeaks","squiks") + emote_hear = list("squeeks","squeaks","squiks") + emote_see = list("runs in a circle", "shakes", "scritches at something") + var/squeak_sound = 'sound/creatures/mousesqueak.ogg' + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + maxHealth = 5 + health = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + density = 0 + ventcrawler = 2 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + var/mouse_color //brown, gray and white, leave blank for random + layer = MOB_LAYER + atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + universal_speak = 0 + can_hide = 1 + holder_type = /obj/item/holder/mouse + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + var/chew_probability = 1 + +/mob/living/simple_animal/mouse/Initialize(mapload) + . = ..() + AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100) + +/mob/living/simple_animal/mouse/handle_automated_action() + if(prob(chew_probability) && isturf(loc)) + var/turf/simulated/floor/F = get_turf(src) + if(istype(F) && !F.intact) + var/obj/structure/cable/C = locate() in F + if(C && prob(15)) + if(C.avail()) + visible_message("[src] chews through [C]. It's toast!") + playsound(src, 'sound/effects/sparks2.ogg', 100, 1) + C.deconstruct() + toast() // mmmm toasty. + else + C.deconstruct() + visible_message("[src] chews through [C].") + +/mob/living/simple_animal/mouse/handle_automated_speech() + ..() + if(prob(speak_chance) && !incapacitated()) + playsound(src, squeak_sound, 100, 1) + +/mob/living/simple_animal/mouse/handle_automated_movement() + . = ..() + if(resting) + if(prob(1)) + StopResting() + else if(prob(5)) + custom_emote(2, "snuffles") + else if(prob(0.5)) + StartResting() + +/mob/living/simple_animal/mouse/New() + ..() + if(!mouse_color) + mouse_color = pick( list("brown","gray","white") ) + icon_state = "mouse_[mouse_color]" + icon_living = "mouse_[mouse_color]" + icon_dead = "mouse_[mouse_color]_dead" + icon_resting = "mouse_[mouse_color]_sleep" + desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." + +/mob/living/simple_animal/mouse/proc/splat() + src.health = 0 + src.stat = DEAD + src.icon_dead = "mouse_[mouse_color]_splat" + src.icon_state = "mouse_[mouse_color]_splat" + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) + if(M.a_intent == INTENT_HELP) + get_scooped(M) + ..() + +/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things + to_chat(src, "You are too small to pull anything.") + return + +/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) + if(ishuman(AM)) + if(!stat) + var/mob/M = AM + to_chat(M, "[bicon(src)] Squeek!") + ..() + +/mob/living/simple_animal/mouse/proc/toast() + add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) + desc = "It's toast." + death() + +/mob/living/simple_animal/mouse/death(gibbed) + // Only execute the below if we successfully died + playsound(src, squeak_sound, 40, 1) + . = ..(gibbed) + if(!.) + return FALSE + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force) + if(stat != CONSCIOUS) + return + + var/on_CD = 0 + act = lowertext(act) + switch(act) + if("squeak") //Mouse time + on_CD = handle_emote_CD() + else + on_CD = 0 + + if(!force && on_CD == 1) + return + + switch(act) + if("squeak") + message = "\The [src] [pick(emote_hear)]!" + m_type = 2 //audible + playsound(src, squeak_sound, 40, 1) + if("help") + to_chat(src, "scream, squeak") + + ..() + +/* + * Mouse types + */ + +/mob/living/simple_animal/mouse/white + mouse_color = "white" + icon_state = "mouse_white" + +/mob/living/simple_animal/mouse/gray + mouse_color = "gray" + icon_state = "mouse_gray" + +/mob/living/simple_animal/mouse/brown + mouse_color = "brown" + icon_state = "mouse_brown" + +//TOM IS ALIVE! SQUEEEEEEEE~K :) +/mob/living/simple_animal/mouse/brown/Tom + name = "Tom" + desc = "Jerry the cat is not amused." + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "splats" + unique_pet = TRUE + gold_core_spawnable = NO_SPAWN + + +/mob/living/simple_animal/mouse/blobinfected + maxHealth = 100 + health = 100 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + gold_core_spawnable = NO_SPAWN + var/cycles_alive = 0 + var/cycles_limit = 60 + var/has_burst = FALSE + +/mob/living/simple_animal/mouse/blobinfected/Life() + cycles_alive++ + var/timeleft = (cycles_limit - cycles_alive) * 2 + if(ismob(loc)) // if someone ate it, burst immediately + burst(FALSE) + else if(timeleft < 1) // if timer expired, burst. + burst(FALSE) + else if(cycles_alive % 2 == 0) // give the mouse/player a countdown reminder every 2 cycles + to_chat(src, "[timeleft] seconds until you burst, and become a blob...") + return ..() + +/mob/living/simple_animal/mouse/blobinfected/death(gibbed) + burst(gibbed) + return ..(gibbed) + +/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) + if(has_burst) + return FALSE + var/turf/T = get_turf(src) + if(!is_station_level(T.z) || isspaceturf(T)) + to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + return FALSE + has_burst = TRUE + var/datum/mind/blobmind = mind + var/client/C = client + if(istype(blobmind) && istype(C)) + blobmind.special_role = SPECIAL_ROLE_BLOB + var/obj/structure/blob/core/core = new(T, 200, C, 3) + core.lateblobtimer() + else + new /obj/structure/blob/core(T) // Ghosts will be prompted to control it. + if(ismob(loc)) // in case some taj/etc ate the mouse. + var/mob/M = loc + M.gib() + if(!gibbed) + gib() + +/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) + to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") + to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") + +/mob/living/simple_animal/mouse/fluff/clockwork + name = "Chip" + real_name = "Chip" + mouse_color = "clockwork" + icon_state = "mouse_clockwork" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + gold_core_spawnable = NO_SPAWN + can_collar = 0 + butcher_results = list(/obj/item/stack/sheet/metal = 1) diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index d2dba6da1c94..dc00e2526614 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -64,4 +64,4 @@ icon_dead = "penguin_baby_dead" density = FALSE pass_flags = PASSMOB - mob_size = MOB_SIZE_SMALL \ No newline at end of file + mob_size = MOB_SIZE_SMALL diff --git a/code/modules/mob/living/simple_animal/friendly/pet.dm b/code/modules/mob/living/simple_animal/friendly/pet.dm index 8a9899b7fa94..ac4f33670022 100644 --- a/code/modules/mob/living/simple_animal/friendly/pet.dm +++ b/code/modules/mob/living/simple_animal/friendly/pet.dm @@ -13,4 +13,4 @@ setDir(i) sleep(1) else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index ddf311d62d4b..d5b47c189ff2 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -29,4 +29,4 @@ name = "Paperwork" desc = "Cargo's pet sloth. About as useful as the rest of the techs." unique_pet = TRUE - gold_core_spawnable = NO_SPAWN \ No newline at end of file + gold_core_spawnable = NO_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/snake.dm b/code/modules/mob/living/simple_animal/friendly/snake.dm index 1cd85c1b6223..b4a15ba27b42 100644 --- a/code/modules/mob/living/simple_animal/friendly/snake.dm +++ b/code/modules/mob/living/simple_animal/friendly/snake.dm @@ -58,4 +58,4 @@ QDEL_NULL(target) adjustHealth(-2) else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index b541d55096aa..5a369a043d75 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -1,164 +1,164 @@ -/mob/living/simple_animal/hostile/alien - name = "alien hunter" - desc = "Hiss!" - icon = 'icons/mob/alien.dmi' - icon_state = "alienh_running" - icon_living = "alienh_running" - icon_dead = "alienh_dead" - icon_gib = "syndicate_gib" - gender = FEMALE - response_help = "pokes" - response_disarm = "shoves" - response_harm = "hits" - speed = 0 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 3, /obj/item/stack/sheet/animalhide/xeno = 1) - maxHealth = 125 - health = 125 - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 25 - melee_damage_upper = 25 - attacktext = "slashes" - speak_emote = list("hisses") - a_intent = INTENT_HARM - attack_sound = 'sound/weapons/bladeslice.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 15 - heat_damage_per_tick = 20 - faction = list("alien") - status_flags = CANPUSH - minbodytemp = 0 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - gold_core_spawnable = HOSTILE_SPAWN - death_sound = 'sound/voice/hiss6.ogg' - deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." - - -/mob/living/simple_animal/hostile/alien/drone - name = "alien drone" - icon_state = "aliend_running" - icon_living = "aliend_running" - icon_dead = "aliend_dead" - melee_damage_lower = 15 - melee_damage_upper = 15 - var/plant_cooldown = 30 - var/plants_off = 0 - -/mob/living/simple_animal/hostile/alien/drone/handle_automated_action() - if(!..()) //AIStatus is off - return - plant_cooldown-- - if(AIStatus == AI_IDLE) - if(!plants_off && prob(10) && plant_cooldown<=0) - plant_cooldown = initial(plant_cooldown) - SpreadPlants() - -/mob/living/simple_animal/hostile/alien/sentinel - name = "alien sentinel" - icon_state = "aliens_running" - icon_living = "aliens_running" - icon_dead = "aliens_dead" - health = 150 - maxHealth = 150 - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/neurotox - projectilesound = 'sound/weapons/pierce.ogg' - - -/mob/living/simple_animal/hostile/alien/queen - name = "alien queen" - icon_state = "alienq_running" - icon_living = "alienq_running" - icon_dead = "alienq_d" - health = 250 - maxHealth = 250 - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - move_to_delay = 4 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 4, /obj/item/stack/sheet/animalhide/xeno = 1) - projectiletype = /obj/item/projectile/neurotox - projectilesound = 'sound/weapons/pierce.ogg' - status_flags = 0 - var/sterile = 1 - var/plants_off = 0 - var/egg_cooldown = 30 - var/plant_cooldown = 30 - -/mob/living/simple_animal/hostile/alien/queen/handle_automated_action() - if(!..()) - return - egg_cooldown-- - plant_cooldown-- - if(AIStatus == AI_IDLE) - if(!plants_off && prob(10) && plant_cooldown<=0) - plant_cooldown = initial(plant_cooldown) - SpreadPlants() - if(!sterile && prob(10) && egg_cooldown<=0) - egg_cooldown = initial(egg_cooldown) - LayEggs() - -/mob/living/simple_animal/hostile/alien/proc/SpreadPlants() - if(!isturf(loc) || isspaceturf(loc)) - return - if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) - return - visible_message("[src] has planted some alien weeds!") - new /obj/structure/alien/weeds/node(loc) - -/mob/living/simple_animal/hostile/alien/proc/LayEggs() - if(!isturf(loc) || isspaceturf(loc)) - return - if(locate(/obj/structure/alien/egg) in get_turf(src)) - return - visible_message("[src] has laid an egg!") - new /obj/structure/alien/egg(loc) - -/mob/living/simple_animal/hostile/alien/queen/large - name = "alien empress" - icon = 'icons/mob/alienlarge.dmi' - icon_state = "queen_s" - icon_living = "queen_s" - icon_dead = "queen_dead" - move_to_delay = 4 - maxHealth = 400 - health = 400 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 10, /obj/item/stack/sheet/animalhide/xeno = 2) - mob_size = MOB_SIZE_LARGE - gold_core_spawnable = NO_SPAWN - -/obj/item/projectile/neurotox - name = "neurotoxin" - damage = 30 - icon_state = "toxin" - -/mob/living/simple_animal/hostile/alien/maid - name = "lusty xenomorph maid" - melee_damage_lower = 0 - melee_damage_upper = 0 - a_intent = INTENT_HELP - friendly = "caresses" - obj_damage = 0 - environment_smash = ENVIRONMENT_SMASH_NONE - gold_core_spawnable = HOSTILE_SPAWN - icon_state = "maid" - icon_living = "maid" - icon_dead = "maid_dead" - -/mob/living/simple_animal/hostile/alien/maid/AttackingTarget() - if(ismovableatom(target)) - if(istype(target, /obj/effect/decal/cleanable)) - visible_message("\The [src] cleans up \the [target].") - qdel(target) - return TRUE - var/atom/movable/M = target - M.clean_blood() - visible_message("\The [src] polishes \the [target].") - return TRUE +/mob/living/simple_animal/hostile/alien + name = "alien hunter" + desc = "Hiss!" + icon = 'icons/mob/alien.dmi' + icon_state = "alienh_running" + icon_living = "alienh_running" + icon_dead = "alienh_dead" + icon_gib = "syndicate_gib" + gender = FEMALE + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + speed = 0 + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 3, /obj/item/stack/sheet/animalhide/xeno = 1) + maxHealth = 125 + health = 125 + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + speak_emote = list("hisses") + a_intent = INTENT_HARM + attack_sound = 'sound/weapons/bladeslice.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + heat_damage_per_tick = 20 + faction = list("alien") + status_flags = CANPUSH + minbodytemp = 0 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + gold_core_spawnable = HOSTILE_SPAWN + death_sound = 'sound/voice/hiss6.ogg' + deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." + + +/mob/living/simple_animal/hostile/alien/drone + name = "alien drone" + icon_state = "aliend_running" + icon_living = "aliend_running" + icon_dead = "aliend_dead" + melee_damage_lower = 15 + melee_damage_upper = 15 + var/plant_cooldown = 30 + var/plants_off = 0 + +/mob/living/simple_animal/hostile/alien/drone/handle_automated_action() + if(!..()) //AIStatus is off + return + plant_cooldown-- + if(AIStatus == AI_IDLE) + if(!plants_off && prob(10) && plant_cooldown<=0) + plant_cooldown = initial(plant_cooldown) + SpreadPlants() + +/mob/living/simple_animal/hostile/alien/sentinel + name = "alien sentinel" + icon_state = "aliens_running" + icon_living = "aliens_running" + icon_dead = "aliens_dead" + health = 150 + maxHealth = 150 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + + +/mob/living/simple_animal/hostile/alien/queen + name = "alien queen" + icon_state = "alienq_running" + icon_living = "alienq_running" + icon_dead = "alienq_d" + health = 250 + maxHealth = 250 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + move_to_delay = 4 + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 4, /obj/item/stack/sheet/animalhide/xeno = 1) + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + status_flags = 0 + var/sterile = 1 + var/plants_off = 0 + var/egg_cooldown = 30 + var/plant_cooldown = 30 + +/mob/living/simple_animal/hostile/alien/queen/handle_automated_action() + if(!..()) + return + egg_cooldown-- + plant_cooldown-- + if(AIStatus == AI_IDLE) + if(!plants_off && prob(10) && plant_cooldown<=0) + plant_cooldown = initial(plant_cooldown) + SpreadPlants() + if(!sterile && prob(10) && egg_cooldown<=0) + egg_cooldown = initial(egg_cooldown) + LayEggs() + +/mob/living/simple_animal/hostile/alien/proc/SpreadPlants() + if(!isturf(loc) || isspaceturf(loc)) + return + if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) + return + visible_message("[src] has planted some alien weeds!") + new /obj/structure/alien/weeds/node(loc) + +/mob/living/simple_animal/hostile/alien/proc/LayEggs() + if(!isturf(loc) || isspaceturf(loc)) + return + if(locate(/obj/structure/alien/egg) in get_turf(src)) + return + visible_message("[src] has laid an egg!") + new /obj/structure/alien/egg(loc) + +/mob/living/simple_animal/hostile/alien/queen/large + name = "alien empress" + icon = 'icons/mob/alienlarge.dmi' + icon_state = "queen_s" + icon_living = "queen_s" + icon_dead = "queen_dead" + move_to_delay = 4 + maxHealth = 400 + health = 400 + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 10, /obj/item/stack/sheet/animalhide/xeno = 2) + mob_size = MOB_SIZE_LARGE + gold_core_spawnable = NO_SPAWN + +/obj/item/projectile/neurotox + name = "neurotoxin" + damage = 30 + icon_state = "toxin" + +/mob/living/simple_animal/hostile/alien/maid + name = "lusty xenomorph maid" + melee_damage_lower = 0 + melee_damage_upper = 0 + a_intent = INTENT_HELP + friendly = "caresses" + obj_damage = 0 + environment_smash = ENVIRONMENT_SMASH_NONE + gold_core_spawnable = HOSTILE_SPAWN + icon_state = "maid" + icon_living = "maid" + icon_dead = "maid_dead" + +/mob/living/simple_animal/hostile/alien/maid/AttackingTarget() + if(ismovableatom(target)) + if(istype(target, /obj/effect/decal/cleanable)) + visible_message("\The [src] cleans up \the [target].") + qdel(target) + return TRUE + var/atom/movable/M = target + M.clean_blood() + visible_message("\The [src] polishes \the [target].") + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index ae851fe6dcbe..be04fc217e9f 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -73,4 +73,4 @@ pass_flags = PASSTABLE universal_speak = 1 universal_understand = 1 - gold_core_spawnable = NO_SPAWN //badmin only \ No newline at end of file + gold_core_spawnable = NO_SPAWN //badmin only diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index cfe673a78f18..2530fb4b3895 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -1,56 +1,56 @@ -//Space bears! -/mob/living/simple_animal/hostile/bear - name = "space bear" - desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." - icon_state = "bear" - icon_living = "bear" - icon_dead = "bear_dead" - icon_gib = "bear_gib" - speak = list("RAWR!","Rawr!","GRR!","Growl!") - speak_emote = list("growls", "roars") - emote_hear = list("rawrs","grumbles","grawls") - emote_see = list("stares ferociously", "stomps") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/bearmeat = 5, /obj/item/clothing/head/bearpelt = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - stop_automated_movement_when_pulled = 0 - maxHealth = 60 - health = 60 - obj_damage = 60 - melee_damage_lower = 20 - melee_damage_upper = 30 - attacktext = "mauls" - friendly = "bear hugs" - attack_sound = 'sound/weapons/genhit3.ogg' - - //Space bears aren't affected by atmos. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("russian") - gold_core_spawnable = HOSTILE_SPAWN - -//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! -/mob/living/simple_animal/hostile/bear/Hudson - name = "Hudson" - desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... - -/mob/living/simple_animal/hostile/bear/Hudson/New() - ..() - var/unbearable_pun = pick("He's unbearably cute.", "It looks like he is a bearer of bad news.", "Sadly, he is bearly able to comprehend puns.") - desc = "That's Hudson. " + unbearable_pun// I am not sorry for this. - -/mob/living/simple_animal/hostile/bear/Move() - ..() - if(stat != DEAD) - if(loc && istype(loc,/turf/space)) - icon_state = "bear" - else - icon_state = "bearfloor" - -/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/movement_dir = 0) - return 1 //No drifting in space for space bears! +//Space bears! +/mob/living/simple_animal/hostile/bear + name = "space bear" + desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." + icon_state = "bear" + icon_living = "bear" + icon_dead = "bear_dead" + icon_gib = "bear_gib" + speak = list("RAWR!","Rawr!","GRR!","Growl!") + speak_emote = list("growls", "roars") + emote_hear = list("rawrs","grumbles","grawls") + emote_see = list("stares ferociously", "stomps") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/bearmeat = 5, /obj/item/clothing/head/bearpelt = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + stop_automated_movement_when_pulled = 0 + maxHealth = 60 + health = 60 + obj_damage = 60 + melee_damage_lower = 20 + melee_damage_upper = 30 + attacktext = "mauls" + friendly = "bear hugs" + attack_sound = 'sound/weapons/genhit3.ogg' + + //Space bears aren't affected by atmos. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("russian") + gold_core_spawnable = HOSTILE_SPAWN + +//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! +/mob/living/simple_animal/hostile/bear/Hudson + name = "Hudson" + desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... + +/mob/living/simple_animal/hostile/bear/Hudson/New() + ..() + var/unbearable_pun = pick("He's unbearably cute.", "It looks like he is a bearer of bad news.", "Sadly, he is bearly able to comprehend puns.") + desc = "That's Hudson. " + unbearable_pun// I am not sorry for this. + +/mob/living/simple_animal/hostile/bear/Move() + ..() + if(stat != DEAD) + if(loc && istype(loc,/turf/space)) + icon_state = "bear" + else + icon_state = "bearfloor" + +/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/movement_dir = 0) + return 1 //No drifting in space for space bears! diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index 046bc162d7a5..c1e7548d9a3d 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -352,4 +352,4 @@ if(. && target && isliving(target)) var/mob/living/L = target if(L.stat) - LoseTarget() \ No newline at end of file + LoseTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index eb2bbddc4375..a0cb935ba254 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -1,171 +1,171 @@ -#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin for megacarps (ty robustin!) - -/mob/living/simple_animal/hostile/carp - name = "space carp" - desc = "A ferocious, fang-bearing creature that resembles a fish." - icon = 'icons/mob/carp.dmi' - icon_state = "base" - icon_living = "base" - icon_dead = "base_dead" - icon_gib = "carp_gib" - speak_chance = 0 - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 2) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - emote_taunt = list("gnashes") - taunt_chance = 30 - speed = 0 - maxHealth = 25 - health = 25 - - harm_intent_damage = 8 - obj_damage = 50 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("gnashes") - - //Space carp aren't affected by atmos. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = 1500 - faction = list("carp") - flying = TRUE - pressure_resistance = 200 - gold_core_spawnable = HOSTILE_SPAWN - - var/random_color = TRUE //if the carp uses random coloring - var/rarechance = 1 //chance for rare color variant - - var/static/list/carp_colors = list(\ - "lightpurple" = "#c3b9f1", \ - "lightpink" = "#da77a8", \ - "green" = "#70ff25", \ - "grape" = "#df0afb", \ - "swamp" = "#e5e75a", \ - "turquoise" = "#04e1ed", \ - "brown" = "#ca805a", \ - "teal" = "#20e28e", \ - "lightblue" = "#4d88cc", \ - "rusty" = "#dd5f34", \ - "beige" = "#bbaeaf", \ - "yellow" = "#f3ca4a", \ - "blue" = "#09bae1", \ - "palegreen" = "#7ef099", \ - ) - var/static/list/carp_colors_rare = list(\ - "silver" = "#fdfbf3", \ - ) - -/mob/living/simple_animal/hostile/carp/Initialize(mapload) - . = ..() - carp_randomify(rarechance) - update_icons() - -/mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) - if(random_color) - var/our_color - if(prob(rarechance)) - our_color = pick(carp_colors_rare) - add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY) - else - our_color = pick(carp_colors) - add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) - add_carp_overlay() - -/mob/living/simple_animal/hostile/carp/proc/add_carp_overlay() - if(!random_color) - return - cut_overlays() - var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth") - base_overlay.appearance_flags = RESET_COLOR - add_overlay(base_overlay) - -/mob/living/simple_animal/hostile/carp/proc/add_dead_carp_overlay() - if(!random_color) - return - cut_overlays() - var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth") - base_dead_overlay.appearance_flags = RESET_COLOR - add_overlay(base_dead_overlay) - -/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) - return TRUE //No drifting in space for space carp! //original comments do not steal - -/mob/living/simple_animal/hostile/carp/AttackingTarget() - . = ..() - if(. && ishuman(target)) - var/mob/living/carbon/human/H = target - H.adjustStaminaLoss(8) - -/mob/living/simple_animal/hostile/carp/death(gibbed) - . = ..() - cut_overlays() - if(!random_color || gibbed) - return - add_dead_carp_overlay() - -/mob/living/simple_animal/hostile/carp/revive() - ..() - regenerate_icons() - -/mob/living/simple_animal/hostile/carp/regenerate_icons() - cut_overlays() - if(!random_color) - return - if(stat != DEAD) - add_carp_overlay() - else - add_dead_carp_overlay() - ..() - -/mob/living/simple_animal/hostile/carp/holocarp - icon_state = "holocarp" - icon_living = "holocarp" - maxbodytemp = INFINITY - gold_core_spawnable = NO_SPAWN - del_on_death = 1 - random_color = FALSE - -/mob/living/simple_animal/hostile/carp/megacarp - icon = 'icons/mob/alienqueen.dmi' - name = "Mega Space Carp" - desc = "A ferocious, fang bearing creature that resembles a shark. This one seems especially ticked off." - icon_state = "megacarp" - icon_living = "megacarp" - icon_dead = "megacarp_dead" - icon_gib = "megacarp_gib" - maxHealth = 20 - health = 20 - pixel_x = -16 - mob_size = MOB_SIZE_LARGE - random_color = FALSE - - obj_damage = 80 - melee_damage_lower = 20 - melee_damage_upper = 20 - - var/regen_cooldown = 0 - -/mob/living/simple_animal/hostile/carp/megacarp/Initialize() - . = ..() - name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" - melee_damage_lower += rand(2, 10) - melee_damage_upper += rand(10, 20) - maxHealth += rand(30, 60) - move_to_delay = rand(3, 7) - -/mob/living/simple_animal/hostile/carp/megacarp/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(.) - regen_cooldown = world.time + REGENERATION_DELAY - -/mob/living/simple_animal/hostile/carp/megacarp/Life() - ..() - if(regen_cooldown < world.time) - heal_overall_damage(4) - -#undef REGENERATION_DELAY \ No newline at end of file +#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin for megacarps (ty robustin!) + +/mob/living/simple_animal/hostile/carp + name = "space carp" + desc = "A ferocious, fang-bearing creature that resembles a fish." + icon = 'icons/mob/carp.dmi' + icon_state = "base" + icon_living = "base" + icon_dead = "base_dead" + icon_gib = "carp_gib" + speak_chance = 0 + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 2) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + emote_taunt = list("gnashes") + taunt_chance = 30 + speed = 0 + maxHealth = 25 + health = 25 + + harm_intent_damage = 8 + obj_damage = 50 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("gnashes") + + //Space carp aren't affected by atmos. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = 1500 + faction = list("carp") + flying = TRUE + pressure_resistance = 200 + gold_core_spawnable = HOSTILE_SPAWN + + var/random_color = TRUE //if the carp uses random coloring + var/rarechance = 1 //chance for rare color variant + + var/static/list/carp_colors = list(\ + "lightpurple" = "#c3b9f1", \ + "lightpink" = "#da77a8", \ + "green" = "#70ff25", \ + "grape" = "#df0afb", \ + "swamp" = "#e5e75a", \ + "turquoise" = "#04e1ed", \ + "brown" = "#ca805a", \ + "teal" = "#20e28e", \ + "lightblue" = "#4d88cc", \ + "rusty" = "#dd5f34", \ + "beige" = "#bbaeaf", \ + "yellow" = "#f3ca4a", \ + "blue" = "#09bae1", \ + "palegreen" = "#7ef099", \ + ) + var/static/list/carp_colors_rare = list(\ + "silver" = "#fdfbf3", \ + ) + +/mob/living/simple_animal/hostile/carp/Initialize(mapload) + . = ..() + carp_randomify(rarechance) + update_icons() + +/mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) + if(random_color) + var/our_color + if(prob(rarechance)) + our_color = pick(carp_colors_rare) + add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY) + else + our_color = pick(carp_colors) + add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) + add_carp_overlay() + +/mob/living/simple_animal/hostile/carp/proc/add_carp_overlay() + if(!random_color) + return + cut_overlays() + var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth") + base_overlay.appearance_flags = RESET_COLOR + add_overlay(base_overlay) + +/mob/living/simple_animal/hostile/carp/proc/add_dead_carp_overlay() + if(!random_color) + return + cut_overlays() + var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth") + base_dead_overlay.appearance_flags = RESET_COLOR + add_overlay(base_dead_overlay) + +/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) + return TRUE //No drifting in space for space carp! //original comments do not steal + +/mob/living/simple_animal/hostile/carp/AttackingTarget() + . = ..() + if(. && ishuman(target)) + var/mob/living/carbon/human/H = target + H.adjustStaminaLoss(8) + +/mob/living/simple_animal/hostile/carp/death(gibbed) + . = ..() + cut_overlays() + if(!random_color || gibbed) + return + add_dead_carp_overlay() + +/mob/living/simple_animal/hostile/carp/revive() + ..() + regenerate_icons() + +/mob/living/simple_animal/hostile/carp/regenerate_icons() + cut_overlays() + if(!random_color) + return + if(stat != DEAD) + add_carp_overlay() + else + add_dead_carp_overlay() + ..() + +/mob/living/simple_animal/hostile/carp/holocarp + icon_state = "holocarp" + icon_living = "holocarp" + maxbodytemp = INFINITY + gold_core_spawnable = NO_SPAWN + del_on_death = 1 + random_color = FALSE + +/mob/living/simple_animal/hostile/carp/megacarp + icon = 'icons/mob/alienqueen.dmi' + name = "Mega Space Carp" + desc = "A ferocious, fang bearing creature that resembles a shark. This one seems especially ticked off." + icon_state = "megacarp" + icon_living = "megacarp" + icon_dead = "megacarp_dead" + icon_gib = "megacarp_gib" + maxHealth = 20 + health = 20 + pixel_x = -16 + mob_size = MOB_SIZE_LARGE + random_color = FALSE + + obj_damage = 80 + melee_damage_lower = 20 + melee_damage_upper = 20 + + var/regen_cooldown = 0 + +/mob/living/simple_animal/hostile/carp/megacarp/Initialize() + . = ..() + name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" + melee_damage_lower += rand(2, 10) + melee_damage_upper += rand(10, 20) + maxHealth += rand(30, 60) + move_to_delay = rand(3, 7) + +/mob/living/simple_animal/hostile/carp/megacarp/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(.) + regen_cooldown = world.time + REGENERATION_DELAY + +/mob/living/simple_animal/hostile/carp/megacarp/Life() + ..() + if(regen_cooldown < world.time) + heal_overall_damage(4) + +#undef REGENERATION_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/creature.dm b/code/modules/mob/living/simple_animal/hostile/creature.dm index 225cbfea5b10..3934a9b1d2fd 100644 --- a/code/modules/mob/living/simple_animal/hostile/creature.dm +++ b/code/modules/mob/living/simple_animal/hostile/creature.dm @@ -1,17 +1,17 @@ -/mob/living/simple_animal/hostile/creature - name = "creature" - desc = "A sanity-destroying otherthing." - speak_emote = list("gibbers") - icon_state = "otherthing" - icon_living = "otherthing" - icon_dead = "otherthing-dead" - health = 80 - maxHealth = 80 - obj_damage = 100 - melee_damage_lower = 25 - melee_damage_upper = 50 - attacktext = "chomps" - attack_sound = 'sound/weapons/bite.ogg' - faction = list("creature") - gold_core_spawnable = HOSTILE_SPAWN - +/mob/living/simple_animal/hostile/creature + name = "creature" + desc = "A sanity-destroying otherthing." + speak_emote = list("gibbers") + icon_state = "otherthing" + icon_living = "otherthing" + icon_dead = "otherthing-dead" + health = 80 + maxHealth = 80 + obj_damage = 100 + melee_damage_lower = 25 + melee_damage_upper = 50 + attacktext = "chomps" + attack_sound = 'sound/weapons/bite.ogg' + faction = list("creature") + gold_core_spawnable = HOSTILE_SPAWN + diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index 5065ce63545e..0ed560f24e9f 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -1,41 +1,41 @@ -/mob/living/simple_animal/hostile/faithless - name = "Faithless" - desc = "The Wish Granter's faith in humanity, incarnate" - icon_state = "faithless" - icon_living = "faithless" - icon_dead = "faithless_dead" - speak_chance = 0 - turns_per_move = 5 - response_help = "passes through the" - response_disarm = "shoves" - response_harm = "hits the" - speed = 0 - maxHealth = 80 - health = 80 - obj_damage = 50 - harm_intent_damage = 10 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "grips" - attack_sound = 'sound/hallucinations/growl1.ogg' - speak_emote = list("growls") - emote_taunt = list("wails") - taunt_chance = 25 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("faithless") - gold_core_spawnable = HOSTILE_SPAWN - -/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0) - return 1 - -/mob/living/simple_animal/hostile/faithless/AttackingTarget() - . = ..() - if(. && iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(12)) - C.Weaken(3) - C.visible_message("\The [src] knocks down \the [C]!", \ - "\The [src] knocks you down!") \ No newline at end of file +/mob/living/simple_animal/hostile/faithless + name = "Faithless" + desc = "The Wish Granter's faith in humanity, incarnate" + icon_state = "faithless" + icon_living = "faithless" + icon_dead = "faithless_dead" + speak_chance = 0 + turns_per_move = 5 + response_help = "passes through the" + response_disarm = "shoves" + response_harm = "hits the" + speed = 0 + maxHealth = 80 + health = 80 + obj_damage = 50 + harm_intent_damage = 10 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "grips" + attack_sound = 'sound/hallucinations/growl1.ogg' + speak_emote = list("growls") + emote_taunt = list("wails") + taunt_chance = 25 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("faithless") + gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0) + return 1 + +/mob/living/simple_animal/hostile/faithless/AttackingTarget() + . = ..() + if(. && iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(12)) + C.Weaken(3) + C.visible_message("\The [src] knocks down \the [C]!", \ + "\The [src] knocks you down!") diff --git a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm index 78f5a3ea2b94..4067d5520898 100644 --- a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm +++ b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm @@ -25,4 +25,4 @@ faction = list("cat") atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 5 - pass_flags = PASSTABLE \ No newline at end of file + pass_flags = PASSTABLE diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm index c3939e41395a..c0dfa097f160 100644 --- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm +++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm @@ -78,8 +78,8 @@ pixel_y = 8 if(is_type_in_typecache(get_area(loc), invalid_area_typecache)) - var/area = pick(teleportlocs) - var/area/tp = teleportlocs[area] + var/area = pick(GLOB.teleportlocs) + var/area/tp = GLOB.teleportlocs[area] forceMove(pick(get_area_turfs(tp.type))) if((!current_victim && !admincluwne) || QDELETED(current_victim)) @@ -426,4 +426,4 @@ #undef STAGE_SPOOK #undef STAGE_TORMENT #undef STAGE_ATTACK -#undef MANIFEST_DELAY \ No newline at end of file +#undef MANIFEST_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 0465d1370078..b456db6efaf4 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -1,246 +1,246 @@ - -#define SPINNING_WEB 1 -#define LAYING_EGGS 2 -#define MOVING_TO_TARGET 3 -#define SPINNING_COCOON 4 - -//basic spider mob, these generally guard nests -/mob/living/simple_animal/hostile/poison/giant_spider - name = "giant spider" - desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes." - icon_state = "guard" - var/butcher_state = 8 // Icon state for dead spider icons - icon_living = "guard" - icon_dead = "guard_dead" - speak_emote = list("chitters") - emote_hear = list("chitters") - speak_chance = 5 - turns_per_move = 5 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - maxHealth = 200 - health = 200 - obj_damage = 60 - melee_damage_lower = 15 - melee_damage_upper = 20 - heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - faction = list("spiders") - pass_flags = PASSTABLE - move_to_delay = 6 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - gold_core_spawnable = HOSTILE_SPAWN - var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. - var/busy = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() - // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. - . = ..() - if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) - var/mob/living/carbon/C = target - var/inject_target = pick("chest", "head") - if(C.can_inject(null, FALSE, inject_target, FALSE)) - C.reagents.add_reagent("spidertoxin", venom_per_bite) - -//nursemaids - these create webs and eggs -/mob/living/simple_animal/hostile/poison/giant_spider/nurse - desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." - icon_state = "nurse" - icon_living = "nurse" - icon_dead = "nurse_dead" - butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8, /obj/item/reagent_containers/food/snacks/spidereggs = 4) - - maxHealth = 40 - health = 40 - melee_damage_lower = 5 - melee_damage_upper = 10 - venom_per_bite = 30 - var/atom/cocoon_target - var/fed = 0 - -//hunters have the most poison and move the fastest, so they can find prey -/mob/living/simple_animal/hostile/poison/giant_spider/hunter - desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." - icon_state = "hunter" - icon_living = "hunter" - icon_dead = "hunter_dead" - maxHealth = 120 - health = 120 - melee_damage_lower = 10 - melee_damage_upper = 20 - venom_per_bite = 10 - move_to_delay = 5 - -/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. - . = ..() - if(AIStatus == AI_IDLE) - //1% chance to skitter madly away - if(!busy && prob(1)) - stop_automated_movement = 1 - Goto(pick(urange(20, src, 1)), move_to_delay) - spawn(50) - stop_automated_movement = 0 - walk(src,0) - return 1 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) - spawn(100) - if(busy == MOVING_TO_TARGET) - if(cocoon_target == C && get_dist(src,cocoon_target) > 1) - cocoon_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly. - if(..()) - var/list/can_see = view(src, 10) - if(!busy && prob(30)) //30% chance to stop wandering and do something - //first, check for potential food nearby to cocoon - for(var/mob/living/C in can_see) - if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored) - cocoon_target = C - busy = MOVING_TO_TARGET - Goto(C, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(C) - return - //second, spin a sticky spiderweb on this tile - var/obj/structure/spider/stickyweb/W = locate() in get_turf(src) - if(!W) - Web() - else - //third, lay an egg cluster there - if(fed) - LayEggs() - else - //fourthly, cocoon any nearby items so those pesky pinkskins can't use them - for(var/obj/O in can_see) - if(O.anchored) - continue - - if(isitem(O) || isstructure(O) || ismachinery(O)) - cocoon_target = O - busy = MOVING_TO_TARGET - stop_automated_movement = 1 - Goto(O, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(O) - - else if(busy == MOVING_TO_TARGET && cocoon_target) - if(get_dist(src, cocoon_target) <= 1) - Wrap() - - else - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/verb/Web() - set name = "Lay Web" - set category = "Spider" - set desc = "Spread a sticky web to slow down prey." - - var/T = src.loc - - if(busy != SPINNING_WEB) - busy = SPINNING_WEB - src.visible_message("\the [src] begins to secrete a sticky substance.") - stop_automated_movement = 1 - spawn(40) - if(busy == SPINNING_WEB && src.loc == T) - new /obj/structure/spider/stickyweb(T) - busy = 0 - stop_automated_movement = 0 - - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/Wrap() - set name = "Wrap" - set category = "Spider" - set desc = "Wrap up prey to feast upon and objects for safe keeping." - - if(!cocoon_target) - var/list/choices = list() - for(var/mob/living/L in view(1,src)) - if(L == src) - continue - if(Adjacent(L)) - choices += L - for(var/obj/O in src.loc) - if(Adjacent(O)) - choices += O - cocoon_target = input(src,"What do you wish to cocoon?") in null|choices - - if(cocoon_target && busy != SPINNING_COCOON) - busy = SPINNING_COCOON - src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") - stop_automated_movement = 1 - walk(src,0) - spawn(50) - if(busy == SPINNING_COCOON) - if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) - var/obj/structure/spider/cocoon/C = new(cocoon_target.loc) - var/large_cocoon = 0 - C.pixel_x = cocoon_target.pixel_x - C.pixel_y = cocoon_target.pixel_y - for(var/obj/item/I in C.loc) - I.loc = C - for(var/obj/structure/S in C.loc) - if(!S.anchored) - S.loc = C - large_cocoon = 1 - for(var/obj/machinery/M in C.loc) - if(!M.anchored) - M.loc = C - large_cocoon = 1 - for(var/mob/living/L in C.loc) - if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) - continue - large_cocoon = 1 - L.loc = C - C.pixel_x = L.pixel_x - C.pixel_y = L.pixel_y - fed++ - visible_message("\the [src] sticks a proboscis into \the [L] and sucks a viscous substance out.") - - break - if(large_cocoon) - C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") - cocoon_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/LayEggs() - set name = "Lay Eggs" - set category = "Spider" - set desc = "Lay a clutch of eggs, but you must wrap a creature for feeding first." - - var/obj/structure/spider/eggcluster/E = locate() in get_turf(src) - if(E) - to_chat(src, "There is already a cluster of eggs here!") - else if(!fed) - to_chat(src, "You are too hungry to do this!") - else if(busy != LAYING_EGGS) - busy = LAYING_EGGS - src.visible_message("\the [src] begins to lay a cluster of eggs.") - stop_automated_movement = 1 - spawn(50) - if(busy == LAYING_EGGS) - E = locate() in get_turf(src) - if(!E) - var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(src.loc) - C.faction = faction.Copy() - C.master_commander = master_commander - if(ckey) - C.player_spiders = 1 - fed-- - busy = 0 - stop_automated_movement = 0 - -#undef SPINNING_WEB -#undef LAYING_EGGS -#undef MOVING_TO_TARGET -#undef SPINNING_COCOON + +#define SPINNING_WEB 1 +#define LAYING_EGGS 2 +#define MOVING_TO_TARGET 3 +#define SPINNING_COCOON 4 + +//basic spider mob, these generally guard nests +/mob/living/simple_animal/hostile/poison/giant_spider + name = "giant spider" + desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes." + icon_state = "guard" + var/butcher_state = 8 // Icon state for dead spider icons + icon_living = "guard" + icon_dead = "guard_dead" + speak_emote = list("chitters") + emote_hear = list("chitters") + speak_chance = 5 + turns_per_move = 5 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + maxHealth = 200 + health = 200 + obj_damage = 60 + melee_damage_lower = 15 + melee_damage_upper = 20 + heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + faction = list("spiders") + pass_flags = PASSTABLE + move_to_delay = 6 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + gold_core_spawnable = HOSTILE_SPAWN + var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. + var/busy = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() + // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. + . = ..() + if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) + var/mob/living/carbon/C = target + var/inject_target = pick("chest", "head") + if(C.can_inject(null, FALSE, inject_target, FALSE)) + C.reagents.add_reagent("spidertoxin", venom_per_bite) + +//nursemaids - these create webs and eggs +/mob/living/simple_animal/hostile/poison/giant_spider/nurse + desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." + icon_state = "nurse" + icon_living = "nurse" + icon_dead = "nurse_dead" + butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8, /obj/item/reagent_containers/food/snacks/spidereggs = 4) + + maxHealth = 40 + health = 40 + melee_damage_lower = 5 + melee_damage_upper = 10 + venom_per_bite = 30 + var/atom/cocoon_target + var/fed = 0 + +//hunters have the most poison and move the fastest, so they can find prey +/mob/living/simple_animal/hostile/poison/giant_spider/hunter + desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." + icon_state = "hunter" + icon_living = "hunter" + icon_dead = "hunter_dead" + maxHealth = 120 + health = 120 + melee_damage_lower = 10 + melee_damage_upper = 20 + venom_per_bite = 10 + move_to_delay = 5 + +/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. + . = ..() + if(AIStatus == AI_IDLE) + //1% chance to skitter madly away + if(!busy && prob(1)) + stop_automated_movement = 1 + Goto(pick(urange(20, src, 1)), move_to_delay) + spawn(50) + stop_automated_movement = 0 + walk(src,0) + return 1 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) + spawn(100) + if(busy == MOVING_TO_TARGET) + if(cocoon_target == C && get_dist(src,cocoon_target) > 1) + cocoon_target = null + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly. + if(..()) + var/list/can_see = view(src, 10) + if(!busy && prob(30)) //30% chance to stop wandering and do something + //first, check for potential food nearby to cocoon + for(var/mob/living/C in can_see) + if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored) + cocoon_target = C + busy = MOVING_TO_TARGET + Goto(C, move_to_delay) + //give up if we can't reach them after 10 seconds + GiveUp(C) + return + //second, spin a sticky spiderweb on this tile + var/obj/structure/spider/stickyweb/W = locate() in get_turf(src) + if(!W) + Web() + else + //third, lay an egg cluster there + if(fed) + LayEggs() + else + //fourthly, cocoon any nearby items so those pesky pinkskins can't use them + for(var/obj/O in can_see) + if(O.anchored) + continue + + if(isitem(O) || isstructure(O) || ismachinery(O)) + cocoon_target = O + busy = MOVING_TO_TARGET + stop_automated_movement = 1 + Goto(O, move_to_delay) + //give up if we can't reach them after 10 seconds + GiveUp(O) + + else if(busy == MOVING_TO_TARGET && cocoon_target) + if(get_dist(src, cocoon_target) <= 1) + Wrap() + + else + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/verb/Web() + set name = "Lay Web" + set category = "Spider" + set desc = "Spread a sticky web to slow down prey." + + var/T = src.loc + + if(busy != SPINNING_WEB) + busy = SPINNING_WEB + src.visible_message("\the [src] begins to secrete a sticky substance.") + stop_automated_movement = 1 + spawn(40) + if(busy == SPINNING_WEB && src.loc == T) + new /obj/structure/spider/stickyweb(T) + busy = 0 + stop_automated_movement = 0 + + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/Wrap() + set name = "Wrap" + set category = "Spider" + set desc = "Wrap up prey to feast upon and objects for safe keeping." + + if(!cocoon_target) + var/list/choices = list() + for(var/mob/living/L in view(1,src)) + if(L == src) + continue + if(Adjacent(L)) + choices += L + for(var/obj/O in src.loc) + if(Adjacent(O)) + choices += O + cocoon_target = input(src,"What do you wish to cocoon?") in null|choices + + if(cocoon_target && busy != SPINNING_COCOON) + busy = SPINNING_COCOON + src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") + stop_automated_movement = 1 + walk(src,0) + spawn(50) + if(busy == SPINNING_COCOON) + if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) + var/obj/structure/spider/cocoon/C = new(cocoon_target.loc) + var/large_cocoon = 0 + C.pixel_x = cocoon_target.pixel_x + C.pixel_y = cocoon_target.pixel_y + for(var/obj/item/I in C.loc) + I.loc = C + for(var/obj/structure/S in C.loc) + if(!S.anchored) + S.loc = C + large_cocoon = 1 + for(var/obj/machinery/M in C.loc) + if(!M.anchored) + M.loc = C + large_cocoon = 1 + for(var/mob/living/L in C.loc) + if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) + continue + large_cocoon = 1 + L.loc = C + C.pixel_x = L.pixel_x + C.pixel_y = L.pixel_y + fed++ + visible_message("\the [src] sticks a proboscis into \the [L] and sucks a viscous substance out.") + + break + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + cocoon_target = null + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/LayEggs() + set name = "Lay Eggs" + set category = "Spider" + set desc = "Lay a clutch of eggs, but you must wrap a creature for feeding first." + + var/obj/structure/spider/eggcluster/E = locate() in get_turf(src) + if(E) + to_chat(src, "There is already a cluster of eggs here!") + else if(!fed) + to_chat(src, "You are too hungry to do this!") + else if(busy != LAYING_EGGS) + busy = LAYING_EGGS + src.visible_message("\the [src] begins to lay a cluster of eggs.") + stop_automated_movement = 1 + spawn(50) + if(busy == LAYING_EGGS) + E = locate() in get_turf(src) + if(!E) + var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(src.loc) + C.faction = faction.Copy() + C.master_commander = master_commander + if(ckey) + C.player_spiders = 1 + fed-- + busy = 0 + stop_automated_movement = 0 + +#undef SPINNING_WEB +#undef LAYING_EGGS +#undef MOVING_TO_TARGET +#undef SPINNING_COCOON diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index c94648fe6411..d2a97949c8e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -1,108 +1,108 @@ -/obj/item/projectile/hivebotbullet - damage = 10 - damage_type = BRUTE - -/mob/living/simple_animal/hostile/hivebot - name = "Hivebot" - desc = "A small robot" - icon = 'icons/mob/hivebot.dmi' - icon_state = "basic" - icon_living = "basic" - icon_dead = "basic" - health = 15 - maxHealth = 15 - melee_damage_lower = 2 - melee_damage_upper = 3 - attacktext = "claws" - attack_sound = 'sound/weapons/bladeslice.ogg' - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - projectiletype = /obj/item/projectile/hivebotbullet - faction = list("hivebot") - check_friendly_fire = 1 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speak_emote = list("states") - gold_core_spawnable = HOSTILE_SPAWN - loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) - deathmessage = "blows apart!" - del_on_death = 1 - -/mob/living/simple_animal/hostile/hivebot/range - name = "Hivebot" - desc = "A smallish robot, this one is armed!" - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - -/mob/living/simple_animal/hostile/hivebot/rapid - ranged = 1 - rapid = 3 - retreat_distance = 5 - minimum_distance = 5 - -/mob/living/simple_animal/hostile/hivebot/strong - name = "Strong Hivebot" - desc = "A robot, this one is armed and looks tough!" - health = 80 - maxHealth = 80 - ranged = 1 - -/mob/living/simple_animal/hostile/hivebot/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - do_sparks(3, 1, src) - -/mob/living/simple_animal/hostile/hivebot/tele//this still needs work - name = "Beacon" - desc = "Some odd beacon thing" - icon = 'icons/mob/hivebot.dmi' - icon_state = "def_radar-off" - icon_living = "def_radar-off" - health = 200 - maxHealth = 200 - status_flags = 0 - anchored = 1 - stop_automated_movement = 1 - var/bot_type = "norm" - var/bot_amt = 10 - var/spawn_delay = 600 - var/turn_on = 0 - var/auto_spawn = 1 - proc - warpbots() - - -/mob/living/simple_animal/hostile/hivebot/tele/New() - ..() - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(5, 0, src.loc) - smoke.start() - visible_message("The [src] warps in!") - playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) - -/mob/living/simple_animal/hostile/hivebot/tele/warpbots() - icon_state = "def_radar" - visible_message("The [src] turns on!") - while(bot_amt > 0) - bot_amt-- - switch(bot_type) - if("norm") - var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src)) - H.faction = faction - if("range") - var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src)) - R.faction = faction - if("rapid") - var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src)) - F.faction = faction - spawn(100) - qdel(src) - return - -/mob/living/simple_animal/hostile/hivebot/tele/handle_automated_action() - if(!..()) - return - if(prob(2))//Might be a bit low, will mess with it likely - warpbots() +/obj/item/projectile/hivebotbullet + damage = 10 + damage_type = BRUTE + +/mob/living/simple_animal/hostile/hivebot + name = "Hivebot" + desc = "A small robot" + icon = 'icons/mob/hivebot.dmi' + icon_state = "basic" + icon_living = "basic" + icon_dead = "basic" + health = 15 + maxHealth = 15 + melee_damage_lower = 2 + melee_damage_upper = 3 + attacktext = "claws" + attack_sound = 'sound/weapons/bladeslice.ogg' + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + projectiletype = /obj/item/projectile/hivebotbullet + faction = list("hivebot") + check_friendly_fire = 1 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speak_emote = list("states") + gold_core_spawnable = HOSTILE_SPAWN + loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) + deathmessage = "blows apart!" + del_on_death = 1 + +/mob/living/simple_animal/hostile/hivebot/range + name = "Hivebot" + desc = "A smallish robot, this one is armed!" + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + +/mob/living/simple_animal/hostile/hivebot/rapid + ranged = 1 + rapid = 3 + retreat_distance = 5 + minimum_distance = 5 + +/mob/living/simple_animal/hostile/hivebot/strong + name = "Strong Hivebot" + desc = "A robot, this one is armed and looks tough!" + health = 80 + maxHealth = 80 + ranged = 1 + +/mob/living/simple_animal/hostile/hivebot/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + do_sparks(3, 1, src) + +/mob/living/simple_animal/hostile/hivebot/tele//this still needs work + name = "Beacon" + desc = "Some odd beacon thing" + icon = 'icons/mob/hivebot.dmi' + icon_state = "def_radar-off" + icon_living = "def_radar-off" + health = 200 + maxHealth = 200 + status_flags = 0 + anchored = 1 + stop_automated_movement = 1 + var/bot_type = "norm" + var/bot_amt = 10 + var/spawn_delay = 600 + var/turn_on = 0 + var/auto_spawn = 1 + proc + warpbots() + + +/mob/living/simple_animal/hostile/hivebot/tele/New() + ..() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, 0, src.loc) + smoke.start() + visible_message("The [src] warps in!") + playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) + +/mob/living/simple_animal/hostile/hivebot/tele/warpbots() + icon_state = "def_radar" + visible_message("The [src] turns on!") + while(bot_amt > 0) + bot_amt-- + switch(bot_type) + if("norm") + var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src)) + H.faction = faction + if("range") + var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src)) + R.faction = faction + if("rapid") + var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src)) + F.faction = faction + spawn(100) + qdel(src) + return + +/mob/living/simple_animal/hostile/hivebot/tele/handle_automated_action() + if(!..()) + return + if(prob(2))//Might be a bit low, will mess with it likely + warpbots() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index c5749e939c42..d539721817ab 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -1,568 +1,568 @@ -/mob/living/simple_animal/hostile - faction = list("hostile") - stop_automated_movement_when_pulled = 0 - obj_damage = 40 - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls - var/atom/target - var/ranged = FALSE - var/rapid = 0 //How many shots per volley. - var/rapid_fire_delay = 2 //Time between rapid fire shots - - var/dodging = FALSE - var/approaching_target = FALSE //We should dodge now - var/in_melee = FALSE //We should sidestep now - var/dodge_prob = 30 - var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee - - var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile - var/projectilesound - var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING - var/move_to_delay = 3 //delay for the automated movement. - var/list/friends = list() - var/list/emote_taunt = list() - var/taunt_chance = 0 - - var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. - var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled - - var/ranged_message = "fires" //Fluff text for ranged mobs - var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time - var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is - var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash - var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting - var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. - var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance - - -//These vars are related to how mobs locate and target - var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs - var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view - var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius - var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3 - var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked - var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back - var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled - var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead. - var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses - var/attack_same = 0 //Set us to 1 to allow us to attack our own faction - var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such - var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. - - var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach - var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever - -/mob/living/simple_animal/hostile/Initialize(mapload) - . = ..() - - if(!targets_from) - targets_from = src - wanted_objects = typecacheof(wanted_objects) - -/mob/living/simple_animal/hostile/Destroy() - targets_from = null - target = null - return ..() - -/mob/living/simple_animal/hostile/Life(seconds, times_fired) - . = ..() - if(!.) - walk(src, 0) - return FALSE - -/mob/living/simple_animal/hostile/handle_automated_action() - if(AIStatus == AI_OFF) - return 0 - var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use. - - if(environment_smash) - EscapeConfinement() - - if(AICanContinue(possible_targets)) - if(!QDELETED(target) && !targets_from.Adjacent(target)) - DestroyPathToTarget() - if(!MoveToTarget(possible_targets)) //if we lose our target - if(AIShouldSleep(possible_targets)) // we try to acquire a new one - toggle_ai(AI_IDLE) // otherwise we go idle - return 1 - -/mob/living/simple_animal/hostile/handle_automated_movement() - . = ..() - if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) - var/datum/cb = CALLBACK(src,.proc/sidestep) - if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later - var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle - for(var/i in 1 to sidestep_per_cycle) - addtimer(cb, (i - 1) * sidestep_delay) - else //Otherwise randomize it to make the players guessing. - addtimer(cb,rand(1, SSnpcpool.wait)) - -/mob/living/simple_animal/hostile/proc/sidestep() - if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) - return - var/target_dir = get_dir(src, target) - - var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) - var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) - var/chosen_dir = 0 - if (target_dir & (target_dir - 1)) - chosen_dir = pick(diagonal_sidestep_directions) - else - chosen_dir = pick(cardinal_sidestep_directions) - if(chosen_dir) - chosen_dir = turn(target_dir, chosen_dir) - Move(get_step(src, chosen_dir)) - face_atom(target) //Looks better if they keep looking at you when dodging - -/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) - FindTarget(list(user), 1) - return ..() - -/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) - if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) - FindTarget(list(P.firer), 1) - Goto(P.starting, move_to_delay, 3) - return ..() - -//////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// - -/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see - if(!search_objects) - . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide - - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - - for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) - if(can_see(targets_from, HM, vision_range)) - . += HM - else - . = oview(vision_range, targets_from) - -/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about - . = list() - if(!HasTargetsList) - possible_targets = ListTargets() - for(var/pos_targ in possible_targets) - var/atom/A = pos_targ - if(Found(A))//Just in case people want to override targetting - . = list(A) - break - if(CanAttack(A))//Can we attack it? - . += A - continue - var/Target = PickTarget(.) - GiveTarget(Target) - return Target //We now have a target - -/mob/living/simple_animal/hostile/proc/PossibleThreats() - . = list() - for(var/pos_targ in ListTargets()) - var/atom/A = pos_targ - if(Found(A)) - . = list(A) - break - if(CanAttack(A)) - . += A - continue - -/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available - return - -/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets - if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets - for(var/pos_targ in Targets) - var/atom/A = pos_targ - var/target_dist = get_dist(targets_from, target) - var/possible_target_distance = get_dist(targets_from, A) - if(target_dist < possible_target_distance) - Targets -= A - if(!Targets.len)//We didnt find nothin! - return - var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random - return chosen_target - -// Please do not add one-off mob AIs here, but override this function for your mob -/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? - if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids - return FALSE - - if(ismob(the_target)) //Target is in godmode, ignore it. - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - - if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it - return FALSE - if(search_objects < 2) - if(isliving(the_target)) - var/mob/living/L = the_target - var/faction_check = faction_check_mob(L) - if(robust_searching) - if(faction_check && !attack_same) - return FALSE - if(L.stat > stat_attack) - return FALSE - if(L in friends) - return FALSE - else - if((faction_check && !attack_same) || L.stat) - return FALSE - return TRUE - - if(ismecha(the_target)) - var/obj/mecha/M = the_target - if(M.occupant)//Just so we don't attack empty mechs - if(CanAttack(M.occupant)) - return TRUE - - if(isspacepod(the_target)) - var/obj/spacepod/S = the_target - if(S.pilot)//Just so we don't attack empty pods - if(CanAttack(S.pilot)) - return TRUE - - if(istype(the_target, /obj/machinery/porta_turret)) - var/obj/machinery/porta_turret/P = the_target - if(P.faction in faction) - return FALSE - if(!P.raised) //Don't attack invincible turrets - return FALSE - if(P.stat & BROKEN) //Or turrets that are already broken - return FALSE - return TRUE - - if(isobj(the_target)) - if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects)) - return TRUE - - return FALSE - -/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target - target = new_target - LosePatience() - if(target != null) - GainPatience() - Aggro() - return 1 - -//What we do after closing in -/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) - if(rapid_melee > 1) - var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) - var/delay = SSnpcpool.wait / rapid_melee - for(var/i in 1 to rapid_melee) - addtimer(cb, (i - 1)*delay) - else - AttackingTarget() - if(patience) - GainPatience() - -/mob/living/simple_animal/hostile/proc/CheckAndAttack() - if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) - AttackingTarget() - -/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target - stop_automated_movement = 1 - if(!target || !CanAttack(target)) - LoseTarget() - return 0 - if(target in possible_targets) - var/turf/T = get_turf(src) - if(target.z != T.z) - LoseTarget() - return 0 - var/target_distance = get_dist(targets_from,target) - if(ranged) //We ranged? Shoot at em - if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown - OpenFire(target) - if(!Process_Spacemove()) //Drifting - walk(src,0) - return 1 - if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target - if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run - walk_away(src,target,retreat_distance,move_to_delay) - else - Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them - else - Goto(target,move_to_delay,minimum_distance) - if(target) - if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack - MeleeAction() - else - if(rapid_melee > 1 && target_distance <= melee_queue_distance) - MeleeAction(FALSE) - in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode - return 1 - return 0 - if(environment_smash) - if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still - if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! - OpenFire(target) - if((environment_smash & ENVIRONMENT_SMASH_WALLS) || (environment_smash & ENVIRONMENT_SMASH_RWALLS)) //If we're capable of smashing through walls, forget about vision completely after finding our target - Goto(target,move_to_delay,minimum_distance) - FindHidden() - return 1 - else - if(FindHidden()) - return 1 - LoseTarget() - return 0 - -/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) - if(target == src.target) - approaching_target = TRUE - else - approaching_target = FALSE - walk_to(src, target, minimum_distance, delay) - -/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) - . = ..() - if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs - if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight - target = null - LoseSearchObjects() - if(AIStatus != AI_ON && AIStatus != AI_OFF) - toggle_ai(AI_ON) - FindTarget() - else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one - FindTarget() - -/mob/living/simple_animal/hostile/proc/AttackingTarget() - SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) - in_melee = TRUE - return target.attack_animal(src) - -/mob/living/simple_animal/hostile/proc/Aggro() - vision_range = aggro_vision_range - if(target && emote_taunt.len && prob(taunt_chance)) - emote("me", 1, "[pick(emote_taunt)] at [target].") - taunt_chance = max(taunt_chance-7,2) - -/mob/living/simple_animal/hostile/proc/LoseAggro() - stop_automated_movement = 0 - vision_range = initial(vision_range) - taunt_chance = initial(taunt_chance) - -/mob/living/simple_animal/hostile/proc/LoseTarget() - target = null - approaching_target = FALSE - in_melee = FALSE - walk(src, 0) - LoseAggro() - -//////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// - -/mob/living/simple_animal/hostile/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - LoseTarget() - -/mob/living/simple_animal/hostile/proc/summon_backup(distance) - do_alert_animation(src) - playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) - for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) - if(faction_check_mob(M, TRUE)) - if(M.AIStatus == AI_OFF) - return - else - M.Goto(src,M.move_to_delay,M.minimum_distance) - -/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A) - if(check_friendly_fire) - for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory - for(var/mob/living/L in T) - if(L == src || L == A) - continue - if(faction_check_mob(L) && !attack_same) - return TRUE - -/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) - if(CheckFriendlyFire(A)) - return - visible_message("[src] [ranged_message] at [A]!") - - - if(rapid > 1) - var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) - for(var/i in 1 to rapid) - addtimer(cb, (i - 1)*rapid_fire_delay) - else - Shoot(A) - ranged_cooldown = world.time + ranged_cooldown_time - -/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) - if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) - return - var/turf/startloc = get_turf(targets_from) - if(casingtype) - var/obj/item/ammo_casing/casing = new casingtype(startloc) - playsound(src, projectilesound, 100, 1) - casing.fire(targeted_atom, src, zone_override = ran_zone()) - else if(projectiletype) - var/obj/item/projectile/P = new projectiletype(startloc) - playsound(src, projectilesound, 100, 1) - P.current = startloc - P.starting = startloc - P.firer = src - P.yo = targeted_atom.y - startloc.y - P.xo = targeted_atom.x - startloc.x - if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space - newtonian_move(get_dir(targeted_atom, targets_from)) - P.original = targeted_atom - P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src) - P.fire() - return P - -/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T) - return iswallturf(T) || ismineralturf(T) - -/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) - if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) - return dodge(newloc, dir) - else - return ..() - -/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) - //Assuming we move towards the target we want to swerve toward them to get closer - var/cdir = turn(move_direction, 45) - var/ccdir = turn(move_direction, -45) - dodging = FALSE - . = Move(get_step(loc,pick(cdir,ccdir))) - if(!.)//Can't dodge there so we just carry on - . = Move(moving_to,move_direction) - dodging = TRUE - -/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) - var/turf/T = get_step(targets_from, direction) - if(QDELETED(T)) - return - if(T.Adjacent(targets_from)) - if(CanSmashTurfs(T)) - T.attack_animal(src) - return - for(var/obj/O in T.contents) - if(!O.Adjacent(targets_from)) - continue - if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) - O.attack_animal(src) - return - -/mob/living/simple_animal/hostile/proc/DestroyPathToTarget() - if(environment_smash) - EscapeConfinement() - var/dir_to_target = get_dir(targets_from, target) - var/dir_list = list() - if(dir_to_target in diagonals) //it's diagonal, so we need two directions to hit - for(var/direction in cardinal) - if(direction & dir_to_target) - dir_list += direction - else - dir_list += dir_to_target - for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need - DestroyObjectsInDirection(direction) - -/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them - if(environment_smash) - EscapeConfinement() - for(var/dir in cardinal) - DestroyObjectsInDirection(dir) - -/mob/living/simple_animal/hostile/proc/EscapeConfinement() - if(buckled) - buckled.attack_animal(src) - if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? - var/atom/A = targets_from.loc - A.attack_animal(src)//Bang on it till we get out - -/mob/living/simple_animal/hostile/proc/FindHidden() - if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) - var/atom/A = target.loc - Goto(A,move_to_delay,minimum_distance) - if(A.Adjacent(targets_from)) - A.attack_animal(src) - return 1 - -/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing - if(ranged && ranged_cooldown <= world.time) - target = A - OpenFire(A) - ..() - - - -////// AI Status /////// -/mob/living/simple_animal/hostile/proc/AICanContinue(var/list/possible_targets) - switch(AIStatus) - if(AI_ON) - . = 1 - if(AI_IDLE) - if(FindTarget(possible_targets, 1)) - . = 1 - toggle_ai(AI_ON) //Wake up for more than one Life() cycle. - else - . = 0 - -/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) - return !FindTarget(possible_targets, 1) - - -//These two procs handle losing our target if we've failed to attack them for -//more than lose_patience_timeout deciseconds, which probably means we're stuck -/mob/living/simple_animal/hostile/proc/GainPatience() - if(lose_patience_timeout) - LosePatience() - lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) - - -/mob/living/simple_animal/hostile/proc/LosePatience() - deltimer(lose_patience_timer_id) - - -//These two procs handle losing and regaining search_objects when attacked by a mob -/mob/living/simple_animal/hostile/proc/LoseSearchObjects() - search_objects = 0 - deltimer(search_objects_timer_id) - search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE) - - -/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value) - if(!value) - value = initial(search_objects) - search_objects = value - -/mob/living/simple_animal/hostile/consider_wakeup() - ..() - var/list/tlist - var/turf/T = get_turf(src) - - if(!T) - return - - if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 - toggle_ai(AI_Z_OFF) - return - - var/cheap_search = isturf(T) && !is_station_level(T.z) - if(cheap_search) - tlist = ListTargetsLazy(T.z) - else - tlist = ListTargets() - - if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) - if(cheap_search) //Try again with full effort - FindTarget() - toggle_ai(AI_ON) - -/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - . = list() - for(var/I in SSmobs.clients_by_zlevel[_Z]) - var/mob/M = I - if(get_dist(M, src) < vision_range) - if(isturf(M.loc)) - . += M - else if(M.loc.type in hostile_machines) - . += M.loc \ No newline at end of file +/mob/living/simple_animal/hostile + faction = list("hostile") + stop_automated_movement_when_pulled = 0 + obj_damage = 40 + environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls + var/atom/target + var/ranged = FALSE + var/rapid = 0 //How many shots per volley. + var/rapid_fire_delay = 2 //Time between rapid fire shots + + var/dodging = FALSE + var/approaching_target = FALSE //We should dodge now + var/in_melee = FALSE //We should sidestep now + var/dodge_prob = 30 + var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee + + var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile + var/projectilesound + var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING + var/move_to_delay = 3 //delay for the automated movement. + var/list/friends = list() + var/list/emote_taunt = list() + var/taunt_chance = 0 + + var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. + var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled + + var/ranged_message = "fires" //Fluff text for ranged mobs + var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time + var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is + var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash + var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting + var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. + var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance + + +//These vars are related to how mobs locate and target + var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs + var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view + var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius + var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3 + var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked + var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back + var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled + var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead. + var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses + var/attack_same = 0 //Set us to 1 to allow us to attack our own faction + var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such + var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. + + var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach + var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever + +/mob/living/simple_animal/hostile/Initialize(mapload) + . = ..() + + if(!targets_from) + targets_from = src + wanted_objects = typecacheof(wanted_objects) + +/mob/living/simple_animal/hostile/Destroy() + targets_from = null + target = null + return ..() + +/mob/living/simple_animal/hostile/Life(seconds, times_fired) + . = ..() + if(!.) + walk(src, 0) + return FALSE + +/mob/living/simple_animal/hostile/handle_automated_action() + if(AIStatus == AI_OFF) + return 0 + var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use. + + if(environment_smash) + EscapeConfinement() + + if(AICanContinue(possible_targets)) + if(!QDELETED(target) && !targets_from.Adjacent(target)) + DestroyPathToTarget() + if(!MoveToTarget(possible_targets)) //if we lose our target + if(AIShouldSleep(possible_targets)) // we try to acquire a new one + toggle_ai(AI_IDLE) // otherwise we go idle + return 1 + +/mob/living/simple_animal/hostile/handle_automated_movement() + . = ..() + if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) + var/datum/cb = CALLBACK(src,.proc/sidestep) + if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later + var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle + for(var/i in 1 to sidestep_per_cycle) + addtimer(cb, (i - 1) * sidestep_delay) + else //Otherwise randomize it to make the players guessing. + addtimer(cb,rand(1, SSnpcpool.wait)) + +/mob/living/simple_animal/hostile/proc/sidestep() + if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) + return + var/target_dir = get_dir(src, target) + + var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) + var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) + var/chosen_dir = 0 + if (target_dir & (target_dir - 1)) + chosen_dir = pick(diagonal_sidestep_directions) + else + chosen_dir = pick(cardinal_sidestep_directions) + if(chosen_dir) + chosen_dir = turn(target_dir, chosen_dir) + Move(get_step(src, chosen_dir)) + face_atom(target) //Looks better if they keep looking at you when dodging + +/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) + FindTarget(list(user), 1) + return ..() + +/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) + if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) + FindTarget(list(P.firer), 1) + Goto(P.starting, move_to_delay, 3) + return ..() + +//////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// + +/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see + if(!search_objects) + . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + + for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) + if(can_see(targets_from, HM, vision_range)) + . += HM + else + . = oview(vision_range, targets_from) + +/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about + . = list() + if(!HasTargetsList) + possible_targets = ListTargets() + for(var/pos_targ in possible_targets) + var/atom/A = pos_targ + if(Found(A))//Just in case people want to override targetting + . = list(A) + break + if(CanAttack(A))//Can we attack it? + . += A + continue + var/Target = PickTarget(.) + GiveTarget(Target) + return Target //We now have a target + +/mob/living/simple_animal/hostile/proc/PossibleThreats() + . = list() + for(var/pos_targ in ListTargets()) + var/atom/A = pos_targ + if(Found(A)) + . = list(A) + break + if(CanAttack(A)) + . += A + continue + +/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available + return + +/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets + if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets + for(var/pos_targ in Targets) + var/atom/A = pos_targ + var/target_dist = get_dist(targets_from, target) + var/possible_target_distance = get_dist(targets_from, A) + if(target_dist < possible_target_distance) + Targets -= A + if(!Targets.len)//We didnt find nothin! + return + var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random + return chosen_target + +// Please do not add one-off mob AIs here, but override this function for your mob +/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? + if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids + return FALSE + + if(ismob(the_target)) //Target is in godmode, ignore it. + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + + if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it + return FALSE + if(search_objects < 2) + if(isliving(the_target)) + var/mob/living/L = the_target + var/faction_check = faction_check_mob(L) + if(robust_searching) + if(faction_check && !attack_same) + return FALSE + if(L.stat > stat_attack) + return FALSE + if(L in friends) + return FALSE + else + if((faction_check && !attack_same) || L.stat) + return FALSE + return TRUE + + if(ismecha(the_target)) + var/obj/mecha/M = the_target + if(M.occupant)//Just so we don't attack empty mechs + if(CanAttack(M.occupant)) + return TRUE + + if(isspacepod(the_target)) + var/obj/spacepod/S = the_target + if(S.pilot)//Just so we don't attack empty pods + if(CanAttack(S.pilot)) + return TRUE + + if(istype(the_target, /obj/machinery/porta_turret)) + var/obj/machinery/porta_turret/P = the_target + if(P.faction in faction) + return FALSE + if(!P.raised) //Don't attack invincible turrets + return FALSE + if(P.stat & BROKEN) //Or turrets that are already broken + return FALSE + return TRUE + + if(isobj(the_target)) + if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects)) + return TRUE + + return FALSE + +/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target + target = new_target + LosePatience() + if(target != null) + GainPatience() + Aggro() + return 1 + +//What we do after closing in +/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) + if(rapid_melee > 1) + var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) + var/delay = SSnpcpool.wait / rapid_melee + for(var/i in 1 to rapid_melee) + addtimer(cb, (i - 1)*delay) + else + AttackingTarget() + if(patience) + GainPatience() + +/mob/living/simple_animal/hostile/proc/CheckAndAttack() + if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) + AttackingTarget() + +/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target + stop_automated_movement = 1 + if(!target || !CanAttack(target)) + LoseTarget() + return 0 + if(target in possible_targets) + var/turf/T = get_turf(src) + if(target.z != T.z) + LoseTarget() + return 0 + var/target_distance = get_dist(targets_from,target) + if(ranged) //We ranged? Shoot at em + if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown + OpenFire(target) + if(!Process_Spacemove()) //Drifting + walk(src,0) + return 1 + if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target + if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run + walk_away(src,target,retreat_distance,move_to_delay) + else + Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them + else + Goto(target,move_to_delay,minimum_distance) + if(target) + if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack + MeleeAction() + else + if(rapid_melee > 1 && target_distance <= melee_queue_distance) + MeleeAction(FALSE) + in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode + return 1 + return 0 + if(environment_smash) + if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still + if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! + OpenFire(target) + if((environment_smash & ENVIRONMENT_SMASH_WALLS) || (environment_smash & ENVIRONMENT_SMASH_RWALLS)) //If we're capable of smashing through walls, forget about vision completely after finding our target + Goto(target,move_to_delay,minimum_distance) + FindHidden() + return 1 + else + if(FindHidden()) + return 1 + LoseTarget() + return 0 + +/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) + if(target == src.target) + approaching_target = TRUE + else + approaching_target = FALSE + walk_to(src, target, minimum_distance, delay) + +/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) + . = ..() + if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs + if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight + target = null + LoseSearchObjects() + if(AIStatus != AI_ON && AIStatus != AI_OFF) + toggle_ai(AI_ON) + FindTarget() + else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one + FindTarget() + +/mob/living/simple_animal/hostile/proc/AttackingTarget() + SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) + in_melee = TRUE + return target.attack_animal(src) + +/mob/living/simple_animal/hostile/proc/Aggro() + vision_range = aggro_vision_range + if(target && emote_taunt.len && prob(taunt_chance)) + emote("me", 1, "[pick(emote_taunt)] at [target].") + taunt_chance = max(taunt_chance-7,2) + +/mob/living/simple_animal/hostile/proc/LoseAggro() + stop_automated_movement = 0 + vision_range = initial(vision_range) + taunt_chance = initial(taunt_chance) + +/mob/living/simple_animal/hostile/proc/LoseTarget() + target = null + approaching_target = FALSE + in_melee = FALSE + walk(src, 0) + LoseAggro() + +//////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// + +/mob/living/simple_animal/hostile/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + LoseTarget() + +/mob/living/simple_animal/hostile/proc/summon_backup(distance) + do_alert_animation(src) + playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) + for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) + if(faction_check_mob(M, TRUE)) + if(M.AIStatus == AI_OFF) + return + else + M.Goto(src,M.move_to_delay,M.minimum_distance) + +/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A) + if(check_friendly_fire) + for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory + for(var/mob/living/L in T) + if(L == src || L == A) + continue + if(faction_check_mob(L) && !attack_same) + return TRUE + +/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) + if(CheckFriendlyFire(A)) + return + visible_message("[src] [ranged_message] at [A]!") + + + if(rapid > 1) + var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) + for(var/i in 1 to rapid) + addtimer(cb, (i - 1)*rapid_fire_delay) + else + Shoot(A) + ranged_cooldown = world.time + ranged_cooldown_time + +/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) + if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) + return + var/turf/startloc = get_turf(targets_from) + if(casingtype) + var/obj/item/ammo_casing/casing = new casingtype(startloc) + playsound(src, projectilesound, 100, 1) + casing.fire(targeted_atom, src, zone_override = ran_zone()) + else if(projectiletype) + var/obj/item/projectile/P = new projectiletype(startloc) + playsound(src, projectilesound, 100, 1) + P.current = startloc + P.starting = startloc + P.firer = src + P.yo = targeted_atom.y - startloc.y + P.xo = targeted_atom.x - startloc.x + if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space + newtonian_move(get_dir(targeted_atom, targets_from)) + P.original = targeted_atom + P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src) + P.fire() + return P + +/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T) + return iswallturf(T) || ismineralturf(T) + +/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) + if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) + return dodge(newloc, dir) + else + return ..() + +/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) + //Assuming we move towards the target we want to swerve toward them to get closer + var/cdir = turn(move_direction, 45) + var/ccdir = turn(move_direction, -45) + dodging = FALSE + . = Move(get_step(loc,pick(cdir,ccdir))) + if(!.)//Can't dodge there so we just carry on + . = Move(moving_to,move_direction) + dodging = TRUE + +/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) + var/turf/T = get_step(targets_from, direction) + if(QDELETED(T)) + return + if(T.Adjacent(targets_from)) + if(CanSmashTurfs(T)) + T.attack_animal(src) + return + for(var/obj/O in T.contents) + if(!O.Adjacent(targets_from)) + continue + if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) + O.attack_animal(src) + return + +/mob/living/simple_animal/hostile/proc/DestroyPathToTarget() + if(environment_smash) + EscapeConfinement() + var/dir_to_target = get_dir(targets_from, target) + var/dir_list = list() + if(dir_to_target in GLOB.diagonals) //it's diagonal, so we need two directions to hit + for(var/direction in GLOB.cardinal) + if(direction & dir_to_target) + dir_list += direction + else + dir_list += dir_to_target + for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need + DestroyObjectsInDirection(direction) + +/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them + if(environment_smash) + EscapeConfinement() + for(var/dir in GLOB.cardinal) + DestroyObjectsInDirection(dir) + +/mob/living/simple_animal/hostile/proc/EscapeConfinement() + if(buckled) + buckled.attack_animal(src) + if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? + var/atom/A = targets_from.loc + A.attack_animal(src)//Bang on it till we get out + +/mob/living/simple_animal/hostile/proc/FindHidden() + if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) + var/atom/A = target.loc + Goto(A,move_to_delay,minimum_distance) + if(A.Adjacent(targets_from)) + A.attack_animal(src) + return 1 + +/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing + if(ranged && ranged_cooldown <= world.time) + target = A + OpenFire(A) + ..() + + + +////// AI Status /////// +/mob/living/simple_animal/hostile/proc/AICanContinue(var/list/possible_targets) + switch(AIStatus) + if(AI_ON) + . = 1 + if(AI_IDLE) + if(FindTarget(possible_targets, 1)) + . = 1 + toggle_ai(AI_ON) //Wake up for more than one Life() cycle. + else + . = 0 + +/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) + return !FindTarget(possible_targets, 1) + + +//These two procs handle losing our target if we've failed to attack them for +//more than lose_patience_timeout deciseconds, which probably means we're stuck +/mob/living/simple_animal/hostile/proc/GainPatience() + if(lose_patience_timeout) + LosePatience() + lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) + + +/mob/living/simple_animal/hostile/proc/LosePatience() + deltimer(lose_patience_timer_id) + + +//These two procs handle losing and regaining search_objects when attacked by a mob +/mob/living/simple_animal/hostile/proc/LoseSearchObjects() + search_objects = 0 + deltimer(search_objects_timer_id) + search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE) + + +/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value) + if(!value) + value = initial(search_objects) + search_objects = value + +/mob/living/simple_animal/hostile/consider_wakeup() + ..() + var/list/tlist + var/turf/T = get_turf(src) + + if(!T) + return + + if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 + toggle_ai(AI_Z_OFF) + return + + var/cheap_search = isturf(T) && !is_station_level(T.z) + if(cheap_search) + tlist = ListTargetsLazy(T.z) + else + tlist = ListTargets() + + if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) + if(cheap_search) //Try again with full effort + FindTarget() + toggle_ai(AI_ON) + +/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + . = list() + for(var/I in SSmobs.clients_by_zlevel[_Z]) + var/mob/M = I + if(get_dist(M, src) < vision_range) + if(isturf(M.loc)) + . += M + else if(M.loc.type in hostile_machines) + . += M.loc diff --git a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm index 79d6c42cb110..934e13126061 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm @@ -42,4 +42,4 @@ if(prob(15) && iscarbon(target)) var/mob/living/carbon/C = target C.Weaken(3) - C.visible_message("\the [src] knocks down \the [C]!") \ No newline at end of file + C.visible_message("\the [src] knocks down \the [C]!") diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 3061c7653592..70585b818f58 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -27,7 +27,7 @@ Difficulty: Medium maxHealth = 900 icon_state = "miner" icon_living = "miner" - icon = 'icons/mob/alienqueen.dmi' + icon = 'icons/mob/lavaland/blood_drunk.dmi' light_color = "#E4C7C5" flying = FALSE speak_emote = list("roars") @@ -37,7 +37,7 @@ Difficulty: Medium projectilesound = 'sound/weapons/kenetic_accel.ogg' ranged = TRUE ranged_cooldown_time = 16 - pixel_x = -16 + pixel_x = -7 crusher_loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator, /obj/item/crusher_trophy/miner_eye) loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator) wander = FALSE @@ -290,4 +290,4 @@ Difficulty: Medium if(. && prob(12)) INVOKE_ASYNC(src, .proc/dash) -#undef MINER_DASH_RANGE \ No newline at end of file +#undef MINER_DASH_RANGE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 66799ffc6c6e..6cde07d0b8d8 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -396,9 +396,9 @@ Difficulty: Hard if(. > 0 && prob(25)) var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc) if(prob(40)) - step(B, pick(cardinal)) + step(B, pick(GLOB.cardinal)) else - B.setDir(pick(cardinal)) + B.setDir(pick(GLOB.cardinal)) /obj/effect/decal/cleanable/blood/gibs/bubblegum name = "thick blood" @@ -548,4 +548,4 @@ Difficulty: Hard return /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 8eed750804fd..7f1b3942d94e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -103,7 +103,7 @@ Difficulty: Very Hard visible_message("\"You can't dodge.\"") ranged_cooldown = world.time + 30 telegraph() - dir_shots(alldirs) + dir_shots(GLOB.alldirs) move_to_delay = 3 return else @@ -127,13 +127,13 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() ranged_cooldown = world.time + 40 - dir_shots(diagonals) + dir_shots(GLOB.diagonals) SLEEP_CHECK_DEATH(10) - dir_shots(cardinal) + dir_shots(GLOB.cardinal) SLEEP_CHECK_DEATH(10) - dir_shots(diagonals) + dir_shots(GLOB.diagonals) SLEEP_CHECK_DEATH(10) - dir_shots(cardinal) + dir_shots(GLOB.cardinal) /mob/living/simple_animal/hostile/megafauna/colossus/proc/select_spiral_attack() telegraph() @@ -150,7 +150,7 @@ Difficulty: Very Hard INVOKE_ASYNC(src, .proc/spiral_shoot, TRUE) /mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = pick(TRUE, FALSE), counter_start = 8) - var/turf/start_turf = get_step(src, pick(alldirs)) + var/turf/start_turf = get_step(src, pick(GLOB.alldirs)) var/counter = counter_start for(var/i in 1 to 80) if(negative) @@ -198,7 +198,7 @@ Difficulty: Very Hard /mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs) if(!islist(dirs)) - dirs = alldirs.Copy() + dirs = GLOB.alldirs.Copy() playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2) for(var/d in dirs) var/turf/E = get_step(src, d) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 8b6d6fed947f..e97b7671b67f 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -366,7 +366,7 @@ Difficulty: Medium if(L && !QDELETED(L)) // Some mobs are deleted on death var/throw_dir = get_dir(src, L) if(L.loc == loc) - throw_dir = pick(alldirs) + throw_dir = pick(GLOB.alldirs) var/throwtarget = get_edge_target_turf(src, throw_dir) L.throw_at(throwtarget, 3) visible_message("[L] is thrown clear of [src]!") @@ -669,4 +669,4 @@ obj/effect/temp_visual/fireball ..(targets, user, 3) /mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/AltClickOn(atom/movable/A) - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 088d8631e77c..716f54362233 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -179,18 +179,18 @@ Difficulty: Hard if((prob(anger_modifier) || target.Adjacent(src)) && target != src) var/obj/effect/temp_visual/hierophant/chaser/OC = new(loc, src, target, chaser_speed * 1.5, FALSE) OC.moving = 4 - OC.moving_dir = pick(cardinal - C.moving_dir) + OC.moving_dir = pick(GLOB.cardinal - C.moving_dir) else if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2) blink(target) else if(prob(70 - anger_modifier)) //a cross blast of some type if(prob(anger_modifier * (2 / target_slowness)) && health < maxHealth * 0.5) //we're super angry do it at all dirs - INVOKE_ASYNC(src, .proc/blasts, target, alldirs) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.alldirs) else if(prob(60)) - INVOKE_ASYNC(src, .proc/blasts, target, cardinal) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.cardinal) else - INVOKE_ASYNC(src, .proc/blasts, target, diagonals) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.diagonals) else //just release a burst of power INVOKE_ASYNC(src, .proc/burst, get_turf(src)) @@ -226,9 +226,9 @@ Difficulty: Hard while(!QDELETED(target) && cross_counter) cross_counter-- if(prob(60)) - INVOKE_ASYNC(src, .proc/blasts, target, cardinal) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.cardinal) else - INVOKE_ASYNC(src, .proc/blasts, target, diagonals) + INVOKE_ASYNC(src, .proc/blasts, target, GLOB.diagonals) SLEEP_CHECK_DEATH(6 + target_slowness) animate(src, color = oldcolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) @@ -244,7 +244,7 @@ Difficulty: Hard animate(src, color = "#660099", time = 6) SLEEP_CHECK_DEATH(6) var/list/targets = ListTargets() - var/list/cardinal_copy = cardinal.Copy() + var/list/cardinal_copy = GLOB.cardinal.Copy() while(targets.len && cardinal_copy.len) var/mob/living/pickedtarget = pick(targets) if(targets.len >= cardinal_copy.len) @@ -263,13 +263,13 @@ Difficulty: Hard SLEEP_CHECK_DEATH(8) blinking = FALSE -/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blasts(mob/victim, var/list/directions = cardinal) //fires cross blasts with a delay +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blasts(mob/victim, var/list/directions = GLOB.cardinal) //fires cross blasts with a delay var/turf/T = get_turf(victim) if(!T) return - if(directions == cardinal) + if(directions == GLOB.cardinal) new /obj/effect/temp_visual/hierophant/telegraph/cardinal(T, src) - else if(directions == diagonals) + else if(directions == GLOB.diagonals) new /obj/effect/temp_visual/hierophant/telegraph/diagonal(T, src) else new /obj/effect/temp_visual/hierophant/telegraph(T, src) @@ -295,7 +295,7 @@ Difficulty: Hard if((istype(get_area(T), /area/ruin/unpowered/hierophant) || istype(get_area(src), /area/ruin/unpowered/hierophant)) && victim != src) return arena_cooldown = world.time + initial(arena_cooldown) - for(var/d in cardinal) + for(var/d in GLOB.cardinal) INVOKE_ASYNC(src, .proc/arena_squares, T, d) for(var/t in RANGE_TURFS(11, T)) if(t && get_dist(t, T) == 11) @@ -565,7 +565,7 @@ Difficulty: Hard /obj/effect/temp_visual/hierophant/chaser/proc/get_target_dir() . = get_cardinal_dir(src, targetturf) if((. != previous_moving_dir && . == more_previouser_moving_dir) || . == 0) //we're alternating, recalculate - var/list/cardinal_copy = cardinal.Copy() + var/list/cardinal_copy = GLOB.cardinal.Copy() cardinal_copy -= more_previouser_moving_dir . = pick(cardinal_copy) @@ -737,4 +737,4 @@ Difficulty: Hard icon_state = null gpstag = "Zealous Signal" desc = "Heed its words." - invisibility = 100 \ No newline at end of file + invisibility = 100 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 81c96135b0ec..b3618efff0a1 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -158,4 +158,4 @@ /datum/action/innate/megafauna_attack/Activate() M.chosen_attack = chosen_attack_num - to_chat(M, chosen_message) \ No newline at end of file + to_chat(M, chosen_message) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index f31b6a8619b0..2515da9dff9f 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa /mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Initialize(mapload) . = ..() swarmer_caps = GLOB.AISwarmerCapsByType //for admin-edits - for(var/ddir in cardinal) + for(var/ddir in GLOB.cardinal) new /obj/structure/swarmer/blockade (get_step(src, ddir)) var/mob/living/simple_animal/hostile/swarmer/ai/resource/R = new(loc) step(R, ddir) //Step the swarmers, instead of spawning them there, incase the turf is solid @@ -285,4 +285,4 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa name = "swarmer catwalk" desc = "A catwalk-like mesh, produced by swarmers to allow them to navigate hostile terrain." icon = 'icons/obj/smooth_structures/swarmer_catwalk.dmi' - icon_state = "swarmer_catwalk" \ No newline at end of file + icon_state = "swarmer_catwalk" diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 761f44d11bd9..848a285abf49 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -1,283 +1,283 @@ -/mob/living/simple_animal/hostile/mimic - name = "crate" - desc = "A rectangular steel crate." - icon = 'icons/obj/crates.dmi' - icon_state = "crate" - icon_living = "crate" - - response_help = "touches the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 0 - maxHealth = 250 - health = 250 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "attacks" - attack_sound = 'sound/weapons/bite.ogg' - emote_taunt = list("growls") - speak_emote = list("creaks") - taunt_chance = 30 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("mimic") - move_to_delay = 9 - - var/is_electronic = 0 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - -/mob/living/simple_animal/hostile/mimic/emp_act(severity) - if(is_electronic) - switch(severity) - if(1) - death() - if(2) - adjustBruteLoss(50) - ..(severity) - -// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. -/mob/living/simple_animal/hostile/mimic/crate - attacktext = "bites" - stop_automated_movement = 1 - wander = 0 - var/attempt_open = 0 - -// Pickup loot -/mob/living/simple_animal/hostile/mimic/crate/Initialize() - ..() - for(var/obj/item/I in loc) - I.loc = src - -/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() - ..() - if(prob(90)) - icon_state = "[initial(icon_state)]open" - else - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/ListTargets() - if(attempt_open) - return ..() - return ..(1) - -/mob/living/simple_animal/hostile/mimic/crate/FindTarget() - . = ..() - if(.) - trigger() - -/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() - . = ..() - if(.) - icon_state = initial(icon_state) - if(prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(2) - C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() - if(!attempt_open) - visible_message("[src] starts to move!") - attempt_open = 1 - -/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) - trigger() - . = ..() - -/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() - ..() - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) - if(can_die()) - var/obj/structure/closet/crate/C = new(get_turf(src)) - // Put loot in crate - for(var/obj/O in src) - O.forceMove(C) - // due to `del_on_death` - return ..() - -var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window) - -/mob/living/simple_animal/hostile/mimic/copy - health = 100 - maxHealth = 100 - var/mob/living/creator = null // the creator - var/destroy_objects = 0 - var/knockdown_people = 0 - var/image/googly_eyes = null - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/hostile/mimic/copy/New(loc, obj/copy, mob/living/creator, destroy_original = 0) - ..(loc) - CopyObject(copy, creator, destroy_original) - -/mob/living/simple_animal/hostile/mimic/copy/Life() - ..() - if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize - adjustBruteLoss(1) - for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell - death() - -/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) - if(can_die()) - for(var/atom/movable/M in src) - M.loc = get_turf(src) - // due to `del_on_death` - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ListTargets() - . = ..() - return . - creator - -/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(var/mob/owner) - if(owner != creator) - LoseTarget() - creator = owner - faction |= "\ref[owner]" - -/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(var/obj/O) - if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, protected_objects)) - return 1 - return 0 - -/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/user, var/destroy_original = 0) - if(destroy_original || CheckObject(O)) - O.loc = src - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - icon_living = icon_state - overlays = O.overlays - googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") - overlays += googly_eyes - if(istype(O, /obj/structure) || istype(O, /obj/machinery)) - health = (anchored * 50) + 50 - destroy_objects = 1 - if(O.density && O.anchored) - knockdown_people = 1 - melee_damage_lower *= 2 - melee_damage_upper *= 2 - if(istype(O, /obj/machinery)) - is_electronic = 1 - else if(istype(O, /obj/item)) - var/obj/item/I = O - health = 15 * I.w_class - melee_damage_lower = 2 + I.force - melee_damage_upper = 2 + I.force - move_to_delay = 2 * I.w_class + 1 - maxHealth = health - if(user) - creator = user - faction += "\ref[creator]" // very unique - if(destroy_original) - qdel(O) - return 1 - -/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() - if(destroy_objects) - ..() - -/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() - . = ..() - if(knockdown_people && . && prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(2) - C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/mimic/copy/Aggro() - ..() - googly_eyes.dir = get_dir(src,target) - -/mob/living/simple_animal/hostile/mimic/copy/machine - speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ - "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") - speak_chance = 15 - -/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(var/atom/the_target) - if(the_target == creator) // Don't attack our creator AI. - return 0 - if(isrobot(the_target)) - var/mob/living/silicon/robot/R = the_target - if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. - return 0 - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ranged - var/obj/item/gun/TrueGun = null - var/obj/item/gun/magic/Zapstick - var/obj/item/gun/projectile/Pewgun - var/obj/item/gun/energy/Zapgun - -/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) - if(..()) - emote_see = list("aims menacingly") - obj_damage = 0 - environment_smash = 0 //needed? seems weird for them to do so - ranged = 1 - retreat_distance = 1 //just enough to shoot - minimum_distance = 6 - var/obj/item/gun/G = O - melee_damage_upper = G.force - melee_damage_lower = G.force - max(0, (G.force / 2)) - move_to_delay = 2 * G.w_class + 1 - projectilesound = G.fire_sound - TrueGun = G - if(istype(G, /obj/item/gun/magic)) - Zapstick = G - var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type - projectiletype = initial(M.projectile_type) - if(istype(G, /obj/item/gun/projectile)) - Pewgun = G - var/obj/item/ammo_box/magazine/M = Pewgun.mag_type - casingtype = initial(M.ammo_type) - if(istype(G, /obj/item/gun/energy)) - Zapgun = G - var/selectfiresetting = Zapgun.select - var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] - projectiletype = initial(E.projectile_type) - -/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) - if(Zapgun) - if(Zapgun.cell) - var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] - if(Zapgun.cell.charge >= shot.e_cost) - Zapgun.cell.use(shot.e_cost) - Zapgun.update_icon() - ..() - else if(Zapstick) - if(Zapstick.charges) - Zapstick.charges-- - Zapstick.update_icon() - ..() - else if(Pewgun) - if(Pewgun.chambered) - if(Pewgun.chambered.BB) - qdel(Pewgun.chambered.BB) - Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update - Pewgun.chambered.update_icon() - ..() - else - visible_message("The [src] clears a jam!") - Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs - Pewgun.chambered = null - if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - Pewgun.update_icon() - else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - visible_message("The [src] cocks itself!") - else - ranged = 0 //BANZAIIII - retreat_distance = 0 - minimum_distance = 1 - return - icon_state = TrueGun.icon_state - icon_living = TrueGun.icon_state +/mob/living/simple_animal/hostile/mimic + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/crates.dmi' + icon_state = "crate" + icon_living = "crate" + + response_help = "touches the" + response_disarm = "pushes the" + response_harm = "hits the" + speed = 0 + maxHealth = 250 + health = 250 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "attacks" + attack_sound = 'sound/weapons/bite.ogg' + emote_taunt = list("growls") + speak_emote = list("creaks") + taunt_chance = 30 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("mimic") + move_to_delay = 9 + + var/is_electronic = 0 + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + +/mob/living/simple_animal/hostile/mimic/emp_act(severity) + if(is_electronic) + switch(severity) + if(1) + death() + if(2) + adjustBruteLoss(50) + ..(severity) + +// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. +/mob/living/simple_animal/hostile/mimic/crate + attacktext = "bites" + stop_automated_movement = 1 + wander = 0 + var/attempt_open = 0 + +// Pickup loot +/mob/living/simple_animal/hostile/mimic/crate/Initialize() + ..() + for(var/obj/item/I in loc) + I.loc = src + +/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() + ..() + if(prob(90)) + icon_state = "[initial(icon_state)]open" + else + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/mimic/crate/ListTargets() + if(attempt_open) + return ..() + return ..(1) + +/mob/living/simple_animal/hostile/mimic/crate/FindTarget() + . = ..() + if(.) + trigger() + +/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() + . = ..() + if(.) + icon_state = initial(icon_state) + if(prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(2) + C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() + if(!attempt_open) + visible_message("[src] starts to move!") + attempt_open = 1 + +/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) + trigger() + . = ..() + +/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() + ..() + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) + if(can_die()) + var/obj/structure/closet/crate/C = new(get_turf(src)) + // Put loot in crate + for(var/obj/O in src) + O.forceMove(C) + // due to `del_on_death` + return ..() + +GLOBAL_LIST_INIT(protected_objects, list(/obj/structure/table, /obj/structure/cable, /obj/structure/window)) + +/mob/living/simple_animal/hostile/mimic/copy + health = 100 + maxHealth = 100 + var/mob/living/creator = null // the creator + var/destroy_objects = 0 + var/knockdown_people = 0 + var/image/googly_eyes = null + gold_core_spawnable = NO_SPAWN + +/mob/living/simple_animal/hostile/mimic/copy/New(loc, obj/copy, mob/living/creator, destroy_original = 0) + ..(loc) + CopyObject(copy, creator, destroy_original) + +/mob/living/simple_animal/hostile/mimic/copy/Life() + ..() + if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize + adjustBruteLoss(1) + for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell + death() + +/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) + if(can_die()) + for(var/atom/movable/M in src) + M.loc = get_turf(src) + // due to `del_on_death` + return ..() + +/mob/living/simple_animal/hostile/mimic/copy/ListTargets() + . = ..() + return . - creator + +/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(var/mob/owner) + if(owner != creator) + LoseTarget() + creator = owner + faction |= "\ref[owner]" + +/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(var/obj/O) + if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, GLOB.protected_objects)) + return 1 + return 0 + +/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/user, var/destroy_original = 0) + if(destroy_original || CheckObject(O)) + O.loc = src + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + icon_living = icon_state + overlays = O.overlays + googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") + overlays += googly_eyes + if(istype(O, /obj/structure) || istype(O, /obj/machinery)) + health = (anchored * 50) + 50 + destroy_objects = 1 + if(O.density && O.anchored) + knockdown_people = 1 + melee_damage_lower *= 2 + melee_damage_upper *= 2 + if(istype(O, /obj/machinery)) + is_electronic = 1 + else if(istype(O, /obj/item)) + var/obj/item/I = O + health = 15 * I.w_class + melee_damage_lower = 2 + I.force + melee_damage_upper = 2 + I.force + move_to_delay = 2 * I.w_class + 1 + maxHealth = health + if(user) + creator = user + faction += "\ref[creator]" // very unique + if(destroy_original) + qdel(O) + return 1 + +/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() + if(destroy_objects) + ..() + +/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() + . = ..() + if(knockdown_people && . && prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(2) + C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/mimic/copy/Aggro() + ..() + googly_eyes.dir = get_dir(src,target) + +/mob/living/simple_animal/hostile/mimic/copy/machine + speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ + "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") + speak_chance = 15 + +/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(var/atom/the_target) + if(the_target == creator) // Don't attack our creator AI. + return 0 + if(isrobot(the_target)) + var/mob/living/silicon/robot/R = the_target + if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. + return 0 + return ..() + +/mob/living/simple_animal/hostile/mimic/copy/ranged + var/obj/item/gun/TrueGun = null + var/obj/item/gun/magic/Zapstick + var/obj/item/gun/projectile/Pewgun + var/obj/item/gun/energy/Zapgun + +/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) + if(..()) + emote_see = list("aims menacingly") + obj_damage = 0 + environment_smash = 0 //needed? seems weird for them to do so + ranged = 1 + retreat_distance = 1 //just enough to shoot + minimum_distance = 6 + var/obj/item/gun/G = O + melee_damage_upper = G.force + melee_damage_lower = G.force - max(0, (G.force / 2)) + move_to_delay = 2 * G.w_class + 1 + projectilesound = G.fire_sound + TrueGun = G + if(istype(G, /obj/item/gun/magic)) + Zapstick = G + var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type + projectiletype = initial(M.projectile_type) + if(istype(G, /obj/item/gun/projectile)) + Pewgun = G + var/obj/item/ammo_box/magazine/M = Pewgun.mag_type + casingtype = initial(M.ammo_type) + if(istype(G, /obj/item/gun/energy)) + Zapgun = G + var/selectfiresetting = Zapgun.select + var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] + projectiletype = initial(E.projectile_type) + +/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) + if(Zapgun) + if(Zapgun.cell) + var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] + if(Zapgun.cell.charge >= shot.e_cost) + Zapgun.cell.use(shot.e_cost) + Zapgun.update_icon() + ..() + else if(Zapstick) + if(Zapstick.charges) + Zapstick.charges-- + Zapstick.update_icon() + ..() + else if(Pewgun) + if(Pewgun.chambered) + if(Pewgun.chambered.BB) + qdel(Pewgun.chambered.BB) + Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update + Pewgun.chambered.update_icon() + ..() + else + visible_message("The [src] clears a jam!") + Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs + Pewgun.chambered = null + if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) + Pewgun.chambered = Pewgun.magazine.get_round(0) + Pewgun.chambered.loc = Pewgun + Pewgun.update_icon() + else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think + Pewgun.chambered = Pewgun.magazine.get_round(0) + Pewgun.chambered.loc = Pewgun + visible_message("The [src] cocks itself!") + else + ranged = 0 //BANZAIIII + retreat_distance = 0 + minimum_distance = 1 + return + icon_state = TrueGun.icon_state + icon_living = TrueGun.icon_state diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm index 490e056e62f8..288d45c9b1ac 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm @@ -164,7 +164,7 @@ /obj/effect/temp_visual/goliath_tentacle/original/Initialize(mapload, new_spawner) . = ..() - var/list/directions = cardinal.Copy() + var/list/directions = GLOB.cardinal.Copy() for(var/i in 1 to 3) var/spawndir = pick_n_take(directions) var/turf/T = get_step(src, spawndir) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm index 463b6e61e48f..2f896f4adb0b 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm @@ -163,4 +163,4 @@ L.faction = faction.Copy() L.setDir(dir) visible_message("[src] grows up into [L].") - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index 3d7da9d0fcce..ca9b632e06a9 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -327,9 +327,9 @@ /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/dwarf/equip(mob/living/carbon/human/H) . = ..() - H.dna.SetSEState(SMALLSIZEBLOCK, 1, 1) + H.dna.SetSEState(GLOB.smallsizeblock, 1, 1) H.mutations.Add(DWARF) - genemutcheck(H, SMALLSIZEBLOCK, null, MUTCHK_FORCED) + genemutcheck(H, GLOB.smallsizeblock, null, MUTCHK_FORCED) H.update_mutations() /obj/effect/mob_spawn/human/corpse/damaged/legioninfested/Initialize(mapload) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm index e1e86536f190..7c7200e85885 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm @@ -63,4 +63,4 @@ ..(gibbed) /mob/living/simple_animal/hostile/asteroid/proc/spawn_crusher_loot() - butcher_results[crusher_loot] = 1 \ No newline at end of file + butcher_results[crusher_loot] = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm index 92c01d7d72b1..e8ce4027b4e4 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm @@ -108,4 +108,4 @@ blank.name = "[M]" blank.desc = "It's [M], but [M.p_their()] flesh has an ashy texture, and [M.p_their()] face is featureless save an eerie smile." visible_message("[M] reemerges from the link!") - qdel(M) \ No newline at end of file + qdel(M) diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm index 4e3f71b30b18..d8d2d55a8ee2 100644 --- a/code/modules/mob/living/simple_animal/hostile/pirate.dm +++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm @@ -1,45 +1,45 @@ -/mob/living/simple_animal/hostile/pirate - name = "Pirate" - desc = "Does what he wants cause a pirate is free." - icon = 'icons/mob/simple_human.dmi' - icon_state = "piratemelee" - icon_living = "piratemelee" - icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pushes the" - response_disarm = "shoves" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - - atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 15 - speak_emote = list("yarrs") - loot = list(/obj/effect/mob_spawn/human/corpse/pirate, - /obj/item/melee/energy/sword/pirate) - del_on_death = 1 - faction = list("pirate") - sentience_type = SENTIENCE_OTHER - -/mob/living/simple_animal/hostile/pirate/ranged - name = "Pirate Gunner" - icon_state = "pirateranged" - icon_living = "pirateranged" - icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. - projectilesound = 'sound/weapons/laser.ogg' - ranged = 1 - rapid = 2 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/beam - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, - /obj/item/gun/energy/laser) \ No newline at end of file +/mob/living/simple_animal/hostile/pirate + name = "Pirate" + desc = "Does what he wants cause a pirate is free." + icon = 'icons/mob/simple_human.dmi' + icon_state = "piratemelee" + icon_living = "piratemelee" + icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pushes the" + response_disarm = "shoves" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + + atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + speak_emote = list("yarrs") + loot = list(/obj/effect/mob_spawn/human/corpse/pirate, + /obj/item/melee/energy/sword/pirate) + del_on_death = 1 + faction = list("pirate") + sentience_type = SENTIENCE_OTHER + +/mob/living/simple_animal/hostile/pirate/ranged + name = "Pirate Gunner" + icon_state = "pirateranged" + icon_living = "pirateranged" + icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. + projectilesound = 'sound/weapons/laser.ogg' + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/beam + loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, + /obj/item/gun/energy/laser) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm index 5a278a65b24d..f58f7fcfda2a 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -1,53 +1,53 @@ -/mob/living/simple_animal/hostile/retaliate/clown - name = "Clown" - desc = "A denizen of clown planet" - icon = 'icons/mob/simple_human.dmi' - icon_state = "clown" - icon_living = "clown" - icon_dead = "clown_dead" - icon_gib = "clown_gib" - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - speak = list("HONK", "Honk!", "Welcome to clown planet!") - emote_see = list("honks") - speak_chance = 1 - a_intent = INTENT_HARM - maxHealth = 75 - health = 75 - speed = 0 - harm_intent_damage = 8 - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "attacks" - attack_sound = 'sound/items/bikehorn.ogg' - obj_damage = 0 - environment_smash = 0 - minbodytemp = 270 - maxbodytemp = 370 - heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - unsuitable_atmos_damage = 10 - - -/mob/living/simple_animal/hostile/retaliate/clown/goblin - icon = 'icons/mob/animal.dmi' - name = "clown goblin" - desc = "A tiny walking mask and clown shoes. You want to honk his nose!" - icon_state = "clowngoblin" - icon_living = "clowngoblin" - icon_dead = null - response_help = "honks the" - speak = list("Honk!") - speak_emote = list("sqeaks") - emote_see = list("honks") - maxHealth = 100 - health = 100 - - speed = -1 - turns_per_move = 1 - - del_on_death = TRUE - loot = list(/obj/item/clothing/mask/gas/clown_hat, /obj/item/clothing/shoes/clown_shoes) +/mob/living/simple_animal/hostile/retaliate/clown + name = "Clown" + desc = "A denizen of clown planet" + icon = 'icons/mob/simple_human.dmi' + icon_state = "clown" + icon_living = "clown" + icon_dead = "clown_dead" + icon_gib = "clown_gib" + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + speak = list("HONK", "Honk!", "Welcome to clown planet!") + emote_see = list("honks") + speak_chance = 1 + a_intent = INTENT_HARM + maxHealth = 75 + health = 75 + speed = 0 + harm_intent_damage = 8 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "attacks" + attack_sound = 'sound/items/bikehorn.ogg' + obj_damage = 0 + environment_smash = 0 + minbodytemp = 270 + maxbodytemp = 370 + heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + unsuitable_atmos_damage = 10 + + +/mob/living/simple_animal/hostile/retaliate/clown/goblin + icon = 'icons/mob/animal.dmi' + name = "clown goblin" + desc = "A tiny walking mask and clown shoes. You want to honk his nose!" + icon_state = "clowngoblin" + icon_living = "clowngoblin" + icon_dead = null + response_help = "honks the" + speak = list("Honk!") + speak_emote = list("sqeaks") + emote_see = list("honks") + maxHealth = 100 + health = 100 + + speed = -1 + turns_per_move = 1 + + del_on_death = TRUE + loot = list(/obj/item/clothing/mask/gas/clown_hat, /obj/item/clothing/shoes/clown_shoes) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index d3bf35671e2f..7afaf3679c29 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -1,56 +1,56 @@ -/mob/living/simple_animal/hostile/retaliate - var/list/enemies = list() - -/mob/living/simple_animal/hostile/retaliate/Found(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(!L.stat) - return L - else - enemies -= L - else if(ismecha(A)) - var/obj/mecha/M = A - if(M.occupant) - return A - else if(isspacepod(A)) - var/obj/spacepod/S = A - if(S.pilot) - return A - -/mob/living/simple_animal/hostile/retaliate/ListTargets() - if(!enemies.len) - return list() - var/list/see = ..() - see &= enemies // Remove all entries that aren't in enemies - return see - -/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() - var/list/around = view(src, vision_range) - - for(var/atom/movable/A in around) - if(A == src) - continue - if(isliving(A)) - var/mob/living/M = A - if(faction_check_mob(M) && attack_same || !faction_check_mob(M)) - enemies |= M - else if(ismecha(A)) - var/obj/mecha/M = A - if(M.occupant) - enemies |= M - enemies |= M.occupant - else if(isspacepod(A)) - var/obj/spacepod/S = A - if(S.pilot) - enemies |= S - enemies |= S.pilot - - for(var/mob/living/simple_animal/hostile/retaliate/H in around) - if(faction_check_mob(H) && !attack_same && !H.attack_same) - H.enemies |= enemies - return 0 - -/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(amount > 0 && stat == CONSCIOUS) - Retaliate() \ No newline at end of file +/mob/living/simple_animal/hostile/retaliate + var/list/enemies = list() + +/mob/living/simple_animal/hostile/retaliate/Found(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(!L.stat) + return L + else + enemies -= L + else if(ismecha(A)) + var/obj/mecha/M = A + if(M.occupant) + return A + else if(isspacepod(A)) + var/obj/spacepod/S = A + if(S.pilot) + return A + +/mob/living/simple_animal/hostile/retaliate/ListTargets() + if(!enemies.len) + return list() + var/list/see = ..() + see &= enemies // Remove all entries that aren't in enemies + return see + +/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() + var/list/around = view(src, vision_range) + + for(var/atom/movable/A in around) + if(A == src) + continue + if(isliving(A)) + var/mob/living/M = A + if(faction_check_mob(M) && attack_same || !faction_check_mob(M)) + enemies |= M + else if(ismecha(A)) + var/obj/mecha/M = A + if(M.occupant) + enemies |= M + enemies |= M.occupant + else if(isspacepod(A)) + var/obj/spacepod/S = A + if(S.pilot) + enemies |= S + enemies |= S.pilot + + for(var/mob/living/simple_animal/hostile/retaliate/H in around) + if(faction_check_mob(H) && !attack_same && !H.attack_same) + H.enemies |= enemies + return 0 + +/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(amount > 0 && stat == CONSCIOUS) + Retaliate() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm index 6e05b699ea5c..a3e32e9d35e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm @@ -116,4 +116,4 @@ faction = list("undead") loot = list(/obj/effect/decal/cleanable/blood/gibs) - del_on_death = 1 \ No newline at end of file + del_on_death = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm index 37ae67cfef51..83c6fa67e1d5 100644 --- a/code/modules/mob/living/simple_animal/hostile/russian.dm +++ b/code/modules/mob/living/simple_animal/hostile/russian.dm @@ -1,44 +1,44 @@ -/mob/living/simple_animal/hostile/russian - name = "Russian" - desc = "For the Motherland!" - icon = 'icons/mob/simple_human.dmi' - icon_state = "russianmelee" - icon_living = "russianmelee" - icon_dead = "russianmelee_dead" // Does not actually exist. del_on_death. - icon_gib = "russianmelee_gib" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "shoves the" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - harm_intent_damage = 5 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = INTENT_HARM - unsuitable_atmos_damage = 15 - faction = list("russian") - status_flags = CANPUSH - loot = list(/obj/effect/mob_spawn/human/corpse/russian, - /obj/item/kitchen/knife) - del_on_death = 1 - sentience_type = SENTIENCE_OTHER - -/mob/living/simple_animal/hostile/russian/ranged - icon_state = "russianranged" - icon_living = "russianranged" - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - casingtype = /obj/item/ammo_casing/a357 - loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba) - -/mob/living/simple_animal/hostile/russian/ranged/mosin - loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, - /obj/item/gun/projectile/shotgun/boltaction) - casingtype = /obj/item/ammo_casing/a762 +/mob/living/simple_animal/hostile/russian + name = "Russian" + desc = "For the Motherland!" + icon = 'icons/mob/simple_human.dmi' + icon_state = "russianmelee" + icon_living = "russianmelee" + icon_dead = "russianmelee_dead" // Does not actually exist. del_on_death. + icon_gib = "russianmelee_gib" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "shoves the" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM + unsuitable_atmos_damage = 15 + faction = list("russian") + status_flags = CANPUSH + loot = list(/obj/effect/mob_spawn/human/corpse/russian, + /obj/item/kitchen/knife) + del_on_death = 1 + sentience_type = SENTIENCE_OTHER + +/mob/living/simple_animal/hostile/russian/ranged + icon_state = "russianranged" + icon_living = "russianranged" + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + casingtype = /obj/item/ammo_casing/a357 + loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba) + +/mob/living/simple_animal/hostile/russian/ranged/mosin + loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, + /obj/item/gun/projectile/shotgun/boltaction) + casingtype = /obj/item/ammo_casing/a762 diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index acfd9050c049..e0e71506bbeb 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -46,4 +46,4 @@ loot = list(/obj/effect/decal/remains/human, /obj/item/twohanded/spear, /obj/item/clothing/shoes/winterboots, - /obj/item/clothing/suit/hooded/wintercoat) \ No newline at end of file + /obj/item/clothing/suit/hooded/wintercoat) diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 513ab39b1803..f33891084985 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -1,367 +1,367 @@ -/mob/living/simple_animal/hostile/syndicate - name = "Syndicate Operative" - desc = "Death to Nanotrasen." - icon = 'icons/mob/simple_human.dmi' - icon_state = "syndicate" - icon_living = "syndicate" - icon_dead = "syndicate_dead" // Does not actually exist. del_on_death. - icon_gib = "syndicate_gib" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "shoves the" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - harm_intent_damage = 5 - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = INTENT_HARM - unsuitable_atmos_damage = 15 - faction = list("syndicate") - check_friendly_fire = 1 - status_flags = CANPUSH - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) - del_on_death = 1 - sentience_type = SENTIENCE_OTHER - -///////////////Sword and shield//////////// - -/mob/living/simple_animal/hostile/syndicate/melee - melee_damage_lower = 20 - melee_damage_upper = 25 - icon_state = "syndicate_sword" - icon_living = "syndicate_sword" - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - armour_penetration = 28 - status_flags = 0 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) - var/melee_block_chance = 20 - var/ranged_block_chance = 35 - -/mob/living/simple_animal/hostile/syndicate/melee/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - if(O.force) - if(prob(melee_block_chance)) - visible_message("[src] blocks the [O] with its shield! ") - else - var/damage = O.force - if(O.damtype == STAMINA) - damage = 0 - if(force_threshold && damage < force_threshold) - visible_message("[src] is unharmed by [O]!") - return - adjustHealth(damage) - visible_message("[src] has been attacked with the [O] by [user]. ") - playsound(loc, O.hitsound, 25, 1, -1) - else - to_chat(usr, "This weapon is ineffective, it does no damage.") - visible_message("[user] gently taps [src] with the [O]. ") - - -/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/item/projectile/Proj) - if(!Proj) - return - if(prob(ranged_block_chance)) - visible_message("[src] blocks [Proj] with its shield!") - else - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - adjustHealth(Proj.damage) - return 0 - -/mob/living/simple_animal/hostile/syndicate/melee/autogib - loot = list()//no loot, its gonna delete and gib. - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot - name = "Syndicate Operative" - force_threshold = 6 // Prevents people using punches to bypass eshield - robust_searching = 1 // Together with stat_attack, ensures dionae/etc that regen are killed properly - stat_attack = UNCONSCIOUS - universal_speak = 1 - icon_state = "syndicate_swordonly" - icon_living = "syndicate_swordonly" - melee_block_chance = 0 - ranged_block_chance = 0 - del_on_death = 1 - var/area/syndicate_depot/core/depotarea - var/raised_alert = FALSE - var/alert_on_death = FALSE - var/alert_on_timeout = TRUE - var/alert_on_spacing = TRUE - var/alert_on_shield_breach = FALSE - var/seen_enemy = FALSE - var/seen_enemy_name = null - var/seen_revived_enemy = FALSE - var/aggro_cycles = 0 - var/scan_cycles = 0 - var/shield_key = FALSE - var/turf/spawn_turf - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/New() - ..() - name = "[name] [pick(GLOB.last_names)]" - // Do not attempt to move this code to Initialize() or LateInitialize(). Doing so with other objects has caused bugs in the past, because assigning "depotarea" may not work there. - depotarea = areaMaster - spawn_turf = get_turf(src) - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/ListTargetsLazy() - // The normal ListTargetsLazy ignores walls, which is very bad in the case of depot mobs. So we override it. - return ListTargets() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/Aggro() - . = ..() - if(!istype(depotarea)) - return - if(target) - if(!seen_enemy) - seen_enemy = TRUE - if(!ranged) - playsound(loc, 'sound/weapons/saberon.ogg', 35, 1) - if(alert_on_shield_breach) - if(depotarea.shield_list.len) - raise_alert("[name] reports that [target] is trying to breach the armory shield!") - alert_on_shield_breach = FALSE - raised_alert = FALSE - alert_on_death = TRUE - if(isliving(target)) - var/mob/living/M = target - depotarea.list_add(M, depotarea.hostile_list) - if(M.mind && M.mind.special_role == SPECIAL_ROLE_TRAITOR) - depotarea.saw_double_agent(M) - depotarea.declare_started() - seen_enemy_name = target.name - if(istype(target, /obj/mecha)) - depotarea.saw_mech(target) - if(istype(target, /obj/spacepod)) - depotarea.saw_pod(target) - if(depotarea.list_includes(target, depotarea.dead_list)) - seen_revived_enemy = TRUE - raise_alert("[name] reports intruder [target] has returned from death!") - depotarea.list_remove(target, depotarea.dead_list) - if(!atoms_share_level(src, target) && prob(20)) - // This prevents someone from aggroing a depot mob, then hiding in a locker, perfectly safe, while the mob stands there getting killed by their friends. - LoseTarget() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_action() - . = ..() - if(!.) - return - if(!istype(depotarea)) - return - if(seen_enemy) - aggro_cycles++ - if(alert_on_timeout && !raised_alert && aggro_cycles >= 60) - raise_alert("[name] has reported contact with hostile entity: [seen_enemy_name]") - if(scan_cycles >= 15) - scan_cycles = 0 - if(!atoms_share_level(src, spawn_turf)) - if(istype(loc, /obj/structure/closet)) - var/obj/structure/closet/O = loc - forceMove(get_turf(src)) - visible_message("[src] smashes their way out of [O]!") - qdel(O) - raise_alert("[src] reported being trapped in a locker.") - raised_alert = FALSE - return - if(alert_on_spacing) - raise_alert("[src] lost in space.") - death() - return - for(var/mob/living/body in hearers(vision_range, targets_from)) - if(body.stat != DEAD) - continue - if(depotarea.list_includes(body, depotarea.dead_list)) - continue - if(faction_check_mob(body)) - continue - say("Target [body]... terminated.") - depotarea.list_add(body, depotarea.dead_list) - pointed(body) - else - scan_cycles++ - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/AIShouldSleep(var/list/possible_targets) - FindTarget(possible_targets, 1) - return FALSE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason) - if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct) - raised_alert = TRUE - say("Intruder!") - depotarea.increase_alert(reason) - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/death() - if(!istype(depotarea)) - return ..() - if(alert_on_death) - if(seen_enemy_name) - raise_alert("[name] has died in combat with [seen_enemy_name].") - else - raise_alert("[name] has died.") - if(shield_key && depotarea) - depotarea.shields_key_check() - if(depotarea) - depotarea.list_remove(src, depotarea.guard_list) - new /obj/effect/gibspawner/human(get_turf(src)) - return ..() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0) - if(isliving(mover)) - var/mob/living/blocker = mover - if(faction_check_mob(blocker)) - return 1 - return ..(mover, target, height) - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer - name = "Syndicate Officer" - icon_state = "syndicate_sword" - icon_living = "syndicate_sword" - melee_block_chance = 20 - ranged_block_chance = 35 - alert_on_death = TRUE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/Initialize(mapload) - . = ..() - if(prob(50)) - // 50% chance of switching to ranged variant. - // Designed to counter players taking cover behind reinforced plasmasglass. - // Does almost no danage in melee, but decent damage at range, and its shots go through glass. - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - ranged = 1 - rapid = 3 - retreat_distance = 3 - minimum_distance = 3 - melee_block_chance = 0 - ranged_block_chance = 0 - icon_state = "syndicate_pistol" - icon_living = "syndicate_pistol" - projectiletype = /obj/item/projectile/beam/laser - projectilesound = 'sound/weapons/laser.ogg' - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory - name = "Syndicate Quartermaster" - icon_state = "syndicate_stormtrooper_sword" - icon_living = "syndicate_stormtrooper_sword" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxHealth = 200 - health = 200 - melee_block_chance = 40 - alert_on_shield_breach = TRUE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) - ..() - if(prob(50)) - // 50% chance of switching to extremely dangerous ranged variant - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - ranged = 1 - retreat_distance = 3 - minimum_distance = 3 - melee_block_chance = 0 - ranged_block_chance = 0 - icon_state = "syndicate_stormtrooper_shotgun" - icon_living = "syndicate_stormtrooper_shotgun" - projectiletype = /obj/item/projectile/bullet/sniper/penetrator // Ignores cover. - projectilesound = 'sound/weapons/gunshots/gunshot_sniper.ogg' - return INITIALIZE_HINT_LATELOAD - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/LateInitialize() - if(istype(depotarea)) - var/list/key_candidates = list() - for(var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O in GLOB.living_mob_list) - key_candidates += O - if(key_candidates.len) - var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O = pick(key_candidates) - O.shield_key = TRUE - depotarea.shields_up() - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space - name = "Syndicate Backup" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - icon_state = "syndicate_space_sword" - icon_living = "syndicate_space_sword" - speed = 1 - wander = 0 - alert_on_spacing = FALSE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - - - -/mob/living/simple_animal/hostile/syndicate/melee/space - name = "Syndicate Commando" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - icon_state = "syndicate_space_sword" - icon_living = "syndicate_space_sword" - speed = 1 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) - -/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - - -/mob/living/simple_animal/hostile/syndicate/ranged - ranged = 1 - rapid = 2 - retreat_distance = 5 - minimum_distance = 5 - icon_state = "syndicate_smg" - icon_living = "syndicate_smg" - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - casingtype = /obj/item/ammo_casing/c45 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r) - -/mob/living/simple_animal/hostile/syndicate/ranged/space - icon_state = "syndicate_space_smg" - icon_living = "syndicate_space_smg" - name = "Syndicate Commando" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speed = 1 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) - -/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - -/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib - loot = list()//gonna gibe, no loot. - -/mob/living/simple_animal/hostile/viscerator - name = "viscerator" - desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." - icon = 'icons/mob/critter.dmi' - icon_state = "viscerator_attack" - icon_living = "viscerator_attack" - pass_flags = PASSTABLE - health = 15 - maxHealth = 15 - obj_damage = 0 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "cuts" - attack_sound = 'sound/weapons/bladeslice.ogg' - faction = list("syndicate") - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - mob_size = MOB_SIZE_TINY - flying = 1 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - deathmessage = "is smashed into pieces!" \ No newline at end of file +/mob/living/simple_animal/hostile/syndicate + name = "Syndicate Operative" + desc = "Death to Nanotrasen." + icon = 'icons/mob/simple_human.dmi' + icon_state = "syndicate" + icon_living = "syndicate" + icon_dead = "syndicate_dead" // Does not actually exist. del_on_death. + icon_gib = "syndicate_gib" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "shoves the" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM + unsuitable_atmos_damage = 15 + faction = list("syndicate") + check_friendly_fire = 1 + status_flags = CANPUSH + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) + del_on_death = 1 + sentience_type = SENTIENCE_OTHER + +///////////////Sword and shield//////////// + +/mob/living/simple_animal/hostile/syndicate/melee + melee_damage_lower = 20 + melee_damage_upper = 25 + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + armour_penetration = 28 + status_flags = 0 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) + var/melee_block_chance = 20 + var/ranged_block_chance = 35 + +/mob/living/simple_animal/hostile/syndicate/melee/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + if(O.force) + if(prob(melee_block_chance)) + visible_message("[src] blocks the [O] with its shield! ") + else + var/damage = O.force + if(O.damtype == STAMINA) + damage = 0 + if(force_threshold && damage < force_threshold) + visible_message("[src] is unharmed by [O]!") + return + adjustHealth(damage) + visible_message("[src] has been attacked with the [O] by [user]. ") + playsound(loc, O.hitsound, 25, 1, -1) + else + to_chat(usr, "This weapon is ineffective, it does no damage.") + visible_message("[user] gently taps [src] with the [O]. ") + + +/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/item/projectile/Proj) + if(!Proj) + return + if(prob(ranged_block_chance)) + visible_message("[src] blocks [Proj] with its shield!") + else + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + adjustHealth(Proj.damage) + return 0 + +/mob/living/simple_animal/hostile/syndicate/melee/autogib + loot = list()//no loot, its gonna delete and gib. + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot + name = "Syndicate Operative" + force_threshold = 6 // Prevents people using punches to bypass eshield + robust_searching = 1 // Together with stat_attack, ensures dionae/etc that regen are killed properly + stat_attack = UNCONSCIOUS + universal_speak = 1 + icon_state = "syndicate_swordonly" + icon_living = "syndicate_swordonly" + melee_block_chance = 0 + ranged_block_chance = 0 + del_on_death = 1 + var/area/syndicate_depot/core/depotarea + var/raised_alert = FALSE + var/alert_on_death = FALSE + var/alert_on_timeout = TRUE + var/alert_on_spacing = TRUE + var/alert_on_shield_breach = FALSE + var/seen_enemy = FALSE + var/seen_enemy_name = null + var/seen_revived_enemy = FALSE + var/aggro_cycles = 0 + var/scan_cycles = 0 + var/shield_key = FALSE + var/turf/spawn_turf + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/New() + ..() + name = "[name] [pick(GLOB.last_names)]" + // Do not attempt to move this code to Initialize() or LateInitialize(). Doing so with other objects has caused bugs in the past, because assigning "depotarea" may not work there. + depotarea = areaMaster + spawn_turf = get_turf(src) + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/ListTargetsLazy() + // The normal ListTargetsLazy ignores walls, which is very bad in the case of depot mobs. So we override it. + return ListTargets() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/Aggro() + . = ..() + if(!istype(depotarea)) + return + if(target) + if(!seen_enemy) + seen_enemy = TRUE + if(!ranged) + playsound(loc, 'sound/weapons/saberon.ogg', 35, 1) + if(alert_on_shield_breach) + if(depotarea.shield_list.len) + raise_alert("[name] reports that [target] is trying to breach the armory shield!") + alert_on_shield_breach = FALSE + raised_alert = FALSE + alert_on_death = TRUE + if(isliving(target)) + var/mob/living/M = target + depotarea.list_add(M, depotarea.hostile_list) + if(M.mind && M.mind.special_role == SPECIAL_ROLE_TRAITOR) + depotarea.saw_double_agent(M) + depotarea.declare_started() + seen_enemy_name = target.name + if(istype(target, /obj/mecha)) + depotarea.saw_mech(target) + if(istype(target, /obj/spacepod)) + depotarea.saw_pod(target) + if(depotarea.list_includes(target, depotarea.dead_list)) + seen_revived_enemy = TRUE + raise_alert("[name] reports intruder [target] has returned from death!") + depotarea.list_remove(target, depotarea.dead_list) + if(!atoms_share_level(src, target) && prob(20)) + // This prevents someone from aggroing a depot mob, then hiding in a locker, perfectly safe, while the mob stands there getting killed by their friends. + LoseTarget() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_action() + . = ..() + if(!.) + return + if(!istype(depotarea)) + return + if(seen_enemy) + aggro_cycles++ + if(alert_on_timeout && !raised_alert && aggro_cycles >= 60) + raise_alert("[name] has reported contact with hostile entity: [seen_enemy_name]") + if(scan_cycles >= 15) + scan_cycles = 0 + if(!atoms_share_level(src, spawn_turf)) + if(istype(loc, /obj/structure/closet)) + var/obj/structure/closet/O = loc + forceMove(get_turf(src)) + visible_message("[src] smashes their way out of [O]!") + qdel(O) + raise_alert("[src] reported being trapped in a locker.") + raised_alert = FALSE + return + if(alert_on_spacing) + raise_alert("[src] lost in space.") + death() + return + for(var/mob/living/body in hearers(vision_range, targets_from)) + if(body.stat != DEAD) + continue + if(depotarea.list_includes(body, depotarea.dead_list)) + continue + if(faction_check_mob(body)) + continue + say("Target [body]... terminated.") + depotarea.list_add(body, depotarea.dead_list) + pointed(body) + else + scan_cycles++ + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/AIShouldSleep(var/list/possible_targets) + FindTarget(possible_targets, 1) + return FALSE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason) + if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct) + raised_alert = TRUE + say("Intruder!") + depotarea.increase_alert(reason) + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/death() + if(!istype(depotarea)) + return ..() + if(alert_on_death) + if(seen_enemy_name) + raise_alert("[name] has died in combat with [seen_enemy_name].") + else + raise_alert("[name] has died.") + if(shield_key && depotarea) + depotarea.shields_key_check() + if(depotarea) + depotarea.list_remove(src, depotarea.guard_list) + new /obj/effect/gibspawner/human(get_turf(src)) + return ..() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0) + if(isliving(mover)) + var/mob/living/blocker = mover + if(faction_check_mob(blocker)) + return 1 + return ..(mover, target, height) + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer + name = "Syndicate Officer" + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + melee_block_chance = 20 + ranged_block_chance = 35 + alert_on_death = TRUE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/Initialize(mapload) + . = ..() + if(prob(50)) + // 50% chance of switching to ranged variant. + // Designed to counter players taking cover behind reinforced plasmasglass. + // Does almost no danage in melee, but decent damage at range, and its shots go through glass. + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + ranged = 1 + rapid = 3 + retreat_distance = 3 + minimum_distance = 3 + melee_block_chance = 0 + ranged_block_chance = 0 + icon_state = "syndicate_pistol" + icon_living = "syndicate_pistol" + projectiletype = /obj/item/projectile/beam/laser + projectilesound = 'sound/weapons/laser.ogg' + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory + name = "Syndicate Quartermaster" + icon_state = "syndicate_stormtrooper_sword" + icon_living = "syndicate_stormtrooper_sword" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxHealth = 200 + health = 200 + melee_block_chance = 40 + alert_on_shield_breach = TRUE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) + ..() + if(prob(50)) + // 50% chance of switching to extremely dangerous ranged variant + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + ranged = 1 + retreat_distance = 3 + minimum_distance = 3 + melee_block_chance = 0 + ranged_block_chance = 0 + icon_state = "syndicate_stormtrooper_shotgun" + icon_living = "syndicate_stormtrooper_shotgun" + projectiletype = /obj/item/projectile/bullet/sniper/penetrator // Ignores cover. + projectilesound = 'sound/weapons/gunshots/gunshot_sniper.ogg' + return INITIALIZE_HINT_LATELOAD + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/LateInitialize() + if(istype(depotarea)) + var/list/key_candidates = list() + for(var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O in GLOB.living_mob_list) + key_candidates += O + if(key_candidates.len) + var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O = pick(key_candidates) + O.shield_key = TRUE + depotarea.shields_up() + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space + name = "Syndicate Backup" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + speed = 1 + wander = 0 + alert_on_spacing = FALSE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + + + +/mob/living/simple_animal/hostile/syndicate/melee/space + name = "Syndicate Commando" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + speed = 1 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) + +/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + + +/mob/living/simple_animal/hostile/syndicate/ranged + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + icon_state = "syndicate_smg" + icon_living = "syndicate_smg" + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + casingtype = /obj/item/ammo_casing/c45 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r) + +/mob/living/simple_animal/hostile/syndicate/ranged/space + icon_state = "syndicate_space_smg" + icon_living = "syndicate_space_smg" + name = "Syndicate Commando" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) + +/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + +/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib + loot = list()//gonna gibe, no loot. + +/mob/living/simple_animal/hostile/viscerator + name = "viscerator" + desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." + icon = 'icons/mob/critter.dmi' + icon_state = "viscerator_attack" + icon_living = "viscerator_attack" + pass_flags = PASSTABLE + health = 15 + maxHealth = 15 + obj_damage = 0 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "cuts" + attack_sound = 'sound/weapons/bladeslice.ogg' + faction = list("syndicate") + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + mob_size = MOB_SIZE_TINY + flying = 1 + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + deathmessage = "is smashed into pieces!" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index b72ec90cf584..2b15e584922a 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -58,7 +58,7 @@ if(feedings_left > 0) to_chat(user, "You must wrap [feedings_left] more humanoid prey before you can do this!") return - for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/queen/Q in GLOB.ts_spiderlist) if(Q.spider_awaymission == user.spider_awaymission) to_chat(user, "The presence of another Queen in the area is preventing you from maturing.") return @@ -278,4 +278,4 @@ forceMove(C.loc) C.visible_message("[src] smashes the welded cover off [C]!") return - to_chat(src, "There is no welded vent or scrubber close enough to do this.") \ No newline at end of file + to_chat(src, "There is no welded vent or scrubber close enough to do this.") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm index eae926b88e1f..a1bb0fb0ae98 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm @@ -56,4 +56,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) to_chat(C, "[src] slices into you!") - C.reagents.add_reagent("terror_black_toxin", 30) \ No newline at end of file + C.reagents.add_reagent("terror_black_toxin", 30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm index fd30532a7bcc..137d6739466b 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm @@ -96,13 +96,13 @@ S.amount_grown = 250 /mob/living/simple_animal/hostile/poison/terror_spider/queen/empress/proc/EraseBrood() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.spider_tier < spider_tier) T.degenerate = 1 to_chat(T, "Through the hivemind, the raw power of [src] floods into your body, burning it from the inside out!") - for(var/obj/structure/spider/eggcluster/terror_eggcluster/T in ts_egg_list) + for(var/obj/structure/spider/eggcluster/terror_eggcluster/T in GLOB.ts_egg_list) qdel(T) - for(var/obj/structure/spider/spiderling/terror_spiderling/T in ts_spiderling_list) + for(var/obj/structure/spider/spiderling/terror_spiderling/T in GLOB.ts_spiderling_list) qdel(T) to_chat(src, "All Terror Spiders, except yourself, will die off shortly.") @@ -110,4 +110,4 @@ /obj/item/projectile/terrorqueenspit/empress damage_type = BURN damage = 30 - bonus_tox = 0 \ No newline at end of file + bonus_tox = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm index 283cbb6ad0d1..daed363b8d9a 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm @@ -14,7 +14,7 @@ var/error_on_humanize = "" var/humanize_prompt = "Take direct control of [src]?" humanize_prompt += " Role: [spider_role_summary]" - if(user.ckey in ts_ckey_blacklist) + if(user.ckey in GLOB.ts_ckey_blacklist) error_on_humanize = "You are not able to control any terror spider this round." else if(cannotPossess(user)) error_on_humanize = "You have enabled antag HUD and are unable to re-enter the round." @@ -43,4 +43,4 @@ return key = user.key for(var/mob/dead/observer/G in GLOB.player_list) - G.show_message("A ghost has taken control of [src]. ([ghost_follow_link(src, ghost=G)]).") \ No newline at end of file + G.show_message("A ghost has taken control of [src]. ([ghost_follow_link(src, ghost=G)]).") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm index c06a7c2fee6b..544f7201d248 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm @@ -63,4 +63,4 @@ /obj/structure/spider/terrorweb/gray alpha = 100 name = "transparent web" - desc = "This web is partly transparent, making it harder to see, and easier to get caught by." \ No newline at end of file + desc = "This web is partly transparent, making it harder to see, and easier to get caught by." diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm index d4b1886e49ea..245ae95c5e99 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm @@ -4,7 +4,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/DoHiveSense() var/hsline = "" to_chat(src, "Your Brood: ") - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.spider_awaymission != spider_awaymission) continue hsline = "* [T] in [get_area(T)], " @@ -20,21 +20,21 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpiders() var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) numspiders += 1 return numspiders /mob/living/simple_animal/hostile/poison/terror_spider/proc/CountSpidersType(specific_type) var/numspiders = 0 - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.stat != DEAD && !T.spider_placed && spider_awaymission == T.spider_awaymission) if(T.type == specific_type) numspiders += 1 - for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in ts_egg_list) + for(var/obj/structure/spider/eggcluster/terror_eggcluster/E in GLOB.ts_egg_list) if(E.spiderling_type == specific_type && E.z == z) numspiders += E.spiderling_number - for(var/obj/structure/spider/spiderling/terror_spiderling/L in ts_spiderling_list) + for(var/obj/structure/spider/spiderling/terror_spiderling/L in GLOB.ts_spiderling_list) if(!L.stillborn && L.grow_as == specific_type && L.z == z) numspiders += 1 - return numspiders \ No newline at end of file + return numspiders diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm index 92c7688e1e17..7e36b411baeb 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm @@ -94,4 +94,4 @@ name = "thick web" desc = "This web is so thick, most cannot see beyond it." opacity = 1 - max_integrity = 40 \ No newline at end of file + max_integrity = 40 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm index af4fda646254..241cceb3d2c1 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm @@ -88,7 +88,7 @@ if(spider_uo71) UnlockBlastDoors("UO71_Caves") // When a queen dies, so do her player-controlled purple-type guardians. Intended as a motivator for purples to ensure they guard her. - for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/purple/P in GLOB.ts_spiderlist) if(ckey) P.visible_message("\The [src] writhes in pain!") to_chat(P,"\The [src] has died. Without her hivemind link, purple terrors like yourself cannot survive more than a few minutes!") @@ -97,7 +97,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/queen/Retaliate() ..() - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) T.enemies |= enemies /mob/living/simple_animal/hostile/poison/terror_spider/queen/proc/ai_nest_is_full() @@ -353,4 +353,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) C.Hallucinate(400) - C.adjustToxLoss(30) \ No newline at end of file + C.adjustToxLoss(30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index 4bba74001fda..a889bc6d3282 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -23,12 +23,12 @@ /obj/structure/spider/spiderling/terror_spiderling/New() ..() - ts_spiderling_list += src + GLOB.ts_spiderling_list += src if(is_away_level(z)) spider_awaymission = TRUE /obj/structure/spider/spiderling/terror_spiderling/Destroy() - ts_spiderling_list -= src + GLOB.ts_spiderling_list -= src return ..() /obj/structure/spider/spiderling/terror_spiderling/Bump(obj/O) @@ -205,7 +205,7 @@ /obj/structure/spider/eggcluster/terror_eggcluster/New() ..() - ts_egg_list += src + GLOB.ts_egg_list += src spawn(50) if(spiderling_type == /mob/living/simple_animal/hostile/poison/terror_spider/red) name = "red terror eggs" @@ -227,7 +227,7 @@ name = "queen of terror eggs" /obj/structure/spider/eggcluster/terror_eggcluster/Destroy() - ts_egg_list -= src + GLOB.ts_egg_list -= src return ..() /obj/structure/spider/eggcluster/terror_eggcluster/process() @@ -243,4 +243,4 @@ S.enemies = enemies if(spider_growinstantly) S.amount_grown = 250 - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm index 8d18471b1cb6..11e4cac3e4a6 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_ai.dm @@ -103,8 +103,8 @@ return if(!target) var/my_ventcrawl_freq = freq_ventcrawl_idle - if(ts_count_dead > 0) - if(world.time < (ts_death_last + ts_death_window)) + if(GLOB.ts_count_dead > 0) + if(world.time < (GLOB.ts_death_last + GLOB.ts_death_window)) my_ventcrawl_freq = freq_ventcrawl_combat // First, check for general actions that any spider could take. if(path_to_vent) @@ -267,7 +267,7 @@ /mob/living/simple_animal/hostile/poison/terror_spider/proc/ClearObstacle(turf/target_turf) var/list/valid_obstacles = list(/obj/structure/window, /obj/structure/closet, /obj/structure/table, /obj/structure/grille, /obj/structure/rack, /obj/machinery/door/window) - for(var/dir in cardinal) // North, South, East, West + for(var/dir in GLOB.cardinal) // North, South, East, West var/obj/structure/obstacle = locate(/obj/structure, get_step(src, dir)) if(is_type_in_list(obstacle, valid_obstacles)) obstacle.attack_animal(src) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm index d6d64096ff18..520143d71d02 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm @@ -1,12 +1,12 @@ -var/global/list/ts_ckey_blacklist = list() -var/global/ts_count_dead = 0 -var/global/ts_count_alive_awaymission = 0 -var/global/ts_count_alive_station = 0 -var/global/ts_death_last = 0 -var/global/ts_death_window = 9000 // 15 minutes -var/global/list/ts_spiderlist = list() -var/global/list/ts_egg_list = list() -var/global/list/ts_spiderling_list = list() +GLOBAL_LIST_EMPTY(ts_ckey_blacklist) +GLOBAL_VAR_INIT(ts_count_dead, 0) +GLOBAL_VAR_INIT(ts_count_alive_awaymission, 0) +GLOBAL_VAR_INIT(ts_count_alive_station, 0) +GLOBAL_VAR_INIT(ts_death_last, 0) +GLOBAL_VAR_INIT(ts_death_window, 9000) // 15 minutes +GLOBAL_LIST_EMPTY(ts_spiderlist) +GLOBAL_LIST_EMPTY(ts_egg_list) +GLOBAL_LIST_EMPTY(ts_spiderling_list) // -------------------------------------------------------------------------------- // --------------------- TERROR SPIDERS: DEFAULTS --------------------------------- @@ -245,7 +245,7 @@ var/global/list/ts_spiderling_list = list() /mob/living/simple_animal/hostile/poison/terror_spider/New() ..() - ts_spiderlist += src + GLOB.ts_spiderlist += src add_language("Spider Hivemind") if(spider_tier >= TS_TIER_2) add_language("Galactic Common") @@ -262,7 +262,7 @@ var/global/list/ts_spiderling_list = list() msg_terrorspiders("[src] has grown in [get_area(src)].") if(is_away_level(z)) spider_awaymission = 1 - ts_count_alive_awaymission++ + GLOB.ts_count_alive_awaymission++ if(spider_tier >= 3) ai_ventcrawls = FALSE // means that pre-spawned bosses on away maps won't ventcrawl. Necessary to keep prince/mother in one place. if(istype(get_area(src), /area/awaymission/UO71)) // if we are playing the away mission with our special spiders... @@ -272,11 +272,11 @@ var/global/list/ts_spiderling_list = list() ai_ventcrawls = FALSE spider_placed = 1 else - ts_count_alive_station++ + GLOB.ts_count_alive_station++ // after 3 seconds, assuming nobody took control of it yet, offer it to ghosts. addtimer(CALLBACK(src, .proc/CheckFaction), 20) addtimer(CALLBACK(src, .proc/announcetoghosts), 30) - var/datum/atom_hud/U = huds[DATA_HUD_MEDICAL_ADVANCED] + var/datum/atom_hud/U = GLOB.huds[DATA_HUD_MEDICAL_ADVANCED] U.add_hud_to(src) /mob/living/simple_animal/hostile/poison/terror_spider/proc/announcetoghosts() @@ -292,7 +292,7 @@ var/global/list/ts_spiderling_list = list() notify_ghosts("[src] has appeared in [get_area(src)].", enter_link = "(Click to control)", source = src, alert_overlay = alert_overlay, action = NOTIFY_ATTACK) /mob/living/simple_animal/hostile/poison/terror_spider/Destroy() - ts_spiderlist -= src + GLOB.ts_spiderlist -= src handle_dying() return ..() @@ -322,12 +322,12 @@ var/global/list/ts_spiderling_list = list() /mob/living/simple_animal/hostile/poison/terror_spider/proc/handle_dying() if(!hasdied) hasdied = 1 - ts_count_dead++ - ts_death_last = world.time + GLOB.ts_count_dead++ + GLOB.ts_death_last = world.time if(spider_awaymission) - ts_count_alive_awaymission-- + GLOB.ts_count_alive_awaymission-- else - ts_count_alive_station-- + GLOB.ts_count_alive_station-- /mob/living/simple_animal/hostile/poison/terror_spider/death(gibbed) if(can_die()) @@ -352,7 +352,7 @@ var/global/list/ts_spiderling_list = list() . = ..() /mob/living/simple_animal/hostile/poison/terror_spider/proc/msg_terrorspiders(msgtext) - for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in ts_spiderlist) + for(var/mob/living/simple_animal/hostile/poison/terror_spider/T in GLOB.ts_spiderlist) if(T.stat != DEAD) to_chat(T, "TerrorSense: [msgtext]") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm index f62167792a3d..919f91742cc6 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm @@ -68,4 +68,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) to_chat(C, "[src] slices into you!") - new /obj/item/organ/internal/body_egg/terror_eggs(C) \ No newline at end of file + new /obj/item/organ/internal/body_egg/terror_eggs(C) diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm index 510e609756bb..6881e3d7b128 100644 --- a/code/modules/mob/living/simple_animal/hostile/tree.dm +++ b/code/modules/mob/living/simple_animal/hostile/tree.dm @@ -1,45 +1,45 @@ -/mob/living/simple_animal/hostile/tree - name = "pine tree" - desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." - icon = 'icons/obj/flora/pinetrees.dmi' - icon_state = "pine_1" - icon_living = "pine_1" - icon_dead = "pine_1" - icon_gib = "pine_1" - speak_chance = 0 - turns_per_move = 5 - response_help = "brushes the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 1 - maxHealth = 250 - health = 250 - mob_size = MOB_SIZE_LARGE - - pixel_x = -16 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("pines") - emote_taunt = list("growls") - taunt_chance = 20 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("hostile", "winter") - loot = list(/obj/item/stack/sheet/wood) - gold_core_spawnable = HOSTILE_SPAWN - deathmessage = "is hacked into pieces!" - del_on_death = 1 - -/mob/living/simple_animal/hostile/tree/AttackingTarget() - . = ..() - if(. && iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(15)) - C.Weaken(3) - C.visible_message("\the [src] knocks down \the [C]!") \ No newline at end of file +/mob/living/simple_animal/hostile/tree + name = "pine tree" + desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_1" + icon_living = "pine_1" + icon_dead = "pine_1" + icon_gib = "pine_1" + speak_chance = 0 + turns_per_move = 5 + response_help = "brushes the" + response_disarm = "pushes the" + response_harm = "hits the" + speed = 1 + maxHealth = 250 + health = 250 + mob_size = MOB_SIZE_LARGE + + pixel_x = -16 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("pines") + emote_taunt = list("growls") + taunt_chance = 20 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("hostile", "winter") + loot = list(/obj/item/stack/sheet/wood) + gold_core_spawnable = HOSTILE_SPAWN + deathmessage = "is hacked into pieces!" + del_on_death = 1 + +/mob/living/simple_animal/hostile/tree/AttackingTarget() + . = ..() + if(. && iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(15)) + C.Weaken(3) + C.visible_message("\the [src] knocks down \the [C]!") diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 0cc174827ae5..49c63b59ab5b 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -1,729 +1,729 @@ -/* Parrots! - * Contains - * Defines - * Inventory (headset stuff) - * Attack responces - * AI - * Procs / Verbs (usable by players) - * Sub-types - */ - -/* - * Defines - */ - -//Only a maximum of one action and one intent should be active at any given time. -//Actions -#define PARROT_PERCH 1 //Sitting/sleeping, not moving -#define PARROT_SWOOP 2 //Moving towards or away from a target -#define PARROT_WANDER 4 //Moving without a specific target in mind - -//Intents -#define PARROT_STEAL 8 //Flying towards a target to steal it/from it -#define PARROT_ATTACK 16 //Flying towards a target to attack it -#define PARROT_RETURN 32 //Flying towards its perch -#define PARROT_FLEE 64 //Flying away from its attacker - - -/mob/living/simple_animal/parrot - name = "\improper Parrot" - desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" - icon = 'icons/mob/animal.dmi' - icon_state = "parrot_fly" - icon_living = "parrot_fly" - icon_dead = "parrot_dead" - pass_flags = PASSTABLE - can_collar = 1 - - var/list/clean_speak = list( - "Hi", - "Hello!", - "Cracker?", - "BAWWWWK george mellons griffing me") - speak_emote = list("squawks","says","yells") - emote_hear = list("squawks","bawks") - emote_see = list("flutters its wings") - - speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3) - - response_help = "pets the" - response_disarm = "gently moves aside the" - response_harm = "swats the" - stop_automated_movement = 1 - universal_speak = 1 - mob_size = MOB_SIZE_SMALL - - var/parrot_state = PARROT_WANDER //Hunt for a perch when created - var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick. - var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down - var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack - - var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. - var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed. - - var/list/speech_buffer = list() - var/list/available_channels = list() - - //Headset for Poly to yell at engineers :) - var/obj/item/radio/headset/ears = null - - //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, - //mobs it wants to attack or mobs that have attacked it - var/atom/movable/parrot_interest = null - - //Parrots will generally sit on their pertch unless something catches their eye. - //These vars store their preffered perch and if they dont have one, what they can use as a perch - var/obj/parrot_perch = null - var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \ - /obj/structure/filingcabinet, /obj/machinery/teleport, \ - /obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \ - /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ - /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ - /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ - /obj/machinery/computer) - - //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - var/obj/item/held_item = null - gold_core_spawnable = FRIENDLY_SPAWN - - -/mob/living/simple_animal/parrot/New() - ..() - GLOB.hear_radio_list += src - if(!ears) - var/headset = pick(/obj/item/radio/headset/headset_sec, \ - /obj/item/radio/headset/headset_eng, \ - /obj/item/radio/headset/headset_med, \ - /obj/item/radio/headset/headset_sci, \ - /obj/item/radio/headset/headset_cargo) - ears = new headset(src) - update_speak() - - parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - - verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ - /mob/living/simple_animal/parrot/proc/steal_from_mob, \ - /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ - /mob/living/simple_animal/parrot/proc/perch_player) - -/mob/living/simple_animal/parrot/Destroy() - GLOB.hear_radio_list -= src - return ..() - -/mob/living/simple_animal/parrot/death(gibbed) - if(can_die()) - if(held_item) - held_item.loc = src.loc - held_item = null - walk(src,0) - return ..() - -/mob/living/simple_animal/parrot/Stat() - ..() - stat("Held Item", held_item) - -/* - * Inventory - */ -/mob/living/simple_animal/parrot/show_inv(mob/user as mob) - user.set_machine(src) - - var/dat = {""} - - dat += "" - if(can_collar) - dat += "" - dat += "" - - dat += {"
    Headset:[(ears && !(ears.flags&ABSTRACT)) ? ears : "Empty"]
     
    Collar:[(pcollar && !(pcollar.flags&ABSTRACT)) ? pcollar : "Empty"]
    - Close - "} - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) - popup.set_content(dat) - popup.open() - -/mob/living/simple_animal/parrot/Topic(href, href_list) - - //Can the usr physically do this? - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - - //Is the usr's mob type able to do this? - if(ishuman(usr) || isrobot(usr)) - if(href_list["remove_inv"]) - var/remove_from = href_list["remove_inv"] - switch(remove_from) - if("ears") - if(ears) - if(available_channels.len) - say("[pick(available_channels)]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - else - say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - ears.forceMove(loc) - ears = null - update_speak() - else - to_chat(usr, "There is nothing to remove from its [remove_from].") - return - show_inv(usr) - else if(href_list["add_inv"]) - var/add_to = href_list["add_inv"] - if(!usr.get_active_hand()) - to_chat(usr, "You have nothing in your hand to put on its [add_to].") - return - switch(add_to) - if("ears") - if(ears) - to_chat(usr, "It's already wearing something.") - return - else - var/obj/item/item_to_add = usr.get_active_hand() - if(!item_to_add) - return - - if( !istype(item_to_add, /obj/item/radio/headset) ) - to_chat(usr, "This object won't fit.") - return - - var/obj/item/radio/headset/headset_to_add = item_to_add - - usr.drop_item() - headset_to_add.forceMove(src) - ears = headset_to_add - to_chat(usr, "You fit the headset onto [src].") - - available_channels.Cut() - for(var/ch in headset_to_add.channels) - switch(ch) - if("Engineering") - available_channels.Add(":e") - if("Command") - available_channels.Add(":c") - if("Security") - available_channels.Add(":s") - if("Science") - available_channels.Add(":n") - if("Medical") - available_channels.Add(":m") - if("Mining") - available_channels.Add(":d") - if("Cargo") - available_channels.Add(":q") - - if(headset_to_add.translate_binary) - available_channels.Add(":b") - update_speak() - show_inv(usr) - else - ..() - - -/* - * Attack responces - */ -//Humans, monkeys, aliens -/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) - ..() - if(client) return - if(!stat && M.a_intent == "hurt") - - icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks - - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = M - parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. - - if(M.health < 50) //Weakened mob? Fight back! - parrot_state |= PARROT_ATTACK - else - parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! - drop_held_item(0) - return - -//Mobs with objects -/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - ..() - if(!stat && !client && !istype(O, /obj/item/stack/medical)) - if(O.force) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = user - parrot_state = PARROT_SWOOP | PARROT_FLEE - icon_state = "parrot_fly" - drop_held_item(0) - return - -//Bullets -/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj) - ..() - if(!stat && !client) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = null - parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! - parrot_been_shot += 5 - icon_state = "parrot_fly" - drop_held_item(0) - return - - -/* - * AI - Not really intelligent, but I'm calling it AI anyway. - */ -/mob/living/simple_animal/parrot/Life(seconds, times_fired) - ..() - - //Sprite and AI update for when a parrot gets pulled - if(pulledby && stat == CONSCIOUS) - icon_state = "parrot_fly" - -/mob/living/simple_animal/parrot/proc/update_speak() - speak.Cut() - - if(available_channels.len && ears) - for(var/possible_phrase in clean_speak) - //50/50 chance to not use the radio at all - speak += "[prob(50) ? pick(available_channels) : ""][possible_phrase]" - - else //If we have no headset or channels to use, dont try to use any! - for(var/possible_phrase in clean_speak) - speak += possible_phrase - -/mob/living/simple_animal/parrot/handle_automated_movement() - if(pulledby) - parrot_state = PARROT_WANDER - return - - if(!isturf(src.loc) || !canmove || buckled) - return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) - - -//-----SPEECH - /* Parrot speech mimickry! - Phrases that the parrot hears in mob/living/say() get added to speach_buffer. - Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. - Then it clears the buffer to make sure they dont magically remember something from hours ago. */ - if(speech_buffer.len && prob(10)) - if(clean_speak.len) - clean_speak -= pick(clean_speak) - - clean_speak += pick(speech_buffer) - speech_buffer.Cut() - - -//-----SLEEPING - if(parrot_state == PARROT_PERCH) - if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us - if(parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - icon_state = "parrot_fly" - return - else - parrot_state = PARROT_WANDER - icon_state = "parrot_fly" - return - - if(--parrot_sleep_dur) //Zzz - return - - else - //This way we only call the stuff below once every [sleep_max] ticks. - parrot_sleep_dur = parrot_sleep_max - - //Cycle through message modes for the headset - update_speak() - - //Search for item to steal - parrot_interest = search_for_item() - if(parrot_interest) - custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.") - parrot_state = PARROT_SWOOP | PARROT_STEAL - icon_state = "parrot_fly" - return - -//-----WANDERING - This is basically a 'I dont know what to do yet' state - else if(parrot_state == PARROT_WANDER) - //Stop movement, we'll set it later - walk(src, 0) - parrot_interest = null - - //Wander around aimlessly. This will help keep the loops from searches down - //and possibly move the mob into a new are in view of something they can use - if(prob(90)) - step(src, pick(cardinal)) - return - - if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. - var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item - if(AM) - if(istype(AM, /obj/item) || isliving(AM)) //If stealable item - parrot_interest = AM - custom_emote(1,"turns and flies towards [parrot_interest]") - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - else //Else it's a perch - parrot_perch = AM - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - return - - if(parrot_interest && parrot_interest in view(src)) - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - - if(parrot_perch && parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - else //Have an item but no perch? Find one! - parrot_perch = search_for_perch() - if(parrot_perch) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return -//-----STEALING - else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) - walk(src,0) - if(!parrot_interest || held_item) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(!(parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(in_range(src, parrot_interest)) - - if(isliving(parrot_interest)) - steal_from_mob() - - else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch - if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) - held_item = parrot_interest - parrot_interest.loc = src - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") - - parrot_interest = null - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - walk_to(src, parrot_interest, 1, parrot_speed) - return - -//-----RETURNING TO PERCH - else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) - walk(src, 0) - if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. - parrot_perch = null - parrot_state = PARROT_WANDER - return - - if(in_range(src, parrot_perch)) - src.loc = parrot_perch.loc - drop_held_item() - parrot_state = PARROT_PERCH - icon_state = "parrot_sit" - return - - walk_to(src, parrot_perch, 1, parrot_speed) - return - -//-----FLEEING - else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) - walk(src,0) - if(!parrot_interest || !isliving(parrot_interest)) //Sanity - parrot_state = PARROT_WANDER - - walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot) - parrot_been_shot-- - return - -//-----ATTACKING - else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) - - //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander - if(!parrot_interest || !isliving(parrot_interest)) - parrot_interest = null - parrot_state = PARROT_WANDER - return - - var/mob/living/L = parrot_interest - - //If the mob is close enough to interact with - if(in_range(src, parrot_interest)) - - //If the mob we've been chasing/attacking dies or falls into crit, check for loot! - if(L.stat) - parrot_interest = null - if(!held_item) - held_item = steal_from_ground() - if(!held_item) - held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. - if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home - parrot_state = PARROT_SWOOP | PARROT_RETURN - else - parrot_state = PARROT_WANDER - return - - //Time for the hurt to begin! - var/damage = rand(5,10) - - if(ishuman(parrot_interest)) - var/mob/living/carbon/human/H = parrot_interest - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) - - H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1) - custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) - - else - L.adjustBruteLoss(damage) - custom_emote(1, pick("pecks at [L].", "claws [L].")) - return - - //Otherwise, fly towards the mob! - else - walk_to(src, parrot_interest, 1, parrot_speed) - return -//-----STATE MISHAP - else //This should not happen. If it does lets reset everything and try again - walk(src,0) - parrot_interest = null - parrot_perch = null - drop_held_item() - parrot_state = PARROT_WANDER - return - -/* - * Procs - */ - -/mob/living/simple_animal/parrot/movement_delay() - if(client && stat == CONSCIOUS && parrot_state != "parrot_fly") - icon_state = "parrot_fly" - //Because the most appropriate place to set icon_state is movement_delay(), clearly - return ..() - -/mob/living/simple_animal/parrot/proc/search_for_item() - for(var/atom/movable/AM in view(src)) - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(I.w_class < WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - if((C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) || (C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)) - return C - return null - -/mob/living/simple_animal/parrot/proc/search_for_perch() - for(var/obj/O in view(src)) - for(var/path in desired_perches) - if(istype(O, path)) - return O - return null - -//This proc was made to save on doing two 'in view' loops seperatly -/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() - for(var/atom/movable/AM in view(src)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - return AM - - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(I.w_class <= WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL || C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) - return C - return null - - -/* - * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. - */ -/mob/living/simple_animal/parrot/proc/steal_from_ground() - set name = "Steal from ground" - set category = "Parrot" - set desc = "Grabs a nearby item." - - if(stat) - return -1 - - if(held_item) - to_chat(src, "You are already holding the [held_item]") - return 1 - - for(var/obj/item/I in view(1,src)) - //Make sure we're not already holding it and it's small enough - if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) - - //If we have a perch and the item is sitting on it, continue - if(!client && parrot_perch && I.loc == parrot_perch.loc) - continue - - held_item = I - I.loc = src - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") - return held_item - - to_chat(src, "There is nothing of interest to take.") - return 0 - -/mob/living/simple_animal/parrot/proc/steal_from_mob() - set name = "Steal from mob" - set category = "Parrot" - set desc = "Steals an item right out of a person's hand!" - - if(stat) - return -1 - - if(held_item) - to_chat(src, "You are already holding the [held_item]") - return 1 - - var/obj/item/stolen_item = null - - for(var/mob/living/carbon/C in view(1,src)) - if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = C.l_hand - - if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = C.r_hand - - if(stolen_item) - C.unEquip(stolen_item) - held_item = stolen_item - stolen_item.loc = src - visible_message("[src] grabs the [held_item] out of [C]'s hand!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") - return held_item - - to_chat(src, "There is nothing of interest to take.") - return 0 - -/mob/living/simple_animal/parrot/verb/drop_held_item_player() - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return - - src.drop_held_item() - - return - -/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1) - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return -1 - - if(!held_item) - to_chat(src, "You have nothing to drop!") - return 0 - - if(!drop_gently) - if(istype(held_item, /obj/item/grenade)) - var/obj/item/grenade/G = held_item - G.loc = src.loc - G.prime() - to_chat(src, "You let go of the [held_item]!") - held_item = null - return 1 - - to_chat(src, "You drop the [held_item].") - - held_item.loc = src.loc - held_item = null - return 1 - -/mob/living/simple_animal/parrot/proc/perch_player() - set name = "Sit" - set category = "Parrot" - set desc = "Sit on a nice comfy perch." - - if(stat || !client) - return - - if(icon_state == "parrot_fly") - for(var/atom/movable/AM in view(src,1)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - src.loc = AM.loc - icon_state = "parrot_sit" - return - to_chat(src, "There is no perch nearby to sit on.") - return - -/* - * Sub-types - */ -/mob/living/simple_animal/parrot/Poly - name = "Poly" - desc = "Poly the Parrot. An expert on quantum cracker theory." - clean_speak = list( - "Poly wanna cracker!", - "Check the singlo, you chucklefucks!", - "Check the tesla, you shits!", - "STOP HOT-WIRING THE ENGINE, FUCKING CHRIST!", - "Wire the solars, you lazy bums!", - "WHO TOOK THE DAMN HARDSUITS?", - "OH GOD ITS FREE CALL THE SHUTTLE", - "Why are there so many atmos alerts?", - "OH GOD WHY WOULD YOU TURN ON THE PA BEFORE CONTAINMENT IS UP?", - "Remember to lock the emitters!", - "Stop goofing off and repair the goddam station!", - "The singularity is not your friend!", - "What were the wires again?", - "Goddam emaggers!" - ) - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/parrot/Poly/New() - ears = new /obj/item/radio/headset/headset_eng(src) - available_channels = list(":e") - ..() - -/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) - if(message_mode && istype(ears)) - ears.talk_into(src, message_pieces, message_mode, verb) - used_radios += ears - -/mob/living/simple_animal/parrot/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null) - if(speaker != src && prob(50)) - parrot_hear(html_decode(multilingual_to_message(message_pieces))) - ..() - - - -/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) - if(speaker != src && prob(50)) - parrot_hear(html_decode(multilingual_to_message(message_pieces))) - ..() - - -/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") - if(!message || stat) - return - speech_buffer.Add(message) +/* Parrots! + * Contains + * Defines + * Inventory (headset stuff) + * Attack responces + * AI + * Procs / Verbs (usable by players) + * Sub-types + */ + +/* + * Defines + */ + +//Only a maximum of one action and one intent should be active at any given time. +//Actions +#define PARROT_PERCH 1 //Sitting/sleeping, not moving +#define PARROT_SWOOP 2 //Moving towards or away from a target +#define PARROT_WANDER 4 //Moving without a specific target in mind + +//Intents +#define PARROT_STEAL 8 //Flying towards a target to steal it/from it +#define PARROT_ATTACK 16 //Flying towards a target to attack it +#define PARROT_RETURN 32 //Flying towards its perch +#define PARROT_FLEE 64 //Flying away from its attacker + + +/mob/living/simple_animal/parrot + name = "\improper Parrot" + desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" + icon = 'icons/mob/animal.dmi' + icon_state = "parrot_fly" + icon_living = "parrot_fly" + icon_dead = "parrot_dead" + pass_flags = PASSTABLE + can_collar = 1 + + var/list/clean_speak = list( + "Hi", + "Hello!", + "Cracker?", + "BAWWWWK george mellons griffing me") + speak_emote = list("squawks","says","yells") + emote_hear = list("squawks","bawks") + emote_see = list("flutters its wings") + + speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3) + + response_help = "pets the" + response_disarm = "gently moves aside the" + response_harm = "swats the" + stop_automated_movement = 1 + universal_speak = 1 + mob_size = MOB_SIZE_SMALL + + var/parrot_state = PARROT_WANDER //Hunt for a perch when created + var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick. + var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down + var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack + + var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. + var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed. + + var/list/speech_buffer = list() + var/list/available_channels = list() + + //Headset for Poly to yell at engineers :) + var/obj/item/radio/headset/ears = null + + //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, + //mobs it wants to attack or mobs that have attacked it + var/atom/movable/parrot_interest = null + + //Parrots will generally sit on their pertch unless something catches their eye. + //These vars store their preffered perch and if they dont have one, what they can use as a perch + var/obj/parrot_perch = null + var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \ + /obj/structure/filingcabinet, /obj/machinery/teleport, \ + /obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \ + /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ + /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ + /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ + /obj/machinery/computer) + + //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + var/obj/item/held_item = null + gold_core_spawnable = FRIENDLY_SPAWN + + +/mob/living/simple_animal/parrot/New() + ..() + GLOB.hear_radio_list += src + if(!ears) + var/headset = pick(/obj/item/radio/headset/headset_sec, \ + /obj/item/radio/headset/headset_eng, \ + /obj/item/radio/headset/headset_med, \ + /obj/item/radio/headset/headset_sci, \ + /obj/item/radio/headset/headset_cargo) + ears = new headset(src) + update_speak() + + parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var + + verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ + /mob/living/simple_animal/parrot/proc/steal_from_mob, \ + /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ + /mob/living/simple_animal/parrot/proc/perch_player) + +/mob/living/simple_animal/parrot/Destroy() + GLOB.hear_radio_list -= src + return ..() + +/mob/living/simple_animal/parrot/death(gibbed) + if(can_die()) + if(held_item) + held_item.loc = src.loc + held_item = null + walk(src,0) + return ..() + +/mob/living/simple_animal/parrot/Stat() + ..() + stat("Held Item", held_item) + +/* + * Inventory + */ +/mob/living/simple_animal/parrot/show_inv(mob/user as mob) + user.set_machine(src) + + var/dat = {""} + + dat += "" + if(can_collar) + dat += "" + dat += "" + + dat += {"
    Headset:[(ears && !(ears.flags&ABSTRACT)) ? ears : "Empty"]
     
    Collar:[(pcollar && !(pcollar.flags&ABSTRACT)) ? pcollar : "Empty"]
    + Close + "} + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) + popup.set_content(dat) + popup.open() + +/mob/living/simple_animal/parrot/Topic(href, href_list) + + //Can the usr physically do this? + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + + //Is the usr's mob type able to do this? + if(ishuman(usr) || isrobot(usr)) + if(href_list["remove_inv"]) + var/remove_from = href_list["remove_inv"] + switch(remove_from) + if("ears") + if(ears) + if(available_channels.len) + say("[pick(available_channels)]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + else + say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + ears.forceMove(loc) + ears = null + update_speak() + else + to_chat(usr, "There is nothing to remove from its [remove_from].") + return + show_inv(usr) + else if(href_list["add_inv"]) + var/add_to = href_list["add_inv"] + if(!usr.get_active_hand()) + to_chat(usr, "You have nothing in your hand to put on its [add_to].") + return + switch(add_to) + if("ears") + if(ears) + to_chat(usr, "It's already wearing something.") + return + else + var/obj/item/item_to_add = usr.get_active_hand() + if(!item_to_add) + return + + if( !istype(item_to_add, /obj/item/radio/headset) ) + to_chat(usr, "This object won't fit.") + return + + var/obj/item/radio/headset/headset_to_add = item_to_add + + usr.drop_item() + headset_to_add.forceMove(src) + ears = headset_to_add + to_chat(usr, "You fit the headset onto [src].") + + available_channels.Cut() + for(var/ch in headset_to_add.channels) + switch(ch) + if("Engineering") + available_channels.Add(":e") + if("Command") + available_channels.Add(":c") + if("Security") + available_channels.Add(":s") + if("Science") + available_channels.Add(":n") + if("Medical") + available_channels.Add(":m") + if("Mining") + available_channels.Add(":d") + if("Cargo") + available_channels.Add(":q") + + if(headset_to_add.translate_binary) + available_channels.Add(":b") + update_speak() + show_inv(usr) + else + ..() + + +/* + * Attack responces + */ +//Humans, monkeys, aliens +/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) + ..() + if(client) return + if(!stat && M.a_intent == "hurt") + + icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks + + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = M + parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. + + if(M.health < 50) //Weakened mob? Fight back! + parrot_state |= PARROT_ATTACK + else + parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! + drop_held_item(0) + return + +//Mobs with objects +/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + ..() + if(!stat && !client && !istype(O, /obj/item/stack/medical)) + if(O.force) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = user + parrot_state = PARROT_SWOOP | PARROT_FLEE + icon_state = "parrot_fly" + drop_held_item(0) + return + +//Bullets +/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj) + ..() + if(!stat && !client) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = null + parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! + parrot_been_shot += 5 + icon_state = "parrot_fly" + drop_held_item(0) + return + + +/* + * AI - Not really intelligent, but I'm calling it AI anyway. + */ +/mob/living/simple_animal/parrot/Life(seconds, times_fired) + ..() + + //Sprite and AI update for when a parrot gets pulled + if(pulledby && stat == CONSCIOUS) + icon_state = "parrot_fly" + +/mob/living/simple_animal/parrot/proc/update_speak() + speak.Cut() + + if(available_channels.len && ears) + for(var/possible_phrase in clean_speak) + //50/50 chance to not use the radio at all + speak += "[prob(50) ? pick(available_channels) : ""][possible_phrase]" + + else //If we have no headset or channels to use, dont try to use any! + for(var/possible_phrase in clean_speak) + speak += possible_phrase + +/mob/living/simple_animal/parrot/handle_automated_movement() + if(pulledby) + parrot_state = PARROT_WANDER + return + + if(!isturf(src.loc) || !canmove || buckled) + return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) + + +//-----SPEECH + /* Parrot speech mimickry! + Phrases that the parrot hears in mob/living/say() get added to speach_buffer. + Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. + Then it clears the buffer to make sure they dont magically remember something from hours ago. */ + if(speech_buffer.len && prob(10)) + if(clean_speak.len) + clean_speak -= pick(clean_speak) + + clean_speak += pick(speech_buffer) + speech_buffer.Cut() + + +//-----SLEEPING + if(parrot_state == PARROT_PERCH) + if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us + if(parrot_perch in view(src)) + parrot_state = PARROT_SWOOP | PARROT_RETURN + icon_state = "parrot_fly" + return + else + parrot_state = PARROT_WANDER + icon_state = "parrot_fly" + return + + if(--parrot_sleep_dur) //Zzz + return + + else + //This way we only call the stuff below once every [sleep_max] ticks. + parrot_sleep_dur = parrot_sleep_max + + //Cycle through message modes for the headset + update_speak() + + //Search for item to steal + parrot_interest = search_for_item() + if(parrot_interest) + custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.") + parrot_state = PARROT_SWOOP | PARROT_STEAL + icon_state = "parrot_fly" + return + +//-----WANDERING - This is basically a 'I dont know what to do yet' state + else if(parrot_state == PARROT_WANDER) + //Stop movement, we'll set it later + walk(src, 0) + parrot_interest = null + + //Wander around aimlessly. This will help keep the loops from searches down + //and possibly move the mob into a new are in view of something they can use + if(prob(90)) + step(src, pick(GLOB.cardinal)) + return + + if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. + var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item + if(AM) + if(istype(AM, /obj/item) || isliving(AM)) //If stealable item + parrot_interest = AM + custom_emote(1,"turns and flies towards [parrot_interest]") + parrot_state = PARROT_SWOOP | PARROT_STEAL + return + else //Else it's a perch + parrot_perch = AM + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + return + + if(parrot_interest && parrot_interest in view(src)) + parrot_state = PARROT_SWOOP | PARROT_STEAL + return + + if(parrot_perch && parrot_perch in view(src)) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + else //Have an item but no perch? Find one! + parrot_perch = search_for_perch() + if(parrot_perch) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return +//-----STEALING + else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) + walk(src,0) + if(!parrot_interest || held_item) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + if(!(parrot_interest in view(src))) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + if(in_range(src, parrot_interest)) + + if(isliving(parrot_interest)) + steal_from_mob() + + else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch + if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) + held_item = parrot_interest + parrot_interest.loc = src + visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + + parrot_interest = null + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + walk_to(src, parrot_interest, 1, parrot_speed) + return + +//-----RETURNING TO PERCH + else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) + walk(src, 0) + if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. + parrot_perch = null + parrot_state = PARROT_WANDER + return + + if(in_range(src, parrot_perch)) + src.loc = parrot_perch.loc + drop_held_item() + parrot_state = PARROT_PERCH + icon_state = "parrot_sit" + return + + walk_to(src, parrot_perch, 1, parrot_speed) + return + +//-----FLEEING + else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) + walk(src,0) + if(!parrot_interest || !isliving(parrot_interest)) //Sanity + parrot_state = PARROT_WANDER + + walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot) + parrot_been_shot-- + return + +//-----ATTACKING + else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) + + //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander + if(!parrot_interest || !isliving(parrot_interest)) + parrot_interest = null + parrot_state = PARROT_WANDER + return + + var/mob/living/L = parrot_interest + + //If the mob is close enough to interact with + if(in_range(src, parrot_interest)) + + //If the mob we've been chasing/attacking dies or falls into crit, check for loot! + if(L.stat) + parrot_interest = null + if(!held_item) + held_item = steal_from_ground() + if(!held_item) + held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. + if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home + parrot_state = PARROT_SWOOP | PARROT_RETURN + else + parrot_state = PARROT_WANDER + return + + //Time for the hurt to begin! + var/damage = rand(5,10) + + if(ishuman(parrot_interest)) + var/mob/living/carbon/human/H = parrot_interest + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) + + H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1) + custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) + + else + L.adjustBruteLoss(damage) + custom_emote(1, pick("pecks at [L].", "claws [L].")) + return + + //Otherwise, fly towards the mob! + else + walk_to(src, parrot_interest, 1, parrot_speed) + return +//-----STATE MISHAP + else //This should not happen. If it does lets reset everything and try again + walk(src,0) + parrot_interest = null + parrot_perch = null + drop_held_item() + parrot_state = PARROT_WANDER + return + +/* + * Procs + */ + +/mob/living/simple_animal/parrot/movement_delay() + if(client && stat == CONSCIOUS && parrot_state != "parrot_fly") + icon_state = "parrot_fly" + //Because the most appropriate place to set icon_state is movement_delay(), clearly + return ..() + +/mob/living/simple_animal/parrot/proc/search_for_item() + for(var/atom/movable/AM in view(src)) + //Skip items we already stole or are wearing or are too big + if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) + continue + + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.w_class < WEIGHT_CLASS_SMALL) + return I + + if(iscarbon(AM)) + var/mob/living/carbon/C = AM + if((C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) || (C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)) + return C + return null + +/mob/living/simple_animal/parrot/proc/search_for_perch() + for(var/obj/O in view(src)) + for(var/path in desired_perches) + if(istype(O, path)) + return O + return null + +//This proc was made to save on doing two 'in view' loops seperatly +/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() + for(var/atom/movable/AM in view(src)) + for(var/perch_path in desired_perches) + if(istype(AM, perch_path)) + return AM + + //Skip items we already stole or are wearing or are too big + if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) + continue + + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.w_class <= WEIGHT_CLASS_SMALL) + return I + + if(iscarbon(AM)) + var/mob/living/carbon/C = AM + if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL || C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) + return C + return null + + +/* + * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. + */ +/mob/living/simple_animal/parrot/proc/steal_from_ground() + set name = "Steal from ground" + set category = "Parrot" + set desc = "Grabs a nearby item." + + if(stat) + return -1 + + if(held_item) + to_chat(src, "You are already holding the [held_item]") + return 1 + + for(var/obj/item/I in view(1,src)) + //Make sure we're not already holding it and it's small enough + if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) + + //If we have a perch and the item is sitting on it, continue + if(!client && parrot_perch && I.loc == parrot_perch.loc) + continue + + held_item = I + I.loc = src + visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + return held_item + + to_chat(src, "There is nothing of interest to take.") + return 0 + +/mob/living/simple_animal/parrot/proc/steal_from_mob() + set name = "Steal from mob" + set category = "Parrot" + set desc = "Steals an item right out of a person's hand!" + + if(stat) + return -1 + + if(held_item) + to_chat(src, "You are already holding the [held_item]") + return 1 + + var/obj/item/stolen_item = null + + for(var/mob/living/carbon/C in view(1,src)) + if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) + stolen_item = C.l_hand + + if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) + stolen_item = C.r_hand + + if(stolen_item) + C.unEquip(stolen_item) + held_item = stolen_item + stolen_item.loc = src + visible_message("[src] grabs the [held_item] out of [C]'s hand!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") + return held_item + + to_chat(src, "There is nothing of interest to take.") + return 0 + +/mob/living/simple_animal/parrot/verb/drop_held_item_player() + set name = "Drop held item" + set category = "Parrot" + set desc = "Drop the item you're holding." + + if(stat) + return + + src.drop_held_item() + + return + +/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1) + set name = "Drop held item" + set category = "Parrot" + set desc = "Drop the item you're holding." + + if(stat) + return -1 + + if(!held_item) + to_chat(src, "You have nothing to drop!") + return 0 + + if(!drop_gently) + if(istype(held_item, /obj/item/grenade)) + var/obj/item/grenade/G = held_item + G.loc = src.loc + G.prime() + to_chat(src, "You let go of the [held_item]!") + held_item = null + return 1 + + to_chat(src, "You drop the [held_item].") + + held_item.loc = src.loc + held_item = null + return 1 + +/mob/living/simple_animal/parrot/proc/perch_player() + set name = "Sit" + set category = "Parrot" + set desc = "Sit on a nice comfy perch." + + if(stat || !client) + return + + if(icon_state == "parrot_fly") + for(var/atom/movable/AM in view(src,1)) + for(var/perch_path in desired_perches) + if(istype(AM, perch_path)) + src.loc = AM.loc + icon_state = "parrot_sit" + return + to_chat(src, "There is no perch nearby to sit on.") + return + +/* + * Sub-types + */ +/mob/living/simple_animal/parrot/Poly + name = "Poly" + desc = "Poly the Parrot. An expert on quantum cracker theory." + clean_speak = list( + "Poly wanna cracker!", + "Check the singlo, you chucklefucks!", + "Check the tesla, you shits!", + "STOP HOT-WIRING THE ENGINE, FUCKING CHRIST!", + "Wire the solars, you lazy bums!", + "WHO TOOK THE DAMN HARDSUITS?", + "OH GOD ITS FREE CALL THE SHUTTLE", + "Why are there so many atmos alerts?", + "OH GOD WHY WOULD YOU TURN ON THE PA BEFORE CONTAINMENT IS UP?", + "Remember to lock the emitters!", + "Stop goofing off and repair the goddam station!", + "The singularity is not your friend!", + "What were the wires again?", + "Goddam emaggers!" + ) + unique_pet = TRUE + gold_core_spawnable = NO_SPAWN + +/mob/living/simple_animal/parrot/Poly/New() + ears = new /obj/item/radio/headset/headset_eng(src) + available_channels = list(":e") + ..() + +/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) + if(message_mode && istype(ears)) + ears.talk_into(src, message_pieces, message_mode, verb) + used_radios += ears + +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) + if(speaker != src && prob(50)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) + ..() + + + +/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) + if(speaker != src && prob(50)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) + ..() + + +/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") + if(!message || stat) + return + speech_buffer.Add(message) diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 948c16314c95..663838d3a451 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -1,61 +1,61 @@ -/mob/living/simple_animal/shade - name = "Shade" - real_name = "Shade" - desc = "A bound spirit" - icon = 'icons/mob/mob.dmi' - icon_state = "shade" - icon_living = "shade" - icon_dead = "shade_dead" - maxHealth = 50 - health = 50 - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - response_help = "puts their hand through" - response_disarm = "flails at" - response_harm = "punches the" - melee_damage_lower = 5 - melee_damage_upper = 15 - attacktext = "drains the life from" - minbodytemp = 0 - maxbodytemp = 4000 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - speed = -1 - stop_automated_movement = 1 - status_flags = 0 - faction = list("cult") - status_flags = CANPUSH - loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) - del_on_death = 1 - deathmessage = "lets out a contented sigh as their form unwinds." - -/mob/living/simple_animal/shade/death(gibbed) - . = ..() - SSticker.mode.remove_cultist(src.mind, FALSE) - -/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri - if(istype(O, /obj/item/soulstone)) - O.transfer_soul("SHADE", src, user) - else - if(O.force) - var/damage = O.force - if(O.damtype == STAMINA) - damage = 0 - health -= damage - user.visible_message("[src] has been attacked with the [O] by [user]. ") - else - user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.") - return - -/mob/living/simple_animal/shade/cult/Initialize(mapload) - . = ..() - name = SSticker.cultdat?.shade_name - real_name = SSticker.cultdat?.shade_name - icon_state = SSticker.cultdat?.shade_icon_state - -/mob/living/simple_animal/shade/sword - universal_speak = 1 - faction = list("neutral") - -/mob/living/simple_animal/shade/sword/Initialize(mapload) - .=..() - status_flags |= GODMODE \ No newline at end of file +/mob/living/simple_animal/shade + name = "Shade" + real_name = "Shade" + desc = "A bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "shade" + icon_living = "shade" + icon_dead = "shade_dead" + maxHealth = 50 + health = 50 + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "puts their hand through" + response_disarm = "flails at" + response_harm = "punches the" + melee_damage_lower = 5 + melee_damage_upper = 15 + attacktext = "drains the life from" + minbodytemp = 0 + maxbodytemp = 4000 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + speed = -1 + stop_automated_movement = 1 + status_flags = 0 + faction = list("cult") + status_flags = CANPUSH + loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) + del_on_death = 1 + deathmessage = "lets out a contented sigh as their form unwinds." + +/mob/living/simple_animal/shade/death(gibbed) + . = ..() + SSticker.mode.remove_cultist(src.mind, FALSE) + +/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri + if(istype(O, /obj/item/soulstone)) + O.transfer_soul("SHADE", src, user) + else + if(O.force) + var/damage = O.force + if(O.damtype == STAMINA) + damage = 0 + health -= damage + user.visible_message("[src] has been attacked with the [O] by [user]. ") + else + user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.") + return + +/mob/living/simple_animal/shade/cult/Initialize(mapload) + . = ..() + name = SSticker.cultdat?.shade_name + real_name = SSticker.cultdat?.shade_name + icon_state = SSticker.cultdat?.shade_icon_state + +/mob/living/simple_animal/shade/sword + universal_speak = 1 + faction = list("neutral") + +/mob/living/simple_animal/shade/sword/Initialize(mapload) + .=..() + status_flags |= GODMODE diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 01c1f7cfe430..b740c783ac70 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -1,615 +1,615 @@ -/mob/living/simple_animal - name = "animal" - icon = 'icons/mob/animal.dmi' - health = 20 - maxHealth = 20 - gender = PLURAL //placeholder - - universal_understand = 1 - universal_speak = 0 - status_flags = CANPUSH - - var/icon_living = "" - var/icon_dead = "" - var/icon_resting = "" - var/icon_gib = null //We only try to show a gibbing animation if this exists. - var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites. - - var/list/speak = list() - var/speak_chance = 0 - var/list/emote_hear = list() //Hearable emotes - var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps - - var/turns_per_move = 1 - var/turns_since_move = 0 - var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. - var/wander = 1 // Does the mob wander around when idle? - var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. - - //Interaction - var/response_help = "pokes" - var/response_disarm = "shoves" - var/response_harm = "hits" - var/harm_intent_damage = 3 - var/force_threshold = 0 //Minimum force required to deal any damage - - //Temperature effect - var/minbodytemp = 250 - var/maxbodytemp = 350 - var/heat_damage_per_tick = 2 //amount of damage applied if animal's body temperature is higher than maxbodytemp - var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - - //Healable by medical stacks? Defaults to yes. - var/healable = 1 - - //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage - var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum - var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above - - //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly - var/melee_damage_lower = 0 - var/melee_damage_upper = 0 - var/obj_damage = 0 //how much damage this simple animal does to objects, if any - var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value - var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. - var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source - var/attacktext = "attacks" - var/attack_sound = null - var/friendly = "nuzzles" //If the mob does no damage with it's attack - var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls - - var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster - var/can_hide = 0 - - var/obj/item/clothing/accessory/petcollar/pcollar = null - var/collar_type //if the mob has collar sprites, define them. - var/unique_pet = FALSE // if the mob can be renamed - var/can_collar = FALSE // can add collar to mob or not - - //Hot simple_animal baby making vars - var/list/childtype = null - var/next_scan_time = 0 - var/animal_species //Sorry, no spider+corgi buttbabies. - - var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against - var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood - - var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions). - - var/datum/component/spawner/nest - - var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions - - var/list/loot = list() //list of things spawned at mob's loc when it dies - var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses - var/deathmessage = "" - var/death_sound = null //The sound played on death - - var/allow_movement_on_non_turfs = FALSE - - var/attacked_sound = "punch" - - var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) - var/can_have_ai = TRUE //once we have become sentient, we can never go back - - var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. - - //domestication - var/tame = 0 - - var/my_z // I don't want to confuse this with client registered_z - -/mob/living/simple_animal/Initialize(mapload) - . = ..() - GLOB.simple_animals[AIStatus] += src - if(gender == PLURAL) - gender = pick(MALE, FEMALE) - if(!real_name) - real_name = name - if(!loc) - stack_trace("Simple animal being instantiated in nullspace") - verbs -= /mob/verb/observe - if(!can_hide) - verbs -= /mob/living/simple_animal/verb/hide - if(pcollar) - pcollar = new(src) - regenerate_icons() - -/mob/living/simple_animal/Destroy() - QDEL_NULL(pcollar) - master_commander = null - GLOB.simple_animals[AIStatus] -= src - if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) - SSnpcpool.currentrun -= src - - if(nest) - nest.spawned_mobs -= src - nest = null - - var/turf/T = get_turf(src) - if (T && AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - - return ..() - -/mob/living/simple_animal/handle_atom_del(atom/A) - if(A == pcollar) - pcollar = null - return ..() - -/mob/living/simple_animal/examine(mob/user) - . = ..() - if(stat == DEAD) - . += "Upon closer examination, [p_they()] appear[p_s()] to be dead." - -/mob/living/simple_animal/updatehealth(reason = "none given") - ..(reason) - health = Clamp(health, 0, maxHealth) - med_hud_set_status() - -/mob/living/simple_animal/StartResting(updating = 1) - ..() - if(icon_resting && stat != DEAD) - icon_state = icon_resting - if(collar_type) - collar_type = "[initial(collar_type)]_rest" - regenerate_icons() - -/mob/living/simple_animal/StopResting(updating = 1) - ..() - if(icon_resting && stat != DEAD) - icon_state = icon_living - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() - -/mob/living/simple_animal/update_stat(reason = "none given") - if(status_flags & GODMODE) - return - - ..(reason) - if(stat != DEAD) - if(health < 1) - death() - create_debug_log("died of damage, trigger reason: [reason]") - -/mob/living/simple_animal/proc/handle_automated_action() - set waitfor = FALSE - return - -/mob/living/simple_animal/proc/handle_automated_movement() - set waitfor = FALSE - if(!stop_automated_movement && wander) - if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled - var/anydir = pick(cardinal) - if(Process_Spacemove(anydir)) - Move(get_step(src,anydir), anydir) - turns_since_move = 0 - return 1 - -/mob/living/simple_animal/proc/handle_automated_speech(override) - set waitfor = FALSE - if(speak_chance) - if(prob(speak_chance) || override) - if(speak && speak.len) - if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) - var/length = speak.len - if(emote_hear && emote_hear.len) - length += emote_hear.len - if(emote_see && emote_see.len) - length += emote_see.len - var/randomValue = rand(1,length) - if(randomValue <= speak.len) - say(pick(speak)) - else - randomValue -= speak.len - if(emote_see && randomValue <= emote_see.len) - custom_emote(1, pick(emote_see)) - else - custom_emote(2, pick(emote_hear)) - else - say(pick(speak)) - else - if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - custom_emote(1, pick(emote_see)) - if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) - custom_emote(2, pick(emote_hear)) - if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - var/length = emote_hear.len + emote_see.len - var/pick = rand(1,length) - if(pick <= emote_see.len) - custom_emote(1, pick(emote_see)) - else - custom_emote(2,pick(emote_hear)) - - -/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) - var/atmos_suitable = 1 - - var/areatemp = get_temperature(environment) - - if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations)) - var/diff = areatemp - bodytemperature - diff = diff / 5 - bodytemperature += diff - - var/tox = environment.toxins - var/oxy = environment.oxygen - var/n2 = environment.nitrogen - var/co2 = environment.carbon_dioxide - - if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) - atmos_suitable = 0 - throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) - else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) - atmos_suitable = 0 - throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) - else - clear_alert("not_enough_oxy") - clear_alert("too_much_oxy") - - if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) - atmos_suitable = 0 - throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) - else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) - atmos_suitable = 0 - throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) - else - clear_alert("too_much_tox") - clear_alert("not_enough_tox") - - if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) - atmos_suitable = 0 - - if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) - atmos_suitable = 0 - - if(!atmos_suitable) - adjustHealth(unsuitable_atmos_damage) - - handle_temperature_damage() - -/mob/living/simple_animal/proc/handle_temperature_damage() - if(bodytemperature < minbodytemp) - adjustHealth(cold_damage_per_tick) - else if(bodytemperature > maxbodytemp) - adjustHealth(heat_damage_per_tick) - -/mob/living/simple_animal/gib() - if(icon_gib) - flick(icon_gib, src) - if(butcher_results) - var/atom/Tsec = drop_location() - for(var/path in butcher_results) - for(var/i in 1 to butcher_results[path]) - new path(Tsec) - if(pcollar) - pcollar.forceMove(drop_location()) - pcollar = null - ..() - -/mob/living/simple_animal/emote(act, m_type = 1, message = null, force) - if(stat) - return - act = lowertext(act) - switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. - if("scream") - message = "\The [src] whimpers." - m_type = 2 - if("help") - to_chat(src, "scream") - - ..() - -/mob/living/simple_animal/say_quote(message) - var/verb = "says" - - if(speak_emote.len) - verb = pick(speak_emote) - - return verb - -/mob/living/simple_animal/movement_delay() - . = ..() - - . = speed - - . += config.animal_delay - -/mob/living/simple_animal/Stat() - ..() - if(statpanel("Status")) - stat(null, "Health: [round((health / maxHealth) * 100)]%") - return TRUE - -/mob/living/simple_animal/proc/drop_loot() - if(loot.len) - for(var/i in loot) - new i(loc) - -/mob/living/simple_animal/revive() - ..() - density = initial(density) - -/mob/living/simple_animal/death(gibbed) - // Only execute the below if we successfully died - . = ..() - if(!.) - return FALSE - flying = FALSE - if(nest) - nest.spawned_mobs -= src - nest = null - drop_loot() - if(!gibbed) - if(death_sound) - playsound(get_turf(src),death_sound, 200, 1) - if(deathmessage) - visible_message("\The [src] [deathmessage]") - else if(!del_on_death) - visible_message("\The [src] stops moving...") - if(del_on_death) - //Prevent infinite loops if the mob Destroy() is overridden in such - //a manner as to cause a call to death() again - del_on_death = FALSE - ghostize() - qdel(src) - else - health = 0 - icon_state = icon_dead - if(flip_on_death) - transform = transform.Turn(180) - density = 0 - if(collar_type) - collar_type = "[initial(collar_type)]_dead" - regenerate_icons() - -/mob/living/simple_animal/proc/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility) - return FALSE - if(ismob(the_target)) - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - if(isliving(the_target)) - var/mob/living/L = the_target - if(L.stat != CONSCIOUS) - return FALSE - if(ismecha(the_target)) - var/obj/mecha/M = the_target - if(M.occupant) - return FALSE - if(isspacepod(the_target)) - var/obj/spacepod/S = the_target - if(S.pilot) - return FALSE - return TRUE - -/mob/living/simple_animal/handle_fire() - return TRUE - -/mob/living/simple_animal/IgniteMob() - return FALSE - -/mob/living/simple_animal/ExtinguishMob() - return - -/mob/living/simple_animal/revive() - ..() - health = maxHealth - icon = initial(icon) - icon_state = icon_living - density = initial(density) - update_canmove() - flying = initial(flying) - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() - -/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 - if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) - return FALSE - next_scan_time = world.time + 400 - - var/alone = TRUE - var/mob/living/simple_animal/partner - var/children = 0 - - for(var/mob/M in oview(7, src)) - if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. - continue - else if(istype(M, childtype)) //Check for children SECOND. - children++ - else if(istype(M, animal_species)) - if(M.ckey) - continue - else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; - partner = M - else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. - return //we never mate when not alone, so just abort early - - if(alone && partner && children < 3) - var/childspawn = pickweight(childtype) - var/turf/target = get_turf(loc) - if(target) - return new childspawn(target) - -/mob/living/simple_animal/show_inv(mob/user as mob) - if(!can_collar) - return - - user.set_machine(src) - var/dat = "
    Collar:[(pcollar && !(pcollar.flags & ABSTRACT)) ? pcollar : "Empty"]
    " - dat += "Close" - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250) - popup.set_content(dat) - popup.open() - -/mob/living/simple_animal/get_item_by_slot(slot_id) - switch(slot_id) - if(slot_collar) - return pcollar - . = ..() - -/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) - // . = ..() // Do not call parent. We do not want animals using their hand slots. - switch(slot) - if(slot_collar) - if(pcollar) - return FALSE - if(!can_collar) - return FALSE - if(!istype(I, /obj/item/clothing/accessory/petcollar)) - return FALSE - return TRUE - -/mob/living/simple_animal/equip_to_slot(obj/item/W, slot) - if(!istype(W)) - return FALSE - - if(!slot) - return FALSE - - W.layer = ABOVE_HUD_LAYER - W.plane = ABOVE_HUD_PLANE - - switch(slot) - if(slot_collar) - add_collar(W) - -/mob/living/simple_animal/unEquip(obj/item/I, force) - . = ..() - if(!. || !I) - return - - if(I == pcollar) - pcollar = null - regenerate_icons() - -/mob/living/simple_animal/get_access() - . = ..() - if(pcollar) - . |= pcollar.GetAccess() - -/mob/living/simple_animal/update_canmove(delay_action_updates = 0) - if(paralysis || stunned || IsWeakened() || stat || resting) - drop_r_hand() - drop_l_hand() - canmove = 0 - else if(buckled) - canmove = 0 - else - canmove = 1 - if(!canmove) - walk(src, 0) //stop mid walk - - update_transform() - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -/mob/living/simple_animal/update_transform() - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - var/changed = FALSE - - if(resize != RESIZE_DEFAULT_SIZE) - changed = TRUE - ntransform.Scale(resize) - resize = RESIZE_DEFAULT_SIZE - - if(changed) - animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) - -/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion - toggle_ai(AI_OFF) - can_have_ai = FALSE - -/mob/living/simple_animal/grant_death_vision() - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_OBSERVER - sync_lighting_plane_alpha() - -/mob/living/simple_animal/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() - -/mob/living/simple_animal/proc/toggle_ai(togglestatus) - if(!can_have_ai && (togglestatus != AI_OFF)) - return - if(AIStatus != togglestatus) - if(togglestatus > 0 && togglestatus < 5) - if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) - var/turf/T = get_turf(src) - if(AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - else - SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src - GLOB.simple_animals[AIStatus] -= src - GLOB.simple_animals[togglestatus] += src - AIStatus = togglestatus - else - stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") - -/mob/living/simple_animal/proc/consider_wakeup() - if(pulledby || shouldwakeup) - toggle_ai(AI_ON) - -/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(!ckey && !stat)//Not unconscious - if(AIStatus == AI_IDLE) - toggle_ai(AI_ON) - -/mob/living/simple_animal/onTransitZ(old_z, new_z) - ..() - if(AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src - toggle_ai(initial(AIStatus)) - -/mob/living/simple_animal/proc/add_collar(obj/item/clothing/accessory/petcollar/P, mob/user) - if(QDELETED(P) || pcollar) - return - if(user && !user.unEquip(P)) - return - P.forceMove(src) - P.equipped(src) - pcollar = P - regenerate_icons() - if(user) - to_chat(user, "You put [P] around [src]'s neck.") - if(P.tagname && !unique_pet) - name = P.tagname - real_name = P.tagname - -/mob/living/simple_animal/regenerate_icons() - if(pcollar && collar_type) - cut_overlays() - add_overlay("[collar_type]collar") - add_overlay("[collar_type]tag") \ No newline at end of file +/mob/living/simple_animal + name = "animal" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + gender = PLURAL //placeholder + + universal_understand = 1 + universal_speak = 0 + status_flags = CANPUSH + + var/icon_living = "" + var/icon_dead = "" + var/icon_resting = "" + var/icon_gib = null //We only try to show a gibbing animation if this exists. + var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites. + + var/list/speak = list() + var/speak_chance = 0 + var/list/emote_hear = list() //Hearable emotes + var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/turns_per_move = 1 + var/turns_since_move = 0 + var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. + var/wander = 1 // Does the mob wander around when idle? + var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. + + //Interaction + var/response_help = "pokes" + var/response_disarm = "shoves" + var/response_harm = "hits" + var/harm_intent_damage = 3 + var/force_threshold = 0 //Minimum force required to deal any damage + + //Temperature effect + var/minbodytemp = 250 + var/maxbodytemp = 350 + var/heat_damage_per_tick = 2 //amount of damage applied if animal's body temperature is higher than maxbodytemp + var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + + //Healable by medical stacks? Defaults to yes. + var/healable = 1 + + //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage + var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum + var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above + + //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly + var/melee_damage_lower = 0 + var/melee_damage_upper = 0 + var/obj_damage = 0 //how much damage this simple animal does to objects, if any + var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value + var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. + var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source + var/attacktext = "attacks" + var/attack_sound = null + var/friendly = "nuzzles" //If the mob does no damage with it's attack + var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls + + var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster + var/can_hide = 0 + + var/obj/item/clothing/accessory/petcollar/pcollar = null + var/collar_type //if the mob has collar sprites, define them. + var/unique_pet = FALSE // if the mob can be renamed + var/can_collar = FALSE // can add collar to mob or not + + //Hot simple_animal baby making vars + var/list/childtype = null + var/next_scan_time = 0 + var/animal_species //Sorry, no spider+corgi buttbabies. + + var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against + var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood + + var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions). + + var/datum/component/spawner/nest + + var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions + + var/list/loot = list() //list of things spawned at mob's loc when it dies + var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses + var/deathmessage = "" + var/death_sound = null //The sound played on death + + var/allow_movement_on_non_turfs = FALSE + + var/attacked_sound = "punch" + + var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) + var/can_have_ai = TRUE //once we have become sentient, we can never go back + + var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. + + //domestication + var/tame = 0 + + var/my_z // I don't want to confuse this with client registered_z + +/mob/living/simple_animal/Initialize(mapload) + . = ..() + GLOB.simple_animals[AIStatus] += src + if(gender == PLURAL) + gender = pick(MALE, FEMALE) + if(!real_name) + real_name = name + if(!loc) + stack_trace("Simple animal being instantiated in nullspace") + verbs -= /mob/verb/observe + if(!can_hide) + verbs -= /mob/living/simple_animal/verb/hide + if(pcollar) + pcollar = new(src) + regenerate_icons() + +/mob/living/simple_animal/Destroy() + QDEL_NULL(pcollar) + master_commander = null + GLOB.simple_animals[AIStatus] -= src + if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) + SSnpcpool.currentrun -= src + + if(nest) + nest.spawned_mobs -= src + nest = null + + var/turf/T = get_turf(src) + if (T && AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + + return ..() + +/mob/living/simple_animal/handle_atom_del(atom/A) + if(A == pcollar) + pcollar = null + return ..() + +/mob/living/simple_animal/examine(mob/user) + . = ..() + if(stat == DEAD) + . += "Upon closer examination, [p_they()] appear[p_s()] to be dead." + +/mob/living/simple_animal/updatehealth(reason = "none given") + ..(reason) + health = Clamp(health, 0, maxHealth) + med_hud_set_status() + +/mob/living/simple_animal/StartResting(updating = 1) + ..() + if(icon_resting && stat != DEAD) + icon_state = icon_resting + if(collar_type) + collar_type = "[initial(collar_type)]_rest" + regenerate_icons() + +/mob/living/simple_animal/StopResting(updating = 1) + ..() + if(icon_resting && stat != DEAD) + icon_state = icon_living + if(collar_type) + collar_type = "[initial(collar_type)]" + regenerate_icons() + +/mob/living/simple_animal/update_stat(reason = "none given") + if(status_flags & GODMODE) + return + + ..(reason) + if(stat != DEAD) + if(health < 1) + death() + create_debug_log("died of damage, trigger reason: [reason]") + +/mob/living/simple_animal/proc/handle_automated_action() + set waitfor = FALSE + return + +/mob/living/simple_animal/proc/handle_automated_movement() + set waitfor = FALSE + if(!stop_automated_movement && wander) + if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled + var/anydir = pick(GLOB.cardinal) + if(Process_Spacemove(anydir)) + Move(get_step(src,anydir), anydir) + turns_since_move = 0 + return 1 + +/mob/living/simple_animal/proc/handle_automated_speech(override) + set waitfor = FALSE + if(speak_chance) + if(prob(speak_chance) || override) + if(speak && speak.len) + if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) + var/length = speak.len + if(emote_hear && emote_hear.len) + length += emote_hear.len + if(emote_see && emote_see.len) + length += emote_see.len + var/randomValue = rand(1,length) + if(randomValue <= speak.len) + say(pick(speak)) + else + randomValue -= speak.len + if(emote_see && randomValue <= emote_see.len) + custom_emote(1, pick(emote_see)) + else + custom_emote(2, pick(emote_hear)) + else + say(pick(speak)) + else + if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + custom_emote(1, pick(emote_see)) + if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) + custom_emote(2, pick(emote_hear)) + if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + var/length = emote_hear.len + emote_see.len + var/pick = rand(1,length) + if(pick <= emote_see.len) + custom_emote(1, pick(emote_see)) + else + custom_emote(2,pick(emote_hear)) + + +/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) + var/atmos_suitable = 1 + + var/areatemp = get_temperature(environment) + + if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations)) + var/diff = areatemp - bodytemperature + diff = diff / 5 + bodytemperature += diff + + var/tox = environment.toxins + var/oxy = environment.oxygen + var/n2 = environment.nitrogen + var/co2 = environment.carbon_dioxide + + if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) + atmos_suitable = 0 + throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) + else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) + atmos_suitable = 0 + throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) + else + clear_alert("not_enough_oxy") + clear_alert("too_much_oxy") + + if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) + atmos_suitable = 0 + throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) + else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) + atmos_suitable = 0 + throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) + else + clear_alert("too_much_tox") + clear_alert("not_enough_tox") + + if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) + atmos_suitable = 0 + + if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) + atmos_suitable = 0 + + if(!atmos_suitable) + adjustHealth(unsuitable_atmos_damage) + + handle_temperature_damage() + +/mob/living/simple_animal/proc/handle_temperature_damage() + if(bodytemperature < minbodytemp) + adjustHealth(cold_damage_per_tick) + else if(bodytemperature > maxbodytemp) + adjustHealth(heat_damage_per_tick) + +/mob/living/simple_animal/gib() + if(icon_gib) + flick(icon_gib, src) + if(butcher_results) + var/atom/Tsec = drop_location() + for(var/path in butcher_results) + for(var/i in 1 to butcher_results[path]) + new path(Tsec) + if(pcollar) + pcollar.forceMove(drop_location()) + pcollar = null + ..() + +/mob/living/simple_animal/emote(act, m_type = 1, message = null, force) + if(stat) + return + act = lowertext(act) + switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. + if("scream") + message = "\The [src] whimpers." + m_type = 2 + if("help") + to_chat(src, "scream") + + ..() + +/mob/living/simple_animal/say_quote(message) + var/verb = "says" + + if(speak_emote.len) + verb = pick(speak_emote) + + return verb + +/mob/living/simple_animal/movement_delay() + . = ..() + + . = speed + + . += config.animal_delay + +/mob/living/simple_animal/Stat() + ..() + if(statpanel("Status")) + stat(null, "Health: [round((health / maxHealth) * 100)]%") + return TRUE + +/mob/living/simple_animal/proc/drop_loot() + if(loot.len) + for(var/i in loot) + new i(loc) + +/mob/living/simple_animal/revive() + ..() + density = initial(density) + +/mob/living/simple_animal/death(gibbed) + // Only execute the below if we successfully died + . = ..() + if(!.) + return FALSE + flying = FALSE + if(nest) + nest.spawned_mobs -= src + nest = null + drop_loot() + if(!gibbed) + if(death_sound) + playsound(get_turf(src),death_sound, 200, 1) + if(deathmessage) + visible_message("\The [src] [deathmessage]") + else if(!del_on_death) + visible_message("\The [src] stops moving...") + if(del_on_death) + //Prevent infinite loops if the mob Destroy() is overridden in such + //a manner as to cause a call to death() again + del_on_death = FALSE + ghostize() + qdel(src) + else + health = 0 + icon_state = icon_dead + if(flip_on_death) + transform = transform.Turn(180) + density = 0 + if(collar_type) + collar_type = "[initial(collar_type)]_dead" + regenerate_icons() + +/mob/living/simple_animal/proc/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility) + return FALSE + if(ismob(the_target)) + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + if(isliving(the_target)) + var/mob/living/L = the_target + if(L.stat != CONSCIOUS) + return FALSE + if(ismecha(the_target)) + var/obj/mecha/M = the_target + if(M.occupant) + return FALSE + if(isspacepod(the_target)) + var/obj/spacepod/S = the_target + if(S.pilot) + return FALSE + return TRUE + +/mob/living/simple_animal/handle_fire() + return TRUE + +/mob/living/simple_animal/IgniteMob() + return FALSE + +/mob/living/simple_animal/ExtinguishMob() + return + +/mob/living/simple_animal/revive() + ..() + health = maxHealth + icon = initial(icon) + icon_state = icon_living + density = initial(density) + update_canmove() + flying = initial(flying) + if(collar_type) + collar_type = "[initial(collar_type)]" + regenerate_icons() + +/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 + if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) + return FALSE + next_scan_time = world.time + 400 + + var/alone = TRUE + var/mob/living/simple_animal/partner + var/children = 0 + + for(var/mob/M in oview(7, src)) + if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. + continue + else if(istype(M, childtype)) //Check for children SECOND. + children++ + else if(istype(M, animal_species)) + if(M.ckey) + continue + else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; + partner = M + else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. + return //we never mate when not alone, so just abort early + + if(alone && partner && children < 3) + var/childspawn = pickweight(childtype) + var/turf/target = get_turf(loc) + if(target) + return new childspawn(target) + +/mob/living/simple_animal/show_inv(mob/user as mob) + if(!can_collar) + return + + user.set_machine(src) + var/dat = "
    Collar:[(pcollar && !(pcollar.flags & ABSTRACT)) ? pcollar : "Empty"]
    " + dat += "Close" + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250) + popup.set_content(dat) + popup.open() + +/mob/living/simple_animal/get_item_by_slot(slot_id) + switch(slot_id) + if(slot_collar) + return pcollar + . = ..() + +/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) + // . = ..() // Do not call parent. We do not want animals using their hand slots. + switch(slot) + if(slot_collar) + if(pcollar) + return FALSE + if(!can_collar) + return FALSE + if(!istype(I, /obj/item/clothing/accessory/petcollar)) + return FALSE + return TRUE + +/mob/living/simple_animal/equip_to_slot(obj/item/W, slot) + if(!istype(W)) + return FALSE + + if(!slot) + return FALSE + + W.layer = ABOVE_HUD_LAYER + W.plane = ABOVE_HUD_PLANE + + switch(slot) + if(slot_collar) + add_collar(W) + +/mob/living/simple_animal/unEquip(obj/item/I, force) + . = ..() + if(!. || !I) + return + + if(I == pcollar) + pcollar = null + regenerate_icons() + +/mob/living/simple_animal/get_access() + . = ..() + if(pcollar) + . |= pcollar.GetAccess() + +/mob/living/simple_animal/update_canmove(delay_action_updates = 0) + if(paralysis || stunned || IsWeakened() || stat || resting) + drop_r_hand() + drop_l_hand() + canmove = 0 + else if(buckled) + canmove = 0 + else + canmove = 1 + if(!canmove) + walk(src, 0) //stop mid walk + + update_transform() + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +/mob/living/simple_animal/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/changed = FALSE + + if(resize != RESIZE_DEFAULT_SIZE) + changed = TRUE + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + if(changed) + animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) + +/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion + toggle_ai(AI_OFF) + can_have_ai = FALSE + +/mob/living/simple_animal/grant_death_vision() + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_OBSERVER + sync_lighting_plane_alpha() + +/mob/living/simple_animal/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() + +/mob/living/simple_animal/proc/toggle_ai(togglestatus) + if(!can_have_ai && (togglestatus != AI_OFF)) + return + if(AIStatus != togglestatus) + if(togglestatus > 0 && togglestatus < 5) + if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) + var/turf/T = get_turf(src) + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + else + SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src + GLOB.simple_animals[AIStatus] -= src + GLOB.simple_animals[togglestatus] += src + AIStatus = togglestatus + else + stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") + +/mob/living/simple_animal/proc/consider_wakeup() + if(pulledby || shouldwakeup) + toggle_ai(AI_ON) + +/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(!ckey && !stat)//Not unconscious + if(AIStatus == AI_IDLE) + toggle_ai(AI_ON) + +/mob/living/simple_animal/onTransitZ(old_z, new_z) + ..() + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src + toggle_ai(initial(AIStatus)) + +/mob/living/simple_animal/proc/add_collar(obj/item/clothing/accessory/petcollar/P, mob/user) + if(QDELETED(P) || pcollar) + return + if(user && !user.unEquip(P)) + return + P.forceMove(src) + P.equipped(src) + pcollar = P + regenerate_icons() + if(user) + to_chat(user, "You put [P] around [src]'s neck.") + if(P.tagname && !unique_pet) + name = P.tagname + real_name = P.tagname + +/mob/living/simple_animal/regenerate_icons() + if(pcollar && collar_type) + cut_overlays() + add_overlay("[collar_type]collar") + add_overlay("[collar_type]tag") diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm index 29a1adbd347d..cf72363ac9af 100644 --- a/code/modules/mob/living/simple_animal/slime/death.dm +++ b/code/modules/mob/living/simple_animal/slime/death.dm @@ -29,4 +29,4 @@ /mob/living/simple_animal/slime/gib() death(TRUE) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm index 7fc7f4c5f877..94806ede9d1c 100644 --- a/code/modules/mob/living/simple_animal/slime/emote.dm +++ b/code/modules/mob/living/simple_animal/slime/emote.dm @@ -102,4 +102,4 @@ loc.audible_message(message) if(regenerate_icons) - regenerate_icons() \ No newline at end of file + regenerate_icons() diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index efd4c19d55c5..d03e7ada419d 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -368,7 +368,7 @@ if (holding_still) holding_still = max(holding_still - hungry, 0) else if(canmove && isturf(loc) && prob(50)) - step(src, pick(cardinal)) + step(src, pick(GLOB.cardinal)) else if(holding_still) @@ -376,7 +376,7 @@ else if (docile && pulledby) holding_still = 10 else if(canmove && isturf(loc) && prob(33)) - step(src, pick(cardinal)) + step(src, pick(GLOB.cardinal)) else if(!AIproc) INVOKE_ASYNC(src, .proc/AIprocess) @@ -614,4 +614,4 @@ return 0 if(holding_still) return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm index 01269aab8aa2..74241dc676cd 100644 --- a/code/modules/mob/living/simple_animal/slime/say.dm +++ b/code/modules/mob/living/simple_animal/slime/say.dm @@ -9,7 +9,7 @@ return verb -/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol) +/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) if(speaker != src && !stat) if(speaker in Friends) speech_buffer = list() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index f02b3bd59180..339240af5295 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -507,4 +507,4 @@ if(buckled) to_chat(src, "I can't vent crawl while feeding...") return - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/tribbles.dm b/code/modules/mob/living/simple_animal/tribbles.dm index b916fa801f0a..d5f130324bbd 100644 --- a/code/modules/mob/living/simple_animal/tribbles.dm +++ b/code/modules/mob/living/simple_animal/tribbles.dm @@ -1,4 +1,4 @@ -var/global/totaltribbles = 0 //global variable so it updates for all tribbles, not just the new one being made. +GLOBAL_VAR_INIT(totaltribbles, 0) //global variable so it updates for all tribbles, not just the new one being made. /mob/living/simple_animal/tribble @@ -35,7 +35,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles, //random pixel offsets so they cover the floor src.pixel_x = rand(-5.0, 5) src.pixel_y = rand(-5.0, 5) - totaltribbles += 1 + GLOB.totaltribbles += 1 /mob/living/simple_animal/tribble/attack_hand(mob/user as mob) @@ -61,7 +61,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles, /mob/living/simple_animal/tribble/proc/procreate() ..() - if(totaltribbles <= maxtribbles) + if(GLOB.totaltribbles <= maxtribbles) for(var/mob/living/simple_animal/tribble/F in src.loc) if(!F || F == src) new /mob/living/simple_animal/tribble(src.loc) @@ -84,7 +84,7 @@ var/global/totaltribbles = 0 //global variable so it updates for all tribbles, . = ..(gibbed) if(!.) return FALSE - totaltribbles -= 1 + GLOB.totaltribbles -= 1 //||Item version of the trible || diff --git a/code/modules/mob/living/stat_states.dm b/code/modules/mob/living/stat_states.dm index 70cced62aabb..926bdc5970dc 100644 --- a/code/modules/mob/living/stat_states.dm +++ b/code/modules/mob/living/stat_states.dm @@ -7,6 +7,7 @@ else if(stat == UNCONSCIOUS) return 0 create_attack_log("Fallen unconscious at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Fallen unconscious", ATKLOG_ALL) log_game("[key_name(src)] fell unconscious at [atom_loc_line(get_turf(src))]") stat = UNCONSCIOUS if(updating) @@ -22,6 +23,7 @@ else if(stat == CONSCIOUS) return 0 create_attack_log("Woken up at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Woken up", ATKLOG_ALL) log_game("[key_name(src)] woke up at [atom_loc_line(get_turf(src))]") stat = CONSCIOUS if(updating) @@ -45,6 +47,7 @@ if(!can_be_revived()) return 0 create_attack_log("Came back to life at [atom_loc_line(get_turf(src))]") + add_attack_logs(src, null, "Came back to life", ATKLOG_ALL) log_game("[key_name(src)] came back to life at [atom_loc_line(get_turf(src))]") stat = CONSCIOUS GLOB.dead_mob_list -= src @@ -73,4 +76,4 @@ return 1 /mob/living/proc/check_death_method() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 72f136304d70..b2871038155d 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -478,7 +478,7 @@ . = val_change ? STATUS_UPDATE_BLIND : STATUS_UPDATE_NONE disabilities &= ~BLIND if(val_change && updating) - CureIfHasDisability(BLINDBLOCK) + CureIfHasDisability(GLOB.blindblock) update_blind_effects() // Coughing @@ -488,7 +488,7 @@ /mob/living/proc/CureCoughing() disabilities &= ~COUGHING - CureIfHasDisability(COUGHBLOCK) + CureIfHasDisability(GLOB.coughblock) // Deaf @@ -497,7 +497,7 @@ /mob/living/proc/CureDeaf() disabilities &= ~DEAF - CureIfHasDisability(DEAFBLOCK) + CureIfHasDisability(GLOB.deafblock) // Epilepsy @@ -506,7 +506,7 @@ /mob/living/proc/CureEpilepsy() disabilities &= ~EPILEPSY - CureIfHasDisability(EPILEPSYBLOCK) + CureIfHasDisability(GLOB.epilepsyblock) // Mute @@ -515,7 +515,7 @@ /mob/living/proc/CureMute() disabilities &= ~MUTE - CureIfHasDisability(MUTEBLOCK) + CureIfHasDisability(GLOB.muteblock) // Nearsighted @@ -531,7 +531,7 @@ . = val_change ? STATUS_UPDATE_NEARSIGHTED : STATUS_UPDATE_NONE disabilities &= ~NEARSIGHTED if(val_change && updating) - CureIfHasDisability(GLASSESBLOCK) + CureIfHasDisability(GLOB.glassesblock) update_nearsighted_effects() // Nervous @@ -541,7 +541,7 @@ /mob/living/proc/CureNervous() disabilities &= ~NERVOUS - CureIfHasDisability(NERVOUSBLOCK) + CureIfHasDisability(GLOB.nervousblock) // Tourettes @@ -550,7 +550,7 @@ /mob/living/proc/CureTourettes() disabilities &= ~TOURETTES - CureIfHasDisability(TWITCHBLOCK) + CureIfHasDisability(GLOB.twitchblock) /mob/living/proc/CureIfHasDisability(block) if(dna && dna.GetSEState(block)) @@ -598,4 +598,4 @@ else if(priority_absorb_key["self_message"]) to_chat(src, "[priority_absorb_key["self_message"]]") priority_absorb_key["stuns_absorbed"] += amount - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index a403aa39244e..03b0ff5752b0 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -27,4 +27,4 @@ // "something indescribable" -> too many tastes, not enough flavor. last_taste_time = world.time - last_taste_text = text_output \ No newline at end of file + last_taste_text = text_output diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm index 20cc969119fe..d24301b0dd0c 100644 --- a/code/modules/mob/living/update_status.dm +++ b/code/modules/mob/living/update_status.dm @@ -65,8 +65,14 @@ return !(IsWeakened() || paralysis || stat || (status_flags & FAKEDEATH)) // Whether the mob is capable of actions or not -/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE) - if(stat || paralysis || stunned || IsWeakened() || (!ignore_restraints && restrained()) || (!ignore_lying && lying)) +/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, ignore_lying = FALSE, list/extra_checks = list(), use_default_checks = TRUE) + // By default, checks for weakness and stunned get added to the extra_checks list. + // Setting `use_default_checks` to FALSE means that you don't want it checking for these statuses or you are supplying your own checks. + if(use_default_checks) + extra_checks += CALLBACK(src, /mob.proc/IsWeakened) + extra_checks += CALLBACK(src, /mob.proc/IsStunned) + + if(stat || paralysis || (!ignore_restraints && restrained()) || (!ignore_lying && lying) || check_for_true_callbacks(extra_checks)) return TRUE // wonderful proc names, I know - used to check whether the blur overlay diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 66b4f2f9a16a..5345bda5b94c 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -1,70 +1,71 @@ -//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying -/mob/proc/update_Login_details() - //Multikey checks and logging - lastKnownIP = client.address - computer_id = client.computer_id - log_access_in(client) - create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") - if(config.log_access) - for(var/mob/M in GLOB.player_list) - if(M == src) continue - if( M.key && (M.key != key) ) - var/matches - if( (M.lastKnownIP == client.address) ) - matches += "IP ([client.address])" - if( (M.computer_id == client.computer_id) ) - if(matches) matches += " and " - matches += "ID ([client.computer_id])" - if(!config.disable_cid_warn_popup) - spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") - if(matches) - if(M.client) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") - else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") - -/mob/Login() - GLOB.player_list |= src - update_Login_details() - world.update_status() - - client.images = null //remove the images such as AIs being unable to see runes - client.screen = list() //remove hud items just in case - if(client.click_intercept) - client.click_intercept.quit() // Let's not keep any old click_intercepts - - if(!hud_used) - create_mob_hud() - if(hud_used) - hud_used.show_hud(hud_used.hud_version) - - next_move = 1 - sight |= SEE_SELF - ..() - - reset_perspective(loc) - - - if(ckey in GLOB.deadmins) - verbs += /client/proc/readmin - - //Clear ability list and update from mob. - client.verbs -= ability_verbs - - if(abilities) - client.verbs |= abilities - - //HUD updates (antag hud, etc) - //readd this mob's HUDs (antag, med, etc) - reload_huds() - - add_click_catcher() - - if(viewing_alternate_appearances && viewing_alternate_appearances.len) - for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) - AA.display_to(list(src)) - - update_client_colour(0) - update_morgue() +//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying +/mob/proc/update_Login_details() + //Multikey checks and logging + lastKnownIP = client.address + computer_id = client.computer_id + log_access_in(client) + create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") + create_log(MISC_LOG, "Logged in") + if(config.log_access) + for(var/mob/M in GLOB.player_list) + if(M == src) continue + if( M.key && (M.key != key) ) + var/matches + if( (M.lastKnownIP == client.address) ) + matches += "IP ([client.address])" + if( (M.computer_id == client.computer_id) ) + if(matches) matches += " and " + matches += "ID ([client.computer_id])" + if(!config.disable_cid_warn_popup) + spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") + if(matches) + if(M.client) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") + else + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") + +/mob/Login() + GLOB.player_list |= src + update_Login_details() + world.update_status() + + client.images = null //remove the images such as AIs being unable to see runes + client.screen = list() //remove hud items just in case + if(client.click_intercept) + client.click_intercept.quit() // Let's not keep any old click_intercepts + + if(!hud_used) + create_mob_hud() + if(hud_used) + hud_used.show_hud(hud_used.hud_version) + + next_move = 1 + sight |= SEE_SELF + ..() + + reset_perspective(loc) + + + if(ckey in GLOB.deadmins) + verbs += /client/proc/readmin + + //Clear ability list and update from mob. + client.verbs -= GLOB.ability_verbs + + if(abilities) + client.verbs |= abilities + + //HUD updates (antag hud, etc) + //readd this mob's HUDs (antag, med, etc) + reload_huds() + + add_click_catcher() + + if(viewing_alternate_appearances && viewing_alternate_appearances.len) + for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) + AA.display_to(list(src)) + + update_client_colour(0) + update_morgue() diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 376b8d46ea55..8bdb0d89332e 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,20 +1,21 @@ -/mob/Logout() - SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs - unset_machine() - GLOB.player_list -= src - log_access_out(src) - create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") - // `holder` is nil'd out by now, so we check the `admin_datums` array directly - //Only report this stuff if we are currently playing. - if(admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/datum/admins/temp_admin = admin_datums[ckey] - // Triggers on people with banhammer power only - no mentors tripping the alarm - if(temp_admin.rights & R_BAN) - message_admins("Admin logout: [key_name_admin(src)]") - var/list/admincounter = staff_countup(R_BAN) - if(admincounter[1] == 0) // No active admins - send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") - - ..() - update_morgue() - return 1 +/mob/Logout() + SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs + unset_machine() + GLOB.player_list -= src + log_access_out(src) + create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") + create_log(MISC_LOG, "Logged out") + // `holder` is nil'd out by now, so we check the `admin_datums` array directly + //Only report this stuff if we are currently playing. + if(GLOB.admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/datum/admins/temp_admin = GLOB.admin_datums[ckey] + // Triggers on people with banhammer power only - no mentors tripping the alarm + if(temp_admin.rights & R_BAN) + message_admins("Admin logout: [key_name_admin(src)]") + var/list/admincounter = staff_countup(R_BAN) + if(admincounter[1] == 0) // No active admins + send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") + + ..() + update_morgue() + return 1 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 609940bc2a64..d93b2723024f 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -22,6 +22,7 @@ for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) AA.viewers -= src viewing_alternate_appearances = null + logs.Cut() ..() return QDEL_HINT_HARDDEL @@ -261,7 +262,7 @@ //The list of slots by priority. equip_to_appropriate_slot() uses this list. Doesn't matter if a mob type doesn't have a slot. -var/list/slot_equipment_priority = list( \ +GLOBAL_LIST_INIT(slot_equipment_priority, list( \ slot_back,\ slot_wear_pda,\ slot_wear_id,\ @@ -279,14 +280,14 @@ var/list/slot_equipment_priority = list( \ slot_tie,\ slot_l_store,\ slot_r_store\ - ) + )) //puts the item "W" into an appropriate slot in a human's inventory //returns 0 if it cannot, 1 if successful /mob/proc/equip_to_appropriate_slot(obj/item/W) if(!istype(W)) return 0 - for(var/slot in slot_equipment_priority) + for(var/slot in GLOB.slot_equipment_priority) if(istype(W,/obj/item/storage/) && slot == slot_head) // Storage items should be put on the belt before the head continue if(equip_to_slot_if_possible(W, slot, 0, 1, 1)) //del_on_fail = 0; disable_warning = 0; redraw_mob = 1 @@ -297,7 +298,7 @@ var/list/slot_equipment_priority = list( \ /mob/proc/check_for_open_slot(obj/item/W) if(!istype(W)) return 0 var/openslot = 0 - for(var/slot in slot_equipment_priority) + for(var/slot in GLOB.slot_equipment_priority) if(W.mob_check_equip(src, slot, 1) == 1) openslot = 1 break @@ -741,7 +742,7 @@ var/list/slot_equipment_priority = list( \ set name = "Respawn" set category = "OOC" - if(!abandon_allowed) + if(!GLOB.abandon_allowed) to_chat(usr, "Respawning is disabled.") return @@ -1118,10 +1119,10 @@ var/list/slot_equipment_priority = list( \ /mob/proc/become_mouse() var/timedifference = world.time - client.time_died_as_mouse - if(client.time_died_as_mouse && timedifference <= mouse_respawn_time * 600) + if(client.time_died_as_mouse && timedifference <= GLOB.mouse_respawn_time * 600) var/timedifference_text - timedifference_text = time2text(mouse_respawn_time * 600 - timedifference,"mm:ss") - to_chat(src, "You may only spawn again as a mouse more than [mouse_respawn_time] minutes after your death. You have [timedifference_text] left.") + timedifference_text = time2text(GLOB.mouse_respawn_time * 600 - timedifference,"mm:ss") + to_chat(src, "You may only spawn again as a mouse more than [GLOB.mouse_respawn_time] minutes after your death. You have [timedifference_text] left.") return //find a viable mouse candidate @@ -1257,14 +1258,20 @@ var/list/slot_equipment_priority = list( \ return list() //must return list or IGNORE_ACCESS /mob/proc/create_attack_log(text, collapse = TRUE) - LAZYINITLIST(attack_log) - create_log_in_list(attack_log, text, collapse, last_log) + LAZYINITLIST(attack_log_old) + create_log_in_list(attack_log_old, text, collapse, last_log) last_log = world.timeofday /mob/proc/create_debug_log(text, collapse = TRUE) LAZYINITLIST(debug_log) create_log_in_list(debug_log, text, collapse, world.timeofday) +/mob/proc/create_log(log_type, what, target = null, turf/where = get_turf(src)) + LAZYINITLIST(logs[log_type]) + var/list/log_list = logs[log_type] + var/datum/log_record/record = new(log_type, src, what, target, where, world.time) + log_list.Add(record) + /proc/create_log_in_list(list/target, text, collapse = TRUE, last_log)//forgive me code gods for this shitcode proc //this proc enables lovely stuff like an attack log that looks like this: "[18:20:29-18:20:45]21x John Smith attacked Andrew Jackson with a crowbar." //That makes the logs easier to read, but because all of this is stored in strings, weird things have to be used to get it all out. diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 4091b8df9d7f..2f074aeaa9fe 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -33,8 +33,11 @@ var/computer_id = null var/lastattacker = null var/lastattacked = null - var/list/attack_log = list( ) + var/list/attack_log_old = list( ) var/list/debug_log = null + + var/list/logs = list() // Logs for each log type defined in __DEFINES/logs.dm + var/last_log = 0 var/obj/machinery/machine = null var/other_mobs = null diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 329a01907e20..f595f6b4839c 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -1,452 +1,452 @@ -#define UPGRADE_COOLDOWN 40 -#define UPGRADE_KILL_TIMER 100 - -//times it takes for a mob to eat -#define EAT_TIME_XENO 30 -#define EAT_TIME_FAT 100 - -//time it takes for a mob to be eaten (in deciseconds) (overrides mob eat time) -#define EAT_TIME_ANIMAL 30 - -/obj/item/grab - name = "grab" - flags = NOBLUDGEON | ABSTRACT | DROPDEL - var/obj/screen/grab/hud = null - var/mob/living/affecting = null - var/mob/living/assailant = null - var/state = GRAB_PASSIVE - - var/allow_upgrade = 1 - var/last_upgrade = 0 - var/last_hit_zone = 0 -// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. - var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position - - layer = 21 - plane = HUD_PLANE - item_state = "nothing" - icon = 'icons/mob/screen_gen.dmi' - w_class = WEIGHT_CLASS_BULKY - - -/obj/item/grab/New(var/mob/user, var/mob/victim) - ..() - - //Okay, first off, some fucking sanity checking. No user, or no victim, or they are not mobs, no grab. - if(!istype(user) || !istype(victim)) - return - - loc = user - assailant = user - affecting = victim - - if(affecting.anchored) - qdel(src) - return - - affecting.grabbed_by += src - - hud = new /obj/screen/grab(src) - hud.icon_state = "reinforce" - icon_state = "grabbed" - hud.name = "reinforce grab" - hud.master = src - - //check if assailant is grabbed by victim as well - if(assailant.grabbed_by) - for(var/obj/item/grab/G in assailant.grabbed_by) - if(G.assailant == affecting && G.affecting == assailant) - G.dancing = 1 - G.adjust_position() - dancing = 1 - - clean_grabbed_by(assailant, affecting) - adjust_position() - -/obj/item/grab/proc/clean_grabbed_by(var/mob/user, var/mob/victim) //Cleans up any nulls in the grabbed_by list. - if(istype(user)) - - for(var/entry in user.grabbed_by) - if(isnull(entry) || !istype(entry, /obj/item/grab)) //the isnull() here is probably redundant- but it might outperform istype, who knows. Better safe than sorry. - user.grabbed_by -= entry - - if(istype(victim)) - - for(var/entry in victim.grabbed_by) - if(isnull(entry) || !istype(entry, /obj/item/grab)) - victim.grabbed_by -= entry - - return 1 - -//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. -/obj/item/grab/proc/get_mob_if_throwable() - if(affecting && assailant.Adjacent(affecting)) - if(affecting.buckled) - return null - if(state >= GRAB_AGGRESSIVE) - return affecting - return null - -//This makes sure that the grab screen object is displayed in the correct hand. -/obj/item/grab/proc/synch() - if(affecting) - if(assailant.r_hand == src) - hud.screen_loc = ui_rhand - else - hud.screen_loc = ui_lhand - assailant.client.screen += hud - -/obj/item/grab/process() - if(!confirm()) - return //If the confirm fails, the grab is about to be deleted. That means it shouldn't continue processing. - - if(assailant.client) - if(!hud) - return //this somehow can runtime under the right circumstances - assailant.client.screen -= hud - assailant.client.screen += hud - - var/hit_zone = assailant.zone_selected - last_hit_zone = hit_zone - - if(assailant.pulling == affecting) - assailant.stop_pulling() - - if(state <= GRAB_AGGRESSIVE) - allow_upgrade = 1 - //disallow upgrading if we're grabbing more than one person - if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) - var/obj/item/grab/G = assailant.l_hand - if(G.affecting != affecting) - allow_upgrade = 0 - if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/grab))) - var/obj/item/grab/G = assailant.r_hand - if(G.affecting != affecting) - allow_upgrade = 0 - - //disallow upgrading past aggressive if we're being grabbed aggressively - for(var/obj/item/grab/G in affecting.grabbed_by) - if(G == src) continue - if(G.state >= GRAB_AGGRESSIVE) - allow_upgrade = 0 - - if(allow_upgrade) - if(state < GRAB_AGGRESSIVE) - hud.icon_state = "reinforce" - else - hud.icon_state = "reinforce1" - else - hud.icon_state = "!reinforce" - - if(state >= GRAB_AGGRESSIVE) - if(!HAS_TRAIT(assailant, TRAIT_PACIFISM)) - affecting.drop_r_hand() - affecting.drop_l_hand() - - - //var/announce = 0 - //(hit_zone != last_hit_zone) - //announce = 1 - /* if(ishuman(affecting)) - switch(hit_zone) - /*if("mouth") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s mouth!") - if(affecting.silent < 3) - affecting.silent = 3 - if("eyes") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s eyes!") - if(affecting.eye_blind < 3) - affecting.eye_blind = 3*///These are being left in the code as an example for adding new hit-zone based things. - - if(force_down) - if(affecting.loc != assailant.loc) - force_down = 0 - else - affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. - -*/ - - var/breathing_tube = affecting.get_organ_slot("breathing_tube") - - if(state >= GRAB_NECK) - affecting.Stun(5) //It will hamper your voice, being choked and all. - if(isliving(affecting) && !breathing_tube) - var/mob/living/L = affecting - L.adjustOxyLoss(1) - - if(state >= GRAB_KILL) - //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. - affecting.Stuttering(5) //It will hamper your voice, being choked and all. - affecting.Weaken(5) //Should keep you down unless you get help. - if(!breathing_tube) - affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3) - - adjust_position() - - -/obj/item/grab/attack_self(mob/user) - s_click(hud) - -//Updating pixelshift, position and direction -//Gets called on process, when the grab gets upgraded or the assailant moves -/obj/item/grab/proc/adjust_position() - if(affecting.buckled) - return - if(affecting.lying && state != GRAB_KILL) - animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) - return //KJK - /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK - affecting.setDir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up - - var/shift = 0 - var/adir = get_dir(assailant, affecting) - affecting.layer = 4 - switch(state) - if(GRAB_PASSIVE) - shift = 8 - if(dancing) //look at partner - shift = 10 - assailant.setDir(get_dir(assailant, affecting)) - if(GRAB_AGGRESSIVE) - shift = 12 - if(GRAB_NECK, GRAB_UPGRADING) - shift = -10 - adir = assailant.dir - affecting.setDir(assailant.dir) - affecting.forceMove(assailant.loc) - if(GRAB_KILL) - shift = 0 - adir = 1 - affecting.setDir(SOUTH)//face up - affecting.forceMove(assailant.loc) - - switch(adir) - if(NORTH) - animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING) - affecting.layer = 3.9 - if(SOUTH) - animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING) - if(WEST) - animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING) - if(EAST) - animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) - -/obj/item/grab/proc/s_click(obj/screen/S) - if(!affecting) - return - if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM)) - to_chat(assailant, "You don't want to risk hurting [affecting]!") - return - if(state == GRAB_UPGRADING) - return - if(assailant.next_move > world.time) - return - if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) - return - if(!assailant.canmove || assailant.lying) - qdel(src) - return - - last_upgrade = world.time - - if(state < GRAB_AGGRESSIVE) - if(!allow_upgrade) - return - //if(!affecting.lying) - assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") - /* else - assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") - force_down = 1 - affecting.Weaken(3) - step_to(assailant, affecting) - assailant.setDir(EAST) //face the victim - affecting.setDir(SOUTH) //face up //This is an example of a new feature based on the context of the location of the victim. - */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. - state = GRAB_AGGRESSIVE - icon_state = "grabbed1" - hud.icon_state = "reinforce1" - add_attack_logs(assailant, affecting, "Aggressively grabbed", ATKLOG_ALL) - else if(state < GRAB_NECK) - if(isslime(affecting)) - to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") - return - - assailant.visible_message("[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!") - state = GRAB_NECK - icon_state = "grabbed+1" - assailant.setDir(get_dir(assailant, affecting)) - add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL) - if(!iscarbon(assailant)) - affecting.LAssailant = null - else - affecting.LAssailant = assailant - hud.icon_state = "kill" - hud.name = "kill" - affecting.Stun(10) //10 ticks of ensured grab - else if(state < GRAB_UPGRADING) - assailant.visible_message("[assailant] starts to tighten [assailant.p_their()] grip on [affecting]'s neck!") - hud.icon_state = "kill1" - - state = GRAB_KILL - assailant.visible_message("[assailant] has tightened [assailant.p_their()] grip on [affecting]'s neck!") - add_attack_logs(assailant, affecting, "Strangled") - - assailant.next_move = world.time + 10 - if(!affecting.get_organ_slot("breathing_tube")) - affecting.AdjustLoseBreath(1) - affecting.setDir(WEST) - adjust_position() - -//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. -/obj/item/grab/proc/confirm() - if(!assailant || !affecting) - qdel(src) - return 0 - - if(affecting) - if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) - qdel(src) - return 0 - return 1 - - -/obj/item/grab/attack(mob/living/M, mob/living/carbon/user) - if(!affecting) - return - - if(M == affecting) - if(ishuman(M) && ishuman(assailant)) - var/mob/living/carbon/human/affected = affecting - var/mob/living/carbon/human/attacker = assailant - switch(assailant.a_intent) - if(INTENT_HELP) - /*if(force_down) - to_chat(assailant, "You no longer pin [affecting] to the ground.") - force_down = 0 - return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. - //This specific example would allow you to stop pinning people to the floor without moving away from them. - return - - if(INTENT_GRAB) - return - - if(INTENT_HARM) //This checks that the user is on harm intent. - if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. - if(affecting.lying) - return - assailant.visible_message("[assailant] thrusts [assailant.p_their()] head into [affecting]'s skull!") //A visible message for what is going on. - var/damage = 5 - var/obj/item/clothing/hat = attacker.head - if(istype(hat)) - damage += hat.force * 3 - affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) - playsound(assailant.loc, "swing_hit", 25, 1, -1) - add_attack_logs(assailant, affecting, "Headbutted") - return - - /*if(last_hit_zone == "eyes") - if(state < GRAB_NECK) - to_chat(assailant, "You require a better grab to do this.") - return - if((affected.head && affected.head.flags_cover & HEADCOVERSEYES) || \ - (affected.wear_mask && affected.wear_mask.flags_cover & MASKCOVERSEYES) || \ - (affected.glasses && affected.glasses.flags_cover & GLASSESCOVERSEYES)) - to_chat(assailant, "You're going to need to remove the eye covering first.") - return - if(!affected.internal_bodyparts_by_name["eyes"]) - to_chat(assailant, "You cannot locate any eyes on [affecting]!") - return - assailant.visible_message("[assailant] presses [assailant.p_their()] fingers into [affecting]'s eyes!") - to_chat(affecting, "You feel immense pain as digits are being pressed into your eyes!") - add_attack_logs(assailant, affecting, "Eye-fucked with their fingers") - var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes) - eyes.damage += rand(3,4) - if(eyes.damage >= eyes.min_broken_damage) - if(M.stat != 2) - to_chat(M, "You go blind!")*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. - - //This specific example would allow you to squish people's eyes with a GRAB_NECK. - - if(INTENT_DISARM) //This checks that the user is on disarm intent. - /* if(state < GRAB_AGGRESSIVE) - to_chat(assailant, "You require a better grab to do this.") - return - if(!force_down) - assailant.visible_message("[user] is forcing [affecting] to the ground!") - force_down = 1 - affecting.Weaken(3) - affecting.lying = 1 - step_to(assailant, affecting) - assailant.setDir(EAST) //face the victim - affecting.setDir(SOUTH) //face up - return - else - to_chat(assailant, "You are already pinning [affecting] to the ground.") - return*///This is an example of something being done with an agressive grab + disarm intent. - return - - - if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab - if(checkvalid(user, affecting)) //wut - var/mob/living/carbon/attacker = user - - if(affecting.buckled) - to_chat(user, "[affecting] is buckled!") - return - - user.visible_message("[user] is attempting to devour \the [affecting]!") - - if(!do_after(user, checktime(user, affecting), target = affecting)) return - - if(affecting.buckled) - to_chat(user, "[affecting] is buckled!") - return - - user.visible_message("[user] devours \the [affecting]!") - if(affecting.mind) - add_attack_logs(attacker, affecting, "Devoured") - - affecting.forceMove(user) - attacker.stomach_contents.Add(affecting) - qdel(src) - -/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab - if(isalien(attacker) && iscarbon(prey)) //Xenomorphs eating carbon mobs - return 1 - - var/mob/living/carbon/human/H = attacker - if(ishuman(H) && is_type_in_list(prey, H.dna.species.allowed_consumed_mobs)) //species eating of other mobs - return 1 - - return 0 - -/obj/item/grab/proc/checktime(var/mob/attacker, var/mob/prey) //Returns the time the attacker has to wait before they eat the prey - if(isalien(attacker)) - return EAT_TIME_XENO //xenos get a speed boost - - if(istype(prey,/mob/living/simple_animal)) //simple animals get eaten at xeno-eating-speed regardless - return EAT_TIME_ANIMAL - - return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it - -/obj/item/grab/Destroy() - if(affecting) - affecting.pixel_x = 0 - affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing - affecting.layer = initial(affecting.layer) - affecting.grabbed_by -= src - affecting = null - if(assailant) - if(assailant.client) - assailant.client.screen -= hud - assailant = null - QDEL_NULL(hud) - return ..() - - -#undef EAT_TIME_XENO -#undef EAT_TIME_FAT - -#undef EAT_TIME_ANIMAL +#define UPGRADE_COOLDOWN 40 +#define UPGRADE_KILL_TIMER 100 + +//times it takes for a mob to eat +#define EAT_TIME_XENO 30 +#define EAT_TIME_FAT 100 + +//time it takes for a mob to be eaten (in deciseconds) (overrides mob eat time) +#define EAT_TIME_ANIMAL 30 + +/obj/item/grab + name = "grab" + flags = NOBLUDGEON | ABSTRACT | DROPDEL + var/obj/screen/grab/hud = null + var/mob/living/affecting = null + var/mob/living/assailant = null + var/state = GRAB_PASSIVE + + var/allow_upgrade = 1 + var/last_upgrade = 0 + var/last_hit_zone = 0 +// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. + var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position + + layer = 21 + plane = HUD_PLANE + item_state = "nothing" + icon = 'icons/mob/screen_gen.dmi' + w_class = WEIGHT_CLASS_BULKY + + +/obj/item/grab/New(var/mob/user, var/mob/victim) + ..() + + //Okay, first off, some fucking sanity checking. No user, or no victim, or they are not mobs, no grab. + if(!istype(user) || !istype(victim)) + return + + loc = user + assailant = user + affecting = victim + + if(affecting.anchored) + qdel(src) + return + + affecting.grabbed_by += src + + hud = new /obj/screen/grab(src) + hud.icon_state = "reinforce" + icon_state = "grabbed" + hud.name = "reinforce grab" + hud.master = src + + //check if assailant is grabbed by victim as well + if(assailant.grabbed_by) + for(var/obj/item/grab/G in assailant.grabbed_by) + if(G.assailant == affecting && G.affecting == assailant) + G.dancing = 1 + G.adjust_position() + dancing = 1 + + clean_grabbed_by(assailant, affecting) + adjust_position() + +/obj/item/grab/proc/clean_grabbed_by(var/mob/user, var/mob/victim) //Cleans up any nulls in the grabbed_by list. + if(istype(user)) + + for(var/entry in user.grabbed_by) + if(isnull(entry) || !istype(entry, /obj/item/grab)) //the isnull() here is probably redundant- but it might outperform istype, who knows. Better safe than sorry. + user.grabbed_by -= entry + + if(istype(victim)) + + for(var/entry in victim.grabbed_by) + if(isnull(entry) || !istype(entry, /obj/item/grab)) + victim.grabbed_by -= entry + + return 1 + +//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. +/obj/item/grab/proc/get_mob_if_throwable() + if(affecting && assailant.Adjacent(affecting)) + if(affecting.buckled) + return null + if(state >= GRAB_AGGRESSIVE) + return affecting + return null + +//This makes sure that the grab screen object is displayed in the correct hand. +/obj/item/grab/proc/synch() + if(affecting) + if(assailant.r_hand == src) + hud.screen_loc = ui_rhand + else + hud.screen_loc = ui_lhand + assailant.client.screen += hud + +/obj/item/grab/process() + if(!confirm()) + return //If the confirm fails, the grab is about to be deleted. That means it shouldn't continue processing. + + if(assailant.client) + if(!hud) + return //this somehow can runtime under the right circumstances + assailant.client.screen -= hud + assailant.client.screen += hud + + var/hit_zone = assailant.zone_selected + last_hit_zone = hit_zone + + if(assailant.pulling == affecting) + assailant.stop_pulling() + + if(state <= GRAB_AGGRESSIVE) + allow_upgrade = 1 + //disallow upgrading if we're grabbing more than one person + if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) + var/obj/item/grab/G = assailant.l_hand + if(G.affecting != affecting) + allow_upgrade = 0 + if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/grab))) + var/obj/item/grab/G = assailant.r_hand + if(G.affecting != affecting) + allow_upgrade = 0 + + //disallow upgrading past aggressive if we're being grabbed aggressively + for(var/obj/item/grab/G in affecting.grabbed_by) + if(G == src) continue + if(G.state >= GRAB_AGGRESSIVE) + allow_upgrade = 0 + + if(allow_upgrade) + if(state < GRAB_AGGRESSIVE) + hud.icon_state = "reinforce" + else + hud.icon_state = "reinforce1" + else + hud.icon_state = "!reinforce" + + if(state >= GRAB_AGGRESSIVE) + if(!HAS_TRAIT(assailant, TRAIT_PACIFISM)) + affecting.drop_r_hand() + affecting.drop_l_hand() + + + //var/announce = 0 + //(hit_zone != last_hit_zone) + //announce = 1 + /* if(ishuman(affecting)) + switch(hit_zone) + /*if("mouth") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s mouth!") + if(affecting.silent < 3) + affecting.silent = 3 + if("eyes") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s eyes!") + if(affecting.eye_blind < 3) + affecting.eye_blind = 3*///These are being left in the code as an example for adding new hit-zone based things. + + if(force_down) + if(affecting.loc != assailant.loc) + force_down = 0 + else + affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. + +*/ + + var/breathing_tube = affecting.get_organ_slot("breathing_tube") + + if(state >= GRAB_NECK) + affecting.Stun(5) //It will hamper your voice, being choked and all. + if(isliving(affecting) && !breathing_tube) + var/mob/living/L = affecting + L.adjustOxyLoss(1) + + if(state >= GRAB_KILL) + //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. + affecting.Stuttering(5) //It will hamper your voice, being choked and all. + affecting.Weaken(5) //Should keep you down unless you get help. + if(!breathing_tube) + affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3) + + adjust_position() + + +/obj/item/grab/attack_self(mob/user) + s_click(hud) + +//Updating pixelshift, position and direction +//Gets called on process, when the grab gets upgraded or the assailant moves +/obj/item/grab/proc/adjust_position() + if(affecting.buckled) + return + if(affecting.lying && state != GRAB_KILL) + animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) + return //KJK + /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK + affecting.setDir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up + + var/shift = 0 + var/adir = get_dir(assailant, affecting) + affecting.layer = 4 + switch(state) + if(GRAB_PASSIVE) + shift = 8 + if(dancing) //look at partner + shift = 10 + assailant.setDir(get_dir(assailant, affecting)) + if(GRAB_AGGRESSIVE) + shift = 12 + if(GRAB_NECK, GRAB_UPGRADING) + shift = -10 + adir = assailant.dir + affecting.setDir(assailant.dir) + affecting.forceMove(assailant.loc) + if(GRAB_KILL) + shift = 0 + adir = 1 + affecting.setDir(SOUTH)//face up + affecting.forceMove(assailant.loc) + + switch(adir) + if(NORTH) + animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING) + affecting.layer = 3.9 + if(SOUTH) + animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING) + if(WEST) + animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING) + if(EAST) + animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) + +/obj/item/grab/proc/s_click(obj/screen/S) + if(!affecting) + return + if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM)) + to_chat(assailant, "You don't want to risk hurting [affecting]!") + return + if(state == GRAB_UPGRADING) + return + if(assailant.next_move > world.time) + return + if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) + return + if(!assailant.canmove || assailant.lying) + qdel(src) + return + + last_upgrade = world.time + + if(state < GRAB_AGGRESSIVE) + if(!allow_upgrade) + return + //if(!affecting.lying) + assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") + /* else + assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") + force_down = 1 + affecting.Weaken(3) + step_to(assailant, affecting) + assailant.setDir(EAST) //face the victim + affecting.setDir(SOUTH) //face up //This is an example of a new feature based on the context of the location of the victim. + */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. + state = GRAB_AGGRESSIVE + icon_state = "grabbed1" + hud.icon_state = "reinforce1" + add_attack_logs(assailant, affecting, "Aggressively grabbed", ATKLOG_ALL) + else if(state < GRAB_NECK) + if(isslime(affecting)) + to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") + return + + assailant.visible_message("[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!") + state = GRAB_NECK + icon_state = "grabbed+1" + assailant.setDir(get_dir(assailant, affecting)) + add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL) + if(!iscarbon(assailant)) + affecting.LAssailant = null + else + affecting.LAssailant = assailant + hud.icon_state = "kill" + hud.name = "kill" + affecting.Stun(10) //10 ticks of ensured grab + else if(state < GRAB_UPGRADING) + assailant.visible_message("[assailant] starts to tighten [assailant.p_their()] grip on [affecting]'s neck!") + hud.icon_state = "kill1" + + state = GRAB_KILL + assailant.visible_message("[assailant] has tightened [assailant.p_their()] grip on [affecting]'s neck!") + add_attack_logs(assailant, affecting, "Strangled") + + assailant.next_move = world.time + 10 + if(!affecting.get_organ_slot("breathing_tube")) + affecting.AdjustLoseBreath(1) + affecting.setDir(WEST) + adjust_position() + +//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. +/obj/item/grab/proc/confirm() + if(!assailant || !affecting) + qdel(src) + return 0 + + if(affecting) + if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) + qdel(src) + return 0 + return 1 + + +/obj/item/grab/attack(mob/living/M, mob/living/carbon/user) + if(!affecting) + return + + if(M == affecting) + if(ishuman(M) && ishuman(assailant)) + var/mob/living/carbon/human/affected = affecting + var/mob/living/carbon/human/attacker = assailant + switch(assailant.a_intent) + if(INTENT_HELP) + /*if(force_down) + to_chat(assailant, "You no longer pin [affecting] to the ground.") + force_down = 0 + return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. + //This specific example would allow you to stop pinning people to the floor without moving away from them. + return + + if(INTENT_GRAB) + return + + if(INTENT_HARM) //This checks that the user is on harm intent. + if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. + if(affecting.lying) + return + assailant.visible_message("[assailant] thrusts [assailant.p_their()] head into [affecting]'s skull!") //A visible message for what is going on. + var/damage = 5 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 3 + affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) + playsound(assailant.loc, "swing_hit", 25, 1, -1) + add_attack_logs(assailant, affecting, "Headbutted") + return + + /*if(last_hit_zone == "eyes") + if(state < GRAB_NECK) + to_chat(assailant, "You require a better grab to do this.") + return + if((affected.head && affected.head.flags_cover & HEADCOVERSEYES) || \ + (affected.wear_mask && affected.wear_mask.flags_cover & MASKCOVERSEYES) || \ + (affected.glasses && affected.glasses.flags_cover & GLASSESCOVERSEYES)) + to_chat(assailant, "You're going to need to remove the eye covering first.") + return + if(!affected.internal_bodyparts_by_name["eyes"]) + to_chat(assailant, "You cannot locate any eyes on [affecting]!") + return + assailant.visible_message("[assailant] presses [assailant.p_their()] fingers into [affecting]'s eyes!") + to_chat(affecting, "You feel immense pain as digits are being pressed into your eyes!") + add_attack_logs(assailant, affecting, "Eye-fucked with their fingers") + var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes) + eyes.damage += rand(3,4) + if(eyes.damage >= eyes.min_broken_damage) + if(M.stat != 2) + to_chat(M, "You go blind!")*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. + + //This specific example would allow you to squish people's eyes with a GRAB_NECK. + + if(INTENT_DISARM) //This checks that the user is on disarm intent. + /* if(state < GRAB_AGGRESSIVE) + to_chat(assailant, "You require a better grab to do this.") + return + if(!force_down) + assailant.visible_message("[user] is forcing [affecting] to the ground!") + force_down = 1 + affecting.Weaken(3) + affecting.lying = 1 + step_to(assailant, affecting) + assailant.setDir(EAST) //face the victim + affecting.setDir(SOUTH) //face up + return + else + to_chat(assailant, "You are already pinning [affecting] to the ground.") + return*///This is an example of something being done with an agressive grab + disarm intent. + return + + + if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab + if(checkvalid(user, affecting)) //wut + var/mob/living/carbon/attacker = user + + if(affecting.buckled) + to_chat(user, "[affecting] is buckled!") + return + + user.visible_message("[user] is attempting to devour \the [affecting]!") + + if(!do_after(user, checktime(user, affecting), target = affecting)) return + + if(affecting.buckled) + to_chat(user, "[affecting] is buckled!") + return + + user.visible_message("[user] devours \the [affecting]!") + if(affecting.mind) + add_attack_logs(attacker, affecting, "Devoured") + + affecting.forceMove(user) + attacker.stomach_contents.Add(affecting) + qdel(src) + +/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab + if(isalien(attacker) && iscarbon(prey)) //Xenomorphs eating carbon mobs + return 1 + + var/mob/living/carbon/human/H = attacker + if(ishuman(H) && is_type_in_list(prey, H.dna.species.allowed_consumed_mobs)) //species eating of other mobs + return 1 + + return 0 + +/obj/item/grab/proc/checktime(var/mob/attacker, var/mob/prey) //Returns the time the attacker has to wait before they eat the prey + if(isalien(attacker)) + return EAT_TIME_XENO //xenos get a speed boost + + if(istype(prey,/mob/living/simple_animal)) //simple animals get eaten at xeno-eating-speed regardless + return EAT_TIME_ANIMAL + + return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it + +/obj/item/grab/Destroy() + if(affecting) + affecting.pixel_x = 0 + affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing + affecting.layer = initial(affecting.layer) + affecting.grabbed_by -= src + affecting = null + if(assailant) + if(assailant.client) + assailant.client.screen -= hud + assailant = null + QDEL_NULL(hud) + return ..() + + +#undef EAT_TIME_XENO +#undef EAT_TIME_FAT + +#undef EAT_TIME_ANIMAL diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index e2e5de12e316..1e0f31ac6fe7 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -1,686 +1,686 @@ -/proc/issmall(A) - if(A && istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = A - if(H.dna.species && H.dna.species.is_small) - return 1 - return 0 - -/proc/ispet(A) - if(istype(A, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = A - if(SA.can_collar) - return 1 - return 0 - -/mob/proc/isSynthetic() - return 0 - -/mob/living/carbon/human/isSynthetic() - if(ismachine(src)) - return TRUE - return FALSE - -/mob/proc/get_screen_colour() - -/mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length. - if(!client) //No client_colour without client. If the player logs back in they'll be back through here anyway. - return - client.colour_transition(get_screen_colour(), time = time) //Get the colour matrix we're going to transition to depending on relevance (magic glasses first, eyes second). - -/mob/living/carbon/human/get_screen_colour() //Fetch the colour matrix from wherever (e.g. eyes) so it can be compared to client.color. - . = ..() - if(.) - return . - - var/obj/item/clothing/glasses/worn_glasses = glasses - var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) - if(istype(worn_glasses) && worn_glasses.color_view) //Check to see if they got those magic glasses and they're augmenting the colour of what the wearer sees. If they're not, color_view should be null. - return worn_glasses.color_view - else if(eyes) //If they're not, check to see if their eyes got one of them there colour matrices. Will be null if eyes are robotic/the mob isn't colourblind and they have no default colour matrix. - return eyes.get_colourmatrix() - -/proc/ismindshielded(A) //Checks to see if the person contains a mindshield implant, then checks that the implant is actually inside of them - for(var/obj/item/implant/mindshield/L in A) - if(L && L.implanted) - return 1 - return 0 - -/proc/ismindslave(A) //Checks to see if the person contains a mindslave implant, then checks that the implant is actually inside of them - for(var/obj/item/implant/traitor/T in A) - if(T && T.implanted) - return 1 - return 0 - -/proc/isLivingSSD(mob/M) - return istype(M) && M.player_logged && M.stat != DEAD - -/proc/isAntag(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.mind && C.mind.special_role) - return 1 - return 0 - -/proc/isNonCrewAntag(A) - if(!isAntag(A)) - return 0 - - var/mob/living/carbon/C = A - var/special_role = C.mind.special_role - var/list/crew_roles = list( - SPECIAL_ROLE_BLOB, - SPECIAL_ROLE_CULTIST, - SPECIAL_ROLE_CHANGELING, - SPECIAL_ROLE_ERT, - SPECIAL_ROLE_HEAD_REV, - SPECIAL_ROLE_REV, - SPECIAL_ROLE_SHADOWLING, - SPECIAL_ROLE_SHADOWLING_THRALL, - SPECIAL_ROLE_TRAITOR, - SPECIAL_ROLE_VAMPIRE, - SPECIAL_ROLE_VAMPIRE_THRALL - ) - if(special_role in crew_roles) - return 0 - - return 1 - -/proc/cannotPossess(A) - var/mob/dead/observer/G = A - if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) - return 1 - return 0 - - -/proc/iscuffed(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.handcuffed) - return 1 - return 0 - -/proc/hassensorlevel(A, var/level) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode >= level - return 0 - -/proc/getsensorlevel(A) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode - return SUIT_SENSOR_OFF - -/proc/offer_control(mob/M) - to_chat(M, "Control of your mob has been offered to dead players.") - log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") - var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num - message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime") - var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]" - if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes") - question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]" - var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours) - var/mob/dead/observer/theghost = null - - if(LAZYLEN(candidates)) - theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])") - M.ghostize() - M.key = theghost.key - else - to_chat(M, "There were no ghosts willing to take control.") - message_admins("No ghosts were willing to take control of [key_name_admin(M)])") - -/proc/check_zone(zone) - if(!zone) return "chest" - switch(zone) - if("eyes") - zone = "head" - if("mouth") - zone = "head" - return zone - -// Returns zone with a certain probability. -// If the probability misses, returns "chest" instead. -// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm" -// Do not use this if someone is intentionally trying to hit a specific body part. -// Use get_zone_with_miss_chance() for that. -/proc/ran_zone(zone, probability = 80) - - zone = check_zone(zone) - - if(prob(probability)) - return zone - - var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one - switch(t) - if(1) return "head" - if(2) return "chest" - if(3 to 4) return "l_arm" - if(5 to 6) return "l_hand" - if(7 to 8) return "r_arm" - if(9 to 10) return "r_hand" - if(11 to 12) return "l_leg" - if(13 to 14) return "l_foot" - if(15 to 16) return "r_leg" - if(17 to 18) return "r_foot" - - return zone - -/proc/above_neck(zone) - var/list/zones = list("head", "mouth", "eyes") - if(zones.Find(zone)) - return 1 - else - return 0 - -/proc/stars(n, pr) - if(pr == null) - pr = 25 - if(pr <= 0) - return null - else - if(pr >= 100) - return n - var/te = n - var/t = "" - n = length(n) - var/p = null - p = 1 - while(p <= n) - if((copytext(te, p, p + 1) == " " || prob(pr))) - t = text("[][]", t, copytext(te, p, p + 1)) - else - t = text("[]*", t) - p++ - return t - -/proc/stars_all(list/message_pieces, pr) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = stars(S.message, pr) - -/proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,3)==3) - if(lowertext(newletter)=="o") newletter="u" - if(lowertext(newletter)=="s") newletter="ch" - if(lowertext(newletter)=="a") newletter="ah" - if(lowertext(newletter)=="c") newletter="k" - switch(rand(1,15)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - if(2,4,6,15) newletter="[uppertext(newletter)]" - if(7) newletter+=pick(slurletters) - //if(9,10) newletter="[newletter]" - //if(11,12) newletter="[newletter]" - //if(13) newletter="[newletter]" - newphrase+="[newletter]" - counter-=1 - return newphrase - -/proc/stutter(n) - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if(prob(10)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if(prob(20)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - if(prob(5)) - n_letter = null - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - -/proc/robostutter(n) //for robutts - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/robotletter = pick("@", "!", "#", "$", "%", "&", "?") //for beep boop - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if(prob(10)) - n_letter = text("[n_letter]-[robotletter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if(prob(20)) - n_letter = text("[n_letter]-[robotletter]-[n_letter]") - else - if(prob(5)) - n_letter = robotletter - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - - -/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added - /* Turn text into complete gibberish! */ - var/returntext = "" - for(var/i = 1, i <= length(t), i++) - - var/letter = copytext(t, i, i+1) - if(prob(50)) - if(p >= 70) - letter = "" - - for(var/j = 1, j <= rand(0, 2), j++) - letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") - - returntext += letter - - return returntext - -/proc/Gibberish_all(list/message_pieces, p) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = Gibberish(S.message, p) - - -proc/muffledspeech(phrase) - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(newletter in list(" ", "!", "?", ".", ",")) - //do nothing - else if(lowertext(newletter) in list("a", "e", "i", "o", "u", "y")) - newletter = "ph" - else - newletter = "m" - newphrase+="[newletter]" - counter-=1 - return newphrase - -/proc/muffledspeech_all(list/message_pieces) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = muffledspeech(S.message) - - -/proc/shake_camera(mob/M, duration, strength=1) - if(!M || !M.client || M.shakecamera) - return - M.shakecamera = 1 - spawn(1) - - var/atom/oldeye=M.client.eye - var/aiEyeFlag = 0 - if(istype(oldeye, /mob/camera/aiEye)) - aiEyeFlag = 1 - - var/x - for(x=0; x
    ") - return - else - if(alert(src, "You sure you want to sleep for a while?", "Sleep", "Yes", "No") == "Yes") - SetSleeping(20) //Short nap - -/mob/living/verb/lay_down() - set name = "Rest" - set category = "IC" - - if(!resting) - client.move_delay = world.time + 20 - to_chat(src, "You are now resting.") - StartResting() - else if(resting) - to_chat(src, "You are now getting up.") - StopResting() - -/proc/get_multitool(mob/user as mob) - // Get tool - var/obj/item/multitool/P - if(isrobot(user) || ishuman(user)) - P = user.get_active_hand() - else if(isAI(user)) - var/mob/living/silicon/ai/AI=user - P = AI.aiMulti - - if(!istype(P)) - return null - return P - -/proc/get_both_hands(mob/living/carbon/M) - return list(M.l_hand, M.r_hand) - - -//Direct dead say used both by emote and say -//It is somewhat messy. I don't know what to do. -//I know you can't see the change, but I rewrote the name code. It is significantly less messy now -/proc/say_dead_direct(var/message, var/mob/subject = null) - var/name - var/keyname - if(subject && subject.client) - var/client/C = subject.client - keyname = (C.holder && C.holder.fakekey) ? C.holder.fakekey : C.key - if(C.mob) //Most of the time this is the dead/observer mob; we can totally use him if there is no better name - var/mindname - var/realname = C.mob.real_name - if(C.mob.mind) - mindname = C.mob.mind.name - if(C.mob.mind.original && C.mob.mind.original.real_name) - realname = C.mob.mind.original.real_name - if(mindname && mindname != realname) - name = "[realname] died as [mindname]" - else - name = realname - - for(var/mob/M in GLOB.player_list) - if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD)) - var/follow - var/lname - if(subject) - if(subject != M) - follow = "([ghost_follow_link(subject, ghost=M)]) " - if(M.stat != DEAD && check_rights(R_ADMIN|R_MOD,0,M)) - follow = "([admin_jump_link(subject)]) " - var/mob/dead/observer/DM - if(istype(subject, /mob/dead/observer)) - DM = subject - if(check_rights(R_ADMIN|R_MOD,0,M)) // What admins see - lname = "[keyname][(DM && DM.client && DM.client.prefs.ghost_anonsay) ? "*" : (DM ? "" : "^")] ([name])" - else - if(DM && DM.client && DM.client.prefs.ghost_anonsay) // If the person is actually observer they have the option to be anonymous - lname = "Ghost of [name]" - else if(DM) // Non-anons - lname = "[keyname] ([name])" - else // Everyone else (dead people who didn't ghost yet, etc.) - lname = name - lname = "[lname] " - to_chat(M, "[lname][follow][message]") - -/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, title = null, atom/source = null, image/alert_overlay = null, flashwindow = TRUE, var/action = NOTIFY_JUMP) //Easy notification of ghosts. - for(var/mob/dead/observer/O in GLOB.player_list) - if(O.client) - to_chat(O, "[message][(enter_link) ? " [enter_link]" : ""]") - if(ghost_sound) - O << sound(ghost_sound) - if(flashwindow) - window_flash(O.client) - if(source) - var/obj/screen/alert/notify_action/A = O.throw_alert("\ref[source]_notify_action", /obj/screen/alert/notify_action) - if(A) - if(O.client.prefs && O.client.prefs.UI_style) - A.icon = ui_style2icon(O.client.prefs.UI_style) - if(title) - A.name = title - A.desc = message - A.action = action - A.target = source - if(!alert_overlay) - var/old_layer = source.layer - var/old_plane = source.plane - source.layer = FLOAT_LAYER - source.plane = FLOAT_PLANE - A.overlays += source - source.layer = old_layer - source.plane = old_plane - else - alert_overlay.layer = FLOAT_LAYER - alert_overlay.plane = FLOAT_PLANE - A.overlays += alert_overlay - -/mob/proc/switch_to_camera(var/obj/machinery/camera/C) - if(!C.can_use() || stat || (get_dist(C, src) > 1 || machine != src || !has_vision() || !canmove)) - return 0 - check_eye(src) - return 1 - -/mob/proc/rename_character(oldname, newname) - if(!newname) - return 0 - real_name = newname - name = newname - if(mind) - mind.name = newname - if(dna) - dna.real_name = real_name - - if(oldname) - //update the datacore records! This is goig to be a bit costly. - for(var/list/L in list(data_core.general,data_core.medical,data_core.security,data_core.locked)) - for(var/datum/data/record/R in L) - if(R.fields["name"] == oldname) - R.fields["name"] = newname - break - - //update our pda and id if we have them on our person - var/list/searching = GetAllContents(searchDepth = 3) - var/search_id = 1 - var/search_pda = 1 - - for(var/A in searching) - if( search_id && istype(A,/obj/item/card/id) ) - var/obj/item/card/id/ID = A - if(ID.registered_name == oldname) - ID.registered_name = newname - ID.name = "[newname]'s ID Card ([ID.assignment])" - ID.RebuildHTML() - if(!search_pda) break - search_id = 0 - - else if( search_pda && istype(A,/obj/item/pda) ) - var/obj/item/pda/PDA = A - if(PDA.owner == oldname) - PDA.owner = newname - PDA.name = "PDA-[newname] ([PDA.ownjob])" - if(!search_id) break - search_pda = 0 - - //Fixes renames not being reflected in objective text - var/length - var/pos - for(var/datum/objective/objective in GLOB.all_objectives) - if(!mind || objective.target != mind) - continue - length = length(oldname) - pos = findtextEx(objective.explanation_text, oldname) - objective.explanation_text = copytext(objective.explanation_text, 1, pos)+newname+copytext(objective.explanation_text, pos+length) - return 1 - -/mob/proc/rename_self(var/role, var/allow_numbers = FALSE, var/force = FALSE) - spawn(0) - var/oldname = real_name - - var/time_passed = world.time - var/newname - - for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name. - if(force) - newname = clean_input("Pick a new name.", "Name Change", oldname, src) - else - newname = clean_input("You are a [role]. Would you like to change your name to something else? (You have 3 minutes to select a new name.)", "Name Change", oldname, src) - if(((world.time - time_passed) > 1800) && !force) - alert(src, "Unfortunately, more than 3 minutes have passed for selecting your name. If you are a robot, use the Namepick verb; otherwise, adminhelp.", "Name Change") - return //took too long - newname = reject_bad_name(newname,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters. - - for(var/mob/living/M in GLOB.player_list) - if(M == src) - continue - if(!newname || M.real_name == newname) - newname = null - break - if(newname) - break //That's a suitable name! - to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.") - - if(!newname) //we'll stick with the oldname then - return - - rename_character(oldname, newname) - -/proc/cultslur(n) // Inflicted on victims of a stun talisman - var/phrase = html_decode(n) - var/leng = length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,2)==2) - if(lowertext(newletter)=="o") - newletter="u" - if(lowertext(newletter)=="t") - newletter="ch" - if(lowertext(newletter)=="a") - newletter="ah" - if(lowertext(newletter)=="u") - newletter="oo" - if(lowertext(newletter)=="c") - newletter=" NAR " - if(lowertext(newletter)=="s") - newletter=" SIE " - if(rand(1,4)==4) - if(newletter==" ") - newletter=" no hope... " - if(newletter=="H") - newletter=" IT COMES... " - - switch(rand(1,15)) - if(1) - newletter="'" - if(2) - newletter+="agn" - if(3) - newletter="fth" - if(4) - newletter="nglu" - if(5) - newletter="glor" - newphrase+="[newletter]";counter-=1 - return newphrase - -/mob/proc/get_preference(toggleflag) - if(!client) - return FALSE - if(!client.prefs) - log_runtime(EXCEPTION("Mob '[src]', ckey '[ckey]' is missing a prefs datum on the client!")) - return FALSE - // Cast to 1/0 - return !!(client.prefs.toggles & toggleflag) - -// Used to make sure that a player has a valid job preference setup, used to knock players out of eligibility for anything if their prefs don't make sense. -// A "valid job preference setup" in this situation means at least having one job set to low, or not having "return to lobby" enabled -// Prevents "antag rolling" by setting antag prefs on, all jobs to never, and "return to lobby if preferences not availible" -// Doing so would previously allow you to roll for antag, then send you back to lobby if you didn't get an antag role -// This also does some admin notification and logging as well -/mob/proc/has_valid_preferences() - if(!client) - return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe. - if(client.prefs.alternate_option != RETURN_TO_LOBBY) - return TRUE - // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. - var/has_antags = FALSE - if(client.prefs.be_special.len > 0) - has_antags = TRUE - if(!client.prefs.check_any_job()) - to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.") - if(has_antags) - log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.") - message_admins("[src.ckey] just got booted back to lobby with no jobs enabled, but antag rolling enabled. Likely antag rolling abuse.") - return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well - return TRUE - -#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) +/proc/issmall(A) + if(A && istype(A, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = A + if(H.dna.species && H.dna.species.is_small) + return 1 + return 0 + +/proc/ispet(A) + if(istype(A, /mob/living/simple_animal)) + var/mob/living/simple_animal/SA = A + if(SA.can_collar) + return 1 + return 0 + +/mob/proc/isSynthetic() + return 0 + +/mob/living/carbon/human/isSynthetic() + if(ismachine(src)) + return TRUE + return FALSE + +/mob/proc/get_screen_colour() + +/mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length. + if(!client) //No client_colour without client. If the player logs back in they'll be back through here anyway. + return + client.colour_transition(get_screen_colour(), time = time) //Get the colour matrix we're going to transition to depending on relevance (magic glasses first, eyes second). + +/mob/living/carbon/human/get_screen_colour() //Fetch the colour matrix from wherever (e.g. eyes) so it can be compared to client.color. + . = ..() + if(.) + return . + + var/obj/item/clothing/glasses/worn_glasses = glasses + var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) + if(istype(worn_glasses) && worn_glasses.color_view) //Check to see if they got those magic glasses and they're augmenting the colour of what the wearer sees. If they're not, color_view should be null. + return worn_glasses.color_view + else if(eyes) //If they're not, check to see if their eyes got one of them there colour matrices. Will be null if eyes are robotic/the mob isn't colourblind and they have no default colour matrix. + return eyes.get_colourmatrix() + +/proc/ismindshielded(A) //Checks to see if the person contains a mindshield implant, then checks that the implant is actually inside of them + for(var/obj/item/implant/mindshield/L in A) + if(L && L.implanted) + return 1 + return 0 + +/proc/ismindslave(A) //Checks to see if the person contains a mindslave implant, then checks that the implant is actually inside of them + for(var/obj/item/implant/traitor/T in A) + if(T && T.implanted) + return 1 + return 0 + +/proc/isLivingSSD(mob/M) + return istype(M) && M.player_logged && M.stat != DEAD + +/proc/isAntag(A) + if(istype(A, /mob/living/carbon)) + var/mob/living/carbon/C = A + if(C.mind && C.mind.special_role) + return 1 + return 0 + +/proc/isNonCrewAntag(A) + if(!isAntag(A)) + return 0 + + var/mob/living/carbon/C = A + var/special_role = C.mind.special_role + var/list/crew_roles = list( + SPECIAL_ROLE_BLOB, + SPECIAL_ROLE_CULTIST, + SPECIAL_ROLE_CHANGELING, + SPECIAL_ROLE_ERT, + SPECIAL_ROLE_HEAD_REV, + SPECIAL_ROLE_REV, + SPECIAL_ROLE_SHADOWLING, + SPECIAL_ROLE_SHADOWLING_THRALL, + SPECIAL_ROLE_TRAITOR, + SPECIAL_ROLE_VAMPIRE, + SPECIAL_ROLE_VAMPIRE_THRALL + ) + if(special_role in crew_roles) + return 0 + + return 1 + +/proc/cannotPossess(A) + var/mob/dead/observer/G = A + if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) + return 1 + return 0 + + +/proc/iscuffed(A) + if(istype(A, /mob/living/carbon)) + var/mob/living/carbon/C = A + if(C.handcuffed) + return 1 + return 0 + +/proc/hassensorlevel(A, var/level) + var/mob/living/carbon/human/H = A + if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + return U.sensor_mode >= level + return 0 + +/proc/getsensorlevel(A) + var/mob/living/carbon/human/H = A + if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + return U.sensor_mode + return SUIT_SENSOR_OFF + +/proc/offer_control(mob/M) + to_chat(M, "Control of your mob has been offered to dead players.") + log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") + var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num + message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime") + var/question = "Do you want to play as [M.real_name ? M.real_name : M.name][M.job ? " ([M.job])" : ""]" + if(alert("Do you want to show the antag status?","Show antag status","Yes","No") == "Yes") + question += ", [M.mind?.special_role ? M.mind?.special_role : "No special role"]" + var/list/mob/dead/observer/candidates = pollCandidates("[question]?", poll_time = 100, min_hours = minhours) + var/mob/dead/observer/theghost = null + + if(LAZYLEN(candidates)) + theghost = pick(candidates) + to_chat(M, "Your mob has been taken over by a ghost!") + message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])") + M.ghostize() + M.key = theghost.key + else + to_chat(M, "There were no ghosts willing to take control.") + message_admins("No ghosts were willing to take control of [key_name_admin(M)])") + +/proc/check_zone(zone) + if(!zone) return "chest" + switch(zone) + if("eyes") + zone = "head" + if("mouth") + zone = "head" + return zone + +// Returns zone with a certain probability. +// If the probability misses, returns "chest" instead. +// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm" +// Do not use this if someone is intentionally trying to hit a specific body part. +// Use get_zone_with_miss_chance() for that. +/proc/ran_zone(zone, probability = 80) + + zone = check_zone(zone) + + if(prob(probability)) + return zone + + var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one + switch(t) + if(1) return "head" + if(2) return "chest" + if(3 to 4) return "l_arm" + if(5 to 6) return "l_hand" + if(7 to 8) return "r_arm" + if(9 to 10) return "r_hand" + if(11 to 12) return "l_leg" + if(13 to 14) return "l_foot" + if(15 to 16) return "r_leg" + if(17 to 18) return "r_foot" + + return zone + +/proc/above_neck(zone) + var/list/zones = list("head", "mouth", "eyes") + if(zones.Find(zone)) + return 1 + else + return 0 + +/proc/stars(n, pr) + if(pr == null) + pr = 25 + if(pr <= 0) + return null + else + if(pr >= 100) + return n + var/te = n + var/t = "" + n = length(n) + var/p = null + p = 1 + while(p <= n) + if((copytext(te, p, p + 1) == " " || prob(pr))) + t = text("[][]", t, copytext(te, p, p + 1)) + else + t = text("[]*", t) + p++ + return t + +/proc/stars_all(list/message_pieces, pr) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = stars(S.message, pr) + +/proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters + phrase = html_decode(phrase) + var/leng=length(phrase) + var/counter=length(phrase) + var/newphrase="" + var/newletter="" + while(counter>=1) + newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) + if(rand(1,3)==3) + if(lowertext(newletter)=="o") newletter="u" + if(lowertext(newletter)=="s") newletter="ch" + if(lowertext(newletter)=="a") newletter="ah" + if(lowertext(newletter)=="c") newletter="k" + switch(rand(1,15)) + if(1,3,5,8) newletter="[lowertext(newletter)]" + if(2,4,6,15) newletter="[uppertext(newletter)]" + if(7) newletter+=pick(slurletters) + //if(9,10) newletter="[newletter]" + //if(11,12) newletter="[newletter]" + //if(13) newletter="[newletter]" + newphrase+="[newletter]" + counter-=1 + return newphrase + +/proc/stutter(n) + var/te = html_decode(n) + var/t = ""//placed before the message. Not really sure what it's for. + n = length(n)//length of the entire word + var/p = null + p = 1//1 is the start of any word + while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. + var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. + if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) + if(prob(10)) + n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. + else + if(prob(20)) + n_letter = text("[n_letter]-[n_letter]-[n_letter]") + else + if(prob(5)) + n_letter = null + else + n_letter = text("[n_letter]-[n_letter]") + t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. + p++//for each letter p is increased to find where the next letter will be. + return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + +/proc/robostutter(n) //for robutts + var/te = html_decode(n) + var/t = ""//placed before the message. Not really sure what it's for. + n = length(n)//length of the entire word + var/p = null + p = 1//1 is the start of any word + while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. + var/robotletter = pick("@", "!", "#", "$", "%", "&", "?") //for beep boop + var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. + if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) + if(prob(10)) + n_letter = text("[n_letter]-[robotletter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. + else + if(prob(20)) + n_letter = text("[n_letter]-[robotletter]-[n_letter]") + else + if(prob(5)) + n_letter = robotletter + else + n_letter = text("[n_letter]-[n_letter]") + t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. + p++//for each letter p is increased to find where the next letter will be. + return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) + + +/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added + /* Turn text into complete gibberish! */ + var/returntext = "" + for(var/i = 1, i <= length(t), i++) + + var/letter = copytext(t, i, i+1) + if(prob(50)) + if(p >= 70) + letter = "" + + for(var/j = 1, j <= rand(0, 2), j++) + letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") + + returntext += letter + + return returntext + +/proc/Gibberish_all(list/message_pieces, p) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = Gibberish(S.message, p) + + +proc/muffledspeech(phrase) + phrase = html_decode(phrase) + var/leng=length(phrase) + var/counter=length(phrase) + var/newphrase="" + var/newletter="" + while(counter>=1) + newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) + if(newletter in list(" ", "!", "?", ".", ",")) + //do nothing + else if(lowertext(newletter) in list("a", "e", "i", "o", "u", "y")) + newletter = "ph" + else + newletter = "m" + newphrase+="[newletter]" + counter-=1 + return newphrase + +/proc/muffledspeech_all(list/message_pieces) + for(var/datum/multilingual_say_piece/S in message_pieces) + S.message = muffledspeech(S.message) + + +/proc/shake_camera(mob/M, duration, strength=1) + if(!M || !M.client || M.shakecamera) + return + M.shakecamera = 1 + spawn(1) + + var/atom/oldeye=M.client.eye + var/aiEyeFlag = 0 + if(istype(oldeye, /mob/camera/aiEye)) + aiEyeFlag = 1 + + var/x + for(x=0; x
    ") + return + else + if(alert(src, "You sure you want to sleep for a while?", "Sleep", "Yes", "No") == "Yes") + SetSleeping(20) //Short nap + +/mob/living/verb/lay_down() + set name = "Rest" + set category = "IC" + + if(!resting) + client.move_delay = world.time + 20 + to_chat(src, "You are now resting.") + StartResting() + else if(resting) + to_chat(src, "You are now getting up.") + StopResting() + +/proc/get_multitool(mob/user as mob) + // Get tool + var/obj/item/multitool/P + if(isrobot(user) || ishuman(user)) + P = user.get_active_hand() + else if(isAI(user)) + var/mob/living/silicon/ai/AI=user + P = AI.aiMulti + + if(!istype(P)) + return null + return P + +/proc/get_both_hands(mob/living/carbon/M) + return list(M.l_hand, M.r_hand) + + +//Direct dead say used both by emote and say +//It is somewhat messy. I don't know what to do. +//I know you can't see the change, but I rewrote the name code. It is significantly less messy now +/proc/say_dead_direct(var/message, var/mob/subject = null) + var/name + var/keyname + if(subject && subject.client) + var/client/C = subject.client + keyname = (C.holder && C.holder.fakekey) ? C.holder.fakekey : C.key + if(C.mob) //Most of the time this is the dead/observer mob; we can totally use him if there is no better name + var/mindname + var/realname = C.mob.real_name + if(C.mob.mind) + mindname = C.mob.mind.name + if(C.mob.mind.original && C.mob.mind.original.real_name) + realname = C.mob.mind.original.real_name + if(mindname && mindname != realname) + name = "[realname] died as [mindname]" + else + name = realname + + for(var/mob/M in GLOB.player_list) + if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || check_rights(R_ADMIN|R_MOD,0,M)) && M.get_preference(CHAT_DEAD)) + var/follow + var/lname + if(subject) + if(subject != M) + follow = "([ghost_follow_link(subject, ghost=M)]) " + if(M.stat != DEAD && check_rights(R_ADMIN|R_MOD,0,M)) + follow = "([admin_jump_link(subject)]) " + var/mob/dead/observer/DM + if(istype(subject, /mob/dead/observer)) + DM = subject + if(check_rights(R_ADMIN|R_MOD,0,M)) // What admins see + lname = "[keyname][(DM && DM.client && DM.client.prefs.ghost_anonsay) ? "*" : (DM ? "" : "^")] ([name])" + else + if(DM && DM.client && DM.client.prefs.ghost_anonsay) // If the person is actually observer they have the option to be anonymous + lname = "Ghost of [name]" + else if(DM) // Non-anons + lname = "[keyname] ([name])" + else // Everyone else (dead people who didn't ghost yet, etc.) + lname = name + lname = "[lname] " + to_chat(M, "[lname][follow][message]") + +/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, title = null, atom/source = null, image/alert_overlay = null, flashwindow = TRUE, var/action = NOTIFY_JUMP) //Easy notification of ghosts. + for(var/mob/dead/observer/O in GLOB.player_list) + if(O.client) + to_chat(O, "[message][(enter_link) ? " [enter_link]" : ""]") + if(ghost_sound) + O << sound(ghost_sound) + if(flashwindow) + window_flash(O.client) + if(source) + var/obj/screen/alert/notify_action/A = O.throw_alert("\ref[source]_notify_action", /obj/screen/alert/notify_action) + if(A) + if(O.client.prefs && O.client.prefs.UI_style) + A.icon = ui_style2icon(O.client.prefs.UI_style) + if(title) + A.name = title + A.desc = message + A.action = action + A.target = source + if(!alert_overlay) + var/old_layer = source.layer + var/old_plane = source.plane + source.layer = FLOAT_LAYER + source.plane = FLOAT_PLANE + A.overlays += source + source.layer = old_layer + source.plane = old_plane + else + alert_overlay.layer = FLOAT_LAYER + alert_overlay.plane = FLOAT_PLANE + A.overlays += alert_overlay + +/mob/proc/switch_to_camera(var/obj/machinery/camera/C) + if(!C.can_use() || stat || (get_dist(C, src) > 1 || machine != src || !has_vision() || !canmove)) + return 0 + check_eye(src) + return 1 + +/mob/proc/rename_character(oldname, newname) + if(!newname) + return 0 + real_name = newname + name = newname + if(mind) + mind.name = newname + if(dna) + dna.real_name = real_name + + if(oldname) + //update the datacore records! This is goig to be a bit costly. + for(var/list/L in list(GLOB.data_core.general, GLOB.data_core.medical, GLOB.data_core.security, GLOB.data_core.locked)) + for(var/datum/data/record/R in L) + if(R.fields["name"] == oldname) + R.fields["name"] = newname + break + + //update our pda and id if we have them on our person + var/list/searching = GetAllContents(searchDepth = 3) + var/search_id = 1 + var/search_pda = 1 + + for(var/A in searching) + if( search_id && istype(A,/obj/item/card/id) ) + var/obj/item/card/id/ID = A + if(ID.registered_name == oldname) + ID.registered_name = newname + ID.name = "[newname]'s ID Card ([ID.assignment])" + ID.RebuildHTML() + if(!search_pda) break + search_id = 0 + + else if( search_pda && istype(A,/obj/item/pda) ) + var/obj/item/pda/PDA = A + if(PDA.owner == oldname) + PDA.owner = newname + PDA.name = "PDA-[newname] ([PDA.ownjob])" + if(!search_id) break + search_pda = 0 + + //Fixes renames not being reflected in objective text + var/length + var/pos + for(var/datum/objective/objective in GLOB.all_objectives) + if(!mind || objective.target != mind) + continue + length = length(oldname) + pos = findtextEx(objective.explanation_text, oldname) + objective.explanation_text = copytext(objective.explanation_text, 1, pos)+newname+copytext(objective.explanation_text, pos+length) + return 1 + +/mob/proc/rename_self(var/role, var/allow_numbers = FALSE, var/force = FALSE) + spawn(0) + var/oldname = real_name + + var/time_passed = world.time + var/newname + + for(var/i=1,i<=3,i++) //we get 3 attempts to pick a suitable name. + if(force) + newname = clean_input("Pick a new name.", "Name Change", oldname, src) + else + newname = clean_input("You are a [role]. Would you like to change your name to something else? (You have 3 minutes to select a new name.)", "Name Change", oldname, src) + if(((world.time - time_passed) > 1800) && !force) + alert(src, "Unfortunately, more than 3 minutes have passed for selecting your name. If you are a robot, use the Namepick verb; otherwise, adminhelp.", "Name Change") + return //took too long + newname = reject_bad_name(newname,allow_numbers) //returns null if the name doesn't meet some basic requirements. Tidies up a few other things like bad-characters. + + for(var/mob/living/M in GLOB.player_list) + if(M == src) + continue + if(!newname || M.real_name == newname) + newname = null + break + if(newname) + break //That's a suitable name! + to_chat(src, "Sorry, that [role]-name wasn't appropriate, please try another. It's possibly too long/short, has bad characters or is already taken.") + + if(!newname) //we'll stick with the oldname then + return + + rename_character(oldname, newname) + +/proc/cultslur(n) // Inflicted on victims of a stun talisman + var/phrase = html_decode(n) + var/leng = length(phrase) + var/counter=length(phrase) + var/newphrase="" + var/newletter="" + while(counter>=1) + newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) + if(rand(1,2)==2) + if(lowertext(newletter)=="o") + newletter="u" + if(lowertext(newletter)=="t") + newletter="ch" + if(lowertext(newletter)=="a") + newletter="ah" + if(lowertext(newletter)=="u") + newletter="oo" + if(lowertext(newletter)=="c") + newletter=" NAR " + if(lowertext(newletter)=="s") + newletter=" SIE " + if(rand(1,4)==4) + if(newletter==" ") + newletter=" no hope... " + if(newletter=="H") + newletter=" IT COMES... " + + switch(rand(1,15)) + if(1) + newletter="'" + if(2) + newletter+="agn" + if(3) + newletter="fth" + if(4) + newletter="nglu" + if(5) + newletter="glor" + newphrase+="[newletter]";counter-=1 + return newphrase + +/mob/proc/get_preference(toggleflag) + if(!client) + return FALSE + if(!client.prefs) + log_runtime(EXCEPTION("Mob '[src]', ckey '[ckey]' is missing a prefs datum on the client!")) + return FALSE + // Cast to 1/0 + return !!(client.prefs.toggles & toggleflag) + +// Used to make sure that a player has a valid job preference setup, used to knock players out of eligibility for anything if their prefs don't make sense. +// A "valid job preference setup" in this situation means at least having one job set to low, or not having "return to lobby" enabled +// Prevents "antag rolling" by setting antag prefs on, all jobs to never, and "return to lobby if preferences not availible" +// Doing so would previously allow you to roll for antag, then send you back to lobby if you didn't get an antag role +// This also does some admin notification and logging as well +/mob/proc/has_valid_preferences() + if(!client) + return FALSE //Not sure how this would get run without the mob having a client, but let's just be safe. + if(client.prefs.alternate_option != RETURN_TO_LOBBY) + return TRUE + // If they have antags enabled, they're potentially doing this on purpose instead of by accident. Notify admins if so. + var/has_antags = FALSE + if(client.prefs.be_special.len > 0) + has_antags = TRUE + if(!client.prefs.check_any_job()) + to_chat(src, "You have no jobs enabled, along with return to lobby if job is unavailable. This makes you ineligible for any round start role, please update your job preferences.") + if(has_antags) + log_admin("[src.ckey] just got booted back to lobby with no jobs, but antags enabled.") + message_admins("[src.ckey] just got booted back to lobby with no jobs enabled, but antag rolling enabled. Likely antag rolling abuse.") + return FALSE //This is the only case someone should actually be completely blocked from antag rolling as well + return TRUE + +#define isterrorspider(A) (istype((A), /mob/living/simple_animal/hostile/poison/terror_spider)) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 94f1e2048db9..8fb7568cefd4 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -1,509 +1,509 @@ -/mob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover, /obj/item/projectile)) - return (!density || lying) - if(mover.throwing) - return (!density || lying || (mover.throwing.thrower == src)) - if(mover.checkpass(PASSMOB)) - return 1 - if(buckled == mover) - return TRUE - if(ismob(mover)) - var/mob/moving_mob = mover - if((other_mobs && moving_mob.other_mobs)) - return TRUE - if(mover in buckled_mobs) - return TRUE - return (!mover.density || !density || lying) - - -/client/verb/toggle_throw_mode() - set hidden = 1 - if(iscarbon(mob)) - var/mob/living/carbon/C = mob - C.toggle_throw_mode() - else - to_chat(usr, "This mob type cannot throw items.") - - -/client/verb/drop_item() - set hidden = 1 - if(!isrobot(mob)) - mob.drop_item_v() - return - - -/* /client/Center() - /* No 3D movement in 2D spessman game. dir 16 is Z Up - if(isobj(mob.loc)) - var/obj/O = mob.loc - if(mob.canmove) - return O.relaymove(mob, 16) - */ - return - */ - - -/client/proc/Move_object(direct) - if(mob && mob.control_object) - if(mob.control_object.density) - step(mob.control_object, direct) - if(!mob.control_object) - return - mob.control_object.setDir(direct) - else - mob.control_object.forceMove(get_step(mob.control_object, direct)) - return - -#define MOVEMENT_DELAY_BUFFER 0.75 -#define MOVEMENT_DELAY_BUFFER_DELTA 1.25 -/client/Move(n, direct) - if(world.time < move_delay) - return - else - next_move_dir_add = 0 - next_move_dir_sub = 0 - var/old_move_delay = move_delay - move_delay = world.time + world.tick_lag //this is here because Move() can now be called multiple times per tick - if(!mob || !mob.loc) - return 0 - - if(!n || !direct) // why did we never check this before? - return FALSE - - if(mob.notransform) - return 0 //This is sota the goto stop mobs from moving var - - if(mob.control_object) - return Move_object(direct) - - if(!isliving(mob)) - return mob.Move(n, direct) - - if(mob.stat == DEAD) - mob.ghostize() - return 0 - - if(moving) - return 0 - - if(isliving(mob)) - var/mob/living/L = mob - if(L.incorporeal_move)//Move though walls - Process_Incorpmove(direct) - return - - if(mob.remote_control) //we're controlling something, our movement is relayed to it - return mob.remote_control.relaymove(mob, direct) - - if(isAI(mob)) - if(istype(mob.loc, /obj/item/aicard)) - var/obj/O = mob.loc - return O.relaymove(mob, direct) // aicards have special relaymove stuff - return AIMove(n, direct, mob) - - - if(Process_Grab()) - return - - if(mob.buckled) //if we're buckled to something, tell it we moved. - return mob.buckled.relaymove(mob, direct) - - if(!mob.canmove) - return - - if(!mob.lastarea) - mob.lastarea = get_area(mob.loc) - - if(isobj(mob.loc) || ismob(mob.loc)) //Inside an object, tell it we moved - var/atom/O = mob.loc - return O.relaymove(mob, direct) - - if(!mob.Process_Spacemove(direct)) - return 0 - - if(mob.restrained()) // Why being pulled while cuffed prevents you from moving - for(var/mob/M in orange(1, mob)) - if(M.pulling == mob) - if(!M.incapacitated() && mob.Adjacent(M)) - to_chat(src, "You're restrained! You can't move!") - move_delay = world.time + 10 - return 0 - else - M.stop_pulling() - - - //We are now going to move - moving = 1 - var/delay = mob.movement_delay() - if(old_move_delay + (delay * MOVEMENT_DELAY_BUFFER_DELTA) + MOVEMENT_DELAY_BUFFER > world.time) - move_delay = old_move_delay - else - move_delay = world.time - mob.last_movement = world.time - - if(locate(/obj/item/grab, mob)) - move_delay = max(move_delay, world.time + 7) - var/list/L = mob.ret_grab() - if(istype(L, /list)) - if(L.len == 2) - L -= mob - var/mob/M = L[1] - if(M) - if((get_dist(mob, M) <= 1 || M.loc == mob.loc)) - var/turf/prev_loc = mob.loc - . = ..() - if(M && isturf(M.loc)) // Mob may get deleted during parent call - var/diag = get_dir(mob, M) - if((diag - 1) & diag) - else - diag = null - if((get_dist(mob, M) > 1 || diag)) - step(M, get_dir(M.loc, prev_loc)) - else - for(var/mob/M in L) - M.other_mobs = 1 - if(mob != M) - M.animate_movement = 3 - for(var/mob/M in L) - spawn(0) - step(M, direct) - return - spawn(1) - M.other_mobs = null - M.animate_movement = 2 - return - - else if(mob.confused) - var/newdir = 0 - if(mob.confused > 40) - newdir = pick(alldirs) - else if(prob(mob.confused * 1.5)) - newdir = angle2dir(dir2angle(direct) + pick(90, -90)) - else if(prob(mob.confused * 3)) - newdir = angle2dir(dir2angle(direct) + pick(45, -45)) - if(newdir) - direct = newdir - n = get_step(mob, direct) - - . = ..() - mob.setDir(direct) - - for(var/obj/item/grab/G in mob) - if(G.state == GRAB_NECK) - mob.setDir(angle2dir((dir2angle(direct) + 202.5) % 365)) - G.adjust_position() - for(var/obj/item/grab/G in mob.grabbed_by) - G.adjust_position() - if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully - delay = mob.movement_delay() * 2 - move_delay += delay - moving = 0 - if(mob && .) - if(mob.throwing) - mob.throwing.finalize(FALSE) - - for(var/obj/O in mob) - O.on_mob_move(direct, mob) - - - - - -///Process_Grab() -///Called by client/Move() -///Checks to see if you are being grabbed and if so attemps to break it -/client/proc/Process_Grab() - if(mob.grabbed_by.len) - var/list/grabbing = list() - - if(istype(mob.l_hand, /obj/item/grab)) - var/obj/item/grab/G = mob.l_hand - grabbing += G.affecting - - if(istype(mob.r_hand, /obj/item/grab)) - var/obj/item/grab/G = mob.r_hand - grabbing += G.affecting - - for(var/X in mob.grabbed_by) - var/obj/item/grab/G = X - switch(G.state) - - if(GRAB_PASSIVE) - if(!grabbing.Find(G.assailant)) //moving always breaks a passive grab unless we are also grabbing our grabber. - qdel(G) - - if(GRAB_AGGRESSIVE) - move_delay = world.time + 10 - if(!prob(25)) - return 1 - mob.visible_message("[mob] has broken free of [G.assailant]'s grip!") - qdel(G) - - if(GRAB_NECK) - move_delay = world.time + 10 - if(!prob(5)) - return 1 - mob.visible_message("[mob] has broken free of [G.assailant]'s headlock!") - qdel(G) - return 0 - - -///Process_Incorpmove -///Called by client/Move() -///Allows mobs to run though walls -/client/proc/Process_Incorpmove(direct) - var/turf/mobloc = get_turf(mob) - if(!isliving(mob)) - return - var/mob/living/L = mob - switch(L.incorporeal_move) - if(1) - L.forceMove(get_step(L, direct)) - L.dir = direct - if(2) - if(prob(50)) - var/locx - var/locy - switch(direct) - if(NORTH) - locx = mobloc.x - locy = (mobloc.y+2) - if(locy>world.maxy) - return - if(SOUTH) - locx = mobloc.x - locy = (mobloc.y-2) - if(locy<1) - return - if(EAST) - locy = mobloc.y - locx = (mobloc.x+2) - if(locx>world.maxx) - return - if(WEST) - locy = mobloc.y - locx = (mobloc.x-2) - if(locx<1) - return - else - return - L.forceMove(locate(locx,locy,mobloc.z)) - spawn(0) - var/limit = 2//For only two trailing shadows. - for(var/turf/T in getline(mobloc, L.loc)) - new /obj/effect/temp_visual/dir_setting/ninja/shadow(T, L.dir) - limit-- - if(limit<=0) - break - else - new /obj/effect/temp_visual/dir_setting/ninja/shadow(mobloc, L.dir) - L.forceMove(get_step(L, direct)) - L.dir = direct - if(3) //Incorporeal move, but blocked by holy-watered tiles - var/turf/simulated/floor/stepTurf = get_step(L, direct) - if(stepTurf.flags & NOJAUNT) - to_chat(L, "Holy energies block your path.") - L.notransform = 1 - spawn(2) - L.notransform = 0 - else - L.forceMove(get_step(L, direct)) - L.dir = direct - return 1 - - -///Process_Spacemove -///Called by /client/Move() -///For moving in space -///Return 1 for movement 0 for none -/mob/Process_Spacemove(movement_dir = 0) - if(..()) - return 1 - var/atom/movable/backup = get_spacemove_backup() - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - var/opposite_dir = turn(movement_dir, 180) - if(backup.newtonian_move(opposite_dir)) //You're pushing off something movable, so it moves - to_chat(src, "You push off of [backup] to propel yourself.") - return 1 - return 0 - -/mob/get_spacemove_backup() - for(var/A in orange(1, get_turf(src))) - if(isarea(A)) - continue - else if(isturf(A)) - var/turf/turf = A - if(istype(turf, /turf/space)) - continue - if(!turf.density && !mob_negates_gravity()) - continue - return A - else - var/atom/movable/AM = A - if(AM == buckled) //Kind of unnecessary but let's just be sure - continue - if(!AM.CanPass(src) || AM.density) - if(AM.anchored) - return AM - if(pulling == AM) - continue - . = AM - - -/mob/proc/mob_has_gravity(turf/T) - return has_gravity(src, T) - -/mob/proc/mob_negates_gravity() - return 0 - -/mob/proc/Move_Pulled(atom/A) - if(!canmove || restrained() || !pulling) - return - if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src)) - stop_pulling() - return - if(isliving(pulling)) - var/mob/living/L = pulling - if(L.buckled && L.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it - stop_pulling() - return - if(A == loc && pulling.density) - return - if(!Process_Spacemove(get_dir(pulling.loc, A))) - return - if(ismob(pulling)) - var/mob/M = pulling - var/atom/movable/t = M.pulling - M.stop_pulling() - step(pulling, get_dir(pulling.loc, A)) - if(M) - M.start_pulling(t) - else - step(pulling, get_dir(pulling.loc, A)) - return - -/mob/proc/update_gravity() - return -/client/proc/check_has_body_select() - return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel) - -/client/verb/body_toggle_head() - set name = "body-toggle-head" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/next_in_line - switch(mob.zone_selected) - if(BODY_ZONE_HEAD) - next_in_line = BODY_ZONE_PRECISE_EYES - if(BODY_ZONE_PRECISE_EYES) - next_in_line = BODY_ZONE_PRECISE_MOUTH - else - next_in_line = BODY_ZONE_HEAD - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(next_in_line, mob) - -/client/verb/body_r_arm() - set name = "body-r-arm" - set hidden = 1 - if(!check_has_body_select()) - return - - var/next_in_line - if(mob.zone_selected == BODY_ZONE_R_ARM) - next_in_line = BODY_ZONE_PRECISE_R_HAND - else - next_in_line = BODY_ZONE_R_ARM - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(next_in_line, mob) - -/client/verb/body_chest() - set name = "body-chest" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(BODY_ZONE_CHEST, mob) - -/client/verb/body_l_arm() - set name = "body-l-arm" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/next_in_line - if(mob.zone_selected == BODY_ZONE_L_ARM) - next_in_line = BODY_ZONE_PRECISE_L_HAND - else - next_in_line = BODY_ZONE_L_ARM - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(next_in_line, mob) - -/client/verb/body_r_leg() - set name = "body-r-leg" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/next_in_line - if(mob.zone_selected == BODY_ZONE_R_LEG) - next_in_line = BODY_ZONE_PRECISE_R_FOOT - else - next_in_line = BODY_ZONE_R_LEG - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(next_in_line, mob) - -/client/verb/body_groin() - set name = "body-groin" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(BODY_ZONE_PRECISE_GROIN, mob) - -/client/verb/body_l_leg() - set name = "body-l-leg" - set hidden = 1 - - if(!check_has_body_select()) - return - - var/next_in_line - if(mob.zone_selected == BODY_ZONE_L_LEG) - next_in_line = BODY_ZONE_PRECISE_L_FOOT - else - next_in_line = BODY_ZONE_L_LEG - - var/obj/screen/zone_sel/selector = mob.hud_used.zone_select - selector.set_selected_zone(next_in_line, mob) - -/client/verb/toggle_walk_run() - set name = "toggle-walk-run" - set hidden = TRUE - set instant = TRUE - if(mob) - mob.toggle_move_intent(usr) - -/mob/proc/toggle_move_intent(mob/user) - if(m_intent == MOVE_INTENT_RUN) - m_intent = MOVE_INTENT_WALK - else - m_intent = MOVE_INTENT_RUN - if(hud_used && hud_used.static_inventory) - for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) - selector.update_icon(src) +/mob/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover, /obj/item/projectile)) + return (!density || lying) + if(mover.throwing) + return (!density || lying || (mover.throwing.thrower == src)) + if(mover.checkpass(PASSMOB)) + return 1 + if(buckled == mover) + return TRUE + if(ismob(mover)) + var/mob/moving_mob = mover + if((other_mobs && moving_mob.other_mobs)) + return TRUE + if(mover in buckled_mobs) + return TRUE + return (!mover.density || !density || lying) + + +/client/verb/toggle_throw_mode() + set hidden = 1 + if(iscarbon(mob)) + var/mob/living/carbon/C = mob + C.toggle_throw_mode() + else + to_chat(usr, "This mob type cannot throw items.") + + +/client/verb/drop_item() + set hidden = 1 + if(!isrobot(mob)) + mob.drop_item_v() + return + + +/* /client/Center() + /* No 3D movement in 2D spessman game. dir 16 is Z Up + if(isobj(mob.loc)) + var/obj/O = mob.loc + if(mob.canmove) + return O.relaymove(mob, 16) + */ + return + */ + + +/client/proc/Move_object(direct) + if(mob && mob.control_object) + if(mob.control_object.density) + step(mob.control_object, direct) + if(!mob.control_object) + return + mob.control_object.setDir(direct) + else + mob.control_object.forceMove(get_step(mob.control_object, direct)) + return + +#define MOVEMENT_DELAY_BUFFER 0.75 +#define MOVEMENT_DELAY_BUFFER_DELTA 1.25 +/client/Move(n, direct) + if(world.time < move_delay) + return + else + next_move_dir_add = 0 + next_move_dir_sub = 0 + var/old_move_delay = move_delay + move_delay = world.time + world.tick_lag //this is here because Move() can now be called multiple times per tick + if(!mob || !mob.loc) + return 0 + + if(!n || !direct) // why did we never check this before? + return FALSE + + if(mob.notransform) + return 0 //This is sota the goto stop mobs from moving var + + if(mob.control_object) + return Move_object(direct) + + if(!isliving(mob)) + return mob.Move(n, direct) + + if(mob.stat == DEAD) + mob.ghostize() + return 0 + + if(moving) + return 0 + + if(isliving(mob)) + var/mob/living/L = mob + if(L.incorporeal_move)//Move though walls + Process_Incorpmove(direct) + return + + if(mob.remote_control) //we're controlling something, our movement is relayed to it + return mob.remote_control.relaymove(mob, direct) + + if(isAI(mob)) + if(istype(mob.loc, /obj/item/aicard)) + var/obj/O = mob.loc + return O.relaymove(mob, direct) // aicards have special relaymove stuff + return AIMove(n, direct, mob) + + + if(Process_Grab()) + return + + if(mob.buckled) //if we're buckled to something, tell it we moved. + return mob.buckled.relaymove(mob, direct) + + if(!mob.canmove) + return + + if(!mob.lastarea) + mob.lastarea = get_area(mob.loc) + + if(isobj(mob.loc) || ismob(mob.loc)) //Inside an object, tell it we moved + var/atom/O = mob.loc + return O.relaymove(mob, direct) + + if(!mob.Process_Spacemove(direct)) + return 0 + + if(mob.restrained()) // Why being pulled while cuffed prevents you from moving + for(var/mob/M in orange(1, mob)) + if(M.pulling == mob) + if(!M.incapacitated() && mob.Adjacent(M)) + to_chat(src, "You're restrained! You can't move!") + move_delay = world.time + 10 + return 0 + else + M.stop_pulling() + + + //We are now going to move + moving = 1 + var/delay = mob.movement_delay() + if(old_move_delay + (delay * MOVEMENT_DELAY_BUFFER_DELTA) + MOVEMENT_DELAY_BUFFER > world.time) + move_delay = old_move_delay + else + move_delay = world.time + mob.last_movement = world.time + + if(locate(/obj/item/grab, mob)) + move_delay = max(move_delay, world.time + 7) + var/list/L = mob.ret_grab() + if(istype(L, /list)) + if(L.len == 2) + L -= mob + var/mob/M = L[1] + if(M) + if((get_dist(mob, M) <= 1 || M.loc == mob.loc)) + var/turf/prev_loc = mob.loc + . = ..() + if(M && isturf(M.loc)) // Mob may get deleted during parent call + var/diag = get_dir(mob, M) + if((diag - 1) & diag) + else + diag = null + if((get_dist(mob, M) > 1 || diag)) + step(M, get_dir(M.loc, prev_loc)) + else + for(var/mob/M in L) + M.other_mobs = 1 + if(mob != M) + M.animate_movement = 3 + for(var/mob/M in L) + spawn(0) + step(M, direct) + return + spawn(1) + M.other_mobs = null + M.animate_movement = 2 + return + + else if(mob.confused) + var/newdir = 0 + if(mob.confused > 40) + newdir = pick(GLOB.alldirs) + else if(prob(mob.confused * 1.5)) + newdir = angle2dir(dir2angle(direct) + pick(90, -90)) + else if(prob(mob.confused * 3)) + newdir = angle2dir(dir2angle(direct) + pick(45, -45)) + if(newdir) + direct = newdir + n = get_step(mob, direct) + + . = ..() + mob.setDir(direct) + + for(var/obj/item/grab/G in mob) + if(G.state == GRAB_NECK) + mob.setDir(angle2dir((dir2angle(direct) + 202.5) % 365)) + G.adjust_position() + for(var/obj/item/grab/G in mob.grabbed_by) + G.adjust_position() + if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully + delay = mob.movement_delay() * 2 + move_delay += delay + moving = 0 + if(mob && .) + if(mob.throwing) + mob.throwing.finalize(FALSE) + + for(var/obj/O in mob) + O.on_mob_move(direct, mob) + + + + + +///Process_Grab() +///Called by client/Move() +///Checks to see if you are being grabbed and if so attemps to break it +/client/proc/Process_Grab() + if(mob.grabbed_by.len) + var/list/grabbing = list() + + if(istype(mob.l_hand, /obj/item/grab)) + var/obj/item/grab/G = mob.l_hand + grabbing += G.affecting + + if(istype(mob.r_hand, /obj/item/grab)) + var/obj/item/grab/G = mob.r_hand + grabbing += G.affecting + + for(var/X in mob.grabbed_by) + var/obj/item/grab/G = X + switch(G.state) + + if(GRAB_PASSIVE) + if(!grabbing.Find(G.assailant)) //moving always breaks a passive grab unless we are also grabbing our grabber. + qdel(G) + + if(GRAB_AGGRESSIVE) + move_delay = world.time + 10 + if(!prob(25)) + return 1 + mob.visible_message("[mob] has broken free of [G.assailant]'s grip!") + qdel(G) + + if(GRAB_NECK) + move_delay = world.time + 10 + if(!prob(5)) + return 1 + mob.visible_message("[mob] has broken free of [G.assailant]'s headlock!") + qdel(G) + return 0 + + +///Process_Incorpmove +///Called by client/Move() +///Allows mobs to run though walls +/client/proc/Process_Incorpmove(direct) + var/turf/mobloc = get_turf(mob) + if(!isliving(mob)) + return + var/mob/living/L = mob + switch(L.incorporeal_move) + if(1) + L.forceMove(get_step(L, direct)) + L.dir = direct + if(2) + if(prob(50)) + var/locx + var/locy + switch(direct) + if(NORTH) + locx = mobloc.x + locy = (mobloc.y+2) + if(locy>world.maxy) + return + if(SOUTH) + locx = mobloc.x + locy = (mobloc.y-2) + if(locy<1) + return + if(EAST) + locy = mobloc.y + locx = (mobloc.x+2) + if(locx>world.maxx) + return + if(WEST) + locy = mobloc.y + locx = (mobloc.x-2) + if(locx<1) + return + else + return + L.forceMove(locate(locx,locy,mobloc.z)) + spawn(0) + var/limit = 2//For only two trailing shadows. + for(var/turf/T in getline(mobloc, L.loc)) + new /obj/effect/temp_visual/dir_setting/ninja/shadow(T, L.dir) + limit-- + if(limit<=0) + break + else + new /obj/effect/temp_visual/dir_setting/ninja/shadow(mobloc, L.dir) + L.forceMove(get_step(L, direct)) + L.dir = direct + if(3) //Incorporeal move, but blocked by holy-watered tiles + var/turf/simulated/floor/stepTurf = get_step(L, direct) + if(stepTurf.flags & NOJAUNT) + to_chat(L, "Holy energies block your path.") + L.notransform = 1 + spawn(2) + L.notransform = 0 + else + L.forceMove(get_step(L, direct)) + L.dir = direct + return 1 + + +///Process_Spacemove +///Called by /client/Move() +///For moving in space +///Return 1 for movement 0 for none +/mob/Process_Spacemove(movement_dir = 0) + if(..()) + return 1 + var/atom/movable/backup = get_spacemove_backup() + if(backup) + if(istype(backup) && movement_dir && !backup.anchored) + var/opposite_dir = turn(movement_dir, 180) + if(backup.newtonian_move(opposite_dir)) //You're pushing off something movable, so it moves + to_chat(src, "You push off of [backup] to propel yourself.") + return 1 + return 0 + +/mob/get_spacemove_backup() + for(var/A in orange(1, get_turf(src))) + if(isarea(A)) + continue + else if(isturf(A)) + var/turf/turf = A + if(istype(turf, /turf/space)) + continue + if(!turf.density && !mob_negates_gravity()) + continue + return A + else + var/atom/movable/AM = A + if(AM == buckled) //Kind of unnecessary but let's just be sure + continue + if(!AM.CanPass(src) || AM.density) + if(AM.anchored) + return AM + if(pulling == AM) + continue + . = AM + + +/mob/proc/mob_has_gravity(turf/T) + return has_gravity(src, T) + +/mob/proc/mob_negates_gravity() + return 0 + +/mob/proc/Move_Pulled(atom/A) + if(!canmove || restrained() || !pulling) + return + if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src)) + stop_pulling() + return + if(isliving(pulling)) + var/mob/living/L = pulling + if(L.buckled && L.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it + stop_pulling() + return + if(A == loc && pulling.density) + return + if(!Process_Spacemove(get_dir(pulling.loc, A))) + return + if(ismob(pulling)) + var/mob/M = pulling + var/atom/movable/t = M.pulling + M.stop_pulling() + step(pulling, get_dir(pulling.loc, A)) + if(M) + M.start_pulling(t) + else + step(pulling, get_dir(pulling.loc, A)) + return + +/mob/proc/update_gravity() + return +/client/proc/check_has_body_select() + return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /obj/screen/zone_sel) + +/client/verb/body_toggle_head() + set name = "body-toggle-head" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + switch(mob.zone_selected) + if(BODY_ZONE_HEAD) + next_in_line = BODY_ZONE_PRECISE_EYES + if(BODY_ZONE_PRECISE_EYES) + next_in_line = BODY_ZONE_PRECISE_MOUTH + else + next_in_line = BODY_ZONE_HEAD + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_r_arm() + set name = "body-r-arm" + set hidden = 1 + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_selected == BODY_ZONE_R_ARM) + next_in_line = BODY_ZONE_PRECISE_R_HAND + else + next_in_line = BODY_ZONE_R_ARM + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_chest() + set name = "body-chest" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(BODY_ZONE_CHEST, mob) + +/client/verb/body_l_arm() + set name = "body-l-arm" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_selected == BODY_ZONE_L_ARM) + next_in_line = BODY_ZONE_PRECISE_L_HAND + else + next_in_line = BODY_ZONE_L_ARM + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_r_leg() + set name = "body-r-leg" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_selected == BODY_ZONE_R_LEG) + next_in_line = BODY_ZONE_PRECISE_R_FOOT + else + next_in_line = BODY_ZONE_R_LEG + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(next_in_line, mob) + +/client/verb/body_groin() + set name = "body-groin" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(BODY_ZONE_PRECISE_GROIN, mob) + +/client/verb/body_l_leg() + set name = "body-l-leg" + set hidden = 1 + + if(!check_has_body_select()) + return + + var/next_in_line + if(mob.zone_selected == BODY_ZONE_L_LEG) + next_in_line = BODY_ZONE_PRECISE_L_FOOT + else + next_in_line = BODY_ZONE_L_LEG + + var/obj/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(next_in_line, mob) + +/client/verb/toggle_walk_run() + set name = "toggle-walk-run" + set hidden = TRUE + set instant = TRUE + if(mob) + mob.toggle_move_intent(usr) + +/mob/proc/toggle_move_intent(mob/user) + if(m_intent == MOVE_INTENT_RUN) + m_intent = MOVE_INTENT_WALK + else + m_intent = MOVE_INTENT_RUN + if(hud_used && hud_used.static_inventory) + for(var/obj/screen/mov_intent/selector in hud_used.static_inventory) + selector.update_icon(src) diff --git a/code/modules/mob/new_player/login.dm b/code/modules/mob/new_player/login.dm index d8e6a239a9ab..7a0462fd00ff 100644 --- a/code/modules/mob/new_player/login.dm +++ b/code/modules/mob/new_player/login.dm @@ -1,15 +1,15 @@ /mob/new_player/Login() update_Login_details() //handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying - if(join_motd) - to_chat(src, "
    [join_motd]
    ") + if(GLOB.join_motd) + to_chat(src, "
    [GLOB.join_motd]
    ") if(!mind) mind = new /datum/mind(key) mind.active = 1 mind.current = src - if(length(newplayer_start)) - loc = pick(newplayer_start) + if(length(GLOB.newplayer_start)) + loc = pick(GLOB.newplayer_start) else loc = locate(1,1,1) lastarea = loc @@ -29,11 +29,11 @@ spawn(30) // Annoy the player with polls. establish_db_connection() - if(dbcon.IsConnected() && client && client.can_vote()) + if(GLOB.dbcon.IsConnected() && client && client.can_vote()) var/isadmin = 0 if(client && client.holder) isadmin = 1 - var/DBQuery/query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[ckey]\")") + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[ckey]\")") query.Execute() var/newpoll = 0 while(query.NextRow()) diff --git a/code/modules/mob/new_player/logout.dm b/code/modules/mob/new_player/logout.dm index ad1c9b3098d7..de6f38f337fe 100644 --- a/code/modules/mob/new_player/logout.dm +++ b/code/modules/mob/new_player/logout.dm @@ -1,7 +1,7 @@ -/mob/new_player/Logout() - ready = 0 - ..() - if(!spawning)//Here so that if they are spawning and log out, the other procs can play out and they will have a mob to come back to. - key = null//We null their key before deleting the mob, so they are properly kicked out. - qdel(src) - return \ No newline at end of file +/mob/new_player/Logout() + ready = 0 + ..() + if(!spawning)//Here so that if they are spawning and log out, the other procs can play out and they will have a mob to come back to. + key = null//We null their key before deleting the mob, so they are properly kicked out. + qdel(src) + return diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index fc246f661c3f..89e6ce953f57 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -1,619 +1,619 @@ -/mob/new_player - var/ready = 0 - var/spawning = 0 //Referenced when you want to delete the new_player later on in the code. - var/totalPlayers = 0 //Player counts for the Lobby tab - var/totalPlayersReady = 0 - var/tos_consent = FALSE - universal_speak = 1 - - invisibility = 101 - - density = 0 - stat = 2 - canmove = 0 - -/mob/new_player/New() - GLOB.mob_list += src - -/mob/new_player/verb/new_player_panel() - set src = usr - - if(handle_tos_consent()) - new_player_panel_proc() - -/mob/new_player/proc/handle_tos_consent() - if(!GLOB.join_tos) - tos_consent = TRUE - return TRUE - - establish_db_connection() - if(!dbcon.IsConnected()) - tos_consent = TRUE - return TRUE - - var/DBQuery/query = dbcon.NewQuery("SELECT * FROM [format_table_name("privacy")] WHERE ckey='[src.ckey]' AND consent=1") - query.Execute() - while(query.NextRow()) - tos_consent = TRUE - return TRUE - - privacy_consent() - return FALSE - -/mob/new_player/proc/privacy_consent() - src << browse(null, "window=playersetup") - var/output = GLOB.join_tos - output += "

    I consent" - output += "

    I DO NOT consent" - src << browse(output,"window=privacy_consent;size=500x300") - var/datum/browser/popup = new(src, "privacy_consent", "

    Privacy Consent
    ", 500, 400) - popup.set_window_options("can_close=0") - popup.set_content(output) - popup.open(0) - return - - -/mob/new_player/proc/new_player_panel_proc() - var/real_name = client.prefs.real_name - if(client.prefs.randomslot) - real_name = "Random Character Slot" - var/output = "

    Setup Character
    [real_name]

    " - - if(!SSticker || SSticker.current_state <= GAME_STATE_PREGAME) - if(!ready) output += "

    Declare Ready

    " - else output += "

    You are ready (Cancel)

    " - else - output += "

    View the Crew Manifest

    " - output += "

    Join Game!

    " - - var/list/antags = client.prefs.be_special - if(antags && antags.len) - if(!client.skip_antag) output += "

    Global Antag Candidacy" - else output += "

    Global Antag Candidacy" - output += "
    You are [client.skip_antag ? "ineligible" : "eligible"] for all antag roles.

    " - - output += "

    Observe

    " - - if(GLOB.join_tos) - output += "

    Terms of Service

    " - - if(!IsGuestKey(src.key)) - establish_db_connection() - - if(dbcon.IsConnected() && client.can_vote()) - var/isadmin = 0 - if(src.client && src.client.holder) - isadmin = 1 - var/DBQuery/query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[ckey]\")") - query.Execute() - var/newpoll = 0 - while(query.NextRow()) - newpoll = 1 - break - - if(newpoll) - output += "

    Show Player Polls (NEW!)

    " - else - output += "

    Show Player Polls

    " - - output += "
    " - - var/datum/browser/popup = new(src, "playersetup", "
    New Player Options
    ", 220, 290) - popup.set_window_options("can_close=0") - popup.set_content(output) - popup.open(0) - return - -/mob/new_player/Stat() - statpanel("Lobby") - if(client.statpanel=="Lobby" && SSticker) - if(SSticker.hide_mode) - stat("Game Mode:", "Secret") - else - if(SSticker.hide_mode == 0) - stat("Game Mode:", "[master_mode]") // Old setting for showing the game mode - else - stat("Game Mode: ", "Secret") - - if((SSticker.current_state == GAME_STATE_PREGAME) && going) - stat("Time To Start:", round(SSticker.pregame_timeleft/10)) - if((SSticker.current_state == GAME_STATE_PREGAME) && !going) - stat("Time To Start:", "DELAYED") - - if(SSticker.current_state == GAME_STATE_PREGAME) - stat("Players:", "[totalPlayers]") - if(check_rights(R_ADMIN, 0, src)) - stat("Players Ready:", "[totalPlayersReady]") - totalPlayers = 0 - totalPlayersReady = 0 - for(var/mob/new_player/player in GLOB.player_list) - if(check_rights(R_ADMIN, 0, src)) - stat("[player.key]", (player.ready)?("(Playing)"):(null)) - totalPlayers++ - if(player.ready) - totalPlayersReady++ - - ..() - - statpanel("Status") - - -/mob/new_player/Topic(href, href_list[]) - if(!client) return 0 - - if(href_list["consent_signed"]) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("REPLACE INTO [format_table_name("privacy")] (ckey, datetime, consent) VALUES ('[ckey]', '[sqltime]', 1)") - query.Execute() - src << browse(null, "window=privacy_consent") - tos_consent = 1 - new_player_panel_proc() - if(href_list["consent_rejected"]) - tos_consent = 0 - to_chat(usr, "You must consent to the terms of service before you can join!") - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("REPLACE INTO [format_table_name("privacy")] (ckey, datetime, consent) VALUES ('[ckey]', '[sqltime]', 0)") - query.Execute() - - if(href_list["show_preferences"]) - client.prefs.ShowChoices(src) - return 1 - - if(href_list["ready"]) - if(!tos_consent) - to_chat(usr, "You must consent to the terms of service before you can join!") - return 0 - ready = !ready - new_player_panel_proc() - - if(href_list["skip_antag"]) - client.skip_antag = !client.skip_antag - new_player_panel_proc() - - if(href_list["refresh"]) - src << browse(null, "window=playersetup") //closes the player setup window - new_player_panel_proc() - - if(href_list["observe"]) - if(!tos_consent) - to_chat(usr, "You must consent to the terms of service before you can join!") - return 0 - - if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes") - if(!client) - return 1 - var/mob/dead/observer/observer = new() - src << browse(null, "window=playersetup") - spawning = 1 - stop_sound_channel(CHANNEL_LOBBYMUSIC) - - - observer.started_as_observer = 1 - close_spawn_windows() - var/obj/O = locate("landmark*Observer-Start") - to_chat(src, "Now teleporting.") - observer.forceMove(O.loc) - observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. - client.prefs.update_preview_icon(1) - observer.icon = client.prefs.preview_icon - observer.alpha = 127 - - if(client.prefs.be_random_name) - client.prefs.real_name = random_name(client.prefs.gender,client.prefs.species) - observer.real_name = client.prefs.real_name - observer.name = observer.real_name - if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. - observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! - observer.key = key - GLOB.respawnable_list += observer - qdel(src) - return 1 - if(href_list["tos"]) - privacy_consent() - return 0 - - if(href_list["late_join"]) - if(!tos_consent) - to_chat(usr, "You must consent to the terms of service before you can join!") - return 0 - if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING) - to_chat(usr, "The round is either not ready, or has already finished...") - return - if(client.prefs.species in GLOB.whitelisted_species) - - if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) - to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) - return 0 - - LateChoices() - - if(href_list["manifest"]) - ViewManifest() - - if(href_list["SelectedJob"]) - - if(!enter_allowed) - to_chat(usr, "There is an administrative lock on entering the game!") - return - - if(client.prefs.randomslot) - client.prefs.load_random_character_slot(client) - - if(client.prefs.species in GLOB.whitelisted_species) - if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) - to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) - return 0 - - AttemptLateSpawn(href_list["SelectedJob"],client.prefs.spawnpoint) - return - - if(!ready && href_list["preference"]) - if(client) - client.prefs.process_link(src, href_list) - else if(!href_list["late_join"]) - new_player_panel() - -/mob/new_player/proc/IsJobAvailable(rank) - var/datum/job/job = SSjobs.GetJob(rank) - if(!job) return 0 - if(!job.is_position_available()) return 0 - if(jobban_isbanned(src,rank)) return 0 - if(!is_job_whitelisted(src, rank)) return 0 - if(!job.player_old_enough(client)) return 0 - if(job.admin_only && !(check_rights(R_EVENT, 0))) return 0 - if(job.available_in_playtime(client)) - return 0 - - if(config.assistantlimit) - if(job.title == "Civilian") - var/count = 0 - var/datum/job/officer = SSjobs.GetJob("Security Officer") - var/datum/job/warden = SSjobs.GetJob("Warden") - var/datum/job/hos = SSjobs.GetJob("Head of Security") - count += (officer.current_positions + warden.current_positions + hos.current_positions) - if(job.current_positions > (config.assistantratio * count)) - if(count >= 5) // if theres more than 5 security on the station just let assistants join regardless, they should be able to handle the tide - return 1 - return 0 - return 1 - -/mob/new_player/proc/IsAdminJob(rank) - var/datum/job/job = SSjobs.GetJob(rank) - if(job.admin_only) - return 1 - else - return 0 - -/mob/new_player/proc/IsERTSpawnJob(rank) - var/datum/job/job = SSjobs.GetJob(rank) - if(job.spawn_ert) - return 1 - else - return 0 - -/mob/new_player/proc/IsSyndicateCommand(rank) - var/datum/job/job = SSjobs.GetJob(rank) - if(job.syndicate_command) - return 1 - else - return 0 - -/mob/new_player/proc/AttemptLateSpawn(rank,var/spawning_at) - if(src != usr) - return 0 - if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING) - to_chat(usr, "The round is either not ready, or has already finished...") - return 0 - if(!enter_allowed) - to_chat(usr, "There is an administrative lock on entering the game!") - return 0 - if(!IsJobAvailable(rank)) - to_chat(src, alert("[rank] is not available. Please try another.")) - return 0 - var/datum/job/thisjob = SSjobs.GetJob(rank) - if(thisjob.barred_by_disability(client)) - to_chat(src, alert("[rank] is not available due to your character's disability. Please try another.")) - return 0 - - SSjobs.AssignRole(src, rank, 1) - - var/mob/living/character = create_character() //creates the human and transfers vars and mind - character = SSjobs.AssignRank(character, rank, 1) //equips the human - - // AIs don't need a spawnpoint, they must spawn at an empty core - if(character.mind.assigned_role == "AI") - var/mob/living/silicon/ai/ai_character = character.AIize() // AIize the character, but don't move them yet - - // IsJobAvailable for AI checks that there is an empty core available in this list - ai_character.moveToEmptyCore() - AnnounceCyborg(ai_character, rank, "has been downloaded to the empty core in \the [get_area(ai_character)]") - - SSticker.mode.latespawn(ai_character) - qdel(src) - return - - //Find our spawning point. - var/join_message - var/datum/spawnpoint/S - - if(IsAdminJob(rank)) - if(IsERTSpawnJob(rank)) - character.loc = pick(ertdirector) - else if(IsSyndicateCommand(rank)) - character.loc = pick(syndicateofficer) - else - character.forceMove(pick(aroomwarp)) - join_message = "has arrived" - else - if(spawning_at) - S = spawntypes[spawning_at] - if(S && istype(S)) - if(S.check_job_spawning(rank)) - character.forceMove(pick(S.turfs)) - join_message = S.msg - else - to_chat(character, "Your chosen spawnpoint ([S.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") - character.forceMove(pick(latejoin)) - join_message = "has arrived on the station" - else - character.forceMove(pick(latejoin)) - join_message = "has arrived on the station" - - character.lastarea = get_area(loc) - // Moving wheelchair if they have one - if(character.buckled && istype(character.buckled, /obj/structure/chair/wheelchair)) - character.buckled.forceMove(character.loc) - character.buckled.dir = character.dir - - character = SSjobs.EquipRank(character, rank, 1) //equips the human - EquipCustomItems(character) - - SSticker.mode.latespawn(character) - - if(character.mind.assigned_role == "Cyborg") - AnnounceCyborg(character, rank, join_message) - else - SSticker.minds += character.mind//Cyborgs and AIs handle this in the transform proc. //TODO!!!!! ~Carn - if(!IsAdminJob(rank)) - data_core.manifest_inject(character) - AnnounceArrival(character, rank, join_message) - AddEmploymentContract(character) - - if(GLOB.summon_guns_triggered) - give_guns(character) - if(GLOB.summon_magic_triggered) - give_magic(character) - - if(!thisjob.is_position_available() && thisjob in SSjobs.prioritized_jobs) - SSjobs.prioritized_jobs -= thisjob - qdel(src) - - -/mob/new_player/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank, var/join_message) - if(SSticker.current_state == GAME_STATE_PLAYING) - var/ailist[] = list() - for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) - ailist += A - if(ailist.len) - var/mob/living/silicon/ai/announcer = pick(ailist) - if(character.mind) - if((character.mind.assigned_role != "Cyborg") && (character.mind.assigned_role != character.mind.special_role)) - if(character.mind.role_alt_title) - rank = character.mind.role_alt_title - var/arrivalmessage = announcer.arrivalmsg - arrivalmessage = replacetext(arrivalmessage,"$name",character.real_name) - arrivalmessage = replacetext(arrivalmessage,"$rank",rank ? "[rank]" : "visitor") - arrivalmessage = replacetext(arrivalmessage,"$species",character.dna.species.name) - arrivalmessage = replacetext(arrivalmessage,"$age",num2text(character.age)) - arrivalmessage = replacetext(arrivalmessage,"$gender",character.gender == FEMALE ? "Female" : "Male") - announcer.say(";[arrivalmessage]") - else - if(character.mind) - if((character.mind.assigned_role != "Cyborg") && (character.mind.assigned_role != character.mind.special_role)) - if(character.mind.role_alt_title) - rank = character.mind.role_alt_title - global_announcer.autosay("[character.real_name],[rank ? " [rank]," : " visitor," ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer") - -/mob/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee) - spawn(30) - for(var/C in employmentCabinets) - var/obj/structure/filingcabinet/employment/employmentCabinet = C - if(!employmentCabinet.virgin) - employmentCabinet.addFile(employee) - -/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message) - if(SSticker.current_state == GAME_STATE_PLAYING) - var/ailist[] = list() - for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) - ailist += A - if(ailist.len) - var/mob/living/silicon/ai/announcer = pick(ailist) - if(character.mind) - if(character.mind.assigned_role != character.mind.special_role) - var/arrivalmessage = "A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"]." - announcer.say(";[arrivalmessage]") - else - if(character.mind) - if(character.mind.assigned_role != character.mind.special_role) - // can't use their name here, since cyborg namepicking is done post-spawn, so we'll just say "A new Cyborg has arrived"/"A new Android has arrived"/etc. - global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer") - -/mob/new_player/proc/LateChoices() - var/mills = ROUND_TIME // 1/10 of a second, not real milliseconds but whatever - //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something - var/mins = (mills % 36000) / 600 - var/hours = mills / 36000 - - var/dat = "
    " - dat += "Round Duration: [round(hours)]h [round(mins)]m
    " - - if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) - dat += "The station has been evacuated.
    " - else if(SSshuttle.emergency.mode >= SHUTTLE_CALL) - dat += "The station is currently undergoing evacuation procedures.
    " - - if(length(SSjobs.prioritized_jobs)) - dat += "The station has flagged these jobs as high priority: " - var/amt = length(SSjobs.prioritized_jobs) - var/amt_count - for(var/datum/job/a in SSjobs.prioritized_jobs) - amt_count++ - if(amt_count != amt) - dat += " [a.title], " - else - dat += " [a.title].
    " - - - var/num_jobs_available = 0 - var/list/activePlayers = list() - var/list/categorizedJobs = list( - "Command" = list(jobs = list(), titles = command_positions, color = "#aac1ee"), - "Engineering" = list(jobs = list(), titles = engineering_positions, color = "#ffd699"), - "Security" = list(jobs = list(), titles = security_positions, color = "#ff9999"), - "Miscellaneous" = list(jobs = list(), titles = list(), color = "#ffffff", colBreak = 1), - "Synthetic" = list(jobs = list(), titles = nonhuman_positions, color = "#ccffcc"), - "Support / Service" = list(jobs = list(), titles = service_positions, color = "#cccccc"), - "Medical" = list(jobs = list(), titles = medical_positions, color = "#99ffe6", colBreak = 1), - "Science" = list(jobs = list(), titles = science_positions, color = "#e6b3e6"), - "Supply" = list(jobs = list(), titles = supply_positions, color = "#ead4ae"), - ) - for(var/datum/job/job in SSjobs.occupations) - if(job && IsJobAvailable(job.title) && !job.barred_by_disability(client)) - num_jobs_available++ - activePlayers[job] = 0 - var/categorized = 0 - // Only players with the job assigned and AFK for less than 10 minutes count as active - for(var/mob/M in GLOB.player_list) if(M.mind && M.client && M.mind.assigned_role == job.title && M.client.inactivity <= 10 MINUTES) - activePlayers[job]++ - for(var/jobcat in categorizedJobs) - var/list/jobs = categorizedJobs[jobcat]["jobs"] - if(job.title in categorizedJobs[jobcat]["titles"]) - categorized = 1 - if(jobcat == "Command") // Put captain at top of command jobs - if(job.title == "Captain") - jobs.Insert(1, job) - else - jobs += job - else // Put heads at top of non-command jobs - if(job.title in command_positions) - jobs.Insert(1, job) - else - jobs += job - if(!categorized) - categorizedJobs["Miscellaneous"]["jobs"] += job - - if(num_jobs_available) - dat += "Choose from the following open positions:

    " - dat += "
    " - for(var/jobcat in categorizedJobs) - if(categorizedJobs[jobcat]["colBreak"]) - dat += "" - if(length(categorizedJobs[jobcat]["jobs"]) < 1) - continue - var/color = categorizedJobs[jobcat]["color"] - dat += "
    " - dat += "[jobcat]" - for(var/datum/job/job in categorizedJobs[jobcat]["jobs"]) - if(job in SSjobs.prioritized_jobs) - dat += "[job.title] ([job.current_positions]) (Active: [activePlayers[job]])
    " - else - dat += "[job.title] ([job.current_positions]) (Active: [activePlayers[job]])
    " - dat += "

    " - - dat += "
    " - else - dat += "

    Unfortunately, there are no job slots free currently.
    Wait a few minutes, then try again.
    Or, try observing the round.
    " - // Removing the old window method but leaving it here for reference -// src << browse(dat, "window=latechoices;size=300x640;can_close=1") - // Added the new browser window method - var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 900, 600) - popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css') - popup.add_script("delay_interactivity", 'html/browser/delay_interactivity.js') - popup.set_content(dat) - popup.open(0) // 0 is passed to open so that it doesn't use the onclose() proc - -/mob/new_player/proc/create_character() - spawning = 1 - close_spawn_windows() - - check_prefs_are_sane() - var/mob/living/carbon/human/new_character = new(loc) - new_character.lastarea = get_area(loc) - - if(SSticker.random_players || appearance_isbanned(new_character)) - client.prefs.random_character() - client.prefs.real_name = random_name(client.prefs.gender) - client.prefs.copy_to(new_character) - - stop_sound_channel(CHANNEL_LOBBYMUSIC) - - - if(mind) - mind.active = 0 //we wish to transfer the key manually - if(mind.assigned_role == "Clown") //give them a clownname if they are a clown - new_character.real_name = pick(GLOB.clown_names) //I hate this being here of all places but unfortunately dna is based on real_name! - new_character.rename_self("clown") - else if(mind.assigned_role == "Mime") - new_character.real_name = pick(GLOB.mime_names) - new_character.rename_self("mime") - mind.original = new_character - mind.transfer_to(new_character) //won't transfer key since the mind is not active - - - new_character.key = key //Manually transfer the key to log them in - - return new_character - -// This is to check that the player only has preferences set that they're supposed to -/mob/new_player/proc/check_prefs_are_sane() - var/datum/species/chosen_species - if(client.prefs.species) - chosen_species = GLOB.all_species[client.prefs.species] - if(!(chosen_species && (is_species_whitelisted(chosen_species) || has_admin_rights()))) - // Have to recheck admin due to no usr at roundstart. Latejoins are fine though. - log_runtime(EXCEPTION("[src] had species [client.prefs.species], though they weren't supposed to. Setting to Human."), src) - client.prefs.species = "Human" - - var/datum/language/chosen_language - if(client.prefs.language) - chosen_language = GLOB.all_languages[client.prefs.language] - if((chosen_language == null && client.prefs.language != "None") || (chosen_language && chosen_language.flags & RESTRICTED)) - log_runtime(EXCEPTION("[src] had language [client.prefs.language], though they weren't supposed to. Setting to None."), src) - client.prefs.language = "None" - -/mob/new_player/proc/ViewManifest() - var/dat = "" - dat += "

    Crew Manifest

    " - dat += data_core.get_manifest(OOC = 1) - - src << browse(dat, "window=manifest;size=370x420;can_close=1") - -/mob/new_player/Move() - return 0 - - -/mob/new_player/proc/close_spawn_windows() - src << browse(null, "window=latechoices") //closes late choices window - src << browse(null, "window=playersetup") //closes the player setup window - src << browse(null, "window=preferences") //closes job selection - src << browse(null, "window=mob_occupation") - src << browse(null, "window=latechoices") //closes late job selection - - -/mob/new_player/proc/has_admin_rights() - return check_rights(R_ADMIN, 0, src) - -/mob/new_player/proc/is_species_whitelisted(datum/species/S) - if(!S) return 1 - return is_alien_whitelisted(src, S.name) || !config.usealienwhitelist || !(IS_WHITELISTED in S.species_traits) - -/mob/new_player/get_gender() - if(!client || !client.prefs) ..() - return client.prefs.gender - -/mob/new_player/is_ready() - return ready && ..() - -// No hearing announcements -/mob/new_player/can_hear() - return 0 +/mob/new_player + var/ready = 0 + var/spawning = 0 //Referenced when you want to delete the new_player later on in the code. + var/totalPlayers = 0 //Player counts for the Lobby tab + var/totalPlayersReady = 0 + var/tos_consent = FALSE + universal_speak = 1 + + invisibility = 101 + + density = 0 + stat = 2 + canmove = 0 + +/mob/new_player/New() + GLOB.mob_list += src + +/mob/new_player/verb/new_player_panel() + set src = usr + + if(handle_tos_consent()) + new_player_panel_proc() + +/mob/new_player/proc/handle_tos_consent() + if(!GLOB.join_tos) + tos_consent = TRUE + return TRUE + + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + tos_consent = TRUE + return TRUE + + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT * FROM [format_table_name("privacy")] WHERE ckey='[src.ckey]' AND consent=1") + query.Execute() + while(query.NextRow()) + tos_consent = TRUE + return TRUE + + privacy_consent() + return FALSE + +/mob/new_player/proc/privacy_consent() + src << browse(null, "window=playersetup") + var/output = GLOB.join_tos + output += "

    I consent" + output += "

    I DO NOT consent" + src << browse(output,"window=privacy_consent;size=500x300") + var/datum/browser/popup = new(src, "privacy_consent", "

    Privacy Consent
    ", 500, 400) + popup.set_window_options("can_close=0") + popup.set_content(output) + popup.open(0) + return + + +/mob/new_player/proc/new_player_panel_proc() + var/real_name = client.prefs.real_name + if(client.prefs.randomslot) + real_name = "Random Character Slot" + var/output = "

    Setup Character
    [real_name]

    " + + if(!SSticker || SSticker.current_state <= GAME_STATE_PREGAME) + if(!ready) output += "

    Declare Ready

    " + else output += "

    You are ready (Cancel)

    " + else + output += "

    View the Crew Manifest

    " + output += "

    Join Game!

    " + + var/list/antags = client.prefs.be_special + if(antags && antags.len) + if(!client.skip_antag) output += "

    Global Antag Candidacy" + else output += "

    Global Antag Candidacy" + output += "
    You are [client.skip_antag ? "ineligible" : "eligible"] for all antag roles.

    " + + output += "

    Observe

    " + + if(GLOB.join_tos) + output += "

    Terms of Service

    " + + if(!IsGuestKey(src.key)) + establish_db_connection() + + if(GLOB.dbcon.IsConnected() && client.can_vote()) + var/isadmin = 0 + if(src.client && src.client.holder) + isadmin = 1 + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime AND id NOT IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = \"[ckey]\") AND id NOT IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = \"[ckey]\")") + query.Execute() + var/newpoll = 0 + while(query.NextRow()) + newpoll = 1 + break + + if(newpoll) + output += "

    Show Player Polls (NEW!)

    " + else + output += "

    Show Player Polls

    " + + output += "
    " + + var/datum/browser/popup = new(src, "playersetup", "
    New Player Options
    ", 220, 290) + popup.set_window_options("can_close=0") + popup.set_content(output) + popup.open(0) + return + +/mob/new_player/Stat() + statpanel("Lobby") + if(client.statpanel=="Lobby" && SSticker) + if(SSticker.hide_mode) + stat("Game Mode:", "Secret") + else + if(SSticker.hide_mode == 0) + stat("Game Mode:", "[GLOB.master_mode]") // Old setting for showing the game mode + else + stat("Game Mode: ", "Secret") + + if((SSticker.current_state == GAME_STATE_PREGAME) && SSticker.ticker_going) + stat("Time To Start:", round(SSticker.pregame_timeleft/10)) + if((SSticker.current_state == GAME_STATE_PREGAME) && !SSticker.ticker_going) + stat("Time To Start:", "DELAYED") + + if(SSticker.current_state == GAME_STATE_PREGAME) + stat("Players:", "[totalPlayers]") + if(check_rights(R_ADMIN, 0, src)) + stat("Players Ready:", "[totalPlayersReady]") + totalPlayers = 0 + totalPlayersReady = 0 + for(var/mob/new_player/player in GLOB.player_list) + if(check_rights(R_ADMIN, 0, src)) + stat("[player.key]", (player.ready)?("(Playing)"):(null)) + totalPlayers++ + if(player.ready) + totalPlayersReady++ + + ..() + + statpanel("Status") + + +/mob/new_player/Topic(href, href_list[]) + if(!client) return 0 + + if(href_list["consent_signed"]) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = GLOB.dbcon.NewQuery("REPLACE INTO [format_table_name("privacy")] (ckey, datetime, consent) VALUES ('[ckey]', '[sqltime]', 1)") + query.Execute() + src << browse(null, "window=privacy_consent") + tos_consent = 1 + new_player_panel_proc() + if(href_list["consent_rejected"]) + tos_consent = 0 + to_chat(usr, "You must consent to the terms of service before you can join!") + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = GLOB.dbcon.NewQuery("REPLACE INTO [format_table_name("privacy")] (ckey, datetime, consent) VALUES ('[ckey]', '[sqltime]', 0)") + query.Execute() + + if(href_list["show_preferences"]) + client.prefs.ShowChoices(src) + return 1 + + if(href_list["ready"]) + if(!tos_consent) + to_chat(usr, "You must consent to the terms of service before you can join!") + return 0 + ready = !ready + new_player_panel_proc() + + if(href_list["skip_antag"]) + client.skip_antag = !client.skip_antag + new_player_panel_proc() + + if(href_list["refresh"]) + src << browse(null, "window=playersetup") //closes the player setup window + new_player_panel_proc() + + if(href_list["observe"]) + if(!tos_consent) + to_chat(usr, "You must consent to the terms of service before you can join!") + return 0 + + if(alert(src,"Are you sure you wish to observe? You cannot normally join the round after doing this!","Player Setup","Yes","No") == "Yes") + if(!client) + return 1 + var/mob/dead/observer/observer = new() + src << browse(null, "window=playersetup") + spawning = 1 + stop_sound_channel(CHANNEL_LOBBYMUSIC) + + + observer.started_as_observer = 1 + close_spawn_windows() + var/obj/O = locate("landmark*Observer-Start") + to_chat(src, "Now teleporting.") + observer.forceMove(O.loc) + observer.timeofdeath = world.time // Set the time of death so that the respawn timer works correctly. + client.prefs.update_preview_icon(1) + observer.icon = client.prefs.preview_icon + observer.alpha = 127 + + if(client.prefs.be_random_name) + client.prefs.real_name = random_name(client.prefs.gender,client.prefs.species) + observer.real_name = client.prefs.real_name + observer.name = observer.real_name + if(!client.holder && !config.antag_hud_allowed) // For new ghosts we remove the verb from even showing up if it's not allowed. + observer.verbs -= /mob/dead/observer/verb/toggle_antagHUD // Poor guys, don't know what they are missing! + observer.key = key + GLOB.respawnable_list += observer + qdel(src) + return 1 + if(href_list["tos"]) + privacy_consent() + return 0 + + if(href_list["late_join"]) + if(!tos_consent) + to_chat(usr, "You must consent to the terms of service before you can join!") + return 0 + if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING) + to_chat(usr, "The round is either not ready, or has already finished...") + return + if(client.prefs.species in GLOB.whitelisted_species) + + if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) + to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) + return 0 + + LateChoices() + + if(href_list["manifest"]) + ViewManifest() + + if(href_list["SelectedJob"]) + + if(!GLOB.enter_allowed) + to_chat(usr, "There is an administrative lock on entering the game!") + return + + if(client.prefs.randomslot) + client.prefs.load_random_character_slot(client) + + if(client.prefs.species in GLOB.whitelisted_species) + if(!is_alien_whitelisted(src, client.prefs.species) && config.usealienwhitelist) + to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) + return 0 + + AttemptLateSpawn(href_list["SelectedJob"],client.prefs.spawnpoint) + return + + if(!ready && href_list["preference"]) + if(client) + client.prefs.process_link(src, href_list) + else if(!href_list["late_join"]) + new_player_panel() + +/mob/new_player/proc/IsJobAvailable(rank) + var/datum/job/job = SSjobs.GetJob(rank) + if(!job) return 0 + if(!job.is_position_available()) return 0 + if(jobban_isbanned(src,rank)) return 0 + if(!is_job_whitelisted(src, rank)) return 0 + if(!job.player_old_enough(client)) return 0 + if(job.admin_only && !(check_rights(R_EVENT, 0))) return 0 + if(job.available_in_playtime(client)) + return 0 + + if(config.assistantlimit) + if(job.title == "Civilian") + var/count = 0 + var/datum/job/officer = SSjobs.GetJob("Security Officer") + var/datum/job/warden = SSjobs.GetJob("Warden") + var/datum/job/hos = SSjobs.GetJob("Head of Security") + count += (officer.current_positions + warden.current_positions + hos.current_positions) + if(job.current_positions > (config.assistantratio * count)) + if(count >= 5) // if theres more than 5 security on the station just let assistants join regardless, they should be able to handle the tide + return 1 + return 0 + return 1 + +/mob/new_player/proc/IsAdminJob(rank) + var/datum/job/job = SSjobs.GetJob(rank) + if(job.admin_only) + return 1 + else + return 0 + +/mob/new_player/proc/IsERTSpawnJob(rank) + var/datum/job/job = SSjobs.GetJob(rank) + if(job.spawn_ert) + return 1 + else + return 0 + +/mob/new_player/proc/IsSyndicateCommand(rank) + var/datum/job/job = SSjobs.GetJob(rank) + if(job.syndicate_command) + return 1 + else + return 0 + +/mob/new_player/proc/AttemptLateSpawn(rank,var/spawning_at) + if(src != usr) + return 0 + if(!SSticker || SSticker.current_state != GAME_STATE_PLAYING) + to_chat(usr, "The round is either not ready, or has already finished...") + return 0 + if(!GLOB.enter_allowed) + to_chat(usr, "There is an administrative lock on entering the game!") + return 0 + if(!IsJobAvailable(rank)) + to_chat(src, alert("[rank] is not available. Please try another.")) + return 0 + var/datum/job/thisjob = SSjobs.GetJob(rank) + if(thisjob.barred_by_disability(client)) + to_chat(src, alert("[rank] is not available due to your character's disability. Please try another.")) + return 0 + + SSjobs.AssignRole(src, rank, 1) + + var/mob/living/character = create_character() //creates the human and transfers vars and mind + character = SSjobs.AssignRank(character, rank, 1) //equips the human + + // AIs don't need a spawnpoint, they must spawn at an empty core + if(character.mind.assigned_role == "AI") + var/mob/living/silicon/ai/ai_character = character.AIize() // AIize the character, but don't move them yet + + // IsJobAvailable for AI checks that there is an empty core available in this list + ai_character.moveToEmptyCore() + AnnounceCyborg(ai_character, rank, "has been downloaded to the empty core in \the [get_area(ai_character)]") + + SSticker.mode.latespawn(ai_character) + qdel(src) + return + + //Find our spawning point. + var/join_message + var/datum/spawnpoint/S + + if(IsAdminJob(rank)) + if(IsERTSpawnJob(rank)) + character.loc = pick(GLOB.ertdirector) + else if(IsSyndicateCommand(rank)) + character.loc = pick(GLOB.syndicateofficer) + else + character.forceMove(pick(GLOB.aroomwarp)) + join_message = "has arrived" + else + if(spawning_at) + S = GLOB.spawntypes[spawning_at] + if(S && istype(S)) + if(S.check_job_spawning(rank)) + character.forceMove(pick(S.turfs)) + join_message = S.msg + else + to_chat(character, "Your chosen spawnpoint ([S.display_name]) is unavailable for your chosen job. Spawning you at the Arrivals shuttle instead.") + character.forceMove(pick(GLOB.latejoin)) + join_message = "has arrived on the station" + else + character.forceMove(pick(GLOB.latejoin)) + join_message = "has arrived on the station" + + character.lastarea = get_area(loc) + // Moving wheelchair if they have one + if(character.buckled && istype(character.buckled, /obj/structure/chair/wheelchair)) + character.buckled.forceMove(character.loc) + character.buckled.dir = character.dir + + character = SSjobs.EquipRank(character, rank, 1) //equips the human + EquipCustomItems(character) + + SSticker.mode.latespawn(character) + + if(character.mind.assigned_role == "Cyborg") + AnnounceCyborg(character, rank, join_message) + else + SSticker.minds += character.mind//Cyborgs and AIs handle this in the transform proc. //TODO!!!!! ~Carn + if(!IsAdminJob(rank)) + GLOB.data_core.manifest_inject(character) + AnnounceArrival(character, rank, join_message) + AddEmploymentContract(character) + + if(GLOB.summon_guns_triggered) + give_guns(character) + if(GLOB.summon_magic_triggered) + give_magic(character) + + if(!thisjob.is_position_available() && thisjob in SSjobs.prioritized_jobs) + SSjobs.prioritized_jobs -= thisjob + qdel(src) + + +/mob/new_player/proc/AnnounceArrival(var/mob/living/carbon/human/character, var/rank, var/join_message) + if(SSticker.current_state == GAME_STATE_PLAYING) + var/ailist[] = list() + for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) + ailist += A + if(ailist.len) + var/mob/living/silicon/ai/announcer = pick(ailist) + if(character.mind) + if((character.mind.assigned_role != "Cyborg") && (character.mind.assigned_role != character.mind.special_role)) + if(character.mind.role_alt_title) + rank = character.mind.role_alt_title + var/arrivalmessage = announcer.arrivalmsg + arrivalmessage = replacetext(arrivalmessage,"$name",character.real_name) + arrivalmessage = replacetext(arrivalmessage,"$rank",rank ? "[rank]" : "visitor") + arrivalmessage = replacetext(arrivalmessage,"$species",character.dna.species.name) + arrivalmessage = replacetext(arrivalmessage,"$age",num2text(character.age)) + arrivalmessage = replacetext(arrivalmessage,"$gender",character.gender == FEMALE ? "Female" : "Male") + announcer.say(";[arrivalmessage]") + else + if(character.mind) + if((character.mind.assigned_role != "Cyborg") && (character.mind.assigned_role != character.mind.special_role)) + if(character.mind.role_alt_title) + rank = character.mind.role_alt_title + GLOB.global_announcer.autosay("[character.real_name],[rank ? " [rank]," : " visitor," ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer") + +/mob/new_player/proc/AddEmploymentContract(mob/living/carbon/human/employee) + spawn(30) + for(var/C in GLOB.employmentCabinets) + var/obj/structure/filingcabinet/employment/employmentCabinet = C + if(!employmentCabinet.virgin) + employmentCabinet.addFile(employee) + +/mob/new_player/proc/AnnounceCyborg(var/mob/living/character, var/rank, var/join_message) + if(SSticker.current_state == GAME_STATE_PLAYING) + var/ailist[] = list() + for(var/mob/living/silicon/ai/A in GLOB.living_mob_list) + ailist += A + if(ailist.len) + var/mob/living/silicon/ai/announcer = pick(ailist) + if(character.mind) + if(character.mind.assigned_role != character.mind.special_role) + var/arrivalmessage = "A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"]." + announcer.say(";[arrivalmessage]") + else + if(character.mind) + if(character.mind.assigned_role != character.mind.special_role) + // can't use their name here, since cyborg namepicking is done post-spawn, so we'll just say "A new Cyborg has arrived"/"A new Android has arrived"/etc. + GLOB.global_announcer.autosay("A new[rank ? " [rank]" : " visitor" ] [join_message ? join_message : "has arrived on the station"].", "Arrivals Announcement Computer") + +/mob/new_player/proc/LateChoices() + var/mills = ROUND_TIME // 1/10 of a second, not real milliseconds but whatever + //var/secs = ((mills % 36000) % 600) / 10 //Not really needed, but I'll leave it here for refrence.. or something + var/mins = (mills % 36000) / 600 + var/hours = mills / 36000 + + var/dat = "
    " + dat += "Round Duration: [round(hours)]h [round(mins)]m
    " + + if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) + dat += "The station has been evacuated.
    " + else if(SSshuttle.emergency.mode >= SHUTTLE_CALL) + dat += "The station is currently undergoing evacuation procedures.
    " + + if(length(SSjobs.prioritized_jobs)) + dat += "The station has flagged these jobs as high priority: " + var/amt = length(SSjobs.prioritized_jobs) + var/amt_count + for(var/datum/job/a in SSjobs.prioritized_jobs) + amt_count++ + if(amt_count != amt) + dat += " [a.title], " + else + dat += " [a.title].
    " + + + var/num_jobs_available = 0 + var/list/activePlayers = list() + var/list/categorizedJobs = list( + "Command" = list(jobs = list(), titles = GLOB.command_positions, color = "#aac1ee"), + "Engineering" = list(jobs = list(), titles = GLOB.engineering_positions, color = "#ffd699"), + "Security" = list(jobs = list(), titles = GLOB.security_positions, color = "#ff9999"), + "Miscellaneous" = list(jobs = list(), titles = list(), color = "#ffffff", colBreak = 1), + "Synthetic" = list(jobs = list(), titles = GLOB.nonhuman_positions, color = "#ccffcc"), + "Support / Service" = list(jobs = list(), titles = GLOB.service_positions, color = "#cccccc"), + "Medical" = list(jobs = list(), titles = GLOB.medical_positions, color = "#99ffe6", colBreak = 1), + "Science" = list(jobs = list(), titles = GLOB.science_positions, color = "#e6b3e6"), + "Supply" = list(jobs = list(), titles = GLOB.supply_positions, color = "#ead4ae"), + ) + for(var/datum/job/job in SSjobs.occupations) + if(job && IsJobAvailable(job.title) && !job.barred_by_disability(client)) + num_jobs_available++ + activePlayers[job] = 0 + var/categorized = 0 + // Only players with the job assigned and AFK for less than 10 minutes count as active + for(var/mob/M in GLOB.player_list) if(M.mind && M.client && M.mind.assigned_role == job.title && M.client.inactivity <= 10 MINUTES) + activePlayers[job]++ + for(var/jobcat in categorizedJobs) + var/list/jobs = categorizedJobs[jobcat]["jobs"] + if(job.title in categorizedJobs[jobcat]["titles"]) + categorized = 1 + if(jobcat == "Command") // Put captain at top of command jobs + if(job.title == "Captain") + jobs.Insert(1, job) + else + jobs += job + else // Put heads at top of non-command jobs + if(job.title in GLOB.command_positions) + jobs.Insert(1, job) + else + jobs += job + if(!categorized) + categorizedJobs["Miscellaneous"]["jobs"] += job + + if(num_jobs_available) + dat += "Choose from the following open positions:

    " + dat += "
    " + for(var/jobcat in categorizedJobs) + if(categorizedJobs[jobcat]["colBreak"]) + dat += "" + if(length(categorizedJobs[jobcat]["jobs"]) < 1) + continue + var/color = categorizedJobs[jobcat]["color"] + dat += "
    " + dat += "[jobcat]" + for(var/datum/job/job in categorizedJobs[jobcat]["jobs"]) + if(job in SSjobs.prioritized_jobs) + dat += "[job.title] ([job.current_positions]) (Active: [activePlayers[job]])
    " + else + dat += "[job.title] ([job.current_positions]) (Active: [activePlayers[job]])
    " + dat += "

    " + + dat += "
    " + else + dat += "

    Unfortunately, there are no job slots free currently.
    Wait a few minutes, then try again.
    Or, try observing the round.
    " + // Removing the old window method but leaving it here for reference +// src << browse(dat, "window=latechoices;size=300x640;can_close=1") + // Added the new browser window method + var/datum/browser/popup = new(src, "latechoices", "Choose Profession", 900, 600) + popup.add_stylesheet("playeroptions", 'html/browser/playeroptions.css') + popup.add_script("delay_interactivity", 'html/browser/delay_interactivity.js') + popup.set_content(dat) + popup.open(0) // 0 is passed to open so that it doesn't use the onclose() proc + +/mob/new_player/proc/create_character() + spawning = 1 + close_spawn_windows() + + check_prefs_are_sane() + var/mob/living/carbon/human/new_character = new(loc) + new_character.lastarea = get_area(loc) + + if(SSticker.random_players || appearance_isbanned(new_character)) + client.prefs.random_character() + client.prefs.real_name = random_name(client.prefs.gender) + client.prefs.copy_to(new_character) + + stop_sound_channel(CHANNEL_LOBBYMUSIC) + + + if(mind) + mind.active = 0 //we wish to transfer the key manually + if(mind.assigned_role == "Clown") //give them a clownname if they are a clown + new_character.real_name = pick(GLOB.clown_names) //I hate this being here of all places but unfortunately dna is based on real_name! + new_character.rename_self("clown") + else if(mind.assigned_role == "Mime") + new_character.real_name = pick(GLOB.mime_names) + new_character.rename_self("mime") + mind.original = new_character + mind.transfer_to(new_character) //won't transfer key since the mind is not active + + + new_character.key = key //Manually transfer the key to log them in + + return new_character + +// This is to check that the player only has preferences set that they're supposed to +/mob/new_player/proc/check_prefs_are_sane() + var/datum/species/chosen_species + if(client.prefs.species) + chosen_species = GLOB.all_species[client.prefs.species] + if(!(chosen_species && (is_species_whitelisted(chosen_species) || has_admin_rights()))) + // Have to recheck admin due to no usr at roundstart. Latejoins are fine though. + log_runtime(EXCEPTION("[src] had species [client.prefs.species], though they weren't supposed to. Setting to Human."), src) + client.prefs.species = "Human" + + var/datum/language/chosen_language + if(client.prefs.language) + chosen_language = GLOB.all_languages[client.prefs.language] + if((chosen_language == null && client.prefs.language != "None") || (chosen_language && chosen_language.flags & RESTRICTED)) + log_runtime(EXCEPTION("[src] had language [client.prefs.language], though they weren't supposed to. Setting to None."), src) + client.prefs.language = "None" + +/mob/new_player/proc/ViewManifest() + var/dat = "" + dat += "

    Crew Manifest

    " + dat += GLOB.data_core.get_manifest(OOC = 1) + + src << browse(dat, "window=manifest;size=370x420;can_close=1") + +/mob/new_player/Move() + return 0 + + +/mob/new_player/proc/close_spawn_windows() + src << browse(null, "window=latechoices") //closes late choices window + src << browse(null, "window=playersetup") //closes the player setup window + src << browse(null, "window=preferences") //closes job selection + src << browse(null, "window=mob_occupation") + src << browse(null, "window=latechoices") //closes late job selection + + +/mob/new_player/proc/has_admin_rights() + return check_rights(R_ADMIN, 0, src) + +/mob/new_player/proc/is_species_whitelisted(datum/species/S) + if(!S) return 1 + return is_alien_whitelisted(src, S.name) || !config.usealienwhitelist || !(IS_WHITELISTED in S.species_traits) + +/mob/new_player/get_gender() + if(!client || !client.prefs) ..() + return client.prefs.gender + +/mob/new_player/is_ready() + return ready && ..() + +// No hearing announcements +/mob/new_player/can_hear() + return 0 diff --git a/code/modules/mob/new_player/poll.dm b/code/modules/mob/new_player/poll.dm index ecd690c871d4..8a9b71426b88 100644 --- a/code/modules/mob/new_player/poll.dm +++ b/code/modules/mob/new_player/poll.dm @@ -1,661 +1,661 @@ -/datum/polloption - var/optionid - var/optiontext - -/client/verb/polls_verb() - set name = "Show Player Polls" - set category = "OOC" - handle_player_polling() - -/client/proc/can_vote() - return istext(player_age) || player_age >= 30 - -/client/proc/handle_player_polling() - establish_db_connection() - if(dbcon.IsConnected()) - var/isadmin = 0 - if(holder) - isadmin = 1 - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, question, (id IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = '[ckey]') OR id IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = '[ckey]')) AS voted FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime") - select_query.Execute() - - var/output = "
    Player polls" - if(check_rights(R_SERVER)) - output += "(Create new poll)" - output +="
    " - - output += "" - var/color1 = "#ececec" - var/color2 = "#e2e2e2" - var/i = 0 - - output += "" - while(select_query.NextRow()) - var/pollid = select_query.item[1] - var/pollquestion = select_query.item[2] - var/voted = text2num(select_query.item[3]) - output += "" - if(can_vote() && !voted) - output += "" - i++ - - // Show expired polls. Non admins can view admin polls at this stage - // (just like tgstation's web interface so don't complain) - // (Also why was there no ingame interface tg not having an ingame - // interface is retarded because it cucks downstreams) - select_query = dbcon.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() > endtime ORDER BY id DESC") - select_query.Execute() - - output += "" - while(select_query.NextRow()) - var/pollid = select_query.item[1] - var/pollquestion = select_query.item[2] - output += "" - - output += "
    Active Polls
    [pollquestion]
    [poll_player(pollid, 1)]
    Expired Polls
    [pollquestion]
    " - - src << browse(output,"window=playerpolllist;size=500x300") - -/client/proc/poll_results(var/pollid = -1) - if(pollid == -1) - return - establish_db_connection() - if(!dbcon.IsConnected()) - return - var/DBQuery/select_query = dbcon.NewQuery("SELECT polltype, question, adminonly, multiplechoiceoptions, starttime, endtime FROM [format_table_name("poll_question")] WHERE id = [pollid] AND endtime < Now()") - select_query.Execute() - var/question = "" - var/polltype = "" - var/adminonly = 0 - var/multiplechoiceoptions = 0 - var/starttime = "" - var/endtime = "" - var/found = 0 - while(select_query.NextRow()) - polltype = select_query.item[1] - question = select_query.item[2] - adminonly = text2num(select_query.item[3]) - multiplechoiceoptions = text2num(select_query.item[4]) - starttime = select_query.item[5] - endtime = select_query.item[6] - found = 1 - break - if(!found) - to_chat(src, "Poll question details not found. (Maybe the poll isn't expired yet?)") - return - - if(polltype == POLLTYPE_MULTI) - question += " (Choose up to [multiplechoiceoptions] options)" - if(adminonly) - question = "(Admin only poll) " + question - - var output = "" - if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION) - select_query = dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]"); - select_query.Execute() - var/list/options = list() - var/total_votes = 1 - var/total_percent_votes = 1 - var/max_votes = 1 - while(select_query.NextRow()) - var/text = select_query.item[1] - var/percentagecalc = select_query.item[2] - var/votecount = text2num(select_query.item[3]) - if(percentagecalc) - total_percent_votes += votecount - total_votes += votecount - if(votecount > max_votes) - max_votes = votecount - options[++options.len] = list(text, percentagecalc, votecount) - // fuck ie. - output += {" - - - - - - " - for(var/list/option in options) - var/bar_width = option[3] * 390 / max_votes - var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" - output += "" - output += "" - output += "" - output += "" - output += "
    [question]
    [starttime] - [endtime]
    "} - var/list/colors = list("#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3") - var/color_index = 0 - for(var/list/option in options) - var/bar_width = option[3] * 700 / total_votes - var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" - color_index++ - if(color_index > colors.len) - color_index = 1 - output += "
    " - output += "

    (Hover over the colored bar to read description)
    [option[1]][option[3]][percentage]
    " - if(polltype == POLLTYPE_RATING) - output += {" - - - - "} - select_query = dbcon.NewQuery("SELECT id, text, (SELECT AVG(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS avgrating, (SELECT COUNT(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS countvotes, minval, maxval FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - select_query.Execute() - while(select_query.NextRow()) - output += {" - - " - output += "
    [question]
    [starttime] - [endtime]
    [select_query.item[2]] - N = [select_query.item[4]] - AVG = [select_query.item[3]] - "} - var/optionid = select_query.item[1] - var/totalvotes = text2num(select_query.item[4]) - var/minval = text2num(select_query.item[5]) - var/maxval = text2num(select_query.item[6]) - var/maxvote = 1 - var/list/votecounts = list() - for(var/I in minval to maxval) - var/DBQuery/rating_query = dbcon.NewQuery("SELECT COUNT(rating) AS countrating FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND rating = [I] GROUP BY rating") - rating_query.Execute() - var/votecount = 0 - while(rating_query.NextRow()) - votecount = text2num(rating_query.item[1]) - votecounts["[I]"] = votecount - if(votecount > maxvote) - maxvote = votecount - for(var/I in minval to maxval) - var/votecount = votecounts["[I]"] - var/bar_width = votecount * 200 / maxvote - output += {" - - - - - - "} - output += "
    [I][votecount]([votecount / totalvotes]%)
    " - if(polltype == POLLTYPE_TEXT) - select_query = dbcon.NewQuery("SELECT replytext, COUNT(replytext) AS countresponse, GROUP_CONCAT(DISTINCT ckey SEPARATOR ', ') as ckeys FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] GROUP BY replytext ORDER BY countresponse DESC"); - select_query.Execute() - output += {" - - - - "} - while(select_query.NextRow()) - var/replytext = select_query.item[1] - var/countresponse = select_query.item[2] - var/ckeys = select_query.item[3] - output += {" - - - - "} - output += "
    [question]
    [starttime] - [endtime]
    [ckeys] ([countresponse] player\s) responded with:[replytext]
    " - output += "" - - src << browse(output,"window=pollresults;size=950x500") - -/client/proc/poll_player(var/pollid = -1, var/inline = 0) - if(pollid == -1) return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/pollstarttime = "" - var/pollendtime = "" - var/pollquestion = "" - var/polltype = "" - var/found = 0 - var/multiplechoiceoptions = 0 - var/canvote = can_vote() - - while(select_query.NextRow()) - pollstarttime = select_query.item[1] - pollendtime = select_query.item[2] - pollquestion = select_query.item[3] - polltype = select_query.item[4] - found = 1 - break - - if(!found) - to_chat(usr, "Poll question details not found. (Maybe you do not have access?)") - return - - switch(polltype) - //Polls that have enumerated options - if(POLLTYPE_OPTION) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - var/voted = 0 // If the can't vote then consider them voted - var/votedoptionid = 0 - while(voted_query.NextRow()) - votedoptionid = text2num(voted_query.item[1]) - voted = 1 - break - - var/list/datum/polloption/options = list() - - var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - options_query.Execute() - while(options_query.NextRow()) - var/datum/polloption/PO = new() - PO.optionid = text2num(options_query.item[1]) - PO.optiontext = options_query.item[2] - options += PO - - var/output - if(!inline) - output += "
    Player poll" - output +="
    " - output += "Question: [pollquestion]
    " - output += "Poll runs from [pollstarttime] until [pollendtime]

    " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

    " - output += "" - output += "" - - output += "
    " - for(var/datum/polloption/O in options) - if(O.optionid && O.optiontext) - if(voted || !canvote) - if(votedoptionid == O.optionid) - output += "[O.optiontext]
    " - else - output += "[O.optiontext]
    " - else - output += " [O.optiontext]
    " - output += "
    " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

    " - output += "

    " - - output += "
    " - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=500x250") - - //Polls with a text input - if(POLLTYPE_TEXT) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - var/voted = 0 - var/vote_text = "" - while(voted_query.NextRow()) - vote_text = voted_query.item[1] - voted = 1 - break - - - var/output - if(!inline) - output += "
    Player poll" - output +="
    " - output += "Question: [pollquestion]
    " - output += "Feedback gathering runs from [pollstarttime] until [pollendtime]

    " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

    " - output += "" - output += "" - - output += "Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; -
    " - output += "" - - output += "

    " - output += "

    " - - output += "
    " - output += "" - output += "" - output += "" - output += "" - output += "
    " - else - output += "[vote_text]" - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=500x500") - - //Polls with a text input - if(POLLTYPE_RATING) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid") - voted_query.Execute() - - var/output - if(!inline) - output += "
    Player poll" - output +="
    " - output += "Question: [pollquestion]
    " - output += "Poll runs from [pollstarttime] until [pollendtime]

    " - - var/voted = 0 - while(voted_query.NextRow()) - voted = 1 - - var/optiontext = voted_query.item[1] - var/rating = voted_query.item[2] - - output += "
    [optiontext] - [rating]" - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

    " - output += "" - output += "" - - var/minid = 999999 - var/maxid = 0 - - var/DBQuery/option_query = dbcon.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - option_query.Execute() - while(option_query.NextRow()) - var/optionid = text2num(option_query.item[1]) - var/optiontext = option_query.item[2] - var/minvalue = text2num(option_query.item[3]) - var/maxvalue = text2num(option_query.item[4]) - var/descmin = option_query.item[5] - var/descmid = option_query.item[6] - var/descmax = option_query.item[7] - - if(optionid < minid) - minid = optionid - if(optionid > maxid) - maxid = optionid - - var/midvalue = round( (maxvalue + minvalue) / 2) - - if(isnull(minvalue) || isnull(maxvalue) || (minvalue == maxvalue)) - continue - - output += "
    [optiontext]: " - - output += "" - output += "" - - output += "

    " - output += "

    " - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=500x500") - if(POLLTYPE_MULTI) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - var/list/votedfor = list() - var/voted = 0 - while(voted_query.NextRow()) - votedfor.Add(text2num(voted_query.item[1])) - voted = 1 - - var/list/datum/polloption/options = list() - var/maxoptionid = 0 - var/minoptionid = 0 - - var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - options_query.Execute() - while(options_query.NextRow()) - var/datum/polloption/PO = new() - PO.optionid = text2num(options_query.item[1]) - PO.optiontext = options_query.item[2] - if(PO.optionid > maxoptionid) - maxoptionid = PO.optionid - if(PO.optionid < minoptionid || !minoptionid) - minoptionid = PO.optionid - options += PO - - - if(select_query.item[5]) - multiplechoiceoptions = text2num(select_query.item[5]) - - var/output - if(!inline) - output += "
    Player poll" - output +="
    " - output += "Question: [pollquestion]
    You can select up to [multiplechoiceoptions] options. If you select more, the first [multiplechoiceoptions] will be saved.
    " - output += "Poll runs from [pollstarttime] until [pollendtime]

    " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

    " - output += "" - output += "" - output += "" - output += "" - - output += "
    " - for(var/datum/polloption/O in options) - if(O.optionid && O.optiontext) - if(canvote && voted) - if(O.optionid in votedfor) - output += "[O.optiontext]
    " - else - output += "[O.optiontext]
    " - else - output += " [O.optiontext]
    " - output += "
    " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

    " - output += "

    " - - output += "
    " - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=600x250") - return - -/client/proc/vote_on_poll(var/pollid = -1, var/optionid = -1, var/multichoice = 0) - if(pollid == -1 || optionid == -1) - return - - if(!isnum(pollid) || !isnum(optionid)) - return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/validpoll = 0 - var/multiplechoiceoptions = 0 - - while(select_query.NextRow()) - if(select_query.item[4] != POLLTYPE_OPTION && select_query.item[4] != POLLTYPE_MULTI) - return - validpoll = 1 - if(select_query.item[5]) - multiplechoiceoptions = text2num(select_query.item[5]) - break - - if(!validpoll) - to_chat(usr, "Poll is not valid.") - return - - var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") - select_query2.Execute() - - var/validoption = 0 - - while(select_query2.NextRow()) - validoption = 1 - break - - if(!validoption) - to_chat(usr, "Poll option is not valid.") - return - - var/alreadyvoted = 0 - - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - while(voted_query.NextRow()) - alreadyvoted += 1 - if(!multichoice) - break - - if(!multichoice && alreadyvoted) - to_chat(usr, "You already voted in this poll.") - return - - if(multichoice && (alreadyvoted >= multiplechoiceoptions)) - to_chat(usr, "You already have more than [multiplechoiceoptions] logged votes on this poll. Enough is enough. Contact the database admin if this is an error.") - return - - var/adminrank = "Player" - if(usr && usr.client && usr.client.holder) - adminrank = usr.client.holder.rank - - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]')") - insert_query.Execute() - - to_chat(usr, "Vote successful.") - usr << browse(null,"window=playerpoll") - - -/client/proc/log_text_poll_reply(var/pollid = -1, var/replytext = "") - if(pollid == -1 || replytext == "") - return - - if(!isnum(pollid) || !istext(replytext)) - return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/validpoll = 0 - - while(select_query.NextRow()) - if(select_query.item[4] != POLLTYPE_TEXT) - return - validpoll = 1 - break - - if(!validpoll) - to_chat(usr, "Poll is not valid.") - return - - var/alreadyvoted = 0 - - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - while(voted_query.NextRow()) - alreadyvoted = 1 - break - - if(alreadyvoted) - to_chat(usr, "You already sent your feedback for this poll.") - return - - var/adminrank = "Player" - if(usr && usr.client && usr.client.holder) - adminrank = usr.client.holder.rank - - - replytext = replacetext(replytext, "%BR%", "") - replytext = replacetext(replytext, "\n", "%BR%") - var/text_pass = reject_bad_text(replytext,8000) - replytext = replacetext(replytext, "%BR%", "
    ") - - if(!text_pass) - to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.") - return - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')") - insert_query.Execute() - - to_chat(usr, "Feedback logging successful.") - usr << browse(null,"window=playerpoll") - - -/client/proc/vote_on_numval_poll(var/pollid = -1, var/optionid = -1, var/rating = null) - if(pollid == -1 || optionid == -1) - return - - if(!isnum(pollid) || !isnum(optionid)) - return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/validpoll = 0 - - while(select_query.NextRow()) - if(select_query.item[4] != POLLTYPE_RATING) - return - validpoll = 1 - break - - if(!validpoll) - to_chat(usr, "Poll is not valid.") - return - - var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") - select_query2.Execute() - - var/validoption = 0 - - while(select_query2.NextRow()) - validoption = 1 - break - - if(!validoption) - to_chat(usr, "Poll option is not valid.") - return - - var/alreadyvoted = 0 - - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'") - voted_query.Execute() - - while(voted_query.NextRow()) - alreadyvoted = 1 - break - - if(alreadyvoted) - to_chat(usr, "You already voted in this poll.") - return - - var/adminrank = "Player" - if(usr && usr.client && usr.client.holder) - adminrank = usr.client.holder.rank - - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])") - insert_query.Execute() - - to_chat(usr, "Vote successful.") - usr << browse(null,"window=playerpoll") \ No newline at end of file +/datum/polloption + var/optionid + var/optiontext + +/client/verb/polls_verb() + set name = "Show Player Polls" + set category = "OOC" + handle_player_polling() + +/client/proc/can_vote() + return istext(player_age) || player_age >= 30 + +/client/proc/handle_player_polling() + establish_db_connection() + if(GLOB.dbcon.IsConnected()) + var/isadmin = 0 + if(holder) + isadmin = 1 + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT id, question, (id IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = '[ckey]') OR id IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = '[ckey]')) AS voted FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime") + select_query.Execute() + + var/output = "
    Player polls" + if(check_rights(R_SERVER)) + output += "(Create new poll)" + output +="
    " + + output += "" + var/color1 = "#ececec" + var/color2 = "#e2e2e2" + var/i = 0 + + output += "" + while(select_query.NextRow()) + var/pollid = select_query.item[1] + var/pollquestion = select_query.item[2] + var/voted = text2num(select_query.item[3]) + output += "" + if(can_vote() && !voted) + output += "" + i++ + + // Show expired polls. Non admins can view admin polls at this stage + // (just like tgstation's web interface so don't complain) + // (Also why was there no ingame interface tg not having an ingame + // interface is retarded because it cucks downstreams) + select_query = GLOB.dbcon.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() > endtime ORDER BY id DESC") + select_query.Execute() + + output += "" + while(select_query.NextRow()) + var/pollid = select_query.item[1] + var/pollquestion = select_query.item[2] + output += "" + + output += "
    Active Polls
    [pollquestion]
    [poll_player(pollid, 1)]
    Expired Polls
    [pollquestion]
    " + + src << browse(output,"window=playerpolllist;size=500x300") + +/client/proc/poll_results(var/pollid = -1) + if(pollid == -1) + return + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) + return + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT polltype, question, adminonly, multiplechoiceoptions, starttime, endtime FROM [format_table_name("poll_question")] WHERE id = [pollid] AND endtime < Now()") + select_query.Execute() + var/question = "" + var/polltype = "" + var/adminonly = 0 + var/multiplechoiceoptions = 0 + var/starttime = "" + var/endtime = "" + var/found = 0 + while(select_query.NextRow()) + polltype = select_query.item[1] + question = select_query.item[2] + adminonly = text2num(select_query.item[3]) + multiplechoiceoptions = text2num(select_query.item[4]) + starttime = select_query.item[5] + endtime = select_query.item[6] + found = 1 + break + if(!found) + to_chat(src, "Poll question details not found. (Maybe the poll isn't expired yet?)") + return + + if(polltype == POLLTYPE_MULTI) + question += " (Choose up to [multiplechoiceoptions] options)" + if(adminonly) + question = "(Admin only poll) " + question + + var output = "" + if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION) + select_query = GLOB.dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]"); + select_query.Execute() + var/list/options = list() + var/total_votes = 1 + var/total_percent_votes = 1 + var/max_votes = 1 + while(select_query.NextRow()) + var/text = select_query.item[1] + var/percentagecalc = select_query.item[2] + var/votecount = text2num(select_query.item[3]) + if(percentagecalc) + total_percent_votes += votecount + total_votes += votecount + if(votecount > max_votes) + max_votes = votecount + options[++options.len] = list(text, percentagecalc, votecount) + // fuck ie. + output += {" + + + + + + " + for(var/list/option in options) + var/bar_width = option[3] * 390 / max_votes + var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" + output += "" + output += "" + output += "" + output += "" + output += "
    [question]
    [starttime] - [endtime]
    "} + var/list/colors = list("#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3") + var/color_index = 0 + for(var/list/option in options) + var/bar_width = option[3] * 700 / total_votes + var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" + color_index++ + if(color_index > colors.len) + color_index = 1 + output += "
    " + output += "

    (Hover over the colored bar to read description)
    [option[1]][option[3]][percentage]
    " + if(polltype == POLLTYPE_RATING) + output += {" + + + + "} + select_query = GLOB.dbcon.NewQuery("SELECT id, text, (SELECT AVG(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS avgrating, (SELECT COUNT(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS countvotes, minval, maxval FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + select_query.Execute() + while(select_query.NextRow()) + output += {" + + " + output += "
    [question]
    [starttime] - [endtime]
    [select_query.item[2]] + N = [select_query.item[4]] + AVG = [select_query.item[3]] + "} + var/optionid = select_query.item[1] + var/totalvotes = text2num(select_query.item[4]) + var/minval = text2num(select_query.item[5]) + var/maxval = text2num(select_query.item[6]) + var/maxvote = 1 + var/list/votecounts = list() + for(var/I in minval to maxval) + var/DBQuery/rating_query = GLOB.dbcon.NewQuery("SELECT COUNT(rating) AS countrating FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND rating = [I] GROUP BY rating") + rating_query.Execute() + var/votecount = 0 + while(rating_query.NextRow()) + votecount = text2num(rating_query.item[1]) + votecounts["[I]"] = votecount + if(votecount > maxvote) + maxvote = votecount + for(var/I in minval to maxval) + var/votecount = votecounts["[I]"] + var/bar_width = votecount * 200 / maxvote + output += {" + + + + + + "} + output += "
    [I][votecount]([votecount / totalvotes]%)
    " + if(polltype == POLLTYPE_TEXT) + select_query = GLOB.dbcon.NewQuery("SELECT replytext, COUNT(replytext) AS countresponse, GROUP_CONCAT(DISTINCT ckey SEPARATOR ', ') as ckeys FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] GROUP BY replytext ORDER BY countresponse DESC"); + select_query.Execute() + output += {" + + + + "} + while(select_query.NextRow()) + var/replytext = select_query.item[1] + var/countresponse = select_query.item[2] + var/ckeys = select_query.item[3] + output += {" + + + + "} + output += "
    [question]
    [starttime] - [endtime]
    [ckeys] ([countresponse] player\s) responded with:[replytext]
    " + output += "" + + src << browse(output,"window=pollresults;size=950x500") + +/client/proc/poll_player(var/pollid = -1, var/inline = 0) + if(pollid == -1) return + establish_db_connection() + if(GLOB.dbcon.IsConnected()) + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/pollstarttime = "" + var/pollendtime = "" + var/pollquestion = "" + var/polltype = "" + var/found = 0 + var/multiplechoiceoptions = 0 + var/canvote = can_vote() + + while(select_query.NextRow()) + pollstarttime = select_query.item[1] + pollendtime = select_query.item[2] + pollquestion = select_query.item[3] + polltype = select_query.item[4] + found = 1 + break + + if(!found) + to_chat(usr, "Poll question details not found. (Maybe you do not have access?)") + return + + switch(polltype) + //Polls that have enumerated options + if(POLLTYPE_OPTION) + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + var/voted = 0 // If the can't vote then consider them voted + var/votedoptionid = 0 + while(voted_query.NextRow()) + votedoptionid = text2num(voted_query.item[1]) + voted = 1 + break + + var/list/datum/polloption/options = list() + + var/DBQuery/options_query = GLOB.dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + options_query.Execute() + while(options_query.NextRow()) + var/datum/polloption/PO = new() + PO.optionid = text2num(options_query.item[1]) + PO.optiontext = options_query.item[2] + options += PO + + var/output + if(!inline) + output += "
    Player poll" + output +="
    " + output += "Question: [pollquestion]
    " + output += "Poll runs from [pollstarttime] until [pollendtime]

    " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

    " + output += "" + output += "" + + output += "
    " + for(var/datum/polloption/O in options) + if(O.optionid && O.optiontext) + if(voted || !canvote) + if(votedoptionid == O.optionid) + output += "[O.optiontext]
    " + else + output += "[O.optiontext]
    " + else + output += " [O.optiontext]
    " + output += "
    " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

    " + output += "

    " + + output += "
    " + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=500x250") + + //Polls with a text input + if(POLLTYPE_TEXT) + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + var/voted = 0 + var/vote_text = "" + while(voted_query.NextRow()) + vote_text = voted_query.item[1] + voted = 1 + break + + + var/output + if(!inline) + output += "
    Player poll" + output +="
    " + output += "Question: [pollquestion]
    " + output += "Feedback gathering runs from [pollstarttime] until [pollendtime]

    " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

    " + output += "" + output += "" + + output += "Please provide feedback below. You can use any letters of the English alphabet, numbers and the symbols: . , ! ? : ; -
    " + output += "" + + output += "

    " + output += "

    " + + output += "
    " + output += "" + output += "" + output += "" + output += "" + output += "
    " + else + output += "[vote_text]" + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=500x500") + + //Polls with a text input + if(POLLTYPE_RATING) + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid") + voted_query.Execute() + + var/output + if(!inline) + output += "
    Player poll" + output +="
    " + output += "Question: [pollquestion]
    " + output += "Poll runs from [pollstarttime] until [pollendtime]

    " + + var/voted = 0 + while(voted_query.NextRow()) + voted = 1 + + var/optiontext = voted_query.item[1] + var/rating = voted_query.item[2] + + output += "
    [optiontext] - [rating]" + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

    " + output += "" + output += "" + + var/minid = 999999 + var/maxid = 0 + + var/DBQuery/option_query = GLOB.dbcon.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + option_query.Execute() + while(option_query.NextRow()) + var/optionid = text2num(option_query.item[1]) + var/optiontext = option_query.item[2] + var/minvalue = text2num(option_query.item[3]) + var/maxvalue = text2num(option_query.item[4]) + var/descmin = option_query.item[5] + var/descmid = option_query.item[6] + var/descmax = option_query.item[7] + + if(optionid < minid) + minid = optionid + if(optionid > maxid) + maxid = optionid + + var/midvalue = round( (maxvalue + minvalue) / 2) + + if(isnull(minvalue) || isnull(maxvalue) || (minvalue == maxvalue)) + continue + + output += "
    [optiontext]: " + + output += "" + output += "" + + output += "

    " + output += "

    " + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=500x500") + if(POLLTYPE_MULTI) + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + var/list/votedfor = list() + var/voted = 0 + while(voted_query.NextRow()) + votedfor.Add(text2num(voted_query.item[1])) + voted = 1 + + var/list/datum/polloption/options = list() + var/maxoptionid = 0 + var/minoptionid = 0 + + var/DBQuery/options_query = GLOB.dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + options_query.Execute() + while(options_query.NextRow()) + var/datum/polloption/PO = new() + PO.optionid = text2num(options_query.item[1]) + PO.optiontext = options_query.item[2] + if(PO.optionid > maxoptionid) + maxoptionid = PO.optionid + if(PO.optionid < minoptionid || !minoptionid) + minoptionid = PO.optionid + options += PO + + + if(select_query.item[5]) + multiplechoiceoptions = text2num(select_query.item[5]) + + var/output + if(!inline) + output += "
    Player poll" + output +="
    " + output += "Question: [pollquestion]
    You can select up to [multiplechoiceoptions] options. If you select more, the first [multiplechoiceoptions] will be saved.
    " + output += "Poll runs from [pollstarttime] until [pollendtime]

    " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

    " + output += "" + output += "" + output += "" + output += "" + + output += "
    " + for(var/datum/polloption/O in options) + if(O.optionid && O.optiontext) + if(canvote && voted) + if(O.optionid in votedfor) + output += "[O.optiontext]
    " + else + output += "[O.optiontext]
    " + else + output += " [O.optiontext]
    " + output += "
    " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

    " + output += "

    " + + output += "
    " + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=600x250") + return + +/client/proc/vote_on_poll(var/pollid = -1, var/optionid = -1, var/multichoice = 0) + if(pollid == -1 || optionid == -1) + return + + if(!isnum(pollid) || !isnum(optionid)) + return + establish_db_connection() + if(GLOB.dbcon.IsConnected()) + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/validpoll = 0 + var/multiplechoiceoptions = 0 + + while(select_query.NextRow()) + if(select_query.item[4] != POLLTYPE_OPTION && select_query.item[4] != POLLTYPE_MULTI) + return + validpoll = 1 + if(select_query.item[5]) + multiplechoiceoptions = text2num(select_query.item[5]) + break + + if(!validpoll) + to_chat(usr, "Poll is not valid.") + return + + var/DBQuery/select_query2 = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") + select_query2.Execute() + + var/validoption = 0 + + while(select_query2.NextRow()) + validoption = 1 + break + + if(!validoption) + to_chat(usr, "Poll option is not valid.") + return + + var/alreadyvoted = 0 + + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + while(voted_query.NextRow()) + alreadyvoted += 1 + if(!multichoice) + break + + if(!multichoice && alreadyvoted) + to_chat(usr, "You already voted in this poll.") + return + + if(multichoice && (alreadyvoted >= multiplechoiceoptions)) + to_chat(usr, "You already have more than [multiplechoiceoptions] logged votes on this poll. Enough is enough. Contact the database admin if this is an error.") + return + + var/adminrank = "Player" + if(usr && usr.client && usr.client.holder) + adminrank = usr.client.holder.rank + + + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]')") + insert_query.Execute() + + to_chat(usr, "Vote successful.") + usr << browse(null,"window=playerpoll") + + +/client/proc/log_text_poll_reply(var/pollid = -1, var/replytext = "") + if(pollid == -1 || replytext == "") + return + + if(!isnum(pollid) || !istext(replytext)) + return + establish_db_connection() + if(GLOB.dbcon.IsConnected()) + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/validpoll = 0 + + while(select_query.NextRow()) + if(select_query.item[4] != POLLTYPE_TEXT) + return + validpoll = 1 + break + + if(!validpoll) + to_chat(usr, "Poll is not valid.") + return + + var/alreadyvoted = 0 + + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + while(voted_query.NextRow()) + alreadyvoted = 1 + break + + if(alreadyvoted) + to_chat(usr, "You already sent your feedback for this poll.") + return + + var/adminrank = "Player" + if(usr && usr.client && usr.client.holder) + adminrank = usr.client.holder.rank + + + replytext = replacetext(replytext, "%BR%", "") + replytext = replacetext(replytext, "\n", "%BR%") + var/text_pass = reject_bad_text(replytext,8000) + replytext = replacetext(replytext, "%BR%", "
    ") + + if(!text_pass) + to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.") + return + + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')") + insert_query.Execute() + + to_chat(usr, "Feedback logging successful.") + usr << browse(null,"window=playerpoll") + + +/client/proc/vote_on_numval_poll(var/pollid = -1, var/optionid = -1, var/rating = null) + if(pollid == -1 || optionid == -1) + return + + if(!isnum(pollid) || !isnum(optionid)) + return + establish_db_connection() + if(GLOB.dbcon.IsConnected()) + + var/DBQuery/select_query = GLOB.dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/validpoll = 0 + + while(select_query.NextRow()) + if(select_query.item[4] != POLLTYPE_RATING) + return + validpoll = 1 + break + + if(!validpoll) + to_chat(usr, "Poll is not valid.") + return + + var/DBQuery/select_query2 = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") + select_query2.Execute() + + var/validoption = 0 + + while(select_query2.NextRow()) + validoption = 1 + break + + if(!validoption) + to_chat(usr, "Poll option is not valid.") + return + + var/alreadyvoted = 0 + + var/DBQuery/voted_query = GLOB.dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'") + voted_query.Execute() + + while(voted_query.NextRow()) + alreadyvoted = 1 + break + + if(alreadyvoted) + to_chat(usr, "You already voted in this poll.") + return + + var/adminrank = "Player" + if(usr && usr.client && usr.client.holder) + adminrank = usr.client.holder.rank + + + var/DBQuery/insert_query = GLOB.dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])") + insert_query.Execute() + + to_chat(usr, "Vote successful.") + usr << browse(null,"window=playerpoll") diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 4382e209a82f..8324ef87212a 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -1,957 +1,957 @@ -/datum/preferences - //The mob should have a gender you want before running this proc. Will run fine without H -/datum/preferences/proc/random_character(gender_override) - var/datum/species/S = GLOB.all_species[species] - if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character. - species = initial(species) - S = GLOB.all_species[species] - var/datum/robolimb/robohead - - if(S.bodyflags & ALL_RPARTS) - var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" - robohead = all_robolimbs[head_model] - if(gender_override) - gender = gender_override - else - gender = pick(MALE, FEMALE) - underwear = random_underwear(gender, species) - undershirt = random_undershirt(gender, species) - socks = random_socks(gender, species) - if(body_accessory_by_species[species]) - body_accessory = random_body_accessory(species) - if(body_accessory == "None") //Required to prevent a bug where the information/icons in the character preferences screen wouldn't update despite the data being changed. - body_accessory = null - if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE)) - s_tone = random_skin_tone(species) - h_style = random_hair_style(gender, species, robohead) - f_style = random_facial_hair_style(gender, species, robohead) - if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox")) - randomize_hair_color("hair") - randomize_hair_color("facial") - if(S.bodyflags & HAS_HEAD_ACCESSORY) - ha_style = random_head_accessory(species) - hacc_colour = randomize_skin_color(1) - if(S.bodyflags & HAS_HEAD_MARKINGS) - m_styles["head"] = random_marking_style("head", species, robohead, null, alt_head) - m_colours["head"] = randomize_skin_color(1) - if(S.bodyflags & HAS_BODY_MARKINGS) - m_styles["body"] = random_marking_style("body", species) - m_colours["body"] = randomize_skin_color(1) - if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. - m_styles["tail"] = random_marking_style("tail", species, null, body_accessory) - m_colours["tail"] = randomize_skin_color(1) - if(!(S.bodyflags & ALL_RPARTS)) - randomize_eyes_color() - if(S.bodyflags & HAS_SKIN_COLOR) - randomize_skin_color() - backbag = 2 - age = rand(AGE_MIN, AGE_MAX) - - -/datum/preferences/proc/randomize_hair_color(var/target = "hair") - if(prob (75) && target == "facial") // Chance to inherit hair color - f_colour = h_colour - return - - var/red - var/green - var/blue - - var/col = pick ("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", "punk") - switch(col) - if("blonde") - red = 255 - green = 255 - blue = 0 - if("black") - red = 0 - green = 0 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 51 - if("copper") - red = 255 - green = 153 - blue = 0 - if("brown") - red = 102 - green = 51 - blue = 0 - if("wheat") - red = 255 - green = 255 - blue = 153 - if("old") - red = rand (100, 255) - green = red - blue = red - if("punk") - red = rand (0, 255) - green = rand (0, 255) - blue = rand (0, 255) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - switch(target) - if("hair") - h_colour = rgb(red, green, blue) - if("facial") - f_colour = rgb(red, green, blue) - -/datum/preferences/proc/randomize_eyes_color() - var/red - var/green - var/blue - - var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") - switch(col) - if("black") - red = 0 - green = 0 - blue = 0 - if("grey") - red = rand (100, 200) - green = red - blue = red - if("brown") - red = 102 - green = 51 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 0 - if("blue") - red = 51 - green = 102 - blue = 204 - if("lightblue") - red = 102 - green = 204 - blue = 255 - if("green") - red = 0 - green = 102 - blue = 0 - if("albino") - red = rand (200, 255) - green = rand (0, 150) - blue = rand (0, 150) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - e_colour = rgb(red, green, blue) - -/datum/preferences/proc/randomize_skin_color(var/pass_on) - var/red - var/green - var/blue - - var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") - switch(col) - if("black") - red = 0 - green = 0 - blue = 0 - if("grey") - red = rand (100, 200) - green = red - blue = red - if("brown") - red = 102 - green = 51 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 0 - if("blue") - red = 51 - green = 102 - blue = 204 - if("lightblue") - red = 102 - green = 204 - blue = 255 - if("green") - red = 0 - green = 102 - blue = 0 - if("albino") - red = rand (200, 255) - green = rand (0, 150) - blue = rand (0, 150) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - if(pass_on) - return rgb(red, green, blue) - else - s_colour = rgb(red, green, blue) - -/datum/preferences/proc/blend_backpack(var/icon/clothes_s,var/backbag,var/satchel,var/backpack="backpack") - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', satchel), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - return clothes_s - -/datum/preferences/proc/update_preview_icon(var/for_observer=0) //seriously. This is horrendous. - qdel(preview_icon_front) - qdel(preview_icon_side) - qdel(preview_icon) - - var/g = "m" - if(gender == FEMALE) g = "f" - - var/icon/icobase - var/datum/species/current_species = GLOB.all_species[species] - - //Icon-based species colour. - var/coloured_tail - if(current_species) - if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race. - var/mob/living/carbon/human/H = new - H.dna.species = current_species - H.s_tone = s_tone - H.dna.species.updatespeciescolor(H, 0) //The mob's species wasn't set, so it's almost certainly different than the character's species at the moment. Thus, we need to be owner-insensitive. - var/obj/item/organ/external/chest/C = H.get_organ("chest") - icobase = C.icobase ? C.icobase : C.dna.species.icobase - if(H.dna.species.bodyflags & HAS_TAIL) - coloured_tail = H.tail ? H.tail : H.dna.species.tail - - qdel(H) - else - icobase = current_species.icobase - else - icobase = 'icons/mob/human_races/r_human.dmi' - - var/fat="" - if(disabilities & DISABILITY_FLAG_FAT && (CAN_BE_FAT in current_species.species_traits)) - fat="_fat" - preview_icon = new /icon(icobase, "torso_[g][fat]") - preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY) - var/head = "head" - if(alt_head && current_species.bodyflags & HAS_ALT_HEADS) - var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head] - if(H.icon_state) - head = H.icon_state - preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY) - - for(var/name in list("chest", "groin", "head", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "l_arm", "l_hand")) - if(organ_data[name] == "amputated") continue - if(organ_data[name] == "cyborg") - var/datum/robolimb/R - if(rlimb_data[name]) R = all_robolimbs[rlimb_data[name]] - if(!R) R = basic_robolimb - if(name == "chest") - name = "torso" - preview_icon.Blend(icon(R.icon, "[name]"), ICON_OVERLAY) // This doesn't check gendered_icon. Not an issue while only limbs can be robotic. - continue - preview_icon.Blend(new /icon(icobase, "[name]"), ICON_OVERLAY) - - // Skin color - if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) - preview_icon.Blend(s_colour, ICON_ADD) - - // Skin tone - if(current_species && (current_species.bodyflags & HAS_SKIN_TONE)) - if(s_tone >= 0) - preview_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD) - else - preview_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) - - //Tail - if(current_species && (current_species.bodyflags & HAS_TAIL)) - var/tail_icon - var/tail_icon_state - var/tail_shift_x - var/tail_shift_y - var/blend_mode = ICON_ADD - - if(body_accessory) - var/datum/body_accessory/accessory = body_accessory_by_name[body_accessory] - tail_icon = accessory.icon - tail_icon_state = accessory.icon_state - if(accessory.blend_mode) - blend_mode = accessory.blend_mode - if(accessory.pixel_x_offset) - tail_shift_x = accessory.pixel_x_offset - if(accessory.pixel_y_offset) - tail_shift_y = accessory.pixel_y_offset - else - tail_icon = "icons/effects/species.dmi" - if(coloured_tail) - tail_icon_state = "[coloured_tail]_s" - else - tail_icon_state = "[current_species.tail]_s" - - var/icon/temp = new/icon("icon" = tail_icon, "icon_state" = tail_icon_state) - if(tail_shift_x) - temp.Shift(EAST, tail_shift_x) - if(tail_shift_y) - temp.Shift(NORTH, tail_shift_y) - - if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) - temp.Blend(s_colour, blend_mode) - - if(current_species && (current_species.bodyflags & HAS_TAIL_MARKINGS)) - var/tail_marking = m_styles["tail"] - var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] - if(tail_marking_style && tail_marking_style.species_allowed) - var/icon/t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") - t_marking_s.Blend(m_colours["tail"], ICON_ADD) - temp.Blend(t_marking_s, ICON_OVERLAY) - - preview_icon.Blend(temp, ICON_OVERLAY) - - //Markings - if(current_species && ((current_species.bodyflags & HAS_HEAD_MARKINGS) || (current_species.bodyflags & HAS_BODY_MARKINGS))) - if(current_species.bodyflags & HAS_BODY_MARKINGS) //Body markings. - var/body_marking = m_styles["body"] - var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] - if(body_marking_style && body_marking_style.species_allowed) - var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") - b_marking_s.Blend(m_colours["body"], ICON_ADD) - preview_icon.Blend(b_marking_s, ICON_OVERLAY) - if(current_species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. - var/head_marking = m_styles["head"] - var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] - if(head_marking_style && head_marking_style.species_allowed) - var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") - h_marking_s.Blend(m_colours["head"], ICON_ADD) - preview_icon.Blend(h_marking_s, ICON_OVERLAY) - - - var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") - if(!(current_species.bodyflags & NO_EYES)) - var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = current_species ? current_species.eyes : "eyes_s") - eyes_s.Blend(e_colour, ICON_ADD) - face_s.Blend(eyes_s, ICON_OVERLAY) - - - var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style] - if(hair_style) - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - if(current_species.name == "Slime People") // whee I am part of the problem - hair_s.Blend("[s_colour]A0", ICON_ADD) - else if(hair_style.do_colouration) - hair_s.Blend(h_colour, ICON_ADD) - - if(hair_style.secondary_theme) - var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") - if(!hair_style.no_sec_colour && hair_style.do_colouration ) - hair_secondary_s.Blend(h_sec_colour, ICON_ADD) - hair_s.Blend(hair_secondary_s, ICON_OVERLAY) - - face_s.Blend(hair_s, ICON_OVERLAY) - - //Head Accessory - if(current_species && (current_species.bodyflags & HAS_HEAD_ACCESSORY)) - var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[ha_style] - if(head_accessory_style && head_accessory_style.species_allowed) - var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") - head_accessory_s.Blend(hacc_colour, ICON_ADD) - face_s.Blend(head_accessory_s, ICON_OVERLAY) - - var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style] - if(facial_hair_style && facial_hair_style.species_allowed) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(current_species.name == "Slime People") // whee I am part of the problem - facial_s.Blend("[s_colour]A0", ICON_ADD) - else if(facial_hair_style.do_colouration) - facial_s.Blend(f_colour, ICON_ADD) - - if(facial_hair_style.secondary_theme) - var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") - if(!facial_hair_style.no_sec_colour && facial_hair_style.do_colouration) - facial_secondary_s.Blend(f_sec_colour, ICON_ADD) - facial_s.Blend(facial_secondary_s, ICON_OVERLAY) - - face_s.Blend(facial_s, ICON_OVERLAY) - - var/icon/underwear_s = null - if(underwear && (current_species.clothing_flags & HAS_UNDERWEAR)) - var/datum/sprite_accessory/underwear/U = GLOB.underwear_list[underwear] - if(U) - var/u_icon = U.sprite_sheets && (current_species.name in U.sprite_sheets) ? U.sprite_sheets[current_species.name] : U.icon //Species-fit the undergarment. - underwear_s = new/icon(u_icon, "uw_[U.icon_state]_s", ICON_OVERLAY) - - var/icon/undershirt_s = null - if(undershirt && (current_species.clothing_flags & HAS_UNDERSHIRT)) - var/datum/sprite_accessory/undershirt/U2 = GLOB.undershirt_list[undershirt] - if(U2) - var/u2_icon = U2.sprite_sheets && (current_species.name in U2.sprite_sheets) ? U2.sprite_sheets[current_species.name] : U2.icon - undershirt_s = new/icon(u2_icon, "us_[U2.icon_state]_s", ICON_OVERLAY) - - var/icon/socks_s = null - if(socks && (current_species.clothing_flags & HAS_SOCKS)) - var/datum/sprite_accessory/socks/U3 = GLOB.socks_list[socks] - if(U3) - var/u3_icon = U3.sprite_sheets && (current_species.name in U3.sprite_sheets) ? U3.sprite_sheets[current_species.name] : U3.icon - socks_s = new/icon(u3_icon, "sk_[U3.icon_state]_s", ICON_OVERLAY) - - var/icon/clothes_s = null - var/uniform_dmi='icons/mob/uniform.dmi' - if(disabilities&DISABILITY_FLAG_FAT) - uniform_dmi='icons/mob/uniform_fat.dmi' - if(job_support_low & JOB_CIVILIAN)//This gives the preview icon clothes depending on which job(if any) is set to 'high' - clothes_s = new /icon(uniform_dmi, "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_support_high)//I hate how this looks, but there's no reason to go through this switch if it's empty - switch(job_support_high) - if(JOB_HOP) - clothes_s = new /icon(uniform_dmi, "hop_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "ianshirt"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_BARTENDER) - clothes_s = new /icon(uniform_dmi, "ba_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "tophat"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_BOTANIST) - clothes_s = new /icon(uniform_dmi, "hydroponics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "ggloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apron"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "nymph"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-hyd"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CHEF) - clothes_s = new /icon(uniform_dmi, "chef_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "chef"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apronchef"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_JANITOR) - clothes_s = new /icon(uniform_dmi, "janitor_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_janitor"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_LIBRARIAN) - clothes_s = new /icon(uniform_dmi, "red_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hairflower"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_QUARTERMASTER) - clothes_s = new /icon(uniform_dmi, "qm_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "poncho"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CARGOTECH) - clothes_s = new /icon(uniform_dmi, "cargotech_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "flat_cap"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_MINER) - clothes_s = new /icon(uniform_dmi, "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "bearpelt"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_LAWYER) - clothes_s = new /icon(uniform_dmi, "internalaffairs_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "briefcase"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suitjacket_blue"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CHAPLAIN) - clothes_s = new /icon(uniform_dmi, "chapblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "imperium_monk"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CLOWN) - clothes_s = new /icon(uniform_dmi, "clown_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "clown"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "clownpack"), ICON_OVERLAY) - if(JOB_MIME) - clothes_s = new /icon(uniform_dmi, "mime_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "lgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "mime"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suspenders"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_medsci_high) - switch(job_medsci_high) - if(JOB_RD) - clothes_s = new /icon(uniform_dmi, "director_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "petehat"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_SCIENTIST) - clothes_s = new /icon(uniform_dmi, "toxinswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "metroid"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CHEMIST) - clothes_s = new /icon(uniform_dmi, "chemistrywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labgreen"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-chem"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CMO) - clothes_s = new /icon(uniform_dmi, "cmo_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_cmo"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_DOCTOR) - clothes_s = new /icon(uniform_dmi, "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "surgeon"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CORONER) - clothes_s = new /icon(uniform_dmi, "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "mortician"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_GENETICIST) - clothes_s = new /icon(uniform_dmi, "geneticswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "monkeysuit"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-gen"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_VIROLOGIST) - clothes_s = new /icon(uniform_dmi, "virologywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "sterile"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "plaguedoctor"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-vir"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_PSYCHIATRIST) - clothes_s = new /icon(uniform_dmi, "psych_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_PARAMEDIC) - clothes_s = new /icon(uniform_dmi, "paramedic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigoff"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "bluesoft"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(JOB_ROBOTICIST) - clothes_s = new /icon(uniform_dmi, "robotics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "toolbox_blue"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_engsec_high) - switch(job_engsec_high) - if(JOB_CAPTAIN) - clothes_s = new /icon(uniform_dmi, "captain_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "centcomcaptain"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/head.dmi', "captain"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-cap"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_HOS) - clothes_s = new /icon(uniform_dmi, "hosred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_hos"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_WARDEN) - clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "slippers_worn"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_DETECTIVE) - clothes_s = new /icon(uniform_dmi, "detective_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigaron"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "detective"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_OFFICER) - clothes_s = new /icon(uniform_dmi, "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_officer"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CHIEF) - clothes_s = new /icon(uniform_dmi, "chief_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_white"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "blueprints"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_ENGINEER) - clothes_s = new /icon(uniform_dmi, "engine_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "hazard"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_ATMOSTECH) - clothes_s = new /icon(uniform_dmi, "atmos_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "firesuit"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - if(JOB_AI)//Gives AI and borgs assistant-wear, so they can still customize their character - clothes_s = new /icon(uniform_dmi, "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "straight_jacket"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_CYBORG) - clothes_s = new /icon(uniform_dmi, "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "cardborg"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - else if(job_karma_high) - switch(job_karma_high) - if(JOB_MECHANIC) - clothes_s = new /icon(uniform_dmi, "mechanic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_PILOT) - clothes_s = new /icon(uniform_dmi, "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_BRIGDOC) - clothes_s = new /icon(uniform_dmi, "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_NANO) - clothes_s = new /icon(uniform_dmi, "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_BLUESHIELD) - clothes_s = new /icon(uniform_dmi, "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JOB_JUDGE) - clothes_s = new /icon(uniform_dmi, "really_black_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "mercy_hood"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - if(disabilities & NEARSIGHTED) - preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY) - - // Observers get tourist outfit. - if(for_observer) - clothes_s = new /icon(uniform_dmi, "tourist_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - if(underwear_s) - preview_icon.Blend(underwear_s, ICON_OVERLAY) - if(undershirt_s) - preview_icon.Blend(undershirt_s, ICON_OVERLAY) - if(socks_s) - preview_icon.Blend(socks_s, ICON_OVERLAY) - if(clothes_s) - preview_icon.Blend(clothes_s, ICON_OVERLAY) - preview_icon.Blend(face_s, ICON_OVERLAY) - preview_icon_front = new(preview_icon, dir = SOUTH) - preview_icon_side = new(preview_icon, dir = WEST) - - qdel(face_s) - qdel(underwear_s) - qdel(undershirt_s) - qdel(socks_s) - qdel(clothes_s) +/datum/preferences + //The mob should have a gender you want before running this proc. Will run fine without H +/datum/preferences/proc/random_character(gender_override) + var/datum/species/S = GLOB.all_species[species] + if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character. + species = initial(species) + S = GLOB.all_species[species] + var/datum/robolimb/robohead + + if(S.bodyflags & ALL_RPARTS) + var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" + robohead = GLOB.all_robolimbs[head_model] + if(gender_override) + gender = gender_override + else + gender = pick(MALE, FEMALE) + underwear = random_underwear(gender, species) + undershirt = random_undershirt(gender, species) + socks = random_socks(gender, species) + if(GLOB.body_accessory_by_species[species]) + body_accessory = random_body_accessory(species) + if(body_accessory == "None") //Required to prevent a bug where the information/icons in the character preferences screen wouldn't update despite the data being changed. + body_accessory = null + if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE)) + s_tone = random_skin_tone(species) + h_style = random_hair_style(gender, species, robohead) + f_style = random_facial_hair_style(gender, species, robohead) + if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox")) + randomize_hair_color("hair") + randomize_hair_color("facial") + if(S.bodyflags & HAS_HEAD_ACCESSORY) + ha_style = random_head_accessory(species) + hacc_colour = randomize_skin_color(1) + if(S.bodyflags & HAS_HEAD_MARKINGS) + m_styles["head"] = random_marking_style("head", species, robohead, null, alt_head) + m_colours["head"] = randomize_skin_color(1) + if(S.bodyflags & HAS_BODY_MARKINGS) + m_styles["body"] = random_marking_style("body", species) + m_colours["body"] = randomize_skin_color(1) + if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. + m_styles["tail"] = random_marking_style("tail", species, null, body_accessory) + m_colours["tail"] = randomize_skin_color(1) + if(!(S.bodyflags & ALL_RPARTS)) + randomize_eyes_color() + if(S.bodyflags & HAS_SKIN_COLOR) + randomize_skin_color() + backbag = 2 + age = rand(AGE_MIN, AGE_MAX) + + +/datum/preferences/proc/randomize_hair_color(var/target = "hair") + if(prob (75) && target == "facial") // Chance to inherit hair color + f_colour = h_colour + return + + var/red + var/green + var/blue + + var/col = pick ("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", "punk") + switch(col) + if("blonde") + red = 255 + green = 255 + blue = 0 + if("black") + red = 0 + green = 0 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 51 + if("copper") + red = 255 + green = 153 + blue = 0 + if("brown") + red = 102 + green = 51 + blue = 0 + if("wheat") + red = 255 + green = 255 + blue = 153 + if("old") + red = rand (100, 255) + green = red + blue = red + if("punk") + red = rand (0, 255) + green = rand (0, 255) + blue = rand (0, 255) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + + switch(target) + if("hair") + h_colour = rgb(red, green, blue) + if("facial") + f_colour = rgb(red, green, blue) + +/datum/preferences/proc/randomize_eyes_color() + var/red + var/green + var/blue + + var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") + switch(col) + if("black") + red = 0 + green = 0 + blue = 0 + if("grey") + red = rand (100, 200) + green = red + blue = red + if("brown") + red = 102 + green = 51 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 0 + if("blue") + red = 51 + green = 102 + blue = 204 + if("lightblue") + red = 102 + green = 204 + blue = 255 + if("green") + red = 0 + green = 102 + blue = 0 + if("albino") + red = rand (200, 255) + green = rand (0, 150) + blue = rand (0, 150) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + + e_colour = rgb(red, green, blue) + +/datum/preferences/proc/randomize_skin_color(var/pass_on) + var/red + var/green + var/blue + + var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") + switch(col) + if("black") + red = 0 + green = 0 + blue = 0 + if("grey") + red = rand (100, 200) + green = red + blue = red + if("brown") + red = 102 + green = 51 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 0 + if("blue") + red = 51 + green = 102 + blue = 204 + if("lightblue") + red = 102 + green = 204 + blue = 255 + if("green") + red = 0 + green = 102 + blue = 0 + if("albino") + red = rand (200, 255) + green = rand (0, 150) + blue = rand (0, 150) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + + if(pass_on) + return rgb(red, green, blue) + else + s_colour = rgb(red, green, blue) + +/datum/preferences/proc/blend_backpack(var/icon/clothes_s,var/backbag,var/satchel,var/backpack="backpack") + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', satchel), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + return clothes_s + +/datum/preferences/proc/update_preview_icon(var/for_observer=0) //seriously. This is horrendous. + qdel(preview_icon_front) + qdel(preview_icon_side) + qdel(preview_icon) + + var/g = "m" + if(gender == FEMALE) g = "f" + + var/icon/icobase + var/datum/species/current_species = GLOB.all_species[species] + + //Icon-based species colour. + var/coloured_tail + if(current_species) + if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race. + var/mob/living/carbon/human/H = new + H.dna.species = current_species + H.s_tone = s_tone + H.dna.species.updatespeciescolor(H, 0) //The mob's species wasn't set, so it's almost certainly different than the character's species at the moment. Thus, we need to be owner-insensitive. + var/obj/item/organ/external/chest/C = H.get_organ("chest") + icobase = C.icobase ? C.icobase : C.dna.species.icobase + if(H.dna.species.bodyflags & HAS_TAIL) + coloured_tail = H.tail ? H.tail : H.dna.species.tail + + qdel(H) + else + icobase = current_species.icobase + else + icobase = 'icons/mob/human_races/r_human.dmi' + + var/fat="" + if(disabilities & DISABILITY_FLAG_FAT && (CAN_BE_FAT in current_species.species_traits)) + fat="_fat" + preview_icon = new /icon(icobase, "torso_[g][fat]") + preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY) + var/head = "head" + if(alt_head && current_species.bodyflags & HAS_ALT_HEADS) + var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head] + if(H.icon_state) + head = H.icon_state + preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY) + + for(var/name in list("chest", "groin", "head", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "l_arm", "l_hand")) + if(organ_data[name] == "amputated") continue + if(organ_data[name] == "cyborg") + var/datum/robolimb/R + if(rlimb_data[name]) R = GLOB.all_robolimbs[rlimb_data[name]] + if(!R) R = GLOB.basic_robolimb + if(name == "chest") + name = "torso" + preview_icon.Blend(icon(R.icon, "[name]"), ICON_OVERLAY) // This doesn't check gendered_icon. Not an issue while only limbs can be robotic. + continue + preview_icon.Blend(new /icon(icobase, "[name]"), ICON_OVERLAY) + + // Skin color + if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) + preview_icon.Blend(s_colour, ICON_ADD) + + // Skin tone + if(current_species && (current_species.bodyflags & HAS_SKIN_TONE)) + if(s_tone >= 0) + preview_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD) + else + preview_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) + + //Tail + if(current_species && (current_species.bodyflags & HAS_TAIL)) + var/tail_icon + var/tail_icon_state + var/tail_shift_x + var/tail_shift_y + var/blend_mode = ICON_ADD + + if(body_accessory) + var/datum/body_accessory/accessory = GLOB.body_accessory_by_name[body_accessory] + tail_icon = accessory.icon + tail_icon_state = accessory.icon_state + if(accessory.blend_mode) + blend_mode = accessory.blend_mode + if(accessory.pixel_x_offset) + tail_shift_x = accessory.pixel_x_offset + if(accessory.pixel_y_offset) + tail_shift_y = accessory.pixel_y_offset + else + tail_icon = "icons/effects/species.dmi" + if(coloured_tail) + tail_icon_state = "[coloured_tail]_s" + else + tail_icon_state = "[current_species.tail]_s" + + var/icon/temp = new/icon("icon" = tail_icon, "icon_state" = tail_icon_state) + if(tail_shift_x) + temp.Shift(EAST, tail_shift_x) + if(tail_shift_y) + temp.Shift(NORTH, tail_shift_y) + + if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) + temp.Blend(s_colour, blend_mode) + + if(current_species && (current_species.bodyflags & HAS_TAIL_MARKINGS)) + var/tail_marking = m_styles["tail"] + var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] + if(tail_marking_style && tail_marking_style.species_allowed) + var/icon/t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") + t_marking_s.Blend(m_colours["tail"], ICON_ADD) + temp.Blend(t_marking_s, ICON_OVERLAY) + + preview_icon.Blend(temp, ICON_OVERLAY) + + //Markings + if(current_species && ((current_species.bodyflags & HAS_HEAD_MARKINGS) || (current_species.bodyflags & HAS_BODY_MARKINGS))) + if(current_species.bodyflags & HAS_BODY_MARKINGS) //Body markings. + var/body_marking = m_styles["body"] + var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] + if(body_marking_style && body_marking_style.species_allowed) + var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") + b_marking_s.Blend(m_colours["body"], ICON_ADD) + preview_icon.Blend(b_marking_s, ICON_OVERLAY) + if(current_species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. + var/head_marking = m_styles["head"] + var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] + if(head_marking_style && head_marking_style.species_allowed) + var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") + h_marking_s.Blend(m_colours["head"], ICON_ADD) + preview_icon.Blend(h_marking_s, ICON_OVERLAY) + + + var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") + if(!(current_species.bodyflags & NO_EYES)) + var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = current_species ? current_species.eyes : "eyes_s") + eyes_s.Blend(e_colour, ICON_ADD) + face_s.Blend(eyes_s, ICON_OVERLAY) + + + var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style] + if(hair_style) + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + if(current_species.name == "Slime People") // whee I am part of the problem + hair_s.Blend("[s_colour]A0", ICON_ADD) + else if(hair_style.do_colouration) + hair_s.Blend(h_colour, ICON_ADD) + + if(hair_style.secondary_theme) + var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") + if(!hair_style.no_sec_colour && hair_style.do_colouration ) + hair_secondary_s.Blend(h_sec_colour, ICON_ADD) + hair_s.Blend(hair_secondary_s, ICON_OVERLAY) + + face_s.Blend(hair_s, ICON_OVERLAY) + + //Head Accessory + if(current_species && (current_species.bodyflags & HAS_HEAD_ACCESSORY)) + var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[ha_style] + if(head_accessory_style && head_accessory_style.species_allowed) + var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") + head_accessory_s.Blend(hacc_colour, ICON_ADD) + face_s.Blend(head_accessory_s, ICON_OVERLAY) + + var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style] + if(facial_hair_style && facial_hair_style.species_allowed) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(current_species.name == "Slime People") // whee I am part of the problem + facial_s.Blend("[s_colour]A0", ICON_ADD) + else if(facial_hair_style.do_colouration) + facial_s.Blend(f_colour, ICON_ADD) + + if(facial_hair_style.secondary_theme) + var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") + if(!facial_hair_style.no_sec_colour && facial_hair_style.do_colouration) + facial_secondary_s.Blend(f_sec_colour, ICON_ADD) + facial_s.Blend(facial_secondary_s, ICON_OVERLAY) + + face_s.Blend(facial_s, ICON_OVERLAY) + + var/icon/underwear_s = null + if(underwear && (current_species.clothing_flags & HAS_UNDERWEAR)) + var/datum/sprite_accessory/underwear/U = GLOB.underwear_list[underwear] + if(U) + var/u_icon = U.sprite_sheets && (current_species.name in U.sprite_sheets) ? U.sprite_sheets[current_species.name] : U.icon //Species-fit the undergarment. + underwear_s = new/icon(u_icon, "uw_[U.icon_state]_s", ICON_OVERLAY) + + var/icon/undershirt_s = null + if(undershirt && (current_species.clothing_flags & HAS_UNDERSHIRT)) + var/datum/sprite_accessory/undershirt/U2 = GLOB.undershirt_list[undershirt] + if(U2) + var/u2_icon = U2.sprite_sheets && (current_species.name in U2.sprite_sheets) ? U2.sprite_sheets[current_species.name] : U2.icon + undershirt_s = new/icon(u2_icon, "us_[U2.icon_state]_s", ICON_OVERLAY) + + var/icon/socks_s = null + if(socks && (current_species.clothing_flags & HAS_SOCKS)) + var/datum/sprite_accessory/socks/U3 = GLOB.socks_list[socks] + if(U3) + var/u3_icon = U3.sprite_sheets && (current_species.name in U3.sprite_sheets) ? U3.sprite_sheets[current_species.name] : U3.icon + socks_s = new/icon(u3_icon, "sk_[U3.icon_state]_s", ICON_OVERLAY) + + var/icon/clothes_s = null + var/uniform_dmi='icons/mob/uniform.dmi' + if(disabilities&DISABILITY_FLAG_FAT) + uniform_dmi='icons/mob/uniform_fat.dmi' + if(job_support_low & JOB_CIVILIAN)//This gives the preview icon clothes depending on which job(if any) is set to 'high' + clothes_s = new /icon(uniform_dmi, "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + else if(job_support_high)//I hate how this looks, but there's no reason to go through this switch if it's empty + switch(job_support_high) + if(JOB_HOP) + clothes_s = new /icon(uniform_dmi, "hop_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "ianshirt"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BARTENDER) + clothes_s = new /icon(uniform_dmi, "ba_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "tophat"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BOTANIST) + clothes_s = new /icon(uniform_dmi, "hydroponics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "ggloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apron"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "nymph"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-hyd"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHEF) + clothes_s = new /icon(uniform_dmi, "chef_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "chef"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apronchef"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_JANITOR) + clothes_s = new /icon(uniform_dmi, "janitor_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_janitor"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_LIBRARIAN) + clothes_s = new /icon(uniform_dmi, "red_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hairflower"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_QUARTERMASTER) + clothes_s = new /icon(uniform_dmi, "qm_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "poncho"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CARGOTECH) + clothes_s = new /icon(uniform_dmi, "cargotech_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "flat_cap"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_MINER) + clothes_s = new /icon(uniform_dmi, "miner_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "bearpelt"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_LAWYER) + clothes_s = new /icon(uniform_dmi, "internalaffairs_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "briefcase"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suitjacket_blue"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHAPLAIN) + clothes_s = new /icon(uniform_dmi, "chapblack_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "imperium_monk"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CLOWN) + clothes_s = new /icon(uniform_dmi, "clown_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "clown"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "clownpack"), ICON_OVERLAY) + if(JOB_MIME) + clothes_s = new /icon(uniform_dmi, "mime_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "lgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "mime"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suspenders"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + else if(job_medsci_high) + switch(job_medsci_high) + if(JOB_RD) + clothes_s = new /icon(uniform_dmi, "director_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "petehat"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_SCIENTIST) + clothes_s = new /icon(uniform_dmi, "toxinswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "metroid"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHEMIST) + clothes_s = new /icon(uniform_dmi, "chemistrywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labgreen"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-chem"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CMO) + clothes_s = new /icon(uniform_dmi, "cmo_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_cmo"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_DOCTOR) + clothes_s = new /icon(uniform_dmi, "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "surgeon"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CORONER) + clothes_s = new /icon(uniform_dmi, "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "mortician"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_GENETICIST) + clothes_s = new /icon(uniform_dmi, "geneticswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "monkeysuit"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-gen"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_VIROLOGIST) + clothes_s = new /icon(uniform_dmi, "virologywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "sterile"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "plaguedoctor"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-vir"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_PSYCHIATRIST) + clothes_s = new /icon(uniform_dmi, "psych_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_PARAMEDIC) + clothes_s = new /icon(uniform_dmi, "paramedic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigoff"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "bluesoft"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(JOB_ROBOTICIST) + clothes_s = new /icon(uniform_dmi, "robotics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "toolbox_blue"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + else if(job_engsec_high) + switch(job_engsec_high) + if(JOB_CAPTAIN) + clothes_s = new /icon(uniform_dmi, "captain_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "centcomcaptain"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/head.dmi', "captain"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-cap"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_HOS) + clothes_s = new /icon(uniform_dmi, "hosred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_hos"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_WARDEN) + clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "slippers_worn"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_DETECTIVE) + clothes_s = new /icon(uniform_dmi, "detective_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigaron"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "detective"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_OFFICER) + clothes_s = new /icon(uniform_dmi, "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_officer"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHIEF) + clothes_s = new /icon(uniform_dmi, "chief_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_white"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "blueprints"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_ENGINEER) + clothes_s = new /icon(uniform_dmi, "engine_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "hazard"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_ATMOSTECH) + clothes_s = new /icon(uniform_dmi, "atmos_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "firesuit"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + if(JOB_AI)//Gives AI and borgs assistant-wear, so they can still customize their character + clothes_s = new /icon(uniform_dmi, "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "straight_jacket"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CYBORG) + clothes_s = new /icon(uniform_dmi, "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "cardborg"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + else if(job_karma_high) + switch(job_karma_high) + if(JOB_MECHANIC) + clothes_s = new /icon(uniform_dmi, "mechanic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_PILOT) + clothes_s = new /icon(uniform_dmi, "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BRIGDOC) + clothes_s = new /icon(uniform_dmi, "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_NANO) + clothes_s = new /icon(uniform_dmi, "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BLUESHIELD) + clothes_s = new /icon(uniform_dmi, "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_JUDGE) + clothes_s = new /icon(uniform_dmi, "really_black_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "mercy_hood"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_UNDERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + if(disabilities & NEARSIGHTED) + preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY) + + // Observers get tourist outfit. + if(for_observer) + clothes_s = new /icon(uniform_dmi, "tourist_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + if(underwear_s) + preview_icon.Blend(underwear_s, ICON_OVERLAY) + if(undershirt_s) + preview_icon.Blend(undershirt_s, ICON_OVERLAY) + if(socks_s) + preview_icon.Blend(socks_s, ICON_OVERLAY) + if(clothes_s) + preview_icon.Blend(clothes_s, ICON_OVERLAY) + preview_icon.Blend(face_s, ICON_OVERLAY) + preview_icon_front = new(preview_icon, dir = SOUTH) + preview_icon_side = new(preview_icon, dir = WEST) + + qdel(face_s) + qdel(underwear_s) + qdel(undershirt_s) + qdel(socks_s) + qdel(clothes_s) diff --git a/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm b/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm index 3ab334490805..37bb1bdd1ee7 100644 --- a/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm @@ -56,4 +56,4 @@ /datum/sprite_accessory/hair/diona/diona_wildflow name = "Wildflowers" - icon_state = "wildflower" \ No newline at end of file + icon_state = "wildflower" diff --git a/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm index abc169057889..da8d8c9160fa 100644 --- a/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm @@ -4,4 +4,4 @@ /datum/sprite_accessory/body_markings/drask/arm_spines_drask icon = 'icons/mob/sprite_accessories/drask/drask_body_markings.dmi' name = "Drask Arm Spines" - icon_state = "armspines" \ No newline at end of file + icon_state = "armspines" diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm index 9cef42301392..9dd5345d4837 100644 --- a/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm @@ -32,4 +32,4 @@ /datum/sprite_accessory/body_markings/tattoo/nightling name = "Nightling Tattoo" - icon_state = "nightling" \ No newline at end of file + icon_state = "nightling" diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm index e31360113934..d0a3d369c4d6 100644 --- a/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm @@ -102,4 +102,4 @@ icon_state = "unshaven" ////////////////////////////// //////END VG HAIRSTYLES/////// -////////////////////////////// \ No newline at end of file +////////////////////////////// diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm b/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm index bc9931a8782a..f9e1ee1cc495 100644 --- a/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm @@ -755,4 +755,4 @@ /datum/sprite_accessory/hair/fluff/pinapple_fluff_hair //Pineapple Salad hair fluff its for a slime..has to go under human name = "Sasook Hair" icon_state = "psalad_fluff_hair" - species_allowed = list("Slime People") \ No newline at end of file + species_allowed = list("Slime People") diff --git a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm index 85e0f708aaf4..8910a8f52295 100644 --- a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm +++ b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm @@ -132,4 +132,4 @@ /datum/sprite_accessory/hair/ipc/fluff/lumi_waiting //Lumi Fluff hair name = "Lumi Waiting" - icon_state = "lumi_waiting" \ No newline at end of file + icon_state = "lumi_waiting" diff --git a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm index ff157a25d7d9..1a0e27560659 100644 --- a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm +++ b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm @@ -23,4 +23,4 @@ /datum/sprite_accessory/body_markings/head/optics/xion_alt name = "Xion Alt. Optics" icon_state = "xion_alt_optics" - models_allowed = list("Xion Manufacturing Group alt.") \ No newline at end of file + models_allowed = list("Xion Manufacturing Group alt.") diff --git a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm index 622ba3736f46..ba8d90fb7204 100644 --- a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm @@ -4,4 +4,4 @@ /datum/sprite_accessory/body_markings/kidan/outline_kid name = "Kidan Outline" - icon_state = "outline" \ No newline at end of file + icon_state = "outline" diff --git a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm index cf2bd865e267..795c58622beb 100644 --- a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm @@ -31,4 +31,4 @@ /datum/sprite_accessory/hair/kidan/kidan_tall_horns name = "Tall Horns" - icon_state = "tall_horns" \ No newline at end of file + icon_state = "tall_horns" diff --git a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm index 521daa2023fb..aa938926eb99 100644 --- a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm @@ -53,4 +53,4 @@ /datum/sprite_accessory/head_accessory/kidan/kidan_very_short name = "Very Short" icon_state = "very_short" -// End \ No newline at end of file +// End diff --git a/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm b/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm index 87a6f31cc31a..3218a15357af 100644 --- a/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm +++ b/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm @@ -29,4 +29,4 @@ /datum/sprite_accessory/hair/nucleation/nuc_neutron name = "Nucleation Neutron Bomb" - icon_state = "neutron" \ No newline at end of file + icon_state = "neutron" diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm index decbcce85781..572bd5d556b3 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm @@ -20,4 +20,4 @@ /datum/sprite_accessory/body_markings/tajara/patchy_taj name = "Tajaran Patches" - icon_state = "patch" \ No newline at end of file + icon_state = "patch" diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm index 794b89c8d749..3e9ae78ef422 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm @@ -28,4 +28,4 @@ /datum/sprite_accessory/facial_hair/tajara/taj_smallstache name = "Tajara Smallstache" - icon_state = "facial_smallstache" \ No newline at end of file + icon_state = "facial_smallstache" diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm index 7b55508877c0..03cb618ee0bb 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm @@ -78,4 +78,4 @@ /datum/sprite_accessory/hair/tajara/taj_hair_fingercurl name = "Tajara Finger Curls" icon_state = "fingerwave" - glasses_over = null \ No newline at end of file + glasses_over = null diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm index 88d534bb3c27..a3afa6823b48 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm @@ -41,4 +41,4 @@ /datum/sprite_accessory/body_markings/head/tajara/patchy_taj //Companion marking for Tajaran Patches. name = "Tajaran Patches Head" - icon_state = "patch" \ No newline at end of file + icon_state = "patch" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm index 7b16b9fab106..aa8c992e014c 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm @@ -2,4 +2,4 @@ name = "Unathi Sharp Snout" species_allowed = list("Unathi") icon_state = "head_sharp" - suffix = "sharp" \ No newline at end of file + suffix = "sharp" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm index e65fe303d360..a37b65c45616 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm @@ -16,4 +16,4 @@ /datum/sprite_accessory/body_markings/unathi/points_una name = "Unathi Points" - icon_state = "points" \ No newline at end of file + icon_state = "points" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm index 7cba458e2b02..16b9a13fae41 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm @@ -73,4 +73,4 @@ /datum/sprite_accessory/facial_hair/unathi/una_spikes name = "Spikes" icon = 'icons/mob/sprite_accessories/unathi/unathi_hair.dmi' - icon_state = "spikes_horns" \ No newline at end of file + icon_state = "spikes_horns" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm index 0cab3238016c..52968c00219e 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm @@ -51,4 +51,4 @@ /datum/sprite_accessory/head_accessory/unathi/spikes name = "Spikes" - icon_state = "spikes_horns" \ No newline at end of file + icon_state = "spikes_horns" diff --git a/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm index 563b39356d9f..1bf69829bef3 100644 --- a/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm @@ -16,4 +16,4 @@ /datum/sprite_accessory/body_markings/tattoo/vox/tiger_body_vox name = "Vox Tiger-stripe Tattoo" - icon_state = "tiger" \ No newline at end of file + icon_state = "tiger" diff --git a/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm index 18d212de255a..ae1893826695 100644 --- a/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm @@ -17,4 +17,4 @@ /datum/sprite_accessory/facial_hair/vox/vox_beard name = "Vox Quill Beard" - icon_state = "beard" \ No newline at end of file + icon_state = "beard" diff --git a/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm b/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm index e77c6f7e19d9..6a3d50317ac5 100644 --- a/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm @@ -12,4 +12,4 @@ /datum/sprite_accessory/body_markings/tail/vox/vox_stripe name = "Vox Tail Stripe" - icon_state = "stripe" \ No newline at end of file + icon_state = "stripe" diff --git a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm index 66edd5974fc8..047fa6cd73dd 100644 --- a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm @@ -54,4 +54,4 @@ /datum/sprite_accessory/head_accessory/vulpkanin/vulp_slash name = "Slash" - icon_state = "slash" \ No newline at end of file + icon_state = "slash" diff --git a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm index 47253385ba17..c04f7061efc4 100644 --- a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm @@ -32,4 +32,4 @@ /datum/sprite_accessory/body_markings/head/vulpkanin/points_sharp_vulp //Companion marking for Vulpkanin Points Sharp. name = "Vulpkanin Points Head 2" - icon_state = "points_sharp" \ No newline at end of file + icon_state = "points_sharp" diff --git a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm index 05060d3c6760..bceaa8ae7298 100644 --- a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm @@ -28,4 +28,4 @@ /datum/sprite_accessory/body_markings/tail/vulpkanin/vulp_hybrid_silverf name = "Vulpkanin Bushy Straight Tail Black Fade White Tip" tails_allowed = list("Straight Bushy Tail") - icon_state = "sbsilverfade" \ No newline at end of file + icon_state = "sbsilverfade" diff --git a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm index 38d17ea29504..668808de2177 100644 --- a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm +++ b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm @@ -5,4 +5,4 @@ /datum/sprite_accessory/hair/wryn/wry_antennae_default name = "Antennae" - icon_state = "antennae" \ No newline at end of file + icon_state = "antennae" diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 93eb035b187d..81d22e40a0b1 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,208 +1,213 @@ - -/mob/proc/say() - return - -/mob/verb/whisper(message as text) - set name = "Whisper" - set category = "IC" - return - -/mob/verb/say_verb(message as text) - set name = "Say" - set category = "IC" - - //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words - /* - var/first_letter = copytext(message,1,2) - if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) - var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") - if(response == "Edit message") - message = input(usr, "Please edit your message carefully:", "Edit message", message) - if(!message) - return - else if(response == "No") - return - */ - - set_typing_indicator(0) - usr.say(message) - - -/mob/verb/me_verb(message as text) - set name = "Me" - set category = "IC" - - message = strip_html_properly(message) - - set_typing_indicator(0) - if(use_me) - custom_emote(usr.emote_type, message) - else - usr.emote(message) - - -/mob/proc/say_dead(var/message) - if(!(client && client.holder)) - if(!config.dsay_allowed) - to_chat(src, "Deadchat is globally muted.") - return - - if(client && !(client.prefs.toggles & CHAT_DEAD)) - to_chat(usr, "You have deadchat muted.") - return - - say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src) - -/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null) - if(stat == DEAD) - return 1 - - //Universal speak makes everything understandable, for obvious reasons. - if(universal_speak || universal_understand) - return 1 - - //Languages are handled after. - if(!speaking) - if(!other) - return 1 - if(other.universal_speak) - return 1 - if(isAI(src) && ispAI(other)) - return 1 - if(istype(other, src.type) || istype(src, other.type)) - return 1 - return 0 - - if(speaking.flags & INNATE) - return 1 - - //Language check. - for(var/datum/language/L in languages) - if(speaking.name == L.name) - return 1 - - return 0 - - -/mob/proc/say_quote(var/message, var/datum/language/speaking = null) - var/verb = "says" - var/ending = copytext(message, length(message)) - - if(speaking) - verb = speaking.get_spoken_verb(ending) - else - if(ending == "!") - verb = pick("exclaims", "shouts", "yells") - else if(ending == "?") - verb = "asks" - return verb - - -/mob/proc/emote(act, type, message, force) - if(act == "me") - return custom_emote(type, message) - - -/mob/proc/get_ear() - // returns an atom representing a location on the map from which this - // mob can hear things - - // should be overloaded for all mobs whose "ear" is separate from their "mob" - - return get_turf(src) - -/mob/proc/say_test(var/text) - var/ending = copytext(text, length(text)) - if(ending == "?") - return "1" - else if(ending == "!") - return "2" - return "0" - -//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say. -//returns the message mode string or null for no message mode. -//standard mode is the mode returned for the special ';' radio code. -/mob/proc/parse_message_mode(var/message, var/standard_mode = "headset") - if(length(message) >= 1 && copytext(message, 1, 2) == ";") - return standard_mode - - if(length(message) >= 2) - var/channel_prefix = copytext(message, 1 ,3) - return department_radio_keys[channel_prefix] - - return null - -/datum/multilingual_say_piece - var/datum/language/speaking = null - var/message = "" - -/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message) - . = ..() - speaking = new_speaking - if(new_message) - message = new_message - -/mob/proc/find_valid_prefixes(message) - var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]] - for(var/i in 1 to length(message)) - var/selection = trim_right(lowertext(copytext(message, i, i + 3))) - var/datum/language/L = GLOB.language_keys[selection] - if(L != null && can_speak_language(L)) // What the fuck... remove the L != null check if you ever find out what the fuck is adding `null` to the languages list on absolutely random mobs... seriously what the hell... - prefixes[++prefixes.len] = list(L, i, i + length(selection)) - else if(!L && i == 1) - prefixes[++prefixes.len] = list(get_default_language(), i, i) - else - return prefixes - -/proc/strip_prefixes(message) - . = "" - var/last_index = 1 - for(var/i in 1 to length(message)) - var/selection = trim_right(lowertext(copytext(message, i, i + 3))) - var/datum/language/L = GLOB.language_keys[selection] - if(L) - . += copytext(message, last_index, i) - last_index = i + 3 - if(i + 1 > length(message)) - . += copytext(message, last_index) - -// this returns a structured message with language sections -// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!")) -/mob/proc/parse_languages(message) - . = list() - - // Noise language is a snowflake - if(copytext(message, 1, 2) == "!" && length(message) > 1) - return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2))))) - - // Scan the message for prefixes - var/list/prefix_locations = find_valid_prefixes(message) - if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes. - . += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message. - - for(var/i in 1 to length(prefix_locations)) - var/current = prefix_locations[i] // ["Common", keypos] - - // There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages. - var/datum/language/L = current[1] - if(L && L.flags & HIVEMIND) - . = new /datum/multilingual_say_piece(L, trim(strip_prefixes(message))) - break - - if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang - var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L) - . += new /datum/multilingual_say_piece(current[1], spoke_message) - else - var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop. - var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L) - . += new /datum/multilingual_say_piece(current[1], spoke_message) - -/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */ -/proc/message_to_multilingual(message, datum/language/speaking = null) - . = list(new /datum/multilingual_say_piece(speaking, message)) - -/proc/multilingual_to_message(list/message_pieces) - . = "" - for(var/datum/multilingual_say_piece/S in message_pieces) - . += S.message + " " - . = trim_right(.) \ No newline at end of file + +#define ILLEGAL_CHARACTERS_LIST list("<" = "", ">" = "", \ + "\[" = "", "]" = "", "{" = "", "}" = "") + +/mob/proc/say() + return + +/mob/verb/whisper(message as text) + set name = "Whisper" + set category = "IC" + return + +/mob/verb/say_verb(message as text) + set name = "Say" + set category = "IC" + + //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words + /* + var/first_letter = copytext(message,1,2) + if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) + var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") + if(response == "Edit message") + message = input(usr, "Please edit your message carefully:", "Edit message", message) + if(!message) + return + else if(response == "No") + return + */ + message = replace_characters(message, ILLEGAL_CHARACTERS_LIST) + set_typing_indicator(0) + usr.say(message) + + +/mob/verb/me_verb(message as text) + set name = "Me" + set category = "IC" + + message = strip_html_properly(message) + + set_typing_indicator(0) + if(use_me) + custom_emote(usr.emote_type, message) + else + usr.emote(message) + + +/mob/proc/say_dead(var/message) + if(!(client && client.holder)) + if(!config.dsay_allowed) + to_chat(src, "Deadchat is globally muted.") + return + + if(client && !(client.prefs.toggles & CHAT_DEAD)) + to_chat(usr, "You have deadchat muted.") + return + + say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src) + +/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null) + if(stat == DEAD) + return 1 + + //Universal speak makes everything understandable, for obvious reasons. + if(universal_speak || universal_understand) + return 1 + + //Languages are handled after. + if(!speaking) + if(!other) + return 1 + if(other.universal_speak) + return 1 + if(isAI(src) && ispAI(other)) + return 1 + if(istype(other, src.type) || istype(src, other.type)) + return 1 + return 0 + + if(speaking.flags & INNATE) + return 1 + + //Language check. + for(var/datum/language/L in languages) + if(speaking.name == L.name) + return 1 + + return 0 + + +/mob/proc/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "says" + var/ending = copytext(message, length(message)) + + if(speaking) + verb = speaking.get_spoken_verb(ending) + else + if(ending == "!") + verb = pick("exclaims", "shouts", "yells") + else if(ending == "?") + verb = "asks" + return verb + + +/mob/proc/emote(act, type, message, force) + if(act == "me") + return custom_emote(type, message) + + +/mob/proc/get_ear() + // returns an atom representing a location on the map from which this + // mob can hear things + + // should be overloaded for all mobs whose "ear" is separate from their "mob" + + return get_turf(src) + +/mob/proc/say_test(var/text) + var/ending = copytext(text, length(text)) + if(ending == "?") + return "1" + else if(ending == "!") + return "2" + return "0" + +//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say. +//returns the message mode string or null for no message mode. +//standard mode is the mode returned for the special ';' radio code. +/mob/proc/parse_message_mode(var/message, var/standard_mode = "headset") + if(length(message) >= 1 && copytext(message, 1, 2) == ";") + return standard_mode + + if(length(message) >= 2) + var/channel_prefix = copytext(message, 1 ,3) + return GLOB.department_radio_keys[channel_prefix] + + return null + +/datum/multilingual_say_piece + var/datum/language/speaking = null + var/message = "" + +/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message) + . = ..() + speaking = new_speaking + if(new_message) + message = new_message + +/mob/proc/find_valid_prefixes(message) + var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]] + for(var/i in 1 to length(message)) + var/selection = trim_right(lowertext(copytext(message, i, i + 3))) + var/datum/language/L = GLOB.language_keys[selection] + if(L != null && can_speak_language(L)) // What the fuck... remove the L != null check if you ever find out what the fuck is adding `null` to the languages list on absolutely random mobs... seriously what the hell... + prefixes[++prefixes.len] = list(L, i, i + length(selection)) + else if(!L && i == 1) + prefixes[++prefixes.len] = list(get_default_language(), i, i) + else + return prefixes + +/proc/strip_prefixes(message) + . = "" + var/last_index = 1 + for(var/i in 1 to length(message)) + var/selection = trim_right(lowertext(copytext(message, i, i + 3))) + var/datum/language/L = GLOB.language_keys[selection] + if(L) + . += copytext(message, last_index, i) + last_index = i + 3 + if(i + 1 > length(message)) + . += copytext(message, last_index) + +// this returns a structured message with language sections +// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!")) +/mob/proc/parse_languages(message) + . = list() + + // Noise language is a snowflake + if(copytext(message, 1, 2) == "!" && length(message) > 1) + return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2))))) + + // Scan the message for prefixes + var/list/prefix_locations = find_valid_prefixes(message) + if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes. + . += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message. + + for(var/i in 1 to length(prefix_locations)) + var/current = prefix_locations[i] // ["Common", keypos] + + // There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages. + var/datum/language/L = current[1] + if(L && L.flags & HIVEMIND) + . = new /datum/multilingual_say_piece(L, trim(strip_prefixes(message))) + break + + if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang + var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L) + . += new /datum/multilingual_say_piece(current[1], spoke_message) + else + var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop. + var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L) + . += new /datum/multilingual_say_piece(current[1], spoke_message) + +/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */ +/proc/message_to_multilingual(message, datum/language/speaking = null) + . = list(new /datum/multilingual_say_piece(speaking, message)) + +/proc/multilingual_to_message(list/message_pieces) + . = "" + for(var/datum/multilingual_say_piece/S in message_pieces) + . += S.message + " " + . = trim_right(.) + +#undef ILLEGAL_CHARACTERS_LIST diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index 6716ea0e4a9a..b61ce1711941 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -173,6 +173,9 @@ /mob/proc/Stun() return +/mob/proc/IsStunned() + return stunned + /mob/proc/SetStunned() return @@ -204,4 +207,4 @@ /mob/proc/adjust_bodytemperature(amount, min_temp = 0, max_temp = INFINITY) if(bodytemperature >= min_temp && bodytemperature <= max_temp) - bodytemperature = Clamp(bodytemperature + amount, min_temp, max_temp) \ No newline at end of file + bodytemperature = Clamp(bodytemperature + amount, min_temp, max_temp) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 52b3bb61b274..2ee4a9d2a9c1 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -1,301 +1,301 @@ -/mob/living/carbon/human/proc/monkeyize() - var/mob/H = src - H.dna.SetSEState(MONKEYBLOCK,1) - genemutcheck(H,MONKEYBLOCK,null,MUTCHK_FORCED) - -/mob/new_player/AIize() - spawning = 1 - return ..() - -/mob/living/carbon/AIize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - return ..() - -/mob/proc/AIize() - if(client) - stop_sound_channel(CHANNEL_LOBBYMUSIC) - - var/mob/living/silicon/ai/O = new (loc,,,1)//No MMI but safety is in effect. - O.invisibility = 0 - O.aiRestorePowerRoutine = 0 - - if(mind) - mind.transfer_to(O) - O.mind.original = O - else - O.key = key - - O.on_mob_init() - - O.add_ai_verbs() - - O.rename_self("AI",1) - - spawn() - qdel(src) - return O - - - -//human -> robot -/mob/living/carbon/human/proc/Robotize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) - qdel(t) - for(var/i in internal_organs) - qdel(i) - - var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc ) - - // cyborgs produced by Robotize get an automatic power cell - O.cell = new /obj/item/stock_parts/cell/high(O) - - O.gender = gender - O.invisibility = 0 - - if(mind) //TODO - mind.transfer_to(O) - if(O.mind.assigned_role == "Cyborg") - O.mind.original = O - else if(mind && mind.special_role) - O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite") - else - O.key = key - - O.loc = loc - O.job = "Cyborg" - O.notify_ai(1) - - if(O.mind && O.mind.assigned_role == "Cyborg") - if(O.mind.role_alt_title == "Android") - O.mmi = new /obj/item/mmi/robotic_brain(O) - else if(O.mind.role_alt_title == "Robot") - O.mmi = null //Robots do not have removable brains. - else - O.mmi = new /obj/item/mmi(O) - - if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client. - - O.update_pipe_vision() - - O.Namepick() - - spawn(0)//To prevent the proc from returning null. - qdel(src) - return O - -//human -> alien -/mob/living/carbon/human/proc/Alienize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) - qdel(t) - - var/alien_caste = pick("Hunter","Sentinel","Drone") - var/mob/living/carbon/alien/humanoid/new_xeno - switch(alien_caste) - if("Hunter") - new_xeno = new /mob/living/carbon/alien/humanoid/hunter(loc) - if("Sentinel") - new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(loc) - if("Drone") - new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc) - - new_xeno.a_intent = INTENT_HARM - new_xeno.key = key - - to_chat(new_xeno, "You are now an alien.") - new_xeno.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return - -/mob/living/carbon/human/proc/slimeize(reproduce as num) - if(notransform) - return - notransform = TRUE - canmove = FALSE - for(var/obj/item/I in src) - unEquip(I) - regenerate_icons() - icon = null - invisibility = INVISIBILITY_MAXIMUM - for(var/t in bodyparts) - qdel(t) - - var/mob/living/simple_animal/slime/new_slime - if(reproduce) - var/number = pick(14;2,3,4) //reproduce (has a small chance of producing 3 or 4 offspring) - var/list/babies = list() - for(var/i=1,i<=number,i++) - var/mob/living/simple_animal/slime/M = new/mob/living/simple_animal/slime(loc) - M.set_nutrition(round(nutrition / number)) - step_away(M,src) - babies += M - new_slime = pick(babies) - else - new_slime = new /mob/living/simple_animal/slime(loc) - new_slime.a_intent = INTENT_HARM - new_slime.key = key - - to_chat(new_slime, "You are now a slime. Skreee!") - new_slime.update_pipe_vision() - . = new_slime - qdel(src) - -/mob/living/carbon/human/proc/corgize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) //this really should not be necessary - qdel(t) - - var/mob/living/simple_animal/pet/dog/corgi/new_corgi = new /mob/living/simple_animal/pet/dog/corgi (loc) - new_corgi.key = key - - to_chat(new_corgi, "You are now a Corgi. Yap Yap!") - new_corgi.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return - -/mob/living/carbon/human/Animalize() - - var/list/mobtypes = typesof(/mob/living/simple_animal) - var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes - - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - for(var/t in bodyparts) - qdel(t) - - var/mob/new_mob = new mobpath(src.loc) - - new_mob.key = key - new_mob.a_intent = INTENT_HARM - - - to_chat(new_mob, "You suddenly feel more... animalistic.") - new_mob.update_pipe_vision() - spawn() - qdel(src) - return - -/mob/proc/Animalize() - - var/list/mobtypes = typesof(/mob/living/simple_animal) - var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes - - var/mob/new_mob = new mobpath(src.loc) - - new_mob.key = key - new_mob.a_intent = INTENT_HARM - to_chat(new_mob, "You feel more... animalistic") - new_mob.update_pipe_vision() - - qdel(src) - - -/mob/living/carbon/human/proc/paize(var/name) - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) //this really should not be necessary - qdel(t) - - var/obj/item/paicard/card = new(loc) - var/mob/living/silicon/pai/pai = new(card) - pai.key = key - card.setPersonality(pai) - - pai.name = name - pai.real_name = name - card.name = name - - to_chat(pai, "You have become a pAI! Your name is [pai.name].") - pai.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return - -/mob/proc/safe_respawn(var/MP) - if(!MP) - return 0 - - if(!GAMEMODE_IS_NUCLEAR) - if(ispath(MP, /mob/living/simple_animal/pet/cat/Syndi)) - return 0 - if(ispath(MP, /mob/living/simple_animal/pet/cat)) - return 1 - if(ispath(MP, /mob/living/simple_animal/pet/dog/corgi)) - return 1 - if(ispath(MP, /mob/living/simple_animal/crab)) - return 1 - if(ispath(MP, /mob/living/simple_animal/chicken)) - return 1 - if(ispath(MP, /mob/living/simple_animal/cow)) - return 1 - if(ispath(MP, /mob/living/simple_animal/parrot)) - return 1 - if(!GAMEMODE_IS_NUCLEAR) - if(ispath(MP, /mob/living/simple_animal/pet/dog/fox/Syndifox)) - return 0 - if(ispath(MP, /mob/living/simple_animal/pet/dog/fox)) - return 1 - if(ispath(MP, /mob/living/simple_animal/chick)) - return 1 - if(ispath(MP, /mob/living/simple_animal/pet/dog/pug)) - return 1 - if(ispath(MP, /mob/living/simple_animal/butterfly)) - return 1 - - if(ispath(MP, /mob/living/simple_animal/borer) && !jobban_isbanned(src, ROLE_BORER) && !jobban_isbanned(src, "Syndicate")) - return 1 - - if(ispath(MP, /mob/living/simple_animal/diona) && !jobban_isbanned(src, ROLE_NYMPH)) - return 1 - - return 0 +/mob/living/carbon/human/proc/monkeyize() + var/mob/H = src + H.dna.SetSEState(GLOB.monkeyblock,1) + genemutcheck(H,GLOB.monkeyblock,null,MUTCHK_FORCED) + +/mob/new_player/AIize() + spawning = 1 + return ..() + +/mob/living/carbon/AIize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + return ..() + +/mob/proc/AIize() + if(client) + stop_sound_channel(CHANNEL_LOBBYMUSIC) + + var/mob/living/silicon/ai/O = new (loc,,,1)//No MMI but safety is in effect. + O.invisibility = 0 + O.aiRestorePowerRoutine = 0 + + if(mind) + mind.transfer_to(O) + O.mind.original = O + else + O.key = key + + O.on_mob_init() + + O.add_ai_verbs() + + O.rename_self("AI",1) + + spawn() + qdel(src) + return O + + + +//human -> robot +/mob/living/carbon/human/proc/Robotize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) + qdel(t) + for(var/i in internal_organs) + qdel(i) + + var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc ) + + // cyborgs produced by Robotize get an automatic power cell + O.cell = new /obj/item/stock_parts/cell/high(O) + + O.gender = gender + O.invisibility = 0 + + if(mind) //TODO + mind.transfer_to(O) + if(O.mind.assigned_role == "Cyborg") + O.mind.original = O + else if(mind && mind.special_role) + O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite") + else + O.key = key + + O.loc = loc + O.job = "Cyborg" + O.notify_ai(1) + + if(O.mind && O.mind.assigned_role == "Cyborg") + if(O.mind.role_alt_title == "Android") + O.mmi = new /obj/item/mmi/robotic_brain(O) + else if(O.mind.role_alt_title == "Robot") + O.mmi = null //Robots do not have removable brains. + else + O.mmi = new /obj/item/mmi(O) + + if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client. + + O.update_pipe_vision() + + O.Namepick() + + spawn(0)//To prevent the proc from returning null. + qdel(src) + return O + +//human -> alien +/mob/living/carbon/human/proc/Alienize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) + qdel(t) + + var/alien_caste = pick("Hunter","Sentinel","Drone") + var/mob/living/carbon/alien/humanoid/new_xeno + switch(alien_caste) + if("Hunter") + new_xeno = new /mob/living/carbon/alien/humanoid/hunter(loc) + if("Sentinel") + new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(loc) + if("Drone") + new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc) + + new_xeno.a_intent = INTENT_HARM + new_xeno.key = key + + to_chat(new_xeno, "You are now an alien.") + new_xeno.update_pipe_vision() + spawn(0)//To prevent the proc from returning null. + qdel(src) + return + +/mob/living/carbon/human/proc/slimeize(reproduce as num) + if(notransform) + return + notransform = TRUE + canmove = FALSE + for(var/obj/item/I in src) + unEquip(I) + regenerate_icons() + icon = null + invisibility = INVISIBILITY_MAXIMUM + for(var/t in bodyparts) + qdel(t) + + var/mob/living/simple_animal/slime/new_slime + if(reproduce) + var/number = pick(14;2,3,4) //reproduce (has a small chance of producing 3 or 4 offspring) + var/list/babies = list() + for(var/i=1,i<=number,i++) + var/mob/living/simple_animal/slime/M = new/mob/living/simple_animal/slime(loc) + M.set_nutrition(round(nutrition / number)) + step_away(M,src) + babies += M + new_slime = pick(babies) + else + new_slime = new /mob/living/simple_animal/slime(loc) + new_slime.a_intent = INTENT_HARM + new_slime.key = key + + to_chat(new_slime, "You are now a slime. Skreee!") + new_slime.update_pipe_vision() + . = new_slime + qdel(src) + +/mob/living/carbon/human/proc/corgize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) //this really should not be necessary + qdel(t) + + var/mob/living/simple_animal/pet/dog/corgi/new_corgi = new /mob/living/simple_animal/pet/dog/corgi (loc) + new_corgi.key = key + + to_chat(new_corgi, "You are now a Corgi. Yap Yap!") + new_corgi.update_pipe_vision() + spawn(0)//To prevent the proc from returning null. + qdel(src) + return + +/mob/living/carbon/human/Animalize() + + var/list/mobtypes = typesof(/mob/living/simple_animal) + var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes + + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + for(var/t in bodyparts) + qdel(t) + + var/mob/new_mob = new mobpath(src.loc) + + new_mob.key = key + new_mob.a_intent = INTENT_HARM + + + to_chat(new_mob, "You suddenly feel more... animalistic.") + new_mob.update_pipe_vision() + spawn() + qdel(src) + return + +/mob/proc/Animalize() + + var/list/mobtypes = typesof(/mob/living/simple_animal) + var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes + + var/mob/new_mob = new mobpath(src.loc) + + new_mob.key = key + new_mob.a_intent = INTENT_HARM + to_chat(new_mob, "You feel more... animalistic") + new_mob.update_pipe_vision() + + qdel(src) + + +/mob/living/carbon/human/proc/paize(var/name) + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) //this really should not be necessary + qdel(t) + + var/obj/item/paicard/card = new(loc) + var/mob/living/silicon/pai/pai = new(card) + pai.key = key + card.setPersonality(pai) + + pai.name = name + pai.real_name = name + card.name = name + + to_chat(pai, "You have become a pAI! Your name is [pai.name].") + pai.update_pipe_vision() + spawn(0)//To prevent the proc from returning null. + qdel(src) + return + +/mob/proc/safe_respawn(var/MP) + if(!MP) + return 0 + + if(!GAMEMODE_IS_NUCLEAR) + if(ispath(MP, /mob/living/simple_animal/pet/cat/Syndi)) + return 0 + if(ispath(MP, /mob/living/simple_animal/pet/cat)) + return 1 + if(ispath(MP, /mob/living/simple_animal/pet/dog/corgi)) + return 1 + if(ispath(MP, /mob/living/simple_animal/crab)) + return 1 + if(ispath(MP, /mob/living/simple_animal/chicken)) + return 1 + if(ispath(MP, /mob/living/simple_animal/cow)) + return 1 + if(ispath(MP, /mob/living/simple_animal/parrot)) + return 1 + if(!GAMEMODE_IS_NUCLEAR) + if(ispath(MP, /mob/living/simple_animal/pet/dog/fox/Syndifox)) + return 0 + if(ispath(MP, /mob/living/simple_animal/pet/dog/fox)) + return 1 + if(ispath(MP, /mob/living/simple_animal/chick)) + return 1 + if(ispath(MP, /mob/living/simple_animal/pet/dog/pug)) + return 1 + if(ispath(MP, /mob/living/simple_animal/butterfly)) + return 1 + + if(ispath(MP, /mob/living/simple_animal/borer) && !jobban_isbanned(src, ROLE_BORER) && !jobban_isbanned(src, "Syndicate")) + return 1 + + if(ispath(MP, /mob/living/simple_animal/diona) && !jobban_isbanned(src, ROLE_NYMPH)) + return 1 + + return 0 diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index 9ad5449ee8bb..7b13ab68229c 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -5,31 +5,31 @@ mob/var/typing mob/var/last_typed mob/var/last_typed_time -var/global/image/typing_indicator +GLOBAL_DATUM(typing_indicator, /image) /mob/proc/set_typing_indicator(var/state) - if(!typing_indicator) - typing_indicator = image('icons/mob/talk.dmi', null, "typing", MOB_LAYER + 1) - typing_indicator.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + if(!GLOB.typing_indicator) + GLOB.typing_indicator = image('icons/mob/talk.dmi', null, "typing", MOB_LAYER + 1) + GLOB.typing_indicator.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA if(ishuman(src)) var/mob/living/carbon/human/H = src if(H.disabilities & MUTE || H.silent) - overlays -= typing_indicator + overlays -= GLOB.typing_indicator return if(client) if((client.prefs.toggles & SHOW_TYPING) || stat != CONSCIOUS || is_muzzled()) - overlays -= typing_indicator + overlays -= GLOB.typing_indicator else if(state) if(!typing) - overlays += typing_indicator + overlays += GLOB.typing_indicator typing = 1 else if(typing) - overlays -= typing_indicator + overlays -= GLOB.typing_indicator typing = 0 return state @@ -49,7 +49,7 @@ var/global/image/typing_indicator set name = ".Me" set hidden = 1 - + set_typing_indicator(1) hud_typing = 1 var/message = typing_input(src, "", "me (text)") @@ -90,4 +90,4 @@ var/global/image/typing_indicator if(prefs.toggles & SHOW_TYPING) if(istype(mob)) mob.set_typing_indicator(0) - feedback_add_details("admin_verb","TID") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file + feedback_add_details("admin_verb","TID") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/mob/update_icons.dm b/code/modules/mob/update_icons.dm index 8a1f1b04cb1f..0efe10b40cf5 100644 --- a/code/modules/mob/update_icons.dm +++ b/code/modules/mob/update_icons.dm @@ -1,68 +1,68 @@ -//Most of these are defined at this level to reduce on checks elsewhere in the code. -//Having them here also makes for a nice reference list of the various overlay-updating procs available - -/mob/proc/regenerate_icons() //TODO: phase this out completely if possible - return - -/mob/proc/update_icons() - return - -/mob/proc/update_inv_handcuffed() - return - -/mob/proc/update_inv_legcuffed() - return - -/mob/proc/update_inv_back() - return - -/mob/proc/update_inv_l_hand() - return - -/mob/proc/update_inv_r_hand() - return - -/mob/proc/update_inv_wear_mask() - return - -/mob/proc/update_inv_wear_suit() - return - -/mob/proc/update_inv_w_uniform() - return - -/mob/proc/update_inv_belt() - return - -/mob/proc/update_inv_head() - return - -/mob/proc/update_inv_gloves() - return - -/mob/proc/update_mutations() - return - -/mob/proc/update_inv_wear_id() - return - -/mob/proc/update_inv_shoes() - return - -/mob/proc/update_inv_glasses() - return - -/mob/proc/update_inv_s_store() - return - -/mob/proc/update_inv_pockets() - return - -/mob/proc/update_inv_wear_pda() - return - -/mob/proc/update_inv_ears() - return - -/mob/proc/update_transform() - return \ No newline at end of file +//Most of these are defined at this level to reduce on checks elsewhere in the code. +//Having them here also makes for a nice reference list of the various overlay-updating procs available + +/mob/proc/regenerate_icons() //TODO: phase this out completely if possible + return + +/mob/proc/update_icons() + return + +/mob/proc/update_inv_handcuffed() + return + +/mob/proc/update_inv_legcuffed() + return + +/mob/proc/update_inv_back() + return + +/mob/proc/update_inv_l_hand() + return + +/mob/proc/update_inv_r_hand() + return + +/mob/proc/update_inv_wear_mask() + return + +/mob/proc/update_inv_wear_suit() + return + +/mob/proc/update_inv_w_uniform() + return + +/mob/proc/update_inv_belt() + return + +/mob/proc/update_inv_head() + return + +/mob/proc/update_inv_gloves() + return + +/mob/proc/update_mutations() + return + +/mob/proc/update_inv_wear_id() + return + +/mob/proc/update_inv_shoes() + return + +/mob/proc/update_inv_glasses() + return + +/mob/proc/update_inv_s_store() + return + +/mob/proc/update_inv_pockets() + return + +/mob/proc/update_inv_wear_pda() + return + +/mob/proc/update_inv_ears() + return + +/mob/proc/update_transform() + return diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm index 33a94cc31a5a..948824fa32e3 100644 --- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm +++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm @@ -1,4 +1,4 @@ -var/global/static/ntnrc_uid = 0 +GLOBAL_VAR_INIT(ntnrc_uid, 0) /datum/ntnet_conversation var/id = null @@ -9,15 +9,15 @@ var/global/static/ntnrc_uid = 0 var/password /datum/ntnet_conversation/New() - id = ntnrc_uid - ntnrc_uid++ - if(ntnet_global) - ntnet_global.chat_channels.Add(src) + id = GLOB.ntnrc_uid + GLOB.ntnrc_uid++ + if(GLOB.ntnet_global) + GLOB.ntnet_global.chat_channels.Add(src) ..() /datum/ntnet_conversation/Destroy() - if(ntnet_global) - ntnet_global.chat_channels.Remove(src) + if(GLOB.ntnet_global) + GLOB.ntnet_global.chat_channels.Remove(src) return ..() /datum/ntnet_conversation/proc/add_message(message, username) diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm index 000131d6c034..2f9aa3f2e7de 100644 --- a/code/modules/modular_computers/NTNet/NTNet.dm +++ b/code/modules/modular_computers/NTNet/NTNet.dm @@ -1,4 +1,4 @@ -var/global/datum/ntnet/ntnet_global = new() +GLOBAL_DATUM_INIT(ntnet_global, /datum/ntnet, new()) // This is the NTNet datum. There can be only one NTNet datum in game at once. Modular computers read data from this. @@ -26,8 +26,8 @@ var/global/datum/ntnet/ntnet_global = new() // If new NTNet datum is spawned, it replaces the old one. /datum/ntnet/New() - if(ntnet_global && (ntnet_global != src)) - ntnet_global = src // There can be only one. + if(GLOB.ntnet_global && (GLOB.ntnet_global != src)) + GLOB.ntnet_global = src // There can be only one. for(var/obj/machinery/ntnet_relay/R in GLOB.machines) relays.Add(R) R.NTNet = src diff --git a/code/modules/modular_computers/NTNet/NTNet_relay.dm b/code/modules/modular_computers/NTNet/NTNet_relay.dm index 436070470eab..c0cc534ea0b3 100644 --- a/code/modules/modular_computers/NTNet/NTNet_relay.dm +++ b/code/modules/modular_computers/NTNet/NTNet_relay.dm @@ -51,12 +51,12 @@ if((dos_overload > dos_capacity) && !dos_failure) dos_failure = 1 update_icon() - ntnet_global.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") + GLOB.ntnet_global.add_log("Quantum relay switched from normal operation mode to overload recovery mode.") // If the DoS buffer reaches 0 again, restart. if((dos_overload == 0) && dos_failure) dos_failure = 0 update_icon() - ntnet_global.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") + GLOB.ntnet_global.add_log("Quantum relay switched from overload recovery mode to normal operation mode.") ..() /obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -82,10 +82,10 @@ dos_overload = 0 dos_failure = 0 update_icon() - ntnet_global.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") + GLOB.ntnet_global.add_log("Quantum relay manually restarted from overload recovery mode to normal operation mode.") if("toggle") enabled = !enabled - ntnet_global.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].") + GLOB.ntnet_global.add_log("Quantum relay manually [enabled ? "enabled" : "disabled"].") update_icon() return 1 @@ -101,16 +101,16 @@ component_parts += new /obj/item/stack/cable_coil(null, 2) component_parts += new /obj/item/stock_parts/subspace/filter(null) - if(ntnet_global) - ntnet_global.relays.Add(src) - NTNet = ntnet_global - ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") + if(GLOB.ntnet_global) + GLOB.ntnet_global.relays.Add(src) + NTNet = GLOB.ntnet_global + GLOB.ntnet_global.add_log("New quantum relay activated. Current amount of linked relays: [NTNet.relays.len]") ..() /obj/machinery/ntnet_relay/Destroy() - if(ntnet_global) - ntnet_global.relays.Remove(src) - ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]") + if(GLOB.ntnet_global) + GLOB.ntnet_global.relays.Remove(src) + GLOB.ntnet_global.add_log("Quantum relay connection severed. Current amount of linked relays: [NTNet.relays.len]") NTNet = null for(var/datum/computer_file/program/ntnet_dos/D in dos_sources) diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 1e43884df9aa..1dd45a336035 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -289,7 +289,7 @@ if(!get_ntnet_status()) return FALSE var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET] - return ntnet_global.add_log(text, network_card) + return GLOB.ntnet_global.add_log(text, network_card) /obj/item/modular_computer/proc/shutdown_computer(loud = 1) if(enabled) diff --git a/code/modules/modular_computers/computers/item/laptop_presets.dm b/code/modules/modular_computers/computers/item/laptop_presets.dm index e492712d6e4c..43720dfa2fcb 100644 --- a/code/modules/modular_computers/computers/item/laptop_presets.dm +++ b/code/modules/modular_computers/computers/item/laptop_presets.dm @@ -18,4 +18,4 @@ /obj/item/modular_computer/laptop/preset/civillian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) \ No newline at end of file + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index 417b98f15bfc..7bd5be3cf816 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -71,4 +71,4 @@ if(MC_SDD) machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk if(MC_AI) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card \ No newline at end of file + machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index 7547c797e25a..f0a044b1d36d 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -9,4 +9,4 @@ max_hardware_size = 1 w_class = WEIGHT_CLASS_SMALL steel_sheet_cost = 1 - slot_flags = SLOT_ID | SLOT_BELT \ No newline at end of file + slot_flags = SLOT_ID | SLOT_BELT diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index 5dd324945faa..4f1bd2693375 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -73,4 +73,4 @@ /obj/machinery/modular_computer/console/preset/civilian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) \ No newline at end of file + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index 61e26ed98998..391396a7d75a 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -1,5 +1,5 @@ // Global var to track modular computers -var/list/global_modular_computers = list() +GLOBAL_LIST_EMPTY(global_modular_computers) // Modular Computer - device that runs various programs and operates with hardware // DO NOT SPAWN THIS TYPE. Use /laptop/ or /console/ instead. @@ -34,7 +34,7 @@ var/list/global_modular_computers = list() ..() cpu = new(src) cpu.physical = src - global_modular_computers.Add(src) + GLOB.global_modular_computers.Add(src) /obj/machinery/modular_computer/Destroy() QDEL_NULL(cpu) diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm index aeb4c18bc2d3..e16ce82702cb 100644 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -50,4 +50,4 @@ if(cpu) cpu.screen_on = 1 - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index f3b5a9659b2e..b3f8064b72a2 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -1,4 +1,4 @@ -var/global/file_uid = 0 +GLOBAL_VAR_INIT(file_uid, 0) /datum/computer_file var/filename = "NewFile" // Placeholder. No spacebars @@ -12,8 +12,8 @@ var/global/file_uid = 0 /datum/computer_file/New() ..() - uid = file_uid - file_uid++ + uid = GLOB.file_uid + GLOB.file_uid++ /datum/computer_file/Destroy() if(!holder) @@ -47,4 +47,4 @@ var/global/file_uid = 0 input_password = sanitize(input(user, "Please enter a password to access file '[filename]':")) if(input_password == password) return TRUE - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 8aa80631641f..f6bc293db7db 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -51,7 +51,7 @@ return 1 switch(href_list["action"]) if("PRG_target_relay") - for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + for(var/obj/machinery/ntnet_relay/R in GLOB.ntnet_global.relays) if("[R.uid]" == href_list["targid"]) target = R return 1 @@ -66,14 +66,14 @@ if(target) executed = 1 target.dos_sources.Add(src) - if(ntnet_global.intrusion_detection_enabled) + if(GLOB.ntnet_global.intrusion_detection_enabled) var/obj/item/computer_hardware/network_card/network_card = computer.all_components[MC_NET] - ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") - ntnet_global.intrusion_detection_alarm = 1 + GLOB.ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") + GLOB.ntnet_global.intrusion_detection_alarm = 1 return 1 /datum/computer_file/program/ntnet_dos/ui_data(mob/user) - if(!ntnet_global) + if(!GLOB.ntnet_global) return var/list/data = get_header_data() @@ -96,8 +96,8 @@ data["dos_strings"] += list(list("nums" = string)) else data["relays"] = list() - for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + for(var/obj/machinery/ntnet_relay/R in GLOB.ntnet_global.relays) data["relays"] += list(list("id" = R.uid)) data["focus"] = target ? target.uid : null - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 22df710c6f82..42f722af7990 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -71,4 +71,4 @@ data["armed"] = armed - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 686d3a87f6e6..9067a345d4e6 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -105,7 +105,7 @@ if(job) if(!job_blacklisted(job)) if((job.total_positions <= GLOB.player_list.len * (max_relative_positions / 100))) - var/delta = (world.time / 10) - time_last_changed_position + var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] < 0)) return 1 return -2 @@ -117,7 +117,7 @@ if(job) if(!job_blacklisted(job)) if(job.total_positions > job.current_positions) - var/delta = (world.time / 10) - time_last_changed_position + var/delta = (world.time / 10) - GLOB.time_last_changed_position if((change_position_cooldown < delta) || (opened_positions[job.title] > 0)) return 1 return -2 @@ -180,7 +180,7 @@ switch(href_list["action"]) if("PRG_modify") if(modify) - data_core.manifest_modify(modify.registered_name, modify.assignment) + GLOB.data_core.manifest_modify(modify.registered_name, modify.assignment) modify.name = "[modify.registered_name]'s ID Card ([modify.assignment])" card_slot.try_eject(1, user) else @@ -302,7 +302,7 @@ var/content if(mode == 2) title = "crew manifest ([station_time_timestamp()])" - content = "

    Crew Manifest


    [data_core ? data_core.get_manifest(0) : ""]" + content = "

    Crew Manifest


    [GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""]" else if(modify && !mode) title = "access report" content = {"

    Access Report

    @@ -346,7 +346,7 @@ if(can_open_job(j) != 1) return 1 if(opened_positions[edit_job_target] >= 0) - time_last_changed_position = world.time / 10 + GLOB.time_last_changed_position = world.time / 10 j.total_positions++ opened_positions[edit_job_target]++ log_game("[key_name(usr)] has opened a job slot for job \"[j]\".") @@ -362,7 +362,7 @@ return 1 //Allow instant closing without cooldown if a position has been opened before if(opened_positions[edit_job_target] <= 0) - time_last_changed_position = world.time / 10 + GLOB.time_last_changed_position = world.time / 10 j.total_positions-- opened_positions[edit_job_target]-- log_game("[key_name(usr)] has closed a job slot for job \"[j]\".") @@ -413,7 +413,7 @@ data["mode"] = mode data["printing"] = printing data["printer"] = printer ? TRUE : FALSE - data["manifest"] = data_core ? data_core.get_manifest(0) : null + data["manifest"] = GLOB.data_core ? GLOB.data_core.get_manifest(0) : null data["target_name"] = modify ? modify.name : "-----" data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" data["target_rank"] = get_target_rank() @@ -429,19 +429,19 @@ var/list/job_formats = SSjobs.format_jobs_for_id_computer(modify) data["top_jobs"] = format_jobs(list("Captain", "Custom"), data["target_rank"], job_formats) - data["engineering_jobs"] = format_jobs(engineering_positions, data["target_rank"], job_formats) - data["medical_jobs"] = format_jobs(medical_positions, data["target_rank"], job_formats) - data["science_jobs"] = format_jobs(science_positions, data["target_rank"], job_formats) - data["security_jobs"] = format_jobs(security_positions, data["target_rank"], job_formats) - data["support_jobs"] = format_jobs(support_positions, data["target_rank"], job_formats) - data["civilian_jobs"] = format_jobs(civilian_positions, data["target_rank"], job_formats) - data["special_jobs"] = format_jobs(whitelisted_positions, data["target_rank"], job_formats) + data["engineering_jobs"] = format_jobs(GLOB.engineering_positions, data["target_rank"], job_formats) + data["medical_jobs"] = format_jobs(GLOB.medical_positions, data["target_rank"], job_formats) + data["science_jobs"] = format_jobs(GLOB.science_positions, data["target_rank"], job_formats) + data["security_jobs"] = format_jobs(GLOB.security_positions, data["target_rank"], job_formats) + data["support_jobs"] = format_jobs(GLOB.support_positions, data["target_rank"], job_formats) + data["civilian_jobs"] = format_jobs(GLOB.civilian_positions, data["target_rank"], job_formats) + data["special_jobs"] = format_jobs(GLOB.whitelisted_positions, data["target_rank"], job_formats) data["centcom_jobs"] = format_jobs(get_all_centcom_jobs(), data["target_rank"], job_formats) data["card_skins"] = format_card_skins(get_station_card_skins()) data["job_slots"] = format_job_slots() - var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - time_last_changed_position), 1) + var/time_to_wait = round(change_position_cooldown - ((world.time / 10) - GLOB.time_last_changed_position), 1) var/mins = round(time_to_wait / 60) var/seconds = time_to_wait - (60*mins) data["cooldown_mins"] = mins @@ -481,4 +481,4 @@ data["regions"] = regions - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/command/comms.dm b/code/modules/modular_computers/file_system/programs/command/comms.dm index c471d49a0394..c23f0c9e3c70 100644 --- a/code/modules/modular_computers/file_system/programs/command/comms.dm +++ b/code/modules/modular_computers/file_system/programs/command/comms.dm @@ -50,7 +50,7 @@ /datum/computer_file/program/comm/proc/change_security_level(mob/user, new_level) tmp_alertlevel = new_level - var/old_level = security_level + var/old_level = GLOB.security_level if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN if(tmp_alertlevel < SEC_LEVEL_GREEN) @@ -58,10 +58,10 @@ if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this set_security_level(tmp_alertlevel) - if(security_level != old_level) + if(GLOB.security_level != old_level) log_game("[key_name(user)] has changed the security level to [get_security_level()].") message_admins("[key_name_admin(user)] has changed the security level to [get_security_level()].") - switch(security_level) + switch(GLOB.security_level) if(SEC_LEVEL_GREEN) feedback_inc("alert_comms_green", 1) if(SEC_LEVEL_BLUE) @@ -101,7 +101,7 @@ ui.set_layout_key("program") ui.open() -/datum/computer_file/program/comm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/computer_file/program/comm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/list/data = get_header_data() data["is_ai"] = isAI(user) || isrobot(user) data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state @@ -128,7 +128,7 @@ ) ) - data["security_level"] = security_level + data["security_level"] = GLOB.security_level data["str_security_level"] = capitalize(get_security_level()) data["levels"] = list( list("id" = SEC_LEVEL_GREEN, "name" = "Green"), @@ -326,7 +326,7 @@ Nuke_request(input, usr) to_chat(usr, "Request sent.") log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") - priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') + GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') centcomm_message_cooldown = 1 spawn(6000)//10 minute cooldown centcomm_message_cooldown = 0 @@ -382,4 +382,4 @@ to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") SSnanoui.update_uis(src) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm index 226511e2c095..8cb47337a6dc 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm @@ -68,4 +68,4 @@ "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) data["categories"] = categories - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index c30c7dbbd1e6..0763d527345b 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -43,6 +43,6 @@ data["poweravail"] = powernet.avail data["powerload"] = powernet.viewload data["powerdemand"] = powernet.load - data["apcs"] = apc_repository.apc_data(powernet) + data["apcs"] = GLOB.apc_repository.apc_data(powernet) - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 23b2cdab7ab8..e7edc437639e 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -225,4 +225,4 @@ if(!F || F.undeletable) return 1 if(F.can_access_file(usr)) - F.password = "" \ No newline at end of file + F.password = "" diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index d8278081ffa2..b4e5ab4674ca 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -21,7 +21,7 @@ if(downloaded_file) return 0 - var/datum/computer_file/program/PRG = ntnet_global.find_ntnet_file_by_name(filename) + var/datum/computer_file/program/PRG = GLOB.ntnet_global.find_ntnet_file_by_name(filename) if(!PRG || !istype(PRG)) return 0 @@ -37,10 +37,10 @@ ui_header = "downloader_running.gif" - if(PRG in ntnet_global.available_station_software) + if(PRG in GLOB.ntnet_global.available_station_software) generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.") hacked_download = 0 - else if(PRG in ntnet_global.available_antag_software) + else if(PRG in GLOB.ntnet_global.available_antag_software) generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.") hacked_download = 1 else @@ -135,7 +135,7 @@ data["disk_size"] = hard_drive.max_capacity data["disk_used"] = hard_drive.used_capacity var/list/all_entries[0] - for(var/A in ntnet_global.available_station_software) + for(var/A in GLOB.ntnet_global.available_station_software) var/datum/computer_file/program/P = A // Only those programs our user can run will show in the list if(!P.can_run(user,transfer = 1)) @@ -151,7 +151,7 @@ data["hackedavailable"] = 0 if(computer.emagged) // If we are running on emagged computer we have access to some "bonus" software var/list/hacked_programs[0] - for(var/S in ntnet_global.available_antag_software) + for(var/S in GLOB.ntnet_global.available_antag_software) var/datum/computer_file/program/P = S data["hackedavailable"] = 1 hacked_programs.Add(list(list( @@ -175,4 +175,4 @@ /datum/computer_file/program/ntnetdownload/kill_program(forced) abort_file_download() - return ..(forced) \ No newline at end of file + return ..(forced) diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm index 5a6affbb0a0b..517c8f99ebe0 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm @@ -52,7 +52,7 @@ /datum/computer_file/program/chatclient/ui_data(mob/user) - if(!ntnet_global || !ntnet_global.chat_channels) + if(!GLOB.ntnet_global || !GLOB.ntnet_global.chat_channels) return var/list/data = get_header_data() @@ -78,7 +78,7 @@ else // Channel selection screen var/list/all_channels[0] - for(var/C in ntnet_global.chat_channels) + for(var/C in GLOB.ntnet_global.chat_channels) var/datum/ntnet_conversation/conv = C if(conv && conv.title) all_channels.Add(list(list( @@ -108,7 +108,7 @@ if("PRG_joinchannel") . = 1 var/datum/ntnet_conversation/C - for(var/datum/ntnet_conversation/chan in ntnet_global.chat_channels) + for(var/datum/ntnet_conversation/chan in GLOB.ntnet_global.chat_channels) if(chan.id == text2num(href_list["id"])) C = chan break diff --git a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm index ef26a35730c9..1ccefa9311a0 100644 --- a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm +++ b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm @@ -1,4 +1,4 @@ -var/global/nttransfer_uid = 0 +GLOBAL_VAR_INIT(nttransfer_uid, 0) /datum/computer_file/program/nttransfer filename = "nttransfer" @@ -24,8 +24,8 @@ var/global/nttransfer_uid = 0 var/upload_menu = 0 // Whether we show the program list and upload menu /datum/computer_file/program/nttransfer/New() - unique_token = nttransfer_uid - nttransfer_uid++ + unique_token = GLOB.nttransfer_uid + GLOB.nttransfer_uid++ ..() /datum/computer_file/program/nttransfer/process_tick() @@ -100,7 +100,7 @@ var/global/nttransfer_uid = 0 return 1 switch(href_list["action"]) if("PRG_downloadfile") - for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) + for(var/datum/computer_file/program/nttransfer/P in GLOB.ntnet_global.fileservers) if("[P.unique_token]" == href_list["id"]) remote = P break @@ -118,8 +118,8 @@ var/global/nttransfer_uid = 0 error = "" upload_menu = 0 finalize_download() - if(src in ntnet_global.fileservers) - ntnet_global.fileservers.Remove(src) + if(src in GLOB.ntnet_global.fileservers) + GLOB.ntnet_global.fileservers.Remove(src) for(var/datum/computer_file/program/nttransfer/T in connected_clients) T.crash_download("Remote server has forcibly closed the connection") provided_file = null @@ -145,7 +145,7 @@ var/global/nttransfer_uid = 0 if(!P.can_run(usr,transfer = 1)) error = "Access Error: Insufficient rights to upload file." provided_file = F - ntnet_global.fileservers.Add(src) + GLOB.ntnet_global.fileservers.Add(src) return error = "I/O Error: Unable to locate file on hard drive." return 1 @@ -183,7 +183,7 @@ var/global/nttransfer_uid = 0 data["upload_filelist"] = all_files else var/list/all_servers[0] - for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) + for(var/datum/computer_file/program/nttransfer/P in GLOB.ntnet_global.fileservers) all_servers.Add(list(list( "uid" = P.unique_token, "filename" = "[P.provided_file.filename].[P.provided_file.filetype]", @@ -192,4 +192,4 @@ var/global/nttransfer_uid = 0 ))) data["servers"] = all_servers - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/research/airestorer.dm b/code/modules/modular_computers/file_system/programs/research/airestorer.dm index b45245a34412..d5ff31af66bf 100644 --- a/code/modules/modular_computers/file_system/programs/research/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/research/airestorer.dm @@ -134,4 +134,4 @@ /datum/computer_file/program/aidiag/kill_program(forced) restoring = FALSE - return ..(forced) \ No newline at end of file + return ..(forced) diff --git a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm index 1538fe3cc012..0a839a6d13f2 100644 --- a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm @@ -24,22 +24,22 @@ switch(href_list["action"]) if("resetIDS") . = 1 - if(ntnet_global) - ntnet_global.resetIDS() + if(GLOB.ntnet_global) + GLOB.ntnet_global.resetIDS() return 1 if("toggleIDS") . = 1 - if(ntnet_global) - ntnet_global.toggleIDS() + if(GLOB.ntnet_global) + GLOB.ntnet_global.toggleIDS() return 1 if("toggleWireless") . = 1 - if(!ntnet_global) + if(!GLOB.ntnet_global) return 1 // NTNet is disabled. Enabling can be done without user prompt - if(ntnet_global.setting_disabled) - ntnet_global.setting_disabled = 0 + if(GLOB.ntnet_global.setting_disabled) + GLOB.ntnet_global.setting_disabled = 0 return 1 // NTNet is enabled and user is about to shut it down. Let's ask them if they really want to do it, as wirelessly connected computers won't connect without NTNet being enabled (which may prevent people from turning it back on) @@ -48,43 +48,43 @@ return 1 var/response = alert(user, "Really disable NTNet wireless? If your computer is connected wirelessly you won't be able to turn it back on! This will affect all connected wireless devices.", "NTNet shutdown", "Yes", "No") if(response == "Yes") - ntnet_global.setting_disabled = 1 + GLOB.ntnet_global.setting_disabled = 1 return 1 if("purgelogs") . = 1 - if(ntnet_global) - ntnet_global.purge_logs() + if(GLOB.ntnet_global) + GLOB.ntnet_global.purge_logs() if("updatemaxlogs") . = 1 var/mob/user = usr var/logcount = text2num(input(user,"Enter amount of logs to keep in memory ([MIN_NTNET_LOGS]-[MAX_NTNET_LOGS]):")) - if(ntnet_global) - ntnet_global.update_max_log_count(logcount) + if(GLOB.ntnet_global) + GLOB.ntnet_global.update_max_log_count(logcount) if("toggle_function") . = 1 - if(!ntnet_global) + if(!GLOB.ntnet_global) return 1 - ntnet_global.toggle_function(text2num(href_list["id"])) + GLOB.ntnet_global.toggle_function(text2num(href_list["id"])) /datum/computer_file/program/ntnetmonitor/ui_data(mob/user) - if(!ntnet_global) + if(!GLOB.ntnet_global) return var/list/data = get_header_data() - data["ntnetstatus"] = ntnet_global.check_function() - data["ntnetrelays"] = ntnet_global.relays.len - data["idsstatus"] = ntnet_global.intrusion_detection_enabled - data["idsalarm"] = ntnet_global.intrusion_detection_alarm + data["ntnetstatus"] = GLOB.ntnet_global.check_function() + data["ntnetrelays"] = GLOB.ntnet_global.relays.len + data["idsstatus"] = GLOB.ntnet_global.intrusion_detection_enabled + data["idsalarm"] = GLOB.ntnet_global.intrusion_detection_alarm - data["config_softwaredownload"] = ntnet_global.setting_softwaredownload - data["config_peertopeer"] = ntnet_global.setting_peertopeer - data["config_communication"] = ntnet_global.setting_communication - data["config_systemcontrol"] = ntnet_global.setting_systemcontrol + data["config_softwaredownload"] = GLOB.ntnet_global.setting_softwaredownload + data["config_peertopeer"] = GLOB.ntnet_global.setting_peertopeer + data["config_communication"] = GLOB.ntnet_global.setting_communication + data["config_systemcontrol"] = GLOB.ntnet_global.setting_systemcontrol data["ntnetlogs"] = list() - for(var/i in ntnet_global.logs) + for(var/i in GLOB.ntnet_global.logs) data["ntnetlogs"] += list(list("entry" = i)) - data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount + data["ntnetmaxlogs"] = GLOB.ntnet_global.setting_maxlogcount - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/hardware/CPU.dm b/code/modules/modular_computers/hardware/CPU.dm index fa6443240ec4..02e7d41d5122 100644 --- a/code/modules/modular_computers/hardware/CPU.dm +++ b/code/modules/modular_computers/hardware/CPU.dm @@ -41,4 +41,4 @@ w_class = WEIGHT_CLASS_TINY power_usage = 75 max_idle_programs = 2 - origin_tech = "programming=4;engineering=3" \ No newline at end of file + origin_tech = "programming=4;engineering=3" diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index 1a112b248a54..1b97ae5ede80 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -77,4 +77,4 @@ if(istype(I, /obj/item/screwdriver)) to_chat(user, "You press down on the manual eject button with \the [I].") try_eject(0, user, 1) - return \ No newline at end of file + return diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 98f1ab5667e3..7cec597dc13a 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -106,4 +106,4 @@ desc = "A tiny power cell, commonly seen in low-end portable microcomputers." icon_state = "cell_micro" w_class = WEIGHT_CLASS_TINY - maxcharge = 300 \ No newline at end of file + maxcharge = 300 diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 82c4aa5f62ce..ce7fc9971927 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -170,4 +170,4 @@ origin_tech = "programming=1;engineering=1" max_capacity = 32 icon_state = "ssd_micro" - w_class = WEIGHT_CLASS_TINY \ No newline at end of file + w_class = WEIGHT_CLASS_TINY diff --git a/code/modules/modular_computers/hardware/network_card.dm b/code/modules/modular_computers/hardware/network_card.dm index 4768a63b9cb1..3e5409b5d61a 100644 --- a/code/modules/modular_computers/hardware/network_card.dm +++ b/code/modules/modular_computers/hardware/network_card.dm @@ -1,4 +1,4 @@ -var/global/ntnet_card_uid = 1 +GLOBAL_VAR_INIT(ntnet_card_uid, 1) /obj/item/computer_hardware/network_card name = "network card" @@ -26,8 +26,8 @@ var/global/ntnet_card_uid = 1 /obj/item/computer_hardware/network_card/New(var/l) ..(l) - identification_id = ntnet_card_uid - ntnet_card_uid++ + identification_id = GLOB.ntnet_card_uid + GLOB.ntnet_card_uid++ // Returns a string identifier of this network card /obj/item/computer_hardware/network_card/proc/get_network_tag() @@ -44,7 +44,7 @@ var/global/ntnet_card_uid = 1 if(ethernet) // Computer is connected via wired connection. return 3 - if(!ntnet_global || !ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. + if(!GLOB.ntnet_global || !GLOB.ntnet_global.check_function(specific_action)) // NTNet is down and we are not connected via wired connection. No signal. return 0 if(holder) diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm index 0db45f33e6e7..49c5d72a6f15 100644 --- a/code/modules/modular_computers/hardware/printer.dm +++ b/code/modules/modular_computers/hardware/printer.dm @@ -67,4 +67,4 @@ icon_state = "printer_mini" w_class = WEIGHT_CLASS_TINY stored_paper = 5 - max_paper = 15 \ No newline at end of file + max_paper = 15 diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index b2066285dea4..aa69b85b1df3 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -283,7 +283,7 @@ obj/machinery/lapvend/attackby(obj/item/I, mob/user) atom_say("Insufficient funds in account.") return 0 else - customer_account.charge(total_price, vendor_account, + customer_account.charge(total_price, GLOB.vendor_account, "Purchase of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"].", name, customer_account.owner_name, "Sale of [(devtype == 1) ? "laptop computer" : "tablet microcomputer"].", customer_account.owner_name) diff --git a/code/modules/nano/interaction/admin.dm b/code/modules/nano/interaction/admin.dm index 412ebbc12601..9223e1e0763d 100644 --- a/code/modules/nano/interaction/admin.dm +++ b/code/modules/nano/interaction/admin.dm @@ -1,7 +1,7 @@ /* This state checks that the user is an admin, end of story */ -/var/global/datum/topic_state/admin_state/admin_state = new() +GLOBAL_DATUM_INIT(admin_state, /datum/topic_state/admin_state, new()) /datum/topic_state/admin_state/can_use_topic(var/src_object, var/mob/user) return check_rights(R_ADMIN, 0, user) ? STATUS_INTERACTIVE : STATUS_CLOSE diff --git a/code/modules/nano/interaction/conscious.dm b/code/modules/nano/interaction/conscious.dm index 143bc24956fb..e4b478dced6a 100644 --- a/code/modules/nano/interaction/conscious.dm +++ b/code/modules/nano/interaction/conscious.dm @@ -1,7 +1,7 @@ /* This state only checks if user is conscious. */ -/var/global/datum/topic_state/conscious_state/conscious_state = new() +GLOBAL_DATUM_INIT(conscious_state, /datum/topic_state/conscious_state, new()) /datum/topic_state/conscious_state/can_use_topic(var/src_object, var/mob/user) return user.stat == CONSCIOUS ? STATUS_INTERACTIVE : STATUS_CLOSE diff --git a/code/modules/nano/interaction/contained.dm b/code/modules/nano/interaction/contained.dm index 3c84734104aa..03ac30d07ad9 100644 --- a/code/modules/nano/interaction/contained.dm +++ b/code/modules/nano/interaction/contained.dm @@ -1,7 +1,7 @@ /* This state checks if user is somewhere within src_object, as well as the default NanoUI interaction. */ -/var/global/datum/topic_state/contained_state/contained_state = new() +GLOBAL_DATUM_INIT(contained_state, /datum/topic_state/contained_state, new()) /datum/topic_state/contained_state/can_use_topic(var/atom/src_object, var/mob/user) if(!src_object.contains(user)) diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm index e5062bc0b122..ca42220204d2 100644 --- a/code/modules/nano/interaction/default.dm +++ b/code/modules/nano/interaction/default.dm @@ -1,4 +1,4 @@ -/var/global/datum/topic_state/default/default_state = new() +GLOBAL_DATUM_INIT(default_state, /datum/topic_state/default, new()) /datum/topic_state/default/href_list(var/mob/user) return list() @@ -51,7 +51,7 @@ // If we're installed in a chassi, rather than transfered to an inteliCard or other container, then check if we have camera view if(is_in_chassis()) //stop AIs from leaving windows open and using then after they lose vision - if(cameranet && !cameranet.checkTurfVis(get_turf(src_object))) + if(GLOB.cameranet && !GLOB.cameranet.checkTurfVis(get_turf(src_object))) return STATUS_CLOSE return STATUS_INTERACTIVE else if(get_dist(src_object, src) <= client.view) // View does not return what one would expect while installed in an inteliCard diff --git a/code/modules/nano/interaction/ghost.dm b/code/modules/nano/interaction/ghost.dm index 6b0aa3db1ca5..bb435b338962 100644 --- a/code/modules/nano/interaction/ghost.dm +++ b/code/modules/nano/interaction/ghost.dm @@ -1,9 +1,9 @@ /* - This checks that the user is a ghost or alternatively an admin. Used for the mob spawner. + This checks that the user is a ghost or alternatively an admin. Used for the mob spawner. We don't want any living people somehow getting the menu open and reincarnating while alive */ -/var/global/datum/topic_state/ghost_state/ghost_state = new() +GLOBAL_DATUM_INIT(ghost_state, /datum/topic_state/ghost_state, new()) /datum/topic_state/ghost_state/can_use_topic(var/src_object, var/mob/user) if(user.stat == DEAD) diff --git a/code/modules/nano/interaction/inventory.dm b/code/modules/nano/interaction/inventory.dm index cbe5165e5fff..bd0e12ea5a11 100644 --- a/code/modules/nano/interaction/inventory.dm +++ b/code/modules/nano/interaction/inventory.dm @@ -1,7 +1,7 @@ /* This state checks that the src_object is somewhere in the user's first-level inventory (in hands, on ear, etc.), but not further down (such as in bags). */ -/var/global/datum/topic_state/inventory_state/inventory_state = new() +GLOBAL_DATUM_INIT(inventory_state, /datum/topic_state/inventory_state, new()) /datum/topic_state/inventory_state/can_use_topic(var/src_object, var/mob/user) if(!((src_object in user) || user.is_in_active_hand(src_object) || user.is_in_inactive_hand(src_object))) diff --git a/code/modules/nano/interaction/inventory_deep.dm b/code/modules/nano/interaction/inventory_deep.dm index 98078c02c1dc..6d6e3667675b 100644 --- a/code/modules/nano/interaction/inventory_deep.dm +++ b/code/modules/nano/interaction/inventory_deep.dm @@ -1,7 +1,7 @@ /* This state checks if src_object is contained anywhere in the user's inventory, including bags, etc. */ -/var/global/datum/topic_state/deep_inventory_state/deep_inventory_state = new() +GLOBAL_DATUM_INIT(deep_inventory_state, /datum/topic_state/deep_inventory_state, new()) /datum/topic_state/deep_inventory_state/can_use_topic(var/src_object, var/mob/user) if(!user.contains(src_object)) diff --git a/code/modules/nano/interaction/not_incapacitated.dm b/code/modules/nano/interaction/not_incapacitated.dm index a0116409c90f..8539315f3c68 100644 --- a/code/modules/nano/interaction/not_incapacitated.dm +++ b/code/modules/nano/interaction/not_incapacitated.dm @@ -2,13 +2,12 @@ * This state only checks if the user isn't incapacitated **/ -/var/global/datum/topic_state/not_incapacitated_state/not_incapacitated_state = new() +GLOBAL_DATUM_INIT(not_incapacitated_state, /datum/topic_state/not_incapacitated_state, new()) /** * This state checks if the user isn't incapacitated and that their loc is a turf **/ - -/var/global/datum/topic_state/not_incapacitated_state/not_incapacitated_turf_state = new(no_turfs = TRUE) +GLOBAL_DATUM_INIT(not_incapacitated_turf_state, /datum/topic_state/not_incapacitated_state, new(no_turfs = TRUE)) /datum/topic_state/not_incapacitated_state var/turf_check = FALSE @@ -22,4 +21,4 @@ return STATUS_CLOSE if(user.incapacitated() || user.lying || (turf_check && !isturf(user.loc))) return STATUS_DISABLED - return STATUS_INTERACTIVE \ No newline at end of file + return STATUS_INTERACTIVE diff --git a/code/modules/nano/interaction/physical.dm b/code/modules/nano/interaction/physical.dm index 424ea3a63044..193f8c094c16 100644 --- a/code/modules/nano/interaction/physical.dm +++ b/code/modules/nano/interaction/physical.dm @@ -1,4 +1,4 @@ -/var/global/datum/topic_state/physical/physical_state = new() +GLOBAL_DATUM_INIT(physical_state, /datum/topic_state/physical, new()) /datum/topic_state/physical/can_use_topic(var/src_object, var/mob/user) . = user.shared_nano_interaction(src_object) diff --git a/code/modules/nano/interaction/self.dm b/code/modules/nano/interaction/self.dm index a4ae5050347d..a3d944571354 100644 --- a/code/modules/nano/interaction/self.dm +++ b/code/modules/nano/interaction/self.dm @@ -1,7 +1,7 @@ /* This state checks that the src_object is the same the as user */ -/var/global/datum/topic_state/self_state/self_state = new() +GLOBAL_DATUM_INIT(self_state, /datum/topic_state/self_state, new()) /datum/topic_state/self_state/can_use_topic(var/src_object, var/mob/user) if(src_object != user) diff --git a/code/modules/nano/interaction/zlevel.dm b/code/modules/nano/interaction/zlevel.dm index 15052bc6636c..feeb617cd490 100644 --- a/code/modules/nano/interaction/zlevel.dm +++ b/code/modules/nano/interaction/zlevel.dm @@ -2,7 +2,7 @@ This state checks that the user is on the same Z-level as src_object */ -/var/global/datum/topic_state/z_state/z_state = new() +GLOBAL_DATUM_INIT(z_state, /datum/topic_state/z_state, new()) /datum/topic_state/z_state/can_use_topic(var/src_object, var/mob/user) var/turf/turf_obj = get_turf(src_object) diff --git a/code/modules/nano/modules/alarm_monitor.dm b/code/modules/nano/modules/alarm_monitor.dm index dc52841a367a..cee3820b102d 100644 --- a/code/modules/nano/modules/alarm_monitor.dm +++ b/code/modules/nano/modules/alarm_monitor.dm @@ -48,21 +48,21 @@ if(..()) return 1 if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras + var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras if(!C || !isAI(usr)) return usr.switch_to_camera(C) return 1 -/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state) ui.open() ui.set_auto_update(1) -/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/categories[0] diff --git a/code/modules/nano/modules/atmos_control.dm b/code/modules/nano/modules/atmos_control.dm index 3b61ea0f5579..76d14bb3e009 100644 --- a/code/modules/nano/modules/atmos_control.dm +++ b/code/modules/nano/modules/atmos_control.dm @@ -28,7 +28,7 @@ var/datum/topic_state/air_alarm/TS = generate_state(alarm) alarm.ui_interact(usr, master_ui = ui_ref, state = TS) -/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) +/datum/nano_module/atmos_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/master_ui = null, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "atmos_control.tmpl", src.name, 900, 800, state = state) @@ -39,9 +39,9 @@ ui.set_auto_update(1) ui_ref = ui -/datum/nano_module/atmos_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/nano_module/atmos_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] - data["alarms"] = air_alarm_repository.air_alarm_data(monitored_alarms) + data["alarms"] = GLOB.air_alarm_repository.air_alarm_data(monitored_alarms) return data diff --git a/code/modules/nano/modules/crew_monitor.dm b/code/modules/nano/modules/crew_monitor.dm index 755009ac0be2..4ed7fc03de1c 100644 --- a/code/modules/nano/modules/crew_monitor.dm +++ b/code/modules/nano/modules/crew_monitor.dm @@ -16,7 +16,7 @@ AI.ai_actual_track(H) return 1 -/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/nano_module/crew_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "crew_monitor.tmpl", "Crew Monitoring Computer", 900, 800) @@ -31,11 +31,11 @@ // should make the UI auto-update; doesn't seem to? ui.set_auto_update(1) -/datum/nano_module/crew_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/nano_module/crew_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/turf/T = get_turf(nano_host()) data["isAI"] = isAI(user) - data["crewmembers"] = crew_repository.health_data(T) + data["crewmembers"] = GLOB.crew_repository.health_data(T) return data diff --git a/code/modules/nano/modules/ert_manager.dm b/code/modules/nano/modules/ert_manager.dm index e218ad2ae685..98a3e19d052b 100644 --- a/code/modules/nano/modules/ert_manager.dm +++ b/code/modules/nano/modules/ert_manager.dm @@ -11,7 +11,7 @@ var/autoclose = 0 -/datum/nano_module/ert_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = admin_state) +/datum/nano_module/ert_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.admin_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(ui && autoclose) ui.close() @@ -50,7 +50,7 @@ cyborg_slots = text2num(href_list["set_cyb"]) if(href_list["dispatch_ert"]) - ert_request_answered = TRUE + GLOB.ert_request_answered = TRUE var/slots_list = list() if(commander_slots > 0) slots_list += "commander: [commander_slots]" @@ -70,7 +70,7 @@ notify_ghosts("An ERT is being dispatched. Open positions: [slot_text]") message_admins("[key_name_admin(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]", 1) log_admin("[key_name(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]") - event_announcement.Announce("Attention, [station_name()]. We are attempting to assemble an ERT. Standby.", "ERT Protocol Activated") + GLOB.event_announcement.Announce("Attention, [station_name()]. We are attempting to assemble an ERT. Standby.", "ERT Protocol Activated") autoclose = 1 ui_interact(usr) trigger_armed_response_team(convert_ert_string(ert_type), commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) diff --git a/code/modules/nano/modules/human_appearance.dm b/code/modules/nano/modules/human_appearance.dm index 07810eb92a0a..485fee0acb86 100644 --- a/code/modules/nano/modules/human_appearance.dm +++ b/code/modules/nano/modules/human_appearance.dm @@ -25,7 +25,7 @@ src.whitelist = species_whitelist src.blacklist = species_blacklist -/datum/nano_module/appearance_changer/Topic(ref, href_list, var/nowindow, var/datum/topic_state/state = default_state) +/datum/nano_module/appearance_changer/Topic(ref, href_list, var/nowindow, var/datum/topic_state/state = GLOB.default_state) if(..()) return 1 @@ -173,14 +173,14 @@ return 0 -/datum/nano_module/appearance_changer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/nano_module/appearance_changer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "appearance_changer.tmpl", "[src]", 800, 450, state = state) ui.open() ui.set_auto_update(1) -/datum/nano_module/appearance_changer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/nano_module/appearance_changer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) generate_data(check_whitelist, whitelist, blacklist) var/data[0] diff --git a/code/modules/nano/modules/law_manager.dm b/code/modules/nano/modules/law_manager.dm index a8a8d5e688ae..f2423e57c2f7 100644 --- a/code/modules/nano/modules/law_manager.dm +++ b/code/modules/nano/modules/law_manager.dm @@ -152,14 +152,14 @@ return 0 -/datum/nano_module/law_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/nano_module/law_manager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "law_manager.tmpl", sanitize("[src] - [owner.name]"), 800, is_malf(user) ? 600 : 400, state = state) ui.open() ui.set_auto_update(1) -/datum/nano_module/law_manager/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/nano_module/law_manager/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] owner.lawsync() diff --git a/code/modules/nano/modules/nano_module.dm b/code/modules/nano/modules/nano_module.dm index c62067bfaf8e..a9afc94f69b4 100644 --- a/code/modules/nano/modules/nano_module.dm +++ b/code/modules/nano/modules/nano_module.dm @@ -12,5 +12,5 @@ if(host) host.on_ui_close(user) -/datum/nano_module/proc/can_still_topic(var/datum/topic_state/state = default_state) +/datum/nano_module/proc/can_still_topic(var/datum/topic_state/state = GLOB.default_state) return CanUseTopic(usr, state) == STATUS_INTERACTIVE diff --git a/code/modules/nano/modules/power_monitor.dm b/code/modules/nano/modules/power_monitor.dm index 00dcf9819ec3..669482430351 100644 --- a/code/modules/nano/modules/power_monitor.dm +++ b/code/modules/nano/modules/power_monitor.dm @@ -11,20 +11,20 @@ if(!select_monitor) powermonitor = nano_host() -/datum/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/nano_module/power_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state) ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) if(!ui) ui = new(user, src, ui_key, "power_monitor.tmpl", "Power Monitoring Console", 800, 700, state = state) ui.open() ui.set_auto_update(1) -/datum/nano_module/power_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/nano_module/power_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["powermonitor"] = powermonitor if(select_monitor) data["select_monitor"] = 1 - data["powermonitors"] = powermonitor_repository.powermonitor_data() + data["powermonitors"] = GLOB.powermonitor_repository.powermonitor_data() if(powermonitor && !isnull(powermonitor.powernet)) if(select_monitor && (powermonitor.stat & (NOPOWER|BROKEN))) @@ -33,7 +33,7 @@ data["poweravail"] = powermonitor.powernet.avail data["powerload"] = powermonitor.powernet.viewload data["powerdemand"] = powermonitor.powernet.load - data["apcs"] = apc_repository.apc_data(powermonitor.powernet) + data["apcs"] = GLOB.apc_repository.apc_data(powermonitor.powernet) return data diff --git a/code/modules/nano/nanoexternal.dm b/code/modules/nano/nanoexternal.dm index 8e7269972fd0..602a46dbfa32 100644 --- a/code/modules/nano/nanoexternal.dm +++ b/code/modules/nano/nanoexternal.dm @@ -37,7 +37,7 @@ * * @return nothing */ -/datum/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/state = default_state) +/datum/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/state = GLOB.default_state) return /** @@ -57,7 +57,7 @@ * * @return list() */ -/datum/proc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/datum/proc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) return list() // Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm index 10bf8f288a61..be9c5bbe2b39 100644 --- a/code/modules/nano/nanomapgen.dm +++ b/code/modules/nano/nanomapgen.dm @@ -87,4 +87,4 @@ if(Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) return NANOMAP_BADOUTPUT - return NANOMAP_SUCCESS \ No newline at end of file + return NANOMAP_SUCCESS diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm index 0df04d27928b..526ca1ffe042 100644 --- a/code/modules/nano/nanoui.dm +++ b/code/modules/nano/nanoui.dm @@ -71,7 +71,7 @@ nanoui is used to open and update nano browser uis * * @return /nanoui new nanoui object */ -/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/state = default_state) +/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/state = GLOB.default_state) user = nuser src_object = nsrc_object ui_key = nui_key @@ -176,7 +176,7 @@ nanoui is used to open and update nano browser uis var/name = "[src_object]" var/list/config_data = list( "title" = title, - "map" = (using_map && using_map.name) ? using_map.name : "Unknown", + "map" = (GLOB.using_map && GLOB.using_map.name) ? GLOB.using_map.name : "Unknown", "srcObject" = list("name" = name), "stateKey" = state_key, "status" = status, diff --git a/code/modules/ninja/Ninja_Readme.dm b/code/modules/ninja/Ninja_Readme.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/Ninja_Readme.dm +++ b/code/modules/ninja/Ninja_Readme.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/__ninjaDefines.dm b/code/modules/ninja/__ninjaDefines.dm index a8dc65f9f13a..4c3562273cc1 100644 --- a/code/modules/ninja/__ninjaDefines.dm +++ b/code/modules/ninja/__ninjaDefines.dm @@ -10,4 +10,4 @@ Contents: //ninjacost() specificCheck defines #define N_STEALTH_CANCEL 1 #define N_SMOKE_BOMB 2 -#define N_ADRENALINE 3 \ No newline at end of file +#define N_ADRENALINE 3 diff --git a/code/modules/ninja/admin_ninja_verbs.dm b/code/modules/ninja/admin_ninja_verbs.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/admin_ninja_verbs.dm +++ b/code/modules/ninja/admin_ninja_verbs.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm index ee851253449e..cf694e59d838 100644 --- a/code/modules/ninja/energy_katana.dm +++ b/code/modules/ninja/energy_katana.dm @@ -75,4 +75,4 @@ /obj/item/katana/energy/Destroy() QDEL_NULL(spark_system) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/ninja/ninja_event.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm +++ b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm index c7ca781c04ef..54a7f4e42066 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm @@ -20,4 +20,4 @@ /obj/item/gun/energy/shuriken/proc/throw() qdel(src) - return \ No newline at end of file + return diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/suit_process.dm b/code/modules/ninja/suit/suit_process.dm index 0ffdd02fcbce..70b786d12ed0 100644 --- a/code/modules/ninja/suit/suit_process.dm +++ b/code/modules/ninja/suit/suit_process.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index c32e1fc8a88d..b1fe71db72ea 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -265,8 +265,8 @@ /obj/item/paper/contract/infernal/power/FulfillContract(mob/living/carbon/human/user = target.current, blood = 0) if(!user.dna) return -1 - user.dna.SetSEState(HULKBLOCK,1) - genemutcheck(user, HULKBLOCK,null,MUTCHK_FORCED) + user.dna.SetSEState(GLOB.hulkblock,1) + genemutcheck(user, GLOB.hulkblock,null,MUTCHK_FORCED) // Demonic power gives you consequenceless hulk user.gene_stability += GENE_INSTABILITY_MAJOR if(ishuman(user)) @@ -329,4 +329,4 @@ if(!istype(user) || !user.mind) return -1 user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_friend(null)) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/paperwork/fax.dm b/code/modules/paperwork/fax.dm index 4b8d4f9db6ee..360b5a785007 100644 --- a/code/modules/paperwork/fax.dm +++ b/code/modules/paperwork/fax.dm @@ -1,6 +1,6 @@ // Fax datum - holds all faxes sent during the round -var/list/faxes = list() -var/list/adminfaxes = list() +GLOBAL_LIST_EMPTY(faxes) +GLOBAL_LIST_EMPTY(adminfaxes) /datum/fax var/name = "fax" @@ -12,13 +12,13 @@ var/list/adminfaxes = list() var/sent_at = null /datum/fax/New() - faxes += src + GLOB.faxes += src /datum/fax/admin var/list/reply_to = null /datum/fax/admin/New() - adminfaxes += src + GLOB.adminfaxes += src // Fax panel - lets admins check all faxes sent during the round /client/proc/fax_panel() @@ -37,7 +37,7 @@ var/list/adminfaxes = list() html += "

    Admin Faxes

    " html += "" html += "" - for(var/datum/fax/admin/A in adminfaxes) + for(var/datum/fax/admin/A in GLOB.adminfaxes) html += "" html += "" html += "" @@ -66,7 +66,7 @@ var/list/adminfaxes = list() html += "

    Departmental Faxes

    " html += "
    NameFrom DepartmentTo DepartmentSent AtSent ByViewReplyReplied To
    [A.name][A.from_department]
    " html += "" - for(var/datum/fax/F in faxes) + for(var/datum/fax/F in GLOB.faxes) html += "" html += "" html += "" diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index df90d0863688..098580731426 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -1,9 +1,9 @@ -var/list/obj/machinery/photocopier/faxmachine/allfaxes = list() -var/list/admin_departments = list("Central Command") -var/list/hidden_admin_departments = list("Syndicate") -var/list/alldepartments = list() -var/list/hidden_departments = list() -var/global/list/fax_blacklist = list() +GLOBAL_LIST_EMPTY(allfaxes) +GLOBAL_LIST_INIT(admin_departments, list("Central Command")) +GLOBAL_LIST_INIT(hidden_admin_departments, list("Syndicate")) +GLOBAL_LIST_EMPTY(alldepartments) +GLOBAL_LIST_EMPTY(hidden_departments) +GLOBAL_LIST_EMPTY(fax_blacklist) /obj/machinery/photocopier/faxmachine name = "fax machine" @@ -33,13 +33,13 @@ var/global/list/fax_blacklist = list() /obj/machinery/photocopier/faxmachine/New() ..() - allfaxes += src + GLOB.allfaxes += src update_network() /obj/machinery/photocopier/faxmachine/proc/update_network() if(department != "Unknown") - if(!(("[department]" in alldepartments) || ("[department]" in hidden_departments) || ("[department]" in admin_departments) || ("[department]" in hidden_admin_departments))) - alldepartments |= department + if(!(("[department]" in GLOB.alldepartments) || ("[department]" in GLOB.hidden_departments) || ("[department]" in GLOB.admin_departments) || ("[department]" in GLOB.hidden_admin_departments))) + GLOB.alldepartments |= department /obj/machinery/photocopier/faxmachine/longrange name = "long range fax machine" @@ -55,7 +55,7 @@ var/global/list/fax_blacklist = list() /obj/machinery/photocopier/faxmachine/longrange/syndie/update_network() if(department != "Unknown") - hidden_departments |= department + GLOB.hidden_departments |= department /obj/machinery/photocopier/faxmachine/attack_hand(mob/user) ui_interact(user) @@ -86,7 +86,7 @@ var/global/list/fax_blacklist = list() ui = new(user, src, ui_key, "faxmachine.tmpl", "Fax Machine UI", 540, 450) ui.open() -/obj/machinery/photocopier/faxmachine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/photocopier/faxmachine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/is_authenticated = is_authenticated(user) @@ -109,7 +109,7 @@ var/global/list/fax_blacklist = list() data["paperinserted"] = 0 data["destination"] = destination data["cooldown"] = sendcooldown - if((destination in admin_departments) || (destination in hidden_admin_departments)) + if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) data["respectcooldown"] = 1 else data["respectcooldown"] = 0 @@ -130,7 +130,7 @@ var/global/list/fax_blacklist = list() var/is_authenticated = is_authenticated(usr) if(href_list["send"]) if(copyitem && is_authenticated) - if((destination in admin_departments) || (destination in hidden_admin_departments)) + if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments)) send_admin_fax(usr, destination) else sendfax(destination, usr) @@ -163,18 +163,18 @@ var/global/list/fax_blacklist = list() if(href_list["dept"]) if(is_authenticated) var/lastdestination = destination - var/list/combineddepartments = alldepartments.Copy() + var/list/combineddepartments = GLOB.alldepartments.Copy() if(long_range_enabled) - combineddepartments += admin_departments.Copy() + combineddepartments += GLOB.admin_departments.Copy() if(emagged) - combineddepartments += hidden_admin_departments.Copy() - combineddepartments += hidden_departments.Copy() + combineddepartments += GLOB.hidden_admin_departments.Copy() + combineddepartments += GLOB.hidden_departments.Copy() if(syndie_restricted) - combineddepartments = hidden_admin_departments.Copy() - combineddepartments += hidden_departments.Copy() - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) + combineddepartments = GLOB.hidden_admin_departments.Copy() + combineddepartments += GLOB.hidden_departments.Copy() + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.emagged)//we can contact emagged faxes on the station combineddepartments |= F.department @@ -184,7 +184,7 @@ var/global/list/fax_blacklist = list() if(href_list["auth"]) if(!is_authenticated && scan) - if(scan.registered_name in fax_blacklist) + if(scan.registered_name in GLOB.fax_blacklist) playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) else if(check_access(scan)) authenticated = 1 @@ -252,7 +252,7 @@ var/global/list/fax_blacklist = list() use_power(200) var/success = 0 - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.department == destination) success = F.receivefax(copyitem) @@ -324,7 +324,7 @@ var/global/list/fax_blacklist = list() message_admins(sender, "CENTCOM FAX", destination, copyitem, "#006100") if("Syndicate") message_admins(sender, "SYNDICATE FAX", destination, copyitem, "#DC143C") - for(var/obj/machinery/photocopier/faxmachine/F in allfaxes) + for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes) if(F.department == destination) F.receivefax(copyitem) sendcooldown = cooldown_time @@ -344,4 +344,4 @@ var/global/list/fax_blacklist = list() if(scan) scan.forceMove(get_turf(src)) var/mob/living/simple_animal/hostile/mimic/copy/M = new(loc, src, null, 1) // it will delete src on creation and override any machine checks - M.name = "angry fax machine" \ No newline at end of file + M.name = "angry fax machine" diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index 7d6144bee1b5..626a3d99e592 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -119,9 +119,9 @@ /obj/structure/filingcabinet/security/proc/populate() if(virgin) - for(var/datum/data/record/G in data_core.general) + for(var/datum/data/record/G in GLOB.data_core.general) var/datum/data/record/S - for(var/datum/data/record/R in data_core.security) + for(var/datum/data/record/R in GLOB.data_core.security) if(R.fields["name"] == G.fields["name"] || R.fields["id"] == G.fields["id"]) S = R break @@ -153,9 +153,9 @@ /obj/structure/filingcabinet/medical/proc/populate() if(virgin) - for(var/datum/data/record/G in data_core.general) + for(var/datum/data/record/G in GLOB.data_core.general) var/datum/data/record/M - for(var/datum/data/record/R in data_core.medical) + for(var/datum/data/record/R in GLOB.data_core.medical) if(R.fields["name"] == G.fields["name"] || R.fields["id"] == G.fields["id"]) M = R break @@ -183,7 +183,7 @@ * Employment contract Cabinets */ -var/list/employmentCabinets = list() +GLOBAL_LIST_EMPTY(employmentCabinets) /obj/structure/filingcabinet/employment var/cooldown = 0 @@ -191,16 +191,16 @@ var/list/employmentCabinets = list() var/virgin = 1 /obj/structure/filingcabinet/employment/New() - employmentCabinets += src + GLOB.employmentCabinets += src return ..() /obj/structure/filingcabinet/employment/Destroy() - employmentCabinets -= src + GLOB.employmentCabinets -= src return ..() /obj/structure/filingcabinet/employment/proc/fillCurrent() //This proc fills the cabinet with the current crew. - for(var/record in data_core.locked) + for(var/record in GLOB.data_core.locked) var/datum/data/record/G = record if(!G) continue diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 75d6d9084ca4..34acdcf09834 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -21,7 +21,9 @@ max_integrity = 50 attack_verb = list("bapped") dog_fashion = /datum/dog_fashion/head + var/header //Above the main body, displayed at the top var/info //What's actually written on the paper. + var/footer //The bottom stuff before the stamp but after the body var/info_links //A different version of the paper which includes html links at fields and EOF var/stamps //The (text for the) stamps on the paper. var/fields //Amount of user created fields @@ -76,9 +78,9 @@ var/data var/stars = (!user.say_understands(null, GLOB.all_languages["Galactic Common"]) && !forceshow) || forcestars if(stars) //assuming all paper is written in common is better than hardcoded type checks - data = "[stars(info)][stamps]" + data = "[header][stars(info)][footer][stamps]" else - data = "
    [infolinks ? info_links : info]
    [stamps]" + data = "[header]
    [infolinks ? info_links : info]
    [footer][stamps]" if(view) var/datum/browser/popup = new(user, "Paper[UID()]", , paper_width, paper_height) popup.stylesheets = list() @@ -587,6 +589,23 @@ /obj/item/paper/crumpled name = "paper scrap" icon_state = "scrap" + +/obj/item/paper/syndicate + name = "paper" + header = "


    " + info = "" + +/obj/item/paper/nanotrasen + name = "paper" + header = "


    " + info = "" + +/obj/item/paper/central_command + name = "paper" + header ="


    Nanotrasen Central Command

    Official Expedited Memorandum


    " + info = "" + footer = "

    Failure to adhere appropriately to orders that may be contained herein is in violation of Space Law, and punishments may be administered appropriately upon return to Central Command.
    The recipient(s) of this memorandum acknowledge by reading it that they are liable for any and all damages to crew or station that may arise from ignoring suggestions or advice given herein.

    " + /obj/item/paper/crumpled/update_icon() return @@ -677,10 +696,10 @@ to_chat(H, "You feel surrounded by sadness. Sadness... and HONKS!") H.makeCluwne() else if(myeffect == "Demote") - event_announcement.Announce("[target.real_name] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order") + GLOB.event_announcement.Announce("[target.real_name] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order") else if(myeffect == "Demote with Bot") - event_announcement.Announce("[target.real_name] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order") - for(var/datum/data/record/R in sortRecord(data_core.security)) + GLOB.event_announcement.Announce("[target.real_name] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order") + for(var/datum/data/record/R in sortRecord(GLOB.data_core.security)) if(R.fields["name"] == target.real_name) R.fields["criminal"] = "*Arrest*" update_all_mob_security_hud() @@ -689,7 +708,7 @@ new /obj/effect/portal(T) new /mob/living/simple_animal/bot/secbot(T) else if(myeffect == "Revoke Fax Access") - fax_blacklist += target.real_name + GLOB.fax_blacklist += target.real_name if(fax) fax.authenticated = 0 else if(myeffect == "Angry Fax Machine") @@ -725,4 +744,4 @@ var/mytech = pick(possible_techs) var/mylevel = rand(7, 9) origin_tech = "[mytech]=[mylevel]" - name = "research notes - [mytech] [mylevel]" \ No newline at end of file + name = "research notes - [mytech] [mylevel]" diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 68afe9fbd127..d76765b61a78 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -10,7 +10,8 @@ pressure_resistance = 8 var/amount = 30 //How much paper is in the bin. var/list/papers = list() //List of papers put in the bin for reference. - + var/letterhead_type + /obj/item/paper_bin/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) if(amount) amount = 0 @@ -71,7 +72,10 @@ P = papers[papers.len] papers.Remove(P) else - P = new /obj/item/paper + if(letterhead_type && alert("Choose a style",,"Letterhead","Blank")=="Letterhead") + P = new letterhead_type + else + P = new /obj/item/paper if(SSholiday.holidays && SSholiday.holidays[APRIL_FOOLS]) if(prob(30)) P.info = "HONK HONK HONK HONK HONK HONK HONK
    HOOOOOOOOOOOOOOOOOOOOOONK
    APRIL FOOLS
    " @@ -139,3 +143,13 @@ add_fingerprint(user) return + + +/obj/item/paper_bin/nanotrasen + name = "nanotrasen paper bin" + letterhead_type = /obj/item/paper/nanotrasen + +/obj/item/paper_bin/syndicate + name = "syndicate paper bin" + letterhead_type = /obj/item/paper/syndicate + diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 0954127b00a9..5ca533962533 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -64,14 +64,14 @@ if(toner <= 0) break - if(copier_items_printed >= copier_max_items) //global vars defined in misc.dm + if(GLOB.copier_items_printed >= GLOB.copier_max_items) //global vars defined in misc.dm if(prob(10)) visible_message("The printer screen reads \"PC LOAD LETTER\".") else visible_message("The printer screen reads \"PHOTOCOPIER NETWORK OFFLINE, PLEASE CONTACT SYSTEM ADMINISTRATOR\".") - if(!copier_items_printed_logged) - message_admins("Photocopier cap of [copier_max_items] papers reached, all photocopiers are now disabled. This may be the cause of any lag.") - copier_items_printed_logged = TRUE + if(!GLOB.copier_items_printed_logged) + message_admins("Photocopier cap of [GLOB.copier_max_items] papers reached, all photocopiers are now disabled. This may be the cause of any lag.") + GLOB.copier_items_printed_logged = TRUE break if(emag_cooldown > world.time) @@ -98,7 +98,7 @@ else to_chat(usr, "\The [copyitem] can't be copied by \the [src].") break - copier_items_printed++ + GLOB.copier_items_printed++ use_power(active_power_usage) updateUsrDialog() else if(href_list["remove"]) @@ -184,10 +184,12 @@ /obj/machinery/photocopier/wrench_act(mob/user, obj/item/I) . = TRUE default_unfasten_wrench(user, I) - + /obj/machinery/photocopier/proc/copy(var/obj/item/paper/copy) var/obj/item/paper/c = new /obj/item/paper (loc) + c.header = copy.header c.info = copy.info + c.footer = copy.footer c.name = copy.name // -- Doohl c.fields = copy.fields c.stamps = copy.stamps diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 4129ddf30ada..30e8cbd9541c 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -1,580 +1,580 @@ -/* Photography! - * Contains: - * Camera - * Camera Film - * Photos - * Photo Albums - */ - -/******* -* film * -*******/ -/obj/item/camera_film - name = "film cartridge" - icon = 'icons/obj/items.dmi' - desc = "A camera film cartridge. Insert it into a camera to reload it." - icon_state = "film" - item_state = "electropack" - w_class = WEIGHT_CLASS_TINY - resistance_flags = FLAMMABLE - - -/******** -* photo * -********/ -/obj/item/photo - name = "photo" - icon = 'icons/obj/items.dmi' - icon_state = "photo" - item_state = "paper" - w_class = WEIGHT_CLASS_SMALL - resistance_flags = FLAMMABLE - max_integrity = 50 - var/blueprints = 0 // Does this have the blueprints? - var/icon/img //Big photo image - var/scribble //Scribble on the back. - var/icon/tiny - var/photo_size = 3 - -/obj/item/photo/attack_self(mob/user as mob) - user.examinate(src) - -/obj/item/photo/attackby(obj/item/P as obj, mob/user as mob, params) - if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) - var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) - txt = copytext(txt, 1, 128) - if(loc == user && user.stat == 0) - scribble = txt - else if(istype(P, /obj/item/lighter)) - burnphoto(P, user) - ..() - -/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user) - var/class = "" - - if(P.lit && !user.restrained()) - if(istype(P, /obj/item/lighter/zippo)) - class = "" - - user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \ - "[class]You hold [P] up to [src], burning it slowly.") - - spawn(20) - if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) - user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ - "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") - - if(user.is_in_inactive_hand(src)) - user.unEquip(src) - - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) - - else - to_chat(user, "You must hold \the [P] steady to burn \the [src].") - -/obj/item/photo/examine(mob/user) - . = ..() - if(in_range(user, src) || isobserver(user)) - show(user) - else - . += "It is too far away." - -/obj/item/photo/proc/show(mob/user as mob) - var/icon/img_shown = new/icon(img) - var/colormatrix = user.get_screen_colour() - // Apply colorblindness effects, if any. - if(islist(colormatrix)) - img_shown.MapColors( - colormatrix[1], colormatrix[2], colormatrix[3], - colormatrix[4], colormatrix[5], colormatrix[6], - colormatrix[7], colormatrix[8], colormatrix[9], - ) - usr << browse_rsc(img_shown, "tmp_photo.png") - usr << browse("[name]" \ - + "" \ - + "" \ - + "[scribble ? "
    Written on the back:
    [scribble]" : ""]"\ - + "", "window=Photo[UID()];size=[64*photo_size]x[scribble ? 400 : 64*photo_size]") - onclose(usr, "Photo[UID()]") - return - -/obj/item/photo/verb/rename() - set name = "Rename photo" - set category = "Object" - set src in usr - - var/n_name = sanitize(copytext(input(usr, "What would you like to label the photo?", "Photo Labelling", name) as text, 1, MAX_MESSAGE_LEN)) - //loc.loc check is for making possible renaming photos in clipboards - if(( (loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0)) - name = "[(n_name ? text("[n_name]") : "photo")]" - add_fingerprint(usr) - return - - -/************** -* photo album * -**************/ -/obj/item/storage/photo_album - name = "Photo album" - icon = 'icons/obj/items.dmi' - icon_state = "album" - item_state = "briefcase" - can_hold = list(/obj/item/photo) - resistance_flags = FLAMMABLE - -/obj/item/storage/photo_album/MouseDrop(obj/over_object as obj) - - if((istype(usr, /mob/living/carbon/human))) - var/mob/M = usr - if(!( istype(over_object, /obj/screen) )) - return ..() - playsound(loc, "rustle", 50, 1, -5) - if((!( M.restrained() ) && !( M.stat ) && M.back == src)) - switch(over_object.name) - if("r_hand") - M.unEquip(src) - M.put_in_r_hand(src) - if("l_hand") - M.unEquip(src) - M.put_in_l_hand(src) - add_fingerprint(usr) - return - if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) - if(usr.s_active) - usr.s_active.close(usr) - show_to(usr) - return - return - -/********* -* camera * -*********/ -/obj/item/camera - name = "camera" - icon = 'icons/obj/items.dmi' - desc = "A polaroid camera. 10 photos left." - icon_state = "camera" - item_state = "electropack" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - var/list/matter = list("metal" = 2000) - var/pictures_max = 10 - var/pictures_left = 10 - var/on = 1 - var/blueprints = 0 - var/icon_on = "camera" - var/icon_off = "camera_off" - var/size = 3 - var/see_ghosts = 0 //for the spoop of it - - -/obj/item/camera/spooky/CheckParts(list/parts_list) - ..() - var/obj/item/camera/C = locate(/obj/item/camera) in contents - if(C) - pictures_max = C.pictures_max - pictures_left = C.pictures_left - visible_message("[C] has been imbued with godlike power!") - qdel(C) - - -var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","shadow","ghostian2") - -/obj/item/camera/spooky - name = "camera obscura" - desc = "A polaroid camera, some say it can see ghosts!" - see_ghosts = 1 - -/obj/item/camera/verb/change_size() - set name = "Set Photo Focus" - set category = "Object" - var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) - if(nsize) - size = nsize - to_chat(usr, "Camera will now take [size]x[size] photos.") - -/obj/item/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) - return - -/obj/item/camera/attack_self(mob/user as mob) - on = !on - if(on) - src.icon_state = icon_on - else - src.icon_state = icon_off - to_chat(user, "You switch the camera [on ? "on" : "off"].") - return - -/obj/item/camera/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/camera_film)) - if(pictures_left) - to_chat(user, "[src] still has some film in it!") - return - to_chat(user, "You insert [I] into [src].") - user.drop_item() - qdel(I) - pictures_left = pictures_max - return - ..() - - -/obj/item/camera/proc/get_icon(list/turfs, turf/center, mob/user) - - //Bigger icon base to capture those icons that were shifted to the next tile - //i.e. pretty much all wall-mounted machinery - var/icon/res = icon('icons/effects/96x96.dmi', "") - res.Scale(size*32, size*32) - // Initialize the photograph to black. - res.Blend("#000", ICON_OVERLAY) - - var/atoms[] = list() - for(var/turf/the_turf in turfs) - // Add ourselves to the list of stuff to draw - atoms.Add(the_turf); - // As well as anything that isn't invisible. - for(var/atom/A in the_turf) - if(A.invisibility) - if(see_ghosts && istype(A,/mob/dead/observer)) - var/mob/dead/observer/O = A - if(O.following) - continue - if(user.mind && !(user.mind.assigned_role == "Chaplain")) - atoms.Add(image('icons/mob/mob.dmi', O.loc, pick(SpookyGhosts), 4, SOUTH)) - else - atoms.Add(image('icons/mob/mob.dmi', O.loc, "ghost", 4, SOUTH)) - else//its not a ghost - continue - else//not invisable, not a spookyghost add it. - var/disguised = null - if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer. - for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances) - if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude. - atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise. - atoms.Remove(A) //Don't blow their cover. - disguised = 1 - continue - if(!disguised) //If they aren't, treat them normally. - atoms.Add(A) - - - // Sort the atoms into their layers - var/list/sorted = sort_atoms_by_layer(atoms) - var/center_offset = (size-1)/2 * 32 + 1 - for(var/i; i <= sorted.len; i++) - var/atom/A = sorted[i] - if(A) - var/icon/img = getFlatIcon(A)//build_composite_icon(A) - if(istype(A, /obj/item/areaeditor/blueprints)) - blueprints = 1 - - // If what we got back is actually a picture, draw it. - if(istype(img, /icon)) - // Check if we're looking at a mob that's lying down - if(istype(A, /mob/living) && A:lying) - // If they are, apply that effect to their picture. - img.BecomeLying() - // Calculate where we are relative to the center of the photo - var/xoff = (A.x - center.x) * 32 + center_offset - var/yoff = (A.y - center.y) * 32 + center_offset - if(istype(A,/atom/movable)) - xoff+=A:step_x - yoff+=A:step_y - res.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) - - // Lastly, render any contained effects on top. - for(var/turf/the_turf in turfs) - // Calculate where we are relative to the center of the photo - var/xoff = (the_turf.x - center.x) * 32 + center_offset - var/yoff = (the_turf.y - center.y) * 32 + center_offset - res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) - return res - - -/obj/item/camera/proc/get_mobs(turf/the_turf as turf) - var/mob_detail - for(var/mob/M in the_turf) - if(M.invisibility) - if(see_ghosts && istype(M,/mob/dead/observer)) - var/mob/dead/observer/O = M - if(O.following) - continue - if(!mob_detail) - mob_detail = "You can see a g-g-g-g-ghooooost! " - else - mob_detail += "You can also see a g-g-g-g-ghooooost!" - else - continue - - var/holding = null - - if(istype(M, /mob/living/carbon)) - var/mob/living/carbon/A = M - if(A.l_hand || A.r_hand) - if(A.l_hand) holding = "They are holding \a [A.l_hand]" - if(A.r_hand) - if(holding) - holding += " and \a [A.r_hand]" - else - holding = "They are holding \a [A.r_hand]" - - if(!mob_detail) - mob_detail = "You can see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " - else - mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." - return mob_detail - -/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - pictures_left-- - desc = "A polaroid camera. It has [pictures_left] photos left." - to_chat(user, "[pictures_left] photos left.") - icon_state = icon_off - on = 0 - if(istype(src,/obj/item/camera/spooky)) - if(user.mind && user.mind.assigned_role == "Chaplain" && see_ghosts) - if(prob(24)) - handle_haunt(user) - spawn(64) - icon_state = icon_on - on = 1 - -/obj/item/camera/proc/can_capture_turf(turf/T, mob/user) - var/viewer = user - if(user.client) //To make shooting through security cameras possible - viewer = user.client.eye - var/can_see = (T in view(viewer)) //No x-ray vision cameras. - return can_see - -/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/list/turfs = list() - var/mobs = "" - for(var/i = 1; i <= size; i++) - for(var/j = 1; j <= size; j++) - var/turf/T = locate(x_c, y_c, z_c) - if(can_capture_turf(T, user)) - turfs.Add(T) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/datum/picture/P = createpicture(target, user, turfs, mobs, flag, blueprints) - printpicture(user, P) - -/obj/item/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) - var/icon/photoimage = get_icon(turfs, target, user) - - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img,ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) - - var/datum/picture/P = new() - if(istype(src,/obj/item/camera/digital)) - P.fields["name"] = input(user,"Name photo:","photo") - P.name = P.fields["name"]//So the name is displayed on the print/delete list. - else - P.fields["name"] = "photo" - P.fields["author"] = user - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = mobs - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = size - - return P - -/obj/item/camera/proc/printpicture(mob/user, var/datum/picture/P) - var/obj/item/photo/Photo = new/obj/item/photo() - Photo.loc = user.loc - if(!user.get_inactive_hand()) - user.put_in_inactive_hand(Photo) - - if(blueprints) - Photo.blueprints = 1 - blueprints = 0 - Photo.construct(P) - -/obj/item/photo/proc/construct(var/datum/picture/P) - name = P.fields["name"] - icon = P.fields["icon"] - tiny = P.fields["tiny"] - img = P.fields["img"] - desc = P.fields["desc"] - pixel_x = P.fields["pixel_x"] - pixel_y = P.fields["pixel_y"] - photo_size = P.fields["size"] - -/obj/item/photo/proc/copy() - var/obj/item/photo/p = new/obj/item/photo() - - p.icon = icon(icon, icon_state) - p.img = icon(img) - p.tiny = icon(tiny) - p.name = name - p.desc = desc - p.scribble = scribble - p.blueprints = blueprints - - return p - -/***************** -* digital camera * -******************/ -/obj/item/camera/digital - name = "digital camera" - desc = "A digital camera. A small screen shows there is space for 10 photos left." - var/list/datum/picture/saved_pictures = list() - pictures_left = 30 - var/max_storage = 10 - -/obj/item/camera/digital/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - desc = "A digital camera. A small screen shows that there are currently [saved_pictures.len] pictures stored." - icon_state = icon_off - on = 0 - spawn(64) - icon_state = icon_on - on = 1 - -/obj/item/camera/digital/captureimage(atom/target, mob/user, flag) - if(saved_pictures.len >= max_storage) - to_chat(user, "Maximum photo storage capacity reached.") - return - to_chat(user, "Picture saved.") - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/list/turfs = list() - var/mobs = "" - for(var/i = 1; i <= size; i++) - for(var/j = 1; j <= size; j++) - var/turf/T = locate(x_c, y_c, z_c) - if(can_capture_turf(T, user)) - turfs.Add(T) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) - saved_pictures += P - -/obj/item/camera/digital/verb/print_picture() - set name = "Print picture" - set category = "Object" - set src in usr - - if(saved_pictures.len == 0) - to_chat(usr, "No images saved.") - return - if(pictures_left == 0) - to_chat(usr, "There is no film left to print.") - return - - var/datum/picture/P = null - P = input("Select image to print:",P) as null|anything in saved_pictures - if(P) - printpicture(usr,P) - pictures_left -- - -/obj/item/camera/digital/verb/delete_picture() - set name = "Delete picture" - set category = "Object" - set src in usr - - if(saved_pictures.len == 0) - to_chat(usr, "No images saved") - return - var/datum/picture/P = null - P = input("Select image to delete:",P) as null|anything in saved_pictures - if(P) - saved_pictures -= P - -/************** -*video camera * -***************/ - -/obj/item/videocam - name = "video camera" - icon = 'icons/obj/items.dmi' - desc = "video camera that can send live feed to the entertainment network." - icon_state = "videocam" - item_state = "videocam" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - materials = list(MAT_METAL=2000) - var/on = 0 - var/obj/machinery/camera/camera - var/icon_on = "videocam_on" - var/icon_off = "videocam" - var/canhear_range = 7 - -/obj/item/videocam/attack_self(mob/user) - on = !on - if(camera) - if(on==0) - src.icon_state = icon_off - camera.c_tag = null - camera.network = list() - else - src.icon_state = icon_on - camera.network = list("news") - camera.c_tag = user.name - else - - src.icon_state = icon_on - camera = new /obj/machinery/camera(src) - camera.network = list("news") - cameranet.removeCamera(camera) - camera.c_tag = user.name - to_chat(user, "You switch the camera [on ? "on" : "off"].") - -/obj/item/videocam/examine(mob/user) - . = ..() - if(in_range(user, src)) - . += "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active." - -/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) - var/msg = multilingual_to_message(message_pieces) - if(camera && on) - if(get_dist(src, M) <= canhear_range) - talk_into(M, msg) - for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) - if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) - -/obj/item/videocam/hear_message(mob/M as mob, msg) - if(camera && on) - for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) - if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) - - -///hauntings, like hallucinations but more spooky - -/obj/item/camera/proc/handle_haunt(mob/user as mob) - var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/screech.ogg',\ - 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ - 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ - 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ - 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') - user << pick(creepyasssounds) +/* Photography! + * Contains: + * Camera + * Camera Film + * Photos + * Photo Albums + */ + +/******* +* film * +*******/ +/obj/item/camera_film + name = "film cartridge" + icon = 'icons/obj/items.dmi' + desc = "A camera film cartridge. Insert it into a camera to reload it." + icon_state = "film" + item_state = "electropack" + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + + +/******** +* photo * +********/ +/obj/item/photo + name = "photo" + icon = 'icons/obj/items.dmi' + icon_state = "photo" + item_state = "paper" + w_class = WEIGHT_CLASS_SMALL + resistance_flags = FLAMMABLE + max_integrity = 50 + var/blueprints = 0 // Does this have the blueprints? + var/icon/img //Big photo image + var/scribble //Scribble on the back. + var/icon/tiny + var/photo_size = 3 + +/obj/item/photo/attack_self(mob/user as mob) + user.examinate(src) + +/obj/item/photo/attackby(obj/item/P as obj, mob/user as mob, params) + if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) + var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) + txt = copytext(txt, 1, 128) + if(loc == user && user.stat == 0) + scribble = txt + else if(istype(P, /obj/item/lighter)) + burnphoto(P, user) + ..() + +/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user) + var/class = "" + + if(P.lit && !user.restrained()) + if(istype(P, /obj/item/lighter/zippo)) + class = "" + + user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \ + "[class]You hold [P] up to [src], burning it slowly.") + + spawn(20) + if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) + user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ + "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") + + if(user.is_in_inactive_hand(src)) + user.unEquip(src) + + new /obj/effect/decal/cleanable/ash(get_turf(src)) + qdel(src) + + else + to_chat(user, "You must hold \the [P] steady to burn \the [src].") + +/obj/item/photo/examine(mob/user) + . = ..() + if(in_range(user, src) || isobserver(user)) + show(user) + else + . += "It is too far away." + +/obj/item/photo/proc/show(mob/user as mob) + var/icon/img_shown = new/icon(img) + var/colormatrix = user.get_screen_colour() + // Apply colorblindness effects, if any. + if(islist(colormatrix)) + img_shown.MapColors( + colormatrix[1], colormatrix[2], colormatrix[3], + colormatrix[4], colormatrix[5], colormatrix[6], + colormatrix[7], colormatrix[8], colormatrix[9], + ) + usr << browse_rsc(img_shown, "tmp_photo.png") + usr << browse("[name]" \ + + "" \ + + "" \ + + "[scribble ? "
    Written on the back:
    [scribble]" : ""]"\ + + "", "window=Photo[UID()];size=[64*photo_size]x[scribble ? 400 : 64*photo_size]") + onclose(usr, "Photo[UID()]") + return + +/obj/item/photo/verb/rename() + set name = "Rename photo" + set category = "Object" + set src in usr + + var/n_name = sanitize(copytext(input(usr, "What would you like to label the photo?", "Photo Labelling", name) as text, 1, MAX_MESSAGE_LEN)) + //loc.loc check is for making possible renaming photos in clipboards + if(( (loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0)) + name = "[(n_name ? text("[n_name]") : "photo")]" + add_fingerprint(usr) + return + + +/************** +* photo album * +**************/ +/obj/item/storage/photo_album + name = "Photo album" + icon = 'icons/obj/items.dmi' + icon_state = "album" + item_state = "briefcase" + can_hold = list(/obj/item/photo) + resistance_flags = FLAMMABLE + +/obj/item/storage/photo_album/MouseDrop(obj/over_object as obj) + + if((istype(usr, /mob/living/carbon/human))) + var/mob/M = usr + if(!( istype(over_object, /obj/screen) )) + return ..() + playsound(loc, "rustle", 50, 1, -5) + if((!( M.restrained() ) && !( M.stat ) && M.back == src)) + switch(over_object.name) + if("r_hand") + M.unEquip(src) + M.put_in_r_hand(src) + if("l_hand") + M.unEquip(src) + M.put_in_l_hand(src) + add_fingerprint(usr) + return + if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) + if(usr.s_active) + usr.s_active.close(usr) + show_to(usr) + return + return + +/********* +* camera * +*********/ +/obj/item/camera + name = "camera" + icon = 'icons/obj/items.dmi' + desc = "A polaroid camera. 10 photos left." + icon_state = "camera" + item_state = "electropack" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + var/list/matter = list("metal" = 2000) + var/pictures_max = 10 + var/pictures_left = 10 + var/on = 1 + var/blueprints = 0 + var/icon_on = "camera" + var/icon_off = "camera_off" + var/size = 3 + var/see_ghosts = 0 //for the spoop of it + + +/obj/item/camera/spooky/CheckParts(list/parts_list) + ..() + var/obj/item/camera/C = locate(/obj/item/camera) in contents + if(C) + pictures_max = C.pictures_max + pictures_left = C.pictures_left + visible_message("[C] has been imbued with godlike power!") + qdel(C) + + +GLOBAL_LIST_INIT(SpookyGhosts, list("ghost","shade","shade2","ghost-narsie","horror","shadow","ghostian2")) + +/obj/item/camera/spooky + name = "camera obscura" + desc = "A polaroid camera, some say it can see ghosts!" + see_ghosts = 1 + +/obj/item/camera/verb/change_size() + set name = "Set Photo Focus" + set category = "Object" + var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) + if(nsize) + size = nsize + to_chat(usr, "Camera will now take [size]x[size] photos.") + +/obj/item/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) + return + +/obj/item/camera/attack_self(mob/user as mob) + on = !on + if(on) + src.icon_state = icon_on + else + src.icon_state = icon_off + to_chat(user, "You switch the camera [on ? "on" : "off"].") + return + +/obj/item/camera/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/camera_film)) + if(pictures_left) + to_chat(user, "[src] still has some film in it!") + return + to_chat(user, "You insert [I] into [src].") + user.drop_item() + qdel(I) + pictures_left = pictures_max + return + ..() + + +/obj/item/camera/proc/get_icon(list/turfs, turf/center, mob/user) + + //Bigger icon base to capture those icons that were shifted to the next tile + //i.e. pretty much all wall-mounted machinery + var/icon/res = icon('icons/effects/96x96.dmi', "") + res.Scale(size*32, size*32) + // Initialize the photograph to black. + res.Blend("#000", ICON_OVERLAY) + + var/atoms[] = list() + for(var/turf/the_turf in turfs) + // Add ourselves to the list of stuff to draw + atoms.Add(the_turf); + // As well as anything that isn't invisible. + for(var/atom/A in the_turf) + if(A.invisibility) + if(see_ghosts && istype(A,/mob/dead/observer)) + var/mob/dead/observer/O = A + if(O.following) + continue + if(user.mind && !(user.mind.assigned_role == "Chaplain")) + atoms.Add(image('icons/mob/mob.dmi', O.loc, pick(GLOB.SpookyGhosts), 4, SOUTH)) + else + atoms.Add(image('icons/mob/mob.dmi', O.loc, "ghost", 4, SOUTH)) + else//its not a ghost + continue + else//not invisable, not a spookyghost add it. + var/disguised = null + if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer. + for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances) + if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude. + atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise. + atoms.Remove(A) //Don't blow their cover. + disguised = 1 + continue + if(!disguised) //If they aren't, treat them normally. + atoms.Add(A) + + + // Sort the atoms into their layers + var/list/sorted = sort_atoms_by_layer(atoms) + var/center_offset = (size-1)/2 * 32 + 1 + for(var/i; i <= sorted.len; i++) + var/atom/A = sorted[i] + if(A) + var/icon/img = getFlatIcon(A)//build_composite_icon(A) + if(istype(A, /obj/item/areaeditor/blueprints)) + blueprints = 1 + + // If what we got back is actually a picture, draw it. + if(istype(img, /icon)) + // Check if we're looking at a mob that's lying down + if(istype(A, /mob/living) && A:lying) + // If they are, apply that effect to their picture. + img.BecomeLying() + // Calculate where we are relative to the center of the photo + var/xoff = (A.x - center.x) * 32 + center_offset + var/yoff = (A.y - center.y) * 32 + center_offset + if(istype(A,/atom/movable)) + xoff+=A:step_x + yoff+=A:step_y + res.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) + + // Lastly, render any contained effects on top. + for(var/turf/the_turf in turfs) + // Calculate where we are relative to the center of the photo + var/xoff = (the_turf.x - center.x) * 32 + center_offset + var/yoff = (the_turf.y - center.y) * 32 + center_offset + res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) + return res + + +/obj/item/camera/proc/get_mobs(turf/the_turf as turf) + var/mob_detail + for(var/mob/M in the_turf) + if(M.invisibility) + if(see_ghosts && istype(M,/mob/dead/observer)) + var/mob/dead/observer/O = M + if(O.following) + continue + if(!mob_detail) + mob_detail = "You can see a g-g-g-g-ghooooost! " + else + mob_detail += "You can also see a g-g-g-g-ghooooost!" + else + continue + + var/holding = null + + if(istype(M, /mob/living/carbon)) + var/mob/living/carbon/A = M + if(A.l_hand || A.r_hand) + if(A.l_hand) holding = "They are holding \a [A.l_hand]" + if(A.r_hand) + if(holding) + holding += " and \a [A.r_hand]" + else + holding = "They are holding \a [A.r_hand]" + + if(!mob_detail) + mob_detail = "You can see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " + else + mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." + return mob_detail + +/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) + + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + pictures_left-- + desc = "A polaroid camera. It has [pictures_left] photos left." + to_chat(user, "[pictures_left] photos left.") + icon_state = icon_off + on = 0 + if(istype(src,/obj/item/camera/spooky)) + if(user.mind && user.mind.assigned_role == "Chaplain" && see_ghosts) + if(prob(24)) + handle_haunt(user) + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/camera/proc/can_capture_turf(turf/T, mob/user) + var/viewer = user + if(user.client) //To make shooting through security cameras possible + viewer = user.client.eye + var/can_see = (T in view(viewer)) //No x-ray vision cameras. + return can_see + +/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) + var/x_c = target.x - (size-1)/2 + var/y_c = target.y + (size-1)/2 + var/z_c = target.z + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= size; i++) + for(var/j = 1; j <= size; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - size + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag, blueprints) + printpicture(user, P) + +/obj/item/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) + var/icon/photoimage = get_icon(turfs, target, user) + + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img,ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + if(istype(src,/obj/item/camera/digital)) + P.fields["name"] = input(user,"Name photo:","photo") + P.name = P.fields["name"]//So the name is displayed on the print/delete list. + else + P.fields["name"] = "photo" + P.fields["author"] = user + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = mobs + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + P.fields["size"] = size + + return P + +/obj/item/camera/proc/printpicture(mob/user, var/datum/picture/P) + var/obj/item/photo/Photo = new/obj/item/photo() + Photo.loc = user.loc + if(!user.get_inactive_hand()) + user.put_in_inactive_hand(Photo) + + if(blueprints) + Photo.blueprints = 1 + blueprints = 0 + Photo.construct(P) + +/obj/item/photo/proc/construct(var/datum/picture/P) + name = P.fields["name"] + icon = P.fields["icon"] + tiny = P.fields["tiny"] + img = P.fields["img"] + desc = P.fields["desc"] + pixel_x = P.fields["pixel_x"] + pixel_y = P.fields["pixel_y"] + photo_size = P.fields["size"] + +/obj/item/photo/proc/copy() + var/obj/item/photo/p = new/obj/item/photo() + + p.icon = icon(icon, icon_state) + p.img = icon(img) + p.tiny = icon(tiny) + p.name = name + p.desc = desc + p.scribble = scribble + p.blueprints = blueprints + + return p + +/***************** +* digital camera * +******************/ +/obj/item/camera/digital + name = "digital camera" + desc = "A digital camera. A small screen shows there is space for 10 photos left." + var/list/datum/picture/saved_pictures = list() + pictures_left = 30 + var/max_storage = 10 + +/obj/item/camera/digital/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) + + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + desc = "A digital camera. A small screen shows that there are currently [saved_pictures.len] pictures stored." + icon_state = icon_off + on = 0 + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/camera/digital/captureimage(atom/target, mob/user, flag) + if(saved_pictures.len >= max_storage) + to_chat(user, "Maximum photo storage capacity reached.") + return + to_chat(user, "Picture saved.") + var/x_c = target.x - (size-1)/2 + var/y_c = target.y + (size-1)/2 + var/z_c = target.z + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= size; i++) + for(var/j = 1; j <= size; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - size + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) + saved_pictures += P + +/obj/item/camera/digital/verb/print_picture() + set name = "Print picture" + set category = "Object" + set src in usr + + if(saved_pictures.len == 0) + to_chat(usr, "No images saved.") + return + if(pictures_left == 0) + to_chat(usr, "There is no film left to print.") + return + + var/datum/picture/P = null + P = input("Select image to print:",P) as null|anything in saved_pictures + if(P) + printpicture(usr,P) + pictures_left -- + +/obj/item/camera/digital/verb/delete_picture() + set name = "Delete picture" + set category = "Object" + set src in usr + + if(saved_pictures.len == 0) + to_chat(usr, "No images saved") + return + var/datum/picture/P = null + P = input("Select image to delete:",P) as null|anything in saved_pictures + if(P) + saved_pictures -= P + +/************** +*video camera * +***************/ + +/obj/item/videocam + name = "video camera" + icon = 'icons/obj/items.dmi' + desc = "video camera that can send live feed to the entertainment network." + icon_state = "videocam" + item_state = "videocam" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + materials = list(MAT_METAL=2000) + var/on = 0 + var/obj/machinery/camera/camera + var/icon_on = "videocam_on" + var/icon_off = "videocam" + var/canhear_range = 7 + +/obj/item/videocam/attack_self(mob/user) + on = !on + if(camera) + if(on==0) + src.icon_state = icon_off + camera.c_tag = null + camera.network = list() + else + src.icon_state = icon_on + camera.network = list("news") + camera.c_tag = user.name + else + + src.icon_state = icon_on + camera = new /obj/machinery/camera(src) + camera.network = list("news") + GLOB.cameranet.removeCamera(camera) + camera.c_tag = user.name + to_chat(user, "You switch the camera [on ? "on" : "off"].") + +/obj/item/videocam/examine(mob/user) + . = ..() + if(in_range(user, src)) + . += "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active." + +/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) + var/msg = multilingual_to_message(message_pieces) + if(camera && on) + if(get_dist(src, M) <= canhear_range) + talk_into(M, msg) + for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) + if(T.watchers[M] == camera) + T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) + +/obj/item/videocam/hear_message(mob/M as mob, msg) + if(camera && on) + for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) + if(T.watchers[M] == camera) + T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) + + +///hauntings, like hallucinations but more spooky + +/obj/item/camera/proc/handle_haunt(mob/user as mob) + var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/screech.ogg',\ + 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ + 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ + 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ + 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') + user << pick(creepyasssounds) diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index b2cf43e5b841..f17666af19da 100755 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -1,520 +1,520 @@ - -//The advanced pea-green monochrome lcd of tomorrow. - -var/global/list/obj/item/pda/PDAs = list() - - -/obj/item/pda - name = "PDA" - desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." - icon = 'icons/obj/pda.dmi' - icon_state = "pda" - item_state = "electronic" - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_ID | SLOT_BELT | SLOT_PDA - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - origin_tech = "programming=2" - - //Main variables - var/owner = null - var/default_cartridge = 0 // Access level defined by cartridge - var/obj/item/cartridge/cartridge = null //current cartridge - var/datum/data/pda/app/current_app = null - var/datum/data/pda/app/lastapp = null - var/ui_tick = 0 - - //Secondary variables - var/model_name = "Thinktronic 5230 Personal Data Assistant" - var/datum/data/pda/utility/scanmode/scanmode = null - - var/lock_code = "" // Lockcode to unlock uplink - var/honkamt = 0 //How many honks left when infected with honk.exe - var/mimeamt = 0 //How many silence left when infected with mime.exe - var/detonate = 1 // Can the PDA be blown up? - var/ttone = "beep" //The ringtone! - var/list/ttone_sound = list("beep" = 'sound/machines/twobeep.ogg', - "boom" = 'sound/effects/explosionfar.ogg', - "slip" = 'sound/misc/slip.ogg', - "honk" = 'sound/items/bikehorn.ogg', - "SKREE" = 'sound/voice/shriek1.ogg', - "holy" = 'sound/items/PDA/ambicha4-short.ogg', - "xeno" = 'sound/voice/hiss1.ogg') - - var/list/programs = list( - new/datum/data/pda/app/main_menu, - new/datum/data/pda/app/notekeeper, - new/datum/data/pda/app/messenger, - new/datum/data/pda/app/manifest, - new/datum/data/pda/app/atmos_scanner, - new/datum/data/pda/utility/scanmode/notes, - new/datum/data/pda/utility/flashlight) - var/list/shortcut_cache = list() - var/list/shortcut_cat_order = list() - var/list/notifying_programs = list() - - var/obj/item/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. - var/ownjob = null //related to above - var/ownrank = null // this one is rank, never alt title - - var/obj/item/paicard/pai = null // A slot for a personal AI device - var/retro_mode = 0 - - -/* - * The Actual PDA - */ -/obj/item/pda/Initialize(mapload) - . = ..() - PDAs += src - PDAs = sortAtom(PDAs) - update_programs() - if(default_cartridge) - cartridge = new default_cartridge(src) - cartridge.update_programs(src) - new /obj/item/pen(src) - start_program(find_program(/datum/data/pda/app/main_menu)) - -/obj/item/pda/proc/can_use() - if(!ismob(loc)) - return 0 - - var/mob/M = loc - if(M.incapacitated()) - return 0 - if((src in M.contents) || ( istype(loc, /turf) && in_range(src, M) )) - return 1 - else - return 0 - -/obj/item/pda/GetAccess() - if(id) - return id.GetAccess() - else - return ..() - -/obj/item/pda/GetID() - return id - -/obj/item/pda/MouseDrop(obj/over_object as obj, src_location, over_location) - var/mob/M = usr - if((!istype(over_object, /obj/screen)) && can_use()) - return attack_self(M) - -/obj/item/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) - ui_tick++ - var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") - var/auto_update = 1 - if(!current_app) - return - - if(current_app.update == PDA_APP_NOUPDATE && current_app == lastapp) - auto_update = 0 - if(old_ui && (current_app == lastapp && ui_tick % 5 && current_app.update == PDA_APP_UPDATE_SLOW)) - return - - lastapp = current_app - - var/title = "Personal Data Assistant" - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "pda.tmpl", title, 630, 600, state = state) - ui.set_state_key("pda") - - // open the new ui window - ui.open() - - // auto update every Master Controller tick - ui.set_auto_update(auto_update) - -/obj/item/pda/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) - var/data[0] - - data["owner"] = owner // Who is your daddy... - data["ownjob"] = ownjob // ...and what does he do? - - // update list of shortcuts, only if they changed - if(!shortcut_cache.len) - shortcut_cache = list() - shortcut_cat_order = list() - var/prog_list = programs.Copy() - if(cartridge) - prog_list |= cartridge.programs - - for(var/A in prog_list) - var/datum/data/pda/P = A - - if(P.hidden) - continue - var/list/cat - if(P.category in shortcut_cache) - cat = shortcut_cache[P.category] - else - cat = list() - shortcut_cache[P.category] = cat - shortcut_cat_order += P.category - cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) - - // force the order of a few core categories - shortcut_cat_order = list("General") \ - + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ - + list("Scanners", "Utilities") - - data["idInserted"] = (id ? 1 : 0) - data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") - - data["useRetro"] = retro_mode - - data["cartridge_name"] = cartridge ? cartridge.name : "" - data["stationTime"] = station_time_timestamp() - - data["app"] = list() - current_app.update_ui(user, data) - data["app"] |= list( - "name" = current_app.title, - "icon" = current_app.icon, - "template" = current_app.template, - "has_back" = current_app.has_back) - - return data - -/obj/item/pda/attack_self(mob/user as mob) - user.set_machine(src) - if(active_uplink_check(user)) - return - ui_interact(user) //NanoUI requires this proc - -/obj/item/pda/proc/start_program(datum/data/pda/P) - if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) - return P.start() - return 0 - -/obj/item/pda/proc/find_program(type) - var/datum/data/pda/A = locate(type) in programs - if(A) - return A - if(cartridge) - A = locate(type) in cartridge.programs - if(A) - return A - return null - -// force the cache to rebuild on update_ui -/obj/item/pda/proc/update_shortcuts() - shortcut_cache.Cut() - -/obj/item/pda/proc/update_programs() - for(var/A in programs) - var/datum/data/pda/P = A - P.pda = src - -/obj/item/pda/Topic(href, href_list) - . = ..() - if(.) - return - - var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - var/mob/living/U = usr - if(usr.stat == DEAD) - return 0 - if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. - U.unset_machine() - if(ui) - ui.close() - return 0 - - add_fingerprint(U) - U.set_machine(src) - - if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) - cartridge.radio.Topic(href, href_list) - return 1 - - . = 1 - - switch(href_list["choice"]) - if("Home")//Go home, largely replaces the old Return - var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) - if(A) - start_program(A) - if("StartProgram") - if(href_list["program"]) - var/datum/data/pda/app/A = locate(href_list["program"]) - if(A) - start_program(A) - if("Eject")//Ejects the cart, only done from hub. - if(!isnull(cartridge)) - var/turf/T = loc - if(ismob(T)) - T = T.loc - var/obj/item/cartridge/C = cartridge - C.forceMove(T) - if(scanmode in C.programs) - scanmode = null - if(current_app in C.programs) - start_program(find_program(/datum/data/pda/app/main_menu)) - if(C.radio) - C.radio.hostpda = null - for(var/datum/data/pda/P in notifying_programs) - if(P in C.programs) - P.unnotify() - cartridge = null - update_shortcuts() - if("Authenticate")//Checks for ID - id_check(usr, 1) - if("Retro") - retro_mode = !retro_mode - if("Ringtone") - return set_ringtone() - else - if(current_app) - . = current_app.Topic(href, href_list) - -//EXTRA FUNCTIONS=================================== - if((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) - - return // return 1 tells it to refresh the UI in NanoUI - -/obj/item/pda/proc/close(mob/user) - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - ui.close() - -/obj/item/pda/verb/verb_reset_pda() - set category = "Object" - set name = "Reset PDA" - set src in usr - - if(issilicon(usr)) - return - - if(can_use(usr)) - start_program(find_program(/datum/data/pda/app/main_menu)) - notifying_programs.Cut() - overlays -= image('icons/obj/pda.dmi', "pda-r") - to_chat(usr, "You press the reset button on \the [src].") - else - to_chat(usr, "You cannot do this while restrained.") - -/obj/item/pda/AltClick(mob/user) - ..() - if(issilicon(user)) - return - - if(can_use(user)) - if(id) - remove_id(user) - else - to_chat(user, "This PDA does not have an ID in it!") - -/obj/item/pda/CtrlClick(mob/user) - ..() - if(issilicon(user)) - return - - if(can_use(user)) - remove_pen(user) - -/obj/item/pda/proc/remove_id(mob/user) - if(id) - if(ismob(loc)) - var/mob/M = loc - M.put_in_hands(id) - to_chat(user, "You remove the ID from the [name].") - else - id.forceMove(get_turf(src)) - overlays -= image('icons/goonstation/objects/pda_overlay.dmi', id.icon_state) - id = null - -/obj/item/pda/verb/verb_remove_id() - set category = "Object" - set name = "Remove id" - set src in usr - - if(issilicon(usr)) - return - - if( can_use(usr) ) - if(id) - remove_id(usr) - else - to_chat(usr, "This PDA does not have an ID in it.") - else - to_chat(usr, "You cannot do this while restrained.") - -/obj/item/pda/verb/verb_remove_pen() - set category = "Object" - set name = "Remove pen" - set src in usr - remove_pen(usr) - -/obj/item/pda/proc/remove_pen(mob/user) - - if(issilicon(user)) - return - - if( can_use(user) ) - var/obj/item/pen/O = locate() in src - if(O) - to_chat(user, "You remove the [O] from [src].") - if(istype(loc, /mob)) - var/mob/M = loc - if(M.get_active_hand() == null) - M.put_in_hands(O) - return - O.forceMove(get_turf(src)) - else - to_chat(user, "This PDA does not have a pen in it.") - else - to_chat(user, "You cannot do this while restrained.") - -/obj/item/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. - if(choice == 1) - if(id) - remove_id(user) - else - var/obj/item/I = user.get_active_hand() - if(istype(I, /obj/item/card/id)) - user.drop_item() - I.forceMove(src) - id = I - else - var/obj/item/card/I = user.get_active_hand() - if(istype(I, /obj/item/card/id) && I:registered_name) - var/obj/old_id = id - user.drop_item() - I.forceMove(src) - id = I - user.put_in_hands(old_id) - return - -/obj/item/pda/attackby(obj/item/C as obj, mob/user as mob, params) - ..() - if(istype(C, /obj/item/cartridge) && !cartridge) - cartridge = C - user.drop_item() - cartridge.forceMove(src) - cartridge.update_programs(src) - update_shortcuts() - to_chat(user, "You insert [cartridge] into [src].") - if(cartridge.radio) - cartridge.radio.hostpda = src - - else if(istype(C, /obj/item/card/id)) - var/obj/item/card/id/idcard = C - if(!idcard.registered_name) - to_chat(user, "\The [src] rejects the ID.") - return - if(!owner) - owner = idcard.registered_name - ownjob = idcard.assignment - ownrank = idcard.rank - name = "PDA-[owner] ([ownjob])" - to_chat(user, "Card scanned.") - else - //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. - if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) - if( can_use(user) )//If they can still act. - id_check(user, 2) - to_chat(user, "You put the ID into \the [src]'s slot.
    You can remove it with ALT click.
    ") - overlays += image('icons/goonstation/objects/pda_overlay.dmi', C.icon_state) - - else if(istype(C, /obj/item/paicard) && !src.pai) - user.drop_item() - C.forceMove(src) - pai = C - to_chat(user, "You slot \the [C] into [src].") - else if(istype(C, /obj/item/pen)) - var/obj/item/pen/O = locate() in src - if(O) - to_chat(user, "There is already a pen in \the [src].") - else - user.drop_item() - C.forceMove(src) - to_chat(user, "You slide \the [C] into \the [src].") - else if(istype(C, /obj/item/nanomob_card)) - if(cartridge && istype(cartridge, /obj/item/cartridge/mob_hunt_game)) - cartridge.attackby(C, user, params) - -/obj/item/pda/attack(mob/living/C as mob, mob/living/user as mob) - if(istype(C, /mob/living/carbon) && scanmode) - scanmode.scan_mob(C, user) - -/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - if(proximity && scanmode) - scanmode.scan_atom(A, user) - -/obj/item/pda/proc/explode() //This needs tuning. - if(!detonate) - return - var/turf/T = get_turf(src.loc) - - if(ismob(loc)) - var/mob/M = loc - M.show_message("Your [src] explodes!", 1) - - if(T) - T.hotspot_expose(700,125) - - explosion(T, -1, -1, 2, 3) - qdel(src) - return - -/obj/item/pda/Destroy() - PDAs -= src - var/T = get_turf(loc) - if(id) - id.forceMove(T) - if(pai) - pai.forceMove(T) - current_app = null - scanmode = null - QDEL_LIST(programs) - QDEL_NULL(cartridge) - return ..() - -// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP -/obj/item/pda/emp_act(severity) - for(var/atom/A in src) - A.emp_act(severity) - -/obj/item/pda/proc/play_ringtone() - var/S - - if(ttone in ttone_sound) - S = ttone_sound[ttone] - else - S = 'sound/machines/twobeep.ogg' - playsound(loc, S, 50, 1) - for(var/mob/O in hearers(3, loc)) - O.show_message(text("[bicon(src)] *[ttone]*")) - -/obj/item/pda/proc/set_ringtone() - var/t = input("Please enter new ringtone", name, ttone) as text - if(in_range(src, usr) && loc == usr) - if(t) - if(hidden_uplink && hidden_uplink.check_trigger(usr, lowertext(t), lowertext(lock_code))) - to_chat(usr, "The PDA softly beeps.") - close(usr) - else - t = sanitize(copytext(t, 1, 20)) - ttone = t - return 1 - else - close(usr) - return 0 - -/obj/item/pda/process() - if(current_app) - current_app.program_process() - -/obj/item/pda/extinguish_light() - var/datum/data/pda/utility/flashlight/FL = find_program(/datum/data/pda/utility/flashlight) - if(FL && FL.fon) - FL.start() + +//The advanced pea-green monochrome lcd of tomorrow. + +GLOBAL_LIST_EMPTY(PDAs) + + +/obj/item/pda + name = "PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." + icon = 'icons/obj/pda.dmi' + icon_state = "pda" + item_state = "electronic" + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_ID | SLOT_BELT | SLOT_PDA + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + origin_tech = "programming=2" + + //Main variables + var/owner = null + var/default_cartridge = 0 // Access level defined by cartridge + var/obj/item/cartridge/cartridge = null //current cartridge + var/datum/data/pda/app/current_app = null + var/datum/data/pda/app/lastapp = null + var/ui_tick = 0 + + //Secondary variables + var/model_name = "Thinktronic 5230 Personal Data Assistant" + var/datum/data/pda/utility/scanmode/scanmode = null + + var/lock_code = "" // Lockcode to unlock uplink + var/honkamt = 0 //How many honks left when infected with honk.exe + var/mimeamt = 0 //How many silence left when infected with mime.exe + var/detonate = 1 // Can the PDA be blown up? + var/ttone = "beep" //The ringtone! + var/list/ttone_sound = list("beep" = 'sound/machines/twobeep.ogg', + "boom" = 'sound/effects/explosionfar.ogg', + "slip" = 'sound/misc/slip.ogg', + "honk" = 'sound/items/bikehorn.ogg', + "SKREE" = 'sound/voice/shriek1.ogg', + "holy" = 'sound/items/PDA/ambicha4-short.ogg', + "xeno" = 'sound/voice/hiss1.ogg') + + var/list/programs = list( + new/datum/data/pda/app/main_menu, + new/datum/data/pda/app/notekeeper, + new/datum/data/pda/app/messenger, + new/datum/data/pda/app/manifest, + new/datum/data/pda/app/atmos_scanner, + new/datum/data/pda/utility/scanmode/notes, + new/datum/data/pda/utility/flashlight) + var/list/shortcut_cache = list() + var/list/shortcut_cat_order = list() + var/list/notifying_programs = list() + + var/obj/item/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. + var/ownjob = null //related to above + var/ownrank = null // this one is rank, never alt title + + var/obj/item/paicard/pai = null // A slot for a personal AI device + var/retro_mode = 0 + + +/* + * The Actual PDA + */ +/obj/item/pda/Initialize(mapload) + . = ..() + GLOB.PDAs += src + GLOB.PDAs = sortAtom(GLOB.PDAs) + update_programs() + if(default_cartridge) + cartridge = new default_cartridge(src) + cartridge.update_programs(src) + new /obj/item/pen(src) + start_program(find_program(/datum/data/pda/app/main_menu)) + +/obj/item/pda/proc/can_use() + if(!ismob(loc)) + return 0 + + var/mob/M = loc + if(M.incapacitated()) + return 0 + if((src in M.contents) || ( istype(loc, /turf) && in_range(src, M) )) + return 1 + else + return 0 + +/obj/item/pda/GetAccess() + if(id) + return id.GetAccess() + else + return ..() + +/obj/item/pda/GetID() + return id + +/obj/item/pda/MouseDrop(obj/over_object as obj, src_location, over_location) + var/mob/M = usr + if((!istype(over_object, /obj/screen)) && can_use()) + return attack_self(M) + +/obj/item/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.inventory_state) + ui_tick++ + var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") + var/auto_update = 1 + if(!current_app) + return + + if(current_app.update == PDA_APP_NOUPDATE && current_app == lastapp) + auto_update = 0 + if(old_ui && (current_app == lastapp && ui_tick % 5 && current_app.update == PDA_APP_UPDATE_SLOW)) + return + + lastapp = current_app + + var/title = "Personal Data Assistant" + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "pda.tmpl", title, 630, 600, state = state) + ui.set_state_key("pda") + + // open the new ui window + ui.open() + + // auto update every Master Controller tick + ui.set_auto_update(auto_update) + +/obj/item/pda/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.inventory_state) + var/data[0] + + data["owner"] = owner // Who is your daddy... + data["ownjob"] = ownjob // ...and what does he do? + + // update list of shortcuts, only if they changed + if(!shortcut_cache.len) + shortcut_cache = list() + shortcut_cat_order = list() + var/prog_list = programs.Copy() + if(cartridge) + prog_list |= cartridge.programs + + for(var/A in prog_list) + var/datum/data/pda/P = A + + if(P.hidden) + continue + var/list/cat + if(P.category in shortcut_cache) + cat = shortcut_cache[P.category] + else + cat = list() + shortcut_cache[P.category] = cat + shortcut_cat_order += P.category + cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) + + // force the order of a few core categories + shortcut_cat_order = list("General") \ + + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ + + list("Scanners", "Utilities") + + data["idInserted"] = (id ? 1 : 0) + data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") + + data["useRetro"] = retro_mode + + data["cartridge_name"] = cartridge ? cartridge.name : "" + data["stationTime"] = station_time_timestamp() + + data["app"] = list() + current_app.update_ui(user, data) + data["app"] |= list( + "name" = current_app.title, + "icon" = current_app.icon, + "template" = current_app.template, + "has_back" = current_app.has_back) + + return data + +/obj/item/pda/attack_self(mob/user as mob) + user.set_machine(src) + if(active_uplink_check(user)) + return + ui_interact(user) //NanoUI requires this proc + +/obj/item/pda/proc/start_program(datum/data/pda/P) + if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) + return P.start() + return 0 + +/obj/item/pda/proc/find_program(type) + var/datum/data/pda/A = locate(type) in programs + if(A) + return A + if(cartridge) + A = locate(type) in cartridge.programs + if(A) + return A + return null + +// force the cache to rebuild on update_ui +/obj/item/pda/proc/update_shortcuts() + shortcut_cache.Cut() + +/obj/item/pda/proc/update_programs() + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = src + +/obj/item/pda/Topic(href, href_list) + . = ..() + if(.) + return + + var/mob/user = usr + var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") + var/mob/living/U = usr + if(usr.stat == DEAD) + return 0 + if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. + U.unset_machine() + if(ui) + ui.close() + return 0 + + add_fingerprint(U) + U.set_machine(src) + + if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) + cartridge.radio.Topic(href, href_list) + return 1 + + . = 1 + + switch(href_list["choice"]) + if("Home")//Go home, largely replaces the old Return + var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) + if(A) + start_program(A) + if("StartProgram") + if(href_list["program"]) + var/datum/data/pda/app/A = locate(href_list["program"]) + if(A) + start_program(A) + if("Eject")//Ejects the cart, only done from hub. + if(!isnull(cartridge)) + var/turf/T = loc + if(ismob(T)) + T = T.loc + var/obj/item/cartridge/C = cartridge + C.forceMove(T) + if(scanmode in C.programs) + scanmode = null + if(current_app in C.programs) + start_program(find_program(/datum/data/pda/app/main_menu)) + if(C.radio) + C.radio.hostpda = null + for(var/datum/data/pda/P in notifying_programs) + if(P in C.programs) + P.unnotify() + cartridge = null + update_shortcuts() + if("Authenticate")//Checks for ID + id_check(usr, 1) + if("Retro") + retro_mode = !retro_mode + if("Ringtone") + return set_ringtone() + else + if(current_app) + . = current_app.Topic(href, href_list) + +//EXTRA FUNCTIONS=================================== + if((honkamt > 0) && (prob(60)))//For clown virus. + honkamt-- + playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) + + return // return 1 tells it to refresh the UI in NanoUI + +/obj/item/pda/proc/close(mob/user) + var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") + ui.close() + +/obj/item/pda/verb/verb_reset_pda() + set category = "Object" + set name = "Reset PDA" + set src in usr + + if(issilicon(usr)) + return + + if(can_use(usr)) + start_program(find_program(/datum/data/pda/app/main_menu)) + notifying_programs.Cut() + overlays -= image('icons/obj/pda.dmi', "pda-r") + to_chat(usr, "You press the reset button on \the [src].") + else + to_chat(usr, "You cannot do this while restrained.") + +/obj/item/pda/AltClick(mob/user) + ..() + if(issilicon(user)) + return + + if(can_use(user)) + if(id) + remove_id(user) + else + to_chat(user, "This PDA does not have an ID in it!") + +/obj/item/pda/CtrlClick(mob/user) + ..() + if(issilicon(user)) + return + + if(can_use(user)) + remove_pen(user) + +/obj/item/pda/proc/remove_id(mob/user) + if(id) + if(ismob(loc)) + var/mob/M = loc + M.put_in_hands(id) + to_chat(user, "You remove the ID from the [name].") + else + id.forceMove(get_turf(src)) + overlays -= image('icons/goonstation/objects/pda_overlay.dmi', id.icon_state) + id = null + +/obj/item/pda/verb/verb_remove_id() + set category = "Object" + set name = "Remove id" + set src in usr + + if(issilicon(usr)) + return + + if( can_use(usr) ) + if(id) + remove_id(usr) + else + to_chat(usr, "This PDA does not have an ID in it.") + else + to_chat(usr, "You cannot do this while restrained.") + +/obj/item/pda/verb/verb_remove_pen() + set category = "Object" + set name = "Remove pen" + set src in usr + remove_pen(usr) + +/obj/item/pda/proc/remove_pen(mob/user) + + if(issilicon(user)) + return + + if( can_use(user) ) + var/obj/item/pen/O = locate() in src + if(O) + to_chat(user, "You remove the [O] from [src].") + if(istype(loc, /mob)) + var/mob/M = loc + if(M.get_active_hand() == null) + M.put_in_hands(O) + return + O.forceMove(get_turf(src)) + else + to_chat(user, "This PDA does not have a pen in it.") + else + to_chat(user, "You cannot do this while restrained.") + +/obj/item/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. + if(choice == 1) + if(id) + remove_id(user) + else + var/obj/item/I = user.get_active_hand() + if(istype(I, /obj/item/card/id)) + user.drop_item() + I.forceMove(src) + id = I + else + var/obj/item/card/I = user.get_active_hand() + if(istype(I, /obj/item/card/id) && I:registered_name) + var/obj/old_id = id + user.drop_item() + I.forceMove(src) + id = I + user.put_in_hands(old_id) + return + +/obj/item/pda/attackby(obj/item/C as obj, mob/user as mob, params) + ..() + if(istype(C, /obj/item/cartridge) && !cartridge) + cartridge = C + user.drop_item() + cartridge.forceMove(src) + cartridge.update_programs(src) + update_shortcuts() + to_chat(user, "You insert [cartridge] into [src].") + if(cartridge.radio) + cartridge.radio.hostpda = src + + else if(istype(C, /obj/item/card/id)) + var/obj/item/card/id/idcard = C + if(!idcard.registered_name) + to_chat(user, "\The [src] rejects the ID.") + return + if(!owner) + owner = idcard.registered_name + ownjob = idcard.assignment + ownrank = idcard.rank + name = "PDA-[owner] ([ownjob])" + to_chat(user, "Card scanned.") + else + //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. + if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) + if( can_use(user) )//If they can still act. + id_check(user, 2) + to_chat(user, "You put the ID into \the [src]'s slot.
    You can remove it with ALT click.
    ") + overlays += image('icons/goonstation/objects/pda_overlay.dmi', C.icon_state) + + else if(istype(C, /obj/item/paicard) && !src.pai) + user.drop_item() + C.forceMove(src) + pai = C + to_chat(user, "You slot \the [C] into [src].") + else if(istype(C, /obj/item/pen)) + var/obj/item/pen/O = locate() in src + if(O) + to_chat(user, "There is already a pen in \the [src].") + else + user.drop_item() + C.forceMove(src) + to_chat(user, "You slide \the [C] into \the [src].") + else if(istype(C, /obj/item/nanomob_card)) + if(cartridge && istype(cartridge, /obj/item/cartridge/mob_hunt_game)) + cartridge.attackby(C, user, params) + +/obj/item/pda/attack(mob/living/C as mob, mob/living/user as mob) + if(istype(C, /mob/living/carbon) && scanmode) + scanmode.scan_mob(C, user) + +/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + if(proximity && scanmode) + scanmode.scan_atom(A, user) + +/obj/item/pda/proc/explode() //This needs tuning. + if(!detonate) + return + var/turf/T = get_turf(src.loc) + + if(ismob(loc)) + var/mob/M = loc + M.show_message("Your [src] explodes!", 1) + + if(T) + T.hotspot_expose(700,125) + + explosion(T, -1, -1, 2, 3) + qdel(src) + return + +/obj/item/pda/Destroy() + GLOB.PDAs -= src + var/T = get_turf(loc) + if(id) + id.forceMove(T) + if(pai) + pai.forceMove(T) + current_app = null + scanmode = null + QDEL_LIST(programs) + QDEL_NULL(cartridge) + return ..() + +// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP +/obj/item/pda/emp_act(severity) + for(var/atom/A in src) + A.emp_act(severity) + +/obj/item/pda/proc/play_ringtone() + var/S + + if(ttone in ttone_sound) + S = ttone_sound[ttone] + else + S = 'sound/machines/twobeep.ogg' + playsound(loc, S, 50, 1) + for(var/mob/O in hearers(3, loc)) + O.show_message(text("[bicon(src)] *[ttone]*")) + +/obj/item/pda/proc/set_ringtone() + var/t = input("Please enter new ringtone", name, ttone) as text + if(in_range(src, usr) && loc == usr) + if(t) + if(hidden_uplink && hidden_uplink.check_trigger(usr, lowertext(t), lowertext(lock_code))) + to_chat(usr, "The PDA softly beeps.") + close(usr) + else + t = sanitize(copytext(t, 1, 20)) + ttone = t + return 1 + else + close(usr) + return 0 + +/obj/item/pda/process() + if(current_app) + current_app.program_process() + +/obj/item/pda/extinguish_light() + var/datum/data/pda/utility/flashlight/FL = find_program(/datum/data/pda/utility/flashlight) + if(FL && FL.fon) + FL.start() diff --git a/code/modules/pda/ai.dm b/code/modules/pda/ai.dm index 5860ff660f3d..ac4e6c9fd6b7 100644 --- a/code/modules/pda/ai.dm +++ b/code/modules/pda/ai.dm @@ -101,4 +101,4 @@ if(!pAI.software["messenger"]) to_chat(usr, "You have not purchased the digital messenger!") return 0 - return ..() && !pAI.silence_time \ No newline at end of file + return ..() && !pAI.silence_time diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm index 7b7ec95b0e3e..6339f5375d71 100644 --- a/code/modules/pda/cart.dm +++ b/code/modules/pda/cart.dm @@ -1,325 +1,325 @@ -/obj/item/cartridge - name = "generic cartridge" - desc = "A data cartridge for portable microcomputers." - icon = 'icons/obj/pda.dmi' - icon_state = "cart" - item_state = "electronic" - w_class = WEIGHT_CLASS_TINY - - var/obj/item/integrated_radio/radio = null - - var/charges = 0 - - var/list/stored_data = list() - var/list/programs = list() - var/list/messenger_plugins = list() - -/obj/item/cartridge/Destroy() - QDEL_NULL(radio) - QDEL_LIST(programs) - QDEL_LIST(messenger_plugins) - return ..() - -/obj/item/cartridge/proc/update_programs(obj/item/pda/pda) - for(var/A in programs) - var/datum/data/pda/P = A - P.pda = pda - for(var/A in messenger_plugins) - var/datum/data/pda/messenger_plugin/P = A - P.pda = pda - -/obj/item/cartridge/engineering - name = "Power-ON Cartridge" - icon_state = "cart-e" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen) - -/obj/item/cartridge/atmos - name = "BreatheDeep Cartridge" - icon_state = "cart-a" - programs = list(new/datum/data/pda/utility/scanmode/gas) - -/obj/item/cartridge/medical - name = "Med-U Cartridge" - icon_state = "cart-m" - programs = list( - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical) - -/obj/item/cartridge/chemistry - name = "ChemWhiz Cartridge" - icon_state = "cart-chem" - programs = list(new/datum/data/pda/utility/scanmode/reagent) - -/obj/item/cartridge/security - name = "R.O.B.U.S.T. Cartridge" - icon_state = "cart-s" - programs = list( - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control) - -/obj/item/cartridge/security/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/detective - name = "D.E.T.E.C.T. Cartridge" - icon_state = "cart-s" - programs = list( - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/app/crew_records/security) - - -/obj/item/cartridge/janitor - name = "CustodiPRO Cartridge" - desc = "The ultimate in clean-room design." - icon_state = "cart-j" - programs = list(new/datum/data/pda/app/janitor) - -/obj/item/cartridge/lawyer - name = "P.R.O.V.E. Cartridge" - icon_state = "cart-s" - programs = list(new/datum/data/pda/app/crew_records/security) - -/obj/item/cartridge/clown - name = "Honkworks 5.0" - icon_state = "cart-clown" - charges = 5 - programs = list(new/datum/data/pda/utility/honk) - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown) - -/obj/item/cartridge/mime - name = "Gestur-O 1000" - icon_state = "cart-mi" - charges = 5 - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime) - -/* -/obj/item/cartridge/botanist - name = "Green Thumb v4.20" - icon_state = "cart-b" - access_flora = 1 -*/ - -/obj/item/cartridge/signal - name = "generic signaler cartridge" - desc = "A data cartridge with an integrated radio signaler module." - programs = list(new/datum/data/pda/app/signaller) - -/obj/item/cartridge/signal/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/signal(src) - -/obj/item/cartridge/signal/toxins - name = "Signal Ace 2" - desc = "Complete with integrated radio signaler!" - icon_state = "cart-tox" - programs = list( - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/signaller) - -/obj/item/cartridge/quartermaster - name = "Space Parts & Space Vendors Cartridge" - desc = "Perfect for the Quartermaster on the go!" - icon_state = "cart-q" - programs = list( - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control) - -/obj/item/cartridge/quartermaster/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/mule(src) - -/obj/item/cartridge/head - name = "Easy-Record DELUXE" - icon_state = "cart-h" - programs = list(new/datum/data/pda/app/status_display) - -/obj/item/cartridge/hop - name = "HumanResources9001" - icon_state = "cart-h" - programs = list( - new/datum/data/pda/app/crew_records/security, - - new/datum/data/pda/app/janitor, - - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/hop/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/mule(src) - -/obj/item/cartridge/hos - name = "R.O.B.U.S.T. DELUXE" - icon_state = "cart-hos" - programs = list( - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/hos/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/ce - name = "Power-On DELUXE" - icon_state = "cart-ce" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen, - - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/cmo - name = "Med-U DELUXE" - icon_state = "cart-cmo" - programs = list( - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/rd - name = "Signal Ace DELUXE" - icon_state = "cart-rd" - programs = list( - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/signaller, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/rd/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/signal(src) - -/obj/item/cartridge/captain - name = "Value-PAK Cartridge" - desc = "Now with 200% more value!" - icon_state = "cart-c" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen, - - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control, - - new/datum/data/pda/app/janitor, - - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/captain/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/supervisor - name = "Easy-Record DELUXE" - icon_state = "cart-h" - programs = list( - new/datum/data/pda/app/crew_records/security, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/centcom - name = "Value-PAK Cartridge" - desc = "Now with 200% more value!" - icon_state = "cart-c" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen, - - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control, - - new/datum/data/pda/app/janitor, - - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/centcom/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/syndicate - name = "Detomatix Cartridge" - icon_state = "cart" - var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. - charges = 4 - programs = list(new/datum/data/pda/utility/toggle_door) - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate) - -/obj/item/cartridge/syndicate/Initialize(mapload) - . = ..() - var/datum/data/pda/utility/toggle_door/D = programs[1] - if(istype(D)) - D.remote_door_id = initial_remote_door_id - -/obj/item/cartridge/frame - name = "F.R.A.M.E. cartridge" - icon_state = "cart" - charges = 5 - var/telecrystals = 0 - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/frame) - -/obj/item/cartridge/mob_hunt_game - name = "Nano-Mob Hunter GO! Cartridge" - desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!" - icon_state = "cart-eye" - programs = list(new/datum/data/pda/app/mob_hunter_game) - var/emagged = 0 - -/obj/item/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/nanomob_card)) - var/obj/item/nanomob_card/card = O - var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] - if(my_game.register_capture(card.mob_data)) - to_chat(user, "Transfer successful!") - qdel(card) - else - to_chat(user, "Transfer failed. Could not read mob data from card.") - else - ..() - -/obj/item/cartridge/mob_hunt_game/emag_act(mob/user) - if(!emagged) - emagged = 1 - var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] - my_game.hacked = 1 - to_chat(user, "TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.") - to_chat(user, "You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.") - description_antag = "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap." \ No newline at end of file +/obj/item/cartridge + name = "generic cartridge" + desc = "A data cartridge for portable microcomputers." + icon = 'icons/obj/pda.dmi' + icon_state = "cart" + item_state = "electronic" + w_class = WEIGHT_CLASS_TINY + + var/obj/item/integrated_radio/radio = null + + var/charges = 0 + + var/list/stored_data = list() + var/list/programs = list() + var/list/messenger_plugins = list() + +/obj/item/cartridge/Destroy() + QDEL_NULL(radio) + QDEL_LIST(programs) + QDEL_LIST(messenger_plugins) + return ..() + +/obj/item/cartridge/proc/update_programs(obj/item/pda/pda) + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = pda + for(var/A in messenger_plugins) + var/datum/data/pda/messenger_plugin/P = A + P.pda = pda + +/obj/item/cartridge/engineering + name = "Power-ON Cartridge" + icon_state = "cart-e" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen) + +/obj/item/cartridge/atmos + name = "BreatheDeep Cartridge" + icon_state = "cart-a" + programs = list(new/datum/data/pda/utility/scanmode/gas) + +/obj/item/cartridge/medical + name = "Med-U Cartridge" + icon_state = "cart-m" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical) + +/obj/item/cartridge/chemistry + name = "ChemWhiz Cartridge" + icon_state = "cart-chem" + programs = list(new/datum/data/pda/utility/scanmode/reagent) + +/obj/item/cartridge/security + name = "R.O.B.U.S.T. Cartridge" + icon_state = "cart-s" + programs = list( + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control) + +/obj/item/cartridge/security/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/detective + name = "D.E.T.E.C.T. Cartridge" + icon_state = "cart-s" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/app/crew_records/security) + + +/obj/item/cartridge/janitor + name = "CustodiPRO Cartridge" + desc = "The ultimate in clean-room design." + icon_state = "cart-j" + programs = list(new/datum/data/pda/app/janitor) + +/obj/item/cartridge/lawyer + name = "P.R.O.V.E. Cartridge" + icon_state = "cart-s" + programs = list(new/datum/data/pda/app/crew_records/security) + +/obj/item/cartridge/clown + name = "Honkworks 5.0" + icon_state = "cart-clown" + charges = 5 + programs = list(new/datum/data/pda/utility/honk) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown) + +/obj/item/cartridge/mime + name = "Gestur-O 1000" + icon_state = "cart-mi" + charges = 5 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime) + +/* +/obj/item/cartridge/botanist + name = "Green Thumb v4.20" + icon_state = "cart-b" + access_flora = 1 +*/ + +/obj/item/cartridge/signal + name = "generic signaler cartridge" + desc = "A data cartridge with an integrated radio signaler module." + programs = list(new/datum/data/pda/app/signaller) + +/obj/item/cartridge/signal/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/signal(src) + +/obj/item/cartridge/signal/toxins + name = "Signal Ace 2" + desc = "Complete with integrated radio signaler!" + icon_state = "cart-tox" + programs = list( + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/signaller) + +/obj/item/cartridge/quartermaster + name = "Space Parts & Space Vendors Cartridge" + desc = "Perfect for the Quartermaster on the go!" + icon_state = "cart-q" + programs = list( + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control) + +/obj/item/cartridge/quartermaster/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/mule(src) + +/obj/item/cartridge/head + name = "Easy-Record DELUXE" + icon_state = "cart-h" + programs = list(new/datum/data/pda/app/status_display) + +/obj/item/cartridge/hop + name = "HumanResources9001" + icon_state = "cart-h" + programs = list( + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/hop/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/mule(src) + +/obj/item/cartridge/hos + name = "R.O.B.U.S.T. DELUXE" + icon_state = "cart-hos" + programs = list( + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/hos/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/ce + name = "Power-On DELUXE" + icon_state = "cart-ce" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/cmo + name = "Med-U DELUXE" + icon_state = "cart-cmo" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/rd + name = "Signal Ace DELUXE" + icon_state = "cart-rd" + programs = list( + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/signaller, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/rd/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/signal(src) + +/obj/item/cartridge/captain + name = "Value-PAK Cartridge" + desc = "Now with 200% more value!" + icon_state = "cart-c" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/captain/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/supervisor + name = "Easy-Record DELUXE" + icon_state = "cart-h" + programs = list( + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/centcom + name = "Value-PAK Cartridge" + desc = "Now with 200% more value!" + icon_state = "cart-c" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/centcom/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/syndicate + name = "Detomatix Cartridge" + icon_state = "cart" + var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. + charges = 4 + programs = list(new/datum/data/pda/utility/toggle_door) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate) + +/obj/item/cartridge/syndicate/Initialize(mapload) + . = ..() + var/datum/data/pda/utility/toggle_door/D = programs[1] + if(istype(D)) + D.remote_door_id = initial_remote_door_id + +/obj/item/cartridge/frame + name = "F.R.A.M.E. cartridge" + icon_state = "cart" + charges = 5 + var/telecrystals = 0 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/frame) + +/obj/item/cartridge/mob_hunt_game + name = "Nano-Mob Hunter GO! Cartridge" + desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!" + icon_state = "cart-eye" + programs = list(new/datum/data/pda/app/mob_hunter_game) + var/emagged = 0 + +/obj/item/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/nanomob_card)) + var/obj/item/nanomob_card/card = O + var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] + if(my_game.register_capture(card.mob_data)) + to_chat(user, "Transfer successful!") + qdel(card) + else + to_chat(user, "Transfer failed. Could not read mob data from card.") + else + ..() + +/obj/item/cartridge/mob_hunt_game/emag_act(mob/user) + if(!emagged) + emagged = 1 + var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] + my_game.hacked = 1 + to_chat(user, "TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.") + to_chat(user, "You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.") + description_antag = "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap." diff --git a/code/modules/pda/cart_apps.dm b/code/modules/pda/cart_apps.dm index 4ca5b4af4272..6c03241a0967 100644 --- a/code/modules/pda/cart_apps.dm +++ b/code/modules/pda/cart_apps.dm @@ -103,12 +103,12 @@ "poweravail" = powmonitor.powernet.avail, "powerload" = num2text(powmonitor.powernet.viewload, 10), "powerdemand" = powmonitor.powernet.load, - "apcs" = apc_repository.apc_data(powmonitor.powernet)) + "apcs" = GLOB.apc_repository.apc_data(powmonitor.powernet)) has_back = 1 else data["records"] = list( "powerconnected" = 0, - "powermonitors" = powermonitor_repository.powermonitor_data()) + "powermonitors" = GLOB.powermonitor_repository.powermonitor_data()) has_back = 0 /datum/data/pda/app/power/Topic(href, list/href_list) @@ -127,12 +127,12 @@ /datum/data/pda/app/crew_records/update_ui(mob/user as mob, list/data) var/list/records[0] - if(general_records && (general_records in data_core.general)) + if(general_records && (general_records in GLOB.data_core.general)) data["records"] = records records["general"] = general_records.fields return records else - for(var/A in sortRecord(data_core.general)) + for(var/A in sortRecord(GLOB.data_core.general)) var/datum/data/record/R = A if(R) records += list(list(Name = R.fields["name"], "ref" = "\ref[R]")) @@ -143,7 +143,7 @@ switch(href_list["choice"]) if("Records") var/datum/data/record/R = locate(href_list["target"]) - if(R && (R in data_core.general)) + if(R && (R in GLOB.data_core.general)) load_records(R) if("Back") general_records = null @@ -166,14 +166,14 @@ if(!records) return - if(medical_records && (medical_records in data_core.medical)) + if(medical_records && (medical_records in GLOB.data_core.medical)) records["medical"] = medical_records.fields return records /datum/data/pda/app/crew_records/medical/load_records(datum/data/record/R) ..(R) - for(var/A in data_core.medical) + for(var/A in GLOB.data_core.medical) var/datum/data/record/E = A if(E && (E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) medical_records = E @@ -192,14 +192,14 @@ if(!records) return - if(security_records && (security_records in data_core.security)) + if(security_records && (security_records in GLOB.data_core.security)) records["security"] = security_records.fields return records /datum/data/pda/app/crew_records/security/load_records(datum/data/record/R) ..(R) - for(var/A in data_core.security) + for(var/A in GLOB.data_core.security) var/datum/data/record/E = A if(E && (E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) security_records = E diff --git a/code/modules/pda/core_apps.dm b/code/modules/pda/core_apps.dm index 2b5ce32355d5..41f9f994420c 100644 --- a/code/modules/pda/core_apps.dm +++ b/code/modules/pda/core_apps.dm @@ -71,8 +71,8 @@ update = PDA_APP_UPDATE_SLOW /datum/data/pda/app/manifest/update_ui(mob/user as mob, list/data) - data_core.get_manifest_json() - data["manifest"] = PDA_Manifest + GLOB.data_core.get_manifest_json() + data["manifest"] = GLOB.PDA_Manifest /datum/data/pda/app/manifest/Topic(href, list/href_list) @@ -108,4 +108,4 @@ "reading" = 1 ) if(isnull(data["aircontents"])) - data["aircontents"] = list("reading" = 0) \ No newline at end of file + data["aircontents"] = list("reading" = 0) diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm index ab97c956d85a..066db440cc88 100644 --- a/code/modules/pda/messenger.dm +++ b/code/modules/pda/messenger.dm @@ -39,7 +39,7 @@ else var/convopdas[0] var/pdas[0] - for(var/A in PDAs) + for(var/A in GLOB.PDAs) var/obj/item/pda/P = A var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) @@ -143,8 +143,8 @@ // check if telecomms I/O route 1459 is stable //var/telecomms_intact = telecomms_process(P.owner, owner, t) var/obj/machinery/message_server/useMS = null - if(message_servers) - for(var/A in message_servers) + if(GLOB.message_servers) + for(var/A in GLOB.message_servers) var/obj/machinery/message_server/MS = A //PDAs are now dependent on the Message Server. if(MS.active) @@ -201,7 +201,7 @@ to_chat(usr, "Turn on your receiver in order to send messages.") return - for(var/A in PDAs) + for(var/A in GLOB.PDAs) var/obj/item/pda/P = A var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) diff --git a/code/modules/pda/messenger_plugins.dm b/code/modules/pda/messenger_plugins.dm index 2666b82c5df7..c69d8890164c 100644 --- a/code/modules/pda/messenger_plugins.dm +++ b/code/modules/pda/messenger_plugins.dm @@ -88,4 +88,4 @@ var/obj/item/cartridge/frame/parent_cart = pda.cartridge P.hidden_uplink.uses = parent_cart.telecrystals parent_cart.telecrystals = 0 - P.hidden_uplink.active = TRUE \ No newline at end of file + P.hidden_uplink.active = TRUE diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm index 2bb28ba0264e..c48ebdd4e9e2 100644 --- a/code/modules/pda/mob_hunt_game_app.dm +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -173,4 +173,4 @@ if("Transfer") print_card() if("Set_Trap") - set_trap() \ No newline at end of file + set_trap() diff --git a/code/modules/pda/radio.dm b/code/modules/pda/radio.dm index a49305598e6e..f214e7052689 100644 --- a/code/modules/pda/radio.dm +++ b/code/modules/pda/radio.dm @@ -1,208 +1,208 @@ -//TODO convert this crap over to proper radios or find a way to utilize regualr radios for this object, this thing needs to go. - -/obj/item/integrated_radio - name = "\improper PDA radio module" - desc = "An electronic radio system of Nanotrasen origin." - icon = 'icons/obj/module.dmi' - icon_state = "power_mod" - var/obj/item/pda/hostpda = null - var/list/botlist = null // list of bots - var/mob/living/simple_animal/bot/active // the active bot; if null, show bot list - var/list/botstatus // the status signal sent by the bot - var/bot_type //The type of bot it is. - var/bot_filter //Determines which radio filter to use. - - var/control_freq = 1447 - - var/on = 0 //Are we currently active?? - var/menu_message = "" - -/obj/item/integrated_radio/Initialize(mapload) - . = ..() - if(istype(loc.loc, /obj/item/pda)) - hostpda = loc.loc - if(bot_filter) - add_to_radio(bot_filter) - -/obj/item/integrated_radio/Destroy() - if(SSradio) - SSradio.remove_object(src, control_freq) - hostpda = null - return ..() - -/obj/item/integrated_radio/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3,var/key4, var/value4, s_filter) - -// to_chat(world, "Post: [freq]: [key]=[value], [key2]=[value2]") - var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) - - if(!frequency) - return - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - signal.data[key] = value - if(key2) - signal.data[key2] = value2 - if(key3) - signal.data[key3] = value3 - if(key4) - signal.data[key4] = value4 - - frequency.post_signal(src, signal, filter = s_filter) - -/obj/item/integrated_radio/receive_signal(datum/signal/signal) - if(bot_type && istype(signal.source, /obj/machinery/bot_core) && signal.data["type"] == bot_type) - if(!botlist) - botlist = new() - - var/obj/machinery/bot_core/core = signal.source - - if(istype(core) && !(core.owner in botlist)) - botlist += core.owner - - if(active == core.owner) - var/list/b = signal.data - botstatus = b.Copy() - -/obj/item/integrated_radio/Topic(href, href_list) - ..() - switch(href_list["op"]) - if("control") - active = locate(href_list["bot"]) - spawn(0) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) - - if("scanbots") // find all bots - botlist = null - spawn(0) - post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) - - if("botlist") - active = null - - if("stop", "go", "home") - spawn(0) - post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) - - if("summon") - spawn(0) - post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) - -/obj/item/integrated_radio/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. - if(SSradio) - SSradio.add_object(src, control_freq, filter = bot_filter) - -/obj/item/integrated_radio/honkbot - bot_filter = RADIO_HONKBOT - bot_type = HONK_BOT - -/obj/item/integrated_radio/beepsky - bot_filter = RADIO_SECBOT - bot_type = SEC_BOT - -/obj/item/integrated_radio/medbot - bot_filter = RADIO_MEDBOT - bot_type = MED_BOT - -/obj/item/integrated_radio/floorbot - bot_filter = RADIO_FLOORBOT - bot_type = FLOOR_BOT - -/obj/item/integrated_radio/cleanbot - bot_filter = RADIO_CLEANBOT - bot_type = CLEAN_BOT - -/obj/item/integrated_radio/mule - bot_filter = RADIO_MULEBOT - bot_type = MULE_BOT - -/obj/item/integrated_radio/mule/Topic(href, href_list) - ..() - switch(href_list["op"]) - if("start") - spawn(0) - post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT) - - if("unload") - spawn(0) - post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT) - - if("setdest") - if(GLOB.deliverybeacons) - var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in GLOB.deliverybeacontags - if(dest) - spawn(0) - post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT) - - if("retoff") - spawn(0) - post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT) - - if("reton") - spawn(0) - post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT) - - if("pickoff") - spawn(0) - post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT) - - if("pickon") - spawn(0) - post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT) - - spawn(10) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT) - - - -/* - * Radio Cartridge, essentially a signaler. - */ - - -/obj/item/integrated_radio/signal - var/frequency = RSD_FREQ - var/code = 30.0 - var/last_transmission - var/datum/radio_frequency/radio_connection - -/obj/item/integrated_radio/signal/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/item/integrated_radio/signal/Initialize(mapload) - . = ..() - if(!SSradio) - return - if(src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ) - src.frequency = sanitize_frequency(src.frequency) - - set_frequency(frequency) - -/obj/item/integrated_radio/signal/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency) - -/obj/item/integrated_radio/signal/proc/send_signal(message="ACTIVATE") - - if(last_transmission && world.time < (last_transmission + 5)) - return - last_transmission = world.time - - var/time = time2text(world.realtime,"hh:mm:ss") - var/turf/T = get_turf(src) - lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = message - - spawn(0) - radio_connection.post_signal(src, signal) \ No newline at end of file +//TODO convert this crap over to proper radios or find a way to utilize regualr radios for this object, this thing needs to go. + +/obj/item/integrated_radio + name = "\improper PDA radio module" + desc = "An electronic radio system of Nanotrasen origin." + icon = 'icons/obj/module.dmi' + icon_state = "power_mod" + var/obj/item/pda/hostpda = null + var/list/botlist = null // list of bots + var/mob/living/simple_animal/bot/active // the active bot; if null, show bot list + var/list/botstatus // the status signal sent by the bot + var/bot_type //The type of bot it is. + var/bot_filter //Determines which radio filter to use. + + var/control_freq = 1447 + + var/on = 0 //Are we currently active?? + var/menu_message = "" + +/obj/item/integrated_radio/Initialize(mapload) + . = ..() + if(istype(loc.loc, /obj/item/pda)) + hostpda = loc.loc + if(bot_filter) + add_to_radio(bot_filter) + +/obj/item/integrated_radio/Destroy() + if(SSradio) + SSradio.remove_object(src, control_freq) + hostpda = null + return ..() + +/obj/item/integrated_radio/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3,var/key4, var/value4, s_filter) + +// to_chat(world, "Post: [freq]: [key]=[value], [key2]=[value2]") + var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) + + if(!frequency) + return + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = 1 + signal.data[key] = value + if(key2) + signal.data[key2] = value2 + if(key3) + signal.data[key3] = value3 + if(key4) + signal.data[key4] = value4 + + frequency.post_signal(src, signal, filter = s_filter) + +/obj/item/integrated_radio/receive_signal(datum/signal/signal) + if(bot_type && istype(signal.source, /obj/machinery/bot_core) && signal.data["type"] == bot_type) + if(!botlist) + botlist = new() + + var/obj/machinery/bot_core/core = signal.source + + if(istype(core) && !(core.owner in botlist)) + botlist += core.owner + + if(active == core.owner) + var/list/b = signal.data + botstatus = b.Copy() + +/obj/item/integrated_radio/Topic(href, href_list) + ..() + switch(href_list["op"]) + if("control") + active = locate(href_list["bot"]) + spawn(0) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + + if("scanbots") // find all bots + botlist = null + spawn(0) + post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) + + if("botlist") + active = null + + if("stop", "go", "home") + spawn(0) + post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + + if("summon") + spawn(0) + post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + +/obj/item/integrated_radio/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. + if(SSradio) + SSradio.add_object(src, control_freq, filter = bot_filter) + +/obj/item/integrated_radio/honkbot + bot_filter = RADIO_HONKBOT + bot_type = HONK_BOT + +/obj/item/integrated_radio/beepsky + bot_filter = RADIO_SECBOT + bot_type = SEC_BOT + +/obj/item/integrated_radio/medbot + bot_filter = RADIO_MEDBOT + bot_type = MED_BOT + +/obj/item/integrated_radio/floorbot + bot_filter = RADIO_FLOORBOT + bot_type = FLOOR_BOT + +/obj/item/integrated_radio/cleanbot + bot_filter = RADIO_CLEANBOT + bot_type = CLEAN_BOT + +/obj/item/integrated_radio/mule + bot_filter = RADIO_MULEBOT + bot_type = MULE_BOT + +/obj/item/integrated_radio/mule/Topic(href, href_list) + ..() + switch(href_list["op"]) + if("start") + spawn(0) + post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT) + + if("unload") + spawn(0) + post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT) + + if("setdest") + if(GLOB.deliverybeacons) + var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in GLOB.deliverybeacontags + if(dest) + spawn(0) + post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT) + + if("retoff") + spawn(0) + post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT) + + if("reton") + spawn(0) + post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT) + + if("pickoff") + spawn(0) + post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT) + + if("pickon") + spawn(0) + post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT) + + spawn(10) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT) + + + +/* + * Radio Cartridge, essentially a signaler. + */ + + +/obj/item/integrated_radio/signal + var/frequency = RSD_FREQ + var/code = 30.0 + var/last_transmission + var/datum/radio_frequency/radio_connection + +/obj/item/integrated_radio/signal/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/item/integrated_radio/signal/Initialize(mapload) + . = ..() + if(!SSradio) + return + if(src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ) + src.frequency = sanitize_frequency(src.frequency) + + set_frequency(frequency) + +/obj/item/integrated_radio/signal/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency) + +/obj/item/integrated_radio/signal/proc/send_signal(message="ACTIVATE") + + if(last_transmission && world.time < (last_transmission + 5)) + return + last_transmission = world.time + + var/time = time2text(world.realtime,"hh:mm:ss") + var/turf/T = get_turf(src) + GLOB.lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = message + + spawn(0) + radio_connection.post_signal(src, signal) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 94978eec2657..ca2f1e13ff7d 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1,1384 +1,1384 @@ -#define APC_WIRE_IDSCAN 1 -#define APC_WIRE_MAIN_POWER1 2 -#define APC_WIRE_MAIN_POWER2 3 -#define APC_WIRE_AI_CONTROL 4 - -//update_state -#define UPSTATE_CELL_IN 1 -#define UPSTATE_OPENED1 2 -#define UPSTATE_OPENED2 4 -#define UPSTATE_MAINT 8 -#define UPSTATE_BROKE 16 -#define UPSTATE_BLUESCREEN 32 -#define UPSTATE_WIREEXP 64 -#define UPSTATE_ALLGOOD 128 - -//update_overlay -#define APC_UPOVERLAY_CHARGEING0 1 -#define APC_UPOVERLAY_CHARGEING1 2 -#define APC_UPOVERLAY_CHARGEING2 4 -#define APC_UPOVERLAY_EQUIPMENT0 8 -#define APC_UPOVERLAY_EQUIPMENT1 16 -#define APC_UPOVERLAY_EQUIPMENT2 32 -#define APC_UPOVERLAY_LIGHTING0 64 -#define APC_UPOVERLAY_LIGHTING1 128 -#define APC_UPOVERLAY_LIGHTING2 256 -#define APC_UPOVERLAY_ENVIRON0 512 -#define APC_UPOVERLAY_ENVIRON1 1024 -#define APC_UPOVERLAY_ENVIRON2 2048 -#define APC_UPOVERLAY_LOCKED 4096 - -#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds - -// APC malf status -#define APC_MALF_NOT_HACKED 1 -#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core. -#define APC_MALF_SHUNTED_HERE 3 // User is shunted in this APC -#define APC_MALF_SHUNTED_OTHER 4 // User is shunted in another APC - -// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) -// one per area, needs wire conection to power network through a terminal - -// controls power to devices in that area -// may be opened to change power cell -// three different channels (lighting/equipment/environ) - may each be set to on, off, or auto - - -//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx - - -/obj/machinery/power/apc - name = "area power controller" - desc = "A control terminal for the area electrical systems." - icon_state = "apc0" - use_power = NO_POWER_USE - max_integrity = 200 - integrity_failure = 50 - resistance_flags = FIRE_PROOF - req_access = list(ACCESS_ENGINE_EQUIP) - siemens_strength = 1 - damage_deflection = 10 - var/area/area - var/areastring = null - var/obj/item/stock_parts/cell/cell - var/start_charge = 90 // initial cell charge % - var/cell_type = 2500 //Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs - var/opened = 0 //0=closed, 1=opened, 2=cover removed - var/shorted = 0 - var/lighting = 3 - var/equipment = 3 - var/environ = 3 - var/operating = 1 - var/charging = 0 - var/chargemode = 1 - var/chargecount = 0 - var/locked = 1 - var/coverlocked = 1 - var/aidisabled = 0 - var/tdir = null - var/obj/machinery/power/terminal/terminal = null - var/lastused_light = 0 - var/lastused_equip = 0 - var/lastused_environ = 0 - var/lastused_total = 0 - var/main_status = 0 - powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( - var/malfhack = 0 //New var for my changes to AI malf. --NeoFite - var/mob/living/silicon/ai/malfai = null //See above --NeoFite - var/debug= 0 - var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. -// luminosity = 1 - var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver - var/overload = 1 //used for the Blackout malf module - var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment - var/mob/living/silicon/ai/occupier = null - var/longtermpower = 10 - var/update_state = -1 - var/update_overlay = -1 - var/global/status_overlays = 0 - var/updating_icon = 0 - var/datum/wires/apc/wires = null - //var/debug = 0 - var/global/list/status_overlays_lock - var/global/list/status_overlays_charging - var/global/list/status_overlays_equipment - var/global/list/status_overlays_lighting - var/global/list/status_overlays_environ - var/indestructible = 0 // If set, prevents aliens from destroying it - var/keep_preset_name = 0 - - var/report_power_alarm = TRUE - - var/shock_proof = 0 //if set to 1, this APC will not arc bolts of electricity if it's overloaded. - - // Nightshift - var/nightshift_lights = FALSE - var/last_nightshift_switch = 0 - -/obj/machinery/power/apc/worn_out - name = "\improper Worn out APC" - keep_preset_name = 1 - locked = 0 - environ = 0 - equipment = 0 - lighting = 0 - operating = 0 - -/obj/machinery/power/apc/noalarm - report_power_alarm = FALSE - -/obj/machinery/power/apc/syndicate //general syndicate access - req_access = list(ACCESS_SYNDICATE) - report_power_alarm = FALSE - -/obj/item/apc_electronics - name = "power control module" - desc = "Heavy-duty switching circuits for power control." - icon = 'icons/obj/module.dmi' - icon_state = "power_mod" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "engineering=2;programming=1" - item_state = "electronic" - flags = CONDUCT - usesound = 'sound/items/deconstruct.ogg' - toolspeed = 1 - -/obj/machinery/power/apc/get_cell() - return cell - -/obj/machinery/power/apc/connect_to_network() - //Override because the APC does not directly connect to the network; it goes through a terminal. - //The terminal is what the power computer looks for anyway. - if(terminal) - terminal.connect_to_network() - -/obj/machinery/power/apc/New(turf/loc, ndir, building = 0) - if(!armor) - armor = list("melee" = 20, "bullet" = 20, "laser" = 10, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50) - ..() - GLOB.apcs += src - GLOB.apcs = sortAtom(GLOB.apcs) - - wires = new(src) - // offset 24 pixels in direction of dir - // this allows the APC to be embedded in a wall, yet still inside an area - if(building) - setDir(ndir) - tdir = dir // to fix Vars bug - setDir(SOUTH) - - pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24) - pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0 - if(building) - area = get_area(src) - area.apc |= src - opened = 1 - operating = 0 - name = "[area.name] APC" - stat |= MAINT - update_icon() - addtimer(CALLBACK(src, .proc/update), 5) - -/obj/machinery/power/apc/Destroy() - GLOB.apcs -= src - if(malfai && operating) - malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 - area.power_change() - if(occupier) - malfvacate(1) - QDEL_NULL(wires) - QDEL_NULL(cell) - if(terminal) - disconnect_terminal() - area.apc -= src - return ..() - -/obj/machinery/power/apc/proc/make_terminal() - // create a terminal object at the same position as original turf loc - // wires will attach to this - terminal = new/obj/machinery/power/terminal(src.loc) - terminal.setDir(tdir) - terminal.master = src - -/obj/machinery/power/apc/Initialize(mapload) - . = ..() - if(!mapload) - return - has_electronics = 2 //installed and secured - // is starting with a power cell installed, create it and set its charge level - if(cell_type) - cell = new/obj/item/stock_parts/cell/upgraded(src) - cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) - cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) - - var/area/A = get_area(src) - - - //if area isn't specified use current - if(keep_preset_name) - if(isarea(A)) - area = A - // no-op, keep the name - else if(isarea(A) && src.areastring == null) - area = A - name = "\improper [area.name] APC" - else - area = get_area_name(areastring) - name = "\improper [area.name] APC" - area.apc |= src - - update_icon() - - make_terminal() - - addtimer(CALLBACK(src, .proc/update), 5) - -/obj/machinery/power/apc/examine(mob/user) - . = ..() - if(in_range(user, src)) - if(stat & BROKEN) - . += "Looks broken." - else if(opened) - if(has_electronics && terminal) - . += "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." - else if(!has_electronics && terminal) - . += "There are some wires but no electronics." - else if(has_electronics && !terminal) - . += "Electronics installed but not wired." - else /* if(!has_electronics && !terminal) */ - . += "There is no electronics nor connected wires." - else - if(stat & MAINT) - . += "The cover is closed. Something wrong with it: it doesn't work." - else if(malfhack) - . += "The cover is broken. It may be hard to force it open." - else - . += "The cover is closed." - -// update the APC icon to show the three base states -// also add overlays for indicator lights -/obj/machinery/power/apc/update_icon(force_update = FALSE) - - if(!status_overlays || force_update) - status_overlays = 1 - status_overlays_lock = new - status_overlays_charging = new - status_overlays_equipment = new - status_overlays_lighting = new - status_overlays_environ = new - - status_overlays_lock.len = 2 - status_overlays_charging.len = 3 - status_overlays_equipment.len = 4 - status_overlays_lighting.len = 4 - status_overlays_environ.len = 4 - - status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red - status_overlays_lock[2] = image(icon, "apcox-1") - - status_overlays_charging[1] = image(icon, "apco3-0") - status_overlays_charging[2] = image(icon, "apco3-1") - status_overlays_charging[3] = image(icon, "apco3-2") - - status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue - status_overlays_equipment[2] = image(icon, "apco0-1") - status_overlays_equipment[3] = image(icon, "apco0-2") - status_overlays_equipment[4] = image(icon, "apco0-3") - - status_overlays_lighting[1] = image(icon, "apco1-0") - status_overlays_lighting[2] = image(icon, "apco1-1") - status_overlays_lighting[3] = image(icon, "apco1-2") - status_overlays_lighting[4] = image(icon, "apco1-3") - - status_overlays_environ[1] = image(icon, "apco2-0") - status_overlays_environ[2] = image(icon, "apco2-1") - status_overlays_environ[3] = image(icon, "apco2-2") - status_overlays_environ[4] = image(icon, "apco2-3") - - - - var/update = check_updates() //returns 0 if no need to update icons. - // 1 if we need to update the icon_state - // 2 if we need to update the overlays - if(!update && !force_update) - return - - if(force_update || update & 1) // Updating the icon state - if(update_state & UPSTATE_ALLGOOD) - icon_state = "apc0" - else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) - var/basestate = "apc[ cell ? "2" : "1" ]" - if(update_state & UPSTATE_OPENED1) - if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) - icon_state = "apcmaint" //disabled APC cannot hold cell - else - icon_state = basestate - else if(update_state & UPSTATE_OPENED2) - icon_state = "[basestate]-nocover" - else if(update_state & UPSTATE_BROKE) - icon_state = "apc-b" - else if(update_state & UPSTATE_BLUESCREEN) - icon_state = "apcemag" - else if(update_state & UPSTATE_WIREEXP) - icon_state = "apcewires" - - - - if(!(update_state & UPSTATE_ALLGOOD)) - if(overlays.len) - overlays = 0 - return - - - - if(force_update || update & 2) - - if(overlays.len) - overlays.len = 0 - - if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) - overlays += status_overlays_lock[locked+1] - overlays += status_overlays_charging[charging+1] - if(operating) - overlays += status_overlays_equipment[equipment+1] - overlays += status_overlays_lighting[lighting+1] - overlays += status_overlays_environ[environ+1] - - -/obj/machinery/power/apc/proc/check_updates() - - var/last_update_state = update_state - var/last_update_overlay = update_overlay - update_state = 0 - update_overlay = 0 - - if(cell) - update_state |= UPSTATE_CELL_IN - if(stat & BROKEN) - update_state |= UPSTATE_BROKE - if(stat & MAINT) - update_state |= UPSTATE_MAINT - if(opened) - if(opened==1) - update_state |= UPSTATE_OPENED1 - if(opened==2) - update_state |= UPSTATE_OPENED2 - else if(emagged || malfai) - update_state |= UPSTATE_BLUESCREEN - else if(panel_open) - update_state |= UPSTATE_WIREEXP - if(update_state <= 1) - update_state |= UPSTATE_ALLGOOD - - if(update_state & UPSTATE_ALLGOOD) - if(locked) - update_overlay |= APC_UPOVERLAY_LOCKED - - if(!charging) - update_overlay |= APC_UPOVERLAY_CHARGEING0 - else if(charging == 1) - update_overlay |= APC_UPOVERLAY_CHARGEING1 - else if(charging == 2) - update_overlay |= APC_UPOVERLAY_CHARGEING2 - - if(!equipment) - update_overlay |= APC_UPOVERLAY_EQUIPMENT0 - else if(equipment == 1) - update_overlay |= APC_UPOVERLAY_EQUIPMENT1 - else if(equipment == 2) - update_overlay |= APC_UPOVERLAY_EQUIPMENT2 - - if(!lighting) - update_overlay |= APC_UPOVERLAY_LIGHTING0 - else if(lighting == 1) - update_overlay |= APC_UPOVERLAY_LIGHTING1 - else if(lighting == 2) - update_overlay |= APC_UPOVERLAY_LIGHTING2 - - if(!environ) - update_overlay |= APC_UPOVERLAY_ENVIRON0 - else if(environ==1) - update_overlay |= APC_UPOVERLAY_ENVIRON1 - else if(environ==2) - update_overlay |= APC_UPOVERLAY_ENVIRON2 - - var/results = 0 - if(last_update_state == update_state && last_update_overlay == update_overlay) - return 0 - if(last_update_state != update_state) - results += 1 - if(last_update_overlay != update_overlay) - results += 2 - return results - -// Used in process so it doesn't update the icon too much -/obj/machinery/power/apc/proc/queue_icon_update() - - if(!updating_icon) - updating_icon = 1 - // Start the update - spawn(APC_UPDATE_ICON_COOLDOWN) - update_icon() - updating_icon = 0 - -/obj/machinery/power/apc/get_spooked(second_pass = FALSE) - if(opened || panel_open) - return - if(stat & (NOPOWER | BROKEN)) - return - if(!second_pass) //The first time, we just cut overlays - addtimer(CALLBACK(src, .get_spooked, TRUE), 1) - cut_overlays() - else - flick("apcemag", src) //Second time we cause the APC to update its icon, then add a timer to update icon later - addtimer(CALLBACK(src, .proc/update_icon, TRUE), 10) - -//attack with an item - open/close cover, insert cell, or (un)lock interface -/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) - - if(issilicon(user) && get_dist(src,user)>1) - return src.attack_hand(user) - - else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside - if(cell) - to_chat(user, "There is a power cell already installed!") - return - else - if(stat & MAINT) - to_chat(user, "There is no connector for your power cell!") - return - if(!user.drop_item()) - return - W.forceMove(src) - cell = W - user.visible_message(\ - "[user.name] has inserted the power cell to [src.name]!",\ - "You insert the power cell.") - chargecount = 0 - update_icon() - - else if(W.GetID()) // trying to unlock the interface with an ID card - togglelock(user) - - else if(istype(W, /obj/item/stack/cable_coil) && opened) - var/turf/host_turf = get_turf(src) - if(!host_turf) - throw EXCEPTION("attackby on APC when it's not on a turf") - return - if(host_turf.intact) - to_chat(user, "You must remove the floor plating in front of the APC first!") - return - else if(terminal) // it already have terminal - to_chat(user, "This APC is already wired!") - return - else if(has_electronics == 0) - to_chat(user, "There is nothing to wire!") - return - - var/obj/item/stack/cable_coil/C = W - if(C.get_amount() < 10) - to_chat(user, "You need ten lengths of cable for APC!") - return - user.visible_message("[user.name] adds cables to the APC frame.", \ - "You start adding cables to the APC frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - if(do_after(user, 20, target = src)) - if(C.get_amount() < 10 || !C) - return - if(C.get_amount() >= 10 && !terminal && opened && has_electronics > 0) - var/turf/T = get_turf(src) - var/obj/structure/cable/N = T.get_cable_node() - if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) - do_sparks(5, TRUE, src) - return - C.use(10) - to_chat(user, "You add cables to the APC frame.") - make_terminal() - terminal.connect_to_network() - - else if(istype(W, /obj/item/apc_electronics) && opened) - if(has_electronics!=0) // there are already electronicks inside - to_chat(user, "You cannot put the board inside, there already is one!") - return - else if(stat & BROKEN) - to_chat(user, "You cannot put the board inside, the frame is damaged!") - return - - user.visible_message("[user.name] inserts the power control board into [src].", \ - "You start to insert the power control board into the frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - if(do_after(user, 10, target = src)) - if(has_electronics==0) - has_electronics = 1 - locked = FALSE - to_chat(user, "You place the power control board inside the frame.") - qdel(W) - - else if(istype(W, /obj/item/mounted/frame/apc_frame) && opened) - if(!(stat & BROKEN || opened==2 || obj_integrity < max_integrity)) // There is nothing to repair - to_chat(user, "You found no reason for repairing this APC") - return - if(!(stat & BROKEN) && opened==2) // Cover is the only thing broken, we do not need to remove elctronicks to replace cover - user.visible_message("[user.name] replaces missing APC's cover.",\ - "You begin to replace APC's cover...") - if(do_after(user, 20, target = src)) // replacing cover is quicker than replacing whole frame - to_chat(user, "You replace missing APC's cover.") - qdel(W) - opened = 1 - update_icon() - return - if(has_electronics) - to_chat(user, "You cannot repair this APC until you remove the electronics still inside!") - return - user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ - "You begin to replace the damaged APC frame...") - if(do_after(user, 50, target = src)) - to_chat(user, "You replace the damaged APC frame with a new one.") - qdel(W) - stat &= ~BROKEN - obj_integrity = max_integrity - if(opened==2) - opened = 1 - update_icon() - return - else - return ..() - - -/obj/machinery/power/apc/crowbar_act(mob/living/user, obj/item/I) - . = TRUE - if(!I.tool_start_check(user, 0)) - return - if(opened) // a) on open apc - if(has_electronics==1) - if(terminal) - to_chat(user, "Disconnect the wires first!") - return - to_chat(user, "You are trying to remove the power control board..." ) - if(I.use_tool(src, user, 50, volume = I.tool_volume)) - if(has_electronics==1) - has_electronics = 0 - if(stat & BROKEN) - user.visible_message(\ - "[user.name] has broken the power control board inside [src.name]!", - "You break the charred power control board and remove the remains.", - "You hear a crack.") - return - //SSticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 - else if(emagged) // We emag board, not APC's frame - emagged = FALSE - user.visible_message( - "[user.name] has discarded emaged power control board from [src.name]!", - "You discarded shorten board.") - return - else if(malfhack) // AI hacks board, not APC's frame - user.visible_message(\ - "[user.name] has discarded strangely programmed power control board from [src.name]!", - "You discarded strangely programmed board.") - malfai = null - malfhack = 0 - return - else - user.visible_message(\ - "[user.name] has removed the power control board from [src.name]!", - "You remove the power control board.") - new /obj/item/apc_electronics(loc) - return - else if(opened!=2) //cover isn't removed - opened = 0 - coverlocked = TRUE //closing cover relocks it - update_icon() - return - else if(!(stat & BROKEN)) // b) on closed and not broken APC - if(coverlocked && !(stat & MAINT)) // locked... - to_chat(user, "The cover is locked and cannot be opened!") - return - else if(panel_open) // wires are exposed - to_chat(user, "Exposed wires prevents you from opening it!") - return - else - opened = 1 - update_icon() - -/obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - else if(opened) - if(cell && !(stat & MAINT)) - to_chat(user, "Close the APC first!") //Less hints more mystery! - return - else - if(has_electronics==1) - has_electronics = 2 - stat &= ~MAINT - to_chat(user, "You screw the circuit electronics into place.") - else if(has_electronics==2) - has_electronics = 1 - stat |= MAINT - to_chat(user, "You unfasten the electronics.") - else - to_chat(user, "There is nothing to secure!") - return - update_icon() - else if(emagged) - to_chat(user, "The interface is broken!") - else - panel_open = !panel_open - to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") - update_icon() - - -/obj/machinery/power/apc/wirecutter_act(mob/living/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(panel_open && !opened) - wires.Interact(user) - else if(terminal && opened) - terminal.dismantle(user, I) - -/obj/machinery/power/apc/multitool_act(mob/living/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(panel_open && !opened) - wires.Interact(user) - -/obj/machinery/power/apc/proc/togglelock(mob/living/user) - if(emagged) - to_chat(user, "The interface is broken!") - else if(opened) - to_chat(user, "You must close the cover to swipe an ID card!") - else if(panel_open) - to_chat(user, "You must close the panel!") - else if(stat & (BROKEN|MAINT)) - to_chat(user, "Nothing happens!") - else - if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") - update_icon() - else - to_chat(user, "Access denied.") - -/obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(stat & BROKEN) - return damage_amount - . = ..() - -/obj/machinery/power/apc/obj_break(damage_flag) - if(!(flags & NODECONSTRUCT)) - set_broken() - -/obj/machinery/power/apc/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - set_broken() - if(opened != 2) - opened = 2 - coverlocked = FALSE - visible_message("The APC cover is knocked down!") - update_icon() - -/obj/machinery/power/apc/welder_act(mob/user, obj/item/I) - if(!opened || has_electronics || terminal) - return - . = TRUE - if(!I.tool_use_check(user, 3)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(I.use_tool(src, user, 50, amount = 3, volume = I.tool_volume)) - if((stat & BROKEN) || opened==2) - new /obj/item/stack/sheet/metal(loc) - user.visible_message(\ - "[user.name] has cut [src] apart with [I].",\ - "You disassembled the broken APC frame.") - else - new /obj/item/mounted/frame/apc_frame(loc) - user.visible_message(\ - "[user.name] has cut [src] from the wall with [I].",\ - "You cut the APC frame from the wall.") - qdel(src) - -/obj/machinery/power/apc/emag_act(user as mob) - if(!(emagged || malfhack)) // trying to unlock with an emag card - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else if(panel_open) - to_chat(user, "You must close the panel first.") - else if(stat & (BROKEN|MAINT)) - to_chat(user, "Nothing happens.") - else - flick("apc-spark", src) - emagged = 1 - locked = 0 - to_chat(user, "You emag the APC interface.") - update_icon() - -// attack with hand - remove cell (if cover open) or interact with the APC -/obj/machinery/power/apc/attack_hand(mob/user) -// if(!can_use(user)) This already gets called in interact() and in topic() -// return - if(!user) - return - src.add_fingerprint(user) - - if(usr == user && opened && (!issilicon(user))) - if(cell) - if(issilicon(user)) - cell.loc=src.loc // Drop it, whoops. - else - user.put_in_hands(cell) - cell.add_fingerprint(user) - cell.update_icon() - - src.cell = null - user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.") -// to_chat(user, "You remove the power cell.") - charging = 0 - src.update_icon() - return - if(stat & (BROKEN|MAINT)) - return - - src.interact(user) - -/obj/machinery/power/apc/attack_ghost(mob/user) - if(panel_open) - wires.Interact(user) - return ui_interact(user) - -/obj/machinery/power/apc/interact(mob/user) - if(!user) - return - - if(panel_open) - wires.Interact(user) - - return ui_interact(user) - - -/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) - if(!istype(malf)) - return FALSE - - // Only if they're a traitor OR they have the malf picker from the combat module - if(!malf.mind.has_antag_datum(/datum/antagonist/traitor) && !malf.malf_picker) - return FALSE - - if(malfai == (malf.parent || malf)) - if(occupier == malf) - return APC_MALF_SHUNTED_HERE - else if(istype(malf.loc, /obj/machinery/power/apc)) - return APC_MALF_SHUNTED_OTHER - else - return APC_MALF_HACKED - else - return APC_MALF_NOT_HACKED - -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!user) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new one - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460) - ui.open() - // Auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["locked"] = is_locked(user) - data["isOperating"] = operating - data["externalPower"] = main_status - data["powerCellStatus"] = cell ? cell.percent() : null - data["chargeMode"] = chargemode - data["chargingStatus"] = charging - data["totalLoad"] = round(lastused_equip + lastused_light + lastused_environ) - data["coverLocked"] = coverlocked - data["siliconUser"] = istype(user, /mob/living/silicon) - data["siliconLock"] = locked - data["malfStatus"] = get_malf_status(user) - data["nightshiftLights"] = nightshift_lights - - var/powerChannels[0] - powerChannels[++powerChannels.len] = list( - "title" = "Equipment", - "powerLoad" = round(lastused_equip), - "status" = equipment, - "topicParams" = list( - "auto" = list("eqp" = 3), - "on" = list("eqp" = 2), - "off" = list("eqp" = 1) - ) - ) - powerChannels[++powerChannels.len] = list( - "title" = "Lighting", - "powerLoad" = round(lastused_light), - "status" = lighting, - "topicParams" = list( - "auto" = list("lgt" = 3), - "on" = list("lgt" = 2), - "off" = list("lgt" = 1) - ) - ) - powerChannels[++powerChannels.len] = list( - "title" = "Environment", - "powerLoad" = round(lastused_environ), - "status" = environ, - "topicParams" = list( - "auto" = list("env" = 3), - "on" = list("env" = 2), - "off" = list("env" = 1) - ) - ) - - data["powerChannels"] = powerChannels - - return data - -/obj/machinery/power/apc/proc/report() - return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" - -/obj/machinery/power/apc/proc/update() - if(operating && !shorted) - area.power_light = (lighting > 1) - area.power_equip = (equipment > 1) - area.power_environ = (environ > 1) -// if(area.name == "AI Chamber") -// spawn(10) -// to_chat(world, " [area.name] [area.power_equip]") - else - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 -// if(area.name == "AI Chamber") -// to_chat(world, "[area.power_equip]") - area.power_change() - -/obj/machinery/power/apc/proc/isWireCut(var/wireIndex) - return wires.IsIndexCut(wireIndex) - - -/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic() - if(user.can_admin_interact()) - return 1 - - autoflag = 5 - if(istype(user, /mob/living/silicon)) - var/mob/living/silicon/ai/AI = user - var/mob/living/silicon/robot/robot = user - if( \ - src.aidisabled || \ - malfhack && istype(malfai) && \ - ( \ - (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ - (istype(robot) && (robot in malfai.connected_robots)) \ - ) \ - ) - if(!loud) - to_chat(user, "\The [src] has AI control disabled!") - user << browse(null, "window=apc") - user.unset_machine() - return 0 - else - if((!in_range(src, user) || !istype(src.loc, /turf))) - return 0 - - var/mob/living/carbon/human/H = user - if(istype(H)) - if(H.getBrainLoss() >= 60) - for(var/mob/M in viewers(src, null)) - to_chat(M, "[H] stares cluelessly at [src] and drools.") - return 0 - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return 0 - return 1 - -/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob) - if(user.can_admin_interact()) - return 1 - if(isAI(user) || isrobot(user)) - return 1 - else - return !locked - -/obj/machinery/power/apc/proc/is_locked(mob/user as mob) - if(user.can_admin_interact()) - return 0 - if(isAI(user) || isrobot(user)) - return 0 - else - return locked - -/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) - if(..()) - return 1 - - if(!can_use(usr, 1)) - return 1 - - if(href_list["lock"]) - if(!is_authenticated(usr)) - return - - coverlocked = !coverlocked - - else if(href_list["breaker"]) - if(!is_authenticated(usr)) - return - - toggle_breaker() - - else if(href_list["toggle_nightshift"]) - if(!is_authenticated(usr)) - return - - if(last_nightshift_switch > world.time + 100) // don't spam... - to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") - return - last_nightshift_switch = world.time - set_nightshift(!nightshift_lights) - - else if(href_list["cmode"]) - if(!is_authenticated(usr)) - return - - chargemode = !chargemode - if(!chargemode) - charging = 0 - update_icon() - - else if(href_list["eqp"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["eqp"]) - equipment = setsubsystem(val) - update_icon() - update() - - else if(href_list["lgt"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["lgt"]) - lighting = setsubsystem(val) - update_icon() - update() - - else if(href_list["env"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["env"]) - environ = setsubsystem(val) - update_icon() - update() - else if( href_list["close"] ) - SSnanoui.close_user_uis(usr, src) - - return 0 - else if(href_list["close2"]) - usr << browse(null, "window=apcwires") - - return 0 - - else if(href_list["overload"]) - if(issilicon(usr) && !aidisabled) - overload_lighting() - - else if(href_list["malfhack"]) - if(get_malf_status(usr)) - malfhack(usr) - - else if(href_list["occupyapc"]) - if(get_malf_status(usr)) - malfoccupy(usr) - - else if(href_list["deoccupyapc"]) - if(get_malf_status(usr)) - malfvacate() - - else if(href_list["toggleaccess"]) - if(istype(usr, /mob/living/silicon)) - if(emagged || aidisabled || (stat & (BROKEN|MAINT))) - to_chat(usr, "The APC does not respond to the command.") - else - locked = !locked - update_icon() - - return 0 - -/obj/machinery/power/apc/proc/toggle_breaker() - operating = !operating - update() - update_icon() - -/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf) - if(!istype(malf)) - return - if(get_malf_status(malf) != APC_MALF_NOT_HACKED) - return - if(malf.malfhacking) - to_chat(malf, "You are already hacking an APC.") - return - to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") - malf.malfhack = src - malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) - var/obj/screen/alert/hackingapc/A - A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) - A.target = src - -/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) - if(!istype(malf)) - return - if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC - to_chat(malf, "You must evacuate your current APC first!") - return - if(!malf.can_shunt) - to_chat(malf, "You cannot shunt!") - return - if(!is_station_level(src.z)) - return - occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) - occupier.adjustOxyLoss(malf.getOxyLoss()) - if(!findtext(occupier.name, "APC Copy")) - occupier.name = "[malf.name] APC Copy" - if(malf.parent) - occupier.parent = malf.parent - else - occupier.parent = malf - malf.shunted = 1 - malf.mind.transfer_to(occupier) - occupier.eyeobj.name = "[occupier.name] (AI Eye)" - if(malf.parent) - qdel(malf) - occupier.verbs += /mob/living/silicon/ai/proc/corereturn - occupier.cancel_camera() - if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking) - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - point.the_disk = src //the pinpointer will detect the shunted AI - -/obj/machinery/power/apc/proc/malfvacate(forced) - if(!occupier) - return - if(occupier.parent && occupier.parent.stat != DEAD) - occupier.mind.transfer_to(occupier.parent) - occupier.parent.shunted = 0 - occupier.parent.adjustOxyLoss(occupier.getOxyLoss()) - occupier.parent.cancel_camera() - qdel(occupier) - if(seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer tracks the AI back into its core. - else - to_chat(occupier, "Primary core damaged, unable to return core processes.") - if(forced) - occupier.loc = loc - occupier.death() - occupier.gib() - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - point.the_disk = null //Pinpointers go back to tracking the nuke disk - -/obj/machinery/power/apc/proc/ion_act() - //intended to be exactly the same as an AI malf attack - if(!src.malfhack && is_station_level(src.z)) - if(prob(3)) - src.locked = 1 - if(src.cell.charge > 0) - src.cell.charge = 0 - cell.corrupt() - src.malfhack = 1 - update_icon() - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - do_sparks(3, 1, src) - for(var/mob/M in viewers(src)) - M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) - - -/obj/machinery/power/apc/surplus() - if(terminal) - return terminal.surplus() - else - return 0 - -/obj/machinery/power/apc/add_load(amount) - if(terminal && terminal.powernet) - terminal.add_load(amount) - -/obj/machinery/power/apc/avail() - if(terminal) - return terminal.avail() - else - return 0 - -/obj/machinery/power/apc/process() - if(stat & (BROKEN|MAINT)) - return - if(!area.requires_power) - return - - lastused_light = area.usage(STATIC_LIGHT) - lastused_light += area.usage(LIGHT) - lastused_equip = area.usage(EQUIP) - lastused_equip += area.usage(STATIC_EQUIP) - lastused_environ = area.usage(ENVIRON) - lastused_environ += area.usage(STATIC_ENVIRON) - area.clear_usage() - - lastused_total = lastused_light + lastused_equip + lastused_environ - - //store states to update icon if any change - var/last_lt = lighting - var/last_eq = equipment - var/last_en = environ - var/last_ch = charging - - var/excess = surplus() - - if(!src.avail()) - main_status = 0 - else if(excess < 0) - main_status = 1 - else - main_status = 2 - - if(debug) - log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]") - - if(cell && !shorted) - // draw power from cell as before to power the area - var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell - cell.use(cellused) - - if(excess > lastused_total) // if power excess recharge the cell - // by the same amount just used - cell.give(cellused) - add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell - - - else // no excess, and not enough per-apc - if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? - cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can - add_load(excess) // so draw what we can from the grid - charging = 0 - - else // not enough power available to run the last tick! - charging = 0 - chargecount = 0 - // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - autoflag = 0 - - - // Set channels depending on how much charge we have left - - // Allow the APC to operate as normal if the cell can charge - if(charging && longtermpower < 10) - longtermpower += 1 - else if(longtermpower > -10) - longtermpower -= 2 - - if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101 - if(autoflag != 3) - equipment = autoset(equipment, 1) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - autoflag = 3 - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.clearAlarm(loc, src) - else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment - if(autoflag != 2) - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 2 - else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment - if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 2) - environ = autoset(environ, 1) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 1 - else if(cell.charge <= 0) // zero charge, turn all off - if(autoflag != 0) - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 0 - - // now trickle-charge the cell - if(chargemode && charging == 1 && operating) - if(excess > 0) // check to make sure we have enough to charge - // Max charge is capped to % per second constant - var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL) - add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid - cell.give(ch) // actually recharge the cell - - else - charging = 0 // stop charging - chargecount = 0 - - // show cell as fully charged if so - if(cell.charge >= cell.maxcharge) - cell.charge = cell.maxcharge - charging = 2 - - if(chargemode) - if(!charging) - if(excess > cell.maxcharge*GLOB.CHARGELEVEL) - chargecount++ - else - chargecount = 0 - - if(chargecount == 10) - - chargecount = 0 - charging = 1 - - else // chargemode off - charging = 0 - chargecount = 0 - - if(excess >= 5000000 && !shock_proof) //If there's more than 5,000,000 watts in the grid, start arcing and shocking people. - if(prob(5)) - var/list/shock_mobs = list() - for(var/C in view(get_turf(src), 5)) //We only want to shock a single random mob in range, not every one. - if(isliving(C)) - shock_mobs += C - if(shock_mobs.len) - var/mob/living/L = pick(shock_mobs) - L.electrocute_act(rand(5, 25), "electrical arc") - playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1) - Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) - - else // no cell, switch everything off - - charging = 0 - chargecount = 0 - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 0 - - // update icon & area power if anything changed - - if(last_lt != lighting || last_eq != equipment || last_en != environ) - queue_icon_update() - update() - else if(last_ch != charging) - queue_icon_update() - -/obj/machinery/power/apc/proc/autoset(var/val, var/on) - if(on==0) - if(val==2) // if on, return off - return 0 - else if(val==3) // if auto-on, return auto-off - return 1 - - else if(on==1) - if(val==1) // if auto-off, return auto-on - return 3 - - else if(on==2) - if(val==3) // if auto-on, return auto-off - return 1 - - return val - -// damage and destruction acts - -/obj/machinery/power/apc/emp_act(severity) - if(cell) - cell.emp_act(severity) - if(occupier) - occupier.emp_act(severity) - lighting = 0 - equipment = 0 - environ = 0 - update_icon() - update() - spawn(600) - equipment = 3 - environ = 3 - update_icon() - update() - ..() - -/obj/machinery/power/apc/blob_act(obj/structure/blob/B) - set_broken() - -/obj/machinery/power/apc/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null - -/obj/machinery/power/apc/proc/set_broken() - if(malfai && operating) - malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) - stat |= BROKEN - operating = 0 - if(occupier) - malfvacate(1) - update_icon() - update() - -// overload all the lights in this APC area - -/obj/machinery/power/apc/proc/overload_lighting(chance = 100) - if(!operating || shorted) - return - if(cell && cell.charge >= 20) - cell.use(20) - spawn(0) - for(var/obj/machinery/light/L in area) - if(prob(chance)) - L.break_light_tube(0, 1) - stoplag() - -/obj/machinery/power/apc/proc/null_charge() - for(var/obj/machinery/light/L in area) - L.break_light_tube(0, 1) - stoplag() - -/obj/machinery/power/apc/proc/setsubsystem(val) - if(cell && cell.charge > 0) - return (val==1) ? 0 : val - else if(val == 3) - return 1 - else - return 0 - -/obj/machinery/power/apc/proc/set_nightshift(on) - set waitfor = FALSE - nightshift_lights = on - for(var/obj/machinery/light/L in area) - if(L.nightshift_allowed) - L.nightshift_enabled = nightshift_lights - L.update(FALSE) - CHECK_TICK - -#undef APC_UPDATE_ICON_COOLDOWN +#define APC_WIRE_IDSCAN 1 +#define APC_WIRE_MAIN_POWER1 2 +#define APC_WIRE_MAIN_POWER2 3 +#define APC_WIRE_AI_CONTROL 4 + +//update_state +#define UPSTATE_CELL_IN 1 +#define UPSTATE_OPENED1 2 +#define UPSTATE_OPENED2 4 +#define UPSTATE_MAINT 8 +#define UPSTATE_BROKE 16 +#define UPSTATE_BLUESCREEN 32 +#define UPSTATE_WIREEXP 64 +#define UPSTATE_ALLGOOD 128 + +//update_overlay +#define APC_UPOVERLAY_CHARGEING0 1 +#define APC_UPOVERLAY_CHARGEING1 2 +#define APC_UPOVERLAY_CHARGEING2 4 +#define APC_UPOVERLAY_EQUIPMENT0 8 +#define APC_UPOVERLAY_EQUIPMENT1 16 +#define APC_UPOVERLAY_EQUIPMENT2 32 +#define APC_UPOVERLAY_LIGHTING0 64 +#define APC_UPOVERLAY_LIGHTING1 128 +#define APC_UPOVERLAY_LIGHTING2 256 +#define APC_UPOVERLAY_ENVIRON0 512 +#define APC_UPOVERLAY_ENVIRON1 1024 +#define APC_UPOVERLAY_ENVIRON2 2048 +#define APC_UPOVERLAY_LOCKED 4096 + +#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds + +// APC malf status +#define APC_MALF_NOT_HACKED 1 +#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core. +#define APC_MALF_SHUNTED_HERE 3 // User is shunted in this APC +#define APC_MALF_SHUNTED_OTHER 4 // User is shunted in another APC + +// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) +// one per area, needs wire conection to power network through a terminal + +// controls power to devices in that area +// may be opened to change power cell +// three different channels (lighting/equipment/environ) - may each be set to on, off, or auto + + +//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx + + +/obj/machinery/power/apc + name = "area power controller" + desc = "A control terminal for the area electrical systems." + icon_state = "apc0" + use_power = NO_POWER_USE + max_integrity = 200 + integrity_failure = 50 + resistance_flags = FIRE_PROOF + req_access = list(ACCESS_ENGINE_EQUIP) + siemens_strength = 1 + damage_deflection = 10 + var/area/area + var/areastring = null + var/obj/item/stock_parts/cell/cell + var/start_charge = 90 // initial cell charge % + var/cell_type = 2500 //Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs + var/opened = 0 //0=closed, 1=opened, 2=cover removed + var/shorted = 0 + var/lighting = 3 + var/equipment = 3 + var/environ = 3 + var/operating = 1 + var/charging = 0 + var/chargemode = 1 + var/chargecount = 0 + var/locked = 1 + var/coverlocked = 1 + var/aidisabled = 0 + var/tdir = null + var/obj/machinery/power/terminal/terminal = null + var/lastused_light = 0 + var/lastused_equip = 0 + var/lastused_environ = 0 + var/lastused_total = 0 + var/main_status = 0 + powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( + var/malfhack = 0 //New var for my changes to AI malf. --NeoFite + var/mob/living/silicon/ai/malfai = null //See above --NeoFite + var/debug= 0 + var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. +// luminosity = 1 + var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver + var/overload = 1 //used for the Blackout malf module + var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment + var/mob/living/silicon/ai/occupier = null + var/longtermpower = 10 + var/update_state = -1 + var/update_overlay = -1 + var/global/status_overlays = 0 + var/updating_icon = 0 + var/datum/wires/apc/wires = null + //var/debug = 0 + var/global/list/status_overlays_lock + var/global/list/status_overlays_charging + var/global/list/status_overlays_equipment + var/global/list/status_overlays_lighting + var/global/list/status_overlays_environ + var/indestructible = 0 // If set, prevents aliens from destroying it + var/keep_preset_name = 0 + + var/report_power_alarm = TRUE + + var/shock_proof = 0 //if set to 1, this APC will not arc bolts of electricity if it's overloaded. + + // Nightshift + var/nightshift_lights = FALSE + var/last_nightshift_switch = 0 + +/obj/machinery/power/apc/worn_out + name = "\improper Worn out APC" + keep_preset_name = 1 + locked = 0 + environ = 0 + equipment = 0 + lighting = 0 + operating = 0 + +/obj/machinery/power/apc/noalarm + report_power_alarm = FALSE + +/obj/machinery/power/apc/syndicate //general syndicate access + req_access = list(ACCESS_SYNDICATE) + report_power_alarm = FALSE + +/obj/item/apc_electronics + name = "power control module" + desc = "Heavy-duty switching circuits for power control." + icon = 'icons/obj/module.dmi' + icon_state = "power_mod" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "engineering=2;programming=1" + item_state = "electronic" + flags = CONDUCT + usesound = 'sound/items/deconstruct.ogg' + toolspeed = 1 + +/obj/machinery/power/apc/get_cell() + return cell + +/obj/machinery/power/apc/connect_to_network() + //Override because the APC does not directly connect to the network; it goes through a terminal. + //The terminal is what the power computer looks for anyway. + if(terminal) + terminal.connect_to_network() + +/obj/machinery/power/apc/New(turf/loc, ndir, building = 0) + if(!armor) + armor = list("melee" = 20, "bullet" = 20, "laser" = 10, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50) + ..() + GLOB.apcs += src + GLOB.apcs = sortAtom(GLOB.apcs) + + wires = new(src) + // offset 24 pixels in direction of dir + // this allows the APC to be embedded in a wall, yet still inside an area + if(building) + setDir(ndir) + tdir = dir // to fix Vars bug + setDir(SOUTH) + + pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24) + pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0 + if(building) + area = get_area(src) + area.apc |= src + opened = 1 + operating = 0 + name = "[area.name] APC" + stat |= MAINT + update_icon() + addtimer(CALLBACK(src, .proc/update), 5) + +/obj/machinery/power/apc/Destroy() + GLOB.apcs -= src + if(malfai && operating) + malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 + area.power_change() + if(occupier) + malfvacate(1) + QDEL_NULL(wires) + QDEL_NULL(cell) + if(terminal) + disconnect_terminal() + area.apc -= src + return ..() + +/obj/machinery/power/apc/proc/make_terminal() + // create a terminal object at the same position as original turf loc + // wires will attach to this + terminal = new/obj/machinery/power/terminal(src.loc) + terminal.setDir(tdir) + terminal.master = src + +/obj/machinery/power/apc/Initialize(mapload) + . = ..() + if(!mapload) + return + has_electronics = 2 //installed and secured + // is starting with a power cell installed, create it and set its charge level + if(cell_type) + cell = new/obj/item/stock_parts/cell/upgraded(src) + cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) + cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) + + var/area/A = get_area(src) + + + //if area isn't specified use current + if(keep_preset_name) + if(isarea(A)) + area = A + // no-op, keep the name + else if(isarea(A) && src.areastring == null) + area = A + name = "\improper [area.name] APC" + else + area = get_area_name(areastring) + name = "\improper [area.name] APC" + area.apc |= src + + update_icon() + + make_terminal() + + addtimer(CALLBACK(src, .proc/update), 5) + +/obj/machinery/power/apc/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(stat & BROKEN) + . += "Looks broken." + else if(opened) + if(has_electronics && terminal) + . += "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." + else if(!has_electronics && terminal) + . += "There are some wires but no electronics." + else if(has_electronics && !terminal) + . += "Electronics installed but not wired." + else /* if(!has_electronics && !terminal) */ + . += "There is no electronics nor connected wires." + else + if(stat & MAINT) + . += "The cover is closed. Something wrong with it: it doesn't work." + else if(malfhack) + . += "The cover is broken. It may be hard to force it open." + else + . += "The cover is closed." + +// update the APC icon to show the three base states +// also add overlays for indicator lights +/obj/machinery/power/apc/update_icon(force_update = FALSE) + + if(!status_overlays || force_update) + status_overlays = 1 + status_overlays_lock = new + status_overlays_charging = new + status_overlays_equipment = new + status_overlays_lighting = new + status_overlays_environ = new + + status_overlays_lock.len = 2 + status_overlays_charging.len = 3 + status_overlays_equipment.len = 4 + status_overlays_lighting.len = 4 + status_overlays_environ.len = 4 + + status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red + status_overlays_lock[2] = image(icon, "apcox-1") + + status_overlays_charging[1] = image(icon, "apco3-0") + status_overlays_charging[2] = image(icon, "apco3-1") + status_overlays_charging[3] = image(icon, "apco3-2") + + status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue + status_overlays_equipment[2] = image(icon, "apco0-1") + status_overlays_equipment[3] = image(icon, "apco0-2") + status_overlays_equipment[4] = image(icon, "apco0-3") + + status_overlays_lighting[1] = image(icon, "apco1-0") + status_overlays_lighting[2] = image(icon, "apco1-1") + status_overlays_lighting[3] = image(icon, "apco1-2") + status_overlays_lighting[4] = image(icon, "apco1-3") + + status_overlays_environ[1] = image(icon, "apco2-0") + status_overlays_environ[2] = image(icon, "apco2-1") + status_overlays_environ[3] = image(icon, "apco2-2") + status_overlays_environ[4] = image(icon, "apco2-3") + + + + var/update = check_updates() //returns 0 if no need to update icons. + // 1 if we need to update the icon_state + // 2 if we need to update the overlays + if(!update && !force_update) + return + + if(force_update || update & 1) // Updating the icon state + if(update_state & UPSTATE_ALLGOOD) + icon_state = "apc0" + else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) + var/basestate = "apc[ cell ? "2" : "1" ]" + if(update_state & UPSTATE_OPENED1) + if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) + icon_state = "apcmaint" //disabled APC cannot hold cell + else + icon_state = basestate + else if(update_state & UPSTATE_OPENED2) + icon_state = "[basestate]-nocover" + else if(update_state & UPSTATE_BROKE) + icon_state = "apc-b" + else if(update_state & UPSTATE_BLUESCREEN) + icon_state = "apcemag" + else if(update_state & UPSTATE_WIREEXP) + icon_state = "apcewires" + + + + if(!(update_state & UPSTATE_ALLGOOD)) + if(overlays.len) + overlays = 0 + return + + + + if(force_update || update & 2) + + if(overlays.len) + overlays.len = 0 + + if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) + overlays += status_overlays_lock[locked+1] + overlays += status_overlays_charging[charging+1] + if(operating) + overlays += status_overlays_equipment[equipment+1] + overlays += status_overlays_lighting[lighting+1] + overlays += status_overlays_environ[environ+1] + + +/obj/machinery/power/apc/proc/check_updates() + + var/last_update_state = update_state + var/last_update_overlay = update_overlay + update_state = 0 + update_overlay = 0 + + if(cell) + update_state |= UPSTATE_CELL_IN + if(stat & BROKEN) + update_state |= UPSTATE_BROKE + if(stat & MAINT) + update_state |= UPSTATE_MAINT + if(opened) + if(opened==1) + update_state |= UPSTATE_OPENED1 + if(opened==2) + update_state |= UPSTATE_OPENED2 + else if(emagged || malfai) + update_state |= UPSTATE_BLUESCREEN + else if(panel_open) + update_state |= UPSTATE_WIREEXP + if(update_state <= 1) + update_state |= UPSTATE_ALLGOOD + + if(update_state & UPSTATE_ALLGOOD) + if(locked) + update_overlay |= APC_UPOVERLAY_LOCKED + + if(!charging) + update_overlay |= APC_UPOVERLAY_CHARGEING0 + else if(charging == 1) + update_overlay |= APC_UPOVERLAY_CHARGEING1 + else if(charging == 2) + update_overlay |= APC_UPOVERLAY_CHARGEING2 + + if(!equipment) + update_overlay |= APC_UPOVERLAY_EQUIPMENT0 + else if(equipment == 1) + update_overlay |= APC_UPOVERLAY_EQUIPMENT1 + else if(equipment == 2) + update_overlay |= APC_UPOVERLAY_EQUIPMENT2 + + if(!lighting) + update_overlay |= APC_UPOVERLAY_LIGHTING0 + else if(lighting == 1) + update_overlay |= APC_UPOVERLAY_LIGHTING1 + else if(lighting == 2) + update_overlay |= APC_UPOVERLAY_LIGHTING2 + + if(!environ) + update_overlay |= APC_UPOVERLAY_ENVIRON0 + else if(environ==1) + update_overlay |= APC_UPOVERLAY_ENVIRON1 + else if(environ==2) + update_overlay |= APC_UPOVERLAY_ENVIRON2 + + var/results = 0 + if(last_update_state == update_state && last_update_overlay == update_overlay) + return 0 + if(last_update_state != update_state) + results += 1 + if(last_update_overlay != update_overlay) + results += 2 + return results + +// Used in process so it doesn't update the icon too much +/obj/machinery/power/apc/proc/queue_icon_update() + + if(!updating_icon) + updating_icon = 1 + // Start the update + spawn(APC_UPDATE_ICON_COOLDOWN) + update_icon() + updating_icon = 0 + +/obj/machinery/power/apc/get_spooked(second_pass = FALSE) + if(opened || panel_open) + return + if(stat & (NOPOWER | BROKEN)) + return + if(!second_pass) //The first time, we just cut overlays + addtimer(CALLBACK(src, .get_spooked, TRUE), 1) + cut_overlays() + else + flick("apcemag", src) //Second time we cause the APC to update its icon, then add a timer to update icon later + addtimer(CALLBACK(src, .proc/update_icon, TRUE), 10) + +//attack with an item - open/close cover, insert cell, or (un)lock interface +/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) + + if(issilicon(user) && get_dist(src,user)>1) + return src.attack_hand(user) + + else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside + if(cell) + to_chat(user, "There is a power cell already installed!") + return + else + if(stat & MAINT) + to_chat(user, "There is no connector for your power cell!") + return + if(!user.drop_item()) + return + W.forceMove(src) + cell = W + user.visible_message(\ + "[user.name] has inserted the power cell to [src.name]!",\ + "You insert the power cell.") + chargecount = 0 + update_icon() + + else if(W.GetID()) // trying to unlock the interface with an ID card + togglelock(user) + + else if(istype(W, /obj/item/stack/cable_coil) && opened) + var/turf/host_turf = get_turf(src) + if(!host_turf) + throw EXCEPTION("attackby on APC when it's not on a turf") + return + if(host_turf.intact) + to_chat(user, "You must remove the floor plating in front of the APC first!") + return + else if(terminal) // it already have terminal + to_chat(user, "This APC is already wired!") + return + else if(has_electronics == 0) + to_chat(user, "There is nothing to wire!") + return + + var/obj/item/stack/cable_coil/C = W + if(C.get_amount() < 10) + to_chat(user, "You need ten lengths of cable for APC!") + return + user.visible_message("[user.name] adds cables to the APC frame.", \ + "You start adding cables to the APC frame...") + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + if(do_after(user, 20, target = src)) + if(C.get_amount() < 10 || !C) + return + if(C.get_amount() >= 10 && !terminal && opened && has_electronics > 0) + var/turf/T = get_turf(src) + var/obj/structure/cable/N = T.get_cable_node() + if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) + do_sparks(5, TRUE, src) + return + C.use(10) + to_chat(user, "You add cables to the APC frame.") + make_terminal() + terminal.connect_to_network() + + else if(istype(W, /obj/item/apc_electronics) && opened) + if(has_electronics!=0) // there are already electronicks inside + to_chat(user, "You cannot put the board inside, there already is one!") + return + else if(stat & BROKEN) + to_chat(user, "You cannot put the board inside, the frame is damaged!") + return + + user.visible_message("[user.name] inserts the power control board into [src].", \ + "You start to insert the power control board into the frame...") + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + if(do_after(user, 10, target = src)) + if(has_electronics==0) + has_electronics = 1 + locked = FALSE + to_chat(user, "You place the power control board inside the frame.") + qdel(W) + + else if(istype(W, /obj/item/mounted/frame/apc_frame) && opened) + if(!(stat & BROKEN || opened==2 || obj_integrity < max_integrity)) // There is nothing to repair + to_chat(user, "You found no reason for repairing this APC") + return + if(!(stat & BROKEN) && opened==2) // Cover is the only thing broken, we do not need to remove elctronicks to replace cover + user.visible_message("[user.name] replaces missing APC's cover.",\ + "You begin to replace APC's cover...") + if(do_after(user, 20, target = src)) // replacing cover is quicker than replacing whole frame + to_chat(user, "You replace missing APC's cover.") + qdel(W) + opened = 1 + update_icon() + return + if(has_electronics) + to_chat(user, "You cannot repair this APC until you remove the electronics still inside!") + return + user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ + "You begin to replace the damaged APC frame...") + if(do_after(user, 50, target = src)) + to_chat(user, "You replace the damaged APC frame with a new one.") + qdel(W) + stat &= ~BROKEN + obj_integrity = max_integrity + if(opened==2) + opened = 1 + update_icon() + return + else + return ..() + + +/obj/machinery/power/apc/crowbar_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.tool_start_check(user, 0)) + return + if(opened) // a) on open apc + if(has_electronics==1) + if(terminal) + to_chat(user, "Disconnect the wires first!") + return + to_chat(user, "You are trying to remove the power control board..." ) + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + if(has_electronics==1) + has_electronics = 0 + if(stat & BROKEN) + user.visible_message(\ + "[user.name] has broken the power control board inside [src.name]!", + "You break the charred power control board and remove the remains.", + "You hear a crack.") + return + //SSticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 + else if(emagged) // We emag board, not APC's frame + emagged = FALSE + user.visible_message( + "[user.name] has discarded emaged power control board from [src.name]!", + "You discarded shorten board.") + return + else if(malfhack) // AI hacks board, not APC's frame + user.visible_message(\ + "[user.name] has discarded strangely programmed power control board from [src.name]!", + "You discarded strangely programmed board.") + malfai = null + malfhack = 0 + return + else + user.visible_message(\ + "[user.name] has removed the power control board from [src.name]!", + "You remove the power control board.") + new /obj/item/apc_electronics(loc) + return + else if(opened!=2) //cover isn't removed + opened = 0 + coverlocked = TRUE //closing cover relocks it + update_icon() + return + else if(!(stat & BROKEN)) // b) on closed and not broken APC + if(coverlocked && !(stat & MAINT)) // locked... + to_chat(user, "The cover is locked and cannot be opened!") + return + else if(panel_open) // wires are exposed + to_chat(user, "Exposed wires prevents you from opening it!") + return + else + opened = 1 + update_icon() + +/obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + else if(opened) + if(cell && !(stat & MAINT)) + to_chat(user, "Close the APC first!") //Less hints more mystery! + return + else + if(has_electronics==1) + has_electronics = 2 + stat &= ~MAINT + to_chat(user, "You screw the circuit electronics into place.") + else if(has_electronics==2) + has_electronics = 1 + stat |= MAINT + to_chat(user, "You unfasten the electronics.") + else + to_chat(user, "There is nothing to secure!") + return + update_icon() + else if(emagged) + to_chat(user, "The interface is broken!") + else + panel_open = !panel_open + to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") + update_icon() + + +/obj/machinery/power/apc/wirecutter_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(panel_open && !opened) + wires.Interact(user) + else if(terminal && opened) + terminal.dismantle(user, I) + +/obj/machinery/power/apc/multitool_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(panel_open && !opened) + wires.Interact(user) + +/obj/machinery/power/apc/proc/togglelock(mob/living/user) + if(emagged) + to_chat(user, "The interface is broken!") + else if(opened) + to_chat(user, "You must close the cover to swipe an ID card!") + else if(panel_open) + to_chat(user, "You must close the panel!") + else if(stat & (BROKEN|MAINT)) + to_chat(user, "Nothing happens!") + else + if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") + update_icon() + else + to_chat(user, "Access denied.") + +/obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(stat & BROKEN) + return damage_amount + . = ..() + +/obj/machinery/power/apc/obj_break(damage_flag) + if(!(flags & NODECONSTRUCT)) + set_broken() + +/obj/machinery/power/apc/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + set_broken() + if(opened != 2) + opened = 2 + coverlocked = FALSE + visible_message("The APC cover is knocked down!") + update_icon() + +/obj/machinery/power/apc/welder_act(mob/user, obj/item/I) + if(!opened || has_electronics || terminal) + return + . = TRUE + if(!I.tool_use_check(user, 3)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, amount = 3, volume = I.tool_volume)) + if((stat & BROKEN) || opened==2) + new /obj/item/stack/sheet/metal(loc) + user.visible_message(\ + "[user.name] has cut [src] apart with [I].",\ + "You disassembled the broken APC frame.") + else + new /obj/item/mounted/frame/apc_frame(loc) + user.visible_message(\ + "[user.name] has cut [src] from the wall with [I].",\ + "You cut the APC frame from the wall.") + qdel(src) + +/obj/machinery/power/apc/emag_act(user as mob) + if(!(emagged || malfhack)) // trying to unlock with an emag card + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else if(panel_open) + to_chat(user, "You must close the panel first.") + else if(stat & (BROKEN|MAINT)) + to_chat(user, "Nothing happens.") + else + flick("apc-spark", src) + emagged = 1 + locked = 0 + to_chat(user, "You emag the APC interface.") + update_icon() + +// attack with hand - remove cell (if cover open) or interact with the APC +/obj/machinery/power/apc/attack_hand(mob/user) +// if(!can_use(user)) This already gets called in interact() and in topic() +// return + if(!user) + return + src.add_fingerprint(user) + + if(usr == user && opened && (!issilicon(user))) + if(cell) + if(issilicon(user)) + cell.loc=src.loc // Drop it, whoops. + else + user.put_in_hands(cell) + cell.add_fingerprint(user) + cell.update_icon() + + src.cell = null + user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.") +// to_chat(user, "You remove the power cell.") + charging = 0 + src.update_icon() + return + if(stat & (BROKEN|MAINT)) + return + + src.interact(user) + +/obj/machinery/power/apc/attack_ghost(mob/user) + if(panel_open) + wires.Interact(user) + return ui_interact(user) + +/obj/machinery/power/apc/interact(mob/user) + if(!user) + return + + if(panel_open) + wires.Interact(user) + + return ui_interact(user) + + +/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) + if(!istype(malf)) + return FALSE + + // Only if they're a traitor OR they have the malf picker from the combat module + if(!malf.mind.has_antag_datum(/datum/antagonist/traitor) && !malf.malf_picker) + return FALSE + + if(malfai == (malf.parent || malf)) + if(occupier == malf) + return APC_MALF_SHUNTED_HERE + else if(istype(malf.loc, /obj/machinery/power/apc)) + return APC_MALF_SHUNTED_OTHER + else + return APC_MALF_HACKED + else + return APC_MALF_NOT_HACKED + +/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!user) + return + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new one + ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460) + ui.open() + // Auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + data["locked"] = is_locked(user) + data["isOperating"] = operating + data["externalPower"] = main_status + data["powerCellStatus"] = cell ? cell.percent() : null + data["chargeMode"] = chargemode + data["chargingStatus"] = charging + data["totalLoad"] = round(lastused_equip + lastused_light + lastused_environ) + data["coverLocked"] = coverlocked + data["siliconUser"] = istype(user, /mob/living/silicon) + data["siliconLock"] = locked + data["malfStatus"] = get_malf_status(user) + data["nightshiftLights"] = nightshift_lights + + var/powerChannels[0] + powerChannels[++powerChannels.len] = list( + "title" = "Equipment", + "powerLoad" = round(lastused_equip), + "status" = equipment, + "topicParams" = list( + "auto" = list("eqp" = 3), + "on" = list("eqp" = 2), + "off" = list("eqp" = 1) + ) + ) + powerChannels[++powerChannels.len] = list( + "title" = "Lighting", + "powerLoad" = round(lastused_light), + "status" = lighting, + "topicParams" = list( + "auto" = list("lgt" = 3), + "on" = list("lgt" = 2), + "off" = list("lgt" = 1) + ) + ) + powerChannels[++powerChannels.len] = list( + "title" = "Environment", + "powerLoad" = round(lastused_environ), + "status" = environ, + "topicParams" = list( + "auto" = list("env" = 3), + "on" = list("env" = 2), + "off" = list("env" = 1) + ) + ) + + data["powerChannels"] = powerChannels + + return data + +/obj/machinery/power/apc/proc/report() + return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" + +/obj/machinery/power/apc/proc/update() + if(operating && !shorted) + area.power_light = (lighting > 1) + area.power_equip = (equipment > 1) + area.power_environ = (environ > 1) +// if(area.name == "AI Chamber") +// spawn(10) +// to_chat(world, " [area.name] [area.power_equip]") + else + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 +// if(area.name == "AI Chamber") +// to_chat(world, "[area.power_equip]") + area.power_change() + +/obj/machinery/power/apc/proc/isWireCut(var/wireIndex) + return wires.IsIndexCut(wireIndex) + + +/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic() + if(user.can_admin_interact()) + return 1 + + autoflag = 5 + if(istype(user, /mob/living/silicon)) + var/mob/living/silicon/ai/AI = user + var/mob/living/silicon/robot/robot = user + if( \ + src.aidisabled || \ + malfhack && istype(malfai) && \ + ( \ + (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ + (istype(robot) && (robot in malfai.connected_robots)) \ + ) \ + ) + if(!loud) + to_chat(user, "\The [src] has AI control disabled!") + user << browse(null, "window=apc") + user.unset_machine() + return 0 + else + if((!in_range(src, user) || !istype(src.loc, /turf))) + return 0 + + var/mob/living/carbon/human/H = user + if(istype(H)) + if(H.getBrainLoss() >= 60) + for(var/mob/M in viewers(src, null)) + to_chat(M, "[H] stares cluelessly at [src] and drools.") + return 0 + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return 0 + return 1 + +/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob) + if(user.can_admin_interact()) + return 1 + if(isAI(user) || isrobot(user)) + return 1 + else + return !locked + +/obj/machinery/power/apc/proc/is_locked(mob/user as mob) + if(user.can_admin_interact()) + return 0 + if(isAI(user) || isrobot(user)) + return 0 + else + return locked + +/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) + if(..()) + return 1 + + if(!can_use(usr, 1)) + return 1 + + if(href_list["lock"]) + if(!is_authenticated(usr)) + return + + coverlocked = !coverlocked + + else if(href_list["breaker"]) + if(!is_authenticated(usr)) + return + + toggle_breaker() + + else if(href_list["toggle_nightshift"]) + if(!is_authenticated(usr)) + return + + if(last_nightshift_switch > world.time + 100) // don't spam... + to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") + return + last_nightshift_switch = world.time + set_nightshift(!nightshift_lights) + + else if(href_list["cmode"]) + if(!is_authenticated(usr)) + return + + chargemode = !chargemode + if(!chargemode) + charging = 0 + update_icon() + + else if(href_list["eqp"]) + if(!is_authenticated(usr)) + return + + var/val = text2num(href_list["eqp"]) + equipment = setsubsystem(val) + update_icon() + update() + + else if(href_list["lgt"]) + if(!is_authenticated(usr)) + return + + var/val = text2num(href_list["lgt"]) + lighting = setsubsystem(val) + update_icon() + update() + + else if(href_list["env"]) + if(!is_authenticated(usr)) + return + + var/val = text2num(href_list["env"]) + environ = setsubsystem(val) + update_icon() + update() + else if( href_list["close"] ) + SSnanoui.close_user_uis(usr, src) + + return 0 + else if(href_list["close2"]) + usr << browse(null, "window=apcwires") + + return 0 + + else if(href_list["overload"]) + if(issilicon(usr) && !aidisabled) + overload_lighting() + + else if(href_list["malfhack"]) + if(get_malf_status(usr)) + malfhack(usr) + + else if(href_list["occupyapc"]) + if(get_malf_status(usr)) + malfoccupy(usr) + + else if(href_list["deoccupyapc"]) + if(get_malf_status(usr)) + malfvacate() + + else if(href_list["toggleaccess"]) + if(istype(usr, /mob/living/silicon)) + if(emagged || aidisabled || (stat & (BROKEN|MAINT))) + to_chat(usr, "The APC does not respond to the command.") + else + locked = !locked + update_icon() + + return 0 + +/obj/machinery/power/apc/proc/toggle_breaker() + operating = !operating + update() + update_icon() + +/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf) + if(!istype(malf)) + return + if(get_malf_status(malf) != APC_MALF_NOT_HACKED) + return + if(malf.malfhacking) + to_chat(malf, "You are already hacking an APC.") + return + to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") + malf.malfhack = src + malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) + var/obj/screen/alert/hackingapc/A + A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) + A.target = src + +/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) + if(!istype(malf)) + return + if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC + to_chat(malf, "You must evacuate your current APC first!") + return + if(!malf.can_shunt) + to_chat(malf, "You cannot shunt!") + return + if(!is_station_level(src.z)) + return + occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) + occupier.adjustOxyLoss(malf.getOxyLoss()) + if(!findtext(occupier.name, "APC Copy")) + occupier.name = "[malf.name] APC Copy" + if(malf.parent) + occupier.parent = malf.parent + else + occupier.parent = malf + malf.shunted = 1 + malf.mind.transfer_to(occupier) + occupier.eyeobj.name = "[occupier.name] (AI Eye)" + if(malf.parent) + qdel(malf) + occupier.verbs += /mob/living/silicon/ai/proc/corereturn + occupier.cancel_camera() + if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking) + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + point.the_disk = src //the pinpointer will detect the shunted AI + +/obj/machinery/power/apc/proc/malfvacate(forced) + if(!occupier) + return + if(occupier.parent && occupier.parent.stat != DEAD) + occupier.mind.transfer_to(occupier.parent) + occupier.parent.shunted = 0 + occupier.parent.adjustOxyLoss(occupier.getOxyLoss()) + occupier.parent.cancel_camera() + qdel(occupier) + if(seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + for(var/mob/living/silicon/ai/A in GLOB.ai_list) + if((A.stat != DEAD) && A.nuking) + point.the_disk = A //The pinpointer tracks the AI back into its core. + else + to_chat(occupier, "Primary core damaged, unable to return core processes.") + if(forced) + occupier.loc = loc + occupier.death() + occupier.gib() + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + point.the_disk = null //Pinpointers go back to tracking the nuke disk + +/obj/machinery/power/apc/proc/ion_act() + //intended to be exactly the same as an AI malf attack + if(!src.malfhack && is_station_level(src.z)) + if(prob(3)) + src.locked = 1 + if(src.cell.charge > 0) + src.cell.charge = 0 + cell.corrupt() + src.malfhack = 1 + update_icon() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + do_sparks(3, 1, src) + for(var/mob/M in viewers(src)) + M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) + + +/obj/machinery/power/apc/surplus() + if(terminal) + return terminal.surplus() + else + return 0 + +/obj/machinery/power/apc/add_load(amount) + if(terminal && terminal.powernet) + terminal.add_load(amount) + +/obj/machinery/power/apc/avail() + if(terminal) + return terminal.avail() + else + return 0 + +/obj/machinery/power/apc/process() + if(stat & (BROKEN|MAINT)) + return + if(!area.requires_power) + return + + lastused_light = area.usage(STATIC_LIGHT) + lastused_light += area.usage(LIGHT) + lastused_equip = area.usage(EQUIP) + lastused_equip += area.usage(STATIC_EQUIP) + lastused_environ = area.usage(ENVIRON) + lastused_environ += area.usage(STATIC_ENVIRON) + area.clear_usage() + + lastused_total = lastused_light + lastused_equip + lastused_environ + + //store states to update icon if any change + var/last_lt = lighting + var/last_eq = equipment + var/last_en = environ + var/last_ch = charging + + var/excess = surplus() + + if(!src.avail()) + main_status = 0 + else if(excess < 0) + main_status = 1 + else + main_status = 2 + + if(debug) + log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]") + + if(cell && !shorted) + // draw power from cell as before to power the area + var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell + cell.use(cellused) + + if(excess > lastused_total) // if power excess recharge the cell + // by the same amount just used + cell.give(cellused) + add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell + + + else // no excess, and not enough per-apc + if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? + cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can + add_load(excess) // so draw what we can from the grid + charging = 0 + + else // not enough power available to run the last tick! + charging = 0 + chargecount = 0 + // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + autoflag = 0 + + + // Set channels depending on how much charge we have left + + // Allow the APC to operate as normal if the cell can charge + if(charging && longtermpower < 10) + longtermpower += 1 + else if(longtermpower > -10) + longtermpower -= 2 + + if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101 + if(autoflag != 3) + equipment = autoset(equipment, 1) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + autoflag = 3 + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.clearAlarm(loc, src) + else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment + if(autoflag != 2) + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 2 + else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment + if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 2) + environ = autoset(environ, 1) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 1 + else if(cell.charge <= 0) // zero charge, turn all off + if(autoflag != 0) + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 0 + + // now trickle-charge the cell + if(chargemode && charging == 1 && operating) + if(excess > 0) // check to make sure we have enough to charge + // Max charge is capped to % per second constant + var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL) + add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid + cell.give(ch) // actually recharge the cell + + else + charging = 0 // stop charging + chargecount = 0 + + // show cell as fully charged if so + if(cell.charge >= cell.maxcharge) + cell.charge = cell.maxcharge + charging = 2 + + if(chargemode) + if(!charging) + if(excess > cell.maxcharge*GLOB.CHARGELEVEL) + chargecount++ + else + chargecount = 0 + + if(chargecount == 10) + + chargecount = 0 + charging = 1 + + else // chargemode off + charging = 0 + chargecount = 0 + + if(excess >= 5000000 && !shock_proof) //If there's more than 5,000,000 watts in the grid, start arcing and shocking people. + if(prob(5)) + var/list/shock_mobs = list() + for(var/C in view(get_turf(src), 5)) //We only want to shock a single random mob in range, not every one. + if(isliving(C)) + shock_mobs += C + if(shock_mobs.len) + var/mob/living/L = pick(shock_mobs) + L.electrocute_act(rand(5, 25), "electrical arc") + playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1) + Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) + + else // no cell, switch everything off + + charging = 0 + chargecount = 0 + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 0 + + // update icon & area power if anything changed + + if(last_lt != lighting || last_eq != equipment || last_en != environ) + queue_icon_update() + update() + else if(last_ch != charging) + queue_icon_update() + +/obj/machinery/power/apc/proc/autoset(var/val, var/on) + if(on==0) + if(val==2) // if on, return off + return 0 + else if(val==3) // if auto-on, return auto-off + return 1 + + else if(on==1) + if(val==1) // if auto-off, return auto-on + return 3 + + else if(on==2) + if(val==3) // if auto-on, return auto-off + return 1 + + return val + +// damage and destruction acts + +/obj/machinery/power/apc/emp_act(severity) + if(cell) + cell.emp_act(severity) + if(occupier) + occupier.emp_act(severity) + lighting = 0 + equipment = 0 + environ = 0 + update_icon() + update() + spawn(600) + equipment = 3 + environ = 3 + update_icon() + update() + ..() + +/obj/machinery/power/apc/blob_act(obj/structure/blob/B) + set_broken() + +/obj/machinery/power/apc/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + +/obj/machinery/power/apc/proc/set_broken() + if(malfai && operating) + malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) + stat |= BROKEN + operating = 0 + if(occupier) + malfvacate(1) + update_icon() + update() + +// overload all the lights in this APC area + +/obj/machinery/power/apc/proc/overload_lighting(chance = 100) + if(!operating || shorted) + return + if(cell && cell.charge >= 20) + cell.use(20) + spawn(0) + for(var/obj/machinery/light/L in area) + if(prob(chance)) + L.break_light_tube(0, 1) + stoplag() + +/obj/machinery/power/apc/proc/null_charge() + for(var/obj/machinery/light/L in area) + L.break_light_tube(0, 1) + stoplag() + +/obj/machinery/power/apc/proc/setsubsystem(val) + if(cell && cell.charge > 0) + return (val==1) ? 0 : val + else if(val == 3) + return 1 + else + return 0 + +/obj/machinery/power/apc/proc/set_nightshift(on) + set waitfor = FALSE + nightshift_lights = on + for(var/obj/machinery/light/L in area) + if(L.nightshift_allowed) + L.nightshift_enabled = nightshift_lights + L.update(FALSE) + CHECK_TICK + +#undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 9f48d451f36a..7260c836d78c 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -1,867 +1,867 @@ -#define HEALPERCABLE 3 -#define MAXCABLEPERHEAL 8 -/////////////////////////////// -//CABLE STRUCTURE -/////////////////////////////// - - -//////////////////////////////// -// Definitions -//////////////////////////////// - -/* Cable directions (d1 and d2) - - - 9 1 5 - \ | / - 8 - 0 - 4 - / | \ - 10 2 6 - -If d1 = 0 and d2 = 0, there's no cable -If d1 = 0 and d2 = dir, it's a O-X cable, getting from the center of the tile to dir (knot cable) -If d1 = dir1 and d2 = dir2, it's a full X-X cable, getting from dir1 to dir2 -By design, d1 is the smallest direction and d2 is the highest -*/ - -/obj/structure/cable - level = 1 - anchored = 1 - on_blueprints = TRUE - var/datum/powernet/powernet - name = "power cable" - desc = "A flexible superconducting cable for heavy-duty power transfer" - icon = 'icons/obj/power_cond/power_cond_white.dmi' - icon_state = "0-1" - var/d1 = 0 - var/d2 = 1 - layer = WIRE_LAYER //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4 - color = COLOR_RED - -/obj/structure/cable/yellow - color = COLOR_YELLOW - -/obj/structure/cable/green - color = COLOR_GREEN - -/obj/structure/cable/blue - color = COLOR_BLUE - -/obj/structure/cable/pink - color = COLOR_PINK - -/obj/structure/cable/orange - color = COLOR_ORANGE - -/obj/structure/cable/cyan - color = COLOR_CYAN - -/obj/structure/cable/white - color = COLOR_WHITE - -/obj/structure/cable/Initialize(mapload) - . = ..() - - // ensure d1 & d2 reflect the icon_state for entering and exiting cable - var/dash = findtext(icon_state, "-") - d1 = text2num(copytext( icon_state, 1, dash )) - d2 = text2num(copytext( icon_state, dash+1 )) - - var/turf/T = get_turf(src) // hide if turf is not intact - if(level == 1) - hide(T.intact) - LAZYADD(GLOB.cable_list, src) //add it to the global cable list - -/obj/structure/cable/Destroy() // called when a cable is deleted - if(powernet) - cut_cable_from_powernet() // update the powernets - LAZYREMOVE(GLOB.cable_list, src) //remove it from global cable list - return ..() // then go ahead and delete the cable - -/obj/structure/cable/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/turf/T = get_turf(src) - if(d1) // 0-X cables are 1 unit, X-X cables are 2 units long - new/obj/item/stack/cable_coil(T, 2, paramcolor = color) - else - new/obj/item/stack/cable_coil(T, 1, paramcolor = color) - qdel(src) - -/////////////////////////////////// -// General procedures -/////////////////////////////////// - -//If underfloor, hide the cable -/obj/structure/cable/hide(i) - - if(level == 1 && isturf(loc)) - invisibility = i ? 101 : 0 - updateicon() - -/obj/structure/cable/proc/updateicon() - if(invisibility) - icon_state = "[d1]-[d2]-f" - else - icon_state = "[d1]-[d2]" - - -//////////////////////////////////////////// -// Power related -/////////////////////////////////////////// - -// All power generation handled in add_avail() -// Machines should use add_load(), surplus(), avail() -// Non-machines should use add_delayedload(), delayed_surplus(), newavail() - -/obj/structure/cable/proc/add_avail(amount) - if(powernet) - powernet.newavail += amount - -/obj/structure/cable/proc/add_load(amount) - if(powernet) - powernet.load += amount - -/obj/structure/cable/proc/surplus() - if(powernet) - return Clamp(powernet.avail-powernet.load, 0, powernet.avail) - else - return 0 - -/obj/structure/cable/proc/avail() - if(powernet) - return powernet.avail - else - return 0 - -/obj/structure/cable/proc/add_delayedload(amount) - if(powernet) - powernet.delayedload += amount - -/obj/structure/cable/proc/delayed_surplus() - if(powernet) - return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) - else - return 0 - -/obj/structure/cable/proc/newavail() - if(powernet) - return powernet.newavail - else - return 0 - -//Telekinesis has no effect on a cable -/obj/structure/cable/attack_tk(mob/user) - return - -// Items usable on a cable : -// - Wirecutters : cut it duh ! -// - Cable coil : merge cables -// - Multitool : get the power currently passing through the cable -// -/obj/structure/cable/attackby(obj/item/W, mob/user) - var/turf/T = get_turf(src) - if(T.intact) - return - - else if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = W - if(coil.get_amount() < 1) - to_chat(user, "Not enough cable!") - return - coil.cable_join(src, user) - - else if(istype(W, /obj/item/twohanded/rcl)) - var/obj/item/twohanded/rcl/R = W - if(R.loaded) - R.loaded.cable_join(src, user) - R.is_empty(user) - - else if(istype(W, /obj/item/toy/crayon)) - var/obj/item/toy/crayon/C = W - cable_color(C.colourName) - - else - if(W.flags & CONDUCT) - shock(user, 50, 0.7) - - add_fingerprint(user) - -/obj/structure/cable/multitool_act(mob/user, obj/item/I) - . = TRUE - var/turf/T = get_turf(src) - if(T.intact) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(powernet && (powernet.avail > 0)) // is it powered? - to_chat(user, "Total power: [DisplayPower(powernet.avail)]\nLoad: [DisplayPower(powernet.load)]\nExcess power: [DisplayPower(surplus())]") - else - to_chat(user, "The cable is not powered.") - shock(user, 5, 0.2) - -/obj/structure/cable/wirecutter_act(mob/user, obj/item/I) - . = TRUE - var/turf/T = get_turf(src) - if(T.intact) - return - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(shock(user, 50)) - return - user.visible_message("[user] cuts the cable.", "You cut the cable.") - investigate_log("was cut by [key_name(usr, 1)] in [get_area(user)]([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])","wires") - deconstruct() - -// shock the user with probability prb -/obj/structure/cable/proc/shock(mob/user, prb, siemens_coeff = 1) - if(!prob(prb)) - return FALSE - if(electrocute_mob(user, powernet, src, siemens_coeff)) - do_sparks(5, 1, src) - return TRUE - else - return FALSE - -/obj/structure/cable/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -obj/structure/cable/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - color = color_rainbow() - else - color = colorC - else - color = "#DD0000" - -/obj/structure/cable/proc/color_rainbow() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - return color - -///////////////////////////////////////////////// -// Cable laying helpers -//////////////////////////////////////////////// - -//handles merging diagonally matching cables -//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically -/obj/structure/cable/proc/mergeDiagonalsNetworks(direction) - - //search for and merge diagonally matching cables from the first direction component (north/south) - var/turf/T = get_step(src, direction&3)//go north/south - - for(var/obj/structure/cable/C in T) - - if(!C) - continue - - if(src == C) - continue - - if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - - //the same from the second direction component (east/west) - T = get_step(src, direction&12)//go east/west - - for(var/obj/structure/cable/C in T) - - if(!C) - continue - - if(src == C) - continue - if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - -// merge with the powernets of power objects in the given direction -/obj/structure/cable/proc/mergeConnectedNetworks(direction) - - var/fdir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf - - if(!(d1 == direction || d2 == direction)) //if the cable is not pointed in this direction, do nothing - return - - var/turf/TB = get_step(src, direction) - - for(var/obj/structure/cable/C in TB) - - if(!C) - continue - - if(src == C) - continue - - if(C.d1 == fdir || C.d2 == fdir) //we've got a matching cable in the neighbor turf - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - -// merge with the powernets of power objects in the source turf -/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() - var/list/to_connect = list() - - if(!powernet) //if we somehow have no powernet, make one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(src) - - //first let's add turf cables to our powernet - //then we'll connect machines on turf with a node cable is present - for(var/AM in loc) - if(istype(AM, /obj/structure/cable)) - var/obj/structure/cable/C = AM - if(C.d1 == d1 || C.d2 == d1 || C.d1 == d2 || C.d2 == d2) //only connected if they have a common direction - if(C.powernet == powernet) - continue - if(C.powernet) - merge_powernets(powernet, C.powernet) - else - powernet.add_cable(C) //the cable was powernetless, let's just add it to our powernet - - else if(istype(AM, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/N = AM - if(!N.terminal) - continue // APC are connected through their terminal - - if(N.terminal.powernet == powernet) - continue - - to_connect += N.terminal //we'll connect the machines after all cables are merged - - else if(istype(AM, /obj/machinery/power)) //other power machines - var/obj/machinery/power/M = AM - - if(M.powernet == powernet) - continue - - to_connect += M //we'll connect the machines after all cables are merged - - //now that cables are done, let's connect found machines - for(var/obj/machinery/power/PM in to_connect) - if(!PM.connect_to_network()) - PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless - -////////////////////////////////////////////// -// Powernets handling helpers -////////////////////////////////////////////// - -//if powernetless_only = 1, will only get connections without powernet -/obj/structure/cable/proc/get_connections(powernetless_only = 0) - . = list() // this will be a list of all connected power objects - var/turf/T - - //get matching cables from the first direction - if(d1) //if not a node cable - T = get_step(src, d1) - if(T) - . += power_list(T, src, turn(d1, 180), powernetless_only) //get adjacents matching cables - - if(d1&(d1-1)) //diagonal direction, must check the 4 possibles adjacents tiles - T = get_step(src,d1&3) // go north/south - if(T) - . += power_list(T, src, d1 ^ 3, powernetless_only) //get diagonally matching cables - T = get_step(src,d1&12) // go east/west - if(T) - . += power_list(T, src, d1 ^ 12, powernetless_only) //get diagonally matching cables - - . += power_list(loc, src, d1, powernetless_only) //get on turf matching cables - - //do the same on the second direction (which can't be 0) - T = get_step(src, d2) - if(T) - . += power_list(T, src, turn(d2, 180), powernetless_only) //get adjacents matching cables - - if(d2&(d2-1)) //diagonal direction, must check the 4 possibles adjacents tiles - T = get_step(src,d2&3) // go north/south - if(T) - . += power_list(T, src, d2 ^ 3, powernetless_only) //get diagonally matching cables - T = get_step(src,d2&12) // go east/west - if(T) - . += power_list(T, src, d2 ^ 12, powernetless_only) //get diagonally matching cables - . += power_list(loc, src, d2, powernetless_only) //get on turf matching cables - - return . - -//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. -//needed as this can, unlike other placements, disconnect cables -/obj/structure/cable/proc/denode() - var/turf/T1 = loc - if(!T1) - return - - var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet - if(powerlist.len>0) - var/datum/powernet/PN = new() - propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable - - if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables - qdel(PN) - -/obj/structure/cable/proc/auto_propogate_cut_cable(obj/O) - if(O && !QDELETED(O)) - var/datum/powernet/newPN = new()// creates a new powernet... - propagate_network(O, newPN)//... and propagates it to the other side of the cable - -// cut the cable's powernet at this cable and updates the powergrid -/obj/structure/cable/proc/cut_cable_from_powernet(remove=TRUE) - var/turf/T1 = loc - var/list/P_list - if(!T1) - return - if(d1) - T1 = get_step(T1, d1) - P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... - - P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf - - - if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet - powernet.remove_cable(src) - - for(var/obj/machinery/power/P in T1)//check if it was powering a machine - if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to - P.disconnect_from_network() //remove from current network (and delete powernet) - return - - var/obj/O = P_list[1] - // remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist - if(remove) - loc = null - powernet.remove_cable(src) //remove the cut cable from its powernet - - // queue it to rebuild - SSmachines.deferred_powernet_rebuilds += O -// addtimer(CALLBACK(O, .proc/auto_propogate_cut_cable, O), 0) //so we don't rebuild the network X times when singulo/explosion destroys a line of X cables - - // Disconnect machines connected to nodes - if(d1 == 0) // if we cut a node (O-X) cable - for(var/obj/machinery/power/P in T1) - if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to - P.disconnect_from_network() //remove from current network - - -/////////////////////////////////////////////// -// The cable coil object, used for laying cable -/////////////////////////////////////////////// - -//////////////////////////////// -// Definitions -//////////////////////////////// - -GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15))) - -/obj/item/stack/cable_coil - name = "cable coil" - singular_name = "cable" - icon = 'icons/obj/power.dmi' - icon_state = "coil" - item_state = "coil_red" - amount = MAXCOIL - max_amount = MAXCOIL - merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves - color = COLOR_RED - desc = "A coil of power cable." - throwforce = 10 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 2 - throw_range = 5 - materials = list(MAT_METAL=10, MAT_GLASS=5) - flags = CONDUCT - slot_flags = SLOT_BELT - item_state = "coil" - attack_verb = list("whipped", "lashed", "disciplined", "flogged") - usesound = 'sound/items/deconstruct.ogg' - toolspeed = 1 - -/obj/item/stack/cable_coil/suicide_act(mob/user) - if(locate(/obj/structure/chair/stool) in user.loc) - user.visible_message("[user] is making a noose with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") - else - user.visible_message("[user] is strangling [user.p_them()]self with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") - return OXYLOSS - -/obj/item/stack/cable_coil/New(loc, length = MAXCOIL, paramcolor = null) - ..() - amount = length - if(paramcolor) - color = paramcolor - pixel_x = rand(-2,2) - pixel_y = rand(-2,2) - update_icon() - recipes = GLOB.cable_coil_recipes - update_wclass() - -/////////////////////////////////// -// General procedures -/////////////////////////////////// -//you can use wires to heal robotics -/obj/item/stack/cable_coil/attack(mob/M, mob/user) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected] - - if(!S) - return - if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2) - return ..() - - if(S.burn_dam > ROBOLIMB_SELF_REPAIR_CAP) - to_chat(user, "The damage is far too severe to patch over externally.") - return - - if(!S.burn_dam) - to_chat(user, "Nothing to fix!") - return - - if(H == user) - if(!do_mob(user, H, 10)) - return 0 - var/cable_used = 0 - var/childlist - if(!isnull(S.children)) - childlist = S.children.Copy() - var/parenthealed = FALSE - while(cable_used <= MAXCABLEPERHEAL && amount >= 1) - var/obj/item/organ/external/E - if(S.burn_dam) - E = S - else if(LAZYLEN(childlist)) - E = pick_n_take(childlist) - if(!E.burn_dam || !E.is_robotic()) - continue - else if(S.parent && !parenthealed) - E = S.parent - parenthealed = TRUE - if(!E.burn_dam || !E.is_robotic()) - break - else - break - while(cable_used <= MAXCABLEPERHEAL && E.burn_dam && amount >= 1) - use(1) - cable_used += 1 - E.heal_damage(0, HEALPERCABLE, 0, 1) - user.visible_message("\The [user] repairs some burn damage on \the [M]'s [E.name] with \the [src].") - return 1 - - else - return ..() - -/obj/item/stack/cable_coil/split() - var/obj/item/stack/cable_coil/C = ..() - C.color = color - return C - -/obj/item/stack/cable_coil/update_icon() - if(!color) - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - if(amount == 1) - icon_state = "coil1" - name = "cable piece" - else if(amount == 2) - icon_state = "coil2" - name = "cable piece" - else - icon_state = "coil" - name = "cable coil" - ..() - -/obj/item/stack/cable_coil/proc/update_wclass() - if(amount == 1) - w_class = WEIGHT_CLASS_TINY - else - w_class = WEIGHT_CLASS_SMALL - -/obj/item/stack/cable_coil/examine(mob/user) - . = ..() - if(in_range(user, src)) - if(get_amount() == 1) - . += "A short piece of power cable." - else if(get_amount() == 2) - . += "A piece of power cable." - else - . += "A coil of power cable. There are [get_amount()] lengths of cable in the coil." - -// Items usable on a cable coil : -// - Wirecutters : cut them duh ! -// - Cable coil : merge cables -/obj/item/stack/cable_coil/attackby(obj/item/W, mob/user) - ..() - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - // Cable merging is handled by parent proc - if(C.amount >= MAXCOIL) - to_chat(user, "The coil is as long as it will get.") - return - if( (C.amount + src.amount <= MAXCOIL) ) - to_chat(user, "You join the cable coils together.") - return - else - to_chat(user, "You transfer [get_amount_transferred()] length\s of cable from one coil to the other.") - return - - if(istype(W, /obj/item/toy/crayon)) - var/obj/item/toy/crayon/C = W - cable_color(C.colourName) - -/////////////////////////////////////////////// -// Cable laying procedures -////////////////////////////////////////////// - -/obj/item/stack/cable_coil/proc/get_new_cable(location) - var/obj/structure/cable/C = new(location) - C.cable_color(color) - - return C - -// called when cable_coil is clicked on a turf/simulated/floor -/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew) - if(!isturf(user.loc)) - return - - if(!isturf(T) || T.intact || !T.can_have_cabling()) - to_chat(user, "You can only lay cables on catwalks and plating!") - return - - if(get_amount() < 1) // Out of cable - to_chat(user, "There is no cable left!") - return - - if(get_dist(T,user) > 1) // Too far - to_chat(user, "You can't lay cable at a place that far away!") - return - - var/dirn - if(!dirnew) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm) - if(user.loc == T) - dirn = user.dir //If laying on the tile we're on, lay in the direction we're facing - else - dirn = get_dir(T, user) - else - dirn = dirnew - - for(var/obj/structure/cable/LC in T) - if(LC.d2 == dirn && LC.d1 == 0) - to_chat(user, "There's already a cable at that position!") - return - - var/obj/structure/cable/C = get_new_cable(T) - - //set up the new cable - C.d1 = 0 //it's a O-X node cable - C.d2 = dirn - C.add_fingerprint(user) - C.updateicon() - - //create a new powernet with the cable, if needed it will be merged later - var/datum/powernet/PN = new() - PN.add_cable(C) - - C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets - C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets - - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) - - use(1) - - if(C.shock(user, 50)) - if(prob(50)) //fail - new /obj/item/stack/cable_coil(get_turf(C), 1, paramcolor = C.color) - C.deconstruct() - - return C - -// called when cable_coil is click on an installed obj/cable -// or click on a turf that already contains a "node" cable -/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) - var/turf/U = user.loc - if(!isturf(U)) - return - - var/turf/T = get_turf(C) - - if(!isturf(T) || T.intact) // sanity checks, also stop use interacting with T-scanner revealed cable - return - - if(get_dist(C, user) > 1) // make sure it's close enough - to_chat(user, "You can't lay cable at a place that far away!") - return - - - if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing - place_turf(T,user) - return - - var/dirn = get_dir(C, user) - - // one end of the clicked cable is pointing towards us - if(C.d1 == dirn || C.d2 == dirn) - if(U.intact) // can't place a cable if the floor is complete - to_chat(user, "You can't lay cable there unless the floor tiles are removed!") - return - else - // cable is pointing at us, we're standing on an open tile - // so create a stub pointing at the clicked cable on our tile - - var/fdirn = turn(dirn, 180) // the opposite direction - - for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already - if(LC.d1 == fdirn || LC.d2 == fdirn) - to_chat(user, "There's already a cable at that position!") - return - - var/obj/structure/cable/NC = get_new_cable (U) - - NC.d1 = 0 - NC.d2 = fdirn - NC.add_fingerprint(user) - NC.update_icon() - - //create a new powernet with the cable, if needed it will be merged later - var/datum/powernet/newPN = new() - newPN.add_cable(NC) - - NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets - NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets - - if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - NC.mergeDiagonalsNetworks(NC.d2) - - use(1) - - if(NC.shock(user, 50)) - if(prob(50)) //fail - NC.deconstruct() - return - - // exisiting cable doesn't point at our position, so see if it's a stub - else if(C.d1 == 0) - // if so, make it a full cable pointing from it's old direction to our dirn - var/nd1 = C.d2 // these will be the new directions - var/nd2 = dirn - - - if(nd1 > nd2) // swap directions to match icons/states - nd1 = dirn - nd2 = C.d2 - - - for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable - if(LC == C) // skip the cable we're interacting with - continue - if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction - to_chat(user, "There's already a cable at that position!") - return - - - C.cable_color(color) - - C.d1 = nd1 - C.d2 = nd2 - - C.add_fingerprint() - C.updateicon() - - - C.mergeConnectedNetworks(C.d1) //merge the powernets... - C.mergeConnectedNetworks(C.d2) //...in the two new cable directions - C.mergeConnectedNetworksOnTurf() - - if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d1) - - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) - - use(1) - - if(C.shock(user, 50)) - if(prob(50)) //fail - C.deconstruct() - return - - C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. - return - -////////////////////////////// -// Misc. -///////////////////////////// - -/obj/item/stack/cable_coil/cut - item_state = "coil2" - -/obj/item/stack/cable_coil/cut/Initialize(mapload) - . = ..() - src.amount = rand(1,2) - pixel_x = rand(-2,2) - pixel_y = rand(-2,2) - update_icon() - update_wclass() - -/obj/item/stack/cable_coil/yellow - color = COLOR_YELLOW - -/obj/item/stack/cable_coil/blue - color = COLOR_BLUE - -/obj/item/stack/cable_coil/green - color = COLOR_GREEN - -/obj/item/stack/cable_coil/pink - color = COLOR_PINK - -/obj/item/stack/cable_coil/orange - color = COLOR_ORANGE - -/obj/item/stack/cable_coil/cyan - color = COLOR_CYAN - -/obj/item/stack/cable_coil/white - color = COLOR_WHITE - -/obj/item/stack/cable_coil/random/New() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - ..() - -/obj/item/stack/cable_coil/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - colorC = color_rainbow() - color = colorC - else - color = COLOR_RED - -/obj/item/stack/cable_coil/proc/color_rainbow() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - return color - -/obj/item/stack/cable_coil/cyborg - name = "cyborg cable coil" - -/obj/item/stack/cable_coil/cyborg/attack_self(mob/user) - var/cablecolor = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") - color = cablecolor - update_icon() - -#undef MAXCABLEPERHEAL -#undef HEALPERCABLE +#define HEALPERCABLE 3 +#define MAXCABLEPERHEAL 8 +/////////////////////////////// +//CABLE STRUCTURE +/////////////////////////////// + + +//////////////////////////////// +// Definitions +//////////////////////////////// + +/* Cable directions (d1 and d2) + + + 9 1 5 + \ | / + 8 - 0 - 4 + / | \ + 10 2 6 + +If d1 = 0 and d2 = 0, there's no cable +If d1 = 0 and d2 = dir, it's a O-X cable, getting from the center of the tile to dir (knot cable) +If d1 = dir1 and d2 = dir2, it's a full X-X cable, getting from dir1 to dir2 +By design, d1 is the smallest direction and d2 is the highest +*/ + +/obj/structure/cable + level = 1 + anchored = 1 + on_blueprints = TRUE + var/datum/powernet/powernet + name = "power cable" + desc = "A flexible superconducting cable for heavy-duty power transfer" + icon = 'icons/obj/power_cond/power_cond_white.dmi' + icon_state = "0-1" + var/d1 = 0 + var/d2 = 1 + layer = WIRE_LAYER //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4 + color = COLOR_RED + +/obj/structure/cable/yellow + color = COLOR_YELLOW + +/obj/structure/cable/green + color = COLOR_GREEN + +/obj/structure/cable/blue + color = COLOR_BLUE + +/obj/structure/cable/pink + color = COLOR_PINK + +/obj/structure/cable/orange + color = COLOR_ORANGE + +/obj/structure/cable/cyan + color = COLOR_CYAN + +/obj/structure/cable/white + color = COLOR_WHITE + +/obj/structure/cable/Initialize(mapload) + . = ..() + + // ensure d1 & d2 reflect the icon_state for entering and exiting cable + var/dash = findtext(icon_state, "-") + d1 = text2num(copytext( icon_state, 1, dash )) + d2 = text2num(copytext( icon_state, dash+1 )) + + var/turf/T = get_turf(src) // hide if turf is not intact + if(level == 1) + hide(T.intact) + LAZYADD(GLOB.cable_list, src) //add it to the global cable list + +/obj/structure/cable/Destroy() // called when a cable is deleted + if(powernet) + cut_cable_from_powernet() // update the powernets + LAZYREMOVE(GLOB.cable_list, src) //remove it from global cable list + return ..() // then go ahead and delete the cable + +/obj/structure/cable/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/turf/T = get_turf(src) + if(d1) // 0-X cables are 1 unit, X-X cables are 2 units long + new/obj/item/stack/cable_coil(T, 2, paramcolor = color) + else + new/obj/item/stack/cable_coil(T, 1, paramcolor = color) + qdel(src) + +/////////////////////////////////// +// General procedures +/////////////////////////////////// + +//If underfloor, hide the cable +/obj/structure/cable/hide(i) + + if(level == 1 && isturf(loc)) + invisibility = i ? 101 : 0 + updateicon() + +/obj/structure/cable/proc/updateicon() + if(invisibility) + icon_state = "[d1]-[d2]-f" + else + icon_state = "[d1]-[d2]" + + +//////////////////////////////////////////// +// Power related +/////////////////////////////////////////// + +// All power generation handled in add_avail() +// Machines should use add_load(), surplus(), avail() +// Non-machines should use add_delayedload(), delayed_surplus(), newavail() + +/obj/structure/cable/proc/add_avail(amount) + if(powernet) + powernet.newavail += amount + +/obj/structure/cable/proc/add_load(amount) + if(powernet) + powernet.load += amount + +/obj/structure/cable/proc/surplus() + if(powernet) + return Clamp(powernet.avail-powernet.load, 0, powernet.avail) + else + return 0 + +/obj/structure/cable/proc/avail() + if(powernet) + return powernet.avail + else + return 0 + +/obj/structure/cable/proc/add_delayedload(amount) + if(powernet) + powernet.delayedload += amount + +/obj/structure/cable/proc/delayed_surplus() + if(powernet) + return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) + else + return 0 + +/obj/structure/cable/proc/newavail() + if(powernet) + return powernet.newavail + else + return 0 + +//Telekinesis has no effect on a cable +/obj/structure/cable/attack_tk(mob/user) + return + +// Items usable on a cable : +// - Wirecutters : cut it duh ! +// - Cable coil : merge cables +// - Multitool : get the power currently passing through the cable +// +/obj/structure/cable/attackby(obj/item/W, mob/user) + var/turf/T = get_turf(src) + if(T.intact) + return + + else if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = W + if(coil.get_amount() < 1) + to_chat(user, "Not enough cable!") + return + coil.cable_join(src, user) + + else if(istype(W, /obj/item/twohanded/rcl)) + var/obj/item/twohanded/rcl/R = W + if(R.loaded) + R.loaded.cable_join(src, user) + R.is_empty(user) + + else if(istype(W, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/C = W + cable_color(C.colourName) + + else + if(W.flags & CONDUCT) + shock(user, 50, 0.7) + + add_fingerprint(user) + +/obj/structure/cable/multitool_act(mob/user, obj/item/I) + . = TRUE + var/turf/T = get_turf(src) + if(T.intact) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(powernet && (powernet.avail > 0)) // is it powered? + to_chat(user, "Total power: [DisplayPower(powernet.avail)]\nLoad: [DisplayPower(powernet.load)]\nExcess power: [DisplayPower(surplus())]") + else + to_chat(user, "The cable is not powered.") + shock(user, 5, 0.2) + +/obj/structure/cable/wirecutter_act(mob/user, obj/item/I) + . = TRUE + var/turf/T = get_turf(src) + if(T.intact) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(shock(user, 50)) + return + user.visible_message("[user] cuts the cable.", "You cut the cable.") + investigate_log("was cut by [key_name(usr, 1)] in [get_area(user)]([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])","wires") + deconstruct() + +// shock the user with probability prb +/obj/structure/cable/proc/shock(mob/user, prb, siemens_coeff = 1) + if(!prob(prb)) + return FALSE + if(electrocute_mob(user, powernet, src, siemens_coeff)) + do_sparks(5, 1, src) + return TRUE + else + return FALSE + +/obj/structure/cable/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +obj/structure/cable/proc/cable_color(var/colorC) + if(colorC) + if(colorC == "rainbow") + color = color_rainbow() + else + color = colorC + else + color = "#DD0000" + +/obj/structure/cable/proc/color_rainbow() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + return color + +///////////////////////////////////////////////// +// Cable laying helpers +//////////////////////////////////////////////// + +//handles merging diagonally matching cables +//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically +/obj/structure/cable/proc/mergeDiagonalsNetworks(direction) + + //search for and merge diagonally matching cables from the first direction component (north/south) + var/turf/T = get_step(src, direction&3)//go north/south + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + + //the same from the second direction component (east/west) + T = get_step(src, direction&12)//go east/west + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + +// merge with the powernets of power objects in the given direction +/obj/structure/cable/proc/mergeConnectedNetworks(direction) + + var/fdir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf + + if(!(d1 == direction || d2 == direction)) //if the cable is not pointed in this direction, do nothing + return + + var/turf/TB = get_step(src, direction) + + for(var/obj/structure/cable/C in TB) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == fdir || C.d2 == fdir) //we've got a matching cable in the neighbor turf + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + +// merge with the powernets of power objects in the source turf +/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() + var/list/to_connect = list() + + if(!powernet) //if we somehow have no powernet, make one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(src) + + //first let's add turf cables to our powernet + //then we'll connect machines on turf with a node cable is present + for(var/AM in loc) + if(istype(AM, /obj/structure/cable)) + var/obj/structure/cable/C = AM + if(C.d1 == d1 || C.d2 == d1 || C.d1 == d2 || C.d2 == d2) //only connected if they have a common direction + if(C.powernet == powernet) + continue + if(C.powernet) + merge_powernets(powernet, C.powernet) + else + powernet.add_cable(C) //the cable was powernetless, let's just add it to our powernet + + else if(istype(AM, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/N = AM + if(!N.terminal) + continue // APC are connected through their terminal + + if(N.terminal.powernet == powernet) + continue + + to_connect += N.terminal //we'll connect the machines after all cables are merged + + else if(istype(AM, /obj/machinery/power)) //other power machines + var/obj/machinery/power/M = AM + + if(M.powernet == powernet) + continue + + to_connect += M //we'll connect the machines after all cables are merged + + //now that cables are done, let's connect found machines + for(var/obj/machinery/power/PM in to_connect) + if(!PM.connect_to_network()) + PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless + +////////////////////////////////////////////// +// Powernets handling helpers +////////////////////////////////////////////// + +//if powernetless_only = 1, will only get connections without powernet +/obj/structure/cable/proc/get_connections(powernetless_only = 0) + . = list() // this will be a list of all connected power objects + var/turf/T + + //get matching cables from the first direction + if(d1) //if not a node cable + T = get_step(src, d1) + if(T) + . += power_list(T, src, turn(d1, 180), powernetless_only) //get adjacents matching cables + + if(d1&(d1-1)) //diagonal direction, must check the 4 possibles adjacents tiles + T = get_step(src,d1&3) // go north/south + if(T) + . += power_list(T, src, d1 ^ 3, powernetless_only) //get diagonally matching cables + T = get_step(src,d1&12) // go east/west + if(T) + . += power_list(T, src, d1 ^ 12, powernetless_only) //get diagonally matching cables + + . += power_list(loc, src, d1, powernetless_only) //get on turf matching cables + + //do the same on the second direction (which can't be 0) + T = get_step(src, d2) + if(T) + . += power_list(T, src, turn(d2, 180), powernetless_only) //get adjacents matching cables + + if(d2&(d2-1)) //diagonal direction, must check the 4 possibles adjacents tiles + T = get_step(src,d2&3) // go north/south + if(T) + . += power_list(T, src, d2 ^ 3, powernetless_only) //get diagonally matching cables + T = get_step(src,d2&12) // go east/west + if(T) + . += power_list(T, src, d2 ^ 12, powernetless_only) //get diagonally matching cables + . += power_list(loc, src, d2, powernetless_only) //get on turf matching cables + + return . + +//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. +//needed as this can, unlike other placements, disconnect cables +/obj/structure/cable/proc/denode() + var/turf/T1 = loc + if(!T1) + return + + var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet + if(powerlist.len>0) + var/datum/powernet/PN = new() + propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable + + if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables + qdel(PN) + +/obj/structure/cable/proc/auto_propogate_cut_cable(obj/O) + if(O && !QDELETED(O)) + var/datum/powernet/newPN = new()// creates a new powernet... + propagate_network(O, newPN)//... and propagates it to the other side of the cable + +// cut the cable's powernet at this cable and updates the powergrid +/obj/structure/cable/proc/cut_cable_from_powernet(remove=TRUE) + var/turf/T1 = loc + var/list/P_list + if(!T1) + return + if(d1) + T1 = get_step(T1, d1) + P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... + + P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf + + + if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet + powernet.remove_cable(src) + + for(var/obj/machinery/power/P in T1)//check if it was powering a machine + if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to + P.disconnect_from_network() //remove from current network (and delete powernet) + return + + var/obj/O = P_list[1] + // remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist + if(remove) + loc = null + powernet.remove_cable(src) //remove the cut cable from its powernet + + // queue it to rebuild + SSmachines.deferred_powernet_rebuilds += O +// addtimer(CALLBACK(O, .proc/auto_propogate_cut_cable, O), 0) //so we don't rebuild the network X times when singulo/explosion destroys a line of X cables + + // Disconnect machines connected to nodes + if(d1 == 0) // if we cut a node (O-X) cable + for(var/obj/machinery/power/P in T1) + if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to + P.disconnect_from_network() //remove from current network + + +/////////////////////////////////////////////// +// The cable coil object, used for laying cable +/////////////////////////////////////////////// + +//////////////////////////////// +// Definitions +//////////////////////////////// + +GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15))) + +/obj/item/stack/cable_coil + name = "cable coil" + singular_name = "cable" + icon = 'icons/obj/power.dmi' + icon_state = "coil" + item_state = "coil_red" + amount = MAXCOIL + max_amount = MAXCOIL + merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves + color = COLOR_RED + desc = "A coil of power cable." + throwforce = 10 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 2 + throw_range = 5 + materials = list(MAT_METAL=10, MAT_GLASS=5) + flags = CONDUCT + slot_flags = SLOT_BELT + item_state = "coil" + attack_verb = list("whipped", "lashed", "disciplined", "flogged") + usesound = 'sound/items/deconstruct.ogg' + toolspeed = 1 + +/obj/item/stack/cable_coil/suicide_act(mob/user) + if(locate(/obj/structure/chair/stool) in user.loc) + user.visible_message("[user] is making a noose with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") + else + user.visible_message("[user] is strangling [user.p_them()]self with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") + return OXYLOSS + +/obj/item/stack/cable_coil/New(loc, length = MAXCOIL, paramcolor = null) + ..() + amount = length + if(paramcolor) + color = paramcolor + pixel_x = rand(-2,2) + pixel_y = rand(-2,2) + update_icon() + recipes = GLOB.cable_coil_recipes + update_wclass() + +/////////////////////////////////// +// General procedures +/////////////////////////////////// +//you can use wires to heal robotics +/obj/item/stack/cable_coil/attack(mob/M, mob/user) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected] + + if(!S) + return + if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2) + return ..() + + if(S.burn_dam > ROBOLIMB_SELF_REPAIR_CAP) + to_chat(user, "The damage is far too severe to patch over externally.") + return + + if(!S.burn_dam) + to_chat(user, "Nothing to fix!") + return + + if(H == user) + if(!do_mob(user, H, 10)) + return 0 + var/cable_used = 0 + var/childlist + if(!isnull(S.children)) + childlist = S.children.Copy() + var/parenthealed = FALSE + while(cable_used <= MAXCABLEPERHEAL && amount >= 1) + var/obj/item/organ/external/E + if(S.burn_dam) + E = S + else if(LAZYLEN(childlist)) + E = pick_n_take(childlist) + if(!E.burn_dam || !E.is_robotic()) + continue + else if(S.parent && !parenthealed) + E = S.parent + parenthealed = TRUE + if(!E.burn_dam || !E.is_robotic()) + break + else + break + while(cable_used <= MAXCABLEPERHEAL && E.burn_dam && amount >= 1) + use(1) + cable_used += 1 + E.heal_damage(0, HEALPERCABLE, 0, 1) + user.visible_message("\The [user] repairs some burn damage on \the [M]'s [E.name] with \the [src].") + return 1 + + else + return ..() + +/obj/item/stack/cable_coil/split() + var/obj/item/stack/cable_coil/C = ..() + C.color = color + return C + +/obj/item/stack/cable_coil/update_icon() + if(!color) + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + if(amount == 1) + icon_state = "coil1" + name = "cable piece" + else if(amount == 2) + icon_state = "coil2" + name = "cable piece" + else + icon_state = "coil" + name = "cable coil" + ..() + +/obj/item/stack/cable_coil/proc/update_wclass() + if(amount == 1) + w_class = WEIGHT_CLASS_TINY + else + w_class = WEIGHT_CLASS_SMALL + +/obj/item/stack/cable_coil/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(get_amount() == 1) + . += "A short piece of power cable." + else if(get_amount() == 2) + . += "A piece of power cable." + else + . += "A coil of power cable. There are [get_amount()] lengths of cable in the coil." + +// Items usable on a cable coil : +// - Wirecutters : cut them duh ! +// - Cable coil : merge cables +/obj/item/stack/cable_coil/attackby(obj/item/W, mob/user) + ..() + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + // Cable merging is handled by parent proc + if(C.amount >= MAXCOIL) + to_chat(user, "The coil is as long as it will get.") + return + if( (C.amount + src.amount <= MAXCOIL) ) + to_chat(user, "You join the cable coils together.") + return + else + to_chat(user, "You transfer [get_amount_transferred()] length\s of cable from one coil to the other.") + return + + if(istype(W, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/C = W + cable_color(C.colourName) + +/////////////////////////////////////////////// +// Cable laying procedures +////////////////////////////////////////////// + +/obj/item/stack/cable_coil/proc/get_new_cable(location) + var/obj/structure/cable/C = new(location) + C.cable_color(color) + + return C + +// called when cable_coil is clicked on a turf/simulated/floor +/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew) + if(!isturf(user.loc)) + return + + if(!isturf(T) || T.intact || !T.can_have_cabling()) + to_chat(user, "You can only lay cables on catwalks and plating!") + return + + if(get_amount() < 1) // Out of cable + to_chat(user, "There is no cable left!") + return + + if(get_dist(T,user) > 1) // Too far + to_chat(user, "You can't lay cable at a place that far away!") + return + + var/dirn + if(!dirnew) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm) + if(user.loc == T) + dirn = user.dir //If laying on the tile we're on, lay in the direction we're facing + else + dirn = get_dir(T, user) + else + dirn = dirnew + + for(var/obj/structure/cable/LC in T) + if(LC.d2 == dirn && LC.d1 == 0) + to_chat(user, "There's already a cable at that position!") + return + + var/obj/structure/cable/C = get_new_cable(T) + + //set up the new cable + C.d1 = 0 //it's a O-X node cable + C.d2 = dirn + C.add_fingerprint(user) + C.updateicon() + + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/PN = new() + PN.add_cable(C) + + C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets + C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + + use(1) + + if(C.shock(user, 50)) + if(prob(50)) //fail + new /obj/item/stack/cable_coil(get_turf(C), 1, paramcolor = C.color) + C.deconstruct() + + return C + +// called when cable_coil is click on an installed obj/cable +// or click on a turf that already contains a "node" cable +/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) + var/turf/U = user.loc + if(!isturf(U)) + return + + var/turf/T = get_turf(C) + + if(!isturf(T) || T.intact) // sanity checks, also stop use interacting with T-scanner revealed cable + return + + if(get_dist(C, user) > 1) // make sure it's close enough + to_chat(user, "You can't lay cable at a place that far away!") + return + + + if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing + place_turf(T,user) + return + + var/dirn = get_dir(C, user) + + // one end of the clicked cable is pointing towards us + if(C.d1 == dirn || C.d2 == dirn) + if(U.intact) // can't place a cable if the floor is complete + to_chat(user, "You can't lay cable there unless the floor tiles are removed!") + return + else + // cable is pointing at us, we're standing on an open tile + // so create a stub pointing at the clicked cable on our tile + + var/fdirn = turn(dirn, 180) // the opposite direction + + for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already + if(LC.d1 == fdirn || LC.d2 == fdirn) + to_chat(user, "There's already a cable at that position!") + return + + var/obj/structure/cable/NC = get_new_cable (U) + + NC.d1 = 0 + NC.d2 = fdirn + NC.add_fingerprint(user) + NC.update_icon() + + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/newPN = new() + newPN.add_cable(NC) + + NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets + NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + NC.mergeDiagonalsNetworks(NC.d2) + + use(1) + + if(NC.shock(user, 50)) + if(prob(50)) //fail + NC.deconstruct() + return + + // exisiting cable doesn't point at our position, so see if it's a stub + else if(C.d1 == 0) + // if so, make it a full cable pointing from it's old direction to our dirn + var/nd1 = C.d2 // these will be the new directions + var/nd2 = dirn + + + if(nd1 > nd2) // swap directions to match icons/states + nd1 = dirn + nd2 = C.d2 + + + for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable + if(LC == C) // skip the cable we're interacting with + continue + if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction + to_chat(user, "There's already a cable at that position!") + return + + + C.cable_color(color) + + C.d1 = nd1 + C.d2 = nd2 + + C.add_fingerprint() + C.updateicon() + + + C.mergeConnectedNetworks(C.d1) //merge the powernets... + C.mergeConnectedNetworks(C.d2) //...in the two new cable directions + C.mergeConnectedNetworksOnTurf() + + if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d1) + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + + use(1) + + if(C.shock(user, 50)) + if(prob(50)) //fail + C.deconstruct() + return + + C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. + return + +////////////////////////////// +// Misc. +///////////////////////////// + +/obj/item/stack/cable_coil/cut + item_state = "coil2" + +/obj/item/stack/cable_coil/cut/Initialize(mapload) + . = ..() + src.amount = rand(1,2) + pixel_x = rand(-2,2) + pixel_y = rand(-2,2) + update_icon() + update_wclass() + +/obj/item/stack/cable_coil/yellow + color = COLOR_YELLOW + +/obj/item/stack/cable_coil/blue + color = COLOR_BLUE + +/obj/item/stack/cable_coil/green + color = COLOR_GREEN + +/obj/item/stack/cable_coil/pink + color = COLOR_PINK + +/obj/item/stack/cable_coil/orange + color = COLOR_ORANGE + +/obj/item/stack/cable_coil/cyan + color = COLOR_CYAN + +/obj/item/stack/cable_coil/white + color = COLOR_WHITE + +/obj/item/stack/cable_coil/random/New() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + ..() + +/obj/item/stack/cable_coil/proc/cable_color(var/colorC) + if(colorC) + if(colorC == "rainbow") + colorC = color_rainbow() + color = colorC + else + color = COLOR_RED + +/obj/item/stack/cable_coil/proc/color_rainbow() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + return color + +/obj/item/stack/cable_coil/cyborg + name = "cyborg cable coil" + +/obj/item/stack/cable_coil/cyborg/attack_self(mob/user) + var/cablecolor = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") + color = cablecolor + update_icon() + +#undef MAXCABLEPERHEAL +#undef HEALPERCABLE diff --git a/code/modules/power/cable_heavyduty.dm b/code/modules/power/cable_heavyduty.dm index 03b6aafed282..2519dbcd198f 100644 --- a/code/modules/power/cable_heavyduty.dm +++ b/code/modules/power/cable_heavyduty.dm @@ -25,4 +25,4 @@ ..() /obj/structure/cable/heavyduty/cable_color(var/colorC) - return \ No newline at end of file + return diff --git a/code/modules/power/cable_logic.dm b/code/modules/power/cable_logic.dm index b262b6fc3b89..09d10320fd58 100644 --- a/code/modules/power/cable_logic.dm +++ b/code/modules/power/cable_logic.dm @@ -1,292 +1,292 @@ -#define LOGIC_HIGH 5 - -//Indicators only have one input and no outputs -/obj/machinery/logic/indicator - //Input is searched from the 'dir' direction - var/obj/structure/cable/input - -/obj/machinery/logic/indicator/process() - if(input) - return 1 - - - if(!input) - var/turf/T = get_step(src, dir) - if(T) - var/inv_dir = turn(dir, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input = C - return 1 - - return 0 //If it gets to here, it means no suitable wire to link to was found. - -/obj/machinery/logic/indicator/bulb - icon = 'icons/obj/lighting.dmi' - icon_state = "bulb0" - -/obj/machinery/logic/indicator/bulb/process() - if(!..()) //Parent proc checks if input1 exists. - return - - var/datum/powernet/pn_input = input.powernet - if(!pn_input) - return - - if(pn_input.avail >= LOGIC_HIGH) - icon_state = "bulb1" - else - icon_state = "bulb0" - - - - -//Sensors only have one output and no inputs -/obj/machinery/logic/sensor - //Output is searched from the 'dir' direction - var/obj/structure/cable/output - -/obj/machinery/logic/sensor/process() - if(output) - return 1 - - if(!output) - var/turf/T = get_step(src, dir) - if(T) - var/inv_dir = turn(dir, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - output = C - return 1 - - return 0 //If it gets to here, it means no suitable wire to link to was found. - -//Constant high generator. This will continue to send a signal of LOGIC_HIGH as long as it exists. -/obj/machinery/logic/sensor/constant_high - icon = 'icons/obj/atmospherics/outlet_injector.dmi' - icon_state = "off" - -/obj/machinery/logic/sensor/constant_high/process() - if(!..()) //Parent proc checks if input1 exists. - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) - - - - -//ONE INPUT logic elements have one input and one output -/obj/machinery/logic/oneinput - var/dir_input = 2 - var/dir_output = 1 - var/obj/structure/cable/input - var/obj/structure/cable/output - icon = 'icons/atmos/heat.dmi' - icon_state = "intact" - -/obj/machinery/logic/oneinput/process() - if(input && output) - return 1 - - if(!dir_input || !dir_output) - return 0 - - if(!input) - var/turf/T = get_step(src, dir_input) - if(T) - var/inv_dir = turn(dir_input, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input = C - - if(!output) - var/turf/T = get_step(src, dir_output) - if(T) - var/inv_dir = turn(dir_output, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - output = C - - return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. - -//NOT GATE -/obj/machinery/logic/oneinput/not/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input = input.powernet - - if(!pn_input) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( !(pn_input.avail >= LOGIC_HIGH)) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - - - - - - - - - -//TWO INPUT logic elements have two inputs and one output -/obj/machinery/logic/twoinput - var/dir_input1 = 2 - var/dir_input2 = 8 - var/dir_output = 1 - var/obj/structure/cable/input1 - var/obj/structure/cable/input2 - var/obj/structure/cable/output - icon = 'icons/obj/atmospherics/mixer.dmi' - icon_state = "intact_off" - -/obj/machinery/logic/twoinput/process() - if(input1 && input2 && output) - return 1 - - if(!dir_input1 || !dir_input2 || !dir_output) - return 0 - - if(!input1) - var/turf/T = get_step(src, dir_input1) - if(T) - var/inv_dir = turn(dir_input1, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input1 = C - - if(!input2) - var/turf/T = get_step(src, dir_input2) - if(T) - var/inv_dir = turn(dir_input2, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input2 = C - - if(!output) - var/turf/T = get_step(src, dir_output) - if(T) - var/inv_dir = turn(dir_output, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - output = C - - return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. - -//AND GATE -/obj/machinery/logic/twoinput/and/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) && (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -//OR GATE -/obj/machinery/logic/twoinput/or/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) || (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -//XOR GATE -/obj/machinery/logic/twoinput/xor/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) != (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -//XNOR GATE (EQUIVALENCE) -/obj/machinery/logic/twoinput/xnor/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) == (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -#define RELAY_POWER_TRANSFER 2000 //How much power a relay transfers through. - -//RELAY - input1 governs the flow from input2 to output -/obj/machinery/logic/twoinput/relay/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - - if(!pn_input1) - return - - if( pn_input1.avail >= LOGIC_HIGH ) - var/datum/powernet/pn_input2 = input2.powernet - var/datum/powernet/pn_output = output.powernet - - if(!pn_output) - return - - if(pn_input2.avail >= RELAY_POWER_TRANSFER) - pn_input2.load += RELAY_POWER_TRANSFER - pn_output.newavail += RELAY_POWER_TRANSFER - - -#undef RELAY_POWER_TRANSFER -#undef LOGIC_HIGH \ No newline at end of file +#define LOGIC_HIGH 5 + +//Indicators only have one input and no outputs +/obj/machinery/logic/indicator + //Input is searched from the 'dir' direction + var/obj/structure/cable/input + +/obj/machinery/logic/indicator/process() + if(input) + return 1 + + + if(!input) + var/turf/T = get_step(src, dir) + if(T) + var/inv_dir = turn(dir, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input = C + return 1 + + return 0 //If it gets to here, it means no suitable wire to link to was found. + +/obj/machinery/logic/indicator/bulb + icon = 'icons/obj/lighting.dmi' + icon_state = "bulb0" + +/obj/machinery/logic/indicator/bulb/process() + if(!..()) //Parent proc checks if input1 exists. + return + + var/datum/powernet/pn_input = input.powernet + if(!pn_input) + return + + if(pn_input.avail >= LOGIC_HIGH) + icon_state = "bulb1" + else + icon_state = "bulb0" + + + + +//Sensors only have one output and no inputs +/obj/machinery/logic/sensor + //Output is searched from the 'dir' direction + var/obj/structure/cable/output + +/obj/machinery/logic/sensor/process() + if(output) + return 1 + + if(!output) + var/turf/T = get_step(src, dir) + if(T) + var/inv_dir = turn(dir, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + output = C + return 1 + + return 0 //If it gets to here, it means no suitable wire to link to was found. + +//Constant high generator. This will continue to send a signal of LOGIC_HIGH as long as it exists. +/obj/machinery/logic/sensor/constant_high + icon = 'icons/obj/atmospherics/outlet_injector.dmi' + icon_state = "off" + +/obj/machinery/logic/sensor/constant_high/process() + if(!..()) //Parent proc checks if input1 exists. + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) + + + + +//ONE INPUT logic elements have one input and one output +/obj/machinery/logic/oneinput + var/dir_input = 2 + var/dir_output = 1 + var/obj/structure/cable/input + var/obj/structure/cable/output + icon = 'icons/atmos/heat.dmi' + icon_state = "intact" + +/obj/machinery/logic/oneinput/process() + if(input && output) + return 1 + + if(!dir_input || !dir_output) + return 0 + + if(!input) + var/turf/T = get_step(src, dir_input) + if(T) + var/inv_dir = turn(dir_input, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input = C + + if(!output) + var/turf/T = get_step(src, dir_output) + if(T) + var/inv_dir = turn(dir_output, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + output = C + + return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. + +//NOT GATE +/obj/machinery/logic/oneinput/not/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input = input.powernet + + if(!pn_input) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( !(pn_input.avail >= LOGIC_HIGH)) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + + + + + + + + + +//TWO INPUT logic elements have two inputs and one output +/obj/machinery/logic/twoinput + var/dir_input1 = 2 + var/dir_input2 = 8 + var/dir_output = 1 + var/obj/structure/cable/input1 + var/obj/structure/cable/input2 + var/obj/structure/cable/output + icon = 'icons/obj/atmospherics/mixer.dmi' + icon_state = "intact_off" + +/obj/machinery/logic/twoinput/process() + if(input1 && input2 && output) + return 1 + + if(!dir_input1 || !dir_input2 || !dir_output) + return 0 + + if(!input1) + var/turf/T = get_step(src, dir_input1) + if(T) + var/inv_dir = turn(dir_input1, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input1 = C + + if(!input2) + var/turf/T = get_step(src, dir_input2) + if(T) + var/inv_dir = turn(dir_input2, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input2 = C + + if(!output) + var/turf/T = get_step(src, dir_output) + if(T) + var/inv_dir = turn(dir_output, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + output = C + + return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. + +//AND GATE +/obj/machinery/logic/twoinput/and/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) && (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +//OR GATE +/obj/machinery/logic/twoinput/or/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) || (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +//XOR GATE +/obj/machinery/logic/twoinput/xor/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) != (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +//XNOR GATE (EQUIVALENCE) +/obj/machinery/logic/twoinput/xnor/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) == (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +#define RELAY_POWER_TRANSFER 2000 //How much power a relay transfers through. + +//RELAY - input1 governs the flow from input2 to output +/obj/machinery/logic/twoinput/relay/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + + if(!pn_input1) + return + + if( pn_input1.avail >= LOGIC_HIGH ) + var/datum/powernet/pn_input2 = input2.powernet + var/datum/powernet/pn_output = output.powernet + + if(!pn_output) + return + + if(pn_input2.avail >= RELAY_POWER_TRANSFER) + pn_input2.load += RELAY_POWER_TRANSFER + pn_output.newavail += RELAY_POWER_TRANSFER + + +#undef RELAY_POWER_TRANSFER +#undef LOGIC_HIGH diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 588ea6b231f5..24f55d344541 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -1,356 +1,356 @@ -/obj/item/stock_parts/cell - name = "power cell" - desc = "A rechargeable electrochemical power cell." - icon = 'icons/obj/power.dmi' - icon_state = "cell" - item_state = "cell" - origin_tech = "powerstorage=1" - force = 5 - throwforce = 5 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/charge = 0 // note %age conveted to actual charge in New - var/maxcharge = 1000 - materials = list(MAT_METAL = 700, MAT_GLASS = 50) - var/rigged = FALSE // true if rigged to explode - var/chargerate = 100 //how much power is given every tick in a recharger - var/self_recharge = 0 //does it self recharge, over time, or not? - var/ratingdesc = TRUE - var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it. - -/obj/item/stock_parts/cell/get_cell() - return src - -/obj/item/stock_parts/cell/New() - ..() - START_PROCESSING(SSobj, src) - charge = maxcharge - if(ratingdesc) - desc += " This one has a power rating of [DisplayPower(maxcharge)], and you should not swallow it." - update_icon() - -/obj/item/stock_parts/cell/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/stock_parts/cell/vv_edit_var(var_name, var_value) - switch(var_name) - if("self_recharge") - if(var_value) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/stock_parts/cell/process() - if(self_recharge) - give(chargerate * 0.25) - else - return PROCESS_KILL - -/obj/item/stock_parts/cell/update_icon() - overlays.Cut() - if(grown_battery) - overlays += image('icons/obj/power.dmi', "grown_wires") - if(charge < 0.01) - return - else if(charge/maxcharge >=0.995) - overlays += "cell-o2" - else - overlays += "cell-o1" - -/obj/item/stock_parts/cell/proc/percent() // return % charge of cell - return 100 * charge / maxcharge - -// use power from a cell -/obj/item/stock_parts/cell/use(amount) - if(rigged && amount > 0) - explode() - return 0 - if(charge < amount) - return 0 - charge = (charge - amount) - return 1 - -// recharge the cell -/obj/item/stock_parts/cell/proc/give(amount) - if(rigged && amount > 0) - explode() - return 0 - if(maxcharge < amount) - amount = maxcharge - var/power_used = min(maxcharge - charge, amount) - charge += power_used - return power_used - -/obj/item/stock_parts/cell/examine(mob/user) - . = ..() - if(rigged) - . += "This power cell seems to be faulty!" - else - . += "The charge meter reads [round(percent() )]%." - -/obj/item/stock_parts/cell/suicide_act(mob/user) - to_chat(viewers(user), "[user] is licking the electrodes of the [src]! It looks like [user.p_theyre()] trying to commit suicide.") - return FIRELOSS - -/obj/item/stock_parts/cell/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/reagent_containers/syringe)) - var/obj/item/reagent_containers/syringe/S = W - - to_chat(user, "You inject the solution into the power cell.") - - if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) - - rigged = TRUE - - log_admin("LOG: [key_name(user)] injected a power cell with plasma, rigging it to explode.") - message_admins("LOG: [key_name_admin(user)] injected a power cell with plasma, rigging it to explode.") - - S.reagents.clear_reagents() - else - return ..() - - -/obj/item/stock_parts/cell/proc/explode() - var/turf/T = get_turf(loc) - if(charge == 0) - return - var/devastation_range = -1 //round(charge/11000) - var/heavy_impact_range = round(sqrt(charge) / 60) - var/light_impact_range = round(sqrt(charge) / 30) - var/flash_range = light_impact_range - if(light_impact_range == 0) - rigged = FALSE - corrupt() - return - //explosion(T, 0, 1, 2, 2) - log_admin("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") - - explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range) - qdel(src) - -/obj/item/stock_parts/cell/proc/corrupt() - charge /= 2 - maxcharge = max(maxcharge / 2, chargerate) - if(prob(10)) - rigged = TRUE //broken batterys are dangerous - -/obj/item/stock_parts/cell/emp_act(severity) - charge -= 1000 / severity - if(charge < 0) - charge = 0 - ..() - -/obj/item/stock_parts/cell/ex_act(severity) - ..() - if(!QDELETED(src)) - switch(severity) - if(2) - if(prob(50)) - corrupt() - if(3) - if(prob(25)) - corrupt() - -/obj/item/stock_parts/cell/blob_act(obj/structure/blob/B) - ex_act(EXPLODE_DEVASTATE) - -/obj/item/stock_parts/cell/proc/get_electrocute_damage() - if(charge >= 1000) - return Clamp(20 + round(charge / 25000), 20, 195) + rand(-5, 5) - else - return 0 - -// Cell variants -/obj/item/stock_parts/cell/empty/New() - ..() - charge = 0 - -/obj/item/stock_parts/cell/crap - name = "\improper Nanotrasen brand rechargeable AA battery" - desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT - maxcharge = 500 - materials = list(MAT_GLASS = 40) - rating = 2 - -/obj/item/stock_parts/cell/crap/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/upgraded - name = "upgraded power cell" - desc = "A power cell with a slightly higher capacity than normal!" - maxcharge = 2500 - materials = list(MAT_GLASS = 50) - rating = 2 - chargerate = 1000 - -/obj/item/stock_parts/cell/upgraded/plus - name = "upgraded power cell+" - desc = "A power cell with an even higher capacity than the base model!" - maxcharge = 5000 - -/obj/item/stock_parts/cell/secborg - name = "security borg rechargeable D battery" - origin_tech = null - maxcharge = 600 //600 max charge / 100 charge per shot = six shots - materials = list(MAT_GLASS = 40) - rating = 2.5 - -/obj/item/stock_parts/cell/secborg/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/pulse //200 pulse shots - name = "pulse rifle power cell" - maxcharge = 40000 - rating = 3 - chargerate = 1500 - -/obj/item/stock_parts/cell/pulse/carbine //25 pulse shots - name = "pulse carbine power cell" - maxcharge = 5000 - -/obj/item/stock_parts/cell/pulse/pistol //10 pulse shots - name = "pulse pistol power cell" - maxcharge = 2000 - -/obj/item/stock_parts/cell/high - name = "high-capacity power cell" - origin_tech = "powerstorage=2" - icon_state = "hcell" - maxcharge = 10000 - materials = list(MAT_GLASS = 60) - rating = 3 - chargerate = 1500 - -/obj/item/stock_parts/cell/high/plus - name = "high-capacity power cell+" - desc = "Where did these come from?" - icon_state = "h+cell" - maxcharge = 15000 - chargerate = 2250 - -/obj/item/stock_parts/cell/high/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/super - name = "super-capacity power cell" - origin_tech = "powerstorage=3;materials=3" - icon_state = "scell" - maxcharge = 20000 - materials = list(MAT_GLASS = 300) - rating = 4 - chargerate = 2000 - -/obj/item/stock_parts/cell/super/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/hyper - name = "hyper-capacity power cell" - origin_tech = "powerstorage=4;engineering=4;materials=4" - icon_state = "hpcell" - maxcharge = 30000 - materials = list(MAT_GLASS = 400) - rating = 5 - chargerate = 3000 - -/obj/item/stock_parts/cell/hyper/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/bluespace - name = "bluespace power cell" - desc = "A rechargeable transdimensional power cell." - origin_tech = "powerstorage=5;bluespace=4;materials=4;engineering=4" - icon_state = "bscell" - maxcharge = 40000 - materials = list(MAT_GLASS = 600) - rating = 6 - chargerate = 4000 - -/obj/item/stock_parts/cell/bluespace/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/infinite - name = "infinite-capacity power cell!" - icon_state = "icell" - origin_tech = "powerstorage=7" - maxcharge = 30000 - materials = list(MAT_GLASS=1000) - rating = 6 - chargerate = 30000 - -/obj/item/stock_parts/cell/infinite/use() - return 1 - -/obj/item/stock_parts/cell/infinite/abductor - name = "void core" - desc = "An alien power cell that produces energy seemingly out of nowhere." - icon = 'icons/obj/abductor.dmi' - icon_state = "cell" - maxcharge = 50000 - rating = 12 - ratingdesc = FALSE - -/obj/item/stock_parts/cell/infinite/abductor/update_icon() - return - - -/obj/item/stock_parts/cell/potato - name = "potato battery" - desc = "A rechargeable starch based power cell." - icon = 'icons/obj/hydroponics/harvest.dmi' - icon_state = "potato" - origin_tech = "powerstorage=1;biotech=1" - charge = 100 - maxcharge = 300 - materials = list() - rating = 1 - grown_battery = TRUE //it has the overlays for wires - -/obj/item/stock_parts/cell/high/slime - name = "charged slime core" - desc = "A yellow slime core infused with plasma, it crackles with power." - origin_tech = "powerstorage=5;biotech=4" - icon = 'icons/mob/slimes.dmi' - icon_state = "yellow slime extract" - materials = list() - rating = 5 //self-recharge makes these desirable - self_recharge = 1 // Infused slime cores self-recharge, over time - chargerate = 500 - -/obj/item/stock_parts/cell/emproof - name = "\improper EMP-proof cell" - desc = "An EMP-proof cell." - maxcharge = 500 - rating = 3 - -/obj/item/stock_parts/cell/emproof/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/emproof/emp_act(severity) - return - -/obj/item/stock_parts/cell/emproof/corrupt() - return - -/obj/item/stock_parts/cell/ninja - name = "spider-clan power cell" - desc = "A standard ninja-suit power cell." - maxcharge = 10000 - materials = list(MAT_GLASS = 60) \ No newline at end of file +/obj/item/stock_parts/cell + name = "power cell" + desc = "A rechargeable electrochemical power cell." + icon = 'icons/obj/power.dmi' + icon_state = "cell" + item_state = "cell" + origin_tech = "powerstorage=1" + force = 5 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + var/charge = 0 // note %age conveted to actual charge in New + var/maxcharge = 1000 + materials = list(MAT_METAL = 700, MAT_GLASS = 50) + var/rigged = FALSE // true if rigged to explode + var/chargerate = 100 //how much power is given every tick in a recharger + var/self_recharge = 0 //does it self recharge, over time, or not? + var/ratingdesc = TRUE + var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it. + +/obj/item/stock_parts/cell/get_cell() + return src + +/obj/item/stock_parts/cell/New() + ..() + START_PROCESSING(SSobj, src) + charge = maxcharge + if(ratingdesc) + desc += " This one has a power rating of [DisplayPower(maxcharge)], and you should not swallow it." + update_icon() + +/obj/item/stock_parts/cell/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/stock_parts/cell/vv_edit_var(var_name, var_value) + switch(var_name) + if("self_recharge") + if(var_value) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/stock_parts/cell/process() + if(self_recharge) + give(chargerate * 0.25) + else + return PROCESS_KILL + +/obj/item/stock_parts/cell/update_icon() + overlays.Cut() + if(grown_battery) + overlays += image('icons/obj/power.dmi', "grown_wires") + if(charge < 0.01) + return + else if(charge/maxcharge >=0.995) + overlays += "cell-o2" + else + overlays += "cell-o1" + +/obj/item/stock_parts/cell/proc/percent() // return % charge of cell + return 100 * charge / maxcharge + +// use power from a cell +/obj/item/stock_parts/cell/use(amount) + if(rigged && amount > 0) + explode() + return 0 + if(charge < amount) + return 0 + charge = (charge - amount) + return 1 + +// recharge the cell +/obj/item/stock_parts/cell/proc/give(amount) + if(rigged && amount > 0) + explode() + return 0 + if(maxcharge < amount) + amount = maxcharge + var/power_used = min(maxcharge - charge, amount) + charge += power_used + return power_used + +/obj/item/stock_parts/cell/examine(mob/user) + . = ..() + if(rigged) + . += "This power cell seems to be faulty!" + else + . += "The charge meter reads [round(percent() )]%." + +/obj/item/stock_parts/cell/suicide_act(mob/user) + to_chat(viewers(user), "[user] is licking the electrodes of the [src]! It looks like [user.p_theyre()] trying to commit suicide.") + return FIRELOSS + +/obj/item/stock_parts/cell/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/reagent_containers/syringe)) + var/obj/item/reagent_containers/syringe/S = W + + to_chat(user, "You inject the solution into the power cell.") + + if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) + + rigged = TRUE + + log_admin("LOG: [key_name(user)] injected a power cell with plasma, rigging it to explode.") + message_admins("LOG: [key_name_admin(user)] injected a power cell with plasma, rigging it to explode.") + + S.reagents.clear_reagents() + else + return ..() + + +/obj/item/stock_parts/cell/proc/explode() + var/turf/T = get_turf(loc) + if(charge == 0) + return + var/devastation_range = -1 //round(charge/11000) + var/heavy_impact_range = round(sqrt(charge) / 60) + var/light_impact_range = round(sqrt(charge) / 30) + var/flash_range = light_impact_range + if(light_impact_range == 0) + rigged = FALSE + corrupt() + return + //explosion(T, 0, 1, 2, 2) + log_admin("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") + + explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range) + qdel(src) + +/obj/item/stock_parts/cell/proc/corrupt() + charge /= 2 + maxcharge = max(maxcharge / 2, chargerate) + if(prob(10)) + rigged = TRUE //broken batterys are dangerous + +/obj/item/stock_parts/cell/emp_act(severity) + charge -= 1000 / severity + if(charge < 0) + charge = 0 + ..() + +/obj/item/stock_parts/cell/ex_act(severity) + ..() + if(!QDELETED(src)) + switch(severity) + if(2) + if(prob(50)) + corrupt() + if(3) + if(prob(25)) + corrupt() + +/obj/item/stock_parts/cell/blob_act(obj/structure/blob/B) + ex_act(EXPLODE_DEVASTATE) + +/obj/item/stock_parts/cell/proc/get_electrocute_damage() + if(charge >= 1000) + return Clamp(20 + round(charge / 25000), 20, 195) + rand(-5, 5) + else + return 0 + +// Cell variants +/obj/item/stock_parts/cell/empty/New() + ..() + charge = 0 + +/obj/item/stock_parts/cell/crap + name = "\improper Nanotrasen brand rechargeable AA battery" + desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT + maxcharge = 500 + materials = list(MAT_GLASS = 40) + rating = 2 + +/obj/item/stock_parts/cell/crap/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/upgraded + name = "upgraded power cell" + desc = "A power cell with a slightly higher capacity than normal!" + maxcharge = 2500 + materials = list(MAT_GLASS = 50) + rating = 2 + chargerate = 1000 + +/obj/item/stock_parts/cell/upgraded/plus + name = "upgraded power cell+" + desc = "A power cell with an even higher capacity than the base model!" + maxcharge = 5000 + +/obj/item/stock_parts/cell/secborg + name = "security borg rechargeable D battery" + origin_tech = null + maxcharge = 600 //600 max charge / 100 charge per shot = six shots + materials = list(MAT_GLASS = 40) + rating = 2.5 + +/obj/item/stock_parts/cell/secborg/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/pulse //200 pulse shots + name = "pulse rifle power cell" + maxcharge = 40000 + rating = 3 + chargerate = 1500 + +/obj/item/stock_parts/cell/pulse/carbine //25 pulse shots + name = "pulse carbine power cell" + maxcharge = 5000 + +/obj/item/stock_parts/cell/pulse/pistol //10 pulse shots + name = "pulse pistol power cell" + maxcharge = 2000 + +/obj/item/stock_parts/cell/high + name = "high-capacity power cell" + origin_tech = "powerstorage=2" + icon_state = "hcell" + maxcharge = 10000 + materials = list(MAT_GLASS = 60) + rating = 3 + chargerate = 1500 + +/obj/item/stock_parts/cell/high/plus + name = "high-capacity power cell+" + desc = "Where did these come from?" + icon_state = "h+cell" + maxcharge = 15000 + chargerate = 2250 + +/obj/item/stock_parts/cell/high/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/super + name = "super-capacity power cell" + origin_tech = "powerstorage=3;materials=3" + icon_state = "scell" + maxcharge = 20000 + materials = list(MAT_GLASS = 300) + rating = 4 + chargerate = 2000 + +/obj/item/stock_parts/cell/super/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/hyper + name = "hyper-capacity power cell" + origin_tech = "powerstorage=4;engineering=4;materials=4" + icon_state = "hpcell" + maxcharge = 30000 + materials = list(MAT_GLASS = 400) + rating = 5 + chargerate = 3000 + +/obj/item/stock_parts/cell/hyper/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/bluespace + name = "bluespace power cell" + desc = "A rechargeable transdimensional power cell." + origin_tech = "powerstorage=5;bluespace=4;materials=4;engineering=4" + icon_state = "bscell" + maxcharge = 40000 + materials = list(MAT_GLASS = 600) + rating = 6 + chargerate = 4000 + +/obj/item/stock_parts/cell/bluespace/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/infinite + name = "infinite-capacity power cell!" + icon_state = "icell" + origin_tech = "powerstorage=7" + maxcharge = 30000 + materials = list(MAT_GLASS=1000) + rating = 6 + chargerate = 30000 + +/obj/item/stock_parts/cell/infinite/use() + return 1 + +/obj/item/stock_parts/cell/infinite/abductor + name = "void core" + desc = "An alien power cell that produces energy seemingly out of nowhere." + icon = 'icons/obj/abductor.dmi' + icon_state = "cell" + maxcharge = 50000 + rating = 12 + ratingdesc = FALSE + +/obj/item/stock_parts/cell/infinite/abductor/update_icon() + return + + +/obj/item/stock_parts/cell/potato + name = "potato battery" + desc = "A rechargeable starch based power cell." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "potato" + origin_tech = "powerstorage=1;biotech=1" + charge = 100 + maxcharge = 300 + materials = list() + rating = 1 + grown_battery = TRUE //it has the overlays for wires + +/obj/item/stock_parts/cell/high/slime + name = "charged slime core" + desc = "A yellow slime core infused with plasma, it crackles with power." + origin_tech = "powerstorage=5;biotech=4" + icon = 'icons/mob/slimes.dmi' + icon_state = "yellow slime extract" + materials = list() + rating = 5 //self-recharge makes these desirable + self_recharge = 1 // Infused slime cores self-recharge, over time + chargerate = 500 + +/obj/item/stock_parts/cell/emproof + name = "\improper EMP-proof cell" + desc = "An EMP-proof cell." + maxcharge = 500 + rating = 3 + +/obj/item/stock_parts/cell/emproof/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/emproof/emp_act(severity) + return + +/obj/item/stock_parts/cell/emproof/corrupt() + return + +/obj/item/stock_parts/cell/ninja + name = "spider-clan power cell" + desc = "A standard ninja-suit power cell." + maxcharge = 10000 + materials = list(MAT_GLASS = 60) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 4a88e3363fcb..4a2d8b665183 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -1,250 +1,250 @@ -/obj/machinery/power/generator - name = "thermoelectric generator" - desc = "It's a high efficiency thermoelectric generator." - icon_state = "teg" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - - var/obj/machinery/atmospherics/binary/circulator/cold_circ - var/obj/machinery/atmospherics/binary/circulator/hot_circ - - var/cold_dir = WEST - var/hot_dir = EAST - - var/lastgen = 0 - var/lastgenlev = -1 - var/lastcirc = "00" - -/obj/machinery/power/generator/New() - ..() - update_desc() - -/obj/machinery/power/generator/proc/update_desc() - desc = initial(desc) + " Its cold circulator is located on the [dir2text(cold_dir)] side, and its heat circulator is located on the [dir2text(hot_dir)] side." - -/obj/machinery/power/generator/Destroy() - disconnect() - return ..() - -/obj/machinery/power/generator/proc/disconnect() - if(cold_circ) - cold_circ.generator = null - if(hot_circ) - hot_circ.generator = null - if(powernet) - disconnect_from_network() - -/obj/machinery/power/generator/Initialize() - ..() - connect() - -/obj/machinery/power/generator/proc/connect() - connect_to_network() - - var/obj/machinery/atmospherics/binary/circulator/circpath = /obj/machinery/atmospherics/binary/circulator - cold_circ = locate(circpath) in get_step(src, cold_dir) - hot_circ = locate(circpath) in get_step(src, hot_dir) - - if(cold_circ && cold_circ.side == cold_dir) - cold_circ.generator = src - cold_circ.update_icon() - else - cold_circ = null - - if(hot_circ && hot_circ.side == hot_dir) - hot_circ.generator = src - hot_circ.update_icon() - else - hot_circ = null - - power_change() - update_icon() - updateDialog() - -/obj/machinery/power/generator/power_change() - if(!anchored) - stat |= NOPOWER - else - ..() - -/obj/machinery/power/generator/update_icon() - if(stat & (NOPOWER|BROKEN)) - overlays.Cut() - else - overlays.Cut() - - if(lastgenlev != 0) - overlays += image('icons/obj/power.dmi', "teg-op[lastgenlev]") - - overlays += image('icons/obj/power.dmi', "teg-oc[lastcirc]") - -/obj/machinery/power/generator/process() - if(stat & (NOPOWER|BROKEN)) - return - - if(!cold_circ || !hot_circ) - return - - lastgen = 0 - - if(powernet) - - //log_debug("cold_circ and hot_circ pass") - - var/datum/gas_mixture/cold_air = cold_circ.return_transfer_air() - var/datum/gas_mixture/hot_air = hot_circ.return_transfer_air() - - //log_debug("hot_air = [hot_air]; cold_air = [cold_air];") - - if(cold_air && hot_air) - - //log_debug("hot_air = [hot_air] temperature = [hot_air.temperature]; cold_air = [cold_air] temperature = [hot_air.temperature];") - - //log_debug("coldair and hotair pass") - var/cold_air_heat_capacity = cold_air.heat_capacity() - var/hot_air_heat_capacity = hot_air.heat_capacity() - - var/delta_temperature = hot_air.temperature - cold_air.temperature - - //log_debug("delta_temperature = [delta_temperature]; cold_air_heat_capacity = [cold_air_heat_capacity]; hot_air_heat_capacity = [hot_air_heat_capacity]") - - if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0) - var/efficiency = 0.65 - - var/energy_transfer = delta_temperature * hot_air_heat_capacity * cold_air_heat_capacity / (hot_air_heat_capacity + cold_air_heat_capacity) - - var/heat = energy_transfer * (1 - efficiency) - lastgen = energy_transfer * efficiency - - //log_debug("lastgen = [lastgen]; heat = [heat]; delta_temperature = [delta_temperature]; hot_air_heat_capacity = [hot_air_heat_capacity]; cold_air_heat_capacity = [cold_air_heat_capacity];") - - hot_air.temperature = hot_air.temperature - energy_transfer / hot_air_heat_capacity - cold_air.temperature = cold_air.temperature + heat / cold_air_heat_capacity - - //log_debug("POWER: [lastgen] W generated at [efficiency * 100]% efficiency and sinks sizes [cold_air_heat_capacity], [hot_air_heat_capacity]") - - add_avail(lastgen) - // update icon overlays only if displayed level has changed - - if(hot_air) - var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() - hot_circ_air1.merge(hot_air) - - if(cold_air) - var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() - cold_circ_air1.merge(cold_air) - - var/genlev = max(0, min( round(11 * lastgen / 100000), 11)) - var/circ = "[cold_circ && cold_circ.last_pressure_delta > 0 ? "1" : "0"][hot_circ && hot_circ.last_pressure_delta > 0 ? "1" : "0"]" - if((genlev != lastgenlev) || (circ != lastcirc)) - lastgenlev = genlev - lastcirc = circ - update_icon() - - updateDialog() - -/obj/machinery/power/generator/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/power/generator/attack_ghost(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - interact(user) - -/obj/machinery/power/generator/attack_hand(mob/user) - if(..()) - user << browse(null, "window=teg") - return - interact(user) - -/obj/machinery/power/generator/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(cold_dir == WEST) - cold_dir = EAST - hot_dir = WEST - else if(cold_dir == NORTH) - cold_dir = SOUTH - hot_dir = NORTH - else if(cold_dir == EAST) - cold_dir = WEST - hot_dir = EAST - else - cold_dir = NORTH - hot_dir = SOUTH - connect() - to_chat(user, "You reverse the generator's circulator settings. The cold circulator is now on the [dir2text(cold_dir)] side, and the heat circulator is now on the [dir2text(hot_dir)] side.") - update_desc() - -/obj/machinery/power/generator/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - anchored = !anchored - if(!anchored) - disconnect() - power_change() - else - connect() - to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.") - -/obj/machinery/power/generator/proc/get_menu(include_link = 1) - var/t = "" - if(!powernet) - t += "Unable to connect to the power network!" - t += "
    Retry" - else if(cold_circ && hot_circ) - var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() - var/datum/gas_mixture/cold_circ_air2 = cold_circ.get_inlet_air() - var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() - var/datum/gas_mixture/hot_circ_air2 = hot_circ.get_inlet_air() - - t += "
    " - - t += "Output: [round(lastgen)] W" - - t += "
    " - - t += "Cold loop
    " - t += "Temperature Inlet: [round(cold_circ_air2.temperature, 0.1)] K / Outlet: [round(cold_circ_air1.temperature, 0.1)] K
    " - t += "Pressure Inlet: [round(cold_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(cold_circ_air1.return_pressure(), 0.1)] kPa
    " - - t += "Hot loop
    " - t += "Temperature Inlet: [round(hot_circ_air2.temperature, 0.1)] K / Outlet: [round(hot_circ_air1.temperature, 0.1)] K
    " - t += "Pressure Inlet: [round(hot_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(hot_circ_air1.return_pressure(), 0.1)] kPa
    " - - t += "
    " - else - t += "Unable to locate all parts!" - t += "
    Retry" - if(include_link) - t += "
    Close" - - return t - -/obj/machinery/power/generator/interact(mob/user) - user.set_machine(src) - - var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300) - popup.set_content(get_menu()) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - return 1 - -/obj/machinery/power/generator/Topic(href, href_list) - if(..()) - return 0 - if( href_list["close"] ) - usr << browse(null, "window=teg") - usr.unset_machine() - return 0 - if( href_list["check"] ) - if(!powernet || !cold_circ || !hot_circ) - connect() - return 1 - -/obj/machinery/power/generator/power_change() - ..() - update_icon() +/obj/machinery/power/generator + name = "thermoelectric generator" + desc = "It's a high efficiency thermoelectric generator." + icon_state = "teg" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + + var/obj/machinery/atmospherics/binary/circulator/cold_circ + var/obj/machinery/atmospherics/binary/circulator/hot_circ + + var/cold_dir = WEST + var/hot_dir = EAST + + var/lastgen = 0 + var/lastgenlev = -1 + var/lastcirc = "00" + +/obj/machinery/power/generator/New() + ..() + update_desc() + +/obj/machinery/power/generator/proc/update_desc() + desc = initial(desc) + " Its cold circulator is located on the [dir2text(cold_dir)] side, and its heat circulator is located on the [dir2text(hot_dir)] side." + +/obj/machinery/power/generator/Destroy() + disconnect() + return ..() + +/obj/machinery/power/generator/proc/disconnect() + if(cold_circ) + cold_circ.generator = null + if(hot_circ) + hot_circ.generator = null + if(powernet) + disconnect_from_network() + +/obj/machinery/power/generator/Initialize() + ..() + connect() + +/obj/machinery/power/generator/proc/connect() + connect_to_network() + + var/obj/machinery/atmospherics/binary/circulator/circpath = /obj/machinery/atmospherics/binary/circulator + cold_circ = locate(circpath) in get_step(src, cold_dir) + hot_circ = locate(circpath) in get_step(src, hot_dir) + + if(cold_circ && cold_circ.side == cold_dir) + cold_circ.generator = src + cold_circ.update_icon() + else + cold_circ = null + + if(hot_circ && hot_circ.side == hot_dir) + hot_circ.generator = src + hot_circ.update_icon() + else + hot_circ = null + + power_change() + update_icon() + updateDialog() + +/obj/machinery/power/generator/power_change() + if(!anchored) + stat |= NOPOWER + else + ..() + +/obj/machinery/power/generator/update_icon() + if(stat & (NOPOWER|BROKEN)) + overlays.Cut() + else + overlays.Cut() + + if(lastgenlev != 0) + overlays += image('icons/obj/power.dmi', "teg-op[lastgenlev]") + + overlays += image('icons/obj/power.dmi', "teg-oc[lastcirc]") + +/obj/machinery/power/generator/process() + if(stat & (NOPOWER|BROKEN)) + return + + if(!cold_circ || !hot_circ) + return + + lastgen = 0 + + if(powernet) + + //log_debug("cold_circ and hot_circ pass") + + var/datum/gas_mixture/cold_air = cold_circ.return_transfer_air() + var/datum/gas_mixture/hot_air = hot_circ.return_transfer_air() + + //log_debug("hot_air = [hot_air]; cold_air = [cold_air];") + + if(cold_air && hot_air) + + //log_debug("hot_air = [hot_air] temperature = [hot_air.temperature]; cold_air = [cold_air] temperature = [hot_air.temperature];") + + //log_debug("coldair and hotair pass") + var/cold_air_heat_capacity = cold_air.heat_capacity() + var/hot_air_heat_capacity = hot_air.heat_capacity() + + var/delta_temperature = hot_air.temperature - cold_air.temperature + + //log_debug("delta_temperature = [delta_temperature]; cold_air_heat_capacity = [cold_air_heat_capacity]; hot_air_heat_capacity = [hot_air_heat_capacity]") + + if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0) + var/efficiency = 0.65 + + var/energy_transfer = delta_temperature * hot_air_heat_capacity * cold_air_heat_capacity / (hot_air_heat_capacity + cold_air_heat_capacity) + + var/heat = energy_transfer * (1 - efficiency) + lastgen = energy_transfer * efficiency + + //log_debug("lastgen = [lastgen]; heat = [heat]; delta_temperature = [delta_temperature]; hot_air_heat_capacity = [hot_air_heat_capacity]; cold_air_heat_capacity = [cold_air_heat_capacity];") + + hot_air.temperature = hot_air.temperature - energy_transfer / hot_air_heat_capacity + cold_air.temperature = cold_air.temperature + heat / cold_air_heat_capacity + + //log_debug("POWER: [lastgen] W generated at [efficiency * 100]% efficiency and sinks sizes [cold_air_heat_capacity], [hot_air_heat_capacity]") + + add_avail(lastgen) + // update icon overlays only if displayed level has changed + + if(hot_air) + var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() + hot_circ_air1.merge(hot_air) + + if(cold_air) + var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() + cold_circ_air1.merge(cold_air) + + var/genlev = max(0, min( round(11 * lastgen / 100000), 11)) + var/circ = "[cold_circ && cold_circ.last_pressure_delta > 0 ? "1" : "0"][hot_circ && hot_circ.last_pressure_delta > 0 ? "1" : "0"]" + if((genlev != lastgenlev) || (circ != lastcirc)) + lastgenlev = genlev + lastcirc = circ + update_icon() + + updateDialog() + +/obj/machinery/power/generator/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/power/generator/attack_ghost(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + interact(user) + +/obj/machinery/power/generator/attack_hand(mob/user) + if(..()) + user << browse(null, "window=teg") + return + interact(user) + +/obj/machinery/power/generator/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(cold_dir == WEST) + cold_dir = EAST + hot_dir = WEST + else if(cold_dir == NORTH) + cold_dir = SOUTH + hot_dir = NORTH + else if(cold_dir == EAST) + cold_dir = WEST + hot_dir = EAST + else + cold_dir = NORTH + hot_dir = SOUTH + connect() + to_chat(user, "You reverse the generator's circulator settings. The cold circulator is now on the [dir2text(cold_dir)] side, and the heat circulator is now on the [dir2text(hot_dir)] side.") + update_desc() + +/obj/machinery/power/generator/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(!anchored) + disconnect() + power_change() + else + connect() + to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.") + +/obj/machinery/power/generator/proc/get_menu(include_link = 1) + var/t = "" + if(!powernet) + t += "Unable to connect to the power network!" + t += "
    Retry" + else if(cold_circ && hot_circ) + var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() + var/datum/gas_mixture/cold_circ_air2 = cold_circ.get_inlet_air() + var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() + var/datum/gas_mixture/hot_circ_air2 = hot_circ.get_inlet_air() + + t += "
    " + + t += "Output: [round(lastgen)] W" + + t += "
    " + + t += "Cold loop
    " + t += "Temperature Inlet: [round(cold_circ_air2.temperature, 0.1)] K / Outlet: [round(cold_circ_air1.temperature, 0.1)] K
    " + t += "Pressure Inlet: [round(cold_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(cold_circ_air1.return_pressure(), 0.1)] kPa
    " + + t += "Hot loop
    " + t += "Temperature Inlet: [round(hot_circ_air2.temperature, 0.1)] K / Outlet: [round(hot_circ_air1.temperature, 0.1)] K
    " + t += "Pressure Inlet: [round(hot_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(hot_circ_air1.return_pressure(), 0.1)] kPa
    " + + t += "
    " + else + t += "Unable to locate all parts!" + t += "
    Retry" + if(include_link) + t += "
    Close" + + return t + +/obj/machinery/power/generator/interact(mob/user) + user.set_machine(src) + + var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300) + popup.set_content(get_menu()) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + return 1 + +/obj/machinery/power/generator/Topic(href, href_list) + if(..()) + return 0 + if( href_list["close"] ) + usr << browse(null, "window=teg") + usr.unset_machine() + return 0 + if( href_list["check"] ) + if(!powernet || !cold_circ || !hot_circ) + connect() + return 1 + +/obj/machinery/power/generator/power_change() + ..() + update_icon() diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index a5b83d8472c8..cf04c8a66f8f 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -3,7 +3,7 @@ // Gravity Generator // -var/list/gravity_generators = list() // We will keep track of this by adding new gravity generators to the list, and keying it with the z level. +GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding new gravity generators to the list, and keying it with the z level. #define GRAV_POWER_IDLE 0 #define GRAV_POWER_UP 1 @@ -388,19 +388,19 @@ var/list/gravity_generators = list() // We will keep track of this by adding new var/turf/T = get_turf(src) if(!T) return 0 - if(gravity_generators["[T.z]"]) - return length(gravity_generators["[T.z]"]) + if(GLOB.gravity_generators["[T.z]"]) + return length(GLOB.gravity_generators["[T.z]"]) return 0 /obj/machinery/gravity_generator/main/proc/update_list() var/turf/T = get_turf(src.loc) if(T) - if(!gravity_generators["[T.z]"]) - gravity_generators["[T.z]"] = list() + if(!GLOB.gravity_generators["[T.z]"]) + GLOB.gravity_generators["[T.z]"] = list() if(on) - gravity_generators["[T.z]"] |= src + GLOB.gravity_generators["[T.z]"] |= src else - gravity_generators["[T.z]"] -= src + GLOB.gravity_generators["[T.z]"] -= src // Misc diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index d02263d8ed83..78c2561ae7d4 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -1,740 +1,740 @@ -// The lighting system -// -// consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) - - -// status values shared between lighting fixtures and items -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - -/obj/machinery/light_construct - name = "light fixture frame" - desc = "A light fixture under construction." - icon = 'icons/obj/lighting.dmi' - icon_state = "tube-construct-stage1" - anchored = 1 - layer = 5 - max_integrity = 200 - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - var/stage = 1 - var/fixture_type = "tube" - var/sheets_refunded = 2 - var/obj/machinery/light/newlight = null - -/obj/machinery/light_construct/New() - ..() - if(fixture_type == "bulb") - icon_state = "bulb-construct-stage1" - -/obj/machinery/light_construct/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - switch(stage) - if(1) - . += "It's an empty frame." - if(2) - . += "It's wired." - if(3) - . += "The casing is closed." - -/obj/machinery/light_construct/attackby(obj/item/W as obj, mob/living/user as mob, params) - src.add_fingerprint(user) - if(istype(W, /obj/item/wrench)) - if(src.stage == 1) - playsound(src.loc, W.usesound, 75, 1) - to_chat(usr, "You begin deconstructing [src].") - if(!do_after(usr, 30 * W.toolspeed, target = src)) - return - new /obj/item/stack/sheet/metal( get_turf(src.loc), sheets_refunded ) - user.visible_message("[user.name] deconstructs [src].", \ - "You deconstruct [src].", "You hear a noise.") - playsound(src.loc, W.usesound, 75, 1) - qdel(src) - if(src.stage == 2) - to_chat(usr, "You have to remove the wires first.") - return - - if(src.stage == 3) - to_chat(usr, "You have to unscrew the case first.") - return - - if(istype(W, /obj/item/wirecutters)) - if(src.stage != 2) return - src.stage = 1 - switch(fixture_type) - if("tube") - src.icon_state = "tube-construct-stage1" - if("bulb") - src.icon_state = "bulb-construct-stage1" - new /obj/item/stack/cable_coil(get_turf(src.loc), 1, paramcolor = COLOR_RED) - user.visible_message("[user.name] removes the wiring from [src].", \ - "You remove the wiring from [src].", "You hear a noise.") - playsound(loc, W.usesound, 100, 1) - return - - if(istype(W, /obj/item/stack/cable_coil)) - if(src.stage != 1) return - var/obj/item/stack/cable_coil/coil = W - coil.use(1) - switch(fixture_type) - if("tube") - src.icon_state = "tube-construct-stage2" - if("bulb") - src.icon_state = "bulb-construct-stage2" - src.stage = 2 - playsound(loc, coil.usesound, 50, 1) - user.visible_message("[user.name] adds wires to [src].", \ - "You add wires to [src].") - return - - if(istype(W, /obj/item/screwdriver)) - if(src.stage == 2) - switch(fixture_type) - if("tube") - src.icon_state = "tube-empty" - if("bulb") - src.icon_state = "bulb-empty" - src.stage = 3 - user.visible_message("[user.name] closes [src]'s casing.", \ - "You close [src]'s casing.", "You hear a noise.") - playsound(src.loc, W.usesound, 75, 1) - - switch(fixture_type) - - if("tube") - newlight = new /obj/machinery/light/built(src.loc) - if("bulb") - newlight = new /obj/machinery/light/small/built(src.loc) - - newlight.dir = src.dir - src.transfer_fingerprints_to(newlight) - qdel(src) - return - else - return ..() - -/obj/machinery/light_construct/blob_act(obj/structure/blob/B) - if(B && B.loc == loc) - qdel(src) - -/obj/machinery/light_construct/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, sheets_refunded) - qdel(src) - -/obj/machinery/light_construct/small - name = "small light fixture frame" - desc = "A small light fixture under construction." - icon = 'icons/obj/lighting.dmi' - icon_state = "bulb-construct-stage1" - anchored = 1 - layer = 5 - stage = 1 - fixture_type = "bulb" - sheets_refunded = 1 - - -// the standard tube light fixture -/obj/machinery/light - name = "light fixture" - icon = 'icons/obj/lighting.dmi' - var/base_state = "tube" // base description and icon_state - icon_state = "tube1" - desc = "A lighting fixture." - anchored = 1 - layer = 5 // They were appearing under mobs which is a little weird - Ostaf - max_integrity = 100 - use_power = ACTIVE_POWER_USE - idle_power_usage = 2 - active_power_usage = 20 - power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list - var/on = FALSE // 1 if on, 0 if off - var/on_gs = 0 - var/static_power_used = 0 - var/brightness_range = 8 // luminosity when on, also used in power calculation - var/brightness_power = 1 - var/brightness_color = "#FFFFFF" - var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN - var/flickering = 0 - var/light_type = /obj/item/light/tube // the type of light item - var/fitting = "tube" - var/switchcount = 0 // count of number of times switched on/off - // this is used to calc the probability the light burns out - - var/rigged = 0 // true if rigged to explode - var/lightmaterials = list(MAT_GLASS=100) //stores the materials the light is made of to stop infinite glass exploit - - var/nightshift_enabled = FALSE //Currently in night shift mode? - var/nightshift_allowed = TRUE //Set to FALSE to never let this light get switched to night mode. - var/nightshift_light_range = 8 - var/nightshift_light_power = 0.45 - var/nightshift_light_color = "#FFDDCC" - -// the smaller bulb light fixture - -/obj/machinery/light/small - icon_state = "bulb1" - base_state = "bulb" - fitting = "bulb" - brightness_range = 4 - brightness_color = "#a0a080" - nightshift_light_range = 4 - desc = "A small lighting fixture." - light_type = /obj/item/light/bulb - -/obj/machinery/light/spot - name = "spotlight" - fitting = "large tube" - light_type = /obj/item/light/tube/large - brightness_range = 12 - brightness_power = 4 - -/obj/machinery/light/built/New() - status = LIGHT_EMPTY - update(0) - ..() - -/obj/machinery/light/small/built/New() - status = LIGHT_EMPTY - update(0) - ..() - -// create a new lighting fixture -/obj/machinery/light/New() - ..() - spawn(2) - var/area/A = get_area(src) - if(A && !A.requires_power) - on = 1 - - switch(fitting) - if("tube") - brightness_range = 8 - if(prob(2)) - break_light_tube(1) - if("bulb") - brightness_range = 4 - brightness_color = "#a0a080" - if(prob(5)) - break_light_tube() - spawn(1) - update(0) - -/obj/machinery/light/Destroy() - var/area/A = get_area(src) - if(A) - on = FALSE -// A.update_lights() - return ..() - -/obj/machinery/light/update_icon() - - switch(status) // set icon_states - if(LIGHT_OK) - icon_state = "[base_state][on]" - if(LIGHT_EMPTY) - icon_state = "[base_state]-empty" - on = FALSE - if(LIGHT_BURNED) - icon_state = "[base_state]-burned" - on = FALSE - if(LIGHT_BROKEN) - icon_state = "[base_state]-broken" - on = FALSE - return - -/obj/machinery/light/get_spooked() - flicker() - -// update the icon_state and luminosity of the light depending on its state -/obj/machinery/light/proc/update(var/trigger = TRUE) - switch(status) - if(LIGHT_BROKEN, LIGHT_BURNED, LIGHT_EMPTY) - on = FALSE - update_icon() - if(on) - var/BR = nightshift_enabled ? nightshift_light_range : brightness_range - var/PO = nightshift_enabled ? nightshift_light_power : brightness_power - var/CO = nightshift_enabled ? nightshift_light_color : brightness_color - var/matching = light_range == BR && light_power == PO && light_color == CO - if(!matching) - switchcount++ - if(rigged) - if(status == LIGHT_OK && trigger) - log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") - explode() - else if(prob(min(60, switchcount * switchcount * 0.01))) - if(status == LIGHT_OK && trigger) - status = LIGHT_BURNED - icon_state = "[base_state]-burned" - on = FALSE - set_light(0) - else - use_power = ACTIVE_POWER_USE - set_light(BR, PO, CO) - else - use_power = IDLE_POWER_USE - set_light(0) - - active_power_usage = (brightness_range * 10) - if(on != on_gs) - on_gs = on - if(on) - static_power_used = brightness_range * 20 //20W per unit luminosity - addStaticPower(static_power_used, STATIC_LIGHT) - else - removeStaticPower(static_power_used, STATIC_LIGHT) - -// attempt to set the light's on/off status -// will not switch on if broken/burned/empty -/obj/machinery/light/proc/seton(var/s) - on = (s && status == LIGHT_OK) - update() - -// examine verb -/obj/machinery/light/examine(mob/user) - . = ..() - if(in_range(user, src)) - switch(status) - if(LIGHT_OK) - . += "[desc] It is turned [on? "on" : "off"]." - if(LIGHT_EMPTY) - . += "[desc] The [fitting] has been removed." - if(LIGHT_BURNED) - . += "[desc] The [fitting] is burnt out." - if(LIGHT_BROKEN) - . += "[desc] The [fitting] has been smashed." - - - -// attack with item - insert light (if right type), otherwise try to break the light - -/obj/machinery/light/attackby(obj/item/W, mob/living/user, params) - user.changeNext_move(CLICK_CD_MELEE) // This is an ugly hack and I hate it forever - //Light replacer code - if(istype(W, /obj/item/lightreplacer)) - var/obj/item/lightreplacer/LR = W - LR.ReplaceLight(src, user) - - // attempt to insert light - else if(istype(W, /obj/item/light)) - if(status != LIGHT_EMPTY) - to_chat(user, "There is a [fitting] already inserted.") - else - src.add_fingerprint(user) - var/obj/item/light/L = W - if(istype(L, light_type)) - status = L.status - to_chat(user, "You insert the [L.name].") - switchcount = L.switchcount - rigged = L.rigged - brightness_range = L.brightness_range - brightness_power = L.brightness_power - brightness_color = L.brightness_color - lightmaterials = L.materials - on = has_power() - update() - - user.drop_item() //drop the item to update overlays and such - qdel(L) - - if(on && rigged) - - log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") - - explode() - else - to_chat(user, "This type of light requires a [fitting].") - return - - // attempt to break the light - //If xenos decide they want to smash a light bulb with a toolbox, who am I to stop them? /N - - else if(status != LIGHT_BROKEN && status != LIGHT_EMPTY) - - user.do_attack_animation(src) - if(prob(1+W.force * 5)) - - to_chat(user, "You hit the light, and it smashes!") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] smashed the light!", 3, "You hear a tinkle of breaking glass", 2) - if(on && (W.flags & CONDUCT)) - if(prob(12)) - electrocute_mob(user, get_area(src), src, 0.3, TRUE) - break_light_tube() - - else - user.visible_message("[user.name] hits the light.") - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - - // attempt to stick weapon into light socket - else if(status == LIGHT_EMPTY) - if(istype(W, /obj/item/screwdriver)) //If it's a screwdriver open it. - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] opens [src]'s casing.", \ - "You open [src]'s casing.", "You hear a noise.") - deconstruct() - return - - to_chat(user, "You stick \the [W] into the light socket!") - if(has_power() && (W.flags & CONDUCT)) - do_sparks(3, 1, src) - if(prob(75)) - electrocute_mob(user, get_area(src), src, rand(0.7, 1), TRUE) - else - return ..() - -/obj/machinery/light/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/obj/machinery/light_construct/newlight = null - var/cur_stage = 2 - if(!disassembled) - cur_stage = 1 - switch(fitting) - if("tube") - newlight = new /obj/machinery/light_construct(src.loc) - newlight.icon_state = "tube-construct-stage2" - - if("bulb") - newlight = new /obj/machinery/light_construct/small(src.loc) - newlight.icon_state = "bulb-construct-stage2" - newlight.setDir(src.dir) - newlight.stage = cur_stage - if(!disassembled) - newlight.obj_integrity = newlight.max_integrity * 0.5 - if(status != LIGHT_BROKEN) - break_light_tube() - if(status != LIGHT_EMPTY) - drop_light_tube() - new /obj/item/stack/cable_coil(loc, 1, "red") - transfer_fingerprints_to(newlight) - qdel(src) - -/obj/machinery/light/attacked_by(obj/item/I, mob/living/user) - ..() - if(status == LIGHT_BROKEN || status == LIGHT_EMPTY) - if(on && (I.flags & CONDUCT)) - if(prob(12)) - electrocute_mob(user, get_area(src), src, 0.3, TRUE) - -/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) - . = ..() - if(. && !QDELETED(src)) - if(prob(damage_amount * 5)) - break_light_tube() - -/obj/machinery/light/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - switch(status) - if(LIGHT_EMPTY) - playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) - if(LIGHT_BROKEN) - playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 90, TRUE) - else - playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -// returns whether this light has power -// true if area has power and lightswitch is on -/obj/machinery/light/proc/has_power() - var/area/A = get_area(src) - return A.lightswitch && A.power_light - -/obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) - if(flickering) return - flickering = 1 - spawn(0) - if(on && status == LIGHT_OK) - for(var/i = 0; i < amount; i++) - if(status != LIGHT_OK) break - on = !on - update(0) - sleep(rand(5, 15)) - on = (status == LIGHT_OK) - update(0) - flickering = 0 - -// ai attack - make lights flicker, because why not -/obj/machinery/light/attack_ai(mob/user) - src.flicker(1) - -// attack with hand - remove tube/bulb -// if hands aren't protected and the light is on, burn the player - -/obj/machinery/light/attack_hand(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - add_fingerprint(user) - - if(status == LIGHT_EMPTY) - to_chat(user, "There is no [fitting] in this light.") - return - - // make it burn hands if not wearing fire-insulated gloves - if(on) - var/prot = 0 - var/mob/living/carbon/human/H = user - - if(istype(H)) - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.max_heat_protection_temperature) - prot = (G.max_heat_protection_temperature > 360) - else - prot = 1 - - if(prot > 0 || (HEATRES in user.mutations)) - to_chat(user, "You remove the light [fitting]") - else if(TK in user.mutations) - to_chat(user, "You telekinetically remove the light [fitting].") - else - if(user.a_intent == INTENT_DISARM || user.a_intent == INTENT_GRAB) - to_chat(user, "You try to remove the light [fitting], but you burn your hand on it!") - - var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_hand") - if(affecting.receive_damage( 0, 5 )) // 5 burn damage - H.UpdateDamageIcon() - H.updatehealth() - return - else - to_chat(user, "You try to remove the light [fitting], but it's too hot to touch!") - return - else - to_chat(user, "You remove the light [fitting].") - // create a light tube/bulb item and put it in the user's hand - drop_light_tube(user) - -// break the light and make sparks if was on - -/obj/machinery/light/proc/drop_light_tube(mob/user) - var/obj/item/light/L = new light_type() - L.status = status - L.rigged = rigged - L.brightness_range = brightness_range - L.brightness_power = brightness_power - L.brightness_color = brightness_color - L.materials = lightmaterials - - // light item inherits the switchcount, then zero it - L.switchcount = switchcount - switchcount = 0 - - L.update() - L.forceMove(loc) - - if(user) //puts it in our active hand - L.add_fingerprint(user) - user.put_in_active_hand(L) - - status = LIGHT_EMPTY - update() - return L - -/obj/machinery/light/attack_tk(mob/user) - if(status == LIGHT_EMPTY) - to_chat(user, "There is no [fitting] in this light.") - return - - to_chat(user, "You telekinetically remove the light [fitting].") - // create a light tube/bulb item and put it in the user's hand - var/obj/item/light/L = drop_light_tube() - L.attack_tk(user) - -/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = 0, overloaded = 0) - if(status == LIGHT_EMPTY || status == LIGHT_BROKEN) - return - - if(!skip_sound_and_sparks) - if(status == LIGHT_OK || status == LIGHT_BURNED) - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - if(on || overloaded) - do_sparks(3, 1, src) - status = LIGHT_BROKEN - update() - -/obj/machinery/light/proc/fix() - if(status == LIGHT_OK) - return - status = LIGHT_OK - on = 1 - update() - -/obj/machinery/light/tesla_act(power, explosive = FALSE) - if(explosive) - explosion(loc,0,0,0,flame_range = 5, adminlog = 0) - qdel(src) - -// timed process -// use power - -// called when area power state changes -/obj/machinery/light/power_change() - var/area/A = get_area(src) - if(A) - seton(A.lightswitch && A.power_light) - -// called when on fire - -/obj/machinery/light/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(prob(max(0, exposed_temperature - 673))) //0% at <400C, 100% at >500C - break_light_tube() - -// explode the light - -/obj/machinery/light/proc/explode() - var/turf/T = get_turf(src.loc) - spawn(0) - break_light_tube() // break it first to give a warning - sleep(2) - explosion(T, 0, 0, 2, 2) - sleep(1) - qdel(src) - -// the light item -// can be tube or bulb subtypes -// will fit into empty /obj/machinery/light of the corresponding type - -/obj/item/light - icon = 'icons/obj/lighting.dmi' - force = 2 - throwforce = 5 - w_class = WEIGHT_CLASS_TINY - var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN - var/base_state - var/switchcount = 0 // number of times switched - materials = list(MAT_GLASS=100) - var/rigged = 0 // true if rigged to explode - var/brightness_range = 2 //how much light it gives off - var/brightness_power = 1 - var/brightness_color = null - -/obj/item/light/ComponentInitialize() - . = ..() - AddComponent(/datum/component/caltrop, force) - -/obj/item/light/Crossed(mob/living/L) - if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || L.flying) - return - playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) - if(status == LIGHT_BURNED || status == LIGHT_OK) - shatter() - return ..() - -/obj/item/light/tube - name = "light tube" - desc = "A replacement light tube." - icon_state = "ltube" - base_state = "ltube" - item_state = "c_tube" - brightness_range = 8 - -/obj/item/light/tube/large - w_class = WEIGHT_CLASS_SMALL - name = "large light tube" - brightness_range = 15 - brightness_power = 2 - -/obj/item/light/bulb - name = "light bulb" - desc = "A replacement light bulb." - icon_state = "lbulb" - base_state = "lbulb" - item_state = "contvapour" - brightness_range = 5 - brightness_color = "#a0a080" - -/obj/item/light/throw_impact(atom/hit_atom) - ..() - shatter() - -/obj/item/light/bulb/fire - name = "fire bulb" - desc = "A replacement fire bulb." - icon_state = "fbulb" - base_state = "fbulb" - item_state = "egg4" - brightness_range = 5 - -// update the icon state and description of the light - -/obj/item/light/proc/update() - switch(status) - if(LIGHT_OK) - icon_state = base_state - desc = "A replacement [name]." - if(LIGHT_BURNED) - icon_state = "[base_state]-burned" - desc = "A burnt-out [name]." - if(LIGHT_BROKEN) - icon_state = "[base_state]-broken" - desc = "A broken [name]." - - -/obj/item/light/New() - ..() - switch(name) - if("light tube") - brightness_range = rand(6,9) - if("light bulb") - brightness_range = rand(4,6) - update() - - -// attack bulb/tube with object -// if a syringe, can inject plasma to make it explode -/obj/item/light/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/syringe)) - var/obj/item/reagent_containers/syringe/S = I - - to_chat(user, "You inject the solution into the [src].") - - if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) - - log_admin("LOG: [key_name(user)] injected a light with plasma, rigging it to explode.") - message_admins("LOG: [key_name_admin(user)] injected a light with plasma, rigging it to explode.") - - rigged = 1 - - S.reagents.clear_reagents() - else - return ..() - -/obj/item/light/attack(mob/living/M, mob/living/user, def_zone) - ..() - shatter() - -/obj/item/light/attack_obj(obj/O, mob/living/user) - ..() - shatter() - -/obj/item/light/proc/shatter() - if(status == LIGHT_OK || status == LIGHT_BURNED) - src.visible_message("[name] shatters.","You hear a small glass object shatter.") - status = LIGHT_BROKEN - force = 5 - sharp = 1 - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - update() - -/obj/item/light/suicide_act(mob/living/carbon/human/user) - user.visible_message("[user] touches [src], burning [user.p_their()] hands off!", "You touch [src], burning your hands off!") - - for(var/oname in list("l_hand", "r_hand")) - var/obj/item/organ/external/limb = user.get_organ(oname) - if(limb) - limb.droplimb(0, DROPLIMB_BURN) - return FIRELOSS - -/obj/machinery/light/extinguish_light() - on = FALSE - visible_message("[src] flickers and falls dark.") - update(0) +// The lighting system +// +// consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) + + +// status values shared between lighting fixtures and items +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + +/obj/machinery/light_construct + name = "light fixture frame" + desc = "A light fixture under construction." + icon = 'icons/obj/lighting.dmi' + icon_state = "tube-construct-stage1" + anchored = 1 + layer = 5 + max_integrity = 200 + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + var/stage = 1 + var/fixture_type = "tube" + var/sheets_refunded = 2 + var/obj/machinery/light/newlight = null + +/obj/machinery/light_construct/New() + ..() + if(fixture_type == "bulb") + icon_state = "bulb-construct-stage1" + +/obj/machinery/light_construct/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + switch(stage) + if(1) + . += "It's an empty frame." + if(2) + . += "It's wired." + if(3) + . += "The casing is closed." + +/obj/machinery/light_construct/attackby(obj/item/W as obj, mob/living/user as mob, params) + src.add_fingerprint(user) + if(istype(W, /obj/item/wrench)) + if(src.stage == 1) + playsound(src.loc, W.usesound, 75, 1) + to_chat(usr, "You begin deconstructing [src].") + if(!do_after(usr, 30 * W.toolspeed, target = src)) + return + new /obj/item/stack/sheet/metal( get_turf(src.loc), sheets_refunded ) + user.visible_message("[user.name] deconstructs [src].", \ + "You deconstruct [src].", "You hear a noise.") + playsound(src.loc, W.usesound, 75, 1) + qdel(src) + if(src.stage == 2) + to_chat(usr, "You have to remove the wires first.") + return + + if(src.stage == 3) + to_chat(usr, "You have to unscrew the case first.") + return + + if(istype(W, /obj/item/wirecutters)) + if(src.stage != 2) return + src.stage = 1 + switch(fixture_type) + if("tube") + src.icon_state = "tube-construct-stage1" + if("bulb") + src.icon_state = "bulb-construct-stage1" + new /obj/item/stack/cable_coil(get_turf(src.loc), 1, paramcolor = COLOR_RED) + user.visible_message("[user.name] removes the wiring from [src].", \ + "You remove the wiring from [src].", "You hear a noise.") + playsound(loc, W.usesound, 100, 1) + return + + if(istype(W, /obj/item/stack/cable_coil)) + if(src.stage != 1) return + var/obj/item/stack/cable_coil/coil = W + coil.use(1) + switch(fixture_type) + if("tube") + src.icon_state = "tube-construct-stage2" + if("bulb") + src.icon_state = "bulb-construct-stage2" + src.stage = 2 + playsound(loc, coil.usesound, 50, 1) + user.visible_message("[user.name] adds wires to [src].", \ + "You add wires to [src].") + return + + if(istype(W, /obj/item/screwdriver)) + if(src.stage == 2) + switch(fixture_type) + if("tube") + src.icon_state = "tube-empty" + if("bulb") + src.icon_state = "bulb-empty" + src.stage = 3 + user.visible_message("[user.name] closes [src]'s casing.", \ + "You close [src]'s casing.", "You hear a noise.") + playsound(src.loc, W.usesound, 75, 1) + + switch(fixture_type) + + if("tube") + newlight = new /obj/machinery/light/built(src.loc) + if("bulb") + newlight = new /obj/machinery/light/small/built(src.loc) + + newlight.dir = src.dir + src.transfer_fingerprints_to(newlight) + qdel(src) + return + else + return ..() + +/obj/machinery/light_construct/blob_act(obj/structure/blob/B) + if(B && B.loc == loc) + qdel(src) + +/obj/machinery/light_construct/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, sheets_refunded) + qdel(src) + +/obj/machinery/light_construct/small + name = "small light fixture frame" + desc = "A small light fixture under construction." + icon = 'icons/obj/lighting.dmi' + icon_state = "bulb-construct-stage1" + anchored = 1 + layer = 5 + stage = 1 + fixture_type = "bulb" + sheets_refunded = 1 + + +// the standard tube light fixture +/obj/machinery/light + name = "light fixture" + icon = 'icons/obj/lighting.dmi' + var/base_state = "tube" // base description and icon_state + icon_state = "tube1" + desc = "A lighting fixture." + anchored = 1 + layer = 5 // They were appearing under mobs which is a little weird - Ostaf + max_integrity = 100 + use_power = ACTIVE_POWER_USE + idle_power_usage = 2 + active_power_usage = 20 + power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list + var/on = FALSE // 1 if on, 0 if off + var/on_gs = 0 + var/static_power_used = 0 + var/brightness_range = 8 // luminosity when on, also used in power calculation + var/brightness_power = 1 + var/brightness_color = "#FFFFFF" + var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN + var/flickering = 0 + var/light_type = /obj/item/light/tube // the type of light item + var/fitting = "tube" + var/switchcount = 0 // count of number of times switched on/off + // this is used to calc the probability the light burns out + + var/rigged = 0 // true if rigged to explode + var/lightmaterials = list(MAT_GLASS=100) //stores the materials the light is made of to stop infinite glass exploit + + var/nightshift_enabled = FALSE //Currently in night shift mode? + var/nightshift_allowed = TRUE //Set to FALSE to never let this light get switched to night mode. + var/nightshift_light_range = 8 + var/nightshift_light_power = 0.45 + var/nightshift_light_color = "#FFDDCC" + +// the smaller bulb light fixture + +/obj/machinery/light/small + icon_state = "bulb1" + base_state = "bulb" + fitting = "bulb" + brightness_range = 4 + brightness_color = "#a0a080" + nightshift_light_range = 4 + desc = "A small lighting fixture." + light_type = /obj/item/light/bulb + +/obj/machinery/light/spot + name = "spotlight" + fitting = "large tube" + light_type = /obj/item/light/tube/large + brightness_range = 12 + brightness_power = 4 + +/obj/machinery/light/built/New() + status = LIGHT_EMPTY + update(0) + ..() + +/obj/machinery/light/small/built/New() + status = LIGHT_EMPTY + update(0) + ..() + +// create a new lighting fixture +/obj/machinery/light/New() + ..() + spawn(2) + var/area/A = get_area(src) + if(A && !A.requires_power) + on = 1 + + switch(fitting) + if("tube") + brightness_range = 8 + if(prob(2)) + break_light_tube(1) + if("bulb") + brightness_range = 4 + brightness_color = "#a0a080" + if(prob(5)) + break_light_tube() + spawn(1) + update(0) + +/obj/machinery/light/Destroy() + var/area/A = get_area(src) + if(A) + on = FALSE +// A.update_lights() + return ..() + +/obj/machinery/light/update_icon() + + switch(status) // set icon_states + if(LIGHT_OK) + icon_state = "[base_state][on]" + if(LIGHT_EMPTY) + icon_state = "[base_state]-empty" + on = FALSE + if(LIGHT_BURNED) + icon_state = "[base_state]-burned" + on = FALSE + if(LIGHT_BROKEN) + icon_state = "[base_state]-broken" + on = FALSE + return + +/obj/machinery/light/get_spooked() + flicker() + +// update the icon_state and luminosity of the light depending on its state +/obj/machinery/light/proc/update(var/trigger = TRUE) + switch(status) + if(LIGHT_BROKEN, LIGHT_BURNED, LIGHT_EMPTY) + on = FALSE + update_icon() + if(on) + var/BR = nightshift_enabled ? nightshift_light_range : brightness_range + var/PO = nightshift_enabled ? nightshift_light_power : brightness_power + var/CO = nightshift_enabled ? nightshift_light_color : brightness_color + var/matching = light_range == BR && light_power == PO && light_color == CO + if(!matching) + switchcount++ + if(rigged) + if(status == LIGHT_OK && trigger) + log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") + explode() + else if(prob(min(60, switchcount * switchcount * 0.01))) + if(status == LIGHT_OK && trigger) + status = LIGHT_BURNED + icon_state = "[base_state]-burned" + on = FALSE + set_light(0) + else + use_power = ACTIVE_POWER_USE + set_light(BR, PO, CO) + else + use_power = IDLE_POWER_USE + set_light(0) + + active_power_usage = (brightness_range * 10) + if(on != on_gs) + on_gs = on + if(on) + static_power_used = brightness_range * 20 //20W per unit luminosity + addStaticPower(static_power_used, STATIC_LIGHT) + else + removeStaticPower(static_power_used, STATIC_LIGHT) + +// attempt to set the light's on/off status +// will not switch on if broken/burned/empty +/obj/machinery/light/proc/seton(var/s) + on = (s && status == LIGHT_OK) + update() + +// examine verb +/obj/machinery/light/examine(mob/user) + . = ..() + if(in_range(user, src)) + switch(status) + if(LIGHT_OK) + . += "[desc] It is turned [on? "on" : "off"]." + if(LIGHT_EMPTY) + . += "[desc] The [fitting] has been removed." + if(LIGHT_BURNED) + . += "[desc] The [fitting] is burnt out." + if(LIGHT_BROKEN) + . += "[desc] The [fitting] has been smashed." + + + +// attack with item - insert light (if right type), otherwise try to break the light + +/obj/machinery/light/attackby(obj/item/W, mob/living/user, params) + user.changeNext_move(CLICK_CD_MELEE) // This is an ugly hack and I hate it forever + //Light replacer code + if(istype(W, /obj/item/lightreplacer)) + var/obj/item/lightreplacer/LR = W + LR.ReplaceLight(src, user) + + // attempt to insert light + else if(istype(W, /obj/item/light)) + if(status != LIGHT_EMPTY) + to_chat(user, "There is a [fitting] already inserted.") + else + src.add_fingerprint(user) + var/obj/item/light/L = W + if(istype(L, light_type)) + status = L.status + to_chat(user, "You insert the [L.name].") + switchcount = L.switchcount + rigged = L.rigged + brightness_range = L.brightness_range + brightness_power = L.brightness_power + brightness_color = L.brightness_color + lightmaterials = L.materials + on = has_power() + update() + + user.drop_item() //drop the item to update overlays and such + qdel(L) + + if(on && rigged) + + log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") + + explode() + else + to_chat(user, "This type of light requires a [fitting].") + return + + // attempt to break the light + //If xenos decide they want to smash a light bulb with a toolbox, who am I to stop them? /N + + else if(status != LIGHT_BROKEN && status != LIGHT_EMPTY) + + user.do_attack_animation(src) + if(prob(1+W.force * 5)) + + to_chat(user, "You hit the light, and it smashes!") + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] smashed the light!", 3, "You hear a tinkle of breaking glass", 2) + if(on && (W.flags & CONDUCT)) + if(prob(12)) + electrocute_mob(user, get_area(src), src, 0.3, TRUE) + break_light_tube() + + else + user.visible_message("[user.name] hits the light.") + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + + // attempt to stick weapon into light socket + else if(status == LIGHT_EMPTY) + if(istype(W, /obj/item/screwdriver)) //If it's a screwdriver open it. + playsound(src.loc, W.usesound, 75, 1) + user.visible_message("[user.name] opens [src]'s casing.", \ + "You open [src]'s casing.", "You hear a noise.") + deconstruct() + return + + to_chat(user, "You stick \the [W] into the light socket!") + if(has_power() && (W.flags & CONDUCT)) + do_sparks(3, 1, src) + if(prob(75)) + electrocute_mob(user, get_area(src), src, rand(0.7, 1), TRUE) + else + return ..() + +/obj/machinery/light/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/obj/machinery/light_construct/newlight = null + var/cur_stage = 2 + if(!disassembled) + cur_stage = 1 + switch(fitting) + if("tube") + newlight = new /obj/machinery/light_construct(src.loc) + newlight.icon_state = "tube-construct-stage2" + + if("bulb") + newlight = new /obj/machinery/light_construct/small(src.loc) + newlight.icon_state = "bulb-construct-stage2" + newlight.setDir(src.dir) + newlight.stage = cur_stage + if(!disassembled) + newlight.obj_integrity = newlight.max_integrity * 0.5 + if(status != LIGHT_BROKEN) + break_light_tube() + if(status != LIGHT_EMPTY) + drop_light_tube() + new /obj/item/stack/cable_coil(loc, 1, "red") + transfer_fingerprints_to(newlight) + qdel(src) + +/obj/machinery/light/attacked_by(obj/item/I, mob/living/user) + ..() + if(status == LIGHT_BROKEN || status == LIGHT_EMPTY) + if(on && (I.flags & CONDUCT)) + if(prob(12)) + electrocute_mob(user, get_area(src), src, 0.3, TRUE) + +/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) + . = ..() + if(. && !QDELETED(src)) + if(prob(damage_amount * 5)) + break_light_tube() + +/obj/machinery/light/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + switch(status) + if(LIGHT_EMPTY) + playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) + if(LIGHT_BROKEN) + playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 90, TRUE) + else + playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +// returns whether this light has power +// true if area has power and lightswitch is on +/obj/machinery/light/proc/has_power() + var/area/A = get_area(src) + return A.lightswitch && A.power_light + +/obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) + if(flickering) return + flickering = 1 + spawn(0) + if(on && status == LIGHT_OK) + for(var/i = 0; i < amount; i++) + if(status != LIGHT_OK) break + on = !on + update(0) + sleep(rand(5, 15)) + on = (status == LIGHT_OK) + update(0) + flickering = 0 + +// ai attack - make lights flicker, because why not +/obj/machinery/light/attack_ai(mob/user) + src.flicker(1) + +// attack with hand - remove tube/bulb +// if hands aren't protected and the light is on, burn the player + +/obj/machinery/light/attack_hand(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + add_fingerprint(user) + + if(status == LIGHT_EMPTY) + to_chat(user, "There is no [fitting] in this light.") + return + + // make it burn hands if not wearing fire-insulated gloves + if(on) + var/prot = 0 + var/mob/living/carbon/human/H = user + + if(istype(H)) + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.max_heat_protection_temperature) + prot = (G.max_heat_protection_temperature > 360) + else + prot = 1 + + if(prot > 0 || (HEATRES in user.mutations)) + to_chat(user, "You remove the light [fitting]") + else if(TK in user.mutations) + to_chat(user, "You telekinetically remove the light [fitting].") + else + if(user.a_intent == INTENT_DISARM || user.a_intent == INTENT_GRAB) + to_chat(user, "You try to remove the light [fitting], but you burn your hand on it!") + + var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_hand") + if(affecting.receive_damage( 0, 5 )) // 5 burn damage + H.UpdateDamageIcon() + H.updatehealth() + return + else + to_chat(user, "You try to remove the light [fitting], but it's too hot to touch!") + return + else + to_chat(user, "You remove the light [fitting].") + // create a light tube/bulb item and put it in the user's hand + drop_light_tube(user) + +// break the light and make sparks if was on + +/obj/machinery/light/proc/drop_light_tube(mob/user) + var/obj/item/light/L = new light_type() + L.status = status + L.rigged = rigged + L.brightness_range = brightness_range + L.brightness_power = brightness_power + L.brightness_color = brightness_color + L.materials = lightmaterials + + // light item inherits the switchcount, then zero it + L.switchcount = switchcount + switchcount = 0 + + L.update() + L.forceMove(loc) + + if(user) //puts it in our active hand + L.add_fingerprint(user) + user.put_in_active_hand(L) + + status = LIGHT_EMPTY + update() + return L + +/obj/machinery/light/attack_tk(mob/user) + if(status == LIGHT_EMPTY) + to_chat(user, "There is no [fitting] in this light.") + return + + to_chat(user, "You telekinetically remove the light [fitting].") + // create a light tube/bulb item and put it in the user's hand + var/obj/item/light/L = drop_light_tube() + L.attack_tk(user) + +/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = 0, overloaded = 0) + if(status == LIGHT_EMPTY || status == LIGHT_BROKEN) + return + + if(!skip_sound_and_sparks) + if(status == LIGHT_OK || status == LIGHT_BURNED) + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + if(on || overloaded) + do_sparks(3, 1, src) + status = LIGHT_BROKEN + update() + +/obj/machinery/light/proc/fix() + if(status == LIGHT_OK) + return + status = LIGHT_OK + on = 1 + update() + +/obj/machinery/light/tesla_act(power, explosive = FALSE) + if(explosive) + explosion(loc,0,0,0,flame_range = 5, adminlog = 0) + qdel(src) + +// timed process +// use power + +// called when area power state changes +/obj/machinery/light/power_change() + var/area/A = get_area(src) + if(A) + seton(A.lightswitch && A.power_light) + +// called when on fire + +/obj/machinery/light/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(prob(max(0, exposed_temperature - 673))) //0% at <400C, 100% at >500C + break_light_tube() + +// explode the light + +/obj/machinery/light/proc/explode() + var/turf/T = get_turf(src.loc) + spawn(0) + break_light_tube() // break it first to give a warning + sleep(2) + explosion(T, 0, 0, 2, 2) + sleep(1) + qdel(src) + +// the light item +// can be tube or bulb subtypes +// will fit into empty /obj/machinery/light of the corresponding type + +/obj/item/light + icon = 'icons/obj/lighting.dmi' + force = 2 + throwforce = 5 + w_class = WEIGHT_CLASS_TINY + var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN + var/base_state + var/switchcount = 0 // number of times switched + materials = list(MAT_GLASS=100) + var/rigged = 0 // true if rigged to explode + var/brightness_range = 2 //how much light it gives off + var/brightness_power = 1 + var/brightness_color = null + +/obj/item/light/ComponentInitialize() + . = ..() + AddComponent(/datum/component/caltrop, force) + +/obj/item/light/Crossed(mob/living/L) + if(istype(L) && has_gravity(loc)) + if(L.incorporeal_move || L.flying) + return + playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) + if(status == LIGHT_BURNED || status == LIGHT_OK) + shatter() + return ..() + +/obj/item/light/tube + name = "light tube" + desc = "A replacement light tube." + icon_state = "ltube" + base_state = "ltube" + item_state = "c_tube" + brightness_range = 8 + +/obj/item/light/tube/large + w_class = WEIGHT_CLASS_SMALL + name = "large light tube" + brightness_range = 15 + brightness_power = 2 + +/obj/item/light/bulb + name = "light bulb" + desc = "A replacement light bulb." + icon_state = "lbulb" + base_state = "lbulb" + item_state = "contvapour" + brightness_range = 5 + brightness_color = "#a0a080" + +/obj/item/light/throw_impact(atom/hit_atom) + ..() + shatter() + +/obj/item/light/bulb/fire + name = "fire bulb" + desc = "A replacement fire bulb." + icon_state = "fbulb" + base_state = "fbulb" + item_state = "egg4" + brightness_range = 5 + +// update the icon state and description of the light + +/obj/item/light/proc/update() + switch(status) + if(LIGHT_OK) + icon_state = base_state + desc = "A replacement [name]." + if(LIGHT_BURNED) + icon_state = "[base_state]-burned" + desc = "A burnt-out [name]." + if(LIGHT_BROKEN) + icon_state = "[base_state]-broken" + desc = "A broken [name]." + + +/obj/item/light/New() + ..() + switch(name) + if("light tube") + brightness_range = rand(6,9) + if("light bulb") + brightness_range = rand(4,6) + update() + + +// attack bulb/tube with object +// if a syringe, can inject plasma to make it explode +/obj/item/light/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/syringe)) + var/obj/item/reagent_containers/syringe/S = I + + to_chat(user, "You inject the solution into the [src].") + + if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) + + log_admin("LOG: [key_name(user)] injected a light with plasma, rigging it to explode.") + message_admins("LOG: [key_name_admin(user)] injected a light with plasma, rigging it to explode.") + + rigged = 1 + + S.reagents.clear_reagents() + else + return ..() + +/obj/item/light/attack(mob/living/M, mob/living/user, def_zone) + ..() + shatter() + +/obj/item/light/attack_obj(obj/O, mob/living/user) + ..() + shatter() + +/obj/item/light/proc/shatter() + if(status == LIGHT_OK || status == LIGHT_BURNED) + src.visible_message("[name] shatters.","You hear a small glass object shatter.") + status = LIGHT_BROKEN + force = 5 + sharp = 1 + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + update() + +/obj/item/light/suicide_act(mob/living/carbon/human/user) + user.visible_message("[user] touches [src], burning [user.p_their()] hands off!", "You touch [src], burning your hands off!") + + for(var/oname in list("l_hand", "r_hand")) + var/obj/item/organ/external/limb = user.get_organ(oname) + if(limb) + limb.droplimb(0, DROPLIMB_BURN) + return FIRELOSS + +/obj/machinery/light/extinguish_light() + on = FALSE + visible_message("[src] flickers and falls dark.") + update(0) diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 05150c7389da..b534fbbfda47 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -1,459 +1,459 @@ -//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). -/obj/machinery/power/port_gen - name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. - desc = "A portable generator for emergency backup power" - icon = 'icons/obj/power.dmi' - icon_state = "portgen0_0" - density = 1 - anchored = 0 - use_power = NO_POWER_USE - - var/active = 0 - var/power_gen = 5000 - var/open = 0 - var/recent_fault = 0 - var/power_output = 1 - var/base_icon = "portgen0" - -/obj/machinery/power/port_gen/proc/IsBroken() - return (stat & (BROKEN|EMPED)) - -/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check. - return 1 - -/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use. - return - -/obj/machinery/power/port_gen/proc/DropFuel() - return - -/obj/machinery/power/port_gen/proc/handleInactive() - return - -/obj/machinery/power/port_gen/update_icon() - icon_state = "[base_icon]_[active]" - -/obj/machinery/power/port_gen/process() - if(active && HasFuel() && !IsBroken() && anchored && powernet) - add_avail(power_gen * power_output) - UseFuel() - else - active = 0 - handleInactive() - update_icon() - -/obj/machinery/power/powered() - return 1 //doesn't require an external power source - -/obj/machinery/power/port_gen/attack_hand(mob/user as mob) - if(..()) - return - if(!anchored) - return - -/obj/machinery/power/port_gen/examine(mob/user) - . = ..() - if(!in_range(user, src)) - if(active) - . += "The generator is on." - else - . += "The generator is off." - -/obj/machinery/power/port_gen/emp_act(severity) - var/duration = 6000 //ten minutes - switch(severity) - if(1) - stat &= BROKEN - if(prob(75)) explode() - if(2) - if(prob(25)) stat &= BROKEN - if(prob(10)) explode() - if(3) - if(prob(10)) stat &= BROKEN - duration = 300 - - stat |= EMPED - if(duration) - spawn(duration) - stat &= ~EMPED - -/obj/machinery/power/port_gen/proc/explode() - explosion(src.loc, -1, 3, 5, -1) - qdel(src) - -#define TEMPERATURE_DIVISOR 40 -#define TEMPERATURE_CHANGE_MAX 20 - -//A power generator that runs on solid plasma sheets. -/obj/machinery/power/port_gen/pacman - name = "\improper P.A.C.M.A.N.-type Portable Generator" - desc = "A power generator that runs on solid plasma sheets. Rated for 80 kW max safe output." - - var/sheet_name = "Plasma Sheets" - var/sheet_path = /obj/item/stack/sheet/mineral/plasma - var/board_path = /obj/item/circuitboard/pacman - - /* - These values were chosen so that the generator can run safely up to 80 kW - A full 50 plasma sheet stack should last 20 minutes at power_output = 4 - temperature_gain and max_temperature are set so that the max safe power level is 4. - Setting to 5 or higher can only be done temporarily before the generator overheats. - */ - power_gen = 20000 //Watts output per power_output level - var/max_power_output = 5 //The maximum power setting without emagging. - var/max_safe_output = 4 // For UI use, maximal output that won't cause overheat. - var/time_per_sheet = 96 //fuel efficiency - how long 1 sheet lasts at power level 1 - var/max_sheets = 100 //max capacity of the hopper - var/max_temperature = 300 //max temperature before overheating increases - var/temperature_gain = 50 //how much the temperature increases per power output level, in degrees per level - - var/sheets = 0 //How many sheets of material are loaded in the generator - var/sheet_left = 0 //How much is left of the current sheet - var/temperature = 0 //The current temperature - var/overheating = 0 //if this gets high enough the generator explodes - -/obj/machinery/power/port_gen/pacman/Initialize() - ..() - if(anchored) - connect_to_network() - -/obj/machinery/power/port_gen/pacman/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/Destroy() - DropFuel() - return ..() - -/obj/machinery/power/port_gen/pacman/RefreshParts() - var/temp_rating = 0 - for(var/obj/item/stock_parts/SP in component_parts) - if(istype(SP, /obj/item/stock_parts/matter_bin)) - max_sheets = SP.rating * SP.rating * 50 - else if(istype(SP, /obj/item/stock_parts/micro_laser) || istype(SP, /obj/item/stock_parts/capacitor)) - temp_rating += SP.rating - - power_gen = round(initial(power_gen) * (max(2, temp_rating) / 2)) - -/obj/machinery/power/port_gen/pacman/examine(mob/user) - . = ..() - . += "\The [src] appears to be producing [power_gen*power_output] W." - . += "There [sheets == 1 ? "is" : "are"] [sheets] sheet\s left in the hopper." - if(IsBroken()) - . += "\The [src] seems to have broken down." - if(overheating) - . += "\The [src] is overheating!" - -/obj/machinery/power/port_gen/pacman/HasFuel() - var/needed_sheets = power_output / time_per_sheet - if(sheets >= needed_sheets - sheet_left) - return 1 - return 0 - -//Removes one stack's worth of material from the generator. -/obj/machinery/power/port_gen/pacman/DropFuel() - if(sheets) - var/obj/item/stack/sheet/mineral/S = new sheet_path(loc) - var/amount = min(sheets, S.max_amount) - S.amount = amount - sheets -= amount - -/obj/machinery/power/port_gen/pacman/UseFuel() - - //how much material are we using this iteration? - var/needed_sheets = power_output / time_per_sheet - - //HasFuel() should guarantee us that there is enough fuel left, so no need to check that - //the only thing we need to worry about is if we are going to rollover to the next sheet - if(needed_sheets > sheet_left) - sheets-- - sheet_left = (1 + sheet_left) - needed_sheets - else - sheet_left -= needed_sheets - - //calculate the "target" temperature range - //This should probably depend on the external temperature somehow, but whatever. - var/lower_limit = 56 + power_output * temperature_gain - var/upper_limit = 76 + power_output * temperature_gain - - /* - Hot or cold environments can affect the equilibrium temperature - The lower the pressure the less effect it has. I guess it cools using a radiator or something when in vacuum. - Gives traitors more opportunities to sabotage the generator or allows enterprising engineers to build additional - cooling in order to get more power out. - */ - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) - var/ambient = environment.temperature - T20C - lower_limit += ambient*ratio - upper_limit += ambient*ratio - - var/average = (upper_limit + lower_limit)/2 - - //calculate the temperature increase - var/bias = 0 - if(temperature < lower_limit) - bias = min(round((average - temperature)/TEMPERATURE_DIVISOR, 1), TEMPERATURE_CHANGE_MAX) - else if(temperature > upper_limit) - bias = max(round((temperature - average)/TEMPERATURE_DIVISOR, 1), -TEMPERATURE_CHANGE_MAX) - - //limit temperature increase so that it cannot raise temperature above upper_limit, - //or if it is already above upper_limit, limit the increase to 0. - var/inc_limit = max(upper_limit - temperature, 0) - var/dec_limit = min(temperature - lower_limit, 0) - temperature += between(dec_limit, rand(-7 + bias, 7 + bias), inc_limit) - - if(temperature > max_temperature) - overheat() - else if(overheating > 0) - overheating-- - -/obj/machinery/power/port_gen/pacman/handleInactive() - var/cooling_temperature = 20 - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) - var/ambient = environment.temperature - T20C - cooling_temperature += ambient*ratio - - if(temperature > cooling_temperature) - var/temp_loss = (temperature - cooling_temperature)/TEMPERATURE_DIVISOR - temp_loss = between(2, round(temp_loss, 1), TEMPERATURE_CHANGE_MAX) - temperature = max(temperature - temp_loss, cooling_temperature) - SSnanoui.update_uis(src) - - if(overheating) - overheating-- - -/obj/machinery/power/port_gen/pacman/proc/overheat() - overheating++ - if(overheating > 60) - explode() - -/obj/machinery/power/port_gen/pacman/explode() - //Vapourize all the plasma - //When ground up in a grinder, 1 sheet produces 20 u of plasma -- Chemistry-Machinery.dm - //1 mol = 10 u? I dunno. 1 mol of carbon is definitely bigger than a pill - /*var/plasma = (sheets+sheet_left)*20 - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - environment.adjust_gas("plasma", plasma/10, temperature + T0C)*/ - - sheets = 0 - sheet_left = 0 - ..() - -/obj/machinery/power/port_gen/pacman/emag_act(var/remaining_charges, var/mob/user) - if(active && prob(25)) - explode() //if they're foolish enough to emag while it's running - - if(!emagged) - emagged = 1 - return 1 - -/obj/machinery/power/port_gen/pacman/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, sheet_path)) - var/obj/item/stack/addstack = O - var/amount = min((max_sheets - sheets), addstack.amount) - if(amount < 1) - to_chat(user, "The [src.name] is full!") - return - to_chat(user, "You add [amount] sheet\s to the [src.name].") - sheets += amount - addstack.use(amount) - SSnanoui.update_uis(src) - return - else if(!active) - if(istype(O, /obj/item/wrench)) - - if(!anchored) - connect_to_network() - to_chat(user, "You secure the generator to the floor.") - else - disconnect_from_network() - to_chat(user, "You unsecure the generator from the floor.") - - playsound(src.loc, O.usesound, 50, 1) - anchored = !anchored - - else if(istype(O, /obj/item/screwdriver)) - panel_open = !panel_open - playsound(src.loc, O.usesound, 50, 1) - if(panel_open) - to_chat(user, "You open the access panel.") - else - to_chat(user, "You close the access panel.") - else if(istype(O, /obj/item/storage/part_replacer) && panel_open) - exchange_parts(user, O) - return - else if(istype(O, /obj/item/crowbar) && panel_open) - default_deconstruction_crowbar(user, O) - else - return ..() - -/obj/machinery/power/port_gen/pacman/attack_hand(mob/user as mob) - ..() - if(!anchored) - return - ui_interact(user) - -/obj/machinery/power/port_gen/pacman/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/power/port_gen/pacman/attack_ghost(var/mob/user) - return src.attack_hand(user) - -/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(IsBroken()) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "pacman.tmpl", src.name, 500, 560) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/port_gen/pacman/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["active"] = active - if(istype(user, /mob/living/silicon/ai)) - data["is_ai"] = 1 - else if(istype(user, /mob/living/silicon/robot) && !Adjacent(user)) - data["is_ai"] = 1 - else - data["is_ai"] = 0 - - data["output_set"] = power_output - data["output_max"] = max_power_output - data["output_safe"] = max_safe_output - data["output_watts"] = power_output * power_gen - data["temperature_current"] = src.temperature - data["temperature_max"] = src.max_temperature - data["temperature_overheat"] = overheating - // 1 sheet = 1000cm3? - data["fuel_stored"] = round((sheets * 1000) + (sheet_left * 1000)) - data["fuel_capacity"] = round(max_sheets * 1000, 0.1) - data["fuel_usage"] = active ? round((power_output / time_per_sheet) * 1000) : 0 - data["fuel_type"] = sheet_name - - return data - -/obj/machinery/power/port_gen/pacman/Topic(href, href_list) - if(..()) - return - - src.add_fingerprint(usr) - if(href_list["action"]) - if(href_list["action"] == "enable") - if(!active && HasFuel() && !IsBroken()) - active = 1 - update_icon() - if(href_list["action"] == "disable") - if(active) - active = 0 - update_icon() - if(href_list["action"] == "eject") - if(!active) - DropFuel() - if(href_list["action"] == "lower_power") - if(power_output > 1) - power_output-- - if(href_list["action"] == "higher_power") - if(power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5))) - power_output++ - - SSnanoui.update_uis(src) - -/obj/machinery/power/port_gen/pacman/super - name = "S.U.P.E.R.P.A.C.M.A.N.-type Portable Generator" - desc = "A power generator that utilizes uranium sheets as fuel. Can run for much longer than the standard PACMAN type generators. Rated for 80 kW max safe output." - icon_state = "portgen1_0" - base_icon = "portgen1" - sheet_path = /obj/item/stack/sheet/mineral/uranium - sheet_name = "Uranium Sheets" - time_per_sheet = 576 //same power output, but a 50 sheet stack will last 2 hours at max safe power - board_path = /obj/item/circuitboard/pacman/super - -/obj/machinery/power/port_gen/pacman/super/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/super/UseFuel() - //produces a tiny amount of radiation when in use - if(prob(2*power_output)) - for(var/mob/living/L in range(src, 5)) - L.apply_effect(1, IRRADIATE) //should amount to ~5 rads per minute at max safe power - ..() - -/obj/machinery/power/port_gen/pacman/super/explode() - //a nice burst of radiation - var/rads = 50 + (sheets + sheet_left)*1.5 - for(var/mob/living/L in range(src, 10)) - //should really fall with the square of the distance, but that makes the rads value drop too fast - //I dunno, maybe physics works different when you live in 2D -- SM radiation also works like this, apparently - L.apply_effect(max(20, round(rads/get_dist(L,src))), IRRADIATE) - - explosion(src.loc, 3, 3, 5, 3) - qdel(src) - -/obj/machinery/power/port_gen/pacman/mrs - name = "M.R.S.P.A.C.M.A.N.-type Portable Generator" - desc = "An advanced power generator that runs on diamonds. Rated for 200 kW maximum safe output!" - icon_state = "portgen2_0" - base_icon = "portgen2" - sheet_path = /obj/item/stack/sheet/mineral/diamond - sheet_name = "Diamond Sheets" - - //I don't think tritium has any other use, so we might as well make this rewarding for players - //max safe power output (power level = 8) is 200 kW and lasts for 1 hour - 3 or 4 of these could power the station - power_gen = 25000 //watts - max_power_output = 10 - max_safe_output = 8 - time_per_sheet = 576 - max_temperature = 800 - temperature_gain = 90 - board_path = /obj/item/circuitboard/pacman/mrs - -/obj/machinery/power/port_gen/pacman/mrs/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/mrs/explode() - //no special effects, but the explosion is pretty big (same as a supermatter shard). - explosion(src.loc, 3, 6, 12, 16, 1) - qdel(src) +//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). +/obj/machinery/power/port_gen + name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. + desc = "A portable generator for emergency backup power" + icon = 'icons/obj/power.dmi' + icon_state = "portgen0_0" + density = 1 + anchored = 0 + use_power = NO_POWER_USE + + var/active = 0 + var/power_gen = 5000 + var/open = 0 + var/recent_fault = 0 + var/power_output = 1 + var/base_icon = "portgen0" + +/obj/machinery/power/port_gen/proc/IsBroken() + return (stat & (BROKEN|EMPED)) + +/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check. + return 1 + +/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use. + return + +/obj/machinery/power/port_gen/proc/DropFuel() + return + +/obj/machinery/power/port_gen/proc/handleInactive() + return + +/obj/machinery/power/port_gen/update_icon() + icon_state = "[base_icon]_[active]" + +/obj/machinery/power/port_gen/process() + if(active && HasFuel() && !IsBroken() && anchored && powernet) + add_avail(power_gen * power_output) + UseFuel() + else + active = 0 + handleInactive() + update_icon() + +/obj/machinery/power/powered() + return 1 //doesn't require an external power source + +/obj/machinery/power/port_gen/attack_hand(mob/user as mob) + if(..()) + return + if(!anchored) + return + +/obj/machinery/power/port_gen/examine(mob/user) + . = ..() + if(!in_range(user, src)) + if(active) + . += "The generator is on." + else + . += "The generator is off." + +/obj/machinery/power/port_gen/emp_act(severity) + var/duration = 6000 //ten minutes + switch(severity) + if(1) + stat &= BROKEN + if(prob(75)) explode() + if(2) + if(prob(25)) stat &= BROKEN + if(prob(10)) explode() + if(3) + if(prob(10)) stat &= BROKEN + duration = 300 + + stat |= EMPED + if(duration) + spawn(duration) + stat &= ~EMPED + +/obj/machinery/power/port_gen/proc/explode() + explosion(src.loc, -1, 3, 5, -1) + qdel(src) + +#define TEMPERATURE_DIVISOR 40 +#define TEMPERATURE_CHANGE_MAX 20 + +//A power generator that runs on solid plasma sheets. +/obj/machinery/power/port_gen/pacman + name = "\improper P.A.C.M.A.N.-type Portable Generator" + desc = "A power generator that runs on solid plasma sheets. Rated for 80 kW max safe output." + + var/sheet_name = "Plasma Sheets" + var/sheet_path = /obj/item/stack/sheet/mineral/plasma + var/board_path = /obj/item/circuitboard/pacman + + /* + These values were chosen so that the generator can run safely up to 80 kW + A full 50 plasma sheet stack should last 20 minutes at power_output = 4 + temperature_gain and max_temperature are set so that the max safe power level is 4. + Setting to 5 or higher can only be done temporarily before the generator overheats. + */ + power_gen = 20000 //Watts output per power_output level + var/max_power_output = 5 //The maximum power setting without emagging. + var/max_safe_output = 4 // For UI use, maximal output that won't cause overheat. + var/time_per_sheet = 96 //fuel efficiency - how long 1 sheet lasts at power level 1 + var/max_sheets = 100 //max capacity of the hopper + var/max_temperature = 300 //max temperature before overheating increases + var/temperature_gain = 50 //how much the temperature increases per power output level, in degrees per level + + var/sheets = 0 //How many sheets of material are loaded in the generator + var/sheet_left = 0 //How much is left of the current sheet + var/temperature = 0 //The current temperature + var/overheating = 0 //if this gets high enough the generator explodes + +/obj/machinery/power/port_gen/pacman/Initialize() + ..() + if(anchored) + connect_to_network() + +/obj/machinery/power/port_gen/pacman/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/Destroy() + DropFuel() + return ..() + +/obj/machinery/power/port_gen/pacman/RefreshParts() + var/temp_rating = 0 + for(var/obj/item/stock_parts/SP in component_parts) + if(istype(SP, /obj/item/stock_parts/matter_bin)) + max_sheets = SP.rating * SP.rating * 50 + else if(istype(SP, /obj/item/stock_parts/micro_laser) || istype(SP, /obj/item/stock_parts/capacitor)) + temp_rating += SP.rating + + power_gen = round(initial(power_gen) * (max(2, temp_rating) / 2)) + +/obj/machinery/power/port_gen/pacman/examine(mob/user) + . = ..() + . += "\The [src] appears to be producing [power_gen*power_output] W." + . += "There [sheets == 1 ? "is" : "are"] [sheets] sheet\s left in the hopper." + if(IsBroken()) + . += "\The [src] seems to have broken down." + if(overheating) + . += "\The [src] is overheating!" + +/obj/machinery/power/port_gen/pacman/HasFuel() + var/needed_sheets = power_output / time_per_sheet + if(sheets >= needed_sheets - sheet_left) + return 1 + return 0 + +//Removes one stack's worth of material from the generator. +/obj/machinery/power/port_gen/pacman/DropFuel() + if(sheets) + var/obj/item/stack/sheet/mineral/S = new sheet_path(loc) + var/amount = min(sheets, S.max_amount) + S.amount = amount + sheets -= amount + +/obj/machinery/power/port_gen/pacman/UseFuel() + + //how much material are we using this iteration? + var/needed_sheets = power_output / time_per_sheet + + //HasFuel() should guarantee us that there is enough fuel left, so no need to check that + //the only thing we need to worry about is if we are going to rollover to the next sheet + if(needed_sheets > sheet_left) + sheets-- + sheet_left = (1 + sheet_left) - needed_sheets + else + sheet_left -= needed_sheets + + //calculate the "target" temperature range + //This should probably depend on the external temperature somehow, but whatever. + var/lower_limit = 56 + power_output * temperature_gain + var/upper_limit = 76 + power_output * temperature_gain + + /* + Hot or cold environments can affect the equilibrium temperature + The lower the pressure the less effect it has. I guess it cools using a radiator or something when in vacuum. + Gives traitors more opportunities to sabotage the generator or allows enterprising engineers to build additional + cooling in order to get more power out. + */ + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) + var/ambient = environment.temperature - T20C + lower_limit += ambient*ratio + upper_limit += ambient*ratio + + var/average = (upper_limit + lower_limit)/2 + + //calculate the temperature increase + var/bias = 0 + if(temperature < lower_limit) + bias = min(round((average - temperature)/TEMPERATURE_DIVISOR, 1), TEMPERATURE_CHANGE_MAX) + else if(temperature > upper_limit) + bias = max(round((temperature - average)/TEMPERATURE_DIVISOR, 1), -TEMPERATURE_CHANGE_MAX) + + //limit temperature increase so that it cannot raise temperature above upper_limit, + //or if it is already above upper_limit, limit the increase to 0. + var/inc_limit = max(upper_limit - temperature, 0) + var/dec_limit = min(temperature - lower_limit, 0) + temperature += between(dec_limit, rand(-7 + bias, 7 + bias), inc_limit) + + if(temperature > max_temperature) + overheat() + else if(overheating > 0) + overheating-- + +/obj/machinery/power/port_gen/pacman/handleInactive() + var/cooling_temperature = 20 + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) + var/ambient = environment.temperature - T20C + cooling_temperature += ambient*ratio + + if(temperature > cooling_temperature) + var/temp_loss = (temperature - cooling_temperature)/TEMPERATURE_DIVISOR + temp_loss = between(2, round(temp_loss, 1), TEMPERATURE_CHANGE_MAX) + temperature = max(temperature - temp_loss, cooling_temperature) + SSnanoui.update_uis(src) + + if(overheating) + overheating-- + +/obj/machinery/power/port_gen/pacman/proc/overheat() + overheating++ + if(overheating > 60) + explode() + +/obj/machinery/power/port_gen/pacman/explode() + //Vapourize all the plasma + //When ground up in a grinder, 1 sheet produces 20 u of plasma -- Chemistry-Machinery.dm + //1 mol = 10 u? I dunno. 1 mol of carbon is definitely bigger than a pill + /*var/plasma = (sheets+sheet_left)*20 + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + environment.adjust_gas("plasma", plasma/10, temperature + T0C)*/ + + sheets = 0 + sheet_left = 0 + ..() + +/obj/machinery/power/port_gen/pacman/emag_act(var/remaining_charges, var/mob/user) + if(active && prob(25)) + explode() //if they're foolish enough to emag while it's running + + if(!emagged) + emagged = 1 + return 1 + +/obj/machinery/power/port_gen/pacman/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, sheet_path)) + var/obj/item/stack/addstack = O + var/amount = min((max_sheets - sheets), addstack.amount) + if(amount < 1) + to_chat(user, "The [src.name] is full!") + return + to_chat(user, "You add [amount] sheet\s to the [src.name].") + sheets += amount + addstack.use(amount) + SSnanoui.update_uis(src) + return + else if(!active) + if(istype(O, /obj/item/wrench)) + + if(!anchored) + connect_to_network() + to_chat(user, "You secure the generator to the floor.") + else + disconnect_from_network() + to_chat(user, "You unsecure the generator from the floor.") + + playsound(src.loc, O.usesound, 50, 1) + anchored = !anchored + + else if(istype(O, /obj/item/screwdriver)) + panel_open = !panel_open + playsound(src.loc, O.usesound, 50, 1) + if(panel_open) + to_chat(user, "You open the access panel.") + else + to_chat(user, "You close the access panel.") + else if(istype(O, /obj/item/storage/part_replacer) && panel_open) + exchange_parts(user, O) + return + else if(istype(O, /obj/item/crowbar) && panel_open) + default_deconstruction_crowbar(user, O) + else + return ..() + +/obj/machinery/power/port_gen/pacman/attack_hand(mob/user as mob) + ..() + if(!anchored) + return + ui_interact(user) + +/obj/machinery/power/port_gen/pacman/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/power/port_gen/pacman/attack_ghost(var/mob/user) + return src.attack_hand(user) + +/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(IsBroken()) + return + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "pacman.tmpl", src.name, 500, 560) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/power/port_gen/pacman/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["active"] = active + if(istype(user, /mob/living/silicon/ai)) + data["is_ai"] = 1 + else if(istype(user, /mob/living/silicon/robot) && !Adjacent(user)) + data["is_ai"] = 1 + else + data["is_ai"] = 0 + + data["output_set"] = power_output + data["output_max"] = max_power_output + data["output_safe"] = max_safe_output + data["output_watts"] = power_output * power_gen + data["temperature_current"] = src.temperature + data["temperature_max"] = src.max_temperature + data["temperature_overheat"] = overheating + // 1 sheet = 1000cm3? + data["fuel_stored"] = round((sheets * 1000) + (sheet_left * 1000)) + data["fuel_capacity"] = round(max_sheets * 1000, 0.1) + data["fuel_usage"] = active ? round((power_output / time_per_sheet) * 1000) : 0 + data["fuel_type"] = sheet_name + + return data + +/obj/machinery/power/port_gen/pacman/Topic(href, href_list) + if(..()) + return + + src.add_fingerprint(usr) + if(href_list["action"]) + if(href_list["action"] == "enable") + if(!active && HasFuel() && !IsBroken()) + active = 1 + update_icon() + if(href_list["action"] == "disable") + if(active) + active = 0 + update_icon() + if(href_list["action"] == "eject") + if(!active) + DropFuel() + if(href_list["action"] == "lower_power") + if(power_output > 1) + power_output-- + if(href_list["action"] == "higher_power") + if(power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5))) + power_output++ + + SSnanoui.update_uis(src) + +/obj/machinery/power/port_gen/pacman/super + name = "S.U.P.E.R.P.A.C.M.A.N.-type Portable Generator" + desc = "A power generator that utilizes uranium sheets as fuel. Can run for much longer than the standard PACMAN type generators. Rated for 80 kW max safe output." + icon_state = "portgen1_0" + base_icon = "portgen1" + sheet_path = /obj/item/stack/sheet/mineral/uranium + sheet_name = "Uranium Sheets" + time_per_sheet = 576 //same power output, but a 50 sheet stack will last 2 hours at max safe power + board_path = /obj/item/circuitboard/pacman/super + +/obj/machinery/power/port_gen/pacman/super/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/super/UseFuel() + //produces a tiny amount of radiation when in use + if(prob(2*power_output)) + for(var/mob/living/L in range(src, 5)) + L.apply_effect(1, IRRADIATE) //should amount to ~5 rads per minute at max safe power + ..() + +/obj/machinery/power/port_gen/pacman/super/explode() + //a nice burst of radiation + var/rads = 50 + (sheets + sheet_left)*1.5 + for(var/mob/living/L in range(src, 10)) + //should really fall with the square of the distance, but that makes the rads value drop too fast + //I dunno, maybe physics works different when you live in 2D -- SM radiation also works like this, apparently + L.apply_effect(max(20, round(rads/get_dist(L,src))), IRRADIATE) + + explosion(src.loc, 3, 3, 5, 3) + qdel(src) + +/obj/machinery/power/port_gen/pacman/mrs + name = "M.R.S.P.A.C.M.A.N.-type Portable Generator" + desc = "An advanced power generator that runs on diamonds. Rated for 200 kW maximum safe output!" + icon_state = "portgen2_0" + base_icon = "portgen2" + sheet_path = /obj/item/stack/sheet/mineral/diamond + sheet_name = "Diamond Sheets" + + //I don't think tritium has any other use, so we might as well make this rewarding for players + //max safe power output (power level = 8) is 200 kW and lasts for 1 hour - 3 or 4 of these could power the station + power_gen = 25000 //watts + max_power_output = 10 + max_safe_output = 8 + time_per_sheet = 576 + max_temperature = 800 + temperature_gain = 90 + board_path = /obj/item/circuitboard/pacman/mrs + +/obj/machinery/power/port_gen/pacman/mrs/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/mrs/explode() + //no special effects, but the explosion is pretty big (same as a supermatter shard). + explosion(src.loc, 3, 6, 12, 16, 1) + qdel(src) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 33741d1475b7..c74f9cc00074 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -1,380 +1,380 @@ -////////////////////////////// -// POWER MACHINERY BASE CLASS -////////////////////////////// - -///////////////////////////// -// Definitions -///////////////////////////// - -/obj/machinery/power - name = null - icon = 'icons/obj/power.dmi' - anchored = TRUE - on_blueprints = TRUE - var/datum/powernet/powernet = null - use_power = NO_POWER_USE - idle_power_usage = 0 - active_power_usage = 0 - -/obj/machinery/power/Destroy() - disconnect_from_network() - return ..() - -/////////////////////////////// -// General procedures -////////////////////////////// - -// common helper procs for all power machines -// All power generation handled in add_avail() -// Machines should use add_load(), surplus(), avail() -// Non-machines should use add_delayedload(), delayed_surplus(), newavail() - -/obj/machinery/power/proc/add_avail(amount) - if(powernet) - powernet.newavail += amount - return TRUE - else - return FALSE - -/obj/machinery/power/proc/add_load(amount) - if(powernet) - powernet.load += amount - -/obj/machinery/power/proc/surplus() - if(powernet) - return Clamp(powernet.avail-powernet.load, 0, powernet.avail) - else - return 0 - -/obj/machinery/power/proc/avail() - if(powernet) - return powernet.avail - else - return 0 - -/obj/machinery/power/proc/add_delayedload(amount) - if(powernet) - powernet.delayedload += amount - -/obj/machinery/power/proc/delayed_surplus() - if(powernet) - return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) - else - return 0 - -/obj/machinery/power/proc/newavail() - if(powernet) - return powernet.newavail - else - return 0 - -/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl. - return - -// returns true if the area has power on given channel (or doesn't require power). -// defaults to power_channel -/obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel - if(!loc) - return FALSE - if(!use_power) - return TRUE - - var/area/A = get_area(src) // make sure it's in an area - if(!A) - return FALSE // if not, then not powered - if(chan == -1) - chan = power_channel - return A.powered(chan) // return power status of the area - -// increment the power usage stats for an area -/obj/machinery/proc/use_power(amount, chan = -1) // defaults to power_channel - var/area/A = get_area(src) // make sure it's in an area - if(!A) - return - if(chan == -1) - chan = power_channel - A.use_power(amount, chan) - -/obj/machinery/proc/addStaticPower(value, powerchannel) - var/area/A = get_area(src) - if(!A) - return - A.addStaticPower(value, powerchannel) - -/obj/machinery/proc/removeStaticPower(value, powerchannel) - addStaticPower(-value, powerchannel) - -/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change - // by default, check equipment channel & set flag - // can override if needed - if(powered(power_channel)) - stat &= ~NOPOWER - else - - stat |= NOPOWER - return - -// connect the machine to a powernet if a node cable is present on the turf -/obj/machinery/power/proc/connect_to_network() - var/turf/T = src.loc - if(!T || !istype(T)) - return FALSE - - var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked - if(!C || !C.powernet) - return FALSE - - C.powernet.add_machine(src) - return TRUE - -// remove and disconnect the machine from its current powernet -/obj/machinery/power/proc/disconnect_from_network() - if(!powernet) - return FALSE - powernet.remove_machine(src) - return TRUE - -// attach a wire to a power machine - leads from the turf you are standing on -//almost never called, overwritten by all power machines but terminal and generator -/obj/machinery/power/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = I - var/turf/T = user.loc - if(T.intact || !isfloorturf(T)) - return - if(get_dist(src, user) > 1) - return - coil.place_turf(T, user) - else - return ..() - - -/////////////////////////////////////////// -// Powernet handling helpers -////////////////////////////////////////// - -//returns all the cables WITHOUT a powernet in neighbors turfs, -//pointing towards the turf the machine is located at -/obj/machinery/power/proc/get_connections() - - . = list() - - var/cdir - var/turf/T - - for(var/card in cardinal) - T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.powernet) - continue - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -//returns all the cables in neighbors turfs, -//pointing towards the turf the machine is located at -/obj/machinery/power/proc/get_marked_connections() - - . = list() - - var/cdir - var/turf/T - - for(var/card in cardinal) - T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at -/obj/machinery/power/proc/get_indirect_connections() - . = list() - for(var/obj/structure/cable/C in loc) - if(C.powernet) - continue - if(C.d1 == 0) // the cable is a node cable - . += C - return . - -/////////////////////////////////////////// -// GLOBAL PROCS for powernets handling -////////////////////////////////////////// - - -// returns a list of all power-related objects (nodes, cable, junctions) in turf, -// excluding source, that match the direction d -// if unmarked==1, only return those with no powernet -/proc/power_list(turf/T, source, d, unmarked=0, cable_only = 0) - . = list() - - for(var/AM in T) - if(AM == source) - continue //we don't want to return source - - if(!cable_only && istype(AM, /obj/machinery/power)) - var/obj/machinery/power/P = AM - if(P.powernet == 0) - continue // exclude APCs which have powernet=0 - - if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet - if(d == 0) - . += P - - else if(istype(AM, /obj/structure/cable)) - var/obj/structure/cable/C = AM - - if(!unmarked || !C.powernet) - if(C.d1 == d || C.d2 == d) - . += C - return . - -//remove the old powernet and replace it with a new one throughout the network. -/proc/propagate_network(obj/O, datum/powernet/PN) - var/list/worklist = list() - var/list/found_machines = list() - var/index = 1 - var/obj/P = null - - worklist+=O //start propagating from the passed object - - while(index<=worklist.len) //until we've exhausted all power objects - P = worklist[index] //get the next power object found - index++ - - if(istype(P, /obj/structure/cable)) - var/obj/structure/cable/C = P - if(C.powernet != PN) //add it to the powernet, if it isn't already there - PN.add_cable(C) - worklist |= C.get_connections() //get adjacents power objects, with or without a powernet - - else if(P.anchored && istype(P, /obj/machinery/power)) - var/obj/machinery/power/M = P - found_machines |= M //we wait until the powernet is fully propagates to connect the machines - - else - continue - - //now that the powernet is set, connect found machines to it - for(var/obj/machinery/power/PM in found_machines) - if(!PM.connect_to_network()) //couldn't find a node on its turf... - PM.disconnect_from_network() //... so disconnect if already on a powernet - - -//Merge two powernets, the bigger (in cable length term) absorbing the other -/proc/merge_powernets(datum/powernet/net1, datum/powernet/net2) - if(!net1 || !net2) //if one of the powernet doesn't exist, return - return - - if(net1 == net2) //don't merge same powernets - return - - //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. - if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around - var/temp = net1 - net1 = net2 - net2 = temp - - //merge net2 into net1 - for(var/obj/structure/cable/Cable in net2.cables) //merge cables - net1.add_cable(Cable) - - for(var/obj/machinery/power/Node in net2.nodes) //merge power machines - if(!Node.connect_to_network()) - Node.disconnect_from_network() //if somehow we can't connect the machine to the new powernet, disconnect it from the old nonetheless - - return net1 - -//Determines how strong could be shock, deals damage to mob, uses power. -//M is a mob who touched wire/whatever -//power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null -//source is an object caused electrocuting (airlock, grille, etc) -//No animations will be performed by this proc. -/proc/electrocute_mob(mob/living/M, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE) - if(!M || ismecha(M.loc)) - return FALSE //feckin mechs are dumb - if(dist_check) - if(!in_range(source, M)) - return FALSE - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.siemens_coefficient == 0) - return FALSE //to avoid spamming with insulated glvoes on - - var/area/source_area - if(istype(power_source, /area)) - source_area = power_source - power_source = source_area.get_apc() - if(istype(power_source, /obj/structure/cable)) - var/obj/structure/cable/Cable = power_source - power_source = Cable.powernet - - var/datum/powernet/PN - var/obj/item/stock_parts/cell/cell - - if(istype(power_source, /datum/powernet)) - PN = power_source - else if(istype(power_source, /obj/item/stock_parts/cell)) - cell = power_source - else if(istype(power_source, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/apc = power_source - cell = apc.cell - if(apc.terminal) - PN = apc.terminal.powernet - else if(!power_source) - return 0 - else - log_admin("ERROR: /proc/electrocute_mob([M], [power_source], [source]): wrong power_source") - return 0 - if(!cell && !PN) - return 0 - var/PN_damage = 0 - var/cell_damage = 0 - if(PN) - PN_damage = PN.get_electrocute_damage() - if(cell) - cell_damage = cell.get_electrocute_damage() - var/shock_damage = 0 - if(PN_damage >= cell_damage) - power_source = PN - shock_damage = PN_damage - else - power_source = cell - shock_damage = cell_damage - var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! - var/drained_energy = drained_hp*20 - - if(source_area) - source_area.use_power(drained_energy/GLOB.CELLRATE) - else if(istype(power_source, /datum/powernet)) - var/drained_power = drained_energy/GLOB.CELLRATE //convert from "joules" to "watts" - PN.delayedload += (min(drained_power, max(PN.newavail - PN.delayedload, 0))) - else if (istype(power_source, /obj/item/stock_parts/cell)) - cell.use(drained_energy) - return drained_energy - -//////////////////////////////////////////////// -// Misc. -/////////////////////////////////////////////// - - -// return a knot cable (O-X) if one is present in the turf -// null if there's none -/turf/proc/get_cable_node() - if(!can_have_cabling()) - return null - for(var/obj/structure/cable/C in src) - if(C.d1 == 0) - return C - return null - -/area/proc/get_apc() - for(var/obj/machinery/power/apc/APC in GLOB.apcs) - if(APC.area == src) - return APC \ No newline at end of file +////////////////////////////// +// POWER MACHINERY BASE CLASS +////////////////////////////// + +///////////////////////////// +// Definitions +///////////////////////////// + +/obj/machinery/power + name = null + icon = 'icons/obj/power.dmi' + anchored = TRUE + on_blueprints = TRUE + var/datum/powernet/powernet = null + use_power = NO_POWER_USE + idle_power_usage = 0 + active_power_usage = 0 + +/obj/machinery/power/Destroy() + disconnect_from_network() + return ..() + +/////////////////////////////// +// General procedures +////////////////////////////// + +// common helper procs for all power machines +// All power generation handled in add_avail() +// Machines should use add_load(), surplus(), avail() +// Non-machines should use add_delayedload(), delayed_surplus(), newavail() + +/obj/machinery/power/proc/add_avail(amount) + if(powernet) + powernet.newavail += amount + return TRUE + else + return FALSE + +/obj/machinery/power/proc/add_load(amount) + if(powernet) + powernet.load += amount + +/obj/machinery/power/proc/surplus() + if(powernet) + return Clamp(powernet.avail-powernet.load, 0, powernet.avail) + else + return 0 + +/obj/machinery/power/proc/avail() + if(powernet) + return powernet.avail + else + return 0 + +/obj/machinery/power/proc/add_delayedload(amount) + if(powernet) + powernet.delayedload += amount + +/obj/machinery/power/proc/delayed_surplus() + if(powernet) + return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) + else + return 0 + +/obj/machinery/power/proc/newavail() + if(powernet) + return powernet.newavail + else + return 0 + +/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl. + return + +// returns true if the area has power on given channel (or doesn't require power). +// defaults to power_channel +/obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel + if(!loc) + return FALSE + if(!use_power) + return TRUE + + var/area/A = get_area(src) // make sure it's in an area + if(!A) + return FALSE // if not, then not powered + if(chan == -1) + chan = power_channel + return A.powered(chan) // return power status of the area + +// increment the power usage stats for an area +/obj/machinery/proc/use_power(amount, chan = -1) // defaults to power_channel + var/area/A = get_area(src) // make sure it's in an area + if(!A) + return + if(chan == -1) + chan = power_channel + A.use_power(amount, chan) + +/obj/machinery/proc/addStaticPower(value, powerchannel) + var/area/A = get_area(src) + if(!A) + return + A.addStaticPower(value, powerchannel) + +/obj/machinery/proc/removeStaticPower(value, powerchannel) + addStaticPower(-value, powerchannel) + +/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change + // by default, check equipment channel & set flag + // can override if needed + if(powered(power_channel)) + stat &= ~NOPOWER + else + + stat |= NOPOWER + return + +// connect the machine to a powernet if a node cable is present on the turf +/obj/machinery/power/proc/connect_to_network() + var/turf/T = src.loc + if(!T || !istype(T)) + return FALSE + + var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked + if(!C || !C.powernet) + return FALSE + + C.powernet.add_machine(src) + return TRUE + +// remove and disconnect the machine from its current powernet +/obj/machinery/power/proc/disconnect_from_network() + if(!powernet) + return FALSE + powernet.remove_machine(src) + return TRUE + +// attach a wire to a power machine - leads from the turf you are standing on +//almost never called, overwritten by all power machines but terminal and generator +/obj/machinery/power/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = I + var/turf/T = user.loc + if(T.intact || !isfloorturf(T)) + return + if(get_dist(src, user) > 1) + return + coil.place_turf(T, user) + else + return ..() + + +/////////////////////////////////////////// +// Powernet handling helpers +////////////////////////////////////////// + +//returns all the cables WITHOUT a powernet in neighbors turfs, +//pointing towards the turf the machine is located at +/obj/machinery/power/proc/get_connections() + + . = list() + + var/cdir + var/turf/T + + for(var/card in GLOB.cardinal) + T = get_step(loc,card) + cdir = get_dir(T,loc) + + for(var/obj/structure/cable/C in T) + if(C.powernet) + continue + if(C.d1 == cdir || C.d2 == cdir) + . += C + return . + +//returns all the cables in neighbors turfs, +//pointing towards the turf the machine is located at +/obj/machinery/power/proc/get_marked_connections() + + . = list() + + var/cdir + var/turf/T + + for(var/card in GLOB.cardinal) + T = get_step(loc,card) + cdir = get_dir(T,loc) + + for(var/obj/structure/cable/C in T) + if(C.d1 == cdir || C.d2 == cdir) + . += C + return . + +//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at +/obj/machinery/power/proc/get_indirect_connections() + . = list() + for(var/obj/structure/cable/C in loc) + if(C.powernet) + continue + if(C.d1 == 0) // the cable is a node cable + . += C + return . + +/////////////////////////////////////////// +// GLOBAL PROCS for powernets handling +////////////////////////////////////////// + + +// returns a list of all power-related objects (nodes, cable, junctions) in turf, +// excluding source, that match the direction d +// if unmarked==1, only return those with no powernet +/proc/power_list(turf/T, source, d, unmarked=0, cable_only = 0) + . = list() + + for(var/AM in T) + if(AM == source) + continue //we don't want to return source + + if(!cable_only && istype(AM, /obj/machinery/power)) + var/obj/machinery/power/P = AM + if(P.powernet == 0) + continue // exclude APCs which have powernet=0 + + if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet + if(d == 0) + . += P + + else if(istype(AM, /obj/structure/cable)) + var/obj/structure/cable/C = AM + + if(!unmarked || !C.powernet) + if(C.d1 == d || C.d2 == d) + . += C + return . + +//remove the old powernet and replace it with a new one throughout the network. +/proc/propagate_network(obj/O, datum/powernet/PN) + var/list/worklist = list() + var/list/found_machines = list() + var/index = 1 + var/obj/P = null + + worklist+=O //start propagating from the passed object + + while(index<=worklist.len) //until we've exhausted all power objects + P = worklist[index] //get the next power object found + index++ + + if(istype(P, /obj/structure/cable)) + var/obj/structure/cable/C = P + if(C.powernet != PN) //add it to the powernet, if it isn't already there + PN.add_cable(C) + worklist |= C.get_connections() //get adjacents power objects, with or without a powernet + + else if(P.anchored && istype(P, /obj/machinery/power)) + var/obj/machinery/power/M = P + found_machines |= M //we wait until the powernet is fully propagates to connect the machines + + else + continue + + //now that the powernet is set, connect found machines to it + for(var/obj/machinery/power/PM in found_machines) + if(!PM.connect_to_network()) //couldn't find a node on its turf... + PM.disconnect_from_network() //... so disconnect if already on a powernet + + +//Merge two powernets, the bigger (in cable length term) absorbing the other +/proc/merge_powernets(datum/powernet/net1, datum/powernet/net2) + if(!net1 || !net2) //if one of the powernet doesn't exist, return + return + + if(net1 == net2) //don't merge same powernets + return + + //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. + if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around + var/temp = net1 + net1 = net2 + net2 = temp + + //merge net2 into net1 + for(var/obj/structure/cable/Cable in net2.cables) //merge cables + net1.add_cable(Cable) + + for(var/obj/machinery/power/Node in net2.nodes) //merge power machines + if(!Node.connect_to_network()) + Node.disconnect_from_network() //if somehow we can't connect the machine to the new powernet, disconnect it from the old nonetheless + + return net1 + +//Determines how strong could be shock, deals damage to mob, uses power. +//M is a mob who touched wire/whatever +//power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null +//source is an object caused electrocuting (airlock, grille, etc) +//No animations will be performed by this proc. +/proc/electrocute_mob(mob/living/M, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE) + if(!M || ismecha(M.loc)) + return FALSE //feckin mechs are dumb + if(dist_check) + if(!in_range(source, M)) + return FALSE + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.siemens_coefficient == 0) + return FALSE //to avoid spamming with insulated glvoes on + + var/area/source_area + if(istype(power_source, /area)) + source_area = power_source + power_source = source_area.get_apc() + if(istype(power_source, /obj/structure/cable)) + var/obj/structure/cable/Cable = power_source + power_source = Cable.powernet + + var/datum/powernet/PN + var/obj/item/stock_parts/cell/cell + + if(istype(power_source, /datum/powernet)) + PN = power_source + else if(istype(power_source, /obj/item/stock_parts/cell)) + cell = power_source + else if(istype(power_source, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/apc = power_source + cell = apc.cell + if(apc.terminal) + PN = apc.terminal.powernet + else if(!power_source) + return 0 + else + log_admin("ERROR: /proc/electrocute_mob([M], [power_source], [source]): wrong power_source") + return 0 + if(!cell && !PN) + return 0 + var/PN_damage = 0 + var/cell_damage = 0 + if(PN) + PN_damage = PN.get_electrocute_damage() + if(cell) + cell_damage = cell.get_electrocute_damage() + var/shock_damage = 0 + if(PN_damage >= cell_damage) + power_source = PN + shock_damage = PN_damage + else + power_source = cell + shock_damage = cell_damage + var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! + var/drained_energy = drained_hp*20 + + if(source_area) + source_area.use_power(drained_energy/GLOB.CELLRATE) + else if(istype(power_source, /datum/powernet)) + var/drained_power = drained_energy/GLOB.CELLRATE //convert from "joules" to "watts" + PN.delayedload += (min(drained_power, max(PN.newavail - PN.delayedload, 0))) + else if (istype(power_source, /obj/item/stock_parts/cell)) + cell.use(drained_energy) + return drained_energy + +//////////////////////////////////////////////// +// Misc. +/////////////////////////////////////////////// + + +// return a knot cable (O-X) if one is present in the turf +// null if there's none +/turf/proc/get_cable_node() + if(!can_have_cabling()) + return null + for(var/obj/structure/cable/C in src) + if(C.d1 == 0) + return C + return null + +/area/proc/get_apc() + for(var/obj/machinery/power/apc/APC in GLOB.apcs) + if(APC.area == src) + return APC diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index b64481b11041..a4ab64a2099d 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -99,4 +99,4 @@ if(avail >= 1000) return Clamp(20 + round(avail / 25000), 20, 195) + rand(-5, 5) else - return 0 \ No newline at end of file + return 0 diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 711ffbc8f445..e6a933c44580 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -1,148 +1,148 @@ -var/global/list/rad_collectors = list() - -/obj/machinery/power/rad_collector - name = "Radiation Collector Array" - desc = "A device which uses Hawking Radiation and plasma to produce power." - icon = 'icons/obj/singularity.dmi' - icon_state = "ca" - anchored = 0 - density = 1 - req_access = list(ACCESS_ENGINE_EQUIP) -// use_power = NO_POWER_USE - max_integrity = 350 - integrity_failure = 80 - var/obj/item/tank/plasma/P = null - var/last_power = 0 - var/active = 0 - var/locked = 0 - var/drainratio = 1 - -/obj/machinery/power/rad_collector/Initialize(mapload) - . = ..() - rad_collectors += src - -/obj/machinery/power/rad_collector/Destroy() - rad_collectors -= src - return ..() - -/obj/machinery/power/rad_collector/process() - if(P) - if(P.air_contents.toxins <= 0) - investigate_log("out of fuel.","singulo") - P.air_contents.toxins = 0 - eject() - else - P.air_contents.toxins -= 0.001*drainratio - return - - -/obj/machinery/power/rad_collector/attack_hand(mob/user as mob) - if(anchored) - if(!src.locked) - toggle_power() - user.visible_message("[user.name] turns the [src.name] [active? "on":"off"].", \ - "You turn the [src.name] [active? "on":"off"].") - investigate_log("turned [active?"on":"off"] by [user.key]. [P?"Fuel: [round(P.air_contents.toxins/0.29)]%":"It is empty"].","singulo") - return - else - to_chat(user, "The controls are locked!") - return - - -/obj/machinery/power/rad_collector/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/multitool)) - to_chat(user, "The [W.name] detects that [last_power]W were recently produced.") - return 1 - else if(istype(W, /obj/item/analyzer) && P) - atmosanalyzer_scan(P.air_contents, user) - else if(istype(W, /obj/item/tank/plasma)) - if(!src.anchored) - to_chat(user, "The [src] needs to be secured to the floor first.") - return 1 - if(src.P) - to_chat(user, "There's already a plasma tank loaded.") - return 1 - user.drop_item() - src.P = W - W.loc = src - update_icons() - else if(istype(W, /obj/item/crowbar)) - if(P && !src.locked) - eject() - return 1 - else if(istype(W, /obj/item/wrench)) - if(P) - to_chat(user, "Remove the plasma tank first.") - return 1 - playsound(src.loc, W.usesound, 75, 1) - src.anchored = !src.anchored - user.visible_message("[user.name] [anchored? "secures":"unsecures"] the [src.name].", \ - "You [anchored? "secure":"undo"] the external bolts.", \ - "You hear a ratchet") - if(anchored) - connect_to_network() - else - disconnect_from_network() - else if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) - if(src.allowed(user)) - if(active) - src.locked = !src.locked - to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") - else - src.locked = 0 //just in case it somehow gets locked - to_chat(user, "The controls can only be locked when the [src] is active") - else - to_chat(user, "Access denied!") - return 1 - else - return ..() - -/obj/machinery/power/rad_collector/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - eject() - stat |= BROKEN - -/obj/machinery/power/rad_collector/proc/eject() - locked = 0 - var/obj/item/tank/plasma/Z = src.P - if(!Z) - return - Z.loc = get_turf(src) - Z.layer = initial(Z.layer) - Z.plane = initial(Z.plane) - src.P = null - if(active) - toggle_power() - else - update_icons() - -/obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength) - if(P && active) - var/power_produced = 0 - power_produced = P.air_contents.toxins*pulse_strength*20 - add_avail(power_produced) - last_power = power_produced - return - return - - -/obj/machinery/power/rad_collector/proc/update_icons() - overlays.Cut() - if(P) - overlays += image('icons/obj/singularity.dmi', "ptank") - if(stat & (NOPOWER|BROKEN)) - return - if(active) - overlays += image('icons/obj/singularity.dmi', "on") - - -/obj/machinery/power/rad_collector/proc/toggle_power() - active = !active - if(active) - icon_state = "ca_on" - flick("ca_active", src) - else - icon_state = "ca" - flick("ca_deactive", src) - update_icons() - return +GLOBAL_LIST_EMPTY(rad_collectors) + +/obj/machinery/power/rad_collector + name = "Radiation Collector Array" + desc = "A device which uses Hawking Radiation and plasma to produce power." + icon = 'icons/obj/singularity.dmi' + icon_state = "ca" + anchored = 0 + density = 1 + req_access = list(ACCESS_ENGINE_EQUIP) +// use_power = NO_POWER_USE + max_integrity = 350 + integrity_failure = 80 + var/obj/item/tank/plasma/P = null + var/last_power = 0 + var/active = 0 + var/locked = 0 + var/drainratio = 1 + +/obj/machinery/power/rad_collector/Initialize(mapload) + . = ..() + GLOB.rad_collectors += src + +/obj/machinery/power/rad_collector/Destroy() + GLOB.rad_collectors -= src + return ..() + +/obj/machinery/power/rad_collector/process() + if(P) + if(P.air_contents.toxins <= 0) + investigate_log("out of fuel.","singulo") + P.air_contents.toxins = 0 + eject() + else + P.air_contents.toxins -= 0.001*drainratio + return + + +/obj/machinery/power/rad_collector/attack_hand(mob/user as mob) + if(anchored) + if(!src.locked) + toggle_power() + user.visible_message("[user.name] turns the [src.name] [active? "on":"off"].", \ + "You turn the [src.name] [active? "on":"off"].") + investigate_log("turned [active?"on":"off"] by [user.key]. [P?"Fuel: [round(P.air_contents.toxins/0.29)]%":"It is empty"].","singulo") + return + else + to_chat(user, "The controls are locked!") + return + + +/obj/machinery/power/rad_collector/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/multitool)) + to_chat(user, "The [W.name] detects that [last_power]W were recently produced.") + return 1 + else if(istype(W, /obj/item/analyzer) && P) + atmosanalyzer_scan(P.air_contents, user) + else if(istype(W, /obj/item/tank/plasma)) + if(!src.anchored) + to_chat(user, "The [src] needs to be secured to the floor first.") + return 1 + if(src.P) + to_chat(user, "There's already a plasma tank loaded.") + return 1 + user.drop_item() + src.P = W + W.loc = src + update_icons() + else if(istype(W, /obj/item/crowbar)) + if(P && !src.locked) + eject() + return 1 + else if(istype(W, /obj/item/wrench)) + if(P) + to_chat(user, "Remove the plasma tank first.") + return 1 + playsound(src.loc, W.usesound, 75, 1) + src.anchored = !src.anchored + user.visible_message("[user.name] [anchored? "secures":"unsecures"] the [src.name].", \ + "You [anchored? "secure":"undo"] the external bolts.", \ + "You hear a ratchet") + if(anchored) + connect_to_network() + else + disconnect_from_network() + else if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) + if(src.allowed(user)) + if(active) + src.locked = !src.locked + to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") + else + src.locked = 0 //just in case it somehow gets locked + to_chat(user, "The controls can only be locked when the [src] is active") + else + to_chat(user, "Access denied!") + return 1 + else + return ..() + +/obj/machinery/power/rad_collector/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + eject() + stat |= BROKEN + +/obj/machinery/power/rad_collector/proc/eject() + locked = 0 + var/obj/item/tank/plasma/Z = src.P + if(!Z) + return + Z.loc = get_turf(src) + Z.layer = initial(Z.layer) + Z.plane = initial(Z.plane) + src.P = null + if(active) + toggle_power() + else + update_icons() + +/obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength) + if(P && active) + var/power_produced = 0 + power_produced = P.air_contents.toxins*pulse_strength*20 + add_avail(power_produced) + last_power = power_produced + return + return + + +/obj/machinery/power/rad_collector/proc/update_icons() + overlays.Cut() + if(P) + overlays += image('icons/obj/singularity.dmi', "ptank") + if(stat & (NOPOWER|BROKEN)) + return + if(active) + overlays += image('icons/obj/singularity.dmi', "on") + + +/obj/machinery/power/rad_collector/proc/toggle_power() + active = !active + if(active) + icon_state = "ca_on" + flick("ca_active", src) + else + icon_state = "ca" + flick("ca_deactive", src) + update_icons() + return diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index d28518ae32b0..70ef60b11d6c 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -1,126 +1,126 @@ -/obj/machinery/field/containment - name = "Containment Field" - desc = "An energy field." - icon = 'icons/obj/singularity.dmi' - icon_state = "Contain_F" - anchored = 1 - density = 0 - move_resist = INFINITY - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - use_power = NO_POWER_USE - light_range = 4 - layer = OBJ_LAYER + 0.1 - var/obj/machinery/field/generator/FG1 = null - var/obj/machinery/field/generator/FG2 = null - -/obj/machinery/field/containment/Destroy() - FG1.fields -= src - FG2.fields -= src - return ..() - -/obj/machinery/field/containment/attack_hand(mob/user) - if(get_dist(src, user) > 1) - return 0 - else - shock_field(user) - return 1 - -/obj/machinery/field/containment/attackby(obj/item/W, mob/user, params) - shock(user) - return TRUE - -/obj/machinery/field/containment/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BURN) - playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) - if(BRUTE) - playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) - -/obj/machinery/field/containment/blob_act(obj/structure/blob/B) - return FALSE - - -/obj/machinery/field/containment/ex_act(severity) - return 0 - -/obj/machinery/field/containment/attack_animal(mob/living/simple_animal/M) - if(!FG1 || !FG2) - qdel(src) - return - if(ismegafauna(M)) - M.visible_message("[M] glows fiercely as the containment field flickers out!") - FG1.calc_power(INFINITY) //rip that 'containment' field - M.adjustHealth(-M.obj_damage) - else - ..() - -/obj/machinery/field/containment/Crossed(mob/mover, oldloc) - if(isliving(mover)) - shock_field(mover) - - if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) - bump_field(mover) - -/obj/machinery/field/containment/proc/set_master(master1,master2) - if(!master1 || !master2) - return 0 - FG1 = master1 - FG2 = master2 - return 1 - -/obj/machinery/field/containment/shock_field(mob/living/user) - if(!FG1 || !FG2) - qdel(src) - return 0 - ..() - -/obj/machinery/field/containment/Move() - qdel(src) - -// Abstract Field Class -// Used for overriding certain procs - -/obj/machinery/field - var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. - -/obj/machinery/field/CanPass(atom/movable/mover, turf/target, height=0) - if(hasShocked) - return 0 - if(isliving(mover)) // Don't let mobs through - shock_field(mover) - return 0 - if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) - bump_field(mover) - return 0 - return ..() - -/obj/machinery/field/proc/shock_field(mob/living/user) - if(isliving(user)) - var/shock_damage = min(rand(30,40),rand(30,40)) - - if(isliving(user) && !issilicon(user)) - var/stun = min(shock_damage, 15) - user.Stun(stun) - user.Weaken(10) - user.electrocute_act(shock_damage, src, 1) - - else if(issilicon(user)) - if(prob(20)) - user.Stun(2) - user.take_overall_damage(0, shock_damage) - user.visible_message("[user.name] was shocked by the [src.name]!", \ - "Energy pulse detected, system damaged!", \ - "You hear an electrical crack.") - - user.updatehealth() - bump_field(user) - -/obj/machinery/field/proc/bump_field(atom/movable/AM as mob|obj) - if(hasShocked) - return 0 - hasShocked = 1 - do_sparks(5, 1, AM.loc) - var/atom/target = get_edge_target_turf(AM, get_dir(src, get_step_away(AM, src))) - AM.throw_at(target, 200, 4) - spawn(5) - hasShocked = 0 +/obj/machinery/field/containment + name = "Containment Field" + desc = "An energy field." + icon = 'icons/obj/singularity.dmi' + icon_state = "Contain_F" + anchored = 1 + density = 0 + move_resist = INFINITY + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + use_power = NO_POWER_USE + light_range = 4 + layer = OBJ_LAYER + 0.1 + var/obj/machinery/field/generator/FG1 = null + var/obj/machinery/field/generator/FG2 = null + +/obj/machinery/field/containment/Destroy() + FG1.fields -= src + FG2.fields -= src + return ..() + +/obj/machinery/field/containment/attack_hand(mob/user) + if(get_dist(src, user) > 1) + return 0 + else + shock_field(user) + return 1 + +/obj/machinery/field/containment/attackby(obj/item/W, mob/user, params) + shock(user) + return TRUE + +/obj/machinery/field/containment/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BURN) + playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) + if(BRUTE) + playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) + +/obj/machinery/field/containment/blob_act(obj/structure/blob/B) + return FALSE + + +/obj/machinery/field/containment/ex_act(severity) + return 0 + +/obj/machinery/field/containment/attack_animal(mob/living/simple_animal/M) + if(!FG1 || !FG2) + qdel(src) + return + if(ismegafauna(M)) + M.visible_message("[M] glows fiercely as the containment field flickers out!") + FG1.calc_power(INFINITY) //rip that 'containment' field + M.adjustHealth(-M.obj_damage) + else + ..() + +/obj/machinery/field/containment/Crossed(mob/mover, oldloc) + if(isliving(mover)) + shock_field(mover) + + if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) + bump_field(mover) + +/obj/machinery/field/containment/proc/set_master(master1,master2) + if(!master1 || !master2) + return 0 + FG1 = master1 + FG2 = master2 + return 1 + +/obj/machinery/field/containment/shock_field(mob/living/user) + if(!FG1 || !FG2) + qdel(src) + return 0 + ..() + +/obj/machinery/field/containment/Move() + qdel(src) + +// Abstract Field Class +// Used for overriding certain procs + +/obj/machinery/field + var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. + +/obj/machinery/field/CanPass(atom/movable/mover, turf/target, height=0) + if(hasShocked) + return 0 + if(isliving(mover)) // Don't let mobs through + shock_field(mover) + return 0 + if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) + bump_field(mover) + return 0 + return ..() + +/obj/machinery/field/proc/shock_field(mob/living/user) + if(isliving(user)) + var/shock_damage = min(rand(30,40),rand(30,40)) + + if(isliving(user) && !issilicon(user)) + var/stun = min(shock_damage, 15) + user.Stun(stun) + user.Weaken(10) + user.electrocute_act(shock_damage, src, 1) + + else if(issilicon(user)) + if(prob(20)) + user.Stun(2) + user.take_overall_damage(0, shock_damage) + user.visible_message("[user.name] was shocked by the [src.name]!", \ + "Energy pulse detected, system damaged!", \ + "You hear an electrical crack.") + + user.updatehealth() + bump_field(user) + +/obj/machinery/field/proc/bump_field(atom/movable/AM as mob|obj) + if(hasShocked) + return 0 + hasShocked = 1 + do_sparks(5, 1, AM.loc) + var/atom/target = get_edge_target_turf(AM, get_dir(src, get_step_away(AM, src))) + AM.throw_at(target, 200, 4) + spawn(5) + hasShocked = 0 diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 2b44be14a13f..9579fcc6bf4a 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -1,346 +1,346 @@ -/obj/machinery/power/emitter - name = "Emitter" - desc = "A heavy duty industrial laser" - icon = 'icons/obj/singularity.dmi' - icon_state = "emitter" - anchored = 0 - density = 1 - req_access = list(ACCESS_ENGINE_EQUIP) - - use_power = NO_POWER_USE - idle_power_usage = 10 - active_power_usage = 300 - - var/active = 0 - var/powered = 0 - var/fire_delay = 100 - var/maximum_fire_delay = 100 - var/minimum_fire_delay = 20 - var/last_shot = 0 - var/shot_number = 0 - var/state = 0 - var/locked = 0 - - var/frequency = 0 - var/id_tag = null - var/projectile_type = /obj/item/projectile/beam/emitter - var/projectile_sound = 'sound/weapons/emitter.ogg' - var/datum/radio_frequency/radio_connection - var/datum/effect_system/spark_spread/sparks - -/obj/machinery/power/emitter/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/emitter(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - RefreshParts() - if(state == 2 && anchored) - connect_to_network() - sparks = new - sparks.attach(src) - sparks.set_up(5, 1, src) - if(frequency) - set_frequency(frequency) - -/obj/machinery/power/emitter/RefreshParts() - var/max_firedelay = 120 - var/firedelay = 120 - var/min_firedelay = 24 - var/power_usage = 350 - for(var/obj/item/stock_parts/micro_laser/L in component_parts) - max_firedelay -= 20 * L.rating - min_firedelay -= 4 * L.rating - firedelay -= 20 * L.rating - maximum_fire_delay = max_firedelay - minimum_fire_delay = min_firedelay - fire_delay = firedelay - for(var/obj/item/stock_parts/manipulator/M in component_parts) - power_usage -= 50 * M.rating - active_power_usage = power_usage - - //Radio remote control -/obj/machinery/power/emitter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - - -/obj/machinery/power/emitter/verb/rotate() - set name = "Rotate" - set category = "Object" - set src in oview(1) - - if(src.anchored || usr:stat) - to_chat(usr, "It is fastened to the floor!") - return 0 - src.dir = turn(src.dir, 90) - return 1 - -/obj/machinery/power/emitter/AltClick(mob/user) - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!Adjacent(user)) - return - rotate() - -/obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/multitool/P) - return {" - - "} - -/obj/machinery/power/emitter/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id_tag)) - return 0 - - var/on=0 - switch(signal.data["command"]) - if("on") - on=1 - - if("off") - on=0 - - if("set") - on = signal.data["state"] > 0 - - if("toggle") - on = !active - - if(anchored && state == 2 && on != active) - active=on - var/statestr=on?"on":"off" - // Spammy message_admins("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in [formatJumpTo(src)]",0,1) - log_game("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in ([x], [y], [z]) AAC prints: [jointext(signal.data["hiddenprints"], "")]") - investigate_log("turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) AAC prints: [jointext(signal.data["hiddenprints"], "")]","singulo") - update_icon() - -/obj/machinery/power/emitter/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - msg_admin_attack("Emitter deleted at ([x],[y],[z] - [ADMIN_JMP(src)])", ATKLOG_FEW) - log_game("Emitter deleted at ([x],[y],[z])") - investigate_log("deleted at ([x],[y],[z])","singulo") - QDEL_NULL(sparks) - return ..() - -/obj/machinery/power/emitter/update_icon() - if(active && powernet && avail(active_power_usage)) - icon_state = "emitter_+a" - else - icon_state = "emitter" - - -/obj/machinery/power/emitter/attack_hand(mob/user as mob) - src.add_fingerprint(user) - if(state == 2) - if(!powernet) - to_chat(user, "The emitter isn't connected to a wire.") - return 1 - if(!src.locked) - if(src.active==1) - src.active = 0 - to_chat(user, "You turn off the [src].") - message_admins("Emitter turned off by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) - log_game("Emitter turned off by [key_name(user)] in [x], [y], [z]") - investigate_log("turned off by [key_name(usr)]","singulo") - else - src.active = 1 - to_chat(user, "You turn on the [src].") - src.shot_number = 0 - src.fire_delay = maximum_fire_delay - message_admins("Emitter turned on by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) - log_game("Emitter turned on by [key_name(user)] in [x], [y], [z]") - investigate_log("turned on by [key_name(usr)]","singulo") - update_icon() - else - to_chat(user, "The controls are locked!") - else - to_chat(user, "The [src] needs to be firmly secured to the floor first.") - return 1 - - -/obj/machinery/power/emitter/emp_act(var/severity)//Emitters are hardened but still might have issues -// add_load(1000) -/* if((severity == 1)&&prob(1)&&prob(1)) - if(src.active) - src.active = 0 - src.use_power = IDLE_POWER_USE */ - return 1 - -/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M) - if(ismegafauna(M) && anchored) - state = 0 - anchored = FALSE - M.visible_message("[M] rips [src] free from its moorings!") - else - ..() - if(!anchored) - step(src, get_dir(M, src)) - -/obj/machinery/power/emitter/process() - if(stat & (BROKEN)) - return - if(src.state != 2 || (!powernet && active_power_usage)) - src.active = 0 - update_icon() - return - if(active == TRUE) - if(!active_power_usage || surplus() >= active_power_usage) - add_load(active_power_usage) - if(!powered) - powered = 1 - update_icon() - investigate_log("regained power and turned on","singulo") - else - if(powered) - powered = 0 - update_icon() - investigate_log("lost power and turned off","singulo") - return - - if(!check_delay()) - return FALSE - fire_beam() - -/obj/machinery/power/emitter/proc/check_delay() - if((last_shot + fire_delay) <= world.time) - return TRUE - return FALSE - -/obj/machinery/power/emitter/proc/fire_beam() - var/obj/item/projectile/P = new projectile_type(get_turf(src)) - playsound(get_turf(src), projectile_sound, 50, TRUE) - if(prob(35)) - sparks.start() - switch(dir) - if(NORTH) - P.yo = 20 - P.xo = 0 - if(NORTHEAST) - P.yo = 20 - P.xo = 20 - if(EAST) - P.yo = 0 - P.xo = 20 - if(SOUTHEAST) - P.yo = -20 - P.xo = 20 - if(WEST) - P.yo = 0 - P.xo = -20 - if(SOUTHWEST) - P.yo = -20 - P.xo = -20 - if(NORTHWEST) - P.yo = 20 - P.xo = -20 - else // Any other - P.yo = -20 - P.xo = 0 - - last_shot = world.time - if(shot_number < 3) - fire_delay = 20 - shot_number ++ - else - fire_delay = rand(minimum_fire_delay, maximum_fire_delay) - shot_number = 0 - P.setDir(dir) - P.starting = loc - P.Angle = null - P.fire() - return P - -/obj/machinery/power/emitter/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/wrench)) - if(active) - to_chat(user, "Turn off the [src] first.") - return - switch(state) - if(0) - state = 1 - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] secures [src.name] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear a ratchet") - src.anchored = 1 - if(1) - state = 0 - playsound(src.loc,W.usesound, 75, 1) - user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", \ - "You undo the external reinforcing bolts.", \ - "You hear a ratchet") - src.anchored = 0 - if(2) - to_chat(user, "The [src.name] needs to be unwelded from the floor.") - return - - if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) - if(emagged) - to_chat(user, "The lock seems to be broken") - return - if(src.allowed(user)) - if(active) - src.locked = !src.locked - to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") - else - src.locked = 0 //just in case it somehow gets locked - to_chat(user, "The controls can only be locked when the [src] is online") - else - to_chat(user, "Access denied.") - return - - if(default_deconstruction_screwdriver(user, "emitter_open", "emitter", W)) - return - - if(exchange_parts(user, W)) - return - - if(default_deconstruction_crowbar(user, W)) - return - - return ..() - -/obj/machinery/power/emitter/emag_act(var/mob/living/user as mob) - if(!emagged) - locked = 0 - emagged = 1 - if(user) - user.visible_message("[user.name] emags the [src.name].","You short out the lock.") - - -/obj/machinery/power/emitter/welder_act(mob/user, obj/item/I) - if(active) - to_chat(user, "Turn off [src] first.") - return - if(state == 0) - to_chat(user, "[src] needs to be wrenched to the floor.") - return - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(state == 1) - WELDER_ATTEMPT_FLOOR_WELD_MESSAGE - else if(state == 2) - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(!I.use_tool(src, user, 20, volume = I.tool_volume)) - return - if(state == 1) - WELDER_FLOOR_WELD_SUCCESS_MESSAGE - connect_to_network() - state = 2 - else if(state == 2) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - disconnect_from_network() - state = 1 +/obj/machinery/power/emitter + name = "Emitter" + desc = "A heavy duty industrial laser" + icon = 'icons/obj/singularity.dmi' + icon_state = "emitter" + anchored = 0 + density = 1 + req_access = list(ACCESS_ENGINE_EQUIP) + + use_power = NO_POWER_USE + idle_power_usage = 10 + active_power_usage = 300 + + var/active = 0 + var/powered = 0 + var/fire_delay = 100 + var/maximum_fire_delay = 100 + var/minimum_fire_delay = 20 + var/last_shot = 0 + var/shot_number = 0 + var/state = 0 + var/locked = 0 + + var/frequency = 0 + var/id_tag = null + var/projectile_type = /obj/item/projectile/beam/emitter + var/projectile_sound = 'sound/weapons/emitter.ogg' + var/datum/radio_frequency/radio_connection + var/datum/effect_system/spark_spread/sparks + +/obj/machinery/power/emitter/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/emitter(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + RefreshParts() + if(state == 2 && anchored) + connect_to_network() + sparks = new + sparks.attach(src) + sparks.set_up(5, 1, src) + if(frequency) + set_frequency(frequency) + +/obj/machinery/power/emitter/RefreshParts() + var/max_firedelay = 120 + var/firedelay = 120 + var/min_firedelay = 24 + var/power_usage = 350 + for(var/obj/item/stock_parts/micro_laser/L in component_parts) + max_firedelay -= 20 * L.rating + min_firedelay -= 4 * L.rating + firedelay -= 20 * L.rating + maximum_fire_delay = max_firedelay + minimum_fire_delay = min_firedelay + fire_delay = firedelay + for(var/obj/item/stock_parts/manipulator/M in component_parts) + power_usage -= 50 * M.rating + active_power_usage = power_usage + + //Radio remote control +/obj/machinery/power/emitter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + + +/obj/machinery/power/emitter/verb/rotate() + set name = "Rotate" + set category = "Object" + set src in oview(1) + + if(src.anchored || usr:stat) + to_chat(usr, "It is fastened to the floor!") + return 0 + src.dir = turn(src.dir, 90) + return 1 + +/obj/machinery/power/emitter/AltClick(mob/user) + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!Adjacent(user)) + return + rotate() + +/obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/multitool/P) + return {" + + "} + +/obj/machinery/power/emitter/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id_tag)) + return 0 + + var/on=0 + switch(signal.data["command"]) + if("on") + on=1 + + if("off") + on=0 + + if("set") + on = signal.data["state"] > 0 + + if("toggle") + on = !active + + if(anchored && state == 2 && on != active) + active=on + var/statestr=on?"on":"off" + // Spammy message_admins("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in [formatJumpTo(src)]",0,1) + log_game("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in ([x], [y], [z]) AAC prints: [jointext(signal.data["hiddenprints"], "")]") + investigate_log("turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) AAC prints: [jointext(signal.data["hiddenprints"], "")]","singulo") + update_icon() + +/obj/machinery/power/emitter/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + msg_admin_attack("Emitter deleted at ([x],[y],[z] - [ADMIN_JMP(src)])", ATKLOG_FEW) + log_game("Emitter deleted at ([x],[y],[z])") + investigate_log("deleted at ([x],[y],[z])","singulo") + QDEL_NULL(sparks) + return ..() + +/obj/machinery/power/emitter/update_icon() + if(active && powernet && avail(active_power_usage)) + icon_state = "emitter_+a" + else + icon_state = "emitter" + + +/obj/machinery/power/emitter/attack_hand(mob/user as mob) + src.add_fingerprint(user) + if(state == 2) + if(!powernet) + to_chat(user, "The emitter isn't connected to a wire.") + return 1 + if(!src.locked) + if(src.active==1) + src.active = 0 + to_chat(user, "You turn off the [src].") + message_admins("Emitter turned off by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) + log_game("Emitter turned off by [key_name(user)] in [x], [y], [z]") + investigate_log("turned off by [key_name(usr)]","singulo") + else + src.active = 1 + to_chat(user, "You turn on the [src].") + src.shot_number = 0 + src.fire_delay = maximum_fire_delay + message_admins("Emitter turned on by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) + log_game("Emitter turned on by [key_name(user)] in [x], [y], [z]") + investigate_log("turned on by [key_name(usr)]","singulo") + update_icon() + else + to_chat(user, "The controls are locked!") + else + to_chat(user, "The [src] needs to be firmly secured to the floor first.") + return 1 + + +/obj/machinery/power/emitter/emp_act(var/severity)//Emitters are hardened but still might have issues +// add_load(1000) +/* if((severity == 1)&&prob(1)&&prob(1)) + if(src.active) + src.active = 0 + src.use_power = IDLE_POWER_USE */ + return 1 + +/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M) + if(ismegafauna(M) && anchored) + state = 0 + anchored = FALSE + M.visible_message("[M] rips [src] free from its moorings!") + else + ..() + if(!anchored) + step(src, get_dir(M, src)) + +/obj/machinery/power/emitter/process() + if(stat & (BROKEN)) + return + if(src.state != 2 || (!powernet && active_power_usage)) + src.active = 0 + update_icon() + return + if(active == TRUE) + if(!active_power_usage || surplus() >= active_power_usage) + add_load(active_power_usage) + if(!powered) + powered = 1 + update_icon() + investigate_log("regained power and turned on","singulo") + else + if(powered) + powered = 0 + update_icon() + investigate_log("lost power and turned off","singulo") + return + + if(!check_delay()) + return FALSE + fire_beam() + +/obj/machinery/power/emitter/proc/check_delay() + if((last_shot + fire_delay) <= world.time) + return TRUE + return FALSE + +/obj/machinery/power/emitter/proc/fire_beam() + var/obj/item/projectile/P = new projectile_type(get_turf(src)) + playsound(get_turf(src), projectile_sound, 50, TRUE) + if(prob(35)) + sparks.start() + switch(dir) + if(NORTH) + P.yo = 20 + P.xo = 0 + if(NORTHEAST) + P.yo = 20 + P.xo = 20 + if(EAST) + P.yo = 0 + P.xo = 20 + if(SOUTHEAST) + P.yo = -20 + P.xo = 20 + if(WEST) + P.yo = 0 + P.xo = -20 + if(SOUTHWEST) + P.yo = -20 + P.xo = -20 + if(NORTHWEST) + P.yo = 20 + P.xo = -20 + else // Any other + P.yo = -20 + P.xo = 0 + + last_shot = world.time + if(shot_number < 3) + fire_delay = 20 + shot_number ++ + else + fire_delay = rand(minimum_fire_delay, maximum_fire_delay) + shot_number = 0 + P.setDir(dir) + P.starting = loc + P.Angle = null + P.fire() + return P + +/obj/machinery/power/emitter/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/wrench)) + if(active) + to_chat(user, "Turn off the [src] first.") + return + switch(state) + if(0) + state = 1 + playsound(src.loc, W.usesound, 75, 1) + user.visible_message("[user.name] secures [src.name] to the floor.", \ + "You secure the external reinforcing bolts to the floor.", \ + "You hear a ratchet") + src.anchored = 1 + if(1) + state = 0 + playsound(src.loc,W.usesound, 75, 1) + user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", \ + "You undo the external reinforcing bolts.", \ + "You hear a ratchet") + src.anchored = 0 + if(2) + to_chat(user, "The [src.name] needs to be unwelded from the floor.") + return + + if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) + if(emagged) + to_chat(user, "The lock seems to be broken") + return + if(src.allowed(user)) + if(active) + src.locked = !src.locked + to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") + else + src.locked = 0 //just in case it somehow gets locked + to_chat(user, "The controls can only be locked when the [src] is online") + else + to_chat(user, "Access denied.") + return + + if(default_deconstruction_screwdriver(user, "emitter_open", "emitter", W)) + return + + if(exchange_parts(user, W)) + return + + if(default_deconstruction_crowbar(user, W)) + return + + return ..() + +/obj/machinery/power/emitter/emag_act(var/mob/living/user as mob) + if(!emagged) + locked = 0 + emagged = 1 + if(user) + user.visible_message("[user.name] emags the [src.name].","You short out the lock.") + + +/obj/machinery/power/emitter/welder_act(mob/user, obj/item/I) + if(active) + to_chat(user, "Turn off [src] first.") + return + if(state == 0) + to_chat(user, "[src] needs to be wrenched to the floor.") + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(state == 1) + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + else if(state == 2) + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + if(state == 1) + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + connect_to_network() + state = 2 + else if(state == 2) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + disconnect_from_network() + state = 1 diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 7dcd7c71135b..62f2ed36065f 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -1,337 +1,337 @@ -/* -field_generator power level display - The icon used for the field_generator need to have 'num_power_levels' number of icon states - named 'Field_Gen +p[num]' where 'num' ranges from 1 to 'num_power_levels' - - The power level is displayed using overlays. The current displayed power level is stored in 'powerlevel'. - The overlay in use and the powerlevel variable must be kept in sync. A powerlevel equal to 0 means that - no power level overlay is currently in the overlays list. - -Aygar -*/ - -#define field_generator_max_power 250 - -#define FG_UNSECURED 0 -#define FG_SECURED 1 -#define FG_WELDED 2 - -#define FG_OFFLINE 0 -#define FG_CHARGING 1 -#define FG_ONLINE 2 - -/obj/machinery/field/generator - name = "Field Generator" - desc = "A large thermal battery that projects a high amount of energy when powered." - icon = 'icons/obj/machines/field_generator.dmi' - icon_state = "Field_Gen" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - max_integrity = 500 - //100% immune to lasers and energy projectiles since it absorbs their energy. - armor = list("melee" = 25, "bullet" = 10, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70) - var/const/num_power_levels = 6 // Total number of power level icon has - var/power_level = 0 - var/active = FG_OFFLINE - var/power = 20 // Current amount of power - var/state = FG_UNSECURED - var/warming_up = 0 - var/list/obj/machinery/field/containment/fields - var/list/obj/machinery/field/generator/connected_gens - var/clean_up = 0 - -/obj/machinery/field/generator/update_icon() - overlays.Cut() - if(warming_up) - overlays += "+a[warming_up]" - if(fields.len) - overlays += "+on" - if(power_level) - overlays += "+p[power_level]" - - -/obj/machinery/field/generator/Initialize(mapload) - . = ..() - fields = list() - connected_gens = list() - - -/obj/machinery/field/generator/process() - if(active == FG_ONLINE) - calc_power() - -/obj/machinery/field/generator/attack_hand(mob/user) - if(state == FG_WELDED) - if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on - if(active >= FG_CHARGING) - to_chat(user, "You are unable to turn off the [name] once it is online!") - return 1 - else - user.visible_message("[user.name] turns on the [name].", \ - "You turn on the [name].", \ - "You hear heavy droning.") - turn_on() - investigate_log("activated by [user.key].","singulo") - - add_fingerprint(user) - else - to_chat(user, "[src] needs to be firmly secured to the floor first!") - - -/obj/machinery/field/generator/attackby(obj/item/W, mob/user, params) - if(active) - to_chat(user, "[src] needs to be off!") - return - else if(istype(W, /obj/item/wrench)) - switch(state) - if(FG_UNSECURED) - if(isinspace()) return - state = FG_SECURED - playsound(loc, W.usesound, 75, 1) - user.visible_message("[user.name] secures [name] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear ratchet.") - anchored = 1 - if(FG_SECURED) - state = FG_UNSECURED - playsound(loc, W.usesound, 75, 1) - user.visible_message("[user.name] unsecures [name] reinforcing bolts from the floor.", \ - "You undo the external reinforcing bolts.", \ - "You hear ratchet.") - anchored = 0 - if(FG_WELDED) - to_chat(user, "The [name] needs to be unwelded from the floor!") - else - return ..() - - -/obj/machinery/field/generator/welder_act(mob/user, obj/item/I) - . = TRUE - if(state == FG_UNSECURED) - to_chat(user, "[src] needs to be wrenched to the floor!") - return - if(!I.tool_use_check(user, 0)) - return - if(state == FG_SECURED) - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - else if(state == FG_WELDED) - WELDER_ATTEMPT_FLOOR_WELD_MESSAGE - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - if(state == FG_SECURED) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - state = FG_WELDED - else if(state == FG_WELDED) - WELDER_FLOOR_WELD_SUCCESS_MESSAGE - state = FG_SECURED - -/obj/machinery/field/generator/emp_act() - return 0 - -/obj/machinery/field/generator/attack_animal(mob/living/simple_animal/M) - if(M.environment_smash & ENVIRONMENT_SMASH_RWALLS && active == FG_OFFLINE && state != FG_UNSECURED) - state = FG_UNSECURED - anchored = FALSE - M.visible_message("[M] rips [src] free from its moorings!") - else - ..() - if(!anchored) - step(src, get_dir(M, src)) - -/obj/machinery/field/generator/blob_act(obj/structure/blob/B) - if(active) - return 0 - else - ..() - -/obj/machinery/field/generator/bullet_act(obj/item/projectile/Proj) - if(Proj.flag != "bullet") - power = min(power + Proj.damage, field_generator_max_power) - check_power_level() - return 0 - - -/obj/machinery/field/generator/Destroy() - cleanup() - return ..() - - -/obj/machinery/field/generator/proc/check_power_level() - var/new_level = round(num_power_levels * power / field_generator_max_power) - if(new_level != power_level) - power_level = new_level - update_icon() - -/obj/machinery/field/generator/proc/turn_off() - active = FG_OFFLINE - spawn(1) - cleanup() - while(warming_up > 0 && !active) - sleep(50) - warming_up-- - update_icon() - -/obj/machinery/field/generator/proc/turn_on() - active = FG_CHARGING - spawn(1) - while(warming_up < 3 && active) - sleep(50) - warming_up++ - update_icon() - if(warming_up >= 3) - start_fields() - - -/obj/machinery/field/generator/proc/calc_power() - var/power_draw = 2 + fields.len - - if(draw_power(round(power_draw/2, 1))) - check_power_level() - return 1 - else - visible_message("The [name] shuts down!", "You hear something shutting down.") - turn_off() - investigate_log("ran out of power and deactivated","singulo") - power = 0 - check_power_level() - return 0 - -//This could likely be better, it tends to start loopin if you have a complex generator loop setup. Still works well enough to run the engine fields will likely recode the field gens and fields sometime -Mport -/obj/machinery/field/generator/proc/draw_power(draw = 0, failsafe = 0, obj/machinery/field/generator/G = null, obj/machinery/field/generator/last = null) - if((G && (G == src)) || (failsafe >= 8))//Loopin, set fail - return 0 - else - failsafe++ - - if(power >= draw)//We have enough power - power -= draw - return 1 - - else//Need more power - draw -= power - power = 0 - for(var/CG in connected_gens) - var/obj/machinery/field/generator/FG = CG - if(FG == last)//We just asked you - continue - if(G)//Another gen is askin for power and we dont have it - if(FG.draw_power(draw,failsafe,G,src))//Can you take the load - return 1 - else - return 0 - else//We are askin another for power - if(FG.draw_power(draw,failsafe,src,src)) - return 1 - else - return 0 - - -/obj/machinery/field/generator/proc/start_fields() - if(state != FG_WELDED || !anchored) - turn_off() - return - spawn(1) - setup_field(1) - spawn(2) - setup_field(2) - spawn(3) - setup_field(4) - spawn(4) - setup_field(8) - spawn(5) - active = FG_ONLINE - - -/obj/machinery/field/generator/proc/setup_field(NSEW) - var/turf/T = loc - if(!istype(T)) - return 0 - - var/obj/machinery/field/generator/G = null - var/steps = 0 - if(!NSEW)//Make sure its ran right - return 0 - for(var/dist in 0 to 7) // checks out to 8 tiles away for another generator - T = get_step(T, NSEW) - if(T.density)//We cant shoot a field though this - return 0 - - G = locate(/obj/machinery/field/generator) in T - if(G) - steps -= 1 - if(!G.active) - return 0 - break - - for(var/TC in T.contents) - var/atom/A = TC - if(ismob(A)) - continue - if(A.density) - return 0 - - steps++ - - if(!G) - return 0 - - T = loc - for(var/dist in 0 to steps) // creates each field tile - var/field_dir = get_dir(T,get_step(G.loc, NSEW)) - T = get_step(T, NSEW) - if(!locate(/obj/machinery/field/containment) in T) - var/obj/machinery/field/containment/CF = new/obj/machinery/field/containment() - CF.set_master(src,G) - CF.loc = T - CF.dir = field_dir - fields += CF - G.fields += CF - for(var/mob/living/L in T) - CF.Crossed(L, null) - - connected_gens |= G - G.connected_gens |= src - update_icon() - - -/obj/machinery/field/generator/proc/cleanup() - clean_up = 1 - for(var/F in fields) - qdel(F) - - for(var/CG in connected_gens) - var/obj/machinery/field/generator/FG = CG - FG.connected_gens -= src - if(!FG.clean_up)//Makes the other gens clean up as well - FG.cleanup() - connected_gens -= FG - clean_up = 0 - update_icon() - - //This is here to help fight the "hurr durr, release singulo cos nobody will notice before the - //singulo eats the evidence". It's not fool-proof but better than nothing. - //I want to avoid using global variables. - spawn(1) - var/temp = 1 //stops spam - for(var/obj/singularity/O in GLOB.singularities) - if(O.last_warning && temp) - if((world.time - O.last_warning) > 50) //to stop message-spam - temp = 0 - message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (JMP)",1) - investigate_log("has failed whilst a singulo exists.","singulo") - O.last_warning = world.time - -/obj/machinery/field/generator/shock_field(mob/living/user) - if(fields.len) - ..() - -/obj/machinery/field/generator/bump_field(atom/movable/AM as mob|obj) - if(fields.len) - ..() - -#undef FG_UNSECURED -#undef FG_SECURED -#undef FG_WELDED - -#undef FG_OFFLINE -#undef FG_CHARGING -#undef FG_ONLINE +/* +field_generator power level display + The icon used for the field_generator need to have 'num_power_levels' number of icon states + named 'Field_Gen +p[num]' where 'num' ranges from 1 to 'num_power_levels' + + The power level is displayed using overlays. The current displayed power level is stored in 'powerlevel'. + The overlay in use and the powerlevel variable must be kept in sync. A powerlevel equal to 0 means that + no power level overlay is currently in the overlays list. + -Aygar +*/ + +#define field_generator_max_power 250 + +#define FG_UNSECURED 0 +#define FG_SECURED 1 +#define FG_WELDED 2 + +#define FG_OFFLINE 0 +#define FG_CHARGING 1 +#define FG_ONLINE 2 + +/obj/machinery/field/generator + name = "Field Generator" + desc = "A large thermal battery that projects a high amount of energy when powered." + icon = 'icons/obj/machines/field_generator.dmi' + icon_state = "Field_Gen" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + max_integrity = 500 + //100% immune to lasers and energy projectiles since it absorbs their energy. + armor = list("melee" = 25, "bullet" = 10, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70) + var/const/num_power_levels = 6 // Total number of power level icon has + var/power_level = 0 + var/active = FG_OFFLINE + var/power = 20 // Current amount of power + var/state = FG_UNSECURED + var/warming_up = 0 + var/list/obj/machinery/field/containment/fields + var/list/obj/machinery/field/generator/connected_gens + var/clean_up = 0 + +/obj/machinery/field/generator/update_icon() + overlays.Cut() + if(warming_up) + overlays += "+a[warming_up]" + if(fields.len) + overlays += "+on" + if(power_level) + overlays += "+p[power_level]" + + +/obj/machinery/field/generator/Initialize(mapload) + . = ..() + fields = list() + connected_gens = list() + + +/obj/machinery/field/generator/process() + if(active == FG_ONLINE) + calc_power() + +/obj/machinery/field/generator/attack_hand(mob/user) + if(state == FG_WELDED) + if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on + if(active >= FG_CHARGING) + to_chat(user, "You are unable to turn off the [name] once it is online!") + return 1 + else + user.visible_message("[user.name] turns on the [name].", \ + "You turn on the [name].", \ + "You hear heavy droning.") + turn_on() + investigate_log("activated by [user.key].","singulo") + + add_fingerprint(user) + else + to_chat(user, "[src] needs to be firmly secured to the floor first!") + + +/obj/machinery/field/generator/attackby(obj/item/W, mob/user, params) + if(active) + to_chat(user, "[src] needs to be off!") + return + else if(istype(W, /obj/item/wrench)) + switch(state) + if(FG_UNSECURED) + if(isinspace()) return + state = FG_SECURED + playsound(loc, W.usesound, 75, 1) + user.visible_message("[user.name] secures [name] to the floor.", \ + "You secure the external reinforcing bolts to the floor.", \ + "You hear ratchet.") + anchored = 1 + if(FG_SECURED) + state = FG_UNSECURED + playsound(loc, W.usesound, 75, 1) + user.visible_message("[user.name] unsecures [name] reinforcing bolts from the floor.", \ + "You undo the external reinforcing bolts.", \ + "You hear ratchet.") + anchored = 0 + if(FG_WELDED) + to_chat(user, "The [name] needs to be unwelded from the floor!") + else + return ..() + + +/obj/machinery/field/generator/welder_act(mob/user, obj/item/I) + . = TRUE + if(state == FG_UNSECURED) + to_chat(user, "[src] needs to be wrenched to the floor!") + return + if(!I.tool_use_check(user, 0)) + return + if(state == FG_SECURED) + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + else if(state == FG_WELDED) + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + if(state == FG_SECURED) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + state = FG_WELDED + else if(state == FG_WELDED) + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + state = FG_SECURED + +/obj/machinery/field/generator/emp_act() + return 0 + +/obj/machinery/field/generator/attack_animal(mob/living/simple_animal/M) + if(M.environment_smash & ENVIRONMENT_SMASH_RWALLS && active == FG_OFFLINE && state != FG_UNSECURED) + state = FG_UNSECURED + anchored = FALSE + M.visible_message("[M] rips [src] free from its moorings!") + else + ..() + if(!anchored) + step(src, get_dir(M, src)) + +/obj/machinery/field/generator/blob_act(obj/structure/blob/B) + if(active) + return 0 + else + ..() + +/obj/machinery/field/generator/bullet_act(obj/item/projectile/Proj) + if(Proj.flag != "bullet") + power = min(power + Proj.damage, field_generator_max_power) + check_power_level() + return 0 + + +/obj/machinery/field/generator/Destroy() + cleanup() + return ..() + + +/obj/machinery/field/generator/proc/check_power_level() + var/new_level = round(num_power_levels * power / field_generator_max_power) + if(new_level != power_level) + power_level = new_level + update_icon() + +/obj/machinery/field/generator/proc/turn_off() + active = FG_OFFLINE + spawn(1) + cleanup() + while(warming_up > 0 && !active) + sleep(50) + warming_up-- + update_icon() + +/obj/machinery/field/generator/proc/turn_on() + active = FG_CHARGING + spawn(1) + while(warming_up < 3 && active) + sleep(50) + warming_up++ + update_icon() + if(warming_up >= 3) + start_fields() + + +/obj/machinery/field/generator/proc/calc_power() + var/power_draw = 2 + fields.len + + if(draw_power(round(power_draw/2, 1))) + check_power_level() + return 1 + else + visible_message("The [name] shuts down!", "You hear something shutting down.") + turn_off() + investigate_log("ran out of power and deactivated","singulo") + power = 0 + check_power_level() + return 0 + +//This could likely be better, it tends to start loopin if you have a complex generator loop setup. Still works well enough to run the engine fields will likely recode the field gens and fields sometime -Mport +/obj/machinery/field/generator/proc/draw_power(draw = 0, failsafe = 0, obj/machinery/field/generator/G = null, obj/machinery/field/generator/last = null) + if((G && (G == src)) || (failsafe >= 8))//Loopin, set fail + return 0 + else + failsafe++ + + if(power >= draw)//We have enough power + power -= draw + return 1 + + else//Need more power + draw -= power + power = 0 + for(var/CG in connected_gens) + var/obj/machinery/field/generator/FG = CG + if(FG == last)//We just asked you + continue + if(G)//Another gen is askin for power and we dont have it + if(FG.draw_power(draw,failsafe,G,src))//Can you take the load + return 1 + else + return 0 + else//We are askin another for power + if(FG.draw_power(draw,failsafe,src,src)) + return 1 + else + return 0 + + +/obj/machinery/field/generator/proc/start_fields() + if(state != FG_WELDED || !anchored) + turn_off() + return + spawn(1) + setup_field(1) + spawn(2) + setup_field(2) + spawn(3) + setup_field(4) + spawn(4) + setup_field(8) + spawn(5) + active = FG_ONLINE + + +/obj/machinery/field/generator/proc/setup_field(NSEW) + var/turf/T = loc + if(!istype(T)) + return 0 + + var/obj/machinery/field/generator/G = null + var/steps = 0 + if(!NSEW)//Make sure its ran right + return 0 + for(var/dist in 0 to 7) // checks out to 8 tiles away for another generator + T = get_step(T, NSEW) + if(T.density)//We cant shoot a field though this + return 0 + + G = locate(/obj/machinery/field/generator) in T + if(G) + steps -= 1 + if(!G.active) + return 0 + break + + for(var/TC in T.contents) + var/atom/A = TC + if(ismob(A)) + continue + if(A.density) + return 0 + + steps++ + + if(!G) + return 0 + + T = loc + for(var/dist in 0 to steps) // creates each field tile + var/field_dir = get_dir(T,get_step(G.loc, NSEW)) + T = get_step(T, NSEW) + if(!locate(/obj/machinery/field/containment) in T) + var/obj/machinery/field/containment/CF = new/obj/machinery/field/containment() + CF.set_master(src,G) + CF.loc = T + CF.dir = field_dir + fields += CF + G.fields += CF + for(var/mob/living/L in T) + CF.Crossed(L, null) + + connected_gens |= G + G.connected_gens |= src + update_icon() + + +/obj/machinery/field/generator/proc/cleanup() + clean_up = 1 + for(var/F in fields) + qdel(F) + + for(var/CG in connected_gens) + var/obj/machinery/field/generator/FG = CG + FG.connected_gens -= src + if(!FG.clean_up)//Makes the other gens clean up as well + FG.cleanup() + connected_gens -= FG + clean_up = 0 + update_icon() + + //This is here to help fight the "hurr durr, release singulo cos nobody will notice before the + //singulo eats the evidence". It's not fool-proof but better than nothing. + //I want to avoid using global variables. + spawn(1) + var/temp = 1 //stops spam + for(var/obj/singularity/O in GLOB.singularities) + if(O.last_warning && temp) + if((world.time - O.last_warning) > 50) //to stop message-spam + temp = 0 + message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (JMP)",1) + investigate_log("has failed whilst a singulo exists.","singulo") + O.last_warning = world.time + +/obj/machinery/field/generator/shock_field(mob/living/user) + if(fields.len) + ..() + +/obj/machinery/field/generator/bump_field(atom/movable/AM as mob|obj) + if(fields.len) + ..() + +#undef FG_UNSECURED +#undef FG_SECURED +#undef FG_WELDED + +#undef FG_OFFLINE +#undef FG_CHARGING +#undef FG_ONLINE diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm index c5494f372e77..9bed8105a3e6 100644 --- a/code/modules/power/singularity/generator.dm +++ b/code/modules/power/singularity/generator.dm @@ -1,38 +1,38 @@ -/////SINGULARITY SPAWNER -/obj/machinery/the_singularitygen - name = "Gravitational Singularity Generator" - desc = "An odd device which produces a Gravitational Singularity when set up." - icon = 'icons/obj/singularity.dmi' - icon_state = "TheSingGen" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - resistance_flags = FIRE_PROOF - var/energy = 0 - var/creation_type = /obj/singularity - -/obj/machinery/the_singularitygen/process() - var/turf/T = get_turf(src) - if(src.energy >= 200) - message_admins("A [creation_type] has been created at [x], [y], [z] (JMP)") - investigate_log("A [creation_type] has been created at [x], [y], [z]","singulo") - - var/obj/singularity/S = new creation_type(T, 50) - transfer_fingerprints_to(S) - if(src) qdel(src) - -/obj/machinery/the_singularitygen/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/wrench)) - anchored = !anchored - playsound(src.loc, W.usesound, 75, 1) - if(anchored) - user.visible_message("[user.name] secures [src.name] to the floor.", \ - "You secure the [src.name] to the floor.", \ - "You hear a ratchet") - src.add_hiddenprint(user) - else - user.visible_message("[user.name] unsecures [src.name] from the floor.", \ - "You unsecure the [src.name] from the floor.", \ - "You hear a ratchet") - return - return ..() +/////SINGULARITY SPAWNER +/obj/machinery/the_singularitygen + name = "Gravitational Singularity Generator" + desc = "An odd device which produces a Gravitational Singularity when set up." + icon = 'icons/obj/singularity.dmi' + icon_state = "TheSingGen" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + resistance_flags = FIRE_PROOF + var/energy = 0 + var/creation_type = /obj/singularity + +/obj/machinery/the_singularitygen/process() + var/turf/T = get_turf(src) + if(src.energy >= 200) + message_admins("A [creation_type] has been created at [x], [y], [z] (JMP)") + investigate_log("A [creation_type] has been created at [x], [y], [z]","singulo") + + var/obj/singularity/S = new creation_type(T, 50) + transfer_fingerprints_to(S) + if(src) qdel(src) + +/obj/machinery/the_singularitygen/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/wrench)) + anchored = !anchored + playsound(src.loc, W.usesound, 75, 1) + if(anchored) + user.visible_message("[user.name] secures [src.name] to the floor.", \ + "You secure the [src.name] to the floor.", \ + "You hear a ratchet") + src.add_hiddenprint(user) + else + user.visible_message("[user.name] unsecures [src.name] from the floor.", \ + "You unsecure the [src.name] from the floor.", \ + "You hear a ratchet") + return + return ..() diff --git a/code/modules/power/singularity/investigate.dm b/code/modules/power/singularity/investigate.dm index 6e22b1ad118f..43e8c9f8a8be 100644 --- a/code/modules/power/singularity/investigate.dm +++ b/code/modules/power/singularity/investigate.dm @@ -1,4 +1,4 @@ /area/engine/engineering/power_alert(var/alarming) if(alarming) investigate_log("has a power alarm!","singulo") - ..() \ No newline at end of file + ..() diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index c7fcfcda5e73..e409ccc7602f 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -1,59 +1,59 @@ -/obj/effect/accelerated_particle - name = "Accelerated Particles" - desc = "Small things moving very fast." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "particle" - anchored = TRUE - density = FALSE - var/movement_range = 10 - var/energy = 10 - var/speed = 1 - -/obj/effect/accelerated_particle/weak - movement_range = 8 - energy = 5 - -/obj/effect/accelerated_particle/strong - movement_range = 15 - energy = 15 - -/obj/effect/accelerated_particle/powerful - movement_range = 20 - energy = 50 - - -/obj/effect/accelerated_particle/Initialize(loc) - . = ..() - addtimer(CALLBACK(src, .proc/propagate), 1) - RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/try_irradiate) - RegisterSignal(src, COMSIG_MOVABLE_CROSSED, .proc/try_irradiate) - QDEL_IN(src, movement_range) - -/obj/effect/accelerated_particle/proc/try_irradiate(src, atom/A) - if(isliving(A)) - var/mob/living/L = A - L.apply_effect((energy * 6), IRRADIATE, 0) - else if(istype(A, /obj/machinery/the_singularitygen)) - var/obj/machinery/the_singularitygen/S = A - S.energy += energy - else if(istype(A, /obj/structure/blob)) - var/obj/structure/blob/B = A - B.take_damage(energy * 0.6) - movement_range = 0 - -/obj/effect/accelerated_particle/Bump(obj/singularity/S) - if(!istype(S)) - return ..() - S.energy += energy - - -/obj/effect/accelerated_particle/ex_act(severity) - qdel(src) - -/obj/effect/accelerated_particle/singularity_pull() - return - -/obj/effect/accelerated_particle/proc/propagate() - addtimer(CALLBACK(src, .proc/propagate), 1) - if(!step(src,dir)) - forceMove(get_step(src, dir)) +/obj/effect/accelerated_particle + name = "Accelerated Particles" + desc = "Small things moving very fast." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "particle" + anchored = TRUE + density = FALSE + var/movement_range = 10 + var/energy = 10 + var/speed = 1 + +/obj/effect/accelerated_particle/weak + movement_range = 8 + energy = 5 + +/obj/effect/accelerated_particle/strong + movement_range = 15 + energy = 15 + +/obj/effect/accelerated_particle/powerful + movement_range = 20 + energy = 50 + + +/obj/effect/accelerated_particle/Initialize(loc) + . = ..() + addtimer(CALLBACK(src, .proc/propagate), 1) + RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/try_irradiate) + RegisterSignal(src, COMSIG_MOVABLE_CROSSED, .proc/try_irradiate) + QDEL_IN(src, movement_range) + +/obj/effect/accelerated_particle/proc/try_irradiate(src, atom/A) + if(isliving(A)) + var/mob/living/L = A + L.apply_effect((energy * 6), IRRADIATE, 0) + else if(istype(A, /obj/machinery/the_singularitygen)) + var/obj/machinery/the_singularitygen/S = A + S.energy += energy + else if(istype(A, /obj/structure/blob)) + var/obj/structure/blob/B = A + B.take_damage(energy * 0.6) + movement_range = 0 + +/obj/effect/accelerated_particle/Bump(obj/singularity/S) + if(!istype(S)) + return ..() + S.energy += energy + + +/obj/effect/accelerated_particle/ex_act(severity) + qdel(src) + +/obj/effect/accelerated_particle/singularity_pull() + return + +/obj/effect/accelerated_particle/proc/propagate() + addtimer(CALLBACK(src, .proc/propagate), 1) + if(!step(src,dir)) + forceMove(get_step(src, dir)) diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index 059f5e78b7b8..20113d61a359 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -1,360 +1,360 @@ -/*Composed of 7 parts -3 Particle emitters -proc -emit_particle() - -1 power box -the only part of this thing that uses power, can hack to mess with the pa/make it better - -1 fuel chamber -contains procs for mixing gas and whatever other fuel it uses -mix_gas() - -1 gas holder WIP -acts like a tank valve on the ground that you wrench gas tanks onto -proc -extract_gas() -return_gas() -attach_tank() -remove_tank() -get_available_mix() - -1 End Cap - -1 Control computer -interface for the pa, acts like a computer with an html menu for diff parts and a status report -all other parts contain only a ref to this -a /machine/, tells the others to do work -contains ref for all parts -proc -process() -check_build() - -Setup map - |EC| -CC|FC| - |PB| -PE|PE|PE - - -Icon Addemdum -Icon system is much more robust, and the icons are all variable based. -Each part has a reference string, powered, strength, and contruction values. -Using this the update_icon() proc is simplified a bit (using for absolutely was problematic with naming), -so the icon_state comes out be: -"[reference][strength]", with a switch controlling construction_states and ensuring that it doesn't -power on while being contructed, and all these variables are set by the computer through it's scan list -Essential order of the icons: -Standard - [reference] -Wrenched - [reference] -Wired - [reference]w -Closed - [reference]c -Powered - [reference]p[strength] -Strength being set by the computer and a null strength (Computer is powered off or inactive) returns a 'null', counting as empty -So, hopefully this is helpful if any more icons are to be added/changed/wondering what the hell is going on here - -*/ -#define ACCELERATOR_UNWRENCHED 0 -#define ACCELERATOR_WRENCHED 1 -#define ACCELERATOR_WIRED 2 -#define ACCELERATOR_READY 3 - -/obj/structure/particle_accelerator - name = "Particle Accelerator" - desc = "Part of a Particle Accelerator." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "none" - anchored = 0 - density = 1 - max_integrity = 500 - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 80) - var/obj/machinery/particle_accelerator/control_box/master = null - var/construction_state = 0 - var/reference = null - var/powered = 0 - var/strength = null - var/desc_holder = null - -/obj/structure/particle_accelerator/Destroy() - construction_state = 0 - if(master) - master.part_scan() - return ..() - -/obj/structure/particle_accelerator/end_cap - name = "Alpha Particle Generation Array" - desc_holder = "This is where Alpha particles are generated from \[REDACTED\]" - icon_state = "end_cap" - reference = "end_cap" - -/obj/structure/particle_accelerator/update_icon() - ..() - return - - -/obj/structure/particle_accelerator/verb/rotate() - set name = "Rotate Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 270) - return 1 - -/obj/structure/particle_accelerator/AltClick(mob/user) - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!Adjacent(user)) - return - rotate() - -/obj/structure/particle_accelerator/verb/rotateccw() - set name = "Rotate Counter Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 90) - return 1 - -/obj/structure/particle_accelerator/examine(mob/user) - switch(construction_state) - if(0) - desc = text("A [name], looks like it's not attached to the flooring") - if(1) - desc = text("A [name], it is missing some cables") - if(2) - desc = text("A [name], the panel is open") - if(3) - desc = text("The [name] is assembled") - if(powered) - desc = desc_holder - . = ..() - -/obj/structure/particle_accelerator/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal (loc, 5) - qdel(src) - -/obj/structure/particle_accelerator/Move() - . = ..() - if(master && master.active) - master.toggle_power() - investigate_log("was moved whilst active; it powered down.","singulo") - -/obj/machinery/particle_accelerator/control_box/blob_act(obj/structure/blob/B) - if(prob(50)) - qdel(src) - -/obj/structure/particle_accelerator/update_icon() - switch(construction_state) - if(0,1) - icon_state="[reference]" - if(2) - icon_state="[reference]w" - if(3) - if(powered) - icon_state="[reference]p[strength]" - else - icon_state="[reference]c" - return - -/obj/structure/particle_accelerator/proc/update_state() - if(master) - master.update_state() - return 0 - - -/obj/structure/particle_accelerator/proc/report_ready(var/obj/O) - if(O && (O == master)) - if(construction_state >= 3) - return 1 - return 0 - - -/obj/structure/particle_accelerator/proc/report_master() - if(master) - return master - return 0 - - -/obj/structure/particle_accelerator/proc/connect_master(var/obj/O) - if(O && istype(O,/obj/machinery/particle_accelerator/control_box)) - if(O.dir == dir) - master = O - return 1 - return 0 - -/obj/structure/particle_accelerator/attackby(obj/item/W, mob/user, params) - if(!iscoil(W)) - return ..() - if(construction_state == ACCELERATOR_WRENCHED) - var/obj/item/stack/cable_coil/C = W - if(C.use(1)) - playsound(loc, C.usesound, 50, 1) - user.visible_message("[user.name] adds wires to the [name].", \ - "You add some wires.") - construction_state = ACCELERATOR_WIRED - update_icon() - -/obj/structure/particle_accelerator/screwdriver_act(mob/user, obj/item/I) - if(construction_state != ACCELERATOR_WIRED && construction_state != ACCELERATOR_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(construction_state == ACCELERATOR_WIRED) - SCREWDRIVER_CLOSE_PANEL_MESSAGE - construction_state = ACCELERATOR_READY - - else - construction_state = ACCELERATOR_WIRED - SCREWDRIVER_OPEN_PANEL_MESSAGE - update_state() - update_icon() - -/obj/structure/particle_accelerator/wirecutter_act(mob/user, obj/item/I) - if(construction_state != ACCELERATOR_WIRED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - WIRECUTTER_SNIP_MESSAGE - construction_state = ACCELERATOR_WRENCHED - -/obj/structure/particle_accelerator/wrench_act(mob/user, obj/item/I) - if(construction_state != ACCELERATOR_UNWRENCHED && construction_state != ACCELERATOR_WRENCHED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(construction_state == ACCELERATOR_UNWRENCHED) - anchored = TRUE - WRENCH_ANCHOR_MESSAGE - construction_state = ACCELERATOR_WRENCHED - else - anchored = FALSE - WRENCH_UNANCHOR_MESSAGE - construction_state = ACCELERATOR_UNWRENCHED - update_icon() - - -/obj/machinery/particle_accelerator - name = "Particle Accelerator" - desc = "Part of a Particle Accelerator." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "none" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - idle_power_usage = 0 - active_power_usage = 0 - var/construction_state = 0 - var/active = 0 - var/reference = null - var/powered = null - var/strength = 0 - var/desc_holder = null - - -/obj/machinery/particle_accelerator/verb/rotate() - set name = "Rotate Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 270) - return 1 - -/obj/machinery/particle_accelerator/verb/rotateccw() - set name = "Rotate Counter-Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 90) - return 1 - -/obj/machinery/particle_accelerator/update_icon() - return - -/obj/machinery/particle_accelerator/attackby(obj/item/W, mob/user, params) - if(!iscoil(W)) - return ..() - if(construction_state == ACCELERATOR_WRENCHED) - var/obj/item/stack/cable_coil/C = W - if(C.use(1)) - playsound(loc, C.usesound, 50, 1) - user.visible_message("[user.name] adds wires to the [name].", \ - "You add some wires.") - construction_state = ACCELERATOR_WIRED - update_icon() - -/obj/machinery/particle_accelerator/screwdriver_act(mob/user, obj/item/I) - if(construction_state != ACCELERATOR_WIRED && construction_state != ACCELERATOR_READY) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(construction_state == ACCELERATOR_WIRED) - SCREWDRIVER_CLOSE_PANEL_MESSAGE - construction_state = ACCELERATOR_READY - use_power = IDLE_POWER_USE - else - construction_state = ACCELERATOR_WIRED - SCREWDRIVER_OPEN_PANEL_MESSAGE - use_power = NO_POWER_USE - update_state() - update_icon() - -/obj/machinery/particle_accelerator/wirecutter_act(mob/user, obj/item/I) - if(construction_state != ACCELERATOR_WIRED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - WIRECUTTER_SNIP_MESSAGE - construction_state = ACCELERATOR_WRENCHED - -/obj/machinery/particle_accelerator/wrench_act(mob/user, obj/item/I) - if(construction_state != ACCELERATOR_UNWRENCHED && construction_state != ACCELERATOR_WRENCHED) - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(construction_state == ACCELERATOR_UNWRENCHED) - anchored = TRUE - WRENCH_ANCHOR_MESSAGE - construction_state = ACCELERATOR_WRENCHED - else - anchored = FALSE - WRENCH_UNANCHOR_MESSAGE - construction_state = ACCELERATOR_UNWRENCHED - update_icon() - - -/obj/machinery/particle_accelerator/proc/update_state() - return 0 - - -#undef ACCELERATOR_UNWRENCHED -#undef ACCELERATOR_WRENCHED -#undef ACCELERATOR_WIRED -#undef ACCELERATOR_READY +/*Composed of 7 parts +3 Particle emitters +proc +emit_particle() + +1 power box +the only part of this thing that uses power, can hack to mess with the pa/make it better + +1 fuel chamber +contains procs for mixing gas and whatever other fuel it uses +mix_gas() + +1 gas holder WIP +acts like a tank valve on the ground that you wrench gas tanks onto +proc +extract_gas() +return_gas() +attach_tank() +remove_tank() +get_available_mix() + +1 End Cap + +1 Control computer +interface for the pa, acts like a computer with an html menu for diff parts and a status report +all other parts contain only a ref to this +a /machine/, tells the others to do work +contains ref for all parts +proc +process() +check_build() + +Setup map + |EC| +CC|FC| + |PB| +PE|PE|PE + + +Icon Addemdum +Icon system is much more robust, and the icons are all variable based. +Each part has a reference string, powered, strength, and contruction values. +Using this the update_icon() proc is simplified a bit (using for absolutely was problematic with naming), +so the icon_state comes out be: +"[reference][strength]", with a switch controlling construction_states and ensuring that it doesn't +power on while being contructed, and all these variables are set by the computer through it's scan list +Essential order of the icons: +Standard - [reference] +Wrenched - [reference] +Wired - [reference]w +Closed - [reference]c +Powered - [reference]p[strength] +Strength being set by the computer and a null strength (Computer is powered off or inactive) returns a 'null', counting as empty +So, hopefully this is helpful if any more icons are to be added/changed/wondering what the hell is going on here + +*/ +#define ACCELERATOR_UNWRENCHED 0 +#define ACCELERATOR_WRENCHED 1 +#define ACCELERATOR_WIRED 2 +#define ACCELERATOR_READY 3 + +/obj/structure/particle_accelerator + name = "Particle Accelerator" + desc = "Part of a Particle Accelerator." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "none" + anchored = 0 + density = 1 + max_integrity = 500 + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 80) + var/obj/machinery/particle_accelerator/control_box/master = null + var/construction_state = 0 + var/reference = null + var/powered = 0 + var/strength = null + var/desc_holder = null + +/obj/structure/particle_accelerator/Destroy() + construction_state = 0 + if(master) + master.part_scan() + return ..() + +/obj/structure/particle_accelerator/end_cap + name = "Alpha Particle Generation Array" + desc_holder = "This is where Alpha particles are generated from \[REDACTED\]" + icon_state = "end_cap" + reference = "end_cap" + +/obj/structure/particle_accelerator/update_icon() + ..() + return + + +/obj/structure/particle_accelerator/verb/rotate() + set name = "Rotate Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 270) + return 1 + +/obj/structure/particle_accelerator/AltClick(mob/user) + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!Adjacent(user)) + return + rotate() + +/obj/structure/particle_accelerator/verb/rotateccw() + set name = "Rotate Counter Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 90) + return 1 + +/obj/structure/particle_accelerator/examine(mob/user) + switch(construction_state) + if(0) + desc = text("A [name], looks like it's not attached to the flooring") + if(1) + desc = text("A [name], it is missing some cables") + if(2) + desc = text("A [name], the panel is open") + if(3) + desc = text("The [name] is assembled") + if(powered) + desc = desc_holder + . = ..() + +/obj/structure/particle_accelerator/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal (loc, 5) + qdel(src) + +/obj/structure/particle_accelerator/Move() + . = ..() + if(master && master.active) + master.toggle_power() + investigate_log("was moved whilst active; it powered down.","singulo") + +/obj/machinery/particle_accelerator/control_box/blob_act(obj/structure/blob/B) + if(prob(50)) + qdel(src) + +/obj/structure/particle_accelerator/update_icon() + switch(construction_state) + if(0,1) + icon_state="[reference]" + if(2) + icon_state="[reference]w" + if(3) + if(powered) + icon_state="[reference]p[strength]" + else + icon_state="[reference]c" + return + +/obj/structure/particle_accelerator/proc/update_state() + if(master) + master.update_state() + return 0 + + +/obj/structure/particle_accelerator/proc/report_ready(var/obj/O) + if(O && (O == master)) + if(construction_state >= 3) + return 1 + return 0 + + +/obj/structure/particle_accelerator/proc/report_master() + if(master) + return master + return 0 + + +/obj/structure/particle_accelerator/proc/connect_master(var/obj/O) + if(O && istype(O,/obj/machinery/particle_accelerator/control_box)) + if(O.dir == dir) + master = O + return 1 + return 0 + +/obj/structure/particle_accelerator/attackby(obj/item/W, mob/user, params) + if(!iscoil(W)) + return ..() + if(construction_state == ACCELERATOR_WRENCHED) + var/obj/item/stack/cable_coil/C = W + if(C.use(1)) + playsound(loc, C.usesound, 50, 1) + user.visible_message("[user.name] adds wires to the [name].", \ + "You add some wires.") + construction_state = ACCELERATOR_WIRED + update_icon() + +/obj/structure/particle_accelerator/screwdriver_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED && construction_state != ACCELERATOR_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_WIRED) + SCREWDRIVER_CLOSE_PANEL_MESSAGE + construction_state = ACCELERATOR_READY + + else + construction_state = ACCELERATOR_WIRED + SCREWDRIVER_OPEN_PANEL_MESSAGE + update_state() + update_icon() + +/obj/structure/particle_accelerator/wirecutter_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + WIRECUTTER_SNIP_MESSAGE + construction_state = ACCELERATOR_WRENCHED + +/obj/structure/particle_accelerator/wrench_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_UNWRENCHED && construction_state != ACCELERATOR_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_UNWRENCHED) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE + construction_state = ACCELERATOR_WRENCHED + else + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + construction_state = ACCELERATOR_UNWRENCHED + update_icon() + + +/obj/machinery/particle_accelerator + name = "Particle Accelerator" + desc = "Part of a Particle Accelerator." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "none" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + idle_power_usage = 0 + active_power_usage = 0 + var/construction_state = 0 + var/active = 0 + var/reference = null + var/powered = null + var/strength = 0 + var/desc_holder = null + + +/obj/machinery/particle_accelerator/verb/rotate() + set name = "Rotate Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 270) + return 1 + +/obj/machinery/particle_accelerator/verb/rotateccw() + set name = "Rotate Counter-Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 90) + return 1 + +/obj/machinery/particle_accelerator/update_icon() + return + +/obj/machinery/particle_accelerator/attackby(obj/item/W, mob/user, params) + if(!iscoil(W)) + return ..() + if(construction_state == ACCELERATOR_WRENCHED) + var/obj/item/stack/cable_coil/C = W + if(C.use(1)) + playsound(loc, C.usesound, 50, 1) + user.visible_message("[user.name] adds wires to the [name].", \ + "You add some wires.") + construction_state = ACCELERATOR_WIRED + update_icon() + +/obj/machinery/particle_accelerator/screwdriver_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED && construction_state != ACCELERATOR_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_WIRED) + SCREWDRIVER_CLOSE_PANEL_MESSAGE + construction_state = ACCELERATOR_READY + use_power = IDLE_POWER_USE + else + construction_state = ACCELERATOR_WIRED + SCREWDRIVER_OPEN_PANEL_MESSAGE + use_power = NO_POWER_USE + update_state() + update_icon() + +/obj/machinery/particle_accelerator/wirecutter_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + WIRECUTTER_SNIP_MESSAGE + construction_state = ACCELERATOR_WRENCHED + +/obj/machinery/particle_accelerator/wrench_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_UNWRENCHED && construction_state != ACCELERATOR_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_UNWRENCHED) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE + construction_state = ACCELERATOR_WRENCHED + else + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + construction_state = ACCELERATOR_UNWRENCHED + update_icon() + + +/obj/machinery/particle_accelerator/proc/update_state() + return 0 + + +#undef ACCELERATOR_UNWRENCHED +#undef ACCELERATOR_WRENCHED +#undef ACCELERATOR_WIRED +#undef ACCELERATOR_READY diff --git a/code/modules/power/singularity/particle_accelerator/particle_chamber.dm b/code/modules/power/singularity/particle_accelerator/particle_chamber.dm index 411c039d4803..c3c058a52891 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_chamber.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_chamber.dm @@ -1,10 +1,10 @@ -/obj/structure/particle_accelerator/fuel_chamber - name = "EM Acceleration Chamber" - desc_holder = "This part is where the Alpha particles are accelerated to radical speeds." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "fuel_chamber" - reference = "fuel_chamber" - -/obj/structure/particle_accelerator/fuel_chamber/update_icon() - ..() - return \ No newline at end of file +/obj/structure/particle_accelerator/fuel_chamber + name = "EM Acceleration Chamber" + desc_holder = "This part is where the Alpha particles are accelerated to radical speeds." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "fuel_chamber" + reference = "fuel_chamber" + +/obj/structure/particle_accelerator/fuel_chamber/update_icon() + ..() + return diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index 80de53fe22d2..f1a7f130592c 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -1,279 +1,279 @@ -/obj/machinery/particle_accelerator/control_box - name = "Particle Accelerator Control Console" - desc = "This part controls the density of the particles." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "control_box" - reference = "control_box" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - idle_power_usage = 500 - active_power_usage = 10000 - construction_state = 0 - active = 0 - dir = 1 - var/strength_upper_limit = 2 - var/interface_control = 1 - var/list/obj/structure/particle_accelerator/connected_parts - var/assembled = 0 - var/parts = null - var/datum/wires/particle_acc/control_box/wires = null - -/obj/machinery/particle_accelerator/control_box/Initialize(mapload) - . = ..() - wires = new(src) - connected_parts = list() - update_icon() - -/obj/machinery/particle_accelerator/control_box/Destroy() - if(active) - toggle_power() - QDEL_NULL(wires) - return ..() - -/obj/machinery/particle_accelerator/control_box/attack_ghost(user as mob) - return attack_hand(user) - -/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user as mob) - if(construction_state >= 3) - interact(user) - else if(construction_state == 2) // Wires exposed - wires.Interact(user) - -/obj/machinery/particle_accelerator/control_box/update_state() - if(construction_state < 3) - use_power = NO_POWER_USE - assembled = 0 - active = 0 - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = null - part.powered = 0 - part.update_icon() - connected_parts = list() - return - if(!part_scan()) - use_power = IDLE_POWER_USE - active = 0 - connected_parts = list() - - return - -/obj/machinery/particle_accelerator/control_box/update_icon() - if(active) - icon_state = "[reference]p[strength]" - else - if(stat & NOPOWER) - icon_state = "[reference]w" - return - else if(use_power && assembled) - icon_state = "[reference]p" - else - switch(construction_state) - if(0) - icon_state = "[reference]" - if(1) - icon_state = "[reference]" - if(2) - icon_state = "[reference]w" - else - icon_state = "[reference]c" - return - -/obj/machinery/particle_accelerator/control_box/Topic(href, href_list) - if(..(href, href_list)) - return 1 - - if(!interface_control) - to_chat(usr, "ERROR: Request timed out. Check wire contacts.") - return - - if(href_list["close"]) - usr << browse(null, "window=pacontrol") - usr.unset_machine() - return - if(href_list["togglep"]) - if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE)) - toggle_power() - - else if(href_list["scan"]) - part_scan() - - else if(href_list["strengthup"]) - if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) - add_strength() - - else if(href_list["strengthdown"]) - if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) - remove_strength() - - updateDialog() - update_icon() - return - - -/obj/machinery/particle_accelerator/control_box/proc/strength_change() - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = strength - part.update_icon() - -/obj/machinery/particle_accelerator/control_box/proc/add_strength(var/s) - if(assembled) - strength++ - if(strength > strength_upper_limit) - strength = strength_upper_limit - else - message_admins("PA Control Computer increased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) - log_game("PA Control Computer increased to [strength] by [key_name(usr)] in ([x],[y],[z])") - investigate_log("increased to [strength] by [key_name(usr)]","singulo") - use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has increased the PA Control Computer to [strength].") - - investigate_log("increased to [strength] by [usr.key]","singulo") - strength_change() - -/obj/machinery/particle_accelerator/control_box/proc/remove_strength(var/s) - if(assembled) - strength-- - if(strength < 0) - strength = 0 - else - message_admins("PA Control Computer decreased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) - log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in ([x],[y],[z])") - investigate_log("decreased to [strength] by [key_name(usr)]","singulo") - use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has decreased the PA Control Computer to [strength].") - - strength_change() - -/obj/machinery/particle_accelerator/control_box/power_change() - ..() - if(stat & NOPOWER) - active = 0 - use_power = NO_POWER_USE - else if(!stat && construction_state <= 3) - use_power = IDLE_POWER_USE - update_icon() - - if((stat & NOPOWER) || (!stat && construction_state <= 3)) //Only update the part icons if something's changed (i.e. any of the above condition sets are met). - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = null - part.powered = 0 - part.update_icon() - return - - -/obj/machinery/particle_accelerator/control_box/process() - if(active) - //a part is missing! - if(length(connected_parts) < 6) - investigate_log("lost a connected part; It powered down.","singulo") - toggle_power() - return - //emit some particles - for(var/obj/structure/particle_accelerator/particle_emitter/PE in connected_parts) - if(PE) - PE.emit_particle(strength) - return - - -/obj/machinery/particle_accelerator/control_box/proc/part_scan() - for(var/obj/structure/particle_accelerator/fuel_chamber/F in orange(1,src)) - dir = F.dir - connected_parts = list() - var/tally = 0 - var/ldir = turn(dir,-90) - var/rdir = turn(dir,90) - var/odir = turn(dir,180) - var/turf/T = loc - T = get_step(T,rdir) - if(check_part(T,/obj/structure/particle_accelerator/fuel_chamber)) - tally++ - T = get_step(T,odir) - if(check_part(T,/obj/structure/particle_accelerator/end_cap)) - tally++ - T = get_step(T,dir) - T = get_step(T,dir) - if(check_part(T,/obj/structure/particle_accelerator/power_box)) - tally++ - T = get_step(T,dir) - if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/center)) - tally++ - T = get_step(T,ldir) - if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/left)) - tally++ - T = get_step(T,rdir) - T = get_step(T,rdir) - if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/right)) - tally++ - if(tally >= 6) - assembled = 1 - return 1 - else - assembled = 0 - return 0 - - -/obj/machinery/particle_accelerator/control_box/proc/check_part(var/turf/T, var/type) - if(!(T)||!(type)) - return 0 - var/obj/structure/particle_accelerator/PA = locate(/obj/structure/particle_accelerator) in T - if(istype(PA, type)) - if(PA.connect_master(src)) - if(PA.report_ready(src)) - connected_parts.Add(PA) - return 1 - return 0 - - -/obj/machinery/particle_accelerator/control_box/proc/toggle_power() - active = !active - investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo") - if(active) - msg_admin_attack("PA Control Computer turned ON by [key_name_admin(usr)]", ATKLOG_FEW) - log_game("PA Control Computer turned ON by [key_name(usr)] in ([x],[y],[z])") - use_log += text("\[[time_stamp()]\] [key_name(usr)] has turned on the PA Control Computer.") - if(active) - use_power = ACTIVE_POWER_USE - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = strength - part.powered = 1 - part.update_icon() - else - use_power = IDLE_POWER_USE - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = null - part.powered = 0 - part.update_icon() - return 1 - - -/obj/machinery/particle_accelerator/control_box/interact(mob/user) - if(((get_dist(src, user) > 1) && !isobserver(user)) || (stat & (BROKEN|NOPOWER))) - if(!istype(user, /mob/living/silicon)) - user.unset_machine() - user << browse(null, "window=pacontrol") - return - user.set_machine(src) - - var/dat = "" - dat += "Close

    " - dat += "

    Status

    " - if(!assembled) - dat += "Unable to detect all parts!
    " - dat += "Run Scan

    " - else - dat += "All parts in place.

    " - dat += "Power:" - if(active) - dat += "On
    " - else - dat += "Off
    " - dat += "Toggle Power

    " - dat += "Particle Strength: [strength] " - dat += "--|++

    " - - //user << browse(dat, "window=pacontrol;size=420x500") - //onclose(user, "pacontrol") - var/datum/browser/popup = new(user, "pacontrol", name, 420, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - return +/obj/machinery/particle_accelerator/control_box + name = "Particle Accelerator Control Console" + desc = "This part controls the density of the particles." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "control_box" + reference = "control_box" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + idle_power_usage = 500 + active_power_usage = 10000 + construction_state = 0 + active = 0 + dir = 1 + var/strength_upper_limit = 2 + var/interface_control = 1 + var/list/obj/structure/particle_accelerator/connected_parts + var/assembled = 0 + var/parts = null + var/datum/wires/particle_acc/control_box/wires = null + +/obj/machinery/particle_accelerator/control_box/Initialize(mapload) + . = ..() + wires = new(src) + connected_parts = list() + update_icon() + +/obj/machinery/particle_accelerator/control_box/Destroy() + if(active) + toggle_power() + QDEL_NULL(wires) + return ..() + +/obj/machinery/particle_accelerator/control_box/attack_ghost(user as mob) + return attack_hand(user) + +/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user as mob) + if(construction_state >= 3) + interact(user) + else if(construction_state == 2) // Wires exposed + wires.Interact(user) + +/obj/machinery/particle_accelerator/control_box/update_state() + if(construction_state < 3) + use_power = NO_POWER_USE + assembled = 0 + active = 0 + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = null + part.powered = 0 + part.update_icon() + connected_parts = list() + return + if(!part_scan()) + use_power = IDLE_POWER_USE + active = 0 + connected_parts = list() + + return + +/obj/machinery/particle_accelerator/control_box/update_icon() + if(active) + icon_state = "[reference]p[strength]" + else + if(stat & NOPOWER) + icon_state = "[reference]w" + return + else if(use_power && assembled) + icon_state = "[reference]p" + else + switch(construction_state) + if(0) + icon_state = "[reference]" + if(1) + icon_state = "[reference]" + if(2) + icon_state = "[reference]w" + else + icon_state = "[reference]c" + return + +/obj/machinery/particle_accelerator/control_box/Topic(href, href_list) + if(..(href, href_list)) + return 1 + + if(!interface_control) + to_chat(usr, "ERROR: Request timed out. Check wire contacts.") + return + + if(href_list["close"]) + usr << browse(null, "window=pacontrol") + usr.unset_machine() + return + if(href_list["togglep"]) + if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE)) + toggle_power() + + else if(href_list["scan"]) + part_scan() + + else if(href_list["strengthup"]) + if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) + add_strength() + + else if(href_list["strengthdown"]) + if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) + remove_strength() + + updateDialog() + update_icon() + return + + +/obj/machinery/particle_accelerator/control_box/proc/strength_change() + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = strength + part.update_icon() + +/obj/machinery/particle_accelerator/control_box/proc/add_strength(var/s) + if(assembled) + strength++ + if(strength > strength_upper_limit) + strength = strength_upper_limit + else + message_admins("PA Control Computer increased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) + log_game("PA Control Computer increased to [strength] by [key_name(usr)] in ([x],[y],[z])") + investigate_log("increased to [strength] by [key_name(usr)]","singulo") + use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has increased the PA Control Computer to [strength].") + + investigate_log("increased to [strength] by [usr.key]","singulo") + strength_change() + +/obj/machinery/particle_accelerator/control_box/proc/remove_strength(var/s) + if(assembled) + strength-- + if(strength < 0) + strength = 0 + else + message_admins("PA Control Computer decreased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) + log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in ([x],[y],[z])") + investigate_log("decreased to [strength] by [key_name(usr)]","singulo") + use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has decreased the PA Control Computer to [strength].") + + strength_change() + +/obj/machinery/particle_accelerator/control_box/power_change() + ..() + if(stat & NOPOWER) + active = 0 + use_power = NO_POWER_USE + else if(!stat && construction_state <= 3) + use_power = IDLE_POWER_USE + update_icon() + + if((stat & NOPOWER) || (!stat && construction_state <= 3)) //Only update the part icons if something's changed (i.e. any of the above condition sets are met). + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = null + part.powered = 0 + part.update_icon() + return + + +/obj/machinery/particle_accelerator/control_box/process() + if(active) + //a part is missing! + if(length(connected_parts) < 6) + investigate_log("lost a connected part; It powered down.","singulo") + toggle_power() + return + //emit some particles + for(var/obj/structure/particle_accelerator/particle_emitter/PE in connected_parts) + if(PE) + PE.emit_particle(strength) + return + + +/obj/machinery/particle_accelerator/control_box/proc/part_scan() + for(var/obj/structure/particle_accelerator/fuel_chamber/F in orange(1,src)) + dir = F.dir + connected_parts = list() + var/tally = 0 + var/ldir = turn(dir,-90) + var/rdir = turn(dir,90) + var/odir = turn(dir,180) + var/turf/T = loc + T = get_step(T,rdir) + if(check_part(T,/obj/structure/particle_accelerator/fuel_chamber)) + tally++ + T = get_step(T,odir) + if(check_part(T,/obj/structure/particle_accelerator/end_cap)) + tally++ + T = get_step(T,dir) + T = get_step(T,dir) + if(check_part(T,/obj/structure/particle_accelerator/power_box)) + tally++ + T = get_step(T,dir) + if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/center)) + tally++ + T = get_step(T,ldir) + if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/left)) + tally++ + T = get_step(T,rdir) + T = get_step(T,rdir) + if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/right)) + tally++ + if(tally >= 6) + assembled = 1 + return 1 + else + assembled = 0 + return 0 + + +/obj/machinery/particle_accelerator/control_box/proc/check_part(var/turf/T, var/type) + if(!(T)||!(type)) + return 0 + var/obj/structure/particle_accelerator/PA = locate(/obj/structure/particle_accelerator) in T + if(istype(PA, type)) + if(PA.connect_master(src)) + if(PA.report_ready(src)) + connected_parts.Add(PA) + return 1 + return 0 + + +/obj/machinery/particle_accelerator/control_box/proc/toggle_power() + active = !active + investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo") + if(active) + msg_admin_attack("PA Control Computer turned ON by [key_name_admin(usr)]", ATKLOG_FEW) + log_game("PA Control Computer turned ON by [key_name(usr)] in ([x],[y],[z])") + use_log += text("\[[time_stamp()]\] [key_name(usr)] has turned on the PA Control Computer.") + if(active) + use_power = ACTIVE_POWER_USE + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = strength + part.powered = 1 + part.update_icon() + else + use_power = IDLE_POWER_USE + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = null + part.powered = 0 + part.update_icon() + return 1 + + +/obj/machinery/particle_accelerator/control_box/interact(mob/user) + if(((get_dist(src, user) > 1) && !isobserver(user)) || (stat & (BROKEN|NOPOWER))) + if(!istype(user, /mob/living/silicon)) + user.unset_machine() + user << browse(null, "window=pacontrol") + return + user.set_machine(src) + + var/dat = "" + dat += "Close

    " + dat += "

    Status

    " + if(!assembled) + dat += "Unable to detect all parts!
    " + dat += "Run Scan

    " + else + dat += "All parts in place.

    " + dat += "Power:" + if(active) + dat += "On
    " + else + dat += "Off
    " + dat += "Toggle Power

    " + dat += "Particle Strength: [strength] " + dat += "--|++

    " + + //user << browse(dat, "window=pacontrol;size=420x500") + //onclose(user, "pacontrol") + var/datum/browser/popup = new(user, "pacontrol", name, 420, 500) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + return diff --git a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm b/code/modules/power/singularity/particle_accelerator/particle_emitter.dm index c2a1ccfa8da7..a3a7d4b2cfc9 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_emitter.dm @@ -1,48 +1,48 @@ -/obj/structure/particle_accelerator/particle_emitter - name = "EM Containment Grid" - desc_holder = "This part launches the Alpha particles. You might not want to stand near this end." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "none" - var/fire_delay = 50 - var/last_shot = 0 - -/obj/structure/particle_accelerator/particle_emitter/center - icon_state = "emitter_center" - reference = "emitter_center" - -/obj/structure/particle_accelerator/particle_emitter/left - icon_state = "emitter_left" - reference = "emitter_left" - -/obj/structure/particle_accelerator/particle_emitter/right - icon_state = "emitter_right" - reference = "emitter_right" - -/obj/structure/particle_accelerator/particle_emitter/update_icon() - ..() - return - -/obj/structure/particle_accelerator/particle_emitter/proc/set_delay(var/delay) - if(delay && delay >= 0) - fire_delay = delay - return 1 - return 0 - - -/obj/structure/particle_accelerator/particle_emitter/proc/emit_particle(strength = 0) - if((last_shot + fire_delay) <= world.time) - last_shot = world.time - var/turf/T = get_turf(src) - var/obj/effect/accelerated_particle/P - switch(strength) - if(0) - P = new/obj/effect/accelerated_particle/weak(T) - if(1) - P = new/obj/effect/accelerated_particle(T) - if(2) - P = new/obj/effect/accelerated_particle/strong(T) - if(3) - P = new/obj/effect/accelerated_particle/powerful(T) - P.setDir(dir) - return TRUE - return FALSE \ No newline at end of file +/obj/structure/particle_accelerator/particle_emitter + name = "EM Containment Grid" + desc_holder = "This part launches the Alpha particles. You might not want to stand near this end." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "none" + var/fire_delay = 50 + var/last_shot = 0 + +/obj/structure/particle_accelerator/particle_emitter/center + icon_state = "emitter_center" + reference = "emitter_center" + +/obj/structure/particle_accelerator/particle_emitter/left + icon_state = "emitter_left" + reference = "emitter_left" + +/obj/structure/particle_accelerator/particle_emitter/right + icon_state = "emitter_right" + reference = "emitter_right" + +/obj/structure/particle_accelerator/particle_emitter/update_icon() + ..() + return + +/obj/structure/particle_accelerator/particle_emitter/proc/set_delay(var/delay) + if(delay && delay >= 0) + fire_delay = delay + return 1 + return 0 + + +/obj/structure/particle_accelerator/particle_emitter/proc/emit_particle(strength = 0) + if((last_shot + fire_delay) <= world.time) + last_shot = world.time + var/turf/T = get_turf(src) + var/obj/effect/accelerated_particle/P + switch(strength) + if(0) + P = new/obj/effect/accelerated_particle/weak(T) + if(1) + P = new/obj/effect/accelerated_particle(T) + if(2) + P = new/obj/effect/accelerated_particle/strong(T) + if(3) + P = new/obj/effect/accelerated_particle/powerful(T) + P.setDir(dir) + return TRUE + return FALSE diff --git a/code/modules/power/singularity/particle_accelerator/particle_power.dm b/code/modules/power/singularity/particle_accelerator/particle_power.dm index 75ae7eb32639..527c745436e3 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_power.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_power.dm @@ -1,6 +1,6 @@ -/obj/structure/particle_accelerator/power_box - name = "Particle Focusing EM Lens" - desc_holder = "This part uses electromagnetic waves to focus the Alpha particles." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "power_box" - reference = "power_box" \ No newline at end of file +/obj/structure/particle_accelerator/power_box + name = "Particle Focusing EM Lens" + desc_holder = "This part uses electromagnetic waves to focus the Alpha particles." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "power_box" + reference = "power_box" diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index c67e676ee132..61e1d7864907 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -1,443 +1,443 @@ -/obj/singularity - name = "gravitational singularity" - desc = "A gravitational singularity." - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - anchored = 1 - density = 1 - layer = MASSIVE_OBJ_LAYER - light_range = 6 - appearance_flags = 0 - var/current_size = 1 - var/allowed_size = 1 - var/contained = 1 //Are we going to move around? - var/energy = 100 //How strong are we? - var/dissipate = 1 //Do we lose energy over time? - var/dissipate_delay = 10 - var/dissipate_track = 0 - var/dissipate_strength = 1 //How much energy do we lose? - var/move_self = 1 //Do we move on our own? - var/grav_pull = 4 //How many tiles out do we pull? - move_resist = INFINITY //no, you don't get to push the singulo. Not even you OP wizard gateway statues - var/consume_range = 0 //How many tiles out do we eat - var/event_chance = 15 //Prob for event each tick - var/target = null //its target. moves towards the target if it has one - var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing - var/last_warning - var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six - allow_spin = 0 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF - -/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) - //CARN: admin-alert for chuckle-fuckery. - admin_investigate_setup() - - src.energy = starting_energy - ..() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - GLOB.singularities += src - for(var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines) - if(singubeacon.active) - target = singubeacon - break - -/obj/singularity/Destroy() - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - GLOB.singularities -= src - target = null - return ..() - -/obj/singularity/Move(atom/newloc, direct) - if(current_size >= STAGE_FIVE || check_turfs_in(direct)) - last_failed_movement = 0//Reset this because we moved - return ..() - else - last_failed_movement = direct - return 0 - - -/obj/singularity/attack_hand(mob/user) - consume(user) - return 1 - -/obj/singularity/attack_alien(mob/user) - consume(user) - -/obj/singularity/attack_animal(mob/user) - consume(user) - -/obj/singularity/attackby(obj/item/W, mob/user, params) - consume(user) - return 1 - -/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! - return 0 - -/obj/singularity/blob_act(obj/structure/blob/B) - return - -/obj/singularity/ex_act(severity) - switch(severity) - if(1) - if(current_size <= STAGE_TWO) - investigate_log("has been destroyed by a heavy explosion.","singulo") - qdel(src) - return - else - energy -= round(((energy+1)/2),1) - if(2) - energy -= round(((energy+1)/3),1) - if(3) - energy -= round(((energy+1)/4),1) - return - - -/obj/singularity/bullet_act(obj/item/projectile/P) - qdel(P) - return 0 //Will there be an impact? Who knows. Will we see it? No. - - -/obj/singularity/Bump(atom/A) - consume(A) - return - - -/obj/singularity/Bumped(atom/A) - consume(A) - return - - -/obj/singularity/process() - if(allowed_size >= STAGE_TWO) - // Start moving even before we reach "true" stage two. - // If we are stage one and are sufficiently energetic to be allowed to 2, - // it might mean we are stuck in a corner somewere. So move around to try to expand. - move() - if(current_size >= STAGE_TWO) - pulse() - if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit - event() - eat() - dissipate() - check_energy() - - return - - -/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one. - return - - -/obj/singularity/proc/admin_investigate_setup() - last_warning = world.time - var/count = locate(/obj/machinery/field/containment) in urange(30, src, 1) - if(!count) - message_admins("A singularity has been created without containment fields active at [x], [y], [z] (JMP)") - investigate_log("was created. [count?"":"No containment fields were active"]","singulo") - -/obj/singularity/proc/dissipate() - if(!dissipate) - return - if(dissipate_track >= dissipate_delay) - src.energy -= dissipate_strength - dissipate_track = 0 - else - dissipate_track++ - - -/obj/singularity/proc/expand(force_size = 0) - var/temp_allowed_size = src.allowed_size - if(force_size) - temp_allowed_size = force_size - if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter) - temp_allowed_size = STAGE_FIVE - switch(temp_allowed_size) - if(STAGE_ONE) - current_size = STAGE_ONE - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - pixel_x = 0 - pixel_y = 0 - grav_pull = 4 - consume_range = 0 - dissipate_delay = 10 - dissipate_track = 0 - dissipate_strength = 1 - if(STAGE_TWO) - if((check_turfs_in(1,1))&&(check_turfs_in(2,1))&&(check_turfs_in(4,1))&&(check_turfs_in(8,1))) - current_size = STAGE_TWO - icon = 'icons/effects/96x96.dmi' - icon_state = "singularity_s3" - pixel_x = -32 - pixel_y = -32 - grav_pull = 6 - consume_range = 1 - dissipate_delay = 5 - dissipate_track = 0 - dissipate_strength = 5 - if(STAGE_THREE) - if((check_turfs_in(1,2))&&(check_turfs_in(2,2))&&(check_turfs_in(4,2))&&(check_turfs_in(8,2))) - current_size = STAGE_THREE - icon = 'icons/effects/160x160.dmi' - icon_state = "singularity_s5" - pixel_x = -64 - pixel_y = -64 - grav_pull = 8 - consume_range = 2 - dissipate_delay = 4 - dissipate_track = 0 - dissipate_strength = 20 - if(STAGE_FOUR) - if((check_turfs_in(1,3))&&(check_turfs_in(2,3))&&(check_turfs_in(4,3))&&(check_turfs_in(8,3))) - current_size = STAGE_FOUR - icon = 'icons/effects/224x224.dmi' - icon_state = "singularity_s7" - pixel_x = -96 - pixel_y = -96 - grav_pull = 10 - consume_range = 3 - dissipate_delay = 10 - dissipate_track = 0 - dissipate_strength = 10 - if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything - current_size = STAGE_FIVE - icon = 'icons/effects/288x288.dmi' - icon_state = "singularity_s9" - pixel_x = -128 - pixel_y = -128 - grav_pull = 10 - consume_range = 4 - dissipate = 0 //It cant go smaller due to e loss - if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard. - current_size = STAGE_SIX - icon = 'icons/effects/352x352.dmi' - icon_state = "singularity_s11" - pixel_x = -160 - pixel_y = -160 - grav_pull = 15 - consume_range = 5 - dissipate = 0 - if(current_size == allowed_size) - investigate_log("grew to size [current_size]","singulo") - return 1 - else if(current_size < (--temp_allowed_size)) - expand(temp_allowed_size) - else - return 0 - - -/obj/singularity/proc/check_energy() - if(energy <= 0) - investigate_log("collapsed.","singulo") - qdel(src) - return 0 - switch(energy)//Some of these numbers might need to be changed up later -Mport - if(1 to 199) - allowed_size = STAGE_ONE - if(200 to 499) - allowed_size = STAGE_TWO - if(500 to 999) - allowed_size = STAGE_THREE - if(1000 to 1999) - allowed_size = STAGE_FOUR - if(2000 to INFINITY) - if(energy >= 3000 && consumedSupermatter) - allowed_size = STAGE_SIX - else - allowed_size = STAGE_FIVE - if(current_size != allowed_size) - expand() - return 1 - - -/obj/singularity/proc/eat() - set background = BACKGROUND_ENABLED - for(var/tile in spiral_range_turfs(grav_pull, src)) - var/turf/T = tile - if(!T || !isturf(loc)) - continue - if(get_dist(T, src) > consume_range) - T.singularity_pull(src, current_size) - else - consume(T) - for(var/thing in T) - if(isturf(loc) && thing != src) - var/atom/movable/X = thing - if(get_dist(X, src) > consume_range) - X.singularity_pull(src, current_size) - else - consume(X) - CHECK_TICK - - -/obj/singularity/proc/consume(atom/A) - var/gain = A.singularity_act(current_size) - src.energy += gain - if(istype(A, /obj/machinery/power/supermatter_shard) && !consumedSupermatter) - desc = "[initial(desc)] It glows fiercely with inner fire." - name = "supermatter-charged [initial(name)]" - consumedSupermatter = 1 - set_light(10) - return - - -/obj/singularity/proc/move(force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one - - step(src, movement_dir) - - -/obj/singularity/proc/check_turfs_in(direction = 0, step = 0) - if(!direction) - return 0 - var/steps = 0 - if(!step) - switch(current_size) - if(STAGE_ONE) - steps = 1 - if(STAGE_TWO) - steps = 3//Yes this is right - if(STAGE_THREE) - steps = 3 - if(STAGE_FOUR) - steps = 4 - if(STAGE_FIVE) - steps = 5 - else - steps = step - var/list/turfs = list() - var/turf/T = src.loc - for(var/i = 1 to steps) - T = get_step(T,direction) - if(!isturf(T)) - return 0 - turfs.Add(T) - var/dir2 = 0 - var/dir3 = 0 - switch(direction) - if(NORTH||SOUTH) - dir2 = 4 - dir3 = 8 - if(EAST||WEST) - dir2 = 1 - dir3 = 2 - var/turf/T2 = T - for(var/j = 1 to steps-1) - T2 = get_step(T2,dir2) - if(!isturf(T2)) - return 0 - turfs.Add(T2) - for(var/k = 1 to steps-1) - T = get_step(T,dir3) - if(!isturf(T)) - return 0 - turfs.Add(T) - for(var/turf/T3 in turfs) - if(isnull(T3)) - continue - if(!can_move(T3)) - return 0 - return 1 - - -/obj/singularity/proc/can_move(turf/T) - if(!T) - return 0 - if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T)) - return 0 - else if(locate(/obj/machinery/field/generator) in T) - var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T - if(G && G.active) - return 0 - else if(locate(/obj/machinery/shieldwallgen) in T) - var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T - if(S && S.active) - return 0 - return 1 - - -/obj/singularity/proc/event() - var/numb = pick(1,2,3,4,5,6) - switch(numb) - if(1)//EMP - emp_area() - if(2,3)//tox damage all carbon mobs in area - toxmob() - if(4)//Stun mobs who lack optic scanners - mezzer() - if(5,6) //Sets all nearby mobs on fire - if(current_size < STAGE_SIX) - return 0 - combust_mobs() - else - return 0 - return 1 - - -/obj/singularity/proc/toxmob() - var/toxrange = 10 - var/radiation = 15 - var/radiationmin = 3 - if(energy>200) - radiation += round((energy-150)/10,1) - radiationmin = round((radiation/5),1) - for(var/mob/living/M in view(toxrange, src.loc)) - M.apply_effect(rand(radiationmin,radiation), IRRADIATE) - - -/obj/singularity/proc/combust_mobs() - for(var/mob/living/carbon/C in urange(20, src, 1)) - C.visible_message("[C]'s skin bursts into flame!", \ - "You feel an inner fire as your skin bursts into flames!") - C.adjust_fire_stacks(5) - C.IgniteMob() - return - - -/obj/singularity/proc/mezzer() - for(var/mob/living/carbon/M in oviewers(8, src)) - if(istype(M, /mob/living/carbon/brain)) //Ignore brains - continue - - if(M.stat == CONSCIOUS) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(istype(H.glasses, /obj/item/clothing/glasses/meson)) - var/obj/item/clothing/glasses/meson/MS = H.glasses - if(MS.vision_flags == SEE_TURFS) - to_chat(H, "You look directly into the [src.name], good thing you had your protective eyewear on!") - return - - M.apply_effect(3, STUN) - M.visible_message("[M] stares blankly at the [src.name]!", \ - "You look directly into the [src.name] and feel weak.") - return - - -/obj/singularity/proc/emp_area() - empulse(src, 8, 10) - return - - -/obj/singularity/proc/pulse() - for(var/obj/machinery/power/rad_collector/R in rad_collectors) - if(R.z == z && get_dist(R, src) <= 15) // Better than using orange() every process - R.receive_pulse(energy) - -/obj/singularity/singularity_act() - var/gain = (energy/2) - var/dist = max((current_size - 2),1) - explosion(src.loc,(dist),(dist*2),(dist*4)) - qdel(src) - return(gain) +/obj/singularity + name = "gravitational singularity" + desc = "A gravitational singularity." + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + anchored = 1 + density = 1 + layer = MASSIVE_OBJ_LAYER + light_range = 6 + appearance_flags = 0 + var/current_size = 1 + var/allowed_size = 1 + var/contained = 1 //Are we going to move around? + var/energy = 100 //How strong are we? + var/dissipate = 1 //Do we lose energy over time? + var/dissipate_delay = 10 + var/dissipate_track = 0 + var/dissipate_strength = 1 //How much energy do we lose? + var/move_self = 1 //Do we move on our own? + var/grav_pull = 4 //How many tiles out do we pull? + move_resist = INFINITY //no, you don't get to push the singulo. Not even you OP wizard gateway statues + var/consume_range = 0 //How many tiles out do we eat + var/event_chance = 15 //Prob for event each tick + var/target = null //its target. moves towards the target if it has one + var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing + var/last_warning + var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six + allow_spin = 0 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + +/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) + //CARN: admin-alert for chuckle-fuckery. + admin_investigate_setup() + + src.energy = starting_energy + ..() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + GLOB.singularities += src + for(var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines) + if(singubeacon.active) + target = singubeacon + break + +/obj/singularity/Destroy() + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + GLOB.singularities -= src + target = null + return ..() + +/obj/singularity/Move(atom/newloc, direct) + if(current_size >= STAGE_FIVE || check_turfs_in(direct)) + last_failed_movement = 0//Reset this because we moved + return ..() + else + last_failed_movement = direct + return 0 + + +/obj/singularity/attack_hand(mob/user) + consume(user) + return 1 + +/obj/singularity/attack_alien(mob/user) + consume(user) + +/obj/singularity/attack_animal(mob/user) + consume(user) + +/obj/singularity/attackby(obj/item/W, mob/user, params) + consume(user) + return 1 + +/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! + return 0 + +/obj/singularity/blob_act(obj/structure/blob/B) + return + +/obj/singularity/ex_act(severity) + switch(severity) + if(1) + if(current_size <= STAGE_TWO) + investigate_log("has been destroyed by a heavy explosion.","singulo") + qdel(src) + return + else + energy -= round(((energy+1)/2),1) + if(2) + energy -= round(((energy+1)/3),1) + if(3) + energy -= round(((energy+1)/4),1) + return + + +/obj/singularity/bullet_act(obj/item/projectile/P) + qdel(P) + return 0 //Will there be an impact? Who knows. Will we see it? No. + + +/obj/singularity/Bump(atom/A) + consume(A) + return + + +/obj/singularity/Bumped(atom/A) + consume(A) + return + + +/obj/singularity/process() + if(allowed_size >= STAGE_TWO) + // Start moving even before we reach "true" stage two. + // If we are stage one and are sufficiently energetic to be allowed to 2, + // it might mean we are stuck in a corner somewere. So move around to try to expand. + move() + if(current_size >= STAGE_TWO) + pulse() + if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit + event() + eat() + dissipate() + check_energy() + + return + + +/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one. + return + + +/obj/singularity/proc/admin_investigate_setup() + last_warning = world.time + var/count = locate(/obj/machinery/field/containment) in urange(30, src, 1) + if(!count) + message_admins("A singularity has been created without containment fields active at [x], [y], [z] (JMP)") + investigate_log("was created. [count?"":"No containment fields were active"]","singulo") + +/obj/singularity/proc/dissipate() + if(!dissipate) + return + if(dissipate_track >= dissipate_delay) + src.energy -= dissipate_strength + dissipate_track = 0 + else + dissipate_track++ + + +/obj/singularity/proc/expand(force_size = 0) + var/temp_allowed_size = src.allowed_size + if(force_size) + temp_allowed_size = force_size + if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter) + temp_allowed_size = STAGE_FIVE + switch(temp_allowed_size) + if(STAGE_ONE) + current_size = STAGE_ONE + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + pixel_x = 0 + pixel_y = 0 + grav_pull = 4 + consume_range = 0 + dissipate_delay = 10 + dissipate_track = 0 + dissipate_strength = 1 + if(STAGE_TWO) + if((check_turfs_in(1,1))&&(check_turfs_in(2,1))&&(check_turfs_in(4,1))&&(check_turfs_in(8,1))) + current_size = STAGE_TWO + icon = 'icons/effects/96x96.dmi' + icon_state = "singularity_s3" + pixel_x = -32 + pixel_y = -32 + grav_pull = 6 + consume_range = 1 + dissipate_delay = 5 + dissipate_track = 0 + dissipate_strength = 5 + if(STAGE_THREE) + if((check_turfs_in(1,2))&&(check_turfs_in(2,2))&&(check_turfs_in(4,2))&&(check_turfs_in(8,2))) + current_size = STAGE_THREE + icon = 'icons/effects/160x160.dmi' + icon_state = "singularity_s5" + pixel_x = -64 + pixel_y = -64 + grav_pull = 8 + consume_range = 2 + dissipate_delay = 4 + dissipate_track = 0 + dissipate_strength = 20 + if(STAGE_FOUR) + if((check_turfs_in(1,3))&&(check_turfs_in(2,3))&&(check_turfs_in(4,3))&&(check_turfs_in(8,3))) + current_size = STAGE_FOUR + icon = 'icons/effects/224x224.dmi' + icon_state = "singularity_s7" + pixel_x = -96 + pixel_y = -96 + grav_pull = 10 + consume_range = 3 + dissipate_delay = 10 + dissipate_track = 0 + dissipate_strength = 10 + if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything + current_size = STAGE_FIVE + icon = 'icons/effects/288x288.dmi' + icon_state = "singularity_s9" + pixel_x = -128 + pixel_y = -128 + grav_pull = 10 + consume_range = 4 + dissipate = 0 //It cant go smaller due to e loss + if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard. + current_size = STAGE_SIX + icon = 'icons/effects/352x352.dmi' + icon_state = "singularity_s11" + pixel_x = -160 + pixel_y = -160 + grav_pull = 15 + consume_range = 5 + dissipate = 0 + if(current_size == allowed_size) + investigate_log("grew to size [current_size]","singulo") + return 1 + else if(current_size < (--temp_allowed_size)) + expand(temp_allowed_size) + else + return 0 + + +/obj/singularity/proc/check_energy() + if(energy <= 0) + investigate_log("collapsed.","singulo") + qdel(src) + return 0 + switch(energy)//Some of these numbers might need to be changed up later -Mport + if(1 to 199) + allowed_size = STAGE_ONE + if(200 to 499) + allowed_size = STAGE_TWO + if(500 to 999) + allowed_size = STAGE_THREE + if(1000 to 1999) + allowed_size = STAGE_FOUR + if(2000 to INFINITY) + if(energy >= 3000 && consumedSupermatter) + allowed_size = STAGE_SIX + else + allowed_size = STAGE_FIVE + if(current_size != allowed_size) + expand() + return 1 + + +/obj/singularity/proc/eat() + set background = BACKGROUND_ENABLED + for(var/tile in spiral_range_turfs(grav_pull, src)) + var/turf/T = tile + if(!T || !isturf(loc)) + continue + if(get_dist(T, src) > consume_range) + T.singularity_pull(src, current_size) + else + consume(T) + for(var/thing in T) + if(isturf(loc) && thing != src) + var/atom/movable/X = thing + if(get_dist(X, src) > consume_range) + X.singularity_pull(src, current_size) + else + consume(X) + CHECK_TICK + + +/obj/singularity/proc/consume(atom/A) + var/gain = A.singularity_act(current_size) + src.energy += gain + if(istype(A, /obj/machinery/power/supermatter_shard) && !consumedSupermatter) + desc = "[initial(desc)] It glows fiercely with inner fire." + name = "supermatter-charged [initial(name)]" + consumedSupermatter = 1 + set_light(10) + return + + +/obj/singularity/proc/move(force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(GLOB.alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one + + step(src, movement_dir) + + +/obj/singularity/proc/check_turfs_in(direction = 0, step = 0) + if(!direction) + return 0 + var/steps = 0 + if(!step) + switch(current_size) + if(STAGE_ONE) + steps = 1 + if(STAGE_TWO) + steps = 3//Yes this is right + if(STAGE_THREE) + steps = 3 + if(STAGE_FOUR) + steps = 4 + if(STAGE_FIVE) + steps = 5 + else + steps = step + var/list/turfs = list() + var/turf/T = src.loc + for(var/i = 1 to steps) + T = get_step(T,direction) + if(!isturf(T)) + return 0 + turfs.Add(T) + var/dir2 = 0 + var/dir3 = 0 + switch(direction) + if(NORTH||SOUTH) + dir2 = 4 + dir3 = 8 + if(EAST||WEST) + dir2 = 1 + dir3 = 2 + var/turf/T2 = T + for(var/j = 1 to steps-1) + T2 = get_step(T2,dir2) + if(!isturf(T2)) + return 0 + turfs.Add(T2) + for(var/k = 1 to steps-1) + T = get_step(T,dir3) + if(!isturf(T)) + return 0 + turfs.Add(T) + for(var/turf/T3 in turfs) + if(isnull(T3)) + continue + if(!can_move(T3)) + return 0 + return 1 + + +/obj/singularity/proc/can_move(turf/T) + if(!T) + return 0 + if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T)) + return 0 + else if(locate(/obj/machinery/field/generator) in T) + var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T + if(G && G.active) + return 0 + else if(locate(/obj/machinery/shieldwallgen) in T) + var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T + if(S && S.active) + return 0 + return 1 + + +/obj/singularity/proc/event() + var/numb = pick(1,2,3,4,5,6) + switch(numb) + if(1)//EMP + emp_area() + if(2,3)//tox damage all carbon mobs in area + toxmob() + if(4)//Stun mobs who lack optic scanners + mezzer() + if(5,6) //Sets all nearby mobs on fire + if(current_size < STAGE_SIX) + return 0 + combust_mobs() + else + return 0 + return 1 + + +/obj/singularity/proc/toxmob() + var/toxrange = 10 + var/radiation = 15 + var/radiationmin = 3 + if(energy>200) + radiation += round((energy-150)/10,1) + radiationmin = round((radiation/5),1) + for(var/mob/living/M in view(toxrange, src.loc)) + M.apply_effect(rand(radiationmin,radiation), IRRADIATE) + + +/obj/singularity/proc/combust_mobs() + for(var/mob/living/carbon/C in urange(20, src, 1)) + C.visible_message("[C]'s skin bursts into flame!", \ + "You feel an inner fire as your skin bursts into flames!") + C.adjust_fire_stacks(5) + C.IgniteMob() + return + + +/obj/singularity/proc/mezzer() + for(var/mob/living/carbon/M in oviewers(8, src)) + if(istype(M, /mob/living/carbon/brain)) //Ignore brains + continue + + if(M.stat == CONSCIOUS) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(istype(H.glasses, /obj/item/clothing/glasses/meson)) + var/obj/item/clothing/glasses/meson/MS = H.glasses + if(MS.vision_flags == SEE_TURFS) + to_chat(H, "You look directly into the [src.name], good thing you had your protective eyewear on!") + return + + M.apply_effect(3, STUN) + M.visible_message("[M] stares blankly at the [src.name]!", \ + "You look directly into the [src.name] and feel weak.") + return + + +/obj/singularity/proc/emp_area() + empulse(src, 8, 10) + return + + +/obj/singularity/proc/pulse() + for(var/obj/machinery/power/rad_collector/R in GLOB.rad_collectors) + if(R.z == z && get_dist(R, src) <= 15) // Better than using orange() every process + R.receive_pulse(energy) + +/obj/singularity/singularity_act() + var/gain = (energy/2) + var/dist = max((current_size - 2),1) + explosion(src.loc,(dist),(dist*2),(dist*4)) + qdel(src) + return(gain) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 031ef83a2647..f2e8fa53a8d2 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -1,493 +1,493 @@ -// the SMES -// stores power - -#define SMESMAXCHARGELEVEL 200000 -#define SMESMAXOUTPUT 200000 -#define SMESRATE 0.05 // rate of internal charge to external power - - - -/obj/machinery/power/smes - name = "power storage unit" - desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." - icon_state = "smes" - density = TRUE - use_power = NO_POWER_USE - - var/capacity = 5e6 // maximum charge - var/charge = 0 // actual charge - - var/input_attempt = TRUE // 1 = attempting to charge, 0 = not attempting to charge - var/inputting = TRUE // 1 = actually inputting, 0 = not inputting - var/input_level = 50000 // amount of power the SMES attempts to charge by - var/input_level_max = 200000 // cap on input_level - var/input_available = 0 // amount of charge available from input last tick - - var/output_attempt = TRUE // 1 = attempting to output, 0 = not attempting to output - var/outputting = TRUE // 1 = actually outputting, 0 = not outputting - var/output_level = 50000 // amount of power the SMES attempts to output - var/output_level_max = 200000 // cap on output_level - var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power - - //Holders for powerout event. - var/last_output_attempt = 0 - var/last_input_attempt = 0 - var/last_charge = 0 - - var/name_tag = null - var/obj/machinery/power/terminal/terminal = null - -/obj/machinery/power/smes/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/smes(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/capacitor(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() - - dir_loop: - for(var/d in cardinal) - var/turf/T = get_step(src, d) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(d, 180)) - terminal = term - break dir_loop - - if(!terminal) - stat |= BROKEN - return - terminal.master = src - update_icon() - -/obj/machinery/power/smes/upgraded/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/smes(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() - -/obj/machinery/power/smes/RefreshParts() - var/IO = 0 - var/C = 0 - for(var/obj/item/stock_parts/capacitor/CP in component_parts) - IO += CP.rating - input_level_max = 200000 * IO - output_level_max = 200000 * IO - for(var/obj/item/stock_parts/cell/PC in component_parts) - C += PC.maxcharge - capacity = C / (15000) * 1e6 - -/obj/machinery/power/smes/update_icon() - overlays.Cut() - if(stat & BROKEN) return - - overlays += image('icons/obj/power.dmi', "smes-op[outputting]") - - if(inputting == 2) - overlays += image('icons/obj/power.dmi', "smes-oc2") - else if(inputting == 1) - overlays += image('icons/obj/power.dmi', "smes-oc1") - else - if(input_attempt) - overlays += image('icons/obj/power.dmi', "smes-oc0") - - var/clevel = chargedisplay() - if(clevel>0) - overlays += image('icons/obj/power.dmi', "smes-og[clevel]") - return - -/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) - //opening using screwdriver - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) - update_icon() - return - - //changing direction using wrench - if(default_change_direction_wrench(user, I)) - terminal = null - var/turf/T = get_step(src, dir) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(dir, 180)) - terminal = term - terminal.master = src - to_chat(user, "Terminal found.") - break - if(!terminal) - to_chat(user, "No power source found.") - return - stat &= ~BROKEN - update_icon() - return - - //exchanging parts using the RPE - if(exchange_parts(user, I)) - return - - //building and linking a terminal - if(istype(I, /obj/item/stack/cable_coil)) - var/dir = get_dir(user,src) - if(dir & (dir-1))//we don't want diagonal click - return - - if(terminal) //is there already a terminal ? - to_chat(user, "This SMES already has a power terminal!") - return - - if(!panel_open) //is the panel open ? - to_chat(user, "You must open the maintenance panel first!") - return - - var/turf/T = get_turf(user) - if(T.intact) //is the floor plating removed ? - to_chat(user, "You must first remove the floor plating!") - return - - var/obj/item/stack/cable_coil/C = I - if(C.amount < 10) - to_chat(user, "You need more wires.") - return - - if(user.loc == loc) - to_chat(user, "You must not be on the same tile as the [src].") - return - - //Direction the terminal will face to - var/tempDir = get_dir(user, src) - switch(tempDir) - if(NORTHEAST, SOUTHEAST) - tempDir = EAST - if(NORTHWEST, SOUTHWEST) - tempDir = WEST - var/turf/tempLoc = get_step(src, reverse_direction(tempDir)) - if(istype(tempLoc, /turf/space)) - to_chat(user, "You can't build a terminal on space.") - return - else if(istype(tempLoc)) - if(tempLoc.intact) - to_chat(user, "You must remove the floor plating first.") - return - - to_chat(user, "You start adding cable to the [src].") - playsound(loc, C.usesound, 50, 1) - - if(do_after(user, 50, target = src)) - if(!terminal && panel_open) - T = get_turf(user) - var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one - if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) //animate the electrocution if uncautious and unlucky - do_sparks(5, 1, src) - return - - C.use(10) // make sure the cable gets used up - user.visible_message(\ - "[user.name] adds the cables and connects the power terminal.",\ - "You add the cables and connect the power terminal.") - - make_terminal(user, tempDir, tempLoc) - terminal.connect_to_network() - return - - //disassembling the terminal - if(istype(I, /obj/item/wirecutters) && terminal && panel_open) - var/turf/T = get_turf(terminal) - if(T.intact) //is the floor plating removed ? - to_chat(user, "You must first expose the power terminal!") - return - - to_chat(user, "You begin to dismantle the power terminal...") - playsound(src.loc, I.usesound, 50, 1) - - if(do_after(user, 50 * I.toolspeed, target = src)) - if(terminal && panel_open) - if(prob(50) && electrocute_mob(usr, terminal.powernet, terminal, 1, TRUE)) //animate the electrocution if uncautious and unlucky - do_sparks(5, 1, src) - return - - //give the wires back and delete the terminal - new /obj/item/stack/cable_coil(T,10) - user.visible_message(\ - "[user.name] cuts the cables and dismantles the power terminal.",\ - "You cut the cables and dismantle the power terminal.") - inputting = 0 //stop inputting, since we have don't have a terminal anymore - qdel(terminal) - return - - //crowbarring it ! - if(default_deconstruction_crowbar(user, I)) - return - return ..() - -/obj/machinery/power/smes/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null - return 1 - return 0 - -/obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc) - // create a terminal object at the same position as original turf loc - // wires will attach to this - terminal = new /obj/machinery/power/terminal(tempLoc) - terminal.dir = tempDir - terminal.master = src - -/obj/machinery/power/smes/Destroy() - if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/area/area = get_area(src) - if(area) - message_admins("SMES deleted at ([area.name])") - log_game("SMES deleted at ([area.name])") - investigate_log("deleted at ([area.name])","singulo") - if(terminal) - disconnect_terminal() - return ..() - - return round(5.5*charge/(capacity ? capacity : 5e6)) - -/obj/machinery/power/smes/proc/chargedisplay() - return round(5.5*charge/(capacity ? capacity : 5e6)) - -/obj/machinery/power/smes/process() - if(stat & BROKEN) - return - - //store machine state to see if we need to update the icon overlays - var/last_disp = chargedisplay() - var/last_chrg = inputting - var/last_onln = outputting - - //inputting - if(terminal && input_attempt) - input_available = terminal.surplus() - - if(inputting) - if(input_available > 0) // if there's power available, try to charge - - var/load = min(min((capacity-charge)/SMESRATE, input_level), input_available) // charge at set rate, limited to spare capacity - - charge += load * SMESRATE // increase the charge - - terminal.add_load(load) // add the load to the terminal side network - - else // if not enough capcity - inputting = FALSE // stop inputting - - else - if(input_attempt && input_available > 0) - inputting = TRUE - else - inputting = FALSE - - //outputting - if(output_attempt) - if(outputting) - output_used = min( charge/SMESRATE, output_level) //limit output to that stored - - if (add_avail(output_used)) // add output to powernet if it exists (smes side) - charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) - else - outputting = FALSE - - if(output_used < 0.0001) // either from no charge or set to 0 - outputting = FALSE - investigate_log("lost power and turned off", "singulo") - else if(output_attempt && charge > output_level && output_level > 0) - outputting = TRUE - else - output_used = 0 - else - outputting = FALSE - - // only update icon if state changed - if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) - update_icon() - - - -// called after all power processes are finished -// restores charge level to smes if there was excess this ptick -/obj/machinery/power/smes/proc/restore() - if(stat & BROKEN) - return - - if(!outputting) - output_used = 0 - return - - var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes - - excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick - - excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) - - // now recharge this amount - - var/clev = chargedisplay() - - charge += excess * SMESRATE // restore unused power - powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it - - output_used -= excess - - if(clev != chargedisplay() ) //if needed updates the icons overlay - update_icon() - return - -/obj/machinery/power/smes/attack_ai(mob/user) - add_hiddenprint(user) - ui_interact(user) - -/obj/machinery/power/smes/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/power/smes/attack_hand(mob/user) - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & BROKEN) - return - - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["nameTag"] = name_tag - data["storedCapacity"] = round(100.0*charge/capacity, 0.1) - data["charging"] = inputting - data["chargeMode"] = input_attempt - data["chargeLevel"] = input_level - data["chargeMax"] = input_level_max - data["outputOnline"] = output_attempt - data["outputLevel"] = output_level - data["outputMax"] = output_level_max - data["outputLoad"] = round(output_used) - - if(outputting) - data["outputting"] = 2 // smes is outputting - else if(!outputting && output_attempt) - data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low - else - data["outputting"] = 0 // smes is not outputting - - return data - -/obj/machinery/power/smes/Topic(href, href_list) - if(..()) - return 1 - - if( href_list["cmode"] ) - inputting(!input_attempt) - update_icon() - - else if( href_list["online"] ) - outputting(!output_attempt) - update_icon() - - else if( href_list["input"] ) - switch( href_list["input"] ) - if("min") - input_level = 0 - if("max") - input_level = input_level_max - if("set") - input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num - input_level = max(0, min(input_level_max, input_level)) // clamp to range - - else if( href_list["output"] ) - switch( href_list["output"] ) - if("min") - output_level = 0 - if("max") - output_level = output_level_max - if("set") - output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num - output_level = max(0, min(output_level_max, output_level)) // clamp to range - - investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") - - return 1 - -/obj/machinery/power/smes/proc/ion_act() - if(is_station_level(src.z)) - if(prob(1)) //explosion - for(var/mob/M in viewers(src)) - M.show_message("The [src.name] is making strange noises!", 3, "You hear sizzling electronics.", 2) - sleep(10*pick(4,5,6,7,10,14)) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - explosion(src.loc, -1, 0, 1, 3, 1, 0) - qdel(src) - return - if(prob(15)) //Power drain - do_sparks(3, 1, src) - if(prob(50)) - emp_act(1) - else - emp_act(2) - if(prob(5)) //smoke only - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - -/obj/machinery/power/smes/proc/inputting(var/do_input) - input_attempt = do_input - if(!input_attempt) - inputting = 0 - -/obj/machinery/power/smes/proc/outputting(var/do_output) - output_attempt = do_output - if(!output_attempt) - outputting = 0 - -/obj/machinery/power/smes/emp_act(severity) - inputting(rand(0,1)) - outputting(rand(0,1)) - output_level = rand(0, output_level_max) - input_level = rand(0, input_level_max) - charge -= 1e6/severity - if(charge < 0) - charge = 0 - update_icon() - ..() - -/obj/machinery/power/smes/engineering - charge = 2e6 // Engineering starts with some charge for singulo - -/obj/machinery/power/smes/magical - name = "magical power storage unit" - desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." - capacity = 9000000 - output_level = 250000 - -/obj/machinery/power/smes/magical/process() - capacity = INFINITY - charge = INFINITY - ..() - -#undef SMESRATE +// the SMES +// stores power + +#define SMESMAXCHARGELEVEL 200000 +#define SMESMAXOUTPUT 200000 +#define SMESRATE 0.05 // rate of internal charge to external power + + + +/obj/machinery/power/smes + name = "power storage unit" + desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." + icon_state = "smes" + density = TRUE + use_power = NO_POWER_USE + + var/capacity = 5e6 // maximum charge + var/charge = 0 // actual charge + + var/input_attempt = TRUE // 1 = attempting to charge, 0 = not attempting to charge + var/inputting = TRUE // 1 = actually inputting, 0 = not inputting + var/input_level = 50000 // amount of power the SMES attempts to charge by + var/input_level_max = 200000 // cap on input_level + var/input_available = 0 // amount of charge available from input last tick + + var/output_attempt = TRUE // 1 = attempting to output, 0 = not attempting to output + var/outputting = TRUE // 1 = actually outputting, 0 = not outputting + var/output_level = 50000 // amount of power the SMES attempts to output + var/output_level_max = 200000 // cap on output_level + var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power + + //Holders for powerout event. + var/last_output_attempt = 0 + var/last_input_attempt = 0 + var/last_charge = 0 + + var/name_tag = null + var/obj/machinery/power/terminal/terminal = null + +/obj/machinery/power/smes/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/smes(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/capacitor(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + + dir_loop: + for(var/d in GLOB.cardinal) + var/turf/T = get_step(src, d) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(d, 180)) + terminal = term + break dir_loop + + if(!terminal) + stat |= BROKEN + return + terminal.master = src + update_icon() + +/obj/machinery/power/smes/upgraded/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/smes(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + +/obj/machinery/power/smes/RefreshParts() + var/IO = 0 + var/C = 0 + for(var/obj/item/stock_parts/capacitor/CP in component_parts) + IO += CP.rating + input_level_max = 200000 * IO + output_level_max = 200000 * IO + for(var/obj/item/stock_parts/cell/PC in component_parts) + C += PC.maxcharge + capacity = C / (15000) * 1e6 + +/obj/machinery/power/smes/update_icon() + overlays.Cut() + if(stat & BROKEN) return + + overlays += image('icons/obj/power.dmi', "smes-op[outputting]") + + if(inputting == 2) + overlays += image('icons/obj/power.dmi', "smes-oc2") + else if(inputting == 1) + overlays += image('icons/obj/power.dmi', "smes-oc1") + else + if(input_attempt) + overlays += image('icons/obj/power.dmi', "smes-oc0") + + var/clevel = chargedisplay() + if(clevel>0) + overlays += image('icons/obj/power.dmi', "smes-og[clevel]") + return + +/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) + //opening using screwdriver + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) + update_icon() + return + + //changing direction using wrench + if(default_change_direction_wrench(user, I)) + terminal = null + var/turf/T = get_step(src, dir) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(dir, 180)) + terminal = term + terminal.master = src + to_chat(user, "Terminal found.") + break + if(!terminal) + to_chat(user, "No power source found.") + return + stat &= ~BROKEN + update_icon() + return + + //exchanging parts using the RPE + if(exchange_parts(user, I)) + return + + //building and linking a terminal + if(istype(I, /obj/item/stack/cable_coil)) + var/dir = get_dir(user,src) + if(dir & (dir-1))//we don't want diagonal click + return + + if(terminal) //is there already a terminal ? + to_chat(user, "This SMES already has a power terminal!") + return + + if(!panel_open) //is the panel open ? + to_chat(user, "You must open the maintenance panel first!") + return + + var/turf/T = get_turf(user) + if(T.intact) //is the floor plating removed ? + to_chat(user, "You must first remove the floor plating!") + return + + var/obj/item/stack/cable_coil/C = I + if(C.amount < 10) + to_chat(user, "You need more wires.") + return + + if(user.loc == loc) + to_chat(user, "You must not be on the same tile as the [src].") + return + + //Direction the terminal will face to + var/tempDir = get_dir(user, src) + switch(tempDir) + if(NORTHEAST, SOUTHEAST) + tempDir = EAST + if(NORTHWEST, SOUTHWEST) + tempDir = WEST + var/turf/tempLoc = get_step(src, reverse_direction(tempDir)) + if(istype(tempLoc, /turf/space)) + to_chat(user, "You can't build a terminal on space.") + return + else if(istype(tempLoc)) + if(tempLoc.intact) + to_chat(user, "You must remove the floor plating first.") + return + + to_chat(user, "You start adding cable to the [src].") + playsound(loc, C.usesound, 50, 1) + + if(do_after(user, 50, target = src)) + if(!terminal && panel_open) + T = get_turf(user) + var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one + if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) //animate the electrocution if uncautious and unlucky + do_sparks(5, 1, src) + return + + C.use(10) // make sure the cable gets used up + user.visible_message(\ + "[user.name] adds the cables and connects the power terminal.",\ + "You add the cables and connect the power terminal.") + + make_terminal(user, tempDir, tempLoc) + terminal.connect_to_network() + return + + //disassembling the terminal + if(istype(I, /obj/item/wirecutters) && terminal && panel_open) + var/turf/T = get_turf(terminal) + if(T.intact) //is the floor plating removed ? + to_chat(user, "You must first expose the power terminal!") + return + + to_chat(user, "You begin to dismantle the power terminal...") + playsound(src.loc, I.usesound, 50, 1) + + if(do_after(user, 50 * I.toolspeed, target = src)) + if(terminal && panel_open) + if(prob(50) && electrocute_mob(usr, terminal.powernet, terminal, 1, TRUE)) //animate the electrocution if uncautious and unlucky + do_sparks(5, 1, src) + return + + //give the wires back and delete the terminal + new /obj/item/stack/cable_coil(T,10) + user.visible_message(\ + "[user.name] cuts the cables and dismantles the power terminal.",\ + "You cut the cables and dismantle the power terminal.") + inputting = 0 //stop inputting, since we have don't have a terminal anymore + qdel(terminal) + return + + //crowbarring it ! + if(default_deconstruction_crowbar(user, I)) + return + return ..() + +/obj/machinery/power/smes/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + return 1 + return 0 + +/obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc) + // create a terminal object at the same position as original turf loc + // wires will attach to this + terminal = new /obj/machinery/power/terminal(tempLoc) + terminal.dir = tempDir + terminal.master = src + +/obj/machinery/power/smes/Destroy() + if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/area/area = get_area(src) + if(area) + message_admins("SMES deleted at ([area.name])") + log_game("SMES deleted at ([area.name])") + investigate_log("deleted at ([area.name])","singulo") + if(terminal) + disconnect_terminal() + return ..() + + return round(5.5*charge/(capacity ? capacity : 5e6)) + +/obj/machinery/power/smes/proc/chargedisplay() + return round(5.5*charge/(capacity ? capacity : 5e6)) + +/obj/machinery/power/smes/process() + if(stat & BROKEN) + return + + //store machine state to see if we need to update the icon overlays + var/last_disp = chargedisplay() + var/last_chrg = inputting + var/last_onln = outputting + + //inputting + if(terminal && input_attempt) + input_available = terminal.surplus() + + if(inputting) + if(input_available > 0) // if there's power available, try to charge + + var/load = min(min((capacity-charge)/SMESRATE, input_level), input_available) // charge at set rate, limited to spare capacity + + charge += load * SMESRATE // increase the charge + + terminal.add_load(load) // add the load to the terminal side network + + else // if not enough capcity + inputting = FALSE // stop inputting + + else + if(input_attempt && input_available > 0) + inputting = TRUE + else + inputting = FALSE + + //outputting + if(output_attempt) + if(outputting) + output_used = min( charge/SMESRATE, output_level) //limit output to that stored + + if (add_avail(output_used)) // add output to powernet if it exists (smes side) + charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) + else + outputting = FALSE + + if(output_used < 0.0001) // either from no charge or set to 0 + outputting = FALSE + investigate_log("lost power and turned off", "singulo") + else if(output_attempt && charge > output_level && output_level > 0) + outputting = TRUE + else + output_used = 0 + else + outputting = FALSE + + // only update icon if state changed + if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) + update_icon() + + + +// called after all power processes are finished +// restores charge level to smes if there was excess this ptick +/obj/machinery/power/smes/proc/restore() + if(stat & BROKEN) + return + + if(!outputting) + output_used = 0 + return + + var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes + + excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick + + excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) + + // now recharge this amount + + var/clev = chargedisplay() + + charge += excess * SMESRATE // restore unused power + powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it + + output_used -= excess + + if(clev != chargedisplay() ) //if needed updates the icons overlay + update_icon() + return + +/obj/machinery/power/smes/attack_ai(mob/user) + add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/power/smes/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/power/smes/attack_hand(mob/user) + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(stat & BROKEN) + return + + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["nameTag"] = name_tag + data["storedCapacity"] = round(100.0*charge/capacity, 0.1) + data["charging"] = inputting + data["chargeMode"] = input_attempt + data["chargeLevel"] = input_level + data["chargeMax"] = input_level_max + data["outputOnline"] = output_attempt + data["outputLevel"] = output_level + data["outputMax"] = output_level_max + data["outputLoad"] = round(output_used) + + if(outputting) + data["outputting"] = 2 // smes is outputting + else if(!outputting && output_attempt) + data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low + else + data["outputting"] = 0 // smes is not outputting + + return data + +/obj/machinery/power/smes/Topic(href, href_list) + if(..()) + return 1 + + if( href_list["cmode"] ) + inputting(!input_attempt) + update_icon() + + else if( href_list["online"] ) + outputting(!output_attempt) + update_icon() + + else if( href_list["input"] ) + switch( href_list["input"] ) + if("min") + input_level = 0 + if("max") + input_level = input_level_max + if("set") + input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num + input_level = max(0, min(input_level_max, input_level)) // clamp to range + + else if( href_list["output"] ) + switch( href_list["output"] ) + if("min") + output_level = 0 + if("max") + output_level = output_level_max + if("set") + output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num + output_level = max(0, min(output_level_max, output_level)) // clamp to range + + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") + + return 1 + +/obj/machinery/power/smes/proc/ion_act() + if(is_station_level(src.z)) + if(prob(1)) //explosion + for(var/mob/M in viewers(src)) + M.show_message("The [src.name] is making strange noises!", 3, "You hear sizzling electronics.", 2) + sleep(10*pick(4,5,6,7,10,14)) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + explosion(src.loc, -1, 0, 1, 3, 1, 0) + qdel(src) + return + if(prob(15)) //Power drain + do_sparks(3, 1, src) + if(prob(50)) + emp_act(1) + else + emp_act(2) + if(prob(5)) //smoke only + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + +/obj/machinery/power/smes/proc/inputting(var/do_input) + input_attempt = do_input + if(!input_attempt) + inputting = 0 + +/obj/machinery/power/smes/proc/outputting(var/do_output) + output_attempt = do_output + if(!output_attempt) + outputting = 0 + +/obj/machinery/power/smes/emp_act(severity) + inputting(rand(0,1)) + outputting(rand(0,1)) + output_level = rand(0, output_level_max) + input_level = rand(0, input_level_max) + charge -= 1e6/severity + if(charge < 0) + charge = 0 + update_icon() + ..() + +/obj/machinery/power/smes/engineering + charge = 2e6 // Engineering starts with some charge for singulo + +/obj/machinery/power/smes/magical + name = "magical power storage unit" + desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." + capacity = 9000000 + output_level = 250000 + +/obj/machinery/power/smes/magical/process() + capacity = INFINITY + charge = INFINITY + ..() + +#undef SMESRATE diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index 7d830d693211..945cc286b310 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -1,521 +1,521 @@ -#define SOLAR_MAX_DIST 40 -#define SOLARGENRATE 1500 - -/obj/machinery/power/solar - name = "solar panel" - desc = "A solar panel. Generates electricity when in contact with sunlight." - icon = 'icons/obj/power.dmi' - icon_state = "sp_base" - density = TRUE - use_power = NO_POWER_USE - idle_power_usage = 0 - active_power_usage = 0 - max_integrity = 150 - integrity_failure = 50 - var/id = 0 - var/obscured = 0 - var/sunfrac = 0 - var/adir = SOUTH // actual dir - var/ndir = SOUTH // target dir - var/turn_angle = 0 - var/obj/machinery/power/solar_control/control = null - -/obj/machinery/power/solar/Initialize(mapload, obj/item/solar_assembly/S) - . = ..() - Make(S) - connect_to_network() - -/obj/machinery/power/solar/Destroy() - unset_control() //remove from control computer - return ..() - -//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST -/obj/machinery/power/solar/proc/set_control(var/obj/machinery/power/solar_control/SC) - if(!SC || (get_dist(src, SC) > SOLAR_MAX_DIST)) - return 0 - control = SC - SC.connected_panels |= src - return 1 - -//set the control of the panel to null and removes it from the control list of the previous control computer if needed -/obj/machinery/power/solar/proc/unset_control() - if(control) - control.connected_panels.Remove(src) - control = null - -/obj/machinery/power/solar/proc/Make(var/obj/item/solar_assembly/S) - if(!S) - S = new /obj/item/solar_assembly(src) - S.glass_type = /obj/item/stack/sheet/glass - S.anchored = 1 - S.loc = src - if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass - max_integrity *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to - obj_integrity = max_integrity - update_icon() - - -/obj/machinery/power/solar/crowbar_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - playsound(loc, 'sound/machines/click.ogg', 50, 1) - user.visible_message("[user] begins to take the glass off the solar panel.", "You begin to take the glass off the solar panel...") - if(I.use_tool(src, user, 50, volume = I.tool_volume)) - user.visible_message("[user] takes the glass off the solar panel.", "You take the glass off the solar panel.") - deconstruct(TRUE) - -/obj/machinery/power/solar/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(stat & BROKEN) - playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 60, TRUE) - else - playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) - if(BURN) - playsound(loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/power/solar/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) - stat |= BROKEN - unset_control() - update_icon() - -/obj/machinery/power/solar/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - var/obj/item/solar_assembly/S = locate() in src - if(S) - S.forceMove(loc) - S.give_glass(stat & BROKEN) - else - playsound(src, "shatter", 70, TRUE) - new /obj/item/shard(src.loc) - new /obj/item/shard(src.loc) - qdel(src) - -/obj/machinery/power/solar/update_icon() - ..() - overlays.Cut() - if(stat & BROKEN) - overlays += image('icons/obj/power.dmi', icon_state = "solar_panel-b", layer = FLY_LAYER) - else - overlays += image('icons/obj/power.dmi', icon_state = "solar_panel", layer = FLY_LAYER) - src.dir = angle2dir(adir) - return - -//calculates the fraction of the sunlight that the panel recieves -/obj/machinery/power/solar/proc/update_solar_exposure() - if(obscured) - sunfrac = 0 - return - - //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) - var/p_angle = min(abs(adir - SSsun.angle), 360 - abs(adir - SSsun.angle)) - - if(p_angle > 90) // if facing more than 90deg from sun, zero output - sunfrac = 0 - return - - sunfrac = cos(p_angle) ** 2 - //isn't the power received from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? - -/obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY - if(stat & BROKEN) - return - if(!control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed - return - - if(powernet) - if(powernet == control.powernet)//check if the panel is still connected to the computer - if(obscured) //get no light from the sun, so don't generate power - return - var/sgen = SOLARGENRATE * sunfrac - add_avail(sgen) - control.gen += sgen - else //if we're no longer on the same powernet, remove from control computer - unset_control() - -/obj/machinery/power/solar/proc/broken() - . = (!(stat & BROKEN)) - stat |= BROKEN - unset_control() - update_icon() - -/obj/machinery/power/solar/fake/New(var/turf/loc, var/obj/item/solar_assembly/S) - ..(loc, S, 0) - -/obj/machinery/power/solar/fake/process() - . = PROCESS_KILL - return - -//trace towards sun to see if we're in shadow -/obj/machinery/power/solar/proc/occlusion() - - var/ax = x // start at the solar panel - var/ay = y - var/turf/T = null - var/dx = SSsun.dx - var/dy = SSsun.dy - - for(var/i = 1 to 20) // 20 steps is enough - ax += dx // do step - ay += dy - - T = locate( round(ax,0.5),round(ay,0.5),z) - - if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge - break - - if(T.density) // if we hit a solid turf, panel is obscured - obscured = 1 - return - - obscured = 0 // if hit the edge or stepped 20 times, not obscured - update_solar_exposure() - - -// -// Solar Assembly - For construction of solar arrays. -// - -/obj/item/solar_assembly - name = "solar panel assembly" - desc = "A solar panel assembly kit, allows constructions of a solar panel, or with a tracking circuit board, a solar tracker" - icon = 'icons/obj/power.dmi' - icon_state = "sp_base" - item_state = "electropack" - w_class = WEIGHT_CLASS_BULKY // Pretty big! - anchored = 0 - var/tracker = 0 - var/glass_type = null - -/obj/item/solar_assembly/attack_hand(var/mob/user) - if(!anchored && isturf(loc)) // You can't pick it up - ..() - -// Give back the glass type we were supplied with -/obj/item/solar_assembly/proc/give_glass() - if(glass_type) - var/obj/item/stack/sheet/S = new glass_type(src.loc) - S.amount = 2 - glass_type = null - - -/obj/item/solar_assembly/attackby(var/obj/item/W, var/mob/user, params) - - if(!anchored && isturf(loc)) - if(istype(W, /obj/item/wrench)) - anchored = 1 - user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.") - playsound(src.loc, W.usesound, 50, 1) - return 1 - else - if(istype(W, /obj/item/wrench)) - anchored = 0 - user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.") - playsound(src.loc, W.usesound, 50, 1) - return 1 - - if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) - var/obj/item/stack/sheet/S = W - if(S.use(2)) - glass_type = W.type - playsound(loc, S.usesound, 50, 1) - user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") - if(tracker) - new /obj/machinery/power/tracker(get_turf(src), src) - else - new /obj/machinery/power/solar(get_turf(src), src) - else - to_chat(user, "You need two sheets of glass to put them into a solar panel.") - return - return 1 - - if(!tracker) - if(istype(W, /obj/item/tracker_electronics)) - if(!user.drop_item()) - return - tracker = 1 - qdel(W) - user.visible_message("[user] inserts the electronics into the solar assembly.", "You insert the electronics into the solar assembly.") - return 1 - else if(istype(W, /obj/item/crowbar)) - new /obj/item/tracker_electronics(src.loc) - tracker = 0 - playsound(loc, W.usesound, 50, 1) - user.visible_message("[user] takes out the electronics from the solar assembly.", "You take out the electronics from the solar assembly.") - return 1 - else - return ..() - -// -// Solar Control Computer -// - -/obj/machinery/power/solar_control - name = "solar panel control" - desc = "A controller for solar panel arrays." - icon = 'icons/obj/computer.dmi' - icon_state = "computer" - anchored = 1 - density = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 250 - max_integrity = 200 - integrity_failure = 100 - var/icon_screen = "solar" - var/icon_keyboard = "power_key" - var/id = 0 - var/cdir = 0 - var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) - var/gen = 0 - var/lastgen = 0 - var/track = 0 // 0= off 1=timed 2=auto (tracker) - var/trackrate = 600 // 300-900 seconds - var/nexttime = 0 // time for a panel to rotate of 1? in manual tracking - var/autostart = 0 // Automatically search for connected devices - var/obj/machinery/power/tracker/connected_tracker = null - var/list/connected_panels = list() - -// Used for mapping in solar array which automatically starts itself (telecomms, for example) -/obj/machinery/power/solar_control/autostart - track = 2 // Auto tracking mode - autostart = 1 // Automatically start - -/obj/machinery/power/solar_control/Initialize() - ..() - if(!powernet) - return - connect_to_network() - set_panels(cdir) - if(autostart) - src.search_for_connected() - if(connected_tracker && track == 2) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - -/obj/machinery/power/solar_control/Destroy() - for(var/obj/machinery/power/solar/M in connected_panels) - M.unset_control() - if(connected_tracker) - connected_tracker.unset_control() - return ..() - -/obj/machinery/power/solar_control/disconnect_from_network() - ..() - SSsun.solars.Remove(src) - -/obj/machinery/power/solar_control/connect_to_network() - var/to_return = ..() - if(powernet) //if connected and not already in solar list... - SSsun.solars |= src //... add it - return to_return - -//search for unconnected panels and trackers in the computer powernet and connect them -/obj/machinery/power/solar_control/proc/search_for_connected() - if(powernet) - for(var/obj/machinery/power/M in powernet.nodes) - if(istype(M, /obj/machinery/power/solar)) - var/obj/machinery/power/solar/S = M - if(!S.control) //i.e unconnected - S.set_control(src) - else if(istype(M, /obj/machinery/power/tracker)) - if(!connected_tracker) //if there's already a tracker connected to the computer don't add another - var/obj/machinery/power/tracker/T = M - if(!T.control) //i.e unconnected - T.set_control(src) - -//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly -/obj/machinery/power/solar_control/proc/update() - if(stat & (NOPOWER | BROKEN)) - return - - switch(track) - if(1) - if(trackrate) //we're manual tracking. If we set a rotation speed... - cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) - if(2) // auto-tracking - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - - set_panels(cdir) - updateDialog() - -/obj/machinery/power/solar_control/update_icon() - overlays.Cut() - if(stat & NOPOWER) - overlays += "[icon_keyboard]_off" - return - overlays += icon_keyboard - if(stat & BROKEN) - overlays += "[icon_state]_broken" - else - overlays += icon_screen - if(cdir > -1) - overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) - -/obj/machinery/power/solar_control/attack_ai(mob/user as mob) - src.add_hiddenprint(user) - ui_interact(user) - -/obj/machinery/power/solar_control/attack_ghost(mob/user as mob) - ui_interact(user) - -/obj/machinery/power/solar_control/attack_hand(mob/user) - if(..(user)) - return 1 - - if(stat & BROKEN) - return - - ui_interact(user) - -/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "solar_control.tmpl", name, 490, 420) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/solar_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["generated"] = round(lastgen) - data["angle"] = cdir - data["direction"] = angle2text(cdir) - - data["tracking_state"] = track - data["tracking_rate"] = trackrate - data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") - - data["connected_panels"] = connected_panels.len - data["connected_tracker"] = (connected_tracker ? 1 : 0) - - return data - -/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - playsound(src.loc, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed, target = src)) - if(src.stat & BROKEN) - to_chat(user, "The broken glass falls out.") - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - new /obj/item/shard( src.loc ) - var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) - for(var/obj/C in src) - C.loc = src.loc - A.circuit = M - A.state = 3 - A.icon_state = "3" - A.anchored = 1 - qdel(src) - else - to_chat(user, "You disconnect the monitor.") - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) - for(var/obj/C in src) - C.loc = src.loc - A.circuit = M - A.state = 4 - A.icon_state = "4" - A.anchored = 1 - qdel(src) - else - return ..() - -/obj/machinery/power/solar_control/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(stat & BROKEN) - playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) - else - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/power/solar_control/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) - stat |= BROKEN - update_icon() - -/obj/machinery/power/solar_control/process() - lastgen = gen - gen = 0 - - if(stat & (NOPOWER | BROKEN)) - return - - if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list - if(connected_tracker.powernet != powernet) - connected_tracker.unset_control() - - if(track==1 && trackrate) //manual tracking and set a rotation speed - if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1?... - targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it - nexttime += 36000/abs(trackrate) //reset the counter for the next 1? - -/obj/machinery/power/solar_control/Topic(href, href_list) - if(..()) - return - - if(href_list["rate_control"]) - if(href_list["cdir"]) - src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) - src.targetdir = src.cdir - if(track == 2) //manual update, so losing auto-tracking - track = 0 - spawn(1) - set_panels(cdir) - if(href_list["tdir"]) - src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - - if(href_list["track"]) - track = text2num(href_list["track"]) - if(track == 2) - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - else if(track == 1) //begin manual tracking - src.targetdir = src.cdir - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - set_panels(targetdir) - - if(href_list["search_connected"]) - search_for_connected() - if(connected_tracker && track == 2) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - - return - -//rotates the panel to the passed angle -/obj/machinery/power/solar_control/proc/set_panels(var/cdir) - - for(var/obj/machinery/power/solar/S in connected_panels) - S.adir = cdir //instantly rotates the panel - S.occlusion()//and - S.update_icon() //update it - - update_icon() - - -/obj/machinery/power/solar_control/power_change() - ..() - update_icon() - - -/obj/machinery/power/solar_control/proc/broken() - stat |= BROKEN - update_icon() - -// -// MISC -// - -/obj/item/paper/solar - name = "paper- 'Going green! Setup your own solar array instructions.'" - info = "

    Welcome

    At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or plasma! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

    You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

    Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

    Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

    That's all to it, be safe, be green!

    " +#define SOLAR_MAX_DIST 40 +#define SOLARGENRATE 1500 + +/obj/machinery/power/solar + name = "solar panel" + desc = "A solar panel. Generates electricity when in contact with sunlight." + icon = 'icons/obj/power.dmi' + icon_state = "sp_base" + density = TRUE + use_power = NO_POWER_USE + idle_power_usage = 0 + active_power_usage = 0 + max_integrity = 150 + integrity_failure = 50 + var/id = 0 + var/obscured = 0 + var/sunfrac = 0 + var/adir = SOUTH // actual dir + var/ndir = SOUTH // target dir + var/turn_angle = 0 + var/obj/machinery/power/solar_control/control = null + +/obj/machinery/power/solar/Initialize(mapload, obj/item/solar_assembly/S) + . = ..() + Make(S) + connect_to_network() + +/obj/machinery/power/solar/Destroy() + unset_control() //remove from control computer + return ..() + +//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/solar/proc/set_control(var/obj/machinery/power/solar_control/SC) + if(!SC || (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + SC.connected_panels |= src + return 1 + +//set the control of the panel to null and removes it from the control list of the previous control computer if needed +/obj/machinery/power/solar/proc/unset_control() + if(control) + control.connected_panels.Remove(src) + control = null + +/obj/machinery/power/solar/proc/Make(var/obj/item/solar_assembly/S) + if(!S) + S = new /obj/item/solar_assembly(src) + S.glass_type = /obj/item/stack/sheet/glass + S.anchored = 1 + S.loc = src + if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass + max_integrity *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to + obj_integrity = max_integrity + update_icon() + + +/obj/machinery/power/solar/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + playsound(loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar panel.", "You begin to take the glass off the solar panel...") + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + user.visible_message("[user] takes the glass off the solar panel.", "You take the glass off the solar panel.") + deconstruct(TRUE) + +/obj/machinery/power/solar/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(stat & BROKEN) + playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 60, TRUE) + else + playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) + if(BURN) + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/power/solar/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) + stat |= BROKEN + unset_control() + update_icon() + +/obj/machinery/power/solar/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + var/obj/item/solar_assembly/S = locate() in src + if(S) + S.forceMove(loc) + S.give_glass(stat & BROKEN) + else + playsound(src, "shatter", 70, TRUE) + new /obj/item/shard(src.loc) + new /obj/item/shard(src.loc) + qdel(src) + +/obj/machinery/power/solar/update_icon() + ..() + overlays.Cut() + if(stat & BROKEN) + overlays += image('icons/obj/power.dmi', icon_state = "solar_panel-b", layer = FLY_LAYER) + else + overlays += image('icons/obj/power.dmi', icon_state = "solar_panel", layer = FLY_LAYER) + src.dir = angle2dir(adir) + return + +//calculates the fraction of the sunlight that the panel recieves +/obj/machinery/power/solar/proc/update_solar_exposure() + if(obscured) + sunfrac = 0 + return + + //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) + var/p_angle = min(abs(adir - SSsun.angle), 360 - abs(adir - SSsun.angle)) + + if(p_angle > 90) // if facing more than 90deg from sun, zero output + sunfrac = 0 + return + + sunfrac = cos(p_angle) ** 2 + //isn't the power received from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? + +/obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY + if(stat & BROKEN) + return + if(!control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed + return + + if(powernet) + if(powernet == control.powernet)//check if the panel is still connected to the computer + if(obscured) //get no light from the sun, so don't generate power + return + var/sgen = SOLARGENRATE * sunfrac + add_avail(sgen) + control.gen += sgen + else //if we're no longer on the same powernet, remove from control computer + unset_control() + +/obj/machinery/power/solar/proc/broken() + . = (!(stat & BROKEN)) + stat |= BROKEN + unset_control() + update_icon() + +/obj/machinery/power/solar/fake/New(var/turf/loc, var/obj/item/solar_assembly/S) + ..(loc, S, 0) + +/obj/machinery/power/solar/fake/process() + . = PROCESS_KILL + return + +//trace towards sun to see if we're in shadow +/obj/machinery/power/solar/proc/occlusion() + + var/ax = x // start at the solar panel + var/ay = y + var/turf/T = null + var/dx = SSsun.dx + var/dy = SSsun.dy + + for(var/i = 1 to 20) // 20 steps is enough + ax += dx // do step + ay += dy + + T = locate( round(ax,0.5),round(ay,0.5),z) + + if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge + break + + if(T.density) // if we hit a solid turf, panel is obscured + obscured = 1 + return + + obscured = 0 // if hit the edge or stepped 20 times, not obscured + update_solar_exposure() + + +// +// Solar Assembly - For construction of solar arrays. +// + +/obj/item/solar_assembly + name = "solar panel assembly" + desc = "A solar panel assembly kit, allows constructions of a solar panel, or with a tracking circuit board, a solar tracker" + icon = 'icons/obj/power.dmi' + icon_state = "sp_base" + item_state = "electropack" + w_class = WEIGHT_CLASS_BULKY // Pretty big! + anchored = 0 + var/tracker = 0 + var/glass_type = null + +/obj/item/solar_assembly/attack_hand(var/mob/user) + if(!anchored && isturf(loc)) // You can't pick it up + ..() + +// Give back the glass type we were supplied with +/obj/item/solar_assembly/proc/give_glass() + if(glass_type) + var/obj/item/stack/sheet/S = new glass_type(src.loc) + S.amount = 2 + glass_type = null + + +/obj/item/solar_assembly/attackby(var/obj/item/W, var/mob/user, params) + + if(!anchored && isturf(loc)) + if(istype(W, /obj/item/wrench)) + anchored = 1 + user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.") + playsound(src.loc, W.usesound, 50, 1) + return 1 + else + if(istype(W, /obj/item/wrench)) + anchored = 0 + user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.") + playsound(src.loc, W.usesound, 50, 1) + return 1 + + if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) + var/obj/item/stack/sheet/S = W + if(S.use(2)) + glass_type = W.type + playsound(loc, S.usesound, 50, 1) + user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") + if(tracker) + new /obj/machinery/power/tracker(get_turf(src), src) + else + new /obj/machinery/power/solar(get_turf(src), src) + else + to_chat(user, "You need two sheets of glass to put them into a solar panel.") + return + return 1 + + if(!tracker) + if(istype(W, /obj/item/tracker_electronics)) + if(!user.drop_item()) + return + tracker = 1 + qdel(W) + user.visible_message("[user] inserts the electronics into the solar assembly.", "You insert the electronics into the solar assembly.") + return 1 + else if(istype(W, /obj/item/crowbar)) + new /obj/item/tracker_electronics(src.loc) + tracker = 0 + playsound(loc, W.usesound, 50, 1) + user.visible_message("[user] takes out the electronics from the solar assembly.", "You take out the electronics from the solar assembly.") + return 1 + else + return ..() + +// +// Solar Control Computer +// + +/obj/machinery/power/solar_control + name = "solar panel control" + desc = "A controller for solar panel arrays." + icon = 'icons/obj/computer.dmi' + icon_state = "computer" + anchored = 1 + density = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 250 + max_integrity = 200 + integrity_failure = 100 + var/icon_screen = "solar" + var/icon_keyboard = "power_key" + var/id = 0 + var/cdir = 0 + var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) + var/gen = 0 + var/lastgen = 0 + var/track = 0 // 0= off 1=timed 2=auto (tracker) + var/trackrate = 600 // 300-900 seconds + var/nexttime = 0 // time for a panel to rotate of 1? in manual tracking + var/autostart = 0 // Automatically search for connected devices + var/obj/machinery/power/tracker/connected_tracker = null + var/list/connected_panels = list() + +// Used for mapping in solar array which automatically starts itself (telecomms, for example) +/obj/machinery/power/solar_control/autostart + track = 2 // Auto tracking mode + autostart = 1 // Automatically start + +/obj/machinery/power/solar_control/Initialize() + ..() + if(!powernet) + return + connect_to_network() + set_panels(cdir) + if(autostart) + src.search_for_connected() + if(connected_tracker && track == 2) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + +/obj/machinery/power/solar_control/Destroy() + for(var/obj/machinery/power/solar/M in connected_panels) + M.unset_control() + if(connected_tracker) + connected_tracker.unset_control() + return ..() + +/obj/machinery/power/solar_control/disconnect_from_network() + ..() + SSsun.solars.Remove(src) + +/obj/machinery/power/solar_control/connect_to_network() + var/to_return = ..() + if(powernet) //if connected and not already in solar list... + SSsun.solars |= src //... add it + return to_return + +//search for unconnected panels and trackers in the computer powernet and connect them +/obj/machinery/power/solar_control/proc/search_for_connected() + if(powernet) + for(var/obj/machinery/power/M in powernet.nodes) + if(istype(M, /obj/machinery/power/solar)) + var/obj/machinery/power/solar/S = M + if(!S.control) //i.e unconnected + S.set_control(src) + else if(istype(M, /obj/machinery/power/tracker)) + if(!connected_tracker) //if there's already a tracker connected to the computer don't add another + var/obj/machinery/power/tracker/T = M + if(!T.control) //i.e unconnected + T.set_control(src) + +//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly +/obj/machinery/power/solar_control/proc/update() + if(stat & (NOPOWER | BROKEN)) + return + + switch(track) + if(1) + if(trackrate) //we're manual tracking. If we set a rotation speed... + cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) + if(2) // auto-tracking + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + + set_panels(cdir) + updateDialog() + +/obj/machinery/power/solar_control/update_icon() + overlays.Cut() + if(stat & NOPOWER) + overlays += "[icon_keyboard]_off" + return + overlays += icon_keyboard + if(stat & BROKEN) + overlays += "[icon_state]_broken" + else + overlays += icon_screen + if(cdir > -1) + overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) + +/obj/machinery/power/solar_control/attack_ai(mob/user as mob) + src.add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/power/solar_control/attack_ghost(mob/user as mob) + ui_interact(user) + +/obj/machinery/power/solar_control/attack_hand(mob/user) + if(..(user)) + return 1 + + if(stat & BROKEN) + return + + ui_interact(user) + +/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "solar_control.tmpl", name, 490, 420) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/power/solar_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + data["generated"] = round(lastgen) + data["angle"] = cdir + data["direction"] = angle2text(cdir) + + data["tracking_state"] = track + data["tracking_rate"] = trackrate + data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") + + data["connected_panels"] = connected_panels.len + data["connected_tracker"] = (connected_tracker ? 1 : 0) + + return data + +/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + playsound(src.loc, I.usesound, 50, 1) + if(do_after(user, 20 * I.toolspeed, target = src)) + if(src.stat & BROKEN) + to_chat(user, "The broken glass falls out.") + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + new /obj/item/shard( src.loc ) + var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) + for(var/obj/C in src) + C.loc = src.loc + A.circuit = M + A.state = 3 + A.icon_state = "3" + A.anchored = 1 + qdel(src) + else + to_chat(user, "You disconnect the monitor.") + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) + for(var/obj/C in src) + C.loc = src.loc + A.circuit = M + A.state = 4 + A.icon_state = "4" + A.anchored = 1 + qdel(src) + else + return ..() + +/obj/machinery/power/solar_control/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(stat & BROKEN) + playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) + else + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/power/solar_control/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) + stat |= BROKEN + update_icon() + +/obj/machinery/power/solar_control/process() + lastgen = gen + gen = 0 + + if(stat & (NOPOWER | BROKEN)) + return + + if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list + if(connected_tracker.powernet != powernet) + connected_tracker.unset_control() + + if(track==1 && trackrate) //manual tracking and set a rotation speed + if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1?... + targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it + nexttime += 36000/abs(trackrate) //reset the counter for the next 1? + +/obj/machinery/power/solar_control/Topic(href, href_list) + if(..()) + return + + if(href_list["rate_control"]) + if(href_list["cdir"]) + src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) + src.targetdir = src.cdir + if(track == 2) //manual update, so losing auto-tracking + track = 0 + spawn(1) + set_panels(cdir) + if(href_list["tdir"]) + src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + + if(href_list["track"]) + track = text2num(href_list["track"]) + if(track == 2) + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + else if(track == 1) //begin manual tracking + src.targetdir = src.cdir + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + set_panels(targetdir) + + if(href_list["search_connected"]) + search_for_connected() + if(connected_tracker && track == 2) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + + return + +//rotates the panel to the passed angle +/obj/machinery/power/solar_control/proc/set_panels(var/cdir) + + for(var/obj/machinery/power/solar/S in connected_panels) + S.adir = cdir //instantly rotates the panel + S.occlusion()//and + S.update_icon() //update it + + update_icon() + + +/obj/machinery/power/solar_control/power_change() + ..() + update_icon() + + +/obj/machinery/power/solar_control/proc/broken() + stat |= BROKEN + update_icon() + +// +// MISC +// + +/obj/item/paper/solar + name = "paper- 'Going green! Setup your own solar array instructions.'" + info = "

    Welcome

    At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or plasma! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

    You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

    Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

    Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

    That's all to it, be safe, be green!

    " diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 463121a7bdf2..f8c23a696faa 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -344,7 +344,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/power/supermatter_shard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/power/supermatter_shard/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["integrity_percentage"] = round(get_integrity()) @@ -366,7 +366,7 @@ return data /obj/machinery/power/supermatter_shard/proc/transfer_energy() - for(var/obj/machinery/power/rad_collector/R in rad_collectors) + for(var/obj/machinery/power/rad_collector/R in GLOB.rad_collectors) if(get_dist(R, src) <= 15) // Better than using orange() every process R.receive_pulse(power/10) return diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 058872cde6d5..e66128ba7241 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -1,76 +1,76 @@ -// the underfloor wiring terminal for the APC -// autogenerated when an APC is placed -// all conduit connects go to this object instead of the APC -// using this solves the problem of having the APC in a wall yet also inside an area - -/obj/machinery/power/terminal - name = "terminal" - icon_state = "term" - desc = "It's an underfloor wiring terminal for power equipment." - level = 1 - layer = WIRE_TERMINAL_LAYER //a bit above wires - var/obj/machinery/power/master = null - - -/obj/machinery/power/terminal/Initialize(mapload) - . = ..() - var/turf/T = get_turf(src) - if(level == 1) - hide(T.intact) - -/obj/machinery/power/terminal/Destroy() - if(master) - master.disconnect_terminal() - master = null - return ..() - - -/obj/machinery/power/terminal/hide(i) - if(i) - invisibility = 101 - icon_state = "term-f" - else - invisibility = 0 - icon_state = "term" - -/obj/machinery/power/proc/can_terminal_dismantle() - . = 0 - -/obj/machinery/power/apc/can_terminal_dismantle() - . = 0 - if(opened) - . = 1 - -/obj/machinery/power/smes/can_terminal_dismantle() - . = 0 - if(panel_open) - . = 1 - - -/obj/machinery/power/terminal/proc/dismantle(mob/living/user, obj/item/W) - if(isturf(loc)) - var/turf/T = loc - if(T.intact) - to_chat(user, "You must first expose the power terminal!") - return - - if(!master || master.can_terminal_dismantle()) - user.visible_message("[user.name] dismantles the power terminal from [master].", \ - "You begin to cut the cables...") - - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - if(do_after(user, 50*W.toolspeed, target = src)) - if(!master || master.can_terminal_dismantle()) - if(prob(50) && electrocute_mob(user, powernet, src, 1, TRUE)) - do_sparks(5, TRUE, master) - return - new /obj/item/stack/cable_coil(loc, 10) - to_chat(user, "You cut the cables and dismantle the power terminal.") - qdel(src) - - -/obj/machinery/power/terminal/attackby(obj/item/W, mob/living/user, params) - if(istype(W, /obj/item/wirecutters)) - dismantle(user, W) - else - return ..() \ No newline at end of file +// the underfloor wiring terminal for the APC +// autogenerated when an APC is placed +// all conduit connects go to this object instead of the APC +// using this solves the problem of having the APC in a wall yet also inside an area + +/obj/machinery/power/terminal + name = "terminal" + icon_state = "term" + desc = "It's an underfloor wiring terminal for power equipment." + level = 1 + layer = WIRE_TERMINAL_LAYER //a bit above wires + var/obj/machinery/power/master = null + + +/obj/machinery/power/terminal/Initialize(mapload) + . = ..() + var/turf/T = get_turf(src) + if(level == 1) + hide(T.intact) + +/obj/machinery/power/terminal/Destroy() + if(master) + master.disconnect_terminal() + master = null + return ..() + + +/obj/machinery/power/terminal/hide(i) + if(i) + invisibility = 101 + icon_state = "term-f" + else + invisibility = 0 + icon_state = "term" + +/obj/machinery/power/proc/can_terminal_dismantle() + . = 0 + +/obj/machinery/power/apc/can_terminal_dismantle() + . = 0 + if(opened) + . = 1 + +/obj/machinery/power/smes/can_terminal_dismantle() + . = 0 + if(panel_open) + . = 1 + + +/obj/machinery/power/terminal/proc/dismantle(mob/living/user, obj/item/W) + if(isturf(loc)) + var/turf/T = loc + if(T.intact) + to_chat(user, "You must first expose the power terminal!") + return + + if(!master || master.can_terminal_dismantle()) + user.visible_message("[user.name] dismantles the power terminal from [master].", \ + "You begin to cut the cables...") + + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + if(do_after(user, 50*W.toolspeed, target = src)) + if(!master || master.can_terminal_dismantle()) + if(prob(50) && electrocute_mob(user, powernet, src, 1, TRUE)) + do_sparks(5, TRUE, master) + return + new /obj/item/stack/cable_coil(loc, 10) + to_chat(user, "You cut the cables and dismantle the power terminal.") + qdel(src) + + +/obj/machinery/power/terminal/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/wirecutters)) + dismantle(user, W) + else + return ..() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 7d7910b45d42..60b2a30dc34e 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -67,7 +67,7 @@ //we face the last thing we zapped, so this lets us favor that direction a bit var/first_move = dir for(var/i in 0 to move_amount) - var/move_dir = pick(alldirs + first_move) //give the first move direction a bit of favoring. + var/move_dir = pick(GLOB.alldirs + first_move) //give the first move direction a bit of favoring. if(target && prob(60)) move_dir = get_dir(src,target) var/turf/T = get_step(src, move_dir) @@ -288,4 +288,4 @@ closest_blob.tesla_act(power, explosive) else if(closest_structure) - closest_structure.tesla_act(power, explosive) \ No newline at end of file + closest_structure.tesla_act(power, explosive) diff --git a/code/modules/power/tesla/generator.dm b/code/modules/power/tesla/generator.dm index 53eea847ad93..63bce099fab9 100644 --- a/code/modules/power/tesla/generator.dm +++ b/code/modules/power/tesla/generator.dm @@ -7,4 +7,4 @@ /obj/machinery/the_singularitygen/tesla/tesla_act(power, explosive = FALSE) if(explosive) - energy += power \ No newline at end of file + energy += power diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index 69dc8c4fd037..c8964c7ec343 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -1,100 +1,100 @@ -//Solar tracker - -//Machine that tracks the sun and reports it's direction to the solar controllers -//As long as this is working, solar panels on same powernet will track automatically - -/obj/machinery/power/tracker - name = "solar tracker" - desc = "A solar directional tracker." - icon = 'icons/obj/power.dmi' - icon_state = "tracker" - density = TRUE - use_power = NO_POWER_USE - max_integrity = 250 - integrity_failure = 50 - - var/id = 0 - var/sun_angle = 0 // sun angle as set by sun datum - var/obj/machinery/power/solar_control/control = null - -/obj/machinery/power/tracker/Initialize(mapload, obj/item/solar_assembly/S) - . = ..() - Make(S) - connect_to_network() - -/obj/machinery/power/tracker/Destroy() - unset_control() //remove from control computer - return ..() - -//set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST -/obj/machinery/power/tracker/proc/set_control(obj/machinery/power/solar_control/SC) - if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) - return 0 - control = SC - SC.connected_tracker = src - return 1 - -//set the control of the tracker to null and removes it from the previous control computer if needed -/obj/machinery/power/tracker/proc/unset_control() - if(control) - control.connected_tracker = null - control = null - -/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S) - if(!S) - S = new /obj/item/solar_assembly(src) - S.glass_type = /obj/item/stack/sheet/glass - S.tracker = 1 - S.anchored = TRUE - S.forceMove(src) - update_icon() - -//updates the tracker icon and the facing angle for the control computer -/obj/machinery/power/tracker/proc/set_angle(angle) - sun_angle = angle - - //set icon dir to show sun illumination - setDir(turn(NORTH, -angle - 22.5)) // 22.5 deg bias ensures, e.g. 67.5-112.5 is EAST - - if(powernet && (powernet == control.powernet)) //update if we're still in the same powernet - control.cdir = angle - -/obj/machinery/power/tracker/crowbar_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) - user.visible_message("[user] begins to take the glass off the solar tracker.") - if(I.use_tool(src, user, 50, volume = I.tool_volume)) - user.visible_message("[user] takes the glass off the tracker.") - deconstruct(TRUE) - - -/obj/machinery/power/tracker/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) - stat |= BROKEN - unset_control() - -/obj/machinery/power/solar/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - var/obj/item/solar_assembly/S = locate() in src - if(S) - S.forceMove(loc) - S.give_glass(stat & BROKEN) - else - playsound(src, "shatter", 70, TRUE) - new /obj/item/shard(src.loc) - new /obj/item/shard(src.loc) - qdel(src) - -// Tracker Electronic - -/obj/item/tracker_electronics - - name = "tracker electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "engineering=2;programming=1" +//Solar tracker + +//Machine that tracks the sun and reports it's direction to the solar controllers +//As long as this is working, solar panels on same powernet will track automatically + +/obj/machinery/power/tracker + name = "solar tracker" + desc = "A solar directional tracker." + icon = 'icons/obj/power.dmi' + icon_state = "tracker" + density = TRUE + use_power = NO_POWER_USE + max_integrity = 250 + integrity_failure = 50 + + var/id = 0 + var/sun_angle = 0 // sun angle as set by sun datum + var/obj/machinery/power/solar_control/control = null + +/obj/machinery/power/tracker/Initialize(mapload, obj/item/solar_assembly/S) + . = ..() + Make(S) + connect_to_network() + +/obj/machinery/power/tracker/Destroy() + unset_control() //remove from control computer + return ..() + +//set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/tracker/proc/set_control(obj/machinery/power/solar_control/SC) + if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + SC.connected_tracker = src + return 1 + +//set the control of the tracker to null and removes it from the previous control computer if needed +/obj/machinery/power/tracker/proc/unset_control() + if(control) + control.connected_tracker = null + control = null + +/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S) + if(!S) + S = new /obj/item/solar_assembly(src) + S.glass_type = /obj/item/stack/sheet/glass + S.tracker = 1 + S.anchored = TRUE + S.forceMove(src) + update_icon() + +//updates the tracker icon and the facing angle for the control computer +/obj/machinery/power/tracker/proc/set_angle(angle) + sun_angle = angle + + //set icon dir to show sun illumination + setDir(turn(NORTH, -angle - 22.5)) // 22.5 deg bias ensures, e.g. 67.5-112.5 is EAST + + if(powernet && (powernet == control.powernet)) //update if we're still in the same powernet + control.cdir = angle + +/obj/machinery/power/tracker/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar tracker.") + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + user.visible_message("[user] takes the glass off the tracker.") + deconstruct(TRUE) + + +/obj/machinery/power/tracker/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) + stat |= BROKEN + unset_control() + +/obj/machinery/power/solar/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + var/obj/item/solar_assembly/S = locate() in src + if(S) + S.forceMove(loc) + S.give_glass(stat & BROKEN) + else + playsound(src, "shatter", 70, TRUE) + new /obj/item/shard(src.loc) + new /obj/item/shard(src.loc) + qdel(src) + +// Tracker Electronic + +/obj/item/tracker_electronics + + name = "tracker electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "engineering=2;programming=1" diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 15a9dcbdb841..97499ba6b3ba 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -1,405 +1,405 @@ -// TURBINE v2 AKA rev4407 Engine reborn! - -// How to use it? - Mappers -// -// This is a very good power generating mechanism. All you need is a blast furnace with soaring flames and output. -// Not everything is included yet so the turbine can run out of fuel quiet quickly. The best thing about the turbine is that even -// though something is on fire that passes through it, it won't be on fire as it passes out of it. So the exhaust fumes can still -// containt unreacted fuel - plasma and oxygen that needs to be filtered out and re-routed back. This of course requires smart piping -// For a computer to work with the turbine the compressor requires a comp_id matching with the turbine computer's id. This will be -// subjected to a change in the near future mind you. Right now this method of generating power is a good backup but don't expect it -// become a main power source unless some work is done. Have fun. At 50k RPM it generates 60k power. So more than one turbine is needed! -// -// - Numbers -// -// Example setup S - sparker -// B - Blast doors into space for venting -// *BBB****BBB* C - Compressor -// S CT * T - Turbine -// * ^ * * V * D - Doors with firedoor -// **|***D**|** ^ - Fuel feed (Not vent, but a gas outlet) -// | | V - Suction vent (Like the ones in atmos -// - - -/obj/machinery/power/compressor - name = "compressor" - desc = "The compressor stage of a gas turbine generator." - icon = 'icons/obj/pipes.dmi' - icon_state = "compressor" - anchored = 1 - density = 1 - resistance_flags = FIRE_PROOF - var/obj/machinery/power/turbine/turbine - var/datum/gas_mixture/gas_contained - var/turf/simulated/inturf - var/starter = 0 - var/rpm = 0 - var/rpmtarget = 0 - var/capacity = 1e6 - var/comp_id = 0 - var/efficiency - - -/obj/machinery/power/turbine - name = "gas turbine generator" - desc = "A gas turbine used for backup power generation." - icon = 'icons/obj/pipes.dmi' - icon_state = "turbine" - anchored = 1 - density = 1 - resistance_flags = FIRE_PROOF - var/opened = 0 - var/obj/machinery/power/compressor/compressor - var/turf/simulated/outturf - var/lastgen - var/productivity = 1 - -/obj/machinery/computer/turbine_computer - name = "gas turbine control computer" - desc = "A computer to remotely control a gas turbine" - icon_screen = "turbinecomp" - icon_keyboard = "tech_key" - circuit = /obj/item/circuitboard/turbine_computer - var/obj/machinery/power/compressor/compressor - var/id = 0 - -// the inlet stage of the gas turbine electricity generator - -/obj/machinery/power/compressor/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/power_compressor(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() -// The inlet of the compressor is the direction it faces - - gas_contained = new - inturf = get_step(src, dir) - locate_machinery() - if(!turbine) - stat |= BROKEN - - -#define COMPFRICTION 5e5 -#define COMPSTARTERLOAD 2800 - - -// Crucial to make things work!!!! -// OLD FIX - explanation given down below. -// /obj/machinery/power/compressor/CanPass(atom/movable/mover, turf/target, height=0) -// return !density - -/obj/machinery/power/compressor/locate_machinery() - if(turbine) - return - turbine = locate() in get_step(src, get_dir(inturf, src)) - if(turbine) - turbine.locate_machinery() - -/obj/machinery/power/compressor/RefreshParts() - var/E = 0 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - E += M.rating - efficiency = E / 6 - -/obj/machinery/power/compressor/attackby(obj/item/I, mob/user, params) - if(default_change_direction_wrench(user, I)) - turbine = null - inturf = get_step(src, dir) - locate_machinery() - if(turbine) - to_chat(user, "Turbine connected.") - stat &= ~BROKEN - else - to_chat(user, "Turbine not connected.") - stat |= BROKEN - return - - if(exchange_parts(user, I)) - return - - - return ..() - -/obj/machinery/power/compressor/crowbar_act(mob/user, obj/item/I) - if(default_deconstruction_crowbar(user, I)) - return TRUE - -/obj/machinery/power/compressor/screwdriver_act(mob/user, obj/item/I) - if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) - return TRUE - -/obj/machinery/power/compressor/CanAtmosPass(turf/T) - return !density - -/obj/machinery/power/compressor/process() - if(!turbine) - stat = BROKEN - if(stat & BROKEN || panel_open) - return - if(!starter) - return - overlays.Cut() - - rpm = 0.9* rpm + 0.1 * rpmtarget - var/datum/gas_mixture/environment = inturf.return_air() - - // It's a simplified version taking only 1/10 of the moles from the turf nearby. It should be later changed into a better version - - var/transfer_moles = environment.total_moles()/10 - //var/transfer_moles = rpm/10000*capacity - var/datum/gas_mixture/removed = inturf.remove_air(transfer_moles) - gas_contained.merge(removed) - -// RPM function to include compression friction - be advised that too low/high of a compfriction value can make things screwy - - rpm = max(0, rpm - (rpm*rpm)/(COMPFRICTION*efficiency)) - - - if(starter && !(stat & NOPOWER)) - use_power(2800) - if(rpm<1000) - rpmtarget = 1000 - else - if(rpm<1000) - rpmtarget = 0 - - - if(rpm>50000) - overlays += image('icons/obj/pipes.dmi', "comp-o4", FLY_LAYER) - else if(rpm>10000) - overlays += image('icons/obj/pipes.dmi', "comp-o3", FLY_LAYER) - else if(rpm>2000) - overlays += image('icons/obj/pipes.dmi', "comp-o2", FLY_LAYER) - else if(rpm>500) - overlays += image('icons/obj/pipes.dmi', "comp-o1", FLY_LAYER) - //TODO: DEFERRED - -// These are crucial to working of a turbine - the stats modify the power output. TurbGenQ modifies how much raw energy can you get from -// rpms, TurbGenG modifies the shape of the curve - the lower the value the less straight the curve is. - -#define TURBPRES 9000000 -#define TURBGENQ 100000 -#define TURBGENG 0.5 - -/obj/machinery/power/turbine/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/power_turbine(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stack/cable_coil(src, 5) - RefreshParts() -// The outlet is pointed at the direction of the turbine component - - outturf = get_step(src, dir) - locate_machinery() - if(!compressor) - stat |= BROKEN - -/obj/machinery/power/turbine/RefreshParts() - var/P = 0 - for(var/obj/item/stock_parts/capacitor/C in component_parts) - P += C.rating - productivity = P / 6 - -/obj/machinery/power/turbine/locate_machinery() - if(compressor) - return - compressor = locate() in get_step(src, get_dir(outturf, src)) - if(compressor) - compressor.locate_machinery() - -/obj/machinery/power/turbine/CanAtmosPass(turf/T) - return !density - -/obj/machinery/power/turbine/process() - - if(!compressor) - stat = BROKEN - - if((stat & BROKEN) || panel_open) - return - if(!compressor.starter) - return - overlays.Cut() - - // This is the power generation function. If anything is needed it's good to plot it in EXCEL before modifying - // the TURBGENQ and TURBGENG values - - lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) * TURBGENQ * productivity - - add_avail(lastgen) - - // Weird function but it works. Should be something else... - - var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles())/4 - - newrpm = max(0, newrpm) - - if(!compressor.starter || newrpm > 1000) - compressor.rpmtarget = newrpm - - if(compressor.gas_contained.total_moles()>0) - var/oamount = min(compressor.gas_contained.total_moles(), (compressor.rpm+100)/35000*compressor.capacity) - var/datum/gas_mixture/removed = compressor.gas_contained.remove(oamount) - outturf.assume_air(removed) - -// If it works, put an overlay that it works! - - if(lastgen > 100) - overlays += image('icons/obj/pipes.dmi', "turb-o", FLY_LAYER) - - updateDialog() - -/obj/machinery/power/turbine/attack_hand(mob/user) - - if(..()) - return - - interact(user) - -/obj/machinery/power/turbine/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) - return - - if(default_change_direction_wrench(user, I)) - compressor = null - outturf = get_step(src, dir) - locate_machinery() - if(compressor) - to_chat(user, "Compressor connected.") - stat &= ~BROKEN - else - to_chat(user, "Compressor not connected.") - stat |= BROKEN - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(user, I)) - return - return ..() - -/obj/machinery/power/turbine/interact(mob/user) - - if( !Adjacent(user) || (stat & (NOPOWER|BROKEN)) && (!istype(user, /mob/living/silicon)) ) - user.unset_machine(src) - user << browse(null, "window=turbine") - return - - var/t = "Gas Turbine Generator
    "
    -
    -	t += "Generated power : [round(lastgen)] W

    " - - t += "Turbine: [round(compressor.rpm)] RPM
    " - - t += "Starter: [ compressor.starter ? "Off On" : "Off On"]" - - t += "

    Close" - - t += "
    " - var/datum/browser/popup = new(user, "turbine", name) - popup.set_content(t) - popup.open() - - return - -/obj/machinery/power/turbine/Topic(href, href_list) - if(..()) - return - - if( href_list["close"] ) - usr << browse(null, "window=turbine") - usr.unset_machine(src) - return - - else if( href_list["str"] ) - if(compressor) - compressor.starter = !compressor.starter - - updateDialog() - - - - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// COMPUTER NEEDS A SERIOUS REWRITE. - - - -/obj/machinery/computer/turbine_computer/Initialize() - ..() - spawn(10) - locate_machinery() - -/obj/machinery/computer/turbine_computer/locate_machinery() - compressor = locate(/obj/machinery/power/compressor) in range(5, src) - -/obj/machinery/computer/turbine_computer/attack_hand(var/mob/user as mob) - if(..()) - return - - interact(user) - -/obj/machinery/computer/turbine_computer/interact(mob/user) - - var/dat - if(compressor && compressor.turbine) - dat += "
    Gas turbine remote control system
    " - if(compressor.stat || compressor.turbine.stat) - dat += "[compressor.stat ? "Compressor is inoperable
    " : "Turbine is inoperable"]" - else - dat += {"Turbine status: [ src.compressor.starter ? "Off On" : "Off On"] - \n
    - \nTurbine speed: [src.compressor.rpm]rpm
    - \nPower currently being generated: [src.compressor.turbine.lastgen]W
    - \nInternal gas temperature: [src.compressor.gas_contained.temperature]K
    - \n
    Close - \n
    - \n"} - else - dat += "There is [!compressor ? "no compressor" : " compressor[!compressor.turbine ? " but no turbine" : ""]"].
    " - if(!compressor) - dat += "Search for compressor" - - var/datum/browser/popup = new(user, "turbinecomputer", name) - popup.set_content(dat) - popup.open() - return - -/obj/machinery/computer/turbine_computer/Topic(href, href_list) - if(..()) - return - - else if( href_list["str"] ) - if(compressor && compressor.turbine) - compressor.starter = !compressor.starter - else if( href_list["close"] ) - usr << browse(null, "window=turbinecomputer") - usr.unset_machine(src) - return - else if(href_list["search"]) - locate_machinery() - - src.updateUsrDialog() - return - -/obj/machinery/computer/turbine_computer/process() - src.updateDialog() - return +// TURBINE v2 AKA rev4407 Engine reborn! + +// How to use it? - Mappers +// +// This is a very good power generating mechanism. All you need is a blast furnace with soaring flames and output. +// Not everything is included yet so the turbine can run out of fuel quiet quickly. The best thing about the turbine is that even +// though something is on fire that passes through it, it won't be on fire as it passes out of it. So the exhaust fumes can still +// containt unreacted fuel - plasma and oxygen that needs to be filtered out and re-routed back. This of course requires smart piping +// For a computer to work with the turbine the compressor requires a comp_id matching with the turbine computer's id. This will be +// subjected to a change in the near future mind you. Right now this method of generating power is a good backup but don't expect it +// become a main power source unless some work is done. Have fun. At 50k RPM it generates 60k power. So more than one turbine is needed! +// +// - Numbers +// +// Example setup S - sparker +// B - Blast doors into space for venting +// *BBB****BBB* C - Compressor +// S CT * T - Turbine +// * ^ * * V * D - Doors with firedoor +// **|***D**|** ^ - Fuel feed (Not vent, but a gas outlet) +// | | V - Suction vent (Like the ones in atmos +// + + +/obj/machinery/power/compressor + name = "compressor" + desc = "The compressor stage of a gas turbine generator." + icon = 'icons/obj/pipes.dmi' + icon_state = "compressor" + anchored = 1 + density = 1 + resistance_flags = FIRE_PROOF + var/obj/machinery/power/turbine/turbine + var/datum/gas_mixture/gas_contained + var/turf/simulated/inturf + var/starter = 0 + var/rpm = 0 + var/rpmtarget = 0 + var/capacity = 1e6 + var/comp_id = 0 + var/efficiency + + +/obj/machinery/power/turbine + name = "gas turbine generator" + desc = "A gas turbine used for backup power generation." + icon = 'icons/obj/pipes.dmi' + icon_state = "turbine" + anchored = 1 + density = 1 + resistance_flags = FIRE_PROOF + var/opened = 0 + var/obj/machinery/power/compressor/compressor + var/turf/simulated/outturf + var/lastgen + var/productivity = 1 + +/obj/machinery/computer/turbine_computer + name = "gas turbine control computer" + desc = "A computer to remotely control a gas turbine" + icon_screen = "turbinecomp" + icon_keyboard = "tech_key" + circuit = /obj/item/circuitboard/turbine_computer + var/obj/machinery/power/compressor/compressor + var/id = 0 + +// the inlet stage of the gas turbine electricity generator + +/obj/machinery/power/compressor/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/power_compressor(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() +// The inlet of the compressor is the direction it faces + + gas_contained = new + inturf = get_step(src, dir) + locate_machinery() + if(!turbine) + stat |= BROKEN + + +#define COMPFRICTION 5e5 +#define COMPSTARTERLOAD 2800 + + +// Crucial to make things work!!!! +// OLD FIX - explanation given down below. +// /obj/machinery/power/compressor/CanPass(atom/movable/mover, turf/target, height=0) +// return !density + +/obj/machinery/power/compressor/locate_machinery() + if(turbine) + return + turbine = locate() in get_step(src, get_dir(inturf, src)) + if(turbine) + turbine.locate_machinery() + +/obj/machinery/power/compressor/RefreshParts() + var/E = 0 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + E += M.rating + efficiency = E / 6 + +/obj/machinery/power/compressor/attackby(obj/item/I, mob/user, params) + if(default_change_direction_wrench(user, I)) + turbine = null + inturf = get_step(src, dir) + locate_machinery() + if(turbine) + to_chat(user, "Turbine connected.") + stat &= ~BROKEN + else + to_chat(user, "Turbine not connected.") + stat |= BROKEN + return + + if(exchange_parts(user, I)) + return + + + return ..() + +/obj/machinery/power/compressor/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/power/compressor/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) + return TRUE + +/obj/machinery/power/compressor/CanAtmosPass(turf/T) + return !density + +/obj/machinery/power/compressor/process() + if(!turbine) + stat = BROKEN + if(stat & BROKEN || panel_open) + return + if(!starter) + return + overlays.Cut() + + rpm = 0.9* rpm + 0.1 * rpmtarget + var/datum/gas_mixture/environment = inturf.return_air() + + // It's a simplified version taking only 1/10 of the moles from the turf nearby. It should be later changed into a better version + + var/transfer_moles = environment.total_moles()/10 + //var/transfer_moles = rpm/10000*capacity + var/datum/gas_mixture/removed = inturf.remove_air(transfer_moles) + gas_contained.merge(removed) + +// RPM function to include compression friction - be advised that too low/high of a compfriction value can make things screwy + + rpm = max(0, rpm - (rpm*rpm)/(COMPFRICTION*efficiency)) + + + if(starter && !(stat & NOPOWER)) + use_power(2800) + if(rpm<1000) + rpmtarget = 1000 + else + if(rpm<1000) + rpmtarget = 0 + + + if(rpm>50000) + overlays += image('icons/obj/pipes.dmi', "comp-o4", FLY_LAYER) + else if(rpm>10000) + overlays += image('icons/obj/pipes.dmi', "comp-o3", FLY_LAYER) + else if(rpm>2000) + overlays += image('icons/obj/pipes.dmi', "comp-o2", FLY_LAYER) + else if(rpm>500) + overlays += image('icons/obj/pipes.dmi', "comp-o1", FLY_LAYER) + //TODO: DEFERRED + +// These are crucial to working of a turbine - the stats modify the power output. TurbGenQ modifies how much raw energy can you get from +// rpms, TurbGenG modifies the shape of the curve - the lower the value the less straight the curve is. + +#define TURBPRES 9000000 +#define TURBGENQ 100000 +#define TURBGENG 0.5 + +/obj/machinery/power/turbine/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/power_turbine(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stack/cable_coil(src, 5) + RefreshParts() +// The outlet is pointed at the direction of the turbine component + + outturf = get_step(src, dir) + locate_machinery() + if(!compressor) + stat |= BROKEN + +/obj/machinery/power/turbine/RefreshParts() + var/P = 0 + for(var/obj/item/stock_parts/capacitor/C in component_parts) + P += C.rating + productivity = P / 6 + +/obj/machinery/power/turbine/locate_machinery() + if(compressor) + return + compressor = locate() in get_step(src, get_dir(outturf, src)) + if(compressor) + compressor.locate_machinery() + +/obj/machinery/power/turbine/CanAtmosPass(turf/T) + return !density + +/obj/machinery/power/turbine/process() + + if(!compressor) + stat = BROKEN + + if((stat & BROKEN) || panel_open) + return + if(!compressor.starter) + return + overlays.Cut() + + // This is the power generation function. If anything is needed it's good to plot it in EXCEL before modifying + // the TURBGENQ and TURBGENG values + + lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) * TURBGENQ * productivity + + add_avail(lastgen) + + // Weird function but it works. Should be something else... + + var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles())/4 + + newrpm = max(0, newrpm) + + if(!compressor.starter || newrpm > 1000) + compressor.rpmtarget = newrpm + + if(compressor.gas_contained.total_moles()>0) + var/oamount = min(compressor.gas_contained.total_moles(), (compressor.rpm+100)/35000*compressor.capacity) + var/datum/gas_mixture/removed = compressor.gas_contained.remove(oamount) + outturf.assume_air(removed) + +// If it works, put an overlay that it works! + + if(lastgen > 100) + overlays += image('icons/obj/pipes.dmi', "turb-o", FLY_LAYER) + + updateDialog() + +/obj/machinery/power/turbine/attack_hand(mob/user) + + if(..()) + return + + interact(user) + +/obj/machinery/power/turbine/attackby(obj/item/I, mob/user, params) + if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) + return + + if(default_change_direction_wrench(user, I)) + compressor = null + outturf = get_step(src, dir) + locate_machinery() + if(compressor) + to_chat(user, "Compressor connected.") + stat &= ~BROKEN + else + to_chat(user, "Compressor not connected.") + stat |= BROKEN + return + + if(exchange_parts(user, I)) + return + + if(default_deconstruction_crowbar(user, I)) + return + return ..() + +/obj/machinery/power/turbine/interact(mob/user) + + if( !Adjacent(user) || (stat & (NOPOWER|BROKEN)) && (!istype(user, /mob/living/silicon)) ) + user.unset_machine(src) + user << browse(null, "window=turbine") + return + + var/t = "Gas Turbine Generator
    "
    +
    +	t += "Generated power : [round(lastgen)] W

    " + + t += "Turbine: [round(compressor.rpm)] RPM
    " + + t += "Starter: [ compressor.starter ? "Off On" : "Off On"]" + + t += "

    Close" + + t += "
    " + var/datum/browser/popup = new(user, "turbine", name) + popup.set_content(t) + popup.open() + + return + +/obj/machinery/power/turbine/Topic(href, href_list) + if(..()) + return + + if( href_list["close"] ) + usr << browse(null, "window=turbine") + usr.unset_machine(src) + return + + else if( href_list["str"] ) + if(compressor) + compressor.starter = !compressor.starter + + updateDialog() + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// COMPUTER NEEDS A SERIOUS REWRITE. + + + +/obj/machinery/computer/turbine_computer/Initialize() + ..() + spawn(10) + locate_machinery() + +/obj/machinery/computer/turbine_computer/locate_machinery() + compressor = locate(/obj/machinery/power/compressor) in range(5, src) + +/obj/machinery/computer/turbine_computer/attack_hand(var/mob/user as mob) + if(..()) + return + + interact(user) + +/obj/machinery/computer/turbine_computer/interact(mob/user) + + var/dat + if(compressor && compressor.turbine) + dat += "
    Gas turbine remote control system
    " + if(compressor.stat || compressor.turbine.stat) + dat += "[compressor.stat ? "Compressor is inoperable
    " : "Turbine is inoperable"]" + else + dat += {"Turbine status: [ src.compressor.starter ? "Off On" : "Off On"] + \n
    + \nTurbine speed: [src.compressor.rpm]rpm
    + \nPower currently being generated: [src.compressor.turbine.lastgen]W
    + \nInternal gas temperature: [src.compressor.gas_contained.temperature]K
    + \n
    Close + \n
    + \n"} + else + dat += "There is [!compressor ? "no compressor" : " compressor[!compressor.turbine ? " but no turbine" : ""]"].
    " + if(!compressor) + dat += "Search for compressor" + + var/datum/browser/popup = new(user, "turbinecomputer", name) + popup.set_content(dat) + popup.open() + return + +/obj/machinery/computer/turbine_computer/Topic(href, href_list) + if(..()) + return + + else if( href_list["str"] ) + if(compressor && compressor.turbine) + compressor.starter = !compressor.starter + else if( href_list["close"] ) + usr << browse(null, "window=turbinecomputer") + usr.unset_machine(src) + return + else if(href_list["search"]) + locate_machinery() + + src.updateUsrDialog() + return + +/obj/machinery/computer/turbine_computer/process() + src.updateDialog() + return diff --git a/code/modules/procedural_mapping/mapGeneratorModule.dm b/code/modules/procedural_mapping/mapGeneratorModule.dm index 030c61e972a5..e8db8c8390b2 100644 --- a/code/modules/procedural_mapping/mapGeneratorModule.dm +++ b/code/modules/procedural_mapping/mapGeneratorModule.dm @@ -142,4 +142,4 @@ /datum/mapGeneratorModule/denseLayer clusterCheckFlags = CLUSTER_CHECK_NONE spawnableAtoms = list(/atom = 75) - spawnableTurfs = list(/turf = 75) \ No newline at end of file + spawnableTurfs = list(/turf = 75) diff --git a/code/modules/procedural_mapping/mapGeneratorReadme.dm b/code/modules/procedural_mapping/mapGeneratorReadme.dm index ae656b5b30bc..ce34e3729cb6 100644 --- a/code/modules/procedural_mapping/mapGeneratorReadme.dm +++ b/code/modules/procedural_mapping/mapGeneratorReadme.dm @@ -143,4 +143,4 @@ Variable Breakdown (For Mappers): -*/ \ No newline at end of file +*/ diff --git a/code/modules/procedural_mapping/mapGenerators/asteroid.dm b/code/modules/procedural_mapping/mapGenerators/asteroid.dm index 10fe0fc9332a..90ca219588ec 100644 --- a/code/modules/procedural_mapping/mapGenerators/asteroid.dm +++ b/code/modules/procedural_mapping/mapGenerators/asteroid.dm @@ -42,4 +42,4 @@ /datum/mapGeneratorModule/splatterLayer/asteroidMonsters) /datum/mapGenerator/asteroid/filled - modules = list(/datum/mapGeneratorModule/bottomLayer/asteroidWalls) \ No newline at end of file + modules = list(/datum/mapGeneratorModule/bottomLayer/asteroidWalls) diff --git a/code/modules/procedural_mapping/mapGenerators/lava_river.dm b/code/modules/procedural_mapping/mapGenerators/lava_river.dm index 86f9bd68baf0..19a61aaabe65 100644 --- a/code/modules/procedural_mapping/mapGenerators/lava_river.dm +++ b/code/modules/procedural_mapping/mapGenerators/lava_river.dm @@ -22,4 +22,4 @@ var/datum/mapGenerator/lavaland/L = mother if(!istype(L)) return - spawn_rivers(L.start_z, river_nodes, river_type, min_x = L.min_x, min_y = L.min_y, max_x = L.max_x, max_y = L.max_y) \ No newline at end of file + spawn_rivers(L.start_z, river_nodes, river_type, min_x = L.min_x, min_y = L.min_y, max_x = L.max_x, max_y = L.max_y) diff --git a/code/modules/procedural_mapping/mapGenerators/lavaland.dm b/code/modules/procedural_mapping/mapGenerators/lavaland.dm index c65a0d84d714..a0ba2ae816e6 100644 --- a/code/modules/procedural_mapping/mapGenerators/lavaland.dm +++ b/code/modules/procedural_mapping/mapGenerators/lavaland.dm @@ -26,4 +26,4 @@ modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_mineral/dense) /datum/mapGenerator/lavaland/normal_ores - modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_mineral) \ No newline at end of file + modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_mineral) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index d2df6b4a03b3..8536a0195e38 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -1,222 +1,222 @@ -/obj/item/ammo_casing - name = "bullet casing" - desc = "A bullet casing." - icon = 'icons/obj/ammo.dmi' - icon_state = "s-casing" - flags = CONDUCT - slot_flags = SLOT_BELT - throwforce = 1 - w_class = WEIGHT_CLASS_TINY - var/fire_sound = null //What sound should play when this ammo is fired - var/drop_sound = "casingdrop" //What sound should play when this ammo hits the ground - var/caliber = null //Which kind of guns it can be loaded into - var/projectile_type = null //The bullet type to create when New() is called - var/obj/item/projectile/BB = null //The loaded bullet - var/pellets = 1 //Pellets for spreadshot - var/variance = 0 //Variance for inaccuracy fundamental to the casing - var/delay = 0 //Delay for energy weapons - var/randomspread = 0 //Randomspread for automatics - var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. - var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal - -/obj/item/ammo_casing/New() - ..() - if(projectile_type) - BB = new projectile_type(src) - pixel_x = rand(-10.0, 10) - pixel_y = rand(-10.0, 10) - dir = pick(alldirs) - update_icon() - -/obj/item/ammo_casing/update_icon() - ..() - icon_state = "[initial(icon_state)][BB ? "-live" : ""]" - desc = "[initial(desc)][BB ? "" : " This one is spent."]" - -/obj/item/ammo_casing/proc/newshot(params) //For energy weapons, shotgun shells and wands (!). - if(!BB) - BB = new projectile_type(src, params) - return - -/obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/ammo_box)) - var/obj/item/ammo_box/box = I - if(isturf(loc)) - var/boolets = 0 - for(var/obj/item/ammo_casing/bullet in loc) - if(box.stored_ammo.len >= box.max_ammo) - break - if(bullet.BB) - if(box.give_round(bullet, 0)) - boolets++ - else - continue - if(boolets > 0) - box.update_icon() - to_chat(user, "You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s.") - playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) - else - to_chat(user, "You fail to collect anything!") - else - if(istype(I, /obj/item/screwdriver)) - if(BB) - if(initial(BB.name) == "bullet") - var/tmp_label = "" - var/label_text = sanitize(input(user, "Inscribe some text into \the [initial(BB.name)]","Inscription",tmp_label)) - if(length(label_text) > 20) - to_chat(user, "The inscription can be at most 20 characters long.") - else - if(label_text == "") - to_chat(user, "You scratch the inscription off of [initial(BB)].") - BB.name = initial(BB.name) - else - to_chat(user, "You inscribe \"[label_text]\" into \the [initial(BB.name)].") - BB.name = "[initial(BB.name)] \"[label_text]\"" - else - to_chat(user, "You can only inscribe a metal bullet.")//because inscribing beanbags is silly - - else - to_chat(user, "There is no bullet in the casing to inscribe anything into.") - ..() - -//Boxes of ammo -/obj/item/ammo_box - name = "ammo box (generic)" - desc = "A box of ammo?" - icon_state = "357" - icon = 'icons/obj/ammo.dmi' - flags = CONDUCT - slot_flags = SLOT_BELT - item_state = "syringe_kit" - materials = list(MAT_METAL = 30000) - throwforce = 2 - w_class = WEIGHT_CLASS_TINY - throw_speed = 4 - throw_range = 10 - var/list/stored_ammo = list() - var/ammo_type = /obj/item/ammo_casing - var/max_ammo = 7 - var/multiple_sprites = 0 - var/caliber - var/multiload = 1 - var/list/initial_mats //For calculating refund values. - -/obj/item/ammo_box/New() - ..() - for(var/i in 1 to max_ammo) - stored_ammo += new ammo_type(src) - update_icon() - initial_mats = materials.Copy() - update_mat_value() - -/obj/item/ammo_box/Destroy() - QDEL_LIST(stored_ammo) - stored_ammo = null - return ..() - -/obj/item/ammo_box/proc/get_round(keep = 0) - if(!stored_ammo.len) - return null - else - var/b = stored_ammo[stored_ammo.len] - stored_ammo -= b - if(keep) - stored_ammo.Insert(1,b) - update_mat_value() - return b - -/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) - // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type. - if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type)) - return 0 - - if(stored_ammo.len < max_ammo) - stored_ammo += R - R.loc = src - playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) - update_mat_value() - return 1 - //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo - else if(replace_spent) - for(var/obj/item/ammo_casing/AC in stored_ammo) - if(!AC.BB)//found a spent ammo - stored_ammo -= AC - AC.loc = get_turf(loc) - - stored_ammo += R - R.loc = src - playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) - update_mat_value() - return 1 - - return 0 - -/obj/item/ammo_box/proc/can_load(mob/user) - return 1 - -/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = 0, replace_spent = 0) - var/num_loaded = 0 - if(!can_load(user)) - return - if(istype(A, /obj/item/ammo_box)) - var/obj/item/ammo_box/AM = A - for(var/obj/item/ammo_casing/AC in AM.stored_ammo) - var/did_load = give_round(AC, replace_spent) - if(did_load) - AM.stored_ammo -= AC - num_loaded++ - if(!multiload || !did_load) - break - if(istype(A, /obj/item/ammo_casing)) - var/obj/item/ammo_casing/AC = A - if(give_round(AC, replace_spent)) - user.drop_item() - AC.loc = src - num_loaded++ - if(num_loaded) - if(!silent) - to_chat(user, "You load [num_loaded] shell\s into \the [src]!") - playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) - A.update_icon() - update_icon() - - return num_loaded - -/obj/item/ammo_box/attack_self(mob/user as mob) - var/obj/item/ammo_casing/A = get_round() - if(A) - user.put_in_hands(A) - playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) - to_chat(user, "You remove a round from \the [src]!") - update_icon() - -/obj/item/ammo_box/update_icon() - switch(multiple_sprites) - if(1) - icon_state = "[initial(icon_state)]-[stored_ammo.len]" - if(2) - icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]" - desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!" - -/obj/item/ammo_box/proc/update_mat_value() - var/num_ammo = 0 - for(var/B in stored_ammo) - var/obj/item/ammo_casing/AC = B - if(!AC.BB) //Skip any casing which are empty - continue - num_ammo++ - for(var/M in initial_mats) //In case we have multiple types of materials - var/materials_per = initial_mats[M] / max_ammo - - var/value = max(materials_per * num_ammo, 500) //Enforce a minimum of 500 units even if empty. - materials[M] = value - -//Behavior for magazines -/obj/item/ammo_box/magazine/proc/ammo_count() - return stored_ammo.len - -/obj/item/ammo_box/magazine/proc/empty_magazine() - var/turf_mag = get_turf(src) - for(var/obj/item/ammo in stored_ammo) - ammo.forceMove(turf_mag) - stored_ammo -= ammo +/obj/item/ammo_casing + name = "bullet casing" + desc = "A bullet casing." + icon = 'icons/obj/ammo.dmi' + icon_state = "s-casing" + flags = CONDUCT + slot_flags = SLOT_BELT + throwforce = 1 + w_class = WEIGHT_CLASS_TINY + var/fire_sound = null //What sound should play when this ammo is fired + var/drop_sound = "casingdrop" //What sound should play when this ammo hits the ground + var/caliber = null //Which kind of guns it can be loaded into + var/projectile_type = null //The bullet type to create when New() is called + var/obj/item/projectile/BB = null //The loaded bullet + var/pellets = 1 //Pellets for spreadshot + var/variance = 0 //Variance for inaccuracy fundamental to the casing + var/delay = 0 //Delay for energy weapons + var/randomspread = 0 //Randomspread for automatics + var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. + var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal + +/obj/item/ammo_casing/New() + ..() + if(projectile_type) + BB = new projectile_type(src) + pixel_x = rand(-10.0, 10) + pixel_y = rand(-10.0, 10) + dir = pick(GLOB.alldirs) + update_icon() + +/obj/item/ammo_casing/update_icon() + ..() + icon_state = "[initial(icon_state)][BB ? "-live" : ""]" + desc = "[initial(desc)][BB ? "" : " This one is spent."]" + +/obj/item/ammo_casing/proc/newshot(params) //For energy weapons, shotgun shells and wands (!). + if(!BB) + BB = new projectile_type(src, params) + return + +/obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/ammo_box)) + var/obj/item/ammo_box/box = I + if(isturf(loc)) + var/boolets = 0 + for(var/obj/item/ammo_casing/bullet in loc) + if(box.stored_ammo.len >= box.max_ammo) + break + if(bullet.BB) + if(box.give_round(bullet, 0)) + boolets++ + else + continue + if(boolets > 0) + box.update_icon() + to_chat(user, "You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s.") + playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) + else + to_chat(user, "You fail to collect anything!") + else + if(istype(I, /obj/item/screwdriver)) + if(BB) + if(initial(BB.name) == "bullet") + var/tmp_label = "" + var/label_text = sanitize(input(user, "Inscribe some text into \the [initial(BB.name)]","Inscription",tmp_label)) + if(length(label_text) > 20) + to_chat(user, "The inscription can be at most 20 characters long.") + else + if(label_text == "") + to_chat(user, "You scratch the inscription off of [initial(BB)].") + BB.name = initial(BB.name) + else + to_chat(user, "You inscribe \"[label_text]\" into \the [initial(BB.name)].") + BB.name = "[initial(BB.name)] \"[label_text]\"" + else + to_chat(user, "You can only inscribe a metal bullet.")//because inscribing beanbags is silly + + else + to_chat(user, "There is no bullet in the casing to inscribe anything into.") + ..() + +//Boxes of ammo +/obj/item/ammo_box + name = "ammo box (generic)" + desc = "A box of ammo?" + icon_state = "357" + icon = 'icons/obj/ammo.dmi' + flags = CONDUCT + slot_flags = SLOT_BELT + item_state = "syringe_kit" + materials = list(MAT_METAL = 30000) + throwforce = 2 + w_class = WEIGHT_CLASS_TINY + throw_speed = 4 + throw_range = 10 + var/list/stored_ammo = list() + var/ammo_type = /obj/item/ammo_casing + var/max_ammo = 7 + var/multiple_sprites = 0 + var/caliber + var/multiload = 1 + var/list/initial_mats //For calculating refund values. + +/obj/item/ammo_box/New() + ..() + for(var/i in 1 to max_ammo) + stored_ammo += new ammo_type(src) + update_icon() + initial_mats = materials.Copy() + update_mat_value() + +/obj/item/ammo_box/Destroy() + QDEL_LIST(stored_ammo) + stored_ammo = null + return ..() + +/obj/item/ammo_box/proc/get_round(keep = 0) + if(!stored_ammo.len) + return null + else + var/b = stored_ammo[stored_ammo.len] + stored_ammo -= b + if(keep) + stored_ammo.Insert(1,b) + update_mat_value() + return b + +/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) + // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type. + if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type)) + return 0 + + if(stored_ammo.len < max_ammo) + stored_ammo += R + R.loc = src + playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) + update_mat_value() + return 1 + //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo + else if(replace_spent) + for(var/obj/item/ammo_casing/AC in stored_ammo) + if(!AC.BB)//found a spent ammo + stored_ammo -= AC + AC.loc = get_turf(loc) + + stored_ammo += R + R.loc = src + playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) + update_mat_value() + return 1 + + return 0 + +/obj/item/ammo_box/proc/can_load(mob/user) + return 1 + +/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = 0, replace_spent = 0) + var/num_loaded = 0 + if(!can_load(user)) + return + if(istype(A, /obj/item/ammo_box)) + var/obj/item/ammo_box/AM = A + for(var/obj/item/ammo_casing/AC in AM.stored_ammo) + var/did_load = give_round(AC, replace_spent) + if(did_load) + AM.stored_ammo -= AC + num_loaded++ + if(!multiload || !did_load) + break + if(istype(A, /obj/item/ammo_casing)) + var/obj/item/ammo_casing/AC = A + if(give_round(AC, replace_spent)) + user.drop_item() + AC.loc = src + num_loaded++ + if(num_loaded) + if(!silent) + to_chat(user, "You load [num_loaded] shell\s into \the [src]!") + playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) + A.update_icon() + update_icon() + + return num_loaded + +/obj/item/ammo_box/attack_self(mob/user as mob) + var/obj/item/ammo_casing/A = get_round() + if(A) + user.put_in_hands(A) + playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) + to_chat(user, "You remove a round from \the [src]!") + update_icon() + +/obj/item/ammo_box/update_icon() + switch(multiple_sprites) + if(1) + icon_state = "[initial(icon_state)]-[stored_ammo.len]" + if(2) + icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]" + desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!" + +/obj/item/ammo_box/proc/update_mat_value() + var/num_ammo = 0 + for(var/B in stored_ammo) + var/obj/item/ammo_casing/AC = B + if(!AC.BB) //Skip any casing which are empty + continue + num_ammo++ + for(var/M in initial_mats) //In case we have multiple types of materials + var/materials_per = initial_mats[M] / max_ammo + + var/value = max(materials_per * num_ammo, 500) //Enforce a minimum of 500 units even if empty. + materials[M] = value + +//Behavior for magazines +/obj/item/ammo_box/magazine/proc/ammo_count() + return stored_ammo.len + +/obj/item/ammo_box/magazine/proc/empty_magazine() + var/turf_mag = get_turf(src) + for(var/obj/item/ammo in stored_ammo) + ammo.forceMove(turf_mag) + stored_ammo -= ammo diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm index ae88bc9db613..4484be63c831 100644 --- a/code/modules/projectiles/ammunition/boxes.dm +++ b/code/modules/projectiles/ammunition/boxes.dm @@ -1,148 +1,148 @@ -/obj/item/ammo_box/a357 - name = "speed loader (.357)" - desc = "Designed to quickly reload revolvers." - icon_state = "357" - ammo_type = /obj/item/ammo_casing/a357 - max_ammo = 7 - multiple_sprites = 1 - -/obj/item/ammo_box/c38 - name = "speed loader (.38)" - desc = "Designed to quickly reload revolvers." - icon_state = "38" - ammo_type = /obj/item/ammo_casing/c38 - max_ammo = 6 - multiple_sprites = 1 - -/obj/item/ammo_box/c9mm - name = "ammo box (9mm)" - icon_state = "9mmbox" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/c9mm - max_ammo = 30 - -/obj/item/ammo_box/c10mm - name = "ammo box (10mm)" - icon_state = "10mmbox" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/c10mm - max_ammo = 20 - -/obj/item/ammo_box/c45 - name = "ammo box (.45)" - icon_state = "45box" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/c45 - max_ammo = 20 - -/obj/item/ammo_box/rubber45 - name = "ammo box (.45 rubber)" - icon_state = "45box-r" - ammo_type = /obj/item/ammo_casing/rubber45 - max_ammo = 16 - -/obj/item/ammo_box/a40mm - name = "ammo box (40mm grenades)" - icon_state = "40mm" - ammo_type = /obj/item/ammo_casing/a40mm - max_ammo = 4 - multiple_sprites = 1 - -/obj/item/ammo_box/a762 - name = "stripper clip (7.62mm)" - desc = "A stripper clip." - icon_state = "762" - ammo_type = /obj/item/ammo_casing/a762 - max_ammo = 5 - multiple_sprites = 1 - -/obj/item/ammo_box/n762 - name = "ammo box (7.62x38mmR)" - icon_state = "riflebox" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/n762 - max_ammo = 14 - -/obj/item/ammo_box/shotgun - name = "Shotgun Speedloader (slug)" - icon_state = "slugloader" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/shotgun - max_ammo = 7 - materials = list(MAT_METAL=28000) - multiple_sprites = 1 - -/obj/item/ammo_box/shotgun/buck - name = "Shotgun Speedloader (buckshot)" - icon_state = "buckloader" - ammo_type = /obj/item/ammo_casing/shotgun/buckshot - - -/obj/item/ammo_box/shotgun/dragonsbreath - name = "Shotgun Speedloader (dragonsbreath)" - icon_state = "dragonsbreathloader" - ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath - - -/obj/item/ammo_box/shotgun/stun - name = "Shotgun Speedloader (stun shells)" - icon_state = "stunloader" - ammo_type = /obj/item/ammo_casing/shotgun/stunslug - - -/obj/item/ammo_box/shotgun/beanbag - name = "Shotgun Speedloader (beanbag shells)" - icon_state = "beanbagloader" - ammo_type = /obj/item/ammo_casing/shotgun/beanbag - materials = list(MAT_METAL=1750) - - -/obj/item/ammo_box/shotgun/rubbershot - name = "Shotgun Speedloader (rubbershot shells)" - icon_state = "rubbershotloader" - ammo_type = /obj/item/ammo_casing/shotgun/rubbershot - materials = list(MAT_METAL=1750) - - -/obj/item/ammo_box/shotgun/tranquilizer - name = "Shotgun Speedloader (tranquilizer darts)" - icon_state = "tranqloader" - ammo_type = /obj/item/ammo_casing/shotgun/tranquilizer - materials = list(MAT_METAL=1750) - - -//FOAM DARTS -/obj/item/ammo_box/foambox - name = "ammo box (Foam Darts)" - icon = 'icons/obj/guns/toy.dmi' - icon_state = "foambox" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart - max_ammo = 40 - materials = list(MAT_METAL = 500) - -/obj/item/ammo_box/foambox/riot - icon_state = "foambox_riot" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot - materials = list(MAT_METAL = 50000) - -/obj/item/ammo_box/foambox/sniper - name = "ammo box (Foam Sniper Darts)" - icon = 'icons/obj/guns/toy.dmi' - icon_state = "foambox_sniper" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper - max_ammo = 40 - materials = list(MAT_METAL = 900) - -/obj/item/ammo_box/foambox/sniper/riot - icon_state = "foambox_sniper_riot" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot - materials = list(MAT_METAL = 90000) - - - -/obj/item/ammo_box/caps - name = "speed loader (caps)" - icon_state = "357" - ammo_type = /obj/item/ammo_casing/cap - max_ammo = 7 - multiple_sprites = 1 +/obj/item/ammo_box/a357 + name = "speed loader (.357)" + desc = "Designed to quickly reload revolvers." + icon_state = "357" + ammo_type = /obj/item/ammo_casing/a357 + max_ammo = 7 + multiple_sprites = 1 + +/obj/item/ammo_box/c38 + name = "speed loader (.38)" + desc = "Designed to quickly reload revolvers." + icon_state = "38" + ammo_type = /obj/item/ammo_casing/c38 + max_ammo = 6 + multiple_sprites = 1 + +/obj/item/ammo_box/c9mm + name = "ammo box (9mm)" + icon_state = "9mmbox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/c9mm + max_ammo = 30 + +/obj/item/ammo_box/c10mm + name = "ammo box (10mm)" + icon_state = "10mmbox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/c10mm + max_ammo = 20 + +/obj/item/ammo_box/c45 + name = "ammo box (.45)" + icon_state = "45box" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/c45 + max_ammo = 20 + +/obj/item/ammo_box/rubber45 + name = "ammo box (.45 rubber)" + icon_state = "45box-r" + ammo_type = /obj/item/ammo_casing/rubber45 + max_ammo = 16 + +/obj/item/ammo_box/a40mm + name = "ammo box (40mm grenades)" + icon_state = "40mm" + ammo_type = /obj/item/ammo_casing/a40mm + max_ammo = 4 + multiple_sprites = 1 + +/obj/item/ammo_box/a762 + name = "stripper clip (7.62mm)" + desc = "A stripper clip." + icon_state = "762" + ammo_type = /obj/item/ammo_casing/a762 + max_ammo = 5 + multiple_sprites = 1 + +/obj/item/ammo_box/n762 + name = "ammo box (7.62x38mmR)" + icon_state = "riflebox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/n762 + max_ammo = 14 + +/obj/item/ammo_box/shotgun + name = "Shotgun Speedloader (slug)" + icon_state = "slugloader" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/shotgun + max_ammo = 7 + materials = list(MAT_METAL=28000) + multiple_sprites = 1 + +/obj/item/ammo_box/shotgun/buck + name = "Shotgun Speedloader (buckshot)" + icon_state = "buckloader" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + + +/obj/item/ammo_box/shotgun/dragonsbreath + name = "Shotgun Speedloader (dragonsbreath)" + icon_state = "dragonsbreathloader" + ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath + + +/obj/item/ammo_box/shotgun/stun + name = "Shotgun Speedloader (stun shells)" + icon_state = "stunloader" + ammo_type = /obj/item/ammo_casing/shotgun/stunslug + + +/obj/item/ammo_box/shotgun/beanbag + name = "Shotgun Speedloader (beanbag shells)" + icon_state = "beanbagloader" + ammo_type = /obj/item/ammo_casing/shotgun/beanbag + materials = list(MAT_METAL=1750) + + +/obj/item/ammo_box/shotgun/rubbershot + name = "Shotgun Speedloader (rubbershot shells)" + icon_state = "rubbershotloader" + ammo_type = /obj/item/ammo_casing/shotgun/rubbershot + materials = list(MAT_METAL=1750) + + +/obj/item/ammo_box/shotgun/tranquilizer + name = "Shotgun Speedloader (tranquilizer darts)" + icon_state = "tranqloader" + ammo_type = /obj/item/ammo_casing/shotgun/tranquilizer + materials = list(MAT_METAL=1750) + + +//FOAM DARTS +/obj/item/ammo_box/foambox + name = "ammo box (Foam Darts)" + icon = 'icons/obj/guns/toy.dmi' + icon_state = "foambox" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart + max_ammo = 40 + materials = list(MAT_METAL = 500) + +/obj/item/ammo_box/foambox/riot + icon_state = "foambox_riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + materials = list(MAT_METAL = 50000) + +/obj/item/ammo_box/foambox/sniper + name = "ammo box (Foam Sniper Darts)" + icon = 'icons/obj/guns/toy.dmi' + icon_state = "foambox_sniper" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper + max_ammo = 40 + materials = list(MAT_METAL = 900) + +/obj/item/ammo_box/foambox/sniper/riot + icon_state = "foambox_sniper_riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot + materials = list(MAT_METAL = 90000) + + + +/obj/item/ammo_box/caps + name = "speed loader (caps)" + icon_state = "357" + ammo_type = /obj/item/ammo_casing/cap + max_ammo = 7 + multiple_sprites = 1 diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index d756dda4f41c..5890110b6355 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -506,4 +506,4 @@ icon_state = "c20r45-[round(ammo_count(),2)]" /obj/item/ammo_box/magazine/toy/smgm45/riot - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot \ No newline at end of file + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index d4a333c495dd..411a460ac62c 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1,524 +1,524 @@ -/obj/item/gun - name = "gun" - desc = "It's a gun. It's pretty terrible, though." - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "detective" - item_state = "gun" - flags = CONDUCT - slot_flags = SLOT_BELT - materials = list(MAT_METAL=2000) - w_class = WEIGHT_CLASS_NORMAL - throwforce = 5 - throw_speed = 3 - throw_range = 5 - force = 5 - origin_tech = "combat=1" - needs_permit = 1 - attack_verb = list("struck", "hit", "bashed") - - var/fire_sound = "gunshot" - var/magin_sound = 'sound/weapons/gun_interactions/smg_magin.ogg' - var/magout_sound = 'sound/weapons/gun_interactions/smg_magout.ogg' - var/fire_sound_text = "gunshot" //the fire sound that shows in chat messages: laser blast, gunshot, etc. - var/suppressed = 0 //whether or not a message is displayed when fired - var/can_suppress = 0 - var/can_unsuppress = 1 - var/recoil = 0 //boom boom shake the room - var/clumsy_check = 1 - var/obj/item/ammo_casing/chambered = null - var/trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers - var/sawn_desc = null //description change if weapon is sawn-off - var/sawn_state = SAWN_INTACT - var/burst_size = 1 //how large a burst is - var/fire_delay = 0 //rate of fire for burst firing and semi auto - var/firing_burst = 0 //Prevent the weapon from firing again while already firing - var/semicd = 0 //cooldown handler - var/weapon_weight = WEAPON_LIGHT - var/list/restricted_species - - var/spread = 0 - var/randomspread = 1 - - var/unique_rename = 0 //allows renaming with a pen - var/unique_reskin = 0 //allows one-time reskinning - var/current_skin = null //the skin choice if we had a reskin - var/list/options = list() - - lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/guns_righthand.dmi' - - var/obj/item/flashlight/gun_light = null - var/can_flashlight = 0 - - var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one. - var/obj/item/kitchen/knife/bayonet - var/mutable_appearance/knife_overlay - var/knife_x_offset = 0 - var/knife_y_offset = 0 - - var/list/upgrades = list() - - var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite - var/ammo_y_offset = 0 - var/flight_x_offset = 0 - var/flight_y_offset = 0 - - //Zooming - var/zoomable = FALSE //whether the gun generates a Zoom action on creation - var/zoomed = FALSE //Zoom toggle - var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) - var/datum/action/toggle_scope_zoom/azoom - -/obj/item/gun/New() - ..() - if(gun_light) - verbs += /obj/item/gun/proc/toggle_gunlight - build_zooming() - -/obj/item/gun/Destroy() - QDEL_NULL(bayonet) - return ..() - -/obj/item/gun/handle_atom_del(atom/A) - if(A == bayonet) - clear_bayonet() - return ..() - -/obj/item/gun/examine(mob/user) - . = ..() - if(unique_reskin && !current_skin) - . += "Alt-click it to reskin it." - if(unique_rename) - . += "Use a pen on it to rename it." - if(bayonet) - . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it." - if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent. - . += "[bayonet] looks like it can be unscrewed from [src]." - else if(can_bayonet) - . += "It has a bayonet lug on it." - -/obj/item/gun/proc/process_chamber() - return 0 - -//check if there's enough ammo/energy/whatever to shoot one time -//i.e if clicking would make it shoot -/obj/item/gun/proc/can_shoot() - return 1 - -/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) - to_chat(user, "*click*") - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - -/obj/item/gun/proc/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) - if(recoil) - shake_camera(user, recoil + 1, recoil) - - if(suppressed) - playsound(user, fire_sound, 10, 1) - else - playsound(user, fire_sound, 50, 1) - if(!message) - return - if(pointblank) - user.visible_message("[user] fires [src] point blank at [pbtarget]!", "You fire [src] point blank at [pbtarget]!", "You hear \a [fire_sound_text]!") - else - user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") - -/obj/item/gun/emp_act(severity) - for(var/obj/O in contents) - O.emp_act(severity) - -/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) - if(firing_burst) - return - if(flag) //It's adjacent, is the user, or is on the user's person - if(target in user.contents) //can't shoot stuff inside us. - return - if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack - return - if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected) - return - - if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. - var/mob/living/L = user - if(!can_trigger_gun(L)) - return - - if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can't shoot. - shoot_with_empty_chamber(user) - return - - if(flag) - if(user.zone_selected == "mouth") - handle_suicide(user, target, params) - return - - - //Exclude lasertag guns from the CLUMSY check. - if(clumsy_check) - if(istype(user)) - if((CLUMSY in user.mutations) && prob(40)) - to_chat(user, "You shoot yourself in the foot with \the [src]!") - var/shot_leg = pick("l_foot", "r_foot") - process_fire(user, user, 0, params, zone_override = shot_leg) - user.drop_item() - return - - if(weapon_weight == WEAPON_HEAVY && user.get_inactive_hand()) - to_chat(user, "You need both hands free to fire \the [src]!") - return - - //DUAL WIELDING - var/bonus_spread = 0 - var/loop_counter = 0 - if(ishuman(user) && user.a_intent == INTENT_HARM) - var/mob/living/carbon/human/H = user - for(var/obj/item/gun/G in get_both_hands(H)) - if(G == src || G.weapon_weight >= WEAPON_MEDIUM) - continue - else if(G.can_trigger_gun(user)) - bonus_spread += 24 * G.weapon_weight - loop_counter++ - addtimer(CALLBACK(G, .proc/process_fire, target, user, 1, params, null, bonus_spread), loop_counter) - - process_fire(target,user,1,params, null, bonus_spread) - -/obj/item/gun/proc/can_trigger_gun(mob/living/user) - if(!user.can_use_guns(src)) - return 0 - if(restricted_species && restricted_species.len && !is_type_in_list(user.dna.species, restricted_species)) - to_chat(user, "[src] is incompatible with your biology!") - return 0 - return 1 - -/obj/item/gun/proc/newshot() - return - -/obj/item/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override, bonus_spread = 0) - add_fingerprint(user) - - if(semicd) - return - - var/sprd = 0 - var/randomized_gun_spread = 0 - if(spread) - randomized_gun_spread = rand(0,spread) - var/randomized_bonus_spread = rand(0, bonus_spread) - - if(burst_size > 1) - if(chambered && chambered.harmful) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. - to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") - return - firing_burst = 1 - for(var/i = 1 to burst_size) - if(!user) - break - if(!issilicon(user)) - if( i>1 && !(src in get_both_hands(user))) //for burst firing - break - if(chambered) - if(randomspread) - sprd = round((rand() - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) - else - sprd = round((i / burst_size - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) - if(!chambered.fire(target, user, params, ,suppressed, zone_override, sprd)) - shoot_with_empty_chamber(user) - break - else - if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot - shoot_live_shot(user, 1, target, message) - else - shoot_live_shot(user, 0, target, message) - else - shoot_with_empty_chamber(user) - break - process_chamber() - update_icon() - sleep(fire_delay) - firing_burst = 0 - else - if(chambered) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. - if(chambered.harmful) // Is the bullet chambered harmful? - to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") - return - sprd = round((pick(1,-1)) * (randomized_gun_spread + randomized_bonus_spread)) - if(!chambered.fire(target, user, params, , suppressed, zone_override, sprd)) - shoot_with_empty_chamber(user) - return - else - if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot - shoot_live_shot(user, 1, target, message) - else - shoot_live_shot(user, 0, target, message) - else - shoot_with_empty_chamber(user) - return - process_chamber() - update_icon() - semicd = 1 - spawn(fire_delay) - semicd = 0 - - if(user) - if(user.hand) - user.update_inv_l_hand() - else - user.update_inv_r_hand() - feedback_add_details("gun_fired","[type]") - -/obj/item/gun/attack(mob/M, mob/user) - if(user.a_intent == INTENT_HARM) //Flogging - if(bayonet) - M.attackby(bayonet, user) - else - return ..() - -/obj/item/gun/attack_obj(obj/O, mob/user) - if(user.a_intent == INTENT_HARM) - if(bayonet) - O.attackby(bayonet, user) - return - return ..() - -/obj/item/gun/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/flashlight/seclite)) - var/obj/item/flashlight/seclite/S = I - if(can_flashlight) - if(!gun_light) - if(!user.unEquip(I)) - return - to_chat(user, "You click [S] into place on [src].") - if(S.on) - set_light(0) - gun_light = S - I.loc = src - update_icon() - update_gun_light(user) - var/datum/action/A = new /datum/action/item_action/toggle_gunlight(src) - if(loc == user) - A.Grant(user) - - if(unique_rename) - if(istype(I, /obj/item/pen)) - rename_gun(user) - if(istype(I, /obj/item/kitchen/knife)) - var/obj/item/kitchen/knife/K = I - if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it. - return ..() - if(!user.drop_item()) - return - K.forceMove(src) - to_chat(user, "You attach [K] to [src]'s bayonet lug.") - bayonet = K - var/state = "bayonet" //Generic state. - if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? - state = bayonet.icon_state - var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' - knife_overlay = mutable_appearance(bayonet_icons, state) - knife_overlay.pixel_x = knife_x_offset - knife_overlay.pixel_y = knife_y_offset - overlays += knife_overlay - else - return ..() - -/obj/item/gun/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(gun_light && can_flashlight) - for(var/obj/item/flashlight/seclite/S in src) - to_chat(user, "You unscrew the seclite from [src].") - gun_light = null - S.loc = get_turf(user) - update_gun_light(user) - S.update_brightness(user) - update_icon() - for(var/datum/action/item_action/toggle_gunlight/TGL in actions) - qdel(TGL) - else if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed - bayonet.forceMove(get_turf(user)) - clear_bayonet() - -/obj/item/gun/proc/toggle_gunlight() - set name = "Toggle Gun Light" - set category = "Object" - set desc = "Click to toggle your weapon's attached flashlight." - - if(!gun_light) - return - - var/mob/living/carbon/human/user = usr - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc]!") - gun_light.on = !gun_light.on - to_chat(user, "You toggle the gun light [gun_light.on ? "on":"off"].") - - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - update_gun_light(user) - -/obj/item/gun/proc/update_gun_light(mob/user = null) - if(gun_light) - if(gun_light.on) - set_light(gun_light.brightness_on) - else - set_light(0) - update_icon() - else - set_light(0) - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/gun/proc/clear_bayonet() - if(!bayonet) - return - bayonet = null - if(knife_overlay) - overlays -= knife_overlay - knife_overlay = null - return TRUE - -/obj/item/gun/extinguish_light() - if(gun_light.on) - toggle_gunlight() - visible_message("[src]'s light fades and turns off.") - -/obj/item/gun/pickup(mob/user) - . = ..() - if(azoom) - azoom.Grant(user) - -/obj/item/gun/dropped(mob/user) - ..() - zoom(user,FALSE) - if(azoom) - azoom.Remove(user) - -/obj/item/gun/AltClick(mob/user) - ..() - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(unique_reskin && !current_skin && loc == user) - reskin_gun(user) - -/obj/item/gun/proc/reskin_gun(mob/M) - var/choice = input(M,"Warning, you can only reskin your weapon once!","Reskin Gun") in options - - if(src && choice && !current_skin && !M.incapacitated() && in_range(M,src)) - if(options[choice] == null) - return - current_skin = options[choice] - to_chat(M, "Your gun is now skinned as [choice]. Say hello to your new friend.") - update_icon() - -/obj/item/gun/proc/rename_gun(mob/M) - var/input = stripped_input(M,"What do you want to name the gun?", ,"", MAX_NAME_LEN) - if(src && input && !M.stat && in_range(M,src) && !M.restrained() && M.canmove) - name = input - to_chat(M, "You name the gun [input]. Say hello to your new friend.") - return - -/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params) - if(!ishuman(user) || !ishuman(target)) - return - - if(semicd) - return - - if(user == target) - target.visible_message("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger...", \ - "You stick [src] in your mouth, ready to pull the trigger...") - else - target.visible_message("[user] points [src] at [target]'s head, ready to pull the trigger...", \ - "[user] points [src] at your head, ready to pull the trigger...") - - semicd = 1 - - if(!do_mob(user, target, 120) || user.zone_selected != "mouth") - if(user) - if(user == target) - user.visible_message("[user] decided life was worth living.") - else if(target && target.Adjacent(user)) - target.visible_message("[user] has decided to spare [target]'s life.", "[user] has decided to spare your life!") - semicd = 0 - return - - semicd = 0 - - target.visible_message("[user] pulls the trigger!", "[user] pulls the trigger!") - - if(chambered && chambered.BB) - chambered.BB.damage *= 5 - - process_fire(target, user, 1, params) - -/obj/item/gun/proc/isHandgun() - return 1 - -///////////// -// ZOOMING // -///////////// - -/datum/action/toggle_scope_zoom - name = "Toggle Scope" - check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING - button_icon_state = "sniper_zoom" - var/obj/item/gun/gun = null - -/datum/action/toggle_scope_zoom/Trigger() - gun.zoom(owner) - -/datum/action/toggle_scope_zoom/IsAvailable() - . = ..() - if(!. && gun) - gun.zoom(owner, FALSE) - -/datum/action/toggle_scope_zoom/Remove(mob/living/L) - gun.zoom(L, FALSE) - ..() - -/obj/item/gun/proc/zoom(mob/living/user, forced_zoom) - if(!user || !user.client) - return - - switch(forced_zoom) - if(FALSE) - zoomed = FALSE - if(TRUE) - zoomed = TRUE - else - zoomed = !zoomed - - if(zoomed) - var/_x = 0 - var/_y = 0 - switch(user.dir) - if(NORTH) - _y = zoom_amt - if(EAST) - _x = zoom_amt - if(SOUTH) - _y = -zoom_amt - if(WEST) - _x = -zoom_amt - - user.client.pixel_x = world.icon_size*_x - user.client.pixel_y = world.icon_size*_y - else - user.client.pixel_x = 0 - user.client.pixel_y = 0 - - -//Proc, so that gun accessories/scopes/etc. can easily add zooming. -/obj/item/gun/proc/build_zooming() - if(azoom) - return - - if(zoomable) - azoom = new() - azoom.gun = src +/obj/item/gun + name = "gun" + desc = "It's a gun. It's pretty terrible, though." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "detective" + item_state = "gun" + flags = CONDUCT + slot_flags = SLOT_BELT + materials = list(MAT_METAL=2000) + w_class = WEIGHT_CLASS_NORMAL + throwforce = 5 + throw_speed = 3 + throw_range = 5 + force = 5 + origin_tech = "combat=1" + needs_permit = 1 + attack_verb = list("struck", "hit", "bashed") + + var/fire_sound = "gunshot" + var/magin_sound = 'sound/weapons/gun_interactions/smg_magin.ogg' + var/magout_sound = 'sound/weapons/gun_interactions/smg_magout.ogg' + var/fire_sound_text = "gunshot" //the fire sound that shows in chat messages: laser blast, gunshot, etc. + var/suppressed = 0 //whether or not a message is displayed when fired + var/can_suppress = 0 + var/can_unsuppress = 1 + var/recoil = 0 //boom boom shake the room + var/clumsy_check = 1 + var/obj/item/ammo_casing/chambered = null + var/trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers + var/sawn_desc = null //description change if weapon is sawn-off + var/sawn_state = SAWN_INTACT + var/burst_size = 1 //how large a burst is + var/fire_delay = 0 //rate of fire for burst firing and semi auto + var/firing_burst = 0 //Prevent the weapon from firing again while already firing + var/semicd = 0 //cooldown handler + var/weapon_weight = WEAPON_LIGHT + var/list/restricted_species + + var/spread = 0 + var/randomspread = 1 + + var/unique_rename = 0 //allows renaming with a pen + var/unique_reskin = 0 //allows one-time reskinning + var/current_skin = null //the skin choice if we had a reskin + var/list/options = list() + + lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/guns_righthand.dmi' + + var/obj/item/flashlight/gun_light = null + var/can_flashlight = 0 + + var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one. + var/obj/item/kitchen/knife/bayonet + var/mutable_appearance/knife_overlay + var/knife_x_offset = 0 + var/knife_y_offset = 0 + + var/list/upgrades = list() + + var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite + var/ammo_y_offset = 0 + var/flight_x_offset = 0 + var/flight_y_offset = 0 + + //Zooming + var/zoomable = FALSE //whether the gun generates a Zoom action on creation + var/zoomed = FALSE //Zoom toggle + var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) + var/datum/action/toggle_scope_zoom/azoom + +/obj/item/gun/New() + ..() + if(gun_light) + verbs += /obj/item/gun/proc/toggle_gunlight + build_zooming() + +/obj/item/gun/Destroy() + QDEL_NULL(bayonet) + return ..() + +/obj/item/gun/handle_atom_del(atom/A) + if(A == bayonet) + clear_bayonet() + return ..() + +/obj/item/gun/examine(mob/user) + . = ..() + if(unique_reskin && !current_skin) + . += "Alt-click it to reskin it." + if(unique_rename) + . += "Use a pen on it to rename it." + if(bayonet) + . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it." + if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent. + . += "[bayonet] looks like it can be unscrewed from [src]." + else if(can_bayonet) + . += "It has a bayonet lug on it." + +/obj/item/gun/proc/process_chamber() + return 0 + +//check if there's enough ammo/energy/whatever to shoot one time +//i.e if clicking would make it shoot +/obj/item/gun/proc/can_shoot() + return 1 + +/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) + to_chat(user, "*click*") + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + +/obj/item/gun/proc/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) + if(recoil) + shake_camera(user, recoil + 1, recoil) + + if(suppressed) + playsound(user, fire_sound, 10, 1) + else + playsound(user, fire_sound, 50, 1) + if(!message) + return + if(pointblank) + user.visible_message("[user] fires [src] point blank at [pbtarget]!", "You fire [src] point blank at [pbtarget]!", "You hear \a [fire_sound_text]!") + else + user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") + +/obj/item/gun/emp_act(severity) + for(var/obj/O in contents) + O.emp_act(severity) + +/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) + if(firing_burst) + return + if(flag) //It's adjacent, is the user, or is on the user's person + if(target in user.contents) //can't shoot stuff inside us. + return + if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack + return + if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected) + return + + if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. + var/mob/living/L = user + if(!can_trigger_gun(L)) + return + + if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can't shoot. + shoot_with_empty_chamber(user) + return + + if(flag) + if(user.zone_selected == "mouth") + handle_suicide(user, target, params) + return + + + //Exclude lasertag guns from the CLUMSY check. + if(clumsy_check) + if(istype(user)) + if((CLUMSY in user.mutations) && prob(40)) + to_chat(user, "You shoot yourself in the foot with \the [src]!") + var/shot_leg = pick("l_foot", "r_foot") + process_fire(user, user, 0, params, zone_override = shot_leg) + user.drop_item() + return + + if(weapon_weight == WEAPON_HEAVY && user.get_inactive_hand()) + to_chat(user, "You need both hands free to fire \the [src]!") + return + + //DUAL WIELDING + var/bonus_spread = 0 + var/loop_counter = 0 + if(ishuman(user) && user.a_intent == INTENT_HARM) + var/mob/living/carbon/human/H = user + for(var/obj/item/gun/G in get_both_hands(H)) + if(G == src || G.weapon_weight >= WEAPON_MEDIUM) + continue + else if(G.can_trigger_gun(user)) + bonus_spread += 24 * G.weapon_weight + loop_counter++ + addtimer(CALLBACK(G, .proc/process_fire, target, user, 1, params, null, bonus_spread), loop_counter) + + process_fire(target,user,1,params, null, bonus_spread) + +/obj/item/gun/proc/can_trigger_gun(mob/living/user) + if(!user.can_use_guns(src)) + return 0 + if(restricted_species && restricted_species.len && !is_type_in_list(user.dna.species, restricted_species)) + to_chat(user, "[src] is incompatible with your biology!") + return 0 + return 1 + +/obj/item/gun/proc/newshot() + return + +/obj/item/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override, bonus_spread = 0) + add_fingerprint(user) + + if(semicd) + return + + var/sprd = 0 + var/randomized_gun_spread = 0 + if(spread) + randomized_gun_spread = rand(0,spread) + var/randomized_bonus_spread = rand(0, bonus_spread) + + if(burst_size > 1) + if(chambered && chambered.harmful) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. + to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") + return + firing_burst = 1 + for(var/i = 1 to burst_size) + if(!user) + break + if(!issilicon(user)) + if( i>1 && !(src in get_both_hands(user))) //for burst firing + break + if(chambered) + if(randomspread) + sprd = round((rand() - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) + else + sprd = round((i / burst_size - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) + if(!chambered.fire(target, user, params, ,suppressed, zone_override, sprd)) + shoot_with_empty_chamber(user) + break + else + if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot + shoot_live_shot(user, 1, target, message) + else + shoot_live_shot(user, 0, target, message) + else + shoot_with_empty_chamber(user) + break + process_chamber() + update_icon() + sleep(fire_delay) + firing_burst = 0 + else + if(chambered) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. + if(chambered.harmful) // Is the bullet chambered harmful? + to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") + return + sprd = round((pick(1,-1)) * (randomized_gun_spread + randomized_bonus_spread)) + if(!chambered.fire(target, user, params, , suppressed, zone_override, sprd)) + shoot_with_empty_chamber(user) + return + else + if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot + shoot_live_shot(user, 1, target, message) + else + shoot_live_shot(user, 0, target, message) + else + shoot_with_empty_chamber(user) + return + process_chamber() + update_icon() + semicd = 1 + spawn(fire_delay) + semicd = 0 + + if(user) + if(user.hand) + user.update_inv_l_hand() + else + user.update_inv_r_hand() + feedback_add_details("gun_fired","[type]") + +/obj/item/gun/attack(mob/M, mob/user) + if(user.a_intent == INTENT_HARM) //Flogging + if(bayonet) + M.attackby(bayonet, user) + else + return ..() + +/obj/item/gun/attack_obj(obj/O, mob/user) + if(user.a_intent == INTENT_HARM) + if(bayonet) + O.attackby(bayonet, user) + return + return ..() + +/obj/item/gun/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/flashlight/seclite)) + var/obj/item/flashlight/seclite/S = I + if(can_flashlight) + if(!gun_light) + if(!user.unEquip(I)) + return + to_chat(user, "You click [S] into place on [src].") + if(S.on) + set_light(0) + gun_light = S + I.loc = src + update_icon() + update_gun_light(user) + var/datum/action/A = new /datum/action/item_action/toggle_gunlight(src) + if(loc == user) + A.Grant(user) + + if(unique_rename) + if(istype(I, /obj/item/pen)) + rename_gun(user) + if(istype(I, /obj/item/kitchen/knife)) + var/obj/item/kitchen/knife/K = I + if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it. + return ..() + if(!user.drop_item()) + return + K.forceMove(src) + to_chat(user, "You attach [K] to [src]'s bayonet lug.") + bayonet = K + var/state = "bayonet" //Generic state. + if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? + state = bayonet.icon_state + var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' + knife_overlay = mutable_appearance(bayonet_icons, state) + knife_overlay.pixel_x = knife_x_offset + knife_overlay.pixel_y = knife_y_offset + overlays += knife_overlay + else + return ..() + +/obj/item/gun/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(gun_light && can_flashlight) + for(var/obj/item/flashlight/seclite/S in src) + to_chat(user, "You unscrew the seclite from [src].") + gun_light = null + S.loc = get_turf(user) + update_gun_light(user) + S.update_brightness(user) + update_icon() + for(var/datum/action/item_action/toggle_gunlight/TGL in actions) + qdel(TGL) + else if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed + bayonet.forceMove(get_turf(user)) + clear_bayonet() + +/obj/item/gun/proc/toggle_gunlight() + set name = "Toggle Gun Light" + set category = "Object" + set desc = "Click to toggle your weapon's attached flashlight." + + if(!gun_light) + return + + var/mob/living/carbon/human/user = usr + if(!isturf(user.loc)) + to_chat(user, "You cannot turn the light on while in this [user.loc]!") + gun_light.on = !gun_light.on + to_chat(user, "You toggle the gun light [gun_light.on ? "on":"off"].") + + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + update_gun_light(user) + +/obj/item/gun/proc/update_gun_light(mob/user = null) + if(gun_light) + if(gun_light.on) + set_light(gun_light.brightness_on) + else + set_light(0) + update_icon() + else + set_light(0) + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/gun/proc/clear_bayonet() + if(!bayonet) + return + bayonet = null + if(knife_overlay) + overlays -= knife_overlay + knife_overlay = null + return TRUE + +/obj/item/gun/extinguish_light() + if(gun_light.on) + toggle_gunlight() + visible_message("[src]'s light fades and turns off.") + +/obj/item/gun/pickup(mob/user) + . = ..() + if(azoom) + azoom.Grant(user) + +/obj/item/gun/dropped(mob/user) + ..() + zoom(user,FALSE) + if(azoom) + azoom.Remove(user) + +/obj/item/gun/AltClick(mob/user) + ..() + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(unique_reskin && !current_skin && loc == user) + reskin_gun(user) + +/obj/item/gun/proc/reskin_gun(mob/M) + var/choice = input(M,"Warning, you can only reskin your weapon once!","Reskin Gun") in options + + if(src && choice && !current_skin && !M.incapacitated() && in_range(M,src)) + if(options[choice] == null) + return + current_skin = options[choice] + to_chat(M, "Your gun is now skinned as [choice]. Say hello to your new friend.") + update_icon() + +/obj/item/gun/proc/rename_gun(mob/M) + var/input = stripped_input(M,"What do you want to name the gun?", ,"", MAX_NAME_LEN) + if(src && input && !M.stat && in_range(M,src) && !M.restrained() && M.canmove) + name = input + to_chat(M, "You name the gun [input]. Say hello to your new friend.") + return + +/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params) + if(!ishuman(user) || !ishuman(target)) + return + + if(semicd) + return + + if(user == target) + target.visible_message("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger...", \ + "You stick [src] in your mouth, ready to pull the trigger...") + else + target.visible_message("[user] points [src] at [target]'s head, ready to pull the trigger...", \ + "[user] points [src] at your head, ready to pull the trigger...") + + semicd = 1 + + if(!do_mob(user, target, 120) || user.zone_selected != "mouth") + if(user) + if(user == target) + user.visible_message("[user] decided life was worth living.") + else if(target && target.Adjacent(user)) + target.visible_message("[user] has decided to spare [target]'s life.", "[user] has decided to spare your life!") + semicd = 0 + return + + semicd = 0 + + target.visible_message("[user] pulls the trigger!", "[user] pulls the trigger!") + + if(chambered && chambered.BB) + chambered.BB.damage *= 5 + + process_fire(target, user, 1, params) + +/obj/item/gun/proc/isHandgun() + return 1 + +///////////// +// ZOOMING // +///////////// + +/datum/action/toggle_scope_zoom + name = "Toggle Scope" + check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING + button_icon_state = "sniper_zoom" + var/obj/item/gun/gun = null + +/datum/action/toggle_scope_zoom/Trigger() + gun.zoom(owner) + +/datum/action/toggle_scope_zoom/IsAvailable() + . = ..() + if(!. && gun) + gun.zoom(owner, FALSE) + +/datum/action/toggle_scope_zoom/Remove(mob/living/L) + gun.zoom(L, FALSE) + ..() + +/obj/item/gun/proc/zoom(mob/living/user, forced_zoom) + if(!user || !user.client) + return + + switch(forced_zoom) + if(FALSE) + zoomed = FALSE + if(TRUE) + zoomed = TRUE + else + zoomed = !zoomed + + if(zoomed) + var/_x = 0 + var/_y = 0 + switch(user.dir) + if(NORTH) + _y = zoom_amt + if(EAST) + _x = zoom_amt + if(SOUTH) + _y = -zoom_amt + if(WEST) + _x = -zoom_amt + + user.client.pixel_x = world.icon_size*_x + user.client.pixel_y = world.icon_size*_y + else + user.client.pixel_x = 0 + user.client.pixel_y = 0 + + +//Proc, so that gun accessories/scopes/etc. can easily add zooming. +/obj/item/gun/proc/build_zooming() + if(azoom) + return + + if(zoomable) + azoom = new() + azoom.gun = src diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index a686eda11f06..d17da9a6d993 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -1,220 +1,220 @@ -/obj/item/gun/energy - icon_state = "energy" - name = "energy gun" - desc = "A basic energy-based gun." - icon = 'icons/obj/guns/energy.dmi' - fire_sound_text = "laser blast" - - var/obj/item/stock_parts/cell/cell //What type of power cell this uses - var/cell_type = /obj/item/stock_parts/cell - var/modifystate = 0 - var/list/ammo_type = list(/obj/item/ammo_casing/energy) - var/select = 1 //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. - var/can_charge = 1 - var/charge_sections = 4 - ammo_x_offset = 2 - var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail - var/selfcharge = 0 - var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically - var/charge_tick = 0 - var/charge_delay = 4 - -/obj/item/gun/energy/emp_act(severity) - cell.use(round(cell.charge / severity)) - if(chambered)//phil235 - if(chambered.BB) - qdel(chambered.BB) - chambered.BB = null - chambered = null - newshot() //phil235 - update_icon() - -/obj/item/gun/energy/get_cell() - return cell - -/obj/item/gun/energy/New() - ..() - if(cell_type) - cell = new cell_type(src) - else - cell = new(src) - cell.give(cell.maxcharge) - update_ammo_types() - on_recharge() - if(selfcharge) - START_PROCESSING(SSobj, src) - update_icon() - -/obj/item/gun/energy/proc/update_ammo_types() - var/obj/item/ammo_casing/energy/shot - for(var/i = 1, i <= ammo_type.len, i++) - var/shottype = ammo_type[i] - shot = new shottype(src) - ammo_type[i] = shot - shot = ammo_type[select] - fire_sound = shot.fire_sound - fire_delay = shot.delay - -/obj/item/gun/energy/Destroy() - if(selfcharge) - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/gun/energy/process() - if(selfcharge) //Every [recharge_time] ticks, recharge a shot for the cyborg - charge_tick++ - if(charge_tick < charge_delay) - return - charge_tick = 0 - if(!cell) - return // check if we actually need to recharge - var/obj/item/ammo_casing/energy/E = ammo_type[select] - if(use_external_power) - var/obj/item/stock_parts/cell/external = get_external_cell() - if(!external || !external.use(E.e_cost)) //Take power from the borg... - return //Note, uses /10 because of shitty mods to the cell system - cell.give(100) //... to recharge the shot - on_recharge() - update_icon() - -/obj/item/gun/energy/proc/on_recharge() - newshot() - -/obj/item/gun/energy/attack_self(mob/living/user as mob) - if(ammo_type.len > 1) - select_fire(user) - update_icon() - if(istype(user,/mob/living/carbon/human)) //This has to be here or else if you toggle modes by clicking the gun in hand - var/mob/living/carbon/human/H = user //Otherwise the mob icon doesn't update, blame shitty human update_icons() code - H.update_inv_l_hand() - H.update_inv_r_hand() - -/obj/item/gun/energy/can_shoot() - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - return cell.charge >= shot.e_cost - -/obj/item/gun/energy/newshot() - if(!ammo_type || !cell) - return - if(!chambered) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - if(cell.charge >= shot.e_cost) //if there's enough power in the WEAPON'S cell... - chambered = shot //...prepare a new shot based on the current ammo type selected - if(!chambered.BB) - chambered.newshot() - -/obj/item/gun/energy/process_chamber() - if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... - var/obj/item/ammo_casing/energy/shot = chambered - cell.use(shot.e_cost)//... drain the cell cell - robocharge() - chambered = null //either way, released the prepared shot - newshot() - -/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = 1, params, zone_override, bonus_spread = 0) - if(!chambered && can_shoot()) - process_chamber() - return ..() - -/obj/item/gun/energy/proc/select_fire(mob/living/user) - select++ - if(select > ammo_type.len) - select = 1 - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - fire_sound = shot.fire_sound - fire_delay = shot.delay - if(shot.select_name) - to_chat(user, "[src] is now set to [shot.select_name].") - if(chambered)//phil235 - if(chambered.BB) - qdel(chambered.BB) - chambered.BB = null - chambered = null - newshot() - update_icon() - return - -/obj/item/gun/energy/update_icon() - overlays.Cut() - var/ratio = Ceiling((cell.charge / cell.maxcharge) * charge_sections) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - var/iconState = "[icon_state]_charge" - var/itemState = null - if(!initial(item_state)) - itemState = icon_state - if(modifystate) - overlays += "[icon_state]_[shot.select_name]" - iconState += "_[shot.select_name]" - if(itemState) - itemState += "[shot.select_name]" - if(cell.charge < shot.e_cost) - overlays += "[icon_state]_empty" - else - if(!shaded_charge) - for(var/i = ratio, i >= 1, i--) - overlays += image(icon = icon, icon_state = iconState, pixel_x = ammo_x_offset * (i -1)) - else - overlays += image(icon = icon, icon_state = "[icon_state]_[modifystate ? "[shot.select_name]_" : ""]charge[ratio]") - if(gun_light && can_flashlight) - var/iconF = "flight" - if(gun_light.on) - iconF = "flight_on" - overlays += image(icon = icon, icon_state = iconF, pixel_x = flight_x_offset, pixel_y = flight_y_offset) - if(bayonet && can_bayonet) - overlays += knife_overlay - if(itemState) - itemState += "[ratio]" - item_state = itemState - -/obj/item/gun/energy/ui_action_click() - toggle_gunlight() - -/obj/item/gun/energy/suicide_act(mob/user) - if(can_shoot()) - user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") - sleep(25) - if(user.l_hand == src || user.r_hand == src) - user.visible_message("[user] melts [user.p_their()] face off with the [name]!") - playsound(loc, fire_sound, 50, 1, -1) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - cell.use(shot.e_cost) - update_icon() - return FIRELOSS - else - user.visible_message("[user] panics and starts choking to death!") - return OXYLOSS - else - user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) - return OXYLOSS - -/obj/item/gun/energy/vv_edit_var(var_name, var_value) - switch(var_name) - if("selfcharge") - if(var_value) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/gun/energy/proc/robocharge() - if(cell.charge == cell.maxcharge) - // No point in recharging a weapon's cell that is already at 100%. That would just waste borg cell power for no reason. - return - if(isrobot(loc)) - var/mob/living/silicon/robot/R = loc - if(R && R.cell) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot - if(R.cell.use(shot.e_cost)) //Take power from the borg... - cell.give(shot.e_cost) //... to recharge the shot - -/obj/item/gun/energy/proc/get_external_cell() - if(istype(loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = loc - if(module.holder && module.holder.wearer) - var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/rig/suit = H.back - if(istype(suit)) - return suit.cell - return null +/obj/item/gun/energy + icon_state = "energy" + name = "energy gun" + desc = "A basic energy-based gun." + icon = 'icons/obj/guns/energy.dmi' + fire_sound_text = "laser blast" + + var/obj/item/stock_parts/cell/cell //What type of power cell this uses + var/cell_type = /obj/item/stock_parts/cell + var/modifystate = 0 + var/list/ammo_type = list(/obj/item/ammo_casing/energy) + var/select = 1 //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. + var/can_charge = 1 + var/charge_sections = 4 + ammo_x_offset = 2 + var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail + var/selfcharge = 0 + var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically + var/charge_tick = 0 + var/charge_delay = 4 + +/obj/item/gun/energy/emp_act(severity) + cell.use(round(cell.charge / severity)) + if(chambered)//phil235 + if(chambered.BB) + qdel(chambered.BB) + chambered.BB = null + chambered = null + newshot() //phil235 + update_icon() + +/obj/item/gun/energy/get_cell() + return cell + +/obj/item/gun/energy/New() + ..() + if(cell_type) + cell = new cell_type(src) + else + cell = new(src) + cell.give(cell.maxcharge) + update_ammo_types() + on_recharge() + if(selfcharge) + START_PROCESSING(SSobj, src) + update_icon() + +/obj/item/gun/energy/proc/update_ammo_types() + var/obj/item/ammo_casing/energy/shot + for(var/i = 1, i <= ammo_type.len, i++) + var/shottype = ammo_type[i] + shot = new shottype(src) + ammo_type[i] = shot + shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + +/obj/item/gun/energy/Destroy() + if(selfcharge) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/gun/energy/process() + if(selfcharge) //Every [recharge_time] ticks, recharge a shot for the cyborg + charge_tick++ + if(charge_tick < charge_delay) + return + charge_tick = 0 + if(!cell) + return // check if we actually need to recharge + var/obj/item/ammo_casing/energy/E = ammo_type[select] + if(use_external_power) + var/obj/item/stock_parts/cell/external = get_external_cell() + if(!external || !external.use(E.e_cost)) //Take power from the borg... + return //Note, uses /10 because of shitty mods to the cell system + cell.give(100) //... to recharge the shot + on_recharge() + update_icon() + +/obj/item/gun/energy/proc/on_recharge() + newshot() + +/obj/item/gun/energy/attack_self(mob/living/user as mob) + if(ammo_type.len > 1) + select_fire(user) + update_icon() + if(istype(user,/mob/living/carbon/human)) //This has to be here or else if you toggle modes by clicking the gun in hand + var/mob/living/carbon/human/H = user //Otherwise the mob icon doesn't update, blame shitty human update_icons() code + H.update_inv_l_hand() + H.update_inv_r_hand() + +/obj/item/gun/energy/can_shoot() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + return cell.charge >= shot.e_cost + +/obj/item/gun/energy/newshot() + if(!ammo_type || !cell) + return + if(!chambered) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(cell.charge >= shot.e_cost) //if there's enough power in the WEAPON'S cell... + chambered = shot //...prepare a new shot based on the current ammo type selected + if(!chambered.BB) + chambered.newshot() + +/obj/item/gun/energy/process_chamber() + if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... + var/obj/item/ammo_casing/energy/shot = chambered + cell.use(shot.e_cost)//... drain the cell cell + robocharge() + chambered = null //either way, released the prepared shot + newshot() + +/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = 1, params, zone_override, bonus_spread = 0) + if(!chambered && can_shoot()) + process_chamber() + return ..() + +/obj/item/gun/energy/proc/select_fire(mob/living/user) + select++ + if(select > ammo_type.len) + select = 1 + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if(shot.select_name) + to_chat(user, "[src] is now set to [shot.select_name].") + if(chambered)//phil235 + if(chambered.BB) + qdel(chambered.BB) + chambered.BB = null + chambered = null + newshot() + update_icon() + return + +/obj/item/gun/energy/update_icon() + overlays.Cut() + var/ratio = Ceiling((cell.charge / cell.maxcharge) * charge_sections) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + var/iconState = "[icon_state]_charge" + var/itemState = null + if(!initial(item_state)) + itemState = icon_state + if(modifystate) + overlays += "[icon_state]_[shot.select_name]" + iconState += "_[shot.select_name]" + if(itemState) + itemState += "[shot.select_name]" + if(cell.charge < shot.e_cost) + overlays += "[icon_state]_empty" + else + if(!shaded_charge) + for(var/i = ratio, i >= 1, i--) + overlays += image(icon = icon, icon_state = iconState, pixel_x = ammo_x_offset * (i -1)) + else + overlays += image(icon = icon, icon_state = "[icon_state]_[modifystate ? "[shot.select_name]_" : ""]charge[ratio]") + if(gun_light && can_flashlight) + var/iconF = "flight" + if(gun_light.on) + iconF = "flight_on" + overlays += image(icon = icon, icon_state = iconF, pixel_x = flight_x_offset, pixel_y = flight_y_offset) + if(bayonet && can_bayonet) + overlays += knife_overlay + if(itemState) + itemState += "[ratio]" + item_state = itemState + +/obj/item/gun/energy/ui_action_click() + toggle_gunlight() + +/obj/item/gun/energy/suicide_act(mob/user) + if(can_shoot()) + user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") + sleep(25) + if(user.l_hand == src || user.r_hand == src) + user.visible_message("[user] melts [user.p_their()] face off with the [name]!") + playsound(loc, fire_sound, 50, 1, -1) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + cell.use(shot.e_cost) + update_icon() + return FIRELOSS + else + user.visible_message("[user] panics and starts choking to death!") + return OXYLOSS + else + user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) + return OXYLOSS + +/obj/item/gun/energy/vv_edit_var(var_name, var_value) + switch(var_name) + if("selfcharge") + if(var_value) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/gun/energy/proc/robocharge() + if(cell.charge == cell.maxcharge) + // No point in recharging a weapon's cell that is already at 100%. That would just waste borg cell power for no reason. + return + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc + if(R && R.cell) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot + if(R.cell.use(shot.e_cost)) //Take power from the borg... + cell.give(shot.e_cost) //... to recharge the shot + +/obj/item/gun/energy/proc/get_external_cell() + if(istype(loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.back) + var/obj/item/rig/suit = H.back + if(istype(suit)) + return suit.cell + return null diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 518caa8ba701..ecf5eeed77e9 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -599,4 +599,4 @@ desc = "Causes kinetic accelerator bolts to have an adjustable-colored tracer trail and explosion. Use in-hand to change color." /obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user) - bolt_color = input(user,"","Choose Color",bolt_color) as color|null \ No newline at end of file + bolt_color = input(user,"","Choose Color",bolt_color) as color|null diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 8635e08dfbae..9d4cfaee3d47 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -1,159 +1,159 @@ -/obj/item/gun/energy/laser - name = "laser gun" - desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal." - icon_state = "laser" - item_state = "laser" - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=2000) - origin_tech = "combat=4;magnets=2" - ammo_type = list(/obj/item/ammo_casing/energy/lasergun) - ammo_x_offset = 1 - shaded_charge = 1 - -/obj/item/gun/energy/laser/practice - name = "practice laser gun" - desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice." - origin_tech = "combat=2;magnets=2" - ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) - clumsy_check = 0 - needs_permit = 0 - -/obj/item/gun/energy/laser/retro - name ="retro laser gun" - icon_state = "retro" - desc = "An older model of the basic lasergun, no longer used by Nanotrasen's private security or military forces. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." - ammo_x_offset = 3 - -/obj/item/gun/energy/laser/captain - name = "antique laser gun" - icon_state = "caplaser" - item_state = "caplaser" - desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding." - force = 10 - origin_tech = null - ammo_x_offset = 3 - selfcharge = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/gun/energy/laser/captain/scattershot - name = "scatter shot laser rifle" - icon_state = "lasercannon" - item_state = "laser" - desc = "An industrial-grade heavy-duty laser rifle with a modified laser lense to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theorically infinite use." - origin_tech = "combat=5;materials=4;powerstorage=4" - ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) - shaded_charge = 0 - -/obj/item/gun/energy/laser/cyborg - can_charge = 0 - desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" - ammo_type = list(/obj/item/ammo_casing/energy/laser/cyborg) - origin_tech = null - -/obj/item/gun/energy/laser/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/laser/cyborg/emp_act() - return - -/obj/item/gun/energy/laser/scatter - name = "scatter laser gun" - desc = "A laser gun equipped with a refraction kit that spreads bolts." - ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) - -///Laser Cannon - -/obj/item/gun/energy/lasercannon - name = "accelerator laser cannon" - desc = "An advanced laser cannon that does more damage the farther away the target is." - icon_state = "lasercannon" - item_state = "laser" - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - origin_tech = "combat=4;magnets=4;powerstorage=3" - ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) - ammo_x_offset = 3 - -/obj/item/gun/energy/lasercannon/isHandgun() - return 0 - -/obj/item/ammo_casing/energy/laser/accelerator - projectile_type = /obj/item/projectile/beam/laser/accelerator - select_name = "accelerator" - fire_sound = 'sound/weapons/lasercannonfire.ogg' - -/obj/item/projectile/beam/laser/accelerator - name = "accelerator laser" - icon_state = "heavylaser" - range = 255 - damage = 6 - -/obj/item/projectile/beam/laser/accelerator/Range() - ..() - damage = min(damage+7, 100) - -/obj/item/gun/energy/lasercannon/mounted - name = "mounted laser cannon" - selfcharge = 1 - use_external_power = 1 - charge_delay = 10 - -/obj/item/gun/energy/lasercannon/cyborg - -/obj/item/gun/energy/lasercannon/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/lasercannon/cyborg/emp_act() - return - -/obj/item/gun/energy/xray - name = "xray laser gun" - desc = "A high-power laser gun capable of expelling concentrated xray blasts. These blasts will penetrate solid objects, but will decrease in power the longer they have to travel." - icon_state = "xray" - origin_tech = "combat=6;materials=4;magnets=4;syndicate=1" - ammo_type = list(/obj/item/ammo_casing/energy/xray) - -/obj/item/gun/energy/immolator - name = "Immolator laser gun" - desc = "A modified laser gun, shooting highly concetrated beams with higher intensity that ignites the target, for the cost of draining more power per shot" - icon_state = "immolator" - item_state = "laser" - ammo_type = list(/obj/item/ammo_casing/energy/immolator) - origin_tech = "combat=4;magnets=4;powerstorage=3" - shaded_charge = 1 - -/obj/item/gun/energy/immolator/multi - name = "multi lens immolator cannon" - desc = "A large laser cannon, similar to the Immolator Laser, with toggleable firemodes. It is frequently used by military-like forces through Nanotrasen." - icon_state = "multilensimmolator" - ammo_type = list(/obj/item/ammo_casing/energy/immolator/strong, /obj/item/ammo_casing/energy/immolator/scatter) - origin_tech = "combat=5;magnets=5;powerstorage=4" - -/obj/item/gun/energy/immolator/multi/update_icon() - ..() - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - var/append = shot.select_name - overlays += image(icon = icon, icon_state = "multilensimmolator-[append]") - -////////Laser Tag//////////////////// - -/obj/item/gun/energy/laser/tag - name = "laser tag gun" - desc = "Standard issue weapon of the Imperial Guard" - origin_tech = "combat=2;magnets=2" - clumsy_check = 0 - needs_permit = 0 - ammo_x_offset = 2 - selfcharge = 1 - -/obj/item/gun/energy/laser/tag/blue - icon_state = "bluetag" - ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag) - -/obj/item/gun/energy/laser/tag/red - icon_state = "redtag" - ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag) +/obj/item/gun/energy/laser + name = "laser gun" + desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal." + icon_state = "laser" + item_state = "laser" + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=2000) + origin_tech = "combat=4;magnets=2" + ammo_type = list(/obj/item/ammo_casing/energy/lasergun) + ammo_x_offset = 1 + shaded_charge = 1 + +/obj/item/gun/energy/laser/practice + name = "practice laser gun" + desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice." + origin_tech = "combat=2;magnets=2" + ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) + clumsy_check = 0 + needs_permit = 0 + +/obj/item/gun/energy/laser/retro + name ="retro laser gun" + icon_state = "retro" + desc = "An older model of the basic lasergun, no longer used by Nanotrasen's private security or military forces. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." + ammo_x_offset = 3 + +/obj/item/gun/energy/laser/captain + name = "antique laser gun" + icon_state = "caplaser" + item_state = "caplaser" + desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding." + force = 10 + origin_tech = null + ammo_x_offset = 3 + selfcharge = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/gun/energy/laser/captain/scattershot + name = "scatter shot laser rifle" + icon_state = "lasercannon" + item_state = "laser" + desc = "An industrial-grade heavy-duty laser rifle with a modified laser lense to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theorically infinite use." + origin_tech = "combat=5;materials=4;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) + shaded_charge = 0 + +/obj/item/gun/energy/laser/cyborg + can_charge = 0 + desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" + ammo_type = list(/obj/item/ammo_casing/energy/laser/cyborg) + origin_tech = null + +/obj/item/gun/energy/laser/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/laser/cyborg/emp_act() + return + +/obj/item/gun/energy/laser/scatter + name = "scatter laser gun" + desc = "A laser gun equipped with a refraction kit that spreads bolts." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) + +///Laser Cannon + +/obj/item/gun/energy/lasercannon + name = "accelerator laser cannon" + desc = "An advanced laser cannon that does more damage the farther away the target is." + icon_state = "lasercannon" + item_state = "laser" + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + origin_tech = "combat=4;magnets=4;powerstorage=3" + ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) + ammo_x_offset = 3 + +/obj/item/gun/energy/lasercannon/isHandgun() + return 0 + +/obj/item/ammo_casing/energy/laser/accelerator + projectile_type = /obj/item/projectile/beam/laser/accelerator + select_name = "accelerator" + fire_sound = 'sound/weapons/lasercannonfire.ogg' + +/obj/item/projectile/beam/laser/accelerator + name = "accelerator laser" + icon_state = "heavylaser" + range = 255 + damage = 6 + +/obj/item/projectile/beam/laser/accelerator/Range() + ..() + damage = min(damage+7, 100) + +/obj/item/gun/energy/lasercannon/mounted + name = "mounted laser cannon" + selfcharge = 1 + use_external_power = 1 + charge_delay = 10 + +/obj/item/gun/energy/lasercannon/cyborg + +/obj/item/gun/energy/lasercannon/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/lasercannon/cyborg/emp_act() + return + +/obj/item/gun/energy/xray + name = "xray laser gun" + desc = "A high-power laser gun capable of expelling concentrated xray blasts. These blasts will penetrate solid objects, but will decrease in power the longer they have to travel." + icon_state = "xray" + origin_tech = "combat=6;materials=4;magnets=4;syndicate=1" + ammo_type = list(/obj/item/ammo_casing/energy/xray) + +/obj/item/gun/energy/immolator + name = "Immolator laser gun" + desc = "A modified laser gun, shooting highly concetrated beams with higher intensity that ignites the target, for the cost of draining more power per shot" + icon_state = "immolator" + item_state = "laser" + ammo_type = list(/obj/item/ammo_casing/energy/immolator) + origin_tech = "combat=4;magnets=4;powerstorage=3" + shaded_charge = 1 + +/obj/item/gun/energy/immolator/multi + name = "multi lens immolator cannon" + desc = "A large laser cannon, similar to the Immolator Laser, with toggleable firemodes. It is frequently used by military-like forces through Nanotrasen." + icon_state = "multilensimmolator" + ammo_type = list(/obj/item/ammo_casing/energy/immolator/strong, /obj/item/ammo_casing/energy/immolator/scatter) + origin_tech = "combat=5;magnets=5;powerstorage=4" + +/obj/item/gun/energy/immolator/multi/update_icon() + ..() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + var/append = shot.select_name + overlays += image(icon = icon, icon_state = "multilensimmolator-[append]") + +////////Laser Tag//////////////////// + +/obj/item/gun/energy/laser/tag + name = "laser tag gun" + desc = "Standard issue weapon of the Imperial Guard" + origin_tech = "combat=2;magnets=2" + clumsy_check = 0 + needs_permit = 0 + ammo_x_offset = 2 + selfcharge = 1 + +/obj/item/gun/energy/laser/tag/blue + icon_state = "bluetag" + ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag) + +/obj/item/gun/energy/laser/tag/red + icon_state = "redtag" + ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag) diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index 4663b2d5c040..09b2400c165e 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -1,99 +1,99 @@ -/obj/item/gun/energy/gun - name = "energy gun" - desc = "A basic energy-based gun with two settings: kill and disable." - icon_state = "energy" - item_state = null //so the human update icon uses the icon_state instead. - ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) - origin_tech = "combat=4;magnets=3" - modifystate = 2 - can_flashlight = 1 - ammo_x_offset = 3 - flight_x_offset = 15 - flight_y_offset = 10 - -/obj/item/gun/energy/gun/cyborg - desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" - -/obj/item/gun/energy/gun/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/gun/cyborg/emp_act() - return - -/obj/item/gun/energy/gun/mounted - name = "mounted energy gun" - selfcharge = 1 - use_external_power = 1 - -/obj/item/gun/energy/gun/mini - name = "miniature energy gun" - desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: disable and kill." - icon_state = "mini" - w_class = WEIGHT_CLASS_SMALL - ammo_x_offset = 2 - charge_sections = 3 - can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update - actions_types = list(/datum/action/item_action/toggle_gunlight) - -/obj/item/gun/energy/gun/mini/New() - gun_light = new /obj/item/flashlight/seclite(src) - ..() - cell.maxcharge = 600 - cell.charge = 600 - -/obj/item/gun/energy/gun/mini/update_icon() - ..() - if(gun_light && gun_light.on) - overlays += "mini-light" - -/obj/item/gun/energy/gun/hos - name = "\improper X-01 MultiPhase Energy Gun" - desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time." - icon_state = "hoslaser" - origin_tech = null - force = 10 - ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/disabler) - ammo_x_offset = 4 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/gun/energy/gun/blueshield - name = "advanced stun revolver" - desc = "An advanced stun revolver with the capacity to shoot both electrodes and lasers." - icon_state = "bsgun" - item_state = "gun" - force = 7 - ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos) - ammo_x_offset = 1 - shaded_charge = 1 - -/obj/item/gun/energy/gun/blueshield/pdw9 - name = "PDW-9 taser pistol" - desc = "A military grade sidearm, used by many militia forces throughout the local sector." - icon_state = "pdw9pistol" - -/obj/item/gun/energy/gun/turret - name = "hybrid turret gun" - desc = "A heavy hybrid energy cannon with two settings: Stun and kill." - icon_state = "turretlaser" - item_state = "turretlaser" - slot_flags = null - w_class = WEIGHT_CLASS_HUGE - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) - weapon_weight = WEAPON_HEAVY - can_flashlight = 0 - trigger_guard = TRIGGER_GUARD_NONE - ammo_x_offset = 2 - -/obj/item/gun/energy/gun/nuclear - name = "advanced energy gun" - desc = "An energy gun with an experimental miniaturized nuclear reactor that automatically charges the internal power cell." - icon_state = "nucgun" - item_state = "nucgun" - origin_tech = "combat=4;magnets=4;powerstorage=4" - var/fail_tick = 0 - charge_delay = 5 - can_charge = 0 - ammo_x_offset = 1 - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler) - selfcharge = 1 +/obj/item/gun/energy/gun + name = "energy gun" + desc = "A basic energy-based gun with two settings: kill and disable." + icon_state = "energy" + item_state = null //so the human update icon uses the icon_state instead. + ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) + origin_tech = "combat=4;magnets=3" + modifystate = 2 + can_flashlight = 1 + ammo_x_offset = 3 + flight_x_offset = 15 + flight_y_offset = 10 + +/obj/item/gun/energy/gun/cyborg + desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" + +/obj/item/gun/energy/gun/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/gun/cyborg/emp_act() + return + +/obj/item/gun/energy/gun/mounted + name = "mounted energy gun" + selfcharge = 1 + use_external_power = 1 + +/obj/item/gun/energy/gun/mini + name = "miniature energy gun" + desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: disable and kill." + icon_state = "mini" + w_class = WEIGHT_CLASS_SMALL + ammo_x_offset = 2 + charge_sections = 3 + can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update + actions_types = list(/datum/action/item_action/toggle_gunlight) + +/obj/item/gun/energy/gun/mini/New() + gun_light = new /obj/item/flashlight/seclite(src) + ..() + cell.maxcharge = 600 + cell.charge = 600 + +/obj/item/gun/energy/gun/mini/update_icon() + ..() + if(gun_light && gun_light.on) + overlays += "mini-light" + +/obj/item/gun/energy/gun/hos + name = "\improper X-01 MultiPhase Energy Gun" + desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time." + icon_state = "hoslaser" + origin_tech = null + force = 10 + ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 4 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/gun/energy/gun/blueshield + name = "advanced stun revolver" + desc = "An advanced stun revolver with the capacity to shoot both electrodes and lasers." + icon_state = "bsgun" + item_state = "gun" + force = 7 + ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos) + ammo_x_offset = 1 + shaded_charge = 1 + +/obj/item/gun/energy/gun/blueshield/pdw9 + name = "PDW-9 taser pistol" + desc = "A military grade sidearm, used by many militia forces throughout the local sector." + icon_state = "pdw9pistol" + +/obj/item/gun/energy/gun/turret + name = "hybrid turret gun" + desc = "A heavy hybrid energy cannon with two settings: Stun and kill." + icon_state = "turretlaser" + item_state = "turretlaser" + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + weapon_weight = WEAPON_HEAVY + can_flashlight = 0 + trigger_guard = TRIGGER_GUARD_NONE + ammo_x_offset = 2 + +/obj/item/gun/energy/gun/nuclear + name = "advanced energy gun" + desc = "An energy gun with an experimental miniaturized nuclear reactor that automatically charges the internal power cell." + icon_state = "nucgun" + item_state = "nucgun" + origin_tech = "combat=4;magnets=4;powerstorage=4" + var/fail_tick = 0 + charge_delay = 5 + can_charge = 0 + ammo_x_offset = 1 + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler) + selfcharge = 1 diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 1a5766dc9002..c773cb3563c7 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -1,85 +1,85 @@ -/obj/item/gun/energy/pulse - name = "pulse rifle" - desc = "A heavy-duty, multifaceted energy rifle with three modes. Preferred by front-line combat personnel." - icon_state = "pulse" - item_state = null - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) - cell_type = /obj/item/stock_parts/cell/pulse - -/obj/item/gun/energy/pulse/emp_act(severity) - return - -/obj/item/gun/energy/pulse/isHandgun() - return 0 - -/obj/item/gun/energy/pulse/cyborg - -/obj/item/gun/energy/pulse/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/pulse/carbine - name = "pulse carbine" - desc = "A compact variant of the pulse rifle with less firepower but easier storage." - w_class = WEIGHT_CLASS_NORMAL - slot_flags = SLOT_BELT - icon_state = "pulse_carbine" - item_state = "pulse" - cell_type = /obj/item/stock_parts/cell/pulse/carbine - can_flashlight = 1 - flight_x_offset = 18 - flight_y_offset = 12 - -/obj/item/gun/energy/pulse/pistol - name = "pulse pistol" - desc = "A pulse rifle in an easily concealed handgun package with low capacity." - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - icon_state = "pulse_pistol" - item_state = "gun" - cell_type = /obj/item/stock_parts/cell/pulse/pistol - can_charge = 0 - -/obj/item/gun/energy/pulse/pistol/isHandgun() - return 1 - -/obj/item/gun/energy/pulse/destroyer - name = "pulse destroyer" - desc = "A heavy-duty, pulse-based energy weapon." - cell_type = /obj/item/stock_parts/cell/infinite - ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) - -/obj/item/gun/energy/pulse/destroyer/attack_self(mob/living/user) - to_chat(user, "[name] has three settings, and they are all DESTROY.") - -/obj/item/gun/energy/pulse/destroyer/annihilator - name = "pulse ANNIHILATOR" - desc = "For when the situation calls for a little more than a pulse destroyer." - ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter/pulse) - -/obj/item/gun/energy/pulse/pistol/m1911 - name = "\improper M1911-P" - desc = "A compact pulse core in a classic handgun frame for Nanotrasen officers. It's not the size of the gun, it's the size of the hole it puts through people." - icon_state = "m1911" - item_state = "gun" - cell_type = /obj/item/stock_parts/cell/infinite - -/obj/item/gun/energy/pulse/pistol/m1911/isHandgun() - return 1 - -/obj/item/gun/energy/pulse/turret - name = "pulse turret gun" - desc = "A heavy, turret-mounted pulse energy cannon." - icon_state = "turretlaser" - item_state = "turretlaser" - slot_flags = null - w_class = WEIGHT_CLASS_HUGE - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser/pulse) - weapon_weight = WEAPON_MEDIUM - can_flashlight = 0 - trigger_guard = TRIGGER_GUARD_NONE - ammo_x_offset = 2 +/obj/item/gun/energy/pulse + name = "pulse rifle" + desc = "A heavy-duty, multifaceted energy rifle with three modes. Preferred by front-line combat personnel." + icon_state = "pulse" + item_state = null + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + cell_type = /obj/item/stock_parts/cell/pulse + +/obj/item/gun/energy/pulse/emp_act(severity) + return + +/obj/item/gun/energy/pulse/isHandgun() + return 0 + +/obj/item/gun/energy/pulse/cyborg + +/obj/item/gun/energy/pulse/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/pulse/carbine + name = "pulse carbine" + desc = "A compact variant of the pulse rifle with less firepower but easier storage." + w_class = WEIGHT_CLASS_NORMAL + slot_flags = SLOT_BELT + icon_state = "pulse_carbine" + item_state = "pulse" + cell_type = /obj/item/stock_parts/cell/pulse/carbine + can_flashlight = 1 + flight_x_offset = 18 + flight_y_offset = 12 + +/obj/item/gun/energy/pulse/pistol + name = "pulse pistol" + desc = "A pulse rifle in an easily concealed handgun package with low capacity." + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + icon_state = "pulse_pistol" + item_state = "gun" + cell_type = /obj/item/stock_parts/cell/pulse/pistol + can_charge = 0 + +/obj/item/gun/energy/pulse/pistol/isHandgun() + return 1 + +/obj/item/gun/energy/pulse/destroyer + name = "pulse destroyer" + desc = "A heavy-duty, pulse-based energy weapon." + cell_type = /obj/item/stock_parts/cell/infinite + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) + +/obj/item/gun/energy/pulse/destroyer/attack_self(mob/living/user) + to_chat(user, "[name] has three settings, and they are all DESTROY.") + +/obj/item/gun/energy/pulse/destroyer/annihilator + name = "pulse ANNIHILATOR" + desc = "For when the situation calls for a little more than a pulse destroyer." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter/pulse) + +/obj/item/gun/energy/pulse/pistol/m1911 + name = "\improper M1911-P" + desc = "A compact pulse core in a classic handgun frame for Nanotrasen officers. It's not the size of the gun, it's the size of the hole it puts through people." + icon_state = "m1911" + item_state = "gun" + cell_type = /obj/item/stock_parts/cell/infinite + +/obj/item/gun/energy/pulse/pistol/m1911/isHandgun() + return 1 + +/obj/item/gun/energy/pulse/turret + name = "pulse turret gun" + desc = "A heavy, turret-mounted pulse energy cannon." + icon_state = "turretlaser" + item_state = "turretlaser" + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser/pulse) + weapon_weight = WEAPON_MEDIUM + can_flashlight = 0 + trigger_guard = TRIGGER_GUARD_NONE + ammo_x_offset = 2 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 0572ce36f76b..7705f3eb66c2 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -1,519 +1,519 @@ -// Ion Rifles // -/obj/item/gun/energy/ionrifle - name = "ion rifle" - desc = "A man portable anti-armor weapon designed to disable mechanical threats" - icon_state = "ionrifle" - item_state = null //so the human update icon uses the icon_state instead. - fire_sound = 'sound/weapons/ionrifle.ogg' - origin_tech = "combat=4;magnets=4" - w_class = WEIGHT_CLASS_HUGE - flags = CONDUCT - slot_flags = SLOT_BACK - ammo_type = list(/obj/item/ammo_casing/energy/ion) - ammo_x_offset = 3 - flight_x_offset = 17 - flight_y_offset = 9 - -/obj/item/gun/energy/ionrifle/emp_act(severity) - return - -/obj/item/gun/energy/ionrifle/isHandgun() - return 0 - -/obj/item/gun/energy/ionrifle/carbine - name = "ion carbine" - desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient." - icon_state = "ioncarbine" - w_class = WEIGHT_CLASS_NORMAL - slot_flags = SLOT_BELT - ammo_x_offset = 2 - flight_x_offset = 18 - flight_y_offset = 11 - -// Decloner // -/obj/item/gun/energy/decloner - name = "biological demolecularisor" - desc = "A gun that discharges high amounts of controlled radiation to slowly break a target into component elements." - icon_state = "decloner" - fire_sound = 'sound/weapons/pulse3.ogg' - origin_tech = "combat=4;materials=4;biotech=5;plasmatech=6" - ammo_type = list(/obj/item/ammo_casing/energy/declone) - ammo_x_offset = 1 - -/obj/item/gun/energy/decloner/update_icon() - ..() - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - if(cell.charge > shot.e_cost) - overlays += "decloner_spin" - -// Flora Gun // -/obj/item/gun/energy/floragun - name = "floral somatoray" - desc = "A tool that discharges controlled radiation which induces mutation in plant cells." - icon_state = "flora" - item_state = "gun" - fire_sound = 'sound/effects/stealthoff.ogg' - ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut) - origin_tech = "materials=2;biotech=4" - modifystate = 1 - ammo_x_offset = 1 - selfcharge = 1 - -// Meteor Gun // -/obj/item/gun/energy/meteorgun - name = "meteor gun" - desc = "For the love of god, make sure you're aiming this the right way!" - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "riotgun" - item_state = "c20r" - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - w_class = WEIGHT_CLASS_BULKY - ammo_type = list(/obj/item/ammo_casing/energy/meteor) - cell_type = /obj/item/stock_parts/cell/potato - clumsy_check = 0 //Admin spawn only, might as well let clowns use it. - selfcharge = 1 - -/obj/item/gun/energy/meteorgun/pen - name = "meteor pen" - desc = "The pen is mightier than the sword." - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "pen" - item_state = "pen" - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - w_class = WEIGHT_CLASS_TINY - -// Mind Flayer // -/obj/item/gun/energy/mindflayer - name = "\improper Mind Flayer" - desc = "A prototype weapon recovered from the ruins of Research-Station Epsilon." - icon_state = "xray" - item_state = null - ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) - ammo_x_offset = 2 - -// Energy Crossbows // -/obj/item/gun/energy/kinetic_accelerator/crossbow - name = "mini energy crossbow" - desc = "A weapon favored by syndicate stealth specialists." - icon_state = "crossbow" - item_state = "crossbow" - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=2000) - origin_tech = "combat=4;magnets=4;syndicate=5" - suppressed = 1 - ammo_type = list(/obj/item/ammo_casing/energy/bolt) - weapon_weight = WEAPON_LIGHT - unique_rename = 0 - overheat_time = 20 - holds_charge = TRUE - unique_frequency = TRUE - can_flashlight = 0 - max_mod_capacity = 0 - empty_state = null - -/obj/item/gun/energy/kinetic_accelerator/crossbow/ninja - name = "energy dart thrower" - ammo_type = list(/obj/item/ammo_casing/energy/dart) - -/obj/item/gun/energy/kinetic_accelerator/crossbow/large - name = "energy crossbow" - desc = "A reverse engineered weapon using syndicate technology." - icon_state = "crossbowlarge" - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=4000) - origin_tech = "combat=4;magnets=4;syndicate=2" - suppressed = 0 - ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) - -/obj/item/gun/energy/kinetic_accelerator/crossbow/large/cyborg - desc = "One and done!" - icon_state = "crossbowlarge" - origin_tech = null - materials = list() - -/obj/item/gun/energy/kinetic_accelerator/suicide_act(mob/user) - if(!suppressed) - playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) - user.visible_message("[user] cocks the [name] and pretends to blow [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!") - shoot_live_shot() - return OXYLOSS - -// Plasma Cutters // -/obj/item/gun/energy/plasmacutter - name = "plasma cutter" - desc = "A mining tool capable of expelling concentrated plasma bursts. You could use it to cut limbs off of xenos! Or, you know, mine stuff." - icon_state = "plasmacutter" - item_state = "plasmacutter" - modifystate = -1 - origin_tech = "combat=1;materials=3;magnets=2;plasmatech=3;engineering=1" - ammo_type = list(/obj/item/ammo_casing/energy/plasma) - fire_sound = 'sound/weapons/laser.ogg' - usesound = 'sound/items/welder.ogg' - toolspeed = 1 - container_type = OPENCONTAINER - flags = CONDUCT - attack_verb = list("attacked", "slashed", "cut", "sliced") - force = 12 - sharp = 1 - can_charge = 0 - -/obj/item/gun/energy/plasmacutter/examine(mob/user) - . = ..() - if(cell) - . += "[src] is [round(cell.percent())]% charged." - -/obj/item/gun/energy/plasmacutter/attackby(obj/item/A, mob/user) - if(istype(A, /obj/item/stack/sheet/mineral/plasma)) - if(cell.charge >= cell.maxcharge) - to_chat(user,"[src] is already fully charged.") - return - var/obj/item/stack/sheet/S = A - S.use(1) - cell.give(1000) - on_recharge() - to_chat(user, "You insert [A] in [src], recharging it.") - else if(istype(A, /obj/item/stack/ore/plasma)) - if(cell.charge >= cell.maxcharge) - to_chat(user,"[src] is already fully charged.") - return - var/obj/item/stack/ore/S = A - S.use(1) - cell.give(500) - on_recharge() - to_chat(user, "You insert [A] in [src], recharging it.") - else - return ..() - -/obj/item/gun/energy/plasmacutter/update_icon() - return - -/obj/item/gun/energy/plasmacutter/adv - name = "advanced plasma cutter" - icon_state = "adv_plasmacutter" - modifystate = "adv_plasmacutter" - origin_tech = "combat=3;materials=4;magnets=3;plasmatech=4;engineering=2" - ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv) - force = 15 - -// Wormhole Projectors // -/obj/item/gun/energy/wormhole_projector - name = "bluespace wormhole projector" - desc = "A projector that emits high density quantum-coupled bluespace beams." - ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange) - item_state = null - icon_state = "wormhole_projector1" - origin_tech = "combat=4;bluespace=6;plasmatech=4;engineering=4" - var/obj/effect/portal/blue - var/obj/effect/portal/orange - - -/obj/item/gun/energy/wormhole_projector/update_icon() - icon_state = "wormhole_projector[select]" - item_state = icon_state - return - -/obj/item/gun/energy/wormhole_projector/process_chamber() - ..() - select_fire(usr) - -/obj/item/gun/energy/wormhole_projector/portal_destroyed(obj/effect/portal/P) - if(P.icon_state == "portal") - blue = null - if(orange) - orange.target = null - else - orange = null - if(blue) - blue.target = null - -/obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W) - var/obj/effect/portal/P = new /obj/effect/portal(get_turf(W), null, src) - P.precision = 0 - P.failchance = 0 - P.can_multitool_to_remove = 1 - if(W.name == "bluespace beam") - qdel(blue) - blue = P - else - qdel(orange) - P.icon_state = "portal1" - orange = P - if(orange && blue) - blue.target = get_turf(orange) - orange.target = get_turf(blue) - -/* 3d printer 'pseudo guns' for borgs */ -/obj/item/gun/energy/printer - name = "cyborg lmg" - desc = "A machinegun that fires 3d-printed flachettes slowly regenerated using a cyborg's internal power source." - icon_state = "l6closed0" - icon = 'icons/obj/guns/projectile.dmi' - cell_type = /obj/item/stock_parts/cell/secborg - ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) - can_charge = 0 - -/obj/item/gun/energy/printer/update_icon() - return - -/obj/item/gun/energy/printer/emp_act() - return - -// Instakill Lasers // -/obj/item/gun/energy/laser/instakill - name = "instakill rifle" - icon_state = "instagib" - item_state = "instagib" - desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." - ammo_type = list(/obj/item/ammo_casing/energy/instakill) - force = 60 - origin_tech = "combat=7;magnets=6" - -/obj/item/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib - return - -/obj/item/gun/energy/laser/instakill/red - desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." - icon_state = "instagibred" - item_state = "instagibred" - ammo_type = list(/obj/item/ammo_casing/energy/instakill/red) - -/obj/item/gun/energy/laser/instakill/blue - desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a blue design." - icon_state = "instagibblue" - item_state = "instagibblue" - ammo_type = list(/obj/item/ammo_casing/energy/instakill/blue) - -// HONK Rifle // -/obj/item/gun/energy/clown - name = "HONK Rifle" - desc = "Clown Planet's finest." - icon_state = "disabler" - ammo_type = list(/obj/item/ammo_casing/energy/clown) - clumsy_check = 0 - selfcharge = 1 - ammo_x_offset = 3 - -/obj/item/gun/energy/toxgun - name = "plasma pistol" - desc = "A specialized firearm designed to fire lethal bolts of toxins." - icon_state = "toxgun" - fire_sound = 'sound/effects/stealthoff.ogg' - - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=4;magnets=4;powerstorage=3" - ammo_type = list(/obj/item/ammo_casing/energy/toxplasma) - shaded_charge = 1 - -// Energy Sniper // -/obj/item/gun/energy/sniperrifle - name = "L.W.A.P. Sniper Rifle" - desc = "A rifle constructed of lightweight materials, fitted with a SMART aiming-system scope." - icon_state = "esniper" - origin_tech = "combat=6;materials=5;powerstorage=4" - ammo_type = list(/obj/item/ammo_casing/energy/sniper) - slot_flags = SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - zoomable = TRUE - zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. - shaded_charge = 1 - -// Temperature Gun // -/obj/item/gun/energy/temperature - name = "temperature gun" - icon = 'icons/obj/guns/gun_temperature.dmi' - icon_state = "tempgun_4" - item_state = "tempgun_4" - slot_flags = SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - fire_sound = 'sound/weapons/pulse3.ogg' - desc = "A gun that changes the body temperature of its targets." - var/temperature = 300 - var/target_temperature = 300 - origin_tech = "combat=4;materials=4;powerstorage=3;magnets=2" - - ammo_type = list(/obj/item/ammo_casing/energy/temp) - selfcharge = 1 - - var/powercost = "" - var/powercostcolor = "" - - var/emagged = 0 //ups the temperature cap from 500 to 1000, targets hit by beams over 500 Kelvin will burst into flames - var/dat = "" - -/obj/item/gun/energy/temperature/New() - ..() - update_icon() - START_PROCESSING(SSobj, src) - - -/obj/item/gun/energy/temperature/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/gun/energy/temperature/newshot() - ..() - -/obj/item/gun/energy/temperature/attack_self(mob/living/user as mob) - user.set_machine(src) - update_dat() - user << browse("Temperature Gun Configuration
    [dat]", "window=tempgun;size=510x120") - onclose(user, "tempgun") - -/obj/item/gun/energy/temperature/emag_act(mob/user) - if(!emagged) - emagged = TRUE - to_chat(user, "You double the gun's temperature cap! Targets hit by searing beams will burst into flames!") - desc = "A gun that changes the body temperature of its targets. Its temperature cap has been hacked." - -/obj/item/gun/energy/temperature/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - target_temperature = min((500 + 500*emagged), target_temperature+amount) - else - target_temperature = max(0, target_temperature+amount) - if(istype(loc, /mob)) - attack_self(loc) - add_fingerprint(usr) - return - -/obj/item/gun/energy/temperature/process() - ..() - var/obj/item/ammo_casing/energy/temp/T = ammo_type[select] - T.temp = temperature - switch(temperature) - if(0 to 100) - T.e_cost = 300 - powercost = "High" - if(100 to 250) - T.e_cost = 200 - powercost = "Medium" - if(251 to 300) - T.e_cost = 100 - powercost = "Low" - if(301 to 400) - T.e_cost = 200 - powercost = "Medium" - if(401 to 1000) - T.e_cost = 300 - powercost = "High" - switch(powercost) - if("High") - powercostcolor = "orange" - if("Medium") - powercostcolor = "green" - else - powercostcolor = "blue" - if(target_temperature != temperature) - var/difference = abs(target_temperature - temperature) - if(difference >= (10 + 40*emagged)) //so emagged temp guns adjust their temperature much more quickly - if(target_temperature < temperature) - temperature -= (10 + 40*emagged) - else - temperature += (10 + 40*emagged) - else - temperature = target_temperature - update_icon() - - if(istype(loc, /mob/living/carbon)) - var /mob/living/carbon/M = loc - if(src == M.machine) - update_dat() - M << browse("Temperature Gun Configuration
    [dat]", "window=tempgun;size=510x102") - return - -/obj/item/gun/energy/temperature/proc/update_dat() - dat = "" - dat += "Current output temperature: " - if(temperature > 500) - dat += "[temperature] ([round(temperature-T0C)]°C)" - dat += " SEARING!" - else if(temperature > (T0C + 50)) - dat += "[temperature] ([round(temperature-T0C)]°C)" - else if(temperature > (T0C - 50)) - dat += "[temperature] ([round(temperature-T0C)]°C)" - else - dat += "[temperature] ([round(temperature-T0C)]°C)" - dat += "
    " - dat += "Target output temperature: " //might be string idiocy, but at least it's easy to read - dat += "- " - dat += "- " - dat += "- " - dat += "[target_temperature] " - dat += "+ " - dat += "+ " - dat += "+" - dat += "
    " - dat += "Power cost: " - dat += "[powercost]" - -/obj/item/gun/energy/temperature/proc/update_temperature() - switch(temperature) - if(501 to INFINITY) - item_state = "tempgun_8" - if(400 to 500) - item_state = "tempgun_7" - if(360 to 400) - item_state = "tempgun_6" - if(335 to 360) - item_state = "tempgun_5" - if(295 to 335) - item_state = "tempgun_4" - if(260 to 295) - item_state = "tempgun_3" - if(200 to 260) - item_state = "tempgun_2" - if(120 to 260) - item_state = "tempgun_1" - if(-INFINITY to 120) - item_state = "tempgun_0" - icon_state = item_state - -/obj/item/gun/energy/temperature/update_icon() - overlays = 0 - update_temperature() - update_user() - update_charge() - -/obj/item/gun/energy/temperature/proc/update_user() - if(istype(loc,/mob/living/carbon)) - var/mob/living/carbon/M = loc - M.update_inv_back() - M.update_inv_l_hand() - M.update_inv_r_hand() - -/obj/item/gun/energy/temperature/proc/update_charge() - var/charge = cell.charge - switch(charge) - if(900 to INFINITY) overlays += "900" - if(800 to 900) overlays += "800" - if(700 to 800) overlays += "700" - if(600 to 700) overlays += "600" - if(500 to 600) overlays += "500" - if(400 to 500) overlays += "400" - if(300 to 400) overlays += "300" - if(200 to 300) overlays += "200" - if(100 to 202) overlays += "100" - if(-INFINITY to 100) overlays += "0" - -// Mimic Gun // -/obj/item/gun/energy/mimicgun - name = "mimic gun" - desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse. Why does this one have teeth?" - icon_state = "disabler" - ammo_type = list(/obj/item/ammo_casing/energy/mimic) - clumsy_check = 0 //Admin spawn only, might as well let clowns use it. - selfcharge = 1 - ammo_x_offset = 3 - var/mimic_type = /obj/item/gun/projectile/automatic/pistol //Setting this to the mimicgun type does exactly what you think it will. - -/obj/item/gun/energy/mimicgun/newshot() - var/obj/item/ammo_casing/energy/mimic/M = ammo_type[select] - M.mimic_type = mimic_type - ..() +// Ion Rifles // +/obj/item/gun/energy/ionrifle + name = "ion rifle" + desc = "A man portable anti-armor weapon designed to disable mechanical threats" + icon_state = "ionrifle" + item_state = null //so the human update icon uses the icon_state instead. + fire_sound = 'sound/weapons/ionrifle.ogg' + origin_tech = "combat=4;magnets=4" + w_class = WEIGHT_CLASS_HUGE + flags = CONDUCT + slot_flags = SLOT_BACK + ammo_type = list(/obj/item/ammo_casing/energy/ion) + ammo_x_offset = 3 + flight_x_offset = 17 + flight_y_offset = 9 + +/obj/item/gun/energy/ionrifle/emp_act(severity) + return + +/obj/item/gun/energy/ionrifle/isHandgun() + return 0 + +/obj/item/gun/energy/ionrifle/carbine + name = "ion carbine" + desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient." + icon_state = "ioncarbine" + w_class = WEIGHT_CLASS_NORMAL + slot_flags = SLOT_BELT + ammo_x_offset = 2 + flight_x_offset = 18 + flight_y_offset = 11 + +// Decloner // +/obj/item/gun/energy/decloner + name = "biological demolecularisor" + desc = "A gun that discharges high amounts of controlled radiation to slowly break a target into component elements." + icon_state = "decloner" + fire_sound = 'sound/weapons/pulse3.ogg' + origin_tech = "combat=4;materials=4;biotech=5;plasmatech=6" + ammo_type = list(/obj/item/ammo_casing/energy/declone) + ammo_x_offset = 1 + +/obj/item/gun/energy/decloner/update_icon() + ..() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(cell.charge > shot.e_cost) + overlays += "decloner_spin" + +// Flora Gun // +/obj/item/gun/energy/floragun + name = "floral somatoray" + desc = "A tool that discharges controlled radiation which induces mutation in plant cells." + icon_state = "flora" + item_state = "gun" + fire_sound = 'sound/effects/stealthoff.ogg' + ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut) + origin_tech = "materials=2;biotech=4" + modifystate = 1 + ammo_x_offset = 1 + selfcharge = 1 + +// Meteor Gun // +/obj/item/gun/energy/meteorgun + name = "meteor gun" + desc = "For the love of god, make sure you're aiming this the right way!" + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "riotgun" + item_state = "c20r" + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + w_class = WEIGHT_CLASS_BULKY + ammo_type = list(/obj/item/ammo_casing/energy/meteor) + cell_type = /obj/item/stock_parts/cell/potato + clumsy_check = 0 //Admin spawn only, might as well let clowns use it. + selfcharge = 1 + +/obj/item/gun/energy/meteorgun/pen + name = "meteor pen" + desc = "The pen is mightier than the sword." + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "pen" + item_state = "pen" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + +// Mind Flayer // +/obj/item/gun/energy/mindflayer + name = "\improper Mind Flayer" + desc = "A prototype weapon recovered from the ruins of Research-Station Epsilon." + icon_state = "xray" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) + ammo_x_offset = 2 + +// Energy Crossbows // +/obj/item/gun/energy/kinetic_accelerator/crossbow + name = "mini energy crossbow" + desc = "A weapon favored by syndicate stealth specialists." + icon_state = "crossbow" + item_state = "crossbow" + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=2000) + origin_tech = "combat=4;magnets=4;syndicate=5" + suppressed = 1 + ammo_type = list(/obj/item/ammo_casing/energy/bolt) + weapon_weight = WEAPON_LIGHT + unique_rename = 0 + overheat_time = 20 + holds_charge = TRUE + unique_frequency = TRUE + can_flashlight = 0 + max_mod_capacity = 0 + empty_state = null + +/obj/item/gun/energy/kinetic_accelerator/crossbow/ninja + name = "energy dart thrower" + ammo_type = list(/obj/item/ammo_casing/energy/dart) + +/obj/item/gun/energy/kinetic_accelerator/crossbow/large + name = "energy crossbow" + desc = "A reverse engineered weapon using syndicate technology." + icon_state = "crossbowlarge" + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=4000) + origin_tech = "combat=4;magnets=4;syndicate=2" + suppressed = 0 + ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) + +/obj/item/gun/energy/kinetic_accelerator/crossbow/large/cyborg + desc = "One and done!" + icon_state = "crossbowlarge" + origin_tech = null + materials = list() + +/obj/item/gun/energy/kinetic_accelerator/suicide_act(mob/user) + if(!suppressed) + playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) + user.visible_message("[user] cocks the [name] and pretends to blow [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!") + shoot_live_shot() + return OXYLOSS + +// Plasma Cutters // +/obj/item/gun/energy/plasmacutter + name = "plasma cutter" + desc = "A mining tool capable of expelling concentrated plasma bursts. You could use it to cut limbs off of xenos! Or, you know, mine stuff." + icon_state = "plasmacutter" + item_state = "plasmacutter" + modifystate = -1 + origin_tech = "combat=1;materials=3;magnets=2;plasmatech=3;engineering=1" + ammo_type = list(/obj/item/ammo_casing/energy/plasma) + fire_sound = 'sound/weapons/laser.ogg' + usesound = 'sound/items/welder.ogg' + toolspeed = 1 + container_type = OPENCONTAINER + flags = CONDUCT + attack_verb = list("attacked", "slashed", "cut", "sliced") + force = 12 + sharp = 1 + can_charge = 0 + +/obj/item/gun/energy/plasmacutter/examine(mob/user) + . = ..() + if(cell) + . += "[src] is [round(cell.percent())]% charged." + +/obj/item/gun/energy/plasmacutter/attackby(obj/item/A, mob/user) + if(istype(A, /obj/item/stack/sheet/mineral/plasma)) + if(cell.charge >= cell.maxcharge) + to_chat(user,"[src] is already fully charged.") + return + var/obj/item/stack/sheet/S = A + S.use(1) + cell.give(1000) + on_recharge() + to_chat(user, "You insert [A] in [src], recharging it.") + else if(istype(A, /obj/item/stack/ore/plasma)) + if(cell.charge >= cell.maxcharge) + to_chat(user,"[src] is already fully charged.") + return + var/obj/item/stack/ore/S = A + S.use(1) + cell.give(500) + on_recharge() + to_chat(user, "You insert [A] in [src], recharging it.") + else + return ..() + +/obj/item/gun/energy/plasmacutter/update_icon() + return + +/obj/item/gun/energy/plasmacutter/adv + name = "advanced plasma cutter" + icon_state = "adv_plasmacutter" + modifystate = "adv_plasmacutter" + origin_tech = "combat=3;materials=4;magnets=3;plasmatech=4;engineering=2" + ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv) + force = 15 + +// Wormhole Projectors // +/obj/item/gun/energy/wormhole_projector + name = "bluespace wormhole projector" + desc = "A projector that emits high density quantum-coupled bluespace beams." + ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange) + item_state = null + icon_state = "wormhole_projector1" + origin_tech = "combat=4;bluespace=6;plasmatech=4;engineering=4" + var/obj/effect/portal/blue + var/obj/effect/portal/orange + + +/obj/item/gun/energy/wormhole_projector/update_icon() + icon_state = "wormhole_projector[select]" + item_state = icon_state + return + +/obj/item/gun/energy/wormhole_projector/process_chamber() + ..() + select_fire(usr) + +/obj/item/gun/energy/wormhole_projector/portal_destroyed(obj/effect/portal/P) + if(P.icon_state == "portal") + blue = null + if(orange) + orange.target = null + else + orange = null + if(blue) + blue.target = null + +/obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W) + var/obj/effect/portal/P = new /obj/effect/portal(get_turf(W), null, src) + P.precision = 0 + P.failchance = 0 + P.can_multitool_to_remove = 1 + if(W.name == "bluespace beam") + qdel(blue) + blue = P + else + qdel(orange) + P.icon_state = "portal1" + orange = P + if(orange && blue) + blue.target = get_turf(orange) + orange.target = get_turf(blue) + +/* 3d printer 'pseudo guns' for borgs */ +/obj/item/gun/energy/printer + name = "cyborg lmg" + desc = "A machinegun that fires 3d-printed flachettes slowly regenerated using a cyborg's internal power source." + icon_state = "l6closed0" + icon = 'icons/obj/guns/projectile.dmi' + cell_type = /obj/item/stock_parts/cell/secborg + ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) + can_charge = 0 + +/obj/item/gun/energy/printer/update_icon() + return + +/obj/item/gun/energy/printer/emp_act() + return + +// Instakill Lasers // +/obj/item/gun/energy/laser/instakill + name = "instakill rifle" + icon_state = "instagib" + item_state = "instagib" + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." + ammo_type = list(/obj/item/ammo_casing/energy/instakill) + force = 60 + origin_tech = "combat=7;magnets=6" + +/obj/item/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib + return + +/obj/item/gun/energy/laser/instakill/red + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." + icon_state = "instagibred" + item_state = "instagibred" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/red) + +/obj/item/gun/energy/laser/instakill/blue + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a blue design." + icon_state = "instagibblue" + item_state = "instagibblue" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/blue) + +// HONK Rifle // +/obj/item/gun/energy/clown + name = "HONK Rifle" + desc = "Clown Planet's finest." + icon_state = "disabler" + ammo_type = list(/obj/item/ammo_casing/energy/clown) + clumsy_check = 0 + selfcharge = 1 + ammo_x_offset = 3 + +/obj/item/gun/energy/toxgun + name = "plasma pistol" + desc = "A specialized firearm designed to fire lethal bolts of toxins." + icon_state = "toxgun" + fire_sound = 'sound/effects/stealthoff.ogg' + + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=4;magnets=4;powerstorage=3" + ammo_type = list(/obj/item/ammo_casing/energy/toxplasma) + shaded_charge = 1 + +// Energy Sniper // +/obj/item/gun/energy/sniperrifle + name = "L.W.A.P. Sniper Rifle" + desc = "A rifle constructed of lightweight materials, fitted with a SMART aiming-system scope." + icon_state = "esniper" + origin_tech = "combat=6;materials=5;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/sniper) + slot_flags = SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + zoomable = TRUE + zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. + shaded_charge = 1 + +// Temperature Gun // +/obj/item/gun/energy/temperature + name = "temperature gun" + icon = 'icons/obj/guns/gun_temperature.dmi' + icon_state = "tempgun_4" + item_state = "tempgun_4" + slot_flags = SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + fire_sound = 'sound/weapons/pulse3.ogg' + desc = "A gun that changes the body temperature of its targets." + var/temperature = 300 + var/target_temperature = 300 + origin_tech = "combat=4;materials=4;powerstorage=3;magnets=2" + + ammo_type = list(/obj/item/ammo_casing/energy/temp) + selfcharge = 1 + + var/powercost = "" + var/powercostcolor = "" + + var/emagged = 0 //ups the temperature cap from 500 to 1000, targets hit by beams over 500 Kelvin will burst into flames + var/dat = "" + +/obj/item/gun/energy/temperature/New() + ..() + update_icon() + START_PROCESSING(SSobj, src) + + +/obj/item/gun/energy/temperature/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/gun/energy/temperature/newshot() + ..() + +/obj/item/gun/energy/temperature/attack_self(mob/living/user as mob) + user.set_machine(src) + update_dat() + user << browse("Temperature Gun Configuration
    [dat]", "window=tempgun;size=510x120") + onclose(user, "tempgun") + +/obj/item/gun/energy/temperature/emag_act(mob/user) + if(!emagged) + emagged = TRUE + to_chat(user, "You double the gun's temperature cap! Targets hit by searing beams will burst into flames!") + desc = "A gun that changes the body temperature of its targets. Its temperature cap has been hacked." + +/obj/item/gun/energy/temperature/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + target_temperature = min((500 + 500*emagged), target_temperature+amount) + else + target_temperature = max(0, target_temperature+amount) + if(istype(loc, /mob)) + attack_self(loc) + add_fingerprint(usr) + return + +/obj/item/gun/energy/temperature/process() + ..() + var/obj/item/ammo_casing/energy/temp/T = ammo_type[select] + T.temp = temperature + switch(temperature) + if(0 to 100) + T.e_cost = 300 + powercost = "High" + if(100 to 250) + T.e_cost = 200 + powercost = "Medium" + if(251 to 300) + T.e_cost = 100 + powercost = "Low" + if(301 to 400) + T.e_cost = 200 + powercost = "Medium" + if(401 to 1000) + T.e_cost = 300 + powercost = "High" + switch(powercost) + if("High") + powercostcolor = "orange" + if("Medium") + powercostcolor = "green" + else + powercostcolor = "blue" + if(target_temperature != temperature) + var/difference = abs(target_temperature - temperature) + if(difference >= (10 + 40*emagged)) //so emagged temp guns adjust their temperature much more quickly + if(target_temperature < temperature) + temperature -= (10 + 40*emagged) + else + temperature += (10 + 40*emagged) + else + temperature = target_temperature + update_icon() + + if(istype(loc, /mob/living/carbon)) + var /mob/living/carbon/M = loc + if(src == M.machine) + update_dat() + M << browse("Temperature Gun Configuration
    [dat]", "window=tempgun;size=510x102") + return + +/obj/item/gun/energy/temperature/proc/update_dat() + dat = "" + dat += "Current output temperature: " + if(temperature > 500) + dat += "[temperature] ([round(temperature-T0C)]°C)" + dat += " SEARING!" + else if(temperature > (T0C + 50)) + dat += "[temperature] ([round(temperature-T0C)]°C)" + else if(temperature > (T0C - 50)) + dat += "[temperature] ([round(temperature-T0C)]°C)" + else + dat += "[temperature] ([round(temperature-T0C)]°C)" + dat += "
    " + dat += "Target output temperature: " //might be string idiocy, but at least it's easy to read + dat += "- " + dat += "- " + dat += "- " + dat += "[target_temperature] " + dat += "+ " + dat += "+ " + dat += "+" + dat += "
    " + dat += "Power cost: " + dat += "[powercost]" + +/obj/item/gun/energy/temperature/proc/update_temperature() + switch(temperature) + if(501 to INFINITY) + item_state = "tempgun_8" + if(400 to 500) + item_state = "tempgun_7" + if(360 to 400) + item_state = "tempgun_6" + if(335 to 360) + item_state = "tempgun_5" + if(295 to 335) + item_state = "tempgun_4" + if(260 to 295) + item_state = "tempgun_3" + if(200 to 260) + item_state = "tempgun_2" + if(120 to 260) + item_state = "tempgun_1" + if(-INFINITY to 120) + item_state = "tempgun_0" + icon_state = item_state + +/obj/item/gun/energy/temperature/update_icon() + overlays = 0 + update_temperature() + update_user() + update_charge() + +/obj/item/gun/energy/temperature/proc/update_user() + if(istype(loc,/mob/living/carbon)) + var/mob/living/carbon/M = loc + M.update_inv_back() + M.update_inv_l_hand() + M.update_inv_r_hand() + +/obj/item/gun/energy/temperature/proc/update_charge() + var/charge = cell.charge + switch(charge) + if(900 to INFINITY) overlays += "900" + if(800 to 900) overlays += "800" + if(700 to 800) overlays += "700" + if(600 to 700) overlays += "600" + if(500 to 600) overlays += "500" + if(400 to 500) overlays += "400" + if(300 to 400) overlays += "300" + if(200 to 300) overlays += "200" + if(100 to 202) overlays += "100" + if(-INFINITY to 100) overlays += "0" + +// Mimic Gun // +/obj/item/gun/energy/mimicgun + name = "mimic gun" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse. Why does this one have teeth?" + icon_state = "disabler" + ammo_type = list(/obj/item/ammo_casing/energy/mimic) + clumsy_check = 0 //Admin spawn only, might as well let clowns use it. + selfcharge = 1 + ammo_x_offset = 3 + var/mimic_type = /obj/item/gun/projectile/automatic/pistol //Setting this to the mimicgun type does exactly what you think it will. + +/obj/item/gun/energy/mimicgun/newshot() + var/obj/item/ammo_casing/energy/mimic/M = ammo_type[select] + M.mimic_type = mimic_type + ..() diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 5f06a0fa11e3..70bb8be7e464 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -1,60 +1,60 @@ -/obj/item/gun/energy/taser - name = "taser gun" - desc = "A small, low capacity gun used for non-lethal takedowns." - icon_state = "taser" - item_state = null //so the human update icon uses the icon_state instead. - origin_tech = "combat=3" - ammo_type = list(/obj/item/ammo_casing/energy/electrode) - ammo_x_offset = 3 - -/obj/item/gun/energy/taser/mounted - name = "mounted taser gun" - selfcharge = 1 - use_external_power = 1 - -/obj/item/gun/energy/shock_revolver - name = "tesla revolver" - desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers." - icon_state = "stunrevolver" - item_state = "gun" - origin_tech = "combat=4;materials=4;powerstorage=4" - ammo_type = list(/obj/item/ammo_casing/energy/shock_revolver) - can_flashlight = 0 - shaded_charge = 1 - -/obj/item/gun/energy/gun/advtaser - name = "hybrid taser" - desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams." - icon_state = "advtaser" - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler) - origin_tech = "combat=4" - ammo_x_offset = 2 - -/obj/item/gun/energy/gun/advtaser/cyborg - name = "cyborg taser" - desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." - can_flashlight = 0 - can_charge = 0 - -/obj/item/gun/energy/gun/advtaser/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/disabler - name = "disabler" - desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." - icon_state = "disabler" - item_state = null - origin_tech = "combat=3" - ammo_type = list(/obj/item/ammo_casing/energy/disabler) - ammo_x_offset = 3 - -/obj/item/gun/energy/disabler/cyborg - name = "cyborg disabler" - desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." - ammo_type = list(/obj/item/ammo_casing/energy/disabler/cyborg) - can_charge = 0 - -/obj/item/gun/energy/disabler/cyborg/newshot() - ..() - robocharge() +/obj/item/gun/energy/taser + name = "taser gun" + desc = "A small, low capacity gun used for non-lethal takedowns." + icon_state = "taser" + item_state = null //so the human update icon uses the icon_state instead. + origin_tech = "combat=3" + ammo_type = list(/obj/item/ammo_casing/energy/electrode) + ammo_x_offset = 3 + +/obj/item/gun/energy/taser/mounted + name = "mounted taser gun" + selfcharge = 1 + use_external_power = 1 + +/obj/item/gun/energy/shock_revolver + name = "tesla revolver" + desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers." + icon_state = "stunrevolver" + item_state = "gun" + origin_tech = "combat=4;materials=4;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/shock_revolver) + can_flashlight = 0 + shaded_charge = 1 + +/obj/item/gun/energy/gun/advtaser + name = "hybrid taser" + desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams." + icon_state = "advtaser" + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler) + origin_tech = "combat=4" + ammo_x_offset = 2 + +/obj/item/gun/energy/gun/advtaser/cyborg + name = "cyborg taser" + desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." + can_flashlight = 0 + can_charge = 0 + +/obj/item/gun/energy/gun/advtaser/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/disabler + name = "disabler" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." + icon_state = "disabler" + item_state = null + origin_tech = "combat=3" + ammo_type = list(/obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 3 + +/obj/item/gun/energy/disabler/cyborg + name = "cyborg disabler" + desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." + ammo_type = list(/obj/item/ammo_casing/energy/disabler/cyborg) + can_charge = 0 + +/obj/item/gun/energy/disabler/cyborg/newshot() + ..() + robocharge() diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index 544ca67a361c..f91d7e66734b 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -51,6 +51,7 @@ user.visible_message("[user] zaps [user.p_them()]self with [src].") playsound(user, fire_sound, 50, 1) user.create_attack_log("[key_name(user)] zapped [user.p_them()]self with a [src]") + add_attack_logs(null, user, "zapped [user.p_them()]self with a [src]", ATKLOG_ALL) ///////////////////////////////////// //WAND OF DEATH diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm index 89cceab6cd3d..6b29a203c02f 100644 --- a/code/modules/projectiles/guns/misc/blastcannon.dm +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -127,4 +127,4 @@ lightr = max(lightr - 1, 0) /obj/item/projectile/blastwave/ex_act() - return \ No newline at end of file + return diff --git a/code/modules/projectiles/guns/mounted.dm b/code/modules/projectiles/guns/mounted.dm index d024c596fe1e..162cc541fd28 100644 --- a/code/modules/projectiles/guns/mounted.dm +++ b/code/modules/projectiles/guns/mounted.dm @@ -17,4 +17,4 @@ item_state = "armcannonlase" force = 5 selfcharge = 1 - trigger_guard = TRIGGER_GUARD_ALLOW_ALL \ No newline at end of file + trigger_guard = TRIGGER_GUARD_ALLOW_ALL diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 133331f8e44b..25db1f10dcbc 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -1,214 +1,214 @@ -/obj/item/gun/projectile - desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason" - name = "projectile gun" - icon_state = "pistol" - origin_tech = "combat=2;materials=2" - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=1000) - - var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info - var/obj/item/ammo_box/magazine/magazine - var/can_tactical = FALSE //check to see if the gun can tactically reload - -/obj/item/gun/projectile/New() - ..() - if(!magazine) - magazine = new mag_type(src) - chamber_round() - update_icon() - return - -/obj/item/gun/projectile/update_icon() - ..() - if(current_skin) - icon_state = "[current_skin][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" - else - icon_state = "[initial(icon_state)][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" - if(bayonet && can_bayonet) - overlays += knife_overlay - -/obj/item/gun/projectile/process_chamber(eject_casing = 1, empty_chamber = 1) - var/obj/item/ammo_casing/AC = chambered //Find chambered round - if(isnull(AC) || !istype(AC)) - chamber_round() - return - if(eject_casing) - AC.loc = get_turf(src) //Eject casing onto ground. - AC.SpinAnimation(10, 1) //next gen special effects - playsound(src, chambered.drop_sound, 100, 1) - if(empty_chamber) - chambered = null - chamber_round() - return - -/obj/item/gun/projectile/proc/chamber_round() - if(chambered || !magazine) - return - else if(magazine.ammo_count()) - chambered = magazine.get_round() - chambered.loc = src - return - -/obj/item/gun/projectile/can_shoot() - if(!magazine || !magazine.ammo_count(0)) - return 0 - return 1 - -/obj/item/gun/projectile/proc/can_reload() - return !magazine - -/obj/item/gun/projectile/proc/reload(obj/item/ammo_box/magazine/AM, mob/user as mob) - user.remove_from_mob(AM) - magazine = AM - magazine.loc = src - playsound(src, magin_sound, 50, 1) - chamber_round() - AM.update_icon() - update_icon() - return - -/obj/item/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob, params) - if(istype(A, /obj/item/ammo_box/magazine)) - var/obj/item/ammo_box/magazine/AM = A - if(istype(AM, mag_type)) - if(can_reload()) - reload(AM, user) - to_chat(user, "You load a new magazine into \the [src].") - return TRUE - else if(!can_tactical) - to_chat(user, "There's already a magazine in \the [src].") - return TRUE - else - to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") - magazine.loc = get_turf(loc) - magazine.update_icon() - magazine = null - reload(AM, user) - return TRUE - else - to_chat(user, "You can't put this type of ammo in \the [src].") - return TRUE - if(istype(A, /obj/item/suppressor)) - var/obj/item/suppressor/S = A - if(can_suppress) - if(!suppressed) - if(!user.unEquip(A)) - return - to_chat(user, "You screw [S] onto [src].") - suppressed = A - S.oldsound = fire_sound - S.initial_w_class = w_class - fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' - w_class = WEIGHT_CLASS_NORMAL //so pistols do not fit in pockets when suppressed - A.loc = src - update_icon() - return - else - to_chat(user, "[src] already has a suppressor.") - return - else - to_chat(user, "You can't seem to figure out how to fit [S] on [src].") - return - else - return ..() - -/obj/item/gun/projectile/attack_hand(mob/user) - if(loc == user) - if(suppressed && can_unsuppress) - var/obj/item/suppressor/S = suppressed - if(user.l_hand != src && user.r_hand != src) - ..() - return - to_chat(user, "You unscrew [suppressed] from [src].") - user.put_in_hands(suppressed) - fire_sound = S.oldsound - w_class = S.initial_w_class - suppressed = 0 - update_icon() - return - ..() - -/obj/item/gun/projectile/attack_self(mob/living/user as mob) - var/obj/item/ammo_casing/AC = chambered //Find chambered round - if(magazine) - magazine.loc = get_turf(loc) - user.put_in_hands(magazine) - magazine.update_icon() - magazine = null - to_chat(user, "You pull the magazine out of \the [src]!") - playsound(src, magout_sound, 50, 1) - else if(chambered) - AC.loc = get_turf(src) - AC.SpinAnimation(10, 1) - chambered = null - to_chat(user, "You unload the round from \the [src]'s chamber.") - playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) - else - to_chat(user, "There's no magazine in \the [src].") - update_icon() - return - -/obj/item/gun/projectile/examine(mob/user) - . = ..() - . += "Has [get_ammo()] round\s remaining." - -/obj/item/gun/projectile/proc/get_ammo(countchambered = 1) - var/boolets = 0 //mature var names for mature people - if(chambered && countchambered) - boolets++ - if(magazine) - boolets += magazine.ammo_count() - return boolets - -/obj/item/gun/projectile/suicide_act(mob/user) - if(chambered && chambered.BB && !chambered.BB.nodamage) - user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") - sleep(25) - if(user.l_hand == src || user.r_hand == src) - process_fire(user, user, 0, zone_override = "head") - user.visible_message("[user] blows [user.p_their()] brains out with the [name]!") - return BRUTELOSS - else - user.visible_message("[user] panics and starts choking to death!") - return OXYLOSS - else - user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) - return OXYLOSS - -/obj/item/gun/projectile/proc/sawoff(mob/user) - if(sawn_state == SAWN_OFF) - to_chat(user, "\The [src] is already shortened!") - return - if(bayonet) - to_chat(user, "You cannot saw-off [src] with [bayonet] attached!") - return - user.changeNext_move(CLICK_CD_MELEE) - user.visible_message("[user] begins to shorten \the [src].", "You begin to shorten \the [src]...") - - //if there's any live ammo inside the gun, makes it go off - if(blow_up(user)) - user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") - return - - if(do_after(user, 30, target = src)) - if(sawn_state == SAWN_OFF) - return - user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") - name = "sawn-off [name]" - desc = sawn_desc - w_class = WEIGHT_CLASS_NORMAL - item_state = "gun"//phil235 is it different with different skin? - slot_flags &= ~SLOT_BACK //you can't sling it on your back - slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) - sawn_state = SAWN_OFF - update_icon() - return 1 - -// Sawing guns related proc -/obj/item/gun/projectile/proc/blow_up(mob/user) - . = 0 - for(var/obj/item/ammo_casing/AC in magazine.stored_ammo) - if(AC.BB) - process_fire(user, user,0) - . = 1 \ No newline at end of file +/obj/item/gun/projectile + desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason" + name = "projectile gun" + icon_state = "pistol" + origin_tech = "combat=2;materials=2" + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=1000) + + var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info + var/obj/item/ammo_box/magazine/magazine + var/can_tactical = FALSE //check to see if the gun can tactically reload + +/obj/item/gun/projectile/New() + ..() + if(!magazine) + magazine = new mag_type(src) + chamber_round() + update_icon() + return + +/obj/item/gun/projectile/update_icon() + ..() + if(current_skin) + icon_state = "[current_skin][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" + else + icon_state = "[initial(icon_state)][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" + if(bayonet && can_bayonet) + overlays += knife_overlay + +/obj/item/gun/projectile/process_chamber(eject_casing = 1, empty_chamber = 1) + var/obj/item/ammo_casing/AC = chambered //Find chambered round + if(isnull(AC) || !istype(AC)) + chamber_round() + return + if(eject_casing) + AC.loc = get_turf(src) //Eject casing onto ground. + AC.SpinAnimation(10, 1) //next gen special effects + playsound(src, chambered.drop_sound, 100, 1) + if(empty_chamber) + chambered = null + chamber_round() + return + +/obj/item/gun/projectile/proc/chamber_round() + if(chambered || !magazine) + return + else if(magazine.ammo_count()) + chambered = magazine.get_round() + chambered.loc = src + return + +/obj/item/gun/projectile/can_shoot() + if(!magazine || !magazine.ammo_count(0)) + return 0 + return 1 + +/obj/item/gun/projectile/proc/can_reload() + return !magazine + +/obj/item/gun/projectile/proc/reload(obj/item/ammo_box/magazine/AM, mob/user as mob) + user.remove_from_mob(AM) + magazine = AM + magazine.loc = src + playsound(src, magin_sound, 50, 1) + chamber_round() + AM.update_icon() + update_icon() + return + +/obj/item/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob, params) + if(istype(A, /obj/item/ammo_box/magazine)) + var/obj/item/ammo_box/magazine/AM = A + if(istype(AM, mag_type)) + if(can_reload()) + reload(AM, user) + to_chat(user, "You load a new magazine into \the [src].") + return TRUE + else if(!can_tactical) + to_chat(user, "There's already a magazine in \the [src].") + return TRUE + else + to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") + magazine.loc = get_turf(loc) + magazine.update_icon() + magazine = null + reload(AM, user) + return TRUE + else + to_chat(user, "You can't put this type of ammo in \the [src].") + return TRUE + if(istype(A, /obj/item/suppressor)) + var/obj/item/suppressor/S = A + if(can_suppress) + if(!suppressed) + if(!user.unEquip(A)) + return + to_chat(user, "You screw [S] onto [src].") + suppressed = A + S.oldsound = fire_sound + S.initial_w_class = w_class + fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' + w_class = WEIGHT_CLASS_NORMAL //so pistols do not fit in pockets when suppressed + A.loc = src + update_icon() + return + else + to_chat(user, "[src] already has a suppressor.") + return + else + to_chat(user, "You can't seem to figure out how to fit [S] on [src].") + return + else + return ..() + +/obj/item/gun/projectile/attack_hand(mob/user) + if(loc == user) + if(suppressed && can_unsuppress) + var/obj/item/suppressor/S = suppressed + if(user.l_hand != src && user.r_hand != src) + ..() + return + to_chat(user, "You unscrew [suppressed] from [src].") + user.put_in_hands(suppressed) + fire_sound = S.oldsound + w_class = S.initial_w_class + suppressed = 0 + update_icon() + return + ..() + +/obj/item/gun/projectile/attack_self(mob/living/user as mob) + var/obj/item/ammo_casing/AC = chambered //Find chambered round + if(magazine) + magazine.loc = get_turf(loc) + user.put_in_hands(magazine) + magazine.update_icon() + magazine = null + to_chat(user, "You pull the magazine out of \the [src]!") + playsound(src, magout_sound, 50, 1) + else if(chambered) + AC.loc = get_turf(src) + AC.SpinAnimation(10, 1) + chambered = null + to_chat(user, "You unload the round from \the [src]'s chamber.") + playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) + else + to_chat(user, "There's no magazine in \the [src].") + update_icon() + return + +/obj/item/gun/projectile/examine(mob/user) + . = ..() + . += "Has [get_ammo()] round\s remaining." + +/obj/item/gun/projectile/proc/get_ammo(countchambered = 1) + var/boolets = 0 //mature var names for mature people + if(chambered && countchambered) + boolets++ + if(magazine) + boolets += magazine.ammo_count() + return boolets + +/obj/item/gun/projectile/suicide_act(mob/user) + if(chambered && chambered.BB && !chambered.BB.nodamage) + user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") + sleep(25) + if(user.l_hand == src || user.r_hand == src) + process_fire(user, user, 0, zone_override = "head") + user.visible_message("[user] blows [user.p_their()] brains out with the [name]!") + return BRUTELOSS + else + user.visible_message("[user] panics and starts choking to death!") + return OXYLOSS + else + user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) + return OXYLOSS + +/obj/item/gun/projectile/proc/sawoff(mob/user) + if(sawn_state == SAWN_OFF) + to_chat(user, "\The [src] is already shortened!") + return + if(bayonet) + to_chat(user, "You cannot saw-off [src] with [bayonet] attached!") + return + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message("[user] begins to shorten \the [src].", "You begin to shorten \the [src]...") + + //if there's any live ammo inside the gun, makes it go off + if(blow_up(user)) + user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") + return + + if(do_after(user, 30, target = src)) + if(sawn_state == SAWN_OFF) + return + user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") + name = "sawn-off [name]" + desc = sawn_desc + w_class = WEIGHT_CLASS_NORMAL + item_state = "gun"//phil235 is it different with different skin? + slot_flags &= ~SLOT_BACK //you can't sling it on your back + slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) + sawn_state = SAWN_OFF + update_icon() + return 1 + +// Sawing guns related proc +/obj/item/gun/projectile/proc/blow_up(mob/user) + . = 0 + for(var/obj/item/ammo_casing/AC in magazine.stored_ammo) + if(AC.BB) + process_fire(user, user,0) + . = 1 diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 6601f75d52bc..f2ce54bbd52d 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -1,306 +1,306 @@ -/obj/item/gun/projectile/automatic - w_class = WEIGHT_CLASS_NORMAL - var/alarmed = 0 - var/select = 1 - can_tactical = TRUE - can_suppress = 1 - burst_size = 3 - fire_delay = 2 - actions_types = list(/datum/action/item_action/toggle_firemode) - -/obj/item/gun/projectile/automatic/isHandgun() - return 0 - -/obj/item/gun/projectile/automatic/update_icon() - ..() - overlays.Cut() - if(!select) - overlays += "[initial(icon_state)]semi" - if(select == 1) - overlays += "[initial(icon_state)]burst" - icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - if(bayonet && can_bayonet) - overlays += knife_overlay - -/obj/item/gun/projectile/automatic/attackby(var/obj/item/A as obj, mob/user as mob, params) - . = ..() - if(.) - if(alarmed) // Did the empty clip alarm go off already? - alarmed = 0 // Reset the alarm once a magazine is loaded - return - if(istype(A, /obj/item/ammo_box/magazine)) - var/obj/item/ammo_box/magazine/AM = A - if(istype(AM, mag_type)) - if(magazine) - to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") - magazine.loc = get_turf(loc) - magazine.update_icon() - magazine = null - else - to_chat(user, "You insert the magazine into \the [src].") - if(alarmed) - alarmed = 0 - user.remove_from_mob(AM) - magazine = AM - magazine.loc = src - chamber_round() - A.update_icon() - update_icon() - return 1 - -/obj/item/gun/projectile/automatic/ui_action_click() - burst_select() - -/obj/item/gun/projectile/automatic/proc/burst_select() - var/mob/living/carbon/human/user = usr - select = !select - if(!select) - burst_size = 1 - fire_delay = 0 - to_chat(user, "You switch to semi-automatic.") - else - burst_size = initial(burst_size) - fire_delay = initial(fire_delay) - to_chat(user, "You switch to [burst_size] round burst.") - - playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) - update_icon() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/gun/projectile/automatic/can_shoot() - return get_ammo() - -/obj/item/gun/projectile/automatic/proc/empty_alarm() - if(!chambered && !get_ammo() && !alarmed) - playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) - update_icon() - alarmed = 1 - -//Saber SMG// -/obj/item/gun/projectile/automatic/proto - name = "\improper Nanotrasen Saber SMG" - desc = "A prototype three-round burst 9mm submachine gun, designated 'SABR'. Has a threaded barrel for suppressors." - icon_state = "saber" - mag_type = /obj/item/ammo_box/magazine/smgm9mm - origin_tech = "combat=4;materials=2" - fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' - -//C-20r SMG// -/obj/item/gun/projectile/automatic/c20r - name = "\improper C-20r SMG" - desc = "A two-round burst .45 SMG, designated 'C-20r'. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp." - icon_state = "c20r" - item_state = "c20r" - origin_tech = "combat=5;materials=2;syndicate=6" - mag_type = /obj/item/ammo_box/magazine/smgm45 - fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' - fire_delay = 2 - burst_size = 2 - can_bayonet = TRUE - knife_x_offset = 26 - knife_y_offset = 12 - -/obj/item/gun/projectile/automatic/c20r/New() - ..() - update_icon() - -/obj/item/gun/projectile/automatic/c20r/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) - ..() - empty_alarm() - -/obj/item/gun/projectile/automatic/c20r/update_icon() - ..() - icon_state = "c20r[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - -//WT550// -/obj/item/gun/projectile/automatic/wt550 - name = "security auto rifle" - desc = "An outdated personal defense weapon utilized by law enforcement. The WT-550 Automatic Rifle fires 4.6x30mm rounds." - icon_state = "wt550" - item_state = "arg" - mag_type = /obj/item/ammo_box/magazine/wt550m9 - fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - fire_delay = 2 - can_suppress = 0 - burst_size = 1 - actions_types = list() - can_bayonet = TRUE - knife_x_offset = 25 - knife_y_offset = 12 - -/obj/item/gun/projectile/automatic/wt550/update_icon() - ..() - icon_state = "wt550[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""]" - -//Type-U3 Uzi// -/obj/item/gun/projectile/automatic/mini_uzi - name = "\improper 'Type U3' Uzi" - desc = "A lightweight, burst-fire submachine gun, for when you really want someone dead. Uses 9mm rounds." - icon_state = "mini-uzi" - origin_tech = "combat=4;materials=2;syndicate=4" - mag_type = /obj/item/ammo_box/magazine/uzim9mm - fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' - burst_size = 2 - -//M-90gl Carbine// -/obj/item/gun/projectile/automatic/m90 - name = "\improper M-90gl Carbine" - desc = "A three-round burst 5.56 toploading carbine, designated 'M-90gl'. Has an attached underbarrel grenade launcher which can be toggled on and off." - icon_state = "m90" - item_state = "m90-4" - origin_tech = "combat=5;materials=2;syndicate=6" - mag_type = /obj/item/ammo_box/magazine/m556 - fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - var/obj/item/gun/projectile/revolver/grenadelauncher/underbarrel - burst_size = 3 - fire_delay = 2 - -/obj/item/gun/projectile/automatic/m90/New() - ..() - underbarrel = new /obj/item/gun/projectile/revolver/grenadelauncher(src) - update_icon() - -/obj/item/gun/projectile/automatic/m90/afterattack(var/atom/target, var/mob/living/user, flag, params) - if(select == 2) - underbarrel.afterattack(target, user, flag, params) - else - ..() - return - -/obj/item/gun/projectile/automatic/m90/attackby(var/obj/item/A, mob/user, params) - if(istype(A, /obj/item/ammo_casing)) - if(istype(A, underbarrel.magazine.ammo_type)) - underbarrel.attack_self() - underbarrel.attackby(A, user, params) - else - return ..() - -/obj/item/gun/projectile/automatic/m90/update_icon() - ..() - overlays.Cut() - switch(select) - if(0) - overlays += "[initial(icon_state)]semi" - if(1) - overlays += "[initial(icon_state)]burst" - if(2) - overlays += "[initial(icon_state)]gren" - icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" - if(magazine) - overlays += image(icon = icon, icon_state = "m90-[Ceiling(get_ammo(0)/6)*6]") - item_state = "m90-[Ceiling(get_ammo(0)/7.5)]" - else - item_state = "m90-0" - return - -/obj/item/gun/projectile/automatic/m90/burst_select() - var/mob/living/carbon/human/user = usr - switch(select) - if(0) - select = 1 - burst_size = initial(burst_size) - fire_delay = initial(fire_delay) - to_chat(user, "You switch to [burst_size] round burst.") - if(1) - select = 2 - to_chat(user, "You switch to grenades.") - if(2) - select = 0 - burst_size = 1 - fire_delay = 0 - to_chat(user, "You switch to semi-auto.") - playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) - update_icon() - -//Tommy Gun// -/obj/item/gun/projectile/automatic/tommygun - name = "\improper Thompson SMG" - desc = "A genuine 'Chicago Typewriter'." - icon_state = "tommygun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_HUGE - slot_flags = 0 - origin_tech = "combat=5;materials=1;syndicate=3" - mag_type = /obj/item/ammo_box/magazine/tommygunm45 - fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' - can_suppress = 0 - burst_size = 4 - fire_delay = 1 - -//ARG Assault Rifle// -/obj/item/gun/projectile/automatic/ar - name = "ARG" - desc = "A robust assault rile used by Nanotrasen fighting forces." - icon_state = "arg" - item_state = "arg" - slot_flags = 0 - origin_tech = "combat=6;engineering=4" - mag_type = /obj/item/ammo_box/magazine/m556 - fire_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - burst_size = 3 - fire_delay = 1 - -// Bulldog shotgun // -/obj/item/gun/projectile/automatic/shotgun/bulldog - name = "\improper 'Bulldog' Shotgun" - desc = "A compact, mag-fed semi-automatic shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines." - icon_state = "bulldog" - item_state = "bulldog" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=6;materials=4;syndicate=6" - mag_type = /obj/item/ammo_box/magazine/m12g - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - burst_size = 1 - fire_delay = 0 - actions_types = list() - -/obj/item/gun/projectile/automatic/shotgun/bulldog/New() - ..() - update_icon() - -/obj/item/gun/projectile/automatic/shotgun/bulldog/proc/update_magazine() - if(magazine) - overlays.Cut() - overlays += "[magazine.icon_state]" - return - -/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon() - overlays.Cut() - update_magazine() - icon_state = "bulldog[chambered ? "" : "-e"]" - -/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) - ..() - empty_alarm() - -//Laser carbine// -/obj/item/gun/projectile/automatic/lasercarbine - name = "\improper IK-60 Laser Carbine" - desc = "A short, compact carbine like rifle, relying more on battery cartridges rather than a built in power cell. Utilized by the Nanotrasen Navy for combat operations." - icon_state = "lasercarbine" - item_state = "laser" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=4;materials=2" - mag_type = /obj/item/ammo_box/magazine/laser - fire_sound = 'sound/weapons/gunshots/gunshot_lascarbine.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - burst_size = 2 - -/obj/item/gun/projectile/automatic/lasercarbine/update_icon() - ..() - icon_state = "lasercarbine[magazine ? "-[Ceiling(get_ammo(0)/5)*5]" : ""]" +/obj/item/gun/projectile/automatic + w_class = WEIGHT_CLASS_NORMAL + var/alarmed = 0 + var/select = 1 + can_tactical = TRUE + can_suppress = 1 + burst_size = 3 + fire_delay = 2 + actions_types = list(/datum/action/item_action/toggle_firemode) + +/obj/item/gun/projectile/automatic/isHandgun() + return 0 + +/obj/item/gun/projectile/automatic/update_icon() + ..() + overlays.Cut() + if(!select) + overlays += "[initial(icon_state)]semi" + if(select == 1) + overlays += "[initial(icon_state)]burst" + icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + if(bayonet && can_bayonet) + overlays += knife_overlay + +/obj/item/gun/projectile/automatic/attackby(var/obj/item/A as obj, mob/user as mob, params) + . = ..() + if(.) + if(alarmed) // Did the empty clip alarm go off already? + alarmed = 0 // Reset the alarm once a magazine is loaded + return + if(istype(A, /obj/item/ammo_box/magazine)) + var/obj/item/ammo_box/magazine/AM = A + if(istype(AM, mag_type)) + if(magazine) + to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") + magazine.loc = get_turf(loc) + magazine.update_icon() + magazine = null + else + to_chat(user, "You insert the magazine into \the [src].") + if(alarmed) + alarmed = 0 + user.remove_from_mob(AM) + magazine = AM + magazine.loc = src + chamber_round() + A.update_icon() + update_icon() + return 1 + +/obj/item/gun/projectile/automatic/ui_action_click() + burst_select() + +/obj/item/gun/projectile/automatic/proc/burst_select() + var/mob/living/carbon/human/user = usr + select = !select + if(!select) + burst_size = 1 + fire_delay = 0 + to_chat(user, "You switch to semi-automatic.") + else + burst_size = initial(burst_size) + fire_delay = initial(fire_delay) + to_chat(user, "You switch to [burst_size] round burst.") + + playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) + update_icon() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/gun/projectile/automatic/can_shoot() + return get_ammo() + +/obj/item/gun/projectile/automatic/proc/empty_alarm() + if(!chambered && !get_ammo() && !alarmed) + playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) + update_icon() + alarmed = 1 + +//Saber SMG// +/obj/item/gun/projectile/automatic/proto + name = "\improper Nanotrasen Saber SMG" + desc = "A prototype three-round burst 9mm submachine gun, designated 'SABR'. Has a threaded barrel for suppressors." + icon_state = "saber" + mag_type = /obj/item/ammo_box/magazine/smgm9mm + origin_tech = "combat=4;materials=2" + fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' + +//C-20r SMG// +/obj/item/gun/projectile/automatic/c20r + name = "\improper C-20r SMG" + desc = "A two-round burst .45 SMG, designated 'C-20r'. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp." + icon_state = "c20r" + item_state = "c20r" + origin_tech = "combat=5;materials=2;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/smgm45 + fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' + fire_delay = 2 + burst_size = 2 + can_bayonet = TRUE + knife_x_offset = 26 + knife_y_offset = 12 + +/obj/item/gun/projectile/automatic/c20r/New() + ..() + update_icon() + +/obj/item/gun/projectile/automatic/c20r/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) + ..() + empty_alarm() + +/obj/item/gun/projectile/automatic/c20r/update_icon() + ..() + icon_state = "c20r[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + +//WT550// +/obj/item/gun/projectile/automatic/wt550 + name = "security auto rifle" + desc = "An outdated personal defense weapon utilized by law enforcement. The WT-550 Automatic Rifle fires 4.6x30mm rounds." + icon_state = "wt550" + item_state = "arg" + mag_type = /obj/item/ammo_box/magazine/wt550m9 + fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + fire_delay = 2 + can_suppress = 0 + burst_size = 1 + actions_types = list() + can_bayonet = TRUE + knife_x_offset = 25 + knife_y_offset = 12 + +/obj/item/gun/projectile/automatic/wt550/update_icon() + ..() + icon_state = "wt550[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""]" + +//Type-U3 Uzi// +/obj/item/gun/projectile/automatic/mini_uzi + name = "\improper 'Type U3' Uzi" + desc = "A lightweight, burst-fire submachine gun, for when you really want someone dead. Uses 9mm rounds." + icon_state = "mini-uzi" + origin_tech = "combat=4;materials=2;syndicate=4" + mag_type = /obj/item/ammo_box/magazine/uzim9mm + fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' + burst_size = 2 + +//M-90gl Carbine// +/obj/item/gun/projectile/automatic/m90 + name = "\improper M-90gl Carbine" + desc = "A three-round burst 5.56 toploading carbine, designated 'M-90gl'. Has an attached underbarrel grenade launcher which can be toggled on and off." + icon_state = "m90" + item_state = "m90-4" + origin_tech = "combat=5;materials=2;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/m556 + fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + var/obj/item/gun/projectile/revolver/grenadelauncher/underbarrel + burst_size = 3 + fire_delay = 2 + +/obj/item/gun/projectile/automatic/m90/New() + ..() + underbarrel = new /obj/item/gun/projectile/revolver/grenadelauncher(src) + update_icon() + +/obj/item/gun/projectile/automatic/m90/afterattack(var/atom/target, var/mob/living/user, flag, params) + if(select == 2) + underbarrel.afterattack(target, user, flag, params) + else + ..() + return + +/obj/item/gun/projectile/automatic/m90/attackby(var/obj/item/A, mob/user, params) + if(istype(A, /obj/item/ammo_casing)) + if(istype(A, underbarrel.magazine.ammo_type)) + underbarrel.attack_self() + underbarrel.attackby(A, user, params) + else + return ..() + +/obj/item/gun/projectile/automatic/m90/update_icon() + ..() + overlays.Cut() + switch(select) + if(0) + overlays += "[initial(icon_state)]semi" + if(1) + overlays += "[initial(icon_state)]burst" + if(2) + overlays += "[initial(icon_state)]gren" + icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" + if(magazine) + overlays += image(icon = icon, icon_state = "m90-[Ceiling(get_ammo(0)/6)*6]") + item_state = "m90-[Ceiling(get_ammo(0)/7.5)]" + else + item_state = "m90-0" + return + +/obj/item/gun/projectile/automatic/m90/burst_select() + var/mob/living/carbon/human/user = usr + switch(select) + if(0) + select = 1 + burst_size = initial(burst_size) + fire_delay = initial(fire_delay) + to_chat(user, "You switch to [burst_size] round burst.") + if(1) + select = 2 + to_chat(user, "You switch to grenades.") + if(2) + select = 0 + burst_size = 1 + fire_delay = 0 + to_chat(user, "You switch to semi-auto.") + playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) + update_icon() + +//Tommy Gun// +/obj/item/gun/projectile/automatic/tommygun + name = "\improper Thompson SMG" + desc = "A genuine 'Chicago Typewriter'." + icon_state = "tommygun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_HUGE + slot_flags = 0 + origin_tech = "combat=5;materials=1;syndicate=3" + mag_type = /obj/item/ammo_box/magazine/tommygunm45 + fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' + can_suppress = 0 + burst_size = 4 + fire_delay = 1 + +//ARG Assault Rifle// +/obj/item/gun/projectile/automatic/ar + name = "ARG" + desc = "A robust assault rile used by Nanotrasen fighting forces." + icon_state = "arg" + item_state = "arg" + slot_flags = 0 + origin_tech = "combat=6;engineering=4" + mag_type = /obj/item/ammo_box/magazine/m556 + fire_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + burst_size = 3 + fire_delay = 1 + +// Bulldog shotgun // +/obj/item/gun/projectile/automatic/shotgun/bulldog + name = "\improper 'Bulldog' Shotgun" + desc = "A compact, mag-fed semi-automatic shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines." + icon_state = "bulldog" + item_state = "bulldog" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=6;materials=4;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/m12g + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + burst_size = 1 + fire_delay = 0 + actions_types = list() + +/obj/item/gun/projectile/automatic/shotgun/bulldog/New() + ..() + update_icon() + +/obj/item/gun/projectile/automatic/shotgun/bulldog/proc/update_magazine() + if(magazine) + overlays.Cut() + overlays += "[magazine.icon_state]" + return + +/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon() + overlays.Cut() + update_magazine() + icon_state = "bulldog[chambered ? "" : "-e"]" + +/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) + ..() + empty_alarm() + +//Laser carbine// +/obj/item/gun/projectile/automatic/lasercarbine + name = "\improper IK-60 Laser Carbine" + desc = "A short, compact carbine like rifle, relying more on battery cartridges rather than a built in power cell. Utilized by the Nanotrasen Navy for combat operations." + icon_state = "lasercarbine" + item_state = "laser" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/laser + fire_sound = 'sound/weapons/gunshots/gunshot_lascarbine.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + burst_size = 2 + +/obj/item/gun/projectile/automatic/lasercarbine/update_icon() + ..() + icon_state = "lasercarbine[magazine ? "-[Ceiling(get_ammo(0)/5)*5]" : ""]" diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 64d0c2d7a67b..1314410cd365 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -1,118 +1,118 @@ -//Stetchkin// -/obj/item/gun/projectile/automatic/pistol - name = "stechkin pistol" - desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors." - icon_state = "pistol" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "combat=3;materials=2;syndicate=4" - mag_type = /obj/item/ammo_box/magazine/m10mm - fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' - magin_sound = 'sound/weapons/gun_interactions/pistol_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/pistol_magout.ogg' - can_suppress = 1 - burst_size = 1 - fire_delay = 0 - actions_types = list() - -/obj/item/gun/projectile/automatic/pistol/isHandgun() - return 1 - -/obj/item/gun/projectile/automatic/pistol/update_icon() - ..() - icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - return - -//M1911// -/obj/item/gun/projectile/automatic/pistol/m1911 - name = "\improper M1911" - desc = "A classic .45 handgun with a small magazine capacity." - icon_state = "m1911" - w_class = WEIGHT_CLASS_NORMAL - mag_type = /obj/item/ammo_box/magazine/m45 - can_suppress = 0 - -//Enforcer// -/obj/item/gun/projectile/automatic/pistol/enforcer - name = "Enforcer" - desc = "A pistol of modern design." - icon_state = "enforcer_grey" - force = 10 - mag_type = /obj/item/ammo_box/magazine/enforcer - can_suppress = TRUE - unique_reskin = TRUE - can_flashlight = TRUE - -/obj/item/gun/projectile/automatic/pistol/enforcer/New() - ..() - options["Grey slide"] = "enforcer_grey" - options["Red slide"] = "enforcer_red" - options["Green slide"] = "enforcer_green" - options["Tan slide"] = "enforcer_tan" - options["Black slide"] = "enforcer_black" - options["Green Handle"] = "enforcer_greengrip" - options["Tan Handle"] = "enforcer_tangrip" - options["Red Handle"] = "enforcer_redgrip" - options["Cancel"] = null - -/obj/item/gun/projectile/automatic/pistol/enforcer/update_icon() - ..() - if(current_skin) - icon_state = "[current_skin][chambered ? "" : "-e"]" - else - icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" - overlays.Cut() - if(suppressed) - overlays += image(icon = icon, icon_state = "enforcer_supp", pixel_x = 4) - if(gun_light) - var/iconF = "Enforcer_light" - if(gun_light.on) - iconF = "Enforcer_light-on" - overlays += image(icon = icon, icon_state = iconF, pixel_x = 0) - -/obj/item/gun/projectile/automatic/pistol/enforcer/ui_action_click() - toggle_gunlight() - -/obj/item/gun/projectile/automatic/pistol/enforcer/lethal - -/obj/item/gun/projectile/automatic/pistol/enforcer/lethal/New() - magazine = new/obj/item/ammo_box/magazine/enforcer/lethal - ..() - -//Desert Eagle// -/obj/item/gun/projectile/automatic/pistol/deagle - name = "desert eagle" - desc = "A robust .50 AE handgun." - icon_state = "deagle" - force = 14.0 - mag_type = /obj/item/ammo_box/magazine/m50 - fire_sound = 'sound/weapons/gunshots/gunshot_pistolH.ogg' - magin_sound = 'sound/weapons/gun_interactions/hpistol_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/hpistol_magout.ogg' - can_suppress = 0 - -/obj/item/gun/projectile/automatic/pistol/deagle/update_icon() - ..() - icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" - -/obj/item/gun/projectile/automatic/pistol/deagle/gold - desc = "A gold plated desert eagle folded over a million times by superior martian gunsmiths. Uses .50 AE ammo." - icon_state = "deagleg" - item_state = "deagleg" - -/obj/item/gun/projectile/automatic/pistol/deagle/camo - desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE ammo." - icon_state = "deaglecamo" - item_state = "deagleg" - -//APS Pistol// -/obj/item/gun/projectile/automatic/pistol/APS - name = "stechkin APS pistol" - desc = "The original russian version of a widely used Syndicate sidearm. Uses 9mm ammo." - icon_state = "aps" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=3;materials=2;syndicate=3" - mag_type = /obj/item/ammo_box/magazine/pistolm9mm - can_suppress = 0 - burst_size = 3 - fire_delay = 2 - actions_types = list(/datum/action/item_action/toggle_firemode) +//Stetchkin// +/obj/item/gun/projectile/automatic/pistol + name = "stechkin pistol" + desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors." + icon_state = "pistol" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "combat=3;materials=2;syndicate=4" + mag_type = /obj/item/ammo_box/magazine/m10mm + fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' + magin_sound = 'sound/weapons/gun_interactions/pistol_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/pistol_magout.ogg' + can_suppress = 1 + burst_size = 1 + fire_delay = 0 + actions_types = list() + +/obj/item/gun/projectile/automatic/pistol/isHandgun() + return 1 + +/obj/item/gun/projectile/automatic/pistol/update_icon() + ..() + icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + return + +//M1911// +/obj/item/gun/projectile/automatic/pistol/m1911 + name = "\improper M1911" + desc = "A classic .45 handgun with a small magazine capacity." + icon_state = "m1911" + w_class = WEIGHT_CLASS_NORMAL + mag_type = /obj/item/ammo_box/magazine/m45 + can_suppress = 0 + +//Enforcer// +/obj/item/gun/projectile/automatic/pistol/enforcer + name = "Enforcer" + desc = "A pistol of modern design." + icon_state = "enforcer_grey" + force = 10 + mag_type = /obj/item/ammo_box/magazine/enforcer + can_suppress = TRUE + unique_reskin = TRUE + can_flashlight = TRUE + +/obj/item/gun/projectile/automatic/pistol/enforcer/New() + ..() + options["Grey slide"] = "enforcer_grey" + options["Red slide"] = "enforcer_red" + options["Green slide"] = "enforcer_green" + options["Tan slide"] = "enforcer_tan" + options["Black slide"] = "enforcer_black" + options["Green Handle"] = "enforcer_greengrip" + options["Tan Handle"] = "enforcer_tangrip" + options["Red Handle"] = "enforcer_redgrip" + options["Cancel"] = null + +/obj/item/gun/projectile/automatic/pistol/enforcer/update_icon() + ..() + if(current_skin) + icon_state = "[current_skin][chambered ? "" : "-e"]" + else + icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" + overlays.Cut() + if(suppressed) + overlays += image(icon = icon, icon_state = "enforcer_supp", pixel_x = 4) + if(gun_light) + var/iconF = "Enforcer_light" + if(gun_light.on) + iconF = "Enforcer_light-on" + overlays += image(icon = icon, icon_state = iconF, pixel_x = 0) + +/obj/item/gun/projectile/automatic/pistol/enforcer/ui_action_click() + toggle_gunlight() + +/obj/item/gun/projectile/automatic/pistol/enforcer/lethal + +/obj/item/gun/projectile/automatic/pistol/enforcer/lethal/New() + magazine = new/obj/item/ammo_box/magazine/enforcer/lethal + ..() + +//Desert Eagle// +/obj/item/gun/projectile/automatic/pistol/deagle + name = "desert eagle" + desc = "A robust .50 AE handgun." + icon_state = "deagle" + force = 14.0 + mag_type = /obj/item/ammo_box/magazine/m50 + fire_sound = 'sound/weapons/gunshots/gunshot_pistolH.ogg' + magin_sound = 'sound/weapons/gun_interactions/hpistol_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/hpistol_magout.ogg' + can_suppress = 0 + +/obj/item/gun/projectile/automatic/pistol/deagle/update_icon() + ..() + icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" + +/obj/item/gun/projectile/automatic/pistol/deagle/gold + desc = "A gold plated desert eagle folded over a million times by superior martian gunsmiths. Uses .50 AE ammo." + icon_state = "deagleg" + item_state = "deagleg" + +/obj/item/gun/projectile/automatic/pistol/deagle/camo + desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE ammo." + icon_state = "deaglecamo" + item_state = "deagleg" + +//APS Pistol// +/obj/item/gun/projectile/automatic/pistol/APS + name = "stechkin APS pistol" + desc = "The original russian version of a widely used Syndicate sidearm. Uses 9mm ammo." + icon_state = "aps" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=3;materials=2;syndicate=3" + mag_type = /obj/item/ammo_box/magazine/pistolm9mm + can_suppress = 0 + burst_size = 3 + fire_delay = 2 + actions_types = list(/datum/action/item_action/toggle_firemode) diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index b29544c57553..642e0fabc3da 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -1,475 +1,475 @@ -/obj/item/gun/projectile/revolver - name = "\improper .357 revolver" - desc = "A suspicious revolver. Uses .357 ammo." - icon_state = "revolver" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder - origin_tech = "combat=3;materials=2" - fire_sound = 'sound/weapons/gunshots/gunshot_strong.ogg' - -/obj/item/gun/projectile/revolver/New() - ..() - if(!istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) - verbs -= /obj/item/gun/projectile/revolver/verb/spin - -/obj/item/gun/projectile/revolver/chamber_round(var/spin = 1) - if(spin) - chambered = magazine.get_round(1) - else - chambered = magazine.stored_ammo[1] - return - -/obj/item/gun/projectile/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj) - ..() - chamber_round(1) - -/obj/item/gun/projectile/revolver/process_chamber() - return ..(0, 1) - -/obj/item/gun/projectile/revolver/attackby(obj/item/A, mob/user, params) - . = ..() - if(.) - return - var/num_loaded = magazine.attackby(A, user, params, 1) - if(num_loaded) - to_chat(user, "You load [num_loaded] shell\s into \the [src].") - A.update_icon() - update_icon() - chamber_round(0) - -/obj/item/gun/projectile/revolver/attack_self(mob/living/user) - var/num_unloaded = 0 - chambered = null - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - if(CB) - CB.loc = get_turf(loc) - CB.SpinAnimation(10, 1) - CB.update_icon() - playsound(get_turf(CB), "casingdrop", 60, 1) - num_unloaded++ - if(num_unloaded) - to_chat(user, "You unload [num_unloaded] shell\s from [src].") - else - to_chat(user, "[src] is empty!") - -/obj/item/gun/projectile/revolver/verb/spin() - set name = "Spin Chamber" - set category = "Object" - set desc = "Click to spin your revolver's chamber." - - var/mob/M = usr - - if(M.stat || !in_range(M,src)) - return - - if(istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) - var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine - C.spin() - chamber_round(0) - playsound(loc, 'sound/weapons/revolver_spin.ogg', 50, 1) - usr.visible_message("[usr] spins [src]'s chamber.", "You spin [src]'s chamber.") - else - verbs -= /obj/item/gun/projectile/revolver/verb/spin - -/obj/item/gun/projectile/revolver/can_shoot() - return get_ammo(0,0) - -/obj/item/gun/projectile/revolver/get_ammo(countchambered = 0, countempties = 1) - var/boolets = 0 //mature var names for mature people - if(chambered && countchambered) - boolets++ - if(magazine) - boolets += magazine.ammo_count(countempties) - return boolets - -/obj/item/gun/projectile/revolver/examine(mob/user) - . = ..() - . += "[get_ammo(0,0)] of those are live rounds." - -/obj/item/gun/projectile/revolver/detective - desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds." - name = "\improper .38 Mars Special" - icon_state = "detective" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38 - unique_rename = 1 - unique_reskin = 1 - -/obj/item/gun/projectile/revolver/detective/New() - ..() - options["The Original"] = "detective" - options["Leopard Spots"] = "detective_leopard" - options["Black Panther"] = "detective_panther" - options["Gold Trim"] = "detective_gold" - options["The Peacemaker"] = "detective_peacemaker" - options["Cancel"] = null - -/obj/item/gun/projectile/revolver/detective/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override = "") - if(magazine.caliber != initial(magazine.caliber)) - if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60 - playsound(user, fire_sound, 50, 1) - to_chat(user, "[src] blows up in your face!") - user.take_organ_damage(0,20) - user.unEquip(src) - return 0 - ..() - -/obj/item/gun/projectile/revolver/detective/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - if(magazine.caliber == "38") - to_chat(user, "You begin to reinforce the barrel of [src]...") - if(magazine.ammo_count()) - afterattack(user, user) //you know the drill - user.visible_message("[src] goes off!", "[src] goes off in your face!") - return - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - if(magazine.ammo_count()) - to_chat(user, "You can't modify it!") - return - magazine.caliber = "357" - desc = "The barrel and chamber assembly seems to have been modified." - to_chat(user, "You reinforce the barrel of [src]. Now it will fire .357 rounds.") - else - to_chat(user, "You begin to revert the modifications to [src]...") - if(magazine.ammo_count()) - afterattack(user, user) //and again - user.visible_message("[src] goes off!", "[src] goes off in your face!") - return - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - if(magazine.ammo_count()) - to_chat(user, "You can't modify it!") - return - magazine.caliber = "38" - desc = initial(desc) - to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.") - -/obj/item/gun/projectile/revolver/fingergun //Summoned by the Finger Gun spell, from advanced mimery traitor item - name = "\improper finger gun" - desc = "Bang bang bang!" - icon_state = "fingergun" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible - origin_tech = "" - flags = ABSTRACT | NODROP | DROPDEL - slot_flags = null - fire_sound = null - fire_sound_text = null - lefthand_file = null - righthand_file = null - clumsy_check = 0 //Stole your uplink! Honk! - needs_permit = 0 //go away beepsky - -/obj/item/gun/projectile/revolver/fingergun/fake - desc = "Pew pew pew!" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible/fake - -/obj/item/gun/projectile/revolver/fingergun/New() - ..() - verbs -= /obj/item/gun/projectile/revolver/verb/spin - -/obj/item/gun/projectile/revolver/fingergun/shoot_with_empty_chamber(/*mob/living/user as mob|obj*/) - to_chat(usr, "You are out of ammo! You holster your fingers.") - qdel(src) - return - -/obj/item/gun/projectile/revolver/fingergun/afterattack(atom/target, mob/living/user, flag, params) - if(!user.mind.miming) - to_chat(usr, "You must dedicate yourself to silence first. Use your fingers if you wish to holster them.") - return - ..() - -/obj/item/gun/projectile/revolver/fingergun/attackby(obj/item/A, mob/user, params) - return - -/obj/item/gun/projectile/revolver/fingergun/attack_self(mob/living/user) - to_chat(usr, "You holster your fingers. Another time.") - qdel(src) - return - -/obj/item/gun/projectile/revolver/mateba - name = "\improper Unica 6 auto-revolver" - desc = "A retro high-powered autorevolver typically used by officers of the New Russia military. Uses .357 ammo." //>10mm hole >.357 - icon_state = "mateba" - -/obj/item/gun/projectile/revolver/golden - name = "\improper Golden revolver" - desc = "This ain't no game, ain't never been no show, And I'll gladly gun down the oldest lady you know. Uses .357 ammo." - icon_state = "goldrevolver" - fire_sound = 'sound/weapons/resonator_blast.ogg' - recoil = 8 - -/obj/item/gun/projectile/revolver/nagant - name = "nagant revolver" - desc = "An old model of revolver that originated in Russia. Able to be suppressed. Uses 7.62x38mmR ammo." - icon_state = "nagant" - origin_tech = "combat=3" - can_suppress = 1 - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev762 - -// A gun to play Russian Roulette! -// You can spin the chamber to randomize the position of the bullet. - -/obj/item/gun/projectile/revolver/russian - name = "\improper Russian Revolver" - desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism that spins the chamber before each trigger pull." - origin_tech = "combat=2;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/rus357 - var/spun = 0 - - -/obj/item/gun/projectile/revolver/russian/New() - ..() - Spin() - update_icon() - -/obj/item/gun/projectile/revolver/russian/proc/Spin() - chambered = null - var/random = rand(1, magazine.max_ammo) - if(random <= get_ammo(0,0)) - chamber_round() - spun = 1 - -/obj/item/gun/projectile/revolver/russian/attackby(obj/item/A, mob/user, params) - var/num_loaded = ..() - if(num_loaded) - user.visible_message("[user] loads a single bullet into the revolver and spins the chamber.", "You load a single bullet into the chamber and spin it.") - else - user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") - if(get_ammo() > 0) - Spin() - update_icon() - A.update_icon() - return - -/obj/item/gun/projectile/revolver/russian/attack_self(mob/user) - if(!spun && can_shoot()) - user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") - Spin() - else - var/num_unloaded = 0 - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round() - chambered = null - CB.loc = get_turf(loc) - CB.update_icon() - playsound(get_turf(CB), "casingdrop", 60, 1) - num_unloaded++ - if(num_unloaded) - to_chat(user, "You unload [num_unloaded] shell\s from [src].") - else - to_chat(user, "[src] is empty.") - -/obj/item/gun/projectile/revolver/russian/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) - if(flag) - if(!(target in user.contents) && ismob(target)) - if(user.a_intent == INTENT_HARM) // Flogging action - return - - if(isliving(user)) - if(!can_trigger_gun(user)) - return - if(target != user) - if(ismob(target)) - to_chat(user, "A mechanism prevents you from shooting anyone but yourself!") - return - - if(ishuman(user)) - if(!spun) - to_chat(user, "You need to spin the revolver's chamber first!") - return - - spun = 0 - - if(chambered) - var/obj/item/ammo_casing/AC = chambered - if(AC.fire(user, user)) - playsound(user, fire_sound, 50, 1) - var/zone = check_zone(user.zone_selected) - if(zone == "head" || zone == "eyes" || zone == "mouth") - shoot_self(user, zone) - else - user.visible_message("[user.name] cowardly fires [src] at [user.p_their()] [zone]!", "You cowardly fire [src] at your [zone]!", "You hear a gunshot!") - return - - user.visible_message("*click*") - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - -/obj/item/gun/projectile/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = "head") - user.apply_damage(300, BRUTE, affecting) - user.visible_message("[user.name] fires [src] at [user.p_their()] head!", "You fire [src] at your head!", "You hear a gunshot!") - -/obj/item/gun/projectile/revolver/russian/soul - name = "cursed Russian revolver" - desc = "To play with this revolver requires wagering your very soul." - -/obj/item/gun/projectile/revolver/russian/soul/shoot_self(mob/living/user) - ..() - var/obj/item/soulstone/anybody/SS = new /obj/item/soulstone/anybody(get_turf(src)) - if(!SS.transfer_soul("FORCE", user)) //Something went wrong - qdel(SS) - return - user.visible_message("[user.name]'s soul is captured by \the [src]!", "You've lost the gamble! Your soul is forfeit!") - -/obj/item/gun/projectile/revolver/capgun - name = "cap gun" - desc = "Looks almost like the real thing! Ages 8 and up." - origin_tech = null - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/cap - -///////////////////////////// -// DOUBLE BARRELED SHOTGUN // -///////////////////////////// - -/obj/item/gun/projectile/revolver/doublebarrel - name = "double-barreled shotgun" - desc = "A true classic." - icon_state = "dshotgun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - mag_type = /obj/item/ammo_box/magazine/internal/shot/dual - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - sawn_desc = "Omar's coming!" - unique_rename = 1 - unique_reskin = 1 - -/obj/item/gun/projectile/revolver/doublebarrel/New() - ..() - options["Default"] = "dshotgun" - options["Dark Red Finish"] = "dshotgun-d" - options["Ash"] = "dshotgun-f" - options["Faded Grey"] = "dshotgun-g" - options["Maple"] = "dshotgun-l" - options["Rosewood"] = "dshotgun-p" - options["Cancel"] = null - -/obj/item/gun/projectile/revolver/doublebarrel/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing)) - chamber_round() - if(istype(A, /obj/item/melee/energy)) - var/obj/item/melee/energy/W = A - if(W.active) - sawoff(user) - if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) - sawoff(user) - else - return ..() - -/obj/item/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user) - var/num_unloaded = 0 - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - chambered = null - CB.loc = get_turf(loc) - CB.SpinAnimation(10, 1) - CB.update_icon() - playsound(get_turf(CB), 'sound/weapons/gun_interactions/shotgun_fall.ogg', 70, 1) - num_unloaded++ - if(num_unloaded) - to_chat(user, "You break open \the [src] and unload [num_unloaded] shell\s.") - else - to_chat(user, "[src] is empty.") - -/obj/item/gun/projectile/revolver/doublebarrel/isHandgun() //contrary to popular opinion, double barrels are not, shockingly, handguns - return 0 - -// IMPROVISED SHOTGUN // - -/obj/item/gun/projectile/revolver/doublebarrel/improvised - name = "improvised shotgun" - desc = "Essentially a tube that aims shotgun shells." - icon_state = "ishotgun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_BULKY - force = 10 - slot_flags = null - mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - sawn_desc = "I'm just here for the gasoline." - unique_rename = 0 - unique_reskin = 0 - var/slung = 0 - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/stack/cable_coil) && !sawn_state) - var/obj/item/stack/cable_coil/C = A - if(C.use(10)) - slot_flags = SLOT_BACK - icon_state = "ishotgunsling" - to_chat(user, "You tie the lengths of cable to the shotgun, making a sling.") - slung = 1 - update_icon() - else - to_chat(user, "You need at least ten lengths of cable if you want to make a sling.") - return - else - return ..() - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/update_icon() - ..() - if(slung && (slot_flags & SLOT_BELT) ) - slung = 0 - icon_state = "ishotgun-sawn" - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/sawoff(mob/user) - . = ..() - if(. && slung) //sawing off the gun removes the sling - new /obj/item/stack/cable_coil(get_turf(src), 10) - slung = 0 - update_icon() - -//caneshotgun - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane - name = "cane" - desc = "A cane used by a true gentleman. Or a clown." - icon = 'icons/obj/items.dmi' - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - icon_state = "cane" - item_state = "stick" - sawn_state = SAWN_OFF - w_class = WEIGHT_CLASS_SMALL - force = 10 - can_unsuppress = 0 - slot_flags = null - origin_tech = "" // NO GIVAWAYS - mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised/cane - sawn_desc = "I'm sorry, but why did you saw your cane in the first place?" - attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") - fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' - suppressed = 1 - needs_permit = 0 //its just a cane beepsky..... - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/is_crutch() - return 1 - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/update_icon() - return - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/stack/cable_coil)) - return - else - return ..() - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/examine(mob/user) // HAD TO REPEAT EXAMINE CODE BECAUSE GUN CODE DOESNT STEALTH - var/f_name = "\a [src]." - if(blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - f_name += "blood-stained [name]!" - - . = list("[bicon(src)] That's [f_name]") - - if(desc) - . += desc +/obj/item/gun/projectile/revolver + name = "\improper .357 revolver" + desc = "A suspicious revolver. Uses .357 ammo." + icon_state = "revolver" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder + origin_tech = "combat=3;materials=2" + fire_sound = 'sound/weapons/gunshots/gunshot_strong.ogg' + +/obj/item/gun/projectile/revolver/New() + ..() + if(!istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) + verbs -= /obj/item/gun/projectile/revolver/verb/spin + +/obj/item/gun/projectile/revolver/chamber_round(var/spin = 1) + if(spin) + chambered = magazine.get_round(1) + else + chambered = magazine.stored_ammo[1] + return + +/obj/item/gun/projectile/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj) + ..() + chamber_round(1) + +/obj/item/gun/projectile/revolver/process_chamber() + return ..(0, 1) + +/obj/item/gun/projectile/revolver/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + var/num_loaded = magazine.attackby(A, user, params, 1) + if(num_loaded) + to_chat(user, "You load [num_loaded] shell\s into \the [src].") + A.update_icon() + update_icon() + chamber_round(0) + +/obj/item/gun/projectile/revolver/attack_self(mob/living/user) + var/num_unloaded = 0 + chambered = null + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + if(CB) + CB.loc = get_turf(loc) + CB.SpinAnimation(10, 1) + CB.update_icon() + playsound(get_turf(CB), "casingdrop", 60, 1) + num_unloaded++ + if(num_unloaded) + to_chat(user, "You unload [num_unloaded] shell\s from [src].") + else + to_chat(user, "[src] is empty!") + +/obj/item/gun/projectile/revolver/verb/spin() + set name = "Spin Chamber" + set category = "Object" + set desc = "Click to spin your revolver's chamber." + + var/mob/M = usr + + if(M.stat || !in_range(M,src)) + return + + if(istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) + var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine + C.spin() + chamber_round(0) + playsound(loc, 'sound/weapons/revolver_spin.ogg', 50, 1) + usr.visible_message("[usr] spins [src]'s chamber.", "You spin [src]'s chamber.") + else + verbs -= /obj/item/gun/projectile/revolver/verb/spin + +/obj/item/gun/projectile/revolver/can_shoot() + return get_ammo(0,0) + +/obj/item/gun/projectile/revolver/get_ammo(countchambered = 0, countempties = 1) + var/boolets = 0 //mature var names for mature people + if(chambered && countchambered) + boolets++ + if(magazine) + boolets += magazine.ammo_count(countempties) + return boolets + +/obj/item/gun/projectile/revolver/examine(mob/user) + . = ..() + . += "[get_ammo(0,0)] of those are live rounds." + +/obj/item/gun/projectile/revolver/detective + desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds." + name = "\improper .38 Mars Special" + icon_state = "detective" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38 + unique_rename = 1 + unique_reskin = 1 + +/obj/item/gun/projectile/revolver/detective/New() + ..() + options["The Original"] = "detective" + options["Leopard Spots"] = "detective_leopard" + options["Black Panther"] = "detective_panther" + options["Gold Trim"] = "detective_gold" + options["The Peacemaker"] = "detective_peacemaker" + options["Cancel"] = null + +/obj/item/gun/projectile/revolver/detective/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override = "") + if(magazine.caliber != initial(magazine.caliber)) + if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60 + playsound(user, fire_sound, 50, 1) + to_chat(user, "[src] blows up in your face!") + user.take_organ_damage(0,20) + user.unEquip(src) + return 0 + ..() + +/obj/item/gun/projectile/revolver/detective/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(magazine.caliber == "38") + to_chat(user, "You begin to reinforce the barrel of [src]...") + if(magazine.ammo_count()) + afterattack(user, user) //you know the drill + user.visible_message("[src] goes off!", "[src] goes off in your face!") + return + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + if(magazine.ammo_count()) + to_chat(user, "You can't modify it!") + return + magazine.caliber = "357" + desc = "The barrel and chamber assembly seems to have been modified." + to_chat(user, "You reinforce the barrel of [src]. Now it will fire .357 rounds.") + else + to_chat(user, "You begin to revert the modifications to [src]...") + if(magazine.ammo_count()) + afterattack(user, user) //and again + user.visible_message("[src] goes off!", "[src] goes off in your face!") + return + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + if(magazine.ammo_count()) + to_chat(user, "You can't modify it!") + return + magazine.caliber = "38" + desc = initial(desc) + to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.") + +/obj/item/gun/projectile/revolver/fingergun //Summoned by the Finger Gun spell, from advanced mimery traitor item + name = "\improper finger gun" + desc = "Bang bang bang!" + icon_state = "fingergun" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible + origin_tech = "" + flags = ABSTRACT | NODROP | DROPDEL + slot_flags = null + fire_sound = null + fire_sound_text = null + lefthand_file = null + righthand_file = null + clumsy_check = 0 //Stole your uplink! Honk! + needs_permit = 0 //go away beepsky + +/obj/item/gun/projectile/revolver/fingergun/fake + desc = "Pew pew pew!" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible/fake + +/obj/item/gun/projectile/revolver/fingergun/New() + ..() + verbs -= /obj/item/gun/projectile/revolver/verb/spin + +/obj/item/gun/projectile/revolver/fingergun/shoot_with_empty_chamber(/*mob/living/user as mob|obj*/) + to_chat(usr, "You are out of ammo! You holster your fingers.") + qdel(src) + return + +/obj/item/gun/projectile/revolver/fingergun/afterattack(atom/target, mob/living/user, flag, params) + if(!user.mind.miming) + to_chat(usr, "You must dedicate yourself to silence first. Use your fingers if you wish to holster them.") + return + ..() + +/obj/item/gun/projectile/revolver/fingergun/attackby(obj/item/A, mob/user, params) + return + +/obj/item/gun/projectile/revolver/fingergun/attack_self(mob/living/user) + to_chat(usr, "You holster your fingers. Another time.") + qdel(src) + return + +/obj/item/gun/projectile/revolver/mateba + name = "\improper Unica 6 auto-revolver" + desc = "A retro high-powered autorevolver typically used by officers of the New Russia military. Uses .357 ammo." //>10mm hole >.357 + icon_state = "mateba" + +/obj/item/gun/projectile/revolver/golden + name = "\improper Golden revolver" + desc = "This ain't no game, ain't never been no show, And I'll gladly gun down the oldest lady you know. Uses .357 ammo." + icon_state = "goldrevolver" + fire_sound = 'sound/weapons/resonator_blast.ogg' + recoil = 8 + +/obj/item/gun/projectile/revolver/nagant + name = "nagant revolver" + desc = "An old model of revolver that originated in Russia. Able to be suppressed. Uses 7.62x38mmR ammo." + icon_state = "nagant" + origin_tech = "combat=3" + can_suppress = 1 + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev762 + +// A gun to play Russian Roulette! +// You can spin the chamber to randomize the position of the bullet. + +/obj/item/gun/projectile/revolver/russian + name = "\improper Russian Revolver" + desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism that spins the chamber before each trigger pull." + origin_tech = "combat=2;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/rus357 + var/spun = 0 + + +/obj/item/gun/projectile/revolver/russian/New() + ..() + Spin() + update_icon() + +/obj/item/gun/projectile/revolver/russian/proc/Spin() + chambered = null + var/random = rand(1, magazine.max_ammo) + if(random <= get_ammo(0,0)) + chamber_round() + spun = 1 + +/obj/item/gun/projectile/revolver/russian/attackby(obj/item/A, mob/user, params) + var/num_loaded = ..() + if(num_loaded) + user.visible_message("[user] loads a single bullet into the revolver and spins the chamber.", "You load a single bullet into the chamber and spin it.") + else + user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") + if(get_ammo() > 0) + Spin() + update_icon() + A.update_icon() + return + +/obj/item/gun/projectile/revolver/russian/attack_self(mob/user) + if(!spun && can_shoot()) + user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") + Spin() + else + var/num_unloaded = 0 + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round() + chambered = null + CB.loc = get_turf(loc) + CB.update_icon() + playsound(get_turf(CB), "casingdrop", 60, 1) + num_unloaded++ + if(num_unloaded) + to_chat(user, "You unload [num_unloaded] shell\s from [src].") + else + to_chat(user, "[src] is empty.") + +/obj/item/gun/projectile/revolver/russian/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) + if(flag) + if(!(target in user.contents) && ismob(target)) + if(user.a_intent == INTENT_HARM) // Flogging action + return + + if(isliving(user)) + if(!can_trigger_gun(user)) + return + if(target != user) + if(ismob(target)) + to_chat(user, "A mechanism prevents you from shooting anyone but yourself!") + return + + if(ishuman(user)) + if(!spun) + to_chat(user, "You need to spin the revolver's chamber first!") + return + + spun = 0 + + if(chambered) + var/obj/item/ammo_casing/AC = chambered + if(AC.fire(user, user)) + playsound(user, fire_sound, 50, 1) + var/zone = check_zone(user.zone_selected) + if(zone == "head" || zone == "eyes" || zone == "mouth") + shoot_self(user, zone) + else + user.visible_message("[user.name] cowardly fires [src] at [user.p_their()] [zone]!", "You cowardly fire [src] at your [zone]!", "You hear a gunshot!") + return + + user.visible_message("*click*") + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + +/obj/item/gun/projectile/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = "head") + user.apply_damage(300, BRUTE, affecting) + user.visible_message("[user.name] fires [src] at [user.p_their()] head!", "You fire [src] at your head!", "You hear a gunshot!") + +/obj/item/gun/projectile/revolver/russian/soul + name = "cursed Russian revolver" + desc = "To play with this revolver requires wagering your very soul." + +/obj/item/gun/projectile/revolver/russian/soul/shoot_self(mob/living/user) + ..() + var/obj/item/soulstone/anybody/SS = new /obj/item/soulstone/anybody(get_turf(src)) + if(!SS.transfer_soul("FORCE", user)) //Something went wrong + qdel(SS) + return + user.visible_message("[user.name]'s soul is captured by \the [src]!", "You've lost the gamble! Your soul is forfeit!") + +/obj/item/gun/projectile/revolver/capgun + name = "cap gun" + desc = "Looks almost like the real thing! Ages 8 and up." + origin_tech = null + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/cap + +///////////////////////////// +// DOUBLE BARRELED SHOTGUN // +///////////////////////////// + +/obj/item/gun/projectile/revolver/doublebarrel + name = "double-barreled shotgun" + desc = "A true classic." + icon_state = "dshotgun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + mag_type = /obj/item/ammo_box/magazine/internal/shot/dual + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + sawn_desc = "Omar's coming!" + unique_rename = 1 + unique_reskin = 1 + +/obj/item/gun/projectile/revolver/doublebarrel/New() + ..() + options["Default"] = "dshotgun" + options["Dark Red Finish"] = "dshotgun-d" + options["Ash"] = "dshotgun-f" + options["Faded Grey"] = "dshotgun-g" + options["Maple"] = "dshotgun-l" + options["Rosewood"] = "dshotgun-p" + options["Cancel"] = null + +/obj/item/gun/projectile/revolver/doublebarrel/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing)) + chamber_round() + if(istype(A, /obj/item/melee/energy)) + var/obj/item/melee/energy/W = A + if(W.active) + sawoff(user) + if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) + sawoff(user) + else + return ..() + +/obj/item/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user) + var/num_unloaded = 0 + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + chambered = null + CB.loc = get_turf(loc) + CB.SpinAnimation(10, 1) + CB.update_icon() + playsound(get_turf(CB), 'sound/weapons/gun_interactions/shotgun_fall.ogg', 70, 1) + num_unloaded++ + if(num_unloaded) + to_chat(user, "You break open \the [src] and unload [num_unloaded] shell\s.") + else + to_chat(user, "[src] is empty.") + +/obj/item/gun/projectile/revolver/doublebarrel/isHandgun() //contrary to popular opinion, double barrels are not, shockingly, handguns + return 0 + +// IMPROVISED SHOTGUN // + +/obj/item/gun/projectile/revolver/doublebarrel/improvised + name = "improvised shotgun" + desc = "Essentially a tube that aims shotgun shells." + icon_state = "ishotgun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + slot_flags = null + mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + sawn_desc = "I'm just here for the gasoline." + unique_rename = 0 + unique_reskin = 0 + var/slung = 0 + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/stack/cable_coil) && !sawn_state) + var/obj/item/stack/cable_coil/C = A + if(C.use(10)) + slot_flags = SLOT_BACK + icon_state = "ishotgunsling" + to_chat(user, "You tie the lengths of cable to the shotgun, making a sling.") + slung = 1 + update_icon() + else + to_chat(user, "You need at least ten lengths of cable if you want to make a sling.") + return + else + return ..() + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/update_icon() + ..() + if(slung && (slot_flags & SLOT_BELT) ) + slung = 0 + icon_state = "ishotgun-sawn" + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/sawoff(mob/user) + . = ..() + if(. && slung) //sawing off the gun removes the sling + new /obj/item/stack/cable_coil(get_turf(src), 10) + slung = 0 + update_icon() + +//caneshotgun + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane + name = "cane" + desc = "A cane used by a true gentleman. Or a clown." + icon = 'icons/obj/items.dmi' + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + icon_state = "cane" + item_state = "stick" + sawn_state = SAWN_OFF + w_class = WEIGHT_CLASS_SMALL + force = 10 + can_unsuppress = 0 + slot_flags = null + origin_tech = "" // NO GIVAWAYS + mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised/cane + sawn_desc = "I'm sorry, but why did you saw your cane in the first place?" + attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") + fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' + suppressed = 1 + needs_permit = 0 //its just a cane beepsky..... + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/is_crutch() + return 1 + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/update_icon() + return + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/stack/cable_coil)) + return + else + return ..() + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/examine(mob/user) // HAD TO REPEAT EXAMINE CODE BECAUSE GUN CODE DOESNT STEALTH + var/f_name = "\a [src]." + if(blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + f_name += "blood-stained [name]!" + + . = list("[bicon(src)] That's [f_name]") + + if(desc) + . += desc diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index ee6b013af0d4..17a55722d2c5 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -1,331 +1,331 @@ -/obj/item/gun/projectile/shotgun - name = "shotgun" - desc = "A traditional shotgun with wood furniture and a four-shell capacity underneath." - icon_state = "shotgun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - origin_tech = "combat=4;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/shot - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - var/recentpump = 0 // to prevent spammage - weapon_weight = WEAPON_MEDIUM - -/obj/item/gun/projectile/shotgun/attackby(obj/item/A, mob/user, params) - . = ..() - if(.) - return - var/num_loaded = magazine.attackby(A, user, params, 1) - if(num_loaded) - to_chat(user, "You load [num_loaded] shell\s into \the [src]!") - A.update_icon() - update_icon() - - -/obj/item/gun/projectile/shotgun/process_chamber() - return ..(0, 0) - -/obj/item/gun/projectile/shotgun/chamber_round() - return - -/obj/item/gun/projectile/shotgun/can_shoot() - if(!chambered) - return 0 - return (chambered.BB ? 1 : 0) - -/obj/item/gun/projectile/shotgun/attack_self(mob/living/user) - if(recentpump) - return - pump(user) - recentpump = 1 - spawn(10) - recentpump = 0 - return - - -/obj/item/gun/projectile/shotgun/proc/pump(mob/M) - playsound(M, 'sound/weapons/gun_interactions/shotgunpump.ogg', 60, 1) - pump_unload(M) - pump_reload(M) - update_icon() //I.E. fix the desc - return 1 - -/obj/item/gun/projectile/shotgun/proc/pump_unload(mob/M) - if(chambered)//We have a shell in the chamber - chambered.loc = get_turf(src)//Eject casing - chambered.SpinAnimation(5, 1) - playsound(src, chambered.drop_sound, 60, 1) - chambered = null - -/obj/item/gun/projectile/shotgun/proc/pump_reload(mob/M) - if(!magazine.ammo_count()) - return 0 - var/obj/item/ammo_casing/AC = magazine.get_round() //load next casing. - chambered = AC - -/obj/item/gun/projectile/shotgun/examine(mob/user) - . = ..() - if(chambered) - . += "A [chambered.BB ? "live" : "spent"] one is in the chamber." - -/obj/item/gun/projectile/shotgun/isHandgun() //You cannot, in fact, holster a shotgun. - return 0 - -/obj/item/gun/projectile/shotgun/lethal - mag_type = /obj/item/ammo_box/magazine/internal/shot/lethal - -// RIOT SHOTGUN // - -/obj/item/gun/projectile/shotgun/riot //for spawn in the armory - name = "riot shotgun" - desc = "A sturdy shotgun with a longer magazine and a fixed tactical stock designed for non-lethal riot control." - icon_state = "riotshotgun" - mag_type = /obj/item/ammo_box/magazine/internal/shot/riot - sawn_desc = "Come with me if you want to live." - sawn_state = SAWN_INTACT - -/obj/item/gun/projectile/shotgun/riot/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) - sawoff(user) - if(istype(A, /obj/item/melee/energy)) - var/obj/item/melee/energy/W = A - if(W.active) - sawoff(user) - if(istype(A, /obj/item/pipe)) - unsaw(A, user) - else - return ..() - -/obj/item/gun/projectile/shotgun/riot/sawoff(mob/user) - if(sawn_state == SAWN_OFF) - to_chat(user, "[src] has already been shortened!") - return - if(istype(loc, /obj/item/storage)) //To prevent inventory exploits - to_chat(user, "How do you plan to modify [src] while it's in a bag.") - return - if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' - if(chambered.BB) - afterattack(user, user) - user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") - return - else - afterattack(user, user) - user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") - if(magazine.ammo_count()) //Spill the mag onto the floor - user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") - while(get_ammo(0) > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - if(CB) - CB.loc = get_turf(loc) - CB.update_icon() - - if(do_after(user, 30, target = src)) - user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") - post_sawoff() - return 1 - - -/obj/item/gun/projectile/shotgun/riot/proc/post_sawoff() - name = "assault shotgun" - desc = sawn_desc - w_class = WEIGHT_CLASS_NORMAL - current_skin = "riotshotgun-short" - item_state = "gun" //phil235 is it different with different skin? - slot_flags &= ~SLOT_BACK //you can't sling it on your back - slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) - sawn_state = SAWN_OFF - magazine.max_ammo = 3 - update_icon() - - -/obj/item/gun/projectile/shotgun/riot/proc/unsaw(obj/item/A, mob/user) - if(sawn_state == SAWN_INTACT) - to_chat(user, "[src] has not been shortened!") - return - if(istype(loc, /obj/item/storage)) //To prevent inventory exploits - to_chat(user, "How do you plan to modify [src] while it's in a bag.") - return - if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' - if(chambered.BB) - afterattack(user, user) - user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") - return - else - afterattack(user, user) - user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") - if(magazine.ammo_count()) //Spill the mag onto the floor - user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - if(CB) - CB.loc = get_turf(loc) - CB.update_icon() - - if(do_after(user, 30, target = src)) - qdel(A) - user.visible_message("[user] lengthens [src]!", "You lengthen [src].") - post_unsaw(user) - return 1 - -/obj/item/gun/projectile/shotgun/riot/proc/post_unsaw() - name = initial(name) - desc = initial(desc) - w_class = initial(w_class) - current_skin = "riotshotgun" - item_state = initial(item_state) - slot_flags &= ~SLOT_BELT - slot_flags |= SLOT_BACK - sawn_state = SAWN_INTACT - magazine.max_ammo = 6 - update_icon() - -/obj/item/gun/projectile/shotgun/riot/update_icon() //Can't use the old proc as it makes it go to riotshotgun-short_sawn - ..() - if(current_skin) - icon_state = "[current_skin]" - else - icon_state = "[initial(icon_state)]" - -/obj/item/gun/projectile/shotgun/riot/short - mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/short - -/obj/item/gun/projectile/shotgun/riot/short/New() - ..() - post_sawoff() - - - -/////////////////////// -// BOLT ACTION RIFLE // -/////////////////////// - -/obj/item/gun/projectile/shotgun/boltaction - name = "\improper Mosin Nagant" - desc = "This piece of junk looks like something that could have been used 700 years ago." - icon_state = "moistnugget" - item_state = "moistnugget" - slot_flags = 0 //no SLOT_BACK sprite, alas - mag_type = /obj/item/ammo_box/magazine/internal/boltaction - fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' - var/bolt_open = 0 - can_bayonet = TRUE - knife_x_offset = 27 - knife_y_offset = 13 - -/obj/item/gun/projectile/shotgun/boltaction/pump(mob/M) - playsound(M, 'sound/weapons/gun_interactions/rifle_load.ogg', 60, 1) - if(bolt_open) - pump_reload(M) - else - pump_unload(M) - bolt_open = !bolt_open - update_icon() //I.E. fix the desc - return 1 - -/obj/item/gun/projectile/shotgun/blow_up(mob/user) - . = 0 - if(chambered && chambered.BB) - process_fire(user, user,0) - . = 1 - -/obj/item/gun/projectile/shotgun/boltaction/attackby(obj/item/A, mob/user, params) - if(!bolt_open) - to_chat(user, "The bolt is closed!") - return - . = ..() - -/obj/item/gun/projectile/shotgun/boltaction/examine(mob/user) - . = ..() - . += "The bolt is [bolt_open ? "open" : "closed"]." - -/obj/item/gun/projectile/shotgun/boltaction/enchanted - name = "enchanted bolt action rifle" - desc = "Careful not to lose your head." - var/guns_left = 30 - mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted - can_bayonet = FALSE - -/obj/item/gun/projectile/shotgun/boltaction/enchanted/New() - ..() - bolt_open = 1 - pump() - -/obj/item/gun/projectile/shotgun/boltaction/enchanted/dropped() - ..() - guns_left = 0 - -/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) - ..() - if(guns_left) - var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new - GUN.guns_left = guns_left - 1 - user.drop_item() - user.swap_hand() - user.put_in_hands(GUN) - else - user.drop_item() - spawn(0) - throw_at(pick(oview(7,get_turf(user))),1,1) - user.visible_message("[user] tosses aside the spent rifle!") - -// Automatic Shotguns// - -/obj/item/gun/projectile/shotgun/automatic - -/obj/item/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) - ..() - pump(user) - -/obj/item/gun/projectile/shotgun/automatic/combat - name = "combat shotgun" - desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath." - icon_state = "cshotgun" - origin_tech = "combat=6" - mag_type = /obj/item/ammo_box/magazine/internal/shot/com - w_class = WEIGHT_CLASS_HUGE - -//Dual Feed Shotgun - -/obj/item/gun/projectile/shotgun/automatic/dual_tube - name = "cycler shotgun" - desc = "An advanced shotgun with two separate magazine tubes, allowing you to quickly toggle between ammo types." - icon_state = "cycler" - origin_tech = "combat=4;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/shot/tube - w_class = WEIGHT_CLASS_HUGE - var/toggled = 0 - var/obj/item/ammo_box/magazine/internal/shot/alternate_magazine - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/New() - ..() - if(!alternate_magazine) - alternate_magazine = new mag_type(src) - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/attack_self(mob/living/user) - if(!chambered && magazine.contents.len) - pump() - else - toggle_tube(user) - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/proc/toggle_tube(mob/living/user) - var/current_mag = magazine - var/alt_mag = alternate_magazine - magazine = alt_mag - alternate_magazine = current_mag - toggled = !toggled - if(toggled) - to_chat(user, "You switch to tube B.") - else - to_chat(user, "You switch to tube A.") - playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/AltClick(mob/living/user) - if(user.incapacitated() || !Adjacent(user) || !istype(user)) - return - pump() - -// DOUBLE BARRELED SHOTGUN, IMPROVISED SHOTGUN, and CANE SHOTGUN are in revolver.dm +/obj/item/gun/projectile/shotgun + name = "shotgun" + desc = "A traditional shotgun with wood furniture and a four-shell capacity underneath." + icon_state = "shotgun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/shot + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + var/recentpump = 0 // to prevent spammage + weapon_weight = WEAPON_MEDIUM + +/obj/item/gun/projectile/shotgun/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + var/num_loaded = magazine.attackby(A, user, params, 1) + if(num_loaded) + to_chat(user, "You load [num_loaded] shell\s into \the [src]!") + A.update_icon() + update_icon() + + +/obj/item/gun/projectile/shotgun/process_chamber() + return ..(0, 0) + +/obj/item/gun/projectile/shotgun/chamber_round() + return + +/obj/item/gun/projectile/shotgun/can_shoot() + if(!chambered) + return 0 + return (chambered.BB ? 1 : 0) + +/obj/item/gun/projectile/shotgun/attack_self(mob/living/user) + if(recentpump) + return + pump(user) + recentpump = 1 + spawn(10) + recentpump = 0 + return + + +/obj/item/gun/projectile/shotgun/proc/pump(mob/M) + playsound(M, 'sound/weapons/gun_interactions/shotgunpump.ogg', 60, 1) + pump_unload(M) + pump_reload(M) + update_icon() //I.E. fix the desc + return 1 + +/obj/item/gun/projectile/shotgun/proc/pump_unload(mob/M) + if(chambered)//We have a shell in the chamber + chambered.loc = get_turf(src)//Eject casing + chambered.SpinAnimation(5, 1) + playsound(src, chambered.drop_sound, 60, 1) + chambered = null + +/obj/item/gun/projectile/shotgun/proc/pump_reload(mob/M) + if(!magazine.ammo_count()) + return 0 + var/obj/item/ammo_casing/AC = magazine.get_round() //load next casing. + chambered = AC + +/obj/item/gun/projectile/shotgun/examine(mob/user) + . = ..() + if(chambered) + . += "A [chambered.BB ? "live" : "spent"] one is in the chamber." + +/obj/item/gun/projectile/shotgun/isHandgun() //You cannot, in fact, holster a shotgun. + return 0 + +/obj/item/gun/projectile/shotgun/lethal + mag_type = /obj/item/ammo_box/magazine/internal/shot/lethal + +// RIOT SHOTGUN // + +/obj/item/gun/projectile/shotgun/riot //for spawn in the armory + name = "riot shotgun" + desc = "A sturdy shotgun with a longer magazine and a fixed tactical stock designed for non-lethal riot control." + icon_state = "riotshotgun" + mag_type = /obj/item/ammo_box/magazine/internal/shot/riot + sawn_desc = "Come with me if you want to live." + sawn_state = SAWN_INTACT + +/obj/item/gun/projectile/shotgun/riot/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) + sawoff(user) + if(istype(A, /obj/item/melee/energy)) + var/obj/item/melee/energy/W = A + if(W.active) + sawoff(user) + if(istype(A, /obj/item/pipe)) + unsaw(A, user) + else + return ..() + +/obj/item/gun/projectile/shotgun/riot/sawoff(mob/user) + if(sawn_state == SAWN_OFF) + to_chat(user, "[src] has already been shortened!") + return + if(istype(loc, /obj/item/storage)) //To prevent inventory exploits + to_chat(user, "How do you plan to modify [src] while it's in a bag.") + return + if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' + if(chambered.BB) + afterattack(user, user) + user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") + return + else + afterattack(user, user) + user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") + if(magazine.ammo_count()) //Spill the mag onto the floor + user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") + while(get_ammo(0) > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + if(CB) + CB.loc = get_turf(loc) + CB.update_icon() + + if(do_after(user, 30, target = src)) + user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") + post_sawoff() + return 1 + + +/obj/item/gun/projectile/shotgun/riot/proc/post_sawoff() + name = "assault shotgun" + desc = sawn_desc + w_class = WEIGHT_CLASS_NORMAL + current_skin = "riotshotgun-short" + item_state = "gun" //phil235 is it different with different skin? + slot_flags &= ~SLOT_BACK //you can't sling it on your back + slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) + sawn_state = SAWN_OFF + magazine.max_ammo = 3 + update_icon() + + +/obj/item/gun/projectile/shotgun/riot/proc/unsaw(obj/item/A, mob/user) + if(sawn_state == SAWN_INTACT) + to_chat(user, "[src] has not been shortened!") + return + if(istype(loc, /obj/item/storage)) //To prevent inventory exploits + to_chat(user, "How do you plan to modify [src] while it's in a bag.") + return + if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' + if(chambered.BB) + afterattack(user, user) + user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") + return + else + afterattack(user, user) + user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") + if(magazine.ammo_count()) //Spill the mag onto the floor + user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + if(CB) + CB.loc = get_turf(loc) + CB.update_icon() + + if(do_after(user, 30, target = src)) + qdel(A) + user.visible_message("[user] lengthens [src]!", "You lengthen [src].") + post_unsaw(user) + return 1 + +/obj/item/gun/projectile/shotgun/riot/proc/post_unsaw() + name = initial(name) + desc = initial(desc) + w_class = initial(w_class) + current_skin = "riotshotgun" + item_state = initial(item_state) + slot_flags &= ~SLOT_BELT + slot_flags |= SLOT_BACK + sawn_state = SAWN_INTACT + magazine.max_ammo = 6 + update_icon() + +/obj/item/gun/projectile/shotgun/riot/update_icon() //Can't use the old proc as it makes it go to riotshotgun-short_sawn + ..() + if(current_skin) + icon_state = "[current_skin]" + else + icon_state = "[initial(icon_state)]" + +/obj/item/gun/projectile/shotgun/riot/short + mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/short + +/obj/item/gun/projectile/shotgun/riot/short/New() + ..() + post_sawoff() + + + +/////////////////////// +// BOLT ACTION RIFLE // +/////////////////////// + +/obj/item/gun/projectile/shotgun/boltaction + name = "\improper Mosin Nagant" + desc = "This piece of junk looks like something that could have been used 700 years ago." + icon_state = "moistnugget" + item_state = "moistnugget" + slot_flags = 0 //no SLOT_BACK sprite, alas + mag_type = /obj/item/ammo_box/magazine/internal/boltaction + fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' + var/bolt_open = 0 + can_bayonet = TRUE + knife_x_offset = 27 + knife_y_offset = 13 + +/obj/item/gun/projectile/shotgun/boltaction/pump(mob/M) + playsound(M, 'sound/weapons/gun_interactions/rifle_load.ogg', 60, 1) + if(bolt_open) + pump_reload(M) + else + pump_unload(M) + bolt_open = !bolt_open + update_icon() //I.E. fix the desc + return 1 + +/obj/item/gun/projectile/shotgun/blow_up(mob/user) + . = 0 + if(chambered && chambered.BB) + process_fire(user, user,0) + . = 1 + +/obj/item/gun/projectile/shotgun/boltaction/attackby(obj/item/A, mob/user, params) + if(!bolt_open) + to_chat(user, "The bolt is closed!") + return + . = ..() + +/obj/item/gun/projectile/shotgun/boltaction/examine(mob/user) + . = ..() + . += "The bolt is [bolt_open ? "open" : "closed"]." + +/obj/item/gun/projectile/shotgun/boltaction/enchanted + name = "enchanted bolt action rifle" + desc = "Careful not to lose your head." + var/guns_left = 30 + mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted + can_bayonet = FALSE + +/obj/item/gun/projectile/shotgun/boltaction/enchanted/New() + ..() + bolt_open = 1 + pump() + +/obj/item/gun/projectile/shotgun/boltaction/enchanted/dropped() + ..() + guns_left = 0 + +/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) + ..() + if(guns_left) + var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new + GUN.guns_left = guns_left - 1 + user.drop_item() + user.swap_hand() + user.put_in_hands(GUN) + else + user.drop_item() + spawn(0) + throw_at(pick(oview(7,get_turf(user))),1,1) + user.visible_message("[user] tosses aside the spent rifle!") + +// Automatic Shotguns// + +/obj/item/gun/projectile/shotgun/automatic + +/obj/item/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) + ..() + pump(user) + +/obj/item/gun/projectile/shotgun/automatic/combat + name = "combat shotgun" + desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath." + icon_state = "cshotgun" + origin_tech = "combat=6" + mag_type = /obj/item/ammo_box/magazine/internal/shot/com + w_class = WEIGHT_CLASS_HUGE + +//Dual Feed Shotgun + +/obj/item/gun/projectile/shotgun/automatic/dual_tube + name = "cycler shotgun" + desc = "An advanced shotgun with two separate magazine tubes, allowing you to quickly toggle between ammo types." + icon_state = "cycler" + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/shot/tube + w_class = WEIGHT_CLASS_HUGE + var/toggled = 0 + var/obj/item/ammo_box/magazine/internal/shot/alternate_magazine + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/New() + ..() + if(!alternate_magazine) + alternate_magazine = new mag_type(src) + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/attack_self(mob/living/user) + if(!chambered && magazine.contents.len) + pump() + else + toggle_tube(user) + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/proc/toggle_tube(mob/living/user) + var/current_mag = magazine + var/alt_mag = alternate_magazine + magazine = alt_mag + alternate_magazine = current_mag + toggled = !toggled + if(toggled) + to_chat(user, "You switch to tube B.") + else + to_chat(user, "You switch to tube A.") + playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/AltClick(mob/living/user) + if(user.incapacitated() || !Adjacent(user) || !istype(user)) + return + pump() + +// DOUBLE BARRELED SHOTGUN, IMPROVISED SHOTGUN, and CANE SHOTGUN are in revolver.dm diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index de3f4c02a87e..09d9ec15aed5 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -208,4 +208,4 @@ else if(ammo) overlays += image('icons/obj/ammo.dmi', icon_state = ".50mag-f") else - icon_state = "[initial(icon_state)]" \ No newline at end of file + icon_state = "[initial(icon_state)]" diff --git a/code/modules/projectiles/guns/projectile/toy.dm b/code/modules/projectiles/guns/projectile/toy.dm index e9625e697957..55a9aa79c6b6 100644 --- a/code/modules/projectiles/guns/projectile/toy.dm +++ b/code/modules/projectiles/guns/projectile/toy.dm @@ -133,4 +133,4 @@ mag_type = /obj/item/ammo_box/magazine/toy/sniper_rounds /obj/item/gun/projectile/automatic/sniper_rifle/toy/process_chamber(eject_casing = 0, empty_chamber = 1) - ..() \ No newline at end of file + ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index d31231823f89..626bcaf05a89 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -1,355 +1,355 @@ -/obj/item/projectile - name = "projectile" - icon = 'icons/obj/projectiles.dmi' - icon_state = "bullet" - density = 0 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots. - flags = ABSTRACT - pass_flags = PASSTABLE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - hitsound = 'sound/weapons/pierce.ogg' - var/hitsound_wall = "" - var/def_zone = "" //Aiming at - var/mob/firer = null//Who shot it - var/obj/item/ammo_casing/ammo_casing = null - var/suppressed = 0 //Attack message - var/yo = null - var/xo = null - var/current = null - var/atom/original = null // the original target clicked - var/turf/starting = null // the projectile's starting turf - var/list/permutated = list() // we've passed through these atoms, don't try to hit them again - var/paused = FALSE //for suspending the projectile midair - var/p_x = 16 - var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center - var/speed = 1 //Amount of deciseconds it takes for projectile to travel - var/Angle = null - var/spread = 0 //amount (in degrees) of projectile spread - var/legacy = FALSE //legacy projectile system - animate_movement = 0 - - var/ignore_source_check = FALSE - - var/damage = 10 - var/tile_dropoff = 0 //how much damage should be decremented as the bullet moves - var/tile_dropoff_s = 0 //same as above but for stamina - var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here - var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions - var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid - var/projectile_type = "/obj/item/projectile" - var/range = 50 //This will de-increment every step. When 0, it will delete the projectile. - var/is_reflectable = FALSE // Can it be reflected or not? - var/alwayslog = FALSE // ALWAYS log this projectile on hit even if it doesn't hit a living target. Useful for AOE explosion / EMP. - //Effects - var/stun = 0 - var/weaken = 0 - var/paralyze = 0 - var/irradiate = 0 - var/stutter = 0 - var/slur = 0 - var/eyeblur = 0 - var/drowsy = 0 - var/stamina = 0 - var/jitter = 0 - var/forcedodge = 0 //to pass through everything - var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all. - var/impact_effect_type //what type of impact effect to show when hitting something - var/ricochets = 0 - var/ricochets_max = 2 - var/ricochet_chance = 30 - - var/log_override = FALSE //whether print to admin attack logs or just keep it in the diary - -/obj/item/projectile/New() - permutated = list() - return ..() - -/obj/item/projectile/proc/Range() - range-- - if(damage && tile_dropoff) - damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves - if(stamina && tile_dropoff_s) - stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina - if(range <= 0 && loc) - on_range() - if(!damage && !stamina && (tile_dropoff || tile_dropoff_s)) - on_range() - -/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range - qdel(src) - -/obj/item/projectile/proc/prehit(atom/target) - return TRUE - -/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, hit_zone) - var/turf/target_loca = get_turf(target) - var/hitx - var/hity - if(target == original) - hitx = target.pixel_x + p_x - 16 - hity = target.pixel_y + p_y - 16 - else - hitx = target.pixel_x + rand(-8, 8) - hity = target.pixel_y + rand(-8, 8) - if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75)) - var/turf/simulated/wall/W = target_loca - if(impact_effect_type) - new impact_effect_type(target_loca, hitx, hity) - - W.add_dent(WALL_DENT_SHOT, hitx, hity) - return 0 - if(alwayslog) - add_attack_logs(firer, target, "Shot with a [type]") - if(!isliving(target)) - if(impact_effect_type) - new impact_effect_type(target_loca, hitx, hity) - return 0 - var/mob/living/L = target - var/mob/living/carbon/human/H - if(blocked < 100) // not completely blocked - if(damage && L.blood_volume && damage_type == BRUTE) - var/splatter_dir = dir - if(starting) - splatter_dir = get_dir(starting, target_loca) - if(isalien(L)) - new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_loca, splatter_dir) - else - var/blood_color = "#C80000" - if(ishuman(target)) - H = target - blood_color = H.dna.species.blood_color - new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, blood_color) - if(prob(33)) - var/list/shift = list("x" = 0, "y" = 0) - var/turf/step_over = get_step(target_loca, splatter_dir) - - if(get_splatter_blockage(step_over, target, splatter_dir, target_loca)) //If you can't cross the tile or any of its relevant obstacles... - shift = pixel_shift_dir(splatter_dir) //Pixel shift the blood there instead (so you can't see wallsplatter through walls). - else - target_loca = step_over - L.add_splatter_floor(target_loca, shift_x = shift["x"], shift_y = shift["y"]) - if(istype(H)) - for(var/mob/living/carbon/human/M in step_over) //Bloody the mobs who're infront of the spray. - M.bloody_hands(H) - /* Uncomment when bloody_body stops randomly not transferring blood colour. - M.bloody_body(H) */ - else if(impact_effect_type) - new impact_effect_type(target_loca, hitx, hity) - var/organ_hit_text = "" - if(L.has_limbs) - organ_hit_text = " in \the [parse_zone(def_zone)]" - if(suppressed) - playsound(loc, hitsound, 5, 1, -1) - to_chat(L, "You're shot by \a [src][organ_hit_text]!") - else - if(hitsound) - var/volume = vol_by_damage() - playsound(loc, hitsound, volume, 1, -1) - L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ - "[L] is hit by \a [src][organ_hit_text]!") //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter - - var/reagent_note - var/has_reagents = FALSE - if(reagents && reagents.reagent_list) - reagent_note = " REAGENTS:" - for(var/datum/reagent/R in reagents.reagent_list) - reagent_note += R.id + " (" - reagent_note += num2text(R.volume) + ") " - has_reagents = TRUE - if(!log_override && firer && !alwayslog) - if(has_reagents) - add_attack_logs(firer, L, "Shot with a [type] (containing [reagent_note])") - else - add_attack_logs(firer, L, "Shot with a [type]") - return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) - -/obj/item/projectile/proc/get_splatter_blockage(var/turf/step_over, var/atom/target, var/splatter_dir, var/target_loca) //Check whether the place we want to splatter blood is blocked (i.e. by windows). - var/turf/step_cardinal = !(splatter_dir in list(NORTH, SOUTH, EAST, WEST)) ? get_step(target_loca, get_cardinal_dir(target_loca, step_over)) : null - - if(step_over.density && !step_over.CanPass(target, step_over, 1)) //Preliminary simple check. - return TRUE - for(var/atom/movable/border_obstacle in step_over) //Check to see if we're blocked by a (non-full) window or some such. Do deeper investigation if we're splattering blood diagonally. - if(border_obstacle.flags&ON_BORDER && get_dir(step_cardinal ? step_cardinal : target_loca, step_over) == turn(border_obstacle.dir, 180)) - return TRUE - -/obj/item/projectile/proc/vol_by_damage() - if(damage) - return Clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 - else - return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume - -/obj/item/projectile/Bump(atom/A, yes) - if(!yes) //prevents double bumps. - return - - if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max) - ricochets++ - if(A.handle_ricochet(src)) - on_ricochet(A) - ignore_source_check = TRUE - range = initial(range) - return TRUE - if(firer && !ignore_source_check) - if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech - loc = A.loc - return 0 - - var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. - def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. - - if(isturf(A) && hitsound_wall) - var/volume = Clamp(vol_by_damage() + 20, 0, 100) - if(suppressed) - volume = 5 - playsound(loc, hitsound_wall, volume, 1, -1) - else if(ishuman(A)) - var/mob/living/carbon/human/H = A - var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) - if(isnull(organ)) - return - - var/turf/target_turf = get_turf(A) - prehit(A) - var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null - if(permutation == -1 || forcedodge)// the bullet passes through a dense object! - loc = target_turf - if(A) - permutated.Add(A) - return 0 - else - if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. - var/list/mobs_list = list() - for(var/mob/living/L in target_turf) - mobs_list += L - if(mobs_list.len) - var/mob/living/picked_mob = pick(mobs_list) - prehit(picked_mob) - picked_mob.bullet_act(src, def_zone) - qdel(src) - -/obj/item/projectile/Process_Spacemove(var/movement_dir = 0) - return 1 //Bullets don't drift in space - -/obj/item/projectile/proc/fire(var/setAngle) - if(setAngle) - Angle = setAngle - if(!legacy) //new projectiles - set waitfor = 0 - while(loc) - if(!paused) - if((!( current ) || loc == current)) - current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) - if(isnull(Angle)) - Angle=round(Get_Angle(src,current)) - if(spread) - Angle += (rand() - 0.5) * spread - var/matrix/M = new - M.Turn(Angle) - transform = M - - var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) - var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) - var/pixel_x_offset = pixel_x + Pixel_x - var/pixel_y_offset = pixel_y + Pixel_y - var/new_x = x - var/new_y = y - - while(pixel_x_offset > 16) - pixel_x_offset -= 32 - pixel_x -= 32 - new_x++// x++ - while(pixel_x_offset < -16) - pixel_x_offset += 32 - pixel_x += 32 - new_x-- - - while(pixel_y_offset > 16) - pixel_y_offset -= 32 - pixel_y -= 32 - new_y++ - while(pixel_y_offset < -16) - pixel_y_offset += 32 - pixel_y += 32 - new_y-- - - speed = round(speed) - step_towards(src, locate(new_x, new_y, z)) - if(speed <= 1) - pixel_x = pixel_x_offset - pixel_y = pixel_y_offset - else - animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) - - if(original && (original.layer>=2.75) || ismob(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original, 1) - Range() - sleep(max(1, speed)) - else //old projectile system - set waitfor = 0 - while(loc) - if(!paused) - if((!( current ) || loc == current)) - current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) - step_towards(src, current) - if(original && (original.layer>=2.75) || ismob(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original, 1) - Range() - sleep(1) - -obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2)) - if(starting) - var/new_x = starting.x + pick(position_modifiers) - var/new_y = starting.y + pick(position_modifiers) - var/turf/curloc = get_turf(source) - - if(ismob(source)) - firer = source // The reflecting mob will be the new firer - else - firer = null // Reflected by something other than a mob so firer will be null - - // redirect the projectile - original = locate(new_x, new_y, z) - starting = curloc - current = curloc - yo = new_y - curloc.y - xo = new_x - curloc.x - Angle = null // Will be calculated in fire() - -obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. - ..() - if(isliving(AM) && AM.density && !checkpass(PASSMOB)) - Bump(AM, 1) - -/obj/item/projectile/Destroy() - ammo_casing = null - return ..() - -/obj/item/projectile/proc/dumbfire(var/dir) - current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. - fire() - - -/obj/item/projectile/proc/on_ricochet(atom/A) - return - -/obj/item/projectile/proc/check_ricochet() - if(prob(ricochet_chance)) - return TRUE - return FALSE - -/obj/item/projectile/proc/check_ricochet_flag(atom/A) - if(A.flags_2 & CHECK_RICOCHET_2) - return TRUE - return FALSE - -/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides. - Angle = new_angle - return TRUE - -/obj/item/projectile/experience_pressure_difference() - return \ No newline at end of file +/obj/item/projectile + name = "projectile" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bullet" + density = 0 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots. + flags = ABSTRACT + pass_flags = PASSTABLE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + hitsound = 'sound/weapons/pierce.ogg' + var/hitsound_wall = "" + var/def_zone = "" //Aiming at + var/mob/firer = null//Who shot it + var/obj/item/ammo_casing/ammo_casing = null + var/suppressed = 0 //Attack message + var/yo = null + var/xo = null + var/current = null + var/atom/original = null // the original target clicked + var/turf/starting = null // the projectile's starting turf + var/list/permutated = list() // we've passed through these atoms, don't try to hit them again + var/paused = FALSE //for suspending the projectile midair + var/p_x = 16 + var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + var/speed = 1 //Amount of deciseconds it takes for projectile to travel + var/Angle = null + var/spread = 0 //amount (in degrees) of projectile spread + var/legacy = FALSE //legacy projectile system + animate_movement = 0 + + var/ignore_source_check = FALSE + + var/damage = 10 + var/tile_dropoff = 0 //how much damage should be decremented as the bullet moves + var/tile_dropoff_s = 0 //same as above but for stamina + var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here + var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions + var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid + var/projectile_type = "/obj/item/projectile" + var/range = 50 //This will de-increment every step. When 0, it will delete the projectile. + var/is_reflectable = FALSE // Can it be reflected or not? + var/alwayslog = FALSE // ALWAYS log this projectile on hit even if it doesn't hit a living target. Useful for AOE explosion / EMP. + //Effects + var/stun = 0 + var/weaken = 0 + var/paralyze = 0 + var/irradiate = 0 + var/stutter = 0 + var/slur = 0 + var/eyeblur = 0 + var/drowsy = 0 + var/stamina = 0 + var/jitter = 0 + var/forcedodge = 0 //to pass through everything + var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all. + var/impact_effect_type //what type of impact effect to show when hitting something + var/ricochets = 0 + var/ricochets_max = 2 + var/ricochet_chance = 30 + + var/log_override = FALSE //whether print to admin attack logs or just keep it in the diary + +/obj/item/projectile/New() + permutated = list() + return ..() + +/obj/item/projectile/proc/Range() + range-- + if(damage && tile_dropoff) + damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves + if(stamina && tile_dropoff_s) + stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina + if(range <= 0 && loc) + on_range() + if(!damage && !stamina && (tile_dropoff || tile_dropoff_s)) + on_range() + +/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range + qdel(src) + +/obj/item/projectile/proc/prehit(atom/target) + return TRUE + +/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, hit_zone) + var/turf/target_loca = get_turf(target) + var/hitx + var/hity + if(target == original) + hitx = target.pixel_x + p_x - 16 + hity = target.pixel_y + p_y - 16 + else + hitx = target.pixel_x + rand(-8, 8) + hity = target.pixel_y + rand(-8, 8) + if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75)) + var/turf/simulated/wall/W = target_loca + if(impact_effect_type) + new impact_effect_type(target_loca, hitx, hity) + + W.add_dent(WALL_DENT_SHOT, hitx, hity) + return 0 + if(alwayslog) + add_attack_logs(firer, target, "Shot with a [type]") + if(!isliving(target)) + if(impact_effect_type) + new impact_effect_type(target_loca, hitx, hity) + return 0 + var/mob/living/L = target + var/mob/living/carbon/human/H + if(blocked < 100) // not completely blocked + if(damage && L.blood_volume && damage_type == BRUTE) + var/splatter_dir = dir + if(starting) + splatter_dir = get_dir(starting, target_loca) + if(isalien(L)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_loca, splatter_dir) + else + var/blood_color = "#C80000" + if(ishuman(target)) + H = target + blood_color = H.dna.species.blood_color + new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, blood_color) + if(prob(33)) + var/list/shift = list("x" = 0, "y" = 0) + var/turf/step_over = get_step(target_loca, splatter_dir) + + if(get_splatter_blockage(step_over, target, splatter_dir, target_loca)) //If you can't cross the tile or any of its relevant obstacles... + shift = pixel_shift_dir(splatter_dir) //Pixel shift the blood there instead (so you can't see wallsplatter through walls). + else + target_loca = step_over + L.add_splatter_floor(target_loca, shift_x = shift["x"], shift_y = shift["y"]) + if(istype(H)) + for(var/mob/living/carbon/human/M in step_over) //Bloody the mobs who're infront of the spray. + M.bloody_hands(H) + /* Uncomment when bloody_body stops randomly not transferring blood colour. + M.bloody_body(H) */ + else if(impact_effect_type) + new impact_effect_type(target_loca, hitx, hity) + var/organ_hit_text = "" + if(L.has_limbs) + organ_hit_text = " in \the [parse_zone(def_zone)]" + if(suppressed) + playsound(loc, hitsound, 5, 1, -1) + to_chat(L, "You're shot by \a [src][organ_hit_text]!") + else + if(hitsound) + var/volume = vol_by_damage() + playsound(loc, hitsound, volume, 1, -1) + L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ + "[L] is hit by \a [src][organ_hit_text]!") //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + + var/reagent_note + var/has_reagents = FALSE + if(reagents && reagents.reagent_list) + reagent_note = " REAGENTS:" + for(var/datum/reagent/R in reagents.reagent_list) + reagent_note += R.id + " (" + reagent_note += num2text(R.volume) + ") " + has_reagents = TRUE + if(!log_override && firer && !alwayslog) + if(has_reagents) + add_attack_logs(firer, L, "Shot with a [type] (containing [reagent_note])") + else + add_attack_logs(firer, L, "Shot with a [type]") + return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) + +/obj/item/projectile/proc/get_splatter_blockage(var/turf/step_over, var/atom/target, var/splatter_dir, var/target_loca) //Check whether the place we want to splatter blood is blocked (i.e. by windows). + var/turf/step_cardinal = !(splatter_dir in list(NORTH, SOUTH, EAST, WEST)) ? get_step(target_loca, get_cardinal_dir(target_loca, step_over)) : null + + if(step_over.density && !step_over.CanPass(target, step_over, 1)) //Preliminary simple check. + return TRUE + for(var/atom/movable/border_obstacle in step_over) //Check to see if we're blocked by a (non-full) window or some such. Do deeper investigation if we're splattering blood diagonally. + if(border_obstacle.flags&ON_BORDER && get_dir(step_cardinal ? step_cardinal : target_loca, step_over) == turn(border_obstacle.dir, 180)) + return TRUE + +/obj/item/projectile/proc/vol_by_damage() + if(damage) + return Clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 + else + return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume + +/obj/item/projectile/Bump(atom/A, yes) + if(!yes) //prevents double bumps. + return + + if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max) + ricochets++ + if(A.handle_ricochet(src)) + on_ricochet(A) + ignore_source_check = TRUE + range = initial(range) + return TRUE + if(firer && !ignore_source_check) + if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech + loc = A.loc + return 0 + + var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. + def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. + + if(isturf(A) && hitsound_wall) + var/volume = Clamp(vol_by_damage() + 20, 0, 100) + if(suppressed) + volume = 5 + playsound(loc, hitsound_wall, volume, 1, -1) + else if(ishuman(A)) + var/mob/living/carbon/human/H = A + var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) + if(isnull(organ)) + return + + var/turf/target_turf = get_turf(A) + prehit(A) + var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null + if(permutation == -1 || forcedodge)// the bullet passes through a dense object! + loc = target_turf + if(A) + permutated.Add(A) + return 0 + else + if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. + var/list/mobs_list = list() + for(var/mob/living/L in target_turf) + mobs_list += L + if(mobs_list.len) + var/mob/living/picked_mob = pick(mobs_list) + prehit(picked_mob) + picked_mob.bullet_act(src, def_zone) + qdel(src) + +/obj/item/projectile/Process_Spacemove(var/movement_dir = 0) + return 1 //Bullets don't drift in space + +/obj/item/projectile/proc/fire(var/setAngle) + if(setAngle) + Angle = setAngle + if(!legacy) //new projectiles + set waitfor = 0 + while(loc) + if(!paused) + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + if(isnull(Angle)) + Angle=round(Get_Angle(src,current)) + if(spread) + Angle += (rand() - 0.5) * spread + var/matrix/M = new + M.Turn(Angle) + transform = M + + var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) + var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) + var/pixel_x_offset = pixel_x + Pixel_x + var/pixel_y_offset = pixel_y + Pixel_y + var/new_x = x + var/new_y = y + + while(pixel_x_offset > 16) + pixel_x_offset -= 32 + pixel_x -= 32 + new_x++// x++ + while(pixel_x_offset < -16) + pixel_x_offset += 32 + pixel_x += 32 + new_x-- + + while(pixel_y_offset > 16) + pixel_y_offset -= 32 + pixel_y -= 32 + new_y++ + while(pixel_y_offset < -16) + pixel_y_offset += 32 + pixel_y += 32 + new_y-- + + speed = round(speed) + step_towards(src, locate(new_x, new_y, z)) + if(speed <= 1) + pixel_x = pixel_x_offset + pixel_y = pixel_y_offset + else + animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) + + if(original && (original.layer>=2.75) || ismob(original)) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original, 1) + Range() + sleep(max(1, speed)) + else //old projectile system + set waitfor = 0 + while(loc) + if(!paused) + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + step_towards(src, current) + if(original && (original.layer>=2.75) || ismob(original)) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original, 1) + Range() + sleep(1) + +obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2)) + if(starting) + var/new_x = starting.x + pick(position_modifiers) + var/new_y = starting.y + pick(position_modifiers) + var/turf/curloc = get_turf(source) + + if(ismob(source)) + firer = source // The reflecting mob will be the new firer + else + firer = null // Reflected by something other than a mob so firer will be null + + // redirect the projectile + original = locate(new_x, new_y, z) + starting = curloc + current = curloc + yo = new_y - curloc.y + xo = new_x - curloc.x + Angle = null // Will be calculated in fire() + +obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. + ..() + if(isliving(AM) && AM.density && !checkpass(PASSMOB)) + Bump(AM, 1) + +/obj/item/projectile/Destroy() + ammo_casing = null + return ..() + +/obj/item/projectile/proc/dumbfire(var/dir) + current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. + fire() + + +/obj/item/projectile/proc/on_ricochet(atom/A) + return + +/obj/item/projectile/proc/check_ricochet() + if(prob(ricochet_chance)) + return TRUE + return FALSE + +/obj/item/projectile/proc/check_ricochet_flag(atom/A) + if(A.flags_2 & CHECK_RICOCHET_2) + return TRUE + return FALSE + +/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides. + Angle = new_angle + return TRUE + +/obj/item/projectile/experience_pressure_difference() + return diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 814ac3d7e460..13f959be5900 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -1,171 +1,171 @@ -/obj/item/projectile/beam - name = "laser" - icon_state = "laser" - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - damage = 20 - damage_type = BURN - hitsound = 'sound/weapons/sear.ogg' - hitsound_wall = 'sound/weapons/effects/searwall.ogg' - flag = "laser" - eyeblur = 2 - impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - is_reflectable = TRUE - light_range = 2 - light_color = LIGHT_COLOR_RED - ricochets_max = 50 //Honk! - ricochet_chance = 80 - -/obj/item/projectile/beam/laser - -/obj/item/projectile/beam/laser/heavylaser - name = "heavy laser" - icon_state = "heavylaser" - damage = 40 - -/obj/item/projectile/beam/practice - name = "practice laser" - damage = 0 - nodamage = 1 - log_override = TRUE - -/obj/item/projectile/beam/scatter - name = "laser pellet" - icon_state = "scatterlaser" - damage = 5 - -/obj/item/projectile/beam/xray - name = "xray beam" - icon_state = "xray" - damage = 15 - tile_dropoff = 0.75 - irradiate = 30 - forcedodge = 1 - range = 15 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - light_color = LIGHT_COLOR_GREEN - -/obj/item/projectile/beam/disabler - name = "disabler beam" - icon_state = "omnilaser" - damage = 30 - damage_type = STAMINA - flag = "energy" - hitsound = 'sound/weapons/tap.ogg' - eyeblur = 0 - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_CYAN - -/obj/item/projectile/beam/pulse - name = "pulse" - icon_state = "u_laser" - damage = 50 - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_DARKBLUE - -/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0) - if(istype(target,/turf/)||istype(target,/obj/structure/)) - target.ex_act(2) - ..() - -/obj/item/projectile/beam/pulse/shot - damage = 40 - -/obj/item/projectile/beam/emitter - name = "emitter beam" - icon_state = "emitter" - damage = 30 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - light_color = LIGHT_COLOR_GREEN - -/obj/item/projectile/beam/emitter/singularity_pull() - return //don't want the emitters to miss - -/obj/item/projectile/beam/lasertag - name = "laser tag beam" - icon_state = "omnilaser" - hitsound = 'sound/weapons/tap.ogg' - nodamage = 1 - damage_type = STAMINA - flag = "laser" - var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) - log_override = TRUE - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_DARKBLUE - -/obj/item/projectile/beam/lasertag/on_hit(atom/target, blocked = 0) - . = ..() - if(ishuman(target)) - var/mob/living/carbon/human/M = target - if(istype(M.wear_suit)) - if(M.wear_suit.type in suit_types) - M.adjustStaminaLoss(34) - return 1 - -/obj/item/projectile/beam/lasertag/omni - name = "laser tag beam" - icon_state = "omnilaser" - -/obj/item/projectile/beam/lasertag/redtag - icon_state = "laser" - suit_types = list(/obj/item/clothing/suit/bluetag) - impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - light_color = LIGHT_COLOR_RED - -/obj/item/projectile/beam/lasertag/bluetag - icon_state = "bluelaser" - suit_types = list(/obj/item/clothing/suit/redtag) - -/obj/item/projectile/beam/sniper - name = "sniper beam" - icon_state = "sniperlaser" - damage = 60 - stun = 5 - weaken = 5 - stutter = 5 - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - light_color = LIGHT_COLOR_PINK - -/obj/item/projectile/beam/immolator - name = "immolation beam" - -/obj/item/projectile/beam/immolator/strong - name = "heavy immolation beam" - damage = 45 - icon_state = "heavylaser" - -/obj/item/projectile/beam/immolator/weak - name = "light immolation beam" - damage = 8 - icon_state = "scatterlaser" - -/obj/item/projectile/beam/immolator/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(istype(target, /mob/living/carbon)) - var/mob/living/carbon/M = target - M.adjust_fire_stacks(1) - M.IgniteMob() - -/obj/item/projectile/beam/instakill - name = "instagib laser" - icon_state = "purple_laser" - damage = 200 - damage_type = BURN - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - light_color = LIGHT_COLOR_PURPLE - -/obj/item/projectile/beam/instakill/blue - icon_state = "blue_laser" - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_DARKBLUE - -/obj/item/projectile/beam/instakill/red - icon_state = "red_laser" - impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - light_color = LIGHT_COLOR_RED - -/obj/item/projectile/beam/instakill/on_hit(atom/target) - . = ..() - if(isliving(target)) - var/mob/living/L = target - L.visible_message("[L] explodes!") - L.gib() \ No newline at end of file +/obj/item/projectile/beam + name = "laser" + icon_state = "laser" + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + damage = 20 + damage_type = BURN + hitsound = 'sound/weapons/sear.ogg' + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + flag = "laser" + eyeblur = 2 + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + is_reflectable = TRUE + light_range = 2 + light_color = LIGHT_COLOR_RED + ricochets_max = 50 //Honk! + ricochet_chance = 80 + +/obj/item/projectile/beam/laser + +/obj/item/projectile/beam/laser/heavylaser + name = "heavy laser" + icon_state = "heavylaser" + damage = 40 + +/obj/item/projectile/beam/practice + name = "practice laser" + damage = 0 + nodamage = 1 + log_override = TRUE + +/obj/item/projectile/beam/scatter + name = "laser pellet" + icon_state = "scatterlaser" + damage = 5 + +/obj/item/projectile/beam/xray + name = "xray beam" + icon_state = "xray" + damage = 15 + tile_dropoff = 0.75 + irradiate = 30 + forcedodge = 1 + range = 15 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + +/obj/item/projectile/beam/disabler + name = "disabler beam" + icon_state = "omnilaser" + damage = 30 + damage_type = STAMINA + flag = "energy" + hitsound = 'sound/weapons/tap.ogg' + eyeblur = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_CYAN + +/obj/item/projectile/beam/pulse + name = "pulse" + icon_state = "u_laser" + damage = 50 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_DARKBLUE + +/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0) + if(istype(target,/turf/)||istype(target,/obj/structure/)) + target.ex_act(2) + ..() + +/obj/item/projectile/beam/pulse/shot + damage = 40 + +/obj/item/projectile/beam/emitter + name = "emitter beam" + icon_state = "emitter" + damage = 30 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + +/obj/item/projectile/beam/emitter/singularity_pull() + return //don't want the emitters to miss + +/obj/item/projectile/beam/lasertag + name = "laser tag beam" + icon_state = "omnilaser" + hitsound = 'sound/weapons/tap.ogg' + nodamage = 1 + damage_type = STAMINA + flag = "laser" + var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) + log_override = TRUE + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_DARKBLUE + +/obj/item/projectile/beam/lasertag/on_hit(atom/target, blocked = 0) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + if(istype(M.wear_suit)) + if(M.wear_suit.type in suit_types) + M.adjustStaminaLoss(34) + return 1 + +/obj/item/projectile/beam/lasertag/omni + name = "laser tag beam" + icon_state = "omnilaser" + +/obj/item/projectile/beam/lasertag/redtag + icon_state = "laser" + suit_types = list(/obj/item/clothing/suit/bluetag) + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = LIGHT_COLOR_RED + +/obj/item/projectile/beam/lasertag/bluetag + icon_state = "bluelaser" + suit_types = list(/obj/item/clothing/suit/redtag) + +/obj/item/projectile/beam/sniper + name = "sniper beam" + icon_state = "sniperlaser" + damage = 60 + stun = 5 + weaken = 5 + stutter = 5 + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + light_color = LIGHT_COLOR_PINK + +/obj/item/projectile/beam/immolator + name = "immolation beam" + +/obj/item/projectile/beam/immolator/strong + name = "heavy immolation beam" + damage = 45 + icon_state = "heavylaser" + +/obj/item/projectile/beam/immolator/weak + name = "light immolation beam" + damage = 8 + icon_state = "scatterlaser" + +/obj/item/projectile/beam/immolator/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(istype(target, /mob/living/carbon)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(1) + M.IgniteMob() + +/obj/item/projectile/beam/instakill + name = "instagib laser" + icon_state = "purple_laser" + damage = 200 + damage_type = BURN + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + light_color = LIGHT_COLOR_PURPLE + +/obj/item/projectile/beam/instakill/blue + icon_state = "blue_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_DARKBLUE + +/obj/item/projectile/beam/instakill/red + icon_state = "red_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = LIGHT_COLOR_RED + +/obj/item/projectile/beam/instakill/on_hit(atom/target) + . = ..() + if(isliving(target)) + var/mob/living/L = target + L.visible_message("[L] explodes!") + L.gib() diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 6303178217c5..2323aa06cf4a 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -1,300 +1,300 @@ -/obj/item/projectile/bullet - name = "bullet" - icon_state = "bullet" - damage = 60 - damage_type = BRUTE - flag = "bullet" - hitsound_wall = "ricochet" - impact_effect_type = /obj/effect/temp_visual/impact_effect - -/obj/item/projectile/bullet/weakbullet //beanbag, heavy stamina damage - name = "beanbag slug" - damage = 5 - stamina = 80 - -/obj/item/projectile/bullet/weakbullet/booze - -/obj/item/projectile/bullet/weakbullet/booze/on_hit(atom/target, blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.AdjustDizzy(20) - M.AdjustSlur(20) - M.AdjustConfused(20) - M.AdjustEyeBlurry(20) - M.AdjustDrowsy(20) - for(var/datum/reagent/consumable/ethanol/A in M.reagents.reagent_list) - M.AdjustParalysis(2) - M.AdjustDizzy(10) - M.AdjustSlur(10) - M.AdjustConfused(10) - M.AdjustEyeBlurry(10) - M.AdjustDrowsy(10) - A.volume += 5 //Because we can - -/obj/item/projectile/bullet/weakbullet2 //detective revolver instastuns, but multiple shots are better for keeping punks down - name = "rubber bullet" - damage = 5 - weaken = 3 - stamina = 60 - icon_state = "bullet-r" - -/obj/item/projectile/bullet/weakbullet2/invisible //finger gun bullets - name = "invisible bullet" - damage = 0 - icon_state = null - hitsound_wall = null - -/obj/item/projectile/bullet/weakbullet2/invisible/fake - weaken = 0 - stamina = 0 - nodamage = 1 - log_override = TRUE - -/obj/item/projectile/bullet/weakbullet3 - damage = 20 - -/obj/item/projectile/bullet/weakbullet4 - name = "rubber bullet" - damage = 5 - stamina = 30 - icon_state = "bullet-r" - -/obj/item/projectile/bullet/toxinbullet - damage = 15 - damage_type = TOX - -/obj/item/projectile/bullet/incendiary - -/obj/item/projectile/bullet/incendiary/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(iscarbon(target)) - var/mob/living/carbon/M = target - M.adjust_fire_stacks(4) - M.IgniteMob() - -/obj/item/projectile/bullet/incendiary/firebullet - damage = 10 - -/obj/item/projectile/bullet/armourpiercing - damage = 17 - armour_penetration = 10 - -/obj/item/projectile/bullet/pellet - name = "pellet" - damage = 12.5 - tile_dropoff = 0.75 - tile_dropoff_s = 1.25 - -/obj/item/projectile/bullet/pellet/rubber - name = "rubber pellet" - damage = 3 - stamina = 25 - icon_state = "bullet-r" - -/obj/item/projectile/bullet/pellet/weak - tile_dropoff = 0.55 //Come on it does 6 damage don't be like that. - damage = 6 - -/obj/item/projectile/bullet/pellet/weak/New() - range = rand(1, 8) - ..() - -/obj/item/projectile/bullet/pellet/weak/on_range() - do_sparks(1, 1, src) - ..() - -/obj/item/projectile/bullet/pellet/overload - damage = 3 - -/obj/item/projectile/bullet/pellet/overload/New() - range = rand(1, 10) - ..() - -/obj/item/projectile/bullet/pellet/assassination - damage = 12 - tile_dropoff = 1 // slightly less damage and greater damage falloff compared to normal buckshot - -/obj/item/projectile/bullet/pellet/assassination/on_hit(atom/target, blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.AdjustSilence(2) // HELP MIME KILLING ME IN MAINT - -/obj/item/projectile/bullet/pellet/overload/on_hit(atom/target, blocked = 0) - ..() - explosion(target, 0, 0, 2) - -/obj/item/projectile/bullet/pellet/overload/on_range() - explosion(src, 0, 0, 2) - do_sparks(3, 3, src) - ..() - -/obj/item/projectile/bullet/midbullet - damage = 20 - stamina = 65 //two rounds from the c20r knocks people down - -/obj/item/projectile/bullet/midbullet_r - damage = 5 - stamina = 75 //Still two rounds to knock people down - -/obj/item/projectile/bullet/midbullet2 - damage = 25 - -/obj/item/projectile/bullet/midbullet3 - damage = 30 - -/obj/item/projectile/bullet/midbullet3/hp - damage = 40 - armour_penetration = -50 - -/obj/item/projectile/bullet/midbullet3/ap - damage = 27 - armour_penetration = 40 - -/obj/item/projectile/bullet/midbullet3/fire/on_hit(atom/target, blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.adjust_fire_stacks(1) - M.IgniteMob() - -/obj/item/projectile/bullet/heavybullet - damage = 35 - -/obj/item/projectile/bullet/stunshot//taser slugs for shotguns, nothing special - name = "stunshot" - damage = 5 - stun = 5 - weaken = 5 - stutter = 5 - jitter = 20 - range = 7 - icon_state = "spark" - color = "#FFFF00" - -/obj/item/projectile/bullet/incendiary/shell - name = "incendiary slug" - damage = 20 - -/obj/item/projectile/bullet/incendiary/shell/Move() - ..() - var/turf/location = get_turf(src) - if(location) - new /obj/effect/hotspot(location) - location.hotspot_expose(700, 50, 1) - -/obj/item/projectile/bullet/incendiary/shell/dragonsbreath - name = "dragonsbreath round" - damage = 5 - -/obj/item/projectile/bullet/meteorshot - name = "meteor" - icon = 'icons/obj/meteor.dmi' - icon_state = "dust" - damage = 30 - weaken = 8 - stun = 8 - hitsound = 'sound/effects/meteorimpact.ogg' - -/obj/item/projectile/bullet/meteorshot/on_hit(var/atom/target, var/blocked = 0) - ..() - if(istype(target, /atom/movable)) - var/atom/movable/M = target - var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) - M.throw_at(throw_target, 3, 2) - -/obj/item/projectile/bullet/meteorshot/New() - ..() - SpinAnimation() - -/obj/item/projectile/bullet/meteorshot/weak - damage = 10 - weaken = 4 - stun = 4 - -/obj/item/projectile/bullet/mime - damage = 0 - stun = 5 - weaken = 5 - slur = 20 - stutter = 20 - -/obj/item/projectile/bullet/mime/on_hit(var/atom/target, var/blocked = 0) - ..(target, blocked) - if(iscarbon(target)) - var/mob/living/carbon/M = target - M.Silence(10) - else if(istype(target, /obj/mecha/combat/honker)) - var/obj/mecha/chassis = target - chassis.occupant_message("A mimetech anti-honk bullet has hit \the [chassis]!") - chassis.use_power(chassis.get_charge() / 2) - for(var/obj/item/mecha_parts/mecha_equipment/weapon/honker in chassis.equipment) - honker.set_ready_state(0) - -/obj/item/projectile/bullet/dart - name = "dart" - icon_state = "cbbolt" - damage = 6 - var/piercing = FALSE - -/obj/item/projectile/bullet/dart/New() - ..() - create_reagents(50) - reagents.set_reacting(FALSE) - -/obj/item/projectile/bullet/dart/on_hit(var/atom/target, var/blocked = 0, var/hit_zone) - if(iscarbon(target)) - var/mob/living/carbon/M = target - if(blocked != 100) - if(M.can_inject(null, FALSE, hit_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. - ..() - reagents.trans_to(M, reagents.total_volume) - return 1 - else - blocked = 100 - target.visible_message("The [name] was deflected!", \ - "You were protected against the [name]!") - ..(target, blocked, hit_zone) - reagents.set_reacting(TRUE) - reagents.handle_reactions() - return 1 - -/obj/item/projectile/bullet/dart/metalfoam - -/obj/item/projectile/bullet/dart/metalfoam/New() - ..() - reagents.add_reagent("aluminum", 15) - reagents.add_reagent("fluorosurfactant", 5) - reagents.add_reagent("sacid", 5) - -//This one is for future syringe guns update -/obj/item/projectile/bullet/dart/syringe - name = "syringe" - icon = 'icons/obj/chemical.dmi' - icon_state = "syringeproj" - -/obj/item/projectile/bullet/dart/syringe/tranquilizer - -/obj/item/projectile/bullet/dart/syringe/tranquilizer/New() - ..() - reagents.add_reagent("haloperidol", 15) - -/obj/item/projectile/bullet/neurotoxin - name = "neurotoxin spit" - icon_state = "neurotoxin" - damage = 5 - damage_type = TOX - weaken = 5 - -/obj/item/projectile/bullet/neurotoxin/on_hit(var/atom/target, var/blocked = 0) - if(isalien(target)) - weaken = 0 - nodamage = 1 - . = ..() // Execute the rest of the code. - -/obj/item/projectile/bullet/cap - name = "cap" - damage = 0 - nodamage = 1 - -/obj/item/projectile/bullet/cap/fire() - loc = null - qdel(src) +/obj/item/projectile/bullet + name = "bullet" + icon_state = "bullet" + damage = 60 + damage_type = BRUTE + flag = "bullet" + hitsound_wall = "ricochet" + impact_effect_type = /obj/effect/temp_visual/impact_effect + +/obj/item/projectile/bullet/weakbullet //beanbag, heavy stamina damage + name = "beanbag slug" + damage = 5 + stamina = 80 + +/obj/item/projectile/bullet/weakbullet/booze + +/obj/item/projectile/bullet/weakbullet/booze/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.AdjustDizzy(20) + M.AdjustSlur(20) + M.AdjustConfused(20) + M.AdjustEyeBlurry(20) + M.AdjustDrowsy(20) + for(var/datum/reagent/consumable/ethanol/A in M.reagents.reagent_list) + M.AdjustParalysis(2) + M.AdjustDizzy(10) + M.AdjustSlur(10) + M.AdjustConfused(10) + M.AdjustEyeBlurry(10) + M.AdjustDrowsy(10) + A.volume += 5 //Because we can + +/obj/item/projectile/bullet/weakbullet2 //detective revolver instastuns, but multiple shots are better for keeping punks down + name = "rubber bullet" + damage = 5 + weaken = 3 + stamina = 60 + icon_state = "bullet-r" + +/obj/item/projectile/bullet/weakbullet2/invisible //finger gun bullets + name = "invisible bullet" + damage = 0 + icon_state = null + hitsound_wall = null + +/obj/item/projectile/bullet/weakbullet2/invisible/fake + weaken = 0 + stamina = 0 + nodamage = 1 + log_override = TRUE + +/obj/item/projectile/bullet/weakbullet3 + damage = 20 + +/obj/item/projectile/bullet/weakbullet4 + name = "rubber bullet" + damage = 5 + stamina = 30 + icon_state = "bullet-r" + +/obj/item/projectile/bullet/toxinbullet + damage = 15 + damage_type = TOX + +/obj/item/projectile/bullet/incendiary + +/obj/item/projectile/bullet/incendiary/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(4) + M.IgniteMob() + +/obj/item/projectile/bullet/incendiary/firebullet + damage = 10 + +/obj/item/projectile/bullet/armourpiercing + damage = 17 + armour_penetration = 10 + +/obj/item/projectile/bullet/pellet + name = "pellet" + damage = 12.5 + tile_dropoff = 0.75 + tile_dropoff_s = 1.25 + +/obj/item/projectile/bullet/pellet/rubber + name = "rubber pellet" + damage = 3 + stamina = 25 + icon_state = "bullet-r" + +/obj/item/projectile/bullet/pellet/weak + tile_dropoff = 0.55 //Come on it does 6 damage don't be like that. + damage = 6 + +/obj/item/projectile/bullet/pellet/weak/New() + range = rand(1, 8) + ..() + +/obj/item/projectile/bullet/pellet/weak/on_range() + do_sparks(1, 1, src) + ..() + +/obj/item/projectile/bullet/pellet/overload + damage = 3 + +/obj/item/projectile/bullet/pellet/overload/New() + range = rand(1, 10) + ..() + +/obj/item/projectile/bullet/pellet/assassination + damage = 12 + tile_dropoff = 1 // slightly less damage and greater damage falloff compared to normal buckshot + +/obj/item/projectile/bullet/pellet/assassination/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.AdjustSilence(2) // HELP MIME KILLING ME IN MAINT + +/obj/item/projectile/bullet/pellet/overload/on_hit(atom/target, blocked = 0) + ..() + explosion(target, 0, 0, 2) + +/obj/item/projectile/bullet/pellet/overload/on_range() + explosion(src, 0, 0, 2) + do_sparks(3, 3, src) + ..() + +/obj/item/projectile/bullet/midbullet + damage = 20 + stamina = 65 //two rounds from the c20r knocks people down + +/obj/item/projectile/bullet/midbullet_r + damage = 5 + stamina = 75 //Still two rounds to knock people down + +/obj/item/projectile/bullet/midbullet2 + damage = 25 + +/obj/item/projectile/bullet/midbullet3 + damage = 30 + +/obj/item/projectile/bullet/midbullet3/hp + damage = 40 + armour_penetration = -50 + +/obj/item/projectile/bullet/midbullet3/ap + damage = 27 + armour_penetration = 40 + +/obj/item/projectile/bullet/midbullet3/fire/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.adjust_fire_stacks(1) + M.IgniteMob() + +/obj/item/projectile/bullet/heavybullet + damage = 35 + +/obj/item/projectile/bullet/stunshot//taser slugs for shotguns, nothing special + name = "stunshot" + damage = 5 + stun = 5 + weaken = 5 + stutter = 5 + jitter = 20 + range = 7 + icon_state = "spark" + color = "#FFFF00" + +/obj/item/projectile/bullet/incendiary/shell + name = "incendiary slug" + damage = 20 + +/obj/item/projectile/bullet/incendiary/shell/Move() + ..() + var/turf/location = get_turf(src) + if(location) + new /obj/effect/hotspot(location) + location.hotspot_expose(700, 50, 1) + +/obj/item/projectile/bullet/incendiary/shell/dragonsbreath + name = "dragonsbreath round" + damage = 5 + +/obj/item/projectile/bullet/meteorshot + name = "meteor" + icon = 'icons/obj/meteor.dmi' + icon_state = "dust" + damage = 30 + weaken = 8 + stun = 8 + hitsound = 'sound/effects/meteorimpact.ogg' + +/obj/item/projectile/bullet/meteorshot/on_hit(var/atom/target, var/blocked = 0) + ..() + if(istype(target, /atom/movable)) + var/atom/movable/M = target + var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) + M.throw_at(throw_target, 3, 2) + +/obj/item/projectile/bullet/meteorshot/New() + ..() + SpinAnimation() + +/obj/item/projectile/bullet/meteorshot/weak + damage = 10 + weaken = 4 + stun = 4 + +/obj/item/projectile/bullet/mime + damage = 0 + stun = 5 + weaken = 5 + slur = 20 + stutter = 20 + +/obj/item/projectile/bullet/mime/on_hit(var/atom/target, var/blocked = 0) + ..(target, blocked) + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.Silence(10) + else if(istype(target, /obj/mecha/combat/honker)) + var/obj/mecha/chassis = target + chassis.occupant_message("A mimetech anti-honk bullet has hit \the [chassis]!") + chassis.use_power(chassis.get_charge() / 2) + for(var/obj/item/mecha_parts/mecha_equipment/weapon/honker in chassis.equipment) + honker.set_ready_state(0) + +/obj/item/projectile/bullet/dart + name = "dart" + icon_state = "cbbolt" + damage = 6 + var/piercing = FALSE + +/obj/item/projectile/bullet/dart/New() + ..() + create_reagents(50) + reagents.set_reacting(FALSE) + +/obj/item/projectile/bullet/dart/on_hit(var/atom/target, var/blocked = 0, var/hit_zone) + if(iscarbon(target)) + var/mob/living/carbon/M = target + if(blocked != 100) + if(M.can_inject(null, FALSE, hit_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. + ..() + reagents.trans_to(M, reagents.total_volume) + return 1 + else + blocked = 100 + target.visible_message("The [name] was deflected!", \ + "You were protected against the [name]!") + ..(target, blocked, hit_zone) + reagents.set_reacting(TRUE) + reagents.handle_reactions() + return 1 + +/obj/item/projectile/bullet/dart/metalfoam + +/obj/item/projectile/bullet/dart/metalfoam/New() + ..() + reagents.add_reagent("aluminum", 15) + reagents.add_reagent("fluorosurfactant", 5) + reagents.add_reagent("sacid", 5) + +//This one is for future syringe guns update +/obj/item/projectile/bullet/dart/syringe + name = "syringe" + icon = 'icons/obj/chemical.dmi' + icon_state = "syringeproj" + +/obj/item/projectile/bullet/dart/syringe/tranquilizer + +/obj/item/projectile/bullet/dart/syringe/tranquilizer/New() + ..() + reagents.add_reagent("haloperidol", 15) + +/obj/item/projectile/bullet/neurotoxin + name = "neurotoxin spit" + icon_state = "neurotoxin" + damage = 5 + damage_type = TOX + weaken = 5 + +/obj/item/projectile/bullet/neurotoxin/on_hit(var/atom/target, var/blocked = 0) + if(isalien(target)) + weaken = 0 + nodamage = 1 + . = ..() // Execute the rest of the code. + +/obj/item/projectile/bullet/cap + name = "cap" + damage = 0 + nodamage = 1 + +/obj/item/projectile/bullet/cap/fire() + loc = null + qdel(src) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index e1ca470c95a5..1781e1970eb8 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -1,97 +1,97 @@ -/obj/item/projectile/energy - name = "energy" - icon_state = "spark" - damage = 0 - damage_type = BURN - flag = "energy" - is_reflectable = TRUE - -/obj/item/projectile/energy/electrode - name = "electrode" - icon_state = "spark" - color = "#FFFF00" - nodamage = 1 - stun = 5 - weaken = 5 - stutter = 5 - jitter = 20 - hitsound = 'sound/weapons/tase.ogg' - range = 7 - //Damage will be handled on the MOB side, to prevent window shattering. - -/obj/item/projectile/energy/electrode/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks! - do_sparks(1, 1, src) - else if(iscarbon(target)) - var/mob/living/carbon/C = target - if(HULK in C.mutations) - C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - else if(C.status_flags & CANWEAKEN) - spawn(5) - C.do_jitter_animation(jitter) - -/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet - do_sparks(1, 1, src) - ..() - -/obj/item/projectile/energy/declone - name = "declone" - icon_state = "declone" - damage = 20 - damage_type = CLONE - irradiate = 10 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - -/obj/item/projectile/energy/dart - name = "dart" - icon_state = "toxin" - damage = 5 - damage_type = TOX - weaken = 5 - range = 7 - -/obj/item/projectile/energy/shuriken - name = "shuriken" - icon_state = "toxin" - damage = 10 - damage_type = TOX - weaken = 5 - stutter = 5 - -/obj/item/projectile/energy/bolt - name = "bolt" - icon_state = "cbbolt" - damage = 15 - damage_type = TOX - nodamage = 0 - weaken = 5 - stutter = 5 - -/obj/item/projectile/energy/bolt/large - damage = 20 - -/obj/item/projectile/energy/shock_revolver - name = "shock bolt" - icon_state = "purple_laser" - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - var/chain - -/obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") - ..() - var/obj/item/projectile/energy/shock_revolver/P = BB - spawn(1) - P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) - -/obj/item/projectile/energy/shock_revolver/on_hit(atom/target) - . = ..() - if(isliving(target)) - tesla_zap(src, 3, 10000) - qdel(chain) - -/obj/item/projectile/energy/toxplasma - name = "plasma bolt" - icon_state = "energy" - damage = 20 - damage_type = TOX - irradiate = 20 +/obj/item/projectile/energy + name = "energy" + icon_state = "spark" + damage = 0 + damage_type = BURN + flag = "energy" + is_reflectable = TRUE + +/obj/item/projectile/energy/electrode + name = "electrode" + icon_state = "spark" + color = "#FFFF00" + nodamage = 1 + stun = 5 + weaken = 5 + stutter = 5 + jitter = 20 + hitsound = 'sound/weapons/tase.ogg' + range = 7 + //Damage will be handled on the MOB side, to prevent window shattering. + +/obj/item/projectile/energy/electrode/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks! + do_sparks(1, 1, src) + else if(iscarbon(target)) + var/mob/living/carbon/C = target + if(HULK in C.mutations) + C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + else if(C.status_flags & CANWEAKEN) + spawn(5) + C.do_jitter_animation(jitter) + +/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet + do_sparks(1, 1, src) + ..() + +/obj/item/projectile/energy/declone + name = "declone" + icon_state = "declone" + damage = 20 + damage_type = CLONE + irradiate = 10 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + +/obj/item/projectile/energy/dart + name = "dart" + icon_state = "toxin" + damage = 5 + damage_type = TOX + weaken = 5 + range = 7 + +/obj/item/projectile/energy/shuriken + name = "shuriken" + icon_state = "toxin" + damage = 10 + damage_type = TOX + weaken = 5 + stutter = 5 + +/obj/item/projectile/energy/bolt + name = "bolt" + icon_state = "cbbolt" + damage = 15 + damage_type = TOX + nodamage = 0 + weaken = 5 + stutter = 5 + +/obj/item/projectile/energy/bolt/large + damage = 20 + +/obj/item/projectile/energy/shock_revolver + name = "shock bolt" + icon_state = "purple_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + var/chain + +/obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") + ..() + var/obj/item/projectile/energy/shock_revolver/P = BB + spawn(1) + P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) + +/obj/item/projectile/energy/shock_revolver/on_hit(atom/target) + . = ..() + if(isliving(target)) + tesla_zap(src, 3, 10000) + qdel(chain) + +/obj/item/projectile/energy/toxplasma + name = "plasma bolt" + icon_state = "energy" + damage = 20 + damage_type = TOX + irradiate = 20 diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 76d7c513bccd..dc70eda9cec2 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -263,12 +263,14 @@ return M.create_attack_log("[key_name(M)] became [new_mob.real_name].") - new_mob.attack_log = M.attack_log + add_attack_logs(null, M, "became [new_mob.real_name]", ATKLOG_ALL) new_mob.a_intent = INTENT_HARM if(M.mind) M.mind.transfer_to(new_mob) else + new_mob.attack_log_old = M.attack_log_old.Copy() + new_mob.logs = M.logs.Copy() new_mob.key = M.key to_chat(new_mob, "Your form morphs into that of a [randomize].") @@ -283,7 +285,7 @@ /obj/item/projectile/magic/animate/Bump(var/atom/change) ..() - if(istype(change, /obj/item) || istype(change, /obj/structure) && !is_type_in_list(change, protected_objects)) + if(istype(change, /obj/item) || istype(change, /obj/structure) && !is_type_in_list(change, GLOB.protected_objects)) if(istype(change, /obj/structure/closet/statue)) for(var/mob/living/carbon/human/H in change.contents) var/mob/living/simple_animal/hostile/statue/S = new /mob/living/simple_animal/hostile/statue(change.loc, firer) @@ -344,4 +346,4 @@ to_chat(target, "You get splatted by [src].") M.Weaken(slip_weaken) M.Stun(slip_stun) - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/reusable.dm index bd2490379465..244100c31206 100644 --- a/code/modules/projectiles/projectile/reusable.dm +++ b/code/modules/projectiles/projectile/reusable.dm @@ -77,4 +77,4 @@ icon_state = "foamdartsniper_riot" ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot stamina = 100 - log_override = FALSE \ No newline at end of file + log_override = FALSE diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 06ab5c9a61b2..1f82155b7eee 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -1,348 +1,348 @@ -/obj/item/projectile/ion - name = "ion bolt" - icon_state = "ion" - damage = 0 - alwayslog = TRUE - damage_type = BURN - nodamage = 1 - impact_effect_type = /obj/effect/temp_visual/impact_effect/ion - flag = "energy" - -/obj/item/projectile/ion/on_hit(var/atom/target, var/blocked = 0) - ..() - empulse(target, 1, 1, 1, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/ion/weak - -/obj/item/projectile/ion/weak/on_hit(atom/target, blocked = 0) - ..() - empulse(target, 0, 0, 1, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/bullet/gyro - name ="explosive bolt" - icon_state= "bolter" - damage = 50 - alwayslog = TRUE - flag = "bullet" - -/obj/item/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) - ..() - explosion(target, -1, 0, 2, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/bullet/a40mm - name ="40mm grenade" - desc = "USE A WEEL GUN" - icon_state= "bolter" - alwayslog = TRUE - damage = 60 - flag = "bullet" - -/obj/item/projectile/bullet/a40mm/on_hit(atom/target, blocked = 0) - ..() - explosion(target, -1, 0, 2, 1, 0, flame_range = 3, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/temp - name = "temperature beam" - icon_state = "temp_4" - damage = 0 - damage_type = BURN - nodamage = 1 - flag = "energy" - var/temperature = 300 - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - -/obj/item/projectile/temp/New(loc, shot_temp) - ..() - if(!isnull(shot_temp)) - temperature = shot_temp - switch(temperature) - if(501 to INFINITY) - name = "searing beam" //if emagged - icon_state = "temp_8" - if(400 to 500) - name = "burning beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_2 - icon_state = "temp_7" - if(360 to 400) - name = "hot beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_1 - icon_state = "temp_6" - if(335 to 360) - name = "warm beam" //temp at which players get notified of their high body temp - icon_state = "temp_5" - if(295 to 335) - name = "ambient beam" - icon_state = "temp_4" - if(260 to 295) - name = "cool beam" //temp at which players get notified of their low body temp - icon_state = "temp_3" - if(200 to 260) - name = "cold beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_1 - icon_state = "temp_2" - if(120 to 260) - name = "ice beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_2 - icon_state = "temp_1" - if(-INFINITY to 120) - name = "freeze beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_3 - icon_state = "temp_0" - else - name = "temperature beam"//failsafe - icon_state = "temp_4" - - -/obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob - ..() - if(isliving(target)) - var/mob/living/M = target - M.bodytemperature = temperature - if(temperature > 500)//emagged - M.adjust_fire_stacks(0.5) - M.IgniteMob() - playsound(M.loc, 'sound/effects/bamf.ogg', 50, 0) - return 1 - -/obj/item/projectile/meteor - name = "meteor" - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - damage = 0 - damage_type = BRUTE - nodamage = 1 - flag = "bullet" - -/obj/item/projectile/meteor/Bump(atom/A, yes) - if(yes) - return - if(A == firer) - loc = A.loc - return - playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) - for(var/mob/M in urange(10, src)) - if(!M.stat) - shake_camera(M, 3, 1) - qdel(src) - -/obj/item/projectile/energy/floramut - name = "alpha somatoray" - icon_state = "energy" - damage = 0 - damage_type = TOX - nodamage = 1 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - flag = "energy" - -/obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) - ..() - var/mob/living/M = target - if(ishuman(target)) - var/mob/living/carbon/human/H = M - if(IS_PLANT in H.dna.species.species_traits) - if(prob(15)) - M.apply_effect((rand(30,80)),IRRADIATE) - M.Weaken(5) - M.visible_message("[M] writhes in pain as [M.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.") - if(prob(80)) - randmutb(M) - domutcheck(M,null) - else - randmutg(M) - domutcheck(M,null) - else - M.adjustFireLoss(rand(5,15)) - M.show_message("The radiation beam singes you!") - else if(iscarbon(target)) - M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 - -/obj/item/projectile/energy/florayield - name = "beta somatoray" - icon_state = "energy2" - damage = 0 - damage_type = TOX - nodamage = 1 - flag = "energy" - -/obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) - ..() - var/mob/M = target - if(ishuman(target)) //These rays make plantmen fat. - var/mob/living/carbon/human/H = M - if(IS_PLANT in H.dna.species.species_traits) - H.set_nutrition(min(H.nutrition+30, NUTRITION_LEVEL_FULL)) - else if(iscarbon(target)) - M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 - - -/obj/item/projectile/beam/mindflayer - name = "flayer ray" - -/obj/item/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(ishuman(target)) - var/mob/living/carbon/human/M = target - M.adjustBrainLoss(20) - M.AdjustHallucinate(20) - -/obj/item/projectile/clown - name = "snap-pop" - icon = 'icons/obj/toy.dmi' - icon_state = "snappop" - -/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area) - do_sparks(3, 1, src) - new /obj/effect/decal/cleanable/ash(loc) - visible_message("The [name] explodes!","You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) - -/obj/item/projectile/beam/wormhole - name = "bluespace beam" - icon_state = "spark" - hitsound = "sparks" - damage = 0 - var/obj/item/gun/energy/wormhole_projector/gun - color = "#33CCFF" - nodamage = TRUE - -/obj/item/projectile/beam/wormhole/orange - name = "orange bluespace beam" - color = "#FF6600" - -/obj/item/projectile/beam/wormhole/New(var/obj/item/ammo_casing/energy/wormhole/casing) - if(casing) - gun = casing.gun - -/obj/item/projectile/beam/wormhole/on_hit(atom/target) - if(ismob(target)) - if(is_teleport_allowed(target.z)) - var/turf/portal_destination = pick(orange(6, src)) - do_teleport(target, portal_destination) - return ..() - if(!gun) - qdel(src) - gun.create_portal(src) - -/obj/item/projectile/bullet/frag12 - name ="explosive slug" - damage = 25 - weaken = 5 - alwayslog = TRUE - -/obj/item/projectile/bullet/frag12/on_hit(atom/target, blocked = 0) - ..() - explosion(target, -1, 0, 1) - return 1 - -/obj/item/projectile/plasma - name = "plasma blast" - icon_state = "plasmacutter" - damage_type = BRUTE - damage = 5 - range = 3 - dismemberment = 20 - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - -/obj/item/projectile/plasma/on_hit(atom/target) - . = ..() - if(ismineralturf(target)) - forcedodge = 1 - var/turf/simulated/mineral/M = target - M.gets_drilled(firer) - else - forcedodge = 0 - -/obj/item/projectile/plasma/adv - damage = 7 - range = 5 - -/obj/item/projectile/plasma/adv/mech - damage = 10 - range = 9 - -/obj/item/projectile/energy/teleport - name = "teleportation burst" - icon_state = "bluespace" - damage = 0 - nodamage = 1 - alwayslog = TRUE - var/teleport_target = null - -/obj/item/projectile/energy/teleport/New(loc, tele_target) - ..(loc) - if(tele_target) - teleport_target = tele_target - -/obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) - if(isliving(target)) - if(teleport_target) - do_teleport(target, teleport_target, 0)//teleport what's in the tile to the beacon - else - do_teleport(target, target, 15) //Otherwise it just warps you off somewhere. - add_attack_logs(firer, target, "Shot with a [type] [teleport_target ? "(Destination: [teleport_target])" : ""]") - -/obj/item/projectile/snowball - name = "snowball" - icon_state = "snowball" - hitsound = 'sound/items/dodgeball.ogg' - damage = 4 - damage_type = BURN - -/obj/item/projectile/snowball/on_hit(atom/target) //chilling - . = ..() - if(istype(target, /mob/living)) - var/mob/living/M = target - M.bodytemperature = max(0, M.bodytemperature - 50) //each hit will drop your body temp, so don't get surrounded! - M.ExtinguishMob() //bright side, they counter being on fire! - -/obj/item/projectile/ornament - name = "ornament" - icon_state = "ornament-1" - hitsound = 'sound/effects/glasshit.ogg' - damage = 7 - damage_type = BRUTE - -/obj/item/projectile/ornament/New() - icon_state = pick("ornament-1", "ornament-2") - ..() - -/obj/item/projectile/ornament/on_hit(atom/target) //knockback - ..() - if(istype(target, /turf)) - return 0 - var/obj/T = target - var/throwdir = get_dir(firer,target) - T.throw_at(get_edge_target_turf(target, throwdir),10,10) - return 1 - -/obj/item/projectile/mimic - name = "googly-eyed gun" - hitsound = 'sound/weapons/genhit1.ogg' - damage = 0 - nodamage = 1 - damage_type = BURN - flag = "melee" - var/obj/item/gun/stored_gun - -/obj/item/projectile/mimic/New(loc, mimic_type) - ..(loc) - if(mimic_type) - stored_gun = new mimic_type(src) - icon = stored_gun.icon - icon_state = stored_gun.icon_state - overlays = stored_gun.overlays - SpinAnimation(20, -1) - -/obj/item/projectile/mimic/on_hit(atom/target) - ..() - var/turf/T = get_turf(src) - var/obj/item/gun/G = stored_gun - stored_gun = null - G.forceMove(T) - var/mob/living/simple_animal/hostile/mimic/copy/ranged/R = new /mob/living/simple_animal/hostile/mimic/copy/ranged(T, G, firer) - if(ismob(target)) - R.target = target +/obj/item/projectile/ion + name = "ion bolt" + icon_state = "ion" + damage = 0 + alwayslog = TRUE + damage_type = BURN + nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + flag = "energy" + +/obj/item/projectile/ion/on_hit(var/atom/target, var/blocked = 0) + ..() + empulse(target, 1, 1, 1, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/ion/weak + +/obj/item/projectile/ion/weak/on_hit(atom/target, blocked = 0) + ..() + empulse(target, 0, 0, 1, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/bullet/gyro + name ="explosive bolt" + icon_state= "bolter" + damage = 50 + alwayslog = TRUE + flag = "bullet" + +/obj/item/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) + ..() + explosion(target, -1, 0, 2, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/bullet/a40mm + name ="40mm grenade" + desc = "USE A WEEL GUN" + icon_state= "bolter" + alwayslog = TRUE + damage = 60 + flag = "bullet" + +/obj/item/projectile/bullet/a40mm/on_hit(atom/target, blocked = 0) + ..() + explosion(target, -1, 0, 2, 1, 0, flame_range = 3, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/temp + name = "temperature beam" + icon_state = "temp_4" + damage = 0 + damage_type = BURN + nodamage = 1 + flag = "energy" + var/temperature = 300 + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + +/obj/item/projectile/temp/New(loc, shot_temp) + ..() + if(!isnull(shot_temp)) + temperature = shot_temp + switch(temperature) + if(501 to INFINITY) + name = "searing beam" //if emagged + icon_state = "temp_8" + if(400 to 500) + name = "burning beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_2 + icon_state = "temp_7" + if(360 to 400) + name = "hot beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_1 + icon_state = "temp_6" + if(335 to 360) + name = "warm beam" //temp at which players get notified of their high body temp + icon_state = "temp_5" + if(295 to 335) + name = "ambient beam" + icon_state = "temp_4" + if(260 to 295) + name = "cool beam" //temp at which players get notified of their low body temp + icon_state = "temp_3" + if(200 to 260) + name = "cold beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_1 + icon_state = "temp_2" + if(120 to 260) + name = "ice beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_2 + icon_state = "temp_1" + if(-INFINITY to 120) + name = "freeze beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_3 + icon_state = "temp_0" + else + name = "temperature beam"//failsafe + icon_state = "temp_4" + + +/obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob + ..() + if(isliving(target)) + var/mob/living/M = target + M.bodytemperature = temperature + if(temperature > 500)//emagged + M.adjust_fire_stacks(0.5) + M.IgniteMob() + playsound(M.loc, 'sound/effects/bamf.ogg', 50, 0) + return 1 + +/obj/item/projectile/meteor + name = "meteor" + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + damage = 0 + damage_type = BRUTE + nodamage = 1 + flag = "bullet" + +/obj/item/projectile/meteor/Bump(atom/A, yes) + if(yes) + return + if(A == firer) + loc = A.loc + return + playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) + for(var/mob/M in urange(10, src)) + if(!M.stat) + shake_camera(M, 3, 1) + qdel(src) + +/obj/item/projectile/energy/floramut + name = "alpha somatoray" + icon_state = "energy" + damage = 0 + damage_type = TOX + nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + flag = "energy" + +/obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) + ..() + var/mob/living/M = target + if(ishuman(target)) + var/mob/living/carbon/human/H = M + if(IS_PLANT in H.dna.species.species_traits) + if(prob(15)) + M.apply_effect((rand(30,80)),IRRADIATE) + M.Weaken(5) + M.visible_message("[M] writhes in pain as [M.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.") + if(prob(80)) + randmutb(M) + domutcheck(M,null) + else + randmutg(M) + domutcheck(M,null) + else + M.adjustFireLoss(rand(5,15)) + M.show_message("The radiation beam singes you!") + else if(iscarbon(target)) + M.show_message("The radiation beam dissipates harmlessly through your body.") + else + return 1 + +/obj/item/projectile/energy/florayield + name = "beta somatoray" + icon_state = "energy2" + damage = 0 + damage_type = TOX + nodamage = 1 + flag = "energy" + +/obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) + ..() + var/mob/M = target + if(ishuman(target)) //These rays make plantmen fat. + var/mob/living/carbon/human/H = M + if(IS_PLANT in H.dna.species.species_traits) + H.set_nutrition(min(H.nutrition+30, NUTRITION_LEVEL_FULL)) + else if(iscarbon(target)) + M.show_message("The radiation beam dissipates harmlessly through your body.") + else + return 1 + + +/obj/item/projectile/beam/mindflayer + name = "flayer ray" + +/obj/item/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + M.adjustBrainLoss(20) + M.AdjustHallucinate(20) + +/obj/item/projectile/clown + name = "snap-pop" + icon = 'icons/obj/toy.dmi' + icon_state = "snappop" + +/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area) + do_sparks(3, 1, src) + new /obj/effect/decal/cleanable/ash(loc) + visible_message("The [name] explodes!","You hear a snap!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) + +/obj/item/projectile/beam/wormhole + name = "bluespace beam" + icon_state = "spark" + hitsound = "sparks" + damage = 0 + var/obj/item/gun/energy/wormhole_projector/gun + color = "#33CCFF" + nodamage = TRUE + +/obj/item/projectile/beam/wormhole/orange + name = "orange bluespace beam" + color = "#FF6600" + +/obj/item/projectile/beam/wormhole/New(var/obj/item/ammo_casing/energy/wormhole/casing) + if(casing) + gun = casing.gun + +/obj/item/projectile/beam/wormhole/on_hit(atom/target) + if(ismob(target)) + if(is_teleport_allowed(target.z)) + var/turf/portal_destination = pick(orange(6, src)) + do_teleport(target, portal_destination) + return ..() + if(!gun) + qdel(src) + gun.create_portal(src) + +/obj/item/projectile/bullet/frag12 + name ="explosive slug" + damage = 25 + weaken = 5 + alwayslog = TRUE + +/obj/item/projectile/bullet/frag12/on_hit(atom/target, blocked = 0) + ..() + explosion(target, -1, 0, 1) + return 1 + +/obj/item/projectile/plasma + name = "plasma blast" + icon_state = "plasmacutter" + damage_type = BRUTE + damage = 5 + range = 3 + dismemberment = 20 + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + +/obj/item/projectile/plasma/on_hit(atom/target) + . = ..() + if(ismineralturf(target)) + forcedodge = 1 + var/turf/simulated/mineral/M = target + M.gets_drilled(firer) + else + forcedodge = 0 + +/obj/item/projectile/plasma/adv + damage = 7 + range = 5 + +/obj/item/projectile/plasma/adv/mech + damage = 10 + range = 9 + +/obj/item/projectile/energy/teleport + name = "teleportation burst" + icon_state = "bluespace" + damage = 0 + nodamage = 1 + alwayslog = TRUE + var/teleport_target = null + +/obj/item/projectile/energy/teleport/New(loc, tele_target) + ..(loc) + if(tele_target) + teleport_target = tele_target + +/obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) + if(isliving(target)) + if(teleport_target) + do_teleport(target, teleport_target, 0)//teleport what's in the tile to the beacon + else + do_teleport(target, target, 15) //Otherwise it just warps you off somewhere. + add_attack_logs(firer, target, "Shot with a [type] [teleport_target ? "(Destination: [teleport_target])" : ""]") + +/obj/item/projectile/snowball + name = "snowball" + icon_state = "snowball" + hitsound = 'sound/items/dodgeball.ogg' + damage = 4 + damage_type = BURN + +/obj/item/projectile/snowball/on_hit(atom/target) //chilling + . = ..() + if(istype(target, /mob/living)) + var/mob/living/M = target + M.bodytemperature = max(0, M.bodytemperature - 50) //each hit will drop your body temp, so don't get surrounded! + M.ExtinguishMob() //bright side, they counter being on fire! + +/obj/item/projectile/ornament + name = "ornament" + icon_state = "ornament-1" + hitsound = 'sound/effects/glasshit.ogg' + damage = 7 + damage_type = BRUTE + +/obj/item/projectile/ornament/New() + icon_state = pick("ornament-1", "ornament-2") + ..() + +/obj/item/projectile/ornament/on_hit(atom/target) //knockback + ..() + if(istype(target, /turf)) + return 0 + var/obj/T = target + var/throwdir = get_dir(firer,target) + T.throw_at(get_edge_target_turf(target, throwdir),10,10) + return 1 + +/obj/item/projectile/mimic + name = "googly-eyed gun" + hitsound = 'sound/weapons/genhit1.ogg' + damage = 0 + nodamage = 1 + damage_type = BURN + flag = "melee" + var/obj/item/gun/stored_gun + +/obj/item/projectile/mimic/New(loc, mimic_type) + ..(loc) + if(mimic_type) + stored_gun = new mimic_type(src) + icon = stored_gun.icon + icon_state = stored_gun.icon_state + overlays = stored_gun.overlays + SpinAnimation(20, -1) + +/obj/item/projectile/mimic/on_hit(atom/target) + ..() + var/turf/T = get_turf(src) + var/obj/item/gun/G = stored_gun + stored_gun = null + G.forceMove(T) + var/mob/living/simple_animal/hostile/mimic/copy/ranged/R = new /mob/living/simple_animal/hostile/mimic/copy/ranged(T, G, firer) + if(ismob(target)) + R.target = target diff --git a/code/modules/reagents/chem_splash.dm b/code/modules/reagents/chem_splash.dm index 6ad517799651..9b15b6dca5d8 100644 --- a/code/modules/reagents/chem_splash.dm +++ b/code/modules/reagents/chem_splash.dm @@ -41,7 +41,7 @@ for(var/turf/T in (orange(i, epicenter) - orange(i-1, epicenter))) turflist |= T for(var/turf/T in turflist) - if( !(get_dir(T,epicenter) in cardinal) && (abs(T.x - epicenter.x) == abs(T.y - epicenter.y) )) + if( !(get_dir(T,epicenter) in GLOB.cardinal) && (abs(T.x - epicenter.x) == abs(T.y - epicenter.y) )) turflist.Remove(T) turflist.Add(T) // we move the purely diagonal turfs to the end of the list. for(var/turf/T in turflist) @@ -51,7 +51,7 @@ var/turf/NT = thing if(!(NT in accessible)) continue - if(!(get_dir(T,NT) in cardinal)) + if(!(get_dir(T,NT) in GLOB.cardinal)) continue accessible[T] = 1 break diff --git a/code/modules/reagents/chemistry/colors.dm b/code/modules/reagents/chemistry/colors.dm index 0eea945349cc..f5f4f252a01a 100644 --- a/code/modules/reagents/chemistry/colors.dm +++ b/code/modules/reagents/chemistry/colors.dm @@ -2,7 +2,7 @@ * Returns: * #RRGGBB(AA) on success, null on failure */ -var/list/random_color_list = list("#00aedb","#a200ff","#f47835","#d41243","#d11141","#00b159","#00aedb","#f37735","#ffc425","#008744","#0057e7","#d62d20","#ffa700") +GLOBAL_LIST_INIT(random_color_list, list("#00aedb","#a200ff","#f47835","#d41243","#d11141","#00b159","#00aedb","#f37735","#ffc425","#008744","#0057e7","#d62d20","#ffa700")) /proc/mix_color_from_reagents(const/list/reagent_list) if(!istype(reagent_list)) @@ -28,4 +28,4 @@ var/list/random_color_list = list("#00aedb","#a200ff","#f47835","#d41243","#d111 color = BlendRGB(color, reagent_color, vol_temp/vol_counter) else color = BlendRGB(reagent_color, color, vol_temp/vol_counter) - return color \ No newline at end of file + return color diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 758b228a9897..2de854eff88a 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -19,7 +19,7 @@ var/image/icon_beaker = null //cached overlay var/list/dispensable_reagents = list("hydrogen", "lithium", "carbon", "nitrogen", "oxygen", "fluorine", "sodium", "aluminum", "silicon", "phosphorus", "sulfur", "chlorine", "potassium", "iron", - "copper", "mercury", "plasma", "radium", "water", "ethanol", "sugar", "iodine", "bromine", "silver") + "copper", "mercury", "plasma", "radium", "water", "ethanol", "sugar", "iodine", "bromine", "silver", "chromium") var/list/upgrade_reagents = list("oil", "ash", "acetone", "saltpetre", "ammonia", "diethylamine", "fuel") var/list/hacked_reagents = list("toxin") var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode." @@ -155,7 +155,7 @@ // open the new ui window ui.open() -/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["amount"] = amount @@ -397,4 +397,4 @@ component_parts += new /obj/item/stock_parts/manipulator/pico(null) component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new cell_type(null) - RefreshParts() \ No newline at end of file + RefreshParts() diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 169b668250c7..79fc47d61a86 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -140,7 +140,7 @@ ui = new(user, src, ui_key, "chem_heater.tmpl", "ChemHeater", 350, 270) ui.open() -/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["targetTemp"] = desired_temp diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index ea53c0c782d9..435a253bed56 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -445,7 +445,7 @@ ui = new(user, src, ui_key, "chem_master.tmpl", name, 575, 500) ui.open() -/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["condi"] = condi @@ -502,4 +502,4 @@ component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new /obj/item/reagent_containers/glass/beaker(null) component_parts += new /obj/item/reagent_containers/glass/beaker(null) - RefreshParts() \ No newline at end of file + RefreshParts() diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index ced53381c9fc..c748df2a8e49 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -84,8 +84,8 @@ var/vaccine_name = "Unknown" if(!ispath(vaccine_type)) - if(archive_diseases[path]) - var/datum/disease/D = archive_diseases[path] + if(GLOB.archive_diseases[path]) + var/datum/disease/D = GLOB.archive_diseases[path] if(D) vaccine_name = D.name vaccine_type = path @@ -109,11 +109,11 @@ var/datum/disease/D = null if(!ispath(type)) D = GetVirusByIndex(text2num(href_list["create_virus_culture"])) - var/datum/disease/advance/A = archive_diseases[D.GetDiseaseID()] + var/datum/disease/advance/A = GLOB.archive_diseases[D.GetDiseaseID()] if(A) D = new A.type(0, A) else if(type) - if(type in diseases) // Make sure this is a disease + if(type in GLOB.diseases) // Make sure this is a disease D = new type(0, null) if(!D) return @@ -154,15 +154,15 @@ if(..()) return var/id = GetVirusTypeByIndex(text2num(href_list["name_disease"])) - if(archive_diseases[id]) - var/datum/disease/advance/A = archive_diseases[id] + if(GLOB.archive_diseases[id]) + var/datum/disease/advance/A = GLOB.archive_diseases[id] A.AssignName(new_name) for(var/datum/disease/advance/AD in GLOB.active_diseases) AD.Refresh() updateUsrDialog() else if(href_list["print_form"]) var/datum/disease/D = GetVirusByIndex(text2num(href_list["print_form"])) - D = archive_diseases[D.GetDiseaseID()]//We know it's advanced no need to check + D = GLOB.archive_diseases[D.GetDiseaseID()]//We know it's advanced no need to check print_form(D, usr) @@ -180,7 +180,7 @@ //Prints a nice virus release form. Props to Urbanliner for the layout /obj/machinery/computer/pandemic/proc/print_form(var/datum/disease/advance/D, mob/living/user) - D = archive_diseases[D.GetDiseaseID()] + D = GLOB.archive_diseases[D.GetDiseaseID()] if(!(printing) && D) var/reason = input(user,"Enter a reason for the release", "Write", null) as message reason += "" @@ -260,7 +260,7 @@ if(istype(D, /datum/disease/advance)) var/datum/disease/advance/A = D - D = archive_diseases[A.GetDiseaseID()] + D = GLOB.archive_diseases[A.GetDiseaseID()] if(D) if(D.name == "Unknown") dat += "Name Disease
    " @@ -300,7 +300,7 @@ var/disease_name = "Unknown" if(!ispath(type)) - var/datum/disease/advance/A = archive_diseases[type] + var/datum/disease/advance/A = GLOB.archive_diseases[type] if(A) disease_name = A.name else diff --git a/code/modules/reagents/chemistry/readme.dm b/code/modules/reagents/chemistry/readme.dm index 6c76d0a3a0f2..ddcdb8a123e3 100644 --- a/code/modules/reagents/chemistry/readme.dm +++ b/code/modules/reagents/chemistry/readme.dm @@ -238,4 +238,4 @@ About the Tools: It simply tells us how much to transfer when 'pouring' our reagents into something else. -*/ \ No newline at end of file +*/ diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index d0ba5c61f124..26dfa2d815f9 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -218,4 +218,4 @@ if(M.healthdoll) M.healthdoll.cached_healthdoll_overlays.Cut() if(M.dna.species) - M.dna.species.handle_hud_icons(M) \ No newline at end of file + M.dna.species.handle_hud_icons(M) diff --git a/code/modules/reagents/chemistry/reagents/blob.dm b/code/modules/reagents/chemistry/reagents/blob.dm index e2ea941709c0..acf244a892d3 100644 --- a/code/modules/reagents/chemistry/reagents/blob.dm +++ b/code/modules/reagents/chemistry/reagents/blob.dm @@ -157,4 +157,4 @@ if(message_living && !issilicon(M)) totalmessage += message_living totalmessage += "!" - to_chat(M, "[totalmessage]") \ No newline at end of file + to_chat(M, "[totalmessage]") diff --git a/code/modules/reagents/chemistry/reagents/disease.dm b/code/modules/reagents/chemistry/reagents/disease.dm index 331157d4acc6..1cd1111b49e4 100644 --- a/code/modules/reagents/chemistry/reagents/disease.dm +++ b/code/modules/reagents/chemistry/reagents/disease.dm @@ -211,4 +211,4 @@ /datum/reagent/plasma_dust/plasmavirusfood/weak name = "weakened virus plasma" id = "weakplasmavirusfood" - color = "#CEC3C6" // rgb: 206,195,198 \ No newline at end of file + color = "#CEC3C6" // rgb: 206,195,198 diff --git a/code/modules/reagents/chemistry/reagents/drugs.dm b/code/modules/reagents/chemistry/reagents/drugs.dm index a9f5d8a9cb5c..10cd5b2acce9 100644 --- a/code/modules/reagents/chemistry/reagents/drugs.dm +++ b/code/modules/reagents/chemistry/reagents/drugs.dm @@ -9,7 +9,7 @@ /datum/reagent/lithium/on_mob_life(mob/living/M) if(isturf(M.loc) && !istype(M.loc, /turf/space)) if(M.canmove && !M.restrained()) - step(M, pick(cardinal)) + step(M, pick(GLOB.cardinal)) if(prob(5)) M.emote(pick("twitch","drool","moan")) return ..() @@ -45,7 +45,7 @@ update_flags |= M.Druggy(15, FALSE) if(isturf(M.loc) && !istype(M.loc, /turf/space)) if(M.canmove && !M.restrained()) - step(M, pick(cardinal)) + step(M, pick(GLOB.cardinal)) if(prob(7)) M.emote(pick("twitch","drool","moan","giggle")) return ..() | update_flags diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm index d25ffe8c4957..6d1c4d74e72f 100644 --- a/code/modules/reagents/chemistry/reagents/food.dm +++ b/code/modules/reagents/chemistry/reagents/food.dm @@ -1029,4 +1029,4 @@ if(prob(80)) update_flags |= M.adjustBruteLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) update_flags |= M.adjustFireLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) - return ..() | update_flags \ No newline at end of file + return ..() | update_flags diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index 686b0fe08cbc..c9e7ac0c1dc4 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -908,6 +908,43 @@ M.reagents.remove_reagent("sugar", 5) return ..() +/datum/reagent/heparin + name = "Heparin" + id = "heparin" + description = "An anticoagulant used in heart surgeries, and in the treatment of heart attacks and blood clots." + reagent_state = LIQUID + color = "#eee6da" + overdose_threshold = 20 + taste_description = "bitterness" + +/datum/reagent/heparin/on_mob_life(mob/living/M) + M.reagents.remove_reagent("cholesterol", 2) + return ..() + +/datum/reagent/heparin/overdose_process(mob/living/carbon/M, severity) + var/list/overdose_info = ..() + var/effect = overdose_info[REAGENT_OVERDOSE_EFFECT] + var/update_flags = overdose_info[REAGENT_OVERDOSE_FLAGS] + if(severity == 1) + if(effect <= 2) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(5, 10), 0) + else if(effect <= 4) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(1, 2), 0) + else if(severity == 2) + if(effect <= 2) + M.visible_message("[M] is bleeding from [M.p_their()] very pores!") + M.bleed(rand(10, 20)) + else if(effect <= 4) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(5, 10), 0) + else if(effect <= 8) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(1, 2), 0) + return list(effect, update_flags) + + /datum/reagent/medicine/teporone name = "Teporone" id = "teporone" diff --git a/code/modules/reagents/chemistry/reagents/misc.dm b/code/modules/reagents/chemistry/reagents/misc.dm index 10821377c297..d84ed59e6dec 100644 --- a/code/modules/reagents/chemistry/reagents/misc.dm +++ b/code/modules/reagents/chemistry/reagents/misc.dm @@ -130,7 +130,6 @@ color = "#A8A8A8" // rgb: 168, 168, 168 taste_description = "metal" - /datum/reagent/silicon name = "Silicon" id = "silicon" @@ -147,6 +146,12 @@ color = "#6E3B08" // rgb: 110, 59, 8 taste_description = "copper" +/datum/reagent/chromium + name = "Chromium" + id = "chromium" + description = "A catalytic chemical element." + color = "#DCDCDC" + taste_description = "bitterness" /datum/reagent/iron name = "Iron" @@ -305,14 +310,14 @@ /datum/reagent/colorful_reagent/reaction_mob(mob/living/simple_animal/M, method=REAGENT_TOUCH, volume) if(isanimal(M)) - M.color = pick(random_color_list) + M.color = pick(GLOB.random_color_list) ..() /datum/reagent/colorful_reagent/reaction_obj(obj/O, volume) - O.color = pick(random_color_list) + O.color = pick(GLOB.random_color_list) /datum/reagent/colorful_reagent/reaction_turf(turf/T, volume) - T.color = pick(random_color_list) + T.color = pick(GLOB.random_color_list) /datum/reagent/hair_dye name = "Quantum Hair Dye" diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index 1f2d15a3fe0e..28ed544cb18c 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -383,6 +383,36 @@ H.adjustFireLoss(min(max(8, (volume - 5) * 3), 75)) to_chat(H, "The blueish acidic substance stings[volume < 5 ? " you, but isn't concentrated enough to harm you" : null]!") +/datum/reagent/acetic_acid + name = "acetic acid" + id = "acetic_acid" + description = "A weak acid that is the main component of vinegar and bad hangovers." + color = "#0080ff" + reagent_state = LIQUID + taste_description = "vinegar" + +/datum/reagent/acetic_acid/reaction_mob(mob/M, method = REAGENT_TOUCH, volume) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(method == REAGENT_TOUCH) + if(H.wear_mask || H.head) + return + if(volume >= 50 && prob(75)) + var/obj/item/organ/external/affecting = H.get_organ("head") + if(affecting) + affecting.disfigure() + H.adjustBruteLoss(5) + H.adjustFireLoss(15) + H.emote("scream") + else + H.adjustBruteLoss(min(5, volume * 0.25)) + else + to_chat(H, "The transparent acidic substance stings[volume < 25 ? " you, but isn't concentrated enough to harm you" : null]!") + if(volume >= 25) + H.adjustBruteLoss(2) + H.emote("scream") + + /datum/reagent/carpotoxin name = "Carpotoxin" id = "carpotoxin" @@ -546,7 +576,7 @@ id = "formaldehyde" description = "Formaldehyde is a common industrial chemical and is used to preserve corpses and medical samples. It is highly toxic and irritating." reagent_state = LIQUID - color = "#DED6D0" + color = "#B44B00" penetrates_skin = TRUE taste_description = "bitterness" @@ -557,6 +587,20 @@ M.reagents.add_reagent("histamine",rand(5,15)) return ..() | update_flags +/datum/reagent/acetaldehyde + name = "Acetaldehyde" + id = "acetaldehyde" + description = "Acetaldehyde is a common industrial chemical. It is a severe irritant." + reagent_state = LIQUID + color = "#B44B00" + penetrates_skin = TRUE + taste_description = "apples" + +/datum/reagent/acetaldehyde/on_mob_life(mob/living/M) + var/update_flags = STATUS_UPDATE_NONE + update_flags |= M.adjustFireLoss(1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + return ..() | update_flags + /datum/reagent/venom name = "Venom" id = "venom" @@ -924,23 +968,6 @@ M.AdjustLoseBreath(1) return ..() | update_flags -/datum/reagent/heparin //Based on a real-life anticoagulant. - name = "Heparin" - id = "heparin" - description = "A powerful anticoagulant. Victims will bleed uncontrollably and suffer scaling bruising." - reagent_state = LIQUID - color = "#C8C8C8" //RGB: 200, 200, 200 - metabolization_rate = 0.2 * REAGENTS_METABOLISM - taste_mult = 0 - -/datum/reagent/heparin/on_mob_life(mob/living/M) - var/update_flags = STATUS_UPDATE_NONE - if(ishuman(M)) - var/mob/living/carbon/human/H = M - H.bleed_rate = min(H.bleed_rate + 2, 8) - update_flags |= H.adjustBruteLoss(1, FALSE) - return ..() | update_flags - /datum/reagent/sarin name = "Sarin" id = "sarin" diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index 144f7b9a8acb..2d99b4025bd6 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -66,4 +66,4 @@ if(setting_type) X.throw_at(T, 20 + round(volume * 2), 1 + round(volume / 10)) else - X.throw_at(get_edge_target_turf(T, get_dir(T, X)), 20 + round(volume * 2), 1 + round(volume / 10)) \ No newline at end of file + X.throw_at(get_edge_target_turf(T, get_dir(T, X)), 20 + round(volume * 2), 1 + round(volume / 10)) diff --git a/code/modules/reagents/chemistry/recipes/drinks.dm b/code/modules/reagents/chemistry/recipes/drinks.dm index 5d088bd9ccbb..1f15c95bbf27 100644 --- a/code/modules/reagents/chemistry/recipes/drinks.dm +++ b/code/modules/reagents/chemistry/recipes/drinks.dm @@ -867,4 +867,4 @@ result = "bacchus_blessing" required_reagents = list("hooch" = 1, "absinthe" = 1, "manlydorf" = 1, "syndicatebomb" = 1) result_amount = 4 - mix_message = "The mixture turns to a sickening froth." \ No newline at end of file + mix_message = "The mixture turns to a sickening froth." diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm index d2499a06c541..f8c165989460 100644 --- a/code/modules/reagents/chemistry/recipes/drugs.dm +++ b/code/modules/reagents/chemistry/recipes/drugs.dm @@ -113,4 +113,4 @@ result = "surge" required_reagents = list("thermite" = 3, "uranium" = 1, "fluorosurfactant" = 1, "sacid" = 1) result_amount = 6 - mix_message = "The mixture congeals into a metallic green gel that crackles with electrical activity." \ No newline at end of file + mix_message = "The mixture congeals into a metallic green gel that crackles with electrical activity." diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 8bce89128d60..f7fb78dcdec3 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -69,6 +69,13 @@ required_reagents = list("sodiumchloride" = 1, "water" = 1, "sugar" = 1) result_amount = 3 +/datum/chemical_reaction/heparin + name = "Heparin" + id = "Heparin" + result = "heparin" + required_reagents = list("sugar" = 1, "meatslurry" = 1, "phenol" = 1, "sacid" = 1) + result_amount = 2 + /datum/chemical_reaction/synthflesh name = "Synthflesh" id = "synthflesh" @@ -272,4 +279,4 @@ required_reagents = list("ethanol" = 1, "copper" = 1, "silver" = 1) result_amount = 3 min_temp = T0C + 100 - mix_message = "The solution gently swirls with a metallic sheen." \ No newline at end of file + mix_message = "The solution gently swirls with a metallic sheen." diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index c118af7d7637..e0f9b41e01cb 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -322,6 +322,23 @@ result_amount = 3 mix_message = "The solution crystallizes with a brief flare of light." +/datum/chemical_reaction/acetaldehyde + name = "Acetaldehyde" + id = "acetaldehyde" + result = "acetaldehyde" + required_reagents = list("chromium" = 1, "oxygen" = 1, "copper" = 1, "ethanol" = 1) + result_amount = 3 + min_temp = T0C + 275 + mix_message = "It smells like a bad hangover in here." + +/datum/chemical_reaction/acetic_acid + name = "Acetic Acid" + id = "acetic_acid" + result = "acetic_acid" + required_reagents = list("acetaldehyde" = 1, "oxygen" = 1, "nitrogen" = 4) + result_amount = 3 + mix_message = "It smells like vinegar and a bad hangover in here." + /datum/chemical_reaction/ice name = "Ice" id = "ice" diff --git a/code/modules/reagents/chemistry/recipes/toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm index 6d93a5752407..977e3e0b2765 100644 --- a/code/modules/reagents/chemistry/recipes/toxins.dm +++ b/code/modules/reagents/chemistry/recipes/toxins.dm @@ -167,19 +167,10 @@ required_reagents = list("mutadone" = 3, "lithium" = 1) result_amount = 4 -/datum/chemical_reaction/heparin - name = "Heparin" - id = "Heparin" - result = "heparin" - required_reagents = list("formaldehyde" = 1, "sodium" = 1, "chlorine" = 1, "lithium" = 1) - result_amount = 4 - mix_message = "The mixture thins and loses all color." - mix_sound = 'sound/goonstation/misc/drinkfizz.ogg' - /datum/chemical_reaction/rotatium name = "Rotatium" id = "Rotatium" result = "rotatium" required_reagents = list("lsd" = 1, "teslium" = 1, "methamphetamine" = 1) result_amount = 3 - mix_message = "After sparks, fire, and the smell of LSD, the mix is constantly spinning with no stop in sight." \ No newline at end of file + mix_message = "After sparks, fire, and the smell of LSD, the mix is constantly spinning with no stop in sight." diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 41f9524874b9..db65f761e2ea 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -1,74 +1,74 @@ -/obj/item/reagent_containers - name = "Container" - desc = "..." - icon = 'icons/obj/chemical.dmi' - icon_state = null - w_class = WEIGHT_CLASS_TINY - var/amount_per_transfer_from_this = 5 - var/possible_transfer_amounts = list(5,10,15,25,30) - var/volume = 30 - var/list/list_reagents = null - var/spawned_disease = null - var/disease_amount = 20 - var/has_lid = FALSE // Used for containers where we want to put lids on and off - -/obj/item/reagent_containers/verb/set_APTFT() //set amount_per_transfer_from_this - set name = "Set transfer amount" - set category = "Object" - set src in range(0) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - var/default = null - if(amount_per_transfer_from_this in possible_transfer_amounts) - default = amount_per_transfer_from_this - var/N = input("Amount per transfer from this:", "[src]", default) as null|anything in possible_transfer_amounts - if(N) - amount_per_transfer_from_this = N - -/obj/item/reagent_containers/New() - ..() - if(!possible_transfer_amounts) - verbs -= /obj/item/reagent_containers/verb/set_APTFT - create_reagents(volume) - if(spawned_disease) - var/datum/disease/F = new spawned_disease(0) - var/list/data = list("viruses" = list(F), "blood_color" = "#A10808") - reagents.add_reagent("blood", disease_amount, data) - add_initial_reagents() - -obj/item/reagent_containers/proc/add_initial_reagents() - if(list_reagents) - reagents.add_reagent_list(list_reagents) - -/obj/item/reagent_containers/ex_act() - if(reagents) - for(var/datum/reagent/R in reagents.reagent_list) - R.on_ex_act() - if(!QDELETED(src)) - ..() - -/obj/item/reagent_containers/attack_self(mob/user) - if(has_lid) - if(is_open_container()) - to_chat(usr, "You put the lid on [src].") - container_type ^= REFILLABLE | DRAINABLE - else - to_chat(usr, "You take the lid off [src].") - container_type |= REFILLABLE | DRAINABLE - update_icon() - return - -/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag) - return - -/obj/item/reagent_containers/wash(mob/user, atom/source) - if(is_open_container()) - if(reagents.total_volume >= volume) - to_chat(user, "[src] is full.") - return - else - reagents.add_reagent("water", min(volume - reagents.total_volume, amount_per_transfer_from_this)) - to_chat(user, "You fill [src] from [source].") - return - ..() +/obj/item/reagent_containers + name = "Container" + desc = "..." + icon = 'icons/obj/chemical.dmi' + icon_state = null + w_class = WEIGHT_CLASS_TINY + var/amount_per_transfer_from_this = 5 + var/possible_transfer_amounts = list(5,10,15,25,30) + var/volume = 30 + var/list/list_reagents = null + var/spawned_disease = null + var/disease_amount = 20 + var/has_lid = FALSE // Used for containers where we want to put lids on and off + +/obj/item/reagent_containers/verb/set_APTFT() //set amount_per_transfer_from_this + set name = "Set transfer amount" + set category = "Object" + set src in range(0) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + var/default = null + if(amount_per_transfer_from_this in possible_transfer_amounts) + default = amount_per_transfer_from_this + var/N = input("Amount per transfer from this:", "[src]", default) as null|anything in possible_transfer_amounts + if(N) + amount_per_transfer_from_this = N + +/obj/item/reagent_containers/New() + ..() + if(!possible_transfer_amounts) + verbs -= /obj/item/reagent_containers/verb/set_APTFT + create_reagents(volume) + if(spawned_disease) + var/datum/disease/F = new spawned_disease(0) + var/list/data = list("viruses" = list(F), "blood_color" = "#A10808") + reagents.add_reagent("blood", disease_amount, data) + add_initial_reagents() + +obj/item/reagent_containers/proc/add_initial_reagents() + if(list_reagents) + reagents.add_reagent_list(list_reagents) + +/obj/item/reagent_containers/ex_act() + if(reagents) + for(var/datum/reagent/R in reagents.reagent_list) + R.on_ex_act() + if(!QDELETED(src)) + ..() + +/obj/item/reagent_containers/attack_self(mob/user) + if(has_lid) + if(is_open_container()) + to_chat(usr, "You put the lid on [src].") + container_type ^= REFILLABLE | DRAINABLE + else + to_chat(usr, "You take the lid off [src].") + container_type |= REFILLABLE | DRAINABLE + update_icon() + return + +/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag) + return + +/obj/item/reagent_containers/wash(mob/user, atom/source) + if(is_open_container()) + if(reagents.total_volume >= volume) + to_chat(user, "[src] is full.") + return + else + reagents.add_reagent("water", min(volume - reagents.total_volume, amount_per_transfer_from_this)) + to_chat(user, "You fill [src] from [source].") + return + ..() diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index d82c128be47f..f80fe2cb62ae 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -1,116 +1,116 @@ - -/obj/item/reagent_containers/borghypo - name = "Cyborg Hypospray" - desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." - icon = 'icons/obj/hypo.dmi' - item_state = "hypo" - icon_state = "borghypo" - amount_per_transfer_from_this = 5 - volume = 30 - possible_transfer_amounts = null - var/mode = 1 - var/charge_cost = 50 - var/charge_tick = 0 - var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) - var/bypass_protection = 0 //If the hypospray can go through armor or thick material - - var/list/datum/reagents/reagent_list = list() - var/list/reagent_ids = list("salglu_solution", "epinephrine", "spaceacillin", "charcoal", "hydrocodone") - //var/list/reagent_ids = list("salbutamol", "silver_sulfadiazine", "styptic_powder", "charcoal", "epinephrine", "spaceacillin", "hydrocodone") - -/obj/item/reagent_containers/borghypo/surgeon - reagent_ids = list("styptic_powder", "epinephrine", "salbutamol") - -/obj/item/reagent_containers/borghypo/crisis - reagent_ids = list("salglu_solution", "epinephrine", "sal_acid") - -/obj/item/reagent_containers/borghypo/syndicate - name = "syndicate cyborg hypospray" - desc = "An experimental piece of Syndicate technology used to produce powerful restorative nanites used to very quickly restore injuries of all types. Also metabolizes potassium iodide, for radiation poisoning, and hydrocodone, for field surgery and pain relief." - icon_state = "borghypo_s" - charge_cost = 20 - recharge_time = 2 - reagent_ids = list("syndicate_nanites", "potass_iodide", "hydrocodone") - bypass_protection = 1 - -/obj/item/reagent_containers/borghypo/New() - ..() - for(var/R in reagent_ids) - add_reagent(R) - - START_PROCESSING(SSobj, src) - -/obj/item/reagent_containers/borghypo/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg - charge_tick++ - if(charge_tick < recharge_time) return 0 - charge_tick = 0 - - if(isrobot(loc)) - var/mob/living/silicon/robot/R = loc - if(R && R.cell) - var/datum/reagents/RG = reagent_list[mode] - if(RG.total_volume < RG.maximum_volume) //Don't recharge reagents and drain power if the storage is full. - R.cell.use(charge_cost) //Take power from borg... - RG.add_reagent(reagent_ids[mode], 5) //And fill hypo with reagent. - //update_icon() - return 1 - -// Use this to add more chemicals for the borghypo to produce. -/obj/item/reagent_containers/borghypo/proc/add_reagent(reagent) - reagent_ids |= reagent - var/datum/reagents/RG = new(30) - RG.my_atom = src - reagent_list += RG - - var/datum/reagents/R = reagent_list[reagent_list.len] - R.add_reagent(reagent, 30) - -/obj/item/reagent_containers/borghypo/attack(mob/living/M, mob/user) - var/datum/reagents/R = reagent_list[mode] - if(!R.total_volume) - to_chat(user, "The injector is empty.") - return - if(!istype(M)) - return - if(R.total_volume && M.can_inject(user, TRUE, user.zone_selected, penetrate_thick = bypass_protection)) - to_chat(user, "You inject [M] with the injector.") - to_chat(M, "You feel a tiny prick!") - - R.add_reagent(M) - if(M.reagents) - var/datum/reagent/injected = GLOB.chemical_reagents_list[reagent_ids[mode]] - var/contained = injected.name - var/trans = R.trans_to(M, amount_per_transfer_from_this) - add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [trans] units", injected.harmless ? ATKLOG_ALMOSTALL : null) - M.LAssailant = user - to_chat(user, "[trans] units injected. [R.total_volume] units remaining.") - return - -/obj/item/reagent_containers/borghypo/attack_self(mob/user) - playsound(loc, 'sound/effects/pop.ogg', 50, 0) //Change the mode - mode++ - if(mode > reagent_list.len) - mode = 1 - - charge_tick = 0 //Prevents wasted chems/cell charge if you're cycling through modes. - var/datum/reagent/R = GLOB.chemical_reagents_list[reagent_ids[mode]] - to_chat(user, "Synthesizer is now producing '[R.name]'.") - return - -/obj/item/reagent_containers/borghypo/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - var/empty = TRUE - - for(var/datum/reagents/RS in reagent_list) - var/datum/reagent/R = locate() in RS.reagent_list - if(R) - . += "It currently has [R.volume] units of [R.name] stored." - empty = FALSE - - if(empty) - . += "It is currently empty. Allow some time for the internal syntheszier to produce more." + +/obj/item/reagent_containers/borghypo + name = "Cyborg Hypospray" + desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." + icon = 'icons/obj/hypo.dmi' + item_state = "hypo" + icon_state = "borghypo" + amount_per_transfer_from_this = 5 + volume = 30 + possible_transfer_amounts = null + var/mode = 1 + var/charge_cost = 50 + var/charge_tick = 0 + var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) + var/bypass_protection = 0 //If the hypospray can go through armor or thick material + + var/list/datum/reagents/reagent_list = list() + var/list/reagent_ids = list("salglu_solution", "epinephrine", "spaceacillin", "charcoal", "hydrocodone") + //var/list/reagent_ids = list("salbutamol", "silver_sulfadiazine", "styptic_powder", "charcoal", "epinephrine", "spaceacillin", "hydrocodone") + +/obj/item/reagent_containers/borghypo/surgeon + reagent_ids = list("styptic_powder", "epinephrine", "salbutamol") + +/obj/item/reagent_containers/borghypo/crisis + reagent_ids = list("salglu_solution", "epinephrine", "sal_acid") + +/obj/item/reagent_containers/borghypo/syndicate + name = "syndicate cyborg hypospray" + desc = "An experimental piece of Syndicate technology used to produce powerful restorative nanites used to very quickly restore injuries of all types. Also metabolizes potassium iodide, for radiation poisoning, and hydrocodone, for field surgery and pain relief." + icon_state = "borghypo_s" + charge_cost = 20 + recharge_time = 2 + reagent_ids = list("syndicate_nanites", "potass_iodide", "hydrocodone") + bypass_protection = 1 + +/obj/item/reagent_containers/borghypo/New() + ..() + for(var/R in reagent_ids) + add_reagent(R) + + START_PROCESSING(SSobj, src) + +/obj/item/reagent_containers/borghypo/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg + charge_tick++ + if(charge_tick < recharge_time) return 0 + charge_tick = 0 + + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc + if(R && R.cell) + var/datum/reagents/RG = reagent_list[mode] + if(RG.total_volume < RG.maximum_volume) //Don't recharge reagents and drain power if the storage is full. + R.cell.use(charge_cost) //Take power from borg... + RG.add_reagent(reagent_ids[mode], 5) //And fill hypo with reagent. + //update_icon() + return 1 + +// Use this to add more chemicals for the borghypo to produce. +/obj/item/reagent_containers/borghypo/proc/add_reagent(reagent) + reagent_ids |= reagent + var/datum/reagents/RG = new(30) + RG.my_atom = src + reagent_list += RG + + var/datum/reagents/R = reagent_list[reagent_list.len] + R.add_reagent(reagent, 30) + +/obj/item/reagent_containers/borghypo/attack(mob/living/M, mob/user) + var/datum/reagents/R = reagent_list[mode] + if(!R.total_volume) + to_chat(user, "The injector is empty.") + return + if(!istype(M)) + return + if(R.total_volume && M.can_inject(user, TRUE, user.zone_selected, penetrate_thick = bypass_protection)) + to_chat(user, "You inject [M] with the injector.") + to_chat(M, "You feel a tiny prick!") + + R.add_reagent(M) + if(M.reagents) + var/datum/reagent/injected = GLOB.chemical_reagents_list[reagent_ids[mode]] + var/contained = injected.name + var/trans = R.trans_to(M, amount_per_transfer_from_this) + add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [trans] units", injected.harmless ? ATKLOG_ALMOSTALL : null) + M.LAssailant = user + to_chat(user, "[trans] units injected. [R.total_volume] units remaining.") + return + +/obj/item/reagent_containers/borghypo/attack_self(mob/user) + playsound(loc, 'sound/effects/pop.ogg', 50, 0) //Change the mode + mode++ + if(mode > reagent_list.len) + mode = 1 + + charge_tick = 0 //Prevents wasted chems/cell charge if you're cycling through modes. + var/datum/reagent/R = GLOB.chemical_reagents_list[reagent_ids[mode]] + to_chat(user, "Synthesizer is now producing '[R.name]'.") + return + +/obj/item/reagent_containers/borghypo/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + var/empty = TRUE + + for(var/datum/reagents/RS in reagent_list) + var/datum/reagent/R = locate() in RS.reagent_list + if(R) + . += "It currently has [R.volume] units of [R.name] stored." + empty = FALSE + + if(empty) + . += "It is currently empty. Allow some time for the internal syntheszier to produce more." diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index b9080bf3d12e..61552eae7896 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -392,4 +392,4 @@ name = "BVAK bottle" desc = "A small bottle containing Bio Virus Antidote Kit." icon_state = "wide_bottle" - list_reagents = list("atropine" = 5, "epinephrine" = 5, "salbutamol" = 10, "spaceacillin" = 10) \ No newline at end of file + list_reagents = list("atropine" = 5, "epinephrine" = 5, "salbutamol" = 10, "spaceacillin" = 10) diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm index c9b9867efaac..ddd15413820e 100644 --- a/code/modules/reagents/reagent_containers/dropper.dm +++ b/code/modules/reagents/reagent_containers/dropper.dm @@ -1,144 +1,144 @@ -//////////////////////////////////////////////////////////////////////////////// -/// Droppers. /// -//////////////////////////////////////////////////////////////////////////////// - -/obj/item/reagent_containers/dropper - name = "dropper" - desc = "A dropper. Transfers 5 units." - icon_state = "dropper" - item_state = "dropper" - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list(1, 2, 3, 4, 5) - volume = 5 - -/obj/item/reagent_containers/dropper/on_reagent_change() - if(!reagents.total_volume) - icon_state = "[initial(icon_state)]" - else - icon_state = "[initial(icon_state)]1" - -/obj/item/reagent_containers/dropper/attack(mob/living/M, mob/living/user, def_zone) - return - -/obj/item/reagent_containers/dropper/afterattack(atom/target, mob/user, proximity) - if(!proximity) - return - var/to_transfer = 0 - if(iscarbon(target)) - var/mob/living/carbon/C = target - if(!reagents.total_volume) - return - if(user != C) - visible_message("[user] begins to drip something into [C]'s eyes!") - if(!do_mob(user, C, 30)) - return - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/safe_thing = null - - if(H.glasses) - safe_thing = H.glasses - if(H.wear_mask ) - if(H.wear_mask.flags_cover & MASKCOVERSEYES) - safe_thing = H.wear_mask - if(H.head) - if(H.head.flags_cover & MASKCOVERSEYES) - safe_thing = H.head - - if(safe_thing) - visible_message("[user] tries to drip something into [H]'s eyes, but fails!") - - reagents.reaction(safe_thing, REAGENT_TOUCH) - to_transfer = reagents.remove_any(amount_per_transfer_from_this) - - to_chat(user, "You transfer [to_transfer] units of the solution.") - return - - visible_message("[user] drips something into [C]'s eyes!") - reagents.reaction(C, REAGENT_TOUCH) - - var/list/injected = list() - for(var/datum/reagent/R in reagents.reagent_list) - injected += R.name - var/contained = english_list(injected) - add_attack_logs(user, C, "Dripped with [src] containing ([contained]), transfering [to_transfer]") - - to_transfer = reagents.trans_to(C, amount_per_transfer_from_this) - to_chat(user, "You transfer [to_transfer] units of the solution.") - - if(isobj(target)) - if(!target.reagents) - return - - if(reagents.total_volume) - if(!target.is_open_container() && !(istype(target, /obj/item/reagent_containers/food) && !istype(target, /obj/item/reagent_containers/food/pill)) && !istype(target, /obj/item/clothing/mask/cigarette)) - to_chat(user, "You cannot directly fill this object.") - return - - if(target.reagents.total_volume >= target.reagents.maximum_volume) - to_chat(user, "[target] is full.") - return - - to_transfer = reagents.trans_to(target, amount_per_transfer_from_this) - to_chat(user, "You transfer [to_transfer] units of the solution.") - - else - if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers)) - to_chat(user, "You cannot directly remove reagents from [target].") - return - - if(!target.reagents.total_volume) - to_chat(user, "[target] is empty.") - return - - to_transfer = target.reagents.trans_to(src, amount_per_transfer_from_this) - - to_chat(user, "You fill [src] with [to_transfer] units of the solution.") - -/obj/item/reagent_containers/dropper/cyborg - name = "Industrial Dropper" - desc = "A larger dropper. Transfers 10 units." - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - volume = 10 - -/obj/item/reagent_containers/dropper/precision - name = "pipette" - desc = "A high precision pippette. Holds 1 unit." - icon_state = "pipette" - amount_per_transfer_from_this = 1 - possible_transfer_amounts = list(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1) - volume = 1 - -//Syndicate item. Virus transmitting mini hypospray -/obj/item/reagent_containers/dropper/precision/viral_injector - -/obj/item/reagent_containers/dropper/precision/viral_injector/attack(mob/living/M, mob/living/user, def_zone) - if(M.can_inject(user, TRUE)) - to_chat(user, "You stab [M] with the [src].") - if(reagents.total_volume && M.reagents) - var/list/injected = list() - for(var/datum/reagent/R in reagents.reagent_list) - injected += R.name - var/datum/reagent/blood/B = R - - if(istype(B) && B.data["viruses"]) - var/virList = list() - for(var/dis in B.data["viruses"]) - var/datum/disease/D = dis - var/virusData = D.name - var/english_symptoms = list() - var/datum/disease/advance/A = D - if(A) - for(var/datum/symptom/S in A.symptoms) - english_symptoms += S.name - virusData += " ([english_list(english_symptoms)])" - virList += virusData - var/str = english_list(virList) - add_attack_logs(user, M, "Infected with [str].") - - reagents.reaction(M, REAGENT_INGEST, reagents.total_volume) - reagents.trans_to(M, 1) - - var/contained = english_list(injected) - add_attack_logs(user, M, "Injected with [src] containing ([contained])") +//////////////////////////////////////////////////////////////////////////////// +/// Droppers. /// +//////////////////////////////////////////////////////////////////////////////// + +/obj/item/reagent_containers/dropper + name = "dropper" + desc = "A dropper. Transfers 5 units." + icon_state = "dropper" + item_state = "dropper" + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list(1, 2, 3, 4, 5) + volume = 5 + +/obj/item/reagent_containers/dropper/on_reagent_change() + if(!reagents.total_volume) + icon_state = "[initial(icon_state)]" + else + icon_state = "[initial(icon_state)]1" + +/obj/item/reagent_containers/dropper/attack(mob/living/M, mob/living/user, def_zone) + return + +/obj/item/reagent_containers/dropper/afterattack(atom/target, mob/user, proximity) + if(!proximity) + return + var/to_transfer = 0 + if(iscarbon(target)) + var/mob/living/carbon/C = target + if(!reagents.total_volume) + return + if(user != C) + visible_message("[user] begins to drip something into [C]'s eyes!") + if(!do_mob(user, C, 30)) + return + if(ishuman(target)) + var/mob/living/carbon/human/H = target + var/obj/item/safe_thing = null + + if(H.glasses) + safe_thing = H.glasses + if(H.wear_mask ) + if(H.wear_mask.flags_cover & MASKCOVERSEYES) + safe_thing = H.wear_mask + if(H.head) + if(H.head.flags_cover & MASKCOVERSEYES) + safe_thing = H.head + + if(safe_thing) + visible_message("[user] tries to drip something into [H]'s eyes, but fails!") + + reagents.reaction(safe_thing, REAGENT_TOUCH) + to_transfer = reagents.remove_any(amount_per_transfer_from_this) + + to_chat(user, "You transfer [to_transfer] units of the solution.") + return + + visible_message("[user] drips something into [C]'s eyes!") + reagents.reaction(C, REAGENT_TOUCH) + + var/list/injected = list() + for(var/datum/reagent/R in reagents.reagent_list) + injected += R.name + var/contained = english_list(injected) + add_attack_logs(user, C, "Dripped with [src] containing ([contained]), transfering [to_transfer]") + + to_transfer = reagents.trans_to(C, amount_per_transfer_from_this) + to_chat(user, "You transfer [to_transfer] units of the solution.") + + if(isobj(target)) + if(!target.reagents) + return + + if(reagents.total_volume) + if(!target.is_open_container() && !(istype(target, /obj/item/reagent_containers/food) && !istype(target, /obj/item/reagent_containers/food/pill)) && !istype(target, /obj/item/clothing/mask/cigarette)) + to_chat(user, "You cannot directly fill this object.") + return + + if(target.reagents.total_volume >= target.reagents.maximum_volume) + to_chat(user, "[target] is full.") + return + + to_transfer = reagents.trans_to(target, amount_per_transfer_from_this) + to_chat(user, "You transfer [to_transfer] units of the solution.") + + else + if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers)) + to_chat(user, "You cannot directly remove reagents from [target].") + return + + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty.") + return + + to_transfer = target.reagents.trans_to(src, amount_per_transfer_from_this) + + to_chat(user, "You fill [src] with [to_transfer] units of the solution.") + +/obj/item/reagent_containers/dropper/cyborg + name = "Industrial Dropper" + desc = "A larger dropper. Transfers 10 units." + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + volume = 10 + +/obj/item/reagent_containers/dropper/precision + name = "pipette" + desc = "A high precision pippette. Holds 1 unit." + icon_state = "pipette" + amount_per_transfer_from_this = 1 + possible_transfer_amounts = list(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1) + volume = 1 + +//Syndicate item. Virus transmitting mini hypospray +/obj/item/reagent_containers/dropper/precision/viral_injector + +/obj/item/reagent_containers/dropper/precision/viral_injector/attack(mob/living/M, mob/living/user, def_zone) + if(M.can_inject(user, TRUE)) + to_chat(user, "You stab [M] with the [src].") + if(reagents.total_volume && M.reagents) + var/list/injected = list() + for(var/datum/reagent/R in reagents.reagent_list) + injected += R.name + var/datum/reagent/blood/B = R + + if(istype(B) && B.data["viruses"]) + var/virList = list() + for(var/dis in B.data["viruses"]) + var/datum/disease/D = dis + var/virusData = D.name + var/english_symptoms = list() + var/datum/disease/advance/A = D + if(A) + for(var/datum/symptom/S in A.symptoms) + english_symptoms += S.name + virusData += " ([english_list(english_symptoms)])" + virList += virusData + var/str = english_list(virList) + add_attack_logs(user, M, "Infected with [str].") + + reagents.reaction(M, REAGENT_INGEST, reagents.total_volume) + reagents.trans_to(M, 1) + + var/contained = english_list(injected) + add_attack_logs(user, M, "Injected with [src] containing ([contained])") diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 6e0d8c766200..3e9480f166dc 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -397,4 +397,4 @@ amount_per_transfer_from_this = 20 /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty - list_reagents = list() \ No newline at end of file + list_reagents = list() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 3b71a5bd741d..490a5d808faa 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -1,175 +1,175 @@ -//////////////////////////////////////////////////////////////////////////////// -/// HYPOSPRAY -//////////////////////////////////////////////////////////////////////////////// - -/obj/item/reagent_containers/hypospray - name = "hypospray" - desc = "The DeForest Medical Corporation hypospray is a sterile, air-needle autoinjector for rapid administration of drugs to patients." - icon = 'icons/obj/hypo.dmi' - item_state = "hypo" - icon_state = "hypo" - amount_per_transfer_from_this = 5 - volume = 30 - possible_transfer_amounts = list(1,2,3,4,5,10,15,20,25,30) - resistance_flags = ACID_PROOF - container_type = OPENCONTAINER - slot_flags = SLOT_BELT - var/ignore_flags = FALSE - var/emagged = FALSE - var/safety_hypo = FALSE - -/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user) - if(!reagents.total_volume) - to_chat(user, "[src] is empty!") - return - if(!iscarbon(M)) - return - - if(reagents.total_volume && (ignore_flags || M.can_inject(user, TRUE))) // Ignore flag should be checked first or there will be an error message. - to_chat(M, "You feel a tiny prick!") - to_chat(user, "You inject [M] with [src].") - - if(M.reagents) - var/list/injected = list() - for(var/datum/reagent/R in reagents.reagent_list) - injected += R.name - - var/primary_reagent_name = reagents.get_master_reagent_name() - var/trans = reagents.trans_to(M, amount_per_transfer_from_this) - - if(safety_hypo) - visible_message("[user] injects [M] with [trans] units of [primary_reagent_name].") - playsound(loc, 'sound/goonstation/items/hypo.ogg', 80, 0) - - to_chat(user, "[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].") - - var/contained = english_list(injected) - - add_attack_logs(user, M, "Injected with [src] containing ([contained])", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) - - return TRUE - -/obj/item/reagent_containers/hypospray/on_reagent_change() - if(safety_hypo && !emagged) - var/found_forbidden_reagent = FALSE - for(var/datum/reagent/R in reagents.reagent_list) - if(!GLOB.safe_chem_list.Find(R.id)) - reagents.del_reagent(R.id) - found_forbidden_reagent = TRUE - if(found_forbidden_reagent) - if(ismob(loc)) - to_chat(loc, "[src] identifies and removes a harmful substance.") - else - visible_message("[src] identifies and removes a harmful substance.") - -/obj/item/reagent_containers/hypospray/emag_act(mob/user) - if(safety_hypo && !emagged) - emagged = TRUE - ignore_flags = TRUE - to_chat(user, "You short out the safeties on [src].") - -/obj/item/reagent_containers/hypospray/safety - name = "medical hypospray" - desc = "A general use medical hypospray for quick injection of chemicals. There is a safety button by the trigger." - icon_state = "medivend_hypo" - safety_hypo = TRUE - -/obj/item/reagent_containers/hypospray/safety/ert - list_reagents = list("omnizine" = 30) - -/obj/item/reagent_containers/hypospray/CMO - list_reagents = list("omnizine" = 30) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/reagent_containers/hypospray/combat - name = "combat stimulant injector" - desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat." - amount_per_transfer_from_this = 15 - possible_transfer_amounts = list(15) - icon_state = "combat_hypo" - volume = 90 - ignore_flags = 1 // So they can heal their comrades. - list_reagents = list("epinephrine" = 30, "weak_omnizine" = 30, "salglu_solution" = 30) - -/obj/item/reagent_containers/hypospray/combat/nanites - desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with expensive medical nanites for rapid healing." - volume = 100 - list_reagents = list("nanites" = 100) - -/obj/item/reagent_containers/hypospray/autoinjector - name = "emergency autoinjector" - desc = "A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge." - icon_state = "autoinjector" - item_state = "autoinjector" - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10) - volume = 10 - ignore_flags = TRUE //so you can medipen through hardsuits - container_type = DRAWABLE - flags = null - list_reagents = list("epinephrine" = 10) - -/obj/item/reagent_containers/hypospray/autoinjector/attack(mob/M, mob/user) - if(!reagents.total_volume) - to_chat(user, "[src] is empty!") - return - ..() - update_icon() - return TRUE - -/obj/item/reagent_containers/hypospray/autoinjector/update_icon() - if(reagents.total_volume > 0) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]0" - -/obj/item/reagent_containers/hypospray/autoinjector/examine() - . = ..() - if(reagents && reagents.reagent_list.len) - . += "It is currently loaded." - else - . += "It is spent." - -/obj/item/reagent_containers/hypospray/autoinjector/teporone //basilisks - name = "teporone autoinjector" - desc = "A rapid way to regulate your body's temperature in the event of a hardsuit malfunction." - icon_state = "lepopen" - list_reagents = list("teporone" = 10) - -/obj/item/reagent_containers/hypospray/autoinjector/stimpack //goliath kiting - name = "stimpack autoinjector" - desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor." - icon_state = "stimpen" - volume = 20 - amount_per_transfer_from_this = 20 - list_reagents = list("methamphetamine" = 10, "coffee" = 10) - -/obj/item/reagent_containers/hypospray/autoinjector/stimulants - name = "Stimulants autoinjector" - desc = "Rapidly stimulates and regenerates the body's organ system." - icon_state = "stimpen" - amount_per_transfer_from_this = 50 - possible_transfer_amounts = list(50) - volume = 50 - list_reagents = list("stimulants" = 50) - -/obj/item/reagent_containers/hypospray/autoinjector/survival - name = "survival medipen" - desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards.
    WARNING: Do not inject more than one pen in quick succession." - icon_state = "stimpen" - volume = 42 - amount_per_transfer_from_this = 42 - list_reagents = list("salbutamol" = 10, "teporone" = 15, "epinephrine" = 10, "lavaland_extract" = 2, "weak_omnizine" = 5) //Short burst of healing, followed by minor healing from the saline - -/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium - name = "nanocalcium autoinjector" - desc = "After a short period of time the nanites will slow the body's systems and assist with bone repair. Nanomachines son." - icon_state = "bonepen" - amount_per_transfer_from_this = 30 - possible_transfer_amounts = list(30) - volume = 30 - list_reagents = list("nanocalcium" = 30) - -/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium/attack(mob/living/M, mob/user) - if(..()) - playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, 1) +//////////////////////////////////////////////////////////////////////////////// +/// HYPOSPRAY +//////////////////////////////////////////////////////////////////////////////// + +/obj/item/reagent_containers/hypospray + name = "hypospray" + desc = "The DeForest Medical Corporation hypospray is a sterile, air-needle autoinjector for rapid administration of drugs to patients." + icon = 'icons/obj/hypo.dmi' + item_state = "hypo" + icon_state = "hypo" + amount_per_transfer_from_this = 5 + volume = 30 + possible_transfer_amounts = list(1,2,3,4,5,10,15,20,25,30) + resistance_flags = ACID_PROOF + container_type = OPENCONTAINER + slot_flags = SLOT_BELT + var/ignore_flags = FALSE + var/emagged = FALSE + var/safety_hypo = FALSE + +/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user) + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + if(!iscarbon(M)) + return + + if(reagents.total_volume && (ignore_flags || M.can_inject(user, TRUE))) // Ignore flag should be checked first or there will be an error message. + to_chat(M, "You feel a tiny prick!") + to_chat(user, "You inject [M] with [src].") + + if(M.reagents) + var/list/injected = list() + for(var/datum/reagent/R in reagents.reagent_list) + injected += R.name + + var/primary_reagent_name = reagents.get_master_reagent_name() + var/trans = reagents.trans_to(M, amount_per_transfer_from_this) + + if(safety_hypo) + visible_message("[user] injects [M] with [trans] units of [primary_reagent_name].") + playsound(loc, 'sound/goonstation/items/hypo.ogg', 80, 0) + + to_chat(user, "[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].") + + var/contained = english_list(injected) + + add_attack_logs(user, M, "Injected with [src] containing ([contained])", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) + + return TRUE + +/obj/item/reagent_containers/hypospray/on_reagent_change() + if(safety_hypo && !emagged) + var/found_forbidden_reagent = FALSE + for(var/datum/reagent/R in reagents.reagent_list) + if(!GLOB.safe_chem_list.Find(R.id)) + reagents.del_reagent(R.id) + found_forbidden_reagent = TRUE + if(found_forbidden_reagent) + if(ismob(loc)) + to_chat(loc, "[src] identifies and removes a harmful substance.") + else + visible_message("[src] identifies and removes a harmful substance.") + +/obj/item/reagent_containers/hypospray/emag_act(mob/user) + if(safety_hypo && !emagged) + emagged = TRUE + ignore_flags = TRUE + to_chat(user, "You short out the safeties on [src].") + +/obj/item/reagent_containers/hypospray/safety + name = "medical hypospray" + desc = "A general use medical hypospray for quick injection of chemicals. There is a safety button by the trigger." + icon_state = "medivend_hypo" + safety_hypo = TRUE + +/obj/item/reagent_containers/hypospray/safety/ert + list_reagents = list("omnizine" = 30) + +/obj/item/reagent_containers/hypospray/CMO + list_reagents = list("omnizine" = 30) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/reagent_containers/hypospray/combat + name = "combat stimulant injector" + desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat." + amount_per_transfer_from_this = 15 + possible_transfer_amounts = list(15) + icon_state = "combat_hypo" + volume = 90 + ignore_flags = 1 // So they can heal their comrades. + list_reagents = list("epinephrine" = 30, "weak_omnizine" = 30, "salglu_solution" = 30) + +/obj/item/reagent_containers/hypospray/combat/nanites + desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with expensive medical nanites for rapid healing." + volume = 100 + list_reagents = list("nanites" = 100) + +/obj/item/reagent_containers/hypospray/autoinjector + name = "emergency autoinjector" + desc = "A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge." + icon_state = "autoinjector" + item_state = "autoinjector" + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(10) + volume = 10 + ignore_flags = TRUE //so you can medipen through hardsuits + container_type = DRAWABLE + flags = null + list_reagents = list("epinephrine" = 10) + +/obj/item/reagent_containers/hypospray/autoinjector/attack(mob/M, mob/user) + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + ..() + update_icon() + return TRUE + +/obj/item/reagent_containers/hypospray/autoinjector/update_icon() + if(reagents.total_volume > 0) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]0" + +/obj/item/reagent_containers/hypospray/autoinjector/examine() + . = ..() + if(reagents && reagents.reagent_list.len) + . += "It is currently loaded." + else + . += "It is spent." + +/obj/item/reagent_containers/hypospray/autoinjector/teporone //basilisks + name = "teporone autoinjector" + desc = "A rapid way to regulate your body's temperature in the event of a hardsuit malfunction." + icon_state = "lepopen" + list_reagents = list("teporone" = 10) + +/obj/item/reagent_containers/hypospray/autoinjector/stimpack //goliath kiting + name = "stimpack autoinjector" + desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor." + icon_state = "stimpen" + volume = 20 + amount_per_transfer_from_this = 20 + list_reagents = list("methamphetamine" = 10, "coffee" = 10) + +/obj/item/reagent_containers/hypospray/autoinjector/stimulants + name = "Stimulants autoinjector" + desc = "Rapidly stimulates and regenerates the body's organ system." + icon_state = "stimpen" + amount_per_transfer_from_this = 50 + possible_transfer_amounts = list(50) + volume = 50 + list_reagents = list("stimulants" = 50) + +/obj/item/reagent_containers/hypospray/autoinjector/survival + name = "survival medipen" + desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards.
    WARNING: Do not inject more than one pen in quick succession." + icon_state = "stimpen" + volume = 42 + amount_per_transfer_from_this = 42 + list_reagents = list("salbutamol" = 10, "teporone" = 15, "epinephrine" = 10, "lavaland_extract" = 2, "weak_omnizine" = 5) //Short burst of healing, followed by minor healing from the saline + +/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium + name = "nanocalcium autoinjector" + desc = "After a short period of time the nanites will slow the body's systems and assist with bone repair. Nanomachines son." + icon_state = "bonepen" + amount_per_transfer_from_this = 30 + possible_transfer_amounts = list(30) + volume = 30 + list_reagents = list("nanocalcium" = 30) + +/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium/attack(mob/living/M, mob/user) + if(..()) + playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, 1) diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index e22e9c86793f..f6980c036209 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -1,155 +1,155 @@ -//////////////////////////////////////////////////////////////////////////////// -/// Pills. -//////////////////////////////////////////////////////////////////////////////// -/obj/item/reagent_containers/food/pill - name = "pill" - desc = "a pill." - icon = 'icons/obj/chemical.dmi' - icon_state = null - item_state = "pill" - possible_transfer_amounts = null - volume = 100 - consume_sound = null - can_taste = FALSE - antable = FALSE - -/obj/item/reagent_containers/food/pill/New() - ..() - if(!icon_state) - icon_state = "pill[rand(1,20)]" - -/obj/item/reagent_containers/food/pill/attack_self(mob/user) - return - -/obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone) - if(!istype(M)) - return 0 - bitesize = reagents.total_volume - if(M.eat(src, user)) - spawn(0) - qdel(src) - return 1 - return 0 - -/obj/item/reagent_containers/food/pill/afterattack(obj/target, mob/user, proximity) - if(!proximity) - return - - if(target.is_open_container() != 0 && target.reagents) - if(!target.reagents.total_volume) - to_chat(user, "[target] is empty. Cant dissolve [src].") - return - - to_chat(user, "You dissolve [src] in [target].") - reagents.trans_to(target, reagents.total_volume) - for(var/mob/O in viewers(2, user)) - O.show_message("[user] puts something in [target].", 1) - spawn(5) - qdel(src) - -//////////////////////////////////////////////////////////////////////////////// -/// Pills. END -//////////////////////////////////////////////////////////////////////////////// - -//Pills -/obj/item/reagent_containers/food/pill/tox - name = "Toxins pill" - desc = "Highly toxic." - icon_state = "pill21" - list_reagents = list("toxin" = 50) - -/obj/item/reagent_containers/food/pill/initropidril - name = "initropidril pill" - desc = "Don't swallow this." - icon_state = "pill21" - list_reagents = list("initropidril" = 50) - -/obj/item/reagent_containers/food/pill/fakedeath - name = "fake death pill" - desc = "Swallow then rest to appear dead, stand up to wake up. Also mutes the user's voice." - icon_state = "pill4" - list_reagents = list("capulettium_plus" = 50) - -/obj/item/reagent_containers/food/pill/adminordrazine - name = "Adminordrazine pill" - desc = "It's magic. We don't have to explain it." - icon_state = "pill16" - list_reagents = list("adminordrazine" = 50) - -/obj/item/reagent_containers/food/pill/morphine - name = "Morphine pill" - desc = "Commonly used to treat insomnia." - icon_state = "pill8" - list_reagents = list("morphine" = 30) - -/obj/item/reagent_containers/food/pill/methamphetamine - name = "Methamphetamine pill" - desc = "Helps improve the ability to concentrate." - icon_state = "pill8" - list_reagents = list("methamphetamine" = 5) - -/obj/item/reagent_containers/food/pill/haloperidol - name = "Haloperidol pill" - desc = "Haloperidol is an anti-psychotic use to treat psychiatric problems." - icon_state = "pill8" - list_reagents = list("haloperidol" = 15) - -/obj/item/reagent_containers/food/pill/happy - name = "Happy pill" - desc = "Happy happy joy joy!" - icon_state = "pill18" - list_reagents = list("space_drugs" = 15, "sugar" = 15) - -/obj/item/reagent_containers/food/pill/zoom - name = "Zoom pill" - desc = "Zoooom!" - icon_state = "pill18" - list_reagents = list("synaptizine" = 5, "methamphetamine" = 5) - -/obj/item/reagent_containers/food/pill/charcoal - name = "Charcoal pill" - desc = "Neutralizes many common toxins." - icon_state = "pill17" - list_reagents = list("charcoal" = 50) - -/obj/item/reagent_containers/food/pill/epinephrine - name = "Epinephrine pill" - desc = "Used to provide shots of adrenaline." - icon_state = "pill6" - list_reagents = list("epinephrine" = 50) - -/obj/item/reagent_containers/food/pill/salicylic - name = "Salicylic Acid pill" - desc = "Commonly used to treat moderate pain and fevers." - icon_state = "pill4" - list_reagents = list("sal_acid" = 20) - -/obj/item/reagent_containers/food/pill/salbutamol - name = "Salbutamol pill" - desc = "Used to treat respiratory distress." - icon_state = "pill8" - list_reagents = list("salbutamol" = 20) - -/obj/item/reagent_containers/food/pill/hydrocodone - name = "Hydrocodone pill" - desc = "Used to treat extreme pain." - icon_state = "pill6" - list_reagents = list("hydrocodone" = 15) - -/obj/item/reagent_containers/food/pill/calomel - name = "calomel pill" - desc = "Can be used to purge impurities, but is highly toxic itself." - icon_state = "pill3" - list_reagents = list("calomel" = 15) - -/obj/item/reagent_containers/food/pill/mutadone - name = "mutadone pill" - desc = "Used to cure genetic abnormalities." - icon_state = "pill18" - list_reagents = list("mutadone" = 20) - -/obj/item/reagent_containers/food/pill/mannitol - name = "mannitol pill" - desc = "Used to treat cranial swelling." - icon_state = "pill19" - list_reagents = list("mannitol" = 20) \ No newline at end of file +//////////////////////////////////////////////////////////////////////////////// +/// Pills. +//////////////////////////////////////////////////////////////////////////////// +/obj/item/reagent_containers/food/pill + name = "pill" + desc = "a pill." + icon = 'icons/obj/chemical.dmi' + icon_state = null + item_state = "pill" + possible_transfer_amounts = null + volume = 100 + consume_sound = null + can_taste = FALSE + antable = FALSE + +/obj/item/reagent_containers/food/pill/New() + ..() + if(!icon_state) + icon_state = "pill[rand(1,20)]" + +/obj/item/reagent_containers/food/pill/attack_self(mob/user) + return + +/obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone) + if(!istype(M)) + return 0 + bitesize = reagents.total_volume + if(M.eat(src, user)) + spawn(0) + qdel(src) + return 1 + return 0 + +/obj/item/reagent_containers/food/pill/afterattack(obj/target, mob/user, proximity) + if(!proximity) + return + + if(target.is_open_container() != 0 && target.reagents) + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty. Cant dissolve [src].") + return + + to_chat(user, "You dissolve [src] in [target].") + reagents.trans_to(target, reagents.total_volume) + for(var/mob/O in viewers(2, user)) + O.show_message("[user] puts something in [target].", 1) + spawn(5) + qdel(src) + +//////////////////////////////////////////////////////////////////////////////// +/// Pills. END +//////////////////////////////////////////////////////////////////////////////// + +//Pills +/obj/item/reagent_containers/food/pill/tox + name = "Toxins pill" + desc = "Highly toxic." + icon_state = "pill21" + list_reagents = list("toxin" = 50) + +/obj/item/reagent_containers/food/pill/initropidril + name = "initropidril pill" + desc = "Don't swallow this." + icon_state = "pill21" + list_reagents = list("initropidril" = 50) + +/obj/item/reagent_containers/food/pill/fakedeath + name = "fake death pill" + desc = "Swallow then rest to appear dead, stand up to wake up. Also mutes the user's voice." + icon_state = "pill4" + list_reagents = list("capulettium_plus" = 50) + +/obj/item/reagent_containers/food/pill/adminordrazine + name = "Adminordrazine pill" + desc = "It's magic. We don't have to explain it." + icon_state = "pill16" + list_reagents = list("adminordrazine" = 50) + +/obj/item/reagent_containers/food/pill/morphine + name = "Morphine pill" + desc = "Commonly used to treat insomnia." + icon_state = "pill8" + list_reagents = list("morphine" = 30) + +/obj/item/reagent_containers/food/pill/methamphetamine + name = "Methamphetamine pill" + desc = "Helps improve the ability to concentrate." + icon_state = "pill8" + list_reagents = list("methamphetamine" = 5) + +/obj/item/reagent_containers/food/pill/haloperidol + name = "Haloperidol pill" + desc = "Haloperidol is an anti-psychotic use to treat psychiatric problems." + icon_state = "pill8" + list_reagents = list("haloperidol" = 15) + +/obj/item/reagent_containers/food/pill/happy + name = "Happy pill" + desc = "Happy happy joy joy!" + icon_state = "pill18" + list_reagents = list("space_drugs" = 15, "sugar" = 15) + +/obj/item/reagent_containers/food/pill/zoom + name = "Zoom pill" + desc = "Zoooom!" + icon_state = "pill18" + list_reagents = list("synaptizine" = 5, "methamphetamine" = 5) + +/obj/item/reagent_containers/food/pill/charcoal + name = "Charcoal pill" + desc = "Neutralizes many common toxins." + icon_state = "pill17" + list_reagents = list("charcoal" = 50) + +/obj/item/reagent_containers/food/pill/epinephrine + name = "Epinephrine pill" + desc = "Used to provide shots of adrenaline." + icon_state = "pill6" + list_reagents = list("epinephrine" = 50) + +/obj/item/reagent_containers/food/pill/salicylic + name = "Salicylic Acid pill" + desc = "Commonly used to treat moderate pain and fevers." + icon_state = "pill4" + list_reagents = list("sal_acid" = 20) + +/obj/item/reagent_containers/food/pill/salbutamol + name = "Salbutamol pill" + desc = "Used to treat respiratory distress." + icon_state = "pill8" + list_reagents = list("salbutamol" = 20) + +/obj/item/reagent_containers/food/pill/hydrocodone + name = "Hydrocodone pill" + desc = "Used to treat extreme pain." + icon_state = "pill6" + list_reagents = list("hydrocodone" = 15) + +/obj/item/reagent_containers/food/pill/calomel + name = "calomel pill" + desc = "Can be used to purge impurities, but is highly toxic itself." + icon_state = "pill3" + list_reagents = list("calomel" = 15) + +/obj/item/reagent_containers/food/pill/mutadone + name = "mutadone pill" + desc = "Used to cure genetic abnormalities." + icon_state = "pill18" + list_reagents = list("mutadone" = 20) + +/obj/item/reagent_containers/food/pill/mannitol + name = "mannitol pill" + desc = "Used to treat cranial swelling." + icon_state = "pill19" + list_reagents = list("mannitol" = 20) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index a4320639f39b..8826efa5eb8c 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -1,217 +1,217 @@ -/obj/item/reagent_containers/spray - name = "spray bottle" - desc = "A spray bottle, with an unscrewable top." - icon = 'icons/obj/janitor.dmi' - icon_state = "cleaner" - item_state = "cleaner" - flags = NOBLUDGEON - container_type = OPENCONTAINER - slot_flags = SLOT_BELT - throwforce = 0 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 7 - var/spray_maxrange = 3 //what the sprayer will set spray_currentrange to in the attack_self. - var/spray_currentrange = 3 //the range of tiles the sprayer will reach when in fixed mode. - amount_per_transfer_from_this = 5 - volume = 250 - possible_transfer_amounts = null - - -/obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) - if(istype(A, /obj/item/storage) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ - || istype(A, /obj/item/reagent_containers) || istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) - return - - if(istype(A, /obj/effect/proc_holder/spell)) - return - - if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1) //this block copypasted from reagent_containers/glass, for lack of a better solution - if(!A.reagents.total_volume && A.reagents) - to_chat(user, "[A] is empty.") - return - - if(reagents.total_volume >= reagents.maximum_volume) - to_chat(user, "[src] is full.") - return - - var/trans = A.reagents.trans_to(src, 50) //This is a static amount, otherwise, it'll take forever to fill. - to_chat(user, "You fill [src] with [trans] units of the contents of [A].") - return - - if(reagents.total_volume < amount_per_transfer_from_this) - to_chat(user, "[src] is empty!") - return - - var/contents_log = reagents.reagent_list.Join(", ") - spray(A) - - playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) - user.changeNext_move(CLICK_CD_RANGE*2) - user.newtonian_move(get_dir(A, user)) - - if(reagents.reagent_list.len == 1 && reagents.has_reagent("cleaner")) // Only show space cleaner logs if it's burning people from being too hot or cold - if(reagents.chem_temp < 300 && reagents.chem_temp > 280) // 280 is the cold threshold for slimes, 300 the hot threshold for drask - return - - var/attack_log_type = ATKLOG_MOST - if(reagents.has_reagent("sacid") || reagents.has_reagent("facid") || reagents.has_reagent("lube")) - attack_log_type = ATKLOG_FEW - msg_admin_attack("[key_name_admin(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K", attack_log_type) - log_game("[key_name(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K") - return - - -/obj/item/reagent_containers/spray/proc/spray(var/atom/A) - var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this) - reagents.trans_to(D, amount_per_transfer_from_this, 1/spray_currentrange) - D.icon += mix_color_from_reagents(D.reagents.reagent_list) - spawn(0) - for(var/i=0, iYou [amount_per_transfer_from_this == 10 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray.
    ") - -/obj/item/reagent_containers/spray/examine(mob/user) - . = ..() - if(get_dist(user, src) && user == loc) - . += "[round(reagents.total_volume)] units left." - -/obj/item/reagent_containers/spray/verb/empty() - - set name = "Empty Spray Bottle" - set category = "Object" - set src in usr - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(alert(usr, "Are you sure you want to empty that?", "Empty Bottle:", "Yes", "No") != "Yes") - return - if(isturf(usr.loc) && loc == usr) - to_chat(usr, "You empty [src] onto the floor.") - reagents.reaction(usr.loc) - reagents.clear_reagents() - -//space cleaner -/obj/item/reagent_containers/spray/cleaner - name = "space cleaner" - desc = "BLAM!-brand non-foaming space cleaner!" - list_reagents = list("cleaner" = 250) - -/obj/item/reagent_containers/spray/cleaner/drone - name = "space cleaner" - desc = "BLAM!-brand non-foaming space cleaner!" - volume = 50 - list_reagents = list("cleaner" = 50) - -//spray tan -/obj/item/reagent_containers/spray/spraytan - name = "spray tan" - volume = 50 - desc = "Gyaro brand spray tan. Do not spray near eyes or other orifices." - list_reagents = list("spraytan" = 50) - -//pepperspray -/obj/item/reagent_containers/spray/pepper - name = "pepperspray" - desc = "Manufactured by UhangInc, used to blind and down an opponent quickly." - icon = 'icons/obj/items.dmi' - icon_state = "pepperspray" - item_state = "pepperspray" - volume = 40 - spray_maxrange = 4 - amount_per_transfer_from_this = 5 - list_reagents = list("condensedcapsaicin" = 40) - -//water flower -/obj/item/reagent_containers/spray/waterflower - name = "water flower" - desc = "A seemingly innocent sunflower...with a twist." - icon = 'icons/obj/hydroponics/harvest.dmi' - icon_state = "sunflower" - item_state = "sunflower" - amount_per_transfer_from_this = 1 - volume = 10 - list_reagents = list("water" = 10) - -/obj/item/reagent_containers/spray/waterflower/attack_self(mob/user) //Don't allow changing how much the flower sprays - return - -//chemsprayer -/obj/item/reagent_containers/spray/chemsprayer - name = "chem sprayer" - desc = "A utility used to spray large amounts of reagents in a given area." - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "chemsprayer" - item_state = "chemsprayer" - throwforce = 0 - w_class = WEIGHT_CLASS_NORMAL - spray_maxrange = 7 - spray_currentrange = 7 - amount_per_transfer_from_this = 10 - volume = 600 - origin_tech = "combat=3;materials=3;engineering=3" - - -/obj/item/reagent_containers/spray/chemsprayer/spray(var/atom/A) - var/Sprays[3] - for(var/i=1, i<=3, i++) // intialize sprays - if(reagents.total_volume < 1) break - var/obj/effect/decal/chempuff/D = new/obj/effect/decal/chempuff(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this) - reagents.trans_to(D, amount_per_transfer_from_this) - - D.icon += mix_color_from_reagents(D.reagents.reagent_list) - - Sprays[i] = D - - var/direction = get_dir(src, A) - var/turf/T = get_turf(A) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - var/list/the_targets = list(T,T1,T2) - - for(var/i=1, i<=Sprays.len, i++) - spawn() - var/obj/effect/decal/chempuff/D = Sprays[i] - if(!D) continue - - // Spreads the sprays a little bit - var/turf/my_target = pick(the_targets) - the_targets -= my_target - - for(var/j=0, j<=spray_currentrange, j++) - step_towards(D, my_target) - D.reagents.reaction(get_turf(D)) - for(var/atom/t in get_turf(D)) - D.reagents.reaction(t) - sleep(2) - qdel(D) - - - -/obj/item/reagent_containers/spray/chemsprayer/attack_self(var/mob/user) - - amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10) - to_chat(user, "You adjust the output switch. You'll now use [amount_per_transfer_from_this] units per spray.") - - -// Plant-B-Gone -/obj/item/reagent_containers/spray/plantbgone // -- Skie - name = "Plant-B-Gone" - desc = "Kills those pesky weeds!" - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "plantbgone" - item_state = "plantbgone" - volume = 100 - list_reagents = list("glyphosate" = 100) +/obj/item/reagent_containers/spray + name = "spray bottle" + desc = "A spray bottle, with an unscrewable top." + icon = 'icons/obj/janitor.dmi' + icon_state = "cleaner" + item_state = "cleaner" + flags = NOBLUDGEON + container_type = OPENCONTAINER + slot_flags = SLOT_BELT + throwforce = 0 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 7 + var/spray_maxrange = 3 //what the sprayer will set spray_currentrange to in the attack_self. + var/spray_currentrange = 3 //the range of tiles the sprayer will reach when in fixed mode. + amount_per_transfer_from_this = 5 + volume = 250 + possible_transfer_amounts = null + + +/obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) + if(istype(A, /obj/item/storage) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ + || istype(A, /obj/item/reagent_containers) || istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) + return + + if(istype(A, /obj/effect/proc_holder/spell)) + return + + if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1) //this block copypasted from reagent_containers/glass, for lack of a better solution + if(!A.reagents.total_volume && A.reagents) + to_chat(user, "[A] is empty.") + return + + if(reagents.total_volume >= reagents.maximum_volume) + to_chat(user, "[src] is full.") + return + + var/trans = A.reagents.trans_to(src, 50) //This is a static amount, otherwise, it'll take forever to fill. + to_chat(user, "You fill [src] with [trans] units of the contents of [A].") + return + + if(reagents.total_volume < amount_per_transfer_from_this) + to_chat(user, "[src] is empty!") + return + + var/contents_log = reagents.reagent_list.Join(", ") + spray(A) + + playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) + user.changeNext_move(CLICK_CD_RANGE*2) + user.newtonian_move(get_dir(A, user)) + + if(reagents.reagent_list.len == 1 && reagents.has_reagent("cleaner")) // Only show space cleaner logs if it's burning people from being too hot or cold + if(reagents.chem_temp < 300 && reagents.chem_temp > 280) // 280 is the cold threshold for slimes, 300 the hot threshold for drask + return + + var/attack_log_type = ATKLOG_MOST + if(reagents.has_reagent("sacid") || reagents.has_reagent("facid") || reagents.has_reagent("lube")) + attack_log_type = ATKLOG_FEW + msg_admin_attack("[key_name_admin(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K", attack_log_type) + log_game("[key_name(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K") + return + + +/obj/item/reagent_containers/spray/proc/spray(var/atom/A) + var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) + D.create_reagents(amount_per_transfer_from_this) + reagents.trans_to(D, amount_per_transfer_from_this, 1/spray_currentrange) + D.icon += mix_color_from_reagents(D.reagents.reagent_list) + spawn(0) + for(var/i=0, iYou [amount_per_transfer_from_this == 10 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray.
    ") + +/obj/item/reagent_containers/spray/examine(mob/user) + . = ..() + if(get_dist(user, src) && user == loc) + . += "[round(reagents.total_volume)] units left." + +/obj/item/reagent_containers/spray/verb/empty() + + set name = "Empty Spray Bottle" + set category = "Object" + set src in usr + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(alert(usr, "Are you sure you want to empty that?", "Empty Bottle:", "Yes", "No") != "Yes") + return + if(isturf(usr.loc) && loc == usr) + to_chat(usr, "You empty [src] onto the floor.") + reagents.reaction(usr.loc) + reagents.clear_reagents() + +//space cleaner +/obj/item/reagent_containers/spray/cleaner + name = "space cleaner" + desc = "BLAM!-brand non-foaming space cleaner!" + list_reagents = list("cleaner" = 250) + +/obj/item/reagent_containers/spray/cleaner/drone + name = "space cleaner" + desc = "BLAM!-brand non-foaming space cleaner!" + volume = 50 + list_reagents = list("cleaner" = 50) + +//spray tan +/obj/item/reagent_containers/spray/spraytan + name = "spray tan" + volume = 50 + desc = "Gyaro brand spray tan. Do not spray near eyes or other orifices." + list_reagents = list("spraytan" = 50) + +//pepperspray +/obj/item/reagent_containers/spray/pepper + name = "pepperspray" + desc = "Manufactured by UhangInc, used to blind and down an opponent quickly." + icon = 'icons/obj/items.dmi' + icon_state = "pepperspray" + item_state = "pepperspray" + volume = 40 + spray_maxrange = 4 + amount_per_transfer_from_this = 5 + list_reagents = list("condensedcapsaicin" = 40) + +//water flower +/obj/item/reagent_containers/spray/waterflower + name = "water flower" + desc = "A seemingly innocent sunflower...with a twist." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "sunflower" + item_state = "sunflower" + amount_per_transfer_from_this = 1 + volume = 10 + list_reagents = list("water" = 10) + +/obj/item/reagent_containers/spray/waterflower/attack_self(mob/user) //Don't allow changing how much the flower sprays + return + +//chemsprayer +/obj/item/reagent_containers/spray/chemsprayer + name = "chem sprayer" + desc = "A utility used to spray large amounts of reagents in a given area." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "chemsprayer" + item_state = "chemsprayer" + throwforce = 0 + w_class = WEIGHT_CLASS_NORMAL + spray_maxrange = 7 + spray_currentrange = 7 + amount_per_transfer_from_this = 10 + volume = 600 + origin_tech = "combat=3;materials=3;engineering=3" + + +/obj/item/reagent_containers/spray/chemsprayer/spray(var/atom/A) + var/Sprays[3] + for(var/i=1, i<=3, i++) // intialize sprays + if(reagents.total_volume < 1) break + var/obj/effect/decal/chempuff/D = new/obj/effect/decal/chempuff(get_turf(src)) + D.create_reagents(amount_per_transfer_from_this) + reagents.trans_to(D, amount_per_transfer_from_this) + + D.icon += mix_color_from_reagents(D.reagents.reagent_list) + + Sprays[i] = D + + var/direction = get_dir(src, A) + var/turf/T = get_turf(A) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + var/list/the_targets = list(T,T1,T2) + + for(var/i=1, i<=Sprays.len, i++) + spawn() + var/obj/effect/decal/chempuff/D = Sprays[i] + if(!D) continue + + // Spreads the sprays a little bit + var/turf/my_target = pick(the_targets) + the_targets -= my_target + + for(var/j=0, j<=spray_currentrange, j++) + step_towards(D, my_target) + D.reagents.reaction(get_turf(D)) + for(var/atom/t in get_turf(D)) + D.reagents.reaction(t) + sleep(2) + qdel(D) + + + +/obj/item/reagent_containers/spray/chemsprayer/attack_self(var/mob/user) + + amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10) + to_chat(user, "You adjust the output switch. You'll now use [amount_per_transfer_from_this] units per spray.") + + +// Plant-B-Gone +/obj/item/reagent_containers/spray/plantbgone // -- Skie + name = "Plant-B-Gone" + desc = "Kills those pesky weeds!" + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "plantbgone" + item_state = "plantbgone" + volume = 100 + list_reagents = list("glyphosate" = 100) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 0d0b05014b6e..e87e72d8750d 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -1,229 +1,234 @@ -#define SYRINGE_DRAW 0 -#define SYRINGE_INJECT 1 -#define SYRINGE_BROKEN 2 - -/obj/item/reagent_containers/syringe - name = "Syringe" - desc = "A syringe." - icon = 'icons/goonstation/objects/syringe.dmi' - item_state = "syringe_0" - icon_state = "0" - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list() - volume = 15 - sharp = TRUE - var/busy = FALSE - var/mode = SYRINGE_DRAW - var/projectile_type = /obj/item/projectile/bullet/dart/syringe - materials = list(MAT_METAL=10, MAT_GLASS=20) - container_type = TRANSPARENT - -/obj/item/reagent_containers/syringe/New() - ..() - if(list_reagents) //syringe starts in inject mode if its already got something inside - mode = SYRINGE_INJECT - update_icon() - -/obj/item/reagent_containers/syringe/on_reagent_change() - update_icon() - -/obj/item/reagent_containers/syringe/pickup(mob/user) - . = ..() - update_icon() - -/obj/item/reagent_containers/syringe/dropped(mob/user) - ..() - update_icon() - -/obj/item/reagent_containers/syringe/attack_self(mob/user) - mode = !mode - update_icon() - -/obj/item/reagent_containers/syringe/attack_hand() - ..() - update_icon() - -/obj/item/reagent_containers/syringe/attack(mob/living/M, mob/living/user, def_zone) - return - -/obj/item/reagent_containers/syringe/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/storage/bag)) - ..() - -/obj/item/reagent_containers/syringe/afterattack(atom/target, mob/user , proximity) - if(!proximity) - return - if(!target.reagents) - return - - var/mob/living/L - if(isliving(target)) - L = target - if(!L.can_inject(user, TRUE)) - return - - switch(mode) - if(SYRINGE_DRAW) - - if(reagents.holder_full()) - to_chat(user, "The syringe is full.") - return - - if(L) //living mob - var/drawn_amount = reagents.maximum_volume - reagents.total_volume - if(target != user) - target.visible_message("[user] is trying to take a blood sample from [target]!", \ - "[user] is trying to take a blood sample from [target]!") - busy = TRUE - if(!do_mob(user, target)) - busy = FALSE - return - if(reagents.holder_full()) - return - busy = FALSE - if(L.transfer_blood_to(src, drawn_amount)) - user.visible_message("[user] takes a blood sample from [L].") - else - to_chat(user, "You are unable to draw any blood from [L]!") - - else //if not mob - if(!target.reagents.total_volume) - to_chat(user, "[target] is empty!") - return - - if(!target.is_drawable(user)) - to_chat(user, "You cannot directly remove reagents from [target]!") - return - - var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) // transfer from, transfer to - who cares? - - to_chat(user, "You fill [src] with [trans] units of the solution. It now contains [reagents.total_volume] units.") - if(reagents.holder_full()) - mode = !mode - update_icon() - - if(SYRINGE_INJECT) - if(!reagents.total_volume) - to_chat(user, "[src] is empty.") - return - - if(!L && !target.is_injectable(user)) //only checks on non-living mobs, due to how can_inject() handles - to_chat(user, "You cannot directly fill [target]!") - return - - if(target.reagents.total_volume >= target.reagents.maximum_volume) - to_chat(user, "[target] is full.") - return - - if(L) //living mob - if(!L.can_inject(user, TRUE)) - return - if(L != user) - L.visible_message("[user] is trying to inject [L]!", \ - "[user] is trying to inject you!") - if(!do_mob(user, L)) - return - if(!reagents.total_volume) - return - if(L.reagents.total_volume >= L.reagents.maximum_volume) - return - L.visible_message("[user] injects [L] with the syringe!", \ - "[user] injects [L] with the syringe!") - - var/list/rinject = list() - for(var/datum/reagent/R in reagents.reagent_list) - rinject += R.name - var/contained = english_list(rinject) - - add_attack_logs(user, L, "Injected with [name] containing [contained], transfered [amount_per_transfer_from_this] units", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) - - var/fraction = min(amount_per_transfer_from_this / reagents.total_volume, 1) - reagents.reaction(L, REAGENT_INGEST, fraction) - reagents.trans_to(target, amount_per_transfer_from_this) - to_chat(user, "You inject [amount_per_transfer_from_this] units of the solution. The syringe now contains [reagents.total_volume] units.") - if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) - mode = SYRINGE_DRAW - update_icon() - -/obj/item/reagent_containers/syringe/update_icon() - cut_overlays() - var/rounded_vol - if(reagents && reagents.total_volume) - rounded_vol = Clamp(round((reagents.total_volume / volume * 15), 5), 1, 15) - var/image/filling_overlay = mutable_appearance('icons/obj/reagentfillings.dmi', "syringe[rounded_vol]") - filling_overlay.icon += mix_color_from_reagents(reagents.reagent_list) - add_overlay(filling_overlay) - else - rounded_vol = 0 - icon_state = "[rounded_vol]" - item_state = "syringe_[rounded_vol]" - if(ismob(loc)) - var/mob/M = loc - var/injoverlay - switch(mode) - if(SYRINGE_DRAW) - injoverlay = "draw" - if(SYRINGE_INJECT) - injoverlay = "inject" - add_overlay(injoverlay) - M.update_inv_l_hand() - M.update_inv_r_hand() - -/obj/item/reagent_containers/syringe/antiviral - name = "Syringe (spaceacillin)" - desc = "Contains antiviral agents." - list_reagents = list("spaceacillin" = 15) - -/obj/item/reagent_containers/syringe/charcoal - name = "Syringe (charcoal)" - desc = "Contains charcoal - used to treat toxins and damage from toxins." - list_reagents = list("charcoal" = 15) - -/obj/item/reagent_containers/syringe/epinephrine - name = "Syringe (Epinephrine)" - desc = "Contains epinephrine - used to stabilize patients." - list_reagents = list("epinephrine" = 15) - -/obj/item/reagent_containers/syringe/insulin - name = "Syringe (insulin)" - desc = "Contains insulin - used to treat diabetes." - list_reagents = list("insulin" = 15) - -/obj/item/reagent_containers/syringe/calomel - name = "Syringe (calomel)" - desc = "Contains calomel, which be used to purge impurities, but is highly toxic itself." - list_reagents = list("calomel" = 15) - -/obj/item/reagent_containers/syringe/bioterror - name = "bioterror syringe" - desc = "Contains several paralyzing reagents." - list_reagents = list("neurotoxin" = 5, "capulettium_plus" = 5, "sodium_thiopental" = 5) - -/obj/item/reagent_containers/syringe/gluttony - name = "Gluttony's Blessing" - desc = "A syringe recovered from a dread place. It probably isn't wise to use." - amount_per_transfer_from_this = 1 - volume = 1 - list_reagents = list("gluttonytoxin" = 1) - -/obj/item/reagent_containers/syringe/capulettium_plus - name = "capulettium plus syringe" - desc = "For silencing targets. Allows for fake deaths." - list_reagents = list("capulettium_plus" = 15) - -/obj/item/reagent_containers/syringe/sarin - name = "sarin syringe" - desc = "A deadly neurotoxin, for killing." - list_reagents = list("sarin" = 15) - -/obj/item/reagent_containers/syringe/pancuronium - name = "pancuronium syringe" - desc = "A powerful paralyzing poison." - list_reagents = list("pancuronium" = 15) - -/obj/item/reagent_containers/syringe/lethal - name = "lethal injection syringe" - desc = "A syringe used for lethal injections. It can hold up to 50 units." - amount_per_transfer_from_this = 50 - volume = 50 - list_reagents = list("toxin" = 15, "pancuronium" = 10, "cyanide" = 5, "facid" = 10, "fluorine" = 10) \ No newline at end of file +#define SYRINGE_DRAW 0 +#define SYRINGE_INJECT 1 +#define SYRINGE_BROKEN 2 + +/obj/item/reagent_containers/syringe + name = "Syringe" + desc = "A syringe." + icon = 'icons/goonstation/objects/syringe.dmi' + item_state = "syringe_0" + icon_state = "0" + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list() + volume = 15 + sharp = TRUE + var/busy = FALSE + var/mode = SYRINGE_DRAW + var/projectile_type = /obj/item/projectile/bullet/dart/syringe + materials = list(MAT_METAL=10, MAT_GLASS=20) + container_type = TRANSPARENT + +/obj/item/reagent_containers/syringe/New() + ..() + if(list_reagents) //syringe starts in inject mode if its already got something inside + mode = SYRINGE_INJECT + update_icon() + +/obj/item/reagent_containers/syringe/on_reagent_change() + update_icon() + +/obj/item/reagent_containers/syringe/pickup(mob/user) + . = ..() + update_icon() + +/obj/item/reagent_containers/syringe/dropped(mob/user) + ..() + update_icon() + +/obj/item/reagent_containers/syringe/attack_self(mob/user) + mode = !mode + update_icon() + +/obj/item/reagent_containers/syringe/attack_hand() + ..() + update_icon() + +/obj/item/reagent_containers/syringe/attack(mob/living/M, mob/living/user, def_zone) + return + +/obj/item/reagent_containers/syringe/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/storage/bag)) + ..() + +/obj/item/reagent_containers/syringe/afterattack(atom/target, mob/user , proximity) + if(!proximity) + return + if(!target.reagents) + return + + var/mob/living/L + if(isliving(target)) + L = target + if(!L.can_inject(user, TRUE)) + return + + switch(mode) + if(SYRINGE_DRAW) + + if(reagents.holder_full()) + to_chat(user, "The syringe is full.") + return + + if(L) //living mob + var/drawn_amount = reagents.maximum_volume - reagents.total_volume + if(target != user) + target.visible_message("[user] is trying to take a blood sample from [target]!", \ + "[user] is trying to take a blood sample from [target]!") + busy = TRUE + if(!do_mob(user, target)) + busy = FALSE + return + if(reagents.holder_full()) + return + busy = FALSE + if(L.transfer_blood_to(src, drawn_amount)) + user.visible_message("[user] takes a blood sample from [L].") + else + to_chat(user, "You are unable to draw any blood from [L]!") + + else //if not mob + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty!") + return + + if(!target.is_drawable(user)) + to_chat(user, "You cannot directly remove reagents from [target]!") + return + + var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) // transfer from, transfer to - who cares? + + to_chat(user, "You fill [src] with [trans] units of the solution. It now contains [reagents.total_volume] units.") + if(reagents.holder_full()) + mode = !mode + update_icon() + + if(SYRINGE_INJECT) + if(!reagents.total_volume) + to_chat(user, "[src] is empty.") + return + + if(!L && !target.is_injectable(user)) //only checks on non-living mobs, due to how can_inject() handles + to_chat(user, "You cannot directly fill [target]!") + return + + if(target.reagents.total_volume >= target.reagents.maximum_volume) + to_chat(user, "[target] is full.") + return + + if(L) //living mob + if(!L.can_inject(user, TRUE)) + return + if(L != user) + L.visible_message("[user] is trying to inject [L]!", \ + "[user] is trying to inject you!") + if(!do_mob(user, L)) + return + if(!reagents.total_volume) + return + if(L.reagents.total_volume >= L.reagents.maximum_volume) + return + L.visible_message("[user] injects [L] with the syringe!", \ + "[user] injects [L] with the syringe!") + + var/list/rinject = list() + for(var/datum/reagent/R in reagents.reagent_list) + rinject += R.name + var/contained = english_list(rinject) + + add_attack_logs(user, L, "Injected with [name] containing [contained], transfered [amount_per_transfer_from_this] units", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) + + var/fraction = min(amount_per_transfer_from_this / reagents.total_volume, 1) + reagents.reaction(L, REAGENT_INGEST, fraction) + reagents.trans_to(target, amount_per_transfer_from_this) + to_chat(user, "You inject [amount_per_transfer_from_this] units of the solution. The syringe now contains [reagents.total_volume] units.") + if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) + mode = SYRINGE_DRAW + update_icon() + +/obj/item/reagent_containers/syringe/update_icon() + cut_overlays() + var/rounded_vol + if(reagents && reagents.total_volume) + rounded_vol = Clamp(round((reagents.total_volume / volume * 15), 5), 1, 15) + var/image/filling_overlay = mutable_appearance('icons/obj/reagentfillings.dmi', "syringe[rounded_vol]") + filling_overlay.icon += mix_color_from_reagents(reagents.reagent_list) + add_overlay(filling_overlay) + else + rounded_vol = 0 + icon_state = "[rounded_vol]" + item_state = "syringe_[rounded_vol]" + if(ismob(loc)) + var/mob/M = loc + var/injoverlay + switch(mode) + if(SYRINGE_DRAW) + injoverlay = "draw" + if(SYRINGE_INJECT) + injoverlay = "inject" + add_overlay(injoverlay) + M.update_inv_l_hand() + M.update_inv_r_hand() + +/obj/item/reagent_containers/syringe/antiviral + name = "Syringe (spaceacillin)" + desc = "Contains antiviral agents." + list_reagents = list("spaceacillin" = 15) + +/obj/item/reagent_containers/syringe/charcoal + name = "Syringe (charcoal)" + desc = "Contains charcoal - used to treat toxins and damage from toxins." + list_reagents = list("charcoal" = 15) + +/obj/item/reagent_containers/syringe/epinephrine + name = "Syringe (Epinephrine)" + desc = "Contains epinephrine - used to stabilize patients." + list_reagents = list("epinephrine" = 15) + +/obj/item/reagent_containers/syringe/insulin + name = "Syringe (insulin)" + desc = "Contains insulin - used to treat diabetes." + list_reagents = list("insulin" = 15) + +/obj/item/reagent_containers/syringe/calomel + name = "Syringe (calomel)" + desc = "Contains calomel, which be used to purge impurities, but is highly toxic itself." + list_reagents = list("calomel" = 15) + +/obj/item/reagent_containers/syringe/heparin + name = "Syringe (heparin)" + desc = "Contains heparin, a blood anticoagulant." + list_reagents = list("heparin" = 15) + +/obj/item/reagent_containers/syringe/bioterror + name = "bioterror syringe" + desc = "Contains several paralyzing reagents." + list_reagents = list("neurotoxin" = 5, "capulettium_plus" = 5, "sodium_thiopental" = 5) + +/obj/item/reagent_containers/syringe/gluttony + name = "Gluttony's Blessing" + desc = "A syringe recovered from a dread place. It probably isn't wise to use." + amount_per_transfer_from_this = 1 + volume = 1 + list_reagents = list("gluttonytoxin" = 1) + +/obj/item/reagent_containers/syringe/capulettium_plus + name = "capulettium plus syringe" + desc = "For silencing targets. Allows for fake deaths." + list_reagents = list("capulettium_plus" = 15) + +/obj/item/reagent_containers/syringe/sarin + name = "sarin syringe" + desc = "A deadly neurotoxin, for killing." + list_reagents = list("sarin" = 15) + +/obj/item/reagent_containers/syringe/pancuronium + name = "pancuronium syringe" + desc = "A powerful paralyzing poison." + list_reagents = list("pancuronium" = 15) + +/obj/item/reagent_containers/syringe/lethal + name = "lethal injection syringe" + desc = "A syringe used for lethal injections. It can hold up to 50 units." + amount_per_transfer_from_this = 50 + volume = 50 + list_reagents = list("toxin" = 15, "pancuronium" = 10, "cyanide" = 5, "facid" = 10, "fluorine" = 10) diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index 1c74be11965e..48c354153876 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -1,278 +1,284 @@ -/obj/structure/reagent_dispensers - name = "Dispenser" - desc = "..." - icon = 'icons/obj/objects.dmi' - icon_state = "watertank" - density = 1 - anchored = 0 - pressure_resistance = 2*ONE_ATMOSPHERE - container_type = DRAINABLE | AMOUNT_VISIBLE - max_integrity = 300 - var/tank_volume = 1000 //In units, how much the dispenser can hold - var/reagent_id = "water" //The ID of the reagent that the dispenser uses - var/lastrigger = "" // The last person to rig this fuel tank - Stored with the object. Only the last person matter for investigation - -/obj/structure/reagent_dispensers/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - if(tank_volume && (damage_flag == "bullet" || damage_flag == "laser")) - boom() - -/obj/structure/reagent_dispensers/attackby(obj/item/I, mob/user, params) - if(I.is_refillable()) - return FALSE //so we can refill them via their afterattack. - return ..() - -/obj/structure/reagent_dispensers/New() - create_reagents(tank_volume) - reagents.add_reagent(reagent_id, tank_volume) - ..() - -/obj/structure/reagent_dispensers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(reagents) - for(var/i in 1 to 8) - if(reagents) - reagents.temperature_reagents(exposed_temperature) - -/obj/structure/reagent_dispensers/proc/boom() - visible_message("[src] ruptures!") - chem_splash(loc, 5, list(reagents)) - qdel(src) - -/obj/structure/reagent_dispensers/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!disassembled) - boom() - else - qdel(src) - -//Dispensers -/obj/structure/reagent_dispensers/watertank - name = "water tank" - desc = "A water tank." - icon_state = "water" - -/obj/structure/reagent_dispensers/watertank/high - name = "high-capacity water tank" - desc = "A highly-pressurized water tank made to hold gargantuan amounts of water.." - icon_state = "water_high" //I was gonna clean my room... - tank_volume = 100000 - - -/obj/structure/reagent_dispensers/oil - name = "oil tank" - desc = "A tank of oil, commonly used to by robotics to fix leaking IPCs or just to loosen up those rusted underused parts." - icon_state = "oil" - reagent_id = "oil" - tank_volume = 3000 - -/obj/structure/reagent_dispensers/fueltank - name = "fuel tank" - desc = "A tank full of industrial welding fuel. Do not consume." - icon_state = "fuel" - reagent_id = "fuel" - tank_volume = 4000 - var/obj/item/assembly_holder/rig = null - var/accepts_rig = 1 - -/obj/structure/reagent_dispensers/fueltank/Destroy() - QDEL_NULL(rig) - return ..() - -/obj/structure/reagent_dispensers/fueltank/bullet_act(obj/item/projectile/P) - ..() - if(!QDELETED(src)) //wasn't deleted by the projectile's effects. - if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE))) - message_admins("[key_name_admin(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)] ") - log_game("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]") - investigate_log("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]", INVESTIGATE_BOMB) - boom() - -/obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion - if(rigtrigger) // If the explosion is triggered by an assembly holder - message_admins("A fueltank, last rigged by [lastrigger], was triggered at [COORD(loc)]") // Then admin is informed of the last person who rigged the fuel tank - log_game("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]") - investigate_log("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]", INVESTIGATE_BOMB) - if(reagents) - reagents.set_reagent_temp(1000) //uh-oh - qdel(src) - -/obj/structure/reagent_dispensers/fueltank/blob_act(obj/structure/blob/B) - boom() - -/obj/structure/reagent_dispensers/fueltank/ex_act() - boom() - -/obj/structure/reagent_dispensers/fueltank/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - ..() - boom() - -/obj/structure/reagent_dispensers/fueltank/tesla_act() - ..() //extend the zap - boom() - -/obj/structure/reagent_dispensers/fueltank/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2 && rig) - . += "There is some kind of device rigged to the tank." - -/obj/structure/reagent_dispensers/fueltank/attack_hand() - if(rig) - usr.visible_message("[usr] begins to detach [rig] from [src].", "You begin to detach [rig] from [src].") - if(do_after(usr, 20, target = src)) - usr.visible_message("[usr] detaches [rig] from [src].", "You detach [rig] from [src].") - rig.forceMove(get_turf(usr)) - rig = null - lastrigger = null - overlays.Cut() - -/obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/assembly_holder) && accepts_rig) - if(rig) - to_chat(user, "There is another device in the way.") - return ..() - user.visible_message("[user] begins rigging [I] to [src].", "You begin rigging [I] to [src]") - if(do_after(user, 20, target = src)) - user.visible_message("[user] rigs [I] to [src].", "You rig [I] to [src].") - - var/obj/item/assembly_holder/H = I - if(istype(H.a_left, /obj/item/assembly/igniter) || istype(H.a_right, /obj/item/assembly/igniter)) - msg_admin_attack("[key_name_admin(user)] rigged [src.name] with [I.name] for explosion (JMP)", ATKLOG_FEW) - log_game("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]") - investigate_log("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]", INVESTIGATE_BOMB) - - lastrigger = "[key_name(user)]" - rig = H - user.drop_item() - H.forceMove(src) - - var/icon/test = getFlatIcon(H) - test.Shift(NORTH, 1) - test.Shift(EAST, 6) - overlays += test - else - return ..() - -obj/structure/reagent_dispensers/fueltank/welder_act(mob/user, obj/item/I) - . = TRUE - if(!reagents.has_reagent("fuel")) - to_chat(user, "[src] is out of fuel!") - return - if(I.tool_enabled && I.use_tool(src, user, volume = I.tool_volume)) //check it's enabled first to prevent duplicate messages when refuelling - user.visible_message("[user] catastrophically fails at refilling [user.p_their()] [I]!", "That was stupid of you.") - message_admins("[key_name_admin(user)] triggered a fueltank explosion at [COORD(loc)]") - log_game("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]") - investigate_log("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]", INVESTIGATE_BOMB) - boom() - else - I.refill(user, src, reagents.get_reagent_amount("fuel")) //Try dump all fuel into the welder - - -/obj/structure/reagent_dispensers/fueltank/Move() - ..() - if(rig) - rig.process_movement() - -/obj/structure/reagent_dispensers/fueltank/HasProximity(atom/movable/AM) - if(rig) - rig.HasProximity(AM) - -/obj/structure/reagent_dispensers/fueltank/Crossed(atom/movable/AM, oldloc) - if(rig) - rig.Crossed(AM, oldloc) - -/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces) - if(rig) - rig.hear_talk(M, message_pieces) - -/obj/structure/reagent_dispensers/fueltank/hear_message(mob/living/M, msg) - if(rig) - rig.hear_message(M, msg) - -/obj/structure/reagent_dispensers/fueltank/Bump() - ..() - if(rig) - rig.process_movement() - - -/obj/structure/reagent_dispensers/peppertank - name = "pepper spray refiller" - desc = "Contains condensed capsaicin for use in law \"enforcement.\"" - icon_state = "pepper" - anchored = 1 - density = 0 - reagent_id = "condensedcapsaicin" - -/obj/structure/reagent_dispensers/water_cooler - name = "liquid cooler" - desc = "A machine that dispenses liquid to drink." - icon = 'icons/obj/vending.dmi' - icon_state = "water_cooler" - anchored = 1 - tank_volume = 500 - reagent_id = "water" - var/paper_cups = 25 //Paper cups left from the cooler - -/obj/structure/reagent_dispensers/water_cooler/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - . += "There are [paper_cups ? paper_cups : "no"] paper cups left." - -/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/living/user) - if(!paper_cups) - to_chat(user, "There aren't any cups left!") - return - user.visible_message("[user] takes a cup from [src].", "You take a paper cup from [src].") - var/obj/item/reagent_containers/food/drinks/sillycup/S = new(get_turf(src)) - user.put_in_hands(S) - paper_cups-- - -/obj/structure/reagent_dispensers/water_cooler/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - default_unfasten_wrench(user, I, 40) - -/obj/structure/reagent_dispensers/beerkeg - name = "beer keg" - desc = "Beer is liquid bread, it's good for you..." - icon_state = "beer" - reagent_id = "beer" - -/obj/structure/reagent_dispensers/beerkeg/blob_act(obj/structure/blob/B) - explosion(loc, 0, 3, 5, 7, 10) - if(!QDELETED(src)) - qdel(src) - -/obj/structure/reagent_dispensers/beerkeg/nuke - name = "Nanotrasen-brand nuclear fission explosive" - desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap\ - to produce and devestatingly effective. Signs explain that though this is just a model, every Nanotrasen station is equipped with one, just in case. \ - All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nuclearbomb0" - -/obj/structure/reagent_dispensers/virusfood - name = "virus food dispenser" - desc = "A dispenser of low-potency virus mutagenic." - icon_state = "virus_food" - anchored = 1 - density = 0 - reagent_id = "virusfood" - -/obj/structure/reagent_dispensers/spacecleanertank - name = "space cleaner refiller" - desc = "Refills space cleaner bottles." - icon_state = "cleaner" - anchored = 1 - density = 0 - tank_volume = 5000 - reagent_id = "cleaner" - -/obj/structure/reagent_dispensers/fueltank/chem - icon_state = "fuel_chem" - anchored = 1 - density = 0 - accepts_rig = 0 - tank_volume = 1000 +/obj/structure/reagent_dispensers + name = "Dispenser" + desc = "..." + icon = 'icons/obj/objects.dmi' + icon_state = "watertank" + density = 1 + anchored = 0 + pressure_resistance = 2*ONE_ATMOSPHERE + container_type = DRAINABLE | AMOUNT_VISIBLE + max_integrity = 300 + var/tank_volume = 1000 //In units, how much the dispenser can hold + var/reagent_id = "water" //The ID of the reagent that the dispenser uses + var/lastrigger = "" // The last person to rig this fuel tank - Stored with the object. Only the last person matter for investigation + +/obj/structure/reagent_dispensers/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + if(tank_volume && (damage_flag == "bullet" || damage_flag == "laser")) + boom(FALSE, TRUE) + +/obj/structure/reagent_dispensers/attackby(obj/item/I, mob/user, params) + if(I.is_refillable()) + return FALSE //so we can refill them via their afterattack. + return ..() + +/obj/structure/reagent_dispensers/New() + create_reagents(tank_volume) + reagents.add_reagent(reagent_id, tank_volume) + ..() + +/obj/structure/reagent_dispensers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(reagents) + for(var/i in 1 to 8) + if(reagents) + reagents.temperature_reagents(exposed_temperature) + +/obj/structure/reagent_dispensers/proc/boom() + visible_message("[src] ruptures!") + chem_splash(loc, 5, list(reagents)) + qdel(src) + +/obj/structure/reagent_dispensers/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!disassembled) + boom(FALSE, TRUE) + else + qdel(src) + +//Dispensers +/obj/structure/reagent_dispensers/watertank + name = "water tank" + desc = "A water tank." + icon_state = "water" + +/obj/structure/reagent_dispensers/watertank/high + name = "high-capacity water tank" + desc = "A highly-pressurized water tank made to hold gargantuan amounts of water.." + icon_state = "water_high" //I was gonna clean my room... + tank_volume = 100000 + + +/obj/structure/reagent_dispensers/oil + name = "oil tank" + desc = "A tank of oil, commonly used to by robotics to fix leaking IPCs or just to loosen up those rusted underused parts." + icon_state = "oil" + reagent_id = "oil" + tank_volume = 3000 + +/obj/structure/reagent_dispensers/fueltank + name = "fuel tank" + desc = "A tank full of industrial welding fuel. Do not consume." + icon_state = "fuel" + reagent_id = "fuel" + tank_volume = 4000 + var/obj/item/assembly_holder/rig = null + var/accepts_rig = 1 + +/obj/structure/reagent_dispensers/fueltank/Destroy() + QDEL_NULL(rig) + return ..() + +/obj/structure/reagent_dispensers/fueltank/bullet_act(obj/item/projectile/P) + ..() + if(!QDELETED(src)) //wasn't deleted by the projectile's effects. + if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE))) + message_admins("[key_name_admin(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)] ") + add_attack_logs(P.firer, src, "shot with [P.name]") + log_game("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]") + investigate_log("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]", INVESTIGATE_BOMB) + boom() + +/obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE, log_attack = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion + if(rigtrigger) // If the explosion is triggered by an assembly holder + message_admins("A fueltank, last rigged by [lastrigger], was triggered at [COORD(loc)]") // Then admin is informed of the last person who rigged the fuel tank + log_game("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]") + add_attack_logs(lastrigger, src, "rigged fuel tank exploded") + investigate_log("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]", INVESTIGATE_BOMB) + if(log_attack) + add_attack_logs(usr, src, "blew up", ATKLOG_FEW) + if(reagents) + reagents.set_reagent_temp(1000) //uh-oh + qdel(src) + +/obj/structure/reagent_dispensers/fueltank/blob_act(obj/structure/blob/B) + boom() + +/obj/structure/reagent_dispensers/fueltank/ex_act() + boom() + +/obj/structure/reagent_dispensers/fueltank/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + ..() + boom() + +/obj/structure/reagent_dispensers/fueltank/tesla_act() + ..() //extend the zap + boom() + +/obj/structure/reagent_dispensers/fueltank/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2 && rig) + . += "There is some kind of device rigged to the tank." + +/obj/structure/reagent_dispensers/fueltank/attack_hand() + if(rig) + usr.visible_message("[usr] begins to detach [rig] from [src].", "You begin to detach [rig] from [src].") + if(do_after(usr, 20, target = src)) + usr.visible_message("[usr] detaches [rig] from [src].", "You detach [rig] from [src].") + rig.forceMove(get_turf(usr)) + rig = null + lastrigger = null + overlays.Cut() + +/obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/assembly_holder) && accepts_rig) + if(rig) + to_chat(user, "There is another device in the way.") + return ..() + user.visible_message("[user] begins rigging [I] to [src].", "You begin rigging [I] to [src]") + if(do_after(user, 20, target = src)) + user.visible_message("[user] rigs [I] to [src].", "You rig [I] to [src].") + + var/obj/item/assembly_holder/H = I + if(istype(H.a_left, /obj/item/assembly/igniter) || istype(H.a_right, /obj/item/assembly/igniter)) + msg_admin_attack("[key_name_admin(user)] rigged [src.name] with [I.name] for explosion (JMP)", ATKLOG_FEW) + log_game("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]") + add_attack_logs(user, src, "rigged fuel tank") + investigate_log("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]", INVESTIGATE_BOMB) + + lastrigger = "[key_name(user)]" + rig = H + user.drop_item() + H.forceMove(src) + + var/icon/test = getFlatIcon(H) + test.Shift(NORTH, 1) + test.Shift(EAST, 6) + overlays += test + else + return ..() + +obj/structure/reagent_dispensers/fueltank/welder_act(mob/user, obj/item/I) + . = TRUE + if(!reagents.has_reagent("fuel")) + to_chat(user, "[src] is out of fuel!") + return + if(I.tool_enabled && I.use_tool(src, user, volume = I.tool_volume)) //check it's enabled first to prevent duplicate messages when refuelling + user.visible_message("[user] catastrophically fails at refilling [user.p_their()] [I]!", "That was stupid of you.") + message_admins("[key_name_admin(user)] triggered a fueltank explosion at [COORD(loc)]") + log_game("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]") + add_attack_logs(user, src, "hit with lit welder") + investigate_log("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]", INVESTIGATE_BOMB) + boom() + else + I.refill(user, src, reagents.get_reagent_amount("fuel")) //Try dump all fuel into the welder + + +/obj/structure/reagent_dispensers/fueltank/Move() + ..() + if(rig) + rig.process_movement() + +/obj/structure/reagent_dispensers/fueltank/HasProximity(atom/movable/AM) + if(rig) + rig.HasProximity(AM) + +/obj/structure/reagent_dispensers/fueltank/Crossed(atom/movable/AM, oldloc) + if(rig) + rig.Crossed(AM, oldloc) + +/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces) + if(rig) + rig.hear_talk(M, message_pieces) + +/obj/structure/reagent_dispensers/fueltank/hear_message(mob/living/M, msg) + if(rig) + rig.hear_message(M, msg) + +/obj/structure/reagent_dispensers/fueltank/Bump() + ..() + if(rig) + rig.process_movement() + + +/obj/structure/reagent_dispensers/peppertank + name = "pepper spray refiller" + desc = "Contains condensed capsaicin for use in law \"enforcement.\"" + icon_state = "pepper" + anchored = 1 + density = 0 + reagent_id = "condensedcapsaicin" + +/obj/structure/reagent_dispensers/water_cooler + name = "liquid cooler" + desc = "A machine that dispenses liquid to drink." + icon = 'icons/obj/vending.dmi' + icon_state = "water_cooler" + anchored = 1 + tank_volume = 500 + reagent_id = "water" + var/paper_cups = 25 //Paper cups left from the cooler + +/obj/structure/reagent_dispensers/water_cooler/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + . += "There are [paper_cups ? paper_cups : "no"] paper cups left." + +/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/living/user) + if(!paper_cups) + to_chat(user, "There aren't any cups left!") + return + user.visible_message("[user] takes a cup from [src].", "You take a paper cup from [src].") + var/obj/item/reagent_containers/food/drinks/sillycup/S = new(get_turf(src)) + user.put_in_hands(S) + paper_cups-- + +/obj/structure/reagent_dispensers/water_cooler/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I, 40) + +/obj/structure/reagent_dispensers/beerkeg + name = "beer keg" + desc = "Beer is liquid bread, it's good for you..." + icon_state = "beer" + reagent_id = "beer" + +/obj/structure/reagent_dispensers/beerkeg/blob_act(obj/structure/blob/B) + explosion(loc, 0, 3, 5, 7, 10) + if(!QDELETED(src)) + qdel(src) + +/obj/structure/reagent_dispensers/beerkeg/nuke + name = "Nanotrasen-brand nuclear fission explosive" + desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap\ + to produce and devestatingly effective. Signs explain that though this is just a model, every Nanotrasen station is equipped with one, just in case. \ + All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nuclearbomb0" + +/obj/structure/reagent_dispensers/virusfood + name = "virus food dispenser" + desc = "A dispenser of low-potency virus mutagenic." + icon_state = "virus_food" + anchored = 1 + density = 0 + reagent_id = "virusfood" + +/obj/structure/reagent_dispensers/spacecleanertank + name = "space cleaner refiller" + desc = "Refills space cleaner bottles." + icon_state = "cleaner" + anchored = 1 + density = 0 + tank_volume = 5000 + reagent_id = "cleaner" + +/obj/structure/reagent_dispensers/fueltank/chem + icon_state = "fuel_chem" + anchored = 1 + density = 0 + accepts_rig = 0 + tank_volume = 1000 diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 4d242212f2c3..e82db0ddc603 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -1,492 +1,492 @@ -#define DIRECTION_FORWARDS 1 -#define DIRECTION_OFF 0 -#define DIRECTION_REVERSED -1 -#define IS_OPERATING (operating && can_conveyor_run()) - -GLOBAL_LIST_INIT(conveyor_belts, list()) //Saves us having to look through the entire machines list for our things -GLOBAL_LIST_INIT(conveyor_switches, list()) - -//conveyor2 is pretty much like the original, except it supports corners, but not diverters. -//Except this is pretty heavily modified so it's more like conveyor2.5 - -/obj/machinery/conveyor - icon = 'icons/obj/recycling.dmi' - icon_state = "conveyor_stopped_cw" - name = "conveyor belt" - desc = "It's a conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly." - layer = CONVEYOR_LAYER // so they appear under stuff but not below stuff like vents - anchored = TRUE - move_force = MOVE_FORCE_DEFAULT - var/operating = FALSE //NB: this can be TRUE while the belt doesn't go - var/forwards // The direction the conveyor sends you in - var/backwards // hopefully self-explanatory - var/clockwise = TRUE // For corner pieces - do we go clockwise or counterclockwise? - var/operable = TRUE // Can this belt actually go? - var/list/affecting // the list of all items that will be moved this ptick - var/reversed = FALSE // set to TRUE to have the conveyor belt be reversed - var/id //ID of the connected lever - - // create a conveyor -/obj/machinery/conveyor/New(loc, new_dir, new_id) - ..(loc) - GLOB.conveyor_belts += src - if(new_id) - id = new_id - if(new_dir) - dir = new_dir - update_move_direction() - for(var/I in GLOB.conveyor_switches) - var/obj/machinery/conveyor_switch/S = I - if(id == S.id) - S.conveyors += src - -/obj/machinery/conveyor/Destroy() - GLOB.conveyor_belts -= src - return ..() - -/obj/machinery/conveyor/setDir(newdir) - . = ..() - update_move_direction() - -// attack with item, place item on conveyor -/obj/machinery/conveyor/attackby(obj/item/I, mob/user) - if(stat & BROKEN) - return ..() - else if(istype(I, /obj/item/conveyor_switch_construct)) - var/obj/item/conveyor_switch_construct/S = I - if(S.id == id) - return ..() - for(var/obj/machinery/conveyor_switch/CS in GLOB.conveyor_switches) - if(CS.id == id) - CS.conveyors -= src - id = S.id - to_chat(user, "You link [I] with [src].") - else if(user.a_intent != INTENT_HARM) - if(user.drop_item()) - I.forceMove(loc) - else - return ..() - - -/obj/machinery/conveyor/crowbar_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(!(stat & BROKEN)) - var/obj/item/conveyor_construct/C = new(loc) - C.id = id - transfer_fingerprints_to(C) - to_chat(user,"You remove [src].") - qdel(src) - -/obj/machinery/conveyor/wrench_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - set_rotation(user) - update_move_direction() - -// attack with hand, move pulled object onto conveyor -/obj/machinery/conveyor/attack_hand(mob/user as mob) - user.Move_Pulled(src) - -/obj/machinery/conveyor/update_icon() - ..() - if(IS_OPERATING) - icon_state = "conveyor_started_[clockwise ? "cw" : "ccw"]" - if(reversed) - icon_state += "_r" - else - icon_state = "conveyor_stopped_[clockwise ? "cw" : "ccw"]" - -/obj/machinery/conveyor/proc/update_move_direction() - update_icon() - switch(dir) - if(NORTH) - forwards = NORTH - backwards = SOUTH - if(EAST) - forwards = EAST - backwards = WEST - if(SOUTH) - forwards = SOUTH - backwards = NORTH - if(WEST) - forwards = WEST - backwards = EAST - if(NORTHEAST) - forwards = clockwise ? EAST : NORTH - backwards = clockwise ? SOUTH : WEST - if(SOUTHEAST) - forwards = clockwise ? SOUTH : EAST - backwards = clockwise ? WEST : NORTH - if(SOUTHWEST) - forwards = clockwise ? WEST : SOUTH - backwards = clockwise ? NORTH : EAST - if(NORTHWEST) - forwards = clockwise ? NORTH : WEST - backwards = clockwise ? EAST : SOUTH - if(!reversed) - return - var/temporary_direction = forwards - forwards = backwards - backwards = temporary_direction - -/obj/machinery/conveyor/proc/set_rotation(mob/user) - dir = turn(reversed ? backwards : forwards, -90) //Fuck it, let's do it this way instead of doing something clever with dir - var/turf/left = get_step(src, turn(dir, 90)) //We need to get conveyors to the right, left, and behind this one to be able to determine if we need to make a corner piece - var/turf/right = get_step(src, turn(dir, -90)) - var/turf/back = get_step(src, turn(dir, 180)) - to_chat(user, "You rotate [src].") - var/obj/machinery/conveyor/CL = locate() in left - var/obj/machinery/conveyor/CR = locate() in right - var/obj/machinery/conveyor/CB = locate() in back - var/link_to_left = FALSE - var/link_to_right = FALSE - var/link_to_back = FALSE - if(CL) - if(CL.id == id && get_step(CL, CL.reversed ? CL.backwards : CL.forwards) == loc) - link_to_left = TRUE - if(CR) - if(CR.id == id && get_step(CR, CR.reversed ? CR.backwards : CR.forwards) == loc) - link_to_right = TRUE - if(CB) - if(CB.id == id && get_step(CB, CB.reversed ? CB.backwards : CB.forwards) == loc) - link_to_back = TRUE - if(link_to_back) //Don't need to do anything because we can assume the conveyor carries on in a line - return - else if(!(link_to_left ^ link_to_right)) //Either no valid conveyors point here, or two point here (making a "junction" with this belt as the middle piece). Either way we don't need a corner - return - if(link_to_right) - dir = turn(dir, 45) - clockwise = TRUE - else if(link_to_left) - dir = turn(dir, -45) - clockwise = FALSE - -/obj/machinery/conveyor/power_change() - ..() - update_icon() - -/obj/machinery/conveyor/process() - if(!IS_OPERATING) - return - use_power(100) - affecting = loc.contents - src // moved items will be all in loc - var/still_stuff_to_move = FALSE - for(var/atom/movable/AM in affecting) - if(AM.anchored) - continue - still_stuff_to_move = TRUE - addtimer(CALLBACK(src, .proc/move_thing, AM), 1) - CHECK_TICK - if(!still_stuff_to_move && speed_process) - makeNormalProcess() - else if(still_stuff_to_move && !speed_process) - makeSpeedProcess() - -/obj/machinery/conveyor/Crossed(atom/movable/AM, oldloc) - if(!speed_process && !AM.anchored) - makeSpeedProcess() - ..() - -/obj/machinery/conveyor/proc/move_thing(atom/movable/AM) - if(move_force < (AM.move_resist)) - return FALSE - if(!AM.anchored && AM.loc == loc) - step(AM, forwards) - - -/obj/machinery/conveyor/proc/can_conveyor_run() - if(stat & BROKEN) - return FALSE - else if(stat & NOPOWER) - return FALSE - else if(!operable) - return FALSE - return TRUE - -// make the conveyor broken and propagate inoperability to any connected conveyor with the same conveyor datum -/obj/machinery/conveyor/proc/make_broken() - stat |= BROKEN - operable = FALSE - update_icon() - var/obj/machinery/conveyor/C = locate() in get_step(src, forwards) - if(C) - C.set_operable(TRUE, id, FALSE) - C = locate() in get_step(src, backwards) - if(C) - C.set_operable(FALSE, id, FALSE) - -/obj/machinery/conveyor/proc/set_operable(propagate_forwards, match_id, op) //Sets a conveyor inoperable if ID matches it, and propagates forwards / backwards - if(id != match_id) - return - operable = op - update_icon() - var/obj/machinery/conveyor/C = locate() in get_step(src, propagate_forwards ? forwards : backwards) - if(C) - C.set_operable(propagate_forwards ? TRUE : FALSE, id, op) - -// the conveyor control switch - -/obj/machinery/conveyor_switch - name = "conveyor switch" - desc = "This switch controls any and all conveyor belts it is linked to." - icon = 'icons/obj/recycling.dmi' - icon_state = "switch-off" - var/position = DIRECTION_OFF - var/reversed = TRUE - var/one_way = FALSE // Do we go in one direction? - anchored = TRUE - var/id - var/list/conveyors = list() - -/obj/machinery/conveyor_switch/New(newloc, new_id) - ..(newloc) - GLOB.conveyor_switches += src - if(!id) - id = new_id - for(var/I in GLOB.conveyor_belts) - var/obj/machinery/conveyor/C = I - if(C.id == id) - conveyors += C - -/obj/machinery/conveyor_switch/Destroy() - GLOB.conveyor_switches -= src - return ..() - -// update the icon depending on the position - -/obj/machinery/conveyor_switch/update_icon() - overlays.Cut() - if(!position) - icon_state = "switch-off" - else if(position == DIRECTION_REVERSED) - icon_state = "switch-rev" - if(!(stat & NOPOWER)) - overlays += "redlight" - else if(position == DIRECTION_FORWARDS) - icon_state = "switch-fwd" - if(!(stat & NOPOWER)) - overlays += "greenlight" - -/obj/machinery/conveyor_switch/oneway - one_way = TRUE - -// attack with hand, switch position -/obj/machinery/conveyor_switch/attack_hand(mob/user) - if(..()) - return TRUE - toggle(user) - -/obj/machinery/conveyor_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - toggle(user) - -/obj/machinery/conveyor_switch/proc/toggle(mob/user) - add_fingerprint(user) - if(!allowed(user) && !user.can_advanced_admin_interact()) //this is in Para but not TG. I don't think there's any which are set anyway. - to_chat(user, "Access denied.") - return - if(position) - position = DIRECTION_OFF - else - reversed = one_way ? FALSE : !reversed - position = reversed ? DIRECTION_REVERSED : DIRECTION_FORWARDS - update_icon() - for(var/obj/machinery/conveyor/C in conveyors) - C.operating = abs(position) - if(C.reversed != reversed) - C.reversed = reversed - C.update_move_direction() - else - C.update_icon() - CHECK_TICK - for(var/I in GLOB.conveyor_switches) // find any switches with same id as this one, and set their positions to match us - var/obj/machinery/conveyor_switch/S = I - if(S == src || S.id != id) - continue - S.position = position - S.one_way = one_way //Break everything!!1! - S.reversed = reversed - S.update_icon() - CHECK_TICK - -/obj/machinery/conveyor_switch/crowbar_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - var/obj/item/conveyor_switch_construct/C = new(loc, id) - transfer_fingerprints_to(C) - to_chat(user,"You detach [src].") - qdel(src) - -/obj/machinery/conveyor_switch/multitool_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - one_way = !one_way - to_chat(user, "[src] will now go [one_way ? "forwards only" : "both forwards and backwards"].") - -/obj/machinery/conveyor_switch/power_change() - ..() - update_icon() - -// CONVEYOR CONSTRUCTION STARTS HERE - -/obj/item/conveyor_construct - icon = 'icons/obj/recycling.dmi' - icon_state = "conveyor_loose" - name = "conveyor belt assembly" - desc = "A conveyor belt assembly, used for the assembly of conveyor belt systems." - w_class = WEIGHT_CLASS_BULKY - var/id - -/obj/item/conveyor_construct/attackby(obj/item/I, mob/user, params) - ..() - if(!istype(I, /obj/item/conveyor_switch_construct)) - return - var/obj/item/conveyor_switch_construct/C = I - to_chat(user, "You link [src] to [C].") - id = C.id - -/obj/item/conveyor_construct/afterattack(turf/T, mob/user, proximity) - if(!proximity) - return - if(user.incapacitated()) - return - if(!istype(T, /turf/simulated/floor)) - return - if(T == get_turf(user)) - to_chat(user, "You cannot place [src] under yourself.") - return - if(locate(/obj/machinery/conveyor) in T) //Can't put conveyors beneath conveyors - to_chat(user, "There's already a conveyor there!") - return - var/obj/machinery/conveyor/C = new(T, user.dir, id) - transfer_fingerprints_to(C) - qdel(src) - -/obj/item/conveyor_switch_construct - name = "conveyor switch assembly" - desc = "A conveyor control switch assembly. When set up, it'll control any and all conveyor belts it is linked to." - icon = 'icons/obj/recycling.dmi' - icon_state = "switch" - w_class = WEIGHT_CLASS_BULKY - var/id - -/obj/item/conveyor_switch_construct/New(loc, new_id) - ..(loc) - if(new_id) - id = new_id - else - id = world.time + rand() //this couldn't possibly go wrong - - -/obj/item/conveyor_switch_construct/afterattack(turf/T, mob/user, proximity) - if(!proximity) - return - if(user.incapacitated()) - return - if(!istype(T, /turf/simulated/floor)) - return - var/found = FALSE - for(var/obj/machinery/conveyor/C in view()) - if(C.id == id) - found = TRUE - break - if(!found) - to_chat(user, "[src] did not detect any linked conveyor belts in range.") - return - var/obj/machinery/conveyor_switch/NC = new(T, id) - transfer_fingerprints_to(NC) - qdel(src) - -/obj/item/conveyor_switch_construct/attackby(obj/item/I, mob/user) - if(!istype(I, /obj/item/conveyor_switch_construct)) - return ..() - var/obj/item/conveyor_switch_construct/S = I - id = S.id - to_chat(user, "You link the two switch constructs.") - -/obj/item/paper/conveyor - name = "paper- 'Nano-it-up U-build series, #9: Build your very own conveyor belt, in SPACE'" - info = "

    Congratulations!

    You are now the proud owner of the best conveyor set available for space mail order! \ - We at Nano-it-up know you love to prepare your own structures without wasting time, so we have devised a special streamlined \ - assembly procedure that puts all other mail-order products to shame!

    \ -

    Firstly, you need to link the conveyor switch assembly to each of the conveyor belt assemblies. After doing so, you simply need to install the belt \ - assemblies onto the floor, et voila, belt built. Our special Nano-it-up smart switch will detected any linked assemblies as far as the eye can see!

    \ -

    Set single directional switches by using your multitool on the switch after you've installed the switch assembly.

    \ -

    This convenience, you can only have it when you Nano-it-up. Stay nano!

    " - -/obj/machinery/conveyor/counterclockwise - clockwise = FALSE - icon_state = "conveyor_stopped_ccw" - -/obj/machinery/conveyor/auto/New(loc, newdir) - ..(loc, newdir) - operating = TRUE - update_icon() - -//Other types of conveyor, mostly for saving yourself a headache during mapping - -/obj/machinery/conveyor/north - dir = NORTH - -/obj/machinery/conveyor/northeast - dir = NORTHEAST - -/obj/machinery/conveyor/east - dir = EAST - -/obj/machinery/conveyor/southeast - dir = SOUTHEAST - -/obj/machinery/conveyor/south - dir = SOUTH - -/obj/machinery/conveyor/southwest - dir = SOUTHWEST - -/obj/machinery/conveyor/west - dir = WEST - -/obj/machinery/conveyor/northwest - dir = NORTHWEST - -/obj/machinery/conveyor/north/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/northeast/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/east/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/southeast/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/south/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/southwest/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/west/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/northwest/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -#undef DIRECTION_FORWARDS -#undef DIRECTION_OFF -#undef DIRECTION_REVERSED -#undef IS_OPERATING +#define DIRECTION_FORWARDS 1 +#define DIRECTION_OFF 0 +#define DIRECTION_REVERSED -1 +#define IS_OPERATING (operating && can_conveyor_run()) + +GLOBAL_LIST_INIT(conveyor_belts, list()) //Saves us having to look through the entire machines list for our things +GLOBAL_LIST_INIT(conveyor_switches, list()) + +//conveyor2 is pretty much like the original, except it supports corners, but not diverters. +//Except this is pretty heavily modified so it's more like conveyor2.5 + +/obj/machinery/conveyor + icon = 'icons/obj/recycling.dmi' + icon_state = "conveyor_stopped_cw" + name = "conveyor belt" + desc = "It's a conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly." + layer = CONVEYOR_LAYER // so they appear under stuff but not below stuff like vents + anchored = TRUE + move_force = MOVE_FORCE_DEFAULT + var/operating = FALSE //NB: this can be TRUE while the belt doesn't go + var/forwards // The direction the conveyor sends you in + var/backwards // hopefully self-explanatory + var/clockwise = TRUE // For corner pieces - do we go clockwise or counterclockwise? + var/operable = TRUE // Can this belt actually go? + var/list/affecting // the list of all items that will be moved this ptick + var/reversed = FALSE // set to TRUE to have the conveyor belt be reversed + var/id //ID of the connected lever + + // create a conveyor +/obj/machinery/conveyor/New(loc, new_dir, new_id) + ..(loc) + GLOB.conveyor_belts += src + if(new_id) + id = new_id + if(new_dir) + dir = new_dir + update_move_direction() + for(var/I in GLOB.conveyor_switches) + var/obj/machinery/conveyor_switch/S = I + if(id == S.id) + S.conveyors += src + +/obj/machinery/conveyor/Destroy() + GLOB.conveyor_belts -= src + return ..() + +/obj/machinery/conveyor/setDir(newdir) + . = ..() + update_move_direction() + +// attack with item, place item on conveyor +/obj/machinery/conveyor/attackby(obj/item/I, mob/user) + if(stat & BROKEN) + return ..() + else if(istype(I, /obj/item/conveyor_switch_construct)) + var/obj/item/conveyor_switch_construct/S = I + if(S.id == id) + return ..() + for(var/obj/machinery/conveyor_switch/CS in GLOB.conveyor_switches) + if(CS.id == id) + CS.conveyors -= src + id = S.id + to_chat(user, "You link [I] with [src].") + else if(user.a_intent != INTENT_HARM) + if(user.drop_item()) + I.forceMove(loc) + else + return ..() + + +/obj/machinery/conveyor/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!(stat & BROKEN)) + var/obj/item/conveyor_construct/C = new(loc) + C.id = id + transfer_fingerprints_to(C) + to_chat(user,"You remove [src].") + qdel(src) + +/obj/machinery/conveyor/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + set_rotation(user) + update_move_direction() + +// attack with hand, move pulled object onto conveyor +/obj/machinery/conveyor/attack_hand(mob/user as mob) + user.Move_Pulled(src) + +/obj/machinery/conveyor/update_icon() + ..() + if(IS_OPERATING) + icon_state = "conveyor_started_[clockwise ? "cw" : "ccw"]" + if(reversed) + icon_state += "_r" + else + icon_state = "conveyor_stopped_[clockwise ? "cw" : "ccw"]" + +/obj/machinery/conveyor/proc/update_move_direction() + update_icon() + switch(dir) + if(NORTH) + forwards = NORTH + backwards = SOUTH + if(EAST) + forwards = EAST + backwards = WEST + if(SOUTH) + forwards = SOUTH + backwards = NORTH + if(WEST) + forwards = WEST + backwards = EAST + if(NORTHEAST) + forwards = clockwise ? EAST : NORTH + backwards = clockwise ? SOUTH : WEST + if(SOUTHEAST) + forwards = clockwise ? SOUTH : EAST + backwards = clockwise ? WEST : NORTH + if(SOUTHWEST) + forwards = clockwise ? WEST : SOUTH + backwards = clockwise ? NORTH : EAST + if(NORTHWEST) + forwards = clockwise ? NORTH : WEST + backwards = clockwise ? EAST : SOUTH + if(!reversed) + return + var/temporary_direction = forwards + forwards = backwards + backwards = temporary_direction + +/obj/machinery/conveyor/proc/set_rotation(mob/user) + dir = turn(reversed ? backwards : forwards, -90) //Fuck it, let's do it this way instead of doing something clever with dir + var/turf/left = get_step(src, turn(dir, 90)) //We need to get conveyors to the right, left, and behind this one to be able to determine if we need to make a corner piece + var/turf/right = get_step(src, turn(dir, -90)) + var/turf/back = get_step(src, turn(dir, 180)) + to_chat(user, "You rotate [src].") + var/obj/machinery/conveyor/CL = locate() in left + var/obj/machinery/conveyor/CR = locate() in right + var/obj/machinery/conveyor/CB = locate() in back + var/link_to_left = FALSE + var/link_to_right = FALSE + var/link_to_back = FALSE + if(CL) + if(CL.id == id && get_step(CL, CL.reversed ? CL.backwards : CL.forwards) == loc) + link_to_left = TRUE + if(CR) + if(CR.id == id && get_step(CR, CR.reversed ? CR.backwards : CR.forwards) == loc) + link_to_right = TRUE + if(CB) + if(CB.id == id && get_step(CB, CB.reversed ? CB.backwards : CB.forwards) == loc) + link_to_back = TRUE + if(link_to_back) //Don't need to do anything because we can assume the conveyor carries on in a line + return + else if(!(link_to_left ^ link_to_right)) //Either no valid conveyors point here, or two point here (making a "junction" with this belt as the middle piece). Either way we don't need a corner + return + if(link_to_right) + dir = turn(dir, 45) + clockwise = TRUE + else if(link_to_left) + dir = turn(dir, -45) + clockwise = FALSE + +/obj/machinery/conveyor/power_change() + ..() + update_icon() + +/obj/machinery/conveyor/process() + if(!IS_OPERATING) + return + use_power(100) + affecting = loc.contents - src // moved items will be all in loc + var/still_stuff_to_move = FALSE + for(var/atom/movable/AM in affecting) + if(AM.anchored) + continue + still_stuff_to_move = TRUE + addtimer(CALLBACK(src, .proc/move_thing, AM), 1) + CHECK_TICK + if(!still_stuff_to_move && speed_process) + makeNormalProcess() + else if(still_stuff_to_move && !speed_process) + makeSpeedProcess() + +/obj/machinery/conveyor/Crossed(atom/movable/AM, oldloc) + if(!speed_process && !AM.anchored) + makeSpeedProcess() + ..() + +/obj/machinery/conveyor/proc/move_thing(atom/movable/AM) + if(move_force < (AM.move_resist)) + return FALSE + if(!AM.anchored && AM.loc == loc) + step(AM, forwards) + + +/obj/machinery/conveyor/proc/can_conveyor_run() + if(stat & BROKEN) + return FALSE + else if(stat & NOPOWER) + return FALSE + else if(!operable) + return FALSE + return TRUE + +// make the conveyor broken and propagate inoperability to any connected conveyor with the same conveyor datum +/obj/machinery/conveyor/proc/make_broken() + stat |= BROKEN + operable = FALSE + update_icon() + var/obj/machinery/conveyor/C = locate() in get_step(src, forwards) + if(C) + C.set_operable(TRUE, id, FALSE) + C = locate() in get_step(src, backwards) + if(C) + C.set_operable(FALSE, id, FALSE) + +/obj/machinery/conveyor/proc/set_operable(propagate_forwards, match_id, op) //Sets a conveyor inoperable if ID matches it, and propagates forwards / backwards + if(id != match_id) + return + operable = op + update_icon() + var/obj/machinery/conveyor/C = locate() in get_step(src, propagate_forwards ? forwards : backwards) + if(C) + C.set_operable(propagate_forwards ? TRUE : FALSE, id, op) + +// the conveyor control switch + +/obj/machinery/conveyor_switch + name = "conveyor switch" + desc = "This switch controls any and all conveyor belts it is linked to." + icon = 'icons/obj/recycling.dmi' + icon_state = "switch-off" + var/position = DIRECTION_OFF + var/reversed = TRUE + var/one_way = FALSE // Do we go in one direction? + anchored = TRUE + var/id + var/list/conveyors = list() + +/obj/machinery/conveyor_switch/New(newloc, new_id) + ..(newloc) + GLOB.conveyor_switches += src + if(!id) + id = new_id + for(var/I in GLOB.conveyor_belts) + var/obj/machinery/conveyor/C = I + if(C.id == id) + conveyors += C + +/obj/machinery/conveyor_switch/Destroy() + GLOB.conveyor_switches -= src + return ..() + +// update the icon depending on the position + +/obj/machinery/conveyor_switch/update_icon() + overlays.Cut() + if(!position) + icon_state = "switch-off" + else if(position == DIRECTION_REVERSED) + icon_state = "switch-rev" + if(!(stat & NOPOWER)) + overlays += "redlight" + else if(position == DIRECTION_FORWARDS) + icon_state = "switch-fwd" + if(!(stat & NOPOWER)) + overlays += "greenlight" + +/obj/machinery/conveyor_switch/oneway + one_way = TRUE + +// attack with hand, switch position +/obj/machinery/conveyor_switch/attack_hand(mob/user) + if(..()) + return TRUE + toggle(user) + +/obj/machinery/conveyor_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + toggle(user) + +/obj/machinery/conveyor_switch/proc/toggle(mob/user) + add_fingerprint(user) + if(!allowed(user) && !user.can_advanced_admin_interact()) //this is in Para but not TG. I don't think there's any which are set anyway. + to_chat(user, "Access denied.") + return + if(position) + position = DIRECTION_OFF + else + reversed = one_way ? FALSE : !reversed + position = reversed ? DIRECTION_REVERSED : DIRECTION_FORWARDS + update_icon() + for(var/obj/machinery/conveyor/C in conveyors) + C.operating = abs(position) + if(C.reversed != reversed) + C.reversed = reversed + C.update_move_direction() + else + C.update_icon() + CHECK_TICK + for(var/I in GLOB.conveyor_switches) // find any switches with same id as this one, and set their positions to match us + var/obj/machinery/conveyor_switch/S = I + if(S == src || S.id != id) + continue + S.position = position + S.one_way = one_way //Break everything!!1! + S.reversed = reversed + S.update_icon() + CHECK_TICK + +/obj/machinery/conveyor_switch/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/obj/item/conveyor_switch_construct/C = new(loc, id) + transfer_fingerprints_to(C) + to_chat(user,"You detach [src].") + qdel(src) + +/obj/machinery/conveyor_switch/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + one_way = !one_way + to_chat(user, "[src] will now go [one_way ? "forwards only" : "both forwards and backwards"].") + +/obj/machinery/conveyor_switch/power_change() + ..() + update_icon() + +// CONVEYOR CONSTRUCTION STARTS HERE + +/obj/item/conveyor_construct + icon = 'icons/obj/recycling.dmi' + icon_state = "conveyor_loose" + name = "conveyor belt assembly" + desc = "A conveyor belt assembly, used for the assembly of conveyor belt systems." + w_class = WEIGHT_CLASS_BULKY + var/id + +/obj/item/conveyor_construct/attackby(obj/item/I, mob/user, params) + ..() + if(!istype(I, /obj/item/conveyor_switch_construct)) + return + var/obj/item/conveyor_switch_construct/C = I + to_chat(user, "You link [src] to [C].") + id = C.id + +/obj/item/conveyor_construct/afterattack(turf/T, mob/user, proximity) + if(!proximity) + return + if(user.incapacitated()) + return + if(!istype(T, /turf/simulated/floor)) + return + if(T == get_turf(user)) + to_chat(user, "You cannot place [src] under yourself.") + return + if(locate(/obj/machinery/conveyor) in T) //Can't put conveyors beneath conveyors + to_chat(user, "There's already a conveyor there!") + return + var/obj/machinery/conveyor/C = new(T, user.dir, id) + transfer_fingerprints_to(C) + qdel(src) + +/obj/item/conveyor_switch_construct + name = "conveyor switch assembly" + desc = "A conveyor control switch assembly. When set up, it'll control any and all conveyor belts it is linked to." + icon = 'icons/obj/recycling.dmi' + icon_state = "switch" + w_class = WEIGHT_CLASS_BULKY + var/id + +/obj/item/conveyor_switch_construct/New(loc, new_id) + ..(loc) + if(new_id) + id = new_id + else + id = world.time + rand() //this couldn't possibly go wrong + + +/obj/item/conveyor_switch_construct/afterattack(turf/T, mob/user, proximity) + if(!proximity) + return + if(user.incapacitated()) + return + if(!istype(T, /turf/simulated/floor)) + return + var/found = FALSE + for(var/obj/machinery/conveyor/C in view()) + if(C.id == id) + found = TRUE + break + if(!found) + to_chat(user, "[src] did not detect any linked conveyor belts in range.") + return + var/obj/machinery/conveyor_switch/NC = new(T, id) + transfer_fingerprints_to(NC) + qdel(src) + +/obj/item/conveyor_switch_construct/attackby(obj/item/I, mob/user) + if(!istype(I, /obj/item/conveyor_switch_construct)) + return ..() + var/obj/item/conveyor_switch_construct/S = I + id = S.id + to_chat(user, "You link the two switch constructs.") + +/obj/item/paper/conveyor + name = "paper- 'Nano-it-up U-build series, #9: Build your very own conveyor belt, in SPACE'" + info = "

    Congratulations!

    You are now the proud owner of the best conveyor set available for space mail order! \ + We at Nano-it-up know you love to prepare your own structures without wasting time, so we have devised a special streamlined \ + assembly procedure that puts all other mail-order products to shame!

    \ +

    Firstly, you need to link the conveyor switch assembly to each of the conveyor belt assemblies. After doing so, you simply need to install the belt \ + assemblies onto the floor, et voila, belt built. Our special Nano-it-up smart switch will detected any linked assemblies as far as the eye can see!

    \ +

    Set single directional switches by using your multitool on the switch after you've installed the switch assembly.

    \ +

    This convenience, you can only have it when you Nano-it-up. Stay nano!

    " + +/obj/machinery/conveyor/counterclockwise + clockwise = FALSE + icon_state = "conveyor_stopped_ccw" + +/obj/machinery/conveyor/auto/New(loc, newdir) + ..(loc, newdir) + operating = TRUE + update_icon() + +//Other types of conveyor, mostly for saving yourself a headache during mapping + +/obj/machinery/conveyor/north + dir = NORTH + +/obj/machinery/conveyor/northeast + dir = NORTHEAST + +/obj/machinery/conveyor/east + dir = EAST + +/obj/machinery/conveyor/southeast + dir = SOUTHEAST + +/obj/machinery/conveyor/south + dir = SOUTH + +/obj/machinery/conveyor/southwest + dir = SOUTHWEST + +/obj/machinery/conveyor/west + dir = WEST + +/obj/machinery/conveyor/northwest + dir = NORTHWEST + +/obj/machinery/conveyor/north/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/northeast/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/east/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/southeast/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/south/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/southwest/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/west/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/northwest/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +#undef DIRECTION_FORWARDS +#undef DIRECTION_OFF +#undef DIRECTION_REVERSED +#undef IS_OPERATING diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/recycling/disposal-construction.dm index 66caa897f21e..df29663588f7 100644 --- a/code/modules/recycling/disposal-construction.dm +++ b/code/modules/recycling/disposal-construction.dm @@ -1,265 +1,265 @@ -// Disposal pipe construction -// This is the pipe that you drag around, not the attached ones. - -/obj/structure/disposalconstruct - - name = "disposal pipe segment" - desc = "A huge pipe segment used for constructing disposal systems." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "conpipe-s" - anchored = 0 - density = 0 - pressure_resistance = 5*ONE_ATMOSPHERE - level = 2 - max_integrity = 200 - var/ptype = PIPE_DISPOSALS_STRAIGHT //Use the defines - var/base_state - var/dpdir = 0 // directions as disposalpipe - -/obj/structure/disposalconstruct/New(loc, pipe_type, direction) - ..() - if(pipe_type) - ptype = pipe_type - if(dir) - dir = direction - update() - - // update iconstate and dpdir due to dir and type -/obj/structure/disposalconstruct/proc/update() - base_state = get_pipe_icon(ptype) - icon_state = "con[base_state]" - var/flip = turn(dir, 180) - var/left = turn(dir, 90) - var/right = turn(dir, -90) - name = get_pipe_name(ptype, PIPETYPE_DISPOSAL) - switch(ptype) - if(PIPE_DISPOSALS_STRAIGHT) - dpdir = dir | flip - if(PIPE_DISPOSALS_BENT) - dpdir = dir | right - if(PIPE_DISPOSALS_JUNCTION_RIGHT) - dpdir = dir | right | flip - if(PIPE_DISPOSALS_JUNCTION_LEFT) - dpdir = dir | left | flip - if(PIPE_DISPOSALS_Y_JUNCTION) - dpdir = dir | left | right - if(PIPE_DISPOSALS_TRUNK) - dpdir = dir - if(PIPE_DISPOSALS_SORT_RIGHT) - dpdir = dir | right | flip - if(PIPE_DISPOSALS_SORT_LEFT) - dpdir = dir | left | flip - // disposal bin has only one dir, thus we don't need to care about setting it - if(PIPE_DISPOSALS_BIN) - if(!anchored) - icon_state = "[base_state]-unanchored" - else - icon_state = base_state - if(PIPE_DISPOSALS_OUTLET) - dpdir = dir - icon_state = base_state - if(PIPE_DISPOSALS_CHUTE) - dpdir = dir - icon_state = base_state - if(!(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE))) - icon_state = "con[base_state]" - if(invisibility) // if invisible, fade icon - icon -= rgb(0,0,0,128) - -// hide called by levelupdate if turf intact status changes -// change visibility status and force update of icon -/obj/structure/disposalconstruct/hide(var/intact) - invisibility = (intact && level==1) ? 101: 0 // hide if floor is intact - update() - - -// flip and rotate verbs -/obj/structure/disposalconstruct/verb/rotate() - set name = "Rotate Pipe" - set src in view(1) - - if(usr.stat) - return - - if(anchored) - to_chat(usr, "You must unfasten the pipe before rotating it.") - return - - dir = turn(dir, -90) - update() - -/obj/structure/disposalconstruct/AltClick(mob/user) - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!Adjacent(user)) - return - rotate() - -/obj/structure/disposalconstruct/verb/flip() - set name = "Flip Pipe" - set src in view(1) - if(usr.stat) - return - - if(anchored) - to_chat(usr, "You must unfasten the pipe before flipping it.") - return - - dir = turn(dir, 180) - switch(ptype) - if(PIPE_DISPOSALS_JUNCTION_RIGHT) - ptype = PIPE_DISPOSALS_JUNCTION_LEFT - if(PIPE_DISPOSALS_JUNCTION_LEFT) - ptype = PIPE_DISPOSALS_JUNCTION_RIGHT - if(PIPE_DISPOSALS_SORT_RIGHT) - ptype = PIPE_DISPOSALS_SORT_LEFT - if(PIPE_DISPOSALS_SORT_LEFT) - ptype = PIPE_DISPOSALS_SORT_RIGHT - - update() - -// returns the type path of disposalpipe corresponding to this item dtype -/obj/structure/disposalconstruct/proc/dpipetype() - switch(ptype) - if(PIPE_DISPOSALS_STRAIGHT, PIPE_DISPOSALS_BENT) - return /obj/structure/disposalpipe/segment - if(PIPE_DISPOSALS_JUNCTION_RIGHT, PIPE_DISPOSALS_JUNCTION_LEFT, PIPE_DISPOSALS_Y_JUNCTION) - return /obj/structure/disposalpipe/junction - if(PIPE_DISPOSALS_TRUNK) - return /obj/structure/disposalpipe/trunk - if(PIPE_DISPOSALS_BIN) - return /obj/machinery/disposal - if(PIPE_DISPOSALS_OUTLET) - return /obj/structure/disposaloutlet - if(PIPE_DISPOSALS_CHUTE) - return /obj/machinery/disposal/deliveryChute - if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) - return /obj/structure/disposalpipe/sortjunction - return - - - -// attackby item -// wrench: (un)anchor -// weldingtool: convert to real pipe - -/obj/structure/disposalconstruct/attackby(var/obj/item/I, var/mob/user, params) - var/nicetype = "pipe" - var/ispipe = 0 // Indicates if we should change the level of this pipe - src.add_fingerprint(user) - switch(ptype) - if(PIPE_DISPOSALS_BIN) - nicetype = "disposal bin" - if(PIPE_DISPOSALS_OUTLET) - nicetype = "disposal outlet" - if(PIPE_DISPOSALS_CHUTE) - nicetype = "delivery chute" - if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) - nicetype = "sorting pipe" - ispipe = 1 - else - nicetype = "pipe" - ispipe = 1 - - var/turf/T = src.loc - if(T.intact) - to_chat(user, "You can only attach the [nicetype] if the floor plating is removed.") - return - - if(istype(I, /obj/item/wrench)) - if(anchored) - anchored = 0 - if(ispipe) - level = 2 - density = 0 - else - density = 1 - to_chat(user, "You detach the [nicetype] from the underfloor.") - else - anchored = 1 - if(ispipe) - level = 1 // We don't want disposal bins to disappear under the floors - density = 0 - else - density = 1 // We don't want disposal bins or outlets to go density 0 - to_chat(user, "You attach the [nicetype] to the underfloor.") - playsound(src.loc, I.usesound, 100, 1) - update() - return - - - if(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE)) // Disposal or outlet - var/obj/structure/disposalpipe/trunk/CP = locate() in T - if(!CP) // There's no trunk - to_chat(user, "The [nicetype] requires a trunk underneath it in order to work.") - return - else - for(var/obj/structure/disposalpipe/CP in T) - if(CP) - update() - var/pdir = CP.dpdir - if(istype(CP, /obj/structure/disposalpipe/broken)) - pdir = CP.dir - if(pdir & dpdir) - to_chat(user, "There is already a [nicetype] at that location.") - return - - if(istype(I, /obj/item/weldingtool)) - if(anchored) - if(I.tool_use_check(user, 0)) - to_chat(user, "Welding the [nicetype] in place.") - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - to_chat(user, "The [nicetype] has been welded in place!") - update() // TODO: Make this neat - if(ispipe) // Pipe - - var/pipetype = dpipetype() - var/obj/structure/disposalpipe/P = new pipetype(src.loc) - src.transfer_fingerprints_to(P) - P.base_icon_state = base_state - P.dir = dir - P.dpdir = dpdir - P.update_icon() - - //Needs some special treatment ;) - if(ptype == PIPE_DISPOSALS_SORT_RIGHT || ptype == PIPE_DISPOSALS_SORT_LEFT) - var/obj/structure/disposalpipe/sortjunction/SortP = P - SortP.updatedir() - - else if(ptype == PIPE_DISPOSALS_BIN) // Disposal bin - var/obj/machinery/disposal/P = new /obj/machinery/disposal(src.loc) - src.transfer_fingerprints_to(P) - P.mode = 0 // start with pump off - - else if(ptype == PIPE_DISPOSALS_OUTLET) // Disposal outlet - - var/obj/structure/disposaloutlet/P = new /obj/structure/disposaloutlet(src.loc) - src.transfer_fingerprints_to(P) - P.dir = dir - - else if(ptype==PIPE_DISPOSALS_CHUTE) // Disposal outlet - - var/obj/machinery/disposal/deliveryChute/P = new /obj/machinery/disposal/deliveryChute(src.loc) - src.transfer_fingerprints_to(P) - P.dir = dir - - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - else - to_chat(user, "You need to attach it to the plating first!") - return - -/obj/structure/disposalconstruct/rpd_act(mob/user, obj/item/rpd/our_rpd) - . = TRUE - if(our_rpd.mode == RPD_ROTATE_MODE) - rotate() - else if(our_rpd.mode == RPD_FLIP_MODE) - flip() - else if(our_rpd.mode == RPD_DELETE_MODE) - our_rpd.delete_single_pipe(user, src) - else - return ..() +// Disposal pipe construction +// This is the pipe that you drag around, not the attached ones. + +/obj/structure/disposalconstruct + + name = "disposal pipe segment" + desc = "A huge pipe segment used for constructing disposal systems." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "conpipe-s" + anchored = 0 + density = 0 + pressure_resistance = 5*ONE_ATMOSPHERE + level = 2 + max_integrity = 200 + var/ptype = PIPE_DISPOSALS_STRAIGHT //Use the defines + var/base_state + var/dpdir = 0 // directions as disposalpipe + +/obj/structure/disposalconstruct/New(loc, pipe_type, direction) + ..() + if(pipe_type) + ptype = pipe_type + if(dir) + dir = direction + update() + + // update iconstate and dpdir due to dir and type +/obj/structure/disposalconstruct/proc/update() + base_state = get_pipe_icon(ptype) + icon_state = "con[base_state]" + var/flip = turn(dir, 180) + var/left = turn(dir, 90) + var/right = turn(dir, -90) + name = get_pipe_name(ptype, PIPETYPE_DISPOSAL) + switch(ptype) + if(PIPE_DISPOSALS_STRAIGHT) + dpdir = dir | flip + if(PIPE_DISPOSALS_BENT) + dpdir = dir | right + if(PIPE_DISPOSALS_JUNCTION_RIGHT) + dpdir = dir | right | flip + if(PIPE_DISPOSALS_JUNCTION_LEFT) + dpdir = dir | left | flip + if(PIPE_DISPOSALS_Y_JUNCTION) + dpdir = dir | left | right + if(PIPE_DISPOSALS_TRUNK) + dpdir = dir + if(PIPE_DISPOSALS_SORT_RIGHT) + dpdir = dir | right | flip + if(PIPE_DISPOSALS_SORT_LEFT) + dpdir = dir | left | flip + // disposal bin has only one dir, thus we don't need to care about setting it + if(PIPE_DISPOSALS_BIN) + if(!anchored) + icon_state = "[base_state]-unanchored" + else + icon_state = base_state + if(PIPE_DISPOSALS_OUTLET) + dpdir = dir + icon_state = base_state + if(PIPE_DISPOSALS_CHUTE) + dpdir = dir + icon_state = base_state + if(!(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE))) + icon_state = "con[base_state]" + if(invisibility) // if invisible, fade icon + icon -= rgb(0,0,0,128) + +// hide called by levelupdate if turf intact status changes +// change visibility status and force update of icon +/obj/structure/disposalconstruct/hide(var/intact) + invisibility = (intact && level==1) ? 101: 0 // hide if floor is intact + update() + + +// flip and rotate verbs +/obj/structure/disposalconstruct/verb/rotate() + set name = "Rotate Pipe" + set src in view(1) + + if(usr.stat) + return + + if(anchored) + to_chat(usr, "You must unfasten the pipe before rotating it.") + return + + dir = turn(dir, -90) + update() + +/obj/structure/disposalconstruct/AltClick(mob/user) + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!Adjacent(user)) + return + rotate() + +/obj/structure/disposalconstruct/verb/flip() + set name = "Flip Pipe" + set src in view(1) + if(usr.stat) + return + + if(anchored) + to_chat(usr, "You must unfasten the pipe before flipping it.") + return + + dir = turn(dir, 180) + switch(ptype) + if(PIPE_DISPOSALS_JUNCTION_RIGHT) + ptype = PIPE_DISPOSALS_JUNCTION_LEFT + if(PIPE_DISPOSALS_JUNCTION_LEFT) + ptype = PIPE_DISPOSALS_JUNCTION_RIGHT + if(PIPE_DISPOSALS_SORT_RIGHT) + ptype = PIPE_DISPOSALS_SORT_LEFT + if(PIPE_DISPOSALS_SORT_LEFT) + ptype = PIPE_DISPOSALS_SORT_RIGHT + + update() + +// returns the type path of disposalpipe corresponding to this item dtype +/obj/structure/disposalconstruct/proc/dpipetype() + switch(ptype) + if(PIPE_DISPOSALS_STRAIGHT, PIPE_DISPOSALS_BENT) + return /obj/structure/disposalpipe/segment + if(PIPE_DISPOSALS_JUNCTION_RIGHT, PIPE_DISPOSALS_JUNCTION_LEFT, PIPE_DISPOSALS_Y_JUNCTION) + return /obj/structure/disposalpipe/junction + if(PIPE_DISPOSALS_TRUNK) + return /obj/structure/disposalpipe/trunk + if(PIPE_DISPOSALS_BIN) + return /obj/machinery/disposal + if(PIPE_DISPOSALS_OUTLET) + return /obj/structure/disposaloutlet + if(PIPE_DISPOSALS_CHUTE) + return /obj/machinery/disposal/deliveryChute + if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) + return /obj/structure/disposalpipe/sortjunction + return + + + +// attackby item +// wrench: (un)anchor +// weldingtool: convert to real pipe + +/obj/structure/disposalconstruct/attackby(var/obj/item/I, var/mob/user, params) + var/nicetype = "pipe" + var/ispipe = 0 // Indicates if we should change the level of this pipe + src.add_fingerprint(user) + switch(ptype) + if(PIPE_DISPOSALS_BIN) + nicetype = "disposal bin" + if(PIPE_DISPOSALS_OUTLET) + nicetype = "disposal outlet" + if(PIPE_DISPOSALS_CHUTE) + nicetype = "delivery chute" + if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) + nicetype = "sorting pipe" + ispipe = 1 + else + nicetype = "pipe" + ispipe = 1 + + var/turf/T = src.loc + if(T.intact) + to_chat(user, "You can only attach the [nicetype] if the floor plating is removed.") + return + + if(istype(I, /obj/item/wrench)) + if(anchored) + anchored = 0 + if(ispipe) + level = 2 + density = 0 + else + density = 1 + to_chat(user, "You detach the [nicetype] from the underfloor.") + else + anchored = 1 + if(ispipe) + level = 1 // We don't want disposal bins to disappear under the floors + density = 0 + else + density = 1 // We don't want disposal bins or outlets to go density 0 + to_chat(user, "You attach the [nicetype] to the underfloor.") + playsound(src.loc, I.usesound, 100, 1) + update() + return + + + if(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE)) // Disposal or outlet + var/obj/structure/disposalpipe/trunk/CP = locate() in T + if(!CP) // There's no trunk + to_chat(user, "The [nicetype] requires a trunk underneath it in order to work.") + return + else + for(var/obj/structure/disposalpipe/CP in T) + if(CP) + update() + var/pdir = CP.dpdir + if(istype(CP, /obj/structure/disposalpipe/broken)) + pdir = CP.dir + if(pdir & dpdir) + to_chat(user, "There is already a [nicetype] at that location.") + return + + if(istype(I, /obj/item/weldingtool)) + if(anchored) + if(I.tool_use_check(user, 0)) + to_chat(user, "Welding the [nicetype] in place.") + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + to_chat(user, "The [nicetype] has been welded in place!") + update() // TODO: Make this neat + if(ispipe) // Pipe + + var/pipetype = dpipetype() + var/obj/structure/disposalpipe/P = new pipetype(src.loc) + src.transfer_fingerprints_to(P) + P.base_icon_state = base_state + P.dir = dir + P.dpdir = dpdir + P.update_icon() + + //Needs some special treatment ;) + if(ptype == PIPE_DISPOSALS_SORT_RIGHT || ptype == PIPE_DISPOSALS_SORT_LEFT) + var/obj/structure/disposalpipe/sortjunction/SortP = P + SortP.updatedir() + + else if(ptype == PIPE_DISPOSALS_BIN) // Disposal bin + var/obj/machinery/disposal/P = new /obj/machinery/disposal(src.loc) + src.transfer_fingerprints_to(P) + P.mode = 0 // start with pump off + + else if(ptype == PIPE_DISPOSALS_OUTLET) // Disposal outlet + + var/obj/structure/disposaloutlet/P = new /obj/structure/disposaloutlet(src.loc) + src.transfer_fingerprints_to(P) + P.dir = dir + + else if(ptype==PIPE_DISPOSALS_CHUTE) // Disposal outlet + + var/obj/machinery/disposal/deliveryChute/P = new /obj/machinery/disposal/deliveryChute(src.loc) + src.transfer_fingerprints_to(P) + P.dir = dir + + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return + else + to_chat(user, "You need to attach it to the plating first!") + return + +/obj/structure/disposalconstruct/rpd_act(mob/user, obj/item/rpd/our_rpd) + . = TRUE + if(our_rpd.mode == RPD_ROTATE_MODE) + rotate() + else if(our_rpd.mode == RPD_FLIP_MODE) + flip() + else if(our_rpd.mode == RPD_DELETE_MODE) + our_rpd.delete_single_pipe(user, src) + else + return ..() diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 59f6d395aff8..e16ce4a08242 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -1,1384 +1,1384 @@ -// Disposal bin -// Holds items for disposal into pipe system -// Draws air from turf, gradually charges internal reservoir -// Once full (~1 atm), uses air resv to flush items into the pipes -// Automatically recharges air (unless off), will flush when ready if pre-set -// Can hold items and human size things, no other draggables -// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation -#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE - -/obj/machinery/disposal - name = "disposal unit" - desc = "A pneumatic waste disposal unit." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "disposal" - anchored = 1 - density = 1 - on_blueprints = TRUE - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) - max_integrity = 200 - resistance_flags = FIRE_PROOF - var/datum/gas_mixture/air_contents // internal reservoir - var/mode = 1 // item mode 0=off 1=charging 2=charged - var/flush = 0 // true if flush handle is pulled - var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk - var/flushing = 0 // true if flushing in progress - var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush - var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. - var/last_sound = 0 - var/required_mode_to_deconstruct = -1 - var/deconstructs_to = PIPE_DISPOSALS_BIN - active_power_usage = 600 - idle_power_usage = 100 - - -// create a new disposal -// find the attached trunk (if present) -/obj/machinery/disposal/New() - ..() - trunk_check() - //gas.volume = 1.05 * CELLSTANDARD - update() - -/obj/machinery/disposal/proc/trunk_check() - var/obj/structure/disposalpipe/trunk/T = locate() in loc - if(!T) - mode = 0 - flush = 0 - else - mode = initial(mode) - flush = initial(flush) - T.nicely_link_to_other_stuff(src) - -//When the disposalsoutlet is forcefully moved. Due to meteorshot (not the recall spell) -/obj/machinery/disposal/Moved(atom/OldLoc, Dir) - . = ..() - eject() - var/ptype = istype(src, /obj/machinery/disposal/deliveryChute) ? PIPE_DISPOSALS_CHUTE : PIPE_DISPOSALS_BIN //Check what disposaltype it is - var/turf/T = OldLoc - if(T.intact) - var/turf/simulated/floor/F = T - F.remove_tile(null,TRUE,TRUE) - T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") - if(trunk) - trunk.remove_trunk_links() - var/obj/structure/disposalconstruct/C = new (loc) - transfer_fingerprints_to(C) - C.ptype = ptype - C.update() - C.anchored = 0 - C.density = 1 - qdel(src) - -/obj/machinery/disposal/Destroy() - eject() - if(trunk) - trunk.remove_trunk_links() - return ..() - -/obj/machinery/disposal/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -/obj/machinery/disposal/Initialize() - // this will get a copy of the air turf and take a SEND PRESSURE amount of air from it - ..() - var/atom/L = loc - var/datum/gas_mixture/env = new - env.copy_from(L.return_air()) - var/datum/gas_mixture/removed = env.remove(SEND_PRESSURE + 1) - air_contents = new - air_contents.merge(removed) - trunk_check() - -// attack by item places it in to disposal -/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user, params) - if(stat & BROKEN || !I || !user) - return - - src.add_fingerprint(user) - - if(istype(I, /obj/item/melee/energy/blade)) - to_chat(user, "You can't place that item inside the disposal unit.") - return - - if(istype(I, /obj/item/storage)) - var/obj/item/storage/S = I - if((S.allow_quick_empty || S.allow_quick_gather) && S.contents.len) - S.hide_from(user) - user.visible_message("[user] empties \the [S] into \the [src].", "You empty \the [S] into \the [src].") - for(var/obj/item/O in S.contents) - S.remove_from_storage(O, src) - S.update_icon() // For content-sensitive icons - update() - return - - var/obj/item/grab/G = I - if(istype(G)) // handle grabbed mob - if(ismob(G.affecting)) - var/mob/GM = G.affecting - for(var/mob/V in viewers(usr)) - V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) - if(do_after(usr, 20, target = GM)) - GM.forceMove(src) - for(var/mob/C in viewers(src)) - C.show_message("[GM.name] has been placed in the [src] by [user].", 3) - qdel(G) - add_attack_logs(usr, GM, "Disposal'ed", !!GM.ckey ? null : ATKLOG_ALL) - return - - if(!I) - return - - if(!user.drop_item()) - return - if(I) - I.forceMove(src) - - to_chat(user, "You place \the [I] into the [src].") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] places \the [I] into the [src].", 3) - - update() - - - - -/obj/machinery/disposal/screwdriver_act(mob/user, obj/item/I) - if(mode>0) // It's on - return - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - if(mode==0) // It's off but still not unscrewed - mode=-1 // Set it to doubleoff l0l - else if(mode==-1) - mode=0 - to_chat(user, "You [mode ? "unfasten": "fasten"] the screws around the power connection.") - -/obj/machinery/disposal/welder_act(mob/user, obj/item/I) - . = TRUE - if(mode != required_mode_to_deconstruct) - return - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - var/obj/structure/disposalconstruct/C = new (src.loc) - C.ptype = deconstructs_to - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - -// mouse drop another mob or self -// -/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user) - if(!istype(target) || target.buckled || target.has_buckled_mobs() || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) - return - if(isanimal(user) && target != user) return //animals cannot put mobs other than themselves into disposal - src.add_fingerprint(user) - var/target_loc = target.loc - var/msg - for(var/mob/V in viewers(usr)) - if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) - V.show_message("[usr] starts climbing into the disposal.", 3) - if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) - if(target.anchored) return - V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) - if(!do_after(usr, 20, target = target)) - return - if(target_loc != target.loc) - return - if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) // if drop self, then climbed in - // must be awake, not stunned or whatever - msg = "[user.name] climbs into the [src]." - to_chat(user, "You climb into the [src].") - else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) - msg = "[user.name] stuffs [target.name] into the [src]!" - to_chat(user, "You stuff [target.name] into the [src]!") - - add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL) - else - return - target.forceMove(src) - - for(var/mob/C in viewers(src)) - if(C == user) - continue - C.show_message(msg, 3) - - update() - return - -// attempt to move while inside -/obj/machinery/disposal/relaymove(mob/user as mob) - if(user.stat || src.flushing) - return - src.go_out(user) - return - -// leave the disposal -/obj/machinery/disposal/proc/go_out(mob/user) - if(user) - user.forceMove(loc) - update() - -// ai as human but can't flush -/obj/machinery/disposal/attack_ai(mob/user as mob) - src.add_hiddenprint(user) - ui_interact(user) - -/obj/machinery/disposal/attack_ghost(mob/user as mob) - ui_interact(user) - -// human interact with machine -/obj/machinery/disposal/attack_hand(mob/user as mob) - if(..(user)) - return 1 - - if(stat & BROKEN) - return - - if(user && user.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") - return - - // Clumsy folks can only flush it. - if(user.IsAdvancedToolUser()) - ui_interact(user) - else - flush = !flush - update() - return - -/obj/machinery/disposal/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "disposal_bin.tmpl", "Waste Disposal Unit", 395, 250) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/pressure = Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100) - var/pressure_round = round(pressure,1) - - data["isAI"] = isAI(user) - data["flushing"] = flush - data["mode"] = mode - if(mode <= 0) - data["pumpstatus"] = "N/A" - else if(mode == 1) - data["pumpstatus"] = "Pressurizing" - else if(mode == 2) - data["pumpstatus"] = "Ready" - else - data["pumpstatus"] = "Idle" - data["pressure"] = pressure_round - - return data - -/obj/machinery/disposal/Topic(href, href_list) - if(usr.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") - return - - if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 - to_chat(usr, "The disposal units power is disabled.") - return - - if(..()) - return - - if(stat & BROKEN) - return - - src.add_fingerprint(usr) - - if(usr.stat || usr.restrained() || src.flushing) - return - - if(istype(src.loc, /turf)) - if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 - update() - - if(!isAI(usr)) - if(href_list["handle"]) - flush = text2num(href_list["handle"]) - update() - - if(href_list["eject"]) - eject() - return - -// eject the contents of the disposal unit -/obj/machinery/disposal/proc/eject() - for(var/atom/movable/AM in src) - AM.loc = src.loc - AM.pipe_eject(0) - update() - -// update the icon & overlays to reflect mode & status -/obj/machinery/disposal/proc/update() - overlays.Cut() - if(stat & BROKEN) - icon_state = "disposal-broken" - mode = 0 - flush = 0 - return - - // flush handle - if(flush) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-handle") - - // only handle is shown if no power - if(stat & NOPOWER || mode == -1) - return - - // check for items in disposal - occupied light - if(contents.len > 0) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-full") - - // charging and ready light - if(mode == 1) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-charge") - else if(mode == 2) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-ready") - -// timed process -// charge the gas reservoir and perform flush if ready -/obj/machinery/disposal/process() - use_power = NO_POWER_USE - if(stat & BROKEN) // nothing can happen if broken - return - - flush_count++ - if( flush_count >= flush_every_ticks ) - if( contents.len ) - if(mode == 2) - spawn(0) - feedback_inc("disposal_auto_flush",1) - flush() - flush_count = 0 - - src.updateDialog() - - if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power - flush() - - if(stat & NOPOWER) // won't charge if no power - return - - use_power = IDLE_POWER_USE - - if(mode != 1) // if off or ready, no need to charge - return - - // otherwise charge - use_power = ACTIVE_POWER_USE - - var/atom/L = loc // recharging from loc turf - - var/datum/gas_mixture/env = L.return_air() - var/pressure_delta = (SEND_PRESSURE*1.01) - air_contents.return_pressure() - - if(env.temperature > 0) - var/transfer_moles = 0.1 * pressure_delta*air_contents.volume/(env.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = env.remove(transfer_moles) - air_contents.merge(removed) - air_update_turf() - - - // if full enough, switch to ready mode - if(air_contents.return_pressure() >= SEND_PRESSURE) - mode = 2 - update() - return - -// perform a flush -/obj/machinery/disposal/proc/flush() - - flushing = 1 - flick("[icon_state]-flush", src) - - var/wrapcheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - //Hacky test to get drones to mail themselves through disposals. - for(var/mob/living/silicon/robot/drone/D in src) - wrapcheck = 1 - - for(var/mob/living/silicon/robot/syndicate/saboteur/R in src) - wrapcheck = 1 - - for(var/obj/item/smallDelivery/O in src) - wrapcheck = 1 - - if(wrapcheck == 1) - H.tomail = 1 - - sleep(10) - if(last_sound < world.time + 1) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - last_sound = world.time - sleep(5) // wait for animation to finish - - - H.init(src) // copy the contents of disposer to holder - air_contents = new() // The holder just took our gas; replace it - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - - -// called when area power changes -/obj/machinery/disposal/power_change() - ..() // do default setting/reset of stat NOPOWER bit - update() // update icon - return - - -// called when holder is expelled from a disposal -// should usually only occur if the pipe network is modified -/obj/machinery/disposal/proc/expel(var/obj/structure/disposalholder/H) - - var/turf/target - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it - for(var/atom/movable/AM in H) - target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) - - AM.loc = src.loc - AM.pipe_eject(0) - if(!istype(AM, /mob/living/silicon/robot/drone) && !istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z - spawn(1) - if(AM) - AM.throw_at(target, 5, 1) - - H.vent_gas(loc) - qdel(H) - -/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover,/obj/item) && mover.throwing) - var/obj/item/I = mover - if(istype(I, /obj/item/projectile)) - return - if(prob(75)) - I.loc = src - for(var/mob/M in viewers(src)) - M.show_message("\the [I] lands in \the [src].", 3) - update() - else - for(var/mob/M in viewers(src)) - M.show_message("\the [I] bounces off of \the [src]'s rim!.", 3) - return 0 - else - return ..(mover, target, height) - - -/obj/machinery/disposal/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - qdel(src) - -/obj/machinery/disposal/get_remote_view_fullscreens(mob/user) - if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -// virtual disposal object -// travels through pipes in lieu of actual items -// contents will be items flushed by the disposal -// this allows the gas flushed to be tracked - -/obj/structure/disposalholder - invisibility = 101 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point - var/active = 0 // true if the holder is moving, otherwise inactive - dir = 0 - var/count = 1000 //*** can travel 1000 steps before going inactive (in case of loops) - var/has_fat_guy = 0 // true if contains a fat person - var/destinationTag = 0 // changes if contains a delivery container - var/tomail = 0 //changes if contains wrapped package - var/hasmob = 0 //If it contains a mob - -/obj/structure/disposalholder/Destroy() - QDEL_NULL(gas) - active = 0 - return ..() - - // initialize a holder from the contents of a disposal unit -/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D) - gas = D.air_contents// transfer gas resv. into holder object - - //Check for any living mobs trigger hasmob. - //hasmob effects whether the package goes to cargo or its tagged destination. - for(var/mob/living/M in D) - if(M && M.stat != 2 && !istype(M, /mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) - hasmob = 1 - - //Checks 1 contents level deep. This means that players can be sent through disposals... - //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) - for(var/obj/O in D) - if(O.contents) - for(var/mob/living/M in O.contents) - if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) - hasmob = 1 - - // now everything inside the disposal gets put into the holder - // note AM since can contain mobs or objs - for(var/atom/movable/AM in D) - AM.loc = src - SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D) - if(istype(AM, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = AM - if(FAT in H.mutations) // is a human and fat? - has_fat_guy = 1 // set flag on holder - if(istype(AM, /obj/structure/bigDelivery) && !hasmob) - var/obj/structure/bigDelivery/T = AM - destinationTag = T.sortTag - if(istype(AM, /obj/item/smallDelivery) && !hasmob) - var/obj/item/smallDelivery/T = AM - destinationTag = T.sortTag - //Drones can mail themselves through maint. - if(istype(AM, /mob/living/silicon/robot/drone)) - var/mob/living/silicon/robot/drone/drone = AM - destinationTag = drone.mail_destination - if(istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) - var/mob/living/silicon/robot/syndicate/saboteur/S = AM - destinationTag = S.mail_destination - if(istype(AM, /obj/item/shippingPackage) && !hasmob) - var/obj/item/shippingPackage/sp = AM - if(sp.sealed) //only sealed packages get delivered to their intended destination - destinationTag = sp.sortTag - - - // start the movement process - // argument is the disposal unit the holder started in -/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) - if(!D.trunk) - D.expel(src) // no trunk connected, so expel immediately - return - - loc = D.trunk - active = 1 - dir = DOWN - spawn(1) - move() // spawn off the movement process - - return - - // movement process, persists while holder is moving through pipes -/obj/structure/disposalholder/proc/move() - var/obj/structure/disposalpipe/last - while(active) - /* if(hasmob && prob(3)) - for(var/mob/living/H in src) - if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, - H.take_overall_damage(20, 0, "Blunt Trauma") */ //horribly maim any living creature jumping down disposals. c'est la vie - - if(has_fat_guy && prob(2)) // chance of becoming stuck per segment if contains a fat guy - active = 0 - // find the fat guys - for(var/mob/living/carbon/human/H in src) - - break - sleep(1) // was 1 - var/obj/structure/disposalpipe/curr = loc - last = curr - curr = curr.transfer(src) - if(!curr) - last.expel(src, loc, dir) - - // - if(!(count--)) - active = 0 - return - - - - // find the turf which should contain the next pipe -/obj/structure/disposalholder/proc/nextloc() - return get_step(loc,dir) - - // find a matching pipe on a turf -/obj/structure/disposalholder/proc/findpipe(var/turf/T) - if(!T) - return null - - var/fdir = turn(dir, 180) // flip the movement direction - for(var/obj/structure/disposalpipe/P in T) - if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir - return P - // if no matching pipe, return null - return null - - // merge two holder objects - // used when a a holder meets a stuck holder -/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) - for(var/atom/movable/AM in other) - AM.loc = src // move everything in other holder to this one - if(ismob(AM)) - var/mob/M = AM - if(M.client) // if a client mob, update eye to follow this holder - M.client.eye = src - - if(other.has_fat_guy) - has_fat_guy = 1 - qdel(other) - - - // called when player tries to move while in a pipe -/obj/structure/disposalholder/relaymove(mob/user as mob) - if(!istype(user,/mob/living)) - return - - var/mob/living/U = user - - if(U.stat || U.last_special <= world.time) - return - - U.last_special = world.time+100 - - if(src.loc) - for(var/mob/M in hearers(src.loc.loc)) - to_chat(M, "CLONG, clong!") - - playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0) - - // called to vent all gas in holder to a location -/obj/structure/disposalholder/proc/vent_gas(var/atom/location) - if(location) - location.assume_air(gas) // vent all gas to turf - air_update_turf() - return - -// Disposal pipes - -/obj/structure/disposalpipe - icon = 'icons/obj/pipes/disposal.dmi' - name = "disposal pipe" - desc = "An underfloor disposal pipe." - anchored = 1 - density = 0 - - on_blueprints = TRUE - level = 1 // underfloor only - var/dpdir = 0 // bitmask of pipe directions - dir = 0 // dir will contain dominant direction for junction pipes - var/health = 10 // health points 0-10 - max_integrity = 200 - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) - damage_deflection = 10 - layer = DISPOSAL_PIPE_LAYER // slightly lower than wires and other pipes - var/base_icon_state // initial icon state on map - - // new pipe, set the icon_state as on map -/obj/structure/disposalpipe/New() - ..() - base_icon_state = icon_state - - -// pipe is deleted -// ensure if holder is present, it is expelled -/obj/structure/disposalpipe/Destroy() - for(var/obj/structure/disposalholder/H in contents) - H.active = 0 - var/turf/T = loc - if(T.density) - // deleting pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(0) - qdel(H) - ..() - return - - // otherwise, do normal expel from turf - expel(H, T, 0) - return ..() - -/obj/structure/disposalpipe/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -// returns the direction of the next pipe object, given the entrance dir -// by default, returns the bitmask of remaining directions -/obj/structure/disposalpipe/proc/nextdir(var/fromdir) - return dpdir & (~turn(fromdir, 180)) - -// transfer the holder through this pipe segment -// overriden for special behaviour -// -/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.dir = nextdir - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.loc = P - else // if wasn't a pipe, then set loc to turf - H.loc = T - return null - - return P - - -// update the icon_state to reflect hidden status -/obj/structure/disposalpipe/proc/update() - var/turf/T = src.loc - hide(T.intact && !istype(T,/turf/space)) // space never hides pipes - update_icon() - -// hide called by levelupdate if turf intact status changes -// change visibility status and force update of icon -/obj/structure/disposalpipe/hide(var/intact) - invisibility = intact ? 101: 0 // hide if floor is intact - update_icon() - -// update actual icon_state depending on visibility -// if invisible, append "f" to icon_state to show faded version -// this will be revealed if a T-scanner is used -// if visible, use regular icon_state -/obj/structure/disposalpipe/update_icon() - if(invisibility) - icon_state = "[base_icon_state]f" - else - icon_state = base_icon_state - - -// expel the held objects into a turf -// called when there is a break in the pipe -// - -/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) - - if(!T) - return - - var/turf/target - - if(T.density) // dense ouput turf, so stop holder - H.active = 0 - H.loc = src - return - if(T.intact && istype(T,/turf/simulated/floor)) //intact floor, pop the tile - var/turf/simulated/floor/F = T - new F.builtin_tile.type(H) - F.remove_tile(null,TRUE,FALSE) - - if(direction) // direction is specified - if(istype(T, /turf/space)) // if ended in space, then range is unlimited - target = get_edge_target_turf(T, direction) - else // otherwise limit to 10 tiles - target = get_ranged_target_turf(T, direction, 10) - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(direction) - spawn(1) - if(AM) - AM.throw_at(target, 100, 1) - H.vent_gas(T) - qdel(H) - - else // no specified direction, so throw in random direction - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) - - AM.loc = T - AM.pipe_eject(0) - spawn(1) - if(AM) - AM.throw_at(target, 5, 1) - - H.vent_gas(T) // all gas vent to turf - qdel(H) - -// call to break the pipe -// will expel any holder inside at the time -// then delete the pipe -// remains : set to leave broken pipe pieces in place -/obj/structure/disposalpipe/proc/broken(remains = 0) - if(remains) - for(var/D in cardinal) - if(D & dpdir) - var/obj/structure/disposalpipe/broken/P = new(src.loc) - P.setDir(D) - - invisibility = 101 // make invisible (since we won't delete the pipe immediately) - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // broken pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(0) - qdel(H) - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - - spawn(2) // delete pipe after 2 ticks to ensure expel proc finished - qdel(src) - -// pipe affected by explosion -/obj/structure/disposalpipe/ex_act(severity) - switch(severity) - if(1) - broken(0) - if(2) - health -= rand(5, 15) - healthcheck() - if(3) - health -= rand(0, 15) - healthcheck() - -// test health for brokenness -/obj/structure/disposalpipe/proc/healthcheck() - if(health < -2) - broken(0) - else if(health<1) - broken(1) - return - -//attack by item -//weldingtool: unfasten and convert to obj/disposalconstruct - -/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user, params) - var/turf/T = get_turf(src) - if(T.intact) - return // prevent interaction with T-scanner revealed pipes - - add_fingerprint(user) - -/obj/structure/disposalpipe/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_SLICING_MESSAGE - if(!I.use_tool(src, user, 30, volume = I.tool_volume)) - return - WELDER_SLICING_SUCCESS_MESSAGE - var/obj/structure/disposalconstruct/C = new (get_turf(src)) - switch(base_icon_state) - if("pipe-s") - C.ptype = PIPE_DISPOSALS_STRAIGHT - if("pipe-c") - C.ptype = PIPE_DISPOSALS_BENT - if("pipe-j1") - C.ptype = PIPE_DISPOSALS_JUNCTION_RIGHT - if("pipe-j2") - C.ptype = PIPE_DISPOSALS_JUNCTION_LEFT - if("pipe-y") - C.ptype = PIPE_DISPOSALS_Y_JUNCTION - if("pipe-t") - C.ptype = PIPE_DISPOSALS_TRUNK - if("pipe-j1s") - C.ptype = PIPE_DISPOSALS_SORT_RIGHT - if("pipe-j2s") - C.ptype = PIPE_DISPOSALS_SORT_LEFT - src.transfer_fingerprints_to(C) - C.dir = dir - C.density = FALSE - C.anchored = TRUE - C.update() - - qdel(src) - -// *** TEST verb -//client/verb/dispstop() -// for(var/obj/structure/disposalholder/H in world) -// H.active = 0 - -// a straight or bent segment -/obj/structure/disposalpipe/segment - icon_state = "pipe-s" - -/obj/structure/disposalpipe/segment/New() - ..() - if(icon_state == "pipe-s") - dpdir = dir | turn(dir, 180) - else - dpdir = dir | turn(dir, -90) - - update() - - - -//a three-way junction with dir being the dominant direction -/obj/structure/disposalpipe/junction - icon_state = "pipe-j1" - -/obj/structure/disposalpipe/junction/New() - ..() - if(icon_state == "pipe-j1") - dpdir = dir | turn(dir, -90) | turn(dir,180) - else if(icon_state == "pipe-j2") - dpdir = dir | turn(dir, 90) | turn(dir,180) - else // pipe-y - dpdir = dir | turn(dir,90) | turn(dir, -90) - update() - - - // next direction to move - // if coming in from secondary dirs, then next is primary dir - // if coming in from primary dir, then next is equal chance of other dirs - -/obj/structure/disposalpipe/junction/nextdir(var/fromdir) - var/flipdir = turn(fromdir, 180) - if(flipdir != dir) // came from secondary dir - return dir // so exit through primary - else // came from primary - // so need to choose either secondary exit - var/mask = ..(fromdir) - - // find a bit which is set - var/setbit = 0 - if(mask & NORTH) - setbit = NORTH - else if(mask & SOUTH) - setbit = SOUTH - else if(mask & EAST) - setbit = EAST - else - setbit = WEST - - if(prob(50)) // 50% chance to choose the found bit or the other one - return setbit - else - return mask & (~setbit) - -//a three-way junction that sorts objects -/obj/structure/disposalpipe/sortjunction - - icon_state = "pipe-j1s" - var/sortType = 0 //Look at the list called TAGGERLOCATIONS in /code/_globalvars/lists/flavor_misc.dm - var/posdir = 0 - var/negdir = 0 - var/sortdir = 0 - -/obj/structure/disposalpipe/sortjunction/proc/updatedesc() - desc = "An underfloor disposal pipe with a package sorting mechanism." - if(sortType>0) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[sortType]) - desc += "\nIt's tagged with [tag]" - -/obj/structure/disposalpipe/sortjunction/proc/updatedir() - posdir = dir - negdir = turn(posdir, 180) - - if(icon_state == "pipe-j1s") - sortdir = turn(posdir, -90) - else - icon_state = "pipe-j2s" - sortdir = turn(posdir, 90) - - dpdir = sortdir | posdir | negdir - -/obj/structure/disposalpipe/sortjunction/New() - ..() - updatedir() - updatedesc() - update() - return - -/obj/structure/disposalpipe/sortjunction/attackby(var/obj/item/I, var/mob/user, params) - if(..()) - return - - if(istype(I, /obj/item/destTagger)) - var/obj/item/destTagger/O = I - - if(O.currTag > 0)// Tag set - sortType = O.currTag - name = GLOB.TAGGERLOCATIONS[O.currTag] - playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) - to_chat(user, "Changed filter to [tag]") - updatedesc() - - - // next direction to move - // if coming in from negdir, then next is primary dir or sortdir - // if coming in from posdir, then flip around and go back to posdir - // if coming in from sortdir, go to posdir - -/obj/structure/disposalpipe/sortjunction/nextdir(var/fromdir, var/sortTag) - //var/flipdir = turn(fromdir, 180) - if(fromdir != sortdir) // probably came from the negdir - - if(src.sortType == sortTag) //if destination matches filtered type... - return sortdir // exit through sortdirection - else - return posdir - else // came from sortdir - // so go with the flow to positive direction - return posdir - -/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir, H.destinationTag) - H.dir = nextdir - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - H.loc = P - else // if wasn't a pipe, then set loc to turf - H.loc = T - return null - - return P - - -//a three-way junction that sorts objects destined for the mail office mail table (tomail = 1) -/obj/structure/disposalpipe/wrapsortjunction - desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects." - icon_state = "pipe-j1s" - var/posdir = 0 - var/negdir = 0 - var/sortdir = 0 - -/obj/structure/disposalpipe/wrapsortjunction/New() - ..() - posdir = dir - if(icon_state == "pipe-j1s") - sortdir = turn(posdir, -90) - negdir = turn(posdir, 180) - else - icon_state = "pipe-j2s" - sortdir = turn(posdir, 90) - negdir = turn(posdir, 180) - dpdir = sortdir | posdir | negdir - - update() - return - - - // next direction to move - // if coming in from negdir, then next is primary dir or sortdir - // if coming in from posdir, then flip around and go back to posdir - // if coming in from sortdir, go to posdir - -/obj/structure/disposalpipe/wrapsortjunction/nextdir(var/fromdir, var/istomail) - //var/flipdir = turn(fromdir, 180) - if(fromdir != sortdir) // probably came from the negdir - if(istomail) //if destination matches filtered type... - return sortdir // exit through sortdirection - else - return posdir - else // came from sortdir - return posdir // so go with the flow to positive direction - -/obj/structure/disposalpipe/wrapsortjunction/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir, H.tomail) - H.dir = nextdir - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.loc = P - else // if wasn't a pipe, then set loc to turf - H.loc = T - return null - - return P - -//a trunk joining to a disposal bin or outlet on the same turf -/obj/structure/disposalpipe/trunk - icon_state = "pipe-t" - var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet - -/obj/structure/disposalpipe/trunk/New() - ..() - dpdir = dir - spawn(1) - getlinked() - - update() - return - -/obj/structure/disposalpipe/trunk/Destroy() - if(istype(linked, /obj/structure/disposaloutlet)) - var/obj/structure/disposaloutlet/O = linked - O.expel(animation = 0) - else if(istype(linked, /obj/machinery/disposal)) - var/obj/machinery/disposal/D = linked - if(D.trunk == src) - D.go_out() - D.trunk = null - remove_trunk_links() - return ..() - -/obj/structure/disposalpipe/trunk/proc/getlinked() - var/obj/machinery/disposal/D = locate() in src.loc - if(D) - nicely_link_to_other_stuff(D) - return - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O) - nicely_link_to_other_stuff(O) - -/obj/structure/disposalpipe/trunk/proc/remove_trunk_links() //disposals is well-coded - if(!linked) - return - else if(istype(linked, /obj/machinery/disposal)) //jk lol - var/obj/machinery/disposal/D = linked - D.trunk = null - else if(istype(linked, /obj/structure/disposaloutlet)) //God fucking damn it - var/obj/structure/disposaloutlet/D = linked - D.linkedtrunk = null - linked = null - -/obj/structure/disposalpipe/trunk/proc/nicely_link_to_other_stuff(obj/O) - remove_trunk_links() //Breaks the connections between this trunk and the linked machinery so we don't get sent to nullspace or some shit like that - if(istype(O, /obj/machinery/disposal)) - var/obj/machinery/disposal/D = O - linked = D - D.trunk = src - else if(istype(O, /obj/structure/disposaloutlet)) - var/obj/structure/disposaloutlet/D = O - linked = D - D.linkedtrunk = src - - // Override attackby so we disallow trunkremoval when somethings ontop -/obj/structure/disposalpipe/trunk/attackby(var/obj/item/I, var/mob/user, params) - - //Disposal bins or chutes - //Disposal constructors - var/obj/structure/disposalconstruct/C = locate() in src.loc - if(C && C.anchored) - return - - var/turf/T = src.loc - if(T.intact) - return // prevent interaction with T-scanner revealed pipes - src.add_fingerprint(user) - - // would transfer to next pipe segment, but we are in a trunk - // if not entering from disposal bin, - // transfer to linked object (outlet or bin) - -/obj/structure/disposalpipe/trunk/transfer(obj/structure/disposalholder/H) - if(!H) - return - if(H.dir == DOWN) // we just entered from a disposer - return ..() // so do base transfer proc - // otherwise, go to the linked object - if(!linked) - expel(H, loc, FALSE) // expel at turf - else if(istype(linked, /obj/structure/disposaloutlet)) - var/obj/structure/disposaloutlet/DO = linked - for(var/atom/movable/AM in H) - AM.forceMove(DO) - qdel(H) - H.vent_gas(loc) - DO.expel() - else if(istype(linked, /obj/machinery/disposal)) - var/obj/machinery/disposal/D = linked - H.forceMove(D) - D.expel(H) // expel at disposal - else //just in case - expel(H, loc, FALSE) - // nextdir - -/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) - if(fromdir == DOWN) - return dir - else - return 0 - -// a broken pipe -/obj/structure/disposalpipe/broken - icon_state = "pipe-b" - dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes - // i.e. will be treated as an empty turf - desc = "A broken piece of disposal pipe." - -/obj/structure/disposalpipe/broken/New() - ..() - update() - return - -/obj/structure/disposalpipe/broken/welder_act(mob/user, obj/item/I) - if(I.use_tool(src, user, 0, volume = I.tool_volume)) - to_chat(user, "You remove [src]!") - I.play_tool_sound(src, I.tool_volume) - qdel(src) - return TRUE - -// the disposal outlet machine - -/obj/structure/disposaloutlet - name = "disposal outlet" - desc = "An outlet for the pneumatic disposal system." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "outlet" - density = 1 - anchored = 1 - var/active = 0 - var/turf/target // this will be where the output objects are 'thrown' to. - var/obj/structure/disposalpipe/trunk/linkedtrunk - var/mode = 0 - -/obj/structure/disposaloutlet/New() - ..() - spawn(1) - target = get_ranged_target_turf(src, dir, 10) - var/obj/structure/disposalpipe/trunk/T = locate() in loc - if(T) - T.nicely_link_to_other_stuff(src) - -/obj/structure/disposaloutlet/Destroy() - if(linkedtrunk) - linkedtrunk.remove_trunk_links() - expel(FALSE) - return ..() - - -// expel the contents of the outlet -/obj/structure/disposaloutlet/proc/expel(animation = TRUE) - if(animation) - flick("outlet-open", src) - playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) - sleep(20) //wait until correct animation frame - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - for(var/atom/movable/AM in contents) - AM.forceMove(loc) - AM.pipe_eject(dir) - if(istype(AM,/mob/living/silicon/robot/drone) || istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z - return - spawn(5) - if(QDELETED(AM)) - return - AM.throw_at(target, 3, 1) - - -/obj/structure/disposaloutlet/attackby(var/obj/item/I, var/mob/user, params) - if(!I || !user) - return - src.add_fingerprint(user) - if(istype(I, /obj/item/screwdriver)) - if(mode==0) - mode=1 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(mode==1) - mode=0 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - -/obj/structure/disposaloutlet/welder_act(mob/user, obj/item/I) - . = TRUE - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - var/obj/structure/disposalconstruct/C = new (src.loc) - C.ptype = PIPE_DISPOSALS_OUTLET - C.update() - C.anchored = TRUE - C.density = TRUE - transfer_fingerprints_to(C) - qdel(src) - -//When the disposalsoutlet is forcefully moved. Due to meteorshot or the recall item spell for instance -/obj/structure/disposaloutlet/Moved(atom/OldLoc, Dir) - . = ..() - var/turf/T = OldLoc - if(T.intact) - var/turf/simulated/floor/F = T - F.remove_tile(null,TRUE,TRUE) - T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") - if(linkedtrunk) - linkedtrunk.remove_trunk_links() - var/obj/structure/disposalconstruct/C = new (loc) - transfer_fingerprints_to(C) - C.ptype = PIPE_DISPOSALS_OUTLET - C.update() - C.anchored = 0 - C.density = 1 - qdel(src) - -// called when movable is expelled from a disposal pipe or outlet -// by default does nothing, override for special behaviour - -/atom/movable/proc/pipe_eject(var/direction) - return - -// check if mob has client, if so restore client view on eject -/mob/pipe_eject(var/direction) - reset_perspective(null) - -/obj/effect/decal/cleanable/blood/gibs/pipe_eject(var/direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = alldirs.Copy() - - src.streak(dirs) - -/obj/effect/decal/cleanable/blood/gibs/robot/gib/pipe_eject(var/direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = alldirs.Copy() - - src.streak(dirs) +// Disposal bin +// Holds items for disposal into pipe system +// Draws air from turf, gradually charges internal reservoir +// Once full (~1 atm), uses air resv to flush items into the pipes +// Automatically recharges air (unless off), will flush when ready if pre-set +// Can hold items and human size things, no other draggables +// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation +#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE + +/obj/machinery/disposal + name = "disposal unit" + desc = "A pneumatic waste disposal unit." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "disposal" + anchored = 1 + density = 1 + on_blueprints = TRUE + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) + max_integrity = 200 + resistance_flags = FIRE_PROOF + var/datum/gas_mixture/air_contents // internal reservoir + var/mode = 1 // item mode 0=off 1=charging 2=charged + var/flush = 0 // true if flush handle is pulled + var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk + var/flushing = 0 // true if flushing in progress + var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush + var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. + var/last_sound = 0 + var/required_mode_to_deconstruct = -1 + var/deconstructs_to = PIPE_DISPOSALS_BIN + active_power_usage = 600 + idle_power_usage = 100 + + +// create a new disposal +// find the attached trunk (if present) +/obj/machinery/disposal/New() + ..() + trunk_check() + //gas.volume = 1.05 * CELLSTANDARD + update() + +/obj/machinery/disposal/proc/trunk_check() + var/obj/structure/disposalpipe/trunk/T = locate() in loc + if(!T) + mode = 0 + flush = 0 + else + mode = initial(mode) + flush = initial(flush) + T.nicely_link_to_other_stuff(src) + +//When the disposalsoutlet is forcefully moved. Due to meteorshot (not the recall spell) +/obj/machinery/disposal/Moved(atom/OldLoc, Dir) + . = ..() + eject() + var/ptype = istype(src, /obj/machinery/disposal/deliveryChute) ? PIPE_DISPOSALS_CHUTE : PIPE_DISPOSALS_BIN //Check what disposaltype it is + var/turf/T = OldLoc + if(T.intact) + var/turf/simulated/floor/F = T + F.remove_tile(null,TRUE,TRUE) + T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") + if(trunk) + trunk.remove_trunk_links() + var/obj/structure/disposalconstruct/C = new (loc) + transfer_fingerprints_to(C) + C.ptype = ptype + C.update() + C.anchored = 0 + C.density = 1 + qdel(src) + +/obj/machinery/disposal/Destroy() + eject() + if(trunk) + trunk.remove_trunk_links() + return ..() + +/obj/machinery/disposal/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +/obj/machinery/disposal/Initialize() + // this will get a copy of the air turf and take a SEND PRESSURE amount of air from it + ..() + var/atom/L = loc + var/datum/gas_mixture/env = new + env.copy_from(L.return_air()) + var/datum/gas_mixture/removed = env.remove(SEND_PRESSURE + 1) + air_contents = new + air_contents.merge(removed) + trunk_check() + +// attack by item places it in to disposal +/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user, params) + if(stat & BROKEN || !I || !user) + return + + src.add_fingerprint(user) + + if(istype(I, /obj/item/melee/energy/blade)) + to_chat(user, "You can't place that item inside the disposal unit.") + return + + if(istype(I, /obj/item/storage)) + var/obj/item/storage/S = I + if((S.allow_quick_empty || S.allow_quick_gather) && S.contents.len) + S.hide_from(user) + user.visible_message("[user] empties \the [S] into \the [src].", "You empty \the [S] into \the [src].") + for(var/obj/item/O in S.contents) + S.remove_from_storage(O, src) + S.update_icon() // For content-sensitive icons + update() + return + + var/obj/item/grab/G = I + if(istype(G)) // handle grabbed mob + if(ismob(G.affecting)) + var/mob/GM = G.affecting + for(var/mob/V in viewers(usr)) + V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) + if(do_after(usr, 20, target = GM)) + GM.forceMove(src) + for(var/mob/C in viewers(src)) + C.show_message("[GM.name] has been placed in the [src] by [user].", 3) + qdel(G) + add_attack_logs(usr, GM, "Disposal'ed", !!GM.ckey ? null : ATKLOG_ALL) + return + + if(!I) + return + + if(!user.drop_item()) + return + if(I) + I.forceMove(src) + + to_chat(user, "You place \the [I] into the [src].") + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] places \the [I] into the [src].", 3) + + update() + + + + +/obj/machinery/disposal/screwdriver_act(mob/user, obj/item/I) + if(mode>0) // It's on + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(mode==0) // It's off but still not unscrewed + mode=-1 // Set it to doubleoff l0l + else if(mode==-1) + mode=0 + to_chat(user, "You [mode ? "unfasten": "fasten"] the screws around the power connection.") + +/obj/machinery/disposal/welder_act(mob/user, obj/item/I) + . = TRUE + if(mode != required_mode_to_deconstruct) + return + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (src.loc) + C.ptype = deconstructs_to + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + +// mouse drop another mob or self +// +/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user) + if(!istype(target) || target.buckled || target.has_buckled_mobs() || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) + return + if(isanimal(user) && target != user) return //animals cannot put mobs other than themselves into disposal + src.add_fingerprint(user) + var/target_loc = target.loc + var/msg + for(var/mob/V in viewers(usr)) + if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) + V.show_message("[usr] starts climbing into the disposal.", 3) + if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) + if(target.anchored) return + V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) + if(!do_after(usr, 20, target = target)) + return + if(target_loc != target.loc) + return + if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) // if drop self, then climbed in + // must be awake, not stunned or whatever + msg = "[user.name] climbs into the [src]." + to_chat(user, "You climb into the [src].") + else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) + msg = "[user.name] stuffs [target.name] into the [src]!" + to_chat(user, "You stuff [target.name] into the [src]!") + + add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL) + else + return + target.forceMove(src) + + for(var/mob/C in viewers(src)) + if(C == user) + continue + C.show_message(msg, 3) + + update() + return + +// attempt to move while inside +/obj/machinery/disposal/relaymove(mob/user as mob) + if(user.stat || src.flushing) + return + src.go_out(user) + return + +// leave the disposal +/obj/machinery/disposal/proc/go_out(mob/user) + if(user) + user.forceMove(loc) + update() + +// ai as human but can't flush +/obj/machinery/disposal/attack_ai(mob/user as mob) + src.add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/disposal/attack_ghost(mob/user as mob) + ui_interact(user) + +// human interact with machine +/obj/machinery/disposal/attack_hand(mob/user as mob) + if(..(user)) + return 1 + + if(stat & BROKEN) + return + + if(user && user.loc == src) + to_chat(usr, "You cannot reach the controls from inside.") + return + + // Clumsy folks can only flush it. + if(user.IsAdvancedToolUser()) + ui_interact(user) + else + flush = !flush + update() + return + +/obj/machinery/disposal/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "disposal_bin.tmpl", "Waste Disposal Unit", 395, 250) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + var/pressure = Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100) + var/pressure_round = round(pressure,1) + + data["isAI"] = isAI(user) + data["flushing"] = flush + data["mode"] = mode + if(mode <= 0) + data["pumpstatus"] = "N/A" + else if(mode == 1) + data["pumpstatus"] = "Pressurizing" + else if(mode == 2) + data["pumpstatus"] = "Ready" + else + data["pumpstatus"] = "Idle" + data["pressure"] = pressure_round + + return data + +/obj/machinery/disposal/Topic(href, href_list) + if(usr.loc == src) + to_chat(usr, "You cannot reach the controls from inside.") + return + + if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 + to_chat(usr, "The disposal units power is disabled.") + return + + if(..()) + return + + if(stat & BROKEN) + return + + src.add_fingerprint(usr) + + if(usr.stat || usr.restrained() || src.flushing) + return + + if(istype(src.loc, /turf)) + if(href_list["pump"]) + if(text2num(href_list["pump"])) + mode = 1 + else + mode = 0 + update() + + if(!isAI(usr)) + if(href_list["handle"]) + flush = text2num(href_list["handle"]) + update() + + if(href_list["eject"]) + eject() + return + +// eject the contents of the disposal unit +/obj/machinery/disposal/proc/eject() + for(var/atom/movable/AM in src) + AM.loc = src.loc + AM.pipe_eject(0) + update() + +// update the icon & overlays to reflect mode & status +/obj/machinery/disposal/proc/update() + overlays.Cut() + if(stat & BROKEN) + icon_state = "disposal-broken" + mode = 0 + flush = 0 + return + + // flush handle + if(flush) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-handle") + + // only handle is shown if no power + if(stat & NOPOWER || mode == -1) + return + + // check for items in disposal - occupied light + if(contents.len > 0) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-full") + + // charging and ready light + if(mode == 1) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-charge") + else if(mode == 2) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-ready") + +// timed process +// charge the gas reservoir and perform flush if ready +/obj/machinery/disposal/process() + use_power = NO_POWER_USE + if(stat & BROKEN) // nothing can happen if broken + return + + flush_count++ + if( flush_count >= flush_every_ticks ) + if( contents.len ) + if(mode == 2) + spawn(0) + feedback_inc("disposal_auto_flush",1) + flush() + flush_count = 0 + + src.updateDialog() + + if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power + flush() + + if(stat & NOPOWER) // won't charge if no power + return + + use_power = IDLE_POWER_USE + + if(mode != 1) // if off or ready, no need to charge + return + + // otherwise charge + use_power = ACTIVE_POWER_USE + + var/atom/L = loc // recharging from loc turf + + var/datum/gas_mixture/env = L.return_air() + var/pressure_delta = (SEND_PRESSURE*1.01) - air_contents.return_pressure() + + if(env.temperature > 0) + var/transfer_moles = 0.1 * pressure_delta*air_contents.volume/(env.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = env.remove(transfer_moles) + air_contents.merge(removed) + air_update_turf() + + + // if full enough, switch to ready mode + if(air_contents.return_pressure() >= SEND_PRESSURE) + mode = 2 + update() + return + +// perform a flush +/obj/machinery/disposal/proc/flush() + + flushing = 1 + flick("[icon_state]-flush", src) + + var/wrapcheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + //Hacky test to get drones to mail themselves through disposals. + for(var/mob/living/silicon/robot/drone/D in src) + wrapcheck = 1 + + for(var/mob/living/silicon/robot/syndicate/saboteur/R in src) + wrapcheck = 1 + + for(var/obj/item/smallDelivery/O in src) + wrapcheck = 1 + + if(wrapcheck == 1) + H.tomail = 1 + + sleep(10) + if(last_sound < world.time + 1) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + last_sound = world.time + sleep(5) // wait for animation to finish + + + H.init(src) // copy the contents of disposer to holder + air_contents = new() // The holder just took our gas; replace it + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + + +// called when area power changes +/obj/machinery/disposal/power_change() + ..() // do default setting/reset of stat NOPOWER bit + update() // update icon + return + + +// called when holder is expelled from a disposal +// should usually only occur if the pipe network is modified +/obj/machinery/disposal/proc/expel(var/obj/structure/disposalholder/H) + + var/turf/target + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it + for(var/atom/movable/AM in H) + target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) + + AM.loc = src.loc + AM.pipe_eject(0) + if(!istype(AM, /mob/living/silicon/robot/drone) && !istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z + spawn(1) + if(AM) + AM.throw_at(target, 5, 1) + + H.vent_gas(loc) + qdel(H) + +/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover,/obj/item) && mover.throwing) + var/obj/item/I = mover + if(istype(I, /obj/item/projectile)) + return + if(prob(75)) + I.loc = src + for(var/mob/M in viewers(src)) + M.show_message("\the [I] lands in \the [src].", 3) + update() + else + for(var/mob/M in viewers(src)) + M.show_message("\the [I] bounces off of \the [src]'s rim!.", 3) + return 0 + else + return ..(mover, target, height) + + +/obj/machinery/disposal/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + qdel(src) + +/obj/machinery/disposal/get_remote_view_fullscreens(mob/user) + if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +// virtual disposal object +// travels through pipes in lieu of actual items +// contents will be items flushed by the disposal +// this allows the gas flushed to be tracked + +/obj/structure/disposalholder + invisibility = 101 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point + var/active = 0 // true if the holder is moving, otherwise inactive + dir = 0 + var/count = 1000 //*** can travel 1000 steps before going inactive (in case of loops) + var/has_fat_guy = 0 // true if contains a fat person + var/destinationTag = 0 // changes if contains a delivery container + var/tomail = 0 //changes if contains wrapped package + var/hasmob = 0 //If it contains a mob + +/obj/structure/disposalholder/Destroy() + QDEL_NULL(gas) + active = 0 + return ..() + + // initialize a holder from the contents of a disposal unit +/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D) + gas = D.air_contents// transfer gas resv. into holder object + + //Check for any living mobs trigger hasmob. + //hasmob effects whether the package goes to cargo or its tagged destination. + for(var/mob/living/M in D) + if(M && M.stat != 2 && !istype(M, /mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) + hasmob = 1 + + //Checks 1 contents level deep. This means that players can be sent through disposals... + //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) + for(var/obj/O in D) + if(O.contents) + for(var/mob/living/M in O.contents) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) + hasmob = 1 + + // now everything inside the disposal gets put into the holder + // note AM since can contain mobs or objs + for(var/atom/movable/AM in D) + AM.loc = src + SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D) + if(istype(AM, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = AM + if(FAT in H.mutations) // is a human and fat? + has_fat_guy = 1 // set flag on holder + if(istype(AM, /obj/structure/bigDelivery) && !hasmob) + var/obj/structure/bigDelivery/T = AM + destinationTag = T.sortTag + if(istype(AM, /obj/item/smallDelivery) && !hasmob) + var/obj/item/smallDelivery/T = AM + destinationTag = T.sortTag + //Drones can mail themselves through maint. + if(istype(AM, /mob/living/silicon/robot/drone)) + var/mob/living/silicon/robot/drone/drone = AM + destinationTag = drone.mail_destination + if(istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) + var/mob/living/silicon/robot/syndicate/saboteur/S = AM + destinationTag = S.mail_destination + if(istype(AM, /obj/item/shippingPackage) && !hasmob) + var/obj/item/shippingPackage/sp = AM + if(sp.sealed) //only sealed packages get delivered to their intended destination + destinationTag = sp.sortTag + + + // start the movement process + // argument is the disposal unit the holder started in +/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) + if(!D.trunk) + D.expel(src) // no trunk connected, so expel immediately + return + + loc = D.trunk + active = 1 + dir = DOWN + spawn(1) + move() // spawn off the movement process + + return + + // movement process, persists while holder is moving through pipes +/obj/structure/disposalholder/proc/move() + var/obj/structure/disposalpipe/last + while(active) + /* if(hasmob && prob(3)) + for(var/mob/living/H in src) + if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, + H.take_overall_damage(20, 0, "Blunt Trauma") */ //horribly maim any living creature jumping down disposals. c'est la vie + + if(has_fat_guy && prob(2)) // chance of becoming stuck per segment if contains a fat guy + active = 0 + // find the fat guys + for(var/mob/living/carbon/human/H in src) + + break + sleep(1) // was 1 + var/obj/structure/disposalpipe/curr = loc + last = curr + curr = curr.transfer(src) + if(!curr) + last.expel(src, loc, dir) + + // + if(!(count--)) + active = 0 + return + + + + // find the turf which should contain the next pipe +/obj/structure/disposalholder/proc/nextloc() + return get_step(loc,dir) + + // find a matching pipe on a turf +/obj/structure/disposalholder/proc/findpipe(var/turf/T) + if(!T) + return null + + var/fdir = turn(dir, 180) // flip the movement direction + for(var/obj/structure/disposalpipe/P in T) + if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir + return P + // if no matching pipe, return null + return null + + // merge two holder objects + // used when a a holder meets a stuck holder +/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) + for(var/atom/movable/AM in other) + AM.loc = src // move everything in other holder to this one + if(ismob(AM)) + var/mob/M = AM + if(M.client) // if a client mob, update eye to follow this holder + M.client.eye = src + + if(other.has_fat_guy) + has_fat_guy = 1 + qdel(other) + + + // called when player tries to move while in a pipe +/obj/structure/disposalholder/relaymove(mob/user as mob) + if(!istype(user,/mob/living)) + return + + var/mob/living/U = user + + if(U.stat || U.last_special <= world.time) + return + + U.last_special = world.time+100 + + if(src.loc) + for(var/mob/M in hearers(src.loc.loc)) + to_chat(M, "CLONG, clong!") + + playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0) + + // called to vent all gas in holder to a location +/obj/structure/disposalholder/proc/vent_gas(var/atom/location) + if(location) + location.assume_air(gas) // vent all gas to turf + air_update_turf() + return + +// Disposal pipes + +/obj/structure/disposalpipe + icon = 'icons/obj/pipes/disposal.dmi' + name = "disposal pipe" + desc = "An underfloor disposal pipe." + anchored = 1 + density = 0 + + on_blueprints = TRUE + level = 1 // underfloor only + var/dpdir = 0 // bitmask of pipe directions + dir = 0 // dir will contain dominant direction for junction pipes + var/health = 10 // health points 0-10 + max_integrity = 200 + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) + damage_deflection = 10 + layer = DISPOSAL_PIPE_LAYER // slightly lower than wires and other pipes + var/base_icon_state // initial icon state on map + + // new pipe, set the icon_state as on map +/obj/structure/disposalpipe/New() + ..() + base_icon_state = icon_state + + +// pipe is deleted +// ensure if holder is present, it is expelled +/obj/structure/disposalpipe/Destroy() + for(var/obj/structure/disposalholder/H in contents) + H.active = 0 + var/turf/T = loc + if(T.density) + // deleting pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(0) + qdel(H) + ..() + return + + // otherwise, do normal expel from turf + expel(H, T, 0) + return ..() + +/obj/structure/disposalpipe/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +// returns the direction of the next pipe object, given the entrance dir +// by default, returns the bitmask of remaining directions +/obj/structure/disposalpipe/proc/nextdir(var/fromdir) + return dpdir & (~turn(fromdir, 180)) + +// transfer the holder through this pipe segment +// overriden for special behaviour +// +/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.dir = nextdir + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.loc = P + else // if wasn't a pipe, then set loc to turf + H.loc = T + return null + + return P + + +// update the icon_state to reflect hidden status +/obj/structure/disposalpipe/proc/update() + var/turf/T = src.loc + hide(T.intact && !istype(T,/turf/space)) // space never hides pipes + update_icon() + +// hide called by levelupdate if turf intact status changes +// change visibility status and force update of icon +/obj/structure/disposalpipe/hide(var/intact) + invisibility = intact ? 101: 0 // hide if floor is intact + update_icon() + +// update actual icon_state depending on visibility +// if invisible, append "f" to icon_state to show faded version +// this will be revealed if a T-scanner is used +// if visible, use regular icon_state +/obj/structure/disposalpipe/update_icon() + if(invisibility) + icon_state = "[base_icon_state]f" + else + icon_state = base_icon_state + + +// expel the held objects into a turf +// called when there is a break in the pipe +// + +/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) + + if(!T) + return + + var/turf/target + + if(T.density) // dense ouput turf, so stop holder + H.active = 0 + H.loc = src + return + if(T.intact && istype(T,/turf/simulated/floor)) //intact floor, pop the tile + var/turf/simulated/floor/F = T + new F.builtin_tile.type(H) + F.remove_tile(null,TRUE,FALSE) + + if(direction) // direction is specified + if(istype(T, /turf/space)) // if ended in space, then range is unlimited + target = get_edge_target_turf(T, direction) + else // otherwise limit to 10 tiles + target = get_ranged_target_turf(T, direction, 10) + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(direction) + spawn(1) + if(AM) + AM.throw_at(target, 100, 1) + H.vent_gas(T) + qdel(H) + + else // no specified direction, so throw in random direction + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) + + AM.loc = T + AM.pipe_eject(0) + spawn(1) + if(AM) + AM.throw_at(target, 5, 1) + + H.vent_gas(T) // all gas vent to turf + qdel(H) + +// call to break the pipe +// will expel any holder inside at the time +// then delete the pipe +// remains : set to leave broken pipe pieces in place +/obj/structure/disposalpipe/proc/broken(remains = 0) + if(remains) + for(var/D in GLOB.cardinal) + if(D & dpdir) + var/obj/structure/disposalpipe/broken/P = new(src.loc) + P.setDir(D) + + invisibility = 101 // make invisible (since we won't delete the pipe immediately) + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // broken pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(0) + qdel(H) + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + + spawn(2) // delete pipe after 2 ticks to ensure expel proc finished + qdel(src) + +// pipe affected by explosion +/obj/structure/disposalpipe/ex_act(severity) + switch(severity) + if(1) + broken(0) + if(2) + health -= rand(5, 15) + healthcheck() + if(3) + health -= rand(0, 15) + healthcheck() + +// test health for brokenness +/obj/structure/disposalpipe/proc/healthcheck() + if(health < -2) + broken(0) + else if(health<1) + broken(1) + return + +//attack by item +//weldingtool: unfasten and convert to obj/disposalconstruct + +/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user, params) + var/turf/T = get_turf(src) + if(T.intact) + return // prevent interaction with T-scanner revealed pipes + + add_fingerprint(user) + +/obj/structure/disposalpipe/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + WELDER_SLICING_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (get_turf(src)) + switch(base_icon_state) + if("pipe-s") + C.ptype = PIPE_DISPOSALS_STRAIGHT + if("pipe-c") + C.ptype = PIPE_DISPOSALS_BENT + if("pipe-j1") + C.ptype = PIPE_DISPOSALS_JUNCTION_RIGHT + if("pipe-j2") + C.ptype = PIPE_DISPOSALS_JUNCTION_LEFT + if("pipe-y") + C.ptype = PIPE_DISPOSALS_Y_JUNCTION + if("pipe-t") + C.ptype = PIPE_DISPOSALS_TRUNK + if("pipe-j1s") + C.ptype = PIPE_DISPOSALS_SORT_RIGHT + if("pipe-j2s") + C.ptype = PIPE_DISPOSALS_SORT_LEFT + src.transfer_fingerprints_to(C) + C.dir = dir + C.density = FALSE + C.anchored = TRUE + C.update() + + qdel(src) + +// *** TEST verb +//client/verb/dispstop() +// for(var/obj/structure/disposalholder/H in world) +// H.active = 0 + +// a straight or bent segment +/obj/structure/disposalpipe/segment + icon_state = "pipe-s" + +/obj/structure/disposalpipe/segment/New() + ..() + if(icon_state == "pipe-s") + dpdir = dir | turn(dir, 180) + else + dpdir = dir | turn(dir, -90) + + update() + + + +//a three-way junction with dir being the dominant direction +/obj/structure/disposalpipe/junction + icon_state = "pipe-j1" + +/obj/structure/disposalpipe/junction/New() + ..() + if(icon_state == "pipe-j1") + dpdir = dir | turn(dir, -90) | turn(dir,180) + else if(icon_state == "pipe-j2") + dpdir = dir | turn(dir, 90) | turn(dir,180) + else // pipe-y + dpdir = dir | turn(dir,90) | turn(dir, -90) + update() + + + // next direction to move + // if coming in from secondary dirs, then next is primary dir + // if coming in from primary dir, then next is equal chance of other dirs + +/obj/structure/disposalpipe/junction/nextdir(var/fromdir) + var/flipdir = turn(fromdir, 180) + if(flipdir != dir) // came from secondary dir + return dir // so exit through primary + else // came from primary + // so need to choose either secondary exit + var/mask = ..(fromdir) + + // find a bit which is set + var/setbit = 0 + if(mask & NORTH) + setbit = NORTH + else if(mask & SOUTH) + setbit = SOUTH + else if(mask & EAST) + setbit = EAST + else + setbit = WEST + + if(prob(50)) // 50% chance to choose the found bit or the other one + return setbit + else + return mask & (~setbit) + +//a three-way junction that sorts objects +/obj/structure/disposalpipe/sortjunction + + icon_state = "pipe-j1s" + var/sortType = 0 //Look at the list called TAGGERLOCATIONS in /code/_globalvars/lists/flavor_misc.dm + var/posdir = 0 + var/negdir = 0 + var/sortdir = 0 + +/obj/structure/disposalpipe/sortjunction/proc/updatedesc() + desc = "An underfloor disposal pipe with a package sorting mechanism." + if(sortType>0) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[sortType]) + desc += "\nIt's tagged with [tag]" + +/obj/structure/disposalpipe/sortjunction/proc/updatedir() + posdir = dir + negdir = turn(posdir, 180) + + if(icon_state == "pipe-j1s") + sortdir = turn(posdir, -90) + else + icon_state = "pipe-j2s" + sortdir = turn(posdir, 90) + + dpdir = sortdir | posdir | negdir + +/obj/structure/disposalpipe/sortjunction/New() + ..() + updatedir() + updatedesc() + update() + return + +/obj/structure/disposalpipe/sortjunction/attackby(var/obj/item/I, var/mob/user, params) + if(..()) + return + + if(istype(I, /obj/item/destTagger)) + var/obj/item/destTagger/O = I + + if(O.currTag > 0)// Tag set + sortType = O.currTag + name = GLOB.TAGGERLOCATIONS[O.currTag] + playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) + to_chat(user, "Changed filter to [tag]") + updatedesc() + + + // next direction to move + // if coming in from negdir, then next is primary dir or sortdir + // if coming in from posdir, then flip around and go back to posdir + // if coming in from sortdir, go to posdir + +/obj/structure/disposalpipe/sortjunction/nextdir(var/fromdir, var/sortTag) + //var/flipdir = turn(fromdir, 180) + if(fromdir != sortdir) // probably came from the negdir + + if(src.sortType == sortTag) //if destination matches filtered type... + return sortdir // exit through sortdirection + else + return posdir + else // came from sortdir + // so go with the flow to positive direction + return posdir + +/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir, H.destinationTag) + H.dir = nextdir + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + H.loc = P + else // if wasn't a pipe, then set loc to turf + H.loc = T + return null + + return P + + +//a three-way junction that sorts objects destined for the mail office mail table (tomail = 1) +/obj/structure/disposalpipe/wrapsortjunction + desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects." + icon_state = "pipe-j1s" + var/posdir = 0 + var/negdir = 0 + var/sortdir = 0 + +/obj/structure/disposalpipe/wrapsortjunction/New() + ..() + posdir = dir + if(icon_state == "pipe-j1s") + sortdir = turn(posdir, -90) + negdir = turn(posdir, 180) + else + icon_state = "pipe-j2s" + sortdir = turn(posdir, 90) + negdir = turn(posdir, 180) + dpdir = sortdir | posdir | negdir + + update() + return + + + // next direction to move + // if coming in from negdir, then next is primary dir or sortdir + // if coming in from posdir, then flip around and go back to posdir + // if coming in from sortdir, go to posdir + +/obj/structure/disposalpipe/wrapsortjunction/nextdir(var/fromdir, var/istomail) + //var/flipdir = turn(fromdir, 180) + if(fromdir != sortdir) // probably came from the negdir + if(istomail) //if destination matches filtered type... + return sortdir // exit through sortdirection + else + return posdir + else // came from sortdir + return posdir // so go with the flow to positive direction + +/obj/structure/disposalpipe/wrapsortjunction/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir, H.tomail) + H.dir = nextdir + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.loc = P + else // if wasn't a pipe, then set loc to turf + H.loc = T + return null + + return P + +//a trunk joining to a disposal bin or outlet on the same turf +/obj/structure/disposalpipe/trunk + icon_state = "pipe-t" + var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet + +/obj/structure/disposalpipe/trunk/New() + ..() + dpdir = dir + spawn(1) + getlinked() + + update() + return + +/obj/structure/disposalpipe/trunk/Destroy() + if(istype(linked, /obj/structure/disposaloutlet)) + var/obj/structure/disposaloutlet/O = linked + O.expel(animation = 0) + else if(istype(linked, /obj/machinery/disposal)) + var/obj/machinery/disposal/D = linked + if(D.trunk == src) + D.go_out() + D.trunk = null + remove_trunk_links() + return ..() + +/obj/structure/disposalpipe/trunk/proc/getlinked() + var/obj/machinery/disposal/D = locate() in src.loc + if(D) + nicely_link_to_other_stuff(D) + return + var/obj/structure/disposaloutlet/O = locate() in src.loc + if(O) + nicely_link_to_other_stuff(O) + +/obj/structure/disposalpipe/trunk/proc/remove_trunk_links() //disposals is well-coded + if(!linked) + return + else if(istype(linked, /obj/machinery/disposal)) //jk lol + var/obj/machinery/disposal/D = linked + D.trunk = null + else if(istype(linked, /obj/structure/disposaloutlet)) //God fucking damn it + var/obj/structure/disposaloutlet/D = linked + D.linkedtrunk = null + linked = null + +/obj/structure/disposalpipe/trunk/proc/nicely_link_to_other_stuff(obj/O) + remove_trunk_links() //Breaks the connections between this trunk and the linked machinery so we don't get sent to nullspace or some shit like that + if(istype(O, /obj/machinery/disposal)) + var/obj/machinery/disposal/D = O + linked = D + D.trunk = src + else if(istype(O, /obj/structure/disposaloutlet)) + var/obj/structure/disposaloutlet/D = O + linked = D + D.linkedtrunk = src + + // Override attackby so we disallow trunkremoval when somethings ontop +/obj/structure/disposalpipe/trunk/attackby(var/obj/item/I, var/mob/user, params) + + //Disposal bins or chutes + //Disposal constructors + var/obj/structure/disposalconstruct/C = locate() in src.loc + if(C && C.anchored) + return + + var/turf/T = src.loc + if(T.intact) + return // prevent interaction with T-scanner revealed pipes + src.add_fingerprint(user) + + // would transfer to next pipe segment, but we are in a trunk + // if not entering from disposal bin, + // transfer to linked object (outlet or bin) + +/obj/structure/disposalpipe/trunk/transfer(obj/structure/disposalholder/H) + if(!H) + return + if(H.dir == DOWN) // we just entered from a disposer + return ..() // so do base transfer proc + // otherwise, go to the linked object + if(!linked) + expel(H, loc, FALSE) // expel at turf + else if(istype(linked, /obj/structure/disposaloutlet)) + var/obj/structure/disposaloutlet/DO = linked + for(var/atom/movable/AM in H) + AM.forceMove(DO) + qdel(H) + H.vent_gas(loc) + DO.expel() + else if(istype(linked, /obj/machinery/disposal)) + var/obj/machinery/disposal/D = linked + H.forceMove(D) + D.expel(H) // expel at disposal + else //just in case + expel(H, loc, FALSE) + // nextdir + +/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) + if(fromdir == DOWN) + return dir + else + return 0 + +// a broken pipe +/obj/structure/disposalpipe/broken + icon_state = "pipe-b" + dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes + // i.e. will be treated as an empty turf + desc = "A broken piece of disposal pipe." + +/obj/structure/disposalpipe/broken/New() + ..() + update() + return + +/obj/structure/disposalpipe/broken/welder_act(mob/user, obj/item/I) + if(I.use_tool(src, user, 0, volume = I.tool_volume)) + to_chat(user, "You remove [src]!") + I.play_tool_sound(src, I.tool_volume) + qdel(src) + return TRUE + +// the disposal outlet machine + +/obj/structure/disposaloutlet + name = "disposal outlet" + desc = "An outlet for the pneumatic disposal system." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "outlet" + density = 1 + anchored = 1 + var/active = 0 + var/turf/target // this will be where the output objects are 'thrown' to. + var/obj/structure/disposalpipe/trunk/linkedtrunk + var/mode = 0 + +/obj/structure/disposaloutlet/New() + ..() + spawn(1) + target = get_ranged_target_turf(src, dir, 10) + var/obj/structure/disposalpipe/trunk/T = locate() in loc + if(T) + T.nicely_link_to_other_stuff(src) + +/obj/structure/disposaloutlet/Destroy() + if(linkedtrunk) + linkedtrunk.remove_trunk_links() + expel(FALSE) + return ..() + + +// expel the contents of the outlet +/obj/structure/disposaloutlet/proc/expel(animation = TRUE) + if(animation) + flick("outlet-open", src) + playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) + sleep(20) //wait until correct animation frame + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + for(var/atom/movable/AM in contents) + AM.forceMove(loc) + AM.pipe_eject(dir) + if(istype(AM,/mob/living/silicon/robot/drone) || istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z + return + spawn(5) + if(QDELETED(AM)) + return + AM.throw_at(target, 3, 1) + + +/obj/structure/disposaloutlet/attackby(var/obj/item/I, var/mob/user, params) + if(!I || !user) + return + src.add_fingerprint(user) + if(istype(I, /obj/item/screwdriver)) + if(mode==0) + mode=1 + playsound(src.loc, I.usesound, 50, 1) + to_chat(user, "You remove the screws around the power connection.") + return + else if(mode==1) + mode=0 + playsound(src.loc, I.usesound, 50, 1) + to_chat(user, "You attach the screws around the power connection.") + return + +/obj/structure/disposaloutlet/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (src.loc) + C.ptype = PIPE_DISPOSALS_OUTLET + C.update() + C.anchored = TRUE + C.density = TRUE + transfer_fingerprints_to(C) + qdel(src) + +//When the disposalsoutlet is forcefully moved. Due to meteorshot or the recall item spell for instance +/obj/structure/disposaloutlet/Moved(atom/OldLoc, Dir) + . = ..() + var/turf/T = OldLoc + if(T.intact) + var/turf/simulated/floor/F = T + F.remove_tile(null,TRUE,TRUE) + T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") + if(linkedtrunk) + linkedtrunk.remove_trunk_links() + var/obj/structure/disposalconstruct/C = new (loc) + transfer_fingerprints_to(C) + C.ptype = PIPE_DISPOSALS_OUTLET + C.update() + C.anchored = 0 + C.density = 1 + qdel(src) + +// called when movable is expelled from a disposal pipe or outlet +// by default does nothing, override for special behaviour + +/atom/movable/proc/pipe_eject(var/direction) + return + +// check if mob has client, if so restore client view on eject +/mob/pipe_eject(var/direction) + reset_perspective(null) + +/obj/effect/decal/cleanable/blood/gibs/pipe_eject(var/direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = GLOB.alldirs.Copy() + + src.streak(dirs) + +/obj/effect/decal/cleanable/blood/gibs/robot/gib/pipe_eject(var/direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = GLOB.alldirs.Copy() + + src.streak(dirs) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index 95e076d26705..0fdca68811ca 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -1,444 +1,445 @@ -/obj/structure/bigDelivery - name = "large parcel" - desc = "A big wrapped package." - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycloset" - density = 1 - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - var/obj/wrapped = null - var/init_welded = 0 - var/giftwrapped = 0 - var/sortTag = 0 - -/obj/structure/bigDelivery/Destroy() - var/turf/T = get_turf(src) - for(var/atom/movable/AM in contents) - AM.forceMove(T) - return ..() - -/obj/structure/bigDelivery/ex_act(severity) - for(var/atom/movable/AM in contents) - AM.ex_act() - CHECK_TICK - ..() - -/obj/structure/bigDelivery/attack_hand(mob/user as mob) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) - if(wrapped) - wrapped.loc = get_turf(src) - if(istype(wrapped, /obj/structure/closet)) - var/obj/structure/closet/O = wrapped - O.welded = init_welded - var/turf/T = get_turf(src) - for(var/atom/movable/AM in src) - AM.loc = T - - qdel(src) - -/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - - if(sortTag != O.currTag) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) - to_chat(user, "*[tag]*") - sortTag = O.currTag - playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) - - else if(istype(W, /obj/item/shippingPackage)) - var/obj/item/shippingPackage/sp = W - if(sp.sealed) - return - else - sortTag = sp.sortTag - to_chat(user, "You rip the label off the shipping package and affix it to [src].") - qdel(sp) - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - - else if(istype(W, /obj/item/pen)) - var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - user.visible_message("[user] labels [src] as [str].") - name = "[name] ([str])" - - else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) - var/obj/item/stack/wrapping_paper/WP = W - if(WP.use(3)) - user.visible_message("[user] wraps the package in festive paper!") - giftwrapped = 1 - if(istype(wrapped, /obj/structure/closet/crate)) - icon_state = "giftcrate" - else - icon_state = "giftcloset" - if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube - new /obj/item/c_tube( get_turf(user) ) - else - to_chat(user, "You need more paper.") - else - return ..() - -/obj/item/smallDelivery - name = "small parcel" - desc = "A small wrapped package." - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycrateSmall" - item_state = "deliverypackage" - var/obj/item/wrapped = null - var/giftwrapped = 0 - var/sortTag = 0 - -/obj/item/smallDelivery/ex_act(severity) - for(var/atom/movable/AM in contents) - AM.ex_act() - CHECK_TICK - ..() - -/obj/item/smallDelivery/attack_self(mob/user as mob) - if(wrapped && wrapped.loc) //sometimes items can disappear. For example, bombs. --rastaf0 - wrapped.loc = user.loc - if(ishuman(user)) - user.put_in_hands(wrapped) - else - wrapped.loc = get_turf(src) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) - qdel(src) - -/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - - if(sortTag != O.currTag) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) - to_chat(user, "*[tag]*") - sortTag = O.currTag - playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) - - else if(istype(W, /obj/item/shippingPackage)) - var/obj/item/shippingPackage/sp = W - if(sp.sealed) - return - else - sortTag = sp.sortTag - to_chat(user, "You rip the label off the shipping package and affix it to [src].") - qdel(sp) - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - - else if(istype(W, /obj/item/pen)) - var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - user.visible_message("[user] labels [src] as [str].") - name = "[name] ([str])" - - else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) - var/obj/item/stack/wrapping_paper/WP = W - if(WP.use(1)) - icon_state = "giftcrate[wrapped.w_class]" - giftwrapped = 1 - user.visible_message("[user] wraps the package in festive paper!") - if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube - new /obj/item/c_tube( get_turf(user) ) - else - to_chat(user, "You need more paper.") - else - return ..() - -/obj/item/stack/packageWrap - name = "package wrapper" - icon = 'icons/obj/items.dmi' - icon_state = "deliveryPaper" - singular_name = "package wrapper" - flags = NOBLUDGEON - amount = 25 - max_amount = 25 - resistance_flags = FLAMMABLE - -/obj/item/stack/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity) - if(!proximity) return - if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete - return - if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ - || istype(target, /obj/item/evidencebag) || istype(target, /obj/structure/closet/body_bag)) - return - if(target.anchored) - return - if(target in user) - return - - if(istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box) && !istype(target, /obj/item/shippingPackage))) - var/obj/item/O = target - if(use(1)) - var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! - if(!istype(O.loc, /turf)) - if(user.client) - user.client.screen -= O - P.wrapped = O - O.loc = P - var/i = round(O.w_class) - if(i in list(1,2,3,4,5)) - P.icon_state = "deliverycrate[i]" - P.w_class = i - P.add_fingerprint(usr) - O.add_fingerprint(usr) - add_fingerprint(usr) - else - return - else if(istype(target, /obj/structure/closet/crate)) - var/obj/structure/closet/crate/O = target - if(O.opened) - return - if(amount >= 3 && do_after_once(user, 15, target = target)) - if(O.opened || !use(3)) - return - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.icon_state = "deliverycrate" - P.wrapped = O - O.loc = P - else - to_chat(user, "You need more paper.") - return - else if(istype (target, /obj/structure/closet)) - var/obj/structure/closet/O = target - if(O.opened) - return - if(amount >= 3 && do_after_once(user, 15, target = target)) - if(O.opened || !use(3)) - return - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.wrapped = O - P.init_welded = O.welded - O.welded = 1 - O.loc = P - else - to_chat(user, "You need more paper.") - return - else - to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery.") - return - - user.visible_message("[user] wraps [target].") - user.create_attack_log("Has used [name] on [target]") - - if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube - new /obj/item/c_tube( get_turf(user) ) - return - -/obj/item/destTagger - name = "destination tagger" - desc = "Used to set the destination of properly wrapped packages." - icon = 'icons/obj/device.dmi' - icon_state = "dest_tagger" - var/currTag = 0 - //The whole system for the sorttype var is determined based on the order of this list, - //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude - - w_class = WEIGHT_CLASS_TINY - item_state = "electronic" - flags = CONDUCT - slot_flags = SLOT_BELT - -/obj/item/destTagger/proc/openwindow(mob/user as mob) - var/dat = "

    TagMaster 2.2

    " - - dat += "
    NameFrom DepartmentTo DepartmentSent AtSent ByView
    [F.name][F.from_department]
    " - for(var/i = 1, i <= GLOB.TAGGERLOCATIONS.len, i++) - dat += "" - - if(i%4==0) - dat += "" - - dat += "
    [GLOB.TAGGERLOCATIONS[i]]

    Current Selection: [currTag ? GLOB.TAGGERLOCATIONS[currTag] : "None"]
    " - - user << browse(dat, "window=destTagScreen;size=450x350") - onclose(user, "destTagScreen") - -/obj/item/destTagger/attack_self(mob/user as mob) - openwindow(user) - return - -/obj/item/destTagger/Topic(href, href_list) - src.add_fingerprint(usr) - if(href_list["nextTag"]) - var/n = text2num(href_list["nextTag"]) - src.currTag = n - openwindow(usr) - -/obj/machinery/disposal/deliveryChute - name = "Delivery chute" - desc = "A chute for big and small packages alike!" - density = 1 - icon_state = "intake" - required_mode_to_deconstruct = 1 - deconstructs_to = PIPE_DISPOSALS_CHUTE - var/can_deconstruct = FALSE - -/obj/machinery/disposal/deliveryChute/New() - ..() - spawn(5) - trunk = locate() in src.loc - if(trunk) - trunk.linked = src // link the pipe trunk to self - -/obj/machinery/disposal/deliveryChute/interact() - return - -/obj/machinery/disposal/deliveryChute/update() - return - -/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute - if(istype(AM, /obj/item/projectile)) return - switch(dir) - if(NORTH) - if(AM.loc.y != src.loc.y+1) return - if(EAST) - if(AM.loc.x != src.loc.x+1) return - if(SOUTH) - if(AM.loc.y != src.loc.y-1) return - if(WEST) - if(AM.loc.x != src.loc.x-1) return - - if(istype(AM, /obj)) - var/obj/O = AM - O.loc = src - else if(istype(AM, /mob)) - var/mob/M = AM - M.loc = src - src.flush() - -/obj/machinery/disposal/deliveryChute/flush() - flushing = 1 - flick("intake-closing", src) - var/deliveryCheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - for(var/obj/structure/bigDelivery/O in src) - deliveryCheck = 1 - if(O.sortTag == 0) - O.sortTag = 1 - for(var/obj/item/smallDelivery/O in src) - deliveryCheck = 1 - if(O.sortTag == 0) - O.sortTag = 1 - for(var/obj/item/shippingPackage/O in src) - deliveryCheck = 1 - if(!O.sealed || O.sortTag == 0) //unsealed or untagged shipping packages will default to disposals - O.sortTag = 1 - if(deliveryCheck == 0) - H.destinationTag = 1 - - sleep(10) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - sleep(5) // wait for animation to finish - - H.init(src) // copy the contents of disposer to holder - air_contents = new() // The holder just took our gas; replace it - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - -/obj/machinery/disposal/deliveryChute/screwdriver_act(mob/user, obj/item/I) - . = TRUE - if(!I.use_tool(src, user, 0, volume = I.tool_volume)) - return - can_deconstruct = !can_deconstruct - to_chat(user, "You [can_deconstruct ? "unfasten": "fasten"] the screws around the power connection.") - -/obj/machinery/disposal/deliveryChute/welder_act(mob/user, obj/item/I) - . = TRUE - if(!can_deconstruct) - return - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - if(!I.tool_use_check(user, 0)) - return - WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE - if(I.use_tool(src, user, 20, volume = I.tool_volume)) - WELDER_FLOOR_SLICE_SUCCESS_MESSAGE - var/obj/structure/disposalconstruct/C = new (loc) - C.ptype = deconstructs_to - C.update() - C.anchored = TRUE - C.density = TRUE - qdel(src) - -/obj/item/shippingPackage - name = "Shipping package" - desc = "A pre-labeled package for shipping an item to coworkers." - icon = 'icons/obj/storage.dmi' - icon_state = "shippack" - var/obj/item/wrapped = null - var/sortTag = 0 - var/sealed = 0 - -/obj/item/shippingPackage/attackby(obj/item/O, mob/user, params) - if(sealed) - if(istype(O, /obj/item/pen)) - var/str = copytext(sanitize(input(user,"Intended recipient?","Address","")),1,MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - user.visible_message("[user] addresses [src] to [str].") - name = "Shipping package (RE: [str])" - return - if(wrapped) - to_chat(user, "[src] already contains \a [wrapped].") - return - if(istype(O, /obj/item) && !istype(O, /obj/item/storage) && !istype(O, /obj/item/shippingPackage)) - if(!user.canUnEquip(O)) - to_chat(user, "[O] is stuck to your hand, you cannot put it in [src]!") - return - if(O.w_class > 3) - to_chat(user, "[O] is too large to fit in [src].") - else - wrapped = O - user.unEquip(O) - O.forceMove(src) - O.add_fingerprint(usr) - add_fingerprint(usr) - to_chat(user, "You put [O] in [src].") - -/obj/item/shippingPackage/attack_self(mob/user) - if(sealed) - to_chat(user, "You tear open [src], dropping the contents onto the floor.") - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - user.unEquip(src) - wrapped.forceMove(get_turf(user)) - wrapped = null - qdel(src) - else if(wrapped) - switch(alert("Select an action:",, "Remove Object", "Seal Package", "Cancel")) - if("Remove Object") - to_chat(user, "You shake out [src]'s contents onto the floor.") - wrapped.forceMove(get_turf(user)) - wrapped = null - if("Seal Package") - to_chat(user, "You seal [src], preparing it for delivery.") - icon_state = "shippack_sealed" - sealed = 1 - update_desc() - else - if(alert("Do you want to tear up the package?",, "Yes", "No") == "Yes") - to_chat(user, "You shred [src].") - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - user.unEquip(src) - qdel(src) - -/obj/item/shippingPackage/proc/update_desc() - desc = "A pre-labeled package for shipping an item to coworkers." - if(sortTag) - desc += " The label says \"Deliver to [GLOB.TAGGERLOCATIONS[sortTag]]\"." - if(!sealed) - desc += " The package is not sealed." - -/obj/item/shippingPackage/Destroy() - QDEL_NULL(wrapped) - return ..() +/obj/structure/bigDelivery + name = "large parcel" + desc = "A big wrapped package." + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycloset" + density = 1 + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + var/obj/wrapped = null + var/init_welded = 0 + var/giftwrapped = 0 + var/sortTag = 0 + +/obj/structure/bigDelivery/Destroy() + var/turf/T = get_turf(src) + for(var/atom/movable/AM in contents) + AM.forceMove(T) + return ..() + +/obj/structure/bigDelivery/ex_act(severity) + for(var/atom/movable/AM in contents) + AM.ex_act() + CHECK_TICK + ..() + +/obj/structure/bigDelivery/attack_hand(mob/user as mob) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + if(wrapped) + wrapped.loc = get_turf(src) + if(istype(wrapped, /obj/structure/closet)) + var/obj/structure/closet/O = wrapped + O.welded = init_welded + var/turf/T = get_turf(src) + for(var/atom/movable/AM in src) + AM.loc = T + + qdel(src) + +/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + + if(sortTag != O.currTag) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) + to_chat(user, "*[tag]*") + sortTag = O.currTag + playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) + + else if(istype(W, /obj/item/shippingPackage)) + var/obj/item/shippingPackage/sp = W + if(sp.sealed) + return + else + sortTag = sp.sortTag + to_chat(user, "You rip the label off the shipping package and affix it to [src].") + qdel(sp) + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + + else if(istype(W, /obj/item/pen)) + var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + user.visible_message("[user] labels [src] as [str].") + name = "[name] ([str])" + + else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) + var/obj/item/stack/wrapping_paper/WP = W + if(WP.use(3)) + user.visible_message("[user] wraps the package in festive paper!") + giftwrapped = 1 + if(istype(wrapped, /obj/structure/closet/crate)) + icon_state = "giftcrate" + else + icon_state = "giftcloset" + if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube + new /obj/item/c_tube( get_turf(user) ) + else + to_chat(user, "You need more paper.") + else + return ..() + +/obj/item/smallDelivery + name = "small parcel" + desc = "A small wrapped package." + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycrateSmall" + item_state = "deliverypackage" + var/obj/item/wrapped = null + var/giftwrapped = 0 + var/sortTag = 0 + +/obj/item/smallDelivery/ex_act(severity) + for(var/atom/movable/AM in contents) + AM.ex_act() + CHECK_TICK + ..() + +/obj/item/smallDelivery/attack_self(mob/user as mob) + if(wrapped && wrapped.loc) //sometimes items can disappear. For example, bombs. --rastaf0 + wrapped.loc = user.loc + if(ishuman(user)) + user.put_in_hands(wrapped) + else + wrapped.loc = get_turf(src) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + qdel(src) + +/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + + if(sortTag != O.currTag) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) + to_chat(user, "*[tag]*") + sortTag = O.currTag + playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) + + else if(istype(W, /obj/item/shippingPackage)) + var/obj/item/shippingPackage/sp = W + if(sp.sealed) + return + else + sortTag = sp.sortTag + to_chat(user, "You rip the label off the shipping package and affix it to [src].") + qdel(sp) + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + + else if(istype(W, /obj/item/pen)) + var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + user.visible_message("[user] labels [src] as [str].") + name = "[name] ([str])" + + else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) + var/obj/item/stack/wrapping_paper/WP = W + if(WP.use(1)) + icon_state = "giftcrate[wrapped.w_class]" + giftwrapped = 1 + user.visible_message("[user] wraps the package in festive paper!") + if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube + new /obj/item/c_tube( get_turf(user) ) + else + to_chat(user, "You need more paper.") + else + return ..() + +/obj/item/stack/packageWrap + name = "package wrapper" + icon = 'icons/obj/items.dmi' + icon_state = "deliveryPaper" + singular_name = "package wrapper" + flags = NOBLUDGEON + amount = 25 + max_amount = 25 + resistance_flags = FLAMMABLE + +/obj/item/stack/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity) + if(!proximity) return + if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete + return + if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ + || istype(target, /obj/item/evidencebag) || istype(target, /obj/structure/closet/body_bag)) + return + if(target.anchored) + return + if(target in user) + return + + if(istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box) && !istype(target, /obj/item/shippingPackage))) + var/obj/item/O = target + if(use(1)) + var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! + if(!istype(O.loc, /turf)) + if(user.client) + user.client.screen -= O + P.wrapped = O + O.loc = P + var/i = round(O.w_class) + if(i in list(1,2,3,4,5)) + P.icon_state = "deliverycrate[i]" + P.w_class = i + P.add_fingerprint(usr) + O.add_fingerprint(usr) + add_fingerprint(usr) + else + return + else if(istype(target, /obj/structure/closet/crate)) + var/obj/structure/closet/crate/O = target + if(O.opened) + return + if(amount >= 3 && do_after_once(user, 15, target = target)) + if(O.opened || !use(3)) + return + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.icon_state = "deliverycrate" + P.wrapped = O + O.loc = P + else + to_chat(user, "You need more paper.") + return + else if(istype (target, /obj/structure/closet)) + var/obj/structure/closet/O = target + if(O.opened) + return + if(amount >= 3 && do_after_once(user, 15, target = target)) + if(O.opened || !use(3)) + return + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.wrapped = O + P.init_welded = O.welded + O.welded = 1 + O.loc = P + else + to_chat(user, "You need more paper.") + return + else + to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery.") + return + + user.visible_message("[user] wraps [target].") + user.create_attack_log("Has used [name] on [target]") + add_attack_logs(user, target, "used [name]", ATKLOG_ALL) + + if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube + new /obj/item/c_tube( get_turf(user) ) + return + +/obj/item/destTagger + name = "destination tagger" + desc = "Used to set the destination of properly wrapped packages." + icon = 'icons/obj/device.dmi' + icon_state = "dest_tagger" + var/currTag = 0 + //The whole system for the sorttype var is determined based on the order of this list, + //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude + + w_class = WEIGHT_CLASS_TINY + item_state = "electronic" + flags = CONDUCT + slot_flags = SLOT_BELT + +/obj/item/destTagger/proc/openwindow(mob/user as mob) + var/dat = "

    TagMaster 2.2

    " + + dat += "" + for(var/i = 1, i <= GLOB.TAGGERLOCATIONS.len, i++) + dat += "" + + if(i%4==0) + dat += "" + + dat += "
    [GLOB.TAGGERLOCATIONS[i]]

    Current Selection: [currTag ? GLOB.TAGGERLOCATIONS[currTag] : "None"]
    " + + user << browse(dat, "window=destTagScreen;size=450x350") + onclose(user, "destTagScreen") + +/obj/item/destTagger/attack_self(mob/user as mob) + openwindow(user) + return + +/obj/item/destTagger/Topic(href, href_list) + src.add_fingerprint(usr) + if(href_list["nextTag"]) + var/n = text2num(href_list["nextTag"]) + src.currTag = n + openwindow(usr) + +/obj/machinery/disposal/deliveryChute + name = "Delivery chute" + desc = "A chute for big and small packages alike!" + density = 1 + icon_state = "intake" + required_mode_to_deconstruct = 1 + deconstructs_to = PIPE_DISPOSALS_CHUTE + var/can_deconstruct = FALSE + +/obj/machinery/disposal/deliveryChute/New() + ..() + spawn(5) + trunk = locate() in src.loc + if(trunk) + trunk.linked = src // link the pipe trunk to self + +/obj/machinery/disposal/deliveryChute/interact() + return + +/obj/machinery/disposal/deliveryChute/update() + return + +/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute + if(istype(AM, /obj/item/projectile)) return + switch(dir) + if(NORTH) + if(AM.loc.y != src.loc.y+1) return + if(EAST) + if(AM.loc.x != src.loc.x+1) return + if(SOUTH) + if(AM.loc.y != src.loc.y-1) return + if(WEST) + if(AM.loc.x != src.loc.x-1) return + + if(istype(AM, /obj)) + var/obj/O = AM + O.loc = src + else if(istype(AM, /mob)) + var/mob/M = AM + M.loc = src + src.flush() + +/obj/machinery/disposal/deliveryChute/flush() + flushing = 1 + flick("intake-closing", src) + var/deliveryCheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + for(var/obj/structure/bigDelivery/O in src) + deliveryCheck = 1 + if(O.sortTag == 0) + O.sortTag = 1 + for(var/obj/item/smallDelivery/O in src) + deliveryCheck = 1 + if(O.sortTag == 0) + O.sortTag = 1 + for(var/obj/item/shippingPackage/O in src) + deliveryCheck = 1 + if(!O.sealed || O.sortTag == 0) //unsealed or untagged shipping packages will default to disposals + O.sortTag = 1 + if(deliveryCheck == 0) + H.destinationTag = 1 + + sleep(10) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + sleep(5) // wait for animation to finish + + H.init(src) // copy the contents of disposer to holder + air_contents = new() // The holder just took our gas; replace it + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + +/obj/machinery/disposal/deliveryChute/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + can_deconstruct = !can_deconstruct + to_chat(user, "You [can_deconstruct ? "unfasten": "fasten"] the screws around the power connection.") + +/obj/machinery/disposal/deliveryChute/welder_act(mob/user, obj/item/I) + . = TRUE + if(!can_deconstruct) + return + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (loc) + C.ptype = deconstructs_to + C.update() + C.anchored = TRUE + C.density = TRUE + qdel(src) + +/obj/item/shippingPackage + name = "Shipping package" + desc = "A pre-labeled package for shipping an item to coworkers." + icon = 'icons/obj/storage.dmi' + icon_state = "shippack" + var/obj/item/wrapped = null + var/sortTag = 0 + var/sealed = 0 + +/obj/item/shippingPackage/attackby(obj/item/O, mob/user, params) + if(sealed) + if(istype(O, /obj/item/pen)) + var/str = copytext(sanitize(input(user,"Intended recipient?","Address","")),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + user.visible_message("[user] addresses [src] to [str].") + name = "Shipping package (RE: [str])" + return + if(wrapped) + to_chat(user, "[src] already contains \a [wrapped].") + return + if(istype(O, /obj/item) && !istype(O, /obj/item/storage) && !istype(O, /obj/item/shippingPackage)) + if(!user.canUnEquip(O)) + to_chat(user, "[O] is stuck to your hand, you cannot put it in [src]!") + return + if(O.w_class > 3) + to_chat(user, "[O] is too large to fit in [src].") + else + wrapped = O + user.unEquip(O) + O.forceMove(src) + O.add_fingerprint(usr) + add_fingerprint(usr) + to_chat(user, "You put [O] in [src].") + +/obj/item/shippingPackage/attack_self(mob/user) + if(sealed) + to_chat(user, "You tear open [src], dropping the contents onto the floor.") + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + user.unEquip(src) + wrapped.forceMove(get_turf(user)) + wrapped = null + qdel(src) + else if(wrapped) + switch(alert("Select an action:",, "Remove Object", "Seal Package", "Cancel")) + if("Remove Object") + to_chat(user, "You shake out [src]'s contents onto the floor.") + wrapped.forceMove(get_turf(user)) + wrapped = null + if("Seal Package") + to_chat(user, "You seal [src], preparing it for delivery.") + icon_state = "shippack_sealed" + sealed = 1 + update_desc() + else + if(alert("Do you want to tear up the package?",, "Yes", "No") == "Yes") + to_chat(user, "You shred [src].") + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + user.unEquip(src) + qdel(src) + +/obj/item/shippingPackage/proc/update_desc() + desc = "A pre-labeled package for shipping an item to coworkers." + if(sortTag) + desc += " The label says \"Deliver to [GLOB.TAGGERLOCATIONS[sortTag]]\"." + if(!sealed) + desc += " The package is not sealed." + +/obj/item/shippingPackage/Destroy() + QDEL_NULL(wrapped) + return ..() diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 0332b30a413a..eda48794d86b 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -1,105 +1,105 @@ -/*///////////////Circuit Imprinter (By Darem)//////////////////////// - Used to print new circuit boards (for computers and similar systems) and AI modules. Each circuit board pattern are stored in -a /datum/desgin on the linked R&D console. You can then print them out in a fasion similar to a regular lathe. However, instead of -using metal and glass, it uses glass and reagents (usually sulfuric acis). - -*/ -/obj/machinery/r_n_d/circuit_imprinter - name = "Circuit Imprinter" - desc = "Manufactures circuit boards for the construction of machines." - icon_state = "circuit_imprinter" - container_type = OPENCONTAINER - - var/efficiency_coeff - - var/list/categories = list( - "AI Modules", - "Computer Boards", - "Computer Parts", - "Engineering Machinery", - "Exosuit Modules", - "Hydroponics Machinery", - "Medical Machinery", - "Misc. Machinery", - "Research Machinery", - "Subspace Telecomms", - "Teleportation Machinery" - ) - - reagents = new() - -/obj/machinery/r_n_d/circuit_imprinter/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/circuit_imprinter(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/reagent_containers/glass/beaker(null) - component_parts += new /obj/item/reagent_containers/glass/beaker(null) - RefreshParts() - reagents.my_atom = src - -/obj/machinery/r_n_d/circuit_imprinter/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/circuit_imprinter(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - RefreshParts() - reagents.my_atom = src - -/obj/machinery/r_n_d/circuit_imprinter/RefreshParts() - reagents.maximum_volume = 0 - for(var/obj/item/reagent_containers/glass/G in component_parts) - reagents.maximum_volume += G.volume - G.reagents.trans_to(src, G.reagents.total_volume) - - materials.max_amount = 0 - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - materials.max_amount += M.rating * 75000 - - var/T = 0 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T += M.rating - efficiency_coeff = 2 ** (T - 1) //Only 1 manipulator here, you're making runtimes Razharas - -/obj/machinery/r_n_d/circuit_imprinter/proc/check_mat(datum/design/being_built, var/M) - var/list/all_materials = being_built.reagents_list + being_built.materials - - var/A = materials.amount(M) - if(!A) - A = reagents.get_reagent_amount(M) - - return round(A / max(1, (all_materials[M]/efficiency_coeff))) - -/obj/machinery/r_n_d/circuit_imprinter/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(shocked) - if(shock(user,50)) - return TRUE - if(default_deconstruction_screwdriver(user, "circuit_imprinter_t", "circuit_imprinter", O)) - if(linked_console) - linked_console.linked_imprinter = null - linked_console = null - return - - if(exchange_parts(user, O)) - return - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - for(var/obj/I in component_parts) - if(istype(I, /obj/item/reagent_containers/glass/beaker)) - reagents.trans_to(I, reagents.total_volume) - I.loc = src.loc - materials.retrieve_all() - default_deconstruction_crowbar(user, O) - return - else - to_chat(user, "You can't load the [src.name] while it's opened.") - return - if(O.is_open_container()) - return FALSE - else - return ..() \ No newline at end of file +/*///////////////Circuit Imprinter (By Darem)//////////////////////// + Used to print new circuit boards (for computers and similar systems) and AI modules. Each circuit board pattern are stored in +a /datum/desgin on the linked R&D console. You can then print them out in a fasion similar to a regular lathe. However, instead of +using metal and glass, it uses glass and reagents (usually sulfuric acis). + +*/ +/obj/machinery/r_n_d/circuit_imprinter + name = "Circuit Imprinter" + desc = "Manufactures circuit boards for the construction of machines." + icon_state = "circuit_imprinter" + container_type = OPENCONTAINER + + var/efficiency_coeff + + var/list/categories = list( + "AI Modules", + "Computer Boards", + "Computer Parts", + "Engineering Machinery", + "Exosuit Modules", + "Hydroponics Machinery", + "Medical Machinery", + "Misc. Machinery", + "Research Machinery", + "Subspace Telecomms", + "Teleportation Machinery" + ) + + reagents = new() + +/obj/machinery/r_n_d/circuit_imprinter/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/circuit_imprinter(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/reagent_containers/glass/beaker(null) + component_parts += new /obj/item/reagent_containers/glass/beaker(null) + RefreshParts() + reagents.my_atom = src + +/obj/machinery/r_n_d/circuit_imprinter/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/circuit_imprinter(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + RefreshParts() + reagents.my_atom = src + +/obj/machinery/r_n_d/circuit_imprinter/RefreshParts() + reagents.maximum_volume = 0 + for(var/obj/item/reagent_containers/glass/G in component_parts) + reagents.maximum_volume += G.volume + G.reagents.trans_to(src, G.reagents.total_volume) + + materials.max_amount = 0 + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + materials.max_amount += M.rating * 75000 + + var/T = 0 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + T += M.rating + efficiency_coeff = 2 ** (T - 1) //Only 1 manipulator here, you're making runtimes Razharas + +/obj/machinery/r_n_d/circuit_imprinter/proc/check_mat(datum/design/being_built, var/M) + var/list/all_materials = being_built.reagents_list + being_built.materials + + var/A = materials.amount(M) + if(!A) + A = reagents.get_reagent_amount(M) + + return round(A / max(1, (all_materials[M]/efficiency_coeff))) + +/obj/machinery/r_n_d/circuit_imprinter/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(shocked) + if(shock(user,50)) + return TRUE + if(default_deconstruction_screwdriver(user, "circuit_imprinter_t", "circuit_imprinter", O)) + if(linked_console) + linked_console.linked_imprinter = null + linked_console = null + return + + if(exchange_parts(user, O)) + return + + if(panel_open) + if(istype(O, /obj/item/crowbar)) + for(var/obj/I in component_parts) + if(istype(I, /obj/item/reagent_containers/glass/beaker)) + reagents.trans_to(I, reagents.total_volume) + I.loc = src.loc + materials.retrieve_all() + default_deconstruction_crowbar(user, O) + return + else + to_chat(user, "You can't load the [src.name] while it's opened.") + return + if(O.is_open_container()) + return FALSE + else + return ..() diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index 90a759e3c190..bfe959cb6c39 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -1,47 +1,47 @@ -/*************************************************************** -** Design Datums ** -** All the data for building stuff. ** -***************************************************************/ -/* -For the materials datum, it assumes you need reagents unless specified otherwise. To designate a material that isn't a reagent, -you use one of the material IDs below. These are NOT ids in the usual sense (they aren't defined in the object or part of a datum), -they are simply references used as part of a "has materials?" type proc. They all start with a $ to denote that they aren't reagents. -The currently supporting non-reagent materials: -- MAT_METAL (/obj/item/stack/metal). -- MAT_GLASS (/obj/item/stack/glass). -- MAT_PLASMA (/obj/item/stack/plasma). -- MAT_SILVER (/obj/item/stack/silver). -- MAT_GOLD (/obj/item/stack/gold). -- MAT_URANIUM (/obj/item/stack/uranium). -- MAT_DIAMOND (/obj/item/stack/diamond). -- MAT_BANANIUM (/obj/item/stack/bananium). -- MAT_TRANQUILLITE (/obj/item/stack/tranquillite). -(Insert new ones here) - -Don't add new keyword/IDs if they are made from an existing one (such as rods which are made from metal). Only add raw materials. - -Design Guidlines -- When adding new designs, check rdreadme.dm to see what kind of things have already been made and where new stuff is needed. -- A single sheet of anything is 2000 units of material. Materials besides metal/glass require help from other jobs (mining for -other types of metals and chemistry for reagents). -- Add the AUTOLATHE tag to - - -*/ - -/datum/design //Datum for object designs, used in construction - var/name = "Name" //Name of the created object. - var/desc = "Desc" //Description of the created object. - var/id = "id" //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols - var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements. - var/build_type = null //Flag as to what kind machine the design is built in. See defines. - var/list/materials = list() //List of materials. Format: "id" = amount. - var/construction_time //Amount of time required for building the object - var/build_path = null //The file path of the object that gets created - var/list/make_reagents = list() //Reagents produced. Format: "id" = amount. Currently only supported by the biogenerator. - var/locked = 0 //If true it will spawn inside a lockbox with currently sec access - var/access_requirement = list(ACCESS_ARMORY) //What special access requirements will the lockbox have? Defaults to armory. - var/category = null //Primarily used for Mech Fabricators, but can be used for anything - var/list/reagents_list = list() //List of reagents. Format: "id" = amount. - var/maxstack = 1 - var/lathe_time_factor = 1 //How many times faster than normal is this to build on the protolathe +/*************************************************************** +** Design Datums ** +** All the data for building stuff. ** +***************************************************************/ +/* +For the materials datum, it assumes you need reagents unless specified otherwise. To designate a material that isn't a reagent, +you use one of the material IDs below. These are NOT ids in the usual sense (they aren't defined in the object or part of a datum), +they are simply references used as part of a "has materials?" type proc. They all start with a $ to denote that they aren't reagents. +The currently supporting non-reagent materials: +- MAT_METAL (/obj/item/stack/metal). +- MAT_GLASS (/obj/item/stack/glass). +- MAT_PLASMA (/obj/item/stack/plasma). +- MAT_SILVER (/obj/item/stack/silver). +- MAT_GOLD (/obj/item/stack/gold). +- MAT_URANIUM (/obj/item/stack/uranium). +- MAT_DIAMOND (/obj/item/stack/diamond). +- MAT_BANANIUM (/obj/item/stack/bananium). +- MAT_TRANQUILLITE (/obj/item/stack/tranquillite). +(Insert new ones here) + +Don't add new keyword/IDs if they are made from an existing one (such as rods which are made from metal). Only add raw materials. + +Design Guidlines +- When adding new designs, check rdreadme.dm to see what kind of things have already been made and where new stuff is needed. +- A single sheet of anything is 2000 units of material. Materials besides metal/glass require help from other jobs (mining for +other types of metals and chemistry for reagents). +- Add the AUTOLATHE tag to + + +*/ + +/datum/design //Datum for object designs, used in construction + var/name = "Name" //Name of the created object. + var/desc = "Desc" //Description of the created object. + var/id = "id" //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols + var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements. + var/build_type = null //Flag as to what kind machine the design is built in. See defines. + var/list/materials = list() //List of materials. Format: "id" = amount. + var/construction_time //Amount of time required for building the object + var/build_path = null //The file path of the object that gets created + var/list/make_reagents = list() //Reagents produced. Format: "id" = amount. Currently only supported by the biogenerator. + var/locked = 0 //If true it will spawn inside a lockbox with currently sec access + var/access_requirement = list(ACCESS_ARMORY) //What special access requirements will the lockbox have? Defaults to armory. + var/category = null //Primarily used for Mech Fabricators, but can be used for anything + var/list/reagents_list = list() //List of reagents. Format: "id" = amount. + var/maxstack = 1 + var/lathe_time_factor = 1 //How many times faster than normal is this to build on the protolathe diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm index a0bfdf01c619..b70771cb8d3f 100644 --- a/code/modules/research/designs/biogenerator_designs.dm +++ b/code/modules/research/designs/biogenerator_designs.dm @@ -177,4 +177,4 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 300) build_path = /obj/item/clothing/head/rice_hat - category = list("initial","Leather and Cloth") \ No newline at end of file + category = list("initial","Leather and Cloth") diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm index 3c653107ce68..438a679f97a9 100644 --- a/code/modules/research/designs/computer_part_designs.dm +++ b/code/modules/research/designs/computer_part_designs.dm @@ -248,4 +248,4 @@ build_type = IMPRINTER materials = list(MAT_GLASS = 3200, MAT_GOLD = 1000) build_path = /obj/item/computer_hardware/processor_unit/photonic/small - category = list("Computer Parts") \ No newline at end of file + category = list("Computer Parts") diff --git a/code/modules/research/designs/mining_designs.dm b/code/modules/research/designs/mining_designs.dm index 76439c64987c..1fa33e6d27b2 100644 --- a/code/modules/research/designs/mining_designs.dm +++ b/code/modules/research/designs/mining_designs.dm @@ -1,112 +1,112 @@ -///////////////////////////////////////// -/////////////////Mining////////////////// -///////////////////////////////////////// -/datum/design/drill - name = "Mining Drill" - desc = "Yours is the drill that will pierce through the rock walls." - id = "drill" - req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 6000, MAT_GLASS = 1000) - build_path = /obj/item/pickaxe/drill - category = list("Mining") - -/datum/design/drill_diamond - name = "Diamond Mining Drill" - desc = "Yours is the drill that will pierce the heavens!" - id = "drill_diamond" - req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5) - build_type = PROTOLATHE - materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_DIAMOND = 2000) //Yes, a whole diamond is needed. - build_path = /obj/item/pickaxe/drill/diamonddrill - category = list("Mining") - -/datum/design/plasmacutter - name = "Plasma Cutter" - desc = "You could use it to cut limbs off of xenos! Or, you know, mine stuff." - id = "plasmacutter" - req_tech = list("materials" = 3, "plasmatech" = 3, "magnets" = 2) - build_type = PROTOLATHE - materials = list(MAT_METAL = 1500, MAT_GLASS = 500, MAT_PLASMA = 400) - build_path = /obj/item/gun/energy/plasmacutter - category = list("Mining") - -/datum/design/plasmacutter_adv - name = "Advanced Plasma Cutter" - desc = "It's an advanced plasma cutter, oh my god." - id = "plasmacutter_adv" - req_tech = list("materials" = 4, "plasmatech" = 4, "engineering" = 2, "combat" = 3, "magnets" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_PLASMA = 2000, MAT_GOLD = 500) - build_path = /obj/item/gun/energy/plasmacutter/adv - category = list("Mining") - -/datum/design/jackhammer - name = "Sonic Jackhammer" - desc = "Essentially a handheld planet-cracker. Can drill through walls with ease as well." - id = "jackhammer" - req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 6, "magnets" = 5) - build_type = PROTOLATHE - materials = list(MAT_METAL = 6000, MAT_GLASS = 2000, MAT_SILVER = 2000, MAT_DIAMOND = 6000) - build_path = /obj/item/pickaxe/drill/jackhammer - category = list("Mining") - -/datum/design/superresonator - name = "Upgraded Resonator" - desc = "An upgraded version of the resonator that allows more fields to be active at once." - id = "superresonator" - req_tech = list("materials" = 4, "powerstorage" = 3, "engineering" = 3, "magnets" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 4000, MAT_GLASS = 1500, MAT_SILVER = 1000, MAT_URANIUM = 1000) - build_path = /obj/item/resonator/upgraded - category = list("Mining") - -/datum/design/trigger_guard_mod - name = "Kinetic Accelerator Trigger Guard Mod" - desc = "A device which allows kinetic accelerators to be wielded by any organism." - id = "triggermod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/trigger_guard - category = list("Mining") - -/datum/design/damage_mod - name = "Kinetic Accelerator Damage Mod" - desc = "A device which allows kinetic accelerators to deal more damage." - id = "damagemod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/damage - category = list("Mining", "Cyborg Upgrade Modules") - -/datum/design/cooldown_mod - name = "Kinetic Accelerator Cooldown Mod" - desc = "A device which decreases the cooldown of a Kinetic Accelerator." - id = "cooldownmod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/cooldown - category = list("Mining", "Cyborg Upgrade Modules") - -/datum/design/range_mod - name = "Kinetic Accelerator Range Mod" - desc = "A device which allows kinetic accelerators to fire at a further range." - id = "rangemod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/range - category = list("Mining", "Cyborg Upgrade Modules") - -/datum/design/hyperaccelerator - name = "Kinetic Accelerator Mining AoE Mod" - desc = "A modification kit for Kinetic Accelerators which causes it to fire AoE blasts that destroy rock." - id = "hypermod" - req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 5, "magnets" = 5, "combat" = 4) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 8000, MAT_GLASS = 1500, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_DIAMOND = 2000) - build_path = /obj/item/borg/upgrade/modkit/aoe/turfs - category = list("Mining", "Cyborg Upgrade Modules") \ No newline at end of file +///////////////////////////////////////// +/////////////////Mining////////////////// +///////////////////////////////////////// +/datum/design/drill + name = "Mining Drill" + desc = "Yours is the drill that will pierce through the rock walls." + id = "drill" + req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 6000, MAT_GLASS = 1000) + build_path = /obj/item/pickaxe/drill + category = list("Mining") + +/datum/design/drill_diamond + name = "Diamond Mining Drill" + desc = "Yours is the drill that will pierce the heavens!" + id = "drill_diamond" + req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5) + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_DIAMOND = 2000) //Yes, a whole diamond is needed. + build_path = /obj/item/pickaxe/drill/diamonddrill + category = list("Mining") + +/datum/design/plasmacutter + name = "Plasma Cutter" + desc = "You could use it to cut limbs off of xenos! Or, you know, mine stuff." + id = "plasmacutter" + req_tech = list("materials" = 3, "plasmatech" = 3, "magnets" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 1500, MAT_GLASS = 500, MAT_PLASMA = 400) + build_path = /obj/item/gun/energy/plasmacutter + category = list("Mining") + +/datum/design/plasmacutter_adv + name = "Advanced Plasma Cutter" + desc = "It's an advanced plasma cutter, oh my god." + id = "plasmacutter_adv" + req_tech = list("materials" = 4, "plasmatech" = 4, "engineering" = 2, "combat" = 3, "magnets" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_PLASMA = 2000, MAT_GOLD = 500) + build_path = /obj/item/gun/energy/plasmacutter/adv + category = list("Mining") + +/datum/design/jackhammer + name = "Sonic Jackhammer" + desc = "Essentially a handheld planet-cracker. Can drill through walls with ease as well." + id = "jackhammer" + req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 6, "magnets" = 5) + build_type = PROTOLATHE + materials = list(MAT_METAL = 6000, MAT_GLASS = 2000, MAT_SILVER = 2000, MAT_DIAMOND = 6000) + build_path = /obj/item/pickaxe/drill/jackhammer + category = list("Mining") + +/datum/design/superresonator + name = "Upgraded Resonator" + desc = "An upgraded version of the resonator that allows more fields to be active at once." + id = "superresonator" + req_tech = list("materials" = 4, "powerstorage" = 3, "engineering" = 3, "magnets" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 4000, MAT_GLASS = 1500, MAT_SILVER = 1000, MAT_URANIUM = 1000) + build_path = /obj/item/resonator/upgraded + category = list("Mining") + +/datum/design/trigger_guard_mod + name = "Kinetic Accelerator Trigger Guard Mod" + desc = "A device which allows kinetic accelerators to be wielded by any organism." + id = "triggermod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/trigger_guard + category = list("Mining") + +/datum/design/damage_mod + name = "Kinetic Accelerator Damage Mod" + desc = "A device which allows kinetic accelerators to deal more damage." + id = "damagemod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/damage + category = list("Mining", "Cyborg Upgrade Modules") + +/datum/design/cooldown_mod + name = "Kinetic Accelerator Cooldown Mod" + desc = "A device which decreases the cooldown of a Kinetic Accelerator." + id = "cooldownmod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/cooldown + category = list("Mining", "Cyborg Upgrade Modules") + +/datum/design/range_mod + name = "Kinetic Accelerator Range Mod" + desc = "A device which allows kinetic accelerators to fire at a further range." + id = "rangemod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/range + category = list("Mining", "Cyborg Upgrade Modules") + +/datum/design/hyperaccelerator + name = "Kinetic Accelerator Mining AoE Mod" + desc = "A modification kit for Kinetic Accelerators which causes it to fire AoE blasts that destroy rock." + id = "hypermod" + req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 5, "magnets" = 5, "combat" = 4) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 8000, MAT_GLASS = 1500, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_DIAMOND = 2000) + build_path = /obj/item/borg/upgrade/modkit/aoe/turfs + category = list("Mining", "Cyborg Upgrade Modules") diff --git a/code/modules/research/designs/smelting_designs.dm b/code/modules/research/designs/smelting_designs.dm index a043d90fb19a..0fc368164b58 100644 --- a/code/modules/research/designs/smelting_designs.dm +++ b/code/modules/research/designs/smelting_designs.dm @@ -50,4 +50,4 @@ materials = list(MAT_METAL = 4000, MAT_PLASMA = 4000) build_path = /obj/item/stack/sheet/mineral/abductor category = list("Stock Parts") - lathe_time_factor = 5 \ No newline at end of file + lathe_time_factor = 5 diff --git a/code/modules/research/designs/stock_parts_designs.dm b/code/modules/research/designs/stock_parts_designs.dm index e32b79cc91b0..e27aaf0ee70d 100644 --- a/code/modules/research/designs/stock_parts_designs.dm +++ b/code/modules/research/designs/stock_parts_designs.dm @@ -248,4 +248,4 @@ build_type = PROTOLATHE materials = list(MAT_METAL = 15000, MAT_GLASS = 5000, MAT_SILVER = 2500) //hardcore build_path = /obj/item/storage/part_replacer/bluespace - category = list("Stock Parts") \ No newline at end of file + category = list("Stock Parts") diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index a360e0ee231e..58a8ab212846 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -1,88 +1,88 @@ -/* -Destructive Analyzer - -It is used to destroy hand-held objects and advance technological research. Controls are in the linked R&D console. - -Note: Must be placed within 3 tiles of the R&D Console -*/ -/obj/machinery/r_n_d/destructive_analyzer - name = "Destructive Analyzer" - desc = "Learn science by destroying things!" - icon_state = "d_analyzer" - var/decon_mod = 0 - -/obj/machinery/r_n_d/destructive_analyzer/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/destructive_analyzer(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - RefreshParts() - -/obj/machinery/r_n_d/destructive_analyzer/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/destructive_analyzer(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - RefreshParts() - -/obj/machinery/r_n_d/destructive_analyzer/RefreshParts() - var/T = 0 - for(var/obj/item/stock_parts/S in component_parts) - T += S.rating - decon_mod = T - - -/obj/machinery/r_n_d/destructive_analyzer/proc/ConvertReqString2List(var/list/source_list) - var/list/temp_list = params2list(source_list) - for(var/O in temp_list) - temp_list[O] = text2num(temp_list[O]) - return temp_list - - -/obj/machinery/r_n_d/destructive_analyzer/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(shocked) - if(shock(user,50)) - return TRUE - if(default_deconstruction_screwdriver(user, "d_analyzer_t", "d_analyzer", O)) - if(linked_console) - linked_console.linked_destroy = null - linked_console = null - return - - if(exchange_parts(user, O)) - return - - if(default_deconstruction_crowbar(user, O)) - return - - if(disabled) - return - if(!linked_console) - to_chat(user, "The [src.name] must be linked to an R&D console first!") - return - if(busy) - to_chat(user, "The [src.name] is busy right now.") - return - if(istype(O, /obj/item) && !loaded_item) - if(!O.origin_tech) - to_chat(user, "This doesn't seem to have a tech origin!") - return - var/list/temp_tech = ConvertReqString2List(O.origin_tech) - if(temp_tech.len == 0) - to_chat(user, "You cannot deconstruct this item!") - return - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand, you cannot put it in the [src.name]!") - return - busy = 1 - loaded_item = O - O.loc = src - to_chat(user, "You add the [O.name] to the [src.name]!") - flick("d_analyzer_la", src) - spawn(10) - icon_state = "d_analyzer_l" - busy = 0 \ No newline at end of file +/* +Destructive Analyzer + +It is used to destroy hand-held objects and advance technological research. Controls are in the linked R&D console. + +Note: Must be placed within 3 tiles of the R&D Console +*/ +/obj/machinery/r_n_d/destructive_analyzer + name = "Destructive Analyzer" + desc = "Learn science by destroying things!" + icon_state = "d_analyzer" + var/decon_mod = 0 + +/obj/machinery/r_n_d/destructive_analyzer/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/destructive_analyzer(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + RefreshParts() + +/obj/machinery/r_n_d/destructive_analyzer/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/destructive_analyzer(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + RefreshParts() + +/obj/machinery/r_n_d/destructive_analyzer/RefreshParts() + var/T = 0 + for(var/obj/item/stock_parts/S in component_parts) + T += S.rating + decon_mod = T + + +/obj/machinery/r_n_d/destructive_analyzer/proc/ConvertReqString2List(var/list/source_list) + var/list/temp_list = params2list(source_list) + for(var/O in temp_list) + temp_list[O] = text2num(temp_list[O]) + return temp_list + + +/obj/machinery/r_n_d/destructive_analyzer/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(shocked) + if(shock(user,50)) + return TRUE + if(default_deconstruction_screwdriver(user, "d_analyzer_t", "d_analyzer", O)) + if(linked_console) + linked_console.linked_destroy = null + linked_console = null + return + + if(exchange_parts(user, O)) + return + + if(default_deconstruction_crowbar(user, O)) + return + + if(disabled) + return + if(!linked_console) + to_chat(user, "The [src.name] must be linked to an R&D console first!") + return + if(busy) + to_chat(user, "The [src.name] is busy right now.") + return + if(istype(O, /obj/item) && !loaded_item) + if(!O.origin_tech) + to_chat(user, "This doesn't seem to have a tech origin!") + return + var/list/temp_tech = ConvertReqString2List(O.origin_tech) + if(temp_tech.len == 0) + to_chat(user, "You cannot deconstruct this item!") + return + if(!user.drop_item()) + to_chat(user, "\The [O] is stuck to your hand, you cannot put it in the [src.name]!") + return + busy = 1 + loaded_item = O + O.loc = src + to_chat(user, "You add the [O.name] to the [src.name]!") + flick("d_analyzer_la", src) + spawn(10) + icon_state = "d_analyzer_l" + busy = 0 diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index fa5015987e51..22b18534dbce 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -1,380 +1,380 @@ -var/global/list/obj/machinery/message_server/message_servers = list() - -/datum/data_pda_msg - var/recipient = "Unspecified" //name of the person - var/sender = "Unspecified" //name of the sender - var/message = "Blank" //transferred message - -/datum/data_pda_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "") - - if(param_rec) - recipient = param_rec - if(param_sender) - sender = param_sender - if(param_message) - message = param_message - -/datum/data_rc_msg - var/rec_dpt = "Unspecified" //name of the person - var/send_dpt = "Unspecified" //name of the sender - var/message = "Blank" //transferred message - var/stamp = "Unstamped" - var/id_auth = "Unauthenticated" - var/priority = "Normal" - -/datum/data_rc_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_stamp = "",var/param_id_auth = "",var/param_priority) - if(param_rec) - rec_dpt = param_rec - if(param_sender) - send_dpt = param_sender - if(param_message) - message = param_message - if(param_stamp) - stamp = param_stamp - if(param_id_auth) - id_auth = param_id_auth - if(param_priority) - switch(param_priority) - if(1) - priority = "Normal" - if(2) - priority = "High" - if(3) - priority = "Extreme" - else - priority = "Undetermined" - -/obj/machinery/message_server - icon = 'icons/obj/machines/research.dmi' - icon_state = "server" - name = "Messaging Server" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - - var/list/datum/data_pda_msg/pda_msgs = list() - var/list/datum/data_rc_msg/rc_msgs = list() - var/active = 1 - var/decryptkey = "password" - -/obj/machinery/message_server/New() - message_servers += src - decryptkey = GenerateKey() - send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.") - ..() - return - -/obj/machinery/message_server/Destroy() - message_servers -= src - return ..() - -/obj/machinery/message_server/process() - //if(decryptkey == "password") - // decryptkey = generateKey() - if(active && (stat & (BROKEN|NOPOWER))) - active = 0 - return - if(prob(3)) - playsound(loc, "computer_ambience", 50, 1) - update_icon() - return - -/obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") - pda_msgs += new/datum/data_pda_msg(recipient,sender,message) - -/obj/machinery/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) - rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth) - var/authmsg = "[message]
    " - if(id_auth) - authmsg += "[id_auth]
    " - if(stamp) - authmsg += "[stamp]
    " - for(var/obj/machinery/requests_console/Console in allConsoles) - if(ckey(Console.department) == ckey(recipient)) - if(Console.inoperable()) - Console.message_log += "Message lost due to console failure.
    Please contact [station_name()] system adminsitrator or AI for technical assistance.
    " - continue - if(Console.newmessagepriority < priority) - Console.newmessagepriority = priority - Console.icon_state = "req_comp[priority]" - switch(priority) - if(2) - if(!Console.silent) - playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) - Console.message_log += "High Priority message from [sender]
    [authmsg]" - else - if(!Console.silent) - playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4) - Console.message_log += "Message from [sender]
    [authmsg]" - Console.set_light(2) - -/obj/machinery/message_server/attack_hand(user as mob) -// to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentComm delays.") - to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") - active = !active - update_icon() - - return - -/obj/machinery/message_server/update_icon() - if((stat & (BROKEN|NOPOWER))) - icon_state = "server-nopower" - else if(!active) - icon_state = "server-off" - else - icon_state = "server-on" - - return - - -/datum/feedback_variable - var/variable - var/value - var/details - -/datum/feedback_variable/New(var/param_variable,var/param_value = 0) - variable = param_variable - value = param_value - -/datum/feedback_variable/vv_edit_var(var_name, var_value) - return FALSE // come on guys don't break the stats - -/datum/feedback_variable/proc/inc(var/num = 1) - if(isnum(value)) - value += num - else - value = text2num(value) - if(isnum(value)) - value += num - else - value = num - -/datum/feedback_variable/proc/dec(var/num = 1) - if(isnum(value)) - value -= num - else - value = text2num(value) - if(isnum(value)) - value -= num - else - value = -num - -/datum/feedback_variable/proc/set_value(var/num) - if(isnum(num)) - value = num - -/datum/feedback_variable/proc/get_value() - return value - -/datum/feedback_variable/proc/get_variable() - return variable - -/datum/feedback_variable/proc/set_details(var/text) - if(istext(text)) - details = text - -/datum/feedback_variable/proc/add_details(var/text) - if(istext(text)) - if(!details) - details = text - else - details += " [text]" - -/datum/feedback_variable/proc/get_details() - return details - -/datum/feedback_variable/proc/get_parsed() - return list(variable,value,details) - -var/obj/machinery/blackbox_recorder/blackbox - -//TODO: kill whoever designed this cancer -/obj/machinery/blackbox_recorder - icon = 'icons/obj/stationobjs.dmi' - icon_state = "blackbox" - name = "Blackbox Recorder" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - var/list/messages = list() //Stores messages of non-standard frequencies - var/list/messages_admin = list() - - var/list/msg_common = list() - var/list/msg_science = list() - var/list/msg_command = list() - var/list/msg_medical = list() - var/list/msg_engineering = list() - var/list/msg_security = list() - var/list/msg_deathsquad = list() - var/list/msg_syndicate = list() - var/list/msg_syndteam = list() - var/list/msg_mining = list() - var/list/msg_cargo = list() - var/list/msg_service = list() - - var/list/datum/feedback_variable/feedback = new() - - //Only one can exsist in the world! -/obj/machinery/blackbox_recorder/New() - if(blackbox) - if(istype(blackbox,/obj/machinery/blackbox_recorder)) - qdel(src) - blackbox = src - -/obj/machinery/blackbox_recorder/Destroy() - var/turf/T = locate(1,1,2) - if(T) - blackbox = null - var/obj/machinery/blackbox_recorder/BR = new/obj/machinery/blackbox_recorder(T) - BR.msg_common = msg_common - BR.msg_science = msg_science - BR.msg_command = msg_command - BR.msg_medical = msg_medical - BR.msg_engineering = msg_engineering - BR.msg_security = msg_security - BR.msg_deathsquad = msg_deathsquad - BR.msg_syndicate = msg_syndicate - BR.msg_syndteam = msg_syndteam - BR.msg_mining = msg_mining - BR.msg_cargo = msg_cargo - BR.msg_service = msg_service - BR.feedback = feedback - BR.messages = messages - BR.messages_admin = messages_admin - if(blackbox != BR) - blackbox = BR - return ..() - -/obj/machinery/blackbox_recorder/proc/find_feedback_datum(var/variable) - for(var/datum/feedback_variable/FV in feedback) - if(FV.get_variable() == variable) - return FV - var/datum/feedback_variable/FV = new(variable) - feedback += FV - return FV - -/obj/machinery/blackbox_recorder/proc/get_round_feedback() - return feedback - -/obj/machinery/blackbox_recorder/proc/round_end_data_gathering() - - var/pda_msg_amt = 0 - var/rc_msg_amt = 0 - - for(var/obj/machinery/message_server/MS in GLOB.machines) - if(MS.pda_msgs.len > pda_msg_amt) - pda_msg_amt = MS.pda_msgs.len - if(MS.rc_msgs.len > rc_msg_amt) - rc_msg_amt = MS.rc_msgs.len - - feedback_set_details("radio_usage","") - - feedback_add_details("radio_usage","COM-[msg_common.len]") - feedback_add_details("radio_usage","SCI-[msg_science.len]") - feedback_add_details("radio_usage","HEA-[msg_command.len]") - feedback_add_details("radio_usage","MED-[msg_medical.len]") - feedback_add_details("radio_usage","ENG-[msg_engineering.len]") - feedback_add_details("radio_usage","SEC-[msg_security.len]") - feedback_add_details("radio_usage","DTH-[msg_deathsquad.len]") - feedback_add_details("radio_usage","SYN-[msg_syndicate.len]") - feedback_add_details("radio_usage","SYT-[msg_syndteam.len]") - feedback_add_details("radio_usage","MIN-[msg_mining.len]") - feedback_add_details("radio_usage","CAR-[msg_cargo.len]") - feedback_add_details("radio_usage","SRV-[msg_service.len]") - feedback_add_details("radio_usage","OTH-[messages.len]") - feedback_add_details("radio_usage","PDA-[pda_msg_amt]") - feedback_add_details("radio_usage","RC-[rc_msg_amt]") - - - feedback_set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set. - - -//This proc is only to be called at round end. -/obj/machinery/blackbox_recorder/proc/save_all_data_to_sql() - if(!feedback) return - - round_end_data_gathering() //round_end time logging and some other data processing - establish_db_connection() - if(!dbcon.IsConnected()) return - var/round_id - - var/DBQuery/query = dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM [format_table_name("feedback")]") - query.Execute() - while(query.NextRow()) - round_id = query.item[1] - - if(!isnum(round_id)) - round_id = text2num(round_id) - round_id++ - - for(var/datum/feedback_variable/FV in feedback) - var/sql = "INSERT INTO [format_table_name("feedback")] VALUES (null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() - -/obj/machinery/blackbox_recorder/vv_edit_var(var_name, var_value) - return FALSE // don't fuck with the stupid blackbox shit - - -proc/feedback_set(var/variable,var/value) - if(!blackbox) return - - variable = sanitizeSQL(variable) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.set_value(value) - -proc/feedback_inc(var/variable,var/value) - if(!blackbox) return - - variable = sanitizeSQL(variable) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.inc(value) - -proc/feedback_dec(var/variable,var/value) - if(!blackbox) return - - variable = sanitizeSQL(variable) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.dec(value) - -proc/feedback_set_details(var/variable,var/details) - if(!blackbox) return - - variable = sanitizeSQL(variable) - details = sanitizeSQL(details) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.set_details(details) - -proc/feedback_add_details(var/variable,var/details) - if(!blackbox) return - - variable = sanitizeSQL(variable) - details = sanitizeSQL(details) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.add_details(details) +GLOBAL_LIST_EMPTY(message_servers) + +/datum/data_pda_msg + var/recipient = "Unspecified" //name of the person + var/sender = "Unspecified" //name of the sender + var/message = "Blank" //transferred message + +/datum/data_pda_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "") + + if(param_rec) + recipient = param_rec + if(param_sender) + sender = param_sender + if(param_message) + message = param_message + +/datum/data_rc_msg + var/rec_dpt = "Unspecified" //name of the person + var/send_dpt = "Unspecified" //name of the sender + var/message = "Blank" //transferred message + var/stamp = "Unstamped" + var/id_auth = "Unauthenticated" + var/priority = "Normal" + +/datum/data_rc_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_stamp = "",var/param_id_auth = "",var/param_priority) + if(param_rec) + rec_dpt = param_rec + if(param_sender) + send_dpt = param_sender + if(param_message) + message = param_message + if(param_stamp) + stamp = param_stamp + if(param_id_auth) + id_auth = param_id_auth + if(param_priority) + switch(param_priority) + if(1) + priority = "Normal" + if(2) + priority = "High" + if(3) + priority = "Extreme" + else + priority = "Undetermined" + +/obj/machinery/message_server + icon = 'icons/obj/machines/research.dmi' + icon_state = "server" + name = "Messaging Server" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + + var/list/datum/data_pda_msg/pda_msgs = list() + var/list/datum/data_rc_msg/rc_msgs = list() + var/active = 1 + var/decryptkey = "password" + +/obj/machinery/message_server/New() + GLOB.message_servers += src + decryptkey = GenerateKey() + send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.") + ..() + return + +/obj/machinery/message_server/Destroy() + GLOB.message_servers -= src + return ..() + +/obj/machinery/message_server/process() + //if(decryptkey == "password") + // decryptkey = generateKey() + if(active && (stat & (BROKEN|NOPOWER))) + active = 0 + return + if(prob(3)) + playsound(loc, "computer_ambience", 50, 1) + update_icon() + return + +/obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") + pda_msgs += new/datum/data_pda_msg(recipient,sender,message) + +/obj/machinery/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) + rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth) + var/authmsg = "[message]
    " + if(id_auth) + authmsg += "[id_auth]
    " + if(stamp) + authmsg += "[stamp]
    " + for(var/obj/machinery/requests_console/Console in GLOB.allRequestConsoles) + if(ckey(Console.department) == ckey(recipient)) + if(Console.inoperable()) + Console.message_log += "Message lost due to console failure.
    Please contact [station_name()] system adminsitrator or AI for technical assistance.
    " + continue + if(Console.newmessagepriority < priority) + Console.newmessagepriority = priority + Console.icon_state = "req_comp[priority]" + switch(priority) + if(2) + if(!Console.silent) + playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) + Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) + Console.message_log += "High Priority message from [sender]
    [authmsg]" + else + if(!Console.silent) + playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) + Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4) + Console.message_log += "Message from [sender]
    [authmsg]" + Console.set_light(2) + +/obj/machinery/message_server/attack_hand(user as mob) +// to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentComm delays.") + to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") + active = !active + update_icon() + + return + +/obj/machinery/message_server/update_icon() + if((stat & (BROKEN|NOPOWER))) + icon_state = "server-nopower" + else if(!active) + icon_state = "server-off" + else + icon_state = "server-on" + + return + + +/datum/feedback_variable + var/variable + var/value + var/details + +/datum/feedback_variable/New(var/param_variable,var/param_value = 0) + variable = param_variable + value = param_value + +/datum/feedback_variable/vv_edit_var(var_name, var_value) + return FALSE // come on guys don't break the stats + +/datum/feedback_variable/proc/inc(var/num = 1) + if(isnum(value)) + value += num + else + value = text2num(value) + if(isnum(value)) + value += num + else + value = num + +/datum/feedback_variable/proc/dec(var/num = 1) + if(isnum(value)) + value -= num + else + value = text2num(value) + if(isnum(value)) + value -= num + else + value = -num + +/datum/feedback_variable/proc/set_value(var/num) + if(isnum(num)) + value = num + +/datum/feedback_variable/proc/get_value() + return value + +/datum/feedback_variable/proc/get_variable() + return variable + +/datum/feedback_variable/proc/set_details(var/text) + if(istext(text)) + details = text + +/datum/feedback_variable/proc/add_details(var/text) + if(istext(text)) + if(!details) + details = text + else + details += " [text]" + +/datum/feedback_variable/proc/get_details() + return details + +/datum/feedback_variable/proc/get_parsed() + return list(variable,value,details) + +GLOBAL_DATUM(blackbox, /obj/machinery/blackbox_recorder) + +//TODO: kill whoever designed this cancer +/obj/machinery/blackbox_recorder + icon = 'icons/obj/stationobjs.dmi' + icon_state = "blackbox" + name = "Blackbox Recorder" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + var/list/messages = list() //Stores messages of non-standard frequencies + var/list/messages_admin = list() + + var/list/msg_common = list() + var/list/msg_science = list() + var/list/msg_command = list() + var/list/msg_medical = list() + var/list/msg_engineering = list() + var/list/msg_security = list() + var/list/msg_deathsquad = list() + var/list/msg_syndicate = list() + var/list/msg_syndteam = list() + var/list/msg_mining = list() + var/list/msg_cargo = list() + var/list/msg_service = list() + + var/list/datum/feedback_variable/feedback = new() + + //Only one can exsist in the world! +/obj/machinery/blackbox_recorder/New() + if(GLOB.blackbox) + if(istype(GLOB.blackbox,/obj/machinery/blackbox_recorder)) + qdel(src) + GLOB.blackbox = src + +/obj/machinery/blackbox_recorder/Destroy() + var/turf/T = locate(1,1,2) + if(T) + GLOB.blackbox = null + var/obj/machinery/blackbox_recorder/BR = new/obj/machinery/blackbox_recorder(T) + BR.msg_common = msg_common + BR.msg_science = msg_science + BR.msg_command = msg_command + BR.msg_medical = msg_medical + BR.msg_engineering = msg_engineering + BR.msg_security = msg_security + BR.msg_deathsquad = msg_deathsquad + BR.msg_syndicate = msg_syndicate + BR.msg_syndteam = msg_syndteam + BR.msg_mining = msg_mining + BR.msg_cargo = msg_cargo + BR.msg_service = msg_service + BR.feedback = feedback + BR.messages = messages + BR.messages_admin = messages_admin + if(GLOB.blackbox != BR) + GLOB.blackbox = BR + return ..() + +/obj/machinery/blackbox_recorder/proc/find_feedback_datum(var/variable) + for(var/datum/feedback_variable/FV in feedback) + if(FV.get_variable() == variable) + return FV + var/datum/feedback_variable/FV = new(variable) + feedback += FV + return FV + +/obj/machinery/blackbox_recorder/proc/get_round_feedback() + return feedback + +/obj/machinery/blackbox_recorder/proc/round_end_data_gathering() + + var/pda_msg_amt = 0 + var/rc_msg_amt = 0 + + for(var/obj/machinery/message_server/MS in GLOB.machines) + if(MS.pda_msgs.len > pda_msg_amt) + pda_msg_amt = MS.pda_msgs.len + if(MS.rc_msgs.len > rc_msg_amt) + rc_msg_amt = MS.rc_msgs.len + + feedback_set_details("radio_usage","") + + feedback_add_details("radio_usage","COM-[msg_common.len]") + feedback_add_details("radio_usage","SCI-[msg_science.len]") + feedback_add_details("radio_usage","HEA-[msg_command.len]") + feedback_add_details("radio_usage","MED-[msg_medical.len]") + feedback_add_details("radio_usage","ENG-[msg_engineering.len]") + feedback_add_details("radio_usage","SEC-[msg_security.len]") + feedback_add_details("radio_usage","DTH-[msg_deathsquad.len]") + feedback_add_details("radio_usage","SYN-[msg_syndicate.len]") + feedback_add_details("radio_usage","SYT-[msg_syndteam.len]") + feedback_add_details("radio_usage","MIN-[msg_mining.len]") + feedback_add_details("radio_usage","CAR-[msg_cargo.len]") + feedback_add_details("radio_usage","SRV-[msg_service.len]") + feedback_add_details("radio_usage","OTH-[messages.len]") + feedback_add_details("radio_usage","PDA-[pda_msg_amt]") + feedback_add_details("radio_usage","RC-[rc_msg_amt]") + + + feedback_set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set. + + +//This proc is only to be called at round end. +/obj/machinery/blackbox_recorder/proc/save_all_data_to_sql() + if(!feedback) return + + round_end_data_gathering() //round_end time logging and some other data processing + establish_db_connection() + if(!GLOB.dbcon.IsConnected()) return + var/round_id + + var/DBQuery/query = GLOB.dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM [format_table_name("feedback")]") + query.Execute() + while(query.NextRow()) + round_id = query.item[1] + + if(!isnum(round_id)) + round_id = text2num(round_id) + round_id++ + + for(var/datum/feedback_variable/FV in feedback) + var/sql = "INSERT INTO [format_table_name("feedback")] VALUES (null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")" + var/DBQuery/query_insert = GLOB.dbcon.NewQuery(sql) + query_insert.Execute() + +/obj/machinery/blackbox_recorder/vv_edit_var(var_name, var_value) + return FALSE // don't fuck with the stupid blackbox shit + + +proc/feedback_set(var/variable,var/value) + if(!GLOB.blackbox) return + + variable = sanitizeSQL(variable) + + var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.set_value(value) + +proc/feedback_inc(var/variable,var/value) + if(!GLOB.blackbox) return + + variable = sanitizeSQL(variable) + + var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.inc(value) + +proc/feedback_dec(var/variable,var/value) + if(!GLOB.blackbox) return + + variable = sanitizeSQL(variable) + + var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.dec(value) + +proc/feedback_set_details(var/variable,var/details) + if(!GLOB.blackbox) return + + variable = sanitizeSQL(variable) + details = sanitizeSQL(details) + + var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.set_details(details) + +proc/feedback_add_details(var/variable,var/details) + if(!GLOB.blackbox) return + + variable = sanitizeSQL(variable) + details = sanitizeSQL(details) + + var/datum/feedback_variable/FV = GLOB.blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.add_details(details) diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index de71f51a0cd0..6f2e608ef4bf 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -1,113 +1,113 @@ -/* -Protolathe - -Similar to an autolathe, you load glass and metal sheets (but not other objects) into it to be used as raw materials for the stuff -it creates. All the menus and other manipulation commands are in the R&D console. - -Note: Must be placed west/left of and R&D console to function. - -*/ -/obj/machinery/r_n_d/protolathe - name = "Protolathe" - desc = "Converts raw materials into useful objects." - icon_state = "protolathe" - container_type = OPENCONTAINER - - var/efficiency_coeff - - var/list/categories = list( - "Bluespace", - "Computer Parts", - "Equipment", - "Janitorial", - "Medical", - "Mining", - "Miscellaneous", - "Power", - "Stock Parts", - "Weapons" - ) - - reagents = new() - - -/obj/machinery/r_n_d/protolathe/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/protolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - RefreshParts() - - reagents.my_atom = src - -/obj/machinery/r_n_d/protolathe/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/protolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - RefreshParts() - - reagents.my_atom = src - -/obj/machinery/r_n_d/protolathe/RefreshParts() - var/T = 0 - for(var/obj/item/reagent_containers/glass/G in component_parts) - G.reagents.trans_to(src, G.reagents.total_volume) - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - T += M.rating - materials.max_amount = T * 75000 - T = 1.2 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T -= M.rating/10 - efficiency_coeff = min(max(0, T), 1) - -/obj/machinery/r_n_d/protolathe/proc/check_mat(datum/design/being_built, var/M) // now returns how many times the item can be built with the material - var/A = materials.amount(M) - if(!A) - A = reagents.get_reagent_amount(M) - A = A / max(1, (being_built.reagents_list[M])) - else - A = A / max(1, (being_built.materials[M])) - return A - -/obj/machinery/r_n_d/protolathe/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(shocked) - if(shock(user,50)) - return TRUE - if(default_deconstruction_screwdriver(user, "protolathe_t", "protolathe", O)) - if(linked_console) - linked_console.linked_lathe = null - linked_console = null - return - - if(exchange_parts(user, O)) - return - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - for(var/obj/I in component_parts) - if(istype(I, /obj/item/reagent_containers/glass/beaker)) - reagents.trans_to(I, reagents.total_volume) - I.loc = src.loc - for(var/obj/item/reagent_containers/glass/G in component_parts) - reagents.trans_to(G, G.reagents.maximum_volume) - materials.retrieve_all() - default_deconstruction_crowbar(user, O) - return 1 - else - to_chat(user, "You can't load the [src.name] while it's opened.") - return 1 - if(O.is_open_container()) - return FALSE - else - return ..() \ No newline at end of file +/* +Protolathe + +Similar to an autolathe, you load glass and metal sheets (but not other objects) into it to be used as raw materials for the stuff +it creates. All the menus and other manipulation commands are in the R&D console. + +Note: Must be placed west/left of and R&D console to function. + +*/ +/obj/machinery/r_n_d/protolathe + name = "Protolathe" + desc = "Converts raw materials into useful objects." + icon_state = "protolathe" + container_type = OPENCONTAINER + + var/efficiency_coeff + + var/list/categories = list( + "Bluespace", + "Computer Parts", + "Equipment", + "Janitorial", + "Medical", + "Mining", + "Miscellaneous", + "Power", + "Stock Parts", + "Weapons" + ) + + reagents = new() + + +/obj/machinery/r_n_d/protolathe/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/protolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + RefreshParts() + + reagents.my_atom = src + +/obj/machinery/r_n_d/protolathe/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/protolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + RefreshParts() + + reagents.my_atom = src + +/obj/machinery/r_n_d/protolathe/RefreshParts() + var/T = 0 + for(var/obj/item/reagent_containers/glass/G in component_parts) + G.reagents.trans_to(src, G.reagents.total_volume) + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + T += M.rating + materials.max_amount = T * 75000 + T = 1.2 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + T -= M.rating/10 + efficiency_coeff = min(max(0, T), 1) + +/obj/machinery/r_n_d/protolathe/proc/check_mat(datum/design/being_built, var/M) // now returns how many times the item can be built with the material + var/A = materials.amount(M) + if(!A) + A = reagents.get_reagent_amount(M) + A = A / max(1, (being_built.reagents_list[M])) + else + A = A / max(1, (being_built.materials[M])) + return A + +/obj/machinery/r_n_d/protolathe/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(shocked) + if(shock(user,50)) + return TRUE + if(default_deconstruction_screwdriver(user, "protolathe_t", "protolathe", O)) + if(linked_console) + linked_console.linked_lathe = null + linked_console = null + return + + if(exchange_parts(user, O)) + return + + if(panel_open) + if(istype(O, /obj/item/crowbar)) + for(var/obj/I in component_parts) + if(istype(I, /obj/item/reagent_containers/glass/beaker)) + reagents.trans_to(I, reagents.total_volume) + I.loc = src.loc + for(var/obj/item/reagent_containers/glass/G in component_parts) + reagents.trans_to(G, G.reagents.maximum_volume) + materials.retrieve_all() + default_deconstruction_crowbar(user, O) + return 1 + else + to_chat(user, "You can't load the [src.name] while it's opened.") + return 1 + if(O.is_open_container()) + return FALSE + else + return ..() diff --git a/code/modules/research/rd-readme.dm b/code/modules/research/rd-readme.dm index 1ecc9dbb0da6..d1b8f77056c3 100644 --- a/code/modules/research/rd-readme.dm +++ b/code/modules/research/rd-readme.dm @@ -1,239 +1,239 @@ -/* -Research and Development System. (Designed specifically for the /tg/station 13 (Space Station 13) open source project) - -///////////////Overview/////////////////// -This system is a "tech tree" research and development system designed for SS13. It allows a "researcher" job (this document assumes -the "scientist" job is given this role) the tools necessiary to research new and better technologies. In general, the system works -by breaking existing technology and using what you learn from to advance your knowledge of SCIENCE! As your knowledge progresses, -you can build newer (and better?) devices (which you can also, eventually, deconstruct to advance your knowledge). - -A brief overview is below. For more details, see the related files. - -////////////Game Use///////////// -The major research and development is performed using a combination of four machines: -- R&D Console: A computer console that allows you to manipulate the other devices that are linked to it and view/manipulate the -technologies you have researched so far. -- Protolathe: Used to make new hand-held devices and parts for larger devices. All metals and reagents as raw materials. -- Destructive Analyzer: You can put hand-held objects into it and it'll analyze them for technological advancements but it destroys -them in the process. Destroyed items will send their raw materials to a linked Protolathe (if any) -- Circuit Imprinter: Similar to the Protolathe, it allows for the construction of circuit boards. Uses glass and acid as the raw -materials. - -While researching you are dealing with two different types of information: Technology Paths and Device Designs. Technology Paths -are the "Tech Trees" of the game. You start out with a number of them at the game start and they are improved by using the -Destructive Analyzer. By themselves, they don't do a whole lot. However, they unlock Device Designs. This is the information used -by the circuit imprinter and the protolathe to produce objects. - -//EXISTING TECH -Each tech path should have at LEAST one item at every level (levels 1 - 20). This is to allow for a more fluid progression of the -researching. Existing tech (ie, anything you can find on the station or get from the quartermaster) shouldn't go higher then -level 5 or 7. Everything past that should be stuff you research. - -Below is a checklist to make sure every tree is filled. As new items get added to R&D, add them here if there is an empty slot. -When thinking about new stuff, check here to see if there are any slots unfilled. - -//MATERIALS -1 | Metal -2 | Solid Plasma -3 | Silver -4 | Gold, Super Capacitor -5 | Uranium, Nuclear Gun, SUPERPACMAN -6 | Diamond, MRSPACMAN -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//PLASMA TECH -1 | -2 | Solid Plasma -3 | Pacman Generator -4 | -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//POWER TECH -1 | Basic Capacitor, Basic Cell -2 | High-Capacity Cell (10,000) -3 | Super-Capacity Cell (20,000), Powersink, PACMAN -4 | SUPERPACMAN -5 | MRSPACMAN, Super Capacitor -6 | Hyper-Capacity Cell (30,000) -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//BLUE SPACE -1 | -2 | Teleporter Console Board -3 | Teleport Gun, Hand Tele -4 | Teleportation Scroll -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//BIOTECH -1 | Bruise Pack, Scalple -2 | PANDEMIC Board, Mass Spectrometer -3 | AI Core, Brains (MMI) -4 | MMI+Radio -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//MAGNETS -1 | Basic Sensor -2 | Comm Console Board -3 | Adv Sensor -4 | Adv Mass Spectrometer, Chameleon Projector -5 | Phasic Sensor -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//PROGRAMMING -1 | Arcade Board -2 | Sec Camera -3 | Cloning Machine Console Board -4 | AI Core, Intellicard -5 | Pico-Manipulator, Ultra-Micro-Laser -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//SYNDICATE -1 | Sleepypen -2 | TYRANT Module, Emag -3 | Cloaking Device, Power Sink -4 | -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//COMBAT -1 | Flashbang, Mousetrap, Nettle -2 | Stun Baton -3 | Power Axe, Death Nettle, Nuclear Gun -4 | -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - - - - - - - -*/ \ No newline at end of file +/* +Research and Development System. (Designed specifically for the /tg/station 13 (Space Station 13) open source project) + +///////////////Overview/////////////////// +This system is a "tech tree" research and development system designed for SS13. It allows a "researcher" job (this document assumes +the "scientist" job is given this role) the tools necessiary to research new and better technologies. In general, the system works +by breaking existing technology and using what you learn from to advance your knowledge of SCIENCE! As your knowledge progresses, +you can build newer (and better?) devices (which you can also, eventually, deconstruct to advance your knowledge). + +A brief overview is below. For more details, see the related files. + +////////////Game Use///////////// +The major research and development is performed using a combination of four machines: +- R&D Console: A computer console that allows you to manipulate the other devices that are linked to it and view/manipulate the +technologies you have researched so far. +- Protolathe: Used to make new hand-held devices and parts for larger devices. All metals and reagents as raw materials. +- Destructive Analyzer: You can put hand-held objects into it and it'll analyze them for technological advancements but it destroys +them in the process. Destroyed items will send their raw materials to a linked Protolathe (if any) +- Circuit Imprinter: Similar to the Protolathe, it allows for the construction of circuit boards. Uses glass and acid as the raw +materials. + +While researching you are dealing with two different types of information: Technology Paths and Device Designs. Technology Paths +are the "Tech Trees" of the game. You start out with a number of them at the game start and they are improved by using the +Destructive Analyzer. By themselves, they don't do a whole lot. However, they unlock Device Designs. This is the information used +by the circuit imprinter and the protolathe to produce objects. + +//EXISTING TECH +Each tech path should have at LEAST one item at every level (levels 1 - 20). This is to allow for a more fluid progression of the +researching. Existing tech (ie, anything you can find on the station or get from the quartermaster) shouldn't go higher then +level 5 or 7. Everything past that should be stuff you research. + +Below is a checklist to make sure every tree is filled. As new items get added to R&D, add them here if there is an empty slot. +When thinking about new stuff, check here to see if there are any slots unfilled. + +//MATERIALS +1 | Metal +2 | Solid Plasma +3 | Silver +4 | Gold, Super Capacitor +5 | Uranium, Nuclear Gun, SUPERPACMAN +6 | Diamond, MRSPACMAN +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//PLASMA TECH +1 | +2 | Solid Plasma +3 | Pacman Generator +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//POWER TECH +1 | Basic Capacitor, Basic Cell +2 | High-Capacity Cell (10,000) +3 | Super-Capacity Cell (20,000), Powersink, PACMAN +4 | SUPERPACMAN +5 | MRSPACMAN, Super Capacitor +6 | Hyper-Capacity Cell (30,000) +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//BLUE SPACE +1 | +2 | Teleporter Console Board +3 | Teleport Gun, Hand Tele +4 | Teleportation Scroll +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//BIOTECH +1 | Bruise Pack, Scalple +2 | PANDEMIC Board, Mass Spectrometer +3 | AI Core, Brains (MMI) +4 | MMI+Radio +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//MAGNETS +1 | Basic Sensor +2 | Comm Console Board +3 | Adv Sensor +4 | Adv Mass Spectrometer, Chameleon Projector +5 | Phasic Sensor +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//PROGRAMMING +1 | Arcade Board +2 | Sec Camera +3 | Cloning Machine Console Board +4 | AI Core, Intellicard +5 | Pico-Manipulator, Ultra-Micro-Laser +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//SYNDICATE +1 | Sleepypen +2 | TYRANT Module, Emag +3 | Cloaking Device, Power Sink +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//COMBAT +1 | Flashbang, Mousetrap, Nettle +2 | Stun Baton +3 | Power Axe, Death Nettle, Nuclear Gun +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + + + + + + + +*/ diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index b867803e0e2f..d7bf071a8eb8 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -1,948 +1,948 @@ -/* -Research and Development (R&D) Console - -This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit -imprinter. It also contains the /datum/research holder with all the known/possible technology paths and device designs. - -Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they -aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already -linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that -allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console. - -The imprinting and construction menus do NOT require toxins access to access but all the other menus do. However, if you leave it -on a menu, nothing is to stop the person from using the options on that menu (although they won't be able to change to a different -one). You can also lock the console on the settings menu if you're feeling paranoid and you don't want anyone messing with it who -doesn't have toxins access. - -When a R&D console is destroyed or even partially disassembled, you lose all research data on it. However, there are two ways around -this dire fate: -- The easiest way is to go to the settings menu and select "Sync Database with Network." That causes it to upload (but not download) -it's data to every other device in the game. Each console has a "disconnect from network" option that'll will cause data base sync -operations to skip that console. This is useful if you want to make a "public" R&D console or, for example, give the engineers -a circuit imprinter with certain designs on it and don't want it accidentally updating. The downside of this method is that you have -to have physical access to the other console to send data back. Note: An R&D console is on Centcom so if a random griffan happens to -cause a ton of data to be lost, an admin can go send it back. -- The second method is with Technology Disks and Design Disks. Each of these disks can hold a single technology or design datum in -it's entirety. You can then take the disk to any R&D console and upload it's data to it. This method is a lot more secure (since it -won't update every console in existence) but it's more of a hassle to do. Also, the disks can be stolen. - - -*/ - -// Who likes #defines? -// I don't! -// but I gotta add 'em anyways because we have a bias against /const statements for some reason -#define TECH_UPDATE_DELAY 50 -#define DESIGN_UPDATE_DELAY 50 -#define PROTOLATHE_CONSTRUCT_DELAY 32 -#define SYNC_RESEARCH_DELAY 30 -#define DECONSTRUCT_DELAY 24 -#define SYNC_DEVICE_DELAY 20 -#define RESET_RESEARCH_DELAY 20 -#define IMPRINTER_DELAY 16 - -/obj/machinery/computer/rdconsole - name = "\improper R&D console" - icon_screen = "rdcomp" - icon_keyboard = "rd_key" - light_color = LIGHT_COLOR_FADEDPURPLE - circuit = /obj/item/circuitboard/rdconsole - var/datum/research/files //Stores all the collected research data. - var/obj/item/disk/tech_disk/t_disk = null //Stores the technology disk. - var/obj/item/disk/design_disk/d_disk = null //Stores the design disk. - - var/obj/machinery/r_n_d/destructive_analyzer/linked_destroy = null //Linked Destructive Analyzer - var/obj/machinery/r_n_d/protolathe/linked_lathe = null //Linked Protolathe - var/obj/machinery/r_n_d/circuit_imprinter/linked_imprinter = null //Linked Circuit Imprinter - - var/screen = 1.0 //Which screen is currently showing. - - var/menu = 0 // Current menu. - var/submenu = 0 - var/wait_message = 0 - var/wait_message_timer = 0 - - var/id = 0 //ID of the computer (for server restrictions). - var/sync = 1 //If sync = 0, it doesn't show up on Server Control Console - - req_access = list(ACCESS_TOX) //Data and setting manipulation requires scientist access. - - var/selected_category - var/list/datum/design/matching_designs = list() //for the search function - -/proc/CallTechName(ID) //A simple helper proc to find the name of a tech with a given ID. - for(var/T in subtypesof(/datum/tech)) - var/datum/tech/tt = T - if(initial(tt.id) == ID) - return initial(tt.name) - -/proc/CallMaterialName(ID) - if(copytext(ID, 1, 2) == "$") - var/return_name = copytext(ID, 2) - switch(return_name) - if("metal") - return_name = "Metal" - if("glass") - return_name = "Glass" - if("gold") - return_name = "Gold" - if("silver") - return_name = "Silver" - if("plasma") - return_name = "Solid Plasma" - if("uranium") - return_name = "Uranium" - if("diamond") - return_name = "Diamond" - if("clown") - return_name = "Bananium" - if("mime") - return_name = "Tranquillite" - if("titanium") - return_name = "Titanium" - if("bluespace") - return_name = "Bluespace Mesh" - if("plastic") - return_name = "Plastic" - return return_name - else - for(var/R in subtypesof(/datum/reagent)) - var/datum/reagent/rt = R - if(initial(rt.id) == ID) - return initial(rt.name) - -/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any). - for(var/obj/machinery/r_n_d/D in range(3,src)) - if(!isnull(D.linked_console) || D.disabled || D.panel_open) - continue - if(istype(D, /obj/machinery/r_n_d/destructive_analyzer)) - if(linked_destroy == null) - linked_destroy = D - D.linked_console = src - else if(istype(D, /obj/machinery/r_n_d/protolathe)) - if(linked_lathe == null) - linked_lathe = D - D.linked_console = src - else if(istype(D, /obj/machinery/r_n_d/circuit_imprinter)) - if(linked_imprinter == null) - linked_imprinter = D - D.linked_console = src - return - -//Have it automatically push research to the centcom server so wild griffins can't fuck up R&D's work --NEO -/obj/machinery/computer/rdconsole/proc/griefProtection() - for(var/obj/machinery/r_n_d/server/centcom/C in world) - files.push_data(C.files) - -/obj/machinery/computer/rdconsole/proc/Maximize() - for(var/datum/tech/T in files.possible_tech) - files.known_tech[T.id] = T - for(var/v in files.known_tech) - var/datum/tech/KT = files.known_tech[v] - if(KT.level < KT.max_level) - KT.level=KT.max_level - files.RefreshResearch() - -/obj/machinery/computer/rdconsole/New() - ..() - files = new /datum/research(src) //Setup the research data holder. - matching_designs = list() - if(!id) - for(var/obj/machinery/r_n_d/server/centcom/S in world) - S.initialize_serv() - break - -/obj/machinery/computer/rdconsole/Initialize() - ..() - SyncRDevices() - -/obj/machinery/computer/rdconsole/Destroy() - if(wait_message_timer) - deltimer(wait_message_timer) - wait_message_timer = 0 - return ..() - -/* Instead of calling this every tick, it is only being called when needed -/obj/machinery/computer/rdconsole/process() - griefProtection() -*/ - -/obj/machinery/computer/rdconsole/attackby(var/obj/item/D as obj, var/mob/user as mob, params) - - //Loading a disk into it. - if(istype(D, /obj/item/disk)) - if(t_disk || d_disk) - to_chat(user, "A disk is already loaded into the machine.") - return - - if(istype(D, /obj/item/disk/tech_disk)) t_disk = D - else if(istype(D, /obj/item/disk/design_disk)) d_disk = D - else - to_chat(user, "Machine cannot accept disks in that format.") - return - if(!user.drop_item()) - return - D.loc = src - to_chat(user, "You add the disk to the machine!") - else if(!(linked_destroy && linked_destroy.busy) && !(linked_lathe && linked_lathe.busy) && !(linked_imprinter && linked_imprinter.busy)) - ..() - SSnanoui.update_uis(src) - return - -/obj/machinery/computer/rdconsole/emag_act(user as mob) - if(!emagged) - playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) - req_access = list() - emagged = 1 - to_chat(user, "You disable the security protocols") - -/obj/machinery/computer/rdconsole/Topic(href, href_list) - if(..()) - return 1 - - if(!allowed(usr) && !isobserver(usr)) - return 1 - - add_fingerprint(usr) - - usr.set_machine(src) - if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. - var/temp_screen = text2num(href_list["menu"]) - menu = temp_screen - if(href_list["submenu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. - var/temp_screen = text2num(href_list["submenu"]) - submenu = temp_screen - - if(href_list["category"]) - var/compare - - matching_designs.Cut() - - if(menu == 4) - compare = PROTOLATHE - else - compare = IMPRINTER - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(D.build_type & compare)) - continue - if(href_list["category"] in D.category) - matching_designs.Add(D) - submenu = 1 - - selected_category = "Viewing Category [href_list["category"]]" - - else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk. - add_wait_message("Updating Database...", TECH_UPDATE_DELAY) - spawn(TECH_UPDATE_DELAY) - clear_wait_message() - files.AddTech2Known(t_disk.stored) - SSnanoui.update_uis(src) - griefProtection() //Update centcom too - - else if(href_list["clear_tech"]) //Erase data on the technology disk. - if(t_disk) - t_disk.wipe_tech() - - else if(href_list["eject_tech"]) //Eject the technology disk. - if(t_disk) - t_disk.loc = src.loc - t_disk = null - menu = 0 - submenu = 0 - - else if(href_list["copy_tech"]) //Copy some technology data from the research holder to the disk. - // Somehow this href makes me very nervous - t_disk.stored = files.known_tech[href_list["copy_tech_ID"]] - menu = 2 - submenu = 0 - - else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk. - add_wait_message("Updating Database...", DESIGN_UPDATE_DELAY) - spawn(DESIGN_UPDATE_DELAY) - clear_wait_message() - files.AddDesign2Known(d_disk.blueprint) - SSnanoui.update_uis(src) - griefProtection() //Update centcom too - - else if(href_list["clear_design"]) //Erases data on the design disk. - if(d_disk) - d_disk.wipe_blueprint() - - else if(href_list["eject_design"]) //Eject the design disk. - if(d_disk) - d_disk.loc = src.loc - d_disk = null - menu = 0 - submenu = 0 - - else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk. - // This href ALSO makes me very nervous - var/datum/design/D = files.known_designs[href_list["copy_design_ID"]] - if(D) - // eeeeeep design datums are global be careful! - var/autolathe_friendly = 1 - for(var/x in D.materials) - if( !(x in list(MAT_METAL, MAT_GLASS))) - autolathe_friendly = 0 - D.category -= "Imported" - if(D.locked) - autolathe_friendly = 0 - D.category -= "Imported" - if(D.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)) // Specifically excludes circuit imprinter and mechfab - D.build_type = autolathe_friendly ? (D.build_type | AUTOLATHE) : D.build_type - D.category |= "Imported" - d_disk.blueprint = D - menu = 2 - submenu = 0 - - else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer. - if(linked_destroy) - if(linked_destroy.busy) - to_chat(usr, " The destructive analyzer is busy at the moment.") - - else if(linked_destroy.loaded_item) - linked_destroy.loaded_item.loc = linked_destroy.loc - linked_destroy.loaded_item = null - linked_destroy.icon_state = "d_analyzer" - menu = 3 - - else if(href_list["maxresearch"]) //Eject the item inside the destructive analyzer. - if(!check_rights(R_ADMIN)) - return - if(alert("Are you sure you want to maximize research levels?","Confirmation","Yes","No")=="No") - return - log_admin("[key_name(usr)] has maximized the research levels.") - message_admins("[key_name_admin(usr)] has maximized the research levels.") - Maximize() - SSnanoui.update_uis(src) - griefProtection() //Update centcomm too - - else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder. - if(linked_destroy) - if(linked_destroy.busy) - to_chat(usr, "The destructive analyzer is busy at the moment.") - return - var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) - var/cancontinue = FALSE - for(var/T in temp_tech) - if(files.IsTechHigher(T, temp_tech[T])) - cancontinue = TRUE - break - if(!cancontinue) - var/choice = input("This item does not raise tech levels. Proceed destroying loaded item anyway?") in list("Proceed", "Cancel") - if(choice == "Cancel" || !linked_destroy) - return - linked_destroy.busy = 1 - add_wait_message("Processing and Updating Database...", DECONSTRUCT_DELAY) - SSnanoui.update_uis(src) - flick("d_analyzer_process", linked_destroy) - spawn(DECONSTRUCT_DELAY) - clear_wait_message() - if(linked_destroy) - linked_destroy.busy = 0 - if(!linked_destroy.hacked) - if(!linked_destroy.loaded_item) - to_chat(usr, "The destructive analyzer appears to be empty.") - menu = 0 - submenu = 0 - return - for(var/T in temp_tech) - var/datum/tech/KT = files.known_tech[T] //For stat logging of high levels - if(files.IsTechHigher(T, temp_tech[T]) && KT.level >= 5) //For stat logging of high levels - feedback_add_details("high_research_level","[KT][KT.level + 1]") //+1 to show the level which we're about to get - files.UpdateTech(T, temp_tech[T]) - menu = 0 - submenu = 0 - if(linked_lathe) //Also sends salvaged materials to a linked protolathe, if any. - for(var/material in linked_destroy.loaded_item.materials) - var/can_insert = min(linked_lathe.materials.max_amount - linked_lathe.materials.total_amount, linked_destroy.loaded_item.materials[material] * (linked_destroy.decon_mod / 10), linked_destroy.loaded_item.materials[material]) - linked_lathe.materials.insert_amount(can_insert, material) - linked_destroy.loaded_item = null - else - menu = 0 - submenu = 0 - for(var/obj/I in linked_destroy.contents) - for(var/mob/M in I.contents) - M.death() - if(istype(I,/obj/item/stack/sheet))//Only deconsturcts one sheet at a time instead of the entire stack - var/obj/item/stack/sheet/S = I - if(S.amount > 1) - S.amount-- - linked_destroy.loaded_item = S - else - qdel(S) - linked_destroy.icon_state = "d_analyzer" - else - if(!(I in linked_destroy.component_parts)) - qdel(I) - linked_destroy.icon_state = "d_analyzer" - use_power(250) - SSnanoui.update_uis(src) - - else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected. - if(!sync) - to_chat(usr, "You must connect to the network first!") - else - add_wait_message("Updating Database...", SYNC_RESEARCH_DELAY) - griefProtection() //Putting this here because I dont trust the sync process - spawn(SYNC_RESEARCH_DELAY) - clear_wait_message() - if(src) - for(var/obj/machinery/r_n_d/server/S in world) - var/server_processed = 0 - if(S.disabled) - continue - if((id in S.id_with_upload) || istype(S, /obj/machinery/r_n_d/server/centcom)) - files.push_data(S.files) - server_processed = 1 - if(((id in S.id_with_download) && !istype(S, /obj/machinery/r_n_d/server/centcom)) || S.hacked) - S.files.push_data(files) - server_processed = 1 - if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed) - S.produce_heat(100) - SSnanoui.update_uis(src) - - else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data. - sync = !sync - - else if(href_list["build"]) //Causes the Protolathe to build something. - if(linked_lathe) - if(linked_lathe.busy) - to_chat(usr, "Protolathe is busy at the moment.") - return - var/coeff = linked_lathe.efficiency_coeff - var/g2g = 1 - var/datum/design/being_built = files.known_designs[href_list["build"]] - if(being_built) - var/power = 2000 - var/amount=text2num(href_list["amount"]) - if(being_built.make_reagents.len) - return 0 - amount = max(1, min(10, amount)) - for(var/M in being_built.materials) - power += round(being_built.materials[M] * amount / 5) - power = max(2000, power) - var/key = usr.key //so we don't lose the info during the spawn delay - if(!(being_built.build_type & PROTOLATHE)) - g2g = 0 - message_admins("Protolathe exploit attempted by [key_name(usr, TRUE)]!") - - if(g2g) //If input is incorrect, nothing happens - var/new_coeff = coeff * being_built.lathe_time_factor - var/time_to_construct = PROTOLATHE_CONSTRUCT_DELAY * new_coeff * amount ** 0.8 - var/enough_materials = 1 - - add_wait_message("Constructing Prototype. Please Wait...", time_to_construct) - linked_lathe.busy = 1 - flick("protolathe_n",linked_lathe) - use_power(power) - - var/list/efficient_mats = list() - for(var/MAT in being_built.materials) - efficient_mats[MAT] = being_built.materials[MAT]*coeff - - if(!linked_lathe.materials.has_materials(efficient_mats, amount)) - src.visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") - enough_materials = 0 - g2g = 0 - else - for(var/R in being_built.reagents_list) - if(!linked_lathe.reagents.has_reagent(R, being_built.reagents_list[R])*coeff) - src.visible_message("The [src.name] beeps, \"Not enough reagents to complete prototype.\"") - enough_materials = 0 - g2g = 0 - - if(enough_materials) - linked_lathe.materials.use_amount(efficient_mats, amount) - for(var/R in being_built.reagents_list) - linked_lathe.reagents.remove_reagent(R, being_built.reagents_list[R]*coeff) - - var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. - var/O = being_built.locked - - spawn(time_to_construct) - if(g2g) //And if we only fail the material requirements, we still spend time and power - for(var/i = 0, iCircuit Imprinter is busy at the moment.") - return - var/datum/design/being_built = null - being_built = files.known_designs[href_list["imprint"]] - if(being_built) - var/power = 2000 - for(var/M in being_built.materials) - power += round(being_built.materials[M] / 5) - power = max(2000, power) - if(!(being_built.build_type & IMPRINTER)) - g2g = 0 - message_admins("Circuit imprinter exploit attempted by [key_name(usr, TRUE)]!") - - if(g2g) //Again, if input is wrong, do nothing - add_wait_message("Imprinting Circuit. Please Wait...", IMPRINTER_DELAY) - linked_imprinter.busy = 1 - flick("circuit_imprinter_ani",linked_imprinter) - use_power(power) - - var/list/efficient_mats = list() - for(var/MAT in being_built.materials) - efficient_mats[MAT] = being_built.materials[MAT]/coeff - - if(!linked_imprinter.materials.has_materials(efficient_mats)) - visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") - enough_materials = 0 - g2g = 0 - else - for(var/R in being_built.reagents_list) - if(!linked_imprinter.reagents.has_reagent(R, being_built.reagents_list[R]/coeff)) - visible_message("The [name] beeps, \"Not enough reagents to complete prototype.\"") - enough_materials = 0 - g2g = 0 - - if(enough_materials) - linked_imprinter.materials.use_amount(efficient_mats) - for(var/R in being_built.reagents_list) - linked_imprinter.reagents.remove_reagent(R, being_built.reagents_list[R]/coeff) - - var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. - spawn(IMPRINTER_DELAY) - if(g2g) - var/obj/item/new_item = new P(src) - new_item.loc = linked_imprinter.loc - linked_imprinter.busy = 0 - clear_wait_message() - SSnanoui.update_uis(src) - - else if(href_list["disposeI"] && linked_imprinter) //Causes the circuit imprinter to dispose of a single reagent (all of it) - linked_imprinter.reagents.del_reagent(href_list["disposeI"]) - - else if(href_list["disposeallI"] && linked_imprinter) //Causes the circuit imprinter to dispose of all it's reagents. - linked_imprinter.reagents.clear_reagents() - - else if(href_list["disposeP"] && linked_lathe) //Causes the protolathe to dispose of a single reagent (all of it) - linked_lathe.reagents.del_reagent(href_list["disposeP"]) - - else if(href_list["disposeallP"] && linked_lathe) //Causes the protolathe to dispose of all it's reagents. - linked_lathe.reagents.clear_reagents() - - else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material - var/desired_num_sheets - if(href_list["lathe_ejectsheet_amt"] == "custom") - desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num - desired_num_sheets = max(0,desired_num_sheets) // If you input too high of a number, the mineral datum will take care of it either way - if(!desired_num_sheets) - return - desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery - else - desired_num_sheets = text2num(href_list["lathe_ejectsheet_amt"]) - linked_lathe.materials.retrieve_sheets(desired_num_sheets, href_list["lathe_ejectsheet"]) - - else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material - var/desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) - if(href_list["imprinter_ejectsheet_amt"] == "custom") - desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num - desired_num_sheets = max(0,desired_num_sheets) // for the imprinter they have something hacky, that still will guard against shenanigans. eh - if(!desired_num_sheets) - return - desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery - else - desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) - linked_imprinter.materials.retrieve_sheets(desired_num_sheets, href_list["imprinter_ejectsheet"]) - - else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with. - add_wait_message("Updating Database...", SYNC_DEVICE_DELAY) - spawn(SYNC_DEVICE_DELAY) - SyncRDevices() - clear_wait_message() - SSnanoui.update_uis(src) - - else if(href_list["disconnect"]) //The R&D console disconnects with a specific device. - switch(href_list["disconnect"]) - if("destroy") - linked_destroy.linked_console = null - linked_destroy = null - if("lathe") - linked_lathe.linked_console = null - linked_lathe = null - if("imprinter") - linked_imprinter.linked_console = null - linked_imprinter = null - - else if(href_list["reset"]) //Reset the R&D console's database. - griefProtection() - var/choice = alert("Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "R&D Console Database Reset", "Continue", "Cancel") - if(choice == "Continue") - add_wait_message("Updating Database...", RESET_RESEARCH_DELAY) - qdel(files) - files = new /datum/research(src) - spawn(RESET_RESEARCH_DELAY) - clear_wait_message() - SSnanoui.update_uis(src) - - else if(href_list["search"]) //Search for designs with name matching pattern - var/compare - - matching_designs.Cut() - - if(menu == 4) - compare = PROTOLATHE - else - compare = IMPRINTER - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(D.build_type & compare)) - continue - if(findtext(D.name,href_list["to_search"])) - matching_designs.Add(D) - submenu = 1 - - selected_category = "Search Results for '[href_list["to_search"]]'" - - SSnanoui.update_uis(src) - return - - -/obj/machinery/computer/rdconsole/attack_hand(mob/user as mob) - if(..()) - return 1 - if(!allowed(user) && !isobserver(user)) - to_chat(user, "Access denied.") - return 1 - ui_interact(user) - -/obj/machinery/computer/rdconsole/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "r_n_d.tmpl", src.name, 800, 550) - ui.open() - -/obj/machinery/computer/rdconsole/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - files.RefreshResearch() - - data["menu"] = menu - data["submenu"] = submenu - data["wait_message"] = wait_message - data["src_ref"] = UID() - - data["linked_destroy"] = linked_destroy ? 1 : 0 - data["linked_lathe"] = linked_lathe ? 1 : 0 - data["linked_imprinter"] = linked_imprinter ? 1 : 0 - data["sync"] = sync - data["admin"] = check_rights(R_ADMIN,0) - data["disk_type"] = d_disk ? 2 : (t_disk ? 1 : 0) - data["category"] = selected_category - - if(menu == 0 || menu == 1) - var/list/tech_levels = list() - data["tech_levels"] = tech_levels - for(var/v in files.known_tech) - var/datum/tech/T = files.known_tech[v] - if(T.level <= 0) - continue - var/list/this_tech_list = list() - this_tech_list["name"] = T.name - this_tech_list["level"] = T.level - this_tech_list["desc"] = T.desc - tech_levels[++tech_levels.len] = this_tech_list - - if(menu == 2) - - if(t_disk != null && t_disk.stored != null && submenu == 0) //Technology Disk Menu - var/list/disk_data = list() - data["disk_data"] = disk_data - disk_data["name"] = t_disk.stored.name - disk_data["level"] = t_disk.stored.level - disk_data["desc"] = t_disk.stored.desc - - if(t_disk != null && submenu == 1) - var/list/to_copy = list() - data["to_copy"] = to_copy - for(var/v in files.known_tech) - var/datum/tech/T = files.known_tech[v] - var/list/item = list() - to_copy[++to_copy.len] = item - if(T.level <= 0) - continue - item["name"] = T.name - item["id"] = T.id - - if(d_disk != null && d_disk.blueprint != null && submenu == 0) - var/list/disk_data = list() - data["disk_data"] = disk_data - disk_data["name"] = d_disk.blueprint.name - var/b_type = d_disk.blueprint.build_type - var/list/lathe_types = list() - disk_data["lathe_types"] = lathe_types - if(b_type) - if(b_type & IMPRINTER) lathe_types += "Circuit Imprinter" - if(b_type & PROTOLATHE) lathe_types += "Protolathe" - if(b_type & AUTOLATHE) lathe_types += "Autolathe" - if(b_type & MECHFAB) lathe_types += "Mech Fabricator" - if(b_type & PODFAB) lathe_types += "Spacepod Fabricator" - if(b_type & BIOGENERATOR) lathe_types += "Biogenerator" - if(b_type & SMELTER) lathe_types += "Smelter" - var/list/materials = list() - disk_data["materials"] = materials - for(var/M in d_disk.blueprint.materials) - var/list/material = list() - materials[++materials.len] = material - material["name"] = CallMaterialName(M) - material["amount"] = d_disk.blueprint.materials[M] - - if(d_disk != null && submenu == 1) - var/list/to_copy = list() - data["to_copy"] = to_copy - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - var/list/item = list() - to_copy[++to_copy.len] = item - item["name"] = D.name - item["id"] = D.id - - if(menu == 3 && linked_destroy && linked_destroy.loaded_item) - var/list/loaded_item_list = list() - data["loaded_item"] = loaded_item_list - loaded_item_list["name"] = linked_destroy.loaded_item.name - var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) - var/list/tech_list = list() - loaded_item_list["origin_tech"] = tech_list - for(var/T in temp_tech) - var/list/tech_item = list() - tech_list[++tech_list.len] = tech_item - tech_item["name"] = CallTechName(T) - tech_item["object_level"] = temp_tech[T] - for(var/v in files.known_tech) - var/datum/tech/F = files.known_tech[v] - if(F.name == CallTechName(T)) - tech_item["current_level"] = F.level - break - - if(menu == 4 && linked_lathe) - data["total_materials"] = linked_lathe.materials.total_amount - data["max_materials"] = linked_lathe.materials.max_amount - data["total_chemicals"] = linked_lathe.reagents.total_volume - data["max_chemicals"] = linked_lathe.reagents.maximum_volume - data["categories"] = linked_lathe.categories - if(submenu == 1) - var/list/designs_list = list() - data["matching_designs"] = designs_list - var/coeff = linked_lathe.efficiency_coeff - for(var/datum/design/D in matching_designs) - var/list/design_list = list() - designs_list[++designs_list.len] = design_list - var/list/materials_list = list() - design_list["materials"] = materials_list - design_list["id"] = D.id - design_list["name"] = sanitize(D.name) - var/c = 50 - for(var/M in D.materials) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(M) - material_list["amount"] = D.materials[M]*coeff - var/t = linked_lathe.check_mat(D, M) - - if(t < 1) - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - c = min(c, t) - - for(var/R in D.reagents_list) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(R) - material_list["amount"] = D.reagents_list[R]*coeff - var/t = linked_lathe.check_mat(D, R) - - if(t < 1) - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - c = min(c, t) - design_list["can_build"] = c - if(submenu == 2) - var/list/materials_list = list() - data["loaded_materials"] = materials_list - materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_lathe.materials.amount(MAT_METAL)) - materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_lathe.materials.amount(MAT_GLASS)) - materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_lathe.materials.amount(MAT_GOLD)) - materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_lathe.materials.amount(MAT_SILVER)) - materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_lathe.materials.amount(MAT_PLASMA)) - materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_lathe.materials.amount(MAT_URANIUM)) - materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_lathe.materials.amount(MAT_DIAMOND)) - materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_lathe.materials.amount(MAT_BANANIUM)) - materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_lathe.materials.amount(MAT_TRANQUILLITE)) - materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_lathe.materials.amount(MAT_TITANIUM)) - materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_lathe.materials.amount(MAT_PLASTIC)) - materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_lathe.materials.amount(MAT_BLUESPACE)) - if(submenu == 3) - var/list/loaded_chemicals = list() - data["loaded_chemicals"] = loaded_chemicals - for(var/datum/reagent/R in linked_lathe.reagents.reagent_list) - var/list/loaded_chemical = list() - loaded_chemicals[++loaded_chemicals.len] = loaded_chemical - loaded_chemical["name"] = R.name - loaded_chemical["volume"] = R.volume - loaded_chemical["id"] = R.id - - if(menu == 5 && linked_imprinter) - data["total_materials"] = linked_imprinter.materials.total_amount - data["max_materials"] = linked_imprinter.materials.max_amount - data["total_chemicals"] = linked_imprinter.reagents.total_volume - data["max_chemicals"] = linked_imprinter.reagents.maximum_volume - data["categories"] = linked_imprinter.categories - if(submenu == 1) - var/list/designs_list = list() - data["matching_designs"] = designs_list - var/coeff = linked_imprinter.efficiency_coeff - for(var/datum/design/D in matching_designs) - var/list/design_list = list() - designs_list[++designs_list.len] = design_list - var/list/materials_list = list() - design_list["materials"] = materials_list - design_list["id"] = D.id - design_list["name"] = sanitize(D.name) - var/check_materials = 1 - for(var/M in D.materials) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(M) - material_list["amount"] = D.materials[M] / coeff - if(!linked_imprinter.check_mat(D, M)) - check_materials = 0 - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - - for(var/R in D.reagents_list) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(R) - material_list["amount"] = D.reagents_list[R]*coeff - if(!linked_imprinter.check_mat(D, R)) - check_materials = 0 - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - - design_list["can_build"] = check_materials - if(submenu == 2) - var/list/materials_list = list() - data["loaded_materials"] = materials_list - materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_imprinter.materials.amount(MAT_METAL)) - materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_imprinter.materials.amount(MAT_GLASS)) - materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_imprinter.materials.amount(MAT_GOLD)) - materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_imprinter.materials.amount(MAT_SILVER)) - materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_imprinter.materials.amount(MAT_PLASMA)) - materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_imprinter.materials.amount(MAT_URANIUM)) - materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_imprinter.materials.amount(MAT_DIAMOND)) - materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_imprinter.materials.amount(MAT_BANANIUM)) - materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_imprinter.materials.amount(MAT_TRANQUILLITE)) - materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_imprinter.materials.amount(MAT_TITANIUM)) - materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_imprinter.materials.amount(MAT_PLASTIC)) - materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_imprinter.materials.amount(MAT_BLUESPACE)) - if(submenu == 3) - var/list/loaded_chemicals = list() - data["loaded_chemicals"] = loaded_chemicals - for(var/datum/reagent/R in linked_imprinter.reagents.reagent_list) - var/list/loaded_chemical = list() - loaded_chemicals[++loaded_chemicals.len] = loaded_chemical - loaded_chemical["name"] = R.name - loaded_chemical["volume"] = R.volume - loaded_chemical["id"] = R.id - - return data - -//helper proc that guarantees the wait message will not freeze the UI -/obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay) - wait_message = message - wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE) - -// This is here to guarantee that we never lock the console, so long as the timer -// process is running -// So long as the spawns never runtime, though, the timer should be stopped -// before it gets the chance to fire -// Since the timer process can have significant delays, you should call this -// in the operations that take time, once they complete -/obj/machinery/computer/rdconsole/proc/clear_wait_message() - wait_message = "" - if(wait_message_timer) - // This could be expensive, and will still be called - // if the timer calls this function - deltimer(wait_message_timer) - wait_message_timer = 0 - SSnanoui.update_uis(src) - - -/obj/machinery/computer/rdconsole/core - name = "core R&D console" - desc = "A console used to interface with R&D tools." - id = 1 - -/obj/machinery/computer/rdconsole/robotics - name = "robotics R&D console" - desc = "A console used to interface with R&D tools." - id = 2 - req_access = list(ACCESS_ROBOTICS) - circuit = /obj/item/circuitboard/rdconsole/robotics - -/obj/machinery/computer/rdconsole/experiment - name = "\improper E.X.P.E.R.I-MENTOR R&D console" - desc = "A console used to interface with R&D tools." - id = 3 - circuit = /obj/item/circuitboard/rdconsole/experiment - -/obj/machinery/computer/rdconsole/mechanics - name = "mechanics R&D console" - desc = "A console used to interface with R&D tools." - id = 4 - req_access = list(ACCESS_MECHANIC) - circuit = /obj/item/circuitboard/rdconsole/mechanics - -/obj/machinery/computer/rdconsole/public - name = "public R&D console" - desc = "A console used to interface with R&D tools." - id = 5 - req_access = list() - circuit = /obj/item/circuitboard/rdconsole/public - -#undef TECH_UPDATE_DELAY -#undef DESIGN_UPDATE_DELAY -#undef PROTOLATHE_CONSTRUCT_DELAY -#undef SYNC_RESEARCH_DELAY -#undef DECONSTRUCT_DELAY -#undef SYNC_DEVICE_DELAY -#undef RESET_RESEARCH_DELAY -#undef IMPRINTER_DELAY +/* +Research and Development (R&D) Console + +This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit +imprinter. It also contains the /datum/research holder with all the known/possible technology paths and device designs. + +Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they +aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already +linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that +allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console. + +The imprinting and construction menus do NOT require toxins access to access but all the other menus do. However, if you leave it +on a menu, nothing is to stop the person from using the options on that menu (although they won't be able to change to a different +one). You can also lock the console on the settings menu if you're feeling paranoid and you don't want anyone messing with it who +doesn't have toxins access. + +When a R&D console is destroyed or even partially disassembled, you lose all research data on it. However, there are two ways around +this dire fate: +- The easiest way is to go to the settings menu and select "Sync Database with Network." That causes it to upload (but not download) +it's data to every other device in the game. Each console has a "disconnect from network" option that'll will cause data base sync +operations to skip that console. This is useful if you want to make a "public" R&D console or, for example, give the engineers +a circuit imprinter with certain designs on it and don't want it accidentally updating. The downside of this method is that you have +to have physical access to the other console to send data back. Note: An R&D console is on Centcom so if a random griffan happens to +cause a ton of data to be lost, an admin can go send it back. +- The second method is with Technology Disks and Design Disks. Each of these disks can hold a single technology or design datum in +it's entirety. You can then take the disk to any R&D console and upload it's data to it. This method is a lot more secure (since it +won't update every console in existence) but it's more of a hassle to do. Also, the disks can be stolen. + + +*/ + +// Who likes #defines? +// I don't! +// but I gotta add 'em anyways because we have a bias against /const statements for some reason +#define TECH_UPDATE_DELAY 50 +#define DESIGN_UPDATE_DELAY 50 +#define PROTOLATHE_CONSTRUCT_DELAY 32 +#define SYNC_RESEARCH_DELAY 30 +#define DECONSTRUCT_DELAY 24 +#define SYNC_DEVICE_DELAY 20 +#define RESET_RESEARCH_DELAY 20 +#define IMPRINTER_DELAY 16 + +/obj/machinery/computer/rdconsole + name = "\improper R&D console" + icon_screen = "rdcomp" + icon_keyboard = "rd_key" + light_color = LIGHT_COLOR_FADEDPURPLE + circuit = /obj/item/circuitboard/rdconsole + var/datum/research/files //Stores all the collected research data. + var/obj/item/disk/tech_disk/t_disk = null //Stores the technology disk. + var/obj/item/disk/design_disk/d_disk = null //Stores the design disk. + + var/obj/machinery/r_n_d/destructive_analyzer/linked_destroy = null //Linked Destructive Analyzer + var/obj/machinery/r_n_d/protolathe/linked_lathe = null //Linked Protolathe + var/obj/machinery/r_n_d/circuit_imprinter/linked_imprinter = null //Linked Circuit Imprinter + + var/screen = 1.0 //Which screen is currently showing. + + var/menu = 0 // Current menu. + var/submenu = 0 + var/wait_message = 0 + var/wait_message_timer = 0 + + var/id = 0 //ID of the computer (for server restrictions). + var/sync = 1 //If sync = 0, it doesn't show up on Server Control Console + + req_access = list(ACCESS_TOX) //Data and setting manipulation requires scientist access. + + var/selected_category + var/list/datum/design/matching_designs = list() //for the search function + +/proc/CallTechName(ID) //A simple helper proc to find the name of a tech with a given ID. + for(var/T in subtypesof(/datum/tech)) + var/datum/tech/tt = T + if(initial(tt.id) == ID) + return initial(tt.name) + +/proc/CallMaterialName(ID) + if(copytext(ID, 1, 2) == "$") + var/return_name = copytext(ID, 2) + switch(return_name) + if("metal") + return_name = "Metal" + if("glass") + return_name = "Glass" + if("gold") + return_name = "Gold" + if("silver") + return_name = "Silver" + if("plasma") + return_name = "Solid Plasma" + if("uranium") + return_name = "Uranium" + if("diamond") + return_name = "Diamond" + if("clown") + return_name = "Bananium" + if("mime") + return_name = "Tranquillite" + if("titanium") + return_name = "Titanium" + if("bluespace") + return_name = "Bluespace Mesh" + if("plastic") + return_name = "Plastic" + return return_name + else + for(var/R in subtypesof(/datum/reagent)) + var/datum/reagent/rt = R + if(initial(rt.id) == ID) + return initial(rt.name) + +/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any). + for(var/obj/machinery/r_n_d/D in range(3,src)) + if(!isnull(D.linked_console) || D.disabled || D.panel_open) + continue + if(istype(D, /obj/machinery/r_n_d/destructive_analyzer)) + if(linked_destroy == null) + linked_destroy = D + D.linked_console = src + else if(istype(D, /obj/machinery/r_n_d/protolathe)) + if(linked_lathe == null) + linked_lathe = D + D.linked_console = src + else if(istype(D, /obj/machinery/r_n_d/circuit_imprinter)) + if(linked_imprinter == null) + linked_imprinter = D + D.linked_console = src + return + +//Have it automatically push research to the centcom server so wild griffins can't fuck up R&D's work --NEO +/obj/machinery/computer/rdconsole/proc/griefProtection() + for(var/obj/machinery/r_n_d/server/centcom/C in world) + files.push_data(C.files) + +/obj/machinery/computer/rdconsole/proc/Maximize() + for(var/datum/tech/T in files.possible_tech) + files.known_tech[T.id] = T + for(var/v in files.known_tech) + var/datum/tech/KT = files.known_tech[v] + if(KT.level < KT.max_level) + KT.level=KT.max_level + files.RefreshResearch() + +/obj/machinery/computer/rdconsole/New() + ..() + files = new /datum/research(src) //Setup the research data holder. + matching_designs = list() + if(!id) + for(var/obj/machinery/r_n_d/server/centcom/S in world) + S.initialize_serv() + break + +/obj/machinery/computer/rdconsole/Initialize() + ..() + SyncRDevices() + +/obj/machinery/computer/rdconsole/Destroy() + if(wait_message_timer) + deltimer(wait_message_timer) + wait_message_timer = 0 + return ..() + +/* Instead of calling this every tick, it is only being called when needed +/obj/machinery/computer/rdconsole/process() + griefProtection() +*/ + +/obj/machinery/computer/rdconsole/attackby(var/obj/item/D as obj, var/mob/user as mob, params) + + //Loading a disk into it. + if(istype(D, /obj/item/disk)) + if(t_disk || d_disk) + to_chat(user, "A disk is already loaded into the machine.") + return + + if(istype(D, /obj/item/disk/tech_disk)) t_disk = D + else if(istype(D, /obj/item/disk/design_disk)) d_disk = D + else + to_chat(user, "Machine cannot accept disks in that format.") + return + if(!user.drop_item()) + return + D.loc = src + to_chat(user, "You add the disk to the machine!") + else if(!(linked_destroy && linked_destroy.busy) && !(linked_lathe && linked_lathe.busy) && !(linked_imprinter && linked_imprinter.busy)) + ..() + SSnanoui.update_uis(src) + return + +/obj/machinery/computer/rdconsole/emag_act(user as mob) + if(!emagged) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + req_access = list() + emagged = 1 + to_chat(user, "You disable the security protocols") + +/obj/machinery/computer/rdconsole/Topic(href, href_list) + if(..()) + return 1 + + if(!allowed(usr) && !isobserver(usr)) + return 1 + + add_fingerprint(usr) + + usr.set_machine(src) + if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. + var/temp_screen = text2num(href_list["menu"]) + menu = temp_screen + if(href_list["submenu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. + var/temp_screen = text2num(href_list["submenu"]) + submenu = temp_screen + + if(href_list["category"]) + var/compare + + matching_designs.Cut() + + if(menu == 4) + compare = PROTOLATHE + else + compare = IMPRINTER + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(D.build_type & compare)) + continue + if(href_list["category"] in D.category) + matching_designs.Add(D) + submenu = 1 + + selected_category = "Viewing Category [href_list["category"]]" + + else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk. + add_wait_message("Updating Database...", TECH_UPDATE_DELAY) + spawn(TECH_UPDATE_DELAY) + clear_wait_message() + files.AddTech2Known(t_disk.stored) + SSnanoui.update_uis(src) + griefProtection() //Update centcom too + + else if(href_list["clear_tech"]) //Erase data on the technology disk. + if(t_disk) + t_disk.wipe_tech() + + else if(href_list["eject_tech"]) //Eject the technology disk. + if(t_disk) + t_disk.loc = src.loc + t_disk = null + menu = 0 + submenu = 0 + + else if(href_list["copy_tech"]) //Copy some technology data from the research holder to the disk. + // Somehow this href makes me very nervous + t_disk.stored = files.known_tech[href_list["copy_tech_ID"]] + menu = 2 + submenu = 0 + + else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk. + add_wait_message("Updating Database...", DESIGN_UPDATE_DELAY) + spawn(DESIGN_UPDATE_DELAY) + clear_wait_message() + files.AddDesign2Known(d_disk.blueprint) + SSnanoui.update_uis(src) + griefProtection() //Update centcom too + + else if(href_list["clear_design"]) //Erases data on the design disk. + if(d_disk) + d_disk.wipe_blueprint() + + else if(href_list["eject_design"]) //Eject the design disk. + if(d_disk) + d_disk.loc = src.loc + d_disk = null + menu = 0 + submenu = 0 + + else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk. + // This href ALSO makes me very nervous + var/datum/design/D = files.known_designs[href_list["copy_design_ID"]] + if(D) + // eeeeeep design datums are global be careful! + var/autolathe_friendly = 1 + for(var/x in D.materials) + if( !(x in list(MAT_METAL, MAT_GLASS))) + autolathe_friendly = 0 + D.category -= "Imported" + if(D.locked) + autolathe_friendly = 0 + D.category -= "Imported" + if(D.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)) // Specifically excludes circuit imprinter and mechfab + D.build_type = autolathe_friendly ? (D.build_type | AUTOLATHE) : D.build_type + D.category |= "Imported" + d_disk.blueprint = D + menu = 2 + submenu = 0 + + else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer. + if(linked_destroy) + if(linked_destroy.busy) + to_chat(usr, " The destructive analyzer is busy at the moment.") + + else if(linked_destroy.loaded_item) + linked_destroy.loaded_item.loc = linked_destroy.loc + linked_destroy.loaded_item = null + linked_destroy.icon_state = "d_analyzer" + menu = 3 + + else if(href_list["maxresearch"]) //Eject the item inside the destructive analyzer. + if(!check_rights(R_ADMIN)) + return + if(alert("Are you sure you want to maximize research levels?","Confirmation","Yes","No")=="No") + return + log_admin("[key_name(usr)] has maximized the research levels.") + message_admins("[key_name_admin(usr)] has maximized the research levels.") + Maximize() + SSnanoui.update_uis(src) + griefProtection() //Update centcomm too + + else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder. + if(linked_destroy) + if(linked_destroy.busy) + to_chat(usr, "The destructive analyzer is busy at the moment.") + return + var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) + var/cancontinue = FALSE + for(var/T in temp_tech) + if(files.IsTechHigher(T, temp_tech[T])) + cancontinue = TRUE + break + if(!cancontinue) + var/choice = input("This item does not raise tech levels. Proceed destroying loaded item anyway?") in list("Proceed", "Cancel") + if(choice == "Cancel" || !linked_destroy) + return + linked_destroy.busy = 1 + add_wait_message("Processing and Updating Database...", DECONSTRUCT_DELAY) + SSnanoui.update_uis(src) + flick("d_analyzer_process", linked_destroy) + spawn(DECONSTRUCT_DELAY) + clear_wait_message() + if(linked_destroy) + linked_destroy.busy = 0 + if(!linked_destroy.hacked) + if(!linked_destroy.loaded_item) + to_chat(usr, "The destructive analyzer appears to be empty.") + menu = 0 + submenu = 0 + return + for(var/T in temp_tech) + var/datum/tech/KT = files.known_tech[T] //For stat logging of high levels + if(files.IsTechHigher(T, temp_tech[T]) && KT.level >= 5) //For stat logging of high levels + feedback_add_details("high_research_level","[KT][KT.level + 1]") //+1 to show the level which we're about to get + files.UpdateTech(T, temp_tech[T]) + menu = 0 + submenu = 0 + if(linked_lathe) //Also sends salvaged materials to a linked protolathe, if any. + for(var/material in linked_destroy.loaded_item.materials) + var/can_insert = min(linked_lathe.materials.max_amount - linked_lathe.materials.total_amount, linked_destroy.loaded_item.materials[material] * (linked_destroy.decon_mod / 10), linked_destroy.loaded_item.materials[material]) + linked_lathe.materials.insert_amount(can_insert, material) + linked_destroy.loaded_item = null + else + menu = 0 + submenu = 0 + for(var/obj/I in linked_destroy.contents) + for(var/mob/M in I.contents) + M.death() + if(istype(I,/obj/item/stack/sheet))//Only deconsturcts one sheet at a time instead of the entire stack + var/obj/item/stack/sheet/S = I + if(S.amount > 1) + S.amount-- + linked_destroy.loaded_item = S + else + qdel(S) + linked_destroy.icon_state = "d_analyzer" + else + if(!(I in linked_destroy.component_parts)) + qdel(I) + linked_destroy.icon_state = "d_analyzer" + use_power(250) + SSnanoui.update_uis(src) + + else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected. + if(!sync) + to_chat(usr, "You must connect to the network first!") + else + add_wait_message("Updating Database...", SYNC_RESEARCH_DELAY) + griefProtection() //Putting this here because I dont trust the sync process + spawn(SYNC_RESEARCH_DELAY) + clear_wait_message() + if(src) + for(var/obj/machinery/r_n_d/server/S in world) + var/server_processed = 0 + if(S.disabled) + continue + if((id in S.id_with_upload) || istype(S, /obj/machinery/r_n_d/server/centcom)) + files.push_data(S.files) + server_processed = 1 + if(((id in S.id_with_download) && !istype(S, /obj/machinery/r_n_d/server/centcom)) || S.hacked) + S.files.push_data(files) + server_processed = 1 + if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed) + S.produce_heat(100) + SSnanoui.update_uis(src) + + else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data. + sync = !sync + + else if(href_list["build"]) //Causes the Protolathe to build something. + if(linked_lathe) + if(linked_lathe.busy) + to_chat(usr, "Protolathe is busy at the moment.") + return + var/coeff = linked_lathe.efficiency_coeff + var/g2g = 1 + var/datum/design/being_built = files.known_designs[href_list["build"]] + if(being_built) + var/power = 2000 + var/amount=text2num(href_list["amount"]) + if(being_built.make_reagents.len) + return 0 + amount = max(1, min(10, amount)) + for(var/M in being_built.materials) + power += round(being_built.materials[M] * amount / 5) + power = max(2000, power) + var/key = usr.key //so we don't lose the info during the spawn delay + if(!(being_built.build_type & PROTOLATHE)) + g2g = 0 + message_admins("Protolathe exploit attempted by [key_name(usr, TRUE)]!") + + if(g2g) //If input is incorrect, nothing happens + var/new_coeff = coeff * being_built.lathe_time_factor + var/time_to_construct = PROTOLATHE_CONSTRUCT_DELAY * new_coeff * amount ** 0.8 + var/enough_materials = 1 + + add_wait_message("Constructing Prototype. Please Wait...", time_to_construct) + linked_lathe.busy = 1 + flick("protolathe_n",linked_lathe) + use_power(power) + + var/list/efficient_mats = list() + for(var/MAT in being_built.materials) + efficient_mats[MAT] = being_built.materials[MAT]*coeff + + if(!linked_lathe.materials.has_materials(efficient_mats, amount)) + src.visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") + enough_materials = 0 + g2g = 0 + else + for(var/R in being_built.reagents_list) + if(!linked_lathe.reagents.has_reagent(R, being_built.reagents_list[R])*coeff) + src.visible_message("The [src.name] beeps, \"Not enough reagents to complete prototype.\"") + enough_materials = 0 + g2g = 0 + + if(enough_materials) + linked_lathe.materials.use_amount(efficient_mats, amount) + for(var/R in being_built.reagents_list) + linked_lathe.reagents.remove_reagent(R, being_built.reagents_list[R]*coeff) + + var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. + var/O = being_built.locked + + spawn(time_to_construct) + if(g2g) //And if we only fail the material requirements, we still spend time and power + for(var/i = 0, iCircuit Imprinter is busy at the moment.") + return + var/datum/design/being_built = null + being_built = files.known_designs[href_list["imprint"]] + if(being_built) + var/power = 2000 + for(var/M in being_built.materials) + power += round(being_built.materials[M] / 5) + power = max(2000, power) + if(!(being_built.build_type & IMPRINTER)) + g2g = 0 + message_admins("Circuit imprinter exploit attempted by [key_name(usr, TRUE)]!") + + if(g2g) //Again, if input is wrong, do nothing + add_wait_message("Imprinting Circuit. Please Wait...", IMPRINTER_DELAY) + linked_imprinter.busy = 1 + flick("circuit_imprinter_ani",linked_imprinter) + use_power(power) + + var/list/efficient_mats = list() + for(var/MAT in being_built.materials) + efficient_mats[MAT] = being_built.materials[MAT]/coeff + + if(!linked_imprinter.materials.has_materials(efficient_mats)) + visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") + enough_materials = 0 + g2g = 0 + else + for(var/R in being_built.reagents_list) + if(!linked_imprinter.reagents.has_reagent(R, being_built.reagents_list[R]/coeff)) + visible_message("The [name] beeps, \"Not enough reagents to complete prototype.\"") + enough_materials = 0 + g2g = 0 + + if(enough_materials) + linked_imprinter.materials.use_amount(efficient_mats) + for(var/R in being_built.reagents_list) + linked_imprinter.reagents.remove_reagent(R, being_built.reagents_list[R]/coeff) + + var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. + spawn(IMPRINTER_DELAY) + if(g2g) + var/obj/item/new_item = new P(src) + new_item.loc = linked_imprinter.loc + linked_imprinter.busy = 0 + clear_wait_message() + SSnanoui.update_uis(src) + + else if(href_list["disposeI"] && linked_imprinter) //Causes the circuit imprinter to dispose of a single reagent (all of it) + linked_imprinter.reagents.del_reagent(href_list["disposeI"]) + + else if(href_list["disposeallI"] && linked_imprinter) //Causes the circuit imprinter to dispose of all it's reagents. + linked_imprinter.reagents.clear_reagents() + + else if(href_list["disposeP"] && linked_lathe) //Causes the protolathe to dispose of a single reagent (all of it) + linked_lathe.reagents.del_reagent(href_list["disposeP"]) + + else if(href_list["disposeallP"] && linked_lathe) //Causes the protolathe to dispose of all it's reagents. + linked_lathe.reagents.clear_reagents() + + else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material + var/desired_num_sheets + if(href_list["lathe_ejectsheet_amt"] == "custom") + desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num + desired_num_sheets = max(0,desired_num_sheets) // If you input too high of a number, the mineral datum will take care of it either way + if(!desired_num_sheets) + return + desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery + else + desired_num_sheets = text2num(href_list["lathe_ejectsheet_amt"]) + linked_lathe.materials.retrieve_sheets(desired_num_sheets, href_list["lathe_ejectsheet"]) + + else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material + var/desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) + if(href_list["imprinter_ejectsheet_amt"] == "custom") + desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num + desired_num_sheets = max(0,desired_num_sheets) // for the imprinter they have something hacky, that still will guard against shenanigans. eh + if(!desired_num_sheets) + return + desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery + else + desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) + linked_imprinter.materials.retrieve_sheets(desired_num_sheets, href_list["imprinter_ejectsheet"]) + + else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with. + add_wait_message("Updating Database...", SYNC_DEVICE_DELAY) + spawn(SYNC_DEVICE_DELAY) + SyncRDevices() + clear_wait_message() + SSnanoui.update_uis(src) + + else if(href_list["disconnect"]) //The R&D console disconnects with a specific device. + switch(href_list["disconnect"]) + if("destroy") + linked_destroy.linked_console = null + linked_destroy = null + if("lathe") + linked_lathe.linked_console = null + linked_lathe = null + if("imprinter") + linked_imprinter.linked_console = null + linked_imprinter = null + + else if(href_list["reset"]) //Reset the R&D console's database. + griefProtection() + var/choice = alert("Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "R&D Console Database Reset", "Continue", "Cancel") + if(choice == "Continue") + add_wait_message("Updating Database...", RESET_RESEARCH_DELAY) + qdel(files) + files = new /datum/research(src) + spawn(RESET_RESEARCH_DELAY) + clear_wait_message() + SSnanoui.update_uis(src) + + else if(href_list["search"]) //Search for designs with name matching pattern + var/compare + + matching_designs.Cut() + + if(menu == 4) + compare = PROTOLATHE + else + compare = IMPRINTER + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(D.build_type & compare)) + continue + if(findtext(D.name,href_list["to_search"])) + matching_designs.Add(D) + submenu = 1 + + selected_category = "Search Results for '[href_list["to_search"]]'" + + SSnanoui.update_uis(src) + return + + +/obj/machinery/computer/rdconsole/attack_hand(mob/user as mob) + if(..()) + return 1 + if(!allowed(user) && !isobserver(user)) + to_chat(user, "Access denied.") + return 1 + ui_interact(user) + +/obj/machinery/computer/rdconsole/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "r_n_d.tmpl", src.name, 800, 550) + ui.open() + +/obj/machinery/computer/rdconsole/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) + var/data[0] + + files.RefreshResearch() + + data["menu"] = menu + data["submenu"] = submenu + data["wait_message"] = wait_message + data["src_ref"] = UID() + + data["linked_destroy"] = linked_destroy ? 1 : 0 + data["linked_lathe"] = linked_lathe ? 1 : 0 + data["linked_imprinter"] = linked_imprinter ? 1 : 0 + data["sync"] = sync + data["admin"] = check_rights(R_ADMIN,0) + data["disk_type"] = d_disk ? 2 : (t_disk ? 1 : 0) + data["category"] = selected_category + + if(menu == 0 || menu == 1) + var/list/tech_levels = list() + data["tech_levels"] = tech_levels + for(var/v in files.known_tech) + var/datum/tech/T = files.known_tech[v] + if(T.level <= 0) + continue + var/list/this_tech_list = list() + this_tech_list["name"] = T.name + this_tech_list["level"] = T.level + this_tech_list["desc"] = T.desc + tech_levels[++tech_levels.len] = this_tech_list + + if(menu == 2) + + if(t_disk != null && t_disk.stored != null && submenu == 0) //Technology Disk Menu + var/list/disk_data = list() + data["disk_data"] = disk_data + disk_data["name"] = t_disk.stored.name + disk_data["level"] = t_disk.stored.level + disk_data["desc"] = t_disk.stored.desc + + if(t_disk != null && submenu == 1) + var/list/to_copy = list() + data["to_copy"] = to_copy + for(var/v in files.known_tech) + var/datum/tech/T = files.known_tech[v] + var/list/item = list() + to_copy[++to_copy.len] = item + if(T.level <= 0) + continue + item["name"] = T.name + item["id"] = T.id + + if(d_disk != null && d_disk.blueprint != null && submenu == 0) + var/list/disk_data = list() + data["disk_data"] = disk_data + disk_data["name"] = d_disk.blueprint.name + var/b_type = d_disk.blueprint.build_type + var/list/lathe_types = list() + disk_data["lathe_types"] = lathe_types + if(b_type) + if(b_type & IMPRINTER) lathe_types += "Circuit Imprinter" + if(b_type & PROTOLATHE) lathe_types += "Protolathe" + if(b_type & AUTOLATHE) lathe_types += "Autolathe" + if(b_type & MECHFAB) lathe_types += "Mech Fabricator" + if(b_type & PODFAB) lathe_types += "Spacepod Fabricator" + if(b_type & BIOGENERATOR) lathe_types += "Biogenerator" + if(b_type & SMELTER) lathe_types += "Smelter" + var/list/materials = list() + disk_data["materials"] = materials + for(var/M in d_disk.blueprint.materials) + var/list/material = list() + materials[++materials.len] = material + material["name"] = CallMaterialName(M) + material["amount"] = d_disk.blueprint.materials[M] + + if(d_disk != null && submenu == 1) + var/list/to_copy = list() + data["to_copy"] = to_copy + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + var/list/item = list() + to_copy[++to_copy.len] = item + item["name"] = D.name + item["id"] = D.id + + if(menu == 3 && linked_destroy && linked_destroy.loaded_item) + var/list/loaded_item_list = list() + data["loaded_item"] = loaded_item_list + loaded_item_list["name"] = linked_destroy.loaded_item.name + var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) + var/list/tech_list = list() + loaded_item_list["origin_tech"] = tech_list + for(var/T in temp_tech) + var/list/tech_item = list() + tech_list[++tech_list.len] = tech_item + tech_item["name"] = CallTechName(T) + tech_item["object_level"] = temp_tech[T] + for(var/v in files.known_tech) + var/datum/tech/F = files.known_tech[v] + if(F.name == CallTechName(T)) + tech_item["current_level"] = F.level + break + + if(menu == 4 && linked_lathe) + data["total_materials"] = linked_lathe.materials.total_amount + data["max_materials"] = linked_lathe.materials.max_amount + data["total_chemicals"] = linked_lathe.reagents.total_volume + data["max_chemicals"] = linked_lathe.reagents.maximum_volume + data["categories"] = linked_lathe.categories + if(submenu == 1) + var/list/designs_list = list() + data["matching_designs"] = designs_list + var/coeff = linked_lathe.efficiency_coeff + for(var/datum/design/D in matching_designs) + var/list/design_list = list() + designs_list[++designs_list.len] = design_list + var/list/materials_list = list() + design_list["materials"] = materials_list + design_list["id"] = D.id + design_list["name"] = sanitize(D.name) + var/c = 50 + for(var/M in D.materials) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(M) + material_list["amount"] = D.materials[M]*coeff + var/t = linked_lathe.check_mat(D, M) + + if(t < 1) + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + c = min(c, t) + + for(var/R in D.reagents_list) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(R) + material_list["amount"] = D.reagents_list[R]*coeff + var/t = linked_lathe.check_mat(D, R) + + if(t < 1) + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + c = min(c, t) + design_list["can_build"] = c + if(submenu == 2) + var/list/materials_list = list() + data["loaded_materials"] = materials_list + materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_lathe.materials.amount(MAT_METAL)) + materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_lathe.materials.amount(MAT_GLASS)) + materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_lathe.materials.amount(MAT_GOLD)) + materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_lathe.materials.amount(MAT_SILVER)) + materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_lathe.materials.amount(MAT_PLASMA)) + materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_lathe.materials.amount(MAT_URANIUM)) + materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_lathe.materials.amount(MAT_DIAMOND)) + materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_lathe.materials.amount(MAT_BANANIUM)) + materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_lathe.materials.amount(MAT_TRANQUILLITE)) + materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_lathe.materials.amount(MAT_TITANIUM)) + materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_lathe.materials.amount(MAT_PLASTIC)) + materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_lathe.materials.amount(MAT_BLUESPACE)) + if(submenu == 3) + var/list/loaded_chemicals = list() + data["loaded_chemicals"] = loaded_chemicals + for(var/datum/reagent/R in linked_lathe.reagents.reagent_list) + var/list/loaded_chemical = list() + loaded_chemicals[++loaded_chemicals.len] = loaded_chemical + loaded_chemical["name"] = R.name + loaded_chemical["volume"] = R.volume + loaded_chemical["id"] = R.id + + if(menu == 5 && linked_imprinter) + data["total_materials"] = linked_imprinter.materials.total_amount + data["max_materials"] = linked_imprinter.materials.max_amount + data["total_chemicals"] = linked_imprinter.reagents.total_volume + data["max_chemicals"] = linked_imprinter.reagents.maximum_volume + data["categories"] = linked_imprinter.categories + if(submenu == 1) + var/list/designs_list = list() + data["matching_designs"] = designs_list + var/coeff = linked_imprinter.efficiency_coeff + for(var/datum/design/D in matching_designs) + var/list/design_list = list() + designs_list[++designs_list.len] = design_list + var/list/materials_list = list() + design_list["materials"] = materials_list + design_list["id"] = D.id + design_list["name"] = sanitize(D.name) + var/check_materials = 1 + for(var/M in D.materials) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(M) + material_list["amount"] = D.materials[M] / coeff + if(!linked_imprinter.check_mat(D, M)) + check_materials = 0 + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + + for(var/R in D.reagents_list) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(R) + material_list["amount"] = D.reagents_list[R]*coeff + if(!linked_imprinter.check_mat(D, R)) + check_materials = 0 + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + + design_list["can_build"] = check_materials + if(submenu == 2) + var/list/materials_list = list() + data["loaded_materials"] = materials_list + materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_imprinter.materials.amount(MAT_METAL)) + materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_imprinter.materials.amount(MAT_GLASS)) + materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_imprinter.materials.amount(MAT_GOLD)) + materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_imprinter.materials.amount(MAT_SILVER)) + materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_imprinter.materials.amount(MAT_PLASMA)) + materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_imprinter.materials.amount(MAT_URANIUM)) + materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_imprinter.materials.amount(MAT_DIAMOND)) + materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_imprinter.materials.amount(MAT_BANANIUM)) + materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_imprinter.materials.amount(MAT_TRANQUILLITE)) + materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_imprinter.materials.amount(MAT_TITANIUM)) + materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_imprinter.materials.amount(MAT_PLASTIC)) + materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_imprinter.materials.amount(MAT_BLUESPACE)) + if(submenu == 3) + var/list/loaded_chemicals = list() + data["loaded_chemicals"] = loaded_chemicals + for(var/datum/reagent/R in linked_imprinter.reagents.reagent_list) + var/list/loaded_chemical = list() + loaded_chemicals[++loaded_chemicals.len] = loaded_chemical + loaded_chemical["name"] = R.name + loaded_chemical["volume"] = R.volume + loaded_chemical["id"] = R.id + + return data + +//helper proc that guarantees the wait message will not freeze the UI +/obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay) + wait_message = message + wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE) + +// This is here to guarantee that we never lock the console, so long as the timer +// process is running +// So long as the spawns never runtime, though, the timer should be stopped +// before it gets the chance to fire +// Since the timer process can have significant delays, you should call this +// in the operations that take time, once they complete +/obj/machinery/computer/rdconsole/proc/clear_wait_message() + wait_message = "" + if(wait_message_timer) + // This could be expensive, and will still be called + // if the timer calls this function + deltimer(wait_message_timer) + wait_message_timer = 0 + SSnanoui.update_uis(src) + + +/obj/machinery/computer/rdconsole/core + name = "core R&D console" + desc = "A console used to interface with R&D tools." + id = 1 + +/obj/machinery/computer/rdconsole/robotics + name = "robotics R&D console" + desc = "A console used to interface with R&D tools." + id = 2 + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/rdconsole/robotics + +/obj/machinery/computer/rdconsole/experiment + name = "\improper E.X.P.E.R.I-MENTOR R&D console" + desc = "A console used to interface with R&D tools." + id = 3 + circuit = /obj/item/circuitboard/rdconsole/experiment + +/obj/machinery/computer/rdconsole/mechanics + name = "mechanics R&D console" + desc = "A console used to interface with R&D tools." + id = 4 + req_access = list(ACCESS_MECHANIC) + circuit = /obj/item/circuitboard/rdconsole/mechanics + +/obj/machinery/computer/rdconsole/public + name = "public R&D console" + desc = "A console used to interface with R&D tools." + id = 5 + req_access = list() + circuit = /obj/item/circuitboard/rdconsole/public + +#undef TECH_UPDATE_DELAY +#undef DESIGN_UPDATE_DELAY +#undef PROTOLATHE_CONSTRUCT_DELAY +#undef SYNC_RESEARCH_DELAY +#undef DECONSTRUCT_DELAY +#undef SYNC_DEVICE_DELAY +#undef RESET_RESEARCH_DELAY +#undef IMPRINTER_DELAY diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm index f6fc35932bce..7acdc53ce3d6 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -1,133 +1,133 @@ -//All devices that link into the R&D console fall into thise type for easy identification and some shared procs. - - -/obj/machinery/r_n_d - name = "R&D Device" - icon = 'icons/obj/machines/research.dmi' - density = 1 - anchored = 1 - use_power = IDLE_POWER_USE - var/busy = 0 - var/hacked = 0 - var/disabled = 0 - var/shocked = 0 - var/list/wires = list() - var/hack_wire - var/disable_wire - var/shock_wire - var/obj/machinery/computer/rdconsole/linked_console - var/obj/item/loaded_item = null - var/datum/component/material_container/materials //Store for hyper speed! - -/obj/machinery/r_n_d/New() - materials = AddComponent(/datum/component/material_container, - list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0, - TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) - materials.precise_insertion = TRUE - ..() - wires["Red"] = 0 - wires["Blue"] = 0 - wires["Green"] = 0 - wires["Yellow"] = 0 - wires["Black"] = 0 - wires["White"] = 0 - var/list/w = list("Red","Blue","Green","Yellow","Black","White") - src.hack_wire = pick(w) - w -= src.hack_wire - src.shock_wire = pick(w) - w -= src.shock_wire - src.disable_wire = pick(w) - w -= src.disable_wire - -/obj/machinery/r_n_d/attack_hand(mob/user as mob) - if(shocked) - shock(user,50) - if(panel_open) - var/list/dat = list() - dat += "[src.name] Wires:
    " - for(var/wire in wires) - dat += "[wire] Wire: [src.wires[wire] ? "Mend" : "Cut"] Pulse
    " - - dat += "The red light is [src.disabled ? "off" : "on"].
    " - dat += "The green light is [src.shocked ? "off" : "on"].
    " - dat += "The blue light is [src.hacked ? "off" : "on"].
    " - user << browse("[src.name] Hacking[dat.Join("")]","window=hack_win") - return - - -/obj/machinery/r_n_d/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) - if(href_list["pulse"]) - var/temp_wire = href_list["wire"] - if(!istype(usr.get_active_hand(), /obj/item/multitool)) - to_chat(usr, "You need a multitool!") - else - if(src.wires[temp_wire]) - to_chat(usr, "You can't pulse a cut wire.") - else - if(src.hack_wire == href_list["wire"]) - src.hacked = !src.hacked - spawn(100) src.hacked = !src.hacked - if(src.disable_wire == href_list["wire"]) - src.disabled = !src.disabled - src.shock(usr,50) - spawn(100) src.disabled = !src.disabled - if(src.shock_wire == href_list["wire"]) - src.shocked = !src.shocked - src.shock(usr,50) - spawn(100) src.shocked = !src.shocked - if(href_list["cut"]) - if(!istype(usr.get_active_hand(), /obj/item/wirecutters)) - to_chat(usr, "You need wirecutters!") - else - var/temp_wire = href_list["wire"] - wires[temp_wire] = !wires[temp_wire] - if(src.hack_wire == temp_wire) - src.hacked = !src.hacked - if(src.disable_wire == temp_wire) - src.disabled = !src.disabled - src.shock(usr,50) - if(src.shock_wire == temp_wire) - src.shocked = !src.shocked - src.shock(usr,50) - src.updateUsrDialog() - -//whether the machine can have an item inserted in its current state. -/obj/machinery/r_n_d/proc/is_insertion_ready(mob/user) - if(panel_open) - to_chat(user, "You can't load [src] while it's opened!") - return FALSE - if(disabled) - return FALSE - if(!linked_console) - to_chat(user, "[src] must be linked to an R&D console first!") - return FALSE - if(busy) - to_chat(user, "[src] is busy right now.") - return FALSE - if(stat & BROKEN) - to_chat(user, "[src] is broken.") - return FALSE - if(stat & NOPOWER) - to_chat(user, "[src] has no power.") - return FALSE - if(loaded_item) - to_chat(user, "[src] is already loaded.") - return FALSE - return TRUE - -/obj/machinery/r_n_d/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - var/stack_name - if(ispath(type_inserted, /obj/item/stack/ore/bluespace_crystal)) - stack_name = "bluespace polycrystal" - use_power(MINERAL_MATERIAL_AMOUNT / 10) - else - var/obj/item/stack/S = type_inserted - stack_name = initial(S.name) - use_power(min(1000, (amount_inserted / 100))) - overlays += "[initial(name)]_[stack_name]" - sleep(10) - overlays -= "[initial(name)]_[stack_name]" +//All devices that link into the R&D console fall into thise type for easy identification and some shared procs. + + +/obj/machinery/r_n_d + name = "R&D Device" + icon = 'icons/obj/machines/research.dmi' + density = 1 + anchored = 1 + use_power = IDLE_POWER_USE + var/busy = 0 + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/list/wires = list() + var/hack_wire + var/disable_wire + var/shock_wire + var/obj/machinery/computer/rdconsole/linked_console + var/obj/item/loaded_item = null + var/datum/component/material_container/materials //Store for hyper speed! + +/obj/machinery/r_n_d/New() + materials = AddComponent(/datum/component/material_container, + list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0, + TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + materials.precise_insertion = TRUE + ..() + wires["Red"] = 0 + wires["Blue"] = 0 + wires["Green"] = 0 + wires["Yellow"] = 0 + wires["Black"] = 0 + wires["White"] = 0 + var/list/w = list("Red","Blue","Green","Yellow","Black","White") + src.hack_wire = pick(w) + w -= src.hack_wire + src.shock_wire = pick(w) + w -= src.shock_wire + src.disable_wire = pick(w) + w -= src.disable_wire + +/obj/machinery/r_n_d/attack_hand(mob/user as mob) + if(shocked) + shock(user,50) + if(panel_open) + var/list/dat = list() + dat += "[src.name] Wires:
    " + for(var/wire in wires) + dat += "[wire] Wire: [src.wires[wire] ? "Mend" : "Cut"] Pulse
    " + + dat += "The red light is [src.disabled ? "off" : "on"].
    " + dat += "The green light is [src.shocked ? "off" : "on"].
    " + dat += "The blue light is [src.hacked ? "off" : "on"].
    " + user << browse("[src.name] Hacking[dat.Join("")]","window=hack_win") + return + + +/obj/machinery/r_n_d/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + src.add_fingerprint(usr) + if(href_list["pulse"]) + var/temp_wire = href_list["wire"] + if(!istype(usr.get_active_hand(), /obj/item/multitool)) + to_chat(usr, "You need a multitool!") + else + if(src.wires[temp_wire]) + to_chat(usr, "You can't pulse a cut wire.") + else + if(src.hack_wire == href_list["wire"]) + src.hacked = !src.hacked + spawn(100) src.hacked = !src.hacked + if(src.disable_wire == href_list["wire"]) + src.disabled = !src.disabled + src.shock(usr,50) + spawn(100) src.disabled = !src.disabled + if(src.shock_wire == href_list["wire"]) + src.shocked = !src.shocked + src.shock(usr,50) + spawn(100) src.shocked = !src.shocked + if(href_list["cut"]) + if(!istype(usr.get_active_hand(), /obj/item/wirecutters)) + to_chat(usr, "You need wirecutters!") + else + var/temp_wire = href_list["wire"] + wires[temp_wire] = !wires[temp_wire] + if(src.hack_wire == temp_wire) + src.hacked = !src.hacked + if(src.disable_wire == temp_wire) + src.disabled = !src.disabled + src.shock(usr,50) + if(src.shock_wire == temp_wire) + src.shocked = !src.shocked + src.shock(usr,50) + src.updateUsrDialog() + +//whether the machine can have an item inserted in its current state. +/obj/machinery/r_n_d/proc/is_insertion_ready(mob/user) + if(panel_open) + to_chat(user, "You can't load [src] while it's opened!") + return FALSE + if(disabled) + return FALSE + if(!linked_console) + to_chat(user, "[src] must be linked to an R&D console first!") + return FALSE + if(busy) + to_chat(user, "[src] is busy right now.") + return FALSE + if(stat & BROKEN) + to_chat(user, "[src] is broken.") + return FALSE + if(stat & NOPOWER) + to_chat(user, "[src] has no power.") + return FALSE + if(loaded_item) + to_chat(user, "[src] is already loaded.") + return FALSE + return TRUE + +/obj/machinery/r_n_d/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + var/stack_name + if(ispath(type_inserted, /obj/item/stack/ore/bluespace_crystal)) + stack_name = "bluespace polycrystal" + use_power(MINERAL_MATERIAL_AMOUNT / 10) + else + var/obj/item/stack/S = type_inserted + stack_name = initial(S.name) + use_power(min(1000, (amount_inserted / 100))) + overlays += "[initial(name)]_[stack_name]" + sleep(10) + overlays -= "[initial(name)]_[stack_name]" diff --git a/code/modules/research/research.dm b/code/modules/research/research.dm index 65d9a1d3ad41..b5fc9e9c8798 100644 --- a/code/modules/research/research.dm +++ b/code/modules/research/research.dm @@ -1,400 +1,400 @@ -/* -General Explination: -The research datum is the "folder" where all the research information is stored in a R&D console. It's also a holder for all the -various procs used to manipulate it. It has four variables and seven procs: - -Variables: -- possible_tech is a list of all the /datum/tech that can potentially be researched by the player. The RefreshResearch() proc -(explained later) only goes through those when refreshing what you know. Generally, possible_tech contains ALL of the existing tech -but it is possible to add tech to the game that DON'T start in it (example: Xeno tech). Generally speaking, you don't want to mess -with these since they should be the default version of the datums. They're actually stored in a list rather then using typesof to -refer to them since it makes it a bit easier to search through them for specific information. -- know_tech is the companion list to possible_tech. It's the tech you can actually research and improve. Until it's added to this -list, it can't be improved. All the tech in this list are visible to the player. -- possible_designs is functionally identical to possbile_tech except it's for /datum/design. -- known_designs is functionally identical to known_tech except it's for /datum/design - -Procs: -- TechHasReqs: Used by other procs (specifically RefreshResearch) to see whether all of a tech's requirements are currently in -known_tech and at a high enough level. -- DesignHasReqs: Same as TechHasReqs but for /datum/design and known_design. -- AddTech2Known: Adds a /datum/tech to known_tech. It checks to see whether it already has that tech (if so, it just replaces it). If -it doesn't have it, it adds it. Note: It does NOT check possible_tech at all. So if you want to add something strange to it (like -a player made tech?) you can. -- AddDesign2Known: Same as AddTech2Known except for /datum/design and known_designs. -- RefreshResearch: This is the workhorse of the R&D system. It updates the /datum/research holder and adds any unlocked tech paths -and designs you have reached the requirements for. It only checks through possible_tech and possible_designs, however, so it won't -accidentally add "secret" tech to it. -- UpdateTech is used as part of the actual researching process. It takes an ID and finds techs with that same ID in known_tech. When -it finds it, it checks to see whether it can improve it at all. If the known_tech's level is less then or equal to -the inputted level, it increases the known tech's level to the inputted level -1 or know tech's level +1 (whichever is higher). - -The tech datums are the actual "tech trees" that you improve through researching. Each one has five variables: -- Name: Pretty obvious. This is often viewable to the players. -- Desc: Pretty obvious. Also player viewable. -- ID: This is the unique ID of the tech that is used by the various procs to find and/or maniuplate it. -- Level: This is the current level of the tech. All techs start at 1 and have a max of 20. Devices and some techs require a certain -level in specific techs before you can produce them. -- Req_tech: This is a list of the techs required to unlock this tech path. If left blank, it'll automatically be loaded into the -research holder datum. - -*/ -/*************************************************************** -** Master Types ** -** Includes all the helper procs and basic tech processing. ** -***************************************************************/ - -/datum/research //Holder for all the existing, archived, and known tech. Individual to console. - - //Datum/tech go here. - // Possible is a list of direct datum references - // known is a list of id -> datum mappings - var/list/possible_tech = list() //List of all tech in the game that players have access to (barring special events). - var/list/known_tech = list() //List of locally known tech. - var/list/possible_designs = list() //List of all designs - var/list/known_designs = list() //List of available designs - -/datum/research/New() //Insert techs into possible_tech here. Known_tech automatically updated. - // MON DIEU!!! - // These are semi-global, but not TOTALLY global? - // Using research disks, you can get techs/designs from one research datum - // onto another. What consequences this could have, I am presently unsure, but - // I imagine nothing good. - for(var/T in subtypesof(/datum/tech)) - possible_tech += new T(src) - for(var/D in subtypesof(/datum/design)) - possible_designs += new D(src) - RefreshResearch() - - - -//Checks to see if tech has all the required pre-reqs. -//Input: datum/tech; Output: 0/1 (false/true) -/datum/research/proc/TechHasReqs(var/datum/tech/T) - if(T.req_tech.len == 0) - return TRUE - for(var/req in T.req_tech) - var/datum/tech/known = known_tech[req] - if(!known || known.level < T.req_tech[req]) - return FALSE - return TRUE - -//Checks to see if design has all the required pre-reqs. -//Input: datum/design; Output: 0/1 (false/true) -/datum/research/proc/DesignHasReqs(var/datum/design/D) - if(D.req_tech.len == 0) - return TRUE - for(var/req in D.req_tech) - var/datum/tech/known = known_tech[req] - if(!known || known.level < D.req_tech[req]) - return FALSE - return TRUE - -//Adds a tech to known_tech list. Checks to make sure there aren't duplicates and updates existing tech's levels if needed. -//Input: datum/tech; Output: Null -/datum/research/proc/AddTech2Known(var/datum/tech/T) - if(T.id in known_tech) - var/datum/tech/known = known_tech[T.id] - if(T.level > known.level) - known.level = T.level - return - known_tech[T.id] = T - -/datum/research/proc/AddDesign2Known(var/datum/design/D) - if(D.id in known_designs) - return - // Global datums make me nervous - known_designs[D.id] = D - -//Refreshes known_tech and known_designs list. -//Input/Output: n/a -/datum/research/proc/RefreshResearch() - for(var/datum/tech/PT in possible_tech) - if(TechHasReqs(PT)) - AddTech2Known(PT) - for(var/datum/design/PD in possible_designs) - if(DesignHasReqs(PD)) - AddDesign2Known(PD) - for(var/v in known_tech) - var/datum/tech/T = known_tech[v] - T.level = Clamp(T.level, 0, 20) - -//Refreshes the levels of a given tech. -//Input: Tech's ID and Level; Output: null -/datum/research/proc/UpdateTech(var/ID, var/level) - var/datum/tech/KT = known_tech[ID] - if(KT) - if(KT.level <= level) - // Will bump the tech to (value_of_target) automatically - - // after that it'll bump it up by 1 until it's greater - // than the source tech - KT.level = max((KT.level + 1), level) - -//Checks if the origin level can raise current tech levels -//Input: Tech's ID and Level; Output: TRUE for yes, FALSE for no -/datum/research/proc/IsTechHigher(ID, level) - var/datum/tech/KT = known_tech[ID] - if(KT) - if(KT.level <= level) - return TRUE - else - return FALSE - -/datum/research/proc/FindDesignByID(var/id) - return known_designs[id] - -// A common task is for one research datum to copy over its techs and designs -// and update them on another research datum. -// Arguments: -// `other` - The research datum to send designs and techs to -/datum/research/proc/push_data(datum/research/other) - for(var/v in known_tech) - var/datum/tech/T = known_tech[v] - other.AddTech2Known(T) - for(var/v in known_designs) - var/datum/design/D = known_designs[v] - other.AddDesign2Known(D) - other.RefreshResearch() - - -//Autolathe files -/datum/research/autolathe - -/datum/research/autolathe/DesignHasReqs(var/datum/design/D) - return D && (D.build_type & AUTOLATHE) && ("initial" in D.category) - -/datum/research/autolathe/AddDesign2Known(var/datum/design/D) - if(!(D.build_type & AUTOLATHE)) - return - ..() - -//Biogenerator files -/datum/research/biogenerator/New() - for(var/T in (subtypesof(/datum/tech))) - possible_tech += new T(src) - for(var/path in subtypesof(/datum/design)) - var/datum/design/D = new path(src) - possible_designs += D - if((D.build_type & BIOGENERATOR) && ("initial" in D.category)) - AddDesign2Known(D) - -/datum/research/biogenerator/AddDesign2Known(datum/design/D) - if(!(D.build_type & BIOGENERATOR)) - return - ..() - -//Smelter files -/datum/research/smelter/New() - for(var/T in (subtypesof(/datum/tech))) - possible_tech += new T(src) - for(var/path in subtypesof(/datum/design)) - var/datum/design/D = new path(src) - possible_designs += D - if((D.build_type & SMELTER) && ("initial" in D.category)) - AddDesign2Known(D) - -/datum/research/smelter/AddDesign2Known(datum/design/D) - if(!(D.build_type & SMELTER)) - return - ..() - -/*************************************************************** -** Technology Datums ** -** Includes all the various technoliges and what they make. ** -***************************************************************/ - -/datum/tech //Datum of individual technologies. - var/name = "name" //Name of the technology. - var/desc = "description" //General description of what it does and what it makes. - var/id = "id" //An easily referenced ID. Must be alphanumeric, lower-case, and no symbols. - var/level = 1 //A simple number scale of the research level. Level 0 = Secret tech. - var/max_level = 1 // Maximum level this can be at (for job objectives) - var/rare = 1 //How much CentCom wants to get that tech. Used in supply shuttle tech cost calculation. - var/list/req_tech = list() //List of ids associated values of techs required to research this tech. "id" = # - - -//Trunk Technologies (don't require any other techs and you start knowning them). - -/datum/tech/materials - name = "Materials Research" - desc = "Development of new and improved materials." - id = "materials" - max_level = 7 - -/datum/tech/engineering - name = "Engineering Research" - desc = "Development of new and improved engineering parts and methods." - id = "engineering" - max_level = 7 - -/datum/tech/plasmatech - name = "Plasma Research" - desc = "Research into the mysterious substance colloqually known as 'plasma'." - id = "plasmatech" - max_level = 7 - rare = 3 - -/datum/tech/powerstorage - name = "Power Manipulation Technology" - desc = "The various technologies behind the storage and generation of electicity." - id = "powerstorage" - max_level = 7 - -/datum/tech/bluespace - name = "'Blue-space' Research" - desc = "Research into the sub-reality known as 'blue-space'." - id = "bluespace" - max_level = 7 - rare = 2 - -/datum/tech/biotech - name = "Biological Technology" - desc = "Research into the deeper mysteries of life and organic substances." - id = "biotech" - max_level = 7 - -/datum/tech/combat - name = "Combat Systems Research" - desc = "The development of offensive and defensive systems." - id = "combat" - max_level = 7 - -/datum/tech/magnets - name = "Electromagnetic Spectrum Research" - desc = "Research into the electromagnetic spectrum. No clue how they actually work, though." - id = "magnets" - max_level = 7 - -/datum/tech/programming - name = "Data Theory Research" - desc = "The development of new computer and artificial intelligence and data storage systems." - id = "programming" - max_level = 7 - -/datum/tech/toxins //not meant to be raised by deconstruction, do not give objects toxins as an origin_tech - name = "Toxins Research" - desc = "Research into plasma based explosive devices. Upgrade through testing explosives in the toxins lab." - id = "toxins" - max_level = 7 - rare = 2 - -/datum/tech/syndicate - name = "Illegal Technologies Research" - desc = "The study of technologies that violate standard Nanotrasen regulations." - id = "syndicate" - max_level = 0 // Don't count towards maxed research, since it's illegal. - rare = 4 - -/datum/tech/abductor - name = "Alien Technologies Research" - desc = "The study of technologies used by the advanced alien race known as Abductors." - id = "abductor" - rare = 5 - level = 0 - -/* -datum/tech/arcane - name = "Arcane Research" - desc = "Research into the occult and arcane field for use in practical science" - id = "arcane" - level = 0 //It didn't become "secret" as advertised. - -//Branch Techs -datum/tech/explosives - name = "Explosives Research" - desc = "The creation and application of explosive materials." - id = "explosives" - req_tech = list("materials" = 3) - -datum/tech/generators - name = "Power Generation Technology" - desc = "Research into more powerful and more reliable sources." - id = "generators" - req_tech = list("powerstorage" = 2) - -datum/tech/robotics - name = "Robotics Technology" - desc = "The development of advanced automated, autonomous machines." - id = "robotics" - req_tech = list("materials" = 3, "programming" = 3) -*/ - -/datum/tech/proc/getCost(var/current_level = null) - // Calculates tech disk's supply points sell cost - if(!current_level) - current_level = initial(level) - - if(current_level >= level) - return 0 - - var/cost = 0 - for(var/i=current_level+1, i<=level, i++) - if(i == initial(level)) - continue - cost += i*5*rare - - return cost - -/obj/item/disk/tech_disk - name = "\improper Technology Disk" - desc = "A disk for storing technology data for further research." - icon_state = "datadisk2" - materials = list(MAT_METAL=30, MAT_GLASS=10) - var/datum/tech/stored - var/default_name = "\improper Technology Disk" - var/default_desc = "A disk for storing technology data for further research." - -/obj/item/disk/tech_disk/New() - ..() - src.pixel_x = rand(-5.0, 5) - src.pixel_y = rand(-5.0, 5) - -/obj/item/disk/tech_disk/proc/load_tech(datum/tech/T) - name = "[default_name] \[[T]\]" - desc = T.desc + " Level: '[T.level]'" - // NOTE: This is just a reference to the tech on the system it grabbed it from - // This seems highly fragile - stored = T - -/obj/item/disk/tech_disk/proc/wipe_tech() - name = default_name - desc = default_desc - stored = null - -/obj/item/disk/design_disk - name = "\improper Component Design Disk" - desc = "A disk for storing device design data for construction in lathes." - icon_state = "datadisk2" - materials = list(MAT_METAL=100, MAT_GLASS=100) - var/datum/design/blueprint - // I'm doing this so that disk paths with pre-loaded designs don't get weird names - // Otherwise, I'd use "initial()" - var/default_name = "\improper Component Design Disk" - var/default_desc = "A disk for storing device design data for construction in lathes." - -/obj/item/disk/design_disk/New() - ..() - pixel_x = rand(-5, 5) - pixel_y = rand(-5, 5) - -/obj/item/disk/design_disk/proc/load_blueprint(datum/design/D) - name = "[default_name] \[[D]\]" - desc = D.desc - // NOTE: This is just a reference to the design on the system it grabbed it from - // This seems highly fragile - blueprint = D - -/obj/item/disk/design_disk/proc/wipe_blueprint() - name = default_name - desc = default_desc - blueprint = null - -/obj/item/disk/design_disk/golem_shell - name = "golem creation disk" - desc = "A gift from the Liberator." - icon_state = "datadisk1" - -/obj/item/disk/design_disk/golem_shell/Initialize() - . = ..() - var/datum/design/golem_shell/G = new - blueprint = G \ No newline at end of file +/* +General Explination: +The research datum is the "folder" where all the research information is stored in a R&D console. It's also a holder for all the +various procs used to manipulate it. It has four variables and seven procs: + +Variables: +- possible_tech is a list of all the /datum/tech that can potentially be researched by the player. The RefreshResearch() proc +(explained later) only goes through those when refreshing what you know. Generally, possible_tech contains ALL of the existing tech +but it is possible to add tech to the game that DON'T start in it (example: Xeno tech). Generally speaking, you don't want to mess +with these since they should be the default version of the datums. They're actually stored in a list rather then using typesof to +refer to them since it makes it a bit easier to search through them for specific information. +- know_tech is the companion list to possible_tech. It's the tech you can actually research and improve. Until it's added to this +list, it can't be improved. All the tech in this list are visible to the player. +- possible_designs is functionally identical to possbile_tech except it's for /datum/design. +- known_designs is functionally identical to known_tech except it's for /datum/design + +Procs: +- TechHasReqs: Used by other procs (specifically RefreshResearch) to see whether all of a tech's requirements are currently in +known_tech and at a high enough level. +- DesignHasReqs: Same as TechHasReqs but for /datum/design and known_design. +- AddTech2Known: Adds a /datum/tech to known_tech. It checks to see whether it already has that tech (if so, it just replaces it). If +it doesn't have it, it adds it. Note: It does NOT check possible_tech at all. So if you want to add something strange to it (like +a player made tech?) you can. +- AddDesign2Known: Same as AddTech2Known except for /datum/design and known_designs. +- RefreshResearch: This is the workhorse of the R&D system. It updates the /datum/research holder and adds any unlocked tech paths +and designs you have reached the requirements for. It only checks through possible_tech and possible_designs, however, so it won't +accidentally add "secret" tech to it. +- UpdateTech is used as part of the actual researching process. It takes an ID and finds techs with that same ID in known_tech. When +it finds it, it checks to see whether it can improve it at all. If the known_tech's level is less then or equal to +the inputted level, it increases the known tech's level to the inputted level -1 or know tech's level +1 (whichever is higher). + +The tech datums are the actual "tech trees" that you improve through researching. Each one has five variables: +- Name: Pretty obvious. This is often viewable to the players. +- Desc: Pretty obvious. Also player viewable. +- ID: This is the unique ID of the tech that is used by the various procs to find and/or maniuplate it. +- Level: This is the current level of the tech. All techs start at 1 and have a max of 20. Devices and some techs require a certain +level in specific techs before you can produce them. +- Req_tech: This is a list of the techs required to unlock this tech path. If left blank, it'll automatically be loaded into the +research holder datum. + +*/ +/*************************************************************** +** Master Types ** +** Includes all the helper procs and basic tech processing. ** +***************************************************************/ + +/datum/research //Holder for all the existing, archived, and known tech. Individual to console. + + //Datum/tech go here. + // Possible is a list of direct datum references + // known is a list of id -> datum mappings + var/list/possible_tech = list() //List of all tech in the game that players have access to (barring special events). + var/list/known_tech = list() //List of locally known tech. + var/list/possible_designs = list() //List of all designs + var/list/known_designs = list() //List of available designs + +/datum/research/New() //Insert techs into possible_tech here. Known_tech automatically updated. + // MON DIEU!!! + // These are semi-global, but not TOTALLY global? + // Using research disks, you can get techs/designs from one research datum + // onto another. What consequences this could have, I am presently unsure, but + // I imagine nothing good. + for(var/T in subtypesof(/datum/tech)) + possible_tech += new T(src) + for(var/D in subtypesof(/datum/design)) + possible_designs += new D(src) + RefreshResearch() + + + +//Checks to see if tech has all the required pre-reqs. +//Input: datum/tech; Output: 0/1 (false/true) +/datum/research/proc/TechHasReqs(var/datum/tech/T) + if(T.req_tech.len == 0) + return TRUE + for(var/req in T.req_tech) + var/datum/tech/known = known_tech[req] + if(!known || known.level < T.req_tech[req]) + return FALSE + return TRUE + +//Checks to see if design has all the required pre-reqs. +//Input: datum/design; Output: 0/1 (false/true) +/datum/research/proc/DesignHasReqs(var/datum/design/D) + if(D.req_tech.len == 0) + return TRUE + for(var/req in D.req_tech) + var/datum/tech/known = known_tech[req] + if(!known || known.level < D.req_tech[req]) + return FALSE + return TRUE + +//Adds a tech to known_tech list. Checks to make sure there aren't duplicates and updates existing tech's levels if needed. +//Input: datum/tech; Output: Null +/datum/research/proc/AddTech2Known(var/datum/tech/T) + if(T.id in known_tech) + var/datum/tech/known = known_tech[T.id] + if(T.level > known.level) + known.level = T.level + return + known_tech[T.id] = T + +/datum/research/proc/AddDesign2Known(var/datum/design/D) + if(D.id in known_designs) + return + // Global datums make me nervous + known_designs[D.id] = D + +//Refreshes known_tech and known_designs list. +//Input/Output: n/a +/datum/research/proc/RefreshResearch() + for(var/datum/tech/PT in possible_tech) + if(TechHasReqs(PT)) + AddTech2Known(PT) + for(var/datum/design/PD in possible_designs) + if(DesignHasReqs(PD)) + AddDesign2Known(PD) + for(var/v in known_tech) + var/datum/tech/T = known_tech[v] + T.level = Clamp(T.level, 0, 20) + +//Refreshes the levels of a given tech. +//Input: Tech's ID and Level; Output: null +/datum/research/proc/UpdateTech(var/ID, var/level) + var/datum/tech/KT = known_tech[ID] + if(KT) + if(KT.level <= level) + // Will bump the tech to (value_of_target) automatically - + // after that it'll bump it up by 1 until it's greater + // than the source tech + KT.level = max((KT.level + 1), level) + +//Checks if the origin level can raise current tech levels +//Input: Tech's ID and Level; Output: TRUE for yes, FALSE for no +/datum/research/proc/IsTechHigher(ID, level) + var/datum/tech/KT = known_tech[ID] + if(KT) + if(KT.level <= level) + return TRUE + else + return FALSE + +/datum/research/proc/FindDesignByID(var/id) + return known_designs[id] + +// A common task is for one research datum to copy over its techs and designs +// and update them on another research datum. +// Arguments: +// `other` - The research datum to send designs and techs to +/datum/research/proc/push_data(datum/research/other) + for(var/v in known_tech) + var/datum/tech/T = known_tech[v] + other.AddTech2Known(T) + for(var/v in known_designs) + var/datum/design/D = known_designs[v] + other.AddDesign2Known(D) + other.RefreshResearch() + + +//Autolathe files +/datum/research/autolathe + +/datum/research/autolathe/DesignHasReqs(var/datum/design/D) + return D && (D.build_type & AUTOLATHE) && ("initial" in D.category) + +/datum/research/autolathe/AddDesign2Known(var/datum/design/D) + if(!(D.build_type & AUTOLATHE)) + return + ..() + +//Biogenerator files +/datum/research/biogenerator/New() + for(var/T in (subtypesof(/datum/tech))) + possible_tech += new T(src) + for(var/path in subtypesof(/datum/design)) + var/datum/design/D = new path(src) + possible_designs += D + if((D.build_type & BIOGENERATOR) && ("initial" in D.category)) + AddDesign2Known(D) + +/datum/research/biogenerator/AddDesign2Known(datum/design/D) + if(!(D.build_type & BIOGENERATOR)) + return + ..() + +//Smelter files +/datum/research/smelter/New() + for(var/T in (subtypesof(/datum/tech))) + possible_tech += new T(src) + for(var/path in subtypesof(/datum/design)) + var/datum/design/D = new path(src) + possible_designs += D + if((D.build_type & SMELTER) && ("initial" in D.category)) + AddDesign2Known(D) + +/datum/research/smelter/AddDesign2Known(datum/design/D) + if(!(D.build_type & SMELTER)) + return + ..() + +/*************************************************************** +** Technology Datums ** +** Includes all the various technoliges and what they make. ** +***************************************************************/ + +/datum/tech //Datum of individual technologies. + var/name = "name" //Name of the technology. + var/desc = "description" //General description of what it does and what it makes. + var/id = "id" //An easily referenced ID. Must be alphanumeric, lower-case, and no symbols. + var/level = 1 //A simple number scale of the research level. Level 0 = Secret tech. + var/max_level = 1 // Maximum level this can be at (for job objectives) + var/rare = 1 //How much CentCom wants to get that tech. Used in supply shuttle tech cost calculation. + var/list/req_tech = list() //List of ids associated values of techs required to research this tech. "id" = # + + +//Trunk Technologies (don't require any other techs and you start knowning them). + +/datum/tech/materials + name = "Materials Research" + desc = "Development of new and improved materials." + id = "materials" + max_level = 7 + +/datum/tech/engineering + name = "Engineering Research" + desc = "Development of new and improved engineering parts and methods." + id = "engineering" + max_level = 7 + +/datum/tech/plasmatech + name = "Plasma Research" + desc = "Research into the mysterious substance colloqually known as 'plasma'." + id = "plasmatech" + max_level = 7 + rare = 3 + +/datum/tech/powerstorage + name = "Power Manipulation Technology" + desc = "The various technologies behind the storage and generation of electicity." + id = "powerstorage" + max_level = 7 + +/datum/tech/bluespace + name = "'Blue-space' Research" + desc = "Research into the sub-reality known as 'blue-space'." + id = "bluespace" + max_level = 7 + rare = 2 + +/datum/tech/biotech + name = "Biological Technology" + desc = "Research into the deeper mysteries of life and organic substances." + id = "biotech" + max_level = 7 + +/datum/tech/combat + name = "Combat Systems Research" + desc = "The development of offensive and defensive systems." + id = "combat" + max_level = 7 + +/datum/tech/magnets + name = "Electromagnetic Spectrum Research" + desc = "Research into the electromagnetic spectrum. No clue how they actually work, though." + id = "magnets" + max_level = 7 + +/datum/tech/programming + name = "Data Theory Research" + desc = "The development of new computer and artificial intelligence and data storage systems." + id = "programming" + max_level = 7 + +/datum/tech/toxins //not meant to be raised by deconstruction, do not give objects toxins as an origin_tech + name = "Toxins Research" + desc = "Research into plasma based explosive devices. Upgrade through testing explosives in the toxins lab." + id = "toxins" + max_level = 7 + rare = 2 + +/datum/tech/syndicate + name = "Illegal Technologies Research" + desc = "The study of technologies that violate standard Nanotrasen regulations." + id = "syndicate" + max_level = 0 // Don't count towards maxed research, since it's illegal. + rare = 4 + +/datum/tech/abductor + name = "Alien Technologies Research" + desc = "The study of technologies used by the advanced alien race known as Abductors." + id = "abductor" + rare = 5 + level = 0 + +/* +datum/tech/arcane + name = "Arcane Research" + desc = "Research into the occult and arcane field for use in practical science" + id = "arcane" + level = 0 //It didn't become "secret" as advertised. + +//Branch Techs +datum/tech/explosives + name = "Explosives Research" + desc = "The creation and application of explosive materials." + id = "explosives" + req_tech = list("materials" = 3) + +datum/tech/generators + name = "Power Generation Technology" + desc = "Research into more powerful and more reliable sources." + id = "generators" + req_tech = list("powerstorage" = 2) + +datum/tech/robotics + name = "Robotics Technology" + desc = "The development of advanced automated, autonomous machines." + id = "robotics" + req_tech = list("materials" = 3, "programming" = 3) +*/ + +/datum/tech/proc/getCost(var/current_level = null) + // Calculates tech disk's supply points sell cost + if(!current_level) + current_level = initial(level) + + if(current_level >= level) + return 0 + + var/cost = 0 + for(var/i=current_level+1, i<=level, i++) + if(i == initial(level)) + continue + cost += i*5*rare + + return cost + +/obj/item/disk/tech_disk + name = "\improper Technology Disk" + desc = "A disk for storing technology data for further research." + icon_state = "datadisk2" + materials = list(MAT_METAL=30, MAT_GLASS=10) + var/datum/tech/stored + var/default_name = "\improper Technology Disk" + var/default_desc = "A disk for storing technology data for further research." + +/obj/item/disk/tech_disk/New() + ..() + src.pixel_x = rand(-5.0, 5) + src.pixel_y = rand(-5.0, 5) + +/obj/item/disk/tech_disk/proc/load_tech(datum/tech/T) + name = "[default_name] \[[T]\]" + desc = T.desc + " Level: '[T.level]'" + // NOTE: This is just a reference to the tech on the system it grabbed it from + // This seems highly fragile + stored = T + +/obj/item/disk/tech_disk/proc/wipe_tech() + name = default_name + desc = default_desc + stored = null + +/obj/item/disk/design_disk + name = "\improper Component Design Disk" + desc = "A disk for storing device design data for construction in lathes." + icon_state = "datadisk2" + materials = list(MAT_METAL=100, MAT_GLASS=100) + var/datum/design/blueprint + // I'm doing this so that disk paths with pre-loaded designs don't get weird names + // Otherwise, I'd use "initial()" + var/default_name = "\improper Component Design Disk" + var/default_desc = "A disk for storing device design data for construction in lathes." + +/obj/item/disk/design_disk/New() + ..() + pixel_x = rand(-5, 5) + pixel_y = rand(-5, 5) + +/obj/item/disk/design_disk/proc/load_blueprint(datum/design/D) + name = "[default_name] \[[D]\]" + desc = D.desc + // NOTE: This is just a reference to the design on the system it grabbed it from + // This seems highly fragile + blueprint = D + +/obj/item/disk/design_disk/proc/wipe_blueprint() + name = default_name + desc = default_desc + blueprint = null + +/obj/item/disk/design_disk/golem_shell + name = "golem creation disk" + desc = "A gift from the Liberator." + icon_state = "datadisk1" + +/obj/item/disk/design_disk/golem_shell/Initialize() + . = ..() + var/datum/design/golem_shell/G = new + blueprint = G diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm index ff362b8effc4..de9194953daf 100644 --- a/code/modules/research/server.dm +++ b/code/modules/research/server.dm @@ -1,360 +1,360 @@ -/obj/machinery/r_n_d/server - name = "R&D Server" - icon = 'icons/obj/machines/research.dmi' - icon_state = "server" - var/datum/research/files - var/health = 100 - var/list/id_with_upload = list() //List of R&D consoles with upload to server access. - var/list/id_with_download = list() //List of R&D consoles with download from server access. - var/id_with_upload_string = "" //String versions for easy editing in map editor. - var/id_with_download_string = "" - var/server_id = 0 - var/heat_gen = 100 - var/heating_power = 40000 - var/delay = 10 - req_access = list(ACCESS_RD) //Only the R&D can change server settings. - var/plays_sound = 0 - -/obj/machinery/r_n_d/server/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/rdserver(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stack/cable_coil(null,1) - component_parts += new /obj/item/stack/cable_coil(null,1) - RefreshParts() - initialize_serv(); //Agouri // fuck you agouri - -/obj/machinery/r_n_d/server/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/rdserver(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stack/cable_coil(null,1) - component_parts += new /obj/item/stack/cable_coil(null,1) - RefreshParts() - -/obj/machinery/r_n_d/server/Destroy() - griefProtection() - return ..() - -/obj/machinery/r_n_d/server/RefreshParts() - var/tot_rating = 0 - for(var/obj/item/stock_parts/SP in src) - tot_rating += SP.rating - heat_gen /= max(1, tot_rating) - -/obj/machinery/r_n_d/server/proc/initialize_serv() - if(!files) - files = new /datum/research(src) - var/list/temp_list - if(!id_with_upload.len) - temp_list = list() - temp_list = splittext(id_with_upload_string, ";") - for(var/N in temp_list) - id_with_upload += text2num(N) - if(!id_with_download.len) - temp_list = list() - temp_list = splittext(id_with_download_string, ";") - for(var/N in temp_list) - id_with_download += text2num(N) - -/obj/machinery/r_n_d/server/process() - if(prob(3) && plays_sound) - playsound(loc, "computer_ambience", 50, 1) - - var/datum/gas_mixture/environment = loc.return_air() - switch(environment.temperature) - if(0 to T0C) - health = min(100, health + 1) - if(T0C to (T20C + 20)) - health = Clamp(health, 0, 100) - if((T20C + 20) to (T0C + 70)) - health = max(0, health - 1) - if(health <= 0) - /*griefProtection() This seems to get called twice before running any code that deletes/damages the server or it's files anwyay. - refreshParts and the hasReq procs that get called by this are laggy and do not need to be called by every server on the map every tick */ - var/updateRD = 0 - files.known_designs = list() - for(var/v in files.known_tech) - var/datum/tech/T = files.known_tech[v] - // Slowly decrease research if health drops below 0 - if(prob(1)) - updateRD++ - T.level-- - if(updateRD) - files.RefreshResearch() - if(delay) - delay-- - else - produce_heat(heat_gen) - delay = initial(delay) - -/obj/machinery/r_n_d/server/emp_act(severity) - griefProtection() - ..() - - -/obj/machinery/r_n_d/server/ex_act(severity) - griefProtection() - return ..() - -/obj/machinery/r_n_d/server/blob_act(obj/structure/blob/B) - griefProtection() - return ..() - -// Backup files to CentComm to help admins recover data after griefer attacks -/obj/machinery/r_n_d/server/proc/griefProtection() - for(var/obj/machinery/r_n_d/server/centcom/C in GLOB.machines) - files.push_data(C.files) - -/obj/machinery/r_n_d/server/proc/produce_heat(heat_amt) - if(!(stat & (NOPOWER|BROKEN))) // Blatantly stolen from space heater. - var/turf/simulated/L = loc - if(istype(L)) - var/datum/gas_mixture/env = L.return_air() - if(env.temperature < (heat_amt+T0C)) - - var/transfer_moles = 0.25 * env.total_moles() - - var/datum/gas_mixture/removed = env.remove(transfer_moles) - - if(removed) - - var/heat_capacity = removed.heat_capacity() - if(heat_capacity == 0 || heat_capacity == null) - heat_capacity = 1 - removed.temperature = min((removed.temperature*heat_capacity + heating_power)/heat_capacity, 1000) - - env.merge(removed) - air_update_turf() - -/obj/machinery/r_n_d/server/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(disabled) - return - - if(shocked) - shock(user,50) - - if(istype(O, /obj/item/screwdriver)) - default_deconstruction_screwdriver(user, "server_o", "server", O) - return 1 - - if(exchange_parts(user, O)) - return 1 - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - griefProtection() - default_deconstruction_crowbar(user, O) - return 1 - else - return ..() - -/obj/machinery/r_n_d/server/attack_hand(mob/user as mob) - if(disabled) - return - - if(shocked) - shock(user,50) - return - -/obj/machinery/r_n_d/server/centcom - name = "CentComm. Central R&D Database" - server_id = -1 - -/obj/machinery/r_n_d/server/centcom/Initialize() - ..() - var/list/no_id_servers = list() - var/list/server_ids = list() - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - switch(S.server_id) - if(-1) - continue - if(0) - no_id_servers += S - else - server_ids += S.server_id - - for(var/obj/machinery/r_n_d/server/S in no_id_servers) - var/num = 1 - while(!S.server_id) - if(num in server_ids) - num++ - else - S.server_id = num - server_ids += num - no_id_servers -= S - -/obj/machinery/r_n_d/server/centcom/process() - return PROCESS_KILL //don't need process() - - -/obj/machinery/computer/rdservercontrol - name = "\improper R&D server controller" - icon_screen = "rdcomp" - icon_keyboard = "rd_key" - light_color = LIGHT_COLOR_FADEDPURPLE - circuit = /obj/item/circuitboard/rdservercontrol - var/screen = 0 - var/obj/machinery/r_n_d/server/temp_server - var/list/servers = list() - var/list/consoles = list() - var/badmin = 0 - -/obj/machinery/computer/rdservercontrol/Topic(href, href_list) - if(..()) - return - - add_fingerprint(usr) - usr.set_machine(src) - if(!src.allowed(usr) && !emagged) - to_chat(usr, "You do not have the required access level") - return - - if(href_list["main"]) - screen = 0 - - else if(href_list["access"] || href_list["data"] || href_list["transfer"]) - temp_server = null - consoles = list() - servers = list() - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - if(S.server_id == text2num(href_list["access"]) || S.server_id == text2num(href_list["data"]) || S.server_id == text2num(href_list["transfer"])) - temp_server = S - break - if(href_list["access"]) - screen = 1 - for(var/obj/machinery/computer/rdconsole/C in GLOB.machines) - if(C.sync) - consoles += C - else if(href_list["data"]) - screen = 2 - else if(href_list["transfer"]) - screen = 3 - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - if(S == src) - continue - servers += S - - else if(href_list["upload_toggle"]) - var/num = text2num(href_list["upload_toggle"]) - if(num in temp_server.id_with_upload) - temp_server.id_with_upload -= num - else - temp_server.id_with_upload += num - - else if(href_list["download_toggle"]) - var/num = text2num(href_list["download_toggle"]) - if(num in temp_server.id_with_download) - temp_server.id_with_download -= num - else - temp_server.id_with_download += num - - else if(href_list["reset_tech"]) - var/choice = alert("Technology Data Reset", "Are you sure you want to reset this technology to its default data? Data lost cannot be recovered.", "Continue", "Cancel") - if(choice == "Continue") - for(var/I in temp_server.files.known_tech) - var/datum/tech/T = temp_server.files.known_tech[I] - if(T.id == href_list["reset_tech"]) - T.level = 1 - break - temp_server.files.RefreshResearch() - - else if(href_list["reset_design"]) - var/choice = alert("Design Data Deletion", "Are you sure you want to delete this design? Data lost cannot be recovered.", "Continue", "Cancel") - if(choice == "Continue") - for(var/I in temp_server.files.known_designs) - var/datum/design/D = temp_server.files.known_designs[I] - if(D.id == href_list["reset_design"]) - temp_server.files.known_designs -= D.id - break - temp_server.files.RefreshResearch() - - updateUsrDialog() - return - -/obj/machinery/computer/rdservercontrol/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "" - - switch(screen) - if(0) //Main Menu - dat += "Connected Servers:

    " - - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - if(istype(S, /obj/machinery/r_n_d/server/centcom) && !badmin) - continue - dat += "[S.name] || " - dat += "Access Rights | " - dat += "Data Management" - if(badmin) dat += " | Server-to-Server Transfer" - dat += "
    " - - if(1) //Access rights menu - dat += "[temp_server.name] Access Rights

    " - dat += "Consoles with Upload Access
    " - for(var/obj/machinery/computer/rdconsole/C in consoles) - var/turf/console_turf = get_turf(C) - dat += "* [console_turf.loc]" //FYI, these are all numeric ids, eventually. - if(C.id in temp_server.id_with_upload) - dat += " (Remove)
    " - else - dat += " (Add)
    " - dat += "Consoles with Download Access
    " - for(var/obj/machinery/computer/rdconsole/C in consoles) - var/turf/console_turf = get_turf(C) - dat += "* [console_turf.loc]" - if(C.id in temp_server.id_with_download) - dat += " (Remove)
    " - else - dat += " (Add)
    " - dat += "
    Main Menu" - - if(2) //Data Management menu - dat += "[temp_server.name] Data Management

    " - dat += "Known Technologies
    " - for(var/I in temp_server.files.known_tech) - var/datum/tech/T = temp_server.files.known_tech[I] - if(T.level <= 0) - continue - dat += "* [T.name] " - dat += "(Reset)
    " //FYI, these are all strings. - dat += "Known Designs
    " - for(var/I in temp_server.files.known_designs) - var/datum/design/D = temp_server.files.known_designs[I] - dat += "* [D.name] " - dat += "(Delete)
    " - dat += "
    Main Menu" - - if(3) //Server Data Transfer - dat += "[temp_server.name] Server to Server Transfer

    " - dat += "Send Data to what server?
    " - for(var/obj/machinery/r_n_d/server/S in servers) - dat += "[S.name] (Transfer)
    " - dat += "
    Main Menu" - user << browse("R&D Server Control
    [dat]", "window=server_control;size=575x400") - onclose(user, "server_control") - return - -/obj/machinery/computer/rdservercontrol/emag_act(user as mob) - if(!emagged) - playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) - emagged = 1 - to_chat(user, "You you disable the security protocols") - src.updateUsrDialog() - -/obj/machinery/r_n_d/server/core - name = "Core R&D Server" - id_with_upload_string = "1;3" - id_with_download_string = "1;3" - server_id = 1 - plays_sound = 1 - -/obj/machinery/r_n_d/server/robotics - name = "Robotics and Mechanic R&D Server" - id_with_upload_string = "1;2;4" - id_with_download_string = "1;2;4" - server_id = 2 +/obj/machinery/r_n_d/server + name = "R&D Server" + icon = 'icons/obj/machines/research.dmi' + icon_state = "server" + var/datum/research/files + var/health = 100 + var/list/id_with_upload = list() //List of R&D consoles with upload to server access. + var/list/id_with_download = list() //List of R&D consoles with download from server access. + var/id_with_upload_string = "" //String versions for easy editing in map editor. + var/id_with_download_string = "" + var/server_id = 0 + var/heat_gen = 100 + var/heating_power = 40000 + var/delay = 10 + req_access = list(ACCESS_RD) //Only the R&D can change server settings. + var/plays_sound = 0 + +/obj/machinery/r_n_d/server/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/rdserver(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stack/cable_coil(null,1) + component_parts += new /obj/item/stack/cable_coil(null,1) + RefreshParts() + initialize_serv(); //Agouri // fuck you agouri + +/obj/machinery/r_n_d/server/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/rdserver(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stack/cable_coil(null,1) + component_parts += new /obj/item/stack/cable_coil(null,1) + RefreshParts() + +/obj/machinery/r_n_d/server/Destroy() + griefProtection() + return ..() + +/obj/machinery/r_n_d/server/RefreshParts() + var/tot_rating = 0 + for(var/obj/item/stock_parts/SP in src) + tot_rating += SP.rating + heat_gen /= max(1, tot_rating) + +/obj/machinery/r_n_d/server/proc/initialize_serv() + if(!files) + files = new /datum/research(src) + var/list/temp_list + if(!id_with_upload.len) + temp_list = list() + temp_list = splittext(id_with_upload_string, ";") + for(var/N in temp_list) + id_with_upload += text2num(N) + if(!id_with_download.len) + temp_list = list() + temp_list = splittext(id_with_download_string, ";") + for(var/N in temp_list) + id_with_download += text2num(N) + +/obj/machinery/r_n_d/server/process() + if(prob(3) && plays_sound) + playsound(loc, "computer_ambience", 50, 1) + + var/datum/gas_mixture/environment = loc.return_air() + switch(environment.temperature) + if(0 to T0C) + health = min(100, health + 1) + if(T0C to (T20C + 20)) + health = Clamp(health, 0, 100) + if((T20C + 20) to (T0C + 70)) + health = max(0, health - 1) + if(health <= 0) + /*griefProtection() This seems to get called twice before running any code that deletes/damages the server or it's files anwyay. + refreshParts and the hasReq procs that get called by this are laggy and do not need to be called by every server on the map every tick */ + var/updateRD = 0 + files.known_designs = list() + for(var/v in files.known_tech) + var/datum/tech/T = files.known_tech[v] + // Slowly decrease research if health drops below 0 + if(prob(1)) + updateRD++ + T.level-- + if(updateRD) + files.RefreshResearch() + if(delay) + delay-- + else + produce_heat(heat_gen) + delay = initial(delay) + +/obj/machinery/r_n_d/server/emp_act(severity) + griefProtection() + ..() + + +/obj/machinery/r_n_d/server/ex_act(severity) + griefProtection() + return ..() + +/obj/machinery/r_n_d/server/blob_act(obj/structure/blob/B) + griefProtection() + return ..() + +// Backup files to CentComm to help admins recover data after griefer attacks +/obj/machinery/r_n_d/server/proc/griefProtection() + for(var/obj/machinery/r_n_d/server/centcom/C in GLOB.machines) + files.push_data(C.files) + +/obj/machinery/r_n_d/server/proc/produce_heat(heat_amt) + if(!(stat & (NOPOWER|BROKEN))) // Blatantly stolen from space heater. + var/turf/simulated/L = loc + if(istype(L)) + var/datum/gas_mixture/env = L.return_air() + if(env.temperature < (heat_amt+T0C)) + + var/transfer_moles = 0.25 * env.total_moles() + + var/datum/gas_mixture/removed = env.remove(transfer_moles) + + if(removed) + + var/heat_capacity = removed.heat_capacity() + if(heat_capacity == 0 || heat_capacity == null) + heat_capacity = 1 + removed.temperature = min((removed.temperature*heat_capacity + heating_power)/heat_capacity, 1000) + + env.merge(removed) + air_update_turf() + +/obj/machinery/r_n_d/server/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(disabled) + return + + if(shocked) + shock(user,50) + + if(istype(O, /obj/item/screwdriver)) + default_deconstruction_screwdriver(user, "server_o", "server", O) + return 1 + + if(exchange_parts(user, O)) + return 1 + + if(panel_open) + if(istype(O, /obj/item/crowbar)) + griefProtection() + default_deconstruction_crowbar(user, O) + return 1 + else + return ..() + +/obj/machinery/r_n_d/server/attack_hand(mob/user as mob) + if(disabled) + return + + if(shocked) + shock(user,50) + return + +/obj/machinery/r_n_d/server/centcom + name = "CentComm. Central R&D Database" + server_id = -1 + +/obj/machinery/r_n_d/server/centcom/Initialize() + ..() + var/list/no_id_servers = list() + var/list/server_ids = list() + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + switch(S.server_id) + if(-1) + continue + if(0) + no_id_servers += S + else + server_ids += S.server_id + + for(var/obj/machinery/r_n_d/server/S in no_id_servers) + var/num = 1 + while(!S.server_id) + if(num in server_ids) + num++ + else + S.server_id = num + server_ids += num + no_id_servers -= S + +/obj/machinery/r_n_d/server/centcom/process() + return PROCESS_KILL //don't need process() + + +/obj/machinery/computer/rdservercontrol + name = "\improper R&D server controller" + icon_screen = "rdcomp" + icon_keyboard = "rd_key" + light_color = LIGHT_COLOR_FADEDPURPLE + circuit = /obj/item/circuitboard/rdservercontrol + var/screen = 0 + var/obj/machinery/r_n_d/server/temp_server + var/list/servers = list() + var/list/consoles = list() + var/badmin = 0 + +/obj/machinery/computer/rdservercontrol/Topic(href, href_list) + if(..()) + return + + add_fingerprint(usr) + usr.set_machine(src) + if(!src.allowed(usr) && !emagged) + to_chat(usr, "You do not have the required access level") + return + + if(href_list["main"]) + screen = 0 + + else if(href_list["access"] || href_list["data"] || href_list["transfer"]) + temp_server = null + consoles = list() + servers = list() + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + if(S.server_id == text2num(href_list["access"]) || S.server_id == text2num(href_list["data"]) || S.server_id == text2num(href_list["transfer"])) + temp_server = S + break + if(href_list["access"]) + screen = 1 + for(var/obj/machinery/computer/rdconsole/C in GLOB.machines) + if(C.sync) + consoles += C + else if(href_list["data"]) + screen = 2 + else if(href_list["transfer"]) + screen = 3 + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + if(S == src) + continue + servers += S + + else if(href_list["upload_toggle"]) + var/num = text2num(href_list["upload_toggle"]) + if(num in temp_server.id_with_upload) + temp_server.id_with_upload -= num + else + temp_server.id_with_upload += num + + else if(href_list["download_toggle"]) + var/num = text2num(href_list["download_toggle"]) + if(num in temp_server.id_with_download) + temp_server.id_with_download -= num + else + temp_server.id_with_download += num + + else if(href_list["reset_tech"]) + var/choice = alert("Technology Data Reset", "Are you sure you want to reset this technology to its default data? Data lost cannot be recovered.", "Continue", "Cancel") + if(choice == "Continue") + for(var/I in temp_server.files.known_tech) + var/datum/tech/T = temp_server.files.known_tech[I] + if(T.id == href_list["reset_tech"]) + T.level = 1 + break + temp_server.files.RefreshResearch() + + else if(href_list["reset_design"]) + var/choice = alert("Design Data Deletion", "Are you sure you want to delete this design? Data lost cannot be recovered.", "Continue", "Cancel") + if(choice == "Continue") + for(var/I in temp_server.files.known_designs) + var/datum/design/D = temp_server.files.known_designs[I] + if(D.id == href_list["reset_design"]) + temp_server.files.known_designs -= D.id + break + temp_server.files.RefreshResearch() + + updateUsrDialog() + return + +/obj/machinery/computer/rdservercontrol/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "" + + switch(screen) + if(0) //Main Menu + dat += "Connected Servers:

    " + + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + if(istype(S, /obj/machinery/r_n_d/server/centcom) && !badmin) + continue + dat += "[S.name] || " + dat += "Access Rights | " + dat += "Data Management" + if(badmin) dat += " | Server-to-Server Transfer" + dat += "
    " + + if(1) //Access rights menu + dat += "[temp_server.name] Access Rights

    " + dat += "Consoles with Upload Access
    " + for(var/obj/machinery/computer/rdconsole/C in consoles) + var/turf/console_turf = get_turf(C) + dat += "* [console_turf.loc]" //FYI, these are all numeric ids, eventually. + if(C.id in temp_server.id_with_upload) + dat += " (Remove)
    " + else + dat += " (Add)
    " + dat += "Consoles with Download Access
    " + for(var/obj/machinery/computer/rdconsole/C in consoles) + var/turf/console_turf = get_turf(C) + dat += "* [console_turf.loc]" + if(C.id in temp_server.id_with_download) + dat += " (Remove)
    " + else + dat += " (Add)
    " + dat += "
    Main Menu" + + if(2) //Data Management menu + dat += "[temp_server.name] Data Management

    " + dat += "Known Technologies
    " + for(var/I in temp_server.files.known_tech) + var/datum/tech/T = temp_server.files.known_tech[I] + if(T.level <= 0) + continue + dat += "* [T.name] " + dat += "(Reset)
    " //FYI, these are all strings. + dat += "Known Designs
    " + for(var/I in temp_server.files.known_designs) + var/datum/design/D = temp_server.files.known_designs[I] + dat += "* [D.name] " + dat += "(Delete)
    " + dat += "
    Main Menu" + + if(3) //Server Data Transfer + dat += "[temp_server.name] Server to Server Transfer

    " + dat += "Send Data to what server?
    " + for(var/obj/machinery/r_n_d/server/S in servers) + dat += "[S.name] (Transfer)
    " + dat += "
    Main Menu" + user << browse("R&D Server Control
    [dat]", "window=server_control;size=575x400") + onclose(user, "server_control") + return + +/obj/machinery/computer/rdservercontrol/emag_act(user as mob) + if(!emagged) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You you disable the security protocols") + src.updateUsrDialog() + +/obj/machinery/r_n_d/server/core + name = "Core R&D Server" + id_with_upload_string = "1;3" + id_with_download_string = "1;3" + server_id = 1 + plays_sound = 1 + +/obj/machinery/r_n_d/server/robotics + name = "Robotics and Mechanic R&D Server" + id_with_upload_string = "1;2;4" + id_with_download_string = "1;2;4" + server_id = 2 diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index 746e0ece0e87..84f15745df67 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -188,7 +188,7 @@ var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control var/obj/machinery/computer/camera_advanced/xenobio/X = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) for(var/mob/living/simple_animal/slime/S in X.stored_slimes) S.forceMove(remote_eye.loc) S.visible_message("[S] warps in!") @@ -207,7 +207,7 @@ var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control var/obj/machinery/computer/camera_advanced/xenobio/X = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) for(var/mob/living/simple_animal/slime/S in remote_eye.loc) if(X.stored_slimes.len >= X.max_slimes) break @@ -231,7 +231,7 @@ var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control var/obj/machinery/computer/camera_advanced/xenobio/X = target - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) if(LAZYLEN(SSmobs.cubemonkeys) >= config.cubemonkeycap) to_chat(owner, "Bluespace harmonics prevent the spawning of more than [config.cubemonkeycap] monkeys on the station at one time!") return @@ -259,7 +259,7 @@ if(!recycler) to_chat(owner, "There is no connected monkey recycler. Use a multitool to link one.") return - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) for(var/mob/living/carbon/human/M in remote_eye.loc) if(issmall(M) && M.stat) M.visible_message("[M] vanishes as [M.p_theyre()] reclaimed for recycling!") @@ -279,7 +279,7 @@ var/mob/living/C = owner var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) for(var/mob/living/simple_animal/slime/S in remote_eye.loc) slime_scan(S, C) else @@ -301,7 +301,7 @@ to_chat(owner, "No potion loaded.") return - if(cameranet.checkTurfVis(remote_eye.loc)) + if(GLOB.cameranet.checkTurfVis(remote_eye.loc)) for(var/mob/living/simple_animal/slime/S in remote_eye.loc) X.current_potion.attack(S, C) break @@ -357,7 +357,7 @@ // Scans slime /obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickCtrl(mob/living/user, mob/living/simple_animal/slime/S) - if(!cameranet.checkTurfVis(S.loc)) + if(!GLOB.cameranet.checkTurfVis(S.loc)) to_chat(user, "Target is not near a camera. Cannot proceed.") return var/mob/living/C = user @@ -368,7 +368,7 @@ //Feeds a potion to slime /obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickAlt(mob/living/user, mob/living/simple_animal/slime/S) - if(!cameranet.checkTurfVis(S.loc)) + if(!GLOB.cameranet.checkTurfVis(S.loc)) to_chat(user, "Target is not near a camera. Cannot proceed.") return var/mob/living/C = user @@ -383,7 +383,7 @@ //Picks up slime /obj/machinery/computer/camera_advanced/xenobio/proc/XenoSlimeClickShift(mob/living/user, mob/living/simple_animal/slime/S) - if(!cameranet.checkTurfVis(S.loc)) + if(!GLOB.cameranet.checkTurfVis(S.loc)) to_chat(user, "Target is not near a camera. Cannot proceed.") return var/mob/living/C = user @@ -405,7 +405,7 @@ //Place slimes /obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickShift(mob/living/user, turf/T) - if(!cameranet.checkTurfVis(T)) + if(!GLOB.cameranet.checkTurfVis(T)) to_chat(user, "Target is not near a camera. Cannot proceed.") return var/mob/living/C = user @@ -420,7 +420,7 @@ //Place monkey /obj/machinery/computer/camera_advanced/xenobio/proc/XenoTurfClickCtrl(mob/living/user, turf/T) - if(!cameranet.checkTurfVis(T)) + if(!GLOB.cameranet.checkTurfVis(T)) to_chat(user, "Target is not near a camera. Cannot proceed.") return var/mob/living/C = user @@ -438,7 +438,7 @@ //Pick up monkey /obj/machinery/computer/camera_advanced/xenobio/proc/XenoMonkeyClickCtrl(mob/living/user, mob/living/carbon/human/M) - if(!cameranet.checkTurfVis(M.loc)) + if(!GLOB.cameranet.checkTurfVis(M.loc)) to_chat(user, "Target is not near a camera. Cannot proceed.") return var/mob/living/C = user diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm index 9c8d963fdcdb..513344cc5a6a 100644 --- a/code/modules/response_team/ert.dm +++ b/code/modules/response_team/ert.dm @@ -7,11 +7,11 @@ /datum/game_mode var/list/datum/mind/ert = list() -var/list/response_team_members = list() -var/responseteam_age = 21 // Minimum account age to play as an ERT member -var/datum/response_team/active_team = null -var/send_emergency_team = FALSE -var/ert_request_answered = FALSE +GLOBAL_LIST_EMPTY(response_team_members) +GLOBAL_VAR_INIT(responseteam_age, 21) // Minimum account age to play as an ERT member +GLOBAL_DATUM(active_team, /datum/response_team) +GLOBAL_VAR_INIT(send_emergency_team, FALSE) +GLOBAL_VAR_INIT(ert_request_answered, FALSE) /client/proc/response_team() set name = "Dispatch CentComm Response Team" @@ -29,7 +29,7 @@ var/ert_request_answered = FALSE to_chat(usr, "The round hasn't started yet!") return - if(send_emergency_team) + if(GLOB.send_emergency_team) to_chat(usr, "Central Command has already dispatched an emergency response team!") return @@ -38,7 +38,7 @@ var/ert_request_answered = FALSE /mob/dead/observer/proc/JoinResponseTeam() - if(!send_emergency_team) + if(!GLOB.send_emergency_team) to_chat(src, "No emergency response team is currently being sent.") return 0 @@ -46,7 +46,7 @@ var/ert_request_answered = FALSE to_chat(src, "You are jobbanned from playing on an emergency response team!") return 0 - var/player_age_check = check_client_age(client, responseteam_age) + var/player_age_check = check_client_age(client, GLOB.responseteam_age) if(player_age_check && config.use_age_restriction_for_antags) to_chat(src, "This role is not yet available to you. You need to wait another [player_age_check] days.") return 0 @@ -58,15 +58,15 @@ var/ert_request_answered = FALSE return 1 /proc/trigger_armed_response_team(datum/response_team/response_team_type, commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) - response_team_members = list() - active_team = response_team_type - active_team.setSlots(commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) + GLOB.response_team_members = list() + GLOB.active_team = response_team_type + GLOB.active_team.setSlots(commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) - send_emergency_team = TRUE - var/list/ert_candidates = shuffle(pollCandidates("Join the Emergency Response Team?",, responseteam_age, 600, 1, role_playtime_requirements[ROLE_ERT])) + GLOB.send_emergency_team = TRUE + var/list/ert_candidates = shuffle(pollCandidates("Join the Emergency Response Team?",, GLOB.responseteam_age, 600, 1, GLOB.role_playtime_requirements[ROLE_ERT])) if(!ert_candidates.len) - active_team.cannot_send_team() - send_emergency_team = FALSE + GLOB.active_team.cannot_send_team() + GLOB.send_emergency_team = FALSE return // Respawnable players get first dibs @@ -74,37 +74,37 @@ var/ert_request_answered = FALSE if(jobban_isbanned(M, ROLE_TRAITOR) || jobban_isbanned(M, "Security Officer") || jobban_isbanned(M, "Captain") || jobban_isbanned(M, "Cyborg")) continue if((M in GLOB.respawnable_list) && M.JoinResponseTeam()) - response_team_members |= M + GLOB.response_team_members |= M // If there's still open slots, non-respawnable players can fill them for(var/mob/dead/observer/M in (ert_candidates - GLOB.respawnable_list)) if(M.JoinResponseTeam()) - response_team_members |= M + GLOB.response_team_members |= M - if(!response_team_members.len) - active_team.cannot_send_team() - send_emergency_team = FALSE + if(!GLOB.response_team_members.len) + GLOB.active_team.cannot_send_team() + GLOB.send_emergency_team = FALSE return var/list/ert_gender_prefs = list() - for(var/mob/M in response_team_members) + for(var/mob/M in GLOB.response_team_members) ert_gender_prefs.Add(input_async(M, "Please select a gender (10 seconds):", list("Male", "Female"))) - addtimer(CALLBACK(GLOBAL_PROC, .proc/get_ert_role_prefs, response_team_members, ert_gender_prefs), 100) + addtimer(CALLBACK(GLOBAL_PROC, .proc/get_ert_role_prefs, GLOB.response_team_members, ert_gender_prefs), 100) -/proc/get_ert_role_prefs(list/response_team_members, list/ert_gender_prefs) +/proc/get_ert_role_prefs(list/response_team_members, list/ert_gender_prefs) // Why the FUCK is this variable the EXACT SAME as the global one var/list/ert_role_prefs = list() for(var/datum/async_input/A in ert_gender_prefs) A.close() for(var/mob/M in response_team_members) - ert_role_prefs.Add(input_ranked_async(M, "Please order ERT roles from most to least preferred (20 seconds):", active_team.get_slot_list())) + ert_role_prefs.Add(input_ranked_async(M, "Please order ERT roles from most to least preferred (20 seconds):", GLOB.active_team.get_slot_list())) addtimer(CALLBACK(GLOBAL_PROC, .proc/dispatch_response_team, response_team_members, ert_gender_prefs, ert_role_prefs), 200) /proc/dispatch_response_team(list/response_team_members, list/ert_gender_prefs, list/ert_role_prefs) var/spawn_index = 1 for(var/i = 1, i <= response_team_members.len, i++) - if(spawn_index > emergencyresponseteamspawn.len) + if(spawn_index > GLOB.emergencyresponseteamspawn.len) break - if(!active_team.get_slot_list().len) + if(!GLOB.active_team.get_slot_list().len) break var/gender_pref = ert_gender_prefs[i].result var/role_pref = ert_role_prefs[i].close() @@ -115,9 +115,9 @@ var/ert_request_answered = FALSE // Player was afk and did not select continue for(var/role in role_pref) - if(active_team.check_slot_available(role)) - var/mob/living/new_commando = M.client.create_response_team(gender_pref, role, emergencyresponseteamspawn[spawn_index]) - active_team.reduceSlots(role) + if(GLOB.active_team.check_slot_available(role)) + var/mob/living/new_commando = M.client.create_response_team(gender_pref, role, GLOB.emergencyresponseteamspawn[spawn_index]) + GLOB.active_team.reduceSlots(role) spawn_index++ if(!M || !new_commando) break @@ -125,17 +125,17 @@ var/ert_request_answered = FALSE new_commando.key = M.key new_commando.update_icons() break - send_emergency_team = FALSE + GLOB.send_emergency_team = FALSE - if(active_team.count) - active_team.announce_team() + if(GLOB.active_team.count) + GLOB.active_team.announce_team() return // Everyone who said yes was afk - active_team.cannot_send_team() + GLOB.active_team.cannot_send_team() /client/proc/create_response_team(new_gender, role, turf/spawn_location) if(role == "Cyborg") - var/cyborg_unlock = active_team.getCyborgUnlock() + var/cyborg_unlock = GLOB.active_team.getCyborgUnlock() var/mob/living/silicon/robot/ert/R = new /mob/living/silicon/robot/ert(spawn_location, cyborg_unlock) return R @@ -185,7 +185,7 @@ var/ert_request_answered = FALSE SSjobs.CreateMoneyAccount(M, role, null) - active_team.equip_officer(role, M) + GLOB.active_team.equip_officer(role, M) return M @@ -257,10 +257,10 @@ var/ert_request_answered = FALSE M.equipOutfit(command_outfit) /datum/response_team/proc/cannot_send_team() - event_announcement.Announce("[station_name()], we are unfortunately unable to send you an Emergency Response Team at this time.", "ERT Unavailable") + GLOB.event_announcement.Announce("[station_name()], we are unfortunately unable to send you an Emergency Response Team at this time.", "ERT Unavailable") /datum/response_team/proc/announce_team() - event_announcement.Announce("Attention, [station_name()]. We are sending a team of highly trained assistants to aid(?) you. Standby.", "ERT En-Route") + GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a team of highly trained assistants to aid(?) you. Standby.", "ERT En-Route") // -- AMBER TEAM -- @@ -273,7 +273,7 @@ var/ert_request_answered = FALSE paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/amber /datum/response_team/amber/announce_team() - event_announcement.Announce("Attention, [station_name()]. We are sending a code AMBER light Emergency Response Team. Standby.", "ERT En-Route") + GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code AMBER light Emergency Response Team. Standby.", "ERT En-Route") // -- RED TEAM -- @@ -286,7 +286,7 @@ var/ert_request_answered = FALSE paranormal_outfit = /datum/outfit/job/centcom/response_team/paranormal/red /datum/response_team/red/announce_team() - event_announcement.Announce("Attention, [station_name()]. We are sending a code RED Emergency Response Team. Standby.", "ERT En-Route") + GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code RED Emergency Response Team. Standby.", "ERT En-Route") // -- GAMMA TEAM -- @@ -300,7 +300,7 @@ var/ert_request_answered = FALSE cyborg_unlock = 1 /datum/response_team/gamma/announce_team() - event_announcement.Announce("Attention, [station_name()]. We are sending a code GAMMA elite Emergency Response Team. Standby.", "ERT En-Route") + GLOB.event_announcement.Announce("Attention, [station_name()]. We are sending a code GAMMA elite Emergency Response Team. Standby.", "ERT En-Route") /datum/outfit/job/centcom/response_team name = "Response team" diff --git a/code/modules/ruins/lavalandruin_code/animal_hospital.dm b/code/modules/ruins/lavalandruin_code/animal_hospital.dm index 62793fa42267..1cf21d5a8904 100644 --- a/code/modules/ruins/lavalandruin_code/animal_hospital.dm +++ b/code/modules/ruins/lavalandruin_code/animal_hospital.dm @@ -15,4 +15,4 @@ /obj/effect/mob_spawn/human/doctor/alive/lavaland/Destroy() var/obj/structure/fluff/empty_sleeper/S = new(drop_location()) S.setDir(dir) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/ruins/lavalandruin_code/ash_walker_den.dm b/code/modules/ruins/lavalandruin_code/ash_walker_den.dm index e95a5381b99c..62b2343f10a6 100644 --- a/code/modules/ruins/lavalandruin_code/ash_walker_den.dm +++ b/code/modules/ruins/lavalandruin_code/ash_walker_den.dm @@ -25,7 +25,7 @@ STOP_PROCESSING(SSprocessing, src) /obj/structure/lavaland/ash_walker/deconstruct(disassembled) - new /obj/item/assembly/signaler/anomaly(get_step(loc, pick(alldirs))) + new /obj/item/assembly/signaler/anomaly(get_step(loc, pick(GLOB.alldirs))) new /obj/effect/collapse(loc) return ..() @@ -50,7 +50,7 @@ /obj/structure/lavaland/ash_walker/proc/spawn_mob() if(meat_counter >= ASH_WALKER_SPAWN_THRESHOLD) - new /obj/effect/mob_spawn/human/ash_walker(get_step(loc, pick(alldirs))) + new /obj/effect/mob_spawn/human/ash_walker(get_step(loc, pick(GLOB.alldirs))) visible_message("One of the eggs swells to an unnatural size and tumbles free. It's ready to hatch!") meat_counter -= ASH_WALKER_SPAWN_THRESHOLD @@ -86,4 +86,4 @@ /datum/outfit/ashwalker name ="Ashwalker" head = /obj/item/clothing/head/helmet/gladiator - uniform = /obj/item/clothing/under/gladiator/ash_walker \ No newline at end of file + uniform = /obj/item/clothing/under/gladiator/ash_walker diff --git a/code/modules/ruins/lavalandruin_code/clown_planet.dm b/code/modules/ruins/lavalandruin_code/clown_planet.dm index 6121c40d2af3..3105739126a9 100644 --- a/code/modules/ruins/lavalandruin_code/clown_planet.dm +++ b/code/modules/ruins/lavalandruin_code/clown_planet.dm @@ -4,4 +4,4 @@ info = "If you dare not continue down this path of madness, escape can be found through the chute in this room." /obj/item/paper/crumpled/bloody/ruins/lavaland/clown_planet/hope - info = "Abandon hope, all ye who enter here." \ No newline at end of file + info = "Abandon hope, all ye who enter here." diff --git a/code/modules/ruins/lavalandruin_code/fountain_hall.dm b/code/modules/ruins/lavalandruin_code/fountain_hall.dm index 75e2b8d9aa36..26c8ebca7c68 100644 --- a/code/modules/ruins/lavalandruin_code/fountain_hall.dm +++ b/code/modules/ruins/lavalandruin_code/fountain_hall.dm @@ -46,4 +46,4 @@ if(last_process + time_between_uses > world.time) icon_state = "fountain" else - icon_state = "fountain-red" \ No newline at end of file + icon_state = "fountain-red" diff --git a/code/modules/ruins/lavalandruin_code/pizzaparty.dm b/code/modules/ruins/lavalandruin_code/pizzaparty.dm index 3201652c3718..192a80e3e7f5 100644 --- a/code/modules/ruins/lavalandruin_code/pizzaparty.dm +++ b/code/modules/ruins/lavalandruin_code/pizzaparty.dm @@ -8,4 +8,4 @@ /obj/structure/reagent_dispensers/water_cooler/pizzaparty name = "punch cooler" - reagent_id = "bacchus_blessing" \ No newline at end of file + reagent_id = "bacchus_blessing" diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm index dcd72b44c64b..0220498df5e3 100644 --- a/code/modules/ruins/lavalandruin_code/puzzle.dm +++ b/code/modules/ruins/lavalandruin_code/puzzle.dm @@ -348,4 +348,4 @@ //Move them into random block var/obj/structure/puzzle_element/E = pick(cube.elements) prisoner.forceMove(E) - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/ruins/lavalandruin_code/seed_vault.dm b/code/modules/ruins/lavalandruin_code/seed_vault.dm index db16c7b3a713..2c999a7ce293 100644 --- a/code/modules/ruins/lavalandruin_code/seed_vault.dm +++ b/code/modules/ruins/lavalandruin_code/seed_vault.dm @@ -30,4 +30,4 @@ /obj/effect/mob_spawn/human/seed_vault/Destroy() new/obj/structure/fluff/empty_terrarium(get_turf(src)) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/ruins/lavalandruin_code/sin_ruins.dm b/code/modules/ruins/lavalandruin_code/sin_ruins.dm index 82fbd410ea16..8e3876f7d2ff 100644 --- a/code/modules/ruins/lavalandruin_code/sin_ruins.dm +++ b/code/modules/ruins/lavalandruin_code/sin_ruins.dm @@ -111,7 +111,7 @@ "Perfect. Much better! Now nobody will be able to resist yo-") var/turf/T = get_turf(user) - var/list/levels = space_manager.z_list.Copy() + var/list/levels = GLOB.space_manager.z_list.Copy() for(var/level in levels) if(!is_teleport_allowed(level)) levels -= level diff --git a/code/modules/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/ruins/lavalandruin_code/syndicate_base.dm index b6a55a1361ff..33e4fb527aea 100644 --- a/code/modules/ruins/lavalandruin_code/syndicate_base.dm +++ b/code/modules/ruins/lavalandruin_code/syndicate_base.dm @@ -83,4 +83,4 @@ new /obj/item/gps/internal/lavaland_syndicate_base(src) /obj/item/gps/internal/lavaland_syndicate_base - gpstag = "Encrypted Signal" \ No newline at end of file + gpstag = "Encrypted Signal" diff --git a/code/modules/ruins/objects_and_mobs/gym.dm b/code/modules/ruins/objects_and_mobs/gym.dm index 69abf1a7e882..e2d593a82d6b 100644 --- a/code/modules/ruins/objects_and_mobs/gym.dm +++ b/code/modules/ruins/objects_and_mobs/gym.dm @@ -90,4 +90,4 @@ sleep(3) animate(user, pixel_y = 2, time = 3) sleep(3) - cut_overlay(swole_overlay) \ No newline at end of file + cut_overlay(swole_overlay) diff --git a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm index 5a65a536b460..d5f7a1e1aa2c 100644 --- a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm +++ b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm @@ -407,4 +407,4 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) #undef STABLE #undef COLLAPSE_ON_CROSS #undef DESTROY_ON_CROSS -#undef UNIQUE_EFFECT \ No newline at end of file +#undef UNIQUE_EFFECT diff --git a/code/modules/security_levels/keycard authentication.dm b/code/modules/security_levels/keycard authentication.dm index 99a04205117d..06ebfaa33745 100644 --- a/code/modules/security_levels/keycard authentication.dm +++ b/code/modules/security_levels/keycard authentication.dm @@ -78,7 +78,7 @@ ui = new(user, src, ui_key, "keycard_auth.tmpl", "Keycard Authentication Device UI", 540, 320) ui.open() -/obj/machinery/keycard_auth/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/keycard_auth/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["screen"] = screen data["event"] = event @@ -168,7 +168,7 @@ atom_say("All Emergency Response Teams are dispatched and can not be called at this time.") return atom_say("ERT request transmitted!") - command_announcer.autosay("ERT request transmitted. Reason: [ert_reason]", name) + GLOB.command_announcer.autosay("ERT request transmitted. Reason: [ert_reason]", name) print_centcom_report(ert_reason, station_time_timestamp() + " ERT Request") var/fullmin_count = 0 @@ -176,12 +176,12 @@ if(check_rights(R_EVENT, 0, C.mob)) fullmin_count++ if(fullmin_count) - ert_request_answered = TRUE + GLOB.ert_request_answered = TRUE ERT_Announce(ert_reason , event_triggered_by, 0) ert_reason = "Reason for ERT" feedback_inc("alert_keycard_auth_ert",1) spawn(3000) - if(!ert_request_answered) + if(!GLOB.ert_request_answered) ERT_Announce(ert_reason , event_triggered_by, 1) else trigger_armed_response_team(new /datum/response_team/amber) // No admins? No problem. Automatically send a code amber ERT. @@ -189,37 +189,37 @@ /obj/machinery/keycard_auth/proc/is_ert_blocked() return SSticker.mode && SSticker.mode.ert_disabled -var/global/maint_all_access = 0 -var/global/station_all_access = 0 +GLOBAL_VAR_INIT(maint_all_access, 0) +GLOBAL_VAR_INIT(station_all_access, 0) /proc/make_maint_all_access() for(var/area/maintenance/A in world) for(var/obj/machinery/door/airlock/D in A) D.emergency = 1 D.update_icon(0) - minor_announcement.Announce("Access restrictions on maintenance and external airlocks have been removed.") - maint_all_access = 1 + GLOB.minor_announcement.Announce("Access restrictions on maintenance and external airlocks have been removed.") + GLOB.maint_all_access = 1 /proc/revoke_maint_all_access() for(var/area/maintenance/A in world) for(var/obj/machinery/door/airlock/D in A) D.emergency = 0 D.update_icon(0) - minor_announcement.Announce("Access restrictions on maintenance and external airlocks have been re-added.") - maint_all_access = 0 + GLOB.minor_announcement.Announce("Access restrictions on maintenance and external airlocks have been re-added.") + GLOB.maint_all_access = 0 /proc/make_station_all_access() for(var/obj/machinery/door/airlock/D in GLOB.airlocks) if(is_station_level(D.z)) D.emergency = 1 D.update_icon(0) - minor_announcement.Announce("Access restrictions on all station airlocks have been removed due to an ongoing crisis. Trespassing laws still apply unless ordered otherwise by Command staff.") - station_all_access = 1 + GLOB.minor_announcement.Announce("Access restrictions on all station airlocks have been removed due to an ongoing crisis. Trespassing laws still apply unless ordered otherwise by Command staff.") + GLOB.station_all_access = 1 /proc/revoke_station_all_access() for(var/obj/machinery/door/airlock/D in GLOB.airlocks) if(is_station_level(D.z)) D.emergency = 0 D.update_icon(0) - minor_announcement.Announce("Access restrictions on all station airlocks have been re-added. Seek station AI or a colleague's assistance if you are stuck.") - station_all_access = 0 + GLOB.minor_announcement.Announce("Access restrictions on all station airlocks have been re-added. Seek station AI or a colleague's assistance if you are stuck.") + GLOB.station_all_access = 0 diff --git a/code/modules/security_levels/security levels.dm b/code/modules/security_levels/security levels.dm index effaae3f0adf..0f07107a00b0 100644 --- a/code/modules/security_levels/security levels.dm +++ b/code/modules/security_levels/security levels.dm @@ -1,4 +1,4 @@ -/var/security_level = 0 +GLOBAL_VAR_INIT(security_level, 0) //0 = code green //1 = code blue //2 = code red @@ -7,8 +7,8 @@ //5 = code delta //config.alert_desc_blue_downto -/var/datum/announcement/priority/security/security_announcement_up = new(do_log = 0, do_newscast = 0, new_sound = sound('sound/misc/notice1.ogg')) -/var/datum/announcement/priority/security/security_announcement_down = new(do_log = 0, do_newscast = 0) +GLOBAL_DATUM_INIT(security_announcement_up, /datum/announcement/priority/security, new(do_log = 0, do_newscast = 0, new_sound = sound('sound/misc/notice1.ogg'))) +GLOBAL_DATUM_INIT(security_announcement_down, /datum/announcement/priority/security, new(do_log = 0, do_newscast = 0)) /proc/set_security_level(var/level) switch(level) @@ -26,8 +26,8 @@ level = SEC_LEVEL_DELTA //Will not be announced if you try to set to the same level as it already is - if(level >= SEC_LEVEL_GREEN && level <= SEC_LEVEL_DELTA && level != security_level) - if(level >= SEC_LEVEL_RED && security_level < SEC_LEVEL_RED) + if(level >= SEC_LEVEL_GREEN && level <= SEC_LEVEL_DELTA && level != GLOB.security_level) + if(level >= SEC_LEVEL_RED && GLOB.security_level < SEC_LEVEL_RED) // Mark down this time to prevent shuttle cheese SSshuttle.emergency_sec_level_time = world.time @@ -42,8 +42,8 @@ switch(level) if(SEC_LEVEL_GREEN) - security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.") - security_level = SEC_LEVEL_GREEN + GLOB.security_announcement_down.Announce("All threats to the station have passed. All weapons need to be holstered and privacy laws are once again fully enforced.","Attention! Security level lowered to green.") + GLOB.security_level = SEC_LEVEL_GREEN post_status("alert", "outline") @@ -53,11 +53,11 @@ FA.overlays += image('icons/obj/monitors.dmi', "overlay_green") if(SEC_LEVEL_BLUE) - if(security_level < SEC_LEVEL_BLUE) - security_announcement_up.Announce("The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible and random searches are permitted.","Attention! Security level elevated to blue.") + if(GLOB.security_level < SEC_LEVEL_BLUE) + GLOB.security_announcement_up.Announce("The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible and random searches are permitted.","Attention! Security level elevated to blue.") else - security_announcement_down.Announce("The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed.","Attention! Security level lowered to blue.") - security_level = SEC_LEVEL_BLUE + GLOB.security_announcement_down.Announce("The immediate threat has passed. Security may no longer have weapons drawn at all times, but may continue to have them visible. Random searches are still allowed.","Attention! Security level lowered to blue.") + GLOB.security_level = SEC_LEVEL_BLUE post_status("alert", "outline") @@ -67,11 +67,11 @@ FA.overlays += image('icons/obj/monitors.dmi', "overlay_blue") if(SEC_LEVEL_RED) - if(security_level < SEC_LEVEL_RED) - security_announcement_up.Announce("There is an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!") + if(GLOB.security_level < SEC_LEVEL_RED) + GLOB.security_announcement_up.Announce("There is an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!") else - security_announcement_down.Announce("The station's self-destruct mechanism has been deactivated, but there is still an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!") - security_level = SEC_LEVEL_RED + GLOB.security_announcement_down.Announce("The station's self-destruct mechanism has been deactivated, but there is still an immediate and serious threat to the station. Security may have weapons unholstered at all times. Random searches are allowed and advised.","Attention! Code Red!") + GLOB.security_level = SEC_LEVEL_RED var/obj/machinery/door/airlock/highsecurity/red/R = locate(/obj/machinery/door/airlock/highsecurity/red) in GLOB.airlocks if(R && is_station_level(R.z)) @@ -86,12 +86,12 @@ FA.overlays += image('icons/obj/monitors.dmi', "overlay_red") if(SEC_LEVEL_GAMMA) - security_announcement_up.Announce("Central Command has ordered the Gamma security level on the station. Security is to have weapons equipped at all times, and all civilians are to immediately seek their nearest head for transportation to a secure location. The station's Gamma armory has been unlocked and is ready for use.","Attention! Gamma security level activated!", new_sound = sound('sound/effects/new_siren.ogg')) - security_level = SEC_LEVEL_GAMMA + GLOB.security_announcement_up.Announce("Central Command has ordered the Gamma security level on the station. Security is to have weapons equipped at all times, and all civilians are to immediately seek their nearest head for transportation to a secure location. The station's Gamma armory has been unlocked and is ready for use.","Attention! Gamma security level activated!", new_sound = sound('sound/effects/new_siren.ogg')) + GLOB.security_level = SEC_LEVEL_GAMMA move_gamma_ship() - if(security_level < SEC_LEVEL_RED) + if(GLOB.security_level < SEC_LEVEL_RED) for(var/obj/machinery/door/airlock/highsecurity/red/R in GLOB.airlocks) if(is_station_level(R.z)) R.locked = 0 @@ -111,8 +111,8 @@ FA.update_icon() if(SEC_LEVEL_EPSILON) - security_announcement_up.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.","Attention! Epsilon security level activated!", new_sound = sound('sound/effects/purge_siren.ogg')) - security_level = SEC_LEVEL_EPSILON + GLOB.security_announcement_up.Announce("Central Command has ordered the Epsilon security level on the station. Consider all contracts terminated.","Attention! Epsilon security level activated!", new_sound = sound('sound/effects/purge_siren.ogg')) + GLOB.security_level = SEC_LEVEL_EPSILON post_status("alert", "epsilonalert") @@ -122,8 +122,8 @@ FA.overlays += image('icons/obj/monitors.dmi', "overlay_epsilon") if(SEC_LEVEL_DELTA) - security_announcement_up.Announce("The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.","Attention! Delta security level reached!", new_sound = sound('sound/effects/deltaalarm.ogg')) - security_level = SEC_LEVEL_DELTA + GLOB.security_announcement_up.Announce("The station's self-destruct mechanism has been engaged. All crew are instructed to obey all instructions given by heads of staff. Any violations of these orders can be punished by death. This is not a drill.","Attention! Delta security level reached!", new_sound = sound('sound/effects/deltaalarm.ogg')) + GLOB.security_level = SEC_LEVEL_DELTA post_status("alert", "deltaalert") @@ -133,16 +133,16 @@ FA.overlays += image('icons/obj/monitors.dmi', "overlay_delta") if(level >= SEC_LEVEL_RED) - atc.reroute_traffic(yes = TRUE) // Tell them fuck off we're busy. + GLOB.atc.reroute_traffic(yes = TRUE) // Tell them fuck off we're busy. else - atc.reroute_traffic(yes = FALSE) + GLOB.atc.reroute_traffic(yes = FALSE) SSnightshift.check_nightshift(TRUE) else return /proc/get_security_level() - switch(security_level) + switch(GLOB.security_level) if(SEC_LEVEL_GREEN) return "green" if(SEC_LEVEL_BLUE) diff --git a/code/modules/shuttle/README.md b/code/modules/shuttle/README.md index 9568131ba94f..6a7168c12693 100644 --- a/code/modules/shuttle/README.md +++ b/code/modules/shuttle/README.md @@ -143,4 +143,4 @@ not yet created when `New()` is called. To fix this issue, all docking ports will not initialize automatically on `New()`. Instead, they are manually initialized by the shuttle controller when it is created, via -a proc called `initialize()`. \ No newline at end of file +a proc called `initialize()`. diff --git a/code/modules/shuttle/assault_pod.dm b/code/modules/shuttle/assault_pod.dm index 0d2953017c8a..9505ac475e2b 100644 --- a/code/modules/shuttle/assault_pod.dm +++ b/code/modules/shuttle/assault_pod.dm @@ -33,8 +33,8 @@ /obj/item/assault_pod/attack_self(mob/living/user) var/target_area - target_area = input("Area to land", "Select a Landing Zone", target_area) in teleportlocs - var/area/picked_area = teleportlocs[target_area] + target_area = input("Area to land", "Select a Landing Zone", target_area) in GLOB.teleportlocs + var/area/picked_area = GLOB.teleportlocs[target_area] if(!src || QDELETED(src)) return diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 7f9eddc0df41..f82598b1e143 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -49,20 +49,20 @@ if(auth_need - authorized.len > 0) message_admins("[key_name_admin(user)] has authorized early shuttle launch.") log_game("[key_name(user)] has authorized early shuttle launch in ([x], [y], [z]).") - minor_announcement.Announce("[auth_need - authorized.len] more authorization(s) needed until shuttle is launched early") + GLOB.minor_announcement.Announce("[auth_need - authorized.len] more authorization(s) needed until shuttle is launched early") else message_admins("[key_name_admin(user)] has launched the emergency shuttle [seconds] seconds before launch.") log_game("[key_name(user)] has launched the emergency shuttle in ([x], [y], [z]) [seconds] seconds before launch.") - minor_announcement.Announce("The emergency shuttle will launch in 10 seconds") + GLOB.minor_announcement.Announce("The emergency shuttle will launch in 10 seconds") SSshuttle.emergency.setTimer(100) if("Repeal") if(authorized.Remove(W:registered_name)) - minor_announcement.Announce("[auth_need - authorized.len] authorizations needed until shuttle is launched early") + GLOB.minor_announcement.Announce("[auth_need - authorized.len] authorizations needed until shuttle is launched early") if("Abort") if(authorized.len) - minor_announcement.Announce("All authorizations to launch the shuttle early have been revoked.") + GLOB.minor_announcement.Announce("All authorizations to launch the shuttle early have been revoked.") authorized.Cut() /obj/machinery/computer/emergency_shuttle/emag_act(mob/user) @@ -70,7 +70,7 @@ var/time = SSshuttle.emergency.timeLeft() message_admins("[key_name_admin(user)] has emagged the emergency shuttle: [time] seconds before launch.") log_game("[key_name(user)] has emagged the emergency shuttle in ([x], [y], [z]): [time] seconds before launch.") - minor_announcement.Announce("The emergency shuttle will launch in 10 seconds", "SYSTEM ERROR:") + GLOB.minor_announcement.Announce("The emergency shuttle will launch in 10 seconds", "SYSTEM ERROR:") SSshuttle.emergency.setTimer(100) emagged = 1 @@ -151,9 +151,9 @@ emergency_shuttle_called.Announce("The emergency shuttle has been called. [redAlert ? "Red Alert state confirmed: Dispatching priority shuttle. " : "" ]It will arrive in [timeLeft(600)] minutes.[reason][SSshuttle.emergencyLastCallLoc ? "\n\nCall signal traced. Results can be viewed on any communications console." : "" ]") if(reason == "Automatic Crew Transfer" && signalOrigin == null) // Best way we have to check that it's actually a crew transfer and not just a player using the same message- any other calls to this proc should have a signalOrigin. - atc.shift_ending() + GLOB.atc.shift_ending() else // Emergency shuttle call (probably) - atc.reroute_traffic(yes = TRUE) + GLOB.atc.reroute_traffic(yes = TRUE) /obj/docking_port/mobile/emergency/cancel(area/signalOrigin) @@ -252,7 +252,7 @@ if(SHUTTLE_DOCKED) if(time_left <= 0 && SSshuttle.emergencyNoEscape) - priority_announcement.Announce("Hostile environment detected. Departure has been postponed indefinitely pending conflict resolution.") + GLOB.priority_announcement.Announce("Hostile environment detected. Departure has been postponed indefinitely pending conflict resolution.") sound_played = 0 mode = SHUTTLE_STRANDED @@ -272,9 +272,9 @@ enterTransit() mode = SHUTTLE_ESCAPE timer = world.time - priority_announcement.Announce("The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.") + GLOB.priority_announcement.Announce("The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.") for(var/mob/M in GLOB.player_list) - if(!isnewplayer(M) && !M.client.karma_spent && !(M.client.ckey in karma_spenders) && !M.get_preference(DISABLE_KARMA_REMINDER)) + if(!isnewplayer(M) && !M.client.karma_spent && !(M.client.ckey in GLOB.karma_spenders) && !M.get_preference(DISABLE_KARMA_REMINDER)) to_chat(M, "You have not yet spent your karma for the round; was there a player worthy of receiving your reward? Look under Special Verbs tab, Award Karma.") if(SHUTTLE_ESCAPE) @@ -291,7 +291,7 @@ var/destination_dock = "emergency_away" if(is_hijacked()) destination_dock = "emergency_syndicate" - priority_announcement.Announce("Corruption detected in shuttle navigation protocols. Please contact your supervisor.") + GLOB.priority_announcement.Announce("Corruption detected in shuttle navigation protocols. Please contact your supervisor.") dock_id(destination_dock) diff --git a/code/modules/shuttle/ert.dm b/code/modules/shuttle/ert.dm index 3bc6f627354e..1f664a69c8f7 100644 --- a/code/modules/shuttle/ert.dm +++ b/code/modules/shuttle/ert.dm @@ -20,4 +20,4 @@ flags = NODECONSTRUCT access_tcomms = FALSE access_construction = FALSE - access_mining = FALSE \ No newline at end of file + access_mining = FALSE diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm index 3aeda5131166..c57cea9216e0 100644 --- a/code/modules/shuttle/navigation_computer.dm +++ b/code/modules/shuttle/navigation_computer.dm @@ -354,4 +354,4 @@ remote_eye.setLoc(T) to_chat(target, "Jumped to [selected]") else - playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, 0) \ No newline at end of file + playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, 0) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 0a11a4397adf..dbec1c611f78 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -72,4 +72,4 @@ return FALSE disconnect() LateInitialize() - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 5260fc2b68e6..6441d0f9cb5a 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -774,7 +774,7 @@ ui = new(user, src, ui_key, "shuttle_console.tmpl", M ? M.name : "shuttle", 300, 200) ui.open() -/obj/machinery/computer/shuttle/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/shuttle/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] var/obj/docking_port/mobile/M = SSshuttle.getShuttle(shuttleId) data["status"] = M ? M.getStatusText() : null @@ -942,4 +942,4 @@ T.color = color if(T.dir != dir) T.dir = dir - return T \ No newline at end of file + return T diff --git a/code/modules/shuttle/shuttle_manipulator.dm b/code/modules/shuttle/shuttle_manipulator.dm index 0e67b74de672..c9184b2a4845 100644 --- a/code/modules/shuttle/shuttle_manipulator.dm +++ b/code/modules/shuttle/shuttle_manipulator.dm @@ -64,7 +64,7 @@ if(!ui) // the ui does not exist, so we'll create a new() one // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "shuttle_manipulator.tmpl", "Shuttle Manipulator", 660, 700, null, null, admin_state) + ui = new(user, src, ui_key, "shuttle_manipulator.tmpl", "Shuttle Manipulator", 660, 700, null, null, GLOB.admin_state) // when the ui is first opened this is the data it will use // open the new ui window ui.open() @@ -80,8 +80,8 @@ data["templates_tabs"] = list() data["selected"] = null - for(var/shuttle_id in shuttle_templates) - var/datum/map_template/shuttle/S = shuttle_templates[shuttle_id] + for(var/shuttle_id in GLOB.shuttle_templates) + var/datum/map_template/shuttle/S = GLOB.shuttle_templates[shuttle_id] if(!templates[S.port_id]) data["templates_tabs"] += S.port_id @@ -138,7 +138,7 @@ // Preload some common parameters var/shuttle_id = href_list["shuttle_id"] - var/datum/map_template/shuttle/S = shuttle_templates[shuttle_id] + var/datum/map_template/shuttle/S = GLOB.shuttle_templates[shuttle_id] if(href_list["selectMenuKey"]) diff --git a/code/modules/shuttle/shuttle_rotate.dm b/code/modules/shuttle/shuttle_rotate.dm index bea581d41741..2adaad8e1663 100644 --- a/code/modules/shuttle/shuttle_rotate.dm +++ b/code/modules/shuttle/shuttle_rotate.dm @@ -88,4 +88,4 @@ If ever any of these procs are useful for non-shuttles, rename it to proc/rotate //prevents shuttles attempting to rotate this since it messes up sprites /obj/machinery/gravity_generator/shuttleRotate(rotation, params) params = NONE - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 80094c876df7..b20a82e7b300 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -413,12 +413,12 @@ ui = new(user, src, ui_key, "order_console.tmpl", name, ORDER_SCREEN_WIDTH, ORDER_SCREEN_HEIGHT) ui.open() -/obj/machinery/computer/ordercomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/ordercomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["last_viewed_group"] = last_viewed_group var/category_list[0] - for(var/category in all_supply_groups) + for(var/category in GLOB.all_supply_groups) category_list.Add(list(list("name" = get_supply_group_name(category), "category" = category))) data["categories"] = category_list var/cat = text2num(last_viewed_group) @@ -559,12 +559,12 @@ ui = new(user, src, ui_key, "supply_console.tmpl", name, SUPPLY_SCREEN_WIDTH, SUPPLY_SCREEN_HEIGHT) ui.open() -/obj/machinery/computer/supplycomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/supplycomp/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/data[0] data["last_viewed_group"] = last_viewed_group var/category_list[0] - for(var/category in all_supply_groups) + for(var/category in GLOB.all_supply_groups) category_list.Add(list(list("name" = get_supply_group_name(category), "category" = category))) data["categories"] = category_list diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm index dc588eda8873..f9ec738b167f 100644 --- a/code/modules/shuttle/syndicate.dm +++ b/code/modules/shuttle/syndicate.dm @@ -93,4 +93,4 @@ x_offset = 0 y_offset = 0 -#undef SYNDICATE_CHALLENGE_TIMER \ No newline at end of file +#undef SYNDICATE_CHALLENGE_TIMER diff --git a/code/modules/space_management/level_check.dm b/code/modules/space_management/level_check.dm index a9abdaa5472e..c49843cd7ead 100644 --- a/code/modules/space_management/level_check.dm +++ b/code/modules/space_management/level_check.dm @@ -1,5 +1,5 @@ /proc/is_on_level_name(atom/A,name) - var/datum/space_level/S = space_manager.get_zlev_by_name(name) + var/datum/space_level/S = GLOB.space_manager.get_zlev_by_name(name) return A.z == S.zpos // For expansion later diff --git a/code/modules/space_management/level_traits.dm b/code/modules/space_management/level_traits.dm index 39b584df64d9..1d88f70454fc 100644 --- a/code/modules/space_management/level_traits.dm +++ b/code/modules/space_management/level_traits.dm @@ -36,28 +36,28 @@ secure = (z == level_name_to_num(CENTCOMM)) return secure -var/list/default_map_traits = MAP_TRANSITION_CONFIG +GLOBAL_LIST_INIT(default_map_traits, MAP_TRANSITION_CONFIG) /proc/check_level_trait(z, trait) if(!z) return 0 // If you're nowhere, you have no traits var/list/trait_list - if(space_manager.initialized) - var/datum/space_level/S = space_manager.get_zlev(z) + if(GLOB.space_manager.initialized) + var/datum/space_level/S = GLOB.space_manager.get_zlev(z) trait_list = S.flags else - trait_list = default_map_traits[z] + trait_list = GLOB.default_map_traits[z] trait_list = trait_list["attributes"] return (trait in trait_list) /proc/levels_by_trait(trait) var/list/result = list() - for(var/A in space_manager.z_list) - var/datum/space_level/S = space_manager.z_list[A] + for(var/A in GLOB.space_manager.z_list) + var/datum/space_level/S = GLOB.space_manager.z_list[A] if(trait in S.flags) result |= S.zpos return result /proc/level_name_to_num(name) - var/datum/space_level/S = space_manager.get_zlev_by_name(name) - return S.zpos \ No newline at end of file + var/datum/space_level/S = GLOB.space_manager.get_zlev_by_name(name) + return S.zpos diff --git a/code/modules/space_management/space_chunk.dm b/code/modules/space_management/space_chunk.dm index b311dad00286..fada2527d1c0 100644 --- a/code/modules/space_management/space_chunk.dm +++ b/code/modules/space_management/space_chunk.dm @@ -97,4 +97,4 @@ #undef BOTTOM_LEFT_CHUNK #undef BOTTOM_RIGHT_CHUNK #undef TOP_LEFT_CHUNK -#undef TOP_RIGHT_CHUNK \ No newline at end of file +#undef TOP_RIGHT_CHUNK diff --git a/code/modules/space_management/space_level.dm b/code/modules/space_management/space_level.dm index 8443951d1d3a..44c692363af1 100644 --- a/code/modules/space_management/space_level.dm +++ b/code/modules/space_management/space_level.dm @@ -31,11 +31,11 @@ /datum/space_level/Destroy() if(linkage == CROSSLINKED) - if(space_manager.linkage_map) - remove_from_space_network(space_manager.linkage_map) + if(GLOB.space_manager.linkage_map) + remove_from_space_network(GLOB.space_manager.linkage_map) - space_manager.unbuilt_space_transitions -= src - space_manager.z_list -= "[zpos]" + GLOB.space_manager.unbuilt_space_transitions -= src + GLOB.space_manager.z_list -= "[zpos]" return ..() /datum/space_level/proc/build_space_destination_arrays() @@ -95,7 +95,7 @@ transit_east -= S /datum/space_level/proc/apply_transition(turf/space/S) - if(src in space_manager.unbuilt_space_transitions) + if(src in GLOB.space_manager.unbuilt_space_transitions) return // Let the space manager handle this one switch(linkage) if(UNAFFECTED) @@ -124,10 +124,10 @@ return // Remove ourselves from the linkage map if we were cross-linked if(linkage == CROSSLINKED) - if(space_manager.linkage_map) - remove_from_space_network(space_manager.linkage_map) + if(GLOB.space_manager.linkage_map) + remove_from_space_network(GLOB.space_manager.linkage_map) - space_manager.unbuilt_space_transitions |= src + GLOB.space_manager.unbuilt_space_transitions |= src linkage = transition_type switch(transition_type) if(UNAFFECTED) @@ -143,9 +143,9 @@ D.register() D.forceMove(locate(200, 200, zpos)) -var/list/atmos_machine_typecache = typecacheof(/obj/machinery/atmospherics) -var/list/cable_typecache = typecacheof(/obj/structure/cable) -var/list/maploader_typecache = typecacheof(/obj/effect/landmark/map_loader) +GLOBAL_LIST_INIT(atmos_machine_typecache, typecacheof(/obj/machinery/atmospherics)) +GLOBAL_LIST_INIT(cable_typecache, typecacheof(/obj/structure/cable)) +GLOBAL_LIST_INIT(maploader_typecache, typecacheof(/obj/effect/landmark/map_loader)) /datum/space_level/proc/resume_init() if(dirt_count > 0) @@ -156,9 +156,9 @@ var/list/maploader_typecache = typecacheof(/obj/effect/landmark/map_loader) init_list = list() var/watch = start_watch() listclearnulls(our_atoms) - var/list/late_maps = typecache_filter_list(our_atoms, maploader_typecache) - var/list/pipes = typecache_filter_list(our_atoms, atmos_machine_typecache) - var/list/cables = typecache_filter_list(our_atoms, cable_typecache) + var/list/late_maps = typecache_filter_list(our_atoms, GLOB.maploader_typecache) + var/list/pipes = typecache_filter_list(our_atoms, GLOB.atmos_machine_typecache) + var/list/cables = typecache_filter_list(our_atoms, GLOB.cable_typecache) // If we don't carefully add dirt around the map templates, bad stuff happens // so we separate them out here our_atoms -= late_maps @@ -193,9 +193,9 @@ var/list/maploader_typecache = typecacheof(/obj/effect/landmark/map_loader) /datum/space_level/proc/do_late_maps(list/late_maps) var/watch = start_watch() log_debug("Loading map templates on z-level '[zpos]'!") - space_manager.add_dirt(zpos) // Let's not repeatedly resume init for each template + GLOB.space_manager.add_dirt(zpos) // Let's not repeatedly resume init for each template for(var/atom/movable/AM in late_maps) AM.Initialize() late_maps.Cut() - space_manager.remove_dirt(zpos) + GLOB.space_manager.remove_dirt(zpos) log_debug("Took [stop_watch(watch)]s") diff --git a/code/modules/space_management/space_transition.dm b/code/modules/space_management/space_transition.dm index f432d9733a58..5312938d588f 100644 --- a/code/modules/space_management/space_transition.dm +++ b/code/modules/space_management/space_transition.dm @@ -63,8 +63,8 @@ var/oppose = get_opposite_direction(direction) neighbors[direction] = S S.neighbors[oppose] = src - space_manager.unbuilt_space_transitions |= src - space_manager.unbuilt_space_transitions |= S + GLOB.space_manager.unbuilt_space_transitions |= src + GLOB.space_manager.unbuilt_space_transitions |= S /datum/space_level/proc/get_connection(direction) @@ -169,7 +169,7 @@ var/datum/space_level/S = spl.neighbors[direction] var/oppose = get_opposite_direction(direction) S.neighbors.Remove(oppose) - space_manager.unbuilt_space_transitions |= S + GLOB.space_manager.unbuilt_space_transitions |= S spl.reset_connections() spl = initial(spl) diff --git a/code/modules/space_management/zlevel_manager.dm b/code/modules/space_management/zlevel_manager.dm index acb1cf5c0a06..eb84206eea38 100644 --- a/code/modules/space_management/zlevel_manager.dm +++ b/code/modules/space_management/zlevel_manager.dm @@ -1,4 +1,4 @@ -var/global/datum/zlev_manager/space_manager = new +GLOBAL_DATUM_INIT(space_manager, /datum/zlev_manager, new()) /datum/zlev_manager // A list of z-levels @@ -17,11 +17,11 @@ var/global/datum/zlev_manager/space_manager = new // Populate our space level list // and prepare space transitions /datum/zlev_manager/proc/initialize() - var/num_official_z_levels = map_transition_config.len + var/num_official_z_levels = GLOB.map_transition_config.len var/k = 1 // First take care of "Official" z levels, without visiting levels outside of the list - for(var/list/features in map_transition_config) + for(var/list/features in GLOB.map_transition_config) if(k > world.maxz) CRASH("More map attributes pre-defined than existent z levels - [num_official_z_levels]") var/name = features["name"] @@ -168,4 +168,4 @@ var/global/datum/zlev_manager/space_manager = new var/datum/space_level/heap/heap = z_list["[C.zpos]"] if(!istype(heap)) throw EXCEPTION("Attempted to free chunk at invalid z-level ([C.x],[C.y],[C.zpos]) [C.width]x[C.height]") - heap.free(C) \ No newline at end of file + heap.free(C) diff --git a/code/modules/spacepods/construction.dm b/code/modules/spacepods/construction.dm index eba600d883f7..be2998fc0a30 100644 --- a/code/modules/spacepods/construction.dm +++ b/code/modules/spacepods/construction.dm @@ -225,4 +225,4 @@ spawn_result(mob/user as mob) ..() feedback_inc("spacepod_created",1) - return \ No newline at end of file + return diff --git a/code/modules/spacepods/lock_buster.dm b/code/modules/spacepods/lock_buster.dm index 75f5e4ee9db5..8a83315ce3d7 100644 --- a/code/modules/spacepods/lock_buster.dm +++ b/code/modules/spacepods/lock_buster.dm @@ -11,4 +11,4 @@ icon_state = "lock_buster_on" else icon_state = "lock_buster_off" - to_chat(usr, "You turn the [src] [on ? "on" : "off"].") \ No newline at end of file + to_chat(usr, "You turn the [src] [on ? "on" : "off"].") diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 5b0be5395782..7b154ef2a25e 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -216,6 +216,7 @@ deal_damage(damage) visible_message("[user] [user.attacktext] [src]!") user.create_attack_log("attacked [src.name]") + add_attack_logs(user, src, "attacked") return TRUE /obj/spacepod/attack_alien(mob/user) diff --git a/code/modules/station_goals/bsa.dm b/code/modules/station_goals/bsa.dm index bc0576c9d52c..3e45877ec298 100644 --- a/code/modules/station_goals/bsa.dm +++ b/code/modules/station_goals/bsa.dm @@ -313,7 +313,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/computer/bsa_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/bsa_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/list/data = list() data["connected"] = cannon data["notice"] = notice @@ -352,12 +352,12 @@ /obj/machinery/computer/bsa_control/proc/calibrate(mob/user) var/list/gps_locators = list() - for(var/obj/item/gps/G in GPS_list) //nulls on the list somehow + for(var/obj/item/gps/G in GLOB.GPS_list) //nulls on the list somehow gps_locators[G.gpstag] = G var/list/options = gps_locators if(area_aim) - options += target_all_areas ? ghostteleportlocs : teleportlocs + options += target_all_areas ? GLOB.ghostteleportlocs : GLOB.teleportlocs var/V = input(user,"Select target", "Select target",null) in options|null target = options[V] diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm index a55657fc7249..db801fa5d0f4 100644 --- a/code/modules/station_goals/dna_vault.dm +++ b/code/modules/station_goals/dna_vault.dm @@ -74,7 +74,7 @@ plants = list() dna = list() -var/list/non_simple_animals = typecacheof(list(/mob/living/carbon/human/monkey,/mob/living/carbon/alien)) +GLOBAL_LIST_INIT(non_simple_animals, typecacheof(list(/mob/living/carbon/human/monkey,/mob/living/carbon/alien))) /obj/item/dna_probe/afterattack(atom/target, mob/user, proximity) ..() @@ -95,7 +95,7 @@ var/list/non_simple_animals = typecacheof(list(/mob/living/carbon/human/monkey,/ to_chat(user, "Plant data added to local storage.") //animals - if(isanimal(target) || is_type_in_typecache(target, non_simple_animals)) + if(isanimal(target) || is_type_in_typecache(target, GLOB.non_simple_animals)) if(isanimal(target)) var/mob/living/simple_animal/A = target if(!A.healable)//simple approximation of being animal not a robot or similar @@ -220,7 +220,7 @@ var/list/non_simple_animals = typecacheof(list(/mob/living/carbon/human/monkey,/ L += pick_n_take(possible_powers) power_lottery[user] = L -/obj/machinery/dna_vault/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) //TODO Make it % bars maybe +/obj/machinery/dna_vault/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) //TODO Make it % bars maybe var/list/data = list() data["plants"] = plants.len data["plants_max"] = plants_max @@ -323,4 +323,4 @@ var/list/non_simple_animals = typecacheof(list(/mob/living/carbon/human/monkey,/ #undef VAULT_STUNTIME #undef VAULT_ARMOUR #undef VAULT_SPEED -#undef VAULT_QUICK \ No newline at end of file +#undef VAULT_QUICK diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm index 9995528c9b57..42bd3b2a10a7 100644 --- a/code/modules/station_goals/shield.dm +++ b/code/modules/station_goals/shield.dm @@ -71,7 +71,7 @@ if(S.id == id && atoms_share_level(src, S)) S.toggle() -/obj/machinery/computer/sat_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) +/obj/machinery/computer/sat_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state) var/list/data = list() data["satellites"] = list() diff --git a/code/modules/station_goals/station_goal.dm b/code/modules/station_goals/station_goal.dm index 90d4defa26c1..47f65ea25537 100644 --- a/code/modules/station_goals/station_goal.dm +++ b/code/modules/station_goals/station_goal.dm @@ -12,7 +12,7 @@ var/report_message = "Complete this goal." /datum/station_goal/proc/send_report() - priority_announcement.Announce("Priority Nanotrasen directive received. Project \"[name]\" details inbound.", "Incoming Priority Message", 'sound/AI/commandreport.ogg') + GLOB.priority_announcement.Announce("Priority Nanotrasen directive received. Project \"[name]\" details inbound.", "Incoming Priority Message", 'sound/AI/commandreport.ogg') print_command_report(get_report(), "Nanotrasen Directive [pick(GLOB.phonetic_alphabet)] \Roman[rand(1,50)]") on_report() @@ -46,4 +46,4 @@ on_report() send_report() else if(href_list["remove"]) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/store/store.dm b/code/modules/store/store.dm index 84c85fea29e8..2557008def0c 100644 --- a/code/modules/store/store.dm +++ b/code/modules/store/store.dm @@ -16,7 +16,7 @@ job objectives = good stock market, shitty job objective completion = shitty eco Goal for now is to get the store itself working, however. */ -var/global/datum/store/centcomm_store=new +GLOBAL_DATUM_INIT(centcomm_store, /datum/store, new()) /datum/store var/list/datum/storeitem/items=list() @@ -40,7 +40,7 @@ var/global/datum/store/centcomm_store=new T.target_name = "[command_name()] Merchandising" T.purpose = "Purchase of [item.name]" T.amount = -amount - T.date = current_date_string + T.date = GLOB.current_date_string T.time = station_time_timestamp() T.source_terminal = "\[CLASSIFIED\] Terminal #[rand(111,333)]" mind.initial_account.transaction_log.Add(T) diff --git a/code/modules/surgery/cavity_implant.dm b/code/modules/surgery/cavity_implant.dm index e86ab317fce5..d7b7621e9c0c 100644 --- a/code/modules/surgery/cavity_implant.dm +++ b/code/modules/surgery/cavity_implant.dm @@ -203,4 +203,4 @@ return 1 else to_chat(user, "You don't find anything in [target]'s [target_zone].") - return 0 \ No newline at end of file + return 0 diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm index 21458720978e..a54aa25d228a 100644 --- a/code/modules/surgery/core_removal.dm +++ b/code/modules/surgery/core_removal.dm @@ -58,4 +58,4 @@ /datum/surgery_step/slime/extract_core/fail_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) user.visible_message(" [user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \ " Your hand slips, tearing [target]'s flesh with \the [tool]!") - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm index 26e8a2bf5779..dece58267190 100644 --- a/code/modules/surgery/dental_implant.dm +++ b/code/modules/surgery/dental_implant.dm @@ -55,4 +55,4 @@ target.reagents.trans_to(owner, target.reagents.total_volume) Remove(owner) qdel(target) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/implant_removal.dm b/code/modules/surgery/implant_removal.dm index 48895a3378ae..078d0ec13849 100644 --- a/code/modules/surgery/implant_removal.dm +++ b/code/modules/surgery/implant_removal.dm @@ -76,4 +76,4 @@ else user.visible_message(" [user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ "You could not find anything inside [target]'s [affected.name].") - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm index bec73e6e69be..561257c25955 100644 --- a/code/modules/surgery/limb_augmentation.dm +++ b/code/modules/surgery/limb_augmentation.dm @@ -54,4 +54,4 @@ affected.open = 0 affected.germ_level = 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index 11ceaaeb0fb5..db1ad04aef3f 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -160,7 +160,7 @@ ..() if(E.limb_name == "head") var/obj/item/organ/external/head/H = target.get_organ("head") - var/datum/robolimb/robohead = all_robolimbs[H.model] + var/datum/robolimb/robohead = GLOB.all_robolimbs[H.model] if(robohead.is_monitor) //Ensures that if an IPC gets a head that's got a human hair wig attached to their body, the hair won't wipe. H.h_style = "Bald" H.f_style = "Shaved" diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm index 80dceea5048d..53907e86d79d 100644 --- a/code/modules/surgery/organs/augments_eyes.dm +++ b/code/modules/surgery/organs/augments_eyes.dm @@ -11,7 +11,7 @@ var/see_in_dark = 0 var/see_invisible = 0 var/lighting_alpha - + var/eye_colour = "#000000" // Should never be null var/old_eye_colour = "#000000" var/flash_protect = 0 @@ -88,14 +88,14 @@ /obj/item/organ/internal/cyberimp/eyes/hud/insert(var/mob/living/carbon/M, var/special = 0) ..() if(HUD_type) - var/datum/atom_hud/H = huds[HUD_type] + var/datum/atom_hud/H = GLOB.huds[HUD_type] H.add_hud_to(M) M.permanent_huds |= H /obj/item/organ/internal/cyberimp/eyes/hud/remove(var/mob/living/carbon/M, var/special = 0) . = ..() if(HUD_type) - var/datum/atom_hud/H = huds[HUD_type] + var/datum/atom_hud/H = GLOB.huds[HUD_type] M.permanent_huds ^= H H.remove_hud_from(M) diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 1d4bb3a98761..63a7f8a3590d 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -70,13 +70,15 @@ if(BP.internal_bleeding) internal_bleeding_rate += 0.5 - bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects (heparin) naturally decreases + bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects naturally decreases + + var/additional_bleed = round(Clamp((reagents.get_reagent_amount("heparin") / 10), 0, 2), 1) //Heparin worsens existing bleeding if(internal_bleeding_rate && !(status_flags & FAKEDEATH)) - bleed_internal(internal_bleeding_rate) + bleed_internal(internal_bleeding_rate + additional_bleed) if(bleed_rate && !bleedsuppress && !(status_flags & FAKEDEATH)) - bleed(bleed_rate) + bleed(bleed_rate + additional_bleed) //Makes a blood drop, leaking amt units of blood from the mob /mob/living/carbon/proc/bleed(amt) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index 2973573bc2ce..7491bfbad19e 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -46,8 +46,8 @@ if(!(M.disabilities & COLOURBLIND) && (dependent_disabilities & COLOURBLIND)) //If the eyes are colourblind and we're not, carry over the gene. dependent_disabilities &= ~COLOURBLIND - M.dna.SetSEState(COLOURBLINDBLOCK,1) - genemutcheck(M,COLOURBLINDBLOCK,null,MUTCHK_FORCED) + M.dna.SetSEState(GLOB.colourblindblock,1) + genemutcheck(M,GLOB.colourblindblock,null,MUTCHK_FORCED) else M.update_client_colour() //If we're here, that means the mob acquired the colourblindness gene while they didn't have eyes. Better handle it. @@ -55,8 +55,8 @@ if(!special && (M.disabilities & COLOURBLIND)) //If special is set, that means these eyes are getting deleted (i.e. during set_species()) if(!(dependent_disabilities & COLOURBLIND)) //We only want to change COLOURBLINDBLOCK and such it the eyes are being surgically removed. dependent_disabilities |= COLOURBLIND - M.dna.SetSEState(COLOURBLINDBLOCK,0) - genemutcheck(M,COLOURBLINDBLOCK,null,MUTCHK_FORCED) + M.dna.SetSEState(GLOB.colourblindblock,0) + genemutcheck(M,GLOB.colourblindblock,null,MUTCHK_FORCED) . = ..() /obj/item/organ/internal/eyes/surgeryize() @@ -78,4 +78,4 @@ icon_state = "eyes-prosthetic" desc = "An electronic device designed to mimic the functions of a pair of human eyes. It has no benefits over organic eyes, but is easy to produce." origin_tech = "biotech=4" - status = ORGAN_ROBOT \ No newline at end of file + status = ORGAN_ROBOT diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 987315ac13b9..9269b2340af1 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -267,4 +267,4 @@ owner.adjustFireLoss(numMid) if(prob(numLow)) to_chat(owner, "Your [name] lurches awkwardly!") - owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0)) \ No newline at end of file + owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0)) diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm index 91bba8d4a1b1..ca2698f2ad4c 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -122,4 +122,4 @@ for(var/zone in full) if(has_organ(zone)) full -= zone - return full \ No newline at end of file + return full diff --git a/code/modules/surgery/organs/kidneys.dm b/code/modules/surgery/organs/kidneys.dm index 26b59191a7c4..9d3048beaba5 100644 --- a/code/modules/surgery/organs/kidneys.dm +++ b/code/modules/surgery/organs/kidneys.dm @@ -22,4 +22,4 @@ icon_state = "kidneys-c" desc = "An electronic device designed to mimic the functions of human kidneys. It has no benefits over a pair of organic kidneys, but is easy to produce." origin_tech = "biotech=4" - status = ORGAN_ROBOT \ No newline at end of file + status = ORGAN_ROBOT diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm index ba3112b63924..799b8e8a6abc 100644 --- a/code/modules/surgery/organs/liver.dm +++ b/code/modules/surgery/organs/liver.dm @@ -55,4 +55,4 @@ icon_state = "liver-c" desc = "An electronic device designed to mimic the functions of a human liver. It has no benefits over an organic liver, but is easy to produce." origin_tech = "biotech=4" - status = ORGAN_ROBOT \ No newline at end of file + status = ORGAN_ROBOT diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 47fa74969d00..782bcbe6ca68 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -355,4 +355,4 @@ cold_level_1_threshold = 200 cold_level_2_threshold = 140 - cold_level_3_threshold = 100 \ No newline at end of file + cold_level_3_threshold = 100 diff --git a/code/modules/surgery/organs/mmi_holder.dm b/code/modules/surgery/organs/mmi_holder.dm index d4228fb9625e..bd885471ff1d 100644 --- a/code/modules/surgery/organs/mmi_holder.dm +++ b/code/modules/surgery/organs/mmi_holder.dm @@ -33,4 +33,4 @@ desc = stored_mmi.desc icon = stored_mmi.icon icon_state = stored_mmi.icon_state - set_dna(stored_mmi.brainmob.dna) \ No newline at end of file + set_dna(stored_mmi.brainmob.dna) diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index 305c08ff17eb..607944e4b6af 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -515,7 +515,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(!clean) // Throw limb around. if(src && istype(loc,/turf)) - dropped_part.throw_at(get_edge_target_turf(src,pick(alldirs)),rand(1,3),30) + dropped_part.throw_at(get_edge_target_turf(src,pick(GLOB.alldirs)),rand(1,3),30) dir = 2 brute_dam = 0 burn_dam = 0 //Reset the damage on the limb; the damage should have transferred to the parent; we don't want extra damage being re-applied when then limb is re-attached @@ -663,7 +663,7 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/proc/set_company(var/company) model = company - var/datum/robolimb/R = all_robolimbs[company] + var/datum/robolimb/R = GLOB.all_robolimbs[company] if(R) force_icon = R.icon name = "[R.company] [initial(name)]" diff --git a/code/modules/surgery/organs/organ_icon.dm b/code/modules/surgery/organs/organ_icon.dm index ef2f729811c9..20146b75a0bf 100644 --- a/code/modules/surgery/organs/organ_icon.dm +++ b/code/modules/surgery/organs/organ_icon.dm @@ -1,4 +1,4 @@ -var/global/list/limb_icon_cache = list() +GLOBAL_LIST_EMPTY(limb_icon_cache) /obj/item/organ/external/proc/compile_icon() // I do this so the head's overlays don't get obliterated diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 25f87d711046..7f5e520c88a3 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -231,11 +231,11 @@ /obj/item/organ/internal/honktumor/insert(mob/living/carbon/M, special = 0) ..() M.mutations.Add(CLUMSY) - M.mutations.Add(COMICBLOCK) - M.dna.SetSEState(CLUMSYBLOCK,1,1) - M.dna.SetSEState(COMICBLOCK,1,1) - genemutcheck(M,CLUMSYBLOCK,null,MUTCHK_FORCED) - genemutcheck(M,COMICBLOCK,null,MUTCHK_FORCED) + M.mutations.Add(GLOB.comicblock) + M.dna.SetSEState(GLOB.clumsyblock,1,1) + M.dna.SetSEState(GLOB.comicblock,1,1) + genemutcheck(M,GLOB.clumsyblock,null,MUTCHK_FORCED) + genemutcheck(M,GLOB.comicblock,null,MUTCHK_FORCED) organhonked = world.time waddle = M.AddComponent(/datum/component/waddling) squeak = M.AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) @@ -244,11 +244,11 @@ . = ..() M.mutations.Remove(CLUMSY) - M.mutations.Remove(COMICBLOCK) - M.dna.SetSEState(CLUMSYBLOCK,0) - M.dna.SetSEState(COMICBLOCK,0) - genemutcheck(M,CLUMSYBLOCK,null,MUTCHK_FORCED) - genemutcheck(M,COMICBLOCK,null,MUTCHK_FORCED) + M.mutations.Remove(GLOB.comicblock) + M.dna.SetSEState(GLOB.clumsyblock,0) + M.dna.SetSEState(GLOB.comicblock,0) + genemutcheck(M,GLOB.clumsyblock,null,MUTCHK_FORCED) + genemutcheck(M,GLOB.comicblock,null,MUTCHK_FORCED) QDEL_NULL(waddle) QDEL_NULL(squeak) qdel(src) diff --git a/code/modules/surgery/organs/pain.dm b/code/modules/surgery/organs/pain.dm index 4863bd63901a..91e4a2f67814 100644 --- a/code/modules/surgery/organs/pain.dm +++ b/code/modules/surgery/organs/pain.dm @@ -81,4 +81,4 @@ mob/living/carbon/human/proc/handle_pain() continue if(I.damage > 2 && prob(2)) var/obj/item/organ/external/parent = get_organ(I.parent_organ) - custom_pain("You feel a sharp pain in your [parent.limb_name]") \ No newline at end of file + custom_pain("You feel a sharp pain in your [parent.limb_name]") diff --git a/code/modules/surgery/organs/robolimbs.dm b/code/modules/surgery/organs/robolimbs.dm index d3c297bc0c79..eb40fa409943 100644 --- a/code/modules/surgery/organs/robolimbs.dm +++ b/code/modules/surgery/organs/robolimbs.dm @@ -1,19 +1,19 @@ -var/global/list/all_robolimbs = list() -var/global/list/chargen_robolimbs = list() -var/global/list/selectable_robolimbs = list() -var/global/datum/robolimb/basic_robolimb +GLOBAL_LIST_EMPTY(all_robolimbs) +GLOBAL_LIST_EMPTY(chargen_robolimbs) +GLOBAL_LIST_EMPTY(selectable_robolimbs) +GLOBAL_DATUM(basic_robolimb, /datum/robolimb) /proc/populate_robolimb_list() - basic_robolimb = new() + GLOB.basic_robolimb = new() for(var/limb_type in typesof(/datum/robolimb)) var/datum/robolimb/R = new limb_type() - all_robolimbs[R.company] = R + GLOB.all_robolimbs[R.company] = R if(!R.unavailable_at_chargen) if(R != "head" && R != "chest" && R != "groin" ) //Part of the method that ensures only IPCs can access head, chest and groin prosthetics. if(R.has_subtypes) //Ensures solos get added to the list as well be incorporating has_subtypes == 1 and has_subtypes == 2. - chargen_robolimbs[R.company] = R //List only main brands and solo parts. + GLOB.chargen_robolimbs[R.company] = R //List only main brands and solo parts. if(R.selectable) - selectable_robolimbs[R.company] = R + GLOB.selectable_robolimbs[R.company] = R /datum/robolimb var/company = "Unbranded" // Shown when selecting the limb. @@ -153,4 +153,4 @@ var/global/datum/robolimb/basic_robolimb parts = list("head") is_monitor = 1 selectable = 0 - has_subtypes = null \ No newline at end of file + has_subtypes = null diff --git a/code/modules/surgery/organs/subtypes/abductor.dm b/code/modules/surgery/organs/subtypes/abductor.dm index b499135417e8..7fabbd96ae1c 100644 --- a/code/modules/surgery/organs/subtypes/abductor.dm +++ b/code/modules/surgery/organs/subtypes/abductor.dm @@ -4,4 +4,4 @@ /obj/item/organ/internal/eyes/abductor name = "abductor eyeballs" - see_in_dark = 3 \ No newline at end of file + see_in_dark = 3 diff --git a/code/modules/surgery/organs/subtypes/diona.dm b/code/modules/surgery/organs/subtypes/diona.dm index 0e76c8fe20b4..3317aca46487 100644 --- a/code/modules/surgery/organs/subtypes/diona.dm +++ b/code/modules/surgery/organs/subtypes/diona.dm @@ -102,4 +102,4 @@ name = "nutrient vessel" icon = 'icons/mob/alien.dmi' icon_state = "claw" - alcohol_intensity = 0.5 \ No newline at end of file + alcohol_intensity = 0.5 diff --git a/code/modules/surgery/organs/subtypes/drask.dm b/code/modules/surgery/organs/subtypes/drask.dm index 49b11d096b65..201c05b83e30 100644 --- a/code/modules/surgery/organs/subtypes/drask.dm +++ b/code/modules/surgery/organs/subtypes/drask.dm @@ -26,4 +26,4 @@ name = "drask eyeballs" icon = 'icons/obj/species_organs/drask.dmi' desc = "Drask eyes. They look even stranger disembodied" - see_in_dark = 5 \ No newline at end of file + see_in_dark = 5 diff --git a/code/modules/surgery/organs/subtypes/grey.dm b/code/modules/surgery/organs/subtypes/grey.dm index 6e4a0e3eafdb..15b7fea22645 100644 --- a/code/modules/surgery/organs/subtypes/grey.dm +++ b/code/modules/surgery/organs/subtypes/grey.dm @@ -35,4 +35,4 @@ /obj/item/organ/internal/kidneys/grey name = "grey kidneys" - icon = 'icons/obj/species_organs/grey.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/grey.dmi' diff --git a/code/modules/surgery/organs/subtypes/kidan.dm b/code/modules/surgery/organs/subtypes/kidan.dm index b87531ac3376..6407917bcaec 100644 --- a/code/modules/surgery/organs/subtypes/kidan.dm +++ b/code/modules/surgery/organs/subtypes/kidan.dm @@ -135,4 +135,4 @@ #undef KIDAN_LANTERN_HUNGERCOST #undef KIDAN_LANTERN_MINHUNGER -#undef KIDAN_LANTERN_LIGHT \ No newline at end of file +#undef KIDAN_LANTERN_LIGHT diff --git a/code/modules/surgery/organs/subtypes/machine.dm b/code/modules/surgery/organs/subtypes/machine.dm index 837e9b5ffb7d..52cad684e11c 100644 --- a/code/modules/surgery/organs/subtypes/machine.dm +++ b/code/modules/surgery/organs/subtypes/machine.dm @@ -145,4 +145,4 @@ /obj/item/organ/internal/ears/microphone/remove(mob/living/user, special = FALSE) if(!special) to_chat(owner, "BZZZZZZZZZZZZZZT! Microphone error!") - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/surgery/organs/subtypes/nucleation.dm b/code/modules/surgery/organs/subtypes/nucleation.dm index 3be564fc8000..bac821f7f6c9 100644 --- a/code/modules/surgery/organs/subtypes/nucleation.dm +++ b/code/modules/surgery/organs/subtypes/nucleation.dm @@ -31,4 +31,4 @@ /obj/item/organ/internal/brain/crystal name = "crystallized brain" icon_state = "crystal-brain" - organ_tag = "crystallized brain" \ No newline at end of file + organ_tag = "crystallized brain" diff --git a/code/modules/surgery/organs/subtypes/plasmaman.dm b/code/modules/surgery/organs/subtypes/plasmaman.dm index 3fd59189fb00..4de19d0950e3 100644 --- a/code/modules/surgery/organs/subtypes/plasmaman.dm +++ b/code/modules/surgery/organs/subtypes/plasmaman.dm @@ -18,4 +18,4 @@ /obj/item/organ/internal/kidneys/plasmaman name = "plasmaman kidneys" - icon = 'icons/obj/species_organs/plasmaman.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/plasmaman.dmi' diff --git a/code/modules/surgery/organs/subtypes/shadow.dm b/code/modules/surgery/organs/subtypes/shadow.dm index 56bec1a4a7e2..d147feb03171 100644 --- a/code/modules/surgery/organs/subtypes/shadow.dm +++ b/code/modules/surgery/organs/subtypes/shadow.dm @@ -1,3 +1,3 @@ /obj/item/organ/internal/eyes/shadow name = "dark orbs" - see_in_dark = 8 \ No newline at end of file + see_in_dark = 8 diff --git a/code/modules/surgery/organs/subtypes/skrell.dm b/code/modules/surgery/organs/subtypes/skrell.dm index 3c1e3cd40ce7..8a56956777f1 100644 --- a/code/modules/surgery/organs/subtypes/skrell.dm +++ b/code/modules/surgery/organs/subtypes/skrell.dm @@ -87,4 +87,4 @@ /obj/item/organ/internal/eyes/skrell name = "skrell eyeballs" - icon = 'icons/obj/species_organs/skrell.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/skrell.dmi' diff --git a/code/modules/surgery/organs/subtypes/slime.dm b/code/modules/surgery/organs/subtypes/slime.dm index 14c89aded639..752375047fee 100644 --- a/code/modules/surgery/organs/subtypes/slime.dm +++ b/code/modules/surgery/organs/subtypes/slime.dm @@ -12,4 +12,4 @@ icon = 'icons/obj/species_organs/slime.dmi' name = "slime lungs" icon_state = "lungs" - desc = "This is a slime's gas exchange membrane, this membrane used for oxygen intake and gas exchange. These seem to work similar to lungs." \ No newline at end of file + desc = "This is a slime's gas exchange membrane, this membrane used for oxygen intake and gas exchange. These seem to work similar to lungs." diff --git a/code/modules/surgery/organs/subtypes/tajaran.dm b/code/modules/surgery/organs/subtypes/tajaran.dm index 99598a80fb1c..e8d87b55b899 100644 --- a/code/modules/surgery/organs/subtypes/tajaran.dm +++ b/code/modules/surgery/organs/subtypes/tajaran.dm @@ -32,4 +32,4 @@ /obj/item/organ/internal/kidneys/tajaran name = "tajaran kidneys" - icon = 'icons/obj/species_organs/tajaran.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/tajaran.dmi' diff --git a/code/modules/surgery/organs/subtypes/unathi.dm b/code/modules/surgery/organs/subtypes/unathi.dm index a612bc7f2538..15409b85d7e2 100644 --- a/code/modules/surgery/organs/subtypes/unathi.dm +++ b/code/modules/surgery/organs/subtypes/unathi.dm @@ -27,4 +27,4 @@ /obj/item/organ/internal/kidneys/unathi name = "unathi kidneys" - icon = 'icons/obj/species_organs/unathi.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/unathi.dmi' diff --git a/code/modules/surgery/organs/subtypes/unbreakable.dm b/code/modules/surgery/organs/subtypes/unbreakable.dm index 245629ba26ef..b4fca2150fca 100644 --- a/code/modules/surgery/organs/subtypes/unbreakable.dm +++ b/code/modules/surgery/organs/subtypes/unbreakable.dm @@ -65,4 +65,4 @@ cannot_amputate = TRUE /obj/item/organ/external/head/unbreakable/sturdy - cannot_amputate = TRUE \ No newline at end of file + cannot_amputate = TRUE diff --git a/code/modules/surgery/organs/subtypes/vox.dm b/code/modules/surgery/organs/subtypes/vox.dm index e7366c748c2c..1ab1eef7238e 100644 --- a/code/modules/surgery/organs/subtypes/vox.dm +++ b/code/modules/surgery/organs/subtypes/vox.dm @@ -26,4 +26,4 @@ /obj/item/organ/internal/kidneys/vox name = "vox kidneys" icon = 'icons/obj/species_organs/vox.dmi' - sterile = TRUE \ No newline at end of file + sterile = TRUE diff --git a/code/modules/surgery/organs/subtypes/vulpkanin.dm b/code/modules/surgery/organs/subtypes/vulpkanin.dm index 820b152c43fa..a2815d32dc65 100644 --- a/code/modules/surgery/organs/subtypes/vulpkanin.dm +++ b/code/modules/surgery/organs/subtypes/vulpkanin.dm @@ -32,4 +32,4 @@ /obj/item/organ/internal/kidneys/vulpkanin name = "vulpkanin kidneys" - icon = 'icons/obj/species_organs/vulpkanin.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/vulpkanin.dmi' diff --git a/code/modules/surgery/organs/subtypes/wryn.dm b/code/modules/surgery/organs/subtypes/wryn.dm index 8900bb8b37d9..cbc605997440 100644 --- a/code/modules/surgery/organs/subtypes/wryn.dm +++ b/code/modules/surgery/organs/subtypes/wryn.dm @@ -8,4 +8,4 @@ slot = "hivenode" /obj/item/organ/internal/eyes/wryn - see_in_dark = 3 \ No newline at end of file + see_in_dark = 3 diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 9d91682f0576..c82a0e899f75 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -1,39 +1,39 @@ -var/static/regex/stun_words = regex("stop|wait|stand still|hold on|halt") -var/static/regex/weaken_words = regex("drop|fall|trip") -var/static/regex/sleep_words = regex("sleep|slumber") -var/static/regex/vomit_words = regex("vomit|throw up") -var/static/regex/silence_words = regex("shut up|silence|ssh|quiet|hush") -var/static/regex/hallucinate_words = regex("see the truth|hallucinate") -var/static/regex/wakeup_words = regex("wake up|awaken") -var/static/regex/heal_words = regex("live|heal|survive|mend|heroes never die") -var/static/regex/hurt_words = regex("die|suffer") -var/static/regex/bleed_words = regex("bleed") -var/static/regex/burn_words = regex("burn|ignite") -var/static/regex/repulse_words = regex("shoo|go away|leave me alone|begone|flee|fus ro dah") -var/static/regex/whoareyou_words = regex("who are you|say your name|state your name|identify") -var/static/regex/saymyname_words = regex("say my name") -var/static/regex/knockknock_words = regex("knock knock") -var/static/regex/statelaws_words = regex("state laws|state your laws") -var/static/regex/move_words = regex("move") -var/static/regex/walk_words = regex("walk|slow down") -var/static/regex/run_words = regex("run") -var/static/regex/helpintent_words = regex("help") -var/static/regex/disarmintent_words = regex("disarm") -var/static/regex/grabintent_words = regex("grab") -var/static/regex/harmintent_words = regex("harm|fight") -var/static/regex/throwmode_words = regex("throw|catch") -var/static/regex/flip_words = regex("flip|rotate|revolve|roll|somersault") -var/static/regex/rest_words = regex("rest") -var/static/regex/getup_words = regex("get up") -var/static/regex/sit_words = regex("sit") -var/static/regex/stand_words = regex("stand") -var/static/regex/dance_words = regex("dance") -var/static/regex/jump_words = regex("jump") -var/static/regex/salute_words = regex("salute") -var/static/regex/deathgasp_words = regex("play dead") -var/static/regex/clap_words = regex("clap|applaud") -var/static/regex/honk_words = regex("ho+nk") //hooooooonk -var/static/regex/multispin_words = regex("like a record baby") +GLOBAL_DATUM_INIT(stun_words, /regex, regex("stop|wait|stand still|hold on|halt")) +GLOBAL_DATUM_INIT(weaken_words, /regex, regex("drop|fall|trip")) +GLOBAL_DATUM_INIT(sleep_words, /regex, regex("sleep|slumber")) +GLOBAL_DATUM_INIT(vomit_words, /regex, regex("vomit|throw up")) +GLOBAL_DATUM_INIT(silence_words, /regex, regex("shut up|silence|ssh|quiet|hush")) +GLOBAL_DATUM_INIT(hallucinate_words, /regex, regex("see the truth|hallucinate")) +GLOBAL_DATUM_INIT(wakeup_words, /regex, regex("wake up|awaken")) +GLOBAL_DATUM_INIT(heal_words, /regex, regex("live|heal|survive|mend|heroes never die")) +GLOBAL_DATUM_INIT(hurt_words, /regex, regex("die|suffer")) +GLOBAL_DATUM_INIT(bleed_words, /regex, regex("bleed")) +GLOBAL_DATUM_INIT(burn_words, /regex, regex("burn|ignite")) +GLOBAL_DATUM_INIT(repulse_words, /regex, regex("shoo|go away|leave me alone|begone|flee|fus ro dah")) +GLOBAL_DATUM_INIT(whoareyou_words, /regex, regex("who are you|say your name|state your name|identify")) +GLOBAL_DATUM_INIT(saymyname_words, /regex, regex("say my name")) +GLOBAL_DATUM_INIT(knockknock_words, /regex, regex("knock knock")) +GLOBAL_DATUM_INIT(statelaws_words, /regex, regex("state laws|state your laws")) +GLOBAL_DATUM_INIT(move_words, /regex, regex("move")) +GLOBAL_DATUM_INIT(walk_words, /regex, regex("walk|slow down")) +GLOBAL_DATUM_INIT(run_words, /regex, regex("run")) +GLOBAL_DATUM_INIT(helpintent_words, /regex, regex("help")) +GLOBAL_DATUM_INIT(disarmintent_words, /regex, regex("disarm")) +GLOBAL_DATUM_INIT(grabintent_words, /regex, regex("grab")) +GLOBAL_DATUM_INIT(harmintent_words, /regex, regex("harm|fight")) +GLOBAL_DATUM_INIT(throwmode_words, /regex, regex("throw|catch")) +GLOBAL_DATUM_INIT(flip_words, /regex, regex("flip|rotate|revolve|roll|somersault")) +GLOBAL_DATUM_INIT(rest_words, /regex, regex("rest")) +GLOBAL_DATUM_INIT(getup_words, /regex, regex("get up")) +GLOBAL_DATUM_INIT(sit_words, /regex, regex("sit")) +GLOBAL_DATUM_INIT(stand_words, /regex, regex("stand")) +GLOBAL_DATUM_INIT(dance_words, /regex, regex("dance")) +GLOBAL_DATUM_INIT(jump_words, /regex, regex("jump")) +GLOBAL_DATUM_INIT(salute_words, /regex, regex("salute")) +GLOBAL_DATUM_INIT(deathgasp_words, /regex, regex("play dead")) +GLOBAL_DATUM_INIT(clap_words, /regex, regex("clap|applaud")) +GLOBAL_DATUM_INIT(honk_words, /regex, regex("ho+nk")) //hooooooonk +GLOBAL_DATUM_INIT(multispin_words, /regex, regex("like a record baby")) /obj/item/organ/internal/vocal_cords //organs that are activated through speech with the :x channel name = "vocal cords" @@ -127,7 +127,7 @@ var/static/regex/multispin_words = regex("like a record baby") /obj/item/organ/internal/vocal_cords/colossus/prepare_eat() return - + /obj/item/organ/internal/vocal_cords/colossus/can_speak_with() if(world.time < next_command) to_chat(owner, "You must wait [(next_command - world.time)/10] seconds before Speaking again.") @@ -172,7 +172,7 @@ var/static/regex/multispin_words = regex("like a record baby") if(owner.mind.isholy) power_multiplier *= 2 //Command staff has authority - if(owner.mind.assigned_role in command_positions) + if(owner.mind.assigned_role in GLOB.command_positions) power_multiplier *= 1.4 //Why are you speaking if(owner.mind.assigned_role == "Mime") @@ -210,34 +210,34 @@ var/static/regex/multispin_words = regex("like a record baby") message = copytext(message, 0, 1)+copytext(message, 1 + length(found_string), length(message) + 1) //STUN - if(findtext(message, stun_words)) + if(findtext(message, GLOB.stun_words)) for(var/V in listeners) var/mob/living/L = V L.Stun(3 * power_multiplier) next_command = world.time + cooldown_stun //WEAKEN - else if(findtext(message, weaken_words)) + else if(findtext(message, GLOB.weaken_words)) for(var/V in listeners) var/mob/living/L = V L.Weaken(3 * power_multiplier) next_command = world.time + cooldown_stun //SLEEP - else if((findtext(message, sleep_words))) + else if((findtext(message, GLOB.sleep_words))) for(var/V in listeners) var/mob/living/L = V L.Sleeping(2 * power_multiplier) next_command = world.time + cooldown_stun //VOMIT - else if((findtext(message, vomit_words))) + else if((findtext(message, GLOB.vomit_words))) for(var/mob/living/carbon/C in listeners) C.vomit(10 * power_multiplier) next_command = world.time + cooldown_stun //SILENCE - else if((findtext(message, silence_words))) + else if((findtext(message, GLOB.silence_words))) for(var/mob/living/carbon/C in listeners) if(owner.mind && (owner.mind.assigned_role == "Librarian" || owner.mind.assigned_role == "Mime")) power_multiplier *= 3 @@ -245,41 +245,41 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_stun //HALLUCINATE - else if((findtext(message, hallucinate_words))) + else if((findtext(message, GLOB.hallucinate_words))) for(var/V in listeners) var/mob/living/L = V new /obj/effect/hallucination/delusion(get_turf(L),L,duration=150 * power_multiplier,skip_nearby=0) next_command = world.time + cooldown_meme //WAKE UP - else if((findtext(message, wakeup_words))) + else if((findtext(message, GLOB.wakeup_words))) for(var/V in listeners) var/mob/living/L = V L.SetSleeping(0) next_command = world.time + cooldown_damage //HEAL - else if((findtext(message, heal_words))) + else if((findtext(message, GLOB.heal_words))) for(var/V in listeners) var/mob/living/L = V L.heal_overall_damage(10 * power_multiplier, 10 * power_multiplier, TRUE, 0, 0) next_command = world.time + cooldown_damage //BRUTE DAMAGE - else if((findtext(message, hurt_words))) + else if((findtext(message, GLOB.hurt_words))) for(var/V in listeners) var/mob/living/L = V L.apply_damage(15 * power_multiplier, def_zone = "chest") next_command = world.time + cooldown_damage //BLEED - else if((findtext(message, bleed_words))) + else if((findtext(message, GLOB.bleed_words))) for(var/mob/living/carbon/human/H in listeners) H.bleed_rate += (5 * power_multiplier) next_command = world.time + cooldown_damage //FIRE - else if((findtext(message, burn_words))) + else if((findtext(message, GLOB.burn_words))) for(var/V in listeners) var/mob/living/L = V L.adjust_fire_stacks(1 * power_multiplier) @@ -287,7 +287,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_damage //REPULSE - else if((findtext(message, repulse_words))) + else if((findtext(message, GLOB.repulse_words))) for(var/V in listeners) var/mob/living/L = V var/throwtarget = get_edge_target_turf(owner, get_dir(owner, get_step_away(L, owner))) @@ -295,7 +295,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_damage //WHO ARE YOU? - else if((findtext(message, whoareyou_words))) + else if((findtext(message, GLOB.whoareyou_words))) for(var/V in listeners) var/mob/living/L = V /*if(L.mind && L.mind.devilinfo) @@ -305,34 +305,34 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //SAY MY NAME - else if((findtext(message, saymyname_words))) + else if((findtext(message, GLOB.saymyname_words))) for(var/V in listeners) var/mob/living/L = V L.say("[owner.name]!") //"Unknown!" next_command = world.time + cooldown_meme //KNOCK KNOCK - else if((findtext(message, knockknock_words))) + else if((findtext(message, GLOB.knockknock_words))) for(var/V in listeners) var/mob/living/L = V L.say("Who's there?") next_command = world.time + cooldown_meme //STATE LAWS - else if((findtext(message, statelaws_words))) + else if((findtext(message, GLOB.statelaws_words))) for(var/mob/living/silicon/S in listeners) S.statelaws(S.laws) next_command = world.time + cooldown_stun //MOVE - else if((findtext(message, move_words))) + else if((findtext(message, GLOB.move_words))) for(var/V in listeners) var/mob/living/L = V - step(L, pick(cardinal)) + step(L, pick(GLOB.cardinal)) next_command = world.time + cooldown_meme //WALK - else if((findtext(message, walk_words))) + else if((findtext(message, GLOB.walk_words))) for(var/V in listeners) var/mob/living/L = V if(L.m_intent != MOVE_INTENT_WALK) @@ -342,7 +342,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //RUN - else if((findtext(message, run_words))) + else if((findtext(message, GLOB.run_words))) for(var/V in listeners) var/mob/living/L = V if(L.m_intent != MOVE_INTENT_RUN) @@ -352,44 +352,44 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //HELP INTENT - else if((findtext(message, helpintent_words))) + else if((findtext(message, GLOB.helpintent_words))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_HELP) next_command = world.time + cooldown_meme //DISARM INTENT - else if((findtext(message, disarmintent_words))) + else if((findtext(message, GLOB.disarmintent_words))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_DISARM) next_command = world.time + cooldown_meme //GRAB INTENT - else if((findtext(message, grabintent_words))) + else if((findtext(message, GLOB.grabintent_words))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_GRAB) next_command = world.time + cooldown_meme //HARM INTENT - else if((findtext(message, harmintent_words))) + else if((findtext(message, GLOB.harmintent_words))) for(var/mob/living/carbon/human/H in listeners) H.a_intent_change(INTENT_HARM) next_command = world.time + cooldown_meme //THROW/CATCH - else if((findtext(message, throwmode_words))) + else if((findtext(message, GLOB.throwmode_words))) for(var/mob/living/carbon/C in listeners) C.throw_mode_on() next_command = world.time + cooldown_meme //FLIP - else if((findtext(message, flip_words))) + else if((findtext(message, GLOB.flip_words))) for(var/V in listeners) var/mob/living/L = V L.emote("flip") next_command = world.time + cooldown_meme //REST - else if((findtext(message, rest_words))) + else if((findtext(message, GLOB.rest_words))) for(var/V in listeners) var/mob/living/L = V if(!L.resting) @@ -397,7 +397,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //GET UP - else if((findtext(message, getup_words))) + else if((findtext(message, GLOB.getup_words))) for(var/V in listeners) var/mob/living/L = V if(L.resting) @@ -408,7 +408,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_damage //SIT - else if((findtext(message, sit_words))) + else if((findtext(message, GLOB.sit_words))) for(var/V in listeners) var/mob/living/L = V for(var/obj/structure/chair/chair in get_turf(L)) @@ -417,7 +417,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //STAND UP - else if((findtext(message, stand_words))) + else if((findtext(message, GLOB.stand_words))) for(var/V in listeners) var/mob/living/L = V if(L.buckled && istype(L.buckled, /obj/structure/chair)) @@ -425,14 +425,14 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //DANCE - else if((findtext(message, dance_words))) + else if((findtext(message, GLOB.dance_words))) for(var/V in listeners) var/mob/living/L = V L.emote("dance") next_command = world.time + cooldown_meme //JUMP - else if((findtext(message, jump_words))) + else if((findtext(message, GLOB.jump_words))) for(var/V in listeners) var/mob/living/L = V L.say("HOW HIGH?!!") @@ -440,28 +440,28 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //SALUTE - else if((findtext(message, salute_words))) + else if((findtext(message, GLOB.salute_words))) for(var/V in listeners) var/mob/living/L = V L.emote("salute") next_command = world.time + cooldown_meme //PLAY DEAD - else if((findtext(message, deathgasp_words))) + else if((findtext(message, GLOB.deathgasp_words))) for(var/V in listeners) var/mob/living/L = V L.emote("deathgasp") next_command = world.time + cooldown_meme //PLEASE CLAP - else if((findtext(message, clap_words))) + else if((findtext(message, GLOB.clap_words))) for(var/V in listeners) var/mob/living/L = V L.emote("clap") next_command = world.time + cooldown_meme //HONK - else if((findtext(message, honk_words))) + else if((findtext(message, GLOB.honk_words))) spawn(25) playsound(get_turf(owner), 'sound/items/bikehorn.ogg', 300, 1) if(owner.mind && owner.mind.assigned_role == "Clown") @@ -472,7 +472,7 @@ var/static/regex/multispin_words = regex("like a record baby") next_command = world.time + cooldown_meme //RIGHT ROUND - else if((findtext(message, multispin_words))) + else if((findtext(message, GLOB.multispin_words))) for(var/V in listeners) var/mob/living/L = V L.SpinAnimation(speed = 10, loops = 5) diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm index 0aac0bf705f1..ed93f95c3c91 100644 --- a/code/modules/surgery/plastic_surgery.dm +++ b/code/modules/surgery/plastic_surgery.dm @@ -53,4 +53,4 @@ user.visible_message(" [user]'s hand slips, tearing skin on [target]'s face with [tool]!", \ " Your hand slips, tearing skin on [target]'s face with [tool]!") target.apply_damage(10, BRUTE, head, sharp = TRUE) - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm index 340492545bea..2040138640f8 100644 --- a/code/modules/surgery/remove_embedded_object.dm +++ b/code/modules/surgery/remove_embedded_object.dm @@ -63,4 +63,4 @@ else to_chat(user, "You can't find [target]'s [parse_zone(user.zone_selected)], let alone any objects embedded in it!") - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 31164aaf54e5..62efb2980069 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -575,7 +575,7 @@ ..() /datum/surgery_step/robotics/external/customize_appearance/end_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool,datum/surgery/surgery) - var/chosen_appearance = input(user, "Select the company appearance for this limb.", "Limb Company Selection") as null|anything in selectable_robolimbs + var/chosen_appearance = input(user, "Select the company appearance for this limb.", "Limb Company Selection") as null|anything in GLOB.selectable_robolimbs if(!chosen_appearance) return FALSE var/obj/item/organ/external/affected = target.get_organ(target_zone) diff --git a/code/modules/telesci/bscrystal.dm b/code/modules/telesci/bscrystal.dm index b0b2941b3d2b..0f9b205f391a 100644 --- a/code/modules/telesci/bscrystal.dm +++ b/code/modules/telesci/bscrystal.dm @@ -54,7 +54,7 @@ // Polycrystals, aka stacks -var/global/list/datum/stack_recipe/bluespace_crystal_recipes = list(new/datum/stack_recipe("Breakdown into bluespace crystal", /obj/item/stack/ore/bluespace_crystal/refined, 1)) +GLOBAL_LIST_INIT(bluespace_crystal_recipes, list(new/datum/stack_recipe("Breakdown into bluespace crystal", /obj/item/stack/ore/bluespace_crystal/refined, 1))) /obj/item/stack/sheet/bluespace_crystal name = "bluespace polycrystal" @@ -70,6 +70,6 @@ var/global/list/datum/stack_recipe/bluespace_crystal_recipes = list(new/datum/st /obj/item/stack/sheet/bluespace_crystal/New() ..() - recipes = bluespace_crystal_recipes + recipes = GLOB.bluespace_crystal_recipes pixel_x = rand(0,4)-4 pixel_y = rand(0,4)-4 diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index 00999b47f7af..94920587b540 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -1,4 +1,4 @@ -var/list/GPS_list = list() +GLOBAL_LIST_EMPTY(GPS_list) /obj/item/gps name = "global positioning system" desc = "Helping lost spacemen find their way through the planets since 2016." @@ -15,12 +15,12 @@ var/list/GPS_list = list() /obj/item/gps/New() ..() - GPS_list.Add(src) + GLOB.GPS_list.Add(src) name = "global positioning system ([gpstag])" overlays += "working" /obj/item/gps/Destroy() - GPS_list.Remove(src) + GLOB.GPS_list.Remove(src) return ..() /obj/item/gps/emp_act(severity) @@ -35,7 +35,7 @@ var/list/GPS_list = list() overlays += "working" /obj/item/gps/AltClick(mob/user) - if(CanUseTopic(user, inventory_state) != STATUS_INTERACTIVE) + if(CanUseTopic(user, GLOB.inventory_state) != STATUS_INTERACTIVE) return 1 //user not valid to use gps if(emped) to_chat(user, "It's busted!") @@ -54,7 +54,7 @@ var/list/GPS_list = list() return var/obj/item/gps/t = "" - var/gps_window_height = 110 + GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show + var/gps_window_height = 110 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show if(emped) t += "ERROR" else @@ -66,7 +66,7 @@ var/list/GPS_list = list() var/turf/own_pos = get_turf(src) var/own_z = own_pos.z - for(var/obj/item/gps/G in GPS_list) + for(var/obj/item/gps/G in GLOB.GPS_list) var/turf/pos = get_turf(G) var/area/gps_area = get_area(G) var/tracked_gpstag = G.gpstag diff --git a/code/modules/tram/tram.dm b/code/modules/tram/tram.dm index 6ef45b8cc44a..ea43f3fd6591 100644 --- a/code/modules/tram/tram.dm +++ b/code/modules/tram/tram.dm @@ -84,7 +84,7 @@ last_played_rail = RT return stored_rail = RT - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) for(var/obj/tram/rail/R in get_step(src,cdir)) if(!istype(R)) continue if(R != last_played_rail) @@ -99,7 +99,7 @@ if(!T) return var/obj/tram/floor/TTF = locate(/obj/tram/floor) in T if(istype(TTF)) add_floor(TTF) //Find and link floor on controller turf - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) var/turf/T2 = get_step(T,cdir) var/obj/tram/floor/TF = locate(/obj/tram/floor) in T2 if(istype(TF)) @@ -115,7 +115,7 @@ var/obj/tram/floor/TTW = locate(/obj/tram/wall) in T //Find and link wall on controller turf if(istype(TTW)) add_wall(TTW) for(var/obj/tram/floor/TF in tram_floors) - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) var/obj/tram/wall/TW = locate(/obj/tram/wall) in get_step(TF,cdir) if(istype(TW)) if(TW in tram_walls) continue @@ -201,7 +201,7 @@ collide_list.Cut() var/list/collisions = list() for(var/obj/tram/wall/W in tram_walls) - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) var/turf/T = get_step(W, cdir) if(istype(T)) if(T.density) @@ -212,7 +212,7 @@ if(tram.Find(A)) continue collisions += cdir for(var/obj/tram/floor/F in tram_floors) - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) var/turf/T = get_step(F, cdir) if(istype(T)) if(T.density) @@ -243,6 +243,7 @@ qdel(src) src.visible_message("[M] has [M.attacktext] [src]!") M.create_attack_log("attacked [src.name]") + add_attack_logs(M, src, "attacked") /obj/tram/bullet_act(var/obj/item/projectile/proj) if(prob(proj.damage)) diff --git a/code/modules/tram/tram_control_pad.dm b/code/modules/tram/tram_control_pad.dm index 16d2252ef82c..3bbb3811de21 100644 --- a/code/modules/tram/tram_control_pad.dm +++ b/code/modules/tram/tram_control_pad.dm @@ -30,4 +30,4 @@ usr << browse(null, "window=trampad") src.add_fingerprint(usr) - src.updateUsrDialog() \ No newline at end of file + src.updateUsrDialog() diff --git a/code/modules/tram/tram_floor.dm b/code/modules/tram/tram_floor.dm index fc3b1d162d58..ede3013567b0 100644 --- a/code/modules/tram/tram_floor.dm +++ b/code/modules/tram/tram_floor.dm @@ -11,10 +11,10 @@ var/turf/T = get_turf(src) if(!T) return if(!controller) return - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) var/turf/T2 = get_step(T,cdir) var/obj/tram/floor/TF = locate(/obj/tram/floor) in T2 if(istype(TF)) if(TF in controller.tram_floors) continue controller.add_floor(TF) - TF.spread_floors() \ No newline at end of file + TF.spread_floors() diff --git a/code/modules/tram/tram_rail.dm b/code/modules/tram/tram_rail.dm index 2a22f8d42389..254f2e8636b4 100644 --- a/code/modules/tram/tram_rail.dm +++ b/code/modules/tram/tram_rail.dm @@ -5,4 +5,4 @@ icon_state = "rail" var/godir = null var/stop_duration = null - layer = TURF_LAYER + 0.1 \ No newline at end of file + layer = TURF_LAYER + 0.1 diff --git a/code/modules/tram/tram_wall.dm b/code/modules/tram/tram_wall.dm index bfde77f6a4c2..b39417f5f854 100644 --- a/code/modules/tram/tram_wall.dm +++ b/code/modules/tram/tram_wall.dm @@ -12,10 +12,10 @@ var/turf/T = get_turf(src) if(!T) return if(!controller) return - for(var/cdir in cardinal) + for(var/cdir in GLOB.cardinal) var/turf/T2 = get_step(T,cdir) var/obj/tram/wall/TW = locate(/obj/tram/wall) in T2 if(istype(TW)) if(TW in controller.tram_walls) continue controller.add_wall(TW) - TW.spread_walls() \ No newline at end of file + TW.spread_walls() diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm index 7b4797c6fe3d..450a6c883729 100644 --- a/code/modules/vehicle/ambulance.dm +++ b/code/modules/vehicle/ambulance.dm @@ -118,4 +118,4 @@ to_chat(usr, "You unhook the bed to the ambulance.") else amb.bed = src - to_chat(usr, "You hook the bed to the ambulance.") \ No newline at end of file + to_chat(usr, "You hook the bed to the ambulance.") diff --git a/code/modules/vehicle/atv.dm b/code/modules/vehicle/atv.dm index 39d3e7e4612a..a077c848f050 100644 --- a/code/modules/vehicle/atv.dm +++ b/code/modules/vehicle/atv.dm @@ -74,4 +74,4 @@ turret.pixel_y = 4 if(WEST) turret.pixel_x = 12 - turret.pixel_y = 4 \ No newline at end of file + turret.pixel_y = 4 diff --git a/code/modules/vehicle/janicart.dm b/code/modules/vehicle/janicart.dm index 7df2f8da59ab..12ed9f727eb5 100644 --- a/code/modules/vehicle/janicart.dm +++ b/code/modules/vehicle/janicart.dm @@ -93,4 +93,4 @@ mybag.forceMove(get_turf(user)) user.put_in_hands(mybag) mybag = null - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/vehicle/motorcycle.dm b/code/modules/vehicle/motorcycle.dm index ea165c1c8df6..d208471fa748 100644 --- a/code/modules/vehicle/motorcycle.dm +++ b/code/modules/vehicle/motorcycle.dm @@ -26,4 +26,4 @@ if(dir == SOUTH) layer = ABOVE_MOB_LAYER else - layer = OBJ_LAYER \ No newline at end of file + layer = OBJ_LAYER diff --git a/code/modules/vehicle/secway.dm b/code/modules/vehicle/secway.dm index 48821a6e6308..b3f022c5fe88 100644 --- a/code/modules/vehicle/secway.dm +++ b/code/modules/vehicle/secway.dm @@ -13,4 +13,4 @@ /obj/item/key/security desc = "A keyring with a small steel key, and a rubber stun baton accessory." - icon_state = "keysec" \ No newline at end of file + icon_state = "keysec" diff --git a/code/modules/vehicle/speedbike.dm b/code/modules/vehicle/speedbike.dm index 75e25c2c5e6b..6de2b58d99ac 100644 --- a/code/modules/vehicle/speedbike.dm +++ b/code/modules/vehicle/speedbike.dm @@ -47,4 +47,4 @@ /obj/vehicle/space/speedbike/red icon_state = "speedbike_red" - overlay_state = "cover_red" \ No newline at end of file + overlay_state = "cover_red" diff --git a/code/modules/vehicle/sportscar.dm b/code/modules/vehicle/sportscar.dm index 0f9543d48115..07352f3f65de 100644 --- a/code/modules/vehicle/sportscar.dm +++ b/code/modules/vehicle/sportscar.dm @@ -45,4 +45,4 @@ if(dir == SOUTH) layer = ABOVE_MOB_LAYER else - layer = OBJ_LAYER \ No newline at end of file + layer = OBJ_LAYER diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm index cbaa3dc56e59..79021affa251 100644 --- a/code/modules/vehicle/vehicle.dm +++ b/code/modules/vehicle/vehicle.dm @@ -221,4 +221,4 @@ spaceworthy = TRUE /obj/vehicle/space/Process_Spacemove(direction) - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index c8214ed8283b..0f0f85b088ba 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -66,4 +66,4 @@ var/obj/item/organ/external/O = L.get_organ("head") if(O) O.droplimb() - return (BRUTELOSS) \ No newline at end of file + return (BRUTELOSS) diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index f88ed23c1078..6ea40c90e961 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -96,4 +96,4 @@ to_chat(owner, "You are now a zombie! Do not seek to be cured, do not help any non-zombies in any way, do not harm your zombie brethren and spread the disease by killing others. You are a creature of hunger and violence.") /obj/item/organ/internal/zombie_infection/nodamage - causes_damage = FALSE \ No newline at end of file + causes_damage = FALSE diff --git a/code/world.dm b/code/world.dm index 8e8d21761723..c79588e7c6ca 100644 --- a/code/world.dm +++ b/code/world.dm @@ -1,9 +1,9 @@ -// This file is just for the necessary /world definition -// Try looking in game/world.dm - -/world - mob = /mob/new_player - turf = /turf/space - area = /area/space - view = "15x15" - cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session +// This file is just for the necessary /world definition +// Try looking in game/world.dm + +/world + mob = /mob/new_player + turf = /turf/space + area = /area/space + view = "15x15" + cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css index 7a2c61233837..e7b79557619a 100644 --- a/goon/browserassets/css/browserOutput-dark.css +++ b/goon/browserassets/css/browserOutput-dark.css @@ -381,6 +381,8 @@ h1.alert, h2.alert {color: #FFF;} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} +.specialnotice {color: #36525e; font-weight: bold; font-size: 120%;} + /* /vg/ */ .good {color: green;} .average {color: #FF8000;} diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index 87de7558ab9a..962ce7bb8e7e 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -378,6 +378,10 @@ h1.alert, h2.alert {color: #000000;} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} +.specialnoticebold {color: #36525e; font-weight: bold; font-size: 120%;} + +.specialnotice {color: #36525e; font-size: 120%;} + /* /vg/ */ .good {color: green;} .average {color: #FF8000;} diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm index f782e663d0a2..f32b1d536109 100644 --- a/goon/code/datums/browserOutput.dm +++ b/goon/code/datums/browserOutput.dm @@ -310,4 +310,4 @@ var/to_chat_src return SSchat.queue(target, message, flag) -#undef MAX_COOKIE_LENGTH \ No newline at end of file +#undef MAX_COOKIE_LENGTH diff --git a/icons/mob/alienqueen.dmi b/icons/mob/alienqueen.dmi index 5d09aad248097ed5be854efed3c4e252c09c8977..b01af7e71b4892029136cb87eb6ce8f0b8053df4 100644 GIT binary patch delta 22980 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

    }1cHHO$z5#x`RoveYP)Lb5kx-$fE-k5S0J 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 T6J-&P0Pv@yseiKqV;lKDT6--& delta 31701 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 diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 87f4688dadb2e56bced0b1f37ac4719415d6d299..26cd5a1337b8bfedfdb65a1642fb166aae208e8f 100644 GIT binary patch literal 152689 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⁡j+HAoc_;}-2!hBO^h$^c^;6EL7-&3 z{%hN+JLJ~~)`(LAuJzbV(B@|Yfs)?YHKhmDEWkYDR1&>%l}U)8j(`mtPCq+Kn}!V=E^iniy*WEi}w+KreGbzS^ef z^f!Kikuw_HeU<=2z#$^SQ0*Lt5Zb9l#_@^Ep_4Ch zyBeDMJVFOCc5bCkmkvdq)wbOZx)&^lXLyuV7hifv1Wxd7jS?2pv+5j99p=H%ID7_j_81S@U*X_6XVOuaZUF-K6q2v z+Q4q!tke+GQ@$Z?V8n$8Jsr61usp-GmYU87_)R8gS*Wa!T=`LB?o?GDQ}41S%F@;` zV{ZD|&MJC5P_Cu9zLIj%t#UW8|ENp?fL+Mtql{Kc%)V`Piocp{p(|7ST=>k)H4^BF zKi0;oUyayRRHmEkDGMgv*B9+4biRt+Q)s})YnCl*X zUH$_g9a*z+mqYbrC1*{1MAA`_n(w6=jnWBM%`TGE`Dq=wWEEd4Xpogzdy z`szx4$5d$eYSZKWMDk7_I<|b!USA`e`#fG^W?}IbQs$|qKB^eQ2pE; zXa~3{icK<4QCzOH3a*JWtT6s@1~|*FT)Rfjqgn6()Yhu|8i8^f8s7e! zjNr}Nx4qj!&E9}a`mPUG#yuaZ^XhvA4*3;kT)Zf&;$J!ItY6*j!qn*;A+&Wu1pWB) z#~|(o6LIFFd?ZuWD>wH!b*U%PB>6larvHpl{8lNMY6#?ZQP-heBEe; z_pKE3$e2GFc?Lqs-c4NdPANPOV>RSm_;s~b^Jh7UwLvwU?Mz!zT4BY@>eVX3!McYj*3*-K}X#5pPM`T=(M}A&&kQgG)b@+X`AF_9v;u`$HZJNmKoW z_iDCwbUXqmwCCB%tia!+GajW~u$=wI^qrrLh(aEyf4{+B)a=0~CJ!tMO5QBoc3sz3}{XA@L>&TFCmGYaUe> zDyrhss}@TcdiL$rTjuGKx>GjkIjU8VV19&}q{psn4Sl4H95V>e{4WYxdN|#2${A~Z z@>s0D^|E|U-wDW-8pTCS&1^FYrR*xuM8{ozCGX^aV}VagG|pg8kCU4j8vd;q3zq4Sa z@2NOn-?U{@kSW7jdN%yOi12{fwjY{Do`0JOfmjX)D`Z-Zhn&%&XN&wH0#*b;Zd#NXXbc$P~Qjdry=pZn(D#yois{_?7^oMs)14}Ayr zpKIq7h2li?M(VN$^51aLfu~3Bx&*#tV2B`?4&yHHqvAWEW)i2_#fJ1Upd!PgIL-I^8K8K`?EVXP}$=YA?Gh;2qiGT0Ki;B@-ix!t(-P2;C;czi_cC~LHV273?Ka^kOZ zalM_LHC-}{l`FTg$Q!txbY-}RY^$Z2odHTb6cn}_HeK`s??G_bG2=CmXWdPRv=ZNa zIfeVSQje$Gnze?Q@n6rIi1u_6TJu`>)iScvLZ!RpC_ciSH9Dp8neXpFr$w@6_j-;_ zIrhbMa|zH!!yNMla(13Zrj%O<`czbex3;FRHFy(hwjn?_RS*Th4=ks5s-uR1oqCRT zBEQbyr;cYC*7^Z7pRi1K&|>aINDI9ESvHBmUlZg&cf;(ijz?-Obk5RGK+)Q|G4?I)VzAD;#vn4%LyKgTKG5xWe3W%>E>)1{zQ{AR>xByW<&mQfZ8 zrC4pFsmnXx+7F!6JvG5Nv7@cKDn(8DrNh>4)h(9GD{W zRz&~&su3CXFs)#yODv>kw_%3AdAkCd{L+>E=6(8L+=Ejm*YoaL@0RzMR<*j@oIRBu zVJUbqM=#Nc+*pI$e3O)b^dE=XorJ+ES04MSDb7)sMkVR;?22N5T+|wQUuAuh(FAV+ z&FgP|u4{kuc6IGD6Ib7H&s83~{`p?Zp_;6Z1goK%8=7`fk?w2+?o}8+XwHMBmu1I; zaLDjcc{+CRHI$2$TWwsNT-DI<%T$U*TwYtO$u!@d=q$GK!N{i)Q_KhuVPf29S4A@{Y6|#zYi|Qg9_rj+L_ZPcW{}Cf&a~-5oQC@~vL6c?I0*#@{oq9i0nqvr#aI zMH*{D_)5p+&fL3iI(eDNq8I5CwTqc-9&%R_j|oC}?LkKU(hX@UJEKS#-X z352~s)@uH^g06LEcC(Omuhbj=jGVo3rjLtIE3v&cntShjllsPZAHAx1-UI=c`eSV7 z-~sJK=|mej8+BFn)%u*2*c4NW13AF=3;*hO9bk9_8fSMTde5uh!b?7CAjhjkuHGd#5E{q4qBW=SpvMSl(b+tb&HI@m+|h$AG?kl( zFX)nJp_z!Z7L2?33=Rg$Qw62*0F6`?K!=KcOdT)*1{4~5Z4)X zKR+oZVcRf3Q1kI+V~D1E^F2iEZi+x@CkV^AH?yBz1{OZd*j6u+Ys?|^+-hOIml59x zd2Lbc=I`~@+PfqY#6R^~)Df!fTH5`!%yt27N-&r2rCspY7Qr5CAq;p)`PJYssHZ`V zdU*1fR28^0EQZ zwIo;FBob&a!3unp-*=vyy=Bu6N=2qZ38TtF9G`yO;IV{n9IEMnh1FZ~BcuIG6zihk zB>9lA=?Rj^rExooF|gS$DcTTr)Q_{%Ewg+LGu%2=q$pYl_#prA0z9*gbT*_AbTb^R zl>#md9pLJoJ2Mt|dV*|nKGh+i`-WQknAE`gqv2dRSy`0PFMIaaAvg%RhYWCqI0M$2 z&w1V@U&vioD9da0XXcEW*H)we)*djKureZQtu1vCiYkd1l&wH>(I$YDg_#;b_>+5= z&hFVhzB3ZSjCm8Lj=XPv_B_qD-#jC_;s`Gk4e9=hJ&XHc(gYcLfHOQ|ujU6K5uiXC zQc=6auy$m-u^Su<$l*9R(*70AoYep+w%|Kgt6CJt5O_ipA9h%@llvOj_VlI!X79M#IjE+-z^d~uz9WWU!fZY?{j ztScG*#zA>yWslKk0s^EJ-^34dguO8=80bOR_W9is%B7Vq>wB2xgueLb@hP%ST=bon zrMAMQ;udaQ?WputtjS z;MDI;>VGm#QvFFVK{~(Z|KCKI?0>RpkWT-<3V)XfQhBU5A+cX#sr>Mc$F13qm3E|u zs%^4C;N<@P?KYbc6&17!{RbW1cnvDEJ>M;fiUq0>WU5?dH@FjTVRxUG+k1!jShQcq z`ufg7?L&m<*8*EFFi|GCz-#OQKnZO<~!JF8#b zd#;SuP2rdzZo0wGnMZ*iOV%VRuwVY=r4zXa2b03F$ zcTQyB@o{d)=f(~o&GbyoGvyWya`)S~xAa1pV^z@$=2m`eif_Med2nWRnjMPlx7`fvKm{rd?;#a zT0_#ru51l$?K=&_j&#r2-Pe+pJubgvNUEc*;S<~?TKOoJ_}Hx!>(!U$wnQ~G(G_o@ zlAU-T!{GsKkevlm$ItGw{@RH-4+b&@EQ7SVw0fry)#%*5vk-svw67-;0G;3LipJP|X z0UH=gHWi8gtxhZQBRSk#5SG~X7JkBA?$7`>6Lj8Z`z zZfSk)7_!Lf*EC3fpBfVVZb%L0<}p@nW({7;U#70jCg|GV^~!Z$5gchf#&;&<>R0v+wgNyU`EW?DB7MP z7T9eOB_Iz{gA`2IlLUV3^^djQKf+)0iH3$4V!fws_zR1gy0t~GkG(YZ)k*6&P)_rK zyWkEqD!(dlRpt;VoPN@VX7^*twG4;UWoLtfsXvd1T*Gu76F#~@(}Zikl8%5Ej&Mzk zfDQdQA8b6ARWXgeTV;I~)&Ykag4e_Vu2qrcnnsL;-Hj{`J6#@8JMsFj@EjqCT6>zf zmMAR1=j@_$Wmt`nhvUzwZ4hy6d(_ph)%MG>Z;-;gYZ&>|-6z-4`qh51$$=}&$$<&Y z!f98sD`qsD8k~qVd&MuB*{M32hEi312cAn4ve#=NV#21!V7H8NFnQ7pJo@TzLA2`& zJ{A}CUEg9YkdbC?W`}uhf59*gpv4qM6lmMDC&WLfdc!=hg9& zC}(sj-Z}OHc>z#-kR+b0tWRHx_M#Fyj7H;_M6bT8n)*7hV4=o&VE@z?Npwi}+4aGu zFE9(+o~Mi9h5K@Ia8w_E;Lk{fTlJB8`liUcjQS?- z&Mb)XS7~LU`9EDJ`tB(z<~(0@yJ^rkHMn5mok}m_5;aECyS!EL=FBbPN_B|^{+!kL zr>xbXY2L7?+KuH=aoGEv4Iy*fE~~1q5&E9!oGI3hhjpSHbM3K_n%giQvZF;@|*nDZvaB_KxRlraSEpSupRXQu?ACc7GTrC26jZ(wV00 zEih@9W8Z-|eh#dW2S^xV4NMoW&m@v))1o8{Ki5Qv$D4@mB!l01t`;{(c(I0g&}If+ zHpgQ}27a2NmGXe!;p2U|cS%Uzu2N*7FyX7ln_DA@vypPVY6r&eYkRc4l=iFoSa{=J z%Y;7N4`#5ogQ29!N>3ZF998t^$FF&Lzp^|+%&kL-pRx)YiHC1lbci;*c4iI!gk$+7 zCT|nP!j6??TuG&QbOKU%U^Ql-f1_UR=o zyt68phgMC5qsK_AZHm@y2K^rSRi0x7dp5C$dGquNQ(2)B<=#&X(j|Nd(S3~-#K#)_ zNfjMWYt+11-(Mq+-&Xno&%N*2T{>)2kQ#L!Jmh}B(;Xj9>R%D+5ZTa{#qP|)(#{_5 z*wNBXF$KA3HanW|bGhy{8GTe!3qhnXh&cBNUy->xUZ6{t$J{64!L-T5%*QqDI%-dm zC1ub6Jm^~afQ6h%w5Vy+8&}JCx=AVTd9#Sa*|}mx^AY52!tPtS!XS5~Lie2Zi2Y81 z9Z#&Le`+6tnv1Aeu0(}pOxTN(Z7{WOHM85;cpnRPP!leZwRbrOz6>}w7BDA7_6iV1ZZli#c-betGO-OS zDi)E`vQr(lY0w_SE_5gbMloIO6?PcE&B4*ne>=W+_x8J=p?)_A(aV*F)}@CMR%*W# zhcDji?Ma_F$0Et$8EWlgGZD?SG96TBAhQ_q(7YG$)vDN|k9Mnw9wt!Ct>eo;9i>)` ze8>Ddhb!RYEODB9>PbApn*qdY5sCpoc0Z4UG$+~sfkuiY8FZ|?x)e21n9(%0{vp?yRE_fNzc8CL4i%*x>pN+*ZJJftEKN1=DkVyv%{You< zJQmRHwty@&jDxWkgGRs!9*3iiXea!)R|0;AdvcA701=!E!Z+H+W|~@cVTYNdA(OnE zr_rMczv&+% zwLh*ay(B<vwm^)7P!|S%HkVvEe^^SRmp*Y9xx5qq_29K8g@-y|LtX%eF^- z7-`Sjp;I2W1^e~z7|&ocJ0n8{?9?*jw^Th}Ix}uL=B~UAvdW~&%@pElz2)h-`J>)i zwAAzU2QO6=keFp_h3A!P^dnQ_j~sVVbQL+J73}ZrwcGXK|x(KLG#=$$Esx9by_Z(GO>p&}n9l!TB zpKHcJ#;BO5-<1>dKvl~X(;w6?s?i`yULN;cem*B3Nvb>qa^B#7l~7*G-zUM_|4%yW z|Gh%|Ad|7wBIna5j(9A{lHtGlH^s(-Sa8bKYQoQ{^#()RwdnRMK~#_{&D!0T_eOkw zSN6(!NCacf+GV$F7woXcFw%<03>Vi5*FwDa#-RqBMnX!RP<905ElFTdNecg+sR9D= zxM$k_pns{Mb91|mdJd#ddEY1>trOG6;nbOIQRxNfGz9y=#h!_bI;M>}Ho9^+Nko7g z8ujrc9h|f^m^H03S`Q%g1po%_!;5-s5Kdee7e_%0)o^GhEo7wDnC1^zw5NaM!;T>A z=W49U%NaM?ZO~XD6SsBzW0gy&YHbQ!5DwI- zr^=OiWxYESGnFY{7%g@1INi_lqf6D-(zA?$ch+5jM^Ni`LI?Q^jaD`z;h4zz6sOIItVv~kulSVvfImY`qE78E5*JJON zP4j$O5J=~7^70>}MV(-UcScu|qzp(2sp99@MoTK%sgY)YLRfFj<)y{Y-bTphscN;* zkn3kAOz423ectx(kW!h}3NZ9qGIM=hQc6{|g>&wd$mau+oB53}VTVBu`(2BPa*Kh} z(H)@lG54-nP)y)?HcnvlyNq}QaeV#9B=QcazWV@v*E{6RSkw)-)c7k0Sd*@ARib&$ zCjBp3cY{?->l;`H=(JSmuWhyu0>nJIU~d@GOa0O|GiKrdPP3<dm!QA zy&D`(05#F?`@ybIr@ZIV8CqsxMF7TBmRDnpS-La&`5?m6IT~StuRhB7$X36g03j}o zk}X$;PYi5Md)QCo`?H;?PA&(3v01f`GBLKp&{RoFIychj9PG5yh~&5iZkNU1s%l!B zzt4#Y+3&*cqwVMJxY`w`!xaWD(LNg}Jc~I)`&%@=U}J)a&XML%v{@R+NNkI*l`w%oFle#*nwPk4U}e+9^87RSj%BP4=+tLspZ zSZ`}cH4m*=K-+e9a0?vomk}0s_J?NOCj#}D>0)X)mHJ524M2b0tf=1#ds30>w(udw z(ZREi>F$znyt``pq4&{zL5=iX`_cP1I0W3+^z52fek%2#I846rOUc7goatu z-652o#~_ZHZ}G<4OT15qvj-P|XBHT2Huo3cA_o$&_jMc8wY1s*Ud{mjE$VKP0sOAR z+VhUoigyUN%4fr6qECRuYkOXN^LvFF3^;sJz56sKP>Qm$yQ`wtiF(9hpZz&h+3*>j zw4L3&#qnyKbx&FD2X}+!+z#D)4p%u!EV^L3c0)C;%cQZ}nHddDi9{+c*g*I>O1yTg z`zstK;tbC(Np)TSE50Se=AoCd2e1EhSlP<&ej>ib`+d^cJ$C)+tAzLD+^{+OGAHsv zoj9!8*8oXAPny&Bjxb7Fji;f{7F?ush5c%#f@-IZ?t%Vs#b}p{ya$zuNhkNnPNUX&BT;-$e5xeZ1v8RQp^})> z(C`qz`eF`E&}1*voL>OjiWz=w%&B>Cqloail(%;uRRkTQ4$jasx;`^-IY6Cx9(teD za$4nTKcnNo-XiuS;^glh0H?2xW!zsRD-DC0oRYCqtsTlSAx*s+hMKK667^@js+sja z6gnv4N1D@%f4AOuOYmb}-qXCiZTtE;`BZ5wH8l`T$YQnbJcP9D=CA=RD{6FK5M|K& zrJz~h(T!W%w+4@f=IXM${>7x9GBF2b-5{oAW@hN3gRAF#Z7%;#JS-=~B8o;!}jCXc5zC$8=slV=K%< zk3AgFLun|n^3f@D>byE-El*v8krxC~kyYaQxrdbg%wI4I;SLqWq#@T#tlef+$uCaZ zO@HB4(_ve~f49B0zrX(k>L3ssFy}(52w3@&dKgj7 z$ih-d{q#LIW}Eg@+|30OR&03KqOG8!%fQInavQ0dk$)7be6RtzygHb*5fmQkjkg~H=^uN_)D1G(~@8;?tr^gh1k zy?ZEs&a1{Dv=gal_9(O47XL>M)%qaW8F=|!Xe&@j)d8y=m>QdML3-#N33k6SMoRny z4Ax_|1}2qkWr)$8zb=<|C4dm?7F{<}9HE;N0vY;~Z<}-n{AmZt*+@I2?f>LZ`M(!V zK_PYjCvVNHS@j?1!rqd6V`#;o;ePNxbhjkRvv#%{f(a#KTxd}fKffgf!z$m-4s!H_ zgw`@S9gk!=rG^g_Vuqs|Z@abIOQN^4_?<~wjp-TPU%wmj z&}uP*j0-AEOzSDtJ;Xj*C?3z_%~X&pqwlCDqcaZ^iodbzqa!tOaF(t`}tZ)ApIZcp#5sOZ{BTHweRC?5a`^Z%6XV>q0)?8WkUMf-{Z${&*%yi;MM2JiqNsy0O`&8Sx4ho8A}7#3F)w@Ml3%uUkPKwlOQ?`|>ZhHlWsS z9fi=%nP<0dVMU+*^xnOgU-0mg{-kYq`C`)x+AC1r+{PEm_L9%4`I=j44bdfd;Y`kkp~ zyhlK+xOgza3NkR$^;W%oNbE?j&OX#(T%v7>UwZRjEP(btuA%^KLj&>Wvtcjm-;+in zi4U4-OZtbYcFGwzIWb?peS0y4OqUu$76Pou&Xw?Wcv)Zz=$`jgK zJR|&OW`uHsn+fsQil0As+azyDx(G9S`1R{1&d*S3>oy;+*{DGE56tqqlomueN#j!D z$Ym+<_ZJ<_v;r1u^0~uzUTjl7dLFK$sU6&M^O|3Ka$~kvc79^Nf;kp`16UhVV8@Bt zrMq444K(@FIw;c(cQ7A`mHauDz=cxk`V$P?LNI`z++!*~fD;~GK`+%tkVZm`{odO8 z=BT>Ixk-yozZL@SzNLWA2Bi>q(Gy=(*$H9Xih8vjSidNxe);m_X!-FWTFolI80nnW z>9taa2V-sb2>0Tvrm1Z7u;ZYO?hMtCHP{sne1K1-ZB2T?Jc8_}23F_t4u&-kX%~uv zm!_(|Y+j33g*-P+3%R;Llp9jQ;Zz?Hk|O{6NjX)mChrtH*uFYOgdSlFygFpd3v0el()06;lUTO^x_FU zZqN3$9T1v6Ua>bgtU|jww?4Q9G_CBJP!wMhBX@yTWNxt3EzHdUl>LO@7Yp79c9ch8 zm3JS0cJ28@zPGK?TYwPFL?t@ioGmN=@*~)_%XwJ%s*%8J&}x#3+Fz?HvNT?(J7&Vq ztNW$OeI>!^=4#naPl*}nmz+U}tkQ9@vxYU&J4*vHYsq(N){5yISk&=~m(wwLqU42; zwlI-%7)f0E>BcM-G@B2RJ+pMYs>74ddg;`Z3HiKeS90;s?7LzUK94!W%B88bD4m~Pw*Tg*5Gul13H{^F*duz+9WidaCCw&qhMhVlV=Zl|L*&F-t%1N{o}mn%ymug@BZ$!cC61{ zdqoCKvX|#Y4=N|yz>(4Ja)xJ5l%{W%#h*9 zWhOA(rj|shE0QAIF-$soLhIaBxN3ynx#ms-{i12@{hYJ+UxhJ3H|vSh1S)4Y0z#8F z$mq!5@xb~FrdMkp0~__{&IMgzHX1jJ?pZdw2^_20@qlHk13Hf$cYP7B8I`?R?OD|} zJORUEw;)g@=@=`!hivjWy#-iYa>x;(jCz!ol-aQ$Uc8cC_2vkhC3-JmKVNT(x4-wx z&|_*dr6b~O1r>1Eo#wJ`QS7(nXc#*l>y5>CbKkma-Cu5@Vgb^_mfKEMH?teyA!WS{ zGx=pMt^=el8M9ssZ0IDoO+d5-zw+7{rY<1<%xqxwdUA4d<$*d3m0w<_4ZQ@k1>a&%`wXDC$ZTgn ztIPoE4?)77<+G!DrM<^f>YDND>FF|IZHqo$BLP23zS;}J@g=i0S6U3Fp&W30>q-GM zwTR9&_Tw%6n=GO4w@Xg#19Xpc}aV$Lq^2HJv zfSgX*sSN_rf}|*~jyAcsRe*RT9Qy&~fiwz|qgb zhU}GlTh2=o;@)n!Zp`R_mYswiWC}UbqPCTRsSVAacBnHA%~kf;W2Q390e3CF7Z)LP ze+^`$_PYmc&r%4!p*G94V0tvTcE5MiF_Mif*1K2nq;rW2Y>aVcTlJx|IDX`dZe~5K z6W7*4)PGpg(J6vcwgvm9cd4DuI)xy@KSQAL8^cr_{#%4uaTnWk zE%oW;yTXVKD5AJ#+OcujPqSMgONK~=_fk)8N~_N zc&DggndKgu{%VW-A{G|nJDh?N2P=~WlitH$_ila}um5U(IZmB5S@6_iR(|M%+^0L7 zC3TYzhsXECeAz|AzE#b}0!Q zK4f>yy8AwxvFPG3etW-Z0DP5g7&(uK^09ewRFSM$30-9-kyEO6c~Cg*U!1$A{2*#g z;Z_n&wm8s7si!KT+TLEmwc6zB`TdsTap5?YSEYHMA)TD)1Kzn-2!`gSr# z-e=j3+SMAc3(52Gm257;F$(XABiBvg{k+%A5>rOqAk0#!zM48Uxz~5YA#HOPxpS!N zUklyTnCF&u*ME1$TZIjwkW-CMW1bcn^zPS?UB zm&%}qf>Va?Aq40ERDCjf&xaqwo*!1@OhWmru2tMw3R4kl?OsIJQz$0q<*k=5-Gd=F zC#%XPN*uvMAcdEO zwl=4Hok`mgJ{J2K7+k-(nPI5yM?s!(Ko|=+fXZ>)+l5 zX)>~!AF=`?F@q1EH6-#fRpCN_LX6_Z(*iof!s!NW;NQl@svdT4&a1#^^Wma@+iGBY z)tyRPTaB_r)h$;jUZcsKnN z2GtlOjA^&JEzz323CKB0^XZ&um@0bhR^Q{He>t-hsVKbh+A}d_jO*St1AE|&r+ed2 zsPQ{$4ly^>nQKs*5U;z{2wnJU*QTnc#i1rYk7SkJik z28h*9Q{w249NoifAKJl1)*hF=ehDH~ion}lmEJbz#;Qm_<#P^!lCl6~5Y~*2j&_`$ zszIn5wTg$$Cv6xdeZv@(DaAGxKbuOVP8M-#f@{uvs#a+#&3%7bFpt^e;fv zZ`L|5UACzS>hCz_T3gtuXgA#OfGOk#hX~LdxN|aY6pPC{amKf9Rmr(Xz&>$qYE!iB zF=XQ##4C|uMH|ix4a;{b)=*vx9E)Y38qdgEBrHKzaigB`>UM)DVwbht>?MpkpQ2BaAiJU%a;SO+ zQ(NOUDpn5+sh=Vi&Tf-dIDj%ANMLxT@4Tg_afB^L9P?-fUA>4pm)Elq8<8J1dN)ez z@1+s;{?#_bssg@em|?BNL_b1o3``D>$FDYkhPx_^WpRiT@tV41Su2VpS z=Qi@;cLeo9H_q&(YNpXt`3yFWTi!ZQN^^Zsxct2;X0O%bE=-VwU(H&Na=059@=Wzv zx^cX4t*I920%Hf^3!IqNe65&pJ1yIZ#3Ul{Ge@63QC8TXU2lW^wsYelP(&0=i*bLt zFI5x?U{B{UqiuYjDI|Uo)ABRVlpO5FgFlSL!`>Bk;&?m9^UQ}!-?K%v{skHz9e$wU zb&%cg7~La-Guap~Hd#!^o;ijXG=8@6ls_LJztBqhKp`J$fxRz#eHlLVVoZ8x z5B_3G*$_~7K0x6rV?DxrFvGJ9+a63RKsS(^h*x_0r3~l#uO6_O@p<| zUWaxi3ib|?&k)*NYCd$LK=}N5yE}#%3>Qu8U-B__^#=);=y|16aEn|w@_nEO=hUZ< zH9EaIAv{Xu*0^3$>4t+^)F!N<&3*e)8Vkix-G0i?Z*Tb4)x?{66G z>e(!N{XG+}UPx>V-xho6HJ;V}DzL)=0gm$*c{G=6cLGfa16qYZ(<`c^%0sDEJJ5{c|pd?;V48r>H(-=TX0eMpeXUu?A3tWa)>Yz^XD9IM|n_TOCfK!LcLa3h~+D>I0BB9T=7^r1I?}h|rxERAulsLJG zI@veVvvCu#o&_~H&taS6T~C{EdJ%O!=l&-wj&pRUvg}VUa2D6{E{^8JE+?>1x8fTw z^5g(>gcphuSm&zwFcHziIrQ0ff=I+RrPjubgxb^2m8XVEisJacAQUbZkBFP+ zpR;-b_GQBZu{-aq`P1G%3n$($r@omrvIJ&|vT&oZ-iC>GQ5ZtB3u@hScaR|F9Xc=!4`wiBZe2^$^i*zdK&rk0>Zci|a%s zgy-CjiqO{ybGEcW-sITvh7%T<2diB?4+|5^x*{5q#8zq*>TKt=GG_L4*sG`CW>Hz0 zrQ`VFxZDnKVQDYYs{y93+j`OqO?n*sV`k$9HwWsOl3&vl*1rYLr!cET#}{ zC+t-NyT*juHO4Zg!vox$W*Ur+563^(CxuFc8t@IY3wJC=N0)_rHwjpez2d*R zkH-Gxr&Cc@~06ek}E&rPcfsH`#38*i5QM9*9TP+Rzs7xO>0e({o*+{YbD4ygAxZOa*(S zbv^|#4}#9;%Y!5auz(o=z=y#=9v&&wap9vDu10J`8wj539_fCn^3@8CR*m;9n{d`o z%&JX!*9K4}KHgM&BaWaSbzSvuJiE2pslEXyc(K(|0FVgFC;bS;?ah?n3eUx84b|RQ z{Z9u&#T5ire zEus)bN(lZc%;KWuWb!=WDDjP#!f3!k{jLXNB^*^*${{ZDm@ty!)psP0Y>L)pKatV|JIrZ zQ)~q?L0#6zUIqtcZ;yKv4XQ_ctZ_=Ro2_~F&FT!3H%33xcK6Pab>J4uQqkLR(l?U@ zU3^@U7ZOwv8-k!MarnvC6PVV(A8Kl$cAYoS{5Q|GJ&Lldd+_r-e2U3iDY*wO+V@0* ziEikXP}E!9^u7W=w>bMNiVFf9S4S#@b=ebvGcM7+k z+Ls%l;FRx<3i?`Sxi&AMzqsNF{S{MUXkYd^7~1W;$3L#FX39w@k)bUtc**7QQQ%u7 zlm8Y#i|SeHB`Na44IV@$+UDv%LT%% zOtg=or~e#wN!r?@6Nx1&{cTUw{QzG3O-8KQF%ZSa{B5}eexiOfR>n~Ew=EO_ga2>1 zAN{{`TJ}HqHvKP^Kkk`^sf~Np|Pp10RlO)H%&2nicK1}T#85@2E-bks`StxlMikFekGNvE0LSHTnUdqh<||lUl=8spSnK%4R|sY36msveYHE-T2L>1J|H?X4 zmd{nA_aexOM^^bsktHt*Tv91ACg-Bek}Taw$IC%e16vNr6?bLZIczAL_3U>JZrOaQ zxmE_K@bqnvT*^P~aC(v5m51Me>RIm=BjoN!aF2~WOaVDKEc5rf5)+Q{z(TTNG5pFI z1@|mhSLyw%9Zs)A#|G){_+oqYcIw+Yp0n{WJJp&1AD+9&_z9M@Pu-B~kCy`Z_*?}W zUodj`UCWd%_>Ldodd?;d%4L9ZY{=lTN;v_%AxS*#zzm@Ik;z>pl=b$IWLe(DjCXL^wLwL%|bY#G4RwXp+gp)CEqmWa#PyO!-O{}8|-;1?D%%m^vJYZ*YGf>6gk>KAbz z6%T#H&wj_}(kAxhlX>o|lIBuJfQ3z4C@_RU+^9Rs%G?8GW*hL`JZNQF)7u8rRM^^h z7%>Tp;3;R#$4l<|O>Q!Mi20j-WS(u4my2h;^h8b-h9?pU-(_|ZEuO7mT|qirkt7x~ zu?-sCo}w5+eLw%DiITTJSJ;FcR=2i}B2tpgh!dQfPM+BTE>%|ox*lb5db^$F_g7-~ zBeMjp(F3jVI+d?<=ah3=gixDvz*g9o!0p_#eUC`vY10U03&p{=;Kxf$gH)h7G>zGm zlEnO=sYRfv<){owNrH{GJaE+7=JdDY^E3Fbr;05NE4;xIOeD(ZKPr&MD!ep@t!-4A zeb`&J6C@YNQyk%uE?BI9cp36s*u=m>EYCQCtQol1jrlBV%N3^@MqE!R01vHssiiLj z53RS?8ndrRwgv(!M7)cw1eutEqZQS>qL!5*`>c*9{K z*6cdc3)U3lpL~ND7Pjkpu0@uX<}9|#;Yu5GsAwA#)r)~)XCbzKDkZhFUt@4h1q7jU z1@X;!i6di6Y9ep>3p(KH&H^t)I%)mZ&8OZZc5|3)TFKFi@n_mP$EKcLZA^TK*FU{d z9US+xkoJ4^Pu= z8`PfwWAY9L?|X6Zc=4dWLGJk#B~EJJ!y0(5j$Ix9i}{O2saO!J1h4O+hm)&o<%U=2 z9keFL-MkPhQH}F*?Bs%;Eeb2iAGi|APydDlp6LpA6|v0Ep|3Dv?6lqDZN1^u5@u!| zy7hgYOifpJy-KNiqCG?YV0;KQ>Cj5Qwr@6)MK1~Ss=1T=)8(cIeGP5T9rTpM3iHk0 z2F6q6tGxUNxm+wA3&#fN+4mO2ee~8ktCxx%v?Io*?A6B)SEi>{jt^P=1oX067)oqR zcQKIf2TkQ?%xm5iI{!5qzu>ZN`!IjK=J1{u4omVyAvU)JMxhfgil6T_NOUEt1FP%5 ziYK;54gb z?%mu`^fqZg=c7yTdH%{Z5b@fG|7yJ%Lh!G&rnHzVr$tUVuN$vkm=NHMzXX4>KQu-T zl2*oyM&Eey(&d~n*u7dJKN)8SsF0WKm%g?NQ0pfoe^nN)~X5`IFpO3XiltTLN zbU){oB#PsGJP#E`z~nvRp7U9^^}ZIW4So^u#W}sqYOzIO4mSryHi(3CfH&$PSi!&| zhuk#AVV2Y6L#OtSd8Q^g{`vR*Lv7n=+XG+RGT7G{dwB>cy5G=@1%Tys%|)%~+#L?# z9+3U6&^mCN^xTi&$4AnP9H@zw2>}ITyN4N;NQHN*3SB92q{C*b1DSc%)x!5&99ak7 zxJJZpzkXu9E$Xx+ujE9H_8#=@=A}pmE9NiMk=hxRme@`;X90lupN-fE2NZUi=n zJ*{05ds~M)agegJ4mdsHZBr>TE%2mh`E{@2Ca{RhjB#D+O|=US0uX!n$gWhRPyrk z#TYv;UU@sCd$qy}ifW*5XY?gitLg!^7<2g5c=5b&(yOlJPJd4P$@#U-f6n)BtkZcg3?;WVwkDS?>W z$_lT=Vz!-RI20p5)O-G;$;{`mc@KIXN?F4MUU!8$9K3UzHd#S8I$0on06&Ma-UxQw zFiT?QNcDCc5dL%AKz0bMe(ojMz|@Ury#S@wH16p^aB&Ukzik1A0{a!2>MUkL2gWWO zzBxmz{pbQPdI5yzF1; zS6g|b<+k;z&TcdB@3b<~PNl$khv7^^%{E{~ zmHM8v7B@IM6kIp+yVVhwPKjPN0(K>3%A{%U=(x>TyxoqbPELi0h~9=1F_2U_zn%LM z*tuFe`@2;{M*&3Q{6`tzvi)CP0N^JkXU$W<#{cB;f583ze}SkcHzN2+@OJzcta>90<^11}DiMhwSee$z zkdPSm-%vH~1mgVzQ~{g?g^KWwXjJ%K!=yM~%I608j70pE69^h|n=&{@X-WDq{`7MK zYEx$PvePhiv*uh2teiXBaWGvZ2XO3qSS#bjbZKKZbnUmH>~}W#iYx=Nw;dF35%6lp zXJ_F92Uo9^R8Rn0dwH0{2zDNVlX-lvvW1PNCVx2~hk!ycut@ulJn^6m0E;C6sR{?@ zD9R;Wya3v-%E3M$%hpAG17Oqx55wGE%9_}}ErdP%ty z_3d}vDsHDfnc$inBPmXRr<6|;k?{C2gaG3Jcm;U;0m$ghE`le2-j;L%bnhZ(F+6U) z`0u!=q&Uf(f95^`MKc4SgL(XA#Ow|cUn~k=H_EE8tHxd0*`SY|w z36PA5@S2wB8o*+r$VR}rQa*D^k^&#v%=&ZKTXEC3C_hAfJR$De5XZskjJviN)BYT) zz>tq8l^NzJt)G8ZUf@fbX8Mw|i^Gu6%07fs&fm1i=xS$~1n@nfnhgdTInK3T69r7*rl z+2dksmLa-$Sw#c911=V}3}Mm5^8+H!Zrh$-`SQMtbccXewFzhy5Gq|8xE01U+S+~o z*>y!;${o+$MG{Y+NnO9QSeA5VeV%%{M@0k#T)<&SK*ui-^Gm1M?o!{_snz!udaFyQ z<-a0lskS5lj_Zn&iALXpSs{j>M=3d?{>+dovnbG8KIAhhAZ!2)ub?zQqa@ls|3v-zZ3JYtSvmR)S zJ4K8fQSvPlGG09Z)pMA=0aT8VT9O7(*=6J0xBradPlKZnFrp8RQ%Oc4p`lZwMB>Q= z|8_FoDRAAb{{mGb08~|!oKxa`D|gr)%WBSHxT@>-OW)*d{ALJ`Oayl$0KLXajc-rD zYKN?)Ff|j0uF2WY|NQ$uL1IZ}(Pc=L7p@hVy0DLA1fP9y9F54-#1b^+4{9o^+dQYr zuK=()3a|lVBlzJ4(vs`W9XXNzh9Fl{{Dwr@Ntk%R^zWCA3*OsWJe{%0!ulI@hTOZd z41Z3vk>r2wPfY-ouIitVaDro3Si1f-`y@rD(@Lx8yJJj*N@#+2^qz8{NM*Wxd%mF)kH0A?) z3rz9G?o3U#7B7XyaEE|z&ELnrHUz&f8v|f;h<5L4&;l3< zejEpiOo_@^5gDkeW?(#LyOV?lc|9mGV7&$ApC=v^>-WBj1Dw~3F?)X=NvLB`X)cI#_J;~m$XZ0mdWXT~?&qe7)* zX#ZRgW_(Wcv7NzmFwfpKnXn)}i%%n?@rtbv8Dfm6`ZZf`HmGHT)i%%Y54~Xnoyh)eatnhe&;=THuk?=CjhRaf%TeX zlm2PL*=>>?d?w9R4y)dUZ{w@bpmoYf2wBa+F0(J4SwG{n{#_OWs>n(JPEli4f0*^A zAK$_l8`37BQG1m5vlUk-9d@pn2KHd0M37YYpkb#KzkmF&4r9&Oc1~f^`z0IYOV?jy z%~RBW11e3R(?bE#dv*RfL8sRS&==uxnUI!91w8k#+F~lL4pz+}4XZ2ujRcZD;RJE+ zT_vqv{N=pj0S7I2b-(xRS^@v%CX>-_DI41nPMmd)L(=M@s~myf3z5Z5i9BiYmVZ$2{TUG`h_$ zR}hq$X4?N_;}`_;NtLKC(8u>O8ocJZKI1K(HYdU8Ouz|azyKdSNCJ(2?llbaaS8E|j}ekr$}jn?D?&mk54o#YP~45r#RIHV$3uMs-mjcFwWuNAM~OoI<>( zme}Zf*4%`E`zl6c8|I&E1(@88-pQDu zDR>^ug4pP4ppG>$0SgeP#dF_0z1Ry@VZ{NA!Xkap2jDK?RTgOHy-7V(GQDuX+x|Yj zLOuz?6r_A%=E+;Ce`%rIE;katvw>xU$Xas68`9tvR;e`H&q3g8AG3hF^+-WHpVdg0 z%8RiAtp~u`OrfszU<1anx7;h}`2+)8|4cw0$$_*d7vW*R;pyUdnOXSySVNxYHHCSfMWKvan}1{mI}0^hML+jP1xpGiBw)*-p7eEy8pWE zPA%>u?~WPWWZ4E%dr3h7C7)lC7+nP(KqM21l*0J=1&P_fck5s%)U;aZVrDuDpR{)!3eC~5my@~Qol#Qd!oq`EQ+3F>@1$X0$Dc0XReyCD}ihsHxFd~s%T*RzIOioYdYt~VSPr`G*${}tI$wnwa?q4C@R zoxsZCm>84jLom+OxTk8U z^)#%6IX(fx;muzn(1STtot87)y-wSR+1rzdYQ3mzb6G!xnx|4)dfL}XYfvL48NL^7 zwtVP~?lx7}+udCN9-m6b{A=ZcCT$VbTiIjb3Is>M&R+Y;t%E@dYS9KRmRPUzACi~5 z4URCUmP9v)7m!ESh zvw~J@B@hf*%lT=F&f1x}p}tB*lP9F&!G94IE7!f^J3jq@%J(B{5MW@p?Vw#(!?+C? zkAgJ{4FE_%gI~$+>s+h78No$Z_8q=%!<1`C6_SC}Y(WZ0pP$ILavH0MCEpp*fsita z>jn=6F8Cr{2*Pl^_N!rs7!h5yufWcO9(q`%+PXfPr*`|ai`+?=!6W?ol!v#9DGHBA z5V0hVkZWFOWa4F^>fgOGDak<$z4654g~KV6W0L4*hoQU&jWQcSOssP~%DOMk>dHJ( z9dh7Pdt{21Grb+cbV*x|qOjENa4XzVj;1t;ZJF&*R=>A3oqU2%Xt}wY`i_AszvDwT$ zoHEn>KcNCZCEvfO=l#d-R#KSdKSsDe&i$*Y1UUUAjWr$EMDoUiFe`iU9nd_cwKyLxZdLKr)!Zs}T7H0ra?7p9d7Dt8 zDpE%v9O*DP&(H|#4xxQ^V)^@>E#=#o)hVYt?wL=;Q*%u$ImU6)7DEWzEiD5R8!qNo zWW5*p9oJ;jM^n6Sdev5sa9lwt6*r~hrzH$nRvHd_%|?Mi!vg_-@+7;N5DLZZ-4NRN z?IZGl)bncVVV&D(EI0W%BEPOO%a~koZDkqX-HM1t2A&DjxeEIpqT!4q;^R(MMiJCS z8{ra=3C$=wfI{&HgfrNyF0X_OqZ5Q#~ z^%Jbp2DvDqk1Yxc5*^4o3fh^j>dBUtRy-P|8jg)F(qbKPIGwtHSnqKWr|PUV7Agu7 zTr(u|df1GdqyDQAYfm zuZgx;N2$^!L`C(|@^U`U&Ci|3=Y;qfXnV=9kENy6=^mhigv#-S*_0GFL>Dn@opPfE z6o`lVanNVOqDJv9b-2>+8bnd+$oc+*i(Au zPNzuEp?7XnCf1XbhVPj|q5#*d1Bj2UDTZDzyPXXaZ}+x-_ZB&@J{{qwl^IMfd0(My`Hulro2l|CF5V!qyk4y3gW63&h_@U21xLr2%H z|aEy%9@gFJ;*=sOuoYs z?Ef>r>vS|^WzUH8vFX&=(bn8*4$JMCpK{VBcpP>Y*u(u23j=Yzb3sawkRTFfOypaH z@h}#6<9+J5tUp|(4U(nrjO)v?UBY?$lDps-*FFRVmEaBbC7$J=a)dSVr4gI{(v2Fw zp0-|&!EuC#VX1RH=eEPg%QE+)HH;WEHj$(GXs?5EDpka<3*Ga{P(A$|rE;8%HuGA* zaWI>JOwDMSC9~010Ii;XzQVSyRQD7D8}5R1%7A6+;GbCbMB!xWlQq!cT`gM>430XF z%uLKGuUzw#T|&Mp`amk|yLa;kGv#YPhyxoH|Ju$ai*_mRxcg#R5Iq~z*)TTAVp=h= zpDi&whR$6iX8a{a#t|MJ_7-vFmRiMyO99E3W?V?3UG~7(vFw|9Dx)jDF}^W$;bf69 zZ|WB`q!-7Kckub-S((co&fgGZ70z9ld1oTqeiA07&(T^>44Zy&C=FM(W<%gXK%qDsvL8F81%ttF0#R^H00CpRclk$$`&lolzg{m66Los)xXGxHUqz zF>tKZv)X`OfmrrKn9AkdF*1!?kdi}q372^wIbLcw(Y)Xu+pyO+)?>1R%|dDEYPE$n zO^VeuNCM96vK8JXa%k&k*n&tEmQ*go@|-Yik*H0Yz@*LZ2<}$ML6ys-^VjD-ykGIc zp-%+&CCb2EoJ$gibq5J!N~WbGClGRtp8c2C!MRiVZm%m4r0uOUgMUj z2+eJSM{h=qhcoei3w^(v3{}NZD-4Y>8|E3{qe|X;ikId0*R+=rTQypp4(A9J=|SM# zyB%kqkIAu!_pr&dHg4Xoi_ zU2y*`!zEd(i~2_N3f@!~xg0dgxzjh(#)ominEL)`2yG;uC` zz+5X9Y|B3H`X3Spr0;TmchTNRzLmY4_WK62L*kp&Bk`BTd9x23+{*sB!4K^!JzZTo ze*e;<^g+ul_hw!@5$A%?qFMVqA?(e(lWX|0;nKr}!ZM^YdwY9g!$T(F8l$L({M>rW z)MVIV-W54B{am-ThwY!%ch1*Wg)oiHw$$9N7(6R9ERoE)Sb>GJ z5;TpT6WUsI(lnLk)$OS?tK{>uR*u5n(C;tJnzjx*wR#gQTV1q(Q?`;6CcayAS1l)F*Fq#O%#~H9iq8LTDG{aUiQQJqo>#Y(*Ol3(!OIIsg=WiZkI(wT%^P^btjDRod)Jbk{ zk|y&q3k;ybYxl{AEWTK7!Q(0L)GgYHCVR);a+jo}bK^ALa5%zLoxIkoM{eG8w;h@> z*SbIP*=Z9UYdp^hM!V1r^Xi|4+hUVK($WHoN9pMP^Y(pr5bRv1Inom^5DLdp=T$*GzQ|I z(xb2%rM(@I(ZzMIy~MNiy$U%HR|?5=Mj3J8^G8wM9aei6Iev@sLs5ZVM7DIT3W2wI z_$TALKQSNHI#CCrAWM~R-WbOe4&{lAlkiD{T7L5R92C@*gf&JYdu@~1w~7k(RloDC z$f@W#SjOBvsxL*A+V53%h9HB7@V8Z<9Wom|u@z4aZ*agde$~#y)VzFncZ!`JOe6UJ zhX+h7MFVjq7q3CJJd*RXT~Ol7^nUaQfLEv$GjPPcup;L?av~dPHd#9V|QhV*I zeJRz|qtA)6HO&`hUGn$9f7Nq6Z+7iR4z)u$wY<-r>*(H|G78uZ9;xy?=e^yrJ&7pR zI6PdxEjT&T1zBj{-RQqQq3Rh-vt+Mg@*CmVh8i+mcbN&mLZ%r5XF0w1*lNSO| zpS#x5v*f4lL}3`4MO$WvaAJX?Vb=Zz|*u57Gmiu)eXsPX29U)u{|Q!~Z@ zLMMja<2V?xqi4Y4=7Vyc?}ko)%J)Qm!eP;^VAX3{G%s*y71`44O*i(*QcATYG&F4A z0BSFkU~*{CijwzgwUfsy1?)H664?|CKCt|59SYkJY(22_iAeDlPo;D??0!^eth3x` zSUq&};Z+RkP5u=cr8KBgfXUgu>cGi;xDFOd^p_)M#jAZ}*khq)7E(y=H}Vc*(2 zdSzKHV1z!3J$@3kisgwPmPsC~{HlqL7Rj{etadbl1>U~+eO+$Y^|C@~_&}QYekyJd zzc84Ie?w?&t%O_AP`-weku(ty7bCsr3MKo&c?N#P&V;0LF7Xc@bT zBeIZBaISjRUnk*357Y)xER0=;`rnh5d32k88sul=;)YmY}K zlAPcTAKj%?)m2)bwth>%aVUwpRMwsAPM>eh7ul>1dF>C-%_Plx?!CciZscu1Koy1b zAJ79Z)rvBZAvD4x9H4pZyMN6FPm8O4;@VYb0L`;ct`&AfxBq4l^;3k=gGFb`wYS|XSjFJ2e zXe>+}H)VO%u4zET)UI_dOh!@G7ioO2NDkAeTwOiVqCIb(+cqZ^%kHjMY;Pq(U%4vF zcy?QskR;f}IJJHN4TuntX8Eij;8eoBJ&r25Fdx+=VEV-HKwCe7UnNUE$q4;Eq{6Y@b!y`N)bB`~OgAVPq5(I&ao& zq=;_=k#0=WA<-hS(O$snESxZrtIDZc#YawU9{zt9>OB;j zzA%Nq7n>a-y&SuVeB@bHlQ7!4~`&W?r{P;mu zN$G{$>=d~b--MM76?U!p18twII_FRt=Xu(vwu;@U230~?Cts@lKmKg&29gud(Yuaw z+p2n5Cs-40n81&B9^vy@s~a!;=zKo@T|gpK?>AXxme@_$Q?tDRn!Ock{XFdkbY&zr zv(q;q$2s-Z_I$#jstBRFOQ}_PEu!8_cZNL`d06$NmI!}OA>Qqq6#9notNM6%@TKoU z=^gQUQ1@LNgC&AK`>iRD&0thJmVDZ?HCH@yP(bXNGLfAd`94Ubon!v9ociqhtvB#8 z;2VAco)}uKfm?*sv>-Zvo0Bsi=M6laD>sYZj9#p* zi~aS$uBKKy^~dFs^4g_T`Is1VsI-)v-jf7C*bA^2jbq6#MPhLY5MBh(Mg#{ARe#{K zs~Pwz{)Ty%1uw(D_N@&c8EO8kF#j1Kg?{bIbrxzgY&b`%q#^k~KPMy3WTb;}D7R=Q zLPy%p2nPSRMr;S%_1VaM(&UGCbzdRj?Gck}kk;ObK0M#7Cy`x37R|$N4dr%leH3O{ zMQE#b?{N{%7mPcQvX^VIMchZMu3Ii+TdXgDTL1d)?vHKk;Uh0EmkrN1J19gkEuysS z{Os2{;8aMXB!&bj27voBaMLQp`?rMATYaPxQU>N zbmh9TC%?(1*JjhM9#!IC2a2{!5U0A%^1fQ})w7M^2%)%S06i8_&H_AYZcrkkZQtlwTNhRR!K#BM>gPkJZN_QWs^5#&g4t-6eJU-t< z8&6D1mqR1IQn<{K3lh^2`t&PnU>-z(=eE?=t2 zoH?VS5n4WJM^I&;Q>`L@@q#Ng8Bt33!oG*vBsPWH2j~#72MzW{gv$zkUp8;fTwc-W z6^Uo0IFeF)Yi-EC`nE$PFB`wA+@{2m)tXlENXe4#)>))`c%Uh`JxK)v|%RJ`Fv`Jn+BoU19@8TeX zpZbUgrC6oPVBi*r1+GMa4aZ*&bt8DdAdOc(1vqL@DGQ4*z*B>e$OJ6(v9Smg z`WV5jm%!klOTR>|@Ct7XxcmmWBL}!Y*0WLA2fROeWau>BA3wP=_NSiO0SG5wrIQzD zxoyH<;eUQ~UYCeiSu^XLSIN2oC8f_GEc(ds9VM@&`a+dk2^R>1mWM^z6=WK&v4~}D z9pAT_Yn6@BT;AxoGog7PNm#-!Oop-BCsOew*syw2Q;d%w)>yei%6YtR5wc|OR0OGW z&LU8-8!p(5ONiJ$ca2lAAZCUGT+~^&u*r=gQhM1&<2Ytl8)$B$KX6ewmP?HGWr}}A z*#)fjKQ>38N220p=BxQkWSYSYc#DYoh}J52i-sQz4O3c?XlZW>PNY<$g64%E%8#;v zL0Evm4MTdItn;8!89@S_Fo(V9}`4Gm+OWj7E1UC#Y0{+OMUZRD~i7?7+DCe0MB zF7|OnvL@56TF4z~e1q6?Bhs%SK;4Y7>lL2+PDVD!AKV4w&KDOxVDw`%g>1HF%Ri{S zDsT1)B!;g*`8-VqTWw{;_q#G-R`jihwG79v`AT|V(L~c*hqSJTlH|Hrh9m3`7_Pej zrE*rVB`-=Ge>^m@b|~rF0Ke7U@o9!?JIf-kvv?z=G=ZSLZUNJ8j*Kz+0#_7QzX!rK z#a&B@tiS=dx~ZmN$JfDB98Rck3+~zbaPu3FE8wHOk8@EJe?32^QZV9RXPSGM5{ynO z^^`Q^ygz`iAh7>4SY@Wel=w6(Q#)<1GT#QKth5hVyRk7}?NbYVUI98|bm+l^S2{kM z=i@(Bs3;lasNoeFa^Ffgix-Wq>V`l*LMH5YLpfZ|-!PtSzAqAhGqc6Rdhm6V+t{te z8J_4cx-IbP*mT$sTe%0>YTKE-hNvZNfN%9YOFQb}i>_FdFBj|eI@~OrP*rUkWf^`{ zW|5HhXfSXWXSJ2tO+S?8PEUTcE358w3^k2FImu)qFs&X=NL^@o#IY#72tT@Uk^ zizp*yi(SP=WRp$E%#|zt@I8I0=lE-(ZF`JFswLbePeqt7swkEJbFml0XW)B`UMh=G z$u=kkYuP;K%tnza^guHFT78IWVIU9aHYe$YT`Qp6c%_&PJdus`u?!t>c@>a{JNvFT z`E(8JJn$Rk@95~D(~(Uoa?VkDg)gk@AVh2xxkXkR>JmSSV(hI=eppFH*k1}0=6QA) z8S*PWH(knS1+un65T3$A9D8v_cU1PZNFhL^=fDEV9G&luh|ef843gS*O_3!Nwj5AA z0;+*UJ0iB|^iJUkZ$dFJ8gcVHFk#(^><+sn3&r}R>TX!vDl_X97%?^LT-9-XcIV9N zsX~B+)TyXGYqE|Ao+ovoJz+D4qqQD-Yr(-KF12U{ z@;V(KABr;*0MgxgRU#rc$$h@xT~ypO;Uz$h)0hX%yjA!&7mIZm5RcOy(LKfEYkVcPH3KznAV_oCWA!X^^o>Ej=qc(O%9&=pDR-cyr7baHCt&#D8!q!t z&s(b}A){O!g_n0LzrIri;=W=^2i9E$`%2)_(gB=Lp<+`w5{1rd+^tA-Z9jK0ub$fO zFN=gM6vjF-N=KdEX}Hj-V!bZtSq%q|3QI{ZELza8f^~XBogf5^ZPa6u>P_EoY}h6l z+4!W_0P7K2gOD~MBgk{6lU5K($8>C~Nr$WT#ec+wlzXUU94FR*XYl*2n2Xow#It(l zlHKS9iA`lhA7Ox52r1_!rI8QL2ycTLAC~t=SX{;^385meV_*;|8a-AK^Glf2^-1io zzjDxr?=oEs&CEQB@Y3?}|Dx?ZfST;OwoxpIAVow(K|m2isS47ofFhuvNUtg&0zzn^ z24bNjAR-`Lq<86^AVqp_QbP|dgib<8a&~;4_x-+q=A4-`XXa!kf$Y0v_j{FVt?SAS z_o-)}pY|R5BDPo3m_QzcG;V%=4RMH&yKPl?sQb+A$q&D&-=|h3oG67R59SNR0S4AA z-}67&|D&1l${wwp-rs6w(6F+~03;CA;frBlZvk6!C(Uemk8pxm*R$}0&D`VHkk0V>xAhdV=p^@L9k}mOwA@vxOyEU}F=$&~ zr7+``I3aYi5^I(mAY^EwQ`EJ1#~%c2bEd;b!~l_;%Q(jsjJTzDO4zY%;9 zbFEv`^ZVO>F;gJ5ubhN_<9Z$8@%K;0Cxn$6Y5%#J!okIk@)*SUm-o3zK!hX!V96ec zC|U?={(n!n=J|L?N8fB*d9x}_5#Mj}ztYK8T@$SM{vYeTqQrj;$NvvHBL&6m|Gl@{ z<`#&(&+wv+3;!=pa(;0aS4LY+jokkqr?^wU*kW&2*Yhz`chli@ZX3xfVgH_=&XZ#E z+Oq0v&h8l{Cs9lCNz=9N?(17lrA>$W=03VHLZ-GVwGJh}qjUb%Iu$3j<1)Q?TC7$% zG}~klkF)7Sv_zT?J$IJ@XveALvrOD8Y7ms-wZHb@ET-hsxmhHqW0)7bQEV;s$`DB3 zsVtg#d%Z*_qR0N1OXZE}H71jf*AJ1gdb^=5@u<&uOsU+MYo3-P-5{FCP8~twOKS_$ zA3J3GuA)+ikx&fdkdU`uzN9u=hgNSpy14o0d*Ni6VsMa2rOkW3sp9Jez#0gS?Rfdj z*JHd0@|Xi`Juo(4dKR*p+%?S9YNb-u4HZ96O<{`YQ(&1nAq+lAg)d!Z6JI5nzpzi_ zVByjjAq+@EY7S&U*Fw<~BjpR`?$gj@1|*0Nv?XU`@!JyW%BCO^PK;%Hxzk2r4sIJ( zzOD}n$~P`GYnaSRpJ8w2PEyKzk-F@lDm}uhLpM<-ciVoMMQwI!vP`by06mx^pKAMA z^APjM<5>TSoI{;-txFbbY|?b~bE7jf@0G|3S+3SxI9;xbTPSNRwAbOf8GRi3tQe4D zO;SR3dhR0KRU_Dn_&OzO#nRJ*$Rd1>{2tPC^=XRZ**RrAHY`xqm?gy~cRhpevCsiN zY}WDa9X~Do(c_I?(Vq`ji6{LFwRPRsoxX&))b`LIQ6Nm6KPbEnWHX=>4#Zqi@;J15 zOhF>Pvm18De-$91xW|2Q%sbr*9CuVyl5uUPejjudeO@9ZawE~tZ5Tx;&q=t;3}(pB z**a<=?mtwU9FkQ)JUWRo8$7sIorSzMNzjTt^2@tlGljS@w{32ku>k`!=xWQ#1URNu^^{(>dEk^1q zibmJdf;{9cX)f7sx_Q*g57@r&|HL(uXwLoKB?1^H{}#>exAd4<#QfM}D<3pLH3-M#`S<->e;p{~3NHB$XJppf$rAF(hCBw)w zAaKR%19KWM^!?(ZY+oN6Cv_gjpck0q!2g1k<c#doz7Z&cETksimlqZR@Acc-bmD#o!G6y<15`Ovi+n;T`t;{irQZez>Z4Bt zTq>J^2|wdZs?x)06pPx{)B>Hkpjp0_fY{bNLFNJnDbNSBxzpV@Ht2d8vk1NyGY*Zba5s^I)K0*KCtjR z&s$;bR>N*>drU|HwSt-U?HJEV=3M#88ME*Eglj+Phg2ELM@Xw_ftk9bG_anY?hn+H z-f!g3G+^VsY5DUHY}>E4cSxNc{k>{YTacao2r7xbS3Cm~yE9Bn%hn|hi2>2OecfH* z;E!&FXK4X5I<;bjy3VYoK{1l{JwA7G+(OP{-?OChyV`-&v%lNj2fBe=yZ*6)%R0CQ6Gk(BIb$aWwe^>E?DFZeP4N$lQ4(cN1 zDVsG71i<1k@j2O-#%2c_9edKd@iLDDKA-vExf#Y8fB6IhBS;g->PVz2X`0_(AMHAV zt}o5oZBpZR??}Em$ZeETjfV*2Pk5|82qAYB$bZ8GQp4s;nEN~kpZbvb2P1J)Gx5#W zuV2%joaK~sk8DnLC>t4`(|b|7ebRqgQJVeqsDcTn(#T6~y_h{f?-6&Ph)56IV%L;; za?{F6?b)-)o%w|iThrB2OiePvbIe;-`XlbIu7(?f{!27A6yhqT{QUlUntrQ{yUfXT z7YD0qfrQHb9Az248P?MQpUjvg-LsnXHD37C4U!CO8y6Hrie!o8%`-;W_@=e{=Edh{ z?ftvWrcvU+)52_!veXr0gI9V0`qy@;Z)s4f_Q7NFoNdDgWaM1J)S+qe`#Mv)%6(E_ zX|(E9?;o6m8g}u#Dmdc{*4|=CjD~Ig*$TY_vr%WY{ZZDUZjXVg{G~5CTGxfh8c_Pt zl$ZDWK`8%)JbD*i*XOBgj?MDyhPtJ+tl1A&$Av&B%cu1V5cO~onvZOmTuo=~z z5se3%484@15KQLOS)`yI9ZEBa%e-Dk$JCr%PSVRYjKV0L&)_Y`McA1$sd{HgvnvA= zyo8F&h8(#p3ae4f|To+&j_&~K|(8nWMSd|5)5%N+3z={Awj4qB}*wJhOgF zNge*}fL+{#G61rD`l<3JLQCq32~Q}KOQl6S;V?&ZQ~=Oj6qPtzE-$K040i^z%HFex z-xVmOq2NRWsomA*xG$;yiQt5LwSalWW3c0#A7hIM z2{J)K-qF-egH8UzjH<*HvM|6Sg7N5eF$$y~#AP2o2n5Qt`Dh=nM6&a16NoIIHnS|{ zOfNqH;V^VEy1i~eN-OY6>%<=NmH)^05%2Fd*W~PYS*w%RLj;Stfe$W_6aHhy8za{- zUa#`-$*48`b`Y@j_~&VD^*u^b$!*rsSZ?p09~p@~+)w%upE!O4jpvm8cX@iQ?M!~$F|z*fChYiW-VA=q@ncZX6l}LgESwx+Lmwmf#i&Pyb6YNp8Nk7Ld!K>A z*Y(HFe=u!FP0c}!H@*hx9FFF8{A{wmny30g3oM)tn^NmB$pdg~m>gxyrF!s)D(dDF zZ4ih)zt?$4R#<_}&{hJeI2z9F8*AR*2_(OI-~a>UAqDHHwSK3tQO2XO=hQU;F{w+h zylNCtZKje$wt$?nP8oQ4Qp!eg3aS*FL;OI%`mt0c>AnDVysyX0)k?&&ZWgI}2a z*T#l=&kloF&gXAcD<7EUjMS9|1sS}rC~*5YPd{E3qG~LY0z{;T`_UIxj9e zgn5qdEEU#8=0cBkPS5{3JdtHq_UNlk{91aH<`*(oWdH+b@|MRJX4~c~V#?j;7SwXxB3njRxk+pZ0ZBPf7)b0|gm$2`}nu%}84Z zbC(%S%9na=V9*o^w+oWK!-Wg(Sngq~*p!9!R1Asx35w)`Fy zuI)4YT+S`(TYu|oz-g1=F?%QRrF)(O`%01TqX+LP1JUo0(_1sM zi|6i;D|?Pb=qm_8s2;@o9lle*YGl++9my~<8Y1N8wz=~$Kj+lb*Md`{P7L=78fPFo z_>-g)`KRK&CDU%gZM&16?pr6<{59wSykQOj(SW1?JXnUm=89XpY6#>9a{j_vZ?NZN zh6Prgw2ByND?1p})HAJD4?OB84G*NsJD}AHcg;{WkH%P|?PoS{a&yyPAKB0QFpUN? z@v^?>Y^F)MtK`2<{sWTbJ%KUl+VnK4!+R5hD;;lCT+#ld1;DKwr&G+SLKy{K z{pe}eA_2qs8T<*Jx|0nv#ax0B1;S7*+ndn|qZ$eN&*9ckuYGF~y(noOW{Z)6f4r!B z^45GSmnGM@NA{iY$2rL12G*v|p{s-I~6uZA1CB5NNz^3Fuk!i!1 zw1f2cuxANa5UAhYw_wJ7pycX7PVw|0^J$R(D2R346J}{ZLCO)Z)OG`PAj0d{=I+7w|W3$LbCg6~YW#;5Z zjW_3586#CWlfFL;W90#<)V?Hm=Z62Qn7{joIX~ZVRJX zumsS-q5zUmQv^){Hu#%MDE%f#(^0RCORfT9r;{U?;~(c7MSOy!(~tXDdPSNo_?j>uK7l zfu}6;)3O3tFcc{`FV|27Z2zlXQ*b|z-5K=Nh!qk8#y5SRxN?+t2{79JuX_r32p^KG zeG+L21)hS3+3VaEZ>cdSgtf~Vj%6_2FG>Gq9H5|}2NLdovFKtvb^qf=mb~!)#f^Nu zdk3eJ+jjn(@F$7k%k_60K&`L+!@Q_?HuM2GePr_4z$0JkaK?wMR%t&-!U^cgt>a9FYi! zGz51#|DHw3BWc{{{WSnA$78N&LzdP?u83NL-+0`7=^i*k@ibFNXxKEkGuPX+3wZ{5 zsKeVr;+pvR(63`vx}nJ1=3izmA57%=>|@xBxzSoOayK^h-$S3gP6EH8{SP-^(1-K# zZrg9e(v;64wgXA5lV`6@G55 z>x(J5COR+Av4Wa3-mFL~<{j`^h5iX&UK*3vZR125IMi&@NUbi4Yj5mu;Dr&}IbLr! z&?PTABt}p+Ua(y+RlxAPiBKTA=Om!qLi1HnK1)eukSwyA?=stloci-B28Z69vaJy2;dt3cf{ zZ9aUuTW(wGi=vZ}rJrf*|v}MXI1r6Axf#u*mV>?}HdDu~lXOh+|!*~YnN5*@^h(DVW|4f1#2Yw1)qULIOfWZKo)YmOD;IWzEzLXO2 zge5Zfr=d!^$H41-lAZ%=DHy%*u){w+3BwZd$FHV+5hM#9uAP;tTDM`my-8&j?^JbL zIU??lt0(zppuGzdP>mL7m$M(<@Q+}`D z>O}4PS*%j*jb3VezfEREbsH9W@ntWc+&W;>9R4L8uIV!LtYJ~(eVopCAoqTl}RMO`hk!^)!rT51YXj zCn)L1DQ5fCmOFDosFzYZhdv4Id;?Z#aJ>%7A+h#$jE80U3LWckEL>jP6x4q&SK>T+ zs@tHVMj&;8W9l$;;J{s~+-!u&`L)J|&on?Csk`rMf}(j#PpN?6jVl?RL1iD?q9B6SjIyZF0E9OFLu6M5Be0*i1Pq+AH_+pEXgr4}8B~ z$!Ax7L^tLp>Sg9sc%z9Dyn6U{ds@84lap4@V?Il6UJm=+!x`{dZs2J&COL? zkf8uY-?|ocTQlI?f3hZFKqUrK_jb6VTpiA6uiPB<~_S;JR?bep$ z&MNJ{RXjRrlSA+Sd=)Jf`o&Gibgd3Ht!d@oOp1ra4BL3AHd#nlVOSP6Tx9g&)9#I1 zoOt@ng+$oKGX#&5HgYFx;lYbiJd9FV6-r7zHX-n0NfJf$1NMmns zgt3Z!K~Gld!|x4lcxv9_*Dw6Kv*WDaH>cw>y|t9xTC!OkU1HI9!g$Qxit?C})0h8n z)8+>dhH7@wyJ?^(AQge|BQh32T#KlpV&;t&1E7p3z|PGhTS(^U27wzkVu&|F^?QSB z#9G`dYQoqMAf)9ZQ&`ET6#cGBsu4D5BL}hU*vybrmSq)q-5*NtBW}Sz=1wWVzYRkT(A>6 zU;@?RuqyK<O@@W7!Svdqtmbx7MoOZQ?IX>XIJOD*l^t{;4TJ~X3J8WxJjmb3vRH1nW~ZaN zQ)nZIA&Bh@xA^bcC9#EB@gpMryWo1(P}%VTi&|Z4-Y2HA0Ewluy4bJYpL?tF`Jn!# zHbKhkE5kbJ4KEFRoNamT5F#`yiN?l1BnL!~IFmF9ERi=(u6UeUEikTBPkR`as{kHc zYb{$Qm4FsrKLVrUle7n-VlLAaP6_KPcti%JJ5tsmF%m*{(SlrD<}-3C(ylABox&;r z7ySHiak*VJV@U+0(3qBdFjZK8U}RWg87yE}vKWL@rCI!d7)w>AvSM%JC;NHkQhmFp z4X)$Hp-0l5u)K$0YZ=L?mfG2EQEYUdBns`Kq9ji8c6&v+zz+QlcckA(a?~txD@L^+R55ypoAzYp8f$EK)#kY>(sGn7hmaH{wxR+5&CiZexOK=lAQn z!Ve{NQ7TWsgZ7eeA+XyIk1A$D}s z{(KjhMqFH?(5dBH0~`-)K!CwIRAw`IIOEwb7%t$DS} zhRSy`OGsAGSR5^{-Y}^>XTfXC%7nn@bSJpQLA4zxIdjcgyO=sx3 zoinWTiPh0N7N9!d;6IGRF}5wM1I^=B4Pz4?1|U)mr>VY4uL7#eweqUR^aNeS;cGoK z-^p+tI}JDep+E*`w{FP+_tGq=P5{w1F4c!gZf(MrR=fpBOm2rGXU$ z=q3OSLNlpA+W2`?v>OWH3RL!p%)LhZaIQvedl26oJ|26>un$70L(1zz^gk?HPE-u& znB|0)TYGa&ewTEjty~copZ?8sYh=zb^E(hX8`oXW#WTh;H)4vA1}Z8Q1V=^r+lf3N z7;<@y#v`i);-*c9ikgQCCXNMp zkI{&IY*|IBlLvDh?-A$aCJ6^-0?VzUf*nnN2+@Mi_SChDK#2uTOTl4t{vZ|j0Qc<^ zu*_1X{H*8L@EhaQdPBEi%-g*{asM1lsPwpf#~oB`5||dvjF$eYoQjzM)>L3ptqmNy zRzQ2ve){zMKCX5F(iC!oohJFe8ZE7z+a4q2v2_0UfRD9_&-FR)_;{mY_%fKiJoT4i zJHUiv#{Nzu3p_yU)dfYm5ScHX#nS%xt(RRff}#yaYy47&Q!cHjUG@QZN?)woocD{Z z&0(MG$r5Ex$_<(hJqrIp-+swbjo=tb>5CI>8?^^O+7_BQ;2z^8>%(<*b!!TS?f;`4 z2cK`q$`D%0AY_82;%S)oHmF}w9@gdkF(lA~zNC1Ud{X(a@s9k3lUVLGZLihDqxohL2~-A8J_fLv?7mC3+vC{E^Hp>^2p3S;8oXl@5xA- zD#Pttw|e*wGye~sVHj#~R$mv=%mdN`eCX5AEp{=7er&~HHXW}*4O zFmO#d-*)kNs(jz+IB|1;uWhJ=t$}y$3*8<({8w&n7si{vo|9#6nRDb-Bs-dcv)Mlt z-16E-a>V-Q6MAAhIRv|zBBB>5{!sMhSNi1I>B*{lSFhd9pY>4nW;DFI*F$^u+l^ji z_&Zi|Rf5Uwz1HKiOVC zOYgx!(vV!IsGH6ktt=XOmwK$w6Xh(%NaI>JbZddpy;ueXtRky~4HvL-CnuP5gFoc6 zcjl-#uikZ+5t*Z;nJ$|&dkfyoUwv`QlDGNzv^!{JChdjA^9w>6U@nQM4|>3D{_7(q z7T|AlK++a<{Bx}W`bcF{h3S*a1}MlFIZ+9u0rwC{MP+XWltKu;ZOE@;rYVaucZ-(f zymLvI#``v|u5!Eq=}VrgaF0D??Fs^fy2~D6KpbpY(23(y#GFnt*qwMIx%&CH%p8s_ zttXux1xGkAg*D!HX7g+yCi_C>5 z*EYsQ-Sh-;Yk5T#V4?N=Y%tf;6JG%0*n~JJAJ+pwB}@Ze$X*ExTyy)=?nwWnT6VJY zzc-&np%+f_S#@cq6G6ZCRm&sT^suo)C(MLGQQcpj9nJ`YzU$<#7Y5jYOM*s0NK7ob z%{R4@-}2wmeAt*syWNYV&wE&eIEa59m|sz6trfk&8{09svf@=zXWi$=sSw5{_O%|< zt9C>Xxq`|d4Gh2`KfotxWd$KIKaA#I1sNZ+F_Ycyuo86ODK$Cs5`e9~ zgJY1BDrx!-Fz@#hcr}2T6n-JOjxvQelER_MkJ`33QECwE7}Ur5DwgUU9?(6faV{?B z?W$5Mlnm5zw(&jRxL6h`vQL?`Kif)7$jtfRjRfa24rGyvy&U)6=r&k|0>!}7TSF^0 zZN1NDqp_E>LJ+%z3SlT%KQ77m;fNv`T9I;K@y1Gt&0LEOu74 z>~>>?^;G~_ICwOopMk}pte$zsY{*^wAY)2;IsqmkFlYEY0CO94oVxD0bG+N9kD!S9 zw97lcKV}xi>3e*4dCLN&-SOp~ zK|lm6+k9uOf|UCH_^=l0JOV!)hU2pqvJA$&$$w1tk#hSa{Is+UN%_=W+B65?zuD;& z_dXm`taYhe=s$Py+!_Ro`^>I0IVIA9GTiU?+4G4h5!WF6=gY5SI z0LsC{S;YCF>Ia4x^2cAYK#-WoU0*=#7CO2znap&jv*Glf|pp|f}D($iI>6W!uzk!5PVwQHCW zmx|I0^1x9J^1pv+#J>h~Uf>4nM%T=?hbktO!{(L~AmShF(lxGORInRM=V#Dc(;|RD zxH*S;Yc#6nBNe@RrMWLPj(gYd0bKuArPRv-r0zkR#O?Lnr)>ncSEO%VSab_9T&`n^ zgcHo26h^fFIz^S&#?#t0|>Bic?08{2=iwPScfxQ?@`Q*SMH&W$!{r_{g0g; z*WM9TACgwd`8f6v%l2UX;yIz`Snd1|M;Oe$u2g^dF*i_dmB20R$B^GAzQE#nU7^CB zI)6*}(s~8vOJ_)m)wGLl>RVh*5*K@J!&rrK;J0w*C$r)V#f3%{ehDe%t;+XYmb)XL zr9B+4+58>C337Q%$!JEglPV+{K72U2Fy+!mS4-I%V{bTE=9EF!I6?Xk_&WR|VI`c4|iZ~>tI1Rm2P1?%3cK&}h&!W(eh*xo||mDS?+C+b?t$OM9WD6{sAOP`$g zgY=bt)kmuJ_?8${o;>>M8s2O&{ANtGYUzGGb!~#l_L!YJX2R6HYb^|d0sCwmh$EFb zI@wBoW|h<%SRTB^&80;QgCz63{{H@7qP=7DQ?LroQyXC=_#{g0XV0F=RWjaqc-2c_ zlsV^CB>lKI13P+|wXKk5{efW$13&EMMioCFU;2+9N-AsWb-Q-nMCi^+S_CkS0$!gk zkOAHbkSmoq&MDz`4_+z9OO*NKSHh)zJ4?{s#N)(V96rus4-YrkBD>sR2>@?xD0%n^tV^WPPtL;vV zu1ubTgp+n%snlDmjv{B>-Am3}PpuyInhB0gF;Cu|D6>hEzSsUGKSpqIXsK&Sc!*?b zit51Vw_E0Xw)x1Ra}I!llFlW&=AJ96}ZRhF;)UVNB%(MXH#-n zE3*Z`W4?>zK1l8Cxhro(Qj!P44%s~CIU$KSrw(9nE(;}W{`XYOF7 zf>>|4!MuZs#B}ml6n(xK_7%3j;;eemTA^2NX7Go1@lfG*qAtOc3z|t9h=?mBM2J^5 z-uYukh`1-QF74KKAxFNp?|CvBvH9@R<1wK1*Tc36y0vF?wZ0Tp^H=cVa_+psI_^K2uGv}8 znzURlG#y+D8?)fpwLZs^BVSJxfUb{5|LBNX`l3MG4ziGiu263`*E=r~{1Giz-?ezp zJhk|loZc2f3_KWbdXPKLSrnzfp7mvh^ae#IcIF8F?K#1GJCJQ@ZrS_)C{(a|`rdUM zhfF_InDA5%dr##2y`WH%eWH%}pv*?c98W=973<3Z8_!bXj71d4Ycfh6=$+02O!cJ} zm2)3eY|kih1qUkqn&ex#BoY=!3X=YLXn;$19A$($t;v7RmxwMCrDr_i3?h!HfNb2A z#HJ%c+4J(mo(@r@xH*Cae_LlSOUE3a24$;q0OyBhZ+ZiaJm|lOgPvOYG!v zi%jLfNIG5*=t`$+^^XqF{9sI?z*FLx5aQT*Q-)(nr7>|6xC%0*-8L?dnJd6593xph zrppIskI%JA-YT&m@>V48t!^MWeX|`n#2>S|RX^1cj7&0zaS7KQ#UIb*_ulPW1xF3` zIfRL*8}h%N+&fL}ZdQrk>RzMS?@#H?86e4+lMDjfZ5PIgT1Iu6a)bDJRssU`vB;|Z z_W0dCo56V~0E0EM9dehGMm?uDo0AgO^CW8T6%CM*Q+$OZl05oL2?!r$L9trnb#SGv zZ=CGlTm5dACvh(TD*YcUKn*y~!!`I?9g}{5lrEa|nce0j80gbo<}NyX6SLPw(mi!X zt<-9u^6}%-5u|=`_pO}QT_JSqPT0y#7L^VE=Y)v;y6uq5jX-P`64Z~!D zP*yK0TfZ#D!$lUn?X^l94f=h$@MeoJnfGr;{f}x5^rrcp-R!bEo@eHIQSs5hV2LR*#6ywls8`Cl+u+Z|j@4GB;R-$ViOo^QH_Nfh1qig*&d; z*VUlEMuaMp0D#++c8eRBDS+0k+ArO$*6|o=w|aWy}VVAb#DS>3mfsS z4Za-A+DPSF;Ib7JQXJqtB$u>)nnF?b>|Ar2xbD#*#+)P+T{?6}Mc%IB{v~_+y*^Y5 zoQTQcV7Z`r7TB3)uiA}3tBUGxPA@M61f8gRG{VsU4EkU4^961T3y)QJNJ&ZM0yEIozd87@C6pwLtJX|I#4F}=+Rd_&^zkW}Z-pQE&7dK&q_@=C#v zj{@=EPfGKBqT$kRRnNKfhWS@WbViBsiPW=Jikb88&W$>$Y%{SuVq~Vu)ium~`{C)W z(6R?ADzgM)Hsq1=#A~T8?=N_FQN)%O2+@bt_1SWLZ?!u&GJF8;D25V*!q)Z zOX~>lc^Z%Zlz|wv_vZYwTFJ6GFs$#^?0qy)xLNfZnf`mLNSb@GT~<)K3JE|vvQ5Ne znwOk!la&6~kHvjFA^YFc86!{d)t{#OpYM#6;s591%m3@rBsc=JB~4ai`i zaU}3rE@n6S9T-GC@aRfGh2D^Vu9=^o7r=yetzK-4;ADY&qS$2*mt`KQs}EWc0T1s4 zK$YJ0J@x=8DEf95@`J`-R>6vI2ns$~ZTJj{?^-IUIrGnGYxjecvMM9Fx=?DpPu$9z z#tzjFa*R&p*zK>4G|f3S(i6^5=*K_b^L%lpM8Q+Ha%TwU+IF6dHrbuA-bKafU6p*YetP`A^`{|cl{&g?;ear!0! z;dnQgYyu)yV)kDH0`gaYFf@B2gZE%_IuEZ7y&`_T!DkHBDE)WzT z+LM8vF=wfVyh+2$+YZ;A*Dsg$k7pSj^qKU+VrsOW82gz0`I%E;+w5j3zsF*7QI`$D zL85#TZ@NsZ8NYr^o?m%d?Dt`?I=B6t8*j!6ldj`~!^6cbYrU}Zp~8>ht87+gbvKoeO+9$%%WN&lk|C zXLv{U7bs_)6kiDDO@3OV(4hCxy#YsG_6FI?-Vzwv+2sE21guf~5Y4Z-zhQI45Tc9E zD3K(!|Hi3`_bQY)5hKHPBi<&Gq-g#5GY`vGZj>Vn?^&y`L$1-ViH}&$k_UeoH?LB4 zdYjJ-W6JDLmtZU6DE#)1PFH8AbDylq2M;sN4A}n^YiMY=^qG`jYp7E^0zFaon{F*Y z_bf&WOOHJ}t1Htn6w1+7UJWsKLdTT`HQkPM5>x;@=OWkC=nxLpFi9G!lzT+ zd2-c}W{yCIJW)j9>w;`hYK@YWQyIw)?(x9~$o@Hc#xgfdg-IY@d-2;>iqz>uJZQn` zhwm0$MM1O@;Y*VL7O@3hhZ)|b`W>Vhm^>s0i*=}8x>PWfiaPJ|;!}{+orjC2BBmWF z=j5dRj$peOoCph>^Iim4*ul#UlTnEGu9ALR@BJ|EBAB=K(}YAaMMC|(OtKV&ee|~{ z#Ds@4ICKxYaQNT2Nj4OQvll7Ri=6q}0}Y@?zj18*7xX~_MXy<}p#xEe)ym9`(4e4O z=ii?hbYqx1Bs^=ySWv=FZmmzYw&QJ0@kU6% zdc~7VPb9(_=@i(x77_Ij7jgvxJsGH6=l7cH0--V;vIN}OfwZd2)_sZ4F{W;}cwYkB zM55$-R_i>j-R5Si3dkaLG0OMEHKBE?7AwL#@*Ta1s_%A-E~ENzF2qCyl=*=;`270? zako1jk^F82 zURW-TvW9gkigpgYLWoV?#(PX#-^;<+E6Y%1|441->Dj_yBIbl8q6y2N#PE@y?*33q zx^5Yws6O9A3(`@{C$nFpxYl9!FiU~+dWA2X7Jh|jqzDDe#MtARYUImb$Ms+mQE5it zSlnCq3+-^6$dw-gk)Tej-&*X&vvUBwmCv3b3aoKgw|BaWE zPfMb-zo-1n9v8kf@WG@}25$!;xnh&M&%8O1j>&T*&w|vaTD(z->=(nM-`SbfzD#>F z=uP2J#J?5M{djL<8eeDMP1yC5BqaaFIw}_n@ir1S#`NiSxaFQEA|Q4WZ!a1<_?}&j zJq8qc{F6mRtdb;`y#^B8a9#V}_*aV~0-}y^l>2^Hfp?hP?jUwk>gtB?O{TCmnBNGSdj6npEbT!Y4vAkn(fww}xUub?vljCS3PF}V2`JGSA#Cb9 z+AsAkaM@{f&PW~ zIfXiZ14Hs>XK%_zS5+l-3U zc3`SgpTQ_t>feYpr3MiK`fr zj7)YE1T)**eyt^h(1{kBa5Io}sAHtqTIEf7L2h?|{KGHM2+-29`dmSZ7i|%>chfo` z6>>Mz!a%di8h%hx_)QZyDV#@(7}kiKw_(#HL80504V$D(vFQRcB!24=m%uCdB?1XH zCkuhpk)k-NL=u3Mk`1#u>G8vnF6*OQVN-bcl_%m$pM)?LY2V7t;YY73Q-U33fxc2= z#_hk;;z?E}B7Jk);TPRpzk0U0VIt6cCJ|k?=DeCXH=H~JXI}eZUT==2H2gtZD-~Jr zZLE06e(qau?Is-Udc3J@>n7h!`=f4Opqn=bJ23Nw{{?WM(T7i^y>jSf!>(qed z^Ij4T?^%0xYysHl$-gcLel-8!$fWCLg0q{U?(=Ne2xeXKukz6SU-9m*CPAUgN4i9A z!oM$?i~m(&$R(%l{71GWe-okj|L35hSl+p7T)Cf`SC4B^ZBjyG@ci5iu4L#$@k8Bp z{c<6QTlP@+E-%-QcFR~4kIb-58w#FGT5ltl(MXt_(5e_&3ib9I5&QGr%%`LVnH3s? zI5%rLd(7m_d~7&W3m?tlhTDGi(JN?sWz5dA{>2hP%73m#9L}~)VSo)OOD1Y&aPG2e zW=U>6VK5#8rH4{U>Z3~?jAz7hy=ZfZCSLOdq-3tVuo{dgajB1_Y>nE5bNz5^qL5co zk-8eush~G@A(xVXVKxq+zhn;e)~|IQweKXT-sjQpt$6|)L7Uol_5XTxpXB{Y_h8GO%M-|ARctZibI!{=*9L>0-NDK4P@ZeYqW!#ufm4cBR* zH#Qg525;cce>Nfy-~QT|PGX05+U!&kZ*!I^iODDF99_r%jI2uJ1t82Iehi=E5 zcvMpJnFjmZ;==GoX|6)-1q|u8_P%GwsT2}j%Py(^58*f^=vLwzq1di9*7g_A=t0hZ zXoHUvDgo0N#FBE^S(X=-@_tH>DD!#pO2*_m!?^;8dC=iZ^A9oB31Ooj_E)40%FN4D zN^8VN+8f@2Z?x#^AOUIX$0FP`zwVKs*6FBs?vT@WviF7#DUNm3;O$Wut6Ov>%O?-N zj!6U#;oLFudHoi;>m?r@zbQ~y$|Na`MlU#T-&LRxftfOVH|ME;Uf!2GYDJX@w=BgU z>-w5$Crzi2Jc*pFwSA!OQ`qv8#1V#_mh@`K?Q&MGi^+Mvnr*DAghgNwFPpCKmD@W* zE|(OBAb(!vd6#`B)hn$}vgkrm&VYvG3lb?o@`6|P+roM=83KJokAog$^cLT~aWX|O z<)$G+cEg2-x0CtJ`DAcs)VyC1EHo4OQ_mawGbwOv*bDSD9NNq$u zkCi@;uYb!_8;k_eB$qF7lf$4pm;g`tfJSQjj9!Wi4uKvCTL+ zpORn6191|ox*0OSDv@o6Ki^O{GMfZxI0d3S?@Rh`dHG}7T%3tg^R2uA?KorRCU@LC z$AIs^1^WS3y5b0d=I9ii2dLg8Cy?&jRo?7_pfc6u0c!9pd66YW#C|=C!P4>&frYJL0;!_}MOY(V_>2q%V*?K~5LqKybwKyzR z2TfiQxzufVL}#A{ANAi|1utyUM+y!)xnr@R z5T~`+kC$WbDF&~fJj(l_DBU^du}2;&d5`qVwUsb`xDVl3KYy$4cT}f)pM57s($Ug2 zv-V_&S43eVG~v&|aF&sYNn+LF7Y#?yG8I+TwDk1TASkQi$n%~uIP#egNdUgrK}$~l z+WJnyve%5KZoiXkTx0CTGLdtm+GAj*?-gtcs{_u%yZ z06_D-UI&z`D!;9VewZ&b*J1bv8f9%rVr5KpbOu-*;=qRqX^RJi)vFz;ZzzV$%c|=749INYN*OC%oxq6Iy zf_QhzWA^wwaWhi*mef~8{f~H%m@{9W0Z+_+^$073qnRs+N6<#~ob@lDParI@H=IHp zUE~wRqleNrY<+ zmx3apf~$5(VI;5KTvL9H0E`!;60~}P>72<#Vw=5_xg*u@XZkY7N4_{8x3ls5Oe@9u zzcuSUy>UD5n%va_wjjdh4mvhgr(NkhAVt2Pd1r8MnP=duSC$Hep@;1(D!b=;xxS6e zCXdO$ml?C1+w|gURU58-N)FzLRNsxA+Xb1$qqE!iz+T_<$*wGK7}95_WERkK`KT3* z-Hd=6n#GP2*WcdqRic{KYLM%&Q_3B+)?F8Q&fUj(t6r}-1RD3C7a6qG6P4?uS1wfQ zNVFZbig}bwUTv>~hsmprtT3@LOHM`_)<3OB{a>`b2Q*yW+c!Lj5K$tC76cJB1kr1> z6bTa1TM{iHdep&4bb^Q^gy@mzEjnX#qW8||ozcfI!@DQs#-7pIK|p z%sG3Xb9TSVzOLVuMDRv~^UOuIl|5GS)Q1vs4PADjoz+bBo!nvjXsSdEa7rLPL6cmq zg+6%rkhUvcym{1Vo?Ncw1EGiLyJ@XSAW%6W^Wgr56oN!cVEKG}c$5nuqj-xYzjY&$ z1r1N_OJS|-KZn{wu$6B%YP7%AN?bJa9%<{HN4a&er@lxAb4}nIBHs0D;GW*EzYBb& z+pMdq^YZUKK!PlK%nlSs$*s_eLrLY>TaN~F@`+g#J5nlFKGo$TGI-s+=`I|M@~3+_ zT&AH><(aKsc6(1bt4ubtdF|7OI06?7?N@;knAV~_jnw}8sdSwyZ~wH6sMG1%kmZeT z;J66lgXZYPewBnBtclPp7nqt@g)Lxmm`G`;Juj{?Z7+Iq@ZJx#CGs4me&E-=Eq5!s z)9k^cM|Ab3>63egYYWL*$9A8z3Ld>YD7E9g4QT&7mXmpzZS24GPz-dn6N~Z7y4eMF z(Ca1GTU{TR-jC-eRh+>arbYAG}#I8%A@F;3JypPi~f%|2m&d7a{ZM2*RJ z?NSH)peJ3D4y>ORe3CUT)Q!BFeHgwc5uA;i!*-?GAS7Wfng6={02jYkp*@LKNbw9o z1s3(J(xQwljSJZXEs~SY*c=CE&}{4?36EpbFH}R4+O)wkzA{-UT6=nIi%^Qjgk8`hNDbH~|4h_K&(HIX>&A^Ru&&7-j+$5DyhZ(f z7J{f|+|q19^?=}~U?%Z})i&CHCj;Wr!RcGvVCS>o^;Kz}}A=K$;dmpFnyT0s6$SN=);pAr`Shh&id z$74r1{M;9!F5_9WHR-?9;eU=;OG8Lfp4=>En z(9kr0;kG4^q|ZFb!EMlFoOwJ%7l(u>iHpi53XvZ_dusM{z1-#?^BrE13bP3Rg!$8R z7;^Vd2NG#|$qX-F=mk^z~~Nu@@BbH?=(v=gnIIgvuGmhoK$Yu?|v|AzhKvG>BI8yP*ldED6tMx|bO76GroRqeb(bB`sarPTG&VyJB9P++|S2eix8ZJe1Suhk@ z-<5w57T&x6@y0z3hRc0X_kUkdRvA!_Px)R!3UymZxA#|Lh!ru6P0Ad07+QNw@JHnq z&Hr)f&q$%=Q6}^Ng2~)G_cZ_l-qh4op_fNOQwwbe?*;U2#<{W=#8F(RFm5mKtvu=? zX>N9qXXu4pepg9B{ngsas|w1+3K^^tqcAGpRHf4kYyNDI6UI-BKg%;3fYiaC2oH9g z2k%CC_0t$SpGr!c?FW2;yiY(3;STeywOqQ{z@1fyMkww}2!0@RkTNr%SW)S)9B1Cd%=eY@56f{E`&gXuW!w64t_i=*D&Ioh9 z8*KWQS^cdXWc?r!q!MXL0J*oBgYv!7lXOFbduY(7S}oeO-S?hXH`3)q_n^5tsV7X6 z2(ne9h{S=a((SE*ov45E^dK{RPda`jft3_=BLQTKEd@oj@Ui5nlipLgsdC#-Sp&dM z9t=FdGtQsZB<-O$$|u>Phd6e+-gZe^Rqqc8x~_%pDYX8_4|kk2#f2Ge&G_qNhbW2b zK$0Mv&A}dXR&8mXgG>%=KgzAeSb4km$W?7Mid7cv#!e7Ges*L`ffR`12re`em>5*q zBdbR=3OX*ij#ky}CX<9+WxPmlo4<;FQHO)w#dTH2$dv5gr?tbxECIL7nls(g4?pAC-}AYj+fu$Snq7SdU2RDurra`^;oURy==~HgPm7zSPRVL)cc<_f3ux+3?La3e0%eC(s+EVa}#ZH=kU7 zq5426*TcvAg_e8gNGo?$dGy}LaVq(btgV|=LtZgOjzwP%^dEkuL-sGw$DY?oHpV1X zdOEZneBCUNFpkJ=ees6M>^kQ$i;bKKY9atMP$Ie%OfO=cC}WJTcsUH@%!8r~SGl2X^^(~= z3%Qr(pQwfBWE)#_V3w^Akx3G+VsB2p&|>2+wVRTmhdb*I&E#N_+>&fIhKF_-bBm< z{(Q@B{b}~rzN|egbC|hOWMX3bNQQ_qkQ-6)RN!Qp7VF-KhCI;w#J1UcKUs;wtwfny zS+^EC@C)fRG;mP+XxZm1kLuiT6_x-EmY!nno zF1eFDJvj+`DVUm1l85w+mdAa=?6}k6>Nsd_NnkQva!a0T&Z>51y?vfh7f>|x zeKzBnoGfnZR+qY6@u`pF1owGG+=);g3{NoS=d=0fS9Nou!7UV(CsgqwFf&}YFD%27 zy~e~>XZ)qr!JW^(QDFTAB$45GPkt#6{@`kkkQxhO+0Aw;{s(%=)w~6(Y5x>0r`Q<> z^HNfWOB67RE}b1TwI!XUEJe$~J|jnZ-(!TT7bQSaf>tE5(6%n&CZNvxu2M`R3oY^3 zd0Z)Q7@q+MiUf9=(`t({CYF3_p^qovfOM&*z{T6!^8)pS1xt4F7`O|abKtslt4@=r zT1680$qp#zBnjfxzzqV(CQ5x*hV~0T5`o=SZ*t>t3Py>EZi#2Vp1S_p=DLm5q@BkR z$7-L0=-TgZPX|z1`uW~xAhb+Tx|Nyxk#CL@LMttS(t55rFSKD7L(&iOebp=ff=n$m z#+)w3n$4v_PpTTxcO~pZ!6Q{HYp$_Qm2~~d$+@1Tn->ouyclSCRO@r%2B}j;A2y$L zpN3hUxG_HYmGtF%hN1MC%}{nIa11xP+|ct-lb-*h=BF_fU^1xN9TYvzce)vjDF6zh zAMM?+?P!ehU9Ue`79MoDERSL%7lt@+v{Ff-lee%*f0CZVeD zWV$Sb0&*lJhl7IeByDuDRzxeT$6Unal=q_hb3h^+!uF(Lo;?cxJdRW+=}a!_cPB<@ zLQfjT-@T4qlsiM3QQ19CslgpNX*Z<|(FYkO3CiO0P#OBLw?rjyC~am%zw^XWB|uC~ zX-Bl`n+*^j0!^SJiKEBsS|~#J+sPWA>&^gOh>8>55T(z@EHuj6sQ{UWP)rP z79wCukX$7mDO5GH-FQcitPVmqSI zCP7B=rRGZ&BY*qBGu1?|Lm7RMH!VNBa}yaBDFef!t~QK^J|+n66CD_62LE{Eo!j8J zQkrz~Ok=YxX^F)~nX4mE1+POR&D~|EP9vx)&qaa-rS}1kB2;$n;E?K^pW<6;O7-IC zYVYC4U^9vP?2#qqFE>V7sk0d7pCMXT2-^Y~Sh}0|MPUX5zaz8-63$Y#th>f7wHC05 zBuUkP;!BExO(E?bcfMje;%k#$PGJEIx1!WpIMQ3om|9zI<b-@%fU3F3bu0CtqfRo`@X1xnFYwN60ZVVM)7e zvjcLCglrsYqu?m_=(fEv{No;4nFlAHodniv)rSwsfi1PauRAjI{;DW8+xp2dc^-1g zJq=*PhlGX-?;Eap6?>i59~wy{>=wNW4tjJpwnP~y*Z2~q{JgSwLe$t)Mwl$&H2u(_ z&SSXpRq*-OB1c{o(gzcy&drA~3GXQ7iIH=q5#Tt3V6N4(*$K>DZL)s#W$^k*_U@vG zp9_E4NS^dtgZnc6Rp(}NzbAqvHb5j?NY?g&!OKV2c>XKbGV8$1y2z{3wkg;pm#9|{ zZq|MfyE$P!g^k^u$k_kuNwBk1J=smNt^R1u>g-uG3f3%k(UuqA(_q#Zil5!d5qf&s ze(kO7sV&$NXzCB)PzE#>Pc&$0abyq=$Fwt4_%&o*i2*#k(Ak!~pI%UBj$o86^=i4o z#$dui0DIcw1lBu&^?0_0(#|ZsGp2wgpx=S>IyX7>NQnc>Q-&Ik!`nMkUs%4uAWEy{ z)E{YrLNp#RlhBPQ2-=_noMh+5EJQS+c=d^Z3cd zUh9L>I4=WM=~LVTZ!C(fZ+)7-mplE6+HK_f-<}lwxpv0wFNMXIZkbMx5P;RH!U3_W zF!AA7oBVxk_iLW3+yeo*F3mTQb4!a|QIPr17BZO&nt17|a)K4?-2Qp!!zui*# z&Rs$-Sx9;4ruHQ32EW+`MHAp1soN{>WtL04BNNNg@>lSM`4ndEJljTmWn0^fNKtyk zH#kEDV-;nrqhY+p#jg9!(?@74*@?z&>r83}QmZo%UgW*MoOwKx@gTT!T1mLtoDCr62E!9PFebS>G4m4`f^s>4Us2>Z%f1Ol!u8?Q)gutw|345YN@Dv zHrz?3P!#N4%{nANx?xs&-fAcgmBlh?eJ%QT@$sUjy$0jWwR}o>@+9y|ev=CbMjsLB zl%o*ORJX!oi^1aW8gj>v@VbH?M8D&ezOGc6nFY zolZ)9n9)H%q;41iw4YG%-t$7%iCZ1ue?zd`+s86iQg0^ z_&+iR{l9bP*eGzD4oECx793^6D^7Mv{@!F|mKL}D;wYII6jVp~Q~PRym^%1|KmQdA zjEYUeBO)x#@YT*#`nwTcXLf6AzKTk{O`3G;`MWsq9bJ6GN^Y4@{jJ;I*~retdD}Nx zK67tS+c5EGQvVx18G$zX8vy7#n~R%UGyqsRXm>u;Ty}}1QacF5-GKkS^IQDY{Fhbe zNTNnG%5f%GX6sUq!dXq3%d$qvn@T2>v$O)K75)uWhE>K9?pu?g_`;oZ1k%5-x_`m+ zO!W2e3B$(PXs*GWFvQ#)rB+*yc&n(Qay>$w@#X@OJV?C@uoFo>eJ&Fl604zq8R15E z4SKHeZsyDT6oU5%*bJzl*Q46E?t-Nw-n9Q5;I%lUX*Dz$IPhm&&8nlnIN5;Sr2l4sHR8Sl zXv5cO)&&zduR=8Xq~T7FY57G=>qx8HSTMqM1>-Y&4X?mhP{+62jjf6DUEm$W!^fwD zJ3gRhX66T#cPIV@{~|bIQdCrw=8HQ9i4A?}cZHphwV%C4HVe{#1{#_xcf6E<7=g{a zWl~%vI|5kg1X{mzIXK9u05w;gIv=40G&T?*eqS%Mp9ts6rw!--rAwzrhCd5fh=%yh zaRzIZ-%0-<#N6l`Lk$M)m`e3J>KtUlS>AYwVx&gl3-V|A!eFJZTqx$m$Lpm#E^$ji z4mu^MktiJs`(_iM{ZnJ@vdYQ$k7fC;1=J~r=PCSzD;6U;xC!m9c1KvKFKenijRrV> zHb|c{xgdLFB>dzsup|#0pRXJOW%&uJ=*{Yr4qZ6?qeQ;7j30c}Yrd#jpE$?)2!vdX z+s(tRiD0j+EGA`O2rbf(#DZb?{4wn_Ej>G^)QcA{ZUGI$AmfL;lUlsqYQFVRQS+Wb zBEeXkv>hQx{_sKeGUyxpJ)2|EZ2fLs8xpFN6C~SK#=*Y@N4$#F6TQMpFnY8#P}tQA zg0_9_xSvx8?rB~J@ga?xZ4H*}!G$8PyBaLPS*9uc#<&U0`m4{!(wb!ipG>11J$y+B z{fke=AJ`2niutUhBEN{)lLK{Bq(+iA$A<~lL|IHg<0@?jlZ4tpFzazwCevpADG}A6 zB);D|qHJ&5SXhEcoU{ZYR|MS<*I2+tQ%#L5qz*@PyjGQrr&Nb0{I1CS$3HX@AP@i< zq5SaS`;)yS9GRLY?#l_MSW|0j_|cKagvGNmsu=(hdsdth%2Mx>Db zuKs=2{l7bqA#q!R0^&Ok->aEF-Rf{xnyy8QgKCp0Y)%_kA+0KNRdYmQ6?jop%LOQ( zI%u_SS8_`jyWYFXr)(v?wRh$61Y+?G!Bt8-hj9WD6DJRL16m35%10PgFV9b<94 z;^M=Dd$;XI^4E5eNx0&E03I8#jIsG2l>u5`47= zy0?Yb4Pao3fq47nA6<!yU1Zg4XG?gQK(1PK1j+dEkQc*|}mC*kKRm#Hs}- zISC)5cUqx@bq*-F`)ml(S2?nSZV-0Q*;vr7$pJB2j-IaWb*Wy+a9tm$tMolvE^5uq zCsnAay1=RS{*r?CuHR6GN&uU@XG#gOz`nSO7P2-(v-PwB;RBm-$DZU60qGZM#!`nS z@kKjbbSbkW7PWbrbr;W8%Q>ut0gXvAy7U=6<+zl0RA}g(o`kA3)>`#qWD@@d7u^*b zFEUH9ibUOy>cv)-mrW1Mw`KekDj!eTDn|Szw66GC?@ikOEtjT8A_qojIH9Nh@d*kr z6r@*lKL-qO)*N5bkvDLvC>@1>wISPMti0L81&gzsV$n&q6Xq5#Uu5>i`wvRJ?KXsd zdCYoMLe$P{gZC|FMcg%&3^HV;XC2}>x^Bz|v-v{ES`2lSRCpPT#!*%= zdHLTz*&GDOgrgi>eoXTvLG@=UgNc_O!gZb=dj2Z2q)WGbT69;0_Rwv6c7%{JuobL2 z=-R>-mRhJ;x2mODo;Ui2UIX;v59Ie`;FZ83BQt?3pRV!8wae$f2S$wTxVgEpfu;}K z$wPQCGK^|V1RFfgR(t~jwBH<^*?5UIbE1Mv5K%$4aH_K~7Q^)_>xJX^V2bVMa=lll zNv#&mSR)-AUF!aA+Fgn}i=}OEi({KctDLwNMiekdoKv?&x?r7b{bRLUzT??QnWm*__)hEAl;lAx+fmjXB4BpU z&dJ%>FE5q$jWF??mL#zhX39Umxz8<;bn(#2Me-_PC6jX`3EGJ54yt@p`t6OL`Y?b1 z6UEui`&5z@CUupPdacJ>>tty4^ojr@may*A$n8O!BM5Nj)G9b$-o|2SvXXTS6VPR0 z8dCvQUGcL`vl2wpU&d-|C9>6k4oRZtp#_j>oBJpew>{6o`%qgl!n{6e83%2jQ4(;R z=$b>f>g(XkK!+W7*(NyKu+*kuLt1&We43BU7YCOOXPqc~prrK9!)I1pHqMdji;!6) zpw+8}F2EO|t-HG=Ne=1=TsO^olH|4uOnfnm5I;HS;WGf*>bqG!Nw*zttx5AW#wM-t ztA}NS-XHNe;Oi{E)^V8LPQ3}kATHOSn*2daL$jfC!=;^rCf*mOQ-=CLS2&pCgjccF zRFz)(yz>q(6Z^>ZRbk9@7CC;o83d7=Ej~b_l8wSU3`-gs`c^HcFFgs`Q!}P)79GqD zp*|7n8IItes{dttf%4mW&s`ye_KkZ7dd_RcJAECfnc^#X+c+0nf;zYlqcgPNo%ly< z;4;P?W#4Fd)A0F(#F?JeX)%4YJZvmE=59H$9IVFH*=`jUw?AnXQ;k@;!bv2|L4i z9IyVIf^8wreDP-lc0c0_GzIZ`$_L}2t~vRN?MkUli3P784WDb+?fw$Uo3?b&V=x`F z+a}#t!{v--Yr-ISv@0iZB*Ntn)CDHWABAFwlEe&TU=P1>NJmMypC)@_qlW=`YJiiW zlgM+G&I|VBUr4$AX5Tn92Tf*C3A>inj}o&XE)#NANFvw)z2SLa&4taH7y#Yrgo((2 z!>?7Blqd0bbxF&w)?loyXIf1%5F9*bOIW!jR3F>Mc;vGss!yAFpZBPS%b9g&!{Sf0 zIM3{yYBp~pY$DrXy2jK~#ISrG@hX+6b1u23#V(o}S7icY`?> zVIk%Ru`FKB+18pxs3-*pMDFZ}Xmu!ub8+SlaIy(g)GcTQKdUh8+cyy9@UZPMV){7W zSM+9)ta41-MGUV18kOPe*SRmJQV85s?wT*RMCiso&3-1(yHMHx?AE99Kt39Qsy95H zk{LpmC3CZMO66B|qD&R;-r(_xR@B$dY4#a6b(j38fM9?)=5F;c(4L5rX*o_W`;Y1R zDVaCU8aY37Zvz@qqg2Lahau7cC(lD5rSoU49+bgd&nL41zEkD z9+m1IVppOVHDuhdUYG8YpZpm|G>n^BrQ{FBt1 zprzmN=9YHCoTo>APo;XY08a9}+_%!66In-xx(U99NBUJ8u!?d$)4Ber)Rp>HG$jdz~S;rpXTQG)AM>WoMigpi3D znD|>?f&xC}DUzoEWAA$_b55)f-Z%;b7}{Rf--~vv{J;7Q5_^YSPMs~Kmtm1;Y{y}y zbFD*qH4ySk{=;m$1BBYCnR4iK4EV4ciS} zI^IKiBCzik(wBYds`~oU6(Iy(oCOn0CXNJZpouTH<~Ka>cEBUv_*TqQrarVIf5vHz ztlcJ#Ya1)4D)P@KBqQ6Onx2)NDziSvZ#!6x^t!d^K4Nq+3(3Ade%ipvzcR%Lny(Hf*^?__J^8jhBQROrDw z5Jan=dmyZSA02LIzj&mgi&>bDDiK(2oQsJvo5!VH#VNpBVWO1VIpgTbr~LO+7M5i-n+niF z+93suinf&wI@wszH7Ul@3}4^q{RxNm!7nb88B)%ry+}pvqGv@Mfar_eeM{N;`j^ki z#~7rE%d-b72bqTrO2p2Q)wl1?(yCA29&Pubp}xTj!eG3k6G-re|Z*^t?m(k1c@N7Ifj<<+|W1id`xq zkF;BLy%pZ|HInu8w|Ty)E-Dx;UFQeyy3cBRjllaF(&4U9hNgh%nImQVV$u1_btJnu zpsmyWm|92-!8ODA|P_bX|TXMCjf@5GMnc*W#jQ3 zMIfsnZutHEXv4CPkf!`&zb_HRO%>6YaaGsqnS%R5pD@yYYXHgO$;}Q(pEE8Ti-YH| zpXr-~1D8J!K4AAA!T};)8#x{>D?t>`>ca){)mWIt-_uT2O`EOmiT-o{XrbVG)Anp* z;E~m}CJ7>ffGa5?#<_7Dd*E6#NE1NOEjtJf;cauGRfAB9t2PdN@TC(9iJI>$qfR80 z8x=&=l+CYVOl{Wpq~iZ}ktcpwy+r}3TVgVgzJegxTk0ABkEVVo$>YH3Z_KCeEqdwI z4z#ecMIW;4<-paAl;!KjmshXANVh&&2RXZyR%LQ)HKXjsNUG$cAk=Q)pY}^acKqFF zgdBSOJpf-|eJY3ERH}jm^5KfvvHLalTNz;k9Fm^=}U#r0*4+n z)OhX*ps1G}Sm}TcOC@@FWUipydvg7YPTc+zqsP->sC(PvRkk}F-R?Ws(UPLrTsH3O5wDkb9osyG=;jR z6I38lkrC#D-D!=JcFi8G@r<#IeLaiKOK$*L2Og0O#H8sS3BlR=jPTxU|q%1{nHP`d;nCaI~e6%O?zJdx|!Kw-M2*V!7AZP zK4XlUsD#^`FI@{lz{Ic5P3(FpNB{B&K&|EM$vz|DixBOO`je)PXyewZ_KEFXoKj{ zCB>>`t(*`_4J$@4imDAddHaDp^RZ^WZ#T+ormGAb+J9qWg)ZZVXDu*23IoP(%545! zqjyvK&Fl+spvbzh&l!JK7;W%Ex~p_w3U2yzWx&l|U3umKVQaThl*zyTNi3oCazB(U z`{4bagf(o@=e1Zj@*D0mx@ZX4cOL3Zw^|t%?^8H=?y29Bbh~z&-qA-87@@uMy3?@e z{Ay&GF#aOLRhdf$;e&>oc3eYVqM~vpgLb`mQ#T`>cb0e!Gtk?T8)R&ajeMNrfJ?B6 zY*tBJ(-UAbtm*W@0zjIUS+=Xg@0ifq^P)LEr{jo59TIZ8Af#Bj%dACX{dYgqkt2 z&6xGzJw5I3J!YnRa)aAbD|SH_ff*3k#lv&<1a*Haaww_<|1wLM^RK>EZp+nUzALMn z&IR@-U75?97U}iw@W@eouGw#+IzhJjz|NQ^G(ohOhZXt!OcadY>^TWXW4tEk^XKR;cTBM9Ez}IN z?FPOByea!~{nZlRA~Aqp93<~^yy8aAubX>`n3#BUXP;8i)IB971>zTf$D9AB_6GTL z-@I=cectCj4D`q=5WUT|8VrK#K_Z#N_`XjlU>0K7M=WmlY^%mgyYk_g1>u>uDu+B` zVx^s@ko6PLY4`*+sl*pImf89@^W<4!j;G6xrx_e}r)uZwvjaN#zzTy`sDk~QCNMnH z0N&G`;6Hj81-e8$X;U3i(BPw~HPVUM0Gbio7a;~R2ON51-Dla=K^;hXM&3FW?H(Uo zbZWZFI;9B&ao6xwz_NAX_q*2i@#Rw79E2lX}TqslX+k&o~2JRA6eh zqY)}~LXmQ#8>z_G&1R{*(kkwzv`(+CJg=y3O`^2ln!*HWfO-(9~OtNuykLarM$zTTj!fDu@O>HTb-i2U}fwv3qpyI zvaT?NfF{ZVG1Gu{2poYE-r)HvU^x%XmH~KRtT3z-dOmL%aj*c70ud^BM>t~-YuyGy z%wHZXJFJ|~227$olas(k0k4Ax1o;!ClJ1^b^C&@Lz3e8~Ra)S?tC@KD%|u|nvx`ff zV7;@L>$kH^=cNL^ZX|$!6%>Gr!c2-Zsfy>f%G7H`U4Qt)An@``|d3LP!&Gk{&BU-<0pVc%W0)hb++{4wdp zD+0aZuSfj=TBLw}ppm# z5AwjjUJ)(fbNpr#%-g16A^AICIhdTNN_n2AMalVW`mL?yY|d9xliS)*ixnOM2+_$w zL+_x|i@w48TXa1l0Ym9lz;=F_EfdV-m4QlP<1>C|*y}~N&bgZGq}9^wQBHg^)u*6d zu(m3lZ;Ij8yfR`2gComh*?$>7Mm(!0+zg~Q)jI8xO!fr9g?Or8z&1vUs^kybH$AtM zxAep^a%K`=m_bI~0lfsF_YRj<)v-xlhb>{M*~oOlEP;1&Ee$6}fW$flO5ASxJsm5X zW}-A*Ru)T#ckai@$;lAXSpaK+wlM^DTxOqw9KIpMB=M*nKA3N3GySbFz!pc}9IZ19 zOqoas6Z?86$a68!O9X=5Q{GWKY`p` zl}3%MVDxUlo_<<6_o%?~Dp^3#uXd^blvYGJN3c^sJR-)RjjAZAgCMFXSyA*-@_dw;MWy4yiel zOjGxFm`u#C_aMKbF6Ea=Jzy(fvAGVM)Y<(HgldiLJC=Xgo1o~`YOdA&E!pr{2H%Q* z-Kzex{%hUh2 z0Z3Zmj9kiY^taWgMrwoE{&oLOu2}E?_I?YJ#q--Ymm~wf2L7cQAnp(q$TY!j4?!q% z%_LUtsg`5*)=6yW6}(Fmm4lu|!bRLRANg+xk}eU)ogXvlJ>HqhPgcuzJ z3Dv1iUvyM)dpyha(v3E`#fN1%y}kL-2fX;z7!+MbHJgyfVs~Qx2N*By$zq@u!LP?{ z*H_&(J{R40@I~ZN*wE4*>*Tnjz%SF4X9?^dK#>!nd(dAuUKUpBDVDz~tfcpK7;Z`l z>!j%{(o;K_2GN|N&cG|5L(WFN(>p$%5@e7NF|!@xQJ2N2yftuTOdA{TOPg6@H1uSb zZS?%(rp0WlSA9ExjKdEHH=vm(Bo{sB2ufh<0N#3DZ#@~F^H}TU!{;|hk4O2<)2G|$ z*$FA$FYb&js-8Ck-i!~*ZGVE-Dx|TU5zOSjMH7EsC5Fm;W^g5+?Pb~~dG32xS7nd9B&&u7;>L16tv(_Pz5$yMkk=)3%($Lk2Th7a$A>0UQ4UrKM-G)ma{jL`i z(|+WN7JQ;{S!BZnCeX9nWhHU4e&T}0#sB{B+ASHi=bbcly#~=EkF;p{((anNFM>O~ z$T7LRdTpvjJu4@#>Rrzw1D6s1aQw!NvF+dO{Br4i)hM^jN4I!R7kYN&wqq35FPk#) z&KBJ@PakjdwZa_^9I>J&H0@kIvVT-*Wi6ee&7{Z(uB4+1KdF`D*s7E4Yc&WpHNO`) zt{>VzI?Aw}@xRY!H^0s32p7oR03r?amFfHs?4z)O)}7QeNI! zn;0c@1$|PGDmw7?30hoUA-GT1gq`d>c67O|4r^u?QLURRHg><_VqdefaK8$hHJ4XJ zy+-LQ*5$MwvkZ;|-;r2#7k<_&mB=Hq>Amzq=){Te*;M?>e6C7?@OLGrbZJ{9n9w>n@D4%=_Ife>|0L_U34fz=A#L!%U0M84XKqZ3U|>U1+4- z7dN701BCb*)3wNym1A6OVIu?Qg|BDgHzxug*);y??E&{wf|SOQ;Pt)a?RjgP5f)`G znL~X53V`qCn=c1Vy^lQ{7YEqBY0hpi`~58H+daFm=LJv8%cIE1$Y|^8ieO+3&JRI* z!8s%(Mt0}JRYi<8zPBIBdU8^QZPlKn9m))!jD_l&7u(v2T#56#$sUiGB)psONouXACBF6X&+jyUjwH>p|s??A?av z<=rfFo?ZguLX!UZ0ZgDlZrI-SH?!xh{L>aFjDNsOz%3_&JY`u>KbxFhF}A9l*M(j{ zhv_i+Zp&x6GA7Ujk#g}gQ`(=qACg?%v-acP^QqmlD5HzOj-f&%=doWrcBy z$}pT-8;@~zeuIKlz>m4!$!eHE0BU=owt6)f3^GOQA4ExqH@)pmmtP?{q(xcq^WR+p zckA_$pe<8aX&L3gp&2oeZ`-EdlYg-PNT7Fbv z-Su_57~dG`zR@?DCk~2L50M^jZM!tRMWD@L&K5b%&GQ54d*}PEJXY0{Ta^R4+OcWY zE$gUYu^ByY6!21dQZ5PS;Aak*v`wLc-$MAsryWA=ROT}D04H897hY|MIk4OnFm z7NH{U^-mVZyIg30z4N$hb(k_VX8y}1W%{!1VGZ3YwuX*vAMmHX>J578XPXy+|2jix zU+EX4L|XEr$G2vv?32yiaUHDG&JR=R>m%-hCVx16Ob*CNkUV0jm6)(cC4CG89+XDeT8-YhZ6Q-PD_ zOueM8RUcs!8Xq5r)QLkIYz_P!H?-(APp(9}_Y4LQt}L1Rw97UBYLe;+1`R=t;Zhfn zsGYqodiZkVNrDB@OSuNy^cA{Vb$n~ONeqT!KQ2s2pN@HC@bqvOyO23Ck(vsK6neq7 zeH^~guBzTSW$jyCC5F&0Xp&BIzd1X7&kt3}g&sr4%R}B_FMxq&TJRh@f5GR`?F|Jg z1SgTU$hoa{-T+t~JzDiqI5X2bG*zM=V+;KPiIQ+$fmw=tSryd?WWzp_W{s&?dtIfi z>JfsaKD;R?Ud|z~7&dbLQBr~%EmLjIZ$-H;%gNEDAW&#sL^44JJ36L76Y`Ch*S;QZhM2LgY#9R<+4t@83iOU)1KrcZXWEm`Icts zEa;2WM~v9eW!Wp8k%)Y?EN|PWC$HSNAHG;S2A!X4&UUVIJ^z)^H-bn~Y<6GDCqpk9FnyKG?jV_z4=5LOKVRhgIyQ zlh#a4opRjEv={B8VR)-GeiS27jKa70K4nAtL4D>$#twJtCa4nU)XT@*a9fFiG`r^8^sitMnffED7azj)WVWp11uD##M`l~>n-oe4!78%FDK3%Y3id7wo#4bs=CcD@X z9i>x+_as+BG|kg_HHQHV3@N~{*YLHgIYX)ig@csrxduKZ%61=%s#7(*`H|5g zsqcnMzMbKxVQx8cprqURbl=z7k4}ZGr9Y3VE}~BS>#|kFO_P_I7PdDF&bBiOxIGf2 zw@djCbnTo~F<=dMir~LZPKEZs#GmNa5*g-&L@id#9zR=JKgF*^UlmRwezcmIR%*${ z=P0hbX+Oe6Es_56C+kLd{#V6TyE-ui1qJjV2vitWZ~FZ8GM|Sl=0lq{_Xj{QwX5Bj zIT=ker2%9DO~d;ApsvNm#V;%gBkN+`>I^x7+bV|Uq->0~%DSH)-s;?zlMfyAI^LvL zso1pp?$Kz&&$yM=AKAH4G5zS0EknL5DXRmiL)kG~S09$c!kAb>mVYRC&*A^$ILz%7 zPqGcUg;~}R#w6Rnr4#CXa9r5<`$C!pes-wY6+D*!cnLhzsq6*cYqfF{%NU=SJ(KE> zyUY@0Fd^tv{Rc`pZDI9t9nq4*U1gQ;VnN%{MdB)-cw8OxtXg{|KDYb<%2OHdz3E4C z-r=sxO7E{1OPvwe9un%uVJ}Gkk)&_OYOa{MgWO#m7%M zILY!K5Och={i^i?-M=@VSJlw}c^>i~DB+a`eU3CN~`QT^ncjFJuJ2mm6-qM4fT%U+H}7JT#-UOUw!RFrh%ztI*r8-n@Gk(tq*q zO=uuxM&ue*>!uhIY@LrMOQ7|HYCn&8S55Gg|Lw{56u=1^FnvoY{8-#)nfCh4SDtlJ zY=V6&w@4U}m|nWEJyEjSEAUs~(Ma5=Yc9w?kI*aL7^;A+mLPC13gJ%aYR}pS8)new zw=k_ou+WT4!@y^#%_}=QyUAa*?(7>x++q6lC~AK3^Toiu{0lE-`@=`p-M3K?nn{^1 zUissazE9%`p0DBG=i>ICWk~<7y*|O6H-6t$w;*a_^GaS`o@1qP<6+T{A8L~z&`f52 ze%Xfp9Nqe8u=eod8(MiOSSYfwT_@+s4{=?+Vd_zlmpjSy=Idp4FKupbiCvMMb!Mo$ z(cEjUee5Wm_=7c>e@5)hngxj<`dfW~FsJZMcXAM}EBYyPUM-@oWKuZpmeqB9T2>4T z)4|}WJ~K!0yXnc3NMHWVvnRKpYFtOA5TpfdhuATFu$((20cUB^w%`6%XY z_`o+MT!DcdLs#ea)cg6M^r0GqR}YIs(tFSvE@Z~~Hv7*xKc-e%ZR)>#{pHPb-mSzX zT3v5=6OCfEv0z4NDV=@!g!BBp0AOZ$NnZ>(IGXte3g^{kmU+qd`pNdW?M*8Ay16j2 zn!N`Z&r4%bP!xgWbOiW?1N?r9SJ}0 zyr*6o`=DWH>7H%E*c3W6RE)ZVLh5pM>UnsN4G$scOghB!%U}TvH^~#Ew=0&p3Uq z#XP;BBU$cwlSGAK$~w93Ww0|HN1@c6Z6HydF74E`=ta zw_`3hE0D}p9_I3M}m6d~jz#sa(tdkmQaLTi)tz)HSFiHHofefxGXapFW&e|&mS=$y{<3}Ee8@6H#>#B^^be!g=* zoOew?q!3vAQh8@4QwpJ{DrvktJ;8sJA z8)~G_DQ4s^4St>Uitr0dk(D>!HX&@w$HpE{U2mh09ZqnbGzbLcc>dXRK-c>PQ3)~*%ZUT;DHw9DmEy1iaKx@CxD#vYB< zCn$o!`zD~|7bPfjIz^+!VXa=8>) z`_|UhLGAx?wSJ35jxAUqTrQXL#?^-+P_e*VUt;osA0RZ&$^aO8L-?nDWnn90wvpX9A zu;=wsU{-Fhef2)&GJJoe`fjwX0iKyZ8^?2j0&GrtELoTTUA*T$jD;(--;Z6^qot)K zc-u?syHQ+xE6U5uwLV;s%jFU+EiJ)4=4bxZ6uh+llk(cXMX&H(a=BbeO-+p^c}UacCZ!Bz0#5)> zy>}KR#lf1h7vrh3XMraG91e^*p~f-vc`0im!*H#yUr^l8Fr+=% zhtTIhJhj%#cXD44|La|^c^~v@*UrM##BO!cWe@m*f&z4RccY-7K#%5!ZC}sh4yR<< ztBU*xy*<(PVrf4eXe^@}TD`U6ylVpXemHa$T31&WT3TAr)6;{luCCyR`nFJG z^Y6si{5x@F@^iQ{`8gc9G;zp9LSL`np&Yq15ihOp#=E1OIC5zsj$E3k$ylEJ@m@3@ z=}oGQ9UH1d-;q^Ao|AIyn>TM(WEfstT&!CNUR4#?`@g5B2NNgWsL$L5OBFqj8><#i zowi#djITp~_fErC7AMM#RjB>lJB>eR1i^XY{2Rt{^MC#IugRQo{-d(e{7YH}(01F7 z#)y*#S{|dRut||ytvq%1EaYnIaL1IZvE@qe_TCX_ZEc0WyBYrO<{@qSyPJcnwWF;R z@9pgkwwdVoLW6tR=i-J2rRj2$(o~pu_#RHUmGMTcOb>T5pr)qA)Rpmlm#^8XT|4W$ zd<}2C@r($)%}$iY#zxH=tFNyYm6erB=)amBAi{B?-A-hb_t)PqN+$IK0NT%G;o%qd zCHy?pvMr-@Y;_7F`)cJHtz5ZMtXsD(;r=B1_;g}MtwQ%UKXBjx7A;y7?1Nk#9Bx(g za1wo-RcznBU4#aq+1c4ik3+9|>rLJ70|yQOz@UCha=V^m$BqSWv(h-^zVy*n(udBI z7IQCGyvxu%TwU}yZX)D%5q;UoUyxMGwo;YVYQ*_Bmep#+`8QUo*QlnZM&#w@V&$q; zDLKAmty{ZRXjPjsr&BaFYWqB@si_eq6Rsu|tlNcLm2T!Z&FsU>(a(uPmtRvtKR3Ir zR1~fz@z%-dbZXwOcxW2H6Q#SCpIs#GocJY5CiUapLpgY5mR%0g$qnLW|( zJd8(Y_TUM3pgpU5`PoIHWKv+}_LpHKLFz9H`f0B&rfBgc}RovlRLuBW`b zJlK~0z1B{4cDADB+^<=)Mv3Kwiq^*^A|f)Ayf7;-JUNuCjor5Dq14mm^P|1|6n5Qr zEc%^eRJwlvT|U2N?WKw?pC6_B2LJ%O?mLEE_Z_ocofyCqktW*DWu@h@)!tlHBPJ?7 zyt%4Is6RKljdWm@Iz)|&@1gyd zw}sv}Tzgh#Rshc6ors81mE3EaA^cE0K|xmVid&%Mz!Xi|D&JoO0Ql+ZPQ1dW^$@SP z1%A4^Gx+tqgT=P1(@kb~fGE}c$NDTB-gpt{F0;c_fCRpa^=G{zk7 zxo2~dey4%fN^YM#=A-F1otB z=<4c<7`~eoF2>qF$;f^tQg5UB1|(JshR8%jL=-C3_YhBp%jHt!ML4Ugt63*9+|kZJ z06gztv1S(pop-PpFE8Rd{6ih>6sWttMUUL7N7mmixB{L?V!hUXa8|lDKb=tgnv+`gOpbmo$fw-Rk`tPPlp#5fKql zYH_(-3d46Y%LmKagP;3;*OF9Q>c^|wrGO*bZ)8nvWoOV@=QQ8~>^mE!s#2tP>Ft5fKp)nPqCrECB!~XMC9S|3$TIE7eSGnI-bRfA^r< zy7wNAt5#-Tgq2&+5D{rii>*+&?M!S~Cn6#uA|fI(gO*`AgZIcz&iGKl-dSSEZ8pLc z@I*vJL_|bHL_|cClFVKK&pqIYh=_=Yh=_=Yh=_E+0G^15h=_=Yh=_=YNC*E9gi;j$ TfMzo&00000NkvXXu0mjf3nDo} diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index dde1156d295cc54662aa3fe9314f45b87abd1868..78aa94f2907e719eef10903e284c12ead3275d73 100644 GIT binary patch delta 7055 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 delta 5716 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 diff --git a/icons/mob/lavaland/blood_drunk.dmi b/icons/mob/lavaland/blood_drunk.dmi new file mode 100644 index 0000000000000000000000000000000000000000..7cf9e2c0197d82ff958f6d38e0882efb51bc0d21 GIT binary patch literal 6257 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 R4+9WLQ%z5`M%nK5e*hAbh_L_w literal 0 HcmV?d00001 diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index aea237f5bed3be518b1bbceef927798252caaf94..01295525b159ab1f466d4690f22208e9686bb08c 100644 GIT binary patch delta 9551 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 G!u|(U{IwYX delta 8891 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 H&BOi=B~?;@ diff --git a/icons/obj/trash.dmi b/icons/obj/trash.dmi index 6edd905c5549769f0fbdc32b0f478fa935d353fc..5fa2372fa8085e702e5ff527506b6adb509fc3ef 100644 GIT binary patch literal 12110 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 AHvj+t literal 9990 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 D^Z3ue diff --git a/icons/paper_icons/syndielogo.png b/icons/paper_icons/syndielogo.png new file mode 100644 index 0000000000000000000000000000000000000000..6b0c4966541b5254cee30995b02ead93f22f121a GIT binary patch literal 7734 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 aj2t54HT>7$DfD0eOBp+s diff --git a/tools/travis/README.MD b/tools/travis/README.MD new file mode 100644 index 000000000000..3079e9bfa435 --- /dev/null +++ b/tools/travis/README.MD @@ -0,0 +1,3 @@ +# Info + +All scripts utilised by Travis CI should be placed into this folder for the sake of maintainability, and knowing where stuff is \ No newline at end of file diff --git a/tools/travis/check_grep.sh b/tools/travis/check_grep.sh new file mode 100644 index 000000000000..22d02647bcf9 --- /dev/null +++ b/tools/travis/check_grep.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -euo pipefail + +#nb: must be bash to support shopt globstar +shopt -s globstar + +st=0 + +if grep -El '^\".+\" = \(.+\)' _maps/**/*.dmm; then + echo "ERROR: Non-TGM formatted map detected. Please convert it using Map Merger!" + st=1 +fi; +if grep -P 'step_[xy]' _maps/**/*.dmm; then + echo "ERROR: step_x/step_y variables detected in maps, please remove them." + st=1 +fi; +#### TODO: Uncomment after managing all globs #### +#if grep -P '^/*var/' code/**/*.dm; then + #echo "ERROR: Unmanaged global var use detected in code, please use the helpers." + #st=1 +#fi; +#### +nl=' +' +nl=$'\n' +while read f; do + t=$(tail -c2 "$f"; printf x); r1="${nl}$"; r2="${nl}${r1}" + if [[ ! ${t%x} =~ $r1 ]]; then + echo "file $f is missing a trailing newline" + st=1 + fi; +done < <(find . -type f -name '*.dm') + +exit $st \ No newline at end of file diff --git a/tools/travis/check_line_endings.py b/tools/travis/check_line_endings.py new file mode 100644 index 000000000000..86c2a8b77c7d --- /dev/null +++ b/tools/travis/check_line_endings.py @@ -0,0 +1,49 @@ + +#!/usr/bin/env python + +import os +import sys +import glob + +WINDOWS_NEWLINE = b'\r\n' + +FILES_TO_READ = [] +FILES_TO_READ.extend(glob.glob(r"**/*.dm", recursive=True)) +FILES_TO_READ.extend(glob.glob(r"**/*.dmm", recursive=True)) +FILES_TO_READ.extend(glob.glob(r"*.dme")) +#for i in FILES_TO_READ: +# if os.path.isdir(i): +# FILES_TO_READ.remove(i) + + +def _reader(filepath): + data = open(filepath, "rb") + return data + + +def main(): + filelist = [] + foundfiles = False + + for file in FILES_TO_READ: + data = _reader(file) + lines = data.readlines() + for line in lines: + if line.endswith(WINDOWS_NEWLINE): + filelist.append(file) + foundfiles = True + break + data.close() + + if not foundfiles: + print("No CRLF files found.") + sys.exit(0) + else: + print("Found files with suspected CRLF type.") + for i in filelist: + print(i) + sys.exit(1) + + +if __name__ == "__main__": + main() From 0093b29cfc3102e8096e3f5b172567c1ce3146b8 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Wed, 7 Oct 2020 12:23:16 -0400 Subject: [PATCH 04/10] Deconflicted and updated! --- _maps/map_files/RandomZLevels/beach.dmm | 71076 +--------------- code/__DEFINES/sound.dm | 2 +- code/game/area/Space Station 13 areas.dm | 6 +- code/game/machinery/dance_machine.dm | 8 + code/game/machinery/door_control.dm | 35 +- code/game/machinery/doors/poddoor.dm | 3 +- code/game/machinery/tcomms/presets.dm | 5 + .../game/objects/effects/spawners/lootdrop.dm | 8 +- code/game/turfs/simulated/floor/misc_floor.dm | 2 - code/game/turfs/simulated/walls_misc.dm | 9 +- code/modules/awaymissions/corpse.dm | 22 +- .../simple_animal/hostile/deathsquid.dm | 3 +- 12 files changed, 1072 insertions(+), 70107 deletions(-) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index c4cbdbfb1ad0..885cf8e1aec9 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -1,70084 +1,998 @@ -//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"aa" = ( -/turf/space, -/area/space) -"ab" = ( -/turf/unsimulated/beach/water/deep, -/area/awaymission/undersea) -"ac" = ( -/obj/machinery/poolcontroller/invisible/sea, -/turf/unsimulated/beach/water/deep, -/area/awaymission/undersea) -"ad" = ( -/turf/unsimulated/beach/water/deep/rock_wall, -/area/awaymission/undersea) -"ae" = ( -/obj/structure/ladder/unbreakable/dive_point/anchor{ - id = "pirate" - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"af" = ( -/obj/structure/constructshell, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ag" = ( -/obj/structure/chair/wood/wings, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ah" = ( -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ai" = ( -/obj/structure/cult/pylon, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aj" = ( -/obj/structure/cult/altar, -/obj/item/veilrender/crabrender, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ak" = ( -/obj/structure/cult/altar, -/obj/item/book/codex_gigas, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"al" = ( -/obj/structure/curtain/black, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"am" = ( -/obj/structure/cult/forge, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"an" = ( -/obj/structure/flora/rock/pile, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ao" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ap" = ( -/obj/structure/chair/stool, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aq" = ( -/mob/living/simple_animal/hostile/retaliate/carp, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ar" = ( -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"as" = ( -/obj/structure/boulder, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"at" = ( -/obj/item/flag/cult, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"au" = ( -/obj/structure/mineral_door/sandstone, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"av" = ( -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"aw" = ( -/obj/item/toy/eight_ball/conch, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ax" = ( -/obj/structure/reagent_dispensers/beerkeg, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ay" = ( -/obj/structure/closet/cabinet, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"az" = ( -/obj/structure/rack/skeletal_bar/left, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aA" = ( -/obj/structure/rack/skeletal_bar/right, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aB" = ( -/obj/structure/rack/skeletal_bar, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aC" = ( -/obj/structure/mineral_door/wood{ - tag = "icon-wood"; - icon_state = "wood" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aD" = ( -/obj/structure/chair/comfy/teal{ - dir = 4 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aE" = ( -/obj/structure/table/wood, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aF" = ( -/obj/structure/chair/comfy/teal{ - dir = 8 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aG" = ( -/obj/structure/toilet, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aI" = ( -/obj/structure/chair/wood/wings{ - tag = "icon-wooden_chair_wings (EAST)"; - icon_state = "wooden_chair_wings"; - dir = 4 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aJ" = ( -/obj/structure/chair/wood/wings{ - tag = "icon-wooden_chair_wings (WEST)"; - icon_state = "wooden_chair_wings"; - dir = 8 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aM" = ( -/obj/structure/sink{ - dir = 4; - icon_state = "sink"; - pixel_x = 11; - pixel_y = 0 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aN" = ( -/obj/structure/sink{ - icon_state = "sink"; - dir = 8; - pixel_x = -12; - pixel_y = 2 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aO" = ( -/obj/structure/closet/crate/can, -/obj/item/storage/bag/trash, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"aP" = ( -/obj/item/flag/species/skrell, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"aR" = ( -/obj/structure/mirror{ - icon_state = "mirror_broke"; - pixel_y = 28 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aS" = ( -/obj/structure/bed, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aT" = ( -/obj/structure/dresser, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aU" = ( -/obj/structure/ladder/unbreakable/dive_point/anchor, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"aV" = ( -/obj/structure/barricade/wooden, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"aX" = ( -/obj/item/stack/spacecash/c500, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"aY" = ( -/obj/structure/mineral_door/wood{ - tag = "icon-wood"; - icon_state = "wood" - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"bd" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"bf" = ( -/obj/structure/sign/barsign{ - pixel_y = 32 - }, -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bg" = ( -/obj/item/clockwork/alloy_shards/clockgolem_remains, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"bi" = ( -/obj/structure/closet/gmcloset{ - icon_closed = "black"; - icon_state = "black"; - name = "formal wardrobe" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bj" = ( -/obj/structure/closet/secure_closet/bar, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bl" = ( -/obj/structure/table/wood, -/obj/item/reagent_containers/food/drinks/shaker, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bw" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bx" = ( -/obj/structure/clockwork/decorative/tinkerers_cache, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"by" = ( -/obj/machinery/cooker/foodgrill, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bz" = ( -/obj/structure/table/wood, -/obj/machinery/reagentgrinder, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bB" = ( -/mob/living/simple_animal/butterfly, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"bD" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"bF" = ( -/obj/structure/table/wood, -/obj/machinery/chem_dispenser/soda, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bH" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"bJ" = ( -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bK" = ( -/obj/structure/table/wood, -/obj/machinery/chem_dispenser/beer, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bL" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"bM" = ( -/obj/structure/chair/stool, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bN" = ( -/obj/machinery/vending/cigarette{ - extended_inventory = 1 - }, -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/structure/sign/poster/contraband/smoke{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"bO" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"bT" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"bX" = ( -/obj/machinery/cooking/oven, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"cc" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/stalkybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"cg" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"ci" = ( -/obj/structure/flora/ausbushes/genericbush, -/obj/structure/flora/ausbushes/sparsegrass, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"cl" = ( -/obj/structure/closet/athletic_mixed, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"cH" = ( -/obj/machinery/door_control/brass/beach_brass_temple_switch{ - pixel_y = -32 - }, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"cJ" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"cL" = ( -/obj/structure/flora/ausbushes/sparsegrass, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"cZ" = ( -/obj/structure/bonfire/dense, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"db" = ( -/obj/structure/chair/stool, -/obj/item/instrument/guitar, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"dd" = ( -/obj/structure/dispenser/oxygen, -/obj/structure/sign/poster/official/air1{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"de" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/obj/structure/sign/poster/official/safety_internals{ - pixel_x = -32; - pixel_y = 0 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"dg" = ( -/obj/structure/chair/beachchair, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"dh" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"dj" = ( -/turf/simulated/floor/beach/roughcoastline/dense, -/area/awaymission/beach) -"dk" = ( -/obj/structure/flora/rock, -/turf/simulated/floor/beach/roughcoastline/dense, -/area/awaymission/beach) -"dr" = ( -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"ds" = ( -/turf/unsimulated/beach/water/dense, -/area/awaymission/beach) -"dt" = ( -/turf/unsimulated/beach/water/drop, -/area/awaymission/beach) -"du" = ( -/turf/unsimulated/beach/water/deep, -/area/awaymission/beach) -"dv" = ( -/obj/structure/ladder/unbreakable/dive_point/buoy, -/turf/unsimulated/beach/water/deep, -/area/awaymission/beach) -"dw" = ( -/turf/unsimulated/beach/water/deep/dense, -/area/awaymission/beach) -"dx" = ( -/turf/unsimulated/beach/water/drop/dense, -/area/awaymission/beach) -"dP" = ( -/mob/living/simple_animal/butterfly, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"em" = ( -/obj/structure/closet/crate, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/item/flashlight, -/obj/item/flashlight, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"eo" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 75 - }, -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"er" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 104 - }, -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"ew" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 44 - }, -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"ey" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 97 - }, -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"eB" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 94 - }, -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"eC" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"eM" = ( -/obj/item/shard, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"eN" = ( -/obj/structure/flora/grass/green, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"eS" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/palebush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"fa" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"fc" = ( -/obj/machinery/biogenerator, -/obj/item/reagent_containers/glass/bucket, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"fd" = ( -/obj/structure/clockwork/decorative/relay, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"fj" = ( -/turf/unsimulated/wall{ - desc = "A sturdy wall of brass. It feels strangely warm and mechanical sounds can be heard from within."; - icon = 'icons/turf/walls/clockwork_wall.dmi'; - icon_state = "clockwork_wall"; - name = "clockwork wall" - }, -/area/awaymission/beach/offshore) -"fl" = ( -/obj/machinery/door_control/brass{ - id = "brass temple"; - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon = 'icons/turf/floors/plating.dmi'; - icon_state = "basalt0"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"fm" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"fA" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"fE" = ( -/obj/machinery/vending/syndisnack, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"fJ" = ( -/obj/structure/table/reinforced/brass, -/obj/item/clockwork/weapon/ratvarian_spear, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"fN" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"fR" = ( -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"fV" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/palebush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"gn" = ( -/obj/item/clockwork/component/geis_capacitor/fallen_armor, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"gp" = ( -/obj/structure/sign/poster/official/ue_no{ - pixel_y = 32 - }, -/turf/simulated/floor/light/colour_cycle/dancefloor_a, -/area/awaymission/beach) -"gq" = ( -/obj/machinery/vending/crittercare, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"gt" = ( -/obj/structure/sink{ - icon_state = "sink"; - dir = 8; - pixel_x = -12; - pixel_y = 2 - }, -/obj/structure/sign/poster/official/cleanliness{ - pixel_x = -32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"gw" = ( -/obj/machinery/disco{ - anchored = 1; - desc = "The first three prototypes were discontinued after mass casualty incidents. This one seems completely bolted to the floor."; - move_resist = 1e+031 - }, -/turf/simulated/floor/light/colour_cycle/dancefloor_a, -/area/awaymission/beach) -"gA" = ( -/obj/structure/table/wood, -/obj/item/paper_bin, -/obj/item/pen/multi, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"gB" = ( -/obj/structure/barricade/wooden, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "wood-broken" - }, -/area/awaymission/undersea) -"gD" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/welding, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"gJ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"gL" = ( -/obj/structure/table/reinforced, -/obj/item/soap/syndie, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"gT" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/palebush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"gW" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"hm" = ( -/obj/structure/rack, -/obj/item/stack/sheet/plasteel{ - amount = 20 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"hn" = ( -/obj/effect/baseturf_helper/beach/dense_roughsand, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"hp" = ( -/obj/item/clothing/gloves/botanic_leather, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"hv" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"hB" = ( -/obj/structure/closet, -/obj/item/clothing/under/pinkhawaiianshirt, -/obj/item/clothing/under/pinkhawaiianshirt, -/obj/item/clothing/under/orangehawaiianshirt, -/obj/item/clothing/under/orangehawaiianshirt, -/obj/item/clothing/under/redhawaiianshirt, -/obj/item/clothing/under/redhawaiianshirt, -/obj/item/clothing/under/bluehawaiianshirt, -/obj/item/clothing/under/bluehawaiianshirt, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"hD" = ( -/obj/structure/shuttle/engine/propulsion{ - dir = 1 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"hE" = ( -/obj/structure/rack, -/obj/item/storage/backpack/satchel_flat, -/turf/simulated/floor/plating, -/area/awaymission/beach/offshore) -"hF" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"hH" = ( -/obj/structure/table/wood, -/obj/item/storage/bag/plants, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"hI" = ( -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"hP" = ( -/obj/structure/flora/ausbushes/palebush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"hR" = ( -/obj/structure/table/wood, -/obj/item/toy/figure/wizard, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"hT" = ( -/obj/item/stack/sheet/wood, -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"hY" = ( -/obj/structure/bed, -/obj/item/bedsheet/medical, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"id" = ( -/obj/machinery/vending/hydroseeds{ - extended_inventory = 1 - }, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"if" = ( -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"im" = ( -/obj/machinery/fishtank/tank, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"is" = ( -/obj/structure/table/wood, -/obj/item/clothing/suit/pirate_black, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"iu" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"ix" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"iC" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"iF" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/obj/item/melee/cultblade, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"iI" = ( -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"iK" = ( -/obj/structure/chair/beachchair/red, -/obj/item/toy/plushie/octopus, -/obj/item/clothing/glasses/sunglasses{ - pixel_x = 2; - pixel_y = 6 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"iL" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"iT" = ( -/obj/machinery/photocopier, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"iU" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"iX" = ( -/obj/structure/table/wood, -/obj/item/flashlight, -/obj/item/stack/marker_beacon/thirty, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"jd" = ( -/obj/item/clothing/under/color/brown, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"ji" = ( -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"jr" = ( -/obj/effect/decal/cleanable/blood, -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"jA" = ( -/obj/machinery/vending/crittercare/free, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"jD" = ( -/obj/structure/table/reinforced, -/obj/item/suppressor, -/obj/structure/sign/poster/contraband/syndicate_pistol{ - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"jE" = ( -/obj/structure/table/wood, -/obj/item/reagent_containers/food/snacks/pie, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"jL" = ( -/turf/unsimulated/floor/lava, -/area/awaymission/beach/offshore) -"jN" = ( -/obj/machinery/gateway{ - dir = 9 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"jX" = ( -/obj/structure/sign/poster/official/love_ian{ - pixel_x = 32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"jY" = ( -/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ka" = ( -/mob/living/simple_animal/hostile/retaliate/carp/koi, -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"kd" = ( -/obj/structure/chair/stool, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"kf" = ( -/obj/machinery/smartfridge, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"kg" = ( -/obj/item/clothing/shoes/black, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"km" = ( -/turf/unsimulated/wall{ - icon_state = "rock"; - name = "rock" - }, -/area/awaymission/beach/offshore) -"kp" = ( -/obj/structure/table/reinforced, -/obj/item/gun/projectile/automatic/pistol, -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"kq" = ( -/obj/structure/flora/grass/green, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"ky" = ( -/obj/structure/table/reinforced/brass, -/obj/item/storage/toolbox/brass/prefilled, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"kz" = ( -/obj/structure/closet/crate, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"kE" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone0"; - icon_state = "sandstone0" - }, -/area/awaymission/beach/offshore) -"kH" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"kO" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"kP" = ( -/obj/structure/grille/broken, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"kQ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"kT" = ( -/obj/item/shovel/safety, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"kU" = ( -/obj/machinery/gateway/centeraway, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"la" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"le" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"lg" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/water, -/area/awaymission/beach/offshore) -"lo" = ( -/turf/simulated/floor/wood, -/area/awaymission/beach) -"lC" = ( -/obj/structure/closet/secure_closet/freezer/kitchen{ - locked = 0; - req_access = null - }, -/obj/structure/sign/poster/official/high_class_martini{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"lD" = ( -/obj/structure/sign/poster/contraband/lusty_xenomorph{ - pixel_y = 32 - }, -/obj/structure/closet/cabinet, -/obj/item/clothing/suit/leathercoat, -/obj/item/clothing/head/fedora, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"lH" = ( -/obj/structure/shuttle/engine/propulsion{ - icon_state = "propulsion_l" - }, -/turf/simulated/floor/plating/airless, -/area/awaymission/beach/offshore) -"lJ" = ( -/obj/structure/flora/ausbushes/leafybush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"lK" = ( -/obj/structure/ladder/unbreakable/dive_point/anchor{ - id = "volcano_island" - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"lQ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"lS" = ( -/turf/unsimulated/wall{ - icon = 'icons/turf/walls/abductor_wall.dmi'; - icon_state = "abductor" - }, -/area/awaymission/beach/offshore) -"lX" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"lZ" = ( -/obj/item/instrument/bikehorn, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"mc" = ( -/obj/structure/ladder/unbreakable/dive_point/anchor{ - id = "brass temple" - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ml" = ( -/obj/item/gun/projectile/automatic/pistol/enforcer, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "wood-broken" - }, -/area/awaymission/undersea) -"mp" = ( -/obj/machinery/sleeper, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"mw" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"mA" = ( -/obj/structure/table/wood, -/obj/structure/sign/poster/contraband/have_a_puff{ - pixel_x = -32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"mF" = ( -/obj/structure/table/reinforced, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"mI" = ( -/obj/structure/rack, -/obj/item/stack/sheet/glass/fifty, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"mJ" = ( -/obj/item/harpoon, -/obj/item/harpoon, -/obj/item/harpoon, -/obj/structure/closet/crate, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"mP" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"mQ" = ( -/obj/effect/decal/cleanable/blood, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"mV" = ( -/obj/structure/clockwork/decorative/obelisk, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"mY" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/structure/flora/ausbushes/leafybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"na" = ( -/obj/structure/closet/secure_closet/freezer/meat/open, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"nb" = ( -/obj/structure/table/reinforced, -/obj/machinery/microwave, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"nc" = ( -/obj/structure/bed, -/obj/item/bedsheet/red, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"nl" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"no" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"nD" = ( -/obj/structure/table/wood, -/obj/item/id_decal/silver, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"nE" = ( -/obj/item/clockwork/alloy_shards, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"nF" = ( -/mob/living/simple_animal/hostile/skeleton{ - harm_intent_damage = 10; - health = 100; - name = "The Captain" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"nJ" = ( -/obj/structure/shuttle/engine/propulsion{ - dir = 8 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"nL" = ( -/obj/structure/table/wood, -/obj/item/storage/box/PDAs, -/obj/structure/sign/poster/official/pda_ad{ - pixel_x = -32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"nT" = ( -/turf/unsimulated/beach/water/drop, -/area/awaymission/beach/offshore) -"nU" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/floor/abductor, -/area/awaymission/beach/offshore) -"nV" = ( -/obj/structure/flora/ausbushes/grassybush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"nY" = ( -/obj/item/storage/firstaid/adv, -/obj/structure/table/wood, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"nZ" = ( -/obj/structure/bed, -/obj/item/bedsheet/cult, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"oe" = ( -/obj/structure/table/wood, -/obj/machinery/microwave, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"og" = ( -/turf/unsimulated/beach/water/deep/rock_wall{ - icon = 'icons/turf/walls/shuttle_wall.dmi'; - icon_state = "map-shuttle"; - name = "Shuttle Wall" - }, -/area/awaymission/undersea) -"oi" = ( -/obj/structure/table/reinforced, -/obj/item/paper{ - info = "Man NT falls for the simplest shit sometimes. All you need is an official looking paper with the right stamp and bam you've got redirrected shipments coming your way for pirating and agents with access they wouldn't have gotten otherwise."; - name = "Outpose commander's note" - }, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"ok" = ( -/turf/unsimulated/wall{ - icon = 'icons/turf/mining.dmi'; - icon_state = "rock" - }, -/area/awaymission/beach/offshore) -"op" = ( -/obj/structure/ladder/unbreakable/dive_point/buoy{ - id = "pirate" - }, -/turf/unsimulated/beach/water, -/area/awaymission/beach/offshore) -"ov" = ( -/obj/structure/rack, -/obj/item/stack/sheet/mineral/sandstone{ - amount = 20 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"oy" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"oz" = ( -/obj/effect/overlay/palmtree_r, -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"oB" = ( -/obj/structure/sink{ - pixel_y = 24 - }, -/obj/item/reagent_containers/glass/bucket, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"oC" = ( -/obj/structure/flora/ausbushes/reedbush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"oE" = ( -/obj/structure/table/reinforced, -/obj/item/storage/toolbox/syndicate, -/turf/unsimulated/floor{ - icon_state = "vault"; - dir = 8 - }, -/area/awaymission/beach/offshore) -"oG" = ( -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"oH" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"oL" = ( -/obj/machinery/gateway, -/turf/simulated/floor/carpet, -/area/awaymission/beach) -"oN" = ( -/obj/machinery/fishtank/wall, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"oP" = ( -/obj/structure/cult/altar, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"oU" = ( -/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"oY" = ( -/obj/item/clothing/shoes/sandal, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"pb" = ( -/obj/structure/table/reinforced, -/obj/item/flashlight/seclite, -/turf/unsimulated/floor{ - icon_state = "vault"; - dir = 8 - }, -/area/awaymission/beach/offshore) -"pd" = ( -/mob/living/simple_animal/hostile/pirate/ranged{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"pe" = ( -/obj/structure/closet/radiation, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"ph" = ( -/obj/structure/toilet/secret, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"pp" = ( -/obj/structure/table/wood, -/obj/effect/decal/cleanable/blood, -/obj/item/paper{ - info = "It was tricky shooting down this dodgy ship but this fancy metal will be worth a shit tone of credits on the black market. Damn tough to take apart though so I'm thinking we set up shop here and maybe make this into a hideout once we're done."; - name = "Captain's note" - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"pu" = ( -/mob/living/simple_animal/parrot, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"pw" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"pz" = ( -/obj/machinery/door/airlock/hatch/syndicate, -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"pD" = ( -/obj/machinery/door/unpowered/shuttle, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"pO" = ( -/obj/machinery/sleeper, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"pP" = ( -/obj/structure/table/wood, -/obj/item/pizzabox/hawaiian, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"qb" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"qf" = ( -/obj/machinery/door/airlock{ - name = "Private Restroom" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"qj" = ( -/obj/machinery/light{ - dir = 4; - icon_state = "tube1" - }, -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"ql" = ( -/obj/structure/chair/stool, -/mob/living/simple_animal/hostile/syndicate/melee/autogib, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"qo" = ( -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"qx" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"qK" = ( -/turf/unsimulated/wall/fakeglass{ - dir = 4; - icon_state = "fakewindows"; - opacity = 0 - }, -/area/awaymission/beach/offshore) -"qP" = ( -/obj/structure/flora/grass/brown, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"qR" = ( -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"qT" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"qU" = ( -/obj/structure/flora/ausbushes/fernybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"qX" = ( -/obj/structure/spawner/nether{ - max_mobs = 3; - name = "weak netherworld link" - }, -/obj/effect/rune, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"qY" = ( -/obj/structure/table/wood, -/obj/item/camera, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"rc" = ( -/obj/structure/table/wood, -/obj/item/lighter/zippo/gonzofist, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"rg" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/genericbush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"rk" = ( -/obj/structure/closet/cabinet, -/obj/item/clothing/shoes/black, -/obj/item/clothing/under/color/lightgreen, -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"rt" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"rv" = ( -/obj/effect/spawner/lootdrop/brass_temple_spawner, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"rx" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"rA" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"rD" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/mob/living/simple_animal/butterfly, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"rF" = ( -/mob/living/simple_animal/crab, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"rG" = ( -/obj/structure/sign/electricshock{ - pixel_y = 32 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"rH" = ( -/obj/machinery/juicer, -/obj/structure/table/wood, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"rS" = ( -/obj/structure/flora/ausbushes/ywflowers, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"rT" = ( -/obj/structure/table/wood, -/obj/item/hatchet, -/obj/item/cultivator, -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"rV" = ( -/obj/structure/sign/poster/official/pda_ad{ - pixel_x = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"rX" = ( -/mob/living/simple_animal/hostile/retaliate/carp/koi, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"sa" = ( -/obj/machinery/gateway{ - dir = 6 - }, -/turf/simulated/floor/carpet, -/area/awaymission/beach) -"sg" = ( -/obj/structure/flora/ausbushes/sunnybush, -/obj/structure/flora/ausbushes/sparsegrass, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"sj" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"sk" = ( -/turf/simulated/floor/light/colour_cycle/dancefloor_a, -/area/awaymission/beach) -"sp" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/bandana, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"sv" = ( -/obj/item/clockwork/alloy_shards/large, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"sx" = ( -/obj/structure/table/reinforced, -/obj/item/storage/firstaid/adv, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"sz" = ( -/obj/structure/sign/poster/contraband/hacking_guide{ - pixel_y = 32 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"sF" = ( -/obj/structure/cult/altar, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"sN" = ( -/obj/structure/closet/crate, -/obj/item/clothing/under/pirate, -/obj/item/clothing/under/pirate, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"sO" = ( -/obj/structure/rack, -/obj/item/stack/sheet/mineral/plasma{ - amount = 30 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"sV" = ( -/obj/machinery/door/airlock/public/glass{ - name = "Resort Bar" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"sZ" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"tc" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"te" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"ti" = ( -/turf/simulated/shuttle/wall{ - tag = "icon-swall8"; - icon_state = "swall8" - }, -/area/awaymission/beach/offshore) -"to" = ( -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"tp" = ( -/obj/structure/table/reinforced, -/obj/item/defibrillator/loaded, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"tr" = ( -/obj/structure/shuttle/engine/propulsion, -/turf/simulated/floor/plating/airless, -/area/awaymission/beach/offshore) -"tF" = ( -/obj/structure/table/wood, -/obj/item/reagent_containers/glass/beaker/large, -/obj/item/reagent_containers/glass/beaker/large, -/obj/structure/sign/poster/contraband/eat{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"tM" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"tN" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"tV" = ( -/turf/unsimulated/wall, -/area/awaymission/beach/offshore) -"uc" = ( -/obj/structure/mecha_wreckage/durand, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ue" = ( -/obj/structure/disposalpipe/segment, -/obj/machinery/vending/dinnerware, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"un" = ( -/obj/structure/table/reinforced, -/obj/item/reagent_containers/food/snacks/syndicake, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"us" = ( -/obj/structure/sink{ - pixel_y = 24 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"uz" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"uE" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"uN" = ( -/obj/structure/table/wood, -/obj/item/storage/toolbox/mechanical, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"uR" = ( -/obj/effect/overlay/palmtree_l, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"ve" = ( -/obj/structure/sink{ - dir = 4; - icon_state = "sink"; - pixel_x = 11; - pixel_y = 0 - }, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"vh" = ( -/mob/living/simple_animal/hostile/syndicate/ranged{ - loot = list() - }, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"vk" = ( -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"vm" = ( -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"vn" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"vq" = ( -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/item/coin/gold, -/obj/structure/closet/crate, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"vr" = ( -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"vA" = ( -/obj/item/hatchet, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"vM" = ( -/obj/structure/rack/skeletal_bar/left, -/obj/machinery/chem_dispenser/beer, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"vN" = ( -/obj/item/stack/sheet/mineral/bananium, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"vO" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/collectable/pirate, -/obj/item/stack/sheet/mineral/diamond{ - amount = 2 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"vR" = ( -/obj/effect/decal/cleanable/blood, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"vY" = ( -/obj/structure/shuttle/engine/propulsion{ - icon_state = "propulsion_r" - }, -/turf/simulated/floor/plating/airless, -/area/awaymission/beach/offshore) -"vZ" = ( -/obj/structure/disposalpipe/junction{ - dir = 1; - icon_state = "pipe-j1"; - tag = "icon-pipe-j1 (EAST)" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"wb" = ( -/obj/structure/bed, -/obj/item/bedsheet/medical, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"wc" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"wd" = ( -/obj/machinery/vending/hydronutrients{ - extended_inventory = 1 - }, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"wf" = ( -/obj/structure/sign/poster/official/space_cops{ - pixel_y = 32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"wi" = ( -/obj/item/toy/pet_rock/roxie, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"wl" = ( -/obj/item/clothing/under/pinkhawaiianshirt, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"wr" = ( -/obj/structure/closet/crate/can, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"wu" = ( -/obj/structure/ladder/unbreakable{ - desc = "An extremely sturdy metal ladder. What's it doing out here?"; - icon_state = "ladder01"; - id = "volcanobaseladder"; - name = "mysterious ladder" - }, -/turf/unsimulated/floor{ - icon = 'icons/turf/floors/plating.dmi'; - icon_state = "basalt0"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"ww" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"wx" = ( -/obj/structure/bed, -/obj/item/bedsheet/black, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"wJ" = ( -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"wO" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"wP" = ( -/obj/item/clothing/under/librarian, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"wQ" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/lavendergrass, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"wR" = ( -/obj/structure/flora/ausbushes/reedbush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"wS" = ( -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"wW" = ( -/obj/structure/table/wood, -/obj/item/laser_pointer, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"xb" = ( -/obj/item/stack/sheet/wood, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"xe" = ( -/obj/structure/bed, -/obj/item/bedsheet/syndie, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"xm" = ( -/obj/effect/decal/cleanable/blood, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"xv" = ( -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"xx" = ( -/obj/structure/flora/rock/pile, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"xD" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"xG" = ( -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"xH" = ( -/obj/structure/sign/restroom{ - pixel_x = -32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"xI" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"xN" = ( -/mob/living/simple_animal/crab/evil{ - faction = list("nether") - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"xQ" = ( -/obj/structure/barricade/wooden, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"xR" = ( -/obj/structure/table/abductor, -/turf/unsimulated/floor/abductor, -/area/awaymission/beach/offshore) -"xT" = ( -/obj/machinery/seed_extractor, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"xX" = ( -/obj/structure/sign/poster/official/ue_no{ - pixel_y = 32 - }, -/obj/structure/rack, -/obj/item/toy/katana, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"xZ" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/fernybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"ym" = ( -/obj/structure/flora/ausbushes/genericbush, -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"yq" = ( -/obj/structure/flora/bush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"yr" = ( -/obj/item/clothing/shoes/brown, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"yz" = ( -/obj/item/flag/syndi, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"yB" = ( -/obj/effect/baseturf_helper/beach/roughsand, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"yD" = ( -/obj/structure/closet/crate, -/obj/item/gun/energy/laser/retro, -/obj/item/gun/energy/laser/retro, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"yF" = ( -/obj/structure/rack, -/obj/item/stack/marker_beacon/ten, -/obj/item/stack/marker_beacon/ten, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"yG" = ( -/obj/structure/flora/ausbushes/palebush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"yH" = ( -/obj/structure/table/wood, -/obj/item/storage/box/monkeycubes/neaeracubes, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"yJ" = ( -/obj/structure/flora/grass/brown, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"yS" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"yT" = ( -/obj/structure/reagent_dispensers/watertank/high, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"yU" = ( -/obj/structure/flora/rock, -/turf/unsimulated/beach/water/dense, -/area/awaymission/beach) -"yY" = ( -/obj/structure/closet/crate, -/obj/item/coin/antagtoken/syndicate, -/obj/item/clothing/mask/fawkes, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"zg" = ( -/obj/structure/window/reinforced, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"zh" = ( -/obj/structure/sign/greencross{ - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"zi" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_l, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"zk" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"zn" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"zr" = ( -/obj/structure/flora/bush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"zw" = ( -/obj/structure/flora/grass/green, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"zx" = ( -/obj/machinery/conveyor_switch/oneway{ - id = "beach disposal" - }, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"zB" = ( -/obj/item/clothing/under/rank/nursesuit, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"zC" = ( -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"zL" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"zQ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/obj/structure/table/wood, -/obj/structure/reagent_dispensers/beerkeg, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"zR" = ( -/obj/machinery/door/airlock/hatch/syndicate{ - desc = "There's a note on the door. It says 'All intruders WILL BE SHOT!'." - }, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"zX" = ( -/obj/effect/baseturf_helper/beach/roughsand, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"zZ" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Aa" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"Am" = ( -/obj/structure/closet/crate/secure/loot, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"Ao" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/obj/machinery/vending/cola, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Ap" = ( -/obj/item/clothing/under/color/lightgreen, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"AD" = ( -/obj/machinery/vending/boozeomat{ - emagged = 1 - }, -/obj/structure/sign/poster/official/nanomichi_ad{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"AI" = ( -/obj/effect/overlay/palmtree_r, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"AM" = ( -/turf/unsimulated/beach/water/deep, -/area/awaymission/beach/offshore) -"AP" = ( -/obj/structure/mecha_wreckage/ripley, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"AQ" = ( -/obj/structure/sign/greencross{ - pixel_y = -32 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"AS" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"AW" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"AX" = ( -/obj/structure/curtain/open, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Bc" = ( -/obj/structure/bed/wooden_lounge_chair, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Bi" = ( -/turf/unsimulated/floor{ - icon = 'icons/turf/floors/plating.dmi'; - icon_state = "basalt0"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Bm" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"Bn" = ( -/obj/structure/closet/crate, -/obj/item/stack/sheet/mineral/abductor{ - amount = 25 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Bq" = ( -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Bt" = ( -/mob/living/simple_animal/hostile/retaliate/carp/koi/honk, -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"Bv" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"By" = ( -/obj/structure/flora/ausbushes/sunnybush, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"Bz" = ( -/obj/structure/chair/sofa/left{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"BA" = ( -/obj/structure/chair/office/light, -/mob/living/simple_animal/hostile/pirate/ranged{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"BF" = ( -/obj/structure/closet/crate/internals, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"BH" = ( -/obj/item/clothing/under/color/grey, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"BJ" = ( -/obj/machinery/gateway{ - dir = 10 - }, -/turf/simulated/floor/carpet, -/area/awaymission/beach) -"BM" = ( -/obj/structure/flora/grass/brown, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"BN" = ( -/obj/structure/table/reinforced, -/obj/item/storage/box/donkpockets, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"BR" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"BT" = ( -/obj/structure/closet/crate, -/obj/item/stack/sheet/mineral/gold{ - amount = 10 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"BU" = ( -/obj/structure/flora/rock/pile, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"BY" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Cf" = ( -/obj/structure/closet/abductor, -/turf/unsimulated/floor/abductor, -/area/awaymission/beach/offshore) -"Ch" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Ci" = ( -/obj/structure/bed, -/obj/item/bedsheet/medical, -/obj/item/clothing/under/color/purple, -/obj/item/clothing/shoes/black, -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Cj" = ( -/obj/structure/table/wood, -/obj/item/melee/chainofcommand, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Cm" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Co" = ( -/obj/structure/cult/pylon, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "wood-broken" - }, -/area/awaymission/undersea) -"Cr" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Cv" = ( -/obj/structure/shuttle/engine/heater, -/turf/simulated/floor/plating/airless, -/area/awaymission/beach/offshore) -"Cw" = ( -/turf/unsimulated/beach/coastline{ - dir = 1; - icon_state = "beachcorner" - }, -/area/awaymission/beach/offshore) -"Cz" = ( -/mob/living/simple_animal/hostile/skeleton, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"CA" = ( -/obj/item/clothing/shoes/black, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"CB" = ( -/obj/structure/closet/secure_closet/freezer/fridge/open, -/obj/structure/sign/botany{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"CC" = ( -/obj/machinery/gateway{ - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"CE" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/machinery/vending/snack{ - extended_inventory = 1 - }, -/obj/structure/sign/poster/contraband/donut_corp{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"CF" = ( -/obj/machinery/door/airlock/multi_tile/glass{ - name = "Dance Club" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"CG" = ( -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"CJ" = ( -/obj/item/beach_ball, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"CM" = ( -/turf/unsimulated/beach/water, -/area/awaymission/beach/offshore) -"Db" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/fernybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Dd" = ( -/obj/structure/sign/poster/official/ue_no{ - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"Dm" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/machinery/vending/coffee, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Ds" = ( -/obj/structure/mineral_door/wood{ - icon_state = "wood"; - name = "Captain's Quarters"; - tag = "icon-wood" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"DD" = ( -/obj/structure/table/wood, -/obj/item/soap/deluxe, -/obj/structure/sign/poster/official/ue_no{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"DG" = ( -/obj/item/clockwork/alloy_shards/pinion_lock, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"DP" = ( -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"DR" = ( -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"DV" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"Ed" = ( -/obj/item/shard, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"Ej" = ( -/obj/structure/flora/ausbushes/reedbush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"Ep" = ( -/obj/structure/sign/poster/official/safety_report{ - pixel_y = 32 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Eq" = ( -/obj/machinery/recycler, -/obj/machinery/conveyor/east{ - id = "beach disposal" - }, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"Et" = ( -/obj/structure/table/reinforced, -/obj/item/storage/box/syndie_kit/safecracking, -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"Eu" = ( -/obj/structure/table/wood, -/obj/item/storage/box/characters, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Ez" = ( -/obj/structure/table/wood, -/obj/item/storage/firstaid/o2, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"EB" = ( -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 8 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"EE" = ( -/obj/structure/table/wood, -/obj/item/storage/bag/trash, -/obj/item/storage/bag/trash, -/obj/item/storage/bag/trash, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"EH" = ( -/obj/item/clothing/shoes/winterboots, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"EM" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"ER" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"ET" = ( -/obj/structure/mineral_door/wood{ - icon_state = "wood"; - name = "Scuba Shack"; - tag = "icon-wood" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"EX" = ( -/obj/machinery/light{ - icon_state = "tube1"; - dir = 8 - }, -/obj/structure/chair/comfy/shuttle{ - dir = 4 - }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"EZ" = ( -/obj/structure/flora/grass/green, -/obj/structure/flora/ausbushes/leafybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Fa" = ( -/obj/item/chair/wood/wings, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Fb" = ( -/obj/structure/chair/stool, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Fd" = ( -/obj/machinery/door/poddoor/brass{ - id_tag = "brass temple" - }, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Fi" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"Fl" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Fp" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/obj/item/twohanded/required/kirbyplants, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Fs" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Fv" = ( -/obj/machinery/door/airlock{ - name = "Private Recharge room" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Fx" = ( -/obj/structure/table/wood, -/obj/item/toy/figure/ninja, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Fy" = ( -/obj/machinery/vending/cigarette{ - extended_inventory = 1 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"FB" = ( -/obj/structure/flora/rock, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"FF" = ( -/obj/structure/flora/ausbushes/sunnybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"FM" = ( -/obj/machinery/vending/hydroseeds{ - extended_inventory = 1 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"FN" = ( -/obj/structure/chair/comfy/shuttle, -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"FT" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Gd" = ( -/obj/structure/chair/sofa{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Gj" = ( -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Gm" = ( -/turf/simulated/floor/light/colour_cycle/dancefloor_b, -/area/awaymission/beach) -"Go" = ( -/obj/machinery/power/port_gen/pacman, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"Gu" = ( -/obj/machinery/gateway{ - dir = 5 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Gz" = ( -/obj/effect/waterfall{ - dir = 1; - water_frequency = 192 - }, -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"GK" = ( -/obj/structure/table/wood, -/obj/item/sensor_device, -/obj/item/pinpointer/crew, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"GR" = ( -/obj/machinery/recharge_station, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"GW" = ( -/obj/machinery/biogenerator, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"GZ" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 8 - }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"Hb" = ( -/obj/vehicle/ambulance, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Hf" = ( -/obj/structure/flora/grass/green, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Hm" = ( -/obj/structure/table/reinforced, -/obj/item/stamp/chameleon, -/obj/structure/sign/nuke{ - pixel_y = 32 - }, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"Hp" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Hr" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"Hy" = ( -/obj/item/clothing/shoes/white, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"HC" = ( -/obj/structure/table/wood, -/obj/item/reagent_containers/spray/plantbgone, -/obj/item/reagent_containers/spray/pestspray, -/obj/item/reagent_containers/glass/bucket, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"HI" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"HJ" = ( -/obj/structure/cult/pylon, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"HN" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone10"; - icon_state = "sandstone10" - }, -/area/awaymission/beach/offshore) -"HQ" = ( -/obj/machinery/gateway{ - dir = 8 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"HY" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f6"; - icon_state = "swall_f6" - }, -/area/awaymission/beach/offshore) -"Ia" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Id" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_l, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Ih" = ( -/obj/structure/window/reinforced, -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Il" = ( -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"Im" = ( -/obj/structure/flora/ausbushes/leafybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"In" = ( -/obj/structure/fermenting_barrel, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Io" = ( -/obj/item/clothing/under/color/lightred, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Ip" = ( -/obj/structure/flora/ausbushes/sunnybush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"It" = ( -/mob/living/simple_animal/hostile/syndicate/melee/autogib, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"Iu" = ( -/obj/effect/decal/snow/sand/surround{ - tag = "icon-gravsnow_surround (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_surround"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Iw" = ( -/mob/living/simple_animal/crab/Coffee, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Ix" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"IA" = ( -/obj/machinery/hydroponics/constructable, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"ID" = ( -/mob/living/simple_animal/crab, -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"IG" = ( -/obj/structure/closet, -/obj/item/clothing/under/syndicate/tacticool, -/obj/item/clothing/under/syndicate/tacticool, -/obj/item/clothing/under/syndicate/tacticool, -/obj/item/clothing/gloves/color/black, -/obj/item/clothing/gloves/color/black, -/obj/item/clothing/gloves/color/black, -/turf/unsimulated/floor{ - icon_state = "vault"; - dir = 8 - }, -/area/awaymission/beach/offshore) -"IN" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"IO" = ( -/obj/structure/flora/ausbushes/genericbush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"IZ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Ja" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/obj/structure/closet/crate/can, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Jb" = ( -/obj/structure/disposaloutlet{ - dir = 4 - }, -/obj/structure/disposalpipe/trunk, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"Jc" = ( -/obj/machinery/door_control/brass/beach_brass_temple_switch{ - pixel_x = 32 - }, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Jd" = ( -/mob/living/simple_animal/hostile/pirate{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate) - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Jf" = ( -/obj/machinery/door/airlock/hatch/syndicate, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"Jj" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone12"; - icon_state = "sandstone12" - }, -/area/awaymission/beach/offshore) -"Jl" = ( -/obj/structure/table/wood, -/obj/item/clothing/glasses/sunglasses, -/obj/machinery/vending/wallmed{ - extended_inventory = 1; - pixel_x = 0; - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Jr" = ( -/obj/structure/ladder/unbreakable{ - height = 1; - icon_state = "ladder10"; - id = "volcanobaseladder" - }, -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"Jv" = ( -/obj/structure/closet/crate/can, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"JB" = ( -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/structure/closet/crate, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/item/clothing/shoes/sandal, -/obj/structure/sign/poster/contraband/d_day_promo{ - pixel_x = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"JE" = ( -/obj/structure/chair/stool, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"JF" = ( -/obj/structure/flora/ausbushes/sunnybush, -/obj/structure/flora/ausbushes/genericbush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"JH" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/beret, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"JJ" = ( -/obj/item/toy/pet_rock/fred, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"JK" = ( -/obj/machinery/door/airlock/hatch/syndicate, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"JM" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 6 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"JQ" = ( -/obj/structure/sign/poster/contraband/missing_gloves{ - pixel_y = 32 - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"JZ" = ( -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Ka" = ( -/obj/structure/flora/ausbushes/ppflowers, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Kb" = ( -/obj/machinery/door/poddoor/brass/beach_brass_temple, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Ke" = ( -/obj/structure/table/reinforced, -/obj/item/storage/fancy/cigarettes/cigpack_syndicate, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"Kj" = ( -/obj/effect/spawner/window/reinforced, -/turf/simulated/floor, -/area/awaymission/beach) -"Kk" = ( -/obj/machinery/conveyor/east{ - id = "beach disposal" - }, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"Kr" = ( -/obj/structure/closet/toolcloset, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"Ks" = ( -/obj/structure/table/wood, -/obj/item/gun/energy/gun/mini, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Kt" = ( -/obj/structure/flora/ausbushes/ppflowers, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"KL" = ( -/obj/item/clothing/under/rank/chaplain, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"KM" = ( -/turf/simulated/floor/plasteel{ - dir = 2; - icon_state = "ramptop"; - tag = "icon-stage_stairs" - }, -/area/awaymission/beach) -"KS" = ( -/obj/effect/overlay/palmtree_r, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"KU" = ( -/obj/structure/bed, -/obj/item/bedsheet/red, -/obj/structure/sign/poster/contraband/energy_swords{ - pixel_y = -32 - }, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"Lj" = ( -/obj/structure/rack, -/obj/item/pickaxe/drill, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"Lk" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Lp" = ( -/obj/structure/chair{ - dir = 1 - }, -/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"Lq" = ( -/obj/structure/flora/grass/brown, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Lu" = ( -/obj/structure/table/reinforced/brass, -/obj/item/stack/tile/brass/fifty, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Lv" = ( -/obj/item/shard, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Lz" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"LB" = ( -/obj/item/stack/sheet/wood, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"LE" = ( -/obj/structure/rack, -/obj/item/melee/classic_baton, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"LJ" = ( -/turf/simulated/floor/beach/roughcoastline, -/area/awaymission/beach) -"LK" = ( -/obj/structure/flora/grass/green, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"LL" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone6"; - icon_state = "sandstone6" - }, -/area/awaymission/beach/offshore) -"LM" = ( -/obj/item/shovel, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"LV" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/overlay/coconut, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"LY" = ( -/obj/effect/overlay/palmtree_r, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"LZ" = ( -/obj/machinery/poolcontroller/invisible, -/turf/unsimulated/beach/water/deep, -/area/awaymission/beach) -"Mb" = ( -/turf/unsimulated/beach/water/deep/rock_wall{ - icon = 'icons/turf/walls/shuttle_wall.dmi'; - icon_state = "map-shuttle"; - name = "Shuttle Wall" - }, -/area/shuttle/transport) -"Mq" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"Mw" = ( -/obj/structure/dresser, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Mx" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"My" = ( -/obj/effect/decal/snow/sand/surround{ - name = "rough sand" - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"MB" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/leafybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"MG" = ( -/obj/structure/window/reinforced{ - dir = 1 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"ML" = ( -/obj/structure/flora/ausbushes/leafybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"MO" = ( -/obj/item/clothing/shoes/jackboots, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"MY" = ( -/obj/structure/table/wood, -/obj/item/clothing/accessory/armband, -/obj/item/clothing/accessory/red, -/obj/structure/sign/poster/official/the_owl{ - pixel_x = -32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Nb" = ( -/obj/effect/overlay/palmtree_l, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Ne" = ( -/obj/structure/flora/ausbushes/fernybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"Nn" = ( -/obj/structure/flora/ausbushes/reedbush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Nq" = ( -/obj/structure/closet/crate/internals, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/item/clothing/mask/breath, -/obj/structure/sign/poster/official/safety_internals{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Nr" = ( -/turf/unsimulated/beach/coastline, -/area/awaymission/beach/offshore) -"Ns" = ( -/mob/living/simple_animal/hostile/pirate{ - loot = list(/obj/effect/mob_spawn/human/corpse/pirate) - }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"NE" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/helmet/justice, -/obj/structure/sign/poster/contraband/fun_police{ - pixel_y = 32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"NF" = ( -/obj/structure/chair/stool, -/obj/item/instrument/piano_synth, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"NG" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"NJ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/obj/structure/flora/ausbushes/sunnybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"NP" = ( -/obj/structure/flora/rock, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"NQ" = ( -/obj/structure/bed, -/obj/item/bedsheet/red, -/obj/structure/sign/poster/contraband/lusty_xenomorph{ - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"NR" = ( -/turf/unsimulated/wall/fakeglass{ - dir = 10; - icon_state = "fakewindows" - }, -/area/awaymission/beach/offshore) -"NT" = ( -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"NV" = ( -/obj/structure/shuttle/engine/heater{ - dir = 8 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"NZ" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Ob" = ( -/obj/structure/flora/rock, -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"Oc" = ( -/obj/structure/table/reinforced, -/obj/item/paper_bin, -/obj/structure/sign/poster/contraband/c20r{ - pixel_y = 32 - }, -/obj/item/pen/multi, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"Oi" = ( -/obj/structure/flora/rock, -/turf/unsimulated/floor{ - icon = 'icons/turf/floors/plating.dmi'; - icon_state = "basalt0"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Ol" = ( -/obj/structure/chair/sofa/right{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Op" = ( -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Or" = ( -/obj/structure/table/wood, -/obj/item/cultivator, -/obj/item/seeds/cotton, -/obj/item/seeds/cotton, -/obj/item/seeds/cotton, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Ou" = ( -/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Oz" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"OD" = ( -/obj/structure/window/reinforced{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"OF" = ( -/obj/structure/flora/ausbushes/ppflowers, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"OG" = ( -/obj/structure/closet, -/obj/item/clothing/shoes/chameleon/noslip, -/obj/item/clothing/under/chameleon, -/obj/item/clothing/mask/chameleon, -/turf/simulated/floor/plating, -/area/awaymission/beach/offshore) -"OJ" = ( -/obj/structure/mineral_door/wood{ - tag = "icon-wood"; - icon_state = "wood" - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"ON" = ( -/obj/structure/table/wood, -/obj/item/harpoon, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"OR" = ( -/obj/structure/flora/ausbushes/grassybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"OT" = ( -/obj/machinery/disposal, -/obj/structure/disposalpipe/trunk{ - dir = 1 - }, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"OY" = ( -/obj/structure/flora/ausbushes/reedbush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Pb" = ( -/obj/structure/chair/stool, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Pc" = ( -/obj/effect/mob_spawn/human/resort_host, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"Pm" = ( -/obj/structure/chair/office/light, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Po" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Pp" = ( -/obj/effect/overlay/palmtree_r, -/obj/structure/flora/ausbushes/sunnybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Pu" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Pz" = ( -/obj/machinery/vending/clothing, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"PE" = ( -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"PH" = ( -/obj/structure/flora/grass/green, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"PL" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"PR" = ( -/turf/unsimulated/wall{ - icon_state = "rock"; - name = "rock" - }, -/area/space) -"PW" = ( -/turf/simulated/shuttle/wall{ - tag = "icon-swall3"; - icon_state = "swall3" - }, -/area/awaymission/beach/offshore) -"Qf" = ( -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"Qh" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"Qm" = ( -/obj/structure/chair/beachchair{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Qu" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/stalkybush, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"Qv" = ( -/obj/item/key/ambulance, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"QI" = ( -/turf/unsimulated/wall/fakeglass{ - icon_state = "fakewindows2"; - dir = 1 - }, -/area/awaymission/undersea) -"QJ" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"QM" = ( -/mob/living/simple_animal/hostile/carp/megacarp{ - name = "Mega Sea Carp" - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"QN" = ( -/obj/effect/decal/snow/sand/surround{ - name = "rough sand" - }, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"QR" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"QW" = ( -/obj/structure/chair/beachchair/red{ - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"QY" = ( -/obj/machinery/door_control/brass/beach_brass_temple_switch{ - pixel_y = 32 - }, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"QZ" = ( -/obj/structure/rack, -/obj/item/restraints/handcuffs/cable/zipties, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Rj" = ( -/obj/structure/table/wood, -/obj/item/storage/bag/plants, -/obj/item/clothing/gloves/botanic_leather, -/obj/item/shovel/spade, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"Rk" = ( -/obj/structure/table/wood, -/obj/machinery/pos, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Rl" = ( -/mob/living/simple_animal/hostile/deathsquid, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Ru" = ( -/obj/structure/table/reinforced, -/obj/item/storage/secure/briefcase/syndie, -/turf/unsimulated/floor{ - tag = "icon-dark"; - icon_state = "dark" - }, -/area/awaymission/beach/offshore) -"RG" = ( -/obj/structure/chair/beachchair/red, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"RL" = ( -/obj/structure/chair/wood/wings{ - tag = "icon-wooden_chair_wings (EAST)"; - icon_state = "wooden_chair_wings"; - dir = 4 - }, -/obj/structure/sign/poster/official/cleanliness{ - pixel_x = -32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"RN" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "wood-broken" - }, -/area/awaymission/undersea) -"RQ" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f9"; - icon_state = "swall_f9" - }, -/area/awaymission/beach/offshore) -"RU" = ( -/turf/unsimulated/beach/water/deep/rock_wall{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - name = "Wood Wall" - }, -/area/awaymission/undersea) -"RV" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"RY" = ( -/obj/structure/sign/poster/contraband/syndicate_recruitment{ - pixel_x = 0; - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"Sb" = ( -/obj/structure/closet/crate, -/obj/item/toy/syndicateballoon, -/obj/item/toy/figure/syndie, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"Sc" = ( -/turf/unsimulated/floor/abductor, -/area/awaymission/beach/offshore) -"Se" = ( -/obj/item/clothing/head/nursehat, -/obj/structure/sign/poster/official/healthy{ - pixel_x = 32 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"So" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/overlay/coconut, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Sp" = ( -/obj/structure/mineral_door/sandstone, -/turf/space, -/area/awaymission/undersea) -"Ss" = ( -/obj/structure/sign/poster/official/cohiba_robusto_ad{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Su" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Sw" = ( -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"Sy" = ( -/obj/structure/mecha_wreckage/ripley/firefighter, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Sz" = ( -/obj/structure/flora/ausbushes/reedbush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHEAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 5 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"SD" = ( -/obj/structure/ladder/unbreakable/dive_point/buoy{ - id = "volcano_island" - }, -/turf/unsimulated/beach/water/drop, -/area/awaymission/beach/offshore) -"SL" = ( -/obj/structure/sign/poster/contraband/lusty_xenomorph{ - pixel_x = 32; - pixel_y = 0 - }, -/turf/simulated/floor/light/colour_cycle/dancefloor_a, -/area/awaymission/beach) -"SO" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 9 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"SS" = ( -/mob/living/simple_animal/hostile/retaliate/carp/koi/honk, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"ST" = ( -/obj/structure/flora/rock/pile, -/obj/structure/flora/ausbushes/genericbush, -/obj/structure/flora/ausbushes/sparsegrass, -/turf/unsimulated/floor/grass, -/area/awaymission/beach/offshore) -"SW" = ( -/obj/structure/table/wood, -/obj/machinery/pos, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"SX" = ( -/obj/machinery/vending/coffee/free, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"Td" = ( -/obj/structure/closet/cabinet, -/obj/item/reagent_containers/food/drinks/bottle/rum, -/obj/item/reagent_containers/food/drinks/bottle/rum, -/obj/item/reagent_containers/food/drinks/bottle/rum, -/obj/item/reagent_containers/food/drinks/drinkingglass, -/obj/item/reagent_containers/food/drinks/drinkingglass, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"TF" = ( -/obj/structure/mirror{ - pixel_y = 32 - }, -/obj/structure/sink{ - pixel_y = 16 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"TG" = ( -/obj/structure/dresser, -/obj/item/storage/wallet/random, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"TI" = ( -/obj/structure/closet/crate/secure/loot, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"TL" = ( -/obj/structure/falsewall/brass, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"TP" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Ul" = ( -/obj/structure/flora/ausbushes/sparsegrass, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Uv" = ( -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "wood-broken" - }, -/area/awaymission/undersea) -"Uy" = ( -/mob/living/simple_animal/hostile/carp/megacarp{ - name = "Mega Sea Carp" - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"UK" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"UL" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f10"; - icon_state = "swall_f10" - }, -/area/awaymission/beach/offshore) -"UM" = ( -/obj/structure/chair/stool, -/mob/living/simple_animal/hostile/syndicate/ranged{ - loot = list() - }, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"UN" = ( -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"UP" = ( -/obj/structure/rack/skeletal_bar/right, -/obj/machinery/chem_dispenser/soda, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"UQ" = ( -/obj/structure/sign/directions/medical{ - dir = 1; - pixel_y = 32 - }, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"UR" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"UU" = ( -/obj/structure/closet/crate, -/obj/item/stack/sheet/mineral/uranium{ - amount = 20 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Va" = ( -/obj/structure/grille, -/obj/structure/shuttle/window, -/turf/simulated/floor/plating/airless, -/area/awaymission/beach/offshore) -"Vh" = ( -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Vp" = ( -/obj/effect/decal/cleanable/blood, -/obj/structure/chair/wood/wings{ - tag = "icon-wooden_chair_wings (EAST)"; - icon_state = "wooden_chair_wings"; - dir = 4 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Vq" = ( -/obj/structure/flora/rock, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Vw" = ( -/obj/structure/closet/secure_closet/personal, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"VE" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"VO" = ( -/obj/structure/ladder/unbreakable/dive_point/buoy{ - id = "brass temple" - }, -/turf/unsimulated/beach/water/drop, -/area/awaymission/beach/offshore) -"VP" = ( -/obj/item/ship_in_a_bottle, -/obj/structure/table/wood, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"VR" = ( -/obj/structure/flora/bush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"We" = ( -/obj/structure/flora/ausbushes/palebush, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"Wk" = ( -/obj/structure/bed/amb_trolley, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Wm" = ( -/obj/structure/mecha_wreckage/odysseus, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Wq" = ( -/obj/structure/chair/comfy/shuttle{ - dir = 1 - }, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"Wr" = ( -/obj/effect/overlay/palmtree_l, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (WEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 8 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Wy" = ( -/obj/machinery/gateway{ - dir = 4 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"WC" = ( -/obj/machinery/bodyscanner{ - dir = 4 - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"WK" = ( -/obj/structure/table/wood, -/obj/item/clothing/head/chefhat, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"WL" = ( -/obj/structure/chair/comfy/black{ - dir = 1 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"WN" = ( -/obj/structure/closet/crate/can, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"WQ" = ( -/obj/structure/flora/ausbushes/leafybush, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"WS" = ( -/obj/structure/flora/ausbushes/lavendergrass, -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"WV" = ( -/obj/machinery/seed_extractor, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"Xa" = ( -/obj/effect/decal/remains/human{ - plane = -1 - }, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Xi" = ( -/obj/machinery/door/airlock/hatch/syndicate{ - name = "Bunk House" - }, -/turf/unsimulated/floor{ - icon_state = "bar"; - dir = 2 - }, -/area/awaymission/beach/offshore) -"Xr" = ( -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"Xz" = ( -/obj/item/clothing/under/color/lightred, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"XF" = ( -/obj/structure/table/wood, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"XG" = ( -/obj/structure/flora/rock, -/obj/structure/flora/ausbushes/leafybush, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"XH" = ( -/obj/structure/piano, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"XL" = ( -/obj/effect/overlay/palmtree_r, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"XN" = ( -/obj/structure/table/wood, -/obj/item/storage/firstaid/adv{ - pixel_x = 8; - pixel_y = 8 - }, -/obj/item/storage/firstaid/toxin{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/storage/firstaid/o2, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"XU" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (EAST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 4 - }, -/obj/effect/overlay/palmtree_r, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"XZ" = ( -/obj/structure/chair/stool, -/obj/structure/mirror{ - pixel_y = 32 - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Yc" = ( -/obj/structure/table/wood, -/obj/item/lighter/zippo, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Yi" = ( -/turf/unsimulated/wall/fakeglass{ - icon_state = "fakewindows2"; - dir = 1 - }, -/area/awaymission/beach/offshore) -"Yj" = ( -/obj/structure/closet/cabinet, -/obj/item/clothing/head/pirate, -/obj/item/clothing/suit/pirate_black, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Ym" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (SOUTHWEST)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 10 - }, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Yo" = ( -/obj/structure/shuttle/engine/heater{ - dir = 1 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "floor3" - }, -/area/awaymission/undersea) -"Ys" = ( -/obj/effect/overlay/palmtree_l, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"Yt" = ( -/obj/machinery/hydroponics/soil, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Yv" = ( -/obj/structure/table/reinforced, -/obj/item/storage/backpack/duffel/syndie/med, -/turf/unsimulated/floor{ - icon_state = "white" - }, -/area/awaymission/beach/offshore) -"YB" = ( -/mob/living/simple_animal/crab, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"YF" = ( -/obj/item/clothing/shoes/brown, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"YG" = ( -/turf/unsimulated/beach/sand/dense, -/area/awaymission/beach/offshore) -"YJ" = ( -/obj/structure/table/wood, -/obj/item/stack/spacecash/c100, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"YK" = ( -/obj/structure/flora/ausbushes/genericbush, -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) -"YM" = ( -/obj/machinery/telecomms/relay/preset/beachresort, -/obj/item/multitool, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) -"YO" = ( -/turf/unsimulated/wall/fakeglass{ - icon_state = "fakewindows2"; - dir = 8 - }, -/area/awaymission/beach/offshore) -"YR" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone1"; - icon_state = "sandstone1" - }, -/area/awaymission/beach/offshore) -"YS" = ( -/turf/unsimulated/beach/sand, -/turf/simulated/shuttle/wall{ - tag = "icon-swall_f5"; - icon_state = "swall_f5" - }, -/area/awaymission/beach/offshore) -"YY" = ( -/obj/machinery/vending/autodrobe, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"Zb" = ( -/obj/structure/flora/rock/pile, -/obj/effect/overlay/palmtree_r, -/turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) -"Zg" = ( -/obj/structure/closet/crate, -/obj/item/toy/plushie/nukeplushie, -/obj/item/toy/nuke, -/turf/unsimulated/floor{ - icon_state = "grimy" - }, -/area/awaymission/beach/offshore) -"Zj" = ( -/mob/living/simple_animal/hostile/viscerator, -/turf/unsimulated/floor{ - icon_state = "plastitanium" - }, -/area/awaymission/beach/offshore) -"Zl" = ( -/obj/machinery/door_control/brass/beach_brass_temple_switch{ - pixel_x = -32 - }, -/turf/unsimulated/floor{ - desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; - icon = 'icons/obj/clockwork_objects.dmi'; - icon_state = "clockwork_floor"; - tag = "icon-wood" - }, -/area/awaymission/beach/offshore) -"Zo" = ( -/turf/unsimulated/wall{ - tag = "icon-sandstone3"; - icon_state = "sandstone3" - }, -/area/awaymission/beach/offshore) -"Zp" = ( -/obj/structure/closet/crate{ - name = "Ripley parts crate" - }, -/obj/item/mecha_parts/chassis/ripley, -/obj/item/mecha_parts/part/ripley_left_arm, -/obj/item/mecha_parts/part/ripley_left_leg, -/obj/item/mecha_parts/part/ripley_right_arm, -/obj/item/mecha_parts/part/ripley_right_leg, -/obj/item/mecha_parts/part/ripley_torso, -/obj/item/circuitboard/mecha/ripley/main, -/obj/item/circuitboard/mecha/ripley/peripherals, -/obj/item/mecha_parts/mecha_equipment/drill, -/obj/item/mecha_parts/mecha_equipment/cable_layer, -/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp, -/obj/item/mecha_parts/mecha_equipment/rcd, -/obj/item/stack/sheet/plasteel{ - amount = 20 - }, -/turf/unsimulated/beach/water/deep/wood_floor{ - icon_state = "titanium_blue" - }, -/area/awaymission/undersea) -"Zr" = ( -/obj/effect/rune, -/obj/structure/spawner/nether{ - max_mobs = 3; - name = "weak netherworld link" - }, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) -"Zt" = ( -/obj/item/clothing/under/rainbow, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) -"Zu" = ( -/obj/structure/disposalpipe/segment, -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"Zy" = ( -/obj/structure/table/reinforced, -/obj/item/storage/belt/military/traitor, -/turf/unsimulated/floor{ - icon_state = "vault"; - dir = 8 - }, -/area/awaymission/beach/offshore) -"ZC" = ( -/obj/structure/table/wood, -/obj/item/paicard, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"ZD" = ( -/obj/structure/closet/crate, -/obj/item/stack/marker_beacon/thirty, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"ZE" = ( -/obj/effect/decal/snow/sand/edge{ - tag = "icon-gravsnow_corner (NORTH)"; - name = "rough sand"; - icon_state = "gravsnow_corner"; - dir = 1 - }, -/obj/structure/chair/stool/bar, -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) -"ZG" = ( -/obj/structure/closet/crate, -/obj/item/clothing/suit/pirate_brown, -/turf/unsimulated/beach/sand, -/area/awaymission/beach/offshore) -"ZN" = ( -/obj/machinery/door/airlock{ - name = "Resort Backroom" - }, -/turf/simulated/floor/wood, -/area/awaymission/beach) -"ZU" = ( -/obj/machinery/computer, -/turf/simulated/shuttle/floor, -/area/awaymission/beach/offshore) -"ZW" = ( -/obj/structure/flora/rock/pile, -/turf/unsimulated/beach/water, -/area/awaymission/beach) -"ZZ" = ( -/obj/item/clothing/shoes/sandal, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) +"aa" = (/turf/space,/area/space) +"ab" = (/turf/unsimulated/beach/water/deep,/area/awaymission/undersea) +"ac" = (/obj/machinery/poolcontroller/invisible/sea,/turf/unsimulated/beach/water/deep,/area/awaymission/undersea) +"ad" = (/turf/unsimulated/beach/water/deep/rock_wall,/area/awaymission/undersea) +"ae" = (/obj/structure/ladder/unbreakable/dive_point/anchor{id = "volcano_island"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) +"af" = (/obj/structure/constructshell,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ag" = (/obj/structure/chair/wood/wings,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ah" = (/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ai" = (/obj/structure/cult/pylon,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aj" = (/obj/structure/cult/altar,/obj/item/veilrender/crabrender,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ak" = (/obj/structure/cult/altar,/obj/item/book/codex_gigas,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"al" = (/obj/structure/curtain/black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"am" = (/obj/structure/cult/forge,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"an" = (/obj/structure/flora/rock/pile,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"ao" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"ap" = (/obj/structure/chair/stool,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aq" = (/mob/living/simple_animal/hostile/retaliate/carp,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"ar" = (/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"as" = (/obj/structure/boulder,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"at" = (/obj/item/flag/cult,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"au" = (/obj/structure/mineral_door/sandstone,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"av" = (/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"aw" = (/obj/item/toy/eight_ball/conch,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ax" = (/obj/structure/reagent_dispensers/beerkeg,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ay" = (/obj/structure/closet/cabinet,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"az" = (/obj/structure/rack/skeletal_bar/left,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aA" = (/obj/structure/rack/skeletal_bar/right,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aB" = (/obj/structure/rack/skeletal_bar,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aC" = (/obj/structure/mineral_door/wood{tag = "icon-wood";icon_state = "wood"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aD" = (/obj/structure/chair/comfy/teal{dir = 4},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aE" = (/obj/structure/table/wood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aF" = (/obj/structure/chair/comfy/teal{dir = 8},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aG" = (/obj/structure/toilet,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aH" = (/obj/machinery/disco/immobile,/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) +"aI" = (/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (EAST)";icon_state = "wooden_chair_wings";dir = 4},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aJ" = (/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (WEST)";icon_state = "wooden_chair_wings";dir = 8},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aK" = (/obj/structure/sign/poster/official/nanomichi_ad{pixel_y = 32},/turf/simulated/floor/light/colour_cycle/dancefloor_b,/area/awaymission/beach) +"aL" = (/obj/effect/mob_spawn/human/resort_host{dir = 4},/turf/simulated/floor/plasteel,/area/awaymission/beach/entrance) +"aM" = (/obj/structure/sink{dir = 4;icon_state = "sink";pixel_x = 11;pixel_y = 0},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aN" = (/obj/structure/sink{icon_state = "sink";dir = 8;pixel_x = -12;pixel_y = 2},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aO" = (/obj/structure/closet/crate/can,/obj/item/storage/bag/trash,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"aP" = (/obj/item/flag/species/skrell,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"aQ" = (/obj/structure/closet/gmcloset{icon_closed = "black";icon_state = "black";name = "formal wardrobe"},/turf/simulated/floor/plasteel,/area/awaymission/beach) +"aR" = (/obj/structure/mirror{icon_state = "mirror_broke";pixel_y = 28},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aS" = (/obj/structure/bed,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aT" = (/obj/structure/dresser,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aU" = (/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) +"aV" = (/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"aW" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/machinery/atm{name = "Automatic teller machine";pixel_y = 32},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"aX" = (/obj/item/stack/spacecash/c500,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"aY" = (/obj/structure/mineral_door/wood{tag = "icon-wood";icon_state = "wood"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"aZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"ba" = (/obj/machinery/vending/boozeomat{emagged = 1},/obj/structure/sign/poster/official/high_class_martini{pixel_y = 32},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"bb" = (/obj/structure/closet/secure_closet/freezer/fridge/open,/turf/simulated/floor/wood,/area/awaymission/beach) +"bc" = (/obj/machinery/door/airlock/glass{name = "Resort Backroom";req_access_txt = "25"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bd" = (/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) +"be" = (/obj/structure/table/wood,/obj/machinery/vending/wallmed{extended_inventory = 1;pixel_x = 0;pixel_y = 32},/obj/item/storage/firstaid/adv{pixel_x = 8;pixel_y = 8},/obj/item/storage/firstaid/toxin{pixel_x = 4;pixel_y = 4},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"bf" = (/obj/structure/sign/barsign{pixel_y = 32},/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bg" = (/obj/item/clockwork/alloy_shards/clockgolem_remains,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"bh" = (/obj/structure/sign/botany{pixel_y = 32},/obj/structure/table/wood,/obj/item/reagent_containers/food/drinks/shaker,/obj/item/clothing/glasses/sunglasses,/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"bi" = (/obj/structure/sink{pixel_y = 24},/obj/structure/window/reinforced{dir = 8},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"bj" = (/obj/structure/closet/secure_closet/freezer/meat/open,/obj/structure/disposalpipe/junction{dir = 1;icon_state = "pipe-j2";tag = "icon-pipe-j2"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bk" = (/obj/structure/disposalpipe/segment,/obj/machinery/vending/dinnerware,/obj/structure/window/reinforced{dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) +"bl" = (/obj/structure/closet/secure_closet/freezer/kitchen{locked = 0;req_access = null},/turf/simulated/floor/wood,/area/awaymission/beach) +"bm" = (/obj/structure/table/wood,/obj/item/soap/deluxe,/turf/simulated/floor/wood,/area/awaymission/beach) +"bn" = (/obj/machinery/door/window/eastleft{name = "Resort Bar";req_access_txt = "25"},/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/wood,/area/awaymission/beach) +"bo" = (/obj/structure/chair/stool/bar,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"bp" = (/obj/structure/table/wood,/obj/machinery/chem_dispenser/soda,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) +"bq" = (/obj/structure/table/wood,/obj/machinery/chem_dispenser/beer,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/wood,/area/awaymission/beach) +"br" = (/obj/structure/table/reinforced,/obj/structure/sign/poster/contraband/c20r{pixel_y = 32},/obj/item/storage/secure/briefcase/syndie,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"bs" = (/obj/structure/table/wood,/obj/item/lighter/zippo,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bt" = (/obj/structure/table/wood,/obj/item/paicard,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bu" = (/obj/structure/table/wood,/obj/item/paper_bin,/obj/item/pen/multi,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bv" = (/obj/structure/table/wood,/obj/item/camera,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bw" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/wood,/area/awaymission/beach) +"bx" = (/obj/structure/clockwork/decorative/tinkerers_cache,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"by" = (/obj/machinery/cooker/foodgrill,/turf/simulated/floor/wood,/area/awaymission/beach) +"bz" = (/obj/structure/table/wood,/obj/machinery/reagentgrinder,/turf/simulated/floor/wood,/area/awaymission/beach) +"bA" = (/obj/structure/table/wood,/obj/item/pizzabox/hawaiian,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bB" = (/mob/living/simple_animal/butterfly,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"bC" = (/obj/structure/table/wood,/obj/machinery/pos,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"bD" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"bE" = (/obj/structure/table/reinforced,/obj/item/paper_bin,/obj/item/pen/multi,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"bF" = (/obj/structure/table/reinforced,/obj/structure/sign/nuke{pixel_y = 32},/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"bG" = (/obj/structure/safe,/obj/item/documents/syndicate/yellow,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"bH" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"bI" = (/obj/machinery/sleeper/syndie,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"bJ" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/wood,/area/awaymission/beach) +"bK" = (/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"bL" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"bM" = (/obj/structure/chair/stool,/turf/simulated/floor/wood,/area/awaymission/beach) +"bN" = (/obj/machinery/vending/cigarette{extended_inventory = 1},/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/sign/poster/contraband/smoke{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"bO" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"bP" = (/obj/structure/table/reinforced,/obj/item/clothing/accessory/stethoscope,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"bQ" = (/obj/structure/table/wood,/obj/item/storage/firstaid/adv,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"bR" = (/obj/item/flag/syndi,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"bS" = (/obj/machinery/newscaster{pixel_y = -32},/turf/simulated/floor/wood,/area/awaymission/beach) +"bT" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"bU" = (/obj/machinery/newscaster{pixel_x = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"bV" = (/obj/structure/closet/crate,/obj/item/harpoon,/obj/item/harpoon,/obj/item/harpoon,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"bW" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/extinguisher_cabinet{pixel_y = 32},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"bX" = (/obj/machinery/cooking/oven,/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"bY" = (/obj/machinery/tcomms/relay/beachresort,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"bZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/wood,/area/awaymission/beach) +"ca" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/simulated/floor/wood,/area/awaymission/beach) +"cb" = (/obj/item/toy/figure/ninja,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"cc" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/stalkybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"cd" = (/obj/structure/bed,/obj/item/bedsheet/red,/obj/structure/sign/poster/official/ue_no{pixel_y = 32},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"ce" = (/obj/item/clothing/suit/hastur,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"cf" = (/obj/structure/toilet/secret,/obj/item/bikehorn/rubberducky,/turf/simulated/floor/wood,/area/awaymission/beach) +"cg" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"ch" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel{dir = 10;icon_state = "green"},/area/awaymission/beach) +"ci" = (/obj/structure/flora/ausbushes/genericbush,/obj/structure/flora/ausbushes/sparsegrass,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"cj" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel{dir = 0;icon_state = "green"},/area/awaymission/beach) +"ck" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel{icon_state = "green";dir = 6},/area/awaymission/beach) +"cl" = (/obj/structure/closet/athletic_mixed,/turf/simulated/floor/wood,/area/awaymission/beach) +"cm" = (/obj/machinery/vending/hydroseeds{extended_inventory = 1},/turf/simulated/floor/plasteel{icon_state = "caution";dir = 4},/area/awaymission/beach) +"cn" = (/obj/structure/disposaloutlet{dir = 4},/obj/structure/disposalpipe/trunk,/turf/simulated/floor/plasteel{icon_state = "bot"},/area/awaymission/beach) +"co" = (/turf/simulated/floor/plasteel{dir = 8;icon_state = "neutralfull"},/area/awaymission/beach) +"cp" = (/obj/structure/disposalpipe/segment,/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel{dir = 1;icon_state = "caution"},/area/awaymission/beach) +"cq" = (/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel{dir = 1;icon_state = "caution"},/area/awaymission/beach) +"cr" = (/obj/machinery/conveyor_switch/oneway{id = "beach disposal"},/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel{dir = 1;icon_state = "caution"},/area/awaymission/beach) +"cs" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water,/area/awaymission/beach) +"ct" = (/obj/machinery/gateway{dir = 10},/turf/simulated/floor/carpet,/area/awaymission/beach/entrance) +"cu" = (/obj/machinery/gateway,/turf/simulated/floor/carpet,/area/awaymission/beach/entrance) +"cv" = (/obj/machinery/gateway{dir = 6},/obj/effect/baseturf_helper/beach/roughsand,/turf/simulated/floor/carpet,/area/awaymission/beach/entrance) +"cw" = (/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/entrance) +"cx" = (/obj/structure/ladder/unbreakable/dive_point/anchor,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) +"cy" = (/obj/structure/ladder/unbreakable/dive_point/anchor{id = "pirate"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) +"cz" = (/turf/unsimulated/beach/water/drop,/area/awaymission/beach/entrance) +"cA" = (/obj/structure/ladder/unbreakable/dive_point/buoy,/turf/unsimulated/beach/water/deep,/area/awaymission/beach/entrance) +"cB" = (/turf/unsimulated/beach/water/deep,/area/awaymission/beach/entrance) +"cC" = (/turf/unsimulated/beach/water/deep/dense,/area/awaymission/beach/entrance) +"cD" = (/obj/structure/table/wood,/obj/effect/decal/cleanable/blood,/obj/item/paper{info = "It was tricky shooting down this dodgy ship but this fancy metal will be worth a shit tone of credits on the black market. I can't help but wonder what damaged it in the first place though, even wounded the ship was agile. Anyways, this metal is damn tough to take apart so I'm thinking we set up shop here and maybe make this into a hideout once we're done.";name = "Captain's note"},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"cH" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_y = -32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"cJ" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"cL" = (/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"cZ" = (/obj/structure/bonfire/dense,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"db" = (/obj/structure/chair/stool,/obj/item/instrument/guitar,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"dd" = (/obj/structure/dispenser/oxygen,/obj/structure/sign/poster/official/air1{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"de" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/structure/sign/poster/official/safety_internals{pixel_x = -32;pixel_y = 0},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"dg" = (/obj/structure/chair/beachchair,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"dh" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"dj" = (/turf/simulated/floor/beach/roughcoastline/dense,/area/awaymission/beach) +"dk" = (/obj/structure/flora/rock,/turf/simulated/floor/beach/roughcoastline/dense,/area/awaymission/beach) +"dr" = (/turf/unsimulated/beach/water,/area/awaymission/beach) +"ds" = (/turf/unsimulated/beach/water/dense,/area/awaymission/beach) +"dt" = (/turf/unsimulated/beach/water/drop,/area/awaymission/beach) +"du" = (/turf/unsimulated/beach/water/deep,/area/awaymission/beach) +"dw" = (/turf/unsimulated/beach/water/deep/dense,/area/awaymission/beach) +"dx" = (/turf/unsimulated/beach/water/drop/dense,/area/awaymission/beach) +"dP" = (/mob/living/simple_animal/butterfly,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"em" = (/obj/structure/closet/crate,/obj/item/flashlight,/obj/item/flashlight,/obj/item/flashlight,/obj/item/flashlight,/obj/item/flashlight,/turf/simulated/floor/wood,/area/awaymission/beach) +"eo" = (/obj/effect/waterfall{dir = 1;water_frequency = 75},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"er" = (/obj/effect/waterfall{dir = 1;water_frequency = 104},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"ew" = (/obj/effect/waterfall{dir = 1;water_frequency = 44},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"ey" = (/obj/effect/waterfall{dir = 1;water_frequency = 97},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"eB" = (/obj/effect/waterfall{dir = 1;water_frequency = 94},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"eC" = (/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"eM" = (/obj/item/shard,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"eN" = (/obj/structure/flora/grass/green,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"eS" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/palebush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"fa" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"fc" = (/obj/machinery/biogenerator,/obj/item/reagent_containers/glass/bucket,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"fd" = (/obj/structure/clockwork/decorative/relay,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"fj" = (/turf/unsimulated/wall{desc = "A sturdy wall of brass. It feels strangely warm and mechanical sounds can be heard from within.";icon = 'icons/turf/walls/clockwork_wall.dmi';icon_state = "clockwork_wall";name = "clockwork wall"},/area/awaymission/beach/offshore) +"fl" = (/obj/machinery/door_control/brass{id = "brass temple";pixel_y = 32},/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) +"fm" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"fA" = (/obj/structure/flora/rock,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"fE" = (/obj/machinery/vending/syndisnack,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"fJ" = (/obj/structure/table/reinforced/brass,/obj/item/clockwork/weapon/ratvarian_spear,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"fN" = (/obj/structure/flora/ausbushes/genericbush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"fR" = (/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"fV" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"gn" = (/obj/item/clockwork/component/geis_capacitor/fallen_armor,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"gp" = (/obj/structure/sign/poster/official/ue_no{pixel_y = 32},/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) +"gq" = (/obj/machinery/vending/crittercare,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"gB" = (/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) +"gD" = (/obj/structure/table/wood,/obj/item/clothing/head/welding,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"gJ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"gL" = (/obj/structure/table/reinforced,/obj/item/soap/syndie,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"gT" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"gW" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"hm" = (/obj/structure/rack,/obj/item/stack/sheet/plasteel{amount = 20},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"hn" = (/obj/effect/baseturf_helper/beach/dense_roughsand,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"hp" = (/obj/item/clothing/gloves/botanic_leather,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"hv" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"hB" = (/obj/structure/closet,/obj/item/clothing/under/pinkhawaiianshirt,/obj/item/clothing/under/pinkhawaiianshirt,/obj/item/clothing/under/orangehawaiianshirt,/obj/item/clothing/under/orangehawaiianshirt,/obj/item/clothing/under/redhawaiianshirt,/obj/item/clothing/under/redhawaiianshirt,/obj/item/clothing/under/bluehawaiianshirt,/obj/item/clothing/under/bluehawaiianshirt,/turf/simulated/floor/wood,/area/awaymission/beach) +"hD" = (/obj/structure/shuttle/engine/propulsion{dir = 1},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"hF" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"hH" = (/obj/structure/table/wood,/obj/item/storage/bag/plants,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"hI" = (/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"hP" = (/obj/structure/flora/ausbushes/palebush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"hR" = (/obj/structure/table/wood,/obj/item/toy/figure/wizard,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"hT" = (/obj/item/stack/sheet/wood,/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"hY" = (/obj/structure/bed,/obj/item/bedsheet/medical,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"if" = (/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"im" = (/obj/machinery/fishtank/tank,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"is" = (/obj/structure/table/wood,/obj/item/clothing/suit/pirate_black,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"iu" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"ix" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"iC" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"iF" = (/obj/effect/decal/remains/human{plane = -1},/obj/item/melee/cultblade,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"iI" = (/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"iK" = (/obj/structure/chair/beachchair/red,/obj/item/toy/plushie/octopus,/obj/item/clothing/glasses/sunglasses{pixel_x = 2;pixel_y = 6},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"iL" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) +"iT" = (/obj/machinery/photocopier,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"iU" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"iX" = (/obj/structure/table/wood,/obj/item/flashlight,/obj/item/stack/marker_beacon/thirty,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"jd" = (/obj/item/clothing/under/color/brown,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"ji" = (/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"jr" = (/obj/effect/decal/cleanable/blood,/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"jA" = (/obj/machinery/vending/crittercare/free,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"jE" = (/obj/structure/table/wood,/obj/item/reagent_containers/food/snacks/pie,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"jL" = (/turf/unsimulated/floor/lava,/area/awaymission/beach/offshore) +"jN" = (/obj/machinery/gateway{dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"jX" = (/obj/structure/sign/poster/official/love_ian{pixel_x = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"jY" = (/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"ka" = (/mob/living/simple_animal/hostile/retaliate/carp/koi,/turf/unsimulated/beach/water,/area/awaymission/beach) +"kd" = (/obj/structure/chair/stool,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"kf" = (/obj/machinery/smartfridge,/turf/simulated/floor/wood,/area/awaymission/beach) +"kg" = (/obj/item/clothing/shoes/black,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"km" = (/turf/unsimulated/wall{icon_state = "rock";name = "rock"},/area/awaymission/beach/offshore) +"kq" = (/obj/structure/flora/grass/green,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"ky" = (/obj/structure/table/reinforced/brass,/obj/item/storage/toolbox/brass/prefilled,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"kz" = (/obj/structure/closet/crate,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"kE" = (/turf/unsimulated/wall{tag = "icon-sandstone0";icon_state = "sandstone0"},/area/awaymission/beach/offshore) +"kH" = (/obj/structure/flora/rock,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"kO" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"kP" = (/obj/structure/grille/broken,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"kQ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"kT" = (/obj/item/shovel/safety,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"kU" = (/obj/machinery/gateway/centeraway,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"la" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"le" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"lg" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water,/area/awaymission/beach/offshore) +"lo" = (/turf/simulated/floor/wood,/area/awaymission/beach) +"lD" = (/obj/structure/sign/poster/contraband/lusty_xenomorph{pixel_y = 32},/obj/structure/closet/cabinet,/obj/item/clothing/suit/leathercoat,/obj/item/clothing/head/fedora,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"lH" = (/obj/structure/shuttle/engine/propulsion{icon_state = "propulsion_l"},/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) +"lJ" = (/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"lQ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) +"lS" = (/turf/unsimulated/wall{icon = 'icons/turf/walls/abductor_wall.dmi';icon_state = "abductor"},/area/awaymission/beach/offshore) +"lX" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"lZ" = (/obj/item/instrument/bikehorn,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"mc" = (/obj/structure/ladder/unbreakable/dive_point/anchor{id = "brass temple"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"ml" = (/obj/item/gun/projectile/automatic/pistol/enforcer,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) +"mw" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"mA" = (/obj/structure/table/wood,/obj/structure/sign/poster/contraband/have_a_puff{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"mI" = (/obj/structure/rack,/obj/item/stack/sheet/glass/fifty,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"mP" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"mQ" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"mV" = (/obj/structure/clockwork/decorative/obelisk,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"mY" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"nb" = (/obj/structure/table/reinforced,/obj/machinery/microwave,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"nc" = (/obj/structure/bed,/obj/item/bedsheet/red,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"nl" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"no" = (/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"nD" = (/obj/structure/table/wood,/obj/item/id_decal/silver,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"nE" = (/obj/item/clockwork/alloy_shards,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"nF" = (/mob/living/simple_animal/hostile/skeleton{harm_intent_damage = 10;health = 100;name = "The Captain"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"nJ" = (/obj/structure/shuttle/engine/propulsion{dir = 8},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"nL" = (/obj/structure/table/wood,/obj/item/storage/box/PDAs,/obj/structure/sign/poster/official/pda_ad{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"nT" = (/turf/unsimulated/beach/water/drop,/area/awaymission/beach/offshore) +"nU" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) +"nV" = (/obj/structure/flora/ausbushes/grassybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"nZ" = (/obj/structure/bed,/obj/item/bedsheet/cult,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"oe" = (/obj/structure/table/wood,/obj/machinery/microwave,/turf/simulated/floor/wood,/area/awaymission/beach) +"og" = (/turf/unsimulated/beach/water/deep/rock_wall{icon = 'icons/turf/walls/shuttle_wall.dmi';icon_state = "map-shuttle";name = "Shuttle Wall"},/area/awaymission/undersea) +"ok" = (/turf/unsimulated/wall{icon = 'icons/turf/mining.dmi';icon_state = "rock"},/area/awaymission/beach/offshore) +"op" = (/obj/structure/ladder/unbreakable/dive_point/buoy{id = "pirate"},/turf/unsimulated/beach/water,/area/awaymission/beach/offshore) +"ov" = (/obj/structure/rack,/obj/item/stack/sheet/mineral/sandstone{amount = 20},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"oy" = (/obj/structure/chair/comfy/shuttle{dir = 4},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"oz" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"oB" = (/obj/structure/sink{pixel_y = 24},/obj/item/reagent_containers/glass/bucket,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"oC" = (/obj/structure/flora/ausbushes/reedbush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"oE" = (/obj/structure/table/reinforced,/obj/item/storage/toolbox/syndicate,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) +"oG" = (/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"oH" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"oN" = (/obj/machinery/fishtank/wall,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"oP" = (/obj/structure/cult/altar,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"oU" = (/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"oY" = (/obj/item/clothing/shoes/sandal,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"pb" = (/obj/structure/table/reinforced,/obj/item/flashlight/seclite,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) +"pd" = (/mob/living/simple_animal/hostile/pirate/ranged{loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged)},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"pe" = (/obj/structure/closet/radiation,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"pu" = (/mob/living/simple_animal/parrot,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"pw" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) +"pD" = (/obj/machinery/door/unpowered/shuttle,/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"qb" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"qf" = (/obj/machinery/door/airlock{name = "Private Restroom"},/turf/simulated/floor/wood,/area/awaymission/beach) +"qj" = (/obj/machinery/light{dir = 4;icon_state = "tube1"},/obj/structure/chair/comfy/shuttle{dir = 8},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"ql" = (/obj/structure/chair/stool,/mob/living/simple_animal/hostile/syndicate/melee/autogib,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"qo" = (/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"qx" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"qK" = (/turf/unsimulated/wall/fakeglass{dir = 4;icon_state = "fakewindows";opacity = 0},/area/awaymission/beach/offshore) +"qP" = (/obj/structure/flora/grass/brown,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"qR" = (/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"qT" = (/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"qU" = (/obj/structure/flora/ausbushes/fernybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"qX" = (/obj/structure/spawner/nether{max_mobs = 3;name = "weak netherworld link"},/obj/effect/rune,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"rc" = (/obj/structure/table/wood,/obj/item/lighter/zippo/gonzofist,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"rg" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/genericbush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"rk" = (/obj/structure/closet/cabinet,/obj/item/clothing/shoes/black,/obj/item/clothing/under/color/lightgreen,/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"rt" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"rv" = (/obj/effect/spawner/lootdrop/brass_temple_spawner,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"rx" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"rA" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"rD" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/mob/living/simple_animal/butterfly,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"rF" = (/mob/living/simple_animal/crab,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"rG" = (/obj/structure/sign/electricshock{pixel_y = 32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"rH" = (/obj/machinery/juicer,/obj/structure/table/wood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"rS" = (/obj/structure/flora/ausbushes/ywflowers,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"rT" = (/obj/structure/table/wood,/obj/item/hatchet,/obj/item/cultivator,/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"rV" = (/obj/structure/sign/poster/official/pda_ad{pixel_x = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"rX" = (/mob/living/simple_animal/hostile/retaliate/carp/koi,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"sg" = (/obj/structure/flora/ausbushes/sunnybush,/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"sj" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"sk" = (/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) +"sp" = (/obj/structure/table/wood,/obj/item/clothing/head/bandana,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"sv" = (/obj/item/clockwork/alloy_shards/large,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"sx" = (/obj/structure/table/reinforced,/obj/item/storage/firstaid/adv,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"sz" = (/obj/structure/sign/poster/contraband/hacking_guide{pixel_y = 32},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"sF" = (/obj/structure/cult/altar,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"sN" = (/obj/structure/closet/crate,/obj/item/clothing/under/pirate,/obj/item/clothing/under/pirate,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"sO" = (/obj/structure/rack,/obj/item/stack/sheet/mineral/plasma{amount = 30},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"sZ" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"tc" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"te" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"ti" = (/turf/simulated/shuttle/wall{tag = "icon-swall8";icon_state = "swall8"},/area/awaymission/beach/offshore) +"to" = (/obj/effect/overlay/palmtree_r,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"tp" = (/obj/structure/table/reinforced,/obj/item/defibrillator/loaded,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"tr" = (/obj/structure/shuttle/engine/propulsion,/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) +"tF" = (/obj/structure/table/wood,/obj/item/reagent_containers/glass/beaker/large,/obj/item/reagent_containers/glass/beaker/large,/obj/structure/sign/poster/contraband/eat{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"tM" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"tN" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"tV" = (/turf/unsimulated/wall,/area/awaymission/beach/offshore) +"uc" = (/obj/structure/mecha_wreckage/durand,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"us" = (/obj/structure/sink{pixel_y = 24},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"uz" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"uE" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"uN" = (/obj/structure/table/wood,/obj/item/storage/toolbox/mechanical,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"uR" = (/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"ve" = (/obj/structure/sink{dir = 4;icon_state = "sink";pixel_x = 11;pixel_y = 0},/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"vh" = (/mob/living/simple_animal/hostile/syndicate/ranged{loot = list()},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"vk" = (/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"vm" = (/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/wood,/area/awaymission/beach) +"vn" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"vq" = (/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/structure/closet/crate,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"vr" = (/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"vA" = (/obj/item/hatchet,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"vM" = (/obj/structure/rack/skeletal_bar/left,/obj/machinery/chem_dispenser/beer,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"vN" = (/obj/item/stack/sheet/mineral/bananium,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"vO" = (/obj/structure/table/wood,/obj/item/clothing/head/collectable/pirate,/obj/item/stack/sheet/mineral/diamond{amount = 2},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"vR" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"vY" = (/obj/structure/shuttle/engine/propulsion{icon_state = "propulsion_r"},/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) +"vZ" = (/obj/structure/disposalpipe/junction{dir = 1;icon_state = "pipe-j1";tag = "icon-pipe-j1 (EAST)"},/turf/simulated/floor/wood,/area/awaymission/beach) +"wb" = (/obj/structure/bed,/obj/item/bedsheet/medical,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"wc" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"wd" = (/obj/machinery/vending/hydronutrients{extended_inventory = 1},/turf/simulated/floor/plasteel,/area/awaymission/beach) +"wf" = (/obj/structure/sign/poster/official/space_cops{pixel_y = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"wi" = (/obj/item/toy/pet_rock/roxie,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"wl" = (/obj/item/clothing/under/pinkhawaiianshirt,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"wr" = (/obj/structure/closet/crate/can,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"wu" = (/obj/structure/ladder/unbreakable{desc = "An extremely sturdy metal ladder. What's it doing out here?";icon_state = "ladder01";id = "volcanobaseladder";name = "mysterious ladder"},/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) +"ww" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"wx" = (/obj/structure/bed,/obj/item/bedsheet/black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"wJ" = (/obj/effect/overlay/palmtree_l,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"wO" = (/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"wP" = (/obj/item/clothing/under/librarian,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"wQ" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/lavendergrass,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"wR" = (/obj/structure/flora/ausbushes/reedbush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"wS" = (/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"wW" = (/obj/structure/table/wood,/obj/item/laser_pointer,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"xb" = (/obj/item/stack/sheet/wood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"xe" = (/obj/structure/bed,/obj/item/bedsheet/syndie,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"xm" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"xv" = (/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"xx" = (/obj/structure/flora/rock/pile,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"xD" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"xG" = (/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"xH" = (/obj/structure/sign/restroom{pixel_x = -32},/turf/simulated/floor/wood,/area/awaymission/beach) +"xI" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"xN" = (/mob/living/simple_animal/crab/evil{faction = list("nether")},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"xQ" = (/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"xR" = (/obj/structure/table/abductor,/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) +"xT" = (/obj/machinery/seed_extractor,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"xX" = (/obj/structure/sign/poster/official/ue_no{pixel_y = 32},/obj/structure/rack,/obj/item/toy/katana,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"xZ" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/fernybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"ym" = (/obj/structure/flora/ausbushes/genericbush,/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"yq" = (/obj/structure/flora/bush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"yr" = (/obj/item/clothing/shoes/brown,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"yz" = (/obj/item/flag/syndi,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"yB" = (/obj/effect/baseturf_helper/beach/roughsand,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"yD" = (/obj/structure/closet/crate,/obj/item/gun/energy/laser/retro,/obj/item/gun/energy/laser/retro,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"yF" = (/obj/structure/rack,/obj/item/stack/marker_beacon/ten,/obj/item/stack/marker_beacon/ten,/turf/simulated/floor/wood,/area/awaymission/beach) +"yG" = (/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"yH" = (/obj/structure/table/wood,/obj/item/storage/box/monkeycubes/neaeracubes,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"yJ" = (/obj/structure/flora/grass/brown,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"yS" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/wood,/area/awaymission/beach) +"yT" = (/obj/structure/reagent_dispensers/watertank/high,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"yU" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water/dense,/area/awaymission/beach) +"yY" = (/obj/structure/closet/crate,/obj/item/coin/antagtoken/syndicate,/obj/item/clothing/mask/fawkes,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"zg" = (/obj/structure/window/reinforced,/turf/simulated/floor/wood,/area/awaymission/beach) +"zh" = (/obj/structure/sign/greencross{pixel_y = 32},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"zi" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"zk" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"zn" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"zr" = (/obj/structure/flora/bush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"zw" = (/obj/structure/flora/grass/green,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"zB" = (/obj/item/clothing/under/rank/nursesuit,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"zC" = (/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"zL" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"zQ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/structure/table/wood,/obj/structure/reagent_dispensers/beerkeg,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"zR" = (/obj/machinery/door/airlock/hatch/syndicate{desc = "There's a note on the door. It says 'All intruders WILL BE SHOT!'."},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"zX" = (/obj/effect/baseturf_helper/beach/roughsand,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"zZ" = (/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Aa" = (/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"Am" = (/obj/structure/closet/crate/secure/loot,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"Ao" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/obj/machinery/vending/cola,/turf/simulated/floor/wood,/area/awaymission/beach) +"Ap" = (/obj/item/clothing/under/color/lightgreen,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"AI" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"AM" = (/turf/unsimulated/beach/water/deep,/area/awaymission/beach/offshore) +"AP" = (/obj/structure/mecha_wreckage/ripley,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"AQ" = (/obj/structure/sign/greencross{pixel_y = -32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"AS" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"AW" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"AX" = (/obj/structure/curtain/open,/turf/simulated/floor/wood,/area/awaymission/beach) +"Bc" = (/obj/structure/bed/wooden_lounge_chair,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"Bi" = (/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Bm" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"Bn" = (/obj/structure/closet/crate,/obj/item/stack/sheet/mineral/abductor{amount = 25},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Bq" = (/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"Bt" = (/mob/living/simple_animal/hostile/retaliate/carp/koi/honk,/turf/unsimulated/beach/water,/area/awaymission/beach) +"Bv" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"By" = (/obj/structure/flora/ausbushes/sunnybush,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"Bz" = (/obj/structure/chair/sofa/left{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"BA" = (/obj/structure/chair/office/light,/mob/living/simple_animal/hostile/pirate/ranged{loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged)},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"BF" = (/obj/structure/closet/crate/internals,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/turf/simulated/floor/wood,/area/awaymission/beach) +"BH" = (/obj/item/clothing/under/color/grey,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"BM" = (/obj/structure/flora/grass/brown,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"BN" = (/obj/structure/table/reinforced,/obj/item/storage/box/donkpockets,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"BR" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/wood,/area/awaymission/beach) +"BT" = (/obj/structure/closet/crate,/obj/item/stack/sheet/mineral/gold{amount = 10},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"BU" = (/obj/structure/flora/rock/pile,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"BY" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Cf" = (/obj/structure/closet/abductor,/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) +"Ch" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Ci" = (/obj/structure/bed,/obj/item/bedsheet/medical,/obj/item/clothing/under/color/purple,/obj/item/clothing/shoes/black,/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Cj" = (/obj/structure/table/wood,/obj/item/melee/chainofcommand,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Cm" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Co" = (/obj/structure/cult/pylon,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) +"Cr" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Cv" = (/obj/structure/shuttle/engine/heater,/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) +"Cw" = (/turf/unsimulated/beach/coastline{dir = 1;icon_state = "beachcorner"},/area/awaymission/beach/offshore) +"Cz" = (/mob/living/simple_animal/hostile/skeleton,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"CA" = (/obj/item/clothing/shoes/black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"CC" = (/obj/machinery/gateway{dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"CE" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/machinery/vending/snack{extended_inventory = 1},/obj/structure/sign/poster/contraband/donut_corp{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"CF" = (/obj/machinery/door/airlock/multi_tile/glass{name = "Dance Club"},/turf/simulated/floor/wood,/area/awaymission/beach) +"CG" = (/turf/unsimulated/floor{icon_state = "plastitanium"},/area/awaymission/beach/offshore) +"CJ" = (/obj/item/beach_ball,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"CM" = (/turf/unsimulated/beach/water,/area/awaymission/beach/offshore) +"Db" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/fernybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Dm" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/machinery/vending/coffee,/turf/simulated/floor/wood,/area/awaymission/beach) +"Ds" = (/obj/structure/mineral_door/wood{icon_state = "wood";name = "Captain's Quarters";tag = "icon-wood"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"DG" = (/obj/item/clockwork/alloy_shards/pinion_lock,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"DP" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"DR" = (/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) +"DV" = (/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"Ed" = (/obj/item/shard,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"Ej" = (/obj/structure/flora/ausbushes/reedbush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"Ep" = (/obj/structure/sign/poster/official/safety_report{pixel_y = 32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Eq" = (/obj/machinery/recycler,/obj/machinery/conveyor/east{id = "beach disposal"},/turf/simulated/floor/plasteel,/area/awaymission/beach) +"Eu" = (/obj/structure/table/wood,/obj/item/storage/box/characters,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Ez" = (/obj/structure/table/wood,/obj/item/storage/firstaid/o2,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"EB" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) +"EE" = (/obj/structure/table/wood,/obj/item/storage/bag/trash,/obj/item/storage/bag/trash,/obj/item/storage/bag/trash,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"EH" = (/obj/item/clothing/shoes/winterboots,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"EM" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"ER" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"ET" = (/obj/structure/mineral_door/wood{icon_state = "wood";name = "Scuba Shack";tag = "icon-wood"},/turf/simulated/floor/wood,/area/awaymission/beach) +"EX" = (/obj/machinery/light{icon_state = "tube1";dir = 8},/obj/structure/chair/comfy/shuttle{dir = 4},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"EZ" = (/obj/structure/flora/grass/green,/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Fa" = (/obj/item/chair/wood/wings,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Fb" = (/obj/structure/chair/stool,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"Fd" = (/obj/machinery/door/poddoor/brass{id_tag = "brass temple"},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Fi" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"Fl" = (/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Fp" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/wood,/area/awaymission/beach) +"Fs" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Fv" = (/obj/machinery/door/airlock{name = "Private Recharge room"},/turf/simulated/floor/wood,/area/awaymission/beach) +"Fy" = (/obj/machinery/vending/cigarette{extended_inventory = 1},/turf/simulated/floor/wood,/area/awaymission/beach) +"FB" = (/obj/structure/flora/rock,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"FF" = (/obj/structure/flora/ausbushes/sunnybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"FM" = (/obj/machinery/vending/hydroseeds{extended_inventory = 1},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"FN" = (/obj/structure/chair/comfy/shuttle,/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"FT" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Gd" = (/obj/structure/chair/sofa{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"Gj" = (/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Gm" = (/turf/simulated/floor/light/colour_cycle/dancefloor_b,/area/awaymission/beach) +"Go" = (/obj/machinery/power/port_gen/pacman,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"Gu" = (/obj/machinery/gateway{dir = 5},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"Gz" = (/obj/effect/waterfall{dir = 1;water_frequency = 192},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"GR" = (/obj/machinery/recharge_station,/turf/simulated/floor/wood,/area/awaymission/beach) +"GW" = (/obj/machinery/biogenerator,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"GZ" = (/obj/structure/chair/comfy/shuttle{dir = 8},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"Hf" = (/obj/structure/flora/grass/green,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Hp" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Hr" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"Hy" = (/obj/item/clothing/shoes/white,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"HC" = (/obj/structure/table/wood,/obj/item/reagent_containers/spray/plantbgone,/obj/item/reagent_containers/spray/pestspray,/obj/item/reagent_containers/glass/bucket,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"HI" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/wood,/area/awaymission/beach) +"HJ" = (/obj/structure/cult/pylon,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"HN" = (/turf/unsimulated/wall{tag = "icon-sandstone10";icon_state = "sandstone10"},/area/awaymission/beach/offshore) +"HQ" = (/obj/machinery/gateway{dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"HY" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f6";icon_state = "swall_f6"},/area/awaymission/beach/offshore) +"Ia" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Id" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Ih" = (/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"Il" = (/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"Im" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"In" = (/obj/structure/fermenting_barrel,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Io" = (/obj/item/clothing/under/color/lightred,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Ip" = (/obj/structure/flora/ausbushes/sunnybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Iu" = (/obj/effect/decal/snow/sand/surround{tag = "icon-gravsnow_surround (NORTH)";name = "rough sand";icon_state = "gravsnow_surround";dir = 1},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Iw" = (/mob/living/simple_animal/crab/Coffee,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"Ix" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"ID" = (/mob/living/simple_animal/crab,/turf/unsimulated/beach/water,/area/awaymission/beach) +"IG" = (/obj/structure/closet,/obj/item/clothing/under/syndicate/tacticool,/obj/item/clothing/under/syndicate/tacticool,/obj/item/clothing/under/syndicate/tacticool,/obj/item/clothing/gloves/color/black,/obj/item/clothing/gloves/color/black,/obj/item/clothing/gloves/color/black,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) +"IN" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"IO" = (/obj/structure/flora/ausbushes/genericbush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"IZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Jc" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_x = 32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Jd" = (/mob/living/simple_animal/hostile/pirate{loot = list(/obj/effect/mob_spawn/human/corpse/pirate)},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Jf" = (/obj/machinery/door/airlock/hatch/syndicate,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) +"Jj" = (/turf/unsimulated/wall{tag = "icon-sandstone12";icon_state = "sandstone12"},/area/awaymission/beach/offshore) +"Jr" = (/obj/structure/ladder/unbreakable{height = 1;icon_state = "ladder10";id = "volcanobaseladder"},/turf/unsimulated/floor{icon_state = "plastitanium"},/area/awaymission/beach/offshore) +"Jv" = (/obj/structure/closet/crate/can,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"JB" = (/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/structure/closet/crate,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/structure/sign/poster/contraband/d_day_promo{pixel_x = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"JE" = (/obj/structure/chair/stool,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"JF" = (/obj/structure/flora/ausbushes/sunnybush,/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"JH" = (/obj/structure/table/wood,/obj/item/clothing/head/beret,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"JJ" = (/obj/item/toy/pet_rock/fred,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"JK" = (/obj/machinery/door/airlock/hatch/syndicate,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"JM" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"JQ" = (/obj/structure/sign/poster/contraband/missing_gloves{pixel_y = 32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"JZ" = (/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Ka" = (/obj/structure/flora/ausbushes/ppflowers,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Kb" = (/obj/machinery/door/poddoor/brass/beach_brass_temple,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Ke" = (/obj/structure/table/reinforced,/obj/item/storage/fancy/cigarettes/cigpack_syndicate,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"Kj" = (/obj/effect/spawner/window/reinforced,/turf/simulated/floor,/area/awaymission/beach) +"Kk" = (/obj/machinery/conveyor/east{id = "beach disposal"},/turf/simulated/floor/plasteel,/area/awaymission/beach) +"Kr" = (/obj/structure/closet/toolcloset,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"Ks" = (/obj/structure/table/wood,/obj/item/gun/energy/gun/mini,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Kt" = (/obj/structure/flora/ausbushes/ppflowers,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"KL" = (/obj/item/clothing/under/rank/chaplain,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"KM" = (/turf/simulated/floor/plasteel{dir = 2;icon_state = "ramptop";tag = "icon-stage_stairs"},/area/awaymission/beach) +"KS" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"KU" = (/obj/structure/bed,/obj/item/bedsheet/red,/obj/structure/sign/poster/contraband/energy_swords{pixel_y = -32},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"Lj" = (/obj/structure/rack,/obj/item/pickaxe/drill,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"Lk" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Lq" = (/obj/structure/flora/grass/brown,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Lu" = (/obj/structure/table/reinforced/brass,/obj/item/stack/tile/brass/fifty,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Lv" = (/obj/item/shard,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Lz" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"LB" = (/obj/item/stack/sheet/wood,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"LE" = (/obj/structure/rack,/obj/item/melee/classic_baton,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"LJ" = (/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) +"LK" = (/obj/structure/flora/grass/green,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"LL" = (/turf/unsimulated/wall{tag = "icon-sandstone6";icon_state = "sandstone6"},/area/awaymission/beach/offshore) +"LM" = (/obj/item/shovel,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"LV" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"LY" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"LZ" = (/obj/machinery/poolcontroller/invisible,/turf/unsimulated/beach/water/deep,/area/awaymission/beach) +"Mb" = (/turf/unsimulated/beach/water/deep/rock_wall{icon = 'icons/turf/walls/shuttle_wall.dmi';icon_state = "map-shuttle";name = "Shuttle Wall"},/area/shuttle/transport) +"Mq" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) +"Mw" = (/obj/structure/dresser,/turf/simulated/floor/wood,/area/awaymission/beach) +"Mx" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"My" = (/obj/effect/decal/snow/sand/surround{name = "rough sand"},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"MB" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"MG" = (/obj/structure/window/reinforced{dir = 1},/turf/simulated/floor/wood,/area/awaymission/beach) +"ML" = (/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"MO" = (/obj/item/clothing/shoes/jackboots,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"MY" = (/obj/structure/table/wood,/obj/item/clothing/accessory/armband,/obj/item/clothing/accessory/red,/obj/structure/sign/poster/official/the_owl{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Nb" = (/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Ne" = (/obj/structure/flora/ausbushes/fernybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"Nn" = (/obj/structure/flora/ausbushes/reedbush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Nq" = (/obj/structure/closet/crate/internals,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/structure/sign/poster/official/safety_internals{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"Nr" = (/turf/unsimulated/beach/coastline,/area/awaymission/beach/offshore) +"Ns" = (/mob/living/simple_animal/hostile/pirate{loot = list(/obj/effect/mob_spawn/human/corpse/pirate)},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"NE" = (/obj/structure/table/wood,/obj/item/clothing/head/helmet/justice,/obj/structure/sign/poster/contraband/fun_police{pixel_y = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"NF" = (/obj/structure/chair/stool,/obj/item/instrument/piano_synth,/turf/simulated/floor/wood,/area/awaymission/beach) +"NG" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"NJ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"NP" = (/obj/structure/flora/rock,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"NQ" = (/obj/structure/bed,/obj/item/bedsheet/red,/obj/structure/sign/poster/contraband/lusty_xenomorph{pixel_y = 32},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"NR" = (/turf/unsimulated/wall/fakeglass{dir = 10;icon_state = "fakewindows"},/area/awaymission/beach/offshore) +"NT" = (/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"NV" = (/obj/structure/shuttle/engine/heater{dir = 8},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"NZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Ob" = (/obj/structure/flora/rock,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"Oi" = (/obj/structure/flora/rock,/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Ol" = (/obj/structure/chair/sofa/right{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"Op" = (/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Or" = (/obj/structure/table/wood,/obj/item/cultivator,/obj/item/seeds/cotton,/obj/item/seeds/cotton,/obj/item/seeds/cotton,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Ou" = (/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Oz" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"OD" = (/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"OF" = (/obj/structure/flora/ausbushes/ppflowers,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"OJ" = (/obj/structure/mineral_door/wood{tag = "icon-wood";icon_state = "wood"},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"ON" = (/obj/structure/table/wood,/obj/item/harpoon,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"OR" = (/obj/structure/flora/ausbushes/grassybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"OT" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 1},/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/wood,/area/awaymission/beach) +"OY" = (/obj/structure/flora/ausbushes/reedbush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Pb" = (/obj/structure/chair/stool,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Pm" = (/obj/structure/chair/office/light,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Po" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"Pp" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Pu" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) +"Pz" = (/obj/machinery/vending/clothing,/turf/simulated/floor/wood,/area/awaymission/beach) +"PE" = (/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"PH" = (/obj/structure/flora/grass/green,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"PL" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"PR" = (/turf/unsimulated/wall{icon_state = "rock";name = "rock"},/area/space) +"PW" = (/turf/simulated/shuttle/wall{tag = "icon-swall3";icon_state = "swall3"},/area/awaymission/beach/offshore) +"Qf" = (/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"Qh" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"Qm" = (/obj/structure/chair/beachchair{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"Qu" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/stalkybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"QI" = (/turf/unsimulated/wall/fakeglass{icon_state = "fakewindows2";dir = 1},/area/awaymission/undersea) +"QJ" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_l,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"QM" = (/mob/living/simple_animal/hostile/carp/megacarp{name = "Mega Sea Carp"},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"QN" = (/obj/effect/decal/snow/sand/surround{name = "rough sand"},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"QR" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"QW" = (/obj/structure/chair/beachchair/red{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"QY" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_y = 32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"QZ" = (/obj/structure/rack,/obj/item/restraints/handcuffs/cable/zipties,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Rj" = (/obj/structure/table/wood,/obj/item/storage/bag/plants,/obj/item/clothing/gloves/botanic_leather,/obj/item/shovel/spade,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"Rk" = (/obj/structure/table/wood,/obj/machinery/pos,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Rl" = (/mob/living/simple_animal/hostile/deathsquid,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"RG" = (/obj/structure/chair/beachchair/red,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"RL" = (/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (EAST)";icon_state = "wooden_chair_wings";dir = 4},/obj/structure/sign/poster/official/cleanliness{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"RN" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) +"RQ" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f9";icon_state = "swall_f9"},/area/awaymission/beach/offshore) +"RU" = (/turf/unsimulated/beach/water/deep/rock_wall{icon = 'icons/turf/walls/wood_wall.dmi';icon_state = "wood";name = "Wood Wall"},/area/awaymission/undersea) +"RV" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"RY" = (/obj/structure/sign/poster/contraband/syndicate_recruitment{pixel_x = 0;pixel_y = 32},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"Sb" = (/obj/structure/closet/crate,/obj/item/toy/syndicateballoon,/obj/item/toy/figure/syndie,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"Sc" = (/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) +"Se" = (/obj/item/clothing/head/nursehat,/obj/structure/sign/poster/official/healthy{pixel_x = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"So" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Sp" = (/obj/structure/mineral_door/sandstone,/turf/space,/area/awaymission/undersea) +"Ss" = (/obj/structure/sign/poster/official/cohiba_robusto_ad{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"Su" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Sw" = (/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"Sy" = (/obj/structure/mecha_wreckage/ripley/firefighter,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Sz" = (/obj/structure/flora/ausbushes/reedbush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"SD" = (/obj/structure/ladder/unbreakable/dive_point/buoy{id = "volcano_island"},/turf/unsimulated/beach/water/drop,/area/awaymission/beach/offshore) +"SL" = (/obj/structure/sign/poster/contraband/lusty_xenomorph{pixel_x = 32;pixel_y = 0},/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) +"SO" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"SS" = (/mob/living/simple_animal/hostile/retaliate/carp/koi/honk,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"ST" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/genericbush,/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) +"SX" = (/obj/machinery/vending/coffee/free,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"Td" = (/obj/structure/closet/cabinet,/obj/item/reagent_containers/food/drinks/bottle/rum,/obj/item/reagent_containers/food/drinks/bottle/rum,/obj/item/reagent_containers/food/drinks/bottle/rum,/obj/item/reagent_containers/food/drinks/drinkingglass,/obj/item/reagent_containers/food/drinks/drinkingglass,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"TF" = (/obj/structure/mirror{pixel_y = 32},/obj/structure/sink{pixel_y = 16},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"TG" = (/obj/structure/dresser,/obj/item/storage/wallet/random,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"TI" = (/obj/structure/closet/crate/secure/loot,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"TL" = (/obj/structure/falsewall/brass,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"TP" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) +"Ul" = (/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Uv" = (/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) +"Uy" = (/mob/living/simple_animal/hostile/carp/megacarp{name = "Mega Sea Carp"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"UK" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"UL" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f10";icon_state = "swall_f10"},/area/awaymission/beach/offshore) +"UM" = (/obj/structure/chair/stool,/mob/living/simple_animal/hostile/syndicate/ranged{loot = list()},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"UN" = (/turf/simulated/floor/plasteel,/area/awaymission/beach) +"UP" = (/obj/structure/rack/skeletal_bar/right,/obj/machinery/chem_dispenser/soda,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"UQ" = (/obj/structure/sign/directions/medical{dir = 1;pixel_y = 32},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"UR" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"UU" = (/obj/structure/closet/crate,/obj/item/stack/sheet/mineral/uranium{amount = 20},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Va" = (/obj/structure/grille,/obj/structure/shuttle/window,/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) +"Vh" = (/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Vp" = (/obj/effect/decal/cleanable/blood,/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (EAST)";icon_state = "wooden_chair_wings";dir = 4},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Vq" = (/obj/structure/flora/rock,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Vw" = (/obj/structure/closet/secure_closet/personal,/turf/simulated/floor/wood,/area/awaymission/beach) +"VE" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"VO" = (/obj/structure/ladder/unbreakable/dive_point/buoy{id = "brass temple"},/turf/unsimulated/beach/water/drop,/area/awaymission/beach/offshore) +"VP" = (/obj/item/ship_in_a_bottle,/obj/structure/table/wood,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"VR" = (/obj/structure/flora/bush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"We" = (/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"Wm" = (/obj/structure/mecha_wreckage/odysseus,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Wq" = (/obj/structure/chair/comfy/shuttle{dir = 1},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"Wr" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Wy" = (/obj/machinery/gateway{dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"WL" = (/obj/structure/chair/comfy/black{dir = 1},/turf/simulated/floor/wood,/area/awaymission/beach) +"WN" = (/obj/structure/closet/crate/can,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"WQ" = (/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"WS" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"WV" = (/obj/machinery/seed_extractor,/turf/simulated/floor/plasteel,/area/awaymission/beach) +"Xa" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Xi" = (/obj/machinery/door/airlock/hatch/syndicate{name = "Bunk House"},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) +"Xr" = (/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"Xz" = (/obj/item/clothing/under/color/lightred,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"XF" = (/obj/structure/table/wood,/turf/simulated/floor/wood,/area/awaymission/beach) +"XG" = (/obj/structure/flora/rock,/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"XH" = (/obj/structure/piano,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"XL" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"XU" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"XZ" = (/obj/structure/chair/stool,/obj/structure/mirror{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) +"Yi" = (/turf/unsimulated/wall/fakeglass{icon_state = "fakewindows2";dir = 1},/area/awaymission/beach/offshore) +"Yj" = (/obj/structure/closet/cabinet,/obj/item/clothing/head/pirate,/obj/item/clothing/suit/pirate_black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Ym" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Yo" = (/obj/structure/shuttle/engine/heater{dir = 1},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) +"Ys" = (/obj/effect/overlay/palmtree_l,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"Yt" = (/obj/machinery/hydroponics/soil,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Yv" = (/obj/structure/table/reinforced,/obj/item/storage/backpack/duffel/syndie/med,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) +"YB" = (/mob/living/simple_animal/crab,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"YF" = (/obj/item/clothing/shoes/brown,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"YG" = (/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) +"YJ" = (/obj/structure/table/wood,/obj/item/stack/spacecash/c100,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"YK" = (/obj/structure/flora/ausbushes/genericbush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) +"YO" = (/turf/unsimulated/wall/fakeglass{icon_state = "fakewindows2";dir = 8},/area/awaymission/beach/offshore) +"YR" = (/turf/unsimulated/wall{tag = "icon-sandstone1";icon_state = "sandstone1"},/area/awaymission/beach/offshore) +"YS" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f5";icon_state = "swall_f5"},/area/awaymission/beach/offshore) +"YY" = (/obj/machinery/vending/autodrobe,/turf/simulated/floor/wood,/area/awaymission/beach) +"Zb" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) +"Zg" = (/obj/structure/closet/crate,/obj/item/toy/plushie/nukeplushie,/obj/item/toy/nuke,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) +"Zl" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_x = -32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) +"Zo" = (/turf/unsimulated/wall{tag = "icon-sandstone3";icon_state = "sandstone3"},/area/awaymission/beach/offshore) +"Zp" = (/obj/structure/closet/crate{name = "Ripley parts crate"},/obj/item/mecha_parts/chassis/ripley,/obj/item/mecha_parts/part/ripley_left_arm,/obj/item/mecha_parts/part/ripley_left_leg,/obj/item/mecha_parts/part/ripley_right_arm,/obj/item/mecha_parts/part/ripley_right_leg,/obj/item/mecha_parts/part/ripley_torso,/obj/item/circuitboard/mecha/ripley/main,/obj/item/circuitboard/mecha/ripley/peripherals,/obj/item/mecha_parts/mecha_equipment/drill,/obj/item/mecha_parts/mecha_equipment/cable_layer,/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp,/obj/item/mecha_parts/mecha_equipment/rcd,/obj/item/stack/sheet/plasteel{amount = 20},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) +"Zr" = (/obj/effect/rune,/obj/structure/spawner/nether{max_mobs = 3;name = "weak netherworld link"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) +"Zt" = (/obj/item/clothing/under/rainbow,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +"Zu" = (/obj/structure/disposalpipe/segment,/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) +"Zy" = (/obj/structure/table/reinforced,/obj/item/storage/belt/military/traitor,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) +"ZD" = (/obj/structure/closet/crate,/obj/item/stack/marker_beacon/thirty,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"ZE" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/chair/stool/bar,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) +"ZG" = (/obj/structure/closet/crate,/obj/item/clothing/suit/pirate_brown,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) +"ZU" = (/obj/machinery/computer,/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) +"ZW" = (/obj/structure/flora/rock/pile,/turf/unsimulated/beach/water,/area/awaymission/beach) +"ZZ" = (/obj/item/clothing/shoes/sandal,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) (1,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -JZ -if -zL -Nn -JZ -if -JZ -if -Su -PH -JZ -Nb -cg -PH -Nn -JZ -dh -JZ -Nb -JZ -JZ -if -zL -Nn -JZ -if -NP -RV -IO -NP -Fl -JZ -if -dj -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -ds -du -du -du -du -du -"} -(2,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -iI -SO -Cr -Lk -Cr -Lk -Cr -Ka -fR -iI -wR -fR -iI -Lk -Lk -bH -Ia -fR -zr -fR -iI -SO -Cr -Lk -Cr -Lk -fR -fR -iI -iI -nV -fR -iI -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(3,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -NP -fR -PL -fR -Ia -fR -iI -iI -fR -fR -Ia -fR -lJ -nV -Lq -iI -iI -fR -Ia -uR -Vq -fR -PL -fR -Ia -fR -iI -gW -fR -iI -Lq -gW -zZ -fR -dj -dr -dr -dr -ID -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ID -dr -dr -ds -dt -du -du -du -du -du -"} -(4,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -qP -NZ -fR -Ia -fR -gW -Ia -fR -iI -nV -fR -iI -lJ -uR -fR -gW -fR -iI -fR -iI -Lq -NZ -fR -Ia -fR -gW -Ia -Oz -NG -NG -NG -iI -iI -iI -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -ka -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(5,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Im -Mx -fR -iI -Ia -fR -kq -fR -iI -fR -uR -fR -iI -iI -fR -iI -fR -iI -fR -fR -AS -Mx -fR -iI -Ia -fR -kq -iI -iI -zr -fR -PL -wR -fR -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -Bt -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(6,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -rt -NG -Ia -zr -fR -iI -fN -fR -zZ -fV -Vq -fR -zZ -fR -fN -fN -fR -We -zZ -uR -nl -lJ -nV -Lq -iI -iI -fR -Ia -Vq -zZ -lJ -bL -LY -fm -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -ka -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(7,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -iI -EM -iI -lJ -fN -gT -NP -MB -Fs -NP -MB -Fs -MB -Db -NP -MB -gT -NP -MB -Fs -NP -MB -Fs -MB -NP -Db -Fs -MB -gT -NP -MB -Fs -NP -dk -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -dt -du -du -du -du -du -"} -(8,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -iI -VE -fR -Mx -fR -rg -HI -pw -pw -pw -pw -pw -pw -bw -Po -DR -DR -DR -DR -DR -DR -DR -DR -DR -DR -DR -DR -DR -DR -ZD -hI -JJ -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(9,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -iI -VE -uR -iI -lJ -Fs -yS -kO -XF -Qm -QW -XF -kO -Pu -Po -DR -Vw -lo -DR -XZ -DR -XZ -DR -XZ -DR -ph -DR -GR -DR -mJ -hI -wi -hI -eo -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(10,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -SO -fR -iI -iI -fR -MB -yS -lo -lo -lo -lo -lo -lo -Pu -Po -DR -Vw -lo -DR -AX -DR -AX -DR -AX -DR -qf -DR -Fv -DR -DR -DR -DR -DR -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(11,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Cm -fR -zr -fR -Ia -zZ -NP -Fp -TP -TP -TP -TP -TP -TP -BR -Po -DR -Vw -lo -lo -lo -cl -lo -cl -lo -cl -lo -xH -lo -DR -dd -yF -em -DR -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(12,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -IO -fR -iI -iI -fR -iI -rg -hI -hI -hI -hI -hI -hI -hI -hI -Po -DR -Vw -lo -lo -lo -lo -lo -lo -lo -lo -lo -lo -lo -DR -BF -lo -lo -DR -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ID -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(13,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Hp -fR -iI -iI -fR -fR -Fs -hI -hI -hI -hI -hI -hI -hI -hI -Po -DR -Vw -lo -DR -YY -Pz -lo -lo -rV -lo -JB -hB -Mw -DR -Nq -lo -lo -DR -LJ -dr -dr -dr -dr -ZW -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(14,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -YK -Ia -Mx -zr -fN -MB -hI -xv -hI -hI -hI -hI -hI -hI -bD -DR -DR -DR -DR -DR -DR -lo -lo -DR -DR -DR -DR -DR -DR -DR -DR -ET -DR -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ZW -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(15,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -NP -EM -uR -fR -iI -fR -gT -hI -Qf -hI -hI -xG -hI -hI -hI -hI -hF -wc -wc -bT -wc -la -wc -wc -la -wc -wc -wc -wc -wc -wc -de -hI -hI -er -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(16,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -VE -fR -uR -uR -fN -rg -hI -hI -jN -HQ -BJ -hI -hI -bB -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(17,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -VE -fR -iI -iI -iI -NP -hI -hI -CC -kU -oL -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(18,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -EM -zr -fR -fR -pu -yG -hI -hI -Gu -Wy -sa -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -YB -hI -Gz -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -dr -dr -dr -dr -ZW -dr -ds -dt -du -du -du -du -du -"} -(19,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Iu -fR -iI -fR -Mx -fR -Fs -hI -hI -hI -hI -xG -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(20,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -EM -iI -Ia -fR -lJ -MB -hI -Qf -xv -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(21,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Cm -Ix -fR -uR -uR -fR -gT -DR -DR -iL -DR -DR -DR -DR -DR -sV -DR -DR -Ja -YB -hI -hI -hI -hI -hI -Qf -xv -hI -hI -Qf -hI -hI -dg -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(22,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -ww -iI -EM -fR -uR -fR -Fs -DR -YM -oN -oN -jA -DR -bi -gt -lo -lo -bF -ZE -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -xv -hI -hI -hI -hI -ew -dr -dr -dr -dr -dr -dr -dr -ID -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(23,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -Lk -iI -uR -gW -zZ -NP -DR -oB -UN -UN -UN -DR -bj -lo -lo -lo -bK -ZE -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -RG -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -Bt -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(24,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -fR -iI -Ia -fR -MB -Kj -fc -UN -UN -pO -DR -AD -lo -lo -lo -XF -ZE -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(25,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -iu -iI -zr -fR -fR -hn -Fs -Kj -WV -UN -UN -XN -DR -bl -lo -lo -lo -Yc -ZE -hI -hI -yB -hI -hI -Fb -hI -hI -hI -hI -hI -hI -hI -dg -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(26,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -VE -fR -iI -fR -Id -NP -Kj -IA -UN -UN -GK -DR -DD -lo -lo -lo -ZC -ZE -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -ZW -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(27,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -EM -fR -fR -iI -fR -gT -Kj -IA -UN -Pc -iT -DR -Jl -lo -lo -bM -XF -ZE -hI -hI -hI -Fb -hI -cZ -hI -Fb -hI -hI -hI -hI -hI -RG -hI -ey -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(28,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -qb -iI -iI -gW -fR -zZ -wQ -Kj -IA -UN -UN -UN -kf -lo -lo -lo -lo -gA -ZE -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -dt -du -du -du -du -"} -(29,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -iI -fR -gW -fR -Zb -Kj -IA -UN -UN -UN -ZN -lo -lo -lo -lo -qY -ZE -hI -hI -hI -hI -hI -db -hI -hI -hI -hI -hI -hI -hI -dg -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -dx -dt -du -du -dt -dt -du -"} -(30,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -iu -NG -uR -fR -iI -fR -NP -Kj -wd -UN -Pc -yT -DR -CB -lo -lo -lo -pP -ZE -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -ZW -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -dt -dw -du -du -du -du -dt -du -"} -(31,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -iI -EM -zr -fR -nV -gT -DR -id -UN -UN -iX -DR -lC -lo -lo -lo -SW -ZE -hI -hI -hI -hI -hI -hI -hI -YB -hI -CJ -hI -hI -hI -RG -hI -er -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -dv -dw -du -du -du -du -dt -du -"} -(32,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -PL -fR -Ia -fR -zi -DR -Jb -DP -DP -rT -Zu -na -vZ -bJ -ue -Zu -OT -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -du -dw -du -du -du -du -dt -du -"} -(33,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -EM -fR -uR -fN -Fs -DR -Kk -UN -UN -HC -Mq -WK -Bq -lo -XF -DR -bf -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -Jv -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -dt -dw -du -du -dt -du -du -du -"} -(34,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -iI -EM -Ia -zZ -fR -Db -DR -Eq -UN -kd -Rj -DR -tF -Bq -lo -XF -DR -Pu -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -Qf -xv -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dt -dx -du -du -du -du -du -du -"} -(35,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -Vq -EM -iI -fR -lJ -NP -DR -Kk -zx -EE -zQ -DR -oe -bX -by -bz -DR -bN -hI -hI -hI -hI -hI -hI -hI -Qf -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -ZW -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dx -dt -du -du -du -du -du -"} -(36,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Ym -Lq -VE -zr -iI -fR -MB -Mq -DR -DR -DR -DR -DR -DR -bd -DR -DR -DR -Dm -hI -hI -bB -hI -hI -hI -hI -xv -hI -hI -hI -hI -hI -Ez -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ZW -dr -dr -ds -dt -du -du -du -du -du -"} -(37,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -ww -fR -Ip -fR -fR -lJ -Zb -lQ -Fy -XF -Bz -Gd -Ol -XF -EB -bM -XF -DR -CE -hI -hI -hI -hI -hI -hI -hI -hI -hI -Iw -hI -hI -hI -iK -hI -ey -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(38,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -ww -fR -So -fN -zZ -fR -Db -DR -Ss -lo -lo -lo -lo -lo -lo -lo -bM -DR -Ao -hI -hI -hI -cJ -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -ID -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(39,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -zn -iI -EM -fR -iI -fN -NP -DR -lo -lo -lo -lo -lo -lo -lo -lo -vm -DR -UR -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(40,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -zZ -EM -iI -fR -fR -gT -DR -sk -Gm -sk -Gm -sk -Gm -sk -lo -lo -CF -UR -hI -hI -hI -hI -hI -hI -Fb -hI -Fb -hI -Fb -hI -hI -Bc -hI -eB -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(41,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -My -fR -EM -zZ -fR -lJ -Fs -DR -Gm -sk -Gm -sk -Gm -sk -Gm -lo -lo -lo -UR -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -LJ -dr -dr -dr -dr -ZW -dr -dr -dr -dr -dr -dr -ka -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(42,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -VE -fR -iI -fR -MB -DR -sk -Gm -sk -Gm -sk -Gm -sk -lo -vm -DR -hI -hI -hI -hI -hI -hI -hI -Fb -hI -Fb -hI -Fb -hI -hI -Bc -xv -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ID -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(43,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -VE -fR -Ia -fR -Db -DR -Gm -sk -Gm -gw -Gm -sk -Gm -lo -lo -DR -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -hI -ey -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(44,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Cm -Cr -fR -iI -iI -zZ -NP -DR -gp -Gm -sk -Gm -sk -Gm -sk -lo -WL -DR -UR -xv -hI -hI -hI -hI -hI -AW -gJ -gJ -gJ -AW -hI -hI -Bc -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(45,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -HJ -av -av -ao -av -av -av -HJ -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -fR -iI -fR -wR -zi -DR -Gm -sk -Gm -sk -Gm -sk -Gm -lo -XF -DR -UR -Qf -hI -hI -xv -hI -hI -lo -lo -lo -lo -lo -KM -hI -hI -hI -Gz -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(46,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -ao -ad -ad -ad -ad -ad -ad -ad -ad -HJ -av -av -an -av -av -av -av -uz -av -av -ao -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -NG -Ix -fR -iI -fN -NP -DR -sk -Gm -sk -Gm -sk -Gm -SL -lo -WL -DR -UR -hI -Bc -hI -hI -Bc -hI -MG -lo -NF -lo -zg -hI -xv -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(47,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ao -av -av -av -av -av -uz -av -av -av -av -av -av -av -HJ -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -zn -iI -fR -EM -fR -lJ -gT -Mq -DR -DR -DR -DR -DR -DR -DR -DR -DR -DR -UR -hI -hI -hI -hI -hI -Qf -no -OD -OD -OD -Ih -hI -Qf -hI -hI -LJ -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(48,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -ao -ad -ad -ad -ad -ad -av -av -av -av -av -uz -uz -av -av -an -av -uz -av -av -av -av -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -Vq -fR -VE -zZ -fR -Zb -JZ -MB -Fs -MB -NP -Db -Fs -MB -NP -Db -Fs -NP -NP -Nn -Nn -NP -MB -gT -cc -NP -cc -Db -NP -MB -NP -Fs -cc -dk -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -yU -dt -du -du -du -du -du -"} -(49,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -HJ -av -av -uz -uz -av -av -av -uz -av -av -av -av -av -uz -av -av -ao -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -fR -So -zr -fR -fR -Vq -Ia -fN -Vq -zZ -fR -iI -fN -iI -zZ -iI -fR -lJ -fR -Vq -Ia -fN -Vq -fR -iI -fN -fR -Lk -iI -uR -gW -zZ -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(50,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -ad -ad -TL -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -HJ -ad -ad -ad -av -av -uz -av -av -av -av -uz -av -av -uz -av -uz -av -av -av -av -av -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -SO -iI -fR -zZ -zZ -fR -fR -iI -iI -nV -fR -iI -fR -wR -fR -gW -zr -fR -fR -fR -fR -iI -iI -fR -iI -fR -fR -fR -fR -iI -Ia -fR -dj -dr -dr -dr -ID -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ID -dr -dr -ds -dt -du -du -du -du -du -"} -(51,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -uz -uz -av -av -HJ -av -av -an -av -av -av -av -av -an -av -uz -av -HJ -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -Ym -fR -EM -fR -iI -fR -iI -gW -fR -iI -Lq -gW -zZ -fR -iI -fR -Lq -iI -kq -iI -fR -gW -fR -iI -Lq -zZ -fR -iI -NG -iI -zr -fR -fR -fR -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -ka -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(52,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -ad -jY -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -RU -RU -RU -RU -RU -RU -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -uz -av -av -av -an -av -av -av -uz -av -an -uz -uz -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -zn -iI -oC -fR -fR -fR -wR -Oz -NG -NG -NG -iI -iI -iI -fR -Ia -fR -Ia -fR -iI -fR -Oz -NG -rD -NG -iI -iI -fR -fR -VE -fR -iI -fR -Id -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -Bt -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(53,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -BT -Ks -aE -aE -Td -RU -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -ad -ad -ad -ao -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -uz -an -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -fR -fR -VE -Lq -fR -hv -iI -iI -zr -fR -PL -wR -fR -NG -Ch -zZ -bO -iI -kq -iI -iI -iI -zr -fR -wR -fR -NG -dP -EM -fR -fR -iI -fR -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -ka -dr -ka -dr -dr -dr -dr -dr -dr -dr -ds -dt -du -du -du -du -du -"} -(54,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -ad -ad -mc -av -av -ad -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -RU -ah -Uv -aJ -ah -Uv -RU -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -HJ -ad -ad -ad -av -av -av -av -av -av -av -av -Rl -av -av -uz -av -av -av -av -uz -av -ao -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -PR -PR -PR -PR -PR -PR -PR -PR -JZ -JZ -JZ -Su -JZ -if -mY -ML -NP -Fl -ML -NJ -KS -uE -EZ -ML -if -XG -RV -JZ -if -ML -NP -Fl -ML -KS -uE -EZ -qb -if -if -mP -JZ -Fl -dj -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -dr -ds -du -du -du -du -LZ -"} -(55,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -Yj -ah -nF -ah -wx -RU -uz -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -ad -ad -HJ -av -av -av -av -av -av -av -av -av -av -av -uz -uz -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -fA -YG -XL -Gj -vr -xD -YG -zk -tM -zk -fA -xZ -tM -zk -fA -xZ -tM -fA -zk -Qu -vr -ji -ji -XL -vr -ji -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(56,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -RU -RU -Ds -RU -RU -RU -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -an -av -av -av -uz -av -uz -av -av -av -av -uz -av -av -an -av -av -HJ -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -YG -xI -VR -vr -vr -kH -sZ -wO -kH -Gj -vr -ji -wO -ji -Gj -ji -vr -WQ -vr -vr -ji -vr -vr -ji -ji -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(57,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -ad -av -av -RU -In -In -ah -ah -ah -RU -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -uz -av -av -uz -av -av -av -av -av -av -av -uz -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -lX -ji -vr -Gj -Gj -vr -vr -ji -ji -OR -vr -ji -vr -OY -vr -LV -VR -vr -vr -vr -qx -vr -sZ -vr -qx -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(58,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -RU -In -Uv -ah -iC -Uv -RU -av -av -LB -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -uz -uz -av -av -av -HJ -ad -ao -av -uz -av -av -uz -av -av -av -av -av -an -av -av -av -av -av -av -an -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -tN -YG -BY -vr -ji -vr -ji -LV -vr -ji -yJ -LV -Gj -vr -ji -vr -yJ -ji -Hf -ji -vr -ji -vr -sZ -vr -qx -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(59,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -aq -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -RU -ah -iC -Cz -ah -ah -RU -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -uz -av -av -av -av -av -an -av -av -av -HJ -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -oH -vk -Sz -vr -vr -vr -OY -XU -FT -FT -FT -ji -ji -ji -vr -sZ -vr -sZ -vr -ji -vr -sZ -vr -ji -sZ -vr -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(60,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -RU -ah -ah -ah -ah -ah -RU -av -uz -av -av -uz -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -uz -av -av -av -av -uz -av -av -ao -ad -av -av -av -av -uz -uz -uz -uz -av -av -uz -uz -av -av -av -uz -av -an -ao -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -YG -XL -yJ -vr -sj -vk -vk -yq -YG -qx -OY -vr -FT -JM -Aa -kQ -ji -Hf -ji -fA -vr -sZ -vr -vr -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(61,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -vq -ah -Cz -ah -ah -RU -av -av -av -av -av -av -LB -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -ad -ad -av -uz -uz -av -av -av -ad -HJ -av -av -av -av -uz -av -uz -av -uz -uz -av -av -an -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -YG -BY -vr -ji -Lz -qo -fA -Aa -qo -le -Pp -rA -zw -qo -vk -Ob -sZ -vr -vk -eS -sZ -vr -sZ -sZ -sZ -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(62,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -RU -Co -ah -iC -ah -ah -gB -av -av -av -uz -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -ad -av -av -ad -av -uz -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -uz -uz -av -av -av -uz -av -av -HJ -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -vk -BY -VR -vr -OR -eS -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -kQ -UK -wJ -qx -vr -sZ -BY -VR -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -NT -NT -NT -NT -NT -NT -NT -NT -NT -NT -km -km -km -km -km -fl -Bi -Bi -Bi -Bi -km -km -km -km -km -km -km -"} -(63,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -RU -vq -ah -Cz -ah -ah -aV -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -uz -av -an -av -av -HJ -ad -xQ -xQ -ad -ad -ao -av -uz -av -av -av -av -uz -av -uz -av -av -av -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -qx -vr -sZ -vr -QJ -vr -vr -vr -HY -PW -PW -pD -pD -PW -PW -YS -vr -vr -QJ -BY -vr -Ys -qx -vr -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -NT -PE -NT -NT -cL -NT -cL -cL -NT -NT -NT -to -km -km -km -km -km -km -Bi -km -km -km -km -km -km -km -km -km -"} -(64,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -RU -ah -ah -ah -ah -RN -aV -av -LB -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -uz -av -xQ -xQ -xQ -xQ -ad -ad -av -av -av -av -uz -av -av -av -av -av -av -ao -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -BY -vr -Ys -wO -tM -vr -vr -vr -ti -oy -Ns -oG -oG -EX -Cv -lH -vr -wO -tM -BY -sZ -Gj -BY -vr -sZ -km -km -km -km -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -km -km -km -NT -NT -NT -NT -eC -NT -NT -NT -NT -to -NT -cL -NT -NT -to -FB -NT -to -km -km -Bi -Bi -Bi -km -km -km -km -km -km -km -"} -(65,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -ah -Uv -Cz -ah -ah -RU -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -uz -HJ -ad -ad -HJ -av -av -av -av -av -uz -av -av -ao -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -NT -vk -BY -sZ -Gj -vr -xZ -vr -vr -vr -Va -ZU -Wq -oG -oG -oG -Cv -tr -vr -vr -xZ -BY -ji -vr -BY -sZ -ji -km -km -km -km -fj -fj -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -fj -fj -km -km -km -cL -NT -NT -NT -eC -ym -NT -eC -NT -qT -JF -NT -NT -cL -cL -NT -NT -eC -BU -km -km -km -Bi -km -km -km -km -km -km -km -"} -(66,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -kT -ah -ah -ah -ah -RU -av -ad -av -av -av -av -LB -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -ad -uz -uz -av -av -av -uz -av -av -uz -av -uz -av -av -ao -ad -ad -ad -ad -HJ -ao -av -av -av -HJ -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -fA -BY -ji -vr -WQ -fA -vr -vr -vr -ti -GZ -oG -oG -oG -qj -Cv -vY -vr -WQ -fA -XL -VR -ji -BY -ji -VR -km -km -km -km -fj -fj -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -fj -fj -km -km -eC -NT -NT -cL -eC -NT -eC -NT -NT -eN -NT -JF -JF -NT -NT -to -NT -BU -NT -eC -eC -km -km -Bi -km -km -km -km -km -km -km -"} -(67,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -RU -ah -ah -Cz -ah -ah -RU -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -av -ad -ad -ad -ad -av -av -av -av -uz -HJ -av -av -ao -av -av -av -uz -av -av -av -av -av -av -HJ -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -ix -BM -XL -VR -ji -vr -zk -vr -vr -vr -UL -PW -PW -pD -pD -PW -PW -RQ -vr -vr -zk -FF -vr -vr -XL -VR -vr -km -km -km -km -fj -rv -Kb -Op -Zl -Op -Zl -Op -Zl -Op -Zl -Op -Zl -Op -Zl -Op -Zl -Op -Kb -rv -fj -km -NT -eC -NT -NT -NT -NT -NT -cL -cL -NT -cL -NT -NT -eC -JF -qT -oz -eC -eC -NT -eC -eC -to -km -Bi -km -km -km -km -km -km -km -"} -(68,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -iC -ah -ah -RU -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -ad -av -av -ad -av -uz -av -av -av -av -av -av -uz -av -uz -an -uz -av -uz -av -av -av -av -HJ -ad -ad -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -rx -NT -FF -vr -vr -WQ -xD -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -WQ -xD -xI -wO -Gj -FF -vr -vr -km -km -km -km -fj -fj -fj -QY -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -cH -fj -fj -fj -km -NT -eC -NT -cL -NT -PE -NT -NT -NT -PE -NT -to -eN -eC -NT -qT -qT -eC -eN -NT -NT -FB -eC -km -Bi -km -km -km -km -km -km -km -"} -(69,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -RU -ah -ah -ah -RU -av -av -ad -av -LB -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -uz -av -av -av -av -uz -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -PE -NT -Ne -vr -WQ -qU -fA -vr -vr -WQ -vr -vr -vr -vr -vr -vr -vr -vr -vr -wO -eS -YG -XL -vr -YG -vr -kH -km -km -km -km -fj -rv -Kb -Op -Op -Op -Op -Op -Op -Op -Ou -Op -Op -Op -Op -Op -Op -Op -Kb -rv -fj -km -NT -NT -NT -NT -NT -cL -NT -eN -Ul -NT -vr -vr -vr -NT -vr -NT -qT -NT -NT -to -fa -NT -eC -km -Bi -km -km -km -km -km -km -km -"} -(70,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -ah -RU -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ad -av -av -uz -av -av -av -av -av -av -av -av -av -uz -av -av -av -av -av -av -av -av -av -av -av -HJ -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -NT -NT -to -WQ -vr -vr -fA -WQ -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -Gj -xD -YG -xI -vr -fA -vr -xx -km -km -km -km -fj -fj -fj -QY -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -cH -fj -fj -fj -NT -NT -eC -NT -oU -rS -NT -NT -NT -vr -vr -vr -ji -vr -vr -vr -vr -vr -ji -NT -NT -oz -NT -eC -FB -Bi -km -km -km -km -km -km -km -"} -(71,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -ah -RU -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -uz -av -av -av -av -av -av -av -uz -uz -av -av -av -uz -av -av -av -av -av -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -NT -PE -NT -Ne -WQ -qU -fA -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -YG -lX -ji -vr -YG -vr -kH -km -km -km -km -fj -rv -Kb -Op -Op -fj -Op -Op -Op -Op -fj -Op -Op -Op -Op -fj -Op -Op -Kb -rv -fj -NT -NT -NT -bg -NT -NT -eC -cL -eC -vr -vr -vr -vr -vr -vr -vr -OY -vr -to -cL -eN -eC -NT -ST -FB -km -km -km -km -km -km -km -km -"} -(72,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -RU -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -uz -av -av -uz -av -av -uz -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -rx -YG -Qh -wO -Gj -vr -xZ -LL -Zo -Zo -Zo -Zo -YR -vr -vr -vr -vr -vr -vr -vr -xZ -BY -vr -xx -vr -kH -nU -km -km -km -km -fj -fj -fj -QY -Op -Op -fj -Op -Op -fj -fj -fj -Op -Op -fj -Op -Op -cH -fj -fj -fj -NT -nE -NT -NT -Hr -NT -NT -ci -wO -OY -vr -Nr -CM -CM -nT -vr -vr -vr -NT -NT -NT -eC -to -NT -NT -km -km -km -km -km -km -km -km -"} -(73,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -RU -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -HJ -av -av -ao -av -uz -av -av -uz -av -av -av -ao -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -Fi -to -BY -vr -ji -wO -fA -Jj -CM -CM -CM -CM -CM -vr -vr -vr -vr -vr -vr -wO -fA -BY -ji -xx -kH -Sc -Sc -km -km -km -km -fj -rv -Kb -Op -Op -Op -Op -fj -fj -fj -fj -fj -fj -fj -Op -Op -Op -Op -Kb -rv -fj -NT -NT -DG -sv -NT -oU -NT -NT -vr -vr -vr -Nr -CM -nT -nT -AM -vr -vr -NT -NT -NT -JF -to -NT -NT -NT -km -km -km -km -km -km -km -"} -(74,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -uz -uz -av -uz -av -av -av -av -av -av -av -uz -av -uz -av -av -av -av -av -av -av -av -av -av -av -ao -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -Aa -BY -ji -vr -vr -eS -Jj -CM -CM -CM -CM -CM -vr -vR -vr -Pb -Pb -vr -vr -eS -BY -Gj -vr -lS -Cf -Sc -km -km -km -km -fj -fj -fj -QY -Op -Op -Op -fj -bx -mV -bx -mV -bx -fj -Op -Op -Op -cH -fj -fj -fj -sv -cL -NT -NT -nE -NT -eC -NT -vr -vr -vr -Nr -CM -VO -AM -AM -vr -vr -vr -eC -NT -JF -NT -NT -NT -FB -km -km -km -km -km -km -km -"} -(75,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -uz -av -av -av -uz -av -av -av -av -av -av -av -av -av -av -uz -av -av -av -av -av -an -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -QN -YG -BY -Gj -vr -WQ -tM -Jj -CM -CM -op -CM -CM -pd -vr -Pb -gD -uN -vr -WQ -tM -XL -xx -vr -lS -xR -Sc -km -km -km -km -fj -rv -Kb -Op -Op -Op -fj -fj -fJ -Op -Xa -Op -Op -fj -fj -Op -Op -Op -Op -Op -Fd -NT -gn -NT -cL -bg -iF -eC -rS -zX -vr -vr -Nr -CM -nT -nT -AM -vr -vr -vr -ji -OF -NT -NT -cL -NT -to -km -km -km -km -km -km -km -"} -(76,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -uz -uz -av -av -av -av -av -uz -uz -av -av -av -av -av -uz -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -XL -vr -ji -vr -zk -Jj -CM -CM -CM -CM -CM -vr -vr -Pb -jE -pp -vr -vr -zk -XL -vr -vr -lS -Sc -Sc -km -km -km -km -fj -fj -fj -QY -Op -fj -fj -fj -Lu -Xa -fd -Xa -Op -Kb -Kb -Kb -Op -Op -Op -Op -Fd -NT -NT -Hr -NT -NT -NT -NT -NT -NT -WQ -vr -Nr -CM -CM -nT -vr -OY -vr -vr -vr -eC -eC -to -NT -NT -NT -km -km -km -km -km -km -km -"} -(77,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -ad -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -uz -av -av -av -av -av -av -uz -uz -uz -av -av -av -av -av -av -av -av -av -av -uz -uz -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -XL -vr -sZ -vr -xZ -Jj -CM -CM -CM -CM -CM -vr -vr -vr -Pb -Pb -vr -vr -xZ -vr -ji -vr -lS -Sc -Sc -km -km -km -km -fj -rv -Kb -Op -Op -Op -fj -fj -ky -Op -Xa -Op -Op -fj -fj -Op -Op -Op -Op -Op -Fd -NT -sv -NT -gn -NT -NT -eC -cL -vr -vr -vr -OY -vr -vr -vr -OY -vr -vr -WQ -NT -NT -NT -to -NT -NT -km -km -km -km -km -km -km -km -"} -(78,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -ad -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -IZ -Bv -vr -ji -ji -Gj -fA -HN -Zo -Zo -Zo -Zo -YR -vr -vr -vr -vR -vr -vr -Gj -fA -vr -ji -vr -lS -lS -Sc -km -km -km -km -fj -fj -fj -QY -Op -Op -Op -fj -bx -mV -bx -mV -bx -fj -Op -Op -Op -cH -fj -fj -fj -DG -NT -eC -NT -NT -NT -eC -NT -NT -vr -ji -vr -vr -vr -vr -vr -vr -vr -qT -NT -PE -PE -NT -NT -NT -km -km -km -km -km -km -km -km -"} -(79,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -vr -vr -vr -ji -vr -OY -QJ -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -OY -QJ -UK -vr -vr -vr -lS -lS -km -km -km -km -fj -rv -Kb -Op -Op -Op -Op -fj -fj -fj -fj -fj -fj -fj -Op -Op -Op -Op -Kb -rv -fj -NT -cL -cL -NT -Hr -eC -NT -eC -NT -WQ -vr -vr -NT -NT -wO -NT -ji -vr -qT -qT -eN -PE -NT -BU -NT -km -km -km -km -km -km -km -km -"} -(80,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -uz -uz -av -av -uz -uz -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -vr -FT -UK -vr -ji -wO -fA -LL -Zo -Zo -Zo -Zo -Zo -Zo -Zo -YR -OJ -OJ -kE -wO -fA -YG -BY -vr -kH -vr -lS -km -km -km -km -fj -fj -fj -QY -Op -Op -fj -Op -Op -fj -fj -fj -Op -Op -fj -Op -Op -cH -fj -fj -fj -cL -gn -NT -nE -NT -oU -NT -NT -NT -NT -NT -eC -eC -NT -NT -NT -NT -NT -AI -By -qT -NT -PE -NT -NT -km -km -km -km -km -km -km -km -"} -(81,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -ad -ad -ad -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -oH -vk -YG -BY -vr -WQ -eS -Jj -Cj -Pm -Jj -vr -pd -vr -OJ -vr -vr -vr -Jj -WQ -eS -YG -XL -Gj -YG -vr -vr -km -km -km -km -fj -rv -Kb -Op -Op -fj -Op -Op -Op -Op -fj -Op -Op -Op -Op -fj -Op -Op -Kb -rv -fj -NT -NT -NT -NT -NT -NT -NT -PE -NT -NT -to -eC -NT -NT -NT -NT -NT -NT -qT -qT -qT -BU -NT -BU -km -km -km -km -km -km -km -km -km -"} -(82,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -fA -YG -XL -Gj -vr -xD -Jj -VP -vr -Jj -vr -vr -vr -kE -vr -vr -vr -Jj -vr -xD -YG -xI -VR -YG -kH -vr -km -km -km -km -fj -fj -fj -QY -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -cH -fj -fj -fj -NT -nE -NT -eC -bg -NT -NT -NT -NT -eN -NT -NT -NT -PE -qT -cL -NT -eC -eC -qT -NT -NT -BU -NT -km -km -km -km -km -km -km -km -km -"} -(83,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -ad -av -ad -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -YG -xI -VR -vr -vr -Jj -sp -pd -Jj -sN -vr -vr -kE -vr -Jd -vr -Jj -vr -YG -lX -ji -vr -YG -vr -vr -km -km -km -km -fj -rv -Kb -Op -Op -Op -Op -Op -Op -Op -Ou -Op -Op -Op -Op -Op -Op -Op -Kb -rv -fj -NT -NT -eC -NT -NT -NT -eC -cL -eC -eC -NT -NT -NT -to -NT -PE -OF -NT -to -NT -to -NT -NT -NT -km -km -km -km -km -km -km -km -km -"} -(84,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -HJ -av -av -av -ad -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -lX -ji -vr -Gj -Gj -Jj -Bn -vr -Jj -ZG -vr -vr -OJ -vr -vr -vr -Jj -Gj -Aa -BY -vr -ji -lX -vr -kH -km -km -km -km -fj -fj -fj -QY -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -Op -cH -fj -fj -fj -bg -NT -NT -NT -NT -NT -NT -NT -eC -JF -qT -JF -eC -NT -NT -NT -NT -NT -NT -eC -eC -NT -NT -NT -km -km -km -km -km -km -km -km -km -"} -(85,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -sF -av -av -av -ad -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -tN -YG -BY -vr -ji -vr -ji -Jj -YR -OJ -HN -Zo -Zo -Zo -Zo -YR -vr -vr -Jj -vr -vk -qx -vr -sZ -BY -vr -kH -km -km -km -km -fj -rv -Kb -Op -Jc -Op -Jc -Op -Jc -Op -Jc -Op -Jc -Op -Jc -Op -Jc -Op -Kb -rv -fj -NT -NT -NT -NT -NT -NT -NT -NT -eN -qT -NT -eC -NT -NT -to -eN -eN -cL -NT -eC -cL -NT -NT -to -km -km -km -km -km -km -km -km -km -"} -(86,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -aq -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -HJ -av -av -ad -ad -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -fA -YG -qx -vr -sZ -vr -ji -Jj -pd -vr -vr -OJ -vr -vr -vr -vr -vr -vr -Jj -vr -vk -vr -sZ -vr -qx -vr -vr -km -km -km -km -fj -fj -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -Kb -fj -fj -fj -km -NT -NT -eC -NT -NT -PE -NT -qT -NT -NT -NT -eN -NT -qT -qT -sg -NT -NT -NT -NT -BU -NT -km -km -km -km -km -km -km -km -km -km -"} -(87,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -ad -av -av -ad -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -ad -ad -ad -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -BM -ER -vr -sZ -vr -LV -vr -Jj -vr -vr -vr -OJ -vr -Jd -vr -vr -vr -vr -Jj -iU -YG -qx -vr -sZ -vr -sZ -vr -km -km -km -km -fj -fj -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -rv -fj -fj -fj -km -NT -NT -NT -NT -NT -NT -NT -NT -NT -NT -NT -eC -eC -qT -qT -qT -NT -BU -NT -NT -NT -NT -km -km -km -km -km -km -km -km -km -km -"} -(88,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -ad -ad -ad -ad -ad -av -ad -av -ad -ad -av -ad -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -fA -YG -qx -vr -sZ -vr -ji -Jj -pd -vr -vr -OJ -vr -vr -vr -vr -vr -vr -Jj -YG -vk -vr -sZ -vr -qx -vr -sZ -km -km -km -km -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -fj -km -km -NT -NT -NT -NT -NT -NT -eN -cL -NT -NT -JF -qT -qT -NT -NT -NT -NT -NT -BU -NT -km -km -km -km -km -km -km -km -km -km -km -"} -(89,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -BM -ER -vr -sZ -vr -LV -sZ -Jj -Zo -YR -OJ -LL -Zo -Zo -Zo -YR -vr -vr -Jj -iU -sZ -vr -ji -sZ -vr -sZ -ji -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -NT -NT -NT -PE -NT -PE -NT -NT -NT -NT -eC -NT -NT -NT -NT -NT -NT -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(90,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -vn -QR -vr -ji -sZ -vr -Hf -Jj -UU -vr -vr -Jj -is -vr -vr -OJ -vr -vr -kz -DV -fA -vr -sZ -vr -vr -ji -sZ -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -NT -NT -NT -NT -NT -NT -qT -qT -NT -eC -NT -NT -NT -NT -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(91,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -SS -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -kQ -FT -sZ -VR -vr -ji -wO -Jj -vO -BA -yD -Jj -vr -pd -vr -OJ -vr -vr -WN -qo -eS -sZ -vr -sZ -sZ -VR -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -NT -NT -NT -NT -NT -NT -NT -NT -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(92,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rF -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -vk -BY -ji -WQ -wO -eS -HN -Zo -Zo -Zo -Jj -Zo -Zo -Zo -Zo -Zo -Zo -YR -YG -xD -vr -sZ -vr -BY -ji -sZ -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(93,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -av -av -ad -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -YG -vk -WS -Ej -YG -vk -YG -vk -BY -Hf -vr -Ys -IN -LK -Ej -YG -XL -vr -Ys -vr -sZ -vr -sZ -vk -WS -vr -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(94,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -Uy -av -av -ao -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -YG -vk -lX -Bv -IZ -Bv -IZ -Bv -Kt -vr -ji -OY -vr -ji -IZ -IZ -Wr -sZ -vr -VR -vr -ji -sZ -vr -lX -Bv -sZ -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(95,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -lK -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -fA -YG -qx -vr -sZ -vr -ji -ji -vr -vr -sZ -vr -WQ -OR -yJ -ji -ji -vr -sZ -Ys -vr -BY -ji -sZ -qx -vr -ji -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(96,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -aY -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -BM -ER -vr -sZ -vr -LV -sZ -vr -ji -OR -vr -ji -WQ -Ys -vr -LV -vr -ji -vr -ji -vr -XL -BY -vr -vr -sZ -BY -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(97,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -mw -QR -vr -ji -sZ -vr -Hf -vr -ji -vr -Ys -vr -ji -ji -vr -ji -vr -ji -vr -vr -vr -xI -XL -Gj -vr -ji -XL -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(98,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -te -Bm -tc -yq -vr -vk -DV -vr -Aa -eS -kH -YG -Aa -YG -DV -wO -YG -hP -Gj -YG -YG -YG -xI -VR -tc -yq -xI -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -km -"} -(99,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -ad -ad -av -ad -av -av -ad -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(100,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -Uy -av -av -av -av -av -av -ad -av -av -av -av -ad -ad -av -av -av -av -av -av -av -ad -av -ad -ad -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(101,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(102,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -SS -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(103,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -jL -jL -jL -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(104,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -ao -av -av -av -ad -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -SS -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -jL -jL -jL -jL -jL -jL -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(105,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -jL -tV -Yi -NR -jL -jL -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(106,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -jL -tV -Jr -YO -jL -jL -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(107,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -jL -tV -CG -YO -jL -jL -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(108,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -jL -tV -CG -qK -jL -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(109,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -aU -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -tV -tV -zR -tV -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -nT -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(110,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -ad -ad -av -av -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -tV -ql -zC -tV -tV -tV -tV -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -CM -CM -CM -nT -nT -AM -AM -AM -AM -AM -AM -AM -"} -(111,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -ad -ad -av -av -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -tV -zC -zC -tV -fE -SX -wr -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -nT -nT -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -Nr -CM -CM -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(112,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -ad -av -SS -av -av -ad -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -tV -tV -tV -tV -tV -RY -zC -tV -Dd -wS -wS -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -nT -vr -kH -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -Cw -CM -CM -lg -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(113,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -ad -av -ad -ad -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -ad -ad -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -tV -Yv -sx -tp -tV -zC -zC -tV -nc -wS -KU -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -vr -vr -vr -vr -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -vr -vr -vr -vr -Nr -CM -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(114,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -ad -ad -av -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -vN -av -ad -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -tV -tV -tV -tV -tV -gL -Il -JE -tV -zC -zC -Xi -wS -wS -Zg -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -vr -vr -vr -Bi -jL -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -vr -vr -Cw -CM -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(115,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -ad -av -av -av -ad -ad -ad -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -vN -lZ -vN -ad -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -tV -Et -hE -OG -tV -Il -Il -Il -tV -UQ -zC -tV -NQ -wS -nc -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -vr -Bi -Bi -jL -jL -Bi -Bi -Bi -Bi -Bi -Bi -jL -jL -Bi -Bi -vr -vr -vr -Nr -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(116,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -ad -av -av -av -ad -ad -av -av -av -av -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -vN -av -ad -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -tV -jD -Zj -CG -tV -wb -Il -It -JK -zC -zC -tV -Sb -wS -yY -tV -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -vr -Bi -Bi -jL -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -jL -Bi -Bi -vr -kH -Nr -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(117,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -ad -ad -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -tV -kp -CG -CG -tV -Il -Il -ve -tV -zh -zC -tV -BN -vh -nc -tV -tV -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -jL -Bi -Bi -Bi -ok -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -vr -vr -Nr -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(118,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -ad -av -SS -av -av -ad -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -tV -tV -tV -pz -tV -tV -tV -tV -tV -zC -zC -tV -nb -qR -qR -Zy -tV -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -jL -Bi -ok -ok -ok -ok -ok -Bi -Bi -jL -Bi -Bi -Bi -vr -vr -Nr -CM -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -"} -(119,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -rF -av -av -av -ad -av -av -av -ad -av -av -av -ad -ad -av -av -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -tV -Ru -qR -qR -yz -tV -zC -zC -zC -zC -tV -IG -qR -pb -oE -tV -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -jL -jL -ok -jL -jL -jL -ok -ok -jL -Bi -Bi -Bi -Bi -vr -vr -Nr -CM -CM -nT -nT -AM -AM -AM -AM -AM -AM -AM -"} -(120,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -SS -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -ad -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -ad -ad -av -av -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -tV -Oc -Lp -qR -qR -Jf -zC -zC -zC -zC -tV -tV -tV -tV -tV -tV -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Oi -Bi -Bi -jL -ok -jL -Bi -Bi -jL -jL -Bi -Bi -Bi -jL -Bi -vr -vr -Nr -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(121,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -av -av -ah -ah -av -av -av -av -aq -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -tV -Hm -oi -qR -xe -tV -mF -un -UM -Ke -tV -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -ok -ok -ok -jL -Bi -Bi -Bi -Bi -Bi -Bi -Bi -jL -Bi -vr -vr -Nr -CM -CM -SD -AM -AM -AM -AM -AM -AM -AM -AM -"} -(122,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -av -ah -ah -aw -ah -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -tV -tV -tV -tV -tV -tV -tV -tV -tV -tV -tV -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -ok -jL -jL -Bi -Bi -Bi -Bi -jL -jL -jL -Bi -jL -Bi -vr -vr -Nr -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(123,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -ah -ah -ad -ad -ah -ah -av -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -ok -ok -jL -Bi -wu -Bi -jL -ok -ok -jL -jL -Bi -Bi -vr -vr -Nr -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(124,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -ah -ah -ad -ad -ah -ah -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -vr -Bi -Bi -Bi -ok -jL -Bi -Bi -Bi -jL -ok -Bi -Bi -Bi -jL -Bi -vr -vr -Nr -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(125,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -aq -av -ad -av -av -ah -ah -ah -ah -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -vr -Bi -Oi -Bi -ok -ok -jL -jL -jL -ok -ok -Oi -Bi -Bi -jL -Bi -vr -kH -Nr -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(126,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -ad -av -av -av -ah -ah -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -Bi -ok -ok -ok -ok -ok -Bi -Bi -Bi -Bi -jL -Bi -vr -vr -Nr -CM -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(127,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -ad -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -jL -Bi -vr -vr -Nr -CM -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -"} -(128,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -ok -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -Bi -Bi -jL -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Oi -Bi -Bi -jL -Bi -vr -vr -Nr -CM -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(129,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -ad -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -vr -vr -Bi -Bi -jL -jL -jL -Bi -Bi -jL -Bi -Bi -Bi -Bi -Bi -Bi -vr -vr -Nr -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(130,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ad -ad -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -rX -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -kH -vr -vr -Bi -Bi -Bi -Bi -Bi -Bi -Bi -Bi -jL -Bi -Bi -Bi -vr -vr -nT -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(131,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -vr -vr -vr -vr -vr -vr -Bi -Bi -Bi -Bi -Bi -vr -vr -vr -vr -vr -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(132,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -nT -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -kH -vr -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(133,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -vr -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(134,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -nT -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(135,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(136,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(137,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(138,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -TI -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(139,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(140,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(141,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -TI -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(142,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -og -av -av -og -og -og -og -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(143,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -Am -Sw -Sw -Sw -Am -Sw -Sw -og -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -AM -"} -(144,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -Xr -Sw -Sw -Sw -Ed -Sw -Sw -Sw -Ed -kP -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(145,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -hD -Yo -Sw -Sw -Sw -QM -Ed -Sw -FN -Sw -kP -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(146,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -TI -av -av -av -av -av -av -av -av -hD -Yo -Sw -Sw -Sw -Sw -Sw -Sw -Sw -Ed -kP -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(147,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -og -Mb -Am -Sw -Sw -Sw -Ed -Am -Sw -og -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(148,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -Mb -og -og -og -og -og -og -og -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(149,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -nJ -nJ -nJ -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(150,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -og -NV -NV -NV -og -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(151,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -og -Sw -Sw -Sw -Sw -Sw -og -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(152,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -as -og -Sw -Sw -Zp -Sw -AP -og -as -as -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(153,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -Sw -Sw -Sw -Sw -Sw -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(154,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -uc -av -av -av -av -av -av -av -av -ad -aG -aN -aC -ah -ah -ad -av -av -av -ad -aE -wW -rc -aE -nL -nD -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(155,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -av -av -av -as -av -av -av -av -av -av -as -av -av -ad -ad -ad -ad -ah -ah -ad -av -av -av -ad -yH -ah -ah -ah -ar -BH -ad -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(156,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -Sw -av -Sy -av -Sw -av -av -av -av -ad -lD -ah -ar -ah -aI -ad -av -av -av -ad -im -ah -ah -ah -ah -YF -ad -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(157,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -Lv -av -as -av -av -av -av -av -av -as -aS -ah -ah -ag -Eu -ad -av -av -av -ad -im -ah -YJ -aE -aE -Rk -ad -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(158,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -Ed -av -Sw -av -av -av -av -av -ad -xX -ah -ah -ag -hR -ad -av -av -av -ad -gq -ah -ar -EH -ah -ah -ad -av -av -av -av -ad -ad -av -av -av -ao -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -rF -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(159,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -Lv -av -av -av -av -as -av -Lv -av -av -ad -Fx -ah -ah -ah -aJ -ad -av -av -av -ad -ah -ah -jd -ah -ah -ah -ad -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(160,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -ah -av -av -av -av -av -av -av -ad -ad -aC -ad -ad -ad -ad -av -av -av -ad -ad -ad -ad -aC -aC -ad -ad -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(161,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -mI -hm -Lj -Xr -Go -ad -av -av -av -Wm -ah -ah -eM -Lv -ah -as -av -av -av -av -av -aO -av -av -av -av -av -av -av -av -as -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(162,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -sO -Xr -Xr -Xr -Go -ad -JQ -av -av -ah -ad -ah -ad -ah -ah -av -av -av -av -av -av -av -av -av -av -av -av -av -av -as -av -av -av -av -av -av -av -av -HJ -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(163,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ov -Xr -Xr -Xr -Xr -ad -rG -av -av -av -LB -av -av -av -av -av -as -av -av -av -av -av -av -av -av -av -av -av -av -as -av -av -av -av -av -av -Io -Vh -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(164,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -sz -Xr -Xr -Xr -Xr -aY -av -av -av -av -av -av -av -av -as -LB -av -av -av -av -av -av -av -av -av -av -av -av -av -as -av -av -av -av -as -av -av -yr -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(165,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -Xr -Xr -Kr -pe -Xr -ad -av -av -av -LB -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -as -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(166,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -LB -av -av -av -av -av -as -av -av -av -av -av -av -av -av -av -av -av -av -as -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(167,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(168,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -JH -MY -LE -QZ -ad -av -av -av -av -ad -aD -ah -ar -ah -ah -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(169,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ah -ah -ah -ah -ad -Ep -av -av -av -ad -aE -Ap -oY -ar -ah -ad -aO -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(170,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -wf -ml -Zr -xm -ad -av -av -av -av -ad -aF -ah -Xz -YF -ah -aC -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(171,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -Vp -ah -MO -iC -ad -aP -av -av -av -ad -ah -jX -ah -ah -ah -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(172,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -NE -iC -xm -xb -xb -LM -av -av -av -ad -ad -ad -ad -aC -ad -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(173,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -as -ad -Fa -ah -Uv -xm -ad -hT -mQ -av -av -ad -aG -ah -aC -ah -aS -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -aX -ad -ad -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(174,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -as -av -ad -ad -ad -ad -ad -ad -vA -hp -av -av -ad -ad -aM -ad -aR -TG -ad -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -rF -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(175,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -as -av -av -av -as -av -av -av -av -av -av -av -av -as -ad -ad -ad -ad -ad -ad -av -av -ad -ad -aP -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(176,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -ad -ad -ah -af -ah -ah -ar -ah -ah -ah -ad -ad -av -av -av -av -as -av -as -av -av -av -av -av -av -av -as -av -av -av -av -av -av -xQ -xQ -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(177,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ad -ah -ah -ah -ah -ah -oY -Xz -ah -ah -Ap -ad -av -av -av -av -av -as -av -av -av -HJ -av -av -av -as -av -av -av -av -av -av -av -xQ -xQ -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(178,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -af -ah -ah -ah -ap -ah -ap -ah -ap -YF -ar -ad -av -av -av -av -av -av -as -av -av -av -as -av -av -av -as -as -av -av -HJ -as -av -xQ -xQ -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(179,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ad -CA -ai -ah -ai -ah -ah -ah -ai -ah -at -ad -av -av -av -av -av -av -as -av -av -av -av -av -av -av -as -av -av -av -av -av -av -xQ -xQ -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(180,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ag -KL -ak -ah -ah -jr -jr -jr -ah -ap -ah -au -as -av -av -av -av -av -av -as -aO -av -av -av -av -av -as -av -av -aO -av -av -av -xQ -xQ -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(181,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -ad -ah -xN -oP -ah -ah -jr -Zr -jr -ah -ap -ah -Sp -as -av -av -av -av -ad -ad -ad -ad -aC -ad -ad -av -ad -ad -ad -aC -ad -ad -ad -av -ad -ad -aP -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -av -ad -ad -ad -av -av -av -av -av -rF -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(182,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -ad -ag -ah -aj -ah -ah -jr -jr -jr -ah -ap -ah -au -as -av -av -av -av -ad -aG -ad -rk -ah -aE -ad -av -ad -aG -ad -ah -ah -aE -ad -av -ad -ad -QI -QI -QI -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(183,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -ad -ad -ah -ai -ah -ai -ah -ah -ah -ai -ah -at -ad -av -av -av -av -av -ad -TF -ad -ah -ah -aJ -ad -av -ad -us -ad -aR -ah -aJ -ad -as -ad -ad -ah -ah -ah -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(184,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -ad -af -ah -ah -ah -ap -ah -ap -ah -ap -ah -ar -ad -av -av -av -av -av -ad -ah -aC -ah -ah -ah -ad -av -ad -ah -aC -ar -ah -CA -ad -av -ad -ad -ah -ah -ah -aY -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(185,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -av -av -av -av -av -ad -ad -ah -ah -ah -ah -ah -ah -ah -ah -BH -CA -ad -av -av -av -av -av -ad -ah -ad -ah -ah -aS -ad -av -ad -ah -ad -BH -ah -aS -ad -av -ad -ad -ON -aJ -ah -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(186,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -av -av -av -av -av -av -ad -ad -ah -af -ah -jd -ar -YF -ah -ah -ah -ad -as -av -av -av -av -ad -ad -ad -ad -aC -ad -ad -as -ad -ad -ad -aC -ad -ad -ad -av -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(187,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -av -av -av -av -av -av -ad -ad -al -ad -ad -ad -ad -ad -ad -as -av -as -as -av -av -av -av -av -av -as -av -wl -Vh -HJ -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(188,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -ad -ai -ah -ah -ah -ad -ad -av -av -av -av -av -as -av -av -av -av -av -as -av -av -av -kg -av -av -qX -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(189,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -ad -am -ah -ah -nZ -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -av -av -Zt -av -av -av -kg -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(190,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -av -av -av -av -av -av -av -ad -ai -ah -ah -aT -ad -av -av -av -av -av -av -av -av -av -av -av -ad -aG -aN -ad -av -av -Vh -ZZ -av -wP -Vh -av -Wk -Hb -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(191,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -aC -ad -ad -ad -ad -av -HJ -av -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(192,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -av -av -av -av -av -av -av -av -av -av -an -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ah -ah -mA -ah -aS -ad -av -av -av -AQ -ad -WC -aI -RL -aI -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -ad -ad -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(193,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -ad -xT -ah -aF -ah -ah -ad -aO -av -av -av -aC -ah -Qv -ar -ah -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -ad -ad -ad -av -av -av -av -av -aq -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(194,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -rH -ah -ah -ah -ah -aC -av -av -av -av -ad -nY -zB -Hy -ah -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -rF -av -av -av -av -av -av -ad -av -av -ad -ad -ad -av -ae -av -av -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(195,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -ad -Or -hH -ah -GW -FM -ad -av -av -av -as -ad -mp -Se -hY -Ci -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(196,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -aI -ah -aI -ah -XH -ad -av -av -av -ad -ad -ad -aC -ad -ad -ad -as -av -as -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -ao -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -aq -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(197,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ax -vM -ah -aE -ah -aE -ah -aJ -ad -av -av -av -av -Yt -Yt -av -Yt -Yt -av -av -as -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(198,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ah -UP -ah -aJ -ah -aJ -ah -ah -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -aY -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(199,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ah -az -ah -ah -ah -ah -ah -ah -ah -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(200,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -av -av -ad -av -av -av -av -av -av -av -av -av -av -ad -ah -aA -ah -ah -ah -ah -ah -ah -ah -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(201,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -ad -ah -ah -ah -aI -ah -aI -ah -aI -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(202,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -ad -ay -aB -ah -aE -ah -aE -ah -aE -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -av -av -av -av -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(203,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -ad -ad -ad -ad -aJ -ah -aJ -ah -aJ -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(204,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -av -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(205,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ad -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(206,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(207,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(208,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(209,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(210,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(211,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -ac -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(212,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(213,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(214,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(215,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(216,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(217,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(218,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(219,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(220,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(221,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(222,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(223,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(224,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(225,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(226,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(227,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(228,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(229,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(230,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(231,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(232,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(233,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(234,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(235,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(236,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(237,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(238,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(239,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(240,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(241,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(242,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(243,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(244,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(245,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(246,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(247,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(248,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(249,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(250,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(251,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(252,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(253,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(254,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -"} -(255,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadaeaUavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadavavavavavavavadadadadadadadadadadadavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavadadadadadadadadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadaUaUavadavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavadadadadadadadadadadavadmIsOovszXradadadadavavadavavavavavavavavavavadadadadadadadadavavavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavadadadadadadadavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadavavavadhmXrXrXrXradavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavadadadadadadadadavavadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaYadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadavavavadLjXrXrXrKradavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadadadadadavavadadadavavavavavavavavavavaoavavavavavavavavavavavavavavavavavadavavavavavavavavavavavUyavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadavavavavadXrXrXrXrpeadavavavavavavavavavavavavavavavanavavavavavavavavavanavavavavavavavavavadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavadadadadadadadadadadadadadadadavavavavavavavavavavavavavavavadadadadadadadavavavavadGoGoXrXrXradavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadavavavavavavavadadadavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavadadadadadavavavavavadadadadaYadadavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavadavavavavavavavavavavavavaoavavavadavavavavavadadavavavavavavavavavaqavavavavavavavavavadavavavavavavavavavavavavavavavadadadadavavavavavavavJQrGavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavadadavavavavadadavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavasadavavavavaoavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavUyavavavavavavavavavavavadavavavavavavadavavavadadadadadadadadadadavavavavavavavavavadavavavavavavavavavavavavavavogogavavasavavavLvavavavavavavavavavavavavavavavavadadafadagahagadafadadadadadadadavavavavavavanavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavaqavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavadavavavavavadavavavavavavadavavavadadavavavavavavavavavadadavavavavavavavadavavavavavavavavavavavavavogSwSwSwucavSwLvavavavWmahavavLBavavavavavavavavavadadahahCAKLxNceahahahadadaiamaiadavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadadadavavavavadavavavavavavavavadadavavavadavavavavavavavavavavadadavavavavavavahahavavavavadavavavavavavadavavavavavavavavavavavavnJNVSwSwSwavavavavEdavahahadLBavavavavavavavavavavavadahahahaiakoPajaiahahahalahahahadanavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavadadadavavadavavaoavavavadavavadavavavavavavavavavavavavavadavavavavavavahahahahavavavavadavavavavavadavavavavavavavavavavavavnJNVSwZpSwavavSyasavavavahahaoavavavavavavavavavavavadafahahahahahahahahahafadahahahadavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavadavadavavavavavavadavadavavavavavavavavavavavavavavadadavavavavavahahadadahahavavavadavavavavavadavavavavavavavavavavavavnJNVSwSwSwanavavavSwavaneMadavavavLBavavavanavavavavadahahapaiahahahaiapahahadahnZaTadavavavavadadadadadadadadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadadavavavavavavavavavavavadadavadavadavavavavavavavavaoavavavavavavadadavavavavavavahawadadahahavavavadavavavavavadavavavavavavavavavavavavavogSwAPSwavavSwavavasavLvahavavavavavavavavavavavavadahahahahjrjrjrahahahjdadadadadadavaoavavadaxahahahahayadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavadadadavavavavavavavavavavavavavavadavavavavavavavavavavadavavavadadadavadadavadadadadadavavavavadavavavavavavavavavavadadavavavavavavavahahahahavavavavadavavavavadavavavavavavavavavavavavavavavogogavavavavavavavavahahavasavavavavavavavavavavadaroYapahjrZrjrahapaharadadavavavasasavavadvMUPazaAahaBadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadadadadadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadadavavavadavadavadadadadavavavavavavavavavavavavadadavavavavavavavahahavavavavadavavavavavadavavavavavavavavavavavavavavavavasavavasavanavLvavasavavLBavavavavavavavavavavadahXzahahjrjrjrahahahYFadavavavavavavasadadahahahahahahadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadadavmcadavavavavavavavavavavavavavavavadadavavavavavaoavavavavavavavavavavavavavavavadavavavadavadadavadavavavavavavavavavavadadavavavavavavavavavavavadadadavavavavadadavavavavavavavavavavavavavavavavasadavavavavavavavavavasavavavavavavavavavavavadahahapaiahahahaiapahahadavavavavavavavadaIaEaJahahaIaEaJadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavaoadavavavadadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavadavadadavadavadavadadavavavavavavavavavavadadavavavavavavadadadadadavavavavavavadavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavadahahYFahapapapahahBHahasavavavavavavavadahahahahahahahahadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadavavavavadadadavavavavavavaoavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadadadavavavavavavadadavavavavavavavavavavadadadavavavaqavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavadadadadadasadasasasavavavavasavavavavavavavavadadAparatahahahatarCAahavavavavavavavavadaIaEaJahahaIaEaJadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadavadadadadadadadadavavavavavavavavavavavavavavavavadavavavavavavavavadavadavavavavavavavavavavadavavavavavadavavavavavavavavavavavadadadadavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavadadaGadlDaSxXxbadavasasaoasavavavavavavavavavasadadadadauSpauadadadadasavavavavavavavadahahahahahahahahadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavTLavjYavavavadadadadadavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavadadadavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavadadaNadahahahahaCavavaoasHJavavavavavavavavasavavavavavasasasavavavasasasavavavanavavadXHaJahahahaIaEaJadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadadadadavavadadadadadavavavavavavavavaqavavavavavavavavavavavavavavadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadavadadavadadadadadavavavavavavavavavavavavavavavavavavavavavavadadaCadarahahcbadaOavavavavasavavavavavavasavavavavavavavavavavavavavavavavavavavavavadadadadahahadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadadadadadadadadadadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavadavavavavavavavavavavavavavavavadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavadadahahahagagahadavavavavavavadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadahahaIEuhRaJadavavavavavavadJHahwfVpNEFaadasavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadadadadadavavavavavavavavavavavavavavavavavavavavavavavavadavavadavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavadMYahmlahiCahadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadadadadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadLEahZrMOxmUvadavavavavavavadadadadadadavavadadadadadadadadavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadQZahxmiCxbxmadavavavavasavadaGTFahahadavasadaGadahxTrHOradYtavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadadavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadadadadadxbadadavavavavavasadadadaCadadasavadaNaCahahahhHadYtavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavavavEpavaPLMhTvAavavavavavaOadrkahahahadavavadadadmAaFahahaCavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaEyHimimgqahadavHJasasavavavavavavavavmQhpavavHJavavavaCahahahahaCwlavavavadahahahGWadYtavavavanavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadwWahahahahahadasavavavHJavavavavavavavavavavavavasavavadaEaJahaSadVhkgavavadaSahahFMadYtavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavaqavavavavavavavaqavavavavavavavadadavadadavavavavavavavavavadavavavavaqavavavavavavavavavavavavavadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadrcahahYJarjdadavavavavavasavavavavavavavavavavavavavavadadadadadadHJavZtVhadadadaCadadavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavadavavavavavavadadavavavavavavavavadavavavavavavavavavavavavavadavadadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaEahahaEEHahaCavavavavavavadadadadadadadadasavavavavavavavavavavasavavavZZavavaOavavasavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavaoavavavavavavavavadavavavavavavavavavavadavavavavavavavavadadadavadadavadavavadavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadnLarahaEahahaCavavavavavavadaDaEaFahadaGadadavasavavavadadadadadadavqXavavHJavavavavavasavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavadadadadadadadavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadnDBHYFRkahahadavavavasavavadahApahjXadahaMadasavasasasadaGusahahadavavavwPavavavavavasavavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavadaroYXzahadaCadadavavasavavadadadaCadadavavkgVhavAQavavasavavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavIoavavavadaharYFahaCahaRadavavavavavaCahaRarBHaCavavavavadadaCadadadavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavVhyravavadahahahahadaSTGadavavavavaOadahahahahadavavavavadahahahbQadavavanavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavHJavavavavadadadaCadadadadadavavHJavavadaEaJCAaSadavavavavadaIahzBSeadavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrXavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavaOavavavavavavavavasavavadadadadadadavavavavadRLarHyhYadavavavavavadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavadavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavadadadadadavavavavavavavavavavavavavavavavavavavavavavavasavavavavavavavadaIahahCiadavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadadadadadadadadadadadadxQxQxQxQxQadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadadadadadadadadadxQxQxQxQxQadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaPavavavavavaPQIahahONadavavavavavavavavavavavavavadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavQIahahaJadavavavavavavavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadavavadavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavQIahahahadavavavavavavavavavavavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRURURURURURURURURURURURURURURURUavavavavavavavavavadavavavavavavavavavavavavrFavavavavavavavavavavavavrFavavavavavavrXavanavavavavavavavavavavavaoavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaYadadavavavavavavavavavavavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUBTahYjRUInInahahvqCovqahahkTahRURUavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrXavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUKsUvahRUInUviCahahahahahUvahahiCahRURUavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUaEaJnFDsahahCzahCziCCzahCzahCzahahahahRURUavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUaEahahRUahiCahahahahahahahahahahahRURUavavavavavavavavavavavavrXavavavavavavavavavavrXavavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUTdUvwxRUahUvahahahahahRNahahahRURUavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRURURURURURURURURURUgBaVaVRURURUavavavavadadadavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavuzavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavSSavavavavavavavavavavavaqavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavuzavavavLBavadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavLBavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavuzavavavavavavLBavavavavavavavavavanavavavavavavavavavavavavrFavavavavavavavavavavavavavavavaUaUaUavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavanavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaUcxaUavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavadadavavadavavavavavavavaUaUaUavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavLBavavavavLBavavavavavavavanavavavavavavavavavavanavavavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavanavavavavavavavavavavavavaoavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavrFavavavrXavavavavavavavadavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavSSavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadavavavavavavavavavavavavavavavavavavadavavavavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadadadavavavavavavavadadavavavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavaqavavavavavavavSSavavavavavavavadavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavadavavadavadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaqavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadavavavavavavavavavadavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavadadavadadavadavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavadavavavavavavavavadadavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavadavavavavavavavavavavavavavavadavavavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavadavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavadavavavavavavadadavavavrXavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavadavadavavavavadavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadavavavavavavavavavavavavavavavavavadavadavavadavavadavadadavavavavavavavavavavavavavavavavavavavavadadavadadavavavavavavavadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavadavavavavavavavavavavavavavavavavavavadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadavavavavavavavavavavavavavavavavadadavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadavadavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadavavavavavavavavavavavavavavadavavavavadavavavavaqavavavavadavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavadavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavadavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadavadavavavavavavavavavavavadavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavadadadavavavavavavavavavavavadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavadavavavavavavavavavavadavavadavavavavavavavadavadadadavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavadadavadadavadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadadavavavavavavadadavavavavavavadavadavavavavavavavavavavavavavavSSavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavadadavavavavadavavadavadavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadavavavavavavavavavavrFavavavavavavavavavavavadavavavavavavavavavavavavadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavadadavavavadadadavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavTIavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavadavavavavavavavavavavavavavavavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavadavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavadavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavadadadadadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavaoavavavavavavavavavavavavavavavavadavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavavavadavavavavavavavavavadavavavavavavavadavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavadadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavadavavavavavaoavavavavavadavavavadavavavavavavavavadadadavadadavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavanavavavavavavavavavavavavavavavadavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavadadadavavadavavavadavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavTIavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavadavavavavavavavavadavavavavavavavavavavadadadavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavaoavavavavuzavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadavavavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavadavavadadadadadadadavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavaoavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavanavavavavavavadadadavavuzuzuzavuzavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavadavadavadavadadadavadavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavadavavavavavavavavavadadadavaUaUaUavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadaoavanavavavavavavavavavuzavavavavavavavuzHJavuzavavavavuzuzavuzavavavavavavavavavavavavavavavadavavadavavadavavadavavavavavavavavavavavavadavadavadadavavavadadavavavavavavavavavavavavavavavavavavavhDhDavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavadadadavaUcyaUavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadavavavavavavaoavavavavuzavavuzuzuzavavavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadadadavavavavavavavavavavavavavavavavavavavavXrYoYoogavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadavavavavavavavavadadadavaUaUaUavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadaoavavavavavavavavavavavuzuzavavavavavavavavavavavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavaoSwSwSwMbMbavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavadavavavavavavavavadadavavavavavadadavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadHJavavavHJavavavavavavavavanavavavaoavavavavavavuzavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavTIavavavogAmSwSwSwAmogavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadadadadadavadadadavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadavavavavavavavavavavavuzavavavavuzavavavuzuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavSwSwSwSwSwogavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavadadadadavavavaYavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadHJavaoavavavuzavavavavuzavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavSwEdQMSwSwogavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavadavavavavavavavavavadadadadadadadadadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadadHJavavavavuzavavavavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavogSwSwEdSwSwogavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadadadadadadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadHJavavavaoavHJavavaoavavHJadadxQavuzuzavavavavuzaoavavavuzavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavogAmSwSwSwEdogavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadaoavavavuzuzavavavanavavavavavavxQxQavavavuzavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavogSwSwFNSwAmogavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadHJavavavuzuzavavavavavavuzavavavavxQxQavuzavanavuzavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrFavavadavavavavavavavavavavavavavavavavavavavogSwEdSwEdSwogavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavuzavavavanavavavavavuzavavavadxQuzavavuzavavavuzavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoogkPkPkPogavavavavavavavavavavadavavavavavavavavavrFavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavuzavavavavavavavavavavuzavavadadHJavavavavavavavavuzavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavaqavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadHJanavavavavHJanavavavuzuzuzavuzuzavaoadadaoavuzavavuzavHJavavuzavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavaoavavavavavavavavavavavadavavavavavavavavavavavavadadadavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavuzavavavavavavavavavavavuzavavavavadadavavavavuzuzavuzavavavavavavadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavadadadavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadaXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavuzuzavuzavavavavavuzavavavuzuzavuzavHJadHJavavavavavavavavavavavavavadavadadavavadadavavavavavavavavavavavavavavavavavavavavavadavavavadavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadaoavavavuzavanavavRlavavuzavavavavavavavavadadavavavavavaoavuzavavaoavavadavavHJsFHJavadavavavavavavavavavavavavavavavavavavavavavadavadavadavadavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavuzavavavavavavanavuzavavavavadadavavavavavavavavavavavavadadavavavavavadadavavavavavavavavavavavavavavavavavavavavavadavavavadavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavaqavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavuzavanavuzavavavavavavavavavuzuzuzavuzavHJadHJavavuzavuzavavavavavavadadadadavavavadavavavavavavavavavavavavavavavavavavavavavavavadadadavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavanavuzavavavanavuzavuzavavavaoadadavavavavavavavuzavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavaqavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadHJavavuzavuzavuzavavuzuzavavavavavavuzavavavadadaoavavavavavavavavavavavavavadadadadavavavavavavavavavavavavavavavavavavavavavavadavavSSavavadavavSSavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadaoavavavavavuzavavuzavavavHJavanavavavuzavadadadavavavuzavavavuzavavavavavavadadavavavavavavavavavavavavavavrFavavavavavavavavadadavavavavvNavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadadadadadavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadavavuzavanavavavavavavavavavavavuzavavavadadadHJavavavavanavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavvNlZvNavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadHJavavavavavavavavanavavavuzavuzavavavHJadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadadadavvNavadadadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadavavavuzavuzuzavavuzavavavavavavavaoadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadadadadadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadaoavavavanavavavavavavanavavavaoadadadadadadadadaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadHJavavaoavHJavanavaoavHJadadadadadadadadadadadaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaYGYGYGtNoHYGYGYGYGYGNTYGixrxPENTNTrxFiYGQNYGYGIZvrvroHYGYGYGtNfABMfABMvnkQYGYGYGfABMmwteaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafAYGYGYGvkYGYGvkYGYGvkfABMNTNTNTPEYGtoAaYGYGYGBvvrFTvkfAYGYGYGYGERYGERQRFTvkYGvkYGERQRBmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaYGYGlXBYSzYGYGBYqxBYBYBYXLFFNetoNTQhBYBYBYXLXLvrvrUKYGYGYGlXBYqxvrqxvrvrsZBYvklXqxvrvrtcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaXLxIjivrvrXLBYVRvrvrsZjiVRvrvrWQNewOvrjiGjvrvrjijivrBYXLxIjivrvrsZvrsZjiVRjiWSBvvrsZjiyqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGjVRvrjivryJvrvrsZYsGjvrjivrWQvrWQGjjivrvrjisZjivrjivrGjVRvrjisZvrsZvrsZvrWQEjIZsZvrsZvraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaavrvrGjvrvrvrjiORvrwOvrWQvrWQqUvrqUvrwOvrWQvrvrGjOYwOWQvrvrGjvrvrLVvrLVvrjiwOYGBvvrLVvrvkaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxDvrGjjiOYsjLzeSQJtMxZfAzkxDfAfAfAxZfAeStMzkxZfAQJfAeSxDvrGjjijivrjisZHfwOeSvkIZjisZHfDVaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaYGkHvrLVXUvkqovrvrvrvrvrvrvrvrWQvrLLJjJjJjJjJjHNvrLLJjJjJjJjJjJjJjJjJjJjJjHNYGBvjivrvrvraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazksZvrvrFTvkfAvrvrvrvrvrvrvrvrvrvrZoCMCMCMCMCMZovrZoCjVPspBnYRpdvrvrZoUUvOZovkKtvrjijiAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatMwOjijiFTyqAavrvrvrvrvrvrvrWQvrvrZoCMCMCMCMCMZovrZoPmvrpdvrOJvrvrvrYRvrBAZoBYvrvrORvreSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazkkHjiyJFTYGqovrHYtiVatiULvrvrvrvrZoCMCMopCMCMZovrZoJjJjJjJjHNvrvrvrOJvryDZoHfjisZvrYskHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafAGjORLVjiqxlevrPWoyZUGZPWvrvrvrvrZoCMCMCMCMCMZovrZovrvrsNZGZoOJOJOJLLJjJjJjvrOYvrjivrYGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxZvrvrGjjiOYPpvrPWNsWqoGPWvrvrvrvrYRCMCMCMCMCMYRvrZovrvrvrvrZovrvrvrZoisvrZoYsvrWQWQjiAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatMjijivrjivrrAvrpDoGoGoGpDvrvrvrvrvrvrvrJdvrvrvrvrZovrvrvrvrZovrvrvrZovrvrZoINjiORYsjiYGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazkwOvrjivrFTzwvrpDoGoGoGpDvrvrvrvrvrvrvRvrvrvrvrvrZoOJkEkEOJZovrvrvrZovrvrZoLKIZyJvrvrDVokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafAjiOYvrsZJMqovrPWEXoGqjPWvrvrvrvrvrvrvrPbPbvrvrvrYRvrvrvrvrYRvrvrvrYROJOJZoEjIZjiLVjiwOokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRxZGjvryJvrAavkvrPWCvCvCvPWvrvrvrvrvrvrPbgDjEPbvRvrOJvrvrJdvrvrvrvrvrvrvrvrZoYGWrjivrvrYGokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRtMjiLVjisZkQObvrYSlHtrvYRQvrvrvrvrvrvrPbuNcDPbvrvrOJvrvrvrvrvrvrvrvrvrvrvrZoXLsZvrjijihPokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRfAvrVRHfvrjisZkQvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrkEJjJjJjJjJjJjJjJjJjkzWNYRvrvrsZvrvrGjokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRzkWQvrjijiHfvrUKvrwOvrWQvrWQwOGjvrvrwOvrWQvrvrGjOYwOWQvrvrGjvrvriUYGiUDVqoYGYsVRYsjivrYGokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRQuvrvrvrvrjivkwJQJtMxZfAzkxDeSxDYGxZfAeStMzkxZfAQJfAeSxDYGAavkvkYGvksZfAeSxDvrvrvrvrvrYGokokokokokokokokokokokokokoktVtVtVtVtVtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRvrvrvrjisZfAeSqxBYBYBYXLFFxIYGYGlXBYBYBYXLXLvrvrUKYGYGYGlXBYqxvrqxvrvrvrsZvrsZjiBYXLxIYGokokokokokokokokokokokokokoktVbGbrbEbFtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRjijiqxvrvrvrsZvrvrsZjiVRvrwOXLxIjivrjiGjxxvrjijivrBYXLxIjivrvrsZvrsZjisZvrsZvrsZjiBYXLxIokokokokokokokokokokokokokoktVqRqRqRbPtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRjivrvrsZjisZvrsZYsGjvrjivrGjvrvrvrxxxxvrvrvrvrvrvrvrGjVRvrjisZvrsZvrsZvrsZvrsZvrsZvrGjVRokokokokokokokokokokokokokoktVyzqRqRxetVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZJZNPqPImrtJZJZJZJZCmIOHpJZNPJZJZJZIuJZCmwwJZJZiuJZJZqbJZiuJZJZJZJZJZYmwwwwznJZMyJZJZCmJZJZznJZJZJZYmznJZJZXLvrsZvrsZvrsZBYqxBYBYBYXLFFYGfAYGvrkHlSlSlSlSlSvrkHYGYGYGlXBYqxvrqxvrvrsZBYvklXqxvrvrtcokokokokokjLjLokokokokokoktVtVtVtVJftVtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZiIfRNZMxNGiIiIiISOfRfRfRYKEMVEVEEMfREMIxiILkfRiIVEEMiIfRNGiIfRfRiIVqLqfRfRiIzZfRfRfRCrfRNGiIVqfRfRfRiIfRJZvrjivrqxvrvrsZVRvrvrsZjiVRvrvrvrvrkHScCfxRScSclSlSvrvrkHvrvrvrvrsZvrsZjiVRjiWSBvvrsZjiyqokokokokokokjLjLokokokokoktVYvgLwbIlbItVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ifSOPLfRfRIaEMVEVEfRzriIiIIauRfRfRzriIiIfREMiIfRzrfRfRiIiIuREMPLEMEMEMVEIpSoEMEMEMVEVEfRfRIxfRfRfRSOEMoCfRJZjijiqxvrvrvrsZvrvrsZjiVRvrvrkHxxkHnUScScScScScSclSlSvrvrvrkHkHvrvrsZjisZvrsZvrsZjiBYXLxIokokokokokokokjLokokokokoktVsxIlIlIlIltVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +zLCrfRIaiIzriIfRuRiIfRiIiIMxfRuRiIfRfRIauRfRuRiIfRiIfRgWfRfRzrfRfRIaiIzrfRfNfRiIzZfRfRiIiIfREMVESoiIfRfRVESukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokokokokjLokokokokoktVtpJEIlbKvetVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NnLkIafRIafRlJMxiIiIIafRfRzriIuRiIfRMxfRuRuRgWIafRfRiIfRgWiIfRIauRzZfRiIfRzZiIfRfRiIIaiIfRiIfRzZzrfRiIfRLqJZkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokokjLjLjLjLjLtVtVtVtVtVtVtVJKtVtVtVokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZCrfRgWfRiIfNfRlJfRzZiIfRfNfRfNiIpufRlJfRfRzZfRhnIdfRzZfRfRnVfRfNfRlJfRlJfRfNfRlJfRfRzZwRfNlJfRfRzZfRfRfRifkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokokjLtVtVtVtVtVqlbRRYzCzCUQzCzhUMtVokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ifLkiIIakqfNgTrgFsMBNPrgFsMBgTrgNPyGFsMBgTFsNPMBFsNPgTwQZbNPgTziFsDbNPMBZbDbNPgTFsMBDbNPziNPgTZbfRzZiIwRhvmYkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokjLjLYiJrCGCGzRzCzCzCzCzCzCzCzCKetVokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZCriIfRfRfRNPHIySySFphIhIhIhIhIhIhIhIhIDRDRDRKjKjKjKjKjKjKjDRDRDRDRDRMqlQDRDRDRDRDRDRDRDRDRMqJZVqfRgWOziIMLkmkmkmkmkmkmkmkmkmfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokjLjLNRYOYOqKtVtVtVtVtVXitVtVtVtVtVtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ifKafRiIiIzZMBpwkOloTPhIhIxvQfhIhIhIhIQfDRbYoBfcWVchcjcjckwdcmcnKkEqKkDRFySsloskaKskGmgpGmskDRMBIafRfRNGiINPkmkmkmkmkmkmkmkmkmfjfjfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjfjfjkmkmkmkmkmkmkmkmkmkmokokokjLjLjLjLjLjLjLoktVfEwScdwSNQSbBNnbIGtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +SufRfRnVfRfVFspwXFloTPhIhIhIhIjNCCGuhIxviLoNUNcococococococococpcqcqcrDRXFloloGmskGmskGmskGmDRFsfNiIiINGzrFlkmkmkmkmkmkmkmkmkmfjfjfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjfjfjkmkmkmkmkmkmkmkmkmkmokokokjLokjLjLjLjLokoktVSXwSwSwSwSwSvhqRqRtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PHiIIafRuRVqNPpwQmloTPhIhIhIhIHQkUWyhIhIDRoNUNcocococococococoDPUNkdEEDRBzloloskGmskGmskGmskDRMBVqiILqNGfRMLkmkmkmkmkmkmkmkmkmfjrvKbOpQYOpQYOpQYOpQYOpQYOpQYOpQYOpQYOpQYOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokjLokokokjLjLokoktVwrwSKUZgncyYwSqRpbtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZwRfRiIfRfRMBpwQWloTPhIhIhIxGctcucvxGhIDRjAUNaLaLaQiTcocoyTiXrTHCRjzQDRGdloloGmskGmaHGmskGmDRNPzZnVgWiIPLNJkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokokokokjLokoktVtVtVtVtVtVtVtVZyoEtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NbfRlJlJiIzZFspwXFloTPhIhIhIhIcwcwcwhIhIDRDRDRDRDRDRDRkfbcDRDRZuMqDRDRDROlloloskGmskGmskGmskDRDbfRfRzZiIwRKSkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpfjOpOpOpOpfjOpOpOpOpfjOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokokokjLjLokokokokokokokokoktVtVtVtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +cgiInVuRiIfRMBpwkOloTPhIhIhIhIhIhIhIhIhIURaWbWaZbdbabeBqBqbhbibjbbtFoeDRXFloloGmskGmskGmskGmDRFsiIiIfRiIfRuEkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpfjOpOpfjfjfjOpOpfjOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokjLjLjLokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +PHLkLqfRfRfNDbbwPuPuBRhIhIhIhIbBhIhIhIhIhIhIhIgJbnlolololololovZBqBqbXbdEBloloskGmskGmskGmSLDRMBfNfRiIfRNGEZkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpOpfjfjfjfjfjfjfjOpOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokjLokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NnLkiIgWiIfNNPPoPoPoPoPoPobDhIhIhIhIhIhIhIhIhIbobplobMlolololobJlolobyDRbMlololololololololoDRNPiIwRfRIaChMLkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpfjbxfJLukybxfjOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokjLjLokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZbHiIfRfRfRMBDRDRDRDRDRDRDRhFhIhIhIhIhIhIhIhIbobqbsbtbubvbAbCbkblbmbzDRXFbMvmlolovmbSWLXFWLDRDbzZfRLqfRzZifkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpfjfjmVOpXaOpmVfjfjOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokokjLokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dhIafRiIiIWegTDRVwVwVwVwVwDRwchIhIhIhIhIhIhIhIhIZEZEZEZEZEZEZEZuDRDRDRDRDRDRDRCFloDRDRDRDRDRDRFsiIgWiIIabOXGkmkmkmkmkmkmkmkmkmfjfjfjZlOpOuOpfjfjfjbxXafdXabxfjfjfjOpOuOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokokjLjLokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZfRIafRfRzZNPDRlololololoDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIOTbfPubNDmCEAoURURURURURURURURURNPfRzrkqfRiIRVkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpfjfjmVOpXaOpmVfjfjOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NbzruRiIfRuRMBDRDRDRloloDRDRbThIhIhIhIhIYBhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIxvQfhIhINPlJfRiIiIkqJZkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpfjbxOpOpOpbxfjOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZfRVqLqASnlFsDRXZAXloloYYDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIBchINnfRfRfRfRiIifkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpOpfjfjfjKbfjfjfjOpOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZiIfRNZMxlJNPDRDRDRclloPzDRlahIhIhIhIhIhIhIhIhIyBhIhIhIhIhIhIhIhIhIhIbBhIhIhIhIhIhIhIhIhIhIhINnVqfRgWOziIMLkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpfjOpOpfjKbfjOpOpfjOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ifSOPLfRfRnVMBDRXZAXlololobZwchIhIhIhIhIhIhIhIhIhIhIFbhIhIhIhIhIhIhIhIhIhIcJhIhIhIhIhIhIxvhIhINPIafRfRNGiINPkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpfjOpOpOpOpKbOpOpOpOpfjOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +zLCrfRIaiILqFsDRDRDRcllolocawchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIBchIMBfNiIiIrDzrFlkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NnLkIafRIaiIMBDRXZAXlolorVDRlahIhIhIhIhIhIhIhIhIFbhIcZhIdbhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIQfgTVqiILqNGfRMLkmkmkmkmkmkmkmkmkmfjrvKbOpcHOpcHOpcHOpcHOpOpOpcHOpcHOpcHOpcHOpKbrvfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZCrfRgWfRiINPDRDRDRcllobUDRwchIhIhIhIhIQfhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIFbhIFbhIAWloMGnoccfRfRzZiIwRKSkmkmkmkmkmkmkmkmkmfjfjfjKbfjKbfjKbfjKbfjOpOpOpfjKbfjKbfjKbfjKbfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ifLkiIIakqfRDbDRcfqfloloJBDRwchIhIhIhIhIxvhIhIhIhIhIFbhIhIhIYBhIhIhIQfxvhIhIhIhIhIhIhIgJloloODNPiIiIfRiIfRuEkmkmkmkmkmkmkmkmkmfjfjfjrvfjrvfjrvfjrvfjOpOpOpfjrvfjrvfjrvfjrvfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NPfRgWOziIIaFsDRDRDRxHlohBDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIFbhIFbhIgJloNFODccfNfRiIfRNGEZkmkmkmkmkmkmkmkmkmfjfjfjfjfjfjfjfjfjfjfjFdFdFdfjfjfjfjfjfjfjfjfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +RVfRfRNGiIVqMBDRGRFvloloMwDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhICJhIhIhIhIhIIwhIhIhIhIhIhIgJloloODDbfRfRNGfRdPqbkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmNTNTNTNTsvNTNTNTDGNTcLNTNTNTbgNTkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +IOiIiINGzrzZgTDRDRDRDRDRDRDRwchIhIhIhIhIQfxvhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIFbhIFbhIAWlozgIhNPLkfRiIVEEMifkmkmkmkmkmkmkmkmkmkmkmkmNTNTNTNTNTnENTcLgnNTsvNTcLgnNTnENTNTNTNTNTkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMnTnTnTnTnTnTnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +NPiILqNGfRlJNPZDbVDRddBFNqDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIKMhIhIMBiIfRzrfRfRifkmkmkmkmkmkmkmkmkmkmkmeCeCeCNTeCNTNTDGNTNTHrNTeCcLNTNTNTeCNTNTNTNTNTkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMnTnTvrvrvrvrvrvrvrvrvrvrnTnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +FlnVgWiIPLbLMBhIhIDRyFloloDRdehIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIxvQfNPuRiIfRiIfRmPkmkmkmkmkmkmkmkmkmNTcLNTNTNTNTNTbgNTsvNTcLNTgnNTNTnENTeCNTNTNTeCNTNTNTkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMnTnTvrvrvrBiBiBiBiBiBiBivrvrvrvrvrvrkHnTnTAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +JZfRzZiIwRLYFsJJwiDRemloloEThIhIhIYBhIhIdghIRGhIdghIRGhIdghIRGhIJvQfhIEziKhIhIBchIBchIBchIhIhIFsgWIafRfRiIJZkmkmkmkmkmkmkmkmkmNTNTNTNTcLNToUNTHrNTnEbgNTNTNTHrNTNTbgNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiOiBiBiBiBiBiBiBiBivrvrvrnTAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +ifiIfRiIfRfmNPhIhIDRDRDRDRDRhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIxvhIhIhIhIhIhIhIxvhIhIhIhIhIcczZfRfRIdfRFlkmkmkmkmkmkmkmkmNTNTNTcLNTNTNTrSNTNToUNTiFNTNTNTeCoUNTNTNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMnTnTvrvrBiBiBiBiBiBiBiBiBiBiOiBiBiBiBivrvrnTnTAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +djdjdjdjdjdjdkLJeoLJLJLJLJLJerLJLJGzLJLJLJewLJLJLJLJeyLJLJLJerLJLJLJLJLJeyLJLJeBLJLJeyLJGzLJLJdkdjdjdjdjdjdjkmkmkmkmkmkmkmNTPENTNTeCNTPEcLNTeCNTNTeCeCNTeCeCNTNTNTNTeCNTNTPENTNTPENTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMnTvrvrBijLjLjLjLjLBiokokokBiBiBiBijLBiBivrvrnTnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTeCeCNTNTNTNTNTcLciNTNTrSNTcLNTeCNTPENTcLNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMnTkHvrjLjLBiBiBijLjLokjLokokokBiBiBijLBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTNTymeCcLNTeNNTeCwOvrvrzXNTvrNTNTNTNTNTeCeCeNqTNTeNPENTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTnTvrBiBiBiBiBiokokokokjLjLjLokokBiBijLBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTcLNTNTNTcLNTUlvrvrOYvrvrvrWQvrvrWQNTNTeNeCJFqTNTNTcLNTNTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiokjLjLjLBiBiBijLokBiBijLBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrIDdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrIDdrdrdrdrkmkmkmkmkmkmkmNTNTNTeCNTNTPENTvrvrvrvrvrvrvrvrjivrNTtoNTNTqTNTNTNTNTNTqTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiokokjLBiBiBiwuBijLokBiBiBiBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTcLNTNTeNcLNTvrvrvrNrNrNrNrNrOYvrvreCeCNTNTJFeCNTNTNTNTqTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiokjLBiBiBiBiBijLokBiBiBiBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrIDdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTcLtoqTNTNTtovrjivrCMCMCMCMCMvrvrNTeCNTNTNTeCNTeNeCJFNTNTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiokokjLBiBijLjLokokBiBijLBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTNTJFJFNTeNvrvrvrCMnTVOnTCMvrvrNTNTNTPEtoNTNTNTeCqTeCeCNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiBiokjLBijLokokokBiBiBiBiBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrIDdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTcLNTJFeCeCNTvrvrnTnTAMnTnTvrvrwONTNTqTNTNTtoqTqTqTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBijLBiBiBijLBiBijLokBiOiBiBiOiBijLBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrcsdrdrdrdrdrZWdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTNTNTNTJFNTvrvrvrvrAMAMAMvrOYvrNTNTNTcLPENTeNqTqTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBijLBiBijLBiBiBijLjLBiBiBiBiBiBiBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrkadrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrkadrdrdrdryUdrdrkadrdrdrkmkmkmkmkmkmkmkmtoNTcLNTqTqTNTvrOYvrvrvrvrOYvrvrjiNTNTNTOFNTeNsgqTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrvrBiBijLBiBiBiBiBiBijLBiBiBiBiBiBiBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmkmkmtocLtoozqTqTvrvrvrvrvrvrvrvrvrvrNTNTeCNTNTcLNTNTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrvrBiBiBiBiBiBijLjLjLBijLjLjLjLjLBiBivrkHvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrkayUdrdrdrdrdrdrdrdrdrdrkadrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrkadrdrdrdrdrdryUdrdrdrdrkadrkmkmkmkmkmkmkmkmkmFBNTNTeCeCNTjitoNTNTvrvrvrWQqTqTAIqTeCtoNTNTNTBUNTNTkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrvrvrvrBiBiBiBiBiBiBiBiBiBiBiBiBiBivrvrvrnTnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmkmkmNTNTBUeCeNNTNTcLNTNTeCjivrNTNTqTByqTqTNTeCeCNTNTNTNTkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTNrCwvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrnTnTAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrkadrdrdrdrdrdrdrdrdrdrdrdrcsdrdrdrdrdrdrdrdrIDdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmkmkmtoeCNTNTNTtoNTeNNTNTNTOFeCNTPEeNqTqTNTtoeCcLNTNTBUkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTnTCMCMNrCwvrkHvrvrvrvrvrvrvrvrkHvrvrvrvrnTnTnTAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrBtdryUdrdrdrdrIDdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrBtdrdrkmkmkmkmkmkmkmflkmkmBUeCeCNTfaozeCeCJFJFNTeCNTPEPENTBUNTNTNTNTBUNTNTkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTCMCMCMCMCMNrNrNrNrNrNrNrNrNrNrNrNrNrNrNrnTAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrBtdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBikmkmkmeCeCFBNTNTNTtotoNTNTtotoNTNTPENTBUNTNTNTNTNTkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTCMCMlgCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBiBiBikmkmtoeCeCeCSTNTNTNTcLNTNTNTBUNTBUNTNTNTtokmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrczczczdtdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBikmBikmkmkmkmkmFBFBNTNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTnTCMCMCMCMCMCMCMCMnTnTSDnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdtczcAcBdtdtdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBikmBiBiBiBiBiBiBikmkmNTFBtoNTkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTnTnTnTnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdsdsdsdsdsyUdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdxcCcCcCdwdxdxdsdsdsdsdsdsdsdsdsdsdsdsyUdsdsdsdsdsdrkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dsdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdudududududtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdskmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dudududududududududududududududududududududududududududtdudududududududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dududududududududududududududududududududududududududududududududtdududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dududududududududududududududududududududududududududududtdududududududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dududududududududududududududududududududududududududududtdtdtdtdudududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +dududududududududududududududududududududududududududududududududududududududududududududududududududududuLZkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa "} diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 3fcb9cc3410b..83046ce7a494 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -63,7 +63,7 @@ 'sound/ambience/ambidanger.ogg', 'sound/ambience/ambidanger2.ogg', 'sound/ambience/ambimaint.ogg',\ 'sound/ambience/ambiatmos.ogg', 'sound/ambience/ambiatmos2.ogg', 'sound/ambience/ambiodd.ogg') - +#define BEACH_SOUNDS list('sound/ambience/shore.ogg') #define CREEPY_SOUNDS list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heart_beat.ogg', 'sound/effects/screech.ogg',\ 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm index 6a006490f650..1784dfba9b20 100644 --- a/code/game/area/Space Station 13 areas.dm +++ b/code/game/area/Space Station 13 areas.dm @@ -2063,7 +2063,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station icon_state = "beach" dynamic_lighting = DYNAMIC_LIGHTING_DISABLED requires_power = FALSE - ambientsounds = list('sound/ambience/shore.ogg', 'sound/ambience/seag1.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/seag2.ogg', 'sound/ambience/ambiodd.ogg', 'sound/ambience/ambinice.ogg') + ambientsounds = BEACH_SOUNDS + +/area/awaymission/beach/entrance //this extra area here is to cause the ambiance to play since going through gateways doesn't count as moving to a new area. /area/awaymission/beach/boundry @@ -2073,7 +2075,9 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/awaymission/undersea name = "Undersea" icon_state = "undersea" + ambientsounds = RUINS_SOUNDS +/area/awaymission/undersea/entrance //same with the beach entrance area. ////////////////////////AWAY AREAS/////////////////////////////////// diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm index baf32ab742c6..4a5948021792 100644 --- a/code/game/machinery/dance_machine.dm +++ b/code/game/machinery/dance_machine.dm @@ -483,3 +483,11 @@ playsound(src,'sound/machines/terminal_off.ogg',50,1) icon_state = "disco0" stop = world.time + 100 + +/obj/machinery/disco/immobile + name = "radiant dance machine mark V" + desc = "After repeated theft incidents with previous models, the MKV is rendered completely immovable, its stand made from plastitanium and connected to a large concrete foundation underground." + anchored = TRUE + +/obj/machinery/disco/immobile/wrench_act() + return diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 59cd21f0382c..8adfad7d274f 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -132,5 +132,36 @@ /obj/machinery/door_control/brass/beach_brass_temple_switch/Initialize() . = ..() - id = pick(list("brassbeachtempledoor1", "brassbeachtempledoor2", "brassbeachtempledoor3", "brassbeachtempledoor4", "brassbeachtempledoor5", "brassbeachtempledoor6", "brassbeachtempledoor7", "brassbeachtempledoor8", - "brassbeachtempledoor9", "brassbeachtempledoor10", "brassbeachtempledoor11", "brassbeachtempledoor12", "brassbeachtempledoor13", "brassbeachtempledoor14", "brassbeachtempledoor15", "brassbeachtempledoor16")) + id = "brassbeachtempledoor[rand(1, 12)]" + +/obj/machinery/door_control/brass/beach_brass_temple_switch/attack_hand(mob/user as mob) + . = ..() + var/temple_traps = rand(1,10) //no forbidden temple is complete without some traps! + switch(temple_traps) + if(1,5)//You're safe, this time. + return + if(6,7) + new /mob/living/simple_animal/hostile/poison/giant_spider(get_turf(src)) + visible_message("A hatch opens above you and a giant spider falls down on your head!") + playsound(get_turf(src), 'sound/effects/bin_close.ogg', 200, TRUE) + if(8,9) + addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, user.loc, -1, rand(1,5), rand(1,5), rand(1,5), rand(1,5), 1, 0, 2), 50) + playsound(get_turf(src), 'sound/mecha/powerup.ogg', 200, TRUE) + visible_message("A high pitched whine can be heard and the walls looks to be heating up!") + if(10) + for(var/mob/M in range(5, src)) + shake_camera(M, 15, 1) + visible_message("The ground begins to shake and roaring machinery can be heard! RUN!") + addtimer(CALLBACK(src, .proc/temple_collapse), 50) + +/obj/machinery/door_control/brass/beach_brass_temple_switch/proc/temple_collapse() + for(var/mob/M in range(20, src)) + shake_camera(M, 15, 1) + playsound(get_turf(src),'sound/effects/explosionfar.ogg', 200, TRUE) + visible_message("The brass floor collapses and forms a massive pit!") + for(var/turf/T in range(4,src)) + if(!T.density) + T.TerraformTurf(/turf/simulated/floor/chasm/straight_down/lava_land_surface) + qdel(src) + + diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index 9fd8c789df72..5b8443549c4b 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -141,5 +141,4 @@ /obj/machinery/door/poddoor/brass/beach_brass_temple/Initialize() . = ..() - id_tag = pick(list("brassbeachtempledoor1", "brassbeachtempledoor2", "brassbeachtempledoor3", "brassbeachtempledoor4", "brassbeachtempledoor5", "brassbeachtempledoor6", "brassbeachtempledoor7", "brassbeachtempledoor8", - "brassbeachtempledoor9", "brassbeachtempledoor10", "brassbeachtempledoor11", "brassbeachtempledoor12", "brassbeachtempledoor13", "brassbeachtempledoor14", "brassbeachtempledoor15", "brassbeachtempledoor16")) + id_tag = "brassbeachtempledoor[rand(1, 12)]" diff --git a/code/game/machinery/tcomms/presets.dm b/code/game/machinery/tcomms/presets.dm index efc98866f628..fa7c0acd1b7d 100644 --- a/code/game/machinery/tcomms/presets.dm +++ b/code/game/machinery/tcomms/presets.dm @@ -30,3 +30,8 @@ autolink_id = "STATION-CORE" hidden_link = TRUE +/obj/machinery/tcomms/relay/beachresort + network_id = "BEACH-RESORT" + autolink_id = "STATION-CORE" + active = FALSE + hidden_link = TRUE diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index b9929c244058..625b57332f94 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -409,10 +409,10 @@ lootdoubles = 0 loot = list( - /obj/item/clockwork/component/geis_capacitor/fallen_armor = 1, //you got lucky in a bad way - /obj/item/clockwork/alloy_shards/large = 1, - /obj/item/clockwork/alloy_shards/pinion_lock = 1, - /obj/item/clockwork/alloy_shards/clockgolem_remains = 1, + /obj/item/clockwork/component/geis_capacitor/fallen_armor = 2, //you got lucky in a bad way + /obj/item/clockwork/alloy_shards/large = 4, + /obj/item/clockwork/alloy_shards/pinion_lock = 2, + /obj/item/clockwork/alloy_shards/clockgolem_remains = 3, /obj/item/stack/tile/brass = 8, //general loot /obj/item/stack/tile/brass/five =6, /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden = 6, //your prize is a fight yay! diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index e43e35fab3a4..759997d09469 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -71,8 +71,6 @@ /turf/simulated/floor/beach/roughcoastline name = "Coastline" - //icon = 'icons/misc/beach2.dmi' - //icon_state = "sandwater" icon_state = "beach" water_overlay_image = "water_coast" diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm index 0af8e8ffe778..878dcbae9219 100644 --- a/code/game/turfs/simulated/walls_misc.dm +++ b/code/game/turfs/simulated/walls_misc.dm @@ -64,8 +64,8 @@ girder_type = /obj/structure/clockwork/wall_gear baseturf = /turf/simulated/floor/clockwork/reebe var/heated - //var/obj/effect/clockwork/overlay/wall/realappearance //disabled the fancy build overlay because I can't figure out how to get it to go away when deconstructed -/* + var/obj/effect/clockwork/overlay/wall/realappearance + /turf/simulated/wall/clockwork/Initialize() . = ..() new /obj/effect/temp_visual/ratvar/wall(src) @@ -76,7 +76,7 @@ /turf/simulated/wall/clockwork/Destroy() QDEL_NULL(realappearance) return ..() -*/ + /turf/simulated/wall/clockwork/ReplaceWithLattice() ..() for(var/obj/structure/lattice/L in src) @@ -90,8 +90,6 @@ animate(src, color = previouscolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) -<<<<<<< HEAD -======= /turf/simulated/wall/clockwork/dismantle_wall(devastated=0, explode=0) if(devastated) devastate_wall() @@ -111,7 +109,6 @@ O.forceMove(src) return TRUE ->>>>>>> 26bbfb1175fd4ae6e2871af9216ad6669345a52d /turf/simulated/wall/clockwork/devastate_wall() for(var/i in 1 to 2) new/obj/item/clockwork/alloy_shards/large(src) diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm index 19d281916eab..c1fdf984e76e 100644 --- a/code/modules/awaymissions/corpse.dm +++ b/code/modules/awaymissions/corpse.dm @@ -170,7 +170,7 @@ var/facial_hair_style var/skin_tone - var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) + var/list/del_types = list(/obj/item/pda, /obj/item/radio/headset) //make the var's list blank in the spawns you want to start with headsets and PDAs /obj/effect/mob_spawn/human/Initialize() if(ispath(outfit)) @@ -527,13 +527,14 @@ desc = "A small sleeper with a palm tree logo on it. A well dressed humanoid occupant sleeps within." icon = 'icons/obj/cryogenic2.dmi' icon_state = "sleeper" - flavour_text = "You are a beach resort host! Your job is to man the resort and tend to the customers, handle riff raff, keep the resort clean \ - and treating customer's injuries/recovering their bodies should they perish while exploring the ocean. Don't leave the resort unless it is to recover someone's body from the ocean \ - to take them to the station for revival or to do some light advertising for the resort." + description = "You are a beach resort host!" + flavour_text = " Your job is to man the resort and tend to the customers, handle riff raff, keeping the resort clean, and treating customer's injuries.\ + Don't leave the resort unless it is to recover someone's body from the ocean for revival. Feel free to do some light advertizing for the resort but don't spam coms." assignedrole = "Resort Host" id_job = "Resort Host" id_access_list = list(ACCESS_BAR, ACCESS_KITCHEN) outfit = /datum/outfit/resort_host + del_types = list() //this reenables spawn roles to have headsets and PDAs again /datum/outfit/resort_host name = "Resort Host" @@ -550,8 +551,17 @@ backpack_contents = list( /obj/item/reagent_containers/food/drinks/shaker = 1, /obj/item/soap/deluxe = 1, - /obj/item/storage/firstaid/regular = 1, //in case people run off with the stuff to maintain the resort - /obj/item/radio/headset/headset_service = 1)//for some reason can't get things to appear on ears + /obj/item/storage/firstaid/regular = 1) //in case people run off with the stuff to maintain the resort + +/datum/outfit/resort_host/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(visualsOnly) + return + + var/obj/item/implant/exile/E = new(H) + E.implant(H) + + /////////////////Spooky Undead////////////////////// diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index 97b19fb31a07..5357b45e436d 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -60,8 +60,7 @@ new /obj/item/clothing/head/hardhat/white new /obj/item/clothing/gloves/color/yellow if(15 to 16) - new /obj/item/voodoo(src) - new /obj/item/bedsheet/cult(src) + new /obj/machinery/snow_machine if(17 to 18) new /obj/item/wisp_lantern(src) if(19 to 20) From 2ff07794c8dd16be73334ec9606346bde9b1caf6 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Wed, 7 Oct 2020 13:09:08 -0400 Subject: [PATCH 05/10] HY --- _maps/map_files/RandomZLevels/beach.dmm | 71166 +++++++++++++++++++++- 1 file changed, 70171 insertions(+), 995 deletions(-) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index 885cf8e1aec9..1c5af225c602 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -1,998 +1,70174 @@ -"aa" = (/turf/space,/area/space) -"ab" = (/turf/unsimulated/beach/water/deep,/area/awaymission/undersea) -"ac" = (/obj/machinery/poolcontroller/invisible/sea,/turf/unsimulated/beach/water/deep,/area/awaymission/undersea) -"ad" = (/turf/unsimulated/beach/water/deep/rock_wall,/area/awaymission/undersea) -"ae" = (/obj/structure/ladder/unbreakable/dive_point/anchor{id = "volcano_island"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) -"af" = (/obj/structure/constructshell,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ag" = (/obj/structure/chair/wood/wings,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ah" = (/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ai" = (/obj/structure/cult/pylon,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aj" = (/obj/structure/cult/altar,/obj/item/veilrender/crabrender,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ak" = (/obj/structure/cult/altar,/obj/item/book/codex_gigas,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"al" = (/obj/structure/curtain/black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"am" = (/obj/structure/cult/forge,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"an" = (/obj/structure/flora/rock/pile,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"ao" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"ap" = (/obj/structure/chair/stool,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aq" = (/mob/living/simple_animal/hostile/retaliate/carp,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"ar" = (/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"as" = (/obj/structure/boulder,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"at" = (/obj/item/flag/cult,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"au" = (/obj/structure/mineral_door/sandstone,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"av" = (/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"aw" = (/obj/item/toy/eight_ball/conch,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ax" = (/obj/structure/reagent_dispensers/beerkeg,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ay" = (/obj/structure/closet/cabinet,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"az" = (/obj/structure/rack/skeletal_bar/left,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aA" = (/obj/structure/rack/skeletal_bar/right,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aB" = (/obj/structure/rack/skeletal_bar,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aC" = (/obj/structure/mineral_door/wood{tag = "icon-wood";icon_state = "wood"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aD" = (/obj/structure/chair/comfy/teal{dir = 4},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aE" = (/obj/structure/table/wood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aF" = (/obj/structure/chair/comfy/teal{dir = 8},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aG" = (/obj/structure/toilet,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aH" = (/obj/machinery/disco/immobile,/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) -"aI" = (/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (EAST)";icon_state = "wooden_chair_wings";dir = 4},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aJ" = (/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (WEST)";icon_state = "wooden_chair_wings";dir = 8},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aK" = (/obj/structure/sign/poster/official/nanomichi_ad{pixel_y = 32},/turf/simulated/floor/light/colour_cycle/dancefloor_b,/area/awaymission/beach) -"aL" = (/obj/effect/mob_spawn/human/resort_host{dir = 4},/turf/simulated/floor/plasteel,/area/awaymission/beach/entrance) -"aM" = (/obj/structure/sink{dir = 4;icon_state = "sink";pixel_x = 11;pixel_y = 0},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aN" = (/obj/structure/sink{icon_state = "sink";dir = 8;pixel_x = -12;pixel_y = 2},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aO" = (/obj/structure/closet/crate/can,/obj/item/storage/bag/trash,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"aP" = (/obj/item/flag/species/skrell,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"aQ" = (/obj/structure/closet/gmcloset{icon_closed = "black";icon_state = "black";name = "formal wardrobe"},/turf/simulated/floor/plasteel,/area/awaymission/beach) -"aR" = (/obj/structure/mirror{icon_state = "mirror_broke";pixel_y = 28},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aS" = (/obj/structure/bed,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aT" = (/obj/structure/dresser,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aU" = (/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) -"aV" = (/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"aW" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/machinery/atm{name = "Automatic teller machine";pixel_y = 32},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"aX" = (/obj/item/stack/spacecash/c500,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"aY" = (/obj/structure/mineral_door/wood{tag = "icon-wood";icon_state = "wood"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"aZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"ba" = (/obj/machinery/vending/boozeomat{emagged = 1},/obj/structure/sign/poster/official/high_class_martini{pixel_y = 32},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"bb" = (/obj/structure/closet/secure_closet/freezer/fridge/open,/turf/simulated/floor/wood,/area/awaymission/beach) -"bc" = (/obj/machinery/door/airlock/glass{name = "Resort Backroom";req_access_txt = "25"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bd" = (/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) -"be" = (/obj/structure/table/wood,/obj/machinery/vending/wallmed{extended_inventory = 1;pixel_x = 0;pixel_y = 32},/obj/item/storage/firstaid/adv{pixel_x = 8;pixel_y = 8},/obj/item/storage/firstaid/toxin{pixel_x = 4;pixel_y = 4},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"bf" = (/obj/structure/sign/barsign{pixel_y = 32},/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bg" = (/obj/item/clockwork/alloy_shards/clockgolem_remains,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"bh" = (/obj/structure/sign/botany{pixel_y = 32},/obj/structure/table/wood,/obj/item/reagent_containers/food/drinks/shaker,/obj/item/clothing/glasses/sunglasses,/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"bi" = (/obj/structure/sink{pixel_y = 24},/obj/structure/window/reinforced{dir = 8},/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"bj" = (/obj/structure/closet/secure_closet/freezer/meat/open,/obj/structure/disposalpipe/junction{dir = 1;icon_state = "pipe-j2";tag = "icon-pipe-j2"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bk" = (/obj/structure/disposalpipe/segment,/obj/machinery/vending/dinnerware,/obj/structure/window/reinforced{dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) -"bl" = (/obj/structure/closet/secure_closet/freezer/kitchen{locked = 0;req_access = null},/turf/simulated/floor/wood,/area/awaymission/beach) -"bm" = (/obj/structure/table/wood,/obj/item/soap/deluxe,/turf/simulated/floor/wood,/area/awaymission/beach) -"bn" = (/obj/machinery/door/window/eastleft{name = "Resort Bar";req_access_txt = "25"},/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/wood,/area/awaymission/beach) -"bo" = (/obj/structure/chair/stool/bar,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"bp" = (/obj/structure/table/wood,/obj/machinery/chem_dispenser/soda,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) -"bq" = (/obj/structure/table/wood,/obj/machinery/chem_dispenser/beer,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/wood,/area/awaymission/beach) -"br" = (/obj/structure/table/reinforced,/obj/structure/sign/poster/contraband/c20r{pixel_y = 32},/obj/item/storage/secure/briefcase/syndie,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"bs" = (/obj/structure/table/wood,/obj/item/lighter/zippo,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bt" = (/obj/structure/table/wood,/obj/item/paicard,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bu" = (/obj/structure/table/wood,/obj/item/paper_bin,/obj/item/pen/multi,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bv" = (/obj/structure/table/wood,/obj/item/camera,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bw" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/wood,/area/awaymission/beach) -"bx" = (/obj/structure/clockwork/decorative/tinkerers_cache,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"by" = (/obj/machinery/cooker/foodgrill,/turf/simulated/floor/wood,/area/awaymission/beach) -"bz" = (/obj/structure/table/wood,/obj/machinery/reagentgrinder,/turf/simulated/floor/wood,/area/awaymission/beach) -"bA" = (/obj/structure/table/wood,/obj/item/pizzabox/hawaiian,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bB" = (/mob/living/simple_animal/butterfly,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"bC" = (/obj/structure/table/wood,/obj/machinery/pos,/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"bD" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"bE" = (/obj/structure/table/reinforced,/obj/item/paper_bin,/obj/item/pen/multi,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"bF" = (/obj/structure/table/reinforced,/obj/structure/sign/nuke{pixel_y = 32},/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"bG" = (/obj/structure/safe,/obj/item/documents/syndicate/yellow,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"bH" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"bI" = (/obj/machinery/sleeper/syndie,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"bJ" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/wood,/area/awaymission/beach) -"bK" = (/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"bL" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"bM" = (/obj/structure/chair/stool,/turf/simulated/floor/wood,/area/awaymission/beach) -"bN" = (/obj/machinery/vending/cigarette{extended_inventory = 1},/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/sign/poster/contraband/smoke{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"bO" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"bP" = (/obj/structure/table/reinforced,/obj/item/clothing/accessory/stethoscope,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"bQ" = (/obj/structure/table/wood,/obj/item/storage/firstaid/adv,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"bR" = (/obj/item/flag/syndi,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"bS" = (/obj/machinery/newscaster{pixel_y = -32},/turf/simulated/floor/wood,/area/awaymission/beach) -"bT" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"bU" = (/obj/machinery/newscaster{pixel_x = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"bV" = (/obj/structure/closet/crate,/obj/item/harpoon,/obj/item/harpoon,/obj/item/harpoon,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"bW" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/extinguisher_cabinet{pixel_y = 32},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"bX" = (/obj/machinery/cooking/oven,/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"bY" = (/obj/machinery/tcomms/relay/beachresort,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"bZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/wood,/area/awaymission/beach) -"ca" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/simulated/floor/wood,/area/awaymission/beach) -"cb" = (/obj/item/toy/figure/ninja,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"cc" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/stalkybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"cd" = (/obj/structure/bed,/obj/item/bedsheet/red,/obj/structure/sign/poster/official/ue_no{pixel_y = 32},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"ce" = (/obj/item/clothing/suit/hastur,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"cf" = (/obj/structure/toilet/secret,/obj/item/bikehorn/rubberducky,/turf/simulated/floor/wood,/area/awaymission/beach) -"cg" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"ch" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel{dir = 10;icon_state = "green"},/area/awaymission/beach) -"ci" = (/obj/structure/flora/ausbushes/genericbush,/obj/structure/flora/ausbushes/sparsegrass,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"cj" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel{dir = 0;icon_state = "green"},/area/awaymission/beach) -"ck" = (/obj/machinery/hydroponics/constructable,/turf/simulated/floor/plasteel{icon_state = "green";dir = 6},/area/awaymission/beach) -"cl" = (/obj/structure/closet/athletic_mixed,/turf/simulated/floor/wood,/area/awaymission/beach) -"cm" = (/obj/machinery/vending/hydroseeds{extended_inventory = 1},/turf/simulated/floor/plasteel{icon_state = "caution";dir = 4},/area/awaymission/beach) -"cn" = (/obj/structure/disposaloutlet{dir = 4},/obj/structure/disposalpipe/trunk,/turf/simulated/floor/plasteel{icon_state = "bot"},/area/awaymission/beach) -"co" = (/turf/simulated/floor/plasteel{dir = 8;icon_state = "neutralfull"},/area/awaymission/beach) -"cp" = (/obj/structure/disposalpipe/segment,/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel{dir = 1;icon_state = "caution"},/area/awaymission/beach) -"cq" = (/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel{dir = 1;icon_state = "caution"},/area/awaymission/beach) -"cr" = (/obj/machinery/conveyor_switch/oneway{id = "beach disposal"},/obj/effect/decal/warning_stripes/north,/turf/simulated/floor/plasteel{dir = 1;icon_state = "caution"},/area/awaymission/beach) -"cs" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water,/area/awaymission/beach) -"ct" = (/obj/machinery/gateway{dir = 10},/turf/simulated/floor/carpet,/area/awaymission/beach/entrance) -"cu" = (/obj/machinery/gateway,/turf/simulated/floor/carpet,/area/awaymission/beach/entrance) -"cv" = (/obj/machinery/gateway{dir = 6},/obj/effect/baseturf_helper/beach/roughsand,/turf/simulated/floor/carpet,/area/awaymission/beach/entrance) -"cw" = (/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/entrance) -"cx" = (/obj/structure/ladder/unbreakable/dive_point/anchor,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) -"cy" = (/obj/structure/ladder/unbreakable/dive_point/anchor{id = "pirate"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea/entrance) -"cz" = (/turf/unsimulated/beach/water/drop,/area/awaymission/beach/entrance) -"cA" = (/obj/structure/ladder/unbreakable/dive_point/buoy,/turf/unsimulated/beach/water/deep,/area/awaymission/beach/entrance) -"cB" = (/turf/unsimulated/beach/water/deep,/area/awaymission/beach/entrance) -"cC" = (/turf/unsimulated/beach/water/deep/dense,/area/awaymission/beach/entrance) -"cD" = (/obj/structure/table/wood,/obj/effect/decal/cleanable/blood,/obj/item/paper{info = "It was tricky shooting down this dodgy ship but this fancy metal will be worth a shit tone of credits on the black market. I can't help but wonder what damaged it in the first place though, even wounded the ship was agile. Anyways, this metal is damn tough to take apart so I'm thinking we set up shop here and maybe make this into a hideout once we're done.";name = "Captain's note"},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"cH" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_y = -32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"cJ" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"cL" = (/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"cZ" = (/obj/structure/bonfire/dense,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"db" = (/obj/structure/chair/stool,/obj/item/instrument/guitar,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"dd" = (/obj/structure/dispenser/oxygen,/obj/structure/sign/poster/official/air1{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"de" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/structure/sign/poster/official/safety_internals{pixel_x = -32;pixel_y = 0},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"dg" = (/obj/structure/chair/beachchair,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"dh" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"dj" = (/turf/simulated/floor/beach/roughcoastline/dense,/area/awaymission/beach) -"dk" = (/obj/structure/flora/rock,/turf/simulated/floor/beach/roughcoastline/dense,/area/awaymission/beach) -"dr" = (/turf/unsimulated/beach/water,/area/awaymission/beach) -"ds" = (/turf/unsimulated/beach/water/dense,/area/awaymission/beach) -"dt" = (/turf/unsimulated/beach/water/drop,/area/awaymission/beach) -"du" = (/turf/unsimulated/beach/water/deep,/area/awaymission/beach) -"dw" = (/turf/unsimulated/beach/water/deep/dense,/area/awaymission/beach) -"dx" = (/turf/unsimulated/beach/water/drop/dense,/area/awaymission/beach) -"dP" = (/mob/living/simple_animal/butterfly,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"em" = (/obj/structure/closet/crate,/obj/item/flashlight,/obj/item/flashlight,/obj/item/flashlight,/obj/item/flashlight,/obj/item/flashlight,/turf/simulated/floor/wood,/area/awaymission/beach) -"eo" = (/obj/effect/waterfall{dir = 1;water_frequency = 75},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"er" = (/obj/effect/waterfall{dir = 1;water_frequency = 104},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"ew" = (/obj/effect/waterfall{dir = 1;water_frequency = 44},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"ey" = (/obj/effect/waterfall{dir = 1;water_frequency = 97},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"eB" = (/obj/effect/waterfall{dir = 1;water_frequency = 94},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"eC" = (/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"eM" = (/obj/item/shard,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"eN" = (/obj/structure/flora/grass/green,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"eS" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/palebush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"fa" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"fc" = (/obj/machinery/biogenerator,/obj/item/reagent_containers/glass/bucket,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"fd" = (/obj/structure/clockwork/decorative/relay,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"fj" = (/turf/unsimulated/wall{desc = "A sturdy wall of brass. It feels strangely warm and mechanical sounds can be heard from within.";icon = 'icons/turf/walls/clockwork_wall.dmi';icon_state = "clockwork_wall";name = "clockwork wall"},/area/awaymission/beach/offshore) -"fl" = (/obj/machinery/door_control/brass{id = "brass temple";pixel_y = 32},/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) -"fm" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"fA" = (/obj/structure/flora/rock,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"fE" = (/obj/machinery/vending/syndisnack,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"fJ" = (/obj/structure/table/reinforced/brass,/obj/item/clockwork/weapon/ratvarian_spear,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"fN" = (/obj/structure/flora/ausbushes/genericbush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"fR" = (/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"fV" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"gn" = (/obj/item/clockwork/component/geis_capacitor/fallen_armor,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"gp" = (/obj/structure/sign/poster/official/ue_no{pixel_y = 32},/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) -"gq" = (/obj/machinery/vending/crittercare,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"gB" = (/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) -"gD" = (/obj/structure/table/wood,/obj/item/clothing/head/welding,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"gJ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"gL" = (/obj/structure/table/reinforced,/obj/item/soap/syndie,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"gT" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"gW" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"hm" = (/obj/structure/rack,/obj/item/stack/sheet/plasteel{amount = 20},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"hn" = (/obj/effect/baseturf_helper/beach/dense_roughsand,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"hp" = (/obj/item/clothing/gloves/botanic_leather,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"hv" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"hB" = (/obj/structure/closet,/obj/item/clothing/under/pinkhawaiianshirt,/obj/item/clothing/under/pinkhawaiianshirt,/obj/item/clothing/under/orangehawaiianshirt,/obj/item/clothing/under/orangehawaiianshirt,/obj/item/clothing/under/redhawaiianshirt,/obj/item/clothing/under/redhawaiianshirt,/obj/item/clothing/under/bluehawaiianshirt,/obj/item/clothing/under/bluehawaiianshirt,/turf/simulated/floor/wood,/area/awaymission/beach) -"hD" = (/obj/structure/shuttle/engine/propulsion{dir = 1},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"hF" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"hH" = (/obj/structure/table/wood,/obj/item/storage/bag/plants,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"hI" = (/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"hP" = (/obj/structure/flora/ausbushes/palebush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"hR" = (/obj/structure/table/wood,/obj/item/toy/figure/wizard,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"hT" = (/obj/item/stack/sheet/wood,/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"hY" = (/obj/structure/bed,/obj/item/bedsheet/medical,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"if" = (/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"im" = (/obj/machinery/fishtank/tank,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"is" = (/obj/structure/table/wood,/obj/item/clothing/suit/pirate_black,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"iu" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"ix" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"iC" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"iF" = (/obj/effect/decal/remains/human{plane = -1},/obj/item/melee/cultblade,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"iI" = (/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"iK" = (/obj/structure/chair/beachchair/red,/obj/item/toy/plushie/octopus,/obj/item/clothing/glasses/sunglasses{pixel_x = 2;pixel_y = 6},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"iL" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) -"iT" = (/obj/machinery/photocopier,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"iU" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"iX" = (/obj/structure/table/wood,/obj/item/flashlight,/obj/item/stack/marker_beacon/thirty,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"jd" = (/obj/item/clothing/under/color/brown,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"ji" = (/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"jr" = (/obj/effect/decal/cleanable/blood,/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"jA" = (/obj/machinery/vending/crittercare/free,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"jE" = (/obj/structure/table/wood,/obj/item/reagent_containers/food/snacks/pie,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"jL" = (/turf/unsimulated/floor/lava,/area/awaymission/beach/offshore) -"jN" = (/obj/machinery/gateway{dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"jX" = (/obj/structure/sign/poster/official/love_ian{pixel_x = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"jY" = (/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"ka" = (/mob/living/simple_animal/hostile/retaliate/carp/koi,/turf/unsimulated/beach/water,/area/awaymission/beach) -"kd" = (/obj/structure/chair/stool,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"kf" = (/obj/machinery/smartfridge,/turf/simulated/floor/wood,/area/awaymission/beach) -"kg" = (/obj/item/clothing/shoes/black,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"km" = (/turf/unsimulated/wall{icon_state = "rock";name = "rock"},/area/awaymission/beach/offshore) -"kq" = (/obj/structure/flora/grass/green,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"ky" = (/obj/structure/table/reinforced/brass,/obj/item/storage/toolbox/brass/prefilled,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"kz" = (/obj/structure/closet/crate,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"kE" = (/turf/unsimulated/wall{tag = "icon-sandstone0";icon_state = "sandstone0"},/area/awaymission/beach/offshore) -"kH" = (/obj/structure/flora/rock,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"kO" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"kP" = (/obj/structure/grille/broken,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"kQ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"kT" = (/obj/item/shovel/safety,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"kU" = (/obj/machinery/gateway/centeraway,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"la" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"le" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"lg" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water,/area/awaymission/beach/offshore) -"lo" = (/turf/simulated/floor/wood,/area/awaymission/beach) -"lD" = (/obj/structure/sign/poster/contraband/lusty_xenomorph{pixel_y = 32},/obj/structure/closet/cabinet,/obj/item/clothing/suit/leathercoat,/obj/item/clothing/head/fedora,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"lH" = (/obj/structure/shuttle/engine/propulsion{icon_state = "propulsion_l"},/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) -"lJ" = (/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"lQ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) -"lS" = (/turf/unsimulated/wall{icon = 'icons/turf/walls/abductor_wall.dmi';icon_state = "abductor"},/area/awaymission/beach/offshore) -"lX" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"lZ" = (/obj/item/instrument/bikehorn,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"mc" = (/obj/structure/ladder/unbreakable/dive_point/anchor{id = "brass temple"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"ml" = (/obj/item/gun/projectile/automatic/pistol/enforcer,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) -"mw" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"mA" = (/obj/structure/table/wood,/obj/structure/sign/poster/contraband/have_a_puff{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"mI" = (/obj/structure/rack,/obj/item/stack/sheet/glass/fifty,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"mP" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"mQ" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"mV" = (/obj/structure/clockwork/decorative/obelisk,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"mY" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"nb" = (/obj/structure/table/reinforced,/obj/machinery/microwave,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"nc" = (/obj/structure/bed,/obj/item/bedsheet/red,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"nl" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"no" = (/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"nD" = (/obj/structure/table/wood,/obj/item/id_decal/silver,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"nE" = (/obj/item/clockwork/alloy_shards,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"nF" = (/mob/living/simple_animal/hostile/skeleton{harm_intent_damage = 10;health = 100;name = "The Captain"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"nJ" = (/obj/structure/shuttle/engine/propulsion{dir = 8},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"nL" = (/obj/structure/table/wood,/obj/item/storage/box/PDAs,/obj/structure/sign/poster/official/pda_ad{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"nT" = (/turf/unsimulated/beach/water/drop,/area/awaymission/beach/offshore) -"nU" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) -"nV" = (/obj/structure/flora/ausbushes/grassybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"nZ" = (/obj/structure/bed,/obj/item/bedsheet/cult,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"oe" = (/obj/structure/table/wood,/obj/machinery/microwave,/turf/simulated/floor/wood,/area/awaymission/beach) -"og" = (/turf/unsimulated/beach/water/deep/rock_wall{icon = 'icons/turf/walls/shuttle_wall.dmi';icon_state = "map-shuttle";name = "Shuttle Wall"},/area/awaymission/undersea) -"ok" = (/turf/unsimulated/wall{icon = 'icons/turf/mining.dmi';icon_state = "rock"},/area/awaymission/beach/offshore) -"op" = (/obj/structure/ladder/unbreakable/dive_point/buoy{id = "pirate"},/turf/unsimulated/beach/water,/area/awaymission/beach/offshore) -"ov" = (/obj/structure/rack,/obj/item/stack/sheet/mineral/sandstone{amount = 20},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"oy" = (/obj/structure/chair/comfy/shuttle{dir = 4},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"oz" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"oB" = (/obj/structure/sink{pixel_y = 24},/obj/item/reagent_containers/glass/bucket,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"oC" = (/obj/structure/flora/ausbushes/reedbush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"oE" = (/obj/structure/table/reinforced,/obj/item/storage/toolbox/syndicate,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) -"oG" = (/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"oH" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"oN" = (/obj/machinery/fishtank/wall,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"oP" = (/obj/structure/cult/altar,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"oU" = (/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"oY" = (/obj/item/clothing/shoes/sandal,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"pb" = (/obj/structure/table/reinforced,/obj/item/flashlight/seclite,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) -"pd" = (/mob/living/simple_animal/hostile/pirate/ranged{loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged)},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"pe" = (/obj/structure/closet/radiation,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"pu" = (/mob/living/simple_animal/parrot,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"pw" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) -"pD" = (/obj/machinery/door/unpowered/shuttle,/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"qb" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"qf" = (/obj/machinery/door/airlock{name = "Private Restroom"},/turf/simulated/floor/wood,/area/awaymission/beach) -"qj" = (/obj/machinery/light{dir = 4;icon_state = "tube1"},/obj/structure/chair/comfy/shuttle{dir = 8},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"ql" = (/obj/structure/chair/stool,/mob/living/simple_animal/hostile/syndicate/melee/autogib,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"qo" = (/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"qx" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"qK" = (/turf/unsimulated/wall/fakeglass{dir = 4;icon_state = "fakewindows";opacity = 0},/area/awaymission/beach/offshore) -"qP" = (/obj/structure/flora/grass/brown,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"qR" = (/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"qT" = (/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"qU" = (/obj/structure/flora/ausbushes/fernybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"qX" = (/obj/structure/spawner/nether{max_mobs = 3;name = "weak netherworld link"},/obj/effect/rune,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"rc" = (/obj/structure/table/wood,/obj/item/lighter/zippo/gonzofist,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"rg" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/genericbush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"rk" = (/obj/structure/closet/cabinet,/obj/item/clothing/shoes/black,/obj/item/clothing/under/color/lightgreen,/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"rt" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"rv" = (/obj/effect/spawner/lootdrop/brass_temple_spawner,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"rx" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"rA" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"rD" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/mob/living/simple_animal/butterfly,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"rF" = (/mob/living/simple_animal/crab,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"rG" = (/obj/structure/sign/electricshock{pixel_y = 32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"rH" = (/obj/machinery/juicer,/obj/structure/table/wood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"rS" = (/obj/structure/flora/ausbushes/ywflowers,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"rT" = (/obj/structure/table/wood,/obj/item/hatchet,/obj/item/cultivator,/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"rV" = (/obj/structure/sign/poster/official/pda_ad{pixel_x = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"rX" = (/mob/living/simple_animal/hostile/retaliate/carp/koi,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"sg" = (/obj/structure/flora/ausbushes/sunnybush,/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"sj" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"sk" = (/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) -"sp" = (/obj/structure/table/wood,/obj/item/clothing/head/bandana,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"sv" = (/obj/item/clockwork/alloy_shards/large,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"sx" = (/obj/structure/table/reinforced,/obj/item/storage/firstaid/adv,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"sz" = (/obj/structure/sign/poster/contraband/hacking_guide{pixel_y = 32},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"sF" = (/obj/structure/cult/altar,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"sN" = (/obj/structure/closet/crate,/obj/item/clothing/under/pirate,/obj/item/clothing/under/pirate,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"sO" = (/obj/structure/rack,/obj/item/stack/sheet/mineral/plasma{amount = 30},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"sZ" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"tc" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"te" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"ti" = (/turf/simulated/shuttle/wall{tag = "icon-swall8";icon_state = "swall8"},/area/awaymission/beach/offshore) -"to" = (/obj/effect/overlay/palmtree_r,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"tp" = (/obj/structure/table/reinforced,/obj/item/defibrillator/loaded,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"tr" = (/obj/structure/shuttle/engine/propulsion,/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) -"tF" = (/obj/structure/table/wood,/obj/item/reagent_containers/glass/beaker/large,/obj/item/reagent_containers/glass/beaker/large,/obj/structure/sign/poster/contraband/eat{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"tM" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"tN" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"tV" = (/turf/unsimulated/wall,/area/awaymission/beach/offshore) -"uc" = (/obj/structure/mecha_wreckage/durand,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"us" = (/obj/structure/sink{pixel_y = 24},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"uz" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"uE" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"uN" = (/obj/structure/table/wood,/obj/item/storage/toolbox/mechanical,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"uR" = (/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"ve" = (/obj/structure/sink{dir = 4;icon_state = "sink";pixel_x = 11;pixel_y = 0},/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"vh" = (/mob/living/simple_animal/hostile/syndicate/ranged{loot = list()},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"vk" = (/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"vm" = (/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/wood,/area/awaymission/beach) -"vn" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"vq" = (/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/item/coin/gold,/obj/structure/closet/crate,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"vr" = (/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"vA" = (/obj/item/hatchet,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"vM" = (/obj/structure/rack/skeletal_bar/left,/obj/machinery/chem_dispenser/beer,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"vN" = (/obj/item/stack/sheet/mineral/bananium,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"vO" = (/obj/structure/table/wood,/obj/item/clothing/head/collectable/pirate,/obj/item/stack/sheet/mineral/diamond{amount = 2},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"vR" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"vY" = (/obj/structure/shuttle/engine/propulsion{icon_state = "propulsion_r"},/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) -"vZ" = (/obj/structure/disposalpipe/junction{dir = 1;icon_state = "pipe-j1";tag = "icon-pipe-j1 (EAST)"},/turf/simulated/floor/wood,/area/awaymission/beach) -"wb" = (/obj/structure/bed,/obj/item/bedsheet/medical,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"wc" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"wd" = (/obj/machinery/vending/hydronutrients{extended_inventory = 1},/turf/simulated/floor/plasteel,/area/awaymission/beach) -"wf" = (/obj/structure/sign/poster/official/space_cops{pixel_y = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"wi" = (/obj/item/toy/pet_rock/roxie,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"wl" = (/obj/item/clothing/under/pinkhawaiianshirt,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"wr" = (/obj/structure/closet/crate/can,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"wu" = (/obj/structure/ladder/unbreakable{desc = "An extremely sturdy metal ladder. What's it doing out here?";icon_state = "ladder01";id = "volcanobaseladder";name = "mysterious ladder"},/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) -"ww" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"wx" = (/obj/structure/bed,/obj/item/bedsheet/black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"wJ" = (/obj/effect/overlay/palmtree_l,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"wO" = (/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"wP" = (/obj/item/clothing/under/librarian,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"wQ" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/lavendergrass,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"wR" = (/obj/structure/flora/ausbushes/reedbush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"wS" = (/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"wW" = (/obj/structure/table/wood,/obj/item/laser_pointer,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"xb" = (/obj/item/stack/sheet/wood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"xe" = (/obj/structure/bed,/obj/item/bedsheet/syndie,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"xm" = (/obj/effect/decal/cleanable/blood,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"xv" = (/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"xx" = (/obj/structure/flora/rock/pile,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"xD" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"xG" = (/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"xH" = (/obj/structure/sign/restroom{pixel_x = -32},/turf/simulated/floor/wood,/area/awaymission/beach) -"xI" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"xN" = (/mob/living/simple_animal/crab/evil{faction = list("nether")},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"xQ" = (/obj/structure/barricade/wooden,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"xR" = (/obj/structure/table/abductor,/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) -"xT" = (/obj/machinery/seed_extractor,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"xX" = (/obj/structure/sign/poster/official/ue_no{pixel_y = 32},/obj/structure/rack,/obj/item/toy/katana,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"xZ" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/fernybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"ym" = (/obj/structure/flora/ausbushes/genericbush,/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"yq" = (/obj/structure/flora/bush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"yr" = (/obj/item/clothing/shoes/brown,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"yz" = (/obj/item/flag/syndi,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"yB" = (/obj/effect/baseturf_helper/beach/roughsand,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"yD" = (/obj/structure/closet/crate,/obj/item/gun/energy/laser/retro,/obj/item/gun/energy/laser/retro,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"yF" = (/obj/structure/rack,/obj/item/stack/marker_beacon/ten,/obj/item/stack/marker_beacon/ten,/turf/simulated/floor/wood,/area/awaymission/beach) -"yG" = (/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"yH" = (/obj/structure/table/wood,/obj/item/storage/box/monkeycubes/neaeracubes,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"yJ" = (/obj/structure/flora/grass/brown,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"yS" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/wood,/area/awaymission/beach) -"yT" = (/obj/structure/reagent_dispensers/watertank/high,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"yU" = (/obj/structure/flora/rock,/turf/unsimulated/beach/water/dense,/area/awaymission/beach) -"yY" = (/obj/structure/closet/crate,/obj/item/coin/antagtoken/syndicate,/obj/item/clothing/mask/fawkes,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"zg" = (/obj/structure/window/reinforced,/turf/simulated/floor/wood,/area/awaymission/beach) -"zh" = (/obj/structure/sign/greencross{pixel_y = 32},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"zi" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"zk" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"zn" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"zr" = (/obj/structure/flora/bush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"zw" = (/obj/structure/flora/grass/green,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"zB" = (/obj/item/clothing/under/rank/nursesuit,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"zC" = (/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"zL" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"zQ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/structure/table/wood,/obj/structure/reagent_dispensers/beerkeg,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"zR" = (/obj/machinery/door/airlock/hatch/syndicate{desc = "There's a note on the door. It says 'All intruders WILL BE SHOT!'."},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"zX" = (/obj/effect/baseturf_helper/beach/roughsand,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"zZ" = (/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Aa" = (/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"Am" = (/obj/structure/closet/crate/secure/loot,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"Ao" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/obj/machinery/vending/cola,/turf/simulated/floor/wood,/area/awaymission/beach) -"Ap" = (/obj/item/clothing/under/color/lightgreen,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"AI" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"AM" = (/turf/unsimulated/beach/water/deep,/area/awaymission/beach/offshore) -"AP" = (/obj/structure/mecha_wreckage/ripley,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"AQ" = (/obj/structure/sign/greencross{pixel_y = -32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"AS" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"AW" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"AX" = (/obj/structure/curtain/open,/turf/simulated/floor/wood,/area/awaymission/beach) -"Bc" = (/obj/structure/bed/wooden_lounge_chair,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"Bi" = (/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Bm" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"Bn" = (/obj/structure/closet/crate,/obj/item/stack/sheet/mineral/abductor{amount = 25},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Bq" = (/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"Bt" = (/mob/living/simple_animal/hostile/retaliate/carp/koi/honk,/turf/unsimulated/beach/water,/area/awaymission/beach) -"Bv" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"By" = (/obj/structure/flora/ausbushes/sunnybush,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"Bz" = (/obj/structure/chair/sofa/left{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"BA" = (/obj/structure/chair/office/light,/mob/living/simple_animal/hostile/pirate/ranged{loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged)},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"BF" = (/obj/structure/closet/crate/internals,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/turf/simulated/floor/wood,/area/awaymission/beach) -"BH" = (/obj/item/clothing/under/color/grey,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"BM" = (/obj/structure/flora/grass/brown,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"BN" = (/obj/structure/table/reinforced,/obj/item/storage/box/donkpockets,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"BR" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/wood,/area/awaymission/beach) -"BT" = (/obj/structure/closet/crate,/obj/item/stack/sheet/mineral/gold{amount = 10},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"BU" = (/obj/structure/flora/rock/pile,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"BY" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Cf" = (/obj/structure/closet/abductor,/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) -"Ch" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Ci" = (/obj/structure/bed,/obj/item/bedsheet/medical,/obj/item/clothing/under/color/purple,/obj/item/clothing/shoes/black,/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Cj" = (/obj/structure/table/wood,/obj/item/melee/chainofcommand,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Cm" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Co" = (/obj/structure/cult/pylon,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) -"Cr" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Cv" = (/obj/structure/shuttle/engine/heater,/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) -"Cw" = (/turf/unsimulated/beach/coastline{dir = 1;icon_state = "beachcorner"},/area/awaymission/beach/offshore) -"Cz" = (/mob/living/simple_animal/hostile/skeleton,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"CA" = (/obj/item/clothing/shoes/black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"CC" = (/obj/machinery/gateway{dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"CE" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/machinery/vending/snack{extended_inventory = 1},/obj/structure/sign/poster/contraband/donut_corp{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"CF" = (/obj/machinery/door/airlock/multi_tile/glass{name = "Dance Club"},/turf/simulated/floor/wood,/area/awaymission/beach) -"CG" = (/turf/unsimulated/floor{icon_state = "plastitanium"},/area/awaymission/beach/offshore) -"CJ" = (/obj/item/beach_ball,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"CM" = (/turf/unsimulated/beach/water,/area/awaymission/beach/offshore) -"Db" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/fernybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Dm" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/machinery/vending/coffee,/turf/simulated/floor/wood,/area/awaymission/beach) -"Ds" = (/obj/structure/mineral_door/wood{icon_state = "wood";name = "Captain's Quarters";tag = "icon-wood"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"DG" = (/obj/item/clockwork/alloy_shards/pinion_lock,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"DP" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"DR" = (/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) -"DV" = (/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"Ed" = (/obj/item/shard,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"Ej" = (/obj/structure/flora/ausbushes/reedbush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"Ep" = (/obj/structure/sign/poster/official/safety_report{pixel_y = 32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Eq" = (/obj/machinery/recycler,/obj/machinery/conveyor/east{id = "beach disposal"},/turf/simulated/floor/plasteel,/area/awaymission/beach) -"Eu" = (/obj/structure/table/wood,/obj/item/storage/box/characters,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Ez" = (/obj/structure/table/wood,/obj/item/storage/firstaid/o2,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"EB" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 8},/turf/simulated/floor/wood,/area/awaymission/beach) -"EE" = (/obj/structure/table/wood,/obj/item/storage/bag/trash,/obj/item/storage/bag/trash,/obj/item/storage/bag/trash,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"EH" = (/obj/item/clothing/shoes/winterboots,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"EM" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"ER" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"ET" = (/obj/structure/mineral_door/wood{icon_state = "wood";name = "Scuba Shack";tag = "icon-wood"},/turf/simulated/floor/wood,/area/awaymission/beach) -"EX" = (/obj/machinery/light{icon_state = "tube1";dir = 8},/obj/structure/chair/comfy/shuttle{dir = 4},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"EZ" = (/obj/structure/flora/grass/green,/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Fa" = (/obj/item/chair/wood/wings,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Fb" = (/obj/structure/chair/stool,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"Fd" = (/obj/machinery/door/poddoor/brass{id_tag = "brass temple"},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Fi" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"Fl" = (/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Fp" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/obj/item/twohanded/required/kirbyplants,/turf/simulated/floor/wood,/area/awaymission/beach) -"Fs" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Fv" = (/obj/machinery/door/airlock{name = "Private Recharge room"},/turf/simulated/floor/wood,/area/awaymission/beach) -"Fy" = (/obj/machinery/vending/cigarette{extended_inventory = 1},/turf/simulated/floor/wood,/area/awaymission/beach) -"FB" = (/obj/structure/flora/rock,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"FF" = (/obj/structure/flora/ausbushes/sunnybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"FM" = (/obj/machinery/vending/hydroseeds{extended_inventory = 1},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"FN" = (/obj/structure/chair/comfy/shuttle,/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"FT" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Gd" = (/obj/structure/chair/sofa{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"Gj" = (/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Gm" = (/turf/simulated/floor/light/colour_cycle/dancefloor_b,/area/awaymission/beach) -"Go" = (/obj/machinery/power/port_gen/pacman,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"Gu" = (/obj/machinery/gateway{dir = 5},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"Gz" = (/obj/effect/waterfall{dir = 1;water_frequency = 192},/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"GR" = (/obj/machinery/recharge_station,/turf/simulated/floor/wood,/area/awaymission/beach) -"GW" = (/obj/machinery/biogenerator,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"GZ" = (/obj/structure/chair/comfy/shuttle{dir = 8},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"Hf" = (/obj/structure/flora/grass/green,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Hp" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Hr" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"Hy" = (/obj/item/clothing/shoes/white,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"HC" = (/obj/structure/table/wood,/obj/item/reagent_containers/spray/plantbgone,/obj/item/reagent_containers/spray/pestspray,/obj/item/reagent_containers/glass/bucket,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"HI" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/wood,/area/awaymission/beach) -"HJ" = (/obj/structure/cult/pylon,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"HN" = (/turf/unsimulated/wall{tag = "icon-sandstone10";icon_state = "sandstone10"},/area/awaymission/beach/offshore) -"HQ" = (/obj/machinery/gateway{dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"HY" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f6";icon_state = "swall_f6"},/area/awaymission/beach/offshore) -"Ia" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Id" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Ih" = (/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"Il" = (/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"Im" = (/obj/structure/flora/ausbushes/leafybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"In" = (/obj/structure/fermenting_barrel,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Io" = (/obj/item/clothing/under/color/lightred,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Ip" = (/obj/structure/flora/ausbushes/sunnybush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Iu" = (/obj/effect/decal/snow/sand/surround{tag = "icon-gravsnow_surround (NORTH)";name = "rough sand";icon_state = "gravsnow_surround";dir = 1},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Iw" = (/mob/living/simple_animal/crab/Coffee,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"Ix" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"ID" = (/mob/living/simple_animal/crab,/turf/unsimulated/beach/water,/area/awaymission/beach) -"IG" = (/obj/structure/closet,/obj/item/clothing/under/syndicate/tacticool,/obj/item/clothing/under/syndicate/tacticool,/obj/item/clothing/under/syndicate/tacticool,/obj/item/clothing/gloves/color/black,/obj/item/clothing/gloves/color/black,/obj/item/clothing/gloves/color/black,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) -"IN" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"IO" = (/obj/structure/flora/ausbushes/genericbush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"IZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Jc" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_x = 32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Jd" = (/mob/living/simple_animal/hostile/pirate{loot = list(/obj/effect/mob_spawn/human/corpse/pirate)},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Jf" = (/obj/machinery/door/airlock/hatch/syndicate,/turf/unsimulated/floor{tag = "icon-dark";icon_state = "dark"},/area/awaymission/beach/offshore) -"Jj" = (/turf/unsimulated/wall{tag = "icon-sandstone12";icon_state = "sandstone12"},/area/awaymission/beach/offshore) -"Jr" = (/obj/structure/ladder/unbreakable{height = 1;icon_state = "ladder10";id = "volcanobaseladder"},/turf/unsimulated/floor{icon_state = "plastitanium"},/area/awaymission/beach/offshore) -"Jv" = (/obj/structure/closet/crate/can,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"JB" = (/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/structure/closet/crate,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/item/clothing/shoes/sandal,/obj/structure/sign/poster/contraband/d_day_promo{pixel_x = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"JE" = (/obj/structure/chair/stool,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"JF" = (/obj/structure/flora/ausbushes/sunnybush,/obj/structure/flora/ausbushes/genericbush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"JH" = (/obj/structure/table/wood,/obj/item/clothing/head/beret,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"JJ" = (/obj/item/toy/pet_rock/fred,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"JK" = (/obj/machinery/door/airlock/hatch/syndicate,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"JM" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 6},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"JQ" = (/obj/structure/sign/poster/contraband/missing_gloves{pixel_y = 32},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"JZ" = (/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Ka" = (/obj/structure/flora/ausbushes/ppflowers,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Kb" = (/obj/machinery/door/poddoor/brass/beach_brass_temple,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Ke" = (/obj/structure/table/reinforced,/obj/item/storage/fancy/cigarettes/cigpack_syndicate,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"Kj" = (/obj/effect/spawner/window/reinforced,/turf/simulated/floor,/area/awaymission/beach) -"Kk" = (/obj/machinery/conveyor/east{id = "beach disposal"},/turf/simulated/floor/plasteel,/area/awaymission/beach) -"Kr" = (/obj/structure/closet/toolcloset,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"Ks" = (/obj/structure/table/wood,/obj/item/gun/energy/gun/mini,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Kt" = (/obj/structure/flora/ausbushes/ppflowers,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"KL" = (/obj/item/clothing/under/rank/chaplain,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"KM" = (/turf/simulated/floor/plasteel{dir = 2;icon_state = "ramptop";tag = "icon-stage_stairs"},/area/awaymission/beach) -"KS" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"KU" = (/obj/structure/bed,/obj/item/bedsheet/red,/obj/structure/sign/poster/contraband/energy_swords{pixel_y = -32},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"Lj" = (/obj/structure/rack,/obj/item/pickaxe/drill,/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"Lk" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Lq" = (/obj/structure/flora/grass/brown,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Lu" = (/obj/structure/table/reinforced/brass,/obj/item/stack/tile/brass/fifty,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Lv" = (/obj/item/shard,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Lz" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"LB" = (/obj/item/stack/sheet/wood,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"LE" = (/obj/structure/rack,/obj/item/melee/classic_baton,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"LJ" = (/turf/simulated/floor/beach/roughcoastline,/area/awaymission/beach) -"LK" = (/obj/structure/flora/grass/green,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"LL" = (/turf/unsimulated/wall{tag = "icon-sandstone6";icon_state = "sandstone6"},/area/awaymission/beach/offshore) -"LM" = (/obj/item/shovel,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"LV" = (/obj/effect/overlay/palmtree_l,/obj/effect/overlay/coconut,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"LY" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"LZ" = (/obj/machinery/poolcontroller/invisible,/turf/unsimulated/beach/water/deep,/area/awaymission/beach) -"Mb" = (/turf/unsimulated/beach/water/deep/rock_wall{icon = 'icons/turf/walls/shuttle_wall.dmi';icon_state = "map-shuttle";name = "Shuttle Wall"},/area/shuttle/transport) -"Mq" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) -"Mw" = (/obj/structure/dresser,/turf/simulated/floor/wood,/area/awaymission/beach) -"Mx" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"My" = (/obj/effect/decal/snow/sand/surround{name = "rough sand"},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"MB" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"MG" = (/obj/structure/window/reinforced{dir = 1},/turf/simulated/floor/wood,/area/awaymission/beach) -"ML" = (/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"MO" = (/obj/item/clothing/shoes/jackboots,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"MY" = (/obj/structure/table/wood,/obj/item/clothing/accessory/armband,/obj/item/clothing/accessory/red,/obj/structure/sign/poster/official/the_owl{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Nb" = (/obj/effect/overlay/palmtree_l,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Ne" = (/obj/structure/flora/ausbushes/fernybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"Nn" = (/obj/structure/flora/ausbushes/reedbush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Nq" = (/obj/structure/closet/crate/internals,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/item/clothing/mask/breath,/obj/structure/sign/poster/official/safety_internals{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"Nr" = (/turf/unsimulated/beach/coastline,/area/awaymission/beach/offshore) -"Ns" = (/mob/living/simple_animal/hostile/pirate{loot = list(/obj/effect/mob_spawn/human/corpse/pirate)},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"NE" = (/obj/structure/table/wood,/obj/item/clothing/head/helmet/justice,/obj/structure/sign/poster/contraband/fun_police{pixel_y = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"NF" = (/obj/structure/chair/stool,/obj/item/instrument/piano_synth,/turf/simulated/floor/wood,/area/awaymission/beach) -"NG" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"NJ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/flora/ausbushes/sunnybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"NP" = (/obj/structure/flora/rock,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"NQ" = (/obj/structure/bed,/obj/item/bedsheet/red,/obj/structure/sign/poster/contraband/lusty_xenomorph{pixel_y = 32},/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"NR" = (/turf/unsimulated/wall/fakeglass{dir = 10;icon_state = "fakewindows"},/area/awaymission/beach/offshore) -"NT" = (/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"NV" = (/obj/structure/shuttle/engine/heater{dir = 8},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"NZ" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Ob" = (/obj/structure/flora/rock,/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"Oi" = (/obj/structure/flora/rock,/turf/unsimulated/floor{icon = 'icons/turf/floors/plating.dmi';icon_state = "basalt0";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Ol" = (/obj/structure/chair/sofa/right{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"Op" = (/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Or" = (/obj/structure/table/wood,/obj/item/cultivator,/obj/item/seeds/cotton,/obj/item/seeds/cotton,/obj/item/seeds/cotton,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Ou" = (/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden,/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Oz" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"OD" = (/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"OF" = (/obj/structure/flora/ausbushes/ppflowers,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"OJ" = (/obj/structure/mineral_door/wood{tag = "icon-wood";icon_state = "wood"},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"ON" = (/obj/structure/table/wood,/obj/item/harpoon,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"OR" = (/obj/structure/flora/ausbushes/grassybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"OT" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk{dir = 1},/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/wood,/area/awaymission/beach) -"OY" = (/obj/structure/flora/ausbushes/reedbush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Pb" = (/obj/structure/chair/stool,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Pm" = (/obj/structure/chair/office/light,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Po" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"Pp" = (/obj/effect/overlay/palmtree_r,/obj/structure/flora/ausbushes/sunnybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Pu" = (/obj/effect/decal/snow/sand/edge{name = "rough sand"},/turf/simulated/floor/wood,/area/awaymission/beach) -"Pz" = (/obj/machinery/vending/clothing,/turf/simulated/floor/wood,/area/awaymission/beach) -"PE" = (/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"PH" = (/obj/structure/flora/grass/green,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"PL" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"PR" = (/turf/unsimulated/wall{icon_state = "rock";name = "rock"},/area/space) -"PW" = (/turf/simulated/shuttle/wall{tag = "icon-swall3";icon_state = "swall3"},/area/awaymission/beach/offshore) -"Qf" = (/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"Qh" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"Qm" = (/obj/structure/chair/beachchair{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"Qu" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/stalkybush,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"QI" = (/turf/unsimulated/wall/fakeglass{icon_state = "fakewindows2";dir = 1},/area/awaymission/undersea) -"QJ" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_l,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"QM" = (/mob/living/simple_animal/hostile/carp/megacarp{name = "Mega Sea Carp"},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"QN" = (/obj/effect/decal/snow/sand/surround{name = "rough sand"},/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"QR" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"QW" = (/obj/structure/chair/beachchair/red{dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"QY" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_y = 32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"QZ" = (/obj/structure/rack,/obj/item/restraints/handcuffs/cable/zipties,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Rj" = (/obj/structure/table/wood,/obj/item/storage/bag/plants,/obj/item/clothing/gloves/botanic_leather,/obj/item/shovel/spade,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"Rk" = (/obj/structure/table/wood,/obj/machinery/pos,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Rl" = (/mob/living/simple_animal/hostile/deathsquid,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"RG" = (/obj/structure/chair/beachchair/red,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"RL" = (/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (EAST)";icon_state = "wooden_chair_wings";dir = 4},/obj/structure/sign/poster/official/cleanliness{pixel_x = -32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"RN" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) -"RQ" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f9";icon_state = "swall_f9"},/area/awaymission/beach/offshore) -"RU" = (/turf/unsimulated/beach/water/deep/rock_wall{icon = 'icons/turf/walls/wood_wall.dmi';icon_state = "wood";name = "Wood Wall"},/area/awaymission/undersea) -"RV" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"RY" = (/obj/structure/sign/poster/contraband/syndicate_recruitment{pixel_x = 0;pixel_y = 32},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"Sb" = (/obj/structure/closet/crate,/obj/item/toy/syndicateballoon,/obj/item/toy/figure/syndie,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"Sc" = (/turf/unsimulated/floor/abductor,/area/awaymission/beach/offshore) -"Se" = (/obj/item/clothing/head/nursehat,/obj/structure/sign/poster/official/healthy{pixel_x = 32},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"So" = (/obj/effect/overlay/palmtree_r,/obj/effect/overlay/coconut,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Sp" = (/obj/structure/mineral_door/sandstone,/turf/space,/area/awaymission/undersea) -"Ss" = (/obj/structure/sign/poster/official/cohiba_robusto_ad{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"Su" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Sw" = (/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"Sy" = (/obj/structure/mecha_wreckage/ripley/firefighter,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Sz" = (/obj/structure/flora/ausbushes/reedbush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHEAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 5},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"SD" = (/obj/structure/ladder/unbreakable/dive_point/buoy{id = "volcano_island"},/turf/unsimulated/beach/water/drop,/area/awaymission/beach/offshore) -"SL" = (/obj/structure/sign/poster/contraband/lusty_xenomorph{pixel_x = 32;pixel_y = 0},/turf/simulated/floor/light/colour_cycle/dancefloor_a,/area/awaymission/beach) -"SO" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 9},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"SS" = (/mob/living/simple_animal/hostile/retaliate/carp/koi/honk,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"ST" = (/obj/structure/flora/rock/pile,/obj/structure/flora/ausbushes/genericbush,/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/floor/grass,/area/awaymission/beach/offshore) -"SX" = (/obj/machinery/vending/coffee/free,/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"Td" = (/obj/structure/closet/cabinet,/obj/item/reagent_containers/food/drinks/bottle/rum,/obj/item/reagent_containers/food/drinks/bottle/rum,/obj/item/reagent_containers/food/drinks/bottle/rum,/obj/item/reagent_containers/food/drinks/drinkingglass,/obj/item/reagent_containers/food/drinks/drinkingglass,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"TF" = (/obj/structure/mirror{pixel_y = 32},/obj/structure/sink{pixel_y = 16},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"TG" = (/obj/structure/dresser,/obj/item/storage/wallet/random,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"TI" = (/obj/structure/closet/crate/secure/loot,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"TL" = (/obj/structure/falsewall/brass,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"TP" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/simulated/floor/wood,/area/awaymission/beach) -"Ul" = (/obj/structure/flora/ausbushes/sparsegrass,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Uv" = (/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "wood-broken"},/area/awaymission/undersea) -"Uy" = (/mob/living/simple_animal/hostile/carp/megacarp{name = "Mega Sea Carp"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"UK" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"UL" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f10";icon_state = "swall_f10"},/area/awaymission/beach/offshore) -"UM" = (/obj/structure/chair/stool,/mob/living/simple_animal/hostile/syndicate/ranged{loot = list()},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"UN" = (/turf/simulated/floor/plasteel,/area/awaymission/beach) -"UP" = (/obj/structure/rack/skeletal_bar/right,/obj/machinery/chem_dispenser/soda,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"UQ" = (/obj/structure/sign/directions/medical{dir = 1;pixel_y = 32},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"UR" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"UU" = (/obj/structure/closet/crate,/obj/item/stack/sheet/mineral/uranium{amount = 20},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Va" = (/obj/structure/grille,/obj/structure/shuttle/window,/turf/simulated/floor/plating/airless,/area/awaymission/beach/offshore) -"Vh" = (/mob/living/simple_animal/crab{desc = "A hard-shelled crustacean. There's a mad look in its eyes.";faction = list("nether");melee_damage_lower = 3;melee_damage_upper = 3;name = "strange crab"},/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Vp" = (/obj/effect/decal/cleanable/blood,/obj/structure/chair/wood/wings{tag = "icon-wooden_chair_wings (EAST)";icon_state = "wooden_chair_wings";dir = 4},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Vq" = (/obj/structure/flora/rock,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Vw" = (/obj/structure/closet/secure_closet/personal,/turf/simulated/floor/wood,/area/awaymission/beach) -"VE" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"VO" = (/obj/structure/ladder/unbreakable/dive_point/buoy{id = "brass temple"},/turf/unsimulated/beach/water/drop,/area/awaymission/beach/offshore) -"VP" = (/obj/item/ship_in_a_bottle,/obj/structure/table/wood,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"VR" = (/obj/structure/flora/bush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"We" = (/obj/structure/flora/ausbushes/palebush,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"Wm" = (/obj/structure/mecha_wreckage/odysseus,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Wq" = (/obj/structure/chair/comfy/shuttle{dir = 1},/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"Wr" = (/obj/effect/overlay/palmtree_l,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (WEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 8},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Wy" = (/obj/machinery/gateway{dir = 4},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"WL" = (/obj/structure/chair/comfy/black{dir = 1},/turf/simulated/floor/wood,/area/awaymission/beach) -"WN" = (/obj/structure/closet/crate/can,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"WQ" = (/obj/structure/flora/ausbushes/leafybush,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"WS" = (/obj/structure/flora/ausbushes/lavendergrass,/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"WV" = (/obj/machinery/seed_extractor,/turf/simulated/floor/plasteel,/area/awaymission/beach) -"Xa" = (/obj/effect/decal/remains/human{plane = -1},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Xi" = (/obj/machinery/door/airlock/hatch/syndicate{name = "Bunk House"},/turf/unsimulated/floor{icon_state = "bar";dir = 2},/area/awaymission/beach/offshore) -"Xr" = (/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"Xz" = (/obj/item/clothing/under/color/lightred,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"XF" = (/obj/structure/table/wood,/turf/simulated/floor/wood,/area/awaymission/beach) -"XG" = (/obj/structure/flora/rock,/obj/structure/flora/ausbushes/leafybush,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"XH" = (/obj/structure/piano,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"XL" = (/obj/effect/overlay/palmtree_r,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"XU" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (EAST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 4},/obj/effect/overlay/palmtree_r,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"XZ" = (/obj/structure/chair/stool,/obj/structure/mirror{pixel_y = 32},/turf/simulated/floor/wood,/area/awaymission/beach) -"Yi" = (/turf/unsimulated/wall/fakeglass{icon_state = "fakewindows2";dir = 1},/area/awaymission/beach/offshore) -"Yj" = (/obj/structure/closet/cabinet,/obj/item/clothing/head/pirate,/obj/item/clothing/suit/pirate_black,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Ym" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (SOUTHWEST)";name = "rough sand";icon_state = "gravsnow_corner";dir = 10},/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Yo" = (/obj/structure/shuttle/engine/heater{dir = 1},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "floor3"},/area/awaymission/undersea) -"Ys" = (/obj/effect/overlay/palmtree_l,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"Yt" = (/obj/machinery/hydroponics/soil,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Yv" = (/obj/structure/table/reinforced,/obj/item/storage/backpack/duffel/syndie/med,/turf/unsimulated/floor{icon_state = "white"},/area/awaymission/beach/offshore) -"YB" = (/mob/living/simple_animal/crab,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"YF" = (/obj/item/clothing/shoes/brown,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"YG" = (/turf/unsimulated/beach/sand/dense,/area/awaymission/beach/offshore) -"YJ" = (/obj/structure/table/wood,/obj/item/stack/spacecash/c100,/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"YK" = (/obj/structure/flora/ausbushes/genericbush,/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/turf/simulated/floor/beach/roughsand,/area/awaymission/beach/boundry) -"YO" = (/turf/unsimulated/wall/fakeglass{icon_state = "fakewindows2";dir = 8},/area/awaymission/beach/offshore) -"YR" = (/turf/unsimulated/wall{tag = "icon-sandstone1";icon_state = "sandstone1"},/area/awaymission/beach/offshore) -"YS" = (/turf/unsimulated/beach/sand,/turf/simulated/shuttle/wall{tag = "icon-swall_f5";icon_state = "swall_f5"},/area/awaymission/beach/offshore) -"YY" = (/obj/machinery/vending/autodrobe,/turf/simulated/floor/wood,/area/awaymission/beach) -"Zb" = (/obj/structure/flora/rock/pile,/obj/effect/overlay/palmtree_r,/turf/simulated/floor/beach/roughsand/dense,/area/awaymission/beach/boundry) -"Zg" = (/obj/structure/closet/crate,/obj/item/toy/plushie/nukeplushie,/obj/item/toy/nuke,/turf/unsimulated/floor{icon_state = "grimy"},/area/awaymission/beach/offshore) -"Zl" = (/obj/machinery/door_control/brass/beach_brass_temple_switch{pixel_x = -32},/turf/unsimulated/floor{desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though.";icon = 'icons/obj/clockwork_objects.dmi';icon_state = "clockwork_floor";tag = "icon-wood"},/area/awaymission/beach/offshore) -"Zo" = (/turf/unsimulated/wall{tag = "icon-sandstone3";icon_state = "sandstone3"},/area/awaymission/beach/offshore) -"Zp" = (/obj/structure/closet/crate{name = "Ripley parts crate"},/obj/item/mecha_parts/chassis/ripley,/obj/item/mecha_parts/part/ripley_left_arm,/obj/item/mecha_parts/part/ripley_left_leg,/obj/item/mecha_parts/part/ripley_right_arm,/obj/item/mecha_parts/part/ripley_right_leg,/obj/item/mecha_parts/part/ripley_torso,/obj/item/circuitboard/mecha/ripley/main,/obj/item/circuitboard/mecha/ripley/peripherals,/obj/item/mecha_parts/mecha_equipment/drill,/obj/item/mecha_parts/mecha_equipment/cable_layer,/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp,/obj/item/mecha_parts/mecha_equipment/rcd,/obj/item/stack/sheet/plasteel{amount = 20},/turf/unsimulated/beach/water/deep/wood_floor{icon_state = "titanium_blue"},/area/awaymission/undersea) -"Zr" = (/obj/effect/rune,/obj/structure/spawner/nether{max_mobs = 3;name = "weak netherworld link"},/turf/unsimulated/beach/water/deep/wood_floor,/area/awaymission/undersea) -"Zt" = (/obj/item/clothing/under/rainbow,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) -"Zu" = (/obj/structure/disposalpipe/segment,/turf/simulated/wall/mineral/sandstone,/area/awaymission/beach) -"Zy" = (/obj/structure/table/reinforced,/obj/item/storage/belt/military/traitor,/turf/unsimulated/floor{icon_state = "vault";dir = 8},/area/awaymission/beach/offshore) -"ZD" = (/obj/structure/closet/crate,/obj/item/stack/marker_beacon/thirty,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"ZE" = (/obj/effect/decal/snow/sand/edge{tag = "icon-gravsnow_corner (NORTH)";name = "rough sand";icon_state = "gravsnow_corner";dir = 1},/obj/structure/chair/stool/bar,/turf/simulated/floor/beach/roughsand,/area/awaymission/beach) -"ZG" = (/obj/structure/closet/crate,/obj/item/clothing/suit/pirate_brown,/turf/unsimulated/beach/sand,/area/awaymission/beach/offshore) -"ZU" = (/obj/machinery/computer,/turf/simulated/shuttle/floor,/area/awaymission/beach/offshore) -"ZW" = (/obj/structure/flora/rock/pile,/turf/unsimulated/beach/water,/area/awaymission/beach) -"ZZ" = (/obj/item/clothing/shoes/sandal,/turf/unsimulated/beach/water/deep/sand_floor,/area/awaymission/undersea) +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"aa" = ( +/turf/space, +/area/space) +"ab" = ( +/turf/unsimulated/beach/water/deep, +/area/awaymission/undersea) +"ac" = ( +/obj/machinery/poolcontroller/invisible/sea, +/turf/unsimulated/beach/water/deep, +/area/awaymission/undersea) +"ad" = ( +/turf/unsimulated/beach/water/deep/rock_wall, +/area/awaymission/undersea) +"ae" = ( +/obj/structure/ladder/unbreakable/dive_point/anchor{ + id = "pirate" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea/entrance) +"af" = ( +/obj/structure/constructshell, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ag" = ( +/obj/structure/chair/wood/wings, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ah" = ( +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ai" = ( +/obj/structure/cult/pylon, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aj" = ( +/obj/structure/cult/altar, +/obj/item/veilrender/crabrender, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ak" = ( +/obj/structure/cult/altar, +/obj/item/book/codex_gigas, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"al" = ( +/obj/structure/curtain/black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"am" = ( +/obj/structure/cult/forge, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"an" = ( +/obj/structure/flora/rock/pile, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ao" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ap" = ( +/obj/structure/chair/stool, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aq" = ( +/mob/living/simple_animal/hostile/retaliate/carp, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ar" = ( +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"as" = ( +/obj/structure/boulder, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"at" = ( +/obj/item/flag/cult, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"au" = ( +/obj/structure/mineral_door/sandstone, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"av" = ( +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"aw" = ( +/obj/item/toy/eight_ball/conch, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ax" = ( +/obj/structure/reagent_dispensers/beerkeg, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ay" = ( +/obj/structure/closet/cabinet, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"az" = ( +/obj/structure/rack/skeletal_bar/left, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aA" = ( +/obj/structure/rack/skeletal_bar/right, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aB" = ( +/obj/structure/rack/skeletal_bar, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aC" = ( +/obj/structure/mineral_door/wood{ + tag = "icon-wood"; + icon_state = "wood" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aD" = ( +/obj/structure/chair/comfy/teal{ + dir = 4 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aE" = ( +/obj/structure/table/wood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aF" = ( +/obj/structure/chair/comfy/teal{ + dir = 8 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aG" = ( +/obj/structure/toilet, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aI" = ( +/obj/structure/chair/wood/wings{ + tag = "icon-wooden_chair_wings (EAST)"; + icon_state = "wooden_chair_wings"; + dir = 4 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aJ" = ( +/obj/structure/chair/wood/wings{ + tag = "icon-wooden_chair_wings (WEST)"; + icon_state = "wooden_chair_wings"; + dir = 8 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aM" = ( +/obj/structure/sink{ + dir = 4; + icon_state = "sink"; + pixel_x = 11; + pixel_y = 0 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aN" = ( +/obj/structure/sink{ + icon_state = "sink"; + dir = 8; + pixel_x = -12; + pixel_y = 2 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aO" = ( +/obj/structure/closet/crate/can, +/obj/item/storage/bag/trash, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"aP" = ( +/obj/item/flag/species/skrell, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"aR" = ( +/obj/structure/mirror{ + icon_state = "mirror_broke"; + pixel_y = 28 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aS" = ( +/obj/structure/bed, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aT" = ( +/obj/structure/dresser, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aU" = ( +/obj/structure/ladder/unbreakable/dive_point/anchor, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea/entrance) +"aV" = ( +/obj/structure/barricade/wooden, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"aX" = ( +/obj/item/stack/spacecash/c500, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"aY" = ( +/obj/structure/mineral_door/wood{ + tag = "icon-wood"; + icon_state = "wood" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"bd" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"bf" = ( +/obj/structure/sign/barsign{ + pixel_y = 32 + }, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bg" = ( +/obj/item/clockwork/alloy_shards/clockgolem_remains, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"bi" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/machinery/atm{ + name = "Automatic teller machine"; + pixel_y = 32 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bj" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/extinguisher_cabinet{ + pixel_y = 32 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bt" = ( +/obj/item/clothing/suit/hastur, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"bw" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bx" = ( +/obj/structure/clockwork/decorative/tinkerers_cache, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"by" = ( +/obj/machinery/cooker/foodgrill, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bz" = ( +/obj/structure/table/wood, +/obj/machinery/reagentgrinder, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bB" = ( +/mob/living/simple_animal/butterfly, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bD" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bH" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"bJ" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bL" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"bM" = ( +/obj/structure/chair/stool, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bN" = ( +/obj/machinery/vending/cigarette{ + extended_inventory = 1 + }, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/sign/poster/contraband/smoke{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"bO" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"bT" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"bX" = ( +/obj/machinery/cooking/oven, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"cc" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/stalkybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"cg" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"ci" = ( +/obj/structure/flora/ausbushes/genericbush, +/obj/structure/flora/ausbushes/sparsegrass, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"cl" = ( +/obj/structure/closet/athletic_mixed, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"cH" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_y = -32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"cJ" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"cL" = ( +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"cZ" = ( +/obj/structure/bonfire/dense, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"db" = ( +/obj/structure/chair/stool, +/obj/item/instrument/guitar, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"dd" = ( +/obj/structure/dispenser/oxygen, +/obj/structure/sign/poster/official/air1{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"de" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/obj/structure/sign/poster/official/safety_internals{ + pixel_x = -32; + pixel_y = 0 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"dg" = ( +/obj/structure/chair/beachchair, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"dh" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"dj" = ( +/turf/simulated/floor/beach/roughcoastline/dense, +/area/awaymission/beach) +"dk" = ( +/obj/structure/flora/rock, +/turf/simulated/floor/beach/roughcoastline/dense, +/area/awaymission/beach) +"dr" = ( +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"ds" = ( +/turf/unsimulated/beach/water/dense, +/area/awaymission/beach) +"dt" = ( +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach) +"du" = ( +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach) +"dv" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy, +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach/entrance) +"dw" = ( +/turf/unsimulated/beach/water/deep/dense, +/area/awaymission/beach) +"dx" = ( +/turf/unsimulated/beach/water/drop/dense, +/area/awaymission/beach) +"dP" = ( +/mob/living/simple_animal/butterfly, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"em" = ( +/obj/structure/closet/crate, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/obj/item/flashlight, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"eo" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 75 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"er" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 104 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"ew" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 44 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"ey" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 97 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"eB" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 94 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"eC" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"eM" = ( +/obj/item/shard, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"eN" = ( +/obj/structure/flora/grass/green, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"eS" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/palebush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"fa" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"fc" = ( +/obj/machinery/biogenerator, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"fd" = ( +/obj/structure/clockwork/decorative/relay, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"fj" = ( +/turf/unsimulated/wall{ + desc = "A sturdy wall of brass. It feels strangely warm and mechanical sounds can be heard from within."; + icon = 'icons/turf/walls/clockwork_wall.dmi'; + icon_state = "clockwork_wall"; + name = "clockwork wall" + }, +/area/awaymission/beach/offshore) +"fl" = ( +/obj/machinery/door_control/brass{ + id = "brass temple"; + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"fm" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"fA" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"fE" = ( +/obj/machinery/vending/syndisnack, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"fJ" = ( +/obj/structure/table/reinforced/brass, +/obj/item/clockwork/weapon/ratvarian_spear, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"fN" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"fR" = ( +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"fV" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"gn" = ( +/obj/item/clockwork/component/geis_capacitor/fallen_armor, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"gp" = ( +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) +"gq" = ( +/obj/machinery/vending/crittercare, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"gw" = ( +/obj/machinery/disco/immobile, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) +"gB" = ( +/obj/structure/barricade/wooden, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"gD" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/welding, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"gJ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"gL" = ( +/obj/structure/table/reinforced, +/obj/item/soap/syndie, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"gT" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"gW" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"hm" = ( +/obj/structure/rack, +/obj/item/stack/sheet/plasteel{ + amount = 20 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"hn" = ( +/obj/effect/baseturf_helper/beach/dense_roughsand, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"hp" = ( +/obj/item/clothing/gloves/botanic_leather, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"hs" = ( +/obj/structure/chair/stool/bar, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"hv" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"hB" = ( +/obj/structure/closet, +/obj/item/clothing/under/pinkhawaiianshirt, +/obj/item/clothing/under/pinkhawaiianshirt, +/obj/item/clothing/under/orangehawaiianshirt, +/obj/item/clothing/under/orangehawaiianshirt, +/obj/item/clothing/under/redhawaiianshirt, +/obj/item/clothing/under/redhawaiianshirt, +/obj/item/clothing/under/bluehawaiianshirt, +/obj/item/clothing/under/bluehawaiianshirt, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"hD" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 1 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"hF" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"hH" = ( +/obj/structure/table/wood, +/obj/item/storage/bag/plants, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"hI" = ( +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"hP" = ( +/obj/structure/flora/ausbushes/palebush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"hR" = ( +/obj/structure/table/wood, +/obj/item/toy/figure/wizard, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"hT" = ( +/obj/item/stack/sheet/wood, +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"hY" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"id" = ( +/obj/machinery/vending/hydroseeds{ + extended_inventory = 1 + }, +/turf/simulated/floor/plasteel{ + icon_state = "caution"; + dir = 4 + }, +/area/awaymission/beach) +"if" = ( +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"im" = ( +/obj/machinery/fishtank/tank, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"is" = ( +/obj/structure/table/wood, +/obj/item/clothing/suit/pirate_black, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"iu" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"ix" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"iC" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"iF" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/obj/item/melee/cultblade, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"iI" = ( +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"iK" = ( +/obj/structure/chair/beachchair/red, +/obj/item/toy/plushie/octopus, +/obj/item/clothing/glasses/sunglasses{ + pixel_x = 2; + pixel_y = 6 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"iL" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"iT" = ( +/obj/machinery/photocopier, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"iU" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"iX" = ( +/obj/structure/table/wood, +/obj/item/flashlight, +/obj/item/stack/marker_beacon/thirty, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"jd" = ( +/obj/item/clothing/under/color/brown, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"ji" = ( +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"jr" = ( +/obj/effect/decal/cleanable/blood, +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"jA" = ( +/obj/machinery/vending/crittercare/free, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"jD" = ( +/obj/structure/table/reinforced, +/obj/item/paper_bin, +/obj/item/pen/multi, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"jE" = ( +/obj/structure/table/wood, +/obj/item/reagent_containers/food/snacks/pie, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"jL" = ( +/turf/unsimulated/floor/lava, +/area/awaymission/beach/offshore) +"jN" = ( +/obj/machinery/gateway{ + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"jX" = ( +/obj/structure/sign/poster/official/love_ian{ + pixel_x = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"jY" = ( +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ka" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"kd" = ( +/obj/structure/chair/stool, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"kf" = ( +/obj/machinery/smartfridge, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"kg" = ( +/obj/item/clothing/shoes/black, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"km" = ( +/turf/unsimulated/wall{ + icon_state = "rock"; + name = "rock" + }, +/area/awaymission/beach/offshore) +"kp" = ( +/obj/structure/table/reinforced, +/obj/structure/sign/nuke{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"kq" = ( +/obj/structure/flora/grass/green, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"ku" = ( +/obj/structure/closet/secure_closet/freezer/kitchen{ + locked = 0; + req_access = null + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"ky" = ( +/obj/structure/table/reinforced/brass, +/obj/item/storage/toolbox/brass/prefilled, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"kz" = ( +/obj/structure/closet/crate, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"kE" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone0"; + icon_state = "sandstone0" + }, +/area/awaymission/beach/offshore) +"kH" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"kO" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"kP" = ( +/obj/structure/grille/broken, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"kQ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"kT" = ( +/obj/item/shovel/safety, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"kU" = ( +/obj/machinery/gateway/centeraway, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"la" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"le" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"lg" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/water, +/area/awaymission/beach/offshore) +"lo" = ( +/turf/simulated/floor/wood, +/area/awaymission/beach) +"lC" = ( +/obj/structure/sink{ + pixel_y = 24 + }, +/obj/structure/window/reinforced{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"lD" = ( +/obj/structure/sign/poster/contraband/lusty_xenomorph{ + pixel_y = 32 + }, +/obj/structure/closet/cabinet, +/obj/item/clothing/suit/leathercoat, +/obj/item/clothing/head/fedora, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"lH" = ( +/obj/structure/shuttle/engine/propulsion{ + icon_state = "propulsion_l" + }, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"lJ" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"lK" = ( +/obj/structure/ladder/unbreakable/dive_point/anchor{ + id = "volcano_island" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea/entrance) +"lQ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"lS" = ( +/turf/unsimulated/wall{ + icon = 'icons/turf/walls/abductor_wall.dmi'; + icon_state = "abductor" + }, +/area/awaymission/beach/offshore) +"lX" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"lZ" = ( +/obj/item/instrument/bikehorn, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"mc" = ( +/obj/structure/ladder/unbreakable/dive_point/anchor{ + id = "brass temple" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"me" = ( +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach/entrance) +"mi" = ( +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea/entrance) +"ml" = ( +/obj/item/gun/projectile/automatic/pistol/enforcer, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"mo" = ( +/obj/structure/table/wood, +/obj/item/camera, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"mp" = ( +/obj/structure/table/wood, +/obj/item/storage/firstaid/adv, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"mw" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"mA" = ( +/obj/structure/table/wood, +/obj/structure/sign/poster/contraband/have_a_puff{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"mE" = ( +/obj/structure/table/wood, +/obj/item/paicard, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"mI" = ( +/obj/structure/rack, +/obj/item/stack/sheet/glass/fifty, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"mJ" = ( +/obj/structure/closet/crate, +/obj/item/harpoon, +/obj/item/harpoon, +/obj/item/harpoon, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"mP" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"mQ" = ( +/obj/effect/decal/cleanable/blood, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"mV" = ( +/obj/structure/clockwork/decorative/obelisk, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"mY" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"na" = ( +/obj/structure/closet/secure_closet/freezer/meat/open, +/obj/structure/disposalpipe/junction{ + dir = 1; + icon_state = "pipe-j2"; + tag = "icon-pipe-j2" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"nb" = ( +/obj/structure/table/reinforced, +/obj/machinery/microwave, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"nc" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"nl" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"no" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"nD" = ( +/obj/structure/table/wood, +/obj/item/id_decal/silver, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nE" = ( +/obj/item/clockwork/alloy_shards, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"nF" = ( +/mob/living/simple_animal/hostile/skeleton{ + harm_intent_damage = 10; + health = 100; + name = "The Captain" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nJ" = ( +/obj/structure/shuttle/engine/propulsion{ + dir = 8 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"nL" = ( +/obj/structure/table/wood, +/obj/item/storage/box/PDAs, +/obj/structure/sign/poster/official/pda_ad{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"nT" = ( +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach/offshore) +"nU" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"nV" = ( +/obj/structure/flora/ausbushes/grassybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"nZ" = ( +/obj/structure/bed, +/obj/item/bedsheet/cult, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"oe" = ( +/obj/structure/table/wood, +/obj/machinery/microwave, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"og" = ( +/turf/unsimulated/beach/water/deep/rock_wall{ + icon = 'icons/turf/walls/shuttle_wall.dmi'; + icon_state = "map-shuttle"; + name = "Shuttle Wall" + }, +/area/awaymission/undersea) +"ok" = ( +/turf/unsimulated/wall{ + icon = 'icons/turf/mining.dmi'; + icon_state = "rock" + }, +/area/awaymission/beach/offshore) +"op" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy{ + id = "pirate" + }, +/turf/unsimulated/beach/water, +/area/awaymission/beach/offshore) +"ov" = ( +/obj/structure/rack, +/obj/item/stack/sheet/mineral/sandstone{ + amount = 20 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"oy" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"oz" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"oB" = ( +/obj/structure/sink{ + pixel_y = 24 + }, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"oC" = ( +/obj/structure/flora/ausbushes/reedbush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"oE" = ( +/obj/structure/table/reinforced, +/obj/item/storage/toolbox/syndicate, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"oG" = ( +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"oH" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"oL" = ( +/obj/machinery/gateway, +/turf/simulated/floor/carpet, +/area/awaymission/beach/entrance) +"oN" = ( +/obj/machinery/fishtank/wall, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"oP" = ( +/obj/structure/cult/altar, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"oU" = ( +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"oY" = ( +/obj/item/clothing/shoes/sandal, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"pb" = ( +/obj/structure/table/reinforced, +/obj/item/flashlight/seclite, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"pd" = ( +/mob/living/simple_animal/hostile/pirate/ranged{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"pe" = ( +/obj/structure/closet/radiation, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"ph" = ( +/obj/structure/toilet/secret, +/obj/item/bikehorn/rubberducky, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"pp" = ( +/obj/structure/table/wood, +/obj/effect/decal/cleanable/blood, +/obj/item/paper{ + info = "It was tricky shooting down this dodgy ship but this fancy metal will be worth a shit tone of credits on the black market. I can't help but wonder what damaged it in the first place though, even wounded the ship was agile. Anyways, this metal is damn tough to take apart so I'm thinking we set up shop here and maybe make this into a hideout once we're done."; + name = "Captain's note" + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"pu" = ( +/mob/living/simple_animal/parrot, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"pw" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"pD" = ( +/obj/machinery/door/unpowered/shuttle, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"pL" = ( +/obj/effect/decal/warning_stripes/north, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "caution" + }, +/area/awaymission/beach) +"pO" = ( +/obj/effect/mob_spawn/human/resort_host{ + dir = 4 + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach/entrance) +"qb" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"qf" = ( +/obj/machinery/door/airlock{ + name = "Private Restroom" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"qj" = ( +/obj/machinery/light{ + dir = 4; + icon_state = "tube1" + }, +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"ql" = ( +/obj/structure/chair/stool, +/mob/living/simple_animal/hostile/syndicate/melee/autogib, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"qo" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"qx" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"qK" = ( +/turf/unsimulated/wall/fakeglass{ + dir = 4; + icon_state = "fakewindows"; + opacity = 0 + }, +/area/awaymission/beach/offshore) +"qP" = ( +/obj/structure/flora/grass/brown, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"qR" = ( +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"qT" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"qU" = ( +/obj/structure/flora/ausbushes/fernybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"qX" = ( +/obj/structure/spawner/nether{ + max_mobs = 3; + name = "weak netherworld link" + }, +/obj/effect/rune, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"rc" = ( +/obj/structure/table/wood, +/obj/item/lighter/zippo/gonzofist, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"rg" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/genericbush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"rk" = ( +/obj/structure/closet/cabinet, +/obj/item/clothing/shoes/black, +/obj/item/clothing/under/color/lightgreen, +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"rt" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"rv" = ( +/obj/effect/spawner/lootdrop/brass_temple_spawner, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"rx" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"rA" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"rD" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/mob/living/simple_animal/butterfly, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"rF" = ( +/mob/living/simple_animal/crab, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"rG" = ( +/obj/structure/sign/electricshock{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"rH" = ( +/obj/machinery/juicer, +/obj/structure/table/wood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"rS" = ( +/obj/structure/flora/ausbushes/ywflowers, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"rT" = ( +/obj/structure/table/wood, +/obj/item/hatchet, +/obj/item/cultivator, +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"rV" = ( +/obj/structure/sign/poster/official/pda_ad{ + pixel_x = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"rX" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"sa" = ( +/obj/machinery/gateway{ + dir = 6 + }, +/obj/effect/baseturf_helper/beach/roughsand, +/turf/simulated/floor/carpet, +/area/awaymission/beach/entrance) +"sd" = ( +/obj/machinery/door/window/eastleft{ + name = "Resort Bar"; + req_access_txt = "25" + }, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"sf" = ( +/obj/structure/table/wood, +/obj/item/soap/deluxe, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"sg" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"sj" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"sk" = ( +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) +"sp" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/bandana, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"sv" = ( +/obj/item/clockwork/alloy_shards/large, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"sx" = ( +/obj/structure/table/reinforced, +/obj/item/storage/firstaid/adv, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"sz" = ( +/obj/structure/sign/poster/contraband/hacking_guide{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"sF" = ( +/obj/structure/cult/altar, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"sN" = ( +/obj/structure/closet/crate, +/obj/item/clothing/under/pirate, +/obj/item/clothing/under/pirate, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"sO" = ( +/obj/structure/rack, +/obj/item/stack/sheet/mineral/plasma{ + amount = 30 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"sW" = ( +/obj/structure/table/wood, +/obj/machinery/pos, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"sZ" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"tc" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"te" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"ti" = ( +/turf/simulated/shuttle/wall{ + tag = "icon-swall8"; + icon_state = "swall8" + }, +/area/awaymission/beach/offshore) +"to" = ( +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"tp" = ( +/obj/structure/table/reinforced, +/obj/item/defibrillator/loaded, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"tr" = ( +/obj/structure/shuttle/engine/propulsion, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"tF" = ( +/obj/structure/table/wood, +/obj/item/reagent_containers/glass/beaker/large, +/obj/item/reagent_containers/glass/beaker/large, +/obj/structure/sign/poster/contraband/eat{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"tM" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"tN" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"tV" = ( +/turf/unsimulated/wall, +/area/awaymission/beach/offshore) +"tW" = ( +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/awaymission/beach) +"uc" = ( +/obj/structure/mecha_wreckage/durand, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ue" = ( +/obj/structure/disposalpipe/segment, +/obj/machinery/vending/dinnerware, +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"us" = ( +/obj/structure/sink{ + pixel_y = 24 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"uz" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"uE" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"uN" = ( +/obj/structure/table/wood, +/obj/item/storage/toolbox/mechanical, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"uR" = ( +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"ve" = ( +/obj/structure/sink{ + dir = 4; + icon_state = "sink"; + pixel_x = 11; + pixel_y = 0 + }, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"vh" = ( +/mob/living/simple_animal/hostile/syndicate/ranged{ + loot = list() + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"vk" = ( +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"vm" = ( +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"vn" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"vq" = ( +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/item/coin/gold, +/obj/structure/closet/crate, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"vr" = ( +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"vA" = ( +/obj/item/hatchet, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"vM" = ( +/obj/structure/rack/skeletal_bar/left, +/obj/machinery/chem_dispenser/beer, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"vN" = ( +/obj/item/stack/sheet/mineral/bananium, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"vO" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/collectable/pirate, +/obj/item/stack/sheet/mineral/diamond{ + amount = 2 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"vR" = ( +/obj/effect/decal/cleanable/blood, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"vS" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"vY" = ( +/obj/structure/shuttle/engine/propulsion{ + icon_state = "propulsion_r" + }, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"vZ" = ( +/obj/structure/disposalpipe/junction{ + dir = 1; + icon_state = "pipe-j1"; + tag = "icon-pipe-j1 (EAST)" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"wb" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"wc" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"wd" = ( +/obj/machinery/vending/hydronutrients{ + extended_inventory = 1 + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"wf" = ( +/obj/structure/sign/poster/official/space_cops{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"wi" = ( +/obj/item/toy/pet_rock/roxie, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"wl" = ( +/obj/item/clothing/under/pinkhawaiianshirt, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"wr" = ( +/obj/structure/closet/crate/can, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"wu" = ( +/obj/structure/ladder/unbreakable{ + desc = "An extremely sturdy metal ladder. What's it doing out here?"; + icon_state = "ladder01"; + id = "volcanobaseladder"; + name = "mysterious ladder" + }, +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"ww" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"wx" = ( +/obj/structure/bed, +/obj/item/bedsheet/black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"wJ" = ( +/obj/effect/overlay/palmtree_l, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"wO" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"wP" = ( +/obj/item/clothing/under/librarian, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"wQ" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/lavendergrass, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"wR" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"wS" = ( +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"wW" = ( +/obj/structure/table/wood, +/obj/item/laser_pointer, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xb" = ( +/obj/item/stack/sheet/wood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xe" = ( +/obj/structure/bed, +/obj/item/bedsheet/syndie, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"xj" = ( +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/entrance) +"xm" = ( +/obj/effect/decal/cleanable/blood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xv" = ( +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"xx" = ( +/obj/structure/flora/rock/pile, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"xD" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"xG" = ( +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"xH" = ( +/obj/structure/sign/restroom{ + pixel_x = -32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"xI" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"xN" = ( +/mob/living/simple_animal/crab/evil{ + faction = list("nether") + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xQ" = ( +/obj/structure/barricade/wooden, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"xR" = ( +/obj/structure/table/abductor, +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"xT" = ( +/obj/machinery/seed_extractor, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xX" = ( +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/obj/structure/rack, +/obj/item/toy/katana, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"xZ" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/fernybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"ym" = ( +/obj/structure/flora/ausbushes/genericbush, +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"yq" = ( +/obj/structure/flora/bush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"yr" = ( +/obj/item/clothing/shoes/brown, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"yz" = ( +/obj/item/flag/syndi, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"yB" = ( +/obj/effect/baseturf_helper/beach/roughsand, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"yD" = ( +/obj/structure/closet/crate, +/obj/item/gun/energy/laser/retro, +/obj/item/gun/energy/laser/retro, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"yF" = ( +/obj/structure/rack, +/obj/item/stack/marker_beacon/ten, +/obj/item/stack/marker_beacon/ten, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"yG" = ( +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"yH" = ( +/obj/structure/table/wood, +/obj/item/storage/box/monkeycubes/neaeracubes, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"yJ" = ( +/obj/structure/flora/grass/brown, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"yK" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/beer, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"yS" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"yT" = ( +/obj/structure/reagent_dispensers/watertank/high, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"yU" = ( +/obj/structure/flora/rock, +/turf/unsimulated/beach/water/dense, +/area/awaymission/beach) +"yY" = ( +/obj/structure/closet/crate, +/obj/item/coin/antagtoken/syndicate, +/obj/item/clothing/mask/fawkes, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"zg" = ( +/obj/structure/window/reinforced, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"zh" = ( +/obj/structure/sign/greencross{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"zi" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"zk" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"zn" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"zr" = ( +/obj/structure/flora/bush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"zw" = ( +/obj/structure/flora/grass/green, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"zx" = ( +/obj/machinery/conveyor_switch/oneway{ + id = "beach disposal" + }, +/obj/effect/decal/warning_stripes/north, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "caution" + }, +/area/awaymission/beach) +"zB" = ( +/obj/item/clothing/under/rank/nursesuit, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"zC" = ( +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"zL" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"zQ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/structure/table/wood, +/obj/structure/reagent_dispensers/beerkeg, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"zR" = ( +/obj/machinery/door/airlock/hatch/syndicate{ + desc = "There's a note on the door. It says 'All intruders WILL BE SHOT!'." + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"zW" = ( +/obj/structure/table/reinforced, +/obj/item/clothing/accessory/stethoscope, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"zX" = ( +/obj/effect/baseturf_helper/beach/roughsand, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"zZ" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Aa" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Am" = ( +/obj/structure/closet/crate/secure/loot, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"An" = ( +/obj/item/flag/syndi, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Ao" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/obj/machinery/vending/cola, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Ap" = ( +/obj/item/clothing/under/color/lightgreen, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ax" = ( +/obj/item/toy/figure/ninja, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"AD" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"AI" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"AM" = ( +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach/offshore) +"AP" = ( +/obj/structure/mecha_wreckage/ripley, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"AQ" = ( +/obj/structure/sign/greencross{ + pixel_y = -32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"AS" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"AW" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"AX" = ( +/obj/structure/curtain/open, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Bc" = ( +/obj/structure/bed/wooden_lounge_chair, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Bi" = ( +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Bm" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Bn" = ( +/obj/structure/closet/crate, +/obj/item/stack/sheet/mineral/abductor{ + amount = 25 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Bq" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Bt" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi/honk, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"Bv" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"By" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Bz" = ( +/obj/structure/chair/sofa/left{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"BA" = ( +/obj/structure/chair/office/light, +/mob/living/simple_animal/hostile/pirate/ranged{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged) + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"BF" = ( +/obj/structure/closet/crate/internals, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"BG" = ( +/obj/structure/disposalpipe/segment, +/obj/effect/decal/warning_stripes/north, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "caution" + }, +/area/awaymission/beach) +"BH" = ( +/obj/item/clothing/under/color/grey, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"BJ" = ( +/obj/machinery/gateway{ + dir = 10 + }, +/turf/simulated/floor/carpet, +/area/awaymission/beach/entrance) +"BM" = ( +/obj/structure/flora/grass/brown, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"BN" = ( +/obj/structure/table/reinforced, +/obj/item/storage/box/donkpockets, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"BR" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"BT" = ( +/obj/structure/closet/crate, +/obj/item/stack/sheet/mineral/gold{ + amount = 10 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"BU" = ( +/obj/structure/flora/rock/pile, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"BY" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Cf" = ( +/obj/structure/closet/abductor, +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"Ch" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ci" = ( +/obj/structure/bed, +/obj/item/bedsheet/medical, +/obj/item/clothing/under/color/purple, +/obj/item/clothing/shoes/black, +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Cj" = ( +/obj/structure/table/wood, +/obj/item/melee/chainofcommand, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Cm" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Co" = ( +/obj/structure/cult/pylon, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"Cr" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Cv" = ( +/obj/structure/shuttle/engine/heater, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"Cw" = ( +/turf/unsimulated/beach/coastline{ + dir = 1; + icon_state = "beachcorner" + }, +/area/awaymission/beach/offshore) +"Cz" = ( +/mob/living/simple_animal/hostile/skeleton, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"CA" = ( +/obj/item/clothing/shoes/black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"CB" = ( +/obj/structure/sign/botany{ + pixel_y = 32 + }, +/obj/structure/table/wood, +/obj/item/reagent_containers/food/drinks/shaker, +/obj/item/clothing/glasses/sunglasses, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"CC" = ( +/obj/machinery/gateway{ + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"CE" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/machinery/vending/snack{ + extended_inventory = 1 + }, +/obj/structure/sign/poster/contraband/donut_corp{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"CF" = ( +/obj/machinery/door/airlock/multi_tile/glass{ + name = "Dance Club" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"CG" = ( +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"CJ" = ( +/obj/item/beach_ball, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"CM" = ( +/turf/unsimulated/beach/water, +/area/awaymission/beach/offshore) +"Db" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/fernybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Dm" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/machinery/vending/coffee, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Ds" = ( +/obj/structure/mineral_door/wood{ + icon_state = "wood"; + name = "Captain's Quarters"; + tag = "icon-wood" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Dx" = ( +/obj/structure/table/wood, +/obj/item/lighter/zippo, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"DB" = ( +/obj/structure/table/wood, +/obj/item/paper_bin, +/obj/item/pen/multi, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"DD" = ( +/obj/machinery/vending/boozeomat{ + emagged = 1 + }, +/obj/structure/sign/poster/official/high_class_martini{ + pixel_y = 32 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"DG" = ( +/obj/item/clockwork/alloy_shards/pinion_lock, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"DL" = ( +/obj/machinery/hydroponics/constructable, +/turf/simulated/floor/plasteel{ + dir = 0; + icon_state = "green" + }, +/area/awaymission/beach) +"DP" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"DR" = ( +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"DV" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Ed" = ( +/obj/item/shard, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Ej" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Ep" = ( +/obj/structure/sign/poster/official/safety_report{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Eq" = ( +/obj/machinery/recycler, +/obj/machinery/conveyor/east{ + id = "beach disposal" + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Et" = ( +/obj/structure/table/reinforced, +/obj/structure/sign/poster/contraband/c20r{ + pixel_y = 32 + }, +/obj/item/storage/secure/briefcase/syndie, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"Eu" = ( +/obj/structure/table/wood, +/obj/item/storage/box/characters, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ez" = ( +/obj/structure/table/wood, +/obj/item/storage/firstaid/o2, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"EB" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 8 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"EE" = ( +/obj/structure/table/wood, +/obj/item/storage/bag/trash, +/obj/item/storage/bag/trash, +/obj/item/storage/bag/trash, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"EH" = ( +/obj/item/clothing/shoes/winterboots, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"EM" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"ER" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"ET" = ( +/obj/structure/mineral_door/wood{ + icon_state = "wood"; + name = "Scuba Shack"; + tag = "icon-wood" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"EX" = ( +/obj/machinery/light{ + icon_state = "tube1"; + dir = 8 + }, +/obj/structure/chair/comfy/shuttle{ + dir = 4 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"EZ" = ( +/obj/structure/flora/grass/green, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Fa" = ( +/obj/item/chair/wood/wings, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Fb" = ( +/obj/structure/chair/stool, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Fc" = ( +/turf/unsimulated/beach/water/deep/dense, +/area/awaymission/beach/entrance) +"Fd" = ( +/obj/machinery/door/poddoor/brass{ + id_tag = "brass temple" + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Fi" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Fj" = ( +/obj/structure/table/wood, +/obj/item/pizzabox/hawaiian, +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Fl" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Fp" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/obj/item/twohanded/required/kirbyplants, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Fs" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Fv" = ( +/obj/machinery/door/airlock{ + name = "Private Recharge room" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Fy" = ( +/obj/machinery/vending/cigarette{ + extended_inventory = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"FB" = ( +/obj/structure/flora/rock, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"FF" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"FM" = ( +/obj/machinery/vending/hydroseeds{ + extended_inventory = 1 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"FN" = ( +/obj/structure/chair/comfy/shuttle, +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"FT" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Gd" = ( +/obj/structure/chair/sofa{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Gj" = ( +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Gm" = ( +/turf/simulated/floor/light/colour_cycle/dancefloor_b, +/area/awaymission/beach) +"Go" = ( +/obj/machinery/power/port_gen/pacman, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Gu" = ( +/obj/machinery/gateway{ + dir = 5 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Gz" = ( +/obj/effect/waterfall{ + dir = 1; + water_frequency = 192 + }, +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"GK" = ( +/obj/structure/closet/gmcloset{ + icon_closed = "black"; + icon_state = "black"; + name = "formal wardrobe" + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"GR" = ( +/obj/machinery/recharge_station, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"GW" = ( +/obj/machinery/biogenerator, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"GZ" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 8 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"Hf" = ( +/obj/structure/flora/grass/green, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Hp" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Hr" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Hy" = ( +/obj/item/clothing/shoes/white, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"HC" = ( +/obj/structure/table/wood, +/obj/item/reagent_containers/spray/plantbgone, +/obj/item/reagent_containers/spray/pestspray, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"HI" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"HJ" = ( +/obj/structure/cult/pylon, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"HN" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone10"; + icon_state = "sandstone10" + }, +/area/awaymission/beach/offshore) +"HQ" = ( +/obj/machinery/gateway{ + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"HY" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f6"; + icon_state = "swall_f6" + }, +/area/awaymission/beach/offshore) +"Ia" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Id" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ih" = ( +/obj/structure/window/reinforced, +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Il" = ( +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"Im" = ( +/obj/structure/flora/ausbushes/leafybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"In" = ( +/obj/structure/fermenting_barrel, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Io" = ( +/obj/item/clothing/under/color/lightred, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Ip" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"It" = ( +/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"Iu" = ( +/obj/effect/decal/snow/sand/surround{ + tag = "icon-gravsnow_surround (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_surround"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Iw" = ( +/mob/living/simple_animal/crab/Coffee, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Ix" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"IA" = ( +/obj/machinery/hydroponics/constructable, +/turf/simulated/floor/plasteel{ + dir = 10; + icon_state = "green" + }, +/area/awaymission/beach) +"ID" = ( +/mob/living/simple_animal/crab, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"IG" = ( +/obj/structure/closet, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/under/syndicate/tacticool, +/obj/item/clothing/gloves/color/black, +/obj/item/clothing/gloves/color/black, +/obj/item/clothing/gloves/color/black, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"II" = ( +/obj/structure/safe, +/obj/item/documents/syndicate/yellow, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"IN" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"IO" = ( +/obj/structure/flora/ausbushes/genericbush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"IZ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Jb" = ( +/obj/structure/disposaloutlet{ + dir = 4 + }, +/obj/structure/disposalpipe/trunk, +/turf/simulated/floor/plasteel{ + icon_state = "bot" + }, +/area/awaymission/beach) +"Jc" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_x = 32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Jd" = ( +/mob/living/simple_animal/hostile/pirate{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate) + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Jf" = ( +/obj/machinery/door/airlock/hatch/syndicate, +/turf/unsimulated/floor{ + tag = "icon-dark"; + icon_state = "dark" + }, +/area/awaymission/beach/offshore) +"Jj" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone12"; + icon_state = "sandstone12" + }, +/area/awaymission/beach/offshore) +"Jl" = ( +/obj/structure/table/wood, +/obj/machinery/vending/wallmed{ + extended_inventory = 1; + pixel_x = 0; + pixel_y = 32 + }, +/obj/item/storage/firstaid/adv{ + pixel_x = 8; + pixel_y = 8 + }, +/obj/item/storage/firstaid/toxin{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Jr" = ( +/obj/structure/ladder/unbreakable{ + height = 1; + icon_state = "ladder10"; + id = "volcanobaseladder" + }, +/turf/unsimulated/floor{ + icon_state = "plastitanium" + }, +/area/awaymission/beach/offshore) +"Jv" = ( +/obj/structure/closet/crate/can, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"JB" = ( +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/structure/closet/crate, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/item/clothing/shoes/sandal, +/obj/structure/sign/poster/contraband/d_day_promo{ + pixel_x = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"JE" = ( +/obj/structure/chair/stool, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"JF" = ( +/obj/structure/flora/ausbushes/sunnybush, +/obj/structure/flora/ausbushes/genericbush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"JH" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/beret, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"JJ" = ( +/obj/item/toy/pet_rock/fred, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"JK" = ( +/obj/machinery/door/airlock/hatch/syndicate, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"JM" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"JQ" = ( +/obj/structure/sign/poster/contraband/missing_gloves{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"JZ" = ( +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Ka" = ( +/obj/structure/flora/ausbushes/ppflowers, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Kb" = ( +/obj/machinery/door/poddoor/brass/beach_brass_temple, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Ke" = ( +/obj/structure/table/reinforced, +/obj/item/storage/fancy/cigarettes/cigpack_syndicate, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Kj" = ( +/obj/effect/spawner/window/reinforced, +/turf/simulated/floor, +/area/awaymission/beach) +"Kk" = ( +/obj/machinery/conveyor/east{ + id = "beach disposal" + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Kr" = ( +/obj/structure/closet/toolcloset, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Ks" = ( +/obj/structure/table/wood, +/obj/item/gun/energy/gun/mini, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Kt" = ( +/obj/structure/flora/ausbushes/ppflowers, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"KB" = ( +/obj/structure/sign/poster/official/nanomichi_ad{ + pixel_y = 32 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_b, +/area/awaymission/beach) +"KL" = ( +/obj/item/clothing/under/rank/chaplain, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"KM" = ( +/turf/simulated/floor/plasteel{ + dir = 2; + icon_state = "ramptop"; + tag = "icon-stage_stairs" + }, +/area/awaymission/beach) +"KS" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"KU" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, +/obj/structure/sign/poster/contraband/energy_swords{ + pixel_y = -32 + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Lj" = ( +/obj/structure/rack, +/obj/item/pickaxe/drill, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Lk" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Lq" = ( +/obj/structure/flora/grass/brown, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ls" = ( +/obj/machinery/newscaster{ + pixel_y = -32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Lu" = ( +/obj/structure/table/reinforced/brass, +/obj/item/stack/tile/brass/fifty, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Lv" = ( +/obj/item/shard, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Lz" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"LB" = ( +/obj/item/stack/sheet/wood, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"LE" = ( +/obj/structure/rack, +/obj/item/melee/classic_baton, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"LJ" = ( +/turf/simulated/floor/beach/roughcoastline, +/area/awaymission/beach) +"LK" = ( +/obj/structure/flora/grass/green, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"LL" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone6"; + icon_state = "sandstone6" + }, +/area/awaymission/beach/offshore) +"LM" = ( +/obj/item/shovel, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"LV" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/overlay/coconut, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"LX" = ( +/obj/machinery/sleeper/syndie, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"LY" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"LZ" = ( +/obj/machinery/poolcontroller/invisible, +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach) +"Mb" = ( +/turf/unsimulated/beach/water/deep/rock_wall{ + icon = 'icons/turf/walls/shuttle_wall.dmi'; + icon_state = "map-shuttle"; + name = "Shuttle Wall" + }, +/area/shuttle/transport) +"Mq" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"Mw" = ( +/obj/structure/dresser, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Mx" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"My" = ( +/obj/effect/decal/snow/sand/surround{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"MB" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"MG" = ( +/obj/structure/window/reinforced{ + dir = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"MK" = ( +/obj/machinery/hydroponics/constructable, +/turf/simulated/floor/plasteel{ + icon_state = "green"; + dir = 6 + }, +/area/awaymission/beach) +"ML" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"MO" = ( +/obj/item/clothing/shoes/jackboots, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"MP" = ( +/turf/unsimulated/beach/water/deep, +/area/awaymission/beach/entrance) +"MY" = ( +/obj/structure/table/wood, +/obj/item/clothing/accessory/armband, +/obj/item/clothing/accessory/red, +/obj/structure/sign/poster/official/the_owl{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Nb" = ( +/obj/effect/overlay/palmtree_l, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Ne" = ( +/obj/structure/flora/ausbushes/fernybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Nn" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Nq" = ( +/obj/structure/closet/crate/internals, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/item/clothing/mask/breath, +/obj/structure/sign/poster/official/safety_internals{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Nr" = ( +/turf/unsimulated/beach/coastline, +/area/awaymission/beach/offshore) +"Ns" = ( +/mob/living/simple_animal/hostile/pirate{ + loot = list(/obj/effect/mob_spawn/human/corpse/pirate) + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"NE" = ( +/obj/structure/table/wood, +/obj/item/clothing/head/helmet/justice, +/obj/structure/sign/poster/contraband/fun_police{ + pixel_y = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"NF" = ( +/obj/structure/chair/stool, +/obj/item/instrument/piano_synth, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"NG" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"NJ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/flora/ausbushes/sunnybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"NP" = ( +/obj/structure/flora/rock, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"NQ" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, +/obj/structure/sign/poster/contraband/lusty_xenomorph{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"NR" = ( +/turf/unsimulated/wall/fakeglass{ + dir = 10; + icon_state = "fakewindows" + }, +/area/awaymission/beach/offshore) +"NT" = ( +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"NV" = ( +/obj/structure/shuttle/engine/heater{ + dir = 8 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"NZ" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Ob" = ( +/obj/structure/flora/rock, +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"Oi" = ( +/obj/structure/flora/rock, +/turf/unsimulated/floor{ + icon = 'icons/turf/floors/plating.dmi'; + icon_state = "basalt0"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Ol" = ( +/obj/structure/chair/sofa/right{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Op" = ( +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Or" = ( +/obj/structure/table/wood, +/obj/item/cultivator, +/obj/item/seeds/cotton, +/obj/item/seeds/cotton, +/obj/item/seeds/cotton, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ou" = ( +/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/ocular_warden, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Oz" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"OD" = ( +/obj/structure/window/reinforced{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"OF" = ( +/obj/structure/flora/ausbushes/ppflowers, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"OJ" = ( +/obj/structure/mineral_door/wood{ + tag = "icon-wood"; + icon_state = "wood" + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"ON" = ( +/obj/structure/table/wood, +/obj/item/harpoon, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"OR" = ( +/obj/structure/flora/ausbushes/grassybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"OT" = ( +/obj/machinery/disposal, +/obj/structure/disposalpipe/trunk{ + dir = 1 + }, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"OY" = ( +/obj/structure/flora/ausbushes/reedbush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Pb" = ( +/obj/structure/chair/stool, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Pm" = ( +/obj/structure/chair/office/light, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Po" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Pp" = ( +/obj/effect/overlay/palmtree_r, +/obj/structure/flora/ausbushes/sunnybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Pu" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Pz" = ( +/obj/machinery/vending/clothing, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"PE" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"PH" = ( +/obj/structure/flora/grass/green, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"PL" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"PR" = ( +/turf/unsimulated/wall{ + icon_state = "rock"; + name = "rock" + }, +/area/space) +"PW" = ( +/turf/simulated/shuttle/wall{ + tag = "icon-swall3"; + icon_state = "swall3" + }, +/area/awaymission/beach/offshore) +"Qf" = ( +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"Qh" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"Qm" = ( +/obj/structure/chair/beachchair{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Qu" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/stalkybush, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"QF" = ( +/obj/machinery/newscaster{ + pixel_x = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"QI" = ( +/turf/unsimulated/wall/fakeglass{ + icon_state = "fakewindows2"; + dir = 1 + }, +/area/awaymission/undersea) +"QJ" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_l, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"QM" = ( +/mob/living/simple_animal/hostile/carp/megacarp{ + name = "Mega Sea Carp" + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"QN" = ( +/obj/effect/decal/snow/sand/surround{ + name = "rough sand" + }, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"QR" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"QW" = ( +/obj/structure/chair/beachchair/red{ + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"QY" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"QZ" = ( +/obj/structure/rack, +/obj/item/restraints/handcuffs/cable/zipties, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Rj" = ( +/obj/structure/table/wood, +/obj/item/storage/bag/plants, +/obj/item/clothing/gloves/botanic_leather, +/obj/item/shovel/spade, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Rk" = ( +/obj/structure/table/wood, +/obj/machinery/pos, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Rl" = ( +/mob/living/simple_animal/hostile/deathsquid, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"RG" = ( +/obj/structure/chair/beachchair/red, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"RL" = ( +/obj/structure/chair/wood/wings{ + tag = "icon-wooden_chair_wings (EAST)"; + icon_state = "wooden_chair_wings"; + dir = 4 + }, +/obj/structure/sign/poster/official/cleanliness{ + pixel_x = -32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"RN" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"RQ" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f9"; + icon_state = "swall_f9" + }, +/area/awaymission/beach/offshore) +"RU" = ( +/turf/unsimulated/beach/water/deep/rock_wall{ + icon = 'icons/turf/walls/wood_wall.dmi'; + icon_state = "wood"; + name = "Wood Wall" + }, +/area/awaymission/undersea) +"RV" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"RY" = ( +/obj/structure/sign/poster/contraband/syndicate_recruitment{ + pixel_x = 0; + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Sb" = ( +/obj/structure/closet/crate, +/obj/item/toy/syndicateballoon, +/obj/item/toy/figure/syndie, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Sc" = ( +/turf/unsimulated/floor/abductor, +/area/awaymission/beach/offshore) +"Se" = ( +/obj/item/clothing/head/nursehat, +/obj/structure/sign/poster/official/healthy{ + pixel_x = 32 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"So" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/overlay/coconut, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Sp" = ( +/obj/structure/mineral_door/sandstone, +/turf/space, +/area/awaymission/undersea) +"Ss" = ( +/obj/structure/sign/poster/official/cohiba_robusto_ad{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Su" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Sw" = ( +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Sy" = ( +/obj/structure/mecha_wreckage/ripley/firefighter, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Sz" = ( +/obj/structure/flora/ausbushes/reedbush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"SD" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy{ + id = "volcano_island" + }, +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach/offshore) +"SI" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 6 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"SL" = ( +/obj/structure/sign/poster/contraband/lusty_xenomorph{ + pixel_x = 32; + pixel_y = 0 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) +"SO" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 9 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"SS" = ( +/mob/living/simple_animal/hostile/retaliate/carp/koi/honk, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"ST" = ( +/obj/structure/flora/rock/pile, +/obj/structure/flora/ausbushes/genericbush, +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) +"SX" = ( +/obj/machinery/vending/coffee/free, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Td" = ( +/obj/structure/closet/cabinet, +/obj/item/reagent_containers/food/drinks/bottle/rum, +/obj/item/reagent_containers/food/drinks/bottle/rum, +/obj/item/reagent_containers/food/drinks/bottle/rum, +/obj/item/reagent_containers/food/drinks/drinkingglass, +/obj/item/reagent_containers/food/drinks/drinkingglass, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"TF" = ( +/obj/structure/mirror{ + pixel_y = 32 + }, +/obj/structure/sink{ + pixel_y = 16 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"TG" = ( +/obj/structure/dresser, +/obj/item/storage/wallet/random, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"TI" = ( +/obj/structure/closet/crate/secure/loot, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"TL" = ( +/obj/structure/falsewall/brass, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"TP" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Ul" = ( +/obj/structure/flora/ausbushes/sparsegrass, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Uv" = ( +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, +/area/awaymission/undersea) +"Uy" = ( +/mob/living/simple_animal/hostile/carp/megacarp{ + name = "Mega Sea Carp" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"UF" = ( +/obj/structure/bed, +/obj/item/bedsheet/red, +/obj/structure/sign/poster/official/ue_no{ + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"UK" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"UL" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f10"; + icon_state = "swall_f10" + }, +/area/awaymission/beach/offshore) +"UM" = ( +/obj/structure/chair/stool, +/mob/living/simple_animal/hostile/syndicate/ranged{ + loot = list() + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"UN" = ( +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"UP" = ( +/obj/structure/rack/skeletal_bar/right, +/obj/machinery/chem_dispenser/soda, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"UQ" = ( +/obj/structure/sign/directions/medical{ + dir = 1; + pixel_y = 32 + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"UR" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"UU" = ( +/obj/structure/closet/crate, +/obj/item/stack/sheet/mineral/uranium{ + amount = 20 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Va" = ( +/obj/structure/grille, +/obj/structure/shuttle/window, +/turf/simulated/floor/plating/airless, +/area/awaymission/beach/offshore) +"Vh" = ( +/mob/living/simple_animal/crab{ + desc = "A hard-shelled crustacean. There's a mad look in its eyes."; + faction = list("nether"); + melee_damage_lower = 3; + melee_damage_upper = 3; + name = "strange crab" + }, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Vp" = ( +/obj/effect/decal/cleanable/blood, +/obj/structure/chair/wood/wings{ + tag = "icon-wooden_chair_wings (EAST)"; + icon_state = "wooden_chair_wings"; + dir = 4 + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Vq" = ( +/obj/structure/flora/rock, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Vw" = ( +/obj/structure/closet/secure_closet/personal, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"VE" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"VO" = ( +/obj/structure/ladder/unbreakable/dive_point/buoy{ + id = "brass temple" + }, +/turf/unsimulated/beach/water/drop, +/area/awaymission/beach/offshore) +"VP" = ( +/obj/item/ship_in_a_bottle, +/obj/structure/table/wood, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"VR" = ( +/obj/structure/flora/bush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"We" = ( +/obj/structure/flora/ausbushes/palebush, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"Wm" = ( +/obj/structure/mecha_wreckage/odysseus, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Wq" = ( +/obj/structure/chair/comfy/shuttle{ + dir = 1 + }, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"Wr" = ( +/obj/effect/overlay/palmtree_l, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Wy" = ( +/obj/machinery/gateway{ + dir = 4 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"WK" = ( +/obj/structure/closet/secure_closet/freezer/fridge/open, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"WL" = ( +/obj/structure/chair/comfy/black{ + dir = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"WN" = ( +/obj/structure/closet/crate/can, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"WQ" = ( +/obj/structure/flora/ausbushes/leafybush, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"WS" = ( +/obj/structure/flora/ausbushes/lavendergrass, +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"WV" = ( +/obj/machinery/seed_extractor, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"Xa" = ( +/obj/effect/decal/remains/human{ + plane = -1 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Xc" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Xi" = ( +/obj/machinery/door/airlock/hatch/syndicate{ + name = "Bunk House" + }, +/turf/unsimulated/floor{ + icon_state = "bar"; + dir = 2 + }, +/area/awaymission/beach/offshore) +"Xq" = ( +/obj/structure/table/wood, +/obj/machinery/chem_dispenser/soda, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Xr" = ( +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Xz" = ( +/obj/item/clothing/under/color/lightred, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"XF" = ( +/obj/structure/table/wood, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"XG" = ( +/obj/structure/flora/rock, +/obj/structure/flora/ausbushes/leafybush, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"XH" = ( +/obj/structure/piano, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"XL" = ( +/obj/effect/overlay/palmtree_r, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"XU" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (EAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 4 + }, +/obj/effect/overlay/palmtree_r, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"XZ" = ( +/obj/structure/chair/stool, +/obj/structure/mirror{ + pixel_y = 32 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Yi" = ( +/turf/unsimulated/wall/fakeglass{ + icon_state = "fakewindows2"; + dir = 1 + }, +/area/awaymission/beach/offshore) +"Yj" = ( +/obj/structure/closet/cabinet, +/obj/item/clothing/head/pirate, +/obj/item/clothing/suit/pirate_black, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Ym" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (SOUTHWEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 10 + }, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Yo" = ( +/obj/structure/shuttle/engine/heater{ + dir = 1 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" + }, +/area/awaymission/undersea) +"Ys" = ( +/obj/effect/overlay/palmtree_l, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"Yt" = ( +/obj/machinery/hydroponics/soil, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Yv" = ( +/obj/structure/table/reinforced, +/obj/item/storage/backpack/duffel/syndie/med, +/turf/unsimulated/floor{ + icon_state = "white" + }, +/area/awaymission/beach/offshore) +"YB" = ( +/mob/living/simple_animal/crab, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"YF" = ( +/obj/item/clothing/shoes/brown, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"YG" = ( +/turf/unsimulated/beach/sand/dense, +/area/awaymission/beach/offshore) +"YJ" = ( +/obj/structure/table/wood, +/obj/item/stack/spacecash/c100, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"YK" = ( +/obj/structure/flora/ausbushes/genericbush, +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/boundry) +"YM" = ( +/obj/machinery/tcomms/relay/beachresort, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) +"YO" = ( +/turf/unsimulated/wall/fakeglass{ + icon_state = "fakewindows2"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"YR" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone1"; + icon_state = "sandstone1" + }, +/area/awaymission/beach/offshore) +"YS" = ( +/turf/unsimulated/beach/sand, +/turf/simulated/shuttle/wall{ + tag = "icon-swall_f5"; + icon_state = "swall_f5" + }, +/area/awaymission/beach/offshore) +"YY" = ( +/obj/machinery/vending/autodrobe, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"Zb" = ( +/obj/structure/flora/rock/pile, +/obj/effect/overlay/palmtree_r, +/turf/simulated/floor/beach/roughsand/dense, +/area/awaymission/beach/boundry) +"Zg" = ( +/obj/structure/closet/crate, +/obj/item/toy/plushie/nukeplushie, +/obj/item/toy/nuke, +/turf/unsimulated/floor{ + icon_state = "grimy" + }, +/area/awaymission/beach/offshore) +"Zl" = ( +/obj/machinery/door_control/brass/beach_brass_temple_switch{ + pixel_x = -32 + }, +/turf/unsimulated/floor{ + desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; + icon = 'icons/obj/clockwork_objects.dmi'; + icon_state = "clockwork_floor"; + tag = "icon-wood" + }, +/area/awaymission/beach/offshore) +"Zo" = ( +/turf/unsimulated/wall{ + tag = "icon-sandstone3"; + icon_state = "sandstone3" + }, +/area/awaymission/beach/offshore) +"Zp" = ( +/obj/structure/closet/crate{ + name = "Ripley parts crate" + }, +/obj/item/mecha_parts/chassis/ripley, +/obj/item/mecha_parts/part/ripley_left_arm, +/obj/item/mecha_parts/part/ripley_left_leg, +/obj/item/mecha_parts/part/ripley_right_arm, +/obj/item/mecha_parts/part/ripley_right_leg, +/obj/item/mecha_parts/part/ripley_torso, +/obj/item/circuitboard/mecha/ripley/main, +/obj/item/circuitboard/mecha/ripley/peripherals, +/obj/item/mecha_parts/mecha_equipment/drill, +/obj/item/mecha_parts/mecha_equipment/cable_layer, +/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp, +/obj/item/mecha_parts/mecha_equipment/rcd, +/obj/item/stack/sheet/plasteel{ + amount = 20 + }, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "titanium_blue" + }, +/area/awaymission/undersea) +"Zr" = ( +/obj/effect/rune, +/obj/structure/spawner/nether{ + max_mobs = 3; + name = "weak netherworld link" + }, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) +"Zt" = ( +/obj/item/clothing/under/rainbow, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) +"Zu" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/wall/mineral/sandstone, +/area/awaymission/beach) +"Zy" = ( +/obj/structure/table/reinforced, +/obj/item/storage/belt/military/traitor, +/turf/unsimulated/floor{ + icon_state = "vault"; + dir = 8 + }, +/area/awaymission/beach/offshore) +"ZD" = ( +/obj/structure/closet/crate, +/obj/item/stack/marker_beacon/thirty, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"ZE" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTH)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 1 + }, +/obj/structure/chair/stool/bar, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) +"ZG" = ( +/obj/structure/closet/crate, +/obj/item/clothing/suit/pirate_brown, +/turf/unsimulated/beach/sand, +/area/awaymission/beach/offshore) +"ZN" = ( +/obj/machinery/door/airlock/glass{ + name = "Resort Backroom"; + req_access_txt = "25" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) +"ZU" = ( +/obj/machinery/computer, +/turf/simulated/shuttle/floor, +/area/awaymission/beach/offshore) +"ZW" = ( +/obj/structure/flora/rock/pile, +/turf/unsimulated/beach/water, +/area/awaymission/beach) +"ZZ" = ( +/obj/item/clothing/shoes/sandal, +/turf/unsimulated/beach/water/deep/sand_floor, +/area/awaymission/undersea) (1,1,1) = {" -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadaeaUavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadavavavavavavavadadadadadadadadadadadavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavadadadadadadadadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadaUaUavadavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavadadadadadadadadadadavadmIsOovszXradadadadavavadavavavavavavavavavavadadadadadadadadavavavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavadadadadadadadavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadavavavadhmXrXrXrXradavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavadadadadadadadadavavadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaYadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadavavavadLjXrXrXrKradavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadadadadadavavadadadavavavavavavavavavavaoavavavavavavavavavavavavavavavavavadavavavavavavavavavavavUyavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadavavavavadXrXrXrXrpeadavavavavavavavavavavavavavavavanavavavavavavavavavanavavavavavavavavavadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavadadadadadadadadadadadadadadadavavavavavavavavavavavavavavavadadadadadadadavavavavadGoGoXrXrXradavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadavavavavavavavadadadavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavadadadadadavavavavavadadadadaYadadavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavadavavavavavavavavavavavavaoavavavadavavavavavadadavavavavavavavavavaqavavavavavavavavavadavavavavavavavavavavavavavavavadadadadavavavavavavavJQrGavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavadadavavavavadadavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavasadavavavavaoavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavUyavavavavavavavavavavavadavavavavavavadavavavadadadadadadadadadadavavavavavavavavavadavavavavavavavavavavavavavavogogavavasavavavLvavavavavavavavavavavavavavavavavadadafadagahagadafadadadadadadadavavavavavavanavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavaqavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavadavavavavavadavavavavavavadavavavadadavavavavavavavavavadadavavavavavavavadavavavavavavavavavavavavavogSwSwSwucavSwLvavavavWmahavavLBavavavavavavavavavadadahahCAKLxNceahahahadadaiamaiadavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadadadavavavavadavavavavavavavavadadavavavadavavavavavavavavavavadadavavavavavavahahavavavavadavavavavavavadavavavavavavavavavavavavnJNVSwSwSwavavavavEdavahahadLBavavavavavavavavavavavadahahahaiakoPajaiahahahalahahahadanavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavadadadavavadavavaoavavavadavavadavavavavavavavavavavavavavadavavavavavavahahahahavavavavadavavavavavadavavavavavavavavavavavavnJNVSwZpSwavavSyasavavavahahaoavavavavavavavavavavavadafahahahahahahahahahafadahahahadavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavadavadavavavavavavadavadavavavavavavavavavavavavavavadadavavavavavahahadadahahavavavadavavavavavadavavavavavavavavavavavavnJNVSwSwSwanavavavSwavaneMadavavavLBavavavanavavavavadahahapaiahahahaiapahahadahnZaTadavavavavadadadadadadadadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadadavavavavavavavavavavavadadavadavadavavavavavavavavaoavavavavavavadadavavavavavavahawadadahahavavavadavavavavavadavavavavavavavavavavavavavogSwAPSwavavSwavavasavLvahavavavavavavavavavavavavadahahahahjrjrjrahahahjdadadadadadavaoavavadaxahahahahayadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavadadadavavavavavavavavavavavavavavadavavavavavavavavavavadavavavadadadavadadavadadadadadavavavavadavavavavavavavavavavadadavavavavavavavahahahahavavavavadavavavavadavavavavavavavavavavavavavavavogogavavavavavavavavahahavasavavavavavavavavavavadaroYapahjrZrjrahapaharadadavavavasasavavadvMUPazaAahaBadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadadadadadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadadavavavadavadavadadadadavavavavavavavavavavavavadadavavavavavavavahahavavavavadavavavavavadavavavavavavavavavavavavavavavavasavavasavanavLvavasavavLBavavavavavavavavavavadahXzahahjrjrjrahahahYFadavavavavavavasadadahahahahahahadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadadavmcadavavavavavavavavavavavavavavavadadavavavavavaoavavavavavavavavavavavavavavavadavavavadavadadavadavavavavavavavavavavadadavavavavavavavavavavavadadadavavavavadadavavavavavavavavavavavavavavavavasadavavavavavavavavavasavavavavavavavavavavavadahahapaiahahahaiapahahadavavavavavavavadaIaEaJahahaIaEaJadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavaoadavavavadadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavadavadadavadavadavadadavavavavavavavavavavadadavavavavavavadadadadadavavavavavavadavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavadahahYFahapapapahahBHahasavavavavavavavadahahahahahahahahadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadavavavavadadadavavavavavavaoavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadadadavavavavavavadadavavavavavavavavavavadadadavavavaqavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavadadadadadasadasasasavavavavasavavavavavavavavadadAparatahahahatarCAahavavavavavavavavadaIaEaJahahaIaEaJadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadavadadadadadadadadavavavavavavavavavavavavavavavavadavavavavavavavavadavadavavavavavavavavavavadavavavavavadavavavavavavavavavavavadadadadavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavadadaGadlDaSxXxbadavasasaoasavavavavavavavavavasadadadadauSpauadadadadasavavavavavavavadahahahahahahahahadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavTLavjYavavavadadadadadavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavadadadavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavadadaNadahahahahaCavavaoasHJavavavavavavavavasavavavavavasasasavavavasasasavavavanavavadXHaJahahahaIaEaJadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavadadadadavavadadadadadavavavavavavavavaqavavavavavavavavavavavavavavadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadavadadavadadadadadavavavavavavavavavavavavavavavavavavavavavavadadaCadarahahcbadaOavavavavasavavavavavavasavavavavavavavavavavavavavavavavavavavavavadadadadahahadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadadadadadadadadadadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavadavavavavavavavavavavavavavavavadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavadadahahahagagahadavavavavavavadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadahahaIEuhRaJadavavavavavavadJHahwfVpNEFaadasavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadadadadadavavavavavavavavavavavavavavavavavavavavavavavavadavavadavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavadMYahmlahiCahadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadadadadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadLEahZrMOxmUvadavavavavavavadadadadadadavavadadadadadadadadavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadQZahxmiCxbxmadavavavavasavadaGTFahahadavasadaGadahxTrHOradYtavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadadavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadadadadadxbadadavavavavavasadadadaCadadasavadaNaCahahahhHadYtavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavavavEpavaPLMhTvAavavavavavaOadrkahahahadavavadadadmAaFahahaCavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaEyHimimgqahadavHJasasavavavavavavavavmQhpavavHJavavavaCahahahahaCwlavavavadahahahGWadYtavavavanavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadwWahahahahahadasavavavHJavavavavavavavavavavavavasavavadaEaJahaSadVhkgavavadaSahahFMadYtavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavaqavavavavavavavaqavavavavavavavadadavadadavavavavavavavavavadavavavavaqavavavavavavavavavavavavavadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadrcahahYJarjdadavavavavavasavavavavavavavavavavavavavavadadadadadadHJavZtVhadadadaCadadavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavadavavavavavavadadavavavavavavavavadavavavavavavavavavavavavavadavadadavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaEahahaEEHahaCavavavavavavadadadadadadadadasavavavavavavavavavavasavavavZZavavaOavavasavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavaoavavavavavavavavadavavavavavavavavavavadavavavavavavavavadadadavadadavadavavadavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadnLarahaEahahaCavavavavavavadaDaEaFahadaGadadavasavavavadadadadadadavqXavavHJavavavavavasavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavadadadadadadadavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadnDBHYFRkahahadavavavasavavadahApahjXadahaMadasavasasasadaGusahahadavavavwPavavavavavasavavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavadaroYXzahadaCadadavavasavavadadadaCadadavavkgVhavAQavavasavavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavIoavavavadaharYFahaCahaRadavavavavavaCahaRarBHaCavavavavadadaCadadadavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavVhyravavadahahahahadaSTGadavavavavaOadahahahahadavavavavadahahahbQadavavanavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavHJavavavavadadadaCadadadadadavavHJavavadaEaJCAaSadavavavavadaIahzBSeadavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrXavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavaOavavavavavavavavasavavadadadadadadavavavavadRLarHyhYadavavavavavadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavadavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavadadadadadavavavavavavavavavavavavavavavavavavavavavavavasavavavavavavavadaIahahCiadavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadadadadadadadadadadadadxQxQxQxQxQadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadadadadadadadadadadxQxQxQxQxQadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaPavavavavavaPQIahahONadavavavavavavavavavavavavavadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavQIahahaJadavavavavavavavavavavavavavavadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadavavadavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavQIahahahadavavavavavavavavavavavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRURURURURURURURURURURURURURURURUavavavavavavavavavadavavavavavavavavavavavavrFavavavavavavavavavavavavrFavavavavavavrXavanavavavavavavavavavavavaoavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadaYadadavavavavavavavavavavavavavavavavadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUBTahYjRUInInahahvqCovqahahkTahRURUavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrXavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUKsUvahRUInUviCahahahahahUvahahiCahRURUavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUaEaJnFDsahahCzahCziCCzahCzahCzahahahahRURUavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUaEahahRUahiCahahahahahahahahahahahRURUavavavavavavavavavavavavrXavavavavavavavavavavrXavavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRUTdUvwxRUahUvahahahahahRNahahahRURUavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavRURURURURURURURURURUgBaVaVRURURUavavavavadadadavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavuzavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavSSavavavavavavavavavavavaqavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavuzavavavLBavadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavLBavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavuzavavavavavavLBavavavavavavavavavanavavavavavavavavavavavavrFavavavavavavavavavavavavavavavaUaUaUavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavanavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaUcxaUavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavadadavavadavavavavavavavaUaUaUavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavLBavavavavLBavavavavavavavanavavavavavavavavavavanavavavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavanavavavavavavavavavavavavaoavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavrFavavavrXavavavavavavavadavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavSSavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadavavavavavavavavavavavavavavavavavavadavavavavavavavavavavanavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadadadavavavavavavavadadavavavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavaqavavavavavavavSSavavavavavavavadavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavadavavadavadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaqavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadavavavavavavavavavadavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavadadavadadavadavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavadavavavavavavavavadadavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavadavavavavavavavavavavavavavavadavavavavavavadavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavrXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavadavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavadavavavavavavadadavavavrXavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavadavadavavavavadavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadavavavavavavavavavavavavavavavavavadavadavavadavavadavadadavavavavavavavavavavavavavavavavavavavavadadavadadavavavavavavavadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavadavavavavavavavavavavavavavavavavavavadavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavadavavavavavavavavavavavavavavavavadadavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavadavadavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavadavavavavavavavavavavavavavavadavavavavadavavavavaqavavavavadavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavadavavavavavavavavavavavavavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavadavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavadavadavavavavavavavavavavavadavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavadadadavavavavavavavavavavavadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavadavavavavavavavavavavadavavadavavavavavavavadavadadadavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavadadavadadavadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadadadadadavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavadadavavavavavavadadavavavavavavadavadavavavavavavavavavavavavavavSSavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavadadavavavavadavavadavadavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadavavavavavavavavavavrFavavavavavavavavavavavadavavavavavavavavavavavavadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavadadavavavadadadavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavTIavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavadavavavavavavavavavavavavavavavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavadavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavadavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavadadadadadavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavaoavavavavavavavavavavavavavavavavadavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavavavadavavavavavavavavavadavavavavavavavadavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavavavavavavavavavavavavavavavadadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavadavavavavavaoavavavavavadavavavadavavavavavavavavadadadavadadavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavanavavavavavavavavavavavavavavavadavavavavavavavadadadavavavavavavavavavavavavavavavavavavavavavavadadadavavadavavavadavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavTIavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavadavavavavavavavavadavavavavavavavavavavadadadavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavavaoavavavavuzavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavadavavavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavadavavadadadadadadadavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavaoavavavavavavavavavavadavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavadadadadadadadadadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavavavanavavavavavavadadadavavuzuzuzavuzavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadavavadavadavadavadadadavadavavavavavavavavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavadavavavavavavavavavadadadavaUaUaUavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadaoavanavavavavavavavavavuzavavavavavavavuzHJavuzavavavavuzuzavuzavavavavavavavavavavavavavavavadavavadavavadavavadavavavavavavavavavavavavadavadavadadavavavadadavavavavavavavavavavavavavavavavavavavhDhDavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavadadadavaUcyaUavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadavavavavavavaoavavavavuzavavuzuzuzavavavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadadadavavavavavavavavavavavavavavavavavavavavXrYoYoogavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadavavavavavavavavadadadavaUaUaUavadavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadaoavavavavavavavavavavavuzuzavavavavavavavavavavavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavaoSwSwSwMbMbavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavadavavavavavavavavadadavavavavavadadavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadHJavavavHJavavavavavavavavanavavavaoavavavavavavuzavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavTIavavavogAmSwSwSwAmogavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadadadadadavadadadavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadavavavavavavavavavavavuzavavavavuzavavavuzuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavSwSwSwSwSwogavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavadavavavavavavavavavadadadadavavavaYavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadHJavaoavavavuzavavavavuzavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavSwEdQMSwSwogavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavadavavavavavavavavavadadadadadadadadadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadadHJavavavavuzavavavavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavogSwSwEdSwSwogavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadadadadadadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadHJavavavaoavHJavavaoavavHJadadxQavuzuzavavavavuzaoavavavuzavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavogAmSwSwSwEdogavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadadavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadaoavavavuzuzavavavanavavavavavavxQxQavavavuzavavavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavogSwSwFNSwAmogavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadHJavavavuzuzavavavavavavuzavavavavxQxQavuzavanavuzavavavavavavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavrFavavadavavavavavavavavavavavavavavavavavavavogSwEdSwEdSwogavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavuzavavavanavavavavavuzavavavadxQuzavavuzavavavuzavavavavavavavavavavavavavavavavavavavavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoogkPkPkPogavavavavavavavavavavadavavavavavavavavavrFavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadavavavuzavavavavavavavavavavuzavavadadHJavavavavavavavavuzavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavavavavavavavavavadavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavaqavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadHJanavavavavHJanavavavuzuzuzavuzuzavaoadadaoavuzavavuzavHJavavuzavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavaoavavavavaoavavavavavavavavavavavadavavavavavavavavavavavavadadadavavavavavrFavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavuzavavavavavavavavavavavuzavavavavadadavavavavuzuzavuzavavavavavavadadadadadadadavavavavavavavavavavavavavavavavavavavavavavavadadadavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadaXavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavuzuzavuzavavavavavuzavavavuzuzavuzavHJadHJavavavavavavavavavavavavavadavadadavavadadavavavavavavavavavavavavavavavavavavavavavadavavavadavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadaoavavavuzavanavavRlavavuzavavavavavavavavadadavavavavavaoavuzavavaoavavadavavHJsFHJavadavavavavavavavavavavavavavavavavavavavavavadavadavadavadavadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavuzavavavavavavanavuzavavavavadadavavavavavavavavavavavavadadavavavavavadadavavavavavavavavavavavavavavavavavavavavavadavavavadavadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavaqavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavuzavanavuzavavavavavavavavavuzuzuzavuzavHJadHJavavuzavuzavavavavavavadadadadavavavadavavavavavavavavavavavavavavavavavavavavavavavadadadavavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadavavavavavavavanavuzavavavanavuzavuzavavavaoadadavavavavavavavuzavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavaqavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadHJavavuzavuzavuzavavuzuzavavavavavavuzavavavadadaoavavavavavavavavavavavavavadadadadavavavavavavavavavavavavavavavavavavavavavavadavavSSavavadavavSSavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadaoavavavavavuzavavuzavavavHJavanavavavuzavadadadavavavuzavavavuzavavavavavavadadavavavavavavavavavavavavavavrFavavavavavavavavadadavavavavvNavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavadadadadadavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadavavuzavanavavavavavavavavavavavuzavavavadadadHJavavavavanavuzavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadadavavvNlZvNavavadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadHJavavavavavavavavanavavavuzavuzavavavHJadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavadadadavvNavadadadavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadavavavuzavuzuzavavuzavavavavavavavaoadadadadadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavadadadadadavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadaoavavavanavavavavavavanavavavaoadadadadadadadadaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadHJavavaoavHJavanavaoavHJadadadadadadadadadadadaoavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadadavavavavavavavadadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavavadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadadababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababababaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaYGYGYGtNoHYGYGYGYGYGNTYGixrxPENTNTrxFiYGQNYGYGIZvrvroHYGYGYGtNfABMfABMvnkQYGYGYGfABMmwteaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafAYGYGYGvkYGYGvkYGYGvkfABMNTNTNTPEYGtoAaYGYGYGBvvrFTvkfAYGYGYGYGERYGERQRFTvkYGvkYGERQRBmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaYGYGlXBYSzYGYGBYqxBYBYBYXLFFNetoNTQhBYBYBYXLXLvrvrUKYGYGYGlXBYqxvrqxvrvrsZBYvklXqxvrvrtcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaXLxIjivrvrXLBYVRvrvrsZjiVRvrvrWQNewOvrjiGjvrvrjijivrBYXLxIjivrvrsZvrsZjiVRjiWSBvvrsZjiyqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaGjVRvrjivryJvrvrsZYsGjvrjivrWQvrWQGjjivrvrjisZjivrjivrGjVRvrjisZvrsZvrsZvrWQEjIZsZvrsZvraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaavrvrGjvrvrvrjiORvrwOvrWQvrWQqUvrqUvrwOvrWQvrvrGjOYwOWQvrvrGjvrvrLVvrLVvrjiwOYGBvvrLVvrvkaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxDvrGjjiOYsjLzeSQJtMxZfAzkxDfAfAfAxZfAeStMzkxZfAQJfAeSxDvrGjjijivrjisZHfwOeSvkIZjisZHfDVaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaYGkHvrLVXUvkqovrvrvrvrvrvrvrvrWQvrLLJjJjJjJjJjHNvrLLJjJjJjJjJjJjJjJjJjJjJjHNYGBvjivrvrvraaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazksZvrvrFTvkfAvrvrvrvrvrvrvrvrvrvrZoCMCMCMCMCMZovrZoCjVPspBnYRpdvrvrZoUUvOZovkKtvrjijiAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatMwOjijiFTyqAavrvrvrvrvrvrvrWQvrvrZoCMCMCMCMCMZovrZoPmvrpdvrOJvrvrvrYRvrBAZoBYvrvrORvreSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazkkHjiyJFTYGqovrHYtiVatiULvrvrvrvrZoCMCMopCMCMZovrZoJjJjJjJjHNvrvrvrOJvryDZoHfjisZvrYskHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafAGjORLVjiqxlevrPWoyZUGZPWvrvrvrvrZoCMCMCMCMCMZovrZovrvrsNZGZoOJOJOJLLJjJjJjvrOYvrjivrYGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaxZvrvrGjjiOYPpvrPWNsWqoGPWvrvrvrvrYRCMCMCMCMCMYRvrZovrvrvrvrZovrvrvrZoisvrZoYsvrWQWQjiAaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatMjijivrjivrrAvrpDoGoGoGpDvrvrvrvrvrvrvrJdvrvrvrvrZovrvrvrvrZovrvrvrZovrvrZoINjiORYsjiYGaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazkwOvrjivrFTzwvrpDoGoGoGpDvrvrvrvrvrvrvRvrvrvrvrvrZoOJkEkEOJZovrvrvrZovrvrZoLKIZyJvrvrDVokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafAjiOYvrsZJMqovrPWEXoGqjPWvrvrvrvrvrvrvrPbPbvrvrvrYRvrvrvrvrYRvrvrvrYROJOJZoEjIZjiLVjiwOokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRxZGjvryJvrAavkvrPWCvCvCvPWvrvrvrvrvrvrPbgDjEPbvRvrOJvrvrJdvrvrvrvrvrvrvrvrZoYGWrjivrvrYGokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRtMjiLVjisZkQObvrYSlHtrvYRQvrvrvrvrvrvrPbuNcDPbvrvrOJvrvrvrvrvrvrvrvrvrvrvrZoXLsZvrjijihPokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRfAvrVRHfvrjisZkQvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrkEJjJjJjJjJjJjJjJjJjkzWNYRvrvrsZvrvrGjokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRzkWQvrjijiHfvrUKvrwOvrWQvrWQwOGjvrvrwOvrWQvrvrGjOYwOWQvrvrGjvrvriUYGiUDVqoYGYsVRYsjivrYGokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRQuvrvrvrvrjivkwJQJtMxZfAzkxDeSxDYGxZfAeStMzkxZfAQJfAeSxDYGAavkvkYGvksZfAeSxDvrvrvrvrvrYGokokokokokokokokokokokokokoktVtVtVtVtVtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRvrvrvrjisZfAeSqxBYBYBYXLFFxIYGYGlXBYBYBYXLXLvrvrUKYGYGYGlXBYqxvrqxvrvrvrsZvrsZjiBYXLxIYGokokokokokokokokokokokokokoktVbGbrbEbFtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRjijiqxvrvrvrsZvrvrsZjiVRvrwOXLxIjivrjiGjxxvrjijivrBYXLxIjivrvrsZvrsZjisZvrsZvrsZjiBYXLxIokokokokokokokokokokokokokoktVqRqRqRbPtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRPRjivrvrsZjisZvrsZYsGjvrjivrGjvrvrvrxxxxvrvrvrvrvrvrvrGjVRvrjisZvrsZvrsZvrsZvrsZvrsZvrGjVRokokokokokokokokokokokokokoktVyzqRqRxetVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZJZNPqPImrtJZJZJZJZCmIOHpJZNPJZJZJZIuJZCmwwJZJZiuJZJZqbJZiuJZJZJZJZJZYmwwwwznJZMyJZJZCmJZJZznJZJZJZYmznJZJZXLvrsZvrsZvrsZBYqxBYBYBYXLFFYGfAYGvrkHlSlSlSlSlSvrkHYGYGYGlXBYqxvrqxvrvrsZBYvklXqxvrvrtcokokokokokjLjLokokokokokoktVtVtVtVJftVtVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZiIfRNZMxNGiIiIiISOfRfRfRYKEMVEVEEMfREMIxiILkfRiIVEEMiIfRNGiIfRfRiIVqLqfRfRiIzZfRfRfRCrfRNGiIVqfRfRfRiIfRJZvrjivrqxvrvrsZVRvrvrsZjiVRvrvrvrvrkHScCfxRScSclSlSvrvrkHvrvrvrvrsZvrsZjiVRjiWSBvvrsZjiyqokokokokokokjLjLokokokokoktVYvgLwbIlbItVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ifSOPLfRfRIaEMVEVEfRzriIiIIauRfRfRzriIiIfREMiIfRzrfRfRiIiIuREMPLEMEMEMVEIpSoEMEMEMVEVEfRfRIxfRfRfRSOEMoCfRJZjijiqxvrvrvrsZvrvrsZjiVRvrvrkHxxkHnUScScScScScSclSlSvrvrvrkHkHvrvrsZjisZvrsZvrsZjiBYXLxIokokokokokokokjLokokokokoktVsxIlIlIlIltVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -zLCrfRIaiIzriIfRuRiIfRiIiIMxfRuRiIfRfRIauRfRuRiIfRiIfRgWfRfRzrfRfRIaiIzrfRfNfRiIzZfRfRiIiIfREMVESoiIfRfRVESukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokokokokjLokokokokoktVtpJEIlbKvetVokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NnLkIafRIafRlJMxiIiIIafRfRzriIuRiIfRMxfRuRuRgWIafRfRiIfRgWiIfRIauRzZfRiIfRzZiIfRfRiIIaiIfRiIfRzZzrfRiIfRLqJZkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokokjLjLjLjLjLtVtVtVtVtVtVtVJKtVtVtVokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZCrfRgWfRiIfNfRlJfRzZiIfRfNfRfNiIpufRlJfRfRzZfRhnIdfRzZfRfRnVfRfNfRlJfRlJfRfNfRlJfRfRzZwRfNlJfRfRzZfRfRfRifkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokokjLtVtVtVtVtVqlbRRYzCzCUQzCzhUMtVokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ifLkiIIakqfNgTrgFsMBNPrgFsMBgTrgNPyGFsMBgTFsNPMBFsNPgTwQZbNPgTziFsDbNPMBZbDbNPgTFsMBDbNPziNPgTZbfRzZiIwRhvmYkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmokokokokjLjLYiJrCGCGzRzCzCzCzCzCzCzCzCKetVokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZCriIfRfRfRNPHIySySFphIhIhIhIhIhIhIhIhIDRDRDRKjKjKjKjKjKjKjDRDRDRDRDRMqlQDRDRDRDRDRDRDRDRDRMqJZVqfRgWOziIMLkmkmkmkmkmkmkmkmkmfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokjLjLNRYOYOqKtVtVtVtVtVXitVtVtVtVtVtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ifKafRiIiIzZMBpwkOloTPhIhIxvQfhIhIhIhIQfDRbYoBfcWVchcjcjckwdcmcnKkEqKkDRFySsloskaKskGmgpGmskDRMBIafRfRNGiINPkmkmkmkmkmkmkmkmkmfjfjfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjrvfjfjfjkmkmkmkmkmkmkmkmkmkmokokokjLjLjLjLjLjLjLoktVfEwScdwSNQSbBNnbIGtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -SufRfRnVfRfVFspwXFloTPhIhIhIhIjNCCGuhIxviLoNUNcococococococococpcqcqcrDRXFloloGmskGmskGmskGmDRFsfNiIiINGzrFlkmkmkmkmkmkmkmkmkmfjfjfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjKbfjfjfjkmkmkmkmkmkmkmkmkmkmokokokjLokjLjLjLjLokoktVSXwSwSwSwSwSvhqRqRtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PHiIIafRuRVqNPpwQmloTPhIhIhIhIHQkUWyhIhIDRoNUNcocococococococoDPUNkdEEDRBzloloskGmskGmskGmskDRMBVqiILqNGfRMLkmkmkmkmkmkmkmkmkmfjrvKbOpQYOpQYOpQYOpQYOpQYOpQYOpQYOpQYOpQYOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokjLokokokjLjLokoktVwrwSKUZgncyYwSqRpbtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZwRfRiIfRfRMBpwQWloTPhIhIhIxGctcucvxGhIDRjAUNaLaLaQiTcocoyTiXrTHCRjzQDRGdloloGmskGmaHGmskGmDRNPzZnVgWiIPLNJkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokokokokjLokoktVtVtVtVtVtVtVtVZyoEtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NbfRlJlJiIzZFspwXFloTPhIhIhIhIcwcwcwhIhIDRDRDRDRDRDRDRkfbcDRDRZuMqDRDRDROlloloskGmskGmskGmskDRDbfRfRzZiIwRKSkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpfjOpOpOpOpfjOpOpOpOpfjOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokokokjLjLokokokokokokokokoktVtVtVtVokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -cgiInVuRiIfRMBpwkOloTPhIhIhIhIhIhIhIhIhIURaWbWaZbdbabeBqBqbhbibjbbtFoeDRXFloloGmskGmskGmskGmDRFsiIiIfRiIfRuEkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpfjOpOpfjfjfjOpOpfjOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokjLjLjLokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -PHLkLqfRfRfNDbbwPuPuBRhIhIhIhIbBhIhIhIhIhIhIhIgJbnlolololololovZBqBqbXbdEBloloskGmskGmskGmSLDRMBfNfRiIfRNGEZkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpOpfjfjfjfjfjfjfjOpOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokjLokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NnLkiIgWiIfNNPPoPoPoPoPoPobDhIhIhIhIhIhIhIhIhIbobplobMlolololobJlolobyDRbMlololololololololoDRNPiIwRfRIaChMLkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpfjbxfJLukybxfjOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokjLjLokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZbHiIfRfRfRMBDRDRDRDRDRDRDRhFhIhIhIhIhIhIhIhIbobqbsbtbubvbAbCbkblbmbzDRXFbMvmlolovmbSWLXFWLDRDbzZfRLqfRzZifkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpfjfjmVOpXaOpmVfjfjOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokokjLokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dhIafRiIiIWegTDRVwVwVwVwVwDRwchIhIhIhIhIhIhIhIhIZEZEZEZEZEZEZEZuDRDRDRDRDRDRDRCFloDRDRDRDRDRDRFsiIgWiIIabOXGkmkmkmkmkmkmkmkmkmfjfjfjZlOpOuOpfjfjfjbxXafdXabxfjfjfjOpOuOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmokokokokokokjLjLokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZfRIafRfRzZNPDRlololololoDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIOTbfPubNDmCEAoURURURURURURURURURNPfRzrkqfRiIRVkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpfjfjmVOpXaOpmVfjfjOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmokokokokokokokokokokokokokokokokokokokokokokokokokokokokokokaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NbzruRiIfRuRMBDRDRDRloloDRDRbThIhIhIhIhIYBhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIxvQfhIhINPlJfRiIiIkqJZkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpfjbxOpOpOpbxfjOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZfRVqLqASnlFsDRXZAXloloYYDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIBchINnfRfRfRfRiIifkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpOpOpfjfjfjKbfjfjfjOpOpOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZiIfRNZMxlJNPDRDRDRclloPzDRlahIhIhIhIhIhIhIhIhIyBhIhIhIhIhIhIhIhIhIhIbBhIhIhIhIhIhIhIhIhIhIhINnVqfRgWOziIMLkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpfjOpOpfjKbfjOpOpfjOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ifSOPLfRfRnVMBDRXZAXlololobZwchIhIhIhIhIhIhIhIhIhIhIFbhIhIhIhIhIhIhIhIhIhIcJhIhIhIhIhIhIxvhIhINPIafRfRNGiINPkmkmkmkmkmkmkmkmkmfjrvKbOpOpOpOpfjOpOpOpOpKbOpOpOpOpfjOpOpOpOpKbrvfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -zLCrfRIaiILqFsDRDRDRcllolocawchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIBchIMBfNiIiIrDzrFlkmkmkmkmkmkmkmkmkmfjfjfjZlOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpOpJcfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NnLkIafRIaiIMBDRXZAXlolorVDRlahIhIhIhIhIhIhIhIhIFbhIcZhIdbhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIQfgTVqiILqNGfRMLkmkmkmkmkmkmkmkmkmfjrvKbOpcHOpcHOpcHOpcHOpOpOpcHOpcHOpcHOpcHOpKbrvfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZCrfRgWfRiINPDRDRDRcllobUDRwchIhIhIhIhIQfhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIFbhIFbhIAWloMGnoccfRfRzZiIwRKSkmkmkmkmkmkmkmkmkmfjfjfjKbfjKbfjKbfjKbfjOpOpOpfjKbfjKbfjKbfjKbfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ifLkiIIakqfRDbDRcfqfloloJBDRwchIhIhIhIhIxvhIhIhIhIhIFbhIhIhIYBhIhIhIQfxvhIhIhIhIhIhIhIgJloloODNPiIiIfRiIfRuEkmkmkmkmkmkmkmkmkmfjfjfjrvfjrvfjrvfjrvfjOpOpOpfjrvfjrvfjrvfjrvfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NPfRgWOziIIaFsDRDRDRxHlohBDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIFbhIFbhIgJloNFODccfNfRiIfRNGEZkmkmkmkmkmkmkmkmkmfjfjfjfjfjfjfjfjfjfjfjFdFdFdfjfjfjfjfjfjfjfjfjfjfjkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -RVfRfRNGiIVqMBDRGRFvloloMwDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhICJhIhIhIhIhIIwhIhIhIhIhIhIgJloloODDbfRfRNGfRdPqbkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmNTNTNTNTsvNTNTNTDGNTcLNTNTNTbgNTkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -IOiIiINGzrzZgTDRDRDRDRDRDRDRwchIhIhIhIhIQfxvhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIFbhIFbhIAWlozgIhNPLkfRiIVEEMifkmkmkmkmkmkmkmkmkmkmkmkmNTNTNTNTNTnENTcLgnNTsvNTcLgnNTnENTNTNTNTNTkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMnTnTnTnTnTnTnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -NPiILqNGfRlJNPZDbVDRddBFNqDRwchIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIKMhIhIMBiIfRzrfRfRifkmkmkmkmkmkmkmkmkmkmkmeCeCeCNTeCNTNTDGNTNTHrNTeCcLNTNTNTeCNTNTNTNTNTkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMnTnTvrvrvrvrvrvrvrvrvrvrnTnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -FlnVgWiIPLbLMBhIhIDRyFloloDRdehIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIxvQfNPuRiIfRiIfRmPkmkmkmkmkmkmkmkmkmNTcLNTNTNTNTNTbgNTsvNTcLNTgnNTNTnENTeCNTNTNTeCNTNTNTkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMnTnTvrvrvrBiBiBiBiBiBiBivrvrvrvrvrvrkHnTnTAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -JZfRzZiIwRLYFsJJwiDRemloloEThIhIhIYBhIhIdghIRGhIdghIRGhIdghIRGhIJvQfhIEziKhIhIBchIBchIBchIhIhIFsgWIafRfRiIJZkmkmkmkmkmkmkmkmkmNTNTNTNTcLNToUNTHrNTnEbgNTNTNTHrNTNTbgNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiOiBiBiBiBiBiBiBiBivrvrvrnTAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -ifiIfRiIfRfmNPhIhIDRDRDRDRDRhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIhIxvhIhIhIhIhIhIhIxvhIhIhIhIhIcczZfRfRIdfRFlkmkmkmkmkmkmkmkmNTNTNTcLNTNTNTrSNTNToUNTiFNTNTNTeCoUNTNTNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMnTnTvrvrBiBiBiBiBiBiBiBiBiBiOiBiBiBiBivrvrnTnTAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -djdjdjdjdjdjdkLJeoLJLJLJLJLJerLJLJGzLJLJLJewLJLJLJLJeyLJLJLJerLJLJLJLJLJeyLJLJeBLJLJeyLJGzLJLJdkdjdjdjdjdjdjkmkmkmkmkmkmkmNTPENTNTeCNTPEcLNTeCNTNTeCeCNTeCeCNTNTNTNTeCNTNTPENTNTPENTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMnTvrvrBijLjLjLjLjLBiokokokBiBiBiBijLBiBivrvrnTnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTeCeCNTNTNTNTNTcLciNTNTrSNTcLNTeCNTPENTcLNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMnTkHvrjLjLBiBiBijLjLokjLokokokBiBiBijLBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTNTymeCcLNTeNNTeCwOvrvrzXNTvrNTNTNTNTNTeCeCeNqTNTeNPENTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTnTvrBiBiBiBiBiokokokokjLjLjLokokBiBijLBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTcLNTNTNTcLNTUlvrvrOYvrvrvrWQvrvrWQNTNTeNeCJFqTNTNTcLNTNTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiokjLjLjLBiBiBijLokBiBijLBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrIDdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrIDdrdrdrdrkmkmkmkmkmkmkmNTNTNTeCNTNTPENTvrvrvrvrvrvrvrvrjivrNTtoNTNTqTNTNTNTNTNTqTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiokokjLBiBiBiwuBijLokBiBiBiBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTcLNTNTeNcLNTvrvrvrNrNrNrNrNrOYvrvreCeCNTNTJFeCNTNTNTNTqTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiokjLBiBiBiBiBijLokBiBiBiBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrIDdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTcLtoqTNTNTtovrjivrCMCMCMCMCMvrvrNTeCNTNTNTeCNTeNeCJFNTNTNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiokokjLBiBijLjLokokBiBijLBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTNTJFJFNTeNvrvrvrCMnTVOnTCMvrvrNTNTNTPEtoNTNTNTeCqTeCeCNTkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBiBiBiBiBiokjLBijLokokokBiBiBiBiBiBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrIDdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTcLNTJFeCeCNTvrvrnTnTAMnTnTvrvrwONTNTqTNTNTtoqTqTqTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBijLBiBiBijLBiBijLokBiOiBiBiOiBijLBivrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrcsdrdrdrdrdrZWdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmNTNTNTNTNTJFNTvrvrvrvrAMAMAMvrOYvrNTNTNTcLPENTeNqTqTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrBiBijLBiBijLBiBiBijLjLBiBiBiBiBiBiBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrkadrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrkadrdrdrdryUdrdrkadrdrdrkmkmkmkmkmkmkmkmtoNTcLNTqTqTNTvrOYvrvrvrvrOYvrvrjiNTNTNTOFNTeNsgqTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrvrBiBijLBiBiBiBiBiBijLBiBiBiBiBiBiBivrvrvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmkmkmtocLtoozqTqTvrvrvrvrvrvrvrvrvrvrNTNTeCNTNTcLNTNTNTNTNTkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrvrBiBiBiBiBiBijLjLjLBijLjLjLjLjLBiBivrkHvrnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrkayUdrdrdrdrdrdrdrdrdrdrkadrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrkadrdrdrdrdrdryUdrdrdrdrkadrkmkmkmkmkmkmkmkmkmFBNTNTeCeCNTjitoNTNTvrvrvrWQqTqTAIqTeCtoNTNTNTBUNTNTkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTvrvrvrvrvrBiBiBiBiBiBiBiBiBiBiBiBiBiBivrvrvrnTnTAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmkmkmNTNTBUeCeNNTNTcLNTNTeCjivrNTNTqTByqTqTNTeCeCNTNTNTNTkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTNrCwvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrvrnTnTAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrkadrdrdrdrdrdrdrdrdrdrdrdrcsdrdrdrdrdrdrdrdrIDdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmkmkmtoeCNTNTNTtoNTeNNTNTNTOFeCNTPEeNqTqTNTtoeCcLNTNTBUkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTnTCMCMNrCwvrkHvrvrvrvrvrvrvrvrkHvrvrvrvrnTnTnTAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrBtdryUdrdrdrdrIDdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrBtdrdrkmkmkmkmkmkmkmflkmkmBUeCeCNTfaozeCeCJFJFNTeCNTPEPENTBUNTNTNTNTBUNTNTkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTCMCMCMCMCMNrNrNrNrNrNrNrNrNrNrNrNrNrNrNrnTAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrBtdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBikmkmkmeCeCFBNTNTNTtotoNTNTtotoNTNTPENTBUNTNTNTNTNTkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTCMCMlgCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBiBiBikmkmtoeCeCeCSTNTNTNTcLNTNTNTBUNTBUNTNTNTtokmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMCMnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrZWdrdrdrdrdrdrdrdrdrdrdrczczczdtdrdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBikmBikmkmkmkmkmFBFBNTNTNTNTNTNTNTNTNTkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMnTnTCMCMCMCMCMCMCMCMnTnTSDnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdrdrdrdrdryUdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdrdtczcAcBdtdtdrdrdrdrdrdrdrdrdrdrdrdrdryUdrdrdrdrdrdrkmkmkmkmkmkmkmBikmBiBiBiBiBiBiBikmkmNTFBtoNTkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMnTnTnTnTnTnTnTnTnTnTAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdsdsdsdsdsyUdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdsdxcCcCcCdwdxdxdsdsdsdsdsdsdsdsdsdsdsdsyUdsdsdsdsdsdrkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dsdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdudududududtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdtdskmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dudududududududududududududududududududududududududududtdudududududududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dududududududududududududududududududududududududududududududududtdududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dududududududududududududududududududududududududududududtdududududududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dududududududududududududududududududududududududududududtdtdtdtdudududududududududududududududududududududukmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -dududududududududududududududududududududududududududududududududududududududududududududududududududududuLZkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmkmAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMAMaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +JZ +if +zL +Nn +JZ +if +JZ +if +Su +PH +JZ +Nb +cg +PH +Nn +JZ +dh +JZ +Nb +JZ +JZ +if +zL +Nn +JZ +if +NP +RV +IO +NP +Fl +JZ +if +dj +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +ds +du +du +du +du +du +"} +(2,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +SO +Cr +Lk +Cr +Lk +Cr +Ka +fR +iI +wR +fR +iI +Lk +Lk +bH +Ia +fR +zr +fR +iI +SO +Cr +Lk +Cr +Lk +fR +fR +iI +iI +nV +fR +iI +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(3,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +NP +fR +PL +fR +Ia +fR +iI +iI +fR +fR +Ia +fR +lJ +nV +Lq +iI +iI +fR +Ia +uR +Vq +fR +PL +fR +Ia +fR +iI +gW +fR +iI +Lq +gW +zZ +fR +dj +dr +dr +dr +ID +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(4,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +qP +NZ +fR +Ia +fR +gW +Ia +fR +iI +nV +fR +iI +lJ +uR +fR +gW +fR +iI +fR +iI +Lq +NZ +fR +Ia +fR +gW +Ia +Oz +NG +NG +NG +iI +iI +iI +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(5,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Im +Mx +fR +iI +Ia +fR +kq +fR +iI +fR +uR +fR +iI +iI +fR +iI +fR +iI +fR +fR +AS +Mx +fR +iI +Ia +fR +kq +iI +iI +zr +fR +PL +wR +fR +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +Bt +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(6,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +rt +NG +Ia +zr +fR +iI +fN +fR +zZ +fV +Vq +fR +zZ +fR +fN +fN +fR +We +zZ +uR +nl +lJ +nV +Lq +iI +iI +fR +Ia +Vq +zZ +lJ +bL +LY +fm +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(7,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +EM +iI +lJ +fN +gT +NP +MB +Fs +NP +MB +Fs +MB +Db +NP +MB +gT +NP +MB +Fs +NP +MB +Fs +MB +NP +Db +Fs +MB +gT +NP +MB +Fs +NP +dk +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +dt +du +du +du +du +du +"} +(8,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +VE +fR +Mx +fR +rg +HI +pw +pw +pw +pw +pw +pw +bw +Po +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +ZD +hI +JJ +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(9,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +VE +uR +iI +lJ +Fs +yS +kO +XF +Qm +QW +XF +kO +Pu +Po +DR +Vw +lo +DR +XZ +DR +XZ +DR +XZ +DR +ph +DR +GR +DR +mJ +hI +wi +hI +eo +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(10,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +SO +fR +iI +iI +fR +MB +yS +lo +lo +lo +lo +lo +lo +Pu +Po +DR +Vw +lo +DR +AX +DR +AX +DR +AX +DR +qf +DR +Fv +DR +DR +DR +DR +DR +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(11,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Cm +fR +zr +fR +Ia +zZ +NP +Fp +TP +TP +TP +TP +TP +TP +BR +Po +DR +Vw +lo +lo +lo +cl +lo +cl +lo +cl +lo +xH +lo +DR +dd +yF +em +DR +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(12,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +IO +fR +iI +iI +fR +iI +rg +hI +hI +hI +hI +hI +hI +hI +hI +Po +DR +Vw +lo +lo +lo +lo +lo +lo +lo +lo +lo +lo +lo +DR +BF +lo +lo +DR +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ID +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(13,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Hp +fR +iI +iI +fR +fR +Fs +hI +hI +hI +hI +hI +hI +hI +hI +Po +DR +Vw +lo +DR +YY +Pz +lo +lo +rV +QF +JB +hB +Mw +DR +Nq +lo +lo +DR +LJ +dr +dr +dr +dr +ZW +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(14,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +YK +Ia +Mx +zr +fN +MB +hI +xv +hI +hI +hI +hI +hI +hI +bD +DR +DR +DR +DR +DR +DR +Xc +SI +DR +DR +DR +DR +DR +DR +DR +DR +ET +DR +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ZW +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(15,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +NP +EM +uR +fR +iI +fR +gT +hI +Qf +hI +hI +xG +hI +hI +hI +hI +hF +wc +wc +bT +wc +la +wc +wc +la +wc +wc +wc +wc +wc +wc +de +hI +hI +er +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(16,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +VE +fR +uR +uR +fN +rg +hI +hI +jN +HQ +BJ +xj +hI +bB +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(17,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +VE +fR +iI +iI +iI +NP +hI +hI +CC +kU +oL +xj +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(18,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +EM +zr +fR +fR +pu +yG +hI +hI +Gu +Wy +sa +xj +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +YB +hI +Gz +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +ZW +dr +ds +dt +du +du +du +du +du +"} +(19,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Iu +fR +iI +fR +Mx +fR +Fs +hI +hI +hI +hI +xG +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(20,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +EM +iI +Ia +fR +lJ +MB +hI +Qf +xv +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +vS +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(21,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Cm +Ix +fR +uR +uR +fR +gT +DR +DR +iL +DR +DR +DR +UR +hI +hI +hI +hI +hI +YB +hI +hI +hI +hI +hI +Qf +xv +hI +hI +Qf +hI +hI +dg +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(22,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +ww +iI +EM +fR +uR +fR +Fs +DR +YM +oN +oN +jA +DR +bi +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +xv +hI +hI +hI +hI +ew +dr +dr +dr +dr +dr +dr +dr +ID +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(23,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +Lk +iI +uR +gW +zZ +NP +DR +oB +UN +UN +UN +DR +bj +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +RG +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +Bt +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(24,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +iI +Ia +fR +MB +Kj +fc +tW +tW +pO +DR +AD +gJ +hs +hs +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(25,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +iu +iI +zr +fR +fR +hn +Fs +Kj +WV +tW +tW +pO +DR +bd +sd +Xq +yK +ZE +hI +hI +hI +yB +hI +hI +Fb +hI +hI +hI +hI +hI +hI +hI +dg +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(26,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +VE +fR +iI +fR +Id +NP +Kj +IA +tW +tW +GK +DR +DD +lo +lo +Dx +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +ZW +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(27,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +EM +fR +fR +iI +fR +gT +Kj +DL +tW +tW +iT +DR +Jl +lo +bM +mE +ZE +hI +hI +hI +hI +Fb +hI +cZ +hI +Fb +hI +hI +hI +hI +hI +RG +hI +ey +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(28,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +qb +iI +iI +gW +fR +zZ +wQ +Kj +DL +tW +tW +tW +kf +Bq +lo +lo +DB +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +dt +du +du +du +du +"} +(29,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +iI +fR +gW +fR +Zb +Kj +MK +tW +tW +tW +ZN +Bq +lo +lo +mo +ZE +hI +hI +hI +hI +hI +hI +db +hI +hI +hI +hI +hI +hI +hI +dg +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dt +dx +dt +du +du +dt +dt +du +"} +(30,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +iu +NG +uR +fR +iI +fR +NP +Kj +wd +tW +tW +yT +DR +CB +lo +lo +Fj +ZE +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +ZW +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +me +me +Fc +du +du +du +du +dt +du +"} +(31,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +EM +zr +fR +nV +gT +DR +id +tW +tW +iX +DR +lC +lo +lo +sW +ZE +hI +hI +hI +hI +hI +hI +hI +hI +YB +hI +CJ +hI +hI +hI +RG +hI +er +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +me +dv +Fc +du +du +du +du +dt +du +"} +(32,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +PL +fR +Ia +fR +zi +DR +Jb +BG +DP +rT +Zu +na +vZ +bJ +ue +Zu +OT +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +me +MP +Fc +du +du +du +du +dt +du +"} +(33,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +EM +fR +uR +fN +Fs +DR +Kk +pL +UN +HC +Mq +WK +Bq +lo +ku +DR +bf +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +Jv +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +vS +dr +dr +dr +dt +dt +dw +du +du +dt +du +du +du +"} +(34,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +iI +EM +Ia +zZ +fR +Db +DR +Eq +pL +kd +Rj +DR +tF +Bq +lo +sf +DR +Pu +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +Qf +xv +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dt +dx +du +du +du +du +du +du +"} +(35,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +Vq +EM +iI +fR +lJ +NP +DR +Kk +zx +EE +zQ +DR +oe +bX +by +bz +DR +bN +hI +hI +hI +hI +hI +hI +hI +Qf +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +ZW +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dx +dt +du +du +du +du +du +"} +(36,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Ym +Lq +VE +zr +iI +fR +MB +Mq +DR +DR +DR +DR +DR +DR +bd +DR +DR +DR +Dm +hI +hI +bB +hI +hI +hI +hI +xv +hI +hI +hI +hI +hI +Ez +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ZW +dr +dr +ds +dt +du +du +du +du +du +"} +(37,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +ww +fR +Ip +fR +fR +lJ +Zb +lQ +Fy +XF +Bz +Gd +Ol +XF +EB +bM +XF +DR +CE +hI +hI +hI +hI +hI +hI +hI +hI +hI +Iw +hI +hI +hI +iK +hI +ey +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(38,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +ww +fR +So +fN +zZ +fR +Db +DR +Ss +lo +lo +lo +lo +lo +lo +lo +bM +DR +Ao +hI +hI +hI +cJ +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +ID +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(39,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +zn +iI +EM +fR +iI +fN +NP +DR +lo +lo +lo +lo +lo +lo +lo +lo +vm +DR +UR +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(40,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +zZ +EM +iI +fR +fR +gT +DR +sk +Gm +sk +Gm +sk +Gm +sk +lo +lo +CF +UR +hI +hI +hI +hI +hI +hI +Fb +hI +Fb +hI +Fb +hI +hI +Bc +hI +eB +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(41,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +My +fR +EM +zZ +fR +lJ +Fs +DR +KB +sk +Gm +sk +Gm +sk +Gm +lo +lo +lo +UR +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +LJ +dr +dr +dr +dr +ZW +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(42,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +VE +fR +iI +fR +MB +DR +sk +Gm +sk +Gm +sk +Gm +sk +lo +vm +DR +UR +hI +hI +hI +hI +hI +hI +Fb +hI +Fb +hI +Fb +hI +hI +Bc +xv +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ID +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(43,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +VE +fR +Ia +fR +Db +DR +Gm +sk +Gm +gw +Gm +sk +Gm +lo +Ls +DR +UR +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +hI +ey +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(44,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Cm +Cr +fR +iI +iI +zZ +NP +DR +gp +Gm +sk +Gm +sk +Gm +sk +lo +WL +DR +UR +xv +hI +hI +hI +hI +hI +AW +gJ +gJ +gJ +AW +hI +hI +Bc +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(45,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +HJ +av +av +ao +av +av +av +HJ +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +iI +fR +wR +zi +DR +Gm +sk +Gm +sk +Gm +sk +Gm +lo +XF +DR +UR +Qf +hI +hI +xv +hI +hI +lo +lo +lo +lo +lo +KM +hI +hI +hI +Gz +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(46,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +ao +ad +ad +ad +ad +ad +ad +ad +ad +HJ +av +av +an +av +av +av +av +uz +av +av +ao +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +NG +Ix +fR +iI +fN +NP +DR +sk +Gm +sk +Gm +sk +Gm +SL +lo +WL +DR +UR +hI +Bc +hI +hI +Bc +hI +MG +lo +NF +lo +zg +hI +xv +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(47,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ao +av +av +av +av +av +uz +av +av +av +av +av +av +av +HJ +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +zn +iI +fR +EM +fR +lJ +gT +Mq +DR +DR +DR +DR +DR +DR +DR +DR +DR +DR +UR +hI +hI +hI +hI +hI +Qf +no +OD +OD +OD +Ih +hI +Qf +hI +hI +LJ +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(48,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +ao +ad +ad +ad +ad +ad +av +av +av +av +av +uz +uz +av +av +an +av +uz +av +av +av +av +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +Vq +fR +VE +zZ +fR +Zb +JZ +MB +Fs +MB +NP +Db +Fs +MB +NP +Db +Fs +NP +NP +Nn +Nn +NP +MB +gT +cc +NP +cc +Db +NP +MB +NP +Fs +cc +dk +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +yU +dt +du +du +du +du +du +"} +(49,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +HJ +av +av +uz +uz +av +av +av +uz +av +av +av +av +av +uz +av +av +ao +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +So +zr +fR +fR +Vq +Ia +fN +Vq +zZ +fR +iI +fN +iI +zZ +iI +fR +lJ +fR +Vq +Ia +fN +Vq +fR +iI +fN +fR +Lk +iI +uR +gW +zZ +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(50,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +ad +ad +TL +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +HJ +ad +ad +ad +av +av +uz +av +av +av +av +uz +av +av +uz +av +uz +av +av +av +av +av +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +SO +iI +fR +zZ +zZ +fR +fR +iI +iI +nV +fR +iI +fR +wR +fR +gW +zr +fR +fR +fR +fR +iI +iI +fR +iI +fR +fR +fR +fR +iI +Ia +fR +dj +dr +dr +dr +ID +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(51,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +ad +ad +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +uz +uz +av +av +HJ +av +av +an +av +av +av +av +av +an +av +uz +av +HJ +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +Ym +fR +EM +fR +iI +fR +iI +gW +fR +iI +Lq +gW +zZ +fR +iI +fR +Lq +iI +kq +iI +fR +gW +fR +iI +Lq +zZ +fR +iI +NG +iI +zr +fR +fR +fR +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(52,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +ad +jY +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +RU +RU +RU +RU +RU +RU +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +uz +av +av +av +an +av +av +av +uz +av +an +uz +uz +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +zn +iI +oC +fR +fR +fR +wR +Oz +NG +NG +NG +iI +iI +iI +fR +Ia +fR +Ia +fR +iI +fR +Oz +NG +rD +NG +iI +iI +fR +fR +VE +fR +iI +fR +Id +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +Bt +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(53,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +BT +Ks +aE +aE +Td +RU +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +ad +ad +ad +ao +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +uz +an +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +fR +fR +VE +Lq +fR +hv +iI +iI +zr +fR +PL +wR +fR +NG +Ch +zZ +bO +iI +kq +iI +iI +iI +zr +fR +wR +fR +NG +dP +EM +fR +fR +iI +fR +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ka +dr +dr +dr +dr +dr +dr +dr +ds +dt +du +du +du +du +du +"} +(54,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +ad +ad +mc +av +av +ad +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +RU +ah +Uv +aJ +ah +Uv +RU +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +HJ +ad +ad +ad +av +av +av +av +av +av +av +av +Rl +av +av +uz +av +av +av +av +uz +av +ao +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +PR +PR +PR +PR +PR +PR +PR +PR +JZ +JZ +JZ +Su +JZ +if +mY +ML +NP +Fl +ML +NJ +KS +uE +EZ +ML +if +XG +RV +JZ +if +ML +NP +Fl +ML +KS +uE +EZ +qb +if +if +mP +JZ +Fl +dj +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +dr +ds +du +du +du +du +LZ +"} +(55,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +Yj +ah +nF +ah +wx +RU +uz +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +ad +ad +HJ +av +av +av +av +av +av +av +av +av +av +av +uz +uz +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +fA +YG +XL +Gj +vr +xD +YG +zk +tM +zk +fA +xZ +tM +zk +fA +xZ +tM +fA +zk +Qu +vr +ji +ji +XL +vr +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(56,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +RU +RU +Ds +RU +RU +RU +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +an +av +av +av +uz +av +uz +av +av +av +av +uz +av +av +an +av +av +HJ +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +YG +xI +VR +vr +vr +kH +sZ +wO +kH +Gj +vr +ji +wO +ji +Gj +ji +vr +WQ +vr +vr +ji +vr +vr +ji +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(57,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +ad +av +av +RU +In +In +ah +ah +ah +RU +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +uz +av +av +uz +av +av +av +av +av +av +av +uz +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +lX +ji +vr +Gj +Gj +vr +vr +ji +ji +OR +vr +ji +vr +OY +vr +LV +VR +vr +vr +vr +qx +vr +sZ +vr +qx +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(58,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +RU +In +Uv +ah +iC +Uv +RU +av +av +LB +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +uz +uz +av +av +av +HJ +ad +ao +av +uz +av +av +uz +av +av +av +av +av +an +av +av +av +av +av +av +an +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +tN +YG +BY +vr +ji +vr +ji +LV +vr +ji +yJ +LV +Gj +vr +ji +vr +yJ +ji +Hf +ji +vr +ji +vr +sZ +vr +qx +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(59,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +aq +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +RU +ah +iC +Cz +ah +ah +RU +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +uz +av +av +av +av +av +an +av +av +av +HJ +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +oH +vk +Sz +vr +vr +vr +OY +XU +FT +FT +FT +ji +ji +ji +vr +sZ +vr +sZ +vr +ji +vr +sZ +vr +ji +sZ +vr +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(60,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +RU +ah +ah +ah +ah +ah +RU +av +uz +av +av +uz +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +uz +av +av +av +av +uz +av +av +ao +ad +av +av +av +av +uz +uz +uz +uz +av +av +uz +uz +av +av +av +uz +av +an +ao +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +YG +XL +yJ +vr +sj +vk +vk +yq +YG +qx +OY +vr +FT +JM +Aa +kQ +ji +Hf +ji +fA +vr +sZ +vr +vr +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(61,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +vq +ah +Cz +ah +ah +RU +av +av +av +av +av +av +LB +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +ad +ad +av +uz +uz +av +av +av +ad +HJ +av +av +av +av +uz +av +uz +av +uz +uz +av +av +an +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +YG +BY +vr +ji +Lz +qo +fA +Aa +qo +le +Pp +rA +zw +qo +vk +Ob +sZ +vr +vk +eS +sZ +vr +sZ +sZ +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(62,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +RU +Co +ah +iC +ah +ah +gB +av +av +av +uz +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +ad +av +av +ad +av +uz +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +uz +uz +av +av +av +uz +av +av +HJ +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +vk +BY +VR +vr +OR +eS +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +kQ +UK +wJ +qx +vr +sZ +BY +VR +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +NT +NT +NT +NT +NT +NT +NT +km +km +km +km +km +fl +Bi +Bi +Bi +Bi +km +km +km +km +km +km +km +"} +(63,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +RU +vq +ah +Cz +ah +ah +aV +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +uz +av +an +av +av +HJ +ad +xQ +xQ +ad +ad +ao +av +uz +av +av +av +av +uz +av +uz +av +av +av +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +qx +vr +sZ +vr +QJ +vr +vr +vr +HY +PW +PW +pD +pD +PW +PW +YS +vr +vr +QJ +BY +vr +Ys +qx +vr +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +PE +NT +NT +cL +NT +cL +cL +NT +NT +NT +to +km +km +km +km +km +km +Bi +km +km +km +km +km +km +km +km +km +"} +(64,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +RU +ah +ah +ah +ah +RN +aV +av +LB +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +uz +av +xQ +xQ +xQ +xQ +ad +ad +av +av +av +av +uz +av +av +av +av +av +av +ao +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +BY +vr +Ys +wO +tM +vr +vr +vr +ti +oy +Ns +oG +oG +EX +Cv +lH +vr +wO +tM +BY +sZ +Gj +BY +vr +sZ +km +km +km +km +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +km +km +km +NT +NT +NT +NT +eC +NT +NT +NT +NT +to +NT +cL +NT +NT +to +FB +NT +to +km +km +Bi +Bi +Bi +km +km +km +km +km +km +km +"} +(65,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +ah +Uv +Cz +ah +ah +RU +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +uz +HJ +ad +ad +HJ +av +av +av +av +av +uz +av +av +ao +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +NT +vk +BY +sZ +Gj +vr +xZ +vr +vr +vr +Va +ZU +Wq +oG +oG +oG +Cv +tr +vr +vr +xZ +BY +ji +vr +BY +sZ +ji +km +km +km +km +fj +fj +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +fj +fj +km +km +km +cL +NT +NT +NT +eC +ym +NT +eC +NT +qT +JF +NT +NT +cL +cL +NT +NT +eC +BU +km +km +km +Bi +km +km +km +km +km +km +km +"} +(66,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +kT +ah +ah +ah +ah +RU +av +ad +av +av +av +av +LB +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +ad +uz +uz +av +av +av +uz +av +av +uz +av +uz +av +av +ao +ad +ad +ad +ad +HJ +ao +av +av +av +HJ +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +fA +BY +ji +vr +WQ +fA +vr +vr +vr +ti +GZ +oG +oG +oG +qj +Cv +vY +vr +WQ +fA +XL +VR +ji +BY +ji +VR +km +km +km +km +fj +fj +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +fj +fj +km +km +eC +NT +NT +cL +eC +NT +eC +NT +NT +eN +NT +JF +JF +NT +NT +to +NT +BU +NT +eC +eC +km +km +Bi +km +km +km +km +km +km +km +"} +(67,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +RU +ah +ah +Cz +ah +ah +RU +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +av +ad +ad +ad +ad +av +av +av +av +uz +HJ +av +av +ao +av +av +av +uz +av +av +av +av +av +av +HJ +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +ix +BM +XL +VR +ji +vr +zk +vr +vr +vr +UL +PW +PW +pD +pD +PW +PW +RQ +vr +vr +zk +FF +vr +vr +XL +VR +vr +km +km +km +km +fj +rv +Kb +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Zl +Op +Kb +rv +fj +km +NT +eC +NT +NT +NT +NT +NT +cL +cL +NT +cL +NT +NT +eC +JF +qT +oz +eC +eC +NT +eC +eC +to +km +Bi +km +km +km +km +km +km +km +"} +(68,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +iC +ah +ah +RU +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +ad +av +av +ad +av +uz +av +av +av +av +av +av +uz +av +uz +an +uz +av +uz +av +av +av +av +HJ +ad +ad +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +rx +NT +FF +vr +vr +WQ +xD +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +WQ +xD +xI +wO +Gj +FF +vr +vr +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +km +NT +eC +NT +cL +NT +PE +NT +NT +NT +PE +NT +to +eN +eC +NT +qT +qT +eC +eN +NT +NT +FB +eC +km +Bi +km +km +km +km +km +km +km +"} +(69,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +RU +ah +ah +ah +RU +av +av +ad +av +LB +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +uz +av +av +av +av +uz +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +PE +NT +Ne +vr +WQ +qU +fA +vr +vr +WQ +vr +vr +vr +vr +vr +vr +vr +vr +vr +wO +eS +YG +XL +vr +YG +vr +kH +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +Op +Op +Op +Ou +Op +Op +Op +Op +Op +Op +Op +Kb +rv +fj +km +NT +NT +NT +NT +NT +cL +NT +eN +Ul +NT +vr +vr +vr +NT +vr +NT +qT +NT +NT +to +fa +NT +eC +km +Bi +km +km +km +km +km +km +km +"} +(70,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +ah +RU +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +av +av +uz +av +av +av +av +av +av +av +av +av +uz +av +av +av +av +av +av +av +av +av +av +av +HJ +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +NT +NT +to +WQ +vr +vr +fA +WQ +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +Gj +xD +YG +xI +vr +fA +vr +xx +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +NT +NT +eC +NT +oU +rS +NT +NT +NT +vr +vr +vr +ji +vr +vr +vr +vr +vr +ji +NT +NT +oz +NT +eC +FB +Bi +km +km +km +km +km +km +km +"} +(71,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +ah +RU +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +uz +av +av +av +av +av +av +av +uz +uz +av +av +av +uz +av +av +av +av +av +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +NT +PE +NT +Ne +WQ +qU +fA +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +YG +lX +ji +vr +YG +vr +kH +km +km +km +km +fj +rv +Kb +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Kb +rv +fj +NT +NT +NT +bg +NT +NT +eC +cL +eC +vr +vr +vr +vr +vr +vr +vr +OY +vr +to +cL +eN +eC +NT +ST +FB +km +km +km +km +km +km +km +km +"} +(72,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +RU +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +uz +av +av +uz +av +av +uz +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +rx +YG +Qh +wO +Gj +vr +xZ +LL +Zo +Zo +Zo +Zo +YR +vr +vr +vr +vr +vr +vr +vr +xZ +BY +vr +xx +vr +kH +nU +km +km +km +km +fj +fj +fj +QY +Op +Op +fj +Op +Op +fj +fj +fj +Op +Op +fj +Op +Op +cH +fj +fj +fj +NT +nE +NT +NT +Hr +NT +NT +ci +wO +OY +vr +Nr +CM +CM +nT +vr +vr +vr +NT +NT +NT +eC +to +NT +NT +km +km +km +km +km +km +km +km +"} +(73,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +RU +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +HJ +av +av +ao +av +uz +av +av +uz +av +av +av +ao +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +Fi +to +BY +vr +ji +wO +fA +Jj +CM +CM +CM +CM +CM +vr +vr +vr +vr +vr +vr +wO +fA +BY +ji +xx +kH +Sc +Sc +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +fj +fj +fj +fj +fj +fj +fj +Op +Op +Op +Op +Kb +rv +fj +NT +NT +DG +sv +NT +oU +NT +NT +vr +vr +vr +Nr +CM +nT +nT +AM +vr +vr +NT +NT +NT +JF +to +NT +NT +NT +km +km +km +km +km +km +km +"} +(74,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +uz +uz +av +uz +av +av +av +av +av +av +av +uz +av +uz +av +av +av +av +av +av +av +av +av +av +av +ao +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +Aa +BY +ji +vr +vr +eS +Jj +CM +CM +CM +CM +CM +vr +vR +vr +Pb +Pb +vr +vr +eS +BY +Gj +vr +lS +Cf +Sc +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +fj +bx +mV +bx +mV +bx +fj +Op +Op +Op +cH +fj +fj +fj +sv +cL +NT +NT +nE +NT +eC +NT +vr +vr +vr +Nr +CM +VO +AM +AM +vr +vr +vr +eC +NT +JF +NT +NT +NT +FB +km +km +km +km +km +km +km +"} +(75,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +uz +av +av +av +uz +av +av +av +av +av +av +av +av +av +av +uz +av +av +av +av +av +an +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +QN +YG +BY +Gj +vr +WQ +tM +Jj +CM +CM +op +CM +CM +Jd +vr +Pb +gD +uN +vr +WQ +tM +XL +xx +vr +lS +xR +Sc +km +km +km +km +fj +rv +Kb +Op +Op +Op +fj +fj +fJ +Op +Xa +Op +Op +fj +fj +Op +Op +Op +Op +Op +Fd +NT +gn +NT +cL +bg +iF +eC +rS +zX +vr +vr +Nr +CM +nT +nT +AM +vr +vr +vr +ji +OF +NT +NT +cL +NT +to +km +km +km +km +km +km +km +"} +(76,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +uz +uz +av +av +av +av +av +uz +uz +av +av +av +av +av +uz +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +XL +vr +ji +vr +zk +Jj +CM +CM +CM +CM +CM +vr +vr +Pb +jE +pp +vr +vr +zk +XL +vr +vr +lS +Sc +Sc +km +km +km +km +fj +fj +fj +QY +Op +fj +fj +fj +Lu +Xa +fd +Xa +Op +Kb +Kb +Kb +Op +Op +Op +Op +Fd +NT +NT +Hr +NT +NT +NT +NT +NT +NT +WQ +vr +Nr +CM +CM +nT +vr +OY +vr +vr +vr +eC +eC +to +NT +NT +NT +km +km +km +km +km +km +km +"} +(77,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +ad +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +uz +av +av +av +av +av +av +uz +uz +uz +av +av +av +av +av +av +av +av +av +av +uz +uz +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +XL +vr +sZ +vr +xZ +Jj +CM +CM +CM +CM +CM +vr +vr +vr +Pb +Pb +vr +vr +xZ +vr +ji +vr +lS +Sc +Sc +km +km +km +km +fj +rv +Kb +Op +Op +Op +fj +fj +ky +Op +Xa +Op +Op +fj +fj +Op +Op +Op +Op +Op +Fd +NT +sv +NT +gn +NT +NT +eC +cL +vr +vr +vr +OY +vr +vr +vr +OY +vr +vr +WQ +NT +NT +NT +to +NT +NT +km +km +km +km +km +km +km +km +"} +(78,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +ad +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +IZ +Bv +vr +ji +ji +Gj +fA +HN +Zo +Zo +Zo +Zo +YR +vr +vr +vr +vR +vr +vr +Gj +fA +vr +ji +vr +lS +lS +Sc +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +fj +bx +mV +bx +mV +bx +fj +Op +Op +Op +cH +fj +fj +fj +DG +NT +eC +NT +NT +NT +eC +NT +NT +vr +ji +vr +vr +vr +vr +vr +vr +vr +qT +NT +PE +PE +NT +NT +NT +km +km +km +km +km +km +km +km +"} +(79,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +vr +vr +vr +ji +vr +OY +QJ +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +OY +QJ +UK +vr +vr +vr +lS +lS +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +fj +fj +fj +fj +fj +fj +fj +Op +Op +Op +Op +Kb +rv +fj +NT +cL +cL +NT +Hr +eC +NT +eC +NT +WQ +vr +vr +NT +NT +wO +NT +ji +vr +qT +qT +eN +PE +NT +BU +NT +km +km +km +km +km +km +km +km +"} +(80,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +uz +uz +av +av +uz +uz +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +vr +FT +UK +vr +ji +wO +fA +LL +Zo +Zo +Zo +Zo +Zo +Zo +Zo +YR +OJ +OJ +kE +wO +fA +YG +BY +vr +kH +vr +lS +km +km +km +km +fj +fj +fj +QY +Op +Op +fj +Op +Op +fj +fj +fj +Op +Op +fj +Op +Op +cH +fj +fj +fj +cL +gn +NT +nE +NT +oU +NT +NT +NT +NT +NT +eC +eC +NT +NT +NT +NT +NT +AI +By +qT +NT +PE +NT +NT +km +km +km +km +km +km +km +km +"} +(81,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +ad +ad +ad +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +oH +vk +YG +BY +vr +WQ +eS +Jj +Cj +Pm +Jj +vr +vr +vr +OJ +vr +vr +vr +Jj +WQ +eS +YG +XL +Gj +YG +vr +vr +km +km +km +km +fj +rv +Kb +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Op +Op +fj +Op +Op +Kb +rv +fj +NT +NT +NT +NT +NT +NT +NT +PE +NT +NT +to +eC +NT +NT +NT +NT +NT +NT +qT +qT +qT +BU +NT +BU +km +km +km +km +km +km +km +km +km +"} +(82,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +fA +YG +XL +Gj +vr +xD +Jj +VP +vr +Jj +vr +vr +vr +kE +vr +vr +vr +Jj +vr +xD +YG +xI +VR +YG +kH +vr +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +NT +nE +NT +eC +bg +NT +NT +NT +NT +eN +NT +NT +NT +PE +qT +cL +NT +eC +eC +qT +NT +NT +BU +NT +km +km +km +km +km +km +km +km +km +"} +(83,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +ad +av +ad +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +YG +xI +VR +vr +vr +Jj +sp +pd +Jj +sN +vr +vr +kE +vr +Jd +vr +Jj +vr +YG +lX +ji +vr +YG +vr +vr +km +km +km +km +fj +rv +Kb +Op +Op +Op +Op +Op +Op +Op +Ou +Op +Op +Op +Op +Op +Op +Op +Kb +rv +fj +NT +NT +eC +NT +NT +NT +eC +cL +eC +eC +NT +NT +NT +to +NT +PE +OF +NT +to +NT +to +NT +NT +NT +km +km +km +km +km +km +km +km +km +"} +(84,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +HJ +av +av +av +ad +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +lX +ji +vr +Gj +Gj +Jj +Bn +vr +Jj +ZG +vr +vr +OJ +vr +vr +vr +Jj +Gj +Aa +BY +vr +ji +lX +vr +kH +km +km +km +km +fj +fj +fj +QY +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +Op +cH +fj +fj +fj +bg +NT +NT +NT +NT +NT +NT +NT +eC +JF +qT +JF +eC +NT +NT +NT +NT +NT +NT +eC +eC +NT +NT +NT +km +km +km +km +km +km +km +km +km +"} +(85,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +sF +av +av +av +ad +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +tN +YG +BY +vr +ji +vr +ji +Jj +YR +OJ +HN +Zo +Zo +Zo +Zo +YR +vr +vr +Jj +vr +vk +qx +vr +sZ +BY +vr +kH +km +km +km +km +fj +rv +Kb +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Jc +Op +Kb +rv +fj +NT +NT +NT +NT +NT +NT +NT +NT +eN +qT +NT +eC +NT +NT +to +eN +eN +cL +NT +eC +cL +NT +NT +to +km +km +km +km +km +km +km +km +km +"} +(86,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aq +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +HJ +av +av +ad +ad +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +fA +YG +qx +vr +sZ +vr +ji +Jj +pd +vr +vr +OJ +vr +vr +vr +vr +vr +vr +Jj +vr +vk +vr +sZ +vr +qx +vr +vr +km +km +km +km +fj +fj +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +Kb +fj +fj +fj +km +NT +NT +eC +NT +NT +PE +NT +qT +NT +NT +NT +eN +NT +qT +qT +sg +NT +NT +NT +NT +BU +NT +km +km +km +km +km +km +km +km +km +km +"} +(87,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +ad +av +av +ad +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +ad +ad +ad +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +BM +ER +vr +sZ +vr +LV +vr +Jj +vr +vr +vr +OJ +vr +vr +vr +vr +vr +vr +Jj +iU +YG +qx +vr +sZ +vr +sZ +vr +km +km +km +km +fj +fj +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +rv +fj +fj +fj +km +NT +NT +NT +NT +NT +NT +NT +NT +NT +NT +NT +eC +eC +qT +qT +qT +NT +BU +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +"} +(88,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +ad +ad +ad +ad +ad +av +ad +av +ad +ad +av +ad +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +fA +YG +qx +vr +sZ +vr +ji +Jj +vr +vr +vr +OJ +vr +vr +vr +vr +vr +vr +Jj +YG +vk +vr +sZ +vr +qx +vr +sZ +km +km +km +km +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +fj +km +km +NT +NT +NT +NT +NT +NT +eN +cL +NT +NT +JF +qT +qT +NT +NT +NT +NT +NT +BU +NT +km +km +km +km +km +km +km +km +km +km +km +"} +(89,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +BM +ER +vr +sZ +vr +LV +sZ +Jj +Zo +YR +OJ +LL +Zo +Zo +Zo +YR +vr +vr +Jj +iU +sZ +vr +ji +sZ +vr +sZ +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +PE +NT +PE +NT +NT +NT +NT +eC +NT +NT +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(90,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +vn +QR +vr +ji +sZ +vr +Hf +Jj +UU +vr +vr +Jj +is +vr +vr +OJ +vr +vr +kz +DV +fA +vr +sZ +vr +vr +ji +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +NT +NT +NT +qT +qT +NT +eC +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(91,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +SS +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +kQ +FT +sZ +VR +vr +ji +wO +Jj +vO +BA +yD +Jj +vr +vr +vr +OJ +vr +vr +WN +qo +eS +sZ +vr +sZ +sZ +VR +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +NT +NT +NT +NT +NT +NT +NT +NT +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(92,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +vk +BY +ji +WQ +wO +eS +HN +Zo +Zo +Zo +Jj +Zo +Zo +Zo +Zo +Zo +Zo +YR +YG +xD +vr +sZ +vr +BY +ji +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(93,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +av +av +ad +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +YG +vk +WS +Ej +YG +vk +YG +vk +BY +Hf +vr +Ys +IN +LK +Ej +YG +XL +vr +Ys +vr +sZ +vr +sZ +vk +WS +vr +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(94,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +av +av +Uy +av +av +ao +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +YG +vk +lX +Bv +IZ +Bv +IZ +Bv +Kt +vr +ji +OY +vr +ji +IZ +IZ +Wr +sZ +vr +VR +vr +ji +sZ +vr +lX +Bv +sZ +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(95,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +lK +mi +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +fA +YG +qx +vr +sZ +vr +ji +ji +vr +vr +sZ +vr +WQ +OR +yJ +ji +ji +vr +sZ +Ys +vr +BY +ji +sZ +qx +vr +ji +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(96,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +mi +mi +av +aY +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +BM +ER +vr +sZ +vr +LV +sZ +vr +ji +OR +vr +ji +WQ +Ys +vr +LV +vr +ji +vr +ji +vr +XL +BY +vr +vr +sZ +BY +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(97,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +mw +QR +vr +ji +sZ +vr +Hf +vr +ji +vr +Ys +vr +ji +ji +vr +ji +vr +ji +vr +vr +vr +xI +XL +Gj +vr +ji +XL +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(98,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +te +Bm +tc +yq +vr +vk +DV +vr +Aa +eS +kH +YG +Aa +YG +DV +wO +YG +hP +Gj +YG +YG +YG +xI +VR +tc +yq +xI +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +km +"} +(99,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +ad +ad +av +ad +av +av +ad +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(100,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +Uy +av +av +av +av +av +av +ad +av +av +av +av +ad +ad +av +av +av +av +av +av +av +ad +av +ad +ad +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(101,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(102,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +SS +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(103,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +jL +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(104,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +ao +av +av +av +ad +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +SS +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +ok +ok +ok +jL +jL +jL +jL +jL +jL +ok +ok +ok +jL +jL +jL +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(105,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +jL +ok +ok +jL +tV +Yi +NR +jL +jL +ok +ok +ok +jL +ok +jL +jL +jL +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(106,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +jL +jL +jL +tV +Jr +YO +jL +jL +jL +ok +jL +jL +ok +ok +ok +jL +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(107,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +tV +CG +YO +jL +jL +jL +jL +jL +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(108,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +mi +mi +mi +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +jL +tV +CG +qK +jL +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(109,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +mi +aU +mi +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +tV +zR +tV +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(110,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +mi +mi +mi +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +ad +ad +av +av +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +ql +zC +tV +tV +tV +tV +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +CM +CM +CM +nT +nT +AM +AM +AM +AM +AM +AM +AM +"} +(111,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +ad +ad +av +av +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +An +zC +tV +fE +SX +wr +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +Nr +CM +CM +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(112,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +ad +av +SS +av +av +ad +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +RY +zC +tV +wS +wS +wS +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +vr +kH +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +Cw +CM +CM +lg +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(113,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +ad +av +ad +ad +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +ad +ad +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +Yv +sx +tp +tV +zC +zC +tV +UF +wS +KU +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +vr +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +vr +vr +Nr +CM +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(114,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +ad +ad +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +vN +av +ad +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +tV +II +qR +yz +tV +gL +Il +JE +tV +zC +zC +Xi +wS +wS +Zg +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +Cw +CM +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(115,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +ad +av +av +av +ad +ad +ad +av +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +vN +lZ +vN +ad +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +tV +Et +qR +qR +tV +wb +Il +Il +tV +UQ +zC +tV +NQ +wS +nc +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +jL +jL +Bi +Bi +Bi +Bi +Bi +Bi +jL +jL +Bi +Bi +vr +vr +vr +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(116,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +ad +av +av +av +ad +ad +av +av +av +av +av +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +vN +av +ad +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +tV +jD +qR +qR +Jf +Il +Il +It +JK +zC +zC +tV +Sb +wS +yY +tV +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +Bi +vr +kH +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(117,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +ad +ad +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +tV +kp +zW +xe +tV +LX +Il +ve +tV +zh +zC +tV +BN +vh +wS +tV +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +jL +Bi +Bi +Bi +ok +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(118,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +ad +av +SS +av +av +ad +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +tV +tV +tV +tV +UM +Ke +tV +nb +qR +qR +Zy +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +jL +Bi +ok +ok +ok +ok +ok +Bi +Bi +jL +Bi +Bi +Bi +vr +vr +Nr +CM +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +"} +(119,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +rF +av +av +av +ad +av +av +av +ad +av +av +av +ad +ad +av +av +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +IG +qR +pb +oE +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +jL +jL +ok +jL +jL +jL +ok +ok +jL +Bi +Bi +Bi +Bi +vr +vr +Nr +CM +CM +nT +nT +AM +AM +AM +AM +AM +AM +AM +"} +(120,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +SS +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +ad +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +ad +ad +av +av +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +tV +tV +tV +tV +tV +tV +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Oi +Bi +Bi +jL +ok +jL +Bi +Bi +jL +jL +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(121,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +av +av +ah +ah +av +av +av +av +aq +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +ok +ok +ok +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +SD +AM +AM +AM +AM +AM +AM +AM +AM +"} +(122,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +av +ah +ah +aw +ah +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +ok +jL +jL +Bi +Bi +Bi +Bi +jL +jL +jL +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(123,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +ah +ah +ad +ad +ah +ah +av +ad +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +ok +ok +jL +Bi +wu +Bi +jL +ok +ok +jL +jL +Bi +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(124,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +ah +ah +ad +ad +ah +ah +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +Bi +ok +jL +Bi +Bi +Bi +jL +ok +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(125,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +aq +av +ad +av +av +ah +ah +ah +ah +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +Bi +Oi +Bi +ok +ok +jL +jL +jL +ok +ok +Oi +Bi +Bi +jL +Bi +vr +kH +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(126,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +ad +av +av +av +ah +ah +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +Bi +ok +ok +ok +ok +ok +Bi +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(127,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +"} +(128,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +Bi +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Oi +Bi +Bi +jL +Bi +vr +vr +Nr +CM +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(129,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +ad +ad +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +vr +vr +Bi +Bi +jL +jL +jL +Bi +Bi +jL +Bi +Bi +Bi +Bi +Bi +Bi +vr +vr +Nr +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(130,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +ad +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +rX +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +kH +vr +vr +Bi +Bi +Bi +Bi +Bi +Bi +Bi +Bi +jL +Bi +Bi +Bi +vr +vr +nT +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(131,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +vr +vr +vr +Bi +Bi +Bi +Bi +Bi +vr +vr +vr +vr +vr +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(132,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +kH +vr +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(133,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +vr +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(134,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +nT +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(135,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(136,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(137,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(138,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +TI +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(139,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(140,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(141,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +TI +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(142,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +og +av +av +og +og +og +og +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(143,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +Am +Sw +Sw +Sw +Am +Sw +Sw +og +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +AM +"} +(144,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +Xr +Sw +Sw +Sw +Ed +Sw +Sw +Sw +Ed +kP +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(145,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +hD +Yo +Sw +Sw +Sw +QM +Ed +Sw +FN +Sw +kP +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(146,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +TI +av +av +av +av +av +av +av +av +hD +Yo +Sw +Sw +Sw +Sw +Sw +Sw +Sw +Ed +kP +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(147,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +og +Mb +Am +Sw +Sw +Sw +Ed +Am +Sw +og +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(148,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +Mb +og +og +og +og +og +og +og +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(149,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +nJ +nJ +nJ +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(150,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +og +NV +NV +NV +og +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(151,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +og +Sw +Sw +Sw +Sw +Sw +og +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(152,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +ad +as +og +Sw +Sw +Zp +Sw +AP +og +as +as +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(153,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +Sw +Sw +Sw +Sw +Sw +av +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(154,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +uc +av +av +an +av +av +av +av +av +ad +aG +aN +aC +ah +ah +ad +av +av +av +ad +aE +wW +rc +aE +nL +nD +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(155,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +av +av +av +as +av +av +av +av +av +av +as +av +av +ad +ad +ad +ad +ah +ah +ad +av +av +av +ad +yH +ah +ah +ah +ar +BH +ad +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(156,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +av +av +av +Sw +av +Sy +av +Sw +av +av +av +av +ad +lD +ah +ar +ah +aI +ad +av +av +av +ad +im +ah +ah +ah +ah +YF +ad +av +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(157,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +Lv +av +as +av +av +av +an +av +av +as +aS +ah +ah +ag +Eu +ad +av +av +av +ad +im +ah +YJ +aE +aE +Rk +ad +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(158,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +ao +av +av +Ed +av +Sw +av +av +av +av +av +ad +xX +ah +ah +ag +hR +ad +av +av +av +ad +gq +ah +ar +EH +ah +ah +ad +av +av +av +av +ad +ad +av +av +av +ao +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(159,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +Lv +av +av +av +av +as +av +Lv +av +av +as +xb +ah +Ax +ah +aJ +ad +av +av +av +ad +ah +ah +jd +ah +ah +ah +ad +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(160,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +ah +av +an +av +av +av +av +av +as +ad +aC +ad +ad +ad +ad +av +av +av +ad +ad +ad +ad +aC +aC +ad +ad +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(161,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +mI +hm +Lj +Xr +Go +ad +av +av +av +Wm +ah +ah +eM +Lv +ah +as +av +av +as +av +av +aO +av +av +av +av +av +av +av +av +as +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(162,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +sO +Xr +Xr +Xr +Go +ad +JQ +av +av +ah +ad +ah +ad +ah +ah +av +av +av +av +as +av +av +av +av +av +av +av +av +av +HJ +av +av +av +av +av +av +av +av +HJ +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(163,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ov +Xr +Xr +Xr +Xr +ad +rG +av +av +av +LB +ao +av +av +av +av +as +av +av +as +ao +av +av +av +av +av +av +av +av +as +av +av +av +av +av +av +Io +Vh +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(164,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +sz +Xr +Xr +Xr +Xr +aY +av +av +av +av +av +av +av +av +as +LB +av +av +av +ao +as +av +av +av +av +av +av +av +av +as +av +av +av +av +as +av +av +yr +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(165,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +Xr +Xr +Kr +pe +Xr +ad +av +av +av +LB +av +av +av +av +av +av +av +av +av +as +HJ +av +av +av +av +av +av +av +av +av +HJ +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(166,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +LB +av +av +av +av +av +as +av +av +as +av +av +av +av +av +av +av +av +av +as +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(167,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(168,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +JH +MY +LE +QZ +ad +av +av +av +av +ad +aD +ah +ar +ah +ah +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(169,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ah +ah +ah +ah +ad +Ep +av +av +av +ad +aE +Ap +oY +ar +ah +ad +aO +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(170,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +ad +wf +ml +Zr +xm +ad +av +av +av +av +ad +aF +ah +Xz +YF +ah +aC +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(171,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +Vp +ah +MO +iC +ad +aP +av +av +av +ad +ah +jX +ah +ah +ah +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(172,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +NE +iC +xm +xb +xb +LM +av +av +av +ad +ad +ad +ad +aC +ad +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(173,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +as +ad +Fa +ah +Uv +xm +ad +hT +mQ +av +av +ad +aG +ah +aC +ah +aS +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +aX +ad +ad +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(174,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +as +av +ad +ad +ad +ad +ad +ad +vA +hp +av +av +ad +ad +aM +ad +aR +TG +ad +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +rF +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(175,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +as +av +av +av +as +av +av +av +av +av +av +av +av +as +ad +ad +ad +ad +ad +ad +av +av +ad +ad +aP +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(176,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +ad +ad +ah +af +ah +ah +ar +ah +ah +ah +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +as +av +av +av +av +av +av +xQ +xQ +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(177,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ad +ah +ah +ah +ah +ah +oY +Xz +ah +ah +Ap +ad +av +av +av +av +av +av +av +av +av +HJ +av +av +av +as +av +av +av +av +av +av +av +xQ +xQ +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(178,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +af +ah +ah +ah +ap +ah +ap +ah +ap +YF +ar +ad +av +av +av +av +av +av +av +av +av +av +as +av +av +av +as +as +av +av +HJ +as +av +xQ +xQ +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(179,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ad +CA +ai +ah +ai +ah +ah +ah +ai +ah +at +ad +av +av +av +av +av +av +as +av +av +av +av +av +av +av +as +av +av +av +av +av +av +xQ +xQ +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(180,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ag +KL +ak +ah +ah +jr +jr +jr +ah +ap +ah +au +as +av +av +av +av +av +av +as +aO +av +av +av +av +av +as +av +av +aO +av +av +av +xQ +xQ +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(181,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +ad +ah +xN +oP +ah +ah +jr +Zr +jr +ah +ap +ah +Sp +as +av +av +av +av +ad +ad +ad +ad +aC +ad +ad +av +ad +ad +ad +aC +ad +ad +ad +av +ad +ad +aP +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +av +ad +ad +ad +av +av +av +av +av +rF +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(182,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +an +av +av +av +ad +ag +bt +aj +ah +ah +jr +jr +jr +ah +ap +ah +au +as +av +av +av +av +ad +aG +ad +rk +ah +aE +ad +av +ad +aG +ad +ah +ah +aE +ad +av +ad +ad +QI +QI +QI +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(183,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +ad +ad +ah +ai +ah +ai +ah +ah +ah +ai +ah +at +ad +av +av +av +av +av +ad +TF +ad +ah +ah +aJ +ad +av +ad +us +ad +aR +ah +aJ +ad +as +ad +ad +ah +ah +ah +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(184,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +ad +af +ah +ah +ah +ap +ah +ap +ah +ap +ah +ar +ad +av +av +av +av +av +ad +ah +aC +ah +ah +ah +ad +av +ad +ah +aC +ar +ah +CA +ad +av +ad +ad +ah +ah +ah +aY +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(185,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +av +av +av +av +av +ad +ad +ah +ah +ah +ah +ah +ah +ah +ah +BH +CA +ad +av +av +av +av +av +ad +ah +ad +ah +ah +aS +ad +av +ad +ah +ad +BH +ah +aS +ad +av +ad +ad +ON +aJ +ah +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(186,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +av +av +av +av +av +av +ad +ad +ah +af +ah +jd +ar +YF +ah +ah +ah +ad +as +av +av +av +av +ad +ad +ad +ad +aC +ad +ad +as +ad +ad +ad +aC +ad +ad +ad +av +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(187,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +av +av +av +av +av +av +ad +ad +al +ad +ad +ad +ad +ad +ad +as +av +as +as +av +av +av +av +av +av +as +av +wl +Vh +HJ +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(188,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +ad +ai +ah +ah +ah +ad +ad +av +av +av +av +av +as +av +av +av +av +av +as +av +av +av +kg +av +av +qX +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(189,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +ad +am +ah +ah +nZ +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +av +av +Zt +av +av +av +kg +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(190,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +av +av +av +av +av +av +av +ad +ai +ah +ah +aT +ad +av +av +av +av +av +av +av +av +av +av +av +ad +aG +aN +ad +av +av +Vh +ZZ +av +wP +Vh +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(191,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +aC +ad +ad +ad +ad +av +HJ +av +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(192,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +av +av +av +an +av +av +av +av +av +av +an +av +av +av +as +av +av +av +av +av +an +av +av +av +av +ad +ah +ah +mA +ah +aS +ad +av +av +av +AQ +ad +ah +aI +RL +aI +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +ad +ad +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(193,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +as +av +av +av +av +av +av +av +av +av +av +ad +xT +ah +aF +ah +ah +ad +aO +av +av +av +aC +ah +ah +ar +ah +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +mi +mi +mi +av +ad +ad +ad +av +av +av +av +av +aq +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(194,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +as +av +av +av +av +av +av +av +av +av +ad +rH +ah +ah +ah +ah +aC +av +av +av +av +ad +ah +zB +Hy +ah +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +rF +av +av +av +av +av +av +ad +av +av +ad +ad +ad +mi +ae +mi +av +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(195,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +ad +Or +hH +ah +GW +FM +ad +av +av +av +as +ad +mp +Se +hY +Ci +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +mi +mi +mi +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(196,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +aI +ah +aI +ah +XH +ad +av +av +av +ad +ad +ad +aC +ad +ad +ad +as +av +as +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +ao +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +aq +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(197,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ax +vM +ah +aE +ah +aE +ah +aJ +ad +av +av +av +av +Yt +Yt +av +Yt +Yt +av +av +as +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(198,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +an +av +av +av +ad +ah +UP +ah +aJ +ah +aJ +ah +ah +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +aY +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(199,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ah +az +ah +ah +ah +ah +ah +ah +ah +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +an +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(200,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +av +av +ad +av +av +av +av +av +av +av +av +av +av +ad +ah +aA +ah +ah +ah +ah +ah +ah +ah +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(201,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +ad +ah +ah +ah +aI +ah +aI +ah +aI +ad +av +av +av +av +av +av +av +an +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(202,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +ad +ay +aB +ah +aE +ah +aE +ah +aE +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +av +av +av +av +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(203,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +ad +ad +ad +ad +aJ +ah +aJ +ah +aJ +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(204,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +av +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(205,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ad +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(206,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(207,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(208,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(209,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(210,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(211,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +ac +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +ab +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(212,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(213,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(214,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(215,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(216,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(217,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(218,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(219,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(220,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(221,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(222,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(223,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(224,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(225,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(226,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(227,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(228,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(229,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(230,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(231,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(232,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(233,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(234,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(235,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(236,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(237,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(238,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(239,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(240,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(241,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(242,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(243,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(244,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(245,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(246,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(247,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(248,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(249,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(250,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(251,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(252,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(253,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(254,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +"} +(255,1,1) = {" +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa +aa "} From ddcd628abe8ea8cd7620a6cdc0ad269c5e37f05a Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Wed, 7 Oct 2020 15:04:50 -0400 Subject: [PATCH 06/10] Implements Lemon's suggestion about the brass walls and floors --- code/game/turfs/simulated/floor/misc_floor.dm | 4 ++-- code/game/turfs/simulated/walls_misc.dm | 21 +------------------ 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 759997d09469..37cf8a918d5d 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -163,7 +163,7 @@ icon_state = "clockwork_floor" baseturf = /turf/simulated/floor/clockwork var/dropped_brass - var/uses_overlay = FALSE //disabled the overlay cause it doesn't remove itself + var/uses_overlay = TRUE var/obj/effect/clockwork/overlay/floor/realappearence /turf/simulated/floor/clockwork/Initialize(mapload) @@ -174,7 +174,7 @@ realappearence = new /obj/effect/clockwork/overlay/floor(src) realappearence.linked = src -/turf/simulated/floor/clockwork/Destroy() +/turf/simulated/floor/clockwork/BeforeChange() if(uses_overlay && realappearence) QDEL_NULL(realappearence) return ..() diff --git a/code/game/turfs/simulated/walls_misc.dm b/code/game/turfs/simulated/walls_misc.dm index 878dcbae9219..051bc24a93e6 100644 --- a/code/game/turfs/simulated/walls_misc.dm +++ b/code/game/turfs/simulated/walls_misc.dm @@ -73,7 +73,7 @@ realappearance = new /obj/effect/clockwork/overlay/wall(src) realappearance.linked = src -/turf/simulated/wall/clockwork/Destroy() +/turf/simulated/wall/clockwork/BeforeChange() QDEL_NULL(realappearance) return ..() @@ -90,25 +90,6 @@ animate(src, color = previouscolor, time = 8) addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8) -/turf/simulated/wall/clockwork/dismantle_wall(devastated=0, explode=0) - if(devastated) - devastate_wall() - ChangeTurf(baseturf) - else - playsound(src, 'sound/items/welder.ogg', 100, 1) - var/newgirder = break_wall() - if(newgirder) //maybe we want a gear! - transfer_fingerprints_to(newgirder) - ChangeTurf(baseturf) - - for(var/obj/O in src) //Eject contents! - if(istype(O, /obj/structure/sign/poster)) - var/obj/structure/sign/poster/P = O - P.roll_and_drop(src) - else - O.forceMove(src) - return TRUE - /turf/simulated/wall/clockwork/devastate_wall() for(var/i in 1 to 2) new/obj/item/clockwork/alloy_shards/large(src) From 4bd03eb770c28ad75fd47f3a9b7e32ff963cbfb4 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Fri, 16 Oct 2020 19:41:53 -0400 Subject: [PATCH 07/10] Deconflicts and implements Steel's changes --- _maps/map_files/RandomZLevels/beach.dmm | 178 +++++++++--------- code/game/area/ss13_areas.dm | 2 +- code/game/machinery/door_control.dm | 4 +- .../game/objects/effects/spawners/lootdrop.dm | 2 +- code/game/objects/structures/ladders.dm | 4 +- code/game/turfs/simulated/floor/misc_floor.dm | 10 +- 6 files changed, 100 insertions(+), 100 deletions(-) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index 1c5af225c602..41a0c898a12d 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -315,7 +315,7 @@ dir = 8 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "bJ" = ( /obj/structure/disposalpipe/segment, /turf/simulated/floor/wood, @@ -329,7 +329,7 @@ }, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "bM" = ( /obj/structure/chair/stool, /turf/simulated/floor/wood, @@ -354,7 +354,7 @@ dir = 5 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "bT" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; @@ -376,7 +376,7 @@ /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/stalkybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "cg" = ( /obj/effect/overlay/palmtree_r, /obj/effect/decal/snow/sand/edge{ @@ -386,7 +386,7 @@ dir = 10 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "ci" = ( /obj/structure/flora/ausbushes/genericbush, /obj/structure/flora/ausbushes/sparsegrass, @@ -459,7 +459,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "dj" = ( /turf/simulated/floor/beach/roughcoastline/dense, /area/awaymission/beach) @@ -492,7 +492,7 @@ "dP" = ( /mob/living/simple_animal/butterfly, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "em" = ( /obj/structure/closet/crate, /obj/item/flashlight, @@ -598,7 +598,7 @@ }, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "fA" = ( /obj/structure/flora/rock, /turf/unsimulated/beach/sand/dense, @@ -623,15 +623,15 @@ "fN" = ( /obj/structure/flora/ausbushes/genericbush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "fR" = ( /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "fV" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/palebush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "gn" = ( /obj/item/clockwork/component/geis_capacitor/fallen_armor, /turf/unsimulated/floor/grass, @@ -681,12 +681,12 @@ /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/palebush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "gW" = ( /obj/effect/overlay/palmtree_l, /obj/effect/overlay/coconut, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "hm" = ( /obj/structure/rack, /obj/item/stack/sheet/plasteel{ @@ -699,7 +699,7 @@ "hn" = ( /obj/effect/baseturf_helper/beach/dense_roughsand, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "hp" = ( /obj/item/clothing/gloves/botanic_leather, /turf/unsimulated/beach/water/deep/sand_floor, @@ -719,7 +719,7 @@ name = "rough sand" }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "hB" = ( /obj/structure/closet, /obj/item/clothing/under/pinkhawaiianshirt, @@ -789,7 +789,7 @@ "if" = ( /obj/effect/overlay/palmtree_r, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "im" = ( /obj/machinery/fishtank/tank, /turf/unsimulated/beach/water/deep/wood_floor, @@ -807,7 +807,7 @@ dir = 4 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "ix" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (SOUTHWEST)"; @@ -833,7 +833,7 @@ "iI" = ( /obj/effect/overlay/palmtree_r, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "iK" = ( /obj/structure/chair/beachchair/red, /obj/item/toy/plushie/octopus, @@ -951,7 +951,7 @@ "kq" = ( /obj/structure/flora/grass/green, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "ku" = ( /obj/structure/closet/secure_closet/freezer/kitchen{ locked = 0; @@ -1068,7 +1068,7 @@ "lJ" = ( /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "lK" = ( /obj/structure/ladder/unbreakable/dive_point/anchor{ id = "volcano_island" @@ -1178,7 +1178,7 @@ /obj/effect/overlay/palmtree_l, /obj/effect/overlay/coconut, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "mQ" = ( /obj/effect/decal/cleanable/blood, /turf/unsimulated/beach/water/deep/sand_floor, @@ -1198,7 +1198,7 @@ }, /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "na" = ( /obj/structure/closet/secure_closet/freezer/meat/open, /obj/structure/disposalpipe/junction{ @@ -1232,7 +1232,7 @@ dir = 10 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "no" = ( /obj/structure/window/reinforced{ dir = 1 @@ -1285,7 +1285,7 @@ "nV" = ( /obj/structure/flora/ausbushes/grassybush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "nZ" = ( /obj/structure/bed, /obj/item/bedsheet/cult, @@ -1351,7 +1351,7 @@ dir = 5 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "oE" = ( /obj/structure/table/reinforced, /obj/item/storage/toolbox/syndicate, @@ -1429,7 +1429,7 @@ "pu" = ( /mob/living/simple_animal/parrot, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "pw" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; @@ -1464,7 +1464,7 @@ dir = 9 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "qf" = ( /obj/machinery/door/airlock{ name = "Private Restroom" @@ -1513,7 +1513,7 @@ "qP" = ( /obj/structure/flora/grass/brown, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "qR" = ( /turf/unsimulated/floor{ tag = "icon-dark"; @@ -1545,7 +1545,7 @@ /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/genericbush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "rk" = ( /obj/structure/closet/cabinet, /obj/item/clothing/shoes/black, @@ -1567,7 +1567,7 @@ dir = 5 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "rv" = ( /obj/effect/spawner/lootdrop/brass_temple_spawner, /turf/unsimulated/floor{ @@ -1599,7 +1599,7 @@ }, /mob/living/simple_animal/butterfly, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "rF" = ( /mob/living/simple_animal/crab, /turf/unsimulated/beach/water/deep/sand_floor, @@ -1828,7 +1828,7 @@ }, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "uN" = ( /obj/structure/table/wood, /obj/item/storage/toolbox/mechanical, @@ -1837,7 +1837,7 @@ "uR" = ( /obj/effect/overlay/palmtree_l, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "ve" = ( /obj/structure/sink{ dir = 4; @@ -1993,7 +1993,7 @@ name = "rough sand" }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "wx" = ( /obj/structure/bed, /obj/item/bedsheet/black, @@ -2015,11 +2015,11 @@ /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/lavendergrass, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "wR" = ( /obj/structure/flora/ausbushes/reedbush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "wS" = ( /turf/unsimulated/floor{ icon_state = "grimy" @@ -2153,7 +2153,7 @@ "yG" = ( /obj/structure/flora/ausbushes/palebush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "yH" = ( /obj/structure/table/wood, /obj/item/storage/box/monkeycubes/neaeracubes, @@ -2216,7 +2216,7 @@ /obj/structure/flora/rock/pile, /obj/effect/overlay/palmtree_l, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "zk" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/leafybush, @@ -2230,11 +2230,11 @@ dir = 6 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "zr" = ( /obj/structure/flora/bush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "zw" = ( /obj/structure/flora/grass/green, /obj/structure/flora/ausbushes/leafybush, @@ -2263,7 +2263,7 @@ "zL" = ( /obj/structure/flora/ausbushes/lavendergrass, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "zQ" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (EAST)"; @@ -2299,7 +2299,7 @@ "zZ" = ( /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Aa" = ( /obj/structure/flora/ausbushes/sunnybush, /turf/unsimulated/beach/sand/dense, @@ -2377,7 +2377,7 @@ dir = 9 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "AW" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (EAST)"; @@ -2538,7 +2538,7 @@ dir = 6 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Ci" = ( /obj/structure/bed, /obj/item/bedsheet/medical, @@ -2566,7 +2566,7 @@ dir = 8 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Co" = ( /obj/structure/cult/pylon, /turf/unsimulated/beach/water/deep/wood_floor{ @@ -2582,7 +2582,7 @@ dir = 8 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Cv" = ( /obj/structure/shuttle/engine/heater, /turf/simulated/floor/plating/airless, @@ -2653,7 +2653,7 @@ /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/fernybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Dm" = ( /obj/effect/decal/snow/sand/edge{ name = "rough sand" @@ -2790,7 +2790,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "ER" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (NORTHWEST)"; @@ -2822,7 +2822,7 @@ /obj/structure/flora/grass/green, /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Fa" = ( /obj/item/chair/wood/wings, /turf/unsimulated/beach/water/deep/wood_floor, @@ -2865,7 +2865,7 @@ "Fl" = ( /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Fp" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (NORTHEAST)"; @@ -2880,7 +2880,7 @@ /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Fv" = ( /obj/machinery/door/airlock{ name = "Private Recharge room" @@ -2998,7 +2998,7 @@ dir = 4 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Hr" = ( /obj/effect/decal/remains/human{ plane = -1 @@ -3052,12 +3052,12 @@ /obj/effect/overlay/palmtree_r, /obj/effect/overlay/coconut, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Id" = ( /obj/structure/flora/rock/pile, /obj/effect/overlay/palmtree_l, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Ih" = ( /obj/structure/window/reinforced, /obj/structure/window/reinforced{ @@ -3079,7 +3079,7 @@ dir = 9 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "In" = ( /obj/structure/fermenting_barrel, /turf/unsimulated/beach/water/deep/wood_floor, @@ -3097,7 +3097,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "It" = ( /mob/living/simple_animal/hostile/syndicate/ranged/space/autogib, /turf/unsimulated/floor{ @@ -3112,7 +3112,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Iw" = ( /mob/living/simple_animal/crab/Coffee, /turf/simulated/floor/beach/roughsand, @@ -3126,7 +3126,7 @@ dir = 4 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "IA" = ( /obj/machinery/hydroponics/constructable, /turf/simulated/floor/plasteel{ @@ -3172,7 +3172,7 @@ "IO" = ( /obj/structure/flora/ausbushes/genericbush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "IZ" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; @@ -3315,7 +3315,7 @@ /area/awaymission/undersea) "JZ" = ( /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Ka" = ( /obj/structure/flora/ausbushes/ppflowers, /obj/effect/decal/snow/sand/edge{ @@ -3325,7 +3325,7 @@ dir = 8 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Kb" = ( /obj/machinery/door/poddoor/brass/beach_brass_temple, /turf/unsimulated/floor{ @@ -3395,7 +3395,7 @@ /obj/effect/overlay/palmtree_r, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "KU" = ( /obj/structure/bed, /obj/item/bedsheet/red, @@ -3421,11 +3421,11 @@ dir = 8 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Lq" = ( /obj/structure/flora/grass/brown, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Ls" = ( /obj/machinery/newscaster{ pixel_y = -32 @@ -3494,7 +3494,7 @@ /obj/effect/overlay/palmtree_r, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "LZ" = ( /obj/machinery/poolcontroller/invisible, /turf/unsimulated/beach/water/deep, @@ -3522,18 +3522,18 @@ "Mx" = ( /obj/structure/flora/ausbushes/lavendergrass, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "My" = ( /obj/effect/decal/snow/sand/surround{ name = "rough sand" }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "MB" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "MG" = ( /obj/structure/window/reinforced{ dir = 1 @@ -3550,7 +3550,7 @@ "ML" = ( /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "MO" = ( /obj/item/clothing/shoes/jackboots, /turf/unsimulated/beach/water/deep/wood_floor, @@ -3570,7 +3570,7 @@ "Nb" = ( /obj/effect/overlay/palmtree_l, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Ne" = ( /obj/structure/flora/ausbushes/fernybush, /turf/unsimulated/floor/grass, @@ -3578,7 +3578,7 @@ "Nn" = ( /obj/structure/flora/ausbushes/reedbush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Nq" = ( /obj/structure/closet/crate/internals, /obj/item/clothing/mask/breath, @@ -3621,7 +3621,7 @@ dir = 4 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "NJ" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (NORTH)"; @@ -3631,11 +3631,11 @@ }, /obj/structure/flora/ausbushes/sunnybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "NP" = ( /obj/structure/flora/rock, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "NQ" = ( /obj/structure/bed, /obj/item/bedsheet/red, @@ -3671,7 +3671,7 @@ dir = 9 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Ob" = ( /obj/structure/flora/rock, /obj/structure/flora/ausbushes/leafybush, @@ -3725,7 +3725,7 @@ }, /obj/effect/overlay/palmtree_r, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "OD" = ( /obj/structure/window/reinforced{ dir = 4 @@ -3805,7 +3805,7 @@ "PH" = ( /obj/structure/flora/grass/green, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "PL" = ( /obj/effect/overlay/palmtree_l, /obj/effect/decal/snow/sand/edge{ @@ -3815,7 +3815,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "PR" = ( /turf/unsimulated/wall{ icon_state = "rock"; @@ -3968,7 +3968,7 @@ /obj/effect/overlay/palmtree_r, /obj/effect/overlay/coconut, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "RY" = ( /obj/structure/sign/poster/contraband/syndicate_recruitment{ pixel_x = 0; @@ -4007,7 +4007,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Sp" = ( /obj/structure/mineral_door/sandstone, /turf/space, @@ -4026,7 +4026,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Sw" = ( /turf/unsimulated/beach/water/deep/wood_floor{ icon_state = "titanium_blue" @@ -4077,7 +4077,7 @@ dir = 9 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "SS" = ( /mob/living/simple_animal/hostile/retaliate/carp/koi/honk, /turf/unsimulated/beach/water/deep/sand_floor, @@ -4248,7 +4248,7 @@ "Vq" = ( /obj/structure/flora/rock, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Vw" = ( /obj/structure/closet/secure_closet/personal, /turf/simulated/floor/wood, @@ -4262,7 +4262,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "VO" = ( /obj/structure/ladder/unbreakable/dive_point/buoy{ id = "brass temple" @@ -4281,7 +4281,7 @@ "We" = ( /obj/structure/flora/ausbushes/palebush, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Wm" = ( /obj/structure/mecha_wreckage/odysseus, /turf/unsimulated/beach/water/deep/wood_floor, @@ -4391,7 +4391,7 @@ /obj/structure/flora/rock, /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "XH" = ( /obj/structure/piano, /turf/unsimulated/beach/water/deep/wood_floor, @@ -4443,7 +4443,7 @@ dir = 10 }, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Yo" = ( /obj/structure/shuttle/engine/heater{ dir = 1 @@ -4492,7 +4492,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "YM" = ( /obj/machinery/tcomms/relay/beachresort, /turf/simulated/floor/plasteel, @@ -4524,7 +4524,7 @@ /obj/structure/flora/rock/pile, /obj/effect/overlay/palmtree_r, /turf/simulated/floor/beach/roughsand/dense, -/area/awaymission/beach/boundry) +/area/awaymission/beach/boundary) "Zg" = ( /obj/structure/closet/crate, /obj/item/toy/plushie/nukeplushie, diff --git a/code/game/area/ss13_areas.dm b/code/game/area/ss13_areas.dm index 19b0a3b28b97..7b1ef2ad2682 100644 --- a/code/game/area/ss13_areas.dm +++ b/code/game/area/ss13_areas.dm @@ -2066,7 +2066,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station /area/awaymission/beach/entrance //this extra area here is to cause the ambiance to play since going through gateways doesn't count as moving to a new area. -/area/awaymission/beach/boundry +/area/awaymission/beach/boundary /area/awaymission/beach/offshore name = "Offshore location" diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index 8adfad7d274f..effc81fcefae 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -138,7 +138,7 @@ . = ..() var/temple_traps = rand(1,10) //no forbidden temple is complete without some traps! switch(temple_traps) - if(1,5)//You're safe, this time. + if(1 to 5)//You're safe, this time. return if(6,7) new /mob/living/simple_animal/hostile/poison/giant_spider(get_turf(src)) @@ -152,7 +152,7 @@ for(var/mob/M in range(5, src)) shake_camera(M, 15, 1) visible_message("The ground begins to shake and roaring machinery can be heard! RUN!") - addtimer(CALLBACK(src, .proc/temple_collapse), 50) + addtimer(CALLBACK(src, .proc/temple_collapse), 5 SECONDS) /obj/machinery/door_control/brass/beach_brass_temple_switch/proc/temple_collapse() for(var/mob/M in range(20, src)) diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 625b57332f94..92097bc9660c 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -406,7 +406,7 @@ /obj/effect/spawner/lootdrop/brass_temple_spawner name = "brass temple spawner" - lootdoubles = 0 + lootdoubles = FALSE loot = list( /obj/item/clockwork/component/geis_capacitor/fallen_armor = 2, //you got lucky in a bad way diff --git a/code/game/objects/structures/ladders.dm b/code/game/objects/structures/ladders.dm index 7a1b984586ed..0331674446e6 100644 --- a/code/game/objects/structures/ladders.dm +++ b/code/game/objects/structures/ladders.dm @@ -177,7 +177,7 @@ /obj/structure/ladder/unbreakable/dive_point/buoy name = "diving point buoy" - desc = "A buoy marking the location of an underwater dive area. You can pull people and objects along with you" //the notice is because several people didn't know you could do this and I feel it's important people knew + desc = "A buoy marking the location of an underwater dive area. You can pull people and objects along with you" //the notice is because several people didn't know you could do this and I feel it's important people knew icon = 'icons/misc/beach.dmi' icon_state = "buoy" id = "dive" @@ -187,7 +187,7 @@ /obj/structure/ladder/unbreakable/dive_point/anchor name = "diving point anchor" - desc = "An anchor tethered to the buoy at the surface, to keep the dive area marked. You can pull people and objects along with you" + desc = "An anchor tethered to the buoy at the surface, to keep the dive area marked. You can pull people and objects along with you" icon = 'icons/misc/beach.dmi' icon_state = "anchor" id = "dive" diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 37cf8a918d5d..e6e16b3335ee 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -42,7 +42,7 @@ /turf/simulated/floor/beach name = "beach" icon = 'icons/misc/beach.dmi' - var/water_overlay_image = null + var/water_overlay_image /turf/simulated/floor/beach/crowbar_act() return @@ -56,10 +56,10 @@ icon_state = "desert" /turf/simulated/floor/beach/roughsand/dense //made simulated versions to fix lighting issues - density = 1 + density = TRUE /turf/simulated/floor/beach/roughsand/New() //a simulated version of the unsimulated beach sand - icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4") + icon_state = "desert[rand(0, 4)]" ..() /turf/simulated/floor/beach/roughcoastline/New() @@ -67,7 +67,7 @@ if(water_overlay_image) var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER) overlay_image.plane = GAME_PLANE - overlays += overlay_image + add_overlay(overlay_image) /turf/simulated/floor/beach/roughcoastline name = "Coastline" @@ -75,7 +75,7 @@ water_overlay_image = "water_coast" /turf/simulated/floor/beach/roughcoastline/dense //for boundary "walls" - density = 1 + density = TRUE /turf/simulated/floor/beach/coastline name = "coastline" From 1018a49d7b5f40717cb168dc0ef15c0797e73017 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Fri, 16 Oct 2020 22:17:26 -0400 Subject: [PATCH 08/10] Fixes icons and the map, the point of sale had been removed and was in the resort --- _maps/map_files/RandomZLevels/beach.dmm | 73 ++++++------------ code/game/turfs/simulated/floor/misc_floor.dm | 4 +- code/game/turfs/unsimulated/beach.dm | 28 ++++++- icons/misc/beach.dmi | Bin 202899 -> 202906 bytes 4 files changed, 51 insertions(+), 54 deletions(-) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index 41a0c898a12d..1238adf0f970 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -1297,11 +1297,7 @@ /turf/simulated/floor/wood, /area/awaymission/beach) "og" = ( -/turf/unsimulated/beach/water/deep/rock_wall{ - icon = 'icons/turf/walls/shuttle_wall.dmi'; - icon_state = "map-shuttle"; - name = "Shuttle Wall" - }, +/turf/unsimulated/beach/water/deep/sunken_shuttle_wall, /area/awaymission/undersea) "ok" = ( /turf/unsimulated/wall{ @@ -1720,7 +1716,6 @@ /area/awaymission/undersea) "sW" = ( /obj/structure/table/wood, -/obj/machinery/pos, /obj/effect/decal/snow/sand/edge{ name = "rough sand" }, @@ -2264,6 +2259,10 @@ /obj/structure/flora/ausbushes/lavendergrass, /turf/simulated/floor/beach/roughsand/dense, /area/awaymission/beach/boundary) +"zP" = ( +/obj/item/clothing/head/hasturhood, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) "zQ" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (EAST)"; @@ -3459,7 +3458,6 @@ /area/awaymission/undersea) "LE" = ( /obj/structure/rack, -/obj/item/melee/classic_baton, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "LJ" = ( @@ -3499,13 +3497,6 @@ /obj/machinery/poolcontroller/invisible, /turf/unsimulated/beach/water/deep, /area/awaymission/beach) -"Mb" = ( -/turf/unsimulated/beach/water/deep/rock_wall{ - icon = 'icons/turf/walls/shuttle_wall.dmi'; - icon_state = "map-shuttle"; - name = "Shuttle Wall" - }, -/area/shuttle/transport) "Mq" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; @@ -3861,9 +3852,9 @@ /turf/simulated/floor/wood, /area/awaymission/beach) "QI" = ( -/turf/unsimulated/wall/fakeglass{ - icon_state = "fakewindows2"; - dir = 1 +/obj/effect/spawner/window/reinforced, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "floor3" }, /area/awaymission/undersea) "QJ" = ( @@ -3918,11 +3909,6 @@ /obj/item/shovel/spade, /turf/simulated/floor/plasteel, /area/awaymission/beach) -"Rk" = ( -/obj/structure/table/wood, -/obj/machinery/pos, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) "Rl" = ( /mob/living/simple_animal/hostile/deathsquid, /turf/unsimulated/beach/water/deep/sand_floor, @@ -3958,11 +3944,7 @@ }, /area/awaymission/beach/offshore) "RU" = ( -/turf/unsimulated/beach/water/deep/rock_wall{ - icon = 'icons/turf/walls/wood_wall.dmi'; - icon_state = "wood"; - name = "Wood Wall" - }, +/turf/unsimulated/beach/water/deep/sunken_wood_wall, /area/awaymission/undersea) "RV" = ( /obj/effect/overlay/palmtree_r, @@ -4008,10 +3990,6 @@ }, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) -"Sp" = ( -/obj/structure/mineral_door/sandstone, -/turf/space, -/area/awaymission/undersea) "Ss" = ( /obj/structure/sign/poster/official/cohiba_robusto_ad{ pixel_y = 32 @@ -4566,9 +4544,6 @@ /obj/item/mecha_parts/mecha_equipment/cable_layer, /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp, /obj/item/mecha_parts/mecha_equipment/rcd, -/obj/item/stack/sheet/plasteel{ - amount = 20 - }, /turf/unsimulated/beach/water/deep/wood_floor{ icon_state = "titanium_blue" }, @@ -42291,7 +42266,7 @@ av av av og -Mb +og Am Sw Sw @@ -42548,7 +42523,7 @@ av av av av -Mb +og og og og @@ -44801,7 +44776,7 @@ ah YJ aE aE -Rk +aE ad av av @@ -46328,7 +46303,7 @@ av as av av -as +HJ ao av av @@ -49156,7 +49131,7 @@ av av av av -as +HJ av ad ad @@ -49674,7 +49649,7 @@ av av av av -av +HJ av av av @@ -49932,7 +49907,7 @@ av av av av -av +HJ av av av @@ -50189,7 +50164,7 @@ av av av av -av +as av av av @@ -50447,7 +50422,7 @@ av av av av -as +HJ av av av @@ -50943,7 +50918,7 @@ av av av ad -ah +zP xN oP ah @@ -50954,7 +50929,7 @@ jr ah ap ah -Sp +au as av av @@ -52243,7 +52218,7 @@ ad as av av -av +HJ av ad ad @@ -53013,7 +52988,7 @@ av av av av -av +HJ av av ad @@ -53515,7 +53490,7 @@ av av ad ad -ad +au ad ad ad @@ -54033,7 +54008,7 @@ av av av ao -as +HJ av av av diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index e6e16b3335ee..54c035d2dc63 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -53,13 +53,13 @@ /turf/simulated/floor/beach/roughsand name = "Sand" - icon_state = "desert" + icon_state = "rough_sand1" /turf/simulated/floor/beach/roughsand/dense //made simulated versions to fix lighting issues density = TRUE /turf/simulated/floor/beach/roughsand/New() //a simulated version of the unsimulated beach sand - icon_state = "desert[rand(0, 4)]" + icon_state = "rough_sand[rand(1, 6)]" ..() /turf/simulated/floor/beach/roughcoastline/New() diff --git a/code/game/turfs/unsimulated/beach.dm b/code/game/turfs/unsimulated/beach.dm index a7c73971e998..589d282152a8 100644 --- a/code/game/turfs/unsimulated/beach.dm +++ b/code/game/turfs/unsimulated/beach.dm @@ -13,12 +13,12 @@ /turf/unsimulated/beach/sand name = "Sand" - icon_state = "desert" + icon_state = "rough_sand1" mouse_opacity = MOUSE_OPACITY_ICON /turf/unsimulated/beach/sand/Initialize(mapload) . = ..() //adds some aesthetic randomness to the beach sand - icon_state = pick("desert", "desert0", "desert1", "desert2", "desert3", "desert4") + icon_state = "rough_sand[rand(1, 6)]" /turf/unsimulated/beach/sand/dense //for boundary "walls" density = 1 @@ -120,7 +120,29 @@ /turf/unsimulated/beach/water/deep/rock_wall name = "Reef Stone" - icon_state = "desert7" + icon_state = "reef_stone1" + density = 1 + opacity = 1 + explosion_block = 2 + mouse_opacity = MOUSE_OPACITY_ICON + +/turf/unsimulated/beach/water/deep/rock_wall/Initialize(mapload) + . = ..() + icon_state = "reef_stone[rand(1, 3)]" + +/turf/unsimulated/beach/water/deep/sunken_wood_wall + name = "Sunken Wood Wall" + icon = 'icons/turf/walls/wood_wall.dmi' + icon_state = "wood" + density = 1 + opacity = 1 + explosion_block = 2 + mouse_opacity = MOUSE_OPACITY_ICON + +/turf/unsimulated/beach/water/deep/sunken_shuttle_wall + name = "Sunken Shuttle Wall" + icon = 'icons/turf/walls/shuttle_wall.dmi' + icon_state = "map-shuttle" density = 1 opacity = 1 explosion_block = 2 diff --git a/icons/misc/beach.dmi b/icons/misc/beach.dmi index 46ef2712ed3be7faac102812d5b60a0c3ffbd3a3..022ce561b91cc40d873e8057b82760e2b146ce90 100644 GIT binary patch delta 452 zcmV;#0XzPau?(8A43H#$o_bVRbVOxyV{&P5bZKvH004NLt(3uT!Y~Ym&&^Xra77aZ zwy{Gm6KLW9uYgQrXr+*##9hSO?@*})+Rp8ma%rWnf9?N|J(i#Augy-bwj1${&ga(1 zL@r*YA_}>pOeS(Z6H$iwDWmem6A;Qojt-#pNJJ`kUM4aY(b2(w8O!GyLP)ckTHa7g z5h>Jgm5H2B?#{#KsdWZjEwUIWlG@aof;?Mn>fq1?McUd+6Q2#>oh?q;-h)vwTT0D# z{-K4P4`3Gq*w+E<8_yyh_nxf59zIlT4<9O)_d%gYM|)MAc=Y7J`)N;(Z>i+?c1n(K z#e-1+2;KhBK4@`&To1`?(sJ6g=pVIm(>cEb^h!VYYkMrT?ipEy@~KrQA6$j<*;Odt z)&=xAhJIM*7P||FhY%FvrJb8UY+eyL47pJa37>)1Haw#efNn2PN4WtA9Uo5sLdQoF zfY9-=1R!+$e)I^+*{7^5XX#frV-aPR{Kb+?Wc)FS-zN0f)o^0f)o^0=L8g1J4!@m)2td delta 445 zcmV;u0Yd(ou?&;343H#$mwHrKbVOxyV{&P5bZKvH004NLt(8q~!Y~kp*YPPV*rJLM z+R{Zf6{unXS3pBNB#}&vW}I5wzKclw1!E&BZxZRt)A+ra$xwbTewJ%FUoFKi64$1Y zxtzX@MdE5@E1An|B9aR0va;O^Td*i|IoN{M1CgkjSmrVn$&O%ukKOYbLMW;;HM^i@ zB2lQ}D07*OuFlQpg`o!NEHZskq%ehPTFb#=3xcf=O0+SDK0bMZSI9^u;Ws%%8l6#NjJ|el#NN%IrGK`i4%n??X1k4dumjuiaR+j|KVXGM$<-j6sw)bZ! zP0#&58QnSewCq2BYSoQ*G@sDx{0O(yuGKm+vJ)Ck?SzJdJE7t1PH6aA2hjTz;xO-m zx(X)@At=PdeJ;CMsv;qDb)$L`T!Gfcy`tj4UN(@YTn82@9FGHw6pqG$MGD8_z#@hB z7b7S#FIm~m(nB{>kyOU=!C0Bg^lOxUj(P(AMEj_>;vQNf7+~*T>rqkA6qN~QyKT+U n2My(7J(u& Date: Thu, 22 Oct 2020 19:02:30 -0400 Subject: [PATCH 09/10] Gateway now for certain connects to the station and made some minor edits --- _maps/map_files/RandomZLevels/beach.dmm | 45 ++++++++++++++++--------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index 1238adf0f970..b43d6dace518 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -905,7 +905,7 @@ dir = 9 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) +/area/awaymission/beach/entrance) "jX" = ( /obj/structure/sign/poster/official/love_ian{ pixel_x = 32 @@ -1010,7 +1010,7 @@ "kU" = ( /obj/machinery/gateway/centeraway, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) +/area/awaymission/beach/entrance) "la" = ( /obj/effect/decal/snow/sand/edge{ tag = "icon-gravsnow_corner (WEST)"; @@ -1770,6 +1770,10 @@ }, /turf/simulated/floor/wood, /area/awaymission/beach) +"tJ" = ( +/obj/structure/stone_tile/surrounding_tile, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) "tM" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/sunnybush, @@ -2037,9 +2041,6 @@ icon_state = "dark" }, /area/awaymission/beach/offshore) -"xj" = ( -/turf/simulated/floor/beach/roughsand, -/area/awaymission/beach/entrance) "xm" = ( /obj/effect/decal/cleanable/blood, /turf/unsimulated/beach/water/deep/wood_floor, @@ -2194,6 +2195,10 @@ icon_state = "grimy" }, /area/awaymission/beach/offshore) +"ze" = ( +/obj/structure/stone_tile/cracked, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) "zg" = ( /obj/structure/window/reinforced, /turf/simulated/floor/wood, @@ -2617,7 +2622,7 @@ dir = 1 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) +/area/awaymission/beach/entrance) "CE" = ( /obj/effect/decal/snow/sand/edge{ name = "rough sand" @@ -2954,7 +2959,7 @@ dir = 5 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) +/area/awaymission/beach/entrance) "Gz" = ( /obj/effect/waterfall{ dir = 1; @@ -2970,6 +2975,10 @@ }, /turf/simulated/floor/plasteel, /area/awaymission/beach) +"GN" = ( +/obj/structure/bed/roller, +/turf/unsimulated/beach/water/deep/wood_floor, +/area/awaymission/undersea) "GR" = ( /obj/machinery/recharge_station, /turf/simulated/floor/wood, @@ -3039,7 +3048,7 @@ dir = 8 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) +/area/awaymission/beach/entrance) "HY" = ( /turf/unsimulated/beach/sand, /turf/simulated/shuttle/wall{ @@ -3913,6 +3922,10 @@ /mob/living/simple_animal/hostile/deathsquid, /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) +"Rp" = ( +/obj/structure/stone_tile/block, +/turf/unsimulated/floor/grass, +/area/awaymission/beach/offshore) "RG" = ( /obj/structure/chair/beachchair/red, /turf/simulated/floor/beach/roughsand, @@ -4285,7 +4298,7 @@ dir = 4 }, /turf/simulated/floor/beach/roughsand, -/area/awaymission/beach) +/area/awaymission/beach/entrance) "WK" = ( /obj/structure/closet/secure_closet/freezer/fridge/open, /turf/simulated/floor/wood, @@ -8674,7 +8687,7 @@ hI jN HQ BJ -xj +hI hI bB hI @@ -8931,7 +8944,7 @@ hI CC kU oL -xj +hI hI hI hI @@ -9188,7 +9201,7 @@ hI Gu Wy sa -xj +hI hI hI hI @@ -23853,7 +23866,7 @@ Op Op Op Fd -NT +tJ gn NT cL @@ -24110,7 +24123,7 @@ Op Op Op Fd -NT +Rp NT Hr NT @@ -24367,7 +24380,7 @@ Op Op Op Fd -NT +ze sv NT gn @@ -53774,7 +53787,7 @@ av av AQ ad -ah +GN aI RL aI From dc5c497b03b2ef2ac135d6539b4d92f27e424c52 Mon Sep 17 00:00:00 2001 From: Darkmight9 <45213755+Darkmight9@users.noreply.github.com> Date: Tue, 17 Nov 2020 23:55:16 -0500 Subject: [PATCH 10/10] Deconflicts and implements feedback --- _maps/map_files/RandomZLevels/beach.dmm | 726 ++++++++++-------- code/game/machinery/door_control.dm | 22 +- code/game/machinery/doors/poddoor.dm | 4 +- code/game/turfs/simulated/floor/misc_floor.dm | 4 +- .../mob/living/simple_animal/friendly/crab.dm | 10 + .../simple_animal/hostile/deathsquid.dm | 8 +- .../living/simple_animal/hostile/skeleton.dm | 31 + code/modules/mob/transform_procs.dm | 4 + icons/mob/simple_human.dmi | Bin 119530 -> 118086 bytes 9 files changed, 482 insertions(+), 327 deletions(-) diff --git a/_maps/map_files/RandomZLevels/beach.dmm b/_maps/map_files/RandomZLevels/beach.dmm index b43d6dace518..5001a42a477a 100644 --- a/_maps/map_files/RandomZLevels/beach.dmm +++ b/_maps/map_files/RandomZLevels/beach.dmm @@ -68,13 +68,7 @@ /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) "ar" = ( -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, +/mob/living/simple_animal/crab/strange, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "as" = ( @@ -287,7 +281,7 @@ }, /area/awaymission/beach/offshore) "by" = ( -/obj/machinery/cooker/foodgrill, +/obj/machinery/kitchen_machine/grill, /turf/simulated/floor/wood, /area/awaymission/beach) "bz" = ( @@ -366,10 +360,10 @@ /turf/simulated/floor/beach/roughsand, /area/awaymission/beach) "bX" = ( -/obj/machinery/cooking/oven, /obj/structure/disposalpipe/segment{ dir = 4 }, +/obj/machinery/kitchen_machine/oven, /turf/simulated/floor/wood, /area/awaymission/beach) "cc" = ( @@ -562,7 +556,9 @@ "fc" = ( /obj/machinery/biogenerator, /obj/item/reagent_containers/glass/bucket, -/turf/simulated/floor/plasteel, +/turf/simulated/floor/plasteel{ + icon_state = "bot" + }, /area/awaymission/beach) "fd" = ( /obj/structure/clockwork/decorative/relay, @@ -632,6 +628,12 @@ /obj/structure/flora/ausbushes/palebush, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) +"ga" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) "gn" = ( /obj/item/clockwork/component/geis_capacitor/fallen_armor, /turf/unsimulated/floor/grass, @@ -687,6 +689,16 @@ /obj/effect/overlay/coconut, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) +"gY" = ( +/obj/structure/disposalpipe/segment{ + dir = 2; + icon_state = "pipe-c" + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/awaymission/beach) "hm" = ( /obj/structure/rack, /obj/item/stack/sheet/plasteel{ @@ -781,10 +793,7 @@ /obj/machinery/vending/hydroseeds{ extended_inventory = 1 }, -/turf/simulated/floor/plasteel{ - icon_state = "caution"; - dir = 4 - }, +/turf/simulated/floor/plasteel, /area/awaymission/beach) "if" = ( /obj/effect/overlay/palmtree_r, @@ -843,15 +852,11 @@ }, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach) -"iL" = ( -/obj/effect/decal/snow/sand/edge{ - name = "rough sand" +"iO" = ( +/obj/structure/chair/sofa/corner{ + dir = 8 }, -/turf/simulated/wall/mineral/sandstone, -/area/awaymission/beach) -"iT" = ( -/obj/machinery/photocopier, -/turf/simulated/floor/plasteel, +/turf/simulated/floor/wood, /area/awaymission/beach) "iU" = ( /obj/effect/overlay/palmtree_l, @@ -897,6 +902,12 @@ /obj/item/reagent_containers/food/snacks/pie, /turf/unsimulated/beach/sand, /area/awaymission/beach/offshore) +"jJ" = ( +/obj/structure/chair/sofa{ + dir = 8 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) "jL" = ( /turf/unsimulated/floor/lava, /area/awaymission/beach/offshore) @@ -983,6 +994,15 @@ /obj/structure/flora/rock, /turf/unsimulated/beach/sand, /area/awaymission/beach/offshore) +"kK" = ( +/obj/effect/decal/snow/sand/edge{ + name = "rough sand" + }, +/turf/unsimulated/wall{ + icon_state = "rock"; + name = "rock" + }, +/area/space) "kO" = ( /obj/effect/overlay/palmtree_l, /obj/effect/overlay/coconut, @@ -1201,16 +1221,15 @@ /area/awaymission/beach/boundary) "na" = ( /obj/structure/closet/secure_closet/freezer/meat/open, -/obj/structure/disposalpipe/junction{ - dir = 1; - icon_state = "pipe-j2"; - tag = "icon-pipe-j2" +/obj/structure/disposalpipe/segment{ + dir = 2; + icon_state = "pipe-c" }, /turf/simulated/floor/wood, /area/awaymission/beach) "nb" = ( /obj/structure/table/reinforced, -/obj/machinery/microwave, +/obj/machinery/kitchen_machine/microwave, /turf/unsimulated/floor{ tag = "icon-dark"; icon_state = "dark" @@ -1252,11 +1271,7 @@ /turf/unsimulated/floor/grass, /area/awaymission/beach/offshore) "nF" = ( -/mob/living/simple_animal/hostile/skeleton{ - harm_intent_damage = 10; - health = 100; - name = "The Captain" - }, +/mob/living/simple_animal/hostile/skeleton/pirate_captain, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "nJ" = ( @@ -1293,7 +1308,7 @@ /area/awaymission/undersea) "oe" = ( /obj/structure/table/wood, -/obj/machinery/microwave, +/obj/machinery/kitchen_machine/microwave, /turf/simulated/floor/wood, /area/awaymission/beach) "og" = ( @@ -1331,13 +1346,6 @@ /obj/structure/flora/ausbushes/genericbush, /turf/unsimulated/floor/grass, /area/awaymission/beach/offshore) -"oB" = ( -/obj/structure/sink{ - pixel_y = 24 - }, -/obj/item/reagent_containers/glass/bucket, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) "oC" = ( /obj/structure/flora/ausbushes/reedbush, /obj/effect/decal/snow/sand/edge{ @@ -1439,18 +1447,13 @@ /obj/machinery/door/unpowered/shuttle, /turf/simulated/shuttle/floor, /area/awaymission/beach/offshore) -"pL" = ( -/obj/effect/decal/warning_stripes/north, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "caution" - }, -/area/awaymission/beach) "pO" = ( /obj/effect/mob_spawn/human/resort_host{ dir = 4 }, -/turf/simulated/floor/plasteel, +/turf/simulated/floor/plasteel{ + icon_state = "bot" + }, /area/awaymission/beach/entrance) "qb" = ( /obj/effect/decal/snow/sand/edge{ @@ -1526,7 +1529,7 @@ /area/awaymission/beach/offshore) "qX" = ( /obj/structure/spawner/nether{ - max_mobs = 3; + max_mobs = 2; name = "weak netherworld link" }, /obj/effect/rune, @@ -1546,13 +1549,7 @@ /obj/structure/closet/cabinet, /obj/item/clothing/shoes/black, /obj/item/clothing/under/color/lightgreen, -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, +/mob/living/simple_animal/crab/strange, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "rt" = ( @@ -1596,10 +1593,6 @@ /mob/living/simple_animal/butterfly, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) -"rF" = ( -/mob/living/simple_animal/crab, -/turf/unsimulated/beach/water/deep/sand_floor, -/area/awaymission/undersea) "rG" = ( /obj/structure/sign/electricshock{ pixel_y = 32 @@ -1619,7 +1612,6 @@ /obj/structure/table/wood, /obj/item/hatchet, /obj/item/cultivator, -/obj/structure/disposalpipe/segment, /turf/simulated/floor/plasteel, /area/awaymission/beach) "rV" = ( @@ -1714,6 +1706,18 @@ icon_state = "floor3" }, /area/awaymission/undersea) +"sV" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (WEST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 8 + }, +/turf/unsimulated/wall{ + icon_state = "rock"; + name = "rock" + }, +/area/space) "sW" = ( /obj/structure/table/wood, /obj/effect/decal/snow/sand/edge{ @@ -1770,10 +1774,24 @@ }, /turf/simulated/floor/wood, /area/awaymission/beach) +"tH" = ( +/obj/effect/decal/warning_stripes/north, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "caution" + }, +/area/awaymission/beach) "tJ" = ( /obj/structure/stone_tile/surrounding_tile, /turf/unsimulated/floor/grass, /area/awaymission/beach/offshore) +"tK" = ( +/obj/structure/chair/sofa/right, +/turf/simulated/floor/wood, +/area/awaymission/beach) "tM" = ( /obj/structure/flora/rock/pile, /obj/structure/flora/ausbushes/sunnybush, @@ -1998,6 +2016,14 @@ /obj/item/bedsheet/black, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) +"wA" = ( +/obj/structure/mineral_door/wood{ + icon_state = "wood"; + name = "Disposal room"; + tag = "icon-wood" + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) "wJ" = ( /obj/effect/overlay/palmtree_l, /turf/unsimulated/beach/sand/dense, @@ -2115,6 +2141,12 @@ /obj/structure/flora/ausbushes/genericbush, /turf/unsimulated/floor/grass, /area/awaymission/beach/offshore) +"yo" = ( +/obj/machinery/tcomms/relay/beachresort, +/turf/simulated/floor/plasteel{ + icon_state = "bot" + }, +/area/awaymission/beach) "yq" = ( /obj/structure/flora/bush, /turf/unsimulated/beach/sand/dense, @@ -2241,14 +2273,14 @@ /turf/unsimulated/beach/sand/dense, /area/awaymission/beach/offshore) "zx" = ( -/obj/machinery/conveyor_switch/oneway{ - id = "beach disposal" - }, -/obj/effect/decal/warning_stripes/north, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "caution" +/obj/structure/sink{ + dir = 4; + icon_state = "sink"; + pixel_x = 11; + pixel_y = 0 }, +/obj/item/reagent_containers/glass/bucket, +/turf/simulated/floor/plasteel, /area/awaymission/beach) "zB" = ( /obj/item/clothing/under/rank/nursesuit, @@ -2396,6 +2428,15 @@ /obj/structure/curtain/open, /turf/simulated/floor/wood, /area/awaymission/beach) +"Bb" = ( +/obj/effect/decal/snow/sand/edge{ + tag = "icon-gravsnow_corner (NORTHEAST)"; + name = "rough sand"; + icon_state = "gravsnow_corner"; + dir = 5 + }, +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach) "Bc" = ( /obj/structure/bed/wooden_lounge_chair, /turf/simulated/floor/beach/roughsand, @@ -2470,14 +2511,6 @@ /obj/item/clothing/mask/breath, /turf/simulated/floor/wood, /area/awaymission/beach) -"BG" = ( -/obj/structure/disposalpipe/segment, -/obj/effect/decal/warning_stripes/north, -/turf/simulated/floor/plasteel{ - dir = 1; - icon_state = "caution" - }, -/area/awaymission/beach) "BH" = ( /obj/item/clothing/under/color/grey, /turf/unsimulated/beach/water/deep/wood_floor, @@ -2548,13 +2581,7 @@ /obj/item/bedsheet/medical, /obj/item/clothing/under/color/purple, /obj/item/clothing/shoes/black, -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, +/mob/living/simple_animal/crab/strange, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "Cj" = ( @@ -2598,7 +2625,7 @@ }, /area/awaymission/beach/offshore) "Cz" = ( -/mob/living/simple_animal/hostile/skeleton, +/mob/living/simple_animal/hostile/skeleton/pirate, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "CA" = ( @@ -2650,6 +2677,12 @@ /obj/item/beach_ball, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach) +"CL" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/plasteel, +/area/awaymission/beach) "CM" = ( /turf/unsimulated/beach/water, /area/awaymission/beach/offshore) @@ -2713,10 +2746,6 @@ icon_state = "green" }, /area/awaymission/beach) -"DP" = ( -/obj/structure/disposalpipe/segment, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) "DR" = ( /turf/simulated/wall/mineral/sandstone, /area/awaymission/beach) @@ -2812,6 +2841,10 @@ }, /turf/simulated/floor/wood, /area/awaymission/beach) +"EW" = ( +/obj/structure/chair/comfy/black, +/turf/simulated/floor/wood, +/area/awaymission/beach) "EX" = ( /obj/machinery/light{ icon_state = "tube1"; @@ -2839,7 +2872,7 @@ /turf/unsimulated/beach/water/deep/dense, /area/awaymission/beach/entrance) "Fd" = ( -/obj/machinery/door/poddoor/brass{ +/obj/machinery/door/poddoor/impassable/brass{ id_tag = "brass temple" }, /turf/unsimulated/floor{ @@ -3043,6 +3076,11 @@ icon_state = "sandstone10" }, /area/awaymission/beach/offshore) +"HO" = ( +/obj/structure/table/wood, +/obj/item/ashtray/plastic, +/turf/simulated/floor/wood, +/area/awaymission/beach) "HQ" = ( /obj/machinery/gateway{ dir = 8 @@ -3066,6 +3104,12 @@ /obj/effect/overlay/palmtree_l, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) +"If" = ( +/obj/structure/chair/sofa/right{ + dir = 1 + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) "Ih" = ( /obj/structure/window/reinforced, /obj/structure/window/reinforced{ @@ -3159,6 +3203,15 @@ dir = 8 }, /area/awaymission/beach/offshore) +"IH" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/awaymission/beach) "II" = ( /obj/structure/safe, /obj/item/documents/syndicate/yellow, @@ -3293,6 +3346,9 @@ "JH" = ( /obj/structure/table/wood, /obj/item/clothing/head/beret, +/obj/structure/sign/poster/official/the_owl{ + pixel_x = -32 + }, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "JJ" = ( @@ -3335,7 +3391,7 @@ /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) "Kb" = ( -/obj/machinery/door/poddoor/brass/beach_brass_temple, +/obj/machinery/door/poddoor/impassable/brass/beach_brass_temple, /turf/unsimulated/floor{ desc = "Ancient brass flooring. The sounds of machinery can be heard beneath though."; icon = 'icons/obj/clockwork_objects.dmi'; @@ -3367,11 +3423,6 @@ icon_state = "floor3" }, /area/awaymission/undersea) -"Ks" = ( -/obj/structure/table/wood, -/obj/item/gun/energy/gun/mini, -/turf/unsimulated/beach/water/deep/wood_floor, -/area/awaymission/undersea) "Kt" = ( /obj/structure/flora/ausbushes/ppflowers, /obj/effect/decal/snow/sand/edge{ @@ -3386,6 +3437,9 @@ /obj/structure/sign/poster/official/nanomichi_ad{ pixel_y = 32 }, +/obj/structure/window/reinforced{ + dir = 8 + }, /turf/simulated/floor/light/colour_cycle/dancefloor_b, /area/awaymission/beach) "KL" = ( @@ -3438,6 +3492,9 @@ /obj/machinery/newscaster{ pixel_y = -32 }, +/obj/structure/chair/comfy/black{ + dir = 1 + }, /turf/simulated/floor/wood, /area/awaymission/beach) "Lu" = ( @@ -3466,8 +3523,10 @@ /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) "LE" = ( -/obj/structure/rack, -/turf/unsimulated/beach/water/deep/wood_floor, +/obj/structure/flora/rock/pile, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, /area/awaymission/undersea) "LJ" = ( /turf/simulated/floor/beach/roughcoastline, @@ -3534,6 +3593,9 @@ /obj/structure/flora/ausbushes/leafybush, /turf/simulated/floor/beach/roughsand/dense, /area/awaymission/beach/boundary) +"ME" = ( +/turf/simulated/floor/beach/roughsand, +/area/awaymission/beach/entrance) "MG" = ( /obj/structure/window/reinforced{ dir = 1 @@ -3562,10 +3624,9 @@ /obj/structure/table/wood, /obj/item/clothing/accessory/armband, /obj/item/clothing/accessory/red, -/obj/structure/sign/poster/official/the_owl{ - pixel_x = -32 +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" }, -/turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) "Nb" = ( /obj/effect/overlay/palmtree_l, @@ -3600,6 +3661,13 @@ }, /turf/simulated/shuttle/floor, /area/awaymission/beach/offshore) +"Nt" = ( +/obj/structure/disposalpipe/segment, +/turf/simulated/floor/plasteel{ + dir = 8; + icon_state = "neutralfull" + }, +/area/awaymission/beach) "NE" = ( /obj/structure/table/wood, /obj/item/clothing/head/helmet/justice, @@ -3773,6 +3841,15 @@ /obj/structure/chair/stool, /turf/unsimulated/beach/sand, /area/awaymission/beach/offshore) +"Pk" = ( +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/machinery/seed_extractor, +/turf/simulated/floor/plasteel{ + icon_state = "bot" + }, +/area/awaymission/beach) "Pm" = ( /obj/structure/chair/office/light, /turf/unsimulated/beach/sand, @@ -3909,8 +3986,23 @@ "QZ" = ( /obj/structure/rack, /obj/item/restraints/handcuffs/cable/zipties, -/turf/unsimulated/beach/water/deep/wood_floor, +/turf/unsimulated/beach/water/deep/wood_floor{ + icon_state = "wood-broken" + }, /area/awaymission/undersea) +"Rb" = ( +/obj/machinery/conveyor_switch/oneway{ + id = "beach disposal" + }, +/obj/effect/decal/warning_stripes/north, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "caution" + }, +/area/awaymission/beach) "Rj" = ( /obj/structure/table/wood, /obj/item/storage/bag/plants, @@ -3964,6 +4056,17 @@ /obj/effect/overlay/coconut, /turf/simulated/floor/beach/roughsand/dense, /area/awaymission/beach/boundary) +"RX" = ( +/obj/effect/decal/warning_stripes/north, +/obj/structure/disposalpipe/segment{ + dir = 1; + icon_state = "pipe-c" + }, +/turf/simulated/floor/plasteel{ + dir = 1; + icon_state = "caution" + }, +/area/awaymission/beach) "RY" = ( /obj/structure/sign/poster/contraband/syndicate_recruitment{ pixel_x = 0; @@ -4057,7 +4160,8 @@ pixel_x = 32; pixel_y = 0 }, -/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/obj/structure/chair/sofa/corner, +/turf/simulated/floor/wood, /area/awaymission/beach) "SO" = ( /obj/effect/overlay/palmtree_r, @@ -4095,6 +4199,13 @@ /obj/item/reagent_containers/food/drinks/drinkingglass, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) +"To" = ( +/obj/structure/disposalpipe/junction{ + dir = 1; + icon_state = "pipe-y" + }, +/turf/simulated/floor/wood, +/area/awaymission/beach) "TF" = ( /obj/structure/mirror{ pixel_y = 32 @@ -4113,6 +4224,12 @@ /obj/structure/closet/crate/secure/loot, /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) +"TJ" = ( +/obj/structure/window/reinforced{ + dir = 8 + }, +/turf/simulated/floor/light/colour_cycle/dancefloor_b, +/area/awaymission/beach) "TL" = ( /obj/structure/falsewall/brass, /turf/unsimulated/beach/water/deep/sand_floor, @@ -4126,6 +4243,10 @@ }, /turf/simulated/floor/wood, /area/awaymission/beach) +"TZ" = ( +/obj/structure/window/reinforced, +/turf/simulated/floor/light/colour_cycle/dancefloor_a, +/area/awaymission/beach) "Ul" = ( /obj/structure/flora/ausbushes/sparsegrass, /turf/unsimulated/beach/sand, @@ -4218,13 +4339,7 @@ /turf/simulated/floor/plating/airless, /area/awaymission/beach/offshore) "Vh" = ( -/mob/living/simple_animal/crab{ - desc = "A hard-shelled crustacean. There's a mad look in its eyes."; - faction = list("nether"); - melee_damage_lower = 3; - melee_damage_upper = 3; - name = "strange crab" - }, +/mob/living/simple_animal/crab/strange, /turf/unsimulated/beach/water/deep/sand_floor, /area/awaymission/undersea) "Vp" = ( @@ -4244,6 +4359,12 @@ /obj/structure/closet/secure_closet/personal, /turf/simulated/floor/wood, /area/awaymission/beach) +"VB" = ( +/turf/unsimulated/wall{ + icon_state = "rock"; + name = "rock" + }, +/area/awaymission/beach/boundary) "VE" = ( /obj/effect/overlay/palmtree_r, /obj/effect/decal/snow/sand/edge{ @@ -4321,10 +4442,6 @@ /obj/structure/flora/ausbushes/lavendergrass, /turf/unsimulated/beach/sand/dense, /area/awaymission/beach/offshore) -"WV" = ( -/obj/machinery/seed_extractor, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) "Xa" = ( /obj/effect/decal/remains/human{ plane = -1 @@ -4374,6 +4491,10 @@ /obj/item/clothing/under/color/lightred, /turf/unsimulated/beach/water/deep/wood_floor, /area/awaymission/undersea) +"XD" = ( +/obj/structure/window/reinforced, +/turf/simulated/floor/light/colour_cycle/dancefloor_b, +/area/awaymission/beach) "XF" = ( /obj/structure/table/wood, /turf/simulated/floor/wood, @@ -4407,6 +4528,14 @@ /obj/effect/overlay/palmtree_r, /turf/unsimulated/beach/sand, /area/awaymission/beach/offshore) +"XV" = ( +/obj/effect/mob_spawn/human/resort_host{ + dir = 4 + }, +/turf/simulated/floor/plasteel{ + icon_state = "bot" + }, +/area/awaymission/beach) "XZ" = ( /obj/structure/chair/stool, /obj/structure/mirror{ @@ -4484,10 +4613,6 @@ }, /turf/simulated/floor/beach/roughsand, /area/awaymission/beach/boundary) -"YM" = ( -/obj/machinery/tcomms/relay/beachresort, -/turf/simulated/floor/plasteel, -/area/awaymission/beach) "YO" = ( /turf/unsimulated/wall/fakeglass{ icon_state = "fakewindows2"; @@ -4610,6 +4735,7 @@ name = "Resort Backroom"; req_access_txt = "25" }, +/obj/structure/disposalpipe/segment, /turf/simulated/floor/wood, /area/awaymission/beach) "ZU" = ( @@ -5122,12 +5248,12 @@ dr dr dr dr +ZW dr dr dr dr -dr -dr +ZW dr dr dr @@ -5370,11 +5496,11 @@ zZ fR dj dr +ZW dr dr -ID -dr dr +vS dr dr dr @@ -5633,17 +5759,17 @@ dr dr dr dr -dr +ZW dr ka dr dr dr +ZW dr dr dr -dr -dr +vS dr ds dt @@ -6400,15 +6526,15 @@ dk yU yU yU +ds yU yU yU yU yU +dr yU -yU -yU -yU +dr yU yU yU @@ -6657,15 +6783,15 @@ LJ dr dr dr +yU dr dr dr dr dr +yU dr -dr -dr -dr +yU dr dr dr @@ -8429,7 +8555,7 @@ hI Qf hI hI -xG +hI hI hI hI @@ -8684,9 +8810,9 @@ fN rg hI hI -jN -HQ -BJ +hI +hI +hI hI hI bB @@ -8939,15 +9065,15 @@ iI iI iI NP -hI -hI -CC -kU -oL +gJ +gJ +gJ +gJ hI hI hI hI +xG hI hI hI @@ -9196,16 +9322,16 @@ fR fR pu yG +DR +DR +DR +DR +UR hI -hI -Gu -Wy -sa -hI -hI -hI -hI -hI +jN +HQ +BJ +ME hI hI hI @@ -9453,16 +9579,16 @@ fR Mx fR Fs +DR +Jb +RX +DR +UR hI -hI -hI -hI -xG -hI -hI -hI -hI -hI +CC +kU +oL +ME hI hI hI @@ -9710,16 +9836,16 @@ Ia fR lJ MB -hI -Qf -xv -hI -hI -hI -hI -hI +DR +Kk +tH +wA hI hI +Gu +Wy +sa +ME hI hI hI @@ -9968,14 +10094,14 @@ uR fR gT DR +Eq +tH DR -iL -DR -DR -DR -UR +bj +hI hI hI +xG hI hI hI @@ -9998,7 +10124,7 @@ LJ dr dr dr -dr +vS dr dr dr @@ -10225,12 +10351,12 @@ uR fR Fs DR -YM -oN -oN -jA +Kk +Rb DR -bi +Bb +gJ +hI hI hI hI @@ -10482,12 +10608,12 @@ gW zZ NP DR -oB -UN -UN -UN DR -bj +bd +DR +DR +DR +bi hI hI hI @@ -10726,7 +10852,7 @@ aa PR PR PR -PR +kK PR PR PR @@ -10740,8 +10866,8 @@ fR MB Kj fc -tW -tW +Pk +UN pO DR AD @@ -10996,9 +11122,9 @@ fR hn Fs Kj -WV -tW -tW +UN +CL +UN pO DR bd @@ -11037,7 +11163,7 @@ dr dr dr dr -dr +vS dr dr dr @@ -11254,9 +11380,9 @@ Id NP Kj IA +IH tW -tW -GK +XV DR DD lo @@ -11511,9 +11637,9 @@ fR gT Kj DL +IH tW -tW -iT +GK DR Jl lo @@ -11768,7 +11894,7 @@ zZ wQ Kj DL -tW +IH tW tW kf @@ -12025,11 +12151,11 @@ fR Zb Kj MK -tW -tW -tW +gY +Nt +Nt ZN -Bq +To lo lo mo @@ -12795,11 +12921,11 @@ Ia fR zi DR -Jb -BG -DP +jA +tW +tW rT -Zu +DR na vZ bJ @@ -13052,8 +13178,8 @@ uR fN Fs DR -Kk -pL +oN +UN UN HC Mq @@ -13309,8 +13435,8 @@ zZ fR Db DR -Eq -pL +oN +UN kd Rj DR @@ -13566,7 +13692,7 @@ fR lJ NP DR -Kk +yo zx EE zQ @@ -14579,7 +14705,7 @@ aa aa aa PR -PR +sV PR PR PR @@ -14851,13 +14977,13 @@ fR fR gT DR -sk -Gm -sk -Gm -sk -Gm -sk +EW +XF +WL +lo +lo +lo +lo lo lo CF @@ -15109,12 +15235,12 @@ lJ Fs DR KB -sk -Gm -sk -Gm -sk -Gm +ga +TJ +ga +lo +lo +lo lo lo lo @@ -15370,8 +15496,8 @@ Gm sk Gm sk -Gm -sk +lo +lo lo vm DR @@ -15625,10 +15751,10 @@ DR Gm sk Gm -gw -Gm sk Gm +TZ +lo lo Ls DR @@ -15884,10 +16010,10 @@ Gm sk Gm sk -Gm -sk +XD +lo +lo lo -WL DR UR xv @@ -16137,14 +16263,14 @@ wR zi DR Gm -sk -Gm -sk +gw Gm sk Gm -lo -XF +TZ +tK +HO +If DR UR Qf @@ -16398,10 +16524,10 @@ Gm sk Gm sk -Gm +XD SL -lo -WL +jJ +iO DR UR hI @@ -16679,20 +16805,20 @@ hI LJ dr dr +yU dr dr dr dr +yU dr +yU dr dr dr dr dr -dr -dr -dr -dr +yU dr dr dr @@ -16936,20 +17062,20 @@ cc dk yU yU +dr yU yU yU yU +dr yU +dr yU yU yU yU yU -yU -yU -yU -yU +dr yU yU yU @@ -17451,7 +17577,6 @@ dj dr dr dr -ID dr dr dr @@ -17461,6 +17586,7 @@ dr dr dr dr +vS dr dr dr @@ -17706,12 +17832,12 @@ fR fR dj dr +ZW dr dr dr dr -dr -dr +ZW dr dr ka @@ -17722,7 +17848,7 @@ dr dr dr dr -dr +vS dr ds dt @@ -17927,7 +18053,7 @@ PR PR PR PR -zn +VB iI oC fR @@ -17965,7 +18091,7 @@ dj dr dr dr -dr +vS dr dr dr @@ -18075,7 +18201,7 @@ av av RU BT -Ks +aE aE aE Td @@ -18184,8 +18310,8 @@ PR PR PR PR -JZ -fR +VB +VB fR VE Lq @@ -18232,7 +18358,7 @@ dr dr ka dr -dr +ZW dr dr dr @@ -18441,9 +18567,9 @@ PR PR PR PR -JZ -JZ -JZ +VB +VB +VB Su JZ if @@ -18636,7 +18762,7 @@ av av av av -av +uz av ad ad @@ -18697,10 +18823,10 @@ zk Qu vr ji -ji -XL -vr -ji +km +km +km +km km km km @@ -18956,8 +19082,8 @@ vr ji vr vr -ji -ji +km +km km km km @@ -19214,7 +19340,7 @@ qx vr sZ vr -qx +km km km km @@ -19666,7 +19792,7 @@ av av av av -av +uz ad av av @@ -20437,7 +20563,7 @@ uz av av av -av +uz ad ad av @@ -20694,7 +20820,7 @@ uz av an av -av +uz HJ ad xQ @@ -20951,8 +21077,8 @@ av av av av -uz av +uz xQ xQ xQ @@ -21471,7 +21597,7 @@ uz av uz av -av +uz ao ad ad @@ -21730,7 +21856,7 @@ av av av av -av +uz HJ ad ad @@ -21988,7 +22114,7 @@ uz av uz av -av +uz av av HJ @@ -23013,7 +23139,7 @@ uz av av uz -av +uz av uz av @@ -23267,12 +23393,12 @@ av av av ao -av +uz av av av HJ -av +uz av ao av @@ -23523,9 +23649,9 @@ uz av av av -av -av -av +uz +HJ +uz av uz av @@ -23781,8 +23907,8 @@ uz av av av -av -av +uz +uz av av av @@ -27582,7 +27708,7 @@ av av av av -rF +rX av av av @@ -28106,7 +28232,7 @@ av av av av -rF +rX av ad av @@ -29437,7 +29563,7 @@ av av av av -rF +rX av av av @@ -30218,7 +30344,7 @@ av av av av -rF +rX av av av @@ -30923,7 +31049,7 @@ av av av av -rF +rX av av av @@ -35060,7 +35186,7 @@ av av av av -rF +rX av av av @@ -35090,7 +35216,7 @@ av av av av -rF +rX av av av @@ -36323,7 +36449,7 @@ av ao av av -rF +rX av av av @@ -36593,7 +36719,7 @@ av av av av -rF +rX av av av @@ -43741,8 +43867,8 @@ Sw Sw Sw Sw -av -av +as +ad ad ad ad @@ -45335,7 +45461,7 @@ av av av av -rF +rX av av av @@ -47349,9 +47475,9 @@ av av ad ad -ad -ad -ad +as +av +as ad av av @@ -47684,7 +47810,7 @@ av av av av -rF +rX av av av @@ -47864,7 +47990,7 @@ av ad ah ah -ah +Uv ah ad Ep @@ -49206,7 +49332,7 @@ av av av av -rF +rX av av av @@ -51027,7 +51153,7 @@ av av av av -rF +rX av av av @@ -52481,9 +52607,9 @@ ad ad ad ad -ad as av +av as as av @@ -54342,7 +54468,7 @@ av av av av -rF +rX av av av diff --git a/code/game/machinery/door_control.dm b/code/game/machinery/door_control.dm index effc81fcefae..69e763017bd6 100644 --- a/code/game/machinery/door_control.dm +++ b/code/game/machinery/door_control.dm @@ -140,28 +140,12 @@ switch(temple_traps) if(1 to 5)//You're safe, this time. return - if(6,7) + if(6 to 8) new /mob/living/simple_animal/hostile/poison/giant_spider(get_turf(src)) visible_message("A hatch opens above you and a giant spider falls down on your head!") playsound(get_turf(src), 'sound/effects/bin_close.ogg', 200, TRUE) - if(8,9) - addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, user.loc, -1, rand(1,5), rand(1,5), rand(1,5), rand(1,5), 1, 0, 2), 50) + if(9,10) + addtimer(CALLBACK(GLOBAL_PROC, .proc/explosion, user.loc, -1, rand(1,5), rand(1,5), rand(1,5), rand(1,5), 1, 0, 2), 60) playsound(get_turf(src), 'sound/mecha/powerup.ogg', 200, TRUE) visible_message("A high pitched whine can be heard and the walls looks to be heating up!") - if(10) - for(var/mob/M in range(5, src)) - shake_camera(M, 15, 1) - visible_message("The ground begins to shake and roaring machinery can be heard! RUN!") - addtimer(CALLBACK(src, .proc/temple_collapse), 5 SECONDS) - -/obj/machinery/door_control/brass/beach_brass_temple_switch/proc/temple_collapse() - for(var/mob/M in range(20, src)) - shake_camera(M, 15, 1) - playsound(get_turf(src),'sound/effects/explosionfar.ogg', 200, TRUE) - visible_message("The brass floor collapses and forms a massive pit!") - for(var/turf/T in range(4,src)) - if(!T.density) - T.TerraformTurf(/turf/simulated/floor/chasm/straight_down/lava_land_surface) - qdel(src) - diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm index 5b8443549c4b..c416655978c5 100644 --- a/code/game/machinery/doors/poddoor.dm +++ b/code/game/machinery/doors/poddoor.dm @@ -134,11 +134,11 @@ width = 2 dir = EAST -/obj/machinery/door/poddoor/brass +/obj/machinery/door/poddoor/impassable/brass name = "brass blast door" desc = "A great brass blast door. It looks like it can withstand a lot of punishment." icon = 'icons/obj/doors/blastdoor_brass.dmi' -/obj/machinery/door/poddoor/brass/beach_brass_temple/Initialize() +/obj/machinery/door/poddoor/impassable/brass/beach_brass_temple/Initialize() . = ..() id_tag = "brassbeachtempledoor[rand(1, 12)]" diff --git a/code/game/turfs/simulated/floor/misc_floor.dm b/code/game/turfs/simulated/floor/misc_floor.dm index 54c035d2dc63..29ffaf8ac383 100644 --- a/code/game/turfs/simulated/floor/misc_floor.dm +++ b/code/game/turfs/simulated/floor/misc_floor.dm @@ -58,11 +58,11 @@ /turf/simulated/floor/beach/roughsand/dense //made simulated versions to fix lighting issues density = TRUE -/turf/simulated/floor/beach/roughsand/New() //a simulated version of the unsimulated beach sand +/turf/simulated/floor/beach/roughsand/Initialize() //a simulated version of the unsimulated beach sand icon_state = "rough_sand[rand(1, 6)]" ..() -/turf/simulated/floor/beach/roughcoastline/New() +/turf/simulated/floor/beach/roughcoastline/Initialize() ..() if(water_overlay_image) var/image/overlay_image = image('icons/misc/beach.dmi', icon_state = water_overlay_image, layer = ABOVE_MOB_LAYER) diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 4a51ce62d4e4..766c91df8792 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -53,3 +53,13 @@ response_disarm = "shoves" response_harm = "stomps" gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/crab/strange + name = "Strange Crab" + real_name = "Strange Crab" + desc = "A hard-shelled crustacean. Its eyes regard you with a mix of desperation and madness." + response_help = "pokes" + response_disarm = "shoves" + response_harm = "stomps" + faction = list("nether") + gold_core_spawnable = NO_SPAWN diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index 5357b45e436d..4a9dc3b26920 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -2,10 +2,10 @@ name = "death squid" desc = "A large, floating eldritch horror. Its body glows with an evil red light, and its tentacles look to have been dipped in alien blood." - speed = 1 + speed = 1.1 speak_emote = list("telepathically thunders", "telepathically booms") - maxHealth = 2500 // same as megafauna - health = 2500 + maxHealth = 1000 // same as megafauna + health = 1000 icon = 'icons/mob/deathsquid_large.dmi' // Credit: FullofSkittles icon_state = "deathsquid" @@ -18,7 +18,7 @@ attack_sound = 'sound/weapons/whip.ogg' armour_penetration = 25 melee_damage_lower = 10 - melee_damage_upper = 100 + melee_damage_upper = 50 environment_smash = ENVIRONMENT_SMASH_RWALLS force_threshold = 15 diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index e0e71506bbeb..ae954e38673d 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -47,3 +47,34 @@ /obj/item/twohanded/spear, /obj/item/clothing/shoes/winterboots, /obj/item/clothing/suit/hooded/wintercoat) + +/mob/living/simple_animal/hostile/skeleton/pirate + name = "undead pirate" + desc = "The reanimated remains of a pirate. He still defends his ship, even in undeath." + icon_state = "skeleton_pirate" + icon_living = "skeleton_pirate" + maxHealth = 55 + health = 55 + gold_core_spawnable = NO_SPAWN + melee_damage_lower = 17 + melee_damage_upper = 20 + +/mob/living/simple_animal/hostile/skeleton/pirate_captain + name = "undead pirate captain" + desc = "The reanimated remains of a pirate captain. Not even death will stop him from leading his crew." + icon_state = "skeleton_captain" + icon_living = "skeleton_captain" + maxHealth = 80 + health = 80 + gold_core_spawnable = NO_SPAWN + projectilesound = 'sound/weapons/laser.ogg' + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/beam + deathmessage = "collapses into a pile of bones, its gear falling to the floor! You feel a strange gratitude wash over you." + loot = list(/obj/effect/decal/remains/human, + /obj/item/clothing/head/pirate, + /obj/item/clothing/suit/pirate_brown, + /obj/item/gun/energy/laser) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 212bb41720db..02bb5062983d 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -269,6 +269,10 @@ return 1 if(ispath(MP, /mob/living/simple_animal/pet/dog/corgi)) return 1 + if(ispath(MP, /mob/living/simple_animal/crab/strange)) + return 0 + if(ispath(MP, /mob/living/simple_animal/crab/evil)) + return 0 if(ispath(MP, /mob/living/simple_animal/crab)) return 1 if(ispath(MP, /mob/living/simple_animal/chicken)) diff --git a/icons/mob/simple_human.dmi b/icons/mob/simple_human.dmi index 9ab1c9a1796c2a09ae768310fbe57cffbff7aeb4..0658353109a5b77173ef4265c1f2cfe28e417d97 100644 GIT binary patch literal 118086 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